summaryrefslogtreecommitdiffstats
path: root/usr.sbin
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin')
-rw-r--r--usr.sbin/IPXrouted/IPXrouted.8195
-rw-r--r--usr.sbin/IPXrouted/Makefile12
-rw-r--r--usr.sbin/IPXrouted/af.c298
-rw-r--r--usr.sbin/IPXrouted/af.h77
-rw-r--r--usr.sbin/IPXrouted/defs.h108
-rw-r--r--usr.sbin/IPXrouted/if.c151
-rw-r--r--usr.sbin/IPXrouted/input.c304
-rw-r--r--usr.sbin/IPXrouted/interface.h95
-rw-r--r--usr.sbin/IPXrouted/main.c400
-rw-r--r--usr.sbin/IPXrouted/output.c231
-rw-r--r--usr.sbin/IPXrouted/protocol.h92
-rw-r--r--usr.sbin/IPXrouted/sap.h108
-rw-r--r--usr.sbin/IPXrouted/sap_input.c215
-rw-r--r--usr.sbin/IPXrouted/sap_output.c198
-rw-r--r--usr.sbin/IPXrouted/sap_tables.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.c521
-rw-r--r--usr.sbin/IPXrouted/trace.h138
-rw-r--r--usr.sbin/Makefile197
-rw-r--r--usr.sbin/Makefile.inc4
-rw-r--r--usr.sbin/ac/Makefile20
-rw-r--r--usr.sbin/ac/ac.8151
-rw-r--r--usr.sbin/ac/ac.c567
-rw-r--r--usr.sbin/accton/Makefile9
-rw-r--r--usr.sbin/accton/accton.836
-rw-r--r--usr.sbin/accton/accton.c93
-rw-r--r--usr.sbin/acpi/Makefile7
-rw-r--r--usr.sbin/acpi/Makefile.inc6
-rw-r--r--usr.sbin/acpi/acpiconf/Makefile7
-rw-r--r--usr.sbin/acpi/acpiconf/acpiconf.879
-rw-r--r--usr.sbin/acpi/acpiconf/acpiconf.c129
-rw-r--r--usr.sbin/acpi/acpidump/Makefile13
-rw-r--r--usr.sbin/acpi/acpidump/acpi.c436
-rw-r--r--usr.sbin/acpi/acpidump/acpi_user.c161
-rw-r--r--usr.sbin/acpi/acpidump/acpidump.8184
-rw-r--r--usr.sbin/acpi/acpidump/acpidump.c107
-rw-r--r--usr.sbin/acpi/acpidump/acpidump.h181
-rw-r--r--usr.sbin/acpi/acpidump/aml_dump.c60
-rw-r--r--usr.sbin/acpi/acpidump/asl_dump.c1644
-rw-r--r--usr.sbin/acpi/amldb/Makefile13
-rw-r--r--usr.sbin/acpi/amldb/aml/aml_amlmem.c92
-rw-r--r--usr.sbin/acpi/amldb/aml/aml_amlmem.h65
-rw-r--r--usr.sbin/acpi/amldb/aml/aml_common.c735
-rw-r--r--usr.sbin/acpi/amldb/aml/aml_common.h159
-rw-r--r--usr.sbin/acpi/amldb/aml/aml_env.h46
-rw-r--r--usr.sbin/acpi/amldb/aml/aml_evalobj.c436
-rw-r--r--usr.sbin/acpi/amldb/aml/aml_evalobj.h48
-rw-r--r--usr.sbin/acpi/amldb/aml/aml_memman.c476
-rw-r--r--usr.sbin/acpi/amldb/aml/aml_memman.h172
-rw-r--r--usr.sbin/acpi/amldb/aml/aml_name.c481
-rw-r--r--usr.sbin/acpi/amldb/aml/aml_name.h88
-rw-r--r--usr.sbin/acpi/amldb/aml/aml_obj.c264
-rw-r--r--usr.sbin/acpi/amldb/aml/aml_obj.h227
-rw-r--r--usr.sbin/acpi/amldb/aml/aml_parse.c2020
-rw-r--r--usr.sbin/acpi/amldb/aml/aml_parse.h36
-rw-r--r--usr.sbin/acpi/amldb/aml/aml_region.c349
-rw-r--r--usr.sbin/acpi/amldb/aml/aml_region.h92
-rw-r--r--usr.sbin/acpi/amldb/aml/aml_status.h41
-rw-r--r--usr.sbin/acpi/amldb/aml/aml_store.c349
-rw-r--r--usr.sbin/acpi/amldb/aml/aml_store.h39
-rw-r--r--usr.sbin/acpi/amldb/amldb.8336
-rw-r--r--usr.sbin/acpi/amldb/amldb.c185
-rw-r--r--usr.sbin/acpi/amldb/debug.c310
-rw-r--r--usr.sbin/acpi/amldb/debug.h36
-rw-r--r--usr.sbin/acpi/amldb/region.c510
-rw-r--r--usr.sbin/adduser/Makefile6
-rw-r--r--usr.sbin/adduser/adduser.8243
-rw-r--r--usr.sbin/adduser/adduser.perl1558
-rw-r--r--usr.sbin/adduser/rmuser.8170
-rw-r--r--usr.sbin/adduser/rmuser.perl601
-rw-r--r--usr.sbin/amd/Makefile11
-rw-r--r--usr.sbin/amd/Makefile.inc37
-rw-r--r--usr.sbin/amd/NOTES3
-rw-r--r--usr.sbin/amd/amd/Makefile42
-rw-r--r--usr.sbin/amd/amq/Makefile20
-rw-r--r--usr.sbin/amd/doc/Makefile14
-rw-r--r--usr.sbin/amd/fixmount/Makefile21
-rw-r--r--usr.sbin/amd/fsinfo/Makefile22
-rw-r--r--usr.sbin/amd/hlfsd/Makefile19
-rw-r--r--usr.sbin/amd/include/Makefile19
-rw-r--r--usr.sbin/amd/include/amu_nfs_prot.h1
-rw-r--r--usr.sbin/amd/include/aux_conf.h81
-rw-r--r--usr.sbin/amd/include/build_version.h6
-rw-r--r--usr.sbin/amd/include/config.h2081
-rw-r--r--usr.sbin/amd/include/newvers.sh43
-rw-r--r--usr.sbin/amd/libamu/Makefile31
-rw-r--r--usr.sbin/amd/mk-amd-map/Makefile13
-rw-r--r--usr.sbin/amd/pawd/Makefile20
-rw-r--r--usr.sbin/amd/scripts/Makefile7
-rw-r--r--usr.sbin/amd/wire-test/Makefile16
-rw-r--r--usr.sbin/ancontrol/Makefile12
-rw-r--r--usr.sbin/ancontrol/ancontrol.8526
-rw-r--r--usr.sbin/ancontrol/ancontrol.c1760
-rw-r--r--usr.sbin/apm/Makefile8
-rw-r--r--usr.sbin/apm/apm.8143
-rw-r--r--usr.sbin/apm/apm.c511
-rw-r--r--usr.sbin/apmd/Makefile20
-rw-r--r--usr.sbin/apmd/README213
-rw-r--r--usr.sbin/apmd/apmd.8296
-rw-r--r--usr.sbin/apmd/apmd.c695
-rw-r--r--usr.sbin/apmd/apmd.h108
-rw-r--r--usr.sbin/apmd/apmdlex.l112
-rw-r--r--usr.sbin/apmd/apmdparse.y204
-rw-r--r--usr.sbin/apmd/contrib/pccardq.c286
-rw-r--r--usr.sbin/arp/Makefile8
-rw-r--r--usr.sbin/arp/arp.4143
-rw-r--r--usr.sbin/arp/arp.8175
-rw-r--r--usr.sbin/arp/arp.c760
-rw-r--r--usr.sbin/atm/Makefile29
-rw-r--r--usr.sbin/atm/Makefile.inc26
-rw-r--r--usr.sbin/atm/atmarpd/Makefile36
-rw-r--r--usr.sbin/atm/atmarpd/atmarp_config.c127
-rw-r--r--usr.sbin/atm/atmarpd/atmarp_log.c146
-rw-r--r--usr.sbin/atm/atmarpd/atmarp_scsp.c778
-rw-r--r--usr.sbin/atm/atmarpd/atmarp_subr.c954
-rw-r--r--usr.sbin/atm/atmarpd/atmarp_timer.c229
-rw-r--r--usr.sbin/atm/atmarpd/atmarp_var.h225
-rw-r--r--usr.sbin/atm/atmarpd/atmarpd.8169
-rw-r--r--usr.sbin/atm/atmarpd/atmarpd.c411
-rw-r--r--usr.sbin/atm/scspd/Makefile41
-rw-r--r--usr.sbin/atm/scspd/scsp_cafsm.c1439
-rw-r--r--usr.sbin/atm/scspd/scsp_config.c1158
-rw-r--r--usr.sbin/atm/scspd/scsp_config_lex.c530
-rw-r--r--usr.sbin/atm/scspd/scsp_config_parse.y412
-rw-r--r--usr.sbin/atm/scspd/scsp_hfsm.c577
-rw-r--r--usr.sbin/atm/scspd/scsp_if.c637
-rw-r--r--usr.sbin/atm/scspd/scsp_if.h194
-rw-r--r--usr.sbin/atm/scspd/scsp_input.c1088
-rw-r--r--usr.sbin/atm/scspd/scsp_log.c264
-rw-r--r--usr.sbin/atm/scspd/scsp_msg.c590
-rw-r--r--usr.sbin/atm/scspd/scsp_msg.h461
-rw-r--r--usr.sbin/atm/scspd/scsp_output.c930
-rw-r--r--usr.sbin/atm/scspd/scsp_print.c1301
-rw-r--r--usr.sbin/atm/scspd/scsp_socket.c1344
-rw-r--r--usr.sbin/atm/scspd/scsp_subr.c1112
-rw-r--r--usr.sbin/atm/scspd/scsp_timer.c265
-rw-r--r--usr.sbin/atm/scspd/scsp_var.h465
-rw-r--r--usr.sbin/atm/scspd/scspd.8626
-rw-r--r--usr.sbin/atm/scspd/scspd.c546
-rw-r--r--usr.sbin/boot0cfg/Makefile7
-rw-r--r--usr.sbin/boot0cfg/boot0cfg.8174
-rw-r--r--usr.sbin/boot0cfg/boot0cfg.c427
-rw-r--r--usr.sbin/boot98cfg/Makefile8
-rw-r--r--usr.sbin/boot98cfg/boot98cfg.899
-rw-r--r--usr.sbin/boot98cfg/boot98cfg.c255
-rw-r--r--usr.sbin/bootparamd/Makefile5
-rw-r--r--usr.sbin/bootparamd/Makefile.inc4
-rw-r--r--usr.sbin/bootparamd/bootparamd/Makefile24
-rw-r--r--usr.sbin/bootparamd/bootparamd/README75
-rw-r--r--usr.sbin/bootparamd/bootparamd/bootparamd.875
-rw-r--r--usr.sbin/bootparamd/bootparamd/bootparamd.c342
-rw-r--r--usr.sbin/bootparamd/bootparamd/bootparams.586
-rw-r--r--usr.sbin/bootparamd/bootparamd/main.c119
-rw-r--r--usr.sbin/bootparamd/callbootd/Makefile24
-rw-r--r--usr.sbin/bootparamd/callbootd/callbootd.c200
-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.c556
-rw-r--r--usr.sbin/btxld/elfh.c124
-rw-r--r--usr.sbin/btxld/elfh.h38
-rw-r--r--usr.sbin/burncd/Makefile8
-rw-r--r--usr.sbin/burncd/burncd.8185
-rw-r--r--usr.sbin/burncd/burncd.c568
-rw-r--r--usr.sbin/cdcontrol/Makefile9
-rw-r--r--usr.sbin/cdcontrol/cdcontrol.1199
-rw-r--r--usr.sbin/cdcontrol/cdcontrol.c1269
-rw-r--r--usr.sbin/chkgrp/Makefile8
-rw-r--r--usr.sbin/chkgrp/chkgrp.884
-rw-r--r--usr.sbin/chkgrp/chkgrp.c144
-rw-r--r--usr.sbin/chown/Makefile9
-rw-r--r--usr.sbin/chown/chgrp.1140
-rw-r--r--usr.sbin/chown/chown.8160
-rw-r--r--usr.sbin/chown/chown.c284
-rw-r--r--usr.sbin/chroot/Makefile8
-rw-r--r--usr.sbin/chroot/chroot.878
-rw-r--r--usr.sbin/chroot/chroot.c99
-rw-r--r--usr.sbin/ckdist/Makefile11
-rw-r--r--usr.sbin/ckdist/ckdist.1101
-rw-r--r--usr.sbin/ckdist/ckdist.c440
-rw-r--r--usr.sbin/config/Makefile17
-rw-r--r--usr.sbin/config/SMM.doc/0.t88
-rw-r--r--usr.sbin/config/SMM.doc/1.t61
-rw-r--r--usr.sbin/config/SMM.doc/2.t188
-rw-r--r--usr.sbin/config/SMM.doc/3.t299
-rw-r--r--usr.sbin/config/SMM.doc/4.t442
-rw-r--r--usr.sbin/config/SMM.doc/5.t271
-rw-r--r--usr.sbin/config/SMM.doc/6.t239
-rw-r--r--usr.sbin/config/SMM.doc/Makefile11
-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.8255
-rw-r--r--usr.sbin/config/config.h161
-rw-r--r--usr.sbin/config/config.y291
-rw-r--r--usr.sbin/config/configvers.h11
-rw-r--r--usr.sbin/config/lang.l274
-rw-r--r--usr.sbin/config/main.c480
-rw-r--r--usr.sbin/config/mkheaders.c205
-rw-r--r--usr.sbin/config/mkmakefile.c789
-rw-r--r--usr.sbin/config/mkoptions.c372
-rw-r--r--usr.sbin/cron/Makefile5
-rw-r--r--usr.sbin/cron/Makefile.inc9
-rw-r--r--usr.sbin/cron/cron/Makefile12
-rw-r--r--usr.sbin/cron/cron/compat.h140
-rw-r--r--usr.sbin/cron/cron/config.h87
-rw-r--r--usr.sbin/cron/cron/cron.8149
-rw-r--r--usr.sbin/cron/cron/cron.c435
-rw-r--r--usr.sbin/cron/cron/cron.h292
-rw-r--r--usr.sbin/cron/cron/database.c263
-rw-r--r--usr.sbin/cron/cron/do_command.c548
-rw-r--r--usr.sbin/cron/cron/externs.h145
-rw-r--r--usr.sbin/cron/cron/job.c76
-rw-r--r--usr.sbin/cron/cron/pathnames.h81
-rw-r--r--usr.sbin/cron/cron/popen.c240
-rw-r--r--usr.sbin/cron/cron/user.c128
-rw-r--r--usr.sbin/cron/crontab/Makefile16
-rw-r--r--usr.sbin/cron/crontab/crontab.1125
-rw-r--r--usr.sbin/cron/crontab/crontab.5276
-rw-r--r--usr.sbin/cron/crontab/crontab.c626
-rw-r--r--usr.sbin/cron/doc/CHANGES155
-rw-r--r--usr.sbin/cron/doc/CONVERSION85
-rw-r--r--usr.sbin/cron/doc/FEATURES84
-rw-r--r--usr.sbin/cron/doc/INSTALL87
-rw-r--r--usr.sbin/cron/doc/MAIL475
-rw-r--r--usr.sbin/cron/doc/Makefile.vixie128
-rw-r--r--usr.sbin/cron/doc/README72
-rw-r--r--usr.sbin/cron/doc/README.1ST4
-rw-r--r--usr.sbin/cron/doc/THANKS29
-rw-r--r--usr.sbin/cron/lib/Makefile10
-rw-r--r--usr.sbin/cron/lib/compat.c237
-rw-r--r--usr.sbin/cron/lib/entry.c636
-rw-r--r--usr.sbin/cron/lib/env.c262
-rw-r--r--usr.sbin/cron/lib/misc.c655
-rw-r--r--usr.sbin/crunch/COPYRIGHT25
-rw-r--r--usr.sbin/crunch/Makefile5
-rw-r--r--usr.sbin/crunch/Makefile.inc4
-rw-r--r--usr.sbin/crunch/README88
-rw-r--r--usr.sbin/crunch/crunchgen/Makefile10
-rw-r--r--usr.sbin/crunch/crunchgen/crunched_main.c117
-rw-r--r--usr.sbin/crunch/crunchgen/crunchgen.1448
-rw-r--r--usr.sbin/crunch/crunchgen/crunchgen.c1126
-rw-r--r--usr.sbin/crunch/crunchgen/mkskel.sh15
-rw-r--r--usr.sbin/crunch/crunchide/Makefile23
-rw-r--r--usr.sbin/crunch/crunchide/crunchide.185
-rw-r--r--usr.sbin/crunch/crunchide/crunchide.c268
-rw-r--r--usr.sbin/crunch/crunchide/endian.h57
-rw-r--r--usr.sbin/crunch/crunchide/exec_aout.c196
-rw-r--r--usr.sbin/crunch/crunchide/exec_elf32.c414
-rw-r--r--usr.sbin/crunch/crunchide/exec_elf64.c40
-rw-r--r--usr.sbin/crunch/crunchide/extern.h51
-rw-r--r--usr.sbin/crunch/examples/Makefile33
-rw-r--r--usr.sbin/crunch/examples/filesystem.conf31
-rw-r--r--usr.sbin/crunch/examples/fixit.conf45
-rw-r--r--usr.sbin/crunch/examples/kcopy.conf21
-rw-r--r--usr.sbin/crunch/examples/really-big.conf158
-rw-r--r--usr.sbin/ctm/Makefile5
-rw-r--r--usr.sbin/ctm/Makefile.inc5
-rw-r--r--usr.sbin/ctm/README85
-rw-r--r--usr.sbin/ctm/ctm/Makefile23
-rw-r--r--usr.sbin/ctm/ctm/ctm.1301
-rw-r--r--usr.sbin/ctm/ctm/ctm.5181
-rw-r--r--usr.sbin/ctm/ctm/ctm.c331
-rw-r--r--usr.sbin/ctm/ctm/ctm.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.c301
-rw-r--r--usr.sbin/ctm/ctm/ctm_pass3.c295
-rw-r--r--usr.sbin/ctm/ctm/ctm_passb.c142
-rw-r--r--usr.sbin/ctm/ctm/ctm_syntax.c67
-rw-r--r--usr.sbin/ctm/ctm_dequeue/Makefile11
-rw-r--r--usr.sbin/ctm/ctm_dequeue/ctm_dequeue.c209
-rw-r--r--usr.sbin/ctm/ctm_rmail/Makefile8
-rw-r--r--usr.sbin/ctm/ctm_rmail/ctm_rmail.1487
-rw-r--r--usr.sbin/ctm/ctm_rmail/ctm_rmail.c672
-rw-r--r--usr.sbin/ctm/ctm_rmail/error.c102
-rw-r--r--usr.sbin/ctm/ctm_rmail/error.h5
-rw-r--r--usr.sbin/ctm/ctm_rmail/options.h139
-rw-r--r--usr.sbin/ctm/ctm_smail/Makefile11
-rw-r--r--usr.sbin/ctm/ctm_smail/ctm_smail.c469
-rw-r--r--usr.sbin/ctm/mkCTM/Makefile26
-rw-r--r--usr.sbin/ctm/mkCTM/ctm_conf.cvs-cur9
-rw-r--r--usr.sbin/ctm/mkCTM/ctm_conf.gnats8
-rw-r--r--usr.sbin/ctm/mkCTM/ctm_conf.ports-cur7
-rw-r--r--usr.sbin/ctm/mkCTM/ctm_conf.smp-cur7
-rw-r--r--usr.sbin/ctm/mkCTM/ctm_conf.src-cur9
-rw-r--r--usr.sbin/ctm/mkCTM/ctm_conf.src-special9
-rwxr-xr-xusr.sbin/ctm/mkCTM/dequeue6
-rw-r--r--usr.sbin/ctm/mkCTM/mkCTM186
-rw-r--r--usr.sbin/ctm/mkCTM/mkctm.c596
-rw-r--r--usr.sbin/daemon/Makefile7
-rw-r--r--usr.sbin/daemon/daemon.873
-rw-r--r--usr.sbin/daemon/daemon.c78
-rw-r--r--usr.sbin/dev_mkdb/Makefile9
-rw-r--r--usr.sbin/dev_mkdb/dev_mkdb.891
-rw-r--r--usr.sbin/dev_mkdb/dev_mkdb.c166
-rw-r--r--usr.sbin/devinfo/Makefile11
-rw-r--r--usr.sbin/devinfo/devinfo.868
-rw-r--r--usr.sbin/devinfo/devinfo.c221
-rw-r--r--usr.sbin/digictl/Makefile7
-rw-r--r--usr.sbin/digictl/digictl.8118
-rw-r--r--usr.sbin/digictl/digictl.c171
-rw-r--r--usr.sbin/editmap/Makefile51
-rw-r--r--usr.sbin/edquota/Makefile8
-rw-r--r--usr.sbin/edquota/edquota.8194
-rw-r--r--usr.sbin/edquota/edquota.c770
-rw-r--r--usr.sbin/edquota/pathnames.h39
-rw-r--r--usr.sbin/elf2aout/Makefile8
-rw-r--r--usr.sbin/elf2aout/elf2aout.c162
-rw-r--r--usr.sbin/elf2exe/Makefile7
-rw-r--r--usr.sbin/elf2exe/elf2exe.852
-rw-r--r--usr.sbin/elf2exe/elf2exe.c403
-rw-r--r--usr.sbin/extattrctl/Makefile7
-rw-r--r--usr.sbin/extattrctl/extattrctl.8180
-rw-r--r--usr.sbin/extattrctl/extattrctl.c259
-rw-r--r--usr.sbin/faithd/Makefile23
-rw-r--r--usr.sbin/faithd/README149
-rw-r--r--usr.sbin/faithd/faithd.8420
-rw-r--r--usr.sbin/faithd/faithd.c966
-rw-r--r--usr.sbin/faithd/faithd.h72
-rw-r--r--usr.sbin/faithd/ftp.c1125
-rw-r--r--usr.sbin/faithd/prefix.c360
-rw-r--r--usr.sbin/faithd/prefix.h52
-rw-r--r--usr.sbin/faithd/rsh.c212
-rw-r--r--usr.sbin/faithd/tcp.c304
-rw-r--r--usr.sbin/faithd/test/faithd.rb312
-rw-r--r--usr.sbin/fdcontrol/Makefile12
-rw-r--r--usr.sbin/fdcontrol/fdcontrol.8328
-rw-r--r--usr.sbin/fdcontrol/fdcontrol.c179
-rw-r--r--usr.sbin/fdformat/Makefile11
-rw-r--r--usr.sbin/fdformat/fdformat.1178
-rw-r--r--usr.sbin/fdformat/fdformat.c371
-rw-r--r--usr.sbin/fdread/Makefile6
-rw-r--r--usr.sbin/fdread/fdread.1222
-rw-r--r--usr.sbin/fdread/fdread.c337
-rw-r--r--usr.sbin/fdread/fdutil.c466
-rw-r--r--usr.sbin/fdread/fdutil.h37
-rw-r--r--usr.sbin/fdwrite/Makefile12
-rw-r--r--usr.sbin/fdwrite/fdwrite.1127
-rw-r--r--usr.sbin/fdwrite/fdwrite.c198
-rw-r--r--usr.sbin/getextattr/Makefile8
-rw-r--r--usr.sbin/getextattr/getextattr.8108
-rw-r--r--usr.sbin/getextattr/getextattr.c159
-rw-r--r--usr.sbin/gifconfig/Makefile22
-rw-r--r--usr.sbin/gifconfig/gifconfig.8160
-rw-r--r--usr.sbin/gifconfig/gifconfig.c926
-rw-r--r--usr.sbin/i4b/Makefile7
-rw-r--r--usr.sbin/i4b/Makefile.inc16
-rw-r--r--usr.sbin/i4b/dtmfdecode/Makefile19
-rw-r--r--usr.sbin/i4b/dtmfdecode/dtmfdecode.169
-rw-r--r--usr.sbin/i4b/dtmfdecode/dtmfdecode.c152
-rw-r--r--usr.sbin/i4b/dtmfdecode/dtmfsounds.al.uu2098
-rw-r--r--usr.sbin/i4b/g711conv/Makefile11
-rw-r--r--usr.sbin/i4b/g711conv/g711conv.192
-rw-r--r--usr.sbin/i4b/g711conv/g711conv.c306
-rw-r--r--usr.sbin/i4b/isdnd/Makefile33
-rw-r--r--usr.sbin/i4b/isdnd/alias.c193
-rw-r--r--usr.sbin/i4b/isdnd/config.h61
-rw-r--r--usr.sbin/i4b/isdnd/controller.c525
-rw-r--r--usr.sbin/i4b/isdnd/curses.c891
-rw-r--r--usr.sbin/i4b/isdnd/dial.c161
-rw-r--r--usr.sbin/i4b/isdnd/exec.c407
-rw-r--r--usr.sbin/i4b/isdnd/fsm.c446
-rw-r--r--usr.sbin/i4b/isdnd/holiday.c198
-rw-r--r--usr.sbin/i4b/isdnd/isdnd.8423
-rw-r--r--usr.sbin/i4b/isdnd/isdnd.acct.5107
-rw-r--r--usr.sbin/i4b/isdnd/isdnd.h896
-rw-r--r--usr.sbin/i4b/isdnd/isdnd.rates.5113
-rw-r--r--usr.sbin/i4b/isdnd/isdnd.rc.51070
-rw-r--r--usr.sbin/i4b/isdnd/log.c248
-rw-r--r--usr.sbin/i4b/isdnd/main.c840
-rw-r--r--usr.sbin/i4b/isdnd/monitor.c1284
-rw-r--r--usr.sbin/i4b/isdnd/msghdl.c1340
-rw-r--r--usr.sbin/i4b/isdnd/pathnames.h61
-rw-r--r--usr.sbin/i4b/isdnd/pcause.c230
-rw-r--r--usr.sbin/i4b/isdnd/process.c219
-rw-r--r--usr.sbin/i4b/isdnd/rates.c509
-rw-r--r--usr.sbin/i4b/isdnd/rc_config.c1843
-rw-r--r--usr.sbin/i4b/isdnd/rc_parse.y531
-rw-r--r--usr.sbin/i4b/isdnd/rc_scan.l205
-rw-r--r--usr.sbin/i4b/isdnd/support.c1138
-rw-r--r--usr.sbin/i4b/isdnd/timer.c447
-rw-r--r--usr.sbin/i4b/isdndebug/Makefile7
-rw-r--r--usr.sbin/i4b/isdndebug/isdndebug.8108
-rw-r--r--usr.sbin/i4b/isdndebug/main.c633
-rw-r--r--usr.sbin/i4b/isdndecode/Makefile8
-rw-r--r--usr.sbin/i4b/isdndecode/decode.h76
-rw-r--r--usr.sbin/i4b/isdndecode/facility.c1111
-rw-r--r--usr.sbin/i4b/isdndecode/facility.h180
-rw-r--r--usr.sbin/i4b/isdndecode/isdndecode.8172
-rw-r--r--usr.sbin/i4b/isdndecode/layer1.c82
-rw-r--r--usr.sbin/i4b/isdndecode/layer2.c300
-rw-r--r--usr.sbin/i4b/isdndecode/layer3.c516
-rw-r--r--usr.sbin/i4b/isdndecode/layer3_subr.c1122
-rw-r--r--usr.sbin/i4b/isdndecode/main.c796
-rw-r--r--usr.sbin/i4b/isdndecode/pcause.c330
-rw-r--r--usr.sbin/i4b/isdndecode/pcause.h111
-rw-r--r--usr.sbin/i4b/isdnmonitor/Makefile13
-rw-r--r--usr.sbin/i4b/isdnmonitor/curses.c624
-rw-r--r--usr.sbin/i4b/isdnmonitor/isdnmonitor.8173
-rw-r--r--usr.sbin/i4b/isdnmonitor/main.c1196
-rw-r--r--usr.sbin/i4b/isdnmonitor/monitor.h299
-rw-r--r--usr.sbin/i4b/isdnmonitor/monprivate.h210
-rw-r--r--usr.sbin/i4b/isdnphone/Makefile10
-rw-r--r--usr.sbin/i4b/isdnphone/audio.c159
-rw-r--r--usr.sbin/i4b/isdnphone/defs.h189
-rw-r--r--usr.sbin/i4b/isdnphone/display.c246
-rw-r--r--usr.sbin/i4b/isdnphone/isdn.c191
-rw-r--r--usr.sbin/i4b/isdnphone/isdnphone.880
-rw-r--r--usr.sbin/i4b/isdnphone/main.c471
-rw-r--r--usr.sbin/i4b/isdntel/Makefile10
-rw-r--r--usr.sbin/i4b/isdntel/alias.c141
-rw-r--r--usr.sbin/i4b/isdntel/alias.h51
-rw-r--r--usr.sbin/i4b/isdntel/defs.h157
-rw-r--r--usr.sbin/i4b/isdntel/display.c254
-rw-r--r--usr.sbin/i4b/isdntel/files.c308
-rw-r--r--usr.sbin/i4b/isdntel/isdntel.896
-rw-r--r--usr.sbin/i4b/isdntel/main.c400
-rw-r--r--usr.sbin/i4b/isdntelctl/Makefile7
-rw-r--r--usr.sbin/i4b/isdntelctl/isdntelctl.896
-rw-r--r--usr.sbin/i4b/isdntelctl/main.c228
-rw-r--r--usr.sbin/i4b/isdntest/Makefile10
-rw-r--r--usr.sbin/i4b/isdntest/isdntest.8111
-rw-r--r--usr.sbin/i4b/isdntest/main.c745
-rw-r--r--usr.sbin/i4b/isdntrace/1tr6.c756
-rw-r--r--usr.sbin/i4b/isdntrace/Makefile9
-rw-r--r--usr.sbin/i4b/isdntrace/cable.txt62
-rw-r--r--usr.sbin/i4b/isdntrace/isdntrace.8219
-rw-r--r--usr.sbin/i4b/isdntrace/pcause_1tr6.c166
-rw-r--r--usr.sbin/i4b/isdntrace/pcause_1tr6.h70
-rw-r--r--usr.sbin/i4b/isdntrace/pcause_q850.c330
-rw-r--r--usr.sbin/i4b/isdntrace/pcause_q850.h111
-rw-r--r--usr.sbin/i4b/isdntrace/q921.c268
-rw-r--r--usr.sbin/i4b/isdntrace/q931.c800
-rw-r--r--usr.sbin/i4b/isdntrace/q931_util.c1047
-rw-r--r--usr.sbin/i4b/isdntrace/q932_fac.c1236
-rw-r--r--usr.sbin/i4b/isdntrace/q932_fac.h180
-rw-r--r--usr.sbin/i4b/isdntrace/trace.c850
-rw-r--r--usr.sbin/i4b/isdntrace/trace.h93
-rw-r--r--usr.sbin/i4b/isdntrace/unknownl3.c106
-rw-r--r--usr.sbin/i4b/man/Makefile6
-rw-r--r--usr.sbin/i4b/man/i4b.4110
-rw-r--r--usr.sbin/i4b/man/i4bcapi.459
-rw-r--r--usr.sbin/i4b/man/i4bctl.453
-rw-r--r--usr.sbin/i4b/man/i4bing.469
-rw-r--r--usr.sbin/i4b/man/i4bipr.4100
-rw-r--r--usr.sbin/i4b/man/i4bisppp.4131
-rw-r--r--usr.sbin/i4b/man/i4bq921.452
-rw-r--r--usr.sbin/i4b/man/i4bq931.452
-rw-r--r--usr.sbin/i4b/man/i4brbch.453
-rw-r--r--usr.sbin/i4b/man/i4btel.4136
-rw-r--r--usr.sbin/i4b/man/i4btrc.455
-rw-r--r--usr.sbin/i4b/man/iavc.472
-rw-r--r--usr.sbin/i4b/man/ifpi.464
-rw-r--r--usr.sbin/i4b/man/ifpi2.460
-rw-r--r--usr.sbin/i4b/man/ifpnp.468
-rw-r--r--usr.sbin/i4b/man/ihfc.482
-rw-r--r--usr.sbin/i4b/man/isic.4388
-rw-r--r--usr.sbin/i4b/man/itjc.466
-rw-r--r--usr.sbin/i4b/man/iwic.470
-rw-r--r--usr.sbin/ifmcstat/Makefile11
-rw-r--r--usr.sbin/ifmcstat/ifmcstat.858
-rw-r--r--usr.sbin/ifmcstat/ifmcstat.c271
-rw-r--r--usr.sbin/inetd/Makefile24
-rw-r--r--usr.sbin/inetd/builtins.c817
-rw-r--r--usr.sbin/inetd/inetd.8906
-rw-r--r--usr.sbin/inetd/inetd.c2190
-rw-r--r--usr.sbin/inetd/inetd.h133
-rw-r--r--usr.sbin/inetd/pathnames.h40
-rw-r--r--usr.sbin/iostat/Makefile11
-rw-r--r--usr.sbin/iostat/iostat.8427
-rw-r--r--usr.sbin/iostat/iostat.c764
-rw-r--r--usr.sbin/ipftest/Makefile19
-rw-r--r--usr.sbin/ipresend/Makefile17
-rw-r--r--usr.sbin/ipsend/Makefile23
-rw-r--r--usr.sbin/iptest/Makefile16
-rw-r--r--usr.sbin/jail/Makefile8
-rw-r--r--usr.sbin/jail/jail.8424
-rw-r--r--usr.sbin/jail/jail.c53
-rw-r--r--usr.sbin/kbdcontrol/Makefile13
-rw-r--r--usr.sbin/kbdcontrol/kbdcontrol.1235
-rw-r--r--usr.sbin/kbdcontrol/kbdcontrol.c1130
-rw-r--r--usr.sbin/kbdcontrol/kbdmap.5313
-rw-r--r--usr.sbin/kbdcontrol/lex.h72
-rw-r--r--usr.sbin/kbdcontrol/lex.l148
-rw-r--r--usr.sbin/kbdcontrol/path.h4
-rw-r--r--usr.sbin/kbdmap/Languages.phrases37
-rw-r--r--usr.sbin/kbdmap/Makefile8
-rw-r--r--usr.sbin/kbdmap/TODO6
-rw-r--r--usr.sbin/kbdmap/kbdmap.1152
-rw-r--r--usr.sbin/kbdmap/kbdmap.c842
-rw-r--r--usr.sbin/kbdmap/kbdmap.h34
-rw-r--r--usr.sbin/kernbb/Makefile10
-rw-r--r--usr.sbin/kernbb/kernbb.872
-rw-r--r--usr.sbin/kernbb/kernbb.c139
-rw-r--r--usr.sbin/keyadmin/Makefile7
-rw-r--r--usr.sbin/keyadmin/keyadmin.8246
-rw-r--r--usr.sbin/keyadmin/keyadmin.c1251
-rw-r--r--usr.sbin/keyadmin/keys18
-rw-r--r--usr.sbin/keyserv/Makefile27
-rw-r--r--usr.sbin/keyserv/crypt_server.c305
-rw-r--r--usr.sbin/keyserv/keyserv.878
-rw-r--r--usr.sbin/keyserv/keyserv.c812
-rw-r--r--usr.sbin/keyserv/keyserv.h20
-rw-r--r--usr.sbin/keyserv/setkey.c550
-rw-r--r--usr.sbin/kgmon/Makefile16
-rw-r--r--usr.sbin/kgmon/kgmon.8132
-rw-r--r--usr.sbin/kgmon/kgmon.c533
-rw-r--r--usr.sbin/kgzip/Makefile9
-rw-r--r--usr.sbin/kgzip/aouthdr.c79
-rw-r--r--usr.sbin/kgzip/aouthdr.h59
-rw-r--r--usr.sbin/kgzip/elfhdr.c163
-rw-r--r--usr.sbin/kgzip/elfhdr.h82
-rw-r--r--usr.sbin/kgzip/kgz.h57
-rw-r--r--usr.sbin/kgzip/kgzcmp.c227
-rw-r--r--usr.sbin/kgzip/kgzip.8141
-rw-r--r--usr.sbin/kgzip/kgzip.c176
-rw-r--r--usr.sbin/kgzip/kgzip.h51
-rw-r--r--usr.sbin/kgzip/kgzld.c100
-rw-r--r--usr.sbin/kgzip/xio.c121
-rw-r--r--usr.sbin/kldxref/Makefile8
-rw-r--r--usr.sbin/kldxref/ef.c385
-rw-r--r--usr.sbin/kldxref/ef.h43
-rw-r--r--usr.sbin/kldxref/fileformat40
-rw-r--r--usr.sbin/kldxref/kldxref.895
-rw-r--r--usr.sbin/kldxref/kldxref.c346
-rw-r--r--usr.sbin/lastlogin/Makefile8
-rw-r--r--usr.sbin/lastlogin/lastlogin.876
-rw-r--r--usr.sbin/lastlogin/lastlogin.c134
-rw-r--r--usr.sbin/lpr/Makefile12
-rw-r--r--usr.sbin/lpr/Makefile.inc12
-rw-r--r--usr.sbin/lpr/SMM.doc/0.t68
-rw-r--r--usr.sbin/lpr/SMM.doc/1.t77
-rw-r--r--usr.sbin/lpr/SMM.doc/2.t141
-rw-r--r--usr.sbin/lpr/SMM.doc/3.t73
-rw-r--r--usr.sbin/lpr/SMM.doc/4.t206
-rw-r--r--usr.sbin/lpr/SMM.doc/5.t116
-rw-r--r--usr.sbin/lpr/SMM.doc/6.t94
-rw-r--r--usr.sbin/lpr/SMM.doc/7.t226
-rw-r--r--usr.sbin/lpr/SMM.doc/Makefile12
-rw-r--r--usr.sbin/lpr/SMM.doc/spell.ok70
-rw-r--r--usr.sbin/lpr/chkprintcap/Makefile14
-rw-r--r--usr.sbin/lpr/chkprintcap/chkprintcap.894
-rw-r--r--usr.sbin/lpr/chkprintcap/chkprintcap.c317
-rw-r--r--usr.sbin/lpr/chkprintcap/skimprintcap.c262
-rw-r--r--usr.sbin/lpr/chkprintcap/skimprintcap.h44
-rw-r--r--usr.sbin/lpr/common_source/Makefile13
-rw-r--r--usr.sbin/lpr/common_source/common.c703
-rw-r--r--usr.sbin/lpr/common_source/ctlinfo.c868
-rw-r--r--usr.sbin/lpr/common_source/ctlinfo.h71
-rw-r--r--usr.sbin/lpr/common_source/displayq.c538
-rw-r--r--usr.sbin/lpr/common_source/lp.h302
-rw-r--r--usr.sbin/lpr/common_source/lp.local.h81
-rw-r--r--usr.sbin/lpr/common_source/net.c301
-rw-r--r--usr.sbin/lpr/common_source/pathnames.h51
-rw-r--r--usr.sbin/lpr/common_source/printcap.c449
-rw-r--r--usr.sbin/lpr/common_source/request.c80
-rw-r--r--usr.sbin/lpr/common_source/rmjob.c395
-rw-r--r--usr.sbin/lpr/common_source/startdaemon.c111
-rw-r--r--usr.sbin/lpr/filters.ru/Makefile8
-rw-r--r--usr.sbin/lpr/filters.ru/Makefile.inc3
-rw-r--r--usr.sbin/lpr/filters.ru/bjc-240.sh.sample64
-rw-r--r--usr.sbin/lpr/filters.ru/koi2855/Makefile6
-rw-r--r--usr.sbin/lpr/filters.ru/koi2855/koi2855.c102
-rw-r--r--usr.sbin/lpr/filters.ru/koi2alt/Makefile6
-rw-r--r--usr.sbin/lpr/filters.ru/koi2alt/koi2alt.c101
-rw-r--r--usr.sbin/lpr/filters/Makefile9
-rw-r--r--usr.sbin/lpr/filters/lpf.c220
-rw-r--r--usr.sbin/lpr/lp/Makefile8
-rw-r--r--usr.sbin/lpr/lp/lp.1117
-rw-r--r--usr.sbin/lpr/lp/lp.sh75
-rw-r--r--usr.sbin/lpr/lpc/Makefile17
-rw-r--r--usr.sbin/lpr/lpc/cmds.c1305
-rw-r--r--usr.sbin/lpr/lpc/cmdtab.c92
-rw-r--r--usr.sbin/lpr/lpc/extern.h70
-rw-r--r--usr.sbin/lpr/lpc/lpc.8229
-rw-r--r--usr.sbin/lpr/lpc/lpc.c376
-rw-r--r--usr.sbin/lpr/lpc/lpc.h55
-rw-r--r--usr.sbin/lpr/lpd/Makefile13
-rw-r--r--usr.sbin/lpr/lpd/extern.h50
-rw-r--r--usr.sbin/lpr/lpd/lpd.8317
-rw-r--r--usr.sbin/lpr/lpd/lpd.c947
-rw-r--r--usr.sbin/lpr/lpd/lpdchar.c1071
-rw-r--r--usr.sbin/lpr/lpd/modes.c234
-rw-r--r--usr.sbin/lpr/lpd/printjob.c1892
-rw-r--r--usr.sbin/lpr/lpd/recvjob.c403
-rw-r--r--usr.sbin/lpr/lpq/Makefile16
-rw-r--r--usr.sbin/lpr/lpq/lpq.1140
-rw-r--r--usr.sbin/lpr/lpq/lpq.c198
-rw-r--r--usr.sbin/lpr/lpr/Makefile19
-rw-r--r--usr.sbin/lpr/lpr/lpr.1318
-rw-r--r--usr.sbin/lpr/lpr/lpr.c890
-rw-r--r--usr.sbin/lpr/lpr/printcap.5426
-rw-r--r--usr.sbin/lpr/lprm/Makefile18
-rw-r--r--usr.sbin/lpr/lprm/lprm.1149
-rw-r--r--usr.sbin/lpr/lprm/lprm.c164
-rw-r--r--usr.sbin/lpr/lptest/Makefile6
-rw-r--r--usr.sbin/lpr/lptest/lptest.177
-rw-r--r--usr.sbin/lpr/lptest/lptest.c85
-rw-r--r--usr.sbin/lpr/pac/Makefile14
-rw-r--r--usr.sbin/lpr/pac/pac.8107
-rw-r--r--usr.sbin/lpr/pac/pac.c453
-rw-r--r--usr.sbin/lptcontrol/Makefile8
-rw-r--r--usr.sbin/lptcontrol/lptcontrol.883
-rw-r--r--usr.sbin/lptcontrol/lptcontrol.c97
-rw-r--r--usr.sbin/mailstats/Makefile45
-rw-r--r--usr.sbin/mailwrapper/Makefile21
-rw-r--r--usr.sbin/mailwrapper/mailwrapper.8151
-rw-r--r--usr.sbin/mailwrapper/mailwrapper.c188
-rw-r--r--usr.sbin/mailwrapper/pathnames.h35
-rw-r--r--usr.sbin/makemap/Makefile52
-rw-r--r--usr.sbin/manctl/Makefile6
-rw-r--r--usr.sbin/manctl/manctl.857
-rw-r--r--usr.sbin/manctl/manctl.sh380
-rw-r--r--usr.sbin/memcontrol/Makefile6
-rw-r--r--usr.sbin/memcontrol/memcontrol.8113
-rw-r--r--usr.sbin/memcontrol/memcontrol.c342
-rw-r--r--usr.sbin/mergemaster/Makefile9
-rw-r--r--usr.sbin/mergemaster/mergemaster.8380
-rwxr-xr-xusr.sbin/mergemaster/mergemaster.sh1032
-rw-r--r--usr.sbin/mixer/Makefile8
-rw-r--r--usr.sbin/mixer/mixer.8151
-rw-r--r--usr.sbin/mixer/mixer.c251
-rw-r--r--usr.sbin/mld6query/Makefile23
-rw-r--r--usr.sbin/mld6query/mld6.c270
-rw-r--r--usr.sbin/mld6query/mld6query.889
-rw-r--r--usr.sbin/mlxcontrol/Makefile9
-rw-r--r--usr.sbin/mlxcontrol/command.c712
-rw-r--r--usr.sbin/mlxcontrol/config.c157
-rw-r--r--usr.sbin/mlxcontrol/interface.c287
-rw-r--r--usr.sbin/mlxcontrol/mlxcontrol.8148
-rw-r--r--usr.sbin/mlxcontrol/mlxcontrol.h89
-rw-r--r--usr.sbin/mlxcontrol/util.c172
-rw-r--r--usr.sbin/mount_nwfs/Makefile16
-rw-r--r--usr.sbin/mount_nwfs/mount_nwfs.8230
-rw-r--r--usr.sbin/mount_nwfs/mount_nwfs.c376
-rw-r--r--usr.sbin/mount_portalfs/Makefile15
-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.8152
-rw-r--r--usr.sbin/mount_portalfs/mount_portalfs.c295
-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/mount_smbfs/Makefile23
-rw-r--r--usr.sbin/mountd/Makefile8
-rw-r--r--usr.sbin/mountd/exports.5323
-rw-r--r--usr.sbin/mountd/mountd.8150
-rw-r--r--usr.sbin/mountd/mountd.c2444
-rw-r--r--usr.sbin/mountd/netgroup.5194
-rw-r--r--usr.sbin/mountd/pathnames.h39
-rw-r--r--usr.sbin/moused/Makefile9
-rw-r--r--usr.sbin/moused/moused.8679
-rw-r--r--usr.sbin/moused/moused.c2976
-rw-r--r--usr.sbin/mptable/Makefile5
-rw-r--r--usr.sbin/mptable/mptable.169
-rw-r--r--usr.sbin/mptable/mptable.c1115
-rw-r--r--usr.sbin/mrouted/LICENSE48
-rw-r--r--usr.sbin/mrouted/Makefile5
-rw-r--r--usr.sbin/mrouted/Makefile.inc11
-rw-r--r--usr.sbin/mrouted/RELEASE493
-rw-r--r--usr.sbin/mrouted/VERSION1
-rw-r--r--usr.sbin/mrouted/callout.c250
-rw-r--r--usr.sbin/mrouted/cfparse.y932
-rw-r--r--usr.sbin/mrouted/common/Makefile12
-rw-r--r--usr.sbin/mrouted/config.c175
-rw-r--r--usr.sbin/mrouted/defs.h413
-rw-r--r--usr.sbin/mrouted/dvmrp.h172
-rw-r--r--usr.sbin/mrouted/icmp.c225
-rw-r--r--usr.sbin/mrouted/igmp.c447
-rw-r--r--usr.sbin/mrouted/igmpv2.h42
-rw-r--r--usr.sbin/mrouted/inet.c235
-rw-r--r--usr.sbin/mrouted/ipip.c145
-rw-r--r--usr.sbin/mrouted/kern.c344
-rw-r--r--usr.sbin/mrouted/main.c1060
-rw-r--r--usr.sbin/mrouted/map-mbone.891
-rw-r--r--usr.sbin/mrouted/map-mbone/Makefile15
-rw-r--r--usr.sbin/mrouted/mapper.c1046
-rw-r--r--usr.sbin/mrouted/mrinfo.887
-rw-r--r--usr.sbin/mrouted/mrinfo.c634
-rw-r--r--usr.sbin/mrouted/mrinfo/Makefile16
-rw-r--r--usr.sbin/mrouted/mrouted.8598
-rw-r--r--usr.sbin/mrouted/mrouted.conf44
-rw-r--r--usr.sbin/mrouted/mrouted/Makefile19
-rw-r--r--usr.sbin/mrouted/mtrace.8542
-rw-r--r--usr.sbin/mrouted/mtrace.c3175
-rw-r--r--usr.sbin/mrouted/mtrace.h89
-rw-r--r--usr.sbin/mrouted/mtrace/Makefile13
-rw-r--r--usr.sbin/mrouted/pathnames.h26
-rw-r--r--usr.sbin/mrouted/prune.c2619
-rw-r--r--usr.sbin/mrouted/prune.h152
-rw-r--r--usr.sbin/mrouted/route.c1475
-rw-r--r--usr.sbin/mrouted/route.h53
-rw-r--r--usr.sbin/mrouted/rsrr.c485
-rw-r--r--usr.sbin/mrouted/rsrr.h139
-rw-r--r--usr.sbin/mrouted/rsrr_var.h41
-rw-r--r--usr.sbin/mrouted/testrsrr/Makefile14
-rw-r--r--usr.sbin/mrouted/testrsrr/testrsrr.c124
-rw-r--r--usr.sbin/mrouted/vif.c1862
-rw-r--r--usr.sbin/mrouted/vif.h237
-rw-r--r--usr.sbin/mtest/Makefile6
-rw-r--r--usr.sbin/mtest/mtest.855
-rw-r--r--usr.sbin/mtest/mtest.c230
-rw-r--r--usr.sbin/mtree/Makefile19
-rw-r--r--usr.sbin/mtree/compare.c368
-rw-r--r--usr.sbin/mtree/create.c425
-rw-r--r--usr.sbin/mtree/excludes.c111
-rw-r--r--usr.sbin/mtree/extern.h50
-rw-r--r--usr.sbin/mtree/misc.c129
-rw-r--r--usr.sbin/mtree/mtree.8360
-rw-r--r--usr.sbin/mtree/mtree.c180
-rw-r--r--usr.sbin/mtree/mtree.h99
-rw-r--r--usr.sbin/mtree/spec.c323
-rw-r--r--usr.sbin/mtree/verify.c244
-rw-r--r--usr.sbin/named.reload/Makefile15
-rw-r--r--usr.sbin/named.reload/named.reload.870
-rw-r--r--usr.sbin/named.reload/named.reload.sh7
-rw-r--r--usr.sbin/named.restart/Makefile15
-rw-r--r--usr.sbin/named.restart/named.restart.877
-rw-r--r--usr.sbin/named.restart/named.restart.sh13
-rw-r--r--usr.sbin/named/Makefile45
-rw-r--r--usr.sbin/named/Makefile.inc59
-rw-r--r--usr.sbin/named/Makefile.maninc58
-rw-r--r--usr.sbin/ndc/Makefile14
-rw-r--r--usr.sbin/ndp/Makefile25
-rw-r--r--usr.sbin/ndp/gnuc.h2
-rw-r--r--usr.sbin/ndp/ndp.8182
-rw-r--r--usr.sbin/ndp/ndp.c1528
-rw-r--r--usr.sbin/newsyslog/Makefile8
-rw-r--r--usr.sbin/newsyslog/newsyslog.8390
-rw-r--r--usr.sbin/newsyslog/newsyslog.c1120
-rw-r--r--usr.sbin/newsyslog/pathnames.h28
-rw-r--r--usr.sbin/nfsd/Makefile8
-rw-r--r--usr.sbin/nfsd/nfsd.8191
-rw-r--r--usr.sbin/nfsd/nfsd.c845
-rw-r--r--usr.sbin/ngctl/Makefile12
-rw-r--r--usr.sbin/ngctl/config.c103
-rw-r--r--usr.sbin/ngctl/connect.c86
-rw-r--r--usr.sbin/ngctl/debug.c78
-rw-r--r--usr.sbin/ngctl/list.c114
-rw-r--r--usr.sbin/ngctl/main.c507
-rw-r--r--usr.sbin/ngctl/mkpeer.c85
-rw-r--r--usr.sbin/ngctl/msg.c146
-rw-r--r--usr.sbin/ngctl/name.c75
-rw-r--r--usr.sbin/ngctl/ngctl.8139
-rw-r--r--usr.sbin/ngctl/ngctl.h101
-rw-r--r--usr.sbin/ngctl/rmhook.c82
-rw-r--r--usr.sbin/ngctl/show.c131
-rw-r--r--usr.sbin/ngctl/shutdown.c75
-rw-r--r--usr.sbin/ngctl/status.c95
-rw-r--r--usr.sbin/ngctl/types.c92
-rw-r--r--usr.sbin/ngctl/write.c111
-rw-r--r--usr.sbin/nghook/Makefile11
-rw-r--r--usr.sbin/nghook/main.c238
-rw-r--r--usr.sbin/nghook/nghook.8106
-rw-r--r--usr.sbin/nologin/Makefile7
-rw-r--r--usr.sbin/nologin/nologin.572
-rw-r--r--usr.sbin/nologin/nologin.861
-rw-r--r--usr.sbin/nologin/nologin.sh39
-rw-r--r--usr.sbin/nslookup/Makefile22
-rw-r--r--usr.sbin/nsupdate/Makefile11
-rw-r--r--usr.sbin/ntp/Makefile9
-rw-r--r--usr.sbin/ntp/Makefile.inc25
-rw-r--r--usr.sbin/ntp/config.h994
-rw-r--r--usr.sbin/ntp/doc/Makefile29
-rw-r--r--usr.sbin/ntp/doc/ntp-genkeys.8206
-rw-r--r--usr.sbin/ntp/doc/ntp.conf.52093
-rw-r--r--usr.sbin/ntp/doc/ntp.keys.5120
-rw-r--r--usr.sbin/ntp/doc/ntpd.8585
-rw-r--r--usr.sbin/ntp/doc/ntpdate.8264
-rw-r--r--usr.sbin/ntp/doc/ntpdc.8720
-rw-r--r--usr.sbin/ntp/doc/ntpq.8744
-rw-r--r--usr.sbin/ntp/doc/ntptime.867
-rw-r--r--usr.sbin/ntp/doc/ntptrace.873
-rw-r--r--usr.sbin/ntp/libntp/Makefile28
-rw-r--r--usr.sbin/ntp/libparse/Makefile15
-rw-r--r--usr.sbin/ntp/ntp-genkeys/Makefile15
-rw-r--r--usr.sbin/ntp/ntpd/Makefile38
-rw-r--r--usr.sbin/ntp/ntpdate/Makefile19
-rw-r--r--usr.sbin/ntp/ntpdc/Makefile22
-rw-r--r--usr.sbin/ntp/ntpq/Makefile21
-rw-r--r--usr.sbin/ntp/ntptime/Makefile14
-rw-r--r--usr.sbin/ntp/ntptimeset/Makefile19
-rw-r--r--usr.sbin/ntp/ntptrace/Makefile19
-rwxr-xr-xusr.sbin/ntp/scripts/mkver41
-rwxr-xr-xusr.sbin/ntp/scripts/ntpver8
-rw-r--r--usr.sbin/pccard/Makefile6
-rw-r--r--usr.sbin/pccard/Makefile.inc5
-rw-r--r--usr.sbin/pccard/pccardc/Makefile13
-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.8271
-rw-r--r--usr.sbin/pccard/pccardc/pccardc.c97
-rw-r--r--usr.sbin/pccard/pccardc/pccardmem.c71
-rw-r--r--usr.sbin/pccard/pccardc/power.c82
-rw-r--r--usr.sbin/pccard/pccardc/printcis.c1107
-rw-r--r--usr.sbin/pccard/pccardc/rdattr.c84
-rw-r--r--usr.sbin/pccard/pccardc/rdmap.c109
-rw-r--r--usr.sbin/pccard/pccardc/rdreg.c82
-rw-r--r--usr.sbin/pccard/pccardc/wrattr.c80
-rw-r--r--usr.sbin/pccard/pccardc/wrreg.c73
-rw-r--r--usr.sbin/pccard/pccardd/Makefile14
-rw-r--r--usr.sbin/pccard/pccardd/cardd.c1017
-rw-r--r--usr.sbin/pccard/pccardd/cardd.h207
-rw-r--r--usr.sbin/pccard/pccardd/file.c1093
-rw-r--r--usr.sbin/pccard/pccardd/pccard.conf.5298
-rw-r--r--usr.sbin/pccard/pccardd/pccardd.8180
-rw-r--r--usr.sbin/pccard/pccardd/pccardd.c279
-rw-r--r--usr.sbin/pccard/pccardd/readcis.c788
-rw-r--r--usr.sbin/pccard/pccardd/readcis.h146
-rw-r--r--usr.sbin/pccard/pccardd/server.c187
-rw-r--r--usr.sbin/pccard/pccardd/util.c273
-rw-r--r--usr.sbin/pciconf/Makefile9
-rw-r--r--usr.sbin/pciconf/pathnames.h3
-rw-r--r--usr.sbin/pciconf/pciconf.8214
-rw-r--r--usr.sbin/pciconf/pciconf.c536
-rw-r--r--usr.sbin/pcvt/Makefile9
-rw-r--r--usr.sbin/pcvt/Makefile.inc5
-rw-r--r--usr.sbin/pcvt/Misc/Doc/Acknowledgements111
-rw-r--r--usr.sbin/pcvt/Misc/Doc/Bibliography189
-rw-r--r--usr.sbin/pcvt/Misc/Doc/CharGen149
-rw-r--r--usr.sbin/pcvt/Misc/Doc/Charsets99
-rw-r--r--usr.sbin/pcvt/Misc/Doc/EscapeSequences268
-rw-r--r--usr.sbin/pcvt/Misc/Doc/Keyboard.HP286
-rw-r--r--usr.sbin/pcvt/Misc/Doc/Keyboard.VT231
-rw-r--r--usr.sbin/pcvt/Misc/Doc/Makefile9
-rw-r--r--usr.sbin/pcvt/Misc/Doc/NotesAndHints321
-rw-r--r--usr.sbin/pcvt/Misc/Etc/Makefile7
-rw-r--r--usr.sbin/pcvt/Misc/Etc/Termcap284
-rw-r--r--usr.sbin/pcvt/Misc/Etc/Terminfo41
-rw-r--r--usr.sbin/pcvt/Misc/Etc/pcvt.el19
-rw-r--r--usr.sbin/pcvt/Misc/Etc/pcvt.sh163
-rw-r--r--usr.sbin/pcvt/Misc/Etc/xmodmap-german117
-rw-r--r--usr.sbin/pcvt/Misc/Makefile8
-rw-r--r--usr.sbin/pcvt/Misc/Makefile.inc4
-rw-r--r--usr.sbin/pcvt/Misc/README.FIRST184
-rw-r--r--usr.sbin/pcvt/cursor/Makefile5
-rw-r--r--usr.sbin/pcvt/cursor/cursor.175
-rw-r--r--usr.sbin/pcvt/cursor/cursor.c137
-rw-r--r--usr.sbin/pcvt/demo/Makefile56
-rw-r--r--usr.sbin/pcvt/demo/README20
-rw-r--r--usr.sbin/pcvt/demo/chardemo.vt.gz.uu54
-rw-r--r--usr.sbin/pcvt/demo/colors.vt.gz.uu16
-rw-r--r--usr.sbin/pcvt/demo/cowscene.vt.gz.uu90
-rw-r--r--usr.sbin/pcvt/demo/outerlimit.vt.gz.uu193
-rw-r--r--usr.sbin/pcvt/demo/playvt.c113
-rw-r--r--usr.sbin/pcvt/demo/sgr.vt.gz.uu12
-rw-r--r--usr.sbin/pcvt/demo/twzone.vt.gz.uu350
-rw-r--r--usr.sbin/pcvt/demo/xmas.vt.gz.uu110
-rw-r--r--usr.sbin/pcvt/fed/Makefile9
-rw-r--r--usr.sbin/pcvt/fed/edit.c331
-rw-r--r--usr.sbin/pcvt/fed/fed.159
-rw-r--r--usr.sbin/pcvt/fed/fed.c159
-rw-r--r--usr.sbin/pcvt/fed/fed.h123
-rw-r--r--usr.sbin/pcvt/fed/misc.c344
-rw-r--r--usr.sbin/pcvt/fed/select.c325
-rw-r--r--usr.sbin/pcvt/fontedit/Makefile5
-rw-r--r--usr.sbin/pcvt/fontedit/README36
-rw-r--r--usr.sbin/pcvt/fontedit/fontedit.192
-rw-r--r--usr.sbin/pcvt/fontedit/fontedit.c926
-rw-r--r--usr.sbin/pcvt/fonts/COPYRIGHT38
-rw-r--r--usr.sbin/pcvt/fonts/Makefile14
-rw-r--r--usr.sbin/pcvt/fonts/vt100pc.814.uu83
-rw-r--r--usr.sbin/pcvt/fonts/vt100sg.814.uu83
-rw-r--r--usr.sbin/pcvt/fonts/vt220h.808.uu49
-rw-r--r--usr.sbin/pcvt/fonts/vt220h.810.uu60
-rw-r--r--usr.sbin/pcvt/fonts/vt220h.814.uu83
-rw-r--r--usr.sbin/pcvt/fonts/vt220h.816.uu95
-rw-r--r--usr.sbin/pcvt/fonts/vt220l.808.uu49
-rw-r--r--usr.sbin/pcvt/fonts/vt220l.810.uu60
-rw-r--r--usr.sbin/pcvt/fonts/vt220l.814.uu83
-rw-r--r--usr.sbin/pcvt/fonts/vt220l.816.uu95
-rw-r--r--usr.sbin/pcvt/ispcvt/Makefile6
-rw-r--r--usr.sbin/pcvt/ispcvt/ispcvt.899
-rw-r--r--usr.sbin/pcvt/ispcvt/ispcvt.c250
-rw-r--r--usr.sbin/pcvt/kbdio/Makefile23
-rw-r--r--usr.sbin/pcvt/kbdio/kbdio.y329
-rw-r--r--usr.sbin/pcvt/kbdio/lex.l98
-rw-r--r--usr.sbin/pcvt/kcon/Makefile9
-rw-r--r--usr.sbin/pcvt/kcon/kcon.1129
-rw-r--r--usr.sbin/pcvt/kcon/kcon.c743
-rw-r--r--usr.sbin/pcvt/keycap/Makefile24
-rw-r--r--usr.sbin/pcvt/keycap/keycap.3125
-rw-r--r--usr.sbin/pcvt/keycap/keycap.c383
-rw-r--r--usr.sbin/pcvt/keycap/keycap.h49
-rw-r--r--usr.sbin/pcvt/keycap/keycap.src627
-rw-r--r--usr.sbin/pcvt/keycap/man5/keycap.5129
-rw-r--r--usr.sbin/pcvt/loadfont/Makefile5
-rw-r--r--usr.sbin/pcvt/loadfont/loadfont.187
-rw-r--r--usr.sbin/pcvt/loadfont/loadfont.c306
-rw-r--r--usr.sbin/pcvt/scon/Makefile5
-rw-r--r--usr.sbin/pcvt/scon/scon.1220
-rw-r--r--usr.sbin/pcvt/scon/scon.c782
-rw-r--r--usr.sbin/pcvt/userkeys/Makefile5
-rw-r--r--usr.sbin/pcvt/userkeys/vt220keys.1175
-rw-r--r--usr.sbin/pcvt/userkeys/vt220keys.c287
-rw-r--r--usr.sbin/pcvt/vgaio/CAUTION28
-rw-r--r--usr.sbin/pcvt/vgaio/Makefile24
-rw-r--r--usr.sbin/pcvt/vgaio/lex.l82
-rw-r--r--usr.sbin/pcvt/vgaio/vgaio.8139
-rw-r--r--usr.sbin/pcvt/vgaio/vgaio.h60
-rw-r--r--usr.sbin/pcvt/vgaio/vgaio.y251
-rw-r--r--usr.sbin/pcvt/vttest/Makefile8
-rw-r--r--usr.sbin/pcvt/vttest/README59
-rw-r--r--usr.sbin/pcvt/vttest/esc.c403
-rw-r--r--usr.sbin/pcvt/vttest/header.h45
-rw-r--r--usr.sbin/pcvt/vttest/main.c2018
-rw-r--r--usr.sbin/pcvt/vttest/vttest.121
-rw-r--r--usr.sbin/periodic/Makefile6
-rw-r--r--usr.sbin/periodic/periodic.8252
-rw-r--r--usr.sbin/periodic/periodic.sh106
-rw-r--r--usr.sbin/pkg_install/Makefile10
-rw-r--r--usr.sbin/pkg_install/Makefile.inc15
-rw-r--r--usr.sbin/pkg_install/README8
-rw-r--r--usr.sbin/pkg_install/add/Makefile18
-rw-r--r--usr.sbin/pkg_install/add/add.h44
-rw-r--r--usr.sbin/pkg_install/add/extract.c262
-rw-r--r--usr.sbin/pkg_install/add/futil.c97
-rw-r--r--usr.sbin/pkg_install/add/main.c276
-rw-r--r--usr.sbin/pkg_install/add/perform.c562
-rw-r--r--usr.sbin/pkg_install/add/pkg_add.1483
-rw-r--r--usr.sbin/pkg_install/create/Makefile18
-rw-r--r--usr.sbin/pkg_install/create/create.h53
-rw-r--r--usr.sbin/pkg_install/create/main.c205
-rw-r--r--usr.sbin/pkg_install/create/perform.c464
-rw-r--r--usr.sbin/pkg_install/create/pkg_create.1545
-rw-r--r--usr.sbin/pkg_install/create/pl.c257
-rw-r--r--usr.sbin/pkg_install/delete/Makefile18
-rw-r--r--usr.sbin/pkg_install/delete/delete.h36
-rw-r--r--usr.sbin/pkg_install/delete/main.c157
-rw-r--r--usr.sbin/pkg_install/delete/perform.c383
-rw-r--r--usr.sbin/pkg_install/delete/pkg_delete.1276
-rw-r--r--usr.sbin/pkg_install/info/Makefile18
-rw-r--r--usr.sbin/pkg_install/info/info.h79
-rw-r--r--usr.sbin/pkg_install/info/main.c237
-rw-r--r--usr.sbin/pkg_install/info/perform.c437
-rw-r--r--usr.sbin/pkg_install/info/pkg_info.1225
-rw-r--r--usr.sbin/pkg_install/info/show.c340
-rw-r--r--usr.sbin/pkg_install/lib/Makefile12
-rw-r--r--usr.sbin/pkg_install/lib/deps.c195
-rw-r--r--usr.sbin/pkg_install/lib/exec.c106
-rw-r--r--usr.sbin/pkg_install/lib/file.c534
-rw-r--r--usr.sbin/pkg_install/lib/global.c31
-rw-r--r--usr.sbin/pkg_install/lib/lib.h215
-rw-r--r--usr.sbin/pkg_install/lib/match.c343
-rw-r--r--usr.sbin/pkg_install/lib/msg.c75
-rw-r--r--usr.sbin/pkg_install/lib/pen.c176
-rw-r--r--usr.sbin/pkg_install/lib/plist.c571
-rw-r--r--usr.sbin/pkg_install/lib/str.c108
-rw-r--r--usr.sbin/pkg_install/lib/version.c171
-rw-r--r--usr.sbin/pkg_install/sign/Makefile14
-rw-r--r--usr.sbin/pkg_install/sign/README55
-rw-r--r--usr.sbin/pkg_install/sign/check.c119
-rw-r--r--usr.sbin/pkg_install/sign/common.c90
-rw-r--r--usr.sbin/pkg_install/sign/extern.h100
-rw-r--r--usr.sbin/pkg_install/sign/gzip.c319
-rw-r--r--usr.sbin/pkg_install/sign/gzip.h95
-rw-r--r--usr.sbin/pkg_install/sign/main.c185
-rw-r--r--usr.sbin/pkg_install/sign/pgp.h25
-rw-r--r--usr.sbin/pkg_install/sign/pgp_check.c196
-rw-r--r--usr.sbin/pkg_install/sign/pgp_sign.c280
-rw-r--r--usr.sbin/pkg_install/sign/pkg_sign.1208
-rw-r--r--usr.sbin/pkg_install/sign/sha1.c224
-rw-r--r--usr.sbin/pkg_install/sign/sign.c145
-rw-r--r--usr.sbin/pkg_install/sign/stand.c57
-rw-r--r--usr.sbin/pkg_install/sign/stand.h28
-rw-r--r--usr.sbin/pkg_install/sign/x509.c426
-rwxr-xr-xusr.sbin/pkg_install/tkpkg177
-rw-r--r--usr.sbin/pkg_install/version/Makefile21
-rw-r--r--usr.sbin/pkg_install/version/main.c89
-rw-r--r--usr.sbin/pkg_install/version/perform.c281
-rw-r--r--usr.sbin/pkg_install/version/pkg_version.1202
-rwxr-xr-xusr.sbin/pkg_install/version/test-pkg_version.sh75
-rw-r--r--usr.sbin/pkg_install/version/version.h44
-rw-r--r--usr.sbin/pnpinfo/Makefile19
-rw-r--r--usr.sbin/ppp/Makefile108
-rw-r--r--usr.sbin/ppp/README.changes140
-rw-r--r--usr.sbin/ppp/README.devel47
-rw-r--r--usr.sbin/ppp/README.nat378
-rw-r--r--usr.sbin/ppp/acf.c115
-rw-r--r--usr.sbin/ppp/acf.h33
-rw-r--r--usr.sbin/ppp/arp.c317
-rw-r--r--usr.sbin/ppp/arp.h36
-rw-r--r--usr.sbin/ppp/async.c214
-rw-r--r--usr.sbin/ppp/async.h53
-rw-r--r--usr.sbin/ppp/atm.c237
-rw-r--r--usr.sbin/ppp/atm.h35
-rw-r--r--usr.sbin/ppp/auth.c433
-rw-r--r--usr.sbin/ppp/auth.h70
-rw-r--r--usr.sbin/ppp/bundle.c2008
-rw-r--r--usr.sbin/ppp/bundle.h207
-rw-r--r--usr.sbin/ppp/cbcp.c759
-rw-r--r--usr.sbin/ppp/cbcp.h65
-rw-r--r--usr.sbin/ppp/ccp.c818
-rw-r--r--usr.sbin/ppp/ccp.h165
-rw-r--r--usr.sbin/ppp/chap.c962
-rw-r--r--usr.sbin/ppp/chap.h75
-rw-r--r--usr.sbin/ppp/chap_ms.c417
-rw-r--r--usr.sbin/ppp/chap_ms.h53
-rw-r--r--usr.sbin/ppp/chat.c794
-rw-r--r--usr.sbin/ppp/chat.h82
-rw-r--r--usr.sbin/ppp/command.c3160
-rw-r--r--usr.sbin/ppp/command.h75
-rw-r--r--usr.sbin/ppp/datalink.c1468
-rw-r--r--usr.sbin/ppp/datalink.h157
-rw-r--r--usr.sbin/ppp/deflate.c601
-rw-r--r--usr.sbin/ppp/deflate.h30
-rw-r--r--usr.sbin/ppp/defs.c443
-rw-r--r--usr.sbin/ppp/defs.h142
-rw-r--r--usr.sbin/ppp/descriptor.h53
-rw-r--r--usr.sbin/ppp/ether.c722
-rw-r--r--usr.sbin/ppp/ether.h37
-rw-r--r--usr.sbin/ppp/exec.c233
-rw-r--r--usr.sbin/ppp/exec.h35
-rw-r--r--usr.sbin/ppp/filter.c605
-rw-r--r--usr.sbin/ppp/filter.h101
-rw-r--r--usr.sbin/ppp/fsm.c1209
-rw-r--r--usr.sbin/ppp/fsm.h201
-rw-r--r--usr.sbin/ppp/hdlc.c439
-rw-r--r--usr.sbin/ppp/hdlc.h111
-rw-r--r--usr.sbin/ppp/i4b.c451
-rw-r--r--usr.sbin/ppp/i4b.h37
-rw-r--r--usr.sbin/ppp/id.c303
-rw-r--r--usr.sbin/ppp/id.h91
-rw-r--r--usr.sbin/ppp/iface.c715
-rw-r--r--usr.sbin/ppp/iface.h65
-rw-r--r--usr.sbin/ppp/ip.c955
-rw-r--r--usr.sbin/ppp/ip.h44
-rw-r--r--usr.sbin/ppp/ipcp.c1474
-rw-r--r--usr.sbin/ppp/ipcp.h132
-rw-r--r--usr.sbin/ppp/iplist.c225
-rw-r--r--usr.sbin/ppp/iplist.h51
-rw-r--r--usr.sbin/ppp/ipv6cp.c604
-rw-r--r--usr.sbin/ppp/ipv6cp.h81
-rw-r--r--usr.sbin/ppp/layer.h52
-rw-r--r--usr.sbin/ppp/lcp.c1291
-rw-r--r--usr.sbin/ppp/lcp.h142
-rw-r--r--usr.sbin/ppp/link.c382
-rw-r--r--usr.sbin/ppp/link.h80
-rw-r--r--usr.sbin/ppp/log.c520
-rw-r--r--usr.sbin/ppp/log.h104
-rw-r--r--usr.sbin/ppp/lqr.c447
-rw-r--r--usr.sbin/ppp/lqr.h71
-rw-r--r--usr.sbin/ppp/main.c674
-rw-r--r--usr.sbin/ppp/main.h32
-rw-r--r--usr.sbin/ppp/mbuf.c435
-rw-r--r--usr.sbin/ppp/mbuf.h118
-rw-r--r--usr.sbin/ppp/mp.c1212
-rw-r--r--usr.sbin/ppp/mp.h146
-rw-r--r--usr.sbin/ppp/mppe.c814
-rw-r--r--usr.sbin/ppp/mppe.h33
-rw-r--r--usr.sbin/ppp/nat_cmd.c570
-rw-r--r--usr.sbin/ppp/nat_cmd.h41
-rw-r--r--usr.sbin/ppp/ncp.c536
-rw-r--r--usr.sbin/ppp/ncp.h101
-rw-r--r--usr.sbin/ppp/ncpaddr.c992
-rw-r--r--usr.sbin/ppp/ncpaddr.h109
-rw-r--r--usr.sbin/ppp/netgraph.c741
-rw-r--r--usr.sbin/ppp/netgraph.h37
-rw-r--r--usr.sbin/ppp/pap.c303
-rw-r--r--usr.sbin/ppp/pap.h40
-rw-r--r--usr.sbin/ppp/physical.c1130
-rw-r--r--usr.sbin/ppp/physical.h173
-rw-r--r--usr.sbin/ppp/ppp.8.m45906
-rw-r--r--usr.sbin/ppp/pred.c343
-rw-r--r--usr.sbin/ppp/pred.h31
-rw-r--r--usr.sbin/ppp/probe.c78
-rw-r--r--usr.sbin/ppp/probe.h38
-rw-r--r--usr.sbin/ppp/prompt.c566
-rw-r--r--usr.sbin/ppp/prompt.h96
-rw-r--r--usr.sbin/ppp/proto.c115
-rw-r--r--usr.sbin/ppp/proto.h64
-rw-r--r--usr.sbin/ppp/radius.c1056
-rw-r--r--usr.sbin/ppp/radius.h100
-rw-r--r--usr.sbin/ppp/route.c894
-rw-r--r--usr.sbin/ppp/route.h73
-rw-r--r--usr.sbin/ppp/server.c412
-rw-r--r--usr.sbin/ppp/server.h61
-rw-r--r--usr.sbin/ppp/sig.c119
-rw-r--r--usr.sbin/ppp/sig.h35
-rw-r--r--usr.sbin/ppp/slcompress.c588
-rw-r--r--usr.sbin/ppp/slcompress.h149
-rw-r--r--usr.sbin/ppp/sync.c80
-rw-r--r--usr.sbin/ppp/sync.h29
-rw-r--r--usr.sbin/ppp/systems.c482
-rw-r--r--usr.sbin/ppp/systems.h43
-rw-r--r--usr.sbin/ppp/tcp.c212
-rw-r--r--usr.sbin/ppp/tcp.h34
-rw-r--r--usr.sbin/ppp/tcpmss.c185
-rw-r--r--usr.sbin/ppp/tcpmss.h29
-rw-r--r--usr.sbin/ppp/throughput.c301
-rw-r--r--usr.sbin/ppp/throughput.h70
-rw-r--r--usr.sbin/ppp/timer.c292
-rw-r--r--usr.sbin/ppp/timer.h55
-rw-r--r--usr.sbin/ppp/tty.c756
-rw-r--r--usr.sbin/ppp/tty.h37
-rw-r--r--usr.sbin/ppp/tun.c119
-rw-r--r--usr.sbin/ppp/tun.h39
-rw-r--r--usr.sbin/ppp/ua.h74
-rw-r--r--usr.sbin/ppp/udp.c336
-rw-r--r--usr.sbin/ppp/udp.h35
-rw-r--r--usr.sbin/ppp/vjcomp.c200
-rw-r--r--usr.sbin/ppp/vjcomp.h36
-rw-r--r--usr.sbin/pppctl/Makefile9
-rw-r--r--usr.sbin/pppctl/pppctl.8212
-rw-r--r--usr.sbin/pppctl/pppctl.c649
-rw-r--r--usr.sbin/pppd/Makefile50
-rw-r--r--usr.sbin/pppd/RELNOTES724
-rw-r--r--usr.sbin/pppd/auth.c1637
-rw-r--r--usr.sbin/pppd/cbcp.c430
-rw-r--r--usr.sbin/pppd/cbcp.h26
-rw-r--r--usr.sbin/pppd/ccp.c1113
-rw-r--r--usr.sbin/pppd/ccp.h50
-rw-r--r--usr.sbin/pppd/chap.c870
-rw-r--r--usr.sbin/pppd/chap.h124
-rw-r--r--usr.sbin/pppd/chap_ms.c335
-rw-r--r--usr.sbin/pppd/chap_ms.h33
-rw-r--r--usr.sbin/pppd/demand.c348
-rw-r--r--usr.sbin/pppd/fsm.c798
-rw-r--r--usr.sbin/pppd/fsm.h144
-rw-r--r--usr.sbin/pppd/ipcp.c1530
-rw-r--r--usr.sbin/pppd/ipcp.h70
-rw-r--r--usr.sbin/pppd/ipxcp.c1399
-rw-r--r--usr.sbin/pppd/ipxcp.h71
-rw-r--r--usr.sbin/pppd/lcp.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.c1717
-rw-r--r--usr.sbin/pppd/options.c2570
-rw-r--r--usr.sbin/pppd/patchlevel.h6
-rw-r--r--usr.sbin/pppd/pathnames.h32
-rw-r--r--usr.sbin/pppd/pppd.81196
-rw-r--r--usr.sbin/pppd/pppd.h493
-rw-r--r--usr.sbin/pppd/sys-bsd.c1584
-rw-r--r--usr.sbin/pppd/upap.c618
-rw-r--r--usr.sbin/pppd/upap.h87
-rw-r--r--usr.sbin/pppstats/Makefile9
-rw-r--r--usr.sbin/pppstats/pppstats.8218
-rw-r--r--usr.sbin/pppstats/pppstats.c519
-rw-r--r--usr.sbin/praliases/Makefile52
-rw-r--r--usr.sbin/prefix/Makefile8
-rw-r--r--usr.sbin/prefix/prefix.876
-rw-r--r--usr.sbin/prefix/prefix.sh74
-rw-r--r--usr.sbin/procctl/Makefile8
-rw-r--r--usr.sbin/procctl/procctl.834
-rw-r--r--usr.sbin/procctl/procctl.c81
-rw-r--r--usr.sbin/pstat/Makefile14
-rw-r--r--usr.sbin/pstat/pstat.8262
-rw-r--r--usr.sbin/pstat/pstat.c590
-rw-r--r--usr.sbin/pw/Makefile12
-rw-r--r--usr.sbin/pw/README22
-rw-r--r--usr.sbin/pw/bitmap.c132
-rw-r--r--usr.sbin/pw/bitmap.h50
-rw-r--r--usr.sbin/pw/cpdir.c127
-rw-r--r--usr.sbin/pw/edgroup.c230
-rw-r--r--usr.sbin/pw/fileupd.c205
-rw-r--r--usr.sbin/pw/grupd.c171
-rw-r--r--usr.sbin/pw/psdate.c299
-rw-r--r--usr.sbin/pw/psdate.h40
-rw-r--r--usr.sbin/pw/pw.8929
-rw-r--r--usr.sbin/pw/pw.c451
-rw-r--r--usr.sbin/pw/pw.conf.5301
-rw-r--r--usr.sbin/pw/pw.h131
-rw-r--r--usr.sbin/pw/pw_conf.c501
-rw-r--r--usr.sbin/pw/pw_group.c352
-rw-r--r--usr.sbin/pw/pw_log.c68
-rw-r--r--usr.sbin/pw/pw_nis.c72
-rw-r--r--usr.sbin/pw/pw_user.c1267
-rw-r--r--usr.sbin/pw/pw_vpw.c318
-rw-r--r--usr.sbin/pw/pwupd.c211
-rw-r--r--usr.sbin/pw/pwupd.h160
-rw-r--r--usr.sbin/pw/rm_r.c75
-rw-r--r--usr.sbin/pwd_mkdb/Makefile12
-rw-r--r--usr.sbin/pwd_mkdb/pwd_mkdb.8168
-rw-r--r--usr.sbin/pwd_mkdb/pwd_mkdb.c622
-rw-r--r--usr.sbin/quot/Makefile7
-rw-r--r--usr.sbin/quot/quot.8107
-rw-r--r--usr.sbin/quot/quot.c652
-rw-r--r--usr.sbin/quotaon/Makefile9
-rw-r--r--usr.sbin/quotaon/quotaon.8137
-rw-r--r--usr.sbin/quotaon/quotaon.c266
-rw-r--r--usr.sbin/rarpd/Makefile12
-rw-r--r--usr.sbin/rarpd/rarpd.8145
-rw-r--r--usr.sbin/rarpd/rarpd.c1054
-rw-r--r--usr.sbin/raycontrol/Makefile8
-rw-r--r--usr.sbin/raycontrol/raycontrol.8307
-rw-r--r--usr.sbin/raycontrol/raycontrol.c488
-rw-r--r--usr.sbin/repquota/Makefile8
-rw-r--r--usr.sbin/repquota/repquota.8104
-rw-r--r--usr.sbin/repquota/repquota.c410
-rw-r--r--usr.sbin/rip6query/Makefile10
-rw-r--r--usr.sbin/rip6query/rip6query.863
-rw-r--r--usr.sbin/rip6query/rip6query.c208
-rw-r--r--usr.sbin/rmt/Makefile12
-rw-r--r--usr.sbin/rmt/rmt.8221
-rw-r--r--usr.sbin/rmt/rmt.c261
-rw-r--r--usr.sbin/route6d/Makefile9
-rwxr-xr-xusr.sbin/route6d/misc/chkrt64
-rw-r--r--usr.sbin/route6d/misc/cksum.c52
-rw-r--r--usr.sbin/route6d/route6d.8250
-rw-r--r--usr.sbin/route6d/route6d.c3463
-rw-r--r--usr.sbin/route6d/route6d.h90
-rw-r--r--usr.sbin/rpc.lockd/Makefile30
-rw-r--r--usr.sbin/rpc.lockd/kern.c590
-rw-r--r--usr.sbin/rpc.lockd/lock_proc.c1389
-rw-r--r--usr.sbin/rpc.lockd/lockd.c271
-rw-r--r--usr.sbin/rpc.lockd/lockd.h41
-rw-r--r--usr.sbin/rpc.lockd/lockd_lock.c2253
-rw-r--r--usr.sbin/rpc.lockd/lockd_lock.h25
-rw-r--r--usr.sbin/rpc.lockd/rpc.lockd.8125
-rw-r--r--usr.sbin/rpc.lockd/test.c365
-rw-r--r--usr.sbin/rpc.statd/Makefile26
-rw-r--r--usr.sbin/rpc.statd/file.c329
-rw-r--r--usr.sbin/rpc.statd/procs.c355
-rw-r--r--usr.sbin/rpc.statd/rpc.statd.8105
-rw-r--r--usr.sbin/rpc.statd/statd.c140
-rw-r--r--usr.sbin/rpc.statd/statd.h110
-rw-r--r--usr.sbin/rpc.statd/test.c144
-rw-r--r--usr.sbin/rpc.umntall/Makefile8
-rw-r--r--usr.sbin/rpc.umntall/mounttab.c226
-rw-r--r--usr.sbin/rpc.umntall/mounttab.h46
-rw-r--r--usr.sbin/rpc.umntall/rpc.umntall.8125
-rw-r--r--usr.sbin/rpc.umntall/rpc.umntall.c262
-rw-r--r--usr.sbin/rpc.yppasswdd/Makefile65
-rw-r--r--usr.sbin/rpc.yppasswdd/rpc.yppasswdd.8356
-rw-r--r--usr.sbin/rpc.yppasswdd/yppasswd_private.x72
-rw-r--r--usr.sbin/rpc.yppasswdd/yppasswdd_extern.h70
-rw-r--r--usr.sbin/rpc.yppasswdd/yppasswdd_main.c348
-rw-r--r--usr.sbin/rpc.yppasswdd/yppasswdd_server.c832
-rw-r--r--usr.sbin/rpc.yppasswdd/yppwupdate34
-rw-r--r--usr.sbin/rpc.ypupdated/Makefile32
-rw-r--r--usr.sbin/rpc.ypupdated/update.c331
-rw-r--r--usr.sbin/rpc.ypupdated/yp_dbdelete.c70
-rw-r--r--usr.sbin/rpc.ypupdated/yp_dbupdate.c148
-rwxr-xr-xusr.sbin/rpc.ypupdated/ypupdate33
-rw-r--r--usr.sbin/rpc.ypupdated/ypupdated_extern.h33
-rw-r--r--usr.sbin/rpc.ypupdated/ypupdated_main.c287
-rw-r--r--usr.sbin/rpc.ypupdated/ypupdated_server.c230
-rw-r--r--usr.sbin/rpc.ypxfrd/Makefile35
-rw-r--r--usr.sbin/rpc.ypxfrd/rpc.ypxfrd.8150
-rw-r--r--usr.sbin/rpc.ypxfrd/ypxfrd_extern.h50
-rw-r--r--usr.sbin/rpc.ypxfrd/ypxfrd_main.c303
-rw-r--r--usr.sbin/rpc.ypxfrd/ypxfrd_server.c147
-rw-r--r--usr.sbin/rpcbind/Makefile21
-rw-r--r--usr.sbin/rpcbind/check_bound.c231
-rw-r--r--usr.sbin/rpcbind/pmap_svc.c368
-rw-r--r--usr.sbin/rpcbind/rpcb_stat.c208
-rw-r--r--usr.sbin/rpcbind/rpcb_svc.c233
-rw-r--r--usr.sbin/rpcbind/rpcb_svc_4.c455
-rw-r--r--usr.sbin/rpcbind/rpcb_svc_com.c1463
-rw-r--r--usr.sbin/rpcbind/rpcbind.8112
-rw-r--r--usr.sbin/rpcbind/rpcbind.c568
-rw-r--r--usr.sbin/rpcbind/rpcbind.h144
-rw-r--r--usr.sbin/rpcbind/security.c284
-rw-r--r--usr.sbin/rpcbind/util.c382
-rw-r--r--usr.sbin/rpcbind/warmstart.c179
-rw-r--r--usr.sbin/rrenumd/Makefile36
-rw-r--r--usr.sbin/rrenumd/lexer.l273
-rw-r--r--usr.sbin/rrenumd/parser.y675
-rw-r--r--usr.sbin/rrenumd/rrenumd.8102
-rw-r--r--usr.sbin/rrenumd/rrenumd.c663
-rw-r--r--usr.sbin/rrenumd/rrenumd.conf.5369
-rw-r--r--usr.sbin/rrenumd/rrenumd.h60
-rw-r--r--usr.sbin/rtadvd/Makefile26
-rw-r--r--usr.sbin/rtadvd/advcap.c455
-rw-r--r--usr.sbin/rtadvd/advcap.h46
-rw-r--r--usr.sbin/rtadvd/config.c1085
-rw-r--r--usr.sbin/rtadvd/config.h38
-rw-r--r--usr.sbin/rtadvd/dump.c251
-rw-r--r--usr.sbin/rtadvd/dump.h33
-rw-r--r--usr.sbin/rtadvd/if.c589
-rw-r--r--usr.sbin/rtadvd/if.h58
-rw-r--r--usr.sbin/rtadvd/pathnames.h4
-rw-r--r--usr.sbin/rtadvd/rrenum.c488
-rw-r--r--usr.sbin/rtadvd/rrenum.h34
-rw-r--r--usr.sbin/rtadvd/rtadvd.8201
-rw-r--r--usr.sbin/rtadvd/rtadvd.c1613
-rw-r--r--usr.sbin/rtadvd/rtadvd.conf21
-rw-r--r--usr.sbin/rtadvd/rtadvd.conf.5386
-rw-r--r--usr.sbin/rtadvd/rtadvd.h178
-rw-r--r--usr.sbin/rtadvd/timer.c213
-rw-r--r--usr.sbin/rtadvd/timer.h65
-rw-r--r--usr.sbin/rtprio/Makefile8
-rw-r--r--usr.sbin/rtprio/rtprio.1210
-rw-r--r--usr.sbin/rtprio/rtprio.c142
-rw-r--r--usr.sbin/rtsold/Makefile27
-rw-r--r--usr.sbin/rtsold/dump.c142
-rw-r--r--usr.sbin/rtsold/if.c468
-rw-r--r--usr.sbin/rtsold/probe.c191
-rw-r--r--usr.sbin/rtsold/rtsock.c179
-rw-r--r--usr.sbin/rtsold/rtsol.c345
-rw-r--r--usr.sbin/rtsold/rtsold.8224
-rw-r--r--usr.sbin/rtsold/rtsold.c792
-rw-r--r--usr.sbin/rtsold/rtsold.h95
-rw-r--r--usr.sbin/rwhod/Makefile7
-rw-r--r--usr.sbin/rwhod/rwhod.8235
-rw-r--r--usr.sbin/rwhod/rwhod.c747
-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.8236
-rw-r--r--usr.sbin/sa/usrdb.c286
-rw-r--r--usr.sbin/sade/Makefile93
-rw-r--r--usr.sbin/sade/command.c184
-rw-r--r--usr.sbin/sade/config.c1021
-rw-r--r--usr.sbin/sade/devices.c573
-rw-r--r--usr.sbin/sade/disks.c964
-rw-r--r--usr.sbin/sade/dispatch.c444
-rw-r--r--usr.sbin/sade/dmenu.c316
-rw-r--r--usr.sbin/sade/globals.c73
-rw-r--r--usr.sbin/sade/help/partition.hlp135
-rw-r--r--usr.sbin/sade/help/slice.hlp65
-rw-r--r--usr.sbin/sade/install.c1202
-rw-r--r--usr.sbin/sade/keymap.c95
-rw-r--r--usr.sbin/sade/label.c1481
-rw-r--r--usr.sbin/sade/list.h60
-rw-r--r--usr.sbin/sade/main.c179
-rw-r--r--usr.sbin/sade/menus.c2157
-rw-r--r--usr.sbin/sade/misc.c484
-rw-r--r--usr.sbin/sade/msg.c356
-rw-r--r--usr.sbin/sade/rtermcap.c15
-rw-r--r--usr.sbin/sade/sade.81003
-rw-r--r--usr.sbin/sade/sade.h815
-rw-r--r--usr.sbin/sade/system.c531
-rw-r--r--usr.sbin/sade/termcap.c150
-rw-r--r--usr.sbin/sade/usb.c44
-rw-r--r--usr.sbin/sade/variable.c296
-rw-r--r--usr.sbin/sade/wizard.c202
-rw-r--r--usr.sbin/sendmail/Makefile80
-rw-r--r--usr.sbin/setextattr/Makefile8
-rw-r--r--usr.sbin/setextattr/setextattr.885
-rw-r--r--usr.sbin/setextattr/setextattr.c74
-rw-r--r--usr.sbin/setkey/Makefile62
-rw-r--r--usr.sbin/setkey/parse.y933
-rw-r--r--usr.sbin/setkey/sample.cf219
-rw-r--r--usr.sbin/setkey/scriptdump.pl56
-rw-r--r--usr.sbin/setkey/setkey.8629
-rw-r--r--usr.sbin/setkey/setkey.c648
-rw-r--r--usr.sbin/setkey/test-pfkey.c531
-rw-r--r--usr.sbin/setkey/test-policy.c161
-rw-r--r--usr.sbin/setkey/token.l322
-rw-r--r--usr.sbin/setkey/vchar.h36
-rw-r--r--usr.sbin/sgsc/Makefile5
-rw-r--r--usr.sbin/sgsc/sgsc.1104
-rw-r--r--usr.sbin/sgsc/sgsc.c163
-rw-r--r--usr.sbin/sicontrol/Makefile8
-rw-r--r--usr.sbin/sicontrol/sicontrol.8104
-rw-r--r--usr.sbin/sicontrol/sicontrol.c572
-rw-r--r--usr.sbin/sliplogin/Makefile11
-rw-r--r--usr.sbin/sliplogin/pathnames.h48
-rw-r--r--usr.sbin/sliplogin/sliplogin.8311
-rw-r--r--usr.sbin/sliplogin/sliplogin.c548
-rw-r--r--usr.sbin/slstat/Makefile7
-rw-r--r--usr.sbin/slstat/slstat.8129
-rw-r--r--usr.sbin/slstat/slstat.c247
-rw-r--r--usr.sbin/spkrtest/Makefile6
-rw-r--r--usr.sbin/spkrtest/spkrtest.847
-rw-r--r--usr.sbin/spkrtest/spkrtest.sh113
-rw-r--r--usr.sbin/spray/Makefile9
-rw-r--r--usr.sbin/spray/spray.873
-rw-r--r--usr.sbin/spray/spray.c220
-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/Makefile16
-rw-r--r--usr.sbin/stallion/bootcode/cdk.sys.uu733
-rw-r--r--usr.sbin/stallion/bootcode/stl.4327
-rw-r--r--usr.sbin/stallion/stlload/Makefile9
-rw-r--r--usr.sbin/stallion/stlload/stlload.8130
-rw-r--r--usr.sbin/stallion/stlload/stlload.c503
-rw-r--r--usr.sbin/stallion/stlstats/Makefile10
-rw-r--r--usr.sbin/stallion/stlstats/stlstats.8138
-rw-r--r--usr.sbin/stallion/stlstats/stlstats.c580
-rw-r--r--usr.sbin/sysinstall/Makefile93
-rw-r--r--usr.sbin/sysinstall/anonFTP.c327
-rw-r--r--usr.sbin/sysinstall/cdrom.c208
-rw-r--r--usr.sbin/sysinstall/command.c184
-rw-r--r--usr.sbin/sysinstall/config.c1021
-rw-r--r--usr.sbin/sysinstall/dev2c.sh80
-rw-r--r--usr.sbin/sysinstall/devices.c573
-rw-r--r--usr.sbin/sysinstall/dhcp.c158
-rw-r--r--usr.sbin/sysinstall/disks.c964
-rw-r--r--usr.sbin/sysinstall/dispatch.c444
-rw-r--r--usr.sbin/sysinstall/dist.c933
-rw-r--r--usr.sbin/sysinstall/dist.h138
-rw-r--r--usr.sbin/sysinstall/dmenu.c316
-rw-r--r--usr.sbin/sysinstall/doc.c125
-rw-r--r--usr.sbin/sysinstall/dos.c94
-rw-r--r--usr.sbin/sysinstall/floppy.c189
-rw-r--r--usr.sbin/sysinstall/ftp.c282
-rw-r--r--usr.sbin/sysinstall/globals.c73
-rw-r--r--usr.sbin/sysinstall/help/anonftp.hlp19
-rw-r--r--usr.sbin/sysinstall/help/configure.hlp10
-rw-r--r--usr.sbin/sysinstall/help/distributions.hlp42
-rw-r--r--usr.sbin/sysinstall/help/drives.hlp92
-rw-r--r--usr.sbin/sysinstall/help/fixit.hlp7
-rw-r--r--usr.sbin/sysinstall/help/html.hlp20
-rw-r--r--usr.sbin/sysinstall/help/media.hlp54
-rw-r--r--usr.sbin/sysinstall/help/network_device.hlp58
-rw-r--r--usr.sbin/sysinstall/help/options.hlp124
-rw-r--r--usr.sbin/sysinstall/help/partition.hlp135
-rw-r--r--usr.sbin/sysinstall/help/register.hlp76
-rw-r--r--usr.sbin/sysinstall/help/security.hlp10
-rw-r--r--usr.sbin/sysinstall/help/shortcuts.hlp114
-rw-r--r--usr.sbin/sysinstall/help/slice.hlp65
-rw-r--r--usr.sbin/sysinstall/help/tcp.hlp34
-rw-r--r--usr.sbin/sysinstall/help/usage.hlp65
-rw-r--r--usr.sbin/sysinstall/help/usermgmt.hlp89
-rw-r--r--usr.sbin/sysinstall/http.c279
-rw-r--r--usr.sbin/sysinstall/index.c817
-rw-r--r--usr.sbin/sysinstall/install.c1202
-rw-r--r--usr.sbin/sysinstall/install.cfg99
-rw-r--r--usr.sbin/sysinstall/installUpgrade.c500
-rw-r--r--usr.sbin/sysinstall/keymap.c95
-rw-r--r--usr.sbin/sysinstall/label.c1481
-rw-r--r--usr.sbin/sysinstall/list.h60
-rw-r--r--usr.sbin/sysinstall/main.c179
-rw-r--r--usr.sbin/sysinstall/media.c867
-rw-r--r--usr.sbin/sysinstall/menus.c2157
-rw-r--r--usr.sbin/sysinstall/misc.c484
-rw-r--r--usr.sbin/sysinstall/modules.c205
-rw-r--r--usr.sbin/sysinstall/mouse.c103
-rw-r--r--usr.sbin/sysinstall/msg.c356
-rw-r--r--usr.sbin/sysinstall/network.c355
-rw-r--r--usr.sbin/sysinstall/nfs.c96
-rw-r--r--usr.sbin/sysinstall/options.c335
-rw-r--r--usr.sbin/sysinstall/package.c266
-rw-r--r--usr.sbin/sysinstall/pccard.c290
-rw-r--r--usr.sbin/sysinstall/rtermcap.c15
-rw-r--r--usr.sbin/sysinstall/sysinstall.81003
-rw-r--r--usr.sbin/sysinstall/sysinstall.h815
-rw-r--r--usr.sbin/sysinstall/system.c531
-rw-r--r--usr.sbin/sysinstall/tape.c127
-rw-r--r--usr.sbin/sysinstall/tcpip.c693
-rw-r--r--usr.sbin/sysinstall/termcap.c150
-rw-r--r--usr.sbin/sysinstall/ttys.c162
-rw-r--r--usr.sbin/sysinstall/ufs.c49
-rw-r--r--usr.sbin/sysinstall/usb.c44
-rw-r--r--usr.sbin/sysinstall/user.c741
-rw-r--r--usr.sbin/sysinstall/variable.c296
-rw-r--r--usr.sbin/sysinstall/wizard.c202
-rw-r--r--usr.sbin/syslogd/Makefile13
-rw-r--r--usr.sbin/syslogd/pathnames.h40
-rw-r--r--usr.sbin/syslogd/syslog.conf.5435
-rw-r--r--usr.sbin/syslogd/syslogd.8331
-rw-r--r--usr.sbin/syslogd/syslogd.c2405
-rw-r--r--usr.sbin/tcpdchk/Makefile21
-rw-r--r--usr.sbin/tcpdmatch/Makefile20
-rw-r--r--usr.sbin/tcpdump/Makefile6
-rw-r--r--usr.sbin/tcpdump/Makefile.inc4
-rw-r--r--usr.sbin/tcpdump/tcpdump/Makefile55
-rw-r--r--usr.sbin/tcpdump/tcpdump/config.h201
-rw-r--r--usr.sbin/tcpdump/tcpslice/Makefile23
-rw-r--r--usr.sbin/tcpdump/tcpslice/gwtm2secs.c72
-rw-r--r--usr.sbin/tcpdump/tcpslice/search.c566
-rw-r--r--usr.sbin/tcpdump/tcpslice/tcpslice.1273
-rw-r--r--usr.sbin/tcpdump/tcpslice/tcpslice.c626
-rw-r--r--usr.sbin/tcpdump/tcpslice/tcpslice.h59
-rw-r--r--usr.sbin/tcpdump/tcpslice/util.c56
-rw-r--r--usr.sbin/timed/Makefile6
-rw-r--r--usr.sbin/timed/SMM.doc/timed/Makefile12
-rw-r--r--usr.sbin/timed/SMM.doc/timed/date53
-rw-r--r--usr.sbin/timed/SMM.doc/timed/loop54
-rw-r--r--usr.sbin/timed/SMM.doc/timed/spell.ok34
-rw-r--r--usr.sbin/timed/SMM.doc/timed/time53
-rw-r--r--usr.sbin/timed/SMM.doc/timed/timed.ms462
-rw-r--r--usr.sbin/timed/SMM.doc/timed/unused53
-rw-r--r--usr.sbin/timed/SMM.doc/timedop/Makefile8
-rw-r--r--usr.sbin/timed/SMM.doc/timedop/timed.ms279
-rw-r--r--usr.sbin/timed/timed/CHANGES145
-rw-r--r--usr.sbin/timed/timed/Makefile15
-rw-r--r--usr.sbin/timed/timed/acksend.c132
-rw-r--r--usr.sbin/timed/timed/byteorder.c86
-rw-r--r--usr.sbin/timed/timed/candidate.c167
-rw-r--r--usr.sbin/timed/timed/cksum.c87
-rw-r--r--usr.sbin/timed/timed/correct.c198
-rw-r--r--usr.sbin/timed/timed/extern.h89
-rw-r--r--usr.sbin/timed/timed/globals.h175
-rw-r--r--usr.sbin/timed/timed/master.c853
-rw-r--r--usr.sbin/timed/timed/measure.c346
-rw-r--r--usr.sbin/timed/timed/networkdelta.c264
-rw-r--r--usr.sbin/timed/timed/pathnames.h41
-rw-r--r--usr.sbin/timed/timed/readmsg.c513
-rw-r--r--usr.sbin/timed/timed/slave.c693
-rw-r--r--usr.sbin/timed/timed/timed.8264
-rw-r--r--usr.sbin/timed/timed/timed.c850
-rw-r--r--usr.sbin/timed/timedc/Makefile13
-rw-r--r--usr.sbin/timed/timedc/cmds.c555
-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.8143
-rw-r--r--usr.sbin/timed/timedc/timedc.c260
-rw-r--r--usr.sbin/timed/timedc/timedc.h63
-rw-r--r--usr.sbin/traceroute/Makefile41
-rw-r--r--usr.sbin/traceroute6/Makefile26
-rw-r--r--usr.sbin/traceroute6/traceroute6.8123
-rw-r--r--usr.sbin/traceroute6/traceroute6.c1357
-rw-r--r--usr.sbin/trpt/Makefile11
-rw-r--r--usr.sbin/trpt/trpt.8152
-rw-r--r--usr.sbin/trpt/trpt.c477
-rw-r--r--usr.sbin/tzsetup/Makefile11
-rw-r--r--usr.sbin/tzsetup/paths.h5
-rw-r--r--usr.sbin/tzsetup/tzsetup.8129
-rw-r--r--usr.sbin/tzsetup/tzsetup.c710
-rw-r--r--usr.sbin/usbd/Makefile6
-rw-r--r--usr.sbin/usbd/usbd.8149
-rw-r--r--usr.sbin/usbd/usbd.c1115
-rw-r--r--usr.sbin/usbd/usbd.conf.5163
-rw-r--r--usr.sbin/usbdevs/Makefile9
-rw-r--r--usr.sbin/usbdevs/usbdevs.872
-rw-r--r--usr.sbin/usbdevs/usbdevs.c227
-rw-r--r--usr.sbin/vidcontrol/Makefile6
-rw-r--r--usr.sbin/vidcontrol/decode.c99
-rw-r--r--usr.sbin/vidcontrol/decode.h3
-rw-r--r--usr.sbin/vidcontrol/path.h4
-rw-r--r--usr.sbin/vidcontrol/vidcontrol.1512
-rw-r--r--usr.sbin/vidcontrol/vidcontrol.c831
-rw-r--r--usr.sbin/vipw/Makefile10
-rw-r--r--usr.sbin/vipw/vipw.8109
-rw-r--r--usr.sbin/vipw/vipw.c141
-rw-r--r--usr.sbin/vnconfig/Makefile6
-rw-r--r--usr.sbin/vnconfig/vnconfig.c25
-rw-r--r--usr.sbin/watch/Makefile11
-rw-r--r--usr.sbin/watch/watch.8113
-rw-r--r--usr.sbin/watch/watch.c442
-rw-r--r--usr.sbin/wicontrol/Makefile10
-rw-r--r--usr.sbin/wicontrol/wicontrol.8466
-rw-r--r--usr.sbin/wicontrol/wicontrol.c1015
-rw-r--r--usr.sbin/wlconfig/Makefile6
-rw-r--r--usr.sbin/wlconfig/wlconfig.8138
-rw-r--r--usr.sbin/wlconfig/wlconfig.c417
-rw-r--r--usr.sbin/xten/Makefile8
-rw-r--r--usr.sbin/xten/README367
-rw-r--r--usr.sbin/xten/xten.1115
-rw-r--r--usr.sbin/xten/xten.c180
-rw-r--r--usr.sbin/yp_mkdb/Makefile12
-rw-r--r--usr.sbin/yp_mkdb/yp_mkdb.8205
-rw-r--r--usr.sbin/yp_mkdb/yp_mkdb.c344
-rw-r--r--usr.sbin/ypbind/Makefile10
-rw-r--r--usr.sbin/ypbind/yp_ping.c328
-rw-r--r--usr.sbin/ypbind/yp_ping.h5
-rw-r--r--usr.sbin/ypbind/ypbind.8203
-rw-r--r--usr.sbin/ypbind/ypbind.c993
-rw-r--r--usr.sbin/yppoll/Makefile7
-rw-r--r--usr.sbin/yppoll/yppoll.884
-rw-r--r--usr.sbin/yppoll/yppoll.c98
-rw-r--r--usr.sbin/yppush/Makefile28
-rw-r--r--usr.sbin/yppush/yppush.8179
-rw-r--r--usr.sbin/yppush/yppush_extern.h44
-rw-r--r--usr.sbin/yppush/yppush_main.c686
-rw-r--r--usr.sbin/ypserv/Makefile43
-rw-r--r--usr.sbin/ypserv/Makefile.yp589
-rw-r--r--usr.sbin/ypserv/yp_access.c325
-rw-r--r--usr.sbin/ypserv/yp_dblookup.c735
-rw-r--r--usr.sbin/ypserv/yp_dnslookup.c541
-rw-r--r--usr.sbin/ypserv/yp_error.c75
-rw-r--r--usr.sbin/ypserv/yp_extern.h115
-rw-r--r--usr.sbin/ypserv/yp_main.c340
-rw-r--r--usr.sbin/ypserv/yp_server.c975
-rw-r--r--usr.sbin/ypserv/yp_svc_udp.c73
-rw-r--r--usr.sbin/ypserv/ypinit.8188
-rw-r--r--usr.sbin/ypserv/ypinit.sh390
-rw-r--r--usr.sbin/ypserv/ypserv.8447
-rw-r--r--usr.sbin/ypset/Makefile7
-rw-r--r--usr.sbin/ypset/ypset.885
-rw-r--r--usr.sbin/ypset/ypset.c152
-rw-r--r--usr.sbin/zic/Arts.htm178
-rw-r--r--usr.sbin/zic/Makefile8
-rw-r--r--usr.sbin/zic/Makefile.inc3
-rw-r--r--usr.sbin/zic/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.845
-rw-r--r--usr.sbin/zic/zdump.c374
-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
1597 files changed, 433014 insertions, 0 deletions
diff --git a/usr.sbin/IPXrouted/IPXrouted.8 b/usr.sbin/IPXrouted/IPXrouted.8
new file mode 100644
index 0000000..fc1a1a1
--- /dev/null
+++ b/usr.sbin/IPXrouted/IPXrouted.8
@@ -0,0 +1,195 @@
+.\" Copyright (c) 1986, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Copyright (c) 1995 John Hay. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd October 11, 1995
+.Dt IPXROUTED 8
+.Os
+.Sh NAME
+.Nm IPXrouted
+.Nd IPX Routing Information Protocol daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl N
+.Op Fl q
+.Op Fl s
+.Op Fl S
+.Op Fl t
+.Op Ar logfile
+.Sh DESCRIPTION
+.Nm
+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 N
+Do not reply on GetNearestServer SAP request.
+.It Fl q
+Do not supply routing information (opposite of
+.Fl s
+option below).
+.It Fl s
+Forces
+.Nm
+to supply routing information whether it is acting as an internetwork
+router or not.
+.It Fl S
+Do not supply Service Advertizing Protocol
+(SAP)
+information.
+The default is to supply
+SAP
+information.
+.It Fl t
+All packets sent or received are
+printed on the standard output. In addition,
+.Nm
+will not divorce itself from the controlling terminal
+so that interrupts from the keyboard will kill the process.
+.It Ar logfile
+Name of file in which
+.Nm Ns 's
+actions should be logged. This log contains information
+about any changes to the routing tables and a history of
+recent messages sent and received which are related to
+the changed route.
+.El
+.Pp
+In normal operation
+.Nm
+listens
+for routing information packets. If the host is connected to
+multiple IPX networks, it periodically supplies copies
+of its routing tables to any directly connected hosts
+and networks.
+.Pp
+When
+.Nm
+is started, it uses the
+.Dv SIOCGIFCONF
+.Xr ioctl 2
+to find those
+directly connected interfaces configured into the
+system and marked
+.Dq up
+(the software loopback interface
+is ignored). If multiple interfaces
+are present, it is assumed the host will forward packets
+between networks.
+.Nm
+then transmits a
+.Em request
+packet on each interface (using a broadcast packet if
+the interface supports it) and enters a loop, listening
+for
+.Em request
+and
+.Em response
+packets from other hosts.
+.Pp
+When a
+.Em request
+packet is received,
+.Nm
+formulates a reply based on the information maintained in its
+internal tables. The
+.Em response
+packet generated contains a list of known routes, each marked
+with a
+.Dq hop count
+metric (a count of 16, or greater, is
+considered
+.Dq infinite ) .
+The metric associated with each
+route returned provides a metric
+.Em relative to the sender .
+.Pp
+.Em Response
+packets received by
+.Nm
+are used to update the routing tables if one of the following
+conditions is satisfied:
+.Bl -bullet
+.It
+No routing table entry exists for the destination network
+or host, and the metric indicates the destination is ``reachable''
+(i.e. the hop count is not infinite).
+.It
+The source host of the packet is the same as the router in the
+existing routing table entry. That is, updated information is
+being received from the very internetwork router through which
+packets for the destination are being routed.
+.It
+The existing entry in the routing table has not been updated for
+some time (defined to be 90 seconds) and the route is at least
+as cost effective as the current route.
+.It
+The new route describes a shorter route to the destination than
+the one currently stored in the routing tables; the metric of
+the new route is compared against the one stored in the table
+to decide this.
+.El
+.Pp
+When an update is applied,
+.Nm
+records the change in its internal tables and generates a
+.Em response
+packet to all directly connected hosts and networks.
+.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
+also periodically checks the routing table entries.
+If an entry has not been updated for 3 minutes, the entry's metric
+is set to infinity and marked for deletion. Deletions are delayed
+an additional 60 seconds to insure the invalidation is propagated
+to other routers.
+.Pp
+Hosts acting as internetwork routers gratuitously supply their
+routing tables every 30 seconds to all directly connected hosts
+and networks.
+.Pp
+If
+.Nm
+receives a SIGINFO signal the current contents of the RIP and SAP
+tables are appended to the file /var/log/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..374782d
--- /dev/null
+++ b/usr.sbin/IPXrouted/Makefile
@@ -0,0 +1,12 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+# $FreeBSD$
+
+PROG= IPXrouted
+MAN= IPXrouted.8
+SRCS= af.c if.c input.c main.c output.c startup.c tables.c timer.c trace.c
+SRCS+= sap_input.c sap_tables.c sap_output.c
+
+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..74455df
--- /dev/null
+++ b/usr.sbin/IPXrouted/af.c
@@ -0,0 +1,298 @@
+/*
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * This file includes significant work done at Cornell University by
+ * Bill Nesheim. That work included by permission.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)af.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+#include "defs.h"
+
+/*
+ * Address family support routines
+ */
+af_hash_t null_hash;
+af_netmatch_t null_netmatch;
+af_output_t null_output;
+af_portmatch_t null_portmatch;
+af_portcheck_t null_portcheck;
+af_checkhost_t null_checkhost;
+af_ishost_t null_ishost;
+af_canon_t null_canon;
+
+void ipxnet_hash(struct sockaddr_ipx *, struct afhash *);
+int ipxnet_netmatch(struct sockaddr_ipx *, struct sockaddr_ipx *);
+void ipxnet_output(int, int, struct sockaddr_ipx *, int);
+int ipxnet_portmatch(struct sockaddr_ipx *);
+int ipxnet_checkhost(struct sockaddr_ipx *);
+int ipxnet_ishost(struct sockaddr_ipx *);
+void ipxnet_canon(struct sockaddr_ipx *);
+
+#define NIL \
+ { null_hash, null_netmatch, null_output, \
+ null_portmatch, null_portcheck, null_checkhost, \
+ null_ishost, null_canon }
+#define IPXNET \
+ { (af_hash_t *)ipxnet_hash, \
+ (af_netmatch_t *)ipxnet_netmatch, \
+ (af_output_t *)ipxnet_output, \
+ (af_portmatch_t *)ipxnet_portmatch, \
+ (af_portcheck_t *)ipxnet_portmatch, \
+ (af_checkhost_t *)ipxnet_checkhost, \
+ (af_ishost_t *)ipxnet_ishost, \
+ (af_canon_t *)ipxnet_canon }
+
+struct afswitch afswitch[AF_MAX] =
+ { NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
+ NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
+ NIL, NIL, NIL, IPXNET, NIL, NIL };
+
+struct sockaddr_ipx ipxnet_default = { sizeof(struct sockaddr_ipx), AF_IPX };
+
+union ipx_net ipx_anynet;
+union ipx_net ipx_zeronet;
+
+void
+ipxnet_hash(sipx, hp)
+ register struct sockaddr_ipx *sipx;
+ struct afhash *hp;
+{
+ long hash;
+#if 0
+ u_short *s = sipx->sipx_addr.x_host.s_host;
+#endif
+ u_char *c;
+
+ c = sipx->sipx_addr.x_net.c_net;
+
+#define IMVAL 33
+ hash = 0;
+ hash = hash * IMVAL + *c++;
+ hash = hash * IMVAL + *c++;
+ hash = hash * IMVAL + *c++;
+ hash = hash * IMVAL + *c++;
+#undef IMVAL
+
+ hp->afh_nethash = hash;
+ hp->afh_nethash ^= (hash >> 8);
+ hp->afh_nethash ^= (hash >> 16);
+ hp->afh_nethash ^= (hash >> 24);
+
+#if 0
+ hash = 0;
+ hash = *s++; hash <<= 8; hash += *s++; hash <<= 8; hash += *s;
+ hp->afh_hosthash = hash;
+#endif
+}
+
+int
+ipxnet_netmatch(sxn1, sxn2)
+ struct sockaddr_ipx *sxn1, *sxn2;
+{
+ return (ipx_neteq(sxn1->sipx_addr, sxn2->sipx_addr));
+}
+
+/*
+ * Verify the message is from the right port.
+ */
+int
+ipxnet_portmatch(sipx)
+ register struct sockaddr_ipx *sipx;
+{
+
+ return (ntohs(sipx->sipx_addr.x_port) == IPXPORT_RIP );
+}
+
+
+/*
+ * ipx output routine.
+ */
+#ifdef DEBUG
+int do_output = 0;
+#endif
+void
+ipxnet_output(s, flags, sipx, size)
+ int s;
+ int flags;
+ struct sockaddr_ipx *sipx;
+ int size;
+{
+ struct sockaddr_ipx dst;
+
+ dst = *sipx;
+ sipx = &dst;
+ if (sipx->sipx_addr.x_port == 0)
+ sipx->sipx_addr.x_port = htons(IPXPORT_RIP);
+#ifdef DEBUG
+ if(do_output || ntohs(msg->rip_cmd) == RIPCMD_REQUEST)
+#endif
+ /*
+ * Kludge to allow us to get routes out to machines that
+ * don't know their addresses yet; send to that address on
+ * ALL connected nets
+ */
+ if (ipx_neteqnn(sipx->sipx_addr.x_net, ipx_zeronet)) {
+ extern struct interface *ifnet;
+ register struct interface *ifp;
+
+ for (ifp = ifnet; ifp; ifp = ifp->int_next) {
+ sipx->sipx_addr.x_net =
+ satoipx_addr(ifp->int_addr).x_net;
+ (void) sendto(s, msg, size, flags,
+ (struct sockaddr *)sipx, sizeof (*sipx));
+ }
+ return;
+ }
+
+ (void) sendto(s, msg, size, flags,
+ (struct sockaddr *)sipx, sizeof (*sipx));
+}
+
+/*
+ * Return 1 if we want this route.
+ * We use this to disallow route net G entries for one for multiple
+ * point to point links.
+ */
+int
+ipxnet_checkhost(sipx)
+ struct sockaddr_ipx *sipx;
+{
+ register struct interface *ifp = if_ifwithnet((struct sockaddr *)sipx);
+ /*
+ * We want this route if there is no more than one
+ * point to point interface with this network.
+ */
+ if (ifp == 0 || (ifp->int_flags & IFF_POINTOPOINT)==0) return (1);
+ return (ifp->int_sq.n == ifp->int_sq.p);
+}
+
+/*
+ * Return 1 if the address is
+ * for a host, 0 for a network.
+ */
+int
+ipxnet_ishost(sipx)
+struct sockaddr_ipx *sipx;
+{
+ register u_short *s = sipx->sipx_addr.x_host.s_host;
+
+ if ((s[0]==0x0000) && (s[1]==0x0000) && (s[2]==0x0000))
+ return (0);
+ if ((s[0]==0xffff) && (s[1]==0xffff) && (s[2]==0xffff))
+ return (0);
+
+ return (1);
+}
+
+void
+ipxnet_canon(sipx)
+ struct sockaddr_ipx *sipx;
+{
+
+ sipx->sipx_addr.x_port = 0;
+}
+
+void
+null_hash(addr, hp)
+ struct sockaddr *addr;
+ struct afhash *hp;
+{
+
+ hp->afh_nethash = hp->afh_hosthash = 0;
+}
+
+int
+null_netmatch(a1, a2)
+ struct sockaddr *a1, *a2;
+{
+
+ return (0);
+}
+
+void
+null_output(s, f, a1, n)
+ int s;
+ int f;
+ struct sockaddr *a1;
+ int n;
+{
+
+ ;
+}
+
+int
+null_portmatch(a1)
+ struct sockaddr *a1;
+{
+
+ return (0);
+}
+
+int
+null_portcheck(a1)
+ struct sockaddr *a1;
+{
+
+ return (0);
+}
+
+int
+null_ishost(a1)
+ struct sockaddr *a1;
+{
+
+ return (0);
+}
+
+int
+null_checkhost(a1)
+ struct sockaddr *a1;
+{
+
+ return (0);
+}
+
+void
+null_canon(a1)
+ struct sockaddr *a1;
+{
+
+ ;
+}
+
diff --git a/usr.sbin/IPXrouted/af.h b/usr.sbin/IPXrouted/af.h
new file mode 100644
index 0000000..576a6c7
--- /dev/null
+++ b/usr.sbin/IPXrouted/af.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)af.h 5.1 (Berkeley) 6/4/85 (routed/af.h)
+ *
+ * @(#)af.h 8.1 (Berkeley) 6/5/93
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Routing table management daemon.
+ */
+
+/*
+ * Structure returned by af_hash routines.
+ */
+struct afhash {
+ u_int afh_hosthash; /* host based hash */
+ u_int afh_nethash; /* network based hash */
+};
+
+/*
+ * Per address family routines.
+ */
+typedef void af_hash_t(struct sockaddr *, struct afhash *);
+typedef int af_netmatch_t(struct sockaddr *, struct sockaddr *);
+typedef void af_output_t(int, int, struct sockaddr *, int);
+typedef int af_portmatch_t(struct sockaddr *);
+typedef int af_portcheck_t(struct sockaddr *);
+typedef int af_checkhost_t(struct sockaddr *);
+typedef int af_ishost_t(struct sockaddr *);
+typedef void af_canon_t(struct sockaddr *);
+
+struct afswitch {
+ af_hash_t *af_hash; /* returns keys based on address */
+ af_netmatch_t *af_netmatch; /* verifies net # matching */
+ af_output_t *af_output; /* interprets address for sending */
+ af_portmatch_t *af_portmatch; /* packet from some other router? */
+ af_portcheck_t *af_portcheck; /* packet from privileged peer? */
+ af_checkhost_t *af_checkhost; /* tells if address for host or net */
+ af_ishost_t *af_ishost; /* tells if address is valid */
+ af_canon_t *af_canon; /* canonicalize address for compares */
+};
+
+struct afswitch afswitch[AF_MAX]; /* table proper */
diff --git a/usr.sbin/IPXrouted/defs.h b/usr.sbin/IPXrouted/defs.h
new file mode 100644
index 0000000..c2b28a7
--- /dev/null
+++ b/usr.sbin/IPXrouted/defs.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)defs.h 8.1 (Berkeley) 6/5/93
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <arpa/inet.h>
+#include <net/route.h>
+#include <netipx/ipx.h>
+#if defined(vax) || defined(pdp11)
+#define xnnet(x) ((u_long) (x)->rip_dst[1] << 16 | (u_long) (x)->rip_dst[0] )
+#else
+#define xnnet(x) ((u_long) (x)->rip_dst[0] << 16 | (u_long) (x)->rip_dst[1] )
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "protocol.h"
+#include "sap.h"
+#include "table.h"
+#include "trace.h"
+#include "interface.h"
+#include "af.h"
+
+
+/*
+ * When we find any interfaces marked down we rescan the
+ * kernel every CHECK_INTERVAL seconds to see if they've
+ * come up.
+ */
+#define CHECK_INTERVAL (5*60)
+
+#define equal(a1, a2) \
+ (bcmp((caddr_t)(a1), (caddr_t)(a2), sizeof (struct sockaddr)) == 0)
+#define min(a,b) ((a)>(b)?(b):(a))
+#define max(a,b) ((a)<(b)?(b):(a))
+
+extern int ripsock; /* Socket to listen on */
+extern int sapsock; /* Socket to listen on */
+extern int kmem;
+extern int supplier; /* process should supply updates */
+extern int dosap; /* SAP is enabled */
+extern int dognreply; /* enable GET_NEAREST response */
+extern int install; /* if 1 call kernel */
+extern int lookforinterfaces; /* if 1 probe kernel for new up ifs */
+extern int performnlist; /* if 1 check if /kernel has changed */
+extern int externalinterfaces; /* # of remote and local interfaces */
+extern int timeval; /* local idea of time */
+extern int noteremoterequests; /* squawk on requests from non-local nets */
+extern int r; /* Routing socket to install updates with */
+extern int gateway;
+extern struct sockaddr_ipx ipx_netmask; /* Used in installing routes */
+
+extern char packet[MAXRXPACKETSIZE+1];
+extern struct rip *msg;
+
+extern char **argv0;
+
+#define ADD 1
+#define DELETE 2
+#define CHANGE 3
+
+void sndmsg(struct sockaddr *, int, struct interface *, int);
+void supply(struct sockaddr *, int, struct interface *, int);
+void addrouteforif(struct interface *);
+void ifinit(void);
+void toall(void (*f)(struct sockaddr *, int, struct interface *, int),
+ struct rt_entry *, int);
+void rip_input(struct sockaddr *, int);
+
diff --git a/usr.sbin/IPXrouted/if.c b/usr.sbin/IPXrouted/if.c
new file mode 100644
index 0000000..7b914e7
--- /dev/null
+++ b/usr.sbin/IPXrouted/if.c
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * static char sccsid[] = "@(#)if.c 5.1 (Berkeley) 6/4/85"; (routed/if.c)
+ *
+ * $FreeBSD$
+ */
+
+#ifndef lint
+static 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..a2e8c26
--- /dev/null
+++ b/usr.sbin/IPXrouted/input.c
@@ -0,0 +1,304 @@
+/*
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * This file includes significant work done at Cornell University by
+ * Bill Nesheim. That work included by permission.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef lint
+static 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..ed7b988
--- /dev/null
+++ b/usr.sbin/IPXrouted/interface.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)interface.h 8.1 (Berkeley) 6/5/93
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Routing table management daemon.
+ */
+
+/*
+ * An ``interface'' is similar to an ifnet structure,
+ * except it doesn't contain q'ing info, and it also
+ * handles ``logical'' interfaces (remote gateways
+ * that we want to keep polling even if they go down).
+ * The list of interfaces which we maintain is used
+ * in supplying the gratuitous routing table updates.
+ * We list only one address for each interface, the AF_IPX one.
+ */
+struct interface {
+ struct interface *int_next;
+ struct sockaddr int_addr; /* address on this host */
+ union {
+ struct sockaddr intu_broadaddr;
+ struct sockaddr intu_dstaddr;
+ } int_intu;
+#define int_broadaddr int_intu.intu_broadaddr /* broadcast address */
+#define int_dstaddr int_intu.intu_dstaddr /* other end of p-to-p link */
+ int int_metric; /* init's routing entry */
+ int int_flags; /* see below */
+ struct ifdebug int_input, int_output; /* packet tracing stuff */
+ int int_ipackets; /* input packets received */
+ int int_opackets; /* output packets sent */
+ char *int_name; /* from kernel if structure */
+ u_short int_transitions; /* times gone up-down */
+
+ /* XXX IPX Specific entry */
+ struct sameq {
+ struct sameq *n; /* q of other pt-to-pt links */
+ struct sameq *p; /* with same net # */
+ } int_sq;
+};
+
+/*
+ * 0x1 to 0x10 are reused from the kernel's ifnet definitions,
+ * the others agree with the RTS_ flags defined elsewhere.
+ */
+#define IFF_UP 0x1 /* interface is up */
+#define IFF_BROADCAST 0x2 /* broadcast address valid */
+#define IFF_DEBUG 0x4 /* turn on debugging */
+#define IFF_ROUTE 0x8 /* routing entry installed */
+#define IFF_POINTOPOINT 0x10 /* interface is point-to-point link */
+
+#define IFF_PASSIVE 0x200000 /* can't tell if up/down */
+#define IFF_INTERFACE 0x400000 /* hardware interface */
+#define IFF_REMOTE 0x800000 /* interface isn't on this machine */
+
+struct interface *if_ifwithaddr(struct sockaddr *);
+struct interface *if_ifwithdstaddr(struct sockaddr *);
+struct interface *if_ifwithnet(struct sockaddr *);
+struct interface *if_iflookup(struct sockaddr *);
+
diff --git a/usr.sbin/IPXrouted/main.c b/usr.sbin/IPXrouted/main.c
new file mode 100644
index 0000000..91fe635
--- /dev/null
+++ b/usr.sbin/IPXrouted/main.c
@@ -0,0 +1,400 @@
+/*
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * This file includes significant work done at Cornell University by
+ * Bill Nesheim. That work included by permission.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef lint
+static 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;
+ }
+ if (strcmp(*argv, "-N") == 0) {
+ dognreply = 0;
+ argv++, argc--;
+ continue;
+ }
+ fprintf(stderr,
+ "usage: ipxrouted [ -s ] [ -q ] [ -t ] [ -g ] [ -l ] [ -N ]\n");
+ exit(1);
+ }
+
+
+#ifndef DEBUG
+ if (!tracepackets)
+ daemon(0, 0);
+#endif
+ openlog("IPXrouted", LOG_PID, LOG_DAEMON);
+
+ addr.sipx_family = AF_IPX;
+ addr.sipx_len = sizeof(addr);
+ addr.sipx_port = htons(IPXPORT_RIP);
+ ipx_anynet.s_net[0] = ipx_anynet.s_net[1] = -1;
+ ipx_netmask.sipx_addr.x_net = ipx_anynet;
+ ipx_netmask.sipx_len = 6;
+ ipx_netmask.sipx_family = AF_IPX;
+ r = socket(AF_ROUTE, SOCK_RAW, 0);
+ /* later, get smart about lookingforinterfaces */
+ if (r)
+ shutdown(r, 0); /* for now, don't want reponses */
+ else {
+ fprintf(stderr, "IPXrouted: no routing socket\n");
+ exit(1);
+ }
+ ripsock = getsocket(SOCK_DGRAM, 0, &addr);
+ if (ripsock < 0)
+ exit(1);
+
+ if (dosap) {
+ addr.sipx_port = htons(IPXPORT_SAP);
+ sapsock = getsocket(SOCK_DGRAM, 0, &addr);
+ if (sapsock < 0)
+ exit(1);
+ } else
+ sapsock = -1;
+
+ /*
+ * Any extra argument is considered
+ * a tracing log file.
+ */
+ if (argc > 0)
+ traceon(*argv);
+ /*
+ * Collect an initial view of the world by
+ * snooping in the kernel. Then, send a request packet on all
+ * directly connected networks to find out what
+ * everyone else thinks.
+ */
+ rtinit();
+ sapinit();
+ ifinit();
+ if (supplier < 0)
+ supplier = 0;
+ /* request the state of the world */
+ msg->rip_cmd = htons(RIPCMD_REQUEST);
+ msg->rip_nets[0].rip_dst = ipx_anynet;
+ msg->rip_nets[0].rip_metric = htons(HOPCNT_INFINITY);
+ msg->rip_nets[0].rip_ticks = htons(-1);
+ toall(sndmsg, NULL, 0);
+
+ if (dosap) {
+ sap_msg->sap_cmd = htons(SAP_REQ);
+ sap_msg->sap[0].ServType = htons(SAP_WILDCARD);
+ toall(sapsndmsg, NULL, 0);
+ }
+
+ signal(SIGALRM, catchtimer);
+ signal(SIGHUP, hup);
+ signal(SIGINT, hup);
+ signal(SIGEMT, fkexit);
+ signal(SIGINFO, getinfo);
+
+ tval.it_interval.tv_sec = TIMER_RATE;
+ tval.it_interval.tv_usec = 0;
+ tval.it_value.tv_sec = TIMER_RATE;
+ tval.it_value.tv_usec = 0;
+ setitimer(ITIMER_REAL, &tval, NULL);
+
+ nfds = 1 + max(sapsock, ripsock);
+
+ for (;;) {
+ if (dobcast) {
+ dobcast = 0;
+ lastbcast = time(NULL);
+ timer();
+ }
+
+ FD_ZERO(&fdvar);
+ if (dosap) {
+ FD_SET(sapsock, &fdvar);
+ }
+ FD_SET(ripsock, &fdvar);
+
+ if(select(nfds, &fdvar, (fd_set *)NULL, (fd_set *)NULL,
+ (struct timeval *)NULL) < 0) {
+ if(errno == EINTR)
+ continue;
+ perror("during select");
+ exit(1);
+ }
+
+ if(FD_ISSET(ripsock, &fdvar))
+ process(ripsock, RIP_PKT);
+
+ if(dosap && FD_ISSET(sapsock, &fdvar))
+ process(sapsock, SAP_PKT);
+
+ ttime = time(NULL);
+ if (ttime > (lastbcast + TIMER_RATE + (TIMER_RATE * 2 / 3))) {
+ dobcast = 1;
+ syslog(LOG_ERR, "Missed alarm");
+ }
+ }
+}
+
+void
+process(fd, pkt_type)
+ int fd;
+ int pkt_type;
+{
+ struct sockaddr from;
+ int fromlen = sizeof (from), cc, omask;
+ struct ipx *ipxdp = (struct ipx *)packet;
+
+ cc = recvfrom(fd, packet, sizeof (packet), 0, &from, &fromlen);
+ if (cc <= 0) {
+ if (cc < 0 && errno != EINTR)
+ syslog(LOG_ERR, "recvfrom: %m");
+ return;
+ }
+ if (tracepackets > 1 && ftrace) {
+ fprintf(ftrace,"rcv %d bytes on %s ",
+ cc, ipxdp_ntoa(&ipxdp->ipx_dna));
+ fprintf(ftrace," from %s\n", ipxdp_ntoa(&ipxdp->ipx_sna));
+ }
+
+ if (noteremoterequests &&
+ !ipx_neteqnn(ipxdp->ipx_sna.x_net, ipx_zeronet) &&
+ !ipx_neteq(ipxdp->ipx_sna, ipxdp->ipx_dna))
+ {
+ syslog(LOG_ERR,
+ "net of interface (%s) != net on ether (%s)!\n",
+ ipxdp_nettoa(ipxdp->ipx_dna.x_net),
+ ipxdp_nettoa(ipxdp->ipx_sna.x_net));
+ }
+
+ /* We get the IPX header in front of the RIF packet*/
+ cc -= sizeof (struct ipx);
+#define mask(s) (1<<((s)-1))
+ omask = sigblock(mask(SIGALRM));
+ switch(pkt_type) {
+ case SAP_PKT: sap_input(&from, cc);
+ break;
+ case RIP_PKT: rip_input(&from, cc);
+ break;
+ }
+ sigsetmask(omask);
+}
+
+int
+getsocket(type, proto, sipx)
+ int type, proto;
+ struct sockaddr_ipx *sipx;
+{
+ int domain = sipx->sipx_family;
+ int retry, s, on = 1;
+
+ retry = 1;
+ while ((s = socket(domain, type, proto)) < 0 && retry) {
+ syslog(LOG_ERR, "socket: %m");
+ sleep(5 * retry);
+ retry <<= 1;
+ }
+ if (retry == 0)
+ return (-1);
+ while (bind(s, (struct sockaddr *)sipx, sizeof (*sipx)) < 0 && retry) {
+ syslog(LOG_ERR, "bind: %m");
+ sleep(5 * retry);
+ retry <<= 1;
+ }
+ if (retry == 0)
+ return (-1);
+ if (domain==AF_IPX) {
+ struct ipx ipxdp;
+ if (setsockopt(s, 0, SO_HEADERS_ON_INPUT, &on, sizeof(on))) {
+ syslog(LOG_ERR, "setsockopt SEE HEADERS: %m");
+ exit(1);
+ }
+ if (ntohs(sipx->sipx_addr.x_port) == IPXPORT_RIP)
+ ipxdp.ipx_pt = IPXPROTO_RI;
+ else if (ntohs(sipx->sipx_addr.x_port) == IPXPORT_SAP)
+#ifdef IPXPROTO_SAP
+ ipxdp.ipx_pt = IPXPROTO_SAP;
+#else
+ ipxdp.ipx_pt = IPXPROTO_PXP;
+#endif
+ else {
+ syslog(LOG_ERR, "port should be either RIP or SAP");
+ exit(1);
+ }
+ if (setsockopt(s, 0, SO_DEFAULT_HEADERS, &ipxdp, sizeof(ipxdp))) {
+ syslog(LOG_ERR, "setsockopt SET HEADER: %m");
+ exit(1);
+ }
+ }
+ if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof (on)) < 0) {
+ syslog(LOG_ERR, "setsockopt SO_BROADCAST: %m");
+ exit(1);
+ }
+ return (s);
+}
+
+/*
+ * Fork and exit on EMT-- for profiling.
+ */
+void
+fkexit()
+{
+ if (fork() == 0)
+ exit(0);
+}
+
+void
+catchtimer()
+{
+ dobcast = 1;
+}
+
+void
+getinfo()
+{
+ FILE *fh;
+
+ fh = fopen("/var/log/ipxrouted.dmp", "a");
+ if(fh == NULL)
+ return;
+
+ dumpriptable(fh);
+ dumpsaptable(fh, sap_head);
+
+ fclose(fh);
+}
+
diff --git a/usr.sbin/IPXrouted/output.c b/usr.sbin/IPXrouted/output.c
new file mode 100644
index 0000000..1e60fc8
--- /dev/null
+++ b/usr.sbin/IPXrouted/output.c
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * This file includes significant work done at Cornell University by
+ * Bill Nesheim. That work included by permission.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)output.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+/*
+ * Routing Table Management Daemon
+ */
+#include <unistd.h>
+#include "defs.h"
+
+/*
+ * Apply the function "f" to all non-passive
+ * interfaces. If the interface supports the
+ * use of broadcasting use it, otherwise address
+ * the output to the known router.
+ */
+void
+toall(f, except, changesonly)
+ void (*f)(struct sockaddr *, int, struct interface *, int);
+ struct rt_entry *except;
+ int changesonly;
+{
+ register struct interface *ifp;
+ register struct sockaddr *dst;
+ register int flags;
+ register struct rt_entry *trt;
+ int onlist;
+ extern struct interface *ifnet;
+
+ for (ifp = ifnet; ifp; ifp = ifp->int_next) {
+ if (ifp->int_flags & IFF_PASSIVE)
+ continue;
+
+ /*
+ * Don't send it on interfaces in the except list.
+ */
+ onlist = 0;
+ trt = except;
+ while(trt) {
+ if (ifp == trt->rt_ifp) {
+ onlist = 1;
+ break;
+ }
+ trt = trt->rt_clone;
+ }
+ if (onlist)
+ continue;
+
+ dst = ifp->int_flags & IFF_BROADCAST ? &ifp->int_broadaddr :
+ ifp->int_flags & IFF_POINTOPOINT ? &ifp->int_dstaddr :
+ &ifp->int_addr;
+ flags = ifp->int_flags & IFF_INTERFACE ? MSG_DONTROUTE : 0;
+ (*f)(dst, flags, ifp, changesonly);
+ }
+}
+
+/*
+ * Output a preformed packet.
+ */
+void
+sndmsg(dst, flags, ifp, changesonly)
+ struct sockaddr *dst;
+ int flags;
+ struct interface *ifp;
+ int changesonly;
+{
+
+ (*afswitch[dst->sa_family].af_output)
+ (ripsock, flags, dst, sizeof (struct rip));
+ TRACE_OUTPUT(ifp, dst, sizeof (struct rip));
+}
+
+/*
+ * Supply dst with the contents of the routing tables.
+ * If this won't fit in one packet, chop it up into several.
+ *
+ * This must be done using the split horizon algorithm.
+ * 1. Don't send routing info to the interface from where it was received.
+ * 2. Don't publish an interface to itself.
+ * 3. If a route is received from more than one interface and the cost is
+ * the same, don't publish it on either interface. I am calling this
+ * clones.
+ */
+void
+supply(dst, flags, ifp, changesonly)
+ struct sockaddr *dst;
+ int flags;
+ struct interface *ifp;
+ int changesonly;
+{
+ register struct rt_entry *rt;
+ register struct rt_entry *crt; /* Clone route */
+ register struct rthash *rh;
+ register struct netinfo *nn;
+ register struct netinfo *n = msg->rip_nets;
+ struct sockaddr_ipx *sipx = (struct sockaddr_ipx *) dst;
+ af_output_t *output = afswitch[dst->sa_family].af_output;
+ int size, metric, ticks;
+ union ipx_net net;
+ int delay = 0;
+
+ if (sipx->sipx_port == 0)
+ sipx->sipx_port = htons(IPXPORT_RIP);
+
+ msg->rip_cmd = ntohs(RIPCMD_RESPONSE);
+ for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++)
+ for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
+ size = (char *)n - (char *)msg;
+ if (size >= ((MAXRIPNETS * sizeof (struct netinfo)) +
+ sizeof (msg->rip_cmd))) {
+ (*output)(ripsock, flags, dst, size);
+ TRACE_OUTPUT(ifp, dst, size);
+ n = msg->rip_nets;
+ delay++;
+ if(delay == 2) {
+ usleep(50000);
+ delay = 0;
+ }
+ }
+
+ if (changesonly && !(rt->rt_state & RTS_CHANGED))
+ continue;
+
+ /*
+ * This should do rule one and two of the split horizon
+ * algorithm.
+ */
+ if (rt->rt_ifp == ifp)
+ continue;
+
+ /*
+ * Rule 3.
+ * Look if we have clones (different routes to the same
+ * place with exactly the same cost).
+ *
+ * We should not publish on any of the clone interfaces.
+ */
+ crt = rt->rt_clone;
+ while (crt) {
+ if (crt->rt_ifp == ifp)
+ goto next;
+ crt = crt->rt_clone;
+ }
+
+ sipx = (struct sockaddr_ipx *)&rt->rt_dst;
+ if ((rt->rt_flags & (RTF_HOST|RTF_GATEWAY)) == RTF_HOST)
+ sipx = (struct sockaddr_ipx *)&rt->rt_router;
+ if (rt->rt_metric == HOPCNT_INFINITY)
+ metric = HOPCNT_INFINITY;
+ else {
+ metric = rt->rt_metric + 1;
+ /*
+ * We don't advertize routes with more than 15 hops.
+ */
+ if (metric >= HOPCNT_INFINITY)
+ continue;
+ }
+ /* XXX One day we should cater for slow interfaces also. */
+ ticks = rt->rt_ticks + 1;
+ net = sipx->sipx_addr.x_net;
+
+ /*
+ * Make sure that we don't put out a two net entries
+ * for a pt to pt link (one for the G route, one for the if)
+ * This is a kludge, and won't work if there are lots of nets.
+ */
+ for (nn = msg->rip_nets; nn < n; nn++) {
+ if (ipx_neteqnn(net, nn->rip_dst)) {
+ if (ticks < ntohs(nn->rip_ticks)) {
+ nn->rip_metric = htons(metric);
+ nn->rip_ticks = htons(ticks);
+ } else if ((ticks == ntohs(nn->rip_ticks)) &&
+ (metric < ntohs(nn->rip_metric))) {
+ nn->rip_metric = htons(metric);
+ nn->rip_ticks = htons(ticks);
+ }
+ goto next;
+ }
+ }
+ n->rip_dst = net;
+ n->rip_metric = htons(metric);
+ n->rip_ticks = htons(ticks);
+ n++;
+ next:;
+ }
+ if (n != msg->rip_nets) {
+ size = (char *)n - (char *)msg;
+ (*output)(ripsock, flags, dst, size);
+ TRACE_OUTPUT(ifp, dst, size);
+ }
+}
diff --git a/usr.sbin/IPXrouted/protocol.h b/usr.sbin/IPXrouted/protocol.h
new file mode 100644
index 0000000..7fae536
--- /dev/null
+++ b/usr.sbin/IPXrouted/protocol.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * This file includes significant work done at Cornell University by
+ * Bill Nesheim. That work included by permission.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)protocol.h 8.1 (Berkeley) 6/5/93
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * IPX Routing Information Protocol
+ *
+ */
+
+struct netinfo {
+ union ipx_net rip_dst; /* destination net */
+ u_short rip_metric; /* cost of route */
+ u_short rip_ticks; /* cost of route */
+};
+
+struct rip {
+ u_short rip_cmd; /* request/response */
+ struct netinfo rip_nets[1]; /* variable length */
+};
+
+/*
+ * Packet types.
+ */
+#define RIPCMD_REQUEST 1 /* want info */
+#define RIPCMD_RESPONSE 2 /* responding to request */
+
+#define RIPCMD_MAX 3
+#ifdef RIPCMDS
+char *ripcmds[RIPCMD_MAX] =
+ { "#0", "REQUEST", "RESPONSE" };
+#endif
+
+#define HOPCNT_INFINITY 16 /* per IPX */
+#define DSTNETS_ALL 0xffffffff /* per IPX */
+#define MAXRXPACKETSIZE 1500 /* max rx broadcast size */
+#define MAXRIPNETS 50 /* max nets in tx packet */
+
+extern union ipx_net ipx_anynet;
+extern union ipx_net ipx_zeronet;
+
+/*
+ * Timer values used in managing the routing table.
+ * Every update forces an entry's timer to be reset. After
+ * EXPIRE_TIME without updates, the entry is marked invalid,
+ * but held onto until GARBAGE_TIME so that others may
+ * see it "be deleted".
+ */
+#define TIMER_RATE 30 /* alarm clocks every 30 seconds */
+
+#define SUPPLY_INTERVAL 30 /* time to supply tables */
+#define RIP_INTERVAL 60 /* time to supply rip tables */
+
+#define EXPIRE_TIME 180 /* time to mark entry invalid */
+#define GARBAGE_TIME 240 /* time to garbage collect */
diff --git a/usr.sbin/IPXrouted/sap.h b/usr.sbin/IPXrouted/sap.h
new file mode 100644
index 0000000..b4e9dcb
--- /dev/null
+++ b/usr.sbin/IPXrouted/sap.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by John Hay.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY John Hay AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL John Hay OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef _SAP_H_
+#define _SAP_H_
+
+#define SAP_REQ 1
+#define SAP_RESP 2
+#define SAP_REQ_NEAR 3
+#define SAP_RESP_NEAR 4
+
+#define SAPCMD_MAX 5
+#ifdef SAPCMDS
+char *sapcmds[SAPCMD_MAX] =
+ { "#0", "REQUEST", "RESPONSE", "REQ NEAREST", "RESP NEAREST"};
+#endif
+
+#define MAXSAPENTRIES 7
+#define SAP_WILDCARD 0xFFFF
+#define SERVNAMELEN 48
+typedef struct sap_info {
+ u_short ServType;
+ char ServName[SERVNAMELEN];
+ struct ipx_addr ipx;
+ u_short hops;
+ }sap_info;
+
+typedef struct sap_packet {
+ u_short sap_cmd;
+ sap_info sap[0]; /* Variable length. */
+ }sap_packet;
+
+typedef struct sap_entry {
+ struct sap_entry *forw;
+ struct sap_entry *back;
+ struct sap_entry *clone;
+ struct interface *ifp;
+ struct sap_info sap;
+ struct sockaddr source;
+ int hash;
+ int state;
+ int timer;
+ }sap_entry;
+
+#define SAPHASHSIZ 256 /* Should be a power of 2 */
+#define SAPHASHMASK (SAPHASHSIZ-1)
+typedef struct sap_hash {
+ struct sap_entry *forw;
+ struct sap_entry *back;
+ }sap_hash;
+
+extern sap_hash sap_head[SAPHASHSIZ];
+
+extern struct sap_packet *sap_msg;
+
+void sapinit(void);
+void sap_input(struct sockaddr *from, int size);
+void sapsndmsg(struct sockaddr *dst, int flags, struct interface *ifp,
+ int changesonly);
+void sap_supply_toall(int changesonly);
+void sap_supply(struct sockaddr *dst,
+ int flags,
+ struct interface *ifp,
+ int ServType,
+ int changesonly);
+
+struct sap_entry *sap_lookup(u_short ServType, char *ServName);
+struct sap_entry *sap_nearestserver(ushort ServType, struct interface *ifp);
+void sap_add(struct sap_info *si, struct sockaddr *from);
+void sap_change(struct sap_entry *sap,
+ struct sap_info *si,
+ struct sockaddr *from);
+void sap_add_clone(struct sap_entry *sap,
+ struct sap_info *clone,
+ struct sockaddr *from);
+void sap_delete(struct sap_entry *sap);
+
+#endif /*_SAP_H_*/
+
diff --git a/usr.sbin/IPXrouted/sap_input.c b/usr.sbin/IPXrouted/sap_input.c
new file mode 100644
index 0000000..59281b2
--- /dev/null
+++ b/usr.sbin/IPXrouted/sap_input.c
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by John Hay.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY John Hay AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL John Hay OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * IPX Routing Table Management Daemon
+ */
+#include "defs.h"
+
+int dognreply = 1;
+
+/*
+ * Process a newly received packet.
+ */
+void
+sap_input(from, size)
+ struct sockaddr *from;
+ int size;
+{
+ int newsize;
+ int sapchanged = 0;
+ struct sap_entry *sap;
+ struct sap_info *n;
+ struct interface *ifp = 0;
+ struct afswitch *afp;
+ struct sockaddr_ipx *ipxp;
+
+ ifp = if_ifwithnet(from);
+ ipxp = (struct sockaddr_ipx *)from;
+ if (ifp == 0) {
+ if(ftrace) {
+ fprintf(ftrace, "Received bogus packet from %s\n",
+ ipxdp_ntoa(&ipxp->sipx_addr));
+ }
+ return;
+ }
+
+ if (ftrace)
+ dumpsappacket(ftrace, "received", from, (char *)sap_msg , size);
+
+ if (from->sa_family >= AF_MAX)
+ return;
+ afp = &afswitch[from->sa_family];
+
+ size -= sizeof (u_short) /* command */;
+ n = sap_msg->sap;
+
+ switch (ntohs(sap_msg->sap_cmd)) {
+
+ case SAP_REQ_NEAR:
+ if (ftrace)
+ fprintf(ftrace, "Received a sap REQ_NEAR packet.\n");
+ if (!dognreply)
+ return;
+ sap = sap_nearestserver(n->ServType, ifp);
+ if (sap == NULL)
+ return;
+ sap_msg->sap_cmd = htons(SAP_RESP_NEAR);
+ *n = sap->sap;
+ n->hops = htons(ntohs(n->hops) + 1);
+ if (ntohs(n->hops) >= HOPCNT_INFINITY)
+ return;
+
+ newsize = sizeof(struct sap_info) + sizeof(struct sap_packet);
+ (*afp->af_output)(sapsock, 0, from, newsize);
+ if (ftrace) {
+ fprintf(ftrace, "sap_nearestserver %X %s returned:\n",
+ ntohs(n->ServType),
+ ifp->int_name);
+ fprintf(ftrace, " service %04X %-20.20s "
+ "addr %s.%04X metric %d\n",
+ ntohs(sap->sap.ServType),
+ sap->sap.ServName,
+ ipxdp_ntoa(&sap->sap.ipx),
+ ntohs(sap->sap.ipx.x_port),
+ ntohs(sap->sap.hops));
+ }
+ return;
+
+ case SAP_REQ:
+ if (ftrace)
+ fprintf(ftrace, "Received a sap REQ packet.\n");
+
+ sap_supply(from, 0, ifp, n->ServType, 0);
+ return;
+
+ case SAP_RESP_NEAR:
+ /* XXX We do nothing here, for the moment.
+ * Maybe we should check if the service is in our table?
+ *
+ */
+ if (ftrace)
+ fprintf(ftrace, "Received a sap RESP_NEAR packet.\n");
+
+ return;
+
+ case SAP_RESP:
+ if (ftrace)
+ fprintf(ftrace, "Received a sap RESP packet.\n");
+
+ (*afp->af_canon)(from);
+
+ for (; size > 0; size -= sizeof (struct sap_info), n++) {
+ if (size < sizeof (struct netinfo))
+ break;
+ /*
+ * The idea here is that if the hop count is more
+ * than INFINITY it is bogus and should be discarded.
+ * If it is equal to INFINITY it is a message to say
+ * that a service went down. If we don't allready
+ * have it in our tables discard it. Otherwise
+ * update our table and set the timer to EXPIRE_TIME
+ * so that it is removed next time we go through the
+ * tables.
+ */
+ if (ntohs(n->hops) > HOPCNT_INFINITY)
+ continue;
+ sap = sap_lookup(n->ServType, n->ServName);
+ if (sap == 0) {
+ if (ntohs(n->hops) == HOPCNT_INFINITY)
+ continue;
+ sap_add(n, from);
+ sapchanged = 1;
+ continue;
+ }
+
+ /*
+ * A clone is a different route to the same service
+ * with exactly the same cost (metric).
+ * They must all be recorded because those interfaces
+ * must be handled in the same way as the first route
+ * to that service. ie When using the split horizon
+ * algorithm we must look at these interfaces also.
+ *
+ * Update if from gateway and different,
+ * from anywhere and less hops or
+ * getting stale and equivalent.
+ */
+ if (((ifp != sap->ifp) ||
+ !equal(&sap->source, from)) &&
+ (n->hops == sap->sap.hops) &&
+ (ntohs(n->hops) != HOPCNT_INFINITY)) {
+ register struct sap_entry *tsap = sap->clone;
+
+ while (tsap) {
+ if ((ifp == tsap->ifp) &&
+ equal(&tsap->source, from)) {
+ tsap->timer = 0;
+ break;
+ }
+ tsap = tsap->clone;
+ }
+ if (tsap == NULL) {
+ sap_add_clone(sap, n, from);
+ }
+ continue;
+ }
+ if ((ifp == sap->ifp) &&
+ equal(&sap->source, from) &&
+ (ntohs(n->hops) == ntohs(sap->sap.hops)))
+ sap->timer = 0;
+ else if (((ifp == sap->ifp) &&
+ equal(&sap->source, from) &&
+ (n->hops != sap->sap.hops)) ||
+ (ntohs(n->hops) < ntohs(sap->sap.hops)) ||
+ (sap->timer > (EXPIRE_TIME*2/3) &&
+ ntohs(sap->sap.hops) == ntohs(n->hops) &&
+ ntohs(n->hops) != HOPCNT_INFINITY)) {
+ sap_change(sap, n, from);
+ sapchanged = 1;
+ }
+ }
+ if (sapchanged) {
+ register struct sap_entry *sap;
+ register struct sap_hash *sh;
+ sap_supply_toall(1);
+
+ for (sh = sap_head; sh < &sap_head[SAPHASHSIZ]; sh++)
+ for (sap = sh->forw;
+ sap != (struct sap_entry *)sh;
+ sap = sap->forw)
+ sap->state &= ~RTS_CHANGED;
+ }
+ return;
+ }
+}
diff --git a/usr.sbin/IPXrouted/sap_output.c b/usr.sbin/IPXrouted/sap_output.c
new file mode 100644
index 0000000..d1f1a28
--- /dev/null
+++ b/usr.sbin/IPXrouted/sap_output.c
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by John Hay.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY John Hay AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL John Hay OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Routing Table Management Daemon
+ */
+#include <unistd.h>
+#include "defs.h"
+
+/*
+ * Apply the function "f" to all non-passive
+ * interfaces. If the interface supports the
+ * use of broadcasting use it, otherwise address
+ * the output to the known router.
+ */
+void
+sap_supply_toall(changesonly)
+ int changesonly;
+{
+ register struct interface *ifp;
+ struct sockaddr dst;
+ register struct sockaddr_ipx *ipx_dst;
+ register int flags;
+ extern struct interface *ifnet;
+
+ ipx_dst = (struct sockaddr_ipx *)&dst;
+
+ for (ifp = ifnet; ifp; ifp = ifp->int_next) {
+ if (ifp->int_flags & IFF_PASSIVE)
+ continue;
+
+ dst = ifp->int_flags & IFF_BROADCAST ? ifp->int_broadaddr :
+ ifp->int_flags & IFF_POINTOPOINT ? ifp->int_dstaddr :
+ ifp->int_addr;
+
+ ipx_dst->sipx_addr.x_port = htons(IPXPORT_SAP);
+
+ flags = ifp->int_flags & IFF_INTERFACE ? MSG_DONTROUTE : 0;
+ sap_supply(&dst, flags, ifp, SAP_WILDCARD, changesonly);
+ }
+}
+
+void
+sapsndmsg(dst, flags, ifp, changesonly)
+ struct sockaddr *dst;
+ int flags;
+ struct interface *ifp;
+ int changesonly;
+{
+ struct sockaddr t_dst;
+ struct sockaddr_ipx *ipx_dst;
+
+ t_dst = *dst;
+ ipx_dst = (struct sockaddr_ipx *)&t_dst;
+
+ if (ipx_dst->sipx_addr.x_port == 0)
+ ipx_dst->sipx_addr.x_port = htons(IPXPORT_SAP);
+
+ (*afswitch[dst->sa_family].af_output)
+ (sapsock, flags, &t_dst,
+ sizeof (struct sap_packet) + sizeof(u_short));
+ TRACE_SAP_OUTPUT(ifp, &t_dst,
+ sizeof (struct sap_packet) + sizeof(u_short));
+}
+
+/*
+ * Supply dst with the contents of the SAP tables. If the ServType ==
+ * SAP_WILDCARD (0xFFFF) supply the whole table, otherwise only the
+ * services that are of ServType. If this won't fit in one packet, chop
+ * it up into several.
+ *
+ * This must be done using the split horizon algorithm.
+ * 1. Don't send SAP info to the interface from where it was received.
+ * 2. If a service is received from more than one interface and the cost is
+ * the same, don't publish it on either interface. I am calling this
+ * clones.
+ */
+void
+sap_supply(dst, flags, ifp, ServType, changesonly)
+ struct sockaddr *dst;
+ int flags;
+ struct interface *ifp;
+ int ServType;
+ int changesonly;
+{
+ register struct sap_entry *sap;
+ register struct sap_entry *csap; /* Clone route */
+ register struct sap_hash *sh;
+ register struct sap_info *n = sap_msg->sap;
+ struct sap_hash *base = sap_head;
+ struct sockaddr_ipx *sipx = (struct sockaddr_ipx *) dst;
+ af_output_t *output = afswitch[dst->sa_family].af_output;
+ int size, metric;
+ int delay = 0;
+
+ if (sipx->sipx_port == 0)
+ sipx->sipx_port = htons(IPXPORT_SAP);
+
+ sap_msg->sap_cmd = ntohs(SAP_RESP);
+
+ for (sh = base; sh < &base[SAPHASHSIZ]; sh++)
+ for (sap = sh->forw; sap != (struct sap_entry *)sh; sap = sap->forw) {
+ size = (char *)n - (char *)sap_msg;
+ if (size >= ((MAXSAPENTRIES * sizeof (struct sap_info)) +
+ sizeof (sap_msg->sap_cmd))) {
+ (*output)(sapsock, flags, dst, size);
+ TRACE_SAP_OUTPUT(ifp, dst, size);
+ n = sap_msg->sap;
+ delay++;
+ if(delay == 2) {
+ usleep(50000);
+ delay = 0;
+ }
+ }
+
+ if (changesonly && !(sap->state & RTS_CHANGED))
+ continue;
+
+ /*
+ * Check for the servicetype except if the ServType is
+ * a wildcard (0xFFFF).
+ */
+ if ((ServType != SAP_WILDCARD) &&
+ (ServType != sap->sap.ServType))
+ continue;
+
+ /*
+ * This should do rule one and two of the split horizon
+ * algorithm.
+ */
+ if (sap->ifp == ifp)
+ continue;
+
+ /*
+ * Rule 2.
+ * Look if we have clones (different routes to the same
+ * place with exactly the same cost).
+ *
+ * We should not publish on any of the clone interfaces.
+ */
+ csap = sap->clone;
+ while (csap) {
+ if (csap->ifp == ifp)
+ goto next;
+ csap = csap->clone;
+ }
+
+ /*
+ * Don't advertise services with more than 15 hops. It
+ * will be confused with a service that has gone down.
+ */
+ if (ntohs(sap->sap.hops) == (HOPCNT_INFINITY - 1))
+ continue;
+ metric = min(ntohs(sap->sap.hops) + 1, HOPCNT_INFINITY);
+
+ *n = sap->sap;
+ n->hops = htons(metric);
+ n++;
+next:
+ ;
+ }
+ if (n != sap_msg->sap) {
+ size = (char *)n - (char *)sap_msg;
+ (*output)(sapsock, flags, dst, size);
+ TRACE_SAP_OUTPUT(ifp, dst, size);
+ }
+}
+
diff --git a/usr.sbin/IPXrouted/sap_tables.c b/usr.sbin/IPXrouted/sap_tables.c
new file mode 100644
index 0000000..f2d83e0
--- /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.
+ *
+ * $FreeBSD$
+ */
+
+#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..1740a88
--- /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.
+ *
+ * $FreeBSD$
+ */
+
+#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 <errno.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;
+{
+ 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..3f9693f
--- /dev/null
+++ b/usr.sbin/IPXrouted/table.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)table.h 5.1 (Berkeley) 6/4/85 (routed/table.h)
+ *
+ * @(#)table.h 8.1 (Berkeley) 6/5/93
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Routing table management daemon.
+ */
+
+/*
+ * Routing table structure; differs a bit from kernel tables.
+ *
+ * Note: the union below must agree in the first 4 members
+ * so the ioctl's will work.
+ */
+struct rthash {
+ struct rt_entry *rt_forw;
+ struct rt_entry *rt_back;
+};
+
+#ifdef RTM_ADD
+#define rtentry ortentry
+#endif
+
+struct rt_entry {
+ struct rt_entry *rt_forw;
+ struct rt_entry *rt_back;
+ union {
+ struct rtentry rtu_rt;
+ struct rtuentry {
+ u_long rtu_hash;
+ struct sockaddr rtu_dst;
+ struct sockaddr rtu_router;
+ short rtu_rtflags; /* used by old rtioctl */
+ short rtu_wasted; /* XXX routed does it this way. */
+ int rtu_flags;
+ int rtu_state;
+ int rtu_timer;
+ int rtu_metric;
+ int rtu_ticks;
+ struct interface *rtu_ifp;
+ } rtu_entry;
+ } rt_rtu;
+ struct rt_entry *rt_clone;
+};
+
+#define rt_rt rt_rtu.rtu_entry /* pass to ioctl */
+#define rt_hash rt_rtu.rtu_entry.rtu_hash /* for net or host */
+#define rt_dst rt_rtu.rtu_entry.rtu_dst /* match value */
+#define rt_router rt_rtu.rtu_entry.rtu_router /* who to forward to */
+#define rt_flags rt_rtu.rtu_entry.rtu_flags /* kernel flags */
+#define rt_timer rt_rtu.rtu_entry.rtu_timer /* for invalidation */
+#define rt_state rt_rtu.rtu_entry.rtu_state /* see below */
+#define rt_metric rt_rtu.rtu_entry.rtu_metric /* cost of route */
+#define rt_ticks rt_rtu.rtu_entry.rtu_ticks /* time of route */
+#define rt_ifp rt_rtu.rtu_entry.rtu_ifp /* interface to take */
+
+#define ROUTEHASHSIZ 128 /* must be a power of 2 */
+#define ROUTEHASHMASK (ROUTEHASHSIZ - 1)
+
+/*
+ * "State" of routing table entry.
+ */
+#define RTS_CHANGED 0x1 /* route has been altered recently */
+#define RTS_PASSIVE IFF_PASSIVE /* don't time out route */
+#define RTS_INTERFACE IFF_INTERFACE /* route is for network interface */
+#define RTS_REMOTE IFF_REMOTE /* route is for ``remote'' entity */
+
+extern struct rthash nethash[ROUTEHASHSIZ];
+struct rt_entry *rtlookup(struct sockaddr *);
+struct rt_entry *rtfind(struct sockaddr *);
+void rtadd(struct sockaddr *, struct sockaddr *, short, short, int);
+void rtadd_clone(struct rt_entry *, struct sockaddr *, struct sockaddr *,
+ short, short, int);
+void rtchange(struct rt_entry *, struct sockaddr *, short, short);
+void rtdelete(struct rt_entry *);
+int rtioctl(int, struct rtuentry *);
+void rtinit(void);
+
diff --git a/usr.sbin/IPXrouted/tables.c b/usr.sbin/IPXrouted/tables.c
new file mode 100644
index 0000000..8801b11
--- /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.
+ *
+ * $FreeBSD$
+ */
+
+#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..8b31c1d
--- /dev/null
+++ b/usr.sbin/IPXrouted/timer.c
@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * This file includes significant work done at Cornell University by
+ * Bill Nesheim. That work included by permission.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef lint
+static 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..4ddb1da
--- /dev/null
+++ b/usr.sbin/IPXrouted/trace.c
@@ -0,0 +1,521 @@
+/*
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * This file includes significant work done at Cornell University by
+ * Bill Nesheim. That work included by permission.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)trace.c 8.1 (Berkeley) 6/5/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/*
+ * Routing Table Management Daemon
+ */
+#define RIPCMDS
+#define SAPCMDS
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <time.h>
+#include "defs.h"
+
+#define NRECORDS 50 /* size of circular trace buffer */
+#ifdef DEBUG
+FILE *ftrace = stdout;
+int tracing = 1;
+#else /* DEBUG */
+FILE *ftrace = NULL;
+int tracing = 0;
+#endif
+
+void dumpif(FILE *fd, struct interface *ifp);
+void dumptrace(FILE *fd, char *dir, struct ifdebug *ifd);
+
+void
+traceinit(ifp)
+ register struct interface *ifp;
+{
+ static int iftraceinit();
+
+ if (iftraceinit(ifp, &ifp->int_input) &&
+ iftraceinit(ifp, &ifp->int_output))
+ return;
+ tracing = 0;
+ syslog(LOG_ERR, "traceinit: can't init %s\n", ifp->int_name);
+}
+
+static int
+iftraceinit(ifp, ifd)
+ struct interface *ifp;
+ register struct ifdebug *ifd;
+{
+ register struct iftrace *t;
+
+ ifd->ifd_records =
+ (struct iftrace *)malloc(NRECORDS * sizeof (struct iftrace));
+ if (ifd->ifd_records == 0)
+ return (0);
+ ifd->ifd_front = ifd->ifd_records;
+ ifd->ifd_count = 0;
+ for (t = ifd->ifd_records; t < ifd->ifd_records + NRECORDS; t++) {
+ t->ift_size = 0;
+ t->ift_packet = 0;
+ }
+ ifd->ifd_if = ifp;
+ return (1);
+}
+
+void
+traceon(file)
+ char *file;
+{
+
+ if (ftrace != NULL)
+ return;
+ ftrace = fopen(file, "a");
+ if (ftrace == NULL)
+ return;
+ dup2(fileno(ftrace), 1);
+ dup2(fileno(ftrace), 2);
+ tracing = 1;
+}
+
+void
+traceoff(void)
+{
+ if (!tracing)
+ return;
+ if (ftrace != NULL)
+ fclose(ftrace);
+ ftrace = NULL;
+ tracing = 0;
+}
+
+void
+trace(ifd, who, p, len, m)
+ register struct ifdebug *ifd;
+ struct sockaddr *who;
+ char *p;
+ int len, m;
+{
+ register struct iftrace *t;
+
+ if (ifd->ifd_records == 0)
+ return;
+ t = ifd->ifd_front++;
+ if (ifd->ifd_front >= ifd->ifd_records + NRECORDS)
+ ifd->ifd_front = ifd->ifd_records;
+ if (ifd->ifd_count < NRECORDS)
+ ifd->ifd_count++;
+ if (t->ift_size > 0 && t->ift_packet)
+ free(t->ift_packet);
+ t->ift_packet = 0;
+ t->ift_stamp = time(0);
+ t->ift_who = *who;
+ if (len > 0) {
+ t->ift_packet = malloc(len);
+ if (t->ift_packet)
+ bcopy(p, t->ift_packet, len);
+ else
+ len = 0;
+ }
+ t->ift_size = len;
+ t->ift_metric = m;
+}
+
+void
+traceaction(fd, action, rt)
+ FILE *fd;
+ char *action;
+ struct rt_entry *rt;
+{
+ struct sockaddr_ipx *dst, *gate;
+ static struct bits {
+ int t_bits;
+ char *t_name;
+ } flagbits[] = {
+ { RTF_UP, "UP" },
+ { RTF_GATEWAY, "GATEWAY" },
+ { RTF_HOST, "HOST" },
+ { 0 }
+ }, statebits[] = {
+ { RTS_PASSIVE, "PASSIVE" },
+ { RTS_REMOTE, "REMOTE" },
+ { RTS_INTERFACE,"INTERFACE" },
+ { RTS_CHANGED, "CHANGED" },
+ { 0 }
+ };
+ register struct bits *p;
+ register int first;
+ char *cp;
+
+ if (fd == NULL)
+ return;
+ fprintf(fd, "%s ", action);
+ dst = (struct sockaddr_ipx *)&rt->rt_dst;
+ gate = (struct sockaddr_ipx *)&rt->rt_router;
+ fprintf(fd, "dst %s, ", ipxdp_ntoa(&dst->sipx_addr));
+ fprintf(fd, "router %s, metric %d, ticks %d, flags",
+ ipxdp_ntoa(&gate->sipx_addr), rt->rt_metric, rt->rt_ticks);
+ cp = " %s";
+ for (first = 1, p = flagbits; p->t_bits > 0; p++) {
+ if ((rt->rt_flags & p->t_bits) == 0)
+ continue;
+ fprintf(fd, cp, p->t_name);
+ if (first) {
+ cp = "|%s";
+ first = 0;
+ }
+ }
+ fprintf(fd, " state");
+ cp = " %s";
+ for (first = 1, p = statebits; p->t_bits > 0; p++) {
+ if ((rt->rt_state & p->t_bits) == 0)
+ continue;
+ fprintf(fd, cp, p->t_name);
+ if (first) {
+ cp = "|%s";
+ first = 0;
+ }
+ }
+ putc('\n', fd);
+ if (!tracepackets && (rt->rt_state & RTS_PASSIVE) == 0 && rt->rt_ifp)
+ dumpif(fd, rt->rt_ifp);
+ fflush(fd);
+}
+
+void
+traceactionlog(action, rt)
+ char *action;
+ struct rt_entry *rt;
+{
+ struct sockaddr_ipx *dst, *gate;
+ static struct bits {
+ int t_bits;
+ char *t_name;
+ } flagbits[] = {
+ { RTF_UP, "UP" },
+ { RTF_GATEWAY, "GATEWAY" },
+ { RTF_HOST, "HOST" },
+ { 0 }
+ }, statebits[] = {
+ { RTS_PASSIVE, "PASSIVE" },
+ { RTS_REMOTE, "REMOTE" },
+ { RTS_INTERFACE,"INTERFACE" },
+ { RTS_CHANGED, "CHANGED" },
+ { 0 }
+ };
+ register struct bits *p;
+ register int first;
+ char *cp;
+ char *lstr, *olstr;
+
+ dst = (struct sockaddr_ipx *)&rt->rt_dst;
+ gate = (struct sockaddr_ipx *)&rt->rt_router;
+ asprintf(&lstr, "%s dst %s,", action, ipxdp_ntoa(&dst->sipx_addr));
+ olstr = lstr;
+ asprintf(&lstr, "%s router %s, metric %d, ticks %d, flags",
+ olstr, ipxdp_ntoa(&gate->sipx_addr), rt->rt_metric, rt->rt_ticks);
+ free(olstr);
+ olstr = lstr;
+ cp = "%s %s";
+ for (first = 1, p = flagbits; p->t_bits > 0; p++) {
+ if ((rt->rt_flags & p->t_bits) == 0)
+ continue;
+ asprintf(&lstr, cp, olstr, p->t_name);
+ free(olstr);
+ olstr = lstr;
+ if (first) {
+ cp = "%s|%s";
+ first = 0;
+ }
+ }
+ asprintf(&lstr, "%s state", olstr);
+ free(olstr);
+ olstr = lstr;
+ cp = "%s %s";
+ for (first = 1, p = statebits; p->t_bits > 0; p++) {
+ if ((rt->rt_state & p->t_bits) == 0)
+ continue;
+ asprintf(&lstr, cp, olstr, p->t_name);
+ free(olstr);
+ olstr = lstr;
+ if (first) {
+ cp = "%s|%s";
+ first = 0;
+ }
+ }
+ syslog(LOG_DEBUG, "%s", lstr);
+ free(lstr);
+}
+
+void
+tracesapactionlog(action, sap)
+ char *action;
+ struct sap_entry *sap;
+{
+ syslog(LOG_DEBUG, "%-12.12s service %04X %-20.20s "
+ "addr %s.%04X %c metric %d\n",
+ action,
+ ntohs(sap->sap.ServType),
+ sap->sap.ServName,
+ ipxdp_ntoa(&sap->sap.ipx),
+ ntohs(sap->sap.ipx.x_port),
+ (sap->clone ? 'C' : ' '),
+ ntohs(sap->sap.hops));
+}
+
+void
+dumpif(fd, ifp)
+ register struct interface *ifp;
+ FILE *fd;
+{
+ if (ifp->int_input.ifd_count || ifp->int_output.ifd_count) {
+ fprintf(fd, "*** Packet history for interface %s ***\n",
+ ifp->int_name);
+ dumptrace(fd, "to", &ifp->int_output);
+ dumptrace(fd, "from", &ifp->int_input);
+ fprintf(fd, "*** end packet history ***\n");
+ }
+}
+
+void
+dumptrace(fd, dir, ifd)
+ FILE *fd;
+ char *dir;
+ register struct ifdebug *ifd;
+{
+ register struct iftrace *t;
+ char *cp = !strcmp(dir, "to") ? "Output" : "Input";
+
+ if (ifd->ifd_front == ifd->ifd_records &&
+ ifd->ifd_front->ift_size == 0) {
+ fprintf(fd, "%s: no packets.\n", cp);
+ return;
+ }
+ fprintf(fd, "%s trace:\n", cp);
+ t = ifd->ifd_front - ifd->ifd_count;
+ if (t < ifd->ifd_records)
+ t += NRECORDS;
+ for ( ; ifd->ifd_count; ifd->ifd_count--, t++) {
+ if (t >= ifd->ifd_records + NRECORDS)
+ t = ifd->ifd_records;
+ if (t->ift_size == 0)
+ continue;
+ fprintf(fd, "%.24s: metric=%d\n", ctime(&t->ift_stamp),
+ t->ift_metric);
+ dumppacket(fd, dir, &t->ift_who, t->ift_packet, t->ift_size);
+ }
+}
+
+void
+dumppacket(fd, dir, source, cp, size)
+ FILE *fd;
+ char *dir;
+ struct sockaddr *source;
+ char *cp;
+ register int size;
+{
+ register struct rip *msg = (struct rip *)cp;
+ register struct netinfo *n;
+ struct sockaddr_ipx *who = (struct sockaddr_ipx *)source;
+
+ if (msg->rip_cmd && ntohs(msg->rip_cmd) < RIPCMD_MAX)
+ fprintf(fd, "%s %s %s#%x", ripcmds[ntohs(msg->rip_cmd)],
+ dir, ipxdp_ntoa(&who->sipx_addr),
+ ntohs(who->sipx_addr.x_port));
+ else {
+ fprintf(fd, "Bad cmd 0x%x %s %s#%x\n", ntohs(msg->rip_cmd),
+ dir, ipxdp_ntoa(&who->sipx_addr),
+ ntohs(who->sipx_addr.x_port));
+ fprintf(fd, "size=%d cp=%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..c83a9d1
--- /dev/null
+++ b/usr.sbin/IPXrouted/trace.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * This file includes significant work done at Cornell University by
+ * Bill Nesheim. That work included by permission.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)trace.h 8.1 (Berkeley) 6/5/93
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * IPX Routing Information Protocol.
+ */
+
+/*
+ * Trace record format.
+ */
+struct iftrace {
+ time_t ift_stamp; /* time stamp */
+ struct sockaddr ift_who; /* from/to */
+ char *ift_packet; /* pointer to packet */
+ short ift_size; /* size of packet */
+ short ift_metric; /* metric */
+};
+
+/*
+ * Per interface packet tracing buffers. An incoming and
+ * outgoing circular buffer of packets is maintained, per
+ * interface, for debugging. Buffers are dumped whenever
+ * an interface is marked down.
+ */
+struct ifdebug {
+ struct iftrace *ifd_records; /* array of trace records */
+ struct iftrace *ifd_front; /* next empty trace record */
+ int ifd_count; /* number of unprinted records */
+ struct interface *ifd_if; /* for locating stuff */
+};
+
+/*
+ * Packet tracing stuff.
+ */
+int tracepackets; /* watch packets as they go by */
+int tracing; /* on/off */
+FILE *ftrace; /* output trace file */
+
+#define TRACE_ACTION(action, route) { \
+ if (tracing) \
+ traceaction(ftrace, "action", route); \
+ traceactionlog(action, route); \
+ }
+#define TRACE_SAP_ACTION(action, service) { \
+ tracesapactionlog(action, service); \
+ }
+#define TRACE_INPUT(ifp, src, size) { \
+ if (tracing) { \
+ ifp = if_iflookup(src); \
+ if (ifp) \
+ trace(&ifp->int_input, src, \
+ &packet[sizeof(struct ipx)], size, \
+ ntohl(ifp->int_metric)); \
+ } \
+ if (tracepackets && ftrace) \
+ dumppacket(ftrace, "from", src, \
+ &packet[sizeof(struct ipx)], size); \
+ }
+#define TRACE_OUTPUT(ifp, dst, size) { \
+ if (tracing) { \
+ ifp = if_iflookup(dst); \
+ if (ifp) \
+ trace(&ifp->int_output, dst, \
+ &packet[sizeof(struct ipx)], \
+ size, ifp->int_metric); \
+ } \
+ if (tracepackets && ftrace) \
+ dumppacket(ftrace, "to", dst, \
+ &packet[sizeof(struct ipx)], size); \
+ }
+
+#define TRACE_SAP_OUTPUT(ifp, dst, size) { \
+ if (tracing) { \
+ ifp = if_iflookup(dst); \
+ if (ifp) \
+ trace(&ifp->int_output, dst, \
+ &packet[sizeof(struct ipx)], \
+ size, ifp->int_metric); \
+ } \
+ if (tracepackets && ftrace) \
+ dumpsappacket(ftrace, "to", dst, \
+ &packet[sizeof(struct ipx)], size); \
+ }
+
+void traceinit(struct interface *);
+void traceon(char *file);
+void traceoff(void);
+void traceaction(FILE *, char *, struct rt_entry *);
+void traceactionlog(char *, struct rt_entry *);
+void tracesapactionlog(char *action, struct sap_entry *sap);
+void trace(struct ifdebug *, struct sockaddr *, char *, int, int);
+void dumppacket(FILE *, char *, struct sockaddr *, char *, int);
+void dumpsappacket(FILE *, char *, struct sockaddr *, char *, int);
+void dumpsaptable(FILE *fd, struct sap_hash *sh);
+void dumpriptable(FILE *fd);
+
+char *ipxdp_nettoa(union ipx_net);
+char *ipxdp_ntoa(struct ipx_addr *);
+
diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile
new file mode 100644
index 0000000..ce233e4
--- /dev/null
+++ b/usr.sbin/Makefile
@@ -0,0 +1,197 @@
+# From: @(#)Makefile 5.20 (Berkeley) 6/12/93
+# $FreeBSD$
+
+# XXX MISSING: mkproto
+SUBDIR= IPXrouted \
+ ac \
+ accton \
+ adduser \
+ amd \
+ ancontrol \
+ arp \
+ atm \
+ bootparamd \
+ burncd \
+ cdcontrol \
+ chkgrp \
+ chown \
+ chroot \
+ ckdist \
+ config \
+ cron \
+ crunch \
+ ctm \
+ daemon \
+ dev_mkdb \
+ devinfo \
+ digictl \
+ edquota \
+ elf2aout \
+ extattrctl \
+ faithd \
+ fdcontrol \
+ fdformat \
+ fdread \
+ fdwrite \
+ getextattr \
+ ifmcstat \
+ inetd \
+ iostat \
+ jail \
+ kbdcontrol \
+ kbdmap \
+ kernbb \
+ kldxref \
+ lastlogin \
+ mailwrapper \
+ manctl \
+ memcontrol \
+ mergemaster \
+ mixer \
+ mld6query \
+ mlxcontrol \
+ mountd \
+ moused \
+ mrouted \
+ mtest \
+ mtree \
+ ndp \
+ newsyslog \
+ nfsd \
+ ngctl \
+ ntp \
+ nghook \
+ pccard \
+ pciconf \
+ periodic \
+ pkg_install \
+ ppp \
+ pppd \
+ pppstats \
+ procctl \
+ pstat \
+ pw \
+ pwd_mkdb \
+ quot \
+ quotaon \
+ rarpd \
+ raycontrol \
+ repquota \
+ rip6query \
+ rmt \
+ route6d \
+ rpcbind \
+ rpc.lockd \
+ rpc.statd \
+ rpc.umntall \
+ rpc.yppasswdd \
+ rpc.ypupdated \
+ rpc.ypxfrd \
+ rrenumd \
+ rtadvd \
+ rtprio \
+ rtsold \
+ rwhod \
+ sa \
+ setextattr \
+ setkey \
+ sliplogin \
+ slstat \
+ spray \
+ sysinstall \
+ syslogd \
+ tcpdchk \
+ tcpdmatch \
+ tcpdump \
+ timed \
+ traceroute \
+ traceroute6 \
+ trpt \
+ tzsetup \
+ usbd \
+ usbdevs \
+ vidcontrol \
+ vipw \
+ vnconfig \
+ watch \
+ wicontrol \
+ xten \
+ yp_mkdb \
+ ypbind \
+ yppoll \
+ yppush \
+ ypserv \
+ ypset \
+ zic
+
+.if !defined(NO_IPFILTER)
+SUBDIR+=ipftest \
+ ipresend \
+ ipsend \
+ iptest
+.endif
+
+.if !defined(NOLIBC_R) && ${MACHINE_ARCH} != "ia64" && ${MACHINE_ARCH} != "sparc64"
+SUBDIR+=pppctl
+.endif
+
+.if !defined(NO_BIND)
+SUBDIR+=named \
+ named.reload \
+ named.restart \
+ ndc \
+ nslookup \
+ nsupdate
+.endif
+
+.if !defined(NO_LPR)
+SUBDIR+=lpr
+.endif
+
+.if !defined(NO_SENDMAIL)
+SUBDIR+=editmap \
+ mailstats \
+ makemap \
+ praliases \
+ sendmail
+.endif
+
+.if ${MACHINE_ARCH} == "alpha"
+SUBDIR+=elf2exe \
+ pnpinfo
+.endif
+
+.if ${MACHINE_ARCH} == "i386"
+SUBDIR+=acpi \
+ apm \
+ apmd \
+ btxld \
+ kgmon \
+ kgzip \
+ lptcontrol \
+ mptable \
+ pcvt \
+ pnpinfo \
+ sgsc \
+ sicontrol \
+ spkrtest \
+ stallion \
+ wlconfig
+.if !defined(NO_I4B)
+SUBDIR+=i4b
+.endif
+.endif
+
+.if ${MACHINE} == "i386"
+SUBDIR+=boot0cfg
+.endif
+
+.if ${MACHINE} == "pc98"
+SUBDIR+=boot98cfg
+.endif
+
+.if exists(${.CURDIR}/../crypto) && !defined(NOCRYPT) && !defined(NO_OPENSSL)
+SUBDIR+=keyserv
+.endif
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/Makefile.inc b/usr.sbin/Makefile.inc
new file mode 100644
index 0000000..4347591
--- /dev/null
+++ b/usr.sbin/Makefile.inc
@@ -0,0 +1,4 @@
+# @(#)Makefile.inc 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+BINDIR?= /usr/sbin
diff --git a/usr.sbin/ac/Makefile b/usr.sbin/ac/Makefile
new file mode 100644
index 0000000..1d34fe2
--- /dev/null
+++ b/usr.sbin/ac/Makefile
@@ -0,0 +1,20 @@
+# $FreeBSD$
+
+PROG= ac
+MAN= ac.8
+
+WARNS?= 2
+
+# 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..adbc46b
--- /dev/null
+++ b/usr.sbin/ac/ac.8
@@ -0,0 +1,151 @@
+.\"
+.\" Copyright (c) 1994 Simon J. Gerraty
+.\" Copyright (c) 1994 Christopher G. Demetriou
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Christopher G. Demetriou.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd March 15, 1994
+.Dt AC 8
+.Os
+.Sh NAME
+.Nm ac
+.Nd connect time accounting
+.Sh SYNOPSIS
+.Nm
+.Op Fl dp
+.\".Op Fl c Ar console
+.Op Fl t Ar tty
+.Op Fl w Ar wtmp
+.Op Ar users ...
+.Sh DESCRIPTION
+If the file
+.Pa /var/log/wtmp
+exists, a record of individual login and logout
+times are written to it by
+.Xr login 1
+and
+.Xr init 8 ,
+respectively.
+.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 .
+.Sh DIAGNOSTICS
+.Ex -std
+.Sh FILES
+.Bl -tag -width /var/log/wtmp -compact
+.It Pa /var/log/wtmp
+connect time accounting file
+.El
+.Sh SEE ALSO
+.Xr login 1 ,
+.Xr utmp 5 ,
+.Xr init 8 ,
+.Xr sa 8
+.\" .Sh NOTES
+.\" If COMPAT_SUNOS is defined
+.\" .Nm ac
+.\" ignores the fact that entries with ut_host of ":0.0" are not real
+.\" login sessions. Normally such entries are ignored except in the case
+.\" of a user being logged in when the
+.\" .Pa wtmp
+.\" file was rotated, in which case a login with ut_host of ":0.0" may
+.\" appear without any preceding console logins.
+.\" If no one is logged in on the console, the user is deemed to have
+.\" logged in on at the earliest time stamp found in
+.\" .Pa wtmp .
+.\" Use of
+.\" .Pa console
+.\" allows
+.\" .Nm ac
+.\" to identify and correctly process a logout for the user. The default
+.\" value for
+.\" .Pa console
+.\" is usually correct at compile time.
diff --git a/usr.sbin/ac/ac.c b/usr.sbin/ac/ac.c
new file mode 100644
index 0000000..322865a
--- /dev/null
+++ b/usr.sbin/ac/ac.c
@@ -0,0 +1,567 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <err.h>
+#include <errno.h>
+#include <langinfo.h>
+#include <locale.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <timeconv.h>
+#include <unistd.h>
+#include <utmp.h>
+
+/*
+ * this is for our list of currently logged in sessions
+ */
+struct utmp_list {
+ struct utmp_list *next;
+ struct utmp usr;
+};
+
+/*
+ * this is for our list of users that are accumulating time.
+ */
+struct user_list {
+ struct user_list *next;
+ char name[UT_NAMESIZE+1];
+ time_t secs;
+};
+
+/*
+ * this is for chosing whether to ignore a login
+ */
+struct tty_list {
+ struct tty_list *next;
+ char name[UT_LINESIZE+3];
+ 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((const 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((const 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)
+ const char *name;
+{
+ FILE *fp;
+
+ if ((fp = fopen(name, "r")) == NULL)
+ err(1, "%s", name);
+ /* in case we want to discriminate */
+ if (strcmp(_PATH_WTMP, name))
+ Flags |= AC_W;
+ return fp;
+}
+
+struct tty_list *
+add_tty(name)
+ char *name;
+{
+ struct tty_list *tp;
+ register char *rcp;
+
+ Flags |= AC_T;
+
+ if ((tp = NEW(struct tty_list)) == NULL)
+ errx(1, "malloc failed");
+ tp->len = 0; /* full match */
+ tp->ret = 1; /* do if match */
+ if (*name == '!') { /* don't do if match */
+ tp->ret = 0;
+ name++;
+ }
+ strlcpy(tp->name, name, sizeof (tp->name));
+ if ((rcp = strchr(tp->name, '*')) != NULL) { /* wild card */
+ *rcp = '\0';
+ tp->len = strlen(tp->name); /* match len bytes only */
+ }
+ tp->next = Ttys;
+ Ttys = tp;
+ return Ttys;
+}
+
+/*
+ * should we process the named tty?
+ */
+int
+do_tty(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)
+ errx(1, "malloc failed");
+ up->next = head;
+ strlcpy(up->name, name, sizeof (up->name));
+ 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)
+ const 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;
+ static int d_first = -1;
+
+ if (d_first < 0)
+ d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
+ (void)strftime(date, sizeof (date),
+ d_first ? "%e %b total" : "%b %e total",
+ localtime(&yesterday));
+
+ /* restore the missing second */
+ yesterday++;
+
+ for (lp = logins; lp != NULL; lp = lp->next) {
+ secs = yesterday - lp->usr.ut_time;
+ Users = update_user(Users, lp->usr.ut_name, secs);
+ lp->usr.ut_time = yesterday; /* as if they just logged in */
+ }
+ secs = 0;
+ for (up = users; up != NULL; up = up->next) {
+ secs += up->secs;
+ up->secs = 0; /* for next day */
+ }
+ if (secs)
+ (void)printf("%s %11.2f\n", date, ((double)secs / 3600));
+}
+
+/*
+ * log a user out and update their times.
+ * if ut_line is "~", we log all users out as the system has
+ * been shut down.
+ */
+struct utmp_list *
+log_out(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
+ */
+ strlcpy(up->ut_line, Console, sizeof (up->ut_line));
+ }
+#endif
+ /*
+ * If we are doing specified ttys only, we ignore
+ * anything else.
+ */
+ if (Flags & AC_T)
+ if (!do_tty(up->ut_line))
+ return head;
+
+ /*
+ * go ahead and log them in
+ */
+ if ((lp = NEW(struct utmp_list)) == NULL)
+ errx(1, "malloc failed");
+ lp->next = head;
+ head = lp;
+ memmove((char *)&lp->usr, (char *)up, sizeof (struct utmp));
+#ifdef DEBUG
+ if (Debug) {
+ printf("%-.*s %-.*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) {
+ time_t t = _int_to_time(usr.ut_time);
+ ltm = localtime(&t);
+ 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) {
+ time_t t = _int_to_time(usr.ut_time);
+ ltm = localtime(&t);
+ 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..eec1948
--- /dev/null
+++ b/usr.sbin/accton/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+PROG= accton
+MAN= accton.8
+
+WARNS?= 2
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/accton/accton.8 b/usr.sbin/accton/accton.8
new file mode 100644
index 0000000..8be1951
--- /dev/null
+++ b/usr.sbin/accton/accton.8
@@ -0,0 +1,36 @@
+.\" $FreeBSD$
+.\"
+.Dd May 21, 1993
+.Dt ACCTON 8
+.Os
+.Sh NAME
+.Nm accton
+.Nd enable/disable system accounting
+.Sh SYNOPSIS
+.Nm
+.Op Ar acctfile
+.Sh DESCRIPTION
+The
+.Nm
+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 examine the accounting records.
+If no arguments are given, system accounting is disabled.
+.Sh FILES
+.Bl -tag -width /var/account/acct
+.It Pa /var/account/acct
+default accounting file
+.El
+.Sh SEE ALSO
+.Xr lastcomm 1 ,
+.Xr acct 2 ,
+.Xr sa 8
diff --git a/usr.sbin/accton/accton.c b/usr.sbin/accton/accton.c
new file mode 100644
index 0000000..4e9a932
--- /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[] =
+ "$FreeBSD$";
+#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/acpi/Makefile b/usr.sbin/acpi/Makefile
new file mode 100644
index 0000000..953eca7
--- /dev/null
+++ b/usr.sbin/acpi/Makefile
@@ -0,0 +1,7 @@
+# Makefile for acpi tools
+# $Id: Makefile,v 1.1 2000/07/14 18:16:22 iwasaki Exp $
+# $FreeBSD$
+
+SUBDIR= acpiconf acpidump amldb
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/acpi/Makefile.inc b/usr.sbin/acpi/Makefile.inc
new file mode 100644
index 0000000..c5f729f
--- /dev/null
+++ b/usr.sbin/acpi/Makefile.inc
@@ -0,0 +1,6 @@
+# $Id: Makefile.inc,v 1.1 2000/07/14 18:16:22 iwasaki Exp $
+# $FreeBSD$
+
+CFLAGS+= -I${.CURDIR}/../../../sys
+
+.include "../Makefile.inc"
diff --git a/usr.sbin/acpi/acpiconf/Makefile b/usr.sbin/acpi/acpiconf/Makefile
new file mode 100644
index 0000000..5f862a2
--- /dev/null
+++ b/usr.sbin/acpi/acpiconf/Makefile
@@ -0,0 +1,7 @@
+# $Id: Makefile,v 1.2 2000/07/14 18:16:25 iwasaki Exp $
+# $FreeBSD$
+
+PROG= acpiconf
+MAN= acpiconf.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/acpi/acpiconf/acpiconf.8 b/usr.sbin/acpi/acpiconf/acpiconf.8
new file mode 100644
index 0000000..5c8ec3b
--- /dev/null
+++ b/usr.sbin/acpi/acpiconf/acpiconf.8
@@ -0,0 +1,79 @@
+.\"-
+.\" Copyright (c) 2000 Dag-Erling Coïdan Smørgrav
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer
+.\" in this position and unchanged.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd January 12, 2001
+.Dt ACPICONF 8
+.Os
+.Sh NAME
+.Nm acpiconf
+.Nd control ACPI power management
+.Sh SYNOPSIS
+.Nm
+.Op Fl deh
+.Op Fl s Ar type
+.Sh DESCRIPTION
+The
+.Nm
+command allows the user control of the ACPI power management
+functions.
+The following command-line options are recognized:
+.Bl -tag -width indent
+.It Fl d
+Disables ACPI power management.
+.It Fl e
+Enables ACPI power management.
+.It Fl h
+Displays a summary of available options.
+.It Fl s Ar type
+Enters the specified sleep mode.
+Recognized types are
+.Cm 1 ,
+.Cm 2 ,
+.Cm 3 ,
+.Cm 4 ,
+and
+.Cm 5 .
+.El
+.Sh SEE ALSO
+.Xr acpi 4 ,
+.Xr acpidump 8 ,
+.Xr apm 8
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Fx 5.0 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+command was written by
+.An Mitsuru Iwasaki Aq iwasaki@FreeBSD.org .
+This manual page was written by
+.An Dag-Erling Co\(:idan Sm\(/orgrav Aq des@FreeBSD.org .
diff --git a/usr.sbin/acpi/acpiconf/acpiconf.c b/usr.sbin/acpi/acpiconf/acpiconf.c
new file mode 100644
index 0000000..a5cec5f
--- /dev/null
+++ b/usr.sbin/acpi/acpiconf/acpiconf.c
@@ -0,0 +1,129 @@
+/*-
+ * Copyright (c) 1999 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: acpiconf.c,v 1.5 2000/08/08 14:12:19 iwasaki Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include <dev/acpica/acpiio.h>
+
+#include <contrib/dev/acpica/acgcc.h>
+#include <contrib/dev/acpica/actypes.h>
+
+#define ACPIDEV "/dev/acpi"
+
+static int
+acpi_enable_disable(int enable)
+{
+ int fd;
+
+ fd = open(ACPIDEV, O_RDWR);
+ if (fd == -1) {
+ err(EX_OSFILE, ACPIDEV);
+ }
+ if (ioctl(fd, enable, NULL) == -1) {
+ if (enable == ACPIIO_ENABLE)
+ err(EX_IOERR, "enable failed");
+ else
+ err(EX_IOERR, "disable failed");
+ }
+ close(fd);
+
+ return (0);
+}
+
+static int
+acpi_sleep(int sleep_type)
+{
+ int fd;
+
+ fd = open(ACPIDEV, O_RDWR);
+ if (fd == -1) {
+ err(EX_OSFILE, ACPIDEV);
+ }
+ if (ioctl(fd, ACPIIO_SETSLPSTATE, &sleep_type) == -1) {
+ err(EX_IOERR, "sleep type (%d) failed", sleep_type);
+ }
+ close(fd);
+
+ return (0);
+}
+
+static void
+usage(const char* prog)
+{
+ printf("usage: %s [-deh] [-s [1|2|3|4|4b|5]]\n", prog);
+ exit(0);
+}
+
+int
+main(int argc, char *argv[])
+{
+ char c, *prog;
+ int sleep_type;
+
+ prog = argv[0];
+ sleep_type = -1;
+ while ((c = getopt(argc, argv, "dehs:")) != -1) {
+ switch (c) {
+ case 'd':
+ acpi_enable_disable(ACPIIO_DISABLE);
+ break;
+
+ case 'e':
+ acpi_enable_disable(ACPIIO_ENABLE);
+ break;
+
+ case 'h':
+ usage(prog);
+ break;
+
+ case 's':
+ sleep_type = optarg[0] - '0';
+ if (sleep_type < 0 || sleep_type > 5)
+ errx(EX_USAGE, "invalid sleep type (%d)",
+ sleep_type);
+ break;
+ default:
+ argc -= optind;
+ argv += optind;
+ }
+ }
+
+ if (sleep_type != -1) {
+ sleep(1); /* wait 1 sec. for key-release event */
+ acpi_sleep(sleep_type);
+ }
+ return (0);
+}
diff --git a/usr.sbin/acpi/acpidump/Makefile b/usr.sbin/acpi/acpidump/Makefile
new file mode 100644
index 0000000..9000dd9
--- /dev/null
+++ b/usr.sbin/acpi/acpidump/Makefile
@@ -0,0 +1,13 @@
+# $Id: Makefile,v 1.2 2000/07/14 18:16:29 iwasaki Exp $
+# $FreeBSD$
+
+PROG= acpidump
+MAN= acpidump.8
+SRCS= acpi.c acpi_user.c asl_dump.c aml_dump.c acpidump.c
+SRCS+= aml_parse.c aml_name.c aml_amlmem.c aml_memman.c aml_store.c \
+ aml_obj.c aml_evalobj.c aml_common.c
+
+CFLAGS+= -I${.CURDIR}/../amldb
+
+.include <bsd.prog.mk>
+.PATH: ${.CURDIR}/../amldb/aml
diff --git a/usr.sbin/acpi/acpidump/acpi.c b/usr.sbin/acpi/acpidump/acpi.c
new file mode 100644
index 0000000..da5c386
--- /dev/null
+++ b/usr.sbin/acpi/acpidump/acpi.c
@@ -0,0 +1,436 @@
+/*-
+ * Copyright (c) 1998 Doug Rabson
+ * Copyright (c) 2000 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: acpi.c,v 1.4 2000/08/09 14:47:52 iwasaki Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <assert.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "acpidump.h"
+
+#include "aml/aml_env.h"
+#include "aml/aml_common.h"
+
+#define BEGIN_COMMENT "/*\n"
+#define END_COMMENT " */\n"
+
+struct ACPIsdt dsdt_header = {
+ "DSDT", 0, 1, 0, "OEMID", "OEMTBLID", 0x12345678, "CRTR", 0x12345678
+};
+
+static void
+acpi_trim_string(char *s, size_t length)
+{
+
+ /* Trim trailing spaces and NULLs */
+ while (length > 0 && (s[length - 1] == ' ' || s[length - 1] == '\0'))
+ s[length-- - 1] = '\0';
+}
+
+static void
+acpi_print_dsdt_definition(void)
+{
+ char oemid[6 + 1];
+ char oemtblid[8 + 1];
+
+ acpi_trim_string(dsdt_header.oemid, 6);
+ acpi_trim_string(dsdt_header.oemtblid, 8);
+ strncpy(oemid, dsdt_header.oemid, 6);
+ oemid[6] = '\0';
+ strncpy(oemtblid, dsdt_header.oemtblid, 8);
+ oemtblid[8] = '\0';
+
+ printf("DefinitionBlock (
+ \"acpi_dsdt.aml\", //Output filename
+ \"DSDT\", //Signature
+ 0x%x, //DSDT Revision
+ \"%s\", //OEMID
+ \"%s\", //TABLE ID
+ 0x%x //OEM Revision\n)\n",
+ dsdt_header.rev, oemid, oemtblid, dsdt_header.oemrev);
+}
+
+static void
+acpi_print_string(char *s, size_t length)
+{
+ int c;
+
+ /* Trim trailing spaces and NULLs */
+ while (length > 0 && (s[length - 1] == ' ' || s[length - 1] == '\0'))
+ length--;
+
+ while (length--) {
+ c = *s++;
+ putchar(c);
+ }
+}
+
+static void
+acpi_handle_dsdt(struct ACPIsdt *dsdp)
+{
+ u_int8_t *dp;
+ u_int8_t *end;
+
+ acpi_print_dsdt(dsdp);
+ dp = (u_int8_t *)dsdp->body;
+ end = (u_int8_t *)dsdp + dsdp->len;
+
+ acpi_dump_dsdt(dp, end);
+}
+
+static void
+acpi_handle_facp(struct FACPbody *facp)
+{
+ struct ACPIsdt *dsdp;
+
+ acpi_print_facp(facp);
+ dsdp = (struct ACPIsdt *) acpi_map_sdt(facp->dsdt_ptr);
+ if (acpi_checksum(dsdp, dsdp->len))
+ errx(1, "DSDT is corrupt\n");
+ acpi_handle_dsdt(dsdp);
+ aml_dump(dsdp);
+}
+
+static void
+init_namespace()
+{
+ struct aml_environ env;
+ struct aml_name *newname;
+
+ aml_new_name_group(AML_NAME_GROUP_OS_DEFINED);
+ env.curname = aml_get_rootname();
+ newname = aml_create_name(&env, "\\_OS_");
+ newname->property = aml_alloc_object(aml_t_string, NULL);
+ newname->property->str.needfree = 0;
+ newname->property->str.string = "Microsoft Windows NT";
+}
+
+/*
+ * Public interfaces
+ */
+
+void
+acpi_dump_dsdt(u_int8_t *dp, u_int8_t *end)
+{
+ extern struct aml_environ asl_env;
+
+ acpi_print_dsdt_definition();
+
+ /* 1st stage: parse only w/o printing */
+ init_namespace();
+ aml_new_name_group((int)dp);
+ bzero(&asl_env, sizeof(asl_env));
+
+ asl_env.dp = dp;
+ asl_env.end = end;
+ asl_env.curname = aml_get_rootname();
+
+ aml_local_stack_push(aml_local_stack_create());
+ aml_parse_objectlist(&asl_env, 0);
+ aml_local_stack_delete(aml_local_stack_pop());
+
+ assert(asl_env.dp == asl_env.end);
+ asl_env.dp = dp;
+
+ /* 2nd stage: dump whole object list */
+ printf("\n{\n");
+ asl_dump_objectlist(&dp, end, 0);
+ printf("\n}\n");
+ assert(dp == end);
+}
+void
+acpi_print_sdt(struct ACPIsdt *sdp)
+{
+
+ printf(BEGIN_COMMENT);
+ acpi_print_string(sdp->signature, 4);
+ printf(": Length=%d, Revision=%d, Checksum=%d,\n",
+ sdp->len, sdp->rev, sdp->check);
+ printf("\tOEMID=");
+ acpi_print_string(sdp->oemid, 6);
+ printf(", OEM Table ID=");
+ acpi_print_string(sdp->oemtblid, 8);
+ printf(", OEM Revision=0x%x,\n", sdp->oemrev);
+ printf("\tCreator ID=");
+ acpi_print_string(sdp->creator, 4);
+ printf(", Creator Revision=0x%x\n", sdp->crerev);
+ printf(END_COMMENT);
+ if (!memcmp(sdp->signature, "DSDT", 4)) {
+ memcpy(&dsdt_header, sdp, sizeof(dsdt_header));
+ }
+}
+
+void
+acpi_print_rsdt(struct ACPIsdt *rsdp)
+{
+ int i, entries;
+
+ acpi_print_sdt(rsdp);
+ entries = (rsdp->len - SIZEOF_SDT_HDR) / sizeof(u_int32_t);
+ printf(BEGIN_COMMENT);
+ printf("\tEntries={ ");
+ for (i = 0; i < entries; i++) {
+ if (i > 0)
+ printf(", ");
+ printf("0x%08x", rsdp->body[i]);
+ }
+ printf(" }\n");
+ printf(END_COMMENT);
+}
+
+void
+acpi_print_facp(struct FACPbody *facp)
+{
+ char sep;
+
+ printf(BEGIN_COMMENT);
+ printf("\tDSDT=0x%x\n", facp->dsdt_ptr);
+ printf("\tINT_MODEL=%s\n", facp->int_model ? "APIC" : "PIC");
+ printf("\tSCI_INT=%d\n", facp->sci_int);
+ printf("\tSMI_CMD=0x%x, ", facp->smi_cmd);
+ printf("ACPI_ENABLE=0x%x, ", facp->acpi_enable);
+ printf("ACPI_DISABLE=0x%x, ", facp->acpi_disable);
+ printf("S4BIOS_REQ=0x%x\n", facp->s4biosreq);
+ if (facp->pm1a_evt_blk)
+ printf("\tPM1a_EVT_BLK=0x%x-0x%x\n",
+ facp->pm1a_evt_blk,
+ facp->pm1a_evt_blk + facp->pm1_evt_len - 1);
+ if (facp->pm1b_evt_blk)
+ printf("\tPM1b_EVT_BLK=0x%x-0x%x\n",
+ facp->pm1b_evt_blk,
+ facp->pm1b_evt_blk + facp->pm1_evt_len - 1);
+ if (facp->pm1a_cnt_blk)
+ printf("\tPM1a_CNT_BLK=0x%x-0x%x\n",
+ facp->pm1a_cnt_blk,
+ facp->pm1a_cnt_blk + facp->pm1_cnt_len - 1);
+ if (facp->pm1b_cnt_blk)
+ printf("\tPM1b_CNT_BLK=0x%x-0x%x\n",
+ facp->pm1b_cnt_blk,
+ facp->pm1b_cnt_blk + facp->pm1_cnt_len - 1);
+ if (facp->pm2_cnt_blk)
+ printf("\tPM2_CNT_BLK=0x%x-0x%x\n",
+ facp->pm2_cnt_blk,
+ facp->pm2_cnt_blk + facp->pm2_cnt_len - 1);
+ if (facp->pm_tmr_blk)
+ printf("\tPM2_TMR_BLK=0x%x-0x%x\n",
+ facp->pm_tmr_blk,
+ facp->pm_tmr_blk + facp->pm_tmr_len - 1);
+ if (facp->gpe0_blk)
+ printf("\tPM2_GPE0_BLK=0x%x-0x%x\n",
+ facp->gpe0_blk,
+ facp->gpe0_blk + facp->gpe0_len - 1);
+ if (facp->gpe1_blk)
+ printf("\tPM2_GPE1_BLK=0x%x-0x%x, GPE1_BASE=%d\n",
+ facp->gpe1_blk,
+ facp->gpe1_blk + facp->gpe1_len - 1,
+ facp->gpe1_base);
+ printf("\tP_LVL2_LAT=%dms, P_LVL3_LAT=%dms\n",
+ facp->p_lvl2_lat, facp->p_lvl3_lat);
+ printf("\tFLUSH_SIZE=%d, FLUSH_STRIDE=%d\n",
+ facp->flush_size, facp->flush_stride);
+ printf("\tDUTY_OFFSET=%d, DUTY_WIDTH=%d\n",
+ facp->duty_off, facp->duty_width);
+ printf("\tDAY_ALRM=%d, MON_ALRM=%d, CENTURY=%d\n",
+ facp->day_alrm, facp->mon_alrm, facp->century);
+ printf("\tFlags=");
+ sep = '{';
+
+#define PRINTFLAG(xx) do { \
+ if (facp->flags & ACPI_FACP_FLAG_## xx) { \
+ printf("%c%s", sep, #xx); sep = ','; \
+ } \
+} while (0)
+
+ PRINTFLAG(WBINVD);
+ PRINTFLAG(WBINVD_FLUSH);
+ PRINTFLAG(PROC_C1);
+ PRINTFLAG(P_LVL2_UP);
+ PRINTFLAG(PWR_BUTTON);
+ PRINTFLAG(SLP_BUTTON);
+ PRINTFLAG(FIX_RTC);
+ PRINTFLAG(RTC_S4);
+ PRINTFLAG(TMR_VAL_EXT);
+ PRINTFLAG(DCK_CAP);
+
+#undef PRINTFLAG
+
+ printf("}\n");
+ printf(END_COMMENT);
+}
+
+void
+acpi_print_dsdt(struct ACPIsdt *dsdp)
+{
+
+ acpi_print_sdt(dsdp);
+}
+
+int
+acpi_checksum(void *p, size_t length)
+{
+ u_int8_t *bp;
+ u_int8_t sum;
+
+ bp = p;
+ sum = 0;
+ while (length--)
+ sum += *bp++;
+
+ return (sum);
+}
+
+struct ACPIsdt *
+acpi_map_sdt(vm_offset_t pa)
+{
+ struct ACPIsdt *sp;
+
+ sp = acpi_map_physical(pa, sizeof(struct ACPIsdt));
+ sp = acpi_map_physical(pa, sp->len);
+ return (sp);
+}
+
+void
+acpi_print_rsd_ptr(struct ACPIrsdp *rp)
+{
+
+ printf(BEGIN_COMMENT);
+ printf("RSD PTR: Checksum=%d, OEMID=", rp->sum);
+ acpi_print_string(rp->oem, 6);
+ printf(", RsdtAddress=0x%08x\n", rp->addr);
+ printf(END_COMMENT);
+}
+
+void
+acpi_handle_rsdt(struct ACPIsdt *rsdp)
+{
+ int i;
+ int entries;
+ struct ACPIsdt *sdp;
+
+ entries = (rsdp->len - SIZEOF_SDT_HDR) / sizeof(u_int32_t);
+ acpi_print_rsdt(rsdp);
+ for (i = 0; i < entries; i++) {
+ sdp = (struct ACPIsdt *) acpi_map_sdt(rsdp->body[i]);
+ if (acpi_checksum(sdp, sdp->len))
+ errx(1, "RSDT entry %d is corrupt\n", i);
+ if (!memcmp(sdp->signature, "FACP", 4)) {
+ acpi_handle_facp((struct FACPbody *) sdp->body);
+ } else {
+ acpi_print_sdt(sdp);
+ }
+ }
+}
+
+/*
+ * Dummy functions
+ */
+
+void
+aml_dbgr(struct aml_environ *env1, struct aml_environ *env2)
+{
+ /* do nothing */
+}
+
+int
+aml_region_read_simple(struct aml_region_handle *h, vm_offset_t offset,
+ u_int32_t *valuep)
+{
+ return (0);
+}
+
+int
+aml_region_write_simple(struct aml_region_handle *h, vm_offset_t offset,
+ u_int32_t value)
+{
+ return (0);
+}
+
+u_int32_t
+aml_region_prompt_read(struct aml_region_handle *h, u_int32_t value)
+{
+ return (0);
+}
+
+u_int32_t
+aml_region_prompt_write(struct aml_region_handle *h, u_int32_t value)
+{
+ return (0);
+}
+
+int
+aml_region_prompt_update_value(u_int32_t orgval, u_int32_t value,
+ struct aml_region_handle *h)
+{
+ return (0);
+}
+
+u_int32_t
+aml_region_read(struct aml_environ *env, int regtype, u_int32_t flags,
+ u_int32_t addr, u_int32_t bitoffset, u_int32_t bitlen)
+{
+ return (0);
+}
+
+int
+aml_region_write(struct aml_environ *env, int regtype, u_int32_t flags,
+ u_int32_t value, u_int32_t addr, u_int32_t bitoffset, u_int32_t bitlen)
+{
+ return (0);
+}
+
+int
+aml_region_write_from_buffer(struct aml_environ *env, int regtype,
+ u_int32_t flags, u_int8_t *buffer, u_int32_t addr, u_int32_t bitoffset,
+ u_int32_t bitlen)
+{
+ return (0);
+}
+
+int
+aml_region_bcopy(struct aml_environ *env, int regtype, u_int32_t flags,
+ u_int32_t addr, u_int32_t bitoffset, u_int32_t bitlen,
+ u_int32_t dflags, u_int32_t daddr,
+ u_int32_t dbitoffset, u_int32_t dbitlen)
+{
+ return (0);
+}
+
+int
+aml_region_read_into_buffer(struct aml_environ *env, int regtype,
+ u_int32_t flags, u_int32_t addr, u_int32_t bitoffset,
+ u_int32_t bitlen, u_int8_t *buffer)
+{
+ return (0);
+}
+
diff --git a/usr.sbin/acpi/acpidump/acpi_user.c b/usr.sbin/acpi/acpidump/acpi_user.c
new file mode 100644
index 0000000..c6a8d99
--- /dev/null
+++ b/usr.sbin/acpi/acpidump/acpi_user.c
@@ -0,0 +1,161 @@
+/*-
+ * Copyright (c) 1999 Doug Rabson
+ * Copyright (c) 2000 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: acpi_user.c,v 1.5 2000/08/09 14:47:52 iwasaki Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "acpidump.h"
+
+static int acpi_mem_fd = -1;
+
+struct acpi_user_mapping {
+ LIST_ENTRY(acpi_user_mapping) link;
+ vm_offset_t pa;
+ caddr_t va;
+ size_t size;
+};
+
+LIST_HEAD(acpi_user_mapping_list, acpi_user_mapping) maplist;
+
+static void
+acpi_user_init()
+{
+
+ if (acpi_mem_fd == -1) {
+ acpi_mem_fd = open("/dev/mem", O_RDONLY);
+ if (acpi_mem_fd == -1)
+ err(1, "opening /dev/mem");
+ LIST_INIT(&maplist);
+ }
+}
+
+static struct acpi_user_mapping *
+acpi_user_find_mapping(vm_offset_t pa, size_t size)
+{
+ struct acpi_user_mapping *map;
+
+ /* First search for an existing mapping */
+ for (map = LIST_FIRST(&maplist); map; map = LIST_NEXT(map, link)) {
+ if (map->pa <= pa && map->size >= pa + size - map->pa)
+ return (map);
+ }
+
+ /* Then create a new one */
+ size = round_page(pa + size) - trunc_page(pa);
+ pa = trunc_page(pa);
+ map = malloc(sizeof(struct acpi_user_mapping));
+ if (!map)
+ errx(1, "out of memory");
+ map->pa = pa;
+ map->va = mmap(0, size, PROT_READ, MAP_SHARED, acpi_mem_fd, pa);
+ map->size = size;
+ if ((intptr_t) map->va == -1)
+ err(1, "can't map address");
+ LIST_INSERT_HEAD(&maplist, map, link);
+
+ return (map);
+}
+
+/*
+ * Public interfaces
+ */
+
+struct ACPIrsdp *
+acpi_find_rsd_ptr()
+{
+ int i;
+ u_int8_t buf[sizeof(struct ACPIrsdp)];
+
+ acpi_user_init();
+ for (i = 0; i < 1024 * 1024; i += 16) {
+ read(acpi_mem_fd, buf, 16);
+ if (!memcmp(buf, "RSD PTR ", 8)) {
+ /* Read the rest of the structure */
+ read(acpi_mem_fd, buf + 16, sizeof(struct ACPIrsdp) - 16);
+
+ /* Verify checksum before accepting it. */
+ if (acpi_checksum(buf, sizeof(struct ACPIrsdp)))
+ continue;
+ return (acpi_map_physical(i, sizeof(struct ACPIrsdp)));
+ }
+ }
+
+ return (0);
+}
+
+void *
+acpi_map_physical(vm_offset_t pa, size_t size)
+{
+ struct acpi_user_mapping *map;
+
+ map = acpi_user_find_mapping(pa, size);
+ return (map->va + (pa - map->pa));
+}
+
+void
+acpi_load_dsdt(char *dumpfile, u_int8_t **dpp, u_int8_t **endp)
+{
+ u_int8_t *dp;
+ u_int8_t *end;
+ struct stat sb;
+
+ if ((acpi_mem_fd = open(dumpfile, O_RDONLY)) == -1) {
+ errx(1, "opening %s\n", dumpfile);
+ }
+
+ LIST_INIT(&maplist);
+
+ if (fstat(acpi_mem_fd, &sb) == -1) {
+ errx(1, "fstat %s\n", dumpfile);
+ }
+
+ dp = mmap(0, sb.st_size, PROT_READ, MAP_PRIVATE, acpi_mem_fd, 0);
+ if (dp == NULL) {
+ errx(1, "mmap %s\n", dumpfile);
+ }
+
+ if (strncmp(dp, "DSDT", 4) == 0) {
+ memcpy(&dsdt_header, dp, SIZEOF_SDT_HDR);
+ dp += SIZEOF_SDT_HDR;
+ sb.st_size -= SIZEOF_SDT_HDR;
+ }
+
+ end = (u_int8_t *) dp + sb.st_size;
+ *dpp = dp;
+ *endp = end;
+}
diff --git a/usr.sbin/acpi/acpidump/acpidump.8 b/usr.sbin/acpi/acpidump/acpidump.8
new file mode 100644
index 0000000..e825ab1
--- /dev/null
+++ b/usr.sbin/acpi/acpidump/acpidump.8
@@ -0,0 +1,184 @@
+.\" ACPI (ACPI Package)
+.\"
+.\" Copyright (c) 1999 Doug Rabson <dfr@FreeBSD.org>
+.\" Copyright (c) 2000 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
+.\" Copyright (c) 2000 Yasuo YOKOYAMA <yokoyama@jp.FreeBSD.org>
+.\" Copyright (c) 2000 Hiroki Sato <hrs@FreeBSD.org>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd August 31, 2000
+.Dt ACPIDUMP 8
+.Os
+.Sh NAME
+.Nm acpidump
+.Nd dump ACPI tables
+.Sh SYNOPSIS
+.Nm
+.Op Fl r
+.Nm
+.Op Fl r
+.Op Fl o Ar dsdt_file_for_output
+.Nm
+.Op Fl r
+.Op Fl f Ar dsdt_file_for_input
+.Sh DESCRIPTION
+The
+.Nm
+command analyzes ACPI tables in physical memory and dumps them to standard output.
+In addition,
+.Nm
+can disassemble AML
+(ACPI Machine Language)
+found in these tables and dump them as ASL
+(ACPI Source Language).
+.Pp
+ACPI tables have an essential data block (the DSDT,
+Differentiated System Description Table),
+that includes information used on the kernel side such as
+detailed information about PnP hardware, procedures for controlling
+power management support and so on.
+.Nm
+can extract the DSDT data block from physical memory and store it into
+a DSDT data file, and also can generate an output in ASL
+from a given DSDT data file.
+.Pp
+When
+.Nm
+is invoked without the
+.Fl f
+option, it will read ACPI tables from physical
+memory via a special file
+.Pa /dev/mem
+and dump them.
+First it searches for the RSDP
+(Root System Description Pointer),
+which has the signature
+.Qq RSD PTR\ \& ,
+and then gets the RSDT
+(Root System Description Table),
+which includes a list of pointers to physical memory addresses
+for other tables.
+The RSDT itself and all other tables linked from RSDT are generically
+called SDTs
+(System Description Tables)
+and their header has a common format which consists of items
+such as Signature, Length, Revision, Checksum, OEMID, OEM Table ID,
+OEM Revision, Creator ID and Creator Revision.
+.Nm
+dumps contents of these SDTs.
+For further information about formats of each table,
+see chapter 5:
+.Dq ACPI Software Programming Model
+from the ACPI specifications referenced below.
+.Pp
+There is always a pointer to a physical memory address in RSDT for FACP
+(Fixed ACPI Description Table).
+The FACP defines static system information about power management support
+(ACPI Hardware Register Implementation)
+such as interrupt mode
+(INT_MODEL),
+SCI interrupt number, SMI command port
+(SMI_CMD)
+and location of ACPI registers.
+The FACP also has a pointer to a physical memory address for DSDT,
+which includes information used on the kernel side such as
+PnP, power management support and so on.
+While the other tables are described in fixed format,
+the DSDT consists of AML data which is compiled from sources
+written in free formated ASL, which is the description language for ACPI.
+When
+.Nm
+outputs DSDT, it disassembles the AML data and
+formats it as ASL.
+.Sh OPTIONS
+The following options are supported by
+.Nm :
+.Bl -tag -width indent
+.It Fl o Ar dsdt_file_for_output
+Stores DSDT data block from physical memory into a file specified in
+.Ar dsdt_file_for_output
+in addition to behavior with no option.
+.It Fl f Ar dsdt_file_for_input
+Interprets AML data in DSDT from a file specified in
+.Ar dsdt_file_for_input
+and dumps them in ASL to standard output.
+.It Fl r
+Additionally outputs commented ResourceTemplate() macros for Buffer
+objects that contain valid resource streams.
+These macros are defined in the ACPI 2.0 specification section
+16.2.4.
+.It Fl h
+Displays usage and exit.
+.El
+.Sh EXAMPLES
+This is an example to get a dump of SDTs and a DSDT data file
+simultaneously on a machine that supports ACPI BIOS.
+.Bd -literal -offset indent
+# acpidump -o foo.dsdt > foo.asl
+.Ed
+.Sh BUGS
+In the current implementation,
+.Nm
+doesn't dump any information of Firmware ACPI Control Structure
+(FACS)
+specified by a pointer in FACP.
+.Sh FILES
+.Bl -tag -width /dev/mem
+.It Pa /dev/mem
+.El
+.Sh SEE ALSO
+.Xr acpi 4 ,
+.Xr mem 4 ,
+.Xr acpiconf 8 ,
+.Xr amldb 8
+.Pp
+.Dq Advanced Configuration and Power Interface Specification
+.Bd -literal -offset indent -compact
+Intel
+Microsoft
+Toshiba
+Revision 1.0b, 2.0
+.Ed
+<URL:http://www.teleport.com/~acpi/>
+.Sh AUTHORS
+.An Doug Rabson Aq dfr@FreeBSD.org
+.An Mitsuru IWASAKI Aq iwasaki@FreeBSD.org
+.An Yasuo YOKOYAMA Aq yokoyama@jp.FreeBSD.org
+.Pp
+.An -nosplit
+Some contributions made by
+.An Chitoshi Ohsawa Aq ohsawa@catv1.ccn-net.ne.jp ,
+.An Takayasu IWANASHI Aq takayasu@wendy.a.perfect-liberty.or.jp ,
+.An Yoshihiko SARUMARU Aq mistral@imasy.or.jp ,
+.An Hiroki Sato Aq hrs@FreeBSD.org ,
+.An Michael Lucas Aq mwlucas@blackhelicopters.org
+and
+.An Michael Smith Aq msmith@FreeBSD.org .
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Fx 5.0 .
diff --git a/usr.sbin/acpi/acpidump/acpidump.c b/usr.sbin/acpi/acpidump/acpidump.c
new file mode 100644
index 0000000..a687568
--- /dev/null
+++ b/usr.sbin/acpi/acpidump/acpidump.c
@@ -0,0 +1,107 @@
+/*-
+ * Copyright (c) 2000 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: acpidump.c,v 1.3 2000/08/08 14:12:21 iwasaki Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+
+#include <assert.h>
+#include <err.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "acpidump.h"
+
+static void
+asl_dump_from_file(char *file)
+{
+ u_int8_t *dp;
+ u_int8_t *end;
+ struct ACPIsdt *dsdt;
+
+ acpi_load_dsdt(file, &dp, &end);
+ acpi_dump_dsdt(dp, end);
+}
+
+static void
+asl_dump_from_devmem()
+{
+ struct ACPIrsdp *rp;
+ struct ACPIsdt *rsdp;
+
+ rp = acpi_find_rsd_ptr();
+ if (!rp)
+ errx(1, "Can't find ACPI information\n");
+
+ acpi_print_rsd_ptr(rp);
+ rsdp = (struct ACPIsdt *) acpi_map_sdt(rp->addr);
+ if (memcmp(rsdp->signature, "RSDT", 4) ||
+ acpi_checksum(rsdp, rsdp->len))
+ errx(1, "RSDT is corrupted\n");
+
+ acpi_handle_rsdt(rsdp);
+}
+
+static void
+usage(const char *progname)
+{
+
+ printf("usage:\t%s [-r] [-o dsdt_file_for_output]\n", progname);
+ printf("\t%s [-r] [-f dsdt_file_for_input]\n", progname);
+ printf("\t%s [-h]\n", progname);
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ char c, *progname;
+
+ progname = argv[0];
+ while ((c = getopt(argc, argv, "f:o:hr")) != -1) {
+ switch (c) {
+ case 'f':
+ asl_dump_from_file(optarg);
+ return (0);
+ case 'o':
+ aml_dumpfile = optarg;
+ break;
+ case 'h':
+ usage(progname);
+ break;
+ case 'r':
+ rflag++;
+ break;
+ default:
+ argc -= optind;
+ argv += optind;
+ }
+ }
+
+ asl_dump_from_devmem();
+ return (0);
+}
diff --git a/usr.sbin/acpi/acpidump/acpidump.h b/usr.sbin/acpi/acpidump/acpidump.h
new file mode 100644
index 0000000..dd27f89
--- /dev/null
+++ b/usr.sbin/acpi/acpidump/acpidump.h
@@ -0,0 +1,181 @@
+/*-
+ * Copyright (c) 1999 Doug Rabson
+ * Copyright (c) 2000 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: acpidump.h,v 1.3 2000/08/09 14:47:52 iwasaki Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _ACPIDUMP_H_
+#define _ACPIDUMP_H_
+
+/* Generic Address structure */
+struct ACPIgas {
+ u_int8_t address_space_id;
+#define ACPI_GAS_MEMORY 0
+#define ACPI_GAS_IO 1
+#define ACPI_GAS_PCI 2
+#define ACPI_GAS_EMBEDDED 3
+#define ACPI_GAS_SMBUS 4
+#define ACPI_GAS_FIXED 0x7f
+ u_int8_t register_bit_width;
+ u_int8_t register_bit_offset;
+ u_int8_t res;
+ u_int64_t address;
+} __attribute__((packed));
+
+/* Root System Description Pointer */
+struct ACPIrsdp {
+ u_char signature[8];
+ u_char sum;
+ u_char oem[6];
+ u_char res;
+ u_int32_t addr;
+} __attribute__((packed));
+
+/* System Description Table */
+struct ACPIsdt {
+ u_char signature[4];
+ u_int32_t len;
+ u_char rev;
+ u_char check;
+ u_char oemid[6];
+ u_char oemtblid[8];
+ u_int32_t oemrev;
+ u_char creator[4];
+ u_int32_t crerev;
+#define SIZEOF_SDT_HDR 36 /* struct size except body */
+ u_int32_t body[1];/* This member should be casted */
+} __attribute__((packed));
+
+/* Fixed ACPI Description Table (body) */
+struct FACPbody {
+ u_int32_t facs_ptr;
+ u_int32_t dsdt_ptr;
+ u_int8_t int_model;
+#define ACPI_FACP_INTMODEL_PIC 0 /* Standard PC-AT PIC */
+#define ACPI_FACP_INTMODEL_APIC 1 /* Multiple APIC */
+ u_char reserved1;
+ u_int16_t sci_int;
+ u_int32_t smi_cmd;
+ u_int8_t acpi_enable;
+ u_int8_t acpi_disable;
+ u_int8_t s4biosreq;
+ u_int8_t reserved2;
+ u_int32_t pm1a_evt_blk;
+ u_int32_t pm1b_evt_blk;
+ u_int32_t pm1a_cnt_blk;
+ u_int32_t pm1b_cnt_blk;
+ u_int32_t pm2_cnt_blk;
+ u_int32_t pm_tmr_blk;
+ u_int32_t gpe0_blk;
+ u_int32_t gpe1_blk;
+ u_int8_t pm1_evt_len;
+ u_int8_t pm1_cnt_len;
+ u_int8_t pm2_cnt_len;
+ u_int8_t pm_tmr_len;
+ u_int8_t gpe0_len;
+ u_int8_t gpe1_len;
+ u_int8_t gpe1_base;
+ u_int8_t reserved3;
+ u_int16_t p_lvl2_lat;
+ u_int16_t p_lvl3_lat;
+ u_int16_t flush_size;
+ u_int16_t flush_stride;
+ u_int8_t duty_off;
+ u_int8_t duty_width;
+ u_int8_t day_alrm;
+ u_int8_t mon_alrm;
+ u_int8_t century;
+ u_int16_t iapc_boot_arch;
+ u_char reserved4[1];
+ u_int32_t flags;
+#define ACPI_FACP_FLAG_WBINVD 1 /* WBINVD is correctly supported */
+#define ACPI_FACP_FLAG_WBINVD_FLUSH 2 /* WBINVD flushes caches */
+#define ACPI_FACP_FLAG_PROC_C1 4 /* C1 power state supported */
+#define ACPI_FACP_FLAG_P_LVL2_UP 8 /* C2 power state works on SMP */
+#define ACPI_FACP_FLAG_PWR_BUTTON 16 /* Power button uses control method */
+#define ACPI_FACP_FLAG_SLP_BUTTON 32 /* Sleep button uses control method */
+#define ACPI_FACP_FLAG_FIX_RTC 64 /* RTC wakeup not supported */
+#define ACPI_FACP_FLAG_RTC_S4 128 /* RTC can wakeup from S4 state */
+#define ACPI_FACP_FLAG_TMR_VAL_EXT 256 /* TMR_VAL is 32bit */
+#define ACPI_FACP_FLAG_DCK_CAP 512 /* Can support docking */
+ struct ACPIgas reset_reg;
+ u_int8_t reset_value;
+ u_int8_t reserved5[3];
+ u_int64_t x_firmware_ctrl;
+ u_int64_t x_dsdt;
+ struct ACPIgas x_pm1a_evt_blk;
+ struct ACPIgas x_pm1b_evt_blk;
+ struct ACPIgas x_pm1a_cnt_blk;
+ struct ACPIgas x_pm1b_cnt_blk;
+ struct ACPIgas x_pm2_cnt_blk;
+ struct ACPIgas x_pm_tmr_blk;
+ struct ACPIgas x_gpe0_blk;
+ struct ACPIgas x_gpe1_blk;
+} __attribute__((packed));
+
+/* Firmware ACPI Control Structure */
+struct FACS {
+ u_char signature[4];
+ u_int32_t len;
+ u_char hard_sig[4];
+ /*
+ * NOTE This should be filled with physical address below 1MB!!
+ * sigh....
+ */
+ u_int32_t firm_wake_vec;
+ u_int32_t g_lock; /* bit field */
+ /* 5.2.6.1 Global Lock */
+#define ACPI_GLOBAL_LOCK_PENDING 1
+#define ACPI_GLOBAL_LOCK_OWNED 2
+ u_int32_t flags; /* bit field */
+#define ACPI_FACS_FLAG_S4BIOS_F 1 /* Supports S4BIOS_SEQ */
+ char reserved[40];
+} __attribute__((packed));
+
+void *acpi_map_physical(vm_offset_t, size_t);
+struct ACPIrsdp *acpi_find_rsd_ptr(void);
+int acpi_checksum(void *, size_t);
+struct ACPIsdt *acpi_map_sdt(vm_offset_t);
+void acpi_print_rsd_ptr(struct ACPIrsdp *);
+void acpi_print_sdt(struct ACPIsdt *);
+void acpi_print_rsdt(struct ACPIsdt *);
+void acpi_print_facp(struct FACPbody *);
+void acpi_print_dsdt(struct ACPIsdt *);
+
+void asl_dump_termobj(u_int8_t **, int);
+void asl_dump_objectlist(u_int8_t **, u_int8_t *, int);
+
+void aml_dump(struct ACPIsdt *);
+
+void acpi_handle_rsdt(struct ACPIsdt *);
+void acpi_load_dsdt(char *, u_int8_t **, u_int8_t **);
+void acpi_dump_dsdt(u_int8_t *, u_int8_t *);
+extern char *aml_dumpfile;
+extern struct ACPIsdt dsdt_header;
+extern int rflag;
+
+#endif /* !_ACPIDUMP_H_ */
diff --git a/usr.sbin/acpi/acpidump/aml_dump.c b/usr.sbin/acpi/acpidump/aml_dump.c
new file mode 100644
index 0000000..434001f
--- /dev/null
+++ b/usr.sbin/acpi/acpidump/aml_dump.c
@@ -0,0 +1,60 @@
+/*-
+ * Copyright (c) 2000 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: aml_dump.c,v 1.3 2000/08/08 14:12:21 iwasaki Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "acpidump.h"
+
+char *aml_dumpfile = NULL;
+
+void
+aml_dump(struct ACPIsdt *dsdp)
+{
+ int fd;
+ mode_t mode;
+
+ if (aml_dumpfile == NULL) {
+ return;
+ }
+
+ mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ fd = open(aml_dumpfile, O_WRONLY | O_CREAT | O_TRUNC, mode);
+ if (fd == -1) {
+ return;
+ }
+ write(fd, dsdp, SIZEOF_SDT_HDR);
+ write(fd, dsdp->body, dsdp->len - SIZEOF_SDT_HDR);
+ close(fd);
+}
+
diff --git a/usr.sbin/acpi/acpidump/asl_dump.c b/usr.sbin/acpi/acpidump/asl_dump.c
new file mode 100644
index 0000000..2d645a1
--- /dev/null
+++ b/usr.sbin/acpi/acpidump/asl_dump.c
@@ -0,0 +1,1644 @@
+/*-
+ * Copyright (c) 1999 Doug Rabson
+ * Copyright (c) 2000 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: asl_dump.c,v 1.6 2000/08/09 14:47:52 iwasaki Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+
+#include <assert.h>
+#include <err.h>
+#include <stdio.h>
+
+#include "acpidump.h"
+
+#include "aml/aml_env.h"
+
+struct aml_environ asl_env;
+int rflag;
+
+static u_int32_t
+asl_dump_pkglength(u_int8_t **dpp)
+{
+ u_int8_t *dp;
+ u_int32_t pkglength;
+
+ dp = *dpp;
+ pkglength = *dp++;
+ switch (pkglength >> 6) {
+ case 0:
+ break;
+ case 1:
+ pkglength = (pkglength & 0xf) + (dp[0] << 4);
+ dp += 1;
+ break;
+ case 2:
+ pkglength = (pkglength & 0xf) + (dp[0] << 4) + (dp[1] << 12);
+ dp += 2;
+ break;
+ case 3:
+ pkglength = (pkglength & 0xf)
+ + (dp[0] << 4) + (dp[1] << 12) + (dp[2] << 20);
+ dp += 3;
+ break;
+ }
+
+ *dpp = dp;
+ return (pkglength);
+}
+
+static void
+print_nameseg(u_int8_t *dp)
+{
+
+ if (dp[3] != '_')
+ printf("%c%c%c%c", dp[0], dp[1], dp[2], dp[3]);
+ else if (dp[2] != '_')
+ printf("%c%c%c_", dp[0], dp[1], dp[2]);
+ else if (dp[1] != '_')
+ printf("%c%c__", dp[0], dp[1]);
+ else if (dp[0] != '_')
+ printf("%c___", dp[0]);
+}
+
+static u_int8_t
+asl_dump_bytedata(u_int8_t **dpp)
+{
+ u_int8_t *dp;
+ u_int8_t data;
+
+ dp = *dpp;
+ data = dp[0];
+ *dpp = dp + 1;
+ return (data);
+}
+
+static u_int16_t
+asl_dump_worddata(u_int8_t **dpp)
+{
+ u_int8_t *dp;
+ u_int16_t data;
+
+ dp = *dpp;
+ data = dp[0] + (dp[1] << 8);
+ *dpp = dp + 2;
+ return (data);
+}
+
+static u_int32_t
+asl_dump_dworddata(u_int8_t **dpp)
+{
+ u_int8_t *dp;
+ u_int32_t data;
+
+ dp = *dpp;
+ data = dp[0] + (dp[1] << 8) + (dp[2] << 16) + (dp[3] << 24);
+ *dpp = dp + 4;
+ return (data);
+}
+
+static u_int8_t *
+asl_dump_namestring(u_int8_t **dpp)
+{
+ u_int8_t *dp;
+ u_int8_t *name;
+
+ dp = *dpp;
+ name = dp;
+ if (dp[0] == '\\')
+ dp++;
+ else if (dp[0] == '^')
+ while (dp[0] == '^')
+ dp++;
+ if (dp[0] == 0x00) /* NullName */
+ dp++;
+ else if (dp[0] == 0x2e) /* DualNamePrefix */
+ dp += 1 + 4 + 4;/* NameSeg, NameSeg */
+ else if (dp[0] == 0x2f) { /* MultiNamePrefix */
+ int segcount = dp[1];
+ dp += 1 + 1 + segcount * 4; /* segcount * NameSeg */
+ } else
+ dp += 4; /* NameSeg */
+
+ *dpp = dp;
+ return (name);
+}
+
+static void
+print_namestring(u_int8_t *dp)
+{
+
+ if (dp[0] == '\\') {
+ putchar(dp[0]);
+ dp++;
+ } else if (dp[0] == '^') {
+ while (dp[0] == '^') {
+ putchar(dp[0]);
+ dp++;
+ }
+ }
+ if (dp[0] == 0x00) { /* NullName */
+ /* printf("<null>"); */
+ dp++;
+ } else if (dp[0] == 0x2e) { /* DualNamePrefix */
+ print_nameseg(dp + 1);
+ putchar('.');
+ print_nameseg(dp + 5);
+ } else if (dp[0] == 0x2f) { /* MultiNamePrefix */
+ int segcount = dp[1];
+ int i;
+ for (i = 0, dp += 2; i < segcount; i++, dp += 4) {
+ if (i > 0)
+ putchar('.');
+ print_nameseg(dp);
+ }
+ } else /* NameSeg */
+ print_nameseg(dp);
+}
+
+static void
+print_indent(int indent)
+{
+ int i;
+
+ for (i = 0; i < indent; i++)
+ printf(" ");
+}
+
+#define ASL_ENTER_SCOPE(dp_orig, old_name) do { \
+ u_int8_t *dp_copy; \
+ u_int8_t *name; \
+ old_name = asl_env.curname; \
+ dp_copy = dp_orig; \
+ name = asl_dump_namestring(&dp_copy); \
+ asl_env.curname = aml_search_name(&asl_env, name); \
+} while(0)
+
+#define ASL_LEAVE_SCOPE(old_name) do { \
+ asl_env.curname = old_name; \
+} while(0)
+
+#define ASL_CREATE_LOCALNAMEOBJ(dp) do { \
+ if(scope_within_method){ \
+ aml_create_name(&asl_env, dp); \
+ } \
+}while(0);
+
+static void
+asl_dump_defscope(u_int8_t **dpp, int indent)
+{
+ u_int8_t *dp;
+ u_int8_t *start;
+ u_int8_t *end;
+ u_int32_t pkglength;
+ struct aml_name *oname;
+
+ dp = *dpp;
+ start = dp;
+ pkglength = asl_dump_pkglength(&dp);
+
+ printf("Scope(");
+ ASL_ENTER_SCOPE(dp, oname);
+ asl_dump_termobj(&dp, indent);
+ printf(") {\n");
+ end = start + pkglength;
+ asl_dump_objectlist(&dp, end, indent + 1);
+ print_indent(indent);
+ printf("}");
+
+ assert(dp == end);
+ ASL_LEAVE_SCOPE(oname);
+ *dpp = dp;
+}
+
+static void
+asl_dump_resourcebuffer(u_int8_t *dp, u_int8_t *end, int indent)
+{
+ u_int8_t *p;
+ int print, len, name, indep, i, ofs;
+
+ print = 0;
+ indep = 0;
+restart:
+ if (print) {
+ printf("\n");
+ print_indent(indent);
+ printf("/* ResourceTemplate() {\n");
+ }
+ for (p = dp; p < end; ) {
+ ofs = p - dp;
+ if (*p & 0x80) { /* large resource */
+ if ((end - p) < 3) {
+ return;
+ }
+ name = *p;
+ len = ((int)*(p + 2) << 8) + *(p + 1);
+ p += 3;
+ } else { /* small resource */
+ name = (*p >> 3) & 0x0f;
+ len = *p & 0x7;
+ p++;
+ }
+ if (name == 0xf) { /* end tag */
+ if (print == 0) {
+ print = 1;
+ goto restart;
+ } else {
+ print_indent(indent);
+ printf("} */\n");
+ print_indent(indent);
+ break;
+ }
+ }
+
+ if (print) {
+ print_indent(indent);
+ switch (name) {
+ case 0x06:
+ if (indep) {
+ printf(" }\n");
+ print_indent(indent);
+ }
+ printf(" StartDependentFn(");
+ if (len == 1)
+ printf("%d, %d",
+ *p & 0x3,
+ (*p >> 2) & 0x3);
+ printf(") {\n");
+ indep = 1;
+ continue;
+ case 0x07:
+ if (indep)
+ printf(" }\n");
+ print_indent(indent);
+ printf(" EndDependentFn() {}\n");
+ indep = 0;
+ continue;
+ }
+
+ printf("%s 0x%-04.4x ", indep ? " " : "", ofs);
+ switch (name) {
+ case 0x04: /* IRQ() { } */
+ {
+ int i, first;
+
+ printf("IRQ(");
+ if (len == 3) {
+ printf("%s, Active%s, %s",
+ *(p + 2) & 0x01 ? "Edge" : "Level",
+ *(p + 2) & 0x08 ? "Low" : "High",
+ *(p + 2) & 0x10 ? "Shared" :
+ "Exclusive");
+ }
+ printf(")");
+ first = 1;
+ for (i = 0; i < 16; i++) {
+ if (*(p + (i / 8)) & (1 << (i % 8))) {
+ if (first) {
+ printf(" {");
+ first = 0;
+ } else {
+ printf(", ");
+ }
+ printf("%d", i);
+ }
+ }
+ if (!first)
+ printf("}");
+ printf("\n");
+ break;
+ }
+
+ case 0x05: /* DMA() { } */
+ {
+ int i, first;
+
+ printf("DMA(%s, %sBusMaster, Transfer%s)",
+ (*(p + 1) & 0x60) == 0 ? "Compatibility" :
+ (*(p + 1) & 0x60) == 1 ? "TypeA" :
+ (*(p + 1) & 0x60) == 2 ? "TypeB" : "TypeF",
+ *(p + 1) & 0x04 ? "" : "Not",
+ (*(p + 1) & 0x03) == 0 ? "8" :
+ (*(p + 1) & 0x03) == 1 ? "8_16" : "16");
+ first = 1;
+ for (i = 0; i < 8; i++) {
+ if (*p & (1 << i)) {
+ if (first) {
+ printf(" {");
+ first = 0;
+ } else {
+ printf(", ");
+ }
+ printf("%d", i);
+ }
+ }
+ if (!first)
+ printf("}");
+ printf("\n");
+ break;
+ }
+ case 0x08: /* IO() */
+ printf("IO(Decode%s, 0x%x, 0x%x, 0x%x, 0x%x)\n",
+ *p & 0x01 ? "16" : "10",
+ (int)*(u_int16_t *)(p + 1),
+ (int)*(u_int16_t *)(p + 3),
+ *(p + 5),
+ *(p + 6));
+ break;
+
+ case 0x09: /* FixedIO() */
+ printf("FixedIO(0x%x, 0x%x)\n",
+ *p + ((int)*(p + 1) << 8),
+ *(p + 2));
+ break;
+
+ case 0x0e: /* VendorShort() { }*/
+ case 0x84: /* VendorLong() { } */
+ {
+ int i, first;
+
+ printf("Vendor%s()", name == 0x0e ? "Short" : "Long");
+ first = 0;
+ for (i = 0; i < len; i++) {
+ if (first) {
+ printf(" {");
+ first = 0;
+ } else {
+ printf(", ");
+ }
+ printf("0x%02x", *(p + i));
+ }
+ if (!first)
+ printf("}");
+ printf("\n");
+ break;
+ }
+ case 0x81: /* Memory24() */
+ printf("Memory24(Read%s, 0x%06x, 0x%06x, 0x%x, 0x%x)\n",
+ *p & 0x01 ? "Write" : "Only",
+ (u_int32_t)*(u_int16_t *)(p + 1) << 8,
+ (u_int32_t)*(u_int16_t *)(p + 3) << 8,
+ (int)*(u_int16_t *)(p + 5),
+ (int)*(u_int16_t *)(p + 7));
+ break;
+
+ case 0x82: /* Register() */
+ printf("Register(%s, %d, %d, 0x%016llx)\n",
+ *p == 0x00 ? "SystemMemory" :
+ *p == 0x01 ? "SystemIO" :
+ *p == 0x02 ? "PCIConfigSpace" :
+ *p == 0x03 ? "EmbeddedController" :
+ *p == 0x04 ? "SMBus" :
+ *p == 0x7f ? "FunctionalFixedHardware" : "Unknown",
+ *(p + 1),
+ *(p + 2),
+ *(u_int64_t *)(p + 3));
+ break;
+
+ case 0x85: /* Memory32() */
+ printf("Memory32(Read%s, 0x%08x, 0x%08x, 0x%x, 0x%x)\n",
+ *p & 0x01 ? "Write" : "Only",
+ *(u_int32_t *)(p + 1),
+ *(u_int32_t *)(p + 5),
+ *(u_int32_t *)(p + 9),
+ *(u_int32_t *)(p + 13));
+ break;
+
+ case 0x86: /* Memory32Fixed() */
+ printf("Memory32Fixed(Read%s, 0x%08x, 0x%x)\n",
+ *p & 0x01 ? "Write" : "Only",
+ *(u_int32_t *)(p + 1),
+ *(u_int32_t *)(p + 5));
+ break;
+
+ case 0x87: /* DWordMemory() / DWordIO() */
+ case 0x88: /* WordMemory() / WordIO() */
+ case 0x8a: /* QWordMemory() / QWordIO() */
+ {
+ u_int64_t granularity, minimum, maximum, translation, length;
+ char *size, *source;
+ int index, slen;
+
+ switch (name) {
+ case 0x87:
+ size = "D";
+ granularity = *(u_int32_t *)(p + 3);
+ minimum = *(u_int32_t *)(p + 7);
+ maximum = *(u_int32_t *)(p + 11);
+ translation = *(u_int32_t *)(p + 15);
+ length = *(u_int32_t *)(p + 19);
+ index = *(p + 23);
+ source = p + 24;
+ slen = len - 24;
+ break;
+ case 0x88:
+ size = "";
+ granularity = *(u_int16_t *)(p + 3);
+ minimum = *(u_int16_t *)(p + 5);
+ maximum = *(u_int16_t *)(p + 7);
+ translation = *(u_int16_t *)(p + 9);
+ length = *(u_int16_t *)(p + 11);
+ index = *(p + 13);
+ source = p + 14;
+ slen = len - 14;
+ break;
+ case 0x8a:
+ size = "Q";
+ granularity = *(u_int64_t *)(p + 3);
+ minimum = *(u_int64_t *)(p + 11);
+ maximum = *(u_int64_t *)(p + 19);
+ translation = *(u_int64_t *)(p + 27);
+ length = *(u_int64_t *)(p + 35);
+ index = *(p + 43);
+ source = p + 44;
+ slen = len - 44;
+ break;
+ }
+ switch(*p) {
+ case 0:
+ printf("%sWordMemory("
+ "Resource%s, "
+ "%sDecode, "
+ "Min%sFixed, "
+ "Max%sFixed, "
+ "%s, "
+ "Read%s, "
+ "0x%llx, 0x%llx, 0x%llx, 0x%llx, 0x%llx, "
+ "%d, '%.*s', "
+ "AddressRange%s, "
+ "Type%s)\n",
+ size,
+ *(p + 1) & 0x01 ? "Consumer" : "Producer",
+ *(p + 1) & 0x02 ? "Sub" : "Pos",
+ *(p + 1) & 0x04 ? "" : "Not",
+ *(p + 1) & 0x08 ? "" : "Not",
+ (*(p + 2) >> 1) == 0 ? "NonCacheable" :
+ (*(p + 2) >> 1) == 1 ? "Cacheable" :
+ (*(p + 2) >> 1) == 2 ? "WriteCombining" :
+ "Prefetchable",
+ *(p + 2) & 0x01 ? "Write" : "Only",
+ granularity, minimum, maximum, translation, length,
+ index, slen, source,
+ ((*(p + 2) >> 3) & 0x03) == 0 ? "Memory" :
+ ((*(p + 2) >> 3) & 0x03) == 1 ? "Reserved" :
+ ((*(p + 2) >> 3) & 0x03) == 2 ? "ACPI" : "NVS",
+ *(p + 2) & 0x20 ? "Translation" : "Static");
+ break;
+ case 1:
+ printf("%sWordIO("
+ "Resource%s, "
+ "Min%sFixed, "
+ "Max%sFixed, "
+ "%sDecode, "
+ "%s, "
+ "0x%llx, 0x%llx, 0x%llx, 0x%llx, 0x%llx, "
+ "%d, '%.*s', "
+ "Type%s, "
+ "%sTranslation)\n",
+ size,
+ *(p + 1) & 0x01 ? "Consumer" : "Producer",
+ *(p + 1) & 0x04 ? "" : "Not",
+ *(p + 1) & 0x08 ? "" : "Not",
+ *(p + 1) & 0x02 ? "Sub" : "Pos",
+ (*(p + 2) & 0x03) == 0 ? "EntireRange" :
+ (*(p + 2) & 0x03) == 1 ? "NonISAOnlyRanges" :
+ (*(p + 2) & 0x03) == 2 ? "ISAOnlyRanges" : "EntireRange",
+ granularity, minimum, maximum, translation, length,
+ index, slen, source,
+ *(p + 2) & 0x10 ? "Translation" : "Static",
+ *(p + 2) & 0x20 ? "Sparse" : "Dense");
+ break;
+ case 2:
+ printf("%sWordBus("
+ "Resource%s, "
+ "%sDecode, "
+ "Min%sFixed, "
+ "Max%sFixed, "
+ "0x%llx, 0x%llx, 0x%llx, 0x%llx, 0x%llx, "
+ "%d, '%.*s')\n",
+ size,
+ *(p + 1) & 0x01 ? "Consumer" : "Producer",
+ *(p + 1) & 0x02 ? "Sub" : "Pos",
+ *(p + 1) & 0x04 ? "" : "Not",
+ *(p + 1) & 0x08 ? "" : "Not",
+ granularity, minimum, maximum, translation, length,
+ index, slen, source);
+ break;
+ default:
+ printf("%sWordUnknown()\n", size);
+ }
+ break;
+ }
+ case 0x89: /* Interrupt() { } */
+ {
+ int i, first, pad, sl;
+ char *rp;
+
+ pad = *(p + 1) * 4;
+ rp = p + 1 + pad;
+ sl = len - pad - 3;
+ printf("Interrupt(Resource%s, %s, Active%s, %s, %d, %.*s)",
+ *p & 0x01 ? "Producer" : "Consumer",
+ *p & 0x02 ? "Edge" : "Level",
+ *p & 0x04 ? "Low" : "High",
+ *p & 0x08 ? "Shared" : "Exclusive",
+ (int)*(p + 1 + pad),
+ sl,
+ rp);
+ first = 1;
+ for (i = 0; i < *(p + 1); i++) {
+ if (first) {
+ printf(" {");
+ first = 0;
+ } else {
+ printf(", ");
+ }
+ printf("%u", *(u_int32_t *)(p + 2 + (i * 4)));
+ }
+ if (!first)
+ printf("}");
+ printf("\n");
+ break;
+ }
+ default:
+ printf("Unknown(0x%x, %d)\n", name, len);
+ break;
+ }
+ }
+ p += len;
+ }
+}
+
+static void
+asl_dump_defbuffer(u_int8_t **dpp, int indent)
+{
+ u_int8_t *dp;
+ u_int8_t *start;
+ u_int8_t *end;
+ u_int32_t pkglength;
+
+ dp = *dpp;
+ start = dp;
+ pkglength = asl_dump_pkglength(&dp);
+ end = start + pkglength;
+ printf("Buffer(");
+ asl_dump_termobj(&dp, indent);
+ start = dp;
+ printf(") {");
+ while (dp < end) {
+ printf("0x%x", *dp++);
+ if (dp < end)
+ printf(", ");
+ }
+ if (rflag)
+ asl_dump_resourcebuffer(start, end, indent);
+ printf(" }");
+
+ *dpp = dp;
+}
+
+static void
+asl_dump_defpackage(u_int8_t **dpp, int indent)
+{
+ u_int8_t *dp;
+ u_int8_t *start;
+ u_int8_t *end;
+ u_int8_t numelements;
+ u_int32_t pkglength;
+
+ dp = *dpp;
+ start = dp;
+ pkglength = asl_dump_pkglength(&dp);
+ numelements = asl_dump_bytedata(&dp);
+ end = start + pkglength;
+ printf("Package(0x%x) {\n", numelements);
+ while (dp < end) {
+ print_indent(indent + 1);
+ asl_dump_termobj(&dp, indent + 1);
+ printf(",\n");
+ }
+
+ print_indent(indent);
+ printf("}");
+
+ dp = end;
+
+ *dpp = dp;
+}
+
+int scope_within_method = 0;
+
+static void
+asl_dump_defmethod(u_int8_t **dpp, int indent)
+{
+ u_int8_t *dp;
+ u_int8_t *start;
+ u_int8_t *end;
+ u_int8_t flags;
+ u_int32_t pkglength;
+ struct aml_name *oname;
+
+ dp = *dpp;
+ start = dp;
+ pkglength = asl_dump_pkglength(&dp);
+
+ printf("Method(");
+ ASL_ENTER_SCOPE(dp, oname);
+ asl_dump_termobj(&dp, indent);
+ flags = *dp++;
+ if (flags) {
+ printf(", %d", flags & 7);
+ if (flags & 8) {
+ printf(", Serialized");
+ }
+ }
+ printf(") {\n");
+ end = start + pkglength;
+ scope_within_method = 1;
+ asl_dump_objectlist(&dp, end, indent + 1);
+ scope_within_method = 0;
+ print_indent(indent);
+ printf("}");
+
+ assert(dp == end);
+ ASL_LEAVE_SCOPE(oname);
+ *dpp = dp;
+}
+
+
+static void
+asl_dump_defopregion(u_int8_t **dpp, int indent)
+{
+ u_int8_t *dp;
+ const char *regions[] = {
+ "SystemMemory",
+ "SystemIO",
+ "PCI_Config",
+ "EmbeddedControl",
+ "SMBus",
+ };
+
+ dp = *dpp;
+ printf("OperationRegion(");
+ ASL_CREATE_LOCALNAMEOBJ(dp);
+ asl_dump_termobj(&dp, indent); /* Name */
+ printf(", %s, ", regions[*dp++]); /* Space */
+ asl_dump_termobj(&dp, indent); /* Offset */
+ printf(", ");
+ asl_dump_termobj(&dp, indent); /* Length */
+ printf(")");
+
+ *dpp = dp;
+}
+
+static const char *accessnames[] = {
+ "AnyAcc",
+ "ByteAcc",
+ "WordAcc",
+ "DWordAcc",
+ "BlockAcc",
+ "SMBSendRecvAcc",
+ "SMBQuickAcc"
+};
+
+static int
+asl_dump_field(u_int8_t **dpp, u_int32_t offset)
+{
+ u_int8_t *dp;
+ u_int8_t *name;
+ u_int8_t access, attribute;
+ u_int32_t width;
+
+ dp = *dpp;
+ switch (*dp) {
+ case '\\':
+ case '^':
+ case 'A' ... 'Z':
+ case '_':
+ case '.':
+ case '/':
+ ASL_CREATE_LOCALNAMEOBJ(dp);
+ name = asl_dump_namestring(&dp);
+ width = asl_dump_pkglength(&dp);
+ offset += width;
+ print_namestring(name);
+ printf(",\t%d", width);
+ break;
+ case 0x00:
+ dp++;
+ width = asl_dump_pkglength(&dp);
+ offset += width;
+ if ((offset % 8) == 0) {
+ printf("Offset(0x%x)", offset / 8);
+ } else {
+ printf(",\t%d", width);
+ }
+ break;
+ case 0x01:
+ access = dp[1];
+ attribute = dp[2];
+ dp += 3;
+ printf("AccessAs(%s, %d)", accessnames[access], attribute);
+ break;
+ }
+
+ *dpp = dp;
+ return (offset);
+}
+
+static void
+asl_dump_fieldlist(u_int8_t **dpp, u_int8_t *end, int indent)
+{
+ u_int8_t *dp;
+ u_int32_t offset;
+
+ dp = *dpp;
+ offset = 0;
+ while (dp < end) {
+ print_indent(indent);
+ offset = asl_dump_field(&dp, offset);
+ if (dp < end)
+ printf(",\n");
+ else
+ printf("\n");
+ }
+
+ *dpp = dp;
+}
+
+static void
+asl_dump_deffield(u_int8_t **dpp, int indent)
+{
+ u_int8_t *dp;
+ u_int8_t *start;
+ u_int8_t *end;
+ u_int8_t flags;
+ u_int32_t pkglength;
+ static const char *lockrules[] = {"NoLock", "Lock"};
+ static const char *updaterules[] = {"Preserve", "WriteAsOnes",
+ "WriteAsZeros", "*Error*"};
+
+ dp = *dpp;
+ start = dp;
+ pkglength = asl_dump_pkglength(&dp);
+ end = start + pkglength;
+
+ printf("Field(");
+ asl_dump_termobj(&dp, indent); /* Name */
+ flags = asl_dump_bytedata(&dp);
+ printf(", %s, %s, %s) {\n",
+ accessnames[flags & 0xf],
+ lockrules[(flags >> 4) & 1],
+ updaterules[(flags >> 5) & 3]);
+ asl_dump_fieldlist(&dp, end, indent + 1);
+ print_indent(indent);
+ printf("}");
+
+ assert(dp == end);
+
+ *dpp = dp;
+}
+
+static void
+asl_dump_defindexfield(u_int8_t **dpp, int indent)
+{
+ u_int8_t *dp;
+ u_int8_t *start;
+ u_int8_t *end;
+ u_int8_t flags;
+ u_int32_t pkglength;
+ static const char *lockrules[] = {"NoLock", "Lock"};
+ static const char *updaterules[] = {"Preserve", "WriteAsOnes",
+ "WriteAsZeros", "*Error*"};
+
+ dp = *dpp;
+ start = dp;
+ pkglength = asl_dump_pkglength(&dp);
+ end = start + pkglength;
+
+ printf("IndexField(");
+ asl_dump_termobj(&dp, indent); /* Name1 */
+ printf(", ");
+ asl_dump_termobj(&dp, indent); /* Name2 */
+ flags = asl_dump_bytedata(&dp);
+ printf(", %s, %s, %s) {\n",
+ accessnames[flags & 0xf],
+ lockrules[(flags >> 4) & 1],
+ updaterules[(flags >> 5) & 3]);
+ asl_dump_fieldlist(&dp, end, indent + 1);
+ print_indent(indent);
+ printf("}");
+
+ assert(dp == end);
+
+ *dpp = dp;
+}
+
+static void
+asl_dump_defbankfield(u_int8_t **dpp, int indent)
+{
+ u_int8_t *dp;
+ u_int8_t *start;
+ u_int8_t *end;
+ u_int8_t flags;
+ u_int32_t pkglength;
+ static const char *lockrules[] = {"NoLock", "Lock"};
+ static const char *updaterules[] = {"Preserve", "WriteAsOnes",
+ "WriteAsZeros", "*Error*"};
+
+ dp = *dpp;
+ start = dp;
+ pkglength = asl_dump_pkglength(&dp);
+ end = start + pkglength;
+
+ printf("BankField(");
+ asl_dump_termobj(&dp, indent); /* Name1 */
+ printf(", ");
+ asl_dump_termobj(&dp, indent); /* Name2 */
+ printf(", ");
+ asl_dump_termobj(&dp, indent); /* BankValue */
+ flags = asl_dump_bytedata(&dp);
+ printf(", %s, %s, %s) {\n",
+ accessnames[flags & 0xf],
+ lockrules[(flags >> 4) & 1],
+ updaterules[(flags >> 5) & 3]);
+ asl_dump_fieldlist(&dp, end, indent + 1);
+ print_indent(indent);
+ printf("}");
+
+ assert(dp == end);
+
+ *dpp = dp;
+}
+
+static void
+asl_dump_defdevice(u_int8_t **dpp, int indent)
+{
+ u_int8_t *dp;
+ u_int8_t *start;
+ u_int8_t *end;
+ u_int32_t pkglength;
+ struct aml_name *oname;
+
+ dp = *dpp;
+ start = dp;
+ pkglength = asl_dump_pkglength(&dp);
+ end = start + pkglength;
+
+ printf("Device(");
+ ASL_ENTER_SCOPE(dp, oname);
+ asl_dump_termobj(&dp, indent);
+ printf(") {\n");
+ asl_dump_objectlist(&dp, end, indent + 1);
+ print_indent(indent);
+ printf("}");
+
+ assert(dp == end);
+
+ ASL_LEAVE_SCOPE(oname);
+ *dpp = dp;
+}
+
+static void
+asl_dump_defprocessor(u_int8_t **dpp, int indent)
+{
+ u_int8_t *dp;
+ u_int8_t *start;
+ u_int8_t *end;
+ u_int8_t procid;
+ u_int8_t pblklen;
+ u_int32_t pkglength;
+ u_int32_t pblkaddr;
+ struct aml_name *oname;
+
+ dp = *dpp;
+ start = dp;
+ pkglength = asl_dump_pkglength(&dp);
+ end = start + pkglength;
+
+ printf("Processor(");
+ ASL_ENTER_SCOPE(dp, oname);
+ asl_dump_termobj(&dp, indent);
+ procid = asl_dump_bytedata(&dp);
+ pblkaddr = asl_dump_dworddata(&dp);
+ pblklen = asl_dump_bytedata(&dp);
+ printf(", %d, 0x%x, 0x%x) {\n", procid, pblkaddr, pblklen);
+ asl_dump_objectlist(&dp, end, indent + 1);
+ print_indent(indent);
+ printf("}");
+
+ assert(dp == end);
+
+ ASL_LEAVE_SCOPE(oname);
+ *dpp = dp;
+}
+
+static void
+asl_dump_defpowerres(u_int8_t **dpp, int indent)
+{
+ u_int8_t *dp;
+ u_int8_t *start;
+ u_int8_t *end;
+ u_int8_t systemlevel;
+ u_int16_t resourceorder;
+ u_int32_t pkglength;
+ struct aml_name *oname;
+
+ dp = *dpp;
+ start = dp;
+ pkglength = asl_dump_pkglength(&dp);
+ end = start + pkglength;
+
+ printf("PowerResource(");
+ ASL_ENTER_SCOPE(dp, oname);
+ asl_dump_termobj(&dp, indent);
+ systemlevel = asl_dump_bytedata(&dp);
+ resourceorder = asl_dump_worddata(&dp);
+ printf(", %d, %d) {\n", systemlevel, resourceorder);
+ asl_dump_objectlist(&dp, end, indent + 1);
+ print_indent(indent);
+ printf("}");
+
+ assert(dp == end);
+
+ ASL_LEAVE_SCOPE(oname);
+ *dpp = dp;
+}
+
+static void
+asl_dump_defthermalzone(u_int8_t **dpp, int indent)
+{
+ u_int8_t *dp;
+ u_int8_t *start;
+ u_int8_t *end;
+ u_int32_t pkglength;
+ struct aml_name *oname;
+
+ dp = *dpp;
+ start = dp;
+ pkglength = asl_dump_pkglength(&dp);
+ end = start + pkglength;
+
+ printf("ThermalZone(");
+ ASL_ENTER_SCOPE(dp, oname);
+ asl_dump_termobj(&dp, indent);
+ printf(") {\n");
+ asl_dump_objectlist(&dp, end, indent + 1);
+ print_indent(indent);
+ printf("}");
+
+ assert(dp == end);
+
+ ASL_LEAVE_SCOPE(oname);
+ *dpp = dp;
+}
+
+static void
+asl_dump_defif(u_int8_t **dpp, int indent)
+{
+ u_int8_t *dp;
+ u_int8_t *start;
+ u_int8_t *end;
+ u_int32_t pkglength;
+
+ dp = *dpp;
+ start = dp;
+ pkglength = asl_dump_pkglength(&dp);
+ end = start + pkglength;
+
+ printf("If(");
+ asl_dump_termobj(&dp, indent);
+ printf(") {\n");
+ asl_dump_objectlist(&dp, end, indent + 1);
+ print_indent(indent);
+ printf("}");
+
+ assert(dp == end);
+
+ *dpp = dp;
+}
+
+static void
+asl_dump_defelse(u_int8_t **dpp, int indent)
+{
+ u_int8_t *dp;
+ u_int8_t *start;
+ u_int8_t *end;
+ u_int32_t pkglength;
+
+ dp = *dpp;
+ start = dp;
+ pkglength = asl_dump_pkglength(&dp);
+ end = start + pkglength;
+
+ printf("Else {\n");
+ asl_dump_objectlist(&dp, end, indent + 1);
+ print_indent(indent);
+ printf("}");
+
+ assert(dp == end);
+
+ *dpp = dp;
+}
+
+static void
+asl_dump_defwhile(u_int8_t **dpp, int indent)
+{
+ u_int8_t *dp;
+ u_int8_t *start;
+ u_int8_t *end;
+ u_int32_t pkglength;
+
+ dp = *dpp;
+ start = dp;
+ pkglength = asl_dump_pkglength(&dp);
+ end = start + pkglength;
+
+ printf("While(");
+ asl_dump_termobj(&dp, indent);
+ printf(") {\n");
+ asl_dump_objectlist(&dp, end, indent + 1);
+ print_indent(indent);
+ printf("}");
+
+ assert(dp == end);
+
+ *dpp = dp;
+}
+
+/*
+ * Public interfaces
+ */
+
+void
+asl_dump_termobj(u_int8_t **dpp, int indent)
+{
+ u_int8_t *dp;
+ u_int8_t *name;
+ u_int8_t opcode;
+ struct aml_name *method;
+ const char *matchstr[] = {
+ "MTR", "MEQ", "MLE", "MLT", "MGE", "MGT",
+ };
+
+#define OPTARG() do { \
+ printf(", "); \
+ if (*dp == 0x00) { \
+ dp++; \
+ } else { \
+ asl_dump_termobj(&dp, indent); \
+ } \
+} while (0)
+
+ dp = *dpp;
+ opcode = *dp++;
+ switch (opcode) {
+ case '\\':
+ case '^':
+ case 'A' ... 'Z':
+ case '_':
+ case '.':
+ case '/':
+ dp--;
+ print_namestring((name = asl_dump_namestring(&dp)));
+ if (scope_within_method == 1) {
+ method = aml_search_name(&asl_env, name);
+ if (method != NULL && method->property != NULL &&
+ method->property->type == aml_t_method) {
+ int i, argnum;
+
+ argnum = method->property->meth.argnum & 7;
+ printf("(");
+ for (i = 0; i < argnum; i++) {
+ asl_dump_termobj(&dp, indent);
+ if (i < (argnum-1)) {
+ printf(", ");
+ }
+ }
+ printf(")");
+ }
+ }
+ break;
+ case 0x0a: /* BytePrefix */
+ printf("0x%x", asl_dump_bytedata(&dp));
+ break;
+ case 0x0b: /* WordPrefix */
+ printf("0x%04x", asl_dump_worddata(&dp));
+ break;
+ case 0x0c: /* DWordPrefix */
+ printf("0x%08x", asl_dump_dworddata(&dp));
+ break;
+ case 0x0d: /* StringPrefix */
+ printf("\"%s\"", (const char *) dp);
+ while (*dp)
+ dp++;
+ dp++; /* NUL terminate */
+ break;
+ case 0x00: /* ZeroOp */
+ printf("Zero");
+ break;
+ case 0x01: /* OneOp */
+ printf("One");
+ break;
+ case 0xff: /* OnesOp */
+ printf("Ones");
+ break;
+ case 0x06: /* AliasOp */
+ printf("Alias(");
+ ASL_CREATE_LOCALNAMEOBJ(dp);
+ asl_dump_termobj(&dp, indent);
+ printf(", ");
+ asl_dump_termobj(&dp, indent);
+ printf(")");
+ break;
+ case 0x08: /* NameOp */
+ printf("Name(");
+ ASL_CREATE_LOCALNAMEOBJ(dp);
+ asl_dump_termobj(&dp, indent);
+ printf(", ");
+ asl_dump_termobj(&dp, indent);
+ printf(")");
+ break;
+ case 0x10: /* ScopeOp */
+ asl_dump_defscope(&dp, indent);
+ break;
+ case 0x11: /* BufferOp */
+ asl_dump_defbuffer(&dp, indent);
+ break;
+ case 0x12: /* PackageOp */
+ asl_dump_defpackage(&dp, indent);
+ break;
+ case 0x14: /* MethodOp */
+ asl_dump_defmethod(&dp, indent);
+ break;
+ case 0x5b: /* ExtOpPrefix */
+ opcode = *dp++;
+ switch (opcode) {
+ case 0x01: /* MutexOp */
+ printf("Mutex(");
+ ASL_CREATE_LOCALNAMEOBJ(dp);
+ asl_dump_termobj(&dp, indent);
+ printf(", %d)", *dp++);
+ break;
+ case 0x02: /* EventOp */
+ printf("Event(");
+ asl_dump_termobj(&dp, indent);
+ printf(")");
+ break;
+ case 0x12: /* CondRefOfOp */
+ printf("CondRefOf(");
+ asl_dump_termobj(&dp, indent);
+ printf(", ");
+ asl_dump_termobj(&dp, indent);
+ printf(")");
+ break;
+ case 0x13: /* CreateFieldOp */
+ printf("CreateField(");
+ asl_dump_termobj(&dp, indent);
+ printf(", ");
+ asl_dump_termobj(&dp, indent);
+ printf(", ");
+ asl_dump_termobj(&dp, indent);
+ printf(", ");
+ ASL_CREATE_LOCALNAMEOBJ(dp);
+ asl_dump_termobj(&dp, indent);
+ printf(")");
+ break;
+ case 0x20: /* LoadOp */
+ printf("Load(");
+ asl_dump_termobj(&dp, indent);
+ printf(", ");
+ asl_dump_termobj(&dp, indent);
+ printf(")");
+ break;
+ case 0x21: /* StallOp */
+ printf("Stall(");
+ asl_dump_termobj(&dp, indent);
+ printf(")");
+ break;
+ case 0x22: /* SleepOp */
+ printf("Sleep(");
+ asl_dump_termobj(&dp, indent);
+ printf(")");
+ break;
+ case 0x23: /* AcquireOp */
+ printf("Acquire(");
+ asl_dump_termobj(&dp, indent);
+ printf(", 0x%x)", asl_dump_worddata(&dp));
+ break;
+ case 0x24: /* SignalOp */
+ printf("Signal(");
+ asl_dump_termobj(&dp, indent);
+ printf(")");
+ break;
+ case 0x25: /* WaitOp */
+ printf("Wait(");
+ asl_dump_termobj(&dp, indent);
+ printf(", ");
+ asl_dump_termobj(&dp, indent);
+ printf(")");
+ break;
+ case 0x26: /* ResetOp */
+ printf("Reset(");
+ asl_dump_termobj(&dp, indent);
+ printf(")");
+ break;
+ case 0x27: /* ReleaseOp */
+ printf("Release(");
+ asl_dump_termobj(&dp, indent);
+ printf(")");
+ break;
+ case 0x28: /* FromBCDOp */
+ printf("FromBCD(");
+ asl_dump_termobj(&dp, indent);
+ printf(", ");
+ asl_dump_termobj(&dp, indent);
+ printf(")");
+ break;
+ case 0x29: /* ToBCDOp */
+ printf("ToBCD(");
+ asl_dump_termobj(&dp, indent);
+ OPTARG();
+ printf(")");
+ break;
+ case 0x2a: /* UnloadOp */
+ printf("Unload(");
+ asl_dump_termobj(&dp, indent);
+ printf(")");
+ break;
+ case 0x30:
+ printf("Revision");
+ break;
+ case 0x31:
+ printf("Debug");
+ break;
+ case 0x32: /* FatalOp */
+ printf("Fatal(");
+ printf("0x%x, ", asl_dump_bytedata(&dp));
+ printf("0x%x, ", asl_dump_dworddata(&dp));
+ asl_dump_termobj(&dp, indent);
+ printf(")");
+ break;
+ case 0x80: /* OpRegionOp */
+ asl_dump_defopregion(&dp, indent);
+ break;
+ case 0x81: /* FieldOp */
+ asl_dump_deffield(&dp, indent);
+ break;
+ case 0x82: /* DeviceOp */
+ asl_dump_defdevice(&dp, indent);
+ break;
+ case 0x83: /* ProcessorOp */
+ asl_dump_defprocessor(&dp, indent);
+ break;
+ case 0x84: /* PowerResOp */
+ asl_dump_defpowerres(&dp, indent);
+ break;
+ case 0x85: /* ThermalZoneOp */
+ asl_dump_defthermalzone(&dp, indent);
+ break;
+ case 0x86: /* IndexFieldOp */
+ asl_dump_defindexfield(&dp, indent);
+ break;
+ case 0x87: /* BankFieldOp */
+ asl_dump_defbankfield(&dp, indent);
+ break;
+ default:
+ errx(1, "strange opcode 0x5b, 0x%x\n", opcode);
+ }
+ break;
+ case 0x68 ... 0x6e: /* ArgN */
+ printf("Arg%d", opcode - 0x68);
+ break;
+ case 0x60 ... 0x67:
+ printf("Local%d", opcode - 0x60);
+ break;
+ case 0x70: /* StoreOp */
+ printf("Store(");
+ asl_dump_termobj(&dp, indent);
+ printf(", ");
+ asl_dump_termobj(&dp, indent);
+ printf(")");
+ break;
+ case 0x71: /* RefOfOp */
+ printf("RefOf(");
+ asl_dump_termobj(&dp, indent);
+ printf(")");
+ break;
+ case 0x72: /* AddOp */
+ printf("Add(");
+ asl_dump_termobj(&dp, indent);
+ printf(", ");
+ asl_dump_termobj(&dp, indent);
+ OPTARG();
+ printf(")");
+ break;
+ case 0x73: /* ConcatenateOp */
+ printf("Concatenate(");
+ asl_dump_termobj(&dp, indent);
+ printf(", ");
+ asl_dump_termobj(&dp, indent);
+ printf(", ");
+ asl_dump_termobj(&dp, indent);
+ printf(")");
+ break;
+ case 0x74: /* SubtractOp */
+ printf("Subtract(");
+ asl_dump_termobj(&dp, indent);
+ printf(", ");
+ asl_dump_termobj(&dp, indent);
+ OPTARG();
+ printf(")");
+ break;
+ case 0x75: /* IncrementOp */
+ printf("Increment(");
+ asl_dump_termobj(&dp, indent);
+ printf(")");
+ break;
+ case 0x76: /* DecrementOp */
+ printf("Decrement(");
+ asl_dump_termobj(&dp, indent);
+ printf(")");
+ break;
+ case 0x77: /* MultiplyOp */
+ printf("Multiply(");
+ asl_dump_termobj(&dp, indent);
+ printf(", ");
+ asl_dump_termobj(&dp, indent);
+ OPTARG();
+ printf(")");
+ break;
+ case 0x78: /* DivideOp */
+ printf("Divide(");
+ asl_dump_termobj(&dp, indent);
+ printf(", ");
+ asl_dump_termobj(&dp, indent);
+ OPTARG();
+ OPTARG();
+ printf(")");
+ break;
+ case 0x79: /* ShiftLeftOp */
+ printf("ShiftLeft(");
+ asl_dump_termobj(&dp, indent);
+ printf(", ");
+ asl_dump_termobj(&dp, indent);
+ OPTARG();
+ printf(")");
+ break;
+ case 0x7a: /* ShiftRightOp */
+ printf("ShiftRight(");
+ asl_dump_termobj(&dp, indent);
+ printf(", ");
+ asl_dump_termobj(&dp, indent);
+ OPTARG();
+ printf(")");
+ break;
+ case 0x7b: /* AndOp */
+ printf("And(");
+ asl_dump_termobj(&dp, indent);
+ printf(", ");
+ asl_dump_termobj(&dp, indent);
+ OPTARG();
+ printf(")");
+ break;
+ case 0x7c: /* NAndOp */
+ printf("NAnd(");
+ asl_dump_termobj(&dp, indent);
+ printf(", ");
+ asl_dump_termobj(&dp, indent);
+ OPTARG();
+ printf(")");
+ break;
+ case 0x7d: /* OrOp */
+ printf("Or(");
+ asl_dump_termobj(&dp, indent);
+ printf(", ");
+ asl_dump_termobj(&dp, indent);
+ OPTARG();
+ printf(")");
+ break;
+ case 0x7e: /* NOrOp */
+ printf("NOr(");
+ asl_dump_termobj(&dp, indent);
+ printf(", ");
+ asl_dump_termobj(&dp, indent);
+ OPTARG();
+ printf(")");
+ break;
+ case 0x7f: /* XOrOp */
+ printf("XOr(");
+ asl_dump_termobj(&dp, indent);
+ printf(", ");
+ asl_dump_termobj(&dp, indent);
+ OPTARG();
+ printf(")");
+ break;
+ case 0x80: /* NotOp */
+ printf("Not(");
+ asl_dump_termobj(&dp, indent);
+ OPTARG();
+ printf(")");
+ break;
+ case 0x81: /* FindSetLeftBitOp */
+ printf("FindSetLeftBit(");
+ asl_dump_termobj(&dp, indent);
+ printf(", ");
+ asl_dump_termobj(&dp, indent);
+ printf(")");
+ break;
+ case 0x82: /* FindSetRightBitOp */
+ printf("FindSetRightBit(");
+ asl_dump_termobj(&dp, indent);
+ printf(", ");
+ asl_dump_termobj(&dp, indent);
+ printf(")");
+ break;
+ case 0x83: /* DerefOp */
+ printf("DerefOf(");
+ asl_dump_termobj(&dp, indent);
+ printf(")");
+ break;
+ case 0x86: /* NotifyOp */
+ printf("Notify(");
+ asl_dump_termobj(&dp, indent);
+ printf(", ");
+ asl_dump_termobj(&dp, indent);
+ printf(")");
+ break;
+ case 0x87: /* SizeOfOp */
+ printf("SizeOf(");
+ asl_dump_termobj(&dp, indent);
+ printf(")");
+ break;
+ case 0x88: /* IndexOp */
+ printf("Index(");
+ asl_dump_termobj(&dp, indent);
+ printf(", ");
+ asl_dump_termobj(&dp, indent);
+ OPTARG();
+ printf(")");
+ break;
+ case 0x89: /* MatchOp */
+ printf("Match(");
+ asl_dump_termobj(&dp, indent);
+ printf(", %s, ", matchstr[*dp++]);
+ asl_dump_termobj(&dp, indent);
+ printf(", %s, ", matchstr[*dp++]);
+ asl_dump_termobj(&dp, indent);
+ printf(", ");
+ asl_dump_termobj(&dp, indent);
+ printf(")");
+ break;
+ case 0x8a: /* CreateDWordFieldOp */
+ printf("CreateDWordField(");
+ asl_dump_termobj(&dp, indent);
+ printf(", ");
+ asl_dump_termobj(&dp, indent);
+ printf(", ");
+ ASL_CREATE_LOCALNAMEOBJ(dp);
+ asl_dump_termobj(&dp, indent);
+ printf(")");
+ break;
+ case 0x8b: /* CreateWordFieldOp */
+ printf("CreateWordField(");
+ asl_dump_termobj(&dp, indent);
+ printf(", ");
+ asl_dump_termobj(&dp, indent);
+ printf(", ");
+ ASL_CREATE_LOCALNAMEOBJ(dp);
+ asl_dump_termobj(&dp, indent);
+ printf(")");
+ break;
+ case 0x8c: /* CreateByteFieldOp */
+ printf("CreateByteField(");
+ asl_dump_termobj(&dp, indent);
+ printf(", ");
+ asl_dump_termobj(&dp, indent);
+ printf(", ");
+ ASL_CREATE_LOCALNAMEOBJ(dp);
+ asl_dump_termobj(&dp, indent);
+ printf(")");
+ break;
+ case 0x8d: /* CreateBitFieldOp */
+ printf("CreateBitField(");
+ asl_dump_termobj(&dp, indent);
+ printf(", ");
+ asl_dump_termobj(&dp, indent);
+ printf(", ");
+ ASL_CREATE_LOCALNAMEOBJ(dp);
+ asl_dump_termobj(&dp, indent);
+ printf(")");
+ break;
+ case 0x8e: /* ObjectTypeOp */
+ printf("ObjectType(");
+ asl_dump_termobj(&dp, indent);
+ printf(")");
+ break;
+ case 0x90:
+ printf("LAnd(");
+ asl_dump_termobj(&dp, indent);
+ printf(", ");
+ asl_dump_termobj(&dp, indent);
+ printf(")");
+ break;
+ case 0x91:
+ printf("LOr(");
+ asl_dump_termobj(&dp, indent);
+ printf(", ");
+ asl_dump_termobj(&dp, indent);
+ printf(")");
+ break;
+ case 0x92:
+ printf("LNot(");
+ asl_dump_termobj(&dp, indent);
+ printf(")");
+ break;
+ case 0x93:
+ printf("LEqual(");
+ asl_dump_termobj(&dp, indent);
+ printf(", ");
+ asl_dump_termobj(&dp, indent);
+ printf(")");
+ break;
+ case 0x94:
+ printf("LGreater(");
+ asl_dump_termobj(&dp, indent);
+ printf(", ");
+ asl_dump_termobj(&dp, indent);
+ printf(")");
+ break;
+ case 0x95:
+ printf("LLess(");
+ asl_dump_termobj(&dp, indent);
+ printf(", ");
+ asl_dump_termobj(&dp, indent);
+ printf(")");
+ break;
+ case 0xa0: /* IfOp */
+ asl_dump_defif(&dp, indent);
+ break;
+ case 0xa1: /* ElseOp */
+ asl_dump_defelse(&dp, indent);
+ break;
+ case 0xa2: /* WhileOp */
+ asl_dump_defwhile(&dp, indent);
+ break;
+ case 0xa3: /* NoopOp */
+ printf("Noop");
+ break;
+ case 0xa5: /* BreakOp */
+ printf("Break");
+ break;
+ case 0xa4: /* ReturnOp */
+ printf("Return(");
+ asl_dump_termobj(&dp, indent);
+ printf(")");
+ break;
+ case 0xcc: /* BreakPointOp */
+ printf("BreakPoint");
+ break;
+ default:
+ errx(1, "strange opcode 0x%x\n", opcode);
+ }
+
+ *dpp = dp;
+}
+
+void
+asl_dump_objectlist(u_int8_t **dpp, u_int8_t *end, int indent)
+{
+ u_int8_t *dp;
+
+ dp = *dpp;
+ while (dp < end) {
+ print_indent(indent);
+ asl_dump_termobj(&dp, indent);
+ printf("\n");
+ }
+
+ *dpp = dp;
+}
diff --git a/usr.sbin/acpi/amldb/Makefile b/usr.sbin/acpi/amldb/Makefile
new file mode 100644
index 0000000..68cf9fe
--- /dev/null
+++ b/usr.sbin/acpi/amldb/Makefile
@@ -0,0 +1,13 @@
+# $Id: Makefile,v 1.5 2000/07/14 18:16:30 iwasaki Exp $
+# $FreeBSD$
+
+PROG= amldb
+MAN= amldb.8
+SRCS= amldb.c debug.c region.c
+SRCS+= aml_parse.c aml_name.c aml_amlmem.c aml_memman.c aml_store.c \
+ aml_obj.c aml_evalobj.c aml_common.c
+
+CFLAGS+= -I${.CURDIR}
+
+.include <bsd.prog.mk>
+.PATH: ${.CURDIR}/aml
diff --git a/usr.sbin/acpi/amldb/aml/aml_amlmem.c b/usr.sbin/acpi/amldb/aml/aml_amlmem.c
new file mode 100644
index 0000000..e8f42b9
--- /dev/null
+++ b/usr.sbin/acpi/amldb/aml/aml_amlmem.c
@@ -0,0 +1,92 @@
+/*-
+ * Copyright (c) 1999, 2000 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: aml_amlmem.c,v 1.15 2000/08/09 14:47:43 iwasaki Exp $
+ * $FreeBSD$
+ */
+
+/*
+ * AML Namespace Memory Management
+ */
+
+#include <sys/param.h>
+
+#include <aml/aml_env.h>
+#include <aml/aml_memman.h>
+#include <aml/aml_name.h>
+
+MEMMAN_INITIALSTORAGE_DESC(struct aml_namestr, _aml_namestr_storage);
+MEMMAN_INITIALSTORAGE_DESC(struct aml_num, _aml_num_storage);
+MEMMAN_INITIALSTORAGE_DESC(struct aml_string, _aml_string_storage);
+MEMMAN_INITIALSTORAGE_DESC(struct aml_buffer, _aml_buffer_storage);
+MEMMAN_INITIALSTORAGE_DESC(struct aml_package, _aml_package_storage);
+MEMMAN_INITIALSTORAGE_DESC(struct aml_field, _aml_field_storage);
+MEMMAN_INITIALSTORAGE_DESC(struct aml_method, _aml_method_storage);
+MEMMAN_INITIALSTORAGE_DESC(struct aml_mutex, _aml_mutex_storage);
+MEMMAN_INITIALSTORAGE_DESC(struct aml_opregion, _aml_opregion_storage);
+MEMMAN_INITIALSTORAGE_DESC(struct aml_powerres, _aml_powerres_storage);
+MEMMAN_INITIALSTORAGE_DESC(struct aml_processor, _aml_processor_storage);
+MEMMAN_INITIALSTORAGE_DESC(struct aml_bufferfield, _aml_bufferfield_storage);
+MEMMAN_INITIALSTORAGE_DESC(struct aml_event, _aml_event_storage);
+MEMMAN_INITIALSTORAGE_DESC(enum aml_objtype, _aml_objtype_storage);
+MEMMAN_INITIALSTORAGE_DESC(struct aml_name, _aml_name_storage);
+MEMMAN_INITIALSTORAGE_DESC(struct aml_name_group, _aml_name_group_storage);
+MEMMAN_INITIALSTORAGE_DESC(struct aml_objref, _aml_objref_storage);
+MEMMAN_INITIALSTORAGE_DESC(struct aml_regfield, _aml_regfield_storage);
+MEMMAN_INITIALSTORAGE_DESC(struct aml_environ, _aml_environ_storage);
+MEMMAN_INITIALSTORAGE_DESC(struct aml_local_stack, _aml_local_stack_storage);
+MEMMAN_INITIALSTORAGE_DESC(struct aml_mutex_queue, _aml_mutex_queue_storage);
+
+struct memman_blockman aml_blockman[] = {
+ MEMMAN_MEMBLOCK_DESC(sizeof(struct aml_namestr), _aml_namestr_storage),
+ MEMMAN_MEMBLOCK_DESC(sizeof(struct aml_num), _aml_num_storage),
+ MEMMAN_MEMBLOCK_DESC(sizeof(struct aml_string), _aml_string_storage),
+ MEMMAN_MEMBLOCK_DESC(sizeof(struct aml_buffer), _aml_buffer_storage),
+ MEMMAN_MEMBLOCK_DESC(sizeof(struct aml_package), _aml_package_storage),
+ MEMMAN_MEMBLOCK_DESC(sizeof(struct aml_field), _aml_field_storage),
+ MEMMAN_MEMBLOCK_DESC(sizeof(struct aml_method), _aml_method_storage),
+ MEMMAN_MEMBLOCK_DESC(sizeof(struct aml_mutex), _aml_mutex_storage),
+ MEMMAN_MEMBLOCK_DESC(sizeof(struct aml_opregion), _aml_opregion_storage),
+ MEMMAN_MEMBLOCK_DESC(sizeof(struct aml_powerres), _aml_powerres_storage),
+ MEMMAN_MEMBLOCK_DESC(sizeof(struct aml_processor), _aml_processor_storage),
+ MEMMAN_MEMBLOCK_DESC(sizeof(struct aml_bufferfield), _aml_bufferfield_storage),
+ MEMMAN_MEMBLOCK_DESC(sizeof(struct aml_event), _aml_event_storage),
+ MEMMAN_MEMBLOCK_DESC(sizeof(enum aml_objtype), _aml_objtype_storage),
+ MEMMAN_MEMBLOCK_DESC(sizeof(struct aml_name), _aml_name_storage),
+ MEMMAN_MEMBLOCK_DESC(sizeof(struct aml_name_group), _aml_name_group_storage),
+ MEMMAN_MEMBLOCK_DESC(sizeof(struct aml_objref), _aml_objref_storage),
+ MEMMAN_MEMBLOCK_DESC(sizeof(struct aml_regfield), _aml_regfield_storage),
+ MEMMAN_MEMBLOCK_DESC(sizeof(struct aml_environ), _aml_environ_storage),
+ MEMMAN_MEMBLOCK_DESC(sizeof(struct aml_local_stack), _aml_local_stack_storage),
+ MEMMAN_MEMBLOCK_DESC(sizeof(struct aml_mutex_queue), _aml_mutex_queue_storage),
+};
+
+struct memman_histogram aml_histogram[MEMMAN_HISTOGRAM_SIZE];
+
+static struct memman _aml_memman = MEMMAN_MEMMANAGER_DESC(aml_blockman, 21,
+ aml_histogram, 1);
+
+struct memman *aml_memman = &_aml_memman;
+
diff --git a/usr.sbin/acpi/amldb/aml/aml_amlmem.h b/usr.sbin/acpi/amldb/aml/aml_amlmem.h
new file mode 100644
index 0000000..d210a7e
--- /dev/null
+++ b/usr.sbin/acpi/amldb/aml/aml_amlmem.h
@@ -0,0 +1,65 @@
+/*-
+ * Copyright (c) 1999 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: aml_amlmem.h,v 1.12 2000/08/08 14:12:05 iwasaki Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _AML_AMLMEM_H_
+#define _AML_AMLMEM_H_
+
+/*
+ * AML Namespace Memory Management
+ */
+
+#include <aml/aml_memman.h>
+
+enum {
+ memid_aml_namestr = 0,
+ memid_aml_num,
+ memid_aml_string,
+ memid_aml_buffer,
+ memid_aml_package,
+ memid_aml_field,
+ memid_aml_method,
+ memid_aml_mutex,
+ memid_aml_opregion,
+ memid_aml_powerres,
+ memid_aml_processor,
+ memid_aml_bufferfield,
+ memid_aml_event,
+ memid_aml_objtype,
+ memid_aml_name,
+ memid_aml_name_group,
+ memid_aml_objref,
+ memid_aml_regfield,
+ memid_aml_environ,
+ memid_aml_local_stack,
+ memid_aml_mutex_queue,
+};
+
+extern struct memman *aml_memman;
+
+#endif /* !_AML_AMLMEM_H_ */
diff --git a/usr.sbin/acpi/amldb/aml/aml_common.c b/usr.sbin/acpi/amldb/aml/aml_common.c
new file mode 100644
index 0000000..0b8816b
--- /dev/null
+++ b/usr.sbin/acpi/amldb/aml/aml_common.c
@@ -0,0 +1,735 @@
+/*-
+ * Copyright (c) 1999 Takanori Watanabe
+ * Copyright (c) 1999, 2000 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: aml_common.c,v 1.9 2000/08/09 14:47:43 iwasaki Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+
+#ifndef _KERNEL
+#include <assert.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#else /* _KERNEL */
+#include "opt_acpi.h"
+#include <sys/kernel.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <machine/bus.h>
+#include <dev/acpi/acpireg.h>
+#include <dev/acpi/acpivar.h>
+#ifndef ACPI_NO_OSDFUNC_INLINE
+#include <machine/acpica_osd.h>
+#endif /* !ACPI_NO_OSDFUNC_INLINE */
+#endif /* !_KERNEL */
+
+#include <aml/aml_common.h>
+#include <aml/aml_env.h>
+#include <aml/aml_evalobj.h>
+#include <aml/aml_name.h>
+#include <aml/aml_obj.h>
+#include <aml/aml_parse.h>
+#include <aml/aml_status.h>
+#include <aml/aml_store.h>
+
+/* for debugging */
+#ifdef AML_DEBUG
+int aml_debug = 1;
+#else /* !AML_DEBUG */
+int aml_debug = 0;
+#endif /* AML_DEBUG */
+#ifdef _KERNEL
+SYSCTL_INT(_debug, OID_AUTO, aml_debug, CTLFLAG_RW, &aml_debug, 1, "");
+#endif /* _KERNEL */
+
+static void aml_print_nameseg(u_int8_t *dp);
+
+static void
+aml_print_nameseg(u_int8_t *dp)
+{
+
+ if (dp[3] != '_') {
+ AML_DEBUGPRINT("%c%c%c%c", dp[0], dp[1], dp[2], dp[3]);
+ } else if (dp[2] != '_') {
+ AML_DEBUGPRINT("%c%c%c_", dp[0], dp[1], dp[2]);
+ } else if (dp[1] != '_') {
+ AML_DEBUGPRINT("%c%c__", dp[0], dp[1]);
+ } else if (dp[0] != '_') {
+ AML_DEBUGPRINT("%c___", dp[0]);
+ }
+}
+
+void
+aml_print_namestring(u_int8_t *dp)
+{
+ int segcount;
+ int i;
+
+ if (dp[0] == '\\') {
+ AML_DEBUGPRINT("%c", dp[0]);
+ dp++;
+ } else if (dp[0] == '^') {
+ while (dp[0] == '^') {
+ AML_DEBUGPRINT("%c", dp[0]);
+ dp++;
+ }
+ }
+ if (dp[0] == 0x00) { /* NullName */
+ /* AML_DEBUGPRINT("<null>"); */
+ dp++;
+ } else if (dp[0] == 0x2e) { /* DualNamePrefix */
+ aml_print_nameseg(dp + 1);
+ AML_DEBUGPRINT("%c", '.');
+ aml_print_nameseg(dp + 5);
+ } else if (dp[0] == 0x2f) { /* MultiNamePrefix */
+ segcount = dp[1];
+ for (i = 0, dp += 2; i < segcount; i++, dp += 4) {
+ if (i > 0) {
+ AML_DEBUGPRINT("%c", '.');
+ }
+ aml_print_nameseg(dp);
+ }
+ } else /* NameSeg */
+ aml_print_nameseg(dp);
+}
+
+int
+aml_print_curname(struct aml_name *name)
+{
+ struct aml_name *root;
+
+ root = aml_get_rootname();
+ if (name == root) {
+ AML_DEBUGPRINT("\\");
+ return (0);
+ } else {
+ aml_print_curname(name->parent);
+ }
+ aml_print_nameseg(name->name);
+ AML_DEBUGPRINT(".");
+ return (0);
+}
+
+void
+aml_print_indent(int indent)
+{
+ int i;
+
+ for (i = 0; i < indent; i++)
+ AML_DEBUGPRINT(" ");
+}
+
+void
+aml_showobject(union aml_object * obj)
+{
+ int debug;
+ int i;
+
+ if (obj == NULL) {
+ printf("NO object\n");
+ return;
+ }
+ debug = aml_debug;
+ aml_debug = 1;
+ switch (obj->type) {
+ case aml_t_num:
+ printf("Num:0x%x\n", obj->num.number);
+ break;
+ case aml_t_processor:
+ printf("Processor:No %d,Port 0x%x length 0x%x\n",
+ obj->proc.id, obj->proc.addr, obj->proc.len);
+ break;
+ case aml_t_mutex:
+ printf("Mutex:Level %d\n", obj->mutex.level);
+ break;
+ case aml_t_powerres:
+ printf("PowerResource:Level %d Order %d\n",
+ obj->pres.level, obj->pres.order);
+ break;
+ case aml_t_opregion:
+ printf("OprationRegion:Busspace%d, Offset 0x%x Length 0x%x\n",
+ obj->opregion.space, obj->opregion.offset,
+ obj->opregion.length);
+ break;
+ case aml_t_field:
+ printf("Fieldelement:flag 0x%x offset 0x%x len 0x%x {",
+ obj->field.flags, obj->field.bitoffset,
+ obj->field.bitlen);
+ switch (obj->field.f.ftype) {
+ case f_t_field:
+ aml_print_namestring(obj->field.f.fld.regname);
+ break;
+ case f_t_index:
+ aml_print_namestring(obj->field.f.ifld.indexname);
+ printf(" ");
+ aml_print_namestring(obj->field.f.ifld.dataname);
+ break;
+ case f_t_bank:
+ aml_print_namestring(obj->field.f.bfld.regname);
+ printf(" ");
+ aml_print_namestring(obj->field.f.bfld.bankname);
+ printf("0x%x", obj->field.f.bfld.bankvalue);
+ break;
+ }
+ printf("}\n");
+ break;
+ case aml_t_method:
+ printf("Method: Arg %d From %p To %p\n", obj->meth.argnum,
+ obj->meth.from, obj->meth.to);
+ break;
+ case aml_t_buffer:
+ printf("Buffer: size:0x%x Data %p\n", obj->buffer.size,
+ obj->buffer.data);
+ break;
+ case aml_t_device:
+ printf("Device\n");
+ break;
+ case aml_t_bufferfield:
+ printf("Bufferfield:offset 0x%x len 0x%x Origin %p\n",
+ obj->bfld.bitoffset, obj->bfld.bitlen, obj->bfld.origin);
+ break;
+ case aml_t_string:
+ printf("String:%s\n", obj->str.string);
+ break;
+ case aml_t_package:
+ printf("Package:elements %d \n", obj->package.elements);
+ for (i = 0; i < obj->package.elements; i++) {
+ if (obj->package.objects[i] == NULL) {
+ break;
+ }
+ if (obj->package.objects[i]->type < 0) {
+ continue;
+ }
+ printf(" ");
+ aml_showobject(obj->package.objects[i]);
+ }
+ break;
+ case aml_t_therm:
+ printf("Thermalzone\n");
+ break;
+ case aml_t_event:
+ printf("Event\n");
+ break;
+ case aml_t_ddbhandle:
+ printf("DDBHANDLE\n");
+ break;
+ case aml_t_objref:
+ if (obj->objref.alias == 1) {
+ printf("Alias");
+ } else {
+ printf("Object reference");
+ if (obj->objref.offset >= 0) {
+ printf(" (offset 0x%x)", obj->objref.offset);
+ }
+ }
+ printf(" of ");
+ aml_showobject(obj->objref.ref);
+ break;
+ default:
+ printf("UNK ID=%d\n", obj->type);
+ }
+
+ aml_debug = debug;
+}
+
+void
+aml_showtree(struct aml_name * aname, int lev)
+{
+ int i;
+ struct aml_name *ptr;
+ char name[5];
+
+ for (i = 0; i < lev; i++) {
+ printf(" ");
+ }
+ strncpy(name, aname->name, 4);
+ name[4] = 0;
+ printf("%s ", name);
+ if (aname->property != NULL) {
+ aml_showobject(aname->property);
+ } else {
+ printf("\n");
+ }
+ for (ptr = aname->child; ptr; ptr = ptr->brother)
+ aml_showtree(ptr, lev + 1);
+}
+
+/*
+ * Common Region I/O Stuff
+ */
+
+static __inline u_int64_t
+aml_adjust_bitmask(u_int32_t flags, u_int32_t bitlen)
+{
+ u_int64_t bitmask;
+
+ switch (AML_FIELDFLAGS_ACCESSTYPE(flags)) {
+ case AML_FIELDFLAGS_ACCESS_ANYACC:
+ if (bitlen <= 8) {
+ bitmask = 0x000000ff;
+ break;
+ }
+ if (bitlen <= 16) {
+ bitmask = 0x0000ffff;
+ break;
+ }
+ bitmask = 0xffffffff;
+ break;
+ case AML_FIELDFLAGS_ACCESS_BYTEACC:
+ bitmask = 0x000000ff;
+ break;
+ case AML_FIELDFLAGS_ACCESS_WORDACC:
+ bitmask = 0x0000ffff;
+ break;
+ case AML_FIELDFLAGS_ACCESS_DWORDACC:
+ default:
+ bitmask = 0xffffffff;
+ break;
+ }
+
+ switch (bitlen) {
+ case 16:
+ bitmask |= 0x0000ffff;
+ break;
+ case 32:
+ bitmask |= 0xffffffff;
+ break;
+ }
+
+ return (bitmask);
+}
+
+u_int32_t
+aml_adjust_readvalue(u_int32_t flags, u_int32_t bitoffset, u_int32_t bitlen,
+ u_int32_t orgval)
+{
+ u_int32_t offset, retval;
+ u_int64_t bitmask;
+
+ offset = bitoffset; /* XXX bitoffset may change in this function! */
+ bitmask = aml_adjust_bitmask(flags, bitlen);
+ retval = (orgval >> offset) & (~(bitmask << bitlen)) & bitmask;
+
+ return (retval);
+}
+
+u_int32_t
+aml_adjust_updatevalue(u_int32_t flags, u_int32_t bitoffset, u_int32_t bitlen,
+ u_int32_t orgval, u_int32_t value)
+{
+ u_int32_t offset, retval;
+ u_int64_t bitmask;
+
+ offset = bitoffset; /* XXX bitoffset may change in this function! */
+ bitmask = aml_adjust_bitmask(flags, bitlen);
+ retval = orgval;
+ switch (AML_FIELDFLAGS_UPDATERULE(flags)) {
+ case AML_FIELDFLAGS_UPDATE_PRESERVE:
+ retval &= (~(((u_int64_t)1 << bitlen) - 1) << offset) |
+ (~(bitmask << offset));
+ break;
+ case AML_FIELDFLAGS_UPDATE_WRITEASONES:
+ retval = (~(((u_int64_t)1 << bitlen) - 1) << offset) |
+ (~(bitmask << offset));
+ retval &= bitmask; /* trim the upper bits */
+ break;
+ case AML_FIELDFLAGS_UPDATE_WRITEASZEROS:
+ retval = 0;
+ break;
+ default:
+ printf("illegal update rule: %d\n", flags);
+ return (orgval);
+ }
+
+ retval |= (value << (offset & bitmask));
+ return (retval);
+}
+
+/*
+ * BufferField I/O
+ */
+
+#define AML_BUFFER_INPUT 0
+#define AML_BUFFER_OUTPUT 1
+
+static int aml_bufferfield_io(int io, u_int32_t *valuep,
+ u_int8_t *origin, u_int32_t bitoffset,
+ u_int32_t bitlen);
+
+static int
+aml_bufferfield_io(int io, u_int32_t *valuep, u_int8_t *origin,
+ u_int32_t bitoffset, u_int32_t bitlen)
+{
+ u_int8_t val, tmp, masklow, maskhigh;
+ u_int8_t offsetlow, offsethigh;
+ u_int8_t *addr;
+ int i;
+ u_int32_t value, readval;
+ u_int32_t byteoffset, bytelen;
+
+ masklow = maskhigh = 0xff;
+ val = readval = 0;
+ value = *valuep;
+
+ byteoffset = bitoffset / 8;
+ bytelen = bitlen / 8 + ((bitlen % 8) ? 1 : 0);
+ addr = origin + byteoffset;
+
+ /* simple I/O ? */
+ if (bitlen <= 8 || bitlen == 16 || bitlen == 32) {
+ bcopy(addr, &readval, bytelen);
+ AML_DEBUGPRINT("\n\t[bufferfield:0x%x@%p:%d,%d]",
+ readval, addr, bitoffset % 8, bitlen);
+ switch (io) {
+ case AML_BUFFER_INPUT:
+ value = aml_adjust_readvalue(AML_FIELDFLAGS_ACCESS_BYTEACC,
+ bitoffset % 8, bitlen, readval);
+ *valuep = value;
+ AML_DEBUGPRINT("\n[read(bufferfield, %p)&mask:0x%x]\n",
+ addr, value);
+ break;
+ case AML_BUFFER_OUTPUT:
+ value = aml_adjust_updatevalue(AML_FIELDFLAGS_ACCESS_BYTEACC,
+ bitoffset % 8, bitlen, readval, value);
+ bcopy(&value, addr, bytelen);
+ AML_DEBUGPRINT("->[bufferfield:0x%x@%p:%d,%d]",
+ value, addr, bitoffset % 8, bitlen);
+ break;
+ }
+ goto out;
+ }
+
+ offsetlow = bitoffset % 8;
+ if (bytelen > 1) {
+ offsethigh = (bitlen - (8 - offsetlow)) % 8;
+ } else {
+ offsethigh = 0;
+ }
+
+ if (offsetlow) {
+ masklow = (~((1 << bitlen) - 1) << offsetlow) | ~(0xff << offsetlow);
+ AML_DEBUGPRINT("\t[offsetlow = 0x%x, masklow = 0x%x, ~masklow = 0x%x]\n",
+ offsetlow, masklow, ~masklow & 0xff);
+ }
+ if (offsethigh) {
+ maskhigh = 0xff << offsethigh;
+ AML_DEBUGPRINT("\t[offsethigh = 0x%x, maskhigh = 0x%x, ~maskhigh = 0x%x]\n",
+ offsethigh, maskhigh, ~maskhigh & 0xff);
+ }
+ for (i = bytelen; i > 0; i--, addr++) {
+ val = *addr;
+
+ AML_DEBUGPRINT("\t[bufferfield:0x%02x@%p]", val, addr);
+
+ switch (io) {
+ case AML_BUFFER_INPUT:
+ tmp = val;
+ /* the lowest byte? */
+ if (i == bytelen) {
+ if (offsetlow) {
+ readval = tmp & ~masklow;
+ } else {
+ readval = tmp;
+ }
+ } else {
+ if (i == 1 && offsethigh) {
+ tmp = tmp & ~maskhigh;
+ }
+ readval = (tmp << (8 * (bytelen - i))) | readval;
+ }
+
+ AML_DEBUGPRINT("\n");
+ /* goto to next byte... */
+ if (i > 1) {
+ continue;
+ }
+ /* final adjustment before finishing region access */
+ if (offsetlow) {
+ readval = readval >> offsetlow;
+ }
+ AML_DEBUGPRINT("[read(bufferfield, %p)&mask:0x%x]\n",
+ addr, readval);
+ *valuep = readval;
+
+ break;
+
+ case AML_BUFFER_OUTPUT:
+ tmp = value & 0xff;
+ /* the lowest byte? */
+ if (i == bytelen) {
+ if (offsetlow) {
+ tmp = (val & masklow) | tmp << offsetlow;
+ }
+ value = value >> (8 - offsetlow);
+ } else {
+ if (i == 1 && offsethigh) {
+ tmp = (val & maskhigh) | tmp;
+ }
+ value = value >> 8;
+ }
+
+ AML_DEBUGPRINT("->[bufferfield:0x%02x@%p]\n",
+ tmp, addr);
+ *addr = tmp;
+ }
+ }
+out:
+ return (0);
+}
+
+u_int32_t
+aml_bufferfield_read(u_int8_t *origin, u_int32_t bitoffset,
+ u_int32_t bitlen)
+{
+ int value;
+
+ value = 0;
+ aml_bufferfield_io(AML_BUFFER_INPUT, &value, origin,
+ bitoffset, bitlen);
+ return (value);
+}
+
+int
+aml_bufferfield_write(u_int32_t value, u_int8_t *origin,
+ u_int32_t bitoffset, u_int32_t bitlen)
+{
+ int status;
+
+ status = aml_bufferfield_io(AML_BUFFER_OUTPUT, &value,
+ origin, bitoffset, bitlen);
+ return (status);
+}
+
+int
+aml_region_handle_alloc(struct aml_environ *env, int regtype, u_int32_t flags,
+ u_int32_t baseaddr, u_int32_t bitoffset, u_int32_t bitlen,
+ struct aml_region_handle *h)
+{
+ int state;
+ struct aml_name *pci_info;
+
+ state = 0;
+ pci_info = NULL;
+ bzero(h, sizeof(struct aml_region_handle));
+
+ h->env = env;
+ h->regtype = regtype;
+ h->flags = flags;
+ h->baseaddr = baseaddr;
+ h->bitoffset = bitoffset;
+ h->bitlen = bitlen;
+
+ switch (AML_FIELDFLAGS_ACCESSTYPE(flags)) {
+ case AML_FIELDFLAGS_ACCESS_ANYACC:
+ if (bitlen <= 8) {
+ h->unit = 1;
+ break;
+ }
+ if (bitlen <= 16) {
+ h->unit = 2;
+ break;
+ }
+ h->unit = 4;
+ break;
+ case AML_FIELDFLAGS_ACCESS_BYTEACC:
+ h->unit = 1;
+ break;
+ case AML_FIELDFLAGS_ACCESS_WORDACC:
+ h->unit = 2;
+ break;
+ case AML_FIELDFLAGS_ACCESS_DWORDACC:
+ h->unit = 4;
+ break;
+ default:
+ h->unit = 1;
+ break;
+ }
+
+ h->addr = baseaddr + h->unit * ((bitoffset / 8) / h->unit);
+ h->bytelen = baseaddr + ((bitoffset + bitlen) / 8) - h->addr +
+ ((bitlen % 8) ? 1 : 0);
+
+#ifdef _KERNEL
+ switch (h->regtype) {
+ case AML_REGION_SYSMEM:
+ OsdMapMemory((void *)h->addr, h->bytelen, (void **)&h->vaddr);
+ break;
+
+ case AML_REGION_PCICFG:
+ /* Obtain PCI bus number */
+ pci_info = aml_search_name(env, "_BBN");
+ if (pci_info == NULL || pci_info->property->type != aml_t_num) {
+ AML_DEBUGPRINT("Cannot locate _BBN. Using default 0\n");
+ h->pci_bus = 0;
+ } else {
+ AML_DEBUGPRINT("found _BBN: %d\n",
+ pci_info->property->num.number);
+ h->pci_bus = pci_info->property->num.number & 0xff;
+ }
+
+ /* Obtain device & function number */
+ pci_info = aml_search_name(env, "_ADR");
+ if (pci_info == NULL || pci_info->property->type != aml_t_num) {
+ printf("Cannot locate: _ADR\n");
+ state = -1;
+ goto out;
+ }
+ h->pci_devfunc = pci_info->property->num.number;
+
+ AML_DEBUGPRINT("[pci%d.%d]", h->pci_bus, h->pci_devfunc);
+ break;
+
+ default:
+ break;
+ }
+
+out:
+#endif /* _KERNEL */
+ return (state);
+}
+
+void
+aml_region_handle_free(struct aml_region_handle *h)
+{
+#ifdef _KERNEL
+ switch (h->regtype) {
+ case AML_REGION_SYSMEM:
+ OsdUnMapMemory((void *)h->vaddr, h->bytelen);
+ break;
+
+ default:
+ break;
+ }
+#endif /* _KERNEL */
+}
+
+static int
+aml_region_io_simple(struct aml_environ *env, int io, int regtype,
+ u_int32_t flags, u_int32_t *valuep, u_int32_t baseaddr,
+ u_int32_t bitoffset, u_int32_t bitlen)
+{
+ int i, state;
+ u_int32_t readval, value, offset, bytelen;
+ struct aml_region_handle handle;
+
+ state = aml_region_handle_alloc(env, regtype, flags,
+ baseaddr, bitoffset, bitlen, &handle);
+ if (state == -1) {
+ goto out;
+ }
+
+ readval = 0;
+ offset = bitoffset % (handle.unit * 8);
+ /* limitation of 32 bits alignment */
+ bytelen = (handle.bytelen > 4) ? 4 : handle.bytelen;
+
+ if (io == AML_REGION_INPUT ||
+ AML_FIELDFLAGS_UPDATERULE(flags) == AML_FIELDFLAGS_UPDATE_PRESERVE) {
+ for (i = 0; i < bytelen; i += handle.unit) {
+ state = aml_region_read_simple(&handle, i, &value);
+ if (state == -1) {
+ goto out;
+ }
+ readval |= (value << (i * 8));
+ }
+ AML_DEBUGPRINT("\t[%d:0x%x@0x%x:%d,%d]",
+ regtype, readval, handle.addr, offset, bitlen);
+ }
+
+ switch (io) {
+ case AML_REGION_INPUT:
+ AML_DEBUGPRINT("\n");
+ readval = aml_adjust_readvalue(flags, offset, bitlen, readval);
+ value = readval;
+ value = aml_region_prompt_read(&handle, value);
+ state = aml_region_prompt_update_value(readval, value, &handle);
+ if (state == -1) {
+ goto out;
+ }
+
+ *valuep = value;
+ break;
+ case AML_REGION_OUTPUT:
+ value = *valuep;
+ value = aml_adjust_updatevalue(flags, offset,
+ bitlen, readval, value);
+ value = aml_region_prompt_write(&handle, value);
+ AML_DEBUGPRINT("\t->[%d:0x%x@0x%x:%d,%d]\n", regtype, value,
+ handle.addr, offset, bitlen);
+ for (i = 0; i < bytelen; i += handle.unit) {
+ state = aml_region_write_simple(&handle, i, value);
+ if (state == -1) {
+ goto out;
+ }
+ value = value >> (handle.unit * 8);
+ }
+ break;
+ }
+
+ aml_region_handle_free(&handle);
+out:
+ return (state);
+}
+
+int
+aml_region_io(struct aml_environ *env, int io, int regtype,
+ u_int32_t flags, u_int32_t *valuep, u_int32_t baseaddr,
+ u_int32_t bitoffset, u_int32_t bitlen)
+{
+ u_int32_t unit, offset;
+ u_int32_t offadj, bitadj;
+ u_int32_t value, readval;
+ int state, i;
+
+ readval = 0;
+ state = 0;
+ unit = 4; /* limitation of 32 bits alignment */
+ offset = bitoffset % (unit * 8);
+ offadj = 0;
+ bitadj = 0;
+ if (offset + bitlen > unit * 8) {
+ bitadj = bitlen - (unit * 8 - offset);
+ }
+ for (i = 0; i < offset + bitlen; i += unit * 8) {
+ value = (*valuep) >> offadj;
+ state = aml_region_io_simple(env, io, regtype, flags,
+ &value, baseaddr, bitoffset + offadj, bitlen - bitadj);
+ if (state == -1) {
+ goto out;
+ }
+ readval |= value << offadj;
+ bitadj = offadj = bitlen - bitadj;
+ }
+ *valuep = readval;
+
+out:
+ return (state);
+}
diff --git a/usr.sbin/acpi/amldb/aml/aml_common.h b/usr.sbin/acpi/amldb/aml/aml_common.h
new file mode 100644
index 0000000..00f1116
--- /dev/null
+++ b/usr.sbin/acpi/amldb/aml/aml_common.h
@@ -0,0 +1,159 @@
+/*-
+ * Copyright (c) 2000 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: aml_common.h,v 1.4 2000/08/08 14:12:05 iwasaki Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _AML_COMMON_H_
+#define _AML_COMMON_H_
+
+/*
+ * General Stuff
+ */
+#ifdef _KERNEL
+#define AML_SYSABORT() do { \
+ printf("aml: fatal errer at %s:%d\n", __FILE__, __LINE__); \
+ panic("panic in AML interpreter!"); \
+} while(0)
+#define AML_SYSASSERT(x) do { \
+ if (!(x)) { \
+ AML_SYSABORT(); \
+ } \
+} while(0)
+#define AML_SYSERRX(eval, fmt, args...) do { \
+ printf(fmt, args); \
+} while(0)
+#define AML_DEBUGGER(x, y) /* no debugger in kernel */
+#define AML_STALL(micro) OsdSleepUsec(micro)
+#define AML_SLEEP(sec, milli) OsdSleep(sec, milli)
+#else /* !_KERNEL */
+#define AML_SYSASSERT(x) assert(x)
+#define AML_SYSABORT() abort()
+#define AML_SYSERRX(eval, fmt, args...) errx(eval, fmt, args)
+#define AML_DEBUGGER(x, y) aml_dbgr(x, y)
+#define AML_STALL(micro) /* not required in userland */
+#define AML_SLEEP(sec, milli) /* not required in userland */
+#endif /* _KERNEL */
+
+union aml_object;
+struct aml_name;
+
+extern int aml_debug;
+
+#define AML_DEBUGPRINT(args...) do { \
+ if (aml_debug) { \
+ printf(args); \
+ } \
+} while(0)
+
+void aml_showobject(union aml_object *);
+void aml_showtree(struct aml_name *, int);
+int aml_print_curname(struct aml_name *);
+void aml_print_namestring(u_int8_t *);
+void aml_print_indent(int);
+
+/*
+ * Reigion I/O Stuff for both kernel/userland.
+ */
+
+/*
+ * Field Flags
+ */
+/* bit 0 -3: AccessType */
+#define AML_FIELDFLAGS_ACCESS_ANYACC 0x00
+#define AML_FIELDFLAGS_ACCESS_BYTEACC 0x01
+#define AML_FIELDFLAGS_ACCESS_WORDACC 0x02
+#define AML_FIELDFLAGS_ACCESS_DWORDACC 0x03
+#define AML_FIELDFLAGS_ACCESS_BLOCKACC 0x04
+#define AML_FIELDFLAGS_ACCESS_SMBSENDRECVACC 0x05
+#define AML_FIELDFLAGS_ACCESS_SMBQUICKACC 0x06
+#define AML_FIELDFLAGS_ACCESSTYPE(flags) (flags & 0x0f)
+/* bit 4: LockRule */
+#define AML_FIELDFLAGS_LOCK_NOLOCK 0x00
+#define AML_FIELDFLAGS_LOCK_LOCK 0x10
+#define AML_FIELDFLAGS_LOCKRULE(flags) (flags & 0x10)
+/* bit 5 - 6: UpdateRule */
+#define AML_FIELDFLAGS_UPDATE_PRESERVE 0x00
+#define AML_FIELDFLAGS_UPDATE_WRITEASONES 0x20
+#define AML_FIELDFLAGS_UPDATE_WRITEASZEROS 0x40
+#define AML_FIELDFLAGS_UPDATERULE(flags) (flags & 0x60)
+/* bit 7: reserved (must be 0) */
+
+#define AML_REGION_INPUT 0
+#define AML_REGION_OUTPUT 1
+
+#define AML_REGION_SYSMEM 0
+#define AML_REGION_SYSIO 1
+#define AML_REGION_PCICFG 2
+#define AML_REGION_EMBCTL 3
+#define AML_REGION_SMBUS 4
+
+struct aml_region_handle {
+ /* These are copies of values used on initialization */
+ struct aml_environ *env;
+ int regtype;
+ u_int32_t flags;
+ u_int32_t baseaddr;
+ u_int32_t bitoffset;
+ u_int32_t bitlen;
+
+ /* following is determined on initialization */
+ vm_offset_t addr, bytelen;
+ u_int32_t unit; /* access unit in bytes */
+
+ /* region type dependant */
+ vm_offset_t vaddr; /* SystemMemory */
+ u_int32_t pci_bus, pci_devfunc; /* PCI_Config */
+};
+
+u_int32_t aml_adjust_readvalue(u_int32_t, u_int32_t, u_int32_t,
+ u_int32_t);
+u_int32_t aml_adjust_updatevalue(u_int32_t, u_int32_t, u_int32_t,
+ u_int32_t, u_int32_t);
+
+u_int32_t aml_bufferfield_read(u_int8_t *, u_int32_t, u_int32_t);
+int aml_bufferfield_write(u_int32_t, u_int8_t *,
+ u_int32_t, u_int32_t);
+
+int aml_region_handle_alloc(struct aml_environ *, int, u_int32_t,
+ u_int32_t, u_int32_t, u_int32_t,
+ struct aml_region_handle *);
+void aml_region_handle_free(struct aml_region_handle *);
+
+int aml_region_io(struct aml_environ *, int, int,
+ u_int32_t, u_int32_t *, u_int32_t,
+ u_int32_t, u_int32_t);
+extern int aml_region_read_simple(struct aml_region_handle *, vm_offset_t,
+ u_int32_t *);
+extern int aml_region_write_simple(struct aml_region_handle *, vm_offset_t,
+ u_int32_t);
+extern u_int32_t aml_region_prompt_read(struct aml_region_handle *,
+ u_int32_t);
+extern u_int32_t aml_region_prompt_write(struct aml_region_handle *,
+ u_int32_t);
+extern int aml_region_prompt_update_value(u_int32_t, u_int32_t,
+ struct aml_region_handle *);
+#endif /* !_AML_COMMON_H_ */
diff --git a/usr.sbin/acpi/amldb/aml/aml_env.h b/usr.sbin/acpi/amldb/aml/aml_env.h
new file mode 100644
index 0000000..f155f2d
--- /dev/null
+++ b/usr.sbin/acpi/amldb/aml/aml_env.h
@@ -0,0 +1,46 @@
+/*-
+ * Copyright (c) 1999 Takanori Watanabe
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: aml_env.h,v 1.11 2000/08/08 14:12:05 iwasaki Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _AML_ENV_H_
+#define _AML_ENV_H_
+
+#include <aml/aml_name.h>
+#include <aml/aml_obj.h>
+#include <aml/aml_status.h>
+
+struct aml_environ {
+ u_int8_t *dp;
+ u_int8_t *end;
+ enum aml_status stat;
+ struct aml_name *curname;
+ struct aml_name tempname;
+ union aml_object tempobject;
+};
+
+#endif /* !_AML_ENV_H_ */
diff --git a/usr.sbin/acpi/amldb/aml/aml_evalobj.c b/usr.sbin/acpi/amldb/aml/aml_evalobj.c
new file mode 100644
index 0000000..83f7cd2
--- /dev/null
+++ b/usr.sbin/acpi/amldb/aml/aml_evalobj.c
@@ -0,0 +1,436 @@
+/*-
+ * Copyright (c) 1999 Takanori Watanabe
+ * Copyright (c) 1999, 2000 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: aml_evalobj.c,v 1.27 2000/08/16 18:14:53 iwasaki Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+
+#include <aml/aml_amlmem.h>
+#include <aml/aml_common.h>
+#include <aml/aml_env.h>
+#include <aml/aml_evalobj.h>
+#include <aml/aml_name.h>
+#include <aml/aml_obj.h>
+#include <aml/aml_parse.h>
+#include <aml/aml_region.h>
+#include <aml/aml_status.h>
+#include <aml/aml_store.h>
+
+#ifndef _KERNEL
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include <assert.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "debug.h"
+#else /* _KERNEL */
+#include <sys/systm.h>
+#endif /* !_KERNEL */
+
+static union aml_object *aml_eval_fieldobject(struct aml_environ *env,
+ struct aml_name *name);
+
+static union aml_object *
+aml_eval_fieldobject(struct aml_environ *env, struct aml_name *name)
+{
+ int num;
+ struct aml_name *oname,*wname;
+ struct aml_field *field;
+ struct aml_opregion *or;
+ union aml_object tobj;
+
+ num = 0;
+ /* CANNOT OCCUR! */
+ if (name == NULL || name->property == NULL ||
+ name->property->type != aml_t_field) {
+ printf("????\n");
+ env->stat = aml_stat_panic;
+ return (NULL);
+ }
+ field = &name->property->field;
+ oname = env->curname;
+ if (field->bitlen > 32) {
+ env->tempobject.type = aml_t_regfield;
+ } else {
+ env->tempobject.type = aml_t_num;
+ }
+ env->curname = name;
+ if (field->f.ftype == f_t_field) {
+ wname = aml_search_name(env, field->f.fld.regname);
+ if (wname == NULL || wname->property == NULL ||
+ wname->property->type != aml_t_opregion) {
+ AML_DEBUGPRINT("Inappropreate Type\n");
+ env->stat = aml_stat_panic;
+ env->curname = oname;
+ return (NULL);
+ }
+ or = &wname->property->opregion;
+ if (env->tempobject.type == aml_t_regfield) {
+ env->tempobject.regfield.space = or->space;
+ env->tempobject.regfield.flags = field->flags;
+ env->tempobject.regfield.offset = or->offset;
+ env->tempobject.regfield.bitoffset = field->bitoffset;
+ env->tempobject.regfield.bitlen = field->bitlen;
+ } else {
+ env->tempobject.type = aml_t_num;
+ env->tempobject.num.number = aml_region_read(env,
+ or->space, field->flags, or->offset,
+ field->bitoffset, field->bitlen);
+ AML_DEBUGPRINT("[read(%d, 0x%x)->0x%x]",
+ or->space, or->offset + field->bitoffset / 8,
+ env->tempobject.num.number);
+ }
+ } else if (field->f.ftype == f_t_index) {
+ wname = aml_search_name(env, field->f.ifld.indexname);
+ tobj.type = aml_t_num;
+ tobj.num.number = field->bitoffset / 8;/* AccessType Boundary */
+ aml_store_to_name(env, &tobj, wname);
+ wname = aml_search_name(env, field->f.ifld.dataname);
+ num = aml_objtonum(env, aml_eval_name(env, wname));
+ env->tempobject.type = aml_t_num;
+ env->tempobject.num.number = (num >> (field->bitoffset & 7)) &
+ ((1 << field->bitlen) - 1);
+ }
+ env->curname = oname;
+ return (&env->tempobject);
+}
+
+union aml_object *
+aml_eval_objref(struct aml_environ *env, union aml_object *obj)
+{
+ int offset;
+ union aml_object num1;
+ union aml_object *ref, *ret;
+
+ ret = obj;
+ if (obj->objref.deref == 1) {
+ num1.type = aml_t_num;
+ offset = obj->objref.offset;
+ ref = obj->objref.ref;
+ if (ref == NULL) {
+ goto out;
+ }
+ switch (ref->type) {
+ case aml_t_package:
+ if (ref->package.elements > offset) {
+ ret = ref->package.objects[offset];
+ } else {
+ num1.num.number = 0;
+ env->tempobject = num1;
+ ret = &env->tempobject;
+ }
+ break;
+ case aml_t_buffer:
+ if (ref->buffer.size > offset) {
+ num1.num.number = ref->buffer.data[offset] & 0xff;
+ } else {
+ num1.num.number = 0;
+ }
+ env->tempobject = num1;
+ ret = &env->tempobject;
+ break;
+ default:
+ break;
+ }
+ }
+ if (obj->objref.alias == 1) {
+ ret = aml_eval_name(env, obj->objref.nameref);
+ goto out;
+ }
+out:
+ return (ret);
+}
+
+/*
+ * Eval named object.
+ */
+union aml_object *
+aml_eval_name(struct aml_environ *env, struct aml_name *aname)
+{
+ int argnum, i;
+ int num;
+ struct aml_name *tmp;
+ struct aml_environ *copy;
+ struct aml_local_stack *stack;
+ union aml_object *obj, *ret;
+ union aml_object *src;
+
+ ret = NULL;
+ if (aname == NULL || aname->property == NULL) {
+ return (NULL);
+ }
+ if (env->stat == aml_stat_panic) {
+ return (NULL);
+ }
+ copy = memman_alloc(aml_memman, memid_aml_environ);
+ if (copy == NULL) {
+ return (NULL);
+ }
+ ret = aname->property;
+ i = 0;
+reevaluate:
+ if (i > 10) {
+ env->stat = aml_stat_panic;
+ printf("TOO MANY LOOP\n");
+ ret = NULL;
+ goto out;
+ }
+ switch (aname->property->type) {
+ case aml_t_namestr:
+ tmp = aname;
+ aname = aml_search_name(env, aname->property->nstr.dp);
+ if (aname == NULL) {
+ aname = tmp;
+ }
+ i++;
+ goto reevaluate;
+ case aml_t_objref:
+ ret = aml_eval_objref(env, aname->property);
+ goto out;
+ case aml_t_num:
+ case aml_t_string:
+ case aml_t_buffer:
+ case aml_t_package:
+ case aml_t_debug:
+ ret = aname->property;
+ goto out;
+ case aml_t_field:
+ aml_free_objectcontent(&env->tempobject);
+ ret = aml_eval_fieldobject(env, aname);
+ goto out;
+ case aml_t_method:
+ aml_free_objectcontent(&env->tempobject);
+ argnum = aname->property->meth.argnum & 7;
+ *copy = *env;
+ copy->curname = aname;
+ copy->dp = aname->property->meth.from;
+ copy->end = aname->property->meth.to;
+ copy->stat = aml_stat_none;
+ stack = aml_local_stack_create();
+ AML_DEBUGPRINT("(");
+ for (i = 0; i < argnum; i++) {
+ aml_local_stack_getArgX(stack, i)->property =
+ aml_copy_object(env,
+ aml_eval_name(env,
+ aml_parse_termobj(env, 0)));
+ if (i < argnum - 1)
+ AML_DEBUGPRINT(", ");
+ }
+ AML_DEBUGPRINT(")\n");
+ aml_local_stack_push(stack);
+ if (env->stat == aml_stat_step) {
+ AML_DEBUGGER(env, copy);
+ }
+ tmp = aml_execute_method(copy);
+ obj = aml_eval_name(env, tmp);
+ if (copy->stat == aml_stat_panic) {
+ AML_DEBUGPRINT("PANIC OCCURED IN METHOD");
+ env->stat = aml_stat_panic;
+ ret = NULL;
+ aml_local_stack_delete(aml_local_stack_pop());
+ goto out;
+ }
+ if (aml_debug) {
+ aml_showobject(obj);
+ }
+
+ if (tmp)
+ tmp->property = NULL;
+ aml_local_stack_delete(aml_local_stack_pop());
+ if (obj) {
+ aml_create_local_object()->property = obj;
+ ret = obj;
+ } else {
+ env->tempobject.type = aml_t_num;
+ env->tempobject.num.number = 0;
+ }
+
+ goto out;
+ case aml_t_bufferfield:
+ aml_free_objectcontent(&env->tempobject);
+ if (aname->property->bfld.bitlen > 32) {
+ ret = aname->property;
+ } else {
+ src = aname->property;
+ num = aml_bufferfield_read(src->bfld.origin,
+ src->bfld.bitoffset, src->bfld.bitlen);
+ env->tempobject.type = aml_t_num;
+ env->tempobject.num.number = num;
+ ret = &env->tempobject;
+ }
+ goto out;
+ default:
+ AML_DEBUGPRINT("I eval the object that I should not eval, %s%d",
+ aname->name, aname->property->type);
+ AML_SYSABORT();
+ ret = NULL;
+ goto out;
+ }
+out:
+ memman_free(aml_memman, memid_aml_environ, copy);
+ return (ret);
+}
+
+/*
+ * Eval named object but env variable is not required and return
+ * status of evaluation (success is zero). This function is assumed
+ * to be called by aml_apply_foreach_found_objects().
+ * Note that no arguments are passed if object is a method.
+ */
+
+int
+aml_eval_name_simple(struct aml_name *name, va_list ap)
+{
+ struct aml_environ *env;
+ union aml_object *ret;
+
+ if (name == NULL || name->property == NULL) {
+ return (1);
+ }
+
+ env = memman_alloc(aml_memman, memid_aml_environ);
+ if (env == NULL) {
+ return (1);
+ }
+ bzero(env, sizeof(struct aml_environ));
+
+ aml_local_stack_push(aml_local_stack_create());
+
+ AML_DEBUGPRINT("Evaluating ");
+ aml_print_curname(name);
+ ret = aml_eval_name(env, name);
+ if (name->property->type != aml_t_method) {
+ AML_DEBUGPRINT("\n");
+ if (aml_debug) {
+ aml_showobject(ret);
+ }
+ }
+
+ aml_local_stack_delete(aml_local_stack_pop());
+
+ memman_free(aml_memman, memid_aml_environ, env);
+ return (0);
+}
+
+int
+aml_objtonum(struct aml_environ *env, union aml_object *obj)
+{
+
+ if (obj != NULL && obj->type == aml_t_num) {
+ return (obj->num.number);
+ } else {
+ env->stat = aml_stat_panic;
+ return (-1);
+ }
+}
+
+struct aml_name *
+aml_execute_method(struct aml_environ *env)
+{
+ struct aml_name *name;
+ struct aml_name_group *newgrp;
+
+ newgrp = aml_new_name_group(AML_NAME_GROUP_IN_METHOD);
+
+ AML_DEBUGPRINT("[");
+ aml_print_curname(env->curname);
+ AML_DEBUGPRINT(" START]\n");
+
+ name = aml_parse_objectlist(env, 0);
+ AML_DEBUGPRINT("[");
+ aml_print_curname(env->curname);
+ AML_DEBUGPRINT(" END]\n");
+
+ aml_delete_name_group(newgrp);
+ return (name);
+}
+
+union aml_object *
+aml_invoke_method(struct aml_name *name, int argc, union aml_object *argv)
+{
+ int i;
+ struct aml_name *tmp;
+ struct aml_environ *env;
+ struct aml_local_stack *stack;
+ union aml_object *retval;
+ union aml_object *obj;
+
+ retval = NULL;
+ env = memman_alloc(aml_memman, memid_aml_environ);
+ if (env == NULL) {
+ return (NULL);
+ }
+ bzero(env, sizeof(struct aml_environ));
+
+ if (name != NULL && name->property != NULL &&
+ name->property->type == aml_t_method) {
+ env->curname = name;
+ env->dp = name->property->meth.from;
+ env->end = name->property->meth.to;
+ AML_DEBUGGER(env, env);
+ stack = aml_local_stack_create();
+ for (i = 0; i < argc; i++) {
+ aml_local_stack_getArgX(stack, i)->property =
+ aml_alloc_object(argv[i].type, &argv[i]);
+ }
+ aml_local_stack_push(stack);
+ obj = aml_eval_name(env, tmp = aml_execute_method(env));
+ if (aml_debug) {
+ aml_showtree(name, 0);
+ }
+
+ if (tmp)
+ tmp->property = NULL;
+ aml_local_stack_delete(aml_local_stack_pop());
+ if (obj) {
+ aml_create_local_object()->property = obj;
+ retval = obj;
+ }
+ }
+ memman_free(aml_memman, memid_aml_environ, env);
+ return (retval);
+}
+
+union aml_object *
+aml_invoke_method_by_name(char *method, int argc, union aml_object *argv)
+{
+ struct aml_name *name;
+
+ name = aml_find_from_namespace(aml_get_rootname(), method);
+ if (name == NULL) {
+ return (NULL);
+ }
+
+ return (aml_invoke_method(name, argc, argv));
+}
diff --git a/usr.sbin/acpi/amldb/aml/aml_evalobj.h b/usr.sbin/acpi/amldb/aml/aml_evalobj.h
new file mode 100644
index 0000000..9b3de0a
--- /dev/null
+++ b/usr.sbin/acpi/amldb/aml/aml_evalobj.h
@@ -0,0 +1,48 @@
+/*-
+ * Copyright (c) 1999 Takanori Watanabe
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: aml_evalobj.h,v 1.11 2000/08/16 18:14:53 iwasaki Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _AML_EVALOBJ_H_
+#define _AML_EVALOBJ_H_
+
+#include <machine/stdarg.h>
+
+union aml_object *aml_eval_objref(struct aml_environ *,
+ union aml_object *);
+union aml_object *aml_eval_name(struct aml_environ *,
+ struct aml_name *);
+int aml_eval_name_simple(struct aml_name *, va_list);
+int aml_objtonum(struct aml_environ *,
+ union aml_object *);
+struct aml_name *aml_execute_method(struct aml_environ *);
+union aml_object *aml_invoke_method(struct aml_name *,
+ int, union aml_object *);
+union aml_object *aml_invoke_method_by_name(char *,
+ int, union aml_object *);
+
+#endif /* !_AML_EVALOBJ_H_ */
diff --git a/usr.sbin/acpi/amldb/aml/aml_memman.c b/usr.sbin/acpi/amldb/aml/aml_memman.c
new file mode 100644
index 0000000..46e6aa7
--- /dev/null
+++ b/usr.sbin/acpi/amldb/aml/aml_memman.c
@@ -0,0 +1,476 @@
+/*-
+ * Copyright (c) 1999, 2000 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: aml_memman.c,v 1.10 2000/08/09 14:47:43 iwasaki Exp $
+ * $FreeBSD$
+ */
+
+/*
+ * Generic Memory Management
+ */
+
+#include <sys/param.h>
+
+#include <aml/aml_memman.h>
+
+#ifndef _KERNEL
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#else /* _KERNEL */
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+MALLOC_DEFINE(M_MEMMAN, "memman", "Generic and Simple Memory Management");
+#endif /* !_KERNEL */
+
+unsigned int memid_unkown = 255;
+
+static int manage_block(struct memman *memman, unsigned int id,
+ void *block, unsigned static_mem,
+ unsigned entries);
+static int blockman_init(struct memman *memman, unsigned int id);
+static void memman_flexsize_add_histogram(struct memman *memman,
+ size_t size,
+ int tolerance);
+static int memman_comp_histogram_size(const void *a,
+ const void *b);
+static void memman_sort_histogram_by_size(struct memman *memman);
+static unsigned int memman_guess_memid(struct memman *memman, void *chunk);
+static void memman_statistics_fixedsize(struct memman *memman);
+static void memman_statistics_flexsize(struct memman *memman);
+
+static int
+manage_block(struct memman *memman, unsigned int id, void *block,
+ unsigned static_mem, unsigned entries)
+{
+ unsigned int i;
+ size_t alloc_size;
+ void *tmp, *realblock;
+ struct memman_blockman *bmp;
+ struct memman_block *memblock;
+ struct memman_node *memnodes;
+
+ bmp = &memman->blockman[id];
+ alloc_size = MEMMAN_BLOCKNODE_SIZE(entries);
+
+ if (static_mem) {
+ tmp = (void *)block;
+ realblock = (char *)block + alloc_size;
+ } else {
+ tmp = MEMMAN_SYSMALLOC(alloc_size);
+ if (!tmp) {
+ return (-1);
+ }
+ realblock = block;
+
+ memman->allocated_mem += alloc_size;
+ memman->salloc_called++;
+ }
+
+ memblock = (struct memman_block *)tmp;
+ memnodes = (struct memman_node *)((char *)tmp + sizeof(struct memman_block));
+
+ memblock->block = realblock;
+ memblock->static_mem = static_mem;
+ memblock->allocated = entries;
+ memblock->available = entries;
+ if (!static_mem) {
+ alloc_size += roundup(bmp->size * entries, ROUNDUP_UNIT);
+ }
+ memblock->allocated_mem = alloc_size;
+ LIST_INSERT_HEAD(&bmp->block_list, memblock, links);
+
+ for (i = 0; i < entries; ++i) {
+ memnodes[i].node = ((char *)realblock + (i * (bmp->size)));
+ memnodes[i].memblock = memblock;
+ LIST_INSERT_HEAD(&bmp->free_node_list, &memnodes[i], links);
+ }
+ bmp->available = entries;
+
+ return (0);
+}
+
+static int
+blockman_init(struct memman *memman, unsigned int id)
+{
+ int status;
+ struct memman_blockman *bmp;
+
+ bmp = &memman->blockman[id];
+ bmp->initialized = 1;
+ LIST_INIT(&bmp->block_list);
+ LIST_INIT(&bmp->free_node_list);
+ LIST_INIT(&bmp->occupied_node_list);
+ status = manage_block(memman, id, bmp->initial_block,
+ 1, MEMMAN_INITIAL_SIZE);
+ return (status);
+}
+
+void *
+memman_alloc(struct memman *memman, unsigned int id)
+{
+ size_t alloc_size;
+ void *chunk, *block;
+ struct memman_blockman *bmp;
+ struct memman_node *memnode;
+
+ if (memman->max_memid <= id) {
+ printf("memman_alloc: invalid memory type id\n");
+ return (NULL);
+ }
+ bmp = &memman->blockman[id];
+ if (!bmp->initialized) {
+ if (blockman_init(memman, id)) {
+ goto malloc_fail;
+ }
+ }
+ memman->alloc_called++;
+
+ if (bmp->available == 0) {
+ alloc_size = roundup(bmp->size * MEMMAN_INCR_SIZE,
+ ROUNDUP_UNIT);
+ block = MEMMAN_SYSMALLOC(alloc_size);
+ if (!block) {
+ goto malloc_fail;
+ }
+ memman->required_mem += bmp->size * MEMMAN_INCR_SIZE;
+ memman->allocated_mem += alloc_size;
+ memman->salloc_called++;
+
+ if (manage_block(memman, id, block, 0, MEMMAN_INCR_SIZE)) {
+ goto malloc_fail;
+ }
+ }
+ memnode = LIST_FIRST(&bmp->free_node_list);
+ LIST_REMOVE(memnode, links);
+ chunk = memnode->node;
+ LIST_INSERT_HEAD(&bmp->occupied_node_list, memnode, links);
+ memnode->memblock->available--;
+ bmp->available--;
+
+ return (chunk);
+
+malloc_fail:
+ printf("memman_alloc: could not allocate memory\n");
+ return (NULL);
+}
+
+static void
+memman_flexsize_add_histogram(struct memman *memman, size_t size,
+ int tolerance)
+{
+ int i;
+ int gap;
+
+ if (size == 0) {
+ return;
+ }
+ for (i = 0; i < memman->flex_mem_histogram_ptr; i++) {
+ gap = memman->flex_mem_histogram[i].mem_size - size;
+ if (gap >= (tolerance * -1) && gap <= tolerance) {
+ memman->flex_mem_histogram[i].count++;
+ if (memman->flex_mem_histogram[i].mem_size < size) {
+ memman->flex_mem_histogram[i].mem_size = size;
+ }
+ return;
+ }
+ }
+
+ if (memman->flex_mem_histogram_ptr == MEMMAN_HISTOGRAM_SIZE) {
+ memman_flexsize_add_histogram(memman, size, tolerance + 1);
+ return;
+ }
+ i = memman->flex_mem_histogram_ptr;
+ memman->flex_mem_histogram[i].mem_size = size;
+ memman->flex_mem_histogram[i].count = 1;
+ memman->flex_mem_histogram_ptr++;
+}
+
+static int
+memman_comp_histogram_size(const void *a, const void *b)
+{
+ int delta;
+
+ delta = ((const struct memman_histogram *)a)->mem_size -
+ ((const struct memman_histogram *)b)->mem_size;
+ return (delta);
+}
+
+static void
+memman_sort_histogram_by_size(struct memman *memman)
+{
+ qsort(memman->flex_mem_histogram, memman->flex_mem_histogram_ptr,
+ sizeof(struct memman_histogram), memman_comp_histogram_size);
+}
+
+void *
+memman_alloc_flexsize(struct memman *memman, size_t size)
+{
+ void *mem;
+ struct memman_flexmem_info *info;
+
+ if (size == 0) {
+ return (NULL);
+ }
+ if ((mem = MEMMAN_SYSMALLOC(size)) != NULL) { /* XXX */
+
+ info = MEMMAN_SYSMALLOC(sizeof(struct memman_flexmem_info));
+ if (info) {
+ if (!memman->flex_mem_initialized) {
+ LIST_INIT(&memman->flexmem_info_list);
+ bzero(memman->flex_mem_histogram,
+ sizeof(struct memman_histogram));
+ memman->flex_mem_initialized = 1;
+ }
+ info->addr = mem;
+ info->mem_size = size;
+ LIST_INSERT_HEAD(&memman->flexmem_info_list, info, links);
+ }
+ memman->flex_alloc_called++;
+ memman->flex_salloc_called++;
+ memman->flex_required_mem += size;
+ memman->flex_allocated_mem += size;
+ if (memman->flex_mem_size_min == 0 ||
+ memman->flex_mem_size_min > size) {
+ memman->flex_mem_size_min = size;
+ }
+ if (memman->flex_mem_size_max < size) {
+ memman->flex_mem_size_max = size;
+ }
+ if (memman->flex_peak_mem_usage <
+ (memman->flex_allocated_mem - memman->flex_reclaimed_mem)) {
+ memman->flex_peak_mem_usage =
+ (memman->flex_allocated_mem - memman->flex_reclaimed_mem);
+ }
+ memman_flexsize_add_histogram(memman, size,
+ memman->flex_mem_histogram_initial_tolerance);
+ }
+ return (mem);
+}
+
+static unsigned int
+memman_guess_memid(struct memman *memman, void *chunk)
+{
+ unsigned int id;
+ struct memman_blockman *bmp;
+ struct memman_node *memnode;
+
+ for (id = 0; id < memman->max_memid; id++) {
+ bmp = &memman->blockman[id];
+ if (!bmp->initialized) {
+ if (blockman_init(memman, id)) {
+ printf("memman_free: could not initialized\n");
+ }
+ }
+ LIST_FOREACH(memnode, &bmp->occupied_node_list, links) {
+ if (memnode->node == chunk) {
+ return (id); /* got it! */
+ }
+ }
+ }
+ return (memid_unkown); /* gave up */
+}
+
+void
+memman_free(struct memman *memman, unsigned int memid, void *chunk)
+{
+ unsigned int id;
+ unsigned found;
+ void *block;
+ struct memman_blockman *bmp;
+ struct memman_block *memblock;
+ struct memman_node *memnode;
+
+ id = memid;
+ if (memid == memid_unkown) {
+ id = memman_guess_memid(memman, chunk);
+ }
+ if (memman->max_memid <= id) {
+ printf("memman_free: invalid memory type id\n");
+ MEMMAN_SYSABORT();
+ return;
+ }
+ bmp = &memman->blockman[id];
+ if (!bmp->initialized) {
+ if (blockman_init(memman, id)) {
+ printf("memman_free: could not initialized\n");
+ }
+ }
+ found = 0;
+ LIST_FOREACH(memnode, &bmp->occupied_node_list, links) {
+ if (memnode->node == chunk) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ printf("memman_free: invalid address\n");
+ return;
+ }
+ memman->free_called++;
+
+ LIST_REMOVE(memnode, links);
+ memblock = memnode->memblock;
+ memblock->available++;
+ LIST_INSERT_HEAD(&bmp->free_node_list, memnode, links);
+ bmp->available++;
+
+ if (!memblock->static_mem &&
+ memblock->available == memblock->allocated) {
+ LIST_FOREACH(memnode, &bmp->free_node_list, links) {
+ if (memnode->memblock != memblock) {
+ continue;
+ }
+ LIST_REMOVE(memnode, links);
+ bmp->available--;
+ }
+ block = memblock->block;
+ MEMMAN_SYSFREE(block);
+ memman->sfree_called++;
+
+ LIST_REMOVE(memblock, links);
+ memman->sfree_called++;
+ memman->reclaimed_mem += memblock->allocated_mem;
+ MEMMAN_SYSFREE(memblock);
+ }
+}
+
+void
+memman_free_flexsize(struct memman *memman, void *chunk)
+{
+ struct memman_flexmem_info *info;
+
+ LIST_FOREACH(info, &memman->flexmem_info_list, links) {
+ if (info->addr == chunk) {
+ memman->flex_reclaimed_mem += info->mem_size;
+ LIST_REMOVE(info, links);
+ MEMMAN_SYSFREE(info);
+ break;
+ }
+ }
+ /* XXX */
+ memman->flex_free_called++;
+ memman->flex_sfree_called++;
+ MEMMAN_SYSFREE(chunk);
+}
+
+void
+memman_freeall(struct memman *memman)
+{
+ int id;
+ void *chunk;
+ struct memman_blockman *bmp;
+ struct memman_node *memnode;
+ struct memman_block *memblock;
+ struct memman_flexmem_info *info;
+
+ for (id = 0; id < memman->max_memid; id++) {
+ bmp = &memman->blockman[id];
+
+ while ((memnode = LIST_FIRST(&bmp->occupied_node_list))) {
+ chunk = memnode->node;
+ printf("memman_freeall: fixed size (id = %d)\n", id);
+ memman_free(memman, id, chunk);
+ }
+ while ((memblock = LIST_FIRST(&bmp->block_list))) {
+ LIST_REMOVE(memblock, links);
+ if (!memblock->static_mem) {
+ memman->sfree_called++;
+ memman->reclaimed_mem += memblock->allocated_mem;
+ MEMMAN_SYSFREE(memblock);
+ }
+ }
+ bmp->initialized = 0;
+ }
+
+ LIST_FOREACH(info, &memman->flexmem_info_list, links) {
+ printf("memman_freeall: flex size (size = %d, addr = %p)\n",
+ info->mem_size, info->addr);
+ memman_free_flexsize(memman, info->addr);
+ }
+}
+
+static void
+memman_statistics_fixedsize(struct memman *memman)
+{
+ printf(" fixed size memory blocks\n");
+ printf(" alloc(): %d times\n", memman->alloc_called);
+ printf(" system malloc(): %d times\n", memman->salloc_called);
+ printf(" free(): %d times\n", memman->free_called);
+ printf(" system free(): %d times\n", memman->sfree_called);
+ printf(" required memory: %d bytes\n", memman->required_mem);
+ printf(" allocated memory: %d bytes\n", memman->allocated_mem);
+ printf(" reclaimed memory: %d bytes\n", memman->reclaimed_mem);
+}
+
+static void
+memman_statistics_flexsize(struct memman *memman)
+{
+ int i;
+
+ printf(" flexible size memory blocks\n");
+ printf(" alloc(): %d times\n", memman->flex_alloc_called);
+ printf(" system malloc(): %d times\n", memman->flex_salloc_called);
+ printf(" free(): %d times\n", memman->flex_free_called);
+ printf(" system free(): %d times\n", memman->flex_sfree_called);
+ printf(" required memory: %d bytes\n", memman->flex_required_mem);
+ printf(" allocated memory: %d bytes\n", memman->flex_allocated_mem);
+ printf(" reclaimed memory: %d bytes\n", memman->flex_reclaimed_mem);
+ printf(" peak memory usage: %d bytes\n", memman->flex_peak_mem_usage);
+ printf(" min memory size: %d bytes\n", memman->flex_mem_size_min);
+ printf(" max memory size: %d bytes\n", memman->flex_mem_size_max);
+ printf(" avg memory size: %d bytes\n",
+ (memman->flex_alloc_called) ?
+ memman->flex_allocated_mem / memman->flex_alloc_called : 0);
+
+ printf(" memory size histogram (%d entries):\n",
+ memman->flex_mem_histogram_ptr);
+ printf(" size count\n");
+ memman_sort_histogram_by_size(memman);
+ for (i = 0; i < memman->flex_mem_histogram_ptr; i++) {
+ printf(" %d %d\n",
+ memman->flex_mem_histogram[i].mem_size,
+ memman->flex_mem_histogram[i].count);
+ }
+}
+
+void
+memman_statistics(struct memman *memman)
+{
+ printf("memman: reporting statistics\n");
+ memman_statistics_fixedsize(memman);
+ memman_statistics_flexsize(memman);
+}
+
+size_t
+memman_memid2size(struct memman *memman, unsigned int id)
+{
+ if (memman->max_memid <= id) {
+ printf("memman_alloc: invalid memory type id\n");
+ return (0);
+ }
+ return (memman->blockman[id].size);
+}
diff --git a/usr.sbin/acpi/amldb/aml/aml_memman.h b/usr.sbin/acpi/amldb/aml/aml_memman.h
new file mode 100644
index 0000000..f6806c5
--- /dev/null
+++ b/usr.sbin/acpi/amldb/aml/aml_memman.h
@@ -0,0 +1,172 @@
+/*-
+ * Copyright (c) 1999, 2000 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: aml_memman.h,v 1.9 2000/08/09 14:47:43 iwasaki Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _MEMMAN_H_
+#define _MEMMAN_H_
+
+/*
+ * Generic Memory Management
+ */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+
+/* memory block */
+struct memman_block {
+ LIST_ENTRY(memman_block) links;
+ void *block;
+ unsigned static_mem; /* static memory or not */
+ unsigned int allocated; /* number of allocated chunks */
+ unsigned int available; /* number of available chunks */
+ unsigned int allocated_mem; /* block + misc (in bytes) */
+
+}__attribute__((packed));
+
+LIST_HEAD(memman_block_list, memman_block);
+
+/* memory node in block */
+struct memman_node {
+ LIST_ENTRY(memman_node) links;
+ void *node;
+ struct memman_block *memblock;
+}__attribute__((packed));
+
+LIST_HEAD(memman_node_list, memman_node);
+
+/* memory type id */
+extern unsigned int memid_unkown;
+
+/* memory block manager */
+struct memman_blockman {
+ unsigned int size; /* size of chunk */
+ unsigned int available; /* total # of available chunks */
+ void *initial_block; /* initial memory storage */
+ unsigned initialized; /* initialized or not */
+
+ struct memman_block_list block_list;
+ struct memman_node_list free_node_list;
+ struct memman_node_list occupied_node_list;
+};
+
+/* memory size histogram */
+#define MEMMAN_HISTOGRAM_SIZE 20
+struct memman_histogram {
+ int mem_size;
+ int count;
+};
+
+/* flex size memory allocation info */
+struct memman_flexmem_info {
+ LIST_ENTRY(memman_flexmem_info) links;
+ void *addr;
+ size_t mem_size;
+}__attribute__((packed));
+
+LIST_HEAD(memman_flexmem_info_list, memman_flexmem_info);
+
+/* memory manager */
+struct memman {
+ struct memman_blockman *blockman;
+ unsigned int max_memid; /* max number of valid memid */
+
+ /* fixed size memory blocks */
+ unsigned int alloc_called; /* memman_alloc() calling */
+ unsigned int free_called; /* memman_free() calling */
+ unsigned int salloc_called; /* malloc() calling */
+ unsigned int sfree_called; /* free() calling */
+ size_t required_mem; /* total required memory (in bytes) */
+ size_t allocated_mem; /* total malloc()ed memory */
+ size_t reclaimed_mem; /* total free()ed memory */
+ /* flex size memory blocks */
+ unsigned int flex_alloc_called; /* memman_alloc_flexsize() calling */
+ unsigned int flex_free_called; /* memman_free_flexsize() calling */
+ unsigned int flex_salloc_called;/* malloc() calling */
+ unsigned int flex_sfree_called; /* free() calling */
+ size_t flex_required_mem; /* total required memory (in bytes) */
+ size_t flex_allocated_mem;/* total malloc()ed memory */
+ size_t flex_reclaimed_mem;/* total free()ed memory */
+ size_t flex_mem_size_min; /* min size of allocated memory */
+ size_t flex_mem_size_max; /* max size of allocated memory */
+ size_t flex_peak_mem_usage;/* memory usage at a peak period */
+
+ /* stuff for more detailed statistical information */
+ struct memman_histogram *flex_mem_histogram;
+ unsigned int flex_mem_histogram_ptr;
+ int flex_mem_histogram_initial_tolerance;
+ unsigned flex_mem_initialized;
+ struct memman_flexmem_info_list flexmem_info_list;
+};
+
+#define MEMMAN_BLOCKNODE_SIZE(entries) sizeof(struct memman_block) + \
+ sizeof(struct memman_node) * entries
+
+#ifndef ROUNDUP_UNIT
+#define ROUNDUP_UNIT 4
+#endif
+
+#if !defined(MEMMAN_INITIAL_SIZE) || MEMMAN_INITIAL_SIZE < 2048
+#define MEMMAN_INITIAL_SIZE 2048
+#endif
+
+#if !defined(MEMMAN_INCR_SIZE) || MEMMAN_INCR_SIZE < 512
+#define MEMMAN_INCR_SIZE 512
+#endif
+
+#define MEMMAN_INITIALSTORAGE_DESC(type, name) \
+static struct { \
+ char blocknodes[MEMMAN_BLOCKNODE_SIZE(MEMMAN_INITIAL_SIZE)]; \
+ type realblock[MEMMAN_INITIAL_SIZE]; \
+} name
+
+#define MEMMAN_MEMBLOCK_DESC(size, initial_storage) \
+ { size, MEMMAN_INITIAL_SIZE, &initial_storage, 0 }
+
+#define MEMMAN_MEMMANAGER_DESC(blockman, max_memid, histogram, tolerance) \
+ { blockman, max_memid, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
+ 0, 0, 0, histogram, 0, tolerance, 0}
+
+void *memman_alloc(struct memman *, unsigned int);
+void *memman_alloc_flexsize(struct memman *, size_t);
+void memman_free(struct memman *, unsigned int, void *);
+void memman_free_flexsize(struct memman *, void *);
+void memman_freeall(struct memman *);
+void memman_statistics(struct memman *);
+size_t memman_memid2size(struct memman *, unsigned int);
+
+#ifdef _KERNEL
+#define MEMMAN_SYSMALLOC(size) malloc(size, M_MEMMAN, M_WAITOK)
+#define MEMMAN_SYSFREE(ptr) free(ptr, M_MEMMAN)
+#define MEMMAN_SYSABORT() /* no abort in kernel */
+#else /* !_KERNEL */
+#define MEMMAN_SYSMALLOC(size) malloc(size)
+#define MEMMAN_SYSFREE(ptr) free(ptr)
+#define MEMMAN_SYSABORT() abort()
+#endif /* _KERNEL */
+
+#endif /* !_MEMMAN_H_ */
diff --git a/usr.sbin/acpi/amldb/aml/aml_name.c b/usr.sbin/acpi/amldb/aml/aml_name.c
new file mode 100644
index 0000000..1e07956
--- /dev/null
+++ b/usr.sbin/acpi/amldb/aml/aml_name.c
@@ -0,0 +1,481 @@
+/*-
+ * Copyright (c) 1999 Takanori Watanabe
+ * Copyright (c) 1999, 2000 Yasuo Yokoyama
+ * Copyright (c) 1999, 2000 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: aml_name.c,v 1.15 2000/08/16 18:14:53 iwasaki Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+
+#include <aml/aml_amlmem.h>
+#include <aml/aml_common.h>
+#include <aml/aml_env.h>
+#include <aml/aml_name.h>
+
+#ifndef _KERNEL
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "debug.h"
+#else /* _KERNEL */
+#include <sys/systm.h>
+#endif /* !_KERNEL */
+
+static struct aml_name *aml_find_name(struct aml_name *, char *);
+static struct aml_name *aml_new_name(struct aml_name *, char *);
+static void aml_delete_name(struct aml_name *);
+
+static struct aml_name rootname = {"\\", NULL, NULL, NULL, NULL, NULL};
+
+static struct aml_name_group root_group = {
+ AML_NAME_GROUP_ROOT,
+ &rootname,
+ NULL
+};
+
+struct aml_name_group *name_group_list = &root_group;
+struct aml_local_stack *stack_top = NULL;
+
+struct aml_name *
+aml_get_rootname()
+{
+
+ return (&rootname);
+}
+
+static struct aml_name *
+aml_find_name(struct aml_name *parent, char *name)
+{
+ struct aml_name *result;
+
+ if (!parent)
+ parent = &rootname;
+ for (result = parent->child; result; result = result->brother)
+ if (!strncmp(result->name, name, 4))
+ break;
+ return (result);
+}
+
+/*
+ * Parse given namesppace expression and find a first matched object
+ * under given level of the tree by depth first search.
+ */
+
+struct aml_name *
+aml_find_from_namespace(struct aml_name *parent, char *name)
+{
+ char *ptr;
+ int len;
+ struct aml_name *result;
+
+ ptr = name;
+ if (!parent)
+ parent = &rootname;
+
+ if (ptr[0] == '\\') {
+ ptr++;
+ parent = &rootname;
+ }
+ for (len = 0; ptr[len] != '.' && ptr[len] != '\0'; len++)
+ ;
+
+ for (result = parent->child; result; result = result->brother) {
+ if (!strncmp(result->name, ptr, len)) {
+ if (ptr[len] == '\0' || ptr[len + 1] == '\0') {
+ return (result);
+ }
+ ptr += len;
+ if (ptr[0] != '.') {
+ return (NULL);
+ }
+ ptr++;
+ return (aml_find_from_namespace(result, ptr));
+ }
+ }
+
+ return (NULL);
+}
+
+static void
+_aml_apply_foreach_found_objects(struct aml_name *parent, char *name,
+ int len, int shallow, int (*func)(struct aml_name *, va_list), va_list ap)
+{
+ struct aml_name *child, *ptr;
+
+ child = ptr = NULL;
+
+ /* function to apply must be specified */
+ if (func == NULL) {
+ return;
+ }
+
+ for (child = parent->child; child; child = child->brother) {
+ if (!strncmp(child->name, name, len)) {
+ /* if function call was failed, stop searching */
+ if (func(child, ap) != 0) {
+ return;
+ }
+ }
+ }
+
+ if (shallow == 1) {
+ return;
+ }
+
+ for (ptr = parent->child; ptr; ptr = ptr->brother) {
+ /* do more searching */
+ _aml_apply_foreach_found_objects(ptr, name, len, 0, func, ap);
+ }
+}
+
+/*
+ * Find named objects as many as possible under given level of
+ * namespace, and apply given callback function for each
+ * named objects found. If the callback function returns non-zero
+ * value, then the search terminates immediately.
+ * Note that object name expression is used as forward substring match,
+ * not exact match. The name expression "_L" will match for objects
+ * which have name starting with "_L" such as "\_SB_.LID_._LID" and
+ * "\_GPE._L00" and so on. The name expression can include parent object
+ * name in it like "\_GPE._L". In this case, GPE X level wake handlers
+ * will be found under "\_GPE" in shallow level.
+ */
+
+void
+aml_apply_foreach_found_objects(struct aml_name *start, char *name,
+ int (*func)(struct aml_name *, va_list), ...)
+{
+ int i, len, has_dot, last_is_dot, shallow;
+ struct aml_name *child, *parent;
+ va_list ap;
+
+ shallow = 0;
+ if (start == NULL) {
+ parent = &rootname;
+ } else {
+ parent = start;
+ }
+ if (name[0] == '\\') {
+ name++;
+ parent = &rootname;
+ shallow = 1;
+ }
+
+ len = strlen(name);
+ last_is_dot = 0;
+ /* the last dot should be ignored */
+ if (len > 0 && name[len - 1] == '.') {
+ len--;
+ last_is_dot = 1;
+ }
+
+ has_dot = 0;
+ for (i = 0; i < len - 1; i++) {
+ if (name[i] == '.') {
+ has_dot = 1;
+ break;
+ }
+ }
+
+ /* try to parse expression and find any matched object. */
+ if (has_dot == 1) {
+ child = aml_find_from_namespace(parent, name);
+ if (child == NULL) {
+ return;
+ }
+
+ /*
+ * we have at least one object matched, search all objects
+ * under upper level of the found object.
+ */
+ parent = child->parent;
+
+ /* find the last `.' */
+ for (name = name + len - 1; *name != '.'; name--)
+ ;
+ name++;
+ len = strlen(name) - last_is_dot;
+ shallow = 1;
+ }
+
+ if (len > 4) {
+ return;
+ }
+
+ va_start(ap, func);
+ _aml_apply_foreach_found_objects(parent, name, len, shallow, func, ap);
+ va_end(ap);
+}
+
+struct aml_name_group *
+aml_new_name_group(int id)
+{
+ struct aml_name_group *result;
+
+ result = memman_alloc(aml_memman, memid_aml_name_group);
+ result->id = id;
+ result->head = NULL;
+ result->next = name_group_list;
+ name_group_list = result;
+ return (result);
+}
+
+void
+aml_delete_name_group(struct aml_name_group *target)
+{
+ struct aml_name_group *previous;
+
+ previous = name_group_list;
+ if (previous == target)
+ name_group_list = target->next;
+ else {
+ while (previous && previous->next != target)
+ previous = previous->next;
+ if (previous)
+ previous->next = target->next;
+ }
+ target->next = NULL;
+ if (target->head)
+ aml_delete_name(target->head);
+ memman_free(aml_memman, memid_aml_name_group, target);
+}
+
+static struct aml_name *
+aml_new_name(struct aml_name *parent, char *name)
+{
+ struct aml_name *newname;
+
+ if ((newname = aml_find_name(parent, name)) != NULL)
+ return (newname);
+
+ newname = memman_alloc(aml_memman, memid_aml_name);
+ strncpy(newname->name, name, 4);
+ newname->parent = parent;
+ newname->child = NULL;
+ newname->property = NULL;
+ if (parent->child)
+ newname->brother = parent->child;
+ else
+ newname->brother = NULL;
+ parent->child = newname;
+
+ newname->chain = name_group_list->head;
+ name_group_list->head = newname;
+
+ return (newname);
+}
+
+/*
+ * NOTE:
+ * aml_delete_name() doesn't maintain aml_name_group::{head,tail}.
+ */
+static void
+aml_delete_name(struct aml_name *target)
+{
+ struct aml_name *next;
+ struct aml_name *ptr;
+
+ for (; target; target = next) {
+ next = target->chain;
+ if (target->child) {
+ target->chain = NULL;
+ continue;
+ }
+ if (target->brother) {
+ if (target->parent) {
+ if (target->parent->child == target) {
+ target->parent->child = target->brother;
+ } else {
+ ptr = target->parent->child;
+ while (ptr && ptr->brother != target)
+ ptr = ptr->brother;
+ if (ptr)
+ ptr->brother = target->brother;
+ }
+ target->brother = NULL;
+ }
+ } else if (target->parent) {
+ target->parent->child = NULL;
+ }
+ aml_free_object(&target->property);
+ memman_free(aml_memman, memid_aml_name, target);
+ }
+}
+
+#define AML_SEARCH_NAME 0
+#define AML_CREATE_NAME 1
+static struct aml_name *aml_nameman(struct aml_environ *, u_int8_t *, int);
+
+struct aml_name *
+aml_search_name(struct aml_environ *env, u_int8_t *dp)
+{
+
+ return (aml_nameman(env, dp, AML_SEARCH_NAME));
+}
+
+struct aml_name *
+aml_create_name(struct aml_environ *env, u_int8_t *dp)
+{
+
+ return (aml_nameman(env, dp, AML_CREATE_NAME));
+}
+
+static struct aml_name *
+aml_nameman(struct aml_environ *env, u_int8_t *dp, int flag)
+{
+ int segcount;
+ int i;
+ struct aml_name *newname, *curname;
+ struct aml_name *(*searchfunc) (struct aml_name *, char *);
+
+#define CREATECHECK() do { \
+ if (newname == NULL) { \
+ AML_DEBUGPRINT("ERROR CANNOT FIND NAME\n"); \
+ env->stat = aml_stat_panic; \
+ return (NULL); \
+ } \
+} while(0)
+
+ searchfunc = (flag == AML_CREATE_NAME) ? aml_new_name : aml_find_name;
+ newname = env->curname;
+ if (dp[0] == '\\') {
+ newname = &rootname;
+ dp++;
+ } else if (dp[0] == '^') {
+ while (dp[0] == '^') {
+ newname = newname->parent;
+ CREATECHECK();
+ dp++;
+ }
+ }
+ if (dp[0] == 0x00) { /* NullName */
+ dp++;
+ } else if (dp[0] == 0x2e) { /* DualNamePrefix */
+ newname = (*searchfunc) (newname, dp + 1);
+ CREATECHECK();
+ newname = (*searchfunc) (newname, dp + 5);
+ CREATECHECK();
+ } else if (dp[0] == 0x2f) { /* MultiNamePrefix */
+ segcount = dp[1];
+ for (i = 0, dp += 2; i < segcount; i++, dp += 4) {
+ newname = (*searchfunc) (newname, dp);
+ CREATECHECK();
+ }
+ } else if (flag == AML_CREATE_NAME) { /* NameSeg */
+ newname = aml_new_name(newname, dp);
+ CREATECHECK();
+ } else {
+ curname = newname;
+ for (;;) {
+ newname = aml_find_name(curname, dp);
+ if (newname != NULL)
+ break;
+ if (curname == &rootname)
+ break;
+ curname = curname->parent;
+ }
+ }
+ return (newname);
+}
+
+#undef CREATECHECK
+
+struct aml_local_stack *
+aml_local_stack_create()
+{
+ struct aml_local_stack *result;
+
+ result = memman_alloc(aml_memman, memid_aml_local_stack);
+ memset(result, 0, sizeof(struct aml_local_stack));
+ return (result);
+}
+
+void
+aml_local_stack_push(struct aml_local_stack *stack)
+{
+
+ stack->next = stack_top;
+ stack_top = stack;
+}
+
+struct aml_local_stack *
+aml_local_stack_pop()
+{
+ struct aml_local_stack *result;
+
+ result = stack_top;
+ stack_top = result->next;
+ result->next = NULL;
+ return (result);
+}
+
+void
+aml_local_stack_delete(struct aml_local_stack *stack)
+{
+ int i;
+
+ for (i = 0; i < 8; i++)
+ aml_free_object(&stack->localvalue[i].property);
+ for (i = 0; i < 7; i++)
+ aml_free_object(&stack->argumentvalue[i].property);
+ aml_delete_name(stack->temporary);
+ memman_free(aml_memman, memid_aml_local_stack, stack);
+}
+
+struct aml_name *
+aml_local_stack_getLocalX(int index)
+{
+
+ if (stack_top == NULL)
+ return (NULL);
+ return (&stack_top->localvalue[index]);
+}
+
+struct aml_name *
+aml_local_stack_getArgX(struct aml_local_stack *stack, int index)
+{
+
+ if (!stack)
+ stack = stack_top;
+ if (stack == NULL)
+ return (NULL);
+ return (&stack->argumentvalue[index]);
+}
+
+struct aml_name *
+aml_create_local_object()
+{
+ struct aml_name *result;
+
+ result = memman_alloc(aml_memman, memid_aml_name);
+ result->child = result->brother = result->parent = NULL;
+ result->property = NULL;
+ result->chain = stack_top->temporary;
+ stack_top->temporary = result;
+ return (result);
+}
diff --git a/usr.sbin/acpi/amldb/aml/aml_name.h b/usr.sbin/acpi/amldb/aml/aml_name.h
new file mode 100644
index 0000000..3d22d8b
--- /dev/null
+++ b/usr.sbin/acpi/amldb/aml/aml_name.h
@@ -0,0 +1,88 @@
+/*-
+ * Copyright (c) 1999 Takanori Watanabe
+ * Copyright (c) 1999, 2000 Yasuo Yokoyama
+ * Copyright (c) 1999, 2000 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: aml_name.h,v 1.17 2000/08/16 18:14:54 iwasaki Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _AML_NAME_H_
+#define _AML_NAME_H_
+
+#include <machine/stdarg.h>
+
+#include <aml/aml_obj.h>
+
+struct aml_name {
+ char name[4];
+ union aml_object *property;
+ struct aml_name *parent;
+ struct aml_name *brother;
+ struct aml_name *child;
+ struct aml_name *chain;
+};
+
+#define AML_NAME_GROUP_ROOT 0
+#define AML_NAME_GROUP_OS_DEFINED 1
+#define AML_NAME_GROUP_IN_METHOD 2
+
+struct aml_name_group {
+ int id; /* DSDT address or DBHANDLE */
+ struct aml_name *head;
+ struct aml_name_group *next;
+};
+
+struct aml_local_stack {
+ struct aml_name localvalue[8];
+ struct aml_name argumentvalue[7];
+ struct aml_name *temporary;
+ struct aml_local_stack *next;
+};
+
+/* forward declarement */
+struct aml_envrion;
+
+struct aml_name *aml_get_rootname(void);
+struct aml_name_group *aml_new_name_group(int);
+void aml_delete_name_group(struct aml_name_group *);
+
+struct aml_name *aml_find_from_namespace(struct aml_name *, char *);
+void aml_apply_foreach_found_objects(struct aml_name *,
+ char *, int (*)(struct aml_name *, va_list), ...);
+struct aml_name *aml_search_name(struct aml_environ *, u_int8_t *);
+struct aml_name *aml_create_name(struct aml_environ *, u_int8_t *);
+
+struct aml_local_stack *aml_local_stack_create(void);
+void aml_local_stack_push(struct aml_local_stack *);
+struct aml_local_stack *aml_local_stack_pop(void);
+void aml_local_stack_delete(struct aml_local_stack *);
+struct aml_name *aml_local_stack_getLocalX(int);
+struct aml_name *aml_local_stack_getArgX(struct aml_local_stack *, int);
+struct aml_name *aml_create_local_object(void);
+
+extern struct aml_name_group *name_group_list;
+
+#endif /* !_AML_NAME_H_ */
diff --git a/usr.sbin/acpi/amldb/aml/aml_obj.c b/usr.sbin/acpi/amldb/aml/aml_obj.c
new file mode 100644
index 0000000..17f2a50
--- /dev/null
+++ b/usr.sbin/acpi/amldb/aml/aml_obj.c
@@ -0,0 +1,264 @@
+/*-
+ * Copyright (c) 1999 Takanori Watanabe
+ * Copyright (c) 1999, 2000 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: aml_obj.c,v 1.17 2000/08/12 15:20:45 iwasaki Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+
+#include <aml/aml_amlmem.h>
+#include <aml/aml_env.h>
+#include <aml/aml_name.h>
+#include <aml/aml_obj.h>
+#include <aml/aml_status.h>
+#include <aml/aml_store.h>
+
+#ifndef _KERNEL
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include <assert.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#else /* _KERNEL */
+#include <sys/systm.h>
+#endif /* !_KERNEL */
+
+union aml_object *
+aml_copy_object(struct aml_environ *env, union aml_object *orig)
+{
+ int i;
+ union aml_object *ret;
+
+ if (orig == NULL)
+ return (NULL);
+ switch (orig->type) {
+ case aml_t_regfield:
+ ret = aml_alloc_object(aml_t_buffer, 0);
+ ret->buffer.size = (orig->regfield.bitlen / 8) +
+ ((orig->regfield.bitlen % 8) ? 1 : 0);
+ if (ret->buffer.size == 0) {
+ goto out;
+ }
+ ret->buffer.data = memman_alloc_flexsize(aml_memman, ret->buffer.size);
+ aml_store_to_object(env, orig, ret);
+ break;
+
+ default:
+ ret = aml_alloc_object(0, orig);
+ break;
+ }
+
+ if (1 || orig != &env->tempobject) { /* XXX */
+ if (orig->type == aml_t_buffer) {
+ if (orig->buffer.size == 0) {
+ goto out;
+ }
+ ret->buffer.data = memman_alloc_flexsize(aml_memman,
+ orig->buffer.size);
+ bcopy(orig->buffer.data, ret->buffer.data, orig->buffer.size);
+ } else if (orig->type == aml_t_package) {
+ if (ret->package.elements == 0) {
+ goto out;
+ }
+ ret->package.objects = memman_alloc_flexsize(aml_memman,
+ ret->package.elements * sizeof(union aml_object *));
+ for (i = 0; i < ret->package.elements; i++) {
+ ret->package.objects[i] = aml_copy_object(env, orig->package.objects[i]);
+ }
+ } else if (orig->type == aml_t_string && orig->str.needfree != 0) {
+ ret->str.string = memman_alloc_flexsize(aml_memman,
+ strlen(orig->str.string) + 1);
+ strcpy(orig->str.string, ret->str.string);
+ } else if (orig->type == aml_t_num) {
+ ret->num.constant = 0;
+ }
+ } else {
+ printf("%s:%d\n", __FILE__, __LINE__);
+ env->tempobject.type = aml_t_null;
+ }
+out:
+ return ret;
+}
+
+/*
+ * This function have two function: copy or allocate. if orig != NULL,
+ * orig is duplicated.
+ */
+
+union aml_object *
+aml_alloc_object(enum aml_objtype type, union aml_object *orig)
+{
+ unsigned int memid;
+ union aml_object *ret;
+
+ if (orig != NULL) {
+ type = orig->type;
+ }
+ switch (type) {
+ case aml_t_namestr:
+ memid = memid_aml_namestr;
+ break;
+ case aml_t_buffer:
+ memid = memid_aml_buffer;
+ break;
+ case aml_t_string:
+ memid = memid_aml_string;
+ break;
+ case aml_t_bufferfield:
+ memid = memid_aml_bufferfield;
+ break;
+ case aml_t_package:
+ memid = memid_aml_package;
+ break;
+ case aml_t_num:
+ memid = memid_aml_num;
+ break;
+ case aml_t_powerres:
+ memid = memid_aml_powerres;
+ break;
+ case aml_t_opregion:
+ memid = memid_aml_opregion;
+ break;
+ case aml_t_method:
+ memid = memid_aml_method;
+ break;
+ case aml_t_processor:
+ memid = memid_aml_processor;
+ break;
+ case aml_t_field:
+ memid = memid_aml_field;
+ break;
+ case aml_t_mutex:
+ memid = memid_aml_mutex;
+ break;
+ case aml_t_device:
+ memid = memid_aml_objtype;
+ break;
+ case aml_t_objref:
+ memid = memid_aml_objref;
+ break;
+ default:
+ memid = memid_aml_objtype;
+ break;
+ }
+ ret = memman_alloc(aml_memman, memid);
+ ret->type = type;
+
+ if (orig != NULL) {
+ bcopy(orig, ret, memman_memid2size(aml_memman, memid));
+ }
+ return (ret);
+}
+
+void
+aml_free_objectcontent(union aml_object *obj)
+{
+ int i;
+
+ if (obj->type == aml_t_buffer && obj->buffer.data != NULL) {
+ memman_free_flexsize(aml_memman, obj->buffer.data);
+ obj->buffer.data = NULL;
+ }
+ if (obj->type == aml_t_string && obj->str.string != NULL) {
+ if (obj->str.needfree != 0) {
+ memman_free_flexsize(aml_memman, obj->str.string);
+ obj->str.string = NULL;
+ }
+ }
+ if (obj->type == aml_t_package && obj->package.objects != NULL) {
+ for (i = 0; i < obj->package.elements; i++) {
+ aml_free_object(&obj->package.objects[i]);
+ }
+ memman_free_flexsize(aml_memman, obj->package.objects);
+ obj->package.objects = NULL;
+ }
+}
+
+void
+aml_free_object(union aml_object **obj)
+{
+ union aml_object *body;
+
+ body = *obj;
+ if (body == NULL) {
+ return;
+ }
+ aml_free_objectcontent(*obj);
+ memman_free(aml_memman, memid_unkown, *obj);
+ *obj = NULL;
+}
+
+void
+aml_realloc_object(union aml_object *obj, int size)
+{
+ int i;
+ enum aml_objtype type;
+ union aml_object tmp;
+
+ type = obj->type;
+ switch (type) {
+ case aml_t_buffer:
+ if (obj->buffer.size >= size) {
+ return;
+ }
+ tmp.buffer.size = size;
+ tmp.buffer.data = memman_alloc_flexsize(aml_memman, size);
+ bzero(tmp.buffer.data, size);
+ bcopy(obj->buffer.data, tmp.buffer.data, obj->buffer.size);
+ aml_free_objectcontent(obj);
+ *obj = tmp;
+ break;
+ case aml_t_string:
+ if (strlen(obj->str.string) >= size) {
+ return;
+ }
+ tmp.str.string = memman_alloc_flexsize(aml_memman, size + 1);
+ strcpy(tmp.str.string, obj->str.string);
+ aml_free_objectcontent(obj);
+ *obj = tmp;
+ break;
+ case aml_t_package:
+ if (obj->package.elements >= size) {
+ return;
+ }
+ tmp.package.objects = memman_alloc_flexsize(aml_memman,
+ size * sizeof(union aml_object *));
+ bzero(tmp.package.objects, size * sizeof(union aml_object *));
+ for (i = 0; i < obj->package.elements; i++) {
+ tmp.package.objects[i] = obj->package.objects[i];
+ }
+ memman_free_flexsize(aml_memman, obj->package.objects);
+ obj->package.objects = tmp.package.objects;
+ break;
+ default:
+ break;
+ }
+}
diff --git a/usr.sbin/acpi/amldb/aml/aml_obj.h b/usr.sbin/acpi/amldb/aml/aml_obj.h
new file mode 100644
index 0000000..45546e7
--- /dev/null
+++ b/usr.sbin/acpi/amldb/aml/aml_obj.h
@@ -0,0 +1,227 @@
+/*-
+ * Copyright (c) 1999 Takanori Watanabe
+ * Copyright (c) 1999, 2000 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: aml_obj.h,v 1.15 2000/08/09 14:47:43 iwasaki Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _AML_OBJ_H_
+#define _AML_OBJ_H_
+
+#include <sys/queue.h>
+
+struct aml_environ;
+enum aml_objtype {
+ aml_t_namestr = -3,
+ aml_t_regfield,
+ aml_t_objref,
+ aml_t_null = 0,
+ aml_t_num,
+ aml_t_string,
+ aml_t_buffer,
+ aml_t_package,
+ aml_t_device,
+ aml_t_field,
+ aml_t_event,
+ aml_t_method,
+ aml_t_mutex,
+ aml_t_opregion,
+ aml_t_powerres,
+ aml_t_processor,
+ aml_t_therm,
+ aml_t_bufferfield,
+ aml_t_ddbhandle,
+ aml_t_debug
+};
+
+struct aml_namestr {
+ enum aml_objtype type; /* =aml_t_namestr */
+ u_int8_t *dp;
+};
+
+struct aml_opregion {
+ enum aml_objtype type;
+ int space;
+ int offset;
+ int length;
+};
+
+struct aml_num {
+ enum aml_objtype type; /* =aml_t_num */
+ int number;
+ int constant;
+};
+
+struct aml_package {
+ enum aml_objtype type;
+ int elements;
+ union aml_object **objects;
+};
+
+struct aml_string {
+ enum aml_objtype type; /* =aml_t_string */
+ int needfree;
+ u_int8_t *string;
+};
+
+struct aml_buffer {
+ enum aml_objtype type; /* =aml_t_buffer */
+ int size;
+ u_int8_t *data; /* This should be free when
+ * this object is free.
+ */
+};
+
+enum fieldtype {
+ f_t_field,
+ f_t_index,
+ f_t_bank
+};
+
+struct nfieldd {
+ enum fieldtype ftype; /* f_t_field */
+ u_int8_t *regname; /* Namestring */
+};
+
+struct ifieldd {
+ enum fieldtype ftype; /* f_t_index */
+ u_int8_t *indexname;
+ u_int8_t *dataname;
+};
+
+struct bfieldd {
+ enum fieldtype ftype; /* f_t_bank */
+ u_int8_t *regname;
+ u_int8_t *bankname;
+ u_int32_t bankvalue;
+};
+
+struct aml_field {
+ enum aml_objtype type;
+ u_int32_t flags;
+ int bitoffset; /* Not Byte offset but bitoffset */
+ int bitlen;
+ union {
+ enum fieldtype ftype;
+ struct nfieldd fld;
+ struct ifieldd ifld;
+ struct bfieldd bfld;
+ } f;
+};
+
+struct aml_bufferfield {
+ enum aml_objtype type; /* aml_t_bufferfield */
+ int bitoffset;
+ int bitlen;
+ u_int8_t *origin; /* This should not be free
+ * when this object is free
+ * (Within Buffer object)
+ */
+};
+
+struct aml_method {
+ enum aml_objtype type;
+ int argnum; /* Not argnum but argnum|frag */
+ u_int8_t *from;
+ u_int8_t *to;
+};
+
+struct aml_powerres {
+ enum aml_objtype type;
+ int level;
+ int order;
+};
+
+struct aml_processor {
+ enum aml_objtype type;
+ int id;
+ int addr;
+ int len;
+};
+
+struct aml_mutex_queue {
+ STAILQ_ENTRY(aml_mutex_queue) entry;
+};
+
+struct aml_mutex {
+ enum aml_objtype type;
+ int level;
+ volatile void *cookie; /* In kernel, struct proc? */
+ STAILQ_HEAD(, aml_mutex_queue) queue;
+};
+
+struct aml_objref {
+ enum aml_objtype type;
+ struct aml_name *nameref;
+ union aml_object *ref;
+ int offset; /* of aml_buffer.data or aml_package.objects. */
+ /* if negative value, not ready to dereference for element access. */
+ unsigned deref; /* indicates whether dereffenced or not */
+ unsigned alias; /* true if this is an alias object reference */
+};
+
+struct aml_regfield {
+ enum aml_objtype type;
+ int space;
+ u_int32_t flags;
+ int offset;
+ int bitoffset;
+ int bitlen;
+};
+
+struct aml_event {
+ enum aml_objtype type; /* aml_t_event */
+ int inuse;
+};
+
+union aml_object {
+ enum aml_objtype type;
+ struct aml_num num;
+ struct aml_processor proc;
+ struct aml_powerres pres;
+ struct aml_opregion opregion;
+ struct aml_method meth;
+ struct aml_field field;
+ struct aml_mutex mutex;
+ struct aml_namestr nstr;
+ struct aml_buffer buffer;
+ struct aml_bufferfield bfld;
+ struct aml_package package;
+ struct aml_string str;
+ struct aml_objref objref;
+ struct aml_event event;
+ struct aml_regfield regfield;
+};
+
+union aml_object *aml_copy_object(struct aml_environ *,
+ union aml_object *);
+union aml_object *aml_alloc_object(enum aml_objtype,
+ union aml_object *);
+void aml_free_objectcontent(union aml_object *);
+void aml_free_object(union aml_object **);
+void aml_realloc_object(union aml_object *, int);
+
+#endif /* !_AML_OBJ_H_ */
diff --git a/usr.sbin/acpi/amldb/aml/aml_parse.c b/usr.sbin/acpi/amldb/aml/aml_parse.c
new file mode 100644
index 0000000..83eb34f
--- /dev/null
+++ b/usr.sbin/acpi/amldb/aml/aml_parse.c
@@ -0,0 +1,2020 @@
+/*-
+ * Copyright (c) 1999 Doug Rabson
+ * Copyright (c) 1999, 2000 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: aml_parse.c,v 1.32 2000/08/12 15:20:45 iwasaki Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+
+#include <aml/aml_amlmem.h>
+#include <aml/aml_common.h>
+#include <aml/aml_env.h>
+#include <aml/aml_evalobj.h>
+#include <aml/aml_name.h>
+#include <aml/aml_obj.h>
+#include <aml/aml_parse.h>
+#include <aml/aml_status.h>
+#include <aml/aml_store.h>
+
+#ifndef _KERNEL
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include <assert.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "debug.h"
+#else /* _KERNEL */
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <machine/bus.h>
+#include <dev/acpi/acpireg.h>
+#include <dev/acpi/acpivar.h>
+#ifndef ACPI_NO_OSDFUNC_INLINE
+#include <machine/acpica_osd.h>
+#endif
+#endif /* !_KERNEL */
+
+static int findsetleftbit(int num);
+static int findsetrightbit(int num);
+static int frombcd(int num);
+static int tobcd(int num);
+
+static u_int32_t aml_parse_pkglength(struct aml_environ *env);
+static u_int8_t aml_parse_bytedata(struct aml_environ *env);
+static u_int16_t aml_parse_worddata(struct aml_environ *env);
+static u_int32_t aml_parse_dworddata(struct aml_environ *env);
+static u_int8_t *aml_parse_namestring(struct aml_environ *env);
+static void aml_parse_defscope(struct aml_environ *env,
+ int indent);
+static union aml_object *aml_parse_defbuffer(struct aml_environ *env,
+ int indent);
+static struct aml_name *aml_parse_concat_number(struct aml_environ *env,
+ int num1, int indent);
+static struct aml_name *aml_parse_concat_buffer(struct aml_environ *env,
+ union aml_object *obj,
+ int indent);
+static struct aml_name *aml_parse_concat_string(struct aml_environ *env,
+ union aml_object *obj,
+ int indent);
+static struct aml_name *aml_parse_concatop(struct aml_environ *env,
+ int indent);
+static union aml_object *aml_parse_defpackage(struct aml_environ *env,
+ int indent);
+static void aml_parse_defmethod(struct aml_environ *env,
+ int indent);
+static void aml_parse_defopregion(struct aml_environ *env,
+ int indent);
+static int aml_parse_field(struct aml_environ *env,
+ struct aml_field *template);
+static void aml_parse_fieldlist(struct aml_environ *env,
+ struct aml_field *template,
+ int indent);
+static void aml_parse_deffield(struct aml_environ *env,
+ int indent);
+static void aml_parse_defindexfield(struct aml_environ *env,
+ int indent);
+static void aml_parse_defbankfield(struct aml_environ *env,
+ int indent);
+static void aml_parse_defdevice(struct aml_environ *env,
+ int indent);
+static void aml_parse_defprocessor(struct aml_environ *env,
+ int indent);
+static void aml_parse_defpowerres(struct aml_environ *env,
+ int indent);
+static void aml_parse_defthermalzone(struct aml_environ *env,
+ int indent);
+static struct aml_name *aml_parse_defelse(struct aml_environ *env,
+ int indent, int num);
+static struct aml_name *aml_parse_defif(struct aml_environ *env,
+ int indent);
+static struct aml_name *aml_parse_defwhile(struct aml_environ *env,
+ int indent);
+static void aml_parse_defmutex(struct aml_environ *env,
+ int indent);
+static void aml_createfield_generic(struct aml_environ *env,
+ union aml_object *srcbuf,
+ int index, int len,
+ char *newname);
+static void aml_parse_defcreatefield(struct aml_environ *env,
+ int indent);
+
+static int
+findsetleftbit(int num)
+{
+ int i, filter;
+
+ filter = 0;
+ for (i = 0; i < 32; i++) {
+ filter = filter >> 1;
+ filter |= 1 << 31;
+ if (filter & num) {
+ break;
+ }
+ }
+ i = (i == 32) ? 0 : i + 1;
+ return (i);
+}
+
+static int
+findsetrightbit(int num)
+{
+ int i, filter;
+
+ filter = 0;
+ for (i = 0; i < 32; i++) {
+ filter = filter << 1;
+ filter |= 1;
+ if (filter & num) {
+ break;
+ }
+ }
+ i = (i == 32) ? 0 : i + 1;
+ return (i);
+}
+
+static int
+frombcd(int num)
+{
+ int res, factor;
+
+ res = 0;
+ factor = 1;
+ while (num != 0) {
+ res += ((num & 0xf) * factor);
+ num = num / 16;
+ factor *= 10;
+ }
+ return (res);
+}
+
+static int
+tobcd(int num)
+{
+ int res, factor;
+
+ res = 0;
+ factor = 1;
+ while (num != 0) {
+ res += ((num % 10) * factor);
+ num = num / 10;
+ factor *= 16;
+ }
+ return (res);
+}
+
+static u_int32_t
+aml_parse_pkglength(struct aml_environ *env)
+{
+ u_int8_t *dp;
+ u_int32_t pkglength;
+
+ dp = env->dp;
+ pkglength = *dp++;
+ switch (pkglength >> 6) {
+ case 0:
+ break;
+ case 1:
+ pkglength = (pkglength & 0xf) + (dp[0] << 4);
+ dp += 1;
+ break;
+ case 2:
+ pkglength = (pkglength & 0xf) + (dp[0] << 4) + (dp[1] << 12);
+ dp += 2;
+ break;
+ case 3:
+ pkglength = (pkglength & 0xf)
+ + (dp[0] << 4) + (dp[1] << 12) + (dp[2] << 20);
+ dp += 3;
+ break;
+ }
+
+ env->dp = dp;
+ return (pkglength);
+}
+
+static u_int8_t
+aml_parse_bytedata(struct aml_environ *env)
+{
+ u_int8_t data;
+
+ data = env->dp[0];
+ env->dp++;
+ return (data);
+}
+
+static u_int16_t
+aml_parse_worddata(struct aml_environ *env)
+{
+ u_int16_t data;
+
+ data = env->dp[0] + (env->dp[1] << 8);
+ env->dp += 2;
+ return (data);
+}
+
+static u_int32_t
+aml_parse_dworddata(struct aml_environ *env)
+{
+ u_int32_t data;
+
+ data = env->dp[0] + (env->dp[1] << 8) +
+ (env->dp[2] << 16) + (env->dp[3] << 24);
+ env->dp += 4;
+ return (data);
+}
+
+static u_int8_t *
+aml_parse_namestring(struct aml_environ *env)
+{
+ u_int8_t *name;
+ int segcount;
+
+ name = env->dp;
+ if (env->dp[0] == '\\')
+ env->dp++;
+ else if (env->dp[0] == '^')
+ while (env->dp[0] == '^')
+ env->dp++;
+ if (env->dp[0] == 0x00) /* NullName */
+ env->dp++;
+ else if (env->dp[0] == 0x2e) /* DualNamePrefix */
+ env->dp += 1 + 4 + 4; /* NameSeg, NameSeg */
+ else if (env->dp[0] == 0x2f) { /* MultiNamePrefix */
+ segcount = env->dp[1];
+ env->dp += 1 + 1 + segcount * 4; /* segcount * NameSeg */
+ } else
+ env->dp += 4; /* NameSeg */
+
+ return (name);
+}
+
+struct aml_name *
+aml_parse_objectlist(struct aml_environ *env, int indent)
+{
+ union aml_object *obj;
+
+ obj = NULL;
+ while (env->dp < env->end) {
+ aml_print_indent(indent);
+ obj = aml_eval_name(env, aml_parse_termobj(env, indent));
+ AML_DEBUGPRINT("\n");
+ if (env->stat == aml_stat_step) {
+ AML_DEBUGGER(env, env);
+ continue;
+ }
+ if (env->stat != aml_stat_none) {
+ env->tempname.property = obj;
+ return (&env->tempname);
+ }
+ }
+ return (NULL);
+}
+
+#define AML_CREATE_NAME(amlname, env, namestr, ret) do { \
+ amlname = aml_create_name(env, namestr); \
+ if (env->stat == aml_stat_panic) \
+ return ret; \
+} while(0)
+
+#define AML_COPY_OBJECT(dest, env, src, ret) do { \
+ dest = aml_copy_object(env, src); \
+ if (dest == NULL) { \
+ env->stat = aml_stat_panic; \
+ return ret; \
+ } \
+} while(0)
+
+#define AML_ALLOC_OBJECT(dest, env, type, ret) do { \
+ dest = aml_alloc_object(type, NULL); \
+ if (dest == NULL) { \
+ env->stat= aml_stat_panic; \
+ return ret; \
+ } \
+} while(0)
+
+static void
+aml_parse_defscope(struct aml_environ *env, int indent)
+{
+ u_int8_t *start, *end, *oend;
+ u_int8_t *name;
+ u_int32_t pkglength;
+ struct aml_name *oname;
+
+ start = env->dp;
+ pkglength = aml_parse_pkglength(env);
+
+ AML_DEBUGPRINT("Scope(");
+ name = aml_parse_namestring(env);
+ aml_print_namestring(name);
+ AML_DEBUGPRINT(") {\n");
+ oname = env->curname;
+ AML_CREATE_NAME(env->curname, env, name,);
+ oend = env->end;
+ env->end = end = start + pkglength;
+ aml_parse_objectlist(env, indent + 1);
+ aml_print_indent(indent);
+ AML_DEBUGPRINT("}");
+ AML_SYSASSERT(env->dp == env->end);
+ env->dp = end;
+ env->end = oend;
+ env->curname = oname;
+ env->stat = aml_stat_none;
+}
+
+static union aml_object *
+aml_parse_defbuffer(struct aml_environ *env, int indent)
+{
+ u_int8_t *start;
+ u_int8_t *end;
+ u_int8_t *buffer;
+ u_int32_t pkglength;
+ int size1, size2, size;
+ union aml_object *obj;
+
+ start = env->dp;
+ pkglength = aml_parse_pkglength(env);
+ end = start + pkglength;
+
+ AML_DEBUGPRINT("Buffer(");
+ obj = aml_eval_name(env, aml_parse_termobj(env, indent));
+ size1 = aml_objtonum(env, obj);
+ size2 = end - env->dp;
+ size = (size1 < size2) ? size1 : size2;
+ if (size1 > 0) {
+ buffer = memman_alloc_flexsize(aml_memman, size1);
+ if (buffer == NULL) {
+ AML_DEBUGPRINT("NO MEMORY\n");
+ env->stat = aml_stat_panic;
+ return (NULL);
+ }
+ bzero(buffer, size1);
+ bcopy(env->dp, buffer, size);
+ } else {
+ buffer = NULL;
+ }
+
+ obj = &env->tempobject;
+ obj->type = aml_t_buffer;
+ obj->buffer.size = size1;
+ obj->buffer.data = buffer;
+ AML_DEBUGPRINT(") ");
+ env->dp = end;
+
+ return (obj);
+}
+
+static struct aml_name *
+aml_parse_concat_number(struct aml_environ *env, int num1, int indent)
+{
+ int num2;
+ struct aml_name *destname;
+ union aml_object *obj;
+
+ num2 = aml_objtonum(env, aml_eval_name(env,
+ aml_parse_termobj(env, indent)));
+ AML_DEBUGPRINT(", ");
+ destname = aml_parse_termobj(env, indent);
+ AML_DEBUGPRINT(")");
+ obj = &env->tempobject;
+ obj->type = aml_t_buffer;
+ obj->buffer.size = 2;
+ obj->buffer.data = memman_alloc_flexsize(aml_memman, 2);
+ if (obj->buffer.data == NULL) {
+ env->stat = aml_stat_panic;
+ return (NULL);
+ }
+ obj->buffer.data[0] = num1 & 0xff;
+ obj->buffer.data[1] = num2 & 0xff;
+ aml_store_to_name(env, obj, destname);
+ return (&env->tempname);
+}
+
+static struct aml_name *
+aml_parse_concat_buffer(struct aml_environ *env, union aml_object *obj,
+ int indent)
+{
+ union aml_object *tmpobj, *tmpobj2, *resobj;
+ struct aml_name *destname;
+
+ tmpobj = aml_eval_name(env, aml_parse_termobj(env, indent));
+ AML_DEBUGPRINT(", ");
+ if (tmpobj->type != aml_t_buffer) {
+ env->stat = aml_stat_panic;
+ return (NULL);
+ }
+ AML_COPY_OBJECT(tmpobj2, env, tmpobj, NULL);
+ destname = aml_parse_termobj(env, indent);
+ AML_DEBUGPRINT(")");
+ resobj = &env->tempobject;
+ env->tempname.property = resobj;
+ resobj->buffer.type = aml_t_buffer;
+ resobj->buffer.size = tmpobj2->buffer.size + obj->buffer.size;
+ if (resobj->buffer.size > 0) {
+ resobj->buffer.data = memman_alloc_flexsize(aml_memman,
+ resobj->buffer.size);
+ if (resobj->buffer.data == NULL) {
+ env->stat = aml_stat_panic;
+ return (NULL);
+ }
+ bcopy(obj->buffer.data, resobj->buffer.data, obj->buffer.size);
+ bcopy(tmpobj2->buffer.data,
+ resobj->buffer.data + obj->buffer.size,
+ tmpobj2->buffer.size);
+ } else {
+ resobj->buffer.data = NULL;
+ }
+ aml_free_object(&tmpobj2);
+ aml_store_to_name(env, resobj, destname);
+ return (&env->tempname);
+}
+
+static struct aml_name *
+aml_parse_concat_string(struct aml_environ *env, union aml_object *obj,
+ int indent)
+{
+ int len;
+ union aml_object *tmpobj, *tmpobj2, *resobj;
+ struct aml_name *destname;
+
+ tmpobj = aml_eval_name(env, aml_parse_termobj(env, indent));
+ AML_DEBUGPRINT(", ");
+ if (tmpobj->type != aml_t_string) {
+ env->stat = aml_stat_panic;
+ return (NULL);
+ }
+ AML_COPY_OBJECT(tmpobj2, env, tmpobj, NULL);
+ destname = aml_parse_termobj(env, indent);
+ AML_DEBUGPRINT(")");
+ resobj = &env->tempobject;
+ env->tempname.property = resobj;
+ resobj->type = aml_t_buffer;
+ resobj->str.needfree = 1;
+ len = strlen(obj->str.string) + strlen(tmpobj2->str.string) + 1;
+ if (len > 0) {
+ resobj->str.string = memman_alloc_flexsize(aml_memman, len);
+ if (resobj->str.string == NULL) {
+ env->stat = aml_stat_panic;
+ return (NULL);
+ }
+ strncpy(resobj->str.string, obj->str.string, len);
+ strcat(resobj->str.string, tmpobj->str.string);
+ } else {
+ resobj->str.string = NULL;
+ }
+ aml_free_object(&tmpobj2);
+ aml_store_to_name(env, resobj, destname);
+ return (&env->tempname);
+}
+
+static struct aml_name *
+aml_parse_concatop(struct aml_environ *env, int indent)
+{
+ union aml_object *obj, *tmpobj;
+ struct aml_name *aname;
+
+ AML_DEBUGPRINT("Concat(");
+ obj = aml_eval_name(env, aml_parse_termobj(env, indent));
+ AML_DEBUGPRINT(", ");
+ switch (obj->type) {
+ case aml_t_num:
+ aname = aml_parse_concat_number(env, aml_objtonum(env, obj), indent);
+ break;
+
+ case aml_t_buffer:
+ /* obj may be temporal object */
+ AML_COPY_OBJECT(tmpobj, env, obj, NULL);
+ aname = aml_parse_concat_buffer(env, obj, indent);
+ aml_free_object(&tmpobj);
+ break;
+
+ case aml_t_string:
+ /* obj may be temporal object */
+ AML_COPY_OBJECT(tmpobj, env, obj, NULL);
+ aname = aml_parse_concat_string(env, obj, indent);
+ aml_free_object(&tmpobj);
+ break;
+
+ default:
+ env->stat = aml_stat_panic;
+ aname = NULL;
+ break;
+ }
+
+ AML_DEBUGPRINT("\n");
+ return (aname);
+}
+
+static union aml_object *
+aml_parse_defpackage(struct aml_environ *env, int indent)
+{
+ u_int8_t numelements;
+ u_int8_t *start;
+ u_int32_t pkglength;
+ int i;
+ struct aml_environ *copy;
+ struct aml_name *tmpname;
+ union aml_object *obj, **objects;
+
+ start = env->dp;
+ pkglength = aml_parse_pkglength(env);
+ numelements = aml_parse_bytedata(env);
+ copy = memman_alloc(aml_memman, memid_aml_environ);
+ if (copy == NULL) {
+ env->stat = aml_stat_panic;
+ return (NULL);
+ }
+ if (numelements > 0) {
+ objects = memman_alloc_flexsize(aml_memman,
+ numelements * sizeof(union aml_object *));
+ if (objects == NULL) {
+ env->stat = aml_stat_panic;
+ return (NULL);
+ } else {
+ bzero(objects, numelements * sizeof(union aml_object *));
+ }
+ } else {
+ objects = NULL;
+ }
+
+ *copy = *env;
+ env->dp = copy->end = start + pkglength;
+ AML_DEBUGPRINT("Package() {\n");
+ i = 0;
+ while ((copy->dp < copy->end) && (i < numelements)) {
+ aml_print_indent(indent + 1);
+ tmpname = aml_parse_termobj(copy, indent + 1);
+
+ if (tmpname != NULL) {
+ objects[i] = aml_copy_object(copy, tmpname->property);
+ }
+ AML_DEBUGPRINT(",\n");
+ i++;
+ }
+ aml_free_objectcontent(&copy->tempobject);
+
+ aml_print_indent(indent);
+ AML_DEBUGPRINT("}");
+ obj = &env->tempobject;
+ obj->type = aml_t_package;
+ obj->package.elements = numelements;
+ obj->package.objects = objects;
+
+ memman_free(aml_memman, memid_aml_environ, copy);
+ return (obj);
+}
+
+static void
+aml_parse_defmethod(struct aml_environ *env, int indent)
+{
+ u_int8_t flags;
+ u_int8_t *start;
+ u_int32_t pkglength;
+ char *name;
+ struct aml_environ *copy;
+ struct aml_method *meth;
+ struct aml_name *aname;
+ union aml_object *aobj;
+
+ start = env->dp;
+ pkglength = aml_parse_pkglength(env);
+ copy = memman_alloc(aml_memman, memid_aml_environ);
+ if (copy == NULL) {
+ env->stat = aml_stat_panic;
+ return;
+ }
+ AML_DEBUGPRINT("Method(");
+ name = aml_parse_namestring(env);
+ aml_print_namestring(name);
+ AML_CREATE_NAME(aname, env, name,);
+ if (aname->property != NULL) {
+ env->stat = aml_stat_panic;
+ AML_DEBUGPRINT("Already Defined \n");
+ goto out;
+ }
+ AML_ALLOC_OBJECT(aobj, env, aml_t_method,);
+ meth = &aobj->meth;
+ aname->property = aobj;
+ flags = *env->dp++;
+
+ if (flags) {
+ AML_DEBUGPRINT(", %d", flags);
+ }
+ AML_DEBUGPRINT(") {\n");
+ *copy = *env;
+ meth->argnum = flags;
+ meth->from = env->dp;
+ meth->to = env->dp = copy->end = start + pkglength;
+ aml_print_indent(indent);
+ AML_DEBUGPRINT("}");
+out:
+ memman_free(aml_memman, memid_aml_environ, copy);
+}
+
+static void
+aml_parse_defopregion(struct aml_environ *env, int indent)
+{
+ u_int8_t *name;
+ struct aml_name *aname;
+ struct aml_opregion *opregion;
+ union aml_object *obj;
+ const char *regions[] = {
+ "SystemMemory",
+ "SystemIO",
+ "PCI_Config",
+ "EmbeddedControl",
+ "SMBus",
+ };
+
+ AML_DEBUGPRINT("OperationRegion(");
+ /* Name */
+ name = aml_parse_namestring(env);
+ aml_print_namestring(name);
+ AML_CREATE_NAME(aname, env, name,);
+ if (aname->property != NULL) {
+ env->stat = aml_stat_panic;
+ AML_DEBUGPRINT("Already Defined \n");
+ return;
+ }
+ AML_ALLOC_OBJECT(aname->property, env, aml_t_opregion,);
+ opregion = &aname->property->opregion;
+ opregion->space = *env->dp;
+ AML_DEBUGPRINT(", %s, ", regions[*env->dp]); /* Space */
+ env->dp++;
+ obj = aml_eval_name(env, aml_parse_termobj(env, indent)); /* Offset */
+ opregion->offset = aml_objtonum(env, obj);
+ AML_DEBUGPRINT(", ");
+ obj = aml_eval_name(env, aml_parse_termobj(env, indent)); /* Length */
+ opregion->length = aml_objtonum(env, obj);
+ AML_DEBUGPRINT(")");
+}
+
+static const char *accessnames[] = {
+ "AnyAcc",
+ "ByteAcc",
+ "WordAcc",
+ "DWordAcc",
+ "BlockAcc",
+ "SMBSendRecvAcc",
+ "SMBQuickAcc"
+};
+
+static int
+aml_parse_field(struct aml_environ *env, struct aml_field *template)
+{
+ u_int8_t *name;
+ u_int8_t access, attribute;
+ u_int32_t width;
+ struct aml_name *aname;
+ struct aml_field *prop;
+
+ switch (*env->dp) {
+ case '\\':
+ case '^':
+ case 'A'...'Z':
+ case '_':
+ case '.':
+ case '/':
+ name = aml_parse_namestring(env);
+ width = aml_parse_pkglength(env);
+ template->bitlen = width;
+ aml_print_namestring(name);
+ AML_CREATE_NAME(aname, env, name, NULL);
+ /* Allignment */
+ if (width == 16) {
+ template->bitoffset += 15;
+ template->bitoffset &= (~15);
+ }
+ if (width == 32) {
+ template->bitoffset += 31;
+ template->bitoffset &= (~31);
+ } else if ((width & 7) == 0) {
+ template->bitoffset += 7;
+ template->bitoffset &= (~7);
+ } else if ((width > 32) && (width & 7) != 0) {
+ AML_DEBUGPRINT("??? Can I treat it?\n");
+ }
+ if (aname->property != NULL) {
+ env->stat = aml_stat_panic;
+ AML_DEBUGPRINT("Already Defined \n");
+ return (NULL);
+ }
+ AML_ALLOC_OBJECT(aname->property, env, aml_t_field, NULL);
+ prop = &aname->property->field;
+ *prop = *template;
+ template->bitoffset += width;
+ AML_DEBUGPRINT(",\t%d", width);
+ break;
+ case 0x00:
+ env->dp++;
+ width = aml_parse_pkglength(env);
+ template->bitoffset += width;
+ AML_DEBUGPRINT("Offset(0x%x)", template->bitoffset);
+ break;
+ case 0x01:
+ access = env->dp[1];
+ attribute = env->dp[2];
+ env->dp += 3;
+ AML_DEBUGPRINT("AccessAs(%s, %d)", accessnames[access], attribute);
+ template->bitoffset = attribute;
+ template->flags = (template->flags | 0xf0) | access;
+ break;
+ }
+ return (template->bitoffset);
+}
+
+static void
+aml_parse_fieldlist(struct aml_environ *env, struct aml_field *template,
+ int indent)
+{
+ u_int32_t offset;
+
+ offset = 0;
+ while (env->dp < env->end) {
+ aml_print_indent(indent);
+ offset = aml_parse_field(env, template);
+ if (env->dp < env->end) {
+ AML_DEBUGPRINT(",\n");
+ } else {
+ AML_DEBUGPRINT("\n");
+ }
+ }
+}
+
+static void
+aml_parse_deffield(struct aml_environ *env, int indent)
+{
+ u_int8_t flags;
+ u_int8_t *start, *name;
+ u_int32_t pkglength;
+ struct aml_environ *copy;
+ struct aml_field fieldtemplate;
+ static const char *lockrules[] = {"NoLock", "Lock"};
+ static const char *updaterules[] = {"Preserve", "WriteAsOnes",
+ "WriteAsZeros", "*Error*"};
+
+ start = env->dp;
+ pkglength = aml_parse_pkglength(env);
+ copy = memman_alloc(aml_memman, memid_aml_environ);
+ if (copy == NULL) {
+ env->stat = aml_stat_panic;
+ return;
+ }
+ AML_DEBUGPRINT("Field(");
+ aml_print_namestring(name = aml_parse_namestring(env));
+ fieldtemplate.type = aml_t_field;
+ flags = aml_parse_bytedata(env);
+ fieldtemplate.flags = fieldtemplate.flags = flags;
+
+ *copy = *env;
+ env->dp = copy->end = start + pkglength;
+ fieldtemplate.bitoffset = 0;
+ fieldtemplate.bitlen = 0;
+ fieldtemplate.f.ftype = f_t_field;
+ fieldtemplate.f.fld.regname = name;
+ AML_DEBUGPRINT(", %s, %s, %s) {\n",
+ accessnames[flags & 0xf],
+ lockrules[(flags >> 4) & 1],
+ updaterules[(flags >> 5) & 3]);
+ aml_parse_fieldlist(copy, &fieldtemplate, indent + 1);
+ aml_print_indent(indent);
+ AML_DEBUGPRINT("}");
+ aml_free_objectcontent(&copy->tempobject);
+
+ AML_SYSASSERT(copy->dp == copy->end);
+ memman_free(aml_memman, memid_aml_environ, copy);
+}
+
+static void
+aml_parse_defindexfield(struct aml_environ *env, int indent)
+{
+ u_int8_t flags;
+ u_int8_t *start, *iname, *dname;
+ u_int32_t pkglength;
+ struct aml_environ *copy;
+ struct aml_field template;
+ static const char *lockrules[] = {"NoLock", "Lock"};
+ static const char *updaterules[] = {"Preserve", "WriteAsOnes",
+ "WriteAsZeros", "*Error*"};
+
+ start = env->dp;
+ pkglength = aml_parse_pkglength(env);
+ copy = memman_alloc(aml_memman, memid_aml_environ);
+ if (copy == NULL) {
+ env->stat = aml_stat_panic;
+ return;
+ }
+ AML_DEBUGPRINT("IndexField(");
+ aml_print_namestring(iname = aml_parse_namestring(env)); /* Name1 */
+ AML_DEBUGPRINT(", ");
+ aml_print_namestring(dname = aml_parse_namestring(env)); /* Name2 */
+ template.type = aml_t_field;
+ template.flags = flags = aml_parse_bytedata(env);
+ template.bitoffset = 0;
+ template.bitlen = 0;
+ template.f.ftype = f_t_index;
+ template.f.ifld.indexname = iname;
+ template.f.ifld.dataname = dname;
+ AML_DEBUGPRINT(", %s, %s, %s) {\n",
+ accessnames[flags & 0xf],
+ lockrules[(flags >> 4) & 1],
+ updaterules[(flags >> 5) & 3]);
+ *copy = *env;
+ env->dp = copy->end = start + pkglength;
+ aml_parse_fieldlist(copy, &template, indent + 1);
+ aml_print_indent(indent);
+ AML_DEBUGPRINT("}");
+ aml_free_objectcontent(&copy->tempobject);
+
+ AML_SYSASSERT(copy->dp == copy->end);
+ memman_free(aml_memman, memid_aml_environ, copy);
+}
+
+static void
+aml_parse_defbankfield(struct aml_environ *env, int indent)
+{
+ u_int8_t flags;
+ u_int8_t *start, *rname, *bname;
+ u_int32_t pkglength, bankvalue;
+ struct aml_environ *copy;
+ struct aml_field template;
+ union aml_object *obj;
+ static const char *lockrules[] = {"NoLock", "Lock"};
+ static const char *updaterules[] = {"Preserve", "WriteAsOnes",
+ "WriteAsZeros", "*Error*"};
+
+ start = env->dp;
+ pkglength = aml_parse_pkglength(env);
+ copy = memman_alloc(aml_memman, memid_aml_environ);
+ if (copy == NULL) {
+ env->stat = aml_stat_panic;
+ return;
+ }
+ AML_DEBUGPRINT("BankField(");
+ aml_print_namestring(rname = aml_parse_namestring(env)); /* Name1 */
+ AML_DEBUGPRINT(", ");
+ aml_print_namestring(bname = aml_parse_namestring(env)); /* Name2 */
+ AML_DEBUGPRINT(", ");
+ obj = aml_eval_name(env, aml_parse_termobj(env, indent)); /* BankValue */
+ bankvalue = aml_objtonum(env, obj);
+ template.type = aml_t_field;
+ template.flags = flags = aml_parse_bytedata(env);
+ template.bitoffset = 0;
+ template.bitlen = 0;
+ template.f.ftype = f_t_bank;
+ template.f.bfld.regname = rname;
+ template.f.bfld.bankname = bname;
+ template.f.bfld.bankvalue = bankvalue;
+ *copy = *env;
+ env->dp = copy->end = start + pkglength;
+ AML_DEBUGPRINT(", %s, %s, %s) {\n",
+ accessnames[flags & 0xf],
+ lockrules[(flags >> 4) & 1],
+ updaterules[(flags >> 5) & 3]);
+ aml_parse_fieldlist(copy, &template, indent + 1);
+ aml_print_indent(indent);
+ AML_DEBUGPRINT("}");
+
+ aml_free_objectcontent(&copy->tempobject);
+ AML_SYSASSERT(copy->dp == copy->end);
+ memman_free(aml_memman, memid_aml_environ, copy);
+}
+
+static void
+aml_parse_defdevice(struct aml_environ *env, int indent)
+{
+ u_int8_t *start;
+ u_int8_t *name;
+ u_int32_t pkglength;
+ struct aml_environ *copy;
+
+ start = env->dp;
+ pkglength = aml_parse_pkglength(env);
+ copy = memman_alloc(aml_memman, memid_aml_environ);
+ if (copy == NULL) {
+ env->stat = aml_stat_panic;
+ return;
+ }
+ AML_DEBUGPRINT("Device(");
+ name = aml_parse_namestring(env);
+ aml_print_namestring(name);
+ AML_DEBUGPRINT(") {\n");
+ *copy = *env;
+ AML_CREATE_NAME(copy->curname, env, name,);
+ if (copy->curname->property != NULL) {
+ env->stat = aml_stat_panic;
+ AML_DEBUGPRINT("Already Defined \n");
+ goto out;
+ }
+ AML_ALLOC_OBJECT(copy->curname->property, env, aml_t_device,);
+ env->dp = copy->end = start + pkglength;
+ aml_parse_objectlist(copy, indent + 1);
+ aml_print_indent(indent);
+ AML_DEBUGPRINT("}");
+ aml_free_objectcontent(&copy->tempobject);
+
+ AML_SYSASSERT(copy->dp == copy->end);
+out:
+ memman_free(aml_memman, memid_aml_environ, copy);
+}
+
+static void
+aml_parse_defprocessor(struct aml_environ *env, int indent)
+{
+ u_int8_t *start;
+ u_int8_t *name;
+ u_int32_t pkglength;
+ struct aml_environ *copy;
+ struct aml_processor *proc;
+ union aml_object *obj;
+
+ start = env->dp;
+ pkglength = aml_parse_pkglength(env);
+ copy = memman_alloc(aml_memman, memid_aml_environ);
+ if (copy == NULL) {
+ env->stat = aml_stat_panic;
+ return;
+ }
+ AML_ALLOC_OBJECT(obj, env, aml_t_processor,);
+ proc = &obj->proc;
+ AML_DEBUGPRINT("Processor(");
+ name = aml_parse_namestring(env);
+ aml_print_namestring(name);
+ proc->id = aml_parse_bytedata(env);
+ proc->addr = aml_parse_dworddata(env);
+ proc->len = aml_parse_bytedata(env);
+ AML_DEBUGPRINT(", %d, 0x%x, 0x%x) {\n", proc->id, proc->addr, proc->len);
+ *copy = *env;
+ AML_CREATE_NAME(copy->curname, env, name,);
+ if (copy->curname->property != NULL) {
+ env->stat = aml_stat_panic;
+ AML_DEBUGPRINT("Already Defined \n");
+ goto out;
+ }
+ copy->curname->property = obj;
+ env->dp = copy->end = start + pkglength;
+ aml_parse_objectlist(copy, indent + 1);
+ aml_print_indent(indent);
+ AML_DEBUGPRINT("}");
+ aml_free_objectcontent(&copy->tempobject);
+
+ AML_SYSASSERT(copy->dp == copy->end);
+out:
+ memman_free(aml_memman, memid_aml_environ, copy);
+}
+
+static void
+aml_parse_defpowerres(struct aml_environ *env, int indent)
+{
+ u_int8_t *start;
+ u_int8_t *name;
+ u_int32_t pkglength;
+ struct aml_environ *copy;
+ struct aml_powerres *pres;
+ union aml_object *obj;
+
+ start = env->dp;
+ pkglength = aml_parse_pkglength(env);
+ copy = memman_alloc(aml_memman, memid_aml_environ);
+ if (copy == NULL) {
+ env->stat = aml_stat_panic;
+ return;
+ }
+ AML_DEBUGPRINT("PowerResource(");
+ AML_ALLOC_OBJECT(obj, env, aml_t_powerres,);
+ name = aml_parse_namestring(env);
+ aml_print_namestring(name);
+ pres = &obj->pres;
+ pres->level = aml_parse_bytedata(env);
+ pres->order = aml_parse_worddata(env);
+ AML_DEBUGPRINT(", %d, %d) {\n", pres->level, pres->order);
+ *copy = *env;
+ AML_CREATE_NAME(copy->curname, env, name,);
+ if (copy->curname->property != NULL) {
+ env->stat = aml_stat_panic;
+ AML_DEBUGPRINT("Already Defined \n");
+ goto out;
+ }
+ copy->curname->property = obj;
+ env->dp = copy->end = start + pkglength;
+
+ aml_parse_objectlist(copy, indent + 1);
+ aml_print_indent(indent);
+ AML_DEBUGPRINT("}");
+ aml_free_objectcontent(&copy->tempobject);
+
+ AML_SYSASSERT(copy->dp == copy->end);
+out:
+ memman_free(aml_memman, memid_aml_environ, copy);
+}
+
+static void
+aml_parse_defthermalzone(struct aml_environ *env, int indent)
+{
+ u_int8_t *start;
+ u_int8_t *name;
+ u_int32_t pkglength;
+ struct aml_environ *copy;
+
+ start = env->dp;
+ pkglength = aml_parse_pkglength(env);
+ copy = memman_alloc(aml_memman, memid_aml_environ);
+ if (copy == NULL) {
+ env->stat = aml_stat_panic;
+ return;
+ }
+ AML_DEBUGPRINT("ThermalZone(");
+ name = aml_parse_namestring(env);
+ aml_print_namestring(name);
+ AML_DEBUGPRINT(") {\n");
+ *copy = *env;
+ AML_CREATE_NAME(copy->curname, env, name,);
+ if (copy->curname->property != NULL) {
+ env->stat = aml_stat_panic;
+ AML_DEBUGPRINT("Already Defined \n");
+ goto out;
+ }
+ AML_ALLOC_OBJECT(copy->curname->property, env, aml_t_therm,);
+ env->dp = copy->end = start + pkglength;
+ aml_parse_objectlist(copy, indent + 1);
+ aml_print_indent(indent);
+ AML_DEBUGPRINT("}");
+ aml_free_objectcontent(&copy->tempobject);
+ AML_SYSASSERT(copy->dp == copy->end);
+out:
+ memman_free(aml_memman, memid_aml_environ, copy);
+}
+
+static struct aml_name *
+aml_parse_defelse(struct aml_environ *env, int indent, int num)
+{
+ u_int8_t *start, *end, *oend;
+ u_int32_t pkglength;
+ struct aml_name *aname;
+
+ start = env->dp;
+ pkglength = aml_parse_pkglength(env);
+ oend = env->end;
+ env->end = end = start + pkglength;
+ aname = NULL;
+
+ AML_DEBUGPRINT("Else {\n");
+ if (num == 0) {
+ aname = aml_parse_objectlist(env, indent + 1);
+ aml_print_indent(indent);
+ }
+ AML_DEBUGPRINT("}");
+
+ env->dp = end;
+ env->end = oend;
+ return (aname);
+}
+
+static struct aml_name *
+aml_parse_defif(struct aml_environ *env, int indent)
+{
+ u_int8_t *start, *end, *oend;
+ u_int32_t pkglength;
+ int num;
+ struct aml_name *aname, *aname1;
+
+ start = env->dp;
+ pkglength = aml_parse_pkglength(env);
+ aname = NULL;
+
+ AML_DEBUGPRINT("If(");
+ num = aml_objtonum(env, aml_eval_name
+ (env, aml_parse_termobj(env, indent)));
+ oend = env->end;
+ end = start + pkglength;
+ AML_DEBUGPRINT(")");
+ if (num) {
+ AML_DEBUGPRINT("{\n");
+ env->end = end;
+ aname = aml_parse_objectlist(env, indent + 1);
+ aml_print_indent(indent);
+ AML_DEBUGPRINT("}");
+ }
+ env->dp = end;
+ env->end = oend;
+ if ((end < oend) && *(env->dp) == 0xa1) {
+ env->dp++;
+ aname1 = aml_parse_defelse(env, indent, num);
+ aname = (num == 0) ? aname1 : aname;
+ }
+ return (aname);
+}
+
+static struct aml_name *
+aml_parse_defwhile(struct aml_environ *env, int indent)
+{
+ u_int8_t *start, *end, *oend;
+ u_int32_t pkglength;
+ int num;
+ struct aml_name *aname;
+
+ start = env->dp;
+ pkglength = aml_parse_pkglength(env);
+ oend = env->end;
+ end = start + pkglength;
+ aname = NULL;
+ for (;;) {
+ env->dp = start;
+ aml_parse_pkglength(env);
+ AML_DEBUGPRINT("While(");
+ num = aml_objtonum(env, aml_eval_name
+ (env, aml_parse_termobj(env, indent)));
+ AML_DEBUGPRINT(")");
+ if (num == 0) {
+ break;
+ }
+ AML_DEBUGPRINT(" {\n");
+ env->end = end;
+ aname = aml_parse_objectlist(env, indent + 1);
+ if (env->stat == aml_stat_step) {
+ AML_DEBUGGER(env, env);
+ continue;
+ }
+ if (env->stat != aml_stat_none)
+ break;
+ aml_print_indent(indent);
+ AML_DEBUGPRINT("}");
+ }
+ AML_DEBUGPRINT("\n");
+ env->dp = end;
+ env->end = oend;
+ if (env->stat == aml_stat_break) {
+ env->stat = aml_stat_none;
+ aname = NULL;
+ }
+ return (aname);
+}
+
+static void
+aml_parse_defmutex(struct aml_environ *env, int indent)
+{
+ char *name;
+ struct aml_name *aname;
+ struct aml_mutex *mut;
+
+ /* MutexOp */
+ AML_DEBUGPRINT("Mutex(");
+ name = aml_parse_namestring(env);
+ aml_print_namestring(name);
+ AML_CREATE_NAME(aname, env, name,);
+ if (aname->property != NULL) {
+ env->stat = aml_stat_panic;
+ AML_DEBUGPRINT("Already Defined \n");
+ return;
+ }
+ AML_ALLOC_OBJECT(aname->property, env, aml_t_mutex,);
+ mut = &aname->property->mutex;
+ mut->level = *env->dp++;
+ STAILQ_INIT(&mut->queue);
+ AML_DEBUGPRINT(", %d)", mut->level);
+}
+
+static void
+aml_createfield_generic(struct aml_environ *env,
+ union aml_object *srcbuf, int index,
+ int len, char *newname)
+{
+ struct aml_bufferfield *field;
+ struct aml_name *aname;
+
+ if (srcbuf == NULL || srcbuf->type != aml_t_buffer) {
+ AML_DEBUGPRINT("Not Buffer assigned,");
+ env->stat = aml_stat_panic;
+ return;
+ }
+ AML_CREATE_NAME(aname, env, newname,);
+ if (aname->property != NULL) {
+ env->stat = aml_stat_panic;
+ AML_DEBUGPRINT("Already Defined \n");
+ return;
+ }
+ AML_ALLOC_OBJECT(aname->property, env, aml_t_bufferfield,);
+ field = &aname->property->bfld;
+ field->bitoffset = index;
+ field->bitlen = len;
+ field->origin = srcbuf->buffer.data;
+}
+
+static void
+aml_parse_defcreatefield(struct aml_environ *env, int indent)
+{
+ int index, len;
+ char *newname;
+ union aml_object *obj, *srcbuf;
+
+ /* CreateFieldOp */
+ AML_DEBUGPRINT("CreateField(");
+ srcbuf = aml_eval_name(env, aml_parse_termobj(env, indent));
+ if (srcbuf == &env->tempobject) {
+ AML_DEBUGPRINT("NONAMED BUFFER\n");
+ env->stat = aml_stat_panic;
+ return;
+ }
+ AML_DEBUGPRINT(", ");
+ obj = aml_eval_name(env, aml_parse_termobj(env, indent));
+ index = aml_objtonum(env, obj);
+ AML_DEBUGPRINT(", ");
+ obj = aml_eval_name(env, aml_parse_termobj(env, indent));
+ len = aml_objtonum(env, obj);
+ AML_DEBUGPRINT(", ");
+ newname = aml_parse_namestring(env);
+ aml_print_namestring(newname);
+ aml_createfield_generic(env, srcbuf, index, len, newname);
+ AML_DEBUGPRINT(") ");
+}
+
+/*
+ * Returns Named object or parser buffer. The object need not be free because
+ * it returns preallocated buffer in env or Contain of named object. If You
+ * need to preserve object, create a copy and then store. And The object
+ * returned from this function is not valid after another call is
+ * shared, tempolary buffer may be shared.
+ */
+struct aml_name *
+aml_parse_termobj(struct aml_environ *env, int indent)
+{
+ u_int8_t opcode;
+ u_int8_t *name;
+ int value;
+ int num1, num2;
+ int len;
+ int match1, match2, i, pkgval, start;
+ int widthindex, index;
+ char *newname;
+ struct aml_name *aname;
+ struct aml_name *destname1, *destname2;
+ struct aml_name *tmpname, *srcname;
+ struct aml_name *src;
+ union aml_object *ret;
+ union aml_object *tmpobj;
+ union aml_object anum;
+ union aml_object *objref;
+ union aml_object *srcobj;
+ union aml_object *obj;
+ union aml_object *srcbuf;
+ static int widthtbl[4] = {32, 16, 8, 1};
+ const char *opname[4] = {"CreateDWordField", "CreateWordField",
+ "CreateByteField", "CreateBitField"};
+
+ aname = &env->tempname;
+ ret = &env->tempobject;
+ anum.type = aml_t_num;
+ aname->property = ret;
+ aml_free_objectcontent(ret);
+ if (env->stat == aml_stat_panic) {
+ /*
+ * If previosuly parser panic , parsing next instruction is
+ * prohibited.
+ */
+ return (NULL);
+ }
+ aname = NULL;
+ opcode = *env->dp++;
+ switch (opcode) {
+ case '\\':
+ case '^':
+ case 'A' ... 'Z':
+ case '_':
+ case '.':
+ case '/':
+ env->dp--;
+ ret->type = aml_t_namestr;
+ ret->nstr.dp = aml_parse_namestring(env);
+ aml_print_namestring(ret->nstr.dp);
+ aname = &env->tempname;
+ break;
+ case 0x0a: /* BytePrefix */
+ ret->type = aml_t_num;
+ value = aml_parse_bytedata(env);
+ ret->num.number = value;
+ AML_DEBUGPRINT("0x%x", value);
+ aname = &env->tempname;
+ break;
+ case 0x0b: /* WordPrefix */
+ ret->type = aml_t_num;
+ value = aml_parse_worddata(env);
+ ret->num.number = value;
+ AML_DEBUGPRINT("0x%x", value);
+ aname = &env->tempname;
+ break;
+ case 0x0c: /* DWordPrefix */
+ ret->type = aml_t_num;
+ value = aml_parse_dworddata(env);
+ ret->num.number = value;
+ AML_DEBUGPRINT("0x%x", value);
+ aname = &env->tempname;
+ break;
+ case 0x0d: /* StringPrefix */
+ ret->type = aml_t_string;
+ ret->str.string = env->dp;
+ len = strlen(env->dp);
+ ret->str.needfree = 0;
+ AML_DEBUGPRINT("\"%s\"", (const char *)ret->str.string);
+ env->dp += (len + 1);
+ aname = &env->tempname;
+ break;
+ case 0x00: /* ZeroOp */
+ ret->type = aml_t_num;
+ ret->num.number = 0;
+ ret->num.constant = 1;
+ AML_DEBUGPRINT("Zero");
+ aname = &env->tempname;
+ break;
+ case 0x01: /* OneOp */
+ ret->type = aml_t_num;
+ ret->num.number = 1;
+ ret->num.constant = 1;
+ AML_DEBUGPRINT("One");
+ aname = &env->tempname;
+ break;
+ case 0xff: /* OnesOp */
+ ret->type = aml_t_num;
+ ret->num.number = 0xffffffff;
+ ret->num.constant = 1;
+ AML_DEBUGPRINT("Ones");
+ aname = &env->tempname;
+ break;
+ case 0x06: /* AliasOp */
+ AML_DEBUGPRINT("Alias(");
+ tmpname = aml_parse_termobj(env, indent);
+ if (env->stat == aml_stat_panic) {
+ return (NULL);
+ }
+ if (tmpname->property == NULL ||
+ tmpname->property->type != aml_t_namestr) {
+ env->stat = aml_stat_panic;
+ return (NULL);
+ }
+ /*
+ * XXX if srcname is deleted after this object, what
+ * shall I do?
+ */
+ srcname = aml_search_name(env, tmpname->property->nstr.dp);
+ AML_DEBUGPRINT(", ");
+ name = aml_parse_namestring(env);
+ aml_print_namestring(name);
+ AML_CREATE_NAME(aname, env, name, NULL);
+ if (aname->property != NULL) {
+ env->stat = aml_stat_panic;
+ AML_DEBUGPRINT("Already Defined \n");
+ aml_print_curname(aname);
+ return (NULL);
+ }
+ AML_ALLOC_OBJECT(aname->property, env, aml_t_objref, NULL);
+ objref = aname->property;
+ objref->objref.nameref = srcname;
+ objref->objref.ref = srcname->property;
+ objref->objref.offset = -1;
+ objref->objref.alias = 1; /* Yes, this is an alias */
+ AML_DEBUGPRINT(")");
+ /* shut the interpreter up during the namespace initializing */
+ return (NULL);
+ case 0x08: /* NameOp */
+ AML_DEBUGPRINT("Name(");
+ name = aml_parse_namestring(env);
+ aml_print_namestring(name);
+ AML_CREATE_NAME(aname, env, name, NULL);
+ if (env->stat == aml_stat_panic) {
+ AML_DEBUGPRINT("Already Defined \n");
+ aml_print_curname(aname);
+ return (NULL);
+ }
+ AML_DEBUGPRINT(", ");
+ AML_COPY_OBJECT(aname->property, env,
+ aml_eval_name(env,
+ aml_parse_termobj(env, indent)),
+ NULL);
+ AML_DEBUGPRINT(")");
+ break;
+ case 0x10: /* ScopeOp */
+ aml_parse_defscope(env, indent);
+ break;
+ case 0x11: /* BufferOp */
+ aname = &env->tempname;
+ aname->property = aml_parse_defbuffer(env, indent);
+ break;
+ case 0x12: /* PackageOp */
+ aname = &env->tempname;
+ aname->property = aml_parse_defpackage(env, indent);
+ break;
+ case 0x14: /* MethodOp */
+ aml_parse_defmethod(env, indent);
+ break;
+ case 0x5b: /* ExtOpPrefix */
+ opcode = *env->dp++;
+ switch (opcode) {
+ case 0x01:
+ aml_parse_defmutex(env, indent);
+ break;
+ case 0x02: /* EventOp */
+ AML_DEBUGPRINT("Event(");
+ name = aml_parse_namestring(env);
+ aml_print_namestring(name);
+ AML_CREATE_NAME(aname, env, name, NULL);
+ if (aname->property != NULL) {
+ env->stat = aml_stat_panic;
+ AML_DEBUGPRINT("Already Defined \n");
+ return (NULL);
+ }
+ AML_ALLOC_OBJECT(aname->property, env, aml_t_event, NULL);
+ AML_DEBUGPRINT(")");
+ return (NULL);
+ break;
+ case 0x12: /* CondRefOfOp */
+ AML_DEBUGPRINT("CondRefOf(");
+ src = aml_parse_termobj(env, indent);
+ AML_DEBUGPRINT(", ");
+ if (src == &env->tempname || src == NULL) {
+ aml_parse_termobj(env, indent);
+ AML_DEBUGPRINT(")");
+ anum.num.number = 0xffffffff;
+ env->tempobject.num = anum.num;
+ aname = &env->tempname;
+ break;
+ }
+ AML_ALLOC_OBJECT(objref, env, aml_t_objref, NULL);
+ if (src->property == NULL ||
+ src->property->type != aml_t_namestr) {
+ objref->objref.nameref = src;
+ } else {
+ objref->objref.nameref = aml_create_local_object();
+ }
+ objref->objref.ref = src->property;
+ objref->objref.offset = -1; /* different from IndexOp */
+
+ destname1 = aml_parse_termobj(env, indent);
+ aml_store_to_name(env, objref, destname1);
+ anum.num.number = 0;
+ env->tempobject.num = anum.num;
+ aname = &env->tempname;
+ AML_DEBUGPRINT(")");
+ break;
+ case 0x13:
+ aml_parse_defcreatefield(env, indent);
+ break;
+ case 0x20: /* LoadOp *//* XXX Not Impremented */
+ AML_DEBUGPRINT("Load(");
+ aml_parse_termobj(env, indent);
+ AML_DEBUGPRINT(", ");
+ aml_parse_termobj(env, indent);
+ AML_DEBUGPRINT(")");
+ break;
+ case 0x21: /* StallOp */
+ AML_DEBUGPRINT("Stall(");
+ num1 = aml_objtonum(env, aml_eval_name(env,
+ aml_parse_termobj(env, indent)));
+ AML_DEBUGPRINT(")");
+ AML_STALL(num1);
+ break;
+ case 0x22: /* SleepOp */
+ AML_DEBUGPRINT("Sleep(");
+ num1 = aml_objtonum(env, aml_eval_name(env,
+ aml_parse_termobj(env, indent)));
+ AML_SLEEP(0, num1);
+ AML_DEBUGPRINT(")");
+ break;
+ case 0x23: /* AcquireOp *//* XXX Not yet */
+ AML_DEBUGPRINT("Acquire(");
+ aml_parse_termobj(env, indent);
+ AML_DEBUGPRINT(", 0x%x)", aml_parse_worddata(env));
+ break;
+ case 0x24: /* SignalOp *//* XXX Not yet */
+ AML_DEBUGPRINT("Signal(");
+ aml_parse_termobj(env, indent);
+ AML_DEBUGPRINT(")");
+ break;
+ case 0x25: /* WaitOp *//* XXX Not yet impremented */
+ AML_DEBUGPRINT("Wait(");
+ aml_parse_termobj(env, indent);
+ AML_DEBUGPRINT(", ");
+ aml_parse_termobj(env, indent);
+ AML_DEBUGPRINT(")");
+ break;
+ case 0x26: /* ResetOp *//* XXX Not yet impremented */
+ AML_DEBUGPRINT("Reset(");
+ aml_parse_termobj(env, indent);
+ AML_DEBUGPRINT(")");
+ break;
+ case 0x27: /* ReleaseOp *//* XXX Not yet impremented */
+ AML_DEBUGPRINT("Release(");
+ aml_parse_termobj(env, indent);
+ AML_DEBUGPRINT(")");
+ break;
+#define NUMOP2(opname, operation) do { \
+ AML_DEBUGPRINT(opname); \
+ AML_DEBUGPRINT("("); \
+ num1 = aml_objtonum(env, aml_eval_name(env, \
+ aml_parse_termobj(env, indent))); \
+ AML_DEBUGPRINT(", "); \
+ anum.num.number = operation (num1); \
+ destname1 = aml_parse_termobj(env, indent); \
+ AML_DEBUGPRINT(")"); \
+ aml_store_to_name(env, &anum, destname1); \
+ env->tempobject.num = anum.num; \
+ env->tempname.property = &env->tempobject; \
+ aname = &env->tempname; \
+} while(0)
+
+ case 0x28: /* FromBCDOp */
+ NUMOP2("FromBCD", frombcd);
+ break;
+ case 0x29: /* ToBCDOp */
+ NUMOP2("ToBCD", tobcd);
+ break;
+ case 0x2a: /* UnloadOp *//* XXX Not yet impremented */
+ AML_DEBUGPRINT("Unload(");
+ aml_parse_termobj(env, indent);
+ AML_DEBUGPRINT(")");
+ break;
+ case 0x30:
+ env->tempobject.type = aml_t_num;
+ env->tempobject.num.number = 0;
+ env->tempobject.num.constant = 1;
+ AML_DEBUGPRINT("Revision");
+ break;
+ case 0x31:
+ env->tempobject.type = aml_t_debug;
+ aname = &env->tempname;
+ AML_DEBUGPRINT("Debug");
+ break;
+ case 0x32: /* FatalOp */
+ AML_DEBUGPRINT("Fatal(");
+ AML_DEBUGPRINT("0x%x, ", aml_parse_bytedata(env));
+ AML_DEBUGPRINT("0x%x, ", aml_parse_dworddata(env));
+ aml_parse_termobj(env, indent);
+ env->stat = aml_stat_panic;
+ AML_DEBUGPRINT(")");
+ break;
+ case 0x80: /* OpRegionOp */
+ aml_parse_defopregion(env, indent);
+ break;
+ case 0x81: /* FieldOp */
+ aml_parse_deffield(env, indent);
+ break;
+ case 0x82: /* DeviceOp */
+ aml_parse_defdevice(env, indent);
+ break;
+ case 0x83: /* ProcessorOp */
+ aml_parse_defprocessor(env, indent);
+ break;
+ case 0x84: /* PowerResOp */
+ aml_parse_defpowerres(env, indent);
+ break;
+ case 0x85: /* ThermalZoneOp */
+ aml_parse_defthermalzone(env, indent);
+ break;
+ case 0x86: /* IndexFieldOp */
+ aml_parse_defindexfield(env, indent);
+ break;
+ case 0x87: /* BankFieldOp */
+ aml_parse_defbankfield(env, indent);
+ break;
+ default:
+ AML_SYSERRX(1, "strange opcode 0x5b, 0x%x\n", opcode);
+ AML_SYSABORT();
+ }
+ break;
+ case 0x68 ... 0x6e: /* ArgN */
+ AML_DEBUGPRINT("Arg%d", opcode - 0x68);
+ return (aml_local_stack_getArgX(NULL, opcode - 0x68));
+ break;
+ case 0x60 ... 0x67:
+ AML_DEBUGPRINT("Local%d", opcode - 0x60);
+ return (aml_local_stack_getLocalX(opcode - 0x60));
+ break;
+ case 0x70: /* StoreOp */
+ AML_DEBUGPRINT("Store(");
+ aname = aml_create_local_object();
+ AML_COPY_OBJECT(tmpobj, env,
+ aml_eval_name(env, aml_parse_termobj(env, indent)), NULL);
+ aname->property = tmpobj;
+ AML_DEBUGPRINT(", ");
+ destname1 = aml_parse_termobj(env, indent);
+ AML_DEBUGPRINT(")");
+ /* XXX
+ * temporary object may change during aml_store_to_name()
+ * operation, so we make a copy of it on stack.
+ */
+ if (destname1 == &env->tempname &&
+ destname1->property == &env->tempobject) {
+ destname1 = aml_create_local_object();
+ AML_COPY_OBJECT(destname1->property, env,
+ &env->tempobject, NULL);
+ }
+ aml_store_to_name(env, tmpobj, destname1);
+ if (env->stat == aml_stat_panic) {
+ AML_DEBUGPRINT("StoreOp failed");
+ return (NULL);
+ }
+ aname = aml_create_local_object();
+ AML_COPY_OBJECT(tmpobj, env, destname1->property, NULL);
+ aname->property = tmpobj;
+ if (tmpobj == NULL) {
+ printf("???");
+ break;
+ }
+ break;
+ case 0x71: /* RefOfOp */
+ AML_DEBUGPRINT("RefOf(");
+ src = aml_parse_termobj(env, indent);
+ AML_DEBUGPRINT(")");
+
+ aname = aml_create_local_object();
+ AML_ALLOC_OBJECT(aname->property, env, aml_t_objref, NULL);
+ objref = aname->property;
+ if (src->property == NULL ||
+ src->property->type != aml_t_namestr) {
+ objref->objref.nameref = src;
+ } else {
+ objref->objref.nameref = aml_create_local_object();
+ }
+ objref->objref.ref = src->property;
+ objref->objref.offset = -1; /* different from IndexOp */
+ break;
+
+#define NUMOP3_2(opname, oparation, ope2) do { \
+ AML_DEBUGPRINT(opname); \
+ AML_DEBUGPRINT("("); \
+ num1 = aml_objtonum(env, aml_eval_name(env, \
+ aml_parse_termobj(env, indent))); \
+ AML_DEBUGPRINT(", "); \
+ num2 = aml_objtonum(env, aml_eval_name(env, \
+ aml_parse_termobj(env, indent))); \
+ AML_DEBUGPRINT(", "); \
+ anum.num.number = ope2(num1 oparation num2); \
+ destname1 = aml_parse_termobj(env, indent); \
+ AML_DEBUGPRINT(")"); \
+ aml_store_to_name(env, &anum, destname1); \
+ env->tempobject.num = anum.num; \
+ env->tempname.property = &env->tempobject; \
+ aname = &env->tempname; \
+} while(0)
+
+#define NUMOP3(opname, operation) NUMOP3_2(opname, operation, )
+#define NUMOPN3(opname, operation) NUMOP3_2(opname, operation, ~)
+
+ case 0x72: /* AddOp */
+ NUMOP3("Add", +);
+ break;
+ case 0x73: /* ConcatOp */
+ aname = aml_parse_concatop(env, indent);
+ break;
+ case 0x74: /* SubtractOp */
+ NUMOP3("Subtract", -);
+ break;
+ case 0x75: /* IncrementOp */
+ AML_DEBUGPRINT("Increment(");
+ aname = aml_parse_termobj(env, indent);
+ num1 = aml_objtonum(env, aml_eval_name(env, aname));
+ num1++;
+ anum.num.number = num1;
+ AML_DEBUGPRINT(")");
+ aml_store_to_name(env, &anum, aname);
+ aname = &env->tempname;
+ env->tempobject.num = anum.num;
+ break;
+ case 0x76: /* DecrementOp */
+ AML_DEBUGPRINT("Decrement(");
+ aname = aml_parse_termobj(env, indent);
+ num1 = aml_objtonum(env, aml_eval_name(env, aname));
+ num1--;
+ anum.num.number = num1;
+ AML_DEBUGPRINT(")");
+ aml_store_to_name(env, &anum, aname);
+ aname = &env->tempname;
+ env->tempobject.num = anum.num;
+ break;
+ case 0x77: /* MultiplyOp */
+ NUMOP3("Multiply", *);
+ break;
+ case 0x78: /* DivideOp */
+ AML_DEBUGPRINT("Divide(");
+ num1 = aml_objtonum(env, aml_eval_name(env,
+ aml_parse_termobj(env, indent)));
+ AML_DEBUGPRINT(", ");
+ num2 = aml_objtonum(env, aml_eval_name(env,
+ aml_parse_termobj(env, indent)));
+ AML_DEBUGPRINT(", ");
+ anum.num.number = num1 % num2;
+ destname1 = aml_parse_termobj(env, indent);
+ aml_store_to_name(env, &anum, destname1);
+ AML_DEBUGPRINT(", ");
+ anum.num.number = num1 / num2;
+ destname2 = aml_parse_termobj(env, indent);
+ AML_DEBUGPRINT(")");
+ aml_store_to_name(env, &anum, destname2);
+ env->tempobject.num = anum.num;
+ aname = &env->tempname;
+ break;
+ case 0x79: /* ShiftLeftOp */
+ NUMOP3("ShiftLeft", <<);
+ break;
+ case 0x7a: /* ShiftRightOp */
+ NUMOP3("ShiftRight", >>);
+ break;
+ case 0x7b: /* AndOp */
+ NUMOP3("And", &);
+ break;
+ case 0x7c: /* NAndOp */
+ NUMOPN3("NAnd", &);
+ break;
+ case 0x7d: /* OrOp */
+ NUMOP3("Or", |);
+ break;
+ case 0x7e: /* NOrOp */
+ NUMOPN3("NOr", |);
+ break;
+ case 0x7f: /* XOrOp */
+ NUMOP3("XOr", ^);
+ break;
+ case 0x80: /* NotOp */
+ NUMOP2("Not", ~);
+ break;
+ case 0x81: /* FindSetLeftBitOp */
+ NUMOP2("FindSetLeftBit", findsetleftbit);
+ break;
+ case 0x82: /* FindSetRightBitOp */
+ NUMOP2("FindSetRightBit", findsetrightbit);
+ break;
+ case 0x83: /* DerefOp */
+ AML_DEBUGPRINT("DerefOf(");
+ objref = aml_eval_name(env, aml_parse_termobj(env, indent));
+ AML_DEBUGPRINT(")");
+
+ if (objref->objref.ref == NULL) {
+ env->tempname.property = objref->objref.ref;
+ aname = &env->tempname;
+ break;
+ }
+ switch (objref->objref.ref->type) {
+ case aml_t_package:
+ case aml_t_buffer:
+ if (objref->objref.offset < 0) {
+ env->tempname.property = objref->objref.ref;
+ } else {
+ objref->objref.deref = 1;
+ env->tempname.property = objref;
+ }
+ break;
+ default:
+ env->tempname.property = objref->objref.ref;
+ break;
+ }
+
+ aname = &env->tempname;
+ break;
+ case 0x86: /* NotifyOp *//* XXX Not yet impremented */
+ AML_DEBUGPRINT("Notify(");
+ aml_parse_termobj(env, indent);
+ AML_DEBUGPRINT(", ");
+ aml_parse_termobj(env, indent);
+ AML_DEBUGPRINT(")");
+ break;
+ case 0x87: /* SizeOfOp */
+ AML_DEBUGPRINT("SizeOf(");
+ aname = aml_parse_termobj(env, indent);
+ tmpobj = aml_eval_name(env, aname);
+
+ AML_DEBUGPRINT(")");
+ num1 = 0;
+ switch (tmpobj->type) {
+ case aml_t_buffer:
+ num1 = tmpobj->buffer.size;
+ break;
+ case aml_t_string:
+ num1 = strlen(tmpobj->str.string);
+ break;
+ case aml_t_package:
+ num1 = tmpobj->package.elements;
+ break;
+ default:
+ AML_DEBUGPRINT("Args of SizeOf should be "
+ "buffer/string/package only\n");
+ break;
+ }
+
+ anum.num.number = num1;
+ env->tempobject.num = anum.num;
+ aname = &env->tempname;
+ break;
+ case 0x88: /* IndexOp */
+ AML_DEBUGPRINT("Index(");
+ srcobj = aml_eval_name(env, aml_parse_termobj(env, indent));
+ AML_DEBUGPRINT(", ");
+ num1 = aml_objtonum(env, aml_eval_name(env,
+ aml_parse_termobj(env, indent)));
+ AML_DEBUGPRINT(", ");
+ destname1 = aml_parse_termobj(env, indent);
+ AML_DEBUGPRINT(")");
+ aname = aml_create_local_object();
+ switch (srcobj->type) {
+ case aml_t_package:
+ case aml_t_buffer:
+ AML_ALLOC_OBJECT(objref, env, aml_t_objref, NULL);
+ aname->property = objref;
+ objref->objref.ref = srcobj;
+ objref->objref.offset = num1;
+ objref->objref.deref = 0;
+ break;
+ default:
+ AML_DEBUGPRINT("Arg0 of Index should be either "
+ "buffer or package\n");
+ return (aname);
+ }
+
+ aml_store_to_name(env, objref, destname1);
+ break;
+ case 0x89: /* MatchOp *//* XXX Not yet Impremented */
+ AML_DEBUGPRINT("Match(");
+ AML_COPY_OBJECT(obj, env, aml_eval_name(env,
+ aml_parse_termobj(env, indent)), NULL);
+ if (obj->type != aml_t_package) {
+ env->stat = aml_stat_panic;
+ return (NULL);
+ }
+ anum.num.number = 0xffffffff;
+ match1 = *env->dp;
+ AML_DEBUGPRINT(", %d", *env->dp);
+ env->dp++;
+ num1 = aml_objtonum(env, aml_eval_name(env,
+ aml_parse_termobj(env, indent)));
+ match2 = *env->dp;
+ AML_DEBUGPRINT(", %d", *env->dp);
+ env->dp++;
+ num2 = aml_objtonum(env, aml_eval_name(env,
+ aml_parse_termobj(env, indent)));
+ AML_DEBUGPRINT(", ");
+ start = aml_objtonum(env, aml_eval_name(env,
+ aml_parse_termobj(env, indent)));
+
+#define MATCHOP(opnum, arg1, arg2) ((opnum == 0) ? (1) : \
+ (opnum == 1) ? ((arg1) == (arg2)) : \
+ (opnum == 2) ? ((arg1) <= (arg2)) : \
+ (opnum == 3) ? ((arg1) < (arg2)) : \
+ (opnum == 4) ? ((arg1) >= (arg2)) : \
+ (opnum == 5) ? ((arg1) > (arg2)) : 0 )
+
+ for (i = start; i < obj->package.elements; i++) {
+ pkgval = aml_objtonum(env, obj->package.objects[i]);
+ if (MATCHOP(match1, pkgval, num1) &&
+ MATCHOP(match2, pkgval, num2)) {
+ anum.num.number = i;
+ break;
+ }
+ }
+ AML_DEBUGPRINT(")");
+ aml_free_object(&obj);
+ aname = &env->tempname;
+ env->tempname.property = &env->tempobject;
+ env->tempobject.num = anum.num;
+ break;
+#undef MATCHOP
+ case 0x8a ... 0x8d: /* CreateDWordFieldOp */
+ widthindex = *(env->dp - 1) - 0x8a;
+ AML_DEBUGPRINT("%s(", opname[widthindex]);
+ srcbuf = aml_eval_name(env, aml_parse_termobj(env, indent));
+ if (srcbuf == &env->tempobject) {
+ AML_DEBUGPRINT("NOT NAMEDBUF\n");
+ env->stat = aml_stat_panic;
+ return (NULL);
+ }
+ AML_DEBUGPRINT(", ");
+ index = aml_objtonum(env, aml_eval_name(env,
+ aml_parse_termobj(env, indent)));
+ if (widthindex != 3) {
+ index *= 8;
+ }
+ AML_DEBUGPRINT(", ");
+ newname = aml_parse_namestring(env);
+ aml_print_namestring(newname);
+ aml_createfield_generic(env, srcbuf, index,
+ widthtbl[widthindex], newname);
+ AML_DEBUGPRINT(")");
+ break;
+ case 0x8e: /* ObjectTypeOp */
+ AML_DEBUGPRINT("ObjectType(");
+ aname = aml_parse_termobj(env, indent);
+ if (aname == NULL) {
+ env->tempobject.type = aml_t_num;
+ env->tempobject.num.number = aml_t_null;
+ } else {
+ env->tempobject.type = aml_t_num;
+ env->tempobject.num.number = aname->property->type;
+ }
+ aname = &env->tempname;
+ AML_DEBUGPRINT(")");
+ break;
+
+#define CMPOP(opname,operation) do { \
+ AML_DEBUGPRINT(opname); \
+ AML_DEBUGPRINT("("); \
+ num1 = aml_objtonum(env, aml_eval_name(env, \
+ aml_parse_termobj(env, indent))); \
+ AML_DEBUGPRINT(", "); \
+ num2 = aml_objtonum(env, aml_eval_name(env, \
+ aml_parse_termobj(env, indent))); \
+ aname = &env->tempname; \
+ env->tempobject.type = aml_t_num; \
+ env->tempobject.num.number = (num1 operation num2) ? 0xffffffff : 0; \
+ aname->property = &env->tempobject; \
+ AML_DEBUGPRINT(")"); \
+} while(0)
+
+ case 0x90:
+ CMPOP("LAnd", &&);
+ break;
+ case 0x91:
+ CMPOP("LOr", ||);
+ break;
+ case 0x92:
+ AML_DEBUGPRINT("LNot(");
+ num1 = aml_objtonum(env, aml_eval_name(env,
+ aml_parse_termobj(env, indent)));
+ aname = &env->tempname;
+ env->tempobject.type = aml_t_num;
+ env->tempobject.num.number = (!num1) ? 0xffffffff : 0;
+ aname->property = &env->tempobject;
+ AML_DEBUGPRINT(")");
+ break;
+ case 0x93:
+ CMPOP("LEqual", ==);
+ break;
+ case 0x94:
+ CMPOP("LGreater", >);
+ break;
+ case 0x95:
+ CMPOP("LLess", <);
+ break;
+ case 0xa0: /* IfOp */
+ aname = aml_parse_defif(env, indent);
+ break;
+#if 0
+
+ case 0xa1: /* ElseOp should not be treated in Main parser
+ * But If Op */
+ aml_parse_defelse(env, indent);
+ break;
+#endif
+ case 0xa2: /* WhileOp */
+ aname = aml_parse_defwhile(env, indent);
+ break;
+ case 0xa3: /* NoopOp */
+ AML_DEBUGPRINT("Noop");
+ break;
+ case 0xa5: /* BreakOp */
+ AML_DEBUGPRINT("Break");
+ env->stat = aml_stat_break;
+ break;
+ case 0xa4: /* ReturnOp */
+ AML_DEBUGPRINT("Return(");
+ AML_COPY_OBJECT(env->tempname.property, env, aml_eval_name(env,
+ aml_parse_termobj(env, indent)), NULL);
+ aname = &env->tempname;
+ env->stat = aml_stat_return;
+ AML_DEBUGPRINT(")");
+ break;
+ case 0xcc: /* BreakPointOp */
+ /* XXX Not Yet Impremented (Not need?) */
+ AML_DEBUGPRINT("BreakPoint");
+ break;
+ default:
+ AML_SYSERRX(1, "strange opcode 0x%x\n", opcode);
+ AML_SYSABORT();
+ }
+
+ return (aname);
+}
diff --git a/usr.sbin/acpi/amldb/aml/aml_parse.h b/usr.sbin/acpi/amldb/aml/aml_parse.h
new file mode 100644
index 0000000..96ab22c
--- /dev/null
+++ b/usr.sbin/acpi/amldb/aml/aml_parse.h
@@ -0,0 +1,36 @@
+/*-
+ * Copyright (c) 1999 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: aml_parse.h,v 1.9 2000/08/08 14:12:05 iwasaki Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _AML_PARSE_H_
+#define _AML_PARSE_H_
+
+struct aml_name *aml_parse_objectlist(struct aml_environ *, int);
+struct aml_name *aml_parse_termobj(struct aml_environ *, int);
+
+#endif /* !_AML_PARSE_H_ */
diff --git a/usr.sbin/acpi/amldb/aml/aml_region.c b/usr.sbin/acpi/amldb/aml/aml_region.c
new file mode 100644
index 0000000..f8f403f9
--- /dev/null
+++ b/usr.sbin/acpi/amldb/aml/aml_region.c
@@ -0,0 +1,349 @@
+/*-
+ * Copyright (c) 2000 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
+ * Copyright (c) 2000 Munehiro Matsuda <haro@tk.kubota.co.jp>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: aml_region.c,v 1.10 2000/08/09 14:47:44 iwasaki Exp $
+ * $FreeBSD$
+ */
+
+/*
+ * Region I/O subroutine
+ */
+
+#include "opt_acpi.h"
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <dev/acpi/acpireg.h>
+#include <dev/acpi/acpivar.h>
+#include <aml/aml_common.h>
+#include <aml/aml_region.h>
+#include <aml/aml_name.h>
+
+#ifndef ACPI_NO_OSDFUNC_INLINE
+#include <machine/acpica_osd.h>
+#endif
+
+/*
+ * Dummy functions for aml_region_io_simple()
+ */
+u_int32_t
+aml_region_prompt_read(struct aml_region_handle *h, u_int32_t value)
+{
+
+ return (value);
+}
+
+u_int32_t
+aml_region_prompt_write(struct aml_region_handle *h, u_int32_t value)
+{
+
+ return (value);
+}
+
+int
+aml_region_prompt_update_value(u_int32_t orgval, u_int32_t value,
+ struct aml_region_handle *h)
+{
+ return (0);
+}
+
+/*
+ * Primitive functions for aml_region_io_simple()
+ */
+int
+aml_region_read_simple(struct aml_region_handle *h, vm_offset_t offset, u_int32_t *valuep)
+{
+ u_int32_t value;
+
+ switch (h->regtype) {
+ case AML_REGION_SYSMEM:
+ /* XXX should be MI */
+ switch (h->unit) {
+ case 1:
+ value = *(volatile u_int8_t *)(h->vaddr + offset);
+ value &= 0xff;
+ break;
+ case 2:
+ value = *(volatile u_int16_t *)(h->vaddr + offset);
+ value &= 0xffff;
+ break;
+ case 4:
+ value = *(volatile u_int32_t *)(h->vaddr + offset);
+ break;
+ }
+ break;
+ case AML_REGION_SYSIO:
+ switch (h->unit) {
+ case 1:
+ value = OsdIn8(h->addr + offset);
+ value &= 0xff;
+ break;
+ case 2:
+ value = OsdIn16(h->addr + offset);
+ value &= 0xffff;
+ break;
+ case 4:
+ value = OsdIn32(h->addr + offset);
+ break;
+ }
+ break;
+ case AML_REGION_PCICFG:
+ switch (h->unit) {
+ case 1:
+ OsdReadPciCfgByte(h->pci_bus, h->pci_devfunc,
+ h->addr + offset, (UINT8 *)&value);
+ value &= 0xff;
+ break;
+ case 2:
+ OsdReadPciCfgWord(h->pci_bus, h->pci_devfunc,
+ h->addr + offset, (UINT16 *)&value);
+ value &= 0xffff;
+ break;
+ case 4:
+ OsdReadPciCfgDword(h->pci_bus, h->pci_devfunc,
+ h->addr + offset, &value);
+ break;
+ }
+ break;
+ default:
+ printf("aml_region_read_simple: not supported yet (%d)\n",
+ h->regtype);
+ value = 0;
+ break;
+ }
+ *valuep = value;
+ return (0);
+}
+
+int
+aml_region_write_simple(struct aml_region_handle *h, vm_offset_t offset, u_int32_t value)
+{
+
+ switch (h->regtype) {
+ case AML_REGION_SYSMEM:
+ /* XXX should be MI */
+ switch (h->unit) {
+ case 1:
+ value &= 0xff;
+ *(volatile u_int8_t *)(h->vaddr + offset) = value;
+ break;
+ case 2:
+ value &= 0xffff;
+ *(volatile u_int16_t *)(h->vaddr + offset) = value;
+ break;
+ case 4:
+ *(volatile u_int32_t *)(h->vaddr + offset) = value;
+ break;
+ }
+ break;
+ case AML_REGION_SYSIO:
+ switch (h->unit) {
+ case 1:
+ value &= 0xff;
+ OsdOut8(h->addr + offset, value);
+ break;
+ case 2:
+ value &= 0xffff;
+ OsdOut16(h->addr + offset, value);
+ break;
+ case 4:
+ OsdOut32(h->addr + offset, value);
+ break;
+ }
+ break;
+ case AML_REGION_PCICFG:
+ switch (h->unit) {
+ case 1:
+ OsdWritePciCfgByte(h->pci_bus, h->pci_devfunc,
+ h->addr + offset, value);
+ break;
+ case 2:
+ OsdWritePciCfgWord(h->pci_bus, h->pci_devfunc,
+ h->addr + offset, value);
+ break;
+ case 4:
+ OsdWritePciCfgDword(h->pci_bus, h->pci_devfunc,
+ h->addr + offset, value);
+ break;
+ }
+ break;
+ default:
+ printf("aml_region_write_simple: not supported yet (%d)\n",
+ h->regtype);
+ break;
+ }
+
+ return (0);
+}
+
+static int
+aml_region_io_buffer(boolean_t io, int regtype, u_int32_t flags,
+ u_int8_t *buffer, u_int32_t baseaddr, u_int32_t bitoffset, u_int32_t bitlen)
+{
+ vm_offset_t addr, vaddr;
+ size_t len;
+ const char *funcname[] = {
+ "aml_region_read_into_buffer",
+ "aml_region_write_from_buffer"
+ };
+
+ if (regtype != AML_REGION_SYSMEM) {
+ printf("%s: region type isn't system memory!\n", funcname[io]);
+ return (-1);
+ }
+
+ if (bitlen % 8) {
+ printf("%s: bit length isn't a multiple of 8!\n", funcname[io]);
+ }
+ if (bitoffset % 8) {
+ printf("%s: bit offset isn't a multiple of 8!\n", funcname[io]);
+ }
+
+ addr = baseaddr + bitoffset / 8;
+ len = bitlen / 8 + ((bitlen % 8) ? 1 : 0);
+
+ OsdMapMemory((void *)addr, len, (void **)&vaddr);
+
+ switch (io) {
+ case AML_REGION_INPUT:
+ bcopy((void *)vaddr, (void *)buffer, len);
+ break;
+ case AML_REGION_OUTPUT:
+ bcopy((void *)buffer, (void *)vaddr, len);
+ break;
+ }
+
+ OsdUnMapMemory((void *)vaddr, len);
+
+ return (0);
+}
+
+u_int32_t
+aml_region_read(struct aml_environ *env, int regtype, u_int32_t flags,
+ u_int32_t addr, u_int32_t bitoffset, u_int32_t bitlen)
+{
+ int value;
+ int state;
+
+ AML_REGION_READ_DEBUG(regtype, flags, addr, bitoffset, bitlen);
+
+ state = aml_region_io(env, AML_REGION_INPUT, regtype,
+ flags, &value, addr, bitoffset, bitlen);
+ AML_SYSASSERT(state != -1);
+
+ return (value);
+}
+
+int
+aml_region_read_into_buffer(struct aml_environ *env, int regtype,
+ u_int32_t flags, u_int32_t addr, u_int32_t bitoffset, u_int32_t bitlen,
+ u_int8_t *buffer)
+{
+ int state;
+
+ AML_REGION_READ_INTO_BUFFER_DEBUG(regtype, flags, addr, bitoffset, bitlen);
+ state = aml_region_io_buffer(AML_REGION_INPUT, regtype, flags,
+ buffer, addr, bitoffset, bitlen);
+
+ return (state);
+}
+
+int
+aml_region_write(struct aml_environ *env, int regtype, u_int32_t flags,
+ u_int32_t value, u_int32_t addr, u_int32_t bitoffset, u_int32_t bitlen)
+{
+ int state;
+
+ AML_REGION_WRITE_DEBUG(regtype, flags, value, addr, bitoffset, bitlen);
+
+ state = aml_region_io(env, AML_REGION_OUTPUT, regtype,
+ flags, &value, addr, bitoffset, bitlen);
+ AML_SYSASSERT(state != -1);
+
+ return (state);
+}
+
+int
+aml_region_write_from_buffer(struct aml_environ *env, int regtype,
+ u_int32_t flags, u_int8_t *buffer, u_int32_t addr, u_int32_t bitoffset,
+ u_int32_t bitlen)
+{
+ int state;
+
+ AML_REGION_WRITE_FROM_BUFFER_DEBUG(regtype, flags,
+ addr, bitoffset, bitlen);
+
+ state = aml_region_io_buffer(AML_REGION_OUTPUT, regtype, flags,
+ buffer, addr, bitoffset, bitlen);
+
+ return (state);
+}
+
+int
+aml_region_bcopy(struct aml_environ *env, int regtype,
+ u_int32_t flags, u_int32_t addr, u_int32_t bitoffset, u_int32_t bitlen,
+ u_int32_t dflags, u_int32_t daddr, u_int32_t dbitoffset, u_int32_t dbitlen)
+{
+ vm_offset_t from_addr, from_vaddr;
+ vm_offset_t to_addr, to_vaddr;
+ size_t len;
+
+ AML_REGION_BCOPY_DEBUG(regtype, flags, addr, bitoffset, bitlen,
+ dflags, daddr, dbitoffset, dbitlen);
+
+ if (regtype != AML_REGION_SYSMEM) {
+ printf("aml_region_bcopy: region type isn't system memory!\n");
+ return (-1);
+ }
+
+ if ((bitlen % 8) || (dbitlen % 8)) {
+ printf("aml_region_bcopy: bit length isn't a multiple of 8!\n");
+ }
+ if ((bitoffset % 8) || (dbitoffset % 8)) {
+ printf("aml_region_bcopy: bit offset isn't a multiple of 8!\n");
+ }
+
+ from_addr = addr + bitoffset / 8;
+ to_addr = daddr + dbitoffset / 8;
+
+ len = (bitlen > dbitlen) ? dbitlen : bitlen;
+ len = len / 8 + ((len % 8) ? 1 : 0);
+
+ OsdMapMemory((void *)from_addr, len, (void **)&from_vaddr);
+ OsdMapMemory((void *)to_addr, len, (void **)&to_vaddr);
+
+ bcopy((void *)from_vaddr, (void *)to_vaddr, len);
+
+ OsdUnMapMemory((void *)from_vaddr, len);
+ OsdUnMapMemory((void *)to_vaddr, len);
+
+ return (0);
+}
diff --git a/usr.sbin/acpi/amldb/aml/aml_region.h b/usr.sbin/acpi/amldb/aml/aml_region.h
new file mode 100644
index 0000000..caa3f6c
--- /dev/null
+++ b/usr.sbin/acpi/amldb/aml/aml_region.h
@@ -0,0 +1,92 @@
+/*-
+ * Copyright (c) 2000 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: aml_region.h,v 1.5 2000/08/08 14:12:05 iwasaki Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _AML_REGION_H_
+#define _AML_REGION_H_
+
+/*
+ * Note that common part of region I/O is implemented in aml_common.c.
+ */
+
+/*
+ * Debug macros for region I/O
+ */
+
+#define AML_REGION_READ_DEBUG(regtype, flags, addr, bitoffset, bitlen) \
+ AML_DEBUGPRINT("\n[aml_region_read(%d, %d, 0x%x, 0x%x, 0x%x)]\n",\
+ regtype, flags, addr, bitoffset, bitlen)
+
+#define AML_REGION_READ_INTO_BUFFER_DEBUG(regtype, flags, \
+ addr, bitoffset, bitlen) \
+ AML_DEBUGPRINT("\n[aml_region_read_into_buffer(%d, %d, 0x%x, 0x%x, 0x%x)]\n",\
+ regtype, flags, addr, bitoffset, bitlen)
+
+#define AML_REGION_WRITE_DEBUG(regtype, flags, value, \
+ addr, bitoffset, bitlen) \
+ AML_DEBUGPRINT("\n[aml_region_write(%d, %d, 0x%x, 0x%x, 0x%x, 0x%x)]\n",\
+ regtype, flags, value, addr, bitoffset, bitlen)
+
+#define AML_REGION_WRITE_FROM_BUFFER_DEBUG(regtype, flags, \
+ addr, bitoffset, bitlen) \
+ AML_DEBUGPRINT("\n[aml_region_write_from_buffer(%d, %d, 0x%x, 0x%x, 0x%x)]\n",\
+ regtype, flags, addr, bitoffset, bitlen)
+
+#define AML_REGION_BCOPY_DEBUG(regtype, flags, addr, bitoffset, bitlen, \
+ dflags, daddr, dbitoffset, dbitlen) \
+ AML_DEBUGPRINT("\n[aml_region_bcopy(%d, %d, 0x%x, 0x%x, 0x%x, %d, 0x%x, 0x%x, 0x%x)]\n",\
+ regtype, flags, addr, bitoffset, bitlen, \
+ dflags, daddr, dbitoffset, dbitlen)
+
+/*
+ * Region I/O subroutine
+ */
+
+struct aml_environ;
+
+u_int32_t aml_region_read(struct aml_environ *, int, u_int32_t,
+ u_int32_t, u_int32_t, u_int32_t);
+int aml_region_write(struct aml_environ *, int, u_int32_t,
+ u_int32_t, u_int32_t, u_int32_t, u_int32_t);
+int aml_region_read_into_buffer(struct aml_environ *, int,
+ u_int32_t, u_int32_t, u_int32_t,
+ u_int32_t, u_int8_t *);
+int aml_region_write_from_buffer(struct aml_environ *, int,
+ u_int32_t, u_int8_t *, u_int32_t,
+ u_int32_t, u_int32_t);
+int aml_region_bcopy(struct aml_environ *, int,
+ u_int32_t, u_int32_t, u_int32_t, u_int32_t,
+ u_int32_t, u_int32_t, u_int32_t, u_int32_t);
+
+#ifndef _KERNEL
+void aml_simulation_regdump(const char *);
+extern int aml_debug_prompt_regoutput;
+extern int aml_debug_prompt_reginput;
+#endif /* !_KERNEL */
+
+#endif /* !_AML_REGION_H_ */
diff --git a/usr.sbin/acpi/amldb/aml/aml_status.h b/usr.sbin/acpi/amldb/aml/aml_status.h
new file mode 100644
index 0000000..cbb601c
--- /dev/null
+++ b/usr.sbin/acpi/amldb/aml/aml_status.h
@@ -0,0 +1,41 @@
+/*-
+ * Copyright (c) 1999 Takanori Watanabe
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: aml_status.h,v 1.6 2000/08/08 14:12:05 iwasaki Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _AML_STATUS_H_
+#define _AML_STATUS_H_
+
+enum aml_status {
+ aml_stat_none = 0,
+ aml_stat_return,
+ aml_stat_break,
+ aml_stat_panic,
+ aml_stat_step
+};
+
+#endif /* !_AML_STATUS_H_ */
diff --git a/usr.sbin/acpi/amldb/aml/aml_store.c b/usr.sbin/acpi/amldb/aml/aml_store.c
new file mode 100644
index 0000000..5a49db4
--- /dev/null
+++ b/usr.sbin/acpi/amldb/aml/aml_store.c
@@ -0,0 +1,349 @@
+/*-
+ * Copyright (c) 1999 Takanori Watanabe
+ * Copyright (c) 1999, 2000 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: aml_store.c,v 1.22 2000/08/09 14:47:44 iwasaki Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+
+#include <aml/aml_amlmem.h>
+#include <aml/aml_common.h>
+#include <aml/aml_env.h>
+#include <aml/aml_evalobj.h>
+#include <aml/aml_name.h>
+#include <aml/aml_obj.h>
+#include <aml/aml_region.h>
+#include <aml/aml_status.h>
+#include <aml/aml_store.h>
+
+#ifndef _KERNEL
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "debug.h"
+#else /* _KERNEL */
+#include <sys/systm.h>
+#endif /* !_KERNEL */
+
+static void
+aml_store_to_fieldname(struct aml_environ *env, union aml_object *obj,
+ struct aml_name *name)
+{
+ char *buffer;
+ struct aml_name *wname, *oname, *iname;
+ struct aml_field *field;
+ struct aml_opregion *or;
+ union aml_object tobj, iobj, *tmpobj;
+
+ field = &name->property->field;
+ oname = env->curname;
+ iname = NULL;
+ env->curname = name->parent;
+ if (field->f.ftype == f_t_field) {
+ wname = aml_search_name(env, field->f.fld.regname);
+ if (wname == NULL ||
+ wname->property == NULL ||
+ wname->property->type != aml_t_opregion) {
+ AML_DEBUGPRINT("Inappropreate Type\n");
+ env->stat = aml_stat_panic;
+ env->curname = oname;
+ return;
+ }
+ or = &wname->property->opregion;
+ switch (obj->type) {
+ case aml_t_num:
+ aml_region_write(env, or->space, field->flags,
+ obj->num.number, or->offset,
+ field->bitoffset, field->bitlen);
+ AML_DEBUGPRINT("[write(%d, 0x%x, 0x%x)]",
+ or->space, obj->num.number,
+ or->offset + field->bitoffset / 8);
+ break;
+ case aml_t_buffer:
+ case aml_t_bufferfield:
+ if (obj->type == aml_t_buffer) {
+ buffer = obj->buffer.data;
+ } else {
+ buffer = obj->bfld.origin;
+ buffer += obj->bfld.bitoffset / 8;
+ }
+ aml_region_write_from_buffer(env, or->space,
+ field->flags, buffer, or->offset, field->bitoffset,
+ field->bitlen);
+ break;
+ case aml_t_regfield:
+ if (or->space != obj->regfield.space) {
+ AML_DEBUGPRINT("aml_store_to_fieldname: "
+ "Different type of space\n");
+ break;
+ }
+ aml_region_bcopy(env, obj->regfield.space,
+ obj->regfield.flags, obj->regfield.offset,
+ obj->regfield.bitoffset, obj->regfield.bitlen,
+ field->flags, or->offset, field->bitoffset,
+ field->bitlen);
+ break;
+ default:
+ AML_DEBUGPRINT("aml_store_to_fieldname: "
+ "Inappropreate Type of src object\n");
+ break;
+ }
+ } else if (field->f.ftype == f_t_index) {
+ iname = aml_search_name(env, field->f.ifld.indexname);
+ wname = aml_search_name(env, field->f.ifld.dataname);
+ iobj.type = aml_t_num;
+ iobj.num.number = field->bitoffset / 8; /* AccessType Boundary */
+
+ /* read whole values of IndexField */
+ aml_store_to_name(env, &iobj, iname);
+ tmpobj = aml_eval_name(env, wname);
+
+ /* make the values to be written */
+ tobj.num = obj->num;
+ tobj.num.number = aml_adjust_updatevalue(field->flags,
+ field->bitoffset & 7, field->bitlen,
+ tmpobj->num.number, obj->num.number);
+
+ /* write the values to IndexField */
+ aml_store_to_name(env, &iobj, iname);
+ aml_store_to_name(env, &tobj, wname);
+ }
+ env->curname = oname;
+}
+
+static void
+aml_store_to_buffer(struct aml_environ *env, union aml_object *obj,
+ union aml_object *buf, int offset)
+{
+ int size;
+ int bitlen;
+
+ switch (obj->type) {
+ case aml_t_num:
+ if (offset > buf->buffer.size) {
+ aml_realloc_object(buf, offset);
+ }
+ buf->buffer.data[offset] = obj->num.number & 0xff;
+ AML_DEBUGPRINT("[Store number 0x%x to buffer]",
+ obj->num.number & 0xff);
+ break;
+ case aml_t_string:
+ size = strlen(obj->str.string);
+ if (buf->buffer.size - offset < size) {
+ aml_realloc_object(buf, offset + size + 1);
+ }
+ strcpy(&buf->buffer.data[offset], obj->str.string);
+ AML_DEBUGPRINT("[Store string to buffer]");
+ break;
+ case aml_t_buffer:
+ bzero(buf->buffer.data, buf->buffer.size);
+ if (obj->buffer.size > buf->buffer.size) {
+ size = buf->buffer.size;
+ } else {
+ size = obj->buffer.size;
+ }
+ bcopy(obj->buffer.data, buf->buffer.data, size);
+ break;
+ case aml_t_regfield:
+ bitlen = (buf->buffer.size - offset) * 8;
+ if (bitlen > obj->regfield.bitlen) {
+ bitlen = obj->regfield.bitlen;
+ }
+ aml_region_read_into_buffer(env, obj->regfield.space,
+ obj->regfield.flags, obj->regfield.offset,
+ obj->regfield.bitoffset, bitlen,
+ buf->buffer.data + offset);
+ break;
+ default:
+ goto not_yet;
+ }
+ return;
+not_yet:
+ AML_DEBUGPRINT("[XXX not supported yet]");
+}
+
+
+void
+aml_store_to_object(struct aml_environ *env, union aml_object *src,
+ union aml_object * dest)
+{
+ char *buffer, *srcbuf;
+ int offset, bitlen;
+
+ switch (dest->type) {
+ case aml_t_num:
+ if (src->type == aml_t_num) {
+ dest->num = src->num;
+ AML_DEBUGPRINT("[Store number 0x%x]", src->num.number);
+ } else {
+ env->stat = aml_stat_panic;
+ }
+ break;
+ case aml_t_string:
+ case aml_t_package:
+ break;
+ case aml_t_buffer:
+ aml_store_to_buffer(env, src, dest, 0);
+ break;
+ case aml_t_bufferfield:
+ buffer = dest->bfld.origin;
+ offset = dest->bfld.bitoffset;
+ bitlen = dest->bfld.bitlen;
+
+ switch (src->type) {
+ case aml_t_num:
+ if (aml_bufferfield_write(src->num.number, buffer, offset, bitlen)) {
+ AML_DEBUGPRINT("aml_bufferfield_write() failed\n");
+ }
+ break;
+ case aml_t_buffer:
+ case aml_t_bufferfield:
+ if (src->type == aml_t_buffer) {
+ srcbuf = src->buffer.data;
+ } else {
+ srcbuf = src->bfld.origin;
+ srcbuf += src->bfld.bitoffset / 8;
+ }
+ bcopy(srcbuf, buffer, bitlen / 8);
+ break;
+ case aml_t_regfield:
+ aml_region_read_into_buffer(env, src->regfield.space,
+ src->regfield.flags, src->regfield.offset,
+ src->regfield.bitoffset, src->regfield.bitlen,
+ buffer);
+ break;
+ default:
+ AML_DEBUGPRINT("not implemented yet");
+ break;
+ }
+ break;
+ case aml_t_debug:
+ aml_showobject(src);
+ break;
+ default:
+ AML_DEBUGPRINT("[Unimplemented %d]", dest->type);
+ break;
+ }
+}
+
+static void
+aml_store_to_objref(struct aml_environ *env, union aml_object *obj,
+ union aml_object *r)
+{
+ int offset;
+ union aml_object *ref;
+
+ if (r->objref.ref == NULL) {
+ r->objref.ref = aml_alloc_object(obj->type, NULL); /* XXX */
+ r->objref.nameref->property = r->objref.ref;
+ }
+ ref = r->objref.ref;
+
+ switch (ref->type) {
+ case aml_t_buffer:
+ offset = r->objref.offset;
+ aml_store_to_buffer(env, obj, ref, r->objref.offset);
+ break;
+ case aml_t_package:
+ offset = r->objref.offset;
+ if (r->objref.ref->package.elements < offset) {
+ aml_realloc_object(ref, offset);
+ }
+ if (ref->package.objects[offset] == NULL) {
+ ref->package.objects[offset] = aml_copy_object(env, obj);
+ } else {
+ aml_store_to_object(env, obj, ref->package.objects[offset]);
+ }
+ break;
+ default:
+ aml_store_to_object(env, obj, ref);
+ break;
+ }
+}
+
+/*
+ * Store to Named object
+ */
+void
+aml_store_to_name(struct aml_environ *env, union aml_object *obj,
+ struct aml_name *name)
+{
+ struct aml_name *wname;
+
+ if (env->stat == aml_stat_panic) {
+ return;
+ }
+ if (name == NULL || obj == NULL) {
+ AML_DEBUGPRINT("[Try to store no existant name ]");
+ return;
+ }
+ if (name->property == NULL) {
+ name->property = aml_copy_object(env, obj);
+ AML_DEBUGPRINT("[Copy number 0x%x]", obj->num.number);
+ return;
+ }
+ if (name->property->type == aml_t_namestr) {
+ wname = aml_search_name(env, name->property->nstr.dp);
+ name = wname;
+ }
+ if (name == NULL) {
+ env->stat = aml_stat_panic;
+ return;
+ }
+ if (name->property == NULL || name->property->type == aml_t_null) {
+ name->property = aml_copy_object(env, obj);
+ AML_DEBUGPRINT("[Copy number 0x%x]", obj->num.number);
+ return;
+ }
+ /* Writes to constant object are not allowed */
+ if (name->property != NULL && name->property->type == aml_t_num &&
+ name->property->num.constant == 1) {
+ return;
+ }
+ /* try to dereference */
+ if (obj->type == aml_t_objref && obj->objref.deref == 0) {
+ AML_DEBUGPRINT("Source object isn't dereferenced yet, "
+ "try to dereference anyway\n");
+ obj->objref.deref = 1;
+ obj = aml_eval_objref(env, obj);
+ }
+ switch (name->property->type) {
+ case aml_t_field:
+ aml_store_to_fieldname(env, obj, name);
+ break;
+ case aml_t_objref:
+ aml_store_to_objref(env, obj, name->property);
+ break;
+ case aml_t_num:
+ if (name == &env->tempname)
+ break;
+ default:
+ aml_store_to_object(env, obj, name->property);
+ break;
+ }
+}
diff --git a/usr.sbin/acpi/amldb/aml/aml_store.h b/usr.sbin/acpi/amldb/aml/aml_store.h
new file mode 100644
index 0000000..c41fb8c
--- /dev/null
+++ b/usr.sbin/acpi/amldb/aml/aml_store.h
@@ -0,0 +1,39 @@
+/*-
+ * Copyright (c) 1999 Takanori Watanabe
+ * Copyright (c) 1999, 2000 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: aml_store.h,v 1.8 2000/08/09 14:47:44 iwasaki Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _AML_STORE_H_
+#define _AML_STORE_H_
+
+void aml_store_to_name(struct aml_environ *, union aml_object *,
+ struct aml_name *);
+void aml_store_to_object(struct aml_environ *, union aml_object *,
+ union aml_object *);
+
+#endif /* _AML_STORE_H_ */
diff --git a/usr.sbin/acpi/amldb/amldb.8 b/usr.sbin/acpi/amldb/amldb.8
new file mode 100644
index 0000000..d10e900
--- /dev/null
+++ b/usr.sbin/acpi/amldb/amldb.8
@@ -0,0 +1,336 @@
+.\" ACPI (ACPI Package)
+.\"
+.\" Copyright (c) 2000 Takanori Watanabe <takawata@FreeBSD.org>
+.\" Copyright (c) 2000 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
+.\" Copyright (c) 2000 Yasuo YOKOYAMA <yokoyama@jp.FreeBSD.org>
+.\" Copyright (c) 2000 Norihiro KUMAGAI <kumagai@home.com>
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd August 31, 2000
+.Dt AMLDB 8
+.Os
+.Sh NAME
+.Nm amldb
+.Nd executing and debugging AML interpreter
+(with DSDT files)
+.Sh SYNOPSIS
+.Nm
+.Op Fl dhst
+.Ar dsdt_file ...
+.Sh DESCRIPTION
+.Nm Amldb
+parses the DSDT
+(Differentiated System Description Table)
+files, which usually are acquired from ACPI BIOS, and executes
+the sequence of ACPI Control Methods described in AML
+(ACPI Machine Language)
+with its AML interpreter.
+.Nm Amldb
+also has a simple ACPI virtual machine. During execution of the
+Control Methods each access to the region, such as
+SystemMemory, SystemIO, PCI_Config, does not affect the real
+hardware but only the virtual machine.
+Because the sequence of virtual accesses is maintained in user space,
+AML interpreter developers need not worry about any effect on hardware
+when they analyze DSDT data files. They can develop and debug the
+interpreter, even if the machine has no ACPI BIOS.
+.Pp
+The developer will need to acquire a DSDT data file from any machine
+with ACPI BIOS through
+.Xr acpidump 8 .
+The DSDT is a table, a part of the whole ACPI memory table
+located in somewhere in the BIOS area
+.Pq Li 0xa0000 \- 0x100000 .
+It includes such information as the detailed hardware information
+for PnP, and the set of procedures which perform power management from
+the OS. The information is stored in AML format.
+.Pp
+The AML interpreter can execute any of the Control Methods specified
+by users. When executed, it interprets the byte sequence in the
+Control Method of DSDT, and disassembles the opecodes that it
+recognizes into ASL
+(ACPI Source Language)
+format to be displayed.
+.Pp
+If it encounters one of more accesses to the region such as
+SystemMemory in executing the Control Methods, its ACPI Virtual
+Machine simulates the input/output operations to the resources in the
+region. In writing to a certain region, the ACPI Virtual Machine
+prepares a piece of memory corresponding to its address,
+if necessary, and holds the specified value in the memory as the
+.Em region contents .
+In reading from a certain region, it fetches the value in the memory
+.Pq Em region contents ,
+prompts with it as the following:
+.Bd -literal -offset indent
+DEBUG[read(0, 0x100b6813)&mask:0x1](default: 0x1 / 1) >>
+.Ed
+.Pp
+for users to have the opportunity to modify it, and hands it to
+the AML interpreter. In case that there is no corresponding region
+in the AML Virtual Machine, the value zero is handed.
+.Pp
+The interpreter continues to maintain all of the
+.Em region contents
+until
+.Nm
+terminates. You can specify their initial values with the file
+.Pa region.ini
+in the current directory. If it is executed with
+.Fl d
+option, it dumps the final status of all of its
+.Em region contents
+to the file
+.Pa region.dmp
+when it terminates. Each line of there files consists of the following
+fields, separated by tabs; region type, address, and value.
+Region types are specified as follows;
+.TS H
+box;
+c | l.
+value region type
+=
+0 SystemMemory
+1 SystemIO
+2 PCI_Concig
+3 EmbeddedControl
+4 SMBus
+.TE
+.Pp
+Interactive commands are described below:
+.Bl -tag -width indent
+.It Cm s
+.Em Single step :
+Performs single-step execution of the current Control Method. If
+the next instruction is an invocation of another Control Method,
+the step execution will continue in the following Control Method.
+.It Cm n
+.Em Step program :
+Performs single-step execution of the current Control Method.
+Even if the next instruction is an invocation of another Control
+Method, the step execution will not continue.
+.It Cm c
+.Em Continue program being debugged :
+Resumes execution of the AML interpreter. Because the current
+.Nm
+has no way of breakpoint, this command might not so much useful.
+.It Cm q
+.Em Quit method execution :
+Terminates execution of the current Control Method. If
+.Nm
+is not in execution, this command causes to input the next
+DSDT data file. If there are no next DSDT data files, it
+terminates
+.Nm
+itself.
+.It Cm t
+.Em Show local name space tree and variables :
+Displays the structure of the ACPI namespace tree. If
+.Nm
+is in execution, this command displays the structure that relates
+to the objects, arguments, and local variables below the scope of the
+current Control Method.
+.It Cm i
+.Em Toggle region input prompt :
+Switches whether the prompt for modifying the value read from the
+.Em region contents
+be showed or not. Default is On.
+.It Cm o
+.Em Toggle region output prompt :
+Switches whether the prompt for modifying the value to be written
+to the region contents will be shown or not. The default is Off.
+.It Cm m
+.Em Show memory management statistics :
+Displays the current statistics of the memory management system
+on the AML interpreter.
+.It Cm r Ar method
+.Em Run specified method :
+Executes the specified Control Method. If it requires one or
+more arguments, a prompt such as the following appears;
+.Bd -literal
+Method: Arg 1 From 0x280626ce To 0x28062775
+ Enter argument values (ex. number 1 / string foo). 'q' to quit.
+ Arg0 ?
+.Ed
+.Pp
+For each argument, a pair of type string and value delimited by
+one or more spaces can be entered. Now only
+.Ic number
+and
+.Ic string
+can be specified as the type string.
+In the current implementation, only the first character of the type
+string, such as
+.Ic n
+or
+.Ic s ,
+is identified. For example, we can enter as follows:
+.Bd -literal
+ Arg0 ? n 1
+.Ed
+.Pp
+.It Cm f Ar string
+.Em Find named objects from namespace :
+Lists the named objects that includes the specified string as the
+terminate elements searching from the ACPI namespace. For the
+namespace is expressed as the sequence of four-character elements,
+appropriate number of additional underscore
+.Pq Ql _
+characters are necessary for specifying objects which have less than four
+character string. Unless additional underscores specified, matching
+occurs as the beginning of word with the specified number of characters.
+.It Cm h
+.Em Show help messsage :
+Displays the command summary of
+.Nm .
+.El
+.Sh OPTIONS
+Exactly one of the following options must be specified. Otherwise,
+.Nm
+shows its usage and terminates.
+.Bl -tag -width indent
+.It Fl d
+Dump the final status of all of the
+.Em region contents
+in the ACPI Virtual Machine to the file
+.Pa region.dmp .
+.It Fl h
+Terminate with the usage of this command.
+.It Fl s
+Display the statistics of the memory management system on the
+AML interpreter when
+.Nm
+terminates.
+.It Fl t
+Display the tree structure of ACPI namespace after the
+DSDT data file is read.
+.El
+.Sh EXAMPLES
+The following is an example including, invoking the
+.Nm ,
+searching
+.Li _PRS
+(Possible Resource Settings)
+objects, and executing the
+.Li _PTS
+(Prepare To Sleep)
+Control Method by the AML interpreter.
+.Bd -literal -offset indent
+% amldb p2b.dsdt.dat
+Loading p2b.dsdt.dat...done
+AML>f _PRS
+\\_SB_.PCI0.ISA_.PS2M._PRS.
+\\_SB_.PCI0.ISA_.IRDA._PRS.
+\\_SB_.PCI0.ISA_.UAR2._PRS.
+\\_SB_.PCI0.ISA_.UAR1._PRS.
+\\_SB_.PCI0.ISA_.ECP_._PRS.
+\\_SB_.PCI0.ISA_.LPT_._PRS.
+\\_SB_.PCI0.ISA_.FDC0._PRS.
+\\_SB_.LNKD._PRS.
+\\_SB_.LNKC._PRS.
+\\_SB_.LNKB._PRS.
+\\_SB_.LNKA._PRS.
+AML>r _PTS
+Method: Arg 1 From 0x2805f0a3 To 0x2805f0db
+ Enter argument values (ex. number 1 / string foo). 'q' to quit.
+ Arg0 ? n 5
+==== Running _PTS. ====
+AML>s
+[\_PTS. START]
+If(LNot(LEqual(Arg0, 0x5)))
+AML>
+If(LEqual(Arg0, 0x1))
+AML>
+If(LEqual(Arg0, 0x2))
+AML>
+Store(One, TO12)
+[aml_region_write(1, 1, 0x1, 0xe42c, 0x18, 0x1)]
+amldb: region.ini: No such file or directory
+ [1:0x00@0xe42f]->[1:0x01@0xe42f]
+[write(1, 0x1, 0xe42f)]
+[aml_region_read(1, 1, 0xe42c, 0x18, 0x1)]
+ [1:0x01@0xe42f]
+DEBUG[read(1, 0xe42f)&mask:0x1](default: 0x1 / 1) >>
+[read(1, 0xe42f)->0x1]
+AML>
+Or(Arg0, 0xf0, Local2)[Copy number 0xf5]
+AML>t
+_PTS Method: Arg 1 From 0x2805f0a3 To 0x2805f0db
+ Arg0 Num:0x5
+ Local2 Num:0xf5
+AML>s
+Store(Local2, DBG1)
+[aml_region_write(1, 1, 0xf5, 0x80, 0x0, 0x8)]
+ [1:0x00@0x80]->[1:0xf5@0x80]
+[write(1, 0xf5, 0x80)]
+[aml_region_read(1, 1, 0x80, 0x0, 0x8)]
+ [1:0xf5@0x80]
+DEBUG[read(1, 0x80)&mask:0xf5](default: 0xf5 / 245) >>
+[read(1, 0x80)->0xf5]
+AML>
+[\_PTS. END]
+_PTS Method: Arg 1 From 0x2805f0a3 To 0x2805f0db
+NO object
+==== _PTS finished. ====
+AML>q
+%
+.Ed
+.Sh BUGS
+The ACPI virtual machine does not completely simulate the behavior
+of a machine with an ACPI BIOS. In the current implementation, the
+ACPI virtual machine only reads or writes the stored values by
+emulating access to regions such as SystemMemory.
+.Pp
+Because the AML interpreter interprets and disassembles
+simultaneously, it is impossible to implement such features as setting
+breakpoints with the specified line number in ASL. Setting breakpoints
+at certain Control Methods, which is not very difficult, has not
+yet implemented because nobody has ever needed it.
+.Sh FILES
+.Bl -tag -width region.ini -compact
+.It Pa region.ini
+.It Pa region.dmp
+.El
+.Sh SEE ALSO
+.Xr acpi 4 ,
+.Xr acpiconf 8 ,
+.Xr acpidump 8
+.Sh AUTHORS
+.An Takanori Watanabe Aq takawata@FreeBSD.org
+.An Mitsuru IWASAKI Aq iwasaki@FreeBSD.org
+.An Yasuo YOKOYAMA Aq yokoyama@jp.FreeBSD.org
+.Pp
+Some contributions made by
+.An Chitoshi Ohsawa Aq ohsawa@catv1.ccn-net.ne.jp ,
+.An Takayasu IWANASHI Aq takayasu@wendy.a.perfect-liberty.or.jp ,
+.An Norihiro KUMAGAI Aq kumagai@home.com ,
+.An Kenneth Ingham Aq ingham@I-pi.com ,
+and
+.An Michael Lucas Aq mwlucas@blackhelicopters.org .
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Fx 5.0 .
diff --git a/usr.sbin/acpi/amldb/amldb.c b/usr.sbin/acpi/amldb/amldb.c
new file mode 100644
index 0000000..c35d5a8
--- /dev/null
+++ b/usr.sbin/acpi/amldb/amldb.c
@@ -0,0 +1,185 @@
+/*-
+ * Copyright (c) 1999 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: amldb.c,v 1.8 2000/08/08 14:12:24 iwasaki Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include <aml/aml_amlmem.h>
+#include <aml/aml_common.h>
+#include <aml/aml_env.h>
+#include <aml/aml_parse.h>
+#include <aml/aml_region.h>
+
+#include <assert.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "debug.h"
+
+int regdump_enabled = 0;
+int memstat_enabled = 0;
+int showtree_enabled = 0;
+
+static void aml_init_namespace();
+
+void
+aml_init_namespace()
+{
+ struct aml_environ env;
+ struct aml_name *newname;
+
+ aml_new_name_group(AML_NAME_GROUP_OS_DEFINED);
+ env.curname = aml_get_rootname();
+ newname = aml_create_name(&env, "\\_OS_");
+ newname->property = aml_alloc_object(aml_t_string, NULL);
+ newname->property->str.needfree = 0;
+ newname->property->str.string = "Microsoft Windows NT";
+}
+
+static int
+load_dsdt(const char *dsdtfile)
+{
+ struct aml_environ env;
+ u_int8_t *code;
+ struct stat sb;
+ int fd;
+
+ printf("Loading %s...", dsdtfile);
+
+ fd = open(dsdtfile, O_RDONLY, 0);
+ if (fd == -1) {
+ perror("open");
+ exit(-1);
+ }
+ if (fstat(fd, &sb) == -1) {
+ perror("fstat");
+ exit(-1);
+ }
+ if ((code = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == NULL) {
+ perror("mmap");
+ exit(-1);
+ }
+ aml_init_namespace();
+
+ aml_new_name_group((int)code);
+ bzero(&env, sizeof(env));
+
+#define SIZEOF_SDT_HDR 36 /* struct size except body */
+ if (strncmp(code, "DSDT", 4) == 0) {
+ env.dp = code + SIZEOF_SDT_HDR;
+ } else {
+ env.dp = code;
+ }
+ env.end = code + sb.st_size;
+ env.curname = aml_get_rootname();
+
+ aml_local_stack_push(aml_local_stack_create());
+ aml_parse_objectlist(&env, 0);
+ aml_local_stack_delete(aml_local_stack_pop());
+
+ assert(env.dp == env.end);
+ env.dp = code;
+ env.end = code + sb.st_size;
+
+ printf("done\n");
+
+ aml_debug = 1; /* debug print enabled */
+
+ if (showtree_enabled == 1) {
+ aml_showtree(env.curname, 0);
+ }
+ do {
+ aml_dbgr(&env, &env);
+ } while (env.stat != aml_stat_panic);
+
+ aml_debug = 0; /* debug print disabled */
+
+ if (regdump_enabled == 1) {
+ aml_simulation_regdump("region.dmp");
+ }
+ while (name_group_list->id != AML_NAME_GROUP_ROOT) {
+ aml_delete_name_group(name_group_list);
+ }
+
+ if (memstat_enabled == 1) {
+ memman_statistics(aml_memman);
+ }
+ memman_freeall(aml_memman);
+
+ return (0);
+}
+
+static void
+usage(const char *progname)
+{
+
+ printf("usage: %s [-d] [-s] [-t] [-h] dsdt_files...\n", progname);
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ char c, *progname;
+ int i;
+
+ progname = argv[0];
+ while ((c = getopt(argc, argv, "dsth")) != -1) {
+ switch (c) {
+ case 'd':
+ regdump_enabled = 1;
+ break;
+ case 's':
+ memstat_enabled = 1;
+ break;
+ case 't':
+ showtree_enabled = 1;
+ break;
+ case 'h':
+ default:
+ usage(progname);
+ /* NOTREACHED */
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 0) {
+ usage(progname);
+ }
+ for (i = 0; i < argc; i++) {
+ load_dsdt(argv[i]);
+ }
+
+ return (0);
+}
diff --git a/usr.sbin/acpi/amldb/debug.c b/usr.sbin/acpi/amldb/debug.c
new file mode 100644
index 0000000..cf5b7d3
--- /dev/null
+++ b/usr.sbin/acpi/amldb/debug.c
@@ -0,0 +1,310 @@
+/*-
+ * Copyright (c) 1999 Takanori Watanabe
+ * Copyright (c) 1999, 2000 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: debug.c,v 1.19 2000/08/16 18:15:00 iwasaki Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+
+#include <aml/aml_name.h>
+#include <aml/aml_amlmem.h>
+#include <aml/aml_status.h>
+#include <aml/aml_env.h>
+#include <aml/aml_obj.h>
+#include <aml/aml_evalobj.h>
+#include <aml/aml_parse.h>
+#include <aml/aml_region.h>
+#include <aml/aml_store.h>
+#include <aml/aml_common.h>
+
+#include <assert.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "debug.h"
+
+static int
+print_named_object(struct aml_name *name, va_list ap)
+{
+
+ aml_print_curname(name);
+ printf("\n");
+
+ return (0); /* always return success to continue the search */
+}
+
+void
+aml_dbgr(struct aml_environ *env1, struct aml_environ *env2)
+{
+#define CMDBUFLEN 512
+#define ARGBUFLEN 512
+ static char lastcommand[CMDBUFLEN];
+ char commandline[CMDBUFLEN];
+ char argbuf[7][ARGBUFLEN];
+ char *ptr, *method;
+ char *np, *ep;
+ int i;
+ int argnum;
+ struct aml_name *name;
+ union aml_object argv[7], *retval;
+
+ while (1) {
+ fputs("AML>", stderr);
+ fgets(commandline, 512, stdin);
+ commandline[512 - 1] = '\n'; /* safety */
+ if (feof(stdin)) {
+ commandline[0] = 'q';
+ }
+ if (commandline[0] == '\n') {
+ memcpy(commandline, lastcommand, sizeof commandline);
+ }
+ memcpy(lastcommand, commandline, sizeof commandline);
+ switch (commandline[0]) {
+ case 's':
+ if (env2 != NULL) {
+ env2->stat = aml_stat_step;
+ }
+ /* FALLTHROUGH */
+ case 'n':
+ env1->stat = aml_stat_step;
+ return;
+ case 'c':
+ env1->stat = aml_stat_none;
+ return;
+ case 'q':
+ env1->stat = aml_stat_panic;
+ return;
+ case 't':
+ /* NULL terminate */
+ ptr = &commandline[1];
+ while (ptr[0] != '\n')
+ ptr++;
+ ptr[0] = '\0';
+
+ /* move pointer to object name */
+ ptr = &commandline[1];
+ while (ptr[0] == ' ')
+ ptr++;
+
+ /* show current tree if no argument */
+ if (ptr[0] == '\0') {
+ aml_showtree(env1->curname, 0);
+ goto show_variables;
+ }
+ /* start from root? */
+ if (ptr[0] == '\\') {
+ if (ptr[1] == '\0') {
+ aml_showtree(aml_get_rootname(), 0);
+ goto show_variables;
+ }
+ if ((name = aml_find_from_namespace(aml_get_rootname(), ptr))) {
+ aml_showtree(name, 0);
+ goto show_variables;
+ }
+ }
+ if ((name = aml_find_from_namespace(env1->curname, ptr))) {
+ aml_showtree(name, 0);
+ }
+show_variables:
+ for (i = 0; i < 7; i++) {
+ struct aml_name *tmp =
+ aml_local_stack_getArgX(NULL, i);
+
+ if (tmp == NULL || tmp->property == NULL) {
+ break;
+ }
+ printf(" Arg%d ", i);
+ aml_showobject(tmp->property);
+ }
+ for (i = 0; i < 8; i++) {
+ struct aml_name *tmp =
+ aml_local_stack_getLocalX(i);
+
+ if (tmp == NULL || tmp->property == NULL) {
+ continue;
+ }
+ printf(" Local%d ", i);
+ aml_showobject(tmp->property);
+ }
+ break;
+ case 'i':
+ aml_debug_prompt_reginput =
+ (aml_debug_prompt_reginput == 0) ? 1 : 0;
+ if (aml_debug_prompt_reginput)
+ fputs("REGION INPUT ON\n", stderr);
+ else
+ fputs("REGION INPUT OFF\n", stderr);
+ break;
+ case 'o':
+ aml_debug_prompt_regoutput =
+ (aml_debug_prompt_regoutput == 0) ? 1 : 0;
+ if (aml_debug_prompt_regoutput)
+ fputs("REGION OUTPUT ON\n", stderr);
+ else
+ fputs("REGION OUTPUT OFF\n", stderr);
+ break;
+ case 'm':
+ memman_statistics(aml_memman);
+ break;
+ case 'r':
+ /* NULL terminate */
+ ptr = &commandline[1];
+ while (ptr[0] != '\n')
+ ptr++;
+ ptr[0] = '\0';
+
+ /* move pointer to method name */
+ ptr = &commandline[1];
+ while (ptr[0] == ' ')
+ ptr++;
+
+ if (ptr[0] == '\0') {
+ break;
+ }
+ name = aml_find_from_namespace(aml_get_rootname(), ptr);
+ if (name == NULL) {
+ printf("%s:%d:aml_dbgr: not found name %s\n",
+ __FILE__, __LINE__, ptr);
+ break;
+ }
+ if (name->property == NULL ||
+ name->property->type != aml_t_method) {
+ printf("%s:%d:aml_dbgr: not method %s\n",
+ __FILE__, __LINE__, ptr);
+ break;
+ }
+ aml_showobject(name->property);
+ method = ptr;
+
+ argnum = name->property->meth.argnum & 0x07;
+ if (argnum) {
+ fputs(" Enter argument values "
+ "(ex. number 1 / string foo). "
+ "'q' to quit.\n", stderr);
+ }
+ /* get and parse argument values */
+ for (i = 0; i < argnum; i++) {
+retry:
+ fprintf(stderr, " Arg%d ? ", i);
+ if (read(0, argbuf[i], ARGBUFLEN) == 0) {
+ fputs("\n", stderr);
+ goto retry;
+ }
+ argbuf[i][ARGBUFLEN - 1] = '\n';
+ if (argbuf[i][0] == 'q') {
+ goto finish_execution;
+ }
+ if (argbuf[i][0] == '\n') {
+ goto retry;
+ }
+ /* move pointer to the value */
+ ptr = &argbuf[i][0];
+ while (ptr[0] != ' ' && ptr[0] != '\n') {
+ ptr++;
+ }
+ while (ptr[0] == ' ') {
+ ptr++;
+ }
+ if (ptr[0] == '\n') {
+ goto retry;
+ }
+ switch (argbuf[i][0]) {
+ case 'n':
+ argv[i].type = aml_t_num;
+ np = ptr;
+ if (ptr[0] == '0' &&
+ ptr[1] == 'x') {
+ argv[i].num.number = strtoq(ptr, &ep, 16);
+ } else {
+ argv[i].num.number = strtoq(ptr, &ep, 10);
+ }
+ if (np == ep) {
+ fputs("Wrong value for number.\n",
+ stderr);
+ goto retry;
+ }
+ break;
+ case 's':
+ argv[i].type = aml_t_string;
+ argv[i].str.needfree = 0;
+ argv[i].str.string = ptr;
+ /* NULL ternimate */
+ while (ptr[0] != '\n') {
+ ptr++;
+ }
+ ptr[0] = '\0';
+ break;
+ default:
+ fputs("Invalid data type "
+ "(supports number or string only)\n",
+ stderr);
+ goto retry;
+ }
+ }
+ bzero(lastcommand, sizeof lastcommand);
+ fprintf(stderr, "==== Running %s. ====\n", method);
+ aml_local_stack_push(aml_local_stack_create());
+ retval = aml_invoke_method_by_name(method, argnum, argv);
+ aml_showobject(retval);
+ aml_local_stack_delete(aml_local_stack_pop());
+ fprintf(stderr, "==== %s finished. ====\n", method);
+finish_execution:
+ break;
+ case 'f':
+ /* NULL terminate */
+ ptr = &commandline[1];
+ while (ptr[0] != '\n')
+ ptr++;
+ ptr[0] = '\0';
+
+ /* move pointer to object name */
+ ptr = &commandline[1];
+ while (ptr[0] == ' ')
+ ptr++;
+
+ aml_apply_foreach_found_objects(aml_get_rootname(),
+ ptr, print_named_object);
+ break;
+ case 'h':
+ fputs("s Single step\n"
+ "n Step program\n"
+ "c Continue program being debugged\n"
+ "q Quit method execution\n"
+ "t Show local name space tree and variables\n"
+ "i Toggle region input prompt\n"
+ "o Toggle region output prompt\n"
+ "m Show memory management statistics\n"
+ "r Run specified method\n"
+ "f Find named objects from namespace.\n"
+ "h Show this messsage\n", stderr);
+ break;
+ }
+ }
+}
diff --git a/usr.sbin/acpi/amldb/debug.h b/usr.sbin/acpi/amldb/debug.h
new file mode 100644
index 0000000..3e84efa
--- /dev/null
+++ b/usr.sbin/acpi/amldb/debug.h
@@ -0,0 +1,36 @@
+/*-
+ * Copyright (c) 1999 Takanori Watanabe
+ * Copyright (c) 1999, 2000 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: debug.h,v 1.8 2000/08/09 14:47:57 iwasaki Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _DEBUG_H_
+#define _DEBUG_H_
+
+void aml_dbgr(struct aml_environ *, struct aml_environ *);
+
+#endif /* !_DEBUG_H_ */
diff --git a/usr.sbin/acpi/amldb/region.c b/usr.sbin/acpi/amldb/region.c
new file mode 100644
index 0000000..280a0aa
--- /dev/null
+++ b/usr.sbin/acpi/amldb/region.c
@@ -0,0 +1,510 @@
+/*-
+ * Copyright (c) 1999 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: region.c,v 1.14 2000/08/08 14:12:25 iwasaki Exp $
+ * $FreeBSD$
+ */
+
+/*
+ * Region I/O subroutine
+ */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+
+#include <aml/aml_amlmem.h>
+#include <aml/aml_name.h>
+#include <aml/aml_region.h>
+#include <aml/aml_common.h>
+
+#include <assert.h>
+#include <err.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "debug.h"
+
+int aml_debug_prompt_regoutput = 0;
+int aml_debug_prompt_reginput = 1;
+
+static void aml_simulation_regload(const char *dumpfile);
+
+struct ACPIRegionContent {
+ TAILQ_ENTRY(ACPIRegionContent) links;
+ int regtype;
+ u_int32_t addr;
+ u_int8_t value;
+};
+
+TAILQ_HEAD(ACPIRegionContentList, ACPIRegionContent);
+struct ACPIRegionContentList RegionContentList;
+
+static int aml_simulation_initialized = 0;
+
+static void
+aml_simulation_init()
+{
+
+ aml_simulation_initialized = 1;
+ TAILQ_INIT(&RegionContentList);
+ aml_simulation_regload("region.ini");
+}
+
+static int
+aml_simulate_regcontent_add(int regtype, u_int32_t addr, u_int8_t value)
+{
+ struct ACPIRegionContent *rc;
+
+ rc = malloc(sizeof(struct ACPIRegionContent));
+ if (rc == NULL) {
+ return (-1); /* malloc fail */
+ }
+ rc->regtype = regtype;
+ rc->addr = addr;
+ rc->value = value;
+
+ TAILQ_INSERT_TAIL(&RegionContentList, rc, links);
+ return (0);
+}
+
+static int
+aml_simulate_regcontent_read(int regtype, u_int32_t addr, u_int8_t *valuep)
+{
+ struct ACPIRegionContent *rc;
+
+ if (!aml_simulation_initialized) {
+ aml_simulation_init();
+ }
+ TAILQ_FOREACH(rc, &RegionContentList, links) {
+ if (rc->regtype == regtype && rc->addr == addr) {
+ *valuep = rc->value;
+ return (1); /* found */
+ }
+ }
+
+ return (aml_simulate_regcontent_add(regtype, addr, 0));
+}
+
+static int
+aml_simulate_regcontent_write(int regtype, u_int32_t addr, u_int8_t *valuep)
+{
+ struct ACPIRegionContent *rc;
+
+ if (!aml_simulation_initialized) {
+ aml_simulation_init();
+ }
+ TAILQ_FOREACH(rc, &RegionContentList, links) {
+ if (rc->regtype == regtype && rc->addr == addr) {
+ rc->value = *valuep;
+ return (1); /* exists */
+ }
+ }
+
+ return (aml_simulate_regcontent_add(regtype, addr, *valuep));
+}
+
+static u_int32_t
+aml_simulate_prompt(char *msg, u_int32_t def_val)
+{
+ char buf[16], *ep;
+ u_int32_t val;
+
+ val = def_val;
+ printf("DEBUG");
+ if (msg != NULL) {
+ printf("%s", msg);
+ }
+ printf("(default: 0x%x / %u) >>", val, val);
+ fflush(stdout);
+
+ bzero(buf, sizeof buf);
+ while (1) {
+ if (read(0, buf, sizeof buf) == 0) {
+ continue;
+ }
+ if (buf[0] == '\n') {
+ break; /* use default value */
+ }
+ if (buf[0] == '0' && buf[1] == 'x') {
+ val = strtoq(buf, &ep, 16);
+ } else {
+ val = strtoq(buf, &ep, 10);
+ }
+ break;
+ }
+ return (val);
+}
+
+static void
+aml_simulation_regload(const char *dumpfile)
+{
+ char buf[256], *np, *ep;
+ struct ACPIRegionContent rc;
+ FILE *fp;
+
+ if (!aml_simulation_initialized) {
+ return;
+ }
+ if ((fp = fopen(dumpfile, "r")) == NULL) {
+ warn("%s", dumpfile);
+ return;
+ }
+ while (fgets(buf, sizeof buf, fp) != NULL) {
+ np = buf;
+ /* reading region type */
+ rc.regtype = strtoq(np, &ep, 10);
+ if (np == ep) {
+ continue;
+ }
+ np = ep;
+
+ /* reading address */
+ rc.addr = strtoq(np, &ep, 16);
+ if (np == ep) {
+ continue;
+ }
+ np = ep;
+
+ /* reading value */
+ rc.value = strtoq(np, &ep, 16);
+ if (np == ep) {
+ continue;
+ }
+ aml_simulate_regcontent_write(rc.regtype, rc.addr, &rc.value);
+ }
+
+ fclose(fp);
+}
+
+int
+aml_region_read_simple(struct aml_region_handle *h, vm_offset_t offset,
+ u_int32_t *valuep)
+{
+ int i, state;
+ u_int8_t val;
+ u_int32_t value;
+
+ state = 0;
+ value = val = 0;
+ for (i = 0; i < h->unit; i++) {
+ state = aml_simulate_regcontent_read(h->regtype,
+ h->addr + offset + i, &val);
+ if (state == -1) {
+ goto out;
+ }
+ value |= val << (i * 8);
+ }
+ *valuep = value;
+out:
+ return (state);
+}
+
+int
+aml_region_write_simple(struct aml_region_handle *h, vm_offset_t offset,
+ u_int32_t value)
+{
+ int i, state;
+ u_int8_t val;
+
+ state = 0;
+ val = 0;
+ for (i = 0; i < h->unit; i++) {
+ val = value & 0xff;
+ state = aml_simulate_regcontent_write(h->regtype,
+ h->addr + offset + i, &val);
+ if (state == -1) {
+ goto out;
+ }
+ value = value >> 8;
+ }
+out:
+ return (state);
+}
+
+u_int32_t
+aml_region_prompt_read(struct aml_region_handle *h, u_int32_t value)
+{
+ u_int32_t retval;
+ char buf[64];
+
+ retval = value;
+ sprintf(buf, "[read(%d, 0x%x)&mask:0x%x]",
+ h->regtype, h->addr, value);
+ if (aml_debug_prompt_reginput) {
+ retval = aml_simulate_prompt(buf, value);
+ } else {
+ printf("\t%s\n", buf);
+ }
+
+ return (retval);
+}
+
+u_int32_t
+aml_region_prompt_write(struct aml_region_handle *h, u_int32_t value)
+{
+ u_int32_t retval;
+ char buf[64];
+
+ retval = value;
+ if (aml_debug_prompt_regoutput) {
+ printf("\n");
+ sprintf(buf, "[write(%d, 0x%x, 0x%x)]",
+ h->regtype, value, h->addr);
+ retval = aml_simulate_prompt(buf, value);
+ }
+
+ return (retval);
+}
+
+int
+aml_region_prompt_update_value(u_int32_t orgval, u_int32_t value,
+ struct aml_region_handle *h)
+{
+ int state;
+
+ state = 0;
+ if (orgval != value) {
+ state = aml_region_io(h->env, AML_REGION_OUTPUT, h->regtype,
+ h->flags, &value, h->baseaddr, h->bitoffset, h->bitlen);
+ if (state == -1) {
+ goto out;
+ }
+ }
+
+out:
+ return (state);
+}
+
+static int
+aml_simulate_region_io_buffer(int io, int regtype, u_int32_t flags,
+ u_int8_t *buffer, u_int32_t baseaddr, u_int32_t bitoffset, u_int32_t bitlen)
+{
+ u_int8_t val;
+ u_int8_t offsetlow, offsethigh;
+ u_int32_t addr, byteoffset, bytelen;
+ int state, i;
+
+ val = 0;
+ offsetlow = offsethigh = 0;
+ state = 0;
+
+ byteoffset = bitoffset / 8;
+ bytelen = bitlen / 8 + ((bitlen % 8) ? 1 : 0);
+ addr = baseaddr + byteoffset;
+ offsetlow = bitoffset % 8;
+ assert(offsetlow == 0);
+
+ if (bytelen > 1) {
+ offsethigh = (bitlen - (8 - offsetlow)) % 8;
+ }
+ assert(offsethigh == 0);
+
+ for (i = bytelen; i > 0; i--, addr++) {
+ switch (io) {
+ case AML_REGION_INPUT:
+ val = 0;
+ state = aml_simulate_regcontent_read(regtype, addr, &val);
+ if (state == -1) {
+ goto finish;
+ }
+ buffer[bytelen - i] = val;
+ break;
+ case AML_REGION_OUTPUT:
+ val = buffer[bytelen - i];
+ state = aml_simulate_regcontent_write(regtype,
+ addr, &val);
+ if (state == -1) {
+ goto finish;
+ }
+ break;
+ }
+ }
+finish:
+ return (state);
+}
+
+static u_int32_t
+aml_simulate_region_read(struct aml_environ *env, int regtype,
+ u_int32_t flags, u_int32_t addr, u_int32_t bitoffset, u_int32_t bitlen)
+{
+ int value;
+ int state;
+
+ state = aml_region_io(env, AML_REGION_INPUT, regtype, flags, &value,
+ addr, bitoffset, bitlen);
+ assert(state != -1);
+ return (value);
+}
+
+int
+aml_simulate_region_read_into_buffer(int regtype, u_int32_t flags,
+ u_int32_t addr, u_int32_t bitoffset, u_int32_t bitlen, u_int8_t *buffer)
+{
+ int state;
+
+ state = aml_simulate_region_io_buffer(AML_REGION_INPUT, regtype, flags,
+ buffer, addr, bitoffset, bitlen);
+ assert(state != -1);
+ return (state);
+}
+
+int
+aml_simulate_region_write(struct aml_environ *env, int regtype,
+ u_int32_t flags, u_int32_t value, u_int32_t addr, u_int32_t bitoffset,
+ u_int32_t bitlen)
+{
+ int state;
+
+ state = aml_region_io(env, AML_REGION_OUTPUT, regtype, flags,
+ &value, addr, bitoffset, bitlen);
+ assert(state != -1);
+ return (state);
+}
+
+int
+aml_simulate_region_write_from_buffer(int regtype, u_int32_t flags,
+ u_int8_t *buffer, u_int32_t addr, u_int32_t bitoffset, u_int32_t bitlen)
+{
+ int state;
+
+ state = aml_simulate_region_io_buffer(AML_REGION_OUTPUT, regtype,
+ flags, buffer, addr, bitoffset, bitlen);
+ assert(state != -1);
+ return (state);
+}
+
+int
+aml_simulate_region_bcopy(struct aml_environ *env, int regtype,
+ u_int32_t flags, u_int32_t addr,
+ u_int32_t bitoffset, u_int32_t bitlen,
+ u_int32_t dflags, u_int32_t daddr,
+ u_int32_t dbitoffset, u_int32_t dbitlen)
+{
+ u_int32_t len, i;
+ u_int32_t value;
+ int state;
+
+ len = (bitlen > dbitlen) ? dbitlen : bitlen;
+ len = len / 8 + ((len % 8) ? 1 : 0);
+
+ for (i = 0; i < len; i++) {
+ state = aml_region_io(env, AML_REGION_INPUT, regtype,
+ flags, &value, addr, bitoffset + i * 8, 8);
+ assert(state != -1);
+ state = aml_region_io(env, AML_REGION_OUTPUT, regtype,
+ dflags, &value, daddr, dbitoffset + i * 8, 8);
+ assert(state != -1);
+ }
+
+ return (0);
+}
+
+u_int32_t
+aml_region_read(struct aml_environ *env, int regtype, u_int32_t flags,
+ u_int32_t addr, u_int32_t bitoffset, u_int32_t bitlen)
+{
+
+ AML_REGION_READ_DEBUG(regtype, flags, addr, bitoffset, bitlen);
+
+ return (aml_simulate_region_read(env, regtype, flags, addr,
+ bitoffset, bitlen));
+}
+
+int
+aml_region_read_into_buffer(struct aml_environ *env, int regtype,
+ u_int32_t flags, u_int32_t addr, u_int32_t bitoffset,
+ u_int32_t bitlen, u_int8_t *buffer)
+{
+
+ AML_REGION_READ_INTO_BUFFER_DEBUG(regtype, flags, addr, bitoffset, bitlen);
+
+ return (aml_simulate_region_read_into_buffer(regtype, flags, addr,
+ bitoffset, bitlen, buffer));
+}
+
+int
+aml_region_write(struct aml_environ *env, int regtype, u_int32_t flags,
+ u_int32_t value, u_int32_t addr, u_int32_t bitoffset, u_int32_t bitlen)
+{
+
+ AML_REGION_WRITE_DEBUG(regtype, flags, value, addr, bitoffset, bitlen);
+
+ return (aml_simulate_region_write(env, regtype, flags, value, addr,
+ bitoffset, bitlen));
+}
+
+int
+aml_region_write_from_buffer(struct aml_environ *env, int regtype,
+ u_int32_t flags, u_int8_t *buffer, u_int32_t addr, u_int32_t bitoffset,
+ u_int32_t bitlen)
+{
+
+ AML_REGION_WRITE_FROM_BUFFER_DEBUG(regtype, flags,
+ addr, bitoffset, bitlen);
+
+ return (aml_simulate_region_write_from_buffer(regtype, flags, buffer,
+ addr, bitoffset, bitlen));
+}
+
+int
+aml_region_bcopy(struct aml_environ *env, int regtype, u_int32_t flags,
+ u_int32_t addr, u_int32_t bitoffset, u_int32_t bitlen,
+ u_int32_t dflags, u_int32_t daddr,
+ u_int32_t dbitoffset, u_int32_t dbitlen)
+{
+
+ AML_REGION_BCOPY_DEBUG(regtype, flags, addr, bitoffset, bitlen,
+ dflags, daddr, dbitoffset, dbitlen);
+
+ return (aml_simulate_region_bcopy(env, regtype, flags, addr, bitoffset,
+ bitlen, dflags, daddr, dbitoffset, dbitlen));
+}
+
+void
+aml_simulation_regdump(const char *dumpfile)
+{
+ struct ACPIRegionContent *rc;
+ FILE *fp;
+
+ if (!aml_simulation_initialized) {
+ return;
+ }
+ if ((fp = fopen(dumpfile, "w")) == NULL) {
+ warn("%s", dumpfile);
+ return;
+ }
+ while (!TAILQ_EMPTY(&RegionContentList)) {
+ rc = TAILQ_FIRST(&RegionContentList);
+ fprintf(fp, "%d 0x%x 0x%x\n",
+ rc->regtype, rc->addr, rc->value);
+ TAILQ_REMOVE(&RegionContentList, rc, links);
+ free(rc);
+ }
+
+ fclose(fp);
+ TAILQ_INIT(&RegionContentList);
+}
diff --git a/usr.sbin/adduser/Makefile b/usr.sbin/adduser/Makefile
new file mode 100644
index 0000000..baccd55
--- /dev/null
+++ b/usr.sbin/adduser/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+SCRIPTS=adduser.perl rmuser.perl
+MAN= adduser.8 rmuser.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/adduser/adduser.8 b/usr.sbin/adduser/adduser.8
new file mode 100644
index 0000000..71078a3
--- /dev/null
+++ b/usr.sbin/adduser/adduser.8
@@ -0,0 +1,243 @@
+.\" 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd January 9, 1995
+.Dt ADDUSER 8
+.Os
+.Sh NAME
+.Nm adduser
+.Nd command for adding new users
+.Sh SYNOPSIS
+.Nm
+.Bk -words
+.Op Fl check_only
+.Op Fl class Ar login_class
+.Op Fl config_create
+.Op Fl dotdir Ar dotdir
+.Op Fl group Ar login_group
+.Op Fl h | help
+.Op Fl home Ar home
+.Op Fl message Ar message_file
+.Op Fl noconfig
+.Op Fl shell Ar shell
+.Op Fl s | silent | q | quiet
+.Op Fl uid Ar uid_start
+.Op Fl v | verbose
+.Ek
+.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/mail/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 Fl check_only
+Check /etc/passwd, /etc/group, /etc/shells and exit.
+.It Fl class Ar login_class
+Set default login class.
+.It Fl config_create
+Create new configuration and message file and exit.
+.It Fl 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 Fl group Ar login_group
+Login group.
+.Ar USER
+means that the username is to be used as login group.
+.It Fl help , h , \&?
+Print a summary of options and exit.
+.It Fl home Ar partition
+Default home partition where all users located.
+.It Fl 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 Fl noconfig
+Do not read the default configuration file.
+.It Fl shell Ar shell
+Default shell for new users.
+.It Fl silent , s , quiet , q
+Few warnings, questions, bug reports.
+.It Fl uid Ar uid
+Use uid's from
+.Ar uid
+on up.
+.It Fl verbose , v
+Many warnings, questions.
+Recommended for novice users.
+.El
+.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 aliases 5 ,
+.Xr group 5 ,
+.Xr login.conf 5 ,
+.Xr passwd 5 ,
+.Xr shells 5 ,
+.Xr pw 8 ,
+.Xr pwd_mkdb 8 ,
+.Xr rmuser 8 ,
+.Xr vipw 8 ,
+.Xr yp 8
+.\" .Sh 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..322bac6
--- /dev/null
+++ b/usr.sbin/adduser/adduser.perl
@@ -0,0 +1,1558 @@
+#!/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.
+#
+# $FreeBSD$
+
+
+# read variables
+sub variables {
+ $verbose = 1; # verbose = [0-2]
+ $usernameregexp = "^[a-z0-9_][a-z0-9_-]*\$"; # configurable
+ $defaultusernameregexp = $usernameregexp; # remains constant
+ $defaultusepassword = "yes"; # use password authentication for new users
+ $defaultenableaccount = "yes"; # enable the account by default
+ $defaultemptypassword = "no"; # don't create an empty password
+ $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 = qw(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
+ $usepassword = ''; # use password-based auth
+ $useemptypassword = ''; # use an empty password
+ $enableaccount = ''; # enable or disable account password at creation
+
+ # group
+ $groupname =''; # $groupname{groupname} = gid
+ $groupmembers = ''; # $groupmembers{gid} = members of group/kommalist
+ $gid = ''; # $gid{gid} = groupname; gid form group db
+ @group_comments; # Comments in the group file
+
+ # 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 "$etc_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 empty lines
+ next if /^\s*$/;
+ # Save comments to restore later
+ if (/^\s*\#/) {
+ push(@group_comments, $_);
+ next;
+ }
+
+ ($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, $usernameregexp, "");
+ last if (&new_users_name_valid($name));
+ }
+ return $name;
+}
+
+sub new_users_name_valid {
+ local($name) = @_;
+
+ if ($name eq $usernameregexp) { # user/admin just pressed <Return>
+ warn "Please enter a username\a\n";
+ return 0;
+ } elsif (length($name) > 16) {
+ warn "Username is longer than 16 characters.\a\n";
+ return 0;
+ } elsif ($name =~ /[:\n]/) {
+ warn "Username cannot contain colon or newline characters.\a\n";
+ return 0;
+ } elsif ($name !~ /$usernameregexp/) {
+ if ($usernameregexp eq $defaultusernameregexp) {
+ warn "Illegal username.\n" .
+ "Please use only lowercase Roman, decimal, underscore, " .
+ "or hyphen characters.\n" .
+ "Additionally, a username should not start with a hyphen.\a\n";
+ } else {
+ warn "Username doesn't match the regexp /$usernameregexp/\a\n";
+ }
+ return 0;
+ } elsif (defined($username{$name})) {
+ warn "Username ``$name'' already exists!\a\n"; return 0;
+ }
+ return 1;
+}
+
+# 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 {
+ local ($newpasswd);
+ # Note that we either show "password disabled" or
+ # "****" .. we don't show "empty password" since
+ # the whole point of starring out the password in
+ # the first place is to stop people looking over your
+ # shoulder and seeing the password.. -- adrian
+ if ($usepassword eq "no") {
+ $newpasswd = "Password disabled";
+ } elsif ($enableaccount eq "no") {
+ $newpasswd = "Password disabled";
+ } else {
+ $newpasswd = "****";
+ }
+
+ print <<EOF;
+
+Name: $name
+Password: $newpasswd
+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) = shift;
+ local($name) = shift;
+
+ system(@pwd_mkdb, '-u', $name, $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";
+
+ # Restore comments from the top of the group file
+ @a = @group_comments;
+ 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 {
+
+ 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_use_password {
+ local ($p) = $defaultusepassword;
+ $p = &confirm_yn("Use password-based authentication", $defaultusepassword);
+ return "yes" if (($defaultusepassword eq "yes" && $p) ||
+ ($defaultusepassword eq "no" && !$p));
+ return "no"; # otherwise
+}
+
+sub new_users_enable_account {
+ local ($p) = $defaultenableaccount;
+ $p = &confirm_yn("Enable account password at creation", $defaultenableaccount);
+ return "yes" if (($defaultenableaccount eq "yes" && $p) ||
+ ($defaultenableaccount eq "no" && !$p));
+ return "no"; # otherwise
+}
+
+sub new_users_empty_password {
+ local ($p) = $defaultemptypassword;
+ $p = &confirm_yn("Use an empty password", $defaultemptypassword);
+ return "yes" if (($defaultemptypassword eq "yes" && $p) ||
+ ($defaultemptypassword eq "no" && !$p));
+ return "no"; # otherwise
+}
+
+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}));
+
+
+ # The tricky logic:
+ # If $usepasswd is 0, we use a * as a password
+ # If $usepasswd is 1, then
+ # if $enableaccount is 0, we prepend * as a password
+ # else if $enableaccount is 1 we don't prepend anything
+ # if $useemptypassword is 0 we ask for a password,
+ # else we use a blank one
+ #
+ # The logic is tasty, I'll give you that, but its flexible and
+ # it'll stop people shooting themselves in the foot.
+
+ $new_groups = &new_users_groups($name, $new_groups);
+
+ $usepassword = &new_users_use_password;
+ if ($usepassword eq "no") {
+ # note that the assignments to enableaccount and
+ # useemptypassword functionally do the same as
+ # usepasswd == "no". Just for consistency.
+ $password = ""; # no password!
+ $enableaccount = "no"; # doesn't matter here
+ $useemptypassword = "yes"; # doesn't matter here
+ } else {
+ $useemptypassword = &new_users_empty_password;
+ if ($useemptypassword eq "no") {
+ $password = &new_users_password;
+ }
+ $enableaccount = &new_users_enable_account;
+ }
+
+ if (&new_users_ok) {
+ $new_users_ok = 1;
+
+ $cryptpwd = "";
+ $cryptpwd = crypt($password, &salt) if $password ne "";
+
+ if ($usepassword eq "no") {
+ $cryptpwd = "*";
+ } else {
+ # cryptpwd is valid before this if mess, so if
+ # blankpasswd is no we don't blank the cryptpwd
+ if ($useemptypassword eq "yes") {
+ $cryptpwd = "";
+ }
+ if ($enableaccount eq "no") {
+ $cryptpwd = "*" . $cryptpwd;
+ }
+ }
+ # 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", $name);
+ &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) = $defaultusepassword;
+ if ($verbose) {
+ $p = &confirm_yn("Use password-based authentication", $defaultusepassword);
+ $changes++ unless $p;
+ }
+ return "yes" if (($defaultusepassword eq "yes" && $p) ||
+ ($defaultusepassword eq "no" && !$p));
+ return "no"; # otherwise
+}
+
+# ask for account enable usage
+sub enable_account_default {
+ local ($p) = $defaultenableaccount;
+ if ($verbose) {
+ $p = &confirm_yn("Enable account password at creation", $defaultenableaccount);
+ $changes++ unless $p;
+ }
+ return "yes" if (($defaultenableaccount eq "yes" && $p) ||
+ ($defaultenableaccount eq "no" && !$p));
+ return "no"; # otherwise
+}
+
+# ask for empty password
+sub enable_empty_password {
+ local ($p) = $defaultemptypassword;
+ if ($verbose) {
+ $p = &confirm_yn("Use an empty password", $defaultemptypassword);
+ $changes++ unless $p;
+ }
+ return "yes" if (($defaultemptypassword eq "yes" && $p) ||
+ ($defaultemptypassword 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 < 27; $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 to see " .
+ "all warnings and questions.\n\n";
+ } else {
+ print "Use option ``-verbose'' if you want to see more warnings and " .
+ "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', '-Rh', "$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;
+}
+
+# allow configuring usernameregexp
+sub usernameregexp_default {
+ local($r) = $usernameregexp;
+
+ while ($verbose) {
+ $r = &confirm_list("Usernames must match regular expression:", 1,
+ $r, "");
+ eval "'foo' =~ /$r/";
+ last unless $@;
+ warn "Invalid regular expression\a\n";
+ }
+ $changes++ if $r ne $usernameregexp;
+ return $r;
+}
+
+# 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;
+ $defaultusepassword = "no" unless $defaultusepassword;
+ $defaultenableaccount = "yes" unless $defaultenableaccount;
+ $defaultemptypassword = "no" unless $defaultemptypassword;
+ 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 before the
+# line ``$do_not_delete''
+#
+
+# verbose = [0-2]
+verbose = $verbose
+
+# regular expression usernames are checked against (see perlre(1))
+# usernameregexp = 'regexp'
+usernameregexp = '$usernameregexp'
+
+# use password-based authentication for new users
+# defaultusepassword = "yes" | "no"
+defaultusepassword = "$defaultusepassword"
+
+# enable account password at creation
+# (the password will be prepended with a star if the account isn't enabled)
+# defaultenableaccount = "yes" | "no"
+defaultenableaccount = "$defaultenableaccount"
+
+# allow blank passwords
+# defaultemptypassword = "yes" | "no"
+defaultemptypassword = "$defaultemptypassword"
+
+# 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
+$usernameregexp = &usernameregexp_default; # regexp to check usernames against
+&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
+$defaultusepassword = &password_default; # maybe use password
+if ($defaultusepassword eq "no") {
+ if ($verbose) {
+ print "Creating accounts with a locked password.\n";
+ }
+ $defaultenableaccount = "no";
+ $defaultemptypassword = "yes";
+} else {
+ $defaultenableaccount = &enable_account_default; # enable or disable account
+ $defaultemptypassword = &enable_empty_password; # use empty password or not
+}
+&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..d3ed353
--- /dev/null
+++ b/usr.sbin/adduser/rmuser.8
@@ -0,0 +1,170 @@
+.\" Copyright 1995, 1996, 1997
+.\" Guy Helmer, Ames, Iowa 50014. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer as
+.\" the first lines of this file unmodified.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY GUY HELMER ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL GUY HELMER BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd February 23, 1997
+.Dt RMUSER 8
+.Os
+.Sh NAME
+.Nm rmuser
+.Nd removes users from the system
+.Sh SYNOPSIS
+.Nm
+.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 Ns '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.
+.El
+.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 adduser 8 ,
+.Xr pwd_mkdb 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
+.Bx 4.4
+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..1ffac08
--- /dev/null
+++ b/usr.sbin/adduser/rmuser.perl
@@ -0,0 +1,601 @@
+#!/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
+#
+# $FreeBSD$
+
+use Fcntl;
+
+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";
+$passwd_tmp = "/etc/ptmp";
+$group_file = "/etc/group";
+$new_group_file = "${group_file}.new.$$";
+$mail_dir = "/var/mail";
+$crontab_dir = "/var/cron/tabs";
+$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);
+} 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;
+}
+
+($name, $password, $uid, $gid, $change, $class, $gecos, $home_dir, $shell) =
+ (getpwnam("$login_name"));
+
+if (!defined $uid) {
+ print STDERR "${whoami}: Error: User ${login_name} not in password database\n";
+ &unlockpw;
+ exit 1;
+}
+
+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$name\:$password\:$uid\:$gid\:$class\:$change\:0\:$gecos\:$home_dir\:$shell\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);
+
+#
+# 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 = <>;
+ chomp $login_name;
+ if (not getpwnam("$login_name")) {
+ print STDERR "Sorry, login name not in password database.\n";
+ } else {
+ $done = 1;
+ }
+ }
+
+ print "User name is ${login_name}\n" if $debug;
+ return($login_name);
+}
+
+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);
+
+ print STDERR "Updating password file,";
+ seek(MASTER_PW, 0, 0);
+
+ sysopen(NEW_PW, $passwd_tmp, O_RDWR|O_CREAT|O_EXCL, 0600) ||
+ die "\n${whoami}: Error: Couldn't open file ${passwd_tmp}:\n $!\n";
+
+ $skipped = 0;
+ while (<MASTER_PW>) {
+ if (/^\Q$login_name:/o) {
+ print STDERR "Dropped entry for $login_name\n" if $debug;
+ $skipped = 1;
+ } else {
+ print NEW_PW;
+ # The other perl password tools assume all lowercase entries.
+ # Add a warning to help unsuspecting admins who might be
+ # using the wrong tool for the job, or might otherwise
+ # be unwittingly holding a loaded foot-shooting device.
+ if (/^\Q$login_name:/io) {
+ my $name = $_;
+ $name =~ s#\:.*\n##;
+ print STDERR "\n\n\tThere is also an entry for $name in your",
+ "password file.\n\tThis can cause problems in some ",
+ "situations.\n\n";
+ }
+ }
+ }
+ 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($passwd_tmp) ||
+ print STDERR "\n${whoami}: Warning: couldn't unlink $passwd_tmp ($!)\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', ${passwd_tmp});
+ 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 =~ /\Q$login_name\E/)) {
+ # 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 invoke_atq {
+ local *ATQ;
+ my($user) = (shift || "");
+ my($path_atq) = "/usr/bin/atq";
+ my(@at) = ();
+ my($pid, $line);
+
+ return @at if ($user eq "");
+
+ if (!defined($pid = open(ATQ, "-|"))) {
+ die("creating pipe to atq: $!\n");
+ } elsif ($pid == 0) {
+ exec($path_atq, $user);
+ die("executing $path_atq: $!\n");
+ }
+
+ while(defined($_ = <ATQ>)) {
+ chomp;
+ if (/^\d\d.\d\d.\d\d\s+\d\d.\d\d.\d\d\s+(\S+)\s+\S+\s+(\d+)$/) {
+ push(@at, $2) if ($1 eq $user);
+ }
+ }
+ close ATQ;
+ return @at;
+}
+
+sub invoke_atrm {
+ local *ATRM;
+ my($user) = (shift || "");
+ my($path_atrm) = "/usr/bin/atrm";
+ my(@jobs) = @_;
+ my($pid);
+ my($txt) = "";
+
+ return "Invalid arguments" if (($user eq "") || ($#jobs == -1));
+
+ if (!defined($pid = open(ATRM, "-|"))) {
+ die("creating pipe to atrm: $!\n");
+ } elsif ($pid == 0) {
+ exec($path_atrm, $user, @jobs);
+ }
+
+ while(defined($_ = <ATRM>)) {
+ $txt .= $_;
+ }
+ close ATRM;
+ return $txt;
+}
+
+sub remove_at_jobs {
+ my($user) = (shift || "");
+ my(@at, $atrm);
+
+ return 1 if ($user eq "");
+
+ @at = invoke_atq($user);
+ return 0 if ($#at == -1);
+
+ print STDERR "Removing user's at jobs:";
+ print STDERR " @at:";
+ $atrm = invoke_atrm($user, @at);
+ if ($atrm ne "") {
+ print STDERR " -- $atrm\n";
+ return 1;
+ }
+
+ print STDERR " done.\n";
+ return 0;
+}
+
+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..37e4200
--- /dev/null
+++ b/usr.sbin/amd/Makefile
@@ -0,0 +1,11 @@
+# ex:ts=8
+#
+# Makefile for amd
+# This file is under a "BSD" copyright (c) by David O'Brien 1998
+#
+# $FreeBSD$
+
+SUBDIR= include libamu amd amq doc fixmount fsinfo hlfsd mk-amd-map pawd \
+ scripts wire-test
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/amd/Makefile.inc b/usr.sbin/amd/Makefile.inc
new file mode 100644
index 0000000..fb29b81
--- /dev/null
+++ b/usr.sbin/amd/Makefile.inc
@@ -0,0 +1,37 @@
+# ex:ts=8
+#
+# $FreeBSD$
+#
+# Makefile for amd
+# This file is under a "BSD" copyright (c) by David O'Brien 1998.
+# Portions derived from
+# $NetBSD: Makefile.inc,v 1.10 1998/08/08 22:33:27 christos Exp $
+# Portions derived from amd/libamu/Makefile
+# $NetBSD: Makefile,v 1.8 1998/08/08 22:33:37 christos Exp $
+
+CFLAGS+= -I. -I${.CURDIR}
+CFLAGS+= -I${.CURDIR}/../include
+.if exists(${.OBJDIR}/../include)
+CFLAGS+= -I${.OBJDIR}/../include
+.endif
+CFLAGS+= -I${.CURDIR}/../../../contrib/amd/include
+CFLAGS+= -I${.CURDIR}/../../../contrib/amd
+CFLAGS+= -DHAVE_CONFIG_H
+
+CFLAGS+= -DHOST_CPU=\"${MACHINE_ARCH}\" -DHOST_ARCH=\"${MACHINE_ARCH}\"
+
+.if exists(${.OBJDIR}/../libamu)
+LIBAMUDIR= ${.OBJDIR}/../libamu
+.else
+LIBAMUDIR= ${.CURDIR}/../libamu
+.endif
+LIBAMU= ${LIBAMUDIR}/libamu.a
+
+RPCCOM= rpcgen
+MOUNT_X= ${DESTDIR}/usr/include/rpcsvc/mount.x
+NFS_PROT_X= ${DESTDIR}/usr/include/rpcsvc/nfs_prot.x
+
+
+.if exists(${.CURDIR}/../../Makefile.inc)
+.include "${.CURDIR}/../../Makefile.inc"
+.endif
diff --git a/usr.sbin/amd/NOTES b/usr.sbin/amd/NOTES
new file mode 100644
index 0000000..b04244b
--- /dev/null
+++ b/usr.sbin/amd/NOTES
@@ -0,0 +1,3 @@
+amd/amd supports HESIOD, LDAP, and NIS+ which we don't presently.
+If they are added to FreeBSD, add info_hesiod.c, info_ldap.c, info_nisplus.c
+to amd/amd's Makefile.
diff --git a/usr.sbin/amd/amd/Makefile b/usr.sbin/amd/amd/Makefile
new file mode 100644
index 0000000..a05a728
--- /dev/null
+++ b/usr.sbin/amd/amd/Makefile
@@ -0,0 +1,42 @@
+# ex:ts=8
+#
+# Makefile for amd
+# This file is under a "BSD" copyright (c) by David O'Brien 1998
+#
+# $FreeBSD$
+#
+
+.PATH: ${.CURDIR}/../../../contrib/amd/amd
+
+PROG= amd
+MAN= amd.8
+SRCS= conf_parse.y conf_tok.l
+SRCS+= am_ops.c amd.c amfs_auto.c amfs_direct.c amfs_error.c
+SRCS+= amfs_host.c amfs_inherit.c amfs_link.c amfs_linkx.c amfs_nfsl.c
+SRCS+= amfs_nfsx.c amfs_program.c amfs_root.c amfs_toplvl.c
+SRCS+= amfs_union.c amq_subr.c amq_svc.c autil.c clock.c conf.c
+SRCS+= get_args.c info_file.c info_hesiod.c info_ndbm.c info_nis.c info_passwd.c
+SRCS+= info_union.c map.c mapc.c mntfs.c nfs_prot_svc.c nfs_start.c
+SRCS+= nfs_subr.c ops_cdfs.c ops_mfs.c ops_nfs.c ops_nfs3.c
+SRCS+= ops_nullfs.c ops_pcfs.c ops_tfs.c ops_ufs.c ops_umapfs.c
+SRCS+= ops_unionfs.c opts.c restart.c rpc_fwd.c sched.c
+SRCS+= srvr_amfs_auto.c srvr_nfs.c
+
+CFLAGS+= -I${.CURDIR}/../../../contrib/amd/amd \
+ -I${DESTDIR}/usr/include/rpcsvc
+
+DPADD= ${LIBAMU} ${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..74fc749
--- /dev/null
+++ b/usr.sbin/amd/amq/Makefile
@@ -0,0 +1,20 @@
+# ex:ts=8
+#
+# Makefile for amd
+# This file is under a "BSD" copyright (c) by David O'Brien 1998
+#
+# $FreeBSD$
+#
+
+.PATH: ${.CURDIR}/../../../contrib/amd/amq
+
+PROG= amq
+MAN= amq.8
+SRCS= amq.c amq_clnt.c amq_xdr.c
+
+CFLAGS+= -I${.CURDIR}/../../../contrib/amd/amq
+
+DPADD= ${LIBAMU}
+LDADD= ${LIBAMU}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/amd/doc/Makefile b/usr.sbin/amd/doc/Makefile
new file mode 100644
index 0000000..e9c7707
--- /dev/null
+++ b/usr.sbin/amd/doc/Makefile
@@ -0,0 +1,14 @@
+# This file is under a "BSD" copyright (c) by David O'Brien 1998.
+
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../contrib/amd/doc
+
+INFO= am-utils
+
+INFOSECTION= "AMD Documentation"
+INFOENTRY= "* Am-utils: (am-utils). The Amd automounter suite of utilities"
+
+MAKEINFOFLAGS+= -I ${.CURDIR}/../../../contrib/amd/doc
+
+.include <bsd.info.mk>
diff --git a/usr.sbin/amd/fixmount/Makefile b/usr.sbin/amd/fixmount/Makefile
new file mode 100644
index 0000000..7f96a45
--- /dev/null
+++ b/usr.sbin/amd/fixmount/Makefile
@@ -0,0 +1,21 @@
+# ex:ts=8
+#
+# Makefile for amd
+# This file is under a "BSD" copyright (c) by David O'Brien 1998
+#
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../contrib/amd/fixmount \
+ ${.CURDIR}/../../../contrib/amd/conf/checkmount
+
+PROG= fixmount
+MAN= fixmount.8
+SRCS= fixmount.c
+
+# These would be links created by the GNU-style configure
+SRCS+= checkmount_bsd44.c
+
+DPADD= ${LIBAMU} ${LIBRPCSVC}
+LDADD= ${LIBAMU} -lrpcsvc
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/amd/fsinfo/Makefile b/usr.sbin/amd/fsinfo/Makefile
new file mode 100644
index 0000000..9ae55ad
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/Makefile
@@ -0,0 +1,22 @@
+# ex:ts=8
+#
+# Makefile for amd
+# This file is under a "BSD" copyright (c) by David O'Brien 1998
+#
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../contrib/amd/fsinfo
+
+PROG= fsinfo
+MAN= fsinfo.8
+SRCS= fsi_gram.y fsi_lex.l
+SRCS+= fsi_analyze.c fsi_dict.c fsi_util.c fsinfo.c wr_atab.c
+SRCS+= wr_bparam.c wr_dumpset.c wr_exportfs.c wr_fstab.c
+
+CFLAGS+= -I${.CURDIR}/../../../contrib/amd/fsinfo
+
+CLEANFILES+= fsi_gram.c fsi_gram.h fsi_lex.c
+
+fsi_lex.o: fsi_gram.h
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/amd/hlfsd/Makefile b/usr.sbin/amd/hlfsd/Makefile
new file mode 100644
index 0000000..b2c9447
--- /dev/null
+++ b/usr.sbin/amd/hlfsd/Makefile
@@ -0,0 +1,19 @@
+# ex:ts=8
+#
+# Makefile for amd
+# This file is under a "BSD" copyright (c) by David O'Brien 1998
+#
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../contrib/amd/hlfsd
+
+PROG= hlfsd
+MAN= hlfsd.8
+SRCS= hlfsd.c homedir.c nfs_prot_svc.c stubs.c
+
+CFLAGS+= -I${.CURDIR}/../../../contrib/amd/hlfsd
+
+DPADD= ${LIBAMU} ${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..d3cf000
--- /dev/null
+++ b/usr.sbin/amd/include/Makefile
@@ -0,0 +1,19 @@
+# ex:ts=8
+#
+# $FreeBSD$
+#
+# Makefile for amd
+# This file is under a "BSD" copyright (c) by David O'Brien 1998.
+# Portions derived from amd/libamu/Makefile
+# $NetBSD: Makefile,v 1.8 1998/08/08 22:33:37 christos Exp $
+
+SRCS= config_local.h
+CLEANFILES= ${SRCS}
+
+all depend: ${SRCS}
+
+config_local.h: newvers.sh
+ @rm -f ${.TARGET}
+ sh ${.ALLSRC} ${.CURDIR}/../../../sys/conf/newvers.sh > ${.TARGET}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/amd/include/amu_nfs_prot.h b/usr.sbin/amd/include/amu_nfs_prot.h
new file mode 100644
index 0000000..8e8856f
--- /dev/null
+++ b/usr.sbin/amd/include/amu_nfs_prot.h
@@ -0,0 +1 @@
+#include "conf/nfs_prot/nfs_prot_freebsd3.h"
diff --git a/usr.sbin/amd/include/aux_conf.h b/usr.sbin/amd/include/aux_conf.h
new file mode 100644
index 0000000..a26d3cd
--- /dev/null
+++ b/usr.sbin/amd/include/aux_conf.h
@@ -0,0 +1,81 @@
+/* $FreeBSD$ */
+
+/*
+ * aux_conf.h:
+ * This file gets "filled in" for each architecture.
+ * Generated automatically from aux_conf.h.in by configure.
+ */
+
+#ifndef _AUX_CONF_H
+#define _AUX_CONF_H
+
+/*
+ * The next line is a literal inclusion of a file which includes a
+ * definition for the MOUNT_TRAP macro for a particular architecture.
+ * If it defines the wrong entry, check the AC_CHECK_MOUNT_TRAP m4 macro
+ * in $srcdir/aux/macros.
+ */
+
+/* $srcdir/conf/trap/trap_default.h */
+#ifdef COMMENT_OUT__GET_DIRECTLY_FROM_FILE
+#define MOUNT_TRAP(type, mnt, flags, mnt_data) mount(type, mnt->mnt_dir, flags, mnt_data)
+#endif /* COMMENT_OUT__GET_DIRECTLY_FROM_FILE */
+#include "conf/trap/trap_default.h"
+/* End of included MOUNT_TRAP macro definition file */
+
+/*
+ * The next line is a literal replacement of a variable which defines the
+ * the UNMOUNT_TRAP macro for a particular architecture.
+ * If it defines the wrong entry, check the AC_CHECK_UNMOUNT_CALL m4 macro
+ * in $srcdir/aclocal.m4. If the arguments are being defined wrong, check
+ * the macro AC_CHECK_UNMOUNT_ARGS in $srcdir/aux/macros.
+ */
+#define UNMOUNT_TRAP(mnt) unmount(mnt->mnt_dir)
+/* End of replaced UNMOUNT_TRAP macro definition */
+
+/*
+ * The next line is a literal inclusion of a file which includes a
+ * definition for the NFS_FH_DREF macro for a particular architecture.
+ * If it defines the wrong entry, check the AC_CHECK_NFS_FH_DREF m4 macro
+ * in $srcdir/aux/macros.
+ */
+
+/* $srcdir/conf/fh_dref/fh_dref_freebsd22.h */
+#ifdef COMMENT_OUT__GET_DIRECTLY_FROM_FILE
+#define NFS_FH_DREF(dst, src) (dst) = (u_char *) (src)
+#endif /* COMMENT_OUT__GET_DIRECTLY_FROM_FILE */
+#include "conf/fh_dref/fh_dref_freebsd22.h"
+/* End of included NFS_FH_DREF macro definition file */
+
+/*
+ * The next line is a literal inclusion of a file which includes a
+ * definition for the NFS_SA_DREF macro for a particular architecture.
+ * If it defines the wrong entry, check the AC_CHECK_NFS_SA_DREF m4 macro
+ * in $srcdir/aux/macros.
+ */
+
+/* $srcdir/conf/sa_dref/sa_dref_bsd44.h */
+#ifdef COMMENT_OUT__GET_DIRECTLY_FROM_FILE
+#define NFS_SA_DREF(dst, src) { \
+ (dst)->addr = (struct sockaddr *) (src); \
+ (dst)->addrlen = sizeof(*src); \
+ }
+#endif /* COMMENT_OUT__GET_DIRECTLY_FROM_FILE */
+#include "conf/sa_dref/sa_dref_bsd44.h"
+/* End of included NFS_SA_DREF macro definition file */
+
+/*
+ * The next line is a literal inclusion of a file which includes a
+ * definition for the NFS_HN_DREF macro for a particular architecture.
+ * If it defines the wrong entry, check the AC_CHECK_NFS_HN_DREF m4 macro
+ * in $srcdir/aux/macros.
+ */
+
+/* $srcdir/conf/hn_dref/hn_dref_default.h */
+#ifdef COMMENT_OUT__GET_DIRECTLY_FROM_FILE
+#define NFS_HN_DREF(dst, src) (dst) = (src)
+#endif /* COMMENT_OUT__GET_DIRECTLY_FROM_FILE */
+#include "conf/hn_dref/hn_dref_default.h"
+/* End of included NFS_HN_DREF macro definition file */
+
+#endif /* not _AUX_CONF_H */
diff --git a/usr.sbin/amd/include/build_version.h b/usr.sbin/amd/include/build_version.h
new file mode 100644
index 0000000..2f80eb2
--- /dev/null
+++ b/usr.sbin/amd/include/build_version.h
@@ -0,0 +1,6 @@
+/* do not edit this file by hand */
+/* auto-generated by update_build_version script */
+/* #define AMU_BUILD_VERSION 1 */
+
+#include <sys/param.h>
+#define AMU_BUILD_VERSION __FreeBSD_version
diff --git a/usr.sbin/amd/include/config.h b/usr.sbin/amd/include/config.h
new file mode 100644
index 0000000..610b996
--- /dev/null
+++ b/usr.sbin/amd/include/config.h
@@ -0,0 +1,2081 @@
+/*
+ * $FreeBSD$
+ *
+ * portions derived from
+ * $NetBSD: config.h,v 1.11 1998/08/08 22:33:37 christos Exp $
+ *
+ */
+
+
+/* config.h. Generated automatically by configure. */
+/* config.h.in. Generated automatically from 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: acconfig.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_AMU_FS_AUTO 1
+
+/* Define if have direct automount filesystem */
+#define HAVE_AMU_FS_DIRECT 1
+
+/* Define if have "top-level" filesystem */
+#define HAVE_AMU_FS_TOPLVL 1
+
+/* Define if have error filesystem */
+#define HAVE_AMU_FS_ERROR 1
+
+/* Define if have inheritance filesystem */
+#define HAVE_AMU_FS_INHERIT 1
+
+/* Define if have program filesystem */
+#define HAVE_AMU_FS_PROGRAM 1
+
+/* Define if have symbolic-link filesystem */
+#define HAVE_AMU_FS_LINK 1
+
+/* Define if have symlink with existence check filesystem */
+#define HAVE_AMU_FS_LINKX 1
+
+/* Define if have NFS host-tree filesystem */
+#define HAVE_AMU_FS_HOST 1
+
+/* Define if have nfsl (NFS with local link check) filesystem */
+#define HAVE_AMU_FS_NFSL 1
+
+/* Define if have multi-NFS filesystem */
+#define HAVE_AMU_FS_NFSX 1
+
+/* Define if have union filesystem */
+#define HAVE_AMU_FS_UNION 1
+
+
+/*
+ * 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 */
+#define HAVE_MAP_HESIOD 1
+
+/* 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 */
+/* #undef HAVE_FS_LOFS */
+
+/* Define if have HSFS filesystem */
+/* #undef HAVE_FS_HSFS */
+
+/* Define if have CDFS filesystem */
+#define HAVE_FS_CDFS 1
+
+/* Define if have TFS filesystem */
+/* #undef HAVE_FS_TFS */
+
+/* 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 MOUNT_NFS3
+
+/* Mount(2) type/name for PCFS filesystem */
+/* XXX: conf/trap/trap_hpux.h may override this definition for HPUX 9.0 */
+#define MOUNT_TYPE_PCFS "msdosfs"
+
+/* Mount(2) type/name for LOFS filesystem */
+/* #undef MOUNT_TYPE_LOFS */
+
+/* Mount(2) type/name for CDFS filesystem */
+#define MOUNT_TYPE_CDFS "cd9660"
+
+/* Mount(2) type/name for TFS filesystem */
+/* #undef MOUNT_TYPE_TFS */
+
+/* Mount(2) type/name for TMPFS filesystem */
+/* #undef MOUNT_TYPE_TMPFS */
+
+/* Mount(2) type/name for 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) */
+#define MOUNT_TYPE_IGNORE MNT_IGNORE
+
+/* Mount(2) type/name for NULLFS (loopback on bsd44) filesystem */
+#define MOUNT_TYPE_NULLFS "nullfs"
+
+/* Mount(2) type/name for UNIONFS filesystem */
+#define MOUNT_TYPE_UNIONFS "unionfs"
+
+/* Mount(2) type/name for UMAPFS (uid/gid mapping) filesystem */
+#define MOUNT_TYPE_UMAPFS "umapfs"
+
+
+/*
+ * 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 "nfs3"
+
+/* Mount-table entry name for PCFS filesystem */
+#define MNTTAB_TYPE_PCFS "msdosfs"
+
+/* Mount-table entry name for LOFS filesystem */
+/* #undef MNTTAB_TYPE_LOFS */
+
+/* Mount-table entry name for CDFS filesystem */
+#define MNTTAB_TYPE_CDFS "cd9660"
+
+/* Mount-table entry name for TFS filesystem */
+/* #undef MNTTAB_TYPE_TFS */
+
+/* Mount-table entry name for TMPFS filesystem */
+/* #undef MNTTAB_TYPE_TMPFS */
+
+/* Mount-table entry name for 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 "nullfs"
+
+/* Mount-table entry name for UNIONFS filesystem */
+#define MNTTAB_TYPE_UNIONFS "unionfs"
+
+/* Mount-table entry name for UMAPFS (uid/gid mapping) filesystem */
+#define MNTTAB_TYPE_UMAPFS "umapfs"
+
+/*
+ * 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 */
+
+/* Mount Table option string: support property lists (ACLs) */
+/* #undef MNTTAB_OPT_PROPLIST */
+
+/*
+ * Generic mount(2) options (hex numbers)
+ */
+
+/* asynchronous filesystem access */
+#define MNT2_GEN_OPT_ASYNC 0x40
+
+/* automounter filesystem (ignore) flag, used in bsdi-4.1 */
+/* #undef MNT2_GEN_OPT_AUTOMNTFS */
+
+/* directory hardlink */
+/* #undef MNT2_GEN_OPT_BIND */
+
+/* cache (what?) */
+/* #undef MNT2_GEN_OPT_CACHE */
+
+/* 6-argument mount */
+/* #undef MNT2_GEN_OPT_DATA */
+
+/* old (4-argument) mount (compatibility) */
+/* #undef MNT2_GEN_OPT_FSS */
+
+/* ignore mount entry in df output */
+#define MNT2_GEN_OPT_IGNORE 0x800000
+
+/* 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 */
+
+/* do not interpret special device files */
+#define MNT2_GEN_OPT_NODEV 0x10
+
+/* no exec calls allowed */
+#define MNT2_GEN_OPT_NOEXEC 0x4
+
+/* do not interpret special device files */
+/* #undef MNT2_GEN_OPT_NONDEV */
+
+/* Disallow mounts beneath this mount */
+/* #undef MNT2_GEN_OPT_NOSUB */
+
+/* Setuid programs disallowed */
+#define MNT2_GEN_OPT_NOSUID 0x8
+
+/* Return ENAMETOOLONG for long filenames */
+/* #undef MNT2_GEN_OPT_NOTRUNC */
+
+/* Pass mount option string to kernel */
+/* #undef MNT2_GEN_OPT_OPTIONSTR */
+
+/* allow overlay mounts */
+/* #undef MNT2_GEN_OPT_OVERLAY */
+
+/* check quotas */
+#define MNT2_GEN_OPT_QUOTA 0x2000
+
+/* Read-only */
+#define MNT2_GEN_OPT_RDONLY 0x1
+
+/* change options on an existing mount */
+/* #undef MNT2_GEN_OPT_REMOUNT */
+
+/* read only */
+/* #undef MNT2_GEN_OPT_RONLY */
+
+/* synchronize data immediately to filesystem */
+/* #undef MNT2_GEN_OPT_SYNC */
+
+/* synchronous filesystem access (same as SYNC) */
+#define MNT2_GEN_OPT_SYNCHRONOUS 0x2
+
+/* Mount with Sys 5-specific semantics */
+/* #undef MNT2_GEN_OPT_SYS5 */
+
+/* Union mount */
+/* #undef MNT2_GEN_OPT_UNION */
+
+/*
+ * 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 */
+#define MNT2_NFS_OPT_DEADTHRESH 0x4000
+
+/* Dismount in progress */
+/* #undef MNT2_NFS_OPT_DISMINPROG */
+
+/* Dismounted */
+/* #undef MNT2_NFS_OPT_DISMNT */
+
+/* Don't estimate rtt dynamically */
+#define MNT2_NFS_OPT_DUMBTIMR 0x800
+
+/* 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
+
+/* allow interrupts on hard mount */
+/* #undef MNT2_NFS_OPT_INTR */
+
+/* Bits set internally */
+/* #undef MNT2_NFS_OPT_INTERNAL */
+
+/* Use Kerberos authentication */
+#define MNT2_NFS_OPT_KERB 0x400
+
+/* use kerberos credentials */
+/* #undef MNT2_NFS_OPT_KERBEROS */
+
+/* transport's knetconfig structure */
+/* #undef MNT2_NFS_OPT_KNCONF */
+
+/* set lease term (nqnfs) */
+#define MNT2_NFS_OPT_LEASETERM 0x1000
+
+/* 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 */
+
+/* Don't use locking */
+/* #undef MNT2_NFS_OPT_NONLM */
+
+/* Use Nqnfs protocol */
+#define MNT2_NFS_OPT_NQNFS 0x100
+
+/* 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 */
+
+/* allow property list operations (ACLs over NFS) */
+/* #undef MNT2_NFS_OPT_PROPLIST */
+
+/* Use Readdirplus for NFSv3 */
+/* #undef MNT2_NFS_OPTS_RDIRPLUS */
+
+/* set read ahead */
+#define MNT2_NFS_OPT_READAHEAD 0x2000
+
+/* Set readdir size */
+#define MNT2_NFS_OPT_READDIRSIZE 0x20000
+
+/* Allocate a reserved port */
+#define MNT2_NFS_OPT_RESVPORT 0x8000
+
+/* set number of request retries */
+#define MNT2_NFS_OPT_RETRANS 0x10
+
+/* read only */
+/* #undef MNT2_NFS_OPT_RONLY */
+
+/* use RPC to do secure NFS time sync */
+/* #undef MNT2_NFS_OPT_RPCTIMESYNC */
+
+/* set read size */
+#define MNT2_NFS_OPT_RSIZE 0x4
+
+/* secure mount */
+/* #undef MNT2_NFS_OPT_SECURE */
+
+/* Send socket lock */
+/* #undef MNT2_NFS_OPT_SNDLOCK */
+
+/* soft mount (hard is default) */
+#define MNT2_NFS_OPT_SOFT 0x1
+
+/* spongy mount */
+/* #undef MNT2_NFS_OPT_SPONGY */
+
+/* set initial timeout */
+#define MNT2_NFS_OPT_TIMEO 0x8
+
+/* use TCP for mounts */
+/* #undef MNT2_NFS_OPT_TCP */
+
+/* linux NFSv3 */
+/* #undef MNT2_NFS_OPT_VER3 */
+
+/* Wait for authentication */
+/* #undef MNT2_NFS_OPT_WAITAUTH */
+
+/* Wants an authenticator */
+/* #undef MNT2_NFS_OPT_WANTAUTH */
+
+/* Want receive socket lock */
+/* #undef MNT2_NFS_OPT_WANTRCV */
+
+/* Want send socket lock */
+/* #undef MNT2_NFS_OPT_WANTSND */
+
+/* set write size */
+#define MNT2_NFS_OPT_WSIZE 0x2
+
+/* set symlink cache time-to-live */
+/* #undef MNT2_NFS_OPT_SYMTTL */
+
+/* paging threshold */
+/* #undef MNT2_NFS_OPT_PGTHRESH */
+
+/* 32<->64 dir cookie translation */
+/* #undef MNT2_NFS_OPT_XLATECOOKIE */
+
+/*
+ * 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 a 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 a bsize field? */
+/* #undef HAVE_FIELD_NFS_ARGS_T_BSIZE */
+
+/* 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 a gfs_flags field? */
+/* #undef HAVE_FIELD_NFS_ARGS_T_GFS_FLAGS */
+
+/* does struct nfs_args have a namlen field? */
+/* #undef HAVE_FIELD_NFS_ARGS_T_NAMLEN */
+
+/* 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 ifaddrs have field ifa_next? */
+#define HAVE_FIELD_STRUCT_IFADDRS_IFA_NEXT 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 */
+
+/*
+ * More definitions that depend on configure options.
+ */
+
+/* Turn off general debugging by default */
+/* #undef DEBUG */
+
+/* Turn off memory debugging by default */
+/* #undef DEBUG_MEM */
+
+/* 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.7"
+
+
+/* We [FREEBSD-NATIVE] 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 "freebsd5.0" */
+
+/* Define only name of host machine OS (eg. solaris2) */
+/* #define HOST_OS_NAME "freebsd5" */
+
+/* Define only version of host machine (eg. 2.5.1) */
+/* #define HOST_OS_VERSION "5.0" */
+
+/* Define the header version of (linux) hosts (eg. 2.2.10) */
+/* #define HOST_HEADER_VERSION "5.0" */
+
+/* Define name of host */
+/* #define HOST_NAME "dragon.nuxi.com" */
+
+/* Define user name */
+/* #define USER_NAME "obrien" */
+
+/* Define configuration date */
+/* #define CONFIG_DATE "Sat Sep 1 18:36:26 PDT 2001" */
+
+/* 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_freebsd3
+
+/* define if the host has NFS protocol headers in system headers */
+/* #undef HAVE_NFS_PROT_HEADERS */
+
+/* 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_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
+
+/**************************************************************************/
+/*** 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 if using alloca.c. */
+/* #undef C_ALLOCA */
+
+/* Define to empty if the keyword does not work. */
+/* #undef const */
+
+/* Define to one of _getb67, GETB67, getb67 for Cray-2 and Cray-YMP systems.
+ This function is required for alloca.c support on those systems. */
+/* #undef CRAY_STACKSEG_END */
+
+/* Define to 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 have alloca, as a function or macro. */
+#define HAVE_ALLOCA 1
+
+/* Define if you have <alloca.h> and it should be used (not on Ultrix). */
+/* #undef HAVE_ALLOCA_H */
+
+/* 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 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 */
+
+/* If using the C implementation of alloca, define if you know the
+ direction of stack growth for your system; otherwise it will be
+ automatically deduced at run-time.
+ STACK_DIRECTION > 0 => grows toward higher addresses
+ STACK_DIRECTION < 0 => grows toward lower addresses
+ STACK_DIRECTION = 0 => direction of growth unknown
+ */
+/* #undef STACK_DIRECTION */
+
+/* Define 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 <sys/types.h>
+#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
+
+/* 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 clock_gettime function. */
+#define HAVE_CLOCK_GETTIME 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 getifaddrs function. */
+#define HAVE_GETIFADDRS 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. */
+#define HAVE_HES_INIT 1
+
+/* Define if you have the hesiod_init function. */
+#define HAVE_HESIOD_INIT 1
+
+/* Define if you have the hesiod_reload function. */
+/* #undef HAVE_HESIOD_RELOAD */
+
+/* Define if you have the hesiod_to_bind function. */
+#define HAVE_HESIOD_TO_BIND 1
+
+/* 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 setitimer function. */
+#define HAVE_SETITIMER 1
+
+/* Define if you have the setresuid function. */
+#define HAVE_SETRESUID 1
+
+/* 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 vsnprintf function. */
+#define HAVE_VSNPRINTF 1
+
+/* 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 <db1/ndbm.h> header file. */
+/* #undef HAVE_DB1_NDBM_H */
+
+/* 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. */
+#define HAVE_HESIOD_H 1
+
+/* Define if you have the <hsfs/hsfs.h> header file. */
+/* #undef HAVE_HSFS_HSFS_H */
+
+/* Define if you have the <ifaddrs.h> header file. */
+#define HAVE_IFADDRS_H 1
+
+/* Define if you have the <irs.h> header file. */
+/* #undef HAVE_IRS_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. */
+#define HAVE_LIBGEN_H 1
+
+/* 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/kdev_t.h> header file. */
+/* #undef HAVE_LINUX_KDEV_T_H */
+
+/* Define if you have the <linux/list.h> header file. */
+/* #undef HAVE_LINUX_LIST_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 <linux/posix_types.h> header file. */
+/* #undef HAVE_LINUX_POSIX_TYPES_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. */
+/* #undef HAVE_MSDOSFS_MSDOSFSMOUNT_H */
+
+/* Define if you have the <fs/msdosfs/msdosfsmount.h> header file. */
+#define HAVE_FS_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. */
+#define HAVE_NETCONFIG_H 1
+
+/* 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/nfsmount.h> header file. */
+#define HAVE_NFS_NFSMOUNT_H 1
+
+/* 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. */
+#define HAVE_NSSWITCH_H 1
+
+/* 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/ufsmount.h> header file. */
+#define HAVE_UFS_UFS_UFSMOUNT_H 1
+
+/* Define if you have the <ufs/ufs_mount.h> header file. */
+/* #undef HAVE_UFS_UFS_MOUNT_H */
+
+/* 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 nsl library (-lnsl). */
+/* #undef HAVE_LIBNSL */
+
+/* Define if you have the rpc library (-lrpc). */
+/* #undef HAVE_LIBRPC */
+
+/* Define if you have the rpcsvc library (-lrpcsvc). */
+#define HAVE_LIBRPCSVC 1
+
+/* Define if you have the rt library (-lrt). */
+/* #undef HAVE_LIBRT */
+
+/* Name of package */
+#define PACKAGE "am-utils"
+
+/* Version number of package */
+#define VERSION "6.0.7"
+
+
+/**************************************************************************/
+/*** Everything below this line is part of the "BOTTOM" of acconfig.h. ***/
+/**************************************************************************/
+
+/*
+ * Existence of external definitions.
+ */
+
+/* does extern definition for sys_errlist[] exist? */
+#define HAVE_EXTERN_SYS_ERRLIST 1
+
+/* does extern definition for optarg exist? */
+#define HAVE_EXTERN_OPTARG 1
+
+/* does extern definition for clnt_spcreateerror() exist? */
+#define HAVE_EXTERN_CLNT_SPCREATEERROR 1
+
+/* does extern definition for clnt_sperrno() exist? */
+#define HAVE_EXTERN_CLNT_SPERRNO 1
+
+/* does extern definition for free() exist? */
+#define HAVE_EXTERN_FREE 1
+
+/* does extern definition for get_myaddress() exist? */
+#define HAVE_EXTERN_GET_MYADDRESS 1
+
+/* does extern definition for getccent() (hpux) exist? */
+/* #undef HAVE_EXTERN_GETCCENT */
+
+/* does extern definition for getdomainname() exist? */
+#define HAVE_EXTERN_GETDOMAINNAME 1
+
+/* does extern definition for gethostname() exist? */
+#define HAVE_EXTERN_GETHOSTNAME 1
+
+/* does extern definition for getlogin() exist? */
+#define HAVE_EXTERN_GETLOGIN 1
+
+/* does extern definition for gettablesize() exist? */
+/* #undef HAVE_EXTERN_GETTABLESIZE */
+
+/* does extern definition for getpagesize() exist? */
+#define HAVE_EXTERN_GETPAGESIZE 1
+
+/* does extern definition for innetgr() exist? */
+#define HAVE_EXTERN_INNETGR 1
+
+/* does extern definition for mkstemp() exist? */
+#define HAVE_EXTERN_MKSTEMP 1
+
+/* does extern definition for sbrk() exist? */
+#define HAVE_EXTERN_SBRK 1
+
+/* does extern definition for seteuid() exist? */
+#define HAVE_EXTERN_SETEUID 1
+
+/* does extern definition for setitimer() exist? */
+#define HAVE_EXTERN_SETITIMER 1
+
+/* does extern definition for strcasecmp() exist? */
+#define HAVE_EXTERN_STRCASECMP 1
+
+/* does extern definition for strdup() exist? */
+#define HAVE_EXTERN_STRDUP 1
+
+/* does extern definition for strstr() exist? */
+#define HAVE_EXTERN_STRSTR 1
+
+/* does extern definition for usleep() exist? */
+#define HAVE_EXTERN_USLEEP 1
+
+/* does extern definition for wait3() exist? */
+#define HAVE_EXTERN_WAIT3 1
+
+/* does extern definition for vsnprintf() exist? */
+#define HAVE_EXTERN_VSNPRINTF 1
+
+/* does extern definition for 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..b279c90
--- /dev/null
+++ b/usr.sbin/amd/include/newvers.sh
@@ -0,0 +1,43 @@
+# $NetBSD: mkconf,v 1.1.1.1 1997/07/24 21:20:12 christos Exp $
+# $FreeBSD$
+# mkconf
+# Generate local configuration parameters for amd
+#
+
+if [ -e $1 ]; then
+ eval `egrep '^[A-Z]+=' $1 | grep -v COPYRIGHT`
+ OS=`echo ${TYPE} | tr '[A-Z]' '[a-z]'`
+ echo '/* Define name and version of host machine (eg. solaris2.5.1) */'
+ echo "#define HOST_OS \"${OS}${REVISION}\""
+ echo '/* Define only name of host machine OS (eg. solaris2) */'
+ echo "#define HOST_OS_NAME \"${OS}${REVISION}\"" \
+ | sed -e 's/\.[-._0-9]*//'
+ echo '/* Define only version of host machine (eg. 2.5.1) */'
+ echo "#define HOST_OS_VERSION \"${REVISION}\""
+else
+cat << __NO_newvers_sh
+
+/* Define name and version of host machine (eg. solaris2.5.1) */
+#define HOST_OS "`uname -s | 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 | sed -e 's/[-([:alpha:]].*//'`"
+
+__NO_newvers_sh
+fi
+
+cat << __EOF
+
+/* Define name of host */
+#define HOST_NAME "`hostname`"
+
+/* Define user name */
+#define USER_NAME "`whoami`"
+
+/* Define configuration date */
+#define CONFIG_DATE "`date`"
+
+__EOF
diff --git a/usr.sbin/amd/libamu/Makefile b/usr.sbin/amd/libamu/Makefile
new file mode 100644
index 0000000..8a5de08
--- /dev/null
+++ b/usr.sbin/amd/libamu/Makefile
@@ -0,0 +1,31 @@
+# ex:ts=8
+#
+# Makefile for amd
+# This file is under a "BSD" copyright (c) by David O'Brien 1998
+#
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../contrib/amd/libamu \
+ ${.CURDIR}/../../../contrib/amd/conf/transp \
+ ${.CURDIR}/../../../contrib/amd/conf/mtab \
+ ${.CURDIR}/../../../contrib/amd/conf/umount
+
+LIB= amu
+INTERNALLIB= YES
+SRCS= hasmntopt.c misc_rpc.c mount_fs.c mtab.c nfs_prot_xdr.c \
+ util.c wire.c xutil.c
+
+# These would be links created by the GNU-style configure
+SRCS+= transp_sockets.c mtab_bsd.c umount_bsd44.c
+
+# Generated at compile time (replaces supplied xdr_func.c)
+SRCS+= nfs_prot_x.c
+CLEANFILES+= nfs_prot_x.c
+
+CFLAGS+= -I${.CURDIR}/../../../contrib/amd/libamu \
+ -I${DESTDIR}/usr/include/rpcsvc
+
+nfs_prot_x.c: ${NFS_PROT_X}
+ ${RPCCOM} -c -C -DWANT_NFS3 ${NFS_PROT_X} -o ${.TARGET}
+
+.include <bsd.lib.mk>
diff --git a/usr.sbin/amd/mk-amd-map/Makefile b/usr.sbin/amd/mk-amd-map/Makefile
new file mode 100644
index 0000000..ff792bd
--- /dev/null
+++ b/usr.sbin/amd/mk-amd-map/Makefile
@@ -0,0 +1,13 @@
+# ex:ts=8
+#
+# Makefile for amd
+# This file is under a "BSD" copyright (c) by David O'Brien 1998
+#
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../contrib/amd/mk-amd-map
+
+PROG= mk-amd-map
+MAN= mk-amd-map.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/amd/pawd/Makefile b/usr.sbin/amd/pawd/Makefile
new file mode 100644
index 0000000..c6bb1cc
--- /dev/null
+++ b/usr.sbin/amd/pawd/Makefile
@@ -0,0 +1,20 @@
+# ex:ts=8
+#
+# Makefile for amd
+# This file is under a "BSD" copyright (c) by David O'Brien 1998
+#
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../contrib/amd/amq
+
+BINDIR= /usr/bin
+
+PROG= pawd
+SRCS= pawd.c amq_clnt.c amq_xdr.c
+
+CFLAGS+= -I${.CURDIR}/../../../contrib/amd/amq
+
+DPADD= ${LIBAMU}
+LDADD= ${LIBAMU}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/amd/scripts/Makefile b/usr.sbin/amd/scripts/Makefile
new file mode 100644
index 0000000..822b951
--- /dev/null
+++ b/usr.sbin/amd/scripts/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../contrib/amd/scripts
+
+MAN= amd.conf.5
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/amd/wire-test/Makefile b/usr.sbin/amd/wire-test/Makefile
new file mode 100644
index 0000000..a07e690
--- /dev/null
+++ b/usr.sbin/amd/wire-test/Makefile
@@ -0,0 +1,16 @@
+# ex:ts=8
+#
+# Makefile for amd
+# This file is under a "BSD" copyright (c) by David O'Brien 1998
+#
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../contrib/amd/wire-test
+
+PROG= wire-test
+MAN= wire-test.8
+
+DPADD= ${LIBAMU}
+LDADD= ${LIBAMU}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ancontrol/Makefile b/usr.sbin/ancontrol/Makefile
new file mode 100644
index 0000000..f5a89af
--- /dev/null
+++ b/usr.sbin/ancontrol/Makefile
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+PROG= ancontrol
+MAN= ancontrol.8
+
+CFLAGS+= -I${.CURDIR}/../../sys -DANCACHE
+DPADD= ${LIBMD}
+LDADD= -lmd
+
+WARNS?= 2
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ancontrol/ancontrol.8 b/usr.sbin/ancontrol/ancontrol.8
new file mode 100644
index 0000000..5e0927f
--- /dev/null
+++ b/usr.sbin/ancontrol/ancontrol.8
@@ -0,0 +1,526 @@
+.\" Copyright (c) 1997, 1998, 1999
+.\" Bill Paul <wpaul@ee.columbia.edu> All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Bill Paul.
+.\" 4. Neither the name of the author nor the names of any co-contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+.\" THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd September 10, 1999
+.Dt ANCONTROL 8
+.Os
+.Sh NAME
+.Nm ancontrol
+.Nd configure Aironet 4500/4800 devices
+.Sh SYNOPSIS
+.Nm
+.Fl i Ar iface Fl A
+.Nm
+.Fl i Ar iface Fl N
+.Nm
+.Fl i Ar iface Fl S
+.Nm
+.Fl i Ar iface Fl I
+.Nm
+.Fl i Ar iface Fl T
+.Nm
+.Fl i Ar iface Fl C
+.Nm
+.Fl i Ar iface Fl t Cm 0 Ns - Ns Cm 4
+.Nm
+.Fl i Ar iface Fl s Cm 0 Ns - Ns Cm 3
+.Nm
+.Fl i Ar iface
+.Op Fl v Cm 1 Ns - Ns Cm 4
+.Fl a Ar AP
+.Nm
+.Fl i Ar iface Fl b Ar beacon_period
+.Nm
+.Fl i Ar iface
+.Op Fl v Cm 0 | 1
+.Fl d Cm 0 Ns - Ns Cm 3
+.Nm
+.Fl i Ar iface Fl e Cm 0 Ns - Ns Cm 4
+.Nm
+.Fl i Ar iface
+.Op Fl v Cm 0 Ns - Ns Cm 8
+.Fl k Ar key
+.Nm
+.Fl i Ar iface
+.Fl K Cm 0 Ns - Ns Cm 2
+.Nm
+.Fl i Ar iface
+.Fl W Cm 0 Ns - Ns Cm 2
+.Nm
+.Fl i Ar iface
+.Fl L Ar user_name
+.Nm
+.Fl i Ar iface Fl j Ar netjoin_timeout
+.Nm
+.Fl i Ar iface Fl l Ar station_name
+.Nm
+.Fl i Ar iface Fl m Ar mac_address
+.Nm
+.Fl i Ar iface
+.Op Fl v Cm 1 Ns - Ns Cm 3
+.Fl n Ar SSID
+.Nm
+.Fl i Ar iface Fl o Cm 0 | 1
+.Nm
+.Fl i Ar iface Fl p Ar tx_power
+.Nm
+.Fl i Ar iface Fl c Ar frequency
+.Nm
+.Fl i Ar iface Fl f Ar fragmentation_threshold
+.Nm
+.Fl i Ar iface Fl r Ar RTS_threshold
+.Nm
+.Fl i Ar iface Fl M Cm 0 Ns - Ns Cm 15
+.Nm
+.Fl h
+.Sh DESCRIPTION
+The
+.Nm
+command controls the operation of Aironet wireless networking
+devices via the
+.Xr an 4
+driver.
+Most of the parameters that can be changed relate to the
+IEEE 802.11 protocol which the Aironet cards implement.
+This includes such things as
+the station name, whether the station is operating in ad-hoc (point
+to point) or infrastructure mode, and the network name of a service
+set to join.
+The
+.Nm
+command can also be used to view the current NIC status, configuration
+and to dump out the values of the card's statistics counters.
+.Pp
+The
+.Ar iface
+argument given to
+.Nm
+should be the logical interface name associated with the Aironet
+device
+.Li ( an0 , an1 ,
+etc.).
+If one isn't specified the device
+.Dq Li an0
+will be assumed.
+.Pp
+The
+.Nm
+command is not designed to support the combination of arguments from different
+.Sx SYNOPSIS
+lines in a single
+.Nm
+invocation, and such combinations are not recommended.
+.Sh OPTIONS
+The options are as follows:
+.Bl -tag -width indent
+.It Fl i Ar iface Fl A
+Display the preferred access point list.
+The AP list can be used by
+stations to specify the MAC address of access points with which it
+wishes to associate.
+If no AP list is specified (the default) then
+the station will associate with the first access point that it finds
+which serves the SSID(s) specified in the SSID list.
+The AP list can
+be modified with the
+.Fl a
+option.
+.It Fl i Ar iface Fl N
+Display the SSID list.
+This is a list of service set IDs (i.e., network names)
+with which the station wishes to associate.
+There may be up to three SSIDs
+in the list: the station will go through the list in ascending order and
+associate with the first matching SSID that it finds.
+.It Fl i Ar iface Fl S
+Display NIC status information.
+This includes the current operating
+status, current BSSID, SSID, channel, beacon period and currently
+associated access point.
+The operating mode indicates the state of
+the NIC, MAC status and receiver status.
+When the
+.Qq Li synced
+keyword
+appears, it means the NIC has successfully associated with an access
+point, associated with an ad-hoc
+.Dq master
+station, or become a
+.Dq master
+itself.
+The beacon period can be anything between 20 and 976 milliseconds.
+The default is 100.
+.It Fl i Ar iface Fl I
+Display NIC capability information.
+This shows the device type,
+frequency, speed and power level capabilities and firmware revision levels.
+.It Fl i Ar iface Fl T
+Display the NIC's internal statistics counters.
+.It Fl i Ar iface Fl C
+Display current NIC configuration.
+This shows the current operation mode,
+receive mode, MAC address, power save settings, various timing settings,
+channel selection, diversity, transmit power and transmit speed.
+.It Fl i Ar iface Fl t Cm 0 Ns - Ns Cm 4
+Select transmit speed.
+The available settings are as follows:
+.Bl -column ".Em TX rate" -offset indent
+.Em "TX rate NIC speed"
+.It Cm 0 Ta "Auto -- NIC selects optimal speed"
+.It Cm 1 Ta "1Mbps fixed"
+.It Cm 2 Ta "2Mbps fixed"
+.It Cm 3 Ta "5.5Mbps fixed"
+.It Cm 4 Ta "11Mbps fixed"
+.El
+.Pp
+Note that the 5.5 and 11Mbps settings are only supported on the 4800
+series adapters: the 4500 series adapters have a maximum speed of 2Mbps.
+.It Fl i Ar iface Fl s Cm 0 Ns - Ns Cm 3
+Set power save mode.
+Valid selections are as follows:
+.Bl -column ".Em Selection" -offset indent
+.Em "Selection Power save mode"
+.It Cm 0 Ta "None - power save disabled"
+.It Cm 1 Ta "Constantly awake mode (CAM)"
+.It Cm 2 Ta "Power Save Polling (PSP)"
+.It Cm 3 Ta "Fast Power Save Polling (PSP-CAM)"
+.El
+.Pp
+Note that for IBSS (ad-hoc) mode, only PSP mode is supported, and only
+if the ATIM window is non-zero.
+.It Fl i Ar iface Oo Fl v Cm 1 Ns - Ns Cm 4 Oc Fl a Ar AP
+Set preferred access point.
+The
+.Ar AP
+is specified as a MAC address consisting of 6 hexadecimal values
+separated by colons.
+By default, the
+.Fl a
+option only sets the first entry in the AP list.
+The
+.Fl v
+modifier can be used to specify exactly which AP list entry is to be
+modified.
+If the
+.Fl v
+flag is not used, the first AP list entry will be changed.
+.It Fl i Ar iface Fl b Ar beacon_period
+Set the ad-hoc mode beacon period.
+The
+.Ar beacon_period
+is specified in milliseconds.
+The default is 100ms.
+.It Fl i Ar iface Oo Fl v Cm 0 | 1 Oc Fl d Cm 0 Ns - Ns Cm 3
+Select the antenna diversity.
+Aironet devices can be configured with up
+to two antennas, and transmit and receive diversity can be configured
+accordingly.
+Valid selections are as follows:
+.Bl -column ".Em Selection" -offset indent
+.Em "Selection Diversity"
+.It Cm 0 Ta "Select factory default diversity"
+.It Cm 1 Ta "Antenna 1 only"
+.It Cm 2 Ta "Antenna 2 only"
+.It Cm 3 Ta "Antenna 1 and 2"
+.El
+.Pp
+The receive and transmit diversity can be set independently.
+The user
+must specify which diversity setting is to be modified by using the
+.Fl v
+option: selection
+.Cm 0
+sets the receive diversity and
+.Cm 1
+sets the transmit diversity.
+.It Fl i Ar iface Fl e Cm 0 Ns - Ns Cm 4
+Set the transmit WEP key to use.
+Note that until this command is issued, the device will use the
+last key programmed.
+The transmit key is stored in NVRAM.
+Currently
+set transmit key can be checked via
+.Fl C
+option.
+Selection
+.Cm 4
+sets the card in
+.Dq "Home Network Mode"
+and uses the home key.
+.It Fl i Ar iface Oo Fl v Cm 0 Ns - Ns Cm 8 Oc Fl k Ar key
+Set a WEP key.
+For 40 bit prefix 10 hex character with 0x.
+For 128 bit prefix 26 hex character with 0x.
+Use
+.Qq
+as the key to erase the key.
+Supports 4 keys; even numbers are for permanent keys
+and odd number are for temporary keys.
+For example,
+.Fl v Cm 1
+sets the first temporary key.
+(A
+.Dq permanent
+key is stored in NVRAM; a
+.Dq temporary
+key is not.)
+Note that the device will use the most recently-programmed key by default.
+Currently set keys can be checked via
+.Fl C
+option, only the sizes of the
+keys are returned.
+The value of
+.Cm 8
+is for the home key.
+Note that the value for the home key can be read back from firmware.
+.It Fl i Ar iface Fl K Cm 0 Ns - Ns Cm 2
+Set authorization type.
+Use
+.Cm 0
+for none,
+.Cm 1
+for
+.Dq Open ,
+.Cm 2
+for
+.Dq "Shared Key" .
+.It Fl i Ar iface Fl W Cm 0 Ns - Ns Cm 2
+Enable WEP.
+Use
+.Cm 0
+for no WEP,
+.Cm 1
+to enable full WEP,
+.Cm 2
+for mixed cell.
+.It Fl i Ar iface Fl L Ar user_name
+Enable LEAP and query for password.
+It will check to see if it has authenticated for up to 60s.
+To disable LEAP, set WEP mode.
+.It Fl i Ar iface Fl j Ar netjoin_timeout
+Set the ad-hoc network join timeout.
+When a station is first activated
+in ad-hoc mode, it will search out a
+.Dq master
+station with the desired
+SSID and associate with it.
+If the station is unable to locate another
+station with the same SSID after a suitable timeout, it sets itself up
+as the
+.Dq master
+so that other stations may associate with it.
+This
+timeout defaults to 10000 milliseconds (10 seconds) but may be changed
+with this option.
+The timeout should be specified in milliseconds.
+.It Fl i Ar iface Fl l Ar station_name
+Set the station name used internally by the NIC.
+The
+.Ar station_name
+can be any text string up to 16 characters in length.
+The default name
+is set by the driver to
+.Dq Li FreeBSD .
+.It Fl i Ar iface Fl m Ar mac_address
+Set the station address for the specified interface.
+The
+.Ar mac_address
+is specified as a series of six hexadecimal values separated by colons,
+e.g.:
+.Li 00:60:1d:12:34:56 .
+This programs the new address into the card
+and updates the interface as well.
+.It Fl i Ar iface Oo Fl v Cm 1 Ns - Ns Cm 3 Oc Fl n Ar SSID
+Set the desired SSID (network name).
+There are three SSIDs which allows
+the NIC to work with access points at several locations without needing
+to be reconfigured.
+The NIC checks each SSID in sequence when searching
+for a match.
+The SSID to be changed can be specified with the
+.Fl v
+modifier option.
+If the
+.Fl v
+flag isn't used, the first SSID in the list is set.
+.It Fl i Ar iface Fl o Cm 0 | 1
+Set the operating mode of the Aironet interface.
+Valid selections are
+.Cm 0
+for ad-hoc mode and
+.Cm 1
+for infrastructure mode.
+The default driver setting is for infrastructure
+mode.
+.It Fl i Ar iface Fl p Ar tx_power
+Set the transmit power level in milliwatts.
+Valid power settings
+vary depending on the actual NIC and can be viewed by dumping the
+device capabilities with the
+.Fl I
+flag.
+Typical values are 1, 5, 20, 50 and 100mW.
+Selecting 0 sets
+the factory default.
+.It Fl i Ar iface Fl c Ar frequency
+Set the radio frequency of a given interface.
+The
+.Ar frequency
+should be specfied as a channel ID as shown in the table below.
+The
+list of available frequencies is dependent on radio regulations specified
+by regional authorities.
+Recognized regulatory authorities include
+the FCC (United States), ETSI (Europe), France and Japan.
+Frequencies
+in the table are specified in Mhz.
+.Bl -column ".Em Channel ID" ".Em FCC" ".Em ETSI" ".Em France" ".Em Japan" -offset indent
+.Em "Channel ID FCC ETSI France Japan"
+.It Cm 1 Ta 2412 Ta 2412 Ta - Ta -
+.It Cm 2 Ta 2417 Ta 2417 Ta - Ta -
+.It Cm 3 Ta 2422 Ta 2422 Ta - Ta -
+.It Cm 4 Ta 2427 Ta 2427 Ta - Ta -
+.It Cm 5 Ta 2432 Ta 2432 Ta - Ta -
+.It Cm 6 Ta 2437 Ta 2437 Ta - Ta -
+.It Cm 7 Ta 2442 Ta 2442 Ta - Ta -
+.It Cm 8 Ta 2447 Ta 2447 Ta - Ta -
+.It Cm 9 Ta 2452 Ta 2452 Ta - Ta -
+.It Cm 10 Ta 2457 Ta 2457 Ta 2457 Ta -
+.It Cm 11 Ta 2462 Ta 2462 Ta 2462 Ta -
+.It Cm 12 Ta - Ta 2467 Ta 2467 Ta -
+.It Cm 13 Ta - Ta 2472 Ta 2472 Ta -
+.It Cm 14 Ta - Ta - Ta - Ta 2484
+.El
+.Pp
+If an illegal channel is specified, the
+NIC will revert to its default channel.
+For NICs sold in the United States
+and Europe, the default channel is 3.
+For NICs sold in France, the default
+channel is 11.
+For NICs sold in Japan, the only available channel is 14.
+Note that two stations must be set to the same channel in order to
+communicate.
+.It Fl i Ar iface Fl f Ar fragmentation_threshold
+Set the fragmentation threshold in bytes.
+This threshold controls the
+point at which outgoing packets will be split into multiple fragments.
+If a single fragment is not sent successfully, only that fragment will
+need to be retransmitted instead of the whole packet.
+The fragmentation
+threshold can be anything from 64 to 2312 bytes.
+The default is 2312.
+.It Fl i Ar iface Fl r Ar RTS_threshold
+Set the RTS/CTS threshold for a given interface.
+This controls the
+number of bytes used for the RTS/CTS handhake boundary.
+The
+.Ar RTS_threshold
+can be any value between 0 and 2312.
+The default is 2312.
+.It Fl i Ar iface Fl M Cm 0 Ns - Ns Cm 15
+Set monitor mode via bit mask, meaning:
+.Pp
+.Bl -tag -width indent -offset indent -compact
+.It Em Bit
+.Em Meaning
+.It 0
+to not dump 802.11 packet.
+.It 1
+to enable 802.11 monitor.
+.It 2
+to monitor any SSID.
+.It 4
+to not skip beacons, monitor beacons produces a high system load.
+.It 8
+to enable full Aironet header returned via BPF.
+Note it appears that a SSID must be set.
+.El
+.It Fl h
+Print a list of available options and sample usage.
+.El
+.Sh SECURITY NOTES
+WEP
+.Pq Dq "wired equivalent privacy"
+is based on the RC4 algorithm,
+using a 24 bit initialization vector.
+.Pp
+RC4 is supposedly vunerable to certain known plaintext attacks,
+especially with 40 bit keys.
+So the security of WEP in part depends on how much known plaintext
+is transmitted.
+.Pp
+Because of this, although counter-intuitive, using
+.Dq "shared key"
+authentication (which involves sending known plaintext) is less
+secure than using
+.Dq open
+authentication when WEP is enabled.
+.Pp
+Devices may alternate among all of the configured WEP keys when
+tranmitting packets.
+Therefore, all configured keys (up to four) must agree.
+.Sh EXAMPLES
+.Bd -literal -offset indent
+ancontrol -i an0 -v 0 -k 0x12345678901234567890123456
+ancontrol -i an0 -K 2
+ancontrol -i an0 -W 1
+ancontrol -i an0 -e 0
+.Ed
+.Pp
+Sets a WEP key 0, enables
+.Dq "Shared Key"
+authentication, enables full WEP
+and uses transmit key 0.
+.Sh SEE ALSO
+.Xr an 4 ,
+.Xr ifconfig 8
+.Sh HISTORY
+The
+.Nm
+command first appeared in
+.Fx 4.0 .
+.Sh BUGS
+The statistics counters do not seem to show the amount of transmit
+and received frames as increasing.
+This is likely due to the fact that
+the
+.Xr an 4
+driver uses unmodified packet mode instead of letting the NIC perform
+802.11/ethernet encapsulation itself.
+.Pp
+Setting the channel does not seem to have any effect.
+.Sh AUTHORS
+The
+.Nm
+command was written by
+.An Bill Paul Aq wpaul@ee.columbia.edu .
diff --git a/usr.sbin/ancontrol/ancontrol.c b/usr.sbin/ancontrol/ancontrol.c
new file mode 100644
index 0000000..6fb1cd3
--- /dev/null
+++ b/usr.sbin/ancontrol/ancontrol.c
@@ -0,0 +1,1760 @@
+/*
+ * Copyright 1997, 1998, 1999
+ * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] = "@(#) Copyright (c) 1997, 1998, 1999\
+ Bill Paul. All rights reserved.";
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif
+
+#include <sys/types.h>
+#include <sys/cdefs.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <arpa/inet.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/ethernet.h>
+
+#include <dev/an/if_aironet_ieee.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <err.h>
+#include <md4.h>
+
+static void an_getval __P((const char *, struct an_req *));
+static void an_setval __P((const char *, struct an_req *));
+static void an_printwords __P((u_int16_t *, int));
+static void an_printspeeds __P((u_int8_t*, int));
+static void an_printbool __P((int));
+static void an_printhex __P((char *, int));
+static void an_printstr __P((char *, int));
+static void an_dumpstatus __P((const char *));
+static void an_dumpstats __P((const char *));
+static void an_dumpconfig __P((const char *));
+static void an_dumpcaps __P((const char *));
+static void an_dumpssid __P((const char *));
+static void an_dumpap __P((const char *));
+static void an_setconfig __P((const char *, int, void *));
+static void an_setssid __P((const char *, int, void *));
+static void an_setap __P((const char *, int, void *));
+static void an_setspeed __P((const char *, int, void *));
+static void an_readkeyinfo __P((const char *));
+#ifdef ANCACHE
+static void an_zerocache __P((const char *));
+static void an_readcache __P((const char *));
+#endif
+static int an_hex2int __P((char));
+static void an_str2key __P((char *, struct an_ltv_key *));
+static void an_setkeys __P((const char *, char *, int));
+static void an_enable_tx_key __P((const char *, char *));
+static void an_enable_leap_mode __P((const char *, char *));
+static void usage __P((char *));
+int main __P((int, char **));
+
+#define ACT_DUMPSTATS 1
+#define ACT_DUMPCONFIG 2
+#define ACT_DUMPSTATUS 3
+#define ACT_DUMPCAPS 4
+#define ACT_DUMPSSID 5
+#define ACT_DUMPAP 6
+
+#define ACT_SET_OPMODE 7
+#define ACT_SET_SSID1 8
+#define ACT_SET_SSID2 9
+#define ACT_SET_SSID3 10
+#define ACT_SET_FREQ 11
+#define ACT_SET_AP1 12
+#define ACT_SET_AP2 13
+#define ACT_SET_AP3 14
+#define ACT_SET_AP4 15
+#define ACT_SET_DRIVERNAME 16
+#define ACT_SET_SCANMODE 17
+#define ACT_SET_TXRATE 18
+#define ACT_SET_RTS_THRESH 19
+#define ACT_SET_PWRSAVE 20
+#define ACT_SET_DIVERSITY_RX 21
+#define ACT_SET_DIVERSITY_TX 22
+#define ACT_SET_RTS_RETRYLIM 23
+#define ACT_SET_WAKE_DURATION 24
+#define ACT_SET_BEACON_PERIOD 25
+#define ACT_SET_TXPWR 26
+#define ACT_SET_FRAG_THRESH 27
+#define ACT_SET_NETJOIN 28
+#define ACT_SET_MYNAME 29
+#define ACT_SET_MAC 30
+
+#define ACT_DUMPCACHE 31
+#define ACT_ZEROCACHE 32
+
+#define ACT_ENABLE_WEP 33
+#define ACT_SET_KEY_TYPE 34
+#define ACT_SET_KEYS 35
+#define ACT_ENABLE_TX_KEY 36
+#define ACT_SET_MONITOR_MODE 37
+#define ACT_SET_LEAP_MODE 38
+
+static void an_getval(iface, areq)
+ const char *iface;
+ struct an_req *areq;
+{
+ struct ifreq ifr;
+ int s;
+
+ bzero((char *)&ifr, sizeof(ifr));
+
+ strlcpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name));
+ ifr.ifr_data = (caddr_t)areq;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+
+ if (s == -1)
+ err(1, "socket");
+
+ if (ioctl(s, SIOCGAIRONET, &ifr) == -1)
+ err(1, "SIOCGAIRONET");
+
+ close(s);
+
+ return;
+}
+
+static void an_setval(iface, areq)
+ const char *iface;
+ struct an_req *areq;
+{
+ struct ifreq ifr;
+ int s;
+
+ bzero((char *)&ifr, sizeof(ifr));
+
+ strlcpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name));
+ ifr.ifr_data = (caddr_t)areq;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+
+ if (s == -1)
+ err(1, "socket");
+
+ if (ioctl(s, SIOCSAIRONET, &ifr) == -1)
+ err(1, "SIOCSAIRONET");
+
+ close(s);
+
+ return;
+}
+
+static void an_printstr(str, len)
+ char *str;
+ int len;
+{
+ int i;
+
+ for (i = 0; i < len - 1; i++) {
+ if (str[i] == '\0')
+ str[i] = ' ';
+ }
+
+ printf("[ %.*s ]", len, str);
+
+ return;
+}
+
+static void an_printwords(w, len)
+ u_int16_t *w;
+ int len;
+{
+ int i;
+
+ printf("[ ");
+ for (i = 0; i < len; i++)
+ printf("%d ", w[i]);
+ printf("]");
+
+ return;
+}
+
+static void an_printspeeds(w, len)
+ u_int8_t *w;
+ int len;
+{
+ int i;
+
+ printf("[ ");
+ for (i = 0; i < len && w[i]; i++)
+ printf("%2.1fMbps ", w[i] * 0.500);
+ printf("]");
+
+ return;
+}
+
+static void an_printbool(val)
+ int val;
+{
+ if (val)
+ printf("[ On ]");
+ else
+ printf("[ Off ]");
+
+ return;
+}
+
+static void an_printhex(ptr, len)
+ char *ptr;
+ int len;
+{
+ int i;
+
+ printf("[ ");
+ for (i = 0; i < len; i++) {
+ printf("%02x", ptr[i] & 0xFF);
+ if (i < (len - 1))
+ printf(":");
+ }
+
+ printf(" ]");
+ return;
+}
+
+
+
+static void an_dumpstatus(iface)
+ const char *iface;
+{
+ struct an_ltv_status *sts;
+ struct an_req areq;
+
+ areq.an_len = sizeof(areq);
+ areq.an_type = AN_RID_STATUS;
+
+ an_getval(iface, &areq);
+
+ sts = (struct an_ltv_status *)&areq;
+
+ printf("MAC address:\t\t");
+ an_printhex((char *)&sts->an_macaddr, ETHER_ADDR_LEN);
+ printf("\nOperating mode:\t\t[ ");
+ if (sts->an_opmode & AN_STATUS_OPMODE_CONFIGURED)
+ printf("configured ");
+ if (sts->an_opmode & AN_STATUS_OPMODE_MAC_ENABLED)
+ printf("MAC ON ");
+ if (sts->an_opmode & AN_STATUS_OPMODE_RX_ENABLED)
+ printf("RX ON ");
+ if (sts->an_opmode & AN_STATUS_OPMODE_IN_SYNC)
+ printf("synced ");
+ if (sts->an_opmode & AN_STATUS_OPMODE_ASSOCIATED)
+ printf("associated ");
+ if (sts->an_opmode & AN_STATUS_OPMODE_LEAP)
+ printf("LEAP ");
+ if (sts->an_opmode & AN_STATUS_OPMODE_ERROR)
+ printf("error ");
+ printf("]\n");
+ printf("Error code:\t\t");
+ an_printhex((char *)&sts->an_errcode, 1);
+ printf("\nSignal quality:\t\t");
+ an_printhex((char *)&sts->an_cur_signal_quality, 1);
+ printf("\nSignal strength:\t[ %d%% ]",sts->an_normalized_rssi);
+ printf("\nMax Noise:\t\t[ %d%% ]",sts->an_avg_noise_prev_min);
+ /*
+ * XXX: This uses the old definition of the rate field (units of
+ * 500kbps). Technically the new definition is that this field
+ * contains arbitrary values, but no devices which need this
+ * support exist and the IEEE seems to intend to use the old
+ * definition until they get something big so we'll keep using
+ * it as well because this will work with new cards with
+ * rate <= 63.5Mbps.
+ */
+ printf("\nCurrent TX rate:\t[ %d%s ]", sts->an_current_tx_rate / 2,
+ (sts->an_current_tx_rate % 2) ? ".5" : "");
+ printf("\nCurrent SSID:\t\t");
+ an_printstr((char *)&sts->an_ssid, sts->an_ssidlen);
+ printf("\nCurrent AP name:\t");
+ an_printstr((char *)&sts->an_ap_name, 16);
+ printf("\nCurrent BSSID:\t\t");
+ an_printhex((char *)&sts->an_cur_bssid, ETHER_ADDR_LEN);
+ printf("\nBeacon period:\t\t");
+ an_printwords(&sts->an_beacon_period, 1);
+ printf("\nDTIM period:\t\t");
+ an_printwords(&sts->an_dtim_period, 1);
+ printf("\nATIM duration:\t\t");
+ an_printwords(&sts->an_atim_duration, 1);
+ printf("\nHOP period:\t\t");
+ an_printwords(&sts->an_hop_period, 1);
+ printf("\nChannel set:\t\t");
+ an_printwords(&sts->an_channel_set, 1);
+ printf("\nCurrent channel:\t");
+ an_printwords(&sts->an_cur_channel, 1);
+ printf("\nHops to backbone:\t");
+ an_printwords(&sts->an_hops_to_backbone, 1);
+ printf("\nTotal AP load:\t\t");
+ an_printwords(&sts->an_ap_total_load, 1);
+ printf("\nOur generated load:\t");
+ an_printwords(&sts->an_our_generated_load, 1);
+ printf("\nAccumulated ARL:\t");
+ an_printwords(&sts->an_accumulated_arl, 1);
+ printf("\n");
+ return;
+}
+
+static void an_dumpcaps(iface)
+ const char *iface;
+{
+ struct an_ltv_caps *caps;
+ struct an_req areq;
+ u_int16_t tmp;
+
+ areq.an_len = sizeof(areq);
+ areq.an_type = AN_RID_CAPABILITIES;
+
+ an_getval(iface, &areq);
+
+ caps = (struct an_ltv_caps *)&areq;
+
+ printf("OUI:\t\t\t");
+ an_printhex((char *)&caps->an_oui, 3);
+ printf("\nProduct number:\t\t");
+ an_printwords(&caps->an_prodnum, 1);
+ printf("\nManufacturer name:\t");
+ an_printstr((char *)&caps->an_manufname, 32);
+ printf("\nProduce name:\t\t");
+ an_printstr((char *)&caps->an_prodname, 16);
+ printf("\nFirmware version:\t");
+ an_printstr((char *)&caps->an_prodvers, 1);
+ printf("\nOEM MAC address:\t");
+ an_printhex((char *)&caps->an_oemaddr, ETHER_ADDR_LEN);
+ printf("\nAironet MAC address:\t");
+ an_printhex((char *)&caps->an_aironetaddr, ETHER_ADDR_LEN);
+ printf("\nRadio type:\t\t[ ");
+ if (caps->an_radiotype & AN_RADIOTYPE_80211_FH)
+ printf("802.11 FH");
+ else if (caps->an_radiotype & AN_RADIOTYPE_80211_DS)
+ printf("802.11 DS");
+ else if (caps->an_radiotype & AN_RADIOTYPE_LM2000_DS)
+ printf("LM2000 DS");
+ else
+ printf("unknown (%x)", caps->an_radiotype);
+ printf(" ]");
+ printf("\nRegulatory domain:\t");
+ an_printwords(&caps->an_regdomain, 1);
+ printf("\nAssigned CallID:\t");
+ an_printhex((char *)&caps->an_callid, 6);
+ printf("\nSupported speeds:\t");
+ an_printspeeds(caps->an_rates, 8);
+ printf("\nRX Diversity:\t\t[ ");
+ if (caps->an_rx_diversity == AN_DIVERSITY_ANTENNA_1_ONLY)
+ printf("antenna 1 only");
+ else if (caps->an_rx_diversity == AN_DIVERSITY_ANTENNA_2_ONLY)
+ printf("antenna 2 only");
+ else if (caps->an_rx_diversity == AN_DIVERSITY_ANTENNA_1_AND_2)
+ printf("antenna 1 and 2");
+ printf(" ]");
+ printf("\nTX Diversity:\t\t[ ");
+ if (caps->an_rx_diversity == AN_DIVERSITY_ANTENNA_1_ONLY)
+ printf("antenna 1 only");
+ else if (caps->an_rx_diversity == AN_DIVERSITY_ANTENNA_2_ONLY)
+ printf("antenna 2 only");
+ else if (caps->an_rx_diversity == AN_DIVERSITY_ANTENNA_1_AND_2)
+ printf("antenna 1 and 2");
+ printf(" ]");
+ printf("\nSupported power levels:\t");
+ an_printwords(caps->an_tx_powerlevels, 8);
+ printf("\nHardware revision:\t");
+ tmp = ntohs(caps->an_hwrev);
+ an_printhex((char *)&tmp, 2);
+ printf("\nSoftware revision:\t");
+ tmp = ntohs(caps->an_fwrev);
+ an_printhex((char *)&tmp, 2);
+ printf("\nSoftware subrevision:\t");
+ tmp = ntohs(caps->an_fwsubrev);
+ an_printhex((char *)&tmp, 2);
+ printf("\nInterface revision:\t");
+ tmp = ntohs(caps->an_ifacerev);
+ an_printhex((char *)&tmp, 2);
+ printf("\nBootblock revision:\t");
+ tmp = ntohs(caps->an_bootblockrev);
+ an_printhex((char *)&tmp, 2);
+ printf("\n");
+ return;
+}
+
+static void an_dumpstats(iface)
+ const char *iface;
+{
+ struct an_ltv_stats *stats;
+ struct an_req areq;
+ caddr_t ptr;
+
+ areq.an_len = sizeof(areq);
+ areq.an_type = AN_RID_32BITS_CUM;
+
+ an_getval(iface, &areq);
+
+ ptr = (caddr_t)&areq;
+ ptr -= 2;
+ stats = (struct an_ltv_stats *)ptr;
+
+ printf("RX overruns:\t\t\t\t\t[ %d ]\n", stats->an_rx_overruns);
+ printf("RX PLCP CSUM errors:\t\t\t\t[ %d ]\n",
+ stats->an_rx_plcp_csum_errs);
+ printf("RX PLCP format errors:\t\t\t\t[ %d ]\n",
+ stats->an_rx_plcp_format_errs);
+ printf("RX PLCP length errors:\t\t\t\t[ %d ]\n",
+ stats->an_rx_plcp_len_errs);
+ printf("RX MAC CRC errors:\t\t\t\t[ %d ]\n",
+ stats->an_rx_mac_crc_errs);
+ printf("RX MAC CRC OK:\t\t\t\t\t[ %d ]\n",
+ stats->an_rx_mac_crc_ok);
+ printf("RX WEP errors:\t\t\t\t\t[ %d ]\n",
+ stats->an_rx_wep_errs);
+ printf("RX WEP OK:\t\t\t\t\t[ %d ]\n",
+ stats->an_rx_wep_ok);
+ printf("Long retries:\t\t\t\t\t[ %d ]\n",
+ stats->an_retry_long);
+ printf("Short retries:\t\t\t\t\t[ %d ]\n",
+ stats->an_retry_short);
+ printf("Retries exhausted:\t\t\t\t[ %d ]\n",
+ stats->an_retry_max);
+ printf("Bad ACK:\t\t\t\t\t[ %d ]\n",
+ stats->an_no_ack);
+ printf("Bad CTS:\t\t\t\t\t[ %d ]\n",
+ stats->an_no_cts);
+ printf("RX good ACKs:\t\t\t\t\t[ %d ]\n",
+ stats->an_rx_ack_ok);
+ printf("RX good CTSs:\t\t\t\t\t[ %d ]\n",
+ stats->an_rx_cts_ok);
+ printf("TX good ACKs:\t\t\t\t\t[ %d ]\n",
+ stats->an_tx_ack_ok);
+ printf("TX good RTSs:\t\t\t\t\t[ %d ]\n",
+ stats->an_tx_rts_ok);
+ printf("TX good CTSs:\t\t\t\t\t[ %d ]\n",
+ stats->an_tx_cts_ok);
+ printf("LMAC multicasts transmitted:\t\t\t[ %d ]\n",
+ stats->an_tx_lmac_mcasts);
+ printf("LMAC broadcasts transmitted:\t\t\t[ %d ]\n",
+ stats->an_tx_lmac_bcasts);
+ printf("LMAC unicast frags transmitted:\t\t\t[ %d ]\n",
+ stats->an_tx_lmac_ucast_frags);
+ printf("LMAC unicasts transmitted:\t\t\t[ %d ]\n",
+ stats->an_tx_lmac_ucasts);
+ printf("Beacons transmitted:\t\t\t\t[ %d ]\n",
+ stats->an_tx_beacons);
+ printf("Beacons received:\t\t\t\t[ %d ]\n",
+ stats->an_rx_beacons);
+ printf("Single transmit collisions:\t\t\t[ %d ]\n",
+ stats->an_tx_single_cols);
+ printf("Multiple transmit collisions:\t\t\t[ %d ]\n",
+ stats->an_tx_multi_cols);
+ printf("Transmits without deferrals:\t\t\t[ %d ]\n",
+ stats->an_tx_defers_no);
+ printf("Transmits deferred due to protocol:\t\t[ %d ]\n",
+ stats->an_tx_defers_prot);
+ printf("Transmits deferred due to energy detect:\t\t[ %d ]\n",
+ stats->an_tx_defers_energy);
+ printf("RX duplicate frames/frags:\t\t\t[ %d ]\n",
+ stats->an_rx_dups);
+ printf("RX partial frames:\t\t\t\t[ %d ]\n",
+ stats->an_rx_partial);
+ printf("TX max lifetime exceeded:\t\t\t[ %d ]\n",
+ stats->an_tx_too_old);
+ printf("RX max lifetime exceeded:\t\t\t[ %d ]\n",
+ stats->an_tx_too_old);
+ printf("Sync lost due to too many missed beacons:\t[ %d ]\n",
+ stats->an_lostsync_missed_beacons);
+ printf("Sync lost due to ARL exceeded:\t\t\t[ %d ]\n",
+ stats->an_lostsync_arl_exceeded);
+ printf("Sync lost due to deauthentication:\t\t[ %d ]\n",
+ stats->an_lostsync_deauthed);
+ printf("Sync lost due to disassociation:\t\t[ %d ]\n",
+ stats->an_lostsync_disassociated);
+ printf("Sync lost due to excess change in TSF timing:\t[ %d ]\n",
+ stats->an_lostsync_tsf_timing);
+ printf("Host transmitted multicasts:\t\t\t[ %d ]\n",
+ stats->an_tx_host_mcasts);
+ printf("Host transmitted broadcasts:\t\t\t[ %d ]\n",
+ stats->an_tx_host_bcasts);
+ printf("Host transmitted unicasts:\t\t\t[ %d ]\n",
+ stats->an_tx_host_ucasts);
+ printf("Host transmission failures:\t\t\t[ %d ]\n",
+ stats->an_tx_host_failed);
+ printf("Host received multicasts:\t\t\t[ %d ]\n",
+ stats->an_rx_host_mcasts);
+ printf("Host received broadcasts:\t\t\t[ %d ]\n",
+ stats->an_rx_host_bcasts);
+ printf("Host received unicasts:\t\t\t\t[ %d ]\n",
+ stats->an_rx_host_ucasts);
+ printf("Host receive discards:\t\t\t\t[ %d ]\n",
+ stats->an_rx_host_discarded);
+ printf("HMAC transmitted multicasts:\t\t\t[ %d ]\n",
+ stats->an_tx_hmac_mcasts);
+ printf("HMAC transmitted broadcasts:\t\t\t[ %d ]\n",
+ stats->an_tx_hmac_bcasts);
+ printf("HMAC transmitted unicasts:\t\t\t[ %d ]\n",
+ stats->an_tx_hmac_ucasts);
+ printf("HMAC transmissions failed:\t\t\t[ %d ]\n",
+ stats->an_tx_hmac_failed);
+ printf("HMAC received multicasts:\t\t\t[ %d ]\n",
+ stats->an_rx_hmac_mcasts);
+ printf("HMAC received broadcasts:\t\t\t[ %d ]\n",
+ stats->an_rx_hmac_bcasts);
+ printf("HMAC received unicasts:\t\t\t\t[ %d ]\n",
+ stats->an_rx_hmac_ucasts);
+ printf("HMAC receive discards:\t\t\t\t[ %d ]\n",
+ stats->an_rx_hmac_discarded);
+ printf("HMAC transmits accepted:\t\t\t[ %d ]\n",
+ stats->an_tx_hmac_accepted);
+ printf("SSID mismatches:\t\t\t\t[ %d ]\n",
+ stats->an_ssid_mismatches);
+ printf("Access point mismatches:\t\t\t[ %d ]\n",
+ stats->an_ap_mismatches);
+ printf("Speed mismatches:\t\t\t\t[ %d ]\n",
+ stats->an_rates_mismatches);
+ printf("Authentication rejects:\t\t\t\t[ %d ]\n",
+ stats->an_auth_rejects);
+ printf("Authentication timeouts:\t\t\t[ %d ]\n",
+ stats->an_auth_timeouts);
+ printf("Association rejects:\t\t\t\t[ %d ]\n",
+ stats->an_assoc_rejects);
+ printf("Association timeouts:\t\t\t\t[ %d ]\n",
+ stats->an_assoc_timeouts);
+ printf("Management frames received:\t\t\t[ %d ]\n",
+ stats->an_rx_mgmt_pkts);
+ printf("Management frames transmitted:\t\t\t[ %d ]\n",
+ stats->an_tx_mgmt_pkts);
+ printf("Refresh frames received:\t\t\t[ %d ]\n",
+ stats->an_rx_refresh_pkts),
+ printf("Refresh frames transmitted:\t\t\t[ %d ]\n",
+ stats->an_tx_refresh_pkts),
+ printf("Poll frames received:\t\t\t\t[ %d ]\n",
+ stats->an_rx_poll_pkts);
+ printf("Poll frames transmitted:\t\t\t[ %d ]\n",
+ stats->an_tx_poll_pkts);
+ printf("Host requested sync losses:\t\t\t[ %d ]\n",
+ stats->an_lostsync_hostreq);
+ printf("Host transmitted bytes:\t\t\t\t[ %d ]\n",
+ stats->an_host_tx_bytes);
+ printf("Host received bytes:\t\t\t\t[ %d ]\n",
+ stats->an_host_rx_bytes);
+ printf("Uptime in microseconds:\t\t\t\t[ %d ]\n",
+ stats->an_uptime_usecs);
+ printf("Uptime in seconds:\t\t\t\t[ %d ]\n",
+ stats->an_uptime_secs);
+ printf("Sync lost due to better AP:\t\t\t[ %d ]\n",
+ stats->an_lostsync_better_ap);
+
+ return;
+}
+
+static void an_dumpap(iface)
+ const char *iface;
+{
+ struct an_ltv_aplist *ap;
+ struct an_req areq;
+
+ areq.an_len = sizeof(areq);
+ areq.an_type = AN_RID_APLIST;
+
+ an_getval(iface, &areq);
+
+ ap = (struct an_ltv_aplist *)&areq;
+ printf("Access point 1:\t\t\t");
+ an_printhex((char *)&ap->an_ap1, ETHER_ADDR_LEN);
+ printf("\nAccess point 2:\t\t\t");
+ an_printhex((char *)&ap->an_ap2, ETHER_ADDR_LEN);
+ printf("\nAccess point 3:\t\t\t");
+ an_printhex((char *)&ap->an_ap3, ETHER_ADDR_LEN);
+ printf("\nAccess point 4:\t\t\t");
+ an_printhex((char *)&ap->an_ap4, ETHER_ADDR_LEN);
+ printf("\n");
+
+ return;
+}
+
+static void an_dumpssid(iface)
+ const char *iface;
+{
+ struct an_ltv_ssidlist *ssid;
+ struct an_req areq;
+
+ areq.an_len = sizeof(areq);
+ areq.an_type = AN_RID_SSIDLIST;
+
+ an_getval(iface, &areq);
+
+ ssid = (struct an_ltv_ssidlist *)&areq;
+ printf("SSID 1:\t\t\t[ %.*s ]\n", ssid->an_ssid1_len, ssid->an_ssid1);
+ printf("SSID 2:\t\t\t[ %.*s ]\n", ssid->an_ssid2_len, ssid->an_ssid2);
+ printf("SSID 3:\t\t\t[ %.*s ]\n", ssid->an_ssid3_len, ssid->an_ssid3);
+
+ return;
+}
+
+static void an_dumpconfig(iface)
+ const char *iface;
+{
+ struct an_ltv_genconfig *cfg;
+ struct an_req areq;
+ unsigned char diversity;
+
+ areq.an_len = sizeof(areq);
+ areq.an_type = AN_RID_ACTUALCFG;
+
+ an_getval(iface, &areq);
+
+ cfg = (struct an_ltv_genconfig *)&areq;
+
+ printf("Operating mode:\t\t\t\t[ ");
+ if ((cfg->an_opmode & 0x7) == AN_OPMODE_IBSS_ADHOC)
+ printf("ad-hoc");
+ if ((cfg->an_opmode & 0x7) == AN_OPMODE_INFRASTRUCTURE_STATION)
+ printf("infrastructure");
+ if ((cfg->an_opmode & 0x7) == AN_OPMODE_AP)
+ printf("access point");
+ if ((cfg->an_opmode & 0x7) == AN_OPMODE_AP_REPEATER)
+ printf("access point repeater");
+ printf(" ]");
+ printf("\nReceive mode:\t\t\t\t[ ");
+ if ((cfg->an_rxmode & 0x7) == AN_RXMODE_BC_MC_ADDR)
+ printf("broadcast/multicast/unicast");
+ if ((cfg->an_rxmode & 0x7) == AN_RXMODE_BC_ADDR)
+ printf("broadcast/unicast");
+ if ((cfg->an_rxmode & 0x7) == AN_RXMODE_ADDR)
+ printf("unicast");
+ if ((cfg->an_rxmode & 0x7) == AN_RXMODE_80211_MONITOR_CURBSS)
+ printf("802.11 monitor, current BSSID");
+ if ((cfg->an_rxmode & 0x7) == AN_RXMODE_80211_MONITOR_ANYBSS)
+ printf("802.11 monitor, any BSSID");
+ if ((cfg->an_rxmode & 0x7) == AN_RXMODE_LAN_MONITOR_CURBSS)
+ printf("LAN monitor, current BSSID");
+ printf(" ]");
+ printf("\nFragment threshold:\t\t\t");
+ an_printwords(&cfg->an_fragthresh, 1);
+ printf("\nRTS threshold:\t\t\t\t");
+ an_printwords(&cfg->an_rtsthresh, 1);
+ printf("\nMAC address:\t\t\t\t");
+ an_printhex((char *)&cfg->an_macaddr, ETHER_ADDR_LEN);
+ printf("\nSupported rates:\t\t\t");
+ an_printspeeds(cfg->an_rates, 8);
+ printf("\nShort retry limit:\t\t\t");
+ an_printwords(&cfg->an_shortretry_limit, 1);
+ printf("\nLong retry limit:\t\t\t");
+ an_printwords(&cfg->an_longretry_limit, 1);
+ printf("\nTX MSDU lifetime:\t\t\t");
+ an_printwords(&cfg->an_tx_msdu_lifetime, 1);
+ printf("\nRX MSDU lifetime:\t\t\t");
+ an_printwords(&cfg->an_rx_msdu_lifetime, 1);
+ printf("\nStationary:\t\t\t\t");
+ an_printbool(cfg->an_stationary);
+ printf("\nOrdering:\t\t\t\t");
+ an_printbool(cfg->an_ordering);
+ printf("\nDevice type:\t\t\t\t[ ");
+ if (cfg->an_devtype == AN_DEVTYPE_PC4500)
+ printf("PC4500");
+ else if (cfg->an_devtype == AN_DEVTYPE_PC4800)
+ printf("PC4800");
+ else
+ printf("unknown (%x)", cfg->an_devtype);
+ printf(" ]");
+ printf("\nScanning mode:\t\t\t\t[ ");
+ if (cfg->an_scanmode == AN_SCANMODE_ACTIVE)
+ printf("active");
+ if (cfg->an_scanmode == AN_SCANMODE_PASSIVE)
+ printf("passive");
+ if (cfg->an_scanmode == AN_SCANMODE_AIRONET_ACTIVE)
+ printf("Aironet active");
+ printf(" ]");
+ printf("\nProbe delay:\t\t\t\t");
+ an_printwords(&cfg->an_probedelay, 1);
+ printf("\nProbe energy timeout:\t\t\t");
+ an_printwords(&cfg->an_probe_energy_timeout, 1);
+ printf("\nProbe response timeout:\t\t\t");
+ an_printwords(&cfg->an_probe_response_timeout, 1);
+ printf("\nBeacon listen timeout:\t\t\t");
+ an_printwords(&cfg->an_beacon_listen_timeout, 1);
+ printf("\nIBSS join network timeout:\t\t");
+ an_printwords(&cfg->an_ibss_join_net_timeout, 1);
+ printf("\nAuthentication timeout:\t\t\t");
+ an_printwords(&cfg->an_auth_timeout, 1);
+ printf("\nWEP enabled:\t\t\t\t[ ");
+ if (cfg->an_authtype & AN_AUTHTYPE_PRIVACY_IN_USE)
+ {
+ if (cfg->an_authtype & AN_AUTHTYPE_LEAP)
+ printf("LEAP");
+ else if (cfg->an_authtype & AN_AUTHTYPE_ALLOW_UNENCRYPTED)
+ printf("mixed cell");
+ else
+ printf("full");
+ }
+ else
+ printf("no");
+ printf(" ]");
+ printf("\nAuthentication type:\t\t\t[ ");
+ if ((cfg->an_authtype & AN_AUTHTYPE_MASK) == AN_AUTHTYPE_NONE)
+ printf("none");
+ if ((cfg->an_authtype & AN_AUTHTYPE_MASK) == AN_AUTHTYPE_OPEN)
+ printf("open");
+ if ((cfg->an_authtype & AN_AUTHTYPE_MASK) == AN_AUTHTYPE_SHAREDKEY)
+ printf("shared key");
+ printf(" ]");
+ printf("\nAssociation timeout:\t\t\t");
+ an_printwords(&cfg->an_assoc_timeout, 1);
+ printf("\nSpecified AP association timeout:\t");
+ an_printwords(&cfg->an_specified_ap_timeout, 1);
+ printf("\nOffline scan interval:\t\t\t");
+ an_printwords(&cfg->an_offline_scan_interval, 1);
+ printf("\nOffline scan duration:\t\t\t");
+ an_printwords(&cfg->an_offline_scan_duration, 1);
+ printf("\nLink loss delay:\t\t\t");
+ an_printwords(&cfg->an_link_loss_delay, 1);
+ printf("\nMax beacon loss time:\t\t\t");
+ an_printwords(&cfg->an_max_beacon_lost_time, 1);
+ printf("\nRefresh interval:\t\t\t");
+ an_printwords(&cfg->an_refresh_interval, 1);
+ printf("\nPower save mode:\t\t\t[ ");
+ if (cfg->an_psave_mode == AN_PSAVE_NONE)
+ printf("none");
+ if (cfg->an_psave_mode == AN_PSAVE_CAM)
+ printf("constantly awake mode");
+ if (cfg->an_psave_mode == AN_PSAVE_PSP)
+ printf("PSP");
+ if (cfg->an_psave_mode == AN_PSAVE_PSP_CAM)
+ printf("PSP-CAM (fast PSP)");
+ printf(" ]");
+ printf("\nSleep through DTIMs:\t\t\t");
+ an_printbool(cfg->an_sleep_for_dtims);
+ printf("\nPower save listen interval:\t\t");
+ an_printwords(&cfg->an_listen_interval, 1);
+ printf("\nPower save fast listen interval:\t");
+ an_printwords(&cfg->an_fast_listen_interval, 1);
+ printf("\nPower save listen decay:\t\t");
+ an_printwords(&cfg->an_listen_decay, 1);
+ printf("\nPower save fast listen decay:\t\t");
+ an_printwords(&cfg->an_fast_listen_decay, 1);
+ printf("\nAP/ad-hoc Beacon period:\t\t");
+ an_printwords(&cfg->an_beacon_period, 1);
+ printf("\nAP/ad-hoc ATIM duration:\t\t");
+ an_printwords(&cfg->an_atim_duration, 1);
+ printf("\nAP/ad-hoc current channel:\t\t");
+ an_printwords(&cfg->an_ds_channel, 1);
+ printf("\nAP/ad-hoc DTIM period:\t\t\t");
+ an_printwords(&cfg->an_dtim_period, 1);
+ printf("\nRadio type:\t\t\t\t[ ");
+ if (cfg->an_radiotype & AN_RADIOTYPE_80211_FH)
+ printf("802.11 FH");
+ else if (cfg->an_radiotype & AN_RADIOTYPE_80211_DS)
+ printf("802.11 DS");
+ else if (cfg->an_radiotype & AN_RADIOTYPE_LM2000_DS)
+ printf("LM2000 DS");
+ else
+ printf("unknown (%x)", cfg->an_radiotype);
+ printf(" ]");
+ printf("\nRX Diversity:\t\t\t\t[ ");
+ diversity = cfg->an_diversity & 0xFF;
+ if (diversity == AN_DIVERSITY_ANTENNA_1_ONLY)
+ printf("antenna 1 only");
+ else if (diversity == AN_DIVERSITY_ANTENNA_2_ONLY)
+ printf("antenna 2 only");
+ else if (diversity == AN_DIVERSITY_ANTENNA_1_AND_2)
+ printf("antenna 1 and 2");
+ printf(" ]");
+ printf("\nTX Diversity:\t\t\t\t[ ");
+ diversity = (cfg->an_diversity >> 8) & 0xFF;
+ if (diversity == AN_DIVERSITY_ANTENNA_1_ONLY)
+ printf("antenna 1 only");
+ else if (diversity == AN_DIVERSITY_ANTENNA_2_ONLY)
+ printf("antenna 2 only");
+ else if (diversity == AN_DIVERSITY_ANTENNA_1_AND_2)
+ printf("antenna 1 and 2");
+ printf(" ]");
+ printf("\nTransmit power level:\t\t\t");
+ an_printwords(&cfg->an_tx_power, 1);
+ printf("\nRSS threshold:\t\t\t\t");
+ an_printwords(&cfg->an_rss_thresh, 1);
+ printf("\nNode name:\t\t\t\t");
+ an_printstr((char *)&cfg->an_nodename, 16);
+ printf("\nARL threshold:\t\t\t\t");
+ an_printwords(&cfg->an_arl_thresh, 1);
+ printf("\nARL decay:\t\t\t\t");
+ an_printwords(&cfg->an_arl_decay, 1);
+ printf("\nARL delay:\t\t\t\t");
+ an_printwords(&cfg->an_arl_delay, 1);
+ printf("\nConfiguration:\t\t\t\t[ ");
+ if (cfg->an_home_product & AN_HOME_NETWORK)
+ printf("Home Configuration");
+ else
+ printf("Enterprise Configuration");
+ printf(" ]");
+
+ printf("\n");
+ printf("\n");
+ an_readkeyinfo(iface);
+
+ return;
+}
+
+
+static void usage(p)
+ char *p;
+{
+ fprintf(stderr, "usage: %s -i iface -A (show specified APs)\n", p);
+ fprintf(stderr, "\t%s -i iface -N (show specified SSIDss)\n", p);
+ fprintf(stderr, "\t%s -i iface -S (show NIC status)\n", p);
+ fprintf(stderr, "\t%s -i iface -I (show NIC capabilities)\n", p);
+ fprintf(stderr, "\t%s -i iface -T (show stats counters)\n", p);
+ fprintf(stderr, "\t%s -i iface -C (show current config)\n", p);
+ fprintf(stderr, "\t%s -i iface -t 0-4 (set TX speed)\n", p);
+ fprintf(stderr, "\t%s -i iface -s 0-3 (set power save mode)\n", p);
+ fprintf(stderr, "\t%s -i iface [-v 1-4] -a AP (specify AP)\n", p);
+ fprintf(stderr, "\t%s -i iface -b val (set beacon period)\n", p);
+ fprintf(stderr, "\t%s -i iface [-v 0|1] -d val (set diversity)\n", p);
+ fprintf(stderr, "\t%s -i iface -j val (set netjoin timeout)\n", p);
+ fprintf(stderr, "\t%s -i iface -e 0-4 (enable transmit key)\n", p);
+ fprintf(stderr, "\t%s -i iface [-v 0-8] -k key (set key)\n", p);
+ fprintf(stderr, "\t%s -i iface -K 0-2 (no auth/open/shared secret)\n", p);
+ fprintf(stderr, "\t%s -i iface -W 0-2 (no WEP/full WEP/mixed cell)\n", p);
+ fprintf(stderr, "\t%s -i iface -l val (set station name)\n", p);
+ fprintf(stderr, "\t%s -i iface -m val (set MAC address)\n", p);
+ fprintf(stderr, "\t%s -i iface [-v 1-3] -n SSID "
+ "(specify SSID)\n", p);
+ fprintf(stderr, "\t%s -i iface -o 0|1 (set operating mode)\n", p);
+ fprintf(stderr, "\t%s -i iface -c val (set ad-hoc channel)\n", p);
+ fprintf(stderr, "\t%s -i iface -f val (set frag threshold)\n", p);
+ fprintf(stderr, "\t%s -i iface -r val (set RTS threshold)\n", p);
+ fprintf(stderr, "\t%s -i iface -M 0-15 (set monitor mode)\n", p);
+ fprintf(stderr, "\t%s -i iface -L user (enter LEAP authentication mode)\n", p);
+#ifdef ANCACHE
+ fprintf(stderr, "\t%s -i iface -Q print signal quality cache\n", p);
+ fprintf(stderr, "\t%s -i iface -Z zero out signal cache\n", p);
+#endif
+
+ fprintf(stderr, "\t%s -h (display this message)\n", p);
+
+
+ exit(1);
+}
+
+static void an_setconfig(iface, act, arg)
+ const char *iface;
+ int act;
+ void *arg;
+{
+ struct an_ltv_genconfig *cfg;
+ struct an_ltv_caps *caps;
+ struct an_req areq;
+ struct an_req areq_caps;
+ u_int16_t diversity = 0;
+ struct ether_addr *addr;
+ int i;
+
+ areq.an_len = sizeof(areq);
+ areq.an_type = AN_RID_GENCONFIG;
+ an_getval(iface, &areq);
+ cfg = (struct an_ltv_genconfig *)&areq;
+
+ areq_caps.an_len = sizeof(areq);
+ areq_caps.an_type = AN_RID_CAPABILITIES;
+ an_getval(iface, &areq_caps);
+ caps = (struct an_ltv_caps *)&areq_caps;
+
+ switch(act) {
+ case ACT_SET_OPMODE:
+ cfg->an_opmode = atoi(arg);
+ break;
+ case ACT_SET_FREQ:
+ cfg->an_ds_channel = atoi(arg);
+ break;
+ case ACT_SET_PWRSAVE:
+ cfg->an_psave_mode = atoi(arg);
+ break;
+ case ACT_SET_SCANMODE:
+ cfg->an_scanmode = atoi(arg);
+ break;
+ case ACT_SET_DIVERSITY_RX:
+ case ACT_SET_DIVERSITY_TX:
+ switch(atoi(arg)) {
+ case 0:
+ diversity = AN_DIVERSITY_FACTORY_DEFAULT;
+ break;
+ case 1:
+ diversity = AN_DIVERSITY_ANTENNA_1_ONLY;
+ break;
+ case 2:
+ diversity = AN_DIVERSITY_ANTENNA_2_ONLY;
+ break;
+ case 3:
+ diversity = AN_DIVERSITY_ANTENNA_1_AND_2;
+ break;
+ default:
+ errx(1, "bad diversity setting: %d", diversity);
+ break;
+ }
+ if (atoi(arg) == ACT_SET_DIVERSITY_RX) {
+ cfg->an_diversity &= 0x00FF;
+ cfg->an_diversity |= (diversity << 8);
+ } else {
+ cfg->an_diversity &= 0xFF00;
+ cfg->an_diversity |= diversity;
+ }
+ break;
+ case ACT_SET_TXPWR:
+ for (i = 0; i < 8; i++) {
+ if (caps->an_tx_powerlevels[i] == atoi(arg))
+ break;
+ }
+ if (i == 8)
+ errx(1, "unsupported power level: %dmW", atoi(arg));
+
+ cfg->an_tx_power = atoi(arg);
+ break;
+ case ACT_SET_RTS_THRESH:
+ cfg->an_rtsthresh = atoi(arg);
+ break;
+ case ACT_SET_RTS_RETRYLIM:
+ cfg->an_shortretry_limit =
+ cfg->an_longretry_limit = atoi(arg);
+ break;
+ case ACT_SET_BEACON_PERIOD:
+ cfg->an_beacon_period = atoi(arg);
+ break;
+ case ACT_SET_WAKE_DURATION:
+ cfg->an_atim_duration = atoi(arg);
+ break;
+ case ACT_SET_FRAG_THRESH:
+ cfg->an_fragthresh = atoi(arg);
+ break;
+ case ACT_SET_NETJOIN:
+ cfg->an_ibss_join_net_timeout = atoi(arg);
+ break;
+ case ACT_SET_MYNAME:
+ bzero(cfg->an_nodename, 16);
+ strncpy((char *)&cfg->an_nodename, optarg, 16);
+ break;
+ case ACT_SET_MAC:
+ addr = ether_aton((char *)arg);
+
+ if (addr == NULL)
+ errx(1, "badly formatted address");
+ bzero(cfg->an_macaddr, ETHER_ADDR_LEN);
+ bcopy((char *)addr, (char *)&cfg->an_macaddr, ETHER_ADDR_LEN);
+ break;
+ case ACT_ENABLE_WEP:
+ switch (atoi (arg)) {
+ case 0:
+ /* no WEP */
+ cfg->an_authtype &= ~(AN_AUTHTYPE_PRIVACY_IN_USE
+ | AN_AUTHTYPE_ALLOW_UNENCRYPTED
+ | AN_AUTHTYPE_LEAP);
+ break;
+ case 1:
+ /* full WEP */
+ cfg->an_authtype |= AN_AUTHTYPE_PRIVACY_IN_USE;
+ cfg->an_authtype &= ~AN_AUTHTYPE_ALLOW_UNENCRYPTED;
+ cfg->an_authtype &= ~AN_AUTHTYPE_LEAP;
+ break;
+ case 2:
+ /* mixed cell */
+ cfg->an_authtype = AN_AUTHTYPE_PRIVACY_IN_USE
+ | AN_AUTHTYPE_ALLOW_UNENCRYPTED;
+ break;
+ }
+ break;
+ case ACT_SET_KEY_TYPE:
+ cfg->an_authtype = (cfg->an_authtype & ~AN_AUTHTYPE_MASK)
+ | atoi(arg);
+ break;
+ case ACT_SET_MONITOR_MODE:
+ areq.an_type = AN_RID_MONITOR_MODE;
+ cfg->an_len = atoi(arg); /* mode is put in length */
+ break;
+ default:
+ errx(1, "unknown action");
+ break;
+ }
+
+ an_setval(iface, &areq);
+ exit(0);
+}
+
+static void an_setspeed(iface, act, arg)
+ const char *iface;
+ int act __unused;
+ void *arg;
+{
+ struct an_req areq;
+ struct an_ltv_caps *caps;
+ u_int16_t speed;
+
+ areq.an_len = sizeof(areq);
+ areq.an_type = AN_RID_CAPABILITIES;
+
+ an_getval(iface, &areq);
+ caps = (struct an_ltv_caps *)&areq;
+
+ switch(atoi(arg)) {
+ case 0:
+ speed = 0;
+ break;
+ case 1:
+ speed = AN_RATE_1MBPS;
+ break;
+ case 2:
+ speed = AN_RATE_2MBPS;
+ break;
+ case 3:
+ if (caps->an_rates[2] != AN_RATE_5_5MBPS)
+ errx(1, "5.5Mbps not supported on this card");
+ speed = AN_RATE_5_5MBPS;
+ break;
+ case 4:
+ if (caps->an_rates[3] != AN_RATE_11MBPS)
+ errx(1, "11Mbps not supported on this card");
+ speed = AN_RATE_11MBPS;
+ break;
+ default:
+ errx(1, "unsupported speed");
+ break;
+ }
+
+ areq.an_len = 6;
+ areq.an_type = AN_RID_TX_SPEED;
+ areq.an_val[0] = speed;
+
+ an_setval(iface, &areq);
+ exit(0);
+}
+
+static void an_setap(iface, act, arg)
+ const char *iface;
+ int act;
+ void *arg;
+{
+ struct an_ltv_aplist *ap;
+ struct an_req areq;
+ struct ether_addr *addr;
+
+ areq.an_len = sizeof(areq);
+ areq.an_type = AN_RID_APLIST;
+
+ an_getval(iface, &areq);
+ ap = (struct an_ltv_aplist *)&areq;
+
+ addr = ether_aton((char *)arg);
+
+ if (addr == NULL)
+ errx(1, "badly formatted address");
+
+ switch(act) {
+ case ACT_SET_AP1:
+ bzero(ap->an_ap1, ETHER_ADDR_LEN);
+ bcopy((char *)addr, (char *)&ap->an_ap1, ETHER_ADDR_LEN);
+ break;
+ case ACT_SET_AP2:
+ bzero(ap->an_ap2, ETHER_ADDR_LEN);
+ bcopy((char *)addr, (char *)&ap->an_ap2, ETHER_ADDR_LEN);
+ break;
+ case ACT_SET_AP3:
+ bzero(ap->an_ap3, ETHER_ADDR_LEN);
+ bcopy((char *)addr, (char *)&ap->an_ap3, ETHER_ADDR_LEN);
+ break;
+ case ACT_SET_AP4:
+ bzero(ap->an_ap4, ETHER_ADDR_LEN);
+ bcopy((char *)addr, (char *)&ap->an_ap4, ETHER_ADDR_LEN);
+ break;
+ default:
+ errx(1, "unknown action");
+ break;
+ }
+
+ an_setval(iface, &areq);
+ exit(0);
+}
+
+static void an_setssid(iface, act, arg)
+ const char *iface;
+ int act;
+ void *arg;
+{
+ struct an_ltv_ssidlist *ssid;
+ struct an_req areq;
+
+ areq.an_len = sizeof(areq);
+ areq.an_type = AN_RID_SSIDLIST;
+
+ an_getval(iface, &areq);
+ ssid = (struct an_ltv_ssidlist *)&areq;
+
+ switch (act) {
+ case ACT_SET_SSID1:
+ bzero(ssid->an_ssid1, sizeof(ssid->an_ssid1));
+ strlcpy(ssid->an_ssid1, (char *)arg, sizeof(ssid->an_ssid1));
+ ssid->an_ssid1_len = strlen(ssid->an_ssid1);
+ break;
+ case ACT_SET_SSID2:
+ bzero(ssid->an_ssid2, sizeof(ssid->an_ssid2));
+ strlcpy(ssid->an_ssid2, (char *)arg, sizeof(ssid->an_ssid2));
+ ssid->an_ssid2_len = strlen(ssid->an_ssid2);
+ break;
+ case ACT_SET_SSID3:
+ bzero(ssid->an_ssid3, sizeof(ssid->an_ssid3));
+ strlcpy(ssid->an_ssid3, (char *)arg, sizeof(ssid->an_ssid3));
+ ssid->an_ssid3_len = strlen(ssid->an_ssid3);
+ break;
+ default:
+ errx(1, "unknown action");
+ break;
+ }
+
+ an_setval(iface, &areq);
+ exit(0);
+}
+
+#ifdef ANCACHE
+static void an_zerocache(iface)
+ const char *iface;
+{
+ struct an_req areq;
+
+ bzero((char *)&areq, sizeof(areq));
+ areq.an_len = 0;
+ areq.an_type = AN_RID_ZERO_CACHE;
+
+ an_getval(iface, &areq);
+
+ return;
+}
+
+static void an_readcache(iface)
+ const char *iface;
+{
+ struct an_req areq;
+ int *an_sigitems;
+ struct an_sigcache *sc;
+ char * pt;
+ int i;
+
+ if (iface == NULL)
+ errx(1, "must specify interface name");
+
+ bzero((char *)&areq, sizeof(areq));
+ areq.an_len = AN_MAX_DATALEN;
+ areq.an_type = AN_RID_READ_CACHE;
+
+ an_getval(iface, &areq);
+
+ an_sigitems = (int *) &areq.an_val;
+ pt = ((char *) &areq.an_val);
+ pt += sizeof(int);
+ sc = (struct an_sigcache *) pt;
+
+ for (i = 0; i < *an_sigitems; i++) {
+ printf("[%d/%d]:", i+1, *an_sigitems);
+ printf(" %02x:%02x:%02x:%02x:%02x:%02x,",
+ sc->macsrc[0]&0xff,
+ sc->macsrc[1]&0xff,
+ sc->macsrc[2]&0xff,
+ sc->macsrc[3]&0xff,
+ sc->macsrc[4]&0xff,
+ sc->macsrc[5]&0xff);
+ printf(" %d.%d.%d.%d,",((sc->ipsrc >> 0) & 0xff),
+ ((sc->ipsrc >> 8) & 0xff),
+ ((sc->ipsrc >> 16) & 0xff),
+ ((sc->ipsrc >> 24) & 0xff));
+ printf(" sig: %d, noise: %d, qual: %d\n",
+ sc->signal,
+ sc->noise,
+ sc->quality);
+ sc++;
+ }
+
+ return;
+}
+#endif
+
+static int an_hex2int(c)
+ char c;
+{
+ if (c >= '0' && c <= '9')
+ return (c - '0');
+ if (c >= 'A' && c <= 'F')
+ return (c - 'A' + 10);
+ if (c >= 'a' && c <= 'f')
+ return (c - 'a' + 10);
+
+ return (0);
+}
+
+static void an_str2key(s, k)
+ char *s;
+ struct an_ltv_key *k;
+{
+ int n, i;
+ char *p;
+
+ /* Is this a hex string? */
+ if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
+ /* Yes, convert to int. */
+ n = 0;
+ p = (char *)&k->key[0];
+ for (i = 2; s[i] != '\0' && s[i + 1] != '\0'; i+= 2) {
+ *p++ = (an_hex2int(s[i]) << 4) + an_hex2int(s[i + 1]);
+ n++;
+ }
+ if (s[i] != '\0')
+ errx(1, "hex strings must be of even length");
+ k->klen = n;
+ } else {
+ /* No, just copy it in. */
+ bcopy(s, k->key, strlen(s));
+ k->klen = strlen(s);
+ }
+
+ return;
+}
+
+static void an_setkeys(iface, key, keytype)
+ const char *iface;
+ char *key;
+ int keytype;
+{
+ struct an_req areq;
+ struct an_ltv_key *k;
+
+ bzero((char *)&areq, sizeof(areq));
+ k = (struct an_ltv_key *)&areq;
+
+ if (strlen(key) > 28) {
+ err(1, "encryption key must be no "
+ "more than 18 characters long");
+ }
+
+ an_str2key(key, k);
+
+ k->kindex=keytype/2;
+
+ if (!(k->klen==0 || k->klen==5 || k->klen==13)) {
+ err(1, "encryption key must be 0, 5 or 13 bytes long");
+ }
+
+ /* default mac and only valid one (from manual) 1.0.0.0.0.0 */
+ k->mac[0]=1;
+ k->mac[1]=0;
+ k->mac[2]=0;
+ k->mac[3]=0;
+ k->mac[4]=0;
+ k->mac[5]=0;
+
+ switch(keytype & 1) {
+ case 0:
+ areq.an_len = sizeof(struct an_ltv_key);
+ areq.an_type = AN_RID_WEP_PERM;
+ an_setval(iface, &areq);
+ break;
+ case 1:
+ areq.an_len = sizeof(struct an_ltv_key);
+ areq.an_type = AN_RID_WEP_TEMP;
+ an_setval(iface, &areq);
+ break;
+ }
+
+ return;
+}
+
+static void an_readkeyinfo(iface)
+ const char *iface;
+{
+ struct an_req areq;
+ struct an_ltv_genconfig *cfg;
+ struct an_ltv_key *k;
+ int i;
+ int home;
+
+ areq.an_len = sizeof(areq);
+ areq.an_type = AN_RID_ACTUALCFG;
+ an_getval(iface, &areq);
+ cfg = (struct an_ltv_genconfig *)&areq;
+ if (cfg->an_home_product & AN_HOME_NETWORK)
+ home = 1;
+ else
+ home = 0;
+
+ bzero((char *)&areq, sizeof(areq));
+ k = (struct an_ltv_key *)&areq;
+
+ printf("WEP Key status:\n");
+ areq.an_type = AN_RID_WEP_TEMP; /* read first key */
+ for(i=0; i<5; i++) {
+ areq.an_len = sizeof(struct an_ltv_key);
+ an_getval(iface, &areq);
+ if (k->kindex == 0xffff)
+ break;
+ switch (k->klen) {
+ case 0:
+ printf("\tKey %d is unset\n",k->kindex);
+ break;
+ case 5:
+ printf("\tKey %d is set 40 bits\n",k->kindex);
+ break;
+ case 13:
+ printf("\tKey %d is set 128 bits\n",k->kindex);
+ break;
+ default:
+ printf("\tWEP Key %d has an unknown size %d\n",
+ i, k->klen);
+ }
+
+ areq.an_type = AN_RID_WEP_PERM; /* read next key */
+ }
+ k->kindex = 0xffff;
+ areq.an_len = sizeof(struct an_ltv_key);
+ an_getval(iface, &areq);
+ printf("\tThe active transmit key is %d\n", 4 * home + k->mac[0]);
+
+ return;
+}
+
+static void an_enable_tx_key(iface, arg)
+ const char *iface;
+ char *arg;
+{
+ struct an_req areq;
+ struct an_ltv_key *k;
+ struct an_ltv_genconfig *config;
+
+ bzero((char *)&areq, sizeof(areq));
+
+ /* set home or not home mode */
+ areq.an_len = sizeof(struct an_ltv_genconfig);
+ areq.an_type = AN_RID_GENCONFIG;
+ an_getval(iface, &areq);
+ config = (struct an_ltv_genconfig *)&areq;
+ if (atoi(arg) == 4) {
+ config->an_home_product |= AN_HOME_NETWORK;
+ }else{
+ config->an_home_product &= ~AN_HOME_NETWORK;
+ }
+ an_setval(iface, &areq);
+
+ bzero((char *)&areq, sizeof(areq));
+
+ k = (struct an_ltv_key *)&areq;
+
+ /* From a Cisco engineer write the transmit key to use in the
+ first MAC, index is FFFF*/
+ k->kindex=0xffff;
+ k->klen=0;
+
+ k->mac[0]=atoi(arg);
+ k->mac[1]=0;
+ k->mac[2]=0;
+ k->mac[3]=0;
+ k->mac[4]=0;
+ k->mac[5]=0;
+
+ areq.an_len = sizeof(struct an_ltv_key);
+ areq.an_type = AN_RID_WEP_PERM;
+ an_setval(iface, &areq);
+
+ return;
+}
+
+static void an_enable_leap_mode(iface, username)
+ const char *iface;
+ char *username;
+{
+ struct an_req areq;
+ struct an_ltv_status *sts;
+ struct an_ltv_genconfig *cfg;
+ struct an_ltv_caps *caps;
+ struct an_ltv_leap_username an_username;
+ struct an_ltv_leap_password an_password;
+ char *password;
+ MD4_CTX context;
+ int len;
+ int i;
+ char unicode_password[LEAP_PASSWORD_MAX * 2];
+
+ areq.an_len = sizeof(areq);
+ areq.an_type = AN_RID_CAPABILITIES;
+
+ an_getval(iface, &areq);
+
+ caps = (struct an_ltv_caps *)&areq;
+
+ if (!caps->an_softcaps & AN_AUTHTYPE_LEAP) {
+ fprintf(stderr, "Firmware does not support LEAP\n");
+ exit(1);
+ }
+
+ bzero(&an_username, sizeof(an_username));
+ bzero(&an_password, sizeof(an_password));
+
+ len = strlen(username);
+ if (len > LEAP_USERNAME_MAX) {
+ printf("Username too long (max %d)\n", LEAP_USERNAME_MAX);
+ exit(1);
+ }
+ strncpy(an_username.an_username, username, len);
+ an_username.an_username_len = len;
+ an_username.an_len = sizeof(an_username);
+ an_username.an_type = AN_RID_LEAPUSERNAME;
+
+ password = getpass("Enter LEAP password:");
+
+ len = strlen(password);
+ if (len > LEAP_PASSWORD_MAX) {
+ printf("Password too long (max %d)\n", LEAP_PASSWORD_MAX);
+ exit(1);
+ }
+
+ bzero(&unicode_password, sizeof(unicode_password));
+ for(i = 0; i < len; i++) {
+ unicode_password[i * 2] = *password++;
+ }
+
+ /* First half */
+ MD4Init(&context);
+ MD4Update(&context, unicode_password, len * 2);
+ MD4Final(&an_password.an_password[0], &context);
+
+ /* Second half */
+ MD4Init (&context);
+ MD4Update (&context, &an_password.an_password[0], 16);
+ MD4Final (&an_password.an_password[16], &context);
+
+ an_password.an_password_len = 32;
+ an_password.an_len = sizeof(an_password);
+ an_password.an_type = AN_RID_LEAPPASSWORD;
+
+ an_setval(iface, (struct an_req *)&an_username);
+ an_setval(iface, (struct an_req *)&an_password);
+
+ areq.an_len = sizeof(areq);
+ areq.an_type = AN_RID_GENCONFIG;
+ an_getval(iface, &areq);
+ cfg = (struct an_ltv_genconfig *)&areq;
+ cfg->an_authtype = (AN_AUTHTYPE_PRIVACY_IN_USE | AN_AUTHTYPE_LEAP);
+ an_setval(iface, &areq);
+
+ sts = (struct an_ltv_status *)&areq;
+ areq.an_type = AN_RID_STATUS;
+
+ for (i = 60; i > 0; i--) {
+ an_getval(iface, &areq);
+ if (sts->an_opmode & AN_STATUS_OPMODE_LEAP) {
+ printf("Authenticated\n");
+ break;
+ }
+ sleep(1);
+ }
+
+ if (i == 0) {
+ fprintf(stderr, "Failed LEAP authentication\n");
+ exit(1);
+ }
+}
+
+int main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ch;
+ int act = 0;
+ const char *iface = NULL;
+ int modifier = 0;
+ char *key = NULL;
+ void *arg = NULL;
+ char *p = argv[0];
+
+ /* Get the interface name */
+ opterr = 0;
+ ch = getopt(argc, argv, "i:");
+ if (ch == 'i') {
+ iface = optarg;
+ } else {
+ if (argc > 1 && *argv[1] != '-') {
+ iface = argv[1];
+ optind = 2;
+ } else {
+ iface = "an0";
+ optind = 1;
+ }
+ optreset = 1;
+ }
+ opterr = 1;
+
+ while ((ch = getopt(argc, argv,
+ "ANISCTht:a:e:o:s:n:v:d:j:b:c:r:p:w:m:l:k:K:W:QZM:L:")) != -1) {
+ switch(ch) {
+ case 'Z':
+#ifdef ANCACHE
+ act = ACT_ZEROCACHE;
+#else
+ errx(1, "ANCACHE not available");
+#endif
+ break;
+ case 'Q':
+#ifdef ANCACHE
+ act = ACT_DUMPCACHE;
+#else
+ errx(1, "ANCACHE not available");
+#endif
+ break;
+ case 'A':
+ act = ACT_DUMPAP;
+ break;
+ case 'N':
+ act = ACT_DUMPSSID;
+ break;
+ case 'S':
+ act = ACT_DUMPSTATUS;
+ break;
+ case 'I':
+ act = ACT_DUMPCAPS;
+ break;
+ case 'T':
+ act = ACT_DUMPSTATS;
+ break;
+ case 'C':
+ act = ACT_DUMPCONFIG;
+ break;
+ case 't':
+ act = ACT_SET_TXRATE;
+ arg = optarg;
+ break;
+ case 's':
+ act = ACT_SET_PWRSAVE;
+ arg = optarg;
+ break;
+ case 'p':
+ act = ACT_SET_TXPWR;
+ arg = optarg;
+ break;
+ case 'v':
+ modifier = atoi(optarg);
+ break;
+ case 'a':
+ switch(modifier) {
+ case 0:
+ case 1:
+ act = ACT_SET_AP1;
+ break;
+ case 2:
+ act = ACT_SET_AP2;
+ break;
+ case 3:
+ act = ACT_SET_AP3;
+ break;
+ case 4:
+ act = ACT_SET_AP4;
+ break;
+ default:
+ errx(1, "bad modifier %d: there "
+ "are only 4 access point settings",
+ modifier);
+ usage(p);
+ break;
+ }
+ arg = optarg;
+ break;
+ case 'b':
+ act = ACT_SET_BEACON_PERIOD;
+ arg = optarg;
+ break;
+ case 'd':
+ switch(modifier) {
+ case 0:
+ act = ACT_SET_DIVERSITY_RX;
+ break;
+ case 1:
+ act = ACT_SET_DIVERSITY_TX;
+ break;
+ default:
+ errx(1, "must specift RX or TX diversity");
+ break;
+ }
+ arg = optarg;
+ break;
+ case 'j':
+ act = ACT_SET_NETJOIN;
+ arg = optarg;
+ break;
+ case 'l':
+ act = ACT_SET_MYNAME;
+ arg = optarg;
+ break;
+ case 'm':
+ act = ACT_SET_MAC;
+ arg = optarg;
+ break;
+ case 'n':
+ switch(modifier) {
+ case 0:
+ case 1:
+ act = ACT_SET_SSID1;
+ break;
+ case 2:
+ act = ACT_SET_SSID2;
+ break;
+ case 3:
+ act = ACT_SET_SSID3;
+ break;
+ default:
+ errx(1, "bad modifier %d: there"
+ "are only 3 SSID settings", modifier);
+ usage(p);
+ break;
+ }
+ arg = optarg;
+ break;
+ case 'o':
+ act = ACT_SET_OPMODE;
+ arg = optarg;
+ break;
+ case 'c':
+ act = ACT_SET_FREQ;
+ arg = optarg;
+ break;
+ case 'f':
+ act = ACT_SET_FRAG_THRESH;
+ arg = optarg;
+ break;
+ case 'W':
+ act = ACT_ENABLE_WEP;
+ arg = optarg;
+ break;
+ case 'K':
+ act = ACT_SET_KEY_TYPE;
+ arg = optarg;
+ break;
+ case 'k':
+ act = ACT_SET_KEYS;
+ key = optarg;
+ break;
+ case 'e':
+ act = ACT_ENABLE_TX_KEY;
+ arg = optarg;
+ break;
+ case 'q':
+ act = ACT_SET_RTS_RETRYLIM;
+ arg = optarg;
+ break;
+ case 'r':
+ act = ACT_SET_RTS_THRESH;
+ arg = optarg;
+ break;
+ case 'w':
+ act = ACT_SET_WAKE_DURATION;
+ arg = optarg;
+ break;
+ case 'M':
+ act = ACT_SET_MONITOR_MODE;
+ arg = optarg;
+ break;
+ case 'L':
+ act = ACT_SET_LEAP_MODE;
+ arg = optarg;
+ break;
+ case 'h':
+ default:
+ usage(p);
+ }
+ }
+
+ if (iface == NULL || (!act && !key))
+ usage(p);
+
+ switch(act) {
+ case ACT_DUMPSTATUS:
+ an_dumpstatus(iface);
+ break;
+ case ACT_DUMPCAPS:
+ an_dumpcaps(iface);
+ break;
+ case ACT_DUMPSTATS:
+ an_dumpstats(iface);
+ break;
+ case ACT_DUMPCONFIG:
+ an_dumpconfig(iface);
+ break;
+ case ACT_DUMPSSID:
+ an_dumpssid(iface);
+ break;
+ case ACT_DUMPAP:
+ an_dumpap(iface);
+ break;
+ case ACT_SET_SSID1:
+ case ACT_SET_SSID2:
+ case ACT_SET_SSID3:
+ an_setssid(iface, act, arg);
+ break;
+ case ACT_SET_AP1:
+ case ACT_SET_AP2:
+ case ACT_SET_AP3:
+ case ACT_SET_AP4:
+ an_setap(iface, act, arg);
+ break;
+ case ACT_SET_TXRATE:
+ an_setspeed(iface, act, arg);
+ break;
+#ifdef ANCACHE
+ case ACT_ZEROCACHE:
+ an_zerocache(iface);
+ break;
+ case ACT_DUMPCACHE:
+ an_readcache(iface);
+ break;
+
+#endif
+ case ACT_SET_KEYS:
+ an_setkeys(iface, key, modifier);
+ break;
+ case ACT_ENABLE_TX_KEY:
+ an_enable_tx_key(iface, arg);
+ break;
+ case ACT_SET_LEAP_MODE:
+ an_enable_leap_mode(iface, arg);
+ break;
+ default:
+ an_setconfig(iface, act, arg);
+ break;
+ }
+
+ exit(0);
+}
+
diff --git a/usr.sbin/apm/Makefile b/usr.sbin/apm/Makefile
new file mode 100644
index 0000000..5ab8df1
--- /dev/null
+++ b/usr.sbin/apm/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= apm
+LINKS= ${BINDIR}/apm ${BINDIR}/zzz
+MAN= apm.8
+MLINKS= apm.8 apmconf.8 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..9a07675
--- /dev/null
+++ b/usr.sbin/apm/apm.8
@@ -0,0 +1,143 @@
+.\" LP (Laptop Package)
+.\"
+.\" Copyright (c) 1994 by Tatsumi Hosokawa <hosokawa@jp.FreeBSD.org>
+.\"
+.\" This software may be used, modified, copied, and distributed, in
+.\" both source and binary form provided that the above copyright and
+.\" these terms are retained. Under no circumstances is the author
+.\" responsible for the proper functioning of this software, nor does
+.\" the author assume any responsibility for damages incurred with its
+.\"
+.\" $FreeBSD$
+.\"
+.\" use.
+.Dd November 1, 1994
+.Dt APM 8
+.Os
+.Sh NAME
+.Nm apm , zzz
+.Nd control the APM BIOS and display its information
+.Sh SYNOPSIS
+.Nm
+.Op Fl ablstzZ
+.Op Fl d Ar enable
+.Op Fl e Ar enable
+.Op Fl h Ar enable
+.Op Fl r Ar delta
+.Pp
+.Nm zzz
+.Sh DESCRIPTION
+.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
+(no options are available for
+.Nm zzz ) .
+If no options are specified,
+.Nm
+displays information and current status of APM in verbose mode.
+If multiple display options are given, the values are displayed one
+per line in the order given here.
+.Bl -tag -width indent
+.It Fl a
+Display the current AC-line status as an integer value. The values
+0 and 1 correspond to the
+.Dq off-line
+state or
+.Dq on-line
+state, respectively.
+.It Fl b
+Display an integer value reflecting the current battery status.
+The values 0, 1, 2, 3, correspond to the
+.Dq high
+status,
+.Dq low
+status,
+.Dq critical
+status,
+.Dq charging
+status respectively.
+.It Fl d Ar enable
+Disable/enable suspending of the display separately from a normal suspend
+using the boolean value for
+.Ar enable .
+This feature seems to not work on many different laptops,
+including the Libretto 30CT and 50CT.
+.It Fl e Ar enable
+Enable or disable APM functions of the computer,
+depending on the boolean
+.Ar enable
+argument.
+.It Fl h Ar enable
+Depending on the boolean value of
+.Ar enable ,
+enable or disable the HLT instruction in the kernel context switch routine.
+These options are not necessary for almost all APM implementations,
+but for some implementations whose
+.Dq Pa Idle CPU
+call executes both CPU clock slowdown and HLT instruction,
+.Fl t
+is necessary to prevent the system from reducing its peak performance.
+See
+.Xr apm 4
+for details.
+.It Fl l
+Display the remaining battery percentage. If your laptop does not
+support this function, 255 is displayed.
+.It Fl r Ar delta
+Enable the resume wakeup timer, if the laptop supports it. This
+doesn't actually suspend the laptop, but if the laptop is suspended,
+and it supports resume from suspend, then it will be resumed after
+.Ar delta
+seconds (from when you run this command, not from when you suspend).
+.It Fl s
+Display the status of the APM support as an integer value. The values
+0 and 1 correspond to the
+.Dq disabled
+state or
+.Dq enabled
+state respectively.
+.It Fl t
+Display the estimated remaining battery lifetime in seconds. If
+it is unknown, -1 is displayed.
+.It Fl Z
+Transition the system into standby mode. This mode uses less power than
+full power mode, but more than suspend mode. Some laptops support
+resuming from this state on timer or Ring Indicator events. The
+output of 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 .
+On such systems,
+.Nm
+displays them as unknown.
+.Pp
+Some APM implementations cannot handle events such as pushing the
+power button or closing the cover. On such implementations, the system
+.Ar must
+be suspended
+.Ar only
+by using
+.Nm
+or
+.Nm zzz .
+.Sh NOTES
+.Xr apmconf 8
+has been merged in
+.Xr apm 8
+and thus
+.Xr apm 8
+replaces all of its functionality.
+.Sh SEE ALSO
+.Xr apm 4
+.Sh AUTHORS
+.An Tatsumi Hosokawa Aq hosokawa@jp.FreeBSD.org
diff --git a/usr.sbin/apm/apm.c b/usr.sbin/apm/apm.c
new file mode 100644
index 0000000..6cfd86f
--- /dev/null
+++ b/usr.sbin/apm/apm.c
@@ -0,0 +1,511 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
+
+#include <machine/apm_bios.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <time.h>
+#include <unistd.h>
+
+#define APMDEV "/dev/apm"
+
+#define xh(a) (((a) & 0xff00) >> 8)
+#define xl(a) ((a) & 0xff)
+#define APMERR(a) xh(a)
+
+int cmos_wall = 0; /* True when wall time is in cmos clock, else UTC */
+
+void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n",
+ "usage: apm [-ablstzZ] [-d enable ] [ -e enable ] "
+ "[ -h enable ] [-r delta]",
+ " zzz");
+ exit(1);
+}
+
+/*
+ * Return 1 for boolean true, and 0 for false, according to the
+ * interpretation of the string argument given.
+ */
+int
+is_true(const char *boolean) {
+ char *endp;
+ long val;
+
+ val = strtoul(boolean, &endp, 0);
+ if (*endp == '\0')
+ return (val != 0 ? 1 : 0);
+ if (strcasecmp(boolean, "true") == 0 ||
+ strcasecmp(boolean, "yes") == 0 ||
+ strcasecmp(boolean, "enable") == 0)
+ return (1);
+ if (strcasecmp(boolean, "false") == 0 ||
+ strcasecmp(boolean, "no") == 0 ||
+ strcasecmp(boolean, "disable") == 0)
+ return (0);
+ /* Well, I have no idea what the user wants, so... */
+ warnx("invalid boolean argument \"%s\"", boolean);
+ usage();
+ /* NOTREACHED */
+}
+
+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;
+ int place = 1;
+
+ if (bcd > 0x9999)
+ return -1;
+
+ while (bcd) {
+ retval += (bcd & 0xf) * place;
+ bcd >>= 4;
+ place *= 10;
+ }
+ return retval;
+}
+
+void
+apm_suspend(int fd)
+{
+ if (ioctl(fd, APMIO_SUSPEND, NULL) == -1)
+ err(1, "ioctl(APMIO_SUSPEND)");
+}
+
+void
+apm_standby(int fd)
+{
+ if (ioctl(fd, APMIO_STANDBY, NULL) == -1)
+ err(1, "ioctl(APMIO_STANDBY)");
+}
+
+void
+apm_getinfo(int fd, apm_info_t aip)
+{
+ if (ioctl(fd, APMIO_GETINFO, aip) == -1)
+ err(1, "ioctl(APMIO_GETINFO)");
+}
+
+void
+apm_enable(int fd, int enable) {
+
+ if (enable) {
+ if (ioctl(fd, APMIO_ENABLE) == -1)
+ err(1, "ioctl(APMIO_ENABLE)");
+ } else {
+ if (ioctl(fd, APMIO_DISABLE) == -1)
+ err(1, "ioctl(APMIO_DISABLE)");
+ }
+}
+
+void
+print_all_info(int fd, apm_info_t aip, int bioscall_available)
+{
+ 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[] = { "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[] = { "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\n");
+ else if (aip->ai_batt_life <= 100)
+ printf("%d%%\n", aip->ai_batt_life);
+ else
+ printf("invalid value (0x%x)\n", aip->ai_batt_life);
+ printf("Remaining battery time: ");
+ if (aip->ai_batt_time == -1)
+ printf("unknown\n");
+ 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\n", h, m, s);
+ }
+ if (aip->ai_infoversion >= 1) {
+ printf("Number of batteries: ");
+ if (aip->ai_batteries == (u_int) -1)
+ printf("unknown\n");
+ else {
+ int i;
+ struct apm_pwstatus aps;
+
+ printf("%d\n", aip->ai_batteries);
+ for (i = 0; i < aip->ai_batteries; ++i) {
+ bzero(&aps, sizeof(aps));
+ aps.ap_device = PMDV_BATT0 + i;
+ if (ioctl(fd, APMIO_GETPWSTATUS, &aps) == -1)
+ continue;
+ printf("Battery %d:\n", i);
+ printf("\tBattery status: ");
+ if (aps.ap_batt_flag != 255 &&
+ (aps.ap_batt_flag & APM_BATT_NOT_PRESENT)) {
+ printf("not present\n");
+ continue;
+ }
+ if (aps.ap_batt_stat == 255)
+ printf("unknown\n");
+ else if (aps.ap_batt_stat > 3)
+ printf("invalid value (0x%x)\n",
+ aps.ap_batt_stat);
+ else {
+ char *messages[] = { "high",
+ "low",
+ "critical",
+ "charging" };
+ printf("%s\n",
+ messages[aps.ap_batt_stat]);
+ }
+ printf("\tRemaining battery life: ");
+ if (aps.ap_batt_life == 255)
+ printf("unknown\n");
+ else if (aps.ap_batt_life <= 100)
+ printf("%d%%\n", aps.ap_batt_life);
+ else
+ printf("invalid value (0x%x)\n",
+ aps.ap_batt_life);
+ printf("\tRemaining battery time: ");
+ if (aps.ap_batt_time == -1)
+ printf("unknown\n");
+ else {
+ int t, h, m, s;
+
+ t = aps.ap_batt_time;
+ s = t % 60;
+ t /= 60;
+ m = t % 60;
+ t /= 60;
+ h = t;
+ printf("%2d:%02d:%02d\n", h, m, s);
+ }
+ }
+ }
+ }
+
+ if (bioscall_available) {
+ /*
+ * try to get the suspend timer
+ */
+ bzero(&args, sizeof(args));
+ args.eax = (APM_BIOS) << 8 | APM_RESUMETIMER;
+ args.ebx = PMDV_APMBIOS;
+ args.ecx = 0x0001;
+ if (ioctl(fd, APMIO_BIOS, &args)) {
+ printf("Resume timer: unknown\n");
+ } else {
+ apmerr = APMERR(args.eax);
+ if (apmerr == 0x0d || apmerr == 0x86)
+ printf("Resume timer: disabled\n");
+ else if (apmerr)
+ 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, "ioctl(APMIO_DISPLAY)");
+}
+
+void
+apm_haltcpu(int fd, int enable) {
+
+ if (enable) {
+ if (ioctl(fd, APMIO_HALTCPU, NULL) == -1)
+ err(1, "ioctl(APMIO_HALTCPU)");
+ } else {
+ if (ioctl(fd, APMIO_NOTHALTCPU, NULL) == -1)
+ err(1, "ioctl(APMIO_NOTHALTCPU)");
+ }
+}
+
+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 = -1, batt_life = 0, ac_status = 0, standby = 0;
+ int batt_time = 0, delta = 0, enable = -1, haltcpu = -1;
+ char *cmdname;
+ int bioscall_available = 0;
+ size_t cmos_wall_len = sizeof(cmos_wall);
+
+ if (sysctlbyname("machdep.wall_cmos_clock", &cmos_wall, &cmos_wall_len,
+ NULL, 0) == -1)
+ err(1, "sysctlbyname(machdep.wall_cmos_clock)");
+ if ((cmdname = strrchr(argv[0], '/')) != NULL)
+ cmdname++;
+ else
+ cmdname = argv[0];
+
+ if (strcmp(cmdname, "zzz") == 0) {
+ sleep = 1;
+ all_info = 0;
+ goto finish_option;
+ }
+ while ((c = getopt(argc, argv, "abe:h:lRr:stzd:Z")) != -1) {
+ switch (c) {
+ case 'a':
+ ac_status = 1;
+ all_info = 0;
+ break;
+ case 'b':
+ batt_status = 1;
+ all_info = 0;
+ break;
+ case 'd':
+ display = is_true(optarg);
+ all_info = 0;
+ break;
+ case 'l':
+ batt_life = 1;
+ all_info = 0;
+ break;
+ case 'R':
+ delta = -1;
+ break;
+ case 'r':
+ delta = atoi(optarg);
+ break;
+ case 's':
+ apm_status = 1;
+ all_info = 0;
+ break;
+ case 'e':
+ enable = is_true(optarg);
+ break;
+ case 'h':
+ haltcpu = is_true(optarg);
+ 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:
+ if (haltcpu != -1 || enable != -1 || display != -1 || delta || sleep || standby) {
+ fd = open(APMDEV, O_RDWR);
+ bioscall_available = 1;
+ } else if ((fd = open(APMDEV, O_RDWR)) >= 0)
+ bioscall_available = 1;
+ else
+ fd = open(APMDEV, O_RDONLY);
+ if (fd == -1)
+ err(1, "can't open %s", APMDEV);
+ if (enable != -1)
+ apm_enable(fd, enable);
+ if (haltcpu != -1)
+ apm_haltcpu(fd, haltcpu);
+ if (delta)
+ apm_set_timer(fd, delta);
+ if (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, bioscall_available);
+ if (ac_status)
+ printf("%d\n", info.ai_acline);
+ if (batt_status)
+ printf("%d\n", info.ai_batt_stat);
+ if (batt_life)
+ printf("%d\n", info.ai_batt_life);
+ if (apm_status)
+ printf("%d\n", info.ai_status);
+ if (batt_time)
+ printf("%d\n", info.ai_batt_time);
+ if (display != -1)
+ apm_display(fd, display);
+ }
+ close(fd);
+ exit(0);
+}
diff --git a/usr.sbin/apmd/Makefile b/usr.sbin/apmd/Makefile
new file mode 100644
index 0000000..772eca5
--- /dev/null
+++ b/usr.sbin/apmd/Makefile
@@ -0,0 +1,20 @@
+# $FreeBSD$
+
+PROG= apmd
+MAN= apmd.8
+SRCS= apmd.c apmdlex.l apmdparse.y y.tab.h
+
+DPADD= ${LIBL}
+LDADD= -ll
+
+YFLAGS+=-v
+CFLAGS+=-I. -I${.CURDIR} #-DYY_STACK_USED
+# for debug:
+#CFLAGS+= -g -DDEBUG
+
+CLEANFILES= y.output
+
+test:
+ ./apmd -d -f etc/apmd.conf -n
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/apmd/README b/usr.sbin/apmd/README
new file mode 100644
index 0000000..59a56e3
--- /dev/null
+++ b/usr.sbin/apmd/README
@@ -0,0 +1,213 @@
+FreeBSD apmd Package Release Notes (19990711 version)
+
+1. What is "apmd"?
+==================
+
+The apmd package provides a means of handling various APM events from
+userland code. Using apmd.conf, the apmd(8) configuration file, you
+can select the APM events to be handled from userland and specify the
+commands for a given event, allowing APM behaviour to be configured
+flexibly.
+
+
+2. How to install the apmd package
+==================================
+
+2.1 Making the apmd control device file
+---------------------------------------
+
+apmd(8) uses the new special device file /dev/apmctl. This should be
+created as follows:
+
+# cd /dev
+# mknod apmctl c 39 8
+
+2.2 Applying the kernel patch and building a new kernel
+-------------------------------------------------------
+
+The next step is to apply the patch against the sys source tree.
+Go to the source directory (eg. /usr/src/ or /usr/PAO3/src/) and run
+the patch command as follows:
+
+# gzip -cd [somewhere]/apmd-sys-R320.diff | patch
+
+For PAO3 users, the patch file name would be apmd-sys-PAO3.diff
+instead of apmd-sys-R320.diff. After this step has completed
+successfully, build and install a new kernel and reboot your system.
+
+2.3 Making the apmd program
+---------------------------
+
+Go to src/usr.sbin/ and extract the apmd tarball as follows:
+
+# tar xzpvf [somewhere]/apmd-usr.sbin.tar.gz
+
+Before doing a make all, you need to copy apm_bios.h in the sys source
+tree to /usr/include/machine/ first:
+
+# cp /sys/i386/include/apm_bios.h /usr/include/machine/
+
+Then do the build and install steps in the apmd directory:
+
+# cd src/usr.sbin/apmd
+# make depend all install
+
+2.4 Setting up the configuration file and userland script
+---------------------------------------------------------
+
+In src/usr.sbin/apm/etc/ there are example configuration and userland
+script files which are invoked automatically when the APM BIOS informs
+apmd of an event, such as suspend request. Copy these files to
+/etc/ as follows:
+
+# cp src/usr.sbin/apm/etc/* /etc/
+
+
+3. Running the apmd daemon program
+==================================
+
+To run apmd(8) in background mode, simply type ``apmd''.
+
+# apmd
+
+To make a running apmd reload /etc/apmd.conf, send a SIGHUP signal to
+the apmd(8) process.
+
+# kill -HUP [apmd pid]
+or
+# killall -HUP apmd
+
+apmd has some command line options. For the details, please
+refer to the manpage of apmd.
+
+4. Configuration file
+=====================
+
+The structure of the apmd configuration file is quite simple. For
+example:
+
+apm_event SUSPENDREQ {
+ exec "sync && sync && sync";
+ exec "sleep 1";
+ exec "zzz";
+}
+
+Will cause apmd to recieve the APM event SUSPENDREQ (which may be
+posted by an LCD close), run the sync command 3 times and wait for a
+while, then execute zzz (apm -z) to put the system in the suspend
+state.
+
+4.1 The apm_event keyword
+-------------------------
+`apm_event' is the keyword which indicates the start of configuration for
+each events.
+
+4.2 APM events
+--------------
+
+If you wish to execute the same commands for different events, the
+event names should be delimited by a comma. The following are valid
+event names:
+
+o Events ignored by the kernel if apmd is running:
+
+STANDBYREQ
+SUSPENDREQ
+USERSUSPENDREQ
+BATTERYLOW
+
+o Events passed to apmd after kernel handling:
+
+NORMRESUME
+CRITRESUME
+STANDBYRESUME
+POWERSTATECHANGE
+UPDATETIME
+
+
+Other events will not be sent to apmd.
+
+4.3 command line syntax
+-----------------------
+
+In the example above, the three lines begining with `exec' are commands
+for the event. Each line should be terminated with a semicolon. The
+command list for the event should be enclosed by `{' and `}'. apmd(8)
+uses /bin/sh for double-quotation enclosed command execution, just as
+with system(3). Each command is executed in order until the end of
+the list is reached or a command finishes with a non-zero status code.
+apmd(8) will report any failed command's status code via syslog(3)
+and will then reject the request event posted by APM BIOS.
+
+4.4 Built-in functions
+----------------------
+
+You can also specify apmd built-in functions instead of command lines.
+A built-in function name should be terminated with a semicolon, just as
+with a command line.
+The following built-in functions are currently supported:
+
+o reject;
+
+ Reject last request posted by the APM BIOS. This can be used to reject a
+ SUSPEND request when the LCD is closed and put the system in a STANDBY
+ state instead.
+
+
+
+5. EXAMPLES
+===========
+
+Sample configuration commands include:
+
+apm_event SUSPENDREQ {
+ exec "/etc/rc.suspend";
+}
+
+apm_event USERSUSPENDREQ {
+ exec "sync && sync && sync";
+ exec "sleep 1";
+ exec "apm -z";
+}
+
+apm_event NORMRESUME, STANDBYRESUME {
+ exec "/etc/rc.resume";
+}
+
+# resume event configuration for serial mouse users by
+# reinitializing a moused(8) connected to a serial port.
+#
+#apm_event NORMRESUME {
+# exec "kill -HUP `cat /var/run/moused.pid`";
+#}
+
+# suspend request event configuration for ATA HDD users:
+# execute standby instead of suspend.
+#
+#apm_event SUSPENDREQ {
+# reject;
+# exec "sync && sync && sync";
+# exec "sleep 1";
+# exec "apm -Z";
+#}
+
+
+6. Call for developers
+======================
+
+The initial version of apmd(8) was implemented primarily to test the
+kernel support code and was ALPHA quality. Based on that code, the
+current version was developed by KOIE Hidetaka <hide@koie.org>.
+However, we're still looking around for interesting new features and
+ideas, so if you have any thoughts, please let us know.
+Documentation is also sparse, and the manpage have just written.
+If you wish to collaborate on this work, please e-mail me:
+iwasaki@freebsd.org.
+
+
+June 1, 1999
+Created by: iwasaki@FreeBSD.org
+Edited by: jkh@FreeBSD.org
+ nick@foobar.org
+
+$FreeBSD$
diff --git a/usr.sbin/apmd/apmd.8 b/usr.sbin/apmd/apmd.8
new file mode 100644
index 0000000..3de23c8
--- /dev/null
+++ b/usr.sbin/apmd/apmd.8
@@ -0,0 +1,296 @@
+.\" Copyright (c) 1999 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
+.\" Copyright (c) 1999 KOIE Hidetaka <koie@suri.co.jp>
+.\" Copyright (c) 1999 Yoshihiko SARUMARU Aq <mistral@imasy.or.jp>
+.\" Copyright (c) 1999 Norihiro Kumagai <kuma@nk.rim.or.jp>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)apmd.8 1.1 (FreeBSD) 6/28/99
+.\" $FreeBSD$
+.\"
+.Dd June 28, 1999
+.Dt APMD 8
+.Os
+.Sh NAME
+.Nm apmd
+.Nd Advanced Power Management monitor daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Op Fl f file
+.Op Fl v
+.Sh DESCRIPTION
+.Nm Apmd
+monitors the occurrence of the specified Advanced Power Management
+.Pq Tn APM
+events and, if one of the events occurs, it executes the sequence of
+commands corresponding to the event. Only the events specified in the
+configuration file are notified to
+.Nm ;
+all other events are ignored. For each event posted by the APM BIOS,
+.Nm
+invokes the sequence of commands specified in the configuration file.
+When
+.Nm
+is running with monitoring suspend/standby requests,
+the kernel will not process those requests.
+Therefore, if you wish action to be taken when these events
+occur, you need to explicitly configure the appropriate commands or
+built-in functions in the configuration file.
+.Pp
+.Nm Apmd
+recognizes the following runtime options:
+.Bl -tag -width -f_file
+.It Fl d
+Starts in debug mode. This causes
+.Nm
+to execute in the foreground instead of in daemon mode.
+.It Fl f Ar file
+Specifies a different configuration file
+.Ar file
+to be used in place of the default
+.Pa /etc/apmd.conf .
+.It Fl v
+Verbose mode.
+.El
+.Pp
+When
+.Nm
+starts, it reads the configuration file
+.Pa ( /etc/apmd.conf
+as default)
+and notifies the set of events to be monitored to the APM device driver.
+When it terminates, the APM device driver automatically cancels
+monitored events.
+.Pp
+If the
+.Nm
+process receives a SIGHUP, it will reread its configuration file and
+notify the APM device driver of any changes to its configuration.
+.Pp
+.Nm Apmd
+uses the device
+.Pa /dev/apmctl
+to issue
+.Xr ioctl 2
+requests for monitoring events and for controlling the APM system.
+This device file is opened exclusively, so only a single
+.Nm
+process can be running at any time.
+.Pp
+When
+.Nm
+receives an APM event, it forks a child process to execute the
+commands specified in the configuration file and then continues
+listening for more events. The child process executes the commands
+specified, one at a time and in the order that they are listed.
+.Pp
+While
+.Nm
+is processing the command list for SUSPEND/STANDBY requests, the APM kernel
+device driver issues notifications to APM BIOS once per second so that the
+BIOS knows that there are still some commands pending, and that it should not
+complete the request just yet.
+.Pp
+The
+.Nm
+daemon creates the file
+.Pa /var/run/apmd.pid ,
+and stores its process
+id there.
+This can be used to kill or reconfigure
+.Nm .
+.Sh CONFIGURATION FILE
+The structure of the
+.Nm
+configuration file is quite simple. For example:
+.Pp
+.Bd -literal
+apm_event SUSPENDREQ {
+ exec "sync && sync && sync";
+ exec "sleep 1";
+ exec "zzz";
+}
+.Ed
+.Pp
+will cause
+.Nm
+to receive the APM event
+.Ql SUSPENDREQ
+(which may be posted by an LCD close), run the
+.Ql sync
+command 3 times and wait for a while, then execute
+.Nm zzz ( Ns Nm apm Fl z )
+to put the system in the suspend state.
+.Pp
+.Bl -bullet
+.It
+The apm_event keyword
+.Bd -ragged -offset indent
+.Ql apm_event
+is the keyword which indicates the start of configuration for
+each events.
+.Ed
+.It
+APM events
+.Bd -ragged -offset indent
+If you wish to execute the same commands for different events, the
+event names should be delimited by a comma. The following are
+valid event names:
+.Bl -item
+.It
+- Events ignored by the kernel if
+.Nm
+is running:
+.Pp
+.Bl -tag -width USERSUSPENDREQ -compact -offset indent
+.It STANDBYREQ
+.It USERSTANDBYREQ
+.It SUSPENDREQ
+should include sync in the command list,
+.It USERSUSPENDREQ
+should include sync in the command list,
+.It BATTERYLOW
+only zzz should be specified in the command list.
+.El
+.It
+- Events passed to
+.Nm
+after kernel handling:
+.Pp
+.Bl -tag -width USERSUSPENDREQ -compact -offset indent
+.It NORMRESUME
+.It CRITRESUME
+.It STANDBYRESUME
+.It POWERSTATECHANGE
+.It UPDATETIME
+.It CAPABILITIESCHANGE
+.El
+.Pp
+Other events will not be sent to
+.Nm .
+.El
+.Ed
+.It
+command line syntax
+.Bd -ragged -offset indent
+In the example above, the three lines begining with
+.Ql exec
+are commands for the event.
+Each line should be terminated with a semicolon.
+The command list for the event should be enclosed by
+.Ql {
+and
+.Ql } .
+.Nm
+uses
+.Pa /bin/sh
+for double-quotation enclosed command execution, just as with
+.Xr system 3 .
+Each command is executed in order until the end of
+the list is reached or a command finishes with a non-zero status code.
+.Nm
+will report any failed command's status code via
+.Xr syslog 3
+and will then reject the request event posted by the APM BIOS.
+.Ed
+.It
+Built-in functions
+.Bd -ragged -offset indent
+You can also specify
+.Nm
+built-in functions instead of command lines.
+A built-in function name should be terminated with a semicolon,
+just as with a command line.
+The following built-in functions are currently supported:
+.Bl -item
+.It
+- reject:
+.Bd -ragged -offset indent
+Reject last request posted by APM BIOS. This can be used to reject
+a SUSPEND request when the LCD is closed and put the system in a
+STANDBY state instead.
+.Ed
+.El
+.Ed
+.El
+.Sh EXAMPLES
+Sample configuration commands include:
+.Bd -literal
+apm_event SUSPENDREQ {
+ exec "/etc/rc.suspend";
+}
+
+apm_event USERSUSPENDREQ {
+ exec "sync && sync && sync";
+ exec "sleep 1";
+ exec "apm -z";
+}
+
+apm_event NORMRESUME, STANDBYRESUME {
+ exec "/etc/rc.resume";
+}
+
+# resume event configuration for serial mouse users by
+# reinitializing a moused(8) connected to a serial port.
+#
+#apm_event NORMRESUME {
+# exec "kill -HUP `cat /var/run/moused.pid`";
+#}
+#
+# suspend request event configuration for ATA HDD users:
+# execute standby instead of suspend.
+#
+#apm_event SUSPENDREQ {
+# reject;
+# exec "sync && sync && sync";
+# exec "sleep 1";
+# exec "apm -Z";
+#}
+.Ed
+.Sh FILES
+.Bl -tag -width /etc/apmd.conf -compact
+.It Pa /etc/apmd.conf
+.It Pa /dev/apmctl
+.It Pa /var/run/apmd.pid
+.El
+.Sh SEE ALSO
+.Xr apm 4 ,
+.Xr apm 8
+.Sh AUTHORS
+.An Mitsuru IWASAKI Aq iwasaki@FreeBSD.org
+.An KOIE Hidetaka Aq koie@suri.co.jp
+.Pp
+Some contributions made by
+.An Warner Losh Aq imp@FreeBSD.org ,
+.An Hiroshi Yamashita Aq bluemoon@msj.biglobe.ne.jp ,
+.An Yoshihiko SARUMARU Aq mistral@imasy.or.jp ,
+.An Norihiro Kumagai Aq kuma@nk.rim.or.jp ,
+.An NAKAGAWA Yoshihisa Aq nakagawa@jp.FreeBSD.org ,
+and
+.An Nick Hilliard Aq nick@foobar.org .
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Fx 3.3 .
diff --git a/usr.sbin/apmd/apmd.c b/usr.sbin/apmd/apmd.c
new file mode 100644
index 0000000..a285b9b
--- /dev/null
+++ b/usr.sbin/apmd/apmd.c
@@ -0,0 +1,695 @@
+/*-
+ * APM (Advanced Power Management) Event Dispatcher
+ *
+ * Copyright (c) 1999 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
+ * Copyright (c) 1999 KOIE Hidetaka <koie@suri.co.jp>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <assert.h>
+#include <bitstring.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <machine/apm_bios.h>
+
+#include "apmd.h"
+
+extern int yyparse(void);
+
+int debug_level = 0;
+int verbose = 0;
+const char *apmd_configfile = APMD_CONFIGFILE;
+const char *apmd_pidfile = APMD_PIDFILE;
+int apmctl_fd = -1, apmnorm_fd = -1;
+
+/*
+ * table of event handlers
+ */
+#define EVENT_CONFIG_INITIALIZER(EV,R) { #EV, NULL, R },
+struct event_config events[EVENT_MAX] = {
+ EVENT_CONFIG_INITIALIZER(NOEVENT, 0)
+ EVENT_CONFIG_INITIALIZER(STANDBYREQ, 1)
+ EVENT_CONFIG_INITIALIZER(SUSPENDREQ, 1)
+ EVENT_CONFIG_INITIALIZER(NORMRESUME, 0)
+ EVENT_CONFIG_INITIALIZER(CRITRESUME, 0)
+ EVENT_CONFIG_INITIALIZER(BATTERYLOW, 0)
+ EVENT_CONFIG_INITIALIZER(POWERSTATECHANGE, 0)
+ EVENT_CONFIG_INITIALIZER(UPDATETIME, 0)
+ EVENT_CONFIG_INITIALIZER(CRITSUSPEND, 1)
+ EVENT_CONFIG_INITIALIZER(USERSTANDBYREQ, 1)
+ EVENT_CONFIG_INITIALIZER(USERSUSPENDREQ, 1)
+ EVENT_CONFIG_INITIALIZER(STANDBYRESUME, 0)
+ EVENT_CONFIG_INITIALIZER(CAPABILITIESCHANGE, 0)
+};
+
+/*
+ * List of battery events
+ */
+struct battery_watch_event *battery_watch_list = NULL;
+
+#define BATT_CHK_INTV 10 /* how many seconds between battery state checks? */
+
+/*
+ * default procedure
+ */
+struct event_cmd *
+event_cmd_default_clone(void *this)
+{
+ struct event_cmd * oldone = this;
+ struct event_cmd * newone = malloc(oldone->len);
+
+ newone->next = NULL;
+ newone->len = oldone->len;
+ newone->name = oldone->name;
+ newone->op = oldone->op;
+ return newone;
+}
+
+/*
+ * exec command
+ */
+int
+event_cmd_exec_act(void *this)
+{
+ struct event_cmd_exec * p = this;
+ int status = -1;
+ pid_t pid;
+
+ switch ((pid = fork())) {
+ case -1:
+ (void) warn("cannot fork");
+ goto out;
+ case 0:
+ /* child process */
+ execl(_PATH_BSHELL, "sh", "-c", p->line, (char *)NULL);
+ _exit(127);
+ default:
+ /* parent process */
+ do {
+ pid = waitpid(pid, &status, 0);
+ } while (pid == -1 && errno == EINTR);
+ break;
+ }
+ out:
+ return status;
+}
+void
+event_cmd_exec_dump(void *this, FILE *fp)
+{
+ fprintf(fp, " \"%s\"", ((struct event_cmd_exec *)this)->line);
+}
+struct event_cmd *
+event_cmd_exec_clone(void *this)
+{
+ struct event_cmd_exec * newone = (struct event_cmd_exec *) event_cmd_default_clone(this);
+ struct event_cmd_exec * oldone = this;
+
+ newone->evcmd.next = NULL;
+ newone->evcmd.len = oldone->evcmd.len;
+ newone->evcmd.name = oldone->evcmd.name;
+ newone->evcmd.op = oldone->evcmd.op;
+ if ((newone->line = strdup(oldone->line)) == NULL)
+ err(1, "out of memory");
+ return (struct event_cmd *) newone;
+}
+void
+event_cmd_exec_free(void *this)
+{
+ free(((struct event_cmd_exec *)this)->line);
+}
+struct event_cmd_op event_cmd_exec_ops = {
+ event_cmd_exec_act,
+ event_cmd_exec_dump,
+ event_cmd_exec_clone,
+ event_cmd_exec_free
+};
+
+/*
+ * reject commad
+ */
+int
+event_cmd_reject_act(void *this)
+{
+ int rc = -1;
+
+ if (ioctl(apmctl_fd, APMIO_REJECTLASTREQ, NULL)) {
+ syslog(LOG_NOTICE, "fail to reject\n");
+ goto out;
+ }
+ rc = 0;
+ out:
+ return rc;
+}
+struct event_cmd_op event_cmd_reject_ops = {
+ event_cmd_reject_act,
+ NULL,
+ event_cmd_default_clone,
+ NULL
+};
+
+/*
+ * manipulate event_config
+ */
+struct event_cmd *
+clone_event_cmd_list(struct event_cmd *p)
+{
+ struct event_cmd dummy;
+ struct event_cmd *q = &dummy;
+ for ( ;p; p = p->next) {
+ assert(p->op->clone);
+ if ((q->next = p->op->clone(p)) == NULL)
+ (void) err(1, "out of memory");
+ q = q->next;
+ }
+ q->next = NULL;
+ return dummy.next;
+}
+void
+free_event_cmd_list(struct event_cmd *p)
+{
+ struct event_cmd * q;
+ for ( ; p ; p = q) {
+ q = p->next;
+ if (p->op->free)
+ p->op->free(p);
+ free(p);
+ }
+}
+int
+register_battery_handlers(
+ int level, int direction,
+ struct event_cmd *cmdlist)
+{
+ /*
+ * level is negative if it's in "minutes", non-negative if
+ * percentage.
+ *
+ * direction =1 means we care about this level when charging,
+ * direction =-1 means we care about it when discharging.
+ */
+ if (level>100) /* percentage > 100 */
+ return -1;
+ if (abs(direction) != 1) /* nonsense direction value */
+ return -1;
+
+ if (cmdlist) {
+ struct battery_watch_event *we;
+
+ if ((we = malloc(sizeof(struct battery_watch_event))) == NULL)
+ (void) err(1, "out of memory");
+
+ we->next = battery_watch_list; /* starts at NULL */
+ battery_watch_list = we;
+ we->level = abs(level);
+ we->type = (level<0)?BATTERY_MINUTES:BATTERY_PERCENT;
+ we->direction = (direction<0)?BATTERY_DISCHARGING:
+ BATTERY_CHARGING;
+ we->done = 0;
+ we->cmdlist = clone_event_cmd_list(cmdlist);
+ }
+ return 0;
+}
+int
+register_apm_event_handlers(
+ bitstr_t bit_decl(evlist, EVENT_MAX),
+ struct event_cmd *cmdlist)
+{
+ if (cmdlist) {
+ bitstr_t bit_decl(tmp, EVENT_MAX);
+ memcpy(&tmp, evlist, bitstr_size(EVENT_MAX));
+
+ for (;;) {
+ int n;
+ struct event_cmd *p;
+ struct event_cmd *q;
+ bit_ffs(tmp, EVENT_MAX, &n);
+ if (n < 0)
+ break;
+ p = events[n].cmdlist;
+ if ((q = clone_event_cmd_list(cmdlist)) == NULL)
+ (void) err(1, "out of memory");
+ if (p) {
+ while (p->next != NULL)
+ p = p->next;
+ p->next = q;
+ } else {
+ events[n].cmdlist = q;
+ }
+ bit_clear(tmp, n);
+ }
+ }
+ return 0;
+}
+
+/*
+ * execute command
+ */
+int
+exec_run_cmd(struct event_cmd *p)
+{
+ int status = 0;
+
+ for (; p; p = p->next) {
+ assert(p->op->act);
+ if (verbose)
+ syslog(LOG_INFO, "action: %s", p->name);
+ status = p->op->act(p);
+ if (status) {
+ syslog(LOG_NOTICE, "command finished with %d\n", status);
+ break;
+ }
+ }
+ return status;
+}
+
+/*
+ * execute command -- the event version
+ */
+int
+exec_event_cmd(struct event_config *ev)
+{
+ int status = 0;
+
+ status = exec_run_cmd(ev->cmdlist);
+ if (status && ev->rejectable) {
+ syslog(LOG_ERR, "canceled");
+ (void) event_cmd_reject_act(NULL);
+ }
+ return status;
+}
+
+/*
+ * read config file
+ */
+extern FILE * yyin;
+extern int yydebug;
+
+void
+read_config(void)
+{
+ int i;
+
+ if ((yyin = fopen(apmd_configfile, "r")) == NULL) {
+ (void) err(1, "cannot open config file");
+ }
+
+#ifdef DEBUG
+ yydebug = debug_level;
+#endif
+
+ if (yyparse() != 0)
+ (void) err(1, "cannot parse config file");
+
+ fclose(yyin);
+
+ /* enable events */
+ for (i = 0; i < EVENT_MAX; i++) {
+ if (events[i].cmdlist) {
+ u_int event_type = i;
+ if (write(apmctl_fd, &event_type, sizeof(u_int)) == -1) {
+ (void) err(1, "cannot enable event 0x%x", event_type);
+ }
+ }
+ }
+}
+
+void
+dump_config()
+{
+ int i;
+ struct battery_watch_event *q;
+
+ for (i = 0; i < EVENT_MAX; i++) {
+ struct event_cmd * p;
+ if ((p = events[i].cmdlist)) {
+ fprintf(stderr, "apm_event %s {\n", events[i].name);
+ for ( ; p ; p = p->next) {
+ fprintf(stderr, "\t%s", p->name);
+ if (p->op->dump)
+ p->op->dump(p, stderr);
+ fprintf(stderr, ";\n");
+ }
+ fprintf(stderr, "}\n");
+ }
+ }
+ for (q = battery_watch_list ; q != NULL ; q = q -> next) {
+ struct event_cmd * p;
+ fprintf(stderr, "apm_battery %d%s %s {\n",
+ q -> level,
+ (q -> type == BATTERY_PERCENT)?"%":"m",
+ (q -> direction == BATTERY_CHARGING)?"charging":
+ "discharging");
+ for ( p = q -> cmdlist; p ; p = p->next) {
+ fprintf(stderr, "\t%s", p->name);
+ if (p->op->dump)
+ p->op->dump(p, stderr);
+ fprintf(stderr, ";\n");
+ }
+ fprintf(stderr, "}\n");
+ }
+}
+
+void
+destroy_config()
+{
+ int i;
+ struct battery_watch_event *q;
+
+ /* disable events */
+ for (i = 0; i < EVENT_MAX; i++) {
+ if (events[i].cmdlist) {
+ u_int event_type = i;
+ if (write(apmctl_fd, &event_type, sizeof(u_int)) == -1) {
+ (void) err(1, "cannot disable event 0x%x", event_type);
+ }
+ }
+ }
+
+ for (i = 0; i < EVENT_MAX; i++) {
+ struct event_cmd * p;
+ if ((p = events[i].cmdlist))
+ free_event_cmd_list(p);
+ events[i].cmdlist = NULL;
+ }
+
+ for( ; battery_watch_list; battery_watch_list = battery_watch_list -> next) {
+ free_event_cmd_list(battery_watch_list->cmdlist);
+ q = battery_watch_list->next;
+ free(battery_watch_list);
+ battery_watch_list = q;
+ }
+}
+
+void
+restart()
+{
+ destroy_config();
+ read_config();
+ if (verbose)
+ dump_config();
+}
+
+/*
+ * write pid file
+ */
+static void
+write_pid()
+{
+ FILE *fp = fopen(apmd_pidfile, "w");
+
+ if (fp) {
+ fprintf(fp, "%d\n", getpid());
+ fclose(fp);
+ }
+}
+
+/*
+ * handle signals
+ */
+static int signal_fd[2];
+
+void
+enque_signal(int sig)
+{
+ if (write(signal_fd[1], &sig, sizeof sig) != sizeof sig)
+ (void) err(1, "cannot process signal.");
+}
+
+void
+wait_child()
+{
+ int status;
+ while (waitpid(-1, &status, WNOHANG) > 0)
+ ;
+}
+
+int
+proc_signal(int fd)
+{
+ int rc = -1;
+ int sig;
+
+ while (read(fd, &sig, sizeof sig) == sizeof sig) {
+ syslog(LOG_INFO, "caught signal: %d", sig);
+ switch (sig) {
+ case SIGHUP:
+ syslog(LOG_NOTICE, "restart by SIG");
+ restart();
+ break;
+ case SIGTERM:
+ syslog(LOG_NOTICE, "going down on signal %d", sig);
+ rc = 1;
+ goto out;
+ case SIGCHLD:
+ wait_child();
+ break;
+ default:
+ (void) warn("unexpected signal(%d) received.", sig);
+ break;
+ }
+ }
+ rc = 0;
+ out:
+ return rc;
+}
+void
+proc_apmevent(int fd)
+{
+ struct apm_event_info apmevent;
+
+ while (ioctl(fd, APMIO_NEXTEVENT, &apmevent) == 0) {
+ int status;
+ syslog(LOG_NOTICE, "apmevent %04x index %d\n",
+ apmevent.type, apmevent.index);
+ syslog(LOG_INFO, "apm event: %s", events[apmevent.type].name);
+ if (fork() == 0) {
+ status = exec_event_cmd(&events[apmevent.type]);
+ exit(status);
+ }
+ }
+}
+
+#define AC_POWER_STATE ((pw_info.ai_acline == 1) ? BATTERY_CHARGING :\
+ BATTERY_DISCHARGING)
+
+void
+check_battery()
+{
+
+ static int first_time=1, last_state;
+
+ struct apm_info pw_info;
+ struct battery_watch_event *p;
+
+ /* If we don't care, don't bother */
+ if (battery_watch_list == NULL)
+ return;
+
+ if (first_time) {
+ if ( ioctl(apmnorm_fd, APMIO_GETINFO, &pw_info) < 0)
+ (void) err(1, "cannot check battery state.");
+/*
+ * This next statement isn't entirely true. The spec does not tie AC
+ * line state to battery charging or not, but this is a bit lazier to do.
+ */
+ last_state = AC_POWER_STATE;
+ first_time = 0;
+ return; /* We can't process events, we have no baseline */
+ }
+
+ /*
+ * XXX - should we do this a bunch of times and perform some sort
+ * of smoothing or correction?
+ */
+ if ( ioctl(apmnorm_fd, APMIO_GETINFO, &pw_info) < 0)
+ (void) err(1, "cannot check battery state.");
+
+ /*
+ * If we're not in the state now that we were in last time,
+ * then it's a transition, which means we must clean out
+ * the event-caught state.
+ */
+ if (last_state != AC_POWER_STATE) {
+ last_state = AC_POWER_STATE;
+ for (p = battery_watch_list ; p!=NULL ; p = p -> next)
+ p->done = 0;
+ }
+ for (p = battery_watch_list ; p != NULL ; p = p -> next)
+ if (p -> direction == AC_POWER_STATE &&
+ !(p -> done) &&
+ ((p -> type == BATTERY_PERCENT &&
+ p -> level == pw_info.ai_batt_life) ||
+ (p -> type == BATTERY_MINUTES &&
+ p -> level == (pw_info.ai_batt_time / 60)))) {
+ p -> done++;
+ if (verbose)
+ syslog(LOG_NOTICE, "Caught battery event: %s, %d%s",
+ (p -> direction == BATTERY_CHARGING)?"charging":"discharging",
+ p -> level,
+ (p -> type == BATTERY_PERCENT)?"%":" minutes");
+ if (fork() == 0) {
+ int status;
+ status = exec_run_cmd(p -> cmdlist);
+ exit(status);
+ }
+ }
+}
+void
+event_loop(void)
+{
+ int fdmax = 0;
+ struct sigaction nsa;
+ fd_set master_rfds;
+ sigset_t sigmask, osigmask;
+
+ FD_ZERO(&master_rfds);
+ FD_SET(apmctl_fd, &master_rfds);
+ fdmax = apmctl_fd > fdmax ? apmctl_fd : fdmax;
+
+ FD_SET(signal_fd[0], &master_rfds);
+ fdmax = signal_fd[0] > fdmax ? signal_fd[0] : fdmax;
+
+ memset(&nsa, 0, sizeof nsa);
+ nsa.sa_handler = enque_signal;
+ sigfillset(&nsa.sa_mask);
+ nsa.sa_flags = SA_RESTART;
+ sigaction(SIGHUP, &nsa, NULL);
+ sigaction(SIGCHLD, &nsa, NULL);
+ sigaction(SIGTERM, &nsa, NULL);
+
+ sigemptyset(&sigmask);
+ sigaddset(&sigmask, SIGHUP);
+ sigaddset(&sigmask, SIGCHLD);
+ sigaddset(&sigmask, SIGTERM);
+ sigprocmask(SIG_SETMASK, &sigmask, &osigmask);
+
+ while (1) {
+ fd_set rfds;
+ int res;
+ struct timeval to;
+
+ to.tv_sec = BATT_CHK_INTV;
+ to.tv_usec = 0;
+
+ memcpy(&rfds, &master_rfds, sizeof rfds);
+ sigprocmask(SIG_SETMASK, &osigmask, NULL);
+ if ((res=select(fdmax + 1, &rfds, 0, 0, &to)) < 0) {
+ if (errno != EINTR)
+ (void) err(1, "select");
+ }
+ sigprocmask(SIG_SETMASK, &sigmask, NULL);
+
+ if (res == 0) { /* time to check the battery */
+ check_battery();
+ continue;
+ }
+
+ if (FD_ISSET(signal_fd[0], &rfds)) {
+ if (proc_signal(signal_fd[0]) < 0)
+ goto out;
+ }
+
+ if (FD_ISSET(apmctl_fd, &rfds))
+ proc_apmevent(apmctl_fd);
+ }
+out:
+ return;
+}
+
+int
+main(int ac, char* av[])
+{
+ int ch;
+ int daemonize = 1;
+ char *prog;
+ int logopt = LOG_NDELAY | LOG_PID;
+
+ while ((ch = getopt(ac, av, "df:v")) != EOF) {
+ switch (ch) {
+ case 'd':
+ daemonize = 0;
+ debug_level++;
+ break;
+ case 'f':
+ apmd_configfile = optarg;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ default:
+ (void) err(1, "unknown option `%c'", ch);
+ }
+ }
+
+ if (daemonize)
+ daemon(0, 0);
+
+#ifdef NICE_INCR
+ (void) nice(NICE_INCR);
+#endif
+
+ if (!daemonize)
+ logopt |= LOG_PERROR;
+
+ prog = strrchr(av[0], '/');
+ openlog(prog ? prog+1 : av[0], logopt, LOG_DAEMON);
+
+ syslog(LOG_NOTICE, "start");
+
+ if (pipe(signal_fd) < 0)
+ (void) err(1, "pipe");
+ if (fcntl(signal_fd[0], F_SETFL, O_NONBLOCK) < 0)
+ (void) err(1, "fcntl");
+
+ if ((apmnorm_fd = open(APM_NORM_DEVICEFILE, O_RDWR)) == -1) {
+ (void) err(1, "cannot open device file `%s'", APM_NORM_DEVICEFILE);
+ }
+
+ if ((apmctl_fd = open(APM_CTL_DEVICEFILE, O_RDWR)) == -1) {
+ (void) err(1, "cannot open device file `%s'", APM_CTL_DEVICEFILE);
+ }
+
+ restart();
+ write_pid();
+ event_loop();
+ exit(EXIT_SUCCESS);
+}
+
diff --git a/usr.sbin/apmd/apmd.h b/usr.sbin/apmd/apmd.h
new file mode 100644
index 0000000..b716d0d
--- /dev/null
+++ b/usr.sbin/apmd/apmd.h
@@ -0,0 +1,108 @@
+/*-
+ * APM (Advanced Power Management) Event Dispatcher
+ *
+ * Copyright (c) 1999 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
+ * Copyright (c) 1999 KOIE Hidetaka <koie@suri.co.jp>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define APMD_CONFIGFILE "/etc/apmd.conf"
+#define APM_CTL_DEVICEFILE "/dev/apmctl"
+#define APM_NORM_DEVICEFILE "/dev/apm"
+#define APMD_PIDFILE "/var/run/apmd.pid"
+#define NICE_INCR -20
+
+enum {
+ EVENT_NOEVENT,
+ EVENT_STANDBYREQ,
+ EVENT_SUSPENDREQ,
+ EVENT_NORMRESUME,
+ EVENT_CRITRESUME,
+ EVENT_BATTERYLOW,
+ EVENT_POWERSTATECHANGE,
+ EVENT_UPDATETIME,
+ EVENT_CRITSUSPEND,
+ EVENT_USERSTANDBYREQ,
+ EVENT_USERSUSPENDREQ,
+ EVENT_STANDBYRESUME,
+ EVENT_CAPABILITIESCHANGE,
+ EVENT_MAX
+};
+
+struct event_cmd_op {
+ int (* act) __P((void *this));
+ void (* dump) __P((void *this, FILE * fp));
+ struct event_cmd * (* clone) __P((void *this));
+ void (* free) __P((void *this));
+};
+struct event_cmd {
+ struct event_cmd * next;
+ size_t len;
+ char * name;
+ struct event_cmd_op * op;
+};
+struct event_cmd_exec {
+ struct event_cmd evcmd;
+ char * line; /* Command line */
+};
+struct event_cmd_reject {
+ struct event_cmd evcmd;
+};
+
+struct event_config {
+ const char *name;
+ struct event_cmd * cmdlist;
+ int rejectable;
+};
+
+struct battery_watch_event {
+ struct battery_watch_event *next;
+ int level;
+ enum {
+ BATTERY_CHARGING,
+ BATTERY_DISCHARGING
+ } direction;
+ enum {
+ BATTERY_MINUTES,
+ BATTERY_PERCENT
+ } type;
+ int done;
+ struct event_cmd *cmdlist;
+};
+
+
+extern struct event_cmd_op event_cmd_exec_ops;
+extern struct event_cmd_op event_cmd_reject_ops;
+extern struct event_config events[EVENT_MAX];
+extern struct battery_watch_event *battery_watch_list;
+
+extern int register_battery_handlers(
+ int level, int direction,
+ struct event_cmd *cmdlist);
+extern int register_apm_event_handlers(
+ bitstr_t bit_decl(evlist, EVENT_MAX),
+ struct event_cmd *cmdlist);
+extern void free_event_cmd_list(struct event_cmd *p);
diff --git a/usr.sbin/apmd/apmdlex.l b/usr.sbin/apmd/apmdlex.l
new file mode 100644
index 0000000..0e5cff3
--- /dev/null
+++ b/usr.sbin/apmd/apmdlex.l
@@ -0,0 +1,112 @@
+%{
+/*-
+ * APM (Advanced Power Management) Event Dispatcher
+ *
+ * Copyright (c) 1999 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
+ * Copyright (c) 1999 KOIE Hidetaka <koie@suri.co.jp>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <string.h>
+#include <syslog.h>
+#include <bitstring.h>
+#include "apmd.h"
+#include "y.tab.h"
+
+int lineno;
+int first_time;
+%}
+
+%s TOP
+
+%%
+
+%{
+ if (first_time) {
+ BEGIN TOP;
+ lineno = 1;
+ first_time = 0;
+ }
+%}
+
+<TOP>[ \t]+ ;
+<TOP>\n lineno++;
+<TOP>, { return COMMA; }
+<TOP>; { return SEMICOLON; }
+<TOP>#.*$ ;
+
+<TOP>apm_event { return APMEVENT; }
+
+<TOP>NOEVENT { yylval.ev = EVENT_NOEVENT; return EVENT; }
+<TOP>STANDBYREQ { yylval.ev = EVENT_STANDBYREQ; return EVENT; }
+<TOP>SUSPENDREQ { yylval.ev = EVENT_SUSPENDREQ; return EVENT; }
+<TOP>NORMRESUME { yylval.ev = EVENT_NORMRESUME; return EVENT; }
+<TOP>CRITRESUME { yylval.ev = EVENT_CRITRESUME; return EVENT; }
+<TOP>BATTERYLOW { yylval.ev = EVENT_BATTERYLOW; return EVENT; }
+<TOP>POWERSTATECHANGE { yylval.ev = EVENT_POWERSTATECHANGE; return EVENT; }
+<TOP>UPDATETIME { yylval.ev = EVENT_UPDATETIME; return EVENT; }
+<TOP>CRITSUSPEND { yylval.ev = EVENT_CRITSUSPEND; return EVENT; }
+<TOP>USERSTANDBYREQ { yylval.ev = EVENT_USERSTANDBYREQ; return EVENT; }
+<TOP>USERSUSPENDREQ { yylval.ev = EVENT_USERSUSPENDREQ; return EVENT; }
+<TOP>STANDBYRESUME { yylval.ev = EVENT_STANDBYRESUME; return EVENT; }
+<TOP>CAPABILITIESCHANGE { yylval.ev = EVENT_CAPABILITIESCHANGE; return EVENT; }
+
+<TOP>apm_battery { return APMBATT; }
+
+<TOP>charging { return BATTCHARGE; }
+<TOP>discharging { return BATTDISCHARGE; }
+<TOP>[0-9]+% {
+ yylval.i = atoi(yytext);
+ return BATTPERCENT;
+ }
+<TOP>[0-9]+[Mm] {
+ yylval.i = -atoi(yytext);
+ return BATTTIME;
+ }
+
+<TOP>exec { return EXECCMD; }
+<TOP>reject { return REJECTCMD; }
+
+<TOP>\{ { return BEGINBLOCK; }
+<TOP>\} { return ENDBLOCK; }
+<TOP>\"[^"]+\" {
+ int len = strlen(yytext) - 2;
+ if ((yylval.str = (char *) malloc(len + 1)) == NULL)
+ goto out;
+ memcpy(yylval.str, yytext + 1, len);
+ yylval.str[len] = '\0';
+ out:
+ return STRING;
+ }
+
+<TOP>[^"{},;#\n\t ]+ { yylval.str = strdup(yytext); return UNKNOWN; }
+%%
+
+void
+yyerror(const char *s)
+{
+ syslog(LOG_ERR, "line %d: %s%s %s.\n", lineno, yytext, yytext?":":"", s);
+}
diff --git a/usr.sbin/apmd/apmdparse.y b/usr.sbin/apmd/apmdparse.y
new file mode 100644
index 0000000..f0acea3
--- /dev/null
+++ b/usr.sbin/apmd/apmdparse.y
@@ -0,0 +1,204 @@
+%{
+/*-
+ * APM (Advanced Power Management) Event Dispatcher
+ *
+ * Copyright (c) 1999 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
+ * Copyright (c) 1999 KOIE Hidetaka <koie@suri.co.jp>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <stdio.h>
+#include <bitstring.h>
+#include <stdlib.h>
+#include "apmd.h"
+
+#ifdef DEBUG
+#define YYDEBUG 1
+#endif
+
+extern int first_time;
+
+%}
+
+%union {
+ char *str;
+ bitstr_t bit_decl(evlist, EVENT_MAX);
+ int ev;
+ struct event_cmd * evcmd;
+ int i;
+}
+
+%token BEGINBLOCK ENDBLOCK
+%token COMMA SEMICOLON
+%token APMEVENT
+%token APMBATT
+%token BATTCHARGE BATTDISCHARGE
+%token <str> BATTTIME BATTPERCENT
+%token EXECCMD REJECTCMD
+%token <ev> EVENT
+%token <str> STRING UNKNOWN
+
+%type <i> apm_battery_level
+%type <i> apm_battery_direction
+%type <str> string
+%type <str> unknown
+%type <evlist> event_list
+%type <evcmd> cmd_list
+%type <evcmd> cmd
+%type <evcmd> exec_cmd reject_cmd
+
+%%
+
+config_file
+ : { first_time = 1; } config_list
+ ;
+
+config_list
+ : config
+ | config_list config
+ ;
+
+config
+ : apm_event_statement
+ | apm_battery_statement
+ ;
+
+apm_event_statement
+ : APMEVENT event_list BEGINBLOCK cmd_list ENDBLOCK
+ {
+ if (register_apm_event_handlers($2, $4) < 0)
+ abort(); /* XXX */
+ free_event_cmd_list($4);
+ }
+ ;
+
+apm_battery_level
+ : BATTPERCENT
+ {
+ $$ = $1;
+ }
+ | BATTTIME
+ {
+ $$ = $1;
+ }
+ ;
+
+apm_battery_direction
+ : BATTCHARGE
+ {
+ $$ = 1;
+ }
+ | BATTDISCHARGE
+ {
+ $$ = -1;
+ }
+ ;
+apm_battery_statement
+ : APMBATT apm_battery_level apm_battery_direction
+ BEGINBLOCK cmd_list ENDBLOCK
+ {
+ if (register_battery_handlers($2, $3, $5) < 0)
+ abort(); /* XXX */
+ free_event_cmd_list($5);
+ }
+ ;
+
+event_list
+ : EVENT
+ {
+ bit_nclear($$, 0, EVENT_MAX - 1);
+ bit_set($$, $1);
+ }
+ | event_list COMMA EVENT
+ {
+ memcpy(&($$), &($1), bitstr_size(EVENT_MAX));
+ bit_set($$, $3);
+ }
+ ;
+
+cmd_list
+ : /* empty */
+ {
+ $$ = NULL;
+ }
+ | cmd_list cmd
+ {
+ struct event_cmd * p = $1;
+ if (p) {
+ while (p->next != NULL)
+ p = p->next;
+ p->next = $2;
+ $$ = $1;
+ } else {
+ $$ = $2;
+ }
+ }
+ ;
+
+cmd
+ : exec_cmd SEMICOLON { $$ = $1; }
+ | reject_cmd SEMICOLON { $$ = $1; }
+ ;
+
+exec_cmd
+ : EXECCMD string
+ {
+ size_t len = sizeof (struct event_cmd_exec);
+ struct event_cmd_exec *cmd = malloc(len);
+ cmd->evcmd.next = NULL;
+ cmd->evcmd.len = len;
+ cmd->evcmd.name = "exec";
+ cmd->evcmd.op = &event_cmd_exec_ops;
+ cmd->line = $2;
+ $$ = (struct event_cmd *) cmd;
+ }
+ ;
+
+reject_cmd
+ : REJECTCMD
+ {
+ size_t len = sizeof (struct event_cmd_reject);
+ struct event_cmd_reject *cmd = malloc(len);
+ cmd->evcmd.next = NULL;
+ cmd->evcmd.len = len;
+ cmd->evcmd.name = "reject";
+ cmd->evcmd.op = &event_cmd_reject_ops;
+ $$ = (struct event_cmd *) cmd;
+ }
+ ;
+
+string
+ : STRING { $$ = $1; }
+ ;
+
+unknown
+ : UNKNOWN
+ {
+ $$ = $1;
+ }
+ ;
+%%
+
diff --git a/usr.sbin/apmd/contrib/pccardq.c b/usr.sbin/apmd/contrib/pccardq.c
new file mode 100644
index 0000000..1562c55f
--- /dev/null
+++ b/usr.sbin/apmd/contrib/pccardq.c
@@ -0,0 +1,286 @@
+/* $FreeBSD$ */
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+const char *const pccardd_file = "/var/tmp/.pccardd";
+const char *prog = "pccardq";
+const char *tmp_dir = "/tmp";
+unsigned slot_map = ~0;
+
+void
+usage()
+{
+ fprintf(stderr, "usage: %s [-a] [-n] [-s slot]\n", prog);
+}
+
+int
+proc_arg(int ac, char **av)
+{
+ int rc = -1;
+ int ch;
+
+ char *p = strrchr(av[0], '/');
+ prog = p ? p + 1 : av[0];
+
+ tmp_dir = getenv("TMPDIR") ? getenv("TMPDIR") : tmp_dir;
+
+ while ((ch = getopt(ac, av, "ans:")) != EOF) {
+ switch (ch) {
+ case 'a':
+ slot_map = ~0;
+ break;
+ case 'n':
+ slot_map = 0;
+ break;
+ case 's':
+ {
+ int n = atoi(optarg);
+ if (n < 0 || n >= CHAR_BIT * sizeof slot_map) {
+ warnc(0, "Invalid slot number.");
+ usage();
+ goto out;
+ }
+ if (slot_map == ~0)
+ slot_map = 0;
+ slot_map |= 1 << n;
+ }
+ break;
+ default:
+ usage();
+ goto out;
+ }
+ }
+
+ rc = 0;
+ out:
+ return rc;
+}
+
+int
+connect_to_pccardd(char **path)
+{
+ int so = -1;
+ int pccardd_len;
+ struct sockaddr_un pccardq;
+ struct sockaddr_un pccardd;
+
+ if ((so = socket(PF_UNIX, SOCK_DGRAM, 0)) < 0) {
+ warn("socket");
+ goto err;
+ }
+
+ snprintf(pccardq.sun_path, sizeof pccardq.sun_path,
+ "%s/%s%ld%ld", tmp_dir, prog, (long) getpid(), (long) time(0));
+ pccardq.sun_family = AF_UNIX;
+ pccardq.sun_len = offsetof(struct sockaddr_un, sun_path) + strlen(pccardq.sun_path);
+ if (bind(so, (struct sockaddr *) &pccardq, pccardq.sun_len) < 0) {
+ warn("bind: %s", pccardq.sun_path);
+ goto err;
+ }
+ if ((*path = strdup(pccardq.sun_path)) == NULL) {
+ warn("strdup");
+ goto err;
+ }
+
+ pccardd_len = strlen(pccardd_file) + 1;
+ if (pccardd_len > sizeof pccardd.sun_path) {
+ warnc(0, "%s: too long", pccardd_file);
+ goto err;
+ }
+ pccardd.sun_len = offsetof(struct sockaddr_un, sun_path) + pccardd_len;
+ pccardd.sun_family = AF_UNIX;
+ strcpy(pccardd.sun_path, pccardd_file);
+ if (connect(so, (struct sockaddr *) &pccardd, pccardd.sun_len) < 0) {
+ warn("connect: %s", pccardd_file);
+ goto err;
+ }
+ return so;
+ err:
+ if (so >= 0)
+ close(so);
+ return -1;
+}
+
+int
+get_slot_number(int so)
+{
+ char buf[8];
+ int rv;
+ int nslot;
+
+ if ((rv = write(so, "S", 1)) < 1) {
+ warn("write");
+ goto err;
+ } else if (rv != 1) {
+ warnc(0, "write: fail.");
+ goto err;
+ }
+
+ if ((rv = read(so, buf, sizeof buf)) < 0) {
+ warn("read");
+ goto err;
+ }
+ buf[sizeof buf - 1] = 0;
+ if (sscanf(buf, "%d", &nslot) != 1) {
+ warnc(0, "Invalid response.");
+ goto err;
+ }
+ return nslot;
+ err:
+ return -1;
+}
+
+enum {
+ SLOT_EMPTY = 0,
+ SLOT_FILLED = 1,
+ SLOT_INACTIVE = 2,
+ SLOT_UNDEFINED = 9
+};
+
+int
+get_slot_info(int so, int slot, char **manuf, char **version, char
+ **device, int *state)
+{
+ int rc = -1;
+ int rv;
+ static char buf[1024];
+ int slen;
+ char *s;
+ char *sl;
+
+ char *_manuf;
+ char *_version;
+ char *_device;
+
+ if ((slen = snprintf(buf, sizeof buf, "N%d", slot)) < 0) {
+ warnc(0, "write");
+ goto err;
+ }
+
+ if ((rv = write(so, buf, slen)) < 0) {
+ warn("write");
+ goto err;
+ } else if (rv != slen) {
+ warnc(0, "write");
+ goto err;
+ }
+
+ if ((rv = read(so, buf, sizeof buf)) < 0) {
+ warn("read");
+ goto err;
+ }
+
+ s = buf;
+ if ((sl = strsep(&s, "~")) == NULL)
+ goto parse_err;
+ if (atoi(sl) != slot)
+ goto parse_err;
+ if ((_manuf = strsep(&s, "~")) == NULL)
+ goto parse_err;
+ if ((_version = strsep(&s, "~")) == NULL)
+ goto parse_err;
+ if ((_device = strsep(&s, "~")) == NULL)
+ goto parse_err;
+ if (sscanf(s, "%1d", state) != 1)
+ goto parse_err;
+ if (s != NULL && strchr(s, '~') != NULL)
+ goto parse_err;
+
+ if ((*manuf = strdup(_manuf)) == NULL) {
+ warn("strdup");
+ goto err;
+ }
+ if ((*version = strdup(_version)) == NULL) {
+ warn("strdup");
+ goto err;
+ }
+ if ((*device = strdup(_device)) == NULL) {
+ warn("strdup");
+ goto err;
+ }
+ if (*manuf == NULL || *version == NULL || *device == NULL) {
+ warn("strdup");
+ goto err;
+ }
+
+ rc = 0;
+ err:
+ return rc;
+ parse_err:
+ warnc(0, "Invalid response: %*s", rv, buf);
+ return rc;
+}
+
+const char *
+strstate(int state)
+{
+ switch (state) {
+ case 0:
+ return "empty";
+ case 1:
+ return "filled";
+ case 2:
+ return "inactive";
+ default:
+ return "unknown";
+ }
+}
+
+int
+main(int ac, char **av)
+{
+ char *path = NULL;
+ int so = -1;
+ int nslot;
+ int i;
+
+ if (proc_arg(ac, av) < 0)
+ goto out;
+ if ((so = connect_to_pccardd(&path)) < 0)
+ goto out;
+ if ((nslot = get_slot_number(so)) < 0)
+ goto out;
+ if (slot_map == 0) {
+ printf("%d\n", nslot);
+ } else {
+ for (i = 0; i < nslot; i++) {
+ if ((slot_map & (1 << i))) {
+ char *manuf;
+ char *version;
+ char *device;
+ int state;
+
+ if (get_slot_info(so, i, &manuf, &version, &device,
+ &state) < 0)
+ goto out;
+ if (manuf == NULL || version == NULL || device == NULL)
+ goto out;
+ printf("%d~%s~%s~%s~%s\n",
+ i, manuf, version, device, strstate(state));
+ free(manuf);
+ free(version);
+ free(device);
+ }
+ }
+ }
+ out:
+ if (path) {
+ unlink(path);
+ free(path);
+ }
+ if (so >= 0)
+ close(so);
+ exit(0);
+}
diff --git a/usr.sbin/arp/Makefile b/usr.sbin/arp/Makefile
new file mode 100644
index 0000000..474886a
--- /dev/null
+++ b/usr.sbin/arp/Makefile
@@ -0,0 +1,8 @@
+# @(#)Makefile 8.2 (Berkeley) 4/18/94
+# $FreeBSD$
+
+PROG= arp
+WARNS?= 2
+MAN= arp.4 arp.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/arp/arp.4 b/usr.sbin/arp/arp.4
new file mode 100644
index 0000000..8e539d1
--- /dev/null
+++ b/usr.sbin/arp/arp.4
@@ -0,0 +1,143 @@
+.\" Copyright (c) 1985, 1986, 1988, 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)arp4.4 6.5 (Berkeley) 4/18/94
+.\" $FreeBSD$
+.\"
+.Dd April 18, 1994
+.Dt ARP 4
+.Os
+.Sh NAME
+.Nm arp
+.Nd Address Resolution Protocol
+.Sh SYNOPSIS
+.Cd "pseudo-device ether"
+.Sh DESCRIPTION
+The Address Resolution Protocol (ARP) is used to dynamically
+map between Protocol Addresses (such as IP addresses) and
+Local Network Addresses (such as Ethernet addresses).
+This implementation maps IP addresses to Ethernet,
+ARCnet,
+or Token Ring addresses.
+It is used by all the Ethernet interface drivers.
+.Pp
+ARP caches Internet-Ethernet address mappings.
+When an interface requests a mapping for an address not in the cache,
+ARP queues the message which requires the mapping and broadcasts
+a message on the associated network requesting the address mapping.
+If a response is provided, the new mapping is cached and any pending
+message is transmitted.
+ARP will queue at most one packet while waiting for a response to a
+mapping request;
+only the most recently ``transmitted'' packet is kept.
+If the target host does not respond after several requests,
+the host is considered to be down for a short period (normally 20 seconds),
+allowing an error to be returned to transmission attempts during this
+interval.
+The error is
+.Er EHOSTDOWN
+for a non-responding destination host, and
+.Er EHOSTUNREACH
+for a non-responding router.
+.Pp
+The ARP cache is stored in the system routing table as
+dynamically-created host routes.
+The route to a directly-attached Ethernet network is installed as a
+.Dq cloning
+route (one with the
+.Li RTF_CLONING
+flag set),
+causing routes to individual hosts on that network to be created on
+demand.
+These routes time out periodically (normally 20 minutes after validated;
+entries are not validated when not in use).
+An entry for a host which is not responding is a
+.Dq reject
+route (one with the
+.Li RTF_REJECT
+flag set).
+.Pp
+ARP entries may be added, deleted or changed with the
+.Xr arp 8
+utility.
+Manually-added entries may be temporary or permanent,
+and may be
+.Dq published ,
+in which case the system will respond to ARP requests for that host
+as if it were the target of the request.
+.Pp
+In the past,
+ARP was used to negotiate the use of a trailer encapsulation.
+This is no longer supported.
+.Pp
+ARP watches passively for hosts impersonating the local host (i.e. a host
+which responds to an ARP mapping request for the local host's address).
+.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
+.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..e954df8
--- /dev/null
+++ b/usr.sbin/arp/arp.8
@@ -0,0 +1,175 @@
+.\" Copyright (c) 1985, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)arp.8 8.1 (Berkeley) 6/6/93
+.\" $FreeBSD$
+.\"
+.Dd June 6, 1993
+.Dt ARP 8
+.Os
+.Sh NAME
+.Nm arp
+.Nd address resolution display and control
+.Sh SYNOPSIS
+.Nm
+.Op Fl n
+.Ar hostname
+.Nm
+.Op Fl n
+.Fl a
+.Nm
+.Fl d Ar hostname
+.Op Cm pub
+.Nm
+.Fl d
+.Fl a
+.Nm
+.Fl s Ar hostname ether_addr
+.Op Cm temp
+.Op Cm pub Op Cm only
+.Nm
+.Fl S Ar hostname ether_addr
+.Op Cm temp
+.Op Cm pub Op Cm only
+.Nm
+.Fl f Ar filename
+.Sh DESCRIPTION
+The
+.Nm
+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 indent
+.It Fl a
+The program displays or deletes all of the current
+.Tn ARP
+entries.
+.It Fl d
+A super-user may delete an entry for the host called
+.Ar hostname
+with the
+.Fl d
+flag.
+If the
+.Cm pub
+keyword is specified, only the
+.Dq published
+.Tn ARP
+entry
+for this host will be deleted.
+.Pp
+Alternatively, the
+.Fl d
+flag may be combined with the
+.Fl a
+flag to delete all entries.
+.It Fl n
+Show network addresses as numbers (normally
+.Nm
+attempts to display addresses symbolically).
+.It Fl s Ar hostname ether_addr
+Create an
+.Tn ARP
+entry for the host called
+.Ar hostname
+with the Ethernet address
+.Ar ether_addr .
+The Ethernet address is given as six hex bytes separated by colons.
+The entry will be permanent unless the word
+.Cm temp
+is given in the command.
+If the word
+.Cm pub
+is given, the entry will be
+.Dq published ;
+i.e., this system will
+act as an
+.Tn ARP
+server,
+responding to requests for
+.Ar hostname
+even though the host address is not its own.
+In this case the
+.Ar ether_addr
+can be given as
+.Cm auto
+in which case the interfaces on this host will be examined,
+and if one of them is found to occupy the same subnet, its
+Ethernet address will be used.
+If the
+.Cm only
+keyword is also specified, this will create a
+.Dq "published (proxy only)"
+entry.
+This type of entry is created automatically if
+.Nm
+detects that a routing table entry for
+.Ar hostname
+already exists.
+.It Fl S Ar hostname ether_addr
+Is just like
+.Fl s
+except any existing
+.Tn ARP
+entry for this host will be deleted first.
+.It Fl f Ar filename
+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 -ragged -offset indent -compact
+.Ar hostname ether_addr
+.Op Cm temp
+.Op Cm 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..e6342ed
--- /dev/null
+++ b/usr.sbin/arp/arp.c
@@ -0,0 +1,760 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#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 <net/iso88025.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 <string.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 *addr, struct rt_msghdr *rtm);
+void nuke_entry(struct sockaddr_dl *sdl,
+ struct sockaddr_inarp *addr, struct rt_msghdr *rtm);
+int delete(char *host, char *info);
+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, struct ether_addr *n);
+int rtmsg(int cmd);
+int get_ether_addr(u_int32_t ipaddr, struct ether_addr *hwaddr);
+
+static int pid;
+static int nflag; /* no reverse dns lookups */
+static int aflag; /* do it for all entries */
+static int s = -1;
+
+struct sockaddr_in so_mask;
+struct sockaddr_inarp blank_sin, sin_m;
+struct sockaddr_dl blank_sdl, sdl_m;
+int expire_time, flags, doing_proxy, proxy_only, found_entry;
+struct {
+ struct rt_msghdr m_rtm;
+ char m_space[512];
+} m_rtmsg;
+
+/* 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 ROUNDUP(a) \
+ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+#define SETFUNC(f) { if (func) usage(); func = (f); }
+
+int
+main(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;
+
+ bzero(&so_mask, sizeof(so_mask));
+ so_mask.sin_len = 8;
+ so_mask.sin_addr.s_addr = 0xffffffff;
+ bzero(&blank_sin, sizeof(blank_sin));
+ blank_sin.sin_len = sizeof(blank_sin);
+ blank_sin.sin_family = AF_INET;
+ bzero(&blank_sdl, sizeof(blank_sdl));
+ blank_sdl.sdl_len = sizeof(blank_sdl);
+ blank_sdl.sdl_family = AF_LINK;
+
+ 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 > 6)
+ 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, "%49s %49s %49s %49s %49s", arg[0], arg[1],
+ arg[2], arg[3], arg[4]);
+ if (i < 2) {
+ warnx("bad line: %s", line);
+ retval = 1;
+ continue;
+ }
+ if (set(i, args))
+ retval = 1;
+ }
+ fclose(fp);
+ return (retval);
+}
+
+void
+getsocket(void)
+{
+ if (s < 0) {
+ s = socket(PF_ROUTE, SOCK_RAW, 0);
+ if (s < 0)
+ err(1, "socket");
+ }
+}
+
+/*
+ * Set an individual arp entry
+ */
+int
+set(int argc, char **argv)
+{
+ struct hostent *hp;
+ register struct sockaddr_inarp *addr = &sin_m;
+ register struct sockaddr_dl *sdl;
+ register struct rt_msghdr *rtm = &(m_rtmsg.m_rtm);
+ struct ether_addr *ea;
+ char *host = argv[0], *eaddr = argv[1];
+
+ getsocket();
+ argc -= 2;
+ argv += 2;
+ sdl_m = blank_sdl;
+ sin_m = blank_sin;
+ addr->sin_addr.s_addr = inet_addr(host);
+ if (addr->sin_addr.s_addr == INADDR_NONE) {
+ if (!(hp = gethostbyname(host))) {
+ warnx("%s: %s", host, hstrerror(h_errno));
+ return (1);
+ }
+ bcopy((char *)hp->h_addr, (char *)&addr->sin_addr,
+ sizeof addr->sin_addr);
+ }
+ doing_proxy = flags = proxy_only = expire_time = 0;
+ while (argc-- > 0) {
+ if (strncmp(argv[0], "temp", 4) == 0) {
+ struct timeval tv;
+ gettimeofday(&tv, 0);
+ expire_time = tv.tv_sec + 20 * 60;
+ }
+ else if (strncmp(argv[0], "pub", 3) == 0) {
+ flags |= RTF_ANNOUNCE;
+ doing_proxy = 1;
+ if (argc && strncmp(argv[1], "only", 3) == 0) {
+ proxy_only = 1;
+ sin_m.sin_other = SIN_PROXY;
+ argc--; argv++;
+ }
+ } else if (strncmp(argv[0], "trail", 5) == 0) {
+ printf("%s: Sending trailers is no longer supported\n",
+ host);
+ }
+ argv++;
+ }
+ ea = (struct ether_addr *)LLADDR(&sdl_m);
+ if (doing_proxy && !strcmp(eaddr, "auto")) {
+ if (!get_ether_addr(addr->sin_addr.s_addr, ea)) {
+ printf("no interface found for %s\n",
+ inet_ntoa(addr->sin_addr));
+ return (1);
+ }
+ sdl_m.sdl_alen = ETHER_ADDR_LEN;
+ } else {
+ if (my_ether_aton(eaddr, ea) == 0)
+ sdl_m.sdl_alen = ETHER_ADDR_LEN;
+ }
+tryagain:
+ if (rtmsg(RTM_GET) < 0) {
+ warn("%s", host);
+ return (1);
+ }
+ addr = (struct sockaddr_inarp *)(rtm + 1);
+ sdl = (struct sockaddr_dl *)(ROUNDUP(addr->sin_len) + (char *)addr);
+ if (addr->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: case IFT_L2VLAN:
+ 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;
+ proxy_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 *addr = &sin_m;
+
+ sin_m = blank_sin;
+ addr->sin_addr.s_addr = inet_addr(host);
+ if (addr->sin_addr.s_addr == INADDR_NONE) {
+ if (!(hp = gethostbyname(host)))
+ errx(1, "%s: %s", host, hstrerror(h_errno));
+ bcopy((char *)hp->h_addr, (char *)&addr->sin_addr,
+ sizeof addr->sin_addr);
+ }
+ search(addr->sin_addr.s_addr, print_entry);
+ if (found_entry == 0) {
+ printf("%s (%s) -- no entry\n",
+ host, inet_ntoa(addr->sin_addr));
+ return(1);
+ }
+ return(0);
+}
+
+/*
+ * Delete an arp entry
+ */
+int
+delete(char *host, char *info)
+{
+ struct hostent *hp;
+ register struct sockaddr_inarp *addr = &sin_m;
+ register struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
+ struct sockaddr_dl *sdl;
+
+ getsocket();
+ sin_m = blank_sin;
+ if (info) {
+ if (strncmp(info, "pub", 3) == 0)
+ sin_m.sin_other = SIN_PROXY;
+ else
+ usage();
+ }
+ addr->sin_addr.s_addr = inet_addr(host);
+ if (addr->sin_addr.s_addr == INADDR_NONE) {
+ if (!(hp = gethostbyname(host))) {
+ warnx("%s: %s", host, hstrerror(h_errno));
+ return (1);
+ }
+ bcopy((char *)hp->h_addr, (char *)&addr->sin_addr,
+ sizeof addr->sin_addr);
+ }
+tryagain:
+ if (rtmsg(RTM_GET) < 0) {
+ warn("%s", host);
+ return (1);
+ }
+ addr = (struct sockaddr_inarp *)(rtm + 1);
+ sdl = (struct sockaddr_dl *)(ROUNDUP(addr->sin_len) + (char *)addr);
+ if (addr->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: case IFT_L2VLAN:
+ 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(addr->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 *sin2;
+ struct sockaddr_dl *sdl;
+
+ 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;
+ sin2 = (struct sockaddr_inarp *)(rtm + 1);
+ (char *)sdl = (char *)sin2 + ROUNDUP(sin2->sin_len);
+ if (addr) {
+ if (addr != sin2->sin_addr.s_addr)
+ continue;
+ found_entry = 1;
+ }
+ (*action)(sdl, sin2, rtm);
+ }
+ free(buf);
+}
+
+/*
+ * Display an arp entry
+ */
+void
+print_entry(struct sockaddr_dl *sdl,
+ struct sockaddr_inarp *addr, struct rt_msghdr *rtm)
+{
+ const char *host;
+ struct hostent *hp;
+ struct iso88025_sockaddr_dl_data *trld;
+ char ifname[IF_NAMESIZE];
+ int seg;
+
+ if (nflag == 0)
+ hp = gethostbyaddr((caddr_t)&(addr->sin_addr),
+ sizeof addr->sin_addr, AF_INET);
+ else
+ hp = 0;
+ if (hp)
+ host = hp->h_name;
+ else {
+ host = "?";
+ if (h_errno == TRY_AGAIN)
+ nflag = 1;
+ }
+ printf("%s (%s) at ", host, inet_ntoa(addr->sin_addr));
+ if (sdl->sdl_alen)
+ printf("%s", ether_ntoa((struct ether_addr *)LLADDR(sdl)));
+ else
+ printf("(incomplete)");
+ if (if_indextoname(sdl->sdl_index, ifname) != NULL)
+ printf(" on %s", ifname);
+ if (rtm->rtm_rmx.rmx_expire == 0)
+ printf(" permanent");
+ if (addr->sin_other & SIN_PROXY)
+ printf(" published (proxy only)");
+ if (rtm->rtm_addrs & RTA_NETMASK) {
+ addr = (struct sockaddr_inarp *)
+ (ROUNDUP(sdl->sdl_len) + (char *)sdl);
+ if (addr->sin_addr.s_addr == 0xffffffff)
+ printf(" published");
+ if (addr->sin_len != 8)
+ printf("(weird)");
+ }
+ switch(sdl->sdl_type) {
+ case IFT_ETHER:
+ printf(" [ethernet]");
+ break;
+ case IFT_ISO88025:
+ printf(" [token-ring]");
+ trld = SDL_ISO88025(sdl);
+ if (trld->trld_rcf != 0) {
+ printf(" rt=%x", ntohs(trld->trld_rcf));
+ for (seg = 0;
+ seg < ((TR_RCF_RIFLEN(trld->trld_rcf) - 2 ) / 2);
+ seg++)
+ printf(":%x", ntohs(*(trld->trld_route[seg])));
+ }
+ break;
+ case IFT_FDDI:
+ printf(" [fddi]");
+ break;
+ case IFT_ATM:
+ printf(" [atm]");
+ break;
+ case IFT_L2VLAN:
+ printf(" [vlan]");
+ break;
+ default:
+ break;
+ }
+
+ printf("\n");
+
+}
+
+/*
+ * Nuke an arp entry
+ */
+void
+nuke_entry(struct sockaddr_dl *sdl __unused,
+ struct sockaddr_inarp *addr, struct rt_msghdr *rtm __unused)
+{
+ char ip[20];
+
+ snprintf(ip, sizeof(ip), "%s", inet_ntoa(addr->sin_addr));
+ delete(ip, NULL);
+}
+
+int
+my_ether_aton(char *a, struct ether_addr *n)
+{
+ struct ether_addr *ea;
+
+ if ((ea = ether_aton(a)) == NULL) {
+ warnx("invalid Ethernet address '%s'", a);
+ return (1);
+ }
+ *n = *ea;
+ 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 [pub]",
+ " 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 (proxy_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 += ROUNDUP(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_int32_t ipaddr, struct ether_addr *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];
+ int sock;
+
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock < 0)
+ err(1, "socket");
+
+ ifc.ifc_len = sizeof(ifs);
+ ifc.ifc_req = ifs;
+ if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) {
+ warnx("ioctl(SIOCGIFCONF)");
+ close(sock);
+ 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(sock, 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(sock, 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
+ + MAX(ifr->ifr_addr.sa_len, sizeof(ifr->ifr_addr)));
+ }
+
+ if (ifr >= ifend) {
+ close(sock);
+ 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 (sock);
+ printf("using interface %s for proxy with address ",
+ ifp->ifr_name);
+ printf("%s\n", ether_ntoa(hwaddr));
+ return dla->sdl_alen;
+ }
+ ifr = (struct ifreq *) ((char *)&ifr->ifr_addr
+ + MAX(ifr->ifr_addr.sa_len, sizeof(ifr->ifr_addr)));
+ }
+ return 0;
+}
diff --git a/usr.sbin/atm/Makefile b/usr.sbin/atm/Makefile
new file mode 100644
index 0000000..2367085
--- /dev/null
+++ b/usr.sbin/atm/Makefile
@@ -0,0 +1,29 @@
+# ===================================
+# HARP | Host ATM Research Platform
+# ===================================
+#
+#
+# This Host ATM Research Platform ("HARP") file (the "Software") is
+# made available by Network Computing Services, Inc. ("NetworkCS")
+# "AS IS". NetworkCS does not provide maintenance, improvements or
+# support of any kind.
+#
+# NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+# INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+# SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+# In no event shall NetworkCS be responsible for any damages, including
+# but not limited to consequential damages, arising from or relating to
+# any use of the Software or related support.
+#
+# Copyright 1994-1998 Network Computing Services, Inc.
+#
+# Copies of this Software may be made, however, the above copyright
+# notice must be reproduced on all copies.
+#
+# @(#) $FreeBSD$
+
+SUBDIR= atmarpd \
+ scspd
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/atm/Makefile.inc b/usr.sbin/atm/Makefile.inc
new file mode 100644
index 0000000..46723c1
--- /dev/null
+++ b/usr.sbin/atm/Makefile.inc
@@ -0,0 +1,26 @@
+# ===================================
+# HARP | Host ATM Research Platform
+# ===================================
+#
+#
+# This Host ATM Research Platform ("HARP") file (the "Software") is
+# made available by Network Computing Services, Inc. ("NetworkCS")
+# "AS IS". NetworkCS does not provide maintenance, improvements or
+# support of any kind.
+#
+# NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+# INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+# SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+# In no event shall NetworkCS be responsible for any damages, including
+# but not limited to consequential damages, arising from or relating to
+# any use of the Software or related support.
+#
+# Copyright 1994-1998 Network Computing Services, Inc.
+#
+# Copies of this Software may be made, however, the above copyright
+# notice must be reproduced on all copies.
+#
+# @(#) $FreeBSD$
+
+.include "../Makefile.inc"
diff --git a/usr.sbin/atm/atmarpd/Makefile b/usr.sbin/atm/atmarpd/Makefile
new file mode 100644
index 0000000..1ed613c
--- /dev/null
+++ b/usr.sbin/atm/atmarpd/Makefile
@@ -0,0 +1,36 @@
+# ===================================
+# HARP | Host ATM Research Platform
+# ===================================
+#
+#
+# This Host ATM Research Platform ("HARP") file (the "Software") is
+# made available by Network Computing Services, Inc. ("NetworkCS")
+# "AS IS". NetworkCS does not provide maintenance, improvements or
+# support of any kind.
+#
+# NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+# INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+# SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+# In no event shall NetworkCS be responsible for any damages, including
+# but not limited to consequential damages, arising from or relating to
+# any use of the Software or related support.
+#
+# Copyright 1994-1998 Network Computing Services, Inc.
+#
+# Copies of this Software may be made, however, the above copyright
+# notice must be reproduced on all copies.
+#
+# @(#) $FreeBSD$
+
+PROG= atmarpd
+MAN= atmarpd.8
+SRCS= atmarpd.c atmarp_config.c atmarp_log.c atmarp_scsp.c \
+ atmarp_subr.c atmarp_timer.c
+
+CFLAGS+= -I${.CURDIR}/../../../sys
+
+LDADD= -latm -lmd
+DPADD= ${LIBATM} ${LIBMD}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/atm/atmarpd/atmarp_config.c b/usr.sbin/atm/atmarpd/atmarp_config.c
new file mode 100644
index 0000000..d6d124e
--- /dev/null
+++ b/usr.sbin/atm/atmarpd/atmarp_config.c
@@ -0,0 +1,127 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * SCSP-ATMARP server interface: configuration support
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netatm/port.h>
+#include <netatm/queue.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+
+#include <errno.h>
+#include <libatm.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "../scspd/scsp_msg.h"
+#include "../scspd/scsp_if.h"
+#include "../scspd/scsp_var.h"
+#include "atmarp_var.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+/*
+ * Configure network interface for ATMARP cache synchronization
+ *
+ * Verify the network interface name and set the appropriate fields
+ * in the ATMARP interface entry.
+ *
+ * Arguments:
+ * netif pointer to network interface name
+ *
+ * Returns:
+ * 0 success
+ * errno reason for failure
+ *
+ */
+int
+atmarp_cfg_netif(netif)
+ char *netif;
+{
+ int rc;
+ Atmarp_intf *aip = (Atmarp_intf *)0;
+
+ /*
+ * Get an ATMARP interface block
+ */
+ aip = calloc(1, sizeof(Atmarp_intf));
+ if (aip == NULL)
+ atmarp_mem_err("atmarp_cfg_netif: sizeof(Atmarp_intf)");
+
+ /*
+ * Make sure we're configuring a valid
+ * network interface
+ */
+ rc = verify_nif_name(netif);
+ if (rc == 0) {
+ fprintf(stderr, "%s: \"%s\" is not a valid network interface\n",
+ prog, netif);
+ rc = EINVAL;
+ goto cfg_fail;
+ } else if (rc < 0) {
+ rc = errno;
+ fprintf(stderr, "%s: can't verify network interface \"%s\"\n",
+ prog, netif);
+ goto cfg_fail;
+ }
+
+ /*
+ * Update the interface entry
+ */
+ strcpy(aip->ai_intf, netif);
+ aip->ai_state = AI_STATE_NULL;
+ aip->ai_scsp_sock = -1;
+ LINK2TAIL(aip, Atmarp_intf, atmarp_intf_head, ai_next);
+
+ return(0);
+
+cfg_fail:
+ if (aip)
+ free(aip);
+ return(rc);
+}
diff --git a/usr.sbin/atm/atmarpd/atmarp_log.c b/usr.sbin/atm/atmarpd/atmarp_log.c
new file mode 100644
index 0000000..d697747
--- /dev/null
+++ b/usr.sbin/atm/atmarpd/atmarp_log.c
@@ -0,0 +1,146 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * SCSP-ATMARP server interface: logging routines
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netatm/queue.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+
+#include <errno.h>
+#include <libatm.h>
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+#include <stdio.h>
+#include <syslog.h>
+
+#include "../scspd/scsp_msg.h"
+#include "../scspd/scsp_if.h"
+#include "../scspd/scsp_var.h"
+#include "atmarp_var.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+/*
+ * Write a message to atmarpd's log
+ *
+ * Arguments:
+ * level the level (error, info, etc.) of the message
+ * fmt printf-style format string
+ * ... parameters for printf-style use according to fmt
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+#if __STDC__
+atmarp_log(const int level, const char *fmt, ...)
+#else
+atmarp_log(level, fmt, va_alist)
+ int level;
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+
+ /*
+ * In debug mode, just write to stdout
+ */
+ if (atmarp_debug_mode) {
+ vprintf(fmt, ap);
+ printf("\n");
+ return;
+ }
+
+ /*
+ * Check whether we have a log file set up
+ */
+ if (!atmarp_log_file) {
+ /*
+ * Write to syslog
+ */
+ vsyslog(level, fmt, ap);
+ } else {
+ /*
+ * Write to the log file
+ */
+ vfprintf(atmarp_log_file, fmt, ap);
+ fprintf(atmarp_log_file, "\n");
+ }
+
+ va_end(ap);
+}
+
+
+/*
+ * Log a memory error and exit
+ *
+ * Arguments:
+ * cp message to log
+ *
+ * Returns:
+ * exits, does not return
+ *
+ */
+void
+atmarp_mem_err(cp)
+ char *cp;
+{
+ atmarp_log(LOG_CRIT, "out of memory: %s", cp);
+ exit(2);
+}
diff --git a/usr.sbin/atm/atmarpd/atmarp_scsp.c b/usr.sbin/atm/atmarpd/atmarp_scsp.c
new file mode 100644
index 0000000..6a22217
--- /dev/null
+++ b/usr.sbin/atm/atmarpd/atmarp_scsp.c
@@ -0,0 +1,778 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * SCSP-ATMARP server interface: SCSP/ATMARP interface code
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netatm/port.h>
+#include <netatm/queue.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+#include <netatm/uni/uniip_var.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <libatm.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "../scspd/scsp_msg.h"
+#include "../scspd/scsp_if.h"
+#include "../scspd/scsp_var.h"
+#include "atmarp_var.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+/*
+ * Send the cache for a LIS to SCSP
+ *
+ *
+ * Arguments:
+ * aip pointer to interface block
+ *
+ * Returns:
+ * 0 cache sent to SCSP OK
+ * errno reason for failure
+ *
+ */
+int
+atmarp_scsp_cache(aip, msg)
+ Atmarp_intf *aip;
+ Scsp_if_msg *msg;
+{
+ int i, len, rc = 0;
+ Atmarp *aap;
+ Scsp_if_msg *smp = (Scsp_if_msg *)0;
+ Scsp_atmarp_msg *sap;
+
+ /*
+ * Figure out how big the message needs to be
+ */
+ len = sizeof(Scsp_if_msg_hdr);
+ for (i = 0; i < ATMARP_HASHSIZ; i++) {
+ for (aap = aip->ai_arptbl[i]; aap; aap = aap->aa_next) {
+ len += sizeof(Scsp_atmarp_msg);
+ }
+ }
+
+ /*
+ * Get memory for the cache message
+ */
+ smp = calloc(1, len);
+ if (smp == NULL)
+ atmarp_mem_err("atmarp_scsp_cache: len");
+ /*
+ * Set header fields in SCSP message
+ */
+ smp->si_type = SCSP_CACHE_RSP;
+ smp->si_proto = SCSP_PROTO_ATMARP;
+ smp->si_len = len;
+ smp->si_tok = msg->si_tok;
+
+ /*
+ * Loop through the cache, adding each entry to the SCSP
+ * Cache Response message
+ */
+ sap = &smp->si_atmarp;
+ for (i = 0; i < ATMARP_HASHSIZ; i++) {
+ for (aap = aip->ai_arptbl[i]; aap; aap = aap->aa_next) {
+ sap->sa_state = SCSP_ASTATE_NEW;
+ sap->sa_cpa = aap->aa_dstip;
+ ATM_ADDR_COPY(&aap->aa_dstatm, &sap->sa_cha);
+ ATM_ADDR_COPY(&aap->aa_dstatmsub, &sap->sa_csa);
+ sap->sa_key = aap->aa_key;
+ sap->sa_oid = aap->aa_oid;
+ sap->sa_seq = aap->aa_seq;
+ sap++;
+ }
+ }
+
+ /*
+ * Send the message to SCSP
+ */
+ rc = atmarp_scsp_out(aip, (char *)smp, len);
+
+ /*
+ * Free the message
+ */
+ if (smp)
+ free(smp);
+ return(rc);
+}
+
+
+/*
+ * Answer a reqeust for information about a cache entry
+ *
+ * Arguments:
+ * aap pointer to entry
+ * state entry's new state
+ *
+ * Returns:
+ * 0 success
+ * errno reason for failure
+ *
+ */
+int
+atmarp_scsp_solicit(aip, smp)
+ Atmarp_intf *aip;
+ Scsp_if_msg *smp;
+{
+ int i, rc = 0;
+ Atmarp *aap;
+ Scsp_if_msg *rsp = (Scsp_if_msg *)0;
+
+ /*
+ * Search the interface's ATMARP cache for an entry with
+ * the specified cache key and origin ID
+ */
+ for (i = 0; i < ATMARP_HASHSIZ; i++) {
+ for (aap = aip->ai_arptbl[i]; aap; aap = aap->aa_next) {
+ if (KEY_EQUAL(&aap->aa_key,
+ &smp->si_sum.ss_key) &&
+ OID_EQUAL(&aap->aa_oid,
+ &smp->si_sum.ss_oid))
+ break;
+ }
+ if (aap)
+ break;
+ }
+
+ /*
+ * Get storage for a Solicit Response
+ */
+ rsp = calloc(1, sizeof(Scsp_if_msg));
+ if (rsp == NULL)
+ atmarp_mem_err("atmarp_scsp_solicit: sizeof(Scsp_if_msg)");
+
+ /*
+ * Fill out the Solicit Rsp
+ */
+ rsp->si_type = SCSP_SOLICIT_RSP;
+ rsp->si_proto = smp->si_proto;
+ rsp->si_tok = smp->si_tok;
+
+ if (aap) {
+ /*
+ * Copy fields from the ATMARP entry to the SCSP
+ * Update Request message
+ */
+ rsp->si_rc = SCSP_RSP_OK;
+ rsp->si_len = sizeof(Scsp_if_msg_hdr) +
+ sizeof(Scsp_atmarp_msg);
+ rsp->si_atmarp.sa_state = SCSP_ASTATE_UPD;
+ rsp->si_atmarp.sa_cpa = aap->aa_dstip;
+ ATM_ADDR_COPY(&aap->aa_dstatm, &rsp->si_atmarp.sa_cha);
+ ATM_ADDR_COPY(&aap->aa_dstatmsub, &rsp->si_atmarp.sa_csa);
+ rsp->si_atmarp.sa_key = aap->aa_key;
+ rsp->si_atmarp.sa_oid = aap->aa_oid;
+ rsp->si_atmarp.sa_seq = aap->aa_seq;
+ } else {
+ /*
+ * Entry not found--set return code
+ */
+ rsp->si_rc = SCSP_RSP_NOT_FOUND;
+ rsp->si_len = smp->si_len;
+ rsp->si_sum = smp->si_sum;
+ }
+
+ /*
+ * Send the message to SCSP
+ */
+ rc = atmarp_scsp_out(aip, (char *)rsp, rsp->si_len);
+ free(rsp);
+ return(rc);
+}
+
+
+/*
+ * Send a cache update to SCSP
+ *
+ * Arguments:
+ * aap pointer to entry
+ * state entry's new state
+ *
+ * Returns:
+ * 0 success
+ * errno reason for failure
+ *
+ */
+int
+atmarp_scsp_update(aap, state)
+ Atmarp *aap;
+ int state;
+{
+ int rc = 0;
+ Atmarp_intf *aip = aap->aa_intf;
+ Scsp_if_msg *smp = (Scsp_if_msg *)0;
+
+ /*
+ * Make sure the connection to SCSP is active
+ */
+ if (aip->ai_state == AI_STATE_NULL) {
+ return(0);
+ }
+
+ /*
+ * Get memory for the cache message
+ */
+ smp = calloc(1, sizeof(Scsp_if_msg));
+ if (smp == NULL)
+ atmarp_mem_err("atmarp_scsp_update: sizeof(Scsp_if_msg)");
+
+ /*
+ * Set header fields in SCSP message
+ */
+ smp->si_type = SCSP_UPDATE_REQ;
+ smp->si_proto = SCSP_PROTO_ATMARP;
+ smp->si_len = sizeof(Scsp_if_msg_hdr) + sizeof(Scsp_atmarp_msg);
+
+ /*
+ * Copy fields from the ATMARP entry to the SCSP
+ * Update Request message
+ */
+ smp->si_atmarp.sa_state = state;
+ smp->si_atmarp.sa_cpa = aap->aa_dstip;
+ ATM_ADDR_COPY(&aap->aa_dstatm, &smp->si_atmarp.sa_cha);
+ ATM_ADDR_COPY(&aap->aa_dstatmsub, &smp->si_atmarp.sa_csa);
+ smp->si_atmarp.sa_key = aap->aa_key;
+ smp->si_atmarp.sa_oid = aap->aa_oid;
+ smp->si_atmarp.sa_seq = aap->aa_seq;
+
+ /*
+ * Send the message to SCSP
+ */
+ rc = atmarp_scsp_out(aap->aa_intf, (char *)smp, smp->si_len);
+
+ free(smp);
+ return(rc);
+}
+
+
+/*
+ * Respond to a Cache Update Indication from SCSP
+ *
+ *
+ * Arguments:
+ * aip pointer to interface control block
+ * smp pointer to message from SCSP
+ *
+ * Returns:
+ * 0 Message processed OK
+ * errno Reason for failure
+ *
+ */
+int
+atmarp_scsp_update_in(aip, smp)
+ Atmarp_intf *aip;
+ Scsp_if_msg *smp;
+{
+ int accept, rc;
+ Atmarp *aap;
+
+ /*
+ * Look up the entry
+ */
+ ATMARP_LOOKUP(aip, smp->si_atmarp.sa_cpa.s_addr, aap);
+
+ /*
+ * Whether we accept the request depends on whether we
+ * already have an entry for it
+ */
+ if (!aap) {
+ /*
+ * We don't have this entry--accept it
+ */
+ accept = 1;
+ } else {
+ /*
+ * We do have an entry for this host--check the
+ * origin ID
+ */
+ if (bcmp(&aip->ai_ip_addr.s_addr,
+ smp->si_atmarp.sa_oid.id,
+ SCSP_ATMARP_ID_LEN) == 0) {
+ /*
+ * The received entry originated with us--
+ * reject it
+ */
+ accept = 0;
+ } else if (bcmp(&aip->ai_ip_addr.s_addr,
+ aap->aa_oid.id,
+ SCSP_ATMARP_ID_LEN) == 0) {
+ /*
+ * We originated the entry we currently have--
+ * only accept the new one if SCSP has higher
+ * priority than the existing entry
+ */
+ accept = aap->aa_origin < UAO_SCSP;
+ } else {
+ /*
+ * Accept the entry if it is more up-to-date
+ * than the existing entry
+ */
+ accept = KEY_EQUAL(&aap->aa_key,
+ &smp->si_atmarp.sa_key) &&
+ OID_EQUAL(&aap->aa_oid,
+ &smp->si_atmarp.sa_oid) &&
+ (aap->aa_seq < smp->si_atmarp.sa_seq);
+ }
+ }
+
+ /*
+ * Add the entry to the cache, if appropriate
+ */
+ if (accept) {
+ if (!aap) {
+ /*
+ * Copy info from SCSP to a new cache entry
+ */
+ aap = calloc(1, sizeof(Atmarp));
+ if (aap == NULL)
+ atmarp_mem_err("atmarp_scsp_update_in: sizeof(Atmarp)");
+
+ aap->aa_dstip = smp->si_atmarp.sa_cpa;
+ aap->aa_dstatm = smp->si_atmarp.sa_cha;
+ aap->aa_dstatmsub = smp->si_atmarp.sa_csa;
+ aap->aa_key = smp->si_atmarp.sa_key;
+ aap->aa_oid = smp->si_atmarp.sa_oid;
+ aap->aa_seq = smp->si_atmarp.sa_seq;
+ aap->aa_intf = aip;
+ aap->aa_origin = UAO_SCSP;
+
+ /*
+ * Add the new entry to our cache
+ */
+ ATMARP_ADD(aip, aap);
+ } else {
+ /*
+ * Update the existing entry
+ */
+ aap->aa_dstip = smp->si_atmarp.sa_cpa;
+ aap->aa_dstatm = smp->si_atmarp.sa_cha;
+ aap->aa_dstatmsub = smp->si_atmarp.sa_csa;
+ aap->aa_key = smp->si_atmarp.sa_key;
+ aap->aa_oid = smp->si_atmarp.sa_oid;
+ aap->aa_seq = smp->si_atmarp.sa_seq;
+ aap->aa_origin = UAO_SCSP;
+ }
+
+ /*
+ * Send the updated entry to the kernel
+ */
+ if (atmarp_update_kernel(aap) == 0)
+ rc = SCSP_RSP_OK;
+ else
+ rc = SCSP_RSP_REJ;
+ } else {
+ rc = SCSP_RSP_REJ;
+ }
+
+ /*
+ * Turn the received message into a response
+ */
+ smp->si_type = SCSP_UPDATE_RSP;
+ smp->si_rc = rc;
+
+ /*
+ * Send the message to SCSP
+ */
+ rc = atmarp_scsp_out(aip, (char *)smp, smp->si_len);
+
+ return(rc);
+}
+
+
+/*
+ * Read and process a message from SCSP
+ *
+ *
+ * Arguments:
+ * aip interface for read
+ *
+ * Returns:
+ * 0 success
+ * errno reason for failure
+ *
+ */
+int
+atmarp_scsp_read(aip)
+ Atmarp_intf *aip;
+{
+ int len, rc = 0;
+ char *buff = (char *)0;
+ Scsp_if_msg *smp;
+ Scsp_if_msg_hdr msg_hdr;
+
+ /*
+ * Read the header of the message from SCSP
+ */
+ len = read(aip->ai_scsp_sock, (char *)&msg_hdr,
+ sizeof(msg_hdr));
+ if (len == -1) {
+ rc = errno;
+ goto read_fail;
+ } else if (len != sizeof(msg_hdr)) {
+ rc = EMSGSIZE;
+ goto read_fail;
+ }
+
+ /*
+ * Get a buffer that will hold the message
+ */
+ buff = malloc(msg_hdr.sh_len);
+ if (buff == NULL)
+ atmarp_mem_err("atmarp_scsp_read: msg_hdr.sh_len");
+ bcopy(&msg_hdr, buff, sizeof(msg_hdr));
+
+ /*
+ * Read the rest of the message, if there is more than
+ * just a header
+ */
+ len = msg_hdr.sh_len - sizeof(msg_hdr);
+ if (len > 0) {
+ len = read(aip->ai_scsp_sock, buff + sizeof(msg_hdr),
+ len);
+ if (len == -1) {
+ rc = errno;
+ goto read_fail;
+ } else if (len != msg_hdr.sh_len - sizeof(msg_hdr)) {
+ rc = EMSGSIZE;
+ goto read_fail;
+ }
+ }
+
+ /*
+ * Handle the message based on its type
+ */
+ smp = (Scsp_if_msg *)buff;
+ switch(smp->si_type) {
+ case SCSP_CFG_RSP:
+ if (smp->si_rc != SCSP_RSP_OK) {
+ rc = EINVAL;
+ goto read_fail;
+ }
+ break;
+ case SCSP_CACHE_IND:
+ rc = atmarp_scsp_cache(aip, smp);
+ break;
+ case SCSP_SOLICIT_IND:
+ rc = atmarp_scsp_solicit(aip, smp);
+ break;
+ case SCSP_UPDATE_IND:
+ rc = atmarp_scsp_update_in(aip, smp);
+ break;
+ case SCSP_UPDATE_RSP:
+ /*
+ * Ignore Update Responses
+ */
+ break;
+ default:
+ atmarp_log(LOG_ERR, "Unexpected SCSP message received");
+ return(EOPNOTSUPP);
+ }
+ free(buff);
+ return(rc);
+
+read_fail:
+ if (buff)
+ free(buff);
+
+ /*
+ * Error on socket to SCSP--close the socket and set the state
+ * so that we know to retry when the cache timer fires.
+ */
+ atmarp_scsp_close(aip);
+
+ return(rc);
+}
+
+
+/*
+ * Send a message to SCSP
+ *
+ *
+ * Arguments:
+ * aip pointer to ATMARP interface to send message on
+ * buff pointer to message buffer
+ * len length of message
+ *
+ * Returns:
+ * 0 message sent
+ * errno reason for failure
+ *
+ */
+int
+atmarp_scsp_out(aip, buff, len)
+ Atmarp_intf *aip;
+ char *buff;
+ int len;
+{
+ int rc;
+
+ /*
+ * Send the message to SCSP
+ */
+ rc = write(aip->ai_scsp_sock, buff, len);
+ if (rc == len)
+ return(0);
+
+ /*
+ * Error on write--close the socket to SCSP, clean up and
+ * set the state so that we know to retry when the cache
+ * timer fires.
+ */
+ atmarp_scsp_close(aip);
+
+ /*
+ * Set the return code
+ */
+ if (rc < 0) {
+ rc = errno;
+ } else {
+ rc = EFAULT;
+ }
+
+ return(rc);
+}
+
+
+/*
+ * Set up a socket and connect to SCSP
+ *
+ * Arguments:
+ * aip pointer to interface block
+ *
+ * Returns:
+ * 0 success, ai_scsp_sock is set
+ * errno reason for failure
+ *
+ *
+ */
+int
+atmarp_scsp_connect(aip)
+ Atmarp_intf *aip;
+{
+ int len, rc, sd;
+ char *sn;
+ Scsp_if_msg cfg_msg;
+
+ static struct sockaddr local_addr = {
+#if (defined(BSD) && (BSD >= 199103))
+ sizeof(struct sockaddr), /* sa_len */
+#endif
+ AF_UNIX, /* sa_family */
+ ATMARP_SOCK_PREFIX /* sa_data */
+ };
+ static struct sockaddr scsp_addr = {
+#if (defined(BSD) && (BSD >= 199103))
+ sizeof(struct sockaddr), /* sa_len */
+#endif
+ AF_UNIX, /* sa_family */
+ SCSPD_SOCK_NAME /* sa_data */
+ };
+
+ /*
+ * Construct a name for the socket
+ */
+ strncpy(local_addr.sa_data, ATMARP_SOCK_PREFIX,
+ sizeof(local_addr.sa_data));
+ (void)strncat(local_addr.sa_data, aip->ai_intf,
+ sizeof(local_addr.sa_data));
+ sn = strdup(local_addr.sa_data);
+ if (!sn)
+ atmarp_mem_err("atmarp_scsp_connect: strdup");
+
+ /*
+ * Clean up any old socket
+ */
+ rc = unlink(sn);
+ if (rc < 0 && errno != ENOENT)
+ return(errno);
+
+ /*
+ * Open a socket to SCSP
+ */
+ sd = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (sd == -1) {
+ free(sn);
+ return(errno);
+ }
+ if (sd > atmarp_max_socket) {
+ atmarp_max_socket = sd;
+ }
+
+ /*
+ * Set non-blocking I/O
+ */
+#ifdef sun
+ rc = fcntl(sd, F_SETFL, FNBIO + FNDELAY);
+#else
+ rc = fcntl(sd, F_SETFL, O_NONBLOCK);
+#endif
+ if (rc == -1) {
+ rc = errno;
+ goto scsp_connect_fail;
+ }
+
+ /*
+ * Bind the local socket address
+ */
+ rc = bind(sd, &local_addr, sizeof(local_addr));
+ if (rc) {
+ rc = errno;
+ goto scsp_connect_fail;
+ }
+
+ /*
+ * Connect to SCSP
+ */
+ rc = connect(sd, &scsp_addr, sizeof(scsp_addr));
+ if (rc) {
+ rc = errno;
+ goto scsp_connect_fail;
+ }
+
+ /*
+ * Save socket information in interface control block
+ */
+ aip->ai_scsp_sock = sd;
+ aip->ai_scsp_sockname = sn;
+ aip->ai_state = AI_STATE_UP;
+
+ /*
+ * Send configuration information to SCSP
+ */
+ bzero(&cfg_msg, sizeof(cfg_msg));
+ cfg_msg.si_type = SCSP_CFG_REQ;
+ cfg_msg.si_proto = SCSP_PROTO_ATMARP;
+ strcpy(cfg_msg.si_cfg.atmarp_netif, aip->ai_intf);
+ len =sizeof(Scsp_if_msg_hdr) + strlen(aip->ai_intf) + 1;
+ cfg_msg.si_len = len;
+ rc = atmarp_scsp_out(aip, (char *)&cfg_msg, len);
+ if (rc) {
+ return(rc);
+ }
+
+ return(0);
+
+scsp_connect_fail:
+ (void)close(sd);
+ aip->ai_scsp_sock = -1;
+ free(sn);
+ aip->ai_scsp_sockname = NULL;
+ aip->ai_state = AI_STATE_NULL;
+ return(rc);
+}
+
+
+/*
+ * Close a socket connection to SCSP
+ *
+ * Arguments:
+ * aip pointer to interface block for connection to be closed
+ *
+ * Returns:
+ * none
+ *
+ *
+ */
+void
+atmarp_scsp_close(aip)
+ Atmarp_intf *aip;
+{
+ /*
+ * Close and unlink the SCSP socket
+ */
+ (void)close(aip->ai_scsp_sock);
+ aip->ai_scsp_sock = -1;
+ (void)unlink(aip->ai_scsp_sockname);
+ free(aip->ai_scsp_sockname);
+ aip->ai_scsp_sockname = NULL;
+
+ aip->ai_state = AI_STATE_NULL;
+
+ return;
+}
+
+
+/*
+ * Disconnect an interface from SCSP
+ *
+ * Arguments:
+ * aip pointer to interface block for connection to be closed
+ *
+ * Returns:
+ * 0 success, ai_scsp_sock is set
+ * errno reason for failure
+ *
+ *
+ */
+int
+atmarp_scsp_disconnect(aip)
+ Atmarp_intf *aip;
+{
+ int i;
+ Atmarp *aap;
+
+ /*
+ * Close and unlink the SCSP socket
+ */
+ atmarp_scsp_close(aip);
+
+ /*
+ * Free the ATMARP cache associated with the interface
+ */
+ for (i = 0; i < ATMARP_HASHSIZ; i++) {
+ for (aap = aip->ai_arptbl[i]; aap; aap = aap->aa_next)
+ free(aap);
+ aip->ai_arptbl[i] = (Atmarp *)0;
+ }
+
+ return(0);
+}
diff --git a/usr.sbin/atm/atmarpd/atmarp_subr.c b/usr.sbin/atm/atmarpd/atmarp_subr.c
new file mode 100644
index 0000000..4015a34
--- /dev/null
+++ b/usr.sbin/atm/atmarpd/atmarp_subr.c
@@ -0,0 +1,954 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * SCSP-ATMARP server interface: misc. subroutines
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netatm/port.h>
+#include <netatm/queue.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sigmgr.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+#include <netatm/uni/unisig_var.h>
+#include <netatm/uni/uniip_var.h>
+
+#include <errno.h>
+#include <libatm.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "../scspd/scsp_msg.h"
+#include "../scspd/scsp_if.h"
+#include "../scspd/scsp_var.h"
+#include "atmarp_var.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+/*
+ * Find an ATMARP interface, given its socket number
+ *
+ * Arguments:
+ * sd socket descriptor
+ *
+ * Returns:
+ * 0 failure
+ * else pointer to interface associated with socket
+ *
+ */
+Atmarp_intf *
+atmarp_find_intf_sock(sd)
+ int sd;
+{
+ Atmarp_intf *aip;
+
+ /*
+ * Loop through the list of interfaces
+ */
+ for (aip = atmarp_intf_head; aip; aip = aip->ai_next) {
+ if (aip->ai_scsp_sock == sd)
+ break;
+ }
+
+ return(aip);
+}
+
+
+/*
+ * Find an ATMARP interface, given its name
+ *
+ * Arguments:
+ * name pointer to network interface name
+ *
+ * Returns:
+ * 0 failure
+ * else pointer to interface associated with name
+ *
+ */
+Atmarp_intf *
+atmarp_find_intf_name(name)
+ char *name;
+{
+ Atmarp_intf *aip;
+
+ /*
+ * Loop through the list of interfaces
+ */
+ for (aip = atmarp_intf_head; aip; aip = aip->ai_next) {
+ if (strcmp(name, aip->ai_intf) == 0)
+ break;
+ }
+
+ return(aip);
+}
+
+
+/*
+ * Clear the mark field on all ATMARP cache entries
+ *
+ * Arguments:
+ * none
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+atmarp_clear_marks()
+
+{
+ int i;
+ Atmarp_intf *aip;
+ Atmarp *aap;
+
+ /*
+ * Loop through list of interfaces
+ */
+ for (aip = atmarp_intf_head; aip; aip = aip->ai_next) {
+ /*
+ * Clear mark on every entry in the interface's cache
+ */
+ for (i = 0; i < ATMARP_HASHSIZ; i++ ) {
+ for (aap = aip->ai_arptbl[i]; aap;
+ aap = aap->aa_next) {
+ aap->aa_mark = 0;
+ }
+ }
+ }
+}
+
+
+/*
+ * Check whether the host system is an ATMARP server for
+ * the LIS associated with a given interface
+ *
+ * Arguments:
+ * aip pointer to an ATMARP interface control block
+ *
+ * Returns:
+ * 1 host is a server
+ * 0 host is not a server
+ *
+ */
+int
+atmarp_is_server(aip)
+ Atmarp_intf *aip;
+{
+ int rc;
+ 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);
+ 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
+ */
+ bzero(&air, sizeof(struct atminfreq));
+ air.air_opcode = AIOCS_INF_NIF;
+ strcpy(air.air_netif_intf, aip->ai_intf);
+ len = do_info_ioctl(&air, sizeof(struct air_netif_rsp));
+ if (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
+ */
+ bzero(&air, sizeof(struct atminfreq));
+ air.air_opcode = AIOCS_INF_INT;
+ strcpy(air.air_int_intf, netif_rsp->anp_phy_intf);
+ len = do_info_ioctl(&air, sizeof(struct air_int_rsp));
+ if (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);
+ free(aap);
+ }
+
+ /*
+ * Update the interface entry
+ */
+ aip->ai_ip_addr = ((struct sockaddr_in *)
+ &netif_rsp->anp_proto_addr)->sin_addr;
+ aip->ai_subnet_mask = subnet_mask.sin_addr;
+ aip->ai_mtu = mtu + 8;
+ ATM_ADDR_COPY(&intf_rsp->anp_addr,
+ &aip->ai_atm_addr);
+ ATM_ADDR_COPY(&intf_rsp->anp_subaddr,
+ &aip->ai_atm_subaddr);
+ anp = (Atm_addr_nsap *)aip->ai_atm_addr.address;
+ if (aip->ai_atm_addr.address_format == T_ATM_ENDSYS_ADDR) {
+ anp->aan_sel = sel;
+ } else if (aip->ai_atm_addr.address_format ==
+ T_ATM_E164_ADDR &&
+ aip->ai_atm_subaddr.address_format ==
+ T_ATM_ENDSYS_ADDR) {
+ anp->aan_sel = sel;
+ }
+
+ /*
+ * Get a new ATMARP cache for the interface
+ */
+ aap = calloc(1, sizeof(Atmarp));
+ if (aap == NULL)
+ atmarp_mem_err("atmarp_if_ready: sizeof(Atmarp)");
+
+ /*
+ * Fill out the entry
+ */
+ aap->aa_dstip = aip->ai_ip_addr;
+ ATM_ADDR_COPY(&intf_rsp->anp_addr, &aap->aa_dstatm);
+ ATM_ADDR_COPY(&intf_rsp->anp_subaddr,
+ &aap->aa_dstatmsub);
+ aap->aa_key.key_len = SCSP_ATMARP_KEY_LEN;
+ scsp_cache_key(&aap->aa_dstatm, &aap->aa_dstip,
+ SCSP_ATMARP_KEY_LEN, aap->aa_key.key);
+ aap->aa_oid.id_len = SCSP_ATMARP_ID_LEN;
+ aap->aa_seq = SCSP_CSA_SEQ_MIN;
+ bcopy(&aap->aa_dstip.s_addr, aap->aa_oid.id, SCSP_ATMARP_ID_LEN);
+ aap->aa_intf = aip;
+ aap->aa_flags = AAF_SERVER;
+ aap->aa_origin = UAO_LOCAL;
+
+ /*
+ * Add the entry to the cache
+ */
+ ATMARP_ADD(aip, aap);
+
+ /*
+ * Free dynamic data
+ */
+ free(netif_rsp);
+ free(intf_rsp);
+ return(1);
+
+if_ready_fail:
+ if (netif_rsp)
+ free(netif_rsp);
+ if (intf_rsp)
+ free(intf_rsp);
+ return(0);
+}
+
+
+/*
+ * Copy an ATMARP cache entry from kernel format into an entry
+ * suitable for our cache
+ *
+ * Arguments:
+ * cp pointer to kernel entry
+ *
+ * Returns:
+ * pointer to a new cache entry
+ * 0 error
+ *
+ */
+Atmarp *
+atmarp_copy_cache_entry(cp)
+ struct air_arp_rsp *cp;
+
+{
+ struct sockaddr_in *ipp;
+ Atmarp_intf *aip;
+ Atmarp *aap;
+
+ /*
+ * Sanity checks
+ */
+ if (!cp)
+ return((Atmarp *)0);
+ aip = atmarp_find_intf_name(cp->aap_intf);
+ if (!aip)
+ return((Atmarp *)0);
+
+ /*
+ * Get a new cache entry
+ */
+ aap = calloc(1, sizeof(Atmarp));
+ if (aap == NULL) {
+ errno = ENOMEM;
+ return(NULL);
+ }
+ aap->aa_intf = aip;
+
+ /*
+ * Copy fields from the kernel entry to the new entry
+ */
+ ipp = (struct sockaddr_in *)&cp->aap_arp_addr;
+ bcopy(&ipp->sin_addr.s_addr, &aap->aa_dstip.s_addr,
+ sizeof(aap->aa_dstip.s_addr));
+ ATM_ADDR_COPY(&cp->aap_addr, &aap->aa_dstatm);
+ ATM_ADDR_COPY(&cp->aap_subaddr, &aap->aa_dstatmsub);
+ if (cp->aap_origin == UAO_PERM)
+ aap->aa_flags |= AAF_PERM;
+ aap->aa_origin = cp->aap_origin;
+
+ /*
+ * Set up fields for SCSP
+ */
+ aap->aa_key.key_len = SCSP_ATMARP_KEY_LEN;
+ scsp_cache_key(&cp->aap_addr, &aap->aa_dstip,
+ SCSP_ATMARP_KEY_LEN, (char *)aap->aa_key.key);
+ aap->aa_oid.id_len = SCSP_ATMARP_ID_LEN;
+ bcopy(&aip->ai_ip_addr.s_addr, aap->aa_oid.id, SCSP_ATMARP_ID_LEN);
+ aap->aa_seq = SCSP_CSA_SEQ_MIN;
+
+ return(aap);
+}
+
+
+/*
+ * Send an updated ATMARP cache entry to the kernel
+ *
+ * Arguments:
+ * aap pointer to updated entry
+ *
+ * Returns:
+ * 0 success
+ * errno reason for failure
+ *
+ */
+int
+atmarp_update_kernel(aap)
+ Atmarp *aap;
+{
+ int rc = 0, sd;
+ struct atmaddreq aar;
+ struct sockaddr_in *ipp;
+
+ /*
+ * Build ioctl request
+ */
+ bzero(&aar, sizeof(aar));
+ aar.aar_opcode = AIOCS_ADD_ARP;
+ strncpy(aar.aar_arp_intf, aap->aa_intf->ai_intf,
+ sizeof(aar.aar_arp_intf));
+ aar.aar_arp_origin = UAO_SCSP;
+ ATM_ADDR_COPY(&aap->aa_dstatm, &aar.aar_arp_addr);
+ ipp = (struct sockaddr_in *)&aar.aar_arp_dst;
+ ipp->sin_family = AF_INET;
+#if (defined(BSD) && (BSD >= 199103))
+ ipp->sin_len = sizeof(struct sockaddr_in);
+#endif
+ ipp->sin_addr = aap->aa_dstip;
+
+ /*
+ * Pass the new mapping to the kernel
+ */
+ sd = socket(AF_ATM, SOCK_DGRAM, 0);
+ if (sd < 0) {
+ return(errno);
+ }
+ if (ioctl(sd, AIOCADD, (caddr_t)&aar) < 0) {
+ rc = errno;
+ }
+
+ (void)close(sd);
+ return(rc);
+}
+
+
+/*
+ * Read the ATMARP cache from the kernel and scan it, processing
+ * all entries
+ *
+ * Arguments:
+ * none
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+atmarp_get_updated_cache()
+{
+ int i, 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
+ */
+ free(air.air_buf_addr);
+}
+
+
+/*
+ * Process an ATMARP cache entry from the kernel. If we already
+ * have the entry in our local cache, update it, otherwise, add
+ * it. In either case, mark our local copy so we know it's still
+ * in the kernel's cache.
+ *
+ * Arguments:
+ * cp pointer to kernel's cache entry
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+atmarp_process_cache_entry(cp)
+ struct air_arp_rsp *cp;
+
+{
+ int rc;
+ struct sockaddr_in *ipp = (struct sockaddr_in *)&cp->aap_arp_addr;
+ Atmarp_intf *aip;
+ Atmarp *aap;
+
+ /*
+ * See whether the entry is for an interface that's
+ * both configured and up
+ */
+ aip = atmarp_find_intf_name(cp->aap_intf);
+ if (!aip || aip->ai_state != AI_STATE_UP)
+ return;
+
+ /*
+ * Make sure the entry is valid
+ */
+ if (!(cp->aap_flags & ARPF_VALID))
+ return;
+
+ /*
+ * See whether we have the entry in our cache already
+ */
+ ATMARP_LOOKUP(aip, ipp->sin_addr.s_addr, aap);
+ if (aap) {
+ /*
+ * We already have this in our cache--update it
+ */
+ aap->aa_mark = 1;
+ if ((cp->aap_flags & ARPF_REFRESH) &&
+ cp->aap_origin != UAO_SCSP) {
+ aap->aa_seq++;
+ rc = atmarp_scsp_update(aap, SCSP_ASTATE_UPD);
+ }
+ } else {
+ /*
+ * This is a new entry--add it to the cache
+ */
+ aap = atmarp_copy_cache_entry(cp);
+ if (!aap)
+ return;
+ ATMARP_ADD(aip, aap);
+ aap->aa_mark = 1;
+ rc = atmarp_scsp_update(aap, SCSP_ASTATE_NEW);
+ }
+
+ return;
+}
+
+
+/*
+ * Print an SCSP ID
+ *
+ * Arguments:
+ * df pointer to a FILE for the dump
+ * ip pointer to the SCSP ID to print
+ *
+ * Returns:
+ * None
+ *
+ */
+static void
+print_scsp_id(df, ip)
+ FILE *df;
+ Scsp_id *ip;
+{
+ int i;
+
+ fprintf(df, "\t next: %p\n", ip->next);
+ fprintf(df, "\t id_len: %d\n", ip->id_len);
+ fprintf(df, "\t id: 0x");
+ for (i = 0; i < ip->id_len; i++) {
+ fprintf(df, "%0x ", ip->id[i]);
+ }
+ fprintf(df, "\n");
+}
+
+
+/*
+ * Print an SCSP cacke key
+ *
+ * Arguments:
+ * df pointer to a FILE for the dump
+ * cp pointer to the cacke key to print
+ *
+ * Returns:
+ * None
+ *
+ */
+static void
+print_scsp_cache_key(df, cp)
+ FILE *df;
+ Scsp_ckey *cp;
+{
+ int i;
+
+ fprintf(df, "\t key_len: %d\n", cp->key_len);
+ fprintf(df, "\t key: 0x");
+ for (i = 0; i < cp->key_len; i++) {
+ fprintf(df, "%0x ", cp->key[i]);
+ }
+ fprintf(df, "\n");
+}
+
+
+/*
+ * Print an ATMARP interface entry
+ *
+ * Arguments:
+ * df pointer to a FILE for the dump
+ * aip pointer to interface entry
+ *
+ * Returns:
+ * None
+ *
+ */
+void
+print_atmarp_intf(df, aip)
+ FILE *df;
+ Atmarp_intf *aip;
+{
+ if (!aip) {
+ fprintf(df, "print_atmarp_intf: NULL interface entry address\n");
+ return;
+ }
+
+ fprintf(df, "ATMARP network interface entry at %p\n", aip);
+ fprintf(df, "\tai_next: %p\n", aip->ai_next);
+ fprintf(df, "\tai_intf: %s\n", aip->ai_intf);
+ fprintf(df, "\tai_ip_addr: %s\n",
+ format_ip_addr(&aip->ai_ip_addr));
+ fprintf(df, "\tai_subnet_mask: %s\n",
+ inet_ntoa(aip->ai_subnet_mask));
+ fprintf(df, "\tai_mtu: %d\n", aip->ai_mtu);
+ fprintf(df, "\tai_atm_addr: %s\n",
+ format_atm_addr(&aip->ai_atm_addr));
+ fprintf(df, "\tai_atm_subaddr: %s\n",
+ format_atm_addr(&aip->ai_atm_subaddr));
+ fprintf(df, "\tai_scsp_sock: %d\n", aip->ai_scsp_sock);
+ fprintf(df, "\tai_scsp_sockname: %s\n", aip->ai_scsp_sockname);
+ fprintf(df, "\tai_state: %d\n", aip->ai_state);
+ fprintf(df, "\tai_mark: %d\n", aip->ai_mark);
+}
+
+
+/*
+ * Print an ATMARP cache entry
+ *
+ * Arguments:
+ * df pointer to a FILE for the dump
+ * aap pointer to cache entry
+ *
+ * Returns:
+ * None
+ *
+ */
+void
+print_atmarp_cache(df, aap)
+ FILE *df;
+ Atmarp *aap;
+{
+ if (!aap) {
+ fprintf(df, "print_atmarp_cache: NULL ATMARP entry address\n");
+ return;
+ }
+
+ fprintf(df, "ATMARP entry at %p\n", aap);
+ fprintf(df, "\taa_next: %p\n", aap->aa_next);
+ fprintf(df, "\taa_dstip: %s\n", inet_ntoa(aap->aa_dstip));
+ fprintf(df, "\taa_dstatm: %s\n",
+ format_atm_addr(&aap->aa_dstatm));
+ fprintf(df, "\taa_dstatmsub: %s\n",
+ format_atm_addr(&aap->aa_dstatmsub));
+ fprintf(df, "\taa_key:\n");
+ print_scsp_cache_key(df, &aap->aa_key);
+ fprintf(df, "\taa_oid:\n");
+ print_scsp_id(df, &aap->aa_oid);
+ fprintf(df, "\taa_seq: %ld (0x%lx)\n", aap->aa_seq,
+ aap->aa_seq);
+ fprintf(df, "\taa_intf: %p\n", aap->aa_intf);
+ fprintf(df, "\taa_flags: ");
+ if (aap->aa_flags & AAF_PERM)
+ fprintf(df, "Permanent ");
+ if (aap->aa_flags & AAF_SERVER)
+ fprintf(df, "Server ");
+ fprintf(df, "\n");
+ fprintf(df, "\taa_origin: %d\n", aap->aa_origin);
+ fprintf(df, "\taa_mark: %d\n", aap->aa_mark);
+}
+
+
+/*
+ * Print the entire ATMARP cache
+ *
+ * Arguments:
+ * df pointer to a FILE for the dump
+ * aip pointer to interface whose cache is to be printed
+ *
+ * Returns:
+ * None
+ *
+ */
+void
+dump_atmarp_cache(df, aip)
+ FILE *df;
+ Atmarp_intf *aip;
+{
+ int i;
+ Atmarp *aap;
+
+ if (!aip) {
+ fprintf(df, "dump_atmarp_cache: NULL interface address\n");
+ return;
+ }
+
+ fprintf(df, "ATMARP cache for interface %s\n", aip->ai_intf);
+ for (i=0; i<ATMARP_HASHSIZ; i++) {
+ for (aap=aip->ai_arptbl[i]; aap; aap=aap->aa_next) {
+ print_atmarp_cache(df, aap);
+ }
+ }
+}
+
+
+#ifdef NOTDEF
+/*
+ * Print an ATMARP super-LIS entry
+ *
+ * Arguments:
+ * df pointer to a FILE for the dump
+ * asp pointer to super-LIS entry to be printed
+ *
+ * Returns:
+ * None
+ *
+ */
+void
+print_atmarp_slis(df, asp)
+ FILE *df;
+ Atmarp_slis *asp;
+{
+ Atmarp_intf **aipp;
+
+ if (!asp) {
+ fprintf(df, "print_atmarp_slis: NULL SLIS address\n");
+ return;
+ }
+
+ fprintf(df, "SLIS entry at 0x%0x\n", (u_long)asp);
+ fprintf(df, "\tas_next: 0x%0x\n", (u_long)asp->as_next);
+ fprintf(df, "\tas_name: %s\n", asp->as_name);
+ fprintf(df, "\tas_cnt: %d\n", asp->as_cnt);
+ for (aipp = &asp->as_intfs; *aipp; aipp++) {
+ fprintf(df, "\t%s (%s)\n", (*aipp)->ai_name,
+ (*aipp)->ai_intf);
+ }
+}
+#endif
+
+
+/*
+ * Dump ATMARPD information
+ *
+ * Called as the result of a SIGINT signal.
+ *
+ * Arguments:
+ * sig signal number
+ *
+ * Returns:
+ * None
+ *
+ */
+void
+atmarp_sigint(sig)
+ int sig;
+{
+ Atmarp_intf *aip;
+ FILE *df;
+ char fname[64];
+ static int dump_no = 0;
+
+ /*
+ * Build a file name
+ */
+ bzero(&fname, sizeof(fname));
+ sprintf(fname, "/tmp/atmarpd.%d.%03d.out", getpid(), dump_no++);
+
+ /*
+ * Open the output file
+ */
+ df = fopen(fname, "w");
+ if (df == (FILE *)0)
+ return;
+
+ /*
+ * Dump the interface control blocks and
+ * associated ATMARP caches
+ */
+ for (aip = atmarp_intf_head; aip; aip = aip->ai_next) {
+ print_atmarp_intf(df, aip);
+ fprintf(df, "\n");
+ dump_atmarp_cache(df, aip);
+ fprintf(df, "\n");
+ }
+
+ /*
+ * Close the output file
+ */
+ (void)fclose(df);
+}
diff --git a/usr.sbin/atm/atmarpd/atmarp_timer.c b/usr.sbin/atm/atmarpd/atmarp_timer.c
new file mode 100644
index 0000000..c1c6cd9
--- /dev/null
+++ b/usr.sbin/atm/atmarpd/atmarp_timer.c
@@ -0,0 +1,229 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * SCSP-ATMARP server interface: timer routines
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netatm/port.h>
+#include <netatm/queue.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+
+#include <errno.h>
+#include <libatm.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "../scspd/scsp_msg.h"
+#include "../scspd/scsp_if.h"
+#include "../scspd/scsp_var.h"
+#include "atmarp_var.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+/*
+ * Cache update timeout processing
+ *
+ * When the cache update timer fires, we read the cache from the
+ * kernel, update the internal cache, and restart the timer.
+ *
+ * Arguments:
+ * tp pointer to a HARP timer block
+ *
+ * Returns:
+ * None
+ *
+ */
+void
+atmarp_cache_timeout(tp)
+ Harp_timer *tp;
+{
+ Atmarp_intf *aip;
+
+ /*
+ * Verify the status of all configured interfaces
+ */
+ for (aip = atmarp_intf_head; aip; aip = aip->ai_next) {
+ if (atmarp_if_ready(aip)) {
+ /*
+ * The interface is up but we don't have
+ * a connection to SCSP--make a connection
+ */
+ if (aip->ai_state == AI_STATE_NULL)
+ (void)atmarp_scsp_connect(aip);
+ } else {
+ /*
+ * The interface is down--disconnect from SCSP
+ */
+ if (aip->ai_state != AI_STATE_NULL)
+ (void)atmarp_scsp_disconnect(aip);
+ }
+ }
+
+ /*
+ * Read the cache from the kernel
+ */
+ atmarp_get_updated_cache();
+
+ /*
+ * Restart the cache update timer
+ */
+ HARP_TIMER(tp, ATMARP_CACHE_INTERVAL, atmarp_cache_timeout);
+}
+
+
+/*
+ * Permanent cache entry timer processing
+ *
+ * Permanent cache entries (entries that are administratively added
+ * and the entry for the server itself) don't ever get refreshed, so
+ * we broadcast updates for them every 10 minutes so they won't get
+ * deleted from the remote servers' caches
+ *
+ * Arguments:
+ * tp pointer to a HARP timer block
+ *
+ * Returns:
+ * None
+ *
+ */
+void
+atmarp_perm_timeout(tp)
+ Harp_timer *tp;
+{
+ int i, rc;
+ Atmarp_intf *aip;
+ Atmarp *aap;
+
+ /*
+ * Loop through all interfaces
+ */
+ for (aip = atmarp_intf_head; aip; aip = aip->ai_next) {
+ /*
+ * Loop through the interface's cache
+ */
+ for (i = 0; i < ATMARP_HASHSIZ; i++) {
+ for (aap = aip->ai_arptbl[i]; aap;
+ aap = aap->aa_next) {
+ /*
+ * Find and update permanent entries
+ */
+ if ((aap->aa_flags & (AAF_PERM |
+ AAF_SERVER)) != 0) {
+ aap->aa_seq++;
+ rc = atmarp_scsp_update(aap,
+ SCSP_ASTATE_UPD);
+ }
+ }
+ }
+ }
+
+ /*
+ * Restart the permanent cache entry timer
+ */
+ HARP_TIMER(tp, ATMARP_PERM_INTERVAL, atmarp_perm_timeout);
+}
+
+
+/*
+ * Keepalive timeout processing
+ *
+ * When the keepalive timer fires, we send a NOP to SCSP. This
+ * will help us detect a broken connection.
+ *
+ * Arguments:
+ * tp pointer to a HARP timer block
+ *
+ * Returns:
+ * None
+ *
+ */
+void
+atmarp_keepalive_timeout(tp)
+ Harp_timer *tp;
+{
+ Atmarp_intf *aip;
+ Scsp_if_msg *msg;
+
+ /*
+ * Back off to start of DCS entry
+ */
+ aip = (Atmarp_intf *) ((caddr_t)tp -
+ (int)(&((Atmarp_intf *)0)->ai_keepalive_t));
+
+ /*
+ * Get a message buffer
+ *
+ * XXX arr: Previously, the check on the returned value from
+ * the memory allocation routine was checked and _nothing_
+ * resulted from the check (which would cause problems since
+ * the bzero() of NULL is not fun). At the moment, I am having
+ * it soley return -- this should be reviewed again soon.
+ */
+ msg = calloc(1, sizeof(Scsp_if_msg));
+ if (msg == NULL)
+ return;
+
+ /*
+ * Build a NOP message
+ */
+ msg->si_type = SCSP_NOP_REQ;
+ msg->si_proto = SCSP_PROTO_ATMARP;
+ msg->si_len = sizeof(Scsp_if_msg_hdr);
+
+ /*
+ * Send the message to SCSP
+ */
+ (void)atmarp_scsp_out(aip, (char *)msg, msg->si_len);
+ free(msg);
+
+ /*
+ * Restart the keepalive timer
+ */
+ HARP_TIMER(&aip->ai_keepalive_t, ATMARP_KEEPALIVE_INTERVAL,
+ atmarp_keepalive_timeout);
+}
diff --git a/usr.sbin/atm/atmarpd/atmarp_var.h b/usr.sbin/atm/atmarpd/atmarp_var.h
new file mode 100644
index 0000000..f11d982
--- /dev/null
+++ b/usr.sbin/atm/atmarpd/atmarp_var.h
@@ -0,0 +1,225 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * SCSP-ATMARP server interface: control blocks
+ *
+ */
+
+#ifndef _ATMARP_ATMARP_VAR_H
+#define _ATMARP_ATMARP_VAR_H
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+/*
+ * Operational constants
+ */
+#define ATMARP_DIR "/tmp"
+#define ATMARP_SOCK_PREFIX "AA_"
+#define ATMARP_CACHE_INTERVAL 50
+#define ATMARP_PERM_INTERVAL 600
+#define ATMARP_KEEPALIVE_INTERVAL 5
+
+
+/*
+ * Macros for manipulating ATMARP tables and entries
+ */
+#define ATMARP_HASHSIZ 19 /* Hash table size */
+
+#define ATMARP_HASH(ip) ((u_long)(ip) % ATMARP_HASHSIZ)
+
+#define ATMARP_ADD(ai, aa) \
+{ \
+ Atmarp **h; \
+ h = &ai->ai_arptbl[ATMARP_HASH((aa)->aa_dstip.s_addr)]; \
+ LINK2TAIL((aa), Atmarp, *h, aa_next); \
+}
+
+#define ATMARP_DELETE(ai, aa) \
+{ \
+ Atmarp **h; \
+ h = &ai->ai_arptbl[ATMARP_HASH((aa)->aa_dstip.s_addr)]; \
+ UNLINK((aa), Atmarp, *h, aa_next); \
+}
+
+#define ATMARP_LOOKUP(ai, ip, aa) \
+{ \
+ for ((aa) = (ai)->ai_arptbl[ATMARP_HASH(ip)]; \
+ (aa); (aa) = (aa)->aa_next) { \
+ if ((aa)->aa_dstip.s_addr == (ip)) \
+ break; \
+ } \
+}
+
+
+/*
+ * Macro to compare originator ID structures
+ */
+#define OID_EQUAL(id1, id2) \
+ (((id1)->id_len == (id2)->id_len) && \
+ (bcmp((caddr_t)(id1)->id, \
+ (caddr_t)(id2)->id, \
+ (id1)->id_len) == 0))
+
+#define KEY_EQUAL(key1, key2) \
+ (((key1)->key_len == (key2)->key_len) && \
+ (bcmp((caddr_t)(key1)->key, \
+ (caddr_t)(key2)->key, \
+ (key1)->key_len) == 0))
+
+
+/*
+ * Interface entry for ATMARP SCSP interface daemon
+ */
+struct atmarp_intf {
+ struct atmarp_intf *ai_next; /* Next chained I/F */
+ char ai_intf[IFNAMSIZ]; /* Network I/F name */
+ struct in_addr ai_ip_addr; /* IP address */
+ struct in_addr ai_subnet_mask; /* Subnet mask */
+ int ai_mtu; /* IP MTU */
+ Atm_addr ai_atm_addr; /* ATM address */
+ Atm_addr ai_atm_subaddr; /* ATM subaddress */
+ int ai_scsp_sock; /* Socket to SCSP */
+ Harp_timer ai_keepalive_t; /* Keepalive timer */
+ char *ai_scsp_sockname; /* Socket name */
+ u_char ai_state; /* Interface state */
+ u_char ai_mark;
+ struct atmarp *ai_arptbl[ATMARP_HASHSIZ]; /* ARP cache */
+};
+typedef struct atmarp_intf Atmarp_intf;
+
+#define AI_STATE_NULL 0
+#define AI_STATE_UP 1
+
+
+/*
+ * Super-LIS control block for ATMARP server daemon
+ */
+struct atmarp_slis {
+ struct atmarp_slis *as_next; /* Next super-LIS */
+ char *as_name; /* Name of super-LIS */
+ int as_cnt; /* LIS count */
+ Atmarp_intf *as_intfs; /* List of intfs */
+};
+typedef struct atmarp_slis Atmarp_slis;
+
+
+/*
+ * ATMARP cache entry format
+ */
+struct atmarp {
+ struct atmarp *aa_next; /* Hash chain link */
+ struct in_addr aa_dstip; /* Destination IP addr */
+ Atm_addr aa_dstatm; /* Destination ATM addr */
+ Atm_addr aa_dstatmsub; /* Destination ATM subaddr */
+ struct scsp_ckey aa_key; /* SCSP cache key */
+ struct scsp_id aa_oid; /* SCSP originator ID */
+ long aa_seq; /* SCSP sequence no. */
+ Atmarp_intf *aa_intf; /* Interface for entry */
+ u_char aa_flags; /* Flags (see below) */
+ u_char aa_origin; /* Entry origin */
+ char aa_mark; /* Mark */
+};
+typedef struct atmarp Atmarp;
+
+/*
+ * ATMARP Entry Flags
+ */
+#define AAF_PERM 0x01 /* Entry is permanent */
+#define AAF_SERVER 0x02 /* Entry is for the server */
+
+
+/*
+ * Global variables
+ */
+extern char *prog;
+extern int atmarp_debug_mode;
+extern int atmarp_max_socket;
+extern Atmarp_intf *atmarp_intf_head;
+extern Atmarp_slis *atmarp_slis_head;
+extern FILE *atmarp_log_file;
+
+
+/*
+ * Function definitions
+ */
+
+/* atmarp_config.c */
+extern int atmarp_cfg_netif __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 int atmarp_update_kernel __P((Atmarp *));
+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..49aaebc
--- /dev/null
+++ b/usr.sbin/atm/atmarpd/atmarpd.8
@@ -0,0 +1,169 @@
+.\"
+.\" ===================================
+.\" HARP | Host ATM Research Platform
+.\" ===================================
+.\"
+.\"
+.\" This Host ATM Research Platform ("HARP") file (the "Software") is
+.\" made available by Network Computing Services, Inc. ("NetworkCS")
+.\" "AS IS". NetworkCS does not provide maintenance, improvements or
+.\" support of any kind.
+.\"
+.\" NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+.\" INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+.\" AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+.\" SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+.\" In no event shall NetworkCS be responsible for any damages, including
+.\" but not limited to consequential damages, arising from or relating to
+.\" any use of the Software or related support.
+.\"
+.\" Copyright 1994-1998 Network Computing Services, Inc.
+.\"
+.\" Copies of this Software may be made, however, the above copyright
+.\" notice must be reproduced on all copies.
+.\"
+.\" @(#) $FreeBSD$
+.\"
+.\"
+.Dd August 4, 1998
+.Dt ATMARPD 8
+.Os
+.Sh NAME
+.Nm atmarpd
+.Nd "ATMARP/SCSP interface daemon"
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Op Fl l Aq Ar log_file
+.Aq Ar net_intf
+.Ar ...
+.Sh DESCRIPTION
+.Nm Atmarpd
+provides an interface between the ATMARP server in the
+kernel and the SCSP daemon for the Host ATM Research Platform
+(HARP) networking software.
+.Nm Atmarpd
+reads the ATMARP cache from the kernel periodically
+and passes any updated entries to
+.Xr scspd 8
+so they will be
+propagated to remote servers.
+It also accepts updated entries that remote servers have sent to
+.Xr scspd 8
+and, if they are
+new or more up to date than current entries, installs them
+in the kernel's ATMARP cache.
+Both
+.Nm
+and
+.Xr scspd 8
+must be running before any ATMARP cache synchronization can take place.
+.Pp
+When
+.Nm
+starts, it parses its command line and puts
+itself into the background.
+.Pp
+The command-line options are:
+.Bl -tag -width "-l <log_file>"
+.It Fl l Aq Ar log_file
+Specifies that
+.Nm
+is to write log messages to the
+file named
+.Aq Ar log_file
+rather than to the system log.
+.It Fl d
+Specifies that
+.Nm
+is to be run in debug mode.
+In debug mode,
+.Nm
+is not put into the background.
+Log messages are written to standard output instead of to
+the log file.
+.It Aq Ar net_intf
+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,
+.Nm
+will provide an interface to
+.Xr scspd 8
+for the servers on all the
+specified interfaces.
+.El
+.Sh SIGNAL PROCESSING
+The following signals can be used to control
+.Nm :
+.Bl -tag -width indent
+.It Dv SIGINT
+Dump debugging information to a file.
+When it receives a
+.Dv SIGINT
+signal,
+.Nm
+dumps a summary of
+its control blocks to a text file (see
+.Sx FILES ) .
+.El
+.Sh FILES
+.Bl -tag -width indent
+.It Xo
+.Sm off
+.Pa /tmp/atmarpd.
+.Aq Ar pid
+.Pa \&.
+.Aq Ar seq
+.Pa .out
+.Sm on
+.Xc
+Debugging information dump file name.
+.Nm Atmarpd
+writes a summary of its control blocks to this file
+when it receives a
+.Dv SIGINT
+signal.
+.Aq Ar pid
+is the process ID of the daemon and
+.Aq Ar seq
+is a sequence
+number which is incremented every time a dump is taken.
+.El
+.Sh SEE ALSO
+.Xr atm 8 ,
+.Xr scspd 8
+.Rs
+.%T "Classical IP and ARP over ATM"
+.%O "RFC 1577"
+.Re
+.Rs
+.%T "Classical IP and ARP over ATM"
+.%O "RFC 2225"
+.Re
+.Rs
+.%T "Server Cache Synchronization Protocol (SCSP)"
+.%O "RFC 2334"
+.Re
+.Rs
+.%T "A Distributed ATMARP Service Using SCSP"
+.%O "draft\-ietf\-ion\-scsp\-atmarpd\-00.txt"
+.Re
+.Sh BUGS
+Results are unpredictable if multiple instantiations of
+.Nm
+are run simultaneously for a given network interface.
+.Pp
+Please report any bugs to
+.Aq harp\-bugs@magic.net .
+.Sh COPYRIGHT
+Copyright (c) 1994-1998, Network Computing Services, Inc.
+.Sh AUTHORS
+.An John Cavanaugh ,
+Network Computing Services, Inc.
+.An Mike Spengler ,
+Network Computing Services, Inc.
+.An Joe Thomas ,
+Network Computing Services, Inc.
+.Sh ACKNOWLEDGMENTS
+This software was developed with the support of the Defense
+Advanced Research Projects Agency (DARPA).
diff --git a/usr.sbin/atm/atmarpd/atmarpd.c b/usr.sbin/atm/atmarpd/atmarpd.c
new file mode 100644
index 0000000..2990665
--- /dev/null
+++ b/usr.sbin/atm/atmarpd/atmarpd.c
@@ -0,0 +1,411 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * SCSP-ATMARP server interface: main line code
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/ttycom.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netatm/queue.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <libatm.h>
+#include <paths.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "../scspd/scsp_msg.h"
+#include "../scspd/scsp_if.h"
+#include "../scspd/scsp_var.h"
+#include "atmarp_var.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+/*
+ * Global variables
+ */
+char *prog;
+int atmarp_debug_mode = 0;
+int atmarp_max_socket = 0;
+Atmarp_intf *atmarp_intf_head = (Atmarp_intf *)0;
+Atmarp_slis *atmarp_slis_head = (Atmarp_slis *)0;
+FILE *atmarp_log_file = (FILE *)0;
+char *atmarp_log_file_name = (char *)0;
+Harp_timer cache_timer, perm_timer;
+
+
+/*
+ * Print a usage message
+ *
+ * Arguments:
+ * none
+ *
+ * Returns:
+ * exits, does not return
+ *
+ */
+void
+usage()
+{
+ fprintf(stderr, "usage: %s [-d] [-l <log_file>] <net_intf> ...\n", prog);
+ exit(1);
+}
+
+
+/*
+ * Process command line parameters
+ *
+ * Arguments:
+ * argc number of command-line arguments
+ * argv list of pointers to command-line arguments
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+initialize(argc, argv)
+ int argc;
+ char *argv[];
+
+{
+ int i, rc;
+
+ /*
+ * Save program name, ignoring any path components
+ */
+ if ((prog = (char *)strrchr(argv[0], '/')) != NULL)
+ prog++;
+ else
+ prog = argv[0];
+
+ /*
+ * Make sure we're being invoked by the super user
+ */
+ i = getuid();
+ if (i != 0) {
+ fprintf(stderr, "%s: You must be root to run this program\n",
+ prog);
+ exit(1);
+ }
+
+ /*
+ * Scan arguments, checking for options
+ */
+ for (i = 1; i < argc; i++) {
+ if (argv[i][0] == '-') {
+ if (strcmp(argv[i], "-d") == 0) {
+ atmarp_debug_mode = TRUE;
+ } else if (strcmp(argv[i], "-l") == 0) {
+ i++;
+ if (i >= argc) {
+ fprintf(stderr, "%s: Log file name missing\n",
+ prog);
+ exit(1);
+ }
+ atmarp_log_file_name = argv[i];
+ } else {
+ fprintf(stderr, "%s: Unrecognized option \"%s\"\n",
+ prog, argv[i]);
+ exit(1);
+ }
+ } else {
+ /*
+ * Parameter is a network interface name
+ */
+ rc = atmarp_cfg_netif(argv[i]);
+ if (rc) {
+ fprintf(stderr, "%s: Error configuring network interface %s\n",
+ prog, argv[i]);
+ exit(1);
+ }
+ }
+ }
+
+ /*
+ * Make sure we had at least one interface configured
+ */
+ if (!atmarp_intf_head) {
+ usage();
+ }
+}
+
+
+/*
+ * Daemon housekeeping
+ *
+ * Arguments:
+ * None
+ *
+ * Returns:
+ * None
+ *
+ */
+static void
+start_daemon()
+
+{
+ int dpid, fd, file_count, rc;
+
+ /*
+ * Ignore selected signals
+ */
+#ifdef SIGTTOU
+ signal(SIGTTOU, SIG_IGN);
+#endif
+#ifdef SIGTTIN
+ signal(SIGTTIN, SIG_IGN);
+#endif
+#ifdef SIGTSTP
+ signal(SIGTSTP, SIG_IGN);
+#endif
+#ifdef SIGPIPE
+ signal(SIGPIPE, SIG_IGN);
+#endif
+
+ /*
+ * Skip putting things into the background if we're
+ * in debugging mode
+ */
+ if (atmarp_debug_mode)
+ goto daemon_bypass;
+
+ /*
+ * Set up syslog for error logging
+ */
+ if (!atmarp_log_file) {
+ openlog(prog, LOG_PID | LOG_CONS, LOG_DAEMON);
+ }
+
+ /*
+ * Put the daemon into the background
+ */
+ dpid = fork();
+ if (dpid < 0) {
+ atmarp_log(LOG_ERR, "fork failed");
+ exit(1);
+ }
+ if (dpid > 0) {
+ /*
+ * This is the parent process--just exit and let
+ * the daughter do all the work
+ */
+ exit(0);
+ }
+
+ /*
+ * Disassociate from any controlling terminal
+ */
+ rc = setpgrp(0, getpid());
+ if (rc < 0) {
+ atmarp_log(LOG_ERR, "can't change process group");
+ exit(1);
+ }
+ fd = open(_PATH_TTY, O_RDWR);
+ if (fd >= 0) {
+ ioctl(fd, TIOCNOTTY, (char *)0);
+ close(fd);
+ }
+
+ /*
+ * Close all open file descriptors
+ */
+ file_count = getdtablesize();
+ for (fd=0; fd<file_count; fd++) {
+ close(fd);
+ }
+
+ /*
+ * Open log file, if specified
+ */
+ if (atmarp_log_file_name) {
+ atmarp_log_file = fopen(atmarp_log_file_name, "a");
+ if (!atmarp_log_file) {
+ atmarp_log(LOG_ERR, "%s: Can't open log file \'%s\'\n",
+ prog, atmarp_log_file_name);
+ exit(1);
+ }
+ }
+
+ /*
+ * Set up and start interval timer
+ */
+daemon_bypass:
+ init_timer();
+
+ /*
+ * Move to a safe directory
+ */
+ chdir(ATMARP_DIR);
+
+ /*
+ * Clear the file mode creation mask
+ */
+ umask(0);
+
+
+ /*
+ * Set up signal handlers
+ */
+ if (signal(SIGINT, atmarp_sigint) == SIG_ERR) {
+ atmarp_log(LOG_ERR, "SIGINT signal setup failed");
+ exit(1);
+ }
+}
+
+
+/*
+ * Main line code
+ *
+ * The ATMARP server resides in the kernel, while SCSP runs as a daemon
+ * in user space. This program exists to provide an interface between
+ * the two. It periodically polls the kernel to get the ATMARP cache
+ * and passes information about new entries to SCSP. It also accepts
+ * new information from SCSP and passes it to the kernel.
+ *
+ * Arguments:
+ * argc number of command-line arguments
+ * argv list of pointers to command-line arguments
+ *
+ * Returns:
+ * none
+ *
+ */
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+
+{
+ int i, rc;
+ fd_set read_set, write_set, except_set;
+ Atmarp_intf *aip;
+
+ /*
+ * Process command line arguments
+ */
+ initialize(argc, argv);
+
+ /*
+ * Put the daemon into the background
+ */
+ start_daemon();
+
+ /*
+ * Start the cache update timer
+ */
+ HARP_TIMER(&cache_timer, ATMARP_CACHE_INTERVAL,
+ atmarp_cache_timeout);
+
+ /*
+ * Start the permanent cache entry timer
+ */
+ HARP_TIMER(&perm_timer, ATMARP_PERM_INTERVAL,
+ atmarp_perm_timeout);
+
+ /*
+ * Establish a connection to SCSP for each interface. If a
+ * connect fails, it will be retried when the cache update
+ * timer fires.
+ */
+ for (aip = atmarp_intf_head; aip; aip = aip->ai_next) {
+ if (atmarp_if_ready(aip)) {
+ (void)atmarp_scsp_connect(aip);
+ }
+ }
+
+ /*
+ * Read the cache from the kernel
+ */
+ atmarp_get_updated_cache();
+
+ /*
+ * Main program loop -- wait for data to come in from SCSP.
+ * When the timer fires, it will be handled elsewhere.
+ */
+ while (1) {
+ /*
+ * Wait for input from SCSP
+ */
+ FD_ZERO(&read_set);
+ FD_ZERO(&write_set);
+ FD_ZERO(&except_set);
+ for (aip = atmarp_intf_head; aip; aip = aip->ai_next) {
+ if (aip->ai_scsp_sock != -1) {
+ FD_SET(aip->ai_scsp_sock, &read_set);
+ }
+ }
+ rc = select(atmarp_max_socket + 1,
+ &read_set, &write_set,
+ &except_set, (struct timeval *)0);
+ if (rc < 0) {
+ if (harp_timer_exec) {
+ timer_proc();
+ continue;
+ } else if (errno == EINTR) {
+ continue;
+ } else {
+ atmarp_log(LOG_ERR, "Select failed");
+ abort();
+ }
+ }
+
+ /*
+ * Read and process the input from SCSP
+ */
+ for (i = 0; i <= atmarp_max_socket; i++) {
+ if (FD_ISSET(i, &read_set)) {
+ aip = atmarp_find_intf_sock(i);
+ if (aip)
+ rc = atmarp_scsp_read(aip);
+ }
+ }
+ }
+}
diff --git a/usr.sbin/atm/scspd/Makefile b/usr.sbin/atm/scspd/Makefile
new file mode 100644
index 0000000..7130c2b
--- /dev/null
+++ b/usr.sbin/atm/scspd/Makefile
@@ -0,0 +1,41 @@
+# ===================================
+# HARP | Host ATM Research Platform
+# ===================================
+#
+#
+# This Host ATM Research Platform ("HARP") file (the "Software") is
+# made available by Network Computing Services, Inc. ("NetworkCS")
+# "AS IS". NetworkCS does not provide maintenance, improvements or
+# support of any kind.
+#
+# NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+# INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+# SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+# In no event shall NetworkCS be responsible for any damages, including
+# but not limited to consequential damages, arising from or relating to
+# any use of the Software or related support.
+#
+# Copyright 1994-1998 Network Computing Services, Inc.
+#
+# Copies of this Software may be made, however, the above copyright
+# notice must be reproduced on all copies.
+#
+# @(#) $FreeBSD$
+
+PROG= scspd
+MAN= scspd.8
+SRCS= scspd.c scsp_cafsm.c scsp_config.c scsp_config_lex.c \
+ scsp_config_parse.y \
+ scsp_hfsm.c scsp_if.c scsp_input.c scsp_log.c scsp_msg.c \
+ scsp_output.c scsp_print.c scsp_socket.c scsp_subr.c \
+ scsp_timer.c
+
+CFLAGS+= -I. -I${.CURDIR}/../../../sys -I${.CURDIR}
+
+LDADD= -latm -lmd
+DPADD= ${LIBATM} ${LIBMD}
+
+YFLAGS= -d
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/atm/scspd/scsp_cafsm.c b/usr.sbin/atm/scspd/scsp_cafsm.c
new file mode 100644
index 0000000..80fcfa0
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_cafsm.c
@@ -0,0 +1,1439 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * Cache Alignment finite state machine
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netatm/port.h>
+#include <netatm/queue.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+
+#include <errno.h>
+#include <libatm.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "scsp_msg.h"
+#include "scsp_if.h"
+#include "scsp_var.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+/*
+ * CA FSM actions
+ */
+#define CA_ACTION_CNT 20
+int scsp_ca_act_00 __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_csa *csap;
+
+ /*
+ * Check whether this messages answers a CSUS
+ */
+ scsp_csus_ack(dcsp, msg);
+
+ /*
+ * If all CSAs requestd in CSUS messages have been
+ * received, the cache is aligned, so go to Aligned State
+ */
+ if (!dcsp->sd_csus_rexmt_msg && !dcsp->sd_crl &&
+ dcsp->sd_ca_state != SCSP_CAFSM_ALIGNED) {
+ dcsp->sd_ca_state = SCSP_CAFSM_ALIGNED;
+ rc = scsp_cfsm(dcsp, SCSP_CIFSM_CA_ALIGN,
+ (Scsp_msg *)0, (Scsp_if_msg *)0);
+ }
+
+ /*
+ * Pass the CSU Req to the client interface FSM
+ */
+ rc = scsp_cfsm(dcsp, SCSP_CIFSM_CSU_REQ, msg,
+ (Scsp_if_msg *)0);
+
+ /*
+ * Move the CSA chain from the message to the list of
+ * requests that need acknowledgements
+ */
+ for (csap = msg->sc_csu_msg->csu_csa_rec; csap;
+ csap = csap->next) {
+ LINK2TAIL(csap, Scsp_csa, dcsp->sd_csu_ack_pend, next);
+ }
+ msg->sc_csu_msg->csu_csa_rec = (Scsp_csa *)0;
+
+ return(rc);
+}
+
+
+/*
+ * CA finite state machine action 9
+ * CA Retransmit timer expired in Update Cache or Aligned state--free
+ * the saved CA message
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * p ignored
+ *
+ * Returns:
+ * 0 success
+ * errno error encountered
+ *
+ */
+int
+scsp_ca_act_09(dcsp, p)
+ Scsp_dcs *dcsp;
+ void *p;
+{
+ /*
+ * Free any CA message saved for retransmission
+ */
+ if (dcsp->sd_ca_rexmt_msg) {
+ scsp_free_msg(dcsp->sd_ca_rexmt_msg);
+ dcsp->sd_ca_rexmt_msg = (Scsp_msg *)0;
+ }
+
+ return(0);
+}
+
+
+/*
+ * CA finite state machine action 10
+ * CSU Reply received -- Process the message
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * p pointer to received message
+ *
+ * Returns:
+ * 0 success
+ * errno error encountered
+ *
+ */
+int
+scsp_ca_act_10(dcsp, p)
+ Scsp_dcs *dcsp;
+ void *p;
+{
+ int rc = 0;
+ Scsp_msg *msg = (Scsp_msg *)p;
+ Scsp_csu_rexmt *rxp, *next_rxp;
+ Scsp_csa *csap, *next_csap, *mcp;
+
+ /*
+ * Dequeue acknowledged CSAs. For each CSAS in the received
+ * message, find the corresponding CSA on the CSU Request
+ * retransmit queue. Remove the CSA from the queue; if this
+ * results in the retransmit queue entry being empty, delete
+ * the entry. If the DCS has a newer CSA, send a CSUS to
+ * request it.
+ *
+ * Caution--potentially confusing lack of indentation ahead.
+ */
+ for (mcp = msg->sc_csu_msg->csu_csa_rec; mcp;
+ mcp = mcp->next) {
+ for (rxp = dcsp->sd_csu_rexmt; rxp; rxp = next_rxp) {
+ next_rxp = rxp->sr_next;
+ for (csap = rxp->sr_csa; csap; csap = next_csap) {
+ next_csap = csap->next;
+ if (scsp_cmp_key(&csap->key, &mcp->key) ||
+ scsp_cmp_id(&csap->oid, &mcp->oid))
+ continue;
+ /*
+ * Found a CSA whose key and ID are equal to
+ * those in the CSU Reply
+ */
+ if (csap->seq == mcp->seq) {
+ /*
+ * The queued seq no is equal to the
+ * received seq no--the CSA is acknowledged
+ */
+ UNLINK(csap, Scsp_csa, rxp->sr_csa, next);
+ SCSP_FREE_CSA(csap);
+ } else if (csap->seq < mcp->seq) {
+ /*
+ * Queued seq no is less than received.
+ * We must dequeue the CSA and send a
+ * CSUS to request the more-up-to-date
+ * cache entry.
+ */
+ UNLINK(mcp, Scsp_csa,
+ msg->sc_csu_msg->csu_csa_rec,
+ next);
+ LINK2TAIL(mcp, Scsp_csa, dcsp->sd_crl, next);
+ UNLINK(csap, Scsp_csa, rxp->sr_csa, next);
+ SCSP_FREE_CSA(csap);
+ if (!dcsp->sd_csus_rexmt_msg) {
+ rc = scsp_send_csus(dcsp);
+ if (rc) {
+ return(rc);
+ }
+ }
+ }
+ /*
+ * Queued seq no is greater than
+ * received. Ignore the received CSAS.
+ */
+
+ /*
+ * If the retransmission block is empty, stop the
+ * timer and free it
+ */
+ if (!rxp->sr_csa) {
+ HARP_CANCEL(&rxp->sr_t);
+ UNLINK(rxp, Scsp_csu_rexmt,
+ dcsp->sd_csu_rexmt, sr_next);
+ free(rxp);
+ }
+
+ break;
+ } /* for (csap = ... */
+ } /* for (rxp = ... */
+ } /* for (mcp = ... */
+
+ return(rc);
+}
+
+
+/*
+ * CA finite state machine action 11
+ * Updated cache entry -- update the summary cache and send a
+ * CSU Request
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * p pointer to CSA describing new cache entry
+ *
+ * Returns:
+ * 0 success
+ * errno error encountered
+ *
+ */
+int
+scsp_ca_act_11(dcsp, p)
+ Scsp_dcs *dcsp;
+ void *p;
+{
+ int rc, state;
+ Scsp_csa *csap = (Scsp_csa *)p;
+ Scsp_cse *csep;
+
+ /*
+ * Get the state of the CSA
+ */
+ switch(dcsp->sd_server->ss_pid) {
+ case SCSP_PROTO_ATMARP:
+ state = csap->atmarp_data->sa_state;
+ break;
+ default:
+ SCSP_FREE_CSA(csap);
+ return(EINVAL);
+ }
+
+ if (state < SCSP_ASTATE_NEW || state > SCSP_ASTATE_DEL) {
+ SCSP_FREE_CSA(csap);
+ return(EINVAL);
+ }
+
+ /*
+ * Look up the cache summary entry for the CSA
+ */
+ SCSP_LOOKUP(dcsp->sd_server, &csap->key, csep);
+
+ /*
+ * Process ATMARP entries
+ */
+ if (dcsp->sd_server->ss_pid == SCSP_PROTO_ATMARP) {
+ switch(state) {
+ case SCSP_ASTATE_NEW:
+ case SCSP_ASTATE_UPD:
+ /*
+ * Add the entry if we don't have it already
+ */
+ if (!csep) {
+ csep = calloc(1, sizeof(Scsp_cse));
+ if (csep == NULL)
+ scsp_mem_err("scsp_ca_act_11: sizeof(Scsp_cse)");
+
+ csep->sc_key = csap->key;
+ SCSP_ADD(dcsp->sd_server, csep);
+ }
+
+ /*
+ * Update the cache summary entry
+ */
+ csep->sc_seq = csap->seq;
+ csep->sc_oid = csap->oid;
+ break;
+ case SCSP_ASTATE_DEL:
+ /*
+ * Delete any entry, but don't send the
+ * delete to the DCS
+ */
+ if (csep) {
+ SCSP_DELETE(dcsp->sd_server, csep);
+ free(csep);
+ }
+
+ SCSP_FREE_CSA(csap);
+ return(0);
+ }
+ }
+
+ /*
+ * Send the CSA in a CSU Request
+ */
+ csap->trans_ct = 0;
+ rc = scsp_send_csu_req(dcsp, csap);
+
+ return(rc);
+}
+
+
+/*
+ * CA finite state machine action 12
+ * CSUS retransmit timer expired--send a CSUS with any pending CSA
+ * records
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * p ignored
+ *
+ * Returns:
+ * 0 success
+ * errno error encountered
+ *
+ */
+int
+scsp_ca_act_12(dcsp, p)
+ Scsp_dcs *dcsp;
+ void *p;
+{
+ int rc;
+
+ rc = scsp_send_csus(dcsp);
+
+ return(rc);
+}
+
+
+/*
+ * CA finite state machine action 13
+ * CSU retransmit timer fired in Update or Aligned state--
+ * retransmit CSU Req
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * p pointer to retransmission block whose timer fired
+ *
+ * Returns:
+ * 0 success
+ * errno error encountered
+ *
+ */
+int
+scsp_ca_act_13(dcsp, p)
+ Scsp_dcs *dcsp;
+ void *p;
+{
+ int rc = 0;
+ Scsp_csu_rexmt *rxp = (Scsp_csu_rexmt *)p;
+ Scsp_csa *csap, *csap1, *next_csap;
+
+ /*
+ * Unlink and free the retransmit request block
+ */
+ csap = rxp->sr_csa;
+ UNLINK(rxp, Scsp_csu_rexmt, dcsp->sd_csu_rexmt, sr_next);
+ free(rxp);
+
+ /*
+ * Increment the transmission count for the CSAs in the request
+ */
+ for (csap1 = csap; csap1; csap1 = next_csap) {
+ next_csap = csap1->next;
+ csap1->trans_ct++;
+ if (csap1->trans_ct >= dcsp->sd_csu_rexmt_max) {
+ /*
+ * We've already sent this as many times as
+ * the limit allows. Drop this CSA.
+ */
+ UNLINK(csap1, Scsp_csa, csap, next);
+ SCSP_FREE_CSA(csap1);
+ }
+ }
+
+ /*
+ * Send another CSU Request with the CSA list, if it isn't
+ * empty now
+ */
+ if (csap) {
+ rc = scsp_send_csu_req(dcsp, csap);
+ }
+
+ return(rc);
+}
+
+
+/*
+ * CA finite state machine action 14
+ * Updated cache entry in Master/Slave Negotiation, Master, or
+ * Slave state--add entry to cache and CSA list
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * p pointer to new cache summary entry
+ *
+ * Returns:
+ * 0 success
+ * errno error encountered
+ *
+ */
+int
+scsp_ca_act_14(dcsp, p)
+ Scsp_dcs *dcsp;
+ void *p;
+{
+ Scsp_csa *csap = (Scsp_csa *)p;
+ Scsp_cse *csep, *csep1;
+
+ /*
+ * Check to see whether we already have this
+ */
+ SCSP_LOOKUP(dcsp->sd_server, &csap->key, csep);
+
+ /*
+ * If we don't already have it and it's not being deleted,
+ * build a new cache summary entry
+ */
+ if (!csep && !csap->null) {
+ /*
+ * Get memory for a new entry
+ */
+ csep = calloc(1, sizeof(Scsp_cse));
+ if (csep == NULL)
+ scsp_mem_err("scsp_ca_act_14: sizeof(Scsp_cse)");
+
+ /*
+ * Fill out the new cache entry
+ */
+ csep->sc_seq = csap->seq;
+ csep->sc_key = csap->key;
+ csep->sc_oid = csap->oid;
+
+ /*
+ * Duplicate the new cache entry
+ */
+ csep1 = scsp_dup_cse(csep);
+
+ /*
+ * Add entry to the summary cache and the CSAS list
+ */
+ SCSP_ADD(dcsp->sd_server, csep);
+ LINK2TAIL(csep1, Scsp_cse, dcsp->sd_ca_csas, sc_next);
+ } else {
+ /*
+ * We already have the entry. Find it on the CSAS
+ * list.
+ */
+ for (csep1 = dcsp->sd_ca_csas; csep1;
+ csep1 = csep1->sc_next) {
+ if (scsp_cmp_key(&csep->sc_key,
+ &csep1->sc_key) == 0)
+ break;
+ }
+
+ /*
+ * Update or delete the entry
+ */
+ if (csap->null) {
+ /*
+ * The null flag is set--delete the entry
+ */
+ SCSP_DELETE(dcsp->sd_server, csep);
+ free(csep);
+ if (csep1) {
+ UNLINK(csep1, Scsp_cse,
+ dcsp->sd_ca_csas,
+ sc_next);
+ free(csep1);
+ }
+ } else {
+ /*
+ * Update the entry
+ */
+ csep->sc_seq = csap->seq;
+ csep->sc_oid = csap->oid;
+ if (!csep1) {
+ csep1 = scsp_dup_cse(csep);
+ LINK2TAIL(csep1, Scsp_cse,
+ dcsp->sd_ca_csas, sc_next);
+ } else {
+ csep1->sc_seq = csap->seq;
+ csep1->sc_oid = csap->oid;
+ }
+ }
+ }
+
+ return(0);
+}
+
+
+/*
+ * CA finite state machine action 15
+ * CA message received in Update Cache state--if we have a saved CA
+ * message, retransmit it; otherwise, go to Master/Slave Negotiation
+ * state
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * p ignored
+ *
+ * Returns:
+ * 0 success
+ * errno error encountered
+ *
+ */
+int
+scsp_ca_act_15(dcsp, p)
+ Scsp_dcs *dcsp;
+ void *p;
+{
+ int rc;
+ Scsp_msg *msg = (Scsp_msg *)p;
+
+ /*
+ * If we don't have a saved CA message, or the sequence no. in
+ * the received message isn't right, fall back to Master/Slave
+ * Negotiation state
+ */
+ if (!dcsp->sd_ca_rexmt_msg ||
+ msg->sc_ca->ca_seq != dcsp->sd_ca_seq) {
+ dcsp->sd_ca_state = SCSP_CAFSM_NEG;
+ scsp_dcs_cleanup(dcsp);
+ rc = scsp_ca_act_01(dcsp, (Scsp_msg *)0);
+ } else {
+ /*
+ * Retransmit the saved CA message and reset the
+ * CA timer
+ */
+ rc = scsp_send_msg(dcsp, dcsp->sd_ca_rexmt_msg);
+ if (rc == 0) {
+ HARP_CANCEL(&dcsp->sd_ca_rexmt_t);
+ HARP_TIMER(&dcsp->sd_ca_rexmt_t,
+ dcsp->sd_ca_rexmt_int,
+ scsp_ca_retran_timeout);
+ }
+ }
+
+ return(rc);
+}
+
+
+/*
+ * CA finite state machine action 16
+ * Update Response received from client in Update Cache or Aligned
+ * state. Move the acknowledged CSA to the acknowledged queue. If
+ * the list of CSAs pending acknowledgement is empty, send a CSU
+ * Reply.
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * p pointer to message from client
+ *
+ * Returns:
+ * 0 success
+ * errno error encountered
+ *
+ */
+int
+scsp_ca_act_16(dcsp, p)
+ Scsp_dcs *dcsp;
+ void *p;
+{
+ int found, rc = 0;
+ Scsp_if_msg *cmsg = (Scsp_if_msg *)p;
+ Scsp_csa *csap;
+
+ /*
+ * Find the acknowledged CSA
+ */
+ for (csap = dcsp->sd_csu_ack_pend, found = 0; csap && !found;
+ csap = csap->next) {
+ switch (dcsp->sd_server->ss_pid) {
+ case SCSP_PROTO_ATMARP:
+ found = ((scsp_cmp_key(&csap->key,
+ &cmsg->si_atmarp.sa_key) == 0) &&
+ (scsp_cmp_id(&csap->oid,
+ &cmsg->si_atmarp.sa_oid) == 0));
+ break;
+ default:
+ /*
+ * Protocol not implemented
+ */
+ return(EPROTONOSUPPORT);
+ }
+ if (found)
+ break;
+ }
+
+ if (!found) {
+ if (scsp_trace_mode & SCSP_TRACE_CAFSM) {
+ scsp_trace("scsp_ca_act_16: can't find CSA entry for Update Response\n");
+ }
+ return(0);
+ }
+
+ if (cmsg->si_rc == SCSP_RSP_OK) {
+ /*
+ * The server accepted the cache entry
+ */
+
+ /*
+ * Update SCSP's cache
+ */
+ scsp_update_cache(dcsp, csap);
+
+ /*
+ * Send this CSA to any other DCSs in the server group
+ */
+ rc = scsp_propagate_csa(dcsp, csap);
+ }
+
+ /*
+ * Move the CSA from the ACK pending queue to the
+ * acknowledged queue
+ */
+ UNLINK(csap, Scsp_csa, dcsp->sd_csu_ack_pend, next);
+ LINK2TAIL(csap, Scsp_csa, dcsp->sd_csu_ack, next);
+ if (!dcsp->sd_csu_ack_pend) {
+ /*
+ * ACK pending list is empty--send a CSU Reply
+ */
+ csap = dcsp->sd_csu_ack;
+ dcsp->sd_csu_ack = (Scsp_csa *)0;
+ rc = scsp_send_csu_reply(dcsp, csap);
+ }
+
+ return(rc);
+}
+
+
+/*
+ * CA finite state machine action 17
+ * Ignore an event.
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * p ignored
+ *
+ * Returns:
+ * always returns 0
+ *
+ */
+int
+scsp_ca_act_17(dcsp, p)
+ Scsp_dcs *dcsp;
+ void *p;
+{
+ return(0);
+}
+
+
+/*
+ * CA finite state machine action 18
+ * Updated cache entry in Down state--add entry to summary cache
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * p pointer to new cache summary entry
+ *
+ * Returns:
+ * 0 success
+ * errno error encountered
+ *
+ */
+int
+scsp_ca_act_18(dcsp, p)
+ Scsp_dcs *dcsp;
+ void *p;
+{
+ Scsp_csa *csap = (Scsp_csa *)p;
+
+ /*
+ * Update the cache as appropriate
+ */
+ scsp_update_cache(dcsp, csap);
+
+ return(0);
+}
+
+
+/*
+ * CA finite state machine action 19
+ * Update Response received from client in Master/Slave Negotiation
+ * state. Update the cache as appropriate.
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * p pointer to message from client
+ *
+ * Returns:
+ * 0 success
+ * errno error encountered
+ *
+ */
+int
+scsp_ca_act_19(dcsp, p)
+ Scsp_dcs *dcsp;
+ void *p;
+{
+ Scsp_if_msg *cmsg = (Scsp_if_msg *)p;
+ Scsp_csa *csap;
+
+ /*
+ * Ignore the message if the client rejected the update
+ */
+ if (cmsg->si_rc != SCSP_RSP_OK) {
+ return(0);
+ }
+
+ /*
+ * Create a CSAS from the client's update
+ */
+ csap = calloc(1, sizeof(Scsp_csa));
+ if (csap == NULL)
+ scsp_mem_err("scsp_ca_act_19: sizeof(Scsp_csa)");
+ csap->hops = 1;
+ switch (dcsp->sd_server->ss_pid) {
+ case SCSP_PROTO_ATMARP:
+ csap->null = cmsg->si_atmarp.sa_state ==
+ SCSP_ASTATE_DEL;
+ csap->seq = cmsg->si_atmarp.sa_seq;
+ csap->key = cmsg->si_atmarp.sa_key;
+ csap->oid = cmsg->si_atmarp.sa_oid;
+ break;
+ default:
+ return(EINVAL);
+ }
+
+ /*
+ * Update SCSP's cache
+ */
+ scsp_update_cache(dcsp, csap);
+
+ return(0);
+}
diff --git a/usr.sbin/atm/scspd/scsp_config.c b/usr.sbin/atm/scspd/scsp_config.c
new file mode 100644
index 0000000..324d250
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_config.c
@@ -0,0 +1,1158 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * Configuration file processing
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netatm/port.h>
+#include <netatm/queue.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+
+#include <errno.h>
+#include <libatm.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "scsp_msg.h"
+#include "scsp_if.h"
+#include "scsp_var.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+extern int yyparse __P((void));
+
+/*
+ * Global variables
+ */
+FILE *cfg_file;
+Scsp_server *current_server;
+Scsp_dcs *current_dcs;
+
+
+/*
+ * Process the configuration file
+ *
+ * This routine is called when the daemon starts, and it can also be
+ * called while it is running, as the result of a SIGHUP signal. It
+ * therefore has to be capable of both configuring the daemon from
+ * scratch and modifying the configuration of a running daemon.
+ *
+ * Arguments:
+ * cfn configuration file name
+ *
+ * Returns:
+ * 0 configuration read with no errors
+ * else error found in configuration file
+ *
+ */
+int
+scsp_config(cfn)
+ char *cfn;
+{
+ int rc;
+ Scsp_server *ssp, *snext;
+
+ /*
+ * Open the configuration file
+ */
+ cfg_file = fopen(cfn, "r");
+ if (!cfg_file) {
+ scsp_log(LOG_ERR, "can't open config file %s",
+ (void *)cfn);
+ exit(1);
+ }
+
+ /*
+ * Initialize current interface pointer
+ */
+ current_server = (Scsp_server *)0;
+
+ /*
+ * Clear marks on any existing servers
+ */
+ for (ssp = scsp_server_head; ssp; ssp = ssp->ss_next) {
+ ssp->ss_mark = 0;
+ }
+
+ /*
+ * Scan the configuration file, processing each line as
+ * it is read
+ */
+ rc = yyparse();
+
+ /*
+ * Close the configuration file
+ */
+ fclose(cfg_file);
+
+ /*
+ * Delete any server entries that weren't updated
+ */
+ for (ssp = scsp_server_head; ssp; ssp = snext) {
+ snext = ssp->ss_next;
+ if (!ssp->ss_mark)
+ scsp_server_delete(ssp);
+ }
+
+ return(rc);
+}
+
+
+/*
+ * Prepare for SCSP DCS setup
+ *
+ * This routine is called from yyparse() when a DCS command is found.
+ *
+ * Arguments:
+ * none
+ *
+ * Returns:
+ * 0 success
+ * 1 error encountered
+ *
+ */
+int
+start_dcs()
+{
+ Scsp_dcs *dcsp;
+
+ /*
+ * Make sure we have a current server block
+ */
+ if (!current_server) {
+ parse_error("server not found");
+ return(1);
+ }
+
+ /*
+ * Allocate a DCS block
+ */
+ dcsp = calloc(1, sizeof(Scsp_dcs));
+ if (dcsp == NULL)
+ scsp_mem_err("start_dcs: sizeof(Scsp_dcs)");
+
+ /*
+ * Fill out DCS links and default values
+ */
+ dcsp->sd_server = current_server;
+ dcsp->sd_addr.address_format = T_ATM_ABSENT;
+ dcsp->sd_subaddr.address_format = T_ATM_ABSENT;
+ dcsp->sd_sock = -1;
+ dcsp->sd_ca_rexmt_int = SCSP_CAReXmitInterval;
+ dcsp->sd_csus_rexmt_int = SCSP_CSUSReXmitInterval;
+ dcsp->sd_hops = SCSP_CSA_HOP_CNT;
+ dcsp->sd_csu_rexmt_int = SCSP_CSUReXmitInterval;
+ dcsp->sd_csu_rexmt_max = SCSP_CSUReXmitMax;
+ LINK2TAIL(dcsp, Scsp_dcs, current_server->ss_dcs, sd_next);
+
+ current_dcs = dcsp;
+ return(0);
+}
+
+
+/*
+ * Finish up server configuration
+ *
+ * This routine is called from yyparse() to at the end of a DCS
+ * command. It checks that required fields are set and finishes
+ * up the DCS block.
+ *
+ * Arguments:
+ * none
+ *
+ * Returns:
+ * 0 success
+ * 1 error encountered
+ *
+ */
+int
+finish_dcs()
+{
+ int rc = 0;
+ Scsp_dcs *dcsp;
+ Scsp_server *ssp;
+
+ /*
+ * Make sure we have a current server block and DCS block
+ */
+ if (!current_server) {
+ parse_error("server not found");
+ return(1);
+ }
+
+ if (!current_dcs) {
+ parse_error("server not found");
+ return(1);
+ }
+ ssp = current_server;
+ dcsp = current_dcs;
+
+ /*
+ * Make sure the DCS ID is set
+ */
+ if (dcsp->sd_dcsid.id_len == 0) {
+ parse_error("DCS ID not set");
+ rc++;
+ }
+
+ /*
+ * Make sure the ATM address is set
+ */
+ if (dcsp->sd_addr.address_format == T_ATM_ABSENT) {
+ parse_error("DCS ATM address not set");
+ rc++;
+ }
+
+ current_dcs = (Scsp_dcs *)0;
+ return(rc);
+}
+
+
+/*
+ * Configure DCS ATM address
+ *
+ * This routine is called from yyparse() to process an ATMaddr command.
+ *
+ * Arguments:
+ * ap pointer to DCS's ATM address (in ASCII)
+ * sap pointer to DCS's ATM subaddress (in ASCII)
+ *
+ * Returns:
+ * 0 success
+ * 1 error encountered
+ *
+ */
+int
+set_dcs_addr(ap, sap)
+ char *ap, *sap;
+{
+ Scsp_dcs *dcsp;
+ Atm_addr addr, subaddr;
+
+ /*
+ * Make sure we have a current server block and DCS block
+ */
+ if (!current_server) {
+ parse_error("server not found");
+ return(1);
+ }
+
+ if (!current_dcs) {
+ parse_error("server not found");
+ return(1);
+ }
+ dcsp = current_dcs;
+
+ /*
+ * Initialize
+ */
+ bzero(&addr, sizeof(addr));
+ addr.address_format = T_ATM_ABSENT;
+ bzero(&subaddr, sizeof(subaddr));
+ subaddr.address_format = T_ATM_ABSENT;
+
+ /*
+ * Convert the ATM address from character to internal format
+ */
+ if (ap) {
+ addr.address_length = get_hex_atm_addr(ap,
+ (u_char *)addr.address, strlen(ap));
+ if (addr.address_length == 0) {
+ parse_error("invalid ATM address");
+ return(1);
+ }
+ if (addr.address_length == sizeof(Atm_addr_nsap)) {
+ addr.address_format = T_ATM_ENDSYS_ADDR;
+ } else if (addr.address_length <=
+ sizeof(Atm_addr_e164)) {
+ addr.address_format = T_ATM_E164_ADDR;
+ } else {
+ parse_error("invalid ATM address");
+ return(1);
+ }
+ }
+
+ /*
+ * Convert the ATM subaddress from character to internal format
+ */
+ if (sap) {
+ subaddr.address_length = get_hex_atm_addr(sap,
+ (u_char *)subaddr.address, strlen(sap));
+ if (subaddr.address_length == 0) {
+ parse_error("invalid ATM address");
+ return(1);
+ }
+ if (subaddr.address_length == sizeof(Atm_addr_nsap)) {
+ subaddr.address_format = T_ATM_ENDSYS_ADDR;
+ } else if (subaddr.address_length <=
+ sizeof(Atm_addr_e164)) {
+ subaddr.address_format = T_ATM_E164_ADDR;
+ } else {
+ parse_error("invalid ATM subaddress");
+ return(1);
+ }
+ }
+
+ /*
+ * Make sure we have a legal ATM address type combination
+ */
+ if (((addr.address_format != T_ATM_ENDSYS_ADDR) ||
+ (subaddr.address_format != T_ATM_ABSENT)) &&
+ ((addr.address_format != T_ATM_E164_ADDR) ||
+ (subaddr.address_format != T_ATM_ENDSYS_ADDR))) {
+ parse_error("invalid address/subaddress combination");
+ return(1);
+ }
+
+ /*
+ * Save the address and subaddress
+ */
+ ATM_ADDR_COPY(&addr, &dcsp->sd_addr);
+ ATM_ADDR_COPY(&subaddr, &dcsp->sd_subaddr);
+
+ return(0);
+}
+
+
+/*
+ * Configure CA retransmit interval for DCS
+ *
+ * This routine is called from yyparse() to process a CAReXmitInt
+ * command.
+ *
+ * Arguments:
+ * val time interval
+ *
+ * Returns:
+ * 0 success
+ * 1 error encountered
+ *
+ */
+int
+set_dcs_ca_rexmit(val)
+ int val;
+{
+ Scsp_dcs *dcsp;
+
+ /*
+ * Make sure we have a current server block and DCS block
+ */
+ if (!current_server) {
+ parse_error("server not found");
+ return(1);
+ }
+
+ if (!current_dcs) {
+ parse_error("server not found");
+ return(1);
+ }
+ dcsp = current_dcs;
+
+
+ /*
+ * Validate the interval
+ */
+ if (val <= 0 || val > 1024) {
+ parse_error("invalid CA retransmit interval");
+ return(1);
+ }
+
+ /*
+ * Set CA retransmit interval
+ */
+ dcsp->sd_ca_rexmt_int = val;
+
+ return(0);
+}
+
+
+/*
+ * Configure CSUS retransmit interval for DCS
+ *
+ * This routine is called from yyparse() to process a CSUSReXmitInt
+ * command.
+ *
+ * Arguments:
+ * val time interval
+ *
+ * Returns:
+ * 0 success
+ * 1 error encountered
+ *
+ */
+int
+set_dcs_csus_rexmit(val)
+ int val;
+{
+ Scsp_dcs *dcsp;
+
+ /*
+ * Make sure we have a current server block and DCS block
+ */
+ if (!current_server) {
+ parse_error("server not found");
+ return(1);
+ }
+
+ if (!current_dcs) {
+ parse_error("server not found");
+ return(1);
+ }
+ dcsp = current_dcs;
+
+
+ /*
+ * Validate the interval
+ */
+ if (val <= 0 || val > 1024) {
+ parse_error("invalid CSUS retransmit interval");
+ return(1);
+ }
+
+ /*
+ * Set CSUS retransmit interval
+ */
+ dcsp->sd_csus_rexmt_int = val;
+
+ return(0);
+}
+
+
+/*
+ * Configure CSU retransmit interval for DCS
+ *
+ * This routine is called from yyparse() to process a CSUReXmitInt
+ * command.
+ *
+ * Arguments:
+ * val time interval
+ *
+ * Returns:
+ * 0 success
+ * 1 error encountered
+ *
+ */
+int
+set_dcs_csu_rexmit(val)
+ int val;
+{
+ Scsp_dcs *dcsp;
+
+ /*
+ * Make sure we have a current server block and DCS block
+ */
+ if (!current_server) {
+ parse_error("server not found");
+ return(1);
+ }
+
+ if (!current_dcs) {
+ parse_error("server not found");
+ return(1);
+ }
+ dcsp = current_dcs;
+
+
+ /*
+ * Validate the interval
+ */
+ if (val <= 0 || val > 1024) {
+ parse_error("invalid CSU retransmit interval");
+ return(1);
+ }
+
+ /*
+ * Set CSU retransmit interval
+ */
+ dcsp->sd_csu_rexmt_int = val;
+
+ return(0);
+}
+
+
+/*
+ * Configure CSU retransmit limit for DCS
+ *
+ * This routine is called from yyparse() to process a CSUReXmitMax
+ * command.
+ *
+ * Arguments:
+ * val time interval
+ *
+ * Returns:
+ * 0 success
+ * 1 error encountered
+ *
+ */
+int
+set_dcs_csu_rexmit_max(val)
+ int val;
+{
+ Scsp_dcs *dcsp;
+
+ /*
+ * Make sure we have a current server block and DCS block
+ */
+ if (!current_server) {
+ parse_error("server not found");
+ return(1);
+ }
+
+ if (!current_dcs) {
+ parse_error("server not found");
+ return(1);
+ }
+ dcsp = current_dcs;
+
+
+ /*
+ * Validate the interval
+ */
+ if (val <= 0 || val > 1024) {
+ parse_error("invalid CSU retransmit maximum");
+ return(1);
+ }
+
+ /*
+ * Set CSU retransmit limit
+ */
+ dcsp->sd_csu_rexmt_max = val;
+
+ return(0);
+}
+
+
+/*
+ * Configure Hello dead factor for DCS
+ *
+ * This routine is called from yyparse() to process a HelloDead
+ * command.
+ *
+ * Arguments:
+ * val number of times Hello interval has to expire before
+ * a DCS is considered dead
+ *
+ * Returns:
+ * 0 success
+ * 1 error encountered
+ *
+ */
+int
+set_dcs_hello_df(val)
+ int val;
+{
+ Scsp_dcs *dcsp;
+
+ /*
+ * Make sure we have a current server block and DCS block
+ */
+ if (!current_server) {
+ parse_error("server not found");
+ return(1);
+ }
+
+ if (!current_dcs) {
+ parse_error("server not found");
+ return(1);
+ }
+ dcsp = current_dcs;
+
+
+ /*
+ * Validate the limit
+ */
+ if (val <= 0 || val > 1024) {
+ parse_error("invalid Hello dead factor");
+ return(1);
+ }
+
+ /*
+ * Set Hello dead factor
+ */
+ dcsp->sd_hello_df = val;
+
+ return(0);
+}
+
+
+/*
+ * Configure Hello interval for DCS
+ *
+ * This routine is called from yyparse() to process a HelloInt
+ * command.
+ *
+ * Arguments:
+ * val time interval
+ *
+ * Returns:
+ * 0 success
+ * 1 error encountered
+ *
+ */
+int
+set_dcs_hello_int(val)
+ int val;
+{
+ Scsp_dcs *dcsp;
+
+ /*
+ * Make sure we have a current server block and DCS block
+ */
+ if (!current_server) {
+ parse_error("server not found");
+ return(1);
+ }
+
+ if (!current_dcs) {
+ parse_error("server not found");
+ return(1);
+ }
+ dcsp = current_dcs;
+
+
+ /*
+ * Validate the interval
+ */
+ if (val <= 0 || val > 1024) {
+ parse_error("invalid Hello interval");
+ return(1);
+ }
+
+ /*
+ * Set Hello interval
+ */
+ dcsp->sd_hello_int = val;
+
+ return(0);
+}
+
+
+/*
+ * Configure hop count for SCSP server
+ *
+ * This routine is called from yyparse() to process a Hops command.
+ *
+ * Arguments:
+ * hops number of hops
+ *
+ * Returns:
+ * 0 success
+ * 1 error encountered
+ *
+ */
+int
+set_dcs_hops(hops)
+ int hops;
+{
+ Scsp_dcs *dcsp;
+
+ /*
+ * Make sure we have a current server block and DCS block
+ */
+ if (!current_server) {
+ parse_error("server not found");
+ return(1);
+ }
+
+ if (!current_dcs) {
+ parse_error("server not found");
+ return(1);
+ }
+ dcsp = current_dcs;
+
+
+ /*
+ * Validate the count
+ */
+ if (hops <= 0 || hops > 1024) {
+ parse_error("invalid hop count");
+ return(1);
+ }
+
+ /*
+ * Set hop count
+ */
+ dcsp->sd_hops = hops;
+
+ return(0);
+}
+
+
+/*
+ * Configure DCS ID
+ *
+ * This routine is called from yyparse() to process an ID command.
+ *
+ * Arguments:
+ * name pointer to DCS's DNS name or IP address (in ASCII)
+ *
+ * Returns:
+ * 0 success
+ * 1 error encountered
+ *
+ */
+int
+set_dcs_id(name)
+ char *name;
+{
+ Scsp_dcs *dcsp;
+ Scsp_server *ssp;
+ struct sockaddr_in *ip_addr;
+
+ /*
+ * Make sure we have a current server block and DCS block
+ */
+ if (!current_server) {
+ parse_error("server not found");
+ return(1);
+ }
+
+ if (!current_dcs) {
+ parse_error("server not found");
+ return(1);
+ }
+ ssp = current_server;
+ dcsp = current_dcs;
+
+ /*
+ * Convert the DNS name or IP address
+ */
+ ip_addr = get_ip_addr(name);
+ if (!ip_addr) {
+ parse_error("invalid DCS IP address");
+ return(1);
+ }
+
+ /*
+ * Verify the address length
+ */
+ if (ssp->ss_id_len != sizeof(ip_addr->sin_addr)) {
+ parse_error("invalid DCS ID length");
+ return(1);
+ }
+
+ /*
+ * Set the ID in the DCS block
+ */
+ dcsp->sd_dcsid.id_len = ssp->ss_id_len;
+ bcopy(&ip_addr->sin_addr, dcsp->sd_dcsid.id, ssp->ss_id_len);
+
+ return(0);
+}
+
+
+/*
+ * Configure network interface for SCSP server
+ *
+ * This routine is called from yyparse() to process a Netif command.
+ * It verifies the network interface name, gets interface information
+ * from the kernel, and sets the appropriate fields in the server
+ * control block.
+ *
+ * Arguments:
+ * netif pointer to network interface name
+ *
+ * Returns:
+ * 0 success
+ * 1 error encountered
+ *
+ */
+int
+set_intf(netif)
+ char *netif;
+{
+ int rc;
+ Scsp_server *ssp;
+
+ /*
+ * Get the current network interface address
+ */
+ ssp = current_server;
+ if (!ssp) {
+ parse_error("Server not found");
+ rc = 1;
+ goto set_intf_done;
+ }
+
+ /*
+ * Make sure we're configuring a valid
+ * network interface
+ */
+ rc = verify_nif_name(netif);
+ if (rc == 0) {
+ parse_error("%s is not a valid network interface",
+ (void *)netif);
+ rc = 1;
+ goto set_intf_done;
+ } else if (rc < 0) {
+ scsp_log(LOG_ERR, "Netif name verify error");
+ exit(1);
+ }
+
+ /*
+ * Save the server's network interface name
+ */
+ strcpy(ssp->ss_intf, netif);
+ rc = 0;
+
+set_intf_done:
+ return(rc);
+}
+
+
+/*
+ * Configure protocol for SCSP server
+ *
+ * This routine is called from yyparse() to process a Protocol command.
+ *
+ * Arguments:
+ * proto SCSP protocol being configured
+ *
+ * Returns:
+ * 0 success
+ * 1 error encountered
+ *
+ */
+int
+set_protocol(proto)
+ int proto;
+{
+ Scsp_server *ssp;
+
+ /*
+ * Get address of current server block
+ */
+ ssp = current_server;
+ if (!ssp) {
+ parse_error("server not found");
+ return(1);
+ }
+
+ /*
+ * Process based on protocol ID
+ */
+ switch(proto) {
+ case SCSP_PROTO_ATMARP:
+ ssp->ss_pid = proto;
+ ssp->ss_id_len = SCSP_ATMARP_ID_LEN;
+ ssp->ss_ckey_len = SCSP_ATMARP_KEY_LEN;
+ break;
+ case SCSP_PROTO_NHRP:
+ ssp->ss_pid = proto;
+ ssp->ss_id_len = SCSP_NHRP_ID_LEN;
+ ssp->ss_ckey_len = SCSP_NHRP_KEY_LEN;
+ break;
+ case SCSP_PROTO_MARS:
+ case SCSP_PROTO_DHCP:
+ case SCSP_PROTO_LNNI:
+ default:
+ parse_error("invalid protocol");
+ return(1);
+ }
+
+ return(0);
+}
+
+
+/*
+ * Configure server group for SCSP server
+ *
+ * This routine is called from yyparse() to process a ServerGroupID
+ * command.
+ *
+ * Arguments:
+ * sgid server group id
+ *
+ * Returns:
+ * 0 success
+ * 1 error encountered
+ *
+ */
+int
+set_server_group(sgid)
+ int sgid;
+{
+ Scsp_server *ssp;
+
+ /*
+ * Get address of current server block
+ */
+ ssp = current_server;
+ if (!ssp) {
+ parse_error("server not found");
+ return(1);
+ }
+
+ /*
+ * Validate server group ID
+ */
+ if (sgid <= 0) {
+ parse_error("invalid server group ID");
+ return(1);
+ }
+
+ /*
+ * Save the ID
+ */
+ ssp->ss_sgid = sgid;
+
+ return(0);
+}
+
+
+/*
+ * Prepare for SCSP server setup
+ *
+ * This routine is called from yyparse() when a Server statment is
+ * found.
+ *
+ * Arguments:
+ * name pointer to LIS name
+ *
+ * Returns:
+ * 0 success
+ * else error encountered
+ *
+ */
+int
+start_server(name)
+ char *name;
+{
+ int i;
+ Scsp_server *ssp;
+ Scsp_dcs *dcsp, *next_dcs;
+ Scsp_cse *csep, *next_cse;
+
+ /*
+ * See if we already have an entry for this name
+ */
+ for (ssp = scsp_server_head; ssp; ssp = ssp->ss_next) {
+ if (strcasecmp(ssp->ss_name, name) == 0)
+ break;
+ }
+
+ if (ssp) {
+ /*
+ * Log the fact that we're updating the entry
+ */
+ scsp_log(LOG_INFO, "updating server entry for %s",
+ (void *)name);
+
+ /*
+ * Free the existing cache
+ */
+ for (i = 0; i < SCSP_HASHSZ; i++) {
+ for (csep = ssp->ss_cache[i]; csep;
+ csep = next_cse) {
+ next_cse = csep->sc_next;
+ UNLINK(csep, Scsp_cse, ssp->ss_cache[i],
+ sc_next);
+ free(csep);
+ }
+ }
+
+ /*
+ * Delete existing DCS blocks
+ */
+ for (dcsp = ssp->ss_dcs; dcsp; dcsp = next_dcs) {
+ next_dcs = dcsp->sd_next;
+ scsp_dcs_delete(dcsp);
+ }
+ } else {
+ /*
+ * Get a new server entry
+ */
+ ssp = calloc(1, sizeof(Scsp_server));
+ if (ssp == NULL) {
+ scsp_log(LOG_ERR, "unable to allocate server entry");
+ exit(1);
+ }
+ ssp->ss_sock = -1;
+ ssp->ss_dcs_lsock = -1;
+
+ /*
+ * Set the name
+ */
+ ssp->ss_name = strdup(name);
+
+ /*
+ * Link in the new interface entry
+ */
+ LINK2TAIL(ssp, Scsp_server, scsp_server_head,
+ ss_next);
+ }
+
+ /*
+ * If the mark is already set, this is a duplicate command
+ */
+ if (ssp->ss_mark) {
+ parse_error("duplicate server \"%s\"", name);
+ return(1);
+ }
+
+ /*
+ * Make this the current interface
+ */
+ current_server = ssp;
+
+ return(0);
+}
+
+
+/*
+ * Finish up server configuration
+ *
+ * This routine is called from yyparse() when the end of a server
+ * statement is reached. It checks that required fields are set
+ * and marks the entry as processed.
+ *
+ * Arguments:
+ * None
+ *
+ * Returns:
+ * 0 OK
+ * 1 Error
+ *
+ */
+int
+finish_server()
+{
+ int rc = 0;
+ Scsp_server *ssp;
+
+ /*
+ * Get the current network interface address
+ */
+ ssp = current_server;
+ if (!ssp) {
+ parse_error("Server not found");
+ rc++;
+ }
+
+ /*
+ * Mark the interface as processed
+ */
+ ssp->ss_mark = 1;
+
+ /*
+ * Make sure the interface has been configured
+ */
+ if (ssp->ss_intf == (char *)0) {
+ parse_error("netif missing from server specification");
+ rc++;
+ }
+
+ /*
+ * Make sure the protocol is set
+ */
+ if (ssp->ss_pid == 0) {
+ parse_error("protocol missing from server specification");
+ rc++;
+ }
+
+ /*
+ * Make sure the server group is set
+ */
+ if (ssp->ss_sgid == 0) {
+ parse_error("server group ID missing from server specification");
+ rc++;
+ }
+
+ /*
+ * Make sure at least one DCS is configured
+ */
+ if (ssp->ss_dcs == (Scsp_dcs *)0) {
+ parse_error("no DCS configured for server");
+ rc++;
+ }
+
+ /*
+ * Mark the end of the server
+ */
+ current_server = (Scsp_server *)0;
+
+ return(rc);
+}
+
+
+/*
+ * Configure log file for SCSP server
+ *
+ * This routine is called from yyparse() to process a log File command.
+ *
+ * Arguments:
+ * file name of logging file
+ *
+ * Returns:
+ * 0 success
+ * 1 error encountered
+ *
+ */
+int
+set_log_file(file)
+ char *file;
+{
+ /*
+ * Make sure we haven't already got a log file
+ */
+ if (scsp_log_file) {
+ parse_error("multiple log files specified");
+ return(1);
+ }
+
+ /*
+ * Open the file
+ */
+ scsp_log_file = fopen(file, "a");
+ if (!scsp_log_file) {
+ parse_error("can't open log file");
+ return(1);
+ }
+
+ return(0);
+}
diff --git a/usr.sbin/atm/scspd/scsp_config_lex.c b/usr.sbin/atm/scspd/scsp_config_lex.c
new file mode 100644
index 0000000..20002b2
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_config_lex.c
@@ -0,0 +1,530 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * Parse a configuration file into tokens
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netatm/port.h>
+#include <netatm/queue.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <libatm.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "scsp_msg.h"
+#include "scsp_if.h"
+#include "scsp_var.h"
+#include "scsp_config_parse.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+/*
+ * Global variables
+ */
+int parse_line = 1;
+
+/*
+ * Local definitions
+ */
+#define TOK_MAX_LEN 128
+
+/*
+ * Character classes
+ */
+#define CHAR_INVALID 0 /* Not allowed */
+#define CHAR_ALPHA 1 /* G-W, Y, Z */
+#define CHAR_HEX_DIGIT 2 /* A-F */
+#define CHAR_X 3 /* X */
+#define CHAR_0 4 /* '0' */
+#define CHAR_DIGIT 5 /* 1-9 */
+#define CHAR_SPACE 6 /* space, tab */
+#define CHAR_DECIMAL 7 /* period */
+#define CHAR_SLASH 8 /* slash */
+#define CHAR_ASTERISK 9 /* asterisk */
+#define CHAR_HASH 10 /* pound sign */
+#define CHAR_SPECIAL 11 /* semicolon, braces */
+#define CHAR_MISC 12 /* chars allowd in file names */
+#define CHAR_EOL 13 /* new line */
+#define CHAR_EOF 14 /* EOF */
+#define CHAR_CNT CHAR_EOF + 1
+
+/*
+ * Character class table (initialized by init_class_tbl())
+ */
+static char class_tbl[128];
+
+/*
+ * State table element structure
+ */
+struct state_entry {
+ int action;
+ int next;
+};
+
+/*
+ * Scanner states
+ */
+#define TS_INIT 0
+#define TS_ALPHA 1
+#define TS_INT_1 2
+#define TS_INT 3
+#define TS_HEX 4
+#define TS_SLASH_1 5
+#define TS_COMMENT 6
+#define TS_COMMENT_1 7
+#define TS_FLUSH 8
+#define TS_HEX_1 9
+#define TS_CNT TS_HEX_1 + 1
+
+/*
+ * Token scanner state table
+ */
+static struct state_entry token_state_tbl[CHAR_CNT][TS_CNT] = {
+/* 0 1 2 3 4 5 6 7 8 9 */
+/* bad */{{2,0},{2,0},{2,0},{2,0},{2,0},{2,0},{0,6},{0,6},{0,8},{2,0}},
+/* g-z */{{1,1},{1,1},{1,1},{1,1},{2,0},{1,1},{0,6},{0,6},{0,8},{2,0}},
+/* a-f */{{1,1},{1,1},{1,1},{1,1},{1,9},{1,1},{0,6},{0,6},{0,8},{1,4}},
+/* x */{{1,1},{1,1},{1,4},{1,4},{2,0},{1,1},{0,6},{0,6},{0,8},{2,0}},
+/* 0 */{{1,2},{1,1},{1,3},{1,3},{1,9},{1,1},{0,6},{0,6},{0,8},{1,4}},
+/* 1-9 */{{1,3},{1,1},{1,3},{1,3},{1,9},{1,1},{0,6},{0,6},{0,8},{1,4}},
+/* sp */{{0,0},{6,0},{8,0},{8,0},{7,0},{6,0},{0,6},{0,6},{0,8},{2,0}},
+/* . */{{2,0},{1,1},{1,1},{1,1},{1,4},{1,1},{0,6},{0,6},{0,8},{2,0}},
+/* / */{{1,5},{1,1},{1,1},{1,1},{7,0},{4,8},{0,6},{0,0},{0,8},{2,0}},
+/* * */{{2,0},{6,0},{8,0},{8,0},{7,0},{4,6},{0,7},{0,7},{0,8},{2,0}},
+/* # */{{0,8},{6,0},{8,0},{8,0},{7,0},{6,0},{0,6},{0,6},{0,8},{2,0}},
+/* ;{} */{{3,0},{6,0},{8,0},{8,0},{7,0},{6,0},{0,6},{0,6},{0,8},{2,0}},
+/* Msc */{{2,0},{1,1},{1,1},{1,1},{2,0},{1,1},{0,6},{0,6},{0,8},{2,0}},
+/* EOL */{{0,0},{6,0},{8,0},{8,0},{7,0},{6,0},{0,6},{0,6},{0,0},{2,0}},
+/* EOF */{{9,0},{6,0},{8,0},{8,0},{7,0},{6,0},{2,0},{2,0},{9,0},{2,0}},
+};
+
+
+/*
+ * Reserved words
+ */
+static struct {
+ char *word;
+ int token;
+} rsvd_word_tbl[] = {
+ { "ATMaddr", TOK_DCS_ADDR },
+ { "ATMARP", TOK_ATMARP },
+ { "CAReXmitInt", TOK_DCS_CA_REXMIT_INT },
+ { "CSUSReXmitInt", TOK_DCS_CSUS_REXMIT_INT },
+ { "CSUReXmitInt", TOK_DCS_CSU_REXMIT_INT },
+ { "CSUReXmitMax", TOK_DCS_CSU_REXMIT_MAX },
+ { "DCS", TOK_DCS },
+ { "DHCP", TOK_DHCP },
+ { "familyID", TOK_FAMILY },
+ { "file", TOK_LFN },
+ { "hops", TOK_DCS_HOP_CNT },
+ { "HelloDead", TOK_DCS_HELLO_DF },
+ { "HelloInt", TOK_DCS_HELLO_INT },
+ { "ID", TOK_DCS_ID },
+ { "LNNI", TOK_LNNI },
+ { "log", TOK_LOG },
+ { "MARS", TOK_MARS },
+ { "netif", TOK_NETIF },
+ { "NHRP", TOK_NHRP },
+ { "protocol", TOK_PROTOCOL },
+ { "server", TOK_SERVER },
+ { "ServerGroupID", TOK_SRVGRP },
+ { "syslog", TOK_SYSLOG },
+ { (char *)0, 0 },
+};
+
+
+/*
+ * Copy a character string
+ *
+ * Make a copy of a character string, using strdup. If strdup fails,
+ * meaning we're out of memory, then print an error message and exit.
+ *
+ * Arguments:
+ * s string to be copied
+ *
+ * Returns:
+ * char * pointer to area provided by strdup
+ *
+ */
+static char *
+copy_buffer(s)
+ char *s;
+{
+ char *t;
+
+ t = strdup(s);
+
+ if (!t) {
+ fprintf(stderr, "%s: strdup failed\n", prog);
+ exit(1);
+ }
+
+ return(t);
+}
+
+
+/*
+ * Push a character back onto the input stream.
+ *
+ * Arguments:
+ * c character to be pushed
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+push_char(c)
+ char c;
+{
+ if (c == '\n')
+ parse_line--;
+
+ ungetc(c, cfg_file);
+}
+
+
+/*
+ * Initialize the character class table.
+ *
+ * Set each entry in the character class table to the class
+ * corresponding to the character.
+ *
+ * Arguments:
+ * tbl pointer to table to be initialized
+ *
+ * Returns:
+ * None
+ */
+static void
+init_class_tbl(tbl)
+ char *tbl;
+{
+ int i;
+ char c;
+
+ /*
+ * Set up the table for all ASCII characters
+ */
+ for (i=0; isascii((char)i); i++) {
+ /*
+ * Clear entry
+ */
+ tbl[i] = CHAR_INVALID;
+
+ /*
+ * Set entries depending on character type
+ */
+ c = (char)i;
+ if (c == 'a' || c == 'b' || c == 'c' ||
+ c == 'd' || c == 'e' || c == 'f' ||
+ c == 'A' || c == 'B' || c == 'C' ||
+ c == 'D' || c == 'E' || c == 'F')
+ tbl[i] = CHAR_HEX_DIGIT;
+ else if (c == 'x' || c == 'X')
+ tbl[i] = CHAR_X;
+ else if (isalpha(c))
+ tbl[i] = CHAR_ALPHA;
+ else if (c == '0')
+ tbl[i] = CHAR_0;
+ else if (isdigit(c))
+ tbl[i] = CHAR_DIGIT;
+ else if (c == '\n')
+ tbl[i] = CHAR_EOL;
+ else if (c == ' ' || c == '\t')
+ tbl[i] = CHAR_SPACE;
+ else if (c == '#')
+ tbl[i] = CHAR_HASH;
+ else if (c == '*')
+ tbl[i] = CHAR_ASTERISK;
+ else if (c == '.')
+ tbl[i] = CHAR_DECIMAL;
+ else if (c == '/')
+ tbl[i] = CHAR_SLASH;
+ else if (c == ';' || c == '{' || c == '}')
+ tbl[i] = CHAR_SPECIAL;
+ else if (c == '-' || c == '_' || c == '&' || c == '@' ||
+ c == '~')
+ tbl[i] = CHAR_MISC;
+ }
+}
+
+
+/*
+ * Get the class of a character.
+ *
+ * Arguments:
+ * c character being scanned
+ *
+ * Returns:
+ * int character class
+ */
+static int
+char_class(c)
+ char c;
+{
+ int class = CHAR_INVALID;
+
+ if (c == EOF) {
+ class = CHAR_EOF;
+ } else if (c < 0 || !isascii(c)) {
+ class = CHAR_INVALID;
+ } else {
+ class = class_tbl[(int)c];
+ }
+
+ return(class);
+}
+
+
+/*
+ * Print an error message when the scanner finds an error
+ *
+ * Arguments:
+ * c character on which the error was recognized
+ * state scanner state at error
+ *
+ * Returns:
+ * None
+ */
+static void
+scan_error(c, state)
+ char c;
+ int state;
+{
+ /*
+ * Check for invalid character
+ */
+ if (char_class(c) == CHAR_INVALID) {
+ parse_error("Invalid character 0x%x encountered",
+ c);
+ return;
+ }
+
+ /*
+ * Check for unexpected EOF
+ */
+ if (char_class(c) == CHAR_EOF) {
+ parse_error("Unexpected end of file");
+ return;
+ }
+
+ /*
+ * Error depends on state
+ */
+ switch(state) {
+ case TS_INIT:
+ parse_error("Syntax error at '%c'", c);
+ break;
+ case TS_ALPHA:
+ case TS_INT_1:
+ case TS_INT:
+ case TS_SLASH_1:
+ case TS_COMMENT:
+ case TS_COMMENT_1:
+ case TS_FLUSH:
+ parse_error("Syntax error");
+ break;
+ case TS_HEX:
+ case TS_HEX_1:
+ parse_error("Syntax error in hex string");
+ break;
+ }
+}
+
+
+/*
+ * Assemble a token
+ *
+ * Read a character at a time from the input file, assembling the
+ * characters into tokens as specified by the token scanner state
+ * table. Return the completed token.
+ *
+ * Arguments:
+ * None
+ *
+ * Returns:
+ * token the type of the token found
+ */
+int
+yylex()
+{
+ int i, state;
+ char c, token_buffer[TOK_MAX_LEN];
+
+ /*
+ * Initialize
+ */
+ if (class_tbl['A'] != CHAR_HEX_DIGIT)
+ init_class_tbl(class_tbl);
+ state = TS_INIT;
+ bzero(token_buffer, sizeof(token_buffer));
+ bzero(&yylval, sizeof(yylval));
+
+ /*
+ * Handle a character at a time until a token is built
+ */
+ while(1) {
+ /*
+ * Read a character from the input file.
+ */
+ c = (char)getc(cfg_file);
+ if (c == '\n') {
+ parse_line++;
+ }
+
+#ifdef NOTDEF
+ printf("token_state: state=%d, char=%c, class=%d, action=%d, next=%d\n",
+ state,
+ c,
+ char_class(c),
+ token_state_tbl[char_class][state].action,
+ token_state_tbl[char_class][state].next);
+#endif
+
+ /*
+ * Perform an action based on the state table
+ */
+ switch(token_state_tbl[char_class(c)][state].action) {
+ case 0:
+ /*
+ * Ignore the character
+ */
+ break;
+ case 1:
+ /*
+ * Add character to buffer
+ */
+ if (strlen(token_buffer) < TOK_MAX_LEN) {
+ token_buffer[strlen(token_buffer)] = c;
+ }
+ break;
+ case 2:
+ /*
+ * Error--print a message and start over
+ */
+ scan_error(c, state);
+ break;
+ case 3:
+ /*
+ * Return special character
+ */
+ return(c);
+ break;
+ case 4:
+ /*
+ * Clear the token buffer
+ */
+ bzero(token_buffer, sizeof(token_buffer));
+ break;
+ case 5:
+ /*
+ * Not used
+ */
+ break;
+ case 6:
+ /*
+ * Return character token
+ */
+ push_char(c);
+
+ /*
+ * Check for reserved words
+ */
+ for (i=0; rsvd_word_tbl[i].word; i++) {
+ if (strcasecmp(token_buffer,
+ rsvd_word_tbl[i].word) == 0)
+ break;
+ }
+ if (rsvd_word_tbl[i].word) {
+ return(rsvd_word_tbl[i].token);
+ }
+
+ /*
+ * Word isn't reserved, return alpha string
+ */
+ yylval.tv_alpha = copy_buffer(token_buffer);
+ return(TOK_NAME);
+ break;
+ case 7:
+ /*
+ * Return hex string (ATM address)
+ */
+ push_char(c);
+ yylval.tv_hex = copy_buffer(token_buffer);
+ return(TOK_HEX);
+ break;
+ case 8:
+ /*
+ * Return integer
+ */
+ push_char(c);
+ yylval.tv_int = atoi(token_buffer);
+ return(TOK_INTEGER);
+ break;
+ case 9:
+ /*
+ * Return EOF
+ */
+ return(0);
+ break;
+ default:
+ fprintf(stderr, "Invalid action indicator, state=%d, char=0x%02x\n",
+ state, c);
+ break;
+ }
+
+ /*
+ * Set the next state and bump to the next character
+ */
+ state = token_state_tbl[char_class(c)][state].next;
+ }
+}
diff --git a/usr.sbin/atm/scspd/scsp_config_parse.y b/usr.sbin/atm/scspd/scsp_config_parse.y
new file mode 100644
index 0000000..168974e
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_config_parse.y
@@ -0,0 +1,412 @@
+%{
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * YACC input for configuration file processing
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netatm/port.h>
+#include <netatm/queue.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+
+#include <libatm.h>
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "scsp_msg.h"
+#include "scsp_if.h"
+#include "scsp_var.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+void yyerror __P((char *));
+%}
+
+
+/*
+ * Token value definition
+ */
+%union {
+ char *tv_alpha;
+ int tv_int;
+ char *tv_hex;
+}
+
+
+/*
+ * Token types returned by scanner
+ */
+%token <tv_alpha> TOK_NAME
+%token <tv_int> TOK_INTEGER
+%token <tv_hex> TOK_HEX
+
+/*
+ * Reserved words
+ */
+%token TOK_ATMARP
+%token TOK_DCS
+%token TOK_DCS_ADDR
+%token TOK_DCS_CA_REXMIT_INT
+%token TOK_DCS_CSUS_REXMIT_INT
+%token TOK_DCS_CSU_REXMIT_INT
+%token TOK_DCS_CSU_REXMIT_MAX
+%token TOK_DCS_HELLO_DF
+%token TOK_DCS_HELLO_INT
+%token TOK_DCS_HOP_CNT
+%token TOK_DCS_ID
+%token TOK_DHCP
+%token TOK_FAMILY
+%token TOK_LFN
+%token TOK_LNNI
+%token TOK_LOG
+%token TOK_MARS
+%token TOK_NETIF
+%token TOK_NHRP
+%token TOK_PROTOCOL
+%token TOK_SERVER
+%token TOK_SRVGRP
+%token TOK_SYSLOG
+
+
+%%
+cfg_file: /* Empty */
+ | stmt_seq
+
+stmt_seq: stmt
+ | stmt_seq stmt
+ ;
+
+stmt: server_stmt ';'
+ | log_stmt ';'
+ ;
+
+/*
+ * SCSP server definition statements
+ */
+server_stmt: TOK_SERVER TOK_NAME
+ {
+ int rc;
+
+ rc = start_server($2);
+ free($2);
+ if (rc)
+ return(rc);
+ }
+ '{' server_def '}'
+ {
+ int rc;
+
+ rc = finish_server();
+ if (rc)
+ return(rc);
+ }
+ ;
+
+server_def: server_spec ';'
+ | server_def server_spec ';'
+ ;
+
+server_spec: /* Nothing */
+ | dcs_stmt
+ | TOK_NETIF TOK_NAME
+ {
+ int rc;
+
+ /*
+ * Configure the network interface
+ */
+ rc = set_intf($2);
+ free($2);
+ if (rc)
+ return(rc);
+ }
+ | TOK_PROTOCOL TOK_ATMARP
+ {
+ int rc;
+
+ /*
+ * Configure the protocol
+ */
+ rc = set_protocol(SCSP_PROTO_ATMARP);
+ if (rc)
+ return(rc);
+ }
+ | TOK_PROTOCOL TOK_DHCP | TOK_LNNI | TOK_MARS | TOK_NHRP
+ {
+ yyerror("Protocol not implemented");
+ return(1);
+ }
+ | TOK_SRVGRP TOK_INTEGER
+ {
+ int rc;
+
+ /*
+ * Configure the SCSP server group ID
+ */
+ rc = set_server_group($2);
+ if (rc)
+ return(rc);
+ }
+ ;
+
+/*
+ * SCSP DCS definition statements
+ */
+dcs_stmt: TOK_DCS
+ {
+ int rc;
+
+ rc = start_dcs();
+ if (rc)
+ return(rc);
+ }
+ '{' dcs_def '}'
+ {
+ int rc;
+
+ rc = finish_dcs();
+ if (rc)
+ return(rc);
+ }
+ ;
+
+dcs_def: dcs_spec ';'
+ | dcs_def dcs_spec ';'
+ ;
+
+dcs_spec: /* Nothing */
+ | TOK_DCS_ADDR TOK_HEX
+ {
+ int rc;
+
+ /*
+ * Set DCS address
+ */
+ rc = set_dcs_addr($2, (char *)0);
+ free($2);
+ if (rc)
+ return(rc);
+ }
+ | TOK_DCS_ADDR TOK_HEX TOK_HEX
+ {
+ int rc;
+
+ /*
+ * Set DCS address and subaddress
+ */
+ rc = set_dcs_addr($2, $3);
+ free($2);
+ free($3);
+ if (rc)
+ return(rc);
+ }
+ | TOK_DCS_CA_REXMIT_INT TOK_INTEGER
+ {
+ int rc;
+
+ /*
+ * Configure the CA retransmit interval
+ */
+ rc = set_dcs_ca_rexmit($2);
+ if (rc)
+ return(rc);
+ }
+ | TOK_DCS_CSUS_REXMIT_INT TOK_INTEGER
+ {
+ int rc;
+
+ /*
+ * Configure the CSUS retransmit interval
+ */
+ rc = set_dcs_csus_rexmit($2);
+ if (rc)
+ return(rc);
+ }
+ | TOK_DCS_CSU_REXMIT_INT TOK_INTEGER
+ {
+ int rc;
+
+ /*
+ * Configure the CSU retransmit interval
+ */
+ rc = set_dcs_csu_rexmit($2);
+ if (rc)
+ return(rc);
+ }
+ | TOK_DCS_CSU_REXMIT_MAX TOK_INTEGER
+ {
+ int rc;
+
+ /*
+ * Configure the CSU retransmit limit
+ */
+ rc = set_dcs_csu_rexmit_max($2);
+ if (rc)
+ return(rc);
+ }
+ | TOK_DCS_HELLO_DF TOK_INTEGER
+ {
+ int rc;
+
+ /*
+ * Configure the Hello dead factor
+ */
+ rc = set_dcs_hello_df($2);
+ if (rc)
+ return(rc);
+ }
+ | TOK_DCS_HELLO_INT TOK_INTEGER
+ {
+ int rc;
+
+ /*
+ * Configure the Hello interval
+ */
+ rc = set_dcs_hello_int($2);
+ if (rc)
+ return(rc);
+ }
+ | TOK_DCS_HOP_CNT TOK_INTEGER
+ {
+ int rc;
+
+ /*
+ * Configure the hop count
+ */
+ rc = set_dcs_hops($2);
+ if (rc)
+ return(rc);
+ }
+ | TOK_DCS_ID TOK_NAME
+ {
+ int rc;
+
+ /*
+ * Configure the DCS ID
+ */
+ rc = set_dcs_id($2);
+ free($2);
+ if (rc)
+ return(rc);
+ }
+ ;
+
+
+/*
+ * Logging option statements
+ */
+log_stmt: TOK_LOG
+ '{' log_spec '}'
+ ;
+
+log_spec: /* Nothing */
+ | TOK_LFN TOK_NAME ';'
+ {
+ /*
+ * Configure the log file name
+ */
+ int rc;
+
+ rc = set_log_file($2);
+ free($2);
+ if (rc)
+ return(rc);
+ }
+ ;
+ | TOK_SYSLOG ';'
+ {
+ /*
+ * Configure logging to syslog
+ */
+ scsp_log_syslog = 1;
+ }
+ ;
+
+%%
+
+void
+#if __STDC__
+parse_error(const char *fmt, ...)
+#else
+parse_error(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+ char buff[256];
+
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+
+ vsprintf(buff, fmt, ap);
+ scsp_log(LOG_ERR, "%s: Config file error at line %d: %s\n",
+ prog, parse_line, buff);
+#ifdef NOTDEF
+ fprintf(stderr, "%s: Config file error at line %d: %s\n",
+ prog, parse_line, buff);
+#endif
+ va_end(ap);
+}
+
+
+void
+yyerror(s)
+ char *s;
+{
+ parse_error(s);
+}
diff --git a/usr.sbin/atm/scspd/scsp_hfsm.c b/usr.sbin/atm/scspd/scsp_hfsm.c
new file mode 100644
index 0000000..d1bec16
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_hfsm.c
@@ -0,0 +1,577 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * HELLO finite state machine
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netatm/queue.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+
+#include <errno.h>
+#include <libatm.h>
+#include <stdio.h>
+#include <syslog.h>
+
+#include "scsp_msg.h"
+#include "scsp_if.h"
+#include "scsp_var.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+/*
+ * HELLO FSM actions
+ */
+#define HELLO_ACTION_CNT 7
+int scsp_hello_act_00 __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;
+
+ /*
+ * Cancel any current timers
+ */
+ HARP_CANCEL(&dcsp->sd_hello_h_t);
+ HARP_CANCEL(&dcsp->sd_hello_rcv_t);
+
+ /*
+ * Log the loss of the VCC
+ */
+ if (dcsp->sd_hello_state > SCSP_HFSM_WAITING) {
+ scsp_log(LOG_ERR, "VC to %s closed",
+ format_atm_addr(&dcsp->sd_addr));
+ }
+
+ /*
+ * Tell the CA FSM that the conection to the DCS is lost
+ */
+ rc = scsp_cafsm(dcsp, SCSP_CAFSM_HELLO_DOWN, (void *)0);
+
+ /*
+ * Go to Down state
+ */
+ dcsp->sd_hello_state = SCSP_HFSM_DOWN;
+
+ /*
+ * If our ID is lower than the DCS's, wait a second before
+ * trying to connect. This should keep both of us from
+ * trying to connect at the same time, resulting in two
+ * VCCs being open.
+ */
+ if (scsp_cmp_id(&dcsp->sd_server->ss_lsid,
+ &dcsp->sd_dcsid) < 0) {
+ /*
+ * Our ID is lower--start the VCC open timer for one
+ * second so we'll try to open the VCC if the DCS
+ * doesn't do it by then
+ */
+ HARP_TIMER(&dcsp->sd_open_t, 1, scsp_open_timeout);
+ } else {
+ /*
+ * Our ID is higher--try to reopen the VCC immediately
+ */
+ if (scsp_dcs_connect(dcsp)) {
+ /*
+ * Conncect failed -- set a timer and try
+ * again later
+ */
+ HARP_TIMER(&dcsp->sd_open_t, SCSP_Open_Interval,
+ scsp_open_timeout);
+ }
+ }
+
+ return(0);
+}
+
+
+/*
+ * HELLO finite state machine action 3
+ * Hello timer expired -- send HELLO message, restart hello timer
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * msg pointer to received message (ignored)
+ *
+ * Returns:
+ * 0 success
+ * errno error encountered
+ *
+ */
+int
+scsp_hello_act_03(dcsp, msg)
+ Scsp_dcs *dcsp;
+ Scsp_msg *msg;
+{
+ int rc;
+
+ /*
+ * Send a Hello message
+ */
+ rc = scsp_send_hello(dcsp);
+ if (rc == 0) {
+ /*
+ * Success--restart the Hello timer
+ */
+ HARP_TIMER(&dcsp->sd_hello_h_t, SCSP_HELLO_Interval,
+ scsp_hello_timeout);
+ }
+
+ return(rc);
+}
+
+
+/*
+ * HELLO finite state machine action 4
+ * Receive timer expired -- if we haven't received any Hellos, notify
+ * CA FSM and go to Waiting state; if we've received Hellos, but we
+ * weren't in the receiver ID list, go to Unidirectional state
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * msg pointer to received message (ignored)
+ *
+ * Returns:
+ * 0 success
+ * errno error encountered
+ *
+ */
+int
+scsp_hello_act_04(dcsp, msg)
+ Scsp_dcs *dcsp;
+ Scsp_msg *msg;
+{
+ int rc = 0;
+
+ /*
+ * Check whether we'ver received any Hellos lately
+ */
+ if (dcsp->sd_hello_rcvd) {
+ /*
+ * We've had Hellos since the receive timer was
+ * started--go to Unidirectional state
+ */
+ dcsp->sd_hello_rcvd = 0;
+ dcsp->sd_hello_state = SCSP_HFSM_UNI_DIR;
+ } else {
+ /*
+ * We haven't seen any Hellos at all from the DCS in
+ * hello_interval * dead_factor seconds--go to Waiting
+ * state
+ */
+ dcsp->sd_hello_state = SCSP_HFSM_WAITING;
+ }
+
+ /*
+ * Notify the CA FSM
+ */
+ rc = scsp_cafsm(dcsp, SCSP_CAFSM_HELLO_DOWN, (void *)0);
+
+ return(rc);
+}
+
+
+/*
+ * HELLO finite state machine action 5
+ * Message received -- Ignore all but HELLO messages; if local server
+ * is in receiver list, notify CA FSM and go to Bidirectional state;
+ * otherwise, go to Unidirectional state
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * msg pointer to received message
+ *
+ * Returns:
+ * 0 success
+ * errno error encountered
+ *
+ */
+int
+scsp_hello_act_05(dcsp, msg)
+ Scsp_dcs *dcsp;
+ Scsp_msg *msg;
+{
+ int rc;
+ Scsp_id *ridp;
+
+ /*
+ * Null message pointer means message decode failed, so
+ * message must have been invalid. Go to Waiting state.
+ */
+ if (msg == (Scsp_msg *)0) {
+ dcsp->sd_hello_state = SCSP_HFSM_WAITING;
+ HARP_CANCEL(&dcsp->sd_hello_rcv_t);
+ return(0);
+ }
+
+ /*
+ * Ignore the message if it isn't a Hello
+ */
+ if (msg->sc_msg_type != SCSP_HELLO_MSG) {
+ return(0);
+ }
+
+ /*
+ * Save relevant information about DCS, but don't let him give
+ * us zero for timeout values
+ */
+ if (msg->sc_hello->hello_int) {
+ dcsp->sd_hello_int = msg->sc_hello->hello_int;
+ } else {
+ dcsp->sd_hello_int = 1;
+ }
+ if (msg->sc_hello->dead_factor) {
+ dcsp->sd_hello_df = msg->sc_hello->dead_factor;
+ } else {
+ dcsp->sd_hello_df = 1;
+ }
+ dcsp->sd_dcsid = msg->sc_hello->hello_mcp.sid;
+
+ /*
+ * Check the message for the local server's ID
+ */
+ for (ridp = &msg->sc_hello->hello_mcp.rid;
+ ridp;
+ ridp = ridp->next) {
+ if (scsp_cmp_id(&dcsp->sd_server->ss_lsid, ridp) == 0) {
+ /*
+ * Cancel and restart the receive timer
+ */
+ HARP_CANCEL(&dcsp->sd_hello_rcv_t);
+ HARP_TIMER(&dcsp->sd_hello_rcv_t,
+ dcsp->sd_hello_int * dcsp->sd_hello_df,
+ scsp_hello_rcv_timeout);
+
+ /*
+ * Go to Bidirectional state and notify the
+ * CA FSM that the connection is up
+ */
+ dcsp->sd_hello_state = SCSP_HFSM_BI_DIR;
+ rc = scsp_cafsm(dcsp,
+ SCSP_CAFSM_HELLO_UP,
+ (void *)0);
+ return(rc);
+ }
+ }
+
+ /*
+ * We weren't in the receiver ID list, so go to
+ * Unidirectional state
+ */
+ dcsp->sd_hello_state = SCSP_HFSM_UNI_DIR;
+
+ return(0);
+}
+
+
+/*
+ * HELLO finite state machine action 6
+ * Message received -- if message is not a HELLO, pass it to the CA
+ * FSM; otherwise, if local server is not in receiver list, notify
+ * CA FSM and go to Unidirectional state
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * msg pointer to received message
+ *
+ * Returns:
+ * 0 success
+ * errno error encountered
+ *
+ */
+int
+scsp_hello_act_06(dcsp, msg)
+ Scsp_dcs *dcsp;
+ Scsp_msg *msg;
+{
+ int rc = 0, rcv_found;
+ Scsp_id *ridp;
+
+ /*
+ * Null message pointer means message decode failed, so
+ * message must have been invalid. Go to Waiting state.
+ */
+ if (msg == (Scsp_msg *)0) {
+ HARP_CANCEL(&dcsp->sd_hello_rcv_t);
+ dcsp->sd_hello_state = SCSP_HFSM_WAITING;
+ rc = scsp_cafsm(dcsp, SCSP_CAFSM_HELLO_DOWN, (void *)0);
+ return(rc);
+ }
+
+ /*
+ * Process the message depending on its type
+ */
+ switch(msg->sc_msg_type) {
+ case SCSP_CA_MSG:
+ rc = scsp_cafsm(dcsp, SCSP_CAFSM_CA_MSG, (void *)msg);
+ break;
+ case SCSP_CSU_REQ_MSG:
+ rc = scsp_cafsm(dcsp, SCSP_CAFSM_CSU_REQ, (void *)msg);
+ break;
+ case SCSP_CSU_REPLY_MSG:
+ rc = scsp_cafsm(dcsp, SCSP_CAFSM_CSU_REPLY,
+ (void *)msg);
+ break;
+ case SCSP_CSUS_MSG:
+ rc = scsp_cafsm(dcsp, SCSP_CAFSM_CSUS_MSG, (void *)msg);
+ break;
+ case SCSP_HELLO_MSG:
+ /*
+ * Make sure DCS info is consistent. The sender ID,
+ * family ID, protocol ID, and server group ID are
+ * checked.
+ */
+ if (scsp_cmp_id(&msg->sc_hello->hello_mcp.sid,
+ &dcsp->sd_dcsid) ||
+ (msg->sc_hello->family_id !=
+ dcsp->sd_server->ss_fid) ||
+ (msg->sc_hello->hello_mcp.pid !=
+ dcsp->sd_server->ss_pid) ||
+ (msg->sc_hello->hello_mcp.sgid !=
+ dcsp->sd_server->ss_sgid)) {
+ /*
+ * Bad info--revert to waiting state
+ */
+ HARP_CANCEL(&dcsp->sd_hello_rcv_t);
+ dcsp->sd_hello_state = SCSP_HFSM_WAITING;
+ rc = scsp_cafsm(dcsp,
+ SCSP_CAFSM_HELLO_DOWN,
+ (void *)0);
+ return(rc);
+ }
+
+ /*
+ * Mark the arrival of the Hello message
+ */
+ dcsp->sd_hello_rcvd = 1;
+
+ /*
+ * Check the message for the local server's ID
+ */
+ for (ridp = &msg->sc_hello->hello_mcp.rid,
+ rcv_found = 0;
+ ridp;
+ ridp = ridp->next) {
+ rcv_found = (scsp_cmp_id(ridp,
+ &dcsp->sd_server->ss_lsid) == 0);
+ }
+
+ if (rcv_found) {
+ /*
+ * The LS ID was in the list of receiver IDs--
+ * Reset the Hello receive timer
+ */
+ dcsp->sd_hello_rcvd = 0;
+ HARP_CANCEL(&dcsp->sd_hello_rcv_t);
+ HARP_TIMER(&dcsp->sd_hello_rcv_t,
+ dcsp->sd_hello_int *
+ dcsp->sd_hello_df,
+ scsp_hello_rcv_timeout);
+ }
+ break;
+ }
+
+ return(rc);
+}
diff --git a/usr.sbin/atm/scspd/scsp_if.c b/usr.sbin/atm/scspd/scsp_if.c
new file mode 100644
index 0000000..18638c2
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_if.c
@@ -0,0 +1,637 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * Interface to client server protocol
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netatm/port.h>
+#include <netatm/queue.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+
+#include <errno.h>
+#include <libatm.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "scsp_msg.h"
+#include "scsp_if.h"
+#include "scsp_var.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+/*
+ * SCSP client server interface FSM actions
+ */
+#define SCSP_CIFSM_ACTION_CNT 11
+int scsp_client_act_00
+ __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;
+ Scsp_cse *csep, *ncsep;
+
+ /*
+ * Copy the cache summmary to the CSAS list
+ */
+ for (i = 0; i < SCSP_HASHSZ; i++) {
+ for (csep = dcsp->sd_server->ss_cache[i]; csep;
+ csep = csep->sc_next) {
+ ncsep = scsp_dup_cse(csep);
+ LINK2TAIL(ncsep, Scsp_cse, dcsp->sd_ca_csas,
+ sc_next);
+ }
+ }
+
+ /*
+ * Set the new state
+ */
+ dcsp->sd_client_state = SCSP_CIFSM_SUM;
+
+ return(0);
+}
+
+
+/*
+ * SCSP client server interface finite state machine action 6
+ *
+ * CA FSM went to Aligned state--go to Aligned
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * msg pointer to message from DCS
+ * cmsg pointer to message from server
+ *
+ * Returns:
+ * 0 success
+ * else errno describing error
+ *
+ */
+int
+scsp_client_act_06(dcsp, msg, cmsg)
+ Scsp_dcs *dcsp;
+ Scsp_msg *msg;
+ Scsp_if_msg *cmsg;
+{
+ /*
+ * Set the new state
+ */
+ dcsp->sd_client_state = SCSP_CIFSM_ALIGN;
+
+ return(0);
+}
+
+
+/*
+ * SCSP client server interface finite state machine action 7
+ *
+ * We received a Solicit Rsp or Update Req from the server--pass it
+ * to the CA FSM
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * msg pointer to message from DCS
+ * cmsg pointer to message from server
+ *
+ * Returns:
+ * 0 success
+ * else errno describing error
+ *
+ */
+int
+scsp_client_act_07(dcsp, msg, cmsg)
+ Scsp_dcs *dcsp;
+ Scsp_msg *msg;
+ Scsp_if_msg *cmsg;
+{
+ int rc;
+ Scsp_csa *csap;
+ Scsp_atmarp_csa *acp;
+
+ /*
+ * Allocate memory for a CSA record
+ */
+ csap = calloc(1, sizeof(Scsp_csa));
+ if (csap == NULL)
+ scsp_mem_err("scsp_client_act_07: sizeof(Scsp_csa)");
+ acp = calloc(1, sizeof(Scsp_atmarp_csa));
+ if (acp == NULL)
+ scsp_mem_err("scsp_client_act_07: sizeof(Scsp_atmarp_csa)");
+
+ /*
+ * Build a CSA record from the server's message
+ */
+ csap->hops = dcsp->sd_hops;
+ csap->null = (cmsg->si_atmarp.sa_state == SCSP_ASTATE_DEL) ||
+ (cmsg->si_type == SCSP_SOLICIT_RSP &&
+ cmsg->si_rc != SCSP_RSP_OK);
+ csap->seq = cmsg->si_atmarp.sa_seq;
+ csap->key = cmsg->si_atmarp.sa_key;
+ csap->oid = cmsg->si_atmarp.sa_oid;
+ csap->atmarp_data = acp;
+ acp->sa_state = cmsg->si_atmarp.sa_state;
+ acp->sa_sha = cmsg->si_atmarp.sa_cha;
+ acp->sa_ssa = cmsg->si_atmarp.sa_csa;
+ acp->sa_spa = cmsg->si_atmarp.sa_cpa;
+ acp->sa_tpa = cmsg->si_atmarp.sa_cpa;
+
+ /*
+ * Call the CA FSM
+ */
+ rc = scsp_cafsm(dcsp, SCSP_CAFSM_CACHE_UPD, (void *)csap);
+
+ return(rc);
+}
+
+
+/*
+ * SCSP client server interface finite state machine action 8
+ *
+ * Update Rsp from server--pass the update to the CA FSM.
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * msg pointer to message from DCS
+ * cmsg pointer to message from server
+ *
+ * Returns:
+ * 0 success
+ * else errno describing error
+ *
+ */
+int
+scsp_client_act_08(dcsp, msg, cmsg)
+ Scsp_dcs *dcsp;
+ Scsp_msg *msg;
+ Scsp_if_msg *cmsg;
+{
+ int rc;
+
+ /*
+ * Pass the response to the CA FSM
+ */
+ switch (dcsp->sd_server->ss_pid) {
+ case SCSP_PROTO_ATMARP:
+ rc = scsp_cafsm(dcsp, SCSP_CAFSM_CACHE_RSP, cmsg);
+ break;
+ default:
+ rc = EPROTONOSUPPORT;
+ }
+
+ return(rc);
+}
+
+
+/*
+ * SCSP client server interface finite state machine action 9
+ *
+ * CSU Solicit from DCS--pass Solicit Ind to server
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * msg pointer to message from DCS
+ * cmsg pointer to message from server
+ *
+ * Returns:
+ * 0 success
+ * else errno describing error
+ *
+ */
+int
+scsp_client_act_09(dcsp, msg, cmsg)
+ Scsp_dcs *dcsp;
+ Scsp_msg *msg;
+ Scsp_if_msg *cmsg;
+{
+ int rc, rrc = 0;
+ Scsp_csa *csap;
+ Scsp_if_msg *csip;
+
+ /*
+ * Get memory for a Solicit Ind
+ */
+ csip = calloc(1, sizeof(Scsp_if_msg));
+ if (csip == NULL)
+ scsp_mem_err("scsp_client_act_09: sizeof(Scsp_if_msg)");
+
+ /*
+ * Loop through list of CSAs
+ */
+ for (csap = msg->sc_csu_msg->csu_csa_rec; csap;
+ csap = csap->next) {
+ /*
+ * Fill out the Solicit Indication
+ */
+ bzero(csip, sizeof(Scsp_if_msg));
+ csip->si_type = SCSP_SOLICIT_IND;
+ csip->si_proto = dcsp->sd_server->ss_pid;
+ csip->si_tok = (u_long)dcsp;
+ csip->si_len = sizeof(Scsp_if_msg_hdr) +
+ sizeof(Scsp_sum_msg);
+ csip->si_sum.ss_hops = csap->hops;
+ csip->si_sum.ss_null = csap->null;
+ csip->si_sum.ss_seq = csap->seq;
+ csip->si_sum.ss_key = csap->key;
+ csip->si_sum.ss_oid = csap->oid;
+
+ /*
+ * Send the Solicit Ind to the server
+ */
+ rc = scsp_if_sock_write(dcsp->sd_server->ss_sock, csip);
+ if (rc) {
+ rrc = rc;
+ }
+ }
+ free(csip);
+ return(rrc);
+}
+
+
+/*
+ * SCSP client server interface finite state machine action 10
+ *
+ * CSU Request from DCS--pass it to the server as a Cache Update
+ * Indication
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * msg pointer to message from DCS
+ * cmsg pointer to message from server
+ *
+ * Returns:
+ * 0 success
+ * else errno describing error
+ *
+ */
+int
+scsp_client_act_10(dcsp, msg, cmsg)
+ Scsp_dcs *dcsp;
+ Scsp_msg *msg;
+ Scsp_if_msg *cmsg;
+{
+ int rc, rrc = 0;
+ Scsp_csa *csap;
+ Scsp_atmarp_csa *acp;
+ Scsp_if_msg *cuip;
+
+ /*
+ * Get memory for a Cache Update Ind
+ */
+ cuip = malloc(sizeof(Scsp_if_msg));
+ if (cuip == NULL)
+ scsp_mem_err("scsp_client_act_10: sizeof(Scsp_if_msg)");
+
+ /*
+ * Loop through CSAs in message
+ */
+ for (csap = msg->sc_csu_msg->csu_csa_rec; csap;
+ csap = csap->next) {
+ acp = csap->atmarp_data;
+ if (!acp)
+ continue;
+
+ /*
+ * Fill out the Cache Update Ind
+ */
+ bzero(cuip, sizeof(Scsp_if_msg));
+ cuip->si_type = SCSP_UPDATE_IND;
+ cuip->si_proto = dcsp->sd_server->ss_pid;
+ cuip->si_tok = (u_long)dcsp;
+ switch(dcsp->sd_server->ss_pid) {
+ case SCSP_PROTO_ATMARP:
+ cuip->si_len = sizeof(Scsp_if_msg_hdr) +
+ sizeof(Scsp_atmarp_msg);
+ cuip->si_atmarp.sa_state = acp->sa_state;
+ cuip->si_atmarp.sa_cpa = acp->sa_spa;
+ cuip->si_atmarp.sa_cha = acp->sa_sha;
+ cuip->si_atmarp.sa_csa = acp->sa_ssa;
+ cuip->si_atmarp.sa_key = csap->key;
+ cuip->si_atmarp.sa_oid = csap->oid;
+ cuip->si_atmarp.sa_seq = csap->seq;
+ break;
+ case SCSP_PROTO_NHRP:
+ /*
+ * Not implemented yet
+ */
+ break;
+ }
+
+ /*
+ * Send the Cache Update Ind to the server
+ */
+ rc = scsp_if_sock_write(dcsp->sd_server->ss_sock, cuip);
+ if (rc) {
+ rrc = rc;
+ }
+ }
+ free(cuip);
+ return(rrc);
+}
diff --git a/usr.sbin/atm/scspd/scsp_if.h b/usr.sbin/atm/scspd/scsp_if.h
new file mode 100644
index 0000000..844ee2c
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_if.h
@@ -0,0 +1,194 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * Interface to server clients of SCSP
+ *
+ */
+
+#ifndef _SCSP_SCSP_IF_H
+#define _SCSP_SCSP_IF_H
+
+
+/*
+ * SCSP configuration message
+ */
+struct scsp_cfg_msg {
+ char atmarp_netif[IFNAMSIZ];
+};
+typedef struct scsp_cfg_msg Scsp_cfg_msg;
+
+
+/*
+ * SCSP cache summary
+ */
+struct scsp_sum_msg {
+ u_short ss_hops; /* Hop count */
+ u_char ss_null; /* Null flag */
+ long ss_seq; /* CSA seq. no. */
+ Scsp_ckey ss_key; /* Cache key */
+ Scsp_id ss_oid; /* Originator ID */
+};
+typedef struct scsp_sum_msg Scsp_sum_msg;
+
+
+/*
+ * SCSP constants for ATMARP
+ */
+#define SCSP_ATMARP_PROTO 1
+#define SCSP_ATMARP_SIDL 4
+#define SCSP_ATMARP_RIDL 4
+#define SCSP_ATMARP_CKL 4
+#define SCSP_ATMARP_OIDL 4
+
+
+/*
+ * SCSP ATMARP message
+ */
+struct scsp_atmarp_msg {
+ u_char sa_state; /* Cache entry state (below) */
+ struct in_addr sa_cpa; /* Cached protocol address */
+ Atm_addr sa_cha; /* Cached ATM address */
+ Atm_addr sa_csa; /* Cached ATM subaddress */
+ Scsp_ckey sa_key; /* Cache key for entry */
+ Scsp_id sa_oid; /* Originator ID */
+ long sa_seq; /* Sequence no. */
+};
+typedef struct scsp_atmarp_msg Scsp_atmarp_msg;
+
+#define SCSP_ASTATE_NEW 0 /* ATMARP new server registration */
+#define SCSP_ASTATE_UPD 1 /* ATMARP server refreshed */
+#define SCSP_ASTATE_DEL 2 /* ATMARP server data deleted */
+
+
+/*
+ * SCSP constants for NHRP
+ */
+#define SCSP_NHRP_PROTO 2
+#define SCSP_NHRP_SIDL 4
+#define SCSP_NHRP_RIDL 4
+#define SCSP_NHRP_CKL 4
+#define SCSP_NHRP_OIDL 4
+
+
+/*
+ * SCSP NHRP message
+ */
+struct scsp_nhrp_msg {
+ u_short sn_af; /* Address family */
+ u_short sn_proto; /* NHRP protocol type */
+ u_char sn_snap[5]; /* SNAP */
+ u_char sn_ver; /* NHRP version number */
+ u_short sn_flags; /* Flags */
+ u_long sn_rid; /* Request ID */
+ u_char sn_state; /* State */
+ u_char sn_prel; /* Prefix length */
+ u_short sn_mtu; /* Maximum transmission unit */
+ u_short sn_hold; /* Holding time */
+ Atm_addr sn_addr; /* Server network address */
+ Atm_addr sn_saddr; /* Server network subaddress */
+ struct in_addr sn_paddr; /* Server protocol address */
+ Scsp_ckey sn_key; /* Cache key for entry */
+ Scsp_id sn_oid; /* Originator ID */
+};
+typedef struct scsp_nhrp_msg Scsp_nhrp_msg;
+
+#define SCSP_NSTATE_NEW 0 /* New NHRP server */
+#define SCSP_NSTATE_UPD 1 /* NHRP server re-registered */
+#define SCSP_NSTATE_DEL 2 /* NHRP server data purged */
+#define SCSP_NSTATE_NSD 3 /* NHRP no such data in server */
+
+
+/*
+ * SCSP/server message header
+ */
+struct scsp_if_msg_hdr {
+ u_char sh_type; /* Message type */
+ u_char sh_rc; /* Response code */
+ u_short sh_proto; /* SCSP protocol ID */
+ int sh_len; /* Length of message */
+ u_long sh_tok; /* Token from SCSP daemon */
+};
+typedef struct scsp_if_msg_hdr Scsp_if_msg_hdr;
+
+
+/*
+ * SCSP-server message
+ */
+struct scsp_if_msg {
+ Scsp_if_msg_hdr si_hdr; /* Header fields */
+ union {
+ Scsp_cfg_msg siu_cfg; /* Config data */
+ Scsp_sum_msg siu_sum; /* Cache summary */
+ Scsp_atmarp_msg siu_atmarp; /* ATMARP update */
+ Scsp_nhrp_msg siu_nhrp; /* NHRP update */
+ } si_u;
+};
+typedef struct scsp_if_msg Scsp_if_msg;
+
+#define si_type si_hdr.sh_type
+#define si_rc si_hdr.sh_rc
+#define si_proto si_hdr.sh_proto
+#define si_len si_hdr.sh_len
+#define si_tok si_hdr.sh_tok
+
+#define si_cfg si_u.siu_cfg
+#define si_sum si_u.siu_sum
+#define si_atmarp si_u.siu_atmarp
+#define si_nhrp si_u.siu_nhrp
+
+
+/*
+ * Message types
+ */
+#define SCSP_NOP_REQ 1
+#define SCSP_CFG_REQ 2
+#define SCSP_CFG_RSP 3
+#define SCSP_CACHE_IND 4
+#define SCSP_CACHE_RSP 5
+#define SCSP_SOLICIT_IND 6
+#define SCSP_SOLICIT_RSP 7
+#define SCSP_UPDATE_IND 8
+#define SCSP_UPDATE_REQ 9
+#define SCSP_UPDATE_RSP 10
+
+
+/*
+ * Response codes
+ */
+#define SCSP_RSP_OK 0
+#define SCSP_RSP_ERR 1
+#define SCSP_RSP_REJ 2
+#define SCSP_RSP_NOT_FOUND 3
+
+
+#endif /* _SCSP_SCSP_IF_H */
diff --git a/usr.sbin/atm/scspd/scsp_input.c b/usr.sbin/atm/scspd/scsp_input.c
new file mode 100644
index 0000000..20bd249
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_input.c
@@ -0,0 +1,1088 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * Input packet processing
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netatm/port.h>
+#include <netatm/queue.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+
+#include <errno.h>
+#include <libatm.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "scsp_msg.h"
+#include "scsp_if.h"
+#include "scsp_var.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+static int scsp_parse_atmarp __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
+ */
+ free(cap);
+}
+
+
+/*
+ * Free an SCSP Cache State Update Request, Cache State Update Reply,
+ * or Cache State Update Solicit message in internal format
+ *
+ * Arguments:
+ * csup pointer to CSU message
+ *
+ * Returns:
+ * None
+ *
+ */
+static void
+scsp_free_csu(csup)
+ Scsp_csu_msg *csup;
+{
+ Scsp_csa *csap, *ncsap;
+
+ /*
+ * Return if there's nothing to free
+ */
+ if (csup == (Scsp_csu_msg *)0)
+ return;
+
+ /*
+ * Free the CSA records
+ */
+ for (csap = csup->csu_csa_rec; csap; csap = ncsap) {
+ ncsap = csap->next;
+ SCSP_FREE_CSA(csap);
+ }
+
+ /*
+ * Free the CSU message structure
+ */
+ free(csup);
+}
+
+
+/*
+ * Free an SCSP Hello message in internal format
+ *
+ * Arguments:
+ * hp pointer to Hello message
+ *
+ * Returns:
+ * None
+ *
+ */
+static void
+scsp_free_hello(hp)
+ Scsp_hello *hp;
+{
+ /*
+ * Return if there's nothing to free
+ */
+ if (hp == (Scsp_hello *)0)
+ return;
+
+ /*
+ * Free the Hello message structure
+ */
+ free(hp);
+}
+
+
+/*
+ * Free an SCSP message in internal format
+ *
+ * Arguments:
+ * msg pointer to input packet
+ *
+ * Returns:
+ * None
+ *
+ */
+void
+scsp_free_msg(msg)
+ Scsp_msg *msg;
+{
+ Scsp_ext *exp, *nexp;
+
+ /*
+ * Return if there's nothing to free
+ */
+ if (msg == (Scsp_msg *)0)
+ return;
+
+ /*
+ * Free the message body
+ */
+ switch(msg->sc_msg_type) {
+ case SCSP_CA_MSG:
+ scsp_free_ca(msg->sc_ca);
+ break;
+ case SCSP_CSU_REQ_MSG:
+ case SCSP_CSU_REPLY_MSG:
+ case SCSP_CSUS_MSG:
+ scsp_free_csu(msg->sc_csu_msg);
+ break;
+ case SCSP_HELLO_MSG:
+ scsp_free_hello(msg->sc_hello);
+ break;
+ }
+
+ /*
+ * Free any extensions
+ */
+ for (exp = msg->sc_ext; exp; exp = nexp) {
+ nexp = exp->next;
+ free(exp);
+ }
+
+ /*
+ * Free the message structure
+ */
+ free(msg);
+}
+
+
+/*
+ * Parse a Sender or Receiver ID
+ *
+ * Arguments:
+ * buff pointer to ID
+ * id_len length of ID
+ * idp pointer to structure to receive the ID
+ *
+ * Returns:
+ * 0 input was invalid
+ * else length of ID processed
+ *
+ */
+static int
+scsp_parse_id(buff, id_len, idp)
+ char *buff;
+ int id_len;
+ Scsp_id *idp;
+{
+ /*
+ * Sanity check
+ */
+ if (!buff ||
+ id_len == 0 || id_len > SCSP_MAX_ID_LEN ||
+ !idp) {
+ return(0);
+ }
+
+ /*
+ * Save the ID length
+ */
+ idp->id_len = id_len;
+
+ /*
+ * Get the ID
+ */
+ bcopy(buff, idp->id, id_len);
+
+ /*
+ * Return the ID length
+ */
+ return(id_len);
+}
+
+
+/*
+ * Parse the Mandatory Common Part of an SCSP input packet
+ *
+ * Arguments:
+ * buff pointer to mandatory common part
+ * pdu_len length of input packet
+ * mcp pointer to location of MCP in decoded record
+ *
+ * Returns:
+ * 0 input was invalid
+ * else length of MCP in message
+ *
+ */
+static int
+scsp_parse_mcp(buff, pdu_len, mcp)
+ char *buff;
+ int pdu_len;
+ Scsp_mcp *mcp;
+{
+ int len;
+ u_char *idp;
+ struct scsp_nmcp *smp;
+
+ /*
+ * Get the protocol ID
+ */
+ smp = (struct scsp_nmcp *)buff;
+ mcp->pid = ntohs(smp->sm_pid);
+ if (mcp->pid < SCSP_PROTO_ATMARP ||
+ mcp->pid > SCSP_PROTO_LNNI) {
+ /* Protocol ID is invalid */
+ goto mcp_invalid;
+ }
+
+ /*
+ * Get the server group ID
+ */
+ mcp->sgid = ntohs(smp->sm_sgid);
+
+ /*
+ * Get the flags
+ */
+ mcp->flags = ntohs(smp->sm_flags);
+
+ /*
+ * Get the sender ID and length
+ */
+ idp = (u_char *) ((caddr_t)smp + sizeof(struct scsp_nmcp));
+ len = scsp_parse_id(idp, smp->sm_sid_len, &mcp->sid);
+ if (len == 0) {
+ goto mcp_invalid;
+ }
+
+ /*
+ * Get the receiver ID and length
+ */
+ idp += len;
+ len = scsp_parse_id(idp, smp->sm_rid_len, &mcp->rid);
+ if (len == 0) {
+ goto mcp_invalid;
+ }
+
+ /*
+ * Get the record count
+ */
+ mcp->rec_cnt = ntohs(smp->sm_rec_cnt);
+
+ /*
+ * Return the length of data we processed
+ */
+ return(sizeof(struct scsp_nmcp) + smp->sm_sid_len +
+ smp->sm_rid_len);
+
+mcp_invalid:
+ return(0);
+}
+
+
+/*
+ * Parse an Extension
+ *
+ * Arguments:
+ * buff pointer to Extension
+ * pdu_len length of buffer
+ * expp pointer to location to receive pointer to the Extension
+ *
+ * Returns:
+ * 0 input was invalid
+ * else length of Extension processed
+ *
+ */
+static int
+scsp_parse_ext(buff, pdu_len, expp)
+ char *buff;
+ int pdu_len;
+ Scsp_ext **expp;
+{
+ int len;
+ struct scsp_next *sep;
+ Scsp_ext *exp;
+
+ /*
+ * Get memory for the extension
+ */
+ sep = (struct scsp_next *)buff;
+ len = sizeof(Scsp_ext) + ntohs(sep->se_len);
+ exp = calloc(1, len);
+ if (exp == NULL)
+ goto ext_invalid;
+
+ /*
+ * Get the type
+ */
+ exp->type = ntohs(sep->se_type);
+
+ /*
+ * Get the length
+ */
+ exp->len = ntohs(sep->se_len);
+
+ /*
+ * Get the value
+ */
+ if (exp->len > 0) {
+ bcopy((caddr_t)sep + sizeof(struct scsp_next),
+ (caddr_t)exp + sizeof(Scsp_ext),
+ exp->len);
+ }
+
+ /*
+ * Save a pointer to the extension and return the
+ * number of bytes processed
+ */
+ *expp = exp;
+ return(sizeof(struct scsp_next) + exp->len);
+
+ext_invalid:
+ if (exp) {
+ free(exp);
+ }
+ return(0);
+}
+
+
+/*
+ * Parse a Cache State Advertisement or Cache State Advertisement
+ * Summary record
+ *
+ * Arguments:
+ * buff pointer to CSA or CSAS record
+ * pdu_len length of input packet
+ * csapp pointer to location to put pointer to CSA or CSAS
+ *
+ * Returns:
+ * 0 input was invalid
+ * else length of record processed
+ *
+ */
+static int
+scsp_parse_csa(buff, pdu_len, csapp)
+ char *buff;
+ int pdu_len;
+ Scsp_csa **csapp;
+{
+ int len;
+ char *idp;
+ struct scsp_ncsa *scp;
+ Scsp_csa *csap = NULL;
+
+ /*
+ * Check the record length
+ */
+ scp = (struct scsp_ncsa *)buff;
+ if (ntohs(scp->scs_len) < (sizeof(struct scsp_ncsa) +
+ scp->scs_ck_len + scp->scs_oid_len)) {
+ goto csa_invalid;
+ }
+
+ /*
+ * Get memory for the returned structure
+ */
+ len = sizeof(Scsp_csa) + ntohs(scp->scs_len) -
+ sizeof(struct scsp_ncsa) - scp->scs_ck_len -
+ scp->scs_oid_len;
+ csap = calloc(1, len);
+ if (csap == NULL)
+ goto csa_invalid;
+
+ /*
+ * Get the hop count
+ */
+ csap->hops = ntohs(scp->scs_hop_cnt);
+
+ /*
+ * Set the null flag
+ */
+ csap->null = (ntohs(scp->scs_nfill) & SCSP_CSAS_NULL) != 0;
+
+ /*
+ * Get the sequence number
+ */
+ csap->seq = get_long((u_char *)&scp->scs_seq);
+
+ /*
+ * Get the cache key
+ */
+ if (scp->scs_ck_len == 0 ||
+ scp->scs_ck_len > SCSP_MAX_KEY_LEN) {
+ goto csa_invalid;
+ }
+ csap->key.key_len = scp->scs_ck_len;
+ idp = (char *) ((caddr_t)scp + sizeof(struct scsp_ncsa));
+ bcopy(idp, csap->key.key, scp->scs_ck_len);
+
+ /*
+ * Get the originator ID
+ */
+ idp += scp->scs_ck_len;
+ len = scsp_parse_id(idp, scp->scs_oid_len, &csap->oid);
+ if (len == 0) {
+ goto csa_invalid;
+ }
+
+ /*
+ * Get the protocol-specific data, if present
+ */
+ len = ntohs(scp->scs_len) - (sizeof(struct scsp_ncsa) +
+ scp->scs_ck_len + scp->scs_oid_len);
+ if (len > 0) {
+ idp += scp->scs_oid_len;
+ len = scsp_parse_atmarp(idp, len, &csap->atmarp_data);
+ if (len == 0)
+ goto csa_invalid;
+ }
+
+ /*
+ * Set a pointer to the MCP and return the length
+ * of data we processed
+ */
+ *csapp = csap;
+ return(ntohs(scp->scs_len));
+
+csa_invalid:
+ if (csap)
+ SCSP_FREE_CSA(csap);
+ return(0);
+}
+
+
+/*
+ * Parse a Cache Alignment message
+ *
+ * Arguments:
+ * buff pointer to start of CA in message
+ * pdu_len length of input packet
+ * capp pointer to location to put pointer to CA message
+ *
+ * Returns:
+ * 0 input was invalid
+ * else length of CA message processed
+ *
+ */
+static int
+scsp_parse_ca(buff, pdu_len, capp)
+ char *buff;
+ int pdu_len;
+ Scsp_ca **capp;
+{
+ int i, len, proc_len;
+ struct scsp_nca *scap;
+ Scsp_ca *cap;
+ Scsp_csa **csapp;
+
+ /*
+ * Get memory for the returned structure
+ */
+ scap = (struct scsp_nca *)buff;
+ cap = calloc(1, sizeof(Scsp_ca));
+ if (cap == NULL)
+ goto ca_invalid;
+
+ /*
+ * Get the sequence number
+ */
+ cap->ca_seq = get_long((u_char *)&scap->sca_seq);
+ proc_len = sizeof(scap->sca_seq);
+ buff += sizeof(scap->sca_seq);
+
+ /*
+ * Process the mandatory common part of the message
+ */
+ len = scsp_parse_mcp(buff,
+ pdu_len - proc_len,
+ &cap->ca_mcp);
+ if (len == 0)
+ goto ca_invalid;
+ buff += len;
+ proc_len += len;
+
+ /*
+ * Set the flags
+ */
+ cap->ca_m = (cap->ca_mcp.flags & SCSP_CA_M) != 0;
+ cap->ca_i = (cap->ca_mcp.flags & SCSP_CA_I) != 0;
+ cap->ca_o = (cap->ca_mcp.flags & SCSP_CA_O) != 0;
+
+ /*
+ * Get the CSAS records from the message
+ */
+ for (i = 0, csapp = &cap->ca_csa_rec; i < cap->ca_mcp.rec_cnt;
+ i++, csapp = &(*csapp)->next) {
+ len = scsp_parse_csa(buff, pdu_len - proc_len, csapp);
+ buff += len;
+ proc_len += len;
+ }
+
+ /*
+ * Set the address of the CA message and
+ * return the length of processed data
+ */
+ *capp = cap;
+ return(proc_len);
+
+ca_invalid:
+ if (cap)
+ scsp_free_ca(cap);
+ return(0);
+}
+
+
+/*
+ * Parse the ATMARP-specific part of a CSA record
+ *
+ * Arguments:
+ * buff pointer to ATMARP part of CSU message
+ * pdu_len length of data to process
+ * acspp pointer to location to put pointer to CSU message
+ *
+ * Returns:
+ * 0 input was invalid
+ * else length of CSU Req message processed
+ *
+ */
+static int
+scsp_parse_atmarp(buff, pdu_len, acspp)
+ char *buff;
+ int pdu_len;
+ Scsp_atmarp_csa **acspp;
+{
+ int len, proc_len;
+ struct scsp_atmarp_ncsa *sacp;
+ Scsp_atmarp_csa *acsp = NULL;
+
+ /*
+ * Initial packet verification
+ */
+ sacp = (struct scsp_atmarp_ncsa *)buff;
+ if ((sacp->sa_hrd != ntohs(ARP_ATMFORUM)) ||
+ (sacp->sa_pro != ntohs(ETHERTYPE_IP)))
+ goto acs_invalid;
+
+ /*
+ * Get memory for the returned structure
+ */
+ acsp = calloc(1, sizeof(Scsp_atmarp_csa));
+ if (acsp == NULL)
+ goto acs_invalid;
+
+ /*
+ * Get state code
+ */
+ acsp->sa_state = sacp->sa_state;
+ proc_len = sizeof(struct scsp_atmarp_ncsa);
+
+ /*
+ * Verify/gather source ATM address
+ */
+ acsp->sa_sha.address_format = T_ATM_ABSENT;
+ acsp->sa_sha.address_length = 0;
+ if ((len = (sacp->sa_shtl & ARP_TL_LMASK)) != 0) {
+ if (sacp->sa_shtl & ARP_TL_E164) {
+ if (len > sizeof(Atm_addr_e164))
+ goto acs_invalid;
+ acsp->sa_sha.address_format = T_ATM_E164_ADDR;
+ } else {
+ if (len != sizeof(Atm_addr_nsap))
+ goto acs_invalid;
+ acsp->sa_sha.address_format = T_ATM_ENDSYS_ADDR;
+ }
+ acsp->sa_sha.address_length = len;
+ if (pdu_len < proc_len + len)
+ goto acs_invalid;
+ bcopy(&buff[proc_len], (char *)acsp->sa_sha.address,
+ len);
+ proc_len += len;
+ }
+
+ /*
+ * Verify/gather source ATM subaddress
+ */
+ acsp->sa_ssa.address_format = T_ATM_ABSENT;
+ acsp->sa_ssa.address_length = 0;
+ if ((len = (sacp->sa_sstl & ARP_TL_LMASK)) != 0) {
+ if (((sacp->sa_sstl & ARP_TL_TMASK) != ARP_TL_NSAPA) ||
+ (len != sizeof(Atm_addr_nsap)))
+ goto acs_invalid;
+ acsp->sa_ssa.address_format = T_ATM_ENDSYS_ADDR;
+ acsp->sa_ssa.address_length = len;
+ if (pdu_len < proc_len + len)
+ goto acs_invalid;
+ bcopy(&buff[proc_len], (char *)acsp->sa_ssa.address,
+ len);
+ proc_len += len;
+ }
+
+ /*
+ * Verify/gather source IP address
+ */
+ if ((len = sacp->sa_spln) != 0) {
+ if (len != sizeof(struct in_addr))
+ goto acs_invalid;
+ if (pdu_len < proc_len + len)
+ goto acs_invalid;
+ bcopy(&buff[proc_len], (char *)&acsp->sa_spa, len);
+ proc_len += len;
+ } else {
+ acsp->sa_spa.s_addr = 0;
+ }
+
+ /*
+ * Verify/gather target ATM address
+ */
+ acsp->sa_tha.address_format = T_ATM_ABSENT;
+ acsp->sa_tha.address_length = 0;
+ if ((len = (sacp->sa_thtl & ARP_TL_LMASK)) != 0) {
+ if (sacp->sa_thtl & ARP_TL_E164) {
+ if (len > sizeof(Atm_addr_e164))
+ goto acs_invalid;
+ acsp->sa_tha.address_format = T_ATM_E164_ADDR;
+ } else {
+ if (len != sizeof(Atm_addr_nsap))
+ goto acs_invalid;
+ acsp->sa_tha.address_format = T_ATM_ENDSYS_ADDR;
+ }
+ acsp->sa_tha.address_length = len;
+ if (pdu_len < proc_len + len)
+ goto acs_invalid;
+ bcopy(&buff[proc_len], (char *)acsp->sa_tha.address,
+ len);
+ proc_len += len;
+ }
+
+ /*
+ * Verify/gather target ATM subaddress
+ */
+ acsp->sa_tsa.address_format = T_ATM_ABSENT;
+ acsp->sa_tsa.address_length = 0;
+ if ((len = (sacp->sa_tstl & ARP_TL_LMASK)) != 0) {
+ if (((sacp->sa_tstl & ARP_TL_TMASK) != ARP_TL_NSAPA) ||
+ (len != sizeof(Atm_addr_nsap)))
+ goto acs_invalid;
+ acsp->sa_tsa.address_format = T_ATM_ENDSYS_ADDR;
+ acsp->sa_tsa.address_length = len;
+ if (pdu_len < proc_len + len)
+ goto acs_invalid;
+ bcopy(&buff[proc_len], (char *)acsp->sa_tsa.address,
+ len);
+ proc_len += len;
+ }
+
+ /*
+ * Verify/gather target IP address
+ */
+ if ((len = sacp->sa_tpln) != 0) {
+ if (len != sizeof(struct in_addr))
+ goto acs_invalid;
+ if (pdu_len < proc_len + len)
+ goto acs_invalid;
+ bcopy(&buff[proc_len], (char *)&acsp->sa_tpa, len);
+ proc_len += len;
+ } else {
+ acsp->sa_tpa.s_addr = 0;
+ }
+
+ /*
+ * Verify packet length
+ */
+ if (proc_len != pdu_len)
+ goto acs_invalid;
+
+ *acspp = acsp;
+ return(proc_len);
+
+acs_invalid:
+ if (acsp)
+ free(acsp);
+ return(0);
+}
+
+
+/*
+ * Parse a Cache State Update Request, Cache State Update Reply, or
+ * Cache State Update Solicit message. These all have the same format,
+ * a Mandatory Common Part followed by a number of CSA or CSAS records.
+ *
+ * Arguments:
+ * buff pointer to start of CSU message
+ * pdu_len length of input packet
+ * csupp pointer to location to put pointer to CSU message
+ *
+ * Returns:
+ * 0 input was invalid
+ * else length of CSU Req message processed
+ *
+ */
+static int
+scsp_parse_csu(buff, pdu_len, csupp)
+ char *buff;
+ int pdu_len;
+ Scsp_csu_msg **csupp;
+{
+ int i, len, proc_len;
+ Scsp_csu_msg *csup;
+ Scsp_csa **csapp;
+
+ /*
+ * Get memory for the returned structure
+ */
+ csup = calloc(1, sizeof(Scsp_csu_msg));
+ if (csup == NULL)
+ goto csu_invalid;
+
+ /*
+ * Process the mandatory common part of the message
+ */
+ len = scsp_parse_mcp(buff, pdu_len, &csup->csu_mcp);
+ if (len == 0)
+ goto csu_invalid;
+ buff += len;
+ proc_len = len;
+
+ /*
+ * Get the CSAS records from the message
+ */
+ for (i = 0, csapp = &csup->csu_csa_rec;
+ i < csup->csu_mcp.rec_cnt;
+ i++, csapp = &(*csapp)->next) {
+ len = scsp_parse_csa(buff, pdu_len - proc_len, csapp);
+ buff += len;
+ proc_len += len;
+ }
+
+ /*
+ * Set the address of the CSU Req message and
+ * return the length of processed data
+ */
+ *csupp = csup;
+ return(proc_len);
+
+csu_invalid:
+ if (csup)
+ scsp_free_csu(csup);
+ return(0);
+}
+
+
+/*
+ * Parse a Hello message
+ *
+ * Arguments:
+ * buff pointer to start of Hello in message
+ * pdu_len length of input packet
+ * hpp pointer to location to put pointer to Hello message
+ *
+ * Returns:
+ * 0 input was invalid
+ * else length of Hello message processed
+ *
+ */
+static int
+scsp_parse_hello(buff, pdu_len, hpp)
+ char *buff;
+ int pdu_len;
+ Scsp_hello **hpp;
+{
+ int i, len, proc_len;
+ struct scsp_nhello *shp = (struct scsp_nhello *)buff;
+ Scsp_hello *hp;
+ Scsp_id *idp;
+ Scsp_id **ridpp;
+
+ /*
+ * Get memory for the returned structure
+ */
+ hp = calloc(1, sizeof(Scsp_hello));
+ if (hp == NULL)
+ goto hello_invalid;
+
+ /*
+ * Get the hello interval
+ */
+ hp->hello_int = ntohs(shp->sch_hi);
+
+ /*
+ * Get the dead factor
+ */
+ hp->dead_factor = ntohs(shp->sch_df);
+
+ /*
+ * Get the family ID
+ */
+ hp->family_id = ntohs(shp->sch_fid);
+
+ /*
+ * Process the mandatory common part of the message
+ */
+ proc_len = sizeof(struct scsp_nhello) -
+ sizeof(struct scsp_nmcp);
+ buff += proc_len;
+ len = scsp_parse_mcp(buff, pdu_len - proc_len,
+ &hp->hello_mcp);
+ if (len == 0)
+ goto hello_invalid;
+ buff += len;
+ proc_len += len;
+
+ /*
+ * Get additional receiver ID records from the message
+ */
+ for (i = 0, ridpp = &hp->hello_mcp.rid.next;
+ i < hp->hello_mcp.rec_cnt;
+ i++, ridpp = &idp->next) {
+ idp = calloc(1, sizeof(Scsp_id));
+ if (idp == NULL)
+ goto hello_invalid;
+ len = scsp_parse_id(buff,
+ hp->hello_mcp.rid.id_len,
+ idp);
+ if (len == 0) {
+ free(idp);
+ goto hello_invalid;
+ }
+ buff += len;
+ proc_len += len;
+ *ridpp = idp;
+ }
+
+ /*
+ * Set the address of the CA message and
+ * return the length of processed data
+ */
+ *hpp = hp;
+ return(proc_len);
+
+hello_invalid:
+ if (hp)
+ scsp_free_hello(hp);
+ return(0);
+}
+
+
+/*
+ * Parse an SCSP input packet
+ *
+ * Arguments:
+ * buff pointer to input packet
+ * pdu_len length of input packet
+ *
+ * Returns:
+ * NULL input packet was invalid
+ * else pointer to packet in internal format
+ *
+ */
+Scsp_msg *
+scsp_parse_msg(buff, pdu_len)
+ char *buff;
+ int pdu_len;
+{
+ int ext_off, len, plen;
+ struct scsp_nhdr *shp;
+ Scsp_msg *msg = (Scsp_msg *)0;
+ Scsp_ext **expp;
+
+ /*
+ * Check the message checksum
+ */
+ if (ip_checksum(buff, pdu_len) != 0) {
+ /*
+ * Checksum was bad--discard the message
+ */
+ goto ignore;
+ }
+
+ /*
+ * Allocate storage for the message
+ */
+ msg = calloc(1, sizeof(Scsp_msg));
+ if (msg == NULL)
+ goto ignore;
+
+ /*
+ * Decode the fixed header
+ *
+ * Check the version
+ */
+ shp = (struct scsp_nhdr *)buff;
+ if (shp->sh_ver != SCSP_VER_1)
+ goto ignore;
+
+ /*
+ * Get the message type
+ */
+ msg->sc_msg_type = shp->sh_type;
+
+ /*
+ * Get and check the length
+ */
+ len = ntohs(shp->sh_len);
+ if (len != pdu_len)
+ goto ignore;
+
+ /*
+ * Get the extension offset
+ */
+ ext_off = ntohs(shp->sh_ext_off);
+
+ /*
+ * Decode the body of the message, depending on the type
+ */
+ buff += sizeof(struct scsp_nhdr);
+ len -= sizeof(struct scsp_nhdr);
+ switch(msg->sc_msg_type) {
+ case SCSP_CA_MSG:
+ plen = scsp_parse_ca(buff, len, &msg->sc_ca);
+ break;
+ case SCSP_CSU_REQ_MSG:
+ case SCSP_CSU_REPLY_MSG:
+ case SCSP_CSUS_MSG:
+ plen = scsp_parse_csu(buff, len, &msg->sc_csu_msg);
+ break;
+ case SCSP_HELLO_MSG:
+ plen = scsp_parse_hello(buff, len, &msg->sc_hello);
+ break;
+ default:
+ goto ignore;
+ }
+ if (plen == 0) {
+ goto ignore;
+ }
+ buff += plen;
+ len -= plen;
+
+ /*
+ * Decode any extensions
+ */
+ if (ext_off != 0) {
+ for (expp = &msg->sc_ext; len > 0;
+ expp = &(*expp)->next) {
+ plen = scsp_parse_ext(buff, len, expp);
+ if (plen == 0) {
+ goto ignore;
+ }
+ buff += plen;
+ len -= plen;
+ }
+ }
+
+ /*
+ * Make sure we handled the whole message
+ */
+ if (len != 0) {
+ goto ignore;
+ }
+
+ /*
+ * Return the address of the SCSP message in internal format
+ */
+ return(msg);
+
+ignore:
+ if (msg)
+ scsp_free_msg(msg);
+ return(Scsp_msg *)0;
+}
diff --git a/usr.sbin/atm/scspd/scsp_log.c b/usr.sbin/atm/scspd/scsp_log.c
new file mode 100644
index 0000000..8b76c3e
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_log.c
@@ -0,0 +1,264 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * SCSP logging routines
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netatm/port.h>
+#include <netatm/queue.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+
+#include <errno.h>
+#include <libatm.h>
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "scsp_msg.h"
+#include "scsp_if.h"
+#include "scsp_var.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+/*
+ * Global variables
+ */
+FILE *scsp_trace_file = (FILE *)0;
+
+
+/*
+ * Write a message to SCSP's log
+ *
+ * Arguments:
+ * level pointer to an SCSP cache key structure
+ * fmt printf-style format string
+ * ... parameters for printf-style use according to fmt
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+#if __STDC__
+scsp_log(const int level, const char *fmt, ...)
+#else
+scsp_log(level, fmt, va_alist)
+ int level;
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+
+ /*
+ * In debug mode, just write to stdout
+ */
+ if (scsp_debug_mode) {
+ vprintf(fmt, ap);
+ printf("\n");
+ return;
+ }
+
+ /*
+ * Write to syslog if it's active or if no log file is set up
+ */
+ if (scsp_log_syslog || !scsp_log_file) {
+ vsyslog(level, fmt, ap);
+ }
+
+ /*
+ * Write to the log file if there's one set up
+ */
+ if (scsp_log_file) {
+ vfprintf(scsp_log_file, fmt, ap);
+ fprintf(scsp_log_file, "\n");
+ }
+
+ va_end(ap);
+}
+
+
+/*
+ * Open SCSP's trace file
+ *
+ * Arguments:
+ * none
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+scsp_open_trace()
+{
+ char fname[64];
+
+ /*
+ * Build a file name
+ */
+ bzero(fname, sizeof(fname));
+ sprintf(fname, "/tmp/scspd.%d.trace", getpid());
+
+ /*
+ * Open the trace file. If the open fails, log an error, but
+ * keep going. The trace routine will notice that the file
+ * isn't open and won't try to write to it.
+ */
+ scsp_trace_file = fopen(fname, "w");
+ if (scsp_trace_file == (FILE *)0) {
+ scsp_log(LOG_ERR, "Can't open trace file");
+ }
+}
+
+
+/*
+ * Write a message to SCSP's trace file
+ *
+ * Arguments:
+ * fmt printf-style format string
+ * ... parameters for printf-style use according to fmt
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+#if __STDC__
+scsp_trace(const char *fmt, ...)
+#else
+scsp_trace(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+
+ /*
+ * Write the message to the trace file, if it's open
+ */
+ if (scsp_trace_file) {
+ vfprintf(scsp_trace_file, fmt, ap);
+ }
+
+ va_end(ap);
+}
+
+
+/*
+ * Write an SCSP message to SCSP's trace file
+ *
+ * Arguments:
+ * dcsp pointer to DCS block for the message
+ * msg pointer to the message
+ * dir a direction indicator--0 for sending, 1 for receiving
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+scsp_trace_msg(dcsp, msg, dir)
+ Scsp_dcs *dcsp;
+ Scsp_msg *msg;
+ int dir;
+{
+ struct in_addr addr;
+
+ /*
+ * Copy the remote IP address into a struct in_addr
+ */
+ bcopy(dcsp->sd_dcsid.id, &addr.s_addr, sizeof(struct in_addr));
+
+ /*
+ * Write the message to the trace file, if it's open
+ */
+ if (scsp_trace_file) {
+ scsp_trace("SCSP message at 0x%x %s %s\n",
+ (u_long)msg,
+ (dir ? "received from" : "sent to"),
+ format_ip_addr(&addr));
+ print_scsp_msg(scsp_trace_file, msg);
+ }
+}
+
+
+/*
+ * Log a memory error and exit
+ *
+ * Arguments:
+ * cp message to log
+ *
+ * Returns:
+ * exits, does not return
+ *
+ */
+void
+scsp_mem_err(cp)
+ char *cp;
+{
+ scsp_log(LOG_CRIT, "out of memory: %s", cp);
+ exit(2);
+}
diff --git a/usr.sbin/atm/scspd/scsp_msg.c b/usr.sbin/atm/scspd/scsp_msg.c
new file mode 100644
index 0000000..63e0e40
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_msg.c
@@ -0,0 +1,590 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * SCSP message-handling routines
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netatm/port.h>
+#include <netatm/queue.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+
+#include <errno.h>
+#include <libatm.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "scsp_msg.h"
+#include "scsp_if.h"
+#include "scsp_var.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+/*
+ * Copy CSAS records into a CA record
+ *
+ * Arguments:
+ * dcsp pointer to DCS block for DCS
+ * cap pointer to CA record for CSASs
+ *
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+scsp_ca_csas_setup(dcsp, cap)
+ Scsp_dcs *dcsp;
+ Scsp_ca *cap;
+{
+ int csas_len, len, mtu;
+ Scsp_server *ssp = dcsp->sd_server;
+ Scsp_cse *csep, *next_csep;
+ Scsp_csa *csap;
+
+ /*
+ * Loop through pending CSAS records
+ */
+ len = sizeof(struct scsp_nhdr) + sizeof(struct scsp_nmcp) +
+ ssp->ss_lsid.id_len +
+ dcsp->sd_dcsid.id_len;
+ csas_len = sizeof(struct scsp_ncsa) +
+ dcsp->sd_server->ss_id_len +
+ dcsp->sd_server->ss_ckey_len;
+ mtu = dcsp->sd_server->ss_mtu;
+ for (csep = dcsp->sd_ca_csas;
+ csep && (len < mtu - csas_len);
+ csep = next_csep) {
+ next_csep = csep->sc_next;
+ csap = scsp_cse2csas(csep);
+ LINK2TAIL(csap, Scsp_csa, cap->ca_csa_rec, next);
+ len += csas_len;
+ UNLINK(csep, Scsp_cse, dcsp->sd_ca_csas, sc_next);
+ free(csep);
+ cap->ca_mcp.rec_cnt++;
+ }
+}
+
+
+/*
+ * Process CSA records from a CSU Request that may be in response to
+ * CSAS records sent in a CSUS
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * msg pointer to received message
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+scsp_csus_ack(dcsp, msg)
+ Scsp_dcs *dcsp;
+ Scsp_msg *msg;
+{
+ Scsp_csu_msg *csusp;
+ Scsp_csa *csap, *csasp, *next_csasp;
+
+ /*
+ * If this isn't a CSU Request, or there's no outstanding CSUS,
+ * or the outstanding CSUS has already been satisfied, just
+ * return
+ */
+ if (!msg || msg->sc_msg_type != SCSP_CSU_REQ_MSG ||
+ !dcsp->sd_csus_rexmt_msg ||
+ !dcsp->sd_csus_rexmt_msg->sc_csu_msg ||
+ !dcsp->sd_csus_rexmt_msg->sc_csu_msg->csu_csa_rec)
+ return;
+
+
+ /*
+ * Loop through the CSASs in the CSUS message, checking for
+ * each in the CSA records of the received CSU Request
+ */
+ csusp = dcsp->sd_csus_rexmt_msg->sc_csu_msg;
+ for (csasp = csusp->csu_csa_rec; csasp; csasp = next_csasp) {
+ next_csasp = csasp->next;
+ for (csap = msg->sc_csu_msg->csu_csa_rec;
+ csap; csap = csap->next) {
+ /*
+ * If the records match, unlink and free the
+ * CSAS from the CSUS
+ */
+ if (scsp_cmp_key(&csap->key, &csasp->key) == 0 &&
+ scsp_cmp_key(&csap->key, &csasp->key) == 0 &&
+ scsp_cmp_id(&csap->oid, &csasp->oid) == 0 &&
+ csap->seq >= csasp->seq) {
+ UNLINK(csasp, Scsp_csa,
+ csusp->csu_csa_rec,
+ next);
+ SCSP_FREE_CSA(csasp);
+ dcsp->sd_csus_rexmt_msg->sc_csu_msg->csu_mcp.rec_cnt--;
+ break;
+ }
+ }
+ }
+
+ if (csusp->csu_csa_rec == (Scsp_csa *)0) {
+ /*
+ * All CSASs in the CSUS message have been
+ * answered. Stop the timer and free the
+ * saved message.
+ */
+ HARP_CANCEL(&dcsp->sd_csus_rexmt_t);
+ scsp_free_msg(dcsp->sd_csus_rexmt_msg);
+ dcsp->sd_csus_rexmt_msg = (Scsp_msg *)0;
+
+ /*
+ * If the CRL isn't empty, send another CSUS
+ */
+ if (dcsp->sd_crl) {
+ (void)scsp_send_csus(dcsp);
+ }
+ }
+}
+
+
+/*
+ * Send a CA message
+ *
+ * Arguments:
+ * dcsp pointer to DCS block for DCS
+ *
+ * Returns:
+ * 0 message sent OK
+ * else errno indicating reason for failure
+ *
+ */
+int
+scsp_send_ca(dcsp)
+ Scsp_dcs *dcsp;
+{
+ int rc;
+ Scsp_msg *ca_msg;
+ Scsp_ca *cap;
+ Scsp_server *ssp = dcsp->sd_server;
+
+ /*
+ * Get memory for a CA message
+ */
+ ca_msg = calloc(1, sizeof(Scsp_msg));
+ if (ca_msg == NULL)
+ scsp_mem_err("scsp_send_ca: sizeof(Scsp_msg)");
+ cap = calloc(1, sizeof(Scsp_ca));
+ if (cap == NULL)
+ scsp_mem_err("scsp_send_ca: sizeof(Scsp_ca)");
+
+ /*
+ * Fill out constant fields
+ */
+ ca_msg->sc_msg_type = SCSP_CA_MSG;
+ ca_msg->sc_ca = cap;
+ cap->ca_seq = dcsp->sd_ca_seq;
+ cap->ca_mcp.pid = ssp->ss_pid;
+ cap->ca_mcp.sgid = ssp->ss_sgid;
+ cap->ca_mcp.sid = ssp->ss_lsid;
+ cap->ca_mcp.rid = dcsp->sd_dcsid;
+
+ /*
+ * Fill out state-dependent fields
+ */
+ switch(dcsp->sd_ca_state) {
+ case SCSP_CAFSM_NEG:
+ cap->ca_m = 1;
+ cap->ca_i = 1;
+ cap->ca_o = 1;
+ break;
+ case SCSP_CAFSM_MASTER:
+ cap->ca_m = 1;
+ cap->ca_i = 0;
+ scsp_ca_csas_setup(dcsp, cap);
+ cap->ca_o = dcsp->sd_ca_csas != (Scsp_cse *)0;
+ break;
+ case SCSP_CAFSM_SLAVE:
+ cap->ca_m = 0;
+ cap->ca_i = 0;
+ scsp_ca_csas_setup(dcsp, cap);
+ cap->ca_o = dcsp->sd_ca_csas != (Scsp_cse *)0;
+ break;
+ default:
+ scsp_log(LOG_ERR, "Invalid state in scsp_send_ca");
+ abort();
+ }
+
+ /*
+ * Send the CA message and save a pointer to it in case
+ * it needs to be retransmitted
+ */
+ rc = scsp_send_msg(dcsp, ca_msg);
+ if (rc == 0) {
+ dcsp->sd_ca_rexmt_msg = ca_msg;
+ } else {
+ scsp_free_msg(ca_msg);
+ }
+
+ return(rc);
+}
+
+
+/*
+ * Send a CSU Solicit message
+ *
+ * Arguments:
+ * dcsp pointer to DCS block for DCS
+ *
+ * Returns:
+ * 0 message sent OK
+ * else errno indicating reason for failure
+ *
+ */
+int
+scsp_send_csus(dcsp)
+ Scsp_dcs *dcsp;
+{
+ int csas_len, len, mtu, rc;
+ Scsp_msg *csus_msg;
+ Scsp_csu_msg *csusp;
+ Scsp_csa *csasp, *next_csasp;
+ Scsp_server *ssp = dcsp->sd_server;
+
+ /*
+ * If we have a mesage saved for retransmission, use it.
+ * If not, get memory for a new one.
+ */
+ if (dcsp->sd_csus_rexmt_msg) {
+ csus_msg = dcsp->sd_csus_rexmt_msg;
+ csusp = csus_msg->sc_csu_msg;
+ } else {
+ /*
+ * Get memory for a CSUS message
+ */
+ csus_msg = calloc(1, sizeof(Scsp_msg));
+ if (csus_msg == NULL)
+ scsp_mem_err("scsp_send_csus: sizeof(Scsp_msg)");
+ csusp = calloc(1, sizeof(Scsp_csu_msg));
+ if (csusp == NULL)
+ scsp_mem_err("scsp_send_csus: sizeof(Scsp_csu_msg)");
+
+ /*
+ * Fill out constant fields
+ */
+ csus_msg->sc_msg_type = SCSP_CSUS_MSG;
+ csus_msg->sc_csu_msg = csusp;
+ csusp->csu_mcp.pid = ssp->ss_pid;
+ csusp->csu_mcp.sgid = ssp->ss_sgid;
+ csusp->csu_mcp.sid = ssp->ss_lsid;
+ csusp->csu_mcp.rid = dcsp->sd_dcsid;
+ }
+
+ /*
+ * Move CSAS records from CRL into message
+ */
+ mtu = dcsp->sd_server->ss_mtu;
+ csas_len = sizeof(struct scsp_ncsa) + ssp->ss_id_len +
+ ssp->ss_ckey_len;
+ len = sizeof(struct scsp_nhdr) + sizeof(struct scsp_nmcp) +
+ 2 * ssp->ss_id_len +
+ csas_len * (csusp->csu_mcp.rec_cnt + 1);
+ for (csasp = dcsp->sd_crl;
+ csasp && ((len + csas_len) < mtu);
+ csasp = next_csasp, len += csas_len) {
+ next_csasp = csasp->next;
+ csusp->csu_mcp.rec_cnt++;
+ UNLINK(csasp, Scsp_csa, dcsp->sd_crl, next);
+ LINK2TAIL(csasp, Scsp_csa, csusp->csu_csa_rec, next);
+ csasp->hops = 1;
+ }
+
+ /*
+ * Send the CSUS message and save a pointer to it in case
+ * it needs to be retransmitted
+ */
+ rc = scsp_send_msg(dcsp, csus_msg);
+ if (rc == 0) {
+ /*
+ * Success--Save a pointer to the message and
+ * start the CSUS retransmit timer
+ */
+ dcsp->sd_csus_rexmt_msg = csus_msg;
+ HARP_TIMER(&dcsp->sd_csus_rexmt_t,
+ dcsp->sd_csus_rexmt_int,
+ scsp_csus_retran_timeout);
+ } else {
+ /*
+ * Error--free the CSUS message
+ */
+ scsp_free_msg(csus_msg);
+ }
+
+ return(rc);
+}
+
+
+/*
+ * Send a CSU Request message
+ *
+ * Arguments:
+ * dcsp pointer to DCS block for DCS
+ * csap pointer to CSAs to include
+ *
+ * Returns:
+ * 0 message sent OK
+ * else errno indicating reason for failure
+ *
+ */
+int
+scsp_send_csu_req(dcsp, csap)
+ Scsp_dcs *dcsp;
+ Scsp_csa *csap;
+{
+ int rc;
+ Scsp_server *ssp = dcsp->sd_server;
+ Scsp_csa *cnt_csap;
+ Scsp_msg *csu_msg;
+ Scsp_csu_msg *csup;
+ Scsp_csu_rexmt *rxp;
+
+ /*
+ * Return if CSA list is empty
+ */
+ if (!csap)
+ return(0);
+
+ /*
+ * Get memory for a CSU Req message
+ */
+ csu_msg = calloc(1, sizeof(Scsp_msg));
+ if (csu_msg == NULL)
+ scsp_mem_err("scsp_send_csu_req: sizeof(Scsp_msg)");
+ csup = calloc(1, sizeof(Scsp_csu_msg));
+ if (csup == NULL)
+ scsp_mem_err("scsp_send_csu_req: sizeof(Scsp_csu_msg)");
+
+ /*
+ * Get memory for a CSU Req retransmission queue entry
+ */
+ rxp = calloc(1, sizeof(Scsp_csu_rexmt));
+ if (rxp == NULL)
+ scsp_mem_err("scsp_send_csu_req: sizeof(Scsp_csu_rexmt)");
+
+ /*
+ * Fill out constant fields
+ */
+ csu_msg->sc_msg_type = SCSP_CSU_REQ_MSG;
+ csu_msg->sc_csu_msg = csup;
+ csup->csu_mcp.pid = ssp->ss_pid;
+ csup->csu_mcp.sgid = ssp->ss_sgid;
+ csup->csu_mcp.sid = ssp->ss_lsid;
+ csup->csu_mcp.rid = dcsp->sd_dcsid;
+
+ /*
+ * Put the CSA list into the message
+ */
+ csup->csu_csa_rec = csap;
+ for (cnt_csap = csap; cnt_csap; cnt_csap = cnt_csap->next) {
+ csup->csu_mcp.rec_cnt++;
+ }
+
+ /*
+ * Send the CSU Request
+ */
+ rc = scsp_send_msg(dcsp, csu_msg);
+ if (rc) {
+ scsp_free_msg(csu_msg);
+ return(rc);
+ }
+ free(csu_msg);
+ free(csup);
+
+ /*
+ * Save the CSA entries on the CSU Request retransmission
+ * queue and start the retransmission timer
+ */
+ rxp->sr_dcs = dcsp;
+ rxp->sr_csa = csap;
+ HARP_TIMER(&rxp->sr_t, dcsp->sd_csu_rexmt_int,
+ scsp_csu_req_retran_timeout);
+ LINK2TAIL(rxp, Scsp_csu_rexmt, dcsp->sd_csu_rexmt, sr_next);
+
+ return(0);
+}
+
+
+/*
+ * Send a CSU Reply message
+ *
+ * Arguments:
+ * dcsp pointer to DCS block for DCS
+ * csap pointer to CSAs to include
+ *
+ * Returns:
+ * 0 message sent OK
+ * errno reason for failure
+ *
+ */
+int
+scsp_send_csu_reply(dcsp, csap)
+ Scsp_dcs *dcsp;
+ Scsp_csa *csap;
+{
+ int rc;
+ Scsp_server *ssp = dcsp->sd_server;
+ Scsp_csa *csap1;
+ Scsp_msg *csu_msg;
+ Scsp_csu_msg *csup;
+
+ /*
+ * Return if CSA list is empty
+ */
+ if (!csap)
+ return(0);
+
+ /*
+ * Get memory for a CSU Reply message
+ */
+ csu_msg = calloc(1, sizeof(Scsp_msg));
+ if (csu_msg == NULL)
+ scsp_mem_err("scsp_send_csu_reply: sizeof(Scsp_msg)");
+ csup = calloc(1, sizeof(Scsp_csu_msg));
+ if (csup == NULL)
+ scsp_mem_err("scsp_send_csu_reply: sizeof(Scsp_csu_msg)");
+
+ /*
+ * Fill out constant fields
+ */
+ csu_msg->sc_msg_type = SCSP_CSU_REPLY_MSG;
+ csu_msg->sc_csu_msg = csup;
+ csup->csu_mcp.pid = ssp->ss_pid;
+ csup->csu_mcp.sgid = ssp->ss_sgid;
+ csup->csu_mcp.sid = ssp->ss_lsid;
+ csup->csu_mcp.rid = dcsp->sd_dcsid;
+
+ /*
+ * Put the CSA list into the message. Convert the CSAs into
+ * CSASs by freeing the protocol-specific portion.
+ */
+ csup->csu_csa_rec = csap;
+ for (csap1 = csap; csap1; csap1 = csap1->next) {
+ switch(dcsp->sd_server->ss_pid) {
+ /*
+ * We currently only support ATMARP
+ */
+ case SCSP_PROTO_ATMARP:
+ if (csap1->atmarp_data) {
+ free(csap1->atmarp_data);
+ csap1->atmarp_data =
+ (Scsp_atmarp_csa *)0;
+ }
+ break;
+ }
+ csup->csu_mcp.rec_cnt++;
+ }
+
+ /*
+ * Send the CSU Reply
+ */
+ rc = scsp_send_msg(dcsp, csu_msg);
+ scsp_free_msg(csu_msg);
+
+ return(rc);
+}
+
+
+/*
+ * Send a Hello message
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ *
+ * Returns:
+ * 0 success
+ * errno error encountered
+ *
+ */
+int
+scsp_send_hello(dcsp)
+ Scsp_dcs *dcsp;
+{
+ int rc;
+ Scsp_msg *hello;
+ Scsp_hello *hp;
+
+ /*
+ * Get memory for a Hello message
+ */
+ hello = calloc(1, sizeof(Scsp_msg));
+ if (hello == NULL)
+ scsp_mem_err("scsp_send_hello: sizeof(Scsp_msg)");
+ hp = calloc(1, sizeof(Scsp_hello));
+ if (hp == NULL)
+ scsp_mem_err("scsp_send_hello: sizeof(Scsp_hello)");
+
+ /*
+ * Set up the Hello message
+ */
+ hello->sc_msg_type = SCSP_HELLO_MSG;
+ hello->sc_hello = hp;
+ hp->hello_int = SCSP_HELLO_Interval;
+ hp->dead_factor = SCSP_HELLO_DF;
+ hp->family_id = dcsp->sd_server->ss_fid;
+ hp->hello_mcp.pid = dcsp->sd_server->ss_pid;
+ hp->hello_mcp.sgid = dcsp->sd_server->ss_sgid;
+ hp->hello_mcp.flags = 0;
+ hp->hello_mcp.rec_cnt = 0;
+ hp->hello_mcp.sid = dcsp->sd_server->ss_lsid;
+ hp->hello_mcp.rid = dcsp->sd_dcsid;
+
+ /*
+ * Send and free the message
+ */
+ rc = scsp_send_msg(dcsp, hello);
+ scsp_free_msg(hello);
+
+ return(rc);
+}
diff --git a/usr.sbin/atm/scspd/scsp_msg.h b/usr.sbin/atm/scspd/scsp_msg.h
new file mode 100644
index 0000000..bedea8a
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_msg.h
@@ -0,0 +1,461 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * SCSP message formats
+ *
+ */
+
+#ifndef _SCSP_SCSP_MSG_H
+#define _SCSP_SCSP_MSG_H
+
+
+/*
+ * ATMARP constants
+ */
+#define ARP_ATMFORUM 19
+#define ARP_TL_TMASK 0x40 /* Type mask */
+#define ARP_TL_NSAPA 0x00 /* Type = ATM Forum NSAPA */
+#define ARP_TL_E164 0x40 /* Type = E.164 */
+#define ARP_TL_LMASK 0x3f /* Length mask */
+
+
+/*
+ * SCSP version number
+ */
+#define SCSP_VER_1 1
+
+
+/*
+ * SCSP message types
+ */
+#define SCSP_CA_MSG 1
+#define SCSP_CSU_REQ_MSG 2
+#define SCSP_CSU_REPLY_MSG 3
+#define SCSP_CSUS_MSG 4
+#define SCSP_HELLO_MSG 5
+
+
+/*
+ * SCSP Client Protocol IDs
+ */
+#define SCSP_PROTO_ATMARP 1
+#define SCSP_PROTO_NHRP 2
+#define SCSP_PROTO_MARS 3
+#define SCSP_PROTO_DHCP 4
+#define SCSP_PROTO_LNNI 5
+
+
+/*
+ * Extension types
+ */
+#define SCSP_EXT_END 0
+#define SCSP_EXT_AUTH 1
+#define SCSP_EXT_VENDOR 2
+
+/*
+ * Sequence number bounds
+ */
+#define SCSP_CSA_SEQ_MIN 0x80000001
+#define SCSP_CSA_SEQ_MAX 0x7FFFFFFF
+
+
+/*
+ * Sender, Receiver, or Originator ID lengths
+ */
+#define SCSP_ATMARP_ID_LEN 4
+#define SCSP_NHRP_ID_LEN 4
+#define SCSP_MAX_ID_LEN 4
+
+
+/*
+ * Cache Key lengths
+ */
+#define SCSP_ATMARP_KEY_LEN 4
+#define SCSP_NHRP_KEY_LEN 4
+#define SCSP_MAX_KEY_LEN 4
+
+
+/*
+ * Fixed header
+ */
+struct scsp_nhdr {
+ u_char sh_ver; /* SCSP version */
+ u_char sh_type; /* Message type */
+ u_short sh_len; /* Message length */
+ u_short sh_checksum; /* IP checksum over message */
+ u_short sh_ext_off; /* Offset of first extension */
+};
+
+
+/*
+ * Mandatory common part
+ */
+struct scsp_nmcp {
+ u_short sm_pid; /* Protocol ID */
+ u_short sm_sgid; /* Server group ID */
+ u_short sm_fill_0; /* Unused */
+ u_short sm_flags; /* Flags--see below */
+ u_char sm_sid_len; /* Sender ID length */
+ u_char sm_rid_len; /* Receiver ID length */
+ u_short sm_rec_cnt; /* Number of records */
+#ifdef NOTDEF
+ /* Variable length fields */
+ u_char sm_sid[]; /* Sender ID (variable) */
+ u_char sm_rid[]; /* Receiver ID (variable) */
+#endif
+};
+
+
+/*
+ * Extensions part
+ */
+struct scsp_next {
+ u_short se_type; /* Extension type */
+ u_short se_len; /* Length */
+#ifdef NOTDEF
+ /* Variable length fields */
+ u_char se_value[]; /* Extension value */
+#endif
+};
+
+
+/*
+ * Cache State Advertisement record or
+ * Cache State Advertisement Summary record
+ */
+struct scsp_ncsa {
+ u_short scs_hop_cnt; /* Hop count */
+ u_short scs_len; /* Record length */
+ u_char scs_ck_len; /* Cache key length */
+ u_char scs_oid_len; /* Originator ID length */
+ u_short scs_nfill; /* Null bit and filler */
+ long scs_seq; /* Sequence number */
+#ifdef NOTDEF
+ /* Variable length fields */
+ u_char scs_ckey[]; /* Cache key */
+ u_char scs_oid[]; /* Originator ID */
+ u_char scs_proto[]; /* Protocol-specific (in CSA) */
+#endif
+};
+
+#define SCSP_CSAS_NULL 0x8000
+
+
+/*
+ * Cache Alignment message
+ */
+struct scsp_nca {
+ long sca_seq; /* Sequence number */
+ struct scsp_nmcp sca_mcp; /* Mandatory common */
+#ifdef NOTDEF
+ /* Variable length fields */
+ struct scsp_ncsa sca_rec[]; /* CSASs */
+#endif
+};
+
+#define SCSP_CA_M 0x8000 /* Master/Slave bit */
+#define SCSP_CA_I 0x4000 /* Initialization bit */
+#define SCSP_CA_O 0x2000 /* More bit */
+
+
+/*
+ * Cache State Update Request, Cache State Update Reply, or
+ * Cache State Update Solicit message
+ */
+struct scsp_ncsu_msg {
+ struct scsp_nmcp scr_mcp; /* Mandatory common */
+#ifdef NOTDEF
+ /* Variable length fields */
+ struct scsp_ncsa scr_rec[]; /* CSAs */
+#endif
+};
+
+
+/*
+ * Hello message
+ */
+struct scsp_nhello {
+ u_short sch_hi; /* Hello interval */
+ u_short sch_df; /* Dead factor */
+ u_short sch_fill_0; /* Unused */
+ u_short sch_fid; /* Family ID */
+ struct scsp_nmcp sch_mcp; /* Mandatory common */
+#ifdef NOTDEF
+ /* Variable-length fields */
+ struct scsp_nrid sch_rid[]; /* Receiver IDs */
+#endif
+};
+
+
+/*
+ * ATMARP-specific Cache State Advertisement record
+ */
+struct scsp_atmarp_ncsa {
+ u_short sa_hrd; /* Hardware type -- 0x0013 */
+ u_short sa_pro; /* Protocol type -- 0x0800 */
+ u_char sa_shtl; /* Src ATM addr type/len */
+ u_char sa_sstl; /* Src ATM subaddr type/len */
+ u_char sa_state; /* State */
+ u_char sa_fill1; /* Unused */
+ u_char sa_spln; /* Src proto addr type */
+ u_char sa_thtl; /* Tgt ATM addr type/len */
+ u_char sa_tstl; /* Tgt ATM subaddr type/len */
+ u_char sa_tpln; /* Tgt proto addr len */
+#ifdef NOTDEF
+ /* Variable-length fields */
+ u_char sa_sha[]; /* Source ATM addr */
+ u_char sa_ssa[]; /* Source ATM subaddr */
+ u_char sa_spa[]; /* Source IP addr */
+ u_char sa_tha[]; /* Target ATM addr */
+ u_char sa_tsa[]; /* Target ATM subaddr */
+ u_char sa_tpa[]; /* Target IP addr */
+#endif
+};
+
+
+/*
+ * NHRP-specific Cache State Advertisement record
+ */
+struct scsp_nhrp_ncsa {
+ u_short sn_af; /* Address family */
+ u_short sn_pro; /* NHRP protocol type */
+ u_char sn_snap[5]; /* SNAP header */
+ u_char sn_ver; /* NHRP version no. */
+ u_short sn_flags; /* Flags */
+ u_long sn_rid; /* Request ID */
+ u_char sn_state; /* State */
+ u_char sn_pln; /* Prefix length */
+ u_short sn_fill1; /* Unused */
+ u_short sn_mtu; /* MTU */
+ u_short sn_hold; /* Holding time */
+ u_char sn_csatl; /* Client addr type/len */
+ u_char sn_csstl; /* Client subaddr type/len */
+ u_char sn_cpln; /* Client proto addr len */
+ u_char sn_pref; /* Preference for next hop */
+#ifdef NOTDEF
+ /* Variable-length fields */
+ u_char sn_csa[]; /* Client subnetwork addr */
+ u_char sn_css[]; /* Client subnetwork subaddr */
+ u_char sn_cpa[]; /* Client protocol addr */
+#endif
+};
+
+
+/*
+ * SCSP messages in internal format
+ *
+ *
+ * Fixed message header
+ */
+struct scsp_hdr {
+ u_char msg_type; /* Message type */
+};
+typedef struct scsp_hdr Scsp_hdr;
+
+
+/*
+ * Sender or Receiver ID structure
+ */
+struct scsp_id {
+ struct scsp_id *next; /* Next ID */
+ u_char id_len; /* ID length */
+ u_char id[SCSP_MAX_ID_LEN]; /* ID */
+};
+typedef struct scsp_id Scsp_id;
+
+
+/*
+ * Cacke Key structure
+ */
+struct scsp_ckey {
+ u_char key_len; /* Cache key length */
+ u_char key[SCSP_MAX_KEY_LEN]; /* Cache key */
+};
+typedef struct scsp_ckey Scsp_ckey;
+
+
+/*
+ * Mandatory common part
+ */
+struct scsp_mcp {
+ u_short pid; /* Protocol ID */
+ u_short sgid; /* Server group ID */
+ u_short flags; /* Flags */
+ u_short rec_cnt; /* No. of records attached */
+ Scsp_id sid; /* Sender ID */
+ Scsp_id rid; /* Receiver ID */
+};
+typedef struct scsp_mcp Scsp_mcp;
+
+
+/*
+ * Extensions part
+ */
+struct scsp_ext {
+ struct scsp_ext *next; /* Next extension */
+ u_short type; /* Extension type */
+ u_short len; /* Length */
+#ifdef NOTDEF
+ /* Variable length fields */
+ u_char value[]; /* Extension value */
+#endif
+};
+typedef struct scsp_ext Scsp_ext;
+
+
+/*
+ * Cache State Advertisement record or
+ * Cache State Advertisement Summary record
+ */
+struct scsp_csa {
+ struct scsp_csa *next; /* Next CSAS record */
+ u_short hops; /* Hop count */
+ u_char null; /* Null flag */
+ u_long seq; /* CSA seq. no. */
+ Scsp_ckey key; /* Cache key */
+ Scsp_id oid; /* Originator ID */
+ int trans_ct; /* No. of times CSA sent */
+ struct scsp_atmarp_csa *atmarp_data; /* ATMARP data */
+#ifdef NOTDEF
+ struct scsp_nhrp_csa *nhrp_data; /* NHRP data */
+#endif
+};
+typedef struct scsp_csa Scsp_csa;
+
+/*
+ * Macro to free a CSA and any associated protocol-specific data
+ */
+#define SCSP_FREE_CSA(c) \
+{ \
+ if ((c)->atmarp_data) \
+ free((c)->atmarp_data); \
+ free((c)); \
+}
+
+
+/*
+ * Cache Alignment message
+ */
+struct scsp_ca {
+ long ca_seq; /* CA msg sequence no. */
+ u_char ca_m; /* Master/slave bit */
+ u_char ca_i; /* Initialization bit */
+ u_char ca_o; /* More bit */
+ Scsp_mcp ca_mcp; /* Mandatory common part */
+ Scsp_csa *ca_csa_rec; /* Ptr. to CSAS records */
+};
+typedef struct scsp_ca Scsp_ca;
+
+
+/*
+ * Cache State Update Request, Cache State Update Reply, or
+ * Cache State Update Solicit message
+ */
+struct scsp_csu_msg {
+ Scsp_mcp csu_mcp; /* Mandatory common part */
+ Scsp_csa *csu_csa_rec; /* Ptr. to CSA records */
+};
+typedef struct scsp_csu_msg Scsp_csu_msg;
+
+
+/*
+ * Hello message
+ */
+struct scsp_hello {
+ u_short hello_int; /* Hello interval */
+ u_short dead_factor; /* When is DCS dead? */
+ u_short family_id; /* Family ID */
+ Scsp_mcp hello_mcp; /* Mandatory common part */
+};
+typedef struct scsp_hello Scsp_hello;
+
+
+/*
+ * NHRP-specific Cache State Advertisement record
+ */
+struct scsp_nhrp_csa {
+ u_char req_id; /* Request ID */
+ u_char state; /* State */
+ u_char pref_len; /* Prefix length */
+ u_short flags; /* See below */
+ u_short mtu; /* Maximim transmission unit */
+ u_short hold_time; /* Entry holding time */
+ u_char caddr_tlen; /* Client addr type/length */
+ u_char csaddr_tlen; /* Client subaddr type/length */
+ u_char cproto_len; /* Client proto addr length */
+ u_char pref; /* Preference */
+ Atm_addr caddr; /* Client address */
+ Atm_addr csaddr; /* Client subaddress */
+ struct in_addr cproto_addr; /* Client protocol address */
+};
+typedef struct scsp_nhrp Scsp_nhrp;
+
+#define SCSP_NHRP_UNIQ 0x8000
+#define SCSP_NHRP_ARP 0x4000
+
+
+/*
+ * ATMARP-specific Cache State Advertisement record
+ */
+struct scsp_atmarp_csa {
+ u_char sa_state; /* State */
+ Atm_addr sa_sha; /* Source ATM addr */
+ Atm_addr sa_ssa; /* Source ATM subaddr */
+ struct in_addr sa_spa; /* Source IP addr */
+ Atm_addr sa_tha; /* Target ATM addr */
+ Atm_addr sa_tsa; /* Target ATM subaddr */
+ struct in_addr sa_tpa; /* Target IP addr */
+};
+typedef struct scsp_atmarp_csa Scsp_atmarp_csa;
+
+
+/*
+ * SCSP message
+ */
+struct scsp_msg {
+ Scsp_hdr sc_hdr;
+ union {
+ Scsp_ca *sc_u_ca;
+ Scsp_csu_msg *sc_u_csu_msg;
+ Scsp_hello *sc_u_hello;
+ } sc_msg_u;
+ Scsp_ext *sc_ext;
+};
+typedef struct scsp_msg Scsp_msg;
+
+#define sc_msg_type sc_hdr.msg_type
+#define sc_ca sc_msg_u.sc_u_ca
+#define sc_csu_msg sc_msg_u.sc_u_csu_msg
+#define sc_hello sc_msg_u.sc_u_hello
+
+#endif /* _SCSP_SCSP_MSG_H */
diff --git a/usr.sbin/atm/scspd/scsp_output.c b/usr.sbin/atm/scspd/scsp_output.c
new file mode 100644
index 0000000..c2ea657
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_output.c
@@ -0,0 +1,930 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * Output packet processing
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netatm/port.h>
+#include <netatm/queue.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+
+#include <errno.h>
+#include <libatm.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "scsp_msg.h"
+#include "scsp_if.h"
+#include "scsp_var.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+/*
+ * Put a long integer into the output buffer
+ *
+ * This routine is provided for cases where long ints may not be
+ * word-aligned in the output buffer.
+ *
+ * Arguments:
+ * l long integer
+ * cp pointer to output buffer
+ *
+ * Returns:
+ * None
+ *
+ */
+static void
+put_long(l, cp)
+ u_long l;
+ u_char *cp;
+{
+ u_long nl;
+
+ /*
+ * Convert to network order and copy to output buffer
+ */
+ nl = htonl(l);
+ bcopy(&nl, cp, sizeof(u_long));
+}
+
+
+/*
+ * Format a Sender or Receiver ID
+ *
+ * Arguments:
+ * idp ponter to ID structure
+ * buff pointer to ID
+ *
+ * Returns:
+ * 0 input was invalid
+ * else length of ID processed
+ *
+ */
+static int
+scsp_format_id(idp, buff)
+ Scsp_id *idp;
+ char *buff;
+{
+ /*
+ * Copy the ID
+ */
+ bcopy(idp->id, buff, idp->id_len);
+
+ /*
+ * Return the ID length
+ */
+ return(idp->id_len);
+}
+
+
+/*
+ * Format the Mandatory Common Part of an SCSP input packet
+ *
+ * Arguments:
+ * mcp pointer to MCP
+ * buff pointer to mandatory common part
+ *
+ * Returns:
+ * 0 input was invalid
+ * else length of MCP in message
+ *
+ */
+static int
+scsp_format_mcp(mcp, buff)
+ Scsp_mcp *mcp;
+ char *buff;
+{
+ int len;
+ char *odp;
+ struct scsp_nmcp *smp;
+
+ /*
+ * Set the protocol ID
+ */
+ smp = (struct scsp_nmcp *)buff;
+ smp->sm_pid = htons(mcp->pid);
+
+ /*
+ * Set the server group ID
+ */
+ smp->sm_sgid = htons(mcp->sgid);
+
+ /*
+ * Set the flags
+ */
+ smp->sm_flags = htons(mcp->flags);
+
+ /*
+ * Set the sender ID and length
+ */
+ smp->sm_sid_len = mcp->sid.id_len;
+ odp = buff + sizeof(struct scsp_nmcp);
+ len = scsp_format_id(&mcp->sid, odp);
+ if (len == 0) {
+ goto mcp_invalid;
+ }
+
+ /*
+ * Set the receiver ID and length
+ */
+ smp->sm_rid_len = mcp->rid.id_len;
+ odp += mcp->sid.id_len;
+ len = scsp_format_id(&mcp->rid, odp);
+ if (len == 0) {
+ goto mcp_invalid;
+ }
+
+ /*
+ * Set the record count
+ */
+ smp->sm_rec_cnt = htons(mcp->rec_cnt);
+
+ /*
+ * Return the length of data we processed
+ */
+ return(sizeof(struct scsp_nmcp) + mcp->sid.id_len +
+ mcp->rid.id_len);
+
+mcp_invalid:
+ return(0);
+}
+
+
+/*
+ * Format an Extension
+ *
+ * Arguments:
+ * exp pointer to extension in internal format
+ * buff pointer to output buffer
+ * blen space available in buffer
+ *
+ * Returns:
+ * 0 input was invalid
+ * else length of extension processed
+ *
+ */
+static int
+scsp_format_ext(exp, buff, blen)
+ Scsp_ext *exp;
+ char *buff;
+ int blen;
+{
+ struct scsp_next *sep;
+
+ /*
+ * Make sure there's room in the buffer
+ */
+ if (blen < (sizeof(struct scsp_next) + exp->len))
+ return(0);
+
+ /*
+ * Set the type
+ */
+ sep = (struct scsp_next *)buff;
+ sep->se_type = htons(exp->type);
+
+ /*
+ * Set the length
+ */
+ sep->se_len = htons(exp->len);
+
+ /*
+ * Set the value
+ */
+ if (exp->len > 0) {
+ buff += sizeof(struct scsp_next);
+ bcopy((caddr_t)exp + sizeof(Scsp_ext),
+ buff,
+ exp->len);
+ }
+
+ /*
+ * Return the number of bytes processed
+ */
+ return(sizeof(struct scsp_next) + exp->len);
+}
+
+
+/*
+ * Format the ATMARP part of a CSA record
+ *
+ * Arguments:
+ * acsp pointer to ATMARP protocol-specific CSA record
+ * buff pointer to output buffer
+ *
+ * Returns:
+ * 0 input was invalid
+ * else length of record processed
+ *
+ */
+static int
+scsp_format_atmarp(acsp, buff)
+ Scsp_atmarp_csa *acsp;
+ char *buff;
+{
+ char *cp;
+ int len, pkt_len;
+ struct scsp_atmarp_ncsa *sanp;
+
+ /*
+ * Figure out how long PDU is going to be
+ */
+ pkt_len = sizeof(struct scsp_atmarp_ncsa);
+ switch (acsp->sa_sha.address_format) {
+ case T_ATM_ENDSYS_ADDR:
+ pkt_len += acsp->sa_sha.address_length;
+ break;
+
+ case T_ATM_E164_ADDR:
+ pkt_len += acsp->sa_sha.address_length;
+ if (acsp->sa_ssa.address_format == T_ATM_ENDSYS_ADDR)
+ pkt_len += acsp->sa_ssa.address_length;
+ break;
+ }
+
+ switch (acsp->sa_tha.address_format) {
+ case T_ATM_ENDSYS_ADDR:
+ pkt_len += acsp->sa_tha.address_length;
+ break;
+
+ case T_ATM_E164_ADDR:
+ pkt_len += acsp->sa_tha.address_length;
+ if (acsp->sa_tha.address_format == T_ATM_ENDSYS_ADDR)
+ pkt_len += acsp->sa_tha.address_length;
+ break;
+ }
+
+ if (acsp->sa_spa.s_addr != 0)
+ pkt_len += sizeof(struct in_addr);
+
+ if (acsp->sa_tpa.s_addr != 0)
+ pkt_len += sizeof(struct in_addr);
+
+ /*
+ * Set up pointers
+ */
+ sanp = (struct scsp_atmarp_ncsa *)buff;
+ cp = (char *)sanp + sizeof(struct scsp_atmarp_ncsa);
+
+ /*
+ * Build fields
+ */
+ sanp->sa_hrd = htons(ARP_ATMFORUM);
+ sanp->sa_pro = htons(ETHERTYPE_IP);
+
+ /* sa_sha */
+ len = acsp->sa_sha.address_length;
+ switch (acsp->sa_sha.address_format) {
+ case T_ATM_ENDSYS_ADDR:
+ sanp->sa_shtl = ARP_TL_NSAPA | (len & ARP_TL_LMASK);
+
+ /* sa_sha */
+ bcopy(acsp->sa_sha.address, cp, len);
+ cp += len;
+
+ sanp->sa_sstl = 0;
+ break;
+
+ case T_ATM_E164_ADDR:
+ sanp->sa_shtl = ARP_TL_E164 | (len & ARP_TL_LMASK);
+
+ /* sa_sha */
+ bcopy(acsp->sa_sha.address, cp, len);
+ cp += len;
+
+ if (acsp->sa_ssa.address_format == T_ATM_ENDSYS_ADDR) {
+ len = acsp->sa_ssa.address_length;
+ sanp->sa_sstl = ARP_TL_NSAPA |
+ (len & ARP_TL_LMASK);
+
+ /* sa_ssa */
+ bcopy(acsp->sa_ssa.address, cp, len);
+ cp += len;
+ } else
+ sanp->sa_sstl = 0;
+ break;
+
+ default:
+ sanp->sa_shtl = 0;
+ sanp->sa_sstl = 0;
+ }
+
+ /* sa_state */
+ sanp->sa_state = acsp->sa_state;
+ sanp->sa_fill1 = 0;
+
+ /* sa_spa */
+ if (acsp->sa_spa.s_addr != 0) {
+ sanp->sa_spln = sizeof(struct in_addr);
+ bcopy(&acsp->sa_spa, cp, sizeof(struct in_addr));
+ cp += sizeof(struct in_addr);
+ }
+
+ /* sa_tha */
+ len = acsp->sa_tha.address_length;
+ switch (acsp->sa_tha.address_format) {
+ case T_ATM_ENDSYS_ADDR:
+ sanp->sa_thtl = ARP_TL_NSAPA | (len & ARP_TL_LMASK);
+
+ /* sa_tha */
+ bcopy(acsp->sa_tha.address, cp, len);
+ cp += len;
+
+ sanp->sa_tstl = 0;
+ break;
+
+ case T_ATM_E164_ADDR:
+ sanp->sa_thtl = ARP_TL_E164 | (len & ARP_TL_LMASK);
+
+ /* sa_tha */
+ bcopy(acsp->sa_tha.address, cp, len);
+ cp += len;
+
+ if (acsp->sa_tsa.address_format == T_ATM_ENDSYS_ADDR) {
+ len = acsp->sa_tha.address_length;
+ sanp->sa_tstl = ARP_TL_NSAPA |
+ (len & ARP_TL_LMASK);
+
+ /* sa_tsa */
+ bcopy(acsp->sa_tsa.address, cp, len);
+ cp += len;
+ } else
+ sanp->sa_tstl = 0;
+ break;
+
+ default:
+ sanp->sa_thtl = 0;
+ sanp->sa_tstl = 0;
+ }
+
+ /* sa_tpa */
+ if (acsp->sa_tpa.s_addr != 0) {
+ sanp->sa_tpln = sizeof(struct in_addr);
+ bcopy(&acsp->sa_tpa, cp, sizeof(struct in_addr));
+ }
+
+ return(pkt_len);
+}
+
+
+/*
+ * Format a Cache State Advertisement or Cache State Advertisement
+ * Summary record
+ *
+ * Arguments:
+ * csapp pointer to CSA or CSAS
+ * buff pointer to output buffer
+ *
+ * Returns:
+ * 0 input was invalid
+ * else length of record processed
+ *
+ */
+static int
+scsp_format_csa(csap, buff)
+ Scsp_csa *csap;
+ char *buff;
+{
+ int len = 0;
+ char *odp;
+ struct scsp_ncsa *scp;
+
+ /*
+ * Set the hop count
+ */
+ scp = (struct scsp_ncsa *)buff;
+ scp->scs_hop_cnt = htons(csap->hops);
+
+ /*
+ * Set the null flag
+ */
+ if (csap->null) {
+ scp->scs_nfill = htons(SCSP_CSAS_NULL);
+ }
+
+ /*
+ * Set the sequence number
+ */
+ put_long(csap->seq, (u_char *)&scp->scs_seq);
+
+ /*
+ * Set the cache key
+ */
+ scp->scs_ck_len = csap->key.key_len;
+ odp = buff + sizeof(struct scsp_ncsa);
+ bcopy(csap->key.key, odp, scp->scs_ck_len);
+
+ /*
+ * Set the originator ID
+ */
+ odp += scp->scs_ck_len;
+ scp->scs_oid_len = scsp_format_id(&csap->oid, odp);
+
+ /*
+ * Set the protocol-specific data, if present. At the
+ * moment, we only handle data for ATMARP.
+ */
+ if (csap->atmarp_data) {
+ odp += scp->scs_oid_len;
+ len = scsp_format_atmarp(csap->atmarp_data, odp);
+ }
+
+ /*
+ * Set the record length
+ */
+ scp->scs_len = htons(sizeof(struct scsp_ncsa) +
+ scp->scs_ck_len + scp->scs_oid_len +
+ len);
+
+ /*
+ * Return the length of data we processed
+ */
+ return(ntohs(scp->scs_len));
+}
+
+
+/*
+ * Format a Cache Alignment message
+ *
+ * Arguments:
+ * cap pointer to CA message
+ * buff pointer to output buffer
+ * blen space available in buffer
+ *
+ * Returns:
+ * 0 input was invalid
+ * else length of CA message processed
+ *
+ */
+static int
+scsp_format_ca(cap, buff, blen)
+ Scsp_ca *cap;
+ char *buff;
+ int blen;
+{
+ int i, len, proc_len;
+ struct scsp_nca *scap;
+ Scsp_csa *csap;
+
+ /*
+ * Set the sequence number
+ */
+ scap = (struct scsp_nca *)buff;
+ put_long(cap->ca_seq, (u_char *)&scap->sca_seq);
+ proc_len = sizeof(scap->sca_seq);
+ buff += sizeof(scap->sca_seq);
+
+ /*
+ * Set the flags
+ */
+ cap->ca_mcp.flags = 0;
+ if (cap->ca_m)
+ cap->ca_mcp.flags |= SCSP_CA_M;
+ if (cap->ca_i)
+ cap->ca_mcp.flags |= SCSP_CA_I;
+ if (cap->ca_o)
+ cap->ca_mcp.flags |= SCSP_CA_O;
+
+ /*
+ * Format the mandatory common part of the message
+ */
+ len = scsp_format_mcp(&cap->ca_mcp, buff);
+ if (len == 0)
+ goto ca_invalid;
+ buff += len;
+ proc_len += len;
+
+ /*
+ * Put any CSAS records into the message
+ */
+ for (i = 0, csap = cap->ca_csa_rec; i < cap->ca_mcp.rec_cnt;
+ i++, csap = csap->next) {
+ len = scsp_format_csa(csap, buff);
+ buff += len;
+ proc_len += len;
+ if (proc_len > blen) {
+ scsp_log(LOG_CRIT, "scsp_format_ca: buffer overflow");
+ abort();
+ }
+ }
+
+ /*
+ * Return the length of processed data
+ */
+ return(proc_len);
+
+ca_invalid:
+ return(0);
+}
+
+
+/*
+ * Format a Cache State Update Request, Cache State Update Reply, or
+ * Cache State Update Solicit message. These all have the same format,
+ * a Mandatory Common Part followed by a number of CSA or CSAS records.
+ *
+ * Arguments:
+ * csup pointer to location to put pointer to CSU Req message
+ * buff pointer to output buffer
+ * blen space available in buffer
+ *
+ * Returns:
+ * 0 input was invalid
+ * else length of CSU Req message processed
+ *
+ */
+static int
+scsp_format_csu(csup, buff, blen)
+ Scsp_csu_msg *csup;
+ char *buff;
+ int blen;
+{
+ int i, len, proc_len;
+ struct scsp_ncsu_msg *scsup;
+ Scsp_csa *csap;
+
+ /*
+ * Format the mandatory common part of the message
+ */
+ scsup = (struct scsp_ncsu_msg *)buff;
+ len = scsp_format_mcp(&csup->csu_mcp, buff);
+ if (len == 0)
+ goto csu_invalid;
+ buff += len;
+ proc_len = len;
+
+ /*
+ * Put the CSAS records into the message
+ */
+ for (i = 0, csap = csup->csu_csa_rec;
+ i < csup->csu_mcp.rec_cnt && csap;
+ i++, csap = csap->next) {
+ len = scsp_format_csa(csap, buff);
+ buff += len;
+ proc_len += len;
+ if (proc_len > blen) {
+ scsp_log(LOG_CRIT, "scsp_format_csu: buffer overflow");
+ abort();
+ }
+ }
+
+ /*
+ * Return the length of processed data
+ */
+ return(proc_len);
+
+csu_invalid:
+ return(0);
+}
+
+
+/*
+ * Format a Hello message
+ *
+ * Arguments:
+ * hpp pointer to Hello message
+ * buff pointer to output buffer
+ * blen space available in buffer
+ *
+ * Returns:
+ * 0 input was invalid
+ * else length of Hello message processed
+ *
+ */
+static int
+scsp_format_hello(hp, buff, blen)
+ Scsp_hello *hp;
+ char *buff;
+ int blen;
+{
+ int len, proc_len;
+ struct scsp_nhello *shp;
+ Scsp_id *ridp;
+
+ /*
+ * Set the hello interval
+ */
+ shp = (struct scsp_nhello *)buff;
+ shp->sch_hi = htons(hp->hello_int);
+
+ /*
+ * Set the dead factor
+ */
+ shp->sch_df = htons(hp->dead_factor);
+
+ /*
+ * Set the family ID
+ */
+ shp->sch_fid = htons(hp->family_id);
+
+ /*
+ * Process the mandatory common part of the message
+ */
+ proc_len = sizeof(struct scsp_nhello) -
+ sizeof(struct scsp_nmcp);
+ buff += proc_len;
+ len = scsp_format_mcp(&hp->hello_mcp, buff);
+ if (len == 0)
+ goto hello_invalid;
+ proc_len += len;
+ buff += len;
+
+ /*
+ * Add any additional receiver ID records to the message
+ */
+ for (ridp = hp->hello_mcp.rid.next; ridp;
+ ridp = ridp->next) {
+ len = scsp_format_id(ridp, buff);
+ if (len == 0) {
+ goto hello_invalid;
+ }
+ proc_len += len;
+ buff += len;
+ }
+
+ /*
+ * Return the length of the Hello message body
+ */
+ if (proc_len > blen) {
+ scsp_log(LOG_CRIT, "scsp_format_hello: buffer overflow");
+ abort();
+ }
+ return(proc_len);
+
+hello_invalid:
+ return(0);
+}
+
+
+/*
+ * Format an SCSP output packet
+ *
+ * Arguments:
+ * dcsp pointer to DCS for which message is being prepared
+ * msg pointer to input packet
+ * bpp pointer to location to put pointer to formatted packet
+ *
+ * Returns:
+ * 0 input packet was invalid
+ * else length of formatted packet
+ *
+ */
+int
+scsp_format_msg(dcsp, msg, bpp)
+ Scsp_dcs *dcsp;
+ Scsp_msg *msg;
+ char **bpp;
+{
+ char *buff = (char *)0, *e_buff = (char *)0;
+ int buff_len, e_buff_len;
+ int e_len, len, plen;
+ struct scsp_nhdr *shp;
+ Scsp_ext *exp;
+
+ /*
+ * Allocate a buffer for the message
+ */
+ buff_len = dcsp->sd_server->ss_mtu;
+ buff = calloc(1, buff_len);
+ if (buff == NULL)
+ scsp_mem_err("scsp_format_msg: dcsp->sd_server->ss_mtu");
+ *bpp = buff;
+
+ /*
+ * Encode the fixed header
+ *
+ * Set the version
+ */
+ shp = (struct scsp_nhdr *)buff;
+ shp->sh_ver = SCSP_VER_1;
+
+ /*
+ * Set the message type
+ */
+ shp->sh_type = msg->sc_msg_type;
+
+ /*
+ * Point past the fixed header
+ */
+ len = sizeof(struct scsp_nhdr);
+ buff_len -= len;
+
+ /*
+ * Encode any extensions into a temporary buffer
+ */
+ e_len = 0;
+ if (msg->sc_ext) {
+ /*
+ * Get a buffer for the extensions
+ */
+ e_buff_len = 1024;
+ e_buff = calloc(1, e_buff_len);
+ if (buff)
+ scsp_mem_err("scsp_format_msg: e_buff_len");
+
+ /*
+ * Encode the extensions
+ */
+ for (exp = msg->sc_ext = 0; exp; exp = exp->next) {
+ plen = scsp_format_ext(exp, e_buff + e_len,
+ e_buff_len - e_len);
+ if (plen == 0) {
+ goto ignore;
+ }
+ e_len += plen;
+ }
+
+ /*
+ * Free the buffer if we didn't use it
+ */
+ if (!e_len) {
+ free(e_buff);
+ e_buff = (char *)0;
+ }
+ }
+ buff_len -= e_len;
+
+ /*
+ * Encode the body of the message, depending on the type
+ */
+ switch(msg->sc_msg_type) {
+ case SCSP_CA_MSG:
+ plen = scsp_format_ca(msg->sc_ca, buff + len, buff_len);
+ break;
+ case SCSP_CSU_REQ_MSG:
+ case SCSP_CSU_REPLY_MSG:
+ case SCSP_CSUS_MSG:
+ plen = scsp_format_csu(msg->sc_csu_msg, buff + len,
+ buff_len);
+ break;
+ case SCSP_HELLO_MSG:
+ plen = scsp_format_hello(msg->sc_hello, buff + len,
+ buff_len);
+ break;
+ default:
+ goto ignore;
+ }
+ if (plen == 0) {
+ goto ignore;
+ }
+ len += plen;
+
+ /*
+ * Copy the extensions to the end of the message
+ */
+ if (e_len) {
+ shp->sh_ext_off = htons(len);
+ bcopy(e_buff, buff + len, e_len);
+ free(e_buff);
+ }
+
+ /*
+ * Set the length
+ */
+ shp->sh_len = htons(len);
+
+ /*
+ * Compute the message checksum
+ */
+ shp->sh_checksum = htons(ip_checksum(buff, len));
+
+ /*
+ * Return the length of the buffer
+ */
+ return(len);
+
+ignore:
+ if (buff)
+ free(buff);
+ if (e_buff)
+ free(e_buff);
+ *bpp = (char *)0;
+ return(0);
+}
+
+
+/*
+ * Send an SCSP message
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * msg pointer to message to send
+ *
+ * Returns:
+ * 0 success
+ * errno error encountered
+ *
+ */
+int
+scsp_send_msg(dcsp, msg)
+ Scsp_dcs *dcsp;
+ Scsp_msg *msg;
+{
+ int len, rc;
+ char *buff;
+
+ /*
+ * Make sure we have a socket open
+ */
+ if (dcsp->sd_sock == -1) {
+ return(EBADF);
+ }
+
+ /*
+ * Trace the message
+ */
+ if (((scsp_trace_mode & SCSP_TRACE_HELLO_MSG) &&
+ msg->sc_msg_type == SCSP_HELLO_MSG) ||
+ ((scsp_trace_mode & SCSP_TRACE_CA_MSG) &&
+ msg->sc_msg_type != SCSP_HELLO_MSG)) {
+ scsp_trace_msg(dcsp, msg, 0);
+ scsp_trace("\n");
+ }
+
+ /*
+ * Put the message into network format
+ */
+ len = scsp_format_msg(dcsp, msg, &buff);
+ if (len == 0) {
+ scsp_log(LOG_ERR, "scsp_send_msg: message conversion failed\n");
+ abort();
+ }
+
+ /*
+ * Write the message to the DCS
+ */
+ rc = write(dcsp->sd_sock, (void *)buff, len);
+ free(buff);
+ if (rc == len || (rc == -1 && errno == EINPROGRESS)) {
+ rc = 0;
+ } else {
+ /*
+ * There was an error on the write--close the VCC
+ */
+ (void)close(dcsp->sd_sock);
+ dcsp->sd_sock = -1;
+
+ /*
+ * Inform the Hello FSM
+ */
+ (void)scsp_hfsm(dcsp, SCSP_HFSM_VC_CLOSED,
+ (Scsp_msg *)0);
+
+ /*
+ * Set the return code
+ */
+ if (rc == -1)
+ rc = errno;
+ else
+ rc = EINVAL;
+ }
+
+ return(rc);
+}
diff --git a/usr.sbin/atm/scspd/scsp_print.c b/usr.sbin/atm/scspd/scsp_print.c
new file mode 100644
index 0000000..eb9d6f5
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_print.c
@@ -0,0 +1,1301 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * Print routines
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netatm/port.h>
+#include <netatm/queue.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+
+#include <errno.h>
+#include <libatm.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "scsp_msg.h"
+#include "scsp_if.h"
+#include "scsp_var.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+/*
+ * Indent string
+ */
+#define MIN_INDENT 2
+#define MAX_INDENT 64
+static char indent[MAX_INDENT + 1];
+
+
+/*
+ * Value-name translation table entry
+ */
+struct type_name {
+ char *name;
+ u_char type;
+};
+typedef struct type_name Type_name;
+
+
+/*
+ * SCSP name-type tables
+ */
+static Type_name if_msg_types[] = {
+ { "Config Request", SCSP_CFG_REQ },
+ { "Config Response", SCSP_CFG_RSP },
+ { "Cache Indication", SCSP_CACHE_IND },
+ { "Cache Response", SCSP_CACHE_RSP },
+ { "Solicit Indication", SCSP_SOLICIT_IND },
+ { "Solicit Response", SCSP_SOLICIT_RSP },
+ { "Cache Update Indication", SCSP_UPDATE_IND },
+ { "Cache Update Request", SCSP_UPDATE_REQ },
+ { "Cache Update Response", SCSP_UPDATE_RSP },
+ { (char *)0, 0 }
+};
+
+static Type_name msg_types[] = {
+ { "Cache Alignment", SCSP_CA_MSG },
+ { "CSU Request", SCSP_CSU_REQ_MSG },
+ { "CSU Reply", SCSP_CSU_REPLY_MSG },
+ { "CSU Solicit", SCSP_CSUS_MSG },
+ { "Hello", SCSP_HELLO_MSG },
+ { (char *)0, 0 }
+};
+
+static Type_name proto_types[] = {
+ { "ATMARP", SCSP_PROTO_ATMARP },
+ { "NHRP", SCSP_PROTO_NHRP },
+ { "MARS", SCSP_PROTO_MARS },
+ { "DHCP", SCSP_PROTO_DHCP },
+ { "LNNI", SCSP_PROTO_LNNI },
+ { (char *)0, 0 }
+};
+
+static Type_name ext_types[] = {
+ { "End of Extensions", SCSP_EXT_END },
+ { "Authentication", SCSP_EXT_AUTH },
+ { "Vendor Private", SCSP_EXT_VENDOR },
+ { (char *)0, 0 }
+};
+
+static Type_name hfsm_state_names[] = {
+ { "Down", SCSP_HFSM_DOWN },
+ { "Waiting", SCSP_HFSM_WAITING },
+ { "Unidirectional", SCSP_HFSM_UNI_DIR },
+ { "Bidirectional", SCSP_HFSM_BI_DIR },
+ { (char *)0, 0 }
+};
+
+static Type_name hfsm_event_names[] = {
+ { "VC open", SCSP_HFSM_VC_ESTAB },
+ { "VC closed", SCSP_HFSM_VC_CLOSED },
+ { "Hello timer", SCSP_HFSM_HELLO_T },
+ { "Receive timer", SCSP_HFSM_RCV_T },
+ { "Msg received", SCSP_HFSM_RCVD },
+ { (char *)0, 0 }
+};
+
+static Type_name cafsm_state_names[] = {
+ { "Down", SCSP_CAFSM_DOWN },
+ { "Master/Slave negotiation", SCSP_CAFSM_NEG },
+ { "Master", SCSP_CAFSM_MASTER },
+ { "Slave", SCSP_CAFSM_SLAVE },
+ { "Update cache", SCSP_CAFSM_UPDATE },
+ { "Aligned", SCSP_CAFSM_ALIGNED },
+ { (char *)0, 0 }
+};
+
+static Type_name cafsm_event_names[] = {
+ { "Hello FSM up", SCSP_CAFSM_HELLO_UP },
+ { "Hello FSM down", SCSP_CAFSM_HELLO_DOWN },
+ { "CA received", SCSP_CAFSM_CA_MSG },
+ { "CSU Solicit received", SCSP_CAFSM_CSUS_MSG },
+ { "CSU Request received", SCSP_CAFSM_CSU_REQ },
+ { "CSU Reply received", SCSP_CAFSM_CSU_REPLY },
+ { "CA timer", SCSP_CAFSM_CA_T },
+ { "CSUS timer", SCSP_CAFSM_CSUS_T },
+ { "CSU timer", SCSP_CAFSM_CSU_T },
+ { "Cache Update", SCSP_CAFSM_CACHE_UPD },
+ { "Cache Response", SCSP_CAFSM_CACHE_RSP },
+ { (char *)0, 0 }
+};
+
+static Type_name cifsm_state_names[] = {
+ { "Null", SCSP_CIFSM_NULL },
+ { "Summarize", SCSP_CIFSM_SUM },
+ { "Update", SCSP_CIFSM_UPD },
+ { "Aligned", SCSP_CIFSM_ALIGN },
+ { (char *)0, 0 }
+};
+
+static Type_name cifsm_event_names[] = {
+ { "CA FSM down", SCSP_CIFSM_CA_DOWN },
+ { "CA FSM to Summarize",SCSP_CIFSM_CA_SUMM },
+ { "CA FSM to Update", SCSP_CIFSM_CA_UPD },
+ { "CA FSM to Aligned", SCSP_CIFSM_CA_ALIGN },
+ { "Solicit Rsp", SCSP_CIFSM_SOL_RSP },
+ { "Update Req", SCSP_CIFSM_UPD_REQ },
+ { "Update Rsp", SCSP_CIFSM_UPD_RSP },
+ { "CSU Request", SCSP_CIFSM_CSU_REQ },
+ { "CSU Reply", SCSP_CIFSM_CSU_REPLY },
+ { "CSU Solicit", SCSP_CIFSM_CSU_SOL },
+ { (char *)0, 0 }
+};
+
+static Type_name atmarp_state_names[] = {
+ { "New", SCSP_ASTATE_NEW },
+ { "Updated", SCSP_ASTATE_UPD },
+ { "Deleted", SCSP_ASTATE_DEL },
+ { (char *)0, 0 }
+};
+
+
+/*
+ * Initialize the indent string
+ *
+ * Arguments:
+ * none
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+init_indent()
+{
+ indent[0] = '\0';
+}
+
+
+/*
+ * Increment the indent string
+ *
+ * Arguments:
+ * none
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+inc_indent()
+{
+ if (strlen(indent) >= MAX_INDENT)
+ return;
+ strcat(indent, " ");
+}
+
+
+/*
+ * Decrement the indent string
+ *
+ * Arguments:
+ * none
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+dec_indent()
+{
+ if (strlen(indent) < MIN_INDENT)
+ return;
+ indent[strlen(indent) - 2] = '\0';
+}
+
+
+
+/*
+ * Search for a type in a name-type table
+ *
+ * Arguments:
+ * type the value being searched for
+ * tbl pointer to the table to search
+ *
+ * Returns:
+ * pointer to a string identifying the type
+ *
+ */
+static char *
+scsp_type_name(type, tbl)
+ u_char type;
+ Type_name *tbl;
+{
+ int i;
+
+ /*
+ * Search the table
+ */
+ for (i = 0; tbl[i].name != (char *)0 && tbl[i].type != type;
+ i++)
+ ;
+
+ /*
+ * Check the result and return the appropriate value
+ */
+ if (tbl[i].name)
+ return(tbl[i].name);
+ else
+ return("-");
+}
+
+
+/*
+ * Format a Hello FSM state name
+ *
+ * Arguments:
+ * state the state
+ *
+ * Returns:
+ * pointer to a string identifying the state
+ *
+ */
+char *
+format_hfsm_state(state)
+ int state;
+{
+ return(scsp_type_name((u_char)state, hfsm_state_names));
+}
+
+
+/*
+ * Format a Hello FSM event name
+ *
+ * Arguments:
+ * event the event
+ *
+ * Returns:
+ * pointer to a string identifying the event
+ *
+ */
+char *
+format_hfsm_event(event)
+ int event;
+{
+ char *cp;
+
+ cp = scsp_type_name((u_char)event, hfsm_event_names);
+ return(cp);
+}
+
+
+/*
+ * Format a CA FSM state name
+ *
+ * Arguments:
+ * state the state
+ *
+ * Returns:
+ * pointer to a string identifying the state
+ *
+ */
+char *
+format_cafsm_state(state)
+ int state;
+{
+ return(scsp_type_name((u_char)state, cafsm_state_names));
+}
+
+
+/*
+ * Format a CA FSM event name
+ *
+ * Arguments:
+ * event the event
+ *
+ * Returns:
+ * pointer to a string identifying the event
+ *
+ */
+char *
+format_cafsm_event(event)
+ int event;
+{
+ return(scsp_type_name((u_char)event, cafsm_event_names));
+}
+
+
+/*
+ * Format a client interface FSM state name
+ *
+ * Arguments:
+ * state the state
+ *
+ * Returns:
+ * pointer to a string identifying the state
+ *
+ */
+char *
+format_cifsm_state(state)
+ int state;
+{
+ return(scsp_type_name((u_char)state, cifsm_state_names));
+}
+
+
+/*
+ * Format a client interface FSM event name
+ *
+ * Arguments:
+ * event the event
+ *
+ * Returns:
+ * pointer to a string identifying the event
+ *
+ */
+char *
+format_cifsm_event(event)
+ int event;
+{
+ return(scsp_type_name((u_char)event, cifsm_event_names));
+}
+
+
+/*
+ * Print a Sender or Receiver ID structure
+ *
+ * Arguments:
+ * fp file to print message to
+ * idp pointer to ID to be printed
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+print_scsp_id(fp, idp)
+ FILE *fp;
+ Scsp_id *idp;
+{
+ int i;
+
+ inc_indent();
+ fprintf(fp, "%sNext: %p\n", indent, idp->next);
+ fprintf(fp, "%sLength: %d\n", indent,
+ idp->id_len);
+ fprintf(fp, "%sID: 0x", indent);
+ for (i = 0; i < idp->id_len; i++)
+ fprintf(fp, "%02x ", idp->id[i]);
+ fprintf(fp, "\n");
+ dec_indent();
+}
+
+
+/*
+ * Print a Cache Key structure
+ *
+ * Arguments:
+ * fp file to print message to
+ * ckp pointer to cache key structure
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+print_scsp_cache_key(fp, ckp)
+ FILE *fp;
+ Scsp_ckey *ckp;
+{
+ int i;
+
+ inc_indent();
+ fprintf(fp, "%sLength: %d\n", indent,
+ ckp->key_len);
+ fprintf(fp, "%sKey: 0x", indent);
+ for (i = 0; i < ckp->key_len; i++)
+ fprintf(fp, "%02x ", ckp->key[i]);
+ fprintf(fp, "\n");
+ dec_indent();
+}
+
+
+/*
+ * Print the mandatory common part of a message
+ *
+ * Arguments:
+ * fp file to print message to
+ * mcp pointer to mandatory common part structure
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+print_scsp_mcp(fp, mcp)
+ FILE *fp;
+ Scsp_mcp *mcp;
+{
+ inc_indent();
+ fprintf(fp, "%sProtocol ID: %s (0x%02x)\n", indent,
+ scsp_type_name(mcp->pid, proto_types),
+ mcp->pid);
+ fprintf(fp, "%sServer Group ID: %d\n", indent, mcp->sgid);
+ fprintf(fp, "%sFlags: 0x%04x\n", indent,
+ mcp->flags);
+ fprintf(fp, "%sRecord Count: %d\n", indent,
+ mcp->rec_cnt);
+ fprintf(fp, "%sSender ID:\n", indent);
+ print_scsp_id(fp, &mcp->sid);
+ fprintf(fp, "%sReceiver ID:\n", indent);
+ print_scsp_id(fp, &mcp->rid);
+ dec_indent();
+}
+
+
+/*
+ * Print an extension
+ *
+ * Arguments:
+ * fp file to print message to
+ * exp pointer to extension
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+print_scsp_ext(fp, exp)
+ FILE *fp;
+ Scsp_ext *exp;
+{
+ int i;
+ u_char *cp;
+
+ inc_indent();
+ fprintf(fp, "%sNext: %p\n", indent, exp->next);
+ fprintf(fp, "%sType: %s (0x%02x)\n", indent,
+ scsp_type_name(exp->type, ext_types),
+ exp->type);
+ fprintf(fp, "%sLength: %d\n", indent, exp->len);
+ if (exp->len) {
+ fprintf(fp, "%sValue: 0x", indent);
+ cp = (u_char *)((caddr_t)exp + sizeof(Scsp_ext));
+ for (i = 0; i < exp->len; i++)
+ fprintf(fp, "%02x ", *cp++);
+ fprintf(fp, "\n");
+ }
+ dec_indent();
+}
+
+
+/*
+ * Print an ATMARP Cache State Advertisement record
+ *
+ * Arguments:
+ * fp file to print message to
+ * acsp pointer to extension
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+print_scsp_atmarp_csa(fp, acsp)
+ FILE *fp;
+ Scsp_atmarp_csa *acsp;
+{
+ inc_indent();
+ fprintf(fp, "%sState: %s (%d)\n", indent,
+ scsp_type_name(acsp->sa_state,
+ atmarp_state_names),
+ acsp->sa_state);
+ fprintf(fp, "%sSource ATM addr: %s\n", indent,
+ format_atm_addr(&acsp->sa_sha));
+ fprintf(fp, "%sSource ATM subaddr: %s\n", indent,
+ format_atm_addr(&acsp->sa_ssa));
+ fprintf(fp, "%sSource IP addr: %s\n", indent,
+ format_ip_addr(&acsp->sa_spa));
+ fprintf(fp, "%sTarget ATM addr: %s\n", indent,
+ format_atm_addr(&acsp->sa_tha));
+ fprintf(fp, "%sTarget ATM subaddr: %s\n", indent,
+ format_atm_addr(&acsp->sa_tsa));
+ fprintf(fp, "%sTarget IP addr: %s\n", indent,
+ format_ip_addr(&acsp->sa_tpa));
+ dec_indent();
+}
+
+
+/*
+ * Print a Cache State Advertisement record or
+ * Cache State Advertisement Summary record
+ *
+ * Arguments:
+ * fp file to print message to
+ * csap pointer to CSA or CSAS
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+print_scsp_csa(fp, csap)
+ FILE *fp;
+ Scsp_csa *csap;
+{
+ inc_indent();
+ fprintf(fp, "%sNext: %p\n", indent, csap->next);
+ fprintf(fp, "%sHops: %d\n", indent, csap->hops);
+ fprintf(fp, "%sNull Flag: %s\n", indent,
+ csap->null ? "True" : "False");
+ fprintf(fp, "%sSequence no.: %ld (0x%lx)\n",
+ indent, csap->seq, csap->seq);
+ fprintf(fp, "%sCache Key:\n", indent);
+ print_scsp_cache_key(fp, &csap->key);
+ fprintf(fp, "%sOriginator ID:\n", indent);
+ print_scsp_id(fp, &csap->oid);
+ if (csap->atmarp_data) {
+ fprintf(fp, "%sATMARP data:\n", indent);
+ print_scsp_atmarp_csa(fp, csap->atmarp_data);
+ }
+ dec_indent();
+}
+
+
+/*
+ * Print a Cache Alignment message
+ *
+ * Arguments:
+ * fp file to print message to
+ * cap pointer to extension
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+print_scsp_ca(fp, cap)
+ FILE *fp;
+ Scsp_ca *cap;
+{
+ int n;
+ Scsp_csa *csap;
+
+ inc_indent();
+ fprintf(fp, "%sCA Seq. No.: %ld\n", indent,
+ cap->ca_seq);
+ fprintf(fp, "%sM bit: %s\n", indent,
+ cap->ca_m ? "True" : "False");
+ fprintf(fp, "%sI bit: %s\n", indent,
+ cap->ca_i ? "True" : "False");
+ fprintf(fp, "%sO bit: %s\n", indent,
+ cap->ca_o ? "True" : "False");
+ fprintf(fp, "%sMandatory Common Part:\n", indent);
+ print_scsp_mcp(fp, &cap->ca_mcp);
+ for (csap = cap->ca_csa_rec, n = 1; csap;
+ csap = csap->next, n++) {
+ fprintf(fp, "%sCSA Record %d (%p):\n", indent, n, csap);
+ print_scsp_csa(fp, csap);
+ }
+ dec_indent();
+}
+
+
+/*
+ * Print a Cache State Update Request, Cache State Update Reply, or
+ * Cache State Update Solicit message
+ *
+ * Arguments:
+ * fp file to print message to
+ * csup pointer to CSU message
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+print_scsp_csu(fp, csup)
+ FILE *fp;
+ Scsp_csu_msg *csup;
+{
+ int i;
+ Scsp_csa *csap;
+
+ inc_indent();
+ fprintf(fp, "%sMandatory Common Part:\n", indent);
+ print_scsp_mcp(fp, &csup->csu_mcp);
+ for (csap = csup->csu_csa_rec, i = 1; csap;
+ csap = csap->next, i++) {
+ fprintf(fp, "%sCSA Record %d:\n", indent, i);
+ print_scsp_csa(fp, csap);
+ }
+ dec_indent();
+}
+
+
+/*
+ * Print a Hello message
+ *
+ * Arguments:
+ * fp file to print message to
+ * hp pointer to hello message
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+print_scsp_hello(fp, hp)
+ FILE *fp;
+ Scsp_hello *hp;
+{
+ Scsp_id *ridp;
+
+ inc_indent();
+ fprintf(fp, "%sHello Interval: %d\n", indent,
+ hp->hello_int);
+ fprintf(fp, "%sDead Factor: %d\n", indent,
+ hp->dead_factor);
+ fprintf(fp, "%sFamily ID: %d\n", indent,
+ hp->family_id);
+ fprintf(fp, "%sMandatory Common Part:\n", indent);
+ print_scsp_mcp(fp, &hp->hello_mcp);
+ ridp = hp->hello_mcp.rid.next;
+ if (ridp) {
+ fprintf(fp, "%sAdditional Receiver IDs:\n", indent);
+ for (; ridp; ridp = ridp->next)
+ print_scsp_id(fp, ridp);
+ }
+ dec_indent();
+}
+
+
+#ifdef NOTDEF
+/*
+ * NHRP-specific Cache State Advertisement record
+ */
+struct scsp_nhrp_csa {
+ u_char req_id; /* Request ID */
+ u_char state; /* State */
+ u_char pref_len; /* Prefix length */
+ u_short flags; /* See below */
+ u_short mtu; /* Maximim transmission unit */
+ u_short hold_time; /* Entry holding time */
+ u_char caddr_tlen; /* Client addr type/length */
+ u_char csaddr_tlen; /* Client subaddr type/length */
+ u_char cproto_len; /* Client proto addr length */
+ u_char pref; /* Preference */
+ Atm_addr caddr; /* Client address */
+ Atm_addr csaddr; /* Client subaddress */
+ struct in_addr cproto_addr; /* Client protocol address */
+};
+typedef struct scsp_nhrp Scsp_nhrp;
+
+#define SCSP_NHRP_UNIQ 0x8000
+#define SCSP_NHRP_ARP 0x4000
+
+#endif
+
+
+/*
+ * Print an SCSP message
+ *
+ * Arguments:
+ * fp file to print message to
+ * msg pointer to message to be printed
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+print_scsp_msg(fp, msg)
+ FILE *fp;
+ Scsp_msg *msg;
+{
+ int n;
+ Scsp_ext *exp;
+
+ /*
+ * Initialize
+ */
+ init_indent();
+
+ /*
+ * Print the message type
+ */
+ inc_indent();
+ fprintf(fp, "%sMessage type: %s (0x%02x)\n", indent,
+ scsp_type_name(msg->sc_msg_type, msg_types),
+ msg->sc_msg_type);
+
+ /*
+ * Print the body of the message
+ */
+ switch(msg->sc_msg_type) {
+ case SCSP_CA_MSG:
+ print_scsp_ca(fp, msg->sc_ca);
+ break;
+ case SCSP_CSU_REQ_MSG:
+ case SCSP_CSU_REPLY_MSG:
+ case SCSP_CSUS_MSG:
+ print_scsp_csu(fp, msg->sc_csu_msg);
+ break;
+ case SCSP_HELLO_MSG:
+ print_scsp_hello(fp, msg->sc_hello);
+ break;
+ }
+
+ /*
+ * Print any extensions
+ */
+ for (exp = msg->sc_ext, n = 1; exp; exp = exp->next, n++) {
+ fprintf(fp, "%sExtension %d:\n", indent, n);
+ print_scsp_ext(fp, exp);
+ }
+ dec_indent();
+
+ (void)fflush(fp);
+}
+
+
+/*
+ * Print an SCSP ATMARP message
+ *
+ * Arguments:
+ * fp file to print message to
+ * acp pointer to ATMARP message
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+print_scsp_if_atmarp(fp, amp)
+ FILE *fp;
+ Scsp_atmarp_msg *amp;
+{
+ inc_indent();
+ fprintf(fp, "%sState: %s (%d)\n", indent,
+ scsp_type_name(amp->sa_state,
+ atmarp_state_names),
+ amp->sa_state);
+ fprintf(fp, "%sCached protocol addr: %s\n", indent,
+ format_ip_addr(&amp->sa_cpa));
+ fprintf(fp, "%sCached ATM addr: %s\n", indent,
+ format_atm_addr(&amp->sa_cha));
+ fprintf(fp, "%sCached ATM subaddr: %s\n", indent,
+ format_atm_addr(&amp->sa_csa));
+ fprintf(fp, "%sCache key:\n", indent);
+ print_scsp_cache_key(fp, &amp->sa_key);
+ fprintf(fp, "%sOriginator ID:\n", indent);
+ print_scsp_id(fp, &amp->sa_oid);
+ fprintf(fp, "%sSequence number: %ld (0x%08lx)\n", indent,
+ amp->sa_seq, (u_long)amp->sa_seq);
+ dec_indent();
+}
+
+
+/*
+ * Print an SCSP client interface message
+ *
+ * Arguments:
+ * fp file to print message to
+ * imsg pointer to message to be printed
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+print_scsp_if_msg(fp, imsg)
+ FILE *fp;
+ Scsp_if_msg *imsg;
+{
+ int len;
+ Scsp_atmarp_msg *ap;
+
+ /*
+ * Initialize
+ */
+ init_indent();
+ fprintf(fp, "SCSP Client Interface Message at %p\n", imsg);
+
+ /*
+ * Print the message header
+ */
+ inc_indent();
+ fprintf(fp, "%sMessage type: %s (0x%02x)\n", indent,
+ scsp_type_name(imsg->si_type, if_msg_types),
+ imsg->si_type);
+ fprintf(fp, "%sResponse code: %d\n", indent,
+ imsg->si_rc);
+ fprintf(fp, "%sProtocol type: %s (%d)\n", indent,
+ scsp_type_name(imsg->si_proto, proto_types),
+ imsg->si_proto);
+ fprintf(fp, "%sLength: %d\n", indent,
+ imsg->si_len);
+ fprintf(fp, "%sToken: 0x%lx\n", indent,
+ imsg->si_tok);
+
+ /*
+ * Print the body of the message
+ */
+ switch(imsg->si_type) {
+ case SCSP_CFG_REQ:
+ fprintf(fp, "%sInterface: %s\n", indent,
+ imsg->si_cfg.atmarp_netif);
+ break;
+ case SCSP_CACHE_RSP:
+ case SCSP_UPDATE_IND:
+ case SCSP_UPDATE_REQ:
+ len = imsg->si_len - sizeof(Scsp_if_msg_hdr);
+ ap = &imsg->si_atmarp;
+ while (len) {
+ switch(imsg->si_proto) {
+ case SCSP_PROTO_ATMARP:
+ fprintf(fp, "%sATMARP CSA:\n", indent);
+ print_scsp_if_atmarp(fp, ap);
+ len -= sizeof(Scsp_atmarp_msg);
+ ap++;
+ break;
+ case SCSP_PROTO_NHRP:
+ case SCSP_PROTO_MARS:
+ case SCSP_PROTO_DHCP:
+ case SCSP_PROTO_LNNI:
+ default:
+ fprintf(fp, "Protocol type not implemented\n");
+ break;
+ }
+ }
+ break;
+ }
+ dec_indent();
+
+ (void)fflush(fp);
+}
+
+
+/*
+ * Print an SCSP pending connection block
+ *
+ * Arguments:
+ * fp file to print message to
+ * pp pointer to pending control block
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+print_scsp_pending(fp, pp)
+ FILE *fp;
+ Scsp_pending *pp;
+{
+ /*
+ * Initialize
+ */
+ init_indent();
+
+ /*
+ * Print a header
+ */
+ fprintf(fp, "Pending control block at %p\n", pp);
+
+ /*
+ * Print the fields of the control block
+ */
+ inc_indent();
+ fprintf(fp, "%sNext: %p\n", indent, pp->sp_next);
+ fprintf(fp, "%sSocket: %d\n", indent,
+ pp->sp_sock);
+
+ dec_indent();
+}
+
+
+/*
+ * Print an SCSP server control block
+ *
+ * Arguments:
+ * fp file to print message to
+ * ssp pointer to server control block
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+print_scsp_server(fp, ssp)
+ FILE *fp;
+ Scsp_server *ssp;
+{
+ /*
+ * Initialize
+ */
+ init_indent();
+
+ /*
+ * Print a header
+ */
+ fprintf(fp, "Server control block at %p\n", ssp);
+
+ /*
+ * Print the fields of the client control block
+ */
+ inc_indent();
+ fprintf(fp, "%sNext: %p\n", indent,
+ ssp->ss_next);
+ fprintf(fp, "%sName: %s\n", indent,
+ ssp->ss_name);
+ fprintf(fp, "%sNetwork Interface: %s\n", indent,
+ ssp->ss_intf);
+ fprintf(fp, "%sState: %d\n", indent,
+ ssp->ss_state);
+ fprintf(fp, "%sProtocol ID: 0x%lx\n", indent,
+ ssp->ss_pid);
+ fprintf(fp, "%sID length: %d\n", indent,
+ ssp->ss_id_len);
+ fprintf(fp, "%sCache key length: %d\n", indent,
+ ssp->ss_ckey_len);
+ fprintf(fp, "%sServer Group ID: 0x%lx\n", indent,
+ ssp->ss_sgid);
+ fprintf(fp, "%sFamily ID: 0x%lx\n", indent,
+ ssp->ss_fid);
+ fprintf(fp, "%sSocket: %d\n", indent,
+ ssp->ss_sock);
+ fprintf(fp, "%sDCS Listen Socket: %d\n", indent,
+ ssp->ss_dcs_lsock);
+ fprintf(fp, "%sLocal Server ID:\n", indent);
+ print_scsp_id(fp, &ssp->ss_lsid);
+ fprintf(fp, "%sATM address: %s\n", indent,
+ format_atm_addr(&ssp->ss_addr));
+ fprintf(fp, "%sATM subaddress: %s\n", indent,
+ format_atm_addr(&ssp->ss_subaddr));
+ fprintf(fp, "%sInterface MTU: %d\n", indent,
+ ssp->ss_mtu);
+ fprintf(fp, "%sMark: %d\n", indent,
+ ssp->ss_mark);
+ dec_indent();
+}
+
+
+/*
+ * Print an SCSP client cache summary entry control block
+ *
+ * Arguments:
+ * fp file to print message to
+ * csep pointer to summary entry
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+print_scsp_cse(fp, csep)
+ FILE *fp;
+ Scsp_cse *csep;
+{
+ /*
+ * Print the fields of the cache summary entry
+ */
+ inc_indent();
+ fprintf(fp, "%sNext CSE: %p\n", indent, csep->sc_next);
+ fprintf(fp, "%sCSA sequence no.: %ld (0x%lx)\n", indent,
+ csep->sc_seq, csep->sc_seq);
+ fprintf(fp, "%sCache key:\n", indent);
+ print_scsp_cache_key(fp, &csep->sc_key);
+ fprintf(fp, "%sOrigin ID:\n", indent);
+ print_scsp_id(fp, &csep->sc_oid);
+ dec_indent();
+}
+
+
+/*
+ * Print an SCSP CSU Request retransmission control block
+ *
+ * Arguments:
+ * fp file to print message to
+ * csurp pointer to retransmission entry
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+print_scsp_csu_rexmt(fp, rxp)
+ FILE *fp;
+ Scsp_csu_rexmt *rxp;
+{
+ int i;
+ Scsp_csa *csap;
+
+ inc_indent();
+ fprintf(fp, "%sNext CSU Req rexmt: %p\n", indent, rxp->sr_next);
+ fprintf(fp, "%sDCS address: %p\n", indent, rxp->sr_dcs);
+ for (csap = rxp->sr_csa, i = 1; csap;
+ csap = csap->next, i++) {
+ fprintf(fp, "%sCSA %d:\n", indent, i);
+ print_scsp_csa(fp, csap);
+ }
+ dec_indent();
+}
+
+
+/*
+ * Print an SCSP DCS control block
+ *
+ * Arguments:
+ * fp file to print message to
+ * dcsp pointer to DCS control block
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+print_scsp_dcs(fp, dcsp)
+ FILE *fp;
+ Scsp_dcs *dcsp;
+{
+ Scsp_csa *csap;
+ Scsp_cse *csep;
+ Scsp_csu_rexmt *rxp;
+
+ /*
+ * Initialize
+ */
+ init_indent();
+
+ /*
+ * Print a header
+ */
+ fprintf(fp, "DCS control block at %p\n", dcsp);
+
+ /*
+ * Print the fields of the DCS control block
+ */
+ inc_indent();
+ fprintf(fp, "%sNext DCS block: %p\n", indent, dcsp->sd_next);
+ fprintf(fp, "%sServer control block: %p\n", indent, dcsp->sd_server);
+ fprintf(fp, "%sDCS ID:\n", indent);
+ print_scsp_id(fp, &dcsp->sd_dcsid);
+ fprintf(fp, "%sDCS address: %s\n", indent,
+ format_atm_addr(&dcsp->sd_addr));
+ fprintf(fp, "%sDCS subaddress %s\n", indent,
+ format_atm_addr(&dcsp->sd_subaddr));
+ fprintf(fp, "%sSocket: %d\n", indent,
+ dcsp->sd_sock);
+ fprintf(fp, "%sOpen VCC Retry Timer:\n", indent);
+ fprintf(fp, "%sHello FSM State: %s\n", indent,
+ format_hfsm_state(dcsp->sd_hello_state));
+ fprintf(fp, "%sHello Interval: %d\n", indent,
+ dcsp->sd_hello_int);
+ fprintf(fp, "%sHello Dead Factor: %d\n", indent,
+ dcsp->sd_hello_df);
+ fprintf(fp, "%sHello Rcvd: %d\n", indent,
+ dcsp->sd_hello_rcvd);
+ fprintf(fp, "%sCA FSM State: %s\n", indent,
+ format_cafsm_state(dcsp->sd_ca_state));
+ fprintf(fp, "%sCA Seq. No.: 0x%lx\n", indent,
+ dcsp->sd_ca_seq);
+ fprintf(fp, "%sCA Rexmit Int: %d\n", indent,
+ dcsp->sd_ca_rexmt_int);
+ fprintf(fp, "%sCA Retransmit Msg: %p\n", indent,
+ dcsp->sd_ca_rexmt_msg);
+ fprintf(fp, "%sCSASs to send: ", indent);
+ if (dcsp->sd_ca_csas == (Scsp_cse *)0) {
+ fprintf(fp, "Empty\n");
+ } else {
+ fprintf(fp, "%p\n", dcsp->sd_ca_csas);
+ }
+ fprintf(fp, "%sCSUS Rexmit Int: %d\n", indent,
+ dcsp->sd_csus_rexmt_int);
+ fprintf(fp, "%sCache Request List: ", indent);
+ if (dcsp->sd_crl == (Scsp_csa *)0) {
+ fprintf(fp, "Empty\n");
+ } else {
+ fprintf(fp, "%p\n", dcsp->sd_crl);
+ }
+ fprintf(fp, "%sCSUS Rexmit Msg: %p\n", indent,
+ dcsp->sd_csus_rexmt_msg);
+ fprintf(fp, "%sCSA Hop count: %d\n", indent,
+ dcsp->sd_hops);
+ fprintf(fp, "%sCSAs Pending ACK: %p\n", indent,
+ dcsp->sd_csu_ack_pend);
+ fprintf(fp, "%sCSAs ACKed: %p\n", indent,
+ dcsp->sd_csu_ack);
+ fprintf(fp, "%sCSU Req Rexmit Int: %d\n", indent,
+ dcsp->sd_csu_rexmt_int);
+ fprintf(fp, "%sCSU Req Rexmit Max: %d\n", indent,
+ dcsp->sd_csu_rexmt_max);
+ fprintf(fp, "%sCSU Req Rexmit Queue ", indent);
+ if (!dcsp->sd_csu_rexmt) {
+ fprintf(fp, "Empty\n");
+ } else {
+ fprintf(fp, "%p\n", dcsp->sd_csu_rexmt);
+ }
+ fprintf(fp, "%sClient I/F state: %d\n", indent,
+ dcsp->sd_client_state);
+
+ /*
+ * Print the list of CSASs waiting to be sent
+ */
+ if (dcsp->sd_ca_csas) {
+ fprintf(fp, "\n%sCSASs to send:", indent);
+ inc_indent();
+ for (csep = dcsp->sd_ca_csas; csep;
+ csep = csep->sc_next) {
+ fprintf(fp, "%sCache summary entry at %p\n",
+ indent, csep);
+ print_scsp_cse(fp, csep);
+ }
+ dec_indent();
+ }
+
+ /*
+ * Print the Cache Request List
+ */
+ if (dcsp->sd_crl) {
+ fprintf(fp, "\n%sCache Request List:\n", indent);
+ inc_indent();
+ for (csap = dcsp->sd_crl; csap; csap = csap->next) {
+ fprintf(fp, "%sCSA at %p\n", indent, csap);
+ print_scsp_csa(fp, csap);
+ }
+ dec_indent();
+ }
+
+ /*
+ * Print the CSU retransmit queue
+ */
+ if (dcsp->sd_csu_rexmt) {
+ fprintf(fp, "\n%sCSU Req Rexmit Queue:\n", indent);
+ inc_indent();
+ for (rxp = dcsp->sd_csu_rexmt; rxp;
+ rxp = rxp->sr_next) {
+ fprintf(fp, "%sCSU Rexmit Block at %p\n",
+ indent, rxp);
+ print_scsp_csu_rexmt(fp, rxp);
+ }
+ dec_indent();
+ }
+
+ dec_indent();
+}
+
+
+/*
+ * Print SCSP's control blocks
+ *
+ * Arguments:
+ * none
+ *
+ * Returns:
+ * None
+ *
+ */
+void
+print_scsp_dump()
+{
+ int i;
+ Scsp_server *ssp;
+ Scsp_dcs *dcsp;
+ Scsp_cse *scp;
+ Scsp_pending *pp;
+ FILE *df;
+ char fname[64];
+ static int dump_no = 0;
+
+ /*
+ * Build a file name
+ */
+ bzero(fname, sizeof(fname));
+ sprintf(fname, "/tmp/scspd.%d.%03d.out", getpid(), dump_no++);
+
+ /*
+ * Open the output file
+ */
+ df = fopen(fname, "w");
+ if (df == (FILE *)0)
+ return;
+
+ /*
+ * Dump the server control blocks
+ */
+ for (ssp = scsp_server_head; ssp; ssp = ssp->ss_next) {
+ print_scsp_server(df, ssp);
+ fprintf(df, "\n");
+
+ /*
+ * Print the client's cache summary
+ */
+ for (i = 0; i < SCSP_HASHSZ; i++) {
+ for (scp = ssp->ss_cache[i]; scp;
+ scp = scp->sc_next) {
+ print_scsp_cse(df, scp);
+ fprintf(df, "\n");
+ }
+ }
+
+ /*
+ * Print the client's DCS control blocks
+ */
+ for (dcsp = ssp->ss_dcs; dcsp; dcsp = dcsp->sd_next) {
+ print_scsp_dcs(df, dcsp);
+ fprintf(df, "\n\n");
+ }
+ fprintf(df, "\n\n");
+ }
+
+ /*
+ * Print the pending connection blocks
+ */
+ for (pp = scsp_pending_head; pp; pp = pp->sp_next) {
+ print_scsp_pending(df, pp);
+ fprintf(df, "\n");
+ }
+
+ /*
+ * Close the output file
+ */
+ (void)fclose(df);
+}
diff --git a/usr.sbin/atm/scspd/scsp_socket.c b/usr.sbin/atm/scspd/scsp_socket.c
new file mode 100644
index 0000000..3cfa294
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_socket.c
@@ -0,0 +1,1344 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * SCSP socket management routines
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netatm/port.h>
+#include <netatm/queue.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <libatm.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "scsp_msg.h"
+#include "scsp_if.h"
+#include "scsp_var.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+/*
+ * Local variables
+ */
+static struct t_atm_llc llc_scsp = {
+ T_ATM_LLC_SHARING,
+ 8,
+ {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x5e, 0x00, 0x05}
+};
+
+static struct t_atm_aal5 aal5 = {
+ 0, /* forward_max_SDU_size */
+ 0, /* backward_max_SDU_size */
+ 0 /* SSCS_type */
+};
+
+static struct t_atm_traffic traffic = {
+ { /* forward */
+ T_ATM_ABSENT, /* PCR_high_priority */
+ 0, /* PCR_all_traffic */
+ T_ATM_ABSENT, /* SCR_high_priority */
+ T_ATM_ABSENT, /* SCR_all_traffic */
+ T_ATM_ABSENT, /* MBS_high_priority */
+ T_ATM_ABSENT, /* MBS_all_traffic */
+ T_NO /* tagging */
+ },
+ { /* backward */
+ T_ATM_ABSENT, /* PCR_high_priority */
+ 0, /* PCR_all_traffic */
+ T_ATM_ABSENT, /* SCR_high_priority */
+ T_ATM_ABSENT, /* SCR_all_traffic */
+ T_ATM_ABSENT, /* MBS_high_priority */
+ T_ATM_ABSENT, /* MBS_all_traffic */
+ T_NO /* tagging */
+ },
+ T_YES /* best_effort */
+};
+
+static struct t_atm_bearer bearer = {
+ T_ATM_CLASS_X, /* bearer_class */
+ T_ATM_NULL, /* traffic_type */
+ T_ATM_NULL, /* timing_requirements */
+ T_NO, /* clipping_susceptibility */
+ T_ATM_1_TO_1 /* connection_configuration */
+};
+
+static struct t_atm_qos qos = {
+ T_ATM_NETWORK_CODING, /* coding_standard */
+ { /* forward */
+ T_ATM_QOS_CLASS_0 /* qos_class */
+ },
+ { /* backward */
+ T_ATM_QOS_CLASS_0 /* qos_class */
+ }
+};
+
+static struct t_atm_app_name appname = {
+ "SCSP"
+};
+
+
+/*
+ * Find a DCS, given its socket
+ *
+ * Arguments:
+ * sd socket descriptor
+ *
+ * Returns:
+ * 0 not found
+ * address of DCS block corresponding to socket
+ *
+ */
+Scsp_dcs *
+scsp_find_dcs(sd)
+ int sd;
+{
+ Scsp_server *ssp;
+ Scsp_dcs *dcsp = NULL;
+
+ /*
+ * Loop through the list of servers
+ */
+ for (ssp = scsp_server_head; ssp; ssp = ssp->ss_next) {
+ /*
+ * Check all the DCSs chained from each server
+ */
+ for (dcsp = ssp->ss_dcs; dcsp; dcsp = dcsp->sd_next) {
+ if (dcsp->sd_sock == sd)
+ break;
+ }
+ }
+
+ return(dcsp);
+}
+
+
+/*
+ * Find a server, given its socket
+ *
+ * Arguments:
+ * sd socket descriptor
+ *
+ * Returns:
+ * 0 not found
+ * address of server block corresponding to socket
+ *
+ */
+Scsp_server *
+scsp_find_server(sd)
+ int sd;
+{
+ Scsp_server *ssp;
+
+ /*
+ * Loop through the list of servers
+ */
+ for (ssp = scsp_server_head; ssp; ssp = ssp->ss_next) {
+ if (ssp->ss_sock == sd)
+ break;
+ }
+
+ return(ssp);
+}
+
+
+/*
+ * Connect to a directly connected server
+ *
+ * Arguments:
+ * dcsp pointer to DCS block for server
+ *
+ * Returns:
+ * 0 success (dcsp->sd_sock is set)
+ * else errno indicating reason for failure
+ *
+ */
+int
+scsp_dcs_connect(dcsp)
+ Scsp_dcs *dcsp;
+
+{
+ int rc, sd;
+ struct sockaddr_atm DCS_addr;
+
+ /*
+ * If the DCS already has an open connection, just return
+ */
+ if (dcsp->sd_sock != -1) {
+ return(0);
+ }
+
+ /*
+ * Open an ATM socket
+ */
+ sd = socket(PF_ATM, SOCK_SEQPACKET, ATM_PROTO_AAL5);
+ if (sd == -1) {
+ return(ESOCKTNOSUPPORT);
+ }
+ if (sd > scsp_max_socket) {
+ scsp_max_socket = sd;
+ }
+
+ /*
+ * Set up connection parameters for SCSP connection
+ */
+ bzero(&DCS_addr, sizeof(DCS_addr));
+#if (defined(BSD) && (BSD >= 199103))
+ DCS_addr.satm_len = sizeof(DCS_addr);
+#endif
+ DCS_addr.satm_family = AF_ATM;
+ DCS_addr.satm_addr.t_atm_sap_addr.SVE_tag_addr =
+ T_ATM_PRESENT;
+ DCS_addr.satm_addr.t_atm_sap_addr.SVE_tag_selector =
+ T_ATM_PRESENT;
+ DCS_addr.satm_addr.t_atm_sap_addr.address_format =
+ dcsp->sd_addr.address_format;
+ DCS_addr.satm_addr.t_atm_sap_addr.address_length =
+ dcsp->sd_addr.address_length;
+ bcopy(dcsp->sd_addr.address,
+ DCS_addr.satm_addr.t_atm_sap_addr.address,
+ dcsp->sd_addr.address_length);
+
+ DCS_addr.satm_addr.t_atm_sap_layer2.SVE_tag =
+ T_ATM_PRESENT;
+ DCS_addr.satm_addr.t_atm_sap_layer2.ID_type =
+ T_ATM_SIMPLE_ID;
+ DCS_addr.satm_addr.t_atm_sap_layer2.ID.simple_ID =
+ T_ATM_BLLI2_I8802;
+
+ DCS_addr.satm_addr.t_atm_sap_layer3.SVE_tag =
+ T_ATM_ABSENT;
+ DCS_addr.satm_addr.t_atm_sap_appl.SVE_tag =
+ T_ATM_ABSENT;
+
+ /*
+ * Bind the socket to our address
+ */
+ if (bind(sd, (struct sockaddr *)&DCS_addr,
+ sizeof(DCS_addr))) {
+ rc = errno;
+ goto connect_fail;
+ }
+
+ /*
+ * Set non-blocking operation
+ */
+#ifdef sun
+ rc = fcntl(sd, F_SETFL, FNBIO + FNDELAY);
+#else
+ rc = fcntl(sd, F_SETFL, O_NONBLOCK);
+#endif
+ if (rc == -1) {
+ scsp_log(LOG_ERR, "scsp_dcs_connect: fcntl failed");
+ rc = errno;
+ goto connect_fail;
+ }
+
+ /*
+ * Set AAL 5 options
+ */
+ aal5.forward_max_SDU_size = dcsp->sd_server->ss_mtu;
+ aal5.backward_max_SDU_size = dcsp->sd_server->ss_mtu;
+ if (setsockopt(sd, T_ATM_SIGNALING, T_ATM_AAL5, (caddr_t)&aal5,
+ sizeof(aal5)) < 0) {
+ rc = EOPNOTSUPP;
+ goto connect_fail;
+ }
+
+ /*
+ * Set traffic options
+ */
+ switch(dcsp->sd_server->ss_media) {
+ case MEDIA_TAXI_100:
+ traffic.forward.PCR_all_traffic = ATM_PCR_TAXI100;
+ traffic.backward.PCR_all_traffic = ATM_PCR_TAXI100;
+ break;
+ case MEDIA_TAXI_140:
+ traffic.forward.PCR_all_traffic = ATM_PCR_TAXI140;
+ traffic.backward.PCR_all_traffic = ATM_PCR_TAXI140;
+ break;
+ case MEDIA_OC3C:
+ case MEDIA_UTP155:
+ traffic.forward.PCR_all_traffic = ATM_PCR_OC3C;
+ traffic.backward.PCR_all_traffic = ATM_PCR_OC3C;
+ break;
+ case MEDIA_OC12C:
+ traffic.forward.PCR_all_traffic = ATM_PCR_OC12C;
+ traffic.backward.PCR_all_traffic = ATM_PCR_OC12C;
+ break;
+ case MEDIA_UNKNOWN:
+ break;
+ }
+
+ if (setsockopt(sd, T_ATM_SIGNALING, T_ATM_TRAFFIC,
+ (caddr_t)&traffic, sizeof(traffic)) < 0) {
+ rc = EOPNOTSUPP;
+ goto connect_fail;
+ }
+
+ /*
+ * Set bearer capability options
+ */
+ if (setsockopt(sd, T_ATM_SIGNALING, T_ATM_BEARER_CAP,
+ (caddr_t)&bearer, sizeof(bearer)) < 0) {
+ rc = EOPNOTSUPP;
+ goto connect_fail;
+ }
+
+ /*
+ * Set QOS options
+ */
+ if (setsockopt(sd, T_ATM_SIGNALING, T_ATM_QOS,
+ (caddr_t)&qos, sizeof(qos)) < 0) {
+ rc = EOPNOTSUPP;
+ goto connect_fail;
+ }
+
+ /*
+ * Set LLC identifier
+ */
+ if (setsockopt(sd, T_ATM_SIGNALING, T_ATM_LLC,
+ (caddr_t)&llc_scsp, sizeof(llc_scsp)) < 0) {
+ rc = EOPNOTSUPP;
+ goto connect_fail;
+ }
+
+ /*
+ * Set application name
+ */
+ if (setsockopt(sd, T_ATM_SIGNALING, T_ATM_APP_NAME,
+ (caddr_t)&appname, sizeof(appname)) < 0) {
+ rc = EOPNOTSUPP;
+ goto connect_fail;
+ }
+
+ /*
+ * Connect to DCS
+ */
+ if (connect(sd, (struct sockaddr *)&DCS_addr,
+ sizeof(DCS_addr)) < 0 &&
+ errno != EINPROGRESS) {
+ rc = errno;
+ goto connect_fail;
+ }
+
+ /*
+ * Set return values
+ */
+ dcsp->sd_sock = sd;
+ return(0);
+
+connect_fail:
+ /*
+ * Close the socket if something didn't work
+ */
+ (void)close(sd);
+ dcsp->sd_sock = -1;
+ if (rc == 0)
+ rc = EFAULT;
+ return(rc);
+}
+
+
+/*
+ * Listen for ATM connections from DCSs
+ *
+ * Arguments:
+ * None
+ *
+ * Returns:
+ * sock socket which is listening (also set in
+ ssp->ss_dcs_lsock)
+ * -1 error encountered (reason in errno)
+ *
+ */
+int
+scsp_dcs_listen(ssp)
+ Scsp_server *ssp;
+{
+ int rc, sd;
+ struct sockaddr_atm ls_addr;
+
+ /*
+ * Open a socket
+ */
+ sd = socket(PF_ATM, SOCK_SEQPACKET, ATM_PROTO_AAL5);
+ if (sd == -1) {
+ rc = errno;
+ goto listen_fail;
+ }
+ if (sd > scsp_max_socket) {
+ scsp_max_socket = sd;
+ }
+
+ /*
+ * Set up our address
+ */
+ bzero(&ls_addr, sizeof(ls_addr));
+#if (defined(BSD) && (BSD >= 199103))
+ ls_addr.satm_len = sizeof(ls_addr);
+#endif
+ ls_addr.satm_family = AF_ATM;
+ ls_addr.satm_addr.t_atm_sap_addr.SVE_tag_addr = T_ATM_PRESENT;
+ ls_addr.satm_addr.t_atm_sap_addr.SVE_tag_selector =
+ T_ATM_PRESENT;
+ ls_addr.satm_addr.t_atm_sap_addr.address_format =
+ ssp->ss_addr.address_format;
+ ls_addr.satm_addr.t_atm_sap_addr.address_length =
+ ssp->ss_addr.address_length;
+ bcopy(ssp->ss_addr.address,
+ ls_addr.satm_addr.t_atm_sap_addr.address,
+ ssp->ss_addr.address_length);
+
+ ls_addr.satm_addr.t_atm_sap_layer2.SVE_tag = T_ATM_PRESENT;
+ ls_addr.satm_addr.t_atm_sap_layer2.ID_type = T_ATM_SIMPLE_ID;
+ ls_addr.satm_addr.t_atm_sap_layer2.ID.simple_ID =
+ T_ATM_BLLI2_I8802;
+
+ ls_addr.satm_addr.t_atm_sap_layer3.SVE_tag = T_ATM_ABSENT;
+ ls_addr.satm_addr.t_atm_sap_appl.SVE_tag = T_ATM_ABSENT;
+
+ /*
+ * Bind the socket to our address
+ */
+ rc = bind(sd, (struct sockaddr *)&ls_addr, sizeof(ls_addr));
+ if (rc == -1) {
+ rc = errno;
+ goto listen_fail;
+ }
+
+ /*
+ * Set non-blocking I/O
+ */
+#ifdef sun
+ rc = fcntl(sd, F_SETFL, FNBIO + FNDELAY);
+#else
+ rc = fcntl(sd, F_SETFL, O_NONBLOCK);
+#endif
+ if (rc == -1) {
+ scsp_log(LOG_ERR, "scsp_dcs_listen: fcntl failed");
+ rc = errno;
+ goto listen_fail;
+ }
+
+ /*
+ * Set AAL 5 options
+ */
+ aal5.forward_max_SDU_size = ssp->ss_mtu;
+ aal5.backward_max_SDU_size = ssp->ss_mtu;
+ if (setsockopt(sd, T_ATM_SIGNALING, T_ATM_AAL5, (caddr_t)&aal5,
+ sizeof(aal5)) < 0) {
+ rc = EOPNOTSUPP;
+ goto listen_fail;
+ }
+
+ /*
+ * Set traffic options
+ */
+ switch(ssp->ss_media) {
+ case MEDIA_TAXI_100:
+ traffic.forward.PCR_all_traffic = ATM_PCR_TAXI100;
+ traffic.backward.PCR_all_traffic = ATM_PCR_TAXI100;
+ break;
+ case MEDIA_TAXI_140:
+ traffic.forward.PCR_all_traffic = ATM_PCR_TAXI140;
+ traffic.backward.PCR_all_traffic = ATM_PCR_TAXI140;
+ break;
+ case MEDIA_OC3C:
+ case MEDIA_UTP155:
+ traffic.forward.PCR_all_traffic = ATM_PCR_OC3C;
+ traffic.backward.PCR_all_traffic = ATM_PCR_OC3C;
+ break;
+ case MEDIA_OC12C:
+ traffic.forward.PCR_all_traffic = ATM_PCR_OC12C;
+ traffic.backward.PCR_all_traffic = ATM_PCR_OC12C;
+ break;
+ case MEDIA_UNKNOWN:
+ break;
+ }
+
+ if (setsockopt(sd, T_ATM_SIGNALING, T_ATM_TRAFFIC,
+ (caddr_t)&traffic, sizeof(traffic)) < 0) {
+ rc = EOPNOTSUPP;
+ goto listen_fail;
+ }
+
+ /*
+ * Set bearer capability options
+ */
+ if (setsockopt(sd, T_ATM_SIGNALING, T_ATM_BEARER_CAP,
+ (caddr_t)&bearer, sizeof(bearer)) < 0) {
+ rc = EOPNOTSUPP;
+ goto listen_fail;
+ }
+
+ /*
+ * Set QOS options
+ */
+ if (setsockopt(sd, T_ATM_SIGNALING, T_ATM_QOS,
+ (caddr_t)&qos, sizeof(qos)) < 0) {
+ rc = EOPNOTSUPP;
+ goto listen_fail;
+ }
+
+ /*
+ * Set LLC identifier
+ */
+ if (setsockopt(sd, T_ATM_SIGNALING, T_ATM_LLC,
+ (caddr_t)&llc_scsp, sizeof(llc_scsp)) < 0) {
+ rc = EOPNOTSUPP;
+ goto listen_fail;
+ }
+
+ /*
+ * Set application name
+ */
+ if (setsockopt(sd, T_ATM_SIGNALING, T_ATM_APP_NAME,
+ (caddr_t)&appname, sizeof(appname)) < 0) {
+ rc = EOPNOTSUPP;
+ goto listen_fail;
+ }
+
+ /*
+ * Listen for new connections
+ */
+ if (listen(sd, 5) < 0) {
+ rc = errno;
+ goto listen_fail;
+ }
+
+ ssp->ss_dcs_lsock = sd;
+ return(sd);
+
+listen_fail:
+ /*
+ * Close the socket if anything didn't work
+ */
+ (void)close(sd);
+ if (rc == 0)
+ errno = EFAULT;
+ else
+ errno = rc;
+ ssp->ss_dcs_lsock = -1;
+ return(-1);
+}
+
+
+/*
+ * Accept a connection from a DCS
+ *
+ * Arguments:
+ * ssp pointer to server block
+ *
+ * Returns:
+ * address of DCS with new connection
+ * 0 failure (errno has reason)
+ *
+ */
+Scsp_dcs *
+scsp_dcs_accept(ssp)
+ Scsp_server *ssp;
+{
+ int len, rc, sd;
+ struct sockaddr_atm dcs_sockaddr;
+ struct t_atm_sap_addr *dcs_addr = &dcs_sockaddr.satm_addr.t_atm_sap_addr;
+ Atm_addr dcs_atmaddr;
+ Scsp_dcs *dcsp;
+
+ /*
+ * Accept the new connection
+ */
+ len = sizeof(dcs_sockaddr);
+ sd = accept(ssp->ss_dcs_lsock,
+ (struct sockaddr *)&dcs_sockaddr, &len);
+ if (sd < 0) {
+ return((Scsp_dcs *)0);
+ }
+ if (sd > scsp_max_socket) {
+ scsp_max_socket = sd;
+ }
+
+ /*
+ * Copy the DCS's address from the sockaddr to an Atm_addr
+ */
+ if (dcs_addr->SVE_tag_addr != T_ATM_PRESENT) {
+ dcs_atmaddr.address_format = T_ATM_ABSENT;
+ dcs_atmaddr.address_length = 0;
+ } else {
+ dcs_atmaddr.address_format = dcs_addr->address_format;
+ dcs_atmaddr.address_length = dcs_addr->address_length;
+ bcopy(dcs_addr->address, dcs_atmaddr.address,
+ dcs_addr->address_length);
+ }
+
+ /*
+ * Find out which DCS this connection is for
+ */
+ for (dcsp = ssp->ss_dcs; dcsp; dcsp = dcsp->sd_next) {
+ /*
+ * Compare DCS's address to address
+ * configured by user
+ */
+ if (ATM_ADDR_EQUAL(&dcsp->sd_addr,
+ &dcs_atmaddr))
+ break;
+ }
+
+ /*
+ * Make sure we have this DCS configured
+ */
+ if (!dcsp) {
+ errno = EINVAL;
+ goto dcs_accept_fail;
+ }
+
+ /*
+ * Make sure we are in a state to accept the connection
+ */
+ if (ssp->ss_state != SCSP_SS_ACTIVE) {
+ errno = EACCES;
+ goto dcs_accept_fail;
+ }
+
+ /*
+ * Make sure we don't already have a connection to this DCS
+ */
+ if (dcsp->sd_sock != -1) {
+ errno = EALREADY;
+ goto dcs_accept_fail;
+ }
+
+ /*
+ * Set application name
+ */
+ if (setsockopt(sd, T_ATM_SIGNALING, T_ATM_APP_NAME,
+ (caddr_t)&appname, sizeof(appname)) < 0) {
+ rc = EOPNOTSUPP;
+ goto dcs_accept_fail;
+ }
+
+ /*
+ * Set non-blocking I/O
+ */
+#ifdef sun
+ rc = fcntl(sd, F_SETFL, FNBIO + FNDELAY);
+#else
+ rc = fcntl(sd, F_SETFL, O_NONBLOCK);
+#endif
+ if (rc == -1) {
+ goto dcs_accept_fail;
+ }
+
+ /*
+ * Cancel the open retry timer
+ */
+ HARP_CANCEL(&dcsp->sd_open_t);
+
+ /*
+ * Save the socket address and return the
+ * address of the DCS
+ */
+ dcsp->sd_sock = sd;
+ return(dcsp);
+
+dcs_accept_fail:
+ /*
+ * An error has occured--clean up and return
+ */
+ (void)close(sd);
+ return((Scsp_dcs *)0);
+}
+
+
+/*
+ * Read an SCSP message from a directly connected server
+ *
+ * Arguments:
+ * dcsp pointer to DCS block that has data
+ *
+ * Returns:
+ * 0 success
+ * else errno indicating reason for failure
+ *
+ */
+int
+scsp_dcs_read(dcsp)
+ Scsp_dcs *dcsp;
+
+{
+ int len, rc;
+ char *buff = (char *)0;
+ Scsp_server *ssp = dcsp->sd_server;
+ Scsp_msg *msg;
+
+ /*
+ * Get a buffer to hold the entire message
+ */
+ len = ssp->ss_mtu;
+ buff = calloc(1, len);
+ if (buff == NULL)
+ scsp_mem_err("scsp_dcs_read: ssp->ss_mtu");
+
+ /*
+ * Read the message
+ */
+ len = read(dcsp->sd_sock, buff, len);
+ if (len < 0) {
+ goto dcs_read_fail;
+ }
+
+ /*
+ * Parse the input message and pass it to the Hello FSM
+ */
+ msg = scsp_parse_msg(buff, len);
+ if (msg) {
+ /*
+ * Write the message to the trace file if
+ * it's of a type we're tracing
+ */
+ if (((scsp_trace_mode & SCSP_TRACE_HELLO_MSG) &&
+ msg->sc_msg_type == SCSP_HELLO_MSG) ||
+ ((scsp_trace_mode & SCSP_TRACE_CA_MSG) &&
+ msg->sc_msg_type != SCSP_HELLO_MSG)) {
+ scsp_trace_msg(dcsp, msg, 1);
+ scsp_trace("\n");
+ }
+
+ /*
+ * Pass the message to the Hello FSM
+ */
+ rc = scsp_hfsm(dcsp, SCSP_HFSM_RCVD, msg);
+ scsp_free_msg(msg);
+ } else {
+ /*
+ * Message was invalid. Write it to the trace file
+ * if we're tracing messages.
+ */
+ if (scsp_trace_mode & (SCSP_TRACE_HELLO_MSG &
+ SCSP_TRACE_CA_MSG)) {
+ int i;
+ scsp_trace("Invalid message received:\n");
+ scsp_trace("0x");
+ for (i = 0; i < len; i++) {
+ scsp_trace("%02x ", (u_char)buff[i]);
+ }
+ scsp_trace("\n");
+ }
+ }
+ free(buff);
+
+ return(0);
+
+dcs_read_fail:
+ /*
+ * Error on read--check for special conditions
+ */
+ rc = errno;
+ if (errno == ECONNRESET) {
+ /*
+ * VCC has been closed--pass the event to
+ * the Hello FSM
+ */
+ rc = scsp_hfsm(dcsp, SCSP_HFSM_VC_CLOSED,
+ (Scsp_msg *)0);
+ }
+ if (errno == ECONNREFUSED) {
+ /*
+ * VCC open failed--set a timer and try
+ * again when it fires
+ */
+ HARP_TIMER(&dcsp->sd_open_t,
+ SCSP_Open_Interval,
+ scsp_open_timeout);
+ rc = 0;
+ }
+
+ if (buff)
+ free(buff);
+ return(rc);
+}
+
+
+/*
+ * Listen for Unix connections from SCSP client servers
+ *
+ * Arguments:
+ * None
+ *
+ * Returns:
+ * sock socket which is listening
+ * -1 error (reason in errno)
+ *
+ */
+int
+scsp_server_listen()
+{
+ int rc, sd;
+
+ static struct sockaddr scsp_addr = {
+#if (defined(BSD) && (BSD >= 199103))
+ sizeof(struct sockaddr), /* sa_len */
+#endif
+ AF_UNIX, /* sa_family */
+ SCSPD_SOCK_NAME /* sa_data */
+ };
+
+ /*
+ * Unlink any old socket
+ */
+ rc = unlink(SCSPD_SOCK_NAME);
+ if (rc < 0 && errno != ENOENT)
+ return(-1);
+
+ /*
+ * Open a socket
+ */
+ sd = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (sd == -1) {
+ return(-1);
+ }
+ if (sd > scsp_max_socket) {
+ scsp_max_socket = sd;
+ }
+
+ /*
+ * Bind the socket's address
+ */
+ rc = bind(sd, &scsp_addr, sizeof(scsp_addr));
+ if (rc == -1) {
+ (void)close(sd);
+ return(-1);
+ }
+
+ /*
+ * Set non-blocking I/O
+ */
+#ifdef sun
+ rc = fcntl(sd, F_SETFL, FNBIO + FNDELAY);
+#else
+ rc = fcntl(sd, F_SETFL, O_NONBLOCK);
+#endif
+ if (rc == -1) {
+ (void)close(sd);
+ return(-1);
+ }
+
+ /*
+ * Listen for new connections
+ */
+ if (listen(sd, 5) < 0) {
+ (void)close(sd);
+ return(-1);
+ }
+
+ return(sd);
+}
+
+
+/*
+ * Accept a connection from a server
+ *
+ * We accept a connection, but we won't know which server it is
+ * from until we get the configuration data from the server. We
+ * put the connection on a 'pending' queue and will assign it to
+ * a server when the config data arrives.
+ *
+ * Arguments:
+ * ls listening socket to accept from
+ *
+ * Returns:
+ * 0 success
+ * errno reason for failure
+ *
+ */
+int
+scsp_server_accept(ls)
+ int ls;
+
+{
+ int len, rc, sd;
+ struct sockaddr server_addr;
+ Scsp_pending *psp;
+
+ /*
+ * Accept the new connection
+ */
+ len = sizeof(server_addr);
+ sd = accept(ls, (struct sockaddr *)&server_addr, &len);
+ if (sd < 0) {
+ return(errno);
+ }
+ if (sd > scsp_max_socket) {
+ scsp_max_socket = sd;
+ }
+
+ /*
+ * Set non-blocking operation
+ */
+#ifdef sun
+ rc = fcntl(sd, F_SETFL, FNBIO + FNDELAY);
+#else
+ rc = fcntl(sd, F_SETFL, O_NONBLOCK);
+#endif
+ if (rc == -1) {
+ (void)close(sd);
+ rc = errno;
+ }
+
+ /*
+ * Put the new socket on the 'pending' queue
+ */
+ psp = calloc(1, sizeof(Scsp_pending));
+ if (!psp)
+ scsp_mem_err("scsp_server_accept: sizeof(Scsp_pending)");
+ psp->sp_sock = sd;
+ LINK2TAIL(psp, Scsp_pending, scsp_pending_head, sp_next);
+
+ return(0);
+}
+
+
+/*
+ * Read a server interface message from a socket
+ *
+ * Arguments:
+ * sd socket to read from
+ *
+ * Returns:
+ * msg pointer to message read
+ * 0 failure (errno has reason)
+ *
+ */
+Scsp_if_msg *
+scsp_if_sock_read(sd)
+ int sd;
+
+{
+ int len;
+ char *buff = (char *)0;
+ Scsp_if_msg *msg;
+ Scsp_if_msg_hdr msg_hdr;
+
+ /*
+ * Read the message header from the socket
+ */
+ len = read(sd, (char *)&msg_hdr, sizeof(msg_hdr));
+ if (len != sizeof(msg_hdr)) {
+ if (len >= 0)
+ errno = EINVAL;
+ goto socket_read_fail;
+ }
+
+ /*
+ * Get a buffer and read the rest of the message into it
+ */
+ buff = malloc(msg_hdr.sh_len);
+ if (buff == NULL)
+ scsp_mem_err("scsp_if_sock_read: msg_hdr.sh_len");
+ msg = (Scsp_if_msg *)buff;
+ msg->si_hdr = msg_hdr;
+ len = read(sd, &buff[sizeof(Scsp_if_msg_hdr)],
+ msg->si_len - sizeof(Scsp_if_msg_hdr));
+ if (len != msg->si_len - sizeof(Scsp_if_msg_hdr)) {
+ if (len >= 0) {
+ errno = EINVAL;
+ }
+ goto socket_read_fail;
+ }
+
+ /*
+ * Trace the message
+ */
+ if (scsp_trace_mode & SCSP_TRACE_IF_MSG) {
+ scsp_trace("Received server I/F message:\n");
+ print_scsp_if_msg(scsp_trace_file, msg);
+ scsp_trace("\n");
+ }
+
+ return(msg);
+
+socket_read_fail:
+ if (buff)
+ free(buff);
+ return((Scsp_if_msg *)0);
+}
+
+
+/*
+ * Write a server interface message to a socket
+ *
+ * Arguments:
+ * sd socket to write to
+ * msg pointer to message to write
+ *
+ * Returns:
+ * 0 success
+ * errno reason for failure
+ *
+ */
+int
+scsp_if_sock_write(sd, msg)
+ int sd;
+ Scsp_if_msg *msg;
+{
+ int len, rc;
+
+ /*
+ * Trace the message
+ */
+ if (scsp_trace_mode & SCSP_TRACE_IF_MSG) {
+ scsp_trace("Writing server I/F message:\n");
+ print_scsp_if_msg(scsp_trace_file, msg);
+ scsp_trace("\n");
+ }
+
+ /*
+ * Write the message to the indicated socket
+ */
+ len = write(sd, (char *)msg, msg->si_len);
+ if (len != msg->si_len) {
+ if (len < 0)
+ rc = errno;
+ else
+ rc = EINVAL;
+ } else {
+ rc = 0;
+ }
+
+ return(rc);
+}
+
+
+/*
+ * Read data from a local server
+ *
+ * Arguments:
+ * ssp pointer to server block that has data
+ *
+ * Returns:
+ * 0 success
+ * else errno indicating reason for failure
+ *
+ */
+int
+scsp_server_read(ssp)
+ Scsp_server *ssp;
+{
+ int rc;
+ Scsp_dcs *dcsp;
+ Scsp_if_msg *msg;
+
+ /*
+ * Read the message
+ */
+ msg = scsp_if_sock_read(ssp->ss_sock);
+ if (!msg) {
+ if (errno == EWOULDBLOCK) {
+ /*
+ * Nothing to read--just return
+ */
+ return(0);
+ } else {
+ /*
+ * Error--shut down the server entry
+ */
+ scsp_server_shutdown(ssp);
+ }
+ return(errno);
+ }
+
+ /*
+ * Process the received message
+ */
+ switch(msg->si_type) {
+ case SCSP_NOP_REQ:
+ /*
+ * Ignore a NOP
+ */
+ break;
+ case SCSP_CACHE_RSP:
+ /*
+ * Summarize the server's cache and try to open
+ * connections to all of its DCSs
+ */
+ scsp_process_cache_rsp(ssp, msg);
+ ssp->ss_state = SCSP_SS_ACTIVE;
+ for (dcsp = ssp->ss_dcs; dcsp; dcsp = dcsp->sd_next) {
+ if (scsp_dcs_connect(dcsp)) {
+ /*
+ * Connect failed -- the DCS may not
+ * be up yet, so we'll try again later
+ */
+ HARP_TIMER(&dcsp->sd_open_t,
+ SCSP_Open_Interval,
+ scsp_open_timeout);
+ }
+ }
+ ssp->ss_state = SCSP_SS_ACTIVE;
+ break;
+ case SCSP_SOLICIT_RSP:
+ /*
+ * The server has answered our request for a particular
+ * entry from its cache
+ */
+ dcsp = (Scsp_dcs *)msg->si_tok;
+ rc = scsp_cfsm(dcsp, SCSP_CIFSM_SOL_RSP, (Scsp_msg *)0,
+ msg);
+ break;
+ case SCSP_UPDATE_REQ:
+ /*
+ * Pass the update request to the FSMs for all
+ * DCSs associated with the server
+ */
+ if (ssp->ss_state == SCSP_SS_ACTIVE) {
+ for (dcsp = ssp->ss_dcs; dcsp;
+ dcsp = dcsp->sd_next) {
+ rc = scsp_cfsm(dcsp, SCSP_CIFSM_UPD_REQ,
+ (Scsp_msg *)0, msg);
+ }
+ }
+ break;
+ case SCSP_UPDATE_RSP:
+ /*
+ * Pass the update response to the FSM for the
+ * DCS associated with the request
+ */
+ dcsp = (Scsp_dcs *)msg->si_tok;
+ rc = scsp_cfsm(dcsp, SCSP_CIFSM_UPD_RSP,
+ (Scsp_msg *)0, msg);
+ break;
+ default:
+ scsp_log(LOG_ERR, "invalid message type %d from server",
+ msg->si_type);
+ return(EINVAL);
+ }
+
+ free(msg);
+ return(0);
+}
+
+
+/*
+ * Send a Cache Indication to a server
+ *
+ * Arguments:
+ * ssp pointer to server block block
+ *
+ * Returns:
+ * 0 success
+ * else errno indicating reason for failure
+ *
+ */
+int
+scsp_send_cache_ind(ssp)
+ Scsp_server *ssp;
+{
+ int rc;
+ Scsp_if_msg *msg;
+
+ /*
+ * Get storage for a server interface message
+ */
+ msg = calloc(1, sizeof(Scsp_if_msg));
+ if (msg == NULL)
+ scsp_mem_err("scsp_send_cache_ind: sizeof(Scsp_if_msg)");
+ /*
+ * Fill out the message
+ */
+ msg->si_type = SCSP_CACHE_IND;
+ msg->si_proto = ssp->ss_pid;
+ msg->si_len = sizeof(Scsp_if_msg_hdr);
+ msg->si_tok = (u_long)ssp;
+
+ /*
+ * Send the message
+ */
+ rc = scsp_if_sock_write(ssp->ss_sock, msg);
+ free(msg);
+ return(rc);
+}
+
+
+/*
+ * Read data from a pending server connection
+ *
+ * Arguments:
+ * psp pointer to pending block that has data
+ *
+ * Returns:
+ * 0 success
+ * else errno indicating reason for failure
+ *
+ */
+int
+scsp_pending_read(psp)
+ Scsp_pending *psp;
+
+{
+ int rc;
+ Scsp_server *ssp;
+ Scsp_if_msg *msg;
+
+ /*
+ * Read the message from the pending socket
+ */
+ msg = scsp_if_sock_read(psp->sp_sock);
+ if (!msg) {
+ rc = errno;
+ goto pending_read_fail;
+ }
+
+ /*
+ * Make sure this is configuration data
+ */
+ if (msg->si_type != SCSP_CFG_REQ) {
+ scsp_log(LOG_ERR, "invalid message type %d from pending server",
+ msg->si_type);
+ rc = EINVAL;
+ goto pending_read_fail;
+ }
+
+ /*
+ * Find the server this message is for
+ */
+ for (ssp = scsp_server_head; ssp; ssp = ssp->ss_next) {
+ if (strcmp(ssp->ss_intf, msg->si_cfg.atmarp_netif) == 0)
+ break;
+ }
+ if (!ssp) {
+ scsp_log(LOG_ERR, "refused connection from server for %s",
+ msg->si_cfg.atmarp_netif);
+ rc = EINVAL;
+ goto config_reject;
+ }
+
+ /*
+ * Make sure the server is ready to go
+ */
+ rc = scsp_get_server_info(ssp);
+ if (rc) {
+ goto config_reject;
+ }
+
+ /*
+ * Save the socket
+ */
+ ssp->ss_sock = psp->sp_sock;
+ ssp->ss_state = SCSP_SS_CFG;
+ UNLINK(psp, Scsp_pending, scsp_pending_head, sp_next);
+ free(psp);
+
+ /*
+ * Listen for connections from the server's DCSs
+ */
+ rc = scsp_dcs_listen(ssp);
+ if (rc < 0) {
+ rc = errno;
+ goto config_reject;
+ }
+
+ /*
+ * Respond to the configuration message
+ */
+ msg->si_type = SCSP_CFG_RSP;
+ msg->si_rc = SCSP_RSP_OK;
+ msg->si_len = sizeof(Scsp_if_msg_hdr);
+ rc = scsp_if_sock_write(ssp->ss_sock, msg);
+ if (rc) {
+ goto config_error;;
+ }
+
+ /*
+ * Ask the server to send us its cache
+ */
+ rc = scsp_send_cache_ind(ssp);
+ if (rc) {
+ goto config_error;
+ }
+
+ free(msg);
+ return(0);
+
+config_reject:
+ /*
+ * Respond to the configuration message
+ */
+ msg->si_type = SCSP_CFG_RSP;
+ msg->si_rc = SCSP_RSP_REJ;
+ msg->si_len = sizeof(Scsp_if_msg_hdr);
+ (void)scsp_if_sock_write(ssp->ss_sock, msg);
+
+config_error:
+ if (ssp->ss_sock != -1) {
+ (void)close(ssp->ss_sock);
+ ssp->ss_sock = -1;
+ }
+ if (ssp->ss_dcs_lsock != -1) {
+ (void)close(ssp->ss_dcs_lsock);
+ ssp->ss_sock = -1;
+ }
+ ssp->ss_state = SCSP_SS_NULL;
+ free(msg);
+
+ return(rc);
+
+pending_read_fail:
+ /*
+ * Close the socket and free the pending read block
+ */
+ (void)close(psp->sp_sock);
+ UNLINK(psp, Scsp_pending, scsp_pending_head, sp_next);
+ free(psp);
+ if (msg)
+ free(msg);
+ return(rc);
+}
diff --git a/usr.sbin/atm/scspd/scsp_subr.c b/usr.sbin/atm/scspd/scsp_subr.c
new file mode 100644
index 0000000..4873ca5
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_subr.c
@@ -0,0 +1,1112 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * SCSP subroutines
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netatm/port.h>
+#include <netatm/queue.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sigmgr.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+#include <netatm/uni/unisig_var.h>
+
+#include <errno.h>
+#include <libatm.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "scsp_msg.h"
+#include "scsp_if.h"
+#include "scsp_var.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+/*
+ * Hash an SCSP cache key
+ *
+ * Arguments:
+ * ckp pointer to an SCSP cache key structure
+ *
+ * Returns:
+ * hashed value
+ *
+ */
+int
+scsp_hash(ckp)
+ Scsp_ckey *ckp;
+{
+ int i, j, h;
+
+ /*
+ * Turn cache key into a positive integer
+ */
+ h = 0;
+ for (i = ckp->key_len-1, j = 0;
+ i > 0 && j < sizeof(int);
+ i--, j++)
+ h = (h << 8) + ckp->key[i];
+ h = abs(h);
+
+ /*
+ * Return the hashed value
+ */
+ return(h % SCSP_HASHSZ);
+}
+
+
+/*
+ * Compare two SCSP IDs
+ *
+ * Arguments:
+ * id1p pointer to an SCSP ID structure
+ * id2p pointer to an SCSP ID structure
+ *
+ * Returns:
+ * < 0 id1 is less than id2
+ * 0 id1 and id2 are equal
+ * > 0 id1 is greater than id2
+ *
+ */
+int
+scsp_cmp_id(id1p, id2p)
+ Scsp_id *id1p;
+ Scsp_id *id2p;
+{
+ int diff, i;
+
+ /*
+ * Compare the two IDs, byte for byte
+ */
+ for (i = 0; i < id1p->id_len && i < id2p->id_len; i++) {
+ diff = id1p->id[i] - id2p->id[i];
+ if (diff) {
+ return(diff);
+ }
+ }
+
+ /*
+ * IDs are equal. If lengths differ, the longer ID is
+ * greater than the shorter.
+ */
+ return(id1p->id_len - id2p->id_len);
+}
+
+
+/*
+ * Compare two SCSP cache keys
+ *
+ * Arguments:
+ * ck1p pointer to an SCSP cache key structure
+ * ck2p pointer to an SCSP cache key structure
+ *
+ * Returns:
+ * < 0 ck1 is less than ck2
+ * 0 ck1 and ck2 are equal
+ * > 0 ck1 is greater than ck2
+ *
+ */
+int
+scsp_cmp_key(ck1p, ck2p)
+ Scsp_ckey *ck1p;
+ Scsp_ckey *ck2p;
+{
+ int diff, i;
+
+ /*
+ * Compare the two keys, byte for byte
+ */
+ for (i = 0; i < ck1p->key_len && i < ck2p->key_len; i++) {
+ diff = ck1p->key[i] - ck2p->key[i];
+ if (diff)
+ return(diff);
+ }
+
+ /*
+ * Keys are equal. If lengths differ, the longer key is
+ * greater than the shorter.
+ */
+ return(ck1p->key_len - ck2p->key_len);
+}
+
+
+/*
+ * Check whether the host system is an ATMARP server for
+ * the LIS associated with a given interface
+ *
+ * Arguments:
+ * netif pointer to the network interface name
+ *
+ * Returns:
+ * 1 host is a server
+ * 0 host is not a server
+ *
+ */
+int
+scsp_is_atmarp_server(netif)
+ char *netif;
+{
+ int rc;
+ 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);
+ free(asrv_info);
+ return(rc);
+}
+
+
+/*
+ * Make a copy of a cache summary entry
+ *
+ * Arguments:
+ * csep pointer to CSE entry to copy
+ *
+ * Returns:
+ * 0 copy failed
+ * else pointer to new CSE entry
+ *
+ */
+Scsp_cse *
+scsp_dup_cse(csep)
+ Scsp_cse *csep;
+{
+ Scsp_cse *dupp;
+
+ /*
+ * Allocate memory for the duplicate
+ */
+ dupp = malloc(sizeof(Scsp_cse));
+ if (dupp == NULL)
+ scsp_mem_err("scsp_dup_cse: sizeof(Scsp_cse)");
+
+ /*
+ * Copy data to the duplicate
+ */
+ bcopy(csep, dupp, sizeof(Scsp_cse));
+ dupp->sc_next = (Scsp_cse *)0;
+ return(dupp);
+}
+
+
+/*
+ * Make a copy of a CSA or CSAS record
+ *
+ * Arguments:
+ * csap pointer to CSE entry to copy
+ *
+ * Returns:
+ * 0 copy failed
+ * else pointer to new CSA or CSAS record
+ *
+ */
+Scsp_csa *
+scsp_dup_csa(csap)
+ Scsp_csa *csap;
+{
+ Scsp_csa *dupp;
+ Scsp_atmarp_csa *adp;
+
+ /*
+ * Allocate memory for the duplicate
+ */
+ dupp = malloc(sizeof(Scsp_csa));
+ if (dupp == NULL)
+ scsp_mem_err("scsp_dup_csa: sizeof(Scsp_csa)");
+
+ /*
+ * Copy data to the duplicate
+ */
+ bcopy(csap, dupp, sizeof(Scsp_csa));
+ dupp->next = (Scsp_csa *)0;
+
+ /*
+ * Copy protocol-specific data, if it's present
+ */
+ if (csap->atmarp_data) {
+ adp = malloc(sizeof(Scsp_atmarp_csa));
+ if (adp == NULL)
+ scsp_mem_err("scsp_dup_csa: sizeof(Scsp_atmarp_csa)");
+ bcopy(csap->atmarp_data, adp, sizeof(Scsp_atmarp_csa));
+ dupp->atmarp_data = adp;
+ }
+ return(dupp);
+}
+
+
+/*
+ * Copy a cache summary entry into a CSAS
+ *
+ * Arguments:
+ * csep pointer to CSE entry to copy
+ *
+ * Returns:
+ * 0 copy failed
+ * else pointer to CSAS record summarizing the entry
+ *
+ */
+Scsp_csa *
+scsp_cse2csas(csep)
+ Scsp_cse *csep;
+{
+ Scsp_csa *csap;
+
+ /*
+ * Allocate memory for the duplicate
+ */
+ csap = calloc(1, sizeof(Scsp_csa));
+ if (csap == NULL)
+ scsp_mem_err("scsp_cse2csas: sizeof(Scsp_csa)");
+
+ /*
+ * Copy data to the CSAS entry
+ */
+ csap->seq = csep->sc_seq;
+ csap->key = csep->sc_key;
+ csap->oid = csep->sc_oid;
+
+ return(csap);
+}
+
+
+/*
+ * Copy an ATMARP cache entry into a cache summary entry
+ *
+ * Arguments:
+ * aap pointer to ATMARP cache entry to copy
+ *
+ * Returns:
+ * 0 copy failed
+ * else pointer to CSE record summarizing the entry
+ *
+ */
+Scsp_cse *
+scsp_atmarp2cse(aap)
+ Scsp_atmarp_msg *aap;
+{
+ Scsp_cse *csep;
+
+ /*
+ * Allocate memory for the duplicate
+ */
+ csep = calloc(1, sizeof(Scsp_cse));
+ if (csep == NULL)
+ scsp_mem_err("scsp_atmarp2cse: sizeof(Scsp_cse)");
+
+ /*
+ * Copy data to the CSE entry
+ */
+ csep->sc_seq = aap->sa_seq;
+ csep->sc_key = aap->sa_key;
+ csep->sc_oid = aap->sa_oid;
+
+ return(csep);
+}
+
+
+/*
+ * Clean up a DCS block. This routine is called to clear out any
+ * lingering state information when the CA FSM reverts to an 'earlier'
+ * state (Down or Master/Slave Negotiation).
+ *
+ * Arguments:
+ * dcsp pointer to a DCS control block for the neighbor
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+scsp_dcs_cleanup(dcsp)
+ Scsp_dcs *dcsp;
+{
+ Scsp_cse *csep, *ncsep;
+ Scsp_csa *csap, *next_csap;
+ Scsp_csu_rexmt *rxp, *rx_next;
+
+ /*
+ * Free any CSAS entries waiting to be sent
+ */
+ for (csep = dcsp->sd_ca_csas; csep; csep = ncsep) {
+ ncsep = csep->sc_next;
+ UNLINK(csep, Scsp_cse, dcsp->sd_ca_csas, sc_next);
+ free(csep);
+ }
+
+ /*
+ * Free any entries on the CRL
+ */
+ for (csap = dcsp->sd_crl; csap; csap = next_csap) {
+ next_csap = csap->next;
+ UNLINK(csap, Scsp_csa, dcsp->sd_crl, next);
+ SCSP_FREE_CSA(csap);
+ }
+
+ /*
+ * Free any saved CA message and cancel the CA
+ * retransmission timer
+ */
+ if (dcsp->sd_ca_rexmt_msg) {
+ scsp_free_msg(dcsp->sd_ca_rexmt_msg);
+ dcsp->sd_ca_rexmt_msg = (Scsp_msg *)0;
+ }
+ HARP_CANCEL(&dcsp->sd_ca_rexmt_t);
+
+ /*
+ * Free any saved CSU Solicit message and cancel the CSUS
+ * retransmission timer
+ */
+ if (dcsp->sd_csus_rexmt_msg) {
+ scsp_free_msg(dcsp->sd_csus_rexmt_msg);
+ dcsp->sd_csus_rexmt_msg = (Scsp_msg *)0;
+ }
+ HARP_CANCEL(&dcsp->sd_csus_rexmt_t);
+
+ /*
+ * Free any entries on the CSU Request retransmission queue
+ */
+ for (rxp = dcsp->sd_csu_rexmt; rxp; rxp = rx_next) {
+ rx_next = rxp->sr_next;
+ HARP_CANCEL(&rxp->sr_t);
+ for (csap = rxp->sr_csa; csap; csap = next_csap) {
+ next_csap = csap->next;
+ SCSP_FREE_CSA(csap);
+ }
+ UNLINK(rxp, Scsp_csu_rexmt, dcsp->sd_csu_rexmt,
+ sr_next);
+ free(rxp);
+ }
+}
+
+
+/*
+ * Delete an SCSP DCS block and any associated information
+ *
+ * Arguments:
+ * dcsp pointer to a DCS control block to delete
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+scsp_dcs_delete(dcsp)
+ Scsp_dcs *dcsp;
+{
+ Scsp_cse *csep, *next_cse;
+ Scsp_csu_rexmt *rxp, *next_rxp;
+ Scsp_csa *csap, *next_csa;
+
+ /*
+ * Cancel any pending DCS timers
+ */
+ HARP_CANCEL(&dcsp->sd_open_t);
+ HARP_CANCEL(&dcsp->sd_hello_h_t);
+ HARP_CANCEL(&dcsp->sd_hello_rcv_t);
+ HARP_CANCEL(&dcsp->sd_ca_rexmt_t);
+ HARP_CANCEL(&dcsp->sd_csus_rexmt_t);
+
+ /*
+ * Unlink the DCS block from the server block
+ */
+ UNLINK(dcsp, Scsp_dcs, dcsp->sd_server->ss_dcs, sd_next);
+
+ /*
+ * Close the VCC to the DCS, if one is open
+ */
+ if (dcsp->sd_sock != -1) {
+ (void)close(dcsp->sd_sock);
+ }
+
+ /*
+ * Free any saved CA message
+ */
+ if (dcsp->sd_ca_rexmt_msg) {
+ scsp_free_msg(dcsp->sd_ca_rexmt_msg);
+ }
+
+ /*
+ * Free any pending CSAs waiting for cache alignment
+ */
+ for (csep = dcsp->sd_ca_csas; csep; csep = next_cse) {
+ next_cse = csep->sc_next;
+ free(csep);
+ }
+
+ /*
+ * Free anything on the cache request list
+ */
+ for (csap = dcsp->sd_crl; csap; csap = next_csa) {
+ next_csa = csap->next;
+ SCSP_FREE_CSA(csap);
+ }
+
+ /*
+ * Free any saved CSUS message
+ */
+ if (dcsp->sd_csus_rexmt_msg) {
+ scsp_free_msg(dcsp->sd_csus_rexmt_msg);
+ }
+
+ /*
+ * Free anything on the CSU Request retransmit queue
+ */
+ for (rxp = dcsp->sd_csu_rexmt; rxp; rxp = next_rxp) {
+ /*
+ * Cancel the retransmit timer
+ */
+ HARP_CANCEL(&rxp->sr_t);
+
+ /*
+ * Free the CSAs to be retransmitted
+ */
+ for (csap = rxp->sr_csa; csap; csap = next_csa) {
+ next_csa = csap->next;
+ SCSP_FREE_CSA(csap);
+ }
+
+ /*
+ * Free the CSU Req retransmission control block
+ */
+ next_rxp = rxp->sr_next;
+ free(rxp);
+ }
+
+ /*
+ * Free the DCS block
+ */
+ free(dcsp);
+}
+
+
+/*
+ * Shut down a server. This routine is called when a connection to
+ * a server is lost. It will clear the server's state without deleting
+ * the server.
+ *
+ * Arguments:
+ * ssp pointer to a server control block
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+scsp_server_shutdown(ssp)
+ Scsp_server *ssp;
+{
+ int i;
+ Scsp_dcs *dcsp;
+ Scsp_cse *csep;
+
+ /*
+ * Trace the shutdown
+ */
+ if (scsp_trace_mode & (SCSP_TRACE_IF_MSG | SCSP_TRACE_CFSM)) {
+ scsp_trace("Server %s being shut down\n",
+ ssp->ss_name);
+ }
+
+ /*
+ * Terminate up all the DCS connections and clean
+ * up the control blocks
+ */
+ for (dcsp = ssp->ss_dcs; dcsp; dcsp = dcsp->sd_next) {
+ if (dcsp->sd_sock != -1) {
+ (void)close(dcsp->sd_sock);
+ dcsp->sd_sock = -1;
+ }
+ HARP_CANCEL(&dcsp->sd_open_t);
+ HARP_CANCEL(&dcsp->sd_hello_h_t);
+ HARP_CANCEL(&dcsp->sd_hello_rcv_t);
+ scsp_dcs_cleanup(dcsp);
+ dcsp->sd_hello_state = SCSP_HFSM_DOWN;
+ dcsp->sd_ca_state = SCSP_CAFSM_DOWN;
+ dcsp->sd_client_state = SCSP_CIFSM_NULL;
+ }
+
+ /*
+ * Clean up the server control block
+ */
+ if (ssp->ss_sock != -1) {
+ (void)close(ssp->ss_sock);
+ ssp->ss_sock = -1;
+ }
+ if (ssp->ss_dcs_lsock != -1) {
+ (void)close(ssp->ss_dcs_lsock);
+ ssp->ss_dcs_lsock = -1;
+ }
+ ssp->ss_state = SCSP_SS_NULL;
+
+ /*
+ * Free the entries in the server's summary cache
+ */
+ for (i = 0; i < SCSP_HASHSZ; i++) {
+ while (ssp->ss_cache[i]) {
+ csep = ssp->ss_cache[i];
+ UNLINK(csep, Scsp_cse, ssp->ss_cache[i],
+ sc_next);
+ free(csep);
+ }
+ }
+}
+
+
+/*
+ * Delete an SCSP server block and any associated information
+ *
+ * Arguments:
+ * ssp pointer to a server control block to delete
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+scsp_server_delete(ssp)
+ Scsp_server *ssp;
+{
+ int i;
+ Scsp_dcs *dcsp, *next_dcs;
+ Scsp_cse *csep, *next_cse;
+
+ /*
+ * Unlink the server block from the chain
+ */
+ UNLINK(ssp, Scsp_server, scsp_server_head, ss_next);
+
+ /*
+ * Free the DCS blocks associated with the server
+ */
+ for (dcsp = ssp->ss_dcs; dcsp; dcsp = next_dcs) {
+ next_dcs = dcsp->sd_next;
+ scsp_dcs_delete(dcsp);
+ }
+
+ /*
+ * Free the entries in the server's summary cache
+ */
+ for (i = 0; i < SCSP_HASHSZ; i++) {
+ for (csep = ssp->ss_cache[i]; csep; csep = next_cse) {
+ next_cse = csep->sc_next;
+ free(csep);
+ }
+ }
+
+ /*
+ * Free the server block
+ */
+ free(ssp->ss_name);
+ free(ssp);
+}
+
+
+/*
+ * Get informtion about a server from the kernel
+ *
+ * Arguments:
+ * ssp pointer to the server block
+ *
+ * Returns:
+ * 0 server info is OK
+ * errno server is not ready
+ *
+ */
+int
+scsp_get_server_info(ssp)
+ Scsp_server *ssp;
+{
+ int i, 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;
+ Atm_addr_nsap *anp;
+
+ /*
+ * Make sure we're the server for the interface
+ */
+ if (!scsp_is_atmarp_server(ssp->ss_intf)) {
+ rc = EINVAL;
+ goto server_info_done;
+ }
+
+ /*
+ * Get the IP address and physical interface name
+ * associated with the network interface
+ */
+ bzero(&air, sizeof(struct atminfreq));
+ air.air_opcode = AIOCS_INF_NIF;
+ strcpy(air.air_netif_intf, ssp->ss_intf);
+ len = do_info_ioctl(&air, sizeof(struct air_netif_rsp));
+ if (len <= 0) {
+ rc = EIO;
+ goto server_info_done;
+ }
+ netif_rsp = (struct air_netif_rsp *)air.air_buf_addr;
+
+ ip_addr = (struct sockaddr_in *)&netif_rsp->anp_proto_addr;
+ if (ip_addr->sin_family != AF_INET ||
+ ip_addr->sin_addr.s_addr == 0) {
+ rc = EADDRNOTAVAIL;
+ goto server_info_done;
+ }
+
+ /*
+ * Get the MTU for the network interface
+ */
+ mtu = get_mtu(ssp->ss_intf);
+ if (mtu < 0) {
+ rc = EIO;
+ goto server_info_done;
+ }
+
+ /*
+ * Get the ATM address associated with the
+ * physical interface
+ */
+ bzero(&air, sizeof(struct atminfreq));
+ air.air_opcode = AIOCS_INF_INT;
+ strcpy(air.air_int_intf, netif_rsp->anp_phy_intf);
+ len = do_info_ioctl(&air, sizeof(struct air_int_rsp));
+ if (len <= 0) {
+ rc = EIO;
+ goto server_info_done;
+ }
+ intf_rsp = (struct air_int_rsp *)air.air_buf_addr;
+
+ /*
+ * Make sure we're running UNI signalling
+ */
+ if (intf_rsp->anp_sig_proto != ATM_SIG_UNI30 &&
+ intf_rsp->anp_sig_proto != ATM_SIG_UNI31 &&
+ intf_rsp->anp_sig_proto != ATM_SIG_UNI40) {
+ rc = EINVAL;
+ goto server_info_done;
+ }
+
+ /*
+ * Check the physical interface's state
+ */
+ if (intf_rsp->anp_sig_state != UNISIG_ACTIVE) {
+ rc = EHOSTDOWN;
+ goto server_info_done;
+ }
+
+ /*
+ * Make sure the interface's address is valid
+ */
+ if (intf_rsp->anp_addr.address_format != T_ATM_ENDSYS_ADDR &&
+ !(intf_rsp->anp_addr.address_format ==
+ T_ATM_E164_ADDR &&
+ intf_rsp->anp_subaddr.address_format ==
+ T_ATM_ENDSYS_ADDR)) {
+ rc = EINVAL;
+ goto server_info_done;
+ }
+
+ /*
+ * Find the selector byte value for the interface
+ */
+ for (i=0; i<strlen(ssp->ss_intf); i++) {
+ if (ssp->ss_intf[i] >= '0' &&
+ ssp->ss_intf[i] <= '9')
+ break;
+ }
+ sel = atoi(&ssp->ss_intf[i]);
+
+ /*
+ * Get configuration information associated with the
+ * physical interface
+ */
+ bzero(&air, sizeof(struct atminfreq));
+ air.air_opcode = AIOCS_INF_CFG;
+ strcpy(air.air_int_intf, netif_rsp->anp_phy_intf);
+ len = do_info_ioctl(&air, sizeof(struct air_cfg_rsp));
+ if (len <= 0) {
+ rc = EIO;
+ goto server_info_done;
+ }
+ cfg_rsp = (struct air_cfg_rsp *)air.air_buf_addr;
+
+ /*
+ * Update the server entry
+ */
+ bcopy(&ip_addr->sin_addr, ssp->ss_lsid.id, ssp->ss_id_len);
+ ssp->ss_lsid.id_len = ssp->ss_id_len;
+ ssp->ss_mtu = mtu + 8;
+ ATM_ADDR_COPY(&intf_rsp->anp_addr, &ssp->ss_addr);
+ ATM_ADDR_COPY(&intf_rsp->anp_subaddr, &ssp->ss_subaddr);
+ if (ssp->ss_addr.address_format == T_ATM_ENDSYS_ADDR) {
+ anp = (Atm_addr_nsap *)ssp->ss_addr.address;
+ anp->aan_sel = sel;
+ } else if (ssp->ss_addr.address_format == T_ATM_E164_ADDR &&
+ ssp->ss_subaddr.address_format ==
+ T_ATM_ENDSYS_ADDR) {
+ anp = (Atm_addr_nsap *)ssp->ss_subaddr.address;
+ anp->aan_sel = sel;
+ }
+ ssp->ss_media = cfg_rsp->acp_cfg.ac_media;
+ rc = 0;
+
+ /*
+ * Free dynamic data
+ */
+server_info_done:
+ if (netif_rsp)
+ free(netif_rsp);
+ if (intf_rsp)
+ free(intf_rsp);
+ if (cfg_rsp)
+ free(cfg_rsp);
+
+ return(rc);
+}
+
+
+/*
+ * Process a CA message
+ *
+ * Arguments:
+ * dcsp pointer to a DCS control block for the neighbor
+ * cap pointer to the CA part of the received message
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+scsp_process_ca(dcsp, cap)
+ Scsp_dcs *dcsp;
+ Scsp_ca *cap;
+{
+ Scsp_csa *csap, *next_csap;
+ Scsp_cse *csep;
+ Scsp_server *ssp = dcsp->sd_server;
+
+ /*
+ * Process CSAS records from the CA message
+ */
+ for (csap = cap->ca_csa_rec; csap; csap = next_csap) {
+ next_csap = csap->next;
+ SCSP_LOOKUP(ssp, &csap->key, csep);
+ if (!csep || (scsp_cmp_id(&csap->oid,
+ &csep->sc_oid) == 0 &&
+ csap->seq > csep->sc_seq)) {
+ /*
+ * CSAS entry not in cache or more
+ * up to date than cache, add it to CRL
+ */
+ UNLINK(csap, Scsp_csa, cap->ca_csa_rec, next);
+ LINK2TAIL(csap, Scsp_csa, dcsp->sd_crl, next);
+ }
+ }
+}
+
+
+/*
+ * Process a Cache Response message from a server
+ *
+ * Arguments:
+ * ssp pointer to the server block
+ * smp pointer to the message
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+scsp_process_cache_rsp(ssp, smp)
+ Scsp_server *ssp;
+ Scsp_if_msg *smp;
+{
+ int len;
+ Scsp_atmarp_msg *aap;
+ Scsp_cse *csep;
+
+ /*
+ * Loop through the message, processing each cache entry
+ */
+ len = smp->si_len;
+ len -= sizeof(Scsp_if_msg_hdr);
+ aap = &smp->si_atmarp;
+ while (len > 0) {
+ switch(smp->si_proto) {
+ case SCSP_ATMARP_PROTO:
+ /*
+ * If we already have an entry with this key,
+ * delete it
+ */
+ SCSP_LOOKUP(ssp, &aap->sa_key, csep);
+ if (csep) {
+ SCSP_DELETE(ssp, csep);
+ free(csep);
+ }
+
+ /*
+ * Copy the data from the server to a cache
+ * summary entry
+ */
+ csep = scsp_atmarp2cse(aap);
+
+ /*
+ * Point past this entry
+ */
+ len -= sizeof(Scsp_atmarp_msg);
+ aap++;
+ break;
+ case SCSP_NHRP_PROTO:
+ default:
+ /*
+ * Not implemented yet
+ */
+ return;
+ }
+
+ /*
+ * Add the new summary entry to the cache
+ */
+ SCSP_ADD(ssp, csep);
+ }
+}
+
+
+/*
+ * Propagate a CSA to all the DCSs in the server group except
+ * the one the CSA was received from
+ *
+ * Arguments:
+ * dcsp pointer to a the DCS the CSA came from
+ * csap pointer to a the CSA
+ *
+ * Returns:
+ * 0 success
+ * errno error encountered
+ *
+ */
+int
+scsp_propagate_csa(dcsp, csap)
+ Scsp_dcs *dcsp;
+ Scsp_csa *csap;
+{
+ int rc, ret_rc = 0;
+ Scsp_server *ssp = dcsp->sd_server;
+ Scsp_dcs *dcsp1;
+ Scsp_csa *csap1;
+
+ /*
+ * Check the hop count in the CSA
+ */
+ if (csap->hops <= 1)
+ return(0);
+
+ /*
+ * Pass the cache entry on to the server's other DCSs
+ */
+ for (dcsp1 = ssp->ss_dcs; dcsp1; dcsp1 = dcsp1->sd_next) {
+ /*
+ * Skip this DCS if it's the one we got
+ * the entry from
+ */
+ if (dcsp1 == dcsp)
+ continue;
+
+ /*
+ * Copy the CSA
+ */
+ csap1 = scsp_dup_csa(csap);
+
+ /*
+ * Decrement the hop count
+ */
+ csap1->hops--;
+
+ /*
+ * Send the copy of the CSA to the CA FSM for the DCS
+ */
+ rc = scsp_cafsm(dcsp1, SCSP_CAFSM_CACHE_UPD,
+ (void *) csap1);
+ if (rc)
+ ret_rc = rc;
+ }
+
+ return(ret_rc);
+}
+
+
+/*
+ * Update SCSP's cache given a CSA or CSAS
+ *
+ * Arguments:
+ * dcsp pointer to a DCS
+ * csap pointer to a CSA
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+scsp_update_cache(dcsp, csap)
+ Scsp_dcs *dcsp;
+ Scsp_csa *csap;
+{
+ Scsp_cse *csep;
+
+ /*
+ * Check whether we already have this in the cache
+ */
+ SCSP_LOOKUP(dcsp->sd_server, &csap->key, csep);
+
+ /*
+ * If we don't already have it and it's not being deleted,
+ * build a new cache summary entry
+ */
+ if (!csep && !csap->null) {
+ /*
+ * Get memory for a new entry
+ */
+ csep = calloc(1, sizeof(Scsp_cse));
+ if (csep == NULL)
+ scsp_mem_err("scsp_update_cache: sizeof(Scsp_cse)");
+
+ /*
+ * Fill out the new cache summary entry
+ */
+ csep->sc_seq = csap->seq;
+ csep->sc_key = csap->key;
+ csep->sc_oid = csap->oid;
+
+ /*
+ * Add the new entry to the cache
+ */
+ SCSP_ADD(dcsp->sd_server, csep);
+ }
+
+ /*
+ * Update or delete the entry
+ */
+ if (csap->null) {
+ /*
+ * The null flag is set--delete the entry
+ */
+ if (csep) {
+ SCSP_DELETE(dcsp->sd_server, csep);
+ free(csep);
+ }
+ } else {
+ /*
+ * Update the existing entry
+ */
+ csep->sc_seq = csap->seq;
+ csep->sc_oid = csap->oid;
+ }
+}
+
+
+/*
+ * Reconfigure SCSP
+ *
+ * Called as the result of a SIGHUP interrupt. Reread the
+ * configuration file and solicit the cache from the server.
+ *
+ * Arguments:
+ * none
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+scsp_reconfigure()
+{
+ int rc;
+ Scsp_server *ssp;
+
+ /*
+ * Log a message saying we're reconfiguring
+ */
+ scsp_log(LOG_ERR, "Reconfiguring ...");
+
+ /*
+ * Re-read the configuration file
+ */
+ rc = scsp_config(scsp_config_file);
+ if (rc) {
+ scsp_log(LOG_ERR, "Found %d error%s in configuration file",
+ rc, ((rc == 1) ? "" : "s"));
+ exit(1);
+ }
+
+ /*
+ * If a connection to a server is open, get the cache from
+ * the server
+ */
+ for (ssp = scsp_server_head; ssp; ssp = ssp->ss_next) {
+ if (ssp->ss_sock != -1) {
+ rc = scsp_send_cache_ind(ssp);
+ }
+ }
+}
diff --git a/usr.sbin/atm/scspd/scsp_timer.c b/usr.sbin/atm/scspd/scsp_timer.c
new file mode 100644
index 0000000..82e49fc
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_timer.c
@@ -0,0 +1,265 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * Timer processing
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netatm/queue.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+
+#include <errno.h>
+#include <libatm.h>
+#include <stdio.h>
+#include <syslog.h>
+
+#include "scsp_msg.h"
+#include "scsp_if.h"
+#include "scsp_var.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+/*
+ * Process an SCSP Open timeout
+ *
+ * The open timer is set when an attempt to open a VCC to a DCS fails.
+ * This routine will be called when the timer fires and will retry
+ * the open. Retries can continue indefinitely.
+ *
+ * Arguments:
+ * stp pointer to an SCSP timer block
+ *
+ * Returns:
+ * None
+ *
+ */
+void
+scsp_open_timeout(stp)
+ Harp_timer *stp;
+{
+ Scsp_dcs *dcsp;
+
+ /*
+ * Back off to start of DCS entry
+ */
+ dcsp = (Scsp_dcs *) ((caddr_t)stp -
+ (int)(&((Scsp_dcs *)0)->sd_open_t));
+
+ /*
+ * Retry the connection
+ */
+ if (scsp_dcs_connect(dcsp)) {
+ /*
+ * Connect failed -- we hope the error was temporary
+ * and set the timer to try again later
+ */
+ HARP_TIMER(&dcsp->sd_open_t, SCSP_Open_Interval,
+ scsp_open_timeout);
+ }
+}
+
+
+/*
+ * Process an SCSP Hello timeout
+ *
+ * The Hello timer fires every SCSP_HELLO_Interval seconds. This
+ * routine will notify the Hello FSM when the timer fires.
+ *
+ * Arguments:
+ * stp pointer to an SCSP timer block
+ *
+ * Returns:
+ * None
+ *
+ */
+void
+scsp_hello_timeout(stp)
+ Harp_timer *stp;
+{
+ Scsp_dcs *dcsp;
+
+ /*
+ * Back off to start of DCS entry
+ */
+ dcsp = (Scsp_dcs *) ((caddr_t)stp -
+ (int)(&((Scsp_dcs *)0)->sd_hello_h_t));
+
+ /*
+ * Call the Hello FSM
+ */
+ (void)scsp_hfsm(dcsp, SCSP_HFSM_HELLO_T, (Scsp_msg *)0);
+
+ return;
+}
+
+
+/*
+ * Process an SCSP receive timeout
+ *
+ * The receive timer is started whenever the Hello FSM receives a
+ * Hello message from its DCS. If the timer fires, it means that no
+ * Hello messages have been received in the DCS's Hello interval.
+ *
+ * Arguments:
+ * stp pointer to an SCSP timer block
+ *
+ * Returns:
+ * None
+ *
+ */
+void
+scsp_hello_rcv_timeout(stp)
+ Harp_timer *stp;
+{
+ Scsp_dcs *dcsp;
+
+ /*
+ * Back off to start of DCS entry
+ */
+ dcsp = (Scsp_dcs *) ((caddr_t)stp -
+ (int)(&((Scsp_dcs *)0)->sd_hello_rcv_t));
+
+ /*
+ * Call the Hello FSM
+ */
+ (void)scsp_hfsm(dcsp, SCSP_HFSM_RCV_T, (void *)0);
+
+ return;
+}
+
+
+/*
+ * Process an SCSP CA retransmit timeout
+ *
+ * Arguments:
+ * stp pointer to an SCSP timer block
+ *
+ * Returns:
+ * None
+ *
+ */
+void
+scsp_ca_retran_timeout(stp)
+ Harp_timer *stp;
+{
+ Scsp_dcs *dcsp;
+
+ /*
+ * Back off to start of DCS entry
+ */
+ dcsp = (Scsp_dcs *) ((caddr_t)stp -
+ (int)(&((Scsp_dcs *)0)->sd_ca_rexmt_t));
+
+ /*
+ * Call the CA FSM
+ */
+ (void)scsp_cafsm(dcsp, SCSP_CAFSM_CA_T, (void *)0);
+
+ return;
+}
+
+
+/*
+ * Process an SCSP CSUS retransmit timeout
+ *
+ * Arguments:
+ * stp pointer to an SCSP timer block
+ *
+ * Returns:
+ * None
+ *
+ */
+void
+scsp_csus_retran_timeout(stp)
+ Harp_timer *stp;
+{
+ Scsp_dcs *dcsp;
+
+ /*
+ * Back off to start of DCS entry
+ */
+ dcsp = (Scsp_dcs *) ((caddr_t)stp -
+ (int)(&((Scsp_dcs *)0)->sd_csus_rexmt_t));
+
+ /*
+ * Call the CA FSM
+ */
+ (void)scsp_cafsm(dcsp, SCSP_CAFSM_CSUS_T, (void *)0);
+
+ return;
+}
+
+
+/*
+ * Process an SCSP CSU Req retransmit timeout
+ *
+ * Arguments:
+ * stp pointer to an SCSP timer block
+ *
+ * Returns:
+ * None
+ *
+ */
+void
+scsp_csu_req_retran_timeout(stp)
+ Harp_timer *stp;
+{
+ Scsp_csu_rexmt *rxp;
+ Scsp_dcs *dcsp;
+
+ /*
+ * Back off to start of CSU Request retransmission entry
+ */
+ rxp = (Scsp_csu_rexmt *) ((caddr_t)stp -
+ (int)(&((Scsp_csu_rexmt *)0)->sr_t));
+ dcsp = rxp->sr_dcs;
+
+ /*
+ * Call the CA FSM
+ */
+ (void)scsp_cafsm(dcsp, SCSP_CAFSM_CSU_T, (void *)rxp);
+
+ return;
+}
diff --git a/usr.sbin/atm/scspd/scsp_var.h b/usr.sbin/atm/scspd/scsp_var.h
new file mode 100644
index 0000000..115ec42
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_var.h
@@ -0,0 +1,465 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * SCSP message formats
+ *
+ */
+
+#ifndef _SCSP_SCSP_VAR_H
+#define _SCSP_SCSP_VAR_H
+
+
+/*
+ * Protocol constants
+ */
+#define SCSP_Open_Interval 30
+#define SCSP_HELLO_Interval 3
+#define SCSP_HELLO_DF 3
+#define SCSP_CAReXmitInterval 3
+#define SCSP_CSUSReXmitInterval 3
+#define SCSP_CSA_HOP_CNT 3
+#define SCSP_CSUReXmitInterval 2
+#define SCSP_CSUReXmitMax 5
+
+
+/*
+ * Operational constants
+ */
+#define SCSPD_CONFIG "/etc/scspd.conf"
+#define SCSPD_DIR "/tmp"
+#define SCSPD_DUMP "/tmp/scspd.dump"
+#define SCSP_HASHSZ 19
+#define SCSPD_SOCK_NAME "SCSPD"
+
+
+/*
+ * HELLO finite state machine states
+ */
+#define SCSP_HFSM_DOWN 0
+#define SCSP_HFSM_WAITING 1
+#define SCSP_HFSM_UNI_DIR 2
+#define SCSP_HFSM_BI_DIR 3
+#define SCSP_HFSM_STATE_CNT SCSP_HFSM_BI_DIR + 1
+
+
+/*
+ * HELLO finite state machine events
+ */
+#define SCSP_HFSM_VC_ESTAB 0
+#define SCSP_HFSM_VC_CLOSED 1
+#define SCSP_HFSM_HELLO_T 2
+#define SCSP_HFSM_RCV_T 3
+#define SCSP_HFSM_RCVD 4
+#define SCSP_HFSM_EVENT_CNT SCSP_HFSM_RCVD + 1
+
+
+/*
+ * Cache Alignment finite state machine states
+ */
+#define SCSP_CAFSM_DOWN 0
+#define SCSP_CAFSM_NEG 1
+#define SCSP_CAFSM_MASTER 2
+#define SCSP_CAFSM_SLAVE 3
+#define SCSP_CAFSM_UPDATE 4
+#define SCSP_CAFSM_ALIGNED 5
+#define SCSP_CAFSM_STATE_CNT SCSP_CAFSM_ALIGNED + 1
+
+
+/*
+ * Cache Alignment finite state machine events
+ */
+#define SCSP_CAFSM_HELLO_UP 0
+#define SCSP_CAFSM_HELLO_DOWN 1
+#define SCSP_CAFSM_CA_MSG 2
+#define SCSP_CAFSM_CSUS_MSG 3
+#define SCSP_CAFSM_CSU_REQ 4
+#define SCSP_CAFSM_CSU_REPLY 5
+#define SCSP_CAFSM_CA_T 6
+#define SCSP_CAFSM_CSUS_T 7
+#define SCSP_CAFSM_CSU_T 8
+#define SCSP_CAFSM_CACHE_UPD 9
+#define SCSP_CAFSM_CACHE_RSP 10
+#define SCSP_CAFSM_EVENT_CNT SCSP_CAFSM_CACHE_RSP + 1
+
+
+/*
+ * Client Interface finite state machine states
+ */
+#define SCSP_CIFSM_NULL 0
+#define SCSP_CIFSM_SUM 1
+#define SCSP_CIFSM_UPD 2
+#define SCSP_CIFSM_ALIGN 3
+#define SCSP_CIFSM_STATE_CNT SCSP_CIFSM_ALIGN + 1
+
+
+/*
+ * Client Interface finite state machine events
+ */
+#define SCSP_CIFSM_CA_DOWN 0
+#define SCSP_CIFSM_CA_SUMM 1
+#define SCSP_CIFSM_CA_UPD 2
+#define SCSP_CIFSM_CA_ALIGN 3
+#define SCSP_CIFSM_SOL_RSP 4
+#define SCSP_CIFSM_UPD_REQ 5
+#define SCSP_CIFSM_UPD_RSP 6
+#define SCSP_CIFSM_CSU_REQ 7
+#define SCSP_CIFSM_CSU_REPLY 8
+#define SCSP_CIFSM_CSU_SOL 9
+#define SCSP_CIFSM_EVENT_CNT SCSP_CIFSM_CSU_SOL + 1
+
+
+/*
+ * Server connection states (not part of any FSM)
+ */
+#define SCSP_SS_NULL 0
+#define SCSP_SS_CFG 1
+#define SCSP_SS_ACTIVE 2
+
+
+/*
+ * Hash a cache key
+ *
+ * key pointer to an Scsp_ckey structure
+ */
+#define SCSP_HASH(key) scsp_hash((key))
+
+
+/*
+ * Add a cache summary entry to a client's cache summary
+ *
+ * cpp pointer to a server control block
+ * key pointer to an Scsp_cse structure
+ */
+#define SCSP_ADD(cpp, key) \
+{ \
+ Scsp_cse **c; \
+ c = &(cpp)->ss_cache[SCSP_HASH(&(key)->sc_key)]; \
+ LINK2TAIL((key), Scsp_cse, *c, sc_next); \
+}
+
+
+/*
+ * Delete a cache summary entry from a client's cache summary
+ *
+ * cpp pointer to a server control block
+ * s pointer to an Scsp_cse structure
+ */
+#define SCSP_DELETE(cpp, s) \
+{ \
+ Scsp_cse **c; \
+ c = &(cpp)->ss_cache[SCSP_HASH(&(s)->sc_key)]; \
+ UNLINK((s), Scsp_cse, *c, sc_next); \
+}
+
+
+/*
+ * Search a client's cache summary for a given key
+ *
+ * cpp pointer to a server control block
+ * key pointer to an Scsp_ckey structure to find
+ * s Scsp_cse structure pointer to be set
+ */
+#define SCSP_LOOKUP(cpp, key, s) \
+{ \
+ for ((s) = (cpp)->ss_cache[SCSP_HASH(key)]; \
+ (s); \
+ (s) = (s)->sc_next) { \
+ if (scsp_cmp_key((key), &(s)->sc_key) == 0) \
+ break; \
+ } \
+}
+
+
+/*
+ * SCSP pending connection control block
+ *
+ * The pending connection block is used to keep track of server
+ * connections which are open but haven't been identified yet.
+ */
+struct scsp_pending {
+ struct scsp_pending *sp_next;
+ int sp_sock;
+};
+typedef struct scsp_pending Scsp_pending;
+
+
+/*
+ * SCSP Server instance control block
+ */
+struct scsp_server {
+ struct scsp_server *ss_next; /* Server chain */
+ char *ss_name; /* Server name */
+ char ss_intf[IFNAMSIZ]; /* Interface */
+ Atm_media ss_media; /* Physical comm medium */
+ char ss_state; /* Server connection state */
+ u_long ss_pid; /* Protocol ID */
+ int ss_id_len; /* ID length */
+ int ss_ckey_len; /* Cache key length */
+ u_long ss_sgid; /* Server group ID */
+ u_long ss_fid; /* Family ID */
+ int ss_sock; /* Socket to client */
+ int ss_dcs_lsock; /* DCS listen socket */
+ Scsp_id ss_lsid; /* Local Server ID */
+ Atm_addr ss_addr; /* Local ATM addr */
+ Atm_addr ss_subaddr; /* Local ATM subaddr */
+ int ss_mtu; /* Interface MTU */
+ int ss_mark;
+ struct scsp_dcs *ss_dcs; /* Ptr to list of DCSs */
+ struct scsp_cse *ss_cache[SCSP_HASHSZ]; /* Client's cache */
+};
+typedef struct scsp_server Scsp_server;
+
+
+/*
+ * SCSP client cache summary entry control block
+ */
+struct scsp_cse {
+ struct scsp_cse *sc_next; /* Next on chain */
+ long sc_seq; /* CSA sequence no */
+ Scsp_ckey sc_key; /* Cache key */
+ Scsp_id sc_oid; /* Origin ID */
+};
+typedef struct scsp_cse Scsp_cse;
+
+
+/*
+ * CSU Request retransmission control block
+ */
+struct scsp_csu_rexmt {
+ struct scsp_csu_rexmt *sr_next; /* Next rexmit block */
+ struct scsp_dcs *sr_dcs; /* DCS block */
+ Scsp_csa *sr_csa; /* CSAs for rexmit */
+ Harp_timer sr_t; /* Rexmit timer */
+};
+typedef struct scsp_csu_rexmt Scsp_csu_rexmt;
+
+
+/*
+ * SCSP DCS control block
+ */
+struct scsp_dcs {
+ struct scsp_dcs *sd_next; /* DCS chain */
+ Scsp_server *sd_server; /* Local server */
+ Scsp_id sd_dcsid; /* DCS ID */
+ Atm_addr sd_addr; /* DCS ATM address */
+ Atm_addr sd_subaddr; /* DCS ATM subaddress */
+ int sd_sock; /* Socket to DCS */
+ Harp_timer sd_open_t; /* Open VCC retry timer */
+ int sd_hello_state; /* Hello FSM state */
+ int sd_hello_int; /* Hello interval */
+ int sd_hello_df; /* Hello dead factor */
+ int sd_hello_rcvd; /* Hello msg received */
+ Harp_timer sd_hello_h_t; /* Hello timer */
+ Harp_timer sd_hello_rcv_t; /* Hello receive timer */
+ int sd_ca_state; /* CA FSM state */
+ long sd_ca_seq; /* CA sequence number */
+ int sd_ca_rexmt_int; /* CA rexmit interval */
+ Scsp_msg *sd_ca_rexmt_msg; /* Saved CA msg */
+ Scsp_cse *sd_ca_csas; /* CSAS still to send */
+ Harp_timer sd_ca_rexmt_t; /* CA rexmit timer */
+ int sd_csus_rexmt_int; /* CSUS rexmit int */
+ Scsp_csa *sd_crl; /* Cache req list */
+ Scsp_msg *sd_csus_rexmt_msg; /* Saved CSUS msg */
+ Harp_timer sd_csus_rexmt_t; /* CSUS rexmit timer */
+ int sd_hops; /* CSA hop count */
+ Scsp_csa *sd_csu_ack_pend; /* CSUs to be ACKed */
+ Scsp_csa *sd_csu_ack; /* CSUs ACKed */
+ int sd_csu_rexmt_int; /* CSU Req rxmt time */
+ int sd_csu_rexmt_max; /* CSU Req rxmt limit */
+ Scsp_csu_rexmt *sd_csu_rexmt; /* CSU Req rxmt queue */
+ int sd_client_state; /* Client I/F state */
+};
+typedef struct scsp_dcs Scsp_dcs;
+
+/*
+ * Trace options
+ */
+#define SCSP_TRACE_HFSM 1 /* Trace the Hello FSM */
+#define SCSP_TRACE_CAFSM 2 /* Trace the CA FSM */
+#define SCSP_TRACE_CFSM 4 /* Trace the server I/F FSM */
+#define SCSP_TRACE_HELLO_MSG 8 /* Trace Hello protocol msgs */
+#define SCSP_TRACE_CA_MSG 16 /* Trace CA protocol msgs */
+#define SCSP_TRACE_IF_MSG 32 /* Trace server I/F msgs */
+
+
+/*
+ * Global variables
+ */
+extern char *prog;
+extern FILE *cfg_file;
+extern int parse_line;
+extern char *scsp_config_file;
+extern FILE *scsp_log_file;
+extern int scsp_log_syslog;
+extern Scsp_server *scsp_server_head;
+extern Scsp_pending *scsp_pending_head;
+extern int scsp_max_socket;
+extern int scsp_debug_mode;
+extern int scsp_trace_mode;
+extern FILE *scsp_trace_file;
+
+
+/*
+ * Executable functions
+ */
+/* scsp_cafsm.c */
+extern int scsp_cafsm __P((Scsp_dcs *, int, void *));
+
+/* scsp_config.c */
+extern int scsp_config __P((char *));
+extern int start_dcs __P((void));
+extern int finish_dcs __P((void));
+extern int set_dcs_addr __P((char *, char *));
+extern int set_dcs_ca_rexmit __P((int));
+extern int set_dcs_csus_rexmit __P((int));
+extern int set_dcs_csu_rexmit __P((int));
+extern int set_dcs_csu_rexmit_max __P((int));
+extern int set_dcs_hello_df __P((int));
+extern int set_dcs_hello_int __P((int));
+extern int set_dcs_hops __P((int));
+extern int set_dcs_id __P((char *));
+extern int set_intf __P((char *));
+extern int set_protocol __P((int));
+extern int set_server_group __P((int));
+extern int start_server __P((char *));
+extern int finish_server __P((void));
+extern int set_log_file __P((char *));
+
+/* scsp_config_lex.c */
+extern int yylex __P((void));
+
+/* scsp_config_parse.y */
+#if __STDC__
+extern void parse_error __P((const char *, ...));
+#else
+extern void parse_error __P((char *, va_alist));
+#endif
+
+/* 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_if_sock_write __P((int, Scsp_if_msg *));
+extern int scsp_server_read __P((Scsp_server *));
+extern int scsp_send_cache_ind __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 void scsp_process_cache_rsp __P((Scsp_server *,
+ Scsp_if_msg *));
+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..dcb432c
--- /dev/null
+++ b/usr.sbin/atm/scspd/scspd.8
@@ -0,0 +1,626 @@
+.\"
+.\" ===================================
+.\" HARP | Host ATM Research Platform
+.\" ===================================
+.\"
+.\"
+.\" This Host ATM Research Platform ("HARP") file (the "Software") is
+.\" made available by Network Computing Services, Inc. ("NetworkCS")
+.\" "AS IS". NetworkCS does not provide maintenance, improvements or
+.\" support of any kind.
+.\"
+.\" NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+.\" INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+.\" AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+.\" SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+.\" In no event shall NetworkCS be responsible for any damages, including
+.\" but not limited to consequential damages, arising from or relating to
+.\" any use of the Software or related support.
+.\"
+.\" Copyright 1994-1998 Network Computing Services, Inc.
+.\"
+.\" Copies of this Software may be made, however, the above copyright
+.\" notice must be reproduced on all copies.
+.\"
+.\" @(#) $FreeBSD$
+.\"
+.\"
+.Dd August 21, 1998
+.Dt SCSPD 8
+.Os
+.Sh NAME
+.Nm scspd
+.Nd "SCSP daemon"
+.Sh SYNOPSIS
+.Nm
+.Op Fl f Aq Ar cfg\-file
+.Op Fl d
+.Op Fl T Ns Aq Ar options
+.Sh DESCRIPTION
+.Nm Scspd
+is an implementation of the Server Cache Synchronization
+Protocol (SCSP) for the Host ATM Research Platform (HARP)
+networking software.
+.Nm Scspd
+synchronizes the cache(s) of server(s)
+running on a host with the caches of servers on remote hosts.
+SCSP is defined for a number of different protocols, but the present
+version of
+.Nm
+only supports ATMARP.
+.Pp
+By using
+.Nm
+and
+.Xr atmarpd 8 ,
+one can provide multiple
+ATMARP servers in a single ATM LIS.
+This might be useful, for example, when a LIS consists of a number of
+local-area ATM networks connected by long-distance links.
+Each local-area network could have its own ATMARP server, with all the
+servers' caches being synchronized by SCSP.
+Then, if a long-distance link fails, hosts on a local-area network
+will still have connectivity to other local hosts (since they all use
+the local ATMARP server); when the long-distance link is restored,
+SCSP will re-synchronize the servers' caches, restoring
+connectivity to remote hosts.
+Both
+.Nm
+and
+.Xr atmarpd 8
+must be running before any ATMARP
+cache synchronization can take place.
+.Pp
+.Nm Scspd
+implements SCSP as specified in RFC 2334,
+.%T "Server Cache Synchronization Protocol (SCSP)"
+and
+.Pa draft\-ietf\-ion\-scspd\-atmarpd\-00.txt ,
+.%T "A Distributed ATMARP Service using SCSP" .
+.Pp
+When
+.Nm
+starts, it parses its command line and puts
+itself into the background.
+.Sh TERMINOLOGY
+Some of the vocabulary associated with SCSP can be confusing.
+In this document, the following definitions are used:
+.Pp
+.Em "Client server"
+or
+.Em "local server"
+means the server running on
+the same host as
+.Nm
+whose cache is to be synchronized with that
+of one or more remote servers.
+When the word
+.Em server
+is used alone, it means
+.Em "client server" .
+.Pp
+.Em "Remote server"
+means a server running on some host other than
+the one where
+.Nm
+is running.
+.Pp
+.Em "Directly Connected Server"
+(DCS) means a remote server that
+.Nm
+communicates with directly.
+The remote server will also be running an implementation of SCSP.
+.Pp
+.Em "Cache Alignment"
+(CA) has two meanings.
+The Cache Alignment protocol is a part of the SCSP protocol
+specification, and the Cache Alignment finite state machine (FSM)
+is a finite state machine that implements the Cache Alignment
+protocol.
+.Sh OPTIONS
+The command-line options are:
+.Bl -tag -width "-f <cfg\-file>"
+.It Fl f Aq Ar cfg\-file
+Specifies the name of the configuration file.
+If this option is not specified,
+.Nm
+looks for the
+file
+.Pa /etc/scspd.conf .
+.It Fl d
+Specifies that
+.Nm
+is to be run in debug mode.
+In debug mode, the daemon is not put into the background.
+Log messages are written to standard output instead of to
+the log file specified in the configuration file.
+.It Fl T Ns Aq Ar options
+Specifies that
+.Nm
+will trace specified events and messages
+as it executes.
+The
+.Fl T
+flag is followed by one or more of the following
+options:
+.Pp
+.Bl -tag -width 4n -compact
+.It Cm c
+trace
+.Nm Ns 's
+CA Finite State Machine (FSM),
+.It Cm h
+trace
+.Nm Ns 's
+Hello FSM,
+.It Cm i
+trace
+.Nm Ns 's
+Client Interface FSM,
+.It Cm C
+trace CA, CSUS, CSU Request, and CSU Reply messages,
+.It Cm H
+trace Hello messages,
+.It Cm I
+trace interface messages to and from
+.Nm Ns 's
+clients.
+.El
+.El
+.Sh CONFIGURATION
+The configuration file consists of a sequence of configuration
+statements.
+These statements specify information about the servers,
+both local and remote, whose
+caches are to be synchronized by
+.Nm .
+RFC 2334,
+.%T "Server Cache Synchronization Protocol (SCSP)"
+and
+.Pa draft\-ietf\-ion\-scspd\-atmarpd\-00.txt ,
+.%T "A Distributed ATMARP Service using SCSP"
+will be valuable in understanding how to configure
+.Nm .
+.Pp
+A configuration statement other than a comment is terminated by a
+semicolon.
+Some statements contain blocks, delimited by braces
+.No ( Dq Li {
+and
+.Dq Li } ) .
+Configuration statement keywords are not case-sensitive,
+but some parameters (e.g. interface names) are.
+Configuration statements can span multiple lines.
+.Ss Comments
+Three types of comments are allowed:
+.Bl -hang
+.It Sy "# comments" :
+any characters from
+.Dq Li #
+to the end of the line are ignored.
+.It Sy "C comments" :
+any characters between
+.Dq Li /*
+and
+.Dq Li */
+are ignored.
+.It Sy "C++ comments" :
+any characters from
+.Dq Li //
+to the end of the line are ignored.
+.El
+.Ss Statements
+The configuration statements recognized by
+.Nm
+are:
+.Bd -literal
+Server <name> {
+ Protocol <protocol ID>;
+ Netif <if_name>;
+ ServerGroupID <ID>;
+ FamilyID <ID>;
+ DCS {
+ ATMaddr <ATM address>;
+ ID <host>;
+ CAReXmitInt <int>;
+ CSUSReXmitInt <int>;
+ CSUReXmitInt <int>;
+ CSUReXmitMax <cnt>;
+ HelloDead <cnt>;
+ HelloInt <int>;
+ Hops <cnt>;
+ };
+};
+
+Log {
+ File <file name>;
+ Syslog;
+};
+.Ed
+.Pp
+Where a host address needs to be specified in the configuration file,
+either a DNS name or an IP address in dotted decimal format can
+be used.
+.Pp
+ATM addresses are specified as strings of hex digits, with an
+optional leading
+.Dq Li 0x .
+Fields within the address may be separated by periods, but periods
+are for readability only and are ignored.
+ATM addresses are 20 bytes long.
+The full address, including any leading zeroes, must be given.
+For example:
+.Pp
+.Dl "0x47.0005.80.ffe100.0000.f21a.0170.0020481a0170.00"
+.Ss "Server Statement"
+The
+.Ic server
+statement specifies a client server whose cache
+to be synchronized with the caches of other servers
+running on remote hosts.
+There will be one
+.Ic server
+statement in the configuration file
+for each client server whose cache is to be synchronized by
+.Nm .
+The format of the
+.Ic server
+statement is:
+.Bd -ragged -offset indent
+.Ic Server
+.Aq Ar name
+{
+.Aq Ar statements
+};
+.Ed
+.Pp
+A
+.Ar name
+must be specified on the
+.Ic server
+statement, but it is
+not used by
+.Nm .
+It is expected to give a brief description of the server's purpose.
+.Pp
+The
+.Ic server
+statement has several sub-statements
+that specify the details of the
+.Nm Ns 's
+configuration.
+They are:
+.Bl -tag -width indent
+.It Ic Protocol Cm ATMARP ;
+The only protocol supported by the current version of
+.Nm
+is
+.Cm ATMARP .
+The
+.Ic protocol
+statement must always be specified.
+.It Ic Netif Aq Ar intf ;
+The
+.Ic netif
+statement specifies the name of the ATM network
+interface on which a client server is providing service.
+The
+.Ic netif
+statement must always be specified.
+.It Ic ServerGroupID Aq Ar ID ;
+The
+.Ic ServerGroupID
+statement specifies an identifier for the
+group of servers being synchronized by
+.Nm .
+The
+.Ar ID
+is specified as a decimal number in the range 0 - 65,535.
+The server group ID must be the same for all servers whose caches
+are being synchronized by an SCSP session.
+That is, the server group ID for a host must be the same for all
+Directly Connected Servers (DCSs) pointed to within a
+.Ic server
+statement.
+The
+.Ic ServerGroupID
+statement must always be specified.
+.It Ic FamilyID Aq Ar ID ;
+The
+.Ic familyID
+statement specifies an identifier for a family
+of parallel SCSP sessions running between a group of hosts (i.e. a
+set of SCSP sessions with different protocol IDs but the same set
+of servers).
+The
+.Ar ID
+is specified as a decimal number in the range 0 - 65,535.
+The family ID is currently not used by
+.Nm .
+.El
+.Ss "DCS Statement"
+The
+.Ic DCS
+statement is a sub-statement of the
+.Ic server
+statement
+that specifies the characteristics of a Directly Connected Server (DCS).
+The
+.Ic server
+statement will have one
+.Ic DCS
+statement for
+each DCS that
+.Nm
+is to exchange information with.
+The
+.Ic DCS
+statement has a number of sub-statements that specify the
+details of the configuration for the DCS.
+They are:
+.Bl -tag -width indent
+.It Ic ATMaddr Aq Ar ATM\ address ;
+The
+.Ic ATMaddr
+statement specifies the ATM address of the DCS.
+The
+.Ic ATMaddr
+statement must always be specified.
+.It Ic ID Aq Ar host ;
+The
+.Ic ID
+statement specifies the SCSP identifier of the DCS.
+For ATMARP, the ID is the IP address or DNS name associated with the
+ATM interface of the DCS.
+The
+.Ic ID
+statement must always be specified.
+.It Ic CAReXmitInt Aq Ar int ;
+The
+.Ic CAReXmitInt
+statement specifies the interval that is
+allowed to elapse between retransmissions of CA messages.
+If a CA message is sent and an acknowledgement is not received within
+.Ic CAReXmitInt
+seconds, the message will be retransmitted.
+The default value for
+.Ic CAReXmitInt
+is 3 seconds.
+.It Ic CSUSReXmitInt Aq Ar int ;
+The
+.Ic CSUSReXmitInt
+statement specifies the interval that is
+allowed to elapse between retransmissions of CSU Solicit messages.
+When a CSUS message is sent, any Cache State Advertisements (CSAs)
+requested by the CSUS that have
+not been received within
+.Ic CSUSReXmitInt
+seconds will be requested
+again by another CSUS message.
+The default value for
+.Ic CSUSReXmitInt
+is 3 seconds.
+Be careful not to confuse
+.Ic CSUSReXmitInt
+and
+.Ic CSUReXmitInt .
+.It Ic CSUReXmitInt Aq Ar int ;
+The
+.Ic CSUReXmitInt
+statement specifies the interval that is
+allowed to elapse between retransmissions of CSU Request messages.
+When a CSU Request message is sent, any CSAs that are not acknowledged
+by a CSU Reply message within
+.Ic CSUReXmitInt
+seconds will
+be retransmitted.
+The default value for
+.Ic CSUReXmitInt
+is 2 seconds.
+Be careful not to confuse
+.Ic CSUReXmitInt
+and
+.Ic CSUSReXmitInt .
+.It Ic CSUReXmitMax Aq Ar cnt ;
+The
+.Ic CSUReXmitMax
+statement specifies the number of times that
+a CSA will be retransmitted as described above before SCSP gives up
+on the CSA and discards it.
+The default value for
+.Ic CSUReXmitMax
+is 5.
+.It Ic HelloDead Aq Ar cnt ;
+The
+.Ic HelloDead
+statement specifies the Hello Dead Factor that
+will be sent to the DCS in Hello messages.
+A
+.Dq "DCS down"
+condition will be detected when nothing is received from
+a DCS in
+.Ic HelloDead No * Ic HelloInt
+seconds.
+The default value for
+.Ic HelloDead
+is 3.
+.It Ic HelloInt Aq Ar int ;
+The
+.Ic HelloInt
+statement specifies the Hello Interval that
+will be sent to the DCS in Hello messages.
+The default value for
+.Ic HelloInt
+is 3 seconds.
+.It Ic Hops Aq Ar cnt ;
+The
+.Ic Hops
+statement specifies the number of hops (DCS to DCS)
+that will be specified in CSAs originating from the local server.
+This number must be at least as large as the diameter of the
+server group.
+That is, it must be large enough for a CSA to be propagated from
+server to server all the way across the server group.
+The default value for
+.Ic Hops
+is 3.
+.El
+.Ss "Log Statement"
+The
+.Ic log
+statement specifies how
+.Nm
+is to log
+information about its operation.
+.Nm Scspd
+can write log information to a file, to the system log,
+or both.
+.Bl -tag -width indent
+.It Ic File Aq Ar file\ name ;
+The
+.Ic file
+statement specifies that
+.Nm
+is to write
+its log messages to the named file.
+Log messages will be appended to the end of the file if
+it already exists.
+.It Ic Syslog ;
+The
+.Ic syslog
+statement specifies that
+.Nm
+is to write
+its log messages to the syslog facility.
+.Nm Scspd
+writes its messages to syslog with a facility code
+of
+.Dv LOG_DAEMON .
+.El
+.Pp
+If no
+.Ic log
+statement is specified,
+.Nm
+writes log
+messages to the system log.
+If both
+.Ic file
+and
+.Ic syslog
+are specified,
+.Nm
+will
+write log messages to both the named file and the system log.
+.Ss Examples
+An example of a simple configuration file for
+.Nm
+might be:
+.Bd -literal -offset indent
+server atmarp_ni0 {
+ protocol ATMARP;
+ netif ni0;
+ ServerGroupID 23;
+ DCS {
+ ID 10.1.1.2;
+ ATMaddr 0x47.0005.80.ffdc00.0000.0002.0001.002048061de7.00;
+ hops 2;
+ };
+};
+.Ed
+.Pp
+This configuration would synchronize the cache of the ATMARP server
+operating on network interface ni0 with the cache of a second server
+running on a host whose IP address is 10.1.1.2.
+Log messages would be written to the system log.
+.Sh SIGNAL PROCESSING
+The following signals can be used to control
+.Nm :
+.Bl -tag -width indent
+.It Dv SIGHUP
+Reread the configuration file and restart
+.Nm .
+.It Dv SIGINT
+Dump debugging information to a file.
+When it receives a
+.Dv SIGINT
+signal,
+.Nm
+dumps a summary of
+its control blocks to a text file (see
+.Sx FILES ) .
+.El
+.Sh FILES
+.Bl -tag -width indent
+.It Pa /etc/scspd.conf
+.Nm Scspd
+default configuration file name.
+A different file name can be specified with the
+.Fl f
+command-line
+option.
+.It Xo
+.Sm off
+.Pa /tmp/scspd.
+.Aq Ar pid
+.Pa \&.
+.Aq Ar seq
+.Pa .out
+.Sm on
+.Xc
+Debugging information dump file name.
+.Nm Scspd
+writes a summary of its control blocks to this file
+when it receives a
+.Dv SIGINT
+signal.
+.Aq Ar pid
+is the process ID of the daemon and
+.Aq Ar seq
+is a sequence
+number which is incremented every time a dump is taken.
+.It Xo
+.Sm off
+.Pa /tmp/scspd.
+.Aq Ar pid
+.Pa .trace
+.Sm on
+.Xc
+Trace file.
+.Nm Scspd
+writes trace information to this file if the
+.Fl T
+option is specified on the command line.
+.El
+.Sh SEE ALSO
+.Xr atm 8 ,
+.Xr atmarpd 8
+.Rs
+.%O "RFC 2334"
+.%T "Server Cache Synchronization Protocol (SCSP)"
+.Re
+.Rs
+.%O "draft\-ietf\-ion\-scsp\-atmarpd\-00.txt"
+.%T "A Distributed ATMARP Service Using SCSP"
+.Re
+.Sh BUGS
+If
+.Nm
+terminates and is restarted, there will be a period of
+instability while previously-synchronized cache entries time out and are
+refreshed.
+.Pp
+Please report any bugs to
+.Aq harp\-bugs@magic.net .
+.Sh COPYRIGHT
+Copyright (c) 1994-1998, Network Computing Services, Inc.
+.Sh AUTHORS
+.An John Cavanaugh ,
+Network Computing Services, Inc.
+.An Mike Spengler ,
+Network Computing Services, Inc.
+.An Joe Thomas ,
+Network Computing Services, Inc.
+.Sh ACKNOWLEDGMENTS
+This software was developed with the support of the Defense
+Advanced Research Projects Agency (DARPA).
diff --git a/usr.sbin/atm/scspd/scspd.c b/usr.sbin/atm/scspd/scspd.c
new file mode 100644
index 0000000..fbbf75d
--- /dev/null
+++ b/usr.sbin/atm/scspd/scspd.c
@@ -0,0 +1,546 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * SCSP server daemon main line code
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/ttycom.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netatm/queue.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <libatm.h>
+#include <paths.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "scsp_msg.h"
+#include "scsp_if.h"
+#include "scsp_var.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+/*
+ * Global variables
+ */
+char *prog;
+char *scsp_config_file = SCSPD_CONFIG;
+FILE *scsp_log_file = (FILE *)0;
+int scsp_log_syslog = 0;
+Scsp_server *scsp_server_head = (Scsp_server *)0;
+Scsp_pending *scsp_pending_head = (Scsp_pending *)0;
+int scsp_max_socket = -1;
+int scsp_debug_mode = 0;
+int scsp_trace_mode = 0;
+
+
+/*
+ * Local variables
+ */
+static int scsp_hup_signal = 0;
+static int scsp_int_signal = 0;
+
+
+/*
+ * SIGHUP signal handler
+ *
+ * Arguments:
+ * sig signal number
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+scsp_sighup(sig)
+ int sig;
+{
+ /*
+ * Flag the signal
+ */
+ scsp_hup_signal = 1;
+}
+
+
+/*
+ * SIGINT signal handler
+ *
+ * Arguments:
+ * sig signal number
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+scsp_sigint(sig)
+ int sig;
+{
+ /*
+ * Flag the signal
+ */
+ scsp_int_signal = 1;
+}
+
+
+/*
+ * Process command line parameters
+ *
+ * Arguments:
+ * argc number of command-line arguments
+ * argv list of pointers to command-line arguments
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+initialize(argc, argv)
+ int argc;
+ char **argv;
+{
+ int i;
+ char *cp;
+
+ /*
+ * Save program name, ignoring any path components
+ */
+ if ((prog = (char *)strrchr(argv[0], '/')) != NULL)
+ prog++;
+ else
+ prog = argv[0];
+
+ /*
+ * Make sure we're being invoked by the super user
+ */
+ i = getuid();
+ if (i != 0) {
+ fprintf(stderr, "%s: You must be root to run this program\n",
+ prog);
+ exit(1);
+ }
+
+ /*
+ * Check for command-line options
+ */
+ for (i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-d") == 0) {
+ /*
+ * -d option -- set debug mode
+ */
+ scsp_debug_mode = 1;
+ } else if (strcmp(argv[i], "-f") == 0) {
+ /*
+ * -f option -- set config file name
+ */
+ i++;
+ if (i >= argc) {
+ fprintf(stderr, "%s: Configuration file name missing\n",
+ prog);
+ exit(1);
+ }
+ scsp_config_file = argv[i];
+ } else if (strncmp(argv[i], "-T", 2) == 0) {
+ /*
+ * -T option -- trace options
+ */
+ for (cp = &argv[i][2]; *cp; cp++) {
+ if (*cp == 'c')
+ scsp_trace_mode |= SCSP_TRACE_CAFSM;
+ else if (*cp == 'h')
+ scsp_trace_mode |= SCSP_TRACE_HFSM;
+ else if (*cp == 'i')
+ scsp_trace_mode |= SCSP_TRACE_CFSM;
+ else if (*cp == 'C')
+ scsp_trace_mode |= SCSP_TRACE_CA_MSG;
+ else if (*cp == 'H')
+ scsp_trace_mode |= SCSP_TRACE_HELLO_MSG;
+ else if (*cp == 'I')
+ scsp_trace_mode |= SCSP_TRACE_IF_MSG;
+ else
+ fprintf(stderr, "Invalid trace specification '%c' ignored\n",
+ *cp);
+ }
+ } else {
+ /*
+ * Error -- unrecognized option
+ */
+ fprintf(stderr, "%s: Unrecognized option \"%s\"\n",
+ prog, argv[i]);
+ exit(1);
+ }
+ }
+}
+
+
+/*
+ * Daemon housekeeping
+ *
+ * Arguments:
+ * None
+ *
+ * Returns:
+ * None
+ *
+ */
+static void
+start_daemon()
+
+{
+ int dpid, fd, file_count, rc;
+
+ /*
+ * Ignore selected signals
+ */
+#ifdef SIGTTOU
+ signal(SIGTTOU, SIG_IGN);
+#endif
+#ifdef SIGTTIN
+ signal(SIGTTIN, SIG_IGN);
+#endif
+#ifdef SIGTSTP
+ signal(SIGTSTP, SIG_IGN);
+#endif
+#ifdef SIGPIPE
+ signal(SIGPIPE, SIG_IGN);
+#endif
+
+
+ /*
+ * Don't put the daemon into the background if
+ * we're in debug mode
+ */
+ if (scsp_debug_mode)
+ goto daemon_bypass;
+
+ /*
+ * Put the daemon into the background
+ */
+ dpid = fork();
+ if (dpid < 0) {
+ scsp_log(LOG_ERR, "fork failed");
+ abort();
+ }
+ if (dpid > 0) {
+ /*
+ * This is the parent process--just exit and let
+ * the daughter do all the work
+ */
+ exit(0);
+ }
+
+ /*
+ * Disassociate from any controlling terminal
+ */
+ rc = setpgrp(0, getpid());
+ if (rc <0) {
+ scsp_log(LOG_ERR, "can't change process group");
+ exit(1);
+ }
+ fd = open(_PATH_TTY, O_RDWR);
+ if (fd >= 0) {
+ ioctl(fd, TIOCNOTTY, (char *)0);
+ close(fd);
+ }
+
+ /*
+ * Close all open file descriptors
+ */
+ file_count = getdtablesize();
+ for (fd=0; fd<file_count; fd++) {
+ close(fd);
+ }
+
+ /*
+ * Set up timers
+ */
+daemon_bypass:
+ init_timer();
+
+ /*
+ * Move to a safe directory
+ */
+ chdir(SCSPD_DIR);
+
+ /*
+ * Clear the file mode creation mask
+ */
+ umask(0);
+
+
+ /*
+ * Set up signal handlers
+ */
+ if (signal(SIGHUP, scsp_sighup) == SIG_ERR) {
+ scsp_log(LOG_ERR, "SIGHUP signal setup failed");
+ exit(1);
+ }
+
+ if (signal(SIGINT, scsp_sigint) == SIG_ERR) {
+ scsp_log(LOG_ERR, "SIGINT signal setup failed");
+ exit(1);
+ }
+
+ /*
+ * Set up syslog for error logging
+ */
+ if (scsp_log_syslog || !scsp_log_file) {
+ openlog(prog, LOG_PID | LOG_CONS, LOG_DAEMON);
+ }
+ scsp_log(LOG_INFO, "Starting SCSP daemon");
+}
+
+
+/*
+ * Main line code
+ *
+ * Process command line parameters, read configuration file, connect
+ * to configured clients, process data from DCSs.
+ *
+ * Arguments:
+ * argc number of command-line arguments
+ * argv list of pointers to command-line arguments
+ *
+ * Returns:
+ * none
+ *
+ */
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+
+{
+ int i, rc, scsp_server_lsock;
+ Scsp_server *ssp;
+ Scsp_dcs *dcsp;
+ Scsp_pending *next_psp, *psp;
+ fd_set read_set, write_set, except_set;
+
+ /*
+ * Process command line arguments
+ */
+ initialize(argc, argv);
+
+ /*
+ * Put the daemon into the background
+ */
+ start_daemon();
+
+ /*
+ * Process configuration file
+ */
+ rc = scsp_config(scsp_config_file);
+ if (rc) {
+ scsp_log(LOG_ERR, "Found %d error%s in configuration file",
+ rc, ((rc == 1) ? "" : "s"));
+ exit(1);
+ }
+
+ /*
+ * Open the trace file if we need one
+ */
+ if (scsp_trace_mode) {
+ scsp_open_trace();
+ }
+
+ /*
+ * Listen for connections from clients
+ */
+ scsp_server_lsock = scsp_server_listen();
+ if (scsp_server_lsock == -1) {
+ scsp_log(LOG_ERR, "server listen failed");
+ abort();
+ }
+
+ /*
+ * Main program loop -- we wait for:
+ * a server listen to complete
+ * a DCS listen to complete
+ * a DCS connect to complete
+ * data from a server
+ * data from a DCS
+ */
+ while (1) {
+ /*
+ * Set up the file descriptor sets and select to wait
+ * for input
+ */
+ FD_ZERO(&read_set);
+ FD_ZERO(&write_set);
+ FD_ZERO(&except_set);
+ FD_SET(scsp_server_lsock, &read_set);
+ for (ssp = scsp_server_head; ssp; ssp = ssp->ss_next) {
+ if (ssp->ss_dcs_lsock != -1)
+ FD_SET(ssp->ss_dcs_lsock, &read_set);
+ if (ssp->ss_sock != -1)
+ FD_SET(ssp->ss_sock, &read_set);
+ for (dcsp = ssp->ss_dcs; dcsp;
+ dcsp = dcsp->sd_next) {
+ if (dcsp->sd_sock != -1) {
+ if (dcsp->sd_hello_state ==
+ SCSP_HFSM_DOWN )
+ FD_SET(dcsp->sd_sock,
+ &write_set);
+ else
+ FD_SET(dcsp->sd_sock,
+ &read_set);
+ }
+ }
+ }
+ for (psp = scsp_pending_head; psp; psp = psp->sp_next) {
+ FD_SET(psp->sp_sock, &read_set);
+ }
+ rc = select(scsp_max_socket + 1, &read_set,
+ &write_set, &except_set,
+ (struct timeval *)0);
+ if (rc < 0) {
+ /*
+ * Select error--check for possible signals
+ */
+ if (harp_timer_exec) {
+ /*
+ * Timer tick--process it
+ */
+ timer_proc();
+ continue;
+ } else if (scsp_hup_signal) {
+ /*
+ * SIGHUP signal--reconfigure
+ */
+ scsp_hup_signal = 0;
+ scsp_reconfigure();
+ continue;
+ } else if (scsp_int_signal) {
+ /*
+ * SIGINT signal--dump control blocks
+ */
+ print_scsp_dump();
+ scsp_int_signal = 0;
+ continue;
+ } else if (errno == EINTR) {
+ /*
+ * EINTR--just ignore it
+ */
+ continue;
+ } else {
+ /*
+ * Other error--this is a problem
+ */
+ scsp_log(LOG_ERR, "Select failed");
+ abort();
+ }
+ }
+
+ /*
+ * Check the read set for connections from servers
+ */
+ if (FD_ISSET(scsp_server_lsock, &read_set)) {
+ FD_CLR(scsp_server_lsock, &read_set);
+ rc = scsp_server_accept(scsp_server_lsock);
+ }
+
+ /*
+ * Check the write set for new connections to DCSs
+ */
+ for (i = 0; i <= scsp_max_socket; i++) {
+ if (FD_ISSET(i, &write_set)) {
+ FD_CLR(i, &write_set);
+ if ((dcsp = scsp_find_dcs(i)) != NULL) {
+ rc = scsp_hfsm(dcsp,
+ SCSP_HFSM_VC_ESTAB,
+ (Scsp_msg *)0);
+ }
+ }
+ }
+
+ /*
+ * Check the read set for connections from DCSs
+ */
+ for (ssp = scsp_server_head; ssp; ssp = ssp->ss_next) {
+ if (ssp->ss_dcs_lsock != -1 &&
+ FD_ISSET(ssp->ss_dcs_lsock,
+ &read_set)) {
+ FD_CLR(ssp->ss_dcs_lsock, &read_set);
+ dcsp = scsp_dcs_accept(ssp);
+ if (dcsp) {
+ rc = scsp_hfsm(dcsp,
+ SCSP_HFSM_VC_ESTAB,
+ (Scsp_msg *)0);
+ }
+ }
+ }
+
+ /*
+ * Check the read set for data from pending servers
+ */
+ for (psp = scsp_pending_head; psp; psp = next_psp) {
+ next_psp = psp->sp_next;
+ if (FD_ISSET(psp->sp_sock, &read_set)) {
+ FD_CLR(psp->sp_sock, &read_set);
+ rc = scsp_pending_read(psp);
+ }
+ }
+
+ /*
+ * Check the read set for data from servers or DCSs
+ */
+ for (i = 0; i <= scsp_max_socket; i++) {
+ if (FD_ISSET(i, &read_set)) {
+ if ((ssp = scsp_find_server(i)) != NULL) {
+ rc = scsp_server_read(ssp);
+ } else if ((dcsp = scsp_find_dcs(i)) != NULL) {
+ rc = scsp_dcs_read(dcsp);
+ }
+ }
+ }
+ }
+}
diff --git a/usr.sbin/boot0cfg/Makefile b/usr.sbin/boot0cfg/Makefile
new file mode 100644
index 0000000..ed5f5f6
--- /dev/null
+++ b/usr.sbin/boot0cfg/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+PROG= boot0cfg
+WARNS?= 2
+MAN= 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..0937f68
--- /dev/null
+++ b/usr.sbin/boot0cfg/boot0cfg.8
@@ -0,0 +1,174 @@
+.\" Copyright (c) 1999 Robert Nordier
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+.\" OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+.\" OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+.\" BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+.\" WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+.\" OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+.\" EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd February 21, 1999
+.Dt BOOT0CFG 8
+.Os
+.Sh NAME
+.Nm boot0cfg
+.Nd boot manager installation/configuration utility
+.Sh SYNOPSIS
+.Nm
+.Op Fl Bv
+.Op Fl b Ar boot0
+.Op Fl d Ar drive
+.Op Fl f Ar file
+.Op Fl m Ar mask
+.Op Fl o Ar options
+.Op Fl s Ar slice
+.Op Fl t Ar ticks
+.Ar disk
+.Sh DESCRIPTION
+The
+.Fx
+.Sq boot0
+boot manager permits the operator to select from which disk and
+slice an i386 machine (PC) is booted.
+.Pp
+Note that what are referred to here as
+.Dq slices
+are typically called
+.Dq partitions
+in
+.No non- Ns Bx
+documentation relating to the PC.
+Typically, only non-removable disks are sliced.
+.Pp
+The
+.Nm
+utility optionally installs the
+.Sq boot0
+boot manager on the specified
+.Ar disk ;
+and allows various operational parameters to be configured.
+.Pp
+On PCs, a boot manager typically occupies sector 0 of a disk, which is
+known as the Master Boot Record (MBR).
+The MBR contains both code (to which control is passed by the PC BIOS)
+and data (an embedded table of defined slices).
+.Pp
+The options are:
+.Bl -tag -width indent
+.It Fl B
+Install the
+.Sq boot0
+boot manager.
+This option causes MBR code to be replaced, without
+affecting the embedded slice table.
+.It Fl b Ar boot0
+Specify which
+.Sq boot0
+image to use.
+The default is
+.Pa /boot/boot0 .
+.It Fl d Ar drive
+Specify the drive number used by the PC BIOS in referencing the drive
+which contains the specified
+.Ar disk .
+Typically this will be 0x80 for the first hard drive, 0x81 for the
+second hard drive, and so on; however any integer between 0 and 0xff
+is acceptable here.
+.It Fl f Ar file
+Specify that a backup copy of the preexisting MBR should be written to
+.Ar file .
+This file is created if it does not exist, and replaced if it does.
+.It Fl m Ar mask
+Specify slices to be enabled/disabled, where
+.Ar mask
+is an integer between 0 (no slices enabled) and 0xf (all four slices
+enabled).
+.It Fl o Ar options
+A comma-separated string of any of the following options may be
+specified (with
+.Dq no
+prepended as necessary):
+.Bl -tag -width indent
+.It packet
+Use the disk packet (BIOS Int 0x13 extensions) interface, as
+as opposed to the legacy (CHS) interface, when doing disk I/O.
+This allows booting above cylinder 1023, but requires specific
+BIOS support.
+The default is
+.Sq nopacket .
+.It setdrv
+Forces the drive containing the disk to be referenced using drive
+number definable by means of the -d option.
+The default is
+.Sq nosetdrv .
+.It update
+Allow the MBR to be updated by the boot manager.
+(The MBR may be updated to flag slices as
+.Sq active ,
+and to save slice selection information.)
+This is the default; a
+.Sq noupdate
+option causes the MBR to be treated as read-only.
+.El
+.It Fl s Ar slice
+Set the default boot selection to
+.Ar slice .
+Values between 1 and 4 refer to slices; a value of 5 refers to the
+option of booting from a second disk.
+.It Fl t Ar ticks
+Set the timeout value to
+.Ar ticks .
+(There are approximately 18.2 ticks per second.)
+.It Fl v
+Verbose: display information about the slices defined, etc.
+.El
+.Sh FILES
+.Bl -tag -width /boot/boot0 -compact
+.It Pa /boot/boot0
+The default
+.Sq boot0
+image
+.El
+.Sh EXAMPLES
+The following is an example of a typical usage
+of the
+.Nm
+command to affect the next boot:
+.Pp
+.Dl "boot0cfg -s 2 ad0"
+.Sh DIAGNOSTICS
+.Ex -std
+.Sh SEE ALSO
+.Xr boot 8 ,
+.Xr fdisk 8
+.Sh AUTHORS
+.An Robert Nordier Aq rnordier@FreeBSD.org .
+.Sh BUGS
+Use of the
+.Sq packet
+option may cause
+.Sq boot0
+to fail, depending on the nature of BIOS support.
+.Pp
+Use of the
+.Sq setdrv
+option with an incorrect -d operand may cause the MBR to be written
+to the wrong disk.
+Be careful!
diff --git a/usr.sbin/boot0cfg/boot0cfg.c b/usr.sbin/boot0cfg/boot0cfg.c
new file mode 100644
index 0000000..8dfd9b1
--- /dev/null
+++ b/usr.sbin/boot0cfg/boot0cfg.c
@@ -0,0 +1,427 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#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_VERSION 0x1b0 /* offset: version number */
+#define OFF_OPT 0x1b9 /* offset: default boot option */
+#define OFF_DRIVE 0x1ba /* offset: setdrv drive */
+#define OFF_FLAGS 0x1bb /* offset: option flags */
+#define OFF_TICKS 0x1bc /* offset: clock ticks */
+#define OFF_PTBL 0x1be /* offset: partition table */
+#define OFF_MAGIC 0x1fe /* offset: magic number */
+
+#define cv2(p) ((p)[0] | (p)[1] << 010)
+
+#define mk2(p, x) \
+ (p)[0] = (u_int8_t)(x), \
+ (p)[1] = (u_int8_t)((x) >> 010)
+
+static const struct {
+ const char *tok;
+ int def;
+} opttbl[] = {
+ {"packet", 0},
+ {"update", 1},
+ {"setdrv", 0}
+};
+static const int nopt = sizeof(opttbl) / sizeof(opttbl[0]);
+
+static const char fmt0[] = "# flag start chs type"
+ " end chs offset size\n";
+
+static const char fmt1[] = "%d 0x%02x %4u:%3u:%2u 0x%02x"
+ " %4u:%3u:%2u %10u %10u\n";
+
+static int read_mbr(const char *, u_int8_t **, int);
+static void write_mbr(const char *, int, u_int8_t *, int);
+static void display_mbr(u_int8_t *);
+static int boot0version(const u_int8_t *);
+static int boot0bs(const u_int8_t *);
+static void stropt(const char *, int *, int *);
+static char *mkrdev(const char *);
+static int argtoi(const char *, int, int, int);
+static void usage(void);
+
+/*
+ * Boot manager installation/configuration utility.
+ */
+int
+main(int argc, char *argv[])
+{
+ u_int8_t *mbr, *boot0;
+ int boot0_size, mbr_size;
+ const char *bpath, *fpath;
+ char *disk;
+ int B_flag, v_flag, o_flag;
+ int d_arg, m_arg, s_arg, t_arg;
+ int o_and, o_or;
+ int up, c;
+
+ bpath = "/boot/boot0";
+ fpath = NULL;
+ B_flag = v_flag = o_flag = 0;
+ d_arg = m_arg = s_arg = t_arg = -1;
+ o_and = 0xff;
+ o_or = 0;
+ while ((c = getopt(argc, argv, "Bvb:d:f:m:o:s:t:")) != -1)
+ switch (c) {
+ case 'B':
+ B_flag = 1;
+ break;
+ case 'v':
+ v_flag = 1;
+ break;
+ case 'b':
+ bpath = optarg;
+ break;
+ case 'd':
+ d_arg = argtoi(optarg, 0, 0xff, 'd');
+ break;
+ case 'f':
+ fpath = optarg;
+ break;
+ case 'm':
+ m_arg = argtoi(optarg, 0, 0xf, 'm');
+ break;
+ case 'o':
+ stropt(optarg, &o_and, &o_or);
+ o_flag = 1;
+ break;
+ case 's':
+ s_arg = argtoi(optarg, 1, 5, 's');
+ break;
+ case 't':
+ t_arg = argtoi(optarg, 1, 0xffff, 't');
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc != 1)
+ usage();
+ disk = mkrdev(*argv);
+ up = B_flag || d_arg != -1 || m_arg != -1 || o_flag || s_arg != -1
+ || t_arg != -1;
+
+ /* open the disk and read in the existing mbr */
+ mbr_size = read_mbr(disk, &mbr, !B_flag);
+
+ /* save the existing MBR if we are asked to do so */
+ if (fpath)
+ write_mbr(fpath, O_CREAT | O_TRUNC, mbr, mbr_size);
+
+ /*
+ * If we are installing the boot loader, read it from disk and copy the
+ * slice table over from the existing MBR. If not, then point boot0
+ * back at the MBR we just read in. After this, boot0 is the data to
+ * write back to disk if we are going to do a write.
+ */
+ if (B_flag) {
+ boot0_size = read_mbr(bpath, &boot0, 1);
+ memcpy(boot0 + OFF_PTBL, mbr + OFF_PTBL,
+ sizeof(struct dos_partition) * NDOSPART);
+ } else {
+ boot0 = mbr;
+ boot0_size = mbr_size;
+ }
+
+ /* set the drive */
+ if (d_arg != -1)
+ boot0[OFF_DRIVE] = d_arg;
+
+ /* set various flags */
+ if (m_arg != -1) {
+ boot0[OFF_FLAGS] &= 0xf0;
+ boot0[OFF_FLAGS] |= m_arg;
+ }
+ if (o_flag) {
+ boot0[OFF_FLAGS] &= o_and;
+ boot0[OFF_FLAGS] |= o_or;
+ }
+
+ /* set the default boot selection */
+ if (s_arg != -1)
+ boot0[OFF_OPT] = s_arg - 1;
+
+ /* set the timeout */
+ if (t_arg != -1)
+ mk2(boot0 + OFF_TICKS, t_arg);
+
+ /* write the MBR back to disk */
+ if (up)
+ write_mbr(disk, 0, boot0, boot0_size);
+
+ /* display the MBR */
+ if (v_flag)
+ display_mbr(boot0);
+
+ /* clean up */
+ if (mbr != boot0)
+ free(boot0);
+ free(mbr);
+ free(disk);
+
+ return 0;
+}
+
+/*
+ * Read in the MBR of the disk. If it is boot0, then use the version to
+ * read in all of it if necessary. Use pointers to return a malloc'd
+ * buffer containing the MBR and then return its size.
+ */
+static int
+read_mbr(const char *disk, u_int8_t **mbr, int check_version)
+{
+ u_int8_t buf[MBRSIZE];
+ int mbr_size, fd;
+ ssize_t n;
+
+ if ((fd = open(disk, O_RDONLY)) == -1)
+ err(1, "%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 (!boot0bs(buf)) {
+ if (check_version)
+ errx(1, "%s: unknown or incompatible boot code", disk);
+ } else if (boot0version(buf) == 0x101) {
+ mbr_size = 1024;
+ if ((*mbr = malloc(mbr_size)) == NULL)
+ errx(1, "%s: unable to allocate read buffer", disk);
+ if (lseek(fd, 0, SEEK_SET) == -1 ||
+ (n = read(fd, *mbr, mbr_size)) == -1)
+ err(1, "%s", disk);
+ if (n != mbr_size)
+ errx(1, "%s: short read", disk);
+ return (mbr_size);
+ }
+ *mbr = malloc(sizeof(buf));
+ memcpy(*mbr, buf, sizeof(buf));
+
+ return sizeof(buf);
+}
+
+/*
+ * Write out the mbr to the specified file.
+ */
+static void
+write_mbr(const char *fname, int flags, u_int8_t *mbr, int mbr_size)
+{
+ int fd;
+ ssize_t n;
+
+ if ((fd = open(fname, O_WRONLY | flags, 0666)) == -1 ||
+ (n = write(fd, mbr, mbr_size)) == -1 || close(fd))
+ err(1, "%s", fname);
+ if (n != mbr_size)
+ errx(1, "%s: short write", fname);
+}
+
+/*
+ * Outputs an informative dump of the data in the MBR to stdout.
+ */
+static void
+display_mbr(u_int8_t *mbr)
+{
+ struct dos_partition *part;
+ int i, version;
+
+ part = (struct dos_partition *)(mbr + DOSPARTOFF);
+ printf(fmt0);
+ for (i = 0; i < NDOSPART; i++)
+ if (part[i].dp_typ)
+ printf(fmt1, 1 + i, part[i].dp_flag,
+ part[i].dp_scyl + ((part[i].dp_ssect & 0xc0) << 2),
+ part[i].dp_shd, part[i].dp_ssect & 0x3f, part[i].dp_typ,
+ part[i].dp_ecyl + ((part[i].dp_esect & 0xc0) << 2),
+ part[i].dp_ehd, part[i].dp_esect & 0x3f, part[i].dp_start,
+ part[i].dp_size);
+ printf("\n");
+ version = boot0version(mbr);
+ printf("version=%d.%d drive=0x%x mask=0x%x ticks=%u\noptions=",
+ version >> 8, version & 0xff, mbr[OFF_DRIVE],
+ mbr[OFF_FLAGS] & 0xf, cv2(mbr + OFF_TICKS));
+ for (i = 0; i < nopt; i++) {
+ if (i)
+ printf(",");
+ if (!(mbr[OFF_FLAGS] & 1 << (7 - i)) ^ opttbl[i].def)
+ printf("no");
+ printf("%s", opttbl[i].tok);
+ }
+ printf("\n");
+ printf("default_selection=F%d (", mbr[OFF_OPT] + 1);
+ if (mbr[OFF_OPT] < 4)
+ printf("Slice %d", mbr[OFF_OPT] + 1);
+ else
+ printf("Drive 1");
+ printf(")\n");
+}
+
+/*
+ * Return the boot0 version with the minor revision in the low byte, and
+ * the major revision in the next higher byte.
+ */
+static int
+boot0version(const u_int8_t *bs)
+{
+ static u_int8_t idold[] = {0xfe, 0x45, 0xf2, 0xe9, 0x00, 0x8a};
+
+ /* Check for old version, and return 0x100 if found. */
+ if (memcmp(bs + 0x1c, idold, sizeof(idold)) == 0)
+ return 0x100;
+
+ /* We have a newer boot0, so extract the version number and return it. */
+ return *(const int *)(bs + OFF_VERSION) & 0xffff;
+}
+
+/*
+ * Decide if we have valid boot0 boot code by looking for
+ * characteristic byte sequences at fixed offsets.
+ */
+static int
+boot0bs(const u_int8_t *bs)
+{
+ static u_int8_t id0[] = {0xfc, 0x31, 0xc0, 0x8e, 0xc0, 0x8e, 0xd8,
+ 0x8e, 0xd0, 0xbc, 0x00, 0x7c };
+ static u_int8_t id1[] = {'D', 'r', 'i', 'v', 'e', ' '};
+ static struct {
+ unsigned off;
+ unsigned len;
+ u_int8_t *key;
+ } ident[2] = {
+ {0x0, sizeof(id0), id0},
+ {0x1b2, sizeof(id1), id1}
+ };
+ unsigned int i;
+
+ for (i = 0; i < sizeof(ident) / sizeof(ident[0]); i++)
+ if (memcmp(bs + ident[i].off, ident[i].key, ident[i].len))
+ return 0;
+ return 1;
+};
+
+/*
+ * Adjust "and" and "or" masks for a -o option argument.
+ */
+static void
+stropt(const char *arg, int *xa, int *xo)
+{
+ const char *q;
+ char *s, *s1;
+ int inv, i, x;
+
+ if (!(s = strdup(arg)))
+ err(1, NULL);
+ for (s1 = s; (q = strtok(s1, ",")); s1 = NULL) {
+ if ((inv = !strncmp(q, "no", 2)))
+ q += 2;
+ for (i = 0; i < nopt; i++)
+ if (!strcmp(q, opttbl[i].tok))
+ break;
+ if (i == nopt)
+ errx(1, "%s: Unknown -o option", q);
+ if (opttbl[i].def)
+ inv ^= 1;
+ x = 1 << (7 - i);
+ if (inv)
+ *xa &= ~x;
+ else
+ *xo |= x;
+ }
+ free(s);
+}
+
+/*
+ * Produce a device path for a "canonical" name, where appropriate.
+ */
+static char *
+mkrdev(const char *fname)
+{
+ char buf[MAXPATHLEN];
+ char *s;
+
+ if (!strchr(fname, '/')) {
+ snprintf(buf, sizeof(buf), "%s%s", _PATH_DEV, fname);
+ s = strdup(buf);
+ } else
+ s = strdup(fname);
+
+ if (s == NULL)
+ errx(1, "No more memory");
+ return s;
+}
+
+/*
+ * Convert and check an option argument.
+ */
+static int
+argtoi(const char *arg, int lo, int hi, int opt)
+{
+ char *s;
+ long x;
+
+ errno = 0;
+ x = strtol(arg, &s, 0);
+ if (errno || !*arg || *s || x < lo || x > hi)
+ errx(1, "%s: Bad argument to -%c option", arg, opt);
+ return x;
+}
+
+/*
+ * Display usage information.
+ */
+static void
+usage(void)
+{
+ fprintf(stderr, "%s\n%s\n",
+ "usage: boot0cfg [-Bv] [-b boot0] [-d drive] [-f file] [-m mask]",
+ " [-o options] [-s slice] [-t ticks] disk");
+ exit(1);
+}
diff --git a/usr.sbin/boot98cfg/Makefile b/usr.sbin/boot98cfg/Makefile
new file mode 100644
index 0000000..e7ffd9d
--- /dev/null
+++ b/usr.sbin/boot98cfg/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= boot98cfg
+MAN= boot98cfg.8
+
+WARNS?= 2
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/boot98cfg/boot98cfg.8 b/usr.sbin/boot98cfg/boot98cfg.8
new file mode 100644
index 0000000..fb55d61
--- /dev/null
+++ b/usr.sbin/boot98cfg/boot98cfg.8
@@ -0,0 +1,99 @@
+.\" Copyright (c) KATO Takenori, 2000.
+.\"
+.\" All rights reserved. Unpublished rights reserved under the copyright
+.\" laws of Japan.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer as
+.\" the first lines of this file unmodified.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd July 10, 2000
+.Dt BOOT98CFG 8
+.Os
+.Sh NAME
+.Nm boot98cfg
+.Nd HDD boot manager installation utility
+.Sh SYNOPSIS
+.Nm
+.Op Fl B
+.Op Fl i Ar boot0
+.Op Fl m Ar boot0.5
+.Op Fl s Ar secsize
+.Op Fl v Ar version
+.Op Fl f Ar boot0.bak
+.Op Fl F Ar boot0.5.bak
+.Ar disk
+.Sh DESCRIPTION
+On NEC PC-98s,
+.Sq boot loader
+consists of the
+.Sq IPL
+and
+.Sq HDD boot menu .
+The IPL occupies sector 0 of a disk and is followed by the partition
+table. The IPL loads the HDD boot menu that starts from 0x400.
+.Pp
+The
+.Nm
+installs and makes backup copy of the IPL and the HDD boot menu; and
+allows changing the version number field in the sector 0.
+.Pp
+Note that the format command in NEC's OSs replaces the HDD boot menu
+with its own HDD boot menu when the version number field is smaller
+than that in the format command.
+.Pp
+The options are:
+.Bl -tag -width indent
+.It Fl B
+Install the IPL and HDD boot menu. This option causes the IPL and HDD
+boot menu code to be replaced.
+.It Fl i Ar boot0
+Specify which IPL image to use. The default is /boot/boot0.
+.It Fl m Ar boot0.5
+Specify which HDD boot menu image to use. The default is
+/boot/boot0.5.
+.It Fl f Ar boot0.bak
+Specify that a backup copy of the preexisting IPL should be written to
+.Ar boot0.bak .
+This file is created if it does not exist, and truncated if it does.
+.It Fl F Ar boot0.5.bak
+Specify that a backup copy of the preexisting HDD boot menu should be
+written to
+.Ar boot0.5.bak .
+This file is created if it does not exist, and truncated if it does.
+.It Fl v Ar version
+Specify the version number.
+.It Fl s Ar secsize
+Specify the sector size. The default sector size is 512
+(bytes/sector).
+.El
+.Sh SEE ALSO
+.Xr boot 8 ,
+.Xr fdisk 8
+.Sh DIAGNOSTICS
+.Ex -std
+.Sh AUTHORS
+.An NOKUBI Hirotaka ,
+.An KATO Takenori
diff --git a/usr.sbin/boot98cfg/boot98cfg.c b/usr.sbin/boot98cfg/boot98cfg.c
new file mode 100644
index 0000000..bd4305b
--- /dev/null
+++ b/usr.sbin/boot98cfg/boot98cfg.c
@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) KATO Takenori, 2000.
+ *
+ * All rights reserved. Unpublished rights reserved under the copyright
+ * laws of Japan.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer as
+ * the first lines of this file unmodified.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Copyright (c) 1999 Robert Nordier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define IPLSIZE 512 /* IPL size */
+#define BOOTMENUSIZE 7168 /* Max HDD boot menu size */
+#define BOOTMENUOFF 0x400
+
+static char *mkrdev(char *);
+static void usage(void);
+
+u_char boot0buf[0x2000];
+char ipl[IPLSIZE];
+char menu[BOOTMENUSIZE];
+
+/*
+ * Produce a device path for a "canonical" name, where appropriate.
+ */
+static char *
+mkrdev(char *fname)
+{
+ char buf[MAXPATHLEN];
+ struct stat sb;
+ char *s;
+
+ s = (char *)fname;
+ if (!strchr(fname, '/')) {
+ snprintf(buf, sizeof(buf), "%sr%s", _PATH_DEV, fname);
+ if (stat(buf, &sb))
+ snprintf(buf, sizeof(buf), "%s%s", _PATH_DEV, fname);
+ if (!(s = strdup(buf)))
+ err(1, NULL);
+ }
+ return s;
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr,
+ "boot98cfg [-B][-i boot0][-m boot0.5][-s secsize][-v version]\n"
+ " [-f ipl.bak][-F menu.bak] disk\n");
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ char *endptr;
+ const char *iplpath = "/boot/boot0", *menupath = "/boot/boot0.5";
+ char *iplbakpath = NULL, *menubakpath = NULL;
+ char *disk;
+ int B_flag = 0;
+ int c;
+ int fd, fd1;
+ int n;
+ int secsize = 512;
+ int v_flag = 0, version;
+
+ while ((c = getopt(argc, argv, "BF:f:i:m:s:v:")) != -1) {
+ switch (c) {
+ case 'B':
+ B_flag = 1;
+ break;
+ case 'F':
+ menubakpath = optarg;
+ break;
+ case 'f':
+ iplbakpath = optarg;
+ break;
+ case 'i':
+ iplpath = optarg;
+ break;
+ case 'm':
+ menupath = optarg;
+ break;
+ case 's':
+ secsize = strtol(optarg, &endptr, 0);
+ if (errno || *optarg == NULL || *endptr)
+ errx(1, "%s: Bad argument to -s option",
+ optarg);
+ switch (secsize) {
+ case 256:
+ case 512:
+ case 1024:
+ case 2048:
+ break;
+ default:
+ errx(1, "%s: unsupported sector size", optarg);
+ break;
+ }
+ break;
+ case 'v':
+ v_flag = 1;
+ version = strtol(optarg, &endptr, 0);
+ if (errno || *optarg == NULL || *endptr ||
+ version < 0 || version > 255)
+ errx(1, "%s: Bad argument to -s option",
+ optarg);
+ break;
+ default:
+ usage();
+ /* NOTREACHED */
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc != 1)
+ usage();
+ disk = mkrdev(*argv);
+
+ /* Read IPL, partition table and HDD boot menu. */
+ fd = open(disk, O_RDWR);
+ if (fd < 0)
+ err(1, "%s", disk);
+ n = read(fd, boot0buf, 0x2000);
+ if (n != 0x2000)
+ errx(1, "%s: short read", disk);
+ if (!B_flag && !v_flag)
+ close(fd);
+
+ if (iplbakpath != NULL) {
+ fd1 = open(iplbakpath, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ if (fd1 < 0)
+ err(1, "%s", iplbakpath);
+ n = write(fd1, boot0buf, IPLSIZE);
+ if (n == -1)
+ err(1, "%s", iplbakpath);
+ if (n != IPLSIZE)
+ errx(1, "%s: short write", iplbakpath);
+ close(fd1);
+ }
+
+ if (menubakpath != NULL) {
+ fd1 = open(menubakpath, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ if (fd1 < 0)
+ err(1, "%s", menubakpath);
+ n = write(fd1, boot0buf + BOOTMENUOFF, BOOTMENUSIZE);
+ if (n == -1)
+ err(1, "%s", menubakpath);
+ if (n != BOOTMENUSIZE)
+ errx(1, "%s: short write", menubakpath);
+ close(fd1);
+ }
+
+ if (B_flag) {
+ /* Read IPL (boot0). */
+ fd1 = open(iplpath, O_RDONLY);
+ if (fd1 < 0)
+ err(1, "%s", disk);
+ n = read(fd1, ipl, IPLSIZE);
+ if (n < 0)
+ err(1, "%s", iplpath);
+ if (n != IPLSIZE)
+ errx(1, "%s: invalid file", iplpath);
+ close(fd1);
+
+ /* Read HDD boot menu (boot0.5). */
+ fd1 = open(menupath, O_RDONLY);
+ if (fd1 < 0)
+ err(1, "%s", disk);
+ n = read(fd1, menu, BOOTMENUSIZE);
+ if (n < 0)
+ err(1, "%s", menupath);
+ if (n != BOOTMENUSIZE)
+ errx(1, "%s: invalid file", menupath);
+ close(fd1);
+
+ memcpy(boot0buf, ipl, IPLSIZE);
+ memcpy(boot0buf + BOOTMENUOFF, menu, BOOTMENUSIZE);
+ }
+
+ /* Set version number field. */
+ if (v_flag)
+ *(boot0buf + secsize - 4) = (u_char)version;
+
+ if (B_flag || v_flag) {
+ if (lseek(fd, 0, SEEK_SET) == -1 ||
+ (n = write(fd, boot0buf, 0x2000)) < 0 ||
+ close(fd))
+ err(1, "%s", disk);
+ if (n != 0x2000)
+ errx(1, "%s: short write", disk);
+ }
+
+ return 0;
+}
diff --git a/usr.sbin/bootparamd/Makefile b/usr.sbin/bootparamd/Makefile
new file mode 100644
index 0000000..c4e3361
--- /dev/null
+++ b/usr.sbin/bootparamd/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+SUBDIR= bootparamd callbootd
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/bootparamd/Makefile.inc b/usr.sbin/bootparamd/Makefile.inc
new file mode 100644
index 0000000..1e4d46f
--- /dev/null
+++ b/usr.sbin/bootparamd/Makefile.inc
@@ -0,0 +1,4 @@
+# @(#)Makefile.inc 5.1 (Berkeley) 5/11/90
+# $FreeBSD$
+
+BINDIR?= /usr/sbin
diff --git a/usr.sbin/bootparamd/bootparamd/Makefile b/usr.sbin/bootparamd/bootparamd/Makefile
new file mode 100644
index 0000000..d312288
--- /dev/null
+++ b/usr.sbin/bootparamd/bootparamd/Makefile
@@ -0,0 +1,24 @@
+# from: @(#)Makefile 5.8 (Berkeley) 7/28/90
+# $FreeBSD$
+
+PROG= bootparamd
+MAN= bootparams.5 bootparamd.8
+SRCS= bootparamd.c main.c ${GENSRCS}
+GENSRCS=bootparam_prot.h bootparam_prot_svc.c bootparam_prot_xdr.c
+
+CFLAGS+= -DTFTP_DIR=\"/tftpboot\" -I.
+
+CLEANFILES= ${GENSRCS}
+
+RPCSRC= ${DESTDIR}/usr/include/rpcsvc/bootparam_prot.x
+
+bootparam_prot_svc.c: ${RPCSRC}
+ rpcgen -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..508ee0f
--- /dev/null
+++ b/usr.sbin/bootparamd/bootparamd/README
@@ -0,0 +1,75 @@
+$FreeBSD$
+
+This directory contains a version of the rpc.bootparamd, which have been
+written using the Sun's RPC protocol for bootparamd. To use it you must
+have a copy of the bootparam_prot.x file which on Sun systems you find in
+
+ /usr/include/rpcsvc/bootparam_prot.x
+
+(( This file was retrieved from the Sun-RPC source package ))
+
+
+This code is not copyright, and is placed in the public domain. Feel free to
+use and modify. Please send modifications and/or suggestions + bug fixes to
+
+ Klas Heggemann <klas@nada.kth.se>
+
+
+RPC.BOOTPARAMD
+
+
+The rpc.bootparamd program does NOT use the yellow pages for the bootparams
+database. This data should recide in /etc/bootparams on the local host,
+or another file given when the server is started.
+
+The default router is set to the address of the machine running the server.
+This may not be a good thing to do, so it can be modified using the -r
+option when startning the daemon.
+
+This program was written with the need to keep short hostnames in the
+/etc/bootparams file and long (canonical) names in the hosts database.
+It probably also will work in conjunction with a nameserver, since matching
+is done by comparing the canonical name of the booting machine with the
+canonical name of the hosts found in the bootparams database.
+
+It is kept simple, e g there is no caching of data, but the bootparameter file
+is read at each request.
+
+
+CALLBOOTD
+
+The debugging tool callbootd is used to check the response you get
+to a specific (booting) request. It can be used as
+ callbootd server inet-address
+or
+ callbootd server hostname file
+where "server" is a machine running the rpc.bootparamd program, "inet-address"
+is the internet address of a booting machine, "hostname" is the name of a
+booting machine and "file" the requested file, typically "root", "swap" or
+"dump".
+
+You may also use "all" instead of a specific server, in which case a RPC
+broadcast is performed. The broadcast is performed 4 times and then the
+program times out, after printing all responses.
+
+
+TODO
+
+Cache the date, instead of rereading it.
+Maybe match by comparing the inet address instead. (But beware that caching
+will prevent the server from detecting that a machine has changed name
+or address.)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/usr.sbin/bootparamd/bootparamd/bootparamd.8 b/usr.sbin/bootparamd/bootparamd/bootparamd.8
new file mode 100644
index 0000000..37bd886
--- /dev/null
+++ b/usr.sbin/bootparamd/bootparamd/bootparamd.8
@@ -0,0 +1,75 @@
+.\" @(#)bootparamd.8
+.\" $FreeBSD$
+.Dd December 14, 2000
+.Dt BOOTPARAMD 8
+.Os
+.Sh NAME
+.Nm bootparamd
+.Nd boot parameter server
+.Sh SYNOPSIS
+.Nm
+.Op Fl ds
+.Op Fl r Ar router
+.Op Fl f Ar file
+.Sh DESCRIPTION
+.Nm Bootparamd
+is a server process that provides information to
+.Xr diskless 8
+clients necessary for booting.
+It consults the
+.Pa /etc/bootparams
+file.
+.Pp
+This version will allow the use of aliases on the hostname in the
+.Pa /etc/bootparams
+file.
+The returned hostname to the
+.Em whoami
+request done by the booting client
+will be the name that appears in
+.Pa /etc/bootparams
+and not the canonical name.
+In this way you can keep the answer short enough
+so that machines that cannot handle long hostnames will not fail during boot.
+.Sh OPTIONS
+.Bl -tag -width Fl
+.It Fl d
+Display the debugging information.
+.It Fl s
+Log the debugging information with
+.Xr syslog 3 .
+.It Fl r Ar router
+The default router (a machine or an IP-address).
+This defaults to the machine running the server.
+.It Fl f Ar file
+The file to use as boot parameter file instead of
+.Pa /etc/bootparams .
+.El
+.Sh FILES
+.Bl -tag -width /etc/bootparams -compact
+.It /etc/bootparams
+default boot parameter file
+.El
+.Sh EXAMPLES
+When netbooting diskless SunOS/Xkernel SPARCstations the booted SunOS kernel
+also broadcasts to the all-0 address.
+The SunOS kernel hangs until it receives a reply.
+To accommodate this behaviour add an alias address
+that responds to an all-0 broadcast.
+So, add something like
+.Ql "ifconfig xl0 192.168.200.254 netmask 255.255.255.255 broadcast 192.168.200.0 alias
+on the relevant network interface on your
+.Nm
+server.
+The alias address must of course be free for use.
+.Sh SEE ALSO
+.Xr syslog 3 ,
+.Xr bootparams 5 ,
+.Xr diskless 8
+.Sh BUGS
+You may find the
+.Xr syslog 3
+loggings to be verbose.
+.Sh AUTHORS
+Written by
+.An Klas Heggemann Aq klas@nada.kth.se .
diff --git a/usr.sbin/bootparamd/bootparamd/bootparamd.c b/usr.sbin/bootparamd/bootparamd/bootparamd.c
new file mode 100644
index 0000000..80fbe70
--- /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[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <rpc/rpc.h>
+#include <rpcsvc/yp_prot.h>
+#include <rpcsvc/ypclnt.h>
+#include "bootparam_prot.h"
+#include <ctype.h>
+#include <err.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+extern int debug, dolog;
+extern unsigned long route_addr;
+extern char *bootpfile;
+
+#define MAXLEN 800
+
+struct hostent *he;
+static char buffer[MAXLEN];
+static char hostname[MAX_MACHINE_NAME];
+static char askname[MAX_MACHINE_NAME];
+static char path[MAX_PATH_LEN];
+static char domain_name[MAX_MACHINE_NAME];
+
+int getthefile __P((char *, char *, char *, int));
+int checkhost __P((char *, char *, int));
+
+bp_whoami_res *
+bootparamproc_whoami_1(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..c7c6785
--- /dev/null
+++ b/usr.sbin/bootparamd/bootparamd/bootparams.5
@@ -0,0 +1,86 @@
+.\"
+.\" Copyright (c) 1994 Gordon W. Ross
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" from: Id: bootparams.5,v 1.2 1994/10/03 19:26:13 gwr Exp
+.\" $FreeBSD$
+.\"
+.Dd October 2, 1994
+.Dt BOOTPARAMS 5
+.Os
+.Sh NAME
+.Nm bootparams
+.Nd boot parameter database
+.Sh SYNOPSIS
+.Nm /etc/bootparams
+.Sh DESCRIPTION
+The
+.Nm
+file specifies the boot parameters that
+.Xr diskless 8
+clients may request when booting over the network.
+Each client supported by this server must have an entry in the
+.Nm
+file containing the pathnames for its
+.Dq root
+and (optionally)
+.Dq swap
+areas.
+.Pp
+Each line in the file
+(other than comment lines that begin with a
+.Ql # )
+specifies the client name followed by the pathnames that
+the client may request by their logical names.
+The components of the line are delimited with blank or tab,
+and may be continued onto multiple lines with a backslash.
+.Pp
+For example:
+.Bd -literal -offset indent
+dummy root=host:/export/dummy/root \\
+ swap=host:/export/dummy/swap \\
+ dump=host:/export/dummy/swap
+.Ed
+.Pp
+When the client named
+.Dq dummy
+requests the pathname for its logical
+.Dq root
+it will be given the pathname
+.Dq Pa host:/export/dummy/root
+as the response to its
+.Tn RPC
+request.
+The
+.Dq host:
+component must be supplied.
+.Sh FILES
+.Bl -tag -width /etc/bootparams -compact
+.It Pa /etc/bootparams
+default configuration file
+.El
+.Sh SEE ALSO
+.Xr bootparamd 8 ,
+.Xr diskless 8
diff --git a/usr.sbin/bootparamd/bootparamd/main.c b/usr.sbin/bootparamd/bootparamd/main.c
new file mode 100644
index 0000000..a3cc0e7
--- /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[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <ctype.h>
+#include <err.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include "bootparam_prot.h"
+
+int debug = 0;
+int dolog = 0;
+unsigned long route_addr = -1;
+struct sockaddr_in my_addr;
+char *bootpfile = "/etc/bootparams";
+
+extern void bootparamprog_1();
+static void usage __P((void));
+
+int
+main(argc, argv)
+int argc;
+char **argv;
+{
+ SVCXPRT *transp;
+ 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..8db69ea
--- /dev/null
+++ b/usr.sbin/bootparamd/callbootd/Makefile
@@ -0,0 +1,24 @@
+# from: @(#)Makefile 5.8 (Berkeley) 7/28/90
+# $FreeBSD$
+
+PROG= callbootd
+NOMAN= #true
+SRCS= callbootd.c ${GENSRCS}
+GENSRCS=bootparam_prot.h bootparam_prot_clnt.c bootparam_prot_xdr.c
+
+CFLAGS+= -I.
+
+CLEANFILES= ${GENSRCS}
+
+RPCSRC= ${DESTDIR}/usr/include/rpcsvc/bootparam_prot.x
+
+bootparam_prot_clnt.c: ${RPCSRC}
+ rpcgen -l -o ${.TARGET} ${RPCSRC}
+
+bootparam_prot_xdr.c: ${RPCSRC}
+ rpcgen -c -o ${.TARGET} ${RPCSRC}
+
+bootparam_prot.h: ${RPCSRC}
+ rpcgen -h -o ${.TARGET} ${RPCSRC}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/bootparamd/callbootd/callbootd.c b/usr.sbin/bootparamd/callbootd/callbootd.c
new file mode 100644
index 0000000..b7827ac
--- /dev/null
+++ b/usr.sbin/bootparamd/callbootd/callbootd.c
@@ -0,0 +1,200 @@
+/*
+
+This code is not copyright, and is placed in the public domain. Feel free to
+use and modify. Please send modifications and/or suggestions + bug fixes to
+
+ Klas Heggemann <klas@nada.kth.se>
+
+*/
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include "bootparam_prot.h"
+#include <rpc/rpc.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <err.h>
+#include <netdb.h>
+
+
+/* #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,
+ (xdrproc_t)xdr_bp_whoami_arg, (char *)&whoami_arg,
+ xdr_bp_whoami_res, (char *)&stat_whoami_res,
+ (resultproc_t)eachres_whoami);
+ exit(0);
+ }
+
+ case 4:
+
+ getfile_arg.client_name = argv[2];
+ getfile_arg.file_id = argv[3];
+
+ if (! broadcast ) {
+ getfile_res = bootparamproc_getfile_1(&getfile_arg,clnt);
+ printf("getfile returning:\n");
+ if (printgetfile(getfile_res)) {
+ errx(1, "bad answer returned from server %s", server);
+ } else
+ exit(0);
+ } else {
+ clnt_stat=clnt_broadcast(BOOTPARAMPROG, BOOTPARAMVERS,
+ BOOTPARAMPROC_GETFILE,
+ xdr_bp_getfile_arg, (char *)&getfile_arg,
+ xdr_bp_getfile_res, (char *)&stat_getfile_res,
+ (resultproc_t)eachres_getfile);
+ exit(0);
+ }
+
+ default:
+
+ usage();
+ }
+
+}
+
+
+static void
+usage()
+{
+ fprintf(stderr,
+ "usage: callbootd server procnum (IP-addr | host fileid)\n");
+ exit(1);
+}
+
+int
+printwhoami(res)
+bp_whoami_res *res;
+{
+ if ( res) {
+ printf("client_name:\t%s\ndomain_name:\t%s\n",
+ res->client_name, res->domain_name);
+ printf("router:\t%d.%d.%d.%d\n",
+ 255 & res->router_address.bp_address_u.ip_addr.net,
+ 255 & res->router_address.bp_address_u.ip_addr.host,
+ 255 & res->router_address.bp_address_u.ip_addr.lh,
+ 255 & res->router_address.bp_address_u.ip_addr.impno);
+ return(0);
+ } else {
+ warnx("null answer!!!");
+ return(1);
+ }
+ }
+
+
+
+
+int
+printgetfile(res)
+bp_getfile_res *res;
+{
+ if (res) {
+ printf("server_name:\t%s\nserver_address:\t%s\npath:\t%s\n",
+ res->server_name,
+ inet_ntoa(*(struct in_addr *)&res->server_address.bp_address_u.ip_addr),
+ res->server_path);
+ return(0);
+ } else {
+ warnx("null answer!!!");
+ return(1);
+ }
+ }
diff --git a/usr.sbin/btxld/Makefile b/usr.sbin/btxld/Makefile
new file mode 100644
index 0000000..32cf99d
--- /dev/null
+++ b/usr.sbin/btxld/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+PROG= btxld
+MAN= btxld.8
+SRCS= btxld.c elfh.c
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/btxld/btx.h b/usr.sbin/btxld/btx.h
new file mode 100644
index 0000000..86f0ede
--- /dev/null
+++ b/usr.sbin/btxld/btx.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 1998 Robert Nordier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _BTX_H_
+#define _BTX_H_
+
+#include <sys/types.h>
+
+#define BTX_PGSIZE 0x1000 /* Page size */
+#define BTX_PGBASE 0x5000 /* Start of page tables */
+#define BTX_MAXCWR 0x3bc /* Max. btx_pgctl adjustment */
+
+/*
+ * BTX image header.
+ */
+struct btx_hdr {
+ uint8_t btx_machid; /* Machine ID */
+ uint8_t btx_hdrsz; /* Header size */
+ uint8_t btx_magic[3]; /* Magic */
+ uint8_t btx_majver; /* Major version */
+ uint8_t btx_minver; /* Minor version */
+ uint8_t btx_flags; /* Flags */
+ uint16_t btx_pgctl; /* Paging control */
+ uint16_t btx_textsz; /* Text size */
+ uint32_t btx_entry; /* Client entry address */
+};
+
+/* btx_machid */
+#define BTX_I386 0xeb /* Intel i386 or compatible */
+
+/* btx_magic */
+#define BTX_MAG0 'B'
+#define BTX_MAG1 'T'
+#define BTX_MAG2 'X'
+
+/* btx_flags */
+#define BTX_MAPONE 0x80 /* Start mapping at page 1 */
+
+#define BTX_MAPPED(btx) (((btx).btx_pgctl | (BTX_PGSIZE / 4 - 1)) + 1)
+#define BTX_ORIGIN(btx) (BTX_PGBASE + BTX_MAPPED(btx) * 4)
+#define BTX_ENTRY(btx) (BTX_ORIGIN(btx) + 2 + (btx).btx_hdrsz)
+
+#endif /* !_BTX_H_ */
diff --git a/usr.sbin/btxld/btxld.8 b/usr.sbin/btxld/btxld.8
new file mode 100644
index 0000000..95dff0c
--- /dev/null
+++ b/usr.sbin/btxld/btxld.8
@@ -0,0 +1,97 @@
+.\" Copyright (c) 1998 Robert Nordier
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+.\" OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+.\" OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+.\" BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+.\" WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+.\" OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+.\" EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd September 12, 1998
+.Dt BTXLD 8
+.Os
+.Sh NAME
+.Nm btxld
+.Nd link editor for BTX clients
+.Sh SYNOPSIS
+.Nm
+.Op Fl qv
+.Op Fl b Ar file
+.Op Fl E Ar address
+.Op Fl e Ar address
+.Op Fl f Ar format
+.Op Fl l Ar file
+.Op Fl o Ar filename
+.Op Fl P Ar page
+.Op Fl W Ar page
+.Ar file
+.Sh DESCRIPTION
+The
+.Nm
+utility binds the specified client executable together with a BTX
+loader program and the BTX kernel, and creates a composite object file
+suitable for loading during the boot process.
+.Pp
+The options are:
+.Bl -tag -width indent
+.It Fl q
+Quiet: inhibit warnings.
+.It Fl v
+Verbose: display information about the files processed.
+.It Fl b Ar file
+Specify the BTX kernel to be bound with the client.
+.It Fl E Ar address
+Set the client entry point.
+.It Fl e Ar address
+Set the BTX loader entry point.
+.It Fl f Ar format
+Specify the output format, where
+.Ar format
+is one of
+.Sq bin ,
+.Sq aout ,
+or
+.Sq elf .
+.It Fl l Ar file
+Specify the BTX loader to be bound with the client.
+.It Fl o Ar filename
+Name the output file. The default is
+.Dq a.out .
+.It Fl P Ar page
+Specify the first page of the client's segment to be marked
+.Sq present ,
+where
+.Ar page
+may be 0 or 1.
+.It Fl W Ar page
+Specify the first page of the client's segment to be marked
+.Sq writable ,
+where
+.Ar page
+may be 0, and should not exceed the number of pages occupied by the
+combined .text and .data segments of the client image.
+.El
+.Sh SEE ALSO
+.Xr ld 1 ,
+.Xr boot 8
+.Sh DIAGNOSTICS
+.Ex -std
+.Sh AUTHORS
+.An Robert Nordier Aq rnordier@FreeBSD.org .
diff --git a/usr.sbin/btxld/btxld.c b/usr.sbin/btxld/btxld.c
new file mode 100644
index 0000000..9e83beb
--- /dev/null
+++ b/usr.sbin/btxld/btxld.c
@@ -0,0 +1,556 @@
+/*
+ * Copyright (c) 1998 Robert Nordier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/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 "btx.h"
+#include "elfh.h"
+
+#define BTX_PATH "/sys/boot/i386/btx"
+
+#define I_LDR 0 /* BTX loader */
+#define I_BTX 1 /* BTX kernel */
+#define I_CLNT 2 /* Client program */
+
+#define F_BIN 0 /* Binary */
+#define F_AOUT 1 /* ZMAGIC a.out */
+#define F_ELF 2 /* 32-bit ELF */
+#define F_CNT 3 /* Number of formats */
+
+#define IMPURE 1 /* Writable text */
+#define MAXU32 0xffffffff /* Maximum unsigned 32-bit quantity */
+
+#define align(x, y) (((x) + (y) - 1) & ~((y) - 1))
+
+struct hdr {
+ uint32_t fmt; /* Format */
+ uint32_t flags; /* Bit flags */
+ uint32_t size; /* Size of file */
+ uint32_t text; /* Size of text segment */
+ uint32_t data; /* Size of data segment */
+ uint32_t bss; /* Size of bss segment */
+ uint32_t org; /* Program origin */
+ uint32_t entry; /* Program entry point */
+};
+
+static const char *const fmtlist[] = {"bin", "aout", "elf"};
+
+static const char binfo[] =
+ "kernel: ver=%u.%02u size=%x load=%x entry=%x map=%uM "
+ "pgctl=%x:%x\n";
+static const char cinfo[] =
+ "client: fmt=%s size=%x text=%x data=%x bss=%x entry=%x\n";
+static const char oinfo[] =
+ "output: fmt=%s size=%x text=%x data=%x org=%x entry=%x\n";
+
+static const char *lname =
+ BTX_PATH "/btxldr/btxldr"; /* BTX loader */
+static const char *bname =
+ BTX_PATH "/btx/btx"; /* BTX kernel */
+static const char *oname =
+ "a.out"; /* Output filename */
+
+static int ppage = -1; /* First page present */
+static int wpage = -1; /* First page writable */
+
+static unsigned int format; /* Output format */
+
+static uint32_t centry; /* Client entry address */
+static uint32_t lentry; /* Loader entry address */
+
+static int Eflag; /* Client entry option */
+
+static int quiet; /* Inhibit warnings */
+static int verbose; /* Display information */
+
+static const char *tname; /* Temporary output file */
+static const char *fname; /* Current input file */
+
+static void cleanup(void);
+static void btxld(const char *);
+static void getbtx(int, struct btx_hdr *);
+static void gethdr(int, struct hdr *);
+static void puthdr(int, struct hdr *);
+static void copy(int, int, size_t, off_t);
+static size_t readx(int, void *, size_t, off_t);
+static void writex(int, const void *, size_t);
+static void seekx(int, off_t);
+static unsigned int optfmt(const char *);
+static uint32_t optaddr(const char *);
+static int optpage(const char *, int);
+static void Warn(const char *, const char *, ...);
+static void usage(void);
+
+/*
+ * A link editor for BTX clients.
+ */
+int
+main(int argc, char *argv[])
+{
+ int c;
+
+ while ((c = getopt(argc, argv, "qvb:E:e:f:l:o:P:W:")) != -1)
+ switch (c) {
+ case 'q':
+ quiet = 1;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 'b':
+ bname = optarg;
+ break;
+ case 'E':
+ centry = optaddr(optarg);
+ Eflag = 1;
+ break;
+ case 'e':
+ lentry = optaddr(optarg);
+ break;
+ case 'f':
+ format = optfmt(optarg);
+ break;
+ case 'l':
+ lname = optarg;
+ break;
+ case 'o':
+ oname = optarg;
+ break;
+ case 'P':
+ ppage = optpage(optarg, 1);
+ break;
+ case 'W':
+ wpage = optpage(optarg, BTX_MAXCWR);
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc != 1)
+ usage();
+ atexit(cleanup);
+ btxld(*argv);
+ return 0;
+}
+
+/*
+ * Clean up after errors.
+ */
+static void
+cleanup(void)
+{
+ if (tname)
+ remove(tname);
+}
+
+/*
+ * Read the input files; write the output file; display information.
+ */
+static void
+btxld(const char *iname)
+{
+ char name[FILENAME_MAX];
+ struct btx_hdr btx;
+ struct hdr ihdr, ohdr;
+ unsigned int ldr_size, cwr;
+ int fdi[3], fdo, i;
+
+ ldr_size = 0;
+
+ for (i = I_LDR; i <= I_CLNT; i++) {
+ fname = i == I_LDR ? lname : i == I_BTX ? bname : iname;
+ if ((fdi[i] = open(fname, O_RDONLY)) == -1)
+ err(2, "%s", fname);
+ switch (i) {
+ case I_LDR:
+ gethdr(fdi[i], &ihdr);
+ if (ihdr.fmt != F_BIN)
+ Warn(fname, "Loader format is %s; processing as %s",
+ fmtlist[ihdr.fmt], fmtlist[F_BIN]);
+ ldr_size = ihdr.size;
+ break;
+ case I_BTX:
+ getbtx(fdi[i], &btx);
+ break;
+ case I_CLNT:
+ gethdr(fdi[i], &ihdr);
+ if (ihdr.org && ihdr.org != BTX_PGSIZE)
+ Warn(fname,
+ "Client origin is 0x%x; expecting 0 or 0x%x",
+ ihdr.org, BTX_PGSIZE);
+ }
+ }
+ memset(&ohdr, 0, sizeof(ohdr));
+ ohdr.fmt = format;
+ ohdr.text = ldr_size;
+ ohdr.data = btx.btx_textsz + ihdr.size;
+ ohdr.org = lentry;
+ ohdr.entry = lentry;
+ cwr = 0;
+ if (wpage > 0 || (wpage == -1 && !(ihdr.flags & IMPURE))) {
+ if (wpage > 0)
+ cwr = wpage;
+ else {
+ cwr = howmany(ihdr.text, BTX_PGSIZE);
+ if (cwr > BTX_MAXCWR)
+ cwr = BTX_MAXCWR;
+ }
+ }
+ if (ppage > 0 || (ppage && wpage && ihdr.org >= BTX_PGSIZE)) {
+ btx.btx_flags |= BTX_MAPONE;
+ if (!cwr)
+ cwr++;
+ }
+ btx.btx_pgctl -= cwr;
+ btx.btx_entry = Eflag ? centry : ihdr.entry;
+ if (snprintf(name, sizeof(name), "%s.tmp", oname) >= sizeof(name))
+ errx(2, "%s: Filename too long", oname);
+ if ((fdo = open(name, O_CREAT | O_TRUNC | O_WRONLY, 0666)) == -1)
+ err(2, "%s", name);
+ if (!(tname = strdup(name)))
+ err(2, NULL);
+ puthdr(fdo, &ohdr);
+ for (i = I_LDR; i <= I_CLNT; i++) {
+ fname = i == I_LDR ? lname : i == I_BTX ? bname : iname;
+ switch (i) {
+ case I_LDR:
+ copy(fdi[i], fdo, ldr_size, 0);
+ seekx(fdo, ohdr.size += ohdr.text);
+ break;
+ case I_BTX:
+ 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 int fmt, x, n, i;
+
+ memset(hdr, 0, sizeof(*hdr));
+ if (fstat(fd, &sb))
+ err(2, "%s", fname);
+ if (sb.st_size > MAXU32)
+ errx(1, "%s: Too big", fname);
+ hdr->size = sb.st_size;
+ if ((p = mmap(NULL, hdr->size, PROT_READ, MAP_SHARED, fd,
+ 0)) == MAP_FAILED)
+ err(2, "%s", fname);
+ for (fmt = F_CNT - 1; !hdr->fmt && fmt; fmt--)
+ switch (fmt) {
+ case F_AOUT:
+ ex = p;
+ if (hdr->size >= sizeof(struct 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 int
+optfmt(const char *arg)
+{
+ unsigned int i;
+
+ for (i = 0; i < F_CNT && strcmp(arg, fmtlist[i]); i++);
+ if (i == F_CNT)
+ errx(1, "%s: Unknown format", arg);
+ return i;
+}
+
+/*
+ * Convert an option argument to an address.
+ */
+static uint32_t
+optaddr(const char *arg)
+{
+ char *s;
+ unsigned long x;
+
+ errno = 0;
+ x = strtoul(arg, &s, 0);
+ if (errno || !*arg || *s || x > MAXU32)
+ errx(1, "%s: Illegal address", arg);
+ return x;
+}
+
+/*
+ * Convert an option argument to a page number.
+ */
+static int
+optpage(const char *arg, int hi)
+{
+ char *s;
+ long x;
+
+ errno = 0;
+ x = strtol(arg, &s, 0);
+ if (errno || !*arg || *s || x < 0 || x > hi)
+ errx(1, "%s: Illegal page number", arg);
+ return x;
+}
+
+/*
+ * Display a warning.
+ */
+static void
+Warn(const char *locus, const char *fmt, ...)
+{
+ va_list ap;
+ char *s;
+
+ if (!quiet) {
+ asprintf(&s, "%s: Warning: %s", locus, fmt);
+ va_start(ap, fmt);
+ vwarnx(s, ap);
+ va_end(ap);
+ free(s);
+ }
+}
+
+/*
+ * Display usage information.
+ */
+static void
+usage(void)
+{
+ fprintf(stderr, "%s\n%s\n",
+ "usage: btxld [-qv] [-b file] [-E address] [-e address] [-f format]",
+ " [-l file] [-o filename] [-P page] [-W page] file");
+ exit(1);
+}
diff --git a/usr.sbin/btxld/elfh.c b/usr.sbin/btxld/elfh.c
new file mode 100644
index 0000000..0f0928b
--- /dev/null
+++ b/usr.sbin/btxld/elfh.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 1998 Robert Nordier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+
+#include <stddef.h>
+#include "elfh.h"
+
+#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..a4ab134
--- /dev/null
+++ b/usr.sbin/btxld/elfh.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 1998 Robert Nordier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/elf32.h>
+
+struct elfh {
+ Elf32_Ehdr e; /* ELF header */
+ Elf32_Phdr p[2]; /* program header */
+ Elf32_Shdr sh[4]; /* section header */
+ char shstrtab[28]; /* section header string table */
+};
+
+extern const struct elfh elfhdr; /* ELF header template */
diff --git a/usr.sbin/burncd/Makefile b/usr.sbin/burncd/Makefile
new file mode 100644
index 0000000..bb73d9a
--- /dev/null
+++ b/usr.sbin/burncd/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= burncd
+WARNS?= 2
+NO_WERROR= yes
+MAN= burncd.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/burncd/burncd.8 b/usr.sbin/burncd/burncd.8
new file mode 100644
index 0000000..d0b6564
--- /dev/null
+++ b/usr.sbin/burncd/burncd.8
@@ -0,0 +1,185 @@
+.\"
+.\" Copyright (c) 2000,2001,2002 Søren Schmidt <sos@FreeBSD.org>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer,
+.\" without modification, immediately at the beginning of the file.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd December 4, 2001
+.Os
+.Dt BURNCD 8
+.Sh NAME
+.Nm burncd
+.Nd control the ATAPI CD-R/RW driver
+.Sh SYNOPSIS
+.Nm
+.Op Fl delmnpqtv
+.Op Fl f Ar device
+.Op Fl s Ar speed
+.Op Ar command
+.Op Ar command Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility is used to burn CD-R/RW media using the ATAPI cd driver.
+.Pp
+Available options and operands:
+.Pp
+.Bl -tag -width XXXXXXXXXXXX
+.It Fl d
+burn the CD-R/RW in DAO (disk at once) mode.
+.It Fl e
+eject the media when done.
+.It Fl f Ar device
+set the device to use for the burning process.
+.It Fl l
+read a list of image files from filename.
+.It Fl m
+close disk in multisession mode (otherwise disk is closed as singlesession).
+.It Fl n
+do not write gaps between data tracks in DAO mode.
+.It Fl p
+use preemphasis on audio tracks.
+.It Fl q
+quiet, do not print progress messages.
+.It Fl s Ar speed
+set the speed of the burner device.
+Defaults to 1.
+.It Fl t
+test write, do not actually write on the media.
+.It Fl v
+verbose, print extra progress messages.
+.El
+.Pp
+.Ar command
+may be one of:
+.Pp
+.Bl -tag -width XXXXXXXXXXXX
+.It Cm msinfo
+Show the first LBA of the last track on the media
+and the next writeable address on the media for use with the
+.Xr mkisofs 8 Ns 's
+.Fl C
+switch when adding additional data to ISO filesystems with extra sessions.
+.It Cm blank
+Blank a CD-RW medium.
+This uses the fast blanking method, so data are not physically overwritten,
+only those areas that make the media appear blank for further usage are erased.
+.It Cm erase
+Erase a CD-RW medium.
+This erases the entire media.
+Can take up to 1 hour to finish.
+.It Cm fixate
+Fixate the medium so that the TOC is generated and the media can be used
+in an ordinary CD drive.
+The driver defaults to creating singlesession media (see
+.Fl m
+option).
+Should be the last command to
+.Nm
+as the program exits when this has been done.
+Ignored in DAO mode (see
+.Fl d
+option).
+.It Cm raw | audio
+Set the write mode to produce audio (raw mode) tracks for the following
+images on the command line.
+.It Cm data | mode1
+Set the write mode to produce data (mode1) tracks for the following
+image files
+on the command line.
+.It Cm mode2
+Set the write mode to produce data (mode2) tracks for the following
+image files
+on the command line.
+.It Cm XAmode1
+Set the write mode to produce data (XAmode1) tracks for the following image
+files on the command line.
+.It Cm XAmode2
+Set the write mode to produce data (XAmode2) tracks for the following image
+files on the command line.
+.It Cm vcd
+Set the write mode to produce VCD/SVCD tracks for the following image files
+on the command line.
+This automatically sets DAO
+.Pq Fl d
+and
+.Dq "no gaps"
+.Pq Fl n
+modes.
+.It Ar file
+All other arguments are treated as filenames of images to write to the media,
+or in case the
+.Fl l
+option is used as files containing lists of images.
+.El
+.Pp
+Files whose length are not a multiple of the current media blocksize are
+quietly zero padded to fit the blocksize requirement.
+The conventional filename
+.Fl
+refers to stdin, and can only be used once.
+.Sh EXAMPLES
+The typical usage for burning a data CD-R:
+.Pp
+.Dl "burncd -f /dev/acd0c data file1 fixate"
+.Pp
+The typical usage for burning an audio CD-R:
+.Pp
+.Dl "burncd -f /dev/acd0c audio file1 file2 file3 fixate"
+.Pp
+The typical usage for burning an audio CD-R in DAO mode:
+.Pp
+.Dl "burncd -f /dev/acd0c -d audio file1 file2 file3"
+.Pp
+The typical usage for burning a mixed mode CD-R:
+.Pp
+.Dl "burncd -f /dev/acd0c data file1 audio file2 file3 fixate"
+.Pp
+The typical usage for burning from a compressed image file on stdin:
+.Pp
+.Dl "gunzip -c file.iso.gz | burncd -f /dev/acd0c data - fixate"
+.Pp
+In the examples above, the files burned to data CD-Rs are assumed to
+be ISO9660 filesystems.
+.Xr mkisofs 8 ,
+available in the
+.Fx
+Ports Collection, is commonly used to create ISO9660 filesystem images
+from a given directory tree.
+.Sh BUGS
+Probably, please report when found.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Fx 4.0 .
+.Sh AUTHORS
+The
+.Nm
+command and this manpage was contributed by
+.An S\(/oren Schmidt ,
+Denmark
+.Aq sos@FreeBSD.org .
diff --git a/usr.sbin/burncd/burncd.c b/usr.sbin/burncd/burncd.c
new file mode 100644
index 0000000..31e7d51
--- /dev/null
+++ b/usr.sbin/burncd/burncd.c
@@ -0,0 +1,568 @@
+/*-
+ * Copyright (c) 2000,2001,2002 Søren Schmidt <sos@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <err.h>
+#include <sysexits.h>
+#include <fcntl.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/cdio.h>
+#include <sys/cdrio.h>
+#include <sys/param.h>
+#include <arpa/inet.h>
+
+#define BLOCKS 16
+
+struct track_info {
+ int file;
+ char *file_name;
+ u_int file_size;
+ int block_size;
+ int block_type;
+ int pregap;
+ int addr;
+};
+static struct track_info tracks[100];
+static int fd, quiet, verbose, saved_block_size, notracks;
+
+void add_track(char *, int, int, int);
+void do_DAO(int, int);
+void do_TAO(int, int);
+int write_file(struct track_info *);
+int roundup_blocks(struct track_info *);
+void cue_ent(struct cdr_cue_entry *, int, int, int, int, int, int, int);
+void cleanup(int);
+void usage(void);
+
+int
+main(int argc, char **argv)
+{
+ int ch, arg, addr;
+ int dao = 0, eject = 0, fixate = 0, list = 0, multi = 0, preemp = 0;
+ int nogap = 0, speed = 4, test_write = 0;
+ int block_size = 0, block_type = 0, cdopen = 0;
+ const char *dev = "/dev/acd0c";
+
+ while ((ch = getopt(argc, argv, "def:lmnpqs:tv")) != -1) {
+ switch (ch) {
+ case 'd':
+ dao = 1;
+ break;
+
+ case 'e':
+ eject = 1;
+ break;
+
+ case 'f':
+ dev = optarg;
+ break;
+
+ case 'l':
+ list = 1;
+ break;
+
+ case 'm':
+ multi = 1;
+ break;
+
+ case 'n':
+ nogap = 1;
+ break;
+
+ case 'p':
+ preemp = 1;
+ break;
+
+ case 'q':
+ quiet = 1;
+ break;
+
+ case 's':
+ speed = atoi(optarg);
+ if (speed <= 0)
+ errx(EX_USAGE, "Invalid speed: %s", optarg);
+ break;
+
+ case 't':
+ test_write = 1;
+ break;
+
+ case 'v':
+ verbose = 1;
+ break;
+
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 0)
+ usage();
+
+ if ((fd = open(dev, O_RDWR, 0)) < 0)
+ err(EX_NOINPUT, "open(%s)", dev);
+
+ if (ioctl(fd, CDRIOCGETBLOCKSIZE, &saved_block_size) < 0)
+ err(EX_IOERR, "ioctl(CDRIOCGETBLOCKSIZE)");
+
+ if (ioctl(fd, CDRIOCWRITESPEED, &speed) < 0)
+ err(EX_IOERR, "ioctl(CDRIOCWRITESPEED)");
+
+ err_set_exit(cleanup);
+
+ for (arg = 0; arg < argc; arg++) {
+ if (!strcasecmp(argv[arg], "fixate")) {
+ fixate = 1;
+ break;
+ }
+ if (!strcasecmp(argv[arg], "msinfo")) {
+ struct ioc_read_toc_single_entry entry;
+ struct ioc_toc_header header;
+
+ if (ioctl(fd, CDIOREADTOCHEADER, &header) < 0)
+ err(EX_IOERR, "ioctl(CDIOREADTOCHEADER)");
+ bzero(&entry, sizeof(struct ioc_read_toc_single_entry));
+ entry.address_format = CD_LBA_FORMAT;
+ entry.track = header.ending_track;
+ if (ioctl(fd, CDIOREADTOCENTRY, &entry) < 0)
+ err(EX_IOERR, "ioctl(CDIOREADTOCENTRY)");
+ if (ioctl(fd, CDRIOCNEXTWRITEABLEADDR, &addr) < 0)
+ err(EX_IOERR, "ioctl(CDRIOCNEXTWRITEABLEADDR)");
+ fprintf(stdout, "%d,%d\n",
+ ntohl(entry.entry.addr.lba), addr);
+
+ break;
+ }
+ if (!strcasecmp(argv[arg], "erase") || !strcasecmp(argv[arg], "blank")){
+ int error, blank, percent;
+
+ if (!strcasecmp(argv[arg], "erase"))
+ blank = CDR_B_ALL;
+ else
+ blank = CDR_B_MIN;
+ if (!quiet)
+ fprintf(stderr, "%sing CD, please wait..\r",
+ blank == CDR_B_ALL ? "eras" : "blank");
+
+ if (ioctl(fd, CDRIOCBLANK, &blank) < 0)
+ err(EX_IOERR, "ioctl(CDRIOCBLANK)");
+ while (1) {
+ sleep(1);
+ error = ioctl(fd, CDRIOCGETPROGRESS, &percent);
+ if (percent > 0 && !quiet)
+ fprintf(stderr,
+ "%sing CD - %d %% done \r",
+ blank == CDR_B_ALL ?
+ "eras" : "blank", percent);
+ if (error || percent == 100)
+ break;
+ }
+ if (!quiet)
+ printf("\n");
+ continue;
+ }
+ if (!strcasecmp(argv[arg], "audio") || !strcasecmp(argv[arg], "raw")) {
+ block_type = CDR_DB_RAW;
+ block_size = 2352;
+ continue;
+ }
+ if (!strcasecmp(argv[arg], "data") || !strcasecmp(argv[arg], "mode1")) {
+ block_type = CDR_DB_ROM_MODE1;
+ block_size = 2048;
+ continue;
+ }
+ if (!strcasecmp(argv[arg], "mode2")) {
+ block_type = CDR_DB_ROM_MODE2;
+ block_size = 2336;
+ continue;
+ }
+ if (!strcasecmp(argv[arg], "xamode1")) {
+ block_type = CDR_DB_XA_MODE1;
+ block_size = 2048;
+ continue;
+ }
+ if (!strcasecmp(argv[arg], "xamode2")) {
+ block_type = CDR_DB_XA_MODE2_F2;
+ block_size = 2324;
+ continue;
+ }
+ if (!strcasecmp(argv[arg], "vcd")) {
+ block_type = CDR_DB_XA_MODE2_F2;
+ block_size = 2352;
+ dao = 1;
+ nogap = 1;
+ continue;
+ }
+ if (!block_size)
+ err(EX_NOINPUT, "no data format selected");
+ if (list) {
+ char file_buf[MAXPATHLEN + 1], *eol;
+ FILE *fp;
+
+ if ((fp = fopen(argv[arg], "r")) == NULL)
+ err(EX_NOINPUT, "fopen(%s)", argv[arg]);
+
+ while (fgets(file_buf, sizeof(file_buf), fp) != NULL) {
+ if (*file_buf == '#' || *file_buf == '\n')
+ continue;
+ if ((eol = strchr(file_buf, '\n')))
+ *eol = NULL;
+ add_track(file_buf, block_size, block_type, nogap);
+ }
+ if (feof(fp))
+ fclose(fp);
+ else
+ err(EX_IOERR, "fgets(%s)", file_buf);
+ }
+ else
+ add_track(argv[arg], block_size, block_type, nogap);
+ }
+ if (notracks) {
+ if (ioctl(fd, CDIOCSTART, 0) < 0)
+ err(EX_IOERR, "ioctl(CDIOCSTART)");
+ if (!cdopen) {
+ if (ioctl(fd, CDRIOCINITWRITER, &test_write) < 0)
+ err(EX_IOERR, "ioctl(CDRIOCINITWRITER)");
+ cdopen = 1;
+ }
+ if (dao)
+ do_DAO(test_write, multi);
+ else
+ do_TAO(test_write, preemp);
+ }
+ if (fixate && !dao) {
+ if (!quiet)
+ fprintf(stderr, "fixating CD, please wait..\n");
+ if (ioctl(fd, CDRIOCFIXATE, &multi) < 0)
+ err(EX_IOERR, "ioctl(CDRIOCFIXATE)");
+ }
+
+ if (ioctl(fd, CDRIOCSETBLOCKSIZE, &saved_block_size) < 0) {
+ err_set_exit(NULL);
+ err(EX_IOERR, "ioctl(CDRIOCSETBLOCKSIZE)");
+ }
+
+ if (eject)
+ if (ioctl(fd, CDIOCEJECT) < 0)
+ err(EX_IOERR, "ioctl(CDIOCEJECT)");
+ close(fd);
+ exit(EX_OK);
+}
+
+void
+add_track(char *name, int block_size, int block_type, int nogap)
+{
+ struct stat sb;
+ int file;
+ static int done_stdin = 0;
+
+ if (!strcmp(name, "-")) {
+ if (done_stdin) {
+ warn("skipping multiple usages of stdin");
+ return;
+ }
+ file = STDIN_FILENO;
+ done_stdin = 1;
+ }
+ else if ((file = open(name, O_RDONLY, 0)) < 0)
+ err(EX_NOINPUT, "open(%s)", name);
+ if (fstat(file, &sb) < 0)
+ err(EX_IOERR, "fstat(%s)", name);
+ tracks[notracks].file = file;
+ tracks[notracks].file_name = name;
+ if (file == STDIN_FILENO)
+ tracks[notracks].file_size = -1;
+ else
+ tracks[notracks].file_size = sb.st_size;
+ tracks[notracks].block_size = block_size;
+ tracks[notracks].block_type = block_type;
+
+ if (nogap && notracks)
+ tracks[notracks].pregap = 0;
+ else {
+ if (tracks[notracks - (notracks > 0)].block_type == block_type)
+ tracks[notracks].pregap = 150;
+ else
+ tracks[notracks].pregap = 255;
+ }
+
+ if (verbose) {
+ int pad = 0;
+
+ if (tracks[notracks].file_size / tracks[notracks].block_size !=
+ roundup_blocks(&tracks[notracks]))
+ pad = 1;
+ fprintf(stderr,
+ "adding type 0x%02x file %s size %d KB %d blocks %s\n",
+ tracks[notracks].block_type, name, (int)sb.st_size/1024,
+ roundup_blocks(&tracks[notracks]),
+ pad ? "(0 padded)" : "");
+ }
+ notracks++;
+}
+
+void
+do_DAO(int test_write, int multi)
+{
+ struct cdr_cuesheet sheet;
+ struct cdr_cue_entry cue[100];
+ int format = CDR_SESS_CDROM;
+ int addr, i, j = 0;
+
+ int bt2ctl[16] = { 0x0, -1, -1, -1, -1, -1, -1, -1,
+ 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, -1, -1 };
+
+ int bt2df[16] = { 0x0, -1, -1, -1, -1, -1, -1, -1,
+ 0x10, 0x30, 0x20, -1, 0x21, -1, -1, -1 };
+
+ if (ioctl(fd, CDRIOCNEXTWRITEABLEADDR, &addr) < 0)
+ err(EX_IOERR, "ioctl(CDRIOCNEXTWRITEABLEADDR)");
+ if (verbose)
+ fprintf(stderr, "next writeable LBA %d\n", addr);
+
+ cue_ent(&cue[j++], bt2ctl[tracks[0].block_type], 0x01, 0x00, 0x0,
+ (bt2df[tracks[0].block_type] & 0xf0) |
+ (tracks[0].block_type < 8 ? 0x01 : 0x04), 0x00, addr);
+
+ for (i = 0; i < notracks; i++) {
+ if (bt2ctl[tracks[i].block_type] < 0 ||
+ bt2df[tracks[i].block_type] < 0)
+ err(EX_IOERR, "track type not supported in DAO mode");
+
+ if (tracks[i].block_type >= CDR_DB_XA_MODE1)
+ format = CDR_SESS_CDROM_XA;
+
+ if (i == 0) {
+ addr += tracks[i].pregap;
+ tracks[i].addr = addr;
+
+ cue_ent(&cue[j++], bt2ctl[tracks[i].block_type],
+ 0x01, i+1, 0x1, bt2df[tracks[i].block_type],
+ 0x00, addr);
+
+ }
+ else {
+ if (tracks[i].pregap) {
+ if (tracks[i].block_type > 0x7) {
+ cue_ent(&cue[j++],bt2ctl[tracks[i].block_type],
+ 0x01, i+1, 0x0,
+ (bt2df[tracks[i].block_type] & 0xf0) |
+ (tracks[i].block_type < 8 ? 0x01 :0x04),
+ 0x00, addr);
+ }
+ else
+ cue_ent(&cue[j++],bt2ctl[tracks[i].block_type],
+ 0x01, i+1, 0x0,
+ bt2df[tracks[i].block_type],
+ 0x00, addr);
+ }
+ tracks[i].addr = tracks[i - 1].addr +
+ roundup_blocks(&tracks[i - 1]);
+
+ cue_ent(&cue[j++], bt2ctl[tracks[i].block_type],
+ 0x01, i+1, 0x1, bt2df[tracks[i].block_type],
+ 0x00, addr + tracks[i].pregap);
+
+ if (tracks[i].block_type > 0x7)
+ addr += tracks[i].pregap;
+ }
+ addr += roundup_blocks(&tracks[i]);
+ }
+
+ cue_ent(&cue[j++], bt2ctl[tracks[i - 1].block_type], 0x01, 0xaa, 0x01,
+ (bt2df[tracks[i - 1].block_type] & 0xf0) |
+ (tracks[i - 1].block_type < 8 ? 0x01 : 0x04), 0x00, addr);
+
+ sheet.len = j * 8;
+ sheet.entries = cue;
+ sheet.test_write = test_write;
+ sheet.session_type = multi ? CDR_SESS_MULTI : CDR_SESS_NONE;
+ sheet.session_format = format;
+ if (verbose) {
+ u_int8_t *ptr = (u_int8_t *)sheet.entries;
+
+ fprintf(stderr,"CUE sheet:");
+ for (i = 0; i < sheet.len; i++)
+ if (i % 8)
+ fprintf(stderr," %02x", ptr[i]);
+ else
+ fprintf(stderr,"\n%02x", ptr[i]);
+ fprintf(stderr,"\n");
+ }
+
+ if (ioctl(fd, CDRIOCSENDCUE, &sheet) < 0)
+ err(EX_IOERR, "ioctl(CDRIOCSENDCUE)");
+
+ for (i = 0; i < notracks; i++) {
+ if (write_file(&tracks[i]))
+ err(EX_IOERR, "write_file");
+ }
+
+ ioctl(fd, CDRIOCFLUSH);
+}
+
+void
+do_TAO(int test_write, int preemp)
+{
+ struct cdr_track track;
+ int i;
+
+ for (i = 0; i < notracks; i++) {
+ track.test_write = test_write;
+ track.datablock_type = tracks[i].block_type;
+ track.preemp = preemp;
+ if (ioctl(fd, CDRIOCINITTRACK, &track) < 0)
+ err(EX_IOERR, "ioctl(CDRIOCINITTRACK)");
+
+ if (ioctl(fd, CDRIOCNEXTWRITEABLEADDR, &tracks[i].addr) < 0)
+ err(EX_IOERR, "ioctl(CDRIOCNEXTWRITEABLEADDR)");
+ if (!quiet)
+ fprintf(stderr, "next writeable LBA %d\n",
+ tracks[i].addr);
+ if (write_file(&tracks[i]))
+ err(EX_IOERR, "write_file");
+ if (ioctl(fd, CDRIOCFLUSH) < 0)
+ err(EX_IOERR, "ioctl(CDRIOCFLUSH)");
+ }
+}
+
+int
+write_file(struct track_info *track_info)
+{
+ int size, count, filesize;
+ char buf[2352*BLOCKS];
+ static int tot_size = 0;
+
+ filesize = track_info->file_size / 1024;
+
+ if (ioctl(fd, CDRIOCSETBLOCKSIZE, &track_info->block_size) < 0)
+ err(EX_IOERR, "ioctl(CDRIOCSETBLOCKSIZE)");
+
+ if (track_info->addr >= 0)
+ lseek(fd, track_info->addr * track_info->block_size, SEEK_SET);
+
+ if (verbose)
+ fprintf(stderr, "addr = %d size = %d blocks = %d\n",
+ track_info->addr, track_info->file_size,
+ roundup_blocks(track_info));
+
+ if (!quiet) {
+ if (track_info->file == STDIN_FILENO)
+ fprintf(stderr, "writing from stdin\n");
+ else
+ fprintf(stderr,
+ "writing from file %s size %d KB\n",
+ track_info->file_name, filesize);
+ }
+ size = 0;
+
+ while ((count = read(track_info->file, buf,
+ MIN((track_info->file_size - size),
+ track_info->block_size * BLOCKS))) > 0) {
+ int res;
+
+ if (count % track_info->block_size) {
+ /* pad file to % block_size */
+ bzero(&buf[count],
+ (track_info->block_size * BLOCKS) - count);
+ count = ((count / track_info->block_size) + 1) *
+ track_info->block_size;
+ }
+ if ((res = write(fd, buf, count)) != count) {
+ fprintf(stderr, "\nonly wrote %d of %d bytes err=%d\n",
+ res, count, errno);
+ break;
+ }
+ size += count;
+ tot_size += count;
+ if (!quiet) {
+ int pct;
+
+ fprintf(stderr, "written this track %d KB", size/1024);
+ if (track_info->file != STDIN_FILENO && filesize) {
+ pct = (size / 1024) * 100 / filesize;
+ fprintf(stderr, " (%d%%)", pct);
+ }
+ fprintf(stderr, " total %d KB\r", tot_size/1024);
+ }
+ if (size >= track_info->file_size)
+ break;
+ }
+
+ if (!quiet)
+ fprintf(stderr, "\n");
+ close(track_info->file);
+ return 0;
+}
+
+int
+roundup_blocks(struct track_info *track)
+{
+ return ((track->file_size + track->block_size - 1) / track->block_size);
+}
+
+void
+cue_ent(struct cdr_cue_entry *cue, int ctl, int adr, int track, int idx,
+ int dataform, int scms, int lba)
+{
+ cue->adr = adr;
+ cue->ctl = ctl;
+ cue->track = track;
+ cue->index = idx;
+ cue->dataform = dataform;
+ cue->scms = scms;
+ lba += 150;
+ cue->min = lba / (60*75);
+ cue->sec = (lba % (60*75)) / 75;
+ cue->frame = (lba % (60*75)) % 75;
+}
+
+void
+cleanup(int dummy __unused)
+{
+ if (ioctl(fd, CDRIOCSETBLOCKSIZE, &saved_block_size) < 0)
+ err(EX_IOERR, "ioctl(CDRIOCSETBLOCKSIZE)");
+}
+
+void
+usage(void)
+{
+ fprintf(stderr,
+ "usage: %s [-delmnpqtv] [-f device] [-s speed] [command]"
+ " [command file ...]\n", getprogname());
+ exit(EX_USAGE);
+}
diff --git a/usr.sbin/cdcontrol/Makefile b/usr.sbin/cdcontrol/Makefile
new file mode 100644
index 0000000..0f4d993
--- /dev/null
+++ b/usr.sbin/cdcontrol/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PROG= cdcontrol
+WARNS?= 2
+
+DPADD= ${LIBEDIT} ${LIBTERMCAP}
+LDADD= -ledit -ltermcap
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/cdcontrol/cdcontrol.1 b/usr.sbin/cdcontrol/cdcontrol.1
new file mode 100644
index 0000000..3f986fe
--- /dev/null
+++ b/usr.sbin/cdcontrol/cdcontrol.1
@@ -0,0 +1,199 @@
+.\" $FreeBSD$
+.\"
+.Dd May 8, 2002
+.Dt CDCONTROL 1
+.Os
+.Sh NAME
+.Nm cdcontrol
+.Nd compact disc control utility
+.Sh SYNOPSIS
+.Nm
+.Op Fl sv
+.Op Fl f Ar device
+.Op Ar command ...
+.Sh DESCRIPTION
+The
+.Nm
+utility is a program to control audio features of a CD drive.
+The device is a name such
+as
+.Pa cd0
+or
+.Pa mcd0 .
+.Pp
+If no
+.Ar command
+is given, then
+.Nm
+enters an interactive mode, reading commands from the standard input.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl s
+Silent mode.
+Do not print table headers and human readable comments.
+.It Fl v
+Verbose mode.
+Print as much information as possible.
+.It Fl f Ar device
+Specify a device, such as
+.Pa /dev/cd0c
+or
+.Pa mcd0 .
+Both absolute path and relative to
+.Pa /dev
+filename are possible.
+Suffix
+.Pa 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.
+The word
+.Ic play
+can be omitted or the characters
+.Ic +
+and
+.Ic -
+can be used in the
+place of
+.Ic next
+and
+.Ic prev .
+.Bl -tag -width indent
+.It Ic play Ar first_track Op Ar last_track
+Play from track
+.Ar first_track
+to track
+.Ar last_track .
+The first track has number 1.
+Can be omitted in all cases.
+.It Xo
+.Ic play
+.Ar start_m : Ns Ar start_s . Ns Ar start_f
+.Op Ar end_m : Ns Ar end_s . Ns Ar end_f
+.Xc
+Play from the absolute address
+(MSF) defined by
+.Ar start_m
+in minutes,
+.Ar start_s ,
+in seconds and
+.Ar start_f
+(frame number) to the absolute address defined by
+.Ar end_m
+in minutes,
+.Ar end_s ,
+in seconds and
+.Ar end_f
+(frame number).
+Minutes are in the range 0-99.
+Seconds are in the range 0-59.
+Frame numbers are in the range 0-74.
+.It Ic play Op # Ns Ar start_block Op Ar length
+Play starting from the logical block
+.Ar start_block
+using
+.Ar length
+logical blocks.
+.It Ic next Op Ar tracks
+Skip forward a number of tracks (default 1).
+.It Ic prev Op Ar tracks
+Skip backward a number of tracks (default 1).
+.It Ic pause
+Stop playing.
+Do not stop the disc.
+.It Ic resume
+Resume playing.
+Used after the
+.Ic pause
+command.
+.It Ic stop
+Stop the disc.
+.It Ic eject
+Eject the disc.
+.It Ic close
+Inject the disc.
+.It Ic volume Ar left_channel right_channel
+Set the volume of left channel to
+.Ar left_channel
+and the volume of right channel to
+.Ar right_channel .
+Allowed values are in the range 0-255.
+.It Ic volume Cm mute
+Turn the sound off.
+.It Ic volume Cm mono
+Set the mono mode.
+.It Ic volume Cm stereo
+Set the stereo mode.
+.It Ic volume Cm left
+Play the left subtrack on both left and right channels.
+.It Ic volume Cm right
+Play the right subtrack on both left and right channels.
+.It Ic info
+Print the table of contents.
+.It Ic status Op Cm audio | media | volume
+Print the information about the disc:
+.Pp
+.Bl -tag -width ".Cm volume" -compact
+.It Cm audio
+the current playing status and position
+.It Cm media
+the current media catalog status
+.It Cm volume
+the current values of the volume for left and right channels.
+.El
+.It Ic cdid
+Display the serial number of the CD using the method used by the
+.Tn CDDB
+project
+.Pq Pa http://www.cddb.org/ .
+.It Ic help
+Print the list of available commands.
+.It Ic debug Cm on
+Enable the debugging mode of the CD device driver.
+.It Ic debug Cm off
+Disable the driver debugging mode.
+.It Ic reset
+Perform the hardware reset of the device.
+.It Ic set Cm msf
+Set minute-second-frame ioctl mode (default).
+.It Ic set Cm lba
+Set LBA ioctl mode.
+.It Ic speed Ar s
+Set the highest speed that the drive should use.
+The speed is a multiple of the single speed.
+This command is currently only supported on ATAPI drives.
+.It Ic quit
+Quit the program.
+.El
+.Sh ENVIRONMENT
+The following environment variables affect the execution of
+.Nm :
+.Bl -tag -width ".Ev CD_DRIVE"
+.It Ev CDROM
+The CD device to use, if one is not specified with the
+.Fl f
+option.
+.It Ev CDPLAY , CD_DRIVE , DISC , MUSIC_CD
+These variables have been deprecated in favour of
+.Ev CDROM .
+.El
+.Sh FILES
+.Bl -tag -width ".Pa /dev/mcd0c" -compact
+.It Pa /dev/cd0c
+.It Pa /dev/mcd0c
+.It Pa /dev/acd0c
+.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..e41528e
--- /dev/null
+++ b/usr.sbin/cdcontrol/cdcontrol.c
@@ -0,0 +1,1269 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/cdio.h>
+#include <sys/cdrio.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/param.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <histedit.h>
+#include <limits.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <vis.h>
+
+#define VERSION "2.0"
+
+#define ASTS_INVALID 0x00 /* Audio status byte not valid */
+#define ASTS_PLAYING 0x11 /* Audio play operation in progress */
+#define ASTS_PAUSED 0x12 /* Audio play operation paused */
+#define ASTS_COMPLETED 0x13 /* Audio play operation successfully completed */
+#define ASTS_ERROR 0x14 /* Audio play operation stopped due to error */
+#define ASTS_VOID 0x15 /* No current audio status to return */
+
+#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 CMD_CDID 15
+#define CMD_NEXT 16
+#define CMD_PREVIOUS 17
+#define CMD_SPEED 18
+#define STATUS_AUDIO 0x1
+#define STATUS_MEDIA 0x2
+#define STATUS_VOLUME 0x4
+
+struct cmdtab {
+ int command;
+ const char *name;
+ unsigned min;
+ const char *args;
+} cmdtab[] = {
+{ CMD_CLOSE, "close", 1, "" },
+{ CMD_DEBUG, "debug", 1, "on | off" },
+{ CMD_EJECT, "eject", 1, "" },
+{ CMD_HELP, "?", 1, 0 },
+{ CMD_HELP, "help", 1, "" },
+{ CMD_INFO, "info", 1, "" },
+{ CMD_NEXT, "next", 1, "" },
+{ CMD_PAUSE, "pause", 2, "" },
+{ CMD_PLAY, "play", 1, "min1:sec1[.fram1] [min2:sec2[.fram2]]" },
+{ CMD_PLAY, "play", 1, "track1[.index1] [track2[.index2]]" },
+{ CMD_PLAY, "play", 1, "tr1 m1:s1[.f1] [[tr2] [m2:s2[.f2]]]" },
+{ CMD_PLAY, "play", 1, "[#block [len]]" },
+{ CMD_PREVIOUS, "previous", 2, "" },
+{ CMD_QUIT, "quit", 1, "" },
+{ CMD_RESET, "reset", 4, "" },
+{ CMD_RESUME, "resume", 1, "" },
+{ CMD_SET, "set", 2, "msf | lba" },
+{ CMD_STATUS, "status", 1, "[audio | media | volume]" },
+{ CMD_STOP, "stop", 3, "" },
+{ CMD_VOLUME, "volume", 1,
+ "<l> <r> | left | right | mute | mono | stereo" },
+{ CMD_CDID, "cdid", 2, "" },
+{ CMD_SPEED, "speed", 2, "speed" },
+{ 0, NULL, 0, NULL }
+};
+
+struct cd_toc_entry toc_buffer[100];
+
+const char *cdname;
+int fd = -1;
+int verbose = 1;
+int msf = 1;
+
+int setvol __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 next_prev __P((char *arg, int));
+int play __P((char *arg));
+int info __P((char *arg));
+int cdid __P((void));
+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 __P((void));
+void usage __P((void));
+char *use_cdrom_instead __P((const char *));
+__const char *strstatus __P((int));
+static u_int dbprog_discid __P((void));
+__const char *cdcontrol_prompt __P((void));
+
+void help ()
+{
+ struct cmdtab *c;
+ const char *s;
+ char n;
+ int i;
+
+ for (c=cmdtab; c->name; ++c) {
+ if (! c->args)
+ continue;
+ printf("\t");
+ for (i = c->min, s = c->name; *s; s++, i--) {
+ if (i > 0)
+ n = toupper(*s);
+ else
+ n = *s;
+ putchar(n);
+ }
+ if (*c->args)
+ printf (" %s", c->args);
+ printf ("\n");
+ }
+ printf ("\n\tThe word \"play\" is not required for the play commands.\n");
+ printf ("\tThe plain target address is taken as a synonym for play.\n");
+}
+
+void usage ()
+{
+ fprintf (stderr, "usage: cdcontrol [-sv] [-f device] [command ...]\n");
+ exit (1);
+}
+
+char *use_cdrom_instead(const char *old_envvar)
+{
+ char *device;
+
+ device = getenv(old_envvar);
+ if (device)
+ warnx("%s environment variable deprecated, "
+ "please use CDROM in the future.", old_envvar);
+ return device;
+}
+
+
+int main (int argc, char **argv)
+{
+ int cmd;
+ char *arg;
+
+ for (;;) {
+ switch (getopt (argc, argv, "svhf:")) {
+ case EOF:
+ break;
+ case 's':
+ verbose = 0;
+ continue;
+ case 'v':
+ verbose = 2;
+ continue;
+ case 'f':
+ cdname = optarg;
+ continue;
+ case 'h':
+ default:
+ usage ();
+ }
+ break;
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc > 0 && ! strcasecmp (*argv, "help"))
+ usage ();
+
+ if (! cdname) {
+ cdname = getenv("CDROM");
+ }
+
+ if (! cdname)
+ cdname = use_cdrom_instead("MUSIC_CD");
+ if (! cdname)
+ cdname = use_cdrom_instead("CD_DRIVE");
+ if (! cdname)
+ cdname = use_cdrom_instead("DISC");
+ if (! cdname)
+ cdname = use_cdrom_instead("CDPLAY");
+
+ if (! 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)
+{
+ long speed;
+ int l, r, rc;
+ char *ep;
+
+ switch (cmd) {
+
+ case CMD_QUIT:
+ exit (0);
+
+ case CMD_INFO:
+ if (fd < 0 && ! open_cd ())
+ return (0);
+
+ return info (arg);
+
+ case CMD_CDID:
+ if (fd < 0 && ! open_cd ())
+ return (0);
+
+ return cdid ();
+
+ case CMD_STATUS:
+ if (fd < 0 && ! open_cd ())
+ return (0);
+
+ return pstatus (arg);
+
+ case CMD_NEXT:
+ case CMD_PREVIOUS:
+ if (fd < 0 && ! open_cd ())
+ return (0);
+
+ while (isspace (*arg))
+ arg++;
+
+ return next_prev (arg, cmd);
+
+ case CMD_PAUSE:
+ if (fd < 0 && ! open_cd ())
+ return (0);
+
+ return ioctl (fd, CDIOCPAUSE);
+
+ case CMD_RESUME:
+ if (fd < 0 && ! open_cd ())
+ return (0);
+
+ return ioctl (fd, CDIOCRESUME);
+
+ case CMD_STOP:
+ if (fd < 0 && ! open_cd ())
+ return (0);
+
+ rc = ioctl (fd, CDIOCSTOP);
+
+ (void) ioctl (fd, CDIOCALLOW);
+
+ return (rc);
+
+ case CMD_RESET:
+ if (fd < 0 && ! open_cd ())
+ return (0);
+
+ rc = ioctl (fd, CDIOCRESET);
+ if (rc < 0)
+ return rc;
+ close(fd);
+ fd = -1;
+ return (0);
+
+ case CMD_DEBUG:
+ if (fd < 0 && ! open_cd ())
+ return (0);
+
+ if (! strcasecmp (arg, "on"))
+ return ioctl (fd, CDIOCSETDEBUG);
+
+ if (! strcasecmp (arg, "off"))
+ return ioctl (fd, CDIOCCLRDEBUG);
+
+ warnx("invalid command arguments");
+
+ return (0);
+
+ case CMD_EJECT:
+ if (fd < 0 && ! open_cd ())
+ return (0);
+
+ (void) ioctl (fd, CDIOCALLOW);
+ rc = ioctl (fd, CDIOCEJECT);
+ if (rc < 0)
+ return (rc);
+ return (0);
+
+ case CMD_CLOSE:
+ if (fd < 0 && ! open_cd ())
+ return (0);
+
+ (void) ioctl (fd, CDIOCALLOW);
+ rc = ioctl (fd, CDIOCCLOSE);
+ if (rc < 0)
+ return (rc);
+ close(fd);
+ fd = -1;
+ return (0);
+
+ case CMD_PLAY:
+ if (fd < 0 && ! open_cd ())
+ return (0);
+
+ while (isspace (*arg))
+ arg++;
+
+ return play (arg);
+
+ case CMD_SET:
+ if (! strcasecmp (arg, "msf"))
+ msf = 1;
+ else if (! strcasecmp (arg, "lba"))
+ msf = 0;
+ else
+ warnx("invalid command arguments");
+ return (0);
+
+ case CMD_VOLUME:
+ if (fd < 0 && !open_cd ())
+ return (0);
+
+ if (! strncasecmp (arg, "left", strlen(arg)))
+ return ioctl (fd, CDIOCSETLEFT);
+
+ if (! strncasecmp (arg, "right", strlen(arg)))
+ return ioctl (fd, CDIOCSETRIGHT);
+
+ if (! strncasecmp (arg, "mono", strlen(arg)))
+ return ioctl (fd, CDIOCSETMONO);
+
+ if (! strncasecmp (arg, "stereo", strlen(arg)))
+ return ioctl (fd, CDIOCSETSTERIO);
+
+ if (! strncasecmp (arg, "mute", strlen(arg)))
+ return ioctl (fd, CDIOCSETMUTE);
+
+ if (2 != sscanf (arg, "%d %d", &l, &r)) {
+ warnx("invalid command arguments");
+ return (0);
+ }
+
+ return setvol (l, r);
+
+ case CMD_SPEED:
+ if (fd < 0 && ! open_cd ())
+ return (0);
+
+ errno = 0;
+ speed = strtol(arg, &ep, 10);
+ if (*ep || ep == arg || speed <= 0 || speed > INT_MAX ||
+ errno != 0) {
+ warnx("invalid command arguments %s", arg);
+ return (0);
+ }
+ return ioctl(fd, CDRIOCREADSPEED, &speed);
+
+ default:
+ case CMD_HELP:
+ help ();
+ return (0);
+
+ }
+}
+
+int play (char *arg)
+{
+ struct ioc_toc_header h;
+ unsigned int n;
+ int rc, start, end = 0, istart = 1, iend = 1;
+
+ rc = ioctl (fd, CDIOREADTOCHEADER, &h);
+
+ if (rc < 0)
+ return (rc);
+
+ n = h.ending_track - h.starting_track + 1;
+ rc = read_toc_entrys ((n + 1) * sizeof (struct cd_toc_entry));
+
+ if (rc < 0)
+ return (rc);
+
+ if (! arg || ! *arg) {
+ /* Play the whole disc */
+ if (msf)
+ return play_blocks (0, msf2lba (toc_buffer[n].addr.msf.minute,
+ toc_buffer[n].addr.msf.second,
+ toc_buffer[n].addr.msf.frame));
+ else
+ return play_blocks (0, ntohl(toc_buffer[n].addr.lba));
+ }
+
+ if (strchr (arg, '#')) {
+ /* Play block #blk [ len ] */
+ int blk, len = 0;
+
+ if (2 != sscanf (arg, "#%d%d", &blk, &len) &&
+ 1 != sscanf (arg, "#%d", &blk))
+ goto Clean_up;
+
+ if (len == 0) {
+ if (msf)
+ len = msf2lba (toc_buffer[n].addr.msf.minute,
+ toc_buffer[n].addr.msf.second,
+ toc_buffer[n].addr.msf.frame) - blk;
+ else
+ len = ntohl(toc_buffer[n].addr.lba) - blk;
+ }
+ return play_blocks (blk, len);
+ }
+
+ if (strchr (arg, ':')) {
+ /*
+ * Play MSF m1:s1 [ .f1 ] [ m2:s2 [ .f2 ] ]
+ *
+ * Will now also undestand timed addresses relative
+ * to the beginning of a track in the form...
+ *
+ * tr1 m1:s1[.f1] [[tr2] [m2:s2[.f2]]]
+ */
+ unsigned tr1, tr2;
+ unsigned m1, m2, s1, s2, f1, f2;
+ unsigned char tm, ts, tf;
+
+ tr2 = m2 = s2 = f2 = f1 = 0;
+ if (8 == sscanf (arg, "%d %d:%d.%d %d %d:%d.%d",
+ &tr1, &m1, &s1, &f1, &tr2, &m2, &s2, &f2))
+ goto Play_Relative_Addresses;
+
+ tr2 = m2 = s2 = f2 = f1 = 0;
+ if (7 == sscanf (arg, "%d %d:%d %d %d:%d.%d",
+ &tr1, &m1, &s1, &tr2, &m2, &s2, &f2))
+ goto Play_Relative_Addresses;
+
+ tr2 = m2 = s2 = f2 = f1 = 0;
+ if (7 == sscanf (arg, "%d %d:%d.%d %d %d:%d",
+ &tr1, &m1, &s1, &f1, &tr2, &m2, &s2))
+ goto Play_Relative_Addresses;
+
+ tr2 = m2 = s2 = f2 = f1 = 0;
+ if (7 == sscanf (arg, "%d %d:%d.%d %d:%d.%d",
+ &tr1, &m1, &s1, &f1, &m2, &s2, &f2))
+ goto Play_Relative_Addresses;
+
+ tr2 = m2 = s2 = f2 = f1 = 0;
+ if (6 == sscanf (arg, "%d %d:%d.%d %d:%d",
+ &tr1, &m1, &s1, &f1, &m2, &s2))
+ goto Play_Relative_Addresses;
+
+ tr2 = m2 = s2 = f2 = f1 = 0;
+ if (6 == sscanf (arg, "%d %d:%d %d:%d.%d",
+ &tr1, &m1, &s1, &m2, &s2, &f2))
+ goto Play_Relative_Addresses;
+
+ tr2 = m2 = s2 = f2 = f1 = 0;
+ if (6 == sscanf (arg, "%d %d:%d.%d %d %d",
+ &tr1, &m1, &s1, &f1, &tr2, &m2))
+ goto Play_Relative_Addresses;
+
+ tr2 = m2 = s2 = f2 = f1 = 0;
+ if (5 == sscanf (arg, "%d %d:%d %d:%d", &tr1, &m1, &s1, &m2, &s2))
+ goto Play_Relative_Addresses;
+
+ tr2 = m2 = s2 = f2 = f1 = 0;
+ if (5 == sscanf (arg, "%d %d:%d %d %d",
+ &tr1, &m1, &s1, &tr2, &m2))
+ goto Play_Relative_Addresses;
+
+ tr2 = m2 = s2 = f2 = f1 = 0;
+ if (5 == sscanf (arg, "%d %d:%d.%d %d",
+ &tr1, &m1, &s1, &f1, &tr2))
+ goto Play_Relative_Addresses;
+
+ tr2 = m2 = s2 = f2 = f1 = 0;
+ if (4 == sscanf (arg, "%d %d:%d %d", &tr1, &m1, &s1, &tr2))
+ goto Play_Relative_Addresses;
+
+ tr2 = m2 = s2 = f2 = f1 = 0;
+ if (4 == sscanf (arg, "%d %d:%d.%d", &tr1, &m1, &s1, &f1))
+ goto Play_Relative_Addresses;
+
+ tr2 = m2 = s2 = f2 = f1 = 0;
+ if (3 == sscanf (arg, "%d %d:%d", &tr1, &m1, &s1))
+ goto Play_Relative_Addresses;
+
+ tr2 = m2 = s2 = f2 = f1 = 0;
+ goto Try_Absolute_Timed_Addresses;
+
+Play_Relative_Addresses:
+ if (! tr1)
+ tr1 = 1;
+ else if (tr1 > n)
+ tr1 = n;
+
+ 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);
+}
+
+int next_prev (char *arg, int cmd)
+{
+ struct ioc_toc_header h;
+ int dir, junk, n, off, rc, trk;
+
+ dir = (cmd == CMD_NEXT) ? 1 : -1;
+ rc = ioctl (fd, CDIOREADTOCHEADER, &h);
+ if (rc < 0)
+ return (rc);
+
+ n = h.ending_track - h.starting_track + 1;
+ rc = status (&trk, &junk, &junk, &junk);
+ if (rc < 0)
+ return (-1);
+
+ if (arg && *arg) {
+ if (sscanf (arg, "%u", &off) != 1) {
+ warnx("invalid command argument");
+ return (0);
+ } else
+ trk += off * dir;
+ } else
+ trk += dir;
+
+ if (trk > h.ending_track)
+ trk = 1;
+
+ return (play_track (trk, 1, n, 1));
+}
+
+const char *strstatus (int sts)
+{
+ switch (sts) {
+ case ASTS_INVALID: return ("invalid");
+ case ASTS_PLAYING: return ("playing");
+ case ASTS_PAUSED: return ("paused");
+ case ASTS_COMPLETED: return ("completed");
+ case ASTS_ERROR: return ("error");
+ case ASTS_VOID: return ("void");
+ default: return ("??");
+ }
+}
+
+int pstatus (char *arg)
+{
+ struct ioc_vol v;
+ struct ioc_read_subchannel ss;
+ struct cd_sub_channel_info data;
+ int rc, trk, m, s, f;
+ int what = 0;
+ char *p, vmcn[(4 * 15) + 1];
+
+ while ((p = strtok(arg, " \t"))) {
+ arg = 0;
+ if (!strncasecmp(p, "audio", strlen(p)))
+ what |= STATUS_AUDIO;
+ else if (!strncasecmp(p, "media", strlen(p)))
+ what |= STATUS_MEDIA;
+ else if (!strncasecmp(p, "volume", strlen(p)))
+ what |= STATUS_VOLUME;
+ else {
+ warnx("invalid command arguments");
+ return 0;
+ }
+ }
+ if (!what)
+ what = STATUS_AUDIO|STATUS_MEDIA|STATUS_VOLUME;
+ if (what & STATUS_AUDIO) {
+ rc = status (&trk, &m, &s, &f);
+ if (rc >= 0)
+ if (verbose)
+ printf ("Audio status = %d<%s>, current track = %d, current position = %d:%02d.%02d\n",
+ rc, strstatus (rc), trk, m, s, f);
+ else
+ printf ("%d %d %d:%02d.%02d\n", rc, trk, m, s, f);
+ else
+ printf ("No current status info available\n");
+ }
+ if (what & STATUS_MEDIA) {
+ bzero (&ss, sizeof (ss));
+ ss.data = &data;
+ ss.data_len = sizeof (data);
+ ss.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT;
+ ss.data_format = CD_MEDIA_CATALOG;
+ rc = ioctl (fd, CDIOCREADSUBCHANNEL, (char *) &ss);
+ if (rc >= 0) {
+ printf("Media catalog is %sactive",
+ ss.data->what.media_catalog.mc_valid ? "": "in");
+ if (ss.data->what.media_catalog.mc_valid &&
+ ss.data->what.media_catalog.mc_number[0])
+ {
+ strvisx (vmcn, ss.data->what.media_catalog.mc_number,
+ (sizeof (vmcn) - 1) / 4, VIS_OCTAL | VIS_NL);
+ printf(", number \"%.*s\"", (int)sizeof (vmcn), vmcn);
+ }
+ putchar('\n');
+ } else
+ printf("No media catalog info available\n");
+ }
+ if (what & STATUS_VOLUME) {
+ rc = ioctl (fd, CDIOCGETVOL, &v);
+ if (rc >= 0)
+ if (verbose)
+ printf ("Left volume = %d, right volume = %d\n",
+ v.vol[0], v.vol[1]);
+ else
+ printf ("%d %d\n", v.vol[0], v.vol[1]);
+ else
+ printf ("No volume level info available\n");
+ }
+ return(0);
+}
+
+/*
+ * dbprog_sum
+ * Convert an integer to its text string representation, and
+ * compute its checksum. Used by dbprog_discid to derive the
+ * disc ID.
+ *
+ * Args:
+ * n - The integer value.
+ *
+ * Return:
+ * The integer checksum.
+ */
+static int
+dbprog_sum(int n)
+{
+ char buf[12],
+ *p;
+ int ret = 0;
+
+ /* For backward compatibility this algorithm must not change */
+ sprintf(buf, "%u", n);
+ for (p = buf; *p != '\0'; p++)
+ ret += (*p - '0');
+
+ return(ret);
+}
+
+
+/*
+ * dbprog_discid
+ * Compute a magic disc ID based on the number of tracks,
+ * the length of each track, and a checksum of the string
+ * that represents the offset of each track.
+ *
+ * Args:
+ * s - Pointer to the curstat_t structure.
+ *
+ * Return:
+ * The integer disc ID.
+ */
+static u_int
+dbprog_discid()
+{
+ struct ioc_toc_header h;
+ int rc;
+ int i, ntr,
+ t = 0,
+ n = 0;
+
+ rc = ioctl (fd, CDIOREADTOCHEADER, &h);
+ if (rc < 0)
+ return 0;
+ ntr = h.ending_track - h.starting_track + 1;
+ i = msf;
+ msf = 1;
+ rc = read_toc_entrys ((ntr + 1) * sizeof (struct cd_toc_entry));
+ msf = i;
+ if (rc < 0)
+ return 0;
+ /* For backward compatibility this algorithm must not change */
+ for (i = 0; i < ntr; i++) {
+#define TC_MM(a) toc_buffer[a].addr.msf.minute
+#define TC_SS(a) toc_buffer[a].addr.msf.second
+ n += dbprog_sum((TC_MM(i) * 60) + TC_SS(i));
+
+ t += ((TC_MM(i+1) * 60) + TC_SS(i+1)) -
+ ((TC_MM(i) * 60) + TC_SS(i));
+ }
+
+ return((n % 0xff) << 24 | t << 8 | ntr);
+}
+
+int cdid ()
+{
+ u_int id;
+
+ id = dbprog_discid();
+ if (id)
+ {
+ if (verbose)
+ printf ("CDID=");
+ printf ("%08x\n",id);
+ }
+ return id ? 0 : 1;
+}
+
+int info (char *arg __unused)
+{
+ struct ioc_toc_header h;
+ int rc, i, n;
+
+ rc = ioctl (fd, CDIOREADTOCHEADER, &h);
+ if (rc >= 0) {
+ if (verbose)
+ printf ("Starting track = %d, ending track = %d, TOC size = %d bytes\n",
+ h.starting_track, h.ending_track, h.len);
+ else
+ printf ("%d %d %d\n", h.starting_track,
+ h.ending_track, h.len);
+ } else {
+ warn("getting toc header");
+ return (rc);
+ }
+
+ n = h.ending_track - h.starting_track + 1;
+ rc = read_toc_entrys ((n + 1) * sizeof (struct cd_toc_entry));
+ if (rc < 0)
+ return (rc);
+
+ if (verbose) {
+ printf ("track start duration block length type\n");
+ printf ("-------------------------------------------------\n");
+ }
+
+ for (i = 0; i < n; i++) {
+ printf ("%5d ", toc_buffer[i].track);
+ prtrack (toc_buffer + i, 0);
+ }
+ printf ("%5d ", toc_buffer[n].track);
+ prtrack (toc_buffer + n, 1);
+ return (0);
+}
+
+void lba2msf (unsigned long lba, u_char *m, u_char *s, u_char *f)
+{
+ lba += 150; /* block start offset */
+ lba &= 0xffffff; /* negative lbas use only 24 bits */
+ *m = lba / (60 * 75);
+ lba %= (60 * 75);
+ *s = lba / 75;
+ *f = lba % 75;
+}
+
+unsigned int msf2lba (u_char m, u_char s, u_char f)
+{
+ return (((m * 60) + s) * 75 + f) - 150;
+}
+
+void prtrack (struct cd_toc_entry *e, int lastflag)
+{
+ int block, next, len;
+ u_char m, s, f;
+
+ if (msf) {
+ /* Print track start */
+ printf ("%2d:%02d.%02d ", e->addr.msf.minute,
+ e->addr.msf.second, e->addr.msf.frame);
+
+ block = msf2lba (e->addr.msf.minute, e->addr.msf.second,
+ e->addr.msf.frame);
+ } else {
+ block = ntohl(e->addr.lba);
+ lba2msf(block, &m, &s, &f);
+ /* Print track start */
+ printf ("%2d:%02d.%02d ", m, s, f);
+ }
+ if (lastflag) {
+ /* Last track -- print block */
+ printf (" - %6d - -\n", block);
+ return;
+ }
+
+ if (msf)
+ next = msf2lba (e[1].addr.msf.minute, e[1].addr.msf.second,
+ e[1].addr.msf.frame);
+ else
+ next = ntohl(e[1].addr.lba);
+ len = next - block;
+ 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;
+}
+
+const char *
+cdcontrol_prompt()
+{
+ return ("cdcontrol> ");
+}
+
+char *
+input (int *cmd)
+{
+#define MAXLINE 80
+ static EditLine *el = NULL;
+ static History *hist = NULL;
+ HistEvent he;
+ static char buf[MAXLINE];
+ int num = 0;
+ int len;
+ const char *bp = NULL;
+ char *p;
+
+ do {
+ if (verbose) {
+ if (!el) {
+ el = el_init("cdcontrol", stdin, stdout,
+ stderr);
+ hist = history_init();
+ history(hist, &he, H_EVENT, 100);
+ el_set(el, EL_HIST, history, hist);
+ el_set(el, EL_EDITOR, "emacs");
+ el_set(el, EL_PROMPT, cdcontrol_prompt);
+ el_set(el, EL_SIGNAL, 1);
+ el_source(el, NULL);
+ }
+ if ((bp = el_gets(el, &num)) == NULL || num == 0) {
+ *cmd = CMD_QUIT;
+ fprintf (stderr, "\r\n");
+ return (0);
+ }
+
+ len = (num > MAXLINE) ? MAXLINE : num;
+ memcpy(buf, bp, len);
+ buf[len] = 0;
+ history(hist, &he, H_ENTER, bp);
+#undef MAXLINE
+
+ } else {
+ if (! fgets (buf, sizeof (buf), stdin)) {
+ *cmd = CMD_QUIT;
+ fprintf (stderr, "\r\n");
+ return (0);
+ }
+ }
+ p = parse (buf, cmd);
+ } while (! p);
+ return (p);
+}
+
+char *parse (char *buf, int *cmd)
+{
+ struct cmdtab *c;
+ char *p;
+ unsigned int len;
+
+ for (p=buf; isspace (*p); p++)
+ continue;
+
+ if (isdigit (*p) || (p[0] == '#' && isdigit (p[1]))) {
+ *cmd = CMD_PLAY;
+ return (p);
+ } else if (*p == '+') {
+ *cmd = CMD_NEXT;
+ return (p + 1);
+ } else if (*p == '-') {
+ *cmd = CMD_PREVIOUS;
+ return (p + 1);
+ }
+
+ for (buf = p; *p && ! isspace (*p); p++)
+ continue;
+
+ len = p - buf;
+ if (! len)
+ return (0);
+
+ if (*p) { /* It must be a spacing character! */
+ char *q;
+
+ *p++ = 0;
+ for (q=p; *q && *q != '\n' && *q != '\r'; q++)
+ continue;
+ *q = 0;
+ }
+
+ *cmd = -1;
+ for (c=cmdtab; c->name; ++c) {
+ /* Is it an exact match? */
+ if (! strcasecmp (buf, c->name)) {
+ *cmd = c->command;
+ break;
+ }
+
+ /* Try short hand forms then... */
+ if (len >= c->min && ! strncasecmp (buf, c->name, len)) {
+ if (*cmd != -1 && *cmd != c->command) {
+ warnx("ambiguous command");
+ return (0);
+ }
+ *cmd = c->command;
+ }
+ }
+
+ if (*cmd == -1) {
+ warnx("invalid command, enter ``help'' for commands");
+ return (0);
+ }
+
+ while (isspace (*p))
+ p++;
+ return p;
+}
+
+int open_cd ()
+{
+ char devbuf[MAXPATHLEN];
+
+ if (fd > -1)
+ return (1);
+
+ if (*cdname == '/') {
+ snprintf (devbuf, MAXPATHLEN, "%s", cdname);
+ } else {
+ snprintf (devbuf, MAXPATHLEN, "%s%s", _PATH_DEV, 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..1c52f46
--- /dev/null
+++ b/usr.sbin/chkgrp/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= chkgrp
+MAN= chkgrp.8
+
+WARNS?= 2
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/chkgrp/chkgrp.8 b/usr.sbin/chkgrp/chkgrp.8
new file mode 100644
index 0000000..6d4bf41
--- /dev/null
+++ b/usr.sbin/chkgrp/chkgrp.8
@@ -0,0 +1,84 @@
+.\" Copyright (c) 1998 Dag-Erling Coïdan Smørgrav
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer
+.\" in this position and unchanged.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd May 5, 1998
+.Dt CHKGRP 8
+.Os
+.Sh NAME
+.Nm chkgrp
+.Nd check the syntax of the group file
+.Sh SYNOPSIS
+.Nm
+.Op Ar groupfile
+.Sh DESCRIPTION
+The
+.Nm
+utility
+scans the given file or, failing that, the system-wide group file for
+errors.
+Specifically, it checks that every non-blank, non-comment
+entry is composed of four colon-separated fields, that none of them
+contains whitespace, and that the third field (the group ID) is
+numeric.
+.Sh FILES
+.Bl -tag -width /etc/group -compact
+.It Pa /etc/group
+group database file
+.El
+.Sh SEE ALSO
+.Xr getgrent 3 ,
+.Xr group 5
+.Sh DIAGNOSTICS
+For each error found,
+.Nm
+will print an error message containing the name of the file being
+scanned and the line number on which the error was found.
+Otherwise no
+output is produced.
+.Pp
+The
+.Nm
+utility returns
+.Dv EX_DATAERR
+if errors were found in the group file,
+and
+.Dv EX_OK
+otherwise.
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Fx 3.0 .
+.Sh AUTHORS
+The
+.Nm
+utility and this manual page were written by
+.An Dag-Erling Co\(:idan Sm\(/orgrav Aq des@FreeBSD.org .
+.Sh BUGS
+Should check fields more thoroughly for allowed/disallowed
+characters, and the range of the group ID.
diff --git a/usr.sbin/chkgrp/chkgrp.c b/usr.sbin/chkgrp/chkgrp.c
new file mode 100644
index 0000000..c686858
--- /dev/null
+++ b/usr.sbin/chkgrp/chkgrp.c
@@ -0,0 +1,144 @@
+/*-
+ * 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[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <err.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: chkgrp [groupfile]\n");
+ exit(EX_USAGE);
+}
+
+int
+main(int argc, char *argv[])
+{
+ unsigned int i;
+ size_t len;
+ int n = 0, k, e = 0;
+ char *line, *f[4], *p;
+ const char *gfn;
+ FILE *gf;
+
+ /* check arguments */
+ switch (argc) {
+ case 1:
+ gfn = "/etc/group";
+ break;
+ case 2:
+ gfn = argv[1];
+ break;
+ default:
+ gfn = NULL; /* silence compiler */
+ usage();
+ }
+
+ /* open group file */
+ if ((gf = fopen(gfn, "r")) == NULL)
+ err(EX_IOERR, "%s", gfn); /* XXX - is IO_ERR the correct exit code? */
+
+ /* check line by line */
+ while (++n) {
+ if ((line = fgetln(gf, &len)) == NULL)
+ break;
+ 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..10dec2c
--- /dev/null
+++ b/usr.sbin/chown/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+PROG= chown
+LINKS= ${BINDIR}/chown /usr/bin/chgrp
+WARNS?= 2
+MAN= chgrp.1 chown.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/chown/chgrp.1 b/usr.sbin/chown/chgrp.1
new file mode 100644
index 0000000..b52f7e5
--- /dev/null
+++ b/usr.sbin/chown/chgrp.1
@@ -0,0 +1,140 @@
+.\" Copyright (c) 1983, 1990, 1993, 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)chgrp.1 8.3 (Berkeley) 3/31/94
+.\" $FreeBSD$
+.\"
+.Dd March 31, 1994
+.Dt CHGRP 1
+.Os
+.Sh NAME
+.Nm chgrp
+.Nd change group
+.Sh SYNOPSIS
+.Nm
+.Op Fl fhv
+.Oo
+.Fl R
+.Op Fl H | Fl L | Fl P
+.Oc
+.Ar group
+.Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility sets the group ID of the file named by each
+.Ar file
+operand to the
+.Ar group
+ID specified by the group operand.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl H
+If the
+.Fl R
+option is specified, symbolic links on the command line are followed.
+(Symbolic links encountered in the tree traversal are not followed).
+.It Fl L
+If the
+.Fl R
+option is specified, all symbolic links are followed.
+.It Fl P
+If the
+.Fl R
+option is specified, no symbolic links are followed.
+This is the default.
+.It Fl R
+Change the group ID for the file hierarchies rooted
+in the files instead of just the files themselves.
+.It Fl f
+The force option ignores errors, except for usage errors and doesn't
+query about strange modes (unless the user does not have proper permissions).
+.It Fl h
+If the file is a symbolic link, the group ID of the link itself is changed
+rather than the file that is pointed to.
+.It Fl v
+Cause
+.Nm
+to be verbose, showing files as the group is modified.
+.El
+.Pp
+The
+.Fl H ,
+.Fl L
+and
+.Fl P
+options are ignored unless the
+.Fl R
+option is specified.
+In addition, these options override each other and the
+command's actions are determined by the last one specified.
+.Pp
+The
+.Ar group
+operand can be either a group name from the group database,
+or a numeric group ID.
+If a group name is also a numeric group ID, the operand is used as a
+group name.
+.Pp
+The user invoking
+.Nm
+must belong to the specified group and be the owner of the file,
+or be the super-user.
+.Sh DIAGNOSTICS
+.Ex -std
+.Sh COMPATIBILITY
+In previous versions of this system, symbolic links did not have groups.
+.Pp
+The
+.Fl v
+option is non-standard and its use in scripts is not recommended.
+.Sh FILES
+.Bl -tag -width /etc/group -compact
+.It Pa /etc/group
+group ID file
+.El
+.Sh SEE ALSO
+.Xr chown 2 ,
+.Xr fts 3 ,
+.Xr group 5 ,
+.Xr passwd 5 ,
+.Xr symlink 7 ,
+.Xr chown 8
+.Sh STANDARDS
+The
+.Nm
+utility is expected to be
+.St -p1003.2
+compatible.
diff --git a/usr.sbin/chown/chown.8 b/usr.sbin/chown/chown.8
new file mode 100644
index 0000000..b10b59a
--- /dev/null
+++ b/usr.sbin/chown/chown.8
@@ -0,0 +1,160 @@
+.\" Copyright (c) 1990, 1991, 1993, 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)chown.8 8.3 (Berkeley) 3/31/94
+.\" $FreeBSD$
+.\"
+.Dd March 31, 1994
+.Dt CHOWN 8
+.Os
+.Sh NAME
+.Nm chown
+.Nd change file owner and group
+.Sh SYNOPSIS
+.Nm
+.Op Fl fhv
+.Oo
+.Fl R
+.Op Fl H | Fl L | Fl P
+.Oc
+.Ar owner Ns Op : Ns Ar group
+.Ar
+.Nm
+.Op Fl fhv
+.Oo
+.Fl R
+.Op Fl H | Fl L | Fl P
+.Oc
+.No : Ns Ar group
+.Ar
+.Sh DESCRIPTION
+.Nm Chown
+changes the user ID and/or the group ID of the specified files.
+Symbolic links named by arguments are silently left unchanged unless
+.Fl h
+is used.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl H
+If the
+.Fl R
+option is specified, symbolic links on the command line are followed.
+(Symbolic links encountered in the tree traversal are not followed.)
+.It Fl L
+If the
+.Fl R
+option is specified, all symbolic links are followed.
+.It Fl P
+If the
+.Fl R
+option is specified, no symbolic links are followed.
+This is the default.
+.It Fl R
+Change the user ID and/or the group ID 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.
+.It Fl v
+Cause
+.Nm
+to be verbose, showing files as the owner is modified.
+.El
+.Pp
+The
+.Fl H ,
+.Fl L
+and
+.Fl P
+options are ignored unless the
+.Fl R
+option is specified.
+In addition, these options override each other and the
+command's actions are determined by the last one specified.
+.Pp
+The
+.Ar owner
+and
+.Ar group
+operands are both optional, however, one must be specified.
+If the
+.Ar group
+operand is specified, it must be preceded by a colon (``:'') character.
+.Pp
+The
+.Ar owner
+may be either a numeric user ID or a user name.
+If a user name is also a numeric user ID, the operand is used as a
+user name.
+The
+.Ar group
+may be either a numeric group ID or a group name.
+If a group name is also a numeric group ID, the operand is used as a
+group name.
+.Pp
+The ownership of a file may only be altered by a super-user for
+obvious security reasons.
+.Sh DIAGNOSTICS
+.Ex -std
+.Sh COMPATIBILITY
+Previous versions of the
+.Nm
+utility used the dot (``.'') character to distinguish the group name.
+This has been changed to be a colon (``:'') character so that user and
+group names may contain the dot character.
+.Pp
+On previous versions of this system, symbolic links did not have
+owners.
+.Pp
+The
+.Fl v
+option is non-standard and its use in scripts is not recommended.
+.Sh SEE ALSO
+.Xr chgrp 1 ,
+.Xr find 1 ,
+.Xr chown 2 ,
+.Xr fts 3 ,
+.Xr symlink 7
+.Sh STANDARDS
+The
+.Nm
+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..6812c45
--- /dev/null
+++ b/usr.sbin/chown/chown.c
@@ -0,0 +1,284 @@
+/*
+ * 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";
+#endif
+#endif /* not lint */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fts.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+void a_gid(const char *);
+void a_uid(const char *);
+void chownerr(const char *);
+u_long id(const char *, const char *);
+void usage(void);
+
+uid_t uid;
+gid_t gid;
+int ischown;
+const char *gname;
+
+int
+main(int argc, char **argv)
+{
+ FTS *ftsp;
+ FTSENT *p;
+ int Hflag, Lflag, Rflag, fflag, hflag, vflag;
+ int ch, fts_options, rval;
+ char *cp;
+
+ cp = strrchr(argv[0], '/');
+ cp = (cp != NULL) ? cp + 1 : argv[0];
+ ischown = (strcmp(cp, "chown") == 0);
+
+ Hflag = Lflag = Rflag = fflag = hflag = vflag = 0;
+ while ((ch = getopt(argc, argv, "HLPRfhv")) != -1)
+ switch (ch) {
+ case 'H':
+ Hflag = 1;
+ Lflag = 0;
+ break;
+ case 'L':
+ Lflag = 1;
+ Hflag = 0;
+ break;
+ case 'P':
+ Hflag = Lflag = 0;
+ break;
+ case 'R':
+ Rflag = 1;
+ break;
+ case 'f':
+ fflag = 1;
+ break;
+ case 'h':
+ hflag = 1;
+ break;
+ case 'v':
+ vflag = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argv += optind;
+ argc -= optind;
+
+ if (argc < 2)
+ usage();
+
+ if (Rflag) {
+ fts_options = FTS_PHYSICAL;
+ if (hflag && (Hflag || Lflag))
+ errx(1, "the -R%c and -h options may not be "
+ "specified together", Hflag ? 'H' : 'L');
+ if (Hflag)
+ fts_options |= FTS_COMFOLLOW;
+ else if (Lflag) {
+ fts_options &= ~FTS_PHYSICAL;
+ fts_options |= FTS_LOGICAL;
+ }
+ } else
+ fts_options = hflag ? FTS_PHYSICAL : FTS_LOGICAL;
+
+ uid = (uid_t)-1;
+ gid = (gid_t)-1;
+ if (ischown) {
+ if ((cp = strchr(*argv, ':')) != NULL) {
+ *cp++ = '\0';
+ a_gid(cp);
+ }
+#ifdef SUPPORT_DOT
+ else if ((cp = strchr(*argv, '.')) != NULL) {
+ warnx("seperation of user and group with a period is deprecated");
+ *cp++ = '\0';
+ a_gid(cp);
+ }
+#endif
+ a_uid(*argv);
+ } else
+ a_gid(*argv);
+
+ if ((ftsp = fts_open(++argv, fts_options, 0)) == NULL)
+ err(1, NULL);
+
+ for (rval = 0; (p = fts_read(ftsp)) != NULL;) {
+ switch (p->fts_info) {
+ case FTS_D: /* Change it at FTS_DP. */
+ if (!Rflag)
+ fts_set(ftsp, p, FTS_SKIP);
+ continue;
+ case FTS_DNR: /* Warn, chown. */
+ warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
+ rval = 1;
+ break;
+ case FTS_ERR: /* Warn, continue. */
+ case FTS_NS:
+ warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
+ rval = 1;
+ continue;
+ case FTS_SL:
+ case FTS_SLNONE:
+ /*
+ * The only symlinks that end up here are ones that
+ * don't point to anything and ones that we found
+ * doing a physical walk.
+ */
+ if (hflag)
+ break;
+ else
+ continue;
+ default:
+ break;
+ }
+ if ((uid == (uid_t)-1 || uid == p->fts_statp->st_uid) &&
+ (gid == (gid_t)-1 || gid == p->fts_statp->st_gid))
+ continue;
+ if ((hflag ? lchown : chown)(p->fts_accpath, uid, gid) == -1) {
+ if (!fflag) {
+ chownerr(p->fts_path);
+ rval = 1;
+ }
+ } else {
+ if (vflag)
+ printf("%s\n", p->fts_path);
+ }
+ }
+ if (errno)
+ err(1, "fts_read");
+ exit(rval);
+}
+
+void
+a_gid(const char *s)
+{
+ struct group *gr;
+
+ if (*s == '\0') /* Argument was "uid[:.]". */
+ return;
+ gname = s;
+ gid = ((gr = getgrnam(s)) != NULL) ? gr->gr_gid : id(s, "group");
+}
+
+void
+a_uid(const char *s)
+{
+ struct passwd *pw;
+
+ if (*s == '\0') /* Argument was "[:.]gid". */
+ return;
+ uid = ((pw = getpwnam(s)) != NULL) ? pw->pw_uid : id(s, "user");
+}
+
+u_long
+id(const char *name, const char *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(const char *file)
+{
+ static uid_t euid = -1;
+ static int ngroups = -1;
+ gid_t groups[NGROUPS_MAX];
+
+ /* Check for chown without being root. */
+ if (errno != EPERM || (uid != (uid_t)-1 &&
+ euid == (uid_t)-1 && (euid = geteuid()) != 0)) {
+ warn("%s", file);
+ return;
+ }
+
+ /* Check group membership; kernel just returns EPERM. */
+ if (gid != (gid_t)-1 && ngroups == -1 &&
+ euid == (uid_t)-1 && (euid = geteuid()) != 0) {
+ ngroups = getgroups(NGROUPS_MAX, groups);
+ while (--ngroups >= 0 && gid != groups[ngroups]);
+ if (ngroups < 0) {
+ warnx("you are not a member of group %s", gname);
+ return;
+ }
+ }
+ warn("%s", file);
+}
+
+void
+usage(void)
+{
+
+ if (ischown)
+ (void)fprintf(stderr, "%s\n%s\n",
+ "usage: chown [-fhv] [-R [-H | -L | -P]] owner[:group]"
+ " file ...",
+ " chown [-fhv] [-R [-H | -L | -P]] :group file ...");
+ else
+ (void)fprintf(stderr, "%s\n",
+ "usage: chgrp [-fhv] [-R [-H | -L | -P]] group file ...");
+ exit(1);
+}
diff --git a/usr.sbin/chroot/Makefile b/usr.sbin/chroot/Makefile
new file mode 100644
index 0000000..d83dfa2
--- /dev/null
+++ b/usr.sbin/chroot/Makefile
@@ -0,0 +1,8 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+PROG= chroot
+WARNS?= 2
+MAN= 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..454155c
--- /dev/null
+++ b/usr.sbin/chroot/chroot.8
@@ -0,0 +1,78 @@
+.\" Copyright (c) 1988, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)chroot.8 8.1 (Berkeley) 6/9/93
+.\" $FreeBSD$
+.\"
+.Dd January 24, 2002
+.Dt CHROOT 8
+.Os
+.Sh NAME
+.Nm chroot
+.Nd change root directory
+.Sh SYNOPSIS
+.Nm
+.Ar newroot
+.Op Ar command
+.Sh DESCRIPTION
+The
+.Nm
+command changes its current and root directories to the supplied directory
+.Ar newroot
+and then exec's
+.Ar command ,
+if supplied, or an interactive copy of the user's login shell.
+.Sh ENVIRONMENT
+The following environment variable is referenced by
+.Nm :
+.Bl -tag -width ".Ev SHELL"
+.It Ev SHELL
+If set,
+the string specified by
+.Ev SHELL
+is interpreted as the name of
+the shell to exec.
+If the variable
+.Ev SHELL
+is not set,
+.Pa /bin/sh
+is used.
+.El
+.Sh SEE ALSO
+.Xr chdir 2 ,
+.Xr chroot 2 ,
+.Xr environ 7 ,
+.Xr jail 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Bx 4.4 .
diff --git a/usr.sbin/chroot/chroot.c b/usr.sbin/chroot/chroot.c
new file mode 100644
index 0000000..0a3be8e
--- /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[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <err.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ch;
+ const 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", (char *)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..23430a8
--- /dev/null
+++ b/usr.sbin/ckdist/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../usr.bin/cksum
+
+PROG= ckdist
+SRCS= ckdist.c crc.c
+
+DPADD= ${LIBMD}
+LDADD= -lmd
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ckdist/ckdist.1 b/usr.sbin/ckdist/ckdist.1
new file mode 100644
index 0000000..5dc37b0
--- /dev/null
+++ b/usr.sbin/ckdist/ckdist.1
@@ -0,0 +1,101 @@
+.\" Copyright (c) 1997 Robert Nordier
+.\" All rights reserved.
+.\"
+.\" $FreeBSD$
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
+.\" OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+.\" DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
+.\" INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+.\" (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+.\" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+.\" IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd January 20, 1997
+.Dt CKDIST 1
+.Os
+.Sh NAME
+.Nm ckdist
+.Nd check software distributions
+.Sh SYNOPSIS
+.Nm
+.Bq Fl airsx
+.Bq Fl d Ar dir
+.Bq Fl n Ar name
+.Bq Fl t Ar type
+.Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility reads "checksum" files (which are assumed to specify components
+of a software distribution) and verifies the integrity of the
+distribution by validating the checksum of each component file.
+Both MD5 (128-bit "message digest") and .inf (32-bit CRC) checksum
+formats are supported.
+.Pp
+The
+.Ar file
+operands may refer to regular files or to directories. Regular files
+named "md5", or which have an ".md5" or an ".inf" extension, are
+assumed to be of the implied type, otherwise format is determined from
+content. If a directory is specified, it is searched for
+appropriately-named files only.
+.Pp
+Options are as follows:
+.Bl -tag -width 8n -offset indent
+.It Fl a
+Report on all distribution components, not just those in respect of
+which errors are detected.
+.It Fl i
+Ignore missing distribution components.
+.It Fl r
+Search specified directories recursively.
+.It Fl s
+Suppress complaints about inaccessible checksum files and directories.
+.It Fl x
+Verify the existence of distribution components (and also check sizes,
+in the case of .inf files), but omit the more time-consuming step of
+actually computing and comparing checksums.
+.It Fl d Ar dir
+Look for distribution components in the directory
+.Ar dir .
+.It Fl n Ar name
+Access distribution components using the filename
+.Ar name .
+When accessing .inf file components, append the appropriate
+extension to the filename.
+.It Fl t Ar type
+Assume that all specified checksum files are of the format
+.Ar type ,
+and search directories only for files in this format (where
+.Ar type
+is either "md5" or "inf").
+.El
+.Sh SEE ALSO
+.Xr cksum 1 ,
+.Xr md5 1
+.Sh DIAGNOSTICS
+Exit status is 0 if no errors were detected, 1 if errors were found in
+a distribution, and 2 if usage errors, inaccessible input files, or
+other system errors were encountered.
+.Sh NOTES
+Both
+.Bx
+and
+.Tn DOS
+versions of
+.Nm
+are available.
diff --git a/usr.sbin/ckdist/ckdist.c b/usr.sbin/ckdist/ckdist.c
new file mode 100644
index 0000000..3761b70
--- /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[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fts.h>
+#include <md5.h>
+#include <stdio.h>
+#include <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, "%s", opt_dir);
+ if (!S_ISDIR(sb.st_mode))
+ errx(2, "%s: not a directory", opt_dir);
+ }
+ rval = 0;
+ do {
+ if (isstdin(*argv))
+ rval |= ckdist(*argv, opt_type);
+ else if (stat(*argv, &sb))
+ rval |= fail(*argv, NULL);
+ else if (S_ISREG(sb.st_mode))
+ rval |= ckdist(*argv, opt_type);
+ else {
+ arg[0] = *argv;
+ if ((ftsp = fts_open(arg, FTS_LOGICAL, NULL)) == NULL)
+ err(2, "fts_open");
+ while ((f = fts_read(ftsp)) != NULL)
+ switch (f->fts_info) {
+ case FTS_DC:
+ rval = fail(f->fts_path, "Directory causes a cycle");
+ break;
+ case FTS_DNR:
+ case FTS_ERR:
+ case FTS_NS:
+ rval = fail(f->fts_path, sys_errlist[f->fts_errno]);
+ break;
+ case FTS_D:
+ if (!opt_recurse && f->fts_level > FTS_ROOTLEVEL &&
+ fts_set(ftsp, f, FTS_SKIP))
+ err(2, "fts_set");
+ break;
+ case FTS_F:
+ if ((type = distfile(f->fts_name)) != 0 &&
+ (!opt_type || type == opt_type))
+ rval |= ckdist(f->fts_path, type);
+ break;
+ default: ;
+ }
+ if (errno)
+ err(2, "fts_read");
+ if (fts_close(ftsp))
+ err(2, "fts_close");
+ }
+ } while (*++argv);
+ return rval;
+}
+
+static int
+ckdist(const char *path, int type)
+{
+ FILE *fp;
+ int rval, c;
+
+ if (isstdin(path)) {
+ path = "(stdin)";
+ fp = stdin;
+ } else if ((fp = fopen(path, "r")) == NULL)
+ return fail(path, NULL);
+ if (!type) {
+ if (fp != stdin)
+ type = distfile(path);
+ if (!type)
+ if ((c = fgetc(fp)) != EOF) {
+ type = c == 'M' ? DISTMD5 : c == 'P' ? DISTINF : 0;
+ (void)ungetc(c, fp);
+ }
+ }
+ switch (type) {
+ case DISTMD5:
+ rval = chkmd5(fp, path);
+ break;
+ case DISTINF:
+ rval = chkinf(fp, path);
+ break;
+ default:
+ rval = report(path, NULL, E_UNKNOWN);
+ }
+ if (ferror(fp))
+ warn("%s", path);
+ if (fp != stdin && fclose(fp))
+ err(2, "%s", path);
+ return rval;
+}
+
+static int
+chkmd5(FILE * fp, const char *path)
+{
+ char buf[298]; /* "MD5 (NAMESIZE = MDSUMLEN" */
+ char name[NAMESIZE + 1];
+ char sum[MDSUMLEN + 1], chk[MDSUMLEN + 1];
+ const char *dname;
+ char *s;
+ int rval, error, c, fd;
+ char ch;
+
+ rval = 0;
+ while (fgets(buf, sizeof(buf), fp)) {
+ dname = NULL;
+ error = 0;
+ if (((c = sscanf(buf, "MD5 (%256s = %32s%c", name, sum,
+ &ch)) != 3 && (!feof(fp) || c != 2)) ||
+ (c == 3 && ch != '\n') ||
+ (s = strrchr(name, ')')) == NULL ||
+ strlen(sum) != MDSUMLEN)
+ error = E_BADMD5;
+ else {
+ *s = 0;
+ if ((dname = distname(path, name, NULL)) == NULL)
+ error = E_NAME;
+ else if (opt_exist) {
+ if ((fd = open(dname, O_RDONLY)) == -1)
+ error = E_ERRNO;
+ else if (close(fd))
+ err(2, "%s", dname);
+ } else if (!MD5File((char *)dname, chk))
+ error = E_ERRNO;
+ else if (strcmp(chk, sum))
+ error = E_CHKSUM;
+ }
+ if (opt_ignore && error == E_ERRNO && errno == ENOENT)
+ continue;
+ if (error || opt_all)
+ rval |= report(path, dname, error);
+ if (isfatal(error))
+ break;
+ }
+ return rval;
+}
+
+static int
+chkinf(FILE * fp, const char *path)
+{
+ char buf[30]; /* "cksum.2 = 10 6" */
+ char ext[3];
+ struct stat sb;
+ const char *dname;
+ 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, "%s", dname);
+ if (opt_ignore && error == E_ERRNO && errno == ENOENT)
+ continue;
+ if (error || (opt_all && cnt >= 0))
+ rval |= report(path, dname, error);
+ if (isfatal(error))
+ break;
+ }
+ return rval;
+}
+
+static int
+report(const char *path, const char *name, int error)
+{
+ if (name)
+ name = stripath(name);
+ switch (error) {
+ case E_UNKNOWN:
+ printf("%s: Unknown format\n", path);
+ break;
+ case E_BADMD5:
+ printf("%s: Invalid MD5 format\n", path);
+ break;
+ case E_BADINF:
+ printf("%s: Invalid .inf format\n", path);
+ break;
+ case E_NAME:
+ printf("%s: Can't derive component name\n", path);
+ break;
+ case E_LENGTH:
+ printf("%s: %s: Size mismatch\n", path, name);
+ break;
+ case E_CHKSUM:
+ printf("%s: %s: Checksum mismatch\n", path, name);
+ break;
+ case E_ERRNO:
+ printf("%s: %s: %s\n", path, name, sys_errlist[errno]);
+ break;
+ default:
+ printf("%s: %s: OK\n", path, name);
+ }
+ return error != 0;
+}
+
+static const char *
+distname(const char *path, const char *name, const char *ext)
+{
+ static char buf[NAMESIZE];
+ size_t plen, nlen;
+ char *s;
+
+ if (opt_name)
+ name = opt_name;
+ else if (!name) {
+ if (!path)
+ return NULL;
+ name = stripath(path);
+ }
+ nlen = strlen(name);
+ if (ext && nlen > 4 && name[nlen - 4] == '.' &&
+ disttype(name + nlen - 3) == DISTINF)
+ nlen -= 4;
+ if (opt_dir) {
+ path = opt_dir;
+ plen = strlen(path);
+ } else
+ plen = path && (s = strrchr(path, '/')) != NULL ?
+ (size_t)(s - path) : 0;
+ if (plen + (plen > 0) + nlen + (ext ? 3 : 0) >= sizeof(buf))
+ return NULL;
+ s = buf;
+ if (plen) {
+ memcpy(s, path, plen);
+ s += plen;
+ *s++ = '/';
+ }
+ memcpy(s, name, nlen);
+ s += nlen;
+ if (ext) {
+ *s++ = '.';
+ memcpy(s, ext, 2);
+ s += 2;
+ }
+ *s = 0;
+ return buf;
+}
+
+static char *
+stripath(const char *path)
+{
+ const char *s;
+
+ return (char *)((s = strrchr(path, '/')) != NULL && s[1] ?
+ s + 1 : path);
+}
+
+static int
+distfile(const char *path)
+{
+ const char *s;
+ int type;
+
+ if ((type = disttype(path)) == DISTMD5 ||
+ ((s = strrchr(path, '.')) != NULL && s > path &&
+ (type = disttype(s + 1)) != 0))
+ return type;
+ return 0;
+}
+
+static int
+disttype(const char *name)
+{
+ static const char dname[DISTTYPES][4] = {"md5", "inf"};
+ int i;
+
+ for (i = 0; i < DISTTYPES; i++)
+ if (!strcmp(dname[i], name))
+ return 1 + i;
+ return 0;
+}
+
+static int
+fail(const char *path, const char *msg)
+{
+ if (opt_silent)
+ return 0;
+ warnx("%s: %s", path, msg ? msg : sys_errlist[errno]);
+ return 2;
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr,
+ "usage: ckdist [-airsx] [-d dir] [-n name] [-t type] file ...\n");
+ exit(2);
+}
diff --git a/usr.sbin/config/Makefile b/usr.sbin/config/Makefile
new file mode 100644
index 0000000..982ae14
--- /dev/null
+++ b/usr.sbin/config/Makefile
@@ -0,0 +1,17 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+PROG= config
+MAN= config.8
+SRCS= config.y main.c lang.l mkmakefile.c mkheaders.c \
+ mkoptions.c y.tab.h
+
+CFLAGS+= -I. -I${.CURDIR}
+CFLAGS+= ${BDECFLAGS}
+
+DPADD= ${LIBL}
+LDADD= -ll
+
+mkmakefile.o: configvers.h
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/config/SMM.doc/0.t b/usr.sbin/config/SMM.doc/0.t
new file mode 100644
index 0000000..ae5bf77
--- /dev/null
+++ b/usr.sbin/config/SMM.doc/0.t
@@ -0,0 +1,88 @@
+.\" Copyright (c) 1983, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)0.t 8.1 (Berkeley) 7/5/93
+.\"
+.bd S B 3
+.de UX
+.ie \\n(GA>0 \\$2UNIX\\$1
+.el \{\
+.if n \\$2UNIX\\$1*
+.if t \\$2UNIX\\$1\\f1\(dg\\fP
+.FS
+.if n *UNIX
+.if t \(dgUNIX
+.ie \\$3=1 is a Footnote of Bell Laboratories.
+.el is a Trademark of Bell Laboratories.
+.FE
+.nr GA 1\}
+..
+.de BR
+\fB\\$1\fP\\$2
+..
+.TL
+Building 4.4BSD Kernels with Config
+.AU
+Samuel J. Leffler and Michael J. Karels
+.AI
+Computer Systems Research Group
+Department of Electrical Engineering and Computer Science
+University of California, Berkeley
+Berkeley, California 94720
+.de IR
+\fI\\$1\fP\\$2
+..
+.de DT
+.TA 8 16 24 32 40 48 56 64 72 80
+..
+.AB
+.PP
+This document describes the use of
+\fIconfig\fP\|(8) to configure and create bootable
+4.4BSD system images.
+It discusses the structure of system
+configuration files and how to configure
+systems with non-standard hardware configurations.
+Sections describing the preferred way to
+add new code to the system and how the system's autoconfiguration
+process operates are included. An appendix
+contains a summary of the rules used by the system
+in calculating the size of system data structures,
+and also indicates some of the standard system size
+limitations (and how to change them).
+Other configuration options are also listed.
+.sp
+.LP
+Revised July 5, 1993
+.AE
+.LP
+.OH 'Building 4.4BSD Kernels with Config''SMM:2-%'
+.EH 'SMM:2-%''Building 4.4BSD Kernels with Config'
diff --git a/usr.sbin/config/SMM.doc/1.t b/usr.sbin/config/SMM.doc/1.t
new file mode 100644
index 0000000..453041b
--- /dev/null
+++ b/usr.sbin/config/SMM.doc/1.t
@@ -0,0 +1,61 @@
+.\" Copyright (c) 1983, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)1.t 8.1 (Berkeley) 6/8/93
+.\"
+.\".ds RH Introduction
+.ne 2i
+.sp 3
+.NH
+INTRODUCTION
+.PP
+.I Config
+is a tool used in building 4.4BSD system images (the UNIX kernel).
+It takes a file describing a system's tunable parameters and
+hardware support, and generates a collection
+of files which are then used to build a copy of UNIX appropriate
+to that configuration.
+.I Config
+simplifies system maintenance by isolating system dependencies
+in a single, easy to understand, file.
+.PP
+This document describes the content and
+format of system configuration
+files and the rules which must be followed when creating
+these files. Example configuration files are constructed
+and discussed.
+.PP
+Later sections suggest guidelines to be used in modifying
+system source and explain some of the inner workings of the
+autoconfiguration process. Appendix D summarizes the rules
+used in calculating the most important system data structures
+and indicates some inherent system data structure size
+limitations (and how to go about modifying them).
diff --git a/usr.sbin/config/SMM.doc/2.t b/usr.sbin/config/SMM.doc/2.t
new file mode 100644
index 0000000..34e6b63
--- /dev/null
+++ b/usr.sbin/config/SMM.doc/2.t
@@ -0,0 +1,188 @@
+.\" Copyright (c) 1983, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)2.t 8.1 (Berkeley) 6/8/93
+.\"
+.\".ds RH "Configuration File Contents
+.ne 2i
+.NH
+CONFIGURATION FILE CONTENTS
+.PP
+A system configuration must include at least the following
+pieces of information:
+.IP \(bu 3
+machine type
+.IP \(bu 3
+cpu type
+.IP \(bu 3
+system identification
+.IP \(bu 3
+timezone
+.IP \(bu 3
+maximum number of users
+.IP \(bu 3
+location of the root file system
+.IP \(bu 3
+available hardware
+.PP
+.I Config
+allows multiple system images to be generated from a single
+configuration description. Each system image is configured
+for identical hardware, but may have different locations for the root
+file system and, possibly, other system devices.
+.NH 2
+Machine type
+.PP
+The
+.I "machine type"
+indicates if the system is going to operate on a DEC VAX-11\(dg computer,
+.FS
+\(dg DEC, VAX, UNIBUS, MASSBUS and MicroVAX are trademarks of Digital
+Equipment Corporation.
+.FE
+or some other machine on which 4.4BSD operates. The machine type
+is used to locate certain data files which are machine specific, and
+also to select rules used in constructing the resultant
+configuration files.
+.NH 2
+Cpu type
+.PP
+The
+.I "cpu type"
+indicates which, of possibly many, cpu's the system is to operate on.
+For example, if the system is being configured for a VAX-11, it could
+be running on a VAX 8600, VAX-11/780, VAX-11/750, VAX-11/730 or MicroVAX II.
+(Other VAX cpu types, including the 8650, 785 and 725, are configured using
+the cpu designation for compatible machines introduced earlier.)
+Specifying
+more than one cpu type implies that the system should be configured to run
+on any of the cpu's specified. For some types of machines this is not
+possible and
+.I config
+will print a diagnostic indicating such.
+.NH 2
+System identification
+.PP
+The
+.I "system identification"
+is a moniker attached to the system, and often the machine on which the
+system is to run. For example, at Berkeley we have machines named Ernie
+(Co-VAX), Kim (No-VAX), and so on. The system identifier selected is used to
+create a global C ``#define'' which may be used to isolate system dependent
+pieces of code in the kernel. For example, Ernie's Varian driver used
+to be special cased because its interrupt vectors were wired together. The
+code in the driver which understood how to handle this non-standard hardware
+configuration was conditionally compiled in only if the system
+was for Ernie.
+.PP
+The system identifier ``GENERIC'' is given to a system which
+will run on any cpu of a particular machine type; it should not
+otherwise be used for a system identifier.
+.NH 2
+Timezone
+.PP
+The timezone in which the system is to run is used to define the
+information returned by the \fIgettimeofday\fP\|(2)
+system call. This value is specified as the number of hours east
+or west of GMT. Negative numbers indicate a value east of GMT.
+The timezone specification may also indicate the
+type of daylight savings time rules to be applied.
+.NH 2
+Maximum number of users
+.PP
+The system allocates many system data structures at boot time
+based on the maximum number of users the system will support.
+This number is normally between 8 and 40, depending
+on the hardware and expected job mix. The rules
+used to calculate system data structures are discussed in
+Appendix D.
+.NH 2
+Root file system location
+.PP
+When the system boots it must know the location of
+the root of the file system
+tree. This location and the part(s) of the disk(s) to be used
+for paging and swapping must be specified in order to create
+a complete configuration description.
+.I Config
+uses many rules to calculate default locations for these items;
+these are described in Appendix B.
+.PP
+When a generic system is configured, the root file system is left
+undefined until the system is booted. In this case, the root file
+system need not be specified, only that the system is a generic system.
+.NH 2
+Hardware devices
+.PP
+When the system boots it goes through an
+.I autoconfiguration
+phase. During this period, the system searches for all
+those hardware devices
+which the system builder has indicated might be present. This probing
+sequence requires certain pieces of information such as register
+addresses, bus interconnects, etc. A system's hardware may be configured
+in a very flexible manner or be specified without any flexibility
+whatsoever. Most people do not configure hardware devices into the
+system unless they are currently present on the machine, expect
+them to be present in the near future, or are simply guarding
+against a hardware
+failure somewhere else at the site (it is often wise to configure in
+extra disks in case an emergency requires moving one off a machine which
+has hardware problems).
+.PP
+The specification of hardware devices usually occupies the majority of
+the configuration file. As such, a large portion of this document will
+be spent understanding it. Section 6.3 contains a description of
+the autoconfiguration process, as it applies to those planning to
+write, or modify existing, device drivers.
+.NH 2
+Pseudo devices
+.PP
+Several system facilities are configured in a manner like that used
+for hardware devices although they are not associated with specific hardware.
+These system options are configured as
+.IR pseudo-devices .
+Some pseudo devices allow an optional parameter that sets the limit
+on the number of instances of the device that are active simultaneously.
+.NH 2
+System options
+.PP
+Other than the mandatory pieces of information described above, it
+is also possible to include various optional system facilities
+or to modify system behavior and/or limits.
+For example, 4.4BSD can be configured to support binary compatibility for
+programs built under 4.3BSD. Also, optional support is provided
+for disk quotas and tracing the performance of the virtual memory
+subsystem. Any optional facilities to be configured into
+the system are specified in the configuration file. The resultant
+files generated by
+.I config
+will automatically include the necessary pieces of the system.
diff --git a/usr.sbin/config/SMM.doc/3.t b/usr.sbin/config/SMM.doc/3.t
new file mode 100644
index 0000000..e0b6234
--- /dev/null
+++ b/usr.sbin/config/SMM.doc/3.t
@@ -0,0 +1,299 @@
+.\" Copyright (c) 1983, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)3.t 8.1 (Berkeley) 6/8/93
+.\"
+.\".ds RH "System Building Process
+.ne 2i
+.NH
+SYSTEM BUILDING PROCESS
+.PP
+In this section we consider the steps necessary to build a bootable system
+image. We assume the system source is located in the ``/sys'' directory
+and that, initially, the system is being configured from source code.
+.PP
+Under normal circumstances there are 5 steps in building a system.
+.IP 1) 3
+Create a configuration file for the system.
+.IP 2) 3
+Make a directory for the system to be constructed in.
+.IP 3) 3
+Run
+.I config
+on the configuration file to generate the files required
+to compile and load the system image.
+.IP 4)
+Construct the source code interdependency rules for the
+configured system with
+.I make depend
+using
+.IR make (1).
+.IP 5)
+Compile and load the system with
+.IR make .
+.PP
+Steps 1 and 2 are usually done only once. When a system configuration
+changes it usually suffices to just run
+.I config
+on the modified configuration file, rebuild the source code dependencies,
+and remake the system. Sometimes,
+however, configuration dependencies may not be noticed in which case
+it is necessary to clean out the relocatable object files saved
+in the system's directory; this will be discussed later.
+.NH 2
+Creating a configuration file
+.PP
+Configuration files normally reside in the directory ``/sys/conf''.
+A configuration file is most easily constructed by copying an
+existing configuration file and modifying it. The 4.4BSD distribution
+contains a number of configuration files for machines at Berkeley;
+one may be suitable or, in worst case, a copy
+of the generic configuration file may be edited.
+.PP
+The configuration file must have the same name as the directory in
+which the configured system is to be built.
+Further,
+.I config
+assumes this directory is located in the parent directory of
+the directory in which it
+is run. For example, the generic
+system has a configuration file ``/sys/conf/GENERIC'' and an accompanying
+directory named ``/sys/GENERIC''.
+Although it is not required that the system sources and configuration
+files reside in ``/sys,'' the configuration and compilation procedure
+depends on the relative locations of directories within that hierarchy,
+as most of the system code and the files created by
+.I config
+use pathnames of the form ``../''.
+If the system files are not located in ``/sys,''
+it is desirable to make a symbolic link there for use in installation
+of other parts of the system that share files with the kernel.
+.PP
+When building the configuration file, be sure to include the items
+described in section 2. In particular, the machine type,
+cpu type, timezone, system identifier, maximum users, and root device
+must be specified. The specification of the hardware present may take
+a bit of work; particularly if your hardware is configured at non-standard
+places (e.g. device registers located at funny places or devices not
+supported by the system). Section 4 of this document
+gives a detailed description of the configuration file syntax,
+section 5 explains some sample configuration files, and
+section 6 discusses how to add new devices to
+the system. If the devices to be configured are not already
+described in one of the existing configuration files you should check
+the manual pages in section 4 of the UNIX Programmers Manual. For each
+supported device, the manual page synopsis entry gives a
+sample configuration line.
+.PP
+Once the configuration file is complete, run it through
+.I config
+and look for any errors. Never try and use a system which
+.I config
+has complained about; the results are unpredictable.
+For the most part,
+.IR config 's
+error diagnostics are self explanatory. It may be the case that
+the line numbers given with the error messages are off by one.
+.PP
+A successful run of
+.I config
+on your configuration file will generate a number of files in
+the configuration directory. These files are:
+.IP \(bu 3
+A file to be used by \fImake\fP\|(1)
+in compiling and loading the system,
+.IR Makefile .
+.IP \(bu 3
+One file for each possible system image for this machine,
+.IR swapxxx.c ,
+where
+.I xxx
+is the name of the system image,
+which describes where swapping, the root file system, and other
+miscellaneous system devices are located.
+.IP \(bu 3
+A collection of header files, one per possible device the
+system supports, which define the hardware configured.
+.IP \(bu 3
+A file containing the I/O configuration tables used by the system
+during its
+.I autoconfiguration
+phase,
+.IR ioconf.c .
+.IP \(bu 3
+An assembly language file of interrupt vectors which
+connect interrupts from the machine's external buses to the main
+system path for handling interrupts,
+and a file that contains counters and names for the interrupt vectors.
+.PP
+Unless you have reason to doubt
+.IR config ,
+or are curious how the system's autoconfiguration scheme
+works, you should never have to look at any of these files.
+.NH 2
+Constructing source code dependencies
+.PP
+When
+.I config
+is done generating the files needed to compile and link your system it
+will terminate with a message of the form ``Don't forget to run make depend''.
+This is a reminder that you should change over to the configuration
+directory for the system just configured and type ``make depend''
+to build the rules used by
+.I make
+to recognize interdependencies in the system source code.
+This will insure that any changes to a piece of the system
+source code will result in the proper modules being recompiled
+the next time
+.I make
+is run.
+.PP
+This step is particularly important if your site makes changes
+to the system include files. The rules generated specify which source code
+files are dependent on which include files. Without these rules,
+.I make
+will not recognize when it must rebuild modules
+due to the modification of a system header file.
+The dependency rules are generated by a pass of the C preprocessor
+and reflect the global system options.
+This step must be repeated when the configuration file is changed
+and
+.I config
+is used to regenerate the system makefile.
+.NH 2
+Building the system
+.PP
+The makefile constructed by
+.I config
+should allow a new system to be rebuilt by simply typing ``make image-name''.
+For example, if you have named your bootable system image ``kernel'',
+then ``make kernel''
+will generate a bootable image named ``kernel''. Alternate system image names
+are used when the root file system location and/or swapping configuration
+is done in more than one way. The makefile which
+.I config
+creates has entry points for each system image defined in
+the configuration file.
+Thus, if you have configured ``kernel'' to be a system with the root file
+system on an ``hp'' device and ``hkkernel'' to be a system with the root
+file system on an ``hk'' device, then ``make kernel hkkernel'' will generate
+binary images for each.
+As the system will generally use the disk from which it is loaded
+as the root filesystem, separate system images are only required
+to support different swap configurations.
+.PP
+Note that the name of a bootable image is different from the system
+identifier. All bootable images are configured for the same system;
+only the information about the root file system and paging devices differ.
+(This is described in more detail in section 4.)
+.PP
+The last step in the system building process is to rearrange certain commonly
+used symbols in the symbol table of the system image; the makefile
+generated by
+.I config
+does this automatically for you.
+This is advantageous for programs such as
+\fInetstat\fP\|(1) and \fIvmstat\fP\|(1),
+which run much faster when the symbols they need are located at
+the front of the symbol table.
+Remember also that many programs expect
+the currently executing system to be named ``/kernel''. If you install
+a new system and name it something other than ``/kernel'', many programs
+are likely to give strange results.
+.NH 2
+Sharing object modules
+.PP
+If you have many systems which are all built on a single machine
+there are at least two approaches to saving time in building system
+images. The best way is to have a single system image which is run on
+all machines. This is attractive since it minimizes disk space used
+and time required to rebuild systems after making changes. However,
+it is often the case that one or more systems will require a separately
+configured system image. This may be due to limited memory (building
+a system with many unused device drivers can be expensive), or to
+configuration requirements (one machine may be a development machine
+where disk quotas are not needed, while another is a production machine
+where they are), etc. In these cases it is possible
+for common systems to share relocatable object modules which are not
+configuration dependent; most of the modules in the directory ``/sys/sys''
+are of this sort.
+.PP
+To share object modules, a generic system should be built. Then, for
+each system configure the system as before, but before recompiling and
+linking the system, type ``make links'' in the system compilation directory.
+This will cause the system
+to be searched for source modules which are safe to share between systems
+and generate symbolic links in the current directory to the appropriate
+object modules in the directory ``../GENERIC''. A shell script,
+``makelinks'' is generated with this request and may be checked for
+correctness. The file ``/sys/conf/defines'' contains a list of symbols
+which we believe are safe to ignore when checking the source code
+for modules which may be shared. Note that this list includes the definitions
+used to conditionally compile in the virtual memory tracing facilities, and
+the trace point support used only rarely (even at Berkeley).
+It may be necessary
+to modify this file to reflect local needs. Note further that
+interdependencies which are not directly visible
+in the source code are not caught. This means that if you place
+per-system dependencies in an include file, they will not be recognized
+and the shared code may be selected in an unexpected fashion.
+.NH 2
+Building profiled systems
+.PP
+It is simple to configure a system which will automatically
+collect profiling information as it operates. The profiling data
+may be collected with \fIkgmon\fP\|(8) and processed with
+\fIgprof\fP\|(1)
+to obtain information regarding the system's operation. Profiled
+systems maintain histograms of the program counter as well as the
+number of invocations of each routine. The \fIgprof\fP
+command will also generate a dynamic call graph of the executing
+system and propagate time spent in each routine along the arcs
+of the call graph (consult the \fIgprof\fP documentation for elaboration).
+The program counter sampling can be driven by the system clock, or
+if you have an alternate real time clock, this can be used. The
+latter is highly recommended, as use of the system clock will result
+in statistical anomalies, and time spent in the clock routine will
+not be accurately attributed.
+.PP
+To configure a profiled system, the
+.B \-p
+option should be supplied to \fIconfig\fP.
+A profiled system is about 5-10% larger in its text space due to
+the calls to count the subroutine invocations. When the system
+executes, the profiling data is stored in a buffer which is 1.2
+times the size of the text space. The overhead for running a
+profiled system varies; under normal load we see anywhere from 5-25%
+of the system time spent in the profiling code.
+.PP
+Note that systems configured for profiling should not be shared as
+described above unless all the other shared systems are also to be
+profiled.
diff --git a/usr.sbin/config/SMM.doc/4.t b/usr.sbin/config/SMM.doc/4.t
new file mode 100644
index 0000000..7498185
--- /dev/null
+++ b/usr.sbin/config/SMM.doc/4.t
@@ -0,0 +1,442 @@
+.\" Copyright (c) 1983, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)4.t 8.1 (Berkeley) 6/8/93
+.\"
+.\".ds RH "Configuration File Syntax
+.ne 2i
+.NH
+CONFIGURATION FILE SYNTAX
+.PP
+In this section we consider the specific rules used in writing
+a configuration file. A complete grammar for the input language
+can be found in Appendix A and may be of use if you should have
+problems with syntax errors.
+.PP
+A configuration file is broken up into three logical pieces:
+.IP \(bu 3
+configuration parameters global to all system images
+specified in the configuration file,
+.IP \(bu 3
+parameters specific to each
+system image to be generated, and
+.IP \(bu 3
+device specifications.
+.NH 2
+Global configuration parameters
+.PP
+The global configuration parameters are the type of machine,
+cpu types, options, timezone, system identifier, and maximum users.
+Each is specified with a separate line in the configuration file.
+.IP "\fBmachine\fP \fItype\fP"
+.br
+The system is to run on the machine type specified. No more than
+one machine type can appear in the configuration file. Legal values
+are
+.B vax
+and
+\fBsun\fP.
+.IP "\fBcpu\fP ``\fItype\fP''"
+.br
+This system is to run on the cpu type specified.
+More than one cpu type specification
+can appear in a configuration file.
+Legal types for a
+.B vax
+machine are
+\fBVAX8600\fP, \fBVAX780\fP, \fBVAX750\fP,
+\fBVAX730\fP
+and
+\fBVAX630\fP (MicroVAX II).
+The 8650 is listed as an 8600, the 785 as a 780, and a 725 as a 730.
+.IP "\fBoptions\fP \fIoptionlist\fP"
+.br
+Compile the listed optional code into the system.
+Options in this list are separated by commas.
+Possible options are listed at the top of the generic makefile.
+A line of the form ``options FUNNY,HAHA'' generates global ``#define''s
+\-DFUNNY \-DHAHA in the resultant makefile.
+An option may be given a value by following its name with ``\fB=\fP'',
+then the value enclosed in (double) quotes.
+The following are major options are currently in use:
+COMPAT (include code for compatibility with 4.1BSD binaries),
+INET (Internet communication protocols),
+NS (Xerox NS communication protocols),
+and
+QUOTA (enable disk quotas).
+Other kernel options controlling system sizes and limits
+are listed in Appendix D;
+options for the network are found in Appendix E.
+There are additional options which are associated with certain
+peripheral devices; those are listed in the Synopsis section
+of the manual page for the device.
+.IP "\fBmakeoptions\fP \fIoptionlist\fP"
+.br
+Options that are used within the system makefile
+and evaluated by
+.I make
+are listed as
+.IR makeoptions .
+Options are listed with their values with the form
+``makeoptions name=value,name2=value2.''
+The values must be enclosed in double quotes if they include numerals
+or begin with a dash.
+.IP "\fBtimezone\fP \fInumber\fP [ \fBdst\fP [ \fInumber\fP ] ]"
+.br
+Specifies the timezone used by the system. This is measured in the
+number of hours your timezone is west of GMT.
+EST is 5 hours west of GMT, PST is 8. Negative numbers
+indicate hours east of GMT. If you specify
+\fBdst\fP, the system will operate under daylight savings time.
+An optional integer or floating point number may be included
+to specify a particular daylight saving time correction algorithm;
+the default value is 1, indicating the United States.
+Other values are: 2 (Australian style), 3 (Western European),
+4 (Middle European), and 5 (Eastern European). See
+\fIgettimeofday\fP\|(2) and \fIctime\fP\|(3) for more information.
+.IP "\fBident\fP \fIname\fP"
+.br
+This system is to be known as
+.IR name .
+This is usually a cute name like ERNIE (short for Ernie Co-Vax) or
+VAXWELL (for Vaxwell Smart).
+This value is defined for use in conditional compilation,
+and is also used to locate an optional list of source files specific
+to this system.
+.IP "\fBmaxusers\fP \fInumber\fP"
+.br
+The maximum expected number of simultaneously active user on this system is
+.IR number .
+This number is used to size several system data structures.
+.NH 2
+System image parameters
+.PP
+Multiple bootable images may be specified in a single configuration
+file. The systems will have the same global configuration parameters
+and devices, but the location of the root file system and other
+system specific devices may be different. A system image is specified
+with a ``config'' line:
+.IP
+\fBconfig\fP\ \fIsysname\fP\ \fIconfig-clauses\fP
+.LP
+The
+.I sysname
+field is the name given to the loaded system image; almost everyone
+names their standard system image ``kernel''. The configuration clauses
+are one or more specifications indicating where the root file system
+is located and the number and location of paging devices.
+The device used by the system to process argument lists during
+.IR execve (2)
+calls may also be specified, though in practice this is almost
+always selected by
+.I config
+using one of its rules for selecting default locations for
+system devices.
+.PP
+A configuration clause is one of the following
+.IP
+.nf
+\fBroot\fP [ \fBon\fP ] \fIroot-device\fP
+\fBswap\fP [ \fBon\fP ] \fIswap-device\fP [ \fBand\fP \fIswap-device\fP ] ...
+\fBdumps\fP [ \fBon\fP ] \fIdump-device\fP
+\fBargs\fP [ \fBon\fP ] \fIarg-device\fP
+.LP
+(the ``on'' is optional.) Multiple configuration clauses
+are separated by white space;
+.I config
+allows specifications to be continued across multiple lines
+by beginning the continuation line with a tab character.
+The ``root'' clause specifies where the root file system
+is located, the ``swap'' clause indicates swapping and paging
+area(s), the ``dumps'' clause can be used to force system dumps
+to be taken on a particular device, and the ``args'' clause
+can be used to specify that argument list processing for
+.I execve
+should be done on a particular device.
+.PP
+The device names supplied in the clauses may be fully specified
+as a device, unit, and file system partition; or underspecified
+in which case
+.I config
+will use builtin rules to select default unit numbers and file
+system partitions. The defaulting rules are a bit complicated
+as they are dependent on the overall system configuration.
+For example, the swap area need not be specified at all if
+the root device is specified; in this case the swap area is
+placed in the ``b'' partition of the same disk where the root
+file system is located. Appendix B contains a complete list
+of the defaulting rules used in selecting system configuration
+devices.
+.PP
+The device names are translated to the
+appropriate major and minor device
+numbers on a per-machine basis. A file,
+``/sys/conf/devices.machine'' (where ``machine''
+is the machine type specified in the configuration file),
+is used to map a device name to its major block device number.
+The minor device number is calculated using the standard
+disk partitioning rules: on unit 0, partition ``a'' is minor device
+0, partition ``b'' is minor device 1, and so on; for units
+other than 0, add 8 times the unit number to get the minor
+device.
+.PP
+If the default mapping of device name to major/minor device
+number is incorrect for your configuration, it can be replaced
+by an explicit specification of the major/minor device.
+This is done by substituting
+.IP
+\fBmajor\fP \fIx\fP \fBminor\fP \fIy\fP
+.LP
+where the device name would normally be found. For example,
+.IP
+.nf
+\fBconfig\fP kernel \fBroot\fP \fBon\fP \fBmajor\fP 99 \fBminor\fP 1
+.fi
+.PP
+Normally, the areas configured for swap space are sized by the system
+at boot time. If a non-standard size is to be used for one
+or more swap areas (less than the full partition),
+this can also be specified. To do this, the
+device name specified for a swap area should have a ``size''
+specification appended. For example,
+.IP
+.nf
+\fBconfig\fP kernel \fBroot\fP \fBon\fP hp0 \fBswap\fP \fBon\fP hp0b \fBsize\fP 1200
+.fi
+.LP
+would force swapping to be done in partition ``b'' of ``hp0'' and
+the swap partition size would be set to 1200 sectors. A swap area
+sized larger than the associated disk partition is trimmed to the
+partition size.
+.PP
+To create a generic configuration, only the clause ``swap generic''
+should be specified; any extra clauses will cause an error.
+.NH 2
+Device specifications
+.PP
+Each device attached to a machine must be specified
+to
+.I config
+so that the system generated will know to probe for it during
+the autoconfiguration process carried out at boot time. Hardware
+specified in the configuration need not actually be present on
+the machine where the generated system is to be run. Only the
+hardware actually found at boot time will be used by the system.
+.PP
+The specification of hardware devices in the configuration file
+parallels the interconnection hierarchy of the machine to be
+configured. On the VAX, this means that a configuration file must
+indicate what MASSBUS and UNIBUS adapters are present, and to
+which \fInexi\fP they might be connected.*
+.FS
+* While VAX-11/750's and VAX-11/730 do not actually have
+nexi, the system treats them as having
+.I "simulated nexi"
+to simplify device configuration.
+.FE
+Similarly, devices
+and controllers must be indicated as possibly being connected
+to one or more adapters. A device description may provide a
+complete definition of the possible configuration parameters
+or it may leave certain parameters undefined and make the system
+probe for all the possible values. The latter allows a single
+device configuration list to match many possible physical
+configurations. For example, a disk may be indicated as present
+at UNIBUS adapter 0, or at any UNIBUS adapter which the system
+locates at boot time. The latter scheme, termed
+.IR wildcarding ,
+allows more flexibility in the physical configuration of a system;
+if a disk must be moved around for some reason, the system will
+still locate it at the alternate location.
+.PP
+A device specification takes one of the following forms:
+.IP
+.nf
+\fBmaster\fP \fIdevice-name\fP \fIdevice-info\fP
+\fBcontroller\fP \fIdevice-name\fP \fIdevice-info\fP [ \fIinterrupt-spec\fP ]
+\fBdevice\fP \fIdevice-name\fP \fIdevice-info\fP \fIinterrupt-spec\fP
+\fBdisk\fP \fIdevice-name\fP \fIdevice-info\fP
+\fBtape\fP \fIdevice-name\fP \fIdevice-info\fP
+.fi
+.LP
+A ``master'' is a MASSBUS tape controller; a ``controller'' is a
+disk controller, a UNIBUS tape controller, a MASSBUS adapter, or
+a UNIBUS adapter. A ``device'' is an autonomous device which
+connects directly to a UNIBUS adapter (as opposed to something
+like a disk which connects through a disk controller). ``Disk''
+and ``tape'' identify disk drives and tape drives connected to
+a ``controller'' or ``master.''
+.PP
+The
+.I device-name
+is one of the standard device names, as
+indicated in section 4 of the UNIX Programmers Manual,
+concatenated with the
+.I logical
+unit number to be assigned the device (the
+.I logical
+unit number may be different than the
+.I physical
+unit number indicated on the front of something
+like a disk; the
+.I logical
+unit number is used to refer to the UNIX device, not
+the physical unit number). For example, ``hp0'' is logical
+unit 0 of a MASSBUS storage device, even though it might
+be physical unit 3 on MASSBUS adapter 1.
+.PP
+The
+.I device-info
+clause specifies how the hardware is
+connected in the interconnection hierarchy. On the VAX,
+UNIBUS and MASSBUS adapters are connected to the internal
+system bus through
+a \fInexus\fP.
+Thus, one of the following
+specifications would be used:
+.IP
+.ta 1.5i 2.5i 4.0i
+.nf
+\fBcontroller\fP mba0 \fBat\fP \fBnexus\fP \fIx\fP
+\fBcontroller\fP uba0 \fBat\fP \fBnexus\fP \fIx\fP
+.fi
+.LP
+To tie a controller to a specific nexus, ``x'' would be supplied
+as the number of that nexus; otherwise ``x'' may be specified as
+``?'', in which
+case the system will probe all nexi present looking
+for the specified controller.
+.PP
+The remaining interconnections on the VAX are:
+.IP \(bu 3
+a controller
+may be connected to another controller (e.g. a disk controller attached
+to a UNIBUS adapter),
+.IP \(bu 3
+a master is always attached to a controller (a MASSBUS adapter),
+.IP \(bu 3
+a tape is always attached to a master (for MASSBUS
+tape drives),
+.IP \(bu 3
+a disk is always attached to a controller, and
+.IP \(bu 3
+devices
+are always attached to controllers (e.g. UNIBUS controllers attached
+to UNIBUS adapters).
+.LP
+The following lines give an example of each of these interconnections:
+.IP
+.ta 1.5i 2.5i 4.0i
+.nf
+\fBcontroller\fP hk0 \fBat\fP uba0 ...
+\fBmaster\fP ht0 \fBat\fP mba0 ...
+\fBdisk\fP hp0 \fBat\fP mba0 ...
+\fBtape\fP tu0 \fBat\fP ht0 ...
+\fBdisk\fP rk1 \fBat\fP hk0 ...
+\fBdevice\fP dz0 \fBat\fP uba0 ...
+.fi
+.LP
+Any piece of hardware which may be connected to a specific
+controller may also be wildcarded across multiple controllers.
+.PP
+The final piece of information needed by the system to configure
+devices is some indication of where or how a device will interrupt.
+For tapes and disks, simply specifying the \fIslave\fP or \fIdrive\fP
+number is sufficient to locate the control status register for the
+device.
+\fIDrive\fP numbers may be wildcarded
+on MASSBUS devices, but not on disks on a UNIBUS controller.
+For controllers, the control status register must be
+given explicitly, as well the number of interrupt vectors used and
+the names of the routines to which they should be bound.
+Thus the example lines given above might be completed as:
+.IP
+.ta 1.5i 2.5i 4.0i
+.nf
+\fBcontroller\fP hk0 \fBat\fP uba0 \fBcsr\fP 0177440 \fBvector\fP rkintr
+\fBmaster\fP ht0 \fBat\fP mba0 \fBdrive\fP 0
+\fBdisk\fP hp0 \fBat\fP mba0 \fBdrive\fP ?
+\fBtape\fP tu0 \fBat\fP ht0 \fBslave\fP 0
+\fBdisk\fP rk1 \fBat\fP hk0 \fBdrive\fP 1
+\fBdevice\fP dz0 \fBat\fP uba0 \fBcsr\fP 0160100 \fBvector\fP dzrint dzxint
+.fi
+.PP
+Certain device drivers require extra information passed to them
+at boot time to tailor their operation to the actual hardware present.
+The line printer driver, for example, needs to know how many columns
+are present on each non-standard line printer (i.e. a line printer
+with other than 80 columns). The drivers for the terminal multiplexors
+need to know which lines are attached to modem lines so that no one will
+be allowed to use them unless a connection is present. For this reason,
+one last parameter may be specified to a
+.IR device ,
+a
+.I flags
+field. It has the syntax
+.IP
+\fBflags\fP \fInumber\fP
+.LP
+and is usually placed after the
+.I csr
+specification. The
+.I number
+is passed directly to the associated driver. The manual pages
+in section 4 should be consulted to determine how each driver
+uses this value (if at all).
+Communications interface drivers commonly use the flags
+to indicate whether modem control signals are in use.
+.PP
+The exact syntax for each specific device is given in the Synopsis
+section of its manual page in section 4 of the manual.
+.NH 2
+Pseudo-devices
+.PP
+A number of drivers and software subsystems
+are treated like device drivers without any associated hardware.
+To include any of these pieces, a ``pseudo-device'' specification
+must be used. A specification for a pseudo device takes the form
+.IP
+.DT
+.nf
+\fBpseudo-device\fP \fIdevice-name\fP [ \fIhowmany\fP ]
+.fi
+.PP
+Examples of pseudo devices are
+\fBpty\fP, the pseudo terminal driver (where the optional
+.I howmany
+value indicates the number of pseudo terminals to configure, 32 default),
+and \fBloop\fP, the software loopback network pseudo-interface.
+Other pseudo devices for the network include
+\fBimp\fP (required when a CSS or ACC imp is configured)
+and \fBether\fP (used by the Address Resolution Protocol
+on 10 Mb/sec Ethernets).
+More information on configuring each of these can also be found
+in section 4 of the manual.
diff --git a/usr.sbin/config/SMM.doc/5.t b/usr.sbin/config/SMM.doc/5.t
new file mode 100644
index 0000000..81f2a52
--- /dev/null
+++ b/usr.sbin/config/SMM.doc/5.t
@@ -0,0 +1,271 @@
+.\" Copyright (c) 1983, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)5.t 8.1 (Berkeley) 6/8/93
+.\"
+.\".ds RH "Sample Configuration Files
+.ne 2i
+.NH
+SAMPLE CONFIGURATION FILES
+.PP
+In this section we will consider how to configure a
+sample VAX-11/780 system on which the hardware can be
+reconfigured to guard against various hardware mishaps.
+We then study the rules needed to configure a VAX-11/750
+to run in a networking environment.
+.NH 2
+VAX-11/780 System
+.PP
+Our VAX-11/780 is configured with hardware
+recommended in the document ``Hints on Configuring a VAX for 4.2BSD''
+(this is one of the high-end configurations).
+Table 1 lists the pertinent hardware to be configured.
+.DS B
+.TS
+box;
+l | l | l | l | l
+l | l | l | l | l.
+Item Vendor Connection Name Reference
+_
+cpu DEC VAX780
+MASSBUS controller Emulex nexus ? mba0 hp(4)
+disk Fujitsu mba0 hp0
+disk Fujitsu mba0 hp1
+MASSBUS controller Emulex nexus ? mba1
+disk Fujitsu mba1 hp2
+disk Fujitsu mba1 hp3
+UNIBUS adapter DEC nexus ?
+tape controller Emulex uba0 tm0 tm(4)
+tape drive Kennedy tm0 te0
+tape drive Kennedy tm0 te1
+terminal multiplexor Emulex uba0 dh0 dh(4)
+terminal multiplexor Emulex uba0 dh1
+terminal multiplexor Emulex uba0 dh2
+.TE
+.DE
+.ce
+Table 1. VAX-11/780 Hardware support.
+.LP
+We will call this machine ANSEL and construct a configuration
+file one step at a time.
+.PP
+The first step is to fill in the global configuration parameters.
+The machine is a VAX, so the
+.I "machine type"
+is ``vax''. We will assume this system will
+run only on this one processor, so the
+.I "cpu type"
+is ``VAX780''. The options are empty since this is going to
+be a ``vanilla'' VAX. The system identifier, as mentioned before,
+is ``ANSEL,'' and the maximum number of users we plan to support is
+about 40. Thus the beginning of the configuration file looks like
+this:
+.DS
+.ta 1.5i 2.5i 4.0i
+#
+# ANSEL VAX (a picture perfect machine)
+#
+machine vax
+cpu VAX780
+timezone 8 dst
+ident ANSEL
+maxusers 40
+.DE
+.PP
+To this we must then add the specifications for three
+system images. The first will be our standard system with the
+root on ``hp0'' and swapping on the same drive as the root.
+The second will have the root file system in the same location,
+but swap space interleaved among drives on each controller.
+Finally, the third will be a generic system,
+to allow us to boot off any of the four disk drives.
+.DS
+.ta 1.5i 2.5i
+config kernel root on hp0
+config hpkernel root on hp0 swap on hp0 and hp2
+config genkernel swap generic
+.DE
+.PP
+Finally, the hardware must be specified. Let us first just try
+transcribing the information from Table 1.
+.DS
+.ta 1.5i 2.5i 4.0i
+controller mba0 at nexus ?
+disk hp0 at mba0 disk 0
+disk hp1 at mba0 disk 1
+controller mba1 at nexus ?
+disk hp2 at mba1 disk 2
+disk hp3 at mba1 disk 3
+controller uba0 at nexus ?
+controller tm0 at uba0 csr 0172520 vector tmintr
+tape te0 at tm0 drive 0
+tape te1 at tm0 drive 1
+device dh0 at uba0 csr 0160020 vector dhrint dhxint
+device dm0 at uba0 csr 0170500 vector dmintr
+device dh1 at uba0 csr 0160040 vector dhrint dhxint
+device dh2 at uba0 csr 0160060 vector dhrint dhxint
+.DE
+.LP
+(Oh, I forgot to mention one panel of the terminal multiplexor
+has modem control, thus the ``dm0'' device.)
+.PP
+This will suffice, but leaves us with little flexibility. Suppose
+our first disk controller were to break. We would like to recable the
+drives normally on the second controller so that all our disks could
+still be used without reconfiguring the system. To do this we wildcard
+the MASSBUS adapter connections and also the slave numbers. Further,
+we wildcard the UNIBUS adapter connections in case we decide some time
+in the future to purchase another adapter to offload the single UNIBUS
+we currently have. The revised device specifications would then be:
+.DS
+.ta 1.5i 2.5i 4.0i
+controller mba0 at nexus ?
+disk hp0 at mba? disk ?
+disk hp1 at mba? disk ?
+controller mba1 at nexus ?
+disk hp2 at mba? disk ?
+disk hp3 at mba? disk ?
+controller uba0 at nexus ?
+controller tm0 at uba? csr 0172520 vector tmintr
+tape te0 at tm0 drive 0
+tape te1 at tm0 drive 1
+device dh0 at uba? csr 0160020 vector dhrint dhxint
+device dm0 at uba? csr 0170500 vector dmintr
+device dh1 at uba? csr 0160040 vector dhrint dhxint
+device dh2 at uba? csr 0160060 vector dhrint dhxint
+.DE
+.LP
+The completed configuration file for ANSEL is shown in Appendix C.
+.NH 2
+VAX-11/750 with network support
+.PP
+Our VAX-11/750 system will be located on two 10Mb/s Ethernet
+local area networks and also the DARPA Internet. The system
+will have a MASSBUS drive for the root file system and two
+UNIBUS drives. Paging is interleaved among all three drives.
+We have sold our standard DEC terminal multiplexors since this
+machine will be accessed solely through the network. This
+machine is not intended to have a large user community, it
+does not have a great deal of memory. First the global parameters:
+.DS
+.ta 1.5i 2.5i 4.0i
+#
+# UCBVAX (Gateway to the world)
+#
+machine vax
+cpu "VAX780"
+cpu "VAX750"
+ident UCBVAX
+timezone 8 dst
+maxusers 32
+options INET
+options NS
+.DE
+.PP
+The multiple cpu types allow us to replace UCBVAX with a
+more powerful cpu without reconfiguring the system. The
+value of 32 given for the maximum number of users is done to
+force the system data structures to be over-allocated. That
+is desirable on this machine because, while it is not expected
+to support many users, it is expected to perform a great deal
+of work.
+The ``INET'' indicates that we plan to use the
+DARPA standard Internet protocols on this machine,
+and ``NS'' also includes support for Xerox NS protocols.
+Note that unlike 4.2BSD configuration files,
+the network protocol options do not require corresponding pseudo devices.
+.PP
+The system images and disks are configured next.
+.DS
+.ta 1.5i 2.5i 4.0i
+config kernel root on hp swap on hp and rk0 and rk1
+config upkernel root on up
+config hkkernel root on hk swap on rk0 and rk1
+
+controller mba0 at nexus ?
+controller uba0 at nexus ?
+disk hp0 at mba? drive 0
+disk hp1 at mba? drive 1
+controller sc0 at uba? csr 0176700 vector upintr
+disk up0 at sc0 drive 0
+disk up1 at sc0 drive 1
+controller hk0 at uba? csr 0177440 vector rkintr
+disk rk0 at hk0 drive 0
+disk rk1 at hk0 drive 1
+.DE
+.PP
+UCBVAX requires heavy interleaving of its paging area to keep up
+with all the mail traffic it handles. The limiting factor on this
+system's performance is usually the number of disk arms, as opposed
+to memory or cpu cycles. The extra UNIBUS controller, ``sc0'',
+is in case the MASSBUS controller breaks and a spare controller
+must be installed (most of our old UNIBUS controllers have been
+replaced with the newer MASSBUS controllers, so we have a number
+of these around as spares).
+.PP
+Finally, we add in the network devices.
+Pseudo terminals are needed to allow users to
+log in across the network (remember the only hardwired terminal
+is the console).
+The software loopback device is used for on-machine communications.
+The connection to the Internet is through
+an IMP, this requires yet another
+.I pseudo-device
+(in addition to the actual hardware device used by the
+IMP software). And, finally, there are the two Ethernet devices.
+These use a special protocol, the Address Resolution Protocol (ARP),
+to map between Internet and Ethernet addresses. Thus, yet another
+.I pseudo-device
+is needed. The additional device specifications are show below.
+.DS
+.ta 1.5i 2.5i 4.0i
+pseudo-device pty
+pseudo-device loop
+pseudo-device imp
+device acc0 at uba? csr 0167600 vector accrint accxint
+pseudo-device ether
+device ec0 at uba? csr 0164330 vector ecrint eccollide ecxint
+device il0 at uba? csr 0164000 vector ilrint ilcint
+.DE
+.LP
+The completed configuration file for UCBVAX is shown in Appendix C.
+.NH 2
+Miscellaneous comments
+.PP
+It should be noted in these examples that neither system was
+configured to use disk quotas or the 4.1BSD compatibility mode.
+To use these optional facilities, and others, we would probably
+clean out our current configuration, reconfigure the system, then
+recompile and relink the system image(s). This could, of course,
+be avoided by figuring out which relocatable object files are
+affected by the reconfiguration, then reconfiguring and recompiling
+only those files affected by the configuration change. This technique
+should be used carefully.
diff --git a/usr.sbin/config/SMM.doc/6.t b/usr.sbin/config/SMM.doc/6.t
new file mode 100644
index 0000000..f02baed
--- /dev/null
+++ b/usr.sbin/config/SMM.doc/6.t
@@ -0,0 +1,239 @@
+.\" Copyright (c) 1983, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)6.t 8.1 (Berkeley) 6/8/93
+.\"
+.\".ds RH "Adding New Devices
+.ne 2i
+.NH
+ADDING NEW SYSTEM SOFTWARE
+.PP
+This section is not for the novice, it describes
+some of the inner workings of the configuration process as
+well as the pertinent parts of the system autoconfiguration process.
+It is intended to give
+those people who intend to install new device drivers and/or
+other system facilities sufficient information to do so in the
+manner which will allow others to easily share the changes.
+.PP
+This section is broken into four parts:
+.IP \(bu 3
+general guidelines to be followed in modifying system code,
+.IP \(bu 3
+how to add non-standard system facilities to 4.4BSD,
+.IP \(bu 3
+how to add a device driver to 4.4BSD, and
+.NH 2
+Modifying system code
+.PP
+If you wish to make site-specific modifications to the system
+it is best to bracket them with
+.DS
+#ifdef SITENAME
+\&...
+#endif
+.DE
+to allow your source to be easily distributed to others, and
+also to simplify \fIdiff\fP\|(1) listings. If you choose not
+to use a source code control system (e.g. SCCS, RCS), and
+perhaps even if you do, it is
+recommended that you save the old code with something
+of the form:
+.DS
+#ifndef SITENAME
+\&...
+#endif
+.DE
+We try to isolate our site-dependent code in individual files
+which may be configured with pseudo-device specifications.
+.PP
+Indicate machine-specific code with ``#ifdef vax'' (or other machine,
+as appropriate).
+4.4BSD underwent extensive work to make it extremely portable to
+machines with similar architectures\- you may someday find
+yourself trying to use a single copy of the source code on
+multiple machines.
+.NH 2
+Adding non-standard system facilities
+.PP
+This section considers the work needed to augment
+.IR config 's
+data base files for non-standard system facilities.
+.I Config
+uses a set of files that list the source modules that may be required
+when building a system.
+The data bases are taken from the directory in which
+.I config
+is run, normally /sys/conf.
+Three such files may be used:
+.IR files ,
+.IR files .machine,
+and
+.IR files .ident.
+The first is common to all systems,
+the second contains files unique to a single machine type,
+and the third is an optional list of modules for use on a specific machine.
+This last file may override specifications in the first two.
+The format of the
+.I files
+file has grown somewhat complex over time. Entries are normally of
+the form
+.IP
+.nf
+.DT
+\fIdir/source.c\fP \fItype\fP \fIoption-list\fP \fImodifiers\fP
+.LP
+for example,
+.IP
+.nf
+.DT
+\fIvaxuba/foo.c\fP \fBoptional\fP foo \fBdevice-driver\fP
+.LP
+The
+.I type
+is one of
+.B standard
+or
+.BR optional .
+Files marked as standard are included in all system configurations.
+Optional file specifications include a list of one or more system
+options that together require the inclusion of this module.
+The options in the list may be either names of devices that may
+be in the configuration file,
+or the names of system options that may be defined.
+An optional file may be listed multiple times with different options;
+if all of the options for any of the entries are satisfied,
+the module is included.
+.PP
+If a file is specified as a
+.IR device-driver ,
+any special compilation options for device drivers will be invoked.
+On the VAX this results in the use of the
+.B \-i
+option for the C optimizer. This is required when pointer references
+are made to memory locations in the VAX I/O address space.
+.PP
+Two other optional keywords modify the usage of the file.
+.I Config
+understands that certain files are used especially for
+kernel profiling. These files are indicated in the
+.I files
+files with a
+.I profiling-routine
+keyword. For example, the current profiling subroutines
+are sequestered off in a separate file with the following
+entry:
+.IP
+.nf
+.DT
+\fIsys/subr_mcount.c\fP \fBoptional\fP \fBprofiling-routine\fP
+.fi
+.LP
+The
+.I profiling-routine
+keyword forces
+.I config
+not to compile the source file with the
+.B \-pg
+option.
+.PP
+The second keyword which can be of use is the
+.I config-dependent
+keyword. This causes
+.I config
+to compile the indicated module with the global configuration
+parameters. This allows certain modules, such as
+.I machdep.c
+to size system data structures based on the maximum number
+of users configured for the system.
+.NH 2
+Adding device drivers to 4.4BSD
+.PP
+The I/O system and
+.I config
+have been designed to easily allow new device support to be added.
+The system source directories are organized as follows:
+.DS
+.TS
+lw(1.0i) l.
+/sys/h machine independent include files
+/sys/sys machine-independent system source files
+/sys/conf site configuration files and basic templates
+/sys/net network-protocol-independent, but network-related code
+/sys/netinet DARPA Internet code
+/sys/netimp IMP support code
+/sys/netns Xerox NS code
+/sys/vax VAX-specific mainline code
+/sys/vaxif VAX network interface code
+/sys/vaxmba VAX MASSBUS device drivers and related code
+/sys/vaxuba VAX UNIBUS device drivers and related code
+.TE
+.DE
+.PP
+Existing block and character device drivers for the VAX
+reside in ``/sys/vax'', ``/sys/vaxmba'', and ``/sys/vaxuba''. Network
+interface drivers reside in ``/sys/vaxif''. Any new device
+drivers should be placed in the appropriate source code directory
+and named so as not to conflict with existing devices.
+Normally, definitions for things like device registers are placed in
+a separate file in the same directory. For example, the ``dh''
+device driver is named ``dh.c'' and its associated include file is
+named ``dhreg.h''.
+.PP
+Once the source for the device driver has been placed in a directory,
+the file ``/sys/conf/files.machine'', and possibly
+``/sys/conf/devices.machine'' should be modified. The
+.I files
+files in the conf directory contain a line for each C source or binary-only
+file in the system. Those files which are machine independent are
+located in ``/sys/conf/files,'' while machine specific files
+are in ``/sys/conf/files.machine.'' The ``devices.machine'' file
+is used to map device names to major block device numbers. If the device
+driver being added provides support for a new disk
+you will want to modify this file (the format is obvious).
+.PP
+In addition to including the driver in the
+.I files
+file, it must also be added to the device configuration tables. These
+are located in ``/sys/vax/conf.c'', or similar for machines other than
+the VAX. If you don't understand what to add to this file, you should
+study an entry for an existing driver.
+Remember that the position in the
+device table specifies the major device number.
+The block major number is needed in the ``devices.machine'' file
+if the device is a disk.
+.PP
+With the configuration information in place, your configuration
+file appropriately modified, and a system reconfigured and rebooted
+you should incorporate the shell commands needed to install the special
+files in the file system to the file ``/dev/MAKEDEV'' or
+``/dev/MAKEDEV.local''. This is discussed in the document ``Installing
+and Operating 4.4BSD''.
diff --git a/usr.sbin/config/SMM.doc/Makefile b/usr.sbin/config/SMM.doc/Makefile
new file mode 100644
index 0000000..5055062
--- /dev/null
+++ b/usr.sbin/config/SMM.doc/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 8.1 (Berkeley) 6/8/93
+# $FreeBSD$
+
+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..8e6af76
--- /dev/null
+++ b/usr.sbin/config/config.8
@@ -0,0 +1,255 @@
+.\" Copyright (c) 1980, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)config.8 8.2 (Berkeley) 4/19/94
+.\" $FreeBSD$
+.\"
+.Dd July 4, 2001
+.Dt CONFIG 8
+.Os
+.Sh NAME
+.Nm config
+.Nd build system configuration files
+.Sh SYNOPSIS
+.Nm
+.Op Fl gp
+.Op Fl d Ar destdir
+.Ar SYSTEM_NAME
+.Sh DESCRIPTION
+This is the old version of the
+.Nm
+program.
+It understands the old autoconfiguration scheme
+used on the HP300, i386, DECstation, and derivative platforms.
+The new version of
+.Nm
+is used with the
+SPARC platform.
+Only the version of
+.Nm
+applicable to the architecture that you are running
+will be installed on your machine.
+.Pp
+.Nm
+builds a set of system configuration files from the file
+.Ar SYSTEM_NAME
+which describes
+the system to configure.
+A second file
+tells
+.Nm
+what files are needed to generate a system and
+can be augmented by configuration specific set of files
+that give alternate files for a specific machine
+(see the
+.Sx FILES
+section below).
+.Pp
+Available options and operands:
+.Bl -tag -width ".Ar SYSTEM_NAME"
+.It Fl d Ar destdir
+Use
+.Ar destdir
+as the output directory, instead of the default one.
+Note that
+.Nm
+does not append
+.Ar SYSTEM_NAME
+to the directory given.
+.It Fl g
+Configure a system for debugging.
+.It Fl p
+Configure a system for profiling; for example,
+.Xr kgmon 8
+and
+.Xr gprof 1 .
+If two or more
+.Fl p
+options are supplied,
+.Nm
+configures a system for high resolution profiling.
+.It Ar SYSTEM_NAME
+Specify the name of the system configuration file
+containing device specifications, configuration options
+and other system parameters for one system configuration.
+.El
+.Pp
+.Nm
+should be run from the
+.Pa conf
+subdirectory of the system source (usually
+.Pa /sys/ Ns Va ARCH Ns Pa /conf ) ,
+where
+.Va ARCH
+represents one of the architectures supported by
+.Fx .
+.Nm
+creates the directory
+.Pa ../compile/ Ns Ar SYSTEM_NAME
+or the one given with the
+.Fl d
+option
+as necessary and places all output files there.
+The output of
+.Nm
+consists of a number of files; for the
+.Tn i386 ,
+they are:
+.Pa ioconf.c ,
+a description
+of what I/O devices are attached to the system;
+.Pa Makefile ,
+used by
+.Xr make 1
+in building the system;
+header files,
+definitions of
+the number of various devices that will be compiled into the system.
+.Pp
+After running
+.Nm ,
+it is necessary to run
+.Dq Li make depend
+in the directory where the new makefile
+was created.
+.Nm
+prints a reminder of this when it completes.
+.Pp
+If any other error messages are produced by
+.Nm ,
+the problems in the configuration file should be corrected and
+.Nm
+should be run again.
+Attempts to compile a system that had configuration errors
+are likely to fail.
+.Pp
+If the
+.Cd "options INCLUDE_CONFIG_FILE"
+is used in the configuration file the
+entire input file is embedded in the new kernel.
+This means that
+.Xr strings 1
+can be used to extract it from a kernel:
+to extract the configuration information, use the command
+.Pp
+.Dl "strings kernel | grep ___"
+.Sh DEBUG KERNELS
+Traditional
+.Bx
+kernels compiled without symbols due to the heavy load on the
+system when compiling a
+.Dq debug
+kernel.
+A debug kernel contains complete symbols for all the source files, and
+enables an experienced kernel programmer to analyse the cause of a problem.
+The
+debuggers available prior to
+.Bx 4.4 Lite
+were able to find some information
+from a normal kernel;
+.Xr gdb 1
+provides very little support for normal kernels, and a debug kernel is needed
+for any meaningful analysis.
+.Pp
+For reasons of history, time and space, building a debug kernel is not the
+default with
+.Fx :
+a debug kernel takes up to 30% longer to build and
+requires about 30 MB of disk storage in the build directory, compared to about 6
+MB for a non-debug kernel.
+A debug kernel is about 11 MB in size, compared to
+about 2 MB for a non-debug kernel.
+This space is used both in the root file
+system and at run time in memory.
+Use the
+.Fl g
+option to build a debug kernel.
+With this option,
+.Nm
+causes two kernel files to be built in the kernel build directory:
+.Bl -bullet
+.It
+.Pa kernel.debug
+is the complete debug kernel.
+.It
+.Pa kernel
+is a copy of the kernel with the debug symbols stripped off.
+This is equivalent
+to the normal non-debug kernel.
+.El
+.Pp
+There is currently little sense in installing and booting from a debug kernel,
+since the only tools available which use the symbols do not run on-line.
+There
+are therefore two options for installing a debug kernel:
+.Bl -bullet
+.It
+.Dq Li "make install"
+installs
+.Pa kernel
+in the root filesystem.
+.It
+.Dq Li "make install.debug"
+installs
+.Pa kernel.debug
+in the root filesystem.
+.El
+.Sh FILES
+.Bl -tag -width ".Pa /sys/ Ns Va ARCH Ns Pa /compile/ Ns Ar SYSTEM_NAME" -compact
+.It Pa /sys/conf/files
+list of common files system is built from
+.It Pa /sys/conf/Makefile. Ns Va ARCH
+generic makefile for the
+.Va ARCH
+.It Pa /sys/conf/files. Ns Va ARCH
+list of
+.Va ARCH
+specific files
+.It Pa /sys/ Ns Va ARCH Ns Pa /compile/ Ns Ar SYSTEM_NAME
+default kernel build directory for system
+.Ar SYSTEM_NAME
+on
+.Va ARCH .
+.El
+.Sh SEE ALSO
+The
+.Sx SYNOPSIS
+portion of each device in section 4.
+.Rs
+.%T "Building 4.3 BSD UNIX System with Config"
+.Re
+.Sh BUGS
+The line numbers reported in error messages are usually off by one.
+.Sh HISTORY
+The
+.Nm
+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..02df879
--- /dev/null
+++ b/usr.sbin/config/config.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)config.h 8.1 (Berkeley) 6/6/93
+ * $FreeBSD$
+ */
+
+/*
+ * Config.
+ */
+#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+
+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_compilewith; /* special make rule if present */
+ char *f_depends; /* additional dependancies */
+ char *f_clean; /* File list to add to clean rule */
+ char *f_needs;
+ char *f_warn; /* warning message */
+};
+
+/*
+ * Types.
+ */
+#define NORMAL 1
+#define INVISIBLE 2
+#define PROFILING 3
+#define NODEPEND 4
+#define LOCAL 5
+#define DEVDONE 0x80000000
+#define TYPEMASK 0x7fffffff
+
+/*
+ * Attributes (flags).
+ */
+#define NO_IMPLCT_RULE 1
+#define NO_OBJ 2
+#define BEFORE_DEPEND 4
+#define NEED_COUNT 8
+#define ISDUP 16
+#define NOWERROR 32
+
+struct device {
+ int d_done; /* processed */
+ char *d_name; /* name of device (e.g. rk11) */
+ int d_count; /* device count */
+#define UNKNOWN -2 /* -2 means not set yet */
+ struct device *d_next; /* Next one in list */
+};
+
+struct config {
+ char *s_sysname;
+};
+
+/*
+ * Config has a global notion of which machine type is
+ * being used. It uses the name of the machine in choosing
+ * files and directories. Thus if the name of the machine is ``i386'',
+ * it will build from ``Makefile.i386'' and use ``../i386/inline''
+ * in the makerules, etc.
+ */
+char *machinename;
+
+/*
+ * For each machine, a set of CPU's may be specified as supported.
+ * These and the options (below) are put in the C flags in the makefile.
+ */
+struct cputype {
+ char *cpu_name;
+ 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_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;
+
+extern char *ident;
+extern char *env;
+extern char *hints;
+extern int do_trace;
+extern int envmode;
+extern int hintmode;
+
+char *get_word(FILE *);
+char *get_quoted_word(FILE *);
+char *path(const char *);
+char *raisestr(char *);
+void remember(const char *);
+void moveifchanged(const char *, const char *);
+int yyparse(void);
+int yylex(void);
+void options(void);
+void makefile(void);
+void headers(void);
+
+extern struct device *dtab;
+
+extern char errbuf[80];
+extern int yyline;
+extern const char *yyfile;
+
+extern struct file_list *ftab;
+
+extern int profiling;
+extern int debugging;
+
+extern int maxusers;
+
+extern char *PREFIX; /* Config file name - for error messages */
+extern char srcdir[]; /* root of the kernel source tree */
+
+#define eq(a,b) (!strcmp(a,b))
+#define ns(s) strdup(s)
diff --git a/usr.sbin/config/config.y b/usr.sbin/config/config.y
new file mode 100644
index 0000000..b1f5f2b
--- /dev/null
+++ b/usr.sbin/config/config.y
@@ -0,0 +1,291 @@
+%union {
+ char *str;
+ int val;
+ struct file_list *file;
+}
+
+%token ARCH
+%token COMMA
+%token CONFIG
+%token CPU
+%token DEVICE
+%token ENV
+%token EQUALS
+%token HINTS
+%token IDENT
+%token MAXUSERS
+%token PROFILE
+%token OPTIONS
+%token MAKEOPTIONS
+%token SEMICOLON
+%token INCLUDE
+
+%token <str> ID
+%token <val> NUMBER
+
+%type <str> Save_id
+%type <str> Opt_value
+%type <str> Dev
+
+%{
+
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)config.y 8.1 (Berkeley) 6/6/93
+ * $FreeBSD$
+ */
+
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "config.h"
+
+static struct device *curp = 0;
+
+struct device *dtab;
+char *ident;
+char *env;
+int envmode;
+char *hints;
+int hintmode;
+int yyline;
+const char *yyfile;
+struct file_list *ftab;
+char errbuf[80];
+int maxusers;
+
+#define ns(s) strdup(s)
+int include(const char *, int);
+void yyerror(const char *s);
+
+static char *
+devopt(char *dev)
+{
+ char *ret = malloc(strlen(dev) + 5);
+
+ sprintf(ret, "DEV_%s", dev);
+ raisestr(ret);
+ return ret;
+}
+
+%}
+%%
+Configuration:
+ Many_specs
+ ;
+
+Many_specs:
+ Many_specs Spec
+ |
+ /* lambda */
+ ;
+
+Spec:
+ Device_spec SEMICOLON
+ |
+ Config_spec SEMICOLON
+ |
+ SEMICOLON
+ |
+ error SEMICOLON
+ ;
+
+Config_spec:
+ ARCH Save_id
+ = {
+ machinename = $2;
+ } |
+ 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; } |
+ PROFILE NUMBER
+ = { profiling = $2; } |
+ ENV ID
+ = {
+ env = $2;
+ envmode = 1;
+ } |
+ HINTS ID
+ = {
+ hints = $2;
+ hintmode = 1;
+ } |
+ INCLUDE ID
+ = { include($2, 0); };
+
+System_spec:
+ CONFIG System_id System_parameter_list
+ = { errx(1, "%s:%d: root/dump/swap specifications obsolete",
+ yyfile, yyline);}
+ |
+ CONFIG System_id
+ ;
+
+System_id:
+ Save_id
+ = { newopt(&mkopt, ns("KERNEL"), $1); };
+
+System_parameter_list:
+ System_parameter_list ID
+ | ID
+ ;
+
+Opt_list:
+ Opt_list COMMA Option
+ |
+ Option
+ ;
+
+Option:
+ Save_id
+ = {
+ char *s;
+
+ newopt(&opt, $1, NULL);
+ if ((s = strchr($1, '=')))
+ errx(1, "%s:%d: The `=' in options should not be "
+ "quoted", yyfile, yyline);
+ } |
+ Save_id EQUALS Opt_value
+ = {
+ newopt(&opt, $1, $3);
+ } ;
+
+Opt_value:
+ ID
+ = { $$ = $1; } |
+ NUMBER
+ = {
+ char buf[80];
+
+ (void) snprintf(buf, sizeof(buf), "%d", $1);
+ $$ = ns(buf);
+ } ;
+
+Save_id:
+ ID
+ = { $$ = $1; }
+ ;
+
+Mkopt_list:
+ Mkopt_list COMMA Mkoption
+ |
+ Mkoption
+ ;
+
+Mkoption:
+ Save_id EQUALS Opt_value
+ = { newopt(&mkopt, $1, $3); } ;
+
+Dev:
+ ID
+ = { $$ = $1; }
+ ;
+
+Device_spec:
+ DEVICE Dev
+ = {
+ newopt(&opt, devopt($2), ns("1"));
+ /* and the device part */
+ newdev($2, UNKNOWN);
+ } |
+ DEVICE Dev NUMBER
+ = {
+ newopt(&opt, devopt($2), ns("1"));
+ /* and the device part */
+ newdev($2, $3);
+ if ($3 == 0)
+ errx(1, "%s:%d: devices with zero units are not "
+ "likely to be correct", yyfile, yyline);
+ } ;
+
+%%
+
+void
+yyerror(const char *s)
+{
+
+ errx(1, "%s:%d: %s", yyfile, yyline + 1, s);
+}
+
+/*
+ * add a device to the list of devices
+ */
+static void
+newdev(char *name, int count)
+{
+ struct device *np;
+
+ np = (struct device *) malloc(sizeof *np);
+ memset(np, 0, sizeof(*np));
+ np->d_name = name;
+ np->d_count = count;
+ np->d_next = 0;
+ if (curp == 0)
+ dtab = np;
+ else
+ curp->d_next = np;
+ curp = np;
+}
+
+static void
+newopt(struct opt **list, char *name, char *value)
+{
+ struct opt *op;
+
+ op = (struct opt *)malloc(sizeof (struct opt));
+ memset(op, 0, sizeof(*op));
+ op->op_name = name;
+ op->op_ownfile = 0;
+ op->op_value = value;
+ op->op_next = *list;
+ *list = op;
+}
diff --git a/usr.sbin/config/configvers.h b/usr.sbin/config/configvers.h
new file mode 100644
index 0000000..fcc7265
--- /dev/null
+++ b/usr.sbin/config/configvers.h
@@ -0,0 +1,11 @@
+/*
+ * 6 digits of version. The most significant are branch indicators
+ * (eg: RELENG_2_2 = 22, -current presently = 50 etc). The least
+ * significant digits are incremented for each incompatible change.
+ *
+ * The numbering scheme is inspired by the sys/conf/newvers.sh RELDATE
+ * and <osreldate.h> system.
+ *
+ * $FreeBSD$
+ */
+#define CONFIGVERS 500010
diff --git a/usr.sbin/config/lang.l b/usr.sbin/config/lang.l
new file mode 100644
index 0000000..bef36f1
--- /dev/null
+++ b/usr.sbin/config/lang.l
@@ -0,0 +1,274 @@
+%{
+/*-
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)lang.l 8.1 (Berkeley) 6/6/93
+ * $FreeBSD$
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <string.h>
+#include "y.tab.h"
+#include "config.h"
+
+#define YY_NO_UNPUT
+
+/*
+ * Data for returning to previous files from include files.
+ */
+struct incl {
+ struct incl *in_prev; /* previous includes in effect, if any */
+ YY_BUFFER_STATE in_buf; /* previous lex state */
+ const char *in_fname; /* previous file name */
+ int in_lineno; /* previous line number */
+ int in_ateof; /* token to insert at EOF */
+};
+static struct incl *inclp;
+static const char *lastfile;
+
+/*
+ * Key word table
+ */
+
+struct kt {
+ const char *kt_name;
+ int kt_val;
+} key_words[] = {
+ { "config", CONFIG },
+ { "cpu", CPU },
+ { "device", DEVICE },
+ { "env", ENV },
+ { "hints", HINTS },
+ { "ident", IDENT },
+ { "machine", ARCH }, /* MACHINE is defined in /sys/param.h */
+ { "makeoptions", MAKEOPTIONS },
+ { "maxusers", MAXUSERS },
+ { "profile", PROFILE },
+ { "option", OPTIONS },
+ { "options", OPTIONS },
+ { "include", INCLUDE },
+ { 0, 0 },
+};
+
+
+static int endinclude(void);
+int include(const char *, int);
+int kw_lookup(char *);
+unsigned int octal(const char *);
+unsigned int hex(const char *);
+int yyerror(const char *);
+
+%}
+WORD [A-Za-z_][-A-Za-z_]*
+ID [A-Za-z_][-A-Za-z_0-9]*
+%START NONUM TOEOL
+%%
+<NONUM>{WORD} {
+ int i;
+
+ BEGIN 0;
+ if ((i = kw_lookup(yytext)) == -1)
+ {
+ yylval.str = strdup(yytext);
+ return ID;
+ }
+ return i;
+ }
+<INITIAL>{WORD}/[0-9]* {
+ int i;
+
+ if ((i = kw_lookup(yytext)) == -1)
+ REJECT;
+ if (i == DEVICE)
+ BEGIN NONUM;
+ return i;
+ }
+<INITIAL>{ID} {
+ BEGIN 0;
+ yylval.str = strdup(yytext);
+ return ID;
+ }
+\\\"[^"]+\\\" {
+ BEGIN 0;
+ yytext[yyleng-2] = '"';
+ yytext[yyleng-1] = '\0';
+ yylval.str = strdup(yytext + 1);
+ return ID;
+ }
+\"[^"]+\" {
+ BEGIN 0;
+ yytext[yyleng-1] = '\0';
+ yylval.str = strdup(yytext + 1);
+ return ID;
+ }
+<TOEOL>[^# \t\n]* {
+ BEGIN 0;
+ yylval.str = strdup(yytext);
+ return ID;
+ }
+0[0-7]* {
+ yylval.val = octal(yytext);
+ return NUMBER;
+ }
+0x[0-9a-fA-F]+ {
+ yylval.val = hex(yytext);
+ return NUMBER;
+ }
+-?[1-9][0-9]* {
+ yylval.val = atoi(yytext);
+ return NUMBER;
+ }
+"?" {
+ yylval.val = -1;
+ return NUMBER;
+ }
+\n/[ \t] {
+ yyline++;
+ }
+\n {
+ yyline++;
+ return SEMICOLON;
+ }
+#.* { /* Ignored (comment) */; }
+[ \t\f]* { /* Ignored (white space) */; }
+";" { return SEMICOLON; }
+"," { return COMMA; }
+"=" { BEGIN TOEOL; return EQUALS; }
+<<EOF>> {
+ int tok;
+
+ if (inclp == NULL)
+ return YY_NULL;
+ tok = endinclude();
+ if (tok != 0)
+ return tok;
+ /* otherwise continue scanning */
+ }
+. { return yytext[0]; }
+
+%%
+/*
+ * kw_lookup
+ * Look up a string in the keyword table. Returns a -1 if the
+ * string is not a keyword otherwise it returns the keyword number
+ */
+
+int
+kw_lookup(char *word)
+{
+ struct kt *kp;
+
+ for (kp = key_words; kp->kt_name != 0; kp++)
+ if (eq(word, kp->kt_name))
+ return kp->kt_val;
+ return -1;
+}
+
+/*
+ * Number conversion routines
+ */
+
+unsigned int
+octal(const char *str)
+{
+ unsigned int num;
+
+ (void) sscanf(str, "%o", &num);
+ return num;
+}
+
+unsigned int
+hex(const char *str)
+{
+ unsigned int num;
+
+ (void) sscanf(str+2, "%x", &num);
+ return num;
+}
+
+
+/*
+ * Open the named file for inclusion at the current point. Returns 0 on
+ * success (file opened and previous state pushed), nonzero on failure
+ * (fopen failed, complaint made). The `ateof' parameter controls the
+ * token to be inserted at the end of the include file. If ateof == 0,
+ * then nothing is inserted.
+ */
+int
+include(const char *fname, int ateof)
+{
+ FILE *fp;
+ struct incl *in;
+
+ fp = fopen(fname, "r");
+ if (fp == NULL) {
+ yyerror("cannot open file");
+ return (-1);
+ }
+ in = malloc(sizeof(*in));
+ assert(in != NULL);
+ in->in_prev = inclp;
+ in->in_buf = YY_CURRENT_BUFFER;
+ in->in_fname = yyfile;
+ in->in_lineno = yyline;
+ in->in_ateof = ateof;
+ inclp = in;
+ yy_switch_to_buffer(yy_create_buffer(fp, YY_BUF_SIZE));
+ yyfile = fname;
+ yyline = 0;
+ return (0);
+}
+
+/*
+ * Terminate the most recent inclusion.
+ */
+static int
+endinclude()
+{
+ struct incl *in;
+ int ateof;
+
+ in = inclp;
+ assert(in != NULL);
+ inclp = in->in_prev;
+ lastfile = yyfile;
+ yy_delete_buffer(YY_CURRENT_BUFFER);
+ (void)fclose(yyin);
+ yy_switch_to_buffer(in->in_buf);
+ yyfile = in->in_fname;
+ yyline = in->in_lineno;
+ ateof = in->in_ateof;
+ free(in);
+
+ return (ateof);
+}
diff --git a/usr.sbin/config/main.c b/usr.sbin/config/main.c
new file mode 100644
index 0000000..e6e3eb0
--- /dev/null
+++ b/usr.sbin/config/main.c
@@ -0,0 +1,480 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1980, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <dirent.h>
+#include "y.tab.h"
+#include "config.h"
+
+#ifndef TRUE
+#define TRUE (1)
+#endif
+
+#ifndef FALSE
+#define FALSE (0)
+#endif
+
+#define CDIR "../compile/"
+
+char * PREFIX;
+char destdir[MAXPATHLEN];
+char srcdir[MAXPATHLEN];
+
+int debugging;
+int profiling;
+
+static void configfile(void);
+static void get_srcdir(void);
+static void usage(void);
+static void cleanheaders(char *);
+
+struct hdr_list {
+ char *h_name;
+ struct hdr_list *h_next;
+} *htab;
+
+/*
+ * Config builds a set of files for building a UNIX
+ * system given a description of the desired system.
+ */
+int
+main(int argc, char **argv)
+{
+
+ struct stat buf;
+ int ch, len;
+ char *p;
+ char xxx[MAXPATHLEN];
+
+ while ((ch = getopt(argc, argv, "d:gp")) != -1)
+ switch (ch) {
+ case 'd':
+ if (*destdir == '\0')
+ strlcpy(destdir, optarg, sizeof(destdir));
+ else
+ errx(2, "directory already set");
+ break;
+ case 'g':
+ debugging++;
+ break;
+ case 'p':
+ profiling++;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1)
+ usage();
+
+ if (freopen(PREFIX = *argv, "r", stdin) == NULL)
+ err(2, "%s", PREFIX);
+
+ if (*destdir != '\0') {
+ len = strlen(destdir);
+ while (len > 1 && destdir[len - 1] == '/')
+ destdir[--len] = '\0';
+ get_srcdir();
+ } else {
+ strlcpy(destdir, CDIR, sizeof(destdir));
+ strlcat(destdir, PREFIX, sizeof(destdir));
+ }
+
+ p = path((char *)NULL);
+ if (stat(p, &buf)) {
+ if (mkdir(p, 0777))
+ err(2, "%s", p);
+ }
+ else if ((buf.st_mode & S_IFMT) != S_IFDIR)
+ errx(2, "%s isn't a directory", p);
+
+ dtab = NULL;
+ yyfile = *argv;
+ if (yyparse())
+ exit(3);
+ if (machinename == NULL) {
+ printf("Specify machine type, e.g. ``machine i386''\n");
+ exit(1);
+ }
+ /*
+ * make symbolic links in compilation directory
+ * for "sys" (to make genassym.c work along with #include <sys/xxx>)
+ * and similarly for "machine".
+ */
+ if (*srcdir == '\0')
+ (void)snprintf(xxx, sizeof(xxx), "../../include");
+ else
+ (void)snprintf(xxx, sizeof(xxx), "%s/%s/include",
+ srcdir, machinename);
+ (void) unlink(path("machine"));
+ (void) symlink(xxx, path("machine"));
+ options(); /* make options .h files */
+ makefile(); /* build Makefile */
+ headers(); /* make a lot of .h files */
+ configfile(); /* put config file into kernel*/
+ cleanheaders(p);
+ printf("Kernel build directory is %s\n", p);
+ printf("Don't forget to do a ``make depend''\n");
+ exit(0);
+}
+
+/*
+ * get_srcdir
+ * determine the root of the kernel source tree
+ * and save that in srcdir.
+ */
+static void
+get_srcdir(void)
+{
+
+ if (realpath("../..", srcdir) == NULL)
+ errx(2, "Unable to find root of source tree");
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: config [-gp] [-d destdir] sysname\n");
+ exit(1);
+}
+
+/*
+ * get_word
+ * returns EOF on end of file
+ * NULL on end of line
+ * pointer to the word otherwise
+ */
+char *
+get_word(FILE *fp)
+{
+ static char line[80];
+ int ch;
+ char *cp;
+ int escaped_nl = 0;
+
+begin:
+ while ((ch = getc(fp)) != EOF)
+ if (ch != ' ' && ch != '\t')
+ break;
+ if (ch == EOF)
+ return ((char *)EOF);
+ if (ch == '\\'){
+ escaped_nl = 1;
+ goto begin;
+ }
+ if (ch == '\n') {
+ if (escaped_nl){
+ escaped_nl = 0;
+ goto begin;
+ }
+ else
+ return (NULL);
+ }
+ cp = line;
+ *cp++ = ch;
+ while ((ch = getc(fp)) != EOF) {
+ if (isspace(ch))
+ break;
+ *cp++ = ch;
+ }
+ *cp = 0;
+ if (ch == EOF)
+ return ((char *)EOF);
+ (void) ungetc(ch, fp);
+ return (line);
+}
+
+/*
+ * get_quoted_word
+ * like get_word but will accept something in double or single quotes
+ * (to allow embedded spaces).
+ */
+char *
+get_quoted_word(FILE *fp)
+{
+ static char line[256];
+ int ch;
+ char *cp;
+ int escaped_nl = 0;
+
+begin:
+ while ((ch = getc(fp)) != EOF)
+ if (ch != ' ' && ch != '\t')
+ break;
+ if (ch == EOF)
+ return ((char *)EOF);
+ if (ch == '\\'){
+ escaped_nl = 1;
+ goto begin;
+ }
+ if (ch == '\n') {
+ if (escaped_nl){
+ escaped_nl = 0;
+ goto begin;
+ }
+ else
+ return (NULL);
+ }
+ cp = line;
+ if (ch == '"' || ch == '\'') {
+ int quote = ch;
+
+ while ((ch = getc(fp)) != EOF) {
+ if (ch == quote)
+ break;
+ if (ch == '\n') {
+ *cp = 0;
+ printf("config: missing quote reading `%s'\n",
+ line);
+ exit(2);
+ }
+ *cp++ = ch;
+ }
+ } else {
+ *cp++ = ch;
+ while ((ch = getc(fp)) != EOF) {
+ if (isspace(ch))
+ break;
+ *cp++ = ch;
+ }
+ if (ch != EOF)
+ (void) ungetc(ch, fp);
+ }
+ *cp = 0;
+ if (ch == EOF)
+ return ((char *)EOF);
+ return (line);
+}
+
+/*
+ * prepend the path to a filename
+ */
+char *
+path(const char *file)
+{
+ char *cp = NULL;
+
+ if (file)
+ asprintf(&cp, "%s/%s", destdir, file);
+ else
+ cp = strdup(destdir);
+ return (cp);
+}
+
+static void
+configfile(void)
+{
+ FILE *fi, *fo;
+ char *p;
+ int i;
+
+ fi = fopen(PREFIX, "r");
+ if (!fi)
+ err(2, "%s", PREFIX);
+ fo = fopen(p=path("config.c.new"), "w");
+ if (!fo)
+ err(2, "%s", p);
+ fprintf(fo, "#include \"opt_config.h\"\n");
+ fprintf(fo, "#ifdef INCLUDE_CONFIG_FILE \n");
+ fprintf(fo, "static const char config[] = \"\\\n");
+ fprintf(fo, "START CONFIG FILE %s\\n\\\n___", PREFIX);
+ while (EOF != (i=getc(fi))) {
+ if (i == '\n') {
+ fprintf(fo, "\\n\\\n___");
+ } else if (i == '\"') {
+ fprintf(fo, "\\\"");
+ } else if (i == '\\') {
+ fprintf(fo, "\\\\");
+ } else {
+ putc(i, fo);
+ }
+ }
+ fprintf(fo, "\\n\\\nEND CONFIG FILE %s\\n\\\n", PREFIX);
+ fprintf(fo, "\";\n");
+ fprintf(fo, "\n#endif /* INCLUDE_CONFIG_FILE */\n");
+ fclose(fi);
+ fclose(fo);
+ moveifchanged(path("config.c.new"), path("config.c"));
+}
+
+/*
+ * moveifchanged --
+ * compare two files; rename if changed.
+ */
+void
+moveifchanged(const char *from_name, const char *to_name)
+{
+ char *p, *q;
+ int changed;
+ size_t tsize;
+ struct stat from_sb, to_sb;
+ int from_fd, to_fd;
+
+ changed = 0;
+
+ if ((from_fd = open(from_name, O_RDONLY)) < 0)
+ err(EX_OSERR, "moveifchanged open(%s)", from_name);
+
+ if ((to_fd = open(to_name, O_RDONLY)) < 0)
+ changed++;
+
+ if (!changed && fstat(from_fd, &from_sb) < 0)
+ err(EX_OSERR, "moveifchanged fstat(%s)", from_name);
+
+ if (!changed && fstat(to_fd, &to_sb) < 0)
+ err(EX_OSERR, "moveifchanged fstat(%s)", to_name);
+
+ if (!changed && from_sb.st_size != to_sb.st_size)
+ changed++;
+
+ tsize = (size_t)from_sb.st_size;
+
+ if (!changed) {
+ p = mmap(NULL, tsize, PROT_READ, MAP_SHARED, from_fd, (off_t)0);
+#ifndef MAP_FAILED
+#define MAP_FAILED ((caddr_t) -1)
+#endif
+ if (p == MAP_FAILED)
+ err(EX_OSERR, "mmap %s", from_name);
+ q = mmap(NULL, tsize, PROT_READ, MAP_SHARED, to_fd, (off_t)0);
+ if (q == MAP_FAILED)
+ err(EX_OSERR, "mmap %s", to_name);
+
+ changed = memcmp(p, q, tsize);
+ munmap(p, tsize);
+ munmap(q, tsize);
+ }
+ if (changed) {
+ if (rename(from_name, to_name) < 0)
+ err(EX_OSERR, "rename(%s, %s)", from_name, to_name);
+ } else {
+ if (unlink(from_name) < 0)
+ err(EX_OSERR, "unlink(%s)", from_name);
+ }
+}
+
+static void
+cleanheaders(char *p)
+{
+ DIR *dirp;
+ struct dirent *dp;
+ struct file_list *fl;
+ struct hdr_list *hl;
+ int i;
+
+ remember("y.tab.h");
+ remember("setdefs.h");
+ for (fl = ftab; fl != NULL; fl = fl->f_next)
+ remember(fl->f_fn);
+
+ /*
+ * Scan the build directory and clean out stuff that looks like
+ * it might have been a leftover NFOO header, etc.
+ */
+ dirp = opendir(p);
+ while ((dp = readdir(dirp)) != NULL) {
+ i = dp->d_namlen - 2;
+ /* Skip non-headers */
+ if (dp->d_name[i] != '.' || dp->d_name[i + 1] != 'h')
+ continue;
+ /* Skip special stuff, eg: bus_if.h, but check opt_*.h */
+ if (index(dp->d_name, '_') &&
+ strncmp(dp->d_name, "opt_", 4) != 0)
+ continue;
+ /* Check if it is a target file */
+ for (hl = htab; hl != NULL; hl = hl->h_next) {
+ if (strcmp(dp->d_name, hl->h_name) == 0) {
+ break;
+ }
+ }
+ if (hl)
+ continue;
+ printf("Removing stale header: %s\n", dp->d_name);
+ unlink(path(dp->d_name));
+ }
+ (void)closedir(dirp);
+}
+
+void
+remember(const char *file)
+{
+ char *s;
+ struct hdr_list *hl;
+
+ if ((s = strrchr(file, '/')) != NULL)
+ s = ns(s + 1);
+ else
+ s = ns(file);
+
+ if (index(s, '_') && strncmp(s, "opt_", 4) != 0) {
+ free(s);
+ return;
+ }
+ for (hl = htab; hl != NULL; hl = hl->h_next) {
+ if (strcmp(s, hl->h_name) == 0) {
+ free(s);
+ return;
+ }
+ }
+ hl = malloc(sizeof(*hl));
+ bzero(hl, sizeof(*hl));
+ hl->h_name = s;
+ hl->h_next = htab;
+ htab = hl;
+}
diff --git a/usr.sbin/config/mkheaders.c b/usr.sbin/config/mkheaders.c
new file mode 100644
index 0000000..bc30f55
--- /dev/null
+++ b/usr.sbin/config/mkheaders.c
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)mkheaders.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/*
+ * Make all the .h files for the optional entries
+ */
+
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/param.h>
+#include "config.h"
+#include "y.tab.h"
+
+static void do_header(char *, int);
+static char *toheader(char *);
+static char *tomacro(char *);
+
+void
+headers(void)
+{
+ struct file_list *fl;
+ struct device *dp;
+ int match;
+
+ for (fl = ftab; fl != 0; fl = fl->f_next) {
+ if (fl->f_needs != 0) {
+ match = 0;
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ if (eq(dp->d_name, fl->f_needs)) {
+ match++;
+ dp->d_done |= DEVDONE;
+ }
+ }
+ if (fl->f_flags & NEED_COUNT)
+ do_header(fl->f_needs, match);
+ }
+ }
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ if (!(dp->d_done & DEVDONE))
+ errx(1, "Error: device \"%s\" is unknown",
+ dp->d_name);
+ }
+}
+
+static void
+do_header(char *dev, int match)
+{
+ char *file, *name, *inw;
+ struct file_list *fl, *fl_head, *tflp;
+ struct device *dp;
+ FILE *inf, *outf;
+ int inc, oldcount;
+ 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 (eq(dp->d_name, dev)) {
+ count =
+ dp->d_count != UNKNOWN ? dp->d_count : 1;
+ break;
+ }
+ }
+ file = toheader(dev);
+ name = tomacro(dev);
+ if (match)
+ printf("FYI: static unit limits for %s are set: %s=%d\n", dev, name, count);
+ remember(file);
+ inf = fopen(file, "r");
+ oldcount = -1;
+ if (inf == 0) {
+ outf = fopen(file, "w");
+ if (outf == 0)
+ err(1, "%s", file);
+ fprintf(outf, "#define %s %d\n", name, count);
+ (void) fclose(outf);
+ return;
+ }
+ 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
+ */
+static char *
+toheader(char *dev)
+{
+ static char hbuf[MAXPATHLEN];
+
+ snprintf(hbuf, sizeof(hbuf), "%s.h", path(dev));
+ return (hbuf);
+}
+
+/*
+ * convert a dev name to a macro name
+ */
+static char *
+tomacro(char *dev)
+{
+ static char mbuf[20];
+ char *cp;
+
+ cp = mbuf;
+ *cp++ = 'N';
+ while (*dev)
+ *cp++ = islower(*dev) ? toupper(*dev++) : *dev++;
+ *cp++ = 0;
+ return (mbuf);
+}
diff --git a/usr.sbin/config/mkmakefile.c b/usr.sbin/config/mkmakefile.c
new file mode 100644
index 0000000..c24a121
--- /dev/null
+++ b/usr.sbin/config/mkmakefile.c
@@ -0,0 +1,789 @@
+/*
+ * Copyright (c) 1993, 19801990
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)mkmakefile.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/*
+ * Build the makefile for the system, from
+ * the information in the files files and the
+ * additional files for the machine being compiled to.
+ */
+
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/param.h>
+#include "y.tab.h"
+#include "config.h"
+#include "configvers.h"
+
+#define next_word(fp, wd) \
+ { char *word = get_word(fp); \
+ if (word == (char *)EOF) \
+ return; \
+ else \
+ wd = word; \
+ }
+#define next_quoted_word(fp, wd) \
+ { char *word = get_quoted_word(fp); \
+ if (word == (char *)EOF) \
+ return; \
+ else \
+ wd = word; \
+ }
+
+static struct file_list *fcur;
+
+static char *tail(char *);
+static void do_clean(FILE *);
+static void do_rules(FILE *);
+static void do_xxfiles(char *, FILE *);
+static void do_objs(FILE *);
+static void do_before_depend(FILE *);
+static int opteq(const char *, const char *);
+static void read_files(void);
+
+/*
+ * Lookup a file, by name.
+ */
+static struct file_list *
+fl_lookup(char *file)
+{
+ struct file_list *fp;
+
+ for (fp = ftab ; fp != 0; fp = fp->f_next) {
+ if (eq(fp->f_fn, file))
+ return (fp);
+ }
+ return (0);
+}
+
+/*
+ * Make a new file list entry
+ */
+static struct file_list *
+new_fent(void)
+{
+ struct file_list *fp;
+
+ fp = (struct file_list *) malloc(sizeof *fp);
+ bzero(fp, sizeof *fp);
+ if (fcur == 0)
+ fcur = ftab = fp;
+ else
+ fcur->f_next = fp;
+ fcur = fp;
+ return (fp);
+}
+
+/*
+ * Build the makefile from the skeleton
+ */
+void
+makefile(void)
+{
+ FILE *ifp, *ofp;
+ char line[BUFSIZ];
+ struct opt *op;
+ int versreq;
+ char *s;
+
+ read_files();
+ snprintf(line, sizeof(line), "../../conf/Makefile.%s", machinename);
+ ifp = fopen(line, "r");
+ if (ifp == 0) {
+ snprintf(line, sizeof(line), "Makefile.%s", machinename);
+ ifp = fopen(line, "r");
+ }
+ if (ifp == 0)
+ err(1, "%s", line);
+ ofp = fopen(path("Makefile.new"), "w");
+ if (ofp == 0)
+ err(1, "%s", path("Makefile.new"));
+ fprintf(ofp, "KERN_IDENT=%s\n", raisestr(ident));
+ fprintf(ofp, "IDENT=");
+ if (profiling)
+ fprintf(ofp, " -DGPROF");
+
+ if (cputype == 0) {
+ printf("cpu type must be specified\n");
+ exit(1);
+ }
+ fprintf(ofp, "\n");
+ 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);
+ }
+ if (*srcdir != '\0')
+ fprintf(ofp,"S=%s\n", srcdir);
+ while (fgets(line, BUFSIZ, ifp) != 0) {
+ if (*line != '%') {
+ fprintf(ofp, "%s", line);
+ continue;
+ }
+ if (eq(line, "%BEFORE_DEPEND\n"))
+ do_before_depend(ofp);
+ else if (eq(line, "%OBJS\n"))
+ do_objs(ofp);
+ else if (strncmp(line, "%FILES.", 7) == 0)
+ do_xxfiles(line, ofp);
+ else if (eq(line, "%RULES\n"))
+ do_rules(ofp);
+ else if (eq(line, "%CLEAN\n"))
+ do_clean(ofp);
+ else if (strncmp(line, "%VERSREQ=", sizeof("%VERSREQ=") - 1) == 0) {
+ versreq = atoi(line + sizeof("%VERSREQ=") - 1);
+ if (versreq != CONFIGVERS) {
+ fprintf(stderr, "ERROR: version of config(8) does not match kernel!\n");
+ fprintf(stderr, "config version = %d, ", CONFIGVERS);
+ fprintf(stderr, "version required = %d\n\n", versreq);
+ fprintf(stderr, "Make sure that /usr/src/usr.sbin/config is in sync\n");
+ fprintf(stderr, "with your /usr/src/sys and install a new config binary\n");
+ fprintf(stderr, "before trying this again.\n\n");
+ fprintf(stderr, "If running the new config fails check your config\n");
+ fprintf(stderr, "file against the GENERIC or LINT config files for\n");
+ fprintf(stderr, "changes in config syntax, or option/device naming\n");
+ fprintf(stderr, "conventions\n\n");
+ exit(1);
+ }
+ } else
+ fprintf(stderr,
+ "Unknown %% construct in generic makefile: %s",
+ line);
+ }
+ (void) fclose(ifp);
+ (void) fclose(ofp);
+ moveifchanged(path("Makefile.new"), path("Makefile"));
+
+ /* XXX makefile() should make the Makefile, not hints.c. */
+ if (hints) {
+ ifp = fopen(hints, "r");
+ if (ifp == NULL)
+ err(1, "%s", hints);
+ } else {
+ ifp = NULL;
+ }
+ ofp = fopen(path("hints.c.new"), "w");
+ if (ofp == NULL)
+ err(1, "%s", path("hints.c.new"));
+ fprintf(ofp, "#include <sys/types.h>\n");
+ fprintf(ofp, "#include <sys/systm.h>\n");
+ fprintf(ofp, "\n");
+ fprintf(ofp, "int hintmode = %d;\n", hintmode);
+ fprintf(ofp, "char static_hints[] = {\n");
+ if (ifp) {
+ while (fgets(line, BUFSIZ, ifp) != 0) {
+ /* zap trailing CR and/or LF */
+ while ((s = rindex(line, '\n')) != NULL)
+ *s = '\0';
+ while ((s = rindex(line, '\r')) != NULL)
+ *s = '\0';
+ /* remove # comments */
+ s = index(line, '#');
+ if (s)
+ *s = '\0';
+ /* remove any whitespace and " characters */
+ s = line;
+ while (*s) {
+ if (*s == ' ' || *s == '\t' || *s == '"') {
+ while (*s) {
+ s[0] = s[1];
+ s++;
+ }
+ /* start over */
+ s = line;
+ continue;
+ }
+ s++;
+ }
+ /* anything left? */
+ if (*line == '\0')
+ continue;
+ fprintf(ofp, "\"%s\\0\"\n", line);
+ }
+ }
+ fprintf(ofp, "\"\\0\"\n};\n");
+ if (ifp)
+ fclose(ifp);
+ fclose(ofp);
+ moveifchanged(path("hints.c.new"), path("hints.c"));
+
+ /* XXX makefile() should make the Makefile, not env.c. */
+ if (env) {
+ ifp = fopen(env, "r");
+ if (ifp == NULL)
+ err(1, "%s", env);
+ } else {
+ ifp = NULL;
+ }
+ ofp = fopen(path("env.c.new"), "w");
+ if (ofp == NULL)
+ err(1, "%s", path("env.c.new"));
+ fprintf(ofp, "#include <sys/types.h>\n");
+ fprintf(ofp, "#include <sys/systm.h>\n");
+ fprintf(ofp, "\n");
+ fprintf(ofp, "int envmode = %d;\n", envmode);
+ fprintf(ofp, "char static_env[] = {\n");
+ if (ifp) {
+ while (fgets(line, BUFSIZ, ifp) != 0) {
+ /* zap trailing CR and/or LF */
+ while ((s = rindex(line, '\n')) != NULL)
+ *s = '\0';
+ while ((s = rindex(line, '\r')) != NULL)
+ *s = '\0';
+ /* remove # comments */
+ s = index(line, '#');
+ if (s)
+ *s = '\0';
+ /* remove any whitespace and " characters */
+ s = line;
+ while (*s) {
+ if (*s == ' ' || *s == '\t' || *s == '"') {
+ while (*s) {
+ s[0] = s[1];
+ s++;
+ }
+ /* start over */
+ s = line;
+ continue;
+ }
+ s++;
+ }
+ /* anything left? */
+ if (*line == '\0')
+ continue;
+ fprintf(ofp, "\"%s\\0\"\n", line);
+ }
+ }
+ fprintf(ofp, "\"\\0\"\n};\n");
+ if (ifp)
+ fclose(ifp);
+ fclose(ofp);
+ moveifchanged(path("env.c.new"), path("env.c"));
+}
+
+/*
+ * Read in the information about files used in making the system.
+ * Store it in the ftab linked list.
+ */
+static void
+read_files(void)
+{
+ FILE *fp;
+ struct file_list *tp, *pf;
+ struct device *dp;
+ struct opt *op;
+ char *wd, *this, *needs, *compilewith, *depends, *clean, *warning;
+ char fname[MAXPATHLEN];
+ int nreqs, first = 1, isdup, std, filetype,
+ imp_rule, no_obj, needcount, before_depend, mandatory, nowerror;
+
+ ftab = 0;
+ if (ident == NULL) {
+ printf("no ident line specified\n");
+ exit(1);
+ }
+ (void) snprintf(fname, sizeof(fname), "../../conf/files");
+openit:
+ fp = fopen(fname, "r");
+ if (fp == 0)
+ err(1, "%s", fname);
+next:
+ /*
+ * filename [ standard | mandatory | optional | count ]
+ * [ dev* | profiling-routine ] [ no-obj ]
+ * [ compile-with "compile rule" [no-implicit-rule] ]
+ * [ dependency "dependency-list"] [ before-depend ]
+ * [ clean "file-list"] [ warning "text warning" ]
+ */
+ wd = get_word(fp);
+ if (wd == (char *)EOF) {
+ (void) fclose(fp);
+ if (first == 1) {
+ first++;
+ (void) snprintf(fname, sizeof(fname),
+ "../../conf/files.%s", machinename);
+ fp = fopen(fname, "r");
+ if (fp != 0)
+ goto next;
+ (void) snprintf(fname, sizeof(fname),
+ "files.%s", machinename);
+ goto openit;
+ }
+ return;
+ }
+ if (wd == 0)
+ goto next;
+ if (wd[0] == '#')
+ {
+ while (((wd = get_word(fp)) != (char *)EOF) && wd)
+ ;
+ goto next;
+ }
+ this = ns(wd);
+ next_word(fp, wd);
+ if (wd == 0) {
+ printf("%s: No type for %s.\n",
+ fname, this);
+ exit(1);
+ }
+ if ((pf = fl_lookup(this)) && (pf->f_type != INVISIBLE || pf->f_flags))
+ isdup = ISDUP;
+ else
+ isdup = 0;
+ tp = 0;
+ nreqs = 0;
+ compilewith = 0;
+ depends = 0;
+ clean = 0;
+ warning = 0;
+ needs = 0;
+ std = mandatory = 0;
+ imp_rule = 0;
+ no_obj = 0;
+ needcount = 0;
+ before_depend = 0;
+ nowerror = 0;
+ filetype = NORMAL;
+ if (eq(wd, "standard")) {
+ std = 1;
+ /*
+ * If an entry is marked "mandatory", config will abort if it's
+ * not called by a configuration line in the config file. Apart
+ * from this, the device is handled like one marked "optional".
+ */
+ } else if (eq(wd, "mandatory")) {
+ mandatory = 1;
+ } else if (eq(wd, "count")) {
+ needcount = 1;
+ } else if (!eq(wd, "optional")) {
+ printf("%s: %s must be count, optional, mandatory or standard\n",
+ fname, this);
+ exit(1);
+ }
+nextparam:
+ next_word(fp, wd);
+ if (wd == 0)
+ goto doneparam;
+ if (eq(wd, "no-obj")) {
+ no_obj++;
+ goto nextparam;
+ }
+ if (eq(wd, "no-implicit-rule")) {
+ if (compilewith == 0) {
+ printf("%s: alternate rule required when "
+ "\"no-implicit-rule\" is specified.\n",
+ fname);
+ }
+ imp_rule++;
+ goto nextparam;
+ }
+ if (eq(wd, "before-depend")) {
+ before_depend++;
+ goto nextparam;
+ }
+ if (eq(wd, "dependency")) {
+ next_quoted_word(fp, wd);
+ if (wd == 0) {
+ printf("%s: %s missing compile command string.\n",
+ fname, this);
+ exit(1);
+ }
+ depends = ns(wd);
+ goto nextparam;
+ }
+ if (eq(wd, "clean")) {
+ next_quoted_word(fp, wd);
+ if (wd == 0) {
+ printf("%s: %s missing clean file list.\n",
+ fname, this);
+ exit(1);
+ }
+ clean = ns(wd);
+ goto nextparam;
+ }
+ if (eq(wd, "compile-with")) {
+ next_quoted_word(fp, wd);
+ if (wd == 0) {
+ printf("%s: %s missing compile command string.\n",
+ fname, this);
+ exit(1);
+ }
+ compilewith = ns(wd);
+ goto nextparam;
+ }
+ if (eq(wd, "warning")) {
+ next_quoted_word(fp, wd);
+ if (wd == 0) {
+ printf("%s: %s missing warning text string.\n",
+ fname, this);
+ exit(1);
+ }
+ warning = ns(wd);
+ goto nextparam;
+ }
+ nreqs++;
+ if (eq(wd, "local")) {
+ filetype = LOCAL;
+ goto nextparam;
+ }
+ if (eq(wd, "no-depend")) {
+ filetype = NODEPEND;
+ goto nextparam;
+ }
+ if (eq(wd, "profiling-routine")) {
+ filetype = PROFILING;
+ goto nextparam;
+ }
+ if (eq(wd, "nowerror")) {
+ nowerror = 1;
+ goto nextparam;
+ }
+ if (needs == 0 && nreqs == 1)
+ needs = ns(wd);
+ if (isdup)
+ goto invis;
+ for (dp = dtab; dp != 0; dp = dp->d_next)
+ if (eq(dp->d_name, wd)) {
+ if (std && dp->d_count <= 0)
+ dp->d_count = 1;
+ goto nextparam;
+ }
+ if (mandatory) {
+ printf("%s: mandatory device \"%s\" not found\n",
+ fname, wd);
+ exit(1);
+ }
+ if (std) {
+ printf("standard entry %s has a device keyword - %s!\n",
+ this, wd);
+ exit(1);
+ }
+ 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;
+ if (needcount)
+ tp->f_flags |= NEED_COUNT;
+ tp->f_compilewith = compilewith;
+ tp->f_depends = depends;
+ tp->f_clean = clean;
+ tp->f_warn = warning;
+ goto next;
+
+doneparam:
+ if (std == 0 && nreqs == 0) {
+ printf("%s: what is %s optional on?\n",
+ fname, this);
+ exit(1);
+ }
+
+ if (wd) {
+ printf("%s: syntax error describing %s\n",
+ fname, this);
+ exit(1);
+ }
+ if (filetype == PROFILING && profiling == 0)
+ goto next;
+ if (tp == 0)
+ tp = new_fent();
+ tp->f_fn = this;
+ tp->f_type = filetype;
+ tp->f_flags &= ~ISDUP;
+ if (imp_rule)
+ tp->f_flags |= NO_IMPLCT_RULE;
+ if (no_obj)
+ tp->f_flags |= NO_OBJ;
+ if (before_depend)
+ tp->f_flags |= BEFORE_DEPEND;
+ if (needcount)
+ tp->f_flags |= NEED_COUNT;
+ if (nowerror)
+ tp->f_flags |= NOWERROR;
+ tp->f_needs = needs;
+ tp->f_compilewith = compilewith;
+ tp->f_depends = depends;
+ tp->f_clean = clean;
+ tp->f_warn = warning;
+ if (pf && pf->f_type == INVISIBLE)
+ pf->f_flags |= ISDUP; /* mark as duplicate */
+ goto next;
+}
+
+static int
+opteq(const char *cp, const char *dp)
+{
+ char c, d;
+
+ for (; ; cp++, dp++) {
+ if (*cp != *dp) {
+ c = isupper(*cp) ? tolower(*cp) : *cp;
+ d = isupper(*dp) ? tolower(*dp) : *dp;
+ if (c != d)
+ return (0);
+ }
+ if (*cp == 0)
+ return (1);
+ }
+}
+
+static void
+do_before_depend(FILE *fp)
+{
+ struct file_list *tp;
+ int lpos, len;
+
+ fputs("BEFORE_DEPEND=", fp);
+ lpos = 15;
+ 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);
+}
+
+static void
+do_objs(FILE *fp)
+{
+ struct file_list *tp;
+ int lpos, len;
+ char *cp, och, *sp;
+
+ 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);
+ cp = sp + (len = strlen(sp)) - 1;
+ och = *cp;
+ *cp = 'o';
+ if (len + lpos > 72) {
+ lpos = 8;
+ fprintf(fp, "\\\n\t");
+ }
+ fprintf(fp, "%s ", sp);
+ lpos += len + 1;
+ *cp = och;
+ }
+ if (lpos != 8)
+ putc('\n', fp);
+}
+
+static void
+do_xxfiles(char *tag, FILE *fp)
+{
+ struct file_list *tp;
+ int lpos, len, slen;
+ char *suff, *SUFF;
+
+ if (tag[strlen(tag) - 1] == '\n')
+ tag[strlen(tag) - 1] = '\0';
+
+ suff = ns(tag + 7);
+ SUFF = ns(suff);
+ raisestr(SUFF);
+ slen = strlen(suff);
+
+ fprintf(fp, "%sFILES=", SUFF);
+ lpos = 8;
+ 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 - slen - 1] != '.')
+ continue;
+ if (strcasecmp(&tp->f_fn[len - slen], suff) != 0)
+ continue;
+ if ((len = 3 + len) + lpos > 72) {
+ lpos = 8;
+ fputs("\\\n\t", fp);
+ }
+ if (tp->f_type != LOCAL)
+ fprintf(fp, "$S/%s ", tp->f_fn);
+ else
+ fprintf(fp, "%s ", tp->f_fn);
+ lpos += len + 1;
+ }
+ if (lpos != 8)
+ putc('\n', fp);
+}
+
+static char *
+tail(char *fn)
+{
+ char *cp;
+
+ cp = rindex(fn, '/');
+ if (cp == 0)
+ return (fn);
+ return (cp+1);
+}
+
+/*
+ * Create the makerules for each file
+ * which is part of the system.
+ */
+static void
+do_rules(FILE *f)
+{
+ char *cp, *np, och, *tp;
+ struct file_list *ftp;
+ char *compilewith;
+
+ for (ftp = ftab; ftp != 0; ftp = ftp->f_next) {
+ if (ftp->f_type == INVISIBLE)
+ continue;
+ if (ftp->f_warn)
+ printf("WARNING: %s\n", ftp->f_warn);
+ cp = (np = ftp->f_fn) + strlen(ftp->f_fn) - 1;
+ och = *cp;
+ if (ftp->f_flags & NO_IMPLCT_RULE) {
+ if (ftp->f_depends)
+ fprintf(f, "%s: %s\n", np, ftp->f_depends);
+ else
+ fprintf(f, "%s: \n", np);
+ }
+ else {
+ *cp = '\0';
+ if (och == 'o') {
+ fprintf(f, "%so:\n\t-cp $S/%so .\n\n",
+ tail(np), np);
+ continue;
+ }
+ if (ftp->f_depends)
+ fprintf(f, "%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);
+ compilewith = ftp->f_compilewith;
+ if (compilewith == 0) {
+ const char *ftype = NULL;
+ static char cmd[128];
+
+ switch (ftp->f_type) {
+
+ case NORMAL:
+ ftype = "NORMAL";
+ break;
+
+ case PROFILING:
+ if (!profiling)
+ continue;
+ ftype = "PROFILE";
+ break;
+
+ default:
+ printf("config: don't know rules for %s\n", np);
+ break;
+ }
+ snprintf(cmd, sizeof(cmd), "${%s_%c%s}", ftype,
+ toupper(och),
+ ftp->f_flags & NOWERROR ? "_NOWERROR" : "");
+ compilewith = cmd;
+ }
+ *cp = och;
+ fprintf(f, "\t%s\n\n", compilewith);
+ }
+}
+
+static void
+do_clean(FILE *fp)
+{
+ struct file_list *tp;
+ int lpos, len;
+
+ fputs("CLEAN=", fp);
+ lpos = 7;
+ 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);
+}
+
+char *
+raisestr(char *str)
+{
+ char *cp = str;
+
+ while (*str) {
+ if (islower(*str))
+ *str = toupper(*str);
+ str++;
+ }
+ return (cp);
+}
diff --git a/usr.sbin/config/mkoptions.c b/usr.sbin/config/mkoptions.c
new file mode 100644
index 0000000..77f6168
--- /dev/null
+++ b/usr.sbin/config/mkoptions.c
@@ -0,0 +1,372 @@
+/*
+ * Copyright (c) 1995 Peter Wemm
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)mkheaders.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/*
+ * Make all the .h files for the optional entries
+ */
+
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/param.h>
+#include "config.h"
+#include "y.tab.h"
+
+static struct users {
+ int u_default;
+ int u_min;
+ int u_max;
+} users= { 8, 2, 512 };
+
+static char *lower(char *);
+static void read_options(void);
+static void do_option(char *);
+static char *tooption(char *);
+
+void
+options(void)
+{
+ char buf[40];
+ struct cputype *cp;
+ struct opt_list *ol;
+ struct opt *op;
+
+ /* Fake the cpu types as options. */
+ 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;
+ }
+
+ if (maxusers == 0) {
+ /* printf("maxusers not specified; will auto-size\n"); */
+ } else if (maxusers < users.u_min) {
+ printf("minimum of %d maxusers assumed\n", users.u_min);
+ maxusers = users.u_min;
+ } else if (maxusers > users.u_max)
+ printf("warning: maxusers > %d (%d)\n", users.u_max, maxusers);
+
+ /* Fake MAXUSERS as an option. */
+ op = (struct opt *)malloc(sizeof(*op));
+ memset(op, 0, sizeof(*op));
+ op->op_name = ns("MAXUSERS");
+ snprintf(buf, sizeof(buf), "%d", maxusers);
+ op->op_value = ns(buf);
+ op->op_next = opt;
+ opt = op;
+
+ read_options();
+ for (ol = otab; ol != 0; ol = ol->o_next)
+ do_option(ol->o_name);
+ for (op = opt; op; op = op->op_next) {
+ if (!op->op_ownfile && strncmp(op->op_name, "DEV_", 4)) {
+ printf("%s: unknown option \"%s\"\n",
+ PREFIX, op->op_name);
+ exit(1);
+ }
+ }
+}
+
+/*
+ * Generate an <options>.h file
+ */
+
+static void
+do_option(char *name)
+{
+ char *file, *inw;
+ const char *basefile;
+ struct opt_list *ol;
+ struct opt *op, *op_head, *topp;
+ FILE *inf, *outf;
+ char *value;
+ char *oldvalue;
+ int seen;
+ int tidy;
+
+ 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: option \"%s\" redefined from %s to %s\n",
+ PREFIX, op->op_name, oldvalue,
+ value);
+ op->op_ownfile++;
+ }
+ }
+
+ remember(file);
+ inf = fopen(file, "r");
+ if (inf == 0) {
+ outf = fopen(file, "w");
+ if (outf == 0)
+ err(1, "%s", file);
+
+ /* was the option in the config file? */
+ if (value) {
+ fprintf(outf, "#define %s %s\n", name, value);
+ } /* else empty file */
+
+ (void) fclose(outf);
+ return;
+ }
+ basefile = "";
+ for (ol = otab; ol != 0; ol = ol->o_next)
+ if (eq(name, ol->o_name)) {
+ basefile = ol->o_file;
+ break;
+ }
+ oldvalue = NULL;
+ op_head = NULL;
+ seen = 0;
+ tidy = 0;
+ for (;;) {
+ char *cp;
+ char *invalue;
+
+ /* get the #define */
+ if ((inw = get_word(inf)) == 0 || inw == (char *)EOF)
+ break;
+ /* get the option name */
+ if ((inw = get_word(inf)) == 0 || inw == (char *)EOF)
+ break;
+ inw = ns(inw);
+ /* get the option value */
+ if ((cp = get_word(inf)) == 0 || cp == (char *)EOF)
+ break;
+ /* option value */
+ invalue = ns(cp); /* malloced */
+ if (eq(inw, name)) {
+ oldvalue = invalue;
+ invalue = value;
+ seen++;
+ }
+ for (ol = otab; ol != 0; ol = ol->o_next)
+ if (eq(inw, ol->o_name))
+ break;
+ if (!eq(inw, name) && !ol) {
+ printf("WARNING: unknown option `%s' removed from %s\n",
+ inw, file);
+ tidy++;
+ } else if (ol != NULL && !eq(basefile, ol->o_file)) {
+ printf("WARNING: option `%s' moved from %s to %s\n",
+ inw, basefile, ol->o_file);
+ tidy++;
+ } else {
+ op = (struct opt *) malloc(sizeof *op);
+ bzero(op, sizeof(*op));
+ op->op_name = inw;
+ op->op_value = invalue;
+ op->op_next = op_head;
+ op_head = op;
+ }
+
+ /* EOL? */
+ cp = get_word(inf);
+ if (cp == (char *)EOF)
+ break;
+ }
+ (void) fclose(inf);
+ if (!tidy && ((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.
+ */
+static char *
+tooption(char *name)
+{
+ static char hbuf[MAXPATHLEN];
+ char nbuf[MAXPATHLEN];
+ struct opt_list *po;
+
+ /* "cannot happen"? the otab list should be complete.. */
+ (void) strlcpy(nbuf, "options.h", sizeof(nbuf));
+
+ for (po = otab ; po != 0; po = po->o_next) {
+ if (eq(po->o_name, name)) {
+ strlcpy(nbuf, po->o_file, sizeof(nbuf));
+ break;
+ }
+ }
+
+ (void) strlcpy(hbuf, path(nbuf), sizeof(hbuf));
+ return (hbuf);
+}
+
+/*
+ * read the options and options.<machine> files
+ */
+static void
+read_options(void)
+{
+ FILE *fp;
+ char fname[MAXPATHLEN];
+ char *wd, *this, *val;
+ struct opt_list *po;
+ int first = 1;
+ char genopt[MAXPATHLEN];
+
+ otab = 0;
+ if (ident == NULL) {
+ printf("no ident line specified\n");
+ exit(1);
+ }
+ (void) snprintf(fname, sizeof(fname), "../../conf/options");
+openit:
+ fp = fopen(fname, "r");
+ if (fp == 0) {
+ return;
+ }
+next:
+ wd = get_word(fp);
+ if (wd == (char *)EOF) {
+ (void) fclose(fp);
+ if (first == 1) {
+ first++;
+ (void) snprintf(fname, sizeof fname, "../../conf/options.%s", machinename);
+ fp = fopen(fname, "r");
+ if (fp != 0)
+ goto next;
+ (void) snprintf(fname, sizeof fname, "options.%s", machinename);
+ goto openit;
+ }
+ return;
+ }
+ if (wd == 0)
+ goto next;
+ if (wd[0] == '#')
+ {
+ while (((wd = get_word(fp)) != (char *)EOF) && wd)
+ ;
+ goto next;
+ }
+ this = ns(wd);
+ val = get_word(fp);
+ if (val == (char *)EOF)
+ return;
+ if (val == 0) {
+ char *s = ns(this);
+ (void) snprintf(genopt, sizeof(genopt), "opt_%s.h", lower(s));
+ val = genopt;
+ free(s);
+ }
+ val = ns(val);
+
+ 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(char *str)
+{
+ char *cp = str;
+
+ while (*str) {
+ if (isupper(*str))
+ *str = tolower(*str);
+ str++;
+ }
+ return (cp);
+}
diff --git a/usr.sbin/cron/Makefile b/usr.sbin/cron/Makefile
new file mode 100644
index 0000000..62f853c
--- /dev/null
+++ b/usr.sbin/cron/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+SUBDIR= lib cron crontab
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/cron/Makefile.inc b/usr.sbin/cron/Makefile.inc
new file mode 100644
index 0000000..d7844f5
--- /dev/null
+++ b/usr.sbin/cron/Makefile.inc
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+.if exists(${.OBJDIR}/../lib)
+LIBCRON= ${.OBJDIR}/../lib/libcron.a
+.else
+LIBCRON= ${.CURDIR}/../lib/libcron.a
+.endif
+
+.include "../Makefile.inc"
diff --git a/usr.sbin/cron/cron/Makefile b/usr.sbin/cron/cron/Makefile
new file mode 100644
index 0000000..d578da7
--- /dev/null
+++ b/usr.sbin/cron/cron/Makefile
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+PROG= cron
+MAN= cron.8
+SRCS= cron.c database.c do_command.c job.c user.c popen.c
+
+CFLAGS+= -DLOGIN_CAP
+
+DPADD= ${LIBCRON} ${LIBUTIL}
+LDADD= ${LIBCRON} -lutil
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/cron/cron/compat.h b/usr.sbin/cron/cron/compat.h
new file mode 100644
index 0000000..905c3aa
--- /dev/null
+++ b/usr.sbin/cron/cron/compat.h
@@ -0,0 +1,140 @@
+/* Copyright 1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ */
+
+/*
+ * $FreeBSD$
+ */
+
+#ifndef __P
+# ifdef __STDC__
+# define __P(x) x
+# else
+# define __P(x) ()
+# define const
+# endif
+#endif
+
+#if defined(UNIXPC) || defined(unixpc)
+# define UNIXPC 1
+# define ATT 1
+#endif
+
+#if defined(hpux) || defined(_hpux) || defined(__hpux)
+# define HPUX 1
+# define seteuid(e) setresuid(-1,e,-1)
+# define setreuid(r,e) setresuid(r,e,-1)
+#endif
+
+#if defined(_IBMR2)
+# define AIX 1
+#endif
+
+#if defined(__convex__)
+# define CONVEX 1
+#endif
+
+#if defined(sgi) || defined(_sgi) || defined(__sgi)
+# define IRIX 1
+/* IRIX 4 hdrs are broken: one cannot #include both <stdio.h>
+ * and <stdlib.h> because they disagree on system(), perror().
+ * Therefore we must zap the "const" keyword BEFORE including
+ * either of them.
+ */
+# define const
+#endif
+
+#if defined(_UNICOS)
+# define UNICOS 1
+#endif
+
+#ifndef POSIX
+# if (BSD >= 199103) || defined(__linux) || defined(ultrix) || defined(AIX) ||\
+ defined(HPUX) || defined(CONVEX) || defined(IRIX)
+# define POSIX
+# endif
+#endif
+
+#ifndef BSD
+# if defined(ultrix)
+# define BSD 198902
+# endif
+#endif
+
+/*****************************************************************/
+
+#if !defined(BSD) && !defined(HPUX) && !defined(CONVEX) && !defined(__linux)
+# define NEED_VFORK
+#endif
+
+#if (!defined(BSD) || (BSD < 198902)) && !defined(__linux) && \
+ !defined(IRIX) && !defined(NeXT) && !defined(HPUX)
+# define NEED_STRCASECMP
+#endif
+
+#if (!defined(BSD) || (BSD < 198911)) && !defined(__linux) &&\
+ !defined(IRIX) && !defined(UNICOS) && !defined(HPUX)
+# define NEED_STRDUP
+#endif
+
+#if (!defined(BSD) || (BSD < 198911)) && !defined(POSIX) && !defined(NeXT)
+# define NEED_STRERROR
+#endif
+
+#if defined(HPUX) || defined(AIX) || defined(UNIXPC)
+# define NEED_FLOCK
+#endif
+
+#ifndef POSIX
+# define NEED_SETSID
+#endif
+
+#if (defined(POSIX) && !defined(BSD)) && !defined(__linux)
+# define NEED_GETDTABLESIZE
+#endif
+
+#ifdef POSIX
+#include <unistd.h>
+#ifdef _POSIX_SAVED_IDS
+# define HAVE_SAVED_UIDS
+#endif
+#endif
+
+#if !defined(ATT) && !defined(__linux) && !defined(IRIX) && !defined(UNICOS)
+# define USE_SIGCHLD
+#endif
+
+#if !defined(AIX) && !defined(UNICOS)
+# define SYS_TIME_H 1
+#else
+# define SYS_TIME_H 0
+#endif
+
+#if defined(BSD) && !defined(POSIX)
+# define USE_UTIMES
+#endif
+
+#if defined(AIX) || defined(HPUX) || defined(IRIX)
+# define NEED_SETENV
+#endif
+
+#if !defined(UNICOS) && !defined(UNIXPC)
+# define HAS_FCHOWN
+#endif
+
+#if !defined(UNICOS) && !defined(UNIXPC)
+# define HAS_FCHMOD
+#endif
diff --git a/usr.sbin/cron/cron/config.h b/usr.sbin/cron/cron/config.h
new file mode 100644
index 0000000..6d77da2
--- /dev/null
+++ b/usr.sbin/cron/cron/config.h
@@ -0,0 +1,87 @@
+/* Copyright 1988,1990,1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ */
+
+/* config.h - configurables for Vixie Cron
+ *
+ * $FreeBSD$
+ */
+
+#if !defined(_PATH_SENDMAIL)
+# define _PATH_SENDMAIL "/usr/lib/sendmail"
+#endif /*SENDMAIL*/
+
+/*
+ * these are site-dependent
+ */
+
+#ifndef DEBUGGING
+#define DEBUGGING 1 /* 1 or 0 -- do you want debugging code built in? */
+#endif
+
+ /*
+ * choose one of these MAILCMD commands. I use
+ * /bin/mail for speed; it makes biff bark but doesn't
+ * do aliasing. /usr/lib/sendmail does aliasing but is
+ * a hog for short messages. aliasing is not needed
+ * if you make use of the MAILTO= feature in crontabs.
+ * (hint: MAILTO= was added for this reason).
+ */
+
+#define MAILCMD _PATH_SENDMAIL /*-*/
+#define MAILARGS "%s -FCronDaemon -odi -oem -oi -t" /*-*/
+ /* -Fx = set full-name of sender
+ * -odi = Option Deliverymode Interactive
+ * -oem = Option Errors Mailedtosender
+ * -oi = Option dot message terminator
+ * -t = read recipients from header of message
+ */
+
+/* #define MAILCMD "/bin/mail" */ /*-*/
+/* #define MAILARGS "%s -d %s" */ /*-*/
+ /* -d = undocumented but common flag: deliver locally?
+ */
+
+/* #define MAILCMD "/usr/mmdf/bin/submit" */ /*-*/
+/* #define MAILARGS "%s -mlrxto %s" */ /*-*/
+
+/* #define MAIL_DATE */ /*-*/
+ /* should we include an ersatz Date: header in
+ * generated mail? if you are using sendmail
+ * for MAILCMD, it is better to let sendmail
+ * generate the Date: header.
+ */
+
+ /* if ALLOW_FILE and DENY_FILE are not defined or are
+ * defined but neither exists, should crontab(1) be
+ * usable only by root?
+ */
+/* #define ALLOW_ONLY_ROOT */ /*-*/
+
+ /* if you want to use syslog(3) instead of appending
+ * to CRONDIR/LOG_FILE (/var/cron/log, e.g.), define
+ * SYSLOG here. Note that quite a bit of logging
+ * info is written, and that you probably don't want
+ * to use this on 4.2bsd since everything goes in
+ * /usr/spool/mqueue/syslog. On 4.[34]bsd you can
+ * tell /etc/syslog.conf to send cron's logging to
+ * a separate file.
+ *
+ * Note that if this and LOG_FILE in "pathnames.h"
+ * are both defined, then logging will go to both
+ * places.
+ */
+#define SYSLOG /*-*/
diff --git a/usr.sbin/cron/cron/cron.8 b/usr.sbin/cron/cron/cron.8
new file mode 100644
index 0000000..d47735b
--- /dev/null
+++ b/usr.sbin/cron/cron/cron.8
@@ -0,0 +1,149 @@
+.\"/* Copyright 1988,1990,1993 by Paul Vixie
+.\" * All rights reserved
+.\" *
+.\" * Distribute freely, except: don't remove my name from the source or
+.\" * documentation (don't take credit for my work), mark your changes (don't
+.\" * get me blamed for your possible bugs), don't alter or remove this
+.\" * notice. May be sold if buildable source is provided to buyer. No
+.\" * warrantee of any kind, express or implied, is included with this
+.\" * software; use at your own risk, responsibility for damages (if any) to
+.\" * anyone resulting from the use of this software rests entirely with the
+.\" * user.
+.\" *
+.\" * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+.\" * I'll try to keep a version up to date. I can be reached as follows:
+.\" * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+.\" */
+.\"
+.\" $FreeBSD$
+.\"
+.Dd December 20, 1993
+.Dt CRON 8
+.Os
+.Sh NAME
+.Nm cron
+.Nd daemon to execute scheduled commands (Vixie Cron)
+.Sh SYNOPSIS
+.Nm
+.Op Fl s
+.Op Fl o
+.Op Fl x Ar debugflag Ns Op , Ns Ar ...
+.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 modification time (or
+the modification time on
+.Pa /etc/crontab )
+has changed, and if it has,
+.Nm
+will then examine the modification time on all crontabs and reload those
+which have changed. Thus
+.Nm
+need not be restarted whenever a crontab file is modified. Note that the
+.Xr crontab 1
+command updates the modification time of the spool directory whenever it
+changes a crontab.
+.Pp
+Available options:
+.Bl -tag -width indent
+.It Fl s
+Enable special handling of situations when the GMT offset of the local
+timezone changes, such as the switches between the standard time and
+daylight saving time.
+.Pp
+The jobs run during the GMT offset changes time as
+intuitively expected.
+If a job falls into a time interval that disappears
+(for example, during the switch from
+standard time) to daylight saving time or is
+duplicated (for example, during the reverse switch), then it's handled
+in one of two ways:
+.Pp
+The first case is for the jobs that run every at hour of a time interval
+overlapping with the disappearing or duplicated interval.
+In other words, if the job had run within one hour before the GMT offset change
+(and cron was not restarted nor the
+.Xr crontab 5
+changed after that)
+or would run after the change at the next hour.
+They work as always, skip the skipped time or run in the added
+time as usual.
+.Pp
+The second case is for the jobs that run less frequently.
+They are executed exactly once, they are not skipped nor
+executed twice (unless cron is restarted or the user's
+.Xr crontab 5
+is changed during such a time interval).
+If an interval disappears
+due to the GMT offset change, such jobs are
+executed at the same absolute point of time as they would be in the
+old time zone.
+For example, if exactly one hour disappears, this
+point would be during the next hour at the first minute that is
+specified for them in
+.Xr crontab 5 .
+.It Fl o
+Disable the special handling of situations when the GMT offset of the local
+timezone changes, to be compatible with the old (default) behavior.
+If both options
+.Fl o
+and
+.Fl s
+are specified, the option specified last wins.
+.It Fl x Ar debugflag Ns Op , Ns Ar ...
+Enable writing of debugging information to standard output.
+One or more of the following comma separated
+.Ar debugflag
+identifiers must be specified:
+.Pp
+.Bl -tag -width ".Cm proc" -compact
+.It Cm bit
+currently not used
+.It Cm ext
+make the other debug flags more verbose
+.It Cm load
+be verbose when loading crontab files
+.It Cm misc
+be verbose about miscellaneous one-off events
+.It Cm pars
+be verbose about parsing individual crontab lines
+.It Cm proc
+be verbose about the state of the process, including all of its offspring
+.It Cm sch
+be verbose when iterating through the scheduling algorithms
+.It Cm test
+trace through the execution, but do not perform any actions
+.El
+.El
+.Sh SEE ALSO
+.Xr crontab 1 ,
+.Xr crontab 5
+.Sh AUTHORS
+.An Paul Vixie Aq paul@vix.com
diff --git a/usr.sbin/cron/cron/cron.c b/usr.sbin/cron/cron/cron.c
new file mode 100644
index 0000000..5131645
--- /dev/null
+++ b/usr.sbin/cron/cron/cron.c
@@ -0,0 +1,435 @@
+/* Copyright 1988,1990,1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ */
+
+#if !defined(lint) && !defined(LINT)
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif
+
+#define MAIN_PROGRAM
+
+
+#include "cron.h"
+#include <sys/signal.h>
+#if SYS_TIME_H
+# include <sys/time.h>
+#else
+# include <time.h>
+#endif
+
+
+static void usage __P((void)),
+ run_reboot_jobs __P((cron_db *)),
+ cron_tick __P((cron_db *)),
+ cron_sync __P((void)),
+ cron_sleep __P((cron_db *)),
+ cron_clean __P((cron_db *)),
+#ifdef USE_SIGCHLD
+ sigchld_handler __P((int)),
+#endif
+ sighup_handler __P((int)),
+ parse_args __P((int c, char *v[]));
+
+static time_t last_time = 0;
+static int dst_enabled = 0;
+
+static void
+usage() {
+ char **dflags;
+
+ fprintf(stderr, "usage: cron [-s] [-o] [-x debugflag[,...]]\n");
+ fprintf(stderr, "\ndebugflags: ");
+
+ for(dflags = DebugFlagNames; *dflags; dflags++) {
+ fprintf(stderr, "%s ", *dflags);
+ }
+ fprintf(stderr, "\n");
+
+ exit(ERROR_EXIT);
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ cron_db database;
+
+ ProgramName = argv[0];
+
+#if defined(BSD)
+ setlinebuf(stdout);
+ setlinebuf(stderr);
+#endif
+
+ parse_args(argc, argv);
+
+#ifdef USE_SIGCHLD
+ (void) signal(SIGCHLD, sigchld_handler);
+#else
+ (void) signal(SIGCLD, SIG_IGN);
+#endif
+ (void) signal(SIGHUP, sighup_handler);
+
+ acquire_daemonlock(0);
+ set_cron_uid();
+ set_cron_cwd();
+
+#if defined(POSIX)
+ setenv("PATH", _PATH_DEFPATH, 1);
+#endif
+
+ /* if there are no debug flags turned on, fork as a daemon should.
+ */
+# if DEBUGGING
+ if (DebugFlags) {
+# else
+ if (0) {
+# endif
+ (void) fprintf(stderr, "[%d] cron started\n", getpid());
+ } else {
+ if (daemon(1, 0) == -1) {
+ log_it("CRON",getpid(),"DEATH","can't become daemon");
+ exit(0);
+ }
+ }
+
+ acquire_daemonlock(0);
+ database.head = NULL;
+ database.tail = NULL;
+ database.mtime = (time_t) 0;
+ load_database(&database);
+ run_reboot_jobs(&database);
+ cron_sync();
+ while (TRUE) {
+# if DEBUGGING
+ /* if (!(DebugFlags & DTEST)) */
+# endif /*DEBUGGING*/
+ cron_sleep(&database);
+
+ load_database(&database);
+
+ /* do this iteration
+ */
+ cron_tick(&database);
+
+ /* sleep 1 minute
+ */
+ TargetTime += 60;
+ }
+}
+
+
+static void
+run_reboot_jobs(db)
+ cron_db *db;
+{
+ register user *u;
+ register entry *e;
+
+ for (u = db->head; u != NULL; u = u->next) {
+ for (e = u->crontab; e != NULL; e = e->next) {
+ if (e->flags & WHEN_REBOOT) {
+ job_add(e, u);
+ }
+ }
+ }
+ (void) job_runqueue();
+}
+
+
+static void
+cron_tick(db)
+ cron_db *db;
+{
+ static struct tm lasttm;
+ static time_t diff = 0, /* time difference in seconds from the last offset change */
+ difflimit = 0; /* end point for the time zone correction */
+ struct tm otztm; /* time in the old time zone */
+ int otzminute, otzhour, otzdom, otzmonth, otzdow;
+ register struct tm *tm = localtime(&TargetTime);
+ register int minute, hour, dom, month, dow;
+ register user *u;
+ register entry *e;
+
+ /* make 0-based values out of these so we can use them as indicies
+ */
+ minute = tm->tm_min -FIRST_MINUTE;
+ hour = tm->tm_hour -FIRST_HOUR;
+ dom = tm->tm_mday -FIRST_DOM;
+ month = tm->tm_mon +1 /* 0..11 -> 1..12 */ -FIRST_MONTH;
+ dow = tm->tm_wday -FIRST_DOW;
+
+ Debug(DSCH, ("[%d] tick(%d,%d,%d,%d,%d)\n",
+ getpid(), minute, hour, dom, month, dow))
+
+ if (dst_enabled && last_time != 0
+ && TargetTime > last_time /* exclude stepping back */
+ && tm->tm_gmtoff != lasttm.tm_gmtoff ) {
+
+ diff = tm->tm_gmtoff - lasttm.tm_gmtoff;
+
+ if ( diff > 0 ) { /* ST->DST */
+ /* mark jobs for an earlier run */
+ difflimit = TargetTime + diff;
+ for (u = db->head; u != NULL; u = u->next) {
+ for (e = u->crontab; e != NULL; e = e->next) {
+ e->flags &= ~NOT_UNTIL;
+ if ( e->lastrun >= TargetTime )
+ e->lastrun = 0;
+ /* not include the ends of hourly ranges */
+ if ( e->lastrun < TargetTime - 3600 )
+ e->flags |= RUN_AT;
+ else
+ e->flags &= ~RUN_AT;
+ }
+ }
+ } else { /* diff < 0 : DST->ST */
+ /* mark jobs for skipping */
+ difflimit = TargetTime - diff;
+ for (u = db->head; u != NULL; u = u->next) {
+ for (e = u->crontab; e != NULL; e = e->next) {
+ e->flags |= NOT_UNTIL;
+ e->flags &= ~RUN_AT;
+ }
+ }
+ }
+ }
+
+ if (diff != 0) {
+ /* if the time was reset of the end of special zone is reached */
+ if (last_time == 0 || TargetTime >= difflimit) {
+ /* disable the TZ switch checks */
+ diff = 0;
+ difflimit = 0;
+ for (u = db->head; u != NULL; u = u->next) {
+ for (e = u->crontab; e != NULL; e = e->next) {
+ e->flags &= ~(RUN_AT|NOT_UNTIL);
+ }
+ }
+ } else {
+ /* get the time in the old time zone */
+ time_t difftime = TargetTime + tm->tm_gmtoff - diff;
+ gmtime_r(&difftime, &otztm);
+
+ /* make 0-based values out of these so we can use them as indicies
+ */
+ otzminute = otztm.tm_min -FIRST_MINUTE;
+ otzhour = otztm.tm_hour -FIRST_HOUR;
+ otzdom = otztm.tm_mday -FIRST_DOM;
+ otzmonth = otztm.tm_mon +1 /* 0..11 -> 1..12 */ -FIRST_MONTH;
+ otzdow = otztm.tm_wday -FIRST_DOW;
+ }
+ }
+
+ /* the dom/dow situation is odd. '* * 1,15 * Sun' will run on the
+ * first and fifteenth AND every Sunday; '* * * * Sun' will run *only*
+ * on Sundays; '* * 1,15 * *' will run *only* the 1st and 15th. this
+ * is why we keep 'e->dow_star' and 'e->dom_star'. yes, it's bizarre.
+ * like many bizarre things, it's the standard.
+ */
+ for (u = db->head; u != NULL; u = u->next) {
+ for (e = u->crontab; e != NULL; e = e->next) {
+ Debug(DSCH|DEXT, ("user [%s:%d:%d:...] cmd=\"%s\"\n",
+ env_get("LOGNAME", e->envp),
+ e->uid, e->gid, e->cmd))
+
+ if ( diff != 0 && (e->flags & (RUN_AT|NOT_UNTIL)) ) {
+ if (bit_test(e->minute, otzminute)
+ && bit_test(e->hour, otzhour)
+ && bit_test(e->month, otzmonth)
+ && ( ((e->flags & DOM_STAR) || (e->flags & DOW_STAR))
+ ? (bit_test(e->dow,otzdow) && bit_test(e->dom,otzdom))
+ : (bit_test(e->dow,otzdow) || bit_test(e->dom,otzdom))
+ )
+ ) {
+ if ( e->flags & RUN_AT ) {
+ e->flags &= ~RUN_AT;
+ e->lastrun = TargetTime;
+ job_add(e, u);
+ continue;
+ } else
+ e->flags &= ~NOT_UNTIL;
+ } else if ( e->flags & NOT_UNTIL )
+ continue;
+ }
+
+ if (bit_test(e->minute, minute)
+ && bit_test(e->hour, hour)
+ && bit_test(e->month, month)
+ && ( ((e->flags & DOM_STAR) || (e->flags & DOW_STAR))
+ ? (bit_test(e->dow,dow) && bit_test(e->dom,dom))
+ : (bit_test(e->dow,dow) || bit_test(e->dom,dom))
+ )
+ ) {
+ e->flags &= ~RUN_AT;
+ e->lastrun = TargetTime;
+ job_add(e, u);
+ }
+ }
+ }
+
+ last_time = TargetTime;
+ lasttm = *tm;
+}
+
+
+/* the task here is to figure out how long it's going to be until :00 of the
+ * following minute and initialize TargetTime to this value. TargetTime
+ * will subsequently slide 60 seconds at a time, with correction applied
+ * implicitly in cron_sleep(). it would be nice to let cron execute in
+ * the "current minute" before going to sleep, but by restarting cron you
+ * could then get it to execute a given minute's jobs more than once.
+ * instead we have the chance of missing a minute's jobs completely, but
+ * that's something sysadmin's know to expect what with crashing computers..
+ */
+static void
+cron_sync() {
+ register struct tm *tm;
+
+ TargetTime = time((time_t*)0);
+ tm = localtime(&TargetTime);
+ TargetTime += (60 - tm->tm_sec);
+}
+
+
+static void
+cron_sleep(db)
+ cron_db *db;
+{
+ int seconds_to_wait = 0;
+
+ /*
+ * Loop until we reach the top of the next minute, sleep when possible.
+ */
+
+ for (;;) {
+ seconds_to_wait = (int) (TargetTime - time((time_t*)0));
+
+ /*
+ * If the seconds_to_wait value is insane, jump the cron
+ */
+
+ if (seconds_to_wait < -600 || seconds_to_wait > 600) {
+ cron_clean(db);
+ cron_sync();
+ continue;
+ }
+
+ Debug(DSCH, ("[%d] TargetTime=%ld, sec-to-wait=%d\n",
+ getpid(), (long)TargetTime, seconds_to_wait))
+
+ /*
+ * If we've run out of wait time or there are no jobs left
+ * to run, break
+ */
+
+ if (seconds_to_wait <= 0)
+ break;
+ if (job_runqueue() == 0) {
+ Debug(DSCH, ("[%d] sleeping for %d seconds\n",
+ getpid(), seconds_to_wait))
+
+ sleep(seconds_to_wait);
+ }
+ }
+}
+
+
+/* if the time was changed abruptly, clear the flags related
+ * to the daylight time switch handling to avoid strange effects
+ */
+
+static void
+cron_clean(db)
+ cron_db *db;
+{
+ user *u;
+ entry *e;
+
+ last_time = 0;
+
+ for (u = db->head; u != NULL; u = u->next) {
+ for (e = u->crontab; e != NULL; e = e->next) {
+ e->flags &= ~(RUN_AT|NOT_UNTIL);
+ }
+ }
+}
+
+#ifdef USE_SIGCHLD
+static void
+sigchld_handler(x) {
+ WAIT_T waiter;
+ PID_T pid;
+
+ for (;;) {
+#ifdef POSIX
+ pid = waitpid(-1, &waiter, WNOHANG);
+#else
+ pid = wait3(&waiter, WNOHANG, (struct rusage *)0);
+#endif
+ switch (pid) {
+ case -1:
+ Debug(DPROC,
+ ("[%d] sigchld...no children\n", getpid()))
+ return;
+ case 0:
+ Debug(DPROC,
+ ("[%d] sigchld...no dead kids\n", getpid()))
+ return;
+ default:
+ Debug(DPROC,
+ ("[%d] sigchld...pid #%d died, stat=%d\n",
+ getpid(), pid, WEXITSTATUS(waiter)))
+ }
+ }
+}
+#endif /*USE_SIGCHLD*/
+
+
+static void
+sighup_handler(x) {
+ log_close();
+}
+
+
+static void
+parse_args(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int argch;
+
+ while ((argch = getopt(argc, argv, "osx:")) != -1) {
+ switch (argch) {
+ case 'o':
+ dst_enabled = 0;
+ break;
+ case 's':
+ dst_enabled = 1;
+ break;
+ case 'x':
+ if (!set_debug_flags(optarg))
+ usage();
+ break;
+ default:
+ usage();
+ }
+ }
+}
+
diff --git a/usr.sbin/cron/cron/cron.h b/usr.sbin/cron/cron/cron.h
new file mode 100644
index 0000000..33bdf92
--- /dev/null
+++ b/usr.sbin/cron/cron/cron.h
@@ -0,0 +1,292 @@
+/* Copyright 1988,1990,1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ */
+
+/* cron.h - header for vixie's cron
+ *
+ * $FreeBSD$
+ *
+ * vix 14nov88 [rest of log is in RCS]
+ * vix 14jan87 [0 or 7 can be sunday; thanks, mwm@berkeley]
+ * vix 30dec86 [written]
+ */
+
+/* reorder these #include's at your peril */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include "compat.h"
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <time.h>
+#include <sys/wait.h>
+
+#include "pathnames.h"
+#include "config.h"
+#include "externs.h"
+
+ /* these are really immutable, and are
+ * defined for symbolic convenience only
+ * TRUE, FALSE, and ERR must be distinct
+ * ERR must be < OK.
+ */
+#define TRUE 1
+#define FALSE 0
+ /* system calls return this on success */
+#define OK 0
+ /* or this on error */
+#define ERR (-1)
+
+ /* turn this on to get '-x' code */
+#ifndef DEBUGGING
+#define DEBUGGING FALSE
+#endif
+
+#define READ_PIPE 0 /* which end of a pipe pair do you read? */
+#define WRITE_PIPE 1 /* or write to? */
+#define STDIN 0 /* what is stdin's file descriptor? */
+#define STDOUT 1 /* stdout's? */
+#define STDERR 2 /* stderr's? */
+#define ERROR_EXIT 1 /* exit() with this will scare the shell */
+#define OK_EXIT 0 /* exit() with this is considered 'normal' */
+#define MAX_FNAME 100 /* max length of internally generated fn */
+#define MAX_COMMAND 1000 /* max length of internally generated cmd */
+#define MAX_ENVSTR 1000 /* max length of envvar=value\0 strings */
+#define MAX_TEMPSTR 100 /* obvious */
+#define MAX_UNAME 20 /* max length of username, should be overkill */
+#define ROOT_UID 0 /* don't change this, it really must be root */
+#define ROOT_USER "root" /* ditto */
+
+ /* NOTE: these correspond to DebugFlagNames,
+ * defined below.
+ */
+#define DEXT 0x0001 /* extend flag for other debug masks */
+#define DSCH 0x0002 /* scheduling debug mask */
+#define DPROC 0x0004 /* process control debug mask */
+#define DPARS 0x0008 /* parsing debug mask */
+#define DLOAD 0x0010 /* database loading debug mask */
+#define DMISC 0x0020 /* misc debug mask */
+#define DTEST 0x0040 /* test mode: don't execute any commands */
+#define DBIT 0x0080 /* bit twiddling shown (long) */
+
+#define CRON_TAB(u) "%s/%s", SPOOL_DIR, u
+#define REG register
+#define PPC_NULL ((char **)NULL)
+
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 256
+#endif
+
+#define Skip_Blanks(c, f) \
+ while (c == '\t' || c == ' ') \
+ c = get_char(f);
+
+#define Skip_Nonblanks(c, f) \
+ while (c!='\t' && c!=' ' && c!='\n' && c != EOF) \
+ c = get_char(f);
+
+#define Skip_Line(c, f) \
+ do {c = get_char(f);} while (c != '\n' && c != EOF);
+
+#if DEBUGGING
+# define Debug(mask, message) \
+ if ( (DebugFlags & (mask) ) == (mask) ) \
+ printf message;
+#else /* !DEBUGGING */
+# define Debug(mask, message) \
+ ;
+#endif /* DEBUGGING */
+
+#define MkLower(ch) (isupper(ch) ? tolower(ch) : ch)
+#define MkUpper(ch) (islower(ch) ? toupper(ch) : ch)
+#define Set_LineNum(ln) {Debug(DPARS|DEXT,("linenum=%d\n",ln)); \
+ LineNumber = ln; \
+ }
+
+#define FIRST_MINUTE 0
+#define LAST_MINUTE 59
+#define MINUTE_COUNT (LAST_MINUTE - FIRST_MINUTE + 1)
+
+#define FIRST_HOUR 0
+#define LAST_HOUR 23
+#define HOUR_COUNT (LAST_HOUR - FIRST_HOUR + 1)
+
+#define FIRST_DOM 1
+#define LAST_DOM 31
+#define DOM_COUNT (LAST_DOM - FIRST_DOM + 1)
+
+#define FIRST_MONTH 1
+#define LAST_MONTH 12
+#define MONTH_COUNT (LAST_MONTH - FIRST_MONTH + 1)
+
+/* note on DOW: 0 and 7 are both Sunday, for compatibility reasons. */
+#define FIRST_DOW 0
+#define LAST_DOW 7
+#define DOW_COUNT (LAST_DOW - FIRST_DOW + 1)
+
+#ifdef LOGIN_CAP
+/* see init.c */
+#define RESOURCE_RC "daemon"
+#endif
+
+ /* each user's crontab will be held as a list of
+ * the following structure.
+ *
+ * These are the cron commands.
+ */
+
+typedef struct _entry {
+ struct _entry *next;
+ uid_t uid;
+ gid_t gid;
+#ifdef LOGIN_CAP
+ char *class;
+#endif
+ char **envp;
+ char *cmd;
+ bitstr_t bit_decl(minute, MINUTE_COUNT);
+ bitstr_t bit_decl(hour, HOUR_COUNT);
+ bitstr_t bit_decl(dom, DOM_COUNT);
+ bitstr_t bit_decl(month, MONTH_COUNT);
+ bitstr_t bit_decl(dow, DOW_COUNT);
+ int flags;
+#define DOM_STAR 0x01
+#define DOW_STAR 0x02
+#define WHEN_REBOOT 0x04
+#define RUN_AT 0x08
+#define NOT_UNTIL 0x10
+ time_t lastrun;
+} entry;
+
+ /* the crontab database will be a list of the
+ * following structure, one element per user
+ * plus one for the system.
+ *
+ * These are the crontabs.
+ */
+
+typedef struct _user {
+ struct _user *next, *prev; /* links */
+ char *name;
+ time_t mtime; /* last modtime of crontab */
+ entry *crontab; /* this person's crontab */
+} user;
+
+typedef struct _cron_db {
+ user *head, *tail; /* links */
+ time_t mtime; /* last modtime on spooldir */
+} cron_db;
+
+
+void set_cron_uid __P((void)),
+ set_cron_cwd __P((void)),
+ load_database __P((cron_db *)),
+ open_logfile __P((void)),
+ sigpipe_func __P((void)),
+ job_add __P((entry *, user *)),
+ do_command __P((entry *, user *)),
+ link_user __P((cron_db *, user *)),
+ unlink_user __P((cron_db *, user *)),
+ free_user __P((user *)),
+ env_free __P((char **)),
+ unget_char __P((int, FILE *)),
+ free_entry __P((entry *)),
+ acquire_daemonlock __P((int)),
+ skip_comments __P((FILE *)),
+ log_it __P((char *, int, char *, char *)),
+ log_close __P((void));
+
+int job_runqueue __P((void)),
+ set_debug_flags __P((char *)),
+ get_char __P((FILE *)),
+ get_string __P((char *, int, FILE *, char *)),
+ swap_uids __P((void)),
+ load_env __P((char *, FILE *)),
+ cron_pclose __P((FILE *)),
+ strcmp_until __P((char *, char *, int)),
+ allowed __P((char *)),
+ strdtb __P((char *));
+
+char *env_get __P((char *, char **)),
+ *arpadate __P((time_t *)),
+ *mkprints __P((unsigned char *, unsigned int)),
+ *first_word __P((char *, char *)),
+ **env_init __P((void)),
+ **env_copy __P((char **)),
+ **env_set __P((char **, char *));
+
+user *load_user __P((int, struct passwd *, char *)),
+ *find_user __P((cron_db *, char *));
+
+entry *load_entry __P((FILE *, void (*)(),
+ struct passwd *, char **));
+
+FILE *cron_popen __P((char *, char *, entry *));
+
+
+ /* in the C tradition, we only create
+ * variables for the main program, just
+ * extern them elsewhere.
+ */
+
+#ifdef MAIN_PROGRAM
+# if !defined(LINT) && !defined(lint)
+char *copyright[] = {
+ "@(#) Copyright 1988,1989,1990,1993,1994 by Paul Vixie",
+ "@(#) All rights reserved"
+ };
+# endif
+
+char *MonthNames[] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
+ NULL
+ };
+
+char *DowNames[] = {
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun",
+ NULL
+ };
+
+char *ProgramName;
+int LineNumber;
+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..9ebf75d
--- /dev/null
+++ b/usr.sbin/cron/cron/database.c
@@ -0,0 +1,263 @@
+/* Copyright 1988,1990,1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ */
+
+#if !defined(lint) && !defined(LINT)
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif
+
+/* vix 26jan87 [RCS has the log]
+ */
+
+
+#include "cron.h"
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+
+
+#define TMAX(a,b) ((a)>(b)?(a):(b))
+
+
+static void process_crontab __P((char *, char *, char *,
+ struct stat *,
+ cron_db *, cron_db *));
+
+
+void
+load_database(old_db)
+ cron_db *old_db;
+{
+ DIR *dir;
+ struct stat statbuf;
+ struct stat syscron_stat;
+ DIR_T *dp;
+ cron_db new_db;
+ user *u, *nu;
+
+ Debug(DLOAD, ("[%d] load_database()\n", getpid()))
+
+ /* before we start loading any data, do a stat on SPOOL_DIR
+ * so that if anything changes as of this moment (i.e., before we've
+ * cached any of the database), we'll see the changes next time.
+ */
+ if (stat(SPOOL_DIR, &statbuf) < OK) {
+ log_it("CRON", getpid(), "STAT FAILED", SPOOL_DIR);
+ (void) exit(ERROR_EXIT);
+ }
+
+ /* track system crontab file
+ */
+ if (stat(SYSCRONTAB, &syscron_stat) < OK)
+ syscron_stat.st_mtime = 0;
+
+ /* if spooldir's mtime has not changed, we don't need to fiddle with
+ * the database.
+ *
+ * Note that old_db->mtime is initialized to 0 in main(), and
+ * so is guaranteed to be different than the stat() mtime the first
+ * time this function is called.
+ */
+ if (old_db->mtime == TMAX(statbuf.st_mtime, syscron_stat.st_mtime)) {
+ Debug(DLOAD, ("[%d] spool dir mtime unch, no load needed.\n",
+ getpid()))
+ return;
+ }
+
+ /* something's different. make a new database, moving unchanged
+ * elements from the old database, reloading elements that have
+ * actually changed. Whatever is left in the old database when
+ * we're done is chaff -- crontabs that disappeared.
+ */
+ new_db.mtime = TMAX(statbuf.st_mtime, syscron_stat.st_mtime);
+ new_db.head = new_db.tail = NULL;
+
+ if (syscron_stat.st_mtime) {
+ process_crontab("root", "*system*",
+ SYSCRONTAB, &syscron_stat,
+ &new_db, old_db);
+ }
+
+ /* we used to keep this dir open all the time, for the sake of
+ * efficiency. however, we need to close it in every fork, and
+ * we fork a lot more often than the mtime of the dir changes.
+ */
+ if (!(dir = opendir(SPOOL_DIR))) {
+ log_it("CRON", getpid(), "OPENDIR FAILED", SPOOL_DIR);
+ (void) exit(ERROR_EXIT);
+ }
+
+ while (NULL != (dp = readdir(dir))) {
+ char fname[MAXNAMLEN+1],
+ tabname[MAXNAMLEN+1];
+
+ /* avoid file names beginning with ".". this is good
+ * because we would otherwise waste two guaranteed calls
+ * to getpwnam() for . and .., and also because user names
+ * starting with a period are just too nasty to consider.
+ */
+ if (dp->d_name[0] == '.')
+ continue;
+
+ (void) strncpy(fname, dp->d_name, sizeof(fname));
+ fname[sizeof(fname)-1] = '\0';
+ (void) snprintf(tabname, sizeof tabname, CRON_TAB(fname));
+
+ process_crontab(fname, fname, tabname,
+ &statbuf, &new_db, old_db);
+ }
+ closedir(dir);
+
+ /* if we don't do this, then when our children eventually call
+ * getpwnam() in do_command.c's child_process to verify MAILTO=,
+ * they will screw us up (and v-v).
+ */
+ endpwent();
+
+ /* whatever's left in the old database is now junk.
+ */
+ Debug(DLOAD, ("unlinking old database:\n"))
+ for (u = old_db->head; u != NULL; u = nu) {
+ Debug(DLOAD, ("\t%s\n", u->name))
+ nu = u->next;
+ unlink_user(old_db, u);
+ free_user(u);
+ }
+
+ /* overwrite the database control block with the new one.
+ */
+ *old_db = new_db;
+ Debug(DLOAD, ("load_database is done\n"))
+}
+
+
+void
+link_user(db, u)
+ cron_db *db;
+ user *u;
+{
+ if (db->head == NULL)
+ db->head = u;
+ if (db->tail)
+ db->tail->next = u;
+ u->prev = db->tail;
+ u->next = NULL;
+ db->tail = u;
+}
+
+
+void
+unlink_user(db, u)
+ cron_db *db;
+ user *u;
+{
+ if (u->prev == NULL)
+ db->head = u->next;
+ else
+ u->prev->next = u->next;
+
+ if (u->next == NULL)
+ db->tail = u->prev;
+ else
+ u->next->prev = u->prev;
+}
+
+
+user *
+find_user(db, name)
+ cron_db *db;
+ char *name;
+{
+ char *env_get();
+ user *u;
+
+ for (u = db->head; u != NULL; u = u->next)
+ if (!strcmp(u->name, name))
+ break;
+ return u;
+}
+
+
+static void
+process_crontab(uname, fname, tabname, statbuf, new_db, old_db)
+ char *uname;
+ char *fname;
+ char *tabname;
+ struct stat *statbuf;
+ cron_db *new_db;
+ cron_db *old_db;
+{
+ struct passwd *pw = NULL;
+ int crontab_fd = OK - 1;
+ user *u;
+
+ if (strcmp(fname, "*system*") && !(pw = getpwnam(uname))) {
+ /* file doesn't have a user in passwd file.
+ */
+ log_it(fname, getpid(), "ORPHAN", "no passwd entry");
+ goto next_crontab;
+ }
+
+ if ((crontab_fd = open(tabname, O_RDONLY, 0)) < OK) {
+ /* crontab not accessible?
+ */
+ log_it(fname, getpid(), "CAN'T OPEN", tabname);
+ goto next_crontab;
+ }
+
+ if (fstat(crontab_fd, statbuf) < OK) {
+ log_it(fname, getpid(), "FSTAT FAILED", tabname);
+ goto next_crontab;
+ }
+
+ Debug(DLOAD, ("\t%s:", fname))
+ u = find_user(old_db, fname);
+ if (u != NULL) {
+ /* if crontab has not changed since we last read it
+ * in, then we can just use our existing entry.
+ */
+ if (u->mtime == statbuf->st_mtime) {
+ Debug(DLOAD, (" [no change, using old data]"))
+ unlink_user(old_db, u);
+ link_user(new_db, u);
+ goto next_crontab;
+ }
+
+ /* before we fall through to the code that will reload
+ * the user, let's deallocate and unlink the user in
+ * the old database. This is more a point of memory
+ * efficiency than anything else, since all leftover
+ * users will be deleted from the old database when
+ * we finish with the crontab...
+ */
+ Debug(DLOAD, (" [delete old data]"))
+ unlink_user(old_db, u);
+ free_user(u);
+ log_it(fname, getpid(), "RELOAD", tabname);
+ }
+ u = load_user(crontab_fd, pw, fname);
+ if (u != NULL) {
+ u->mtime = statbuf->st_mtime;
+ link_user(new_db, u);
+ }
+
+next_crontab:
+ if (crontab_fd >= OK) {
+ Debug(DLOAD, (" [done]\n"))
+ close(crontab_fd);
+ }
+}
diff --git a/usr.sbin/cron/cron/do_command.c b/usr.sbin/cron/cron/do_command.c
new file mode 100644
index 0000000..4b6fe33
--- /dev/null
+++ b/usr.sbin/cron/cron/do_command.c
@@ -0,0 +1,548 @@
+/* Copyright 1988,1990,1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ */
+
+#if !defined(lint) && !defined(LINT)
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif
+
+
+#include "cron.h"
+#include <sys/signal.h>
+#if defined(sequent)
+# include <sys/universe.h>
+#endif
+#if defined(SYSLOG)
+# include <syslog.h>
+#endif
+#if defined(LOGIN_CAP)
+# include <login_cap.h>
+#endif
+
+
+static void child_process __P((entry *, user *)),
+ do_univ __P((user *));
+
+
+void
+do_command(e, u)
+ entry *e;
+ user *u;
+{
+ Debug(DPROC, ("[%d] do_command(%s, (%s,%d,%d))\n",
+ getpid(), e->cmd, u->name, e->uid, e->gid))
+
+ /* fork to become asynchronous -- parent process is done immediately,
+ * and continues to run the normal cron code, which means return to
+ * tick(). the child and grandchild don't leave this function, alive.
+ *
+ * vfork() is unsuitable, since we have much to do, and the parent
+ * needs to be able to run off and fork other processes.
+ */
+ switch (fork()) {
+ case -1:
+ log_it("CRON",getpid(),"error","can't fork");
+ break;
+ case 0:
+ /* child process */
+ acquire_daemonlock(1);
+ child_process(e, u);
+ Debug(DPROC, ("[%d] child process done, exiting\n", getpid()))
+ _exit(OK_EXIT);
+ break;
+ default:
+ /* parent process */
+ break;
+ }
+ Debug(DPROC, ("[%d] main process returning to work\n", getpid()))
+}
+
+
+static void
+child_process(e, u)
+ entry *e;
+ user *u;
+{
+ int stdin_pipe[2], stdout_pipe[2];
+ register char *input_data;
+ char *usernm, *mailto;
+ int children = 0;
+# if defined(LOGIN_CAP)
+ struct passwd *pwd;
+ login_cap_t *lc;
+# endif
+
+ Debug(DPROC, ("[%d] child_process('%s')\n", getpid(), e->cmd))
+
+ /* mark ourselves as different to PS command watchers by upshifting
+ * our program name. This has no effect on some kernels.
+ */
+ setproctitle("running job");
+
+ /* discover some useful and important environment settings
+ */
+ usernm = env_get("LOGNAME", e->envp);
+ mailto = env_get("MAILTO", e->envp);
+
+#ifdef USE_SIGCHLD
+ /* our parent is watching for our death by catching SIGCHLD. we
+ * do not care to watch for our children's deaths this way -- we
+ * use wait() explictly. so we have to disable the signal (which
+ * was inherited from the parent).
+ */
+ (void) signal(SIGCHLD, SIG_DFL);
+#else
+ /* on system-V systems, we are ignoring SIGCLD. we have to stop
+ * ignoring it now or the wait() in cron_pclose() won't work.
+ * because of this, we have to wait() for our children here, as well.
+ */
+ (void) signal(SIGCLD, SIG_DFL);
+#endif /*BSD*/
+
+ /* create some pipes to talk to our future child
+ */
+ pipe(stdin_pipe); /* child's stdin */
+ pipe(stdout_pipe); /* child's stdout */
+
+ /* since we are a forked process, we can diddle the command string
+ * we were passed -- nobody else is going to use it again, right?
+ *
+ * if a % is present in the command, previous characters are the
+ * command, and subsequent characters are the additional input to
+ * the command. Subsequent %'s will be transformed into newlines,
+ * but that happens later.
+ *
+ * If there are escaped %'s, remove the escape character.
+ */
+ /*local*/{
+ register int escaped = FALSE;
+ register int ch;
+ register char *p;
+
+ for (input_data = p = e->cmd; (ch = *input_data);
+ input_data++, p++) {
+ if (p != input_data)
+ *p = ch;
+ if (escaped) {
+ if (ch == '%' || ch == '\\')
+ *--p = ch;
+ escaped = FALSE;
+ continue;
+ }
+ if (ch == '\\') {
+ escaped = TRUE;
+ continue;
+ }
+ if (ch == '%') {
+ *input_data++ = '\0';
+ break;
+ }
+ }
+ *p = '\0';
+ }
+
+ /* fork again, this time so we can exec the user's command.
+ */
+ switch (vfork()) {
+ case -1:
+ log_it("CRON",getpid(),"error","can't vfork");
+ exit(ERROR_EXIT);
+ /*NOTREACHED*/
+ case 0:
+ Debug(DPROC, ("[%d] grandchild process Vfork()'ed\n",
+ getpid()))
+
+ /* write a log message. we've waited this long to do it
+ * because it was not until now that we knew the PID that
+ * the actual user command shell was going to get and the
+ * PID is part of the log message.
+ */
+ /*local*/{
+ char *x = mkprints((u_char *)e->cmd, strlen(e->cmd));
+
+ log_it(usernm, getpid(), "CMD", x);
+ free(x);
+ }
+
+ /* that's the last thing we'll log. close the log files.
+ */
+#ifdef SYSLOG
+ closelog();
+#endif
+
+ /* get new pgrp, void tty, etc.
+ */
+ (void) setsid();
+
+ /* close the pipe ends that we won't use. this doesn't affect
+ * the parent, who has to read and write them; it keeps the
+ * kernel from recording us as a potential client TWICE --
+ * which would keep it from sending SIGPIPE in otherwise
+ * appropriate circumstances.
+ */
+ close(stdin_pipe[WRITE_PIPE]);
+ close(stdout_pipe[READ_PIPE]);
+
+ /* grandchild process. make std{in,out} be the ends of
+ * pipes opened by our daddy; make stderr go to stdout.
+ */
+ close(STDIN); dup2(stdin_pipe[READ_PIPE], STDIN);
+ close(STDOUT); dup2(stdout_pipe[WRITE_PIPE], STDOUT);
+ close(STDERR); dup2(STDOUT, STDERR);
+
+ /* close the pipes we just dup'ed. The resources will remain.
+ */
+ close(stdin_pipe[READ_PIPE]);
+ close(stdout_pipe[WRITE_PIPE]);
+
+ /* set our login universe. Do this in the grandchild
+ * so that the child can invoke /usr/lib/sendmail
+ * without surprises.
+ */
+ do_univ(u);
+
+# if defined(LOGIN_CAP)
+ /* Set user's entire context, but skip the environment
+ * as cron provides a separate interface for this
+ */
+ if ((pwd = getpwnam(usernm)) == NULL)
+ pwd = getpwuid(e->uid);
+ lc = NULL;
+ if (pwd != NULL) {
+ pwd->pw_gid = e->gid;
+ if (e->class != NULL)
+ lc = login_getclass(e->class);
+ }
+ if (pwd &&
+ setusercontext(lc, pwd, e->uid,
+ LOGIN_SETALL & ~(LOGIN_SETPATH|LOGIN_SETENV)) == 0)
+ (void) endpwent();
+ else {
+ /* fall back to the old method */
+ (void) endpwent();
+# endif
+ /* set our directory, uid and gid. Set gid first,
+ * since once we set uid, we've lost root privledges.
+ */
+ setgid(e->gid);
+# if defined(BSD)
+ initgroups(usernm, e->gid);
+# endif
+ setlogin(usernm);
+ setuid(e->uid); /* we aren't root after this..*/
+#if defined(LOGIN_CAP)
+ }
+ if (lc != NULL)
+ login_close(lc);
+#endif
+ chdir(env_get("HOME", e->envp));
+
+ /* exec the command.
+ */
+ {
+ char *shell = env_get("SHELL", e->envp);
+
+# if DEBUGGING
+ if (DebugFlags & DTEST) {
+ fprintf(stderr,
+ "debug DTEST is on, not exec'ing command.\n");
+ fprintf(stderr,
+ "\tcmd='%s' shell='%s'\n", e->cmd, shell);
+ _exit(OK_EXIT);
+ }
+# endif /*DEBUGGING*/
+ execle(shell, shell, "-c", e->cmd, (char *)0, e->envp);
+ warn("execl: couldn't exec `%s'", shell);
+ _exit(ERROR_EXIT);
+ }
+ break;
+ default:
+ /* parent process */
+ break;
+ }
+
+ children++;
+
+ /* middle process, child of original cron, parent of process running
+ * the user's command.
+ */
+
+ Debug(DPROC, ("[%d] child continues, closing pipes\n", getpid()))
+
+ /* close the ends of the pipe that will only be referenced in the
+ * grandchild process...
+ */
+ close(stdin_pipe[READ_PIPE]);
+ close(stdout_pipe[WRITE_PIPE]);
+
+ /*
+ * write, to the pipe connected to child's stdin, any input specified
+ * after a % in the crontab entry. while we copy, convert any
+ * additional %'s to newlines. when done, if some characters were
+ * written and the last one wasn't a newline, write a newline.
+ *
+ * Note that if the input data won't fit into one pipe buffer (2K
+ * or 4K on most BSD systems), and the child doesn't read its stdin,
+ * we would block here. thus we must fork again.
+ */
+
+ if (*input_data && fork() == 0) {
+ register FILE *out = fdopen(stdin_pipe[WRITE_PIPE], "w");
+ register int need_newline = FALSE;
+ register int escaped = FALSE;
+ register int ch;
+
+ if (out == NULL) {
+ warn("fdopen failed in child2");
+ _exit(ERROR_EXIT);
+ }
+
+ Debug(DPROC, ("[%d] child2 sending data to grandchild\n", getpid()))
+
+ /* close the pipe we don't use, since we inherited it and
+ * are part of its reference count now.
+ */
+ close(stdout_pipe[READ_PIPE]);
+
+ /* translation:
+ * \% -> %
+ * % -> \n
+ * \x -> \x for all x != %
+ */
+ while ((ch = *input_data++)) {
+ if (escaped) {
+ if (ch != '%')
+ putc('\\', out);
+ } else {
+ if (ch == '%')
+ ch = '\n';
+ }
+
+ if (!(escaped = (ch == '\\'))) {
+ putc(ch, out);
+ need_newline = (ch != '\n');
+ }
+ }
+ if (escaped)
+ putc('\\', out);
+ if (need_newline)
+ putc('\n', out);
+
+ /* close the pipe, causing an EOF condition. fclose causes
+ * stdin_pipe[WRITE_PIPE] to be closed, too.
+ */
+ fclose(out);
+
+ Debug(DPROC, ("[%d] child2 done sending to grandchild\n", getpid()))
+ exit(0);
+ }
+
+ /* close the pipe to the grandkiddie's stdin, since its wicked uncle
+ * ernie back there has it open and will close it when he's done.
+ */
+ close(stdin_pipe[WRITE_PIPE]);
+
+ children++;
+
+ /*
+ * read output from the grandchild. it's stderr has been redirected to
+ * it's stdout, which has been redirected to our pipe. if there is any
+ * output, we'll be mailing it to the user whose crontab this is...
+ * when the grandchild exits, we'll get EOF.
+ */
+
+ Debug(DPROC, ("[%d] child reading output from grandchild\n", getpid()))
+
+ /*local*/{
+ register FILE *in = fdopen(stdout_pipe[READ_PIPE], "r");
+ register int ch = getc(in);
+
+ if (in == NULL) {
+ warn("fdopen failed in child");
+ _exit(ERROR_EXIT);
+ }
+
+ if (ch != EOF) {
+ register FILE *mail;
+ register int bytes = 1;
+ int status = 0;
+
+ Debug(DPROC|DEXT,
+ ("[%d] got data (%x:%c) from grandchild\n",
+ getpid(), ch, ch))
+
+ /* get name of recipient. this is MAILTO if set to a
+ * valid local username; USER otherwise.
+ */
+ if (mailto) {
+ /* MAILTO was present in the environment
+ */
+ if (!*mailto) {
+ /* ... but it's empty. set to NULL
+ */
+ mailto = NULL;
+ }
+ } else {
+ /* MAILTO not present, set to USER.
+ */
+ mailto = usernm;
+ }
+
+ /* if we are supposed to be mailing, MAILTO will
+ * be non-NULL. only in this case should we set
+ * up the mail command and subjects and stuff...
+ */
+
+ if (mailto) {
+ register char **env;
+ auto char mailcmd[MAX_COMMAND];
+ auto char hostname[MAXHOSTNAMELEN];
+
+ (void) gethostname(hostname, MAXHOSTNAMELEN);
+ (void) snprintf(mailcmd, sizeof(mailcmd),
+ MAILARGS, MAILCMD);
+ if (!(mail = cron_popen(mailcmd, "w", e))) {
+ warn("%s", MAILCMD);
+ (void) _exit(ERROR_EXIT);
+ }
+ fprintf(mail, "From: %s (Cron Daemon)\n", usernm);
+ fprintf(mail, "To: %s\n", mailto);
+ fprintf(mail, "Subject: Cron <%s@%s> %s\n",
+ usernm, first_word(hostname, "."),
+ e->cmd);
+# if defined(MAIL_DATE)
+ fprintf(mail, "Date: %s\n",
+ arpadate(&TargetTime));
+# endif /* MAIL_DATE */
+ for (env = e->envp; *env; env++)
+ fprintf(mail, "X-Cron-Env: <%s>\n",
+ *env);
+ fprintf(mail, "\n");
+
+ /* this was the first char from the pipe
+ */
+ putc(ch, mail);
+ }
+
+ /* we have to read the input pipe no matter whether
+ * we mail or not, but obviously we only write to
+ * mail pipe if we ARE mailing.
+ */
+
+ while (EOF != (ch = getc(in))) {
+ bytes++;
+ if (mailto)
+ putc(ch, mail);
+ }
+
+ /* only close pipe if we opened it -- i.e., we're
+ * mailing...
+ */
+
+ if (mailto) {
+ Debug(DPROC, ("[%d] closing pipe to mail\n",
+ getpid()))
+ /* Note: the pclose will probably see
+ * the termination of the grandchild
+ * in addition to the mail process, since
+ * it (the grandchild) is likely to exit
+ * after closing its stdout.
+ */
+ status = cron_pclose(mail);
+ }
+
+ /* if there was output and we could not mail it,
+ * log the facts so the poor user can figure out
+ * what's going on.
+ */
+ if (mailto && status) {
+ char buf[MAX_TEMPSTR];
+
+ snprintf(buf, sizeof(buf),
+ "mailed %d byte%s of output but got status 0x%04x\n",
+ bytes, (bytes==1)?"":"s",
+ status);
+ log_it(usernm, getpid(), "MAIL", buf);
+ }
+
+ } /*if data from grandchild*/
+
+ Debug(DPROC, ("[%d] got EOF from grandchild\n", getpid()))
+
+ fclose(in); /* also closes stdout_pipe[READ_PIPE] */
+ }
+
+ /* wait for children to die.
+ */
+ for (; children > 0; children--)
+ {
+ WAIT_T waiter;
+ PID_T pid;
+
+ Debug(DPROC, ("[%d] waiting for grandchild #%d to finish\n",
+ getpid(), children))
+ pid = wait(&waiter);
+ if (pid < OK) {
+ Debug(DPROC, ("[%d] no more grandchildren--mail written?\n",
+ getpid()))
+ break;
+ }
+ Debug(DPROC, ("[%d] grandchild #%d finished, status=%04x",
+ getpid(), pid, WEXITSTATUS(waiter)))
+ if (WIFSIGNALED(waiter) && WCOREDUMP(waiter))
+ Debug(DPROC, (", dumped core"))
+ Debug(DPROC, ("\n"))
+ }
+}
+
+
+static void
+do_univ(u)
+ user *u;
+{
+#if defined(sequent)
+/* Dynix (Sequent) hack to put the user associated with
+ * the passed user structure into the ATT universe if
+ * necessary. We have to dig the gecos info out of
+ * the user's password entry to see if the magic
+ * "universe(att)" string is present.
+ */
+
+ struct passwd *p;
+ char *s;
+ int i;
+
+ p = getpwuid(u->uid);
+ (void) endpwent();
+
+ if (p == NULL)
+ return;
+
+ s = p->pw_gecos;
+
+ for (i = 0; i < 4; i++)
+ {
+ if ((s = strchr(s, ',')) == NULL)
+ return;
+ s++;
+ }
+ if (strcmp(s, "universe(att)"))
+ return;
+
+ (void) universe(U_ATT);
+#endif
+}
diff --git a/usr.sbin/cron/cron/externs.h b/usr.sbin/cron/cron/externs.h
new file mode 100644
index 0000000..3efe605
--- /dev/null
+++ b/usr.sbin/cron/cron/externs.h
@@ -0,0 +1,145 @@
+/* Copyright 1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ */
+
+#if defined(POSIX) || defined(ATT)
+# include <stdlib.h>
+# include <unistd.h>
+# include <string.h>
+# include <dirent.h>
+# define DIR_T struct dirent
+# define WAIT_T int
+# define WAIT_IS_INT 1
+extern char *tzname[2];
+# define TZONE(tm) tzname[(tm).tm_isdst]
+#endif
+
+#if defined(UNIXPC)
+# undef WAIT_T
+# undef WAIT_IS_INT
+# define WAIT_T union wait
+#endif
+
+#if defined(POSIX)
+# define SIG_T sig_t
+# define TIME_T time_t
+# define PID_T pid_t
+#endif
+
+#if defined(ATT)
+# define SIG_T void
+# define TIME_T long
+# define PID_T int
+#endif
+
+#if !defined(POSIX) && !defined(ATT)
+/* classic BSD */
+extern time_t time();
+extern unsigned sleep();
+extern struct tm *localtime();
+extern struct passwd *getpwnam();
+extern int errno;
+extern void perror(), exit(), free();
+extern char *getenv(), *strcpy(), *strchr(), *strtok();
+extern void *malloc(), *realloc();
+# define SIG_T void
+# define TIME_T long
+# define PID_T int
+# define WAIT_T union wait
+# define DIR_T struct direct
+# include <sys/dir.h>
+# define TZONE(tm) (tm).tm_zone
+#endif
+
+/* getopt() isn't part of POSIX. some systems define it in <stdlib.h> anyway.
+ * of those that do, some complain that our definition is different and some
+ * do not. to add to the misery and confusion, some systems define getopt()
+ * in ways that we cannot predict or comprehend, yet do not define the adjunct
+ * external variables needed for the interface.
+ */
+#if (!defined(BSD) || (BSD < 198911)) && !defined(ATT) && !defined(UNICOS)
+int getopt __P((int, char * const *, const char *));
+#endif
+
+#if (!defined(BSD) || (BSD < 199103))
+extern char *optarg;
+extern int optind, opterr, optopt;
+#endif
+
+#if WAIT_IS_INT
+# ifndef WEXITSTATUS
+# define WEXITSTATUS(x) (((x) >> 8) & 0xff)
+# endif
+# ifndef WTERMSIG
+# define WTERMSIG(x) ((x) & 0x7f)
+# endif
+# ifndef WCOREDUMP
+# define WCOREDUMP(x) ((x) & 0x80)
+# endif
+#else /*WAIT_IS_INT*/
+# ifndef WEXITSTATUS
+# define WEXITSTATUS(x) ((x).w_retcode)
+# endif
+# ifndef WTERMSIG
+# define WTERMSIG(x) ((x).w_termsig)
+# endif
+# ifndef WCOREDUMP
+# define WCOREDUMP(x) ((x).w_coredump)
+# endif
+#endif /*WAIT_IS_INT*/
+
+#ifndef WIFSIGNALED
+#define WIFSIGNALED(x) (WTERMSIG(x) != 0)
+#endif
+#ifndef WIFEXITED
+#define WIFEXITED(x) (WTERMSIG(x) == 0)
+#endif
+
+#ifdef NEED_STRCASECMP
+extern int strcasecmp __P((char *, char *));
+#endif
+
+#ifdef NEED_STRDUP
+extern char *strdup __P((char *));
+#endif
+
+#ifdef NEED_STRERROR
+extern char *strerror __P((int));
+#endif
+
+#ifdef NEED_FLOCK
+extern int flock __P((int, int));
+# define LOCK_SH 1
+# define LOCK_EX 2
+# define LOCK_NB 4
+# define LOCK_UN 8
+#endif
+
+#ifdef NEED_SETSID
+extern int setsid __P((void));
+#endif
+
+#ifdef NEED_GETDTABLESIZE
+extern int getdtablesize __P((void));
+#endif
+
+#ifdef NEED_SETENV
+extern int setenv __P((char *, char *, int));
+#endif
+
+#ifdef NEED_VFORK
+extern PID_T vfork __P((void));
+#endif
diff --git a/usr.sbin/cron/cron/job.c b/usr.sbin/cron/cron/job.c
new file mode 100644
index 0000000..6d1b1af
--- /dev/null
+++ b/usr.sbin/cron/cron/job.c
@@ -0,0 +1,76 @@
+/* Copyright 1988,1990,1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ */
+
+#if !defined(lint) && !defined(LINT)
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif
+
+
+#include "cron.h"
+
+
+typedef struct _job {
+ struct _job *next;
+ entry *e;
+ user *u;
+} job;
+
+
+static job *jhead = NULL, *jtail = NULL;
+
+
+void
+job_add(e, u)
+ register entry *e;
+ register user *u;
+{
+ register job *j;
+
+ /* if already on queue, keep going */
+ for (j=jhead; j; j=j->next)
+ if (j->e == e && j->u == u) { return; }
+
+ /* build a job queue element */
+ if ((j = (job*)malloc(sizeof(job))) == NULL)
+ return;
+ j->next = (job*) NULL;
+ j->e = e;
+ j->u = u;
+
+ /* add it to the tail */
+ if (!jhead) { jhead=j; }
+ else { jtail->next=j; }
+ jtail = j;
+}
+
+
+int
+job_runqueue()
+{
+ register job *j, *jn;
+ register int run = 0;
+
+ for (j=jhead; j; j=jn) {
+ do_command(j->e, j->u);
+ jn = j->next;
+ free(j);
+ run++;
+ }
+ jhead = jtail = NULL;
+ return run;
+}
diff --git a/usr.sbin/cron/cron/pathnames.h b/usr.sbin/cron/cron/pathnames.h
new file mode 100644
index 0000000..ba91bfd
--- /dev/null
+++ b/usr.sbin/cron/cron/pathnames.h
@@ -0,0 +1,81 @@
+/* Copyright 1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ */
+
+/*
+ * $FreeBSD$
+ */
+
+#if (defined(BSD)) && (BSD >= 199103) || defined(__linux) || defined(AIX)
+# include <paths.h>
+#endif /*BSD*/
+
+#ifndef CRONDIR
+ /* CRONDIR is where crond(8) and crontab(1) both chdir
+ * to; SPOOL_DIR, ALLOW_FILE, DENY_FILE, and LOG_FILE
+ * are all relative to this directory.
+ */
+#define CRONDIR "/var/cron"
+#endif
+
+ /* SPOOLDIR is where the crontabs live.
+ * This directory will have its modtime updated
+ * whenever crontab(1) changes a crontab; this is
+ * the signal for crond(8) to look at each individual
+ * crontab file and reload those whose modtimes are
+ * newer than they were last time around (or which
+ * didn't exist last time around...)
+ */
+#define SPOOL_DIR "tabs"
+
+ /* undefining these turns off their features. note
+ * that ALLOW_FILE and DENY_FILE must both be defined
+ * in order to enable the allow/deny code. If neither
+ * LOG_FILE or SYSLOG is defined, we don't log. If
+ * both are defined, we log both ways.
+ */
+#define ALLOW_FILE "allow" /*-*/
+#define DENY_FILE "deny" /*-*/
+/*#define LOG_FILE "log"*/ /*-*/
+
+ /* where should the daemon stick its PID?
+ */
+#ifdef _PATH_VARRUN
+# define PIDDIR _PATH_VARRUN
+#else
+# define PIDDIR "/etc/"
+#endif
+#define PIDFILE "%scron.pid"
+
+ /* 4.3BSD-style crontab */
+#define SYSCRONTAB "/etc/crontab"
+
+ /* what editor to use if no EDITOR or VISUAL
+ * environment variable specified.
+ */
+#if defined(_PATH_VI)
+# define EDITOR _PATH_VI
+#else
+# define EDITOR "/usr/ucb/vi"
+#endif
+
+#ifndef _PATH_BSHELL
+# define _PATH_BSHELL "/bin/sh"
+#endif
+
+#ifndef _PATH_DEFPATH
+# define _PATH_DEFPATH "/usr/bin:/bin"
+#endif
diff --git a/usr.sbin/cron/cron/popen.c b/usr.sbin/cron/cron/popen.c
new file mode 100644
index 0000000..806676d
--- /dev/null
+++ b/usr.sbin/cron/cron/popen.c
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 1988 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software written by Ken Arnold and
+ * published in UNIX Review, Vol. 6, No. 8.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+/* this came out of the ftpd sources; it's been modified to avoid the
+ * globbing stuff since we don't need it. also execvp instead of execv.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)popen.c 5.7 (Berkeley) 2/14/89";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include "cron.h"
+#include <sys/signal.h>
+#include <fcntl.h>
+#include <paths.h>
+#if defined(SYSLOG)
+# include <syslog.h>
+#endif
+#if defined(LOGIN_CAP)
+# include <login_cap.h>
+#endif
+
+
+#define MAX_ARGS 100
+#define WANT_GLOBBING 0
+
+/*
+ * Special version of popen which avoids call to shell. This insures noone
+ * may create a pipe to a hidden program as a side effect of a list or dir
+ * command.
+ */
+static PID_T *pids;
+static int fds;
+
+FILE *
+cron_popen(program, type, e)
+ char *program, *type;
+ entry *e;
+{
+ register char *cp;
+ FILE *iop;
+ int argc, pdes[2];
+ PID_T pid;
+ char *usernm;
+ char *argv[MAX_ARGS + 1];
+# if defined(LOGIN_CAP)
+ struct passwd *pwd;
+ login_cap_t *lc;
+# endif
+#if WANT_GLOBBING
+ char **pop, *vv[2];
+ int gargc;
+ char *gargv[1000];
+ extern char **glob(), **copyblk();
+#endif
+
+ if ((*type != 'r' && *type != 'w') || type[1])
+ return(NULL);
+
+ if (!pids) {
+ if ((fds = getdtablesize()) <= 0)
+ return(NULL);
+ if (!(pids = (PID_T *)malloc((u_int)(fds * sizeof(PID_T)))))
+ return(NULL);
+ bzero((char *)pids, fds * sizeof(PID_T));
+ }
+ if (pipe(pdes) < 0)
+ return(NULL);
+
+ /* break up string into pieces */
+ for (argc = 0, cp = program; argc < MAX_ARGS; cp = NULL)
+ if (!(argv[argc++] = strtok(cp, " \t\n")))
+ break;
+ argv[MAX_ARGS] = NULL;
+
+#if WANT_GLOBBING
+ /* glob each piece */
+ gargv[0] = argv[0];
+ for (gargc = argc = 1; argv[argc]; argc++) {
+ if (!(pop = glob(argv[argc]))) { /* globbing failed */
+ vv[0] = argv[argc];
+ vv[1] = NULL;
+ pop = copyblk(vv);
+ }
+ argv[argc] = (char *)pop; /* save to free later */
+ while (*pop && gargc < 1000)
+ gargv[gargc++] = *pop++;
+ }
+ gargv[gargc] = NULL;
+#endif
+
+ iop = NULL;
+ switch(pid = vfork()) {
+ case -1: /* error */
+ (void)close(pdes[0]);
+ (void)close(pdes[1]);
+ goto pfree;
+ /* NOTREACHED */
+ case 0: /* child */
+ if (e != NULL) {
+#ifdef SYSLOG
+ closelog();
+#endif
+
+ /* get new pgrp, void tty, etc.
+ */
+ (void) setsid();
+ }
+ if (*type == 'r') {
+ /* Do not share our parent's stdin */
+ (void)close(0);
+ (void)open(_PATH_DEVNULL, O_RDWR);
+ if (pdes[1] != 1) {
+ dup2(pdes[1], 1);
+ dup2(pdes[1], 2); /* stderr, too! */
+ (void)close(pdes[1]);
+ }
+ (void)close(pdes[0]);
+ } else {
+ if (pdes[0] != 0) {
+ dup2(pdes[0], 0);
+ (void)close(pdes[0]);
+ }
+ /* Hack: stdout gets revoked */
+ (void)close(1);
+ (void)open(_PATH_DEVNULL, O_RDWR);
+ (void)close(2);
+ (void)open(_PATH_DEVNULL, O_RDWR);
+ (void)close(pdes[1]);
+ }
+ if (e != NULL) {
+ /* Set user's entire context, but skip the environment
+ * as cron provides a separate interface for this
+ */
+ usernm = env_get("LOGNAME", e->envp);
+# if defined(LOGIN_CAP)
+ if ((pwd = getpwnam(usernm)) == NULL)
+ pwd = getpwuid(e->uid);
+ lc = NULL;
+ if (pwd != NULL) {
+ pwd->pw_gid = e->gid;
+ if (e->class != NULL)
+ lc = login_getclass(e->class);
+ }
+ if (pwd &&
+ setusercontext(lc, pwd, e->uid,
+ LOGIN_SETALL & ~(LOGIN_SETPATH|LOGIN_SETENV)) == 0)
+ (void) endpwent();
+ else {
+ /* fall back to the old method */
+ (void) endpwent();
+# endif
+ /* set our directory, uid and gid. Set gid first,
+ * since once we set uid, we've lost root privledges.
+ */
+ setgid(e->gid);
+# if defined(BSD)
+ initgroups(usernm, e->gid);
+# endif
+ setlogin(usernm);
+ setuid(e->uid); /* we aren't root after this..*/
+#if defined(LOGIN_CAP)
+ }
+ if (lc != NULL)
+ login_close(lc);
+#endif
+ chdir(env_get("HOME", e->envp));
+ }
+#if WANT_GLOBBING
+ execvp(gargv[0], gargv);
+#else
+ execvp(argv[0], argv);
+#endif
+ _exit(1);
+ }
+ /* parent; assume fdopen can't fail... */
+ if (*type == 'r') {
+ iop = fdopen(pdes[0], type);
+ (void)close(pdes[1]);
+ } else {
+ iop = fdopen(pdes[1], type);
+ (void)close(pdes[0]);
+ }
+ pids[fileno(iop)] = pid;
+
+pfree:
+#if WANT_GLOBBING
+ for (argc = 1; argv[argc] != NULL; argc++) {
+/* blkfree((char **)argv[argc]); */
+ free((char *)argv[argc]);
+ }
+#endif
+ return(iop);
+}
+
+int
+cron_pclose(iop)
+ FILE *iop;
+{
+ register int fdes;
+ int omask;
+ WAIT_T stat_loc;
+ PID_T pid;
+
+ /*
+ * pclose returns -1 if stream is not associated with a
+ * `popened' command, or, if already `pclosed'.
+ */
+ if (pids == 0 || pids[fdes = fileno(iop)] == 0)
+ return(-1);
+ (void)fclose(iop);
+ omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
+ while ((pid = wait(&stat_loc)) != pids[fdes] && pid != -1)
+ ;
+ (void)sigsetmask(omask);
+ pids[fdes] = 0;
+ return (pid == -1 ? -1 : WEXITSTATUS(stat_loc));
+}
diff --git a/usr.sbin/cron/cron/user.c b/usr.sbin/cron/cron/user.c
new file mode 100644
index 0000000..16fd617
--- /dev/null
+++ b/usr.sbin/cron/cron/user.c
@@ -0,0 +1,128 @@
+/* Copyright 1988,1990,1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ */
+
+#if !defined(lint) && !defined(LINT)
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif
+
+/* vix 26jan87 [log is in RCS file]
+ */
+
+
+#include "cron.h"
+
+static char *User_name;
+
+void
+free_user(u)
+ user *u;
+{
+ entry *e, *ne;
+
+ free(u->name);
+ for (e = u->crontab; e != NULL; e = ne) {
+ ne = e->next;
+ free_entry(e);
+ }
+ free(u);
+}
+
+static void
+log_error(msg)
+ char *msg;
+{
+ log_it(User_name, getpid(), "PARSE", msg);
+}
+
+user *
+load_user(crontab_fd, pw, name)
+ int crontab_fd;
+ struct passwd *pw; /* NULL implies syscrontab */
+ char *name;
+{
+ char envstr[MAX_ENVSTR];
+ FILE *file;
+ user *u;
+ entry *e;
+ int status;
+ char **envp, **tenvp;
+
+ if (!(file = fdopen(crontab_fd, "r"))) {
+ warn("fdopen on crontab_fd in load_user");
+ return NULL;
+ }
+
+ Debug(DPARS, ("load_user()\n"))
+
+ /* file is open. build user entry, then read the crontab file.
+ */
+ if ((u = (user *) malloc(sizeof(user))) == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ if ((u->name = strdup(name)) == NULL) {
+ free(u);
+ errno = ENOMEM;
+ return NULL;
+ }
+ u->crontab = NULL;
+
+ /*
+ * init environment. this will be copied/augmented for each entry.
+ */
+ if ((envp = env_init()) == NULL) {
+ free(u->name);
+ free(u);
+ return NULL;
+ }
+
+ /*
+ * load the crontab
+ */
+ while ((status = load_env(envstr, file)) >= OK) {
+ switch (status) {
+ case ERR:
+ free_user(u);
+ u = NULL;
+ goto done;
+ case FALSE:
+ User_name = u->name; /* for log_error */
+ e = load_entry(file, log_error, pw, envp);
+ if (e) {
+ e->next = u->crontab;
+ u->crontab = e;
+ }
+ break;
+ case TRUE:
+ if ((tenvp = env_set(envp, envstr))) {
+ envp = tenvp;
+ } else {
+ free_user(u);
+ u = NULL;
+ goto done;
+ }
+ break;
+ }
+ }
+
+ done:
+ env_free(envp);
+ fclose(file);
+ Debug(DPARS, ("...load_user() done\n"))
+ return u;
+}
diff --git a/usr.sbin/cron/crontab/Makefile b/usr.sbin/cron/crontab/Makefile
new file mode 100644
index 0000000..96a4bd6
--- /dev/null
+++ b/usr.sbin/cron/crontab/Makefile
@@ -0,0 +1,16 @@
+# $FreeBSD$
+
+BINDIR= /usr/bin
+
+PROG= crontab
+MAN= crontab.1 crontab.5
+BINOWN= root
+BINMODE=4555
+INSTALLFLAGS=-fschg
+
+CFLAGS+= -I${.CURDIR}/../cron
+
+DPADD= ${LIBCRON} ${LIBUTIL}
+LDADD= ${LIBCRON} -lutil
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/cron/crontab/crontab.1 b/usr.sbin/cron/crontab/crontab.1
new file mode 100644
index 0000000..32b0bb1
--- /dev/null
+++ b/usr.sbin/cron/crontab/crontab.1
@@ -0,0 +1,125 @@
+.\"/* Copyright 1988,1990,1993 by Paul Vixie
+.\" * All rights reserved
+.\" *
+.\" * Distribute freely, except: don't remove my name from the source or
+.\" * documentation (don't take credit for my work), mark your changes (don't
+.\" * get me blamed for your possible bugs), don't alter or remove this
+.\" * notice. May be sold if buildable source is provided to buyer. No
+.\" * warrantee of any kind, express or implied, is included with this
+.\" * software; use at your own risk, responsibility for damages (if any) to
+.\" * anyone resulting from the use of this software rests entirely with the
+.\" * user.
+.\" *
+.\" * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+.\" * I'll try to keep a version up to date. I can be reached as follows:
+.\" * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+.\" */
+.\"
+.\" $FreeBSD$
+.\"
+.Dd December 29, 1993
+.Dt CRONTAB 1
+.Os
+.Sh NAME
+.Nm crontab
+.Nd maintain crontab files for individual users (V3)
+.Sh SYNOPSIS
+.Nm
+.Op Fl u Ar user
+.Ar file
+.Nm
+.Op Fl u Ar user
+{
+.Fl l |
+.Fl r |
+.Fl e
+}
+.Sh DESCRIPTION
+The
+.Nm
+utility is the program used to install, deinstall or list the tables
+used to drive the
+.Xr cron 8
+daemon in Vixie Cron. Each user can have their own crontab, and though
+these are files in
+.Pa /var ,
+they are not intended to be edited directly.
+.Pp
+If the
+.Pa allow
+file exists, then you must be listed therein in order to be allowed to use
+this command. If the
+.Pa allow
+file does not exist but the
+.Pa deny
+file does exist, then you must
+.Em not
+be listed in the
+.Pa deny
+file in order to use this command. If neither of these files exists, then
+depending on site-dependent configuration parameters, only the super user
+will be allowed to use this command, or all users will be able to use this
+command.
+The format of these files is one username per line,
+with no leading or trailing whitespace.
+Lines of other formats will be ignored,
+and so can be used for comments.
+.Pp
+The first form of this command is used to install a new crontab from some
+named file or standard input if the pseudo-filename ``-'' 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 1
+can confuse
+.Nm
+and that if you are running inside of
+.Xr su 1
+you should always use the
+.Fl u
+option for safety's sake.
+.It Fl l
+Display the current crontab on standard output.
+.It Fl r
+Remove the current crontab.
+.It Fl e
+Edit the current crontab using the editor specified by
+the
+.Ev VISUAL
+or
+.Ev EDITOR
+environment variables.
+The specified editor
+.Em must
+edit the file in place;
+any editor that unlinks the file and recreates it cannot be used.
+After you exit
+from the editor, the modified crontab will be installed automatically.
+.El
+.Sh SEE ALSO
+.Xr crontab 5 ,
+.Xr cron 8
+.Sh FILES
+.Bl -tag -width /var/cron/allow -compact
+.It Pa /var/cron/allow
+.It Pa /var/cron/deny
+.El
+.Sh STANDARDS
+The
+.Nm
+command conforms to
+.St -p1003.2 .
+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..2578a55
--- /dev/null
+++ b/usr.sbin/cron/crontab/crontab.5
@@ -0,0 +1,276 @@
+.\"/* Copyright 1988,1990,1993,1994 by Paul Vixie
+.\" * All rights reserved
+.\" *
+.\" * Distribute freely, except: don't remove my name from the source or
+.\" * documentation (don't take credit for my work), mark your changes (don't
+.\" * get me blamed for your possible bugs), don't alter or remove this
+.\" * notice. May be sold if buildable source is provided to buyer. No
+.\" * warrantee of any kind, express or implied, is included with this
+.\" * software; use at your own risk, responsibility for damages (if any) to
+.\" * anyone resulting from the use of this software rests entirely with the
+.\" * user.
+.\" *
+.\" * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+.\" * I'll try to keep a version up to date. I can be reached as follows:
+.\" * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+.\" */
+.\"
+.\" $FreeBSD$
+.\"
+.Dd January 24, 1994
+.Dt CRONTAB 5
+.Os
+.Sh NAME
+.Nm crontab
+.Nd tables for driving cron
+.Sh DESCRIPTION
+A
+.Nm
+file contains instructions to the
+.Xr cron 8
+daemon of the general form: ``run this command at this time on this date''.
+Each user has their own crontab, and commands in any given crontab will be
+executed as the user who owns the crontab. Uucp and News will usually have
+their own crontabs, eliminating the need for explicitly running
+.Xr su 1
+as part of a cron command.
+.Pp
+Blank lines and leading spaces and tabs are ignored. Lines whose first
+non-space character is a pound-sign (#) are comments, and are ignored.
+Note that comments are not allowed on the same line as cron commands, since
+they will be taken to be part of the command. Similarly, comments are not
+allowed on the same line as environment variable settings.
+.Pp
+An active line in a crontab will be either an environment setting or a cron
+command. An environment setting is of the form,
+.Bd -literal
+ name = value
+.Ed
+.Pp
+where the spaces around the equal-sign (=) are optional, and any subsequent
+non-leading spaces in
+.Em value
+will be part of the value assigned to
+.Em name .
+The
+.Em value
+string may be placed in quotes (single or double, but matching) to preserve
+leading or trailing blanks.
+The
+.Em name
+string may also be placed in quote (single or double, but matching)
+to preserve leading, traling or inner blanks.
+.Pp
+Several environment variables are set up
+automatically by the
+.Xr cron 8
+daemon.
+.Ev SHELL
+is set to
+.Pa /bin/sh ,
+and
+.Ev LOGNAME
+and
+.Ev HOME
+are set from the
+.Pa /etc/passwd
+line of the crontab's owner.
+.Ev HOME
+and
+.Ev SHELL
+may be overridden by settings in the crontab;
+.Ev LOGNAME
+may not.
+.Pp
+(Another note: the
+.Ev LOGNAME
+variable is sometimes called
+.Ev USER
+on
+.Bx
+systems...
+On these systems,
+.Ev USER
+will be set also).
+.Pp
+In addition to
+.Ev LOGNAME ,
+.Ev HOME ,
+and
+.Ev SHELL ,
+.Xr cron 8
+will look at
+.Ev MAILTO
+if it has any reason to send mail as a result of running
+commands in ``this'' crontab. If
+.Ev MAILTO
+is defined (and non-empty), mail is
+sent to the user so named. If
+.Ev MAILTO
+is defined but empty (MAILTO=""), no
+mail will be sent. Otherwise mail is sent to the owner of the crontab. This
+option is useful if you decide on
+.Pa /bin/mail
+instead of
+.Pa /usr/lib/sendmail
+as
+your mailer when you install cron --
+.Pa /bin/mail
+doesn't do aliasing, and UUCP
+usually doesn't read its mail.
+.Pp
+The format of a cron command is very much the V7 standard, with a number of
+upward-compatible extensions. Each line has five time and date fields,
+followed by a user name
+(with optional ``:<group>'' and ``/<login-class>'' suffixes)
+if this is the system crontab file,
+followed by a command. Commands are executed by
+.Xr cron 8
+when the minute, hour, and month of year fields match the current time,
+.Em and
+when at least one of the two day fields (day of month, or day of week)
+matches the current time (see ``Note'' below).
+.Xr cron 8
+examines cron entries once every minute.
+The time and date fields are:
+.Bd -literal -offset indent
+field allowed values
+----- --------------
+minute 0-59
+hour 0-23
+day of month 1-31
+month 1-12 (or names, see below)
+day of week 0-7 (0 or 7 is Sun, or use names)
+.Ed
+.Pp
+A field may be an asterisk (*), which always stands for ``first\-last''.
+.Pp
+Ranges of numbers are allowed. Ranges are two numbers separated
+with a hyphen. The specified range is inclusive. For example,
+8-11 for an ``hours'' entry specifies execution at hours 8, 9, 10
+and 11.
+.Pp
+Lists are allowed. A list is a set of numbers (or ranges)
+separated by commas. Examples: ``1,2,5,9'', ``0-4,8-12''.
+.Pp
+Step values can be used in conjunction with ranges. Following
+a range with ``/<number>'' specifies skips of the number's value
+through the range. For example, ``0-23/2'' can be used in the hours
+field to specify command execution every other hour (the alternative
+in the V7 standard is ``0,2,4,6,8,10,12,14,16,18,20,22''). Steps are
+also permitted after an asterisk, so if you want to say ``every two
+hours'', just use ``*/2''.
+.Pp
+Names can also be used for the ``month'' and ``day of week''
+fields. Use the first three letters of the particular
+day or month (case doesn't matter). Ranges or
+lists of names are not allowed.
+.Pp
+The ``sixth'' field (the rest of the line) specifies the command to be
+run.
+The entire command portion of the line, up to a newline or %
+character, will be executed by
+.Pa /bin/sh
+or by the shell
+specified in the
+.Ev SHELL
+variable of the cronfile.
+Percent-signs (%) in the command, unless escaped with backslash
+(\\), will be changed into newline characters, and all data
+after the first % will be sent to the command as standard
+input.
+.Pp
+Note: The day of a command's execution can be specified by two
+fields \(em day of month, and day of week. If both fields are
+restricted (ie, aren't *), the command will be run when
+.Em either
+field matches the current time. For example,
+``30 4 1,15 * 5''
+would cause a command to be run at 4:30 am on the 1st and 15th of each
+month, plus every Friday.
+.Pp
+Instead of the first five fields,
+one of eight special strings may appear:
+.Bd -literal -offset indent
+string meaning
+------ -------
+@reboot Run once, at startup.
+@yearly Run once a year, "0 0 1 1 *".
+@annually (same as @yearly)
+@monthly Run once a month, "0 0 1 * *".
+@weekly Run once a week, "0 0 * * 0".
+@daily Run once a day, "0 0 * * *".
+@midnight (same as @daily)
+@hourly Run once an hour, "0 * * * *".
+.Ed
+.Sh EXAMPLE CRON FILE
+.Bd -literal
+
+# use /bin/sh to run commands, overriding the default set by cron
+SHELL=/bin/sh
+# mail any output to `paul', no matter whose crontab this is
+MAILTO=paul
+#
+# run five minutes after midnight, every day
+5 0 * * * $HOME/bin/daily.job >> $HOME/tmp/out 2>&1
+# run at 2:15pm on the first of every month -- output mailed to paul
+15 14 1 * * $HOME/bin/monthly
+# run at 10 pm on weekdays, annoy Joe
+0 22 * * 1-5 mail -s "It's 10pm" joe%Joe,%%Where are your kids?%
+23 0-23/2 * * * echo "run 23 minutes after midn, 2am, 4am ..., everyday"
+5 4 * * sun echo "run at 5 after 4 every sunday"
+.Ed
+.Sh SEE ALSO
+.Xr crontab 1 ,
+.Xr cron 8
+.Sh EXTENSIONS
+When specifying day of week, both day 0 and day 7 will be considered Sunday.
+.Bx
+and
+.Tn ATT
+seem to disagree about this.
+.Pp
+Lists and ranges are allowed to co-exist in the same field. "1-3,7-9" would
+be rejected by
+.Tn ATT
+or
+.Bx
+cron -- they want to see "1-3" or "7,8,9" ONLY.
+.Pp
+Ranges can include "steps", so "1-9/2" is the same as "1,3,5,7,9".
+.Pp
+Names of months or days of the week can be specified by name.
+.Pp
+Environment variables can be set in the crontab. In
+.Bx
+or
+.Tn ATT ,
+the
+environment handed to child processes is basically the one from
+.Pa /etc/rc .
+.Pp
+Command output is mailed to the crontab owner
+.No ( Bx
+can't do this), can be
+mailed to a person other than the crontab owner (SysV can't do this), or the
+feature can be turned off and no mail will be sent at all (SysV can't do this
+either).
+.Pp
+All of the
+.Sq @
+commands that can appear in place of the first five fields
+are extensions.
+.Sh AUTHORS
+.An Paul Vixie Aq paul@vix.com
+.Sh BUGS
+If you're in one of the 70-odd countries that observe Daylight
+Savings Time, jobs scheduled during the rollback or advance will be
+affected. In general, it's not a good idea to schedule jobs during
+this period.
+.Pp
+For US timezones (except parts of IN, AZ, and HI) the time shift occurs at
+2AM local time. For others, the output of the
+.Xr zdump 8
+program's verbose
+.Fl ( v )
+option can be used to determine the moment of time shift.
diff --git a/usr.sbin/cron/crontab/crontab.c b/usr.sbin/cron/crontab/crontab.c
new file mode 100644
index 0000000..d070cd7
--- /dev/null
+++ b/usr.sbin/cron/crontab/crontab.c
@@ -0,0 +1,626 @@
+/* Copyright 1988,1990,1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ * From Id: crontab.c,v 2.13 1994/01/17 03:20:37 vixie Exp
+ */
+
+#if !defined(lint) && !defined(LINT)
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif
+
+/* crontab - install and manage per-user crontab files
+ * vix 02may87 [RCS has the rest of the log]
+ * vix 26jan87 [original]
+ */
+
+#define MAIN_PROGRAM
+
+#include "cron.h"
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#ifdef USE_UTIMES
+# include <sys/time.h>
+#else
+# include <time.h>
+# include <utime.h>
+#endif
+#if defined(POSIX)
+# include <locale.h>
+#endif
+
+
+#define NHEADER_LINES 3
+
+
+enum opt_t { opt_unknown, opt_list, opt_delete, opt_edit, opt_replace };
+
+#if DEBUGGING
+static char *Options[] = { "???", "list", "delete", "edit", "replace" };
+#endif
+
+
+static PID_T Pid;
+static char User[MAX_UNAME], RealUser[MAX_UNAME];
+static char Filename[MAX_FNAME];
+static FILE *NewCrontab;
+static int CheckErrorCount;
+static enum opt_t Option;
+static struct passwd *pw;
+static void list_cmd __P((void)),
+ delete_cmd __P((void)),
+ edit_cmd __P((void)),
+ poke_daemon __P((void)),
+ check_error __P((char *)),
+ parse_args __P((int c, char *v[]));
+static int replace_cmd __P((void));
+
+
+static void
+usage(msg)
+ char *msg;
+{
+ fprintf(stderr, "crontab: usage error: %s\n", msg);
+ fprintf(stderr, "%s\n%s\n",
+ "usage: crontab [-u user] file",
+ " crontab [-u user] { -e | -l | -r }");
+ exit(ERROR_EXIT);
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int exitstatus;
+
+ Pid = getpid();
+ ProgramName = argv[0];
+
+#if defined(POSIX)
+ setlocale(LC_ALL, "");
+#endif
+
+#if defined(BSD)
+ setlinebuf(stderr);
+#endif
+ parse_args(argc, argv); /* sets many globals, opens a file */
+ set_cron_uid();
+ set_cron_cwd();
+ if (!allowed(User)) {
+ warnx("you (%s) are not allowed to use this program", User);
+ log_it(RealUser, Pid, "AUTH", "crontab command not allowed");
+ exit(ERROR_EXIT);
+ }
+ exitstatus = OK_EXIT;
+ switch (Option) {
+ case opt_list: list_cmd();
+ break;
+ case opt_delete: delete_cmd();
+ break;
+ case opt_edit: edit_cmd();
+ break;
+ case opt_replace: if (replace_cmd() < 0)
+ exitstatus = ERROR_EXIT;
+ break;
+ case opt_unknown:
+ break;
+ }
+ exit(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, x;
+
+ log_it(RealUser, Pid, "LIST", User);
+ (void) sprintf(n, CRON_TAB(User));
+ if (!(f = fopen(n, "r"))) {
+ if (errno == ENOENT)
+ errx(ERROR_EXIT, "no crontab for %s", User);
+ else
+ err(ERROR_EXIT, "%s", n);
+ }
+
+ /* file is open. copy to stdout, close.
+ */
+ Set_LineNum(1)
+
+ /* ignore the top few comments since we probably put them there.
+ */
+ for (x = 0; x < NHEADER_LINES; x++) {
+ ch = get_char(f);
+ if (EOF == ch)
+ break;
+ if ('#' != ch) {
+ putchar(ch);
+ break;
+ }
+ while (EOF != (ch = get_char(f)))
+ if (ch == '\n')
+ break;
+ if (EOF == ch)
+ break;
+ }
+
+ while (EOF != (ch = get_char(f)))
+ putchar(ch);
+ fclose(f);
+}
+
+
+static void
+delete_cmd() {
+ char n[MAX_FNAME];
+ int ch, first;
+
+ if (isatty(STDIN_FILENO)) {
+ (void)fprintf(stderr, "remove crontab for %s? ", User);
+ first = ch = getchar();
+ while (ch != '\n' && ch != EOF)
+ ch = getchar();
+ if (first != 'y' && first != 'Y')
+ return;
+ }
+
+ log_it(RealUser, Pid, "DELETE", User);
+ (void) sprintf(n, CRON_TAB(User));
+ if (unlink(n)) {
+ if (errno == ENOENT)
+ errx(ERROR_EXIT, "no crontab for %s", User);
+ else
+ err(ERROR_EXIT, "%s", n);
+ }
+ poke_daemon();
+}
+
+
+static void
+check_error(msg)
+ char *msg;
+{
+ CheckErrorCount++;
+ fprintf(stderr, "\"%s\":%d: %s\n", Filename, LineNumber-1, msg);
+}
+
+
+static void
+edit_cmd() {
+ char n[MAX_FNAME], q[MAX_TEMPSTR], *editor;
+ FILE *f;
+ int ch, t, x;
+ struct stat statbuf, fsbuf;
+ time_t mtime;
+ WAIT_T waiter;
+ PID_T pid, xpid;
+ mode_t um;
+
+ log_it(RealUser, Pid, "BEGIN EDIT", User);
+ (void) sprintf(n, CRON_TAB(User));
+ if (!(f = fopen(n, "r"))) {
+ if (errno != ENOENT)
+ err(ERROR_EXIT, "%s", n);
+ warnx("no crontab for %s - using an empty one", User);
+ if (!(f = fopen(_PATH_DEVNULL, "r")))
+ err(ERROR_EXIT, _PATH_DEVNULL);
+ }
+
+ um = umask(077);
+ (void) sprintf(Filename, "/tmp/crontab.XXXXXXXXXX");
+ if ((t = mkstemp(Filename)) == -1) {
+ warn("%s", Filename);
+ (void) umask(um);
+ goto fatal;
+ }
+ (void) umask(um);
+#ifdef HAS_FCHOWN
+ if (fchown(t, getuid(), getgid()) < 0) {
+#else
+ if (chown(Filename, getuid(), getgid()) < 0) {
+#endif
+ warn("fchown");
+ goto fatal;
+ }
+ if (!(NewCrontab = fdopen(t, "r+"))) {
+ warn("fdopen");
+ goto fatal;
+ }
+
+ Set_LineNum(1)
+
+ /* ignore the top few comments since we probably put them there.
+ */
+ for (x = 0; x < NHEADER_LINES; x++) {
+ ch = get_char(f);
+ if (EOF == ch)
+ break;
+ if ('#' != ch) {
+ putc(ch, NewCrontab);
+ break;
+ }
+ while (EOF != (ch = get_char(f)))
+ if (ch == '\n')
+ break;
+ if (EOF == ch)
+ break;
+ }
+
+ /* copy the rest of the crontab (if any) to the temp file.
+ */
+ if (EOF != ch)
+ while (EOF != (ch = get_char(f)))
+ putc(ch, NewCrontab);
+ fclose(f);
+ if (fflush(NewCrontab))
+ err(ERROR_EXIT, "%s", Filename);
+ if (fstat(t, &fsbuf) < 0) {
+ warn("unable to fstat temp file");
+ goto fatal;
+ }
+ again:
+ if (stat(Filename, &statbuf) < 0) {
+ warn("stat");
+ fatal: unlink(Filename);
+ exit(ERROR_EXIT);
+ }
+ if (statbuf.st_dev != fsbuf.st_dev || statbuf.st_ino != fsbuf.st_ino)
+ errx(ERROR_EXIT, "temp file must be edited in place");
+ mtime = statbuf.st_mtime;
+
+ if ((!(editor = getenv("VISUAL")))
+ && (!(editor = getenv("EDITOR")))
+ ) {
+ editor = EDITOR;
+ }
+
+ /* we still have the file open. editors will generally rewrite the
+ * original file rather than renaming/unlinking it and starting a
+ * new one; even backup files are supposed to be made by copying
+ * rather than by renaming. if some editor does not support this,
+ * then don't use it. the security problems are more severe if we
+ * close and reopen the file around the edit.
+ */
+
+ switch (pid = fork()) {
+ case -1:
+ warn("fork");
+ goto fatal;
+ case 0:
+ /* child */
+ if (setuid(getuid()) < 0)
+ err(ERROR_EXIT, "setuid(getuid())");
+ if (chdir("/tmp") < 0)
+ err(ERROR_EXIT, "chdir(/tmp)");
+ if (strlen(editor) + strlen(Filename) + 2 >= MAX_TEMPSTR)
+ errx(ERROR_EXIT, "editor or filename too long");
+ execlp(editor, editor, Filename, (char *)NULL);
+ err(ERROR_EXIT, "%s", editor);
+ /*NOTREACHED*/
+ default:
+ /* parent */
+ break;
+ }
+
+ /* parent */
+ {
+ void (*f[4])();
+ f[0] = signal(SIGHUP, SIG_IGN);
+ f[1] = signal(SIGINT, SIG_IGN);
+ f[2] = signal(SIGTERM, SIG_IGN);
+ xpid = wait(&waiter);
+ signal(SIGHUP, f[0]);
+ signal(SIGINT, f[1]);
+ signal(SIGTERM, f[2]);
+ }
+ if (xpid != pid) {
+ warnx("wrong PID (%d != %d) from \"%s\"", xpid, pid, editor);
+ goto fatal;
+ }
+ if (WIFEXITED(waiter) && WEXITSTATUS(waiter)) {
+ warnx("\"%s\" exited with status %d", editor, WEXITSTATUS(waiter));
+ goto fatal;
+ }
+ if (WIFSIGNALED(waiter)) {
+ warnx("\"%s\" killed; signal %d (%score dumped)",
+ editor, WTERMSIG(waiter), WCOREDUMP(waiter) ?"" :"no ");
+ goto fatal;
+ }
+ if (stat(Filename, &statbuf) < 0) {
+ warn("stat");
+ goto fatal;
+ }
+ if (statbuf.st_dev != fsbuf.st_dev || statbuf.st_ino != fsbuf.st_ino)
+ errx(ERROR_EXIT, "temp file must be edited in place");
+ if (mtime == statbuf.st_mtime) {
+ warnx("no changes made to crontab");
+ goto remove;
+ }
+ warnx("installing new crontab");
+ switch (replace_cmd()) {
+ case 0:
+ break;
+ case -1:
+ for (;;) {
+ printf("Do you want to retry the same edit? ");
+ fflush(stdout);
+ q[0] = '\0';
+ (void) fgets(q, sizeof q, stdin);
+ switch (islower(q[0]) ? q[0] : tolower(q[0])) {
+ case 'y':
+ goto again;
+ case 'n':
+ goto abandon;
+ default:
+ fprintf(stderr, "Enter Y or N\n");
+ }
+ }
+ /*NOTREACHED*/
+ case -2:
+ abandon:
+ warnx("edits left in %s", Filename);
+ goto done;
+ default:
+ warnx("panic: bad switch() in replace_cmd()");
+ goto fatal;
+ }
+ remove:
+ unlink(Filename);
+ done:
+ log_it(RealUser, Pid, "END EDIT", User);
+}
+
+
+/* returns 0 on success
+ * -1 on syntax error
+ * -2 on install error
+ */
+static int
+replace_cmd() {
+ char n[MAX_FNAME], envstr[MAX_ENVSTR], tn[MAX_FNAME];
+ FILE *tmp;
+ int ch, eof;
+ entry *e;
+ time_t now = time(NULL);
+ char **envp = env_init();
+
+ if (envp == NULL) {
+ warnx("cannot allocate memory");
+ return (-2);
+ }
+
+ (void) sprintf(n, "tmp.%d", Pid);
+ (void) sprintf(tn, CRON_TAB(n));
+ if (!(tmp = fopen(tn, "w+"))) {
+ warn("%s", tn);
+ return (-2);
+ }
+
+ /* write a signature at the top of the file.
+ *
+ * VERY IMPORTANT: make sure NHEADER_LINES agrees with this code.
+ */
+ fprintf(tmp, "# DO NOT EDIT THIS FILE - edit the master and reinstall.\n");
+ fprintf(tmp, "# (%s installed on %-24.24s)\n", Filename, ctime(&now));
+ fprintf(tmp, "# (Cron version -- %s)\n", rcsid);
+
+ /* copy the crontab to the tmp
+ */
+ rewind(NewCrontab);
+ Set_LineNum(1)
+ while (EOF != (ch = get_char(NewCrontab)))
+ putc(ch, tmp);
+ ftruncate(fileno(tmp), ftell(tmp));
+ fflush(tmp); rewind(tmp);
+
+ if (ferror(tmp)) {
+ warnx("error while writing new crontab to %s", tn);
+ fclose(tmp); unlink(tn);
+ return (-2);
+ }
+
+ /* check the syntax of the file being installed.
+ */
+
+ /* BUG: was reporting errors after the EOF if there were any errors
+ * in the file proper -- kludged it by stopping after first error.
+ * vix 31mar87
+ */
+ Set_LineNum(1 - NHEADER_LINES)
+ CheckErrorCount = 0; eof = FALSE;
+ while (!CheckErrorCount && !eof) {
+ switch (load_env(envstr, tmp)) {
+ case ERR:
+ eof = TRUE;
+ break;
+ case FALSE:
+ e = load_entry(tmp, check_error, pw, envp);
+ if (e)
+ free(e);
+ break;
+ case TRUE:
+ break;
+ }
+ }
+
+ if (CheckErrorCount != 0) {
+ warnx("errors in crontab file, can't install");
+ fclose(tmp); unlink(tn);
+ return (-1);
+ }
+
+#ifdef HAS_FCHOWN
+ if (fchown(fileno(tmp), ROOT_UID, -1) < OK)
+#else
+ if (chown(tn, ROOT_UID, -1) < OK)
+#endif
+ {
+ warn("chown");
+ fclose(tmp); unlink(tn);
+ return (-2);
+ }
+
+#ifdef HAS_FCHMOD
+ if (fchmod(fileno(tmp), 0600) < OK)
+#else
+ if (chmod(tn, 0600) < OK)
+#endif
+ {
+ warn("chown");
+ fclose(tmp); unlink(tn);
+ return (-2);
+ }
+
+ if (fclose(tmp) == EOF) {
+ warn("fclose");
+ unlink(tn);
+ return (-2);
+ }
+
+ (void) sprintf(n, CRON_TAB(User));
+ if (rename(tn, n)) {
+ warn("error renaming %s to %s", tn, n);
+ unlink(tn);
+ return (-2);
+ }
+ log_it(RealUser, Pid, "REPLACE", User);
+
+ poke_daemon();
+
+ return (0);
+}
+
+
+static void
+poke_daemon() {
+#ifdef USE_UTIMES
+ struct timeval tvs[2];
+ struct timezone tz;
+
+ (void) gettimeofday(&tvs[0], &tz);
+ tvs[1] = tvs[0];
+ if (utimes(SPOOL_DIR, tvs) < OK) {
+ warn("can't update mtime on spooldir %s", SPOOL_DIR);
+ return;
+ }
+#else
+ if (utime(SPOOL_DIR, NULL) < OK) {
+ warn("can't update mtime on spooldir %s", SPOOL_DIR);
+ return;
+ }
+#endif /*USE_UTIMES*/
+}
diff --git a/usr.sbin/cron/doc/CHANGES b/usr.sbin/cron/doc/CHANGES
new file mode 100644
index 0000000..59f6803
--- /dev/null
+++ b/usr.sbin/cron/doc/CHANGES
@@ -0,0 +1,155 @@
+Vixie Cron Changes from V2 to V3
+Paul Vixie
+29-Dec-1993
+
+The crontab command now conforms to POSIX 1003.2. This means that when you
+install it, if you have any "crontab" command lines floating around in shell
+scripts (such as /etc/rc or /etc/rc.local), you will need to change them.
+
+I have integrated several changes made by BSDi for their BSD/386 operating
+system; these were offerred to me before I started consulting for them, so
+it is safe to say that they were intended for publication. Most notably,
+the name of the cron daemon has changed from "crond" to "cron". This was
+done for compatibility with 4.3BSD. Another change made for the same reason
+is the ability to read in an /etc/crontab file which has an extra field in
+each entry, between the time fields and the command. This field is a user
+name, and it permits the /etc/crontab command to contain commands which are
+to be run by any user on the system. /etc/crontab is not "installed" via
+the crontab(1) command; it is automatically read at startup time and it will
+be reread whenever it changes.
+
+I also added a "-e" option to crontab(1). Nine people also sent me diffs
+to add this option, but I had already implemented it on my own. I actually
+released an interrim version (V2.2, I think) for limited testing, and got a
+chance to fix a bad security bug in the "-e" option thanks to XXX.
+
+The daemon used to be extraordinarily sloppy in its use of file descriptors.
+A heck of a lot of them were left open in spawned jobs, which caused problems
+for the daemon and also caused problems with the spawned jobs if they were
+shell scripts since "sh" and "csh" have traditionally used hidden file
+descriptors to pass information to subshells, and cron was causing them to
+think they were subshells. If you had trouble with "sh" or "csh" scripts in
+V2, chances are good that V3 will fix your problems.
+
+About a dozen people have reminded me that I forgot to initialize
+"crontab_fd" in database.c. Keith Cantrell was the first, so he gets the
+point.
+
+Steve Simmons reminded me that once an account has been deleted from the
+system, "crontab -u USER -d" will not work. My solution is to suggest to
+all of you that before you delete a user's account, you first delete that
+user's crontab file if any. From cron's point of view, usernames can never
+be treated as arbitrary strings. Either they are valid user names, or they
+are not. I will not make an exception for the "-d" case, for security
+reasons that I consider reasonable. It is trivial for a root user to delete
+the entry by hand if necessary.
+
+Dan O'Neil reminded me that I forgot to reset "log_fd" in misc.c. A lot of
+others also reminded me of this, but Dan gets the point. I didn't fix it
+there, since the real bug was that it should have been open in the parent.
+
+Peter Kabal reminded me that I forgot to "#ifdef DEBUGGING" some code in
+misc.c. Hans Trompert actually told me first, but Peter sent the patch so
+he gets the point.
+
+Russell Nelson told me that I'd forgotten to "#include <syslog.h>" in misc.c,
+which explains why a lot of other people complained that it wasn't using
+syslog even when they configured it that way :-). Steve Simmons told me
+first, though, so he gets the point.
+
+An interrim version of the daemon tried to "stat" every file before
+executing it; this turned out to be a horribly bad idea since finding the
+name of a file from a shell command is a hard job (that's why we have
+shells, right?) I removed this bogus code. Dave Burgess gets the point.
+
+Dennis R. Conley sent a suggestion for MMDF systems, which I've added to the
+comments in cron.h.
+
+Mike Heisler noted that I use comments in the CONVERSION file which are
+documented as illegal in the man pages. Thanks, Mike.
+
+Irving Wolfe sent me some very cheerful changes for a NeXT system, but I
+consider the system itself broken and I can't bring myself to #ifdef for
+something as screwed up as this system seems to be. However, various others
+did send me smaller patches which appear to have cause cron to build and run
+correctly on (the latest) NeXT machines, with or without the "-posix" CFLAG.
+Irving also asked for a per-job MAILTO, and this was finally added later when
+I integrated the BSD/386 changes contributed by BSDi, and generalized some of
+the parsing.
+
+Lots of folks complained that the autogenerated "Date:" header wasn't in
+ARPA format. I didn't understand this -- either folks will use Sendmail and
+not generate a Date: at all (since Sendmail will do it), or folks will use
+something other than Sendmail which won't care about Date: formats. But
+I've "fixed" it anyway...
+
+Several people suggested that "*" should be able to take a "/step". One person
+suggested that "N/step" ought to mean "N-last/step", but that's stretching things
+a bit far. "*/step" seems quite intuitive to me, so I've added it. Colin Plumb
+sent in the first and most polite request for this feature.
+
+As with every release of Cron, BIND, and seemingly everything else I do, one
+user stands out with the most critical but also the most useful analysis.
+Cron V3's high score belongs to Peter Holzer, who sent in the nicest looking
+patch for the "%" interpretation problem and also helped me understand a
+tricky bit of badness in the "log_fd" problem.
+
+agulbra@flode.nvg.unit.no wins the honors for being the first to point out the
+nasty security hole in "crontab -r". 'Nuff said.
+
+Several folks pointed out that log_it() needed to exist even if logging was
+disabled. Some day I will create a tool that will compile a subsystem with
+every possible combination and permutation of #ifdef options, but meanwhile
+thanks to everybody.
+
+job_runqueue() was using storage after freeing it, since Jordan told me back
+in 1983 that C let you do that, and I believed him in 1986 when I wrote all
+this junk. Linux was the first to die from this error, and the Linux people
+sent me the most amazing, um, collection of patches for this problem. Thanks
+for all the fish.
+
+Jeremy Bettis reminded me that popen() isn't safe. I grabbed Ken Arnold's
+version of popen/pclose from the ftpd and hacked it to taste. We're safe now,
+from this at least.
+
+Branko Lankester sent me a very timely and helpful fix for a looming security
+problem in my "crontab -e" implementation.
+
+--------
+
+Vixie Cron Changes from V1 to V2
+Paul Vixie
+8-Feb-1988
+
+Many changes were made in a rash of activity about six months ago, the exact
+list of which is no longer clear in my memory. I know that V1 used a file
+called POKECRON in /usr/spool/cron to tell it that it was time to re-read
+all the crontab files; V2 uses the modtime the crontab directory as a flag to
+check out the crontab files; those whose modtime has changed will be re-read,
+and the others left alone. Note that the crontab(1) command will do a utimes
+call to make sure the mtime of the dir changes, since the filename/inode will
+often remain the same after a replacement and the mtime wouldn't change in
+that case.
+
+8-Feb-88: made it possible to use much larger environment variable strings.
+ V1 allowed 100 characters; V2 allows 1000. This was needed for PATH
+ variables on some systems. Thanks to Toerless Eckert for this idea.
+ E-mail: UUCP: ...pyramid!fauern!faui10!eckert
+
+16-Feb-88: added allow/deny, moved /usr/spool/cron/crontabs to
+ /usr/lib/cron/tabs. allow and deny are /usr/lib/cron/{allow,deny},
+ since the sysv naming for this depends on 'at' using the same
+ dir, which would be stupid (hint: use /usr/{lib,spool}/at).
+
+22-Feb-88: made it read the spool directory for crontabs and look each one
+ up using getpwnam() rather than reading all passwds with getpwent()
+ and trying to open each crontab.
+
+9-Dec-88: made it sync to :00 after the minute, makes cron predictable.
+ added logging to /var/cron/log.
+
+14-Apr-90: (actually, changes since December 1989)
+ fixed a number of bugs reported from the net and from John Gilmore.
+ added syslog per Keith Bostic. security features including not
+ being willing to run a command owned or writable by other than
+ the owner of the crontab 9not working well yet)
diff --git a/usr.sbin/cron/doc/CONVERSION b/usr.sbin/cron/doc/CONVERSION
new file mode 100644
index 0000000..f30df7d
--- /dev/null
+++ b/usr.sbin/cron/doc/CONVERSION
@@ -0,0 +1,85 @@
+$FreeBSD$
+
+Conversion of BSD 4.[23] crontab files:
+
+Edit your current crontab (/usr/lib/crontab) into little pieces, with each
+users' commands in a different file. This is different on 4.2 and 4.3,
+but I'll get to that below. The biggest feature of this cron is that you
+can move 'news' and 'uucp' cron commands into files owned and maintainable
+by those two users. You also get to rip all the fancy 'su' footwork out
+of the cron commands. On 4.3, there's no need for the 'su' stuff since the
+user name appears on each command -- but I'd still rather have separate
+crontabs with separate environments and so on.
+
+Leave the original /usr/lib/crontab! This cron doesn't use it, so you may
+as well keep it around for a while in case something goes wakko with this
+fancy version.
+
+Most commands in most crontabs are run by root, have to run by root, and
+should continue to be run by root. They still have to be in their own file;
+I recommend /etc/crontab.src or /usr/adm/crontab.src.
+
+'uucp's commands need their own file; how about /usr/lib/uucp/crontab.src?
+'news' also, perhaps in /usr/lib/news/crontab.src...
+
+I say `how about' and `perhaps' because it really doesn't matter to anyone
+(except you) where you put the crontab source files. The `crontab' command
+COPIES them into a protected directory (CRONDIR/SPOOL_DIR in cron.h), named
+after the user whose crontab it is. If you want to examine, replace, or
+delete a crontab, the `crontab' command does all of those things. The
+various `crontab.src' (my suggested name for them) files are just source
+files---they have to be copied to SPOOLDIR using `crontab' before they'll be
+executed.
+
+On 4.2, your crontab might have a few lines like this:
+
+ 5 * * * * su uucp < /usr/lib/uucp/uudemon.hr
+ 10 4 * * * su uucp < /usr/lib/uucp/uudemon.day
+ 15 5 * * 0 su uucp < /usr/lib/uucp/uudemon.wk
+
+...or like this:
+
+ 5 * * * * echo /usr/lib/uucp/uudemon.hr | su uucp
+ 10 4 * * * echo /usr/lib/uucp/uudemon.day | su uucp
+ 15 5 * * 0 echo /usr/lib/uucp/uudemon.wk | su uucp
+
+On 4.3, they'd look a little bit better, but not much:
+
+ 5 * * * * uucp /usr/lib/uucp/uudemon.hr
+ 10 4 * * * uucp /usr/lib/uucp/uudemon.day
+ 15 5 * * 0 uucp /usr/lib/uucp/uudemon.wk
+
+For this cron, you'd create /usr/lib/uucp/crontab.src (or wherever you want
+to keep uucp's commands) which would look like this:
+
+ # /usr/lib/uucp/crontab.src - uucp's crontab
+ #
+ PATH=/usr/lib/uucp:/bin:/usr/bin
+ SHELL=/bin/sh
+ HOME=/usr/lib/uucp
+ #
+ 5 * * * * uudemon.hr
+ 10 4 * * * uudemon.day
+ 15 5 * * 0 uudemon.wk
+
+The application to the `news' cron commands (if any) is left for you to
+figure out. Likewise if there are any other cruddy-looking 'su' commands in
+your crontab commands, you don't need them anymore: just find a good place
+to put the `crontab.src' (or whatever you want to call it) file for that
+user, put the cron commands into it, and install it using the `crontab'
+command (probably with "-u USERNAME", but see the man page).
+
+If you run a 4.2-derived cron, you could of course just install your current
+crontab in toto as root's crontab. It would work exactly the way your
+current one does, barring the extra steps in installing or changing it.
+There would still be advantages to this cron, mostly that you get mail if
+there is any output from your cron commands.
+
+One note about getting mail from cron: you will probably find, after you
+install this version of cron, that your cron commands are generating a lot
+of irritating output. The work-around for this is to redirect all EXPECTED
+output to a per-execution log file, which you can examine if you want to
+see the output from the "last time" a command was executed; if you get any
+UNEXPECTED output, it will be mailed to you. This takes a while to get
+right, but it's amazingly convenient. Trust me.
+
diff --git a/usr.sbin/cron/doc/FEATURES b/usr.sbin/cron/doc/FEATURES
new file mode 100644
index 0000000..821d1f3
--- /dev/null
+++ b/usr.sbin/cron/doc/FEATURES
@@ -0,0 +1,84 @@
+$FreeBSD$
+
+Features of Vixie's cron relative to BSD 4.[23] and SysV crons:
+
+-- Environment variables can be set in each crontab. SHELL, USER,
+ LOGNAME, and HOME are set from the user's passwd entry; all except
+ USER can be changed in the crontab. PATH is especially useful to
+ set there. TZ can be set, but cron ignores it other than passing
+ it on through to the commands it runs. Format is
+
+ variable=value
+
+ Blanks surrounding the '=' will be eaten; other blanks in value are
+ okay. Leading or trailing blanks can be preserved by quoting, single
+ or double quotes are okay, just so they match.
+
+ PATH=.:/bin:/usr/bin
+ SHELL=/bin/sh
+ FOOBAR = this is a long blanky example
+
+ Above, FOOBAR would get "this is a long blanky example" as its value.
+
+ SHELL and HOME will be used when it's time to run a command; if
+ you don't set them, HOME defaults to your /etc/passwd entry
+ and SHELL defaults to /bin/sh.
+
+ MAILTO, if set to the login name of a user on your system, will be the
+ person that cron mails the output of commands in that crontab. This is
+ useful if you decide on BINMAIL when configuring cron.h, since binmail
+ doesn't know anything about aliasing.
+
+-- Weekdays can be specified by name. Case is not significant, but only
+ the first three letters should be specified.
+
+-- Months can likewise be specified by name. Three letters only.
+
+-- Ranges and lists can be mixed. Standard crons won't allow '1,3-5'.
+
+-- Ranges can specify 'step' values. '10-16/2' is like '10,12,14,16'.
+
+-- Sunday is both day 0 and day 7 -- apparently BSD and ATT disagree
+ about this.
+
+-- Each user gets their own crontab file. This is a win over BSD 4.2,
+ where only root has one, and over BSD 4.3, where they made the crontab
+ format incompatible and although the commands can be run by non-root
+ uid's, root is still the only one who can edit the crontab file. This
+ feature mimics the SysV cron.
+
+-- The 'crontab' command is loosely compatible with SysV, but has more
+ options which just generally make more sense. Running crontab with
+ no arguments will print a cute little summary of the command syntax.
+
+-- Comments and blank lines are allowed in the crontab file. Comments
+ must be on a line by themselves; leading whitespace is ignored, and
+ a '#' introduces the comment.
+
+-- (big win) If the `crontab' command changes anything in any crontab,
+ the 'cron' daemon will reload all the tables before running the
+ next iteration. In some crons, you have to kill and restart the
+ daemon whenever you change a crontab. In other crons, the crontab
+ file is reread and reparsed every minute even if it didn't change.
+
+-- In order to support the automatic reload, the crontab files are not
+ readable or writable except by 'crontab' or 'cron'. This is not a
+ problem, since 'crontab' will let you do pretty much whatever you
+ want to your own crontab, or if you are root, to anybody's crontab.
+
+-- If any output is generated by a command (on stdout OR stderr), it will
+ be mailed to the owner of the crontab that contained the command (or
+ MAILTO, see discussion of environment variables, above). The headers
+ of the mail message will include the command that was run, and a
+ complete list of the environment that was passed to it, which will
+ contain (at least) the USER (LOGNAME on SysV), HOME, and SHELL.
+
+-- the dom/dow situation is odd. '* * 1,15 * Sun' will run on the
+ first and fifteenth AND every Sunday; '* * * * Sun' will run *only*
+ on Sundays; '* * 1,15 * *' will run *only* the 1st and 15th. this
+ is why we keep 'e->dow_star' and 'e->dom_star'. I didn't think up
+ this behaviour; it's how cron has always worked but the documentation
+ hasn't been very clear. I have been told that some AT&T crons do not
+ act this way and do the more reasonable thing, which is (IMHO) to "or"
+ the various field-matches together. In that sense this cron may not
+ be completely similar to some AT&T crons.
diff --git a/usr.sbin/cron/doc/INSTALL b/usr.sbin/cron/doc/INSTALL
new file mode 100644
index 0000000..7f4c997
--- /dev/null
+++ b/usr.sbin/cron/doc/INSTALL
@@ -0,0 +1,87 @@
+/* Copyright 1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ */
+
+$FreeBSD$
+
+Read the comments at the top of the Makefile, then edit the area marked
+'configurable stuff'.
+
+Edit config.h. The stuff I expect you to change is down a bit from the
+top of the file, but it's clearly marked. Also look at pathnames.h.
+
+You don't have to create the /var/cron or /var/cron/tabs directories, since
+both the daemon and the `crontab' program will do this the first time they
+run if they don't exist. You do need to have a /var, though -- just "mkdir
+/var" if you don't have one, or you can "mkdir /usr/var; ln -s /usr/var /var"
+if you expect your /var to have a lot of stuff in it.
+
+You will also need /usr/local/etc and /usr/local/bin directories unless you
+change the Makefile. These will have to be created by hand, but if you are
+a long-time Usenet user you probably have them already. /usr/local/man is
+where I keep my man pages, but I have the source for `man' and you probably
+do not. Therefore you may have to put the man pages into /usr/man/manl,
+which will be hard since there will be name collisions. (Note that the man
+command was originally written by Bill Joy before he left Berkeley, and it
+contains no AT&T code, so it is in UUNET's archive of freely-distributable
+BSD code.)
+
+LINUX note: /usr/include/paths.h on some linux systems shows _PATH_SENDMAIL
+ to be /usr/bin/sendmail even though sendmail is installed in /usr/lib.
+ you should check this out.
+
+say:
+ make all
+
+su and say:
+ make install
+
+Note that if I can get you to "su and say" something just by asking, you have
+a very serious security problem on your system and you should look into it.
+
+Edit your /usr/lib/crontab file into little pieces -- see the CONVERSION file
+for help on this.
+
+Use the `crontab' command to install all the little pieces you just created.
+Some examples (see below before trying any of these!)
+
+ crontab -u uucp -r /usr/lib/uucp/crontab.src
+ crontab -u news -r /usr/lib/news/crontab.src
+ crontab -u root -r /usr/adm/crontab.src
+
+Notes on above examples: (1) the .src files are copied at the time the
+command is issued; changing the source files later will have no effect until
+they are reinstalled with another `crontab -r' command. (2) The crontab
+command will affect the crontab of the person using the command unless `-u
+USER' is given; `-u' only works for root. When using most `su' commands
+under most BSD's, `crontab' will still think of you as yourself even though
+you may think of yourself as root -- so use `-u' liberally. (3) the `-r'
+option stands for `replace'; check the man page for crontab(1) for other
+possibilities.
+
+Kill your existing cron daemon -- do `ps aux' and look for /etc/cron.
+
+Edit your /etc/rc or /etc/rc.local, looking for the line that starts up
+/etc/cron. Comment it out and add a line to start the new cron daemon
+-- usually /usr/local/etc/cron, unless you changed it in the Makefile.
+
+Start up this cron daemon yourself as root. Just type /usr/local/etc/cron
+(or whatever); no '&' is needed since the daemon forks itself and the
+process you executed returns immediately.
+
+ATT notes: for those people unfortunate enough to be stuck on a AT&T UNIX,
+you will need the public-domain "libndir", found in the B News source and in
+any comp.sources.unix archive. You will also need to hack the code some.
diff --git a/usr.sbin/cron/doc/MAIL b/usr.sbin/cron/doc/MAIL
new file mode 100644
index 0000000..3549e94
--- /dev/null
+++ b/usr.sbin/cron/doc/MAIL
@@ -0,0 +1,475 @@
+[ this is really old mail that came to me in response to my 1986 posting
+ to usenet asking for feature suggestions before releasing the first
+ version of cron. it is presented here for its entertainment value.
+ --vix ]
+
+$FreeBSD$
+
+From ptsfa!lll-crg!ames!acornrc!bob Wed Dec 31 10:07:08 1986
+Date: Wed, 31 Dec 86 08:59:31 pst
+From: lll-crg!ames!acornrc!bob (Bob Weissman)
+To: ptsfa!vixie!paul
+Status: RO
+
+Sure, here's a suggestion: I'd like to be able to run a program, say,
+every two hours. Current cron requires me to write
+0,2,4,6,8,10,12,14,16,18,20,22 in the hours field. How about a notation
+to handle this more elegantly?
+
+<< Okay, I've allowed 0-22/2 as a means of handling this.
+ The time specification for my cron is as follows:
+ specification = range {"," range}
+ range = (start "-" finish ["/" step]) | single-unit
+ This allows "1,3,5-7", which the current cron doesn't (it won't
+ do a range inside a list), and handles your specific need. >>
+
+From drw@mit-eddie Wed Dec 31 18:25:27 1986
+Date: Wed, 31 Dec 86 14:28:19 est
+From: drw@mit-eddie (Dale Worley)
+To: mit-eddie!vixie!paul
+Status: RO
+
+We have a lot of lines in our crontab of the form
+
+ 00 12 * * * su user < /usr/users/user/script.file
+
+This barfs (silently!) on our system (Dec Ultrix 1.2 == 4.2bsd) if
+user's shell is csh. This, I am told, is because csh requires that
+the environment be set up in certain ways, which cron doesn't do.
+(Actually, I believe, it is because /etc/rc, which runs cron, doesn't
+set up the environment enough for csh to run, and cron just inherits
+the situation.) Anyway, the point is that if you find out what csh
+really needs in its environment, you might want to set up cron to
+provide some reasonable defaults (if it isn't supplied by cron's
+parent). Also, could you tell me what csh needs, if you find out, so
+we can hack our /etc/rc?
+
+<< well, the environment IS a problem. processes that cron forks
+ will inherit the environment of the person who ran the cron
+ daemon... I plan to edit out such useless things as TERMCAP,
+ TERM, and the like; supply correct values for HOME, USER, CWD,
+ and whatever else comes to mind. I'll make sure csh works... >>
+From ptsfa!ames!seismo!dgis!generous Thu Jan 1 07:33:17 1987
+Date: Thu Jan 1 10:29:20 1987
+From: ames!seismo!dgis!generous (Curtis Generous)
+To: nike!ptsfa!vixie!paul
+Status: RO
+
+Paul:
+
+One of the limitations of the present versions of cron is the lack
+of the capability of specifying a way to execute a command every
+n units of time.
+
+Here is a good example:
+
+# Present method to start up uucico
+02,12,22,32,42,52 * * * * exec /usr/lib/uucp/uucico -r1
+
+# New method ?? (the ':' here is just one possibility for syntax)
+02:10 * * * * exec /usr/lib/uucp/uucico -r1
+
+This method would prove very helpful for those programs that get started
+every few minutes, making the entry long and not easily readable. The first
+number would specify the base time, and the second number the repetition
+interval.
+
+<< Good idea, but bob@acornrc beat you to it. I used '/' instead of
+ ':'. This is my personal preference, and seems intuitive when you
+ think of the divide operator in C... Does anyone have a preference? >>
+
+From ptsfa!lll-lcc!seismo!decuac!c3pe!c3engr!charles Thu Jan 1 17:04:24 1987
+From: lll-lcc!seismo!c3pe!c3engr!charles (Charles Green)
+To: c3pe!decuac!dolqci!vrdxhq!seismo!lll-lcc!ptsfa!vixie!paul
+Date: Thu Jan 1 19:22:47 1987
+Status: RO
+
+Well, this isn't a compatible extension, but I have in times past wondered
+about a facility to let you start a process at intervals of, say, 17 minutes,
+instead of particular minutes out of each hour.
+
+<< This was a popular request! >>
+
+From seismo!uwvax!astroatc!nicmad!norvax!mann Sun Jan 4 13:04:01 1987
+Date: Fri, 2 Jan 87 09:23:53 cst
+From: lll-lcc!seismo!uwvax!astroatc!nicmad!norvax!mann (Tom Mann)
+To: ptsfa!vixie!paul
+Status: RO
+
+I'm not sure if it is in cron (either SysV or BSD ... if it is, I haven't
+figured it out ) but a comment feature would SURE BE NICE!.
+There are times when I want to comment out an entry
+for a period of time; it might also make it a lot more legible.
+
+<< My cron allows blank lines and standard #-type comments. I know
+ that one BSD4.2 cron I've used had it. I don't know about SysV. >>
+
+From ptsfa!hoptoad!hugh Mon Jan 5 10:26:46 1987
+Date: Mon, 5 Jan 87 01:22:17 PST
+From: hoptoad!hugh (Hugh Daniel)
+To: ptsfa!vixie!paul
+Status: RO
+
+ Hi, I do have a BIG one that I would like. I want to log ALL output
+from command lines into a file for each line. Thus I might have a chance
+of finding out why my crontab entry did not work.
+ This would seem to work best if done by cron, as it is now I have a google
+of shell scripts laying about just to put the error output where I can see
+it.
+
+<< My cron (and the SysV cron) will send mail to the owner of the
+ particular crontab file if a command generates any output on stdout
+ or stderr. This can be irritating, but if you write a script such
+ that any output means a problem occurred, you can avoid most logfile
+ needs, and not generate mail except in unforeseen circumstances. >>
+
+From ptsfa!dual!ucbvax!ihnp4!anvil!es!Robert_Toxen Mon Jan 5 13:08:46 1987
+From: dual!ucbvax!ihnp4!anvil!es!Robert_Toxen
+Date: Fri, 2 Jan 87 14:25:29 EST
+To: anvil!ihnp4!ucbvax!dual!ptsfa!vixie!paul
+Status: RO
+
+Here are some suggestions:
+1. Run it through the C preprocessor via "/lib/<whatever>".
+
+<< hmmm. this seems of limited utility, and if you really wanted
+ to do it that way, you could do it yourself (since users can
+ write to their own crontab files). I'll add '-' (read stdin)
+ to the crontab installer program to facilitate this. >>
+
+2. Allow specifying every Nth day of week, i.e., every second Wednesday.
+ I did this to calendar by separating the day of week (Wed=4, which one
+ to start on and N with slashes). I took modulo the day of year as a
+ starting point so that someone with a desk calendar documenting such
+ things can easily determine the offset (second number). I did this
+ while at SGI; alas I don't have a copy of the code.
+
+<< I can see how this could be useful, but I'm not sure how I'd
+ implement it. Cron currently doesn't keep track of the last time
+ a given command was run; whether the current Wednesday is the first
+ or second since the command was last run would be pretty hard to
+ figure out. I'd have to keep a database of commands and their
+ execution around, and purge it when the crontab was overwritten.
+ This is too much work for me, but if someone adds it, let me know. >>
+
+From ptsfa!ames!seismo!cbmvax!devon!paul Tue Jan 6 05:50:17 1987
+From: ames!seismo!cbmvax!devon!paul
+To: cbmvax!seismo!nike!ptsfa!vixie!paul
+Date: Mon Jan 5 09:29:57 1987
+Status: RO
+
+One problem that has always plagued me with cron is the assumed ORing.
+I'd like to see some type of ANDing implemented. I guess I can best
+describe this by example. Say I have the following line in my crontab
+file:
+
+* * 4-31 * 1-6 /usr/bin/command
+
+What this does is run 'command' on the 4th thru 31st days of the
+month, AND on Monday thru Saturday; which probably means running it
+every day of the month (unless Sunday falls on days 1-3). This
+happens because cron runs the command if the day-of-month OR the
+day-of-week is true.
+
+What I'd like to happen with the above line is to run the command ONLY
+on Monday thru Saturday any time after the 3rd of the month, e.g. if
+the day-of-month AND the day-of-week are true.
+
+My proposal to you is to implement some special chars for the first
+five fields. Examples:
+
+* * !1-3 * 1-6 /usr/bin/command
+
+(run command Mon-Sat, but NOT [!] on the first 3 days of the month)
+
+* * &4-31 * &1-6 /usr/bin/command
+
+(run command if day-of-month AND day-of-week are true)
+
+Get the picture? This would be compatable with existing versions of
+cron (which wouldn't currently be using any special characters, so
+that old crontabs would be handled correctly).
+
+<< This message made me aware of the actual boolean expression involved
+ in a crontab entry. I'd assumed that it was
+ (minute && hour && DoM && month && DoW)
+ But it's really
+ (minute && hour && month && (DoM || DoW))
+
+ I can see some value in changing this, but with a fixed order of
+ fields, operators get to be kindof unary, which && and || really
+ aren't. If someone has an idea on a syntax that allows useful
+ variations to the standard (&& && && (||)) default, please suggest. >>
+
+From bobkat!pedz Tue Jan 6 20:02:10 1987
+From: pedz@bobkat.UUCP (Pedz Thing)
+Date: 2 Jan 87 17:34:44 GMT
+Status: RO
+
+Log files! It would be nice to be able to specify a log for cron
+itself and also a log for each program's stdout and stderr to go to.
+The latter can of course be done with > and 2> but it would be nice if
+there could be a single line with some sort of pattern like
+`> /usr/spool/log/%' and the command would be substituted for the %.
+Another thing which would be nice is to be able to specify which shell
+to call to give the command to.
+
+<< Log files are done with mail. The '%' idea could be useful if
+ a different character were used (% is special to cron, see man
+ page); a different directory would have to be chosen, since each
+ user has their own crontab file; and something intelligent would
+ have to be done in the file naming, since the first word of the
+ command might be ambiguous (with other commands). In short, it's
+ too much work. Sorry. >>
+
+From guy%gorodish@sun Tue Jan 6 20:03:13 1987
+From: guy%gorodish@sun (Guy Harris)
+Message-ID: <10944@sun.uucp>
+Date: 5 Jan 87 12:09:09 GMT
+References: <429@vixie.UUCP> <359@bobkat.UUCP>
+Sender: news@sun.uucp
+Status: RO
+
+> Another thing which would be nice is to be able to specify which shell
+> to call to give the command to.
+
+Well, the obvious choice would be the user's shell, but this wouldn't work
+for accounts like "uucico".
+
+<< I use the owning user's shell, and to handle "uucico" I check a
+ list of "acceptable shells" (currently compiled in, does anybody
+ mind?), substituting a default (compiled in) shell if the user's
+ shell isn't on the list.
+
+ BTW, "compiled in" means that it's in a .h file, easily changed
+ during installation, but requiring recompilation to modify. You
+ don't have to go digging through the code to find it... >>
+
+From qantel!hplabs!ucbvax!mwm@violet.berkeley.edu Tue Jan 6 21:24:48 1987
+To: hplabs!qantel!vixie!paul (Paul Vixie Esq)
+Date: 04 Jan 87 00:42:35 PST (Sun)
+From: Mike Meyer <mwm@violet.berkeley.edu>
+Status: RO
+
+<<[Discussion of RMS/FSF, and mwm's GNU Cron deleted]>>
+
+Oh, yeah - here are the extensions on my cron:
+
+1) Sunday is both day 0 and day 7, so it complies with both SysV and
+BSD cron.
+
+<< Good idea. I did it too, thanks for informing me. >>
+
+2) At is integrated into the cron. Instead of atrun to scan the
+/usr/spool/at directory, at files are put into the /usr/lib/cron
+directory along with users cron files, and cron fabricates a line from
+a crontab file to run them. This is considered a major win by all who
+use it.
+
+<< I don't use 'at', and my cron doesn't do anything with it. To run
+ 'at', I use 'atrun' the same way the current BSD cron does. My
+ crontab files are in /usr/spool/cron/crontabs, in the SysV
+ tradition -- not in /usr/lib/cron. This is a configuration
+ parameter, of course. >>
+
+There are two known restrictions:
+
+1) I don't support any of the SysV security hooks. I don't have a use
+for them, and RMS didn't like the idea at all :-).
+
+<< This means cron.allow and cron.deny. I plan to support them, as
+ they've been quite helpful at various HPUX sites I've administered. >>
+
+2) Cron expects to be able to create files with names longer than 14
+characters, which makes it hard to run on SysV. At least one person
+was working on a port, but I don't know how it's going. That might
+make for a good reason for releasing yours, right there.
+
+<< If someone has SysV (with the 14-character limit), they probably
+ won't want my cron, since it doesn't add much to the standard
+ version (which they may have support for). My cron is not currently
+ portable to non-BSD systems, since it relies on interval timers (I
+ needed to sleep for intervals more granular than seconds alone would
+ allow). The port would be trivial, and I will do it if a lot of
+ people ask for it... >>
+
+Oh, yeah - I'm going to see about getting this cron integrated into
+the next 4BSD release.
+
+<< How does one go about this? I have a few nifty gadgets I'd like
+ to contribute, this cron being one of them... >>
+
+<<[more FSF/GNU discussion deleted]>>
+
+From qantel!hplabs!ames!ut-sally!ut-ngp!melpad!bigtex!james Tue Jan 6 21:24:57 1987
+Posted-Date: Fri, 2 Jan 87 19:26:16 est
+Date: Fri, 2 Jan 87 19:26:16 est
+From: hplabs!ames!ut-sally!ut-ngp!bigtex!james
+To: vixie!paul
+Status: RO
+
+Yes!!! There are several critical failures in System V cron...
+
+1. Pass all variables in cron's environment into the environment of things
+ cron starts up, or at least into the crontab entries started up (at jobs
+ will inherit the environment of the user). If nothing else it is critically
+ important that the TZ variable be passed on. PATH should be passed on too.
+ Basically, passing environment values allows one to design a standard
+ environment with TZ and PATH and have that run by everything. If anyone
+ tells you this is no big deal, consider what happens when uucico is
+ started by cron in CA to make a long distance phone link... Unless the
+ administrator is really on his/her toes, calls scheduled at 5pm will really
+ go at two in the afternoon, needlessly incurring huge phone bills, all
+ because System V refuses to pass the TZ from its environment down. There
+ are work arounds, but only putting it in cron will really work. This is
+ not a security hole.
+
+<< delete TERM and TERMCAP; modify HOME, USER, and CWD; pass TZ and
+ PATH through undisturbed. any other requests out there?
+
+ BSD doesn't have this problem -- TZ is passed right on through if
+ you define it in the shell before starting my cron daemon. However,
+ the BSD I'm running this on doesn't need TZ to be defined anyway...
+ The default in the kernel has been just fine so far... But just the
+ same, if/when I port to SysV (I guess I really should), I'll make
+ sure this works right.
+
+ I guess I've been spoiled. HPUX is SysV-based, and I never had a
+ problem with cron and TZ when I used it. >>
+
+2. A way to avoid logging stuff in /usr/lib/cron/log. I have a cron entry
+ run uudemon.hr every 10 minutes. This is 144 times/day. Each run generates
+ three lines of text, for a total of 432 lines of text I don't want to see.
+ Obviously this should be optional, but it would be nice if there were a
+ way to flag an entry so that it wasn't logged at all unless there was an
+ error.
+
+<< I don't know nothin' 'bout no /usr/lib/cron/log. What is this file?
+ I don't see any reason to create log entries, given the mail-the-
+ output behaviour. Opinions, anyone? >>
+
+I will come up with other ideas no doubt, but I can always implement them
+myself.
+
+<< That's what I like about PD software. Please send me the diffs! >>
+
+The other problem you have is making sure you can run standard
+crontabs. I would suggest something like this: if the command part of the
+entry starts with an unescaped -, then flags and options follow immediately
+thereafter. As in:
+
+2,12,22,32,42,52 * * * * -q /usr/lib/uucp/uudemon.hr
+
+This could mean do not log the uudemon.hr run unless there is a problem of
+some kind. This is probably safe as not many filenames start with "-", and
+those that do are already a problem for people.
+
+<< Since I don't plan on supporting /usr/lib/cron/log in ANY form unless
+ many people request it, I won't be needing -q as you've defined it.
+ I could use something like this to avoid sending mail on errors, for
+ the occasional script that you don't want to bullet-proof.
+
+ The compatibility issue is CRITICAL. The 4.3BSD crontab format is
+ a crime against the whole philosophy of Unix(TM), in my opinion. >>
+
+One other minor thing to consider is the ulimit: can different users get
+different ulimits for their crontab entries?
+
+<< Boy I'm ignorant today. What's a ulimit, and what should I do with
+ it in a crontab? Suggestions, enlightenment, etc ?? >>
+
+From qantel!lll-crg!ames!uw-beaver!uw-nsr!john Tue Jan 6 23:32:44 1987
+Date: Thu, 1 Jan 87 10:53:05 pst
+From: lll-crg!ames!uw-beaver!uw-nsr!john (John Sambrook 5-7433)
+To: vixie!paul
+Status: RO
+
+How about not hardwiring the default environment that cron builds for its
+children in the cron program itself? Our cron does this and it's the pits
+because we are TZ=PST8PDT not TZ=EST5EDT !
+
+<< yeachk. I assure you, I will not hardwire the TZ! >>
+From ptsfa!well!dv Fri Jan 9 04:01:50 1987
+Date: Thu, 8 Jan 87 23:50:40 pst
+From: well!dv (David W. Vezie)
+To: ptsfa!vixie!paul
+Status: RO
+
+6, have a special notation called 'H' which would expand to weekends
+ and holidays (you'd have to keep a database somewhere of real
+ holidays), and also 'W' for workdays (neither weekend or holiday).
+
+<< Too much work. There should be a standard way to define and
+ detect holidays under Unix(TM); if there were, I'd use it. As
+ it is, I'll leave this for someone else to add.
+
+ I can see the usefulness; it just doesn't quite seem worth it. >>
+From qantel!gatech!akgua!blnt1!jat Wed Jan 14 20:00:40 1987
+Date: Tue, 13 Jan 87 16:39:38 EST
+From: gatech!akgua!blnt1!jat
+Status: RO
+
+1) Add some way to tell cron to reread the files, say kill -1 <pid>
+
+<< whenever the 'crontab' program is run and updates a crontab file,
+ a file /usr/spool/cron/POKECRON is created; next time the cron
+ daemon wakes up, it sees it, and re-reads the crontab files.
+
+ I thought of handling the signal; even implemented it. Then this
+ clever idea hit me and I ripped it all out and added a single
+ IF-statement to handle the POKECRON file. >>
+
+2) Have some kind of retry time so that if a command fails, cron will try to
+ execute it again after a certain period. This is useful if you have some
+ type of cleanup program that can run at the scheduled time for some reason
+ (such as locked device, unmounted filesystem, etc).
+
+<< Hmmm, sounds useful. I could do this by submitting an 'at' job...
+ I'll think about it. >>
+From ptsfa!dual!ucbvax!ihnp4!mtuxo!ender Sat Jan 3 16:54:00 1987
+From: dual!ucbvax!ihnp4!mtuxo!ender
+Date: Sat, 3 Jan 87 14:05:13 PST
+To: ucbvax!dual!ptsfa!vixie!paul
+Status: RO
+
+It would be nice if nonprivileged users can setup personal crontab files
+(~/.cronrc, say) and be able to run personal jobs at regular intervals.
+
+<< this is done, but in the SysV style: the 'crontab' program installs
+ a new crontab file for the executing user (can be overridden by root
+ for setup of uucp and news). the advantage of this is that (1) when
+ a crontab is changed, the daemon can be informed automatically; and
+ (2) the file can be syntax-checked before installation. >>
+From ptsfa!ames!seismo!ihnp4!lcc!richard Fri Jan 16 04:47:33 1987
+Date: Fri, 16 Jan 87 07:44:57 EST
+To: nike!ptsfa!vixie!paul
+Status: RO
+
+The System V cron is nice, but it has a few annoying features. One is that
+its mail files will say that the previous message is the output of "one of your
+cron commands." I wish it would say WHICH cron commmand.
+
+<< Done. Also which shell, which user (useful when the mail gets
+ forwarded), which home directory, and other useful crud. >>
+
+Another problem is with timezones. It is necessary to specify TZ=PST8PDT (or
+whatever) when you invoke cron (from inittab, or /etc/rc) and it is also
+necessary to add TZ=PST8PDT to each crontab line which might need it. Cron
+should automatically export its idea of the "TZ" to each invoked command, and
+it should be possible to put a line in the crontab file which overrides that
+for every command in the file (e.g., most users are on EST, so cron is run
+with TZ=EST5EDT; but one user is usually on PST and wants all of his cron
+commands to run with TZ=PST8PDT). This might be extended to allow any
+environment variable to be specified once for the whole crontab file (e.g.,
+PATH).
+
+<< Well, since I run the user's shell, you could put this into .cshrc.
+ generic environment-variable setting could be useful, though. Since
+ I have to modify the environment anyway, I'll consider this. >>
+
+A log file might be a nice idea, but the System V cron log is too verbose.
+I seem to remember that cron keeps it open, too; so you can't even have
+something go and periodically clean it out.
+
+<< I don't do /usr/lib/cron/log. I wasn't aware of this file until I
+ got all these suggestions. Do people want this file? Tell me! >>
diff --git a/usr.sbin/cron/doc/Makefile.vixie b/usr.sbin/cron/doc/Makefile.vixie
new file mode 100644
index 0000000..8df1301
--- /dev/null
+++ b/usr.sbin/cron/doc/Makefile.vixie
@@ -0,0 +1,128 @@
+#/* Copyright 1988,1990,1993,1994 by Paul Vixie
+# * All rights reserved
+# *
+# * Distribute freely, except: don't remove my name from the source or
+# * documentation (don't take credit for my work), mark your changes (don't
+# * get me blamed for your possible bugs), don't alter or remove this
+# * notice. May be sold if buildable source is provided to buyer. No
+# * warrantee of any kind, express or implied, is included with this
+# * software; use at your own risk, responsibility for damages (if any) to
+# * anyone resulting from the use of this software rests entirely with the
+# * user.
+# *
+# * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+# * I'll try to keep a version up to date. I can be reached as follows:
+# * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+# */
+
+# Makefile for vixie's cron
+#
+# $FreeBSD$
+#
+# vix 03mar88 [moved to RCS, rest of log is in there]
+# vix 30mar87 [goodbye, time.c; hello, getopt]
+# vix 12feb87 [cleanup for distribution]
+# vix 30dec86 [written]
+
+# NOTES:
+# 'make' can be done by anyone
+# 'make install' must be done by root
+#
+# this package needs getopt(3), bitstring(3), and BSD install(8).
+#
+# the configurable stuff in this makefile consists of compilation
+# options (use -O, cron runs forever) and destination directories.
+# SHELL is for the 'augumented make' systems where 'make' imports
+# SHELL from the environment and then uses it to run its commands.
+# if your environment SHELL variable is /bin/csh, make goes real
+# slow and sometimes does the wrong thing.
+#
+# this package needs the 'bitstring macros' library, which is
+# available from me or from the comp.sources.unix archive. if you
+# put 'bitstring.h' in a non-standard place (i.e., not intuited by
+# cc(1)), you will have to define INCLUDE to set the include
+# directory for cc. INCLUDE should be `-Isomethingorother'.
+#
+# there's more configuration info in config.h; edit that first!
+
+#################################### begin configurable stuff
+#<<DESTROOT is assumed to have ./etc, ./bin, and ./man subdirectories>>
+DESTROOT = $(DESTDIR)/usr
+DESTSBIN = $(DESTROOT)/sbin
+DESTBIN = $(DESTROOT)/bin
+DESTMAN = $(DESTROOT)/share/man
+#<<need bitstring.h>>
+INCLUDE = -I.
+#INCLUDE =
+#<<need getopt()>>
+LIBS =
+#<<optimize or debug?>>
+#OPTIM = -O
+OPTIM = -g
+#<<ATT or BSD or POSIX?>>
+# (ATT untested)
+#COMPAT = -DATT
+#(BSD is only needed if <sys/params.h> does not define it, as on ULTRIX)
+#COMPAT = -DBSD
+# (POSIX)
+#COMPAT = -DPOSIX
+#<<lint flags of choice?>>
+LINTFLAGS = -hbxa $(INCLUDE) $(COMPAT) $(DEBUGGING)
+#<<want to use a nonstandard CC?>>
+#CC = vcc
+#<<manifest defines>>
+DEFS =
+#(SGI IRIX systems need this)
+#DEFS = -D_BSD_SIGNALS -Dconst=
+#<<the name of the BSD-like install program>>
+#INSTALL = installbsd
+INSTALL = install
+#<<any special load flags>>
+LDFLAGS =
+#################################### end configurable stuff
+
+SHELL = /bin/sh
+CFLAGS = $(OPTIM) $(INCLUDE) $(COMPAT) $(DEFS)
+
+INFOS = README CHANGES FEATURES INSTALL CONVERSION THANKS MAIL
+MANPAGES = bitstring.3 crontab.5 crontab.1 cron.8 putman.sh
+HEADERS = bitstring.h cron.h config.h pathnames.h \
+ externs.h compat.h
+SOURCES = cron.c crontab.c database.c do_command.c entry.c \
+ env.c job.c user.c popen.c misc.c compat.c
+SHAR_SOURCE = $(INFOS) $(MANPAGES) Makefile $(HEADERS) $(SOURCES)
+LINT_CRON = cron.c database.c user.c entry.c compat.c \
+ misc.c job.c do_command.c env.c popen.c
+LINT_CRONTAB = crontab.c misc.c entry.c env.c compat.c
+CRON_OBJ = cron.o database.o user.o entry.o job.o do_command.o \
+ misc.o env.o popen.o compat.o
+CRONTAB_OBJ = crontab.o misc.o entry.o env.o compat.o
+
+all : cron crontab
+
+lint :
+ lint $(LINTFLAGS) $(LINT_CRON) $(LIBS) \
+ |grep -v "constant argument to NOT" 2>&1
+ lint $(LINTFLAGS) $(LINT_CRONTAB) $(LIBS) \
+ |grep -v "constant argument to NOT" 2>&1
+
+cron : $(CRON_OBJ)
+ $(CC) $(LDFLAGS) -o cron $(CRON_OBJ) $(LIBS)
+
+crontab : $(CRONTAB_OBJ)
+ $(CC) $(LDFLAGS) -o crontab $(CRONTAB_OBJ) $(LIBS)
+
+install : all
+ $(INSTALL) -c -m 111 -o root -s cron $(DESTSBIN)/
+ $(INSTALL) -c -m 4111 -o root -s crontab $(DESTBIN)/
+ sh putman.sh crontab.1 $(DESTMAN)
+ sh putman.sh cron.8 $(DESTMAN)
+ sh putman.sh crontab.5 $(DESTMAN)
+
+clean :; rm -f *.o cron crontab a.out core tags *~ #*
+
+kit : $(SHAR_SOURCE)
+ makekit -m -s99k $(SHAR_SOURCE)
+
+$(CRON_OBJ) : cron.h compat.h config.h externs.h pathnames.h Makefile
+$(CRONTAB_OBJ) : cron.h compat.h config.h externs.h pathnames.h Makefile
diff --git a/usr.sbin/cron/doc/README b/usr.sbin/cron/doc/README
new file mode 100644
index 0000000..eaced9e
--- /dev/null
+++ b/usr.sbin/cron/doc/README
@@ -0,0 +1,72 @@
+#/* Copyright 1988,1990,1993 by Paul Vixie
+# * All rights reserved
+# *
+# * Distribute freely, except: don't remove my name from the source or
+# * documentation (don't take credit for my work), mark your changes (don't
+# * get me blamed for your possible bugs), don't alter or remove this
+# * notice. May be sold if buildable source is provided to buyer. No
+# * warrantee of any kind, express or implied, is included with this
+# * software; use at your own risk, responsibility for damages (if any) to
+# * anyone resulting from the use of this software rests entirely with the
+# * user.
+# *
+# * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+# * I'll try to keep a version up to date. I can be reached as follows:
+# * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+# */
+
+Vixie Cron V3.0
+December 27, 1993
+[V2.2 was some time in 1992]
+[V2.1 was May 29, 1991]
+[V2.0 was July 5, 1990]
+[V2.0-beta was December 9, 1988]
+[V1.0 was May 6, 1987]
+Paul Vixie
+
+This is a version of 'cron' that is known to run on BSD 4.[23] systems. It
+is functionally based on the SysV cron, which means that each user can have
+their own crontab file (all crontab files are stored in a read-protected
+directory, usually /var/cron/tabs). No direct support is provided for
+'at'; you can continue to run 'atrun' from the crontab as you have been
+doing. If you don't have atrun (i.e., System V) you are in trouble.
+
+A messages is logged each time a command is executed; also, the files
+"allow" and "deny" in /var/cron can be used to control access to the
+"crontab" command (which installs crontabs). It hasn't been tested on
+SysV, although some effort has gone into making the port an easy one.
+
+This is more or less the copyright that USENET contributed software usually
+has. Since ATT couldn't use this version if they had to freely distribute
+source, and since I'd love to see them use it, I'll offer some rediculously
+low license fee just to have them take it. In the unlikely event that they
+do this, I will continue to support and distribute the pseudo-PD version, so
+please, don't flame me for wanting my work to see a wider distribution.
+
+To use this: Sorry, folks, there is no cutesy 'Configure' script. You'll
+have to go edit a couple of files... So, here's the checklist:
+
+ Read all the FEATURES, INSTALL, and CONVERSION files
+ Edit config.h
+ Edit Makefile
+ (both of these files have instructions inside; note that
+ some things in config.h are definable in Makefile and are
+ therefore surrounded by #ifndef...#endif)
+ 'make'
+ 'su' and 'make install'
+ (you may have to install the man pages by hand)
+ kill your existing cron process
+ (actually you can run your existing cron if you want, but why?)
+ build new crontabs using /usr/lib/{crontab,crontab.local}
+ (either put them all in "root"'s crontab, or divide it up
+ and rip out all the 'su' commands, collapse the lengthy
+ lists into ranges with steps -- basically, this step is
+ as much work as you want to make it)
+ start up the new cron
+ (must be done as root)
+ watch it. test it with 'crontab -r' and watch the daemon track your
+ changes.
+ if you like it, change your /etc/{rc,rc.local} to use it instead of
+ the old one.
+
+$FreeBSD$
diff --git a/usr.sbin/cron/doc/README.1ST b/usr.sbin/cron/doc/README.1ST
new file mode 100644
index 0000000..66cd0b2
--- /dev/null
+++ b/usr.sbin/cron/doc/README.1ST
@@ -0,0 +1,4 @@
+Sources substantially rearranged 26 Aug 94, Jordan Hubbard. Content
+remains faithful to the original 3.0 cron source from Paul Vixie, but
+any bugs introduced by this reorganization or the port to FreeBSD remain
+purely my own. - Jordan
diff --git a/usr.sbin/cron/doc/THANKS b/usr.sbin/cron/doc/THANKS
new file mode 100644
index 0000000..3787c29
--- /dev/null
+++ b/usr.sbin/cron/doc/THANKS
@@ -0,0 +1,29 @@
+15 January 1990
+Paul Vixie
+
+Many people have contributed to cron. Many more than I can remember, in fact.
+Rich Salz and Carl Gutekunst were each of enormous help to me in V1; Carl for
+helping me understand UNIX well enough to write it, and Rich for helping me
+get the features right.
+
+John Gilmore wrote me a wonderful review of V2, which took me a whole year to
+answer even though it made me clean up some really awful things in the code.
+(According to John the most awful things are still in here, of course.)
+
+Paul Close made a suggestion which led to /etc/crond.pid and the mutex locking
+on it. Kevin Braunsdorf of Purdue made a suggestion that led to @reboot and
+its brothers and sisters; he also sent some diffs that lead cron toward compil-
+ability with System V, though without at(1) capabilities, this cron isn't going
+to be that useful on System V. Bob Alverson fixed a silly bug in the line
+number counting. Brian Reid made suggestions which led to the run queue and
+the source-file labelling in installed crontabs.
+
+Scott Narveson ported V2 to a Sequent, and sent in the most useful single batch
+of diffs I got from anybody. Changes attributable to Scott are:
+ -> sendmail won't time out if the command is slow to generate output
+ -> day-of-week names aren't off by one anymore
+ -> crontab says the right thing if you do something you shouldn't do
+ -> crontab(5) man page is longer and more informative
+ -> misc changes related to the side effects of fclose()
+ -> Sequent "universe" support added (may also help on Pyramids)
+ -> null pw_shell is dealt with now; default is /bin/sh
diff --git a/usr.sbin/cron/lib/Makefile b/usr.sbin/cron/lib/Makefile
new file mode 100644
index 0000000..ed14dfc
--- /dev/null
+++ b/usr.sbin/cron/lib/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+LIB= cron
+INTERNALLIB= YES
+SRCS= entry.c env.c misc.c
+
+CFLAGS+= -I${.CURDIR}/../cron
+CFLAGS+= -DLOGIN_CAP
+
+.include <bsd.lib.mk>
diff --git a/usr.sbin/cron/lib/compat.c b/usr.sbin/cron/lib/compat.c
new file mode 100644
index 0000000..9686012
--- /dev/null
+++ b/usr.sbin/cron/lib/compat.c
@@ -0,0 +1,237 @@
+/* Copyright 1988,1990,1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ */
+
+#if !defined(lint) && !defined(LINT)
+static char rcsid[] = "$FreeBSD$";
+#endif
+
+/* vix 30dec93 [broke this out of misc.c - see RCS log for history]
+ * vix 15jan87 [added TIOCNOTTY, thanks csg@pyramid]
+ */
+
+
+#include "cron.h"
+#ifdef NEED_GETDTABLESIZE
+# include <limits.h>
+#endif
+#if defined(NEED_SETSID) && defined(BSD)
+# include <sys/ioctl.h>
+#endif
+#include <errno.h>
+#include <paths.h>
+
+
+/* the code does not depend on any of vfork's
+ * side-effects; it just uses it as a quick
+ * fork-and-exec.
+ */
+#ifdef NEED_VFORK
+PID_T
+vfork() {
+ return (fork());
+}
+#endif
+
+
+#ifdef NEED_STRDUP
+char *
+strdup(str)
+ char *str;
+{
+ char *temp;
+
+ if ((temp = malloc(strlen(str) + 1)) == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ (void) strcpy(temp, str);
+ return temp;
+}
+#endif
+
+
+#ifdef NEED_STRERROR
+char *
+strerror(error)
+ int error;
+{
+ extern char *sys_errlist[];
+ extern int sys_nerr;
+ static char buf[32];
+
+ if ((error <= sys_nerr) && (error > 0)) {
+ return sys_errlist[error];
+ }
+
+ sprintf(buf, "Unknown error: %d", error);
+ return buf;
+}
+#endif
+
+
+#ifdef NEED_STRCASECMP
+int
+strcasecmp(left, right)
+ char *left;
+ char *right;
+{
+ while (*left && (MkLower(*left) == MkLower(*right))) {
+ left++;
+ right++;
+ }
+ return MkLower(*left) - MkLower(*right);
+}
+#endif
+
+
+#ifdef NEED_SETSID
+int
+setsid()
+{
+ int newpgrp;
+# if defined(BSD)
+ int fd;
+# if defined(POSIX)
+ newpgrp = setpgid((pid_t)0, getpid());
+# else
+ newpgrp = setpgrp(0, getpid());
+# endif
+ if ((fd = open(_PATH_TTY, 2)) >= 0)
+ {
+ (void) ioctl(fd, TIOCNOTTY, (char*)0);
+ (void) close(fd);
+ }
+# else /*BSD*/
+ newpgrp = setpgrp();
+
+ (void) close(STDIN); (void) open(_PATH_DEVNULL, 0);
+ (void) close(STDOUT); (void) open(_PATH_DEVNULL, 1);
+ (void) close(STDERR); (void) open(_PATH_DEVNULL, 2);
+# endif /*BSD*/
+ return newpgrp;
+}
+#endif /*NEED_SETSID*/
+
+
+#ifdef NEED_GETDTABLESIZE
+int
+getdtablesize() {
+#ifdef _SC_OPEN_MAX
+ return sysconf(_SC_OPEN_MAX);
+#else
+ return _POSIX_OPEN_MAX;
+#endif
+}
+#endif
+
+
+#ifdef NEED_FLOCK
+/* The following flock() emulation snarfed intact *) from the HP-UX
+ * "BSD to HP-UX porting tricks" maintained by
+ * system@alchemy.chem.utoronto.ca (System Admin (Mike Peterson))
+ * from the version "last updated: 11-Jan-1993"
+ * Snarfage done by Jarkko Hietaniemi <Jarkko.Hietaniemi@hut.fi>
+ * *) well, almost, had to K&R the function entry, HPUX "cc"
+ * does not grok ANSI function prototypes */
+
+/*
+ * flock (fd, operation)
+ *
+ * This routine performs some file locking like the BSD 'flock'
+ * on the object described by the int file descriptor 'fd',
+ * which must already be open.
+ *
+ * The operations that are available are:
+ *
+ * LOCK_SH - get a shared lock.
+ * LOCK_EX - get an exclusive lock.
+ * LOCK_NB - don't block (must be ORed with LOCK_SH or LOCK_EX).
+ * LOCK_UN - release a lock.
+ *
+ * Return value: 0 if lock successful, -1 if failed.
+ *
+ * Note that whether the locks are enforced or advisory is
+ * controlled by the presence or absence of the SETGID bit on
+ * the executable.
+ *
+ * Note that there is no difference between shared and exclusive
+ * locks, since the 'lockf' system call in SYSV doesn't make any
+ * distinction.
+ *
+ * The file "<sys/file.h>" should be modified to contain the definitions
+ * of the available operations, which must be added manually (see below
+ * for the values).
+ */
+
+/* this code has been reformatted by vixie */
+
+int
+flock(fd, operation)
+ int fd;
+ int operation;
+{
+ int i;
+
+ switch (operation) {
+ case LOCK_SH: /* get a shared lock */
+ case LOCK_EX: /* get an exclusive lock */
+ i = lockf (fd, F_LOCK, 0);
+ break;
+
+ case LOCK_SH|LOCK_NB: /* get a non-blocking shared lock */
+ case LOCK_EX|LOCK_NB: /* get a non-blocking exclusive lock */
+ i = lockf (fd, F_TLOCK, 0);
+ if (i == -1)
+ if ((errno == EAGAIN) || (errno == EACCES))
+ errno = EWOULDBLOCK;
+ break;
+
+ case LOCK_UN: /* unlock */
+ i = lockf (fd, F_ULOCK, 0);
+ break;
+
+ default: /* can't decipher operation */
+ i = -1;
+ errno = EINVAL;
+ break;
+ }
+
+ return (i);
+}
+#endif /*NEED_FLOCK*/
+
+
+#ifdef NEED_SETENV
+int
+setenv(name, value, overwrite)
+ char *name, *value;
+ int overwrite;
+{
+ char *tmp;
+
+ if (overwrite && getenv(name))
+ return -1;
+
+ if (!(tmp = malloc(strlen(name) + strlen(value) + 2))) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ sprintf(tmp, "%s=%s", name, value);
+ return putenv(tmp); /* intentionally orphan 'tmp' storage */
+}
+#endif
diff --git a/usr.sbin/cron/lib/entry.c b/usr.sbin/cron/lib/entry.c
new file mode 100644
index 0000000..2b886ee
--- /dev/null
+++ b/usr.sbin/cron/lib/entry.c
@@ -0,0 +1,636 @@
+/* Copyright 1988,1990,1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ */
+
+#if !defined(lint) && !defined(LINT)
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif
+
+/* vix 26jan87 [RCS'd; rest of log is in RCS file]
+ * vix 01jan87 [added line-level error recovery]
+ * vix 31dec86 [added /step to the from-to range, per bob@acornrc]
+ * vix 30dec86 [written]
+ */
+
+
+#include "cron.h"
+#include <grp.h>
+#ifdef LOGIN_CAP
+#include <login_cap.h>
+#endif
+
+typedef enum ecode {
+ e_none, e_minute, e_hour, e_dom, e_month, e_dow,
+ e_cmd, e_timespec, e_username, e_group, e_mem
+#ifdef LOGIN_CAP
+ , e_class
+#endif
+} ecode_e;
+
+static char get_list __P((bitstr_t *, int, int, char *[], int, FILE *)),
+ get_range __P((bitstr_t *, int, int, char *[], int, FILE *)),
+ get_number __P((int *, int, char *[], int, FILE *));
+static int set_element __P((bitstr_t *, int, int, int));
+
+static char *ecodes[] =
+ {
+ "no error",
+ "bad minute",
+ "bad hour",
+ "bad day-of-month",
+ "bad month",
+ "bad day-of-week",
+ "bad command",
+ "bad time specifier",
+ "bad username",
+ "bad group name",
+ "out of memory",
+#ifdef LOGIN_CAP
+ "bad class name",
+#endif
+ };
+
+
+void
+free_entry(e)
+ entry *e;
+{
+#ifdef LOGIN_CAP
+ if (e->class != NULL)
+ free(e->class);
+#endif
+ if (e->cmd != NULL)
+ free(e->cmd);
+ if (e->envp != NULL)
+ env_free(e->envp);
+ free(e);
+}
+
+
+/* return NULL if eof or syntax error occurs;
+ * otherwise return a pointer to a new entry.
+ */
+entry *
+load_entry(file, error_func, pw, envp)
+ FILE *file;
+ void (*error_func)();
+ struct passwd *pw;
+ char **envp;
+{
+ /* this function reads one crontab entry -- the next -- from a file.
+ * it skips any leading blank lines, ignores comments, and returns
+ * EOF if for any reason the entry can't be read and parsed.
+ *
+ * the entry is also parsed here.
+ *
+ * syntax:
+ * user crontab:
+ * minutes hours doms months dows cmd\n
+ * system crontab (/etc/crontab):
+ * minutes hours doms months dows USERNAME cmd\n
+ */
+
+ ecode_e ecode = e_none;
+ entry *e;
+ int ch;
+ char cmd[MAX_COMMAND];
+ char envstr[MAX_ENVSTR];
+ char **prev_env;
+
+ Debug(DPARS, ("load_entry()...about to eat comments\n"))
+
+ skip_comments(file);
+
+ ch = get_char(file);
+ if (ch == EOF)
+ return NULL;
+
+ /* ch is now the first useful character of a useful line.
+ * it may be an @special or it may be the first character
+ * of a list of minutes.
+ */
+
+ e = (entry *) calloc(sizeof(entry), sizeof(char));
+
+ if (e == NULL) {
+ warn("load_entry: calloc failed");
+ return NULL;
+ }
+
+ if (ch == '@') {
+ /* all of these should be flagged and load-limited; i.e.,
+ * instead of @hourly meaning "0 * * * *" it should mean
+ * "close to the front of every hour but not 'til the
+ * system load is low". Problems are: how do you know
+ * what "low" means? (save me from /etc/cron.conf!) and:
+ * how to guarantee low variance (how low is low?), which
+ * means how to we run roughly every hour -- seems like
+ * we need to keep a history or let the first hour set
+ * the schedule, which means we aren't load-limited
+ * anymore. too much for my overloaded brain. (vix, jan90)
+ * HINT
+ */
+ Debug(DPARS, ("load_entry()...about to test shortcuts\n"))
+ ch = get_string(cmd, MAX_COMMAND, file, " \t\n");
+ if (!strcmp("reboot", cmd)) {
+ Debug(DPARS, ("load_entry()...reboot shortcut\n"))
+ e->flags |= WHEN_REBOOT;
+ } else if (!strcmp("yearly", cmd) || !strcmp("annually", cmd)){
+ Debug(DPARS, ("load_entry()...yearly shortcut\n"))
+ bit_set(e->minute, 0);
+ bit_set(e->hour, 0);
+ bit_set(e->dom, 0);
+ bit_set(e->month, 0);
+ bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1));
+ e->flags |= DOW_STAR;
+ } else if (!strcmp("monthly", cmd)) {
+ Debug(DPARS, ("load_entry()...monthly shortcut\n"))
+ bit_set(e->minute, 0);
+ bit_set(e->hour, 0);
+ bit_set(e->dom, 0);
+ bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1));
+ bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1));
+ e->flags |= DOW_STAR;
+ } else if (!strcmp("weekly", cmd)) {
+ Debug(DPARS, ("load_entry()...weekly shortcut\n"))
+ bit_set(e->minute, 0);
+ bit_set(e->hour, 0);
+ bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1));
+ e->flags |= DOM_STAR;
+ bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1));
+ bit_set(e->dow, 0);
+ } else if (!strcmp("daily", cmd) || !strcmp("midnight", cmd)) {
+ Debug(DPARS, ("load_entry()...daily shortcut\n"))
+ bit_set(e->minute, 0);
+ bit_set(e->hour, 0);
+ bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1));
+ bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1));
+ bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1));
+ } else if (!strcmp("hourly", cmd)) {
+ Debug(DPARS, ("load_entry()...hourly shortcut\n"))
+ bit_set(e->minute, 0);
+ bit_nset(e->hour, 0, (LAST_HOUR-FIRST_HOUR+1));
+ bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1));
+ bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1));
+ bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1));
+ } else {
+ ecode = e_timespec;
+ goto eof;
+ }
+ /* Advance past whitespace between shortcut and
+ * username/command.
+ */
+ Skip_Blanks(ch, file);
+ if (ch == EOF) {
+ ecode = e_cmd;
+ goto eof;
+ }
+ } else {
+ Debug(DPARS, ("load_entry()...about to parse numerics\n"))
+
+ ch = get_list(e->minute, FIRST_MINUTE, LAST_MINUTE,
+ PPC_NULL, ch, file);
+ if (ch == EOF) {
+ ecode = e_minute;
+ goto eof;
+ }
+
+ /* hours
+ */
+
+ ch = get_list(e->hour, FIRST_HOUR, LAST_HOUR,
+ PPC_NULL, ch, file);
+ if (ch == EOF) {
+ ecode = e_hour;
+ goto eof;
+ }
+
+ /* DOM (days of month)
+ */
+
+ if (ch == '*')
+ e->flags |= DOM_STAR;
+ ch = get_list(e->dom, FIRST_DOM, LAST_DOM,
+ PPC_NULL, ch, file);
+ if (ch == EOF) {
+ ecode = e_dom;
+ goto eof;
+ }
+
+ /* month
+ */
+
+ ch = get_list(e->month, FIRST_MONTH, LAST_MONTH,
+ MonthNames, ch, file);
+ if (ch == EOF) {
+ ecode = e_month;
+ goto eof;
+ }
+
+ /* DOW (days of week)
+ */
+
+ if (ch == '*')
+ e->flags |= DOW_STAR;
+ ch = get_list(e->dow, FIRST_DOW, LAST_DOW,
+ DowNames, ch, file);
+ if (ch == EOF) {
+ ecode = e_dow;
+ goto eof;
+ }
+ }
+
+ /* make sundays equivilent */
+ if (bit_test(e->dow, 0) || bit_test(e->dow, 7)) {
+ bit_set(e->dow, 0);
+ bit_set(e->dow, 7);
+ }
+
+ /* ch is the first character of a command, or a username */
+ unget_char(ch, file);
+
+ if (!pw) {
+ char *username = cmd; /* temp buffer */
+ char *s;
+ struct group *grp;
+#ifdef LOGIN_CAP
+ login_cap_t *lc;
+#endif
+
+ Debug(DPARS, ("load_entry()...about to parse username\n"))
+ ch = get_string(username, MAX_COMMAND, file, " \t");
+
+ Debug(DPARS, ("load_entry()...got %s\n",username))
+ if (ch == EOF) {
+ ecode = e_cmd;
+ goto eof;
+ }
+
+#ifdef LOGIN_CAP
+ if ((s = strrchr(username, '/')) != NULL) {
+ *s = '\0';
+ e->class = strdup(s + 1);
+ if (e->class == NULL)
+ warn("strdup(\"%s\")", s + 1);
+ } else {
+ e->class = strdup(RESOURCE_RC);
+ if (e->class == NULL)
+ warn("strdup(\"%s\")", RESOURCE_RC);
+ }
+ if (e->class == NULL) {
+ ecode = e_mem;
+ goto eof;
+ }
+ if ((lc = login_getclass(e->class)) == NULL) {
+ ecode = e_class;
+ goto eof;
+ }
+ login_close(lc);
+#endif
+ grp = NULL;
+ if ((s = strrchr(username, ':')) != NULL) {
+ *s = '\0';
+ if ((grp = getgrnam(s + 1)) == NULL) {
+ ecode = e_group;
+ goto eof;
+ }
+ }
+
+ pw = getpwnam(username);
+ if (pw == NULL) {
+ ecode = e_username;
+ goto eof;
+ }
+ if (grp != NULL)
+ pw->pw_gid = grp->gr_gid;
+ Debug(DPARS, ("load_entry()...uid %d, gid %d\n",pw->pw_uid,pw->pw_gid))
+#ifdef LOGIN_CAP
+ Debug(DPARS, ("load_entry()...class %s\n",e->class))
+#endif
+ }
+
+ if (pw->pw_expire && time(NULL) >= pw->pw_expire) {
+ ecode = e_username;
+ goto eof;
+ }
+
+ e->uid = pw->pw_uid;
+ e->gid = pw->pw_gid;
+
+ /* copy and fix up environment. some variables are just defaults and
+ * others are overrides.
+ */
+ e->envp = env_copy(envp);
+ if (e->envp == NULL) {
+ warn("env_copy");
+ ecode = e_mem;
+ goto eof;
+ }
+ if (!env_get("SHELL", e->envp)) {
+ prev_env = e->envp;
+ sprintf(envstr, "SHELL=%s", _PATH_BSHELL);
+ e->envp = env_set(e->envp, envstr);
+ if (e->envp == NULL) {
+ warn("env_set(%s)", envstr);
+ env_free(prev_env);
+ ecode = e_mem;
+ goto eof;
+ }
+ }
+ prev_env = e->envp;
+ sprintf(envstr, "HOME=%s", pw->pw_dir);
+ e->envp = env_set(e->envp, envstr);
+ if (e->envp == NULL) {
+ warn("env_set(%s)", envstr);
+ env_free(prev_env);
+ ecode = e_mem;
+ goto eof;
+ }
+ if (!env_get("PATH", e->envp)) {
+ prev_env = e->envp;
+ sprintf(envstr, "PATH=%s", _PATH_DEFPATH);
+ e->envp = env_set(e->envp, envstr);
+ if (e->envp == NULL) {
+ warn("env_set(%s)", envstr);
+ env_free(prev_env);
+ ecode = e_mem;
+ goto eof;
+ }
+ }
+ prev_env = e->envp;
+ sprintf(envstr, "%s=%s", "LOGNAME", pw->pw_name);
+ e->envp = env_set(e->envp, envstr);
+ if (e->envp == NULL) {
+ warn("env_set(%s)", envstr);
+ env_free(prev_env);
+ ecode = e_mem;
+ goto eof;
+ }
+#if defined(BSD)
+ prev_env = e->envp;
+ sprintf(envstr, "%s=%s", "USER", pw->pw_name);
+ e->envp = env_set(e->envp, envstr);
+ if (e->envp == NULL) {
+ warn("env_set(%s)", envstr);
+ env_free(prev_env);
+ ecode = e_mem;
+ goto eof;
+ }
+#endif
+
+ Debug(DPARS, ("load_entry()...about to parse command\n"))
+
+ /* Everything up to the next \n or EOF is part of the command...
+ * too bad we don't know in advance how long it will be, since we
+ * need to malloc a string for it... so, we limit it to MAX_COMMAND.
+ * XXX - should use realloc().
+ */
+ ch = get_string(cmd, MAX_COMMAND, file, "\n");
+
+ /* a file without a \n before the EOF is rude, so we'll complain...
+ */
+ if (ch == EOF) {
+ ecode = e_cmd;
+ goto eof;
+ }
+
+ /* got the command in the 'cmd' string; save it in *e.
+ */
+ e->cmd = strdup(cmd);
+ if (e->cmd == NULL) {
+ warn("strdup(\"%s\")", cmd);
+ ecode = e_mem;
+ goto eof;
+ }
+ Debug(DPARS, ("load_entry()...returning successfully\n"))
+
+ /* success, fini, return pointer to the entry we just created...
+ */
+ return e;
+
+ eof:
+ free_entry(e);
+ if (ecode != e_none && error_func)
+ (*error_func)(ecodes[(int)ecode]);
+ while (ch != EOF && ch != '\n')
+ ch = get_char(file);
+ return NULL;
+}
+
+
+static char
+get_list(bits, low, high, names, ch, file)
+ bitstr_t *bits; /* one bit per flag, default=FALSE */
+ int low, high; /* bounds, impl. offset for bitstr */
+ char *names[]; /* NULL or *[] of names for these elements */
+ int ch; /* current character being processed */
+ FILE *file; /* file being read */
+{
+ register int done;
+
+ /* we know that we point to a non-blank character here;
+ * must do a Skip_Blanks before we exit, so that the
+ * next call (or the code that picks up the cmd) can
+ * assume the same thing.
+ */
+
+ Debug(DPARS|DEXT, ("get_list()...entered\n"))
+
+ /* list = range {"," range}
+ */
+
+ /* clear the bit string, since the default is 'off'.
+ */
+ bit_nclear(bits, 0, (high-low+1));
+
+ /* process all ranges
+ */
+ done = FALSE;
+ while (!done) {
+ ch = get_range(bits, low, high, names, ch, file);
+ if (ch == ',')
+ ch = get_char(file);
+ else
+ done = TRUE;
+ }
+
+ /* exiting. skip to some blanks, then skip over the blanks.
+ */
+ Skip_Nonblanks(ch, file)
+ Skip_Blanks(ch, file)
+
+ Debug(DPARS|DEXT, ("get_list()...exiting w/ %02x\n", ch))
+
+ return ch;
+}
+
+
+static char
+get_range(bits, low, high, names, ch, file)
+ bitstr_t *bits; /* one bit per flag, default=FALSE */
+ int low, high; /* bounds, impl. offset for bitstr */
+ char *names[]; /* NULL or names of elements */
+ int ch; /* current character being processed */
+ FILE *file; /* file being read */
+{
+ /* range = number | number "-" number [ "/" number ]
+ */
+
+ register int i;
+ auto int num1, num2, num3;
+
+ Debug(DPARS|DEXT, ("get_range()...entering, exit won't show\n"))
+
+ if (ch == '*') {
+ /* '*' means "first-last" but can still be modified by /step
+ */
+ num1 = low;
+ num2 = high;
+ ch = get_char(file);
+ if (ch == EOF)
+ return EOF;
+ } else {
+ if (EOF == (ch = get_number(&num1, low, names, ch, file)))
+ return EOF;
+
+ if (ch != '-') {
+ /* not a range, it's a single number.
+ */
+ if (EOF == set_element(bits, low, high, num1))
+ return EOF;
+ return ch;
+ } else {
+ /* eat the dash
+ */
+ ch = get_char(file);
+ if (ch == EOF)
+ return EOF;
+
+ /* get the number following the dash
+ */
+ ch = get_number(&num2, low, names, ch, file);
+ if (ch == EOF)
+ return EOF;
+ }
+ }
+
+ /* check for step size
+ */
+ if (ch == '/') {
+ /* eat the slash
+ */
+ ch = get_char(file);
+ if (ch == EOF)
+ return EOF;
+
+ /* get the step size -- note: we don't pass the
+ * names here, because the number is not an
+ * element id, it's a step size. 'low' is
+ * sent as a 0 since there is no offset either.
+ */
+ ch = get_number(&num3, 0, PPC_NULL, ch, file);
+ if (ch == EOF)
+ return EOF;
+ } else {
+ /* no step. default==1.
+ */
+ num3 = 1;
+ }
+
+ /* range. set all elements from num1 to num2, stepping
+ * by num3. (the step is a downward-compatible extension
+ * proposed conceptually by bob@acornrc, syntactically
+ * designed then implmented by paul vixie).
+ */
+ for (i = num1; i <= num2; i += num3)
+ if (EOF == set_element(bits, low, high, i))
+ return EOF;
+
+ return ch;
+}
+
+
+static char
+get_number(numptr, low, names, ch, file)
+ int *numptr; /* where does the result go? */
+ int low; /* offset applied to result if symbolic enum used */
+ char *names[]; /* symbolic names, if any, for enums */
+ int ch; /* current character */
+ FILE *file; /* source */
+{
+ char temp[MAX_TEMPSTR], *pc;
+ int len, i, all_digits;
+
+ /* collect alphanumerics into our fixed-size temp array
+ */
+ pc = temp;
+ len = 0;
+ all_digits = TRUE;
+ while (isalnum(ch)) {
+ if (++len >= MAX_TEMPSTR)
+ return EOF;
+
+ *pc++ = ch;
+
+ if (!isdigit(ch))
+ all_digits = FALSE;
+
+ ch = get_char(file);
+ }
+ *pc = '\0';
+
+ /* try to find the name in the name list
+ */
+ if (names) {
+ for (i = 0; names[i] != NULL; i++) {
+ Debug(DPARS|DEXT,
+ ("get_num, compare(%s,%s)\n", names[i], temp))
+ if (!strcasecmp(names[i], temp)) {
+ *numptr = i+low;
+ return ch;
+ }
+ }
+ }
+
+ /* no name list specified, or there is one and our string isn't
+ * in it. either way: if it's all digits, use its magnitude.
+ * otherwise, it's an error.
+ */
+ if (all_digits) {
+ *numptr = atoi(temp);
+ return ch;
+ }
+
+ return EOF;
+}
+
+
+static int
+set_element(bits, low, high, number)
+ bitstr_t *bits; /* one bit per flag, default=FALSE */
+ int low;
+ int high;
+ int number;
+{
+ Debug(DPARS|DEXT, ("set_element(?,%d,%d,%d)\n", low, high, number))
+
+ if (number < low || number > high)
+ return EOF;
+
+ bit_set(bits, (number-low));
+ return OK;
+}
diff --git a/usr.sbin/cron/lib/env.c b/usr.sbin/cron/lib/env.c
new file mode 100644
index 0000000..c2adb49
--- /dev/null
+++ b/usr.sbin/cron/lib/env.c
@@ -0,0 +1,262 @@
+/* Copyright 1988,1990,1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ */
+
+#if !defined(lint) && !defined(LINT)
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif
+
+
+#include "cron.h"
+
+
+char **
+env_init()
+{
+ register char **p = (char **) malloc(sizeof(char *));
+
+ if (p)
+ p[0] = NULL;
+ return (p);
+}
+
+
+void
+env_free(envp)
+ char **envp;
+{
+ char **p;
+
+ if ((p = envp))
+ for (; *p; p++)
+ free(*p);
+ free(envp);
+}
+
+
+char **
+env_copy(envp)
+ register char **envp;
+{
+ register int count, i;
+ register char **p;
+
+ for (count = 0; envp[count] != NULL; count++)
+ ;
+ p = (char **) malloc((count+1) * sizeof(char *)); /* 1 for the NULL */
+ if (p == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ for (i = 0; i < count; i++)
+ if ((p[i] = strdup(envp[i])) == NULL) {
+ while (--i >= 0)
+ (void) free(p[i]);
+ free(p);
+ errno = ENOMEM;
+ return NULL;
+ }
+ p[count] = NULL;
+ return (p);
+}
+
+
+char **
+env_set(envp, envstr)
+ char **envp;
+ char *envstr;
+{
+ register int count, found;
+ register char **p;
+ char *q;
+
+ /*
+ * count the number of elements, including the null pointer;
+ * also set 'found' to -1 or index of entry if already in here.
+ */
+ found = -1;
+ for (count = 0; envp[count] != NULL; count++) {
+ if (!strcmp_until(envp[count], envstr, '='))
+ found = count;
+ }
+ count++; /* for the NULL */
+
+ if (found != -1) {
+ /*
+ * it exists already, so just free the existing setting,
+ * save our new one there, and return the existing array.
+ */
+ q = envp[found];
+ if ((envp[found] = strdup(envstr)) == NULL) {
+ envp[found] = q;
+ /* XXX env_free(envp); */
+ errno = ENOMEM;
+ return NULL;
+ }
+ free(q);
+ return (envp);
+ }
+
+ /*
+ * it doesn't exist yet, so resize the array, move null pointer over
+ * one, save our string over the old null pointer, and return resized
+ * array.
+ */
+ p = (char **) realloc((void *) envp,
+ (unsigned) ((count+1) * sizeof(char *)));
+ if (p == NULL) {
+ /* XXX env_free(envp); */
+ errno = ENOMEM;
+ return NULL;
+ }
+ p[count] = p[count-1];
+ if ((p[count-1] = strdup(envstr)) == NULL) {
+ env_free(p);
+ errno = ENOMEM;
+ return NULL;
+ }
+ return (p);
+}
+
+
+/* return ERR = end of file
+ * FALSE = not an env setting (file was repositioned)
+ * TRUE = was an env setting
+ */
+int
+load_env(envstr, f)
+ char *envstr;
+ FILE *f;
+{
+ long filepos;
+ int fileline;
+ char name[MAX_ENVSTR], val[MAX_ENVSTR];
+ char quotechar, *c, *str;
+ int state;
+
+ /* The following states are traversed in order: */
+#define NAMEI 0 /* First char of NAME, may be quote */
+#define NAME 1 /* Subsequent chars of NAME */
+#define EQ1 2 /* After end of name, looking for '=' sign */
+#define EQ2 3 /* After '=', skipping whitespace */
+#define VALUEI 4 /* First char of VALUE, may be quote */
+#define VALUE 5 /* Subsequent chars of VALUE */
+#define FINI 6 /* All done, skipping trailing whitespace */
+#define ERROR 7 /* Error */
+
+ filepos = ftell(f);
+ fileline = LineNumber;
+ skip_comments(f);
+ if (EOF == get_string(envstr, MAX_ENVSTR, f, "\n"))
+ return (ERR);
+
+ Debug(DPARS, ("load_env, read <%s>\n", envstr));
+
+ bzero (name, sizeof name);
+ bzero (val, sizeof val);
+ str = name;
+ state = NAMEI;
+ quotechar = '\0';
+ c = envstr;
+ while (state != ERROR && *c) {
+ switch (state) {
+ case NAMEI:
+ case VALUEI:
+ if (*c == '\'' || *c == '"')
+ quotechar = *c++;
+ ++state;
+ /* FALLTHROUGH */
+ case NAME:
+ case VALUE:
+ if (quotechar) {
+ if (*c == quotechar) {
+ state++;
+ c++;
+ break;
+ }
+ if (state == NAME && *c == '=') {
+ state = ERROR;
+ break;
+ }
+ } else {
+ if (isspace (*c)) {
+ state++;
+ c++;
+ break;
+ }
+ if (state == NAME && *c == '=') {
+ state++;
+ break;
+ }
+ }
+ *str++ = *c++;
+ break;
+
+ case EQ1:
+ if (*c == '=') {
+ state++;
+ str = val;
+ quotechar = '\0';
+ } else {
+ if (!isspace (*c))
+ state = ERROR;
+ }
+ c++;
+ break;
+ case EQ2:
+ case FINI:
+ if (isspace (*c))
+ c++;
+ else
+ state++;
+ break;
+ }
+ }
+ if (state != FINI && !(state == VALUE && !quotechar)) {
+ Debug(DPARS, ("load_env, parse error, state = %d\n", state))
+ fseek(f, filepos, 0);
+ Set_LineNum(fileline);
+ return (FALSE);
+ }
+
+ /* 2 fields from parser; looks like an env setting
+ */
+
+ if (strlen(name) + 1 + strlen(val) >= MAX_ENVSTR-1)
+ return (FALSE);
+ (void) sprintf(envstr, "%s=%s", name, val);
+ Debug(DPARS, ("load_env, <%s> <%s> -> <%s>\n", name, val, envstr))
+ return (TRUE);
+}
+
+
+char *
+env_get(name, envp)
+ register char *name;
+ register char **envp;
+{
+ register int len = strlen(name);
+ register char *p, *q;
+
+ while ((p = *envp++)) {
+ if (!(q = strchr(p, '=')))
+ continue;
+ if ((q - p) == len && !strncmp(p, name, len))
+ return (q+1);
+ }
+ return (NULL);
+}
diff --git a/usr.sbin/cron/lib/misc.c b/usr.sbin/cron/lib/misc.c
new file mode 100644
index 0000000..195ff9c
--- /dev/null
+++ b/usr.sbin/cron/lib/misc.c
@@ -0,0 +1,655 @@
+/* Copyright 1988,1990,1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ */
+
+#if !defined(lint) && !defined(LINT)
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif
+
+/* vix 26jan87 [RCS has the rest of the log]
+ * vix 30dec86 [written]
+ */
+
+
+#include "cron.h"
+#if SYS_TIME_H
+# include <sys/time.h>
+#else
+# include <time.h>
+#endif
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <err.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#if defined(SYSLOG)
+# include <syslog.h>
+#endif
+
+
+#if defined(LOG_DAEMON) && !defined(LOG_CRON)
+#define LOG_CRON LOG_DAEMON
+#endif
+
+
+static int LogFD = ERR;
+
+
+int
+strcmp_until(left, right, until)
+ char *left;
+ char *right;
+ int until;
+{
+ register int diff;
+
+ while (*left && *left != until && *left == *right) {
+ left++;
+ right++;
+ }
+
+ if ((*left=='\0' || *left == until) &&
+ (*right=='\0' || *right == until)) {
+ diff = 0;
+ } else {
+ diff = *left - *right;
+ }
+
+ return diff;
+}
+
+
+/* strdtb(s) - delete trailing blanks in string 's' and return new length
+ */
+int
+strdtb(s)
+ char *s;
+{
+ char *x = s;
+
+ /* scan forward to the null
+ */
+ while (*x)
+ x++;
+
+ /* scan backward to either the first character before the string,
+ * or the last non-blank in the string, whichever comes first.
+ */
+ do {x--;}
+ while (x >= s && isspace(*x));
+
+ /* one character beyond where we stopped above is where the null
+ * goes.
+ */
+ *++x = '\0';
+
+ /* the difference between the position of the null character and
+ * the position of the first character of the string is the length.
+ */
+ return x - s;
+}
+
+
+int
+set_debug_flags(flags)
+ char *flags;
+{
+ /* debug flags are of the form flag[,flag ...]
+ *
+ * if an error occurs, print a message to stdout and return FALSE.
+ * otherwise return TRUE after setting ERROR_FLAGS.
+ */
+
+#if !DEBUGGING
+
+ printf("this program was compiled without debugging enabled\n");
+ return FALSE;
+
+#else /* DEBUGGING */
+
+ char *pc = flags;
+
+ DebugFlags = 0;
+
+ while (*pc) {
+ char **test;
+ int mask;
+
+ /* try to find debug flag name in our list.
+ */
+ for ( test = DebugFlagNames, mask = 1;
+ *test && strcmp_until(*test, pc, ',');
+ test++, mask <<= 1
+ )
+ ;
+
+ if (!*test) {
+ fprintf(stderr,
+ "unrecognized debug flag <%s> <%s>\n",
+ flags, pc);
+ return FALSE;
+ }
+
+ DebugFlags |= mask;
+
+ /* skip to the next flag
+ */
+ while (*pc && *pc != ',')
+ pc++;
+ if (*pc == ',')
+ pc++;
+ }
+
+ if (DebugFlags) {
+ int flag;
+
+ fprintf(stderr, "debug flags enabled:");
+
+ for (flag = 0; DebugFlagNames[flag]; flag++)
+ if (DebugFlags & (1 << flag))
+ fprintf(stderr, " %s", DebugFlagNames[flag]);
+ fprintf(stderr, "\n");
+ }
+
+ return TRUE;
+
+#endif /* DEBUGGING */
+}
+
+
+void
+set_cron_uid()
+{
+#if defined(BSD) || defined(POSIX)
+ if (seteuid(ROOT_UID) < OK)
+ err(ERROR_EXIT, "seteuid");
+#else
+ if (setuid(ROOT_UID) < OK)
+ err(ERROR_EXIT, "setuid");
+#endif
+}
+
+
+void
+set_cron_cwd()
+{
+ struct stat sb;
+
+ /* first check for CRONDIR ("/var/cron" or some such)
+ */
+ if (stat(CRONDIR, &sb) < OK && errno == ENOENT) {
+ warn("%s", CRONDIR);
+ if (OK == mkdir(CRONDIR, 0700)) {
+ warnx("%s: created", CRONDIR);
+ stat(CRONDIR, &sb);
+ } else {
+ err(ERROR_EXIT, "%s: mkdir", CRONDIR);
+ }
+ }
+ if (!(sb.st_mode & S_IFDIR))
+ err(ERROR_EXIT, "'%s' is not a directory, bailing out", CRONDIR);
+ if (chdir(CRONDIR) < OK)
+ err(ERROR_EXIT, "cannot chdir(%s), bailing out", CRONDIR);
+
+ /* CRONDIR okay (now==CWD), now look at SPOOL_DIR ("tabs" or some such)
+ */
+ if (stat(SPOOL_DIR, &sb) < OK && errno == ENOENT) {
+ warn("%s", SPOOL_DIR);
+ if (OK == mkdir(SPOOL_DIR, 0700)) {
+ warnx("%s: created", SPOOL_DIR);
+ stat(SPOOL_DIR, &sb);
+ } else {
+ err(ERROR_EXIT, "%s: mkdir", SPOOL_DIR);
+ }
+ }
+ if (!(sb.st_mode & S_IFDIR))
+ err(ERROR_EXIT, "'%s' is not a directory, bailing out", SPOOL_DIR);
+}
+
+
+/* acquire_daemonlock() - write our PID into /etc/cron.pid, unless
+ * another daemon is already running, which we detect here.
+ *
+ * note: main() calls us twice; once before forking, once after.
+ * we maintain static storage of the file pointer so that we
+ * can rewrite our PID into the PIDFILE after the fork.
+ *
+ * it would be great if fflush() disassociated the file buffer.
+ */
+void
+acquire_daemonlock(closeflag)
+ int closeflag;
+{
+ static FILE *fp = NULL;
+
+ if (closeflag && fp) {
+ fclose(fp);
+ fp = NULL;
+ return;
+ }
+
+ if (!fp) {
+ char pidfile[MAX_FNAME];
+ char buf[MAX_TEMPSTR];
+ int fd, otherpid;
+
+ (void) sprintf(pidfile, PIDFILE, PIDDIR);
+ if ((-1 == (fd = open(pidfile, O_RDWR|O_CREAT, 0644)))
+ || (NULL == (fp = fdopen(fd, "r+")))
+ ) {
+ sprintf(buf, "can't open or create %s: %s",
+ pidfile, strerror(errno));
+ log_it("CRON", getpid(), "DEATH", buf);
+ errx(ERROR_EXIT, "%s", buf);
+ }
+
+ if (flock(fd, LOCK_EX|LOCK_NB) < OK) {
+ int save_errno = errno;
+
+ fscanf(fp, "%d", &otherpid);
+ sprintf(buf, "can't lock %s, otherpid may be %d: %s",
+ pidfile, otherpid, strerror(save_errno));
+ log_it("CRON", getpid(), "DEATH", buf);
+ errx(ERROR_EXIT, "%s", buf);
+ }
+
+ (void) fcntl(fd, F_SETFD, 1);
+ }
+
+ rewind(fp);
+ fprintf(fp, "%d\n", getpid());
+ fflush(fp);
+ (void) ftruncate(fileno(fp), ftell(fp));
+
+ /* abandon fd and fp even though the file is open. we need to
+ * keep it open and locked, but we don't need the handles elsewhere.
+ */
+}
+
+/* get_char(file) : like getc() but increment LineNumber on newlines
+ */
+int
+get_char(file)
+ FILE *file;
+{
+ int ch;
+
+ ch = getc(file);
+ if (ch == '\n')
+ Set_LineNum(LineNumber + 1)
+ return ch;
+}
+
+
+/* unget_char(ch, file) : like ungetc but do LineNumber processing
+ */
+void
+unget_char(ch, file)
+ int ch;
+ FILE *file;
+{
+ ungetc(ch, file);
+ if (ch == '\n')
+ Set_LineNum(LineNumber - 1)
+}
+
+
+/* get_string(str, max, file, termstr) : like fgets() but
+ * (1) has terminator string which should include \n
+ * (2) will always leave room for the null
+ * (3) uses get_char() so LineNumber will be accurate
+ * (4) returns EOF or terminating character, whichever
+ */
+int
+get_string(string, size, file, terms)
+ char *string;
+ int size;
+ FILE *file;
+ char *terms;
+{
+ int ch;
+
+ while (EOF != (ch = get_char(file)) && !strchr(terms, ch)) {
+ if (size > 1) {
+ *string++ = (char) ch;
+ size--;
+ }
+ }
+
+ if (size > 0)
+ *string = '\0';
+
+ return ch;
+}
+
+
+/* skip_comments(file) : read past comment (if any)
+ */
+void
+skip_comments(file)
+ FILE *file;
+{
+ int ch;
+
+ while (EOF != (ch = get_char(file))) {
+ /* ch is now the first character of a line.
+ */
+
+ while (ch == ' ' || ch == '\t')
+ ch = get_char(file);
+
+ if (ch == EOF)
+ break;
+
+ /* ch is now the first non-blank character of a line.
+ */
+
+ if (ch != '\n' && ch != '#')
+ break;
+
+ /* ch must be a newline or comment as first non-blank
+ * character on a line.
+ */
+
+ while (ch != '\n' && ch != EOF)
+ ch = get_char(file);
+
+ /* ch is now the newline of a line which we're going to
+ * ignore.
+ */
+ }
+ if (ch != EOF)
+ unget_char(ch, file);
+}
+
+
+/* int in_file(char *string, FILE *file)
+ * return TRUE if one of the lines in file matches string exactly,
+ * FALSE otherwise.
+ */
+static int
+in_file(string, file)
+ char *string;
+ FILE *file;
+{
+ char line[MAX_TEMPSTR];
+
+ rewind(file);
+ while (fgets(line, MAX_TEMPSTR, file)) {
+ if (line[0] != '\0')
+ if (line[strlen(line)-1] == '\n')
+ line[strlen(line)-1] = '\0';
+ if (0 == strcmp(line, string))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+/* int allowed(char *username)
+ * returns TRUE if (ALLOW_FILE exists and user is listed)
+ * or (DENY_FILE exists and user is NOT listed)
+ * or (neither file exists but user=="root" so it's okay)
+ */
+int
+allowed(username)
+ char *username;
+{
+ 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 (msg == NULL)
+ warnx("failed to allocate memory for log message");
+ else {
+ if (LogFD < OK) {
+ LogFD = open(LOG_FILE, O_WRONLY|O_APPEND|O_CREAT, 0600);
+ if (LogFD < OK) {
+ warn("can't open log file %s", LOG_FILE);
+ } else {
+ (void) fcntl(LogFD, F_SETFD, 1);
+ }
+ }
+
+ /* we have to sprintf() it because fprintf() doesn't always
+ * write everything out in one chunk and this has to be
+ * atomically appended to the log file.
+ */
+ sprintf(msg, "%s (%02d/%02d-%02d:%02d:%02d-%d) %s (%s)\n",
+ username,
+ t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min,
+ t->tm_sec, pid, event, detail);
+
+ /* we have to run strlen() because sprintf() returns (char*)
+ * on old BSD.
+ */
+ if (LogFD < OK || write(LogFD, msg, strlen(msg)) < OK) {
+ if (LogFD >= OK)
+ warn("%s", LOG_FILE);
+ warnx("can't write to log file");
+ write(STDERR, msg, strlen(msg));
+ }
+
+ free(msg);
+ }
+#endif /*LOG_FILE*/
+
+#if defined(SYSLOG)
+ if (!syslog_open) {
+ /* we don't use LOG_PID since the pid passed to us by
+ * our client may not be our own. therefore we want to
+ * print the pid ourselves.
+ */
+# ifdef LOG_DAEMON
+ openlog(ProgramName, LOG_PID, LOG_CRON);
+# else
+ openlog(ProgramName, LOG_PID);
+# endif
+ syslog_open = TRUE; /* assume openlog success */
+ }
+
+ syslog(LOG_INFO, "(%s) %s (%s)\n", username, event, detail);
+
+#endif /*SYSLOG*/
+
+#if DEBUGGING
+ if (DebugFlags) {
+ fprintf(stderr, "log_it: (%s %d) %s (%s)\n",
+ username, pid, event, detail);
+ }
+#endif
+}
+
+
+void
+log_close() {
+ if (LogFD != ERR) {
+ close(LogFD);
+ LogFD = ERR;
+ }
+}
+
+
+/* two warnings:
+ * (1) this routine is fairly slow
+ * (2) it returns a pointer to static storage
+ */
+char *
+first_word(s, t)
+ register char *s; /* string we want the first word of */
+ register char *t; /* terminators, implicitly including \0 */
+{
+ static char retbuf[2][MAX_TEMPSTR + 1]; /* sure wish C had GC */
+ static int retsel = 0;
+ register char *rb, *rp;
+
+ /* select a return buffer */
+ retsel = 1-retsel;
+ rb = &retbuf[retsel][0];
+ rp = rb;
+
+ /* skip any leading terminators */
+ while (*s && (NULL != strchr(t, *s))) {
+ s++;
+ }
+
+ /* copy until next terminator or full buffer */
+ while (*s && (NULL == strchr(t, *s)) && (rp < &rb[MAX_TEMPSTR])) {
+ *rp++ = *s++;
+ }
+
+ /* finish the return-string and return it */
+ *rp = '\0';
+ return rb;
+}
+
+
+/* warning:
+ * heavily ascii-dependent.
+ */
+void
+mkprint(dst, src, len)
+ register char *dst;
+ register unsigned char *src;
+ register int len;
+{
+ while (len-- > 0)
+ {
+ register unsigned char ch = *src++;
+
+ if (ch < ' ') { /* control character */
+ *dst++ = '^';
+ *dst++ = ch + '@';
+ } else if (ch < 0177) { /* printable */
+ *dst++ = ch;
+ } else if (ch == 0177) { /* delete/rubout */
+ *dst++ = '^';
+ *dst++ = '?';
+ } else { /* parity character */
+ sprintf(dst, "\\%03o", ch);
+ dst += 4;
+ }
+ }
+ *dst = '\0';
+}
+
+
+/* warning:
+ * returns a pointer to malloc'd storage, you must call free yourself.
+ */
+char *
+mkprints(src, len)
+ register unsigned char *src;
+ register unsigned int len;
+{
+ register char *dst = malloc(len*4 + 1);
+
+ if (dst != NULL)
+ mkprint(dst, src, len);
+
+ return dst;
+}
+
+
+#ifdef MAIL_DATE
+/* Sat, 27 Feb 93 11:44:51 CST
+ * 123456789012345678901234567
+ */
+char *
+arpadate(clock)
+ time_t *clock;
+{
+ time_t t = clock ?*clock :time(0L);
+ struct tm *tm = localtime(&t);
+ static char ret[32]; /* zone name might be >3 chars */
+
+ if (tm->tm_year >= 100)
+ tm->tm_year += 1900;
+
+ (void) snprintf(ret, sizeof(ret), "%s, %2d %s %d %02d:%02d:%02d %s",
+ DowNames[tm->tm_wday],
+ tm->tm_mday,
+ MonthNames[tm->tm_mon],
+ tm->tm_year,
+ tm->tm_hour,
+ tm->tm_min,
+ tm->tm_sec,
+ TZONE(*tm));
+ return ret;
+}
+#endif /*MAIL_DATE*/
+
+
+#ifdef HAVE_SAVED_UIDS
+static int save_euid;
+int swap_uids() { save_euid = geteuid(); return seteuid(getuid()); }
+int swap_uids_back() { return seteuid(save_euid); }
+#else /*HAVE_SAVED_UIDS*/
+int swap_uids() { return setreuid(geteuid(), getuid()); }
+int swap_uids_back() { return swap_uids(); }
+#endif /*HAVE_SAVED_UIDS*/
diff --git a/usr.sbin/crunch/COPYRIGHT b/usr.sbin/crunch/COPYRIGHT
new file mode 100644
index 0000000..c7b4d2f
--- /dev/null
+++ b/usr.sbin/crunch/COPYRIGHT
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 1994 University of Maryland
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of U.M. not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. U.M. makes no representations about the
+ * suitability of this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ *
+ * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
+ * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: James da Silva, Systems Design and Analysis Group
+ * Computer Science Department
+ * University of Maryland at College Park
+ */
diff --git a/usr.sbin/crunch/Makefile b/usr.sbin/crunch/Makefile
new file mode 100644
index 0000000..6bf600f
--- /dev/null
+++ b/usr.sbin/crunch/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+SUBDIR= crunchgen crunchide
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/crunch/Makefile.inc b/usr.sbin/crunch/Makefile.inc
new file mode 100644
index 0000000..f8ffe67
--- /dev/null
+++ b/usr.sbin/crunch/Makefile.inc
@@ -0,0 +1,4 @@
+# $FreeBSD$
+
+# modify to taste
+BINDIR?= /usr/bin
diff --git a/usr.sbin/crunch/README b/usr.sbin/crunch/README
new file mode 100644
index 0000000..27c2d02
--- /dev/null
+++ b/usr.sbin/crunch/README
@@ -0,0 +1,88 @@
+
+CRUNCH 0.2 README 6/14/94
+
+Crunch is available via anonymous ftp to ftp.cs.umd.edu in
+ pub/bsd/crunch-0.2.tar.gz
+
+
+WHAT'S NEW IN 0.2
+
+* The prototype awk script has been replaced by a more capable and
+ hopefully more robust C program.
+* No fragile template makefiles or dependencies on the details of the
+ bsd build environment.
+* You can build crunched binaries even with no sources on-line, you
+ just need the .o files. Crunchgen still will try to figure out as
+ much as possible on its own, but you can override its guessing by
+ specifying the list of .o files explicitly.
+* Crunch itself has been bmake'd and some man pages written, so it
+ should be ready to install.
+
+
+INTRODUCTION
+
+Crunch is a little package that helps create "crunched" binaries for use
+on boot, install, and fixit floppies. A crunched binary in this case is
+one where many programs have been linked together into one a.out file.
+The different programs are run depending on the value of argv[0], so
+hard links to the crunched binary suffice to simulate a perfectly normal
+system.
+
+As an example, I have created an 980K crunched "fixit" binary containing
+the following programs in their entirety:
+
+ cat chmod cp date dd df echo ed expr hostname kill ln ls mkdir
+ mt mv pwd rcp rm rmdir sh sleep stty sync test [ badsect chown
+ clri disklabel dump rdump dmesg fdisk fsck halt ifconfig init
+ mknod mount newfs ping reboot restore rrestore swapon umount
+ ftp rsh sed telnet rlogin vi cpio gzip gunzip gzcat
+
+Note carefully: vi, cpio, gzip, ed, sed, dump/restore, some networking
+utilities, and the disk management utilities, all in a binary small
+enough to fit on a 1.2 MB root filesystem floppy (albeit with the kernel
+on its own boot floppy). A more reasonable subset can be made to fit
+easily with a kernel for a decent one-disk fixit filesystem.
+
+The linking together of different programs by hand is an old
+space-saving technique. Crunch automates the process by building the
+necessary stub files and makefile for you (via the crunchgen program),
+and by doctoring the symbol tables of the component .o files to allow
+them to link without "symbol multiply defined" conflicts (via the
+crunchide program).
+
+
+BUILDING CRUNCH
+
+Just type make, then make install.
+
+Crunch was written and tested under NetBSD/i386, but should work under
+other PC BSD systems that use GNU ld.
+
+The crunchgen(1) and crunchide(1) man pages have more details on using
+crunch, and the examples subdirectory contains some working .conf files
+and a sample Makefile.
+
+CREDITS
+
+Thanks to the NetBSD team for a consistently high quality effort in
+bringing together a solid, state of the art development environment.
+
+Thanks to the FreeBSD guys; Rod Grimes, Nate Williams and Jordan
+Hubbard; and to Bruce Evans, for immediate and detailed feedback on
+crunch 0.1, and for pressing me to make the prototype more useable.
+
+Crunch was written for the Maruti Hard Real-Time Operating System
+project at the University of Maryland, to help make for better install
+and recovery procedures for our NetBSD-based development environment. It
+is copyright (c) 1994 by the University of Maryland under a UCB-style
+freely- redistributable notice. See the file COPYRIGHT for details.
+
+Please let me know of any problems or of enhancements you make to this
+package. I'm particularly interested in the details of what you found
+was good to put on your fixit or install disks. Thanks!
+
+Share and Enjoy,
+Jaime
+............................................................................
+: Stand on my shoulders, : jds@cs.umd.edu : James da Silva
+: not on my toes. : uunet!mimsy!jds : http://www.cs.umd.edu/users/jds
diff --git a/usr.sbin/crunch/crunchgen/Makefile b/usr.sbin/crunch/crunchgen/Makefile
new file mode 100644
index 0000000..8d0a78b
--- /dev/null
+++ b/usr.sbin/crunch/crunchgen/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+PROG= crunchgen
+SRCS= crunchgen.c crunched_skel.c
+CLEANFILES+= crunched_skel.c
+
+crunched_skel.c: crunched_main.c
+ sh -e ${.CURDIR}/mkskel.sh ${.CURDIR}/crunched_main.c >crunched_skel.c
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/crunch/crunchgen/crunched_main.c b/usr.sbin/crunch/crunchgen/crunched_main.c
new file mode 100644
index 0000000..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..4333866
--- /dev/null
+++ b/usr.sbin/crunch/crunchgen/crunchgen.1
@@ -0,0 +1,448 @@
+.\"
+.\" Copyright (c) 1994 University of Maryland
+.\" All Rights Reserved.
+.\"
+.\" Permission to use, copy, modify, distribute, and sell this software and its
+.\" documentation for any purpose is hereby granted without fee, provided that
+.\" the above copyright notice appear in all copies and that both that
+.\" copyright notice and this permission notice appear in supporting
+.\" documentation, and that the name of U.M. not be used in advertising or
+.\" publicity pertaining to distribution of the software without specific,
+.\" written prior permission. U.M. makes no representations about the
+.\" suitability of this software for any purpose. It is provided "as is"
+.\" without express or implied warranty.
+.\"
+.\" U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
+.\" BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+.\" IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.\" Author: James da Silva, Systems Design and Analysis Group
+.\" Computer Science Department
+.\" University of Maryland at College Park
+.\" $FreeBSD$
+.\"
+.Dd November 16, 2000
+.Dt CRUNCHGEN 1
+.Os
+.Sh NAME
+.Nm crunchgen
+.Nd generates build environment for a crunched binary
+.Sh SYNOPSIS
+.Bk -words
+.Nm
+.Op Fl foql
+.Op Fl h Ar makefile-header-name
+.Op Fl m Ar makefile-name
+.Op Fl p Ar obj-prefix
+.Op Fl c Ar c-file-name
+.Op Fl e Ar exec-file-name
+.Op Ar conf-file
+.Ek
+.Sh DESCRIPTION
+A crunched binary is a program made up of many other programs linked
+together into a single executable.
+The crunched binary
+.Fn main
+function determines which component program to run by the contents of
+.Va argv[0] .
+The main reason to crunch programs together is for fitting
+as many programs as possible onto an installation or system recovery
+floppy.
+.Pp
+The
+.Nm
+utility reads in the specifications in
+.Ar conf-file
+for a crunched binary, and generates a
+.Pa Makefile
+and accompanying
+top-level C source file that when built creates the crunched executable
+file from the component programs.
+For each component program,
+.Nm
+can optionally attempt to determine the object (.o) files that make up
+the program from its source directory
+.Pa Makefile .
+This information is cached between runs.
+The
+.Nm
+utility uses the companion program
+.Xr crunchide 1
+to eliminate link-time conflicts between the component programs by
+hiding all unnecessary symbols.
+.Pp
+The
+.Nm
+utility places specific requirements on package
+.Pa Makefile Ns s
+which make it unsuitable for use with
+.No non- Ns Bx
+sources.
+In particular, the
+.Pa Makefile
+must contain the target
+.Ic depend ,
+and it must define all object files in the variable
+.Va OBJS .
+In some cases, you can use a fake
+.Pa Makefile :
+before looking for
+.Pa Makefile
+in the source directory
+.Pa foo ,
+.Nm
+looks for the file
+.Pa Makefile.foo
+in the current directory.
+.Pp
+After
+.Nm
+is run, the crunched binary can be built by running
+.Dq Li make -f <conf-name>.mk .
+The component programs' object files must already be built.
+An
+.Ic objs
+target, included in the output makefile, will
+run
+.Xr make 1
+in each component program's source dir to build the object
+files for the user.
+This is not done automatically since in release
+engineering circumstances it is generally not desirable to be
+modifying objects in other directories.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl c Ar c-file-name
+Set output C file name to
+.Ar c-file-name .
+The default name is
+.Pa <conf-name>.c .
+.It Fl e Ar exec-file-name
+Set crunched binary executable file name to
+.Ar exec-file-name .
+The default name is
+.Pa <conf-name> .
+.It Fl f
+Flush cache.
+Forces the recalculation of cached parameters.
+.It Fl l
+List names.
+Lists the names this binary will respond to.
+.It Fl h Ar makefile-header-name
+Set the name of a file to be included at the beginning of the
+.Pa Makefile Ns s
+generated by
+.Nm .
+This is useful to define some make variables such as
+.Va RELEASE_CRUNCH
+or similar, which might affect the behaviour of
+.Xr make 1
+and are annoying to pass through environment variables.
+.It Fl m Ar makefile-name
+Set output
+.Pa Makefile
+name to
+.Ar makefile-name .
+The default name is
+.Pa <conf-name>.mk .
+.It Fl o
+Add
+.Dq Li make obj
+rules to each program make target.
+.It Fl p Ar obj-prefix
+Set the pathname to be prepended to the
+.Ic srcdir
+when computing the
+.Ic objdir .
+If this option is not present, then the prefix used
+is the content of the
+.Ev MAKEOBJDIRPREFIX
+environment variable, or
+.Pa /usr/obj .
+.It Fl q
+Quiet operation.
+Status messages are suppressed.
+.El
+.Sh CRUNCHGEN CONFIGURATION FILE COMMANDS
+The
+.Nm
+utility reads specifications from the
+.Ar conf-file
+that describe the components of the crunched binary.
+In its simplest
+use, the component program names are merely listed along with the
+top-level source directories in which their sources can be found.
+The
+.Nm
+utility then calculates (via the source makefiles) and caches the
+list of object files and their locations.
+For more specialized
+situations, the user can specify by hand all the parameters that
+.Nm
+needs.
+.Pp
+The
+.Ar conf-file
+commands are as follows:
+.Bl -tag -width indent
+.It Ic srcdirs Ar dirname ...
+A list of source trees in which the source directories of the
+component programs can be found.
+These dirs are searched using the
+.Bx
+.Dq Pa <source-dir>/<progname>/
+convention.
+Multiple
+.Ic srcdirs
+lines can be specified.
+The directories are searched in the order they are given.
+.It Ic progs Ar progname ...
+A list of programs that make up the crunched binary.
+Multiple
+.Ic progs
+lines can be specified.
+.It Ic libs Ar libspec ...
+A list of library specifications to be included in the crunched binary link.
+Multiple
+.Ic libs
+lines can be specified.
+.It Ic buildopts Ar buildopts ...
+A list of build options to be added to every make target.
+.It Ic ln Ar progname linkname
+Causes the crunched binary to invoke
+.Ar progname
+whenever
+.Ar linkname
+appears in
+.Va argv[0] .
+This allows programs that change their behavior when
+run under different names to operate correctly.
+.El
+.Pp
+To handle specialized situations, such as when the source is not
+available or not built via a conventional
+.Pa Makefile ,
+the following
+.Ic special
+commands can be used to set
+.Nm
+parameters for a component program.
+.Bl -tag -width indent
+.It Ic special Ar progname Ic srcdir Ar pathname
+Set the source directory for
+.Ar progname .
+This is normally calculated by searching the specified
+.Ic srcdirs
+for a directory named
+.Ar progname .
+.It Ic special Ar progname Ic objdir Ar pathname
+Set the
+.Pa obj
+directory for
+.Ar progname .
+The
+.Pa obj
+directory is normally calculated by looking for a directory
+whose name is that of the source directory prepended by
+one of the following components, in order of priority:
+the
+.Fl p
+argument passed to the command line; or,
+the value of the
+.Ev MAKEOBJDIRPREFIX
+environment variable, or
+.Pa /usr/obj .
+If the directory is not found, the
+.Ic srcdir
+itself becomes the
+.Ic objdir .
+.It Ic special Ar progname Ic buildopts Ar buildopts
+Define a set of build options that should be added to
+.Xr make 1
+targets in addition to those specified using
+.Ic buildopts
+when processing
+.Ar progname .
+.It Ic special Ar progname Ic objs Ar object-file-name ...
+Set the list of object files for program
+.Ar progname .
+This is normally calculated by constructing a temporary makefile that includes
+.Dq Ic srcdir Ns / Ns Pa Makefile
+and outputs the value of
+.Va $(OBJS) .
+.It Ic special Ar progname Ic objpaths Ar full-pathname-to-object-file ...
+Sets the pathnames of the object files for program
+.Ar progname .
+This is normally calculated by prepending the
+.Ic objdir
+pathname to each file in the
+.Ic objs
+list.
+.It Ic special Ar progname Ic objvar Ar variable_name
+Sets the name of the
+.Xr make 1
+variable which holds the list of
+object files for program
+.Ar progname .
+This is normally
+.Va OBJS
+but some
+.Pa Makefile Ns s
+might like to use other conventions or
+prepend the program's name to the variable, e.g.\&
+.Va SSHD_OBJS .
+.It Ic special Ar progname Ic lib Ar library-name ...
+Specifies libraries to be linked with object files to produce
+.Ar progname Ns Pa .lo .
+This can be useful with libraries which redefine routines in
+the standard libraries, or poorly written libraries which
+reference symbols in the object files.
+.It Ic special Ar progname Ic keep Ar symbol-name ...
+Add specified list of symbols to the keep list for program
+.Ar progname .
+An underscore
+.Pq Ql _
+is prepended to each symbol and it becomes the argument to a
+.Fl k
+option for the
+.Xr crunchide 1
+phase.
+This option is to be used as a last resort as its use can cause a
+symbol conflict, however in certain instances it may be the only way to
+have a symbol resolve.
+.It Ic special Ar progname Ic ident Ar identifier
+Set the
+.Pa Makefile Ns / Ns Tn C
+identifier for
+.Ar progname .
+This is normally generated from a
+.Ar progname ,
+mapping
+.Ql -
+to
+.Ql _
+and ignoring all other non-identifier characters.
+This leads to programs named
+.Qq Li foo.bar
+and
+.Qq Li foobar
+to map to the same identifier.
+.El
+.Pp
+Only the
+.Ic objpaths
+parameter is actually needed by
+.Nm ,
+but it is calculated from
+.Ic objdir
+and
+.Ic objs ,
+which are in turn calculated from
+.Ic srcdir ,
+so is sometimes convenient to specify the earlier parameters and let
+.Nm
+calculate forward from there if it can.
+.Pp
+The makefile produced by
+.Nm
+contains an optional
+.Ic objs
+target that will build the object files for each component program by
+running
+.Xr make 1
+inside that program's source directory.
+For this to work the
+.Ic srcdir
+and
+.Ic objs
+parameters must also be valid.
+If they are not valid for a particular program, that
+program is skipped in the
+.Ic objs
+target.
+.Sh EXAMPLES
+Here is an example
+.Nm
+input conf file, named
+.Dq Pa kcopy.conf :
+.Pp
+.Bd -literal -offset indent
+srcdirs /usr/src/bin /usr/src/sbin
+
+progs test cp echo sh fsck halt init mount umount myinstall
+progs anotherprog
+ln test [ # test can be invoked via [
+ln sh -sh # init invokes the shell with "-sh" in argv[0]
+
+special myprog objpaths /homes/leroy/src/myinstall.o # no sources
+
+special anotherprog -DNO_FOO WITHOUT_BAR=YES
+
+libs -lutil -lcrypt
+.Ed
+.Pp
+This conf file specifies a small crunched binary consisting of some
+basic system utilities plus a homegrown install program
+.Dq Pa myinstall ,
+for which no source directory is specified, but its object file is
+specified directly with the
+.Ic special
+line.
+.Pp
+Additionally when
+.Dq Pa anotherprog
+is built the arguments
+.Pp
+.Dl -DNO_FOO WITHOUT_BAR=YES
+.Pp
+are added to all build targets.
+.Pp
+The crunched binary
+.Dq Pa kcopy
+can be built as follows:
+.Pp
+.Bd -literal -offset indent
+% crunchgen -m Makefile kcopy.conf # gen Makefile and kcopy.c
+% make objs # build the component programs' .o files
+% make # build the crunched binary kcopy
+% kcopy sh # test that this invokes a sh shell
+$ # it works!
+.Ed
+.Pp
+At this point the binary
+.Dq Pa kcopy
+can be copied onto an install floppy
+and hard-linked to the names of the component programs.
+.Sh SEE ALSO
+.Xr crunchide 1 ,
+.Xr make 1
+.Sh CAVEATS
+While
+.Nm
+takes care to eliminate link conflicts between the component programs
+of a crunched binary, conflicts are still possible between the
+libraries that are linked in.
+Some shuffling in the order of
+libraries may be required, and in some rare cases two libraries may
+have an unresolvable conflict and thus cannot be crunched together.
+.Pp
+Some versions of the
+.Bx
+build environment do not by default build the
+intermediate object file for single-source file programs.
+The
+.Dq Li make objs
+must then be used to get those object files built, or
+some other arrangements made.
+.Sh AUTHORS
+The
+.Nm
+utility was written by
+.An James da Silva Aq jds@cs.umd.edu .
+.Pp
+Copyright (c) 1994 University of Maryland.
+All Rights Reserved.
diff --git a/usr.sbin/crunch/crunchgen/crunchgen.c b/usr.sbin/crunch/crunchgen/crunchgen.c
new file mode 100644
index 0000000..8383b66
--- /dev/null
+++ b/usr.sbin/crunch/crunchgen/crunchgen.c
@@ -0,0 +1,1126 @@
+/*
+ * Copyright (c) 1994 University of Maryland
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of U.M. not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. U.M. makes no representations about the
+ * suitability of this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ *
+ * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
+ * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: James da Silva, Systems Design and Analysis Group
+ * Computer Science Department
+ * University of Maryland at College Park
+ *
+ * $FreeBSD$
+ */
+/*
+ * ========================================================================
+ * crunchgen.c
+ *
+ * Generates a Makefile and main C file for a crunched executable,
+ * from specs given in a .conf file.
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define CRUNCH_VERSION "0.2"
+
+#define MAXLINELEN 16384
+#define MAXFIELDS 2048
+
+
+/* internal representation of conf file: */
+
+/* simple lists of strings suffice for most parms */
+
+typedef struct strlst {
+ struct strlst *next;
+ char *str;
+} strlst_t;
+
+/* progs have structure, each field can be set with "special" or calculated */
+
+typedef struct prog {
+ struct prog *next; /* link field */
+ char *name; /* program name */
+ char *ident; /* C identifier for the program name */
+ char *srcdir;
+ char *realsrcdir;
+ char *objdir;
+ char *objvar; /* Makefile variable to replace OBJS */
+ strlst_t *objs, *objpaths;
+ strlst_t *buildopts;
+ strlst_t *keeplist;
+ strlst_t *links;
+ strlst_t *libs;
+ int goterror;
+} prog_t;
+
+
+/* global state */
+
+strlst_t *buildopts = NULL;
+strlst_t *srcdirs = NULL;
+strlst_t *libs = NULL;
+prog_t *progs = NULL;
+
+char confname[MAXPATHLEN], infilename[MAXPATHLEN];
+char outmkname[MAXPATHLEN], outcfname[MAXPATHLEN], execfname[MAXPATHLEN];
+char tempfname[MAXPATHLEN], cachename[MAXPATHLEN], curfilename[MAXPATHLEN];
+char outhdrname[MAXPATHLEN] ; /* user-supplied header for *.mk */
+char *objprefix; /* where are the objects ? */
+int linenum = -1;
+int goterror = 0;
+
+int verbose, readcache; /* options */
+int reading_cache;
+int makeobj = 0; /* add 'make obj' rules to the makefile */
+
+int list_mode;
+
+/* general library routines */
+
+void status(char *str);
+void out_of_memory(void);
+void add_string(strlst_t **listp, char *str);
+int is_dir(char *pathname);
+int is_nonempty_file(char *pathname);
+
+/* helper routines for main() */
+
+void usage(void);
+void parse_conf_file(void);
+void gen_outputs(void);
+
+
+int main(int argc, char **argv)
+{
+ char *p;
+ int optc;
+
+ verbose = 1;
+ readcache = 1;
+ *outmkname = *outcfname = *execfname = '\0';
+
+ p = getenv("MAKEOBJDIRPREFIX");
+ if (p == NULL || *p == '\0')
+ objprefix = "/usr/obj"; /* default */
+ else
+ if ((objprefix = strdup(p)) == NULL)
+ out_of_memory();
+
+ while((optc = getopt(argc, argv, "lh:m:c:e:p:foq")) != -1) {
+ switch(optc) {
+ case 'f':
+ readcache = 0;
+ break;
+ case 'o':
+ makeobj = 1;
+ break;
+ case 'q':
+ verbose = 0;
+ break;
+
+ case 'm':
+ strlcpy(outmkname, optarg, sizeof(outmkname));
+ break;
+ case 'p':
+ if ((objprefix = strdup(optarg)) == NULL)
+ out_of_memory();
+ break;
+
+ case 'h':
+ strlcpy(outhdrname, optarg, sizeof(outhdrname));
+ break;
+ case 'c':
+ strlcpy(outcfname, optarg, sizeof(outcfname));
+ break;
+ case 'e':
+ strlcpy(execfname, optarg, sizeof(execfname));
+ break;
+
+ case 'l':
+ list_mode++;
+ verbose = 0;
+ break;
+
+ case '?':
+ default:
+ usage();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1)
+ usage();
+
+ /*
+ * generate filenames
+ */
+
+ strlcpy(infilename, argv[0], sizeof(infilename));
+
+ /* confname = `basename infilename .conf` */
+
+ if ((p=strrchr(infilename, '/')) != NULL)
+ strlcpy(confname, p + 1, sizeof(confname));
+ else
+ strlcpy(confname, infilename, sizeof(confname));
+
+ if ((p=strrchr(confname, '.')) != NULL && !strcmp(p, ".conf"))
+ *p = '\0';
+
+ if (!*outmkname)
+ snprintf(outmkname, sizeof(outmkname), "%s.mk", confname);
+ if (!*outcfname)
+ snprintf(outcfname, sizeof(outcfname), "%s.c", confname);
+ if (!*execfname)
+ snprintf(execfname, sizeof(execfname), "%s", confname);
+
+ snprintf(cachename, sizeof(cachename), "%s.cache", confname);
+ snprintf(tempfname, sizeof(tempfname), "%s/crunchgen_%sXXXXXX",
+ getenv("TMPDIR") ? getenv("TMPDIR") : _PATH_TMP, confname);
+
+ parse_conf_file();
+ if (list_mode)
+ exit(goterror);
+
+ gen_outputs();
+
+ exit(goterror);
+}
+
+
+void usage(void)
+{
+ fprintf(stderr, "%s%s\n\t%s%s\n", "usage: crunchgen [-foq] ",
+ "[-h <makefile-header-name>] [-m <makefile>]",
+ "[-p <obj-prefix>] [-c <c-file-name>] [-e <exec-file>] ",
+ "<conffile>");
+ exit(1);
+}
+
+
+/*
+ * ========================================================================
+ * parse_conf_file subsystem
+ *
+ */
+
+/* helper routines for parse_conf_file */
+
+void parse_one_file(char *filename);
+void parse_line(char *line, int *fc, char **fv, int nf);
+void add_srcdirs(int argc, char **argv);
+void add_progs(int argc, char **argv);
+void add_link(int argc, char **argv);
+void add_libs(int argc, char **argv);
+void add_buildopts(int argc, char **argv);
+void add_special(int argc, char **argv);
+
+prog_t *find_prog(char *str);
+void add_prog(char *progname);
+
+
+void parse_conf_file(void)
+{
+ if (!is_nonempty_file(infilename))
+ errx(1, "fatal: input file \"%s\" not found", infilename);
+
+ parse_one_file(infilename);
+ if (readcache && is_nonempty_file(cachename)) {
+ reading_cache = 1;
+ parse_one_file(cachename);
+ }
+}
+
+
+void parse_one_file(char *filename)
+{
+ char *fieldv[MAXFIELDS];
+ int fieldc;
+ void (*f)(int c, char **v);
+ FILE *cf;
+ char line[MAXLINELEN];
+
+ snprintf(line, sizeof(line), "reading %s", filename);
+ status(line);
+ strlcpy(curfilename, filename, sizeof(curfilename));
+
+ if ((cf = fopen(curfilename, "r")) == NULL) {
+ warn("%s", curfilename);
+ goterror = 1;
+ return;
+ }
+
+ linenum = 0;
+ while (fgets(line, MAXLINELEN, cf) != NULL) {
+ linenum++;
+ parse_line(line, &fieldc, fieldv, MAXFIELDS);
+
+ if (fieldc < 1)
+ continue;
+
+ if (!strcmp(fieldv[0], "srcdirs"))
+ f = add_srcdirs;
+ else if(!strcmp(fieldv[0], "progs"))
+ f = add_progs;
+ else if(!strcmp(fieldv[0], "ln"))
+ f = add_link;
+ else if(!strcmp(fieldv[0], "libs"))
+ f = add_libs;
+ else if(!strcmp(fieldv[0], "buildopts"))
+ f = add_buildopts;
+ else if(!strcmp(fieldv[0], "special"))
+ f = add_special;
+ else {
+ warnx("%s:%d: skipping unknown command `%s'",
+ curfilename, linenum, fieldv[0]);
+ goterror = 1;
+ continue;
+ }
+
+ if (fieldc < 2) {
+ warnx("%s:%d: %s %s",
+ curfilename, linenum, fieldv[0],
+ "command needs at least 1 argument, skipping");
+ goterror = 1;
+ continue;
+ }
+
+ f(fieldc, fieldv);
+ }
+
+ if (ferror(cf)) {
+ warn("%s", curfilename);
+ goterror = 1;
+ }
+ fclose(cf);
+}
+
+
+void parse_line(char *line, int *fc, char **fv, int nf)
+{
+ char *p;
+
+ p = line;
+ *fc = 0;
+
+ while (1) {
+ while (isspace(*p))
+ p++;
+
+ if (*p == '\0' || *p == '#')
+ break;
+
+ if (*fc < nf)
+ fv[(*fc)++] = p;
+
+ while (*p && !isspace(*p) && *p != '#')
+ p++;
+
+ if (*p == '\0' || *p == '#')
+ break;
+
+ *p++ = '\0';
+ }
+
+ if (*p)
+ *p = '\0'; /* needed for '#' case */
+}
+
+
+void add_srcdirs(int argc, char **argv)
+{
+ int i;
+
+ for (i = 1; i < argc; i++) {
+ if (is_dir(argv[i]))
+ add_string(&srcdirs, argv[i]);
+ else {
+ warnx("%s:%d: `%s' is not a directory, skipping it",
+ curfilename, linenum, argv[i]);
+ goterror = 1;
+ }
+ }
+}
+
+
+void add_progs(int argc, char **argv)
+{
+ int i;
+
+ for (i = 1; i < argc; i++)
+ add_prog(argv[i]);
+}
+
+
+void add_prog(char *progname)
+{
+ prog_t *p1, *p2;
+
+ /* add to end, but be smart about dups */
+
+ for (p1 = NULL, p2 = progs; p2 != NULL; p1 = p2, p2 = p2->next)
+ if (!strcmp(p2->name, progname))
+ return;
+
+ p2 = malloc(sizeof(prog_t));
+ if(p2) {
+ memset(p2, 0, sizeof(prog_t));
+ p2->name = strdup(progname);
+ }
+ if (!p2 || !p2->name)
+ out_of_memory();
+
+ p2->next = NULL;
+ if (p1 == NULL)
+ progs = p2;
+ else
+ p1->next = p2;
+
+ p2->ident = NULL;
+ p2->srcdir = NULL;
+ p2->realsrcdir = NULL;
+ p2->objdir = NULL;
+ p2->links = NULL;
+ p2->libs = NULL;
+ p2->objs = NULL;
+ p2->keeplist = NULL;
+ p2->buildopts = NULL;
+ p2->goterror = 0;
+
+ if (list_mode)
+ printf("%s\n",progname);
+}
+
+
+void add_link(int argc, char **argv)
+{
+ int i;
+ prog_t *p = find_prog(argv[1]);
+
+ if (p == NULL) {
+ warnx("%s:%d: no prog %s previously declared, skipping link",
+ curfilename, linenum, argv[1]);
+ goterror = 1;
+ return;
+ }
+
+ for (i = 2; i < argc; i++) {
+ if (list_mode)
+ printf("%s\n",argv[i]);
+
+ add_string(&p->links, argv[i]);
+ }
+}
+
+
+void add_libs(int argc, char **argv)
+{
+ int i;
+
+ for(i = 1; i < argc; i++)
+ add_string(&libs, argv[i]);
+}
+
+
+void add_buildopts(int argc, char **argv)
+{
+ int i;
+
+ for (i = 1; i < argc; i++)
+ add_string(&buildopts, argv[i]);
+}
+
+
+void add_special(int argc, char **argv)
+{
+ int i;
+ prog_t *p = find_prog(argv[1]);
+
+ if (p == NULL) {
+ if (reading_cache)
+ return;
+
+ warnx("%s:%d: no prog %s previously declared, skipping special",
+ curfilename, linenum, argv[1]);
+ goterror = 1;
+ return;
+ }
+
+ if (!strcmp(argv[2], "ident")) {
+ if (argc != 4)
+ goto argcount;
+ if ((p->ident = strdup(argv[3])) == NULL)
+ out_of_memory();
+ } else if (!strcmp(argv[2], "srcdir")) {
+ if (argc != 4)
+ goto argcount;
+ if ((p->srcdir = strdup(argv[3])) == NULL)
+ out_of_memory();
+ } else if (!strcmp(argv[2], "objdir")) {
+ if(argc != 4)
+ goto argcount;
+ if((p->objdir = strdup(argv[3])) == NULL)
+ out_of_memory();
+ } else if (!strcmp(argv[2], "objs")) {
+ p->objs = NULL;
+ for (i = 3; i < argc; i++)
+ add_string(&p->objs, argv[i]);
+ } else if (!strcmp(argv[2], "objpaths")) {
+ p->objpaths = NULL;
+ for (i = 3; i < argc; i++)
+ add_string(&p->objpaths, argv[i]);
+ } else if (!strcmp(argv[2], "keep")) {
+ p->keeplist = NULL;
+ for(i = 3; i < argc; i++)
+ add_string(&p->keeplist, argv[i]);
+ } else if (!strcmp(argv[2], "objvar")) {
+ if(argc != 4)
+ goto argcount;
+ if ((p->objvar = strdup(argv[3])) == NULL)
+ out_of_memory();
+ } else if (!strcmp(argv[2], "buildopts")) {
+ p->buildopts = NULL;
+ for (i = 3; i < argc; i++)
+ add_string(&p->buildopts, argv[i]);
+ } else if (!strcmp(argv[2], "lib")) {
+ for (i = 3; i < argc; i++)
+ add_string(&p->libs, argv[i]);
+ } else {
+ warnx("%s:%d: bad parameter name `%s', skipping line",
+ curfilename, linenum, argv[2]);
+ goterror = 1;
+ }
+ return;
+
+ argcount:
+ warnx("%s:%d: too %s arguments, expected \"special %s %s <string>\"",
+ curfilename, linenum, argc < 4? "few" : "many", argv[1], argv[2]);
+ goterror = 1;
+}
+
+
+prog_t *find_prog(char *str)
+{
+ prog_t *p;
+
+ for (p = progs; p != NULL; p = p->next)
+ if (!strcmp(p->name, str))
+ return p;
+
+ return NULL;
+}
+
+
+/*
+ * ========================================================================
+ * gen_outputs subsystem
+ *
+ */
+
+/* helper subroutines */
+
+void remove_error_progs(void);
+void fillin_program(prog_t *p);
+void gen_specials_cache(void);
+void gen_output_makefile(void);
+void gen_output_cfile(void);
+
+void fillin_program_objs(prog_t *p, char *path);
+void top_makefile_rules(FILE *outmk);
+void prog_makefile_rules(FILE *outmk, prog_t *p);
+void output_strlst(FILE *outf, strlst_t *lst);
+char *genident(char *str);
+char *dir_search(char *progname);
+
+
+void gen_outputs(void)
+{
+ prog_t *p;
+
+ for (p = progs; p != NULL; p = p->next)
+ fillin_program(p);
+
+ remove_error_progs();
+ gen_specials_cache();
+ gen_output_cfile();
+ gen_output_makefile();
+ status("");
+ fprintf(stderr,
+ "Run \"make -f %s\" to build crunched binary.\n", outmkname);
+}
+
+/*
+ * run the makefile for the program to find which objects are necessary
+ */
+void fillin_program(prog_t *p)
+{
+ char path[MAXPATHLEN];
+ char line[MAXLINELEN];
+ FILE *f;
+
+ snprintf(line, MAXLINELEN, "filling in parms for %s", p->name);
+ status(line);
+
+ if (!p->ident)
+ p->ident = genident(p->name);
+
+ /* look for the source directory if one wasn't specified by a special */
+ if (!p->srcdir) {
+ p->srcdir = dir_search(p->name);
+ }
+
+ /* Determine the actual srcdir (maybe symlinked). */
+ if (p->srcdir) {
+ snprintf(line, MAXLINELEN, "cd %s && echo -n `/bin/pwd`",
+ p->srcdir);
+ f = popen(line,"r");
+ if (!f)
+ errx(1, "Can't execute: %s\n", line);
+
+ path[0] = '\0';
+ fgets(path, sizeof path, f);
+ if (pclose(f))
+ errx(1, "Can't execute: %s\n", line);
+
+ if (!*path)
+ errx(1, "Can't perform pwd on: %s\n", p->srcdir);
+
+ p->realsrcdir = strdup(path);
+ }
+
+ /* Unless the option to make object files was specified the
+ * the objects will be built in the source directory unless
+ * an object directory already exists.
+ */
+ if (!makeobj && !p->objdir && p->srcdir) {
+ snprintf(line, sizeof line, "%s/%s", objprefix, p->realsrcdir);
+ if (is_dir(line)) {
+ if ((p->objdir = strdup(line)) == NULL)
+ out_of_memory();
+ } else
+ p->objdir = p->realsrcdir;
+ }
+
+ /*
+ * XXX look for a Makefile.{name} in local directory first.
+ * This lets us override the original Makefile.
+ */
+ snprintf(path, sizeof(path), "Makefile.%s", p->name);
+ if (is_nonempty_file(path)) {
+ snprintf(line, MAXLINELEN, "Using %s for %s", path, p->name);
+ status(line);
+ } else
+ if (p->srcdir)
+ snprintf(path, sizeof(path), "%s/Makefile", p->srcdir);
+ if (!p->objs && p->srcdir && is_nonempty_file(path))
+ fillin_program_objs(p, path);
+
+ if (!p->srcdir && !p->objdir && verbose)
+ warnx("%s: %s: %s",
+ "warning: could not find source directory",
+ infilename, p->name);
+ if (!p->objs && verbose)
+ warnx("%s: %s: warning: could not find any .o files",
+ infilename, p->name);
+
+ if ((!p->srcdir || !p->objdir) && !p->objs)
+ p->goterror = 1;
+}
+
+void fillin_program_objs(prog_t *p, char *path)
+{
+ char *obj, *cp;
+ int fd, rc;
+ FILE *f;
+ char *objvar="OBJS";
+ strlst_t *s;
+ char line[MAXLINELEN];
+
+ /* discover the objs from the srcdir Makefile */
+
+ if ((fd = mkstemp(tempfname)) == -1) {
+ perror(tempfname);
+ exit(1);
+ }
+ if ((f = fdopen(fd, "w")) == NULL) {
+ warn("%s", tempfname);
+ goterror = 1;
+ return;
+ }
+ if (p->objvar)
+ objvar = p->objvar;
+
+ /*
+ * XXX include outhdrname (e.g. to contain Make variables)
+ */
+ if (outhdrname[0] != '\0')
+ fprintf(f, ".include \"%s\"\n", outhdrname);
+ fprintf(f, ".include \"%s\"\n", path);
+ if (buildopts) {
+ fprintf(f, "BUILDOPTS+=");
+ output_strlst(f, buildopts);
+ }
+ fprintf(f, ".if defined(PROG) && !defined(%s)\n", objvar);
+ fprintf(f, "%s=${PROG}.o\n", objvar);
+ fprintf(f, ".endif\n");
+ fprintf(f, "loop:\n\t@echo 'OBJS= '${%s}\n", objvar);
+
+ fprintf(f, "crunchgen_objs:\n\t@make -f %s $(BUILDOPTS) $(%s_OPTS)",
+ tempfname, p->ident);
+ for (s = p->buildopts; s != NULL; s = s->next)
+ fprintf(f, " %s", s->str);
+ fprintf(f, " loop\n");
+
+ fclose(f);
+
+ snprintf(line, MAXLINELEN, "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;
+ char line[MAXLINELEN];
+
+ snprintf(line, MAXLINELEN, "generating %s", cachename);
+ status(line);
+
+ if ((cachef = fopen(cachename, "w")) == NULL) {
+ warn("%s", cachename);
+ goterror = 1;
+ return;
+ }
+
+ fprintf(cachef, "# %s - parm cache generated from %s by crunchgen "
+ " %s\n\n",
+ cachename, infilename, CRUNCH_VERSION);
+
+ for (p = progs; p != NULL; p = p->next) {
+ fprintf(cachef, "\n");
+ if (p->srcdir)
+ fprintf(cachef, "special %s srcdir %s\n",
+ p->name, p->srcdir);
+ if (p->objdir)
+ fprintf(cachef, "special %s objdir %s\n",
+ p->name, p->objdir);
+ if (p->objs) {
+ fprintf(cachef, "special %s objs", p->name);
+ output_strlst(cachef, p->objs);
+ }
+ if (p->objpaths) {
+ fprintf(cachef, "special %s objpaths", p->name);
+ output_strlst(cachef, p->objpaths);
+ }
+ }
+ fclose(cachef);
+}
+
+
+void gen_output_makefile(void)
+{
+ prog_t *p;
+ FILE *outmk;
+ char line[MAXLINELEN];
+
+ snprintf(line, MAXLINELEN, "generating %s", outmkname);
+ status(line);
+
+ if ((outmk = fopen(outmkname, "w")) == NULL) {
+ warn("%s", outmkname);
+ goterror = 1;
+ return;
+ }
+
+ fprintf(outmk, "# %s - generated from %s by crunchgen %s\n\n",
+ outmkname, infilename, CRUNCH_VERSION);
+
+ if (outhdrname[0] != '\0')
+ fprintf(outmk, ".include \"%s\"\n", outhdrname);
+
+ top_makefile_rules(outmk);
+ for (p = progs; p != NULL; p = p->next)
+ prog_makefile_rules(outmk, p);
+
+ fprintf(outmk, "\n# ========\n");
+ fclose(outmk);
+}
+
+
+void gen_output_cfile(void)
+{
+ extern char *crunched_skel[];
+ char **cp;
+ FILE *outcf;
+ prog_t *p;
+ strlst_t *s;
+ char line[MAXLINELEN];
+
+ snprintf(line, MAXLINELEN, "generating %s", outcfname);
+ status(line);
+
+ if((outcf = fopen(outcfname, "w")) == NULL) {
+ warn("%s", outcfname);
+ goterror = 1;
+ return;
+ }
+
+ fprintf(outcf,
+ "/* %s - generated from %s by crunchgen %s */\n",
+ outcfname, infilename, CRUNCH_VERSION);
+
+ fprintf(outcf, "#define EXECNAME \"%s\"\n", execfname);
+ for (cp = crunched_skel; *cp != NULL; cp++)
+ fprintf(outcf, "%s\n", *cp);
+
+ for (p = progs; p != NULL; p = p->next)
+ fprintf(outcf, "extern int _crunched_%s_stub();\n", p->ident);
+
+ fprintf(outcf, "\nstruct stub entry_points[] = {\n");
+ for (p = progs; p != NULL; p = p->next) {
+ fprintf(outcf, "\t{ \"%s\", _crunched_%s_stub },\n",
+ p->name, p->ident);
+ for (s = p->links; s != NULL; s = s->next)
+ fprintf(outcf, "\t{ \"%s\", _crunched_%s_stub },\n",
+ s->str, p->ident);
+ }
+
+ fprintf(outcf, "\t{ EXECNAME, crunched_main },\n");
+ fprintf(outcf, "\t{ NULL, NULL }\n};\n");
+ fclose(outcf);
+}
+
+
+char *genident(char *str)
+{
+ char *n, *s, *d;
+
+ /*
+ * generates a Makefile/C identifier from a program name,
+ * mapping '-' to '_' and ignoring all other non-identifier
+ * characters. This leads to programs named "foo.bar" and
+ * "foobar" to map to the same identifier.
+ */
+
+ if ((n = strdup(str)) == NULL)
+ return NULL;
+ for (d = s = n; *s != '\0'; s++) {
+ if (*s == '-')
+ *d++ = '_';
+ else if (*s == '_' || isalnum(*s))
+ *d++ = *s;
+ }
+ *d = '\0';
+ return n;
+}
+
+
+char *dir_search(char *progname)
+{
+ char path[MAXPATHLEN];
+ strlst_t *dir;
+ char *srcdir;
+
+ for (dir = srcdirs; dir != NULL; dir = dir->next) {
+ snprintf(path, MAXPATHLEN, "%s/%s", dir->str, progname);
+ if (!is_dir(path))
+ continue;
+
+ if ((srcdir = strdup(path)) == NULL)
+ out_of_memory();
+
+ return srcdir;
+ }
+ return NULL;
+}
+
+
+void top_makefile_rules(FILE *outmk)
+{
+ prog_t *p;
+
+ fprintf(outmk, "LIBS+=");
+ output_strlst(outmk, libs);
+
+ if (makeobj) {
+ fprintf(outmk, "MAKEOBJDIRPREFIX?=%s\n", objprefix);
+ fprintf(outmk, "MAKE=env MAKEOBJDIRPREFIX=$(MAKEOBJDIRPREFIX) "
+ "make\n");
+ } else {
+ fprintf(outmk, "MAKE=make\n");
+ }
+
+ if (buildopts) {
+ fprintf(outmk, "BUILDOPTS+=");
+ output_strlst(outmk, buildopts);
+ }
+
+ fprintf(outmk, "CRUNCHED_OBJS=");
+ for (p = progs; p != NULL; p = p->next)
+ fprintf(outmk, " %s.lo", p->name);
+ fprintf(outmk, "\n");
+
+ fprintf(outmk, "SUBMAKE_TARGETS=");
+ for (p = progs; p != NULL; p = p->next)
+ fprintf(outmk, " %s_make", p->ident);
+ fprintf(outmk, "\nSUBCLEAN_TARGETS=");
+ for (p = progs; p != NULL; p = p->next)
+ fprintf(outmk, " %s_clean", p->ident);
+ fprintf(outmk, "\n\n");
+
+ fprintf(outmk, "all: objs exe\nobjs: $(SUBMAKE_TARGETS)\n");
+ fprintf(outmk, "exe: %s\n", execfname);
+ fprintf(outmk, "%s: %s.o $(CRUNCHED_OBJS)\n", execfname, execfname);
+ fprintf(outmk, "\t$(CC) -static -o %s %s.o $(CRUNCHED_OBJS) $(LIBS)\n",
+ execfname, execfname);
+ fprintf(outmk, "\tstrip %s\n", execfname);
+ fprintf(outmk, "realclean: clean subclean\n");
+ fprintf(outmk, "clean:\n\trm -f %s *.lo *.o *_stub.c\n", execfname);
+ fprintf(outmk, "subclean: $(SUBCLEAN_TARGETS)\n");
+}
+
+
+void prog_makefile_rules(FILE *outmk, prog_t *p)
+{
+ strlst_t *lst;
+
+ fprintf(outmk, "\n# -------- %s\n\n", p->name);
+
+ fprintf(outmk, "%s_OBJDIR=", p->ident);
+ if (p->objdir)
+ fprintf(outmk, "%s", p->objdir);
+ else
+ fprintf(outmk, "$(MAKEOBJDIRPREFIX)/$(%s_REALSRCDIR)\n",
+ p->ident);
+ fprintf(outmk, "\n");
+
+ if (p->srcdir && p->objs) {
+ fprintf(outmk, "%s_SRCDIR=%s\n", p->ident, p->srcdir);
+ fprintf(outmk, "%s_REALSRCDIR=%s\n", p->ident, p->realsrcdir);
+
+ fprintf(outmk, "%s_OBJS=", p->ident);
+ output_strlst(outmk, p->objs);
+ if (p->buildopts != NULL) {
+ fprintf(outmk, "%s_OPTS+=", p->ident);
+ output_strlst(outmk, p->buildopts);
+ }
+ fprintf(outmk, "%s_make:\n", p->ident);
+ fprintf(outmk, "\t(cd $(%s_SRCDIR) && ", p->ident);
+ if (makeobj)
+ fprintf(outmk, "$(MAKE) obj && ");
+ fprintf(outmk, "\\\n");
+ fprintf(outmk, "\t\t$(MAKE) $(BUILDOPTS) $(%s_OPTS) depend &&",
+ p->ident);
+ fprintf(outmk, "\\\n");
+ fprintf(outmk, "\t\t$(MAKE) $(BUILDOPTS) $(%s_OPTS) "
+ "$(%s_OBJS))",
+ p->ident, p->ident);
+ fprintf(outmk, "\n");
+ fprintf(outmk, "%s_clean:\n", p->ident);
+ fprintf(outmk, "\t(cd $(%s_SRCDIR) && $(MAKE) $(BUILDOPTS) clean cleandepend)\n\n",
+ p->ident);
+ } else {
+ fprintf(outmk, "%s_make:\n", p->ident);
+ fprintf(outmk, "\t@echo \"** cannot make objs for %s\"\n\n",
+ p->name);
+ }
+
+ fprintf(outmk, "%s_OBJPATHS=", p->ident);
+ if (p->objpaths)
+ output_strlst(outmk, p->objpaths);
+ else {
+ for (lst = p->objs; lst != NULL; lst = lst->next) {
+ fprintf(outmk, " $(%s_OBJDIR)/%s", p->ident, lst->str);
+ }
+ fprintf(outmk, "\n");
+ }
+ if (p->libs) {
+ fprintf(outmk, "%s_LIBS=", p->ident);
+ output_strlst(outmk, p->libs);
+ }
+
+ fprintf(outmk, "%s_stub.c:\n", p->name);
+ fprintf(outmk, "\techo \""
+ "int _crunched_%s_stub(int argc, char **argv, char **envp)"
+ "{return main(argc,argv,envp);}\" >%s_stub.c\n",
+ p->ident, p->name);
+ fprintf(outmk, "%s.lo: %s_stub.o $(%s_OBJPATHS)",
+ p->name, p->name, p->ident);
+ if (p->libs)
+ fprintf(outmk, " $(%s_LIBS)", p->ident);
+ fprintf(outmk, "\n");
+ fprintf(outmk, "\tld -dc -r -o %s.lo %s_stub.o $(%s_OBJPATHS)",
+ p->name, p->name, p->ident);
+ if (p->libs)
+ fprintf(outmk, " $(%s_LIBS)", p->ident);
+ fprintf(outmk, "\n");
+ fprintf(outmk, "\tcrunchide -k _crunched_%s_stub ", p->ident);
+ for (lst = p->keeplist; lst != NULL; lst = lst->next)
+ fprintf(outmk, "-k _%s ", lst->str);
+ fprintf(outmk, "%s.lo\n", p->name);
+}
+
+void output_strlst(FILE *outf, strlst_t *lst)
+{
+ for (; lst != NULL; lst = lst->next)
+ fprintf(outf, " %s", lst->str);
+ fprintf(outf, "\n");
+}
+
+
+/*
+ * ========================================================================
+ * general library routines
+ *
+ */
+
+void status(char *str)
+{
+ static int lastlen = 0;
+ int len, spaces;
+
+ if (!verbose)
+ return;
+
+ len = strlen(str);
+ spaces = lastlen - len;
+ if (spaces < 1)
+ spaces = 1;
+
+ fprintf(stderr, " [%s]%*.*s\r", str, spaces, spaces, " ");
+ fflush(stderr);
+ lastlen = len;
+}
+
+
+void out_of_memory(void)
+{
+ err(1, "%s: %d: out of memory, stopping", infilename, linenum);
+}
+
+
+void add_string(strlst_t **listp, char *str)
+{
+ strlst_t *p1, *p2;
+
+ /* add to end, but be smart about dups */
+
+ for (p1 = NULL, p2 = *listp; p2 != NULL; p1 = p2, p2 = p2->next)
+ if (!strcmp(p2->str, str))
+ return;
+
+ p2 = malloc(sizeof(strlst_t));
+ if (p2) {
+ p2->next = NULL;
+ p2->str = strdup(str);
+ }
+ if (!p2 || !p2->str)
+ out_of_memory();
+
+ if (p1 == NULL)
+ *listp = p2;
+ else
+ p1->next = p2;
+}
+
+
+int is_dir(char *pathname)
+{
+ struct stat buf;
+
+ if (stat(pathname, &buf) == -1)
+ return 0;
+
+ return S_ISDIR(buf.st_mode);
+}
+
+int is_nonempty_file(char *pathname)
+{
+ struct stat buf;
+
+ if (stat(pathname, &buf) == -1)
+ return 0;
+
+ return S_ISREG(buf.st_mode) && buf.st_size > 0;
+}
diff --git a/usr.sbin/crunch/crunchgen/mkskel.sh b/usr.sbin/crunch/crunchgen/mkskel.sh
new file mode 100644
index 0000000..fd53d78
--- /dev/null
+++ b/usr.sbin/crunch/crunchgen/mkskel.sh
@@ -0,0 +1,15 @@
+#! /bin/sh
+# idea and sed lines taken straight from flex
+
+cat <<!EOF
+/* File created via mkskel.sh */
+
+char *crunched_skel[] = {
+!EOF
+
+sed 's/\\/&&/g' $* | sed 's/"/\\"/g' | sed 's/.*/ "&",/'
+
+cat <<!EOF
+ 0
+};
+!EOF
diff --git a/usr.sbin/crunch/crunchide/Makefile b/usr.sbin/crunch/crunchide/Makefile
new file mode 100644
index 0000000..7b6abed
--- /dev/null
+++ b/usr.sbin/crunch/crunchide/Makefile
@@ -0,0 +1,23 @@
+# $FreeBSD$
+
+PROG= crunchide
+SRCS= crunchide.c
+
+TARGET_ARCH?= ${MACHINE_ARCH}
+
+.if ${TARGET_ARCH} == i386 && ${MACHINE_ARCH} == i386
+CFLAGS+=-DNLIST_AOUT
+SRCS+= exec_aout.c
+.endif
+
+.if ${TARGET_ARCH} == alpha || ${TARGET_ARCH} == ia64 || \
+ ${TARGET_ARCH} == sparc64
+CFLAGS+=-DNLIST_ELF64
+SRCS+= exec_elf64.c
+exec_elf64.o: exec_elf32.c
+.else
+CFLAGS+=-DNLIST_ELF32
+SRCS+= exec_elf32.c
+.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/crunch/crunchide/crunchide.1 b/usr.sbin/crunch/crunchide/crunchide.1
new file mode 100644
index 0000000..9dcee18
--- /dev/null
+++ b/usr.sbin/crunch/crunchide/crunchide.1
@@ -0,0 +1,85 @@
+.\"
+.\" Copyright (c) 1994 University of Maryland
+.\" All Rights Reserved.
+.\"
+.\" Permission to use, copy, modify, distribute, and sell this software and its
+.\" documentation for any purpose is hereby granted without fee, provided that
+.\" the above copyright notice appear in all copies and that both that
+.\" copyright notice and this permission notice appear in supporting
+.\" documentation, and that the name of U.M. not be used in advertising or
+.\" publicity pertaining to distribution of the software without specific,
+.\" written prior permission. U.M. makes no representations about the
+.\" suitability of this software for any purpose. It is provided "as is"
+.\" without express or implied warranty.
+.\"
+.\" U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
+.\" BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+.\" IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.\" Author: James da Silva, Systems Design and Analysis Group
+.\" Computer Science Department
+.\" University of Maryland at College Park
+.\" $FreeBSD$
+.\"
+.Dd June 14, 1994
+.Dt CRUNCHIDE 1
+.Os
+.Sh NAME
+.Nm crunchide
+.Nd hides symbol names from ld, for crunching programs together
+.Sh SYNOPSIS
+.Nm
+.Op Fl f Ar keep-list-file
+.Op Fl k Ar keep-symbol
+.Op Ar object-file ...
+.Sh DESCRIPTION
+The
+.Nm
+utility hides the global symbols of
+.Ar object-file
+such that they are ignored by subsequent runs of the linker,
+.Xr ld 1 .
+Some symbols may be left visible via the
+.Fl k Ar keep-symbol
+and
+.Fl f Ar keep-list-file
+options. The
+.Ar keep-list-file
+must contain a list of symbols to keep visible, one symbol per line.
+The names given by
+.Ar keep-symbol
+or in
+.Ar keep-list-file
+should be C names. For example,
+to keep the C function
+.Dq foo
+visible, the option
+.Dq -k foo
+should be used.
+.Pp
+The
+.Nm
+utility is designed as a companion program for
+.Xr crunchgen 1 ,
+which automates the process of creating crunched binaries from
+multiple component programs.
+.Sh SEE ALSO
+.Xr crunchgen 1 ,
+.Xr ld 1
+.Sh AUTHORS
+The
+.Nm crunch
+utility was written by
+.An James da Silva Aq jds@cs.umd.edu .
+.Pp
+Copyright (c) 1994 University of Maryland. All Rights Reserved.
+.Pp
+Chris Demetriou <cgd@netbsd.org> reorganized
+.Nm
+so that it supported multiple object formats, and added
+ELF object support and ECOFF object recognition.
+.Pp
+Copyright (c) 1997 Christopher G. Demetriou. All Rights Reserved.
diff --git a/usr.sbin/crunch/crunchide/crunchide.c b/usr.sbin/crunch/crunchide/crunchide.c
new file mode 100644
index 0000000..f50d3e5
--- /dev/null
+++ b/usr.sbin/crunch/crunchide/crunchide.c
@@ -0,0 +1,268 @@
+/* $NetBSD: crunchide.c,v 1.8 1997/11/01 06:51:45 lukem Exp $ */
+/* $FreeBSD$ */
+/*
+ * Copyright (c) 1997 Christopher G. Demetriou. All rights reserved.
+ * Copyright (c) 1994 University of Maryland
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of U.M. not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. U.M. makes no representations about the
+ * suitability of this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ *
+ * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
+ * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: James da Silva, Systems Design and Analysis Group
+ * Computer Science Department
+ * University of Maryland at College Park
+ */
+/*
+ * crunchide.c - tiptoes through an a.out symbol table, hiding all defined
+ * global symbols. Allows the user to supply a "keep list" of symbols
+ * that are not to be hidden. This program relies on the use of the
+ * linker's -dc flag to actually put global bss data into the file's
+ * bss segment (rather than leaving it as undefined "common" data).
+ *
+ * The point of all this is to allow multiple programs to be linked
+ * together without getting multiple-defined errors.
+ *
+ * For example, consider a program "foo.c". It can be linked with a
+ * small stub routine, called "foostub.c", eg:
+ * int foo_main(int argc, char **argv){ return main(argc, argv); }
+ * like so:
+ * cc -c foo.c foostub.c
+ * ld -dc -r foo.o foostub.o -o foo.combined.o
+ * crunchide -k _foo_main foo.combined.o
+ * at this point, foo.combined.o can be linked with another program
+ * and invoked with "foo_main(argc, argv)". foo's main() and any
+ * other globals are hidden and will not conflict with other symbols.
+ *
+ * TODO:
+ * - resolve the theoretical hanging reloc problem (see check_reloc()
+ * below). I have yet to see this problem actually occur in any real
+ * program. In what cases will gcc/gas generate code that needs a
+ * relative reloc from a global symbol, other than PIC? The
+ * solution is to not hide the symbol from the linker in this case,
+ * but to generate some random name for it so that it doesn't link
+ * with anything but holds the place for the reloc.
+ * - arrange that all the BSS segments start at the same address, so
+ * that the final crunched binary BSS size is the max of all the
+ * component programs' BSS sizes, rather than their sum.
+ */
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: crunchide.c,v 1.8 1997/11/01 06:51:45 lukem Exp $");
+#endif
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <a.out.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+
+#include "extern.h"
+
+char *pname = "crunchide";
+
+void usage(void);
+
+void add_to_keep_list(char *symbol);
+void add_file_to_keep_list(char *filename);
+
+int hide_syms(const char *filename);
+
+int verbose;
+
+int main __P((int, char *[]));
+
+int main(argc, argv)
+int argc;
+char **argv;
+{
+ int ch, errors;
+
+ if(argc > 0) pname = argv[0];
+
+ while ((ch = getopt(argc, argv, "k:f:v")) != -1)
+ switch(ch) {
+ case 'k':
+ add_to_keep_list(optarg);
+ break;
+ case 'f':
+ add_file_to_keep_list(optarg);
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ default:
+ usage();
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if(argc == 0) usage();
+
+ errors = 0;
+ while(argc) {
+ if (hide_syms(*argv))
+ errors = 1;
+ argc--, argv++;
+ }
+
+ return errors;
+}
+
+void usage(void)
+{
+ fprintf(stderr,
+ "usage: %s [-k <symbol-name>] [-f <keep-list-file>] <files> ...\n",
+ pname);
+ exit(1);
+}
+
+/* ---------------------------- */
+
+struct keep {
+ struct keep *next;
+ char *sym;
+} *keep_list;
+
+void add_to_keep_list(char *symbol)
+{
+ struct keep *newp, *prevp, *curp;
+ int cmp;
+
+ cmp = 0;
+
+ for(curp = keep_list, prevp = NULL; curp; prevp = curp, curp = curp->next)
+ if((cmp = strcmp(symbol, curp->sym)) <= 0) break;
+
+ if(curp && cmp == 0)
+ return; /* already in table */
+
+ newp = (struct keep *) malloc(sizeof(struct keep));
+ if(newp) newp->sym = strdup(symbol);
+ if(newp == NULL || newp->sym == NULL) {
+ fprintf(stderr, "%s: out of memory for keep list\n", pname);
+ exit(1);
+ }
+
+ newp->next = curp;
+ if(prevp) prevp->next = newp;
+ else keep_list = newp;
+}
+
+int in_keep_list(const char *symbol)
+{
+ struct keep *curp;
+ int cmp;
+
+ cmp = 0;
+
+ for(curp = keep_list; curp; curp = curp->next)
+ if((cmp = strcmp(symbol, curp->sym)) <= 0) break;
+
+ return curp && cmp == 0;
+}
+
+void add_file_to_keep_list(char *filename)
+{
+ FILE *keepf;
+ char symbol[1024];
+ int len;
+
+ if((keepf = fopen(filename, "r")) == NULL) {
+ perror(filename);
+ usage();
+ }
+
+ while(fgets(symbol, 1024, keepf)) {
+ len = strlen(symbol);
+ if(len && symbol[len-1] == '\n')
+ symbol[len-1] = '\0';
+
+ add_to_keep_list(symbol);
+ }
+ fclose(keepf);
+}
+
+/* ---------------------------- */
+
+struct {
+ const char *name;
+ int (*check)(int, const char *); /* 1 if match, zero if not */
+ int (*hide)(int, const char *); /* non-zero if error */
+} exec_formats[] = {
+#ifdef NLIST_AOUT
+ { "a.out", check_aout, hide_aout, },
+#endif
+#ifdef NLIST_ECOFF
+ { "ECOFF", check_elf64, hide_elf64, },
+#endif
+#ifdef NLIST_ELF32
+ { "ELF32", check_elf32, hide_elf32, },
+#endif
+#ifdef NLIST_ELF64
+ { "ELF64", check_elf64, hide_elf64, },
+#endif
+};
+
+int hide_syms(const char *filename)
+{
+ int fd, i, n, rv;
+
+ fd = open(filename, O_RDWR, 0);
+ if (fd == -1) {
+ perror(filename);
+ return 1;
+ }
+
+ rv = 0;
+
+ n = sizeof exec_formats / sizeof exec_formats[0];
+ for (i = 0; i < n; i++) {
+ if (lseek(fd, 0, SEEK_SET) != 0) {
+ perror(filename);
+ goto err;
+ }
+ if ((*exec_formats[i].check)(fd, filename) != 0)
+ break;
+ }
+ if (i == n) {
+ fprintf(stderr, "%s: unknown executable format\n", filename);
+ goto err;
+ }
+
+ if (verbose)
+ fprintf(stderr, "%s is an %s binary\n", filename,
+ exec_formats[i].name);
+
+ if (lseek(fd, 0, SEEK_SET) != 0) {
+ perror(filename);
+ goto err;
+ }
+ rv = (*exec_formats[i].hide)(fd, filename);
+
+out:
+ close (fd);
+ return (rv);
+
+err:
+ rv = 1;
+ goto out;
+}
diff --git a/usr.sbin/crunch/crunchide/endian.h b/usr.sbin/crunch/crunchide/endian.h
new file mode 100644
index 0000000..2234f78
--- /dev/null
+++ b/usr.sbin/crunch/crunchide/endian.h
@@ -0,0 +1,57 @@
+/*
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+
+#if __FreeBSD_version >= 500034
+#include <sys/endian.h>
+#else
+#include <machine/endian.h>
+
+#define bswap16(x) (uint16_t) \
+ ((x >> 8) | (x << 8))
+
+#define bswap32(x) (uint32_t) \
+ ((x >> 24) | ((x >> 8) & 0xff00) | ((x << 8) & 0xff0000) | (x << 24))
+
+#define bswap64(x) (uint64_t) \
+ ((x >> 56) | ((x >> 40) & 0xff00) | ((x >> 24) & 0xff0000) | \
+ ((x >> 8) & 0xff000000) | ((x << 8) & ((uint64_t)0xff << 32)) | \
+ ((x << 24) & ((uint64_t)0xff << 40)) | \
+ ((x << 40) & ((uint64_t)0xff << 48)) | ((x << 56)))
+
+/*
+ * Host to big endian, host to little endian, big endian to host, and little
+ * endian to host byte order functions as detailed in byteorder(9).
+ */
+#if _BYTE_ORDER == _LITTLE_ENDIAN
+#define htobe16(x) bswap16((uint16_t)(x))
+#define htobe32(x) bswap32((uint32_t)(x))
+#define htobe64(x) bswap64((uint64_t)(x))
+#define htole16(x) ((uint16_t)(x))
+#define htole32(x) ((uint32_t)(x))
+#define htole64(x) ((uint64_t)(x))
+
+#define be16toh(x) bswap16((uint16_t)(x))
+#define be32toh(x) bswap32((uint32_t)(x))
+#define be64toh(x) bswap64((uint64_t)(x))
+#define le16toh(x) ((uint16_t)(x))
+#define le32toh(x) ((uint32_t)(x))
+#define le64toh(x) ((uint64_t)(x))
+#else /* _BYTE_ORDER != _LITTLE_ENDIAN */
+#define htobe16(x) ((uint16_t)(x))
+#define htobe32(x) ((uint32_t)(x))
+#define htobe64(x) ((uint64_t)(x))
+#define htole16(x) bswap16((uint16_t)(x))
+#define htole32(x) bswap32((uint32_t)(x))
+#define htole64(x) bswap64((uint64_t)(x))
+
+#define be16toh(x) ((uint16_t)(x))
+#define be32toh(x) ((uint32_t)(x))
+#define be64toh(x) ((uint64_t)(x))
+#define le16toh(x) bswap16((uint16_t)(x))
+#define le32toh(x) bswap32((uint32_t)(x))
+#define le64toh(x) bswap64((uint64_t)(x))
+#endif /* _BYTE_ORDER == _LITTLE_ENDIAN */
+#endif
diff --git a/usr.sbin/crunch/crunchide/exec_aout.c b/usr.sbin/crunch/crunchide/exec_aout.c
new file mode 100644
index 0000000..1300efd
--- /dev/null
+++ b/usr.sbin/crunch/crunchide/exec_aout.c
@@ -0,0 +1,196 @@
+/* $NetBSD: exec_aout.c,v 1.6 1997/08/02 21:30:17 perry Exp $ */
+/*
+ * Copyright (c) 1997 Christopher G. Demetriou. All rights reserved.
+ * Copyright (c) 1994 University of Maryland
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of U.M. not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. U.M. makes no representations about the
+ * suitability of this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ *
+ * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
+ * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: James da Silva, Systems Design and Analysis Group
+ * Computer Science Department
+ * University of Maryland at College Park
+ */
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: exec_aout.c,v 1.6 1997/08/02 21:30:17 perry Exp $");
+__FBSDID("$FreeBSD$");
+#endif
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <a.out.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+
+#include "extern.h"
+
+#if defined(NLIST_AOUT)
+
+int nsyms, ntextrel, ndatarel;
+struct exec *hdrp;
+char *aoutdata, *strbase;
+struct relocation_info *textrel, *datarel;
+struct nlist *symbase;
+
+
+#define SYMSTR(sp) (&strbase[(sp)->n_un.n_strx])
+
+/* is the symbol a global symbol defined in the current file? */
+#define IS_GLOBAL_DEFINED(sp) \
+ (((sp)->n_type & N_EXT) && ((sp)->n_type & N_TYPE) != N_UNDF)
+
+/* is the relocation entry dependent on a symbol? */
+#define IS_SYMBOL_RELOC(rp) \
+ ((rp)->r_extern||(rp)->r_baserel||(rp)->r_jmptable)
+
+static void check_reloc(const char *filename, struct relocation_info *relp);
+
+int check_aout(int inf, const char *filename)
+{
+ struct stat infstat;
+ struct exec eh;
+
+ /*
+ * check the header to make sure it's an a.out-format file.
+ */
+
+ if(fstat(inf, &infstat) == -1)
+ return 0;
+ if(infstat.st_size < sizeof eh)
+ return 0;
+ if(read(inf, &eh, sizeof eh) != sizeof eh)
+ return 0;
+
+ if(N_BADMAG(eh))
+ return 0;
+
+ return 1;
+}
+
+int hide_aout(int inf, const char *filename)
+{
+ struct stat infstat;
+ struct relocation_info *relp;
+ struct nlist *symp;
+ int rc;
+
+ /*
+ * do some error checking.
+ */
+
+ if(fstat(inf, &infstat) == -1) {
+ perror(filename);
+ return 1;
+ }
+
+ /*
+ * Read the entire file into memory. XXX - Really, we only need to
+ * read the header and from TRELOFF to the end of the file.
+ */
+
+ if((aoutdata = (char *) malloc(infstat.st_size)) == NULL) {
+ fprintf(stderr, "%s: too big to read into memory\n", filename);
+ return 1;
+ }
+
+ if((rc = read(inf, aoutdata, infstat.st_size)) < infstat.st_size) {
+ fprintf(stderr, "%s: read error: %s\n", filename,
+ rc == -1? strerror(errno) : "short read");
+ return 1;
+ }
+
+ /*
+ * Calculate offsets and sizes from the header.
+ */
+
+ hdrp = (struct exec *) aoutdata;
+
+#ifdef __FreeBSD__
+ textrel = (struct relocation_info *) (aoutdata + N_RELOFF(*hdrp));
+ datarel = (struct relocation_info *) (aoutdata + N_RELOFF(*hdrp) +
+ hdrp->a_trsize);
+#else
+ textrel = (struct relocation_info *) (aoutdata + N_TRELOFF(*hdrp));
+ datarel = (struct relocation_info *) (aoutdata + N_DRELOFF(*hdrp));
+#endif
+ symbase = (struct nlist *) (aoutdata + N_SYMOFF(*hdrp));
+ strbase = (char *) (aoutdata + N_STROFF(*hdrp));
+
+ ntextrel = hdrp->a_trsize / sizeof(struct relocation_info);
+ ndatarel = hdrp->a_drsize / sizeof(struct relocation_info);
+ nsyms = hdrp->a_syms / sizeof(struct nlist);
+
+ /*
+ * Zap the type field of all globally-defined symbols. The linker will
+ * subsequently ignore these entries. Don't zap any symbols in the
+ * keep list.
+ */
+
+ for(symp = symbase; symp < symbase + nsyms; symp++) {
+ if(!IS_GLOBAL_DEFINED(symp)) /* keep undefined syms */
+ continue;
+
+ /* keep (C) symbols which are on the keep list */
+ if(SYMSTR(symp)[0] == '_' && in_keep_list(SYMSTR(symp) + 1))
+ continue;
+
+ symp->n_type = 0;
+ }
+
+ /*
+ * Check whether the relocation entries reference any symbols that we
+ * just zapped. I don't know whether ld can handle this case, but I
+ * haven't encountered it yet. These checks are here so that the program
+ * doesn't fail silently should such symbols be encountered.
+ */
+
+ for(relp = textrel; relp < textrel + ntextrel; relp++)
+ check_reloc(filename, relp);
+ for(relp = datarel; relp < datarel + ndatarel; relp++)
+ check_reloc(filename, relp);
+
+ /*
+ * Write the .o file back out to disk. XXX - Really, we only need to
+ * write the symbol table entries back out.
+ */
+ lseek(inf, 0, SEEK_SET);
+ if((rc = write(inf, aoutdata, infstat.st_size)) < infstat.st_size) {
+ fprintf(stderr, "%s: write error: %s\n", filename,
+ rc == -1? strerror(errno) : "short write");
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static void check_reloc(const char *filename, struct relocation_info *relp)
+{
+ /* bail out if we zapped a symbol that is needed */
+ if(IS_SYMBOL_RELOC(relp) && symbase[relp->r_symbolnum].n_type == 0) {
+ fprintf(stderr,
+ "%s: oops, have hanging relocation for %s: bailing out!\n",
+ filename, SYMSTR(&symbase[relp->r_symbolnum]));
+ exit(1);
+ }
+}
+
+#endif /* defined(NLIST_AOUT) */
diff --git a/usr.sbin/crunch/crunchide/exec_elf32.c b/usr.sbin/crunch/crunchide/exec_elf32.c
new file mode 100644
index 0000000..5750ced6
--- /dev/null
+++ b/usr.sbin/crunch/crunchide/exec_elf32.c
@@ -0,0 +1,414 @@
+/*
+ * Copyright (c) 1997 Christopher G. Demetriou. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Christopher G. Demetriou
+ * for the NetBSD Project.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+__RCSID("$NetBSD: exec_elf32.c,v 1.4 1997/08/12 06:07:24 mikel Exp $");
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#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 "endian.h"
+#include "extern.h"
+
+#if (defined(NLIST_ELF32) && (ELFSIZE == 32)) || \
+ (defined(NLIST_ELF64) && (ELFSIZE == 64))
+
+#define __ELF_WORD_SIZE ELFSIZE
+#if (ELFSIZE == 32)
+#include <sys/elf32.h>
+#define xewtoh(x) ((data == ELFDATA2MSB) ? be32toh(x) : le32toh(x))
+#elif (ELFSIZE == 64)
+#include <sys/elf64.h>
+#define xewtoh(x) ((data == ELFDATA2MSB) ? be64toh(x) : le64toh(x))
+#endif
+#include <sys/elf_generic.h>
+
+#define CONCAT(x,y) __CONCAT(x,y)
+#define ELFNAME(x) CONCAT(elf,CONCAT(ELFSIZE,CONCAT(_,x)))
+#define ELFNAME2(x,y) CONCAT(x,CONCAT(_elf,CONCAT(ELFSIZE,CONCAT(_,y))))
+#define ELFNAMEEND(x) CONCAT(x,CONCAT(_elf,ELFSIZE))
+#define ELFDEFNNAME(x) CONCAT(ELF,CONCAT(ELFSIZE,CONCAT(_,x)))
+
+#define xe16toh(x) ((data == ELFDATA2MSB) ? be16toh(x) : le16toh(x))
+#define xe32toh(x) ((data == ELFDATA2MSB) ? be32toh(x) : le32toh(x))
+#define htoxe32(x) ((data == ELFDATA2MSB) ? htobe32(x) : htole32(x))
+
+struct listelem {
+ struct listelem *next;
+ void *mem;
+ off_t file;
+ size_t size;
+};
+
+static ssize_t
+xreadatoff(int fd, void *buf, off_t off, size_t size, const char *fn)
+{
+ ssize_t rv;
+
+ if (lseek(fd, off, SEEK_SET) != off) {
+ perror(fn);
+ return -1;
+ }
+ if ((rv = read(fd, buf, size)) != size) {
+ fprintf(stderr, "%s: read error: %s\n", fn,
+ rv == -1 ? strerror(errno) : "short read");
+ return -1;
+ }
+ return size;
+}
+
+static ssize_t
+xwriteatoff(int fd, void *buf, off_t off, size_t size, const char *fn)
+{
+ ssize_t rv;
+
+ if (lseek(fd, off, SEEK_SET) != off) {
+ perror(fn);
+ return -1;
+ }
+ if ((rv = write(fd, buf, size)) != size) {
+ fprintf(stderr, "%s: write error: %s\n", fn,
+ rv == -1 ? strerror(errno) : "short write");
+ return -1;
+ }
+ return size;
+}
+
+static void *
+xmalloc(size_t size, const char *fn, const char *use)
+{
+ void *rv;
+
+ rv = malloc(size);
+ if (rv == NULL)
+ fprintf(stderr, "%s: out of memory (allocating for %s)\n",
+ fn, use);
+ return (rv);
+}
+
+int
+ELFNAMEEND(check)(int fd, const char *fn)
+{
+ Elf_Ehdr eh;
+ struct stat sb;
+ unsigned char data;
+
+ /*
+ * Check the header to maek sure it's an ELF file (of the
+ * appropriate size).
+ */
+ if (fstat(fd, &sb) == -1)
+ return 0;
+ if (sb.st_size < sizeof eh)
+ return 0;
+ if (read(fd, &eh, sizeof eh) != sizeof eh)
+ return 0;
+
+ if (IS_ELF(eh) == 0)
+ return 0;
+
+ data = eh.e_ident[EI_DATA];
+
+ switch (xe16toh(eh.e_machine)) {
+ case EM_386: break;
+ case EM_ALPHA: break;
+ case EM_IA_64: break;
+ case EM_SPARCV9: break;
+/* ELFDEFNNAME(MACHDEP_ID_CASES) */
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+int
+ELFNAMEEND(hide)(int fd, const char *fn)
+{
+ Elf_Ehdr ehdr;
+ Elf_Shdr *shdrp = NULL, *symtabshdr, *strtabshdr;
+ Elf_Sym *symtabp = NULL;
+ char *strtabp = NULL;
+ Elf_Word *symfwmap = NULL, *symrvmap = NULL, nsyms, nlocalsyms, ewi;
+ struct listelem *relalist = NULL, *rellist = NULL, *tmpl;
+ ssize_t shdrsize;
+ int rv, i, weird;
+ unsigned char data;
+
+ rv = 0;
+ if (xreadatoff(fd, &ehdr, 0, sizeof ehdr, fn) != sizeof ehdr)
+ goto bad;
+
+ data = ehdr.e_ident[EI_DATA];
+
+ shdrsize = xe16toh(ehdr.e_shnum) * xe16toh(ehdr.e_shentsize);
+ if ((shdrp = xmalloc(shdrsize, fn, "section header table")) == NULL)
+ goto bad;
+ if (xreadatoff(fd, shdrp, xewtoh(ehdr.e_shoff), shdrsize, fn) !=
+ shdrsize)
+ goto bad;
+
+ symtabshdr = strtabshdr = NULL;
+ weird = 0;
+ for (i = 0; i < xe16toh(ehdr.e_shnum); i++) {
+ switch (xe32toh(shdrp[i].sh_type)) {
+ case SHT_SYMTAB:
+ if (symtabshdr != NULL)
+ weird = 1;
+ symtabshdr = &shdrp[i];
+ strtabshdr = &shdrp[xe32toh(shdrp[i].sh_link)];
+ break;
+ case SHT_RELA:
+ tmpl = xmalloc(sizeof *tmpl, fn, "rela list element");
+ if (tmpl == NULL)
+ goto bad;
+ tmpl->mem = NULL;
+ tmpl->file = 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(xewtoh(symtabshdr->sh_size), fn, "symbol table"))
+ == NULL)
+ goto bad;
+ if (xreadatoff(fd, symtabp, xewtoh(symtabshdr->sh_offset),
+ xewtoh(symtabshdr->sh_size), fn) != xewtoh(symtabshdr->sh_size))
+ goto bad;
+
+ /* string table */
+ if ((strtabp = xmalloc(xewtoh(strtabshdr->sh_size), fn, "string table"))
+ == NULL)
+ goto bad;
+ if (xreadatoff(fd, strtabp, xewtoh(strtabshdr->sh_offset),
+ xewtoh(strtabshdr->sh_size), fn) != xewtoh(strtabshdr->sh_size))
+ goto bad;
+
+ /* any rela tables */
+ for (tmpl = relalist; tmpl != NULL; tmpl = tmpl->next) {
+ if ((tmpl->mem = xmalloc(xewtoh(tmpl->size), fn, "rela table"))
+ == NULL)
+ goto bad;
+ if (xreadatoff(fd, tmpl->mem, xewtoh(tmpl->file),
+ xewtoh(tmpl->size), fn) != xewtoh(tmpl->size))
+ goto bad;
+ }
+
+ /* any rel tables */
+ for (tmpl = rellist; tmpl != NULL; tmpl = tmpl->next) {
+ if ((tmpl->mem = xmalloc(xewtoh(tmpl->size), fn, "rel table"))
+ == NULL)
+ goto bad;
+ if (xreadatoff(fd, tmpl->mem, xewtoh(tmpl->file),
+ xewtoh(tmpl->size), fn) != xewtoh(tmpl->size))
+ goto bad;
+ }
+
+ /* Prepare data structures for symbol movement. */
+ nsyms = xewtoh(symtabshdr->sh_size) / xewtoh(symtabshdr->sh_entsize);
+ nlocalsyms = xe32toh(symtabshdr->sh_info);
+ if ((symfwmap = xmalloc(nsyms * sizeof (Elf_Word), fn,
+ "symbol forward mapping table")) == NULL)
+ goto bad;
+ if ((symrvmap = xmalloc(nsyms * sizeof (Elf_Word), fn,
+ "symbol reverse mapping table")) == NULL)
+ goto bad;
+
+ /* init location -> symbol # table */
+ for (ewi = 0; ewi < nsyms; ewi++)
+ symrvmap[ewi] = ewi;
+
+ /* move symbols, making them local */
+ for (ewi = nlocalsyms; ewi < nsyms; ewi++) {
+ Elf_Sym *sp, symswap;
+ Elf_Word mapswap;
+
+ sp = &symtabp[ewi];
+
+ /* if it's on our keep list, don't move it */
+ if (in_keep_list(strtabp + xe32toh(sp->st_name)))
+ continue;
+
+ /* if it's an undefined symbol, keep it */
+ if (xe16toh(sp->st_shndx) == SHN_UNDEF)
+ continue;
+
+ /* adjust the symbol so that it's local */
+ sp->st_info =
+ ELF_ST_INFO(STB_LOCAL, sp->st_info);
+/* (STB_LOCAL << 4) | ELF_SYM_TYPE(sp->st_info); *//* XXX */
+
+ /*
+ * move the symbol to its new location
+ */
+
+ /* note that symbols in those locations have been swapped */
+ mapswap = symrvmap[ewi];
+ symrvmap[ewi] = symrvmap[nlocalsyms];
+ symrvmap[nlocalsyms] = mapswap;
+
+ /* and swap the symbols */
+ symswap = *sp;
+ *sp = symtabp[nlocalsyms];
+ symtabp[nlocalsyms] = symswap;
+
+ nlocalsyms++; /* note new local sym */
+ }
+ symtabshdr->sh_info = htoxe32(nlocalsyms);
+
+ /* set up symbol # -> location mapping table */
+ for (ewi = 0; ewi < nsyms; ewi++)
+ symfwmap[symrvmap[ewi]] = ewi;
+
+ /* any rela tables */
+ for (tmpl = relalist; tmpl != NULL; tmpl = tmpl->next) {
+ Elf_Rela *relap = tmpl->mem;
+
+ for (ewi = 0; ewi < xewtoh(tmpl->size) / sizeof(*relap); ewi++) {
+ relap[ewi].r_info =
+#if (ELFSIZE == 32) /* XXX */
+ symfwmap[ELF_R_SYM(xe32toh(relap[ewi].r_info))] << 8 |
+ ELF_R_TYPE(xe32toh(relap[ewi].r_info));
+#elif (ELFSIZE == 64) /* XXX */
+ symfwmap[ELF_R_SYM(xewtoh(relap[ewi].r_info))] << 32 |
+ ELF_R_TYPE(xewtoh(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 < xewtoh(tmpl->size) / sizeof *relp; ewi++) {
+ relp[ewi].r_info =
+#if (ELFSIZE == 32) /* XXX */
+ symfwmap[ELF_R_SYM(xe32toh(relp[ewi].r_info))] << 8 |
+ ELF_R_TYPE(xe32toh(relp[ewi].r_info));
+#elif (ELFSIZE == 64) /* XXX */
+ symfwmap[ELF_R_SYM(xewtoh(relp[ewi].r_info))] << 32 |
+ ELF_R_TYPE(xewtoh(relp[ewi].r_info));
+#endif /* XXX */
+ }
+ }
+
+ /*
+ * write new tables to the file
+ */
+ if (xwriteatoff(fd, shdrp, xewtoh(ehdr.e_shoff), shdrsize, fn) !=
+ shdrsize)
+ goto bad;
+ if (xwriteatoff(fd, symtabp, xewtoh(symtabshdr->sh_offset),
+ xewtoh(symtabshdr->sh_size), fn) != xewtoh(symtabshdr->sh_size))
+ goto bad;
+ for (tmpl = relalist; tmpl != NULL; tmpl = tmpl->next) {
+ if (xwriteatoff(fd, tmpl->mem, xewtoh(tmpl->file),
+ xewtoh(tmpl->size), fn) != xewtoh(tmpl->size))
+ goto bad;
+ }
+ for (tmpl = rellist; tmpl != NULL; tmpl = tmpl->next) {
+ if (xwriteatoff(fd, tmpl->mem, xewtoh(tmpl->file),
+ xewtoh(tmpl->size), fn) != xewtoh(tmpl->size))
+ goto bad;
+ }
+
+out:
+ if (shdrp != NULL)
+ free(shdrp);
+ if (symtabp != NULL)
+ free(symtabp);
+ if (strtabp != NULL)
+ free(strtabp);
+ if (symfwmap != NULL)
+ free(symfwmap);
+ if (symrvmap != NULL)
+ free(symrvmap);
+ while ((tmpl = relalist) != NULL) {
+ relalist = tmpl->next;
+ if (tmpl->mem != NULL)
+ free(tmpl->mem);
+ free(tmpl);
+ }
+ while ((tmpl = rellist) != NULL) {
+ rellist = tmpl->next;
+ if (tmpl->mem != NULL)
+ free(tmpl->mem);
+ free(tmpl);
+ }
+ return (rv);
+
+bad:
+ rv = 1;
+ goto out;
+}
+
+#endif /* include this size of ELF */
diff --git a/usr.sbin/crunch/crunchide/exec_elf64.c b/usr.sbin/crunch/crunchide/exec_elf64.c
new file mode 100644
index 0000000..962cc07
--- /dev/null
+++ b/usr.sbin/crunch/crunchide/exec_elf64.c
@@ -0,0 +1,40 @@
+/* $NetBSD: exec_elf64.c,v 1.2 1997/08/02 21:30:19 perry Exp $ */
+
+/*
+ * Copyright (c) 1997 Christopher G. Demetriou. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Christopher G. Demetriou
+ * for the NetBSD Project.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__IDSTRING(elf64rcsid, "$NetBSD: exec_elf64.c,v 1.2 1997/08/02 21:30:19 perry Exp $");
+#endif
+
+#define ELFSIZE 64
+
+#include "exec_elf32.c"
diff --git a/usr.sbin/crunch/crunchide/extern.h b/usr.sbin/crunch/crunchide/extern.h
new file mode 100644
index 0000000..4200bc3
--- /dev/null
+++ b/usr.sbin/crunch/crunchide/extern.h
@@ -0,0 +1,51 @@
+/* $NetBSD: extern.h,v 1.5 1998/05/06 13:16:57 mycroft Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (c) 1997 Christopher G. Demetriou. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Christopher G. Demetriou
+ * for the NetBSD Project.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef NLIST_AOUT
+int check_aout(int, const char *);
+int hide_aout(int, const char *);
+#endif
+#ifdef NLIST_ECOFF
+int check_ecoff(int, const char *);
+int hide_ecoff(int, const char *);
+#endif
+#ifdef NLIST_ELF32
+int check_elf32(int, const char *);
+int hide_elf32(int, const char *);
+#endif
+#ifdef NLIST_ELF64
+int check_elf64(int, const char *);
+int hide_elf64(int, const char *);
+#endif
+
+int in_keep_list(const char *symbol);
diff --git a/usr.sbin/crunch/examples/Makefile b/usr.sbin/crunch/examples/Makefile
new file mode 100644
index 0000000..5ff5263
--- /dev/null
+++ b/usr.sbin/crunch/examples/Makefile
@@ -0,0 +1,33 @@
+# $FreeBSD$
+
+CRUNCHED= fixit
+
+# below is boiler-plate to make $(CRUNCHED) from $(CRUNCHED).conf
+# I'd use PROG instead of CRUNCHED, but the system makefiles REALLY want
+# to build things in the normal way if you use PROG.
+
+CONF= $(CRUNCHED).conf
+
+OUTMK= $(CRUNCHED).mk
+OUTPUTS= $(OUTMK) $(CRUNCHED).c $(CRUNCHED).cache
+
+NOMAN= #true
+CLEANFILES+= $(CRUNCHED) *.o *.lo *.c *.mk *.cache
+CLEANDIRFILES+= $(OUTPUTS)
+
+all: $(CRUNCHED)
+exe: $(CRUNCHED)
+
+$(OUTPUTS): $(CONF)
+ crunchgen ${.CURDIR}/$(CONF)
+
+$(CRUNCHED): $(OUTPUTS) submake
+
+submake:
+ make -f $(OUTMK)
+objs:
+ make -f $(OUTMK) objs
+cleandir:
+ rm -f $(CLEANDIRFILES)
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/crunch/examples/filesystem.conf b/usr.sbin/crunch/examples/filesystem.conf
new file mode 100644
index 0000000..1fb9385
--- /dev/null
+++ b/usr.sbin/crunch/examples/filesystem.conf
@@ -0,0 +1,31 @@
+# $FreeBSD$
+
+srcdirs /usr/src/bin /usr/src/sbin /usr/src/gnu/usr.bin /usr/src/usr.sbin
+srcdirs /usr/src/sbin/i386
+
+# /bin
+progs sh expr ls mkdir rm sync test
+ln test [
+
+# These are needed because of UN*X's idiotic way of indicating that something
+# is a login shell.
+ln sh -
+ln sh -sh
+
+# /sbin
+progs disklabel fdisk init mount newfs reboot umount
+ln reboot halt
+ln reboot fastboot
+ln reboot fasthalt
+
+
+# /usr/bin
+progs cpio gzip
+ln gzip gunzip
+ln gzip gzcat
+ln gzip zcat
+
+# /usr/sbin
+progs bad144
+
+libs -ll -ledit -ltermcap -lutil -lscrypt
diff --git a/usr.sbin/crunch/examples/fixit.conf b/usr.sbin/crunch/examples/fixit.conf
new file mode 100644
index 0000000..80a2346
--- /dev/null
+++ b/usr.sbin/crunch/examples/fixit.conf
@@ -0,0 +1,45 @@
+# fixit.conf - put in anything we think we might want on a fixit floppy
+
+# first, we list the source dirs that our programs reside in. These are
+# searched in order listed to find the dir containing each program.
+
+srcdirs /usr/src/bin /usr/src/sbin /usr/src/usr.bin /usr/src/usr.sbin
+srcdirs /usr/src/gnu/usr.bin /usr/src/usr.bin/vi
+srcdirs /usr/src/sbin/i386
+
+# second, we list all the programs we want to include in our crunched binary.
+# The order doesn't matter. Any program that needs hard links to it gets an
+# `ln' directive.
+
+# /bin stuff
+
+progs cat chmod cp date dd df echo ed expr hostname kill ln ls mkdir
+progs mt mv pwd rcp rm rmdir sh sleep stty sync test
+
+ln test [
+ln sh -sh # init invokes the shell this way
+
+# /sbin stuff
+
+progs badsect chown clri disklabel dump dmesg fdisk fsck ifconfig init
+progs mknod mount newfs ping reboot restore swapon umount
+ln dump rdump
+ln restore rrestore
+
+# /usr/bin stuff
+
+progs ftp rsh sed telnet rlogin common find
+ln common vi
+ln common view
+ln common ex
+
+# gnu stuff
+
+progs cpio gzip
+ln gzip gunzip
+ln gzip gzcat
+
+# finally, we specify the libraries to link in with our binary
+
+libs -lcrypt -ltelnet -lutil -ll
+libs -lcurses -ltermcap -ledit -lkvm
diff --git a/usr.sbin/crunch/examples/kcopy.conf b/usr.sbin/crunch/examples/kcopy.conf
new file mode 100644
index 0000000..3284e86
--- /dev/null
+++ b/usr.sbin/crunch/examples/kcopy.conf
@@ -0,0 +1,21 @@
+# $FreeBSD$
+
+srcdirs /usr/src/bin /usr/src/sbin
+
+# Programs from bin/
+progs sh cp echo test
+ln test [
+
+# These are needed because of UN*X's idiotic way of indicating that something
+# is a login shell.
+ln sh -
+ln sh -sh
+
+#
+# Programs from sbin/
+progs mount mount_cd9660 fsck init reboot umount
+ln reboot halt
+ln reboot fastboot
+ln reboot fasthalt
+
+libs -ll -ledit -ltermcap -lcompat -lutil -lscrypt
diff --git a/usr.sbin/crunch/examples/really-big.conf b/usr.sbin/crunch/examples/really-big.conf
new file mode 100644
index 0000000..f8590ae
--- /dev/null
+++ b/usr.sbin/crunch/examples/really-big.conf
@@ -0,0 +1,158 @@
+# really-big.conf - just about everything, just for testing.
+# This ends up having some good examples of the use of specials for
+# those hard-to-reach programs. I stopped when I got tired, but we
+# could probably get even more stuff (like libexec stuff) in here.
+#
+# This produces a 4608000 byte binary. Pretty sick and twisted, eh?
+#
+# $FreeBSD$
+#
+
+# =========================================================================
+
+srcdirs /usr/src/bin
+
+progs cat chmod cp csh date dd df domainname echo ed expr hostname kill
+progs ln ls mkdir mt mv ps pwd rcp rm rmail rmdir sh sleep stty sync test
+
+ln test [
+ln sh -sh
+
+
+# =========================================================================
+
+srcdirs /usr/src/sbin
+
+progs badsect bim clri disklabel dmesg dump dumpfs fdisk fsck halt
+progs ifconfig init mknod modload modunload mount mount_fdescfs mount_isofs
+progs mount_lofs mount_msdosfs mount_portalfs mount_procfs mountd
+progs newfs nfsd nfsiod ping quotacheck reboot restore route routed savecore
+progs shutdown slattach swapon ttyflags tunefs umount
+# shell scripts: fastboot
+
+ln dump rdump
+ln restore rrestore
+
+
+# =========================================================================
+
+srcdirs /usr/src/usr.bin
+
+progs apropos ar asa at basename biff cal calendar cap_mkdb checknr chpass
+progs cksum cmp col colcrt colrm column comm compress crontab ctags cut
+progs dirname du env error expand false file find finger fmt fold fpr from
+progs fsplit fstat ftp getconf getopt gprof head hexdump id indent ipcrm
+progs ipcs join kdump ktrace last lastcomm leave lex lock logger locate
+progs login logname look m4 machine mail make man mesg mkfifo
+progs mkstr modstat more msgs netstat newsyslog nfsstat nice nm nohup
+progs pagesize passwd paste patch pr printenv printf quota ranlib
+progs renice rev rlogin rpcgen rpcinfo rsh rup ruptime rusers rwall rwho
+progs script sed showmount size soelim split strings strip su tail talk
+progs tcopy tee telnet tftp time tip tn3270 touch tput tr true tset tsort
+progs tty ul uname unexpand unifdef uniq units unvis users uudecode uuencode
+progs vacation vgrind vi vis vmstat w wall wc what whatis whereis who
+progs whois window write xargs xinstall xstr yacc yes ypcat ypmatch ypwhich
+
+# shell scripts: lorder mkdep shar which
+# problems: rdist uses libcompat.a(regex.o), which conflicts with
+# libedit(readline.o) over regerror().
+
+# special requirements
+
+special locate srcdir /usr/src/usr.bin/locate/locate
+special tn3270 srcdir /usr/src/usr.bin/tn3270/tn3270
+
+
+# =========================================================================
+
+srcdirs /usr/src/usr.sbin
+
+progs ac accton amd arp bad144 catman chown chroot config config.new cron
+progs dev_mkdb diskpart edquota flcopy gettable grfinfo hilinfo htable inetd
+progs iostat iteconfig kvm_mkdb mrouted mtree named portmap pppd
+progs pstat pwd_mkdb quot quotaon rarpd rbootd repquota rmt rpc.bootparamd
+progs rwhod sa sliplogin slstats spray sysctl syslogd tcpdump
+progs traceroute trpt trsp update vipw vnconfig ypbind yppoll ypset
+
+special amd srcdir /usr/src/usr.sbin/amd/amd
+special amd objs vers.amd.o afs_ops.o am_ops.o clock.o util.o xutil.o efs_ops.o mapc.o info_file.o info_hes.o info_ndbm.o info_passwd.o info_nis.o info_union.o map.o srvr_afs.o srvr_nfs.o mntfs.o misc_rpc.o mount_fs.o mtab.o mtab_bsd.o nfs_ops.o nfs_prot_svc.o nfs_start.o nfs_subr.o opts.o pfs_ops.o rpc_fwd.o sched.o sfs_ops.o amq_svc.o amq_subr.o umount_fs.o host_ops.o nfsx_ops.o ufs_ops.o ifs_ops.o amd.o get_args.o restart.o wire.o
+
+
+srcdirs /usr/src/usr.sbin/lpr # lpr subsystem
+progs lpr lpc lpq lprm pac lptest
+special lpr srcdir /usr/src/usr.sbin/lpr/lpr
+
+srcdirs /usr/src/usr.sbin/sendmail # sendmail subsystem
+progs mailstats makemap praliases sendmail
+special sendmail srcdir /usr/src/usr.sbin/sendmail/src
+ln sendmail newaliases
+ln sendmail mailq
+
+srcdirs /usr/src/usr.sbin/timed # timed & timedc
+progs timed timedc
+special timed srcdir /usr/src/usr.sbin/timed/timed
+
+srcdirs /usr/src/usr.sbin/xntpd # NTP subsystem
+# xntpd uses a gross hack to pass some information in the global
+# variable `progname' between the actual program (ntpdate in this
+# case), and the NTP library. Add `progname' to the keep list.
+progs ntpdate
+special ntpdate srcdir /usr/src/usr.sbin/xntpd/ntpdate
+special ntpdate keep progname
+libs -L/usr/src/usr.sbin/xntpd/lib -lntp
+
+srcdirs /usr/src/usr.sbin/yp # yp subsystem
+progs ypbind ypwhich ypcat ypmatch ypset yppoll
+
+
+# =========================================================================
+
+srcdirs /usr/src/gnu/usr.bin
+
+progs bc cpio diff diff3 gas gawk grep gzip sdiff sort tar
+# shell scripts: send-pr
+
+srcdirs /usr/src/gnu/usr.bin/ld # ldd and ldconfig
+progs ld ldd ldconfig
+
+# rcs stuff loses because there are cross dependencies between librcs.a and
+# the individual programs. The solution would be to specify the objpaths
+# directly for each one, and include the full path to librcs.a each the
+# objpaths.
+
+# srcdirs /usr/src/gnu/usr.bin/rcs # rcs subsystem
+# progs ci co ident merge rcs rcsclean rcsdiff rcsmerge rlog
+# # shell script: rcsfreeze
+# special rcs srcdir /usr/src/gnu/usr.bin/rcs/rcs
+# libs /usr/src/gnu/usr.bin/rcs/lib/obj/librcs.a
+
+# gdb loses too
+# progs gdb
+# special gdb srcdir /usr/src/gnu/usr.bin/gdb/gdb
+# libs /usr/src/gnu/usr.bin/gdb/bfd/obj/libbfd.a
+# libs /usr/src/gnu/usr.bin/gdb/readline/obj/libreadline.a
+# libs /usr/src/gnu/usr.bin/gdb/libiberty/obj/libiberty.a
+
+# groff has the same problem as rcs
+# srcdirs /usr/src/gnu/usr.bin/groff # groff subsystem
+# progs groff troff tbl pic eqn grops grotty grodvi refer lookbib
+# progs indxbib lkbib tfmtodit addftinfo pfbtops psbb
+# shell script: nroff
+# special groff srcdir /usr/src/gnu/usr.bin/groff/groff
+# libs /usr/src/gnu/usr.bin/groff/libgroff/obj/libgroff.a
+# libs /usr/src/gnu/usr.bin/groff/libbib/obj/libbib.a
+# libs /usr/src/gnu/usr.bin/groff/libdriver/obj/libdriver.a
+
+srcdirs /usr/src/gnu/usr.bin/gcc2 # gcc & friends
+progs cc cpp cc1
+
+# cc1 has the same problem as rcs and groff, but since there's only one program
+# I'll go ahead and solve it as an example.
+
+special cc1 objpaths /usr/src/gnu/usr.bin/gcc2/cc1/obj/c-parse.o /usr/src/gnu/usr.bin/gcc2/cc1/obj/c-lang.o /usr/src/gnu/usr.bin/gcc2/cc1/obj/c-lex.o /usr/src/gnu/usr.bin/gcc2/cc1/obj/c-pragma.o /usr/src/gnu/usr.bin/gcc2/cc1/obj/c-decl.o /usr/src/gnu/usr.bin/gcc2/cc1/obj/c-typeck.o /usr/src/gnu/usr.bin/gcc2/cc1/obj/c-convert.o /usr/src/gnu/usr.bin/gcc2/cc1/obj/c-aux-info.o /usr/src/gnu/usr.bin/gcc2/cc1/obj/c-iterate.o /usr/src/gnu/usr.bin/gcc2/common/obj/libcc1.a
+
+ln gzip gunzip
+ln gzip gzcat
+
+libs -ledit -lgnumalloc -lc -lcrypt -ltermcap -lcurses -ltelnet -lutil -lkvm
+libs -ll -ly -lm -lresolv -lrpcsvc -lcompat
diff --git a/usr.sbin/ctm/Makefile b/usr.sbin/ctm/Makefile
new file mode 100644
index 0000000..630aab0
--- /dev/null
+++ b/usr.sbin/ctm/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+SUBDIR= ctm ctm_rmail ctm_smail ctm_dequeue
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/ctm/Makefile.inc b/usr.sbin/ctm/Makefile.inc
new file mode 100644
index 0000000..c6c2c5a
--- /dev/null
+++ b/usr.sbin/ctm/Makefile.inc
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+.if exists(${.CURDIR}/../../Makefile.inc)
+.include "${.CURDIR}/../../Makefile.inc"
+.endif
diff --git a/usr.sbin/ctm/README b/usr.sbin/ctm/README
new file mode 100644
index 0000000..94c779a
--- /dev/null
+++ b/usr.sbin/ctm/README
@@ -0,0 +1,85 @@
+# ----------------------------------------------------------------------------
+# "THE BEER-WARE LICENSE" (Revision 42):
+# <phk@FreeBSD.org> wrote this file. As long as you retain this notice you
+# can do whatever you want with this stuff. If we meet some day, and you think
+# this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+# ----------------------------------------------------------------------------
+#
+# $FreeBSD$
+#
+
+What will I not find in this file ?
+-----------------------------------
+Instructions on how to obtain FreeBSD via CTM.
+Contact <CTM@FreeBSD.org> for that.
+
+What is CTM ?
+-------------
+CTM was originally "CVS Through eMail", but has since changed scope to be
+much more general.
+CTM is now meant to be the definitive way to make and apply a delta between
+two versions of a directory tree.
+There are two parts to this, making the delta and applying it. These are two
+entirely different things. CTM concentrates the computation-burden on the
+generation of the deltas, as a delta very often is applied more times than
+it is made. Second CTM tries to make the minimal size delta.
+
+Why not use diff/patch ?
+------------------------
+Good question. Primarily because diff and patch doesn't do their job very
+well. They don't deal with binary files (in this case files with '\0' or
+'\0377' characters in them or files that doesn't end in '\n') which isn't
+a big surprise: they were made to deal with text-files only. As a second
+gripe, with patch you send the entire file to delete it. Not particular
+efficient.
+
+So what does CTM do exactly ?
+-----------------------------
+CTM will produce a file, (a delta) containing the instructions and data needed
+to take another copy of the tree from the old to the new status. CTM means to
+do this in the exact sense, and therefore the delta contains MD5 checksums to
+verify that the tree it is applied to is indeed in the state CTM expects.
+
+This means that if you have modified the tree locally, CTM might not be able
+to upgrade your copy.
+
+How do I make a CTM-delta ?
+---------------------------
+
+Read the source, and be prepared to have 2 copies of the tree; One is
+the reference ("From") tree, and the other is the delta ("To") tree.
+The mkCTM script will create the CTM diff of the differences between
+the reference tree and the delta tree. A lot of scratch space is
+required, and your machine will work hard.
+
+How do I apply a CTM-delta ?
+----------------------------
+You pass it to the 'ctm' command. You can pass a CTM-delta on stdin, or
+you can give the filename as an argument. If you do the latter, you make
+life a lot easier for your self, since the program can accept gzip'ed files
+and since it will not have to make a temporary copy of your file. You can
+specify multiple deltas at one time, they will be processed one at a time.
+
+The ctm command runs in a number of passes. It will process the entire
+input file in each pass, before commencing with the next pass.
+
+Pass 1 will validate that the input file is OK. The syntax, the data and
+the global MD5 checksum will be checked. If any of these fail, ctm will
+never be able to do anything with the file, so it will simply reject it.
+
+Pass 2 will validate that the directory tree is in the state expected by
+the CTM-delta. This is done by looking for files and directories which
+should/should not exists and by checking the MD5 checksums of files.
+
+Pass 3 will actually apply the delta.
+
+Should I delete the delta when I have applied it ?
+--------------------------------------------------
+No. You might want to selectively reconstruct a file latter on.
+
+Why is CTM not being maintained?
+--------------------------------
+Because CVSUP has improved on the concept quite a bit, and is now
+the method of choice.
+
+Poul-Henning
diff --git a/usr.sbin/ctm/ctm/Makefile b/usr.sbin/ctm/ctm/Makefile
new file mode 100644
index 0000000..00140de
--- /dev/null
+++ b/usr.sbin/ctm/ctm/Makefile
@@ -0,0 +1,23 @@
+# ----------------------------------------------------------------------------
+# "THE BEER-WARE LICENSE" (Revision 42):
+# <phk@FreeBSD.org> wrote this file. As long as you retain this notice you
+# can do whatever you want with this stuff. If we meet some day, and you think
+# this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+# ----------------------------------------------------------------------------
+#
+# $FreeBSD$
+
+PROG= ctm
+MAN= ctm.1 ctm.5
+SRCS= ctm.c ctm_input.c ctm_pass1.c ctm_pass2.c ctm_pass3.c \
+ ctm_passb.c ctm_syntax.c ctm_ed.c
+
+NOTYET= ctm_ed.c
+
+LDADD= -lmd
+DPADD= ${LIBMD}
+
+.if exists(${.CURDIR}/../../Makefile.inc)
+.include "${.CURDIR}/../../Makefile.inc"
+.endif
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ctm/ctm/ctm.1 b/usr.sbin/ctm/ctm/ctm.1
new file mode 100644
index 0000000..cdc75ab
--- /dev/null
+++ b/usr.sbin/ctm/ctm/ctm.1
@@ -0,0 +1,301 @@
+.\"----------------------------------------------------------------------------
+.\""THE BEER-WARE LICENSE" (Revision 42):
+.\"<joerg@FreeBSD.org> wrote this file. As long as you retain this notice you
+.\"can do whatever you want with this stuff. If we meet some day, and you think
+.\"this stuff is worth it, you can buy me a beer in return. Joerg Wunsch
+.\"----------------------------------------------------------------------------
+.\"
+.\" This manual page is partially obtained from Poul-Hennings CTM README
+.\" file.
+.\"
+.\" CTM and ctm(1) by <phk@FreeBSD.org>
+.\"
+.\" $FreeBSD$
+.\"
+.Dd March 25, 1995
+.Os
+.Dt CTM 1
+.Sh NAME
+.Nm ctm
+.Nd source code mirror program
+.Sh SYNOPSIS
+.Nm
+.Op Fl cFklquv
+.Op Fl b Ar basedir
+.Op Fl B Ar backup-file
+.Op Fl e Ar include-regex
+.Op Fl t Ar tar-command
+.Op Fl T Ar tmpdir
+.Op Fl V Ar level
+.Op Fl x Ar exclude-regex
+.Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility was originally
+.Dq Cvs Through eMail ,
+but now instead it seems more fitting to call it
+.Dq Current Through eMail .
+.Pp
+The
+.Nm
+utility is now meant to be the definitive way to make and apply a delta between
+two versions of a directory tree.
+.Pp
+There are two parts to this, making the delta and applying it. These are two
+entirely different things.
+.Ss Usage
+To apply a CTM delta, you pass it to the
+.Nm
+command. You can pass a CTM delta on stdin, or you can give the
+filename as an argument. If you do the latter, you make life a lot
+easier for your self, since the program can accept gzip'ed files and
+since it will not have to make a temporary copy of your file. You can
+specify multiple deltas at one time, they will be processed one at a
+time. Deltas that are already applied will be ignored.
+.Pp
+The
+.Nm
+command runs in a number of passes. It will process the entire
+input file in each pass, before commencing with the next pass.
+.Pp
+Before working on a file
+.Ar name
+.Nm
+first checks for the existence of the file
+.Ar name.ctm .
+If this file exists,
+.Nm
+works on it instead.
+.Pp
+Pass 1 will verify that the input file is OK. The syntax, the data
+and the global MD5 checksum will be checked. If any of these fail,
+.Nm
+will simply reject the input file.
+.Pp
+Pass 2 will validate that the directory tree is in the state expected by
+the CTM delta. This is done by looking for files and directories which
+should/should not exist and by checking the MD5 checksums of files.
+.Pp
+If a
+.Ar backup-file
+had been specified using the
+.Fl B
+option, all files that would be modified by this
+.Nm
+invocation are backed up
+to this file using the archiver command specified by the
+.Fl t
+option. The default archiver command is
+.Nm "tar -rf %s -T -" .
+.Pp
+Pass 3 will actually apply the delta.
+.Pp
+The list of files that would be modified by
+.Nm
+is subject to filtering regular expressions specified
+using the
+.Fl e
+and
+.Fl x
+options.
+The
+.Fl e
+and
+.Fl x
+options are applied in order of appearance on the command line. The last
+filter that matched a given file name determines whether the file would be
+operated on or left alone by
+.Nm .
+.Pp
+The
+.Nm
+utility
+will extract the file hierarchy below its working directory. Absolute
+filenames or filenames containing references through
+.Sq Pa .\&
+and
+.Sq Pa ..\&
+are explicitly prohibited as a security measure.
+.Ss Options
+.Bl -tag -width indent
+.It Fl b Ar basedir
+Prepend the path
+.Ar basedir
+to every filename.
+.It Fl B Ar backup-file
+Backup all files that would be modified by this CTM run to
+.Ar backup-file .
+If any filters are specified using the
+.Fl e
+and
+.Fl x
+options, then the final set of files backed up are those that would be
+modified by CTM after the filters are applied.
+.It Fl c
+Check it out, don't do anything.
+.It Fl e Ar regular_expression
+Match each name in the CTM file against
+.Ar regular_expression ,
+and if it matches process the file, otherwise leave it alone. There may be
+any number of these options. Use of this option disables the
+.Pa .ctm_status
+sequence number checks. For example, the expression
+.Ic ^usr.sbin/ctm
+for example, will select the
+.Pa usr.sbin/ctm
+source directory and all pathnames under it.
+.Pp
+Pathnames can be disabled from being considered by CTM using the
+.Fl x
+option.
+.It Fl F
+Force.
+.It Fl k
+Keep files and directories and don't remove them even if the CTM file
+specifies they are to be removed. If the
+.Fl B
+option is specified, these files and directories will not be backed up.
+.It Fl l
+List files that would be modified by this invocation of CTM and the
+actions that would be performed on them. Use of the
+.Fl l
+option disables the
+.Pa .ctm_status
+checks and integrity checks on the source tree being operated on. The
+.Fl l
+option can be combined with the
+.Fl e
+and
+.Fl x
+options to determine which files would be modified by the given set of
+command line options.
+.It Fl q
+Tell us less.
+.It Fl t Ar tar-command
+Use
+.Ar tar-command
+instead of the default archiver
+.Nm tar .
+This option takes effect only if a backup file had been specified using the
+.Fl B
+option. A %s in the tar command will be replaced by the name of the backup
+file.
+.It Fl T Ar tmpdir
+Put temporary files under
+.Ar tmpdir .
+.It Fl u
+Set modification time of created and modified files to the CTM delta
+creation time.
+.It Fl v
+Tell us more.
+.It Fl V Ar level
+Tell us more.
+.Ar Level
+is the level of verbosity.
+.It Fl x Ar regular_expression
+Match each name in the CTM file against
+.Ar regular_expression
+and if it matches, leave the file alone. There may be any number of these
+options. Use of this option disables the
+.Pa .ctm_status
+sequence number checks.
+.Pp
+Pathnames can be selected for CTM's consideration using the
+.Fl e
+option.
+.El
+.Sh SECURITY
+On its own, CTM is an insecure protocol
+- there is no authentication performed that the
+changes applied to the source code were sent by a
+trusted party, and so care should be taken if the
+CTM deltas are obtained via an unauthenticated
+medium such as regular email.
+It is a relatively simple matter for an attacker
+to forge a CTM delta to replace or precede the
+legitimate one and insert malicious code into your
+source tree.
+If the legitimate delta is somehow prevented from
+arriving, this will go unnoticed until a later
+delta attempts to touch the same file, at which
+point the MD5 checksum will fail.
+.Pp
+To remedy this insecurity, CTM pieces generated by
+FreeBSD.org are cryptographically signed in a
+format compatible with the GNU Privacy Guard
+utility, available in /usr/ports/security/gpg, and
+the Pretty Good Privacy v5 utility,
+/usr/ports/security/pgp5.
+The relevant public key can be obtained by
+fingering ctm@FreeBSD.org.
+.Pp
+CTM deltas which are thus signed cannot be
+undetectably altered by an attacker.
+Therefore it is recommended that you make use of
+GPG or PGP5 to verify the signatures if you
+receive your CTM deltas via email.
+.Sh ENVIRONMENT
+.Ev TMPDIR ,
+if set to a pathname, will cause ctm to use that pathname
+as the location of temporary file.
+See
+.Xr tempnam 3 ,
+for more details on this.
+The same effect may be achieved with the
+.Fl T
+flag.
+.Sh FILES
+.Pa .ctm_status
+contains the sequence number of the last CTM delta applied. Changing
+or removing this file will greatly confuse
+.Nm .
+.Pp
+Using the
+.Fl e
+and
+.Fl x
+options can update a partial subset of the source tree and causes sources
+to be in an inconsistent state. It is assumed that you know what you are
+doing when you use these options.
+.Sh EXAMPLES
+.Bd -literal
+cd ~cvs
+/usr/sbin/ctm ~ctm/cvs-*
+.Ed
+.Pp
+To extract and patch all sources under `lib'
+.Bd -literal
+cd ~/lib-srcs
+/usr/sbin/ctm -e '^lib' ~ctm/src-cur*
+.Ed
+.Sh DIAGNOSTICS
+Numerous messages, hopefully self-explanatory. The
+.Dq noise level
+can be adjusted with the
+.Fl q ,
+.Fl v
+and
+.Fl V
+options.
+.Sh SEE ALSO
+.Xr ctm_rmail 1 ,
+.Xr ctm 5
+.Sh HISTORY
+Initial trials were run during the work on
+.Fx 1.1.5 ,
+and many bugs and
+methods were hashed out.
+.Pp
+The
+.Nm
+command appeared in
+.Fx 2.1 .
+.Sh AUTHORS
+The CTM system has been designed and implemented by
+.An Poul-Henning Kamp
+.Aq phk@FreeBSD.org .
+.Pp
+.An Joerg Wunsch
+.Aq joerg@FreeBSD.org
+wrote this man-page.
diff --git a/usr.sbin/ctm/ctm/ctm.5 b/usr.sbin/ctm/ctm/ctm.5
new file mode 100644
index 0000000..6165c2e
--- /dev/null
+++ b/usr.sbin/ctm/ctm/ctm.5
@@ -0,0 +1,181 @@
+.\"----------------------------------------------------------------------------
+.\""THE BEER-WARE LICENSE" (Revision 42):
+.\"<joerg@FreeBSD.org> wrote this file. As long as you retain this notice you
+.\"can do whatever you want with this stuff. If we meet some day, and you think
+.\"this stuff is worth it, you can buy me a beer in return. Joerg Wunsch
+.\"----------------------------------------------------------------------------
+.\"
+.\" This manual page is partially obtained from Poul-Hennings CTM README
+.\" file.
+.\"
+.\" CTM and ctm(1) by <phk@FreeBSD.org>
+.\"
+.\" $FreeBSD$
+.\"
+.Dd March 25, 1995
+.Os
+.Dt CTM 5
+.Sh NAME
+.Nm ctm
+.Nd source code mirror system
+.Sh DESCRIPTION
+The
+.Nm
+transfers data in a specific file format, called a CTM delta.
+.Pp
+CTM deltas consist of control lines and data chunks. Each control
+line starts with the letters
+.Dq CTM ,
+followed by a CTM statement and control data, and ends with a '\en'
+character.
+.Pp
+Data chunks always belong to the preceding control line, and the
+last field on that control line is the number of bytes in the data
+chunk.
+A trailing newline '\en' character follows each data chunk, this
+newline is not part of the chunk and isn't included in the count.
+.Pp
+The CTM statements are as follows.
+.Bl -tag -width indent
+.It _BEGIN Ar version name number timestamp prefix
+This is the overall begin of a CTM delta file. The
+.Ar version
+field must match the program version
+(currently 2.0).
+.Ar Name
+is the name and
+.Ar number
+the sequence number of the CTM service, it is matched against the file
+.Pa .ctm_status
+to see if the delta has already been applied.
+.Ar Timestamp
+contains the year, month, day, hour, minute, and second of the
+time of delta creation for reference
+(followed by the letter
+.Sq Z
+meaning this is a UTC timestamp).
+The
+.Ar prefix
+field is currently not implemented.
+.It _END Ar md5
+This statement ends the CTM delta, the global
+.Ar md5
+checksum is matched against the MD5 checksum of the entire delta, up to
+and including the space (0x20) character following ``_END''.
+.It \&FM Ar name uid gid mode md5 count
+Make the file
+.Ar name ,
+the original file had the uid
+.Ar uid
+(numerical, decimal),
+the gid
+.Ar gid
+(numerical, decimal),
+mode
+.Ar mode
+(numerical, octal),
+and the MD5 checksum
+.Ar md5 .
+.Pp
+The following
+.Ar count
+bytes data are the contents of the new file.
+.It \&FS Ar name uid gid mode md5before md5after count
+Substitute the contents of file
+.Ar name ,
+the original file had the new uid
+.Ar uid
+(numerical, decimal),
+the new gid
+.Ar gid
+(numerical, decimal),
+new mode
+.Ar mode
+(numerical, octal),
+the old MD5 checksum
+.Ar md5before ,
+and the new MD5 checksum
+.Ar md5after .
+.Pp
+The following
+.Ar count
+bytes data are the contents of the new file.
+.Pp
+File substitution is used if the commands to edit a file would exceed
+the total file length, so substituting it is more efficient.
+.It \&FN Ar name uid gid mode md5before md5after count
+Edit the file
+.Ar name .
+The arguments are as above, but the data sections contains an
+.Xr diff 1
+-n script which should be applied to the file in question.
+.It \&FR Ar name md5
+Remove the file
+.Ar name ,
+which must match the MD5 checksum
+.Ar md5 .
+.It \&AS Ar name uid gid mode
+The original file
+.Ar name
+changed its owner to
+.Ar uid ,
+its group to
+.Ar gid ,
+and/or its mode to
+.Ar mode .
+.It \&DM Ar name uid gid mode
+The directory
+.Ar name
+is to be created, it had originally the owner
+.Ar uid ,
+group
+.Ar gid ,
+and mode
+.Ar mode .
+.It \&DR Ar name
+The directory
+.Ar name
+is to be removed.
+.El
+.Sh EXAMPLES
+In the following example, long lines have been folded to make them
+printable
+(marked by backslashes).
+.Bd -literal
+CTM_BEGIN 2.0 cvs-cur 485 19950324214652Z .
+CTMFR src/sys/gnu/i386/isa/scd.c,v 5225f13aa3c7e458f9dd0d4bb637b18d
+CTMFR src/sys/gnu/i386/isa/scdreg.h,v e5af42b8a06f2c8030b93a7d71afb223
+CTMDM src/sys/gnu/i386/isa/Attic 0 552 775
+CTMFS .ctm_status 545 552 664 d9ccd2a84a9dbb8db56ba85663adebf0 \\
+e2a10c6f66428981782a0a18a789ee2e 12
+cvs-cur 485
+
+CTMFN CVSROOT/commitlogs/gnu 545 552 664 \\
+5d7bc3549140d860bd9641b5782c002d 7fb04ed84b48160c9b8eea84b4c0b6e3 394
+a6936 21
+ache 95/03/24 09:59:50
+
+ Modified: gnu/lib/libdialog kernel.c prgbox.c
+ Log:
+[...]
+CTM_END 74ddd298d76215ae45a077a4b6a74e9c
+.Ed
+.Sh SEE ALSO
+.Xr ctm 1 ,
+.Xr ctm_rmail 1 ,
+.Xr ed 1
+.Sh HISTORY
+Initial trials ran during the
+.Fx 1.1.5 ,
+and many bugs and
+methods were hashed out.
+The CTM system has been made publicly available in
+.Fx 2.1 .
+.Sh AUTHORS
+The CTM system has been designed and implemented by
+.An Poul-Henning Kamp
+.Aq phk@FreeBSD.org .
+.Pp
+.An Joerg Wunsch
+.Aq joerg@FreeBSD.org
+wrote this man-page.
diff --git a/usr.sbin/ctm/ctm/ctm.c b/usr.sbin/ctm/ctm/ctm.c
new file mode 100644
index 0000000..0ea4895
--- /dev/null
+++ b/usr.sbin/ctm/ctm/ctm.c
@@ -0,0 +1,331 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ *
+ * $FreeBSD$
+ *
+ * This is the client program of 'CTM'. It will apply a CTM-patch to a
+ * collection of files.
+ *
+ * Options we'd like to see:
+ *
+ * -a Attempt best effort.
+ * -d <int> Debug TBD.
+ * -m <mail-addr> Email me instead.
+ * -r <name> Reconstruct file.
+ * -R <file> Read list of files to reconstruct.
+ *
+ * Options we have:
+ * -b <dir> Base-dir
+ * -B <file> Backup to tar-file.
+ * -t Tar command (default as in TARCMD).
+ * -c Check it out, don't do anything.
+ * -F Force
+ * -q Tell us less.
+ * -T <tmpdir>. Temporary files.
+ * -u Set all file modification times to the timestamp
+ * -v Tell us more.
+ * -V <level> Tell us more level = number of -v
+ * -k Keep files and directories that would have been removed.
+ * -l List actions.
+ *
+ * Options we don't actually use:
+ * -p Less paranoid.
+ * -P Paranoid.
+ */
+
+#define EXTERN /* */
+#include <paths.h>
+#include "ctm.h"
+
+#define CTM_STATUS ".ctm_status"
+
+extern int Proc(char *, unsigned applied);
+
+int
+main(int argc, char **argv)
+{
+ int stat=0, err=0;
+ int c;
+ unsigned applied = 0;
+ FILE *statfile;
+ struct CTM_Filter *nfilter = NULL; /* new filter */
+ u_char * basedir;
+
+ basedir = NULL;
+ Verbose = 1;
+ Paranoid = 1;
+ SetTime = 0;
+ KeepIt = 0;
+ ListIt = 0;
+ BackupFile = NULL;
+ TarCmd = TARCMD;
+ LastFilter = FilterList = NULL;
+ TmpDir = getenv("TMPDIR");
+ if (TmpDir == NULL)
+ TmpDir = strdup(_PATH_TMP);
+ setbuf(stderr,0);
+ setbuf(stdout,0);
+
+ while((c=getopt(argc,argv,"ab:B:cd:e:Fklm:pPqr:R:t:T:uV:vx:")) != -1) {
+ switch (c) {
+ case 'b': basedir = optarg; break; /* Base Directory */
+ case 'B': BackupFile = optarg; break;
+ case 'c': CheckIt++; break; /* Only check it */
+ case 'F': Force = 1; break;
+ case 'k': KeepIt++; break; /* Don't do removes */
+ case 'l': ListIt++; break; /* Only list actions and files */
+ case 'p': Paranoid--; break; /* Less Paranoid */
+ case 'P': Paranoid++; break; /* More Paranoid */
+ case 'q': Verbose--; break; /* Quiet */
+ case 't': TarCmd = optarg; break; /* archiver command */
+ case 'T': TmpDir = optarg; break; /* set temporary directory */
+ case 'u': SetTime++; break; /* Set timestamp on files */
+ case 'v': Verbose++; break; /* Verbose */
+ case 'V': sscanf(optarg,"%d", &c); /* Verbose */
+ Verbose += c;
+ break;
+ case 'e': /* filter expressions */
+ case 'x':
+ if (NULL == (nfilter = Malloc(sizeof(struct CTM_Filter)))) {
+ warnx("out of memory for expressions: \"%s\"", optarg);
+ stat++;
+ break;
+ }
+
+ (void) memset(nfilter, 0, sizeof(struct CTM_Filter));
+
+ if (0 != (err =
+ regcomp(&nfilter->CompiledRegex, optarg, REG_NOSUB))) {
+
+ char errmsg[128];
+
+ regerror(err, &nfilter->CompiledRegex, errmsg,
+ sizeof(errmsg));
+ warnx("regular expression: \"%s\"", errmsg);
+ stat++;
+ break;
+ }
+
+ /* note whether the filter enables or disables on match */
+ nfilter->Action =
+ (('e' == c) ? CTM_FILTER_ENABLE : CTM_FILTER_DISABLE);
+
+ /* link in the expression into the list */
+ nfilter->Next = NULL;
+ if (NULL == FilterList) {
+ LastFilter = FilterList = nfilter; /* init head and tail */
+ } else { /* place at tail */
+ LastFilter->Next = nfilter;
+ LastFilter = nfilter;
+ }
+ break;
+ case ':':
+ warnx("option '%c' requires an argument",optopt);
+ stat++;
+ break;
+ case '?':
+ warnx("option '%c' not supported",optopt);
+ stat++;
+ break;
+ default:
+ warnx("option '%c' not yet implemented",optopt);
+ break;
+ }
+ }
+
+ if(stat) {
+ warnx("%d errors during option processing",stat);
+ return Exit_Pilot;
+ }
+ stat = Exit_Done;
+ argc -= optind;
+ argv += optind;
+
+ if (basedir == NULL) {
+ Buffer = (u_char *)Malloc(BUFSIZ + strlen(SUBSUFF) +1);
+ CatPtr = Buffer;
+ *Buffer = '\0';
+ } else {
+ Buffer = (u_char *)Malloc(strlen(basedir)+ BUFSIZ + strlen(SUBSUFF) +1);
+ strcpy(Buffer, basedir);
+ CatPtr = Buffer + strlen(basedir);
+ if (CatPtr[-1] != '/') {
+ strcat(Buffer, "/");
+ CatPtr++;
+ }
+ }
+ strcat(Buffer, CTM_STATUS);
+
+ if(ListIt)
+ applied = 0;
+ else
+ if((statfile = fopen(Buffer, "r")) == NULL) {
+ if (Verbose > 0)
+ warnx("warning: %s not found", Buffer);
+ } else {
+ fscanf(statfile, "%*s %u", &applied);
+ fclose(statfile);
+ }
+
+ if(!argc)
+ stat |= Proc("-", applied);
+
+ while(argc-- && stat == Exit_Done) {
+ stat |= Proc(*argv++, applied);
+ stat &= ~(Exit_Version | Exit_NoMatch);
+ }
+
+ if(stat == Exit_Done)
+ stat = Exit_OK;
+
+ if(Verbose > 0)
+ warnx("exit(%d)",stat);
+
+ if (FilterList)
+ for (nfilter = FilterList; nfilter; ) {
+ struct CTM_Filter *tmp = nfilter->Next;
+ Free(nfilter);
+ nfilter = tmp;
+ }
+ return stat;
+}
+
+int
+Proc(char *filename, unsigned applied)
+{
+ FILE *f;
+ int i;
+ char *p = strrchr(filename,'.');
+
+ if(!strcmp(filename,"-")) {
+ p = 0;
+ f = stdin;
+ } else if(p && (!strcmp(p,".gz") || !strcmp(p,".Z"))) {
+ p = alloca(20 + strlen(filename));
+ strcpy(p,"gunzip < ");
+ strcat(p,filename);
+ f = popen(p,"r");
+ if(!f) { warn("%s", p); return Exit_Garbage; }
+ } else {
+ p = 0;
+ f = fopen(filename,"r");
+ }
+ if(!f) {
+ warn("%s", filename);
+ return Exit_Garbage;
+ }
+
+ if(Verbose > 1)
+ fprintf(stderr,"Working on <%s>\n",filename);
+
+ Delete(FileName);
+ FileName = String(filename);
+
+ /* If we cannot seek, we're doomed, so copy to a tmp-file in that case */
+ if(!p && -1 == fseek(f,0,SEEK_END)) {
+ char *fn;
+ FILE *f2;
+ int fd;
+
+ if (asprintf(&fn, "%s/CTMclient.XXXXXXXXXX", TmpDir) == -1) {
+ fprintf(stderr, "Cannot allocate memory\n");
+ fclose(f);
+ return Exit_Broke;
+ }
+ if ((fd = mkstemp(fn)) == -1 || (f2 = fdopen(fd, "w+")) == NULL) {
+ warn("%s", fn);
+ free(fn);
+ if (fd != -1)
+ close(fd);
+ fclose(f);
+ return Exit_Broke;
+ }
+ unlink(fn);
+ if (Verbose > 0)
+ fprintf(stderr,"Writing tmp-file \"%s\"\n",fn);
+ free(fn);
+ while(EOF != (i=getc(f)))
+ if(EOF == putc(i,f2)) {
+ fclose(f2);
+ return Exit_Broke;
+ }
+ fclose(f);
+ f = f2;
+ }
+
+ if(!p)
+ rewind(f);
+
+ if((i=Pass1(f, applied)))
+ goto exit_and_close;
+
+ if(ListIt) {
+ i = Exit_Done;
+ goto exit_and_close;
+ }
+
+ if(!p) {
+ rewind(f);
+ } else {
+ pclose(f);
+ f = popen(p,"r");
+ if(!f) { warn("%s", p); return Exit_Broke; }
+ }
+
+ i=Pass2(f);
+
+ if(!p) {
+ rewind(f);
+ } else {
+ pclose(f);
+ f = popen(p,"r");
+ if(!f) { warn("%s", p); return Exit_Broke; }
+ }
+
+ if(i) {
+ if((!Force) || (i & ~Exit_Forcible))
+ goto exit_and_close;
+ }
+
+ if(CheckIt) {
+ if (Verbose > 0)
+ fprintf(stderr,"All checks out ok.\n");
+ i = Exit_Done;
+ goto exit_and_close;
+ }
+
+ /* backup files if requested */
+ if(BackupFile) {
+
+ i = PassB(f);
+
+ if(!p) {
+ rewind(f);
+ } else {
+ pclose(f);
+ f = popen(p,"r");
+ if(!f) { warn("%s", p); return Exit_Broke; }
+ }
+ }
+
+ i=Pass3(f);
+
+exit_and_close:
+ if(!p)
+ fclose(f);
+ else
+ pclose(f);
+
+ if(i)
+ return i;
+
+ if (Verbose > 0)
+ fprintf(stderr,"All done ok\n");
+
+ return Exit_Done;
+}
diff --git a/usr.sbin/ctm/ctm/ctm.h b/usr.sbin/ctm/ctm/ctm.h
new file mode 100644
index 0000000..abee2d4
--- /dev/null
+++ b/usr.sbin/ctm/ctm/ctm.h
@@ -0,0 +1,164 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ *
+ * $FreeBSD$
+ *
+ */
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <md5.h>
+#include <regex.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/time.h>
+
+#define VERSION "2.0"
+#define MAXSIZE (1024*1024*40)
+
+#define SUBSUFF ".ctm"
+#define TMPSUFF ".ctmtmp"
+#define TARCMD "tar -rf %s -T -"
+
+/* The fields... */
+#define CTM_F_MASK 0xff
+#define CTM_F_Name 0x01
+#define CTM_F_Uid 0x02
+#define CTM_F_Gid 0x03
+#define CTM_F_Mode 0x04
+#define CTM_F_MD5 0x05
+#define CTM_F_Count 0x06
+#define CTM_F_Bytes 0x07
+
+/* The qualifiers... */
+#define CTM_Q_MASK 0xff00
+#define CTM_Q_Name_File 0x0100
+#define CTM_Q_Name_Dir 0x0200
+#define CTM_Q_Name_New 0x0400
+#define CTM_Q_Name_Subst 0x0800
+#define CTM_Q_MD5_After 0x0100
+#define CTM_Q_MD5_Before 0x0200
+#define CTM_Q_MD5_Chunk 0x0400
+#define CTM_Q_MD5_Force 0x0800
+
+struct CTM_Syntax {
+ char *Key; /* CTM key for operation */
+ int *List; /* List of operations */
+ };
+
+extern struct CTM_Syntax Syntax[];
+
+struct CTM_Filter {
+ struct CTM_Filter *Next; /* next filter in the list */
+ int Action; /* enable or disable */
+ regex_t CompiledRegex; /* compiled regex */
+};
+
+#define CTM_FILTER_DISABLE 0
+#define CTM_FILTER_ENABLE 1
+
+#define Malloc malloc
+#define Free free
+#define Delete(foo) if (!foo) ; else {Free(foo); foo = 0; }
+#define String(foo) strdup(foo)
+
+#ifndef EXTERN
+# define EXTERN extern
+#endif
+EXTERN u_char *Version;
+EXTERN u_char *Name;
+EXTERN u_char *Nbr;
+EXTERN u_char *TimeStamp;
+EXTERN u_char *Prefix;
+EXTERN u_char *FileName;
+EXTERN u_char *TmpDir;
+EXTERN u_char *CatPtr;
+EXTERN u_char *Buffer;
+EXTERN u_char *BackupFile;
+EXTERN u_char *TarCmd;
+
+/*
+ * Paranoid -- Just in case they should be after us...
+ * 0 not at all.
+ * 1 normal.
+ * 2 somewhat.
+ * 3 you bet!.
+ *
+ * Verbose -- What to tell mom...
+ * 0 Nothing which wouldn't surprise.
+ * 1 Normal.
+ * 2 Show progress '.'.
+ * 3 Show progress names, and actions.
+ * 4 even more...
+ * and so on
+ *
+ * ExitCode -- our Epitaph
+ * 0 Perfect, all input digested, no problems
+ * 1 Bad input, no point in retrying.
+ * 2 Pilot error, commandline problem &c
+ * 4 Out of resources.
+ * 8 Destination-tree not correct.
+ * 16 Destination-tree not correct, can force.
+ * 32 Internal problems.
+ *
+ */
+
+EXTERN int Paranoid;
+EXTERN int Verbose;
+EXTERN int Exit;
+EXTERN int Force;
+EXTERN int CheckIt;
+EXTERN int KeepIt;
+EXTERN int ListIt;
+EXTERN int SetTime;
+EXTERN struct timeval Times[2];
+EXTERN struct CTM_Filter *FilterList;
+EXTERN struct CTM_Filter *LastFilter;
+
+#define Exit_OK 0
+#define Exit_Garbage 1
+#define Exit_Pilot 2
+#define Exit_Broke 4
+#define Exit_NotOK 8
+#define Exit_Forcible 16
+#define Exit_Mess 32
+#define Exit_Done 64
+#define Exit_Version 128
+#define Exit_NoMatch 256
+
+void Fatal_(int ln, char *fn, char *kind);
+#define Fatal(foo) Fatal_(__LINE__,__FILE__,foo)
+#define Assert() Fatal_(__LINE__,__FILE__,"Assert failed.")
+#define WRONG {Assert(); return Exit_Mess;}
+
+u_char * Ffield(FILE *fd, MD5_CTX *ctx,u_char term);
+u_char * Fname(FILE *fd, MD5_CTX *ctx,u_char term,int qual, int verbose);
+
+int Fbytecnt(FILE *fd, MD5_CTX *ctx, u_char term);
+
+u_char * Fdata(FILE *fd, int u_chars, MD5_CTX *ctx);
+
+#define GETFIELD(p,q) if(!((p)=Ffield(fd,&ctx,(q)))) return BADREAD
+#define GETFIELDCOPY(p,q) if(!((p)=Ffield(fd,&ctx,(q)))) return BADREAD; else p=String(p)
+#define GETBYTECNT(p,q) if(0 >((p)= Fbytecnt(fd,&ctx,(q)))) return BADREAD
+#define GETDATA(p,q) if(!((p) = Fdata(fd,(q),&ctx))) return BADREAD
+#define GETNAMECOPY(p,q,r,v) if(!((p)=Fname(fd,&ctx,(q),(r),(v)))) return BADREAD; else p=String(p)
+
+int Pass1(FILE *fd, unsigned applied);
+int Pass2(FILE *fd);
+int PassB(FILE *fd);
+int Pass3(FILE *fd);
+
+int ctm_edit(u_char *script, int length, char *filein, char *fileout);
diff --git a/usr.sbin/ctm/ctm/ctm_ed.c b/usr.sbin/ctm/ctm/ctm_ed.c
new file mode 100644
index 0000000..e3ec464
--- /dev/null
+++ b/usr.sbin/ctm/ctm/ctm_ed.c
@@ -0,0 +1,114 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ *
+ * $FreeBSD$
+ *
+ */
+
+#include "ctm.h"
+
+int
+ctm_edit(u_char *script, int length, char *filein, char *fileout)
+{
+ u_char *ep, cmd;
+ int ln, ln2, iln, ret=0, c;
+ FILE *fi=0,*fo=0;
+
+ fi = fopen(filein,"r");
+ if(!fi) {
+ warn("%s", filein);
+ return 8;
+ }
+
+ fo = fopen(fileout,"w");
+ if(!fo) {
+ warn("%s", fileout);
+ fclose(fi);
+ return 4;
+ }
+ iln = 1;
+ for(ep=script;ep < script+length;) {
+ cmd = *ep++;
+ if(cmd != 'a' && cmd != 'd') { ret = 1; goto bye; }
+ ln = 0;
+ while(isdigit(*ep)) {
+ ln *= 10;
+ ln += (*ep++ - '0');
+ }
+ if(*ep++ != ' ') { ret = 1; goto bye; }
+ ln2 = 0;
+ while(isdigit(*ep)) {
+ ln2 *= 10;
+ ln2 += (*ep++ - '0');
+ }
+ if(*ep++ != '\n') { ret = 1; goto bye; }
+
+
+ if(cmd == 'd') {
+ while(iln < ln) {
+ c = getc(fi);
+ if(c == EOF) { ret = 1; goto bye; }
+ putc(c,fo);
+ if(c == '\n')
+ iln++;
+ }
+ while(ln2) {
+ c = getc(fi);
+ if(c == EOF) { ret = 1; goto bye; }
+ if(c != '\n')
+ continue;
+ ln2--;
+ iln++;
+ }
+ continue;
+ }
+ if(cmd == 'a') {
+ while(iln <= ln) {
+ c = getc(fi);
+ if(c == EOF) { ret = 1; goto bye; }
+ putc(c,fo);
+ if(c == '\n')
+ iln++;
+ }
+ while(ln2) {
+ c = *ep++;
+ putc(c,fo);
+ if(c != '\n')
+ continue;
+ ln2--;
+ }
+ continue;
+ }
+ ret = 1;
+ goto bye;
+ }
+ while(1) {
+ c = getc(fi);
+ if(c == EOF) break;
+ putc(c,fo);
+ }
+ ret = 0;
+bye:
+ if(fi) {
+ if(fclose(fi) != 0) {
+ warn("%s", filein);
+ ret = 1;
+ }
+ }
+ if(fo) {
+ if(fflush(fo) != 0) {
+ warn("%s", fileout);
+ ret = 1;
+ }
+ if(fclose(fo) != 0) {
+ warn("%s", fileout);
+ ret = 1;
+ }
+ }
+ return ret;
+}
diff --git a/usr.sbin/ctm/ctm/ctm_input.c b/usr.sbin/ctm/ctm/ctm_input.c
new file mode 100644
index 0000000..0a3eecb
--- /dev/null
+++ b/usr.sbin/ctm/ctm/ctm_input.c
@@ -0,0 +1,138 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ *
+ * $FreeBSD$
+ *
+ */
+
+#include "ctm.h"
+
+/*---------------------------------------------------------------------------*/
+void
+Fatal_(int ln, char *fn, char *kind)
+{
+ if(Verbose > 2)
+ fprintf(stderr,"Fatal error. (%s:%d)\n",fn,ln);
+ fprintf(stderr,"%s Fatal error: %s\n",FileName, kind);
+}
+#define Fatal(foo) Fatal_(__LINE__,__FILE__,foo)
+#define Assert() Fatal_(__LINE__,__FILE__,"Assert failed.")
+
+/*---------------------------------------------------------------------------*/
+/* get next field, check that the terminating whitespace is what we expect */
+u_char *
+Ffield(FILE *fd, MD5_CTX *ctx,u_char term)
+{
+ static u_char buf[BUFSIZ];
+ int i,l;
+
+ for(l=0;;) {
+ if((i=getc(fd)) == EOF) {
+ Fatal("Truncated patch.");
+ return 0;
+ }
+ buf[l++] = i;
+ if(isspace(i))
+ break;
+ if(l >= sizeof buf) {
+ Fatal("Corrupt patch.");
+ printf("Token is too long.\n");
+ return 0;
+ }
+ }
+ buf[l] = '\0';
+ MD5Update(ctx,buf,l);
+ if(buf[l-1] != term) {
+ Fatal("Corrupt patch.");
+ fprintf(stderr,"Expected \"%s\" but didn't find it {%02x}.\n",
+ term == '\n' ? "\\n" : " ",buf[l-1]);
+ if(Verbose > 4)
+ fprintf(stderr,"{%s}\n",buf);
+ return 0;
+ }
+ buf[--l] = '\0';
+ if(Verbose > 4)
+ fprintf(stderr,"<%s>\n",buf);
+ return buf;
+}
+
+int
+Fbytecnt(FILE *fd, MD5_CTX *ctx, u_char term)
+{
+ u_char *p,*q;
+ int u_chars=0;
+
+ p = Ffield(fd,ctx,term);
+ if(!p) return -1;
+ for(q=p;*q;q++) {
+ if(!isdigit(*q)) {
+ Fatal("Bytecount contains non-digit.");
+ return -1;
+ }
+ u_chars *= 10;
+ u_chars += (*q - '0');
+ }
+ 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..026a320
--- /dev/null
+++ b/usr.sbin/ctm/ctm/ctm_pass1.c
@@ -0,0 +1,250 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ *
+ * $FreeBSD$
+ *
+ */
+
+#include "ctm.h"
+#define BADREAD 1
+
+/*---------------------------------------------------------------------------*/
+/* Pass1 -- Validate the incoming CTM-file.
+ */
+
+int
+Pass1(FILE *fd, unsigned applied)
+{
+ u_char *p,*q;
+ MD5_CTX ctx;
+ int i,j,sep,cnt;
+ u_char *md5=0,*name=0,*trash=0;
+ struct CTM_Syntax *sp;
+ int slashwarn=0, match=0, total_matches=0;
+ unsigned current;
+ char md5_1[33];
+
+ if(Verbose>3)
+ printf("Pass1 -- Checking integrity of incoming CTM-patch\n");
+ MD5Init (&ctx);
+
+ GETFIELD(p,' '); /* CTM_BEGIN */
+ if(strcmp(p,"CTM_BEGIN")) {
+ Fatal("Probably not a CTM-patch at all.");
+ if(Verbose>3)
+ fprintf(stderr,"Expected \"CTM_BEGIN\" got \"%s\".\n",p);
+ return 1;
+ }
+
+ GETFIELDCOPY(Version,' '); /* <Version> */
+ if(strcmp(Version,VERSION)) {
+ Fatal("CTM-patch is wrong version.");
+ if(Verbose>3)
+ fprintf(stderr,"Expected \"%s\" got \"%s\".\n",VERSION,p);
+ return 1;
+ }
+
+ GETFIELDCOPY(Name,' '); /* <Name> */
+ GETFIELDCOPY(Nbr,' '); /* <Nbr> */
+ GETFIELDCOPY(TimeStamp,' '); /* <TimeStamp> */
+ GETFIELDCOPY(Prefix,'\n'); /* <Prefix> */
+
+ sscanf(Nbr, "%u", &current);
+ if (FilterList || ListIt)
+ current = 0; /* ignore if -l or if filters are present */
+ if(current && current <= applied) {
+ if(Verbose > 0)
+ fprintf(stderr,"Delta number %u is already applied; ignoring.\n",
+ current);
+ return Exit_Version;
+ }
+
+ for(;;) {
+ Delete(md5);
+ Delete(name);
+ Delete(trash);
+ cnt = -1;
+ /* if a filter list is defined we assume that all pathnames require
+ an action opposite to that requested by the first filter in the
+ list.
+ If no filter is defined, all pathnames are assumed to match. */
+ match = (FilterList ? !(FilterList->Action) : CTM_FILTER_ENABLE);
+
+ GETFIELD(p,' '); /* CTM_something */
+
+ if (p[0] != 'C' || p[1] != 'T' || p[2] != 'M') {
+ Fatal("Expected CTM keyword.");
+ fprintf(stderr,"Got [%s]\n",p);
+ return 1;
+ }
+
+ if(!strcmp(p+3,"_END"))
+ break;
+
+ for(sp=Syntax;sp->Key;sp++)
+ if(!strcmp(p+3,sp->Key))
+ goto found;
+ Fatal("Expected CTM keyword.");
+ fprintf(stderr,"Got [%s]\n",p);
+ return 1;
+ found:
+ if(Verbose > 5)
+ fprintf(stderr,"%s ",sp->Key);
+ for(i=0;(j = sp->List[i]);i++) {
+ if (sp->List[i+1] && (sp->List[i+1] & CTM_F_MASK) != CTM_F_Bytes)
+ sep = ' ';
+ else
+ sep = '\n';
+
+ if(Verbose > 5)
+ fprintf(stderr," %x(%d)",sp->List[i],sep);
+
+ switch (j & CTM_F_MASK) {
+ case CTM_F_Name: /* XXX check for garbage and .. */
+ GETFIELDCOPY(name,sep);
+ j = strlen(name);
+ if(name[j-1] == '/' && !slashwarn) {
+ fprintf(stderr,"Warning: contains trailing slash\n");
+ slashwarn++;
+ }
+ if (name[0] == '/') {
+ Fatal("Absolute paths are illegal.");
+ return Exit_Mess;
+ }
+ q = name;
+ for (;;) {
+ if (q[0] == '.' && q[1] == '.')
+ if (q[2] == '/' || q[2] == '\0') {
+ Fatal("Paths containing '..' are illegal.");
+ return Exit_Mess;
+ }
+ if ((q = strchr(q, '/')) == NULL)
+ break;
+ q++;
+ }
+
+ /* if we have been asked to `keep' files then skip
+ removes; i.e. we don't match these entries at
+ all. */
+ if (KeepIt &&
+ (!strcmp(sp->Key,"DR") || !strcmp(sp->Key,"FR"))) {
+ match = CTM_FILTER_DISABLE;
+ break;
+ }
+
+ /* If filter expression have been defined, match the
+ path name against the expression list. */
+
+ if (FilterList) {
+ struct CTM_Filter *filter;
+
+ for (filter = FilterList; filter;
+ filter = filter->Next) {
+ if (0 == regexec(&filter->CompiledRegex, name,
+ 0, 0, 0))
+ /* if the name matches, adopt the
+ action */
+ match = filter->Action;
+ }
+ }
+
+ /* Add up the total number of matches */
+ total_matches += match;
+ break;
+ case CTM_F_Uid:
+ GETFIELD(p,sep);
+ while(*p) {
+ if(!isdigit(*p)) {
+ Fatal("Non-digit in uid.");
+ return 32;
+ }
+ p++;
+ }
+ break;
+ case CTM_F_Gid:
+ GETFIELD(p,sep);
+ while(*p) {
+ if(!isdigit(*p)) {
+ Fatal("Non-digit in gid.");
+ return 32;
+ }
+ p++;
+ }
+ break;
+ case CTM_F_Mode:
+ GETFIELD(p,sep);
+ while(*p) {
+ if(!isdigit(*p)) {
+ Fatal("Non-digit in mode.");
+ return 32;
+ }
+ p++;
+ }
+ break;
+ case CTM_F_MD5:
+ if(j & CTM_Q_MD5_Chunk) {
+ GETFIELDCOPY(md5,sep); /* XXX check for garbage */
+ } else if(j & CTM_Q_MD5_Before) {
+ GETFIELD(p,sep); /* XXX check for garbage */
+ } else if(j & CTM_Q_MD5_After) {
+ GETFIELD(p,sep); /* XXX check for garbage */
+ } else {
+ fprintf(stderr,"List = 0x%x\n",j);
+ Fatal("Unqualified MD5.");
+ return 32;
+ }
+ break;
+ case CTM_F_Count:
+ GETBYTECNT(cnt,sep);
+ break;
+ case CTM_F_Bytes:
+ if(cnt < 0) WRONG
+ GETDATA(trash,cnt);
+ p = MD5Data(trash,cnt,md5_1);
+ if(md5 && strcmp(md5,p)) {
+ Fatal("Internal MD5 failed.");
+ return Exit_Garbage;
+ default:
+ fprintf(stderr,"List = 0x%x\n",j);
+ Fatal("List had garbage.");
+ return Exit_Garbage;
+ }
+ }
+ }
+ if(Verbose > 5)
+ putc('\n',stderr);
+ if(ListIt && match)
+ printf("> %s %s\n", sp->Key, name);
+ }
+
+ Delete(md5);
+ Delete(name);
+ Delete(trash);
+
+ q = MD5End (&ctx,md5_1);
+ if(Verbose > 2)
+ printf("Expecting Global MD5 <%s>\n",q);
+ GETFIELD(p,'\n'); /* <MD5> */
+ if(Verbose > 2)
+ printf("Reference Global MD5 <%s>\n",p);
+ if(strcmp(q,p)) {
+ Fatal("MD5 sum doesn't match.");
+ fprintf(stderr,"\tI have:<%s>\n",q);
+ fprintf(stderr,"\tShould have been:<%s>\n",p);
+ return Exit_Garbage;
+ }
+ if (-1 != getc(fd)) {
+ if(!Force) {
+ Fatal("Trailing junk in CTM-file. Can Force with -F.");
+ return 16;
+ }
+ }
+ if ((Verbose > 1) && (0 == total_matches))
+ printf("No matches in \"%s\"\n", FileName);
+ return (total_matches ? Exit_OK : Exit_NoMatch);
+}
diff --git a/usr.sbin/ctm/ctm/ctm_pass2.c b/usr.sbin/ctm/ctm/ctm_pass2.c
new file mode 100644
index 0000000..a0b857a
--- /dev/null
+++ b/usr.sbin/ctm/ctm/ctm_pass2.c
@@ -0,0 +1,301 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ *
+ * $FreeBSD$
+ *
+ */
+
+#include "ctm.h"
+#define BADREAD 32
+
+/*---------------------------------------------------------------------------*/
+/* Pass2 -- Validate the incoming CTM-file.
+ */
+
+int
+Pass2(FILE *fd)
+{
+ u_char *p,*q,*md5=0;
+ MD5_CTX ctx;
+ int i,j,sep,cnt,fdesc;
+ u_char *trash=0,*name=0;
+ struct CTM_Syntax *sp;
+ struct stat st;
+ int ret = 0;
+ int match = 0;
+ char md5_1[33];
+ struct CTM_Filter *filter;
+ FILE *ed = NULL;
+ static char *template = NULL;
+
+ if(Verbose>3)
+ printf("Pass2 -- Checking if CTM-patch will apply\n");
+ MD5Init (&ctx);
+
+ GETFIELD(p,' '); if(strcmp("CTM_BEGIN",p)) WRONG
+ GETFIELD(p,' '); if(strcmp(Version,p)) WRONG
+ GETFIELD(p,' '); if(strcmp(Name,p)) WRONG
+ /* XXX Lookup name in /etc/ctm,conf, read stuff */
+ GETFIELD(p,' '); if(strcmp(Nbr,p)) WRONG
+ /* XXX Verify that this is the next patch to apply */
+ GETFIELD(p,' '); if(strcmp(TimeStamp,p)) WRONG
+ GETFIELD(p,'\n'); if(strcmp(Prefix,p)) WRONG
+ /* XXX drop or use ? */
+
+ for(;;) {
+ Delete(trash);
+ Delete(name);
+ Delete(md5);
+ cnt = -1;
+
+ /* if a filter list was specified, check file name against
+ the filters specified
+ if no filter was given operate on all files. */
+ match = (FilterList ?
+ !(FilterList->Action) : CTM_FILTER_ENABLE);
+
+ GETFIELD(p,' ');
+
+ if (p[0] != 'C' || p[1] != 'T' || p[2] != 'M') WRONG
+
+ if(!strcmp(p+3,"_END"))
+ break;
+
+ for(sp=Syntax;sp->Key;sp++)
+ if(!strcmp(p+3,sp->Key))
+ goto found;
+ WRONG
+ found:
+ for(i=0;(j = sp->List[i]);i++) {
+ if (sp->List[i+1] && (sp->List[i+1] & CTM_F_MASK) != CTM_F_Bytes)
+ sep = ' ';
+ else
+ sep = '\n';
+
+ switch (j & CTM_F_MASK) {
+ case CTM_F_Name:
+ GETNAMECOPY(name,sep,j,0);
+ /* If `keep' was specified, we won't remove any files,
+ so don't check if the file exists */
+ if (KeepIt &&
+ (!strcmp(sp->Key,"FR") || !strcmp(sp->Key,"DR"))) {
+ match = CTM_FILTER_DISABLE;
+ break;
+ }
+
+ for (filter = FilterList; filter; filter = filter->Next) if (0 == regexec(&filter->CompiledRegex, name,
+ 0, 0, 0)) {
+ match = filter->Action;
+ }
+
+ if (CTM_FILTER_DISABLE == match)
+ break; /* should ignore this file */
+
+ /* XXX Check DR DM rec's for parent-dir */
+ if(j & CTM_Q_Name_New) {
+ /* XXX Check DR FR rec's for item */
+ if(-1 != stat(name,&st)) {
+ fprintf(stderr," %s: %s exists.\n",
+ sp->Key,name);
+ ret |= Exit_Forcible;
+ }
+ break;
+ }
+ if(-1 == stat(name,&st)) {
+ fprintf(stderr," %s: %s doesn't exist.\n",
+ sp->Key,name);
+ if (sp->Key[1] == 'R')
+ ret |= Exit_Forcible;
+ else
+ ret |= Exit_NotOK;
+ break;
+ }
+ if (SetTime && getuid() && (getuid() != st.st_uid)) {
+ fprintf(stderr,
+ " %s: %s not mine, cannot set time.\n",
+ sp->Key,name);
+ ret |= Exit_NotOK;
+ }
+ if (j & CTM_Q_Name_Dir) {
+ if((st.st_mode & S_IFMT) != S_IFDIR) {
+ fprintf(stderr,
+ " %s: %s exist, but isn't dir.\n",
+ sp->Key,name);
+ ret |= Exit_NotOK;
+ }
+ break;
+ }
+ if (j & CTM_Q_Name_File) {
+ if((st.st_mode & S_IFMT) != S_IFREG) {
+ fprintf(stderr,
+ " %s: %s exist, but isn't file.\n",
+ sp->Key,name);
+ ret |= Exit_NotOK;
+ }
+ break;
+ }
+ break;
+ case CTM_F_Uid:
+ case CTM_F_Gid:
+ case CTM_F_Mode:
+ GETFIELD(p,sep);
+ break;
+ case CTM_F_MD5:
+ if(!name) WRONG
+ if(j & CTM_Q_MD5_Before) {
+ char *tmp;
+ GETFIELD(p,sep);
+ if(match && (st.st_mode & S_IFMT) == S_IFREG &&
+ (tmp = MD5File(name,md5_1)) != NULL &&
+ strcmp(tmp,p)) {
+ fprintf(stderr," %s: %s md5 mismatch.\n",
+ sp->Key,name);
+ GETFIELDCOPY(md5,sep);
+ if(md5 != NULL && strcmp(tmp,md5) == 0) {
+ fprintf(stderr," %s: %s already applied.\n",
+ sp->Key,name);
+ match = CTM_FILTER_DISABLE;
+ } else if(j & CTM_Q_MD5_Force) {
+ if(Force)
+ fprintf(stderr," Can and will force.\n");
+ else
+ fprintf(stderr," Could have forced.\n");
+ ret |= Exit_Forcible;
+ } else {
+ ret |= Exit_NotOK;
+ }
+ }
+ break;
+ } else if(j & CTM_Q_MD5_After) {
+ if(md5 == NULL) {
+ GETFIELDCOPY(md5,sep);
+ }
+ break;
+ }
+ /* Unqualified MD5 */
+ WRONG
+ break;
+ case CTM_F_Count:
+ GETBYTECNT(cnt,sep);
+ break;
+ case CTM_F_Bytes:
+ if(cnt < 0) WRONG
+ GETDATA(trash,cnt);
+ if (!match)
+ break;
+ if (!template) {
+ if (asprintf(&template, "%s/CTMclientXXXXXX",
+ TmpDir) == -1) {
+ fprintf(stderr, " %s: malloc failed.\n",
+ sp->Key);
+ ret |= Exit_Mess;
+ return ret;
+ }
+ }
+ if(!strcmp(sp->Key,"FN")) {
+ if ((p = strdup(template)) == NULL) {
+ fprintf(stderr, " %s: malloc failed.\n",
+ sp->Key);
+ ret |= Exit_Mess;
+ return ret;
+ }
+ if ((fdesc = mkstemp(p)) == -1) {
+ fprintf(stderr, " %s: mkstemp failed.\n",
+ sp->Key);
+ ret |= Exit_Mess;
+ Free(p);
+ return ret;
+ }
+ if (close(fdesc) == -1) {
+ fprintf(stderr, " %s: close failed.\n",
+ sp->Key);
+ ret |= Exit_Mess;
+ unlink(p);
+ Free(p);
+ return ret;
+ }
+ j = ctm_edit(trash,cnt,name,p);
+ if(j) {
+ fprintf(stderr," %s: %s edit returned %d.\n",
+ sp->Key,name,j);
+ ret |= j;
+ unlink(p);
+ Free(p);
+ return ret;
+ } else if(strcmp(md5,MD5File(p,md5_1))) {
+ fprintf(stderr," %s: %s edit fails.\n",
+ sp->Key,name);
+ ret |= Exit_Mess;
+ unlink(p);
+ Free(p);
+ return ret;
+ }
+ unlink(p);
+ Free(p);
+ } else if (!strcmp(sp->Key,"FE")) {
+ if ((p = strdup(template)) == NULL) {
+ fprintf(stderr, " %s: malloc failed.\n",
+ sp->Key);
+ ret |= Exit_Mess;
+ return ret;
+ }
+ if ((fdesc = mkstemp(p)) == -1) {
+ fprintf(stderr, " %s: mkstemp failed.\n",
+ sp->Key);
+ ret |= Exit_Mess;
+ Free(p);
+ return ret;
+ }
+ if (close(fdesc) == -1) {
+ fprintf(stderr, " %s: close failed.\n",
+ sp->Key);
+ ret |= Exit_Mess;
+ unlink(p);
+ Free(p);
+ return ret;
+ }
+ ed = popen("ed","w");
+ if (!ed) {
+ WRONG
+ }
+ fprintf(ed,"e %s\n", name);
+ if (cnt != fwrite(trash,1,cnt,ed)) {
+ warn("%s", name);
+ pclose(ed);
+ WRONG
+ }
+ fprintf(ed,"w %s\n",p);
+ if (pclose(ed)) {
+ warn("%s", p);
+ WRONG
+ }
+ if(strcmp(md5,MD5File(p,md5_1))) {
+ fprintf(stderr,"%s %s MD5 didn't come out right\n",
+ sp->Key, name);
+ WRONG
+ }
+ unlink(p);
+ Free(p);
+ }
+
+ break;
+ default: WRONG
+ }
+ }
+ }
+
+ Delete(trash);
+ Delete(name);
+ Delete(md5);
+
+ q = MD5End (&ctx,md5_1);
+ GETFIELD(p,'\n'); /* <MD5> */
+ if(strcmp(q,p)) WRONG
+ if (-1 != getc(fd)) WRONG
+ return ret;
+}
diff --git a/usr.sbin/ctm/ctm/ctm_pass3.c b/usr.sbin/ctm/ctm/ctm_pass3.c
new file mode 100644
index 0000000..34c6d87c1d
--- /dev/null
+++ b/usr.sbin/ctm/ctm/ctm_pass3.c
@@ -0,0 +1,295 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ *
+ * $FreeBSD$
+ *
+ */
+
+#include "ctm.h"
+#define BADREAD 32
+
+/*---------------------------------------------------------------------------*/
+/* Pass3 -- Validate the incoming CTM-file.
+ */
+
+int
+settime(const char *name, const struct timeval *times)
+{
+ if (SetTime)
+ if (utimes(name,times)) {
+ warn("utimes(): %s", name);
+ return -1;
+ }
+ return 0;
+}
+
+int
+Pass3(FILE *fd)
+{
+ u_char *p,*q,buf[BUFSIZ];
+ MD5_CTX ctx;
+ int i,j,sep,cnt;
+ u_char *md5=0,*md5before=0,*trash=0,*name=0,*uid=0,*gid=0,*mode=0;
+ struct CTM_Syntax *sp;
+ FILE *ed=0;
+ struct stat st;
+ char md5_1[33];
+ int match=0;
+ struct timeval times[2];
+ struct CTM_Filter *filter = NULL;
+ if(Verbose>3)
+ printf("Pass3 -- Applying the CTM-patch\n");
+ MD5Init (&ctx);
+
+ GETFIELD(p,' '); if(strcmp("CTM_BEGIN",p)) WRONG
+ GETFIELD(p,' '); if(strcmp(Version,p)) WRONG
+ GETFIELD(p,' '); if(strcmp(Name,p)) WRONG
+ GETFIELD(p,' '); if(strcmp(Nbr,p)) WRONG
+ GETFIELD(p,' '); if(strcmp(TimeStamp,p)) WRONG
+ GETFIELD(p,'\n'); if(strcmp(Prefix,p)) WRONG
+
+ /*
+ * This would be cleaner if mktime() worked in UTC rather than
+ * local time.
+ */
+ if (SetTime) {
+ struct tm tm;
+ char *tz;
+ char buf[5];
+ int i;
+
+#define SUBSTR(off,len) strncpy(buf, &TimeStamp[off], len), buf[len] = '\0'
+#define WRONGDATE { fprintf(stderr, " %s failed date validation\n",\
+ TimeStamp); WRONG}
+
+ if (strlen(TimeStamp) != 15 || TimeStamp[14] != 'Z') WRONGDATE
+ for (i = 0; i < 14; i++)
+ if (!isdigit(TimeStamp[i])) WRONGDATE
+
+ tz = getenv("TZ");
+ if (setenv("TZ", "UTC", 1) < 0) WRONG
+ tzset();
+
+ tm.tm_isdst = tm.tm_gmtoff = 0;
+
+ SUBSTR(0, 4);
+ tm.tm_year = atoi(buf) - 1900;
+ SUBSTR(4, 2);
+ tm.tm_mon = atoi(buf) - 1;
+ if (tm.tm_mon < 0 || tm.tm_mon > 11) WRONGDATE
+ SUBSTR(6, 2);
+ tm.tm_mday = atoi(buf);
+ if (tm.tm_mday < 1 || tm.tm_mday > 31) WRONG;
+ SUBSTR(8, 2);
+ tm.tm_hour = atoi(buf);
+ if (tm.tm_hour > 24) WRONGDATE
+ SUBSTR(10, 2);
+ tm.tm_min = atoi(buf);
+ if (tm.tm_min > 59) WRONGDATE
+ SUBSTR(12, 2);
+ tm.tm_sec = atoi(buf);
+ if (tm.tm_min > 62) WRONGDATE /* allow leap seconds */
+
+ times[0].tv_sec = times[1].tv_sec = mktime(&tm);
+ if (times[0].tv_sec == -1) WRONGDATE
+ times[0].tv_usec = times[1].tv_usec = 0;
+
+ if (tz) {
+ if (setenv("TZ", tz, 1) < 0) WRONGDATE
+ } else {
+ unsetenv("TZ");
+ }
+ }
+
+ for(;;) {
+ Delete(md5);
+ Delete(uid);
+ Delete(gid);
+ Delete(mode);
+ Delete(md5before);
+ Delete(trash);
+ Delete(name);
+ cnt = -1;
+
+ GETFIELD(p,' ');
+
+ if (p[0] != 'C' || p[1] != 'T' || p[2] != 'M') WRONG
+
+ if(!strcmp(p+3,"_END"))
+ break;
+
+ for(sp=Syntax;sp->Key;sp++)
+ if(!strcmp(p+3,sp->Key))
+ goto found;
+ WRONG
+ found:
+ for(i=0;(j = sp->List[i]);i++) {
+ if (sp->List[i+1] && (sp->List[i+1] & CTM_F_MASK) != CTM_F_Bytes)
+ sep = ' ';
+ else
+ sep = '\n';
+
+ switch (j & CTM_F_MASK) {
+ case CTM_F_Name: GETNAMECOPY(name,sep,j, Verbose); break;
+ case CTM_F_Uid: GETFIELDCOPY(uid,sep); break;
+ case CTM_F_Gid: GETFIELDCOPY(gid,sep); break;
+ case CTM_F_Mode: GETFIELDCOPY(mode,sep); break;
+ case CTM_F_MD5:
+ if(j & CTM_Q_MD5_Before)
+ GETFIELDCOPY(md5before,sep);
+ else
+ GETFIELDCOPY(md5,sep);
+ break;
+ case CTM_F_Count: GETBYTECNT(cnt,sep); break;
+ case CTM_F_Bytes: GETDATA(trash,cnt); break;
+ default: WRONG
+ }
+ }
+ /* XXX This should go away. Disallow trailing '/' */
+ j = strlen(name)-1;
+ if(name[j] == '/') name[j] = '\0';
+
+ /*
+ * If a filter list is specified, run thru the filter list and
+ * match `name' against filters. If the name matches, set the
+ * required action to that specified in the filter.
+ * The default action if no filterlist is given is to match
+ * everything.
+ */
+
+ match = (FilterList ? !(FilterList->Action) : CTM_FILTER_ENABLE);
+ for (filter = FilterList; filter; filter = filter->Next) {
+ if (0 == regexec(&filter->CompiledRegex, name,
+ 0, 0, 0)) {
+ match = filter->Action;
+ }
+ }
+
+ if (CTM_FILTER_DISABLE == match) /* skip file if disabled */
+ continue;
+
+ if (Verbose > 0)
+ fprintf(stderr,"> %s %s\n",sp->Key,name);
+ if(!strcmp(sp->Key,"FM") || !strcmp(sp->Key, "FS")) {
+ i = open(name,O_WRONLY|O_CREAT|O_TRUNC,0666);
+ if(i < 0) {
+ warn("%s", name);
+ WRONG
+ }
+ if(cnt != write(i,trash,cnt)) {
+ warn("%s", name);
+ WRONG
+ }
+ close(i);
+ if(strcmp(md5,MD5File(name,md5_1))) {
+ fprintf(stderr," %s %s MD5 didn't come out right\n",
+ sp->Key,name);
+ WRONG
+ }
+ if (settime(name,times)) WRONG
+ continue;
+ }
+ if(!strcmp(sp->Key,"FE")) {
+ ed = popen("ed","w");
+ if(!ed) {
+ WRONG
+ }
+ fprintf(ed,"e %s\n",name);
+ if(cnt != fwrite(trash,1,cnt,ed)) {
+ warn("%s", name);
+ pclose(ed);
+ WRONG
+ }
+ fprintf(ed,"w %s\n",name);
+ if(pclose(ed)) {
+ warn("ed");
+ WRONG
+ }
+ if(strcmp(md5,MD5File(name,md5_1))) {
+ fprintf(stderr," %s %s MD5 didn't come out right\n",
+ sp->Key,name);
+ WRONG
+ }
+ if (settime(name,times)) WRONG
+ continue;
+ }
+ if(!strcmp(sp->Key,"FN")) {
+ strcpy(buf,name);
+ strcat(buf,TMPSUFF);
+ i = ctm_edit(trash,cnt,name,buf);
+ if(i) {
+ fprintf(stderr," %s %s Edit failed with code %d.\n",
+ sp->Key,name,i);
+ WRONG
+ }
+ if(strcmp(md5,MD5File(buf,md5_1))) {
+ fprintf(stderr," %s %s Edit failed MD5 check.\n",
+ sp->Key,name);
+ WRONG
+ }
+ if (rename(buf,name) == -1)
+ WRONG
+ if (settime(name,times)) WRONG
+ continue;
+ }
+ if(!strcmp(sp->Key,"DM")) {
+ if(0 > mkdir(name,0777)) {
+ sprintf(buf,"mkdir -p %s",name);
+ system(buf);
+ }
+ if(0 > stat(name,&st) || ((st.st_mode & S_IFMT) != S_IFDIR)) {
+ fprintf(stderr,"<%s> mkdir failed\n",name);
+ WRONG
+ }
+ if (settime(name,times)) WRONG
+ continue;
+ }
+ if(!strcmp(sp->Key,"FR")) {
+ if (KeepIt) {
+ if (Verbose > 1)
+ printf("<%s> not removed\n", name);
+ }
+ else if (0 != unlink(name)) {
+ fprintf(stderr,"<%s> unlink failed\n",name);
+ if (!Force)
+ WRONG
+ }
+ continue;
+ }
+ if(!strcmp(sp->Key,"DR")) {
+ /*
+ * We cannot use rmdir() because we do not get the directories
+ * in '-depth' order (cvs-cur.0018.gz for examples)
+ */
+ if (KeepIt) {
+ if (Verbose > 1) {
+ printf("<%s> not removed\n", name);
+ }
+ } else {
+ sprintf(buf,"rm -rf %s",name);
+ system(buf);
+ }
+ continue;
+ }
+ WRONG
+ }
+
+ Delete(md5);
+ Delete(uid);
+ Delete(gid);
+ Delete(mode);
+ Delete(md5before);
+ Delete(trash);
+ Delete(name);
+
+ q = MD5End (&ctx,md5_1);
+ GETFIELD(p,'\n');
+ if(strcmp(q,p)) WRONG
+ if (-1 != getc(fd)) WRONG
+ return 0;
+}
diff --git a/usr.sbin/ctm/ctm/ctm_passb.c b/usr.sbin/ctm/ctm/ctm_passb.c
new file mode 100644
index 0000000..ee3a69c5f
--- /dev/null
+++ b/usr.sbin/ctm/ctm/ctm_passb.c
@@ -0,0 +1,142 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <koshy@india.hp.com> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Joseph Koshy
+ * ----------------------------------------------------------------------------
+ *
+ * $FreeBSD$
+ *
+ */
+
+#include "ctm.h"
+#define BADREAD 32
+
+/*---------------------------------------------------------------------------*/
+/* PassB -- Backup modified files.
+ */
+
+int
+PassB(FILE *fd)
+{
+ u_char *p,*q;
+ MD5_CTX ctx;
+ int i,j,sep,cnt;
+ u_char *md5=0,*md5before=0,*trash=0,*name=0,*uid=0,*gid=0,*mode=0;
+ struct CTM_Syntax *sp;
+ FILE *b = 0; /* backup command */
+ u_char buf[BUFSIZ];
+ char md5_1[33];
+ int ret = 0;
+ int match = 0;
+ struct CTM_Filter *filter = NULL;
+
+ if(Verbose>3)
+ printf("PassB -- Backing up files which would be changed.\n");
+
+ MD5Init (&ctx);
+ snprintf(buf, sizeof(buf), fmtcheck(TarCmd, TARCMD), BackupFile);
+ b=popen(buf, "w");
+ if(!b) { warn("%s", buf); return Exit_Garbage; }
+
+ GETFIELD(p,' '); if(strcmp("CTM_BEGIN",p)) WRONG
+ GETFIELD(p,' '); if(strcmp(Version,p)) WRONG
+ GETFIELD(p,' '); if(strcmp(Name,p)) WRONG
+ GETFIELD(p,' '); if(strcmp(Nbr,p)) WRONG
+ GETFIELD(p,' '); if(strcmp(TimeStamp,p)) WRONG
+ GETFIELD(p,'\n'); if(strcmp(Prefix,p)) WRONG
+
+ for(;;) {
+ Delete(md5);
+ Delete(uid);
+ Delete(gid);
+ Delete(mode);
+ Delete(md5before);
+ Delete(trash);
+ Delete(name);
+ cnt = -1;
+
+ GETFIELD(p,' ');
+
+ if (p[0] != 'C' || p[1] != 'T' || p[2] != 'M') WRONG
+
+ if(!strcmp(p+3,"_END"))
+ break;
+
+ for(sp=Syntax;sp->Key;sp++)
+ if(!strcmp(p+3,sp->Key))
+ goto found;
+ WRONG
+ found:
+ for(i=0;(j = sp->List[i]);i++) {
+ if (sp->List[i+1] && (sp->List[i+1] & CTM_F_MASK) != CTM_F_Bytes)
+ sep = ' ';
+ else
+ sep = '\n';
+
+ switch (j & CTM_F_MASK) {
+ case CTM_F_Name: GETNAMECOPY(name,sep,j, Verbose); break;
+ case CTM_F_Uid: GETFIELDCOPY(uid,sep); break;
+ case CTM_F_Gid: GETFIELDCOPY(gid,sep); break;
+ case CTM_F_Mode: GETFIELDCOPY(mode,sep); break;
+ case CTM_F_MD5:
+ if(j & CTM_Q_MD5_Before)
+ GETFIELDCOPY(md5before,sep);
+ else
+ GETFIELDCOPY(md5,sep);
+ break;
+ case CTM_F_Count: GETBYTECNT(cnt,sep); break;
+ case CTM_F_Bytes: GETDATA(trash,cnt); break;
+ default: WRONG
+ }
+ }
+ /* XXX This should go away. Disallow trailing '/' */
+ j = strlen(name)-1;
+ if(name[j] == '/') name[j] = '\0';
+
+ if (KeepIt &&
+ (!strcmp(sp->Key,"DR") || !strcmp(sp->Key,"FR")))
+ continue;
+
+ /* match the name against the elements of the filter list. The
+ action associated with the last matched filter determines whether
+ this file should be ignored or backed up. */
+ match = (FilterList ? !(FilterList->Action) : CTM_FILTER_ENABLE);
+ for (filter = FilterList; filter; filter = filter->Next) {
+ if (0 == regexec(&filter->CompiledRegex, name, 0, 0, 0))
+ match = filter->Action;
+ }
+
+ if (CTM_FILTER_DISABLE == match)
+ continue;
+
+ if (!strcmp(sp->Key,"FS") || !strcmp(sp->Key,"FN") ||
+ !strcmp(sp->Key,"AS") || !strcmp(sp->Key,"DR") ||
+ !strcmp(sp->Key,"FR")) {
+ /* send name to the archiver for a backup */
+ cnt = strlen(name);
+ if (cnt != fwrite(name,1,cnt,b) || EOF == fputc('\n',b)) {
+ warn("%s", name);
+ pclose(b);
+ WRONG;
+ }
+ }
+ }
+
+ ret = pclose(b);
+
+ Delete(md5);
+ Delete(uid);
+ Delete(gid);
+ Delete(mode);
+ Delete(md5before);
+ Delete(trash);
+ Delete(name);
+
+ q = MD5End (&ctx,md5_1);
+ GETFIELD(p,'\n'); /* <MD5> */
+ if(strcmp(q,p)) WRONG
+ if (-1 != getc(fd)) WRONG
+ return ret;
+}
diff --git a/usr.sbin/ctm/ctm/ctm_syntax.c b/usr.sbin/ctm/ctm/ctm_syntax.c
new file mode 100644
index 0000000..6638943
--- /dev/null
+++ b/usr.sbin/ctm/ctm/ctm_syntax.c
@@ -0,0 +1,67 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ *
+ * $FreeBSD$
+ *
+ */
+
+#include "ctm.h"
+
+/* The fields... */
+#define Name CTM_F_Name
+#define Uid CTM_F_Uid
+#define Gid CTM_F_Gid
+#define Mode CTM_F_Mode
+#define MD5 CTM_F_MD5
+#define Count CTM_F_Count
+#define Bytes CTM_F_Bytes
+
+/* The qualifiers... */
+#define File CTM_Q_Name_File
+#define Dir CTM_Q_Name_Dir
+#define New CTM_Q_Name_New
+#define Subst CTM_Q_Name_Subst
+#define After CTM_Q_MD5_After
+#define Before CTM_Q_MD5_Before
+#define Chunk CTM_Q_MD5_Chunk
+#define Force CTM_Q_MD5_Force
+
+static int ctmFM[] = /* File Make */
+ { Name|File|New|Subst, Uid, Gid, Mode,
+ MD5|After|Chunk, Count, Bytes,0 };
+
+static int ctmFS[] = /* File Substitute */
+ { Name|File|Subst, Uid, Gid, Mode,
+ MD5|Before|Force, MD5|After|Chunk, Count, Bytes,0 };
+
+static int ctmFE[] = /* File Edit */
+ { Name|File|Subst, Uid, Gid, Mode,
+ MD5|Before, MD5|After, Count, Bytes,0 };
+
+static int ctmFR[] = /* File Remove */
+ { Name|File|Subst, MD5|Before, 0 };
+
+static int ctmAS[] = /* Attribute Substitute */
+ { Name|Subst, Uid, Gid, Mode, 0 };
+
+static int ctmDM[] = /* Directory Make */
+ { Name|Dir|New , Uid, Gid, Mode, 0 };
+
+static int ctmDR[] = /* Directory Remove */
+ { Name|Dir, 0 };
+
+struct CTM_Syntax Syntax[] = {
+ { "FM", ctmFM },
+ { "FS", ctmFS },
+ { "FE", ctmFE },
+ { "FN", ctmFE },
+ { "FR", ctmFR },
+ { "AS", ctmAS },
+ { "DM", ctmDM },
+ { "DR", ctmDR },
+ { 0, 0} };
diff --git a/usr.sbin/ctm/ctm_dequeue/Makefile b/usr.sbin/ctm/ctm_dequeue/Makefile
new file mode 100644
index 0000000..f12ecea
--- /dev/null
+++ b/usr.sbin/ctm/ctm_dequeue/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../ctm_rmail
+
+PROG= ctm_dequeue
+NOMAN= #true
+SRCS= ctm_dequeue.c error.c
+
+CFLAGS+= -I${.CURDIR}/../ctm_rmail
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ctm/ctm_dequeue/ctm_dequeue.c b/usr.sbin/ctm/ctm_dequeue/ctm_dequeue.c
new file mode 100644
index 0000000..b5a1411
--- /dev/null
+++ b/usr.sbin/ctm/ctm_dequeue/ctm_dequeue.c
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 1996, Gary J. Palmer
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * ctm_dequeue: Dequeue queued delta pieces and mail them.
+ *
+ * The pieces have already been packaged up as mail messages by ctm_smail,
+ * and will be simply passed to sendmail in alphabetical order.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <fts.h>
+#include <limits.h>
+#include <errno.h>
+#include <paths.h>
+#include "error.h"
+#include "options.h"
+
+#define DEFAULT_NUM 1 /* Default number of pieces mailed per run. */
+
+int fts_sort(const FTSENT **, const 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", (char *)NULL);
+ err("*exec: %s", _PATH_SENDMAIL);
+ _exit(1);
+
+ default: /* Parent */
+ while ((pid = wait(&status)) != child)
+ {
+ if (pid == -1 && errno != EINTR)
+ {
+ err("*wait");
+ return(1);
+ }
+ }
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
+ {
+ err("sendmail failed");
+ return(1);
+ }
+ }
+
+ return(0);
+}
diff --git a/usr.sbin/ctm/ctm_rmail/Makefile b/usr.sbin/ctm/ctm_rmail/Makefile
new file mode 100644
index 0000000..9c7453e
--- /dev/null
+++ b/usr.sbin/ctm/ctm_rmail/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= ctm_rmail
+SRCS= ctm_rmail.c error.c
+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..fc29039
--- /dev/null
+++ b/usr.sbin/ctm/ctm_rmail/ctm_rmail.1
@@ -0,0 +1,487 @@
+.\" NOTICE: This is free documentation. I hope you get some use from these
+.\" words. In return you should think about all the nice people who sweat
+.\" blood to document their free software. Maybe you should write some
+.\" documentation and give it away. Maybe with a free program attached!
+.\"
+.\" Author: Stephen McKay
+.\"
+.\" $FreeBSD$
+.\"
+.Dd January 24, 1995
+.Dt CTM_MAIL 1
+.Os
+.Sh NAME
+.Nm ctm_smail ,
+.Nm ctm_dequeue ,
+.Nm ctm_rmail
+.Nd send and receive
+.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.
+The
+.Nm ctm_smail
+utility is given a compressed
+.Xr ctm
+delta, and a mailing list to send it to. It splits the delta into manageable
+pieces, encodes them as mail messages and sends them to the mailing list
+(optionally queued to spread the mail load).
+Each recipient uses
+.Nm ctm_rmail
+(either manually or automatically) to decode and reassemble the delta, and
+optionally call
+.Xr ctm
+to apply it to the source tree.
+At the moment,
+several source trees are distributed, and by several sites. These include
+the
+.Fx Ns -current
+source and CVS trees, distributed by
+.Li freefall.FreeBSD.org .
+.Pp
+Command line arguments for
+.Nm ctm_smail :
+.Bl -tag -width indent
+.It Fl l Ar log
+Instead of appearing on
+.Em stderr ,
+error diagnostics and informational messages (other than command line errors)
+are time stamped and written to the file
+.Em log .
+.It Fl m Ar maxmsgsize
+Limit the maximum size mail message that
+.Nm ctm_smail
+is allowed to send. It is approximate since mail headers and other niceties
+are not counted in this limit. If not specified, it will default to 64000
+bytes, leaving room for 1535 bytes of headers before the rumoured 64k mail
+limit.
+.It Fl c Ar maxctmsize
+Limit the maximum size delta that will be sent. Deltas bigger that this
+limit will cause an apology mail message to be sent to the mailing list.
+This is to prevent massive changes overwhelming users' mail boxes. Note that
+this is the size before encoding. Encoding causes a 4/3 size increase before
+mail headers are added. If not specified, there is no limit.
+.It Fl q Ar queue-dir
+Instead of mailing the delta pieces now, store them in the given directory
+to be mailed later using
+.Nm ctm_dequeue .
+This feature allows the mailing of large deltas to be spread out over
+hours or even days to limit the impact on recipients with limited network
+bandwidth or small mail spool areas.
+.El
+.Pp
+.Ar ctm-delta
+is the delta to be sent, and
+.Ar mail-alias
+is the mailing list to send the delta to.
+The mail messages are sent using
+.Xr sendmail 8 .
+.Pp
+Command line arguments for
+.Nm ctm_dequeue :
+.Bl -tag -width indent
+.It Fl l Ar log
+Instead of appearing on
+.Em stderr ,
+error diagnostics and informational messages (other than command line errors)
+are time stamped and written to the file
+.Em log .
+.It Fl n Ar numchunks
+Limit the number of mail messages that
+.Nm ctm_dequeue
+will send per run. By default,
+.Nm ctm_dequeue
+will send one mail message per run.
+.El
+.Pp
+.Ar queuedir
+is the directory containing the mail messages stored by
+.Nm ctm_smail .
+Up to
+.Ar numchunks
+mail messages will be sent in each run. The recipient mailing list is already
+encoded in the queued files.
+.Pp
+It is safe to run
+.Nm ctm_dequeue
+while
+.Nm ctm_smail
+is adding entries to the queue, or even to run
+.Nm ctm_smail
+multiple times concurrently, but a separate queue directory should be used
+for each tree being distributed. This is because entries are served in
+alphabetical order, and one tree will be unfairly serviced before any others,
+based on the delta names, not delta creation times.
+.Pp
+Command line arguments for
+.Nm ctm_rmail :
+.Bl -tag -width indent
+.It Fl l Ar log
+Instead of appearing on
+.Em stderr ,
+error diagnostics and informational messages (other than command line errors)
+are time stamped and written to the file
+.Em log .
+.It Fl p Ar piecedir
+Collect pieces of deltas in this directory. Each piece corresponds to a
+single mail message. Pieces are removed when complete deltas are built.
+If this flag is not given, no input files will be read, but completed
+deltas may still be applied with
+.Xr ctm
+if the
+.Fl b
+flag is given.
+.It Fl d Ar deltadir
+Collect completed deltas in this directory. Deltas are built from one or
+more pieces when all pieces are present.
+.It Fl b Ar basedir
+Apply any completed deltas to this source tree. If this flag is not given,
+deltas will be stored, but not applied. The user may then apply the deltas
+manually, or by using
+.Nm ctm_rmail
+without the
+.Fl p
+flag.
+Deltas will not be applied if they do not match the
+.Li .ctm_status
+file in
+.Ar basedir
+(or if
+.Li .ctm_status
+does not exist).
+.It Fl D
+Delete deltas after successful application by
+.Xr ctm .
+It is probably a good idea to avoid this flag (and keep all the deltas) as
+.Xr ctm
+has the ability to recover small groups of files from a full set of deltas.
+.It Fl f
+Fork and execute in the background while applying deltas with
+.Xr ctm .
+This is useful when automatically invoking
+.Nm ctm_rmail
+from
+.Xr sendmail
+because
+.Xr ctm
+can take a very long time to complete, causing other people's mail to
+be delayed, and can in theory cause spurious
+mail retransmission due to the remote
+.Xr sendmail
+timing out, or even termination of
+.Nm ctm_rmail
+by mail filters such as
+.Xr "MH's"
+.Xr slocal .
+Don't worry about zillions of background
+.Xr ctm
+processes loading your machine, since locking is used to prevent more than one
+.Xr ctm
+invocation at a time.
+.It Fl u
+Pass the
+.Fl u
+flag to the
+.Xr ctm
+command when applying the complete deltas, causing it to set the modification
+time of created and modified files to the CTM delta creation time.
+.It Fl v
+Pass the
+.Fl v
+flag to the
+.Xr ctm
+command when applying the complete deltas, causing a more informative
+output. All
+.Xr ctm
+output appears in the
+.Nm ctm_rmail
+log file.
+.El
+.Pp
+The file arguments (or
+.Em stdin ,
+if there are none) are scanned for delta pieces. Multiple delta pieces
+can be read from a single file, so an entire maildrop can be scanned
+and processed with a single command.
+.Pp
+It is safe to invoke
+.Nm ctm_rmail
+multiple times concurrently (with different input files),
+as might happen when
+.Xr sendmail
+is delivering mail asynchronously. This is because locking is used to
+keep things orderly.
+.Sh FILE FORMAT
+Following are the important parts of an actual (very small) delta piece:
+.Bd -literal
+From: owner-src-cur
+To: src-cur
+Subject: ctm-mail src-cur.0003.gz 1/4
+
+CTM_MAIL BEGIN src-cur.0003.gz 1 4
+H4sIAAAAAAACA3VU72/bNhD9bP0VByQoEiyRSZEUSQP9kKTeYCR2gDTdsGFAwB/HRogtG5K8NCj6
+v4+UZSdtUQh6Rz0eee/xaF/dzx8up3/MFlDkBNrGnbttAwyo1pxoRgoiBNX/QJ5d3c9/X8DcPGGo
+lggkPiXngE4W1gUjKPJCYyk5MZRbIqmNW/ASglIFcdwIzTUxaAqhnCPcBqloKEkJVNDMF0Azk+Bo
+dDzzk0Ods/+A5gXv9YyJHjMCtJwQNeESNma7hOmXDRxn
+CTM_MAIL END 61065
+.Ed
+.Pp
+The subject of the message always begins with
+.Dq ctm-mail
+followed by the name of the delta, which piece this is, and how many total
+pieces there are. The data are bracketed by
+.Dq CTM_MAIL BEGIN
+and
+.Dq CTM_MAIL END
+lines, duplicating the information in the subject line, plus a simple checksum.
+.Pp
+If the delta exceeds
+.Ar maxctmsize ,
+then a message like this will be received instead:
+.Bd -literal
+From: owner-src-cur
+To: src-cur
+Subject: ctm-notice src-cur.0999.gz
+
+src-cur.0999.gz is 792843 bytes. The limit is 300000 bytes.
+
+You can retrieve this delta via 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/mail/aliases
+file (assuming the
+.Pa /ctm/tmp
+and
+.Pa /ctm/deltas
+directories and
+.Pa /ctm/log
+file are writable by user
+.Em daemon
+or group
+.Em wheel ) :
+.Bd -literal -offset indent
+receiver-dude: "|ctm_rmail -p /ctm/tmp -d /ctm/deltas -l /ctm/log"
+owner-receiver-dude: real_dude@wherever.you.like
+.Ed
+.Pp
+The second line will catch failures and drop them into your regular mailbox,
+or wherever else you like.
+.Pp
+To apply all the deltas collected, and delete those applied, you could use:
+.Bd -literal -offset indent
+ctm_rmail -D -d /ctm/deltas -b /ctm/src-cur -l /ctm/apply.log
+.Ed
+.Pp
+For maximum flexibility, consider this excerpt from a
+.Xr procmail
+script:
+.Bd -literal -offset indent
+PATH=$HOME/bin:$PATH
+
+:0 w
+* ^Subject: ctm-mail cvs-cur
+| ctm_incoming
+.Ed
+.Pp
+together with the
+shell script
+.Pa ~/bin/ctm_incoming :
+.Bd -literal -offset indent
+#! /bin/sh
+PATH="$HOME/bin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin"
+export PATH
+
+cd $HOME/ctm && ctm_rmail -f -p pieces -d deltas -l log -b /ctm
+.Ed
+.Pp
+which will deposit all
+.Xr ctm
+deltas in
+.Pa ~/ctm/deltas ,
+apply them to the tree in
+.Pa /ctm ,
+and drop any failures into your regular mail box.
+Note the
+.Ev PATH
+manipulation in
+.Pa ctm_incoming
+which allows
+.Nm ctm_rmail
+to execute
+.Xr ctm 1
+on the
+.Pq non- Ns Fx
+machine that this example was taken from.
+.Sh SECURITY
+On its own, CTM is an insecure protocol
+- there is no authentication performed that the
+changes applied to the source code were sent by a
+trusted party, and so care should be taken if the
+CTM deltas are obtained via an unauthenticated
+medium such as regular email.
+It is a relatively simple matter for an attacker
+to forge a CTM delta to replace or precede the
+legitimate one and insert malicious code into your
+source tree.
+If the legitimate delta is somehow prevented from
+arriving, this will go unnoticed until a later
+delta attempts to touch the same file, at which
+point the MD5 checksum will fail.
+.Pp
+To remedy this insecurity, CTM delta pieces generated by
+FreeBSD.org are cryptographically signed in a
+format compatible with the GNU Privacy Guard
+utility, available in /usr/ports/security/gpg, and
+the Pretty Good Privacy v5 utility,
+/usr/ports/security/pgp5.
+The relevant public key can be obtained by
+fingering ctm@FreeBSD.org.
+.Pp
+CTM deltas which are thus signed cannot be
+undetectably altered by an attacker.
+Therefore it is recommended that you make use of
+GPG or PGP5 to verify the signatures if you
+receive your CTM deltas via email.
+.\" This next request is for sections 1, 6, 7 & 8 only
+.Sh ENVIRONMENT
+If deltas are to be applied then
+.Xr ctm 1
+and
+.Xr gunzip 1
+must be in your
+.Ev PATH .
+.Sh FILES
+.Bl -tag -width indent
+.It Pa QUEUEDIR/*
+Pieces of deltas encoded as mail messages waiting to be sent to the
+mailing list.
+.It Pa PIECEDIR/*
+Pieces of deltas waiting for the rest to arrive.
+.It Pa DELTADIR/*
+Completed deltas.
+.It Pa BASEDIR/.ctm_status
+File containing the name and number of the next delta to be applied to this
+source tree.
+.El
+.Sh DIAGNOSTICS
+The
+.Nm ctm_smail ,
+.Nm ctm_dequeue
+and
+.Nm ctm_rmail
+utilities return exit status 0 for success, and 1 for various failures.
+The
+.Nm ctm_rmail
+utility is expected to be called from a mail transfer program, and thus signals
+failure only when the input mail message should be bounced (preferably into
+your regular maildrop, not back to the sender). In short, failure to
+apply a completed delta with
+.Xr ctm
+is not considered an error important enough to bounce the mail, and
+.Nm ctm_rmail
+returns an exit status of 0.
+.Pp
+In normal operation,
+.Nm ctm_smail
+will report messages like:
+.Bd -literal -offset indent
+ctm_smail: src-cur.0250.gz 1/2 sent to src-guys
+.Ed
+.Pp
+or, if queueing,
+.Bd -literal -offset indent
+ctm_smail: src-cur.0250.gz 1/2 queued for src-guys
+.Ed
+.Pp
+The
+.Nm ctm_dequeue
+utility will report messages like:
+.Bd -literal -offset indent
+ctm_dequeue: src-cur.0250.gz 1/2 sent
+.Ed
+.Pp
+The
+.Nm ctm_rmail
+utility will report messages like:
+.Bd -literal -offset indent
+ctm_rmail: src-cur.0250.gz 1/2 stored
+ctm_rmail: src-cur.0250.gz 2/2 stored
+ctm_rmail: src-cur.0250.gz complete
+.Ed
+.Pp
+If any of the input files do not contain a valid delta piece,
+.Nm ctm_rmail
+will report:
+.Bd -literal -offset indent
+ctm_rmail: message contains no delta
+.Ed
+.Pp
+and return an exit status of 1. You can use this to redirect wayward messages
+back into your real mailbox if your mail filter goes wonky.
+.Pp
+These messages go to
+.Em stderr
+or to the log file. Messages from
+.Xr ctm 1
+turn up here too. Error messages should be self explanatory.
+.Sh SEE ALSO
+.Xr ctm 1 ,
+.Xr ctm 5
+.\" .Sh HISTORY
+.Sh AUTHORS
+.An Stephen McKay Aq mckay@FreeBSD.org
diff --git a/usr.sbin/ctm/ctm_rmail/ctm_rmail.c b/usr.sbin/ctm/ctm_rmail/ctm_rmail.c
new file mode 100644
index 0000000..a46a58aa
--- /dev/null
+++ b/usr.sbin/ctm/ctm_rmail/ctm_rmail.c
@@ -0,0 +1,672 @@
+/*
+ * Accept one (or more) ASCII encoded chunks that together make a compressed
+ * CTM delta. Decode them and reconstruct the deltas. Any completed
+ * deltas may be passed to ctm for unpacking.
+ *
+ * Author: Stephen McKay
+ *
+ * NOTICE: This is free software. I hope you get some use from this program.
+ * In return you should think about all the nice people who give away software.
+ * Maybe you should write some free software too.
+ *
+ * $FreeBSD$
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <limits.h>
+#include "error.h"
+#include "options.h"
+
+#define CTM_STATUS ".ctm_status"
+
+char *piece_dir = NULL; /* Where to store pieces of deltas. */
+char *delta_dir = NULL; /* Where to store completed deltas. */
+char *base_dir = NULL; /* The tree to apply deltas to. */
+int delete_after = 0; /* Delete deltas after ctm applies them. */
+int apply_verbose = 0; /* Run with '-v' */
+int set_time = 0; /* Set the time of the files that is changed. */
+int mask = 0; /* The current umask */
+
+void apply_complete(void);
+int read_piece(char *input_file);
+int combine_if_complete(char *delta, int pce, int npieces);
+int combine(char *delta, int npieces, char *dname, char *pname, char *tname);
+int decode_line(char *line, char *out_buf);
+int lock_file(char *name);
+
+/*
+ * If given a '-p' flag, read encoded delta pieces from stdin or file
+ * arguments, decode them and assemble any completed deltas. If given
+ * a '-b' flag, pass any completed deltas to 'ctm' for application to
+ * the source tree. The '-d' flag is mandatory, but either of '-p' or
+ * '-b' can be omitted. If given the '-l' flag, notes and errors will
+ * be timestamped and written to the given file.
+ *
+ * Exit status is 0 for success or 1 for indigestible input. That is,
+ * 0 means the encode input pieces were decoded and stored, and 1 means
+ * some input was discarded. If a delta fails to apply, this won't be
+ * reflected in the exit status. In this case, the delta is left in
+ * 'deltadir'.
+ */
+int
+main(int argc, char **argv)
+ {
+ char *log_file = NULL;
+ int status = 0;
+ int fork_ctm = 0;
+
+ mask = umask(0);
+ umask(mask);
+
+ err_prog_name(argv[0]);
+
+ OPTIONS("[-Dfuv] [-p piecedir] [-d deltadir] [-b basedir] [-l log] [file ...]")
+ FLAG('D', delete_after)
+ FLAG('f', fork_ctm)
+ FLAG('u', set_time)
+ FLAG('v', apply_verbose)
+ STRING('p', piece_dir)
+ STRING('d', delta_dir)
+ STRING('b', base_dir)
+ STRING('l', log_file)
+ ENDOPTS
+
+ if (delta_dir == NULL)
+ usage();
+
+ if (piece_dir == NULL && (base_dir == NULL || argc > 1))
+ usage();
+
+ if (log_file != NULL)
+ err_set_log(log_file);
+
+ /*
+ * Digest each file in turn, or just stdin if no files were given.
+ */
+ if (argc <= 1)
+ {
+ if (piece_dir != NULL)
+ status = read_piece(NULL);
+ }
+ else
+ {
+ while (*++argv != NULL)
+ status |= read_piece(*argv);
+ }
+
+ /*
+ * Maybe it's time to look for and apply completed deltas with ctm.
+ *
+ * Shall we report back to sendmail immediately, and let a child do
+ * the work? Sendmail will be waiting for us to complete, delaying
+ * other mail, and possibly some intermediate process (like MH slocal)
+ * will terminate us if we take too long!
+ *
+ * If fork() fails, it's unlikely we'll be able to run ctm, so give up.
+ * Also, the child exit status is unimportant.
+ */
+ if (base_dir != NULL)
+ if (!fork_ctm || fork() == 0)
+ apply_complete();
+
+ return status;
+ }
+
+
+/*
+ * Construct the file name of a piece of a delta.
+ */
+#define mk_piece_name(fn,d,p,n) \
+ sprintf((fn), "%s/%s+%03d-%03d", piece_dir, (d), (p), (n))
+
+/*
+ * Construct the file name of an assembled delta.
+ */
+#define mk_delta_name(fn,d) \
+ sprintf((fn), "%s/%s", delta_dir, (d))
+
+/*
+ * If the next required delta is now present, let ctm lunch on it and any
+ * contiguous deltas.
+ */
+void
+apply_complete()
+ {
+ int i, dn;
+ int lfd;
+ FILE *fp, *ctm;
+ struct stat sb;
+ char class[20];
+ char delta[30];
+ char junk[2];
+ char fname[PATH_MAX];
+ char here[PATH_MAX];
+ char buf[PATH_MAX*2];
+
+ /*
+ * Grab a lock on the ctm mutex file so that we can be sure we are
+ * working alone, not fighting another ctm_rmail!
+ */
+ strcpy(fname, delta_dir);
+ strcat(fname, "/.mutex_apply");
+ if ((lfd = lock_file(fname)) < 0)
+ return;
+
+ /*
+ * Find out which delta ctm needs next.
+ */
+ sprintf(fname, "%s/%s", base_dir, CTM_STATUS);
+ if ((fp = fopen(fname, "r")) == NULL)
+ {
+ close(lfd);
+ return;
+ }
+
+ i = fscanf(fp, "%19s %d %c", class, &dn, junk);
+ fclose(fp);
+ if (i != 2)
+ {
+ close(lfd);
+ return;
+ }
+
+ /*
+ * We might need to convert the delta filename to an absolute pathname.
+ */
+ here[0] = '\0';
+ if (delta_dir[0] != '/')
+ {
+ getcwd(here, sizeof(here)-1);
+ i = strlen(here) - 1;
+ if (i >= 0 && here[i] != '/')
+ {
+ here[++i] = '/';
+ here[++i] = '\0';
+ }
+ }
+
+ /*
+ * Keep applying deltas until we run out or something bad happens.
+ */
+ for (;;)
+ {
+ sprintf(delta, "%s.%04d.gz", class, ++dn);
+ mk_delta_name(fname, delta);
+
+ if (stat(fname, &sb) < 0)
+ break;
+
+ sprintf(buf, "(cd %s && ctm %s%s%s%s) 2>&1", base_dir,
+ set_time ? "-u " : "",
+ apply_verbose ? "-v " : "", here, fname);
+ if ((ctm = popen(buf, "r")) == NULL)
+ {
+ err("ctm failed to apply %s", delta);
+ break;
+ }
+
+ while (fgets(buf, sizeof(buf), ctm) != NULL)
+ {
+ i = strlen(buf) - 1;
+ if (i >= 0 && buf[i] == '\n')
+ buf[i] = '\0';
+ err("ctm: %s", buf);
+ }
+
+ if (pclose(ctm) != 0)
+ {
+ err("ctm failed to apply %s", delta);
+ break;
+ }
+
+ if (delete_after)
+ unlink(fname);
+
+ err("%s applied%s", delta, delete_after ? " and deleted" : "");
+ }
+
+ /*
+ * Closing the lock file clears the lock.
+ */
+ close(lfd);
+ }
+
+
+/*
+ * This cheap plastic checksum effectively rotates our checksum-so-far
+ * left one, then adds the character. We only want 16 bits of it, and
+ * don't care what happens to the rest. It ain't much, but it's small.
+ */
+#define add_ck(sum,x) \
+ ((sum) += ((x)&0xff) + (sum) + (((sum)&0x8000) ? 1 : 0))
+
+
+/*
+ * Decode the data between BEGIN and END, and stash it in the staging area.
+ * Multiple pieces can be present in a single file, bracketed by BEGIN/END.
+ * If we have all pieces of a delta, combine them. Returns 0 on success,
+ * and 1 for any sort of failure.
+ */
+int
+read_piece(char *input_file)
+ {
+ int status = 0;
+ FILE *ifp, *ofp = 0;
+ int decoding = 0;
+ int got_one = 0;
+ int line_no = 0;
+ int i, n;
+ int pce, npieces;
+ unsigned claimed_cksum;
+ unsigned short cksum = 0;
+ char out_buf[200];
+ char line[200];
+ char delta[30];
+ char pname[PATH_MAX];
+ char tname[PATH_MAX];
+ char junk[2];
+
+ ifp = stdin;
+ if (input_file != NULL && (ifp = fopen(input_file, "r")) == NULL)
+ {
+ err("cannot open '%s' for reading", input_file);
+ return 1;
+ }
+
+ while (fgets(line, sizeof(line), ifp) != NULL)
+ {
+ line_no++;
+
+ /*
+ * Remove all trailing white space.
+ */
+ i = strlen(line) - 1;
+ while (i > 0 && isspace(line[i]))
+ line[i--] = '\0';
+
+ /*
+ * Look for the beginning of an encoded piece.
+ */
+ if (!decoding)
+ {
+ char *s;
+ int fd = -1;
+
+ if (sscanf(line, "CTM_MAIL BEGIN %29s %d %d %c",
+ delta, &pce, &npieces, junk) != 3)
+ continue;
+
+ while ((s = strchr(delta, '/')) != NULL)
+ *s = '_';
+
+ got_one++;
+ strcpy(tname, piece_dir);
+ strcat(tname, "/p.XXXXXXXXXX");
+ if ((fd = mkstemp(tname)) == -1 ||
+ (ofp = fdopen(fd, "w")) == NULL)
+ {
+ if (fd != -1) {
+ err("cannot open '%s' for writing", tname);
+ close(fd);
+ }
+ else
+ err("*mkstemp: '%s'", tname);
+ status++;
+ continue;
+ }
+
+ cksum = 0xffff;
+ decoding++;
+ continue;
+ }
+
+ /*
+ * We are decoding. Stop if we see the end flag.
+ */
+ if (sscanf(line, "CTM_MAIL END %d %c", &claimed_cksum, junk) == 1)
+ {
+ int e;
+
+ decoding = 0;
+
+ fflush(ofp);
+ e = ferror(ofp);
+ fclose(ofp);
+
+ if (e)
+ err("error writing %s", tname);
+
+ if (cksum != claimed_cksum)
+ err("checksum: read %d, calculated %d", claimed_cksum, cksum);
+
+ if (e || cksum != claimed_cksum)
+ {
+ err("%s %d/%d discarded", delta, pce, npieces);
+ unlink(tname);
+ status++;
+ continue;
+ }
+
+ mk_piece_name(pname, delta, pce, npieces);
+ if (rename(tname, pname) < 0)
+ {
+ err("*rename: '%s' to '%s'", tname, pname);
+ err("%s %d/%d lost!", delta, pce, npieces);
+ unlink(tname);
+ status++;
+ continue;
+ }
+
+ err("%s %d/%d stored", delta, pce, npieces);
+
+ if (!combine_if_complete(delta, pce, npieces))
+ status++;
+ continue;
+ }
+
+ /*
+ * Must be a line of encoded data. Decode it, sum it, and save it.
+ */
+ n = decode_line(line, out_buf);
+ if (n <= 0)
+ {
+ err("line %d: illegal character: '%c'", line_no, line[-n]);
+ err("%s %d/%d discarded", delta, pce, npieces);
+
+ fclose(ofp);
+ unlink(tname);
+
+ status++;
+ decoding = 0;
+ continue;
+ }
+
+ for (i = 0; i < n; i++)
+ add_ck(cksum, out_buf[i]);
+
+ fwrite(out_buf, sizeof(char), n, ofp);
+ }
+
+ if (decoding)
+ {
+ err("truncated file");
+ err("%s %d/%d discarded", delta, pce, npieces);
+
+ fclose(ofp);
+ unlink(tname);
+
+ status++;
+ }
+
+ if (ferror(ifp))
+ {
+ err("error reading %s", input_file == NULL ? "stdin" : input_file);
+ status++;
+ }
+
+ if (input_file != NULL)
+ fclose(ifp);
+
+ if (!got_one)
+ {
+ err("message contains no delta");
+ status++;
+ }
+
+ return (status != 0);
+ }
+
+
+/*
+ * Put the pieces together to form a delta, if they are all present.
+ * Returns 1 on success (even if we didn't do anything), and 0 on failure.
+ */
+int
+combine_if_complete(char *delta, int pce, int npieces)
+ {
+ int i, e;
+ int lfd;
+ struct stat sb;
+ char pname[PATH_MAX];
+ char dname[PATH_MAX];
+ char tname[PATH_MAX];
+
+ /*
+ * We can probably just rename() it into place if it is a small delta.
+ */
+ if (npieces == 1)
+ {
+ mk_delta_name(dname, delta);
+ mk_piece_name(pname, delta, 1, 1);
+ if (rename(pname, dname) == 0)
+ {
+ chmod(dname, 0666 & ~mask);
+ err("%s complete", delta);
+ return 1;
+ }
+ }
+
+ /*
+ * Grab a lock on the reassembly mutex file so that we can be sure we are
+ * working alone, not fighting another ctm_rmail!
+ */
+ strcpy(tname, delta_dir);
+ strcat(tname, "/.mutex_build");
+ if ((lfd = lock_file(tname)) < 0)
+ return 0;
+
+ /*
+ * Are all of the pieces present? Of course the current one is,
+ * unless all pieces are missing because another ctm_rmail has
+ * processed them already.
+ */
+ for (i = 1; i <= npieces; i++)
+ {
+ if (i == pce)
+ continue;
+ mk_piece_name(pname, delta, i, npieces);
+ if (stat(pname, &sb) < 0)
+ {
+ close(lfd);
+ return 1;
+ }
+ }
+
+ /*
+ * Stick them together. Let combine() use our file name buffers, since
+ * we're such good buddies. :-)
+ */
+ e = combine(delta, npieces, dname, pname, tname);
+ close(lfd);
+ return e;
+ }
+
+
+/*
+ * Put the pieces together to form a delta.
+ * Returns 1 on success, and 0 on failure.
+ * Note: dname, pname, and tname are room for some file names that just
+ * happened to by lying around in the calling routine. Waste not, want not!
+ */
+int
+combine(char *delta, int npieces, char *dname, char *pname, char *tname)
+ {
+ FILE *dfp, *pfp;
+ int i, n, e;
+ char buf[BUFSIZ];
+ int fd = -1;
+
+ strcpy(tname, delta_dir);
+ strcat(tname, "/d.XXXXXXXXXX");
+ if ((fd = mkstemp(tname)) == -1 ||
+ (dfp = fdopen(fd, "w")) == NULL)
+ {
+ if (fd != -1) {
+ close(fd);
+ err("cannot open '%s' for writing", tname);
+ }
+ else
+ err("*mkstemp: '%s'", tname);
+ return 0;
+ }
+
+ /*
+ * Reconstruct the delta by reading each piece in order.
+ */
+ for (i = 1; i <= npieces; i++)
+ {
+ mk_piece_name(pname, delta, i, npieces);
+ if ((pfp = fopen(pname, "r")) == NULL)
+ {
+ err("cannot open '%s' for reading", pname);
+ fclose(dfp);
+ unlink(tname);
+ return 0;
+ }
+ while ((n = fread(buf, sizeof(char), sizeof(buf), pfp)) != 0)
+ fwrite(buf, sizeof(char), n, dfp);
+ e = ferror(pfp);
+ fclose(pfp);
+ if (e)
+ {
+ err("error reading '%s'", pname);
+ fclose(dfp);
+ unlink(tname);
+ return 0;
+ }
+ }
+ fflush(dfp);
+ e = ferror(dfp);
+ fclose(dfp);
+ if (e)
+ {
+ err("error writing '%s'", tname);
+ unlink(tname);
+ return 0;
+ }
+
+ mk_delta_name(dname, delta);
+ if (rename(tname, dname) < 0)
+ {
+ err("*rename: '%s' to '%s'", tname, dname);
+ unlink(tname);
+ return 0;
+ }
+ chmod(dname, 0666 & ~mask);
+
+ /*
+ * Throw the pieces away.
+ */
+ for (i = 1; i <= npieces; i++)
+ {
+ mk_piece_name(pname, delta, i, npieces);
+ if (unlink(pname) < 0)
+ err("*unlink: '%s'", pname);
+ }
+
+ err("%s complete", delta);
+ return 1;
+ }
+
+
+/*
+ * MIME BASE64 decode table.
+ */
+static unsigned char from_b64[0x80] =
+ {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
+ 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
+ 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+ 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
+ 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
+ 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
+ 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
+ 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
+ };
+
+
+/*
+ * Decode a line of ASCII into binary. Returns the number of bytes in
+ * the output buffer, or < 0 on indigestable input. Error output is
+ * the negative of the index of the inedible character.
+ */
+int
+decode_line(char *line, char *out_buf)
+ {
+ unsigned char *ip = (unsigned char *)line;
+ unsigned char *op = (unsigned char *)out_buf;
+ unsigned long bits;
+ unsigned x;
+
+ for (;;)
+ {
+ if (*ip >= 0x80 || (x = from_b64[*ip]) >= 0x40)
+ break;
+ bits = x << 18;
+ ip++;
+ if (*ip < 0x80 && (x = from_b64[*ip]) < 0x40)
+ {
+ bits |= x << 12;
+ *op++ = bits >> 16;
+ ip++;
+ if (*ip < 0x80 && (x = from_b64[*ip]) < 0x40)
+ {
+ bits |= x << 6;
+ *op++ = bits >> 8;
+ ip++;
+ if (*ip < 0x80 && (x = from_b64[*ip]) < 0x40)
+ {
+ bits |= x;
+ *op++ = bits;
+ ip++;
+ }
+ }
+ }
+ }
+
+ if (*ip == '\0' || *ip == '\n')
+ return op - (unsigned char *)out_buf;
+ else
+ return -(ip - (unsigned char *)line);
+ }
+
+
+/*
+ * Create and lock the given file.
+ *
+ * Clearing the lock is as simple as closing the file descriptor we return.
+ */
+int
+lock_file(char *name)
+ {
+ int lfd;
+
+ if ((lfd = open(name, O_WRONLY|O_CREAT, 0600)) < 0)
+ {
+ err("*open: '%s'", name);
+ return -1;
+ }
+ if (flock(lfd, LOCK_EX) < 0)
+ {
+ close(lfd);
+ err("*flock: '%s'", name);
+ return -1;
+ }
+ return lfd;
+ }
diff --git a/usr.sbin/ctm/ctm_rmail/error.c b/usr.sbin/ctm/ctm_rmail/error.c
new file mode 100644
index 0000000..56d3dc6
--- /dev/null
+++ b/usr.sbin/ctm/ctm_rmail/error.c
@@ -0,0 +1,102 @@
+/*
+ * Routines for logging error messages or other informative messages.
+ *
+ * Log messages can easily contain the program name, a time stamp, system
+ * error messages, and arbitrary printf-style strings, and can be directed
+ * to stderr or a log file.
+ *
+ * Author: Stephen McKay
+ *
+ * NOTICE: This is free software. I hope you get some use from this program.
+ * In return you should think about all the nice people who give away software.
+ * Maybe you should write some free software too.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <time.h>
+#include <errno.h>
+#include "error.h"
+
+static FILE *error_fp = NULL;
+static char *prog = NULL;
+
+
+/*
+ * Log errors to the given file.
+ */
+void
+err_set_log(char *log_file)
+ {
+ FILE *fp;
+
+ if ((fp = fopen(log_file, "a")) == NULL)
+ err("cannot log to '%s'", log_file);
+ else
+ error_fp = fp;
+ }
+
+
+/*
+ * Set the error prefix if not logging to a file.
+ */
+void
+err_prog_name(char *name)
+ {
+ if ((prog = strrchr(name, '/')) == NULL)
+ prog = name;
+ else
+ prog++;
+ }
+
+
+/*
+ * Log an error.
+ *
+ * A leading '*' in the message format means we want the system errno
+ * decoded and appended.
+ */
+void
+err(const char *fmt, ...)
+ {
+ va_list ap;
+ time_t now;
+ struct tm *tm;
+ FILE *fp;
+ int x = errno;
+ int want_errno;
+
+ if ((fp = error_fp) == NULL)
+ {
+ fp = stderr;
+ if (prog != NULL)
+ fprintf(fp, "%s: ", prog);
+ }
+ else
+ {
+ time(&now);
+ tm = localtime(&now);
+ fprintf(fp, "%04d-%02d-%02d %02d:%02d ", tm->tm_year+1900,
+ tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min);
+ }
+
+ want_errno = 0;
+ if (*fmt == '*')
+ want_errno++, fmt++;
+
+ va_start(ap, fmt);
+ vfprintf(fp, fmt, ap);
+ va_end(ap);
+
+ if (want_errno)
+ fprintf(fp, ": %s", strerror(x));
+
+ fprintf(fp, "\n");
+ fflush(fp);
+ }
diff --git a/usr.sbin/ctm/ctm_rmail/error.h b/usr.sbin/ctm/ctm_rmail/error.h
new file mode 100644
index 0000000..c631b67
--- /dev/null
+++ b/usr.sbin/ctm/ctm_rmail/error.h
@@ -0,0 +1,5 @@
+/* $FreeBSD$ */
+
+extern void err_set_log(char *log_file);
+extern void err_prog_name(char *name);
+extern void err(const char *fmt, ...) __printflike(1, 2);
diff --git a/usr.sbin/ctm/ctm_rmail/options.h b/usr.sbin/ctm/ctm_rmail/options.h
new file mode 100644
index 0000000..86c1247
--- /dev/null
+++ b/usr.sbin/ctm/ctm_rmail/options.h
@@ -0,0 +1,139 @@
+/*
+ * Macros for processing command arguments.
+ *
+ * Conforms closely to the command option requirements of intro(1) in System V
+ * and intro(C) in Xenix.
+ *
+ * A command consists of: cmdname [ options ] [ cmdarguments ]
+ *
+ * Options consist of a leading dash '-' and a flag letter. An argument may
+ * follow optionally preceded by white space.
+ * Options without arguments may be grouped behind a single dash.
+ * A dash on its own is interpreted as the end of the options and is retained
+ * as a command argument.
+ * A double dash '--' is interpreted as the end of the options and is discarded.
+ *
+ * For example:
+ * zap -xz -f flame -q34 -- -x
+ *
+ * where zap.c contains the following in main():
+ *
+ * OPTIONS("[-xz] [-q queue-id] [-f dump-file] user")
+ * FLAG('x', xecute)
+ * FLAG('z', zot)
+ * STRING('f', file)
+ * fp = fopen(file, "w");
+ * NUMBER('q', queue)
+ * ENDOPTS
+ *
+ * Results in:
+ * xecute = 1
+ * zot = 1
+ * file = "flame"
+ * fp = fopen("flame", "w")
+ * queue = 34
+ * argc = 2
+ * argv[0] = "zap"
+ * argv[1] = "-x"
+ *
+ * Should the user enter unknown flags or leave out required arguments,
+ * the message:
+ *
+ * Usage: zap [-xz] [-q queue-id] [-f dump-file] user
+ *
+ * will be printed. This message can be printed by calling pusage(), or
+ * usage(). usage() will also cause program termination with exit code 1.
+ *
+ * Author: Stephen McKay, February 1991
+ *
+ * Based on recollection of the original options.h produced at the University
+ * of Queensland by Ross Patterson (and possibly others).
+ *
+ * $FreeBSD$
+ */
+
+static char *O_usage;
+static char *O_name;
+extern long atol();
+
+void
+pusage()
+ {
+ /*
+ * Avoid gratuitously loading stdio.
+ */
+ write(STDERR_FILENO, "usage: ", 7);
+ write(STDERR_FILENO, O_name, strlen(O_name));
+ write(STDERR_FILENO, " ", 1);
+ write(STDERR_FILENO, O_usage, strlen(O_usage));
+ write(STDERR_FILENO, "\n", 1);
+ }
+
+#define usage() (pusage(), exit(1))
+
+#define OPTIONS(usage_msg) \
+ { \
+ char O_cont; \
+ O_usage = (usage_msg); \
+ O_name = argv[0]; \
+ while (*++argv && **argv == '-') \
+ { \
+ if ((*argv)[1] == '\0') \
+ break; \
+ argc--; \
+ if ((*argv)[1] == '-' && (*argv)[2] == '\0') \
+ { \
+ argv++; \
+ break; \
+ } \
+ O_cont = 1; \
+ while (O_cont) \
+ switch (*++*argv) \
+ { \
+ default: \
+ case '-': \
+ usage(); \
+ case '\0': \
+ O_cont = 0;
+
+#define FLAG(x,flag) \
+ break; \
+ case (x): \
+ (flag) = 1;
+
+#define CHAR(x,ch) \
+ break; \
+ case (x): \
+ O_cont = 0; \
+ if (*++*argv == '\0' && (--argc, *++argv == 0)) \
+ usage(); \
+ (ch) = **argv;
+
+#define NUMBER(x,n) \
+ break; \
+ case (x): \
+ O_cont = 0; \
+ if (*++*argv == '\0' && (--argc, *++argv == 0)) \
+ usage(); \
+ (n) = atol(*argv);
+
+#define STRING(x,str) \
+ break; \
+ case (x): \
+ O_cont = 0; \
+ if (*++*argv == '\0' && (--argc, *++argv == 0)) \
+ usage(); \
+ (str) = *argv;
+
+#define SUFFIX(x,str) \
+ break; \
+ case (x): \
+ (str) = ++*argv; \
+ O_cont = 0;
+
+#define ENDOPTS \
+ break; \
+ } \
+ } \
+ *--argv = O_name; \
+ }
diff --git a/usr.sbin/ctm/ctm_smail/Makefile b/usr.sbin/ctm/ctm_smail/Makefile
new file mode 100644
index 0000000..c2d0789
--- /dev/null
+++ b/usr.sbin/ctm/ctm_smail/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../ctm_rmail
+
+PROG= ctm_smail
+NOMAN= #true
+SRCS= ctm_smail.c error.c
+
+CFLAGS+= -I${.CURDIR}/../ctm_rmail
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ctm/ctm_smail/ctm_smail.c b/usr.sbin/ctm/ctm_smail/ctm_smail.c
new file mode 100644
index 0000000..f86d5d0
--- /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.
+ *
+ * $FreeBSD$
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <paths.h>
+#include <limits.h>
+#include "error.h"
+#include "options.h"
+
+#define DEF_MAX_MSG 64000 /* Default maximum mail msg minus headers. */
+
+#define LINE_LENGTH 76 /* Chars per encoded line. Divisible by 4. */
+
+int chop_and_send_or_queue(FILE *dfp, char *delta, off_t ctm_size,
+ long max_msg_size, char *mail_alias, char *queue_dir);
+int chop_and_send(FILE *dfp, char *delta, long msg_size, int npieces,
+ char *mail_alias);
+int chop_and_queue(FILE *dfp, char *delta, long msg_size, int npieces,
+ char *mail_alias, char *queue_dir);
+void clean_up_queue(char *queue_dir);
+int encode_body(FILE *sm_fp, FILE *delta_fp, long msg_size, unsigned *sum);
+void write_header(FILE *sfp, char *mail_alias, char *delta, int pce,
+ int npieces);
+void write_trailer(FILE *sfp, unsigned sum);
+int apologise(char *delta, off_t ctm_size, long max_ctm_size,
+ char *mail_alias);
+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..1ec255d
--- /dev/null
+++ b/usr.sbin/ctm/mkCTM/Makefile
@@ -0,0 +1,26 @@
+# $FreeBSD$
+
+PROG= mkctm
+NOMAN= 1
+SRCS= mkctm.c
+
+LDADD= -lmd
+
+test: mkctm
+ rm -f tst.out*
+ time ./mkctm -v -v /3c/210src /a/r1/usr/src \
+ 2>a | md5 -p > /a/tst.out
+ ls -l /a/tst.out
+ gzip -9 -v /a/tst.out
+ ls -l /a/tst.out.gz
+ # cd /usr/src/release && ctm -c -v -v ${.CURDIR}/tst.out
+
+test1: mkctm
+ rm -f tst.out*
+ time ./mkctm -v -v /3c/210src /home/ncvs/src \
+ 2> b | md5 -p > /a/tst2.out
+ ls -l /a/tst2.out
+ gzip -9 -v /a/tst2.out
+ ls -l /a/tst2.out.gz
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ctm/mkCTM/ctm_conf.cvs-cur b/usr.sbin/ctm/mkCTM/ctm_conf.cvs-cur
new file mode 100644
index 0000000..fbb5bf2
--- /dev/null
+++ b/usr.sbin/ctm/mkCTM/ctm_conf.cvs-cur
@@ -0,0 +1,9 @@
+#!/usr/local/bin/tclsh
+
+set CTMname cvs-cur
+set CTMref /home/ncvs
+set CTMignore {^src/secure|^src/eBones|^src/kerberosIV|^CVSROOT/val-tags$|CVSROOT/\\.#}
+set CTMbogus {\\.core$|/#cvs|/\\.#}
+set CTMmail ctm-cvs-cur-fast@freebsd.org
+set CTMqueuemail ctm-cvs-cur@freebsd.org
+set CTMqueue /home/ctm/queue/ctm-cvs-cur
diff --git a/usr.sbin/ctm/mkCTM/ctm_conf.gnats b/usr.sbin/ctm/mkCTM/ctm_conf.gnats
new file mode 100644
index 0000000..c2223f0
--- /dev/null
+++ b/usr.sbin/ctm/mkCTM/ctm_conf.gnats
@@ -0,0 +1,8 @@
+#!/usr/local/bin/tclsh
+
+#set CTMfirst 1
+set CTMname gnats
+set CTMref /home/gnats
+set CTMdest $CTMSW/../CTM-pub/$CTMname
+set CTMignore {\\.lock$}
+set CTMmail ctm-gnats@freebsd.org
diff --git a/usr.sbin/ctm/mkCTM/ctm_conf.ports-cur b/usr.sbin/ctm/mkCTM/ctm_conf.ports-cur
new file mode 100644
index 0000000..b9d3d13
--- /dev/null
+++ b/usr.sbin/ctm/mkCTM/ctm_conf.ports-cur
@@ -0,0 +1,7 @@
+#!/usr/local/bin/tclsh
+
+set CTMname ports-cur
+set CTMref /usr/ports
+set CTMignore {/CVS$|/CVS/|^distfiles}
+set CTMbogus {\\.core$|/#cvs|/\\.#}
+set CTMmail ctm-ports-cur@freebsd.org
diff --git a/usr.sbin/ctm/mkCTM/ctm_conf.smp-cur b/usr.sbin/ctm/mkCTM/ctm_conf.smp-cur
new file mode 100644
index 0000000..5c3e1d1
--- /dev/null
+++ b/usr.sbin/ctm/mkCTM/ctm_conf.smp-cur
@@ -0,0 +1,7 @@
+#!/usr/local/bin/tclsh
+
+set CTMname smp-cur
+set CTMref /home/smp
+set CTMignore {^CVSROOT/history.*$|^CVSROOT/val-tags$|^CVSROOT/\\.#}
+set CTMbogus {\\.core$|/#cvs|/\\.#}
+set CTMmail smp-cvs-cur@freebsd.org
diff --git a/usr.sbin/ctm/mkCTM/ctm_conf.src-cur b/usr.sbin/ctm/mkCTM/ctm_conf.src-cur
new file mode 100644
index 0000000..8589d04
--- /dev/null
+++ b/usr.sbin/ctm/mkCTM/ctm_conf.src-cur
@@ -0,0 +1,9 @@
+#!/usr/local/bin/tclsh
+
+set CTMname src-cur
+set CTMref /c/src
+set CTMignore {/CVS$|/CVS/|^/secure|^/eBones}
+set CTMbogus {\\.core$|/#cvs|/\\.#}
+set CTMmail ctm-src-cur-fast@freebsd.org
+set CTMqueue /home/ctm/queue/ctm-src-cur
+set CTMqueuemail ctm-src-cur@freebsd.org
diff --git a/usr.sbin/ctm/mkCTM/ctm_conf.src-special b/usr.sbin/ctm/mkCTM/ctm_conf.src-special
new file mode 100644
index 0000000..2a8ca70
--- /dev/null
+++ b/usr.sbin/ctm/mkCTM/ctm_conf.src-special
@@ -0,0 +1,9 @@
+#!/usr/local/bin/tclsh
+
+set CTMname src-cur
+set CTMref $CTMSW/../$CTMname
+set CTMcopy /c/phk/20R/usr/src
+set CTMdont {\.core$|/CVS$|/CVS/|^/secure|^/eBones|/#cvs|/\.#}
+set CTMtest 1
+set CTMspecial 1
+set CTMsuff R20
diff --git a/usr.sbin/ctm/mkCTM/dequeue b/usr.sbin/ctm/mkCTM/dequeue
new file mode 100755
index 0000000..697ec0b
--- /dev/null
+++ b/usr.sbin/ctm/mkCTM/dequeue
@@ -0,0 +1,6 @@
+#! /bin/sh
+# $FreeBSD$
+
+L=/home/ctm/log.dequeue
+/usr/sbin/ctm_dequeue -n 1 -l $L /home/ctm/queue/ctm-cvs-cur
+/usr/sbin/ctm_dequeue -n 1 -l $L /home/ctm/queue/ctm-src-cur
diff --git a/usr.sbin/ctm/mkCTM/mkCTM b/usr.sbin/ctm/mkCTM/mkCTM
new file mode 100644
index 0000000..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..336e824
--- /dev/null
+++ b/usr.sbin/ctm/mkCTM/mkctm.c
@@ -0,0 +1,596 @@
+/* $FreeBSD$ */
+
+/* Still missing:
+ *
+ * mkctm
+ * -B regex Bogus
+ * -I regex Ignore
+ * -D int Damage
+ * -q decrease verbosity
+ * -v increase verbosity
+ * -l file logfile
+ * name cvs-cur
+ * prefix src/secure
+ * dir1 "Soll"
+ * dir2 "Ist"
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+#include <dirent.h>
+#include <regex.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <md5.h>
+#include <err.h>
+#include <paths.h>
+#include <signal.h>
+
+#define DEFAULT_IGNORE "/CVS$|/\\.#|00_TRANS\\.TBL$"
+#define DEFAULT_BOGUS "\\.core$|\\.orig$|\\.rej$|\\.o$"
+regex_t reg_ignore, reg_bogus;
+int flag_ignore, flag_bogus;
+
+int verbose;
+int damage, damage_limit;
+int change;
+
+FILE *logf;
+
+u_long s1_ignored, s2_ignored;
+u_long s1_bogus, s2_bogus;
+u_long s1_wrong, s2_wrong;
+u_long s_new_dirs, s_new_files, s_new_bytes;
+u_long s_del_dirs, s_del_files, s_del_bytes;
+u_long s_files_chg, s_bytes_add, s_bytes_del;
+u_long s_same_dirs, s_same_files, s_same_bytes;
+u_long s_edit_files, s_edit_bytes, s_edit_saves;
+u_long s_sub_files, s_sub_bytes;
+
+void
+Usage(void)
+{
+ fprintf(stderr,
+ "usage: mkctm [-options] name number timestamp prefix dir1 dir2\n");
+ fprintf(stderr, "options:\n");
+ fprintf(stderr, "\t\t-B bogus_regexp\n");
+ fprintf(stderr, "\t\t-D damage_limit\n");
+ fprintf(stderr, "\t\t-I ignore_regexp\n");
+ fprintf(stderr, "\t\t-q\n");
+ fprintf(stderr, "\t\t-v\n");
+}
+
+void
+print_stat(FILE *fd, char *pre)
+{
+ fprintf(fd, "%sNames:\n", pre);
+ fprintf(fd, "%s ignore: %5lu ref %5lu target\n",
+ pre, s1_ignored, s2_ignored);
+ fprintf(fd, "%s bogus: %5lu ref %5lu target\n",
+ pre, s1_bogus, s2_bogus);
+ fprintf(fd, "%s wrong: %5lu ref %5lu target\n",
+ pre, s1_wrong, s2_wrong);
+ fprintf(fd, "%sDelta:\n", pre);
+ fprintf(fd, "%s new: %5lu dirs %5lu files %9lu plus\n",
+ pre, s_new_dirs, s_new_files, s_new_bytes);
+ fprintf(fd, "%s del: %5lu dirs %5lu files %9lu minus\n",
+ pre, s_del_dirs, s_del_files, s_del_bytes);
+ fprintf(fd, "%s chg: %5lu files %9lu plus %9lu minus\n",
+ pre, s_files_chg, s_bytes_add, s_bytes_del);
+ fprintf(fd, "%s same: %5lu dirs %5lu files %9lu bytes\n",
+ pre, s_same_dirs, s_same_files, s_same_bytes);
+ fprintf(fd, "%sMethod:\n", pre);
+ fprintf(fd, "%s edit: %5lu files %9lu bytes %9lu saved\n",
+ pre, s_edit_files, s_edit_bytes, s_edit_saves);
+ fprintf(fd, "%s sub: %5lu files %9lu bytes\n",
+ pre, s_sub_files, s_sub_bytes);
+
+}
+
+void
+stat_info(int foo)
+{
+ signal(SIGINFO, stat_info);
+ print_stat(stderr, "INFO: ");
+}
+
+void DoDir(const char *dir1, const char *dir2, const char *name);
+
+static struct stat st;
+static __inline struct stat *
+StatFile(char *name)
+{
+ if (lstat(name, &st) < 0)
+ err(1, "couldn't stat %s", name);
+ return &st;
+}
+
+int
+dirselect(struct dirent *de)
+{
+ if (!strcmp(de->d_name, ".")) return 0;
+ if (!strcmp(de->d_name, "..")) return 0;
+ return 1;
+}
+
+void
+name_stat(const char *pfx, const char *dir, const char *name, struct dirent *de)
+{
+ char *buf = alloca(strlen(dir) + strlen(name) +
+ strlen(de->d_name) + 3);
+ struct stat *st;
+
+ strcpy(buf, dir);
+ strcat(buf, "/"); strcat(buf, name);
+ strcat(buf, "/"); strcat(buf, de->d_name);
+ st = StatFile(buf);
+ printf("%s %s%s %u %u %o",
+ pfx, name, de->d_name,
+ st->st_uid, st->st_gid, st->st_mode & ~S_IFMT);
+ fprintf(logf, "%s %s%s\n", pfx, name, de->d_name);
+ if (verbose > 1) {
+ fprintf(stderr, "%s %s%s\n", pfx, name, de->d_name);
+ }
+}
+
+void
+Equ(const char *dir1, const char *dir2, const char *name, struct dirent *de)
+{
+ if (de->d_type == DT_DIR) {
+ char *p = alloca(strlen(name)+strlen(de->d_name)+2);
+
+ strcpy(p, name); strcat(p, de->d_name); strcat(p, "/");
+ DoDir(dir1, dir2, p);
+ s_same_dirs++;
+ } else {
+ char *buf1 = alloca(strlen(dir1) + strlen(name) +
+ strlen(de->d_name) + 3);
+ char *buf2 = alloca(strlen(dir2) + strlen(name) +
+ strlen(de->d_name) + 3);
+ char *m1, md5_1[33], *m2, md5_2[33];
+ u_char *p1, *p2;
+ int fd1, fd2;
+ struct stat s1, s2;
+
+ strcpy(buf1, dir1);
+ strcat(buf1, "/"); strcat(buf1, name);
+ strcat(buf1, "/"); strcat(buf1, de->d_name);
+ fd1 = open(buf1, O_RDONLY);
+ if(fd1 < 0) { err(3, "%s", buf1); }
+ fstat(fd1, &s1);
+ strcpy(buf2, dir2);
+ strcat(buf2, "/"); strcat(buf2, name);
+ strcat(buf2, "/"); strcat(buf2, de->d_name);
+ fd2 = open(buf2, O_RDONLY);
+ if(fd2 < 0) { err(3, "%s", buf2); }
+ fstat(fd2, &s2);
+#if 0
+ /* XXX if we could just trust the size to change... */
+ if (s1.st_size == s2.st_size) {
+ s_same_files++;
+ s_same_bytes += s1.st_size;
+ close(fd1);
+ close(fd2);
+ goto finish;
+ }
+#endif
+ p1=mmap(0, s1.st_size, PROT_READ, MAP_PRIVATE, fd1, 0);
+ if (p1 == (u_char *)MAP_FAILED) { err(3, "%s", buf1); }
+ close(fd1);
+
+ p2=mmap(0, s2.st_size, PROT_READ, MAP_PRIVATE, fd2, 0);
+ if (p2 == (u_char *)MAP_FAILED) { err(3, "%s", buf2); }
+ close(fd2);
+
+ /* If identical, we're done. */
+ if((s1.st_size == s2.st_size) && !memcmp(p1, p2, s1.st_size)) {
+ s_same_files++;
+ s_same_bytes += s1.st_size;
+ goto finish;
+ }
+
+ s_files_chg++;
+ change++;
+ if (s1.st_size > s2.st_size)
+ s_bytes_del += (s1.st_size - s2.st_size);
+ else
+ s_bytes_add += (s2.st_size - s1.st_size);
+
+ m1 = MD5Data(p1, s1.st_size, md5_1);
+ m2 = MD5Data(p2, s2.st_size, md5_2);
+
+ /* Just a curiosity... */
+ if(!strcmp(m1, m2)) {
+ if (s1.st_size != s2.st_size)
+ fprintf(stderr,
+ "Notice: MD5 same for files of diffent size:\n\t%s\n\t%s\n",
+ buf1, buf2);
+ goto finish;
+ }
+
+ {
+ u_long l = s2.st_size + 2;
+ u_char *cmd = alloca(strlen(buf1)+strlen(buf2)+100);
+ u_char *ob = 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(_PATH_DEVNULL, "w");
+
+ setbuf(stdout, 0);
+
+ if (argc != 6) {
+ Usage();
+ return (1);
+ }
+
+ signal(SIGINFO, stat_info);
+
+ fprintf(stderr, "CTM_BEGIN 2.0 %s %s %s %s\n",
+ argv[0], argv[1], argv[2], argv[3]);
+ fprintf(logf, "CTM_BEGIN 2.0 %s %s %s %s\n",
+ argv[0], argv[1], argv[2], argv[3]);
+ printf("CTM_BEGIN 2.0 %s %s %s %s\n",
+ argv[0], argv[1], argv[2], argv[3]);
+ DoDir(argv[4], argv[5], "");
+ if (damage_limit && damage > damage_limit) {
+ print_stat(stderr, "DAMAGE: ");
+ errx(1, "damage of %d would exceed %d files",
+ damage, damage_limit);
+ } else if (change < 2) {
+ errx(4, "no changes");
+ } else {
+ printf("CTM_END ");
+ fprintf(logf, "CTM_END\n");
+ print_stat(stderr, "END: ");
+ }
+ exit(0);
+}
diff --git a/usr.sbin/daemon/Makefile b/usr.sbin/daemon/Makefile
new file mode 100644
index 0000000..ab9f706
--- /dev/null
+++ b/usr.sbin/daemon/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+PROG= daemon
+WARNS?= 2
+MAN= daemon.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/daemon/daemon.8 b/usr.sbin/daemon/daemon.8
new file mode 100644
index 0000000..8bdefed
--- /dev/null
+++ b/usr.sbin/daemon/daemon.8
@@ -0,0 +1,73 @@
+.\" Copyright (c) 1999 Berkeley Software Design, Inc. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Berkeley Software Design Inc's name may not be used to endorse or
+.\" promote products derived from this software without specific prior
+.\" written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN INC ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN INC BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd August 30, 2001
+.Dt DAEMON 8
+.Os
+.Sh NAME
+.Nm daemon
+.Nd run detached from the controlling terminal
+.Sh SYNOPSIS
+.Nm
+.Op Fl cf
+.Ar command arguments ...
+.Sh DESCRIPTION
+The
+.Nm
+utility detaches itself from the controlling terminal and
+executes the program specified by its arguments.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl c
+Change the current working directory to the root
+.Pq Dq Pa / .
+.It Fl f
+Redirect standard input, standard output and standard error to
+.Pa /dev/null .
+.El
+.Sh DIAGNOSTICS
+The
+.Nm
+utility exits 1 if an error is returned by the
+.Xr daemon 3
+library routine, otherwise 0.
+If the command cannot be executed, an error message is displayed on
+standard error unless the
+.Fl f
+flag is specified.
+.Sh SEE ALSO
+.Xr daemon 3 ,
+.Xr exec 3 ,
+.Xr termios 4 ,
+.Xr tty 4
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 5.0 .
diff --git a/usr.sbin/daemon/daemon.c b/usr.sbin/daemon/daemon.c
new file mode 100644
index 0000000..89f135b
--- /dev/null
+++ b/usr.sbin/daemon/daemon.c
@@ -0,0 +1,78 @@
+/*-
+ * Copyright (c) 1999 Berkeley Software Design, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Berkeley Software Design Inc's name may not be used to endorse or
+ * promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN INC ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN INC BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * From BSDI: daemon.c,v 1.2 1996/08/15 01:11:09 jch Exp
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ int ch, nochdir, noclose;
+
+ nochdir = noclose = 1;
+ while ((ch = getopt(argc, argv, "-cf")) != -1) {
+ switch (ch) {
+ case 'c':
+ nochdir = 0;
+ break;
+ case 'f':
+ noclose = 0;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 0)
+ usage();
+ if (daemon(nochdir, noclose) == -1)
+ err(1, NULL);
+ execvp(argv[0], argv);
+
+ /* The child is now running, so the exit status doesn't matter. */
+ err(1, "%s", argv[0]);
+}
+
+static void
+usage(void)
+{
+ (void)fprintf(stderr, "usage: daemon [-cf] command arguments ...\n");
+ exit(1);
+}
diff --git a/usr.sbin/dev_mkdb/Makefile b/usr.sbin/dev_mkdb/Makefile
new file mode 100644
index 0000000..b12cfa4
--- /dev/null
+++ b/usr.sbin/dev_mkdb/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+PROG= dev_mkdb
+MAN= dev_mkdb.8
+
+WARNS?= 2
+
+.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..3d420e2
--- /dev/null
+++ b/usr.sbin/dev_mkdb/dev_mkdb.8
@@ -0,0 +1,91 @@
+.\" 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
+.\" $FreeBSD$
+.\"
+.Dd November 18, 2001
+.Os
+.Dt DEV_MKDB 8
+.Sh NAME
+.Nm dev_mkdb
+.Nd create
+.Pa /dev
+database
+.Sh SYNOPSIS
+.Nm
+.Op Fl f Ar file
+.Op Ar directory
+.Sh DESCRIPTION
+The
+.Nm
+command creates a
+.Xr db 3
+hash access method database in
+.Ar file
+.Pa ( /var/run/dev.db
+by default)
+which contains the names of all of the character and block special
+files in
+.Ar directory
+.Pa ( /dev
+by default), using the file type and the
+.Fa st_rdev
+field as the key.
+.Pp
+Keys are a structure containing a
+.Vt mode_t
+followed by a
+.Vt dev_t ,
+with any padding zero'd out.
+The former is the type of the file
+.Va ( st_mode Li & Dv S_IFMT ) ,
+the latter is the
+.Va st_rdev
+field.
+.Sh FILES
+.Bl -tag -width ".Pa /var/run/dev.db" -compact
+.It Pa /dev
+Default device directory.
+.It Pa /var/run/dev.db
+Default database file.
+.El
+.Sh SEE ALSO
+.Xr ps 1 ,
+.Xr stat 2 ,
+.Xr db 3 ,
+.Xr devname 3 ,
+.Xr ttyname 3
+.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..42a24a9
--- /dev/null
+++ b/usr.sbin/dev_mkdb/dev_mkdb.c
@@ -0,0 +1,166 @@
+/*-
+ * 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[] =
+ "$FreeBSD$";
+#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(void);
+int main(int argc, char *argv[]);
+
+int
+main(int argc, char *argv[])
+{
+ DIR *dirp;
+ struct dirent *dp;
+ struct stat sb;
+ struct {
+ mode_t type;
+ dev_t dev;
+ } bkey;
+ DB *db;
+ DBT data, key;
+ int ch, fflag;
+ u_char buf[MAXNAMLEN + 1];
+ char dbtmp[MAXPATHLEN], dbname[MAXPATHLEN];
+ const char *dirname;
+
+ fflag = 0;
+ while ((ch = getopt(argc, argv, "f:")) != -1)
+ switch((char)ch) {
+ case 'f':
+ strlcpy(dbname, optarg, sizeof(dbname));
+ fflag = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc > 1)
+ usage();
+ if (argc == 1)
+ dirname = argv[0];
+ else
+ dirname = _PATH_DEV;
+
+ if (!fflag) {
+ (void)snprintf(dbname, sizeof(dbtmp), "%sdev.db", _PATH_VARRUN);
+ (void)snprintf(dbtmp, sizeof(dbtmp), "%sdev.tmp", _PATH_VARRUN);
+ } else
+ (void)snprintf(dbtmp, sizeof(dbtmp), "%s.tmp", dbname);
+
+ if (chdir(dirname))
+ err(1, "%s", dirname);
+
+ dirp = opendir(".");
+
+ 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, "dbopen %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)
+{
+ (void)fprintf(stderr, "usage: dev_mkdb [-f file] [directory]\n");
+ exit(1);
+}
diff --git a/usr.sbin/devinfo/Makefile b/usr.sbin/devinfo/Makefile
new file mode 100644
index 0000000..52e4d6d
--- /dev/null
+++ b/usr.sbin/devinfo/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+PROG= devinfo
+MAN= devinfo.8
+
+WARNS?= 2
+
+DPADD= ${LIBDEVINFO}
+LDADD= -ldevinfo
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/devinfo/devinfo.8 b/usr.sbin/devinfo/devinfo.8
new file mode 100644
index 0000000..7ffa6d3
--- /dev/null
+++ b/usr.sbin/devinfo/devinfo.8
@@ -0,0 +1,68 @@
+.\" -*- nroff -*-
+.\"
+.\" Copyright (c) 2002 Hiten Pandya
+.\" Copyright (c) 2002 Robert N. M. Watson
+.\"
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd May 10, 2002
+.Os
+.Dt DEVINFO 8
+.Sh NAME
+.Nm devinfo
+.Nd print information about system device configuration
+.Sh SYNOPSIS
+.Nm
+.Op Fl ru
+.Sh DESCRIPTION
+The
+.Nm
+utility, without any arguments, shows the hierarchy of devices available
+in the system, starting from the
+.Dq nexus
+device.
+.Pp
+The following options are accepted.
+.Bl -tag -width indent
+.It Fl r
+Causes hardware resource information (such as IRQ, I/O ports, I/O memory
+addresses) to be also listed, under each device that has reserved those resources.
+.It Fl u
+Displays the same information as with
+.Fl r
+but sorts by resource type rather than by device, allowing to review the
+set of system resources by usage and available resources.
+I.e., it lists all
+the IRQ consumers together.
+.El
+.Sh SEE ALSO
+.Xr systat 1 ,
+.Xr devinfo 3 ,
+.Xr iostat 8 ,
+.Xr pciconf 8 ,
+.Xr pnpinfo 8 ,
+.Xr vmstat 8
+.Sh AUTHORS
+.An Mike Smith Aq msmith@FreeBSD.org
diff --git a/usr.sbin/devinfo/devinfo.c b/usr.sbin/devinfo/devinfo.c
new file mode 100644
index 0000000..c74de6c
--- /dev/null
+++ b/usr.sbin/devinfo/devinfo.c
@@ -0,0 +1,221 @@
+/*-
+ * Copyright (c) 2000, 2001 Michael Smith
+ * Copyright (c) 2000 BSDi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Print information about system device configuration.
+ */
+
+#include <sys/types.h>
+#include <err.h>
+#include <stdio.h>
+#include <unistd.h>
+#include "devinfo.h"
+
+int rflag;
+
+static void print_resource(struct devinfo_res *);
+static int print_device_matching_resource(struct devinfo_res *, void *);
+static int print_device_rman_resources(struct devinfo_rman *, void *);
+static int print_device(struct devinfo_dev *, void *);
+static int print_rman_resource(struct devinfo_res *, void *);
+static int print_rman(struct devinfo_rman *, void *);
+
+struct indent_arg
+{
+ int indent;
+ void *arg;
+};
+
+/*
+ * Print a resource.
+ */
+void
+print_resource(struct devinfo_res *res)
+{
+ struct devinfo_rman *rman;
+ int hexmode;
+
+ rman = devinfo_handle_to_rman(res->dr_rman);
+ hexmode = (rman->dm_size > 100) || (rman->dm_size == 0);
+ printf(hexmode ? "0x%lx" : "%lu", res->dr_start);
+ if (res->dr_size > 1)
+ printf(hexmode ? "-0x%lx" : "-%lu",
+ res->dr_start + res->dr_size - 1);
+}
+
+/*
+ * Print resource information if this resource matches the
+ * given device.
+ *
+ * If the given indent is 0, return an indicator that a matching
+ * resource exists.
+ */
+int
+print_device_matching_resource(struct devinfo_res *res, void *arg)
+{
+ struct indent_arg *ia = (struct indent_arg *)arg;
+ struct devinfo_dev *dev = (struct devinfo_dev *)ia->arg;
+ int i;
+
+ if (devinfo_handle_to_device(res->dr_device) == dev) {
+ /* in 'detect' mode, found a match */
+ if (ia->indent == 0)
+ return(1);
+ for (i = 0; i < ia->indent; i++)
+ printf(" ");
+ print_resource(res);
+ printf("\n");
+ }
+ return(0);
+}
+
+/*
+ * Print resource information for this device and resource manager.
+ */
+int
+print_device_rman_resources(struct devinfo_rman *rman, void *arg)
+{
+ struct indent_arg *ia = (struct indent_arg *)arg;
+ struct devinfo_dev *dev;
+ int indent, i;
+
+ indent = ia->indent;
+ dev = (struct devinfo_dev *)ia->arg;
+
+ /* check whether there are any resources matching this device */
+ ia->indent = 0;
+ if (devinfo_foreach_rman_resource(rman,
+ print_device_matching_resource, ia) != 0) {
+
+ /* there are, print header */
+ for (i = 0; i < indent; i++)
+ printf(" ");
+ printf("%s:\n", rman->dm_desc);
+
+ /* print resources */
+ ia->indent = indent + 4;
+ devinfo_foreach_rman_resource(rman,
+ print_device_matching_resource, ia);
+ }
+ ia->indent = indent;
+ return(0);
+}
+
+/*
+ * Print information about a device.
+ */
+int
+print_device(struct devinfo_dev *dev, void *arg)
+{
+ struct indent_arg ia;
+ int i, indent;
+
+ if (dev->dd_name[0] != 0) {
+ indent = (int)(intptr_t)arg;
+ for (i = 0; i < indent; i++)
+ printf(" ");
+ printf("%s\n", dev->dd_name);
+ if (rflag) {
+ ia.indent = indent + 4;
+ ia.arg = dev;
+ devinfo_foreach_rman(print_device_rman_resources,
+ (void *)&ia);
+ }
+ }
+
+ return(devinfo_foreach_device_child(dev, print_device,
+ (void *)((char *)arg + 2)));
+}
+
+/*
+ * Print information about a resource under a resource manager.
+ */
+int
+print_rman_resource(struct devinfo_res *res, void *arg __unused)
+{
+ struct devinfo_dev *dev;
+
+ printf(" ");
+ print_resource(res);
+ dev = devinfo_handle_to_device(res->dr_device);
+ if ((dev != NULL) && (dev->dd_name[0] != 0)) {
+ printf(" (%s)", dev->dd_name);
+ } else {
+ printf(" ----");
+ }
+ printf("\n");
+ return(0);
+}
+
+/*
+ * Print information about a resource manager.
+ */
+int
+print_rman(struct devinfo_rman *rman, void *arg __unused)
+{
+ printf("%s:\n", rman->dm_desc);
+ devinfo_foreach_rman_resource(rman, print_rman_resource, 0);
+ return(0);
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct devinfo_dev *root;
+ int c, uflag;
+
+ uflag = 0;
+ while ((c = getopt(argc, argv, "ru")) != -1) {
+ switch(c) {
+ case 'r':
+ rflag++;
+ break;
+ case 'u':
+ uflag++;
+ break;
+ default:
+ errx(1, "usage: %s [-ru]", argv[0]);
+ }
+ }
+
+ if (devinfo_init())
+ err(1, "devinfo_init");
+
+ if ((root = devinfo_handle_to_device(DEVINFO_ROOT_DEVICE)) == NULL)
+ errx(1, "can't find root device");
+
+ /* print resource usage? */
+ if (uflag) {
+ devinfo_foreach_rman(print_rman, NULL);
+ } else {
+ /* print device hierarchy */
+ devinfo_foreach_device_child(root, print_device, (void *)0);
+ }
+ return(0);
+}
diff --git a/usr.sbin/digictl/Makefile b/usr.sbin/digictl/Makefile
new file mode 100644
index 0000000..750a253
--- /dev/null
+++ b/usr.sbin/digictl/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+PROG= digictl
+WARNS?= 2
+MAN= digictl.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/digictl/digictl.8 b/usr.sbin/digictl/digictl.8
new file mode 100644
index 0000000..1636e6f
--- /dev/null
+++ b/usr.sbin/digictl/digictl.8
@@ -0,0 +1,118 @@
+.\" $FreeBSD$
+.Dd June 20, 2001
+.Dt DIGICTL 8
+.Os
+.Sh NAME
+.Nm digictl
+.Nd control
+.Tn Digiboard
+devices
+.Sh SYNOPSIS
+.Nm
+.Fl a
+.Cm disable | enable | query
+.Ar device ...
+.Nm
+.Op Fl d Ar debug
+.Op Fl ir
+.Ar ctrl-device ...
+.Sh DESCRIPTION
+The
+.Nm
+program provides control of the
+.Tn Digiboard
+installed with the given
+.Ar ctrl-device
+name and provides control of individual digiboard
+.Ar devices .
+A digiboard
+.Ar ctrl-device
+is usually of the form
+.Sm off
+.Pa /dev/digi Ar N Pa .ctl
+.Sm on
+where
+.Ar N
+is the card number and starts at
+.Dq 0
+for the first attached card.
+A digiboard
+.Ar device
+is usually of the form
+.Sm off
+.Pa /dev/cua Oo Pa il Oc Pa D Ar N Pa \&. Ar P
+.Sm on
+or
+.Sm off
+.Pa /dev/tty Oo Pa il Oc Pa D Ar N Pa \&. Ar P
+.Sm on
+where
+.Ar N
+is the card number and
+.Ar P
+is the port number.
+.Pp
+The following flags are recognized:
+.Bl -tag
+.It Fl a Cm disable | enable | query
+Disable, enable or query the
+.Em ALTPIN
+settings for the given port(s).
+.Pp
+When ALTPIN is enabled, the CD and DSR lines are logically reversed.
+This is useful when wiring serial ports to an 8 way RJ45 cable (full
+10 way RJ45 cables are quite rare).
+.Pp
+A single ALTPIN setting applies to both of the callout and callin devices.
+.It Fl d Ar debug
+If the driver has been compiled with
+.Dv DEBUG
+defined, the following bits from the
+.Ar debug
+variable are used to enable diagnostics in the digiboard driver:
+.Bl -tag -width ".No 1024 ( Em MODEM )"
+.It 1 ( Em INIT )
+Diagnostics during card attach, detach and initialization.
+.It 2 ( Em OPEN )
+Diagnostics when opening a port.
+.It 4 ( Em CLOSE )
+Diagnostics when closing a port.
+.It 8 ( Em SET )
+Diagnostics when setting tty device flags.
+.It 16 ( Em INT )
+Diagnostics when processing card events.
+.It 32 ( Em READ )
+Reports return values from port reads.
+.It 64 ( Em WRITE )
+Reports return values from port writes.
+.It 128 ( Em RX )
+Reports receive queue flow control.
+.It 256 ( Em TX )
+Reports transmit queue flow control.
+.It 512 ( Em IRQ )
+Diagnostics during interrupts (enable these with care).
+.It 1024 ( Em MODEM )
+Diagnostics when setting modem status flags.
+.It 2048 ( Em RI )
+Reports when a RING is received.
+.El
+.It Fl i
+Display the card identification string and type ID.
+.It Fl r
+Reinitialize the card.
+For boards with external port modules, it is possible to add or remove
+modules and dynamically reprobe the number of ports using this switch.
+All ports on the card must be closed in order to reinitialize the card.
+.Pp
+It is preferable to reinitialize the card rather than reload the entire
+digi module as reinitialization only affects the specified board rather
+than affecting all attached boards.
+.El
+.Sh BUGS
+It should be possible to reinitialize a board without closing all of the
+existing ports.
+.Sh HISTORY
+The
+.Nm
+command first appeared in
+.Fx 5.0 .
diff --git a/usr.sbin/digictl/digictl.c b/usr.sbin/digictl/digictl.c
new file mode 100644
index 0000000..0aa5f68
--- /dev/null
+++ b/usr.sbin/digictl/digictl.c
@@ -0,0 +1,171 @@
+/*-
+ * Copyright (c) 2001 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <sys/digiio.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+enum aflag { NO_AFLAG, ALTPIN_DISABLE, ALTPIN_ENABLE, ALTPIN_QUERY };
+
+static int
+usage(const char *prog)
+{
+ fprintf(stderr, "usage: %s -a disable|enable|query device\n", prog);
+ fprintf(stderr, " %s [-d debug] [-ir] ctrl-device...\n", prog);
+ return (EX_USAGE);
+}
+
+int
+main(int argc, char **argv)
+{
+ char namedata[256], *name = namedata;
+ const char *prog;
+ enum digi_model model;
+ int altpin, ch, debug, fd, i, res;
+ int dflag, iflag, rflag;
+ enum aflag aflag;
+
+ if ((prog = strrchr(argv[0], '/')) == NULL)
+ prog = argv[0];
+ else
+ prog++;
+
+ aflag = NO_AFLAG;
+ dflag = iflag = rflag = 0;
+ while ((ch = getopt(argc, argv, "a:d:ir")) != -1)
+ switch (ch) {
+ case 'a':
+ if (strcasecmp(optarg, "disable") == 0)
+ aflag = ALTPIN_DISABLE;
+ else if (strcasecmp(optarg, "enable") == 0)
+ aflag = ALTPIN_ENABLE;
+ else if (strcasecmp(optarg, "query") == 0)
+ aflag = ALTPIN_QUERY;
+ else
+ return (usage(prog));
+ break;
+
+ case 'd':
+ dflag = 1;
+ debug = atoi(optarg);
+ break;
+
+ case 'i':
+ iflag = 1;
+ break;
+
+ case 'r':
+ rflag = 1;
+ break;
+
+ default:
+ return (usage(prog));
+ }
+
+ if ((dflag || iflag || rflag) && aflag != NO_AFLAG)
+ return (usage(prog));
+
+ if (argc <= optind)
+ return (usage(prog));
+
+ altpin = (aflag == ALTPIN_ENABLE);
+ res = 0;
+
+ for (i = optind; i < argc; i++) {
+ if ((fd = open(argv[i], O_RDONLY)) == -1) {
+ fprintf(stderr, "%s: %s: open: %s\n", prog, argv[i],
+ strerror(errno));
+ res++;
+ continue;
+ }
+
+ switch (aflag) {
+ case NO_AFLAG:
+ break;
+
+ case ALTPIN_ENABLE:
+ case ALTPIN_DISABLE:
+ if (ioctl(fd, DIGIIO_SETALTPIN, &altpin) != 0) {
+ fprintf(stderr, "%s: %s: set altpin: %s\n",
+ prog, argv[i], strerror(errno));
+ res++;
+ }
+ break;
+
+ case ALTPIN_QUERY:
+ if (ioctl(fd, DIGIIO_GETALTPIN, &altpin) != 0) {
+ fprintf(stderr, "%s: %s: get altpin: %s\n",
+ prog, argv[i], strerror(errno));
+ res++;
+ } else {
+ if (argc > optind + 1)
+ printf("%s: ", argv[i]);
+ puts(altpin ? "enabled" : "disabled");
+ }
+ break;
+ }
+
+ if (dflag && ioctl(fd, DIGIIO_DEBUG, &debug) != 0) {
+ fprintf(stderr, "%s: %s: debug: %s\n",
+ prog, argv[i], strerror(errno));
+ res++;
+ }
+
+ if (iflag) {
+ if (ioctl(fd, DIGIIO_MODEL, &model) != 0) {
+ fprintf(stderr, "%s: %s: model: %s\n",
+ prog, argv[i], strerror(errno));
+ res++;
+ } else if (ioctl(fd, DIGIIO_IDENT, &name) != 0) {
+ fprintf(stderr, "%s: %s: ident: %s\n",
+ prog, argv[i], strerror(errno));
+ res++;
+ } else {
+ if (argc > optind + 1)
+ printf("%s: ", argv[i]);
+ printf("%s (type %d)\n", name, (int)model);
+ }
+ }
+
+ if (rflag && ioctl(fd, DIGIIO_REINIT) != 0) {
+ fprintf(stderr, "%s: %s: reinit: %s\n",
+ prog, argv[i], strerror(errno));
+ res++;
+ }
+
+ close(fd);
+ }
+
+ return (res);
+}
diff --git a/usr.sbin/editmap/Makefile b/usr.sbin/editmap/Makefile
new file mode 100644
index 0000000..4e75403
--- /dev/null
+++ b/usr.sbin/editmap/Makefile
@@ -0,0 +1,51 @@
+# $FreeBSD$
+
+MAINTAINER= gshapiro@FreeBSD.org
+
+SENDMAIL_DIR=${.CURDIR}/../../contrib/sendmail
+.PATH: ${SENDMAIL_DIR}/editmap
+
+PROG= editmap
+SRCS= editmap.c
+MAN= editmap.8
+
+CFLAGS+= -I${SENDMAIL_DIR}/src -I${SENDMAIL_DIR}/include -I.
+CFLAGS+= -DNEWDB -DNOT_SENDMAIL
+
+.if exists(${.OBJDIR}/../../lib/libsm)
+LIBSMDIR:= ${.OBJDIR}/../../lib/libsm
+.else
+LIBSMDIR!= cd ${.CURDIR}/../../lib/libsm; make -V .OBJDIR
+.endif
+LIBSM:= ${LIBSMDIR}/libsm.a
+
+.if exists(${.OBJDIR}/../../lib/libsmdb)
+LIBSMDBDIR:= ${.OBJDIR}/../../lib/libsmdb
+.else
+LIBSMDBDIR!= cd ${.CURDIR}/../../lib/libsmdb; make -V .OBJDIR
+.endif
+LIBSMDB:= ${LIBSMDBDIR}/libsmdb.a
+
+.if exists(${.OBJDIR}/../../lib/libsmutil)
+LIBSMUTILDIR:= ${.OBJDIR}/../../lib/libsmutil
+.else
+LIBSMUTILDIR!= cd ${.CURDIR}/../../lib/libsmutil; make -V .OBJDIR
+.endif
+LIBSMUTIL:= ${LIBSMUTILDIR}/libsmutil.a
+
+DPADD= ${LIBSMDB} ${LIBSMUTIL} ${LIBSM}
+LDADD= ${LIBSMDB} ${LIBSMUTIL} ${LIBSM}
+
+SRCS+= sm_os.h
+CLEANFILES+=sm_os.h
+
+# User customizations to the sendmail build environment
+CFLAGS+=${SENDMAIL_CFLAGS}
+DPADD+=${SENDMAIL_DPADD}
+LDADD+=${SENDMAIL_LDADD}
+LDFLAGS+=${SENDMAIL_LDFLAGS}
+
+sm_os.h:
+ ln -sf ${SENDMAIL_DIR}/include/sm/os/sm_os_freebsd.h sm_os.h
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/edquota/Makefile b/usr.sbin/edquota/Makefile
new file mode 100644
index 0000000..0dcede4
--- /dev/null
+++ b/usr.sbin/edquota/Makefile
@@ -0,0 +1,8 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+PROG= edquota
+WARNS?= 2
+MAN= 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..6ad0c85
--- /dev/null
+++ b/usr.sbin/edquota/edquota.8
@@ -0,0 +1,194 @@
+.\" Copyright (c) 1983, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Robert Elz at The University of Melbourne.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)edquota.8 8.1 (Berkeley) 6/6/93
+.\" $FreeBSD$
+.\"
+.Dd June 6, 1993
+.Dt EDQUOTA 8
+.Os
+.Sh NAME
+.Nm edquota
+.Nd edit user quotas
+.Sh SYNOPSIS
+.Nm
+.Op Fl u
+.Op Fl f Ar fspath
+.Op Fl p Ar proto-username
+.Ar username ...
+.Nm
+.Fl g
+.Op Fl f Ar fspath
+.Op Fl p Ar proto-groupname
+.Ar groupname ...
+.Nm
+.Fl t
+.Op Fl u
+.Op Fl f Ar fspath
+.Nm
+.Fl t
+.Fl g
+.Op Fl f Ar fspath
+.Sh DESCRIPTION
+.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 invoked with the
+.Fl f
+option,
+.Nm
+will read and modify quotas on the filesystem specified by
+.Ar fspath
+only.
+The
+.Ar fspath
+argument may be either a special device
+or a filesystem mount point.
+The primary purpose of this option is to set the scope for the
+.Fl p
+option, which would overwrite quota records on every
+filesystem with quotas otherwise.
+.Pp
+If the
+.Fl g
+flag is specified,
+.Nm
+is invoked to edit the quotas of
+one or more groups specified on the command line.
+The
+.Fl p
+flag can be specified in conjunction with
+the
+.Fl g
+flag to specify a prototypical group
+to be duplicated among the listed set of groups.
+.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 quotactl 2 ,
+.Xr fstab 5 ,
+.Xr quotacheck 8 ,
+.Xr quotaon 8 ,
+.Xr repquota 8
+.Sh DIAGNOSTICS
+Various messages about inaccessible files; self-explanatory.
diff --git a/usr.sbin/edquota/edquota.c b/usr.sbin/edquota/edquota.c
new file mode 100644
index 0000000..fefb657
--- /dev/null
+++ b/usr.sbin/edquota/edquota.c
@@ -0,0 +1,770 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#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"
+
+const char *qfname = QUOTAFILENAME;
+const char *qfextension[] = INITQFNAMES;
+const char *quotagroup = QUOTAGROUP;
+char tmpfil[] = _PATH_TMP;
+
+struct quotause {
+ struct quotause *next;
+ long flags;
+ struct dqblk dqblk;
+ char fsname[MAXPATHLEN + 1];
+ char qfname[1]; /* actually longer */
+};
+#define FOUND 0x01
+
+int alldigits __P((const 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((const char *, int));
+struct quotause *getprivs __P((long, int, char *));
+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 *fspath = NULL;
+ char buf[30];
+
+ if (argc < 2)
+ usage();
+ if (getuid())
+ errx(1, "permission denied");
+ quotatype = USRQUOTA;
+ while ((ch = getopt(argc, argv, "ugtf:p:")) != -1) {
+ switch(ch) {
+ case 'f':
+ fspath = optarg;
+ break;
+ case 'p':
+ protoname = optarg;
+ pflag++;
+ break;
+ case 'g':
+ quotatype = GRPQUOTA;
+ break;
+ case 'u':
+ quotatype = USRQUOTA;
+ break;
+ case 't':
+ tflag++;
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (pflag) {
+ if ((protoid = getentry(protoname, quotatype)) == -1)
+ exit(1);
+ protoprivs = getprivs(protoid, quotatype, fspath);
+ for (qup = protoprivs; qup; qup = qup->next) {
+ qup->dqblk.dqb_btime = 0;
+ qup->dqblk.dqb_itime = 0;
+ }
+ 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, fspath);
+ if (writetimes(protoprivs, tmpfd, quotatype) == 0)
+ exit(1);
+ if (editit(tmpfil) && readtimes(protoprivs, tmpfil))
+ putprivs(0, quotatype, protoprivs);
+ freeprivs(protoprivs);
+ close(tmpfd);
+ unlink(tmpfil);
+ exit(0);
+ }
+ for ( ; argc > 0; argc--, argv++) {
+ if ((id = getentry(*argv, quotatype)) == -1)
+ continue;
+ curprivs = getprivs(id, quotatype, fspath);
+ if (writeprivs(curprivs, tmpfd, *argv, quotatype) == 0)
+ continue;
+ if (editit(tmpfil) && readprivs(curprivs, tmpfil))
+ putprivs(id, quotatype, curprivs);
+ freeprivs(curprivs);
+ }
+ close(tmpfd);
+ unlink(tmpfil);
+ exit(0);
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n%s\n%s\n",
+ "usage: edquota [-u] [-f fspath] [-p username] username ...",
+ " edquota -g [-f fspath] [-p groupname] groupname ...",
+ " edquota [-u] -t [-f fspath]",
+ " edquota -g -t [-f fspath]");
+ exit(1);
+}
+
+/*
+ * This routine converts a name for a particular quota type to
+ * an identifier. This routine must agree with the kernel routine
+ * getinoquota as to the interpretation of quota types.
+ */
+int
+getentry(name, quotatype)
+ const char *name;
+ int quotatype;
+{
+ struct passwd *pw;
+ struct group *gr;
+
+ if (alldigits(name))
+ return (atoi(name));
+ switch(quotatype) {
+ case USRQUOTA:
+ if ((pw = getpwnam(name)))
+ return (pw->pw_uid);
+ warnx("%s: no such user", name);
+ break;
+ case GRPQUOTA:
+ if ((gr = getgrnam(name)))
+ return (gr->gr_gid);
+ warnx("%s: no such group", name);
+ break;
+ default:
+ warnx("%d: unknown quota type", quotatype);
+ break;
+ }
+ sleep(1);
+ return (-1);
+}
+
+/*
+ * Collect the requested quota information.
+ */
+struct quotause *
+getprivs(id, quotatype, fspath)
+ register long id;
+ int quotatype;
+ char *fspath;
+{
+ register struct fstab *fs;
+ register struct quotause *qup, *quptail;
+ struct quotause *quphead;
+ int qcmd, qupsize, fd;
+ char *qfpathname;
+ static int warned = 0;
+
+ setfsent();
+ quphead = (struct quotause *)0;
+ qcmd = QCMD(Q_GETQUOTA, quotatype);
+ while ((fs = getfsent())) {
+ if (fspath && *fspath && strcmp(fspath, fs->fs_spec) &&
+ strcmp(fspath, fs->fs_file))
+ continue;
+ if (strcmp(fs->fs_vfstype, "ufs"))
+ continue;
+ if (!hasquota(fs, quotatype, &qfpathname))
+ continue;
+ qupsize = sizeof(*qup) + strlen(qfpathname);
+ if ((qup = (struct quotause *)malloc(qupsize)) == NULL)
+ errx(2, "out of memory");
+ if (quotactl(fs->fs_file, qcmd, id, &qup->dqblk) != 0) {
+ if (errno == EOPNOTSUPP && !warned) {
+ warned++;
+ warnx("warning: quotas are not compiled into this kernel");
+ sleep(3);
+ }
+ if ((fd = open(qfpathname, O_RDONLY)) < 0) {
+ fd = open(qfpathname, O_RDWR|O_CREAT, 0640);
+ if (fd < 0 && errno != ENOENT) {
+ warn("%s", qfpathname);
+ free(qup);
+ continue;
+ }
+ warnx("creating quota file %s", qfpathname);
+ sleep(3);
+ (void) fchown(fd, getuid(),
+ getentry(quotagroup, GRPQUOTA));
+ (void) fchmod(fd, 0640);
+ }
+ lseek(fd, (long)(id * sizeof(struct dqblk)), L_SET);
+ switch (read(fd, &qup->dqblk, sizeof(struct dqblk))) {
+ case 0: /* EOF */
+ /*
+ * Convert implicit 0 quota (EOF)
+ * into an explicit one (zero'ed dqblk)
+ */
+ bzero((caddr_t)&qup->dqblk,
+ sizeof(struct dqblk));
+ break;
+
+ case sizeof(struct dqblk): /* OK */
+ break;
+
+ default: /* ERROR */
+ warn("read error in %s", qfpathname);
+ close(fd);
+ free(qup);
+ continue;
+ }
+ close(fd);
+ }
+ strcpy(qup->qfname, qfpathname);
+ strcpy(qup->fsname, fs->fs_file);
+ if (quphead == NULL)
+ quphead = qup;
+ else
+ quptail->next = qup;
+ quptail = qup;
+ qup->next = 0;
+ }
+ endfsent();
+ return (quphead);
+}
+
+/*
+ * Store the requested quota information.
+ */
+void
+putprivs(id, quotatype, quplist)
+ long id;
+ int quotatype;
+ struct quotause *quplist;
+{
+ register struct quotause *qup;
+ int qcmd, fd;
+
+ qcmd = QCMD(Q_SETQUOTA, quotatype);
+ for (qup = quplist; qup; qup = qup->next) {
+ if (quotactl(qup->fsname, qcmd, id, &qup->dqblk) == 0)
+ continue;
+ if ((fd = open(qup->qfname, O_WRONLY)) < 0) {
+ warn("%s", qup->qfname);
+ } else {
+ lseek(fd, (long)id * (long)sizeof (struct dqblk), 0);
+ if (write(fd, &qup->dqblk, sizeof (struct dqblk)) !=
+ sizeof (struct dqblk)) {
+ warn("%s", qup->qfname);
+ }
+ close(fd);
+ }
+ }
+}
+
+/*
+ * Take a list of priviledges and get it edited.
+ */
+int
+editit(tmpf)
+ char *tmpf;
+{
+ long omask;
+ int pid, status;
+
+ omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
+ top:
+ if ((pid = fork()) < 0) {
+
+ if (errno == EPROCLIM) {
+ warnx("you have too many processes");
+ return(0);
+ }
+ if (errno == EAGAIN) {
+ sleep(1);
+ goto top;
+ }
+ warn("fork");
+ return (0);
+ }
+ if (pid == 0) {
+ register const char *ed;
+
+ sigsetmask(omask);
+ setgid(getgid());
+ setuid(getuid());
+ if ((ed = getenv("EDITOR")) == (char *)0)
+ ed = _PATH_VI;
+ execlp(ed, ed, tmpf, (char *)0);
+ err(1, "%s", ed);
+ }
+ waitpid(pid, &status, 0);
+ sigsetmask(omask);
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
+ return (0);
+ return (1);
+}
+
+/*
+ * Convert a quotause list to an ASCII file.
+ */
+int
+writeprivs(quplist, outfd, name, quotatype)
+ struct quotause *quplist;
+ int outfd;
+ char *name;
+ int quotatype;
+{
+ register struct quotause *qup;
+ FILE *fd;
+
+ ftruncate(outfd, 0);
+ lseek(outfd, 0, L_SET);
+ if ((fd = fdopen(dup(outfd), "w")) == NULL)
+ err(1, "%s", tmpfil);
+ fprintf(fd, "Quotas for %s %s:\n", qfextension[quotatype], name);
+ for (qup = quplist; qup; qup = qup->next) {
+ fprintf(fd, "%s: %s %lu, limits (soft = %lu, hard = %lu)\n",
+ qup->fsname, "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:",
+ (unsigned long)qup->dqblk.dqb_curinodes,
+ (unsigned long)qup->dqblk.dqb_isoftlimit,
+ (unsigned long)qup->dqblk.dqb_ihardlimit);
+ }
+ fclose(fd);
+ return (1);
+}
+
+/*
+ * Merge changes to an ASCII file into a quotause list.
+ */
+int
+readprivs(quplist, inname)
+ struct quotause *quplist;
+ char *inname;
+{
+ register struct quotause *qup;
+ FILE *fd;
+ unsigned long bhardlimit, bsoftlimit, curblocks;
+ unsigned long ihardlimit, isoftlimit, curinodes;
+ int cnt;
+ register char *cp;
+ struct dqblk dqblk;
+ char *fsp, line1[BUFSIZ], line2[BUFSIZ];
+
+ fd = fopen(inname, "r");
+ if (fd == NULL) {
+ warnx("can't re-read temp file!!");
+ return (0);
+ }
+ /*
+ * Discard title line, then read pairs of lines to process.
+ */
+ (void) fgets(line1, sizeof (line1), fd);
+ while (fgets(line1, sizeof (line1), fd) != NULL &&
+ fgets(line2, sizeof (line2), fd) != NULL) {
+ if ((fsp = strtok(line1, " \t:")) == NULL) {
+ warnx("%s: bad format", line1);
+ return (0);
+ }
+ if ((cp = strtok((char *)0, "\n")) == NULL) {
+ warnx("%s: %s: bad format", fsp, &fsp[strlen(fsp) + 1]);
+ return (0);
+ }
+ cnt = sscanf(cp,
+ " blocks in use: %lu, limits (soft = %lu, hard = %lu)",
+ &curblocks, &bsoftlimit, &bhardlimit);
+ if (cnt != 3) {
+ warnx("%s:%s: bad format", fsp, cp);
+ return (0);
+ }
+ dqblk.dqb_curblocks = btodb((off_t)curblocks * 1024);
+ dqblk.dqb_bsoftlimit = btodb((off_t)bsoftlimit * 1024);
+ dqblk.dqb_bhardlimit = btodb((off_t)bhardlimit * 1024);
+ if ((cp = strtok(line2, "\n")) == NULL) {
+ warnx("%s: %s: bad format", fsp, line2);
+ return (0);
+ }
+ cnt = sscanf(cp,
+ "\tinodes in use: %lu, limits (soft = %lu, hard = %lu)",
+ &curinodes, &isoftlimit, &ihardlimit);
+ if (cnt != 3) {
+ warnx("%s: %s: bad format", fsp, line2);
+ return (0);
+ }
+ dqblk.dqb_curinodes = curinodes;
+ dqblk.dqb_isoftlimit = isoftlimit;
+ dqblk.dqb_ihardlimit = ihardlimit;
+ for (qup = quplist; qup; qup = qup->next) {
+ if (strcmp(fsp, qup->fsname))
+ continue;
+ /*
+ * Cause time limit to be reset when the quota
+ * is next used if previously had no soft limit
+ * or were under it, but now have a soft limit
+ * and are over it.
+ */
+ if (dqblk.dqb_bsoftlimit &&
+ qup->dqblk.dqb_curblocks >= dqblk.dqb_bsoftlimit &&
+ (qup->dqblk.dqb_bsoftlimit == 0 ||
+ qup->dqblk.dqb_curblocks <
+ qup->dqblk.dqb_bsoftlimit))
+ qup->dqblk.dqb_btime = 0;
+ if (dqblk.dqb_isoftlimit &&
+ qup->dqblk.dqb_curinodes >= dqblk.dqb_isoftlimit &&
+ (qup->dqblk.dqb_isoftlimit == 0 ||
+ qup->dqblk.dqb_curinodes <
+ qup->dqblk.dqb_isoftlimit))
+ qup->dqblk.dqb_itime = 0;
+ qup->dqblk.dqb_bsoftlimit = dqblk.dqb_bsoftlimit;
+ qup->dqblk.dqb_bhardlimit = dqblk.dqb_bhardlimit;
+ qup->dqblk.dqb_isoftlimit = dqblk.dqb_isoftlimit;
+ qup->dqblk.dqb_ihardlimit = dqblk.dqb_ihardlimit;
+ qup->flags |= FOUND;
+ if (dqblk.dqb_curblocks == qup->dqblk.dqb_curblocks &&
+ dqblk.dqb_curinodes == qup->dqblk.dqb_curinodes)
+ break;
+ warnx("%s: cannot change current allocation", fsp);
+ break;
+ }
+ }
+ fclose(fd);
+ /*
+ * Disable quotas for any filesystems that have not been found.
+ */
+ for (qup = quplist; qup; qup = qup->next) {
+ if (qup->flags & FOUND) {
+ qup->flags &= ~FOUND;
+ continue;
+ }
+ qup->dqblk.dqb_bsoftlimit = 0;
+ qup->dqblk.dqb_bhardlimit = 0;
+ qup->dqblk.dqb_isoftlimit = 0;
+ qup->dqblk.dqb_ihardlimit = 0;
+ }
+ return (1);
+}
+
+/*
+ * Convert a quotause list to an ASCII file of grace times.
+ */
+int
+writetimes(quplist, outfd, quotatype)
+ struct quotause *quplist;
+ int outfd;
+ int quotatype;
+{
+ register struct quotause *qup;
+ FILE *fd;
+
+ ftruncate(outfd, 0);
+ lseek(outfd, 0, L_SET);
+ if ((fd = fdopen(dup(outfd), "w")) == NULL)
+ err(1, "%s", tmpfil);
+ fprintf(fd, "Time units may be: days, hours, minutes, or seconds\n");
+ fprintf(fd, "Grace period before enforcing soft limits for %ss:\n",
+ qfextension[quotatype]);
+ for (qup = quplist; qup; qup = qup->next) {
+ fprintf(fd, "%s: block grace period: %s, ",
+ qup->fsname, cvtstoa(qup->dqblk.dqb_btime));
+ fprintf(fd, "file grace period: %s\n",
+ cvtstoa(qup->dqblk.dqb_itime));
+ }
+ fclose(fd);
+ return (1);
+}
+
+/*
+ * Merge changes of grace times in an ASCII file into a quotause list.
+ */
+int
+readtimes(quplist, inname)
+ struct quotause *quplist;
+ char *inname;
+{
+ register struct quotause *qup;
+ FILE *fd;
+ int cnt;
+ register char *cp;
+ time_t itime, btime, iseconds, bseconds;
+ long l_itime, l_btime;
+ char *fsp, bunits[10], iunits[10], line1[BUFSIZ];
+
+ fd = fopen(inname, "r");
+ if (fd == NULL) {
+ warnx("can't re-read temp file!!");
+ return (0);
+ }
+ /*
+ * Discard two title lines, then read lines to process.
+ */
+ (void) fgets(line1, sizeof (line1), fd);
+ (void) fgets(line1, sizeof (line1), fd);
+ while (fgets(line1, sizeof (line1), fd) != NULL) {
+ if ((fsp = strtok(line1, " \t:")) == NULL) {
+ warnx("%s: bad format", line1);
+ return (0);
+ }
+ if ((cp = strtok((char *)0, "\n")) == NULL) {
+ warnx("%s: %s: bad format", fsp, &fsp[strlen(fsp) + 1]);
+ return (0);
+ }
+ cnt = sscanf(cp,
+ " block grace period: %ld %s file grace period: %ld %s",
+ &l_btime, bunits, &l_itime, iunits);
+ if (cnt != 4) {
+ warnx("%s:%s: bad format", fsp, cp);
+ return (0);
+ }
+ btime = l_btime;
+ itime = l_itime;
+ if (cvtatos(btime, bunits, &bseconds) == 0)
+ return (0);
+ if (cvtatos(itime, iunits, &iseconds) == 0)
+ return (0);
+ for (qup = quplist; qup; qup = qup->next) {
+ if (strcmp(fsp, qup->fsname))
+ continue;
+ qup->dqblk.dqb_btime = bseconds;
+ qup->dqblk.dqb_itime = iseconds;
+ qup->flags |= FOUND;
+ break;
+ }
+ }
+ fclose(fd);
+ /*
+ * reset default grace periods for any filesystems
+ * that have not been found.
+ */
+ for (qup = quplist; qup; qup = qup->next) {
+ if (qup->flags & FOUND) {
+ qup->flags &= ~FOUND;
+ continue;
+ }
+ qup->dqblk.dqb_btime = 0;
+ qup->dqblk.dqb_itime = 0;
+ }
+ return (1);
+}
+
+/*
+ * Convert seconds to ASCII times.
+ */
+char *
+cvtstoa(secs)
+ time_t secs;
+{
+ static char buf[20];
+
+ if (secs % (24 * 60 * 60) == 0) {
+ secs /= 24 * 60 * 60;
+ sprintf(buf, "%ld day%s", (long)secs, secs == 1 ? "" : "s");
+ } else if (secs % (60 * 60) == 0) {
+ secs /= 60 * 60;
+ sprintf(buf, "%ld hour%s", (long)secs, secs == 1 ? "" : "s");
+ } else if (secs % 60 == 0) {
+ secs /= 60;
+ sprintf(buf, "%ld minute%s", (long)secs, secs == 1 ? "" : "s");
+ } else
+ sprintf(buf, "%ld second%s", (long)secs, secs == 1 ? "" : "s");
+ return (buf);
+}
+
+/*
+ * Convert ASCII input times to seconds.
+ */
+int
+cvtatos(period, units, seconds)
+ time_t period;
+ char *units;
+ time_t *seconds;
+{
+
+ if (bcmp(units, "second", 6) == 0)
+ *seconds = period;
+ else if (bcmp(units, "minute", 6) == 0)
+ *seconds = period * 60;
+ else if (bcmp(units, "hour", 4) == 0)
+ *seconds = period * 60 * 60;
+ else if (bcmp(units, "day", 3) == 0)
+ *seconds = period * 24 * 60 * 60;
+ else {
+ printf("%s: bad units, specify %s\n", units,
+ "days, hours, minutes, or seconds");
+ return (0);
+ }
+ return (1);
+}
+
+/*
+ * Free a list of quotause structures.
+ */
+void
+freeprivs(quplist)
+ struct quotause *quplist;
+{
+ register struct quotause *qup, *nextqup;
+
+ for (qup = quplist; qup; qup = nextqup) {
+ nextqup = qup->next;
+ free(qup);
+ }
+}
+
+/*
+ * Check whether a string is completely composed of digits.
+ */
+int
+alldigits(s)
+ register const char *s;
+{
+ register int c;
+
+ c = *s++;
+ do {
+ if (!isdigit(c))
+ return (0);
+ } while ((c = *s++));
+ return (1);
+}
+
+/*
+ * Check to see if a particular quota is to be enabled.
+ */
+int
+hasquota(fs, type, qfnamep)
+ register struct fstab *fs;
+ int type;
+ char **qfnamep;
+{
+ register char *opt;
+ char *cp;
+ static char initname, usrname[100], grpname[100];
+ static char buf[BUFSIZ];
+
+ if (!initname) {
+ sprintf(usrname, "%s%s", qfextension[USRQUOTA], qfname);
+ sprintf(grpname, "%s%s", qfextension[GRPQUOTA], qfname);
+ initname = 1;
+ }
+ strcpy(buf, fs->fs_mntops);
+ for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
+ if ((cp = index(opt, '=')))
+ *cp++ = '\0';
+ if (type == USRQUOTA && strcmp(opt, usrname) == 0)
+ break;
+ if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
+ break;
+ }
+ if (!opt)
+ return (0);
+ if (cp) {
+ *qfnamep = cp;
+ return (1);
+ }
+ (void) sprintf(buf, "%s/%s.%s", fs->fs_file, qfname, qfextension[type]);
+ *qfnamep = buf;
+ return (1);
+}
diff --git a/usr.sbin/edquota/pathnames.h b/usr.sbin/edquota/pathnames.h
new file mode 100644
index 0000000..92546f7
--- /dev/null
+++ b/usr.sbin/edquota/pathnames.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/6/93
+ */
+
+#include <paths.h>
+
+#undef _PATH_TMP
+#define _PATH_TMP "/tmp/EdP.aXXXXX"
diff --git a/usr.sbin/elf2aout/Makefile b/usr.sbin/elf2aout/Makefile
new file mode 100644
index 0000000..c05fb5c
--- /dev/null
+++ b/usr.sbin/elf2aout/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= elf2aout
+CFLAGS+=-I${.CURDIR}/../crunch/crunchide
+WARNS?= 5
+NOMAN=
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/elf2aout/elf2aout.c b/usr.sbin/elf2aout/elf2aout.c
new file mode 100644
index 0000000..a9e1a01
--- /dev/null
+++ b/usr.sbin/elf2aout/elf2aout.c
@@ -0,0 +1,162 @@
+/*-
+ * Copyright (c) 2002 Jake Burkholder
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/elf64.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "endian.h"
+#define xe16toh(x) ((data == ELFDATA2MSB) ? be16toh(x) : le16toh(x))
+#define xe32toh(x) ((data == ELFDATA2MSB) ? be32toh(x) : le32toh(x))
+#define xe64toh(x) ((data == ELFDATA2MSB) ? be64toh(x) : le64toh(x))
+#define htoxe32(x) ((data == ELFDATA2MSB) ? htobe32(x) : htole32(x))
+
+struct exec {
+ u_int32_t a_magic;
+ u_int32_t a_text;
+ u_int32_t a_data;
+ u_int32_t a_bss;
+ u_int32_t a_syms;
+ u_int32_t a_entry;
+ u_int32_t a_trsize;
+ u_int32_t a_drsize;
+};
+#define A_MAGIC 0x01030107
+
+extern char *optarg;
+extern int optind;
+
+static void usage(void);
+
+/*
+ * elf to a.out converter for freebsd/sparc64 bootblocks.
+ */
+int
+main(int ac, char **av)
+{
+ Elf64_Quarter phentsize;
+ Elf64_Quarter machine;
+ Elf64_Quarter phnum;
+ Elf64_Size filesz;
+ Elf64_Size memsz;
+ Elf64_Addr entry;
+ Elf64_Off offset;
+ Elf64_Off phoff;
+ Elf64_Half type;
+ unsigned char data;
+ struct stat sb;
+ struct exec a;
+ Elf64_Phdr *p;
+ Elf64_Ehdr *e;
+ void *v;
+ int efd;
+ int fd;
+ int c;
+ int i;
+
+ fd = STDIN_FILENO;
+ while ((c = getopt(ac, av, "o:")) != -1)
+ switch (c) {
+ case 'o':
+ if ((fd = open(optarg, O_CREAT|O_RDWR, 0644)) < 0)
+ err(1, "%s", optarg);
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ ac -= optind;
+ av += optind;
+ if (ac == 0)
+ usage();
+
+ if ((efd = open(*av, O_RDONLY)) < 0 || fstat(efd, &sb) < 0)
+ err(1, NULL);
+ v = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, efd, 0);
+ if ((e = v) == MAP_FAILED)
+ err(1, NULL);
+
+ if (!IS_ELF(*e))
+ errx(1, "not an elf file");
+ if (e->e_ident[EI_CLASS] != ELFCLASS64)
+ errx(1, "wrong class");
+ data = e->e_ident[EI_DATA];
+ if (data != ELFDATA2MSB && data != ELFDATA2LSB)
+ errx(1, "wrong data format");
+ if (e->e_ident[EI_VERSION] != EV_CURRENT)
+ errx(1, "wrong elf version");
+ machine = xe16toh(e->e_machine);
+ if (machine != EM_SPARCV9 && machine != EM_ALPHA)
+ errx(1, "wrong machine type");
+ phentsize = xe16toh(e->e_phentsize);
+ if (phentsize != sizeof(*p))
+ errx(1, "phdr size mismatch");
+
+ entry = xe64toh(e->e_entry);
+ phoff = xe64toh(e->e_phoff);
+ phnum = xe16toh(e->e_phnum);
+ p = (Elf64_Phdr *)((char *)e + phoff);
+ bzero(&a, sizeof(a));
+ for (i = 0; i < phnum; i++) {
+ type = xe32toh(p[i].p_type);
+ switch (type) {
+ case PT_LOAD:
+ if (a.a_magic != 0)
+ errx(1, "too many loadable segments");
+ filesz = xe64toh(p[i].p_filesz);
+ memsz = xe64toh(p[i].p_memsz);
+ offset = xe64toh(p[i].p_offset);
+ a.a_magic = htoxe32(A_MAGIC);
+ a.a_text = htoxe32(filesz);
+ a.a_bss = htoxe32(memsz - filesz);
+ a.a_entry = htoxe32(entry);
+ if (write(fd, &a, sizeof(a)) != sizeof(a) ||
+ write(fd, (char *)e + offset, filesz) != (ssize_t)filesz)
+ err(1, NULL);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return (0);
+}
+
+static void
+usage(void)
+{
+
+ errx(1, "usage: elf2aout [-o outfile] infile");
+}
diff --git a/usr.sbin/elf2exe/Makefile b/usr.sbin/elf2exe/Makefile
new file mode 100644
index 0000000..69e3622
--- /dev/null
+++ b/usr.sbin/elf2exe/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+PROG= elf2exe
+MAN= elf2exe.8
+MANSUBDIR= /${MACHINE_ARCH}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/elf2exe/elf2exe.8 b/usr.sbin/elf2exe/elf2exe.8
new file mode 100644
index 0000000..4817cba
--- /dev/null
+++ b/usr.sbin/elf2exe/elf2exe.8
@@ -0,0 +1,52 @@
+.\" Copyright (c) 1999 Stefan Esser
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd July 26, 1999
+.Dt ELF2EXE 8 Alpha
+.Os
+.Sh NAME
+.Nm elf2exe
+.Nd convert Alpha ELF executable to AlphaBIOS / ARCS format
+.Sh SYNOPSIS
+.Nm
+.Ar infile
+.Ar outfile
+.Sh DESCRIPTION
+.Nm Elf2exe
+creates an executable that can be loaded by the AlphaBIOS or ARCS consoles
+as found on systems designed for
+.Tn Windows/NT .
+The input file must have been
+created as a non-relocatable standalone binary with a load address within
+the memory range available for user programs (0x80000000 to 0x806fdfff
+and 0x80900000 to at least 0x80ffffff).
+.Pp
+The command prints a list of sections found in the ELF executable and the
+section sizes and offsets of the output file for diagnostic purposes.
+.Pp
+Given an object file
+.Pa src.o
+the following two commands will create a binary for ARCS:
+.Bd -literal -offset indent
+ld -o a.out -M -N -Ttext 0x80900000 src.o
+elf2exe a.out a.exe
+.Ed
+.Sh BUGS
+.Nm Elf2exe
+does not even attempt to verify that the input file matches the requirements
+for an ARC executable.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Fx 4.0 .
diff --git a/usr.sbin/elf2exe/elf2exe.c b/usr.sbin/elf2exe/elf2exe.c
new file mode 100644
index 0000000..d0959eb
--- /dev/null
+++ b/usr.sbin/elf2exe/elf2exe.c
@@ -0,0 +1,403 @@
+/*-
+ * Copyright (c) 1999 Stefan Esser
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Make an ARC firmware executable from an ELF file.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/elf64.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#define ALPHA_FMAGIC 0x184
+
+#define TOTHSZ 0x200
+
+typedef struct filehdr {
+ u_int16_t f_magic;
+ u_int16_t f_nscns;
+ u_int32_t f_timdat;
+ u_int32_t f_symptr;
+ u_int32_t f_nsyms;
+ u_int16_t f_opthdr;
+ u_int16_t f_flags;
+} FILHDR;
+#define FILHSZ 20
+
+#define ALPHA_AMAGIC 0407
+
+typedef struct aouthdr {
+ u_int16_t a_magic;
+ u_int16_t a_vstamp;
+ u_int32_t a_tsize;
+ u_int32_t a_dsize;
+ u_int32_t a_bsize;
+ u_int32_t a_entry;
+ u_int32_t a_text_start;
+ u_int32_t a_data_start;
+ u_int32_t a_bss_start;
+ u_int32_t a_gprmask;
+ u_int32_t a_cprmask[4];
+ u_int32_t a_gp_value;
+} AOUTHDR;
+#define AOUTSZ 56
+
+typedef struct scnhdr {
+ char s_name[8];
+ u_int32_t s_fill;
+ u_int32_t s_vaddr;
+ u_int32_t s_size;
+ u_int32_t s_scnptr;
+ u_int32_t s_relptr;
+ u_int32_t s_lnnoptr;
+ u_int16_t s_nreloc;
+ u_int16_t s_nlnno;
+ u_int32_t s_flags;
+} SCNHDR;
+#define SCNHSZ 40
+
+#define ROUNDUP(a,b) ((((a) -1) | ((b) -1)) +1)
+
+/*
+ * initialization subroutines
+ */
+
+int
+open_elffile(char *filename)
+{
+ int fileno = open(filename, O_RDONLY);
+
+ if (fileno < 0)
+ err(1, "%s", filename);
+ return (fileno);
+}
+
+
+Elf64_Ehdr *
+load_ehdr(int fileno)
+{
+ Elf64_Ehdr *ehdr;
+ size_t bytes = sizeof(*ehdr);
+
+ ehdr = malloc(bytes);
+ if (ehdr) {
+ lseek(fileno, 0, SEEK_SET);
+ if (read(fileno, ehdr, bytes) != bytes)
+ errx(1, "file truncated (ehdr)");
+ }
+ return (ehdr);
+}
+
+Elf64_Phdr *
+load_phdr(int fileno, Elf64_Ehdr *ehdr)
+{
+ size_t bytes = ehdr->e_phentsize * ehdr->e_phnum;
+ Elf64_Phdr *phdr = malloc(bytes);
+
+ if (phdr) {
+ lseek(fileno, ehdr->e_phoff, SEEK_SET);
+ if (read(fileno, phdr, bytes) != bytes)
+ errx(1, "file truncated (phdr)");
+ }
+ return (phdr);
+}
+
+Elf64_Shdr *
+load_shdr(int fileno, Elf64_Ehdr *ehdr)
+{
+ size_t bytes = ehdr->e_shentsize * ehdr->e_shnum;
+ Elf64_Shdr *shdr = malloc(bytes);
+
+ if (shdr) {
+ lseek(fileno, ehdr->e_shoff, SEEK_SET);
+ if (read(fileno, shdr, bytes) != bytes)
+ errx(1, "file truncated (shdr)");
+ }
+ return (shdr);
+}
+
+char *
+find_shstrtable(int fileno, int sections, Elf64_Shdr *shdr)
+{
+ size_t bytes;
+ char *shstrtab = NULL;
+ int i, shstrtabindex;
+
+ for (i = 0; shstrtab == NULL && i < sections; i++) {
+ if (shdr[i].sh_type == 3 && shdr[i].sh_flags == 0) {
+ shstrtabindex = i;
+
+ bytes = shdr[shstrtabindex].sh_size;
+ shstrtab = malloc(bytes);
+ if (shstrtab == NULL)
+ errx(1, "malloc failed");
+ lseek(fileno, shdr[shstrtabindex].sh_offset, SEEK_SET);
+ read(fileno, shstrtab, bytes);
+
+ if (strcmp (shstrtab + shdr[i].sh_name, ".shstrtab")) {
+ free(shstrtab);
+ shstrtab = NULL;
+ }
+ }
+ }
+ return (shstrtab);
+}
+
+int
+open_exefile(char *filename)
+{
+ int fileno = open(filename, O_RDWR | O_TRUNC | O_CREAT, 0666);
+
+ if (fileno < 0)
+ err(1, "%s", filename);
+ return (fileno);
+}
+
+/*
+ * utility subroutines
+ */
+
+static char *shstrtab;
+
+char *
+section_name(Elf64_Shdr *shdr, int i)
+{
+ return (shstrtab + shdr[i].sh_name);
+}
+
+int
+section_index(Elf64_Shdr *shdr, int sections, char *name)
+{
+ int i;
+
+ for (i = 0; i < sections; i++)
+ if (strcmp (name, section_name(shdr, i)) == 0)
+ return (i);
+ return (-1);
+}
+
+/* first byte of section */
+u_int64_t
+section_start(Elf64_Shdr *shdr, int sections, char *name)
+{
+ int i = section_index(shdr, sections, name);
+
+ if (i < 0)
+ return (-1);
+ return (shdr[i].sh_addr);
+}
+
+/* last byte of section */
+u_int64_t
+section_end(Elf64_Shdr *shdr, int sections, char *name)
+{
+ int i = section_index(shdr, sections, name);
+
+ if (i < 0)
+ return (-1);
+ return (shdr[i].sh_addr + shdr[i].sh_size -1);
+}
+
+/* last byte of section */
+u_int64_t
+section_size(Elf64_Shdr *shdr, int sections, char *name)
+{
+ int i = section_index(shdr, sections, name);
+
+ if (i < 0)
+ return (-1);
+
+ return (shdr[i].sh_size);
+}
+
+/* file position of section start */
+u_int64_t
+section_fpos(Elf64_Shdr *shdr, int sections, char *name)
+{
+ int i = section_index(shdr, sections, name);
+
+ if (i < 0)
+ return (-1);
+ return (shdr[i].sh_offset);
+}
+
+void
+usage(void)
+{
+ fprintf(stderr, "usage: elf2exe infile outfile\n");
+ exit(1);
+}
+
+int
+main(int argc, char** argv)
+{
+ int infd, outfd, i;
+ Elf64_Ehdr *ehdr;
+ Elf64_Phdr *phdr;
+ Elf64_Shdr *shdr;
+ FILHDR filehdr;
+ AOUTHDR aouthdr;
+ SCNHDR textscn, datascn;
+ u_int64_t textstart, textsize, textsize2, textfsize, textfpos;
+ u_int64_t datastart, datasize, datafsize, datafpos;
+ u_int64_t bssstart, bsssize;
+ u_int64_t progentry;
+ char* p;
+ int sections;
+
+ if (argc != 3)
+ usage();
+
+ infd = open_elffile(argv[1]);
+ ehdr = load_ehdr(infd);
+
+ if (ehdr == NULL)
+ errx(1, "cannot read Elf Header\n");
+
+ sections = ehdr->e_shnum;
+ progentry = ehdr->e_entry;
+
+ phdr = load_phdr(infd, ehdr);
+ shdr = load_shdr(infd, ehdr);
+ outfd = open_exefile(argv[2]);
+
+ shstrtab = find_shstrtable(infd, sections, shdr);
+
+ for (i = 1; i < sections; i++) {
+ printf("section %d (%s): "
+ "type=%x flags=0%llx "
+ "offs=%llx size=%llx addr=%llx\n",
+ i, shstrtab + shdr[i].sh_name,
+ shdr[i].sh_type, (long long)shdr[i].sh_flags,
+ (long long)shdr[i].sh_offset, (long long)shdr[i].sh_size,
+ (long long)shdr[i].sh_addr);
+ }
+
+ textstart = section_start(shdr, sections, ".text");
+ textsize = section_size(shdr, sections, ".text");
+ textsize2 = section_end(shdr, sections, ".rodata") - textstart +1;
+ if (textsize < textsize2)
+ textsize = textsize2;
+ textfsize = ROUNDUP(textsize, 512);
+ textfpos = section_fpos(shdr, sections, ".text");
+
+ datastart = section_start(shdr, sections, ".data");
+ datasize = section_start(shdr, sections, ".bss") - datastart;
+ datafsize = ROUNDUP(datasize, 512);
+ datafpos = section_fpos(shdr, sections, ".data");
+
+ bssstart = section_start(shdr, sections, ".bss");
+ bsssize = section_size(shdr, sections, ".bss");
+
+ printf("text: %llx(%llx) @%llx data: %llx(%llx) @%llx "
+ "bss: %llx(%llx)\n",
+ (long long)textstart, (long long)textsize, (long long)textfpos,
+ (long long)datastart, (long long)datasize, (long long)datafpos,
+ (long long)bssstart, (long long)bsssize);
+
+ memset(&filehdr, 0, sizeof filehdr);
+ memset(&aouthdr, 0, sizeof aouthdr);
+ memset(&textscn, 0, sizeof textscn);
+ memset(&datascn, 0, sizeof datascn);
+
+ filehdr.f_magic = ALPHA_FMAGIC;
+ filehdr.f_nscns = 2;
+ filehdr.f_timdat = time(0);
+ filehdr.f_symptr = 0;
+ filehdr.f_nsyms = 0;
+ filehdr.f_opthdr = AOUTSZ;
+ filehdr.f_flags = 0x010f;
+
+ aouthdr.a_magic = ALPHA_AMAGIC;
+ aouthdr.a_vstamp = 0x5004;
+ aouthdr.a_tsize = textfsize;
+ aouthdr.a_dsize = datafsize;
+ aouthdr.a_bsize = bsssize;
+ aouthdr.a_entry = progentry;
+ aouthdr.a_text_start = textstart;
+ aouthdr.a_data_start = datastart;
+ aouthdr.a_bss_start = bssstart;
+
+ strcpy(textscn.s_name, ".text");
+ textscn.s_fill = textsize;
+ textscn.s_vaddr = textstart;
+ textscn.s_size = textfsize;
+ textscn.s_scnptr = 0x200;
+ textscn.s_relptr = 0;
+ textscn.s_lnnoptr = 0;
+ textscn.s_nreloc = 0;
+ textscn.s_nlnno = 0;
+ textscn.s_flags = 0x20;
+
+ strcpy(datascn.s_name, ".data");
+ datascn.s_fill = datasize;
+ datascn.s_vaddr = datastart;
+ datascn.s_size = datafsize;
+ datascn.s_scnptr = 0x200 + textfsize;
+ datascn.s_relptr = 0;
+ datascn.s_lnnoptr = 0;
+ datascn.s_nreloc = 0;
+ datascn.s_nlnno = 0;
+ datascn.s_flags = 0x40;
+
+ write(outfd, &filehdr, FILHSZ);
+ write(outfd, &aouthdr, AOUTSZ);
+ write(outfd, &textscn, SCNHSZ);
+ write(outfd, &datascn, SCNHSZ);
+
+ lseek(outfd, textscn.s_scnptr, SEEK_SET);
+ p = malloc(ROUNDUP(textsize, 512));
+ if (p == NULL)
+ errx(1, "malloc failed");
+ memset(p, 0, ROUNDUP(textsize, 512));
+ lseek(infd, textfpos, SEEK_SET);
+ read(infd, p, textsize);
+ write(outfd, p, ROUNDUP(textsize, 512));
+ free(p);
+
+ lseek(outfd, datascn.s_scnptr, SEEK_SET);
+ p = malloc(ROUNDUP(datasize, 512));
+ if (p == NULL)
+ errx(1, "malloc failed");
+ memset(p, 0, ROUNDUP(datasize, 512));
+ lseek(infd, datafpos, SEEK_SET);
+ read(infd, p, datasize);
+ write(outfd, p, ROUNDUP(datasize, 512));
+ free(p);
+
+ return (0);
+}
diff --git a/usr.sbin/extattrctl/Makefile b/usr.sbin/extattrctl/Makefile
new file mode 100644
index 0000000..52142af
--- /dev/null
+++ b/usr.sbin/extattrctl/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+PROG= extattrctl
+WARNS?= 2
+MAN= extattrctl.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/extattrctl/extattrctl.8 b/usr.sbin/extattrctl/extattrctl.8
new file mode 100644
index 0000000..511e4e6
--- /dev/null
+++ b/usr.sbin/extattrctl/extattrctl.8
@@ -0,0 +1,180 @@
+.\"-
+.\" Copyright (c) 2000-2001 Robert N. M. Watson
+.\" All rights reserved.
+.\"
+.\" This software was developed by Robert Watson for the TrustedBSD Project.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.\" Developed by the TrustedBSD Project.
+.\" Support for file system extended attribute.
+.\"
+.Dd March 30, 2000
+.Dt EXTATTRCTL 8
+.Os
+.Sh NAME
+.Nm extattrctl
+.Nd manage UFS1 extended attributes
+.Sh SYNOPSIS
+.Nm
+.Cm start
+.Ar path
+.Nm
+.Cm stop
+.Ar path
+.Nm
+.Cm initattr
+.Op Fl f
+.Op Fl p Ar path
+.Ar attrsize
+.Ar attrfile
+.Nm
+.Cm showattr
+.Ar attrfile
+.Nm
+.Cm enable
+.Ar path
+.Ar attrnamespace
+.Ar attrname
+.Ar attrfile
+.Nm
+.Cm disable
+.Ar path
+.Ar attrnamespace
+.Ar attrname
+.Sh DESCRIPTION
+.Nm
+is the management utility for extended attributes over the UFS1 file system.
+.Nm
+allows the starting and stopping of extended attributes on a file system,
+as well as initialization of attribute backing files, and enabling and
+disabling of specific extended attributes on a file system.
+.Pp
+The first argument on the command line indicates the operation to be
+performed.
+Operation must be one of the following:
+.Bl -tag -width indent
+.It Cm start Ar path
+Start extended attribute support on the file system named using
+.Ar path .
+The file system must be an UFS1 file system, and the UFS_EXTATTR kernel
+option must have been enabled.
+.It Cm stop Ar path
+Stop extended attribute support on the file system named using
+.Ar path .
+Extended attribute support must previously have been started.
+.It Xo
+.Cm initattr
+.Op Fl f
+.Op Fl p Ar path
+.Ar attrsize attrfile
+.Xc
+Create and initialize a file to use as an attribute backing file.
+You must specify a maximum per-inode size for the attribute in bytes in
+.Ar attrsize ,
+as well as the file where the attribute will be stored, using
+.Ar attrfile .
+.Pp
+The
+.Fl f
+argument may be used to indicate that it is alright to overwrite an
+existing attribute backing file; otherwise, if the target file exists,
+an error will be returned.
+.Pp
+The
+.Fl p Ar path
+argument may be used to preallocate space for all attributes rather than
+relying on sparse files to conserve space.
+This has the advantage of guaranteeing that space will be available
+for attributes when they are written, preventing low disk space conditions
+from denying attribute service.
+.Pp
+This file should not exist before running
+.Cm initattr .
+.It Cm showattr Ar attrfile
+Show the attribute header values in the attribute file named by
+.Ar attrfile .
+.It Cm enable Ar path attrnamespace attrname attrfile
+Enable an attribute named
+.Ar attrname
+in the namespace
+.Ar attrnamespace
+on the file system identified using
+.Ar path ,
+and backed by initialized attribute file
+.Ar attrfile .
+Available namespaces are "user" and "system".
+The backing file must have been initialized using
+.Cm initattr
+before its first use.
+Attributes must have been started on the file system prior to the
+enabling of any attributes.
+.It Cm disable Ar path attrnamespace attrname
+Disable the attributed named
+.Ar attrname
+in namespace
+.Ar attrnamespace
+on the file system identified by
+.Ar path .
+Available namespaces are "user" and "system".
+The file system must have attributes started on it, and the attribute
+most have been enabled using
+.Cm enable .
+.El
+.Sh EXAMPLES
+.Dl extattrctl start /
+.Pp
+Start extended attributes on the root file system.
+.Pp
+.Dl extattrctl initattr 17 /.attribute/md5
+.Pp
+Create an attribute backing file in /.attribute/md5, and set the maximum
+size of each attribute to 17 bytes, with a sparse file used for storing
+the attributes.
+.Pp
+.Dl extattrctl enable / system md5 /.attribute/md5
+.Pp
+Enable an attribute named md5 on the root file system, backed from the file
+/.attribute/md5.
+.Pp
+.Dl extattrctl disable / md5
+.Pp
+Disable the attribute named md5 on the root file system.
+.Pp
+.Dl extattrctl stop /
+.Pp
+Stop extended attributes on the root file system.
+.Sh SEE ALSO
+.Xr ffs 7 ,
+.Xr getextattr 8 ,
+.Xr setextattr 8 ,
+.Xr extattr 9
+.Sh HISTORY
+Extended attribute support was developed as part of the TrustedBSD Project,
+and introduced in
+.Fx 5.0 .
+It was developed to support security extensions requiring additional labels
+to be associated with each file or directory.
+.Sh AUTHORS
+Robert N M Watson
diff --git a/usr.sbin/extattrctl/extattrctl.c b/usr.sbin/extattrctl/extattrctl.c
new file mode 100644
index 0000000..103fcd3
--- /dev/null
+++ b/usr.sbin/extattrctl/extattrctl.c
@@ -0,0 +1,259 @@
+/*-
+ * Copyright (c) 1999-2002 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * This software was developed by Robert Watson for the TrustedBSD Project.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Developed by the TrustedBSD Project.
+ * Support for file system extended attribute.
+ */
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/extattr.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+
+#include <ufs/ufs/extattr.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <libutil.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+int initattr(int argc, char *argv[]);
+int showattr(int argc, char *argv[]);
+long num_inodes_by_path(char *path);
+void usage(void);
+
+void
+usage()
+{
+
+ fprintf(stderr,
+ "usage:\n"
+ " extattrctl start path\n"
+ " extattrctl stop path\n"
+ " extattrctl initattr [-f] [-p path] attrsize attrfile\n"
+ " extattrctl showattr attrfile\n"
+ " extattrctl enable path attrnamespace attrname attrfile\n"
+ " extattrctl disable path attrnamespace attrname\n");
+ exit(-1);
+}
+
+long
+num_inodes_by_path(char *path)
+{
+ struct statfs buf;
+ int error;
+
+ error = statfs(path, &buf);
+ if (error) {
+ perror("statfs");
+ return (-1);
+ }
+
+ return (buf.f_files);
+}
+
+static const char zero_buf[8192];
+
+int
+initattr(int argc, char *argv[])
+{
+ struct ufs_extattr_fileheader uef;
+ char *fs_path = NULL;
+ int ch, i, error, flags;
+ ssize_t wlen;
+ size_t easize;
+
+ flags = O_CREAT | O_WRONLY | O_TRUNC | O_EXCL;
+ optind = 0;
+ while ((ch = getopt(argc, argv, "fp:r:w:")) != -1)
+ switch (ch) {
+ case 'f':
+ flags &= ~O_EXCL;
+ break;
+ case 'p':
+ fs_path = optarg;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 2)
+ usage();
+
+ error = 0;
+ if ((i = open(argv[1], flags, 0600)) == -1) {
+ /* unable to open file */
+ perror(argv[1]);
+ return (-1);
+ }
+ uef.uef_magic = UFS_EXTATTR_MAGIC;
+ uef.uef_version = UFS_EXTATTR_VERSION;
+ uef.uef_size = atoi(argv[0]);
+ if (write(i, &uef, sizeof(uef)) == -1)
+ error = -1;
+ else if (fs_path != NULL) {
+ easize = (sizeof uef + uef.uef_size) *
+ num_inodes_by_path(fs_path);
+ while (easize > 0) {
+ if (easize > sizeof zero_buf)
+ wlen = write(i, zero_buf, sizeof zero_buf);
+ else
+ wlen = write(i, zero_buf, easize);
+ if (wlen == -1) {
+ error = -1;
+ break;
+ }
+ easize -= wlen;
+ }
+ }
+ if (error == -1) {
+ perror(argv[1]);
+ unlink(argv[1]);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+showattr(int argc, char *argv[])
+{
+ struct ufs_extattr_fileheader uef;
+ int i, fd;
+
+ if (argc != 1)
+ usage();
+
+ fd = open(argv[0], O_RDONLY);
+ if (fd == -1) {
+ perror(argv[0]);
+ return (-1);
+ }
+
+ i = read(fd, &uef, sizeof(uef));
+ if (i == -1) {
+ perror(argv[0]);
+ return (-1);
+ }
+ if (i != sizeof(uef)) {
+ fprintf(stderr, "%s: invalid file header\n", argv[0]);
+ return (-1);
+ }
+
+ if (uef.uef_magic != UFS_EXTATTR_MAGIC) {
+ fprintf(stderr, "%s: bad magic\n", argv[0]);
+ return (-1);
+ }
+
+ printf("%s: version %d, size %d\n", argv[0], uef.uef_version,
+ uef.uef_size);
+
+ return (0);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int error = 0, attrnamespace;
+
+ if (argc < 2)
+ usage();
+
+ if (!strcmp(argv[1], "start")) {
+ if (argc != 3)
+ usage();
+ error = extattrctl(argv[2], UFS_EXTATTR_CMD_START, NULL, 0,
+ NULL);
+ if (error) {
+ perror("extattrctl start");
+ return (-1);
+ }
+ } else if (!strcmp(argv[1], "stop")) {
+ if (argc != 3)
+ usage();
+ error = extattrctl(argv[2], UFS_EXTATTR_CMD_STOP, NULL, 0,
+ NULL);
+ if (error) {
+ perror("extattrctl stop");
+ return (-1);
+ }
+ } else if (!strcmp(argv[1], "enable")) {
+ if (argc != 6)
+ usage();
+ error = extattr_string_to_namespace(argv[3], &attrnamespace);
+ if (error) {
+ perror("extattrctl enable");
+ return (-1);
+ }
+ error = extattrctl(argv[2], UFS_EXTATTR_CMD_ENABLE, argv[5],
+ attrnamespace, argv[4]);
+ if (error) {
+ perror("extattrctl enable");
+ return (-1);
+ }
+ } else if (!strcmp(argv[1], "disable")) {
+ if (argc != 5)
+ usage();
+ error = extattr_string_to_namespace(argv[3], &attrnamespace);
+ if (error) {
+ perror("extattrctl disable");
+ return (-1);
+ }
+ error = extattrctl(argv[2], UFS_EXTATTR_CMD_DISABLE, NULL,
+ attrnamespace, argv[4]);
+ if (error) {
+ perror("extattrctl disable");
+ return (-1);
+ }
+ } else if (!strcmp(argv[1], "initattr")) {
+ argc -= 2;
+ argv += 2;
+ error = initattr(argc, argv);
+ if (error)
+ return (-1);
+ } else if (!strcmp(argv[1], "showattr")) {
+ argc -= 2;
+ argv += 2;
+ error = showattr(argc, argv);
+ if (error)
+ return (-1);
+ } else
+ usage();
+
+ return (0);
+}
diff --git a/usr.sbin/faithd/Makefile b/usr.sbin/faithd/Makefile
new file mode 100644
index 0000000..29634c3
--- /dev/null
+++ b/usr.sbin/faithd/Makefile
@@ -0,0 +1,23 @@
+# Copyright (c) 1996 WIDE Project. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modifications, are permitted provided that the above copyright notice
+# and this paragraph are duplicated in all such forms and that any
+# documentation, advertising materials, and other materials related to
+# such distribution and use acknowledge that the software was developed
+# by the WIDE Project, Japan. The name of the Project may not be used to
+# endorse or promote products derived from this software without
+# specific prior written permission. THIS SOFTWARE IS PROVIDED ``AS IS''
+# AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+# LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE.
+#
+# $FreeBSD$
+
+PROG= faithd
+MAN= faithd.8
+SRCS= faithd.c tcp.c ftp.c rsh.c prefix.c
+
+#CFLAGS+= -DFAITH4
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/faithd/README b/usr.sbin/faithd/README
new file mode 100644
index 0000000..606a590
--- /dev/null
+++ b/usr.sbin/faithd/README
@@ -0,0 +1,149 @@
+Configuring FAITH IPv6-to-IPv4 TCP relay
+
+Kazu Yamamoto and Jun-ichiro itojun Hagino
+$KAME: README,v 1.8 2001/09/05 03:04:20 itojun Exp $
+$FreeBSD$
+
+Introduction
+============
+
+FAITH is a IPv6-to-IPv4 TCP relay. It performs tcp relay just as some of
+firewall-oriented gateway does, but between IPv6 and IPv4 with address
+translation.
+TCP connections has to be made from IPv6 node to IPv4 node. FAITH will
+not relay connections for the opposite direction.
+To perform relays, FAITH daemon needs to be executed on a router between
+your local IPv6 site and outside IPv4 network. The daemon needs to be
+invoked per each TCP services (TCP port number).
+
+ IPv4 node "dest" = 123.4.5.6
+ |
+ [[[[ outside IPv4 ocean ]]]]
+ |
+ node that runs FAITH-daemon (usually a router)
+ |
+ ==+=====+===+==== IPv6, or IPv4/v6 network in your site ^
+ | | | connection
+ clients IPv6 node "src" |
+
+You will have to allocate an IPv6 address prefix to map IPv4 addresses into.
+The following description uses 3ffe:0501:ffff:0000:: as example.
+Please use a prefix which belongs to your site.
+FAITH will make it possible to make a IPv6 TCP connection From IPv6 node
+"src", toward IPv4 node "dest", by specifying FAITH-mapped address
+3ffe:0501:ffff:0000::123.4.5.6
+(which is, 3ffe:0501:ffff:0000:0000:0000:7b04:0506).
+The address mapping can be performed by hand:-), by special nameserver on
+the network, or by special resolver on the source node.
+
+
+Setup
+=====
+
+The following example assumes:
+- You have assigned 3ffe:0501:ffff:0000:: as FAITH adderss prefix.
+- You are willing to provide IPv6-to IPv4 TCP relay for telnet.
+
+<<On the translating router on which faithd runs>>
+
+(1) If you have IPv6 TCP server for the "telnet" service, i.e. telnetd via
+ inet6d, disable that daemon. Comment out the line from "inet6d.conf"
+ and send the HUP signal to "inet6d".
+
+(2) Execute sysctl as root to enable FAITH support in the kernel.
+
+ # sysctl net.inet6.ip6.keepfaith=1
+
+(3) Route packets toward FAITH prefix into "faith0" interface.
+
+ # ifconfig faith0 up
+ # route add -inet6 3ffe:0501:ffff:0000:: -prefixlen 64 ::1
+ # route change -inet6 3ffe:0501:ffff:0000:: -prefixlen 64 -ifp faith0
+
+(4) Execute "faithd" by root as follows:
+
+ # faithd telnet /usr/libexec/telnetd telnetd
+
+ 1st argument is a service name you are willing to provide TCP relay.
+ (it can be specified either by number "23" or by string "telnet")
+ 2nd argument is a path name for local IPv6 TCP server. If there is a
+ connection toward the router itself, this program will be invoked.
+ 3rd and the following arguments are arguments for the local IPv6 TCP
+ server. (3rd argument is typically the program name without its path.)
+
+ More examples:
+
+ # faithd login /usr/libexec/rlogin rlogind
+ # faithd shell /usr/libexec/rshd rshd
+ # faithd ftpd /usr/libexec/ftpd ftpd -l
+ # faithd sshd
+
+If inetd(8) on your platform have special support for faithd, it is possible
+to setup faithd services via inetd(8). Consult manpage for details.
+
+
+<<Routing>>
+
+(4) Make sure that packets whose destinations match the prefix can
+reach from the IPv6 host to the translating router.
+
+<<On the IPv6 host>>
+
+There are two ways to translate IPv4 address to IPv6 address:
+ (a) Faked by DNS
+ (b) Faked by /etc/hosts.
+
+(5.a) Install "newbie" and set up FAITH mode. See kit/ports/newbie.
+
+(5.b) Add an entry into /etc/hosts so that you can resolve hostname into
+faked IPv6 addrss. For example, add the following line for www.netbsd.org:
+
+ 3ffe:0501:ffff:0000::140.160.140.252 www.netbsd.org
+
+<<On the translating router on which faithd runs.>>
+
+(6) To see if "faithd" works, watch "/var/log/daemon". Note: please
+setup "/etc/syslog.conf" so that LOG_DAEMON messages are to be stored
+in "/var/log/daemon".
+
+ <e.g.>
+ daemon.* /var/log/daemon
+
+
+Access control
+==============
+
+Since faithd implements TCP relaying service, it is critical to implement
+proper access control to cope with malicious use. Bad guy may try to
+use your relay router to circumvent access controls, or may try to
+abuse your network (like sending SPAMs from IPv4 address that belong to you).
+Install IPv6 packet filter directives that would reject traffic from
+unwanted source. If you are using inetd-based setup, you may be able to
+use access control mechanisms in inetd.
+
+
+Advanced configuration
+======================
+
+If you would like to restrict IPv4 destination for translation, you may
+want to do the following:
+
+ # route add -inet6 3ffe:0501:ffff:0000::123.0.0.0 -prefixlen 104 ::1
+ # route change -inet6 3ffe:0501:ffff:0000::123.0.0.0 -prefixlen 104 \
+ -ifp faith0
+
+By this way, you can restrict IPv4 destination to 123.0.0.0/8.
+You may also want to reject packets toward 3ffe:0501:ffff:0000::/64 which
+is not in 3ffe:0501:ffff:0000::123.0.0.0/104. This will be left as excerside
+for the reader.
+
+By doing this, you will be able to provide your IPv4 web server to outside
+IPv6 customers, without risks of unwanted open relays.
+
+ [[[[ IPv6 network outside ]]]] |
+ | | connection
+ node that runs FAITH-daemon (usually a router) v
+ |
+ ========+======== IPv4/v6 network in your site
+ | (123.0.0.0/8)
+ IPv4 web server
diff --git a/usr.sbin/faithd/faithd.8 b/usr.sbin/faithd/faithd.8
new file mode 100644
index 0000000..655f8d0
--- /dev/null
+++ b/usr.sbin/faithd/faithd.8
@@ -0,0 +1,420 @@
+.\" $KAME: faithd.8,v 1.33 2001/09/05 03:04:20 itojun Exp $
+.\"
+.\" Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the project nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd May 17, 1998
+.Dt FAITHD 8
+.Os
+.Sh NAME
+.Nm faithd
+.Nd FAITH IPv6/v4 translator daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl dp
+.Op Fl f Ar configfile
+.Ar service
+.Op Ar serverpath Op Ar serverargs
+.Sh DESCRIPTION
+.Nm
+provides IPv6-to-IPv4 TCP relay.
+.Nm
+must be used on an IPv4/v6 dual stack router.
+.Pp
+When
+.Nm
+receives
+.Tn TCPv6
+traffic,
+.Nm
+will relay the
+.Tn TCPv6
+traffic to
+.Tn TCPv4 .
+Destination for relayed
+.Tn TCPv4
+connection will be determined by the last 4 octets of the original
+.Tn IPv6
+destination.
+For example, if
+.Li 3ffe:0501:4819:ffff::
+is reserved for
+.Nm ,
+and the
+.Tn TCPv6
+destination address is
+.Li 3ffe:0501:4819:ffff::0a01:0101 ,
+the traffic will be relayed to IPv4 destination
+.Li 10.1.1.1 .
+.Pp
+To use
+.Nm
+translation service,
+an IPv6 address prefix must be reserved for mapping IPv4 addresses into.
+Kernel must be properly configured to route all the TCP connection
+toward the reserved IPv6 address prefix into the
+.Xr faith 4
+pseudo interface, by using
+.Xr route 8
+command.
+Also,
+.Xr sysctl 8
+should be used to configure
+.Dv net.inet6.ip6.keepfaith
+to
+.Dv 1 .
+.Pp
+The router must be configured to capture all the TCP traffic
+toward reserved
+.Tn IPv6
+address prefix, by using
+.Xr route 8
+and
+.Xr sysctl 8
+commands.
+.Pp
+.Nm
+needs a special name-to-address translation logic, so that
+hostnames gets resolved into special
+.Tn IPv6
+address prefix.
+For small-scale installation, use
+.Xr hosts 5 .
+For large-scale installation, it is useful to have
+a DNS server with special address translation support.
+An implementation called
+.Nm totd
+is available
+at
+.Pa http://www.vermicelli.pasta.cs.uit.no/ipv6/software.html .
+Make sure you do not propagate translated DNS records to normal DNS cloud,
+it is highly harmful.
+.Pp
+.Ss Daemon mode
+When
+.Nm
+is invoked as a standalone program,
+.Nm
+will daemonize itself.
+.Nm
+will listen to
+.Tn TCPv6
+port
+.Ar service .
+If
+.Tn TCPv6
+traffic to port
+.Ar service
+is found, it relays the connection.
+.Pp
+Since
+.Nm
+listens to TCP port
+.Ar service ,
+it is not possible to run local TCP daemons for port
+.Ar service
+on the router, using
+.Xr inetd 8
+or other standard mechanisms.
+By specifying
+.Ar serverpath
+to
+.Nm ,
+you can run local daemons on the router.
+.Nm
+will invoke local daemon at
+.Ar serverpath
+if the destination address is local interface address,
+and will perform translation to IPv4 TCP in other cases.
+You can also specify
+.Ar serverargs
+for the arguments for the local daemon.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl d
+Debugging information will be generated using
+.Xr syslog 3 .
+.It Fl f Ar configfile
+Specify a configuration file for access control.
+See below.
+.It Fl p
+Use privileged TCP port number as source port,
+for IPv4 TCP connection toward final destination.
+For relaying
+.Xr ftp 1
+and
+.Xr rlogin 1 ,
+this flag is not necessary as special program code is supplied.
+.El
+.Pp
+.Nm
+will relay both normal and out-of-band TCP data.
+It is capable of emulating TCP half close as well.
+.Nm
+includes special support for protocols used by
+.Xr ftp 1
+and
+.Xr rlogin 1 .
+When translating FTP protocol,
+.Nm
+translates network level addresses in
+.Li PORT/LPRT/EPRT
+and
+.Li PASV/LPSV/EPSV
+commands.
+For RLOGIN protocol,
+.Nm
+will relay back connection from
+.Xr rlogind 8
+on the server to
+.Xr rlogin 1
+on client.
+.Pp
+Inactive sessions will be disconnected in 30 minutes,
+to avoid stale sessions from chewing up resources.
+This may be inappropriate for some of the services
+(should this be configurable?).
+.Ss inetd mode
+When
+.Nm
+is invoked via
+.Xr inetd 8 ,
+.Nm
+will handle connection passed from standard input.
+If the connection endpoint is in the reserved IPv6 address prefix,
+.Nm
+will relay the connection.
+Otherwise,
+.Nm
+will invoke service-specific daemon like
+.Xr telnetd 8 ,
+by using the command argument passed from
+.Xr inetd 8 .
+.Pp
+.Nm
+determines operation mode by the local TCP port number,
+and enables special protocol handling whenever necessary/possible.
+For example, if
+.Nm
+is invoked via
+.Xr inetd 8
+on FTP port, it will operate as a FTP relay.
+.Pp
+The operation mode requires special support for
+.Nm
+in
+.Xr inetd 8 .
+.Ss Access control
+To prevent malicious accesses,
+.Nm
+implements a simple address-based access control.
+With
+.Pa /etc/faithd.conf
+(or
+.Ar configfile
+specified by
+.Fl f ) ,
+.Nm
+will avoid relaying unwanted traffic.
+The
+.Pa faithd.conf
+contains directives with the following format:
+.Bl -bullet
+.It
+.Ar src Ns / Ns Ar slen Cm deny Ar dst Ns / Ns Ar dlen
+.Pp
+If the source address of a query matches
+.Ar src Ns / Ns Ar slen ,
+and the translated destination address matches
+.Ar dst Ns / Ns Ar dlen ,
+deny the connection.
+.It
+.Ar src Ns / Ns Ar slen Cm permit Ar dst Ns / Ns Ar dlen
+.Pp
+If the source address of a query matches
+.Ar src Ns / Ns Ar slen ,
+and the translated destination address matches
+.Ar dst Ns / Ns Ar dlen ,
+permit the connection.
+.El
+.Pp
+The directives are evaluated in sequence,
+and the first matching entry will be effective.
+If there is no match
+(if we reach the end of the ruleset)
+the traffic will be denied.
+.Pp
+With inetd mode,
+traffic may be filtered by using access control functionality in
+.Xr inetd 8 .
+.Sh EXAMPLES
+Before invoking
+.Nm ,
+.Xr faith 4
+interface has to be configured properly.
+.Bd -literal -offset
+# sysctl net.inet6.ip6.accept_rtadv=0
+# sysctl net.inet6.ip6.forwarding=1
+# sysctl net.inet6.ip6.keepfaith=1
+# ifconfig faith0 up
+# route add -inet6 3ffe:501:4819:ffff:: -prefixlen 96 ::1
+# route change -inet6 3ffe:501:4819:ffff:: -prefixlen 96 -ifp faith0
+.Ed
+.Ss Daemon mode samples
+To translate
+.Li telnet
+service, and provide no local telnet service, invoke
+.Nm
+as follows:
+.Bd -literal -offset
+# faithd telnet
+.Ed
+.Pp
+If you would like to provide local telnet service via
+.Xr telnetd 8
+on
+.Pa /usr/libexec/telnetd ,
+use the following command line:
+.Bd -literal -offset
+# faithd telnet /usr/libexec/telnetd telnetd
+.Ed
+.Pp
+If you would like to pass extra arguments to the local daemon:
+.Bd -literal -offset
+# faithd ftp /usr/libexec/ftpd ftpd -l
+.Ed
+.Pp
+Here are some other examples.
+You may need
+.Fl p
+to translate rsh/rlogin services.
+.Bd -literal -offset
+# faithd ssh
+# faithd login /usr/libexec/rlogin rlogind
+# faithd shell /usr/libexec/rshd rshd
+.Ed
+.Pp
+However, you should be careful when translating rlogin or rsh
+connections.
+See
+.Sx SECURITY CONSIDERATIONS
+for more details.
+.Ss inetd mode samples
+Add the following lines into
+.Xr inetd.conf 5 .
+Syntax may vary depending upon your operating system.
+.Bd -literal -offset
+telnet stream tcp6/faith nowait root /usr/sbin/faithd telnetd
+ftp stream tcp6/faith nowait root /usr/sbin/faithd ftpd -l
+ssh stream tcp6/faith nowait root /usr/sbin/faithd /usr/sbin/sshd -i
+.Ed
+.Pp
+.Xr inetd 8
+will open listening sockets with enabling kernel TCP relay support.
+Whenever connection comes in,
+.Nm
+will be invoked by
+.Xr inetd 8 .
+If it the connection endpoint is in the reserved IPv6 address prefix.
+.Nm
+will relay the connection.
+Otherwise,
+.Nm
+will invoke service-specific daemon like
+.Xr telnetd 8 .
+.Ss Access control samples
+The following illustrates a simple
+.Pa faithd.conf
+setting.
+.Bd -literal -offset
+# permit anyone from 3ffe:501:ffff::/48 to use the translator,
+# to connect to the following IPv4 destinations:
+# - any location except 10.0.0.0/8 and 127.0.0.0/8.
+# Permit no other connections.
+#
+3ffe:501:ffff::/48 deny 10.0.0.0/8
+3ffe:501:ffff::/48 deny 127.0.0.0/8
+3ffe:501:ffff::/48 permit 0.0.0.0/0
+.Ed
+.Sh RETURN VALUES
+.Nm
+exits with
+.Dv EXIT_SUCCESS
+.Pq 0
+on success, and
+.Dv EXIT_FAILURE
+.Pq 1
+on error.
+.Sh SEE ALSO
+.Xr faith 4 ,
+.Xr route 8 ,
+.Xr sysctl 8
+.Rs
+.%A Jun-ichiro itojun Hagino
+.%A Kazu Yamamoto
+.%T "An IPv6-to-IPv4 transport relay translator"
+.%B RFC3142
+.%O ftp://ftp.isi.edu/in-notes/rfc3142.txt
+.%D June 2001
+.Re
+.\"
+.Sh HISTORY
+The
+.Nm
+command first appeared in WIDE Hydrangea IPv6 protocol stack kit.
+.\"
+.Pp
+IPv6 and IPsec support based on the KAME Project (http://www.kame.net/) stack
+was initially integrated into
+.Fx 4.0
+.Sh SECURITY CONSIDERATIONS
+It is very insecure to use
+.Xr rhosts 5
+and other IP-address based authentication, for connections relayed by
+.Nm
+(and any other TCP relaying services).
+.Pp
+Administrators are advised to limit accesses to
+.Nm
+using
+.Pa faithd.conf ,
+or by using IPv6 packet filters.
+It is to protect
+.Nm
+service from malicious parties and avoid theft of service/bandwidth.
+IPv6 destination address can be limited by
+carefully configuring routing entries that points to
+.Xr faith 4 ,
+using
+.Xr route 8 .
+IPv6 source address needs to be filtered by using packet filters.
+Documents listed in
+.Sx SEE ALSO
+have more discussions on this topic.
diff --git a/usr.sbin/faithd/faithd.c b/usr.sbin/faithd/faithd.c
new file mode 100644
index 0000000..856f24a
--- /dev/null
+++ b/usr.sbin/faithd/faithd.c
@@ -0,0 +1,966 @@
+/* $KAME: faithd.c,v 1.46 2002/01/24 16:40:42 sumikawa Exp $ */
+
+/*
+ * Copyright (C) 1997 and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * User level translator from IPv6 to IPv4.
+ *
+ * Usage: faithd [<port> <progpath> <arg1(progname)> <arg2> ...]
+ * e.g. faithd telnet /usr/libexec/telnetd telnetd
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#ifdef __FreeBSD__
+#include <libutil.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <termios.h>
+
+#include <net/if_types.h>
+#ifdef IFT_FAITH
+# define USE_ROUTE
+# include <net/if.h>
+# include <net/route.h>
+# include <net/if_dl.h>
+#endif
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ifaddrs.h>
+
+#ifdef FAITH4
+#include <resolv.h>
+#include <arpa/nameser.h>
+#ifndef FAITH_NS
+#define FAITH_NS "FAITH_NS"
+#endif
+#endif
+
+#include "faithd.h"
+#include "prefix.h"
+
+char *serverpath = NULL;
+char *serverarg[MAXARGV + 1];
+static char *faithdname = NULL;
+char logname[BUFSIZ];
+char procname[BUFSIZ];
+struct myaddrs {
+ struct myaddrs *next;
+ struct sockaddr *addr;
+};
+struct myaddrs *myaddrs = NULL;
+static const char *service;
+#ifdef USE_ROUTE
+static int sockfd = 0;
+#endif
+int dflag = 0;
+static int pflag = 0;
+static int inetd = 0;
+static char *configfile = NULL;
+
+int main __P((int, char **));
+static int inetd_main __P((int, char **));
+static int daemon_main __P((int, char **));
+static void play_service __P((int));
+static void play_child __P((int, struct sockaddr *));
+static int faith_prefix __P((struct sockaddr *));
+static int map6to4 __P((struct sockaddr_in6 *, struct sockaddr_in *));
+#ifdef FAITH4
+static int map4to6 __P((struct sockaddr_in *, struct sockaddr_in6 *));
+#endif
+static void sig_child __P((int));
+static void sig_terminate __P((int));
+static void start_daemon __P((void));
+static void exit_stderr __P((const char *, ...))
+ __attribute__((__format__(__printf__, 1, 2)));
+static void grab_myaddrs __P((void));
+static void free_myaddrs __P((void));
+static void update_myaddrs __P((void));
+static void usage __P((void));
+
+int
+main(int argc, char **argv)
+{
+
+ /*
+ * Initializing stuff
+ */
+
+ faithdname = strrchr(argv[0], '/');
+ if (faithdname)
+ faithdname++;
+ else
+ faithdname = argv[0];
+
+ if (strcmp(faithdname, "faithd") != 0) {
+ inetd = 1;
+ return inetd_main(argc, argv);
+ } else
+ return daemon_main(argc, argv);
+}
+
+static int
+inetd_main(int argc, char **argv)
+{
+ char path[MAXPATHLEN];
+ struct sockaddr_storage me;
+ struct sockaddr_storage from;
+ int melen, fromlen;
+ int i;
+ int error;
+ const int on = 1;
+ char sbuf[NI_MAXSERV], snum[NI_MAXSERV];
+
+ if (config_load(configfile) < 0 && configfile) {
+ exit_failure("could not load config file");
+ /*NOTREACHED*/
+ }
+
+ if (strrchr(argv[0], '/') == NULL)
+ snprintf(path, sizeof(path), "%s/%s", DEFAULT_DIR, argv[0]);
+ else
+ snprintf(path, sizeof(path), "%s", argv[0]);
+
+#ifdef USE_ROUTE
+ grab_myaddrs();
+
+ sockfd = socket(PF_ROUTE, SOCK_RAW, PF_UNSPEC);
+ if (sockfd < 0) {
+ exit_failure("socket(PF_ROUTE): %s", strerror(errno));
+ /*NOTREACHED*/
+ }
+#endif
+
+ melen = sizeof(me);
+ if (getsockname(STDIN_FILENO, (struct sockaddr *)&me, &melen) < 0) {
+ exit_failure("getsockname: %s", strerror(errno));
+ /*NOTREACHED*/
+ }
+ fromlen = sizeof(from);
+ if (getpeername(STDIN_FILENO, (struct sockaddr *)&from, &fromlen) < 0) {
+ exit_failure("getpeername: %s", strerror(errno));
+ /*NOTREACHED*/
+ }
+ if (getnameinfo((struct sockaddr *)&me, melen, NULL, 0,
+ sbuf, sizeof(sbuf), NI_NUMERICHOST) == 0)
+ service = sbuf;
+ else
+ service = DEFAULT_PORT_NAME;
+ if (getnameinfo((struct sockaddr *)&me, melen, NULL, 0,
+ snum, sizeof(snum), NI_NUMERICHOST) != 0)
+ snprintf(snum, sizeof(snum), "?");
+
+ snprintf(logname, sizeof(logname), "faithd %s", snum);
+ snprintf(procname, sizeof(procname), "accepting port %s", snum);
+ openlog(logname, LOG_PID | LOG_NOWAIT, LOG_DAEMON);
+
+ if (argc >= MAXARGV) {
+ exit_failure("too many arguments");
+ /*NOTREACHED*/
+ }
+ serverarg[0] = serverpath = path;
+ for (i = 1; i < argc; i++)
+ serverarg[i] = argv[i];
+ serverarg[i] = NULL;
+
+ error = setsockopt(STDIN_FILENO, SOL_SOCKET, SO_OOBINLINE, &on,
+ sizeof(on));
+ if (error < 0) {
+ exit_failure("setsockopt(SO_OOBINLINE): %s", strerror(errno));
+ /*NOTREACHED*/
+ }
+
+ play_child(STDIN_FILENO, (struct sockaddr *)&from);
+ exit_failure("should not reach here");
+ return 0; /*dummy!*/
+}
+
+static int
+daemon_main(int argc, char **argv)
+{
+ struct addrinfo hints, *res;
+ int s_wld, error, i, serverargc, on = 1;
+ int family = AF_INET6;
+ int c;
+#ifdef FAITH_NS
+ char *ns;
+#endif /* FAITH_NS */
+
+ while ((c = getopt(argc, argv, "df:p46")) != -1) {
+ switch (c) {
+ case 'd':
+ dflag++;
+ break;
+ case 'f':
+ configfile = optarg;
+ break;
+ case 'p':
+ pflag++;
+ break;
+#ifdef FAITH4
+ case '4':
+ family = AF_INET;
+ break;
+ case '6':
+ family = AF_INET6;
+ break;
+#endif
+ default:
+ usage();
+ /*NOTREACHED*/
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (config_load(configfile) < 0 && configfile) {
+ exit_failure("could not load config file");
+ /*NOTREACHED*/
+ }
+
+#ifdef FAITH_NS
+ if ((ns = getenv(FAITH_NS)) != NULL) {
+ struct sockaddr_storage ss;
+ struct addrinfo hints, *res;
+ char serv[NI_MAXSERV];
+
+ memset(&ss, 0, sizeof(ss));
+ memset(&hints, 0, sizeof(hints));
+ snprintf(serv, sizeof(serv), "%u", NAMESERVER_PORT);
+ hints.ai_flags = AI_NUMERICHOST;
+ if (getaddrinfo(ns, serv, &hints, &res) == 0) {
+ res_init();
+ memcpy(&_res_ext.nsaddr, res->ai_addr, res->ai_addrlen);
+ _res.nscount = 1;
+ }
+ }
+#endif /* FAITH_NS */
+
+#ifdef USE_ROUTE
+ grab_myaddrs();
+#endif
+
+ switch (argc) {
+ case 0:
+ usage();
+ /*NOTREACHED*/
+ default:
+ serverargc = argc - NUMARG;
+ if (serverargc >= MAXARGV)
+ exit_stderr("too many arguments");
+
+ serverpath = malloc(strlen(argv[NUMPRG]) + 1);
+ strcpy(serverpath, argv[NUMPRG]);
+ for (i = 0; i < serverargc; i++) {
+ serverarg[i] = malloc(strlen(argv[i + NUMARG]) + 1);
+ strcpy(serverarg[i], argv[i + NUMARG]);
+ }
+ serverarg[i] = NULL;
+ /* fall throuth */
+ case 1: /* no local service */
+ service = argv[NUMPRT];
+ break;
+ }
+
+ /*
+ * Opening wild card socket for this service.
+ */
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = family;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = 0;
+ error = getaddrinfo(NULL, service, &hints, &res);
+ if (error)
+ exit_failure("getaddrinfo: %s", gai_strerror(error));
+
+ s_wld = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+ if (s_wld == -1)
+ exit_failure("socket: %s", strerror(errno));
+
+#ifdef IPV6_FAITH
+ if (res->ai_family == AF_INET6) {
+ error = setsockopt(s_wld, IPPROTO_IPV6, IPV6_FAITH, &on, sizeof(on));
+ if (error == -1)
+ exit_failure("setsockopt(IPV6_FAITH): %s",
+ strerror(errno));
+ }
+#endif
+#ifdef FAITH4
+#ifdef IP_FAITH
+ if (res->ai_family == AF_INET) {
+ error = setsockopt(s_wld, IPPROTO_IP, IP_FAITH, &on, sizeof(on));
+ if (error == -1)
+ exit_failure("setsockopt(IP_FAITH): %s",
+ strerror(errno));
+ }
+#endif
+#endif /* FAITH4 */
+
+ error = setsockopt(s_wld, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+ if (error == -1)
+ exit_failure("setsockopt(SO_REUSEADDR): %s", strerror(errno));
+
+ error = setsockopt(s_wld, SOL_SOCKET, SO_OOBINLINE, &on, sizeof(on));
+ if (error == -1)
+ exit_failure("setsockopt(SO_OOBINLINE): %s", strerror(errno));
+
+ error = bind(s_wld, (struct sockaddr *)res->ai_addr, res->ai_addrlen);
+ if (error == -1)
+ exit_failure("bind: %s", strerror(errno));
+
+ error = listen(s_wld, 5);
+ if (error == -1)
+ exit_failure("listen: %s", strerror(errno));
+
+#ifdef USE_ROUTE
+ sockfd = socket(PF_ROUTE, SOCK_RAW, PF_UNSPEC);
+ if (sockfd < 0) {
+ exit_failure("socket(PF_ROUTE): %s", strerror(errno));
+ /*NOTREACHED*/
+ }
+#endif
+
+ /*
+ * Everything is OK.
+ */
+
+ start_daemon();
+
+ snprintf(logname, sizeof(logname), "faithd %s", service);
+ snprintf(procname, sizeof(procname), "accepting port %s", service);
+ openlog(logname, LOG_PID | LOG_NOWAIT, LOG_DAEMON);
+ syslog(LOG_INFO, "Staring faith daemon for %s port", service);
+
+ play_service(s_wld);
+ /* NOTREACHED */
+ exit(1); /*pacify gcc*/
+}
+
+static void
+play_service(int s_wld)
+{
+ struct sockaddr_storage srcaddr;
+ int len;
+ int s_src;
+ pid_t child_pid;
+ fd_set rfds;
+ int error;
+ int maxfd;
+
+ /*
+ * Wait, accept, fork, faith....
+ */
+again:
+ setproctitle("%s", procname);
+
+ FD_ZERO(&rfds);
+ FD_SET(s_wld, &rfds);
+ maxfd = s_wld;
+#ifdef USE_ROUTE
+ if (sockfd) {
+ FD_SET(sockfd, &rfds);
+ maxfd = (maxfd < sockfd) ? sockfd : maxfd;
+ }
+#endif
+
+ error = select(maxfd + 1, &rfds, NULL, NULL, NULL);
+ if (error < 0) {
+ if (errno == EINTR)
+ goto again;
+ exit_failure("select: %s", strerror(errno));
+ /*NOTREACHED*/
+ }
+
+#ifdef USE_ROUTE
+ if (FD_ISSET(sockfd, &rfds)) {
+ update_myaddrs();
+ }
+#endif
+ if (FD_ISSET(s_wld, &rfds)) {
+ len = sizeof(srcaddr);
+ s_src = accept(s_wld, (struct sockaddr *)&srcaddr,
+ &len);
+ if (s_src < 0) {
+ if (errno == ECONNABORTED)
+ goto again;
+ exit_failure("socket: %s", strerror(errno));
+ /*NOTREACHED*/
+ }
+
+ child_pid = fork();
+
+ if (child_pid == 0) {
+ /* child process */
+ close(s_wld);
+ closelog();
+ openlog(logname, LOG_PID | LOG_NOWAIT, LOG_DAEMON);
+ play_child(s_src, (struct sockaddr *)&srcaddr);
+ exit_failure("should never reach here");
+ /*NOTREACHED*/
+ } else {
+ /* parent process */
+ close(s_src);
+ if (child_pid == -1)
+ syslog(LOG_ERR, "can't fork");
+ }
+ }
+ goto again;
+}
+
+static void
+play_child(int s_src, struct sockaddr *srcaddr)
+{
+ struct sockaddr_storage dstaddr6;
+ struct sockaddr_storage dstaddr4;
+ char src[NI_MAXHOST];
+ char dst6[NI_MAXHOST];
+ char dst4[NI_MAXHOST];
+ int len = sizeof(dstaddr6);
+ int s_dst, error, hport, nresvport, on = 1;
+ struct timeval tv;
+ struct sockaddr *sa4;
+ const struct config *conf;
+
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+
+ getnameinfo(srcaddr, srcaddr->sa_len,
+ src, sizeof(src), NULL, 0, NI_NUMERICHOST);
+ syslog(LOG_INFO, "accepted a client from %s", src);
+
+ error = getsockname(s_src, (struct sockaddr *)&dstaddr6, &len);
+ if (error == -1) {
+ exit_failure("getsockname: %s", strerror(errno));
+ /*NOTREACHED*/
+ }
+
+ getnameinfo((struct sockaddr *)&dstaddr6, len,
+ dst6, sizeof(dst6), NULL, 0, NI_NUMERICHOST);
+ syslog(LOG_INFO, "the client is connecting to %s", dst6);
+
+ if (!faith_prefix((struct sockaddr *)&dstaddr6)) {
+ if (serverpath) {
+ /*
+ * Local service
+ */
+ syslog(LOG_INFO, "executing local %s", serverpath);
+ if (!inetd) {
+ dup2(s_src, 0);
+ close(s_src);
+ dup2(0, 1);
+ dup2(0, 2);
+ }
+ execv(serverpath, serverarg);
+ syslog(LOG_ERR, "execv %s: %s", serverpath,
+ strerror(errno));
+ _exit(EXIT_FAILURE);
+ } else {
+ close(s_src);
+ exit_success("no local service for %s", service);
+ }
+ }
+
+ /*
+ * Act as a translator
+ */
+
+ switch (((struct sockaddr *)&dstaddr6)->sa_family) {
+ case AF_INET6:
+ if (!map6to4((struct sockaddr_in6 *)&dstaddr6,
+ (struct sockaddr_in *)&dstaddr4)) {
+ close(s_src);
+ exit_failure("map6to4 failed");
+ /*NOTREACHED*/
+ }
+ syslog(LOG_INFO, "translating from v6 to v4");
+ break;
+#ifdef FAITH4
+ case AF_INET:
+ if (!map4to6((struct sockaddr_in *)&dstaddr6,
+ (struct sockaddr_in6 *)&dstaddr4)) {
+ close(s_src);
+ exit_failure("map4to6 failed");
+ /*NOTREACHED*/
+ }
+ syslog(LOG_INFO, "translating from v4 to v6");
+ break;
+#endif
+ default:
+ close(s_src);
+ exit_failure("family not supported");
+ /*NOTREACHED*/
+ }
+
+ sa4 = (struct sockaddr *)&dstaddr4;
+ getnameinfo(sa4, sa4->sa_len,
+ dst4, sizeof(dst4), NULL, 0, NI_NUMERICHOST);
+
+ conf = config_match(srcaddr, sa4);
+ if (!conf || !conf->permit) {
+ close(s_src);
+ if (conf) {
+ exit_failure("translation to %s not permitted for %s",
+ dst4, prefix_string(&conf->match));
+ /*NOTREACHED*/
+ } else {
+ exit_failure("translation to %s not permitted", dst4);
+ /*NOTREACHED*/
+ }
+ }
+
+ syslog(LOG_INFO, "the translator is connecting to %s", dst4);
+
+ setproctitle("port %s, %s -> %s", service, src, dst4);
+
+ if (sa4->sa_family == AF_INET6)
+ hport = ntohs(((struct sockaddr_in6 *)&dstaddr4)->sin6_port);
+ else /* AF_INET */
+ hport = ntohs(((struct sockaddr_in *)&dstaddr4)->sin_port);
+
+ switch (hport) {
+ case RLOGIN_PORT:
+ case RSH_PORT:
+ s_dst = rresvport_af(&nresvport, sa4->sa_family);
+ break;
+ default:
+ if (pflag)
+ s_dst = rresvport_af(&nresvport, sa4->sa_family);
+ else
+ s_dst = socket(sa4->sa_family, SOCK_STREAM, 0);
+ break;
+ }
+ if (s_dst < 0) {
+ exit_failure("socket: %s", strerror(errno));
+ /*NOTREACHED*/
+ }
+
+ if (conf->src.a.ss_family) {
+ if (bind(s_dst, (const struct sockaddr *)&conf->src.a,
+ conf->src.a.ss_len) < 0) {
+ exit_failure("bind: %s", strerror(errno));
+ /*NOTREACHED*/
+ }
+ }
+
+ error = setsockopt(s_dst, SOL_SOCKET, SO_OOBINLINE, &on, sizeof(on));
+ if (error < 0) {
+ exit_failure("setsockopt(SO_OOBINLINE): %s", strerror(errno));
+ /*NOTREACHED*/
+ }
+
+ error = setsockopt(s_src, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
+ if (error < 0) {
+ exit_failure("setsockopt(SO_SNDTIMEO): %s", strerror(errno));
+ /*NOTREACHED*/
+ }
+ error = setsockopt(s_dst, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
+ if (error < 0) {
+ exit_failure("setsockopt(SO_SNDTIMEO): %s", strerror(errno));
+ /*NOTREACHED*/
+ }
+
+ error = connect(s_dst, sa4, sa4->sa_len);
+ if (error < 0) {
+ exit_failure("connect: %s", strerror(errno));
+ /*NOTREACHED*/
+ }
+
+ switch (hport) {
+ case FTP_PORT:
+ ftp_relay(s_src, s_dst);
+ break;
+ case RSH_PORT:
+ syslog(LOG_WARNING,
+ "WARINNG: it is insecure to relay rsh port");
+ rsh_relay(s_src, s_dst);
+ break;
+ case RLOGIN_PORT:
+ syslog(LOG_WARNING,
+ "WARINNG: it is insecure to relay rlogin port");
+ /*FALLTHROUGH*/
+ default:
+ tcp_relay(s_src, s_dst, service);
+ break;
+ }
+
+ /* NOTREACHED */
+}
+
+/* 0: non faith, 1: faith */
+static int
+faith_prefix(struct sockaddr *dst)
+{
+#ifndef USE_ROUTE
+ int mib[4], size;
+ struct in6_addr faith_prefix;
+ struct sockaddr_in6 *dst6 = (struct sockaddr_in *)dst;
+
+ if (dst->sa_family != AF_INET6)
+ return 0;
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_INET6;
+ mib[2] = IPPROTO_IPV6;
+ mib[3] = IPV6CTL_FAITH_PREFIX;
+ size = sizeof(struct in6_addr);
+ if (sysctl(mib, 4, &faith_prefix, &size, NULL, 0) < 0) {
+ exit_failure("sysctl: %s", strerror(errno));
+ /*NOTREACHED*/
+ }
+
+ if (memcmp(dst, &faith_prefix,
+ sizeof(struct in6_addr) - sizeof(struct in_addr) == 0) {
+ return 1;
+ }
+ return 0;
+#else
+ struct myaddrs *p;
+ struct sockaddr_in6 *sin6;
+ struct sockaddr_in *sin4;
+ struct sockaddr_in6 *dst6;
+ struct sockaddr_in *dst4;
+ struct sockaddr_in dstmap;
+
+ dst6 = (struct sockaddr_in6 *)dst;
+ if (dst->sa_family == AF_INET6
+ && IN6_IS_ADDR_V4MAPPED(&dst6->sin6_addr)) {
+ /* ugly... */
+ memset(&dstmap, 0, sizeof(dstmap));
+ dstmap.sin_family = AF_INET;
+ dstmap.sin_len = sizeof(dstmap);
+ memcpy(&dstmap.sin_addr, &dst6->sin6_addr.s6_addr[12],
+ sizeof(dstmap.sin_addr));
+ dst = (struct sockaddr *)&dstmap;
+ }
+
+ dst6 = (struct sockaddr_in6 *)dst;
+ dst4 = (struct sockaddr_in *)dst;
+
+ for (p = myaddrs; p; p = p->next) {
+ sin6 = (struct sockaddr_in6 *)p->addr;
+ sin4 = (struct sockaddr_in *)p->addr;
+
+ if (p->addr->sa_len != dst->sa_len
+ || p->addr->sa_family != dst->sa_family)
+ continue;
+
+ switch (dst->sa_family) {
+ case AF_INET6:
+ if (sin6->sin6_scope_id == dst6->sin6_scope_id
+ && IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, &dst6->sin6_addr))
+ return 0;
+ break;
+ case AF_INET:
+ if (sin4->sin_addr.s_addr == dst4->sin_addr.s_addr)
+ return 0;
+ break;
+ }
+ }
+ return 1;
+#endif
+}
+
+/* 0: non faith, 1: faith */
+static int
+map6to4(struct sockaddr_in6 *dst6, struct sockaddr_in *dst4)
+{
+ memset(dst4, 0, sizeof(*dst4));
+ dst4->sin_len = sizeof(*dst4);
+ dst4->sin_family = AF_INET;
+ dst4->sin_port = dst6->sin6_port;
+ memcpy(&dst4->sin_addr, &dst6->sin6_addr.s6_addr[12],
+ sizeof(dst4->sin_addr));
+
+ if (dst4->sin_addr.s_addr == INADDR_ANY
+ || dst4->sin_addr.s_addr == INADDR_BROADCAST
+ || IN_MULTICAST(ntohl(dst4->sin_addr.s_addr)))
+ return 0;
+
+ return 1;
+}
+
+#ifdef FAITH4
+/* 0: non faith, 1: faith */
+static int
+map4to6(struct sockaddr_in *dst4, struct sockaddr_in6 *dst6)
+{
+ char host[NI_MAXHOST];
+ char serv[NI_MAXSERV];
+ struct addrinfo hints, *res;
+ int ai_errno;
+
+ if (getnameinfo((struct sockaddr *)dst4, dst4->sin_len, host, sizeof(host),
+ serv, sizeof(serv), NI_NAMEREQD|NI_NUMERICSERV) != 0)
+ return 0;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = 0;
+ hints.ai_family = AF_INET6;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = 0;
+
+ if ((ai_errno = getaddrinfo(host, serv, &hints, &res)) != 0) {
+ syslog(LOG_INFO, "%s %s: %s", host, serv,
+ gai_strerror(ai_errno));
+ return 0;
+ }
+
+ memcpy(dst6, res->ai_addr, res->ai_addrlen);
+
+ freeaddrinfo(res);
+
+ return 1;
+}
+#endif /* FAITH4 */
+
+static void
+sig_child(int sig)
+{
+ int status;
+ pid_t pid;
+
+ pid = wait3(&status, WNOHANG, (struct rusage *)0);
+ if (pid && WEXITSTATUS(status))
+ syslog(LOG_WARNING, "child %d exit status 0x%x", pid, status);
+}
+
+void
+sig_terminate(int sig)
+{
+ syslog(LOG_INFO, "Terminating faith daemon");
+ exit(EXIT_SUCCESS);
+}
+
+static void
+start_daemon(void)
+{
+#ifdef SA_NOCLDWAIT
+ struct sigaction sa;
+#endif
+
+ if (daemon(0, 0) == -1)
+ exit_stderr("daemon: %s", strerror(errno));
+
+#ifdef SA_NOCLDWAIT
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = sig_child;
+ sa.sa_flags = SA_NOCLDWAIT;
+ sigemptyset(&sa.sa_mask);
+ sigaction(SIGCHLD, &sa, (struct sigaction *)0);
+#else
+ if (signal(SIGCHLD, sig_child) == SIG_ERR) {
+ exit_failure("signal CHLD: %s", strerror(errno));
+ /*NOTREACHED*/
+ }
+#endif
+
+ if (signal(SIGTERM, sig_terminate) == SIG_ERR) {
+ exit_failure("signal TERM: %s", strerror(errno));
+ /*NOTREACHED*/
+ }
+}
+
+static void
+exit_stderr(const char *fmt, ...)
+{
+ va_list ap;
+ char buf[BUFSIZ];
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ fprintf(stderr, "%s\n", buf);
+ exit(EXIT_FAILURE);
+}
+
+void
+exit_failure(const char *fmt, ...)
+{
+ va_list ap;
+ char buf[BUFSIZ];
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ syslog(LOG_ERR, "%s", buf);
+ exit(EXIT_FAILURE);
+}
+
+void
+exit_success(const char *fmt, ...)
+{
+ va_list ap;
+ char buf[BUFSIZ];
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ syslog(LOG_INFO, "%s", buf);
+ exit(EXIT_SUCCESS);
+}
+
+#ifdef USE_ROUTE
+static void
+grab_myaddrs()
+{
+ struct ifaddrs *ifap, *ifa;
+ struct myaddrs *p;
+ struct sockaddr_in6 *sin6;
+
+ if (getifaddrs(&ifap) != 0) {
+ exit_failure("getifaddrs");
+ /*NOTREACHED*/
+ }
+
+ for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+ switch (ifa->ifa_addr->sa_family) {
+ case AF_INET:
+ case AF_INET6:
+ break;
+ default:
+ continue;
+ }
+
+ p = (struct myaddrs *)malloc(sizeof(struct myaddrs) +
+ ifa->ifa_addr->sa_len);
+ if (!p) {
+ exit_failure("not enough core");
+ /*NOTREACHED*/
+ }
+ memcpy(p + 1, ifa->ifa_addr, ifa->ifa_addr->sa_len);
+ p->next = myaddrs;
+ p->addr = (struct sockaddr *)(p + 1);
+#ifdef __KAME__
+ if (ifa->ifa_addr->sa_family == AF_INET6) {
+ sin6 = (struct sockaddr_in6 *)p->addr;
+ if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)
+ || IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr)) {
+ sin6->sin6_scope_id =
+ ntohs(*(u_int16_t *)&sin6->sin6_addr.s6_addr[2]);
+ sin6->sin6_addr.s6_addr[2] = 0;
+ sin6->sin6_addr.s6_addr[3] = 0;
+ }
+ }
+#endif
+ myaddrs = p;
+ if (dflag) {
+ char hbuf[NI_MAXHOST];
+ getnameinfo(p->addr, p->addr->sa_len,
+ hbuf, sizeof(hbuf), NULL, 0,
+ NI_NUMERICHOST);
+ syslog(LOG_INFO, "my interface: %s %s", hbuf,
+ ifa->ifa_name);
+ }
+ }
+
+ freeifaddrs(ifap);
+}
+
+static void
+free_myaddrs()
+{
+ struct myaddrs *p, *q;
+
+ p = myaddrs;
+ while (p) {
+ q = p->next;
+ free(p);
+ p = q;
+ }
+ myaddrs = NULL;
+}
+
+static void
+update_myaddrs()
+{
+ char msg[BUFSIZ];
+ int len;
+ struct rt_msghdr *rtm;
+
+ len = read(sockfd, msg, sizeof(msg));
+ if (len < 0) {
+ syslog(LOG_ERR, "read(PF_ROUTE) failed");
+ return;
+ }
+ rtm = (struct rt_msghdr *)msg;
+ if (len < 4 || len < rtm->rtm_msglen) {
+ syslog(LOG_ERR, "read(PF_ROUTE) short read");
+ return;
+ }
+ if (rtm->rtm_version != RTM_VERSION) {
+ syslog(LOG_ERR, "routing socket version mismatch");
+ close(sockfd);
+ sockfd = 0;
+ return;
+ }
+ switch (rtm->rtm_type) {
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ case RTM_IFINFO:
+ break;
+ default:
+ return;
+ }
+ /* XXX more filters here? */
+
+ syslog(LOG_INFO, "update interface address list");
+ free_myaddrs();
+ grab_myaddrs();
+}
+#endif /*USE_ROUTE*/
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: %s [-dp] [-f conf] service [serverpath [serverargs]]\n",
+ faithdname);
+ exit(0);
+}
diff --git a/usr.sbin/faithd/faithd.h b/usr.sbin/faithd/faithd.h
new file mode 100644
index 0000000..0efb2b8
--- /dev/null
+++ b/usr.sbin/faithd/faithd.h
@@ -0,0 +1,72 @@
+/* $KAME: faithd.h,v 1.8 2001/09/05 03:04:21 itojun Exp $ */
+
+/*
+ * Copyright (C) 1997 and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+extern char logname[];
+extern int dflag;
+
+extern void tcp_relay __P((int, int, const char *));
+extern void ftp_relay __P((int, int));
+extern int ftp_active __P((int, int, int *, int *));
+extern int ftp_passive __P((int, int, int *, int *));
+extern void rsh_relay __P((int, int));
+extern void rsh_dual_relay __P((int, int));
+extern void exit_success __P((const char *, ...))
+ __attribute__((__format__(__printf__, 1, 2)));
+extern void exit_failure __P((const char *, ...))
+ __attribute__((__format__(__printf__, 1, 2)));
+
+#define DEFAULT_PORT_NAME "telnet"
+#define DEFAULT_DIR "/usr/libexec"
+#define DEFAULT_NAME "telnetd"
+#define DEFAULT_PATH (DEFAULT_DIR "/" DEFAULT_NAME)
+
+#define FTP_PORT 21
+#define RLOGIN_PORT 513
+#define RSH_PORT 514
+
+#define RETURN_SUCCESS 0
+#define RETURN_FAILURE 1
+
+#define YES 1
+#define NO 0
+
+#define MSS 2048
+#define MAXARGV 20
+
+#define NUMPRT 0
+#define NUMPRG 1
+#define NUMARG 2
+
+#define UC(b) (((int)b)&0xff)
+
+#define FAITH_TIMEOUT (30 * 60) /*second*/
diff --git a/usr.sbin/faithd/ftp.c b/usr.sbin/faithd/ftp.c
new file mode 100644
index 0000000..5664681
--- /dev/null
+++ b/usr.sbin/faithd/ftp.c
@@ -0,0 +1,1125 @@
+/* $KAME: ftp.c,v 1.11 2001/07/02 14:36:49 itojun Exp $ */
+
+/*
+ * Copyright (C) 1997 and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include "faithd.h"
+
+static char rbuf[MSS];
+static char sbuf[MSS];
+static int passivemode = 0;
+static int wport4 = -1; /* listen() to active */
+static int wport6 = -1; /* listen() to passive */
+static int port4 = -1; /* active: inbound passive: outbound */
+static int port6 = -1; /* active: outbound passive: inbound */
+static struct sockaddr_storage data4; /* server data address */
+static struct sockaddr_storage data6; /* client data address */
+static int epsvall = 0;
+
+#ifdef FAITH4
+enum state { NONE, LPRT, EPRT, PORT, LPSV, EPSV, PASV };
+#else
+enum state { NONE, LPRT, EPRT, LPSV, EPSV };
+#endif
+
+static int ftp_activeconn __P((void));
+static int ftp_passiveconn __P((void));
+static int ftp_copy __P((int, int));
+static int ftp_copyresult __P((int, int, enum state));
+static int ftp_copycommand __P((int, int, enum state *));
+
+void
+ftp_relay(int ctl6, int ctl4)
+{
+ fd_set readfds;
+ int error;
+ enum state state = NONE;
+ struct timeval tv;
+
+ syslog(LOG_INFO, "starting ftp control connection");
+
+ for (;;) {
+ FD_ZERO(&readfds);
+ FD_SET(ctl4, &readfds);
+ FD_SET(ctl6, &readfds);
+ if (0 <= port4)
+ FD_SET(port4, &readfds);
+ if (0 <= port6)
+ FD_SET(port6, &readfds);
+#if 0
+ if (0 <= wport4)
+ FD_SET(wport4, &readfds);
+ if (0 <= wport6)
+ FD_SET(wport6, &readfds);
+#endif
+ tv.tv_sec = FAITH_TIMEOUT;
+ tv.tv_usec = 0;
+
+ error = select(256, &readfds, NULL, NULL, &tv);
+ if (error == -1)
+ exit_failure("select: %s", strerror(errno));
+ else if (error == 0)
+ exit_failure("connection timeout");
+
+ /*
+ * The order of the following checks does (slightly) matter.
+ * It is important to visit all checks (do not use "continue"),
+ * otherwise some of the pipe may become full and we cannot
+ * relay correctly.
+ */
+ if (FD_ISSET(ctl6, &readfds)) {
+ /*
+ * copy control connection from the client.
+ * command translation is necessary.
+ */
+ error = ftp_copycommand(ctl6, ctl4, &state);
+
+ if (error < 0)
+ goto bad;
+ else if (error == 0) {
+ close(ctl4);
+ close(ctl6);
+ exit_success("terminating ftp control connection");
+ }
+ }
+ if (FD_ISSET(ctl4, &readfds)) {
+ /*
+ * copy control connection from the server
+ * translation of result code is necessary.
+ */
+ error = ftp_copyresult(ctl4, ctl6, state);
+
+ if (error < 0)
+ goto bad;
+ else if (error == 0) {
+ close(ctl4);
+ close(ctl6);
+ exit_success("terminating ftp control connection");
+ }
+ }
+ if (0 <= port4 && 0 <= port6 && FD_ISSET(port4, &readfds)) {
+ /*
+ * copy data connection.
+ * no special treatment necessary.
+ */
+ if (FD_ISSET(port4, &readfds))
+ error = ftp_copy(port4, port6);
+ switch (error) {
+ case -1:
+ goto bad;
+ case 0:
+ close(port4);
+ close(port6);
+ port4 = port6 = -1;
+ syslog(LOG_INFO, "terminating data connection");
+ break;
+ default:
+ break;
+ }
+ }
+ if (0 <= port4 && 0 <= port6 && FD_ISSET(port6, &readfds)) {
+ /*
+ * copy data connection.
+ * no special treatment necessary.
+ */
+ if (FD_ISSET(port6, &readfds))
+ error = ftp_copy(port6, port4);
+ switch (error) {
+ case -1:
+ goto bad;
+ case 0:
+ close(port4);
+ close(port6);
+ port4 = port6 = -1;
+ syslog(LOG_INFO, "terminating data connection");
+ break;
+ default:
+ break;
+ }
+ }
+#if 0
+ if (wport4 && FD_ISSET(wport4, &readfds)) {
+ /*
+ * establish active data connection from the server.
+ */
+ ftp_activeconn();
+ }
+ if (wport6 && FD_ISSET(wport6, &readfds)) {
+ /*
+ * establish passive data connection from the client.
+ */
+ ftp_passiveconn();
+ }
+#endif
+ }
+
+ bad:
+ exit_failure("%s", strerror(errno));
+}
+
+static int
+ftp_activeconn()
+{
+ int n;
+ int error;
+ fd_set set;
+ struct timeval timeout;
+ struct sockaddr *sa;
+
+ /* get active connection from server */
+ FD_ZERO(&set);
+ FD_SET(wport4, &set);
+ timeout.tv_sec = 120;
+ timeout.tv_usec = -1;
+ n = sizeof(data4);
+ if (select(wport4 + 1, &set, NULL, NULL, &timeout) == 0
+ || (port4 = accept(wport4, (struct sockaddr *)&data4, &n)) < 0) {
+ close(wport4);
+ wport4 = -1;
+ syslog(LOG_INFO, "active mode data connection failed");
+ return -1;
+ }
+
+ /* ask active connection to client */
+ sa = (struct sockaddr *)&data6;
+ port6 = socket(sa->sa_family, SOCK_STREAM, 0);
+ if (port6 == -1) {
+ close(port4);
+ close(wport4);
+ port4 = wport4 = -1;
+ syslog(LOG_INFO, "active mode data connection failed");
+ return -1;
+ }
+ error = connect(port6, sa, sa->sa_len);
+ if (error < 0) {
+ close(port6);
+ close(port4);
+ close(wport4);
+ port6 = port4 = wport4 = -1;
+ syslog(LOG_INFO, "active mode data connection failed");
+ return -1;
+ }
+
+ syslog(LOG_INFO, "active mode data connection established");
+ return 0;
+}
+
+static int
+ftp_passiveconn()
+{
+ int n;
+ int error;
+ fd_set set;
+ struct timeval timeout;
+ struct sockaddr *sa;
+
+ /* get passive connection from client */
+ FD_ZERO(&set);
+ FD_SET(wport6, &set);
+ timeout.tv_sec = 120;
+ timeout.tv_usec = 0;
+ n = sizeof(data6);
+ if (select(wport6 + 1, &set, NULL, NULL, &timeout) == 0
+ || (port6 = accept(wport6, (struct sockaddr *)&data6, &n)) < 0) {
+ close(wport6);
+ wport6 = -1;
+ syslog(LOG_INFO, "passive mode data connection failed");
+ return -1;
+ }
+
+ /* ask passive connection to server */
+ sa = (struct sockaddr *)&data4;
+ port4 = socket(sa->sa_family, SOCK_STREAM, 0);
+ if (port4 == -1) {
+ close(wport6);
+ close(port6);
+ wport6 = port6 = -1;
+ syslog(LOG_INFO, "passive mode data connection failed");
+ return -1;
+ }
+ error = connect(port4, sa, sa->sa_len);
+ if (error < 0) {
+ close(wport6);
+ close(port4);
+ close(port6);
+ wport6 = port4 = port6 = -1;
+ syslog(LOG_INFO, "passive mode data connection failed");
+ return -1;
+ }
+
+ syslog(LOG_INFO, "passive mode data connection established");
+ return 0;
+}
+
+static int
+ftp_copy(int src, int dst)
+{
+ int error, atmark;
+ int n;
+
+ /* OOB data handling */
+ error = ioctl(src, SIOCATMARK, &atmark);
+ if (error != -1 && atmark == 1) {
+ n = read(src, rbuf, 1);
+ if (n == -1)
+ goto bad;
+ send(dst, rbuf, n, MSG_OOB);
+#if 0
+ n = read(src, rbuf, sizeof(rbuf));
+ if (n == -1)
+ goto bad;
+ write(dst, rbuf, n);
+ return n;
+#endif
+ }
+
+ n = read(src, rbuf, sizeof(rbuf));
+ switch (n) {
+ case -1:
+ case 0:
+ return n;
+ default:
+ write(dst, rbuf, n);
+ return n;
+ }
+
+ bad:
+ exit_failure("%s", strerror(errno));
+ /*NOTREACHED*/
+ return 0; /* to make gcc happy */
+}
+
+static int
+ftp_copyresult(int src, int dst, enum state state)
+{
+ int error, atmark;
+ int n;
+ char *param;
+ int code;
+
+ /* OOB data handling */
+ error = ioctl(src, SIOCATMARK, &atmark);
+ if (error != -1 && atmark == 1) {
+ n = read(src, rbuf, 1);
+ if (n == -1)
+ goto bad;
+ send(dst, rbuf, n, MSG_OOB);
+#if 0
+ n = read(src, rbuf, sizeof(rbuf));
+ if (n == -1)
+ goto bad;
+ write(dst, rbuf, n);
+ return n;
+#endif
+ }
+
+ n = read(src, rbuf, sizeof(rbuf));
+ if (n <= 0)
+ return n;
+ rbuf[n] = '\0';
+
+ /*
+ * parse argument
+ */
+ {
+ char *p;
+ int i;
+
+ p = rbuf;
+ for (i = 0; i < 3; i++) {
+ if (!isdigit(*p)) {
+ /* invalid reply */
+ write(dst, rbuf, n);
+ return n;
+ }
+ p++;
+ }
+ if (!isspace(*p)) {
+ /* invalid reply */
+ write(dst, rbuf, n);
+ return n;
+ }
+ code = atoi(rbuf);
+ param = p;
+ /* param points to first non-command token, if any */
+ while (*param && isspace(*param))
+ param++;
+ if (!*param)
+ param = NULL;
+ }
+
+ switch (state) {
+ case NONE:
+ if (!passivemode && rbuf[0] == '1') {
+ if (ftp_activeconn() < 0) {
+ n = snprintf(rbuf, sizeof(rbuf),
+ "425 Cannot open data connetion\r\n");
+ }
+ }
+ return n > 0 ? write(dst, rbuf, n) : n;
+ case LPRT:
+ case EPRT:
+ /* expecting "200 PORT command successful." */
+ if (code == 200) {
+ char *p;
+
+ p = strstr(rbuf, "PORT");
+ if (p) {
+ p[0] = (state == LPRT) ? 'L' : 'E';
+ p[1] = 'P';
+ }
+ } else {
+ close(wport4);
+ wport4 = -1;
+ }
+ write(dst, rbuf, n);
+ return n;
+#ifdef FAITH4
+ case PORT:
+ /* expecting "200 EPRT command successful." */
+ if (code == 200) {
+ char *p;
+
+ p = strstr(rbuf, "EPRT");
+ if (p) {
+ p[0] = 'P';
+ p[1] = 'O';
+ }
+ } else {
+ close(wport4);
+ wport4 = -1;
+ }
+ write(dst, rbuf, n);
+ return n;
+#endif
+ case LPSV:
+ case EPSV:
+ /*
+ * expecting "227 Entering Passive Mode (x,x,x,x,x,x,x)"
+ * (in some cases result comes without paren)
+ */
+ if (code != 227) {
+passivefail0:
+ close(wport6);
+ wport6 = -1;
+ write(dst, rbuf, n);
+ return n;
+ }
+
+ {
+ unsigned int ho[4], po[2];
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ u_short port;
+ char *p;
+
+ /*
+ * PASV result -> LPSV/EPSV result
+ */
+ p = param;
+ while (*p && *p != '(' && !isdigit(*p)) /*)*/
+ p++;
+ if (!*p)
+ goto passivefail0; /*XXX*/
+ if (*p == '(') /*)*/
+ p++;
+ n = sscanf(p, "%u,%u,%u,%u,%u,%u",
+ &ho[0], &ho[1], &ho[2], &ho[3], &po[0], &po[1]);
+ if (n != 6)
+ goto passivefail0; /*XXX*/
+
+ /* keep PORT parameter */
+ memset(&data4, 0, sizeof(data4));
+ sin = (struct sockaddr_in *)&data4;
+ sin->sin_len = sizeof(*sin);
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = 0;
+ for (n = 0; n < 4; n++) {
+ sin->sin_addr.s_addr |=
+ htonl((ho[n] & 0xff) << ((3 - n) * 8));
+ }
+ sin->sin_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff));
+
+ /* get ready for passive data connection */
+ memset(&data6, 0, sizeof(data6));
+ sin6 = (struct sockaddr_in6 *)&data6;
+ sin6->sin6_len = sizeof(*sin6);
+ sin6->sin6_family = AF_INET6;
+ wport6 = socket(sin6->sin6_family, SOCK_STREAM, 0);
+ if (wport6 == -1) {
+passivefail:
+ n = snprintf(sbuf, sizeof(sbuf),
+ "500 could not translate from PASV\r\n");
+ return n > 0 ? write(src, sbuf, n) : n;
+ }
+#ifdef IPV6_FAITH
+ {
+ int on = 1;
+ error = setsockopt(wport6, IPPROTO_IPV6, IPV6_FAITH,
+ &on, sizeof(on));
+ if (error == -1)
+ exit_failure("setsockopt(IPV6_FAITH): %s", strerror(errno));
+ }
+#endif
+ error = bind(wport6, (struct sockaddr *)sin6, sin6->sin6_len);
+ if (error == -1) {
+ close(wport6);
+ wport6 = -1;
+ goto passivefail;
+ }
+ error = listen(wport6, 1);
+ if (error == -1) {
+ close(wport6);
+ wport6 = -1;
+ goto passivefail;
+ }
+
+ /* transmit LPSV or EPSV */
+ /*
+ * addr from dst, port from wport6
+ */
+ n = sizeof(data6);
+ error = getsockname(wport6, (struct sockaddr *)&data6, &n);
+ if (error == -1) {
+ close(wport6);
+ wport6 = -1;
+ goto passivefail;
+ }
+ sin6 = (struct sockaddr_in6 *)&data6;
+ port = sin6->sin6_port;
+
+ n = sizeof(data6);
+ error = getsockname(dst, (struct sockaddr *)&data6, &n);
+ if (error == -1) {
+ close(wport6);
+ wport6 = -1;
+ goto passivefail;
+ }
+ sin6 = (struct sockaddr_in6 *)&data6;
+ sin6->sin6_port = port;
+
+ if (state == LPSV) {
+ char *a, *p;
+
+ a = (char *)&sin6->sin6_addr;
+ p = (char *)&sin6->sin6_port;
+ n = snprintf(sbuf, sizeof(sbuf),
+"228 Entering Long Passive Mode (%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)\r\n",
+ 6, 16, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
+ UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]),
+ UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]),
+ UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]),
+ 2, UC(p[0]), UC(p[1]));
+ if (n > 0)
+ n = write(dst, sbuf, n);
+ passivemode = 1;
+ return n;
+ } else {
+ n = snprintf(sbuf, sizeof(sbuf),
+"229 Entering Extended Passive Mode (|||%d|)\r\n",
+ ntohs(sin6->sin6_port));
+ if (n > 0)
+ n = write(dst, sbuf, n);
+ passivemode = 1;
+ return n;
+ }
+ }
+#ifdef FAITH4
+ case PASV:
+ /* expecting "229 Entering Extended Passive Mode (|||x|)" */
+ if (code != 229) {
+passivefail1:
+ close(wport6);
+ wport6 = -1;
+ write(dst, rbuf, n);
+ return n;
+ }
+
+ {
+ u_short port;
+ char *p;
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+
+ /*
+ * EPSV result -> PORT result
+ */
+ p = param;
+ while (*p && *p != '(') /*)*/
+ p++;
+ if (!*p)
+ goto passivefail1; /*XXX*/
+ p++;
+ n = sscanf(p, "|||%hu|", &port);
+ if (n != 1)
+ goto passivefail1; /*XXX*/
+
+ /* keep EPRT parameter */
+ n = sizeof(data4);
+ error = getpeername(src, (struct sockaddr *)&data4, &n);
+ if (error == -1)
+ goto passivefail1; /*XXX*/
+ sin6 = (struct sockaddr_in6 *)&data4;
+ sin6->sin6_port = htons(port);
+
+ /* get ready for passive data connection */
+ memset(&data6, 0, sizeof(data6));
+ sin = (struct sockaddr_in *)&data6;
+ sin->sin_len = sizeof(*sin);
+ sin->sin_family = AF_INET;
+ wport6 = socket(sin->sin_family, SOCK_STREAM, 0);
+ if (wport6 == -1) {
+passivefail2:
+ n = snprintf(sbuf, sizeof(sbuf),
+ "500 could not translate from EPSV\r\n");
+ return n > 0 ? write(src, sbuf, n) : n;
+ }
+#ifdef IP_FAITH
+ {
+ int on = 1;
+ error = setsockopt(wport6, IPPROTO_IP, IP_FAITH,
+ &on, sizeof(on));
+ if (error == -1)
+ exit_error("setsockopt(IP_FAITH): %s", strerror(errno));
+ }
+#endif
+ error = bind(wport6, (struct sockaddr *)sin, sin->sin_len);
+ if (error == -1) {
+ close(wport6);
+ wport6 = -1;
+ goto passivefail2;
+ }
+ error = listen(wport6, 1);
+ if (error == -1) {
+ close(wport6);
+ wport6 = -1;
+ goto passivefail2;
+ }
+
+ /* transmit PORT */
+ /*
+ * addr from dst, port from wport6
+ */
+ n = sizeof(data6);
+ error = getsockname(wport6, (struct sockaddr *)&data6, &n);
+ if (error == -1) {
+ close(wport6);
+ wport6 = -1;
+ goto passivefail2;
+ }
+ sin = (struct sockaddr_in *)&data6;
+ port = sin->sin_port;
+
+ n = sizeof(data6);
+ error = getsockname(dst, (struct sockaddr *)&data6, &n);
+ if (error == -1) {
+ close(wport6);
+ wport6 = -1;
+ goto passivefail2;
+ }
+ sin = (struct sockaddr_in *)&data6;
+ sin->sin_port = port;
+
+ {
+ char *a, *p;
+
+ a = (char *)&sin->sin_addr;
+ p = (char *)&sin->sin_port;
+ n = snprintf(sbuf, sizeof(sbuf),
+"227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n",
+ UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
+ UC(p[0]), UC(p[1]));
+ if (n > 0)
+ n = write(dst, sbuf, n);
+ passivemode = 1;
+ return n;
+ }
+ }
+#endif /* FAITH4 */
+ }
+
+ bad:
+ exit_failure("%s", strerror(errno));
+ /*NOTREACHED*/
+ return 0; /* to make gcc happy */
+}
+
+static int
+ftp_copycommand(int src, int dst, enum state *state)
+{
+ int error, atmark;
+ int n;
+ unsigned int af, hal, ho[16], pal, po[2];
+ char *a, *p;
+ char cmd[5], *param;
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ enum state nstate;
+ char ch;
+
+ /* OOB data handling */
+ error = ioctl(src, SIOCATMARK, &atmark);
+ if (error != -1 && atmark == 1) {
+ n = read(src, rbuf, 1);
+ if (n == -1)
+ goto bad;
+ send(dst, rbuf, n, MSG_OOB);
+#if 0
+ n = read(src, rbuf, sizeof(rbuf));
+ if (n == -1)
+ goto bad;
+ write(dst, rbuf, n);
+ return n;
+#endif
+ }
+
+ n = read(src, rbuf, sizeof(rbuf));
+ if (n <= 0)
+ return n;
+ rbuf[n] = '\0';
+
+ if (n < 4) {
+ write(dst, rbuf, n);
+ return n;
+ }
+
+ /*
+ * parse argument
+ */
+ {
+ char *p, *q;
+ int i;
+
+ p = rbuf;
+ q = cmd;
+ for (i = 0; i < 4; i++) {
+ if (!isalpha(*p)) {
+ /* invalid command */
+ write(dst, rbuf, n);
+ return n;
+ }
+ *q++ = islower(*p) ? toupper(*p) : *p;
+ p++;
+ }
+ if (!isspace(*p)) {
+ /* invalid command */
+ write(dst, rbuf, n);
+ return n;
+ }
+ *q = '\0';
+ param = p;
+ /* param points to first non-command token, if any */
+ while (*param && isspace(*param))
+ param++;
+ if (!*param)
+ param = NULL;
+ }
+
+ *state = NONE;
+
+ if (strcmp(cmd, "LPRT") == 0 && param) {
+ /*
+ * LPRT -> PORT
+ */
+ nstate = LPRT;
+
+ close(wport4);
+ close(wport6);
+ close(port4);
+ close(port6);
+ wport4 = wport6 = port4 = port6 = -1;
+
+ if (epsvall) {
+ n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n",
+ cmd);
+ return n > 0 ? write(src, sbuf, n) : n;
+ }
+
+ n = sscanf(param,
+"%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u",
+ &af, &hal, &ho[0], &ho[1], &ho[2], &ho[3],
+ &ho[4], &ho[5], &ho[6], &ho[7],
+ &ho[8], &ho[9], &ho[10], &ho[11],
+ &ho[12], &ho[13], &ho[14], &ho[15],
+ &pal, &po[0], &po[1]);
+ if (n != 21 || af != 6 || hal != 16|| pal != 2) {
+ n = snprintf(sbuf, sizeof(sbuf),
+ "501 illegal parameter to LPRT\r\n");
+ return n > 0 ? write(src, sbuf, n) : n;
+ }
+
+ /* keep LPRT parameter */
+ memset(&data6, 0, sizeof(data6));
+ sin6 = (struct sockaddr_in6 *)&data6;
+ sin6->sin6_len = sizeof(*sin6);
+ sin6->sin6_family = AF_INET6;
+ for (n = 0; n < 16; n++)
+ sin6->sin6_addr.s6_addr[n] = ho[n];
+ sin6->sin6_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff));
+
+sendport:
+ /* get ready for active data connection */
+ n = sizeof(data4);
+ error = getsockname(dst, (struct sockaddr *)&data4, &n);
+ if (error == -1) {
+lprtfail:
+ n = snprintf(sbuf, sizeof(sbuf),
+ "500 could not translate to PORT\r\n");
+ return n > 0 ? write(src, sbuf, n) : n;
+ }
+ if (((struct sockaddr *)&data4)->sa_family != AF_INET)
+ goto lprtfail;
+ sin = (struct sockaddr_in *)&data4;
+ sin->sin_port = 0;
+ wport4 = socket(sin->sin_family, SOCK_STREAM, 0);
+ if (wport4 == -1)
+ goto lprtfail;
+ error = bind(wport4, (struct sockaddr *)sin, sin->sin_len);
+ if (error == -1) {
+ close(wport4);
+ wport4 = -1;
+ goto lprtfail;
+ }
+ error = listen(wport4, 1);
+ if (error == -1) {
+ close(wport4);
+ wport4 = -1;
+ goto lprtfail;
+ }
+
+ /* transmit PORT */
+ n = sizeof(data4);
+ error = getsockname(wport4, (struct sockaddr *)&data4, &n);
+ if (error == -1) {
+ close(wport4);
+ wport4 = -1;
+ goto lprtfail;
+ }
+ if (((struct sockaddr *)&data4)->sa_family != AF_INET) {
+ close(wport4);
+ wport4 = -1;
+ goto lprtfail;
+ }
+ sin = (struct sockaddr_in *)&data4;
+ a = (char *)&sin->sin_addr;
+ p = (char *)&sin->sin_port;
+ n = snprintf(sbuf, sizeof(sbuf), "PORT %d,%d,%d,%d,%d,%d\r\n",
+ UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
+ UC(p[0]), UC(p[1]));
+ if (n > 0)
+ n = write(dst, sbuf, n);
+ *state = nstate;
+ passivemode = 0;
+ return n;
+ } else if (strcmp(cmd, "EPRT") == 0 && param) {
+ /*
+ * EPRT -> PORT
+ */
+ char *afp, *hostp, *portp;
+ struct addrinfo hints, *res;
+
+ nstate = EPRT;
+
+ close(wport4);
+ close(wport6);
+ close(port4);
+ close(port6);
+ wport4 = wport6 = port4 = port6 = -1;
+
+ if (epsvall) {
+ n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n",
+ cmd);
+ return n > 0 ? write(src, sbuf, n) : n;
+ }
+
+ p = param;
+ ch = *p++; /* boundary character */
+ afp = p;
+ while (*p && *p != ch)
+ p++;
+ if (!*p) {
+eprtparamfail:
+ n = snprintf(sbuf, sizeof(sbuf),
+ "501 illegal parameter to EPRT\r\n");
+ return n > 0 ? write(src, sbuf, n) : n;
+ }
+ *p++ = '\0';
+ hostp = p;
+ while (*p && *p != ch)
+ p++;
+ if (!*p)
+ goto eprtparamfail;
+ *p++ = '\0';
+ portp = p;
+ while (*p && *p != ch)
+ p++;
+ if (!*p)
+ goto eprtparamfail;
+ *p++ = '\0';
+
+ n = sscanf(afp, "%d", &af);
+ if (n != 1 || af != 2) {
+ n = snprintf(sbuf, sizeof(sbuf),
+ "501 unsupported address family to EPRT\r\n");
+ return n > 0 ? write(src, sbuf, n) : n;
+ }
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ error = getaddrinfo(hostp, portp, &hints, &res);
+ if (error) {
+ n = snprintf(sbuf, sizeof(sbuf),
+ "501 EPRT: %s\r\n", gai_strerror(error));
+ return n > 0 ? write(src, sbuf, n) : n;
+ }
+ if (res->ai_next) {
+ n = snprintf(sbuf, sizeof(sbuf),
+ "501 EPRT: %s resolved to multiple addresses\r\n", hostp);
+ return n > 0 ? write(src, sbuf, n) : n;
+ }
+
+ memcpy(&data6, res->ai_addr, res->ai_addrlen);
+
+ goto sendport;
+ } else if (strcmp(cmd, "LPSV") == 0 && !param) {
+ /*
+ * LPSV -> PASV
+ */
+ nstate = LPSV;
+
+ close(wport4);
+ close(wport6);
+ close(port4);
+ close(port6);
+ wport4 = wport6 = port4 = port6 = -1;
+
+ if (epsvall) {
+ n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n",
+ cmd);
+ return n > 0 ? write(src, sbuf, n) : n;
+ }
+
+ /* transmit PASV */
+ n = snprintf(sbuf, sizeof(sbuf), "PASV\r\n");
+ if (n > 0)
+ n = write(dst, sbuf, n);
+ *state = LPSV;
+ passivemode = 0; /* to be set to 1 later */
+ return n;
+ } else if (strcmp(cmd, "EPSV") == 0 && !param) {
+ /*
+ * EPSV -> PASV
+ */
+ close(wport4);
+ close(wport6);
+ close(port4);
+ close(port6);
+ wport4 = wport6 = port4 = port6 = -1;
+
+ n = snprintf(sbuf, sizeof(sbuf), "PASV\r\n");
+ if (n > 0)
+ n = write(dst, sbuf, n);
+ *state = EPSV;
+ passivemode = 0; /* to be set to 1 later */
+ return n;
+ } else if (strcmp(cmd, "EPSV") == 0 && param
+ && strncasecmp(param, "ALL", 3) == 0 && isspace(param[3])) {
+ /*
+ * EPSV ALL
+ */
+ epsvall = 1;
+ n = snprintf(sbuf, sizeof(sbuf), "200 EPSV ALL command successful.\r\n");
+ return n > 0 ? write(src, sbuf, n) : n;
+#ifdef FAITH4
+ } else if (strcmp(cmd, "PORT") == 0 && param) {
+ /*
+ * PORT -> EPRT
+ */
+ char host[NI_MAXHOST], serv[NI_MAXSERV];
+
+ nstate = PORT;
+
+ close(wport4);
+ close(wport6);
+ close(port4);
+ close(port6);
+ wport4 = wport6 = port4 = port6 = -1;
+
+ p = param;
+ n = sscanf(p, "%u,%u,%u,%u,%u,%u",
+ &ho[0], &ho[1], &ho[2], &ho[3], &po[0], &po[1]);
+ if (n != 6) {
+ n = snprintf(sbuf, sizeof(sbuf),
+ "501 illegal parameter to PORT\r\n");
+ return n > 0 ? write(src, sbuf, n) : n;
+ }
+
+ memset(&data6, 0, sizeof(data6));
+ sin = (struct sockaddr_in *)&data6;
+ sin->sin_len = sizeof(*sin);
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = htonl(
+ ((ho[0] & 0xff) << 24) | ((ho[1] & 0xff) << 16) |
+ ((ho[2] & 0xff) << 8) | (ho[3] & 0xff));
+ sin->sin_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff));
+
+ /* get ready for active data connection */
+ n = sizeof(data4);
+ error = getsockname(dst, (struct sockaddr *)&data4, &n);
+ if (error == -1) {
+portfail:
+ n = snprintf(sbuf, sizeof(sbuf),
+ "500 could not translate to EPRT\r\n");
+ return n > 0 ? write(src, sbuf, n) : n;
+ }
+ if (((struct sockaddr *)&data4)->sa_family != AF_INET6)
+ goto portfail;
+
+ ((struct sockaddr_in6 *)&data4)->sin6_port = 0;
+ sa = (struct sockaddr *)&data4;
+ wport4 = socket(sa->sa_family, SOCK_STREAM, 0);
+ if (wport4 == -1)
+ goto portfail;
+ error = bind(wport4, sa, sa->sa_len);
+ if (error == -1) {
+ close(wport4);
+ wport4 = -1;
+ goto portfail;
+ }
+ error = listen(wport4, 1);
+ if (error == -1) {
+ close(wport4);
+ wport4 = -1;
+ goto portfail;
+ }
+
+ /* transmit EPRT */
+ n = sizeof(data4);
+ error = getsockname(wport4, (struct sockaddr *)&data4, &n);
+ if (error == -1) {
+ close(wport4);
+ wport4 = -1;
+ goto portfail;
+ }
+ af = 2;
+ sa = (struct sockaddr *)&data4;
+ if (getnameinfo(sa, sa->sa_len, host, sizeof(host),
+ serv, sizeof(serv), NI_NUMERICHOST | NI_NUMERICSERV)) {
+ close(wport4);
+ wport4 = -1;
+ goto portfail;
+ }
+ n = snprintf(sbuf, sizeof(sbuf), "EPRT |%d|%s|%s|\r\n", af, host, serv);
+ if (n > 0)
+ n = write(dst, sbuf, n);
+ *state = nstate;
+ passivemode = 0;
+ return n;
+ } else if (strcmp(cmd, "PASV") == 0 && !param) {
+ /*
+ * PASV -> EPSV
+ */
+
+ nstate = PASV;
+
+ close(wport4);
+ close(wport6);
+ close(port4);
+ close(port6);
+ wport4 = wport6 = port4 = port6 = -1;
+
+ /* transmit EPSV */
+ n = snprintf(sbuf, sizeof(sbuf), "EPSV\r\n");
+ if (n > 0)
+ n = write(dst, sbuf, n);
+ *state = PASV;
+ passivemode = 0; /* to be set to 1 later */
+ return n;
+#else /* FAITH4 */
+ } else if (strcmp(cmd, "PORT") == 0 || strcmp(cmd, "PASV") == 0) {
+ /*
+ * reject PORT/PASV
+ */
+ n = snprintf(sbuf, sizeof(sbuf), "502 %s not implemented.\r\n", cmd);
+ return n > 0 ? write(src, sbuf, n) : n;
+#endif /* FAITH4 */
+ } else if (passivemode
+ && (strcmp(cmd, "STOR") == 0
+ || strcmp(cmd, "STOU") == 0
+ || strcmp(cmd, "RETR") == 0
+ || strcmp(cmd, "LIST") == 0
+ || strcmp(cmd, "NLST") == 0
+ || strcmp(cmd, "APPE") == 0)) {
+ /*
+ * commands with data transfer. need to care about passive
+ * mode data connection.
+ */
+
+ if (ftp_passiveconn() < 0) {
+ n = snprintf(sbuf, sizeof(sbuf), "425 Cannot open data connetion\r\n");
+ if (n > 0)
+ n = write(src, sbuf, n);
+ } else {
+ /* simply relay the command */
+ write(dst, rbuf, n);
+ }
+
+ *state = NONE;
+ return n;
+ } else {
+ /* simply relay it */
+ *state = NONE;
+ return write(dst, rbuf, n);
+ }
+
+ bad:
+ exit_failure("%s", strerror(errno));
+ /*NOTREACHED*/
+ return 0; /* to make gcc happy */
+}
diff --git a/usr.sbin/faithd/prefix.c b/usr.sbin/faithd/prefix.c
new file mode 100644
index 0000000..a13c640
--- /dev/null
+++ b/usr.sbin/faithd/prefix.c
@@ -0,0 +1,360 @@
+/* $KAME: prefix.c,v 1.9 2001/07/02 14:36:49 itojun Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (C) 2000 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#ifndef offsetof
+#define offsetof(type, member) ((size_t)(u_long)(&((type *)0)->member))
+#endif
+
+#include "faithd.h"
+#include "prefix.h"
+
+static int prefix_set __P((const char *, struct prefix *, int));
+static struct config *config_load1 __P((const char *));
+#if 0
+static void config_show1 __P((const struct config *));
+static void config_show __P((void));
+#endif
+
+struct config *config_list = NULL;
+#ifdef NI_WITHSCOPEID
+const int niflags = NI_NUMERICHOST | NI_WITHSCOPEID;
+#else
+const int niflags = NI_NUMERICHOST;
+#endif
+
+static int
+prefix_set(s, prefix, slash)
+ const char *s;
+ struct prefix *prefix;
+ int slash;
+{
+ char *p, *q, *r;
+ struct addrinfo hints, *res = NULL;
+ int max;
+ char *a;
+
+ p = strdup(s);
+ q = strchr(p, '/');
+ if (q) {
+ if (!slash)
+ goto fail;
+ *q++ = '\0';
+ }
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM; /*dummy*/
+ hints.ai_flags = AI_NUMERICHOST;
+ if (getaddrinfo(p, "0", &hints, &res))
+ goto fail;
+ if (res->ai_next || res->ai_addrlen > sizeof(prefix->a))
+ goto fail;
+ memcpy(&prefix->a, res->ai_addr, res->ai_addrlen);
+
+ switch (prefix->a.ss_family) {
+ case AF_INET:
+ max = 32;
+ a = (char *)&((struct sockaddr_in *)&prefix->a)->sin_addr;
+ break;
+ case AF_INET6:
+ max = 128;
+ a = (char *)&((struct sockaddr_in6 *)&prefix->a)->sin6_addr;
+ break;
+ default:
+ a = NULL;
+ max = -1;
+ break;
+ }
+
+ if (q) {
+ r = NULL;
+ prefix->l = (int)strtoul(q, &r, 10);
+ if (!*q || *r)
+ goto fail;
+ if (prefix->l < 0 || prefix->l > max)
+ goto fail;
+ } else
+ prefix->l = max;
+
+ if (p)
+ free(p);
+ if (res)
+ freeaddrinfo(res);
+ return 0;
+
+fail:
+ if (p)
+ free(p);
+ if (res)
+ freeaddrinfo(res);
+ return -1;
+}
+
+const char *
+prefix_string(prefix)
+ const struct prefix *prefix;
+{
+ static char buf[NI_MAXHOST + 20];
+ char hbuf[NI_MAXHOST];
+
+ if (getnameinfo((const struct sockaddr *)&prefix->a, prefix->a.ss_len,
+ hbuf, sizeof(hbuf), NULL, 0, niflags))
+ return NULL;
+ snprintf(buf, sizeof(buf), "%s/%d", hbuf, prefix->l);
+ return buf;
+}
+
+int
+prefix_match(prefix, sa)
+ const struct prefix *prefix;
+ const struct sockaddr *sa;
+{
+ struct sockaddr_storage a, b;
+ char *pa, *pb;
+ int off, l;
+
+ if (prefix->a.ss_family != sa->sa_family ||
+ prefix->a.ss_len != sa->sa_len)
+ return 0;
+
+ if (prefix->a.ss_len > sizeof(a) || sa->sa_len > sizeof(b))
+ return 0;
+
+ switch (prefix->a.ss_family) {
+ case AF_INET:
+ off = offsetof(struct sockaddr_in, sin_addr);
+ break;
+ case AF_INET6:
+ off = offsetof(struct sockaddr_in6, sin6_addr);
+ break;
+ default:
+ if (memcmp(&prefix->a, sa, prefix->a.ss_len) != 0)
+ return 0;
+ else
+ return 1;
+ }
+
+ memcpy(&a, &prefix->a, prefix->a.ss_len);
+ memcpy(&b, sa, sa->sa_len);
+ l = prefix->l / 8 + (prefix->l % 8 ? 1 : 0);
+
+ /* overrun check */
+ if (off + l > a.ss_len)
+ return 0;
+
+ pa = ((char *)&a) + off;
+ pb = ((char *)&b) + off;
+ if (prefix->l % 8) {
+ pa[prefix->l / 8] &= 0xff00 >> (prefix->l % 8);
+ pb[prefix->l / 8] &= 0xff00 >> (prefix->l % 8);
+ }
+ if (memcmp(pa, pb, l) != 0)
+ return 0;
+ else
+ return 1;
+}
+
+/*
+ * prefix/prefixlen permit/deny prefix/prefixlen [srcaddr]
+ * 3ffe::/16 permit 10.0.0.0/8 10.1.1.1
+ */
+static struct config *
+config_load1(line)
+ const char *line;
+{
+ struct config *conf;
+ char buf[BUFSIZ];
+ char *p;
+ char *token[4];
+ int i;
+
+ if (strlen(line) + 1 > sizeof(buf))
+ return NULL;
+ strlcpy(buf, line, sizeof(buf));
+
+ p = strchr(buf, '\n');
+ if (!p)
+ return NULL;
+ *p = '\0';
+ p = strchr(buf, '#');
+ if (p)
+ *p = '\0';
+ if (strlen(buf) == 0)
+ return NULL;
+
+ p = buf;
+ memset(token, 0, sizeof(token));
+ for (i = 0; i < sizeof(token) / sizeof(token[0]); i++) {
+ token[i] = strtok(p, "\t ");
+ p = NULL;
+ if (token[i] == NULL)
+ break;
+ }
+ /* extra tokens? */
+ if (strtok(p, "\t ") != NULL)
+ return NULL;
+ /* insufficient tokens */
+ switch (i) {
+ case 3:
+ case 4:
+ break;
+ default:
+ return NULL;
+ }
+
+ conf = (struct config *)malloc(sizeof(*conf));
+ if (conf == NULL)
+ return NULL;
+ memset(conf, 0, sizeof(*conf));
+
+ if (strcasecmp(token[1], "permit") == 0)
+ conf->permit = 1;
+ else if (strcasecmp(token[1], "deny") == 0)
+ conf->permit = 0;
+ else {
+ /* invalid keyword is considered as "deny" */
+ conf->permit = 0;
+ }
+
+ if (prefix_set(token[0], &conf->match, 1) < 0)
+ goto fail;
+ if (prefix_set(token[2], &conf->dest, 1) < 0)
+ goto fail;
+ if (token[3]) {
+ if (prefix_set(token[3], &conf->src, 0) < 0)
+ goto fail;
+ }
+
+ return conf;
+
+fail:
+ free(conf);
+ return NULL;
+}
+
+int
+config_load(configfile)
+ const char *configfile;
+{
+ FILE *fp;
+ char buf[BUFSIZ];
+ struct config *conf, *p;
+ struct config sentinel;
+
+ config_list = NULL;
+
+ if (!configfile)
+ configfile = _PATH_PREFIX_CONF;
+ fp = fopen(configfile, "r");
+ if (fp == NULL)
+ return -1;
+
+ p = &sentinel;
+ while (fgets(buf, sizeof(buf), fp) != NULL) {
+ conf = config_load1(buf);
+ if (conf) {
+ p->next = conf;
+ p = p->next;
+ }
+ }
+ config_list = sentinel.next;
+
+ fclose(fp);
+ return 0;
+}
+
+#if 0
+static void
+config_show1(conf)
+ const struct config *conf;
+{
+ const char *p;
+
+ p = prefix_string(&conf->match);
+ printf("%s", p ? p : "?");
+
+ if (conf->permit)
+ printf(" permit");
+ else
+ printf(" deny");
+
+ p = prefix_string(&conf->dest);
+ printf(" %s", p ? p : "?");
+
+ printf("\n");
+}
+
+static void
+config_show()
+{
+ struct config *conf;
+
+ for (conf = config_list; conf; conf = conf->next)
+ config_show1(conf);
+}
+#endif
+
+const struct config *
+config_match(sa1, sa2)
+ struct sockaddr *sa1, *sa2;
+{
+ static struct config conf;
+ const struct config *p;
+
+ if (sa1->sa_len > sizeof(conf.match.a) ||
+ sa2->sa_len > sizeof(conf.dest.a))
+ return NULL;
+
+ memset(&conf, 0, sizeof(conf));
+ if (!config_list) {
+ conf.permit = 1;
+ memcpy(&conf.match.a, sa1, sa1->sa_len);
+ memcpy(&conf.dest.a, sa2, sa2->sa_len);
+ return &conf;
+ }
+
+ for (p = config_list; p; p = p->next)
+ if (prefix_match(&p->match, sa1) && prefix_match(&p->dest, sa2))
+ return p;
+
+ return NULL;
+}
diff --git a/usr.sbin/faithd/prefix.h b/usr.sbin/faithd/prefix.h
new file mode 100644
index 0000000..2a29639
--- /dev/null
+++ b/usr.sbin/faithd/prefix.h
@@ -0,0 +1,52 @@
+/* $KAME: prefix.h,v 1.4 2001/09/05 03:04:21 itojun Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (C) 2000 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+struct prefix {
+ struct sockaddr_storage a;
+ int l;
+};
+
+struct config {
+ struct config *next;
+
+ int permit;
+ struct prefix match;
+ struct prefix dest;
+ struct prefix src; /* src to use for outgoing connection */
+};
+
+#define _PATH_PREFIX_CONF "/etc/faithd.conf"
+
+extern const char *prefix_string __P((const struct prefix *));
+extern int prefix_match __P((const struct prefix *, const struct sockaddr *));
+extern int config_load __P((const char *));
+extern const struct config *config_match __P((struct sockaddr *, struct sockaddr *));
diff --git a/usr.sbin/faithd/rsh.c b/usr.sbin/faithd/rsh.c
new file mode 100644
index 0000000..ab8e623e
--- /dev/null
+++ b/usr.sbin/faithd/rsh.c
@@ -0,0 +1,212 @@
+/* $KAME: rsh.c,v 1.7 2001/09/05 01:10:30 itojun Exp $ */
+
+/*
+ * Copyright (C) 1997 and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include "faithd.h"
+
+char rshbuf[MSS];
+
+int s_ctl, s_ctl6, s_rcv, s_snd;
+int half;
+
+void
+rsh_relay(int s_src, int s_dst)
+{
+ ssize_t n;
+ fd_set readfds;
+ int error;
+ struct timeval tv;
+
+ FD_ZERO(&readfds);
+ FD_SET(s_src, &readfds);
+ tv.tv_sec = FAITH_TIMEOUT;
+ tv.tv_usec = 0;
+ error = select(256, &readfds, NULL, NULL, &tv);
+ if (error == -1)
+ exit_failure("select %d: %s", s_src, strerror(errno));
+ else if (error == 0)
+ exit_failure("connection timeout");
+
+ n = read(s_src, rshbuf, sizeof(rshbuf));
+ if (rshbuf[0] != 0) {
+ rsh_dual_relay(s_src, s_dst);
+ /* NOTREACHED */
+ }
+ write(s_dst, rshbuf, n);
+ tcp_relay(s_src, s_dst, "rsh");
+ /* NOTREACHED */
+}
+
+static void
+relay(int src, int dst)
+{
+ int error;
+ ssize_t n;
+ int atmark;
+
+ error = ioctl(s_rcv, SIOCATMARK, &atmark);
+ if (error != -1 && atmark == 1) {
+ n = read(s_rcv, rshbuf, 1);
+ if (n == 1)
+ send(s_snd, rshbuf, 1, MSG_OOB);
+ return;
+ }
+
+ n = read(s_rcv, rshbuf, sizeof(rshbuf));
+
+ switch (n) {
+ case -1:
+ exit_failure("%s", strerror(errno));
+ case 0:
+ if (s_rcv == src) {
+ /* half close */
+ shutdown(dst, 1);
+ half = YES;
+ break;
+ }
+ close(src);
+ close(dst);
+ close(s_ctl);
+ close(s_ctl6);
+ exit_success("terminating rsh/contorol connections");
+ break;
+ default:
+ write(s_snd, rshbuf, n);
+ }
+}
+
+void
+rsh_dual_relay(int s_src, int s_dst)
+{
+ fd_set readfds;
+ int len, s_wld, error;
+ struct sockaddr_storage ctladdr6;
+ struct sockaddr_storage ctladdr;
+ int port6 = 0, lport, lport6;
+ char *p;
+ struct timeval tv;
+ struct sockaddr *sa;
+
+ half = NO;
+ s_rcv = s_src;
+ s_snd = s_dst;
+ syslog(LOG_INFO, "starting rsh connection");
+
+ for (p = rshbuf; *p; p++)
+ port6 = port6 * 10 + *p - '0';
+
+ len = sizeof(ctladdr6);
+ getpeername(s_src, (struct sockaddr *)&ctladdr6, &len);
+ if (((struct sockaddr *)&ctladdr6)->sa_family == AF_INET6)
+ ((struct sockaddr_in6 *)&ctladdr6)->sin6_port = htons(port6);
+ else
+ ((struct sockaddr_in *)&ctladdr6)->sin_port = htons(port6);
+
+ s_wld = rresvport(&lport);
+ if (s_wld == -1) goto bad;
+ error = listen(s_wld, 1);
+ if (error == -1) goto bad;
+ snprintf(rshbuf, sizeof(rshbuf), "%d", lport);
+ write(s_dst, rshbuf, strlen(rshbuf)+1);
+
+ len = sizeof(ctladdr);
+ s_ctl = accept(s_wld, (struct sockaddr *)&ctladdr, &len);
+ if (s_ctl == -1) goto bad;
+ close(s_wld);
+
+ sa = (struct sockaddr *)&ctladdr6;
+ s_ctl6 = rresvport_af(&lport6, sa->sa_family);
+ if (s_ctl6 == -1) goto bad;
+ error = connect(s_ctl6, sa, sa->sa_len);
+ if (error == -1) goto bad;
+
+ syslog(LOG_INFO, "starting rsh control connection");
+
+ for (;;) {
+ FD_ZERO(&readfds);
+ if (half == NO)
+ FD_SET(s_src, &readfds);
+ FD_SET(s_dst, &readfds);
+ FD_SET(s_ctl, &readfds);
+ FD_SET(s_ctl6, &readfds);
+ tv.tv_sec = FAITH_TIMEOUT;
+ tv.tv_usec = 0;
+
+ error = select(256, &readfds, NULL, NULL, &tv);
+ if (error == -1)
+ exit_failure("select 4 sockets: %s", strerror(errno));
+ else if (error == 0)
+ exit_failure("connection timeout");
+
+ if (half == NO && FD_ISSET(s_src, &readfds)) {
+ s_rcv = s_src;
+ s_snd = s_dst;
+ relay(s_src, s_dst);
+ }
+ if (FD_ISSET(s_dst, &readfds)) {
+ s_rcv = s_dst;
+ s_snd = s_src;
+ relay(s_src, s_dst);
+ }
+ if (FD_ISSET(s_ctl, &readfds)) {
+ s_rcv = s_ctl;
+ s_snd = s_ctl6;
+ relay(s_src, s_dst);
+ }
+ if (FD_ISSET(s_ctl6, &readfds)) {
+ s_rcv = s_ctl6;
+ s_snd = s_ctl;
+ relay(s_src, s_dst);
+ }
+ }
+ /* NOTREACHED */
+
+ bad:
+ exit_failure("%s", strerror(errno));
+}
diff --git a/usr.sbin/faithd/tcp.c b/usr.sbin/faithd/tcp.c
new file mode 100644
index 0000000..68a2f51
--- /dev/null
+++ b/usr.sbin/faithd/tcp.c
@@ -0,0 +1,304 @@
+/* $KAME: tcp.c,v 1.8 2001/11/21 07:40:22 itojun Exp $ */
+
+/*
+ * Copyright (C) 1997 and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include "faithd.h"
+
+static char tcpbuf[16*1024];
+ /* bigger than MSS and may be lesser than window size */
+static int tblen, tboff, oob_exists;
+static fd_set readfds, writefds, exceptfds;
+static char atmark_buf[2];
+static pid_t cpid = (pid_t)0;
+static pid_t ppid = (pid_t)0;
+volatile time_t child_lastactive = (time_t)0;
+static time_t parent_lastactive = (time_t)0;
+
+static void sig_ctimeout __P((int));
+static void sig_child __P((int));
+static void notify_inactive __P((void));
+static void notify_active __P((void));
+static void send_data __P((int, int, const char *, int));
+static void relay __P((int, int, const char *, int));
+
+/*
+ * Inactivity timer:
+ * - child side (ppid != 0) will send SIGUSR1 to parent every (FAITH_TIMEOUT/4)
+ * second if traffic is active. if traffic is inactive, don't send SIGUSR1.
+ * - parent side (ppid == 0) will check the last SIGUSR1 it have seen.
+ */
+static void
+sig_ctimeout(int sig)
+{
+ /* parent side: record notification from the child */
+ if (dflag)
+ syslog(LOG_DEBUG, "activity timer from child");
+ child_lastactive = time(NULL);
+}
+
+/* parent will terminate if child dies. */
+static void
+sig_child(int sig)
+{
+ int status;
+ pid_t pid;
+
+ pid = wait3(&status, WNOHANG, (struct rusage *)0);
+ if (pid && WEXITSTATUS(status))
+ syslog(LOG_WARNING, "child %d exit status 0x%x", pid, status);
+ exit_success("terminate connection due to child termination");
+}
+
+static void
+notify_inactive()
+{
+ time_t t;
+
+ /* only on parent side... */
+ if (ppid)
+ return;
+
+ /* parent side should check for timeout. */
+ t = time(NULL);
+ if (dflag) {
+ syslog(LOG_DEBUG, "parent side %sactive, child side %sactive",
+ (FAITH_TIMEOUT < t - parent_lastactive) ? "in" : "",
+ (FAITH_TIMEOUT < t - child_lastactive) ? "in" : "");
+ }
+
+ if (FAITH_TIMEOUT < t - child_lastactive
+ && FAITH_TIMEOUT < t - parent_lastactive) {
+ /* both side timeouted */
+ signal(SIGCHLD, SIG_DFL);
+ kill(cpid, SIGTERM);
+ wait(NULL);
+ exit_failure("connection timeout");
+ /* NOTREACHED */
+ }
+}
+
+static void
+notify_active()
+{
+ if (ppid) {
+ /* child side: notify parent of active traffic */
+ time_t t;
+ t = time(NULL);
+ if (FAITH_TIMEOUT / 4 < t - child_lastactive) {
+ if (kill(ppid, SIGUSR1) < 0) {
+ exit_failure("terminate connection due to parent termination");
+ /* NOTREACHED */
+ }
+ child_lastactive = t;
+ }
+ } else {
+ /* parent side */
+ parent_lastactive = time(NULL);
+ }
+}
+
+static void
+send_data(int s_rcv, int s_snd, const char *service, int direction)
+{
+ int cc;
+
+ if (oob_exists) {
+ cc = send(s_snd, atmark_buf, 1, MSG_OOB);
+ if (cc == -1)
+ goto retry_or_err;
+ oob_exists = 0;
+ FD_SET(s_rcv, &exceptfds);
+ }
+
+ for (; tboff < tblen; tboff += cc) {
+ cc = write(s_snd, tcpbuf + tboff, tblen - tboff);
+ if (cc < 0)
+ goto retry_or_err;
+ }
+#ifdef DEBUG
+ if (tblen) {
+ if (tblen >= sizeof(tcpbuf))
+ tblen = sizeof(tcpbuf) - 1;
+ tcpbuf[tblen] = '\0';
+ syslog(LOG_DEBUG, "from %s (%dbytes): %s",
+ direction == 1 ? "client" : "server", tblen, tcpbuf);
+ }
+#endif /* DEBUG */
+ tblen = 0; tboff = 0;
+ FD_CLR(s_snd, &writefds);
+ FD_SET(s_rcv, &readfds);
+ return;
+ retry_or_err:
+ if (errno != EAGAIN)
+ exit_failure("writing relay data failed: %s", strerror(errno));
+ FD_SET(s_snd, &writefds);
+}
+
+static void
+relay(int s_rcv, int s_snd, const char *service, int direction)
+{
+ int atmark, error, maxfd;
+ struct timeval tv;
+ fd_set oreadfds, owritefds, oexceptfds;
+
+ FD_ZERO(&readfds);
+ FD_ZERO(&writefds);
+ FD_ZERO(&exceptfds);
+ fcntl(s_snd, F_SETFD, O_NONBLOCK);
+ oreadfds = readfds; owritefds = writefds; oexceptfds = exceptfds;
+ FD_SET(s_rcv, &readfds);
+ FD_SET(s_rcv, &exceptfds);
+ oob_exists = 0;
+ maxfd = (s_rcv > s_snd) ? s_rcv : s_snd;
+
+ for (;;) {
+ tv.tv_sec = FAITH_TIMEOUT / 4;
+ tv.tv_usec = 0;
+ oreadfds = readfds;
+ owritefds = writefds;
+ oexceptfds = exceptfds;
+ error = select(maxfd + 1, &readfds, &writefds, &exceptfds, &tv);
+ if (error == -1) {
+ if (errno == EINTR)
+ continue;
+ exit_failure("select: %s", strerror(errno));
+ } else if (error == 0) {
+ readfds = oreadfds;
+ writefds = owritefds;
+ exceptfds = oexceptfds;
+ notify_inactive();
+ continue;
+ }
+
+ /* activity notification */
+ notify_active();
+
+ if (FD_ISSET(s_rcv, &exceptfds)) {
+ error = ioctl(s_rcv, SIOCATMARK, &atmark);
+ if (error != -1 && atmark == 1) {
+ int cc;
+ oob_read_retry:
+ cc = read(s_rcv, atmark_buf, 1);
+ if (cc == 1) {
+ FD_CLR(s_rcv, &exceptfds);
+ FD_SET(s_snd, &writefds);
+ oob_exists = 1;
+ } else if (cc == -1) {
+ if (errno == EINTR)
+ goto oob_read_retry;
+ exit_failure("reading oob data failed"
+ ": %s",
+ strerror(errno));
+ }
+ }
+ }
+ if (FD_ISSET(s_rcv, &readfds)) {
+ relaydata_read_retry:
+ tblen = read(s_rcv, tcpbuf, sizeof(tcpbuf));
+ tboff = 0;
+
+ switch (tblen) {
+ case -1:
+ if (errno == EINTR)
+ goto relaydata_read_retry;
+ exit_failure("reading relay data failed: %s",
+ strerror(errno));
+ /* NOTREACHED */
+ case 0:
+ /* to close opposite-direction relay process */
+ shutdown(s_snd, 0);
+
+ close(s_rcv);
+ close(s_snd);
+ exit_success("terminating %s relay", service);
+ /* NOTREACHED */
+ default:
+ FD_CLR(s_rcv, &readfds);
+ FD_SET(s_snd, &writefds);
+ break;
+ }
+ }
+ if (FD_ISSET(s_snd, &writefds))
+ send_data(s_rcv, s_snd, service, direction);
+ }
+}
+
+void
+tcp_relay(int s_src, int s_dst, const char *service)
+{
+ syslog(LOG_INFO, "starting %s relay", service);
+
+ child_lastactive = parent_lastactive = time(NULL);
+
+ cpid = fork();
+ switch (cpid) {
+ case -1:
+ exit_failure("tcp_relay: can't fork grand child: %s",
+ strerror(errno));
+ /* NOTREACHED */
+ case 0:
+ /* child process: relay going traffic */
+ ppid = getppid();
+ /* this is child so reopen log */
+ closelog();
+ openlog(logname, LOG_PID | LOG_NOWAIT, LOG_DAEMON);
+ relay(s_src, s_dst, service, 1);
+ /* NOTREACHED */
+ default:
+ /* parent process: relay coming traffic */
+ ppid = (pid_t)0;
+ signal(SIGUSR1, sig_ctimeout);
+ signal(SIGCHLD, sig_child);
+ relay(s_dst, s_src, service, 0);
+ /* NOTREACHED */
+ }
+}
diff --git a/usr.sbin/faithd/test/faithd.rb b/usr.sbin/faithd/test/faithd.rb
new file mode 100644
index 0000000..683b504
--- /dev/null
+++ b/usr.sbin/faithd/test/faithd.rb
@@ -0,0 +1,312 @@
+# faithd, ruby version. requires v6-enabled ruby.
+#
+# highly experimental (not working right at all) and very limited
+# functionality.
+#
+# $Id: faithd.rb,v 1.1.1.1 1999/08/08 23:29:31 itojun Exp $
+# $FreeBSD$
+
+require "socket"
+require "thread"
+
+# XXX should be derived from system headers
+IPPROTO_IPV6 = 41
+IPV6_FAITH = 29
+DEBUG = true
+DEBUG_LOOPBACK = true
+
+# TODO: OOB data handling
+def tcpcopy(s1, s2, m)
+ STDERR.print "tcpcopy #{s1} #{s2}\n" if DEBUG
+ buf = ""
+ while TRUE
+ begin
+ buf = s1.sysread(100)
+ s2.syswrite(buf)
+ rescue EOFError
+ break
+ rescue IOError
+ break
+ end
+ end
+ STDERR.print "tcpcopy #{s1} #{s2} finished\n" if DEBUG
+ s1.shutdown(0)
+ s2.shutdown(1)
+end
+
+def relay_ftp_passiveconn(s6, s4, dport6, dport4)
+ Thread.start do
+ d6 = TCPserver.open("::", dport6).accept
+ d4 = TCPsocket.open(s4.getpeer[3], dport4)
+ t = []
+ t[0] = Thread.start do
+ tcpcopy(d6, d4)
+ end
+ t[1] = Thread.start do
+ tcpcopy(d4, d6)
+ end
+ for i in t
+ i.join
+ end
+ d4.close
+ d6.close
+ end
+end
+
+def ftp_parse_2428(line)
+ if (line[0] != line[line.length - 1])
+ return nil
+ end
+ t = line.split(line[0 .. 0]) # as string
+ if (t.size != 4 || t[1] !~ /^[12]$/ || t[3] !~ /^\d+$/)
+ return nil
+ end
+ return t[1 .. 3]
+end
+
+def relay_ftp_command(s6, s4, state)
+ STDERR.print "relay_ftp_command start\n" if DEBUG
+ while TRUE
+ begin
+ STDERR.print "s6.gets\n" if DEBUG
+ line = s6.gets
+ STDERR.print "line is #{line}\n" if DEBUG
+ if line == nil
+ return nil
+ end
+
+ # translate then copy
+ STDERR.print "line is #{line}\n" if DEBUG
+ if (line =~ /^EPSV\r\n/i)
+ STDERR.print "EPSV -> PASV\n" if DEBUG
+ line = "PASV\n"
+ state = "EPSV"
+ elsif (line =~ /^EPRT\s+(.+)\r\n/i)
+ t = ftp_parse_2428($1)
+ if t == nil
+ s6.puts "501 illegal parameter to EPRT\r\n"
+ next
+ end
+
+ # some tricks should be here
+ s6.puts "501 illegal parameter to EPRT\r\n"
+ next
+ end
+ STDERR.print "fail: send #{line} as is\n" if DEBUG
+ s4.puts(line)
+ break
+ rescue EOFError
+ return nil
+ rescue IOError
+ return nil
+ end
+ end
+ STDERR.print "relay_ftp_command finish\n" if DEBUG
+ return state
+end
+
+def relay_ftp_status(s4, s6, state)
+ STDERR.print "relay_ftp_status start\n" if DEBUG
+ while TRUE
+ begin
+ line = s4.gets
+ if line == nil
+ return nil
+ end
+
+ # translate then copy
+ s6.puts(line)
+
+ next if line =~ /^\d\d\d-/
+ next if line !~ /^\d/
+
+ # special post-processing
+ case line
+ when /^221 / # result to QUIT
+ s4.shutdown(0)
+ s6.shutdown(1)
+ end
+
+ break if (line =~ /^\d\d\d /)
+ rescue EOFError
+ return nil
+ rescue IOError
+ return nil
+ end
+ end
+ STDERR.print "relay_ftp_status finish\n" if DEBUG
+ return state
+end
+
+def relay_ftp(sock, name)
+ STDERR.print "relay_ftp(#{sock}, #{name})\n" if DEBUG
+ while TRUE
+ STDERR.print "relay_ftp(#{sock}, #{name}) accepting\n" if DEBUG
+ s = sock.accept
+ STDERR.print "relay_ftp(#{sock}, #{name}) accepted #{s}\n" if DEBUG
+ Thread.start do
+ threads = []
+ STDERR.print "accepted #{s} -> #{Thread.current}\n" if DEBUG
+ s6 = s
+ dest6 = s.addr[3]
+ if !DEBUG_LOOPBACK
+ t = s.getsockname.unpack("x8 x12 C4")
+ dest4 = "#{t[0]}.#{t[1]}.#{t[2]}.#{t[3]}"
+ port4 = s.addr[1]
+ else
+ dest4 = "127.0.0.1"
+ port4 = "ftp"
+ end
+ if DEBUG
+ STDERR.print "IPv6 dest: #{dest6} IPv4 dest: #{dest4}\n" if DEBUG
+ end
+ STDERR.print "connect to #{dest4} #{port4}\n" if DEBUG
+ s4 = TCPsocket.open(dest4, port4)
+ STDERR.print "connected to #{dest4} #{port4}, #{s4.addr[1]}\n" if DEBUG
+ state = 0
+ while TRUE
+ # translate status line
+ state = relay_ftp_status(s4, s6, state)
+ break if state == nil
+ # translate command line
+ state = relay_ftp_command(s6, s4, state)
+ break if state == nil
+ end
+ STDERR.print "relay_ftp(#{sock}, #{name}) closing s4\n" if DEBUG
+ s4.close
+ STDERR.print "relay_ftp(#{sock}, #{name}) closing s6\n" if DEBUG
+ s6.close
+ STDERR.print "relay_ftp(#{sock}, #{name}) done\n" if DEBUG
+ end
+ end
+ STDERR.print "relay_ftp(#{sock}, #{name}) finished\n" if DEBUG
+end
+
+def relay_tcp(sock, name)
+ STDERR.print "relay_tcp(#{sock}, #{name})\n" if DEBUG
+ while TRUE
+ STDERR.print "relay_tcp(#{sock}, #{name}) accepting\n" if DEBUG
+ s = sock.accept
+ STDERR.print "relay_tcp(#{sock}, #{name}) accepted #{s}\n" if DEBUG
+ Thread.start do
+ threads = []
+ STDERR.print "accepted #{s} -> #{Thread.current}\n" if DEBUG
+ s6 = s
+ dest6 = s.addr[3]
+ if !DEBUG_LOOPBACK
+ t = s.getsockname.unpack("x8 x12 C4")
+ dest4 = "#{t[0]}.#{t[1]}.#{t[2]}.#{t[3]}"
+ port4 = s.addr[1]
+ else
+ dest4 = "127.0.0.1"
+ port4 = "telnet"
+ end
+ if DEBUG
+ STDERR.print "IPv6 dest: #{dest6} IPv4 dest: #{dest4}\n" if DEBUG
+ end
+ STDERR.print "connect to #{dest4} #{port4}\n" if DEBUG
+ s4 = TCPsocket.open(dest4, port4)
+ STDERR.print "connected to #{dest4} #{port4}, #{s4.addr[1]}\n" if DEBUG
+ [0, 1].each do |i|
+ threads[i] = Thread.start do
+ if (i == 0)
+ tcpcopy(s6, s4)
+ else
+ tcpcopy(s4, s6)
+ end
+ end
+ end
+ STDERR.print "relay_tcp(#{sock}, #{name}) wait\n" if DEBUG
+ for i in threads
+ STDERR.print "relay_tcp(#{sock}, #{name}) wait #{i}\n" if DEBUG
+ i.join
+ STDERR.print "relay_tcp(#{sock}, #{name}) wait #{i} done\n" if DEBUG
+ end
+ STDERR.print "relay_tcp(#{sock}, #{name}) closing s4\n" if DEBUG
+ s4.close
+ STDERR.print "relay_tcp(#{sock}, #{name}) closing s6\n" if DEBUG
+ s6.close
+ STDERR.print "relay_tcp(#{sock}, #{name}) done\n" if DEBUG
+ end
+ end
+ STDERR.print "relay_tcp(#{sock}, #{name}) finished\n" if DEBUG
+end
+
+def usage()
+ STDERR.print "usage: #{$0} [-f] port...\n"
+end
+
+#------------------------------------------------------------
+
+$mode = "tcp"
+
+while ARGV[0] =~ /^-/ do
+ case ARGV[0]
+ when /^-f/
+ $mode = "ftp"
+ else
+ usage()
+ exit 0
+ end
+ ARGV.shift
+end
+
+if ARGV.length == 0
+ usage()
+ exit 1
+end
+
+ftpport = Socket.getservbyname("ftp")
+
+res = []
+for port in ARGV
+ t = Socket.getaddrinfo(nil, port, Socket::PF_INET6, Socket::SOCK_STREAM,
+ nil, Socket::AI_PASSIVE)
+ if (t.size <= 0)
+ STDERR.print "FATAL: getaddrinfo failed (port=#{port})\n"
+ exit 1
+ end
+ res += t
+end
+
+sockpool = []
+names = []
+listenthreads = []
+
+res.each do |i|
+ s = TCPserver.new(i[3], i[1])
+ n = Socket.getnameinfo(s.getsockname, Socket::NI_NUMERICHOST|Socket::NI_NUMERICSERV).join(" port ")
+ if i[6] == IPPROTO_IPV6
+ s.setsockopt(i[6], IPV6_FAITH, 1)
+ end
+ s.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1)
+ sockpool.push s
+ names.push n
+end
+
+if DEBUG
+ (0 .. sockpool.size - 1).each do |i|
+ STDERR.print "listen[#{i}]: #{sockpool[i]} #{names[i]}\n" if DEBUG
+ end
+end
+
+(0 .. sockpool.size - 1).each do |i|
+ listenthreads[i] = Thread.start do
+ if DEBUG
+ STDERR.print "listen[#{i}]: thread #{Thread.current}\n" if DEBUG
+ end
+ STDERR.print "listen[#{i}]: thread #{Thread.current}\n" if DEBUG
+ case $mode
+ when "tcp"
+ relay_tcp(sockpool[i], names[i])
+ when "ftp"
+ relay_ftp(sockpool[i], names[i])
+ end
+ end
+end
+
+for i in listenthreads
+ i.join
+end
+
+exit 0
diff --git a/usr.sbin/fdcontrol/Makefile b/usr.sbin/fdcontrol/Makefile
new file mode 100644
index 0000000..38f9a1e
--- /dev/null
+++ b/usr.sbin/fdcontrol/Makefile
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../fdread
+
+PROG= fdcontrol
+SRCS= fdcontrol.c fdutil.c
+MAN= fdcontrol.8
+
+WARNS?= 2
+CFLAGS+= -I${.CURDIR}/../fdread
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/fdcontrol/fdcontrol.8 b/usr.sbin/fdcontrol/fdcontrol.8
new file mode 100644
index 0000000..e013554
--- /dev/null
+++ b/usr.sbin/fdcontrol/fdcontrol.8
@@ -0,0 +1,328 @@
+.\"
+.\" Copyright (C) 1994, 2001 by Joerg Wunsch, Dresden
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY
+.\" EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+.\" OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+.\" BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+.\" LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+.\" USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+.\" DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd December 25, 2001
+.Os
+.Dt FDCONTROL 8
+.Sh NAME
+.Nm fdcontrol
+.Nd display and modify floppy disk parameters
+.Sh SYNOPSIS
+.Nm
+.Op Fl F
+.Op Fl d Ar dbg
+.Op Fl f Ar fmt
+.Op Fl s Ar fmtstr
+.Op Fl v
+.Ar device
+.Sh DESCRIPTION
+The
+.Nm
+utility allows the modification of the run-time behavior of the
+.Xr fdc 4
+driver for the device specified by
+.Ar device .
+.Pp
+Commands are implemented to query the current device density settings
+as well as the underlying device hardware as registered with the
+driver, to manipulate debugging levels, and to adjust the device
+density settings.
+All the operations that manipulate the kernel
+settings are restricted to the superuser (by the device driver), while
+all inquiry requests only require read access to
+.Ar device .
+.Pp
+The
+.Ar device
+argument should always be given as a full path name, e.g.\&
+.Pa /dev/fd0 .
+.Ss Inquiry Commands
+Running the
+.Nm
+utility without any of the optional flags will report the drive type
+that has been registered with the device driver.
+In the shortest form, a single string describing the drive type will
+be returned.
+Possible values are:
+.Dq Li 360K ,
+.Dq Li 1.2M ,
+.Dq Li 720K ,
+.Dq Li 1.44M ,
+.Dq Li 2.88M ,
+or
+.Dq Li unknown .
+This information is primarily intented to be easily parsable by
+scripts.
+.Pp
+In order to add some descriptive text that makes the output better
+human readable, the flag
+.Fl v
+can be added.
+.Pp
+Specifying flag
+.Fl F
+will report the device's density settings in a form that is suitable
+as input to the
+.Fl s Ar fmtstr
+option (see below).
+Again, together with
+.Fl v ,
+some more text will be returned, including the total capacity of the
+density settings in kilobytes.
+.Ss Debug Control
+If the
+.Xr fdc 4
+driver has been configured with the
+.Dv FDC_DEBUG
+option, by default, device debugging information is still disabled
+since it could produce huge amounts of kernel messages.
+It needs to
+be turned on using
+.Nm
+together with
+.Dq Fl d Li 1 ,
+usually immediately before starting an operation on the respective
+device the debug information is wanted for, and later turned off again
+using
+.Dq Fl d Li 0 .
+Note that debugging levels are a driver's global option that will
+affect any drives and controllers using the
+.Xr fdc 4
+driver, regardless which
+.Ar device
+has been specified on the
+.Nm
+command line.
+.Ss Density Control
+The
+.Xr fdc 4
+control utilities support two different options how to specify device
+density settings.
+The first form uses
+.Fl f Ar fmt
+to specify the format of the medium in kilobytes.
+Depending on the
+underlying drive type, the value is compared against a table of known
+commonly used device density settings for that drive, and if a match
+has been found, those settings will be used.
+Currently, the following
+values for the respective drive types are acceptable:
+.Bl -item
+.It
+2.88M and 1.44M drives:
+.Bd -ragged -offset indent -compact
+.TS
+lB lB lB lB lB lB lB
+r l l l l l l.
+KB sectrac secsize ncyls speed heads flags
+1721 21 2 (512) 82 500 2 MFM
+1476 18 2 (512) 82 500 2 MFM
+1440 18 2 (512) 80 500 2 MFM
+1200 15 2 (512) 80 500 2 MFM
+820 10 2 (512) 82 250 2 MFM
+800 10 2 (512) 80 250 2 MFM
+720 9 2 (512) 80 250 2 MFM
+.TE
+.Ed
+.It
+1.2M drives:
+.Bd -ragged -offset indent -compact
+.TS
+lB lB lB lB lB lB lB
+r l l l l l l.
+KB sectrac secsize ncyls speed heads flags
+1200 15 2 (512) 80 500 2 MFM
+1232 8 3 (1024) 77 500 2 MFM
+1476 18 2 (512) 82 500 2 MFM
+1440 18 2 (512) 80 500 2 MFM
+1200 15 2 (512) 80 500 2 MFM
+820 10 2 (512) 82 300 2 MFM
+800 10 2 (512) 80 300 2 MFM
+720 9 2 (512) 80 300 2 MFM
+360 9 2 (512) 40 300 2 MFM,2STEP
+640 8 2 (512) 80 300 2 MFM
+.TE
+.Ed
+.It
+720K drives:
+.Bd -ragged -offset indent -compact
+.TS
+lB lB lB lB lB lB lB
+r l l l l l l.
+KB sectrac secsize ncyls speed heads flags
+720 9 2 (512) 80 250 2 MFM
+.TE
+.Ed
+.It
+360K drives:
+.Bd -ragged -offset indent -compact
+.TS
+lB lB lB lB lB lB lB
+r l l l l l l.
+KB sectrac secsize ncyls speed heads flags
+360 9 2 (512) 40 250 2 MFM
+.TE
+.Ed
+.El
+.Pp
+The second form to specify a device density uses
+.Fl s Ar fmtstr
+to explicitly specify each parameter in detail.
+The argument
+.Ar fmtstr
+is a comma-separated list of values of the form:
+.Pp
+.Sm off
+.Ar sectrac , secsize , datalen , gap , ncyls , speed ,
+.Ar heads , f_gap , f_inter , offs2 , flags
+.Sm on
+.Pp
+The meaning of the parameters is:
+.Bl -tag -width ".Ar secsize"
+.It Ar sectrac
+The number of sectors per track.
+.It Ar secsize
+The sector size code, 0 = 128 bytes (or less), 1 = 256 bytes, 2 = 512
+bytes, 3 = 1024 bytes.
+.It Ar datalen
+The actual sector size if the size code is 0, or the (ignored) value
+0xFF for larger size codes.
+.It Ar gap
+The length of the gap 3 parameter for read/write operations.
+.It Ar ncyls
+The number of cylinders.
+.It Ar speed
+The transfer speed in kilobytes per second.
+Can be 250, 300, 500, or
+1000, but each drive type only supports a subset of these values.
+.It Ar heads
+The number of heads.
+.It Ar f_gap
+The length of the gap 3 when formatting media.
+.It Ar f_inter
+The sector interleave to be applied when formatting.
+0 means no
+interleave, 1 means 1:1 etc.
+.It Ar offs2
+The offset of the sector numbers on side 2 (i.e. head number 1).
+Normally, sector numbering on both sides starts with 1.
+.It Ar flags
+A list from one of the following flag values:
+.Pp
+.Bl -tag -width ".Cm +perpend" -compact
+.It Cm +mfm
+Use MFM encoding.
+.It Cm -mfm
+Use FM (single-density) encoding.
+.It Cm +2step
+Use 2 steps per each cylinder (for accessing 40-cylinder media in
+80-cylinder drives).
+.It Cm -2step
+Do not use 2 steps per cylinder, i.e. access each physical cylinder
+of the drive.
+.It Cm +perpend
+Use perpendicular recording (for 2.88 MB media, currently not
+supported).
+.It Cm -perpend
+Use longitudinal recording.
+.El
+.El
+.Pp
+For any missing parameter, the current value will be used, so only
+actual changes need to be specified.
+Thus to turn off a flag bit
+(like
+.Cm +mfm
+which is the default for all drive types), the form with a leading
+minus sign must explicitly be used.
+.Sh EXAMPLES
+A simple inquiry about the drive type:
+.Bd -literal
+$ fdcontrol /dev/fd0
+1.44M
+.Ed
+.Pp
+Same as above, but with verbose output.
+Note that the result is about
+the
+.Em "drive type" ,
+as opposed to a
+.Em "device density" ,
+so it is independent from the actual subdevice being used for
+.Ar device .
+.Bd -literal
+$ fdcontrol -v /dev/fd1.360
+/dev/fd1.360: 1.2M drive (5.25" high-density)
+.Ed
+.\" " <- this one is for Emacs :)
+.Pp
+Inquiry about the density settings of a particular subdevice.
+.Bd -literal
+$ fdcontrol -F /dev/fd0.720
+18,512,0xff,0x1b,80,500,2,0x6c,1,0,+mfm
+.Ed
+.Pp
+Note that just accessing a new subdevice for the first time will clone
+this device using the default density settings for the drive type, as
+explained in
+.Xr fdc 4 .
+Thus, albeit the device name in the example above suggests a 720 KB
+media density, it has actually been initialized (by the driver) to
+1440 KB.
+So, in order to adjust it for standard 720 KB double-density
+media, one of the following
+.Nm
+commands needs to be run:
+.Bd -literal
+# fdcontrol -s 9,,,0x20,,250,,0x50 /dev/fd0.720
+# fdcontrol -f 720 /dev/fd0.720
+.Ed
+.Pp
+As indicated, trailing commas in the parameter list may be omitted.
+.Pp
+In order to access archaic 160 KB single-density (FM encoded) 5.25
+media in a modern 1.2M drive, something like the following definition
+would be needed.
+(Note that not all controller hardware is actually
+capable of handling FM encoding at all.)
+.Bd -literal
+# fdcontrol -s 16,128,0x80,0x2,40,300,,0x10,,,-mfm,+2step /dev/fd1.1
+.Ed
+.Sh SEE ALSO
+.Xr fdc 4
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Fx 2.0 ,
+and has been vastly overhauled in
+.Fx 5.0 .
+.Sh AUTHORS
+The program and this man page have been contributed by
+.An J\(:org Wunsch ,
+Dresden.
diff --git a/usr.sbin/fdcontrol/fdcontrol.c b/usr.sbin/fdcontrol/fdcontrol.c
new file mode 100644
index 0000000..9d447a5
--- /dev/null
+++ b/usr.sbin/fdcontrol/fdcontrol.c
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 1994, 2001 by Joerg Wunsch, Dresden
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/fdcio.h>
+#include <sys/file.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "fdutil.h"
+
+
+static int debug = -1, format, verbose, show = 1, showfmt;
+static char *fmtstring;
+
+static void showdev(enum fd_drivetype, const char *);
+static void usage(void);
+
+static void
+usage(void)
+{
+ errx(EX_USAGE,
+ "usage: fdcontrol [-F] [-d dbg] [-f fmt] [-s fmtstr] [-v] device");
+}
+
+void
+showdev(enum fd_drivetype type, const char *fname)
+{
+ const char *name, *descr;
+
+ getname(type, &name, &descr);
+ if (verbose)
+ printf("%s: %s drive (%s)\n", fname, name, descr);
+ else
+ printf("%s\n", name);
+}
+
+int
+main(int argc, char **argv)
+{
+ enum fd_drivetype type;
+ struct fd_type ft, newft, *fdtp;
+ const char *name, *descr;
+ int fd, i, mode;
+
+ while((i = getopt(argc, argv, "d:Ff:s:v")) != -1)
+ switch(i) {
+ case 'd':
+ if (strcmp(optarg, "0") == 0)
+ debug = 0;
+ else if (strcmp(optarg, "1") == 0)
+ debug = 1;
+ else
+ usage();
+ show = 0;
+ break;
+
+ case 'F':
+ showfmt = 1;
+ show = 0;
+ break;
+
+ case 'f':
+ if (getnum(optarg, &format)) {
+ fprintf(stderr,
+ "Bad argument %s to -f option; must be numeric\n",
+ optarg);
+ usage();
+ }
+ show = 0;
+ break;
+
+ case 's':
+ fmtstring = optarg;
+ show = 0;
+ break;
+
+ case 'v':
+ verbose++;
+ break;
+
+ default:
+ usage();
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if(argc != 1)
+ usage();
+
+ if (show || showfmt)
+ mode = O_RDONLY | O_NONBLOCK;
+ else
+ mode = O_RDWR;
+
+ if((fd = open(argv[0], mode)) < 0)
+ err(EX_UNAVAILABLE, "open(%s)", argv[0]);
+
+ if (ioctl(fd, FD_GDTYPE, &type) == -1)
+ err(EX_OSERR, "ioctl(FD_GDTYPE)");
+ if (ioctl(fd, FD_GTYPE, &ft) == -1)
+ err(EX_OSERR, "ioctl(FD_GTYPE)");
+
+ if (show) {
+ showdev(type, argv[0]);
+ return (0);
+ }
+
+ if (format) {
+ getname(type, &name, &descr);
+ fdtp = get_fmt(format, type);
+ if (fdtp == 0)
+ errx(EX_USAGE,
+ "unknown format %d KB for drive type %s",
+ format, name);
+ ft = *fdtp;
+ }
+
+ if (fmtstring) {
+ parse_fmt(fmtstring, type, ft, &newft);
+ ft = newft;
+ }
+
+ if (showfmt) {
+ if (verbose)
+ printf("%s: %d KB media type, fmt = ",
+ argv[0], ft.size / 2);
+ print_fmt(ft);
+ return (0);
+ }
+
+ if (format || fmtstring) {
+ if (ioctl(fd, FD_STYPE, &ft) == -1)
+ err(EX_OSERR, "ioctl(FD_STYPE)");
+ return (0);
+ }
+
+ if (debug != -1) {
+ if (ioctl(fd, FD_DEBUG, &debug) == -1)
+ err(EX_OSERR, "ioctl(FD_DEBUG)");
+ return (0);
+ }
+
+ return 0;
+}
diff --git a/usr.sbin/fdformat/Makefile b/usr.sbin/fdformat/Makefile
new file mode 100644
index 0000000..8cc164b
--- /dev/null
+++ b/usr.sbin/fdformat/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../fdread
+
+PROG= fdformat
+SRCS= fdformat.c fdutil.c
+
+WARNS?= 2
+CFLAGS+= -I${.CURDIR}/../fdread
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/fdformat/fdformat.1 b/usr.sbin/fdformat/fdformat.1
new file mode 100644
index 0000000..92dae57
--- /dev/null
+++ b/usr.sbin/fdformat/fdformat.1
@@ -0,0 +1,178 @@
+.\" Copyright (C) 1993, 1994, 1995, 2001 by Joerg Wunsch, Dresden
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
+.\" OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+.\" DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
+.\" INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+.\" (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+.\" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+.\" IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd December 25, 2001
+.Os
+.Dt FDFORMAT 1
+.Sh NAME
+.Nm fdformat
+.Nd format floppy disks
+.Sh SYNOPSIS
+.Nm
+.Op Fl F Ar fill
+.Op Fl f Ar fmt
+.Op Fl s Ar fmtstr
+.Op Fl nqvy
+.Ar device
+.Sh DESCRIPTION
+The
+.Nm
+utility formats a floppy disk at
+.Ar device ,
+where
+.Ar device
+may either be given as a full path
+name of a device node for a floppy disk drive
+(e.g.\&
+.Pa /dev/fd0 ) ,
+or using an abbreviated name that will be looked up
+under
+.Pa /dev
+(e.g.\&
+.Dq Li fd0 ) .
+.Pp
+The options are as follows:
+.Bl -tag -width ".Fl s Ar fmtstr"
+.It Fl F Ar fill
+Use
+.Ar fill
+as the fill byte for newly formatted sectors.
+.Ar fill
+must be a number in the range 0 through 255 using common C
+language notation.
+The default value is
+.Dq Li 0xf5 .
+.It Fl f Ar fmt
+Specify the density settings for a
+.Ar fmt
+kilobyte format, as described in
+.Xr fdcontrol 8 .
+.It Fl s Ar fmtstr
+Specify the density settings using explicit parameters, as
+described in
+.Xr fdcontrol 8 .
+.It Fl n
+Do not verify floppy after formatting.
+.It Fl q
+Suppress any normal output from the command, and do not ask the
+user for a confirmation whether to format the floppy disk at
+.Ar device .
+.It Fl v
+Do not format, verify only.
+.It Fl y
+Do not ask for confirmation whether to format the floppy disk but
+still report formatting status.
+.El
+.Pp
+For non-autoselecting subdevices, neither
+.Fl f Ar fmt
+nor
+.Fl s Ar fmtstr
+may be specified, since the preconfigured media density settings
+from the kernel driver will always be used.
+However, if
+.Ar device
+is a device with automatic media density selection (see
+.Xr fdc 4 ) ,
+both methods can be used to override the density settings for the
+newly formatted medium (without permanently changing the density
+settings of
+.Ar device ) .
+.Pp
+If the
+.Fl q
+flag has not been specified, the user is asked for a confirmation
+of the intended formatting process.
+In order to continue, an answer
+of
+.Ql y
+must be given.
+.Pp
+Note that
+.Nm
+does only perform low-level formatting.
+In order to create
+a file system on the medium, see the commands
+.Xr newfs 8
+for a
+.Tn UFS
+file system, or
+.Xr newfs_msdos 8
+for an
+.Tn MS-DOS
+(FAT)
+file system.
+.Sh DIAGNOSTICS
+Unless
+.Fl q
+has been specified, a single letter is printed to standard output
+to inform the user about the progress of work.
+First, an
+.Ql F
+is printed when the track is being formatted, then a
+.Ql V
+while it is being verified, and if an error has been detected, it
+will finally change to
+.Ql E .
+Detailed status information (cylinder, head and sector number, and the
+exact cause of the error) will be printed for up to 10 errors after the
+entire formatting process has completed.
+.Sh DIAGNOSTICS
+An exit status of 0 is returned upon successful operation.
+Exit status
+1 is returned on any errors during floppy formatting, and an exit status
+of 2 reflects invalid arguments given to the program (along with an
+appropriate information written to diagnostic output).
+.Sh SEE ALSO
+.Xr fdc 4 ,
+.Xr fdcontrol 8 ,
+.Xr newfs 8 ,
+.Xr newfs_msdos 8
+.Sh HISTORY
+The
+.Nm
+utility
+has been developed for
+.Bx 386 0.1
+and upgraded to the new
+.Xr fdc 4
+floppy disk driver.
+It later became part of the
+.Fx 1.1
+system.
+Starting with
+.Fx 5.0 ,
+it uses the unified density specifications as described in
+.Xr fdcontrol 8 .
+.Sh AUTHORS
+.An -nosplit
+The program has been contributed by
+.An J\(:org Wunsch ,
+Dresden, with changes by
+.An Serge Vakulenko
+and
+.An Andrey A. Chernov ,
+Moscow.
diff --git a/usr.sbin/fdformat/fdformat.c b/usr.sbin/fdformat/fdformat.c
new file mode 100644
index 0000000..0e624de
--- /dev/null
+++ b/usr.sbin/fdformat/fdformat.c
@@ -0,0 +1,371 @@
+/*
+ * Copyright (C) 1992-1994,2001 by Joerg Wunsch, Dresden
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/fdcio.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "fdutil.h"
+
+static void
+format_track(int fd, int cyl, int secs, int head, int rate,
+ int gaplen, int secsize, int fill, int interleave,
+ int offset)
+{
+ struct fd_formb f;
+ int i, j, il[FD_MAX_NSEC + 1];
+
+ memset(il, 0, sizeof il);
+ for(j = 0, i = 1 + offset; i <= secs + offset; i++) {
+ while(il[(j % secs) + 1])
+ j++;
+ il[(j % secs) + 1] = i;
+ j += interleave;
+ }
+
+ f.format_version = FD_FORMAT_VERSION;
+ f.head = head;
+ f.cyl = cyl;
+ f.transfer_rate = rate;
+
+ f.fd_formb_secshift = secsize;
+ f.fd_formb_nsecs = secs;
+ f.fd_formb_gaplen = gaplen;
+ f.fd_formb_fillbyte = fill;
+ for(i = 0; i < secs; i++) {
+ f.fd_formb_cylno(i) = cyl;
+ f.fd_formb_headno(i) = head;
+ f.fd_formb_secno(i) = il[i+1];
+ f.fd_formb_secsize(i) = secsize;
+ }
+ if(ioctl(fd, FD_FORM, (caddr_t)&f) < 0)
+ err(EX_OSERR, "ioctl(FD_FORM)");
+}
+
+static int
+verify_track(int fd, int track, int tracksize)
+{
+ static char *buf;
+ static int bufsz;
+ int fdopts = -1, ofdopts, rv = 0;
+
+ if (ioctl(fd, FD_GOPTS, &fdopts) < 0)
+ warn("warning: ioctl(FD_GOPTS)");
+ else {
+ ofdopts = fdopts;
+ fdopts |= FDOPT_NORETRY;
+ (void)ioctl(fd, FD_SOPTS, &fdopts);
+ }
+
+ if (bufsz < tracksize)
+ buf = realloc(buf, bufsz = tracksize);
+ if (buf == 0)
+ errx(EX_UNAVAILABLE, "out of memory");
+ if (lseek (fd, (long) track * tracksize, 0) < 0)
+ rv = -1;
+ /* try twice reading it, without using the normal retrier */
+ else if (read (fd, buf, tracksize) != tracksize
+ && read (fd, buf, tracksize) != tracksize)
+ rv = -1;
+ if (fdopts != -1)
+ (void)ioctl(fd, FD_SOPTS, &ofdopts);
+ return (rv);
+}
+
+static void
+usage (void)
+{
+ errx(EX_USAGE,
+ "usage: fdformat [-F fill] [-f fmt] [-s fmtstr] [-nqvy] device");
+}
+
+static int
+yes (void)
+{
+ char reply[256], *p;
+
+ reply[sizeof(reply) - 1] = 0;
+ for (;;) {
+ fflush(stdout);
+ if (!fgets (reply, sizeof(reply) - 1, stdin))
+ return (0);
+ for (p=reply; *p==' ' || *p=='\t'; ++p)
+ continue;
+ if (*p=='y' || *p=='Y')
+ return (1);
+ if (*p=='n' || *p=='N' || *p=='\n' || *p=='\r')
+ return (0);
+ printf("Answer `yes' or `no': ");
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ enum fd_drivetype type;
+ struct fd_type fdt, newft, *fdtp;
+ struct stat sb;
+#define MAXPRINTERRS 10
+ struct fdc_status fdcs[MAXPRINTERRS];
+ int format, fill, quiet, verify, verify_only, confirm;
+ int fd, c, i, track, error, tracks_per_dot, bytes_per_track, errs;
+ int fdopts, flags;
+ char *fmtstring, *device;
+ const char *name, *descr;
+
+ format = quiet = verify_only = confirm = 0;
+ verify = 1;
+ fill = 0xf6;
+ fmtstring = 0;
+
+ while((c = getopt(argc, argv, "F:f:nqs:vy")) != -1)
+ switch(c) {
+ case 'F': /* fill byte */
+ if (getnum(optarg, &fill)) {
+ fprintf(stderr,
+ "Bad argument %s to -F option; must be numeric\n",
+ optarg);
+ usage();
+ }
+ break;
+
+ case 'f': /* format in kilobytes */
+ if (getnum(optarg, &format)) {
+ fprintf(stderr,
+ "Bad argument %s to -f option; must be numeric\n",
+ optarg);
+ usage();
+ }
+ break;
+
+ case 'n': /* don't verify */
+ verify = 0;
+ break;
+
+ case 'q': /* quiet */
+ quiet = 1;
+ break;
+
+ case 's': /* format string with detailed options */
+ fmtstring = optarg;
+ break;
+
+ case 'v': /* verify only */
+ verify = 1;
+ verify_only = 1;
+ break;
+
+ case 'y': /* confirm */
+ confirm = 1;
+ break;
+
+ default:
+ usage();
+ }
+
+ if(optind != argc - 1)
+ usage();
+
+ if (stat(argv[optind], &sb) == -1 && errno == ENOENT) {
+ /* try prepending _PATH_DEV */
+ device = malloc(strlen(argv[optind] + sizeof _PATH_DEV + 1));
+ if (device == 0)
+ errx(EX_UNAVAILABLE, "out of memory");
+ strcpy(device, _PATH_DEV);
+ strcat(device, argv[optind]);
+ if (stat(device, &sb) == -1) {
+ free(device);
+ device = argv[optind]; /* let it fail below */
+ }
+ } else {
+ device = argv[optind];
+ }
+
+ if ((fd = open(device, O_RDWR | O_NONBLOCK)) < 0)
+ err(EX_OSERR, "open(%s)", device);
+
+ /*
+ * Device initialization.
+ *
+ * First, get the device type descriptor. This tells us about
+ * the media geometry data we need to format a medium. It also
+ * lets us know quickly whether the device name actually points
+ * to a floppy disk drive.
+ *
+ * Then, obtain any drive options. We're mainly interested to
+ * see whether we're currently working on a device with media
+ * density autoselection (FDOPT_AUTOSEL). Then, we add the
+ * device option to tell the kernel not to log media errors,
+ * since we can handle them ourselves. If the device does
+ * media density autoselection, we then need to set the device
+ * type appropriately, since by opening with O_NONBLOCK we
+ * told the driver to bypass media autoselection (otherwise we
+ * wouldn't stand a chance to format an unformatted or damaged
+ * medium). We do not attempt to set the media type on any
+ * other devices since this is a privileged operation. For the
+ * same reason, specifying -f and -s options is only possible
+ * for autoselecting devices.
+ *
+ * Finally, we are ready to turn off O_NONBLOCK, and start to
+ * actually format something.
+ */
+ if(ioctl(fd, FD_GTYPE, &fdt) < 0)
+ errx(EX_OSERR, "not a floppy disk: %s", device);
+ if (ioctl(fd, FD_GDTYPE, &type) == -1)
+ err(EX_OSERR, "ioctl(FD_GDTYPE)");
+ if (ioctl(fd, FD_GOPTS, &fdopts) == -1)
+ err(EX_OSERR, "ioctl(FD_GOPTS)");
+ fdopts |= FDOPT_NOERRLOG;
+ if (ioctl(fd, FD_SOPTS, &fdopts) == -1)
+ err(EX_OSERR, "ioctl(FD_SOPTS, FDOPT_NOERRLOG)");
+ if (format) {
+ getname(type, &name, &descr);
+ fdtp = get_fmt(format, type);
+ if (fdtp == 0)
+ errx(EX_USAGE,
+ "unknown format %d KB for drive type %s",
+ format, name);
+ fdt = *fdtp;
+ }
+ if (fmtstring) {
+ parse_fmt(fmtstring, type, fdt, &newft);
+ fdt = newft;
+ }
+ if (fdopts & FDOPT_AUTOSEL) {
+ if (ioctl(fd, FD_STYPE, &fdt) < 0)
+ err(EX_OSERR, "ioctl(FD_STYPE)");
+ } else if (fmtstring || format) {
+ errx(EX_USAGE,
+ "-f fmt or -s fmtstr is only allowed for autoselecting devices");
+ }
+ if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
+ err(EX_OSERR, "fcntl(F_GETFL)");
+ flags &= ~O_NONBLOCK;
+ if (fcntl(fd, F_SETFL, flags) == -1)
+ err(EX_OSERR, "fcntl(F_SETFL)");
+
+ bytes_per_track = fdt.sectrac * (128 << fdt.secsize);
+
+ /* XXX 20/40 = 0.5 */
+ tracks_per_dot = (fdt.tracks * fdt.heads + 20) / 40;
+
+ if (verify_only) {
+ if(!quiet)
+ printf("Verify %dK floppy `%s'.\n",
+ fdt.tracks * fdt.heads * bytes_per_track / 1024,
+ device);
+ }
+ else if(!quiet && !confirm) {
+ printf("Format %dK floppy `%s'? (y/n): ",
+ fdt.tracks * fdt.heads * bytes_per_track / 1024,
+ device);
+ if(!yes()) {
+ printf("Not confirmed.\n");
+ return (EX_UNAVAILABLE);
+ }
+ }
+
+ /*
+ * Formatting.
+ */
+ if(!quiet) {
+ printf("Processing ");
+ for (i = 0; i < (fdt.tracks * fdt.heads) / tracks_per_dot; i++)
+ putchar('-');
+ printf("\rProcessing ");
+ fflush(stdout);
+ }
+
+ error = errs = 0;
+
+ for (track = 0; track < fdt.tracks * fdt.heads; track++) {
+ if (!verify_only) {
+ format_track(fd, track / fdt.heads, fdt.sectrac,
+ track % fdt.heads, fdt.trans, fdt.f_gap,
+ fdt.secsize, fill, fdt.f_inter,
+ track % fdt.heads? fdt.offset_side2: 0);
+ if(!quiet && !((track + 1) % tracks_per_dot)) {
+ putchar('F');
+ fflush(stdout);
+ }
+ }
+ if (verify) {
+ if (verify_track(fd, track, bytes_per_track) < 0) {
+ error = 1;
+ if (errs < MAXPRINTERRS && errno == EIO) {
+ if (ioctl(fd, FD_GSTAT, fdcs + errs) ==
+ -1)
+ errx(EX_IOERR,
+ "floppy IO error, but no FDC status");
+ errs++;
+ }
+ }
+ if(!quiet && !((track + 1) % tracks_per_dot)) {
+ if (!verify_only)
+ putchar('\b');
+ if (error) {
+ putchar('E');
+ error = 0;
+ }
+ else
+ putchar('V');
+ fflush(stdout);
+ }
+ }
+ }
+ if(!quiet)
+ printf(" done.\n");
+
+ if (!quiet && errs) {
+ fflush(stdout);
+ fprintf(stderr, "Errors encountered:\nCyl Head Sect Error\n");
+ for (i = 0; i < errs && i < MAXPRINTERRS; i++) {
+ fprintf(stderr, " %2d %2d %2d ",
+ fdcs[i].status[3], fdcs[i].status[4],
+ fdcs[i].status[5]);
+ printstatus(fdcs + i, 1);
+ putc('\n', stderr);
+ }
+ if (errs >= MAXPRINTERRS)
+ fprintf(stderr, "(Further errors not printed.)\n");
+ }
+
+ return errs != 0;
+}
diff --git a/usr.sbin/fdread/Makefile b/usr.sbin/fdread/Makefile
new file mode 100644
index 0000000..05e588d
--- /dev/null
+++ b/usr.sbin/fdread/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+PROG= fdread
+SRCS= fdread.c fdutil.c
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/fdread/fdread.1 b/usr.sbin/fdread/fdread.1
new file mode 100644
index 0000000..438a268
--- /dev/null
+++ b/usr.sbin/fdread/fdread.1
@@ -0,0 +1,222 @@
+.\"
+.\" Copyright (c) 2001 Joerg Wunsch
+.\"
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.\"
+.Dd May 14, 2001
+.Os
+.Dt FDREAD 1
+.Sh NAME
+.Nm fdread
+.Nd read floppy disks
+.Sh SYNOPSIS
+.Nm
+.Op Fl qr
+.Op Fl d Ar device
+.Op Fl f Ar fillbyte
+.Op Fl o Ar file
+.Nm
+.Op Fl d Ar device
+.Fl I Ar numsects
+.Op Fl t Ar trackno
+.Sh DESCRIPTION
+The
+.Nm
+utility reads floppy disks. Effective read blocking based on the track
+size is performed, and floppy-specific error recovery of otherwise
+bad blocks can be enabled.
+.Pp
+The
+.Nm
+utility
+will always read an entire floppy medium, and write its contents to
+the respective output file. Unlike other tools like
+.Xr dd 1 ,
+.Nm
+automatically uses a read block size that is more efficient than
+reading single blocks (usually one track of data at a time), but
+falls back to reading single floppy sectors in case of an input/output
+error occurred, in order to obtain as much valid data as possible.
+While
+.Nm
+is working, kernel error reporting for floppy errors is turned off, so
+the console and/or syslog are not flooded with kernel error messages.
+.Pp
+The
+.Nm
+utility accepts the following options:
+.Bl -tag -width indent
+.It Fl q
+Turn on quiet mode. By default, the medium parameters of the device
+are being written to standard error output, progress will be indicated
+by the approximate number of kilobytes read so far, and errors will be
+printed out in detail, including the information about the location of
+recovered data in the output. In quiet mode, none of these messages
+will be generated.
+.It Fl r
+Enable error recovery. By default,
+.Nm
+stops after the first unrecovered read error, much like
+.Xr dd 1
+does. In recovery mode, however, one of two recovery actions will be
+taken:
+.Bl -bullet
+.It
+If the error was a CRC error in the data field, the
+kernel is told to ignore the error, and data are transferred to the
+output file anyway.
+.Bf -emphasis
+Note that this will cause the erroneous data
+to be included in the output file!
+.Ef
+Still, this is the best recovery action that can be taken at all.
+.It
+All other errors are really fatal (usually, the FDC didn't find the
+sector ID fields), thus a dummy block with fill
+bytes will be included in the output file.
+.El
+.Pp
+Unless operating in quiet mode, the action taken and the location of
+the error in the output file will be displayed.
+.It Fl d Ar device
+Specify the input floppy device, defaulting to
+.Pa /dev/fd0 .
+The parameter
+.Ar device
+must be a valid floppy disk device.
+.It Fl f Ar fillbyte
+Value of the fill byte used for dummy blocks in the output file in
+recovery mode. Defaults to
+.Ql 0xf0 .
+(Mnemonic:
+.Dq foo . )
+The value can be specified using the usual C language notation of
+the number base.
+.It Fl o Ar file
+Specify the output file to be
+.Ar file .
+By default, the data will be written to standard output.
+.It Fl I Ar numsects
+Read
+.Ar numsects
+sector ID fields, and write out their contents to standard output.
+Each sector ID field contains recorded values for the cylinder number
+.Pq Ql C ,
+the head number
+.Pq Ql H ,
+the record number (sector number starting with 1)
+.Pq Ql R ,
+and the
+.Em sector shift value
+(0 = 128 bytes, 1 = 256 bytes, 2 = 512 bytes, 3 = 1024 bytes)
+.Pq Ql N .
+The
+.Fl I
+option is mutually exclusive with all other options except
+.Fl d Ar device
+and
+.Fl t Ar trackno .
+.It Fl t Ar trackno
+Specify the track number (cylinder number * number of heads + head
+number) to read the sector ID fields from; only allowed together with
+the
+.Fl I Ar numsects
+option.
+.El
+.Sh FILES
+.Bl -tag -width /dev/fd0
+.It Pa /dev/fd0
+Default device to read from.
+.El
+.Sh DIAGNOSTICS
+The
+.Nm
+utility sets the exit value according to
+.Xr sysexits 3 .
+In recovery mode, the exit value will be set to
+.Dv EX_IOERR
+if any error occurred during processing (even in quiet mode).
+.Pp
+Unless running in quiet mode, upon encountering an error, the status
+of the floppy disc controller (FDC) will be printed out, both in
+hexadecimal form, followed by a textual description that translates
+those values into a human-readable form for the most common error
+cases that can happen in a PC environment.
+.Pp
+The FDC error status includes the three FDC status registers
+.Ql ST0 ,
+.Ql ST1 ,
+and
+.Ql ST2 ,
+as well as the location of the error (physical cylinder, head, and sector
+number, plus the
+.Dq sector shift value ,
+respectively). See the manual for the NE765 or compatible for details
+about the status register contents.
+.Pp
+The FDC's status is then examined to determine whether the error is
+deemed to be recoverable. If error recovery was requested, the
+location of the bad block in the output file is indicated by its
+(hexadecimal) bounds. Also, a summary line indicating the total number
+of transfer errors will be printed before exiting.
+.Sh SEE ALSO
+.Xr dd 1 ,
+.Xr fdwrite 1 ,
+.Xr sysexits 3 ,
+.Xr fdc 4 ,
+.Xr fdcontrol 8
+.Sh HISTORY
+The
+.Nm
+utility was written mainly to provide a means of recovering at least some of
+the data on bad media, and to obviate the need to invoke
+.Xr dd 1
+with too many hard to memorize options that might be useful to handle
+a floppy.
+.Pp
+The command appeared in
+.Fx 5.0 .
+.Sh AUTHORS
+Program and man page by
+.An J\(:org Wunsch .
+.Sh BUGS
+Concurrent traffic on the second floppy drive located at the same FDC
+will make error recovery attempts pointless, since the FDC status
+obtained after a read error occurred cannot be guaranteed to actually
+belong to the erroneous transfer. Thus using option
+.Fl r
+is only reliable if
+.Ar device
+is the only active drive on that controller.
+.Pp
+No attempt beyond the floppy error retry mechanism of
+.Xr fdc 4
+is made in order to see whether bad sectors could still be read
+without errors by trying multiple times.
+.Pp
+Bits that are (no longer) available on the floppy medium cannot be
+guessed by
+.Nm .
diff --git a/usr.sbin/fdread/fdread.c b/usr.sbin/fdread/fdread.c
new file mode 100644
index 0000000..85336c2
--- /dev/null
+++ b/usr.sbin/fdread/fdread.c
@@ -0,0 +1,337 @@
+/*
+ * Copyright (c) 2001 Joerg Wunsch
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/fdcio.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include <dev/ic/nec765.h>
+
+#include "fdutil.h"
+
+int quiet, recover;
+unsigned char fillbyte = 0xf0; /* "foo" */
+
+int doread(int fd, FILE *of, const char *devname);
+int doreadid(int fd, unsigned int numids, unsigned int trackno);
+void usage(void);
+
+void
+usage(void)
+{
+
+ errx(EX_USAGE,
+ "usage: fdread [-qr] [-d device] [-f fillbyte]\n"
+ " fdread [-d device] -I numids [-t trackno]");
+}
+
+
+int
+main(int argc, char **argv)
+{
+ int c, errs = 0;
+ unsigned int numids = 0, trackno = 0;
+ const char *fname = 0, *devname = "/dev/fd0";
+ char *cp;
+ FILE *of = stdout;
+ int fd;
+ unsigned long ul;
+
+ while ((c = getopt(argc, argv, "d:f:I:o:qrt:")) != -1)
+ switch (c) {
+ case 'd':
+ devname = optarg;
+ break;
+
+ case 'f':
+ ul = strtoul(optarg, &cp, 0);
+ if (*cp != '\0') {
+ fprintf(stderr,
+ "Bad argument %s to -f option; must be numeric\n",
+ optarg);
+ usage();
+ }
+ if (ul > 0xff)
+ warnx(
+ "Warning: fillbyte %#lx too large, truncating\n",
+ ul);
+ fillbyte = ul & 0xff;
+ break;
+
+ case 'I':
+ ul = strtoul(optarg, &cp, 0);
+ if (*cp != '\0') {
+ fprintf(stderr,
+ "Bad argument %s to -I option; must be numeric\n",
+ optarg);
+ usage();
+ }
+ numids = ul;
+ break;
+
+ case 'o':
+ fname = optarg;
+ break;
+
+ case 'q':
+ quiet++;
+ break;
+
+ case 'r':
+ recover++;
+ break;
+
+ case 't':
+ ul = strtoul(optarg, &cp, 0);
+ if (*cp != '\0') {
+ fprintf(stderr,
+ "Bad argument %s to -t option; must be numeric\n",
+ optarg);
+ usage();
+ }
+ trackno = ul;
+ break;
+
+ default:
+ errs++;
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 0 || errs)
+ usage();
+ /* check for mutually exclusive options */
+ if (numids) {
+ if (fname || quiet || recover)
+ usage();
+ } else {
+ if (trackno)
+ usage();
+ }
+
+ if (fname) {
+ if ((of = fopen(fname, "w")) == NULL)
+ err(EX_OSERR, "cannot create output file %s", fname);
+ }
+
+ if ((fd = open(devname, O_RDONLY)) == -1)
+ err(EX_OSERR, "cannot open device %s", devname);
+
+ return (numids? doreadid(fd, numids, trackno): doread(fd, of, devname));
+}
+
+int
+doread(int fd, FILE *of, const char *devname)
+{
+ char *trackbuf;
+ int rv, fdopts, recoverable, nerrs = 0;
+ unsigned int nbytes, tracksize, mediasize, secsize, n;
+ struct fdc_status fdcs;
+ struct fd_type fdt;
+
+ if (ioctl(fd, FD_GTYPE, &fdt) == -1)
+ err(EX_OSERR, "ioctl(FD_GTYPE) failed -- not a floppy?");
+ fdopts = FDOPT_NOERRLOG;
+ if (ioctl(fd, FD_SOPTS, &fdopts) == -1)
+ err(EX_OSERR, "ioctl(FD_SOPTS, FDOPT_NOERRLOG)");
+
+ secsize = 128 << fdt.secsize;
+ tracksize = fdt.sectrac * secsize;
+ mediasize = tracksize * fdt.tracks * fdt.heads;
+ if ((trackbuf = malloc(tracksize)) == 0)
+ errx(EX_TEMPFAIL, "out of memory");
+
+ if (!quiet)
+ fprintf(stderr, "Reading %d * %d * %d * %d medium at %s\n",
+ fdt.tracks, fdt.heads, fdt.sectrac, secsize, devname);
+
+ for (nbytes = 0; nbytes < mediasize;) {
+ if (lseek(fd, nbytes, SEEK_SET) != nbytes)
+ err(EX_OSERR, "cannot lseek()");
+ rv = read(fd, trackbuf, tracksize);
+ if (rv == 0) {
+ /* EOF? */
+ warnx("premature EOF after %u bytes", nbytes);
+ return (EX_OK);
+ }
+ if (rv == tracksize) {
+ nbytes += rv;
+ if (!quiet)
+ fprintf(stderr, "%5d KB\r", nbytes / 1024);
+ fwrite(trackbuf, sizeof(unsigned char), rv, of);
+ fflush(of);
+ continue;
+ }
+ if (rv < tracksize) {
+ /* should not happen */
+ nbytes += rv;
+ if (!quiet)
+ fprintf(stderr, "\nshort after %5d KB\r",
+ nbytes / 1024);
+ fwrite(trackbuf, sizeof(unsigned char), rv, of);
+ fflush(of);
+ continue;
+ }
+ if (rv == -1) {
+ /* fall back reading one sector at a time */
+ for (n = 0; n < tracksize; n += secsize) {
+ if (lseek(fd, nbytes, SEEK_SET) != nbytes)
+ err(EX_OSERR, "cannot lseek()");
+ rv = read(fd, trackbuf, secsize);
+ if (rv == secsize) {
+ nbytes += rv;
+ if (!quiet)
+ fprintf(stderr, "%5d KB\r",
+ nbytes / 1024);
+ fwrite(trackbuf, sizeof(unsigned char),
+ rv, of);
+ fflush(of);
+ continue;
+ }
+ if (rv == -1) {
+ if (errno != EIO) {
+ if (!quiet)
+ putc('\n', stderr);
+ perror("non-IO error");
+ return (EX_OSERR);
+ }
+ if (ioctl(fd, FD_GSTAT, &fdcs) == -1)
+ errx(EX_IOERR,
+ "floppy IO error, but no FDC status");
+ nerrs++;
+ recoverable = fdcs.status[2] &
+ NE7_ST2_DD;
+ if (!quiet) {
+ printstatus(&fdcs, 0);
+ fputs(" (", stderr);
+ if (!recoverable)
+ fputs("not ", stderr);
+ fputs("recoverable)", stderr);
+ }
+ if (!recover) {
+ if (!quiet)
+ putc('\n', stderr);
+ return (EX_IOERR);
+ }
+ memset(trackbuf, fillbyte, secsize);
+ if (recoverable) {
+ fdopts |= FDOPT_NOERROR;
+ if (ioctl(fd, FD_SOPTS,
+ &fdopts) == -1)
+ err(EX_OSERR,
+ "ioctl(fd, FD_SOPTS, FDOPT_NOERROR)");
+ rv = read(fd, trackbuf,
+ secsize);
+ if (rv != secsize)
+ err(EX_IOERR,
+ "read() with FDOPT_NOERROR still fails");
+ fdopts &= ~FDOPT_NOERROR;
+ (void)ioctl(fd, FD_SOPTS,
+ &fdopts);
+ }
+ if (!quiet) {
+ if (recoverable)
+ fprintf(stderr,
+ ": recovered");
+ else
+ fprintf(stderr,
+ ": dummy");
+ fprintf(stderr,
+ " data @ %#x ... %#x\n",
+ nbytes,
+ nbytes + secsize - 1);
+ }
+ nbytes += secsize;
+ fwrite(trackbuf, sizeof(unsigned char),
+ secsize, of);
+ fflush(of);
+ continue;
+ }
+ errx(EX_OSERR, "unexpected read() result: %d",
+ rv);
+ }
+ }
+ }
+ if (!quiet) {
+ putc('\n', stderr);
+ if (nerrs)
+ fprintf(stderr, "%d error%s\n",
+ nerrs, nerrs > 1? "s": "");
+ }
+
+ return (nerrs? EX_IOERR: EX_OK);
+}
+
+int
+doreadid(int fd, unsigned int numids, unsigned int trackno)
+{
+ int rv = 0, status, fdopts;
+ unsigned int i;
+ struct fdc_readid info;
+ struct fdc_status fdcs;
+ struct fd_type fdt;
+
+ if (ioctl(fd, FD_GTYPE, &fdt) == -1)
+ err(EX_OSERR, "ioctl(FD_GTYPE) failed -- not a floppy?");
+
+ fdopts = FDOPT_NOERRLOG;
+ if (ioctl(fd, FD_SOPTS, &fdopts) == -1)
+ err(EX_OSERR, "ioctl(FD_SOPTS, FDOPT_NOERRLOG)");
+
+ for (i = 0; i < numids; i++) {
+ info.cyl = trackno / fdt.heads;
+ info.head = fdt.heads > 1? trackno % fdt.heads: 0;
+ if ((status = ioctl(fd, FD_READID, &info)) == 0) {
+ printf("C = %d, H = %d, R = %d, N = %d\n",
+ info.cyl, info.head, info.sec, info.secshift);
+ } else {
+ if (errno != EIO) {
+ perror("non-IO error");
+ return (EX_OSERR);
+ }
+ if (ioctl(fd, FD_GSTAT, &fdcs) == -1)
+ errx(EX_IOERR,
+ "floppy IO error, but no FDC status");
+ printstatus(&fdcs, 0);
+ putc('\n', stderr);
+ rv = EX_IOERR;
+ }
+ }
+
+ return (rv);
+}
diff --git a/usr.sbin/fdread/fdutil.c b/usr.sbin/fdread/fdutil.c
new file mode 100644
index 0000000..9990790
--- /dev/null
+++ b/usr.sbin/fdread/fdutil.c
@@ -0,0 +1,466 @@
+/*
+ * Copyright (c) 2001 Joerg Wunsch
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <dev/ic/nec765.h>
+
+#include <sys/fdcio.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+
+#include "fdutil.h"
+
+/*
+ * Decode the FDC status pointed to by `fdcsp', and print a textual
+ * translation to stderr. If `terse' is false, the numerical FDC
+ * register status is printed, too.
+ */
+void
+printstatus(struct fdc_status *fdcsp, int terse)
+{
+ char msgbuf[100];
+
+ if (!terse)
+ fprintf(stderr,
+ "\nFDC status ST0=%#x ST1=%#x ST2=%#x C=%u H=%u R=%u N=%u:\n",
+ fdcsp->status[0] & 0xff,
+ fdcsp->status[1] & 0xff,
+ fdcsp->status[2] & 0xff,
+ fdcsp->status[3] & 0xff,
+ fdcsp->status[4] & 0xff,
+ fdcsp->status[5] & 0xff,
+ fdcsp->status[6] & 0xff);
+
+ if ((fdcsp->status[0] & NE7_ST0_IC_RC) != NE7_ST0_IC_AT) {
+ sprintf(msgbuf, "unexcpted interrupt code %#x",
+ fdcsp->status[0] & NE7_ST0_IC_RC);
+ } else {
+ strcpy(msgbuf, "unexpected error code in ST1/ST2");
+
+ if (fdcsp->status[1] & NE7_ST1_EN)
+ strcpy(msgbuf, "end of cylinder (wrong format)");
+ else if (fdcsp->status[1] & NE7_ST1_DE) {
+ if (fdcsp->status[2] & NE7_ST2_DD)
+ strcpy(msgbuf, "CRC error in data field");
+ else
+ strcpy(msgbuf, "CRC error in ID field");
+ } else if (fdcsp->status[1] & NE7_ST1_MA) {
+ if (fdcsp->status[2] & NE7_ST2_MD)
+ strcpy(msgbuf, "no address mark in data field");
+ else
+ strcpy(msgbuf, "no address mark in ID field");
+ } else if (fdcsp->status[2] & NE7_ST2_WC)
+ strcpy(msgbuf, "wrong cylinder (format mismatch)");
+ else if (fdcsp->status[1] & NE7_ST1_ND)
+ strcpy(msgbuf, "no data (sector not found)");
+ }
+ fputs(msgbuf, stderr);
+}
+
+static struct fd_type fd_types_288m[] =
+{
+#if 0
+{ 36,2,0xFF,0x1B,80,5760,FDC_1MBPS, 2,0x4C,1,1,FL_MFM|FL_PERPND } /*2.88M*/
+#endif
+{ 21,2,0xFF,0x04,82,3444,FDC_500KBPS,2,0x0C,2,0,FL_MFM }, /* 1.72M */
+{ 18,2,0xFF,0x1B,82,2952,FDC_500KBPS,2,0x6C,1,0,FL_MFM }, /* 1.48M */
+{ 18,2,0xFF,0x1B,80,2880,FDC_500KBPS,2,0x6C,1,0,FL_MFM }, /* 1.44M */
+{ 15,2,0xFF,0x1B,80,2400,FDC_500KBPS,2,0x54,1,0,FL_MFM }, /* 1.2M */
+{ 10,2,0xFF,0x10,82,1640,FDC_250KBPS,2,0x2E,1,0,FL_MFM }, /* 820K */
+{ 10,2,0xFF,0x10,80,1600,FDC_250KBPS,2,0x2E,1,0,FL_MFM }, /* 800K */
+{ 9,2,0xFF,0x20,80,1440,FDC_250KBPS,2,0x50,1,0,FL_MFM }, /* 720K */
+};
+
+static struct fd_type fd_types_144m[] =
+{
+{ 21,2,0xFF,0x04,82,3444,FDC_500KBPS,2,0x0C,2,0,FL_MFM }, /* 1.72M */
+{ 18,2,0xFF,0x1B,82,2952,FDC_500KBPS,2,0x6C,1,0,FL_MFM }, /* 1.48M */
+{ 18,2,0xFF,0x1B,80,2880,FDC_500KBPS,2,0x6C,1,0,FL_MFM }, /* 1.44M */
+{ 15,2,0xFF,0x1B,80,2400,FDC_500KBPS,2,0x54,1,0,FL_MFM }, /* 1.2M */
+{ 10,2,0xFF,0x10,82,1640,FDC_250KBPS,2,0x2E,1,0,FL_MFM }, /* 820K */
+{ 10,2,0xFF,0x10,80,1600,FDC_250KBPS,2,0x2E,1,0,FL_MFM }, /* 800K */
+{ 9,2,0xFF,0x20,80,1440,FDC_250KBPS,2,0x50,1,0,FL_MFM }, /* 720K */
+};
+
+static struct fd_type fd_types_12m[] =
+{
+{ 15,2,0xFF,0x1B,80,2400,FDC_500KBPS,2,0x54,1,0,FL_MFM }, /* 1.2M */
+{ 8,3,0xFF,0x35,77,1232,FDC_500KBPS,2,0x74,1,0,FL_MFM }, /* 1.23M */
+{ 18,2,0xFF,0x02,82,2952,FDC_500KBPS,2,0x02,2,0,FL_MFM }, /* 1.48M */
+{ 18,2,0xFF,0x02,80,2880,FDC_500KBPS,2,0x02,2,0,FL_MFM }, /* 1.44M */
+{ 10,2,0xFF,0x10,82,1640,FDC_300KBPS,2,0x2E,1,0,FL_MFM }, /* 820K */
+{ 10,2,0xFF,0x10,80,1600,FDC_300KBPS,2,0x2E,1,0,FL_MFM }, /* 800K */
+{ 9,2,0xFF,0x20,80,1440,FDC_300KBPS,2,0x50,1,0,FL_MFM }, /* 720K */
+{ 9,2,0xFF,0x23,40, 720,FDC_300KBPS,2,0x50,1,0,FL_MFM|FL_2STEP }, /* 360K */
+{ 8,2,0xFF,0x2A,80,1280,FDC_300KBPS,2,0x50,1,0,FL_MFM }, /* 640K */
+};
+
+static struct fd_type fd_types_720k[] =
+{
+{ 9,2,0xFF,0x20,80,1440,FDC_250KBPS,2,0x50,1,0,FL_MFM }, /* 720K */
+};
+
+static struct fd_type fd_types_360k[] =
+{
+{ 9,2,0xFF,0x2A,40, 720,FDC_250KBPS,2,0x50,1,0,FL_MFM }, /* 360K */
+};
+
+/*
+ * Parse a format string, and fill in the parameter pointed to by `out'.
+ *
+ * sectrac,secsize,datalen,gap,ncyls,speed,heads,f_gap,f_inter,offs2,flags[...]
+ *
+ * sectrac = sectors per track
+ * secsize = sector size in bytes
+ * datalen = length of sector if secsize == 128
+ * gap = gap length when reading
+ * ncyls = number of cylinders
+ * speed = transfer speed 250/300/500/1000 KB/s
+ * heads = number of heads
+ * f_gap = gap length when formatting
+ * f_inter = sector interleave when formatting
+ * offs2 = offset of sectors on side 2
+ * flags = +/-mfm | +/-2step | +/-perpend
+ * mfm - use MFM recording
+ * 2step - use 2 steps between cylinders
+ * perpend - user perpendicular (vertical) recording
+ *
+ * Any omitted value will be passed on from parameter `in'.
+ */
+void
+parse_fmt(const char *s, enum fd_drivetype type,
+ struct fd_type in, struct fd_type *out)
+{
+ int i, j;
+ const char *cp;
+ char *s1;
+
+ *out = in;
+
+ for (i = 0;; i++) {
+ if (s == 0)
+ break;
+
+ if ((cp = strchr(s, ',')) == 0) {
+ s1 = strdup(s);
+ if (s1 == NULL)
+ abort();
+ s = 0;
+ } else {
+ s1 = malloc(cp - s + 1);
+ if (s1 == NULL)
+ abort();
+ memcpy(s1, s, cp - s);
+ s1[cp - s] = 0;
+
+ s = cp + 1;
+ }
+ if (strlen(s1) == 0) {
+ free(s1);
+ continue;
+ }
+
+ switch (i) {
+ case 0: /* sectrac */
+ if (getnum(s1, &out->sectrac))
+ errx(EX_USAGE,
+ "bad numeric value for sectrac: %s", s1);
+ break;
+
+ case 1: /* secsize */
+ if (getnum(s1, &j))
+ errx(EX_USAGE,
+ "bad numeric value for secsize: %s", s1);
+ if (j == 128) out->secsize = 0;
+ else if (j == 256) out->secsize = 1;
+ else if (j == 512) out->secsize = 2;
+ else if (j == 1024) out->secsize = 3;
+ else
+ errx(EX_USAGE, "bad sector size %d", j);
+ break;
+
+ case 2: /* datalen */
+ if (getnum(s1, &j))
+ errx(EX_USAGE,
+ "bad numeric value for datalen: %s", s1);
+ if (j >= 256)
+ errx(EX_USAGE, "bad datalen %d", j);
+ out->datalen = j;
+ break;
+
+ case 3: /* gap */
+ if (getnum(s1, &out->gap))
+ errx(EX_USAGE,
+ "bad numeric value for gap: %s", s1);
+ break;
+
+ case 4: /* ncyls */
+ if (getnum(s1, &j))
+ errx(EX_USAGE,
+ "bad numeric value for ncyls: %s", s1);
+ if (j > 85)
+ errx(EX_USAGE, "bad # of cylinders %d", j);
+ out->tracks = j;
+ break;
+
+ case 5: /* speed */
+ if (getnum(s1, &j))
+ errx(EX_USAGE,
+ "bad numeric value for speed: %s", s1);
+ switch (type) {
+ default:
+ abort(); /* paranoia */
+
+ case FDT_360K:
+ case FDT_720K:
+ if (j == 250)
+ out->trans = FDC_250KBPS;
+ else {
+ badspeed:
+ errx(EX_USAGE, "bad speed %d", j);
+ }
+ break;
+
+ case FDT_12M:
+ if (j == 300)
+ out->trans = FDC_300KBPS;
+ else if (j == 500)
+ out->trans = FDC_500KBPS;
+ else
+ goto badspeed;
+ break;
+
+ case FDT_288M:
+ if (j == 1000)
+ out->trans = FDC_1MBPS;
+ /* FALLTHROUGH */
+ case FDT_144M:
+ if (j == 250)
+ out->trans = FDC_250KBPS;
+ else if (j == 500)
+ out->trans = FDC_500KBPS;
+ else
+ goto badspeed;
+ break;
+ }
+ break;
+
+ case 6: /* heads */
+ if (getnum(s1, &j))
+ errx(EX_USAGE,
+ "bad numeric value for heads: %s", s1);
+ if (j == 1 || j == 2)
+ out->heads = j;
+ else
+ errx(EX_USAGE, "bad # of heads %d", j);
+ break;
+
+ case 7: /* f_gap */
+ if (getnum(s1, &out->f_gap))
+ errx(EX_USAGE,
+ "bad numeric value for f_gap: %s", s1);
+ break;
+
+ case 8: /* f_inter */
+ if (getnum(s1, &out->f_inter))
+ errx(EX_USAGE,
+ "bad numeric value for f_inter: %s", s1);
+ break;
+
+ case 9: /* offs2 */
+ if (getnum(s1, &out->offset_side2))
+ errx(EX_USAGE,
+ "bad numeric value for offs2: %s", s1);
+ break;
+
+ default:
+ if (strcmp(s1, "+mfm") == 0)
+ out->flags |= FL_MFM;
+ else if (strcmp(s1, "-mfm") == 0)
+ out->flags &= ~FL_MFM;
+ else if (strcmp(s1, "+2step") == 0)
+ out->flags |= FL_2STEP;
+ else if (strcmp(s1, "-2step") == 0)
+ out->flags &= ~FL_2STEP;
+ else if (strcmp(s1, "+perpnd") == 0)
+ out->flags |= FL_PERPND;
+ else if (strcmp(s1, "-perpnd") == 0)
+ out->flags &= ~FL_PERPND;
+ else
+ errx(EX_USAGE, "bad flag: %s", s1);
+ break;
+ }
+ free(s1);
+ }
+
+ out->size = out->tracks * out->heads * out->sectrac *
+ (128 << out->secsize) / 512;
+}
+
+/*
+ * Print a textual translation of the drive (density) type described
+ * by `in' to stdout. The string uses the same form that is parseable
+ * by parse_fmt().
+ */
+void
+print_fmt(struct fd_type in)
+{
+ int secsize, speed;
+
+ secsize = 128 << in.secsize;
+ switch (in.trans) {
+ case FDC_250KBPS: speed = 250; break;
+ case FDC_300KBPS: speed = 300; break;
+ case FDC_500KBPS: speed = 500; break;
+ case FDC_1MBPS: speed = 1000; break;
+ default: speed = 1; break;
+ }
+
+ printf("%d,%d,%#x,%#x,%d,%d,%d,%#x,%d,%d",
+ in.sectrac, secsize, in.datalen, in.gap, in.tracks,
+ speed, in.heads, in.f_gap, in.f_inter, in.offset_side2);
+ if (in.flags & FL_MFM)
+ printf(",+mfm");
+ if (in.flags & FL_2STEP)
+ printf(",+2step");
+ if (in.flags & FL_PERPND)
+ printf(",+perpnd");
+ putc('\n', stdout);
+}
+
+/*
+ * Based on `size' (in kilobytes), walk through the table of known
+ * densities for drive type `type' and see if we can find one. If
+ * found, return it (as a pointer to static storage), otherwise return
+ * NULL.
+ */
+struct fd_type *
+get_fmt(int size, enum fd_drivetype type)
+{
+ int i, n;
+ struct fd_type *fdtp;
+
+ switch (type) {
+ default:
+ return (0);
+
+ case FDT_360K:
+ fdtp = fd_types_360k;
+ n = sizeof fd_types_360k / sizeof(struct fd_type);
+ break;
+
+ case FDT_720K:
+ fdtp = fd_types_720k;
+ n = sizeof fd_types_720k / sizeof(struct fd_type);
+ break;
+
+ case FDT_12M:
+ fdtp = fd_types_12m;
+ n = sizeof fd_types_12m / sizeof(struct fd_type);
+ break;
+
+ case FDT_144M:
+ fdtp = fd_types_144m;
+ n = sizeof fd_types_144m / sizeof(struct fd_type);
+ break;
+
+ case FDT_288M:
+ fdtp = fd_types_288m;
+ n = sizeof fd_types_288m / sizeof(struct fd_type);
+ break;
+ }
+
+ for (i = 0; i < n; i++, fdtp++)
+ if (fdtp->size / 2 == size)
+ return (fdtp);
+
+ return (0);
+}
+
+/*
+ * Parse a number from `s'. If the string cannot be converted into a
+ * number completely, return -1, otherwise 0. The result is returned
+ * in `*res'.
+ */
+int
+getnum(const char *s, int *res)
+{
+ unsigned long ul;
+ char *cp;
+
+ ul = strtoul(s, &cp, 0);
+ if (*cp != '\0')
+ return (-1);
+
+ *res = (int)ul;
+ return (0);
+}
+
+/*
+ * Return a short name and a verbose description for the drive
+ * described by `t'.
+ */
+void
+getname(enum fd_drivetype t, const char **name, const char **descr)
+{
+
+ switch (t) {
+ default:
+ *name = "unknown";
+ *descr = "unknown drive type";
+ break;
+
+ case FDT_360K:
+ *name = "360K";
+ *descr = "5.25\" double-density";
+ break;
+
+ case FDT_12M:
+ *name = "1.2M";
+ *descr = "5.25\" high-density";
+ break;
+
+ case FDT_720K:
+ *name = "720K";
+ *descr = "3.5\" double-density";
+ break;
+
+ case FDT_144M:
+ *name = "1.44M";
+ *descr = "3.5\" high-density";
+ break;
+
+ case FDT_288M:
+ *name = "2.88M";
+ *descr = "3.5\" extra-density";
+ break;
+ }
+}
diff --git a/usr.sbin/fdread/fdutil.h b/usr.sbin/fdread/fdutil.h
new file mode 100644
index 0000000..a093228
--- /dev/null
+++ b/usr.sbin/fdread/fdutil.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2001 Joerg Wunsch
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+
+void printstatus(struct fdc_status *fdcsp, int terse);
+void parse_fmt(const char *, enum fd_drivetype,
+ struct fd_type, struct fd_type *);
+struct fd_type *get_fmt(int, enum fd_drivetype);
+void print_fmt(struct fd_type);
+int getnum(const char *, int *);
+void getname(enum fd_drivetype, const char **, const char **);
+
diff --git a/usr.sbin/fdwrite/Makefile b/usr.sbin/fdwrite/Makefile
new file mode 100644
index 0000000..b97bdd4
--- /dev/null
+++ b/usr.sbin/fdwrite/Makefile
@@ -0,0 +1,12 @@
+# ----------------------------------------------------------------------------
+# "THE BEER-WARE LICENSE" (Revision 42):
+# <phk@FreeBSD.org> wrote this file. As long as you retain this notice you
+# can do whatever you want with this stuff. If we meet some day, and you think
+# this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+# ----------------------------------------------------------------------------
+#
+# $FreeBSD$
+
+PROG= fdwrite
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/fdwrite/fdwrite.1 b/usr.sbin/fdwrite/fdwrite.1
new file mode 100644
index 0000000..93de399
--- /dev/null
+++ b/usr.sbin/fdwrite/fdwrite.1
@@ -0,0 +1,127 @@
+.\"
+.\" ----------------------------------------------------------------------------
+.\" "THE BEER-WARE LICENSE" (Revision 42):
+.\" <phk@FreeBSD.org> wrote this file. As long as you retain this notice you
+.\" can do whatever you want with this stuff. If we meet some day, and you think
+.\" this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+.\" ----------------------------------------------------------------------------
+.\"
+.\" $FreeBSD$
+.\"
+.\"
+.Dd September 16, 1993
+.Os
+.Dt FDWRITE 1
+.Sh NAME
+.Nm fdwrite
+.Nd format and write floppy disks
+.Sh SYNOPSIS
+.Nm
+.Op Fl v
+.Op Fl y
+.Op Fl f Ar inputfile
+.Op Fl d Ar device
+.Sh DESCRIPTION
+The
+.Nm
+utility formats and writes one and more floppy disks.
+Any floppy disk device capable of formatting can be used.
+.Pp
+The
+.Nm
+utility will ask the user
+(on
+.Pa /dev/tty )
+to insert a new floppy and press return.
+The device will then be opened, and queried for its parameters,
+then each track will be formatted, written with data from the
+.Ar inputfile ,
+read back and compared.
+When the floppy disk is filled, the process is repeated, with the next disk.
+This continues until the program is interrupted or EOF is encountered on the
+.Ar inputfile .
+.Pp
+The options are as follows:
+.Bl -tag -width 10n -offset indent
+.It Fl v
+Toggle verbosity on stdout.
+Default is ``on''.
+After
+.Ar device
+is opened first time the format will be printed.
+During operation progress will be reported with the number of tracks
+remaining on the current floppy disk, and the letters I, Z, F, W,
+R and C, which indicates completion of Input, Zero-fill, Format
+Write, Read and Compare of current track respectively.
+.It Fl y
+Don't ask for presence of a floppy disk in the drive.
+This non-interactive flag
+is useful for shell scripts.
+.It Fl f Ar inputfile
+Input file to read. If none is given, stdin is assumed.
+.It Fl d Ar device
+The name of the floppy device to write to. Default is
+.Pa /dev/fd0 .
+.El
+.Pp
+The
+.Nm
+utility actually closes the
+.Ar device
+while it waits for the user to press return,
+it is thus quite possible to use the drive for other purposes at this
+time and later resume writing with the next floppy.
+.Pp
+The parameters returned from
+.Ar device
+are used for formatting.
+If custom formatting is needed, please use
+.Xr fdformat 1
+instead.
+.Sh EXAMPLES
+The
+.Nm
+utility
+was planned as a tool to make life easier when writing a set of floppies,
+one such use could be to write a tar-archive:
+.Pp
+.Dl "tar cf - . | gzip -9 | fdwrite -d /dev/fd0.1720 -v
+.Pp
+The main difference from using
+.Xr tar 1 Ns 's
+multivolume facility is of course the formatting of the floppies, which
+here is done on the fly,
+thus reducing the amount of work for the floppy-jockey.
+.Sh SEE ALSO
+.Xr fdformat 1
+.Sh HISTORY
+The
+.Nm
+utility was written while waiting for ``make world'' to complete.
+Some of the code was taken from
+.Xr fdformat 1 .
+.Sh AUTHORS
+The program has been contributed by
+.An Poul-Henning Kamp Aq phk@FreeBSD.org .
+.Sh BUGS
+Diagnostics are less than complete at present.
+.Pp
+If a floppy is sick, and the
+.Ar inputfile
+is seekable, it should ask the user to frisbee the disk, insert
+another, and rewind to the right spot and continue.
+.Pp
+This concept could be extended to cover non-seekable input also
+by employing a temporary file.
+.Pp
+An option (defaulting to zero) should allow the user to ask for
+retries in case of failure.
+.Pp
+At present a suitable tool for reading back a multivolume set
+of floppies is missing.
+Programs like
+.Xr tar 1
+for instance, will do the job, if the data has not been compressed.
+One can always trust
+.Xr dd 1
+to help out in this situation of course.
diff --git a/usr.sbin/fdwrite/fdwrite.c b/usr.sbin/fdwrite/fdwrite.c
new file mode 100644
index 0000000..8a9ef34
--- /dev/null
+++ b/usr.sbin/fdwrite/fdwrite.c
@@ -0,0 +1,198 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ *
+ * $FreeBSD$
+ *
+ */
+
+#include <ctype.h>
+#include <err.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include <sys/fdcio.h>
+
+int
+format_track(int fd, int cyl, int secs, int head, int rate,
+ int gaplen, int secsize, int fill,int interleave)
+{
+ struct fd_formb f;
+ register int i,j;
+ int il[100];
+
+ memset(il,0,sizeof il);
+ for(j = 0, i = 1; i <= secs; i++) {
+ while(il[(j%secs)+1]) j++;
+ il[(j%secs)+1] = i;
+ j += interleave;
+ }
+
+ f.format_version = FD_FORMAT_VERSION;
+ f.head = head;
+ f.cyl = cyl;
+ f.transfer_rate = rate;
+
+ f.fd_formb_secshift = secsize;
+ f.fd_formb_nsecs = secs;
+ f.fd_formb_gaplen = gaplen;
+ f.fd_formb_fillbyte = fill;
+ for(i = 0; i < secs; i++) {
+ f.fd_formb_cylno(i) = cyl;
+ f.fd_formb_headno(i) = head;
+ f.fd_formb_secno(i) = il[i+1];
+ f.fd_formb_secsize(i) = secsize;
+ }
+ return ioctl(fd, FD_FORM, (caddr_t)&f);
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: fdwrite [-v] [-y] [-f inputfile] [-d device]\n");
+ exit(2);
+}
+
+int
+main(int argc, char **argv)
+{
+ int inputfd = -1, c, fdn = 0, i,j,fd;
+ int bpt, verbose=1, nbytes=0, track;
+ int interactive = 1, fdopts;
+ char *device= "/dev/fd0", *trackbuf = 0,*vrfybuf = 0;
+ struct fd_type fdt;
+ FILE *tty;
+
+ setbuf(stdout,0);
+ while((c = getopt(argc, argv, "d:f:vy")) != -1)
+ switch(c) {
+ case 'd': /* Which drive */
+ device = optarg;
+ break;
+
+ case 'f': /* input file */
+ if (inputfd >= 0)
+ close(inputfd);
+ inputfd = open(optarg,O_RDONLY);
+ if (inputfd < 0)
+ err(1, "%s", optarg);
+ break;
+
+ case 'v': /* Toggle verbosity */
+ verbose = !verbose;
+ break;
+
+ case 'y': /* Don't confirm? */
+ interactive = 0;
+ break;
+
+ case '?': default:
+ usage();
+ }
+
+ if (inputfd < 0)
+ inputfd = 0;
+
+ if (!isatty(1))
+ interactive = 0;
+
+ if(optind < argc)
+ usage();
+
+ tty = fopen(_PATH_TTY,"r+");
+ if(!tty)
+ err(1, _PATH_TTY);
+ setbuf(tty,0);
+
+ for(j=1;j > 0;) {
+ fdn++;
+ if (interactive) {
+ fprintf(tty,
+ "Please insert floppy #%d in drive %s and press return >",
+ fdn,device);
+ while(1) {
+ i = getc(tty);
+ if(i == '\n') break;
+ }
+ }
+
+ if((fd = open(device, O_RDWR)) < 0)
+ err(1, "%s", device);
+
+ if(ioctl(fd, FD_GTYPE, &fdt) < 0)
+ errx(1, "not a floppy disk: %s", device);
+ fdopts = FDOPT_NOERRLOG;
+ if (ioctl(fd, FD_SOPTS, &fdopts) == -1)
+ err(1, "ioctl(FD_SOPTS, FDOPT_NOERRLOG)");
+
+ bpt = fdt.sectrac * (1<<fdt.secsize) * 128;
+ if(!trackbuf) {
+ trackbuf = malloc(bpt);
+ if(!trackbuf) errx(1, "malloc");
+ }
+ if(!vrfybuf) {
+ vrfybuf = malloc(bpt);
+ if(!vrfybuf) errx(1, "malloc");
+ }
+
+ if(fdn == 1) {
+ if(verbose) {
+ printf("Format: %d cylinders, %d heads, %d sectors, %d bytes = %dkb\n",
+ fdt.tracks,fdt.heads,fdt.sectrac,(1<<fdt.secsize) * 128,
+ fdt.tracks*bpt*fdt.heads/1024);
+
+ }
+ memset(trackbuf,0,bpt);
+ for(j=0;inputfd >= 0 && j<bpt;j+=i) {
+ if(!(i = read(inputfd,trackbuf+j,bpt-j))) {
+ close(inputfd);
+ inputfd = -1;
+ break;
+ }
+ nbytes += i;
+ }
+ }
+ for (track = 0; track < fdt.tracks * fdt.heads; track++) {
+ if(verbose) printf("\r%3d ",fdt.tracks * fdt.heads-track);
+ if(verbose) putc((j ? 'I':'Z'),stdout);
+ format_track(fd, track / fdt.heads, fdt.sectrac, track % fdt.heads,
+ fdt.trans, fdt.f_gap, fdt.secsize, 0xe6,
+ fdt.f_inter);
+ if(verbose) putc('F',stdout);
+
+ if (lseek (fd, (long) track*bpt, 0) < 0) err(1, "lseek");
+ if (write (fd, trackbuf, bpt) != bpt) err(1, "write");
+ if(verbose) putc('W',stdout);
+
+ if (lseek (fd, (long) track*bpt, 0) < 0) err(1, "lseek");
+ if (read (fd, vrfybuf, bpt) != bpt) err(1, "read");
+ if(verbose) putc('R',stdout);
+
+ if (memcmp(trackbuf,vrfybuf,bpt)) err(1, "compare");
+ if(verbose) putc('C',stdout);
+
+ memset(trackbuf,0,bpt);
+ for(j=0;inputfd >= 0 && j<bpt;j+=i) {
+ if(!(i = read(inputfd,trackbuf+j,bpt-j))) {
+ close(inputfd);
+ inputfd = -1;
+ break;
+ }
+ nbytes += i;
+ }
+ }
+ close(fd);
+ putc('\r',stdout);
+ }
+ if(verbose)
+ printf("%d bytes on %d flopp%s\n",nbytes,fdn,fdn==1?"y":"ies");
+ exit(0);
+}
diff --git a/usr.sbin/getextattr/Makefile b/usr.sbin/getextattr/Makefile
new file mode 100644
index 0000000..c16e644
--- /dev/null
+++ b/usr.sbin/getextattr/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= getextattr
+MAN= getextattr.8
+
+WARNS?= 2
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/getextattr/getextattr.8 b/usr.sbin/getextattr/getextattr.8
new file mode 100644
index 0000000..72c165f
--- /dev/null
+++ b/usr.sbin/getextattr/getextattr.8
@@ -0,0 +1,108 @@
+.\"-
+.\" Copyright (c) 2000, 2001, 2002 Robert N. M. Watson
+.\" All rights reserved.
+.\"
+.\" This software was developed by Robert Watson for the TrustedBSD
+.\" Project.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd March 30, 2000
+.Dt GETEXTATTR 8
+.Os
+.Sh NAME
+.Nm getextattr
+.Nd retrieve a named extended attribute
+.Sh SYNOPSIS
+.Nm
+.Op Fl ls
+.Ar attrnamespace
+.Ar attrname
+.Ar filename ...
+.Sh DESCRIPTION
+.Nm
+is a user tool to retrieve a named extended attribute on a file or
+directory.
+The
+.Ar attrnamespace
+argument should be the namespace of the attribute to retrieve: legal
+values are "user" and "system".
+The
+.Ar attrname
+argument should be the name of the attribute, and
+.Ar filename
+a list of files and directories from which to retrieve attribute data.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl l
+Print attributes in the first column and file names in the second.
+Can be used only in conjunction with the
+.Fl s
+option.
+.It Fl s
+Attempt to display the attribute data as a string, although the
+results may not look pretty if the data is binary data.
+The
+.Xr strvisx 3
+function is used to generate the string, so control sequences should
+be safely escaped.
+Otherwise, the attribute data will be represented as a series of two-digit
+hex numbers.
+.El
+.Sh IMPLEMENTATION NOTES
+In order for
+.Nm
+to succeed, the attribute service must be available on the file system,
+and the attribute must of defined for the file queried.
+.Sh EXAMPLES
+.Bd -literal -offset indent
+# getextattr system md5 /boot/kernel/kernel
+/boot/kernel/kernel:
+ 61 61 33 62 39 39 66 65 31 35 35 32 31 62 65 32
+ 62 36 38 36 62 31 66 39 63 64 33 39 35 36 36 31
+# getextattr -s system md5 /boot/kernel/kernel
+/boot/kernel/kernel: "aa3b99fe15521be2b686b1f9cd395661"
+.Ed
+.Pp
+Retrieve the
+.Dq md5
+extended attribute for the file
+.Pa /boot/kernel/kernel .
+.Sh SEE ALSO
+.Xr extattr 2 ,
+.Xr extattr 3 ,
+.Xr extattrctl 8 ,
+.Xr setextattr 8 ,
+.Xr extattr 9
+.Sh HISTORY
+Extended attribute support was developed as part of the TrustedBSD Project,
+and introduced in
+.Fx 5.0 .
+It was developed to support security extensions requiring additional labels
+to be associated with each file or directory.
+.Sh AUTHORS
+Robert N M Watson
+.Sh BUGS
+The output format for this utility is ugly, and worse yet, not very useful.
diff --git a/usr.sbin/getextattr/getextattr.c b/usr.sbin/getextattr/getextattr.c
new file mode 100644
index 0000000..c2ccac6
--- /dev/null
+++ b/usr.sbin/getextattr/getextattr.c
@@ -0,0 +1,159 @@
+/*-
+ * Copyright (c) 1999, 2000, 2001, 2002 Robert N. M. Watson
+ * Copyright (c) 2002 Networks Associates Technology, Inc.
+ * All rights reserved.
+ *
+ * This software was developed by Robert Watson for the TrustedBSD Project.
+ *
+ * This software was developed for the FreeBSD Project in part by NAI Labs,
+ * the Security Research Division of Network Associates, Inc. under
+ * DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA
+ * CHATS research program.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The names of the authors may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+/*
+ * TrustedBSD Project - extended attribute support
+ */
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/extattr.h>
+
+#include <libutil.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <vis.h>
+
+#define BUFSIZE 2048
+
+void usage(void);
+
+void
+usage(void)
+{
+
+ fprintf(stderr, "getextattr [-s] [attrnamespace] [attrname] "
+ "[filename ...]\n");
+ exit(-1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ size_t len;
+ char *attrname;
+ char *buf, *visbuf;
+ int ch, error, i, arg_counter, attrnamespace;
+
+ int flag_as_string = 0;
+ int flag_reverse = 0;
+
+ while ((ch = getopt(argc, argv, "ls")) != -1) {
+ switch (ch) {
+ case 'l':
+ flag_reverse = 1;
+ case 's':
+ flag_as_string = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 3)
+ usage();
+
+ error = extattr_string_to_namespace(argv[0], &attrnamespace);
+ if (error) {
+ perror(argv[0]);
+ return (-1);
+ }
+ attrname = argv[1];
+
+ argc--;
+ argv++;
+
+ /*
+ * XXX: Note: now that EAs support querying the size, we could
+ * actually allocate a buffer of the right size, rather than
+ * truncating at BUFSIZE.
+ */
+ for (arg_counter = 1; arg_counter < argc; arg_counter++) {
+ len = extattr_get_file(argv[arg_counter], attrnamespace,
+ attrname, NULL, 0);
+ if (len == -1) {
+ perror(argv[arg_counter]);
+ continue;
+ }
+ buf = (char *)malloc(len);
+ if (buf == NULL) {
+ perror("malloc");
+ return (-1);
+ }
+ error = extattr_get_file(argv[arg_counter], attrnamespace,
+ attrname, buf, BUFSIZE);
+ if (error == -1)
+ perror(argv[arg_counter]);
+ else {
+ if (flag_as_string) {
+ visbuf = (char *)malloc(len*4);
+ if (visbuf == NULL) {
+ perror("malloc");
+ return (-1);
+ }
+ strvisx(visbuf, buf, error, VIS_SAFE
+ | VIS_WHITE);
+ if (flag_reverse) {
+ printf("%s ", visbuf);
+ printf("%s\n", argv[arg_counter]);
+ } else {
+ printf("%s:", argv[arg_counter]);
+ printf(" \"%s\"\n", visbuf);
+ }
+ free(visbuf);
+ } else {
+ printf("%s:", argv[arg_counter]);
+ for (i = 0; i < error; i++)
+ if (i % 16 == 0)
+ printf("\n %02x ", buf[i]);
+ else if (i % 8 == 0)
+ printf(" %02x ", buf[i]);
+ else
+ printf("%02x ", buf[i]);
+ printf("\n");
+ }
+ }
+ free(buf);
+ }
+
+ return (0);
+}
diff --git a/usr.sbin/gifconfig/Makefile b/usr.sbin/gifconfig/Makefile
new file mode 100644
index 0000000..c6f257c
--- /dev/null
+++ b/usr.sbin/gifconfig/Makefile
@@ -0,0 +1,22 @@
+# Copyright (c) 1996 WIDE Project. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modifications, are permitted provided that the above copyright notice
+# and this paragraph are duplicated in all such forms and that any
+# documentation, advertising materials, and other materials related to
+# such distribution and use acknowledge that the software was developed
+# by the WIDE Project, Japan. The name of the Project may not be used to
+# endorse or promote products derived from this software without
+# specific prior written permission. THIS SOFTWARE IS PROVIDED ``AS IS''
+# AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+# LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE.
+#
+# $FreeBSD$
+
+PROG= gifconfig
+MAN= gifconfig.8
+
+CFLAGS+= -DINET6
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/gifconfig/gifconfig.8 b/usr.sbin/gifconfig/gifconfig.8
new file mode 100644
index 0000000..b7762a3
--- /dev/null
+++ b/usr.sbin/gifconfig/gifconfig.8
@@ -0,0 +1,160 @@
+.\" $FreeBSD$
+.\" $KAME: gifconfig.8,v 1.6 2000/11/22 11:10:09 itojun Exp $
+.\"
+.\" Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the project nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd May 17, 1998
+.Dt GIFCONFIG 8
+.Os
+.Sh NAME
+.Nm gifconfig
+.Nd configure generic IP tunnel
+.\"
+.Sh SYNOPSIS
+.Nm
+.Ar interface
+.Op Ar af
+.Op Ar physsrc physdest
+.Nm
+.Ar interface
+.Ic delete
+.Nm
+.Fl a
+.Sh DESCRIPTION
+.Nm
+configures the physical address for the generic IP tunnel
+inteface, such as "gif0".
+Argument
+.Ar physsrc
+and
+.Ar physdest
+are interpreted as the outer source/destination address for
+encapsulating IPv4/v6 header.
+Argument
+.Ar af
+specifies the address family for
+.Ar physsrc
+and
+.Ar physdest .
+.Ar af
+can be
+.Li inet
+or
+.Li inet6 ,
+and will be treated as
+.Li inet
+if ommitted.
+.Pp
+If a special argument
+.Ic delete
+is specified,
+.Nm
+just deletes the existing source and destination addresses on
+.Ar interface .
+If no outer addresses are specified, this usage has no effect.
+.Pp
+.Nm
+takes the following optional argument:
+.Bl -tag -width Ds
+.It Fl a
+Display information associated with all
+.Xr gif 4
+interfaces.
+.El
+.Pp
+Please note that it is very easy to create infinite routing loop,
+when you configure tunnel over same address family
+(e.g. IPv4-over-IPv4).
+.Pp
+Each
+.Xr gif 4
+interface is created at runtime using interface cloning.
+This is
+most easily done with the
+.Xr ifconfig 8
+.Cm create
+command or using the
+.Va gifconfig_ Ns Aq Ar interface
+variable in
+.Xr rc.conf 5 .
+.Sh EXAMPLES
+If you would like to configure IPv6 over IPv4
+(aka IPv6 in IPv4)
+tunnel between
+.Li 10.1.1.1
+and
+.Li 10.2.3.4 ,
+you should perform the following command:
+.Bd -literal -offset
+# gifconfig gif0 inet 10.1.1.1 10.2.3.4
+.Ed
+.Pp
+.\" To use the
+.\" .Li 0.0.0.0
+.\" feature to establish a tunnel from host1 to host3
+.\" which will encapsulate and carry packets from host2, on host1 do:
+.\" .Bd -literal -offset
+.\" # ifconfig gif0 inet host1 127.0.0.2 # assign an address to gif0
+.\" # gifconfig gif0 inet host1 0.0.0.0 # assign encapsulation addresses
+.\" # route add host2 host3 -ifp gif0: # encap host2 packets, send to host3
+.\" .Ed
+.\" .Pp
+.\" Note: the
+.\" .Fl ifp
+.\" option to route does not work as documented in
+.\" most versions of FreeBSD.
+.\" .Pp
+.\" On host3 do:
+.\" .Bd -literal -offset
+.\" # ifconfig gif0 inet host3 127.0.0.2 # assign an address to gif0
+.\" # gifconfig gif0 inet host3 0.0.0.0 # assign encapsulation addresses
+.\" .Ed
+.\" .Pp
+.\" Now if you ping host2 from host1, the packets should be encapsulated
+.\" with outer source address = host1 and outer destination address = host3,
+.\" and delivered to host3.
+.\" host3 will decapsulate the packet and deliver it normally to host2.
+.\" .Pp
+This is also possible to use IPv6 as outer proto, by replacing
+.Li inet
+to
+.Li inet6 ,
+and IPv4 addresses to some appropriate IPv6 addresses in above example.
+.Sh RETURN VALUES
+The command exits with exit status of 1 on errors, 0 on success.
+.Sh SEE ALSO
+.Xr gif 4 ,
+.Xr ifconfig 8
+.Sh HISTORY
+The
+.Nm
+command first appeared in WIDE Hydrangea IPv6 protocol stack kit.
+.Pp
+IPv6 and IPsec support based on the KAME Project (http://www.kame.net/) stack
+was initially integrated into
+.Fx 4.0
diff --git a/usr.sbin/gifconfig/gifconfig.c b/usr.sbin/gifconfig/gifconfig.c
new file mode 100644
index 0000000..b803236
--- /dev/null
+++ b/usr.sbin/gifconfig/gifconfig.c
@@ -0,0 +1,926 @@
+/* $FreeBSD$ */
+/* $KAME: gifconfig.c,v 1.14 2001/01/01 04:04:56 jinmei Exp $ */
+
+/*
+ * 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.
+ */
+
+/*
+ * gifconfig, derived from ifconfig
+ *
+ * @(#) Copyright (c) 1983, 1993\n\
+ * The Regents of the University of California. All rights reserved.\n
+ *
+ * @(#)ifconfig.c 8.2 (Berkeley) 2/16/94
+ */
+
+/*
+ * 951109 - Andrew@pubnix.net - Changed to iterative buffer growing mechanism
+ * for ifconfig -a so all interfaces are queried.
+ *
+ * 960101 - peter@freebsd.org - Blow away the SIOCGIFCONF code and use
+ * sysctl() to get the structured interface conf
+ * and parse the messages in there. REALLY UGLY!
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+#include <net/if_var.h>
+#endif /* __FreeBSD__ >= 3 */
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <sys/protosw.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <nlist.h>
+#include <kvm.h>
+#include <fcntl.h>
+
+struct ifreq ifr;
+struct ifaliasreq addreq;
+#ifdef INET6
+struct in6_ifreq in6_ifr;
+struct in6_aliasreq in6_addreq;
+#endif
+
+char name[32];
+int flags;
+int metric;
+int mtu;
+int setpsrc = 0;
+int newaddr = 0;
+int s;
+kvm_t *kvmd;
+
+#ifdef INET6
+char ntop_buf[INET6_ADDRSTRLEN]; /*inet_ntop()*/
+#endif
+
+void setifpsrc __P((char *, int));
+void setifpdst __P((char *, int));
+void setifflags __P((char *, int));
+#ifdef SIOCDIFPHYADDR
+void delifaddrs __P((char *, int));
+#endif
+
+#define NEXTARG 0xffffff
+
+static struct cmd {
+ char *c_name;
+ int c_parameter; /* NEXTARG means next argv */
+ void (*c_func) __P((char *, int));
+} cmds[] = {
+ { "up", IFF_UP, setifflags } ,
+ { "down", -IFF_UP, setifflags },
+#ifdef SIOCDIFPHYADDR
+ { "delete", 0, delifaddrs },
+#endif
+ { 0, 0, setifpsrc },
+ { 0, 0, setifpdst },
+};
+
+/*
+ * XNS support liberally adapted from code written at the University of
+ * Maryland principally by James O'Toole and Chris Torek.
+ */
+int main __P((int, char *[]));
+void status __P((void));
+void phys_status __P((int));
+void in_status __P((int));
+#ifdef INET6
+void in6_status __P((int));
+#endif
+void ether_status __P((int));
+void Perror __P((char *));
+void in_getaddr __P((char *, int));
+#ifdef INET6
+void in6_getaddr __P((char *, int));
+void in6_getprefix __P((char *, int));
+#endif
+void printb __P((char *, unsigned int, char *));
+int prefix __P((void *, int));
+
+char ntop_buf[INET6_ADDRSTRLEN];
+
+/* Known address families */
+struct afswtch {
+ char *af_name;
+ short af_af;
+ void (*af_status) __P((int));
+ void (*af_getaddr) __P((char *, int));
+ void (*af_getprefix) __P((char *, int));
+ u_long af_pifaddr;
+ caddr_t af_addreq;
+ caddr_t af_req;
+} afs[] = {
+#define C(x) ((caddr_t) &x)
+ { "inet", AF_INET, in_status, in_getaddr, 0,
+ SIOCSIFPHYADDR, C(addreq), C(ifr) },
+#ifdef INET6
+ { "inet6", AF_INET6, in6_status, in6_getaddr, in6_getprefix,
+ SIOCSIFPHYADDR_IN6, C(in6_addreq), C(in6_ifr) },
+#endif
+ { "ether", AF_INET, ether_status, NULL, NULL }, /* XXX not real!! */
+ { 0, 0, 0, 0, 0 }
+};
+
+struct afswtch *afp = NULL; /*the address family being set or asked about*/
+
+void rt_xaddrs __P((caddr_t, caddr_t, struct rt_addrinfo *));
+int ifconfig __P((int argc, char *argv[], int af, struct afswtch *rafp));
+
+
+
+/*
+ * Expand the compacted form of addresses as returned via the
+ * configuration read via sysctl().
+ */
+
+#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)
+ caddr_t cp, cplim;
+ struct rt_addrinfo *rtinfo;
+{
+ struct sockaddr *sa;
+ int i;
+
+ memset(rtinfo->rti_info, 0, sizeof(rtinfo->rti_info));
+ for (i = 0; (i < RTAX_MAX) && (cp < cplim); i++) {
+ if ((rtinfo->rti_addrs & (1 << i)) == 0)
+ continue;
+ rtinfo->rti_info[i] = sa = (struct sockaddr *)cp;
+ ADVANCE(cp, sa);
+ }
+}
+
+
+/*
+ * Grunge for new-style sysctl() decoding.. :-(
+ * Apologies to the world for committing gross things like this in 1996..
+ */
+struct if_msghdr *ifm;
+struct ifa_msghdr *ifam;
+struct sockaddr_dl *sdl;
+struct rt_addrinfo info;
+char *buf, *lim, *next;
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int af = AF_INET;
+ struct afswtch *rafp = NULL;
+ size_t needed;
+ int mib[6];
+ int all;
+
+ if (argc < 2) {
+ fprintf(stderr,
+ "usage: gifconfig interface [af] [physsrc physdst]\n");
+#ifdef SIOCDIFPHYADDR
+ fprintf(stderr,
+ " gifconfig interface delete\n");
+#endif
+ fprintf(stderr,
+ " gifconfig -a\n");
+ exit(1);
+ }
+ argc--, argv++;
+ strncpy(name, *argv, sizeof(name));
+ strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
+ argc--, argv++;
+ if (argc > 0) {
+ for (afp = rafp = afs; rafp->af_name; rafp++)
+ if (strcmp(rafp->af_name, *argv) == 0) {
+ afp = rafp; argc--; argv++;
+ break;
+ }
+ rafp = afp;
+ af = ifr.ifr_addr.sa_family = rafp->af_af;
+ }
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = 0; /* address family */
+ mib[4] = NET_RT_IFLIST;
+ mib[5] = 0;
+
+ /* if particular family specified, only ask about it */
+ if (afp) {
+ mib[3] = afp->af_af;
+ }
+
+ if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
+ errx(1, "iflist-sysctl-estimate");
+ if ((buf = malloc(needed)) == NULL)
+ errx(1, "malloc");
+ if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
+ errx(1, "actual retrieval of interface table");
+ lim = buf + needed;
+
+ all = 0;
+ if (strcmp(name, "-a") == 0)
+ all = 1; /* All interfaces */
+ else if (strcmp(name, "-au") == 0)
+ all = 2; /* All IFF_UPinterfaces */
+ else if (strcmp(name, "-ad") == 0)
+ all = 3; /* All !IFF_UP interfaces */
+
+ for (next = buf; next < lim; next += ifm->ifm_msglen) {
+
+ ifm = (struct if_msghdr *)next;
+
+ /* XXX: Swallow up leftover NEWADDR messages */
+ if (ifm->ifm_type == RTM_NEWADDR)
+ continue;
+
+ if (ifm->ifm_type == RTM_IFINFO) {
+ sdl = (struct sockaddr_dl *)(ifm + 1);
+ flags = ifm->ifm_flags;
+ } else {
+ errx(1, "out of sync parsing NET_RT_IFLIST");
+ }
+
+ switch(all) {
+ case -1:
+ case 0:
+ if (strlen(name) != sdl->sdl_nlen)
+ continue; /* not same len */
+ if (strncmp(name, sdl->sdl_data, sdl->sdl_nlen) != 0)
+ continue; /* not same name */
+ break;
+ case 1:
+ break; /* always do it */
+ case 2:
+ if ((flags & IFF_UP) == 0)
+ continue; /* not up */
+ break;
+ case 3:
+ if (flags & IFF_UP)
+ continue; /* not down */
+ break;
+ }
+
+ /*
+ * Let's just do it for gif only
+ */
+ if (sdl->sdl_type != IFT_GIF) {
+ if (all != 0)
+ continue;
+
+ fprintf(stderr, "gifconfig: %s is not gif.\n",
+ ifr.ifr_name);
+ exit(1);
+ }
+
+ if (all > 0) {
+ strncpy(name, sdl->sdl_data, sdl->sdl_nlen);
+ name[sdl->sdl_nlen] = '\0';
+ }
+
+ if ((s = socket(af, SOCK_DGRAM, 0)) < 0) {
+ perror("gifconfig: socket");
+ exit(1);
+ }
+
+ ifconfig(argc,argv,af,rafp);
+
+ close(s);
+
+ if (all == 0) {
+ all = -1; /* flag it as 'done' */
+ break;
+ }
+ }
+ free(buf);
+
+ if (all == 0)
+ errx(1, "interface %s does not exist", name);
+
+
+ exit (0);
+}
+
+
+int
+ifconfig(argc, argv, af, rafp)
+ int argc;
+ char *argv[];
+ int af;
+ struct afswtch *rafp;
+{
+
+ af = 0; /*fool gcc*/
+
+ strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
+#ifdef INET6
+ strncpy(in6_ifr.ifr_name, name, sizeof in6_ifr.ifr_name);
+#endif /* INET6 */
+
+ if (ioctl(s, SIOCGIFMETRIC, (caddr_t)&ifr) < 0)
+ perror("ioctl (SIOCGIFMETRIC)");
+ else
+ metric = ifr.ifr_metric;
+
+#if defined(SIOCGIFMTU) && !defined(__OpenBSD__)
+ if (ioctl(s, SIOCGIFMTU, (caddr_t)&ifr) < 0)
+ perror("ioctl (SIOCGIFMTU)");
+ else
+ mtu = ifr.ifr_mtu;
+#else
+ mtu = 0;
+#endif
+
+ if (argc == 0) {
+ status();
+ return(0);
+ }
+
+ while (argc > 0) {
+ register struct cmd *p;
+
+ for (p = cmds; p->c_name; p++)
+ if (strcmp(*argv, p->c_name) == 0)
+ break;
+ if (p->c_name == 0 && setpsrc)
+ p++; /* got src, do dst */
+ if (p->c_func) {
+ if (p->c_parameter == NEXTARG) {
+ if (argv[1] == NULL)
+ errx(1, "'%s' requires argument",
+ p->c_name);
+ (*p->c_func)(argv[1], 0);
+ argc--, argv++;
+ } else
+ (*p->c_func)(*argv, p->c_parameter);
+ }
+ argc--, argv++;
+ }
+ if (newaddr) {
+ strncpy(rafp->af_addreq, name, sizeof ifr.ifr_name);
+ if (ioctl(s, rafp->af_pifaddr, rafp->af_addreq) < 0)
+ Perror("ioctl (SIOCSIFPHYADDR)");
+ }
+ else if (setpsrc) {
+ errx(1, "destination is not specified");
+ }
+ return(0);
+}
+#define PSRC 0
+#define PDST 1
+
+/*ARGSUSED*/
+void
+setifpsrc(addr, param)
+ char *addr;
+ int param;
+{
+ param = 0; /*fool gcc*/
+ (*afp->af_getaddr)(addr, PSRC);
+ setpsrc = 1;
+}
+
+/*ARGSUSED*/
+void
+setifpdst(addr, param)
+ char *addr;
+ int param;
+{
+ param = 0; /*fool gcc*/
+ (*afp->af_getaddr)(addr, PDST);
+ newaddr = 1;
+}
+
+void
+setifflags(vname, value)
+ char *vname;
+ int value;
+{
+ if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
+ Perror("ioctl (SIOCGIFFLAGS)");
+ exit(1);
+ }
+ strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name));
+ flags = ifr.ifr_flags;
+
+ if (value < 0) {
+ value = -value;
+ flags &= ~value;
+ } else
+ flags |= value;
+ ifr.ifr_flags = flags;
+ if (ioctl(s, SIOCSIFFLAGS, (caddr_t)&ifr) < 0)
+ Perror(vname);
+}
+
+#ifdef SIOCDIFPHYADDR
+/* ARGSUSED */
+void
+delifaddrs(vname, param)
+ char *vname;
+ int param;
+{
+ param = 0; /* fool gcc */
+ vname = NULL; /* ditto */
+
+ if (ioctl(s, SIOCDIFPHYADDR, (caddr_t)&ifr) < 0)
+ err(1, "ioctl(SIOCDIFPHYADDR)");
+}
+#endif
+
+#define IFFBITS \
+"\020\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5POINTOPOINT\6NOTRAILERS\7RUNNING\10NOARP\
+\11PROMISC\12ALLMULTI\13OACTIVE\14SIMPLEX\15LINK0\16LINK1\17LINK2\20MULTICAST"
+
+/*
+ * Print the status of the interface. If an address family was
+ * specified, show it and it only; otherwise, show them all.
+ */
+void
+status()
+{
+ struct afswtch *p = NULL;
+ char *mynext;
+ struct if_msghdr *myifm;
+
+ printf("%s: ", name);
+ printb("flags", flags, IFFBITS);
+ if (metric)
+ printf(" metric %d", metric);
+ if (mtu)
+ printf(" mtu %d", mtu);
+ putchar('\n');
+
+ /*
+ * XXX: Sigh. This is bad, I know. At this point, we may have
+ * *zero* RTM_NEWADDR's, so we have to "feel the water" before
+ * incrementing the loop. One day, I might feel inspired enough
+ * to get the top level loop to pass a count down here so we
+ * dont have to mess with this. -Peter
+ */
+ myifm = ifm;
+
+ while (1) {
+
+ mynext = next + ifm->ifm_msglen;
+
+ if (mynext >= lim)
+ break;
+
+ myifm = (struct if_msghdr *)mynext;
+
+ if (myifm->ifm_type != RTM_NEWADDR)
+ break;
+
+ next = mynext;
+
+ ifm = (struct if_msghdr *)next;
+
+ ifam = (struct ifa_msghdr *)myifm;
+ info.rti_addrs = ifam->ifam_addrs;
+
+ /* Expand the compacted addresses */
+ rt_xaddrs((char *)(ifam + 1), ifam->ifam_msglen + (char *)ifam,
+ &info);
+
+ if (afp) {
+ if (afp->af_af == info.rti_info[RTAX_IFA]->sa_family &&
+ afp->af_status != ether_status) {
+ p = afp;
+ if (p->af_status != ether_status)
+ (*p->af_status)(1);
+ }
+ } else for (p = afs; p->af_name; p++) {
+ if (p->af_af == info.rti_info[RTAX_IFA]->sa_family &&
+ p->af_status != ether_status)
+ (*p->af_status)(0);
+ }
+ }
+ if (afp == NULL || afp->af_status == ether_status)
+ ether_status(0);
+ else if (afp && !p) {
+ warnx("%s has no %s IFA address!", name, afp->af_name);
+ }
+
+ phys_status(0);
+}
+
+void
+phys_status(force)
+ int force;
+{
+ char psrcaddr[256];
+ char pdstaddr[256];
+ int flags = NI_NUMERICHOST;
+ char *af;
+#ifndef SIOCGLIFPHYADDR
+ u_long srccmd, dstcmd;
+ struct ifreq *ifrp;
+#ifdef INET6
+ int s6;
+#endif
+
+ force = 0; /*fool gcc*/
+
+ psrcaddr[0] = pdstaddr[0] = '\0';
+
+#ifdef INET6
+ s6 = socket(AF_INET6, SOCK_DGRAM, 0);
+ if (s6 < 0) {
+ ifrp = &ifr;
+ srccmd = SIOCGIFPSRCADDR;
+ dstcmd = SIOCGIFPDSTADDR;
+ } else {
+ close(s6);
+ srccmd = SIOCGIFPSRCADDR_IN6;
+ dstcmd = SIOCGIFPDSTADDR_IN6;
+ ifrp = (struct ifreq *)&in6_ifr;
+ }
+#else /* INET6 */
+ ifrp = &ifr;
+ srccmd = SIOCGIFPSRCADDR;
+ dstcmd = SIOCGIFPDSTADDR;
+#endif /* INET6 */
+
+ if (0 <= ioctl(s, srccmd, (caddr_t)ifrp)) {
+#ifdef INET6
+ if (ifrp->ifr_addr.sa_family == AF_INET6)
+ af = "inet6";
+ else
+ af = "inet";
+#else
+ af = "inet";
+#endif /* INET6 */
+ if (getnameinfo(&ifrp->ifr_addr, ifrp->ifr_addr.sa_len,
+ psrcaddr, sizeof(psrcaddr), 0, 0, flags) != 0)
+ psrcaddr[0] = '\0';
+ }
+ if (0 <= ioctl(s, dstcmd, (caddr_t)ifrp)) {
+ if (getnameinfo(&ifrp->ifr_addr, ifrp->ifr_addr.sa_len,
+ pdstaddr, sizeof(pdstaddr), 0, 0, flags) != 0)
+ pdstaddr[0] = '\0';
+ }
+ printf("\tphysical address %s %s --> %s\n", af, psrcaddr, pdstaddr);
+#else
+ struct if_laddrreq iflr;
+
+ force = 0; /*fool gcc*/
+
+ psrcaddr[0] = pdstaddr[0] = '\0';
+
+ memset(&iflr, 0, sizeof(iflr));
+ memcpy(iflr.iflr_name, ifr.ifr_name, sizeof(iflr.iflr_name));
+
+ if (0 <= ioctl(s, SIOCGLIFPHYADDR, (caddr_t)&iflr)) {
+ switch (iflr.addr.ss_family) {
+ case AF_INET:
+ af = "inet";
+ break;
+#ifdef INET6
+ case AF_INET6:
+ af = "inet6";
+ break;
+#endif /* INET6 */
+ }
+ if (getnameinfo((struct sockaddr *)&iflr.addr, iflr.addr.ss_len,
+ psrcaddr, sizeof(psrcaddr), 0, 0, flags) != 0)
+ psrcaddr[0] = '\0';
+ if (getnameinfo((struct sockaddr *)&iflr.dstaddr,
+ iflr.dstaddr.ss_len, pdstaddr, sizeof(pdstaddr), 0, 0,
+ flags) != 0)
+ pdstaddr[0] = '\0';
+ }
+ printf("\tphysical address %s %s --> %s\n", af, psrcaddr, pdstaddr);
+#endif
+}
+
+void
+in_status(force)
+ int force;
+{
+ struct sockaddr_in *sin, null_sin;
+#if 0
+ char *inet_ntoa();
+#endif
+
+ memset(&null_sin, 0, sizeof(null_sin));
+
+ sin = (struct sockaddr_in *)info.rti_info[RTAX_IFA];
+ if (!sin || sin->sin_family != AF_INET) {
+ if (!force)
+ return;
+ /* warnx("%s has no AF_INET IFA address!", name); */
+ sin = &null_sin;
+ }
+ printf("\tinet %s ", inet_ntoa(sin->sin_addr));
+
+ if (flags & IFF_POINTOPOINT) {
+ /* note RTAX_BRD overlap with IFF_BROADCAST */
+ sin = (struct sockaddr_in *)info.rti_info[RTAX_BRD];
+ if (!sin)
+ sin = &null_sin;
+ printf("--> %s ", inet_ntoa(sin->sin_addr));
+ }
+
+ sin = (struct sockaddr_in *)info.rti_info[RTAX_NETMASK];
+ if (!sin)
+ sin = &null_sin;
+ printf("netmask 0x%x ", (u_int32_t)ntohl(sin->sin_addr.s_addr));
+
+ if (flags & IFF_BROADCAST) {
+ /* note RTAX_BRD overlap with IFF_POINTOPOINT */
+ sin = (struct sockaddr_in *)info.rti_info[RTAX_BRD];
+ if (sin && sin->sin_addr.s_addr != 0)
+ printf("broadcast %s", inet_ntoa(sin->sin_addr));
+ }
+ putchar('\n');
+}
+
+#ifdef INET6
+void
+in6_status(force)
+ int force;
+{
+ struct sockaddr_in6 *sin, null_sin;
+ char hostname[NI_MAXHOST];
+ int niflags = NI_NUMERICHOST;
+
+ memset(&null_sin, 0, sizeof(null_sin));
+#ifdef NI_WITHSCOPEID
+ niflags |= NI_WITHSCOPEID;
+#endif
+
+ sin = (struct sockaddr_in6 *)info.rti_info[RTAX_IFA];
+ if (!sin || sin->sin6_family != AF_INET6) {
+ if (!force)
+ return;
+ /* warnx("%s has no AF_INET6 IFA address!", name); */
+ sin = &null_sin;
+ }
+#ifdef __KAME__
+ if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr)) {
+ sin->sin6_scope_id =
+ ntohs(*(u_int16_t *)&sin->sin6_addr.s6_addr[2]);
+ sin->sin6_addr.s6_addr[2] = 0;
+ sin->sin6_addr.s6_addr[3] = 0;
+ }
+#endif
+ getnameinfo((struct sockaddr *)sin, sin->sin6_len,
+ hostname, sizeof(hostname), 0, 0, niflags);
+ printf("\tinet6 %s ", hostname);
+
+ if (flags & IFF_POINTOPOINT) {
+ /* note RTAX_BRD overlap with IFF_BROADCAST */
+ sin = (struct sockaddr_in6 *)info.rti_info[RTAX_BRD];
+ /*
+ * some of ther interfaces do not have valid destination
+ * address.
+ */
+ if (sin->sin6_family == AF_INET6) {
+#ifdef __KAME__
+ if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr)) {
+ sin->sin6_scope_id =
+ ntohs(*(u_int16_t *)&sin->sin6_addr.s6_addr[2]);
+ sin->sin6_addr.s6_addr[2] = 0;
+ sin->sin6_addr.s6_addr[3] = 0;
+ }
+#endif
+ getnameinfo((struct sockaddr *)sin, sin->sin6_len,
+ hostname, sizeof(hostname), 0, 0, niflags);
+ printf("--> %s ", hostname);
+ }
+ }
+
+ sin = (struct sockaddr_in6 *)info.rti_info[RTAX_NETMASK];
+ if (!sin)
+ sin = &null_sin;
+ printf(" prefixlen %d ", prefix(&sin->sin6_addr,
+ sizeof(struct in6_addr)));
+
+ putchar('\n');
+}
+#endif /*INET6*/
+
+/*ARGSUSED*/
+void
+ether_status(dummy)
+ int dummy;
+{
+ char *cp;
+ int n;
+
+ dummy = 0; /*fool gcc*/
+
+ cp = (char *)LLADDR(sdl);
+ if ((n = sdl->sdl_alen) > 0) {
+ if (sdl->sdl_type == IFT_ETHER)
+ printf ("\tether ");
+ else
+ printf ("\tlladdr ");
+ while (--n >= 0)
+ printf("%02x%c",*cp++ & 0xff, n>0? ':' : ' ');
+ putchar('\n');
+ }
+}
+
+void
+Perror(cmd)
+ char *cmd;
+{
+ switch (errno) {
+
+ case ENXIO:
+ errx(1, "%s: no such interface", cmd);
+ break;
+
+ case EPERM:
+ errx(1, "%s: permission denied", cmd);
+ break;
+
+ default:
+ err(1, "%s", cmd);
+ }
+}
+
+#define SIN(x) ((struct sockaddr_in *) &(x))
+struct sockaddr_in *sintab[] = {
+SIN(addreq.ifra_addr), SIN(addreq.ifra_dstaddr)};
+
+void
+in_getaddr(s, which)
+ char *s;
+ int which;
+{
+ register struct sockaddr_in *sin = sintab[which];
+ struct hostent *hp;
+ struct netent *np;
+
+ sin->sin_len = sizeof(*sin);
+ sin->sin_family = AF_INET;
+
+ if (inet_aton(s, &sin->sin_addr))
+ ;
+ else if ((hp = gethostbyname(s)) != NULL)
+ bcopy(hp->h_addr, (char *)&sin->sin_addr, hp->h_length);
+ else if ((np = getnetbyname(s)) != NULL)
+ sin->sin_addr = inet_makeaddr(np->n_net, INADDR_ANY);
+ else
+ errx(1, "%s: bad value", s);
+}
+
+#ifdef INET6
+#define SIN6(x) ((struct sockaddr_in6 *) &(x))
+struct sockaddr_in6 *sin6tab[] = {
+SIN6(in6_addreq.ifra_addr), SIN6(in6_addreq.ifra_dstaddr)};
+
+void
+in6_getaddr(s, which)
+ char *s;
+ int which;
+{
+ register struct sockaddr_in6 *sin = sin6tab[which];
+
+ sin->sin6_len = sizeof(*sin);
+ sin->sin6_family = AF_INET6;
+
+ if (inet_pton(AF_INET6, s, &sin->sin6_addr) != 1)
+ errx(1, "%s: bad value", s);
+}
+
+void
+in6_getprefix(plen, which)
+ char *plen;
+ int which;
+{
+ register struct sockaddr_in6 *sin = sin6tab[which];
+ register u_char *cp;
+ int len = atoi(plen);
+
+ if ((len < 0) || (len > 128))
+ errx(1, "%s: bad value", plen);
+ sin->sin6_len = sizeof(*sin);
+ sin->sin6_family = AF_INET6;
+ if ((len == 0) || (len == 128)) {
+ memset(&sin->sin6_addr, -1, sizeof(struct in6_addr));
+ return;
+ }
+ for (cp = (u_char *)&sin->sin6_addr; len > 7; len -= 8)
+ *cp++ = -1;
+ *cp = (-1) << (8 - len);
+}
+#endif
+
+/*
+ * Print a value a la the %b format of the kernel's printf
+ */
+void
+printb(s, v, bits)
+ char *s;
+ register unsigned int v;
+ register char *bits;
+{
+ register int i, any = 0;
+ register char c;
+
+ if (bits && *bits == 8)
+ printf("%s=%o", s, v & 0xffff);
+ else
+ printf("%s=%x", s, v & 0xffff);
+ bits++;
+ if (bits) {
+ putchar('<');
+ while ((i = *bits++) != 0) {
+ if ((v & (1 << (i-1))) != 0) {
+ if (any)
+ putchar(',');
+ any = 1;
+ for (; (c = *bits) > 32; bits++)
+ putchar(c);
+ } else
+ for (; *bits > 32; bits++)
+ ;
+ }
+ putchar('>');
+ }
+}
+
+#ifdef INET6
+int
+prefix(val, size)
+ void *val;
+ int size;
+{
+ register u_char *name = (u_char *)val;
+ register int byte, bit, plen = 0;
+
+ for (byte = 0; byte < size; byte++, plen += 8)
+ if (name[byte] != 0xff)
+ break;
+ if (byte == size)
+ return (plen);
+ for (bit = 7; bit != 0; bit--, plen++)
+ if (!(name[byte] & (1 << bit)))
+ break;
+ for (; bit != 0; bit--)
+ if (name[byte] & (1 << bit))
+ return(0);
+ byte++;
+ for (; byte < size; byte++)
+ if (name[byte])
+ return(0);
+ return (plen);
+}
+#endif /*INET6*/
diff --git a/usr.sbin/i4b/Makefile b/usr.sbin/i4b/Makefile
new file mode 100644
index 0000000..01f7642
--- /dev/null
+++ b/usr.sbin/i4b/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+SUBDIR= dtmfdecode g711conv isdnd isdndebug isdndecode isdnmonitor isdnphone \
+ isdntel isdntelctl isdntest isdntrace man
+
+.include <bsd.subdir.mk>
+
diff --git a/usr.sbin/i4b/Makefile.inc b/usr.sbin/i4b/Makefile.inc
new file mode 100644
index 0000000..1195419
--- /dev/null
+++ b/usr.sbin/i4b/Makefile.inc
@@ -0,0 +1,16 @@
+# $FreeBSD$
+
+# if you don't like curses stuff in the daemon (i.e. don't intend
+# to ever run it in the foreground but are using the monitoring
+# utilities instead) define this to compile it without.
+#I4B_WITHOUT_CURSES = 1
+
+# if you would like monitoring support, define this
+I4B_EXTERNAL_MONITOR = 1
+
+# for the security conscious type: restrict monitoring to the
+# local machine by not compiling any tcp/ip support for monitoring
+# at all
+#I4B_NOTCPIP_MONITOR = 1
+
+.include "${.CURDIR}/../../Makefile.inc"
diff --git a/usr.sbin/i4b/dtmfdecode/Makefile b/usr.sbin/i4b/dtmfdecode/Makefile
new file mode 100644
index 0000000..74a9df7
--- /dev/null
+++ b/usr.sbin/i4b/dtmfdecode/Makefile
@@ -0,0 +1,19 @@
+#---------------------------------------------------------------------------
+#
+# $FreeBSD$
+#
+# last edit-date: [Tue Dec 14 21:17:46 1999]
+#
+#---------------------------------------------------------------------------
+
+PROG= dtmfdecode
+
+CFLAGS+= -DDEBUG
+
+# libm is only necessary if USE_COS is defined in the source
+#LDADD= -lm
+
+test: ${PROG}
+ uudecode -p < dtmfsounds.al.uu | ./${PROG}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/i4b/dtmfdecode/dtmfdecode.1 b/usr.sbin/i4b/dtmfdecode/dtmfdecode.1
new file mode 100644
index 0000000..a5b1d79c
--- /dev/null
+++ b/usr.sbin/i4b/dtmfdecode/dtmfdecode.1
@@ -0,0 +1,69 @@
+.\"
+.\" Copyright (c) 1999 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: dtmfdecode.1,v 1.6 1999/12/13 22:11:55 hm Exp $
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Mon Dec 13 22:53:13 1999]
+.\"
+.\"
+.Dd February 15, 1999
+.Dt DTMFDECODE 1
+.Os
+.Sh NAME
+.Nm dtmfdecode
+.Nd decodes DTMF tones from A-law audio data
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+The
+.Nm
+utility
+is part of the isdn4bsd package and is used to detect DTMF tones in the
+audio stream.
+.Pp
+It reads audio G.711 A-Law coded data from stdin and outputs the detected
+numbers values as ASCII charcters to stdout.
+.Pp
+The detector is implemented as 8 narrow band-pass filters realized with
+an integer double-cross recursive algorithm. Various ad-hoc methods are
+employed to provide hysteresis and anti-bounce for the detected signals.
+.Sh EXAMPLES
+The command:
+.Bd -literal -offset indent
+dtmfdecode < beep.al
+.Ed
+.Pp
+will print a "1" to stdout.
+.Sh STANDARDS
+ITU Recommendations G.711
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+utility was written by
+.An Poul-Henning Kamp Aq phk@FreeBSD.org .
+This man page was written by
+.An Hellmuth Michaelis Aq hm@FreeBSD.org .
diff --git a/usr.sbin/i4b/dtmfdecode/dtmfdecode.c b/usr.sbin/i4b/dtmfdecode/dtmfdecode.c
new file mode 100644
index 0000000..1ef49a9
--- /dev/null
+++ b/usr.sbin/i4b/dtmfdecode/dtmfdecode.c
@@ -0,0 +1,152 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ *
+ * $Id: dtmfdecode.c,v 1.6 1999/12/13 21:25:24 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * Extract DTMF signalling from ISDN4BSD A-law coded audio data
+ *
+ * A-Law to linear conversion from the sox package.
+ *
+ */
+
+#include <stdio.h>
+#include <math.h>
+
+/* Integer math scaling factor */
+#define FSC (1<<12)
+
+/* Alaw parameters */
+#define SIGN_BIT (0x80) /* Sign bit for a A-law byte. */
+#define QUANT_MASK (0xf) /* Quantization field mask. */
+#define SEG_SHIFT (4) /* Left shift for segment number. */
+#define SEG_MASK (0x70) /* Segment field mask. */
+
+static int
+alaw2linear(a_val)
+ unsigned char a_val;
+{
+ int t;
+ int seg;
+
+ a_val ^= 0x55;
+
+ t = (a_val & QUANT_MASK) << 4;
+ seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT;
+ switch (seg) {
+ case 0:
+ t += 8;
+ break;
+ case 1:
+ t += 0x108;
+ break;
+ default:
+ t += 0x108;
+ t <<= seg - 1;
+ }
+ return ((a_val & SIGN_BIT) ? t : -t);
+}
+
+#ifdef USE_COS
+/* The frequencies we're trying to detect */
+static int dtmf[8] = {697, 770, 852, 941, 1209, 1336, 1477, 1633};
+#else
+/* precalculated: p1[kk] = (-cos(2 * 3.141592 * dtmf[kk] / 8000.0) * FSC) */
+static int p1[8] = {-3497, -3369, -3212, -3027, -2384, -2040, -1635, -1164};
+#endif
+
+/* This is the Q of the filter (pole radius) */
+#define POLRAD .99
+
+#define P2 ((int)(POLRAD*POLRAD*FSC))
+
+int
+main(int argc, char **argv)
+{
+ int i, kk, t, nn, s, so, ia;
+ int x, c, d, f, h[8], k[8], n, y[8];
+#ifdef USE_COS
+ int p1[8];
+#endif
+ int alaw[256];
+ char key[256];
+
+ for (kk = 0; kk < 8; kk++) {
+ y[kk] = h[kk] = k[kk] = 0;
+#ifdef USE_COS
+ p1[kk] = (-cos(2 * 3.141592 * dtmf[kk] / 8000.0) * FSC);
+#endif
+ }
+
+ for (i = 0; i < 256; i++) {
+ key[i] = '?';
+ alaw[i] = alaw2linear(i) / (32768/FSC);
+ }
+
+ /* We encode the tones in 8 bits, translate those to symbol */
+ key[0x00] = '\0';
+
+ key[0x11] = '1'; key[0x12] = '4'; key[0x14] = '7'; key[0x18] = '*';
+ key[0x21] = '2'; key[0x22] = '5'; key[0x24] = '8'; key[0x28] = '0';
+ key[0x41] = '3'; key[0x42] = '6'; key[0x44] = '9'; key[0x48] = '#';
+ key[0x81] = 'A'; key[0x82] = 'B'; key[0x84] = 'C'; key[0x88] = 'D';
+
+ nn = 0;
+ ia = 0;
+ so = 0;
+ t = 0;
+ while ((i = getchar()) != EOF)
+ {
+ t++;
+
+ /* Convert to our format */
+ x = alaw[i];
+
+ /* Input amplitude */
+ if (x > 0)
+ ia += (x - ia) / 128;
+ else
+ ia += (-x - ia) / 128;
+
+ /* For each tone */
+ s = 0;
+ for(kk = 0; kk < 8; kk++) {
+
+ /* Turn the crank */
+ c = (P2 * (x - k[kk])) / FSC;
+ d = x + c;
+ f = (p1[kk] * (d - h[kk])) / FSC;
+ n = x - k[kk] - c;
+ k[kk] = h[kk] + f;
+ h[kk] = f + d;
+
+ /* Detect and Average */
+ if (n > 0)
+ y[kk] += (n - y[kk]) / 64;
+ else
+ y[kk] += (-n - y[kk]) / 64;
+
+ /* Threshold */
+ if (y[kk] > FSC/10 && y[kk] > ia)
+ s |= 1 << kk;
+ }
+
+ /* Hysteresis and noise supressor */
+ if (s != so) {
+/* printf("x %d %x -> %x\n",t,so, s); */
+ nn = 0;
+ so = s;
+ } else if (nn++ == 520 && key[s]) {
+ putchar(key[s]);
+/* printf(" %d %x\n",t,s); */
+ }
+ }
+ putchar('\n');
+ return (0);
+}
diff --git a/usr.sbin/i4b/dtmfdecode/dtmfsounds.al.uu b/usr.sbin/i4b/dtmfdecode/dtmfsounds.al.uu
new file mode 100644
index 0000000..f4e0c07
--- /dev/null
+++ b/usr.sbin/i4b/dtmfdecode/dtmfsounds.al.uu
@@ -0,0 +1,2098 @@
+$FreeBSD$
+begin 666 dtmfsounds.al
+M5555U=74U-564-34UU%5U-?1UM'7UM37U=5555145%545%155%555%555=75
+M5575U=75U-15U5555575U575U=75U=75U=34U-35U-35U=75U=55U=5555%0
+MU-374535U-;7T=?6U]?4U-75U555551555555555555555555%5555145=75
+MU-34U-34U-35U=75U=75U=55U=75U=55555555755=5545/4U-=15-37T=;1
+MU];7U]34U=5555555%145%145%555555U555U=55U=34U=75U=75U-75U=35
+MU-74U=74U-75U=75U=75U=55U=75U=564-34UU!4U-36U]'6UM?7U-35U555
+M55555555555455155555555555545%55U-74U-34U-34U=74U-35U=35U=75
+MU=75U=75U=75U=75U590U-374534U-'6T=;6U]?5U-555%145%155%545555
+M5555U555U=75U=74U-5555755=75U=74U=75U=75U=35U=34U=74U-35U=75
+M555545/4U-=15-34T=;1U];7U]34U=55555555555%555%545%555=555575
+M5535U=34U-34U-34U-34U-75U=555555555555555555U=75U=514]34UU%4
+MU-?1UM'6UM?6U-15U51555155514551455555=75U=75U=75U=35U=75U=75
+MU=75U-34U-34U-75U=75U=75U=74U=75U=5555%0U-374535U-'6T=;1UM;4
+MU-75555555145%555555555555555575U5555=75U-34U-34U-74U=75U-74
+MU=75U=5555555=55U=75U=755E#4U-=15-34T=;1UM;7U]34U=5555555514
+M5%555%5555155575U=74U=74U55555555=75U-35U-34U-75U=75U-75U=75
+M5555555555104]74UU%5U-;0UM#6UM?7U=35U-755554551455155%155575
+MU=75U=155U755=75U-34U-34U-34U-75U575U555U=55U5555575U=75U5%3
+MU-375E74U]'6T=?6U]?4U-755=5555145%5555555555U=55U555U575U-55
+MU=75U=75U=34U=35U=75U=75U-75U=74U-75U=75U=7545/4U-=15-74UM36
+MU];7U]34U=75U5555%5455555555555555555%5555=4U=74U-34U]34U-35
+MU=75U=75U555555555555555U555U5564]34UU94U-?1U]'7UM37U-35U=75
+M555555145%5455155575U=75U=75U=?4U=75U=75U=75U=75U=75U-35U-75
+MU=75U=35U=75U=7555%3U-?74534U-;7UM?6U]?7U-35U555555555545%14
+M55555575U=75U5545%55U=74U]?7U-34U-35U=75U=75U=75U=75U=75U=75
+MU=5545/5U-965=?7T=;1U];4U]75U5555555555555155%5555555=75U=35
+MU=74U-75U=75U-74U=75U=75U=35U=35U-74U=75U=75555555514]37UE95
+MU-31UM'7UM?7U-35U-5555555%545%145%155575U575U=345%75U=34U-34
+MU-34U=75U=75U=75U=75555555555=55U575U5%3U-374534U]'6T=;1U]?4
+MU-755554551555555555555555555=75U575U-35U=75U=35U-75U-75U-34
+MU=75U=75U=75U=75U=75U55545/5U-=15-74UM;6U];4U]34U=5555555555
+M55555%1555555=55U=75U5145575U=77U-?7U]34U-35U=75U=75U=55U=55
+MU=75U=75U=564]34UU%4U=36U];7U]37U=35U=75555555555%555%555%55
+M5=75U=75U=34U=55U=75U=75U-75U=74U=74U-35U=75U-34U-75U57555%3
+MU=774534U-'6T=;6U];4U-74U555551555545514555555555=75U=155-75
+MU-34U-34U-35U=75U=75U=755=5555555=75U=755=5545#5U-=15=37T=;1
+MU];7U]74U=5555155%555%155%145575U=75U=75U=74U-35U=75U=35U=75
+MU=74U=75U=75U=74U=75U=55U555U5514]75UU%4U=31U]'7UM?7U-35U555
+M5%1455155%55555555555=75U=355515U=75U-34U-37U-35U=75U=35U-75
+M555555555575U=75U590U-375E77U]'6T=;1U]?4U-755555555555555514
+M555455155555U=55U-35U=75U-74U-74U-74U=75U=75U=35U=75U=75U=55
+MU55545+5U=115-37T=;0UM;7UM37U-15U5555%5455555%555%555=75U575
+MU%54U=75U-75U-34U-35U=75U=74U=74U=75U575U=75U=75U=514-34UU%4
+MU-?1UM'6UM?7U-35U5755555551555145515555555755=75U=34U=75U575
+MU555U=55U=75U=75U=75U=34U-75U=75U=75U590U-?65E37U-'6T=?6U];4
+MU]3555545%145U145%1455555575U=55U=555-75U-34U-37U-34U-34U=35
+MU=75U=75U555555555555=755E#4U]965=37T-;1UM;4U]745=55U5555514
+M5%1455155=755=75U=75U=74U]75U=75U=75U=75U=75U=75U=74U-35U-35
+MU=75U555U5514]75UU%4U-31UM'7UM?7U-?4U-7555555%145%=455555575
+MU=755=755%15U=74U-34U-34U-34U-75U=75U=55U=55U=55U=75U=75U590
+MU]365E77U];6T=?7U=35U5755555555555555%5555555=55U555U=75U-?5
+MU=75U-75U-35U-34U=75U=75U=75U=75U=555=55U=7545/5U-=65=37T=;1
+MU]'7UM34U=7555555%1455555%555555U=755=75U5545=75U-34U-34U-75
+MU=75U=75U=755=75U=55U=75U=75U=564-37UE95U-31UM'6UM?7U-35U=75
+M55555%155%14551555555555U=75U=37U]35U=35U=755=75U=74U-35U-75
+MU=75U=75U=75U=75U5%3U=374534U-;7UM?6U-?4U]345=5555545%545555
+M55555555U555U=555%55U=74U-34U]34U-35U=75U=75U=75U=75U=75U=75
+MU=7545/5U-=15-37T='0UM'7U]34U=35U5555%145%14555455555555U=74
+MU-74U]35U5755555U575U=74U-34U-75U=75U=74U=74U=74U-164]34UU%4
+MU-31U]'6T=?7U-35U=755=75555555545%15551555555575U515U-34U-34
+MU-35U-34U-34U-75U55555555555U=75U=75U590U-364534U-;6T=;1U];7
+MU-7555555555555555555%5555755555U555U=34U=75U-74U-35U=35U-74
+MU-74U-35U=75U=75U=75U=755E#4U-=15-34UM?1U]'7UM?7U-3555555%55
+M55555%155%1555555575U=545=34U-?7U-34U-35U=755=75U=75U=35U=75
+MU=75U=74U=564-37UE94U-31UM'7UM37U-35U%5555545%145%555%555575
+MU=75U=75U=74U]75U=75U-74U=74U=75U=75U=75U=75U=35U=75U=75U5%3
+MU=374534U-'6T=?6U-?4U-7555555%145%145%145%55U575U-75U=355%75
+MU=75U=35U=34U-35U-35U=75U-75U=75U=75U=75U=755E/4U-965=37T=;1
+MUM;7U]74U=75555555545%=45%145%155575U=75U=75U-?5U=75U=34U=75
+MU-?5U-15U]=5U=55U-?4U=355=555%534-54UM57U]'4UM#4U]37U577UE+4
+MU432V5?%4U]40-!5U]I4U%-0U=77UE;3UU/64-'24M'45]W55]'1UU574%'5
+MUM34TU14UU!75E'7UM%5U=!77E=4T=+3W];65%]15U70T=S15%105U16UE!6
+MT]7655Q55%?1U-957UU7V<?;TE]#6=39S=A564-15MS:5]944-95U-=65E%4
+MT=#3UU)45U97U];1UM34U=;0T-564577T-;75%%04577U]?555=75U35U-34
+M55175%555]375=?6T]/755155=75U]15U-37U]545%145=74U=174]75U5%4
+MU-?6U-?4UM#0UM?5U5545=545%975555U=545%75U=755575U=36UM75U555
+M5=7455555=74U]?4U-75U]74U-74U-75U57555%0U=774%15U=;7UM;6U];4
+MU-355=35U=5555175%54557555145=755=355U75U=34U=34U-35U-75U-34
+MU]35U=75U=7555755=75U=555E/5U-=15-37T]'3T=;7U-74U=75U5545%54
+M5555U=755%=45%555%55U575U-?555555=75U=74U-74U-?7U=755=755=55
+MU=75U-34U5514]37UE%4U-?1UM#7UM?7U]?5U=7555555%14555555555%55
+M5=75U=75U515U=74U-37U]34U=755=75U=35U=35U=75U=75U=75U=74U%90
+MU=374577U]'1T-;1UM?4U-755=75U5545%545=55555555555%555=55U=34
+MU=555=75U-35U=75U-74U=74U=35U=74U=74U=75U=755E/5U-=15-34T='1
+MUM'7UM37U=75U555555555145U=45%555=7555555=545-75U-37U]34U-34
+MU-74U=75U-355575U=555=75U=35U=114-34UE%5U]31U]'7T=;4U-15U-55
+MU55555=75555U51555555=75U=35U=74U]355=555=755575U=34U-34U-75
+MU-75U=75U=75U=55U5%3U-375E34U]'6T=?1U]?4U]34U555U5555%145%15
+M55555555U=75U=755535U=37U-34U-35U-35U-75U=55U=75U=75U=75U=34
+MU-755E#4U-=15-37T=;1UM;7U]34U=555555555455155555555455555=75
+MU=75U-;7U-545=36U%35U];65U345U355=35T-?4UE-4U5#0T=154]35U5!7
+MU]71UM37UM745%545=37UM9555=75U35U=34U%575U975577U]355575U=?7
+MUM;7U]34U%555574U=555-75U=35U=74U575U%=1U-774577UM/1T-;7U=55
+MU5755575U5555U145%555=555=55U=75U-35U=74U-75U=75U-74U=74U-74
+MU=74U=75U=34U-?4U=55555545/4U-=15-37T=;1UM;4UM34U=35U5545%14
+M5%15555555555=55U=75U=755%75U=34U-37U-?4U-34U=755=7555555=55
+M5=55U=75U5514M74UU95U-?1UM#6UM?7U=75U-7555545%15555555555555
+MU=75U=74U575U-75U=755=75U=75U=75U=75U=34U-74U=34U=35U=7555!3
+M5=774%?5U]#0T=?6U-?7U]745=7555155554U554U5155555U=75U=3455=5
+MU=34U=34U-34U-74U575U=75U=75U575U=555=75U=5545/5U-915=37T-;0
+MUM'4U]74U554U555555555145%1455555575U555U=74U]?55-?6U=;75=17
+M5];4UM94T=14UU!4U%'7U%?3U%31U==17=57U%=5UM31T-76T]70U=745M75
+M5M945]=75=17U%17U595UU37U%7555545-37U-37U]?4U=34U-34U]545%35
+MU575UM1555155=75U%=3U=?65E35U-?7UM31U];7U-755=555%15U5145514
+M5%35U=75U5555-74U=37U]155=75U=77U]34U-75U=75U=75U=345575U-35
+MU]?55E/5U-105-74UM?1UM'7UM?7U-35U5555%175%555%555555U=75U=75
+MU=355=75U=35U-34U-34U-35U-75U5555=55U5755575U=75U5514M74UE95
+MU-31UM'7T=?7U-35U575555555555%545%145=75U=35U=74U=75U-35U575
+M5575U=74U-34U-35U=75U575U=75U=75U=7555%0U=374574U]'6T-?1U-?4
+MU=755=555555555555555%55555555555=75U515U=74U-34U-34U=34U-35
+MU-75U=75U55555155=75U=755E/4U-965-?4T=;1UM'7UM34U=5555155%14
+M555555555=5555555555U=75U=?7U-35U-75U-35U=35U-75U=35U=75U=75
+MU=75557555514E74UU%4U-?1UM'6T=?6U]?4U=7555155%15555555555554
+M5575U=75U=545-75U=34U-?4U-34U-34U=75U=75U=75U=75U=75U=7555%2
+MU=774537U]'6T-;1U]?4U-755=5555555%545%145%555575U=75U=75U=74
+MU]35U=35U-75U=75U=75U=75U=74U-74U=34U=75U55545/5U]965=34UM;1
+MU];4U]34U=75U5555554555555555555U575U=75U=755%35U=34U]?7U-34
+MU=75U=75U=75U=75U=555=75U=35U=514]34UE95U]?1UM'6T=?7U-?4U-75
+M555555145%145%155555U=75U=74U575U-?4U=75U=75U=55U=75U=75U=34
+MU-74U=34U-35U=75U5%3U=?65E74U]'6T=?7U-?5U-75U555U55555555555
+M555555755=75U=75U5545=75U-34U]?7U]34U=75U=75U=755=555=75U=75
+MU=7545/4U]975=?4T=;1U];4U]34U=75U555555555155555555555555=75
+MU=35U=37U-75U=75U=75U=75U=74U=34U-75U=34U-35U=35U5504E74UU%4
+MU-31UM'6T=?7U=34U=75555555555%54555555555575U=75U=155=75U=35
+MU-34U-34U-35U-75U=75U=75U=75U=75U=75U5%3U=365U77U]'6T=;6U-?4
+MU=75555555555%555%555555U=75U=75U=34U=74U]355=755=75U=75U=75
+MU=75U=75U=74U-34U-35U=7545+5U-965=37T=?1U]?7U]745=75U5555554
+M5%14551455155=75U-34U-34U575U=74U-34U-34U-34U=75U=7555555=55
+MU=75U=55U5104E74UU95U]?1UM#1UM?4U=35U5555555555555145%155575
+MU=75U=35U-55U-35U=75U=75U=75U-75U=75U=75U=34U-34U-34U=7555%2
+MU=375E74U]'6T=?6U-?5U-75U=55U5555554555455555575U=75U=74U554
+M5=75U-34U-34U-34U-75U=75U=75U=75U=75U575U=5545+5U-975=?7T=;1
+MUM;4U]74U=75U5555%1455145555555555555555U=75U=37U-75U=75U=75
+MU=34U=34U-35U-75U=75U=74U=5555504E75UE95U-31UM'6T=?7U-34U-75
+M555555155555555555555555U555U=555%75U=34U-37U-34U=74U-75U=75
+MU=55U=75U=75U=75U593U=?65E74U]'6T=?6U-?4U-755555551555145555
+M5555U=75U=75U=75U575U]35U=75U=75U=75U=75U-34U=75U=75U=74U-35
+MU=355E/5U-965=?7T=;1UM;7U]74U=5555545%145%1455155%555575U=75
+MU=35U555U-34U-34U-34U-74U=35U=75U=75U=75U=75U=75U5514E74UE95
+MU-?1UM'6T=?6U-35U555555555155%5455145%155=75U=75U=75U=?4U=75
+MU=75U=75U=75U=34U=75U=34U-75U-34U=75U%93U-?15U77U]#1T-;6U]?4
+MU-35U55555555%145%155%145%555575U=75U5545=74U-34U]?4U]34U]34
+MU-34U=75U=75U575U575U57545+5U-965=37T=;1U];4U]34U55555545%55
+M55555555U55555755=74U=35U=77U]75U=75U=74U=74U=74U=75U=75U=75
+MU-34U-34U=564]77UE95U-?1UM'7UM37U-35U=755=555%55555555555575
+M55555=55U5755574U-?7U]34U-34U=75U=75U=75U=7555555%155=75U5%3
+MU=365E77U]#6T=;6U]?4U-55555555145%1455555555U5755=755=755575
+MU-34U-74U=34U-35U-35U-75U=75U=35U=74U-75U=755E/5U-965=37T=;1
+MUM;7U-34U555U555551455145%145%545555U=75U=74U%75U-74U-34U=34
+MU=35U-74U=75U=75U=74U=74U=74U-564]37UE95U-?1UM'7UM;7U-35U555
+M5%145%155%55551555555=75U=34U-35U=?7U-35U-75U=75U=75U-74U=75
+MU=75U=74U-35U=74U593U=?15U77U]'7T=?6U-35U-755555555555545%54
+M555555555=75U=75U=555=75U-34U]?7U]?7U]34U=755=55U575U=55U555
+MU57545+5U-965=?7T-;0UM;4U]755555551555555=5555155555U=75U=75
+MU-755574U]75U=75U=75U=74U-74U=75U=75U=74U-34U-35U=564]74UU95
+MU-36UM'7T=?7U-35U575U=5455155%145%155%555=55U=75U=755574U-34
+MU=34U-34U-34U=35U=75U=55U575U575U=755514U=745=75U=35U=34U-34
+MU-75U=75U=74U-75U5755=55U=75U=75U=755555U=35U=755=5555555575
+MU=75U-75U-34U-34U-35U=755%15U-15U=74U-34U-75U=755555U=555=75
+MU5755=75U=75U=74U=35U-75U5545575U=75U=55U=75U=75U-74U=75U575
+MU=74U-74U-555-74U%75U=34U-34U-34U-7555555555U=75U=55U=75U=75
+MU=555=75U=75U=37U=555555U5555=75U-74U-34U-74U=74U=74U=75U554
+MU=355575U=34U-34U-34U-35U=5555555=75U555U=75U=75U=55U5555554
+M5-75U=34U-34U=35U-34U-74U=55U=55U555555555755%35U-15U=75U-74
+MU-34U-35U-75U=7555555=555555U=75U=55U=75U5555574U]35U=555=55
+M5575U=75U=75U575U=755=75555555545-75U%75U-74U=35U=34U=75U-34
+MU-35U=35U=75U=75U5555575U=7555755515U=74U=75U-74U=75U=75U=75
+MU=75U=35U=75U=75U555U=745=74U-35U-74U=75U=55U5555575U=75U=75
+MU=75U=75U=74U=35U555U=34U=75U-35U-75U=75U=75U=75U=74U-74U-35
+MU=755535U=15U=75U-34U=75U=74U=34U=34U=75U=755575U575U=75U=34
+MU55555175555U=75U-35U-34U-35U-75U=75U=74U=75U=75U=555-74U%75
+MU-34U]35U=75U=55U5555555U=75U5555555U=75U=34U-75U=75U=37U]75
+MU=75U=755=75U=75U=75U=74U-34U-34U=5555175=745=74U-34U-34U=75
+MU=555=555575U555U575U=75U=74U-34U-34U5555%35U=37U-?4U]?4U-35
+MU=5555145%155575U=74U-34U574U-15U=74U]37U-35U5555575U=75U=75
+MU-75U55555145575U=75U-75U=74U]?5U-755=55U=75U=75U-34U-34U-15
+MU=75U=75U=355=35U%15U=74U=34U]34U-34U-75U=75U575U=75U=74U=34
+MU=555%145%145%=4U=34U]34U]35U-55U575U=74U=35U=55U555U=75U%55
+MU-375=75U=35U-74U-?4U-35U=75555555555575U=75U=75U=35U-35U=35
+MU=34U=75U=75U=75U=75U=75U=74U=75U=75U-34U5755534U-35U=35U-34
+MU=75U=75U=35U-75U=75U5755=5555555=55U=74U=75U5545=55U-34U-35
+MU-35U=75U555U=75U=75U=74U-35U=545]75U%75U-34U-34U-35U=75U575
+MU=75U=5555555575U=75U=74U-75U575U=74U-555575U=75U575U=75U-34
+MU-35U=74U=74U=75U5545=745=74U-?4U]34U-75U=755=55U=75U5555=75
+MU=75U=75U555U=5555555%35U=34U=74U-74U-75U-75U=75U=75U=755=75
+MU=355%35U=15U=74U-74U-34U-35U=75U5555=75U=75U=75U=75U=55U575
+M5575U=75U-?5U=75U=75U-35U-74U=75U=75U=75U=34U=75U=545-75U%75
+MU=74U=34U-75U=75U=75U=75U=75U=75U-75U=75U=755=55551555=5U=74
+MU-34U-35U=35U=75U=75U=35U=75U=755555U514U-345=74U-34U=34U=75
+MU=75U=5555555575U=75U=75U-75U=75U=75U=35U-?7U-75U=755=755=75
+MU=35U=75U=75U555U=74U-755574U-=55=75U-74U=35U-34U=75U5555555
+M5575U575U=75U5555-74U=55U=555=74U-75U-35U=35U=34U-75U=755575
+MU=755575U=555=75U%75U-74U-?4U]34U=75U=75555555555555U575U=75
+MU=75U=75U=75U=74U]5555555=75U=75U=35U-75U-35U=75U=74U-75U555
+MU=755%55U=75U-35U-35U-34U-74U-74U=75U=75U=75U=75U=75U=755=75
+M5535U=75U=75U=75U=75U=74U-34U=755575U=75U=755575U=155=75U=74
+MU-35U]34U-75U-74U574U=74U=34U=35U=75U=75U=55U575U=?5U=755555
+M5555U5555=75U-75U=34U-34U-74U=555-75U515U=74U=75U-75U=75U=75
+MU5555=555575U=75U=555=755=75U=75U%55U=75U-75U-74U=74U=75U=75
+MU=75U-55U555U575U5545=745-74U-37U]?7U-35U=75U=75U=555555U555
+M5575U=74U=74U-35U=755=77U=75U=34U-75U=75U575U=75U=35U-75U=75
+MU=755574U-?5U=34U-34U=75U=75U=75U=75U=55U5555575U=75U575U=74
+MU-75U=155-75U=34U-34U-74U=75U=5555555=55U=555=55U=555-75U515
+MU=74U-37U-75U=75U=75U=75U=75U=55U=75U=75U=75U=75U=75U574U]75
+M55555%555=75U=74U=35U=75U=75U=74U-75U=54U=345575U=34U-34U=75
+MU-75U575U55555555555U=75U=75U=75U=755=755535U=34U-34U-74U=75
+MU=555555U=75U=75U575U=74U574U-155=74U=34U-35U=34U-75U=75U=75
+M55755=75U=75U=75U=75U=75U=55U=35U55555555555U=75U-74U-35U-75
+MU=75U-34U=355=74U515U=74U=34U-35U=75U=75U-75U=555575U=75U=55
+M55555=75U=75U=545=75U-35U-35U-35U=74U=74U=755=5555555=55U555
+MU-345575U=35U-34U=34U-75U=75U=75U=75U=74U=75U=55U55555555=55
+M5=77U-75U555557555755=75U=75U=75U=75U=35U=755574U-55U=75U-74
+MU=35U=75U=75U-75U-35U=75U57555555=75U=75U55555555U75U=35U-35
+MU-74U=35U=35U=74U=74U-75U-75U%555=74U%74U-?4U-34U=755=75U=55
+M5=55U575U57555555575U=34U-35U=75U=75U]?5U=35U=75U555U=75U=75
+MU=35U=34U=74U=75U555U-345574U=34U-74U=35U=75U=75U=75U=75U=75
+MU=755=555575U=755=7555=45=74U-74U-75U=75U575U=75U=75U=55U575
+MU=755575U=545=74U-34U-?4U-75U=55U55555555=75U-34U-34U-34U=75
+MU=555575U=34U555551555555575U=75U=34U=34U-34U-74U=555=35U575
+MU=34U-34U-34U=35U=75U=755=7555555=55U57555555=755555U=545=75
+MU=75U-34U-35U=3555555=555=75U=75U574U555U=755%75U=35U-34U-35
+MU=75U=35U=75U=75U=75U=55U=55U555U-555555U574U%55555555755=75
+M5555U575U=74U-74U=34U=74U577U-15U=75U-34U-?4U-75U=75U=74U=75
+MU=75U=75U=75U=555575U5555=755%15U=74U-34U-35U-75U=755=75U=35
+MU=75U=75U=555=3555=5U=74U-36U-34U-75U5755=75U=7555555575U=75
+MU=35U=755=75U=55U-?555555=75U-74U-75U5555=75U=34U-34U-74U575
+MU-355%55U=74U-74U=35U=74U=74U-75U=75U=755=75U=75U=75U=755575
+M55145=75U=35U=35U=755=75U575U=74U-75U=5555555%75U555U=74U]34
+MU=34U-35U=75U=55U5755=75U555U=75U=55U575U-755=555=3755555555
+M5=755555U555U=34U]34U-34U-75U=555=55U514U=34U-34U%75U=75U=74
+MU-35U=55557555755=55U=55U=34U=75U=545=75U-?4U-34U-35U=75U=75
+MU555U=75U=75U=75U555U=755%75U=34U-35U-34U=75U=555555U575U=75
+MU=75U575U=75U=555555U=74U]355=55U=55U=74U-34U=75U=75U=75U=34
+MU=355=77U-545=75U=35U=35U-35U-75U575U=75U=75U555U5755575U-75
+MU555U=74U535U=74U-34U=74U=74U-34U=75U=755=75U=75U=555=35U515
+MU=74U-34U]75U=75U=755=75U575U=34U=75U575U555U=75U=75U=75U-35
+MU555555555555=75U=74U-74U-74U-34U-35U=55U-355=55U=55U=755=74
+MU=34U-34U-75U575U=75U=55U555U5555=755575U515U-34U-34U=75U-75
+MU=75U=34U=35U=75U=75U5555574U-54U555U-75U-34U-34U-34U-75U=75
+MU5555%555575U=74U-34U=75U=55U=36U]34U575U5755555U=75U=34U-74
+MU-34U-35U=555=34U%35U=75U=75U=35U-35U-35U=75U575U5555575U575
+MU%75U=755=55U=55U=35U-34U-74U=74U-74U-75U-55U575U5755515U575
+MU-345=55U=34U-37U-35U-55U=75U=75U=7555555=75U=75U=7555555575
+MU=75U]1555755575U575U=75U575U=74U=74U=75U=155=75U=545575U-34
+MU-35U-35U=75U=35U=355=55555555555=55U=75U535U=75U575U=74U-34
+MU-74U-74U=75U=755=55U=35U-55U-555=35U555U=74U=34U-34U-75U=55
+M5=555=555=55U=55U555U555U=75U=75U=75U-35U=755%755=55U=155555
+M5=75U=755=55U=755575U-755%155=75U-37U-?4U=74U=75U=5555555=75
+MU=75U=75U=75U-74U-75U-545=35U=35U=75U=75U-75U=75U=75U=555554
+M5=355574U%545575U-74U-34U-34U-?4U-34U=55557555555575U=74U=75
+MU=75U=355=77U-55U555U575U-75U575U-77U=745575U=555=155-35U515
+MU-75U]34U-75U=75U=75U=74U=75U=55U=35U555U=55U=75U=55U=145=75
+MU-75U-34U-35U%75U-74U=74U=74U=75U=35U=55U=545%35U=34U-34U=34
+MU=155575U=55U=55U=355=755=75U=75U=?5U=35U=55U]?5U575U=74U=74
+M5=345=75U=75U-75U-74U-34U574U-545%75U-75U-75U=74U=74U-35U=35
+MU=75U=75U=75U575U=75U=355=745554U=35U-74U]35U-75U=75U=755=74
+MU=755555U=155=35U575U=74U]34U-35U=75U=75U=55U-75U=7555555555
+MU=55U=75U=75U=75U=34U55555555=75U=55U=75U=75U]34U-75U-34U575
+MU-555%75U-35U-34U-75U=34U=75U555U515U=55U5555=755=75U-74U574
+M5=555=55U-75U-35U-55U5755=755=555=555=15U=55U535U-575575U=77
+MU-74U-35U=755=745575U=55U=75U=555=555=75U=7555555=37U]?5U555
+MU515U=755=74U575U-75U=35U=15U=55U=34U%75U-74U=34U-35U-35U=75
+M5575U5155=75U=35U-5555155574U=77U-?45=75U=75U=75U=34U-37U-74
+MU=75U=5555555575U555U-345-755=74U=75U-35U=745=74U-745=75U=75
+MU=75U=74U=755=755=755575U]?5U=5555555575U-55U=755-775-76U=74
+M5=155=34U==55%755=77U=75U-355=75U=74U-34U=75U555U=74U5755=74
+MU-35U=75U5545=75U-355=?5U=35U-75U575U=75U=34U=755=555=75U535
+MU-34U-34U=75U=355=75U555U-35U-75U=55U555U555U=35U-55U-75U-37
+MU5555555U555U=75U-75U-35U=755555U=755=555=75U=55U-34U]74U575
+MU=75U-75U5555=555=155=55U=55U-55U-155=?6U=54U==5U-15U=75UM?4
+MU537U5755=545-=7U-94U-55U=555=145%74U-35U=?4U-745=555=74U-74
+MU=34U-545517U575U%74U]37U=34U=74U%555%75U=35U=555=34U=15U-35
+MU]75U%75U%755=755535U-76U-34U=355575U555U515U577U=745-745=74
+M5%755=35U=355=755%34U-35U=35U-74U=74U=75U-74U-35U=75U555U514
+MU=755=75U=35U-34U-35U-75U=75U55555555575U=75U=55U=75U5755%15
+M5=75U-?4U=35U=7555555575U5555575U575U575U=755%75U=545=75U-75
+MU-75U-35U-34U-35U=755=55U575U=75U=75U=755=755%1555=45575U=75
+MU-34U-34U=34U=34U-3555755575U=755=75U5755=755=75U=75U=755=75
+M5=75U=555=55U=355=35U=35U-34U%55U=75U=37U-35U=5555555=55U575
+MU575U=74U=34U=15U554U=74U=75U=?4U-355=155=75U=74U-755555U555
+M5575U=555=75U-75U555U=555%75U-74U-34U=75557555755=75U=75U=75
+MU-35U=75U=15U=34U-75U=35U575U57555755=75U=75U=34U=55U=75U=75
+MU=55U=75U=74UM35U=555=75U=75U-75U=75U=74U-35U=75U=555=74U-75
+MU=34U-?4U=155=155-555=75U=75U=55U=55U=75U=75U57555555575U515
+M5=34U-?4U-?4U=555=755=35U=75U=55U=7555755-765-77U=74U577U534
+MU%34U%75U%74U57555555575U=55U=75U=355=555=55U-?5U-75U555U=55
+MU=75U=75U=35U=75U=74U=355555U=15U=34U-?4U=35U=74U=74U=745575
+MU5545=55U=75U=75U=74U-555%75U=55U-35U=75U=35U-35U-75U-55U-74
+MU-75U5555%555-75U575U575U=35U-75U=35U=75U=7555755555U=35U=75
+MU575U=75U5545555U=74U]55U=55U5555=75U=75U=75U=35U=75U-75U%55
+MU=345=74U=75U-75U=75U-74U-75U-75U=555=555=75U=75U555U=555515
+M5=555=74U-?7U]34U-34U-75U=75U=755=5555555=555515U=75U=74U-34
+MU=34U=75U=74U=75U=55U5555=75U=75U=75U=74U=555%15U=75U]34U575
+M55755575U=74U-35U-35U-55U=55U=555=75U-75U-34U=35U=75U=74U=34
+MU=75U=555555U=555575U=75U=34U%545=75U535U-37U-34U=35U=75U=74
+MU=75U575U=555555U555U-74U=74U-34U-34U-35U=75U=74U=75U=74U=75
+M5=75U=755555U-355575U=75U=345=755=75U=75U=75U=55U=75U=75U=35
+MU=345%75U=155=75U=34U-35U-75U=75U=755555U555U=75U=75U=55U=74
+MU-555U15U=555=75U-35U-34U-35U=74U=74U=75U=75U=75U=555=75U-74
+MU-37U]?4U-35U=75U=555555555555355555U=75U=75U=?7U=545=75U=77
+MU]74U=75U575U=755=75U=75U=75U=75U=35U5555=74U=75U-34U-355575
+MU-755=75U-75U555U=555=555=755=75U-?45%355=555=74U-?7U-34U=75
+MU=75U=75U55455555=75U=755535U=555=74U]?7U-34U]34U-75U555U=75
+MU%75555555555=75U=34U]755%15U=75U]?4U=35U=75U=75U=75U=35U-75
+MU=75U=75U-555-74U-75U=75U=34U-74U-75U575U=75U=75U575U575U=55
+M5=75U=37U5175%75U555U-34U-37U-34U-755=555=35U=75U575U555U554
+MU5755=75U=75U=75U=74U=75U=755555U5755=75U=75U=75U=75U-?45U=5
+M5=755=?4U=55U555U=75U=75U-75U=35U=75U=75U-355574U-155=74U-77
+MU-35U=35U=75U=34U=75U=75U=75U=755=555=74U]545U555=155=35U=35
+MU-35U-75U-34U-74U=75U=75U=75U5555%75U575U-74U-34U-34U-74U555
+M555555755575U575U=75U=75U=?7U5575575U=74U-75U=75U=75U=75U=55
+MU=75U=75U=74U-75U=55U=74U574U=34U-34U=35U=75U=35U=74U=75U=75
+MU-75U=55U575U-355%145=555575U=34U-34U-74U-74U-35U=75U-74U-34
+MU=755535U=35U=34U]?7U-35U=5555555555U=555=75U=75U=75U=75U=74
+MU]545U15U=75U];4U-75U=755=75U=75U=35U=75U=75U=75U=555%75U%55
+MU=37U-34U-?55=75U=75U=5555555555U=75U=75U=74U-?7U5175%15U555
+MU-34U]?4U-35U=75U=75U=75U=75U575U=75U555U=755=74U=35U=75U=34
+MU-75U-74U=75U=7555155575U=75U=74U]?45%=455555=77U-75U=75U=75
+MU=75U-75U=35U=75U=75U=755575U-35U=34U]34U=75U=755575U=555=75
+M55555=75U=75U=55U=77U]575U15U=755-77U]355=37U-145-75U-75U=37
+MU-355%!6T]'4UE95T]75U5%4U]?5U5=5U]155515U-155%15U=555=74U-35
+M5=74U-;6U%145%755=77UM?4U=35U=555=74U=35U=34U-75U=75U-35U=55
+MU=74U=74U-55U=74U-75U-74U=74U=755-74U=555=34U=555=75U-145%17
+M5%55U-75U]34U]74U]75U-34U-75U=55U=755575U=555%?5U-155=37UM34
+MU=75U-75U=77U-755575U=755=75U=755%75U=34U-545U545=75U]?4U]?4
+MU%75U-355555U-55U=75U-55U-75U]55U=75UU75U-75U-74U%74U=74U=74
+MU-34U=75U=555515U575U575U-?7U5165%55U557U=74U-34U]34U-355555
+M5=555575U5555515U514U=74U=74U=75U-34U=35U-75U-75U=75U555U=75
+M5575U=75U=75U]?55%=45=75U=?7U-155=55U=155=75U%55U=75U=75U=75
+MU=755555U=35U=34U-75U-355=74U=755-74U5555=75U=555=75U=755=77
+MU]575U145=555=75U]35U-75U-35U-75U-75U=74U-75U=55U=555-75U-75
+MU-34U=35U-35U-34U=7555555%555=75U=55U-55U555U-?7U%175%55U=74
+MUM?4U-75U=75U515U=75U%54U=75U=55U=7555175=345575U-?5U=34U=15
+M5=55U-35U-75U-74U574U57555555=74U]?55%=75%75U575U-77U=34U=75
+MU-35U-55U-55U554U%55U575U535U-35U=75U-74U-34U-75U=74U-75U=74
+MU%54U575U515U-74U%77UM545E=55575U=34U=75U575U555U555U=55U=74
+MU-75U-74U-555575U]74U=76U-77U-775=755%755%755=755=755=155=15
+MU=54U=?7UU565%15U5565-35U-15U-?4U]15U-?5U-14U=55U=54U=75U554
+M5=74U=34U-35U-74U-74U-74U57455755=755%55U=555=15U-35U];455=6
+M5%555=34U]34U=35U=355=75U=75U=34U=355=75U-355515U-75U=74U]?4
+MU=55U-35U=75U-34U=75U575U575U5555=555-77U]545U1455755%77U-?7
+MU=74U-75U=55U=75U575U=75U=75U5555%74U-75U=34U-35U=75U-35U-75
+MU-55U=755555U=755=755=75U=3455175%55U=74UM;4U-745=74U=75U=35
+MU=34U=55U=75U=34U%54U=745575U=74U=34U-35U-35U=75U=75U=75U=75
+MU=755=75U=35U-?55%=45-75U514U=75U]34U-34U-74U575U57555755=75
+M5=755515U=555=75U-34U-35U-75U=55U5755555U=75U=75U5755=75U=74
+MU]545E145555U=34U-75U575U-75U%75U-74U%75U=74U574U=745=74U-74
+MU=74U=34U-75U=75U=755575U=74U=35U=35U=35U=35U-?7U5165%=55=55
+M5=75U-?4U-?4U-34U-75U=75U-75U575U55555145=745=74U-34U-34U=34
+MU-35U=75U=75U=75U=75U=55U=555=75U-?55%=55-75U=74U]75U=55U555
+M5575U=75U-74U-34U-35U=745575U=35U-35U-34U=35U=75U=755555U=75
+MU=5555555575U=75U=74U]545U55U=355574U=34U-34U-34U=75U=75U=55
+MU555555555555575U%75U=74U=35U=75U=35U=75U=75U=555=75U=755=75
+MU=75U-?7U5175%35U=75U-?4U=75U=75U-75U=35U-74U-35U=75U=75U=55
+MU=345=75U=35U=75U=75U=75U-34U=74U-75U=75U=75U=75U=75U-?55U=4
+M5%75U555U=74U-34U-75U-75U=35U=34U=75U=75U=755575U=15U=75U-34
+MU=35U-75U=75U=75U-555=755=555=75U=75U=77U]545E145=555=37U=75
+MU=55U=555=75U=75U=75U=75U=75U=555-75U575U-34U-34U-75U=75U=74
+MU=75U=75U=75U=55U=75U=75U=?7U5175%55U=555-75U-34U-34U-34U-34
+MU=75U-75U=74U-34U=75U=355575U=75U-34U-75U=34U=75U=755=555555
+M55755575U=75U-?55%=45-75U=74U]35U=55U=75U575U=75U=75U555U=75
+MU-34U574U-15U-34U-74U=75U=75U=75U=75U=75U=35U-34U=75U5555=77
+MU]545E145575U575U-34U-34U-34U-35U=75U575U=55U5555=555-75U575
+MU-34U=34U-34U=35U-75U55555555575U=75U=55U555U=?7U5175%155575
+MU=34U=75U=75U=75U=75U-75U=75U=75U=74U-55U-755575U=34U-34U-75
+MU=75U=75U=75U=75U=75U=35U=755=75U-?55%=45%75U554U=34U-35U-34
+MU-34U=74U=75U=55U55555555%35U=555=75U-34U=35U=75U=35U=75U=55
+M5555U555U=75U=75U=74U]545U145=75U=77U]75U=75U=34U=34U=75U=35
+MU=35U=75U=555=75U-75U-34U-35U-35U-35U-7555755=755=75U=555=75
+MU=75U=?7U%175555U=555-75U=34U-34U-34U-74U-75U=75U=75U=34U555
+MU=34U=75U=35U=75U=75U=75U-75U=55U575U=75U=755=75U=75U-?55%=4
+M5%74U=74U]34U=555=75U=75U=75U575U5755=75U=74U575U=55U=74U-34
+MU-35U-75U=75U=75U=75U575U=75U=75U=75U=74U]575U145=74U575U-37
+MU-34U-75U-75U=55U5555555U=7555555-75U5555=74U=35U-34U=75U=35
+MU=75U=55U5555=7555555=75U=34U5175575U=75U=?4U=755=75U=555=75
+MU=75U=75U=75U=75U-55U=345=75U=35U-34U-34U-75U=75U-74U-75U-34
+MU-75U=75U575U-?55%=45-75U=545=75U-35U-35U-35U-75U-75U-74U-75
+MU=755575U=35U=75U-35U-35U=75U555U=75U=75U=74U=75U=755575U=77
+MU]545U145=75U=77U-34U-75U=74U=74U=34U=75U=75U-75U=555%55U555
+MU=74U=35U=75U=75U=74U=34U=75U=75U=75U=75U=75U=?7U5175515U=75
+M5=74U-34U-34U=75U=75U=75U=75U=755=755555U=755=75U=35U-75U=75
+MU=74U=75U5555=755555U5555=75U=75U-355%=45%755=75U-?5U=75U=75
+MU=75U=75U=75U=75U=55U=755575U=55U=74U-34U-34U-34U=35U=75U=55
+MU=5555555=55U=75U=77U]545U55U=74U555U=74U-37U-34U=34U=75U=75
+MU=55U575U5555-75U515U=74U=35U-34U=35U=755575U=35U=7555555554
+M5575U=37U%145575U=75U=37U=75U=75U=75U=75U=75U-35U-74U=75U=55
+MU=755=75U=34U-34U=75U=75U=34U-35U-35U-75U575U575U=74U]?55%=4
+M5%75U=155=35U-35U=75U=75U=75U555U=75U=55U=755575U=555=75U-34
+MU-34U-35U=75U=75U=55U5555=55U=75U=75U=74U]545U145=75U=37U]75
+MU=35U=75U=75U=35U=75U=75U=75U=75U=75U-75U-34U-34U-75U=74U-75
+MU574U=75U=75U=75U-75U=75U=?7U5575555U=7555755=74U=34U-74U=75
+MU=75U-75U=35U=74U555U=34U=34U=35U-35U=35U=55U555U555U=75U=75
+M5555U=55U=75U-?455=45555U=75U]?4U-34U=75U=75U=75U=75U=55U=74
+MU=74U=75U=55U=74U]74U-35U=34U=75U=35U=35U=75U=35U-75U=35U-37
+MUM155%555575U514U=75U-35U-34U-74U=35U=555=75U=75U=555=75U515
+MU=75U=75U=34U=75U=75U=75U=75U=75U575557555555=34U5175%555=75
+MU=37U=75U555U555U=75U=75U=75U-75U=34U-55U=755-75U=35U=74U=55
+MU=75U-35U-74U-35U-75U=75U=75U=75U]?55%=45555U=545-75U-34U-34
+MU-35U=75U=55U=75U=75U5755535U=555=74U-34U-34U=75U=75U=75U=75
+MU575U=75U555U=75U=77U]155%55U=74U=74U]34U-755=755575U=35U=75
+MU=74U=35U-755=75U575U=75U=34U-34U-75U=7555555575U5755575U=55
+MU=75U=34U5=75U155=34U-74U]37U-34U-75U=75U=75U=75U57555555514
+M55755%75U=34U-34U=75U=75U=75U=75U5555=75U=75U=555575U-355%94
+M5575U-75U];4U=755=75U=75U=755=55555555555=75U535U=55U=34U-74
+MU=75U=75U=55U5555=555555U=75U=75U-75U=74U]575D!F9'!Q1EK4T-IC
+M)"`_,0T4^)B-L[.UBK>VNJ6]CXFTM)(6$H6RLUP^)C]FB)D/.SLW!`$*"Q&2
+MZ@PX.P6VN[4&/#;DM[:%4N:'AW0`&X"XNX0W.0F/N++D#@]ME_X1%)RTCQX\
+M/QVQNXH#/C!XCXSD$7V1Z!HT#I*\OY`P.C6!O['.#@UFE=,=:H6VB!T\/Q"S
+MNK4&/#9TC(+_%<69E`0V")*_OI$P.C6'O+;9#`-Y[WX:%(>QBAP_/A.PNK4&
+M/3=*@H;`;N:$E@8Q"YV^OI,P.C6'LK?=`@=/X&X&%H&SM1P_/A*PNK4',C1)
+M@85<8I6`D0`S-9V^N9,P.S6%L[37`1O?Y!$#$8.RM!P^/AVPN[4$,S5-AYQQ
+M9Y.-DP(R-)VYN9(Q.PJ:L(I1!Q+S^1\,$X*]MQP^.1RQNXH%,0M$FI1F>)B.
+MG0T]-)VYN9TQ.`N8L8E>!6KA\`4)'8R_MQPY.1RVN(L:-@Y2GN!I<(2+GP\\
+M-YVYN9PV.0B>MXQ`'F7KRP8*'(^^MATY.1^VN8@8-PW5D\P72H"TF0X_-I*Y
+MN9PV/@F<M(-+$U*1Q@,U'HZYMATY.1^WOHD9-0#9EW421H*WFPD^-I*YN9PW
+M/PZ2BH9Q%_^<WPPW&(FYL1(Y.1ZWOX\?"P?"[F(>7(^VA0@Y,9.YN9\W/`^0
+MB85_;^F;UPXV!8BXL!,Y/AZTO(P=#AKWYQ<%5XBPA`LX,9"YN9\T/0V1C)]E
+M9)"$5@LQ!8N[L!,^/AZUO8(2#1S_]AP'T+6SAPHX,9&^N9\U,@*7@)%F=)B!
+M7C4P!(J[L!`^/AZ*LX`0`!?DTP4!W+2RA@H[,):^OI\U,`"4A^]B58>"7C0R
+M!XJZLQ$^/QZ(L(81!V+@308"V+>\@34[,)2_OIP+,0;JFO)I]8.,63<]!HJZ
+MLQ8_/!Z.MH06&W?O?@(,V[:_@34[,)6_OYT)-@3HG5!J^HR)6#8\`8JZLQ<\
+M/!^/MYH4'<&58`\.V+&^@#4Z,.J\O)T.-!KNEG@7XHF+63$_`(JZLQ0\/1R-
+MM9\5%.:7:0@+WK"^@S4Z,.F]O9,,"A_L[&D1Z[6U7C`_`XJZLVH],AV#B)!K
+M8Y20%#4UW;"Y@C4Z,.RRLI`""1#M_Q`2E[2T73`^`HJZLV@R,Q*!CY5H<9V=
+M$30TT+.XC0H[,.*SLY$`#!7BT1D<D+:W5C,Y#8NZLVDS,!.&@N)NW9J9$C<W
+MU;*XC0H[,."PL)8&`&/C<`09DK&VU3,X#8NZL&\P,1&$AO]L^8::'#$V4+*[
+MC`L[,.>QL94'!WW@8P$:G["QTC(X#(B[L&TQ-A>:FMQM[X*$&3`Q1;*[C`@X
+M,>6VM^@%&%'@%0T$F;*PV#(X#(FXL6,V-!6>G71BEX^'&S,P0+VZCP@X,?ZW
+MM.P8$_#@$PX'F[VSPS([#XFXL6$W-6F=E&9BG8B!!3(P3KVZCPDY-OVTBN$<
+M:N/@'@L!A;RRR#T[#XZYMF8T"&*0X6IBF+6#!3TS=KVZCPX^-_&*B?L3993C
+M!34`A;^]]ST[#XR^MV<*#V>7PA!MA+>-!#PR<KVZCP\_-_>(C?867)+B!S<"
+MA+^]\CT[#XV_MV4(`GSK2AYMAK:,!#\]>+VZCPP\-/6/@-IJ_9OM`38-A+Z\
+M_C([#X*\M&4/`4[M8`5M@[".!SX]9;*[CPT]-<B"A%]L[X?O`C$/A[F\Y3(X
+M#X.]M7H-!=?G%`9OC;.)!SX\9K*[CP(R"LB`F7=FD8#I#3,.A[F_Y#(X#X"X
+MKJ&YMX;G:P0,"C<R/#X^.3\\,S$U"0$8:4[AEIJ&@H^)BXJ*M8J+B(Z,@H&'
+MFI^1ZN'SW4)^86]J%!80$!,3$Q,0$187%&II;&-G>GQV2$!?U=_$P\[*]/?W
+M]_?W]/3UR,[-P,?:V=S3T=155U%04U)=7%Q?7U]?7%Q=4E-345%65U=45575
+MU-37UM;6UM;6UM;6UM;6UM;6UM?7U]355U34UM;7UM?7U-35U=75U=75U=75
+MU=75U=75U=74U=34U-34U-34U-74U-34U-34U-34U=34U-35U-34U-34U-35
+MU-34U-34U-34U-34U5565-76UM?7U]?4U-75U=75U=75U=75U=75U=75U=75
+MU=75U=75U=75U=75U-34U=35U=75U=75U=75U-75U-75U=75U=75U-34U=75
+MU=755594U=;6U]?4U-35U=75U=75U=75U=75U=75U=75U=74U=75U=75U=75
+MU=74U-75U=75U575U=55U=75U=75U5555=75U=75U=75U=75U=545E?5UM?7
+MU]34U=75U=755=75U5755=5555555575U=75U=75U=75U=75U=34U-35U-75
+MU=75U=75U=75U=755575U=75U=75U=75U=55U5165]76U]?7U-35U=75U=75
+MU=75U575555555555=75U=75U=755=75U=75U=75U=75U555U=75U=75U=75
+MU=75U575U=75U=755=755=555%%75=;7U]?4U-75U=75U=75U=75U5755=55
+MU=75U=75U=75U575U=75U=74U=35U=55U575U=75U=75U=75U=755=75U=75
+MU=75U=555=545E?5UM?7U]34U=35U=75U=55U555555555555=3ED.GF^<71
+M271^>65E9V1D97A_<G9*3$1>5]?2WMK&P\W.R<C(R\C)SL_"P\;'VMC>W-/6
+MU]555E914%-34U)24E)24U)34U!045%65E=75%555=75U-74U-37UM?4U535
+MU=35U-34U575U=74U=75U5555555555555555%145%155%15U=155=75U-74
+MU=75U=755=75U=74U-55U55555545555U=75U=37U]545E155=75U=75U-?7
+MU-75U=7555545555U=75U=75U=75U=555=75U575U-34U-34U]35U=555555
+M5=75U-34U-74U=7555555=55U=37U5575%755=75U=545=75U=75U=74U=75
+MU=75U575U=75U=75U554U=745=75U=34U-34U-34U-75U=75U555U5555555
+MU575U=75U=75U]?55%=55=75U-34U-36U]755555U5755=75U=75U=755=75
+M55755535U-35U=55U=75U-35U=755555U=75U=75U=7555555555U=75U=37
+MU]155%155=75U=755=34U-?4U-35U5555555U=74U-34U=755=555%75U575
+M5=75U=75U=755555U575U=75U=75U=75U=55U5555555U=36U%54U=555=55
+M5555U-34U=34U=755555U=75U=75U=75U=75U515U=745=74U=75U=755=75
+MU=74U=75U5555555U=75U=35U-555555U=555%?5U=34U-37U%35U-74U-34
+MU-34U-755=75U=34U=75U5555U945=55U=74U]34U-34U=75U=74U-34U=34
+MU5555%555575U-34U]37U]545E155=34U-35U-?4U=55U=75U=74U=555%15
+M5=75U-34U-355=75U515U=74U-?7U]?7U-75U555U=55U=34U-5555555515
+M5=74U=37U%145%15U=75U-15U=35U=55U=34U=34U]?4U-75U5545%55U555
+MU-3455555=55U=75U=34U-34U=75U=75U=75U=75U=7555555=75U]?55515
+M55555=75U=77U]34U=7555155-75U=34U-34U=5555555%35U=35U=34U]34
+MU=55U=74U=75U=75555555755=75U=75U5555%75U-545%55U=55U=555%15
+MU=74U]?7U]34U55555555=75U=75U=555=75U5755=74U=34U=34U=75U555
+MU5555=5555555575U=555575U=75551755155=74U-75U-?5U=555555U=75
+MU-75U-75U=75U=75U555U-745575U=75U=74U=75U=755=755=755=755=75
+MU555U555U555U-?4U5155=75U=75U555U=75U-34U-75U57555555575U=55
+M55555%=45=555=75U=74U-35U=755=755=75U55555155%555575U=755574
+MU]155U555=75U=75U=?4U555U55555555=7555555%555574U-755=75U555
+M5=75U=55U=75U=555=75U=75U=74U-55U555U=75U=75U=37U%545%155=55
+MU=555-75U=75U=74U=75U=34U=35U=555575U554U=755=74U-75U-34U-74
+MU-34U=75U555U=75U=755=75U=75U=75U]?555145555U=75U=74UM34U-75
+MU=555=755555U=75U=75U5555575U=15U=35U-34U-34U-74U-75U=75U=75
+MU=75U=5555555=555=74U]155%55U=5555755%35U-37U]34U-75U=34U=75
+MU=34U-7555555%55U575U=74U-34U-75U=35U=75U=35U=55555555555=75
+MU=75U-?7U%545=75U-34U]35U-34U=7555755=75U=75U=75U=555=75U=55
+MU-345575U-34U-34U-75U-75U-75U=75U=75U=34U=75U=74U=75U]?45514
+M5=75U=75U-55U-74U-35U-55U=35U575U=75U55555555515U=55U=75U-34
+MU-34U=34U=74U=75U5555575U5555=75U=75U=74U-545U55U555U=34U=37
+MU-74U=55U=75U=75U=75U=34U-34U=755=74U-75U=74U-75U=75U-35U=75
+MU=555=75U=7555555575U=75U=34U5175575U=75U=155%75U-34U=75U=34
+MU-75U=75U=75U575U554U=345=75U-?4U-74U=75U-75U-75U=75U555U=75
+MU=34U=35U=75U-355%=55575U=75U=75U]35U-75U=75U5755=555555U=75
+MU=755574U-155=75U-35U=74U=75U=75U=75U=74U=75U=75U-75U=75U=37
+MU]155%555=75U=74U555U=74U-34U-34U-34U-74U=74U=75U=555-75U555
+MU=74U-34U-75U=75U-75U=55U=75U575U=755=75U=75U=34U554U575U=74
+MU-35U-?4U=5555755555U=75U=75U=75U-35U555U-355575U=34U-34U=75
+MU=75U=75U=75U=75U=75U=75U-75U=75U-355%=45575U=74U%55U=34U]35
+MU-34U-34U=755=755=755=555%1555545=74U-34U-?4U-35U-75U=755=55
+M5-555=75U575U=74U=77U]545%55U574U-77U=76U=755=15U%75U535U574
+M5%745%75U==4U=17UU55U-75U-?5U-75UU75UU75U-755=74U=355=555=35
+M5=755=3455175515U575U-34U=77U=34U=75U-75UU75U575U%755=755514
+MU=755555U=34U-35U=35U-35U=75U=74U=75U575U=75U5555=75U-755515
+M5=75U=74U-34U]?5U=75U=55U575U=75U-34U=75U=755574U-35U=75U-34
+MU-75U%75U=55U555U555U575U=75U5755=755=74U-545U555=75U=75U515
+MU-74U=75U-75U=755=75U=75U575U5555575U53555755=74U-34U=34U=35
+MU575U=75U=55U5555=555555U=34U5545%75U=75U-35U-?7U=75U575U=74
+MU575U=35U=75U=755=75U-35=V!^<W5$7]?2Q`4C)S(T&F>5A8V+MKNGOH^!
+M@[>SM)Z5AKRDO/8R)#D-_U8$-0D4XFDP)B,Y;(NUE`8,$N'E!#8Q;K&DI8T%
+M-0'AAI!B%)RSI;+T,#DTYHJ*DF??AK6%`#X["(:\O809`A/D]@<Q-F^WNKR4
+M-3T*99+Z$AGIMKZT$#\[-.NTM9U@1X2)G0(^/@&)N+R%!0P<^?,%-S74L*6R
+M\38\-768Z143E+>]C@0Y.@J?MK>=;GJ?@ND//SP?M+N\G0`+&O7S&S4/XK.Z
+ML60S/PK#AY-F%):TLX$".#L/AK.VDA=NEH?]"3PS;[&ZO>@.-`3`^1P.`)"R
+MN[<3/#X(XX.;=6Z6M;&9"#LY`8^]L9$2$>.95`@]-M*SI;/.-38&S>$1`@68
+MO;B(!SX^#Y&.AM5BEXNTE34[/QZUO['J&QG(D'<),S7IO:6Q9#8Q`?/H;@82
+MA+V^@@PX/@.:M8/#8NN.B_$W.SUOMKFQY`8'295Y#C$/G[^EM!`P,`'AD7\:
+M%8:RO84*.S\$@+>,]6_@@HUT-C@PT;.XMM0-`V;M>@\W`8>^NX@$/3,&E9[=
+M'6&!LK.6-SH\'8ZQCLP4\8:&;#$^-^B]N[1G"`QHX7X"-1N"N;B"#3\R!)*$
+MY1=R@+"V\C$Z/6RULXG1$UB:FQ8Q/PJ9OKN*%C0(%.%U`0D6CKF^A`LY,AF%
+M@.IN2(&QM7XS.C#5MKV)2AEDDY,2-CT-@;B[CAHV-1;M500-88NYO)`W.#,1
+M@8^08$R'MXX7/3LV[K._CF$$%>N5'S8S!X^[N(,`,#06ZO`>!D2UOK/^,#LP
+M8(^+GV1TA;6`&3TX-9Z\OXP4`!+DXAXW-AV*NKF%#C(T%9'B%AKUM;^V>#(Z
+M,=6*M)MG>)Z.A0<\.0Z!OKZ#'P\:R><?-31BM[J_D30\-VR>EVT=Y+6]M1$\
+M.C?BM[::8V*1@Y,`/#\!C[B^AP<+!]7E$@L(4+:ZLOXQ/C1^A)]P%N*ULH\%
+M/CLUG;"QFFL4[X?N`CTR'K6ZOYX"-`!%YQ8/#>>PN[!^,CDTTH"$T&OLB[&!
+M`SDX#H>RL)L1'?*9^`TR,&ZVI;V4"#8"1>UI`P:4L[BW$3PY"N&/@/!OXHZW
+MF0\X.0&,O[.<'AI%D,<-,S=0LZ6R\S0P#52590<8G;.YBP0^.0F1BHWD;>>-
+MBI0+.#\9M;FSE@0&9^I3#3$*XKVEL7XQ,PW(DUP8$YBSOX(".#D-A;>)X6GD
+MI:*DLHF23QP`"34P,C\^.3\_,C`W"@T$$&3(Z)R$@(V.B(JUM;6*BXF/C8.&
+MA)F2E^_ESE)V9&)H%1<1$!,3$Q,3$!$6%Q5K;F)A9'ES=$)84=;>Q,+.RO3T
+M]_?W]_3URLG/PL'$V][=T];45U)24U%04E)=75Q<7%Q<75U34U!15E=75%55
+MU=74U]?6UM;6T='1UM;6UM;6UM;6U]?7U-34U=75U=74U-75U=55U555U=75
+MU=74U=34U-155U34T=;6UM?7U-?4U-74U=74U-34U=35U=75U=74U=34U-34
+MU-35U=75U-74U-34U-34U=74U-34U-34U-34U-75U-75U=34U-35U-74U556
+M5]76UM?6U]?4U-75U=755=75U=75U=755575U=75U=35U-74U=75U=75U-34
+MU-35U=75U=75U=75U=75U=5555555575U=75U=75U=755597U=;6U]?4U-35
+MU=75U575U=75U=755555U=75U=75U=75U=75U=75U=75U=75U=75U575U=75
+MU=75U=75U=555575U575U=75U=75U55445?5UM;6UM?7U-35U-75U=75U=75
+MU=5555555555U=75U57555555575U=75U=75U=55U=75U=75U=75U=75U=75
+MU=75U=75U=75U=75U5115U76UM?7U-35U575U555U=55U555555555555=75
+MU=75U=75U=75U=75U=35U=75U575U=55U=75U=75U=555575U=75U=75U=75
+MU=755%97U='6U]?4U]34U=75U=75U=555555555555555=75U=75U<B7E^'F
+MS]M!=7UY961D9&5E>'ES<4I)1D535M'=V\3#S<G)R,O+R,C.S\W#QL?$VM_<
+MTM#1U]155U%14U)27%U<7%U=4E)34U!045%65U145=75U-?7U-55U=74U-34
+MU-34U%75U-34U-75U=75U57555755555555555145=555U555=75U=75U=75
+MU=75U=74U=75U=755=755=55U5555=75U-355%145575U575U=75U=34U=55
+M55555=55U=75U5755575U=755535U=155=75U=34U=75U-75U=75U=555=55
+MU=75U=75U=75U=75U=74U-545U14U=75U=75U5545=75U=75U=75U=75U-35
+MU=75U=75U=555%555515U=74U=34U-35U=75U=75U-74U=755=7555755=55
+M5575U=34U5145575U=75U-35U=74U]7555555=7555755=755=755=75U554
+MU=355%75U=34U]?7U-3555145%145%75U=74U-34U-35U=75U=55U-355%14
+M5=75U=75U=755%75U=34U=74U=75U=75U=755555U5755575U=55U=75U=34
+MU-34U-74U-35U=75U555555555555=555=75U=74U-555%15U=75U-34U-34
+MU]?4U=755575U=55U575U=555555U5545-74U555U=74U=75U-75U=75U555
+M55555=75U=75U-75U=75U=75U=34U5145%755=75U=74U515U=74U-34U-74
+MU-74U=34U=34U=75U554U=755-75U=35U-34U=75U=75U=35U=75U=755555
+MU=75U=75U=75U-3555145=75U=75U=75U=?7U=75U555U555U555U5555=55
+MU=755575U-15U=74U-34U=35U=74U=75U=75U=75U=75U-74U-35U=75U=75
+MU-555%55U=75U-75U=155=34U-34U-34U-34U55555555555U=555-75U515
+MU=74U=34U-34U-75U=75U=55U5555555U=75U=75U=75U=34U5175%75U=35
+MU-34U=74U]75U=75U=75U575U=75U=74U=75U-55U=345%75U=35U-75U-75
+MU=75U=55U55555555575U=755=75U=75U-?455155=55U=75U=355535U=74
+MU-34U-74U=75U=75555555555%155=545555U=75U=75U-75U=75U=75U=35
+M5=75U=75U=75U=75U=74U-155%55U=75U=74U]35U-?4U555555555555575
+M5=555555U5555=75U555U=75U=75U=74U=75U575U=755=74U=75U=75U=75
+MU=55U=34U5175555U=75U=75U%55U=75U=74U-35U=75U=75555555555514
+M55555U55U=75U-74U=75U=75U=75U=755555U=555%5555555555U-355%=4
+M5=75U=75U=75U=37U=755=7555555=75U=75U-35U=755575U=545=75U-74
+MU=35U=75555555555555U=75U=75U=75U=75U=74U-555%555575U=75U=55
+M5=75U=35U=75U=35U=34U575557555555%75U514U=74U-35U-75U=34U-34
+MU-35U=755=55U=75U=555555U=37U5545555U=75U=75U=74U-35U=755=75
+MU=75U5555=75U=75U=55U=345=75U=75U=75U=75U-75U=75U=75U=75U=75
+MU=74U-34U=35U-?555155=75U-34U-34U575U-37U-34U-35U=755=75U575
+MU=755574U-145=75U-34U-35U=74U=75U=75U-755575U575U=755=75U=74
+MU]145%55U=75U575U575U=?4U575U555U=75U=75U=55U=75U=555-74U%55
+MU=74U-34U-74U-34U-34U=75U=74U=35U-75U=755=75U=?7U%545575U-74
+MU-35U-54U=74U=34U-34U-34U-35U-75U=75U=55U-355=74U-?4U-34U=35
+MU-74U-75U-75555555555575U=555=75U-355%155-75U=34U=35U=77U%75
+M5555U=75U=75U=75U=75U=35U574U-15U=74U-34U-34U=75U=75U=555=55
+MU575U=75U575U555U=74U-545%155=74U-34U-345=74U-?4U-35U=75U555
+MU555U=55U=545=75U515U=75U=34U-34U-75U-75U=75U=75U=75U5555555
+M5575U=34U5545574U-74U-34U-74U]?5U-555=7555755575U5555575U555
+MU-345=75U-34U-35U=75U=75U=75U=75U575U575U=75U=75U=75U-355515
+M5555U=75U=74U555U=74U-75U-34U-75U=75U=74U=755574U=555=74U-34
+MU-35U=75U=75U=75U=555=75U=755=75U=55U=74U]555555U-34U-75U-35
+MU-?4U=75557555755=75U=75U=55U=555=75U575U=74U-34U-35U-75U=75
+MU=75U=75U=75U=75U=55U=55U=34U5545%55U=75U=75U5545575U-34U-34
+MU=75U=75U=75U=35U=55U-755=75U=34U-34U=35U=75U=75U=75U=555555
+M5=75U=75U=55U-3555155=75U=555=755=74U-755=75U575U=75U=55U575
+MU=75U574U=555=74U-34U-34U-74U=7555555=555=555=55U555U555U=74
+MU]155%75U575U-75U=755575U=34U-34U-34U=75U=75U=75U=755=75U515
+M5555U=35U-34U-34U-35U=75U=74U5755555U=75U=75U=?4U5545=75U-34
+MU-34U-74U]?5U=75U=75U-75U=75U=75U=74U555U-345=75U=34U-35U=75
+MU=75U=755575U=75U=75U=75U575U=74U-3455155555U=555=75U554U=74
+MU-34U-34U-75U=34U-35U=75U575U-55U=34U]34U-34U-75U-74U=75U=75
+MU5555=55U=75U=75U575U-545U15U=75U=34U-34U-37U=75U=75U=75U=35
+MU=34U=35U=35U=34U%55U=74U-35U-75U=55U=75U=75U=74U=755=755=55
+M5=75U=74U5175555U=75U-34U-155=75U-34U-34U-34U-74U=5555755555
+MU=7555755=75U=75U=34U=35U=74U=75U=555555U=55U=75U=75U-355%=5
+M5=75U=75U=74U=37U]35U=75U=75U=75U=74U=35U=75U574U-15U=75U-74
+MU-34U-75U=755=755=75U=75U=75U=55U=55U=75U-545%555=75U=74U=74
+M5574U=74U-34U-34U-35U-75U=7555555-755515U=37U]?4U-34U=75U=75
+MU=75U575U555555555555575U=37U5145575U=34U-34U-75U-?5U=75U=75
+M5555U575U=75U57555145=755575U=35U-34U-35U-34U=34U=75U-34U-75
+MU-35U=75U=74U-355%=45%75U=75U574U%55U=74U-34U-35U-74U=35U=55
+M55555535U=545=75U-34U-34U-75U=75U=75U=75U=75U=75U575U575U574
+MU-545%55U=75U=35U-75U=74U555555555555=75U=75U=75U=555=75U%75
+MU-74U-35U-75U-34U-75U555555555555555U=75U=75U=37U5145%55U=75
+MU-35U-355=75U-34U-75U=75U=75U55555555514U=555%75U=34U-34U=75
+MU=555=55U575U=75U=755=75U=75U=55U-3555155555U=75U=34U-37U]35
+MU=555=55U=75U=55U=75U%75U575U=555=75U=75U=55U575U=74U-754WEA
+M<G-,1%#6W]8W(B0R"1/1F(:+B[:POK.*B[6YHZ:W]7^2FQ<S/@J8L8,!/SXT
+M#38Z)36$L8T&/#(!=A8)"?*RI;-E-PI\A)UN%(>YI+8=,S=AAIAE%)BROH`(
+M.#(4AH1W%I&VL)0Q)3)RCH_E:96UM-\S)3'HMK>6;.:/CV(R.S6$O;"0$7:%
+MA!(R.0V.N;.5!1/HD1DS/1NTN[/R#0%>XAXV,6&PI;%E-0EMYQ$U"_B]I;0<
+M,#07[&0"`)"\NXD#/#$4D/T;&9B]OH8*.3!CA))N%(6RLI0Q.C'<CX#68YBV
+MME\R.C:5M(CG8Y&*B1<\.@J$L[?O%/F#AAL\.0*.O['A&6>>D@<]/!^WN+';
+M`![A[`<S,'ZSNK9C"P-`Y1HW->.\NK4>-@AAX1$(`I^_NX\",C1BE'D!&X>_
+MOH0U/C=XF.8=%(&\O>@Q.#?-@YYA88:SMG8].C27BH/6986WBQ,_.PF'L8CS
+M;):)@0<^.0&.O+3V$_6&G0,_/!*WN;=:!V^0[`(],$ZSN[1N#QK^_``P->N_
+MNHL8-`)!_04T#9JYNX(",PA][18/&H"YN9LU/#5/DW4$%(V^O>,P.37QA^X6
+M9(V\L6<].`N6CIIZ<8"PBAX^.`^'MX)<>X6T@`$Y.0>.LHE4:I2,G`PY/!:W
+MOXIQ&=":X@X_,%2RN(MJ`Q25RP\R-):_NXX;"@7VV@,V#(>XN(`",0)1_`4+
+M!8V[OI\U,@[7ZA0#%(FXO?LP/PCYF%89?XB^L6T\.0Z6@Y1J7XZRBAH^.0*$
+MM85Y6X.V@`(X/@6/L8-W99J+G`@X/&JWO8]Z$>B!X0HY,,ZROH\5!$^=W0H\
+M-)._N((;#Q'M0@DP#(&XN8<"-`3W5P,T!8Z[OI,U,`/(^QL/%;6[O?4Q/0WD
+MD6D'=;2YL6H]/P*6A,\3PK6\B@0Y/@&$CI%L](FP@`P[/AF,MX5E4(.TG34[
+M/&VTLX%F8)F,Y#0X,/RSO(,4'>"%03<^-)^_OH$8`7F7934R#(.XOIH#"!/G
+M>`DV!8NZOY0*-@3\5@`+:+2ZLM8Q,P'FX1X`7[:[L18]/0&7G6$9^+:^B@8^
+M/P2:@/\4X+2R@`D[/!*-BY!C^XFVDC0Z/62TMIIC4H"(_#8[,.:PLH05;IV&
+M?S$Y-)B_O(4>&/^=;S8\#(*XOYP``F'N:#0P&K6ZO.P(-1/D9@DU;[:ELTPV
+M-AOFU`$-VK"ZMA`R,P64[A(%[;"YBP$^/1F8FWT0E+&\@0L[/1:#C>=NE;2P
+MD#<Z,G"*M9%BYXZUR3`Z,>VQL9]J0X:"83,X-9J\LI\=%Y:8%C`_#8RXO98&
+M!,:4$S8S&+2ZLN0.#V+A%S4W8[&EL'PW-!#F9@\.\+.ZMQTS,1SKQP<&E+*X
+MB`,_,Q*<EQ<=DK._A@HX,VN!AT)JG;&SEC8Z,$2(CN9BEK2TT#,Z-NBVM)1N
+MY(R,:#([-86]L9<0<X6%'#(^`H^YL^@%$^N1&#,R'K2[L_0-`5#B&38V9+"E
+ML68U"6+G$34+Y+VEM!\P-!3L9P(`D[R[C@,\-A20\!L>F[V^AS4Y,6.$DVD5
+MA;*RE3$[,=V/@5%CF+&W1S(Z-Y6TB>5BD;6)%CP["X2SM.(4^8.'&SPY`(Z_
+MMN<99YZ3!ST]'+>DKJ&XMH'@:00,"C<R/#X^.3\\,S$T"``::G?DE)B'@HR)
+MBXJUM8J*B(Z,@H"'FI^0E>/\V$9\9FQJ%!80$Q,3$Q,0$!$7%!5I;&-F97]Q
+M24=<5=+;P<S(]?3W]_;W]_3UR\[-P\;%V-_=T-16459745!375U<7%Q<7%Q<
+M75U24U!15E=45575U=34U]?6UM;1T='1T='6UM;6UM?7U]?7U]34U-75U=75
+MU=75U=75U=75U=75U=75U-345594U=;6U];7U]?7U]?4U-35U=75U=75U-35
+MU=75U=75U=75U-34U-34U-35U=74U-34U-34U-34U=75U=34U=34U=34U=75
+MU=75U=74U=555E?5UM;7U]34U-?4U-35U=75U=75U=75U=75U=75U=75U=74
+MU-34U-75U=75U=75U=35U=75U=75U=75U=75U=75U=55U575U575U=75U511
+M5E77U]37U-34U-75U=75U55555555=75U57555555=75U=74U=75U=75U=75
+MU=75U=75U=75U575U=75U=75U=75U5555=75U=75U=755%%75=;6U]?4U]34
+MU-34U=55U5555=75U575555555555555U=755555555555755=75U=75U=55
+MU=75U=75U=75U=75U=55U=75U=75U=544595U]?4U]37U-35U=7555555555
+MU57555755555U555U=75U=75U=75U=75U=75U=75U=75555555555=75U=55
+MU5555=555=75U=75U5115U76U]?7U-?4U-35U=75U5555575555555555555
+M5555U=75U^20Z>'YQ==)='YY9&5G965Z>7QR=DM"1UE1U=#>VL;#S,[)R,C(
+MR,G.S,+#P<?8V-[<W=#6U-545E%04U)24EU=75U24E)34%!145965U155=74
+MU-155-74U-?7U]?7U-34U-545=75U=75U5555=555555U5545%175U5555=4
+M5575U=34U-75U=75U5555=555=5555555%555555U575U=37U5545%75U=34
+MU=34U=755=74U%55555555555555U555U575U555U-355=75U=34U-75U=74
+MU=75U=75U=55U=75U=75U=555=555=75U-?55%=45575U=74U-34U-355%75
+MU=75U=75U=34U=75U=555=555575U=545=75U-34U=75U-75U=35U=75U=75
+MU=5555555575U=75U-34U]155U145=55U=75U=75U-74U]35U=755=755555
+MU555U555U=555=75U575U=74U-34U-34U=35U=555=555=75U-75U=75U=75
+MU574U-?7U%145555U=75U=755=75U555U=74U=34U-35U=35U=75U=755514
+MU5555U15U=34U-34U-34U-75U575U575U=75U=75U=555=75U=75U]?55%14
+M5575U=34U-74U-34U-?7U-75U575555555555=75U=755575U5545=75U-74
+MU=75U=75U=75U=755=75U=75U=75U=75U=75U=74U-545U145=75U=75U=74
+MU-55U=34U-34U-34U-34U-74U-35U=555=75U5145=75U=34U=74U-34U-34
+MU-35U-75U=75U=5555555555U=34U5545=75U-75U-75U-35U=77U-755555
+M5=755=55U=75U=75U=75U-355=75U-34U-74U=75U=74U=35U=75U=75U=75
+MU=74U-34U-74U-?555155=75U=35U-75U=755575U=34U-34U-34U-75U=75
+MU=555575U5545=74U-34U-35U-74U=55U=5555555=555=75U=75U=75U=74
+MU%555U155575U=75U=34U=75U-35U5555=75U575U=75U575U5555=35U535
+MU=34U=35U=74U=35U=755=5555555555U=75U=5555555=3455175%75U=75
+MU=75U=75U555U=75U-34U-34U=75U=555575U575U=755%75U=35U-75U-35
+MU-75U=5555555575555555555=755=75U]?55%155=75U=75U=75U=75U=34
+MU=7555555=755575U=75U=755=74U=545=75U-34U=75U=755=75U=755=55
+MU555U55555555575U=74U-545U155575U=75U-75U-555=75U-75U=75U=75
+M5=75555555145-7555145=75U=75U=74U=35U=35U5755555555555145555
+MU555U=35U5175%755=75U=75U=34U=77U]7555555%5555555=5555555555
+MU=755%75U=75U=75U=75U=75U=55555555555=75U=75U=75U=75U-355%=4
+M5%555=75U=35U=755575U-34U-34U-75555555555=755575U=545575U-34
+MU-35U-34U=74U=75U=75U=55U5555575U575U=37U]555U145=75U=75U=75
+MU=75U-?5U=75U=55U5555=75U=75U=555=75U535U-34U-34U-35U=755=75
+M5=755=755=55U=75U=75U=55U=34U5545%55U=75U=74U=34U-55U=34U-34
+MU-35U=75U555U=55U555U-555%555=75U=75U=74U=75U=75U=75U=755=75
+M5=75U=755=75U];455145=75U=34U-35U=75U=37U-75U-75U=74U-34U=34
+MU=75U=74U-55U=75U-75U=35U=75U=75U=75U=55U555U=755=75U=75U=77
+MU-555U75U=75U-34U=75U=755-75U=34U=34U-35U-35U=75U=55U=35U555
+MU=37U-34U-74U=75U=75U=75U=75U=55U555U5755=75U=37U-545575U=74
+MU-34U-34U=74U]155%55555555755=75U575U555U-755=75U=35U-74U=35
+MU=75U=74U=75U=55U=55U=75U=74U=34U]?455155=75U=74U=35U=755515
+MU=74U-34U-75U-75U=75U=755574U=545=74U-34U-34U-34U-35U=75U=75
+MU575U=75U=75U=75U-77U%545U15U=74U-34U-34U=35U-?4U=75U=75U=75
+MU=75U575U=55U=34U515U=74U=75U=74U=35U=75U=35U=75U=75U=34U-75
+MU=75U=37U5175%55U=75U=74U=74U-54U=75U-35U-34U=35U=35U=75U=55
+MU-755%75U=34U-34U-34U-34U=75U=7555555=75U57555555575U-3555=5
+M5=55U=75U=75U-34U-37U-75U=75U=75U=34U=75U=755=74U=155=34U-35
+MU=75U=75U=75U=75U=75U=75U=55U=75U=755=74U-545U55U=74U=34U=75
+MU=355=75U=34U-35U=755575U=75U5555=75U5145=75U=35U-34U=34U-74
+MU=35U-74U=75U=75U=555=55U=34U5145575U=34U-34U-74U-75U]35U=75
+MU=75U=75U=75U=75U=55U-355=74U-74U-75U=75U-75U=75U=75U=74U=75
+MU=74U=75U=34U-?455155=74U=75U=75U=75U535U-34U-74U-75U=75U=75
+MU=755575U5545%55U=74U-34U-34U-75U=75U=75U-75U575555555555=74
+MU]155%55U=75U=74U=75U-75U=?4U=75U=75U=55U=75U575U=75U575U=75
+MU-74U=75U=75U=75U=75U=75U=75U=75U=35U=75U=75U=?7U%545575U=74
+MU-34U-35U=545=75U-34U-35U-55U=75U=75U=75U=75U=755575U=75U=75
+MU=55U=75U=75U=55U=75U=555=755555U=55U=74U=34U=35U-75U=55U=74
+MU-75U=755575U=75U=75U=75U=75U=75U=75U=74U=75U=75U=75U=75U=75
+MU=75U-75U-34U-75U=74U575U=75U=7555555=55U=755%355=75U=74U-74
+MU=74U=74U=75U=75U=755=755=55U5555=55U=75U=75U57555555=75U=74
+MU-34U-35U=75U555U=75U5555575U575U]?4U=755=755=75U=75U=75U=75
+MU=75U=34U-35U=75U=75U=55U=75U575U=75U=74U=75U=75U=74U-75U=75
+MU=34U=35U-34U=75U554U=74U-74U=35U=75U=75U=75U=75U=755555U555
+MU=75U=75U=75U=75U575U=75U=75U555U555U574U575U=75U=75U=35U=35
+MU=75U=?7U=75U575U=555=55U=75U=75U=75U=75U-34U-75U=74U=75U=75
+MU=75U=75U=75U=74U-35U-35U=355=75U=74U-75U=75U=75U=155=75U-34
+MU-34U=75U=75U=75U=75U=55U5545575U=75U=75U575U=75U=74U=75U575
+M55755=75U=75U-75U=74U-34U=35U=35U=34U=74U]75U555U=55U=75U=75
+MU=75U=75U=75U=74U=5555555575U=75U=35U=75U=75U=34U=75U=75U=74
+MU575U=75U-75U=75U-75U=755575U=34U-74U-74U-75U-75U=75U=755=75
+MU=55U=75U=75U=75U555U=755575U=75U=75U=555=55U=3555555555U=75
+MU-74U-74U-75U-3555555575U=75U=74U=75U=75U=75U=34U-35U=75U=75
+MU=75U=75U=75U=75U=75U=75U=75U=74U]75U-34U=74U-55U=74U=74U-55
+MU-74U-34U-34U-75U=555=755575U55555555575U=75U=75U=75U=755=75
+MU=35U=5555555555U=74U575U=74U=74U-74U-34U-75U=37U-5555555=75
+MU=75U=75U575U=75U=74U=35U-75U=75U=55U=74U=75U=75U=75U=75U=75
+MU-74U-35U=75U57555555%75U555U=555=75U=75U-35U-35U-74U=755=75
+M557555555555U=35U=75U=75U=75U=75U-34U-75U=7555755575U5555555
+M5=75U=75U=75U-75U=74U]355575U=75U5555=55U=75U=74U=75U=74U-75
+M5555U=55U=55U=75U555U=75U=75U=55=&!Y<W1$7M?3Q!HC)C(T!6;KA8VT
+MO;*UL+ZEMY^2B;NGM.F5B(<,)"0*EI$'-03C93TC(37AY04&E+2##SH\;X*%
+M2OZVNK`1,S7AC99H[;.DL!TP",V3%P)/L*6T!#`.048/-VRPNHL$-`3V:#<P
+M;[.[B!X-9903,S)ML[F/$1V7F!L\/&RPO($538"&!CD_:;>QG&R4M8(#.S]N
+MBHCE9X>PC@([/&V-A&1]B;R+`C@S9H3F'W6TN8H#/S9_D6H#3K&ZM0$R"%CR
+M!@MUL[J*!S8&]GH*-W"RNH@%"13L$3$P>;*XC!\$_)`:/3UEL[^!$&^;F@8^
+M/&>QLYAJYXZ!`C@_9[2TZ&&;MH(,.SQDB8-;<HRRC@PX,GB`D1=!M;Z(##DQ
+M<YYQ!%&QNXL"/#5'[1L,U+.ZB``P`L-`#S54O;J)!C4?YQ4W-ER]NXP%`TN5
+M&#(S1K*^@!\2D9P&/SU+L[V%%E6#A0(X/'>VMI9LD[6!#SL]=K6.]7Z`L((.
+M.S)WC)IB1(J\CPXX,4B$\![?MKF.#S\T6)<6`,FSNXX-,@_8SP`(]+VZCP`V
+M!/]B-33*O+N-!PYO[!PP,<*\N(`:!>&6!SPSV[*\A!)FA9X".3+1L+"3%>Z)
+MA`X[/52WM>=GA+:!"#LR4(B!<$.)LH(+.S!3@)41Q[>^C0@Y-U>?9P3RL+B,
+M#CP(V^8%#/J]NXP,,`'Q?`D*Y+R[@@,U$.80-C?EO[B`!P#5ZP0R,?F\OH09
+M%I*0`CXS\K*RG!'*@ID..#+WL;?N;9ZUA`L[,\BTC-1TC;"&-3LPP(R?:<2T
+MO(`U.#;$A-P9^;"Y@PL_-<V4$@#@O;N""3(-]]4"#NR\NX,,-QCZ%30U[[^[
+M@0`/9^(:,S;MO[F$!!OOE`,\,..\O9\<?823#CDSYK.QE164B9D*.S/EMHOQ
+M>8&VA34Z,/Z+AV73B[*'-#LV\(#M$/BVOX$U.37VG&\$[;*X@0H\#_#X!P+J
+MO+N!"3$'_F$+")2^NX8-"A3G'S$TE+ZYA0$!R.\!/3:5O[^>&A6<E`\^,>N]
+MLY80Y8*3"CLP[K"TY&.%M)XT.C#BM()-7HZPFC<[-N:/DVO^M[R$-S@T^H5`
+M&>^SOH<T/PG^ZQX!E+RXAPHS`/Y,#`^1OKB$#C<<Y1$W"I.YN(4"#''@!S,T
+MD+Z_G@<?E>X,/S:1O+*1'T2'E`HX,9>RMN-JDXB0-#LQE;:)W':#MIPW.C;I
+MBX5A]K6RF#8[-..`^A#LL+^:-SX+YY(4!9:]N84T/0+E]`$#DKZXF@HQ&N5I
+M"@Z<N;B8#PMOY!HQ-9^YOIP`!_[@`CTWG+Z]D05LGNX+.3:=O+#O$>V-E30[
+M-I"PM?1GA[21-CHVEK2`<]J(L)(Q.S3JC)<5XK:]G#8X"NR%?QZ6LKZ>-S\,
+MX>P;!IR_N9XT,P?D?P\-F;FYGPLT$>4<-PB;N+Z2#`);Y``R-9NYO)8&$I?A
+M"3\TF+^S[A_;ANPT.#>>LK?_:9^(ZS8[-YVVC$5$C+:4,3LTD8N98N2TLY$Q
+M.#65@?40E[.\DS$^#N^0$1J<O+Z2-ST!X-P#`9JYN9,U-A_D%S4,A+B^D0D)
+M9OH',`B$N+^4`@3F^@\\-82YLNX%9YCG-3DTA;RV^Q&5C>`V.S28LXO1>8&U
+M[#`[-)^TAGKSBK'H,#LUD(SI%96QO94P.0F4FV$?G+V_ES$]B*^FOK2$_A4$
+M#`LQ,S\_/C\\,C`W"@P''6#3[).%@8*/B8N*BHJ+B(F/C8.&A)B=E^[D]%1U
+M>F-N%1<6$!`3$Q,3$!$6%Q5K;FUA9'AR=$Q;4]?<Q<#/R_7T]_?W]_3URLC/
+MPL''VMG<T]'455=64%-275U<7%]?7U]<75U24U!15E=45%555=34U-?6UM;1
+MT='1UM;6UM;6UM;7U]?7U-35U-34U-74U-74U=75U=75U=74U=34U-34U-74
+MU-34U-34U-34U-34U-35U=34U-34U-35U=75U=75U-34U-34U-35U=75U=74
+MU-34U-74U=74U=74U-34U-34U=75U=75U=74U-34U-74U=75U=75U=34U-75
+MU=75U=75U=75U-75U=75U=75U=75U-35U-75U=75U=75U=74U=75U=75U=55
+MU=75U-75U=75U=75U=75U=75U=75U555U555U=75U=75U=5555755575U=75
+MU=75U=75U=75U=75U-74U=35U=75U=74U-74U=75U575U5755=75U=75U555
+M5555U=75U=75U=75U575U=75U=34U=34U=75U=75U=74U-75U=75U=75U=75
+MU=75U=75U5555=75U=75U=755=5555755=75U=75U=755=75U=75U=35U=75
+MU=75U=75U=75U=75U=55555555555=75U57555555=75U=75U=75U=75U=75
+MU=75U=75U=75555555755=75U=55U5555=75U=75U=75U=75U=75U=75U=75
+MU=75U5555555U=75U=75U=755=75U=75U=75U=75U=75U=75U=74U=75U=75
+MU=75U=75U-34U-35U=75U=75U=34U=75U=75U=7:ZY'@X?7;64AP>7MD9&=E
+M9'AX?7-T2T%%4U?1TMO$P,+/SLC(RLO(R<[,PL''Q=C>W]+0UM?555=14%!2
+M4E)=4E%27UU24U!04%%65E=75%555=35U=?6U]?4U-35U-35U=34U-75U=75
+MU555555555555%155%5555555555555555755=755=75U=75U=74UM175575
+MU=75U=75U575U575U=555%75U=75U=55U=75U=75U575U555555555555575
+MU=75U=75U=75U=75U=75U=75U=555575U5555=76U5155=75U=35U=75U=35
+MU=75U=75U-75U55555555%755=75U=755=75U=75U=74U=75U=755=755=75
+MU=74U=75U=75U=75U=55U=75U=?55U14U575U=75U=75U-75U=75U5155575
+MU=75U=75U=355=75U=75U575U=355=75U=75U=75U=75U=55U=75U=755=75
+MU575U575U=74UM57U5155=75U=74U-34U-75U-75U=?4U57555555=755=75
+M5=755=75U=55U=35U=74U-75U=75U=75U=75U=75U=75U5555=555=55U=36
+MU5=55%55U575U=74U-75U]34U=545=74U-34U-35U=34U=34U=5555555=75
+MU=555=755=555=75U=75U=75U=75U=75U=75U575U=75U-;55U145575U=75
+M5=35U-75U-35U=77U-75U=55U=75U=55U=75U=75U=75U=75U=34U-35U=75
+MU555U575U=75U=555=755=75U575U=75U-75U=34U=75U=755=75U=55U=75
+M5%75U=35U=75U=35U=75U=75U=555555U5555=75U=75U=755575U575U=75
+MU575U555U=75U=75U=36U5=55=75U=35U-35U=75U=74U-75U]?5U=55U=75
+MU=75U=74U-34U-34U=74U=75U55555555=75U=75U-75U=75U=74U=75U-35
+MU-34U-;45-55U=75U=35U=555555555555=4U=75U-74U-75U=55U575U=75
+MU=75U=55U5555555U575U=74U=75U=55U=75U=35U=35U=35U=74UM145535
+MU=75U-75U-74U=755555U=345555551555555575U=75U=74U=75U=35U-75
+MU=555575U=555555557555555555U=75U=355=77U5=55=75U=34U=75U=75
+MU=75U=545=75U-34U-35U=5555555555U5555555555555755=55U=755=75
+MU-35U=75U=75U=74U=75U=55U=?55U55U=75U=74U=75U=74U=35U=74U-75
+M55555=555=55U5555575U575U=74U=34U=75U=755=75U=75U=75U5555555
+M55555=75U=74UM5755755=75U=74U=75U=55U=755%75U=75U-75U=75U=75
+MU575U575U=755=555555U=75U=75U=75U=75U=34U=75U=755555U=555=77
+MU5=55=75U=75U=75U=75U=74U=75U-35U555555555555=75U555U5555555
+M5=75U=75U=75U=55U=75U=75U=75U=75U=75U=75U=55U=?55%545555U575
+MU=75U-74U=75U555U=74U-34U-35U=74U=75U=74U=75U=55U=55U=35U-75
+MU=755=75U575U=75U=75U=555=75U-75UM575535U=74U=75U=75U=35U-75
+MU=77U=75U=75U=75U=75U=74U=35U=75U=75U=75U-75U-75U=75U-34U-34
+MU-34U=75U=75U-34U=36U%355=75U=75U=75U=34U=75U=155=75U=34U-34
+MU-34U=75U=75U=555=75U=55U=75U=75U-34U-34U-34U-75U=55U=55U575
+MU=75U=?55U15U=74U=74U=75U-35U=75U=74U-35U=755=55U=55U=75U-75
+MU=75U-75U-74U=75U=75U=75U-75U=75U=75U=74U-75U-75U-74UM175575
+M5=75U=35U-34U-75U=34U575U-34U-34U-35U=55U=75U=75U-75U-74U-75
+MU=75U575U=75U=75U-75U=755575U=74U=755=7755=55-75U=75U=35U=75
+MU=75U=75U-15U=555575U555U=75U=75U575U=74U-34U-35U=75U=74U=74
+MU=34U=75U=75U=75U-35U=75U-;55U175=74U=74U-35U=74U-75U-545=75
+MU-35U-34U-75U=75U=75U575U=7555555555U=75U-75U-355=75U5555=75
+M5575U=75U=74UM5655155=75U-35U=35U=75U=75U=37U]35U=75U575U=74
+MU-75U-75U=75U=75U=75U=75U=75U=75U=75U-35U555U-75U=75U-75U=76
+MU5=55-75U=75U=75U=35U-75U=355=75U-?5U=74U=34U=74U=75U=34U=75
+MU-75U=35U=75U=55U=755=75U-35U=74U-75U-35U=75U=35U=75U=75U=75
+M5575U=755=75U575U-35U=75U=35U=75U=555=755555U=75U=74U=75U=75
+MU=75U-35U=34U-75U-34U=75U-75U=74U575U=75U=75U-15U-34U-75U-35
+M5535U-75U=34U555U-355575U5555=7555555=555=75U=755=75U=75U575
+MU-75U5555555U575U=75U5555=75U575U=34U=75U-75U=75U-34U-75U575
+MU=555=75U=34U=35U=755575U-34U=75U5555=75U=75U=75U=75U=75U=75
+MU=75U=34U=75U=755=75U=55U=75U555U=54555"9F5P=D=;U-#%:B<A/#$#
+M:^::@HNUMK:_IJ"]@Y&!B+6=8&F$L[R$-28F/6V:X@$V-P,;#CPE/7JVO(("
+M/C\`F8^'_OV!MK?C"S(.@;JDL74)#&6<EFT>Q8N\L4@R)3#ZL;*"%``=\_P8
+M-#1BM+NRVC,[-MZ)B9%B39N-E`T\,A6QI;WB-3,/VY[F%!2=M["9"SL^'K2Y
+ML.`##13F4P<(&H:_OH$(.S\:C;:/QQ98F9X7-S(-@+B[C0(\,!V9A.1H78&W
+MC1\\.PJ'OKF"!34->>U^!@;EM[ZT$C\Z-9RPL)L1'EZ0]``Q-?FRI;%I/3X(
+M[H^&R6SCC8[V-#DS5[*ZL7PT-@3GE6<;;X&SL.\V.CUDMK^U3P$%5NQN#C40
+MB;B_DC<[,VZ/M85[8I6`G00S/02+NKZ8"#(U89Z19!;CB[&!`#DX`X^YO9X&
+M"1OU_AT/!I*RN8D!.3D-A[&U[A!LE)E\"S,(G;^ZM1H],@&0@)!G99FUB6<P
+M.#:4O+N*$30+%^W+'@9UB+RQ2#(Z,>6QLH)N!&GIX!@T-&>WNK+V,#DWT(^,
+MZVS5A(^4#S\R%K:EO>,U,`W?D\,2%Y^VLY@+.SX9M;ZQX0$`;>-/``H%@;ZY
+M@`@X/!N#MX+6%,J$FQ$V/0V`N+N-`STQ$IR;P!30@K:/'#PX"H2^OH,:"@!T
+MXV(``.2VN;<0/SLUD[&VGA83]YS*`C`U_;*EL6\R/P[M@H57;>N.B/(T.3-:
+ML[JQ<C0T&N3L:P1N@+*SZ#8Z/6&WO(I)!Q_W[A4(-!*(N+^=-S@P:(V+F61D
+MDXV?!S(]!XBZOI@),PIGG>AI$.^ULX,`.3D"C;ZRGP<,'?_W&`D!DK*XB`8Y
+M.0V$MHOB%GN2FGDU,@N2O[JU&#(S!I&&ZVUXA;2+9#`X-NJ\N(L6-0YJXE,%
+M`'2*O[%$,CHQ_+:S@&\8>I?C!3<W8+:ZO?PQ/C17C8/@;,N!B)<./ST0M[JR
+MX@HQ`]J70AD1F;&RF@L[/QB*O[;F!@=^[W("-`2!OKF`"3@\&H"T@%QHYX:%
+M$#$\#(&XNXT`,C83DIU"$<6,L8X</#L*A;^_@!@)!UGF%0T#Y;&YMQ$_.S61
+MMK><%!3@F,H,,S3WLJ6Q8S,\#^.`GG5MEHB*^30X,TJSN[9W-349Y.00!FB"
+MO;*5-CH]8K2]B$X$$>;K%C4W'(B[OI\T.3!K@XZ29G.9CY@'/3P&B+N^FPXP
+M"&60Y!82Z+2R@@`Y.`*"O[.<!`(6Y\,%"@"2O;B+!SX^#9JWCN<409B$>#4]
+M-9"_NK4>,C,'EX7G:G^'MXI[,SLV[[VYBQ<+#&WC<08"<;6^L%8R.C'TM["&
+M;1Q%DNT$-C9LMJ6]Y3$^-%V#AOQO^8**D0X^/1*WNK+M"#<!P>MD!1.;L+V$
+M"SL_&XB\M^<$&ECK90PW!X&^N(,..3T:AHJ'36WH@X<0,#P.AKBXC0$S-Q&0
+MEF02S8ZSB!T\.S68O+R`'@\%V>00#@WZL;BV%SPX-9>WM),5;Y6%]`\R-\.R
+MI;!F,#T,YH:096V3M;3D-S@S?;"[MDD+"QWD]A\#%8*\LI8V.C)NM;*.3AMI
+MZ)43-#8>B+N^GC4^,6N`C9=@0X6(F@8]/P&)N[Z;##$.>9?.'!^5M[V-`3DX
+M#8.\LYT%`6GBW@8U`I*]NXH$/CX-F+6,_FK&A(9X-#PUEK^ZM1PS,`25F<X6
+M?8&QM7TS.S'CLKF(%0D#9.!D``Q\M;FSVS,[,<>TMH=B$/69[P8Q'ZBBI;./
+MDW4<``DT,#T^/SD\/S,P-`L`!!5E^NJ9A(.-B8B*BK6*BXB.C(*`AH6>D)7B
+M^<1;<V=M:!06$1`3$Q,3$!$1%Q05:6QC9F5_<$I!7U33V,;-R?7T]_?W]_?T
+M]<O)S,/!Q-C>TM#6U51645!24EU<7%]?7U]<75U24U!145975U15U=74U]?6
+MUM;6UM;6UM;6UM;6UM?7U]?4U-34U-34U-34U-35U=75U=75U=34U=75U-75
+MU=34U-34U-34U-34U=34U-34U-34U-34U=74U-75U-34U-34U-75U-75U=75
+MU-34U-74U=75U=34U-34U-74U=75U=75U=74U-34U-75U=75U=75U-34U-34
+MU-75U-34U-34U-34U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75
+MU=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=35U-75U=75U=75U=75
+MU=75U=755=75U=75U=75U=555=75U=75U=35U=75U=75U=75U=75U=75U=55
+M5575U=75U=75U=75U=75U=75U=34U-75U=75U=75U=75U=75U=75U=75U=75
+MU=75U=75U=55U=75U=75U=75U=755=75U575U=75U=7555555=75U=75U=75
+MU=75U=75U=75U=75U=5555555555U=75U=75U=55U=75U=75U=75U=75U=75
+M5=75U=75U=75555555555=75U=75U=555=75U=75U-35U-75U=75U=75U=75
+MU=75U55555755=75U=75U=755555U=75U=75U=75U=55U=/BD.WA\\1727=Y
+M>&1E9V5D>WA\<G=U0T1<4=;2V=K!P\S.R,C(R,G)SLS"P,;$VMG?TM/1U]15
+M5%915E!275U<7%Q=75)24U!045%65E=75%75U=34U-?4U-37U]34U-34U-75
+MU575U=75555555555=7555555%55U55555555555555555555515U=75U=75
+MU=74U=75U=75U555555555555575U=75U=555=75U=75U575U=75U-75U-75
+MU=75U=5555555555U=755=55U5755575U555U=34U=55U555555555755=75
+MU=75U=74U=755575U55555555555U=75U=755=75U=75U=75U-74U-75U=75
+MU=75U-75U=75U=55U=75U=555-75U=75U=75U=75U=75U=75U=75U5555=75
+MU=75U=74U555U=55U=75U=75U=75U=75U5555=77U5=55=55U=75U=74U=74
+MU=74U-34U-37U-755=755=555-555=5555755555U=75U=34U-75U575U=75
+MU=75U=75U=75U=75U=75U=75U-;55%55U575U=74U=75U=75U=755=755%35
+M5=75U=75U=75U=75U=755=555=55U=75U=75U=75U=75U-74U-34U-74U-75
+MU%755=75U=74UM5455355=755=75U=75U=75U=75U=75U-35U=7555555555
+M5575U=74U-34U-34U-34U-35U=7555755555U=75U=75U=75U=75U=75U=36
+MU5155=75U=75U=755=75U=75U=75U515U=75U-34U-34U-34U=35U=75U575
+MU5755555U=75U-75U=75U=75U=74U=74U=75U=75U=35U-;55U55U555U=75
+MU=75U-34U-35U-35U=34U5555575U=75U=75U-75U=75U-74U=34U=35U=75
+MU=75U=75U=75U=75U575U=75U=75U=74U]5755155=75U=75U-74U-74U-75
+MU-555=55U=75U=74U=75U=75U575U575U=75U5755=75U=55U=75U=35U=75
+MU=75U=35U=75U=75U=36U%155=55U-75U=75U-35U-75U=74U=74U]755555
+M5555U575U=55U=75U=75U=75U=34U-75U=75U=55U=75U=75U=555=75U=75
+MU=75U-;55U54U=75U=75U=75U=75U-75U=755%755=75U=74U-74U=75U575
+MU=755=75U=555=555=555555U=75U=75U=75U=75U=75U=75U=74UM545535
+MU=75U=75U=74U=35U=75U575U-35U5555555U5555575U=75U=75U=75U=35
+MU-75U=75U=75U=75U=75U=75U=555=75U=75U=76U5945%55U575U=75U=75
+MU=75U55555145575U-34U-35U-75U=555555U55555555555555555555=75
+MU=75U575U555U57555555555U=?55U555=75U=74U=75U=75U=75U-35U=34
+MU=555514555555555=755575U=75U=75U=75U=55U=55U555U5755575U=75
+MU=75U=75U=74UM5755155=75U=75U=75U=75U575U=555-75U=35U=75U=55
+MU=75U=75U=75U=75U5755575U=75U=74U=35U=75U=75U=75U=75U=755=36
+MU5=55-75U=75U=75U=75U-75U=35U=74U]3555555=75U=75U=75U=75U=75
+MU-75U=34U=75U=75U=55U=55U=75U=75U=75U-34U-34U-'55%55U=74U=35
+MU=34U=34U-35U=745535U=74U-35U-75U=75U=75U=75U=75U5755555U=75
+MU=75U=35U=75U=35U-35U=75U575U575UU575535U=74U-74U=34U=34U=35
+MU=75U-?4U575U=75U=75U=75U=75U=75U=35U=75U=75U575U=75U=74U-74
+MU-75U=75U=75U-35U-36U5155=75U-75U-34U-35U-75U555U5575%55U=34
+MU=34U-35U-75U=75U575U=55U555U5755555U=75U=75U=75U=55U5755575
+M5=75U=35U=55U=55U=75U=75U=75U=75U=75U=74U575U555U555U=75U=34
+MU=75U=74U=74U=34U=75U=75U=55U5555=75U=75U=74U=75U=75U=55U575
+MU=75U=75U=75U=55U=75U=155%75U=34U=74U-34U=35U-75U=75U=755=75
+M5555U=75U=75U=34U=74U=75U=34U=75U=75U=75U5555=55U=75U=75U-74
+MU=34U=74U=75U]5555555=75U5755=75U=75U=75U=34U-34U-5555755=75
+MU=75U=755=755=555=75U=75U-35U=75U=75U-75U=75U=75U=75U=74U575
+MU=74U-75U=75U=75U=74U=75U=34U=35U=75U=74U=35U=35U=75U=75U=55
+MU=75U=75U=34U]35U=75U5555575U575U=34U=75U=55U=35557555755=75
+MU=75U=75U=75U=55U=75U=75U=55U=75U=75U=75U=55U=75U=75U-34U-34
+MU%75U=75U=755=74U=75U=74U=75U=555=75U-34U-34U=35U=75U=75U=75
+MU=55U=55U=55U555U555U=75555555555575U=75U=75U=755555U=75U=75
+MU=75U=35U=755=75U=74U-75U=75U=55U555U555U575U=755=75U=35U=75
+MU=55U5755=74U=75U=75U=75U=75U-74U-35U=755=755=7555755555U=75
+MU=555U15U=35U-34U-75U=75U=75U575U=75U575U555U=75U=75U=75U=34
+MU-35U-34U=755575557555555=55U=75U=75U=35U=34U=75U575U]34U=75
+MU555U=75U=75U=75U=75U=75U=74U=75U=75U=75U=75U=75U=74U=75U=75
+MU=5!9F1P<49:U=':8B0@/C$"%/J9@XJUMK&ZH:2"Z96"O;F+D9:(CG(\)SD6
+MB8(=,STU`S0X)32%L(@'/S\"^NP5$I:PN8D#/S'GL+*-Y):(M>$U/`V.NKZ:
+M!P-A[V((-6>QNK8</#T$EI!M'>^WLYTW)3+4M[:>:G^;AVDP/P*(N[SK#PD4
+MX&<,#O>SNK0$/S(<A816%9>WL>\Q)3#KL+"?$&F1GA`P/02TNK+&-340X'(#
+M`^^RN(X,.3(4@(#\;)>UM-4S.C>8O;.3&1/MD1TQ,Q:QI;!A-C<3[M4$!)"R
+MOH$U.S-[CH_C;>N(B6(R.PN#OK.5!QOWZ1PV-G"SI;82,S80E_X>'Y^RO)PV
+M.C#+M8ON;^",@Q`].0*(N+/E`@9$X!TT-?B]NK4'/3$7G.@7%INSL^`P.C:5
+MMK7I%?>!A!@]/P6TN[!>"0)YYQ,*#Y>\NXP,/S%MA))C:9JQMDP].C2;L[?L
+M$$":G`0R,A2QNK9L-0YAYA0/`9F_N88U.39W@)IV8)NVM14\.PF`O;?D&6"3
+ME`8S,4.RNK0<-@MC[&T#&X>_OY(V.#?PCH%49IFTCQX_.`")O[?-!Q;HXP8P
+M-."\NH@&,S5FE'$'$(&\LN0S.S25M8W!8)*(@0<_/ABTN;=V`Q[[^`8V")*^
+MNX(,/35\G<$>:8.]L7T].PN9MX[,;I2,F0,_/6NPN+1H#@3&\@0T`H2YN(0U
+M/S53A>`69(*RM!$_.`R`L(C8%^&!EPP\,U>RNXH<-0!0_1L+!(.YOI$V/C7_
+M@99O<X.PB04Y.0:.LHM$'=N;X`\]-^F_NX\'-P);Y!T,$H^YO?(S.0N5C)YD
+M=8&V@P,Y/A^TO(MZ!7^3]P\R"IFYN(`-,`Q1[Q0`:8F^L&<\.0^>BX1S=H>U
+MF@\Y/&VQOHEK`6CKTPPP#(&XN9L*,@_!EF8%?HN_MQ(^.0.&M(%Q>YN)E@LY
+M,\>RN8\=#!/F4PTW!XR[OY4W/0_EGEL<7(N]BP<Y.02,L8-_;9*#YC4^-I6_
+MN8,$"!C\40`U'(N[O<,P/`SJA/P7P8BS@@PX/Q.ULX-A%^B'WC4_-9JYN8<"
+M-`3QQ0<.:;6[L&(]/P.=@^ENRHZQA0L[/6>VO8-J'/V>=S4]#X.[OIP(-@;R
+M_!L#=+>XMQ\_/P:$CI!CR8RTD30[,_>SO(`3!$26934P!HFZO^PT,`;G[1,'
+MSK>^B@$Y/QB"M9]ATH"(Y3<X-I:\OX<:`&'I80LV'+6ZO5<Q,P;IEFX9Y[>\
+MC0X[/!>(MYMC3H2-6S8Y-86^OY@!#!7A9@XU;[:ZL&DR,@20GG(0[[>RA#4[
+M,G^TL9IH9YZ&83$_#X*XO)8/"Q#D>`T)1;&[MQD\/1N;A]H5Z+2QD38Z,/ZQ
+ML)L6%9>9%3$]!HNZO>0*-!SF30$"^K"XBP`^/1*&@^1OZ8JT_C$[-I&RLYX?
+M'>:0%C8S'[>ELT\W-Q_LP`4'ZK.^@@DX,FB,CNYMXHF(<3,X-82_LI`$!=KK
+M$#<V;;&EL14P,1R4YAP9D+.\A30[,TJ*BI5O^(*-:S(Y#XVYLNX#`7;C$#4U
+MU[.ZM!L],1.=E!40G+"REC$Z,>6WM)<5WX:'$C(_!HJ[L_<.#&'F%PL.XKV[
+MB`,_,!2%G&=JGK&Q]C,Z-Y"PMY40<YB>&#,]'+:ZL'TU"6[A:0\#D+RX@@@Y
+M,6&!A$%MG[>T93T["H2]MNX>;I&6!3,P8["EMA<V-6CL9``$F+R^A30X,5^/
+M@,%@DK6)$3PX#(V_L>4$$^WO!#$WP+VZM1LP-&F46@0<A+R]E#$[-^>*C/-B
+MEHB#&SP^!XJYMM`#&_#G!38*ZKRZC@,]-&.2\Q\4AKVPP#([-)"WB?]I[HV%
+M!CP\$K:[MV4&I:"ZL8*59Q@""#0R,CX_.3P\,S$U"`$::G#DE)B'@XR)B(J*
+MM8J+B(Z,@H"'FI^0E>/\VD1]9FQK%!81$!,3$Q,0$187%&IH;&-F97]Q2$9<
+M5-/8QLW)]?3W]_?W]_3UR\[-P\;%V-_=T-;55%904U)=7%]?7UQ?7%Q=75)3
+M4%!15E=45=75U-34U]?6U];6T='1T=;6UM?7U]?7U]?7U-34U-35U=74U=74
+MU=75U=75U=75U-35U-34U-34U-75U-34U-34U-34U-34U-34U-34U-34U-34
+MU=75U=75U-34U-34U-74U=75U-34U-34U-34U-34U=35U-34U-35U=75U=75
+MU=75U-75U=75U=75U=75U=75U=75U=75U=75U=74U-35U=75U=75U=75U=35
+MU=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U575U=75U=75U=75U575
+MU=75U=75U=75U=75U=75U=75U=75U=75U=555=75U=75U=75U=75U=75U=75
+MU=75U=75U=75U=75U=75U=75U=555=55U=75U=75U=75U=75U=75U=34U-35
+MU-75U=75U-75U-75U=75U=55U=75U=75U=75U=75U=75U=75U=75U=755=55
+MU=75U=75U=75U=75U=75U=35U-75U=75U=75U=75U=755=55555555555=75
+MU575U5555=75U=74U=75U=75U=75U=75U=75U=75555555555=75U=55U555
+M5555U=75U=75U=755=75U=75U=74U=75U=55U=75U=75U=75U=755=75U=75
+MU=75U=75U=75U=75U=75U=75U=75U=75U=75U-75U=75U=75U=75U=35U=75
+MU=7WEI3FY\#90TM\>65E9&=D97AY<G!U24=;4%30W-K&PLW.R<C(R<G)SL_-
+MPL#&Q=O>W]+0UM355%914%-275U=75U=4E)34U!145%145975%555=75U=34
+MU-74U];45-55U-74U-34U-35U5755=555%755515U-1555155%1455555=55
+MU=75U=555=555=755=75U5755575U575U=75U-75U-75U=75U=74U]575%35
+M5575U=75U=75U=55U=75U=55U5145=75U=75U=75U=75U=75U=75U=75U=75
+MU=75U=75U=75U=75U-75U=75U=75U575U5555=76U5=55%555=75U=755=75
+MU=34U=75U-75U=34U=555555U=55U=755=75U=75U=75U=74U=75U=75U=75
+MU=55U555U=75U5555=755575U=?55U145575U=35U=75U-35U-75U-35U=55
+M5U75U=75U=74U-35U=75U=5555555555U=55U=74U-35U=74U-34U=74U-35
+MU=75U=75U=75UM1755755=75U=75U=75U=75U=75U=75U=74U-75U5555575
+MU=755=7555555=75U575U575U-755575U=75U=755=75U=755=75U=75U=36
+MU%155=55U=75U-74U-35U=755=75U5755%15U=75U=75U-35U575U=555575
+MU=75U=75U575U=745=75U=755=75U-75U=75U=555555U==55U145=75U=74
+MU-74U-74U-34U-34U-35U-?5U=7555755575U=75U=75U555U555U575U=75
+MU=75U=755=5555555=75U=75U=75U575UM565575U=35U=75U=75U=75U=74
+MU=74U-55U=35U=74U-34U-75U-75U=75U=75555555755=75U=75U=75U=55
+MU=55U=75U=75U575U=76U5355575U=75U=75U=75U=74U=35U=75U=37U-75
+MU=55U575U=75U=75U=75U=75U=35U=35U-75U=75U=74U-34U=75U=75U=75
+MU=75U=;55U545575U=75U=75U-34U-75U555U=545-75U=35U=75U=75U=75
+M5=55U=75U=55U=555=75U-75U-75U-74U=75U=74U=74U=74U=74UM575515
+M5=75U=34U=75U=75U=75U=75U=74U]35U555555555555=55U575U=75U=55
+M5=75U=75U=55U=75U575U575U555U=75U=75U=34U574U=75U=75U=75U=75
+MU=75U=55U=34U555U=55U=75U=75U=75U555U55555555555555555555555
+M55755=75U=75U=5555555555U=955U545=55U=75U=75U=75U=75U=74U-75
+MU-?55554555455545575U=75U=75U=75U=74U=75U575U=75U555U=755=75
+MU=74U=34U=74UM5755355=555=55U=75U=75U=755555U=555=75U-74U-35
+MU=75U=55U5555555U=55U=55U=75U=75U=755=55U=75U555U555U5555=77
+M55=45-75U=75U=35U=75U=55U=55U=75U=37U%55555455555=55U=74U=75
+MU=75U=75U=74U=555=75U=755=755=75U=75U=75U=55U=?55U55U=75U-35
+MU=75U=75U=75U=75U=355=74U-34U-34U-75U-34U=34U-34U=75U=755575
+MU=75U=75U-74U-35U-35U=755=75U=75U]575575U=75U-75U=75U=75U=75
+MU=35U=75U]34U=75U=75U575U=75U=75U=75U=75U-34U=34U-35U=34U=35
+MU=75U575U=75U-75U-36U%155=74U-34U=34U-34U=74U=755=74U515U=74
+MU-35U-74U=35U-75U=75U=75U=74U=75U=74U-75U=75U=35U=35U=755=75
+M5=75U=?55U545575U-75U-35U=75U=75U575U=75U=?7U=75U575U=75U=75
+MU=55U=55U=75U=75U=35U=75U=35U=75U=75U=75U=75U=34U-34UM145575
+MU=75U=75U=75U=75U=75U=75U-15U=34U-34U-35U=5555555575U=75U=75
+M555555555=75U=35U=35U=75555555555575U=76U5=55555U=75U=75U=75
+MU575U=75U=75U=74U-7555555=555=75U=34U-34U-34U=74U=75U=35U=74
+MU-75U=75U=75U=75U=75U=75U-;45U55U=74U=34U-35U-75U=75U5555=75
+M5%75U=75U=75U=74U=75U=75U=34U-34U-75U=55U=555=75U575U=75U=74
+MU-75U=75U=74UM5755155=75U=75U=74U=35U=34U-34U-74U]?5U=75U=55
+M5=75U=755=75U=75U=74U-34U-75U=75U575U=75U=555=755=75U=75U=36
+MU5355=75U=35U-34U-34U-34U=35U=75U555U=74U-34U-35U=75U=75U=75
+MU=75U=75U5555=75U5755575U=75U=75U=7555755=75U=;55E1455755=75
+MU=75U=75U=75U=75U=35U=?4U=555575U=75U=75U=75U=75U575U=75U=75
+MU-755=75U=75U=34U=75U=75U=75U=74UM14U575U=55U=75U=75U=75U-74
+MU-35U=355575U=75U-34U-35U-74U-75U=75U=74U=74U=74U=75U=74U-34
+MU-34U-75U=755=55557755?55=75U-75U=75U=75U-34U-35U=35U=74U%55
+M5575U=75U=75U=75U575U=75U=34U=35U=35U=75U=355=555575U=75U=55
+MU=55U=35U=75U-75U=74U=74U=74U-34U-34U=755535U=74U=34U=75U=74
+MU=75U=34U=74U-75U=75U-35U=74U-35U=35U=75U=75U=75U=74UM545575
+MU=75U=75U=75U=75U=755=75U=74U]?4U=7555555555U=75U=75U-35U=35
+MU=74U-35U=75U=75U-75U575U=55U=75U=75U=31U5355-75U=34U-34U-75
+MU=555=75U=75U-545=55U=75U=75U=75U=75U=75U=75U=75U-75U-75U-75
+MU=75U-34U=75U=75U=75U=75U=?55U145=75U=34U=75U=75U=75U-75U=75
+MU=?7U=55555555555=55U=75U=75U=75U=75U=75555>969S<T)$5M;913TC
+M)3,/$\N%@(N+MK:SMK>PIZRGA1]FFI@<#G:VL_H\)S\#`S`R!8.-!B4G,6!A
+M``:>LK80/SUIC(#YX+>XMA@]-)&VB>SJM+V&-3@UA;:"0,2/M4D].@^,L(9I
+M<H:!&CDY$[:RA!!DA9P"/C/IO+V2&FF2X`@\"X*[L\H"$NY8"C,%M[JW$`H;
+M^V<*-T.]NHP"-@7^80D/G;FYDS0P&^)_`AJ-N+)D,C`0D%0$;;6XM`$^,7R$
+M^QW"M[^&-#LWZ8WJ:N^VL/HR.P^'BY!BE[2U$CDY'HJWD&V7BH$-.SW`L+:5
+M%.Z-E#0X-X2_MO<?_(1`,3X#M;BT;0=$D!0P,FZRNX\%#6;L'S$TE[ZYF0X(
+M;N48-P*"N[S=-S5LY!X+$[2ZMADR-7+I$P/0L+N,"#\*_9)I&Y:ROY<P.0R3
+MA7(7A;*Q:3\^&H"#TF&&LXD#.SU_BH_.>8&WF#0Z,9"QB-9EAXC#,SL.C+V)
+M96Z9@1$]/ARVOX(1$)>2!SPS^[R_FP<:Y^`",@J&N+WE#@;-Q@PQ!+6EL10T
+M`]]1#35^L[J(`S`"]]X`#9>_N)@T,@'L^00;AKZ]33(]&)SH$FF/OK<$.3)M
+M@9)K1XN]@#4[,>&)FF?WBK'L,SL+A[>$9/^)BQ4_.02*L(5N]8V&`3D]2;"S
+MG!-:AY8(.3:9O[/@!&&=VC0\#8B[MF<-%^AF-#`7L+J+&@L=YVHT-.*_NX<.
+M-QSD%0L#AKB_^38V$.]L#1*+N[$2/3%MDWP'2[>XC@XY-\&%V1WBL;R=,3@(
+MDX+G:I"QL7L_.0:#B.YCG+:)!S@_;+6U[F*<M9H*.S"5L[3X%9&-^38X"X*_
+MM'0=[85L,S\;MKF)$03.D1XS,-B\N88&`TK@!#`*A;B_Z`@.>_T'-P>+NK-O
+M-@M]_@0(8[&ZM0$R"-'B'@#BLKB$-#\/[9$6'IB\O<PR/P:9F&!H@;VW&#D\
+M%H*&='^"LX((.S/RM8)%3(*WE#$Z-86QC7%T@(YF/3@!BKV";V:%AQH_/&:S
+MO(0<%).6`C\VD[Z\EP$?[_,),@R/N[-U"03_0P@Q$[&EM!@W!O9S#@KRO+N#
+M#C`&^DX"`)NYONDQ,P65V@<=C+FP%SPS%IGF'WN*OHL-.3%%@)04]+2]F#8[
+M-)&)G6+CM+9>/3@"@[2>8.R*CALY/A:TL9UKX8R%#S@SX[*PZA++AN`T.36`
+MOK'3!'2<?38\!+>[M!<";>L4-C%TO;N-`0@4YA,W"YRYN9,*-!?D$PH'C+N]
+M>3`W:>\7#&BWN[0'/#1WD&T'\+"Y@#4^"N&%=1*1L[WF,CD-F(#,:)BPMQ,^
+M/A^,COMFA;:"##LRU[>*_F&:M9`W.S>8L[76:)^-3C,Y`HB_B&,3E)H3,CUJ
+ML+Z"'AKEE`<R-I6^OI\#`=+Z`#`/@KN]VPH-0<\#-Q^WI;8<,0]2RP$(U;*Z
+MC`\R#//D!0&3O+Z0,3P`E)42$H:_LV(\/!F%G&A@C+R*`#@R>HR$946.LX0T
+M.S:5M8%]WHZW]S([#H.Q@6?7@HP3/SX?M+*'%7>'F@`^,O>RO9T9;9_O"3\U
+MA[FR^0`1ZE<U,@:UNK9K"1GG934V9K.ZB08T!?EC"PB7OKB;-3$;X60,!X"X
+MO%8P,!*42084B[BV!3\Q8YCU&5&WOH(*.#?Y@.(7X[:RE3,X"9B)EVR7M[=K
+M/SD$C[218I:UC0`X/'RVMI5KE8^2-3@VG;VV^!+FAL0V/@^.N;1Z!=V<:#`]
+M$[&XB1\#?N@<,3;GO[B$`@YMY!@V#X:[O^0T-6SE&#49M;JP$3`U>N(=#'&Q
+MNX@,/`K%EA0$Z+*^GC8^#Y289Q"8O;-V/#X'A(%;;8:SM0<X/&F)C<5X@;:'
+M"CLP[;>.W7F&M>8P.PJ!LHEP8YJ":3T^!;2_C147DYD%/#):O:"LI[R*F=$3
+M`0XU,#(_/CX^/STP-@H,!QUAW.^2A8&-CXB+BK6UBHN)CXV#AH28G9;NY/54
+M=7IC:147$1`3$Q,3$Q`1%A<5:VYM861X<G1-6U#7W\7#S\OU]_?W]_?T]<K(
+MS\+!Q]K9W-+1U%5745!34EU=7%Q?7U]<7%U24U-045975%155=75U-?7U];6
+MUM;6UM;6UM;6UM?7U]?7U]34U=34U=74U-34U=75U=75U=74U=34U-34U-34
+MU-34U-34U-34U-34U-34U-34U-34U-34U-35U=75U=35U-74U-34U=75U=75
+MU-34U-34U-75U=74U-74U-74U=75U=75U=75U=75U=75U=75U=75U=34U-74
+MU=75U=75U=75U=74U=75U=75U=75U=74U-75U=75U=75U=74U-34U=75U=75
+MU=75U=75U=75U=55U=75U=75U=75U55555555=75U=75U=75U555U=75U=75
+MU=75U=75U=75U=75U=75U=75U=75U=75U=74U=75U=75U=75U=75U=75U=55
+M55555575U=75U=75U=75U=75U=74U-74U=35U=75U=75U-34U=75U=75U=75
+MU=74U=75U=5555755=55U=75U=755=555=55U=75U=75U=75U=75U=75U=75
+MU=75U555U575U=75U575555555555=75U=75U=755=75U=75U=35U=75U=75
+MU=75U=75U=75U55555555575U=75U57555555=75U=35U=75U=75U=75U=75
+MU-75U=755=75U=75U=75U=75U555U=75U=75U=75U=7'E9'AX<K:6DMS>7ID
+M9&=E9'AX?7-T2D%%4E;1W=O$P\S.SLO(R\C(R<[-PL'&Q=O9W]+0UM155U91
+M4%)=4EU=4E)24U)34%!04%%15E975U=55575U=35U-34U-?4U-34U-34U=35
+MU=75U=555555555455145%55555555755555U=75U575U5755574U5155554
+M55555=75U=555=75U=75U575U=555=5555555=75U=75U5755575U=75U-36
+MU%155=75U=75U-7555555555U=75U-75U=555%75U=75U=74U=75U=755=75
+M55555=755=55U=75U=55U=75U=75U-75U=75U555U555U=75U=75U=75U=75
+MU=75U=75U=75U=75U575U=74U]35U=755=755=755=75U=75U=75U=75U=75
+MU-75U-35U=75U=75U=75U=75U=75U=75U-75U=75U=74U=75U=75U=75U=75
+MU=755=755%=55=74U=34U=35U=55U=15U=35U=75U5555575U=75U=75U=75
+MU-75U=75U=34U=74U-7555555%555575U=74U-34U=74U=75U=75U=55U=?5
+MU=55555555755555U=55U555U=75U=55U=75U=75U=74U=755=75U=75U555
+M5575U=755=75U575U=75U=75U=34U-35U=75555555175555U=75U=35U-35
+MU=75U=75U575U575U575U575U=75U=75U=34U=34U=75U=755575U=555575
+MU5755575U=35U=34U-?4U-35U=755=74U-5555555=755555U575U=75U-74
+MU-74U=74U=75U=75U=75U=34U=75U=75U=75U=34U-75U=35U=75U=75U=34
+MU=75U=75U-75U=345=75U=34U-74U-74U=74U=74U=75U55555555=75U=55
+MU=75U=75U=75U=75U=75U=35U=555555U=75U=34U-34U=75U=55U=75U=74
+MU=74U]35U5755=75U-74U-35U-75U=75U=35U=74U=34U=75U=755=555=75
+MU=75U-74U-35U-55U575U=75U=74U=75U575U=75U=35U=74U555U=74U=34
+MU=75U=755=5555555555555555555=5555555=75U=75U555U55555755575
+MU575U=75U=75U=75U=75U=75U=75U575U-75U=34U=75U575U=75U=75U=34
+MU=75U=75U=75U=75U555555555555575U=75U=755=75U=75U=75U=75U=75
+MU=75U=75U=75U=75U=75U=545-75U-34U=75U=75U=75U=75U=75U=75U575
+MU=75U575U=75U=55555555555=555555U554555555555555555555755=75
+MU=75U=75U=74U%5555555575U=75U=5555555555U=75U=74U=75U=75U=75
+M5=75U=75U=75U=75U575U575U=75U=75U=75U=75U=75U=75U=75U=755%75
+MU=75U-75U=75U=75U=75U5755555555555555=75U=75U=75U=55U=75U=75
+MU=555=755=75U575U575U=75U575U555U=75U=75U=75U-15U55555555555
+MU=74U=75U-75U=75U=75U=755=75U=75U=75U=35U=75U=75U=75U-55U555
+M5=75U=74U=75U=75U=75U=755575U515U=74U-34U-34U=75U=35U=34U-75
+MU=75U=75U=75U-34U-75U=75U-35U-74U-75U=74U575U=35U=75U=75U=75
+MU=75U=75U=75U=75U=?7U-75U=75U=75U=75U=75U=75U=75U=35U=75U=55
+MU575U-74U-34U-35U=75U=75U-35U=75U=35U-75U=75U=74U-34U-34U=75
+MU=545=75U=75U=75U=75U5555575U=75U-75U=75U575U=75U=75U=74U-34
+MU=74U=75U=75U]575%155=55U=35U=75U=35U=75U=75U=34U=74U-75U=55
+MU=75U575U555U=75U=75U=74U-34U=34U=75U=75U=75U=75U=755=75U=36
+MU%355=55U=34U-34U=75U=75U=35U-35U-35U575U-34U-34U-74U-75U%75
+M555555555=755=75U=35U=74U=75U=34U=35U=55U=75U-?55U1455555=75
+MU-34U-35U-75U=75U=75U575U]?5U=74U=75U575U=75U=34U-34U=35U=75
+MU=75U=75555555555=55U5755=75U=74UM14U575U=75U=75U=75U=75U-34
+MU-34U=75U554U=74U-75U=74U-74U-75U=75U=75U=55U=55U=75U-35U-35
+MU=75U575U575U-75U=76U5=55%55U=75U=75U=75U=75U=75U575U=555=37
+MU5755575U575U=755=75U=55U5555=55U=35U=75U=55U=755=755=75U=75
+MU-75U=;45U55U=74U-34U-35U-74U=75U=74U=755=555-55U=35U-35U-34
+MU-35U=75U=55U=75U=55U575U5555555U=75U=75U=75U=75U=74UM145575
+M5=75U=75U=74U=35U-75U=35U=75U=74U]55U=55557555545=55U=55U=75
+MU=75U=75U=75U=75U=75U575U57555555=55U=71U%=55-55U=75U=74U-34
+MU-35U=75U575U=755575U=35U=75U-35U=75U=75U=75U555555555155575
+MU-34U-34U=34U=74U=35U=34U-;55U545575U=35U=75U=75U=75U-34U=75
+MU=55U-?5U5555515U555U5755=55U5555=75U=74U=75U=75U=75U=75U=55
+M5555U=75U=74T=175%155=75U=75U=75U=75U=75U=755W-@?W-*1%S6W=H"
+M+2<S"A]WD(2/BK.[O(N.B+2DHJ2`&@UKGX#F'01\[7`T)"$_9;6)$#@A)0.0
+MA/`:!7"7\@0T#)&\I;!K,CT?M+B]A1$?5)V5:P5YC[RSYC$Z,.F\N8X%,300
+ME)5D$]:`M9@"/SP?M[J]RS$_"^*.@N%I7YZ%=0@P#82^N(,(.SP1BK.)=P<<
+MQN)N`@Q$M;^W$3\Z"H:^O)H`-`-=E\43%Y.TM^HU.#/2O:6W$3,S!Y*"G')A
+ME(.:'3$R!(B[OY<V.S''M;>%%1]PE?,%-0V7L[F.`CLY!XN_L?H-#A+GY1<$
+M88"PM'TS.S>>OKB-`3,T:IF:]VG%AHF3`CTS8K&ELV4R/@F1M8GB%F/KDGH.
+M-P:!OKZ:-#H]8+>]B6`"!W3C>@<&]+6]BQH^.`R/N+R2#C8-T)SM;VF6BXKR
+M-#XV[K^ZM04\,@6%B81+:>6%DQ\W,1"UN[WP,SLV[+:QA1,':N;S'@X'G;.\
+M@0D[/ARWN;%7"S49X.IE''V!MHAJ,CD+AKBX@0\]-VV'@>QM1YB#[@TS-M>S
+MNK80/SD,FK:*XAP0]Y1[#34>@KZ]ES$Z,].POXX7"0-G[U8>&^6ULX(!/CX&
+MM;N][C4S#_>$DW!MZXR,1S0\-9R^NHX#/C(9@+2!=!%;DY4<-31AM[NP>ST[
+M-)VSLYH;`ASP^Q8`'IFPLI@U.SQLL;NV9C0W&^B=Q!1TA[2"$C(_#8R[N9@U
+M/S=\C8^6;&26A/@",37CLKJU!#DY`X"SM.<%&G;L<0$.%(R\L_,S.C'OO;Z,
+M&30.8Y3E%!/AB+:$`CX\'+>ZLLPV/0_@@X70;.2&AF4T,@^$N;B`"#@R$8ZV
+M@WX9;>SM$P@)7+>YMQ$_.PN'O+*?!@@%S>)C!1&8L;&5-#@SU+*ZM!$Q,1J0
+MA.UM<9B)AQ@S/02+NK^4-CDWWXB*DFL5Y)S.`#</D;VXB0(X/@2.O;?V``!L
+MX%$:`&&/O;9\,CLWG[^Y@P8V"V*2EWL4YXRUG0\_,FRPI;!E,SP,EXZ`]VK3
+MF9MB-3`!@[FYFC0[,F"ULXUC!Q+VX1<,`ORWOXH:/C@,C;ZRD0TT!LR77!)H
+MF;>T_38Y,>R\NHH:,C`8F8.09&60@Y\%,#`2M+J]\S,X-."TM)P1'$>4VP8U
+M`9Z]OH`).S\<M;^W7@\/%^#R$`5WC+.U%3TX"X:YN8<,,S5GFIG?:?V`B>L.
+M/#%6LJ6V$3P_#9ZUC/T19)23;P@W&(RYO)8Q.C!4MKV-%`(%7.!C`0?BM[V,
+M`3DY!HNXLNP+-@#PG.=K8IR*B$TV/C2=OKN.`SPS'(:)F'EOXH27!38W8K>Z
+MLWX].#60L;:<'P1MX\$:#AJ%LKR:-3L\;K&YM&8*"Q+O[F,<68*VCQP]/@R,
+MN[Z9"STT<8&&Y&_2A8/X#C(TX+VZM`4^/P"&MHCP'A?XE6T/"A:)OK+_,SHQ
+MXK.\@AP(`''O21@9Z+2SA@PY/Q^WNK/,-S`"YH26>V"6CX)A-CP)A+FX@PD^
+M,Q:-M81E%M^2[QHT-5^QN[86/S@(A;*PDP0"$/CS$@,2A+.SES<[,ERSN+46
+M-S0<EY-2%U"!M($%/3P'B[J_ES<_--R,C>AI?9.$V@\P")&\NXD#.#\$C+.*
+MP`086.QD``]@B+^Q<#(Z-Y*\OX`$-`Q^E_,6$.N*MIX)/CUHL:6P>#$R`I6#
+MF$9M[8"':S<R`X.XN84T.#-CB[:&8QEGZ.8>"P_YL;FU&CDX#(*_L)<#"1CS
+MX6D$%82QMO@V.S'CO+N+&#`V'9V%YV]'A8F;!S(R'+2EO?\P/C7GBHB7%&GC
+MG%T--P*8O+F#"3L_'(J]M40#`6?C2`0`=8B]M!4\.PJ'OKZ'`C8)?)WJ817N
+MCK65"S\P1+*EMA8R/0.=B8;3:\N;GA0T,06/N+^1-CLP6;>P@14'$?GE$P\`
+M[K&_C@`Y.0:(N;/B#C4$_Y1U'6*%M[5#,3DWDKZ[CP$],1.%@)1A<IV"D`8P
+M,6ZVNK-S,CD*EK>TEA,=T)1:`#4'A+R_A34[/6BWO[5G#@QH[<\=!=:)LX@?
+M/#D/C;B_F0DS"TB%GUENYH*.Y0L\-^:]NK0:/SP!A+6"V!%REI$7"C<3B+B]
+MY#,Z,>>PLH$2`AO9YF@!!9>VO8`,.#\9M+NPSC4V!N><_!5GF+6.8S$^"X2X
+MN(,//S$4@(Z<9F+HA.@&,31#L;JQ%#\X"9NPMY&+HJ2RB9U!'0`)-3`R/SX^
+M/SPR,3<(#043>\+JG82`C8Z(BXJ*BHN(B8^-@X:%F9*4[/K.779D8F@5%Q$0
+M$Q,3$Q,0$184%6AO8F%D>7-U0EA1T=[$PL[*]?3W]_?W]/7*R<_"P<3;WMW3
+MUM155U%04U)=7%]?7U]<7UQ=4E-04%%65E145=75U-?7U];6UM;6UM;6UM;6
+MUM;7U]?7U]34U-34U-34U-34U=75U=75U=75U-34U-34U=74U-34U-37U-34
+MU-34U-34U-34U-34U-34U-35U-35U-34U-75U=75U=74U=34U-34U-35U=35
+MU-34U-34U-35U=75U=74U-34U-34U-75U=75U=74U-34U=75U=75U=75U=75
+MU=75U=75U=75U=74U-35U=75U=75U=75U-35U-75U=75U=75U=75U-75U=75
+M5=75U=75U=75U=755=55U=75U=75U=75U55555555575U=75U=755=75U=75
+MU=75U=35U=75U=75U=34U-34U=75U=75U=75U=75U=755=555555U=75U=75
+MU5755=55U=75U=75U=75U=75U=75U=35U-75U=75U=75U=75U=75U=75U=75
+M5=55U=75U=75U555U=75U=75U=75U=555=75U=75U-75U=75U=75U=75U=75
+MU=75U55555755=75U=75U575U5755=75U=75U=75U=55U575U=75U=75U=55
+M55555=75U=75U=75U575U=75U=75U=75U=75U=75U=75U=35U=75U5555=55
+MU=75U=75U=75U=75U=75U=75U=755=75U=75U=75U=75U=55U=75U=74U=75
+MU=75U=75U=75U=35U=35U=55U=75U=GID./A]\5<2W%Y>V1D9V5D>'A]<W1+
+M0$525M;2V,7`PL_.R,C+R,C)SLS"P,;%VMG?W=#1U]555U914U-275U=7%)=
+M4E-24U-04%%145=45%55U=34U-?4U]?4U]34U-35U-75555555555=755555
+M5555555555555555557555145%555=555=755555U5555575U=75U=74U-74
+MU]15U5555555555555755575U=75U=75U=75U=755=55U=75557555555555
+M5%155=755=75U=75U=35U=35U=35U=35U-35U-75U-75U%55U=74U-35U575
+MU5755555557555555%55U575U=74U-34U=35U-35U-75U=55U=75U=55U=75
+MU=75U=35U-75U-35U=55U555U575U=75U-?4U575557555555575U=75U=34
+MU-35U=75U=75U=75U=755=555=755=75U=75U=75U=75U=75U=75U=75U=75
+MU=75U57555755575U-555=75U-34U=75U=75U=75U=75U=75U=75U=75U-35
+MU-75U=755=55U=75U=75U=75U=35U=75U-74U-75U=75U575U=75U575U=75
+MU=755=74U=75U=5555555=75U=75U-35U-75U-75U=75U=75U=5555555555
+M55555575U=75U-55U=75U=74U-74U575U=75U=755575U=75U=755535U=35
+MU-34U=555=75U=75U-75U55555555575U=74U=75U=74U=75U=75U=75U=74
+MU5555=75U555U=75U=75U=755=75U=75U=755575U]35U=75555555555=75
+MU=35U-34U-34U-34U-75U=755555U575U=75U=75U=75U=755=75U=74U=75
+MU=74U-34U=35U=35U=35U=75U514U575U-74U-34U-74U=75U=74U-35U=75
+MU-35U=75U=75U=75U=74U-75U=75U575UM5755155=74U=74U=74U-34U-75
+MU=75U5555=55U=34555555155575U=75U=74U-34U=74U=75U=75U=75U=75
+MU=75U=75U=755=75U=31U5=55%55U=75U-35U=75U=75U=75U=75U=75U-55
+M5=75U-34U-35U-75U=75U=55555555555=75U=75U5555575U=75U=75U=75
+MU=75U=;55U145575U=35U=75U-75U=75U=75U=75U=75U=74U%7555555=75
+M55555555U5555=75U=75U=75U=75U5755575U=75U=755=75U=74UM145575
+MU=75U=75U=75U=55U=75U=75U=755=755%155575U=75U=34U=35U=75U-75
+MU5755=75U575U=75U575U=75U575U=75U=75U=36U5=45-75U=75U=75U=75
+MU=75U=75U5755575U=74U]35U57555155555557555555555U5555555U=55
+M5575U=75555555755=755=75U=;55U5455755=75U=75U=35U=75U=75U=55
+M55555517U=75U-75U=75U=75U=75U=75U5555=75U=75U=75U=75U=75U=75
+MU=55U=75U=74UM5755155555U=75U-75U=55U=75U=75U=75U=755=34U=55
+M555555755=75U=755=75U=755=75U-35U=75U=7555755=75U=75U=55U=36
+MU%?5U=35U=55U5555=75U=75U=75U=74U-74U-355=34U-34U-34U=75U=75
+MU=75U=75U=75U=75U=35U-35U=35U-34U-75U-75U-75U-;45%55U=74U-75
+MU575U-36U]355%75U-?4U5545%35U]7555145%75U=75U=75U=5555555575
+MU575U=555555U=75U=75U-35U=75U-34UM575574U-34U=74U-74U=75U=75
+M5=74U-7555755U355=35U=15U=75U-?4U575U=34U-35U=75U-7555145=75
+MU-75U=74U-35U=75U-36U%=45-75U=75U=5?969S<T-%5]'91STC)3,/$\J8
+M@+"TBK2WL*6ELXJVO+#B#0SMO;B&-20["6X;"C0:^60P)B$WAK""!C`T%N1E
+M!6B-O[W@,3\'M+N]G11SG)]H#`6#N+B%-SDTE[2.\!?VA94"/3'QO;N-#C\W
+M?H218&":M(,'/C\0L+NT$30/<>IZ!!6#O;%L/SL-C[RT7082Y?X$-`"&OK^5
+M,#LUD+2,P17GAY4",C>5OKN#"3PUTX>087Z'M(``/CQNL[NU'S0,2.EB!VB-
+MO;<0/CL`B+RU?0$0Y_<&-`:#N;SD,S@+GK2-56KMA^\//32=N;B&"CP*]H:7
+M8G:&M(<-.3USLKB)!30"7>]I!V..O+09.3@$M;R+8`$6X<$!-`6,N+W2/3@.
+MA;2#0FOHA^<)/36:N+B;-3P)Y(:5;$&`M(4..3+'O;B,!S<#V>T5!WB(O(H'
+M.#D>M+R.:0$5X]0#-!F)N+)^/#D"A[2!<VF4A_`+/0B&N+F3-ST/[(;N;E2"
+MM)X+.#/AO+F#`#0!R.,6!TZ*O(D`.#X1M[R,%P9I[48"-1**N[!H/SX!@;2&
+M>FR6A]TU/0^"N[[K-CT-E8?C:<:,M)`U.#&4O[F'`C0'\N80!].TO(\,.SQL
+MMKV#$`9B['$,-1>TN[$3/SX$@[2$9F.0ATPT/0*/NKS[,3T`EH?D:/"/M.HW
+M.#:<OKZ;##0$^^42!/&WO(,).SUPL;V&'`9G[WH/-6RWN[8;/C\8C;2;8F:=
+MA'LW,@&(NKU5,#(&DX?]:.6.M.$V.#2%N;^=#C0;Y_X<!.>VO88U.C+;L+*$
+M&0=Y[V`."GZQN[0'.3P3C[2>;WN?A&TV,@2UNK)X,S(%G(3(:..(M?4Q.#6!
+MN+R4"34>X/8>!>FQO84T.C/GL[*8&P1W[VD("E*PNXH#.3T5B;62:7.9A14Q
+M,AFWI;!H,S,9GH7?:>F+M44P.0B"N+W@"S42XL(8&I>QO9PV.C'JLK.=!01&
+M[!0+"/&SN(D/.#)AB+61:DF;FA`Q,Q"VI;$0,C`2FYI2;I2*BV4S.0^.N[+T
+M"C41[-,:&)*PLI<Q.C:2LK"7!`57[1$*">.RN(T(.#!TBHJ5%5^:F1\P,&BP
+MI;<9/3$6A9M";Y"UB&@R.0.+N[!;-0H4[EX%'IFSL^,S.C28O;'N!QO;XQ(*
+M#I2]N(`U.#'9M8OO%=V%GQHS,'JSNK0$/39HA9EV;9VUCA$]/@:UN[%E-0AI
+M[D@$'86RL\LR.S6'O;;G!AG(X1XU#)*\N80T.#;XM8CF%,Z$D@0S,5.]NHL!
+M/3=FA)]_8)FTC!X\/QJWN[9N-`E@Z7T$$X>RL$X].PB`O+?U!A_PY!LU`IB_
+MOIXV.#?LM([_%/*$D0$R-OR\NHX-/#1PAYUE9YJTC04_/QVVN[06-`YEZ64'
+M%H"]L6`\.`R-O+16!AWY^04U`(2^OY8Q.#67M(_)%>6'E`,R-^^_NX(./#5?
+MAY!F>X2T@`8^/!6PN[4<-`QQ[F`':H*]MA<_.`..O+5V`1/D\`<U!H"YO.TS
+M.`N2M(W3%>"'Z`TR-)&^NX$(/`K"AY9C<X:TA@,^/6>SNX@;-`U![V\';(^\
+MM!P^.0>+O(MD`1'FS@$T!(VYO?0R.0Z9M()::N^'X`XR-9ZYN(4U/`C^AI1M
+M28&TA`PY,EBRN(\$-P/4[&H'9XF\M04Y/ANUO(EO`13@W``T&XZXLDP].0R%
+MM(!T:.J'^0@]"X2XN9PT/0[@ANAO7H.TF`DY,_*\N((!-P#'XA<'<(N\BP8X
+M/A*TO(\5`6KB70(U'(NXLV,\/@.&M(9^;Y>'S`H]#H&[OI<W/0SIA^UNW8VT
+MG0HY,.^_N8$#-`;WX!$'7K6\C@(X/Q6WO8(1!F_M2`TU$;6[L!<_/@:`M(=D
+M;9&'4C4R#(V[O^,V/0.4A^9IR(RTES0X-I&^OH4--`?_YQ,$P+2\C0\[/6:V
+MO8`2!F#L?`PU:[2[MA\^/P6"M(5@8)*'<30R`XZZO/4Q,@&1A_AH_XZT[S<X
+M-YF^OYX/-`7E^QT$_K>\@`L[,D"QLH<?!F7O9PXU8;:[MP4^/!^,M)AM9)R$
+M9C<R!HJZO4\P,@>2A/%HYXFU^#8X-(2YOY`.-!CF\A\%X[:]AS4[,_2PLH48
+M!WWO;0D*=+&[M0$Y/1&.M9QN?IZ%:38R!;2ZLV$S,QJ?A,-H[(BUV3`Y"H"X
+MO.@(-1_C]1D%ZK&]F#<Z,..SLYX:!$KL:P@+W["[BP(Y,FF)M9-H<9B%%S$S
+M'+>EL!0R,!^9A=%IZHJ*=S,Y"8VXLN4+-1/MQ1L;D;"RDS8Z,9>RLY,%!5CM
+M%PL(^;*XCPXX,V6+M9=J0YN;$C$S%K&EMATR,!.;FEYOE[6+8#(Y#8F[L\0U
+M"A;OU1H9G;.RZC`Z-YRRL+.BI+*.DW4?``DU,#T_/CX^/#(Q-P@-!1-YS96=
+MA(",CHB+M8JUBXN)CXV#AH69DI3L^LY==F1B:!47$1`3$Q,3$Q`1%A05:&]B
+M861Y<W5#6%'6WL3"SLKT]_?W]_?T]<K)S\+!Q]K>W-/6U%1645-375U<7%]?
+M7UQ?7%U=4E-045975%555=74U-?7UM;6T=;1T=;6UM;6UM?7U]?4U-34U=74
+MU=75U-35U=75U=75U=75U=34U-74U-34U=34U-34U-34U-34U=75U=35U-34
+MU-34U=35U=75U=74U-34U-35U=75U=75U=34U-75U=75U=75U=74U=74U=75
+MU=75U=35U-34U=74U=75U=75U=74U=35U-35U=75U=75U=75U=75U=75U=75
+MU=35U=75U=75U=75U=74U-35U-75U575U575U=75U=755555U575U=75U=75
+MU=75U=75U=75U=75U=75U=75U=75U=75U=75U=555=55U=75U=75U=555555
+MU=75U=75U=75U=755=75U=75U=75U=755555U=55U=75U=75U=75U=75U=74
+MU-34U-35U=75U-75U-35U-75U=75U=75U=75U=75U=75U=75U=75U=75U=75
+M5555U555U=75U=75U555U=75U=75U-75U=755=55U=75U=75U57555555555
+MU=75U=75U=75U=75U=75U-35U=75U=75U=75U=75U=75U55555555575U=75
+MU5755575U=75U=75U=75U=75U=75U=75U=75U=755=55U=75U=75U=75U575
+MU=75U=75U=75U=7:ZY'@X?3$7DAP>7MD9&=E9'AX?7-T2T%%4E;1TMO%P\+.
+MR<C(R,G(SL_-PL#&Q-O9W]W0T=#545%045-24E)24E)=4E)24U-04%%64597
+M5%155=755=74U-?7U]?7U]?7U-?4U-34U=755555U555555555555%145514
+M5515U=75U=74UM165%15U=75U=75U=75U=75U=55557555555=755575U-75
+M55755=555555U=75U=75U=75U=75U=74U-75U=5555555555555555755=76
+MU5945%75U=75U=75U=35U=75U=75U575U=555=55U514U575U=75U=75U=75
+MU=75U57555555=755575U=74U-34U=74U-75U=75U=75U-'45U1455755=75
+MU=75U=75U=35U=35U=55U575U=75U-34U=555555U575U=75U=75U=75U=75
+MU=35U-75U575U=74U=75555555555=75UM1655155=75U=75U-74U-75U-75
+MU=75U=75U=75U=545=75U-75U=75U=75U=75U=75U=75U=755=555575U=75
+MU=74U=34U-75U=74U=36U5945%555=75U=75U=75U=755=75U=75U575U555
+M5574U%75555555555=75U=75U=75U=755=75U=35U=75U=555=75U=55U=55
+M55555==55E55U=74U-34U-35U-74U=75U=75U=75U575U=555U355=75U=75
+MU=755=75U=75U=35U=75U=75U=75U=75U=75U=75U=75U=555575U-555=75
+MU=74U-75U=75U=75U=74U=34U-35U=75U=74U]35U=75U=75U=75U=75U=75
+MU-75U=75U-75U-35U=75U=75U=75U=75U=75U=34U%75U=75U-34U-35U=75
+MU=75U575U=755=755575U554U=75U-35U-35U=75U=75U=75U=75U=75U=75
+MU=75U575U=75U=74U-74U-34U-34555555555=75U=75U=75U=75U=75U=75
+MU=75U=75U=?4U=75U=75U=75U=75U=75U=75U-75U=75U555555555755555
+M557555555555U=55U=75U=35U=35U=75U=35U=75U=75U=75U=75U-555=74
+MU-34U-35U=75U=75555555555575U555557555555575U=75U=75U=35U=34
+MU5755=55U=75U=55U=75U-75U-35U575U=75U=755=74U%75555555155555
+M5575U575U=75U=74U=35U=74U-75U=75555555555=555=555%55U=75U=75
+MU575U=75U=75U=75U=75U-75U=755%74U=75U=74U=35U=75U=75U5555555
+M55555%555=75U=75U=755=75U=75U=75U-55U5555575U=75U=755=75U=75
+MU=75U5555575U575U-35U=75U=75U5555555U=75U=74U=755575U=555555
+M5575U=75U=355555U=74U575U=75U=75U=75U=75U5755=75U=75U=35U=75
+M55145575U=75U-75U=75U=75U=55U=75U=75U=75U=75U=75U575U575U=75
+MU=75U=355%5555555555U=75U=75U=75U575U=75U=75U=75U=37U=555515
+M5575U=75U=35U=75U575U575U=75U=75U=35U5555575U=75U=74U-55U=74
+M5=75U=75U=75U=34U-34U-35U=75U=75U=555-75U=75U-34U-34U-35U-55
+MU55555755=755575U=74U=75U=75U=75U=75U-37U%755=75U=35U=75U=75
+MU=75U=75U-74U-35U=75U=74U]34U=75U=755555U=75U-74U-34U-35U=34
+MU=555=75U=755=74U=755=75U=35U=75U-75U-75U-35U-75U=75U=755555
+MU=75U=75U575U-34U-34U-35U=35U=75U=75U=75U=75U=75U575U=75U=75
+MU=75U=75U=55U-5555555=55U=75U-74U-35U=75U=75U=74U=75U575U-?4
+MU=755575U=75U=34U=75U=74U5555=74U=34U-35U=75U=55U=75U575U=74
+MU5555=75U5755555U=75U=74U=74U=74U=75U=75U554U=35U-34U-34U-35
+M55555555U=55U=755555U575U575U=755575U=74U=75U=555555U=74U-34
+MU-75U=75U575U=75U=35U-75U=75U-37U-75U=75U=75U=75U=75U=75U5-Y
+M87)S3$10UM_6-R(D,@D3T)^&L;>*M+:SO+:TN:"F@`P(T+2TRQMO[QXY(#IL
+MCI$U/C$##ST_!;2RE#$^#Y292GB"O[!I/3#NO;*'QIN(GP\R![>EL&`(&^10
+M`@_NO+Z6,#X"DII_%)NVC`TZ//2PMN`?U)C6-#T$MKJT&S<!_,`&`9R_O_HR
+M/@2&@O%AFK>!"3LPD[RQ_P5DD78T,Q&PNH@#,`/X^!H%FKRS9S\^'HR.YF"?
+MBI\U.S>&OK%=`!3O>34V<+VZ@@@R`^SK$!*'O;8=.3]JM;7B;Y&,Z#0Y"X^X
+MMF,/'^5Y"#7ZO+B%-SP`D9UL%8:RM08[/$:VM.(7XH'Q-S\#M+NW$S4%\G$,
+M#Y:_OI4P/P:8A'1BA[&,##LR[;.VY!S#F$`W/1BQNK4$-P;]4`$!F;^\TCT_
+M!8&#QV:$MX<*.S&>O+;#!7Z1>S0P:+.ZCPTP`.?S!1N$O[-K/C\3CX_^89B*
+MDS0X-("^MG$`%>YF-3=7O;N!"C(`Z>X2$(:]MQLY/&"UBN=ODXSC-SD.B;BW
+M:P\=YV8+"NR_N)\V/0:0DFYH@;*+`SL]QK:TY!?N@=XV/P:WN[0?-1K^?@\,
+MG;Z^XS,\!)J%<F&!L(().S"5L[?Q'/>8<C8R';"ZB`8W!_A'``:%OKUW/#\>
+M@(#69(>WA34[-H6\MU`%<9%A-S!GLKJ-#S`&X?0%&8:_L!$^/!2.C_=GFHJ7
+M-S@U@KZW9`!IZ6TT-_:\NX0U,P;J[1T6@+RT!#@]?+6+_VV=C/@V.0V*N+07
+M#Q/G8@L+E+ZYD3$]!Y*0:V^#LHD-.S+YL;7S%.J!138_!+:[BALU&/MG#PV9
+MOK_S,CP:A9IX9("P@`L[,9"RM,0=_)AG-C(6L[J.`#<%Y74#!X>^LF$\/!*`
+M@5YXAK>9-#LWA[RW2P5/D6\W,7>]NX`),0?CP`0?@+^Q'SD];HZ,P&6%BN@V
+M.`B/N;1M`&WI:S0TYK^XF#0S!)3@'!2"O+4!.#);M(CT8I^,R#$^`[6XM1,/
+M$>9I"@B3OK[J,3(%G9$58X*RCPX[,^*QM<P4E(9S,3P8L;N(!0H?Y6`.`X6Y
+MO-0R/1Z$F&1X@["&-3LVG[*T41WEF&TQ,FBRNXT"-QKG<@,$@;FS:C\]%H.&
+M0WV!MYTW.S6!O+1_!5Z1%38QU[R[AP@Q!>W=!!V"OK8:.3UFB8W2>(2*XS$X
+M#HFYM6L`8.D4-#3IOKB=-S,%E^<?:HR\B@,X,\>TB<1@F8S5,#X&M+B*'`\7
+MYA4U#I^YON$P,AB<EQ1FC+*-"#LPZK&+TA66AF0P/!VPNXX'"AWE;`D`A[F]
+M<3T]$H2987*-L(0T.S>;LK5,$N:9:S$S9K*[@PTW&.9[`@6#N;`1/SUJ@H=T
+M=H.WD38["H._M6<%U)$7-C;QO[B%"C$;[U$'$XR^MP<Y,G.(@E!\AXKY,#@-
+MB[F*%`!DZ1$W-9>YN98W,!B6^AYNC[R(##LS_[2/UV:;C$@P/@2WN(@9#Q7A
+M%S4/FKF_\3,R'YZ4%WJ/LH,*.S&1L8A<:I"&;3`]$;"[C`8*$^1K"0&!N+)A
+M/3(1AY]C=(RPFS<[-(2RBG`2XYD7,#-VO;N!#S<?X6<"&(VYL1P^,FV"AW!/
+M@K>5,3L(C;^*8AK;D1`Q-N:_N)DU,1GN1P<6CKZT`3@S6(B#1'&!B\XP.0.U
+MN8@1`7GN$S<*DKF^Z#8P'Y;\&6*)O(X..S#@MXQ>9)J->#,_&[:XCAL,:.81
+M-0R$N+S4,S,2GNL6<HFR@34[-IVQB4]HDX=J,SUKL[B"``L1Y!4(!H.XLFL\
+M,Q6'G&U`C["?-CLUAK*(>Q/OGA`P,%6\N(0.-!WA8PT>C[BQ&SXS9(V$?T6-
+MM^TP.`Z/OXAI&LZ6'3$WZ;ZYDC4V'.E*!A2)OK4#.##%BX!)2H&+43,Y`;2^
+MB1(!<.\<-@N9N+[A,3`=D?<99HB\C`@[,>FWC4UZA8U@,C\<L;B,!0QOYA(U
+M#8:XO'<R,Q&9Z1%*B+*'-#LWF;".<6Z=AQ8R,F"RN(`#"Q?G%P@'C;BS$3PS
+M;H:2;%.)L9`Q.PJ`LHEF$.B?'3,P][^XF@DT$^!O#1R)N+8$/C!QC85[5HRW
+MY3,X#(F_CA4;\9<>,326N;F6-#83Z',&:XN^B@PX,/.+@79&@(AU,CD'M[Z/
+M'`%*[!XV"(6XO_`Q,1"0S1AXBKR""CLVE[>"=WZ$@F@R/Q"PN8($#&/F'#0"
+M@[N]9S(P%)CM$5J+LH4W.S2:L(Q^;)R'$S(R?;VXAP((%><0";6IH+NQ@^YC
+M&@(+,3,_/SD^/STS-C4.`1EI0>&1FX:"C(F(BHJ*BHN)CHR"@8>;G)'KX?#2
+M3'YA;VH4%A$0$Q,3$Q`1%A<5:VYM8&=[?79.1%+5W=K!S,C*]/?W]_?T]/7(
+MSLW`Q]K8W-+1U]545E%34EU<7%]?7UQ<75U24E-04%%65U15U=34U-?7UM;6
+MUM;6T='1T=;6UM;7U]?7U]37U-34U-34U=75U=75U=75U=75U=75U=75U=74
+MU-34U-35U-74U-34U-34U-34U-75U=34U-34U-74U-35U=75U=35U-34U-35
+MU-74U-34U-34U-34U-35U=75U=34U-34U-35U=75U=75U=74U=75U=75U=75
+MU=74U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=755=75U=75U=75
+MU=75U=755=75U=75U=75U=555=75U=75U=75U=75U=75U=75U=75U=75U=75
+MU=75U=75U=75U=5555555575U=75U=75U575U=75U=74U=74U=75U=75U=75
+MU=74U=75U=75U=75U=34U=75U=75U=75U=75U=75U=75555555755=75U555
+MU55555555575U=75U=7555755575U=75U=75U=75U=75U=74U=75U=75U=75
+MU=75U=75U=75555555755=75U=75U=55U575U=75U=75U=755=755=55U=75
+MU=75U5555555U575U=75U=755=75U=75U=75U=75U=75U=75U=74U-75U=75
+MU=75U=75U=75U=7555555555U=75U=75U=55U=75U=7#E);AYLG:6DAS>7ID
+M9&=E97YY?'-T2T9:4U?0W=O$P\+.SLO(R\C+R<[,PL#&Q=O9W-/3T-=55%91
+M4%-24EU=75U=4E)34U!045%65E=45%155=75U=34U-?4U-?4U-?7T-=4U575
+MU=75U=75555555155514551455155=555=755%=5U=75U=75U=75U=75U=75
+MU575555555555555U=75U=75U=75U=55U575U=36U5945%555=75U=74U=34
+MU=75U=75U=75U55555555555U=?5555555755=555=75U=75U=75U=75U=55
+MU=55551555555555U555U=75U=;55U54U575U-74U=75U=74U=75U=75U=35
+MU-34U=75U554U575U-34U-35U=75U=75U=75U=75U=75U5555555U575U=74
+MU-75U-75U575U=55U=75U=74U=755=75U=75U555U575U-75U-75U=755=74
+MU-755=5555555555U575U575U=75U-75U-35U-75U=75U=75U=75U=755=74
+MU5755=75U=755=75U=75U=35U=75U=75U555U555U5555535U=75U=75U=75
+MU=75U=5555555555U555U574U=74U=74U=75U=75U=34U=?45=55U=75U=75
+MU=75U=75U=75U575U=75U=75U=75U575U]75U57555555555U=55U5555=75
+M5=75U=34U=35U=75U=75U=55U5555=75U-55U555U=75U=75U=75U=75U=75
+MU=74U=75U-75U=755515U=75U=75U=75U-34U-34U-34U=75U=75U-75U=35
+MU=755=75U=55U=75U=74U5555=55U=75U=75U=75U-75U=75U=75U=75U=74
+MU=35U-?4U=75U555U=55U=75U-75U=75U=75U=35U-34U-74U-35U=75U=75
+MU=75U=355=75U-74U-74U-75U=74U=75U=74U=75U=35U-7555545=55U=75
+MU-34U=75U=35U-75U-35U-75U=75U=75U-74U=75U=75U555U=75U-75U=75
+MU=75U=75U=75U=75U-34U=74U=35U=75U=755574U-5555555=555=555555
+MU575U=75U-74U=75U=75U=75U-35U=75U=755=74U5755=74U-34U=75U=55
+MU=755=75U=35U=55U55555555%55U=75U=34U-75U=75U=75U=75U=75U=75
+M5=75U=74U=75U=75U=75U=75U=35U=55U=755=7555555=75U=74U-75U=75
+MU=55U=75U575U]35U575555555155575U=75U=75U575U=75U-75U=75U=35
+MU=755=755=75U-75U=75U=75U-75U=75U55555555555557555555575U514
+M5575U=74U-74U-35U=34U=755=75U=755=75U=75U=75U=75U=75U-35U=34
+MU5555%55555555755555U555U=75U=75U-75U=75U=75U=?4U5555%155555
+M5575U=75U=34U-75U=555=555%555555555555555575U=755555U555U=75
+MU=75U=75U=75U=74U=75U575555555545-75U=34U-35U=35U=35U=755=75
+MU=75U=75U=75U=75U=75U=55U555U575U-55U=75U=75U=75U=75U=75U=75
+MU=755=75U=555=755=74U-75U=55U=555=55U=75U=75U=75U=75U=34U=75
+MU=75U=75U=75U575U=75U5755=75U=75U=35U=75U=74U=75U=75U=75U-74
+MU=355=75U-34U-34U-34U=75U-35U=75U55555555575U=74U=75U-34U-34
+MU=75U-34U=34U-35U=75U=75U=75U=34U=75U=75U=75U=74U=74U]?5U=75
+MU=75U=75U=75U=34U-34U-74U=75U575U555U=75U5755=555=75UM575%35
+MU=35U=75U-75U=75U-75U=35U-75U=35U=75U555U=74U-34U-75U=74U=75
+MU=75U=75U=7555555=75U=75U=74U=755575U=75U555U555U=55U=75U=35
+MU=75U=75U=74U-35U-75U-75U=34U=5555555=75U=74U=34U=34U-34U=75
+MU=35U=555555U=55U=555=55U=555%55U=75U-75U=75U575U=75U=755=75
+MU=755=75U=545=74U=34U-35U=75U=75U=75U=75U=75U=75U=75U=74U=75
+MU=75U=55U575U555U=75U575U=75U=75U=35U=75U=75U=74U=75U=555=75
+MU-7555755=75U=555575U=75U=75U=75U=35U555U575U=75U=75U555U=74
+MU575U=74U=35U=7555555=75U=35U=34U-34U-35U=7555=4U=74U=34U-74
+MU-35U-35U=75U=75U=75U555U5555575U=75U=75U=75U=355=755=75U=75
+MU=75U=75U=555=75U=75U=75U-35U=75U-?4U=75555555555=55U=55U555
+M5575U=75U=75U=75U=34U=75U=755575U-555=75U=35U=35U=75U=75U=35
+MU=75U=75U-75U575U554U=34U-34U-34U=34U=35U=75U575555555555=75
+MU=75U=75U=75U=74U-74U5755=75U=55U=55U=75U-74U=75U=75U=74U=75
+MU=755=37U=75U575U=75U=75U-755=75U-35U=75U=75U=74U=75U5755=55
+MU=75U=35U=55U-75U=75U=75U575U-75U=75U=75U=75U=74U-355=34U=75
+MU=75U=155555U55555555=75U575U=75U=35U=75U=74U-35U575U-35U=75
+M5555U=75U5555=75U=75U=75U=75U=34U=345=75U]75U=75U=75U=75U=35
+M6F=G<'!!1531V'P[(#@P#!;SGH"TM;>WL+*]M[>]IZ>W&S4;B;NQ'3@E-VAX
+M`#0*&@4V)24(@;:=,2<Y8[>QGQH$3)WM:FV&O;^:-3T&MZ2Y[C4T:8"-E7SF
+M@8<6-C%CL+N)"3H]0;>VE@<!8^5B``>6L;#G,SL+C[NR6#8V$YN$]V/B@H80
+M,3!FL[J+##@SW+>VD!L;1.-I`@:3L[/@,SH*C+BS1C<W$)R<0V_JCX`=,S-B
+ML[J*##@ST;2TEQX2].T6#@"=LK+B,SH*C;FS6C0U%Y.79&^0B(,<,C)LL[J*
+M#3DP5+6UZQT5X.P="P.<O;WL,SH*@KFP6S4+:I'B:&Z?M8T?/3UNL[J*`CXQ
+M4XN(XA!FE>X8-0V?O+SN,SH*@[ZQ6@L.;93W$6F:MX\9/#QKL[J*`S\V78F,
+MY1=&DN@%-P^>O[_K,SH*@+^V6@X-9.A''FN$MHD9/SP5L[J*`#PW7(R#]VKT
+MF^H'-@Z9O[^5,SL+@;RW10P`<>UG!16&L(L8/S\4L[J+`#TT7(*'TF_BAY0!
+M,0B9OKZ4,SL+AK*T1P($7^%J!A2`LXH8/C\7L[N+`3(U4X&;0F"7@)<#,PN9
+MN;Z4,SL+A[.U0P$9P/H3`Q>"LK48/CX6L+N(!C`*5H>2?F6<C9$-,C68N;Z7
+M,#@(A+"(3`<0^?(9#!:,O+08.3X1L+B(!S$)U)KJ8'*:B9`//369N;F6,#D)
+MA;:.=1INX\H$"1".O[08.3X0L;B)!#8/W9_E:TJ&BI(./#29N;F1,3X.F+>-
+M=A]]E<0!"A*)OK<8.3X0L;F.!30"VI#2%D2#M)P(/S>9N+F0,3X/GK6`<Q#?
+MD=`"-1V(N;89.3X3MKZ/&S4!PI1\$E&,MYX+/C>>N+F0-C\,G(N'?Q7DG%`/
+M-Q^+N+89.3X2M[^-&0L$].UN&=*)L9@*.3:?N;F0-SP-DHZ;>FV5FUL)-AF*
+MN+$>.3X2M+V"'@\8\OH1!<2+L)LU.#:<N;Z3-S("DXV=9WB2A$,*,1NUN[$?
+M.3\2M+*`'`(3^,D>!\VULX4U.#&=N;Z0-#,`D8"48$.:@4@U,`6UNK$</C\2
+MM;.!$@$5YU,$`<NTLH0U.S&2OK^0-3`&EX?@;=B&@W4W,@2UNK`=/CP2B[&'
+M$`1GXW8!`O2VO8<U.S&3OK^0"C$'E)CU:?V#C'<V/0>UNK`2/SP2B;:%$1E!
+M[F0-#/>QO(8T.S&1O[R0"#<%ZI)!%>&/CG8Q/`>UNK`3/ST2C[29%Q/TZFP.
+M#O:QOX8T.C"6O+V1#C09Z)1G%^Z(BW8Q/`:UNK`1/#(3C+6=%!7BEQ4+"/:P
+MOH$T.C"4O+V6#`H=[N,5$92UM78P/P&UNK`6/3,0@HB6:F:7D!8U"O>SN8`U
+M.C&5O;.7#0X6[/<2$Y&WM'0S/@"UNK`7,C,1@(SH:$^?G1,W-?6RN(,U.S'K
+MLK"4`PUHXE\8')*VMW4R.0.UNK`4,S$6AH/F;L*%GAPV-,ZRN(,U.S'NL['J
+M`0!FX'@''I^QMDXR.0.UNK$5,#84A(?W;.>!FQDQ-\"]NX(U.S'ML;;I!P1T
+MX6\`&YBSL4`].`**N[%K,3<5FIM18NN"A1LP-MN]NX(*.#;CMK?M!1G<X1<-
+M!9JRL%H].`V+N+%I-C1IF9-R8Y&.AP4R,=*]NXT+.#;FM[7A&!#^YAT.!X6]
+MLUT].`V(N+9O-PIMG>IC8)^+A@0],%6]NXT+.3?DM(OZ'&[LYA@+!H2\LE0]
+M.PV)N;=L-`EFD.458)JU@`<\,UR]NHT(/C?ZBH[S$WR7X00U`(>_LM(].PV.
+MOK=B"@QXE-P38(>W@@<_,T2]NHT)/S3XB(+"%]*=X08W`X:^O=H].`R/O[1C
+M"`-WZ'(>8(&QC08_,DZ]NXT./#7_CX'7:^6:X`,V`H:YO,(].`R,O+5A#P9>
+MXFP%8X*PCP$^/7>]NXT,/0K]@H5`8NN'X@TP#(:YO/4].`V"O8MF`AK8Y!8&
+M8HRSC@$Y/7*]NXT-,@CS@)]R9).`[`\S#X&XO/$R.`V#LXEF`!WQ\A\#;(Z]
+MB0$Y/'ZRN(T",P[PAY9G<IB-[PDR"8:XO_TR.0V`L(]G!Q3DS`4,;XB\BP$X
+M/&6RN((#,0SPFNUM38>.Z`@]"(:XO_DR/@*&MHUG&F#LT`8.:8J_B@$X/&>S
+MN8(`-@+SG_%J5H"+Z@H\"X:[O^4S/@*'MX!G'TR56`,+:K6^M0$X//VNK*>\
+MBIO%$`8.-3`R/C\Y/S\R,S<U`@<08,KOG(6`C8Z(BHJUBHJ(B8^-@(:%F9*7
+M[^7)4'=E8FD5%Q$0$Q,3$Q,0$184%6AO8F%D>7-T0EA1UM[$PL[*]/3W]_?W
+M]/7*R<_"P<?;V=S3T=155U%04U)=7%]?7U]<7%U=4E-04%%65U=45=75U-?7
+MU];6UM;6UM;6UM'6UM;6U]?7U]34U-34U-34U-35U=75U=75U=74U=35U=75
+MU=75U=34U-34U-34U-34U-34U-34U-34U-35U=35U-74U-34U=75U=75U=75
+MU-34U-34U-75U=75U=74U-74U=34U=74U=75U-34U-34U-75U=75U=34U-34
+MU-75U=75U=75U=35U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75
+MU=75U=75U=75U=75U=75U=75U=75U=555=55U=55U=75U=75U=75U=75U=75
+MU=75U=75U=75U=75U=75U=75U575U=75U=35U=35U=75U=75U=75U=75U=75
+M5=55U=75U=75U=75U=75U=75U=74U=75U=75U=75U=75U=75U=75U=55U=75
+MU=74U=75U=755=75U=75U=75U=755=755=75U=75U=75U=75U=75U=75U-75
+MU=75U5555575U=75U=75U55555555=75U=75U=75U575U=75U=75U=75U=55
+MU=75U=75U=75U55555555575U=75U=75U575U=75U=75U=75U=75U=75U=75
+MU=75U=755555U575U=75U=75U575U=75U=75U=75U=75U=75U=75U=75U=75
+MU=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U/*1E>;EQM]-2WQY
+M965D9&5E>'YS=W5*05I35-/?VL?"S,G)R\O*R,C.S\W#P<?%V][?TM#6U%57
+M5E934E)=75Q=75U24U)34%!145%75U=55574U=34U-34U-34U-34U-34U=36
+MU%=55=75U=74U55555545%1555545=5555555555555555545-55U=75U-35
+MU-75U=75555555555=75555555555=75U=75U-75U=55U-;55E54U555U=75
+MU=75U=555555U55555555=75U=55U=555=37U=75U=755=755=75U=74U=35
+MU-34U-35U=55U=55555455555555U=75UM565515U=74U-35U=35U=75U=74
+MU=35U=75U=75U=75U=755=75U=34U-34U=75U=75U=755=75U=75U=75U575
+MU=75U=55U=75U=55U=71U%945%55U=74U=74U=34U=74U-75U=75U=75U=75
+MU=755575U-75U=755=7555555555U=55U=75U=75U-74U=75U575U575U=75
+MU=75U-;45U145575U=75U=75U=755=75U=75U=34U=74U-75U=755515U=75
+MU=75U=35U=55555555555575U575U575U=74U-74U-34U=74U=74T=145535
+M5=75U=75U=75U=55U=75U=75U=75U=75U575U=35U=?4U=75U555U5555515
+MU=75U=75U=755=75U=75U=755575U=75U=75U=36U%=45=35U=75U=355=75
+MU=34U=55U=75U=55U=755575U-55U=75U-34U=34U-34U-74U-75U5555=75
+MU=555=755=75U=75U=75U=55U=;55U54U575U=75U=35U=75U-75U=74U=35
+MU=75U=75U=35U=77U]74U=55U=5555555=75U=75U=34U=34U=34U-75U=75
+MU-75U=75U-75U-35U574U=74U-34U-75U=75U=75U=75U=75U=75U=75U=34
+M5575U=34U=75U=75U=75U=75U=75U=75U=555%75U=75U=75U-75U=75U=71
+MU%=55555U555U=75U=75U=74U=75U=75U=75U=75U=75U=75U]1555555=55
+M5=755=75U=5555555555U=75U-35U-75U=35U-75U=75U=;45U55U5755=75
+MU=75U=75U=5555555=75U=75U=35U=34U%75U=75U=75U=75U=75U-75U=35
+MU=75U=75U=74U-75U-75U575U575U574UM565%355=75U=75U=755=74U=75
+MU=75U=75U=75U=55U=55U=34U=55U=75U=755575U=74U=75U=75U=75U-75
+MU=75U=755=75U=75U=31U%=55-55U=35U-35U=75U=75U=75U=75U=55U555
+MU555U5545=75U-74U-755555U=75U=75U55555555%155%55U=75U575U=75
+MU=75U-;55E145555U=55U=75U575U=75U=75U=35U=55U55555755574U%55
+M5%555%555%755=55U=75U=75U=75U=35U=35U=755=755=75U=75UM165555
+MU=75U=55U575U=75U=74U575U=75U575U=555=755515U=75U=75U=34U=35
+MU=755=75U=75U=55U575U575U=75U=75U=75U=76U5=45%555=75U=75U=35
+MU-75U=75U=74U=75U=755=75U=74U]?5U55555555=75U=75U=75U=75U=75
+MU=75U=35U=75U=75U=75U=75U-;45U55U575U=74U=35U-34U=74U-74U=74
+MU=75U=34U=34U-54U=74U-34U-35U-35U=75U=75U=75U555U5555575U=55
+M5575U=75U574UM175535U=75U=74U-34U-35U-75U=75U=75U=75U-74U-35
+MU=34U=75U=75U-75U=35U-34U-34U=75U=75U=75U5555575U=75U=75U=36
+MU5=55-75U=75U=75U=74U-34U-35U=34U=35U=35U=75U=545-75U-34U-34
+MU-75U-75U=75U=75U=74U=55U=75U=75U=74U=75U=75U=;55E145575U=75
+MU=35U-34U-35U-75U=75U575U=55U575U=74U]35U=55555555555575U=74
+MU-77U-34U=34U=74U=75U=75U=75U=75UM5655355=75U-35U-74U-35U=75
+MU-35U=74U=35U-75U=755%35U=74U=74U-75U=75U=75U=75U=75U=75U=75
+MU=34U=75U=75U=75U=36U%=55=555=555555U=75U575U=75U575U=75U=75
+MU=75U575U-355575557555755=75U=35U-35U-35U=35U-35U=75U=75U-34
+MU-74U-'45U55U575U-34U-35U-75U=75U=75U=34U=75U=74U=74U-54U=75
+MU-34U-35U-35U=75U=75U5555=555555U=75U=75U=75U=75U=75U=75U=75
+MU=75U=75U=55U=55U575U=75U=75U=75U=75U=755=37U=5555555=75U=55
+MU=75U-75U-74U-74U=75U=755575U575U=75U=75U5755575U=75U=35U=75
+MU=75U=74U=74U=75U=75U=75U=555-75U=34U-35U=75U=75U=75U=75U575
+MU575U555U=75U=75U=75U=75U=;55E54U=75U=74U=35U-34U=75U=34U=75
+MU=75U=35U=75U=74U]75U=75U575U5555555U=75U=75U=74U=74U=74U=75
+MU=75U=75U=75UM5755355=75U=75U575U=74U-34U-74U-75U=75U=75U=75
+M5535U=74U-34U-34U-74U5755=755=5555555555U=755=75U=75U=755=76
+MU5=45=75U-75U-75U=75U=75U=75U=75U=75U-74U=75U-75U-?4U%555555
+M5555U=555=755=75U=755=555555U=75U4!F9'!Q1EK4T=IL)"`_,0(5Y9F#
+MBK6QL;RRMHNWOJ&FMQ@T#9Z_OY<S)B0(E)\7,3X]"P8""@@?[)$3-C\*F+^Y
+MAPHY-I^XI+<4-0SAM;>`]$;KFY47`1.$L+#J-S@VD;ZX@`H[/6VTLX,?"@UF
+ME.-A%?Z!@LH.,`^;O[Z%-#HR_[R[B`8R-FF"BYMM'7GL^Q,#&9VVMNLT.3:6
+MOKN-"#L]8+>]CQ,*#V"3D5EIPX6`_0TQ#)^]OX4T.CW`O;N+!CTP%X*UA&T;
+M:OCG:P8>EK2WE34^-I6_NX\..SQNMK^($#0+:)V9\VQ$G(?Z`#<,D[*_A#4Z
+M/56]NHH'/#,3C;>`8P02QN-E&ASKBK27"#PV[KR[C@\[/Q6VOHH6-S46GX3L
+M8WB4A><'-`V6L[R'"SL]0KVZM00_,AR,MHUF`1I#[%D=$N..M98./3?@O;N)
+M##L_%K:YM!<V-Q*9@)9F;>>?X!L+`I6QO8<).#UPLKJT!3\\&(RPCGH#!F7I
+M\!40_H**D0TR-^6RNXD-.SX3MKBW%3$V&9B-GG@5WI'C'0X`[+:RA@\Y/7JR
+MI;<:/C\%C[.+<@T";NOB9A;"@8F0`S`T\+.XB`,[/ARVN[9H,3`%FXZ$<1!_
+MZ>T7`@'FM+.&#3XR8;.EMQ@^/P>/LK5(#P\6ZI=;%5V%C)`&,33-L+B+`#@^
+M&;>[L6TP,P>;BX%''&CE[6T!!O^*L($#/S)LL*6V'SX^`8^\MUT)"QV5G?)O
+M=IR#D`4W-="QN8L&.#X;M[JP9C,]`9NUC=`8$][M?`4$R8FQ@0$\,FBQNK8=
+M/CD`C[^VV0LU&Y6;[F9EE(>0&30*6;:^B@<Y.06WNK-[,SP"F[>.RP4;<>)1
+M'!K0C+>!!S(S%;:ZL1`^.`*,O['W"C<$ZH>0?6+FFY$3"@M)M+^*!3XY!+2Z
+MLW<S/PR;MHKY!P=BX_`7'EJ`M(8%,S,7MKJQ%SXX#(R^L_LU,0'K@)A$:-J2
+MEA0."7.UO(H8/SX'M;JR13,^#IBQM.`&`Q?AX&`2=(>*AADP,!:TN[!J/SL/
+MC;FRX#4P`NF"A]H4<Y67;`T/>(B]BA\\/@:UNKW3,CD(F;"WZ`$,'.>50Q9^
+MF(F''38Q$+6XL&P_.PZ"N;WI-#,/[X^#_1%OYY5[`0UGC[.*$ST^`8NZO,@S
+M.`J>L[&7``@:^Y#Q:623C806-S83BKBP83P["(.XO)0T/0GBB8S@$Q'9Z4,%
+M`&&"L(L6,C\`B+J\_S,X-9RRLY,#-0;SG.QG8.N!A6HU-Q*(N;!X/3L+@+B_
+MD30\"N&+B.H='G+MVQ\&8H&VB&HS/`".N[SF,SLTG;VRGP(T`\N;D75MY82;
+M8@@T'8Z_L'0].PJ!N+Z=-#\UY;6UD1P$;N?]%P5LA+>);#$\`XR[O^TS.S>3
+MO;V;`C8,WH29WF[9GYEZ#S4<C;RP6#([-8:XOIXT/C?]M+><'@$0_^%C&6^8
+MM8YG-CT#C;B_ZS`Z-I&\O(0",0E<AH?Y:'&7G70""QR#O;'2,S@UA[BYFS0Y
+M-LNWMIL>`AG)Z4X3;I*(CWTT,@.#N;^4,#HQE+R^A@(P"DJ`@.QK8N&04`8)
+M'(:SL<DP.#6%N+B$-#DQWK>PA!D/!-64]!5IE(R-3C4S`H&^OY$Q.C'KO+F`
+M`C(T?H.,EFH7QI3!&@P<A+"V_38Y-)NYN(<U.#!<MK*!&0@`3I'B9FGC@(-0
+M"#`#A[^_DS8Z,.^\N8("/3=FC8B<:AUR[_,=`QR8MK;E-SXTGKFX@34[,TJV
+MO8(9-0QXDI9,;O.$@=H/-@.%O+R<-SHPX;RXC`(\,6^,M9IK&V[DY10&'9VW
+MM^$T/S2=OKB`"CLR?[&\C!XT"6.?G\YO5)Z']0(W`YB]O)XT.S/[O+N.`SPP
+M%8^WAV@'$/7@8042EK6T[34\-)&_N(,+.SUGL;^)'S<U:YF$X6USEIK\`34`
+MG+.]F#0[,_&\NX@`/S,1CK:`;@$84^Q.'!/HB;7I"#TTE+RXC0@[/6VQOHL=
+M-C06FX&48&/CG_H%"P"0L+V:-3@SQ+VZB@$^,A*.L(UM`@=RZ<D7$>&,B^H/
+M,C7HO;B,#CL\:[&YM1(Q-A*%C9UG%<R0YQX.`92VLH4(.3-6O;JU!CX]'XFS
+MB6`,`V#JX&`7\X".E0TS->VRN(\/.S\4L;BT$#`Q'H6.FG@0=NO@$0T&Z;>S
+MA0X^,TVRNK0'/CP;B;*+9`X/%9643A7=A(R4`#$UY[.YC@TX/Q&QN[<6,S,%
+MA(N&<1QOY^-H`0?@M;"$##\SXZZLI[^UF\81!@XU,#(^/CD_/S(S-S4-!Q!C
+MR>R<A8"-CHF*BK6*BHB)CXV#AH28DI?OY<A1=V5C:147$1`3$Q,3$Q`1%A05
+M:&]B861Y<W1"6%'1WL3"SLOU]/?W]_?T]<K)S\+!Q-O>W-/1U]5745!375Q<
+M7U]?7%Q=75U24U!045975U15U=34U-?7U]?6UM;6UM;1T=;6UM;7U]?7U]?4
+MU-34U-35U=75U=75U=75U=75U=75U=75U=34U-34U-34U-74U-34U-34U-34
+MU-75U-74U-34U-35U-75U=75U=34U-34U-75U=75U=75U-34U-34U=75U=75
+MU=74U-75U=75U=75U=75U=35U-75U=75U=75U=34U-34U-35U=75U=75U-75
+MU=75U=75U=75U=75U=75U=75U=75U=75U-75U=75U=75U=75U=75U=75U555
+M5555U=75U=75U=755=55U575U=75U=75U=75U=75U=75U=75U=75U=75U=75
+MU-75U=75U=75U=75U=75U=75U=555=75U=75U=75U=75U=75U=75U=75U=75
+MU=55U=75U=75U=75U=5555555555U=75U=55555555555=75U575U=555555
+MU=75U=75U=75U=75U=75U=75U=35U=75U=75U=75U=75U=7555755575U=75
+MU=75U555U=75U=75U=75U=75U=75U=75U=75U=75U55555555=55U=555=55
+M5555U=75U=75U=75U=75U=75U-74U=35U=75U=75U=75U=75U=755555U575
+MU=75U=75U=75U=75U=?ZD.CA^\333G5^>61E9V1E>GA_<G9U3$1>4=33WL7&
+MPLS.R,O(R\O+R<S"P\;$VMG?W=/1U]555U914%)34EU24EU24E)34U!14596
+M5E=45-755=75U=75U-34U-34U-34U=35U=75U=75U=55557555555%545U14
+M55555=75U=75U-75U=755=755555555555755=75557555755=75U=555555
+M5=755=755=555=755=75U=75U=75U=74U=75U=75U=74U]35557555555555
+M555555555=755=75U=75U=55U=75U=75U=75557555155-55U=75U=75U=75
+MU=75U=75U=75U=75U=75U=55U=74U575U=34U-34U-35U-35U=75U=755555
+M5555U575U=75U=75U=35U=35U-?55=75U=75U=75U=75U=75U=75U=74U-75
+MU575U575U=555555U=35U575U=75U=75U=75U=75U=75U=74U-75U=34U=75
+MU=5555555=75U-55U575U=75U=35U=75U=755=755=75U=75U=74U=74U=75
+MU=545=75U=75U=35U=75U=75U=75U55555755555U=75U-75U-75U=75U=74
+MU5755=74U-755=755=555=755=75U=75U=55U=35U=75U=755=74U5555555
+M55755=55557555555=75U575U=74U=75U=75U575U=75U=3555555575U=75
+MU=755=75U=75U=755=75U=75U=75U=75U=755575U=34U-34U-74U-74U-74
+MU=75U=7555555555U=55U=75U=74U=75U-555=75U=75U=75U=75U=555=55
+MU=75U=75U57555555575U575U-75U5555=55U=75U=75U=75U=75U=34U=74
+MU=75U=75U=75U=75U=74U-75U=74U=34U-34U-35U=75U=75U575U5555575
+M5=7555755515U=74U=74U-34U=75U=555555U=55U=75U=75U575U=75U=75
+MU-75U=34U=75U-35U=75U=75U-74U=75U=74U-75U=75U575U=75U=75U=34
+MU=75U=755=75U=555=5555555575U=55U=75U=55U55555555=74U-75U=74
+MU-75U=75U=75U575U=75U=75U=35U=755=755=755=545=75U=75U=75U-34
+MU-75U=75U=755=55U=75U=75U=75U=75U=75U-74U535U=75U=35U=755=75
+MU-75U=75U=75U=75U=75U=55U=55U=74U]75U555U575U=75U=75U-75U=75
+MU=75U=7555555555555555755=755%155575U=75U=75U=75U=75U=35U=75
+MU=74U=75U-75U=755515U=74U-74U-75U=75U=75U5555555555555155555
+M5555U=755=75U=54555555555555U=7555555555555555555=55U=75U=75
+MU=75U]35U=755555U555557555755=75U=75U=75U=75U=75U575U=75U=74
+MU5555555U555U=75U-35U=75U=555555U555U=75U=75U=75U5145=75U=74
+MU=74U=75U=7555555575U=75U=75U=75U=75U=75U=75U-'45U545575U=75
+MU=75U=75U=55U=75U=75U=75U=755575U=75U=34U=555555U=75U=75U=35
+MU=75U=75U=75U=35U=75U=75U=75U=75UM5655155=75U=75U-74U-34U-74
+MU=75U575U=75U=555=7555545-75U-34U-35U=55U=75U=75U=75U=55U=55
+MU=75U=75U=74U=75U=76U5945%55U=75U=75U=75U5755=75U=74U=75U=75
+MU=75U=75U=74U]755=55U555U575U=75U=75U-35U-75U-74U-75U-74U-74
+MU=34U-;45U55U=74U-34U-75U-75U=75U=75U=75U=55U=55U=35U=755515
+MU=75U=75U-75U=75U-55U57555555555U5555=75U=75U=75U=75UM565535
+MU=75U=75U=75U=755=755=75U=75U=35U-75U=75U=75U]?4U=75U=75U=75
+MU=74U=74U=75U=75U=75U-74U=75U=75U=75U=36U%=45-75U=75U=34U-35
+MU-74U-35U=555=555=55U=755575U514U=74U-74U-75U=75U=75U=75U=75
+MU575U=75U=75U=75U=75U=75U-;45E54U=75U=75U=75U=55U=75U=75U=75
+MU555U575U=74U=75U=37U555555555555555U555U=75U=755=75U=34U=35
+MU=75U=755575UM565%=5U=75U-75U-34U-35U-75U-35U-755=74U=75U=75
+MU-155=75U-34U-34U-35U=755=75U=75U=75U=555555U=75U-34U-34U-31
+MU%=55=55U=755575U=75U575U555U=75U=75U-34U=35U=75U=74U-75U=75
+MU=75U555U5555=55U=75U=75U=74U=755=75U=75U=75U-;45U54U=75U=75
+MU=34U=34U-35U=755575U=75U=75U=555=75U555U-34U-34U-74U=75U=75
+MU=75557555555555U=7555555=755575UM575%355=555555U=75U=75U=34
+MU-15U=75U=75U=75U=75U575U=?5U575U5555=75U575U=75U=74U=75U=75
+MU=74U=75U=55U=75U=36U%=45%55U=75U-75U=755=75U=75U-35U-75U-75
+MU=35U-75U-54U=35U-34U-74U=75U=75U=75U=75U=75U=75U=74U-75U=74
+MU=74U-'45U55U=75U-75U=75U5555=55U575U=75U=75U-75U=74U-34U=37
+MU-75U=5555555575U=75U=74U=35U=35U=35U-34U-35U=75U=74T=175535
+MU=74U=75U=75U=75U=75U-75U-74U-35U=75U=55U=355=74U-34U-34U-34
+MU=75U=75U=555=755=555555U=75U=75U=75U=76U5955%55U=75U=75U=75
+MU=75U=74U-74U=74U=75U=75U=755=74U-355=75U=755=75U=75U=75U=55
+MU=75U-75U=75U=75U=75U=75U=355-55U=75U=74U-34U-74U=34U=34U-35
+MU=745=75U=75U=74U%55U-34U-35U=75U=75U=55U=755=7555555575U=75
+MU=75U575U575UM565%15U=75U=74U-75U=75U=74U=75U=755=75U=55U=75
+MU=55U-?5U=55U555U=75U=75U=75U=35U-35U=35U-5555755=75U=75U=31
+MU%=55-75U=34U-75U=75U=75U=35U-74U-75U-34U=34U=75U=54U=35U-75
+MU-75U-35U=75U=75U=75U=75U-75U=75U=75U=74U-34U-'75]55U=75U=35
+MU5555=75U=75U=35U=55U=75U=75U=75U=75U=77U-74U=75U=7555555=75
+MU=355555U=34U-35U=75U=35U=75U=34T=175535U=75U=75U=75U=74U-34
+MU=75U575U575U=75U=75U=755%74U-34U-34U=75U=75U=75U57555555555
+M5=55U=75U=555=555=76U5=55-75U=74U-75U=75U-75U=75U=7555555575
+M5=75U=755=74U]35U575U55555754']@?7-.1%/1W-PU(B0R"!)<G(>)B["]
+ML+6WL*:MO<P1E(^!<'2!M<8_(3[!BN`V.S(-#S`V;8Z&"R<Z:[:VX0<7E>(:
+M`)>^I8(U/1^TO8S6_H:9!3<$M:6P!3\V^X^89DZ'AAD\,O"_ON\Q,!R=ZQ5R
+M@K?I,3@"M+N*!#0%X_`8$(&]M0`[,I2\L/0#'_E0`P_BLK_A/3@$B[&>$F^7
+MY0TQ&K2ZM0\Y-)>+FF=;AX4%/3:<N+_',#%JA)!B7(VUQC,Y&K"[C`(W&N_\
+M'Q6#LHP).C&%O[9_#QCR60`#D+VR<C\Y$+>PDQD5Z,H--Q>QNXPU.`J8M9IB
+M3X6<!C(UAKN];STQ>H&=9M&"B&<R/VJ]N(<(-AF4YQ-AC;"'-#LT@KFW%`@:
+M_%P&!)N\L1`Y/WBQL.L$%N/6##5RL[B$-C@/@;>;:W.9EP,S#X^ZL!D\-MR"
+MGV??@XT1/3+1O[F=-#$=D^P7<(RQDC8[#HJXM1DU&N75!!F&O;0'.#W[LK'X
+M`1+D10T(_;*^EC,X`8^VGA9EDN(-,`:UNK<`/C?CCIEFWX"!&#TPE;F^YC$Q
+M%YGJ:UB/M^8P.0:WN(X!-!O@VAH1@[V(##LPD[RQ6P(<^TX"#)2]O,\\.1FU
+ML9(=8I;R##$<MKJ("#DUD(N98-2&F@<]-YJ[O'8S,6*$EFS<C+5",CX=L[B`
+M##<9Z?<>;HVR@PH[-X>^MV(/'OY(`P"?O+-L/CYKM[&4&&CJW@\W;["[@#0Y
+M"86UF6]>A)(#,@N#NK,6/39+@9-@PXV):#T\9;VXF@HV')?[$GJ/L)HW.PJ/
+MN;03"!G[3`$%A+RV&3@\7["QX`07[5H/-5&RN9DP.0*#M)\52ICJ#3,"B+JV
+M!#\W\(*=8<F"@AP],_F^N90W-A&2X!9,CK&4,3@-M;B(!348YUH''("\M0`[
+M,NZRML(!$.9T#`GCO;_L,CD$CK>2$7^=YP\P!+2ZM0T^-.B.G&'/@(8$/3:3
+MN+_),39KF.EJT(ZW]#,Y!;&XC0`T&>+5!1>-O8\).S&9O+9R`A+E<PT-D+R]
+M3#\^$K6VEAUFD<@.-A&QNX\*.0N<BY]BQ(:8`3TTA[N]8#,V9824;\^/BF4R
+M/Q>RN(8.-QSKPQECC[*!-#LT@+ZT:@\<^G,#!IJ\L!8Y/V:VMNX;;Y52#C=E
+ML[B'-CD,A[6<;M>$D0(R#HRZL!P]-U6!EF+VC(X6/3U&O+F<-3<3EO(2<XZP
+MGS8["8FYBAP('^5Q`!J&O+<$.#WTL+;^!!7L=0XU]+VYDS,Y`8*TDA5%F^\/
+M,P"*NK<!/S3DC9-@\XV#&#PP[[Z^[3<V%)WD%E.)MNTP.`"TN(X'"A_F2P<2
+M@KR*#3LPE[VV4P$6X7P/#NJ\O_\]/AN(MY$1=IS_#C`8M[J+#CXUEXZ28_&#
+MA`8\-IFXO%HP-F*8[17"B;11,CX>L+B#`C0<[5D%:HR]C0L[-H6_MV8"$.1[
+M#`*?O[)G/S\4M+:5'&61W`DV%;"[@C0Y"9F+DFWU@9\#/0J`N[(5,C=VA.MN
+M\(Z*;#T\;;*YA0DT$NK=&62.LH0W.S6-OK46#Q+E>P('A+^Q'3D\=[:VX1MC
+ME4P)-$^RN(4Q.0*&M9-HQ827##(,B;JQ&CPTS(&4;?Z/CQ(\,LR_OI$U-Q:1
+M]1U,B+"0,3@,BKF(&`@=Y'X`&("_M`8X,N>SML@':.]\"0KDO;Z5,CD'C+21
+M%%&;X`XS!K2ZM`,_->V-D6/XC(`%/#&7N;_^-C=HG?@1WHBV^C,Y![>YC`8*
+M'>%P!A&,O(@/.S&2O;=T`13@90X/D;^\TSP^'(NWE!!,G/0(,!RVNHD(/@N0
+MCI!B^8*%`#PWA;N]>#`W9)O@%?&(M'`]/A.SN88--1+O3P5OCKV#-3LTAK^T
+M;P(6YV<,`YN_LV@^/V^TM^P<?Y!="#9BL[N`-SX/FHB0;/*!G0T]"X*ZLQ`R
+M-UB'[FG[B8L4/#Q^O;F9"S01E5$8?XBRFS8["(^^BA,/$.1F#02&O[$;.#W7
+ML;?X&V:4<`@TW;VXGS`^`("UD6C+A^H.,@*+NK8$/#3]@95LY(Z,&3PSY+Z^
+MZS0W%9'&'5*+L)4P.`*UN8X%"!/G9`,?@K^U`SLSZ;.WT0=L[F4("^^\ON,R
+M/@6/M)<7WYKE"#,$M[JU#3\*ZX*78N>,@0<\,9*XO-LV-V*<\Q'(B[;,,CD:
+MMKF#``H3X'D&%X^\C@@[-IF]M'@!:N-A"0R=O[UW/S\0BK3H$%B?V@LQ$;&Z
+MC`H^"9V/EF+D@IH#/#2&N[)M,#1RF^04^8NU9CT_%;.YA`\U$>]V!&.(O8$T
+M.S6`O[45`A?F8P\`A+ZS$3D\9+>WX1]VD$\+-GBRNX<V/@V$B)9O^("0#ST)
+MC[JP'S(TW(?B:.:(B!`\/5F\OI(*-!>51!ATB[.?,3L.B;Z('`\6YV,-!8"^
+MM@0X,O:QM_0:EJVAN;>&X6D$#0LW,CT^/SD\/#`Q-0X&&&Q,XY::AH*,B8B*
+MBK6*BXF.C(*!AYN<D>OA\--,>6%O:A06$1`3$Q,3$!$6%Q1J:6U@9WI\=DE$
+M7=7=VL#,R/7T]_?W]_3URLC.S<#&Q=C?TM'7U51645!24EU<7U]?7UQ<75)3
+M4U!15E975%55U=34U];6UM;6UM;6UM;6UM;6UM;7U]?4U-34U-34U-34U-75
+MU=75U=75U=35U=35U-75U=34U-34U-34U-34U-34U-34U-34U-34U-35U-74
+MU=34U-34U-74U=75U=74U-34U-74U=75U-35U=34U-74U=75U=75U=75U-75
+MU=75U=75U=75U=34U-75U=75U=75U=74U=75U=75U=75U=75U=74U=75U=75
+MU=75U-74U-75U=75U=75U=75U=75U=75U555U=75U=75U=75U=55U=75U=75
+MU=75U=755=75U=75U=35U=75U=75U=75U=74U=75U=75U=75U=75U=75U=75
+MU=75U=75U=75U=75U=555=755=75U=75U=75U=75U=75U=34U-35U-75U=75
+MU=74U-75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=555555U=55U=75
+MU=7555755=75U=75U=75U=75U=75U=75U=75U=75555555555=755=55U555
+MU575U=75U=75U=75U=75U=75U=75U=75U5555555U=75U=75U=55U=75U=75
+MU=75U=75U=75U=75U=34U=35U=55U=75U=75U=75U=755575U=75U=75U=75
+MU=75U=;FD^[A_L77271Y>&1D9V1D>GA\<W=U341?4=33WL7&PLS)R,C+R\C(
+MSLS"P\;'VMG?W=#6U]545U914%!24E)=75)=4E)34U!045965E=45%555575
+MU-34U-?6U]34U=75U=74U=755575U57555555%54555555555=55U5555555
+M55555=55U575U=555=75U=75U=75555555555=75U=75U=75U5755%155=75
+MU=75U-74U=75U555555555555=755555U5555555U=55U=555555U=555=75
+M5=75U=75U=75U=55U555U575U=75U=755575U575U-5555555=555555U=75
+MU=75U=75U=35U=35U=75U5755=55U=74U-75U-75U=75U=75U-74U=34U=75
+MU=555575U=75U=74U=35U=34U5155=75U=74U-75U=75U=75U=75U=75U=55
+MU5555555U=75U=75U=75U=755=555=75U=75U=75U=75U=75U=74U=34U-34
+MU-35U=55U=55U=?4U=75U5555=555575U=75U-35U-35U=75U=75U=75U-75
+MU=75U-34U-7555755=555=75U=755=7555555=75U=55U=55555555555554
+M5=74U-34U]34U=35U=75U-75U=75U575U5555575U=35U=75U=75U=755=75
+MU=75U=75U=75U=75U=35U=75U=75U=755555U575U=75U=77U]75U=555555
+MU=75U=75U=75U=75U=74U=34U=75U=75U-35U=755=555=555575U=55U=75
+MU=75U-75U-75U=755575U=75U=75U=755535U=75U=75U5755=75U=75U555
+MU=755=755=75U=75U=755=75U5555555U=75U=75U=555=55U=75U=75U=75
+MU=35U-75U-75U-74U-34U]?4U=55555555755=555=75U=75U-35U-75U-75
+MU=75U-75U=34U=75U=75U=75U=74U-35U=55U555U=75U=755=75U=555555
+M5=755515U=75U-34U-34U-74U=755=75U=75U=55555555555555U=75U-75
+MU=555=55U=55U=55U5755575U575U=75U-74U-34U-75U555U=75U=?7U=55
+MU=555=75U=74U-75U-75U-75U555U5555=75U=75U=75U-355555U=75U=74
+MU=75U=74U=75U=75U=75U=34U=75U=755555U5545%55U=75U5555=555575
+MU=75U=75U=75U5755575U575U=75U=755=5555755=75U=34U=7555555555
+M55755=75U=34U-35U=75U=75U=74U]7555555=5555555555U=75U=75U=75
+MU=75U=75U=75U=75U=75U5755=555555U=35U-35U-355555U=75U-75U=75
+MU=75U=5555555%155=74U=34U=75U=75U=55555555755=5555155=555=75
+MU=75U=74U-34U=75U-75U5555554555555155=55U=75U=75U55555555555
+MU-35U=7555755=555=555555555555755=555555U=75U=75U=55U=555=75
+MU=55U=75U=34U-75U=75U575U-75U=75U=75U=755555U5545=35U=35U-34
+MU-34U-75U=55U=75U575U=555=75U=75U=35U=75U=75U=75U=75U=75U=75
+MU=75U=755=75U575U=75U=75U=75U=75U=37U=75U=555=555=75U=75U=75
+MU-75U=75U=35U=75U=75U555U575U=75U=75U-74U-34U-34U=75U=75U=75
+MU-75U=75U=75U=75U=155-75U=75U-75U=75U-75U-75U=555555U=75U=34
+MU-34U-75U=75U=75U-75U=75U=5555555=75U=74U=34U=34U-75U=75U=75
+MU=35U=74U]75U=755=755555U=75U=75U=75U=75U-74U-35U575U=75U575
+M5=755=75U-74U=35U-34U-75U=35U=35U=75U=75U=75U=75U=755575U-37
+MU-34U-34U-35U-75U=755=55555555555=75U=74U-34U-75U=75U=75U=34
+MU=35U=75U=75U=35U=75U=75U=75U-34U=75U=75U]?4U=75U=75U=75U=74
+MU=75U=75U=75U=34U=35U=75U=34U=75U=75U=75U=74U-34U-75U=75U=75
+MU-34U-35U=55U=75U-75U=75U5575%55U-74U-34U-34U=34U=75U555U575
+M5575U=35U=75U=35U-75U=75U=35U=75U555U575U=75U=75U=75U=75U=55
+M5555U=75U=75U=77U=755575U=5555555=75U=35U=75U=75U-34U575U555
+MU5555=75U=35U-75U=75U=75U575U=75U=74U=75U=75U=75U=75U-75U=15
+M5-75U=34U=74U=34U=34U=75U5555555U5555=55U=75U=75U=75U=75U=75
+MU=75U-75U575U=75U=75U=75U=555=75U=75U=34U=75U=74U]35U5555555
+MU=755=75U=75U-75U=74U=75U575U=75U=75U=75U=75U=75U=75U=35U=34
+MU=74U=75U=75U=75U555U=75U-35U=75U535U=74U-34U-34U-35U=75U=55
+MU=55U5755575U=75U=75U=75U575U575U=75U=75U=75U=75U=75U=75U=75
+M5=555575U=75U=55U555U=35555555555555U=75U=75U=75U=75U=74U=34
+MU=75U=75U=75U=755=75U=75U=75U=75U-75U=75U=35U-34U-35U=75U=55
+MU575U=55U=75U-34U-34U-34U-35U=75U=35U=35U=75U=75U=75U=74U-34
+MU-75U=75U=35U=75U=75U=74U-75U=75U=75U=75U=75U-75U-75U=34U575
+M55755=75U=75U=7555555575U=74U-?4U-755=755=75U=355=75U=75U=75
+MU=75U=75U=34U=755=55U=75U=75U=35U-35U=755575U=35U-34U-34U=75
+MU=7555555575U=755555U575U575U=75U=555=75U=75U=75U5555=75U=75
+MU575U=75U=75U=74U=345=755575U-75U=75U5755575U=35U=75U=555=35
+MU=35U=75U=75U575U=75U=75U=55U575U=74U-34U=34U-35U=75U=75U=75
+MU=35U=75U=75U555U=74U-34U-34U-75U=555575U=75U=75U5555=555=35
+MU=35U=55U=55U=75U-74U-75U575U=34U=35U=75U=75U=74U=75U5555555
+MU=?4U=75U575U=55U575U=755=75U=75U=75U=75U575U575U=75U-755=75
+MU=75U=75U=75U=34U=74U=75U=35U=35U=75U=75U=75U=54U=34U-34U=34
+MU-74U=75U=75U=75U=75U=75U=75U=755=75U=75U5755=55U=75U=75U-75
+MU=75U=75U=75U=7555555=75U=75U-74U=77U]74U=755=55555555555=75
+MU=75U=75U=75U-75U=75U=34U-34U=75U=75U=75U=74U=35U=75U555U=75
+MU=74U=75U5755=755=755%34U-77U-34U-75U=75U=75U=755=555=555=75
+MU=75U=34U=34U]35U-75U-75U-35U=75U=75U-74U=75U=75U=75U-74U-34
+MU-75U575U-35555555755=755=75U=75U-34U-35U-75U-75U=75U=75U=75
+MU575U=75U=74U=75U=75U-74U-75U=74U=75U=75U=75U=35U=74U555U-75
+MU-35U=35U=75U=75U=555=75U=75U=75U=75U=75U-75U=7555555=55U=75
+MU=75U5555=755=75U=75U=35U=75U=755=75U=75U=?7U=75U5555555U575
+MU=75U-35U=75U=75U=755575U=75U=75U-75U=75U=75U=75U=75U=75U=75
+MU=34U-34U-34U=75U=75U=75U=545=75U=35U=75U=75U=755=55U=75U575
+MU555U575U=75U=75U=75U5555=75U=75U=75U=75U=75U=75U=75U=555=55
+MU=35U=75U=34U=37U]7555755575U555U=755=555=55U=75U=34U-75U=75
+MU=75U=35U=75U=75U=55U5555=75U=75U-35U-35U-75U=75U=75U-35U=75
+M5535U=75U=34U-34U-74U575U575555555555=555=75U=35U=34U-35U=75
+MU-35U=75U=555=75U=75U=55U555U=55U=74U=74U=74U=74U]35U-75U=75
+MU=75U=75U=75U=74U=35U-34U]34U=7555755575U=75U=75U=34U=34U-74
+MU=74U=74U=75U=75U-34U=75U=755=75U555U=74U-34U-74U-34U=75U=75
+MU=75U=555=55U=75U=75U=75U=355555U5555575U=75U=75U=55U=75U=75
+MU=75U=74U=75U=75U=55U=34U=555=755=55U=55U=75U-75U-35U=75U-75
+MU=5555555575U=555=75U=75U=75U=75U=75U=75U-75U=75U=35U-34U-75
+MU=74U=555%75U=34U-34U-34U=75U=755=75U=75U=75U=74U-34U-75U=74
+MU-55U=75U=75U=755=75U=75U=34U=75U=75U=55U=75U=75U=75U=77U]34
+MU=7555755=75U=75U575U575U=55U=75U=75U=74U=75U=355555U=75U=75
+MU=75U=75U=75U=74U-34U-35U=75U=75U=75U=755574U-34U-34U-35U=75
+MU=555=7555555555U555U575U=75U=75U-55U=75U=75U=555=75U=75U-75
+MU=75U-75U=75U-34U-75U=74U=75U-?4U=74U=74U-74U575U5755=75U=35
+MU-34U=75U=555555U575U%75U=75U=75U=75U=74U-34U-34U-75U=75U=75
+MU=74U=74U=75U554U=75U-34U-35U-35U=75U=75U=75U=34U-75U=75U=35
+MU=34U=35U=75U=35U-34U=75U=75U=75U575U=55U=75U=75U=75U=75U=35
+MU=37U=75555555555=75U=75U=75U=75U=75U=35U=75U5555555U=55U=75
+MU=75U=75U=75U-74U-34U=74U=7555755=75U=75U-75U=555%55U=75U=75
+MU=74U=75U555U555U555U=55U=75U=55U=75U=74U575U=75U575U5555575
+MU=75U-34U=74U-35U=35U=35U=75U=34U=74U-75U=555=755=55U=75U575
+MU=75U575U=74U-35U=75U=75U=34U=75U=75U=755=755=75U=35U=75U=75
+MU=75U=75U-35U-75U=345535U=74U-34U-74U-34U-74U-75U=75U=75U=75
+MU=34U=75U=75U-555=74U=75U=75U=75U=75U=35U=35U=74U=75U=555555
+MU575U=75U-?4U5755575U=74U=755=75U=75U=75U-74U-35U=75U=75U=75
+MU575U=55U=75U=75U=75U=34U-34U]34U-75U=755=55U=75U575U5545=75
+MU-74U-34U-35U=75U5755=755=7555555575U=755=75U=75U=75U=755=55
+MU=75U=755575U=75U=75U=75U=74U-74U-75U=755=37U=75U=555=755=75
+MU=75U=755=75U=75U=75U=75U=75U=75U-75U575U=75U=75U-75U-75U=75
+MU=75U=75U=74U=74U=74U=75U=355-74U-34U-34U-75U=75U=75U=34U=75
+MU575U=75U-74U-74U-74U=755=75U=755%74U-?5U-=55=555-555=75U-55
+MU-34U=75U-35U=74UM75U5555555U575U=75U=75U=75U-34U-74U-74U535
+MU-755=7455555=75U=75U555U555U=75U=77U-35U=34U=75U=75U=55U=55
+M5515U=74U-?4U-35U-75U55555755=74U=75U555U=75U=75U=74U-34U-34
+MU=75U555U555U=75U=75U=75U=75U=75U=75U575U-74U-55U=34U555U=55
+M5555U=75U=75U=75U=75U=74U=75557555555=555555U=55U=75U=35U=34
+MU-35U-35U=75U=55U=75U-35U=55U57555545=75U]34U-34U=75U=75U=75
+MU=55U=7555555555U=74U=75U=34U=555=55U=75U=55U=75U=75U=75U=75
+MU=34U-34U-75U-55U=55U=77U-75U=55557555755575U=75U=74U-74U-35
+MU=75U=75U=75U=75U=755575U=75U=75U-34U=75U-75U=75U=75U=74U-34
+MU=35U=755%35U=34U-34U-34U=75U5555555U=75U=55U5755=75U=75U=75
+M5575U=75U=75U=75U=75U=755=75U5755575U575U=75U=75U=75U=74U]35
+MU=75U=75U=555=75U=55U=75U=55U=75U=75U=75U=75U=75U575U575U=75
+MU=75U=75U=75U=75U=75U=75U575U=75U=74U=34U%15U=75U=34U-34U-75
+MU=75U=75U=555575U=75U-34U-75U-75U-75U=75U=75U=755=755=75U=75
+MU=75U-75U=75U5555=75U=75U=35U-?4U=75U575U=75U=75U=755=555555
+MU=75U=75U=75U=75U=75U=755=75U=555555U=75U-34U-34U=75U=75U=75
+MU=75U=75U=55U=545-55U=35U-35U=34U-34U=75U=75U575U=75U=75U-34
+MU-75U=35U=755=75U=75U=75U=75U=75U=75U=35U=75U=75U=74U-75U=75
+MU=37U-75U=5555555555U=75U=75U=75U=75U=35U=755=75U=75U=75U=55
+MU=75U=75U-75U=75U=75U-75U575U=75U575U=75U-35U-345=75U=34U-34
+MU-74U=75U=75U=755555U=555555555555555575U=75U=75U-75U=75U=55
+M5575U-35U=75U-75U=75U=74U=34U-34U-75U]?4U555555555545=75U=75
+MU=75U=75U=74U=74U=75U=555555U=55U=55U=75U=75U=34U=75U=75U=75
+MU=75U=74U-75U=35U=75U575U-34U-35U-35U-34U-35U-34U=75U=75U=75
+MU-75U-74U-75U-75U=555575U=75U=34U-74U=355=755575U=74U-75U=75
+MU=55U=55U=?4U=75U57555555=555575U=35U=75U-75U=75U575U=74U-34
+MU=75U=75U575U=75U=75U-75U-75U=75U=75U=35U-35U=34U=75U=555=75
+MU=34U]34U=75U=755=75U=34U575U=75U=75U=75U=74U-74U=35U=35U=75
+M5=755=555555U=75U=75U=75U=75U=55U=75U=75U=77U]34U55555755555
+M5=55U=75U=75U575U=74U=75U=75U-75U575U=75U=75U575U=34U-35U-35
+MU=35U=34U-34U=74U=75U=75U-755574U-34U=34U-75U=75U=74U=74U=75
+MU=75U=75U=75U=75U=34U=75U=35U=75U=34U=75U5755=75U555U5555575
+MU-35U=75U=34U575U-?4U55555555555U=75U=75U=75U=35U=755=75U=75
+M5=75U=555=55U=55U=55U=74U-75U=75U-75U=75U=75U=75U-75U=75U=75
+MU515U=34U-35U=35U=75U-74U-35U=75U-75U=75U=75U=755=75U=75U=74
+MU=74U-75U575U=75U=75U=75U=755575U=75U=75U=75U=55U=34U=75U=55
+M5=55U=75U=75U=75U=755=55U=75U-55U=75U=55U575U=75U=75U-35U=74
+MU=75U575U=75U=75U=75U=75U-75U=34U=755-745=35U=35U=75U=75U-15
+MU=55U=7555555555U575U=755575U=75U555U=555=55U=75U=75U=75U=75
+MU=34U-75U=75U=755=74U-34U]35U-155555U555U575U=75U=75U=555=75
+MU=75U=74U=75U=555=74U=755=74U=75U=34U-75U=35U=55U=74U=75U=35
+MU=555=755515U=74U-75U=75U=75U=75U=75U575U=5555555575U=75U-35
+M5=75U=75U=75U=35U=75U=75U=555575U=75U=75U-35U-34U=75U555U=35
+M55755=5555555575U=75U=75U=75U=75U=34U=35U=55U=75U=755=555555
+MU=75U-75U=75U=75U=74U-35U=74U=75U-34U%75U554U=74U-34U=35U=75
+MU=75U=75U=555575U555U=75U-34U-75U-75U=75U=75U-75U=75U=75U=55
+MU555U=75U=75U=75U=75U=75U=35U=37U55555555555U=75U-74U=35U=75
+MU-35U=74U-34U=75U=75U=75U=75U=75U=75U=75U=74U-35U=74U-75U=75
+MU=75U=75U=35U=355=75U=34U-34U=75U=5555555575U=55U=34U-75U=75
+MU=5555555=75U=75U5755555U=74U-34U=75U=35U=75U=75U=74U-75U555
+M5574U%555=755=75U=555=55U=75U=75U=34U=34U-75U575U=75U=75U=75
+MU=75U=75U=75U=34U=75U=75U=75U-35U=75U=75U=75U=355575U=34U-34
+MU=34U575U=75U-75U=75U=5455545=75U=74U=34U-34U=75U=75U=75U=75
+M5=555=555575U=75U=74U-34U-34U=75U=75U=355=75U=75U=55U=755=75
+MU=75U=75U=75U=755=75U=75U=55U=75U=55U=755=74U-34U-35U-75U=55
+MU=34U-75U=755575U-74U-55U=75U-34U=34U-75U=35U=75U=55U=75U515
+M5=755=77U=74U=75U-35U-15U=75U555U=75U=34U%75U575U-75U-34U=75
+MU=555=555=77U-75U=75U=75555555555=75U=34U-75U-34U=55U=55U=75
+MU=75U=35U=75U555U=75U-75U-34U=755=75U=75U=34U-34U-55U=755%15
+MU=34U=34U=34U=75U=75U=75U=55U=75U=74U-75U=75U575U5755555U=55
+MU=75U=75U-74U-37U-75U=75U=75U=75U=75U=75U=74U]35U=75U=755575
+M5=75U=74U=75U=55U=75U-35U=75U5555=75U575U=75U-75U=34U=55U=75
+MU=74U=34U=75U=75U=35U=15U=75U515U=34U-35U-75U=34U-35U=555=75
+M5=55U5555575U=75U=75U=555=555=75U-35U=755555U=75U=35U=755575
+MU=555=74U-75U-35U=34U=5555155=555=75U-75U-34U-74U=34U=75U=75
+MU=75U=755=7555555=75U-34U-35U5555=75U=74U=34U-75U=75U=75U=75
+MU5545=75U-75U=35U-34U-74U=5555555575U=35U=75U=75U555U=74U-74
+MU=75U=75U-34U=555=75U=75U=75U=75U=75U=75U=55U=75U=77U]75U554
+M55155=75U=34U-35U-755575U=34U-34U=75U=74U-34U-34U=75U=75U=75
+MU=74U-75U-75U=75U=75U=34U=35U-3555755%35U=37U-34U-35U=755=75
+MU=75U=75555555555=55U=75U=75U=75U-55U-34U-75U=555575U=755555
+MU=34U-75U=74U=75U=75U575U-35U5755575U575U=74U=75U=75U=75U=75
+MU=75U=75U=75U-35U-75U=75U=75U=75U575U=75U=75U=75U-35U=75U=75
+MU=74U=35U554U=34U-34U-35U=35U=75U=75U=75U=75U=75U=34U-75U=34
+MU=55U=55U575U=74U-75U=75U=75U=74U575U=75U=75U=75U=75U555U=?4
+MU=75U55555545575U=74U=35U=75U-35U-34U-75U-75U=75U=75U=755575
+MU575U=74U-75U=75U=75U=34U-35U=35U=75U575U-545=35U-75U=35U-35
+MU=34U-35U-75U-75U=75U575U=74U=75U=75U5755=74U=74U-34U=55U555
+MU575U=75U=35U=34U=75U=55U=75U=74U%75U5545575U=75U=75U-74U=34
+MU-75U=75U=74U-34U=75U5555575U=75U=755=55557555555575U=75U=75
+MU=55U575U555U5755-74U-34U-34U=75U=75U=35U-35U=75U=55U575U=75
+MU=75U=74U=75U=75U=75U=75U=75U=75U=75U=75U=755575U=75U=74U575
+MU=75U-15U5555575U=75U=75U=75U=74U-75U=75U=55U=75U-75U=75U555
+M5555U=75U=75U=34U-75U=74U=34U-75U575U=75U=35U=35U515U=74U-34
+MU=74U=75U=34U=35U-755575U=75U=74U-35U=35U=75U-74U=75U-34U=75
+MU=555=75U5755=75U=35U=75U=75U=75U=55U=?7U=55555555555=75U=75
+MU=555575U=75U-74U=75U=55U=75U=755=75U=34U=35U=35U-75U=75U=75
+MU-35U=75U=555575U=75U=555=75U-35U-34U-75U=75U=75U=75U5555575
+MU575U=74U575U-34U-75U=755=75U=75U=75U=755=75U=75U=55U=75U=55
+MU=55U=75U=77U]75U=75557555555555U=75U=75U=75U-37U-75U=75U=75
+MU57555555555U=75U-74U-75U=75U=75U=35U-75U=75U=75U=75U=755574
+MU=34U=74U=75U=75U=74U=34U=75U=34U=75U-75U=75U-755=75U=74U-34
+MU=75U5755=555555U=75U=75U-35U-74U-75U=75U=75U-35U5555%15U=75
+MU=74U=34U-75U=75U=74U-35U=74U=75U=15U=74U-75U-75U=75U=34U-55
+MU575U=75U=75U575U=74U=755=555514U=74U-75U=75U=75U=75U=75U=74
+MU=34U=75U=75U-74U-34U-75U=75U-75U-75U555U555555555755=75U=34
+MU=34U=35U=75U=75U=34U=555555U5755555U=35U=75U-35U=75U=75U555
+MU555U575U=75U=75U=75U-34U-34U=75U=75U=755575U=74U-74U-34U=75
+MU=755-75U-34U-34U-34U=35U=55U=55U=55U%55U=75U575U575U=75U575
+M5=755=75U=75U=35U=75U=35U-34U-34U=75U=34U=35U=75U=74U]75U=55
+MU5555555U=75U=75U=75U575U=74U=755575U=75U-75U=75U=35U-75U-75
+MU-74U=35U=75U=75U=75U-35U=75U-75U=755535U=34U=34U=75U=75U=75
+MU=75U=75U575U=755=75U=75U-3555555555U=75U=35U=35U=75U-75U=75
+MU=75U-35U-34U-74U=75U-75U-34U=75U575U55555755575U=34U-74U-75
+MU-75U=34U-34U-35U=75U=75U=75U-35U-35U=75U=755=75U=75U=75U=75
+MU5755=75U555U=74U-74U-35U-34U-35U-35U555U5555555U5755=75U=34
+MU=75U=35U=35U-34U-35U=75U575U=75U=75U=75U-75U=75U=75U=74U=36
+MU-75555455555=75U=75U=75U-75U-74U-75U575U=555=755555U=75U555
+MU=75U=75U=35U=75U=75U=75U=75U=35U=75U-75U=745%75U=34U-34U=34
+MU=34U-75U=55555555555575U=75U=75U-75U=75U-75U-74U-75U=75U=75
+MU=75U=75U=75U=75U=755=755=75U=74U]35U=5555755575U=75U=75U=75
+MU=75U=75U=75U=75U=74U55555555=55U=75U-34U=75U=75U=75U=75U=75
+MU=75U=35U=75U=75U515U-74U=75U-34U=75U=34U=75U555U5555555U=75
+MU=75U=7555555=75U=74U=74U=755555U=75U=75U=75U=75U=75U=75U575
+MU=75U-?4U=75U555U=75U575U=75U=75U=75U=75U=75U=75U=75U=75U=75
+MU=75U=75U=75U=75U=75U=75U-34U-35U-75U-74U-74U=75U=545-75U=34
+MU-35U=75U=75U=75U=75U=75U=75U=75U=75U=75U5555555U575U=555555
+M55555=75U=75U=75U=75U=75U=75U=75U=75U=37U-5555555=555=75U-35
+MU-34U-35U=35U-34U-34U=75U=35U=35U-75U-74U-755=75U=75U=75U=75
+MU5555=755=75U=755=75U=355=74U=35U=75U-34U-75U=75U=75U=75U=75
+MU-75U=75U=75U-35U=75U=75U-75U=755555U=75U=75U=75U=75U=75U=75
+MU=35U-35U=75U-35U5555555U575U=75U=75U575U=75U=75U=75U=75U=74
+MU575U=755=75U=74U=34U=34U=75U=75U=75U=75U=75U=75U-35U=74U515
+MU=74U-34U-74U=75U=75U=75U=75U=7555555575U=75U=355=75U=75U=75
+MU-34U-74U=35U=75U=75U=55U=75U555U=75U575U=75U-?4U=75U575U=55
+MU=75U=75U5755=75U=74U=75U=75U=75U=5555555555U=75U=75U=35U-34
+MU-75U-75U=75U=75U=75U=75U575U=545-75U=34U-34U=35U=75U=75U=75
+MU=75555555555=75U=74U=5555755=555=55U=75U575U575U=75U555U555
+MU=75U=74U=75U=75U=77U-7555755=55U=755=55U=75U=34U-34U-35U=55
+M5=55U=35U=75U=75U=75U=75U=75U-75U-75U=75U=75U=75U-35U=74U-75
+MU=555%75U=34U-34U-34U=75U=55U=75U=55U=55U=75U=55U=75U-75U=75
+MU=75U=55U=755555U5755=755=75U=34U-34U-34U-34U-34U-74U]?4U-75
+M5555U=75U=35U-75U=75U=55U=74U-34U-75U=75U575U=75U=75U=74U=34
+MU-74U=755=755=75U-34U-35U=55U=75U=75U575U-34U-34U-75U=35U=75
+MU=75U=75U=7555545555U=74U-?4U=75U=75U=74U=34U=34U-35U=75U=75
+MU=35U=34U-75U=75U575U=55U=34U=55U55555555555U=75U575U=75U=74
+MU=75U=75U=75U=555575U=75U=755=555=75U=75U=35U-75U=75U=75U=75
+MU=75U-35U=55U=34U-35U-75U=75U=75U575U=75U=75U=75U=75U=34U-34
+MU=75U=75U=75U=75U=75U=55U=55U=75U=75U=35U=555=75U=75U=75U=37
+MU]75U=555=75557555755=75U=75U=74U-34U=555=75U=755=55U=75U=75
+M5=75U=74U-35U-75U-75U=75U-75U-75U=75U=74U=755%74U=34U-74U=75
+MU=75U=755=75U=75U=75U=5555555=75U=55U=75U=75U=555555U=75U=55
+MU=555575U57555755=75U57555755=75U-35U=55557555555555U575U=75
+MU=35U=75U=75U=755=75U=75U55555555=75U=74U-34U-35U=75U=75U=75
+MU=75U=755=755575U555U=34U-35U-35U=755=755=75U=75555555555=75
+MU=75U-34U=75U=74U=75U-34U-74U=74U-75U=34U=35U=75U=75U=75U=55
+MU=55U=?4U=55U5555555U=55U575U=75U=75U=75U=75U=75U=75U=75U=75
+M5=555=755=75U=75U=74U=75U=75U=75U=75U=35U=75U=75U-145=75U-34
+MU-74U-35U=755575U=75U=74U=75U=75U=34U=74U=74U=75U=75U=75U=75
+MU=75U=75U=55U555U=55U=75U=74U=35U-34U=77U]7555555555U=755=75
+MU=75U=74U=34U=34U=75U=75U=355=75U=75U=75U=75U575U=75U=34U-75
+MU=75U=75U=75U=75U-75U=345574U=34U=34U-34U=34U-75U=75U=75U=75
+MU=75U=34U-35U-355=75U=75U-35U-35U-75U=55U=75U575U575U575U=75
+MU=74U=74U=74U-?5U=55551555555=75U=75U=75U=75U-74U-75U=75U=75
+M55155555U=74U-74U=34U-35U=75U=755=75U=75U=55U=75U=75U=75U557
+M5=75U=74U-75U=75U575U=75U=75U5555555U=75U-74U-34U=75U=74U-75
+MU=75U=74U=35U=35U=75U=74U-74U-35U=75U=75U5555=34U=555=555=55
+MU=75U=74U=34U-74U-75U=75U=75U-74U-35U=75U=75U=75U=75U=75U=75
+MU=35U=75U=755=75U=35U-34U-35U=555-75U-34U-34U-35U=75U=555575
+MU5755=755555U5555=75U575U=75U=75U=7555555=55U=75U=75U=55U=55
+MU=75U575U=55U=75U=74U]75U=75U=74U=75U=75U5755575U=75U-34U=75
+M5555U=755=75U=755575U=75U=75U=55U=75U=75U=75U=75U=75U=75U=75
+MU-355575U=34U-34U-75U=34U-74U=75U=75U=75U=75U=75U=74U-35U=75
+MU=75U=75U=75U-74U-75U=755575U=75U=34U-35U=75U=75U=75U-?4U=75
+MU5755%555555U=75U-34U-35U=75U=75U=75U=75U=75U=75U=75U575U=74
+MU=34U-34U-75U=75U=74U=75U-75U=755555U=545=75U-34U-34U=75U=55
+MU=55U=75U=75U=555555U=75U=74U=75U=75U575U=74U=75U=75U=755=75
+M5=755=75U=34U-75U=75U=755=37U=75555555555555U=74U-34U-34U-75
+MU-34U-75U=75U=55U575U=75U=75U=74U=34U-75U=75U=75U=75U=75U=55
+M5=75U=35U-355=75U-35U-35U-35U-75U=75U=75U575U=75U=74U=75U575
+MU5755=74U=34U=75U=75U=75U=75U=34U-75U=75U5555=75U=74U=74U-74
+MU]355=75U=75U=75U=75U=75U=75U575U=74U-75U=75U=555=55U=35U=75
+MU=75U=75U-74U=75U=75U=34U=75U=75U=75U=74U-34U535U=74U=74U-34
+MU=75U=75U=75U=75U=7555555=755575U=75U5755575U=75U=75U=35U=75
+MU=55U555U575U=75U=75U-75U=75U-35U-?4U=755575U=755=755=55U=75
+MU=75U=35U-35U=75U=35U=75U=75U=555575U=75U=75U=75U=75U=35U=75
+MU=75U=34U=74U-75U5545-75U-34U-34U-34U-75U=7555555=755555U=75
+MU=75U=75U=75U=75U-74U-74U=75U5555=75U=75U-74U=34U=75U=75U=75
+MU=35U=37U=5555555=555=55U=75U=75U=75U=75U-74U-75U=75U5555555
+M5=75U575U=74U=34U-34U-75U=35U=35U=755=75U=75U=75U=755-75U-35
+MU-34U-75U=75U=75U=75U=75U-75U=75U-34U-75U=75U=75U=75U=75U=75
+MU=755=75U=75U=75U-35U=75U=75U=75U=75U=74U]35U5555575U555U=75
+MU=75U=75U=75U=75U575U=75U=75U=75U=75U=55U=75U=75U-35U=55U=75
+MU=75U=755575U=75U=75U=75U535U-74U-75U-74U=75U55555755=55U=75
+MU=755=75U555U=755575U=75U=75U=75U=55U=75U=75U=75U=75U575U=75
+MU-35U=74U-35U-?4U=55U5755555U=755=75U=75U=74U-34U-34U-35U575
+MU=75U=75U=75U=75U=75U=75U575U=75U=75U=75U=74U-35U=75U=75U=15
+M5=75U=34U-74U-35U=75U=75U=75U575U=55U575U=755=75U=75U=75U=75
+MU=75U=75U5555=555=75U=75U=75U=75U=75U=75U=555=74U-5555545555
+MU=75U=75U=74U=75U=75U=75U=75U=75U555U=75U=75U=75U=75U=75U=75
+MU=75U=74U=34U=75U=75U=34U=75U=555%75U=34U-74U-75U=75U=755=75
+M5555U=555575U=75U=75U-74U-34U-34U=74U575U=75U=75U=75U=75U=75
+MU=75U=35U=75U=75U575U-155=555=75U=75U=75U=75U-75U-35U=34U-35
+MU=7555755=75U=75U=755=75U=75U=75U=74U-34U-34U-75U=75U=35U-34
+MU=75U515U-75U-74U-74U-74U=75U=755575U=755=755=75U=75U=75U=75
+MU=75U=55U=75U=55U575U=75U=75U=75U575U=75U=75U=75U=75U-?7U=75
+MU575U5755575U=75U=75U=755=75U=75U=75U=75U=55U=75U=75U=75U=75
+MU=75U=75U-75U=75U=75U=75U=75U=75U=75U=555=75U-75U-34U-34U=75
+MU=75U=75U55555555555U=75U-34U-35U=75U=35U=75U=75U=75U=75U=75
+MU=75U=75U=75U=75U=75U=75U=74U-7555555=75U=75U=75U=75U=75U=75
+MU=34U=555=75U=55U575U=75U=75U=74U=75U=35U-74U-75U=75U=755575
+M5=75U=75U=345535U=34U=34U-74U-34U=34U=75U=35U=75U=75U=75U=75
+MU=75U=74U=75U-74U=35U=75U575U575U=75U=75U=74U=75U=75U=74U=75
+MU-35U55555555=75U=75U=75U-35U=75U=75U-75U575U=75U=75U=75U=75
+MU575U=74U=75U=75U=75U=75U=75U=75U=75U=75U=75U555U-74U-34U-35
+MU=75U=55U555U=75U=75U575U=755575U=75U=75U=75U575U=755=755=75
+MU=75U=75U=75U=75U=75U=35U=75U=755=34U=75U575U=75U=7555755=75
+MU=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=34U-35U=55U555
+MU=75U=75U=34U=75U=155=75U=34U-34U=75U=75U=75U=75U=74U=35U=35
+MU=35U=75U=75U=35U-35U-34U=755=555=75U=75U=75U=75U=55U=75U=34
+MU-34U-37U]55555555555=75U=74U=75U=75U=75U-34U-75U=75U=55U=75
+MU=75U=75U=75U=75U=74U=75U=75U=74U-34U=755=75U=75U=74U535U=74
+MU=74U-34U-75U=75U=7555555555U555U=75U=75U5555=75U=75U-74U=75
+MU5755575U=7444%64TQ37-34TM_>V-C8V=G?W]+2T]S>45C81%#&6]907515
+M1E344=9`T-13UE-7WUE6TE_?T%/25]#04]?65MW75=95UM965-15U]76T]54
+MUU96TE35UU'6U5=45=745%#7U%165M?5U=?5U-!6T]57TU/7UU+25%?15M'6
+M4M?7U=96U]74U=54UM75U5;45]?7U==7U57545365-=6U=17U5775=;45=55
+MU%=5U5365%755M95U-16UE755%575==1UN2AHZ4J*BXA.30%6Y*#B[>QL+.S
+ML["VM[6(C(&%G97DW'-B%!`<&!H%!`0$!`0%&A@>'1,6%&EB9W]T1%39S/?R
+M_OOEY.?GY^3DY?KY__+P]\H)(*NFL)P=-3T[)"8F(28F)R4[/S(V"P,;%4+G
+MD9N&@H^(B[6UM;6*BXB.C(*!AYJ>DY3LY/?307-E86UN:VH5%106$1`0$184
+M%6EM87IR2%O4V\_W\O[[Y>3DY^?DY.7[^?SR\?3(PL39T]=74U];149`0TU-
+M34Q-34)#0$%'1%I;6%Y?75-045975%75U=34U-34U-35U=5555555%145U=7
+M5E965E965E9645965E9645965E975E975U=75U=75%145%145%145%145%14
+M5%145%145%145%145%145%=45%175%145%155%1455155%55555555555555
+M5%555555555455555%555555555555555555555555555555555555555555
+M55555%5555555555555555555555555555755=55U=75U=75U=75U=755=75
+M55755=555=5555555=75U=75U=75U=55U=75U=75U=75U=75U=75U=75U575
+MU=75U=75U=75U=75U=75U=75U=55U55555755575U555U=75U=75U=55U575
+MU=75U575U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75
+MU=75U=75U=75U=74U-75U-35U-34U-75U-75U=75U=75U5755=555=555555
+M5555555555555575U555U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75
+MU=55U=75U=75U=75U=75U=75U=75U=75U=75U=75U=55U=75U575U=755=75
+MU=755=75U5755=75U=755=75U=75U=75U=75U=75U=75U=75U=75U=75U=75
+MU=75U=75U=75U=75U-75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75
+MU=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U-75U=75
+MU=75U=75U=75U=75U=75U=75U=75U=75U=75U=31T=?45=755%165E=45%15
+M5=75U=74U-74U-34U-34U=34U-75U=55U=74U-745%74U%;5E[2U@(25Y'9@
+M%A(9&P4%!1H8&1P2%A5O87YU4M[U_>7FX^WO[NGIZ>[O[.WCX>?E^?WVRL+:
+MW==67%M'0TU.24A+2TA(3D],0D!&1%M97UU345=5U=36UM'0T]/3T]/3T-#0
+MT=;6U]?7U-7555545%145U=75E=75E=75U=75U=75U175%145%1555555555
+M55555555555555555555555555555555555555555555555555555555U555
+M5=555=55555555555555U5555555557555755555557555555555U575U=75
+M5=75U=75U=75U=75U=75U=75U=55U=75U575U555U5755=55U555U575U=55
+MU=75U575U=75U=755555U555U575U=75U=75U=55U5755=555=755=55U=75
+MU=55U=75U575U575U=755=75U=75U=75U=75U=75U=75U=75U=75U=75U=75
+MU=75U=75U=75U=75U555U555U555U575U=55U575U=75U575U=75U=755=75
+M5=55U=55U575U=75U=75U=75U=75U=75U=35U-75U-75U=75U=75U=75U=75
+MU=75U=75U=75U=75U=75U=74U=35U=75U=75U=75U=75U=75U=75U=75U=75
+MU=75U=75U=75U=35U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75
+MU=75U=75U=75U=75U=75U=75U575U=75U=755=75U=75U=75U=75U=75U=75
+MU=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75
+MU=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=755=755=75U=75U=75
+MU=75U=75U=75U=75U=75U=75U=75U=55U=75U5755=55U=55U=75U=75U575
+MU=75U=55U=55U575U=75U=55U=75U=55U=75U=75U=75U=75U=75U=75U=75
+M5=75U=75U=75U=75U575U5755=75U=75U=75U=75U=75U=75U575U=75U=75
+MU=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75
+MU=35U=75U=75U=55U=55U=755=75U=75U=75U=75U=55U=75U=75U=75U=75
+MU=75U=75U=75U=55U575U57555755=55U555U5755=55U555U555U575U575
+M557555755=555=555=555=55U57555755555U5755=555=5555555=555575
+M5555U555555555555=555=55U=75U=75U=75U=75U=75U=75U=75U=75U=75
+MU=75U=75U=75U=755=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75
+MU=75U=755=755=75U575U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75
+MU=75U=75U=75U=75U=75U=75U=75U=75U=75U=55U=75U=75U=75U=75U=75
+MU=75U=75U=75U=75U575U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75
+MU=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75
+MU=75U=75U=75U=75U575U=75U575U=75U=755=75U=75U575U=75U575U=75
+MU=75U=75U=75U=75U=75U=75U=75U=7_____________________________
+M__________________________________________________________]5
+M555555555555555555555555555555555555555555555555555555555555
+M55555555555555555555555553HX,@L1DHJ]N+JXO8J2$0LR.#HX/34<E(BR
+MN+J[O+29:PDS.3H[/#0:[8ZSN;J[O+>%9@PP.3H[/S<'_HVPN;J[O[:&=`(Q
+M/CH[/C8!V8.QOKJZOK&#W`$V/CLZ/C$"2H:VO[NZN;"-_P<W/SLZ.3`,9X2W
+MO+NZN;./[1HT/#LZ.3,):)FTO+NZN+*(E!\U/3@Z.#(+$9*UO;BZN+V*DQ$+
+M,C@Z.#TU')>(LKBZN[RTF6L),SDZ.SPT&^R.L[FZN[RWA6$/,SDZ.S\W!/B-
+ML+FZN[^VAG<",#XZ.S\V`=J#L;ZZNKZQ@-,`,3X[.CXQ`DF&MK^[NKZPC?T'
+M-S\[.CDP#&2$M[^[NKFSC^(:-#P[.CDS"6F8M+R[NKBRB)0?-3TX.C@R"Q:2
+MM;VXNKB]BI,0"C(X.C@]-1R7B+*XNKN\M9YJ"3(X.CL\-!OOCK.YNKN\MX5A
+M#S,Y.CL_-P3ZC+"YNKN_MH9Q`C`^.CL_-@''@[&^NKJ^L8#6`#$^.CH^,0-,
+MAK:_N[J^L(WR!S8_.SHY,`QDA+>_N[JYLX_C!30\.SHY,PYNF+2\N[JXLHB5
+M'S4].#HX,@L6G;6]N+JXO8J0$`H].#HX/34=EXNRN+J[O;6>%0DR.#H[/#08
+M[HZSN;J[O+>%8`\S.3H[/S<$Y8RPN;J[O[:'<`(P/CH[/S8!P(.QOKJZOK&`
+MU0`Q/CHZ/C$#0(&VO[NZOK""\`<V/SLZ.3`,982WO[NZN;./X`4W/#LZ.3,.
+M;IBTO+NZN+*)E1XU/3LZ.#(+%YVUO;BZN+V*D!`*/3@Z.#TU'9:+LKBZN+VU
+MGQ4(,C@Z.SPT&.Z.L[FZN[RTA6,/,SDZ.S\W!.2,L+FZN[^VAW(-,#XZ.S\V
+M!LR#L;ZZNKZQ@%8`,3XZ.CXQ`T>!MK^[NKZP@O$&-C\[.CDP#'J$M[^[NKFS
+MC^$%-SP[.CDS#F^;M+R[NKBRB>H>-3P[.C@R"!><M;VXNKBRBI`3"CTX.C@]
+M-1*6B[*XNKB]M9\4"#(X.CL\-!CIB;.YNKN\M)IB#S,Y.CL_-P3DC+"YNKN_
+MMX=]#3`^.CL_-@;)@K&^NKN^L8!3`#$^.CH^,0-:@;:_N[J^L(+W!C8_.SHY
+M,`UXA+>_N[JYLX_A!3<\.SHY,PYLF[2\N[JYLXGK&30\.SHX,@@7G+6]N+JX
+MLHN1$PH].#HX/342D8NRN+JXO;6?%`@R.#H[/#09Z(FSN;J[O+2:;0\S.3H[
+M/#<$YXRPN;J[O[>'?PTP.3H[/S8&RH*QOKJ[OK:!7`,Q/CHZ/C$#68&VOKNZ
+MOK&"]`8V/SLZ.3`-?H>WO[NZN;",Y@4W/#LZ.3,.;9JTO+NZN;.)ZQDT/#LZ
+M.#((%)RUO;BZN+*+D1(*/3@Z.#T*$I&+LKBZN+VUG!0(,C@Z.SPT&>N)L[FZ
+MN[RTFFT.,SDZ.SPW!>:,L+FZN[^WAWX-,#DZ.S\V!O2"L;ZZN[ZV@5D#,3XZ
+M.CXQ`UR!MKZ[NKZQ@LH&-C\[.CDP#7^'M[^[NKFPC.<$-SP[.CDS#VV:M+R[
+MNKFSB>@9-#P[.C@R"!2?M;VXNKBRBY$2-3TX.C@]"A.1B[*XNKB]M9P7"#(X
+M.CL\-!GKB;.YNKN\M)ML#C,Y.CL\-P7AC[.YNKN_MX1X#3`Y.CL_-@;W@K"^
+MNKN_MH%:`S$^.CH^,0!3@+&^N[J^L8+)!C8_.SH^,`U]A[>_N[JYL(SD!#<_
+M.SHY,P]BFK2\N[JYLXGI&#0\.SHX,@@4G[6]N+JXLHN6$C4].#HX/0H3D(JR
+MN+JXO;6<%P@R.#H[/#4>ZHFRN+J[O+2;;PXS.3H[/#<%X8^SN;J[O[>$>@PP
+M.3H[/S8&\8*POKJ[NU555555555555555555555555555555555555555555
+M555555555555555555555555555555555555555555555555555555555555
+M555555555555555555555555555555555555555555555555555555555555
+M555555555555555555555555555555555555555555555555555555555555
+M555555555555555555555555555555555555555555555555555555555555
+M555555555555555555555555555555555555555555555555555555555555
+M555555555555555555555555555555555555555555555555555555555555
+M555555555555555555555555555555555555555555555555555555555555
+M555555555555555555555555555555555555555555555555555555555555
+M555555555555555555555555555555555555555555555555555555555555
+M555555555555555555555555555555555555555555555555555555555555
+M555555555555555555555555555555555555555555555555555555555555
+M555555555555555555555555555555555555555555555555555555555555
+M555555555555555555555555555555555555555555555555555555555555
+M555555555555555555555555555555555555555555555555555555555555
+M555555555555555555555555555555555555555555555555555555555555
+755555555555555555555555555555555
+`
+end
diff --git a/usr.sbin/i4b/g711conv/Makefile b/usr.sbin/i4b/g711conv/Makefile
new file mode 100644
index 0000000..f24d6e8
--- /dev/null
+++ b/usr.sbin/i4b/g711conv/Makefile
@@ -0,0 +1,11 @@
+#---------------------------------------------------------------------------
+#
+# $FreeBSD$
+#
+# last edit-date: [Thu May 20 11:58:43 1999]
+#
+#---------------------------------------------------------------------------
+
+PROG= g711conv
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/i4b/g711conv/g711conv.1 b/usr.sbin/i4b/g711conv/g711conv.1
new file mode 100644
index 0000000..ca3845b
--- /dev/null
+++ b/usr.sbin/i4b/g711conv/g711conv.1
@@ -0,0 +1,92 @@
+.\"
+.\" Copyright (c) 1999 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: g711conv.1,v 1.3 1999/12/13 22:11:55 hm Exp $
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Mon Dec 13 22:54:33 1999]
+.\"
+.Dd March 15, 1999
+.Dt G711CONV 1
+.Os
+.Sh NAME
+.Nm g711conv
+.Nd conversions according to G.711
+.Sh SYNOPSIS
+.Nm
+.Op Fl a
+.Op Fl u
+.Op Fl P
+.Op Fl A
+.Op Fl R
+.Sh DESCRIPTION
+The
+.Nm
+utility
+is part of the isdn4bsd package and is used to convert between the A-Law and
+u-law formats as specified in ITU G.711. It is based on a freely available
+and freely usable reference implementation done by Sun Microsystems, Inc.
+.Pp
+The following options are available:
+.Bl -tag -width Ds
+.It Fl a
+Convert A-law to u-law
+.It Fl u
+Convert u-law to A-law
+.It Fl r
+Reverse bits before conversion
+.It Fl R
+Reverse bits after conversion
+.It Fl P
+Print the resulting conversion tables (as C-source) to stdout instead of
+doing the actual conversion.
+.El
+.Sh STANDARDS
+A-Law and u-Law conversions are specified in ITU Recommendation G.711.
+.Pp
+The reference implementation done by Sun Microsystems, Inc. is available
+from http://www.itu.int/itudoc/itu-t/rec/g/g700-799/refimpl.txt
+.Sh EXAMPLES
+The command:
+.Bd -literal -offset indent
+g711conv -P -a
+.Ed
+.Pp
+prints out the A-law to u-law conversion table as C-source to stdout.
+.Pp
+The command:
+.Bd -literal -offset indent
+cat max_headroom.ul | g711conv -u -R > /dev/i4btel0
+.Ed
+.Pp
+converts the u-law coded voice of Max Headroom to A-law, reverses the
+bits of the result and moves that to an active isdn4bsd telephone connection.
+.Sh AUTHORS
+The
+.Nm
+utility and this manpage were written by
+.An Hellmuth Michaelis Aq hm@kts.org
+based on the G.711 conversion reference code written by Sun Microsystems,
+Inc. and code contributed to isdn4bsd by Stefan Bethke.
diff --git a/usr.sbin/i4b/g711conv/g711conv.c b/usr.sbin/i4b/g711conv/g711conv.c
new file mode 100644
index 0000000..5c7857d
--- /dev/null
+++ b/usr.sbin/i4b/g711conv/g711conv.c
@@ -0,0 +1,306 @@
+/*
+ * Copyright (c) 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * ---
+ *
+ * The A-law to u-law and u-law to A-law conversion routines and tables
+ * were taken from the G.711 reference implementation from Sun and freely
+ * available as http://www.itu.int/itudoc/itu-t/rec/g/g700-799/refimpl.txt.
+ *
+ * Therefore for that part of the code, the following restrictions apply:
+ *
+ *
+ * This source code is a product of Sun Microsystems, Inc. and is provided
+ * for unrestricted use. Users may copy or modify this source code without
+ * charge.
+ *
+ * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
+ * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun source code is provided with no support and without any obligation on
+ * the part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ *
+ * ---
+ *
+ * The bitreverse table was contributed by Stefan Bethke.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * A-law / u-law conversions as specified in G.711
+ * -----------------------------------------------
+ *
+ * last edit-date: [Mon Dec 13 21:44:01 1999]
+ *
+ * $Id: g711conv.c,v 1.5 1999/12/13 21:25:24 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <machine/i4b_ioctl.h>
+
+/* copy from CCITT G.711 specifications */
+
+/* u- to A-law conversions */
+
+unsigned char _u2a[128] = {
+ 1, 1, 2, 2, 3, 3, 4, 4,
+ 5, 5, 6, 6, 7, 7, 8, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 27, 29, 31, 33, 34, 35, 36,
+ 37, 38, 39, 40, 41, 42, 43, 44,
+ 46, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, 58, 59, 60, 61, 62,
+ 64, 65, 66, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, 76, 77, 78, 79,
+ 81, 82, 83, 84, 85, 86, 87, 88,
+ 89, 90, 91, 92, 93, 94, 95, 96,
+ 97, 98, 99, 100, 101, 102, 103, 104,
+ 105, 106, 107, 108, 109, 110, 111, 112,
+ 113, 114, 115, 116, 117, 118, 119, 120,
+ 121, 122, 123, 124, 125, 126, 127, 128
+};
+
+/* A- to u-law conversions */
+
+unsigned char _a2u[128] = {
+ 1, 3, 5, 7, 9, 11, 13, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 32, 33, 33, 34, 34, 35, 35,
+ 36, 37, 38, 39, 40, 41, 42, 43,
+ 44, 45, 46, 47, 48, 48, 49, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57,
+ 58, 59, 60, 61, 62, 63, 64, 64,
+ 65, 66, 67, 68, 69, 70, 71, 72,
+ 73, 74, 75, 76, 77, 78, 79, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87,
+ 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103,
+ 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127
+};
+
+/* reverse bits (7->0, 6->1, 5->2 etc) for tx to / rx from ISDN */
+
+unsigned char bitreverse[256] = {
+ 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
+ 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
+ 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
+ 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
+ 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
+ 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
+ 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
+ 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
+ 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
+ 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
+ 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
+ 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
+ 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
+ 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
+ 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
+ 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
+};
+
+/* A-law to u-law conversion */
+
+unsigned char alaw2ulaw(unsigned char aval)
+{
+ aval &= 0xff;
+ return ((aval & 0x80) ? (0xFF ^ _a2u[aval ^ 0xD5]) :
+ (0x7F ^ _a2u[aval ^ 0x55]));
+}
+
+/* u-law to A-law conversion */
+
+unsigned char ulaw2alaw(unsigned char uval)
+{
+ uval &= 0xff;
+ return ((uval & 0x80) ? (0xD5 ^ (_u2a[0xFF ^ uval] - 1)) :
+ (0x55 ^ (_u2a[0x7F ^ uval] - 1)));
+}
+
+void
+usage(void)
+{
+ fprintf(stderr, "\n");
+ fprintf(stderr, "g711conv - do conversions according to ITU G.711, (version %d.%d.%d)\n",VERSION, REL, STEP);
+ fprintf(stderr, "usage: g711conv -a -r -R -u -P\n");
+ fprintf(stderr, " -a A-law to u-law conversion\n");
+ fprintf(stderr, " -r reverse bits before conversion\n");
+ fprintf(stderr, " -R reverse bits after conversion\n");
+ fprintf(stderr, " -u u-law to A-law conversion\n");
+ fprintf(stderr, " -P print conversion table as C source\n");
+ fprintf(stderr, "\n");
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ int i;
+ int c;
+ int opt_a = 0;
+ int opt_u = 0;
+ int opt_r = 0;
+ int opt_P = 0;
+ int opt_R = 0;
+ unsigned char uc;
+
+ while ((c = getopt(argc, argv, "aurPR?")) != -1)
+ {
+ switch(c)
+ {
+ case 'a':
+ opt_a = 1;
+ break;
+
+ case 'u':
+ opt_u = 1;
+ break;
+
+ case 'r':
+ opt_r = 1;
+ break;
+
+ case 'R':
+ opt_R = 1;
+ break;
+
+ case 'P':
+ opt_P = 1;
+ break;
+
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if((opt_a + opt_u) > 1)
+ usage();
+
+ if(opt_P)
+ {
+ printf("\n/* ");
+
+ if((opt_a + opt_u) == 0)
+ printf("No Conversion");
+
+ if(opt_a)
+ printf("A-law to u-law conversion");
+
+ if(opt_u)
+ printf("u-law to A-law conversion");
+
+ if(opt_r)
+ printf(", reverse bits BEFORE conversion");
+
+ if(opt_R)
+ printf(", reverse bits AFTER conversion");
+
+ if(opt_a)
+ {
+ printf(" */\n\nunsigned char a2u_tab[256] = {");
+ }
+ else if(opt_u)
+ {
+ printf(" */\n\nunsigned char u2a_tab[256] = {");
+ }
+ else
+ {
+ printf(" */\n\nunsigned char table[256] = {");
+ }
+
+ for(i=0; i < 256; i++)
+ {
+ uc = i;
+
+ if(!(i % 8))
+ printf("\n/* %02x */\t", i);
+
+ if(opt_r)
+ uc = bitreverse[uc];
+
+ if(opt_u)
+ uc = ulaw2alaw(uc);
+
+ if(opt_a)
+ uc = alaw2ulaw(uc);
+
+ if(opt_R)
+ uc = bitreverse[uc];
+
+ if(i == 255)
+ printf("0x%02x", uc);
+ else
+ printf("0x%02x, ", uc);
+ }
+ printf("\n};\n");
+ }
+ else
+ {
+ unsigned char ib[1];
+
+ while(fread(ib, 1, 1, stdin) == 1)
+ {
+ if(opt_r)
+ ib[0] = bitreverse[ib[0]];
+
+ if(opt_u)
+ ib[0] = ulaw2alaw(ib[0]);
+
+ if(opt_a)
+ ib[0] = alaw2ulaw(ib[0]);
+
+ if(opt_R)
+ ib[0] = bitreverse[ib[0]];
+
+ fwrite(ib, 1, 1, stdout);
+ }
+ }
+ return(0);
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnd/Makefile b/usr.sbin/i4b/isdnd/Makefile
new file mode 100644
index 0000000..9ef7e03
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/Makefile
@@ -0,0 +1,33 @@
+# $FreeBSD$
+
+PROG= isdnd
+MAN= isdnd.rc.5 isdnd.rates.5 isdnd.acct.5 isdnd.8
+SRCS= rc_parse.y rc_scan.l main.c rc_config.c log.c curses.c \
+ process.c rates.c msghdl.c fsm.c support.c timer.c \
+ exec.c dial.c monitor.c pcause.c controller.c alias.c \
+ y.tab.h holiday.c
+
+COPTS+= -I. -I${.CURDIR}/../isdnmonitor -I${.CURDIR}/../isdntel -I${.CURDIR}
+
+# compile debug support
+COPTS+= -DDEBUG
+
+# enable rtprio usage
+COPTS+= -DUSE_RTPRIO
+
+.include "${.CURDIR}/../Makefile.inc"
+
+.if !defined(I4B_WITHOUT_CURSES)
+COPTS+= -DUSE_CURSES
+DPADD= ${LIBCURSES}
+LDADD= -lcurses
+.endif
+
+.if defined(I4B_EXTERNAL_MONITOR)
+COPTS+= -DI4B_EXTERNAL_MONITOR
+.if defined(I4B_NOTCPIP_MONITOR)
+COPTS+= -DI4B_NOTCPIP_MONITOR
+.endif
+.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/i4b/isdnd/alias.c b/usr.sbin/i4b/isdnd/alias.c
new file mode 100644
index 0000000..a775fc9
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/alias.c
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * isdnd - common aliasfile handling
+ * =================================
+ *
+ * NOTE: this has to stay in sync with isdntel/alias.c to be able
+ * to share a common aliasfile!
+ *
+ * $Id: alias.c,v 1.8 1999/12/13 21:25:24 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:45:19 1999]
+ *
+ *----------------------------------------------------------------------------*/
+
+#include "isdnd.h"
+
+static struct alias *firsta = NULL;
+
+#define MAXBUFSZ 256
+
+static void free_alias(struct alias *ptr);
+
+/*---------------------------------------------------------------------------*
+ * read in and init aliases
+ *---------------------------------------------------------------------------*/
+void
+init_alias(char *filename)
+{
+ FILE *fp;
+ unsigned char buffer[MAXBUFSZ + 1];
+ unsigned char number[MAXBUFSZ + 1];
+ unsigned char name[MAXBUFSZ + 1];
+ unsigned char *s, *d;
+ struct alias *newa = NULL;
+ struct alias *lasta = NULL;
+
+ firsta = NULL;
+
+ if((fp = fopen(filename, "r")) == NULL)
+ {
+ log(LL_ERR, "init_alias: error opening aliasfile %s: %s!", filename, strerror(errno));
+ exit(1);
+ }
+
+ while((fgets(buffer, MAXBUFSZ, fp)) != NULL)
+ {
+ if(buffer[0] == '#' || buffer[0] == ' ' ||
+ buffer[0] == '\t' || buffer[0] == '\n')
+ {
+ continue;
+ }
+
+ s = buffer;
+ d = number;
+
+ while(*s && (isdigit(*s)))
+ *d++ = *s++;
+
+ *d = '\0';
+
+ while(*s && (isspace(*s)))
+ s++;
+
+ d = name;
+
+ while(*s && (isprint(*s)))
+ *d++ = *s++;
+
+ *d = '\0';
+
+ if((strlen(number) > 1) && (strlen(name) > 1))
+ {
+ if((newa = (struct alias *) malloc(sizeof(struct alias))) == NULL)
+ {
+ log(LL_ERR, "init_alias: malloc failed for struct alias!\n");
+ exit(1);
+ }
+
+ if((newa->number = (char *) malloc(strlen(number)+1)) == NULL)
+ {
+ log(LL_ERR, "init_alias: malloc failed for number alias!\n");
+ exit(1);
+ }
+
+ if((newa->name = (char *) malloc(strlen(name)+1)) == NULL)
+ {
+ log(LL_ERR, "init_alias: malloc failed for name alias!\n");
+ exit(1);
+ }
+
+ strcpy(newa->name, name);
+ strcpy(newa->number, number);
+ newa->next = NULL;
+
+ if(firsta == NULL)
+ {
+ firsta = newa;
+ }
+ else
+ {
+ lasta->next = newa;
+ }
+ lasta = newa;
+ }
+ }
+ fclose(fp);
+}
+
+/*---------------------------------------------------------------------------*
+ * free all aliases
+ *---------------------------------------------------------------------------*/
+void
+free_aliases(void)
+{
+ free_alias(firsta);
+}
+
+/*---------------------------------------------------------------------------*
+ * free aliases
+ *---------------------------------------------------------------------------*/
+static void
+free_alias(struct alias *ptr)
+{
+
+ if(ptr == NULL)
+ return;
+
+ if(ptr->next != NULL)
+ free_alias(ptr->next);
+
+ if(ptr->number != NULL)
+ free(ptr->number);
+
+ if(ptr->name != NULL)
+ free(ptr->name);
+
+ free(ptr);
+}
+
+/*---------------------------------------------------------------------------*
+ * try to find alias for number. if no alias found, return number.
+ *---------------------------------------------------------------------------*/
+char *
+get_alias(char *number)
+{
+ struct alias *ca = NULL;
+
+ if(firsta == NULL)
+ return(number);
+
+ ca = firsta;
+
+ for(;;)
+ {
+ if(strlen(number) == strlen(ca->number))
+ {
+ if(!(strcmp(number, ca->number)))
+ return(ca->name);
+ }
+ if(ca->next == NULL)
+ break;
+ ca = ca->next;
+ }
+ return(number);
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnd/config.h b/usr.sbin/i4b/isdnd/config.h
new file mode 100644
index 0000000..a4b05f1
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/config.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 1997, 2001 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * i4b daemon - compile time configuration header file
+ * ---------------------------------------------------
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon May 21 11:21:15 2001]
+ *
+ *---------------------------------------------------------------------------*/
+
+#ifndef _CONFIG_H_
+#define _CONFIG_H_
+
+/* general values */
+
+#define UMASK 022 /* file creation perm mask */
+#define CFG_ENTRY_MAX 60 /* max no of config entries */
+#define ISDN_CTRL_MAX 4 /* max no of controllers */
+#define MAX_RE 8 /* max regular expression entries */
+
+/* monitor max values */
+
+#define MAX_MHOSTS 8 /* max allowed monitor hosts */
+
+/* timouts */
+
+#define TIMEOUT_CONNECT_ACTIVE 30 /* seconds to wait for MSG_CONN_ACT */
+
+/* utility programs forked */
+
+#define REGPROG_DEF "program" /* default program to use for regexpr */
+#define ANSWERPROG_DEF "answer" /* default telephone answer program */
+
+#endif /* _CONFIG_H_ */
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnd/controller.c b/usr.sbin/i4b/isdnd/controller.c
new file mode 100644
index 0000000..16a60c8
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/controller.c
@@ -0,0 +1,525 @@
+/*
+ * Copyright (c) 1997, 2001 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * i4b daemon - controller state support routines
+ * ----------------------------------------------
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Sun Oct 21 11:02:15 2001]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#include "isdnd.h"
+
+static int
+init_controller_state(int controller, int ctrl_type, int card_type, int tei, int nbch);
+
+/*---------------------------------------------------------------------------*
+ * get name of a controller
+ *---------------------------------------------------------------------------*/
+const char *
+name_of_controller(int ctrl_type, int card_type)
+{
+ static char *passive_card[] = {
+ "Teles S0/8",
+ "Teles S0/16",
+ "Teles S0/16.3",
+ "AVM A1 or Fritz!Card",
+ "Teles S0/16.3 PnP",
+ "Creatix S0 PnP",
+ "USRobotics Sportster ISDN TA",
+ "Dr. Neuhaus NICCY Go@",
+ "Sedlbauer win speed",
+ "Dynalink IS64PH",
+ "ISDN Master, MasterII or Blaster",
+ "AVM PCMCIA Fritz!Card",
+ "ELSA QuickStep 1000pro/ISA",
+ "ELSA QuickStep 1000pro/PCI",
+ "Siemens I-Talk",
+ "ELSA MicroLink ISDN/MC",
+ "ELSA MicroLink MCall",
+ "ITK ix1 micro",
+ "AVM Fritz!Card PCI",
+ "ELSA PCC-16",
+ "AVM Fritz!Card PnP",
+ "Siemens I-Surf 2.0 PnP",
+ "Asuscom ISDNlink 128K PnP",
+ "ASUSCOM P-IN100-ST-D (Winbond W6692)",
+ "Teles S0/16.3c PnP",
+ "AcerISDN P10 PnP",
+ "TELEINT ISDN SPEED No. 1",
+ "Cologne Chip HFC-S PCI based",
+ "Traverse Tech NETjet-S / Teles PCI-TJ",
+ "Eicon.Diehl DIVA 2.0 / 2.02 ISA PnP",
+ "Compaq Microcom 610",
+ "AVM Fritz!Card PCI Version 2",
+ };
+
+ static char *daic_card[] = {
+ "EICON.Diehl S",
+ "EICON.Diehl SX/SXn",
+ "EICON.Diehl SCOM",
+ "EICON.Diehl QUADRO",
+ };
+
+ static char *capi_card[] = {
+ "AVM T1 PCI",
+ "AVM B1 PCI",
+ "AVM B1 ISA",
+ };
+
+ if(ctrl_type == CTRL_PASSIVE)
+ {
+ int index = card_type - CARD_TYPEP_8;
+ if (index >= 0 && index < (sizeof passive_card / sizeof passive_card[0]))
+ return passive_card[index];
+ }
+ else if(ctrl_type == CTRL_DAIC)
+ {
+ int index = card_type - CARD_TYPEA_DAIC_S;
+ if (index >= 0 && index < (sizeof daic_card / sizeof daic_card[0] ))
+ return daic_card[index];
+ }
+ else if(ctrl_type == CTRL_TINADD)
+ {
+ return "Stollmann tina-dd";
+ }
+ else if(ctrl_type == CTRL_CAPI)
+ {
+ int index = card_type - CARD_TYPEC_AVM_T1_PCI;
+ if (index >= 0 && index < (sizeof capi_card / sizeof capi_card[0] ))
+ return capi_card[index];
+ }
+
+ return "unknown card type";
+}
+
+/*---------------------------------------------------------------------------*
+ * init controller state array
+ *---------------------------------------------------------------------------*/
+void
+init_controller(void)
+{
+ int i;
+ int max = 1;
+ msg_ctrl_info_req_t mcir;
+
+ for(i=0; i < max; i++)
+ {
+ mcir.controller = i;
+
+ if((ioctl(isdnfd, I4B_CTRL_INFO_REQ, &mcir)) < 0)
+ {
+ log(LL_ERR, "init_controller: ioctl I4B_CTRL_INFO_REQ failed: %s", strerror(errno));
+ do_exit(1);
+ }
+
+ if((ncontroller = max = mcir.ncontroller) == 0)
+ {
+ log(LL_ERR, "init_controller: no ISDN controller found!");
+ do_exit(1);
+ }
+
+ if(mcir.ctrl_type == -1 || mcir.card_type == -1)
+ {
+ log(LL_ERR, "init_controller: ctrl/card is invalid!");
+ do_exit(1);
+ }
+
+ /* init controller tab */
+
+ if((init_controller_state(i, mcir.ctrl_type, mcir.card_type, mcir.tei, mcir.nbch)) == ERROR)
+ {
+ log(LL_ERR, "init_controller: init_controller_state for controller %d failed", i);
+ do_exit(1);
+ }
+ }
+ DBGL(DL_RCCF, (log(LL_DBG, "init_controller: found %d ISDN controller(s)", max)));
+}
+
+/*--------------------------------------------------------------------------*
+ * init controller state table entry
+ *--------------------------------------------------------------------------*/
+static int
+init_controller_state(int controller, int ctrl_type, int card_type, int tei,
+ int nbch)
+{
+ int i;
+
+ if((controller < 0) || (controller >= ncontroller))
+ {
+ log(LL_ERR, "init_controller_state: invalid controller number [%d]!", controller);
+ return(ERROR);
+ }
+
+ /* init controller tab */
+
+ switch (ctrl_type) {
+ case CTRL_PASSIVE:
+ if((card_type > CARD_TYPEP_UNK) &&
+ (card_type <= CARD_TYPEP_MAX))
+ {
+ isdn_ctrl_tab[controller].ctrl_type = ctrl_type;
+ isdn_ctrl_tab[controller].card_type = card_type;
+ isdn_ctrl_tab[controller].state = CTRL_UP;
+ }
+ else
+ {
+ log(LL_ERR, "init_controller_state: unknown card type %d", card_type);
+ return(ERROR);
+ }
+ break;
+
+ case CTRL_DAIC:
+ isdn_ctrl_tab[controller].ctrl_type = ctrl_type;
+ isdn_ctrl_tab[controller].card_type = card_type;
+ isdn_ctrl_tab[controller].state = CTRL_DOWN;
+ break;
+
+ case CTRL_TINADD:
+ isdn_ctrl_tab[controller].ctrl_type = ctrl_type;
+ isdn_ctrl_tab[controller].card_type = 0;
+ isdn_ctrl_tab[controller].state = CTRL_DOWN;
+ break;
+
+ case CTRL_CAPI:
+ isdn_ctrl_tab[controller].ctrl_type = ctrl_type;
+ isdn_ctrl_tab[controller].card_type = card_type;
+ isdn_ctrl_tab[controller].state = CTRL_UP;
+ break;
+
+ default:
+ log(LL_ERR, "init_controller_state: unknown controller type %d", ctrl_type);
+ return(ERROR);
+ }
+
+ isdn_ctrl_tab[controller].nbch = nbch;
+ isdn_ctrl_tab[controller].freechans = nbch;
+ for (i = 0; i < nbch; i++)
+ isdn_ctrl_tab[controller].stateb[i] = CHAN_IDLE;
+ isdn_ctrl_tab[controller].tei = tei;
+ isdn_ctrl_tab[controller].l1stat = LAYER_IDLE;
+ isdn_ctrl_tab[controller].l2stat = LAYER_IDLE;
+
+ log(LL_DMN, "init_controller_state: controller %d is %s",
+ controller,
+ name_of_controller(isdn_ctrl_tab[controller].ctrl_type,
+ isdn_ctrl_tab[controller].card_type));
+
+ return(GOOD);
+}
+
+/*--------------------------------------------------------------------------*
+ * init active or capi controller
+ *--------------------------------------------------------------------------*/
+void
+init_active_controller(void)
+{
+ int ret;
+ int unit = 0;
+ int controller;
+ char cmdbuf[MAXPATHLEN+128];
+
+ for(controller = 0; controller < ncontroller; controller++)
+ {
+ if(isdn_ctrl_tab[controller].ctrl_type == CTRL_TINADD)
+ {
+ DBGL(DL_RCCF, (log(LL_DBG, "init_active_controller, tina-dd %d: executing [%s %d]", unit, tinainitprog, unit)));
+
+ snprintf(cmdbuf, sizeof(cmdbuf), "%s %d", tinainitprog, unit);
+
+ if((ret = system(cmdbuf)) != 0)
+ {
+ log(LL_ERR, "init_active_controller, tina-dd %d: %s returned %d!", unit, tinainitprog, ret);
+ do_exit(1);
+ }
+ }
+
+ /*
+ * Generic microcode loading. If a controller has
+ * defined a microcode file, load it using the
+ * I4B_CTRL_DOWNLOAD ioctl.
+ */
+
+ if(isdn_ctrl_tab[controller].firmware != NULL)
+ {
+ int fd, ret;
+ struct isdn_dr_prot idp;
+ struct isdn_download_request idr;
+
+ fd = open(isdn_ctrl_tab[controller].firmware, O_RDONLY);
+ if (fd < 0) {
+ log(LL_ERR, "init_active_controller %d: open %s: %s!",
+ controller, isdn_ctrl_tab[controller].firmware,
+ strerror(errno));
+ do_exit(1);
+ }
+
+ idp.bytecount = lseek(fd, 0, SEEK_END);
+ idp.microcode = mmap(0, idp.bytecount, PROT_READ,
+ MAP_SHARED, fd, 0);
+ if (idp.microcode == MAP_FAILED) {
+ log(LL_ERR, "init_active_controller %d: mmap %s: %s!",
+ controller, isdn_ctrl_tab[controller].firmware,
+ strerror(errno));
+ do_exit(1);
+ }
+
+ DBGL(DL_RCCF, (log(LL_DBG, "init_active_controller %d: loading firmware from [%s]", controller, isdn_ctrl_tab[controller].firmware)));
+
+ idr.controller = controller;
+ idr.numprotos = 1;
+ idr.protocols = &idp;
+
+ ret = ioctl(isdnfd, I4B_CTRL_DOWNLOAD, &idr, sizeof(idr));
+ if (ret) {
+ log(LL_ERR, "init_active_controller %d: load %s: %s!",
+ controller, isdn_ctrl_tab[controller].firmware,
+ strerror(errno));
+ do_exit(1);
+ }
+
+ munmap(idp.microcode, idp.bytecount);
+ close(fd);
+ }
+ }
+}
+
+/*--------------------------------------------------------------------------*
+ * init controller D-channel ISDN protocol
+ *--------------------------------------------------------------------------*/
+void
+init_controller_protocol(void)
+{
+ int controller;
+ msg_prot_ind_t mpi;
+
+ for(controller = 0; controller < ncontroller; controller++)
+ {
+ mpi.controller = controller;
+ mpi.protocol = isdn_ctrl_tab[controller].protocol;
+
+ if((ioctl(isdnfd, I4B_PROT_IND, &mpi)) < 0)
+ {
+ log(LL_ERR, "init_controller_protocol: ioctl I4B_PROT_IND failed: %s", strerror(errno));
+ do_exit(1);
+ }
+ }
+}
+
+/*--------------------------------------------------------------------------*
+ * set controller state to UP/DOWN
+ *--------------------------------------------------------------------------*/
+int
+set_controller_state(int controller, int state)
+{
+ if((controller < 0) || (controller >= ncontroller))
+ {
+ log(LL_ERR, "set_controller_state: invalid controller number [%d]!", controller);
+ return(ERROR);
+ }
+
+ if(state == CTRL_UP)
+ {
+ isdn_ctrl_tab[controller].state = CTRL_UP;
+ DBGL(DL_CNST, (log(LL_DBG, "set_controller_state: controller [%d] set UP!", controller)));
+ }
+ else if (state == CTRL_DOWN)
+ {
+ isdn_ctrl_tab[controller].state = CTRL_DOWN;
+ DBGL(DL_CNST, (log(LL_DBG, "set_controller_state: controller [%d] set DOWN!", controller)));
+ }
+ else
+ {
+ log(LL_ERR, "set_controller_state: invalid controller state [%d]!", state);
+ return(ERROR);
+ }
+ return(GOOD);
+}
+
+/*--------------------------------------------------------------------------*
+ * get controller state
+ *--------------------------------------------------------------------------*/
+int
+get_controller_state(int controller)
+{
+ if((controller < 0) || (controller >= ncontroller))
+ {
+ log(LL_ERR, "set_controller_state: invalid controller number [%d]!", controller);
+ return(ERROR);
+ }
+ return(isdn_ctrl_tab[controller].state);
+}
+
+/*--------------------------------------------------------------------------*
+ * decrement number of free channels for controller
+ *--------------------------------------------------------------------------*/
+int
+decr_free_channels(int controller)
+{
+ if((controller < 0) || (controller >= ncontroller))
+ {
+ log(LL_ERR, "decr_free_channels: invalid controller number [%d]!", controller);
+ return(ERROR);
+ }
+ if(isdn_ctrl_tab[controller].freechans > 0)
+ {
+ (isdn_ctrl_tab[controller].freechans)--;
+ DBGL(DL_CNST, (log(LL_DBG, "decr_free_channels: ctrl %d, now %d chan free", controller, isdn_ctrl_tab[controller].freechans)));
+ return(GOOD);
+ }
+ else
+ {
+ log(LL_ERR, "decr_free_channels: controller [%d] already 0 free chans!", controller);
+ return(ERROR);
+ }
+}
+
+/*--------------------------------------------------------------------------*
+ * increment number of free channels for controller
+ *--------------------------------------------------------------------------*/
+int
+incr_free_channels(int controller)
+{
+ if((controller < 0) || (controller >= ncontroller))
+ {
+ log(LL_ERR, "incr_free_channels: invalid controller number [%d]!", controller);
+ return(ERROR);
+ }
+ if(isdn_ctrl_tab[controller].freechans < isdn_ctrl_tab[controller].nbch)
+ {
+ (isdn_ctrl_tab[controller].freechans)++;
+ DBGL(DL_CNST, (log(LL_DBG, "incr_free_channels: ctrl %d, now %d chan free", controller, isdn_ctrl_tab[controller].freechans)));
+ return(GOOD);
+ }
+ else
+ {
+ log(LL_ERR, "incr_free_channels: controller [%d] already %d free chans!", controller, isdn_ctrl_tab[controller].nbch);
+ return(ERROR);
+ }
+}
+
+/*--------------------------------------------------------------------------*
+ * get number of free channels for controller
+ *--------------------------------------------------------------------------*/
+int
+get_free_channels(int controller)
+{
+ if((controller < 0) || (controller >= ncontroller))
+ {
+ log(LL_ERR, "get_free_channels: invalid controller number [%d]!", controller);
+ return(ERROR);
+ }
+ DBGL(DL_CNST, (log(LL_DBG, "get_free_channels: ctrl %d, %d chan free", controller, isdn_ctrl_tab[controller].freechans)));
+ return(isdn_ctrl_tab[controller].freechans);
+}
+
+/*--------------------------------------------------------------------------*
+ * set channel state to busy
+ *--------------------------------------------------------------------------*/
+int
+set_channel_busy(int controller, int channel)
+{
+ if((controller < 0) || (controller >= ncontroller))
+ {
+ log(LL_ERR, "set_channel_busy: invalid controller number [%d]!", controller);
+ return(ERROR);
+ }
+
+ if ((channel < 0) || (channel >= isdn_ctrl_tab[controller].nbch))
+ {
+ log(LL_ERR, "set_channel_busy: controller [%d] invalid channel [%d]!", controller, channel);
+ return(ERROR);
+ }
+
+ if(isdn_ctrl_tab[controller].stateb[channel] == CHAN_RUN)
+ {
+ DBGL(DL_CNST, (log(LL_DBG, "set_channel_busy: controller [%d] channel B%d already busy!", controller, channel+1)));
+ }
+ else
+ {
+ isdn_ctrl_tab[controller].stateb[channel] = CHAN_RUN;
+ DBGL(DL_CNST, (log(LL_DBG, "set_channel_busy: controller [%d] channel B%d set to BUSY!", controller, channel+1)));
+ }
+ return(GOOD);
+}
+
+/*--------------------------------------------------------------------------*
+ * set channel state to idle
+ *--------------------------------------------------------------------------*/
+int
+set_channel_idle(int controller, int channel)
+{
+ if((controller < 0) || (controller >= ncontroller))
+ {
+ log(LL_ERR, "set_channel_idle: invalid controller number [%d]!", controller);
+ return(ERROR);
+ }
+
+ if ((channel < 0) || (channel >= isdn_ctrl_tab[controller].nbch))
+ {
+ log(LL_ERR, "set_channel_busy: controller [%d] invalid channel [%d]!", controller, channel);
+ return(ERROR);
+ }
+
+ if (isdn_ctrl_tab[controller].stateb[channel] == CHAN_IDLE)
+ {
+ DBGL(DL_CNST, (log(LL_DBG, "set_channel_idle: controller [%d] channel B%d already idle!", controller, channel+1)));
+ }
+ else
+ {
+ isdn_ctrl_tab[controller].stateb[channel] = CHAN_IDLE;
+ DBGL(DL_CNST, (log(LL_DBG, "set_channel_idle: controller [%d] channel B%d set to IDLE!", controller, channel+1)));
+ }
+ return(GOOD);
+}
+
+/*--------------------------------------------------------------------------*
+ * return channel state
+ *--------------------------------------------------------------------------*/
+int
+ret_channel_state(int controller, int channel)
+{
+ if((controller < 0) || (controller >= ncontroller))
+ {
+ log(LL_ERR, "ret_channel_state: invalid controller number [%d]!", controller);
+ return(ERROR);
+ }
+
+ if ((channel < 0) || (channel >= isdn_ctrl_tab[controller].nbch))
+ {
+ log(LL_ERR, "set_channel_busy: controller [%d] invalid channel [%d]!", controller, channel);
+ return(ERROR);
+ }
+
+ return(isdn_ctrl_tab[controller].stateb[channel]);
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnd/curses.c b/usr.sbin/i4b/isdnd/curses.c
new file mode 100644
index 0000000..19e7af7
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/curses.c
@@ -0,0 +1,891 @@
+/*
+ * Copyright (c) 1997, 2002 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * i4b daemon - curses fullscreen output
+ * -------------------------------------
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Tue Mar 26 14:35:33 2002]
+ *
+ *---------------------------------------------------------------------------*/
+
+#ifdef USE_CURSES
+
+#include "isdnd.h"
+
+#define CHPOS(cfgp) (((cfgp)->isdncontrollerused*2) + (cfgp)->isdnchannelused)
+
+static void display_budget(void);
+static void display_cards(void);
+static void menuexit(WINDOW *menu_w);
+
+/*---------------------------------------------------------------------------*
+ * init curses fullscreen display
+ *---------------------------------------------------------------------------*/
+void
+init_screen(void)
+{
+ char buffer[512];
+ int uheight, lheight;
+ int i, j;
+ cfg_entry_t *p;
+
+ initscr(); /* curses init */
+
+ if((COLS < 80) || (LINES < 24))
+ {
+ log(LL_ERR, "ERROR, minimal screensize must be 80x24, is %dx%d, terminating!",COLS, LINES);
+ do_exit(1);
+ }
+
+ noecho();
+ raw();
+
+ uheight = ncontroller * 2; /* cards * b-channels */
+ lheight = LINES - uheight - 6 + 1; /* rest of display */
+
+ if((upper_w = newwin(uheight, COLS, UPPER_B, 0)) == NULL)
+ {
+ log(LL_ERR, "ERROR, curses init upper window, terminating!");
+ exit(1);
+ }
+
+ if((mid_w = newwin(1, COLS, UPPER_B+uheight+1, 0)) == NULL)
+ {
+ log(LL_ERR, "ERROR, curses init mid window, terminating!");
+ exit(1);
+ }
+
+ if((lower_w = newwin(lheight, COLS, UPPER_B+uheight+3, 0)) == NULL)
+ {
+ log(LL_ERR, "ERROR, curses init lower window, LINES = %d, lheight = %d, uheight = %d, terminating!", LINES, lheight, uheight);
+ exit(1);
+ }
+
+ scrollok(lower_w, 1);
+
+ snprintf(buffer, sizeof(buffer), "----- isdn controller channel state ------------- isdnd %02d.%02d.%d [pid %d] -", VERSION, REL, STEP, (int)getpid());
+
+ while(strlen(buffer) < COLS && strlen(buffer) < sizeof(buffer) - 1)
+ strcat(buffer, "-");
+
+ move(0, 0);
+ standout();
+ addstr(buffer);
+ standend();
+
+ move(1, 0);
+ /* 01234567890123456789012345678901234567890123456789012345678901234567890123456789 */
+ addstr("c tei b remote iface dir outbytes obps inbytes ibps units");
+
+ snprintf(buffer, sizeof(buffer), "----- isdn userland interface state ------------------------------------------");
+ while(strlen(buffer) < COLS && strlen(buffer) < sizeof(buffer) - 1)
+ strcat(buffer, "-");
+
+ move(uheight+2, 0);
+ standout();
+ addstr(buffer);
+ standend();
+
+ snprintf(buffer, sizeof(buffer), "----- isdnd logfile display --------------------------------------------------");
+ while(strlen(buffer) < COLS && strlen(buffer) < sizeof(buffer) - 1)
+ strcat(buffer, "-");
+
+ move(uheight+4, 0);
+ standout();
+ addstr(buffer);
+ standend();
+
+ refresh();
+
+ for(i=0, j=0; i <= ncontroller; i++, j+=2)
+ {
+ if(isdn_ctrl_tab[i].tei == -1)
+ mvwprintw(upper_w, j, H_CNTL, "%d --- 1 ", i);
+ else
+ mvwprintw(upper_w, j, H_CNTL, "%d %3d 1 ", i, isdn_ctrl_tab[i].tei);
+ mvwprintw(upper_w, j+1, H_CNTL, " L12 2 ");
+ }
+ wrefresh(upper_w);
+
+ for(i=0, j=0; i < nentries; i++) /* walk thru all entries */
+ {
+ p = &cfg_entry_tab[i]; /* get ptr to enry */
+
+ mvwprintw(mid_w, 0, j, "%s%d ", bdrivername(p->usrdevicename), p->usrdeviceunit);
+
+ p->fs_position = j;
+
+ j += ((strlen(bdrivername(p->usrdevicename)) + (p->usrdeviceunit > 9 ? 2 : 1) + 1));
+ }
+ wrefresh(mid_w);
+
+ wmove(lower_w, 0, 0);
+ wrefresh(lower_w);
+
+ curses_ready = 1;
+}
+
+/*---------------------------------------------------------------------------*
+ * curses menu for fullscreen command mode
+ *---------------------------------------------------------------------------*/
+void
+do_menu(void)
+{
+ static char *menu[WMITEMS] =
+ {
+ "1 - (D)isplay refresh",
+ "2 - (H)angup (choose a channel)",
+ "3 - (R)eread config file",
+ "4 - (S)how card types",
+ "5 - (B)udget information",
+ "6 - (Q)uit the program",
+ };
+
+ WINDOW *menu_w;
+ int c;
+ int mpos;
+ fd_set set;
+ struct timeval timeout;
+
+ /* create a new window in the lower screen area */
+
+ if((menu_w = newwin(WMENU_HGT, WMENU_LEN, WMENU_POSLN, WMENU_POSCO )) == NULL)
+ {
+ log(LL_WRN, "ERROR, curses init menu window!");
+ return;
+ }
+
+ /* create a border around the window */
+
+ box(menu_w, '|', '-');
+
+ /* add a title */
+
+ wstandout(menu_w);
+ mvwaddstr(menu_w, 0, (WMENU_LEN / 2) - (strlen(WMENU_TITLE) / 2), WMENU_TITLE);
+ wstandend(menu_w);
+
+ /* fill the window with the menu options */
+
+ for(mpos=0; mpos <= (WMITEMS-1); mpos++)
+ mvwaddstr(menu_w, mpos + 2, 2, menu[mpos]);
+
+ /* highlight the first menu option */
+
+ mpos = 0;
+ wstandout(menu_w);
+ mvwaddstr(menu_w, mpos + 2, 2, menu[mpos]);
+ wstandend(menu_w);
+
+ /* input loop */
+
+ for(;;)
+ {
+ wrefresh(menu_w);
+
+ FD_ZERO(&set);
+ FD_SET(STDIN_FILENO, &set);
+ timeout.tv_sec = WMTIMEOUT;
+ timeout.tv_usec = 0;
+
+ /* if no char is available within timeout, exit menu*/
+
+ if((select(STDIN_FILENO + 1, &set, NULL, NULL, &timeout)) <= 0)
+ goto mexit;
+
+ c = wgetch(menu_w);
+
+ switch(c)
+ {
+ case ' ':
+ case '\t': /* hilite next option */
+ mvwaddstr(menu_w, mpos + 2, 2, menu[mpos]);
+ mpos++;
+ if(mpos >= WMITEMS)
+ mpos = 0;
+ wstandout(menu_w);
+ mvwaddstr(menu_w, mpos + 2, 2, menu[mpos]);
+ wstandend(menu_w);
+ break;
+
+ case ('0'+WBUDGET+1): /* display budget info */
+ case 'B':
+ case 'b':
+ display_budget();
+ goto mexit;
+
+ case ('0'+WREFRESH+1): /* display refresh */
+ case 'D':
+ case 'd':
+ wrefresh(curscr);
+ goto mexit;
+
+ case ('0'+WQUIT+1): /* quit program */
+ case 'Q':
+ case 'q':
+ menuexit(menu_w);
+ do_exit(0);
+ goto mexit;
+
+ case ('0'+WHANGUP+1): /* hangup connection */
+ case 'H':
+ case 'h':
+ display_chans();
+ goto mexit;
+
+ case ('0'+WREREAD+1): /* reread config file */
+ case 'R':
+ case 'r':
+ rereadconfig(42);
+ goto mexit;
+
+ case ('0'+WSHOW+1): /* reread config file */
+ case 'S':
+ case 's':
+ display_cards();
+ goto mexit;
+
+ case '\n':
+ case '\r': /* exec highlighted option */
+ switch(mpos)
+ {
+ case WREFRESH:
+ wrefresh(curscr);
+ break;
+
+ case WQUIT:
+ menuexit(menu_w);
+ do_exit(0);
+ break;
+
+ case WHANGUP:
+ display_chans();
+ break;
+
+ case WREREAD:
+ rereadconfig(42);
+ break;
+
+ case WBUDGET:
+ display_budget();
+ break;
+
+ case WSHOW:
+ display_cards();
+ break;
+ }
+ goto mexit;
+ break;
+
+ default:
+ goto mexit;
+ break;
+ }
+ }
+
+mexit:
+ menuexit(menu_w);
+}
+
+static void
+menuexit(WINDOW *menu_w)
+{
+ int uheight = ncontroller * 2; /* cards * b-channels */
+ char buffer[512];
+
+ /* delete the menu window */
+
+ delwin(menu_w);
+
+ /* re-display the original lower window contents */
+
+ touchwin(mid_w);
+ wrefresh(mid_w);
+
+ touchwin(lower_w);
+ wrefresh(lower_w);
+
+ touchwin(upper_w);
+ wrefresh(upper_w);
+
+ move(1, 0);
+ /* 01234567890123456789012345678901234567890123456789012345678901234567890123456789 */
+ addstr("c tei b remote iface dir outbytes obps inbytes ibps units");
+
+ sprintf(buffer, "----- isdn userland interface state ------------------------------------------");
+ while(strlen(buffer) < COLS)
+ strcat(buffer, "-");
+
+ move(uheight+2, 0);
+ standout();
+ addstr(buffer);
+ standend();
+
+ sprintf(buffer, "----- isdnd logfile display --------------------------------------------------");
+ while(strlen(buffer) < COLS)
+ strcat(buffer, "-");
+
+ move(uheight+4, 0);
+ standout();
+ addstr(buffer);
+ standend();
+
+ refresh();
+}
+
+/*---------------------------------------------------------------------------*
+ * display the charge in units
+ *---------------------------------------------------------------------------*/
+void
+display_charge(cfg_entry_t *cep)
+{
+ mvwprintw(upper_w, CHPOS(cep), H_UNITS, "%d", cep->charge);
+ wclrtoeol(upper_w);
+ wrefresh(upper_w);
+}
+
+/*---------------------------------------------------------------------------*
+ * display the calculated charge in units
+ *---------------------------------------------------------------------------*/
+void
+display_ccharge(cfg_entry_t *cep, int units)
+{
+ mvwprintw(upper_w, CHPOS(cep), H_UNITS, "(%d)", units);
+ wclrtoeol(upper_w);
+ wrefresh(upper_w);
+}
+
+/*---------------------------------------------------------------------------*
+ * display accounting information
+ *---------------------------------------------------------------------------*/
+void
+display_acct(cfg_entry_t *cep)
+{
+ mvwprintw(upper_w, CHPOS(cep), H_OUT, "%-10d", cep->outbytes);
+ mvwprintw(upper_w, CHPOS(cep), H_OUTBPS, "%-4d", cep->outbps);
+ mvwprintw(upper_w, CHPOS(cep), H_IN, "%-10d", cep->inbytes);
+ mvwprintw(upper_w, CHPOS(cep), H_INBPS, "%-4d", cep->inbps);
+ wrefresh(upper_w);
+}
+
+/*---------------------------------------------------------------------------*
+ * display connect information
+ *---------------------------------------------------------------------------*/
+void
+display_connect(cfg_entry_t *cep)
+{
+ char buffer[256];
+
+ /* remote telephone number */
+
+ if(aliasing)
+ {
+ if(cep->direction == DIR_IN)
+ snprintf(buffer, sizeof(buffer), "%s", get_alias(cep->real_phone_incoming.number));
+ else
+ snprintf(buffer, sizeof(buffer), "%s", get_alias(cep->remote_phone_dialout.number));
+ }
+ else
+ {
+ if(cep->direction == DIR_IN)
+ snprintf(buffer, sizeof(buffer), "%s/%s", cep->name, cep->real_phone_incoming.number);
+ else
+ snprintf(buffer, sizeof(buffer), "%s/%s", cep->name, cep->remote_phone_dialout.number);
+ }
+
+ buffer[H_IFN - H_TELN - 1] = '\0';
+
+ mvwprintw(upper_w, CHPOS(cep), H_TELN, "%s", buffer);
+
+ /* interface */
+
+ mvwprintw(upper_w, CHPOS(cep), H_IFN, "%s%d ",
+ bdrivername(cep->usrdevicename), cep->usrdeviceunit);
+
+ mvwprintw(upper_w, CHPOS(cep), H_IO,
+ cep->direction == DIR_OUT ? "out" : "in");
+
+ mvwprintw(upper_w, CHPOS(cep), H_OUT, "-");
+ mvwprintw(upper_w, CHPOS(cep), H_OUTBPS, "-");
+ mvwprintw(upper_w, CHPOS(cep), H_IN, "-");
+ mvwprintw(upper_w, CHPOS(cep), H_INBPS, "-");
+
+ if(do_bell)
+ display_bell();
+
+ wrefresh(upper_w);
+}
+
+/*---------------------------------------------------------------------------*
+ * erase line at disconnect time
+ *---------------------------------------------------------------------------*/
+void
+display_disconnect(cfg_entry_t *cep)
+{
+ wmove(upper_w, CHPOS(cep),
+ H_TELN);
+ wclrtoeol(upper_w);
+ wrefresh(upper_w);
+
+ if(do_bell)
+ display_bell();
+
+}
+
+/*---------------------------------------------------------------------------*
+ * display interface up/down information
+ *---------------------------------------------------------------------------*/
+void
+display_updown(cfg_entry_t *cep, int updown)
+{
+ if(updown)
+ wstandend(mid_w);
+ else
+ wstandout(mid_w);
+
+ mvwprintw(mid_w, 0, cep->fs_position, "%s%d ",
+ bdrivername(cep->usrdevicename), cep->usrdeviceunit);
+
+ wstandend(mid_w);
+ wrefresh(mid_w);
+}
+
+/*---------------------------------------------------------------------------*
+ * display interface up/down information
+ *---------------------------------------------------------------------------*/
+void
+display_l12stat(int controller, int layer, int state)
+{
+ if(controller > ncontroller)
+ return;
+ if(!(layer == 1 || layer == 2))
+ return;
+
+ if(state)
+ wstandout(upper_w);
+ else
+ wstandend(upper_w);
+
+ if(layer == 1)
+ {
+ mvwprintw(upper_w, (controller*2)+1, H_TEI+1, "1");
+ if(!state)
+ mvwprintw(upper_w, (controller*2)+1, H_TEI+2, "2");
+ }
+ else if(layer == 2)
+ {
+ mvwprintw(upper_w, (controller*2)+1, H_TEI+2, "2");
+ if(state)
+ mvwprintw(upper_w, (controller*2)+1, H_TEI+1, "1");
+ }
+
+ wstandend(upper_w);
+ wrefresh(upper_w);
+}
+
+/*---------------------------------------------------------------------------*
+ * display TEI
+ *---------------------------------------------------------------------------*/
+void
+display_tei(int controller, int tei)
+{
+ if(controller > ncontroller)
+ return;
+
+ if(tei == -1)
+ mvwprintw(upper_w, controller*2, H_TEI, "---");
+ else
+ mvwprintw(upper_w, controller*2, H_TEI, "%3d", tei);
+
+ wrefresh(upper_w);
+}
+
+/*---------------------------------------------------------------------------*
+ * display bell :-)
+ *---------------------------------------------------------------------------*/
+void
+display_bell(void)
+{
+ static char bell[1] = { 0x07 };
+ write(STDOUT_FILENO, &bell[0], 1);
+}
+
+/*---------------------------------------------------------------------------*
+ * display channel information for shutdown
+ *---------------------------------------------------------------------------*/
+void
+display_chans(void)
+{
+ char buffer[80];
+ int i;
+ int cnt = 0;
+ WINDOW *chan_w;
+ int nlines, ncols, pos_x, pos_y;
+ fd_set set;
+ struct timeval timeout;
+ cfg_entry_t *cep = NULL;
+
+ /* need this later to close the connection */
+ struct ctlr_chan {
+ int cntl;
+ int chn;
+ } *cc = NULL;
+
+ for (i = 0; i < ncontroller; i++)
+ {
+ if((get_controller_state(i)) != CTRL_UP)
+ continue;
+ if((ret_channel_state(i, CHAN_B1)) == CHAN_RUN)
+ cnt++;
+ if((ret_channel_state(i, CHAN_B2)) == CHAN_RUN)
+ cnt++;
+ }
+
+ if(cnt > 0)
+ {
+ if ((cc = (struct ctlr_chan *)malloc (cnt *
+ sizeof (struct ctlr_chan))) == NULL)
+ {
+ return;
+ }
+ nlines = cnt + 4;
+ ncols = 60;
+ }
+ else
+ {
+ nlines = 5;
+ ncols = 22;
+ }
+
+ pos_y = WMENU_POSLN + 4;
+ pos_x = WMENU_POSCO + 10;
+
+ /* create a new window in the lower screen area */
+
+ if((chan_w = newwin(nlines, ncols, pos_y, pos_x )) == NULL)
+ {
+ log(LL_WRN, "ERROR, curses init channel window!");
+ if (cnt > 0)
+ free(cc);
+ return;
+ }
+
+ /* create a border around the window */
+
+ box(chan_w, '|', '-');
+
+ /* add a title */
+
+ wstandout(chan_w);
+ mvwaddstr(chan_w, 0, (ncols / 2) - (strlen("Channels") / 2), "Channels");
+ wstandend(chan_w);
+
+ /* no active channels */
+ if (cnt == 0)
+ {
+ mvwaddstr(chan_w, 2, 2, "No active channels");
+ wrefresh(chan_w);
+ sleep(1);
+
+ /* delete the channels window */
+
+ delwin(chan_w);
+ return;
+ }
+
+ nlines = 2;
+ ncols = 1;
+
+ for (i = 0; i < ncontroller; i++)
+ {
+ if((get_controller_state(i)) != CTRL_UP)
+ continue;
+
+ if((ret_channel_state(i, CHAN_B1)) == CHAN_RUN)
+ {
+ snprintf(buffer, sizeof(buffer), "%d - Controller %d channel %s", ncols, i, "B1");
+ mvwaddstr(chan_w, nlines, 2, buffer);
+ cc[ncols - 1].cntl = i;
+ cc[ncols - 1].chn = CHAN_B1;
+ nlines++;
+ ncols++;
+ }
+ if((ret_channel_state(i, CHAN_B2)) == CHAN_RUN)
+ {
+ snprintf(buffer, sizeof(buffer), "%d - Controller %d channel %s", ncols, i, "B2");
+ mvwaddstr(chan_w, nlines, 2, buffer);
+ cc[ncols - 1].cntl = i;
+ cc[ncols - 1].chn = CHAN_B2;
+ nlines++;
+ ncols++;
+ }
+ }
+
+ for(;;)
+ {
+ wrefresh(chan_w);
+
+ FD_ZERO(&set);
+ FD_SET(STDIN_FILENO, &set);
+ timeout.tv_sec = WMTIMEOUT;
+ timeout.tv_usec = 0;
+
+ /* if no char is available within timeout, exit menu*/
+
+ if((select(STDIN_FILENO + 1, &set, NULL, NULL, &timeout)) <= 0)
+ break;
+
+ ncols = wgetch(chan_w);
+
+ if (!(isdigit(ncols)))
+ {
+ display_bell();
+ continue;
+ }
+
+ nlines = ncols - '0';
+
+ if ((nlines == 0) || (nlines > cnt))
+ {
+ display_bell();
+ continue;
+ }
+
+ if((cep = get_cep_by_cc(cc[nlines-1].cntl, cc[nlines-1].chn))
+ != NULL)
+ {
+ log(LL_CHD, "%05d %s manual disconnect (fullscreen menu)", cep->cdid, cep->name);
+ cep->hangup = 1;
+ break;
+ }
+ }
+
+ free(cc);
+
+ /* delete the channels window */
+
+ delwin(chan_w);
+}
+
+/*---------------------------------------------------------------------------*
+ * display card type information
+ *---------------------------------------------------------------------------*/
+static void
+display_cards(void)
+{
+ WINDOW *chan_w;
+ int nlines, ncols, pos_x, pos_y;
+ fd_set set;
+ struct timeval timeout;
+ int i;
+
+ nlines = 6+ncontroller;
+ ncols = 60;
+ pos_y = WMENU_POSLN;
+ pos_x = WMENU_POSCO;
+
+ /* create a new window in the lower screen area */
+
+ if((chan_w = newwin(nlines, ncols, pos_y, pos_x )) == NULL)
+ {
+ log(LL_WRN, "ERROR, curses init channel window!");
+ return;
+ }
+
+ /* create a border around the window */
+
+ box(chan_w, '|', '-');
+
+ /* add a title */
+
+ wstandout(chan_w);
+ mvwaddstr(chan_w, 0, (ncols / 2) - (strlen("Cards") / 2), "Cards");
+ wstandend(chan_w);
+
+ mvwprintw(chan_w, 2, 2, "ctrl description");
+ mvwprintw(chan_w, 3, 2, "---- ----------------------------------------------");
+ for (i = 0; i < ncontroller; i++)
+ {
+ mvwprintw(chan_w, 4+i, 2, " #%d %s", i,
+ name_of_controller(isdn_ctrl_tab[i].ctrl_type,
+ isdn_ctrl_tab[i].card_type));
+ }
+
+ wrefresh(chan_w);
+
+ FD_ZERO(&set);
+ FD_SET(STDIN_FILENO, &set);
+ timeout.tv_sec = WMTIMEOUT*2;
+ timeout.tv_usec = 0;
+
+ if((select(STDIN_FILENO + 1, &set, NULL, NULL, &timeout)) <= 0)
+ {
+ delwin(chan_w);
+ return;
+ }
+
+ wgetch(chan_w);
+ delwin(chan_w);
+}
+
+/*---------------------------------------------------------------------------*
+ * display budget info
+ *---------------------------------------------------------------------------*/
+static void
+display_budget(void)
+{
+ WINDOW *bud_w;
+ int nlines, ncols, pos_x, pos_y;
+ fd_set set;
+ struct timeval timeout;
+ int i, j;
+ cfg_entry_t *cep;
+ time_t now;
+ double uptime;
+ int minutes;
+ int hours;
+ int days;
+
+ nlines = 0;
+ ncols = 73;
+ pos_y = WMENU_POSLN;
+ pos_x = WMENU_POSCO-3;
+
+ for(i=0, j=0; i < nentries; i++) /* walk thru all entries */
+ {
+ cep = &cfg_entry_tab[i]; /* get ptr to entry */
+
+ if(cep->budget_callbackperiod && cep->budget_callbackncalls)
+ nlines++;
+ if(cep->budget_calloutperiod && cep->budget_calloutncalls)
+ nlines++;
+ }
+
+ if(nlines == 0)
+ return;
+
+ nlines += 6;
+
+ /* create a new window in the lower screen area */
+
+ if((bud_w = newwin(nlines, ncols, pos_y, pos_x )) == NULL)
+ {
+ log(LL_WRN, "ERROR, curses init budget window!");
+ return;
+ }
+
+ now = time(NULL);
+ uptime = difftime(now, starttime);
+
+ minutes = (time_t) (uptime / 60) % 60;
+ hours = (time_t) (uptime / (60*60)) % (60*60);
+ days = (time_t) (uptime / (60*60*24)) % (60*60*24);
+
+ uptime = uptime / (60*60);
+
+ /* create a border around the window */
+
+ box(bud_w, '|', '-');
+
+ /* add a title */
+
+ wstandout(bud_w);
+ mvwaddstr(bud_w, 0, (ncols / 2) - (strlen("Budget") / 2), "Budget");
+ wstandend(bud_w);
+
+ mvwprintw(bud_w, 1, 2, "isdnd uptime: %d %s - %d %s - %d %s",
+ days,
+ days == 1 ? "day" : "days",
+ hours,
+ hours == 1 ? "hour" : "hours",
+ minutes,
+ minutes == 1 ? "minute" : "minutes");
+
+ mvwprintw(bud_w, 2, 2, "name t period rest ncall rest rqsts /hr rdone /hr rrjct /hr ");
+ mvwprintw(bud_w, 3, 2, "-------- - ------ ------ ----- ----- ----- ---- ----- ---- ----- ----");
+
+ for(i=0, j=4; i < nentries; i++) /* walk thru all entries */
+ {
+ cep = &cfg_entry_tab[i]; /* get ptr to enry */
+
+ if(cep->budget_calloutperiod && cep->budget_calloutncalls)
+ {
+ mvwprintw(bud_w, j, 2, "%-8s %c %-6d %-6ld %-5d %-5d %-5d %-4.1f %-5d %-4.1f %-5d %-4.1f",
+ cep->name,
+ 'o',
+ cep->budget_calloutperiod,
+ (long)(cep->budget_calloutperiod_time - now),
+ cep->budget_calloutncalls,
+ cep->budget_calloutncalls_cnt,
+ cep->budget_callout_req,
+ (double)cep->budget_callout_req / uptime,
+ cep->budget_callout_done,
+ (double)cep->budget_callout_done / uptime,
+ cep->budget_callout_rej,
+ (double)cep->budget_callout_rej / uptime);
+ j++;
+ }
+ if(cep->budget_callbackperiod && cep->budget_callbackncalls)
+ {
+ mvwprintw(bud_w, j, 2, "%-8s %c %-6d %-6ld %-5d %-5d %-5d %-4.1f %-5d %-4.1f %-5d %-4.1f",
+ (cep->budget_calloutperiod && cep->budget_calloutncalls) ? "" : cep->name,
+ 'b',
+ cep->budget_callbackperiod,
+ (long)(cep->budget_callbackperiod_time - now),
+ cep->budget_callbackncalls,
+ cep->budget_callbackncalls_cnt,
+ cep->budget_callback_req,
+ (double)cep->budget_callback_req / uptime,
+ cep->budget_callback_done,
+ (double)cep->budget_callback_done / uptime,
+ cep->budget_callback_rej,
+ (double)cep->budget_callback_rej / uptime);
+ j++;
+ }
+ }
+
+ wrefresh(bud_w);
+
+ FD_ZERO(&set);
+ FD_SET(STDIN_FILENO, &set);
+ timeout.tv_sec = WMTIMEOUT*3;
+ timeout.tv_usec = 0;
+
+ if((select(STDIN_FILENO + 1, &set, NULL, NULL, &timeout)) <= 0)
+ {
+ delwin(bud_w);
+ return;
+ }
+
+ wgetch(bud_w);
+ delwin(bud_w);
+}
+
+#endif
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnd/dial.c b/usr.sbin/i4b/isdnd/dial.c
new file mode 100644
index 0000000..5ccbd33
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/dial.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 1997, 2002 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * i4b daemon - dial handling routines
+ * -----------------------------------
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Tue Mar 26 14:35:19 2002]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "isdnd.h"
+
+/*---------------------------------------------------------------------------*
+ * select the first remote number to dial according to the
+ * dial strategy
+ *---------------------------------------------------------------------------*/
+void
+select_first_dialno(cfg_entry_t *cep)
+{
+ int i, j;
+
+ if(cep->keypad[0] != '\0')
+ return;
+
+ if(cep->remote_numbers_count < 1)
+ {
+ log(LL_ERR, "select_first_dialno: remote_numbers_count < 1!");
+ return;
+ }
+
+ if(cep->remote_numbers_count == 1)
+ {
+ strcpy(cep->remote_phone_dialout.number, cep->remote_numbers[0].number);
+ strcpy(cep->remote_phone_dialout.subaddr, cep->remote_numbers[0].subaddr);
+ DBGL(DL_DIAL, (log(LL_DBG, "select_first_dialno: only one no, no = %s", cep->remote_phone_dialout.number)));
+ cep->last_remote_number = 0;
+ return;
+ }
+
+ if(cep->remote_numbers_handling == RNH_FIRST)
+ {
+ strcpy(cep->remote_phone_dialout.number, cep->remote_numbers[0].number);
+ strcpy(cep->remote_phone_dialout.subaddr, cep->remote_numbers[0].subaddr);
+ DBGL(DL_DIAL, (log(LL_DBG, "select_first_dialno: use first, no = %s", cep->remote_phone_dialout.number)));
+ cep->last_remote_number = 0;
+ return;
+ }
+
+ i = cep->last_remote_number;
+
+ for(j = cep->remote_numbers_count; j > 0; j--)
+ {
+ if(cep->remote_numbers[i].flag == RNF_SUCC)
+ {
+ if(cep->remote_numbers_handling == RNH_LAST)
+ {
+ strcpy(cep->remote_phone_dialout.number, cep->remote_numbers[i].number);
+ strcpy(cep->remote_phone_dialout.subaddr, cep->remote_numbers[i].subaddr);
+ DBGL(DL_DIAL, (log(LL_DBG, "select_first_dialno: use last, no = %s", cep->remote_phone_dialout.number)));
+ cep->last_remote_number = i;
+ return;
+ }
+ else
+ {
+ if(++i >= cep->remote_numbers_count)
+ i = 0;
+
+ strcpy(cep->remote_phone_dialout.number, cep->remote_numbers[i].number);
+ strcpy(cep->remote_phone_dialout.subaddr, cep->remote_numbers[i].subaddr);
+ DBGL(DL_DIAL, (log(LL_DBG, "select_first_dialno: use next, no = %s", cep->remote_phone_dialout.number)));
+ cep->last_remote_number = i;
+ return;
+ }
+ }
+
+ if(++i >= cep->remote_numbers_count)
+ i = 0;
+ }
+ strcpy(cep->remote_phone_dialout.number, cep->remote_numbers[0].number);
+ DBGL(DL_DIAL, (log(LL_DBG, "select_first_dialno: no last found (use 0), no = %s", cep->remote_phone_dialout.number)));
+ cep->last_remote_number = 0;
+}
+
+/*---------------------------------------------------------------------------*
+ * select next remote number to dial (last was unsuccesfull)
+ *---------------------------------------------------------------------------*/
+void
+select_next_dialno(cfg_entry_t *cep)
+{
+ if(cep->remote_numbers_count < 1)
+ {
+ log(LL_ERR, "select_next_dialno: remote_numbers_count < 1!");
+ return;
+ }
+
+ if(cep->remote_numbers_count == 1)
+ {
+ strcpy(cep->remote_phone_dialout.number, cep->remote_numbers[0].number);
+ strcpy(cep->remote_phone_dialout.subaddr, cep->remote_numbers[0].subaddr);
+ DBGL(DL_DIAL, (log(LL_DBG, "select_next_dialno: only one no, no = %s", cep->remote_phone_dialout.number)));
+ cep->last_remote_number = 0;
+ return;
+ }
+
+ /* mark last try as bad */
+
+ cep->remote_numbers[cep->last_remote_number].flag = RNF_IDLE;
+
+ /* next one to try */
+
+ cep->last_remote_number++;
+
+ if(cep->last_remote_number >= cep->remote_numbers_count)
+ cep->last_remote_number = 0;
+
+ strcpy(cep->remote_phone_dialout.number, cep->remote_numbers[cep->last_remote_number].number);
+
+ DBGL(DL_DIAL, (log(LL_DBG, "select_next_dialno: index=%d, no=%s",
+ cep->last_remote_number,
+ cep->remote_numbers[cep->last_remote_number].number)));
+}
+
+/*---------------------------------------------------------------------------*
+ * dial succeded, store this number as the last successful
+ *---------------------------------------------------------------------------*/
+void
+select_this_dialno(cfg_entry_t *cep)
+{
+ cep->remote_numbers[cep->last_remote_number].flag = RNF_SUCC;
+
+ DBGL(DL_DIAL, (log(LL_DBG, "select_this_dialno: index = %d, no = %s",
+ cep->last_remote_number,
+ cep->remote_numbers[cep->last_remote_number].number)));
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnd/exec.c b/usr.sbin/i4b/isdnd/exec.c
new file mode 100644
index 0000000..17bc88e
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/exec.c
@@ -0,0 +1,407 @@
+/*
+ * Copyright (c) 1997, 2002 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * exec.h - supplemental program/script execution
+ * ----------------------------------------------
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Tue Mar 26 14:35:46 2002]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "isdnd.h"
+
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <paths.h>
+
+#define MAX_PIDS 32
+
+static struct pid_tab {
+ pid_t pid;
+ cfg_entry_t *cep;
+} pid_tab[MAX_PIDS];
+
+/*---------------------------------------------------------------------------*
+ * SIGCHLD signal handler
+ *---------------------------------------------------------------------------*/
+void
+sigchild_handler(int sig)
+{
+ int retstat;
+ register int i;
+ pid_t pid;
+
+ if((pid = waitpid(-1, &retstat, WNOHANG)) <= 0)
+ {
+ log(LL_ERR, "ERROR, sigchild_handler, waitpid: %s", strerror(errno));
+ error_exit(1, "ERROR, sigchild_handler, waitpid: %s", strerror(errno));
+ }
+ else
+ {
+ if(WIFEXITED(retstat))
+ {
+ DBGL(DL_PROC, (log(LL_DBG, "normal child (pid=%d) termination, exitstat = %d",
+ pid, WEXITSTATUS(retstat))));
+ }
+ else if(WIFSIGNALED(retstat))
+ {
+ if(WCOREDUMP(retstat))
+ log(LL_WRN, "child (pid=%d) termination due to signal %d (coredump)",
+ pid, WTERMSIG(retstat));
+ else
+ log(LL_WRN, "child (pid=%d) termination due to signal %d",
+ pid, WTERMSIG(retstat));
+ }
+ }
+
+ /* check if hangup required */
+
+ for(i=0; i < MAX_PIDS; i++)
+ {
+ if(pid_tab[i].pid == pid)
+ {
+ if(pid_tab[i].cep->cdid != CDID_UNUSED)
+ {
+ DBGL(DL_PROC, (log(LL_DBG, "sigchild_handler: scheduling hangup for cdid %d, pid %d",
+ pid_tab[i].cep->cdid, pid_tab[i].pid)));
+ pid_tab[i].cep->hangup = 1;
+ }
+ pid_tab[i].pid = 0;
+ break;
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * execute prog as a subprocess and pass an argumentlist
+ *---------------------------------------------------------------------------*/
+pid_t
+exec_prog(char *prog, char **arglist)
+{
+ char tmp[MAXPATHLEN];
+ char path[MAXPATHLEN+1];
+ pid_t pid;
+ int a;
+
+ snprintf(path, sizeof(path), "%s/%s", ETCPATH, prog);
+
+ arglist[0] = path;
+
+ tmp[0] = '\0';
+
+ for(a=1; arglist[a] != NULL; ++a )
+ {
+ strcat(tmp, " " );
+ strcat(tmp, arglist[a]);
+ }
+
+ DBGL(DL_PROC, (log(LL_DBG, "exec_prog: %s, args:%s", path, tmp)));
+
+ switch(pid = fork())
+ {
+ case -1: /* error */
+ log(LL_ERR, "ERROR, exec_prog/fork: %s", strerror(errno));
+ error_exit(1, "ERROR, exec_prog/fork: %s", strerror(errno));
+ case 0: /* child */
+ break;
+ default: /* parent */
+ return(pid);
+ }
+
+ /* this is the child now */
+
+ /*
+ * close files used only by isdnd, e.g.
+ * 1. /dev/i4b
+ * 2. /var/log/isdnd.acct (or similar, when used)
+ * 3. /var/log/isdnd.log (or similar, when used)
+ */
+ close(isdnfd);
+
+ if(useacctfile && acctfp)
+ fclose(acctfp);
+
+ if(uselogfile && logfp)
+ fclose(logfp);
+
+ if(execvp(path,arglist) < 0 )
+ _exit(127);
+
+ return(-1);
+}
+
+/*---------------------------------------------------------------------------*
+ * run interface up/down script
+ *---------------------------------------------------------------------------*/
+int
+exec_connect_prog(cfg_entry_t *cep, const char *prog, int link_down)
+{
+ char *argv[32], **av = argv;
+ char devicename[MAXPATHLEN], addr[100];
+ char *device;
+ int s;
+ struct ifreq ifr;
+
+ /* the obvious things */
+ device = bdrivername(cep->usrdevicename);
+ snprintf(devicename, sizeof(devicename), "%s%d", device, cep->usrdeviceunit);
+ *av++ = (char*)prog;
+ *av++ = "-d";
+ *av++ = devicename;
+ *av++ = "-f";
+ *av++ = link_down ? "down" : "up";
+
+ /* try to figure AF_INET address of interface */
+ addr[0] = '\0';
+ memset(&ifr, 0, sizeof ifr);
+ ifr.ifr_addr.sa_family = AF_INET;
+ strncpy(ifr.ifr_name, devicename, sizeof(ifr.ifr_name));
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s >= 0) {
+ if (ioctl(s, SIOCGIFADDR, (caddr_t)&ifr) >= 0) {
+ struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr;
+ strcpy(addr, inet_ntoa(sin->sin_addr));
+ *av++ = "-a";
+ *av++ = addr;
+ }
+ close(s);
+ }
+
+ /* terminate argv */
+ *av++ = NULL;
+
+ return exec_prog((char*)prog, argv);
+}
+
+/*---------------------------------------------------------------------------*
+ * run answeringmachine application
+ *---------------------------------------------------------------------------*/
+int
+exec_answer(cfg_entry_t *cep)
+{
+ char *argv[32];
+ u_char devicename[MAXPATHLEN];
+ int pid;
+ char *device;
+
+ device = bdrivername(cep->usrdevicename);
+
+ snprintf(devicename, sizeof(devicename), "%si4b%s%d", _PATH_DEV, device,
+ cep->usrdeviceunit);
+
+ argv[0] = cep->answerprog;
+ argv[1] = "-D";
+ argv[2] = devicename;
+ argv[3] = "-d";
+ argv[4] = "unknown";
+ argv[5] = "-s";
+ argv[6] = "unknown";
+ argv[7] = NULL;
+
+ /* if destination telephone number avail, add it as argument */
+
+ if(*cep->local_phone_incoming.number)
+ argv[4] = cep->local_phone_incoming.number;
+
+ /* if source telephone number avail, add it as argument */
+
+ if(*cep->real_phone_incoming.number)
+ argv[6] = cep->real_phone_incoming.number;
+
+ if(*cep->display)
+ {
+ argv[7] = "-t";
+ argv[8] = cep->display;
+ argv[9] = NULL;
+ }
+
+ /* exec program */
+
+ DBGL(DL_PROC, (log(LL_DBG, "exec_answer: prog=[%s]", cep->answerprog)));
+
+ pid = exec_prog(cep->answerprog, argv);
+
+ /* enter pid and conf ptr entry addr into table */
+
+ if(pid != -1)
+ {
+ int i;
+
+ for(i=0; i < MAX_PIDS; i++)
+ {
+ if(pid_tab[i].pid == 0)
+ {
+ pid_tab[i].pid = pid;
+ pid_tab[i].cep = cep;
+ break;
+ }
+ }
+ return(GOOD);
+ }
+ return(ERROR);
+}
+
+/*---------------------------------------------------------------------------*
+ * check if a connection has an outstanding process, if yes, kill it
+ *---------------------------------------------------------------------------*/
+void
+check_and_kill(cfg_entry_t *cep)
+{
+ int i;
+
+ for(i=0; i < MAX_PIDS; i++)
+ {
+ if(pid_tab[i].cep == cep)
+ {
+ pid_t kp;
+
+ DBGL(DL_PROC, (log(LL_DBG, "check_and_kill: killing pid %d", pid_tab[i].pid)));
+
+ kp = pid_tab[i].pid;
+ pid_tab[i].pid = 0;
+ kill(kp, SIGHUP);
+ break;
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * update budget callout/callback statistics counter file
+ *---------------------------------------------------------------------------*/
+void
+upd_callstat_file(char *filename, int rotateflag)
+{
+ FILE *fp;
+ time_t s, l, now;
+ int n;
+ int ret;
+
+ now = time(NULL);
+
+ fp = fopen(filename, "r+");
+
+ if(fp == NULL)
+ {
+ /* file not there, create it and exit */
+
+ log(LL_WRN, "upd_callstat_file: creating %s", filename);
+
+ fp = fopen(filename, "w");
+ if(fp == NULL)
+ {
+ log(LL_ERR, "ERROR, upd_callstat_file: cannot create %s, %s", filename, strerror(errno));
+ return;
+ }
+
+ ret = fprintf(fp, "%ld %ld 1", now, now);
+ if(ret <= 0)
+ log(LL_ERR, "ERROR, upd_callstat_file: fprintf failed: %s", strerror(errno));
+
+ fclose(fp);
+ return;
+ }
+
+ /* get contents */
+
+ ret = fscanf(fp, "%ld %ld %d", &s, &l, &n);
+
+ /* reset fp */
+
+ rewind(fp);
+
+ if(ret != 3)
+ {
+ /* file corrupt ? anyway, initialize */
+
+ log(LL_WRN, "upd_callstat_file: initializing %s", filename);
+
+ s = l = now;
+ n = 0;
+ }
+
+ if(rotateflag)
+ {
+ struct tm *stmp;
+ int dom;
+
+ /* get day of month for last timestamp */
+ stmp = localtime(&l);
+ dom = stmp->tm_mday;
+
+ /* get day of month for just now */
+ stmp = localtime(&now);
+
+ if(dom != stmp->tm_mday)
+ {
+ FILE *nfp;
+ char buf[MAXPATHLEN];
+
+ /* new day, write last days stats */
+
+ sprintf(buf, "%s-%02d", filename, stmp->tm_mday);
+
+ nfp = fopen(buf, "w");
+ if(nfp == NULL)
+ {
+ log(LL_ERR, "ERROR, upd_callstat_file: cannot open for write %s, %s", buf, strerror(errno));
+ return;
+ }
+
+ ret = fprintf(nfp, "%ld %ld %d", s, l, n);
+ if(ret <= 0)
+ log(LL_ERR, "ERROR, upd_callstat_file: fprintf failed: %s", strerror(errno));
+
+ fclose(nfp);
+
+ /* init new days stats */
+ n = 0;
+ s = now;
+
+ log(LL_WRN, "upd_callstat_file: rotate %s, new s=%ld l=%ld n=%d", filename, s, l, n);
+ }
+ }
+
+ n++; /* increment call count */
+
+ /*
+ * the "%-3d" is necessary to overwrite any
+ * leftovers from previous contents!
+ */
+
+ ret = fprintf(fp, "%ld %ld %-3d", s, now, n);
+
+ if(ret <= 0)
+ log(LL_ERR, "ERROR, upd_callstat_file: fprintf failed: %s", strerror(errno));
+
+ fclose(fp);
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnd/fsm.c b/usr.sbin/i4b/isdnd/fsm.c
new file mode 100644
index 0000000..e555fa8
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/fsm.c
@@ -0,0 +1,446 @@
+/*
+ * Copyright (c) 1997, 2002 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * FSM for isdnd
+ * -------------
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Tue Mar 26 14:35:56 2002]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "isdnd.h"
+
+/* table of state descriptions */
+
+static char *state_text[N_STATES] = {
+ "idle",
+ "dialing",
+ "waitdialretry",
+ "dialretry",
+
+ "pcb-dialing",
+ "pcb-dialfail",
+ "pcb-waitcall",
+
+ "acb-waitdisc",
+ "acb-waitdial",
+ "acb-dialing",
+ "acb-dialfail",
+
+ "accepted",
+ "connected",
+ "waitdisconnect",
+ "down",
+ "alert",
+
+ "Illegal State"
+};
+
+/* table of event descriptions */
+
+static char *event_text[N_EVENTS] = {
+
+ /* incoming messages */
+
+ "msg-con-ind",
+ "msg-con-act-ind",
+ "msg-disc-ind",
+ "msg-dialout",
+
+ /* local events */
+
+ "timeout",
+ "disconnect-req",
+ "callback-req",
+ "alert-req",
+
+ /* illegal */
+
+ "Illegal Event"
+};
+
+/*---------------------------------------------------------------------------*
+ * illegal state default action
+ *---------------------------------------------------------------------------*/
+static void
+F_ill(cfg_entry_t *cep)
+{
+ DBGL(DL_STATE, (log(LL_DBG, "F_ill: Illegal State reached !!!")));
+}
+
+/*---------------------------------------------------------------------------*
+ * No change, No action
+ *---------------------------------------------------------------------------*/
+static void
+F_NcNa(cfg_entry_t *cep)
+{
+}
+
+/*---------------------------------------------------------------------------*
+ * incoming CONNECT, accepting call
+ *---------------------------------------------------------------------------*/
+static void
+F_MCI(cfg_entry_t *cep)
+{
+ DBGL(DL_STATE, (log(LL_DBG, "F_MCI: tx SETUP_RESP_ACCEPT")));
+ sendm_connect_resp(cep, cep->cdid, SETUP_RESP_ACCEPT, 0);
+ start_timer(cep, TIMEOUT_CONNECT_ACTIVE);
+}
+
+/*---------------------------------------------------------------------------*
+ * incoming connect active, call is now active
+ *---------------------------------------------------------------------------*/
+static void
+F_MCAI(cfg_entry_t *cep)
+{
+ DBGL(DL_STATE, (log(LL_DBG, "F_MCAI: Connection active!")));
+
+ stop_timer(cep);
+
+ if((cep->dialin_reaction == REACT_ANSWER) &&
+ (cep->b1protocol == BPROT_NONE))
+ {
+ exec_answer(cep);
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * timeout
+ *---------------------------------------------------------------------------*/
+static void
+F_TIMO(cfg_entry_t *cep)
+{
+ DBGL(DL_STATE, (log(LL_DBG, "F_TIMO: Timout occured!")));
+ sendm_disconnect_req(cep, (CAUSET_I4B << 8) | CAUSE_I4B_NORMAL);
+ cep->cdid = CDID_UNUSED;
+}
+
+/*---------------------------------------------------------------------------*
+ * incoming disconnect indication
+ *---------------------------------------------------------------------------*/
+static void
+F_IDIS(cfg_entry_t *cep)
+{
+ DBGL(DL_STATE, (log(LL_DBG, "F_IDIS: disconnect indication")));
+ cep->cdid = CDID_UNUSED;
+}
+
+/*---------------------------------------------------------------------------*
+ * local disconnect request
+ *---------------------------------------------------------------------------*/
+static void
+F_DRQ(cfg_entry_t *cep)
+{
+ DBGL(DL_STATE, (log(LL_DBG, "F_DRQ: local disconnect request")));
+ sendm_disconnect_req(cep, (CAUSET_I4B << 8) | CAUSE_I4B_NORMAL);
+}
+
+/*---------------------------------------------------------------------------*
+ * disconnect indication after local disconnect req
+ *---------------------------------------------------------------------------*/
+static void
+F_MDI(cfg_entry_t *cep)
+{
+ DBGL(DL_STATE, (log(LL_DBG, "F_MDI: disconnect indication, local disconnected")));
+ cep->cdid = CDID_UNUSED;
+}
+
+/*---------------------------------------------------------------------------*
+ * local requested outgoing dial
+ *---------------------------------------------------------------------------*/
+static void
+F_DIAL(cfg_entry_t *cep)
+{
+ DBGL(DL_STATE, (log(LL_DBG, "F_DIAL: local dial out request")));
+
+ if(cep->dialrandincr)
+ cep->randomtime = (random() & RANDOM_MASK) + cep->recoverytime;
+
+ cep->dial_count = 0;
+
+ select_first_dialno(cep);
+
+ sendm_connect_req(cep);
+}
+
+/*---------------------------------------------------------------------------*
+ * outgoing dial successfull
+ *---------------------------------------------------------------------------*/
+static void
+F_DOK(cfg_entry_t *cep)
+{
+ DBGL(DL_STATE, (log(LL_DBG, "F_DOK: dial out ok")));
+ select_this_dialno(cep);
+}
+
+/*---------------------------------------------------------------------------*
+ * outgoing dial fail (ST_SUSE !!!)
+ *---------------------------------------------------------------------------*/
+static void
+F_DFL(cfg_entry_t *cep)
+{
+ cep->last_release_time = time(NULL);
+
+ if(cep->dialouttype == DIALOUT_NORMAL)
+ {
+ cep->dial_count++;
+
+ if(cep->dial_count < cep->dialretries || cep->dialretries == -1)
+ {
+ /* inside normal retry cycle */
+
+ DBGL(DL_STATE, (log(LL_DBG, "F_DFL: dial fail, dial retry")));
+ select_next_dialno(cep);
+ cep->cdid = CDID_RESERVED;
+ cep->state = ST_DIALRTMRCHD;
+ return;
+ }
+
+ /* retries exhausted */
+
+ if(!cep->usedown)
+ {
+ DBGL(DL_STATE, (log(LL_DBG, "F_DFL: dial retry fail, dial retries exhausted")));
+ dialresponse(cep, DSTAT_TFAIL);
+ cep->cdid = CDID_UNUSED;
+ cep->dial_count = 0;
+ cep->state = ST_IDLE;
+ return;
+ }
+
+ /* interface up/down active */
+
+ cep->down_retry_count++;
+
+ if(cep->down_retry_count > cep->downtries)
+ {
+ /* set interface down */
+ DBGL(DL_STATE, (log(LL_DBG, "F_DFL: dial retry cycle fail, setting interface down!")));
+ dialresponse(cep, DSTAT_PFAIL);
+ if_down(cep);
+ cep->state = ST_DOWN;
+ }
+ else
+ {
+ /* enter new dial retry cycle */
+ DBGL(DL_STATE, (log(LL_DBG, "F_DFL: dial retry cycle fail, enter new retry cycle!")));
+ select_next_dialno(cep);
+ cep->state = ST_DIALRTMRCHD;
+ }
+
+ cep->dial_count = 0;
+ cep->cdid = CDID_RESERVED;
+ }
+ else /* cdp->dialouttype == DIALOUT_CALLEDBACK */
+ {
+ DBGL(DL_STATE, (log(LL_DBG, "F_DFL: calledback dial done, wait for incoming call")));
+ cep->cdid = CDID_RESERVED;
+ cep->state = ST_PCB_WAITCALL;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * local requested outgoing dial
+ *---------------------------------------------------------------------------*/
+static void
+F_ACBW(cfg_entry_t *cep)
+{
+ DBGL(DL_STATE, (log(LL_DBG, "F_ACBW: local callback, wait callback recovery time")));
+
+ if(cep->dialrandincr)
+ cep->randomtime = (random() & RANDOM_MASK) + cep->recoverytime;
+
+ cep->dial_count = 0;
+
+ cep->cdid = CDID_RESERVED;
+}
+
+/*---------------------------------------------------------------------------*
+ * active callback dialout retry (ST_SUSE !!!)
+ *---------------------------------------------------------------------------*/
+static void
+F_ACBR(cfg_entry_t *cep)
+{
+ cep->dial_count++;
+
+ if(cep->dial_count < cep->dialretries || cep->dialretries == -1)
+ {
+ /* inside normal retry cycle */
+
+ DBGL(DL_STATE, (log(LL_DBG, "F_ACBR: dial fail, dial retry")));
+ select_next_dialno(cep);
+ cep->cdid = CDID_RESERVED;
+ cep->state = ST_ACB_DIALFAIL;
+ return;
+ }
+
+ /* retries exhausted */
+
+ if(!cep->usedown)
+ {
+ DBGL(DL_STATE, (log(LL_DBG, "F_ACBR: dial retry fail, dial retries exhausted")));
+ dialresponse(cep, DSTAT_TFAIL);
+ cep->cdid = CDID_UNUSED;
+ cep->dial_count = 0;
+ cep->state = ST_IDLE;
+ return;
+ }
+
+ /* interface up/down active */
+
+ cep->down_retry_count++;
+
+ if(cep->down_retry_count > cep->downtries)
+ {
+ /* set interface down */
+ DBGL(DL_STATE, (log(LL_DBG, "F_ACBR: dial retry cycle fail, setting interface down!")));
+ dialresponse(cep, DSTAT_PFAIL);
+ if_down(cep);
+ cep->state = ST_DOWN;
+ }
+ else
+ {
+ /* enter new dial retry cycle */
+ DBGL(DL_STATE, (log(LL_DBG, "F_ACBR: dial retry cycle fail, enter new retry cycle!")));
+ select_next_dialno(cep);
+ cep->state = ST_ACB_DIALFAIL;
+ }
+
+ cep->dial_count = 0;
+ cep->cdid = CDID_RESERVED;
+}
+
+/*---------------------------------------------------------------------------*
+ * local requested to send ALERT message
+ *---------------------------------------------------------------------------*/
+static void
+F_ALRT(cfg_entry_t *cep)
+{
+ DBGL(DL_STATE, (log(LL_DBG, "F_ALRT: local send alert request")));
+
+ cep->alert_time = cep->alert;
+
+ sendm_alert_req(cep);
+}
+
+/*---------------------------------------------------------------------------*
+ * isdn daemon state transition table
+ *---------------------------------------------------------------------------*/
+struct state_tab {
+ void(*func)(cfg_entry_t *cep); /* function to execute */
+ int newstate; /* next state */
+} state_tab[N_EVENTS][N_STATES] = {
+
+/* STATE: ST_IDLE ST_DIAL ST_DIALRTMRCHD ST_DIALRETRY ST_PCB_DIAL ST_PCB_DIALFAIL ST_PCB_WAITCALL ST_ACB_WAITDISC ST_ACB_WAITDIAL ST_ACB_DIAL ST_ACB_DIALFAIL ST_ACCEPTED ST_CONNECTED ST_WAITDISCI ST_DOWN ST_ALERT ST_ILLEGAL */
+/* -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
+/* messages */
+/* EV_MCI */{{F_MCI, ST_ACCEPTED}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_MCI, ST_ACCEPTED}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_MCI, ST_ACCEPTED}, {F_ill, ST_ILL}},
+/* EV_MCAI */{{F_ill, ST_ILL}, {F_DOK, ST_CONNECTED}, {F_ill, ST_ILL}, {F_DOK, ST_CONNECTED}, {F_DOK, ST_CONNECTED}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_DOK, ST_CONNECTED}, {F_ill, ST_ILL}, {F_MCAI,ST_CONNECTED}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}},
+/* EV_MDI */{{F_ill, ST_ILL}, {F_DFL, ST_SUSE}, {F_ill, ST_ILL}, {F_DFL, ST_SUSE}, {F_DFL, ST_SUSE}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ACBW,ST_ACB_WAITDIAL},{F_ill, ST_ILL}, {F_ACBR, ST_SUSE}, {F_ACBR,ST_SUSE}, {F_IDIS,ST_IDLE}, {F_IDIS,ST_IDLE}, {F_MDI, ST_IDLE}, {F_ill, ST_ILL}, {F_MDI, ST_IDLE}, {F_ill, ST_ILL}},
+/* EV_MDO */{{F_DIAL,ST_DIAL}, {F_NcNa,ST_DIAL}, {F_NcNa,ST_DIALRTMRCHD},{F_NcNa,ST_DIALRETRY}, {F_NcNa,ST_PCB_DIAL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}},
+
+/* local requests */
+/* EV_TIMO */{{F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_TIMO,ST_IDLE}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}},
+/* EV_DRQ */{{F_NcNa, ST_IDLE}, {F_DRQ, ST_WAITDISCI}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_DRQ, ST_WAITDISCI}, {F_NcNa,ST_WAITDISCI}, {F_NcNa, ST_DOWN}, {F_ill, ST_ILL}, {F_ill, ST_ILL}},
+/* EV_CBRQ */{{F_NcNa,ST_ACB_WAITDIAL},{F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_NcNa,ST_ACB_WAITDIAL},{F_NcNa, ST_ACB_DIAL}, {F_NcNa,ST_ACB_DIALFAIL},{F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}},
+/* EV_ALRT */{{F_ALRT,ST_ALERT}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}},
+
+/* illegal */
+
+/* EV_ILL */{{F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}}
+};
+
+/*---------------------------------------------------------------------------*
+ * event handler
+ *---------------------------------------------------------------------------*/
+void
+next_state(cfg_entry_t *cep, int event)
+{
+ int currstate, newstate;
+
+ if(event > N_EVENTS)
+ {
+ log(LL_ERR, "next_state: event > N_EVENTS");
+ error_exit(1, "next_state: event > N_EVENTS");
+ }
+
+ currstate = cep->state;
+
+ if(currstate > N_STATES)
+ {
+ log(LL_ERR, "next_state: currstate > N_STATES");
+ error_exit(1, "next_state: currstate > N_STATES");
+ }
+
+ newstate = state_tab[event][currstate].newstate;
+
+ if(newstate > N_STATES)
+ {
+ log(LL_ERR, "next_state: newstate > N_STATES");
+ error_exit(1, "next_state: newstate > N_STATES");
+ }
+
+ if(newstate != ST_SUSE)
+ {
+ DBGL(DL_STATE, (log(LL_DBG, "FSM event [%s]: [%s => %s]", event_text[event],
+ state_text[currstate],
+ state_text[newstate])));
+ }
+
+ (*state_tab[event][currstate].func)(cep);
+
+ if(newstate == ST_ILL)
+ {
+ log(LL_ERR, "FSM ILLEGAL STATE, event=%s: oldstate=%s => newstate=%s]",
+ event_text[event],
+ state_text[currstate],
+ state_text[newstate]);
+ }
+
+ if(newstate == ST_SUSE)
+ {
+ DBGL(DL_STATE, (log(LL_DBG, "FSM (SUSE) event [%s]: [%s => %s]", event_text[event],
+ state_text[currstate],
+ state_text[cep->state])));
+ }
+ else
+ {
+ cep->state = newstate;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * return pointer to current state description
+ *---------------------------------------------------------------------------*/
+char *
+printstate(cfg_entry_t *cep)
+{
+ return((char *) state_text[cep->state]);
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnd/holiday.c b/usr.sbin/i4b/isdnd/holiday.c
new file mode 100644
index 0000000..5bf9582
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/holiday.c
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2000, 2001 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * isdnd - holiday file handling
+ * =============================
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Wed May 2 09:42:56 2001]
+ *
+ * Format:
+ *
+ * day.month.year optional comment (different day every year) or
+ * day.month optional comment (same day every year)
+ *
+ * i.e.:
+ *
+ * 23.4.2000 Ostersonntag
+ * 3.10 Tag der deutschen Einheit
+ *
+ *----------------------------------------------------------------------------*/
+
+#include "isdnd.h"
+
+struct holiday {
+ int day;
+ int month;
+ int year;
+ struct holiday *next;
+};
+
+static struct holiday *firsth = NULL;
+
+#define MAXBUFSZ 256
+
+static void free_holiday(struct holiday *ptr);
+
+/*---------------------------------------------------------------------------*
+ * read in and init holidayes
+ *---------------------------------------------------------------------------*/
+void
+init_holidays(char *filename)
+{
+ FILE *fp;
+ unsigned char buffer[MAXBUFSZ + 1];
+ struct holiday *newh = NULL;
+ struct holiday *lasth = NULL;
+ int ret;
+ int day, month, year;
+
+ firsth = NULL;
+
+ if((fp = fopen(filename, "r")) == NULL)
+ {
+ DBGL(DL_VALID, (log(LL_DBG, "init_holiday: error opening holidayfile %s: %s!", filename, strerror(errno))));
+ return;
+ }
+
+ while((fgets(buffer, MAXBUFSZ, fp)) != NULL)
+ {
+ if(buffer[0] == '#' || buffer[0] == ' ' ||
+ buffer[0] == '\t' || buffer[0] == '\n')
+ {
+ continue;
+ }
+
+ ret = sscanf(buffer, "%d.%d.%d", &day, &month, &year);
+
+ if(ret != 3)
+ {
+ ret = sscanf(buffer, "%d.%d", &day, &month);
+ if(ret != 2)
+ {
+ log(LL_ERR, "init_holiday: parse error for string [%s]!", buffer);
+ exit(1);
+ }
+ year = 0;
+ }
+
+ if((newh = (struct holiday *) malloc(sizeof(struct holiday))) == NULL)
+ {
+ log(LL_ERR, "init_holiday: malloc failed for struct holiday!\n");
+ exit(1);
+ }
+
+ if(year)
+ {
+ DBGL(DL_VALID, (log(LL_DBG, "init_holidays: add %d.%d.%d", day, month, year)));
+ }
+ else
+ {
+ DBGL(DL_VALID, (log(LL_DBG, "init_holidays: add %d.%d", day, month)));
+ }
+
+ newh->day = day;
+ newh->month = month;
+ newh->year = year;
+ newh->next = NULL;
+
+ if(firsth == NULL)
+ {
+ firsth = newh;
+ }
+ else
+ {
+ lasth->next = newh;
+ }
+ lasth = newh;
+ }
+ fclose(fp);
+}
+
+/*---------------------------------------------------------------------------*
+ * free all holidays
+ *---------------------------------------------------------------------------*/
+void
+free_holidays(void)
+{
+ free_holiday(firsth);
+}
+
+/*---------------------------------------------------------------------------*
+ * free holidayes
+ *---------------------------------------------------------------------------*/
+static void
+free_holiday(struct holiday *ptr)
+{
+
+ if(ptr == NULL)
+ return;
+
+ if(ptr->next != NULL)
+ free_holiday(ptr->next);
+
+ free(ptr);
+}
+
+/*---------------------------------------------------------------------------*
+ * check if date/month/year is a holiday
+ *---------------------------------------------------------------------------*/
+int
+isholiday(int d, int m, int y)
+{
+ struct holiday *ch = NULL;
+
+ if(firsth == NULL)
+ return(0);
+
+ ch = firsth;
+
+ for(;;)
+ {
+ if(ch->day == d && ch->month == m)
+ {
+ if(ch->year == 0)
+ {
+ DBGL(DL_VALID, (log(LL_DBG, "isholiday: %d.%d is a holiday!", d, m)));
+ return(1);
+ }
+ else if(ch->year == y)
+ {
+ DBGL(DL_VALID, (log(LL_DBG, "isholiday: %d.%d.%d is a holiday!", d, m, y)));
+ return(1);
+ }
+ }
+
+ if(ch->next == NULL)
+ break;
+
+ ch = ch->next;
+ }
+ return(0);
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnd/isdnd.8 b/usr.sbin/i4b/isdnd/isdnd.8
new file mode 100644
index 0000000..e1bd734
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/isdnd.8
@@ -0,0 +1,423 @@
+.\"
+.\" Copyright (c) 1997, 2001 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: isdnd.8,v 1.29 2000/05/02 11:50:28 hm Exp $
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Wed May 2 10:48:30 2001]
+.\"
+.Dd May 2, 2001
+.Dt ISDND 8
+.Os
+.Sh NAME
+.Nm isdnd
+.Nd isdn4bsd ISDN connection management daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl c Ar configfile
+.Op Fl d Ar debuglevel
+.Op Fl f
+.Op Fl F
+.Op Fl l
+.Op Fl L Ar logfile
+.Op Fl P
+.Op Fl r Ar device
+.Op Fl s Ar facility
+.Op Fl t Ar terminaltype
+.Op Fl u Ar charging unit length
+.Op Fl m
+.Sh DESCRIPTION
+.Nm Isdnd
+is the isdn4bsd package daemon which manages all ISDN related connection
+and disconnection of ISDN devices supported by the package.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl c
+Use
+.Ar configfile
+as the name of the runtime configuration filename for
+.Nm
+instead of the default file
+.Li /etc/isdn/isdnd.rc .
+.It Fl d
+If debugging support is compiled into
+.Nm
+this option is used to specify the debugging level, or better which kind
+of debugging messages are displayed.
+The debugging level is the sum of the
+following values:
+.Pp
+.Bl -tag -width Ds -compact -offset indent
+.It Ar 0x001
+general debugging.
+.It Ar 0x002
+rates calculation.
+.It Ar 0x004
+timing calculations.
+.It Ar 0x008
+state transitions.
+.It Ar 0x010
+retry handling.
+.It Ar 0x020
+dialing.
+.It Ar 0x040
+process handling.
+.It Ar 0x080
+isdn4bsd kernel i/o calls.
+.It Ar 0x100
+controller and channel busy/free messages.
+.It Ar 0x200
+isdnd.rc configuration file processing.
+.It Ar 0x400
+outgoing call budget handling.
+.It Ar 0x800
+valid keyword and holiday file processing.
+.El
+.Pp
+The value can be specified in any number base supported by the
+.Xr sscanf 3
+library routine.
+.Pp
+In addition, this option accepts also the character 'n' as an argument to
+disable displaying debug messages on the full-screen display.
+.Pp
+.It Fl f
+Specifying this option causes
+.Nm
+to enter the full-screen mode of operation.
+When operating in this mode,
+entering the control character
+.Em Control-L
+causes the display to be refreshed and entering
+.Em Carriage-Return
+or
+.Em Enter
+will pop-up a command window.
+Because the
+.Nm
+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.
+See also the keyword
+.Em rotatesuffix
+in the system section of
+.Xr isdnd.rc 5 .
+.It Fl P
+This option prints out the parsed and verified isdnd configuration in the same
+format as the isdnd.rc file.
+This output can be used as an isdnd.rc file.
+This
+feature is especially useful when debugging an isdnd.rc file to see, what the
+default settings of options are when they are not set in the isdnd.rc input
+file.
+.Pp
+The
+.Nm
+exits after the printout is done.
+.It Fl F
+This option prevents
+.Nm
+to detach from the controlling tty and become a daemon.
+.It Fl r
+In conjunction with the
+.Fl t
+option,
+.Ar device
+specifies a terminal device which becomes the controlling tty for
+.Nm
+and on which the full-screen mode output is displayed.
+.It Fl s
+This option may be used to specify the logging facility in case
+.Xr syslog 3
+logging is configured and another facility than the default LOCAL0
+facility shall be used.
+The facility is to be specified as an integer in
+the range 0-11 or 16-23 (see the file /usr/include/syslog.h).
+.It Fl t
+In conjunction with the
+.Fl f
+and
+.Fl r
+options,
+.Ar terminaltype
+specifies a terminal type or termcap entry name (such as vt220) for the device
+used for
+.Nm
+full-screen output.
+This is useful if an unused (no getty running) tty line is
+used for full-screen output for which no
+.Li TERM
+environment variable exists.
+.It Fl u
+Specifies the length of a charging unit in case the config file entry
+keyword
+.Em unitlenghtsrc
+is set to
+.Em cmdl .
+.It Fl m
+If the isdn daemon is compiled with local or remote monitoring support,
+this option disables all monitoring access.
+It overrides the config
+file option
+.Em monitor-allowed .
+.El
+.Sh INTERACTION WITH THE KERNEL
+.Nm Isdnd
+communicates with the kernel part of isdn4bsd by receiving status and
+event messages
+.Xr ( read 2
+from device
+.Pa /dev/i4b )
+and by transmitting commands and responses
+.Xr ( ioctl 2
+from device
+.Pa /dev/i4b ) .
+.Pp
+The messages and message parameters are documented in the include
+file
+.Em /usr/include/machine/i4b_ioctl.h .
+.Pp
+Supported command and response messages (ioctls) to the kernel are:
+.Bl -tag -width Ds -compact -offset indent
+.It Ar I4B_CDID_REQ
+Request a unique Call Description IDentifier (cdid) which identifies
+uniquely a single interaction of the local D channel with the exchange.
+.It Ar I4B_CONNECT_REQ
+Actively request a call setup to a remote ISDN subscriber.
+.It Ar I4B_CONNECT_RESP
+Respond to an incoming call, either accept, reject or ignore it.
+.It Ar I4B_DISCONNECT_REQ
+Actively terminate a connection.
+.It Ar I4B_CTRL_INFO_REQ
+Request information about an installed ISDN controller card.
+.It Ar I4B_DIALOUT_RESP
+Give information about call setup to driver who requested dialing out.
+.It Ar I4B_TIMEOUT_UPD
+Update the kernels timeout value(s) in case of dynamically calculated
+shorthold mode timing changes.
+.It Ar I4B_UPDOWN_IND
+Inform the kernel userland drivers about interface soft up/down status
+changes.
+.It Ar I4B_CTRL_DOWNLOAD
+Download firmware to active card(s).
+.It Ar I4B_ACTIVE_DIAGNOSTIC
+Return diagnostic information from active cards.
+.El
+.Pp
+.Pp
+Supported status and event messages from the kernel are:
+.Bl -tag -width Ds -compact -offset indent
+.It Ar MSG_CONNECT_IND
+An incoming call from a remote ISDN user is indicated.
+.It Ar MSG_CONNECT_ACTIVE_IND
+After an incoming call has been accepted locally or an outgoing call has
+been accepted by a remote, the exchange signaled an active connection
+and the corresponding B-channel is switched through.
+.It Ar MSG_DISCONNECT_IND
+A call was terminated.
+.It Ar MSG_DIALOUT_IND
+A userland interface driver requests the daemon to dial out (typically a
+network interface when a packet arrives in its send queue).
+.It Ar MSG_IDLE_TIMEOUT_IND
+A call was terminated by the isdn4bsd kernel driver because a B-channel
+idle timeout occurred.
+.It Ar MSG_ACCT_IND
+Accounting information from a network driver.
+.It Ar MSG_CHARGING_IND
+Charging information from the kernel.
+.El
+.Pp
+.Ss OUTGOING CALLS
+Currently the only possibility to trigger an outgoing call is that an
+isdn4bsd network driver
+.Em (ipr<n>)
+sends a
+.Em MSG_DIALOUT_IND
+to the
+.Nm
+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 side timing out or the remote
+side hanging up the connection or the local side actively sending a
+.Em I4B_DISCONNECT_REQ
+ioctl message, both events are signaled to the
+.Nm
+by the kernel sending the
+.Em I4B_DISCONNECT_IND
+message and the CDID corresponding to the call is no longer valid.
+.Pp
+.Ss INCOMING CALLS
+Incoming calls are signaled to the
+.Nm
+by the kernel transmitting the
+.Em MSG_CONNECT_IND
+message to the daemon.
+.Pp
+With the information contained in this message, the
+.Nm
+searches the entry section of its configuration database and if a match is
+found, it accepts or rejects the call or, if no match is found, it ignores the
+call - all by issuing a
+.Em I4B_CONNECT_RESP
+ioctl message with the appropriate parameters to the kernel.
+.Pp
+In case the daemon decided to accept the call, the kernel signals this
+by sending a
+.Em MSG_CONNECT_ACTIVE_IND
+message to the daemon.
+.Pp
+The call is terminated by either the local side timing out or the remote
+side hanging up the connection or the local side actively sending a
+.Em I4B_DISCONNECT_REQ
+ioctl message, both events are signaled to the
+.Nm
+by the kernel sending the
+.Em I4B_DISCONNECT_IND
+message and the CDID corresponding to the call is no longer valid.
+.Sh SIGNALS
+Sending a HUP signal to
+.Nm
+causes all open connections to be terminated and the configuration file is
+reread.
+In case aliasfile handling was enabled, the aliasfile is also
+reread.
+.Pp
+Sending a USR1 signal to
+.Nm
+causes the accounting file and the logfile (if logging to a file is used
+instead of logging via the
+.Xr syslog 3
+facility) to be closed and reopened to make logfile rotation possible.
+.Sh ENVIRONMENT
+The following environment variables affect the execution of
+.Nm :
+.Bl -tag -width Ds
+.It Ev TERM
+The terminal type when running in full-screen display mode.
+See
+.Xr environ 7
+for more information.
+.El
+.Sh FILES
+.Bl -tag -width /etc/isdn/isdnd.rates
+.It Pa /dev/i4b
+The device-file used to communicate with the kernel ISDN driver subsystem.
+.It Pa /var/log/messages
+A record of the actions in case of syslogd logging support.
+.It Pa /var/log/isdnd.acct
+The default accounting information filename (if accounting is configured).
+.It Pa /var/log/isdnd.log
+The default logging filename (if logging to a file is configured).
+.It Pa /var/run/isdnd.pid
+The process id of the isdn daemon (also known as "lockfile" to isdnd, preventing multiple invocations of it).
+.It Pa /usr/local/lib/isdn
+.It Pa /etc/isdn
+The directory where isdnd expects some supplementary data files and programs
+for telephone answering support.
+.It Pa /etc/isdn/isdnd.rc
+The default runtime configuration file.
+.It Pa /etc/isdn/isdnd.rates
+The default unit charging rates specification file.
+.It Pa /etc/isdn/isdntel.alias
+The default table (if aliasing is enabled) to convert phone number to caller's name.
+.El
+.Sh EXAMPLES
+For a first try, the following command should be used to start
+.Nm
+in foreground mode for better debugging the configuration setup:
+.Bd -literal -offset indent
+isdnd -d0xf9 -F
+.Ed
+.Pp
+This will start isdnd with reasonable debugging settings and produce
+output on the current terminal.
+.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
+with reasonable debugging messages enabled, full-screen mode of operation,
+full-screen display redirected to /dev/ttyv3 and using a termcap entry
+for vt100 on this display.
+.Sh DIAGNOSTICS
+Exit status is 0 on success, 1 on error.
+.Sh SEE ALSO
+.Xr i4bing 4 ,
+.Xr i4bipr 4 ,
+.Xr i4bisppp 4 ,
+.Xr isdnd.rates 5 ,
+.Xr isdnd.rc 5 ,
+.Xr isdntel 8 ,
+.Xr isdntrace 8 ,
+.Xr syslogd 8
+.Sh BUGS
+Still one or more left.
+.Sh AUTHORS
+The
+.Nm
+daemon and this manual page were written by
+.An Hellmuth Michaelis Aq hm@FreeBSD.org .
diff --git a/usr.sbin/i4b/isdnd/isdnd.acct.5 b/usr.sbin/i4b/isdnd/isdnd.acct.5
new file mode 100644
index 0000000..fdb30d3
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/isdnd.acct.5
@@ -0,0 +1,107 @@
+.\"
+.\" Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: isdnd.acct.5,v 1.11 1999/12/13 22:11:55 hm Exp $
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Mon Dec 13 22:58:12 1999]
+.\"
+.Dd September 11, 1998
+.Dt ISDND.ACCT 5
+.Os
+.Sh NAME
+.Nm isdnd.acct
+.Nd isdn4bsd ISDN management daemon accounting file format
+.Sh DESCRIPTION
+The file
+.Pa isdnd.acct
+contains accounting information which is written if the variable
+.Em useacctfile
+in the
+.Xr isdnd 8
+configuration file
+.Xr isdnd.rc 5
+is set to
+.Em on
+and charging information transmission has been subscribed for the
+ISDN connection (AOCD or AOCE).
+.Pp
+If the variable
+.Em acctall
+is set to
+.Em on ,
+accounting information is written even if the local site was not charged
+or no charging information is available or is not subscribed.
+.Pp
+The general format of an accounting line is as follows:
+.Pp
+.Dl FROM - UNTIL NAME UNITS (SECONDS) (INBYTES/OUTBYTES)
+.Pp
+.Em FROM
+is the time the connection was established in the format
+.Dl Day.Month.Year Hour:Minutes:seconds
+.Pp
+.Em UNTIL
+is the time the connection was closed. The format is the same as
+described for
+.Em FROM
+above.
+.Pp
+.Em NAME
+is the symbolic name got from the
+.Em name
+entry of the
+.Xr isdnd.rc 5
+config file for this connection.
+.Pp
+.Em UNITS
+is the amount of charging units billed for the connection.
+.Pp
+.Em SECONDS
+is the number of seconds the connection lasted.
+.Pp
+.Em INBYTES
+and
+.Em OUTBYTES
+is the (optional) number of bytes that were transferred.
+.Sh FILES
+.Bl -tag -width /var/log/isdnd.acct -compact
+.It Pa /var/log/isdnd.acct
+The default accounting information file for the
+.Nm isdnd
+ISDN daemon.
+.El
+.Sh EXAMPLES
+This is a typical accounting line:
+.Pp
+.Dl 12.06.97 10:41:37 - 12.06.97 10:45:18 GROGGY 2 (65) (4711/1147)
+.Sh SEE ALSO
+.Xr isdnd.rc 5 ,
+.Xr isdnd 8
+.Sh AUTHORS
+The
+.Xr isdnd 8
+daemon and this manual page were written by
+.An Hellmuth Michaelis Aq hm@kts.org .
diff --git a/usr.sbin/i4b/isdnd/isdnd.h b/usr.sbin/i4b/isdnd/isdnd.h
new file mode 100644
index 0000000..91285f3
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/isdnd.h
@@ -0,0 +1,896 @@
+/*
+ * Copyright (c) 1997, 2002 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * i4b daemon - main header file
+ * -----------------------------
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Tue Mar 26 14:36:20 2002]
+ *
+ *---------------------------------------------------------------------------*/
+
+#ifndef _ISDND_H_
+#define _ISDND_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <strings.h>
+#include <string.h>
+#include <ctype.h>
+#include <syslog.h>
+#include <regex.h>
+#include <time.h>
+#include <errno.h>
+
+#ifdef USE_CURSES
+#include <curses.h>
+#endif
+
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+
+#include <sys/queue.h> /* TAILQ_ macros */
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#ifdef USE_RTPRIO
+#include <sys/rtprio.h>
+#endif
+
+#include <net/if_sppp.h>
+
+#include <machine/i4b_ioctl.h>
+#include <machine/i4b_cause.h>
+
+#include "config.h" /* compile time configuration */
+#include "pathnames.h" /* location of files */
+#include "alias.h" /* alias file processing */
+
+/*---------------------------------------------------------------------------*
+ * some general definitions
+ *---------------------------------------------------------------------------*/
+#define GOOD 0 /* general "good" or "ok" return*/
+#define ERROR (-1) /* general error return */
+#define WARNING (-2) /* warning return */
+#define INVALID (-1) /* an invalid integer */
+
+/*---------------------------------------------------------------------------*
+ * misc
+ *---------------------------------------------------------------------------*/
+#define RTPRIO_NOTUSED (-1) /* rtprio is not used for isdnd */
+
+/*---------------------------------------------------------------------------*
+ * debug flag bits
+ *---------------------------------------------------------------------------*/
+#define DL_MSG 0x0001 /* general debug messages */
+#define DL_RATES 0x0002 /* messages related to rates */
+#define DL_TIME 0x0004 /* messages related to timing */
+#define DL_STATE 0x0008 /* messages related to states changes */
+#define DL_RCVRY 0x0010 /* messages related to dial recovery */
+#define DL_DIAL 0x0020 /* messages related to dial recovery */
+#define DL_PROC 0x0040 /* messages related to process handling */
+#define DL_DRVR 0x0080 /* messages related to kernel i4b msg i/o*/
+#define DL_CNST 0x0100 /* messages related to controller state */
+#define DL_RCCF 0x0200 /* messages related to isdnd.rc at boot */
+#define DL_BDGT 0x0400 /* messages related to budgets */
+#define DL_VALID 0x0800 /* messages related to valid keyword */
+
+#ifdef DEBUG
+#define DBGL(cond, dolog) if(cond & debug_flags) dolog
+#else
+#define DBGL(cond, dolog)
+#endif
+
+/*---------------------------------------------------------------------------*
+ * curses fullscreen display definitions
+ *---------------------------------------------------------------------------*/
+
+/* window dimensions */
+#define UPPER_B 2 /* upper window start */
+
+/* horizontal positions for upper window */
+#define H_CNTL 0 /* controller */
+#define H_TEI 2 /* TEI */
+#define H_CHAN (H_TEI+4) /* channel */
+#define H_TELN (H_CHAN+2) /* telephone number */
+#define H_IFN (H_TELN+23) /* interfacename */
+#define H_IO (H_IFN+7) /* incoming or outgoing */
+#define H_OUT (H_IO+4) /* # of bytes out */
+#define H_OUTBPS (H_OUT+11) /* bytes per second out */
+#define H_IN (H_OUTBPS+5) /* # of bytes in */
+#define H_INBPS (H_IN+11) /* bytes per second in */
+#define H_UNITS (H_INBPS+6) /* # of charging units */
+
+/* fullscreen mode menu window */
+#define WMENU_LEN 35 /* width of menu window */
+#define WMENU_TITLE "Command" /* title string */
+#define WMENU_POSLN 10 /* menu position, line */
+#define WMENU_POSCO 5 /* menu position, col */
+#define WMITEMS 6 /* no of menu items */
+#define WMENU_HGT (WMITEMS + 4) /* menu window height */
+
+#define WREFRESH 0
+#define WHANGUP 1
+#define WREREAD 2
+#define WSHOW 3
+#define WBUDGET 4
+#define WQUIT 5
+
+#define WMTIMEOUT 5 /* timeout in seconds */
+
+/*---------------------------------------------------------------------------*
+ * charging rates
+ *---------------------------------------------------------------------------*/
+#define NDAYS 7 /* number of days in a week */
+#define NRATES 4 /* number of rate structures supported */
+
+/* struct for rates - each day has one or more */
+struct rates
+{
+ int start_time; /* hour and min at which this rate starts, e.g. 12:20 12*60+20*/
+ int end_time; /* hour and min at which this rate ends, e.g. 19:10 19*60+10*/
+ int rate; /* how long can I telephone at this price, seconds */
+ struct rates *next;
+};
+
+/*---------------------------------------------------------------------------*
+ * the internal identifiers for isdnd log levels. CAUTION: this has to stay
+ * in sync with the loglevel to text and sysloglevel table in log.c !!
+ *---------------------------------------------------------------------------*/
+enum logids
+{
+ LL_ERR, /* error conditions - everything which caused an error */
+ LL_WRN, /* warning conditions - nonfatal abnormal conditions */
+ LL_DMN, /* normal but significant condition - status of daemon */
+ LL_CHD, /* informational - everything regarding call handling */
+ LL_DBG, /* debug messages - everything which helps debugging */
+ LL_MER, /* monitor error messages - not sent to remote */
+ LL_PKT /* packet logging - log the first few packets */
+};
+
+/*---------------------------------------------------------------------------*
+ * state machine events
+ *---------------------------------------------------------------------------*/
+enum events
+{
+ /* incoming messages */
+
+ EV_MCI, /* MSG_CONNECT_IND */
+ EV_MCAI, /* MSG_CONNECT_ACTIVE_IND */
+ EV_MDI, /* MSG_DISCONNECT_IND */
+ EV_MDO, /* MSG_DIALOUT */
+
+ /* local requests */
+
+ EV_TIMO, /* timer expired */
+ EV_DRQ, /* disconnect request */
+ EV_CBRQ, /* callback request */
+ EV_ALRT, /* alerting request */
+
+ /* illegal */
+
+ EV_ILL /* illegal event */
+};
+
+#define N_EVENTS (EV_ILL+1) /* no of possible events */
+
+/*---------------------------------------------------------------------------*
+ * this struct describes the numbers to try to dial out
+ *---------------------------------------------------------------------------*/
+typedef struct {
+ char number[TELNO_MAX]; /* number */
+ char subaddr[SUBADDR_MAX]; /* subaddr */
+} number_t;
+
+/*---------------------------------------------------------------------------*
+ * this struct describes the numbers to try to dial out
+ *---------------------------------------------------------------------------*/
+typedef struct {
+ char number[TELNO_MAX]; /* remote number to dial */
+ char subaddr[SUBADDR_MAX]; /* remote subaddr */
+ int flag; /* usage flag */
+#define RNF_IDLE 0
+#define RNF_SUCC 1 /* last dial was ok */
+} remote_number_t;
+
+/*---------------------------------------------------------------------------*
+ * this struct describes numbers allowed to dial in
+ *---------------------------------------------------------------------------*/
+typedef struct {
+ char number[TELNO_MAX]; /* calling party number */
+ char subaddr[SUBADDR_MAX];/* calling party subaddr */
+} incoming_number_t;
+
+/*---------------------------------------------------------------------------*
+ * this structure describes a prematurely aborted called-back dialout
+ *---------------------------------------------------------------------------*/
+typedef struct {
+ int cdid; /* call handle */
+ int controller; /* the controller used to dial out */
+ int channel; /* the channel assigned to the outgoing call */
+ /* XXX - timeout handling and error recovery? */
+} phantom_t;
+
+/*---------------------------------------------------------------------------*
+ * this struct describes one complete configuration entry
+ *---------------------------------------------------------------------------*/
+typedef struct cfg_entry {
+
+ /* ====== filled in at startup configuration, then static ===========*/
+
+ char name[32]; /* id for this entry */
+
+ int isdncontroller; /* controller to use 0 ... n */
+ int isdnchannel; /* channel to use */
+
+ int isdntxdelin; /* tx delay, incoming connections */
+ int isdntxdelout; /* tx delay, outgoing connections */
+
+ int usrdevicename; /* userland device to use */
+ int usrdeviceunit; /* userland unit to use */
+
+ int remote_numbers_count; /* number of remote numbers */
+ int remote_subaddr_count; /* number of remote subaddr */
+#define MAXRNUMBERS 8 /* max remote numbers */
+
+ remote_number_t remote_numbers[MAXRNUMBERS]; /* remote numbers to dial */
+
+
+ int remote_numbers_handling; /* how to handle the remote dialing */
+#define RNH_NEXT 0 /* use next number after last successfull */
+#define RNH_LAST 1 /* use last successfull for next call */
+#define RNH_FIRST 2 /* always use first number for next call */
+
+ number_t local_phone_dialout; /* our number to tell remote*/
+ number_t local_phone_incoming; /* answer calls for this local number */
+
+#define MAX_INCOMING 8
+ int incoming_numbers_count; /* number of incoming allowed numbers */
+ int incoming_subaddr_count; /* number of incoming allowed subaddr */
+ incoming_number_t remote_phone_incoming[MAX_INCOMING]; /* answer calls from this remote machine */
+
+ int dialin_reaction; /* what to do with incoming calls */
+#define REACT_ACCEPT 0
+#define REACT_REJECT 1
+#define REACT_IGNORE 2
+#define REACT_ANSWER 3
+#define REACT_CALLBACK 4
+
+ int b1protocol; /* hdlc / raw */
+
+ int idle_time_in; /* max idle time incoming calls */
+ int idle_time_out; /* max idle time outgoing calls */
+
+ int shorthold_algorithm; /* shorthold algorithm */
+
+ int unitlength; /* length of a charging unit */
+#define UNITLENGTH_DEFAULT 60 /* last resort unit length */
+
+ int earlyhangup; /* time in seconds to hangup */
+ /* before the next expected */
+ /* charging unit */
+#define EARLYHANGUP_DEFAULT 5
+
+ int ratetype; /* type of rate */
+#define NO_RATE (NRATES+1)
+#define INVALID_RATE (-1)
+
+ int unitlengthsrc; /* where we get the unit length from */
+#define ULSRC_NONE 0 /* nowhere specified */
+#define ULSRC_CMDL 1 /* specified on commandline */
+#define ULSRC_CMDLMIN 5 /* minimum value from cmdl */
+#define ULSRC_CMDLMAX 3600 /* minimum value from cmdl */
+#define ULSRC_CONF 2 /* get it from config file */
+#define ULSRC_RATE 3 /* get it dynamic from ratesfile*/
+#define ULSRC_DYN 4 /* dynamic calculated from AOCD */
+
+ char *answerprog; /* program to use for answering */
+ char *connectprog; /* program run after negotiation finished */
+ char *disconnectprog; /* program run after shutdown is complete */
+
+ int callbackwait; /* time to wait before calling back */
+#define CALLBACKWAIT_MIN 1
+
+ int calledbackwait; /* time to wait for remote callback */
+#define CALLEDBACKWAIT_MIN 2
+
+ int dialretries; /* no. of dial tries */
+#define DIALRETRIES_DEF 1
+
+ int recoverytime; /* time between 2 dial tries */
+#define RECOVERYTIME_MIN 1
+
+ int dialrandincr; /* use random dial time incr */
+
+ int usedown; /* set interface down yes/no */
+ int downtries; /* retries before i/f is set down */
+#define DOWN_TRIES_MIN 2
+#define DOWN_TRIES_MAX 20
+ int downtime; /* time i/f is down */
+#define DOWN_TIME_MIN 10 /* 10 seconds */
+#define DOWN_TIME_MAX 3600 /* 1 hour */
+
+ int dialouttype; /* type of outgoing connection */
+#define DIALOUT_NORMAL 0 /* normal dialout behaviour */
+#define DIALOUT_CALLEDBACK 1 /* remote is expected to callback */
+
+ int alert; /* alert time in sec if nonzero */
+#define MINALERT 5 /* 5 secs min */
+#define MAXALERT (3*60) /* 3 minutes max */
+
+ int inout; /* in/out, in-only or out-only */
+#define DIR_INOUT 0
+#define DIR_INONLY 1
+#define DIR_OUTONLY 2
+
+ int usesubaddr; /* use subaddresses */
+
+ int budget_callbackperiod; /* length of a budget period (s)*/
+ int budget_callbackncalls; /* call budget for a period */
+ char *budget_callbacks_file; /* filename to store callback stats */
+ char budget_callbacksfile_rotate;
+
+ int budget_calloutperiod; /* length of a budget period (s)*/
+ int budget_calloutncalls; /* call budget for a period */
+ char *budget_callouts_file; /* filename to store callout stats */
+ char budget_calloutsfile_rotate;
+
+ int ppp_expect_auth;
+ int ppp_send_auth;
+#define AUTH_UNDEF 0
+#define AUTH_NONE 1
+#define AUTH_PAP 2
+#define AUTH_CHAP 3
+
+ int ppp_auth_flags;
+#define AUTH_RECHALLENGE 0x01
+#define AUTH_REQUIRED 0x02
+
+ char ppp_expect_name[AUTHNAMELEN]; /* PPP PAP/CHAP login name */
+ char ppp_send_name[AUTHNAMELEN];
+
+ char ppp_expect_password[AUTHKEYLEN];
+ char ppp_send_password[AUTHKEYLEN];
+
+ int day; /* days valid */
+#define SU 0x01
+#define MO 0x02
+#define TU 0x04
+#define WE 0x08
+#define TH 0x10
+#define FR 0x20
+#define SA 0x40
+#define HD 0x80 /* holiday */
+ int fromhr; /* time valid */
+ int frommin;
+ int tohr;
+ int tomin;
+
+ time_t maxconnecttime; /* maximum connection time */
+
+/*===========================================================================*/
+/*============ filled in after start, then dynamic ==========================*/
+/*===========================================================================*/
+
+ int cdid; /* cdid for call */
+#define CDID_RESERVED (-1)
+
+ int isdncontrollerused; /* the one we are using */
+ int isdnchannelused; /* the one we are using */
+
+ int fs_position; /* fullscreen position */
+
+ int state; /* state of connection */
+#define ST_IDLE 0 /* connection is idle / disconnected */
+
+ /* normal dial out to remote */
+#define ST_DIAL 1 /* dialing */
+#define ST_DIALRTMRCHD 2 /* wait for dial retry time reached */
+#define ST_DIALRETRY 3 /* last/first dialing failed, retry */
+
+ /* PCB: passive callback, i'm being called back */
+#define ST_PCB_DIAL 4 /* dialing, trigger a callback */
+#define ST_PCB_DIALFAIL 5 /* dialing failed triggering a callbk */
+#define ST_PCB_WAITCALL 6 /* waiting for callback from remote */
+
+ /* ACB: active callback, i'm calling back */
+#define ST_ACB_WAITDISC 7 /* got call, wait for disconnect */
+#define ST_ACB_WAITDIAL 8 /* wait until allowed to callback */
+#define ST_ACB_DIAL 9 /* callback to remote */
+#define ST_ACB_DIALFAIL 10 /* callback to remote failed */
+
+ /* normal non-dialling states */
+#define ST_ACCEPTED 11 /* remote accepted */
+#define ST_CONNECTED 12 /* connected with remote */
+#define ST_WAITDISCI 13 /* tx disc req, wait for disc ind */
+#define ST_DOWN 14 /* interface is down */
+#define ST_ALERT 15 /* interface is waiting for alert time*/
+
+ /* illegal and pseudo states */
+#define ST_ILL 16 /* illegal state */
+#define ST_SUSE 17 /* subroutine sets new state */
+
+#define N_STATES (ST_ILL+1) /* max number of states */
+
+ cause_t disc_cause; /* cause from disconnect */
+
+ int local_disconnect; /* flag, who disconnected */
+#define DISCON_LOC 0
+#define DISCON_REM 1
+
+ int timerval; /* value for timer, 0 if inactive */
+ int timerremain; /* remaining time */
+
+ int hangup; /* flag, hangup connection asap */
+
+ number_t real_phone_incoming; /* real remote telno in case of wildcard */
+
+ int last_remote_number; /* index of last used dialout number*/
+
+ number_t remote_phone_dialout; /* used remote number to dial */
+
+ int direction; /* incoming or outgoing */
+#define DIR_IN 0
+#define DIR_OUT 1
+
+ int charge; /* charge in units */
+ int last_charge; /* last charge in units */
+
+ int inbytes; /* # of bytes from remote */
+ int iinbytes; /* # of bytes from remote on the line */
+ int inbps; /* bytes/sec from remote */
+ int outbytes; /* # of bytes to remote */
+ int ioutbytes; /* # of bytes to remote on the line */
+ int outbps; /* bytes/sec to remote */
+
+ time_t connect_time; /* time connection was established */
+
+ time_t aoc_last; /* last AOCD timestamp */
+ time_t aoc_now; /* current AOCD timestamp */
+ time_t aoc_diff; /* current unit length */
+ time_t aoc_lastdiff; /* last charge unit length */
+ int aoc_valid; /* flag: time diff is valid */
+#define AOC_INVALID 0 /* aoc_diff is NOT valid */
+#define AOC_VALID 1 /* aoc_diff is valid */
+
+ time_t last_dial_time; /* time of last dialing */
+ time_t last_release_time; /* time of last hangup */
+
+ int dial_count; /* number of dialout tries */
+ int randomtime; /* random() part of recoverytime*/
+#define RANDOM_MASK 0x04 /* bits used from randomtime */
+
+ int down_retry_count; /* retry cycle count for usedown*/
+ time_t went_down_time; /* time i/f went down */
+ phantom_t saved_call; /* outgoing call state if called
+ back too early */
+
+ int alert_time; /* count down of alert time */
+ char display[DISPLAY_MAX];
+
+ time_t budget_callbackperiod_time; /* end of current period */
+ int budget_callbackncalls_cnt; /* amount of calls left */
+
+ int budget_callback_req; /* requests */
+ int budget_callback_done; /* call done */
+ int budget_callback_rej; /* call refused */
+
+ time_t budget_calloutperiod_time; /* end of current period */
+ int budget_calloutncalls_cnt; /* amount of calls left */
+
+ int budget_callout_req; /* requests */
+ int budget_callout_done; /* call done */
+ int budget_callout_rej; /* call refused */
+
+ int budget_calltype; /* type of call */
+#define BUDGET_TYPE_CBACK 1
+#define BUDGET_TYPE_COUT 2
+
+ char keypad[KEYPAD_MAX]; /* keypad string */
+} cfg_entry_t;
+
+/*---------------------------------------------------------------------------*
+ * this struct describes state of controller with MAX_BCHAN b channels
+ *---------------------------------------------------------------------------*/
+typedef struct isdn_ctrl_state {
+ int ctrl_type; /* type: active/passive */
+ int card_type; /* manufacturer (CARD_XXXX) */
+ int protocol; /* ISDN D-channel protocol */
+ char* firmware; /* loadable fimrware file name */
+
+ int state; /* controller state */
+#define CTRL_DOWN 0 /* controller inoparable */
+#define CTRL_UP 1 /* controller may be used */
+#define MAX_BCHAN 30
+ int stateb[MAX_BCHAN]; /* b channel state */
+#define CHAN_IDLE 0 /* channel is free for usage */
+#define CHAN_RUN 1 /* channel is occupied */
+ int nbch; /* number of b channels */
+ int freechans; /* number of unused channels */
+ int tei; /* tei or -1 if invalid */
+ int l1stat; /* layer 1 state */
+ int l2stat; /* layer 2 state */
+} isdn_ctrl_state_t;
+
+/*---------------------------------------------------------------------------*
+ * this struct describes a logging regular expression
+ *---------------------------------------------------------------------------*/
+struct rarr {
+ int re_flg; /* valid entry flag */
+ char *re_expr; /* plain text expression */
+ regex_t re; /* compiled expression */
+ char *re_prog; /* the program to be executed */
+};
+
+#ifdef I4B_EXTERNAL_MONITOR
+/* for each rights entry we keep one of this structures around: */
+struct monitor_rights {
+ TAILQ_ENTRY(monitor_rights) list; /* a list of this structures */
+ char name[FILENAME_MAX]; /* net/host spec or filename */
+ int rights; /* bitmask of allowed acces rights */
+ u_int32_t net; /* net/host address (host byte order!) */
+ u_int32_t mask; /* bitmask 1 = network, 0 = host (host byte order!) */
+ int local; /* zero if remote access via tcp/ip */
+};
+#endif
+
+/*---------------------------------------------------------------------------*
+ * global variables, storage allocation
+ *---------------------------------------------------------------------------*/
+#ifdef MAIN
+
+int isdnfd; /* file handle, /dev/i4b */
+
+char mailto[MAXPATHLEN] = ""; /* panic mail address */
+char mailer[MAXPATHLEN] = ""; /* panic mail address */
+
+char *configfile = CONFIG_FILE_DEF; /* configuration filename */
+int config_error_flag = 0; /* error counter */
+
+#ifdef DEBUG
+int do_debug = 0; /* debug mode flag */
+int debug_flags = 0; /* debug options */
+int debug_noscreen = 0; /* not on fullscreen */
+#endif
+
+int do_bell = 0; /* bell on connect/disconnect */
+
+int do_fork = 1; /* run as daemon/foreground */
+
+int do_ttytype = 0; /* got new terminal type */
+char *ttype = ""; /* termcap entry name string */
+
+int do_rdev = 0; /* redirect output */
+char *rdev = ""; /* new device string */
+
+int do_print = 0; /* config file printout */
+
+int got_unitlen = 0; /* flag, got length of a unit */
+time_t unit_length; /* length of a unit */
+
+cfg_entry_t cfg_entry_tab[CFG_ENTRY_MAX]; /* configuration table */
+isdn_ctrl_state_t isdn_ctrl_tab[ISDN_CTRL_MAX]; /* controller states table */
+
+int ncontroller = 0; /* # of controllers available */
+int nentries = 0; /* # of entries in config tab */
+
+int uselogfile = 0; /* flag, use a logfile */
+char logfile[MAXPATHLEN] = LOG_FILE_DEF; /* log filename */
+FILE *logfp = NULL; /* log file pointer */
+int logfacility = LOG_LOCAL0; /* the syslog facility used */
+int nregex = 0; /* number of reg expr */
+struct rarr rarr[MAX_RE]; /* regexpr & progs table */
+
+char ratesfile[MAXPATHLEN] = RATES_FILE_DEF; /* rates filename */
+char *rate_error = NULL; /* errorcase: error string */
+int got_rate = 0; /* flag, ratesfile found */
+struct rates *rates[NRATES][NDAYS]; /* the rates structure */
+
+int useacctfile = 0; /* flag, write accounting */
+char acctfile[MAXPATHLEN] = ACCT_FILE_DEF; /* accounting filename */
+FILE *acctfp = NULL; /* accounting file pointer */
+int acct_all = 1; /* account all connections */
+
+int aliasing = 0; /* enable alias processing */
+char aliasfile[MAXPATHLEN] = ALIASFILE; /* alias file location */
+
+int do_fullscreen = 0; /* fullscreen log */
+int curses_ready = 0; /* curses initialized */
+
+#ifdef USE_CURSES
+WINDOW *upper_w; /* curses upper window pointer */
+WINDOW *mid_w; /* curses mid window pointer */
+WINDOW *lower_w; /* curses lower window pointer */
+#endif
+
+int rt_prio = RTPRIO_NOTUSED; /* realtime priority */
+
+/* monitor via network */
+
+int do_monitor = 0;
+int inhibit_monitor = 0;
+#ifdef I4B_EXTERNAL_MONITOR
+int monitorport = DEF_MONPORT;
+#else
+int monitorport = -1;
+#endif
+int accepted = 0;
+
+int isdntime = 0; /* flag, log time from exchange */
+int extcallattr = 0; /* flag, display extended caller attributes */
+
+char tinainitprog[MAXPATHLEN] = TINA_FILE_DEF;
+
+char rotatesuffix[MAXPATHLEN] = "";
+
+time_t starttime = 0;
+
+char holidayfile[MAXPATHLEN] = HOLIDAY_FILE_DEF; /* holiday filename */
+
+#else /* !MAIN */
+
+int isdnfd;
+
+char mailto[MAXPATHLEN];
+char mailer[MAXPATHLEN];
+
+char *configfile;
+int config_error_flag;
+
+#ifdef DEBUG
+int do_debug;
+int debug_flags;
+int debug_noscreen;
+#endif
+
+int do_bell;
+
+int do_fork;
+
+int do_ttytype;
+char *ttype;
+
+int do_rdev;
+char *rdev;
+
+int do_print;
+
+int got_unitlen;
+time_t unit_length;
+
+cfg_entry_t cfg_entry_tab[CFG_ENTRY_MAX]; /* configuration table */
+isdn_ctrl_state_t isdn_ctrl_tab[ISDN_CTRL_MAX]; /* controller states table */
+
+int ncontroller;
+int nentries;
+
+int uselogfile;
+char logfile[MAXPATHLEN];
+FILE *logfp;
+int logfacility;
+int nregex;
+struct rarr rarr[MAX_RE];
+
+char ratesfile[MAXPATHLEN];
+char *rate_error;
+int got_rate;
+struct rates *rates[NRATES][NDAYS];
+
+int useacctfile;
+char acctfile[MAXPATHLEN];
+FILE *acctfp;
+int acct_all;
+
+int aliasing;
+char aliasfile[MAXPATHLEN];
+
+int do_fullscreen;
+int curses_ready;
+
+#ifdef USE_CURSES
+WINDOW *upper_w;
+WINDOW *mid_w;
+WINDOW *lower_w;
+#endif
+
+int rt_prio;
+
+int do_monitor;
+int inhibit_monitor;
+int monitorport;
+int accepted;
+
+int isdntime;
+int extcallattr;
+
+char tinainitprog[MAXPATHLEN];
+
+char rotatesuffix[MAXPATHLEN];
+
+time_t starttime;
+
+char holidayfile[MAXPATHLEN];
+
+#endif /* MAIN */
+
+char * bdrivername ( int drivertype );
+void cfg_setval ( int keyword );
+void check_and_kill ( cfg_entry_t *cep );
+void check_pid ( void );
+void close_allactive ( void );
+void configure ( char *filename, int reread );
+void daemonize ( void );
+void dialresponse(cfg_entry_t *cep, int dstat);
+void display_acct ( cfg_entry_t *cep );
+void display_bell ( void );
+void display_ccharge ( cfg_entry_t *cep, int units );
+void display_chans ( void );
+void display_charge ( cfg_entry_t *cep );
+void display_connect ( cfg_entry_t *cep );
+void display_disconnect ( cfg_entry_t *cep );
+void display_l12stat(int controller, int layer, int state);
+void display_tei(int controller, int tei);
+void display_updown ( cfg_entry_t *cep, int updown );
+void do_exit ( int exitval );
+void do_menu ( void );
+int exec_answer ( cfg_entry_t *cep );
+int exec_connect_prog ( cfg_entry_t *cep, const char *prog, int link_down );
+pid_t exec_prog ( char *prog, char **arglist );
+cfg_entry_t * find_by_device_for_dialout ( int drivertype, int driverunit );
+cfg_entry_t *find_by_device_for_dialoutnumber(msg_dialoutnumber_ind_t *mp);
+cfg_entry_t *find_by_device_for_keypad(int drivertype, int driverunit, int cmdlen, char *cmd);
+cfg_entry_t * find_matching_entry_incoming ( msg_connect_ind_t *mp );
+cfg_entry_t * find_active_entry_by_driver ( int drivertype, int driverunit );
+void finish_log ( void );
+char * getlogdatetime ( void );
+int get_cdid ( void );
+cfg_entry_t * get_cep_by_cc ( int ctrlr, int chan );
+cfg_entry_t * get_cep_by_driver ( int drivertype, int driverunit );
+cfg_entry_t * get_cep_by_cdid ( int cdid );
+int get_current_rate ( cfg_entry_t *cep, int logit );
+void handle_charge ( cfg_entry_t *cep );
+void handle_recovery ( void );
+void handle_scrprs(int cdid, int scr, int prs, char *caller);
+void if_up(cfg_entry_t *cep);
+void if_down(cfg_entry_t *cep);
+void init_controller ( void );
+void init_controller_protocol ( void );
+void init_log ( void );
+void init_screen ( void );
+void log ( int what, const char *fmt, ... );
+int main ( int argc, char **argv );
+void msg_accounting ( msg_accounting_ind_t *mp );
+void msg_alert_ind ( msg_alert_ind_t *mp );
+void msg_charging_ind ( msg_charging_ind_t *mp );
+void msg_connect_active_ind ( msg_connect_active_ind_t *mp );
+void msg_connect_ind ( msg_connect_ind_t *mp );
+void msg_pdeact_ind(msg_pdeact_ind_t *md);
+void msg_negcomplete_ind(msg_negcomplete_ind_t *ind);
+void msg_ifstatechg_ind(msg_ifstatechg_ind_t *ind);
+void msg_drvrdisc_req(msg_drvrdisc_req_t *mp);
+void msg_dialout ( msg_dialout_ind_t *mp );
+void msg_dialoutnumber(msg_dialoutnumber_ind_t *mp);
+void msg_disconnect_ind ( msg_disconnect_ind_t *mp );
+void msg_idle_timeout_ind ( msg_idle_timeout_ind_t *mp );
+void msg_l12stat_ind(msg_l12stat_ind_t *ml);
+void msg_teiasg_ind(msg_teiasg_ind_t *mt);
+void msg_proceeding_ind ( msg_proceeding_ind_t *mp );
+void msg_packet_ind( msg_packet_ind_t *mp );
+const char * name_of_controller(int ctrl_type, int card_type);
+void next_state ( cfg_entry_t *cep, int event );
+char * print_i4b_cause( cause_t code );
+char * printstate ( cfg_entry_t *cep );
+int readrates ( char *filename );
+int ret_channel_state(int controller, int channel);
+void reopenfiles ( int dummy );
+void rereadconfig ( int dummy );
+void select_first_dialno ( cfg_entry_t *cep );
+void select_next_dialno ( cfg_entry_t *cep );
+void select_this_dialno ( cfg_entry_t *cep );
+int sendm_alert_req ( cfg_entry_t *cep );
+int sendm_connect_req ( cfg_entry_t *cep );
+int sendm_connect_resp ( cfg_entry_t *cep, int cdid, int response, cause_t cause );
+int sendm_disconnect_req ( cfg_entry_t *cep, cause_t cause );
+int set_channel_busy(int controller, int channel);
+int set_channel_idle(int controller, int channel);
+int setup_dialout(cfg_entry_t *cep);
+void sigchild_handler ( int sig );
+void start_timer ( cfg_entry_t *cep, int seconds );
+void stop_timer ( cfg_entry_t *cep );
+void unitlen_chkupd( cfg_entry_t *cep );
+void write_pid ( void );
+void yyerror ( const char *msg );
+
+void error_exit(int exitval, const char *fmt, ...);
+
+/* montior server module */
+void monitor_init();
+void monitor_exit();
+void monitor_clear_rights();
+void monitor_fixup_rights();
+int monitor_start_rights(const char *clientspec);
+void monitor_add_rights(int rights);
+
+/* possible return codes from monitor_start_rights: */
+#define I4BMAR_OK 0 /* rights added successfully */
+#define I4BMAR_LENGTH 1 /* local socket name to long */
+#define I4BMAR_DUP 2 /* entry already exists */
+#define I4BMAR_CIDR 3 /* cidr netmask is invalid */
+#define I4BMAR_NOIP 4 /* host/net could not be resolved */
+
+int monitor_create_local_socket();
+
+#ifndef I4B_NOTCPIP_MONITOR
+int monitor_create_remote_socket(int portno);
+#endif
+
+void monitor_prepselect(fd_set *selset, int *max_fd);
+void monitor_handle_input(fd_set *selset);
+void monitor_handle_connect(int sockfd, int is_local);
+void monitor_evnt_charge(cfg_entry_t *cep, int units, int estimated);
+void monitor_evnt_connect(cfg_entry_t *cep);
+void monitor_evnt_disconnect(cfg_entry_t *cep);
+void monitor_evnt_updown(cfg_entry_t *cep, int up);
+void monitor_evnt_log(int prio, const char * what, const char * msg);
+
+void monitor_evnt_l12stat(int controller, int layer, int state);
+void monitor_evnt_tei(int controller, int tei);
+void monitor_evnt_acct(cfg_entry_t *cep);
+
+/* controller.c */
+
+void init_active_controller(void);
+int set_controller_state(int controller, int state);
+int get_controller_state(int controller);
+int decr_free_channels(int controller);
+int incr_free_channels(int controller);
+int get_free_channels(int controller);
+int set_channel_busy(int controller, int channel);
+int set_channel_idle(int controller, int channel);
+int ret_channel_state(int controller, int channel);
+
+/* alias.c */
+
+void init_alias(char *filename);
+void free_aliases(void);
+char *get_alias(char *number);
+
+void upd_callstat_file(char *filename, int rotateflag);
+
+/* holiday.c */
+
+void init_holidays(char *filename);
+void free_holidays(void);
+int isholiday(int d, int m, int y);
+
+#endif /* _ISDND_H_ */
diff --git a/usr.sbin/i4b/isdnd/isdnd.rates.5 b/usr.sbin/i4b/isdnd/isdnd.rates.5
new file mode 100644
index 0000000..7ada783
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/isdnd.rates.5
@@ -0,0 +1,113 @@
+.\"
+.\" Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: isdnd.rates.5,v 1.10 1999/12/13 22:11:55 hm Exp $
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Mon Dec 13 22:59:31 1999]
+.\"
+.Dd September 11, 1998
+.Dt ISDND.RATES 5
+.Os
+.Sh NAME
+.Nm isdnd.rates
+.Nd isdn4bsd ISDN management daemon rates description file
+.Sh DESCRIPTION
+The file
+.Pa isdnd.rates
+contains descriptions how long charging units last at a given time of day,
+day of week and the distance to the destination. If this file is available,
+this information may be used by the
+.Xr isdnd 8
+ISDN connection management daemon to calculate the short hold time for a
+connection.
+.Pp
+The format of a rate entry line is as follows:
+.Pp
+The first field, the
+.Pq Fa rate-code
+defines a collection of rates (for each day of the week) which can be
+referenced in the
+.Xr isdnd 8
+configuration file
+.Xr isdnd.rc 5 .
+This field must start with the identifier
+.Dq ra
+followed by a digit in the range of zero to four.
+.Pp
+The second field, the
+.Pq Fa day-number
+selects the day of week for which this entry defines the rates, where 0 stands
+for Sunday, 1 for Monday and so on until the digit 6 which stands for Saturday.
+.Pp
+The rest of the line consists of one or more space separated fields which have
+the following syntax:
+.Bd -ragged -offset indent
+start_hour.start_minutes-end_hour.end_minutes:charge_unit_length
+.Ed
+.Pp
+Start_hour and start_minutes define the begin of a time section and end_hour
+and end_minutes define the end. Charge_unit_length define the length of a
+charging unit in the previously defined time section. No spaces or tabs are
+allowed inside this field. The hour and minutes specifications MUST have
+exactly 2 digits, in case just one digit is needed, a leading 0 must be used.
+.Pp
+For example,
+.Bd -ragged -offset indent
+14.00-18.00:90
+.Ed
+.Pp
+defines, that between 2:00 PM and 6:00 PM the length of one charging unit
+lasts 90 seconds.
+.Sh FILES
+.Bl -tag -width /etc/isdn/isdnd.rates -compact
+.It Pa /etc/isdn/isdnd.rates
+The default rates specification file for the
+.Nm isdnd
+ISDN daemon.
+.El
+.Sh EXAMPLES
+The line:
+.Bd -literal
+ra0 0 00.00-05.00:240 05.00-21.00:150 21.00-24.00:240
+.Ed
+.Pp
+defines the unit lengths for a Sunday.
+.Sh SEE ALSO
+.Xr isdnd.rc 5 ,
+.Xr isdnd 8
+.Sh AUTHORS
+.An -nosplit
+The rates subsystem for the
+.Xr isdnd 8
+daemon to which
+.Nm
+belongs was designed and written by
+.An Gary Jennejohn .
+.Pp
+The
+.Xr isdnd 8
+daemon and this manual page were written by
+.An Hellmuth Michaelis Aq hm@kts.org .
diff --git a/usr.sbin/i4b/isdnd/isdnd.rc.5 b/usr.sbin/i4b/isdnd/isdnd.rc.5
new file mode 100644
index 0000000..1533b93
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/isdnd.rc.5
@@ -0,0 +1,1070 @@
+.\"
+.\" Copyright (c) 1997, 2002 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Tue Mar 26 14:35:02 2002]
+.\"
+.Dd March 26, 2002
+.Dt ISDND.RC 5
+.Os
+.Sh NAME
+.Nm isdnd.rc
+.Nd isdn4bsd ISDN connection management daemon config file format
+.Sh DESCRIPTION
+The file
+.Pa /etc/isdn/isdnd.rc
+contains (if not otherwise specified on the command line) the runtime
+configuration for the
+.Xr isdnd 8
+ISDN connection management daemon which is part of the isdn4bsd package.
+.Pp
+The configuration file consists of keywords which start in column 1 followed by
+one or more spaces or tabs, an equal sign, one or more spaces or tabs
+and a keyword dependent parameter value.
+.Pp
+A line beginning with '#' is treated as a comment line.
+.Pp
+For keywords requiring the specification of a boolean value, the truth
+value can be either
+.Em yes
+or
+.Em on
+while the false value can be either
+.Em no
+or
+.Em off .
+.Pp
+The configuration file consists of one
+.Em system
+section, one or more optional
+.Em controller
+sections and one or more
+.Em entry
+sections.
+In the
+.Em system
+section parameters regarding the daemon operation or parameters
+not associated with a single remote connection can be set.
+In the
+.Em controller
+section parameters regarding a particular controller can be set.
+In the
+.Em entry
+section(s) parameters directly associated with a single remote
+connection can be set.
+.Pp
+The following keywords are recognized by
+.Nm isdnd :
+.Pp
+.Bl -tag -width system
+.It Li system
+This keyword starts the system configuration section.
+It must not
+have a parameter and may be used only once.
+The keyword is mandatory.
+The following keywords are valid in the system configuration section:
+.Bl -tag -width useacctfile
+.It Li acctall
+If this parameter is set to
+.Em on ,
+accounting information is written even if the local site was not charged
+or no charging information is available or is not subscribed.
+(optional)
+.It Li acctfile
+Specifies the name of the accounting file which is used when the keyword
+.Em useacctfile
+(see below) is set to
+.Em on .
+See also system keyword
+.Em rotatesuffix .
+If this keyword is omitted the system default is used.
+(optional)
+.It Li aliasing
+If this parameter is set to
+.Em on ,
+alias processing of telephone-number to name is enabled (see also the
+.Em aliasfile
+keyword below).
+The default is off.
+(optional)
+.It Li aliasfile
+Specifies the name of the telephone number-to-name alias database file shared
+with the
+.Xr isdntel 1
+utility when alias processing is enabled via the
+.Em aliasing
+keyword.
+(optional)
+.It Li beepconnect
+In full-screen mode, if this parameter is set to
+.Em on ,
+ring the bell when connecting or disconnecting a call.
+.It Li extcallattr
+If this parameter is set to
+.Em on ,
+the extended caller attributes "screening indicator" and "presentation
+indicator" are written to the log-file.
+The default is off.
+(optional)
+.It Li holidayfile
+Specifies the name of the holiday file containing the dates of holidays.
+This file is used in conjunction with the
+.Em valid
+keyword to lookup the dates of holidays.
+(optional)
+.It Li isdntime
+If this parameter is set to
+.Em on ,
+date/time information from the exchange (if provided) is written to the
+log-file.
+The default is off.
+(optional)
+.It Li mailer
+This keyword is used to specify the path/name of a mail program which
+which is able to use the "-s" flag to specify a subject on its
+command line.
+In case of a fatal error exit of
+.Nm
+this program is used to send mail to an administrator specified by
+the keyword
+.Em mailto .
+(optional)
+.It Li mailto
+This keyword is used to specify the email address of someone to notify
+in case of a fatal error exit of
+.Nm .
+(See also keyword
+.Em mailer ) .
+(optional)
+.It Li monitor-allowed
+If this parameter is set to
+.Em on
+or
+.Em yes ,
+monitoring via a local or remote machine is enabled.
+This parameter is optional and is set to
+.Em off
+by default.
+.It Li monitor-port
+sets the TCP port number for remote monitoring.
+This integer parameter is optional and is set to port 451 by default.
+.It Li monitor
+This keyword specifies a local socket name or a host or network for remote
+monitoring.
+The
+.Em monitor
+specification may either be:
+.Pp
+.Bl -tag -width Ds -compact
+.It Ar the name of a local (UNIX-domain) socket
+this MUST start with a "/", example: /var/run/isdn-monitor
+.It Ar a dotted-quad host specification
+example: 192.168.1.2
+.It Ar a dotted-quad network address with netmask
+example: 192.168.1.0/24
+.It Ar a resolvable host name
+example: localhost
+.It Ar a resolvable network name with netmask
+example: up-vision-net/24
+.El
+.It Li monitor-access
+This keyword specifies the access rights for a previously used
+.Em monitor
+keyword.
+The supported access rights are:
+.Pp
+.Bl -tag -width Ds -compact
+.It Ar fullcmd
+.It Ar restrictedcmd
+.It Ar channelstate
+.It Ar logevents
+.It Ar callin
+.It Ar callout
+.El
+.It Li ratesfile
+Specifies the name of the ratesfile.
+If this keyword is omitted the system
+default is used.
+(optional)
+.It Li regexpr
+This keyword is used to specify regular expressions.
+It can be specified
+more than once up to a compile time dependent value (currently set to 5 by
+the MAX_RE definition in the source).
+.Pp
+All specified regular expressions are compared to the log strings at runtime
+and if a match is found, a program is run with the log text as a parameter
+(see also the keyword
+.Em regprog
+below).
+.Pp
+For an explanation how regular expressions are specified, please have a
+look at
+.Xr re_format 7
+and
+.Xr regex 3 .
+The
+.Em extended
+regular expression syntax is supported here.
+.Pp
+Hint: it might be necessary to properly quote the expression to avoid
+improper interpretation by the configuration file parser.
+(optional)
+.It Li regprog
+This keyword is used to specify the name of a program which is run in
+case a corresponding regular expression is matched by a logging string.
+.Nm Isdnd
+expects to find the program below the path
+.Pa /etc/isdn
+which is prepended to the string specified as a parameter to this keyword.
+(optional)
+.It Li rotatesuffix
+Specifies a suffix for renaming the log- and the accounting-filename.
+In case
+rotatesuffix is used and a USR1 signal is sent to isdnd, the log-file and the
+accounting file is not only closed and reopened but the old log-file is also
+renamed to the former filename with the rotatesuffix string appended.
+If this keyword is omitted, the log-files are just closed and reopened; this
+is also the default behavior.
+(optional)
+.It Li rtprio
+Specifies the real-time priority
+.Nm isdnd
+runs at as an integer value in the range 0...31 with 0 being the highest
+priority.
+This keyword is optional; if not specified the process priority of
+.Nm isdnd
+is not touched in any way.
+( See also
+.Xr rtprio 1 ) .
+This keyword is only available if
+.Nm
+was compiled with -DUSE_RTPRIO.
+.It Li useacctfile
+If this parameter is set to
+.Em on
+charging (if available) and accounting information is written to the
+accounting file.
+(optional)
+.El
+.It Li controller
+This keyword starts the controller configuration section.
+It must not
+have a parameter and may be used once for every controller.
+The keyword
+is optional.
+The following keywords are valid in a controller
+configuration section:
+.Bl -tag -width useacctfile
+.It Li protocol
+This keyword is used to set the D-channel protocol for the S0-bus a
+controller is connected to.
+The following parameters are currently
+supported:
+.Pp
+.Bl -tag -width calledback -compact
+.It Ar dss1
+The DSS1 or so-called "Euro-ISDN" D-channel protocol according to
+ITU Recommendations Q.921 and Q.931.
+.It Ar d64s
+An ISDN leased line with a single B-channel (called D64S in Germany).
+.El
+.It Li firmware
+This keyword is used like
+.Li firmware Ns = Ns Ar /path/to/file
+to download the
+firmware to active controllers supported by the
+.Em iavc
+driver (AVM B1, T1).
+This keyword is supported for all controller types,
+and causes
+.Dv I4B_CTRL_DOWNLOAD
+ioctl to be invoked with the specified file
+as an argument.
+In systems equipped with both active and passive adapters,
+and the passive cards being detected first, dummy
+.Ql controller
+entries
+are required for the passive cards to get the correct firmwares to
+correct adapters.
+.El
+.It Li entry
+This keyword starts one configuration entry.
+It must not have a parameter.
+This keyword must be used at least once.
+The following keywords are valid in an entry section:
+.Bl -tag -width unitlengthsrc
+.It Li answerprog
+This keyword is used to specify the name of a program which is run in
+case an incoming telephone connection specified
+.Em answer
+in its configuration entry.
+The default name is
+.Em answer .
+.Nm Isdnd
+expects to find this program beneath the path
+.Pa /etc/isdn
+which is prepended to the string specified as a parameter to this keyword.
+(optional)
+.It Li alert
+is used to specify a time in seconds to wait before accepting a call.
+This
+keyword is only usable for incoming telephone calls (dialin-reaction = answer).
+It is used to have a chance to accept an incoming call on the phone before
+the answering machine starts to run.
+The minimum value for the alert parameter
+is 5 seconds and the maximum parameter allowed is 180 seconds.
+(optional)
+.It Li b1protocol
+The B channel layer 1 protocol used for this connection.
+The keyword is mandatory.
+The currently configurable values are:
+.Pp
+.Bl -tag -width Ds -compact
+.It Ar hdlc
+HDLC framing.
+.It Ar raw
+No framing at all (used for telephony).
+.El
+.It Li budget-calloutperiod
+is used to specify a time period in seconds.
+Within this period, the number of calls
+specified by
+.Em budget-calloutncalls
+are allowed to succeed, any further attempt to call out will be blocked for the rest
+of the time left in the time period.
+(optional)
+.It Li budget-calloutncalls
+The number of outgoing calls allowed within the time period specified by
+.Em budget-calloutperiod .
+(optional)
+.It Li budget-calloutsfile
+A path/filename to which the number of successfull callouts are written.
+The contents of the file is preserved when it exists during startup of isdnd.
+The format of this file is: start time, last update time, number of calls.
+(optional)
+.It Li budget-calloutsfile-rotate
+If set to
+.Em on
+rotate budget-calloutsfile every night when an attempt is made to update
+the file on a new day.
+The statistics for the previous day are witten to
+a file with the filename specified by budget-calloutsfile to which a hyphen
+and the new day's (!) day of month number is appended.
+(optional)
+.It Li budget-callbackperiod
+.It Li budget-callbackncalls
+.It Li budget-callbacksfile
+.It Li budget-calloutsfile-rotate
+See
+.Em budget-calloutperiod ,
+.Em budget-calloutncalls ,
+.Em budget-calloutsfile ,
+and
+.Em budget-calloutsfile-rotate
+above.
+These are used to specify the budgets for calling back a remote site.
+.It Li callbackwait
+The time in seconds to wait between hanging up the call from a remote site
+and calling back the remote site.
+(optional)
+.It Li calledbackwait
+The time in seconds to wait for a remote site calling back the local site
+after a call from the local site to the remote site has been made.
+(optional)
+.It Li clone
+This causes the contents of the specified entry to be copied from the
+existing named entry to the current one.
+When using this feature at least a new entry specific
+.Ql name
+and
+.Ql usrdeviceunit
+value should be specified for the current entry.
+.It Li connectprog
+specifies a program run every time after a connection is established and
+address negotiation is complete (i.e.: the connection is usable).
+.Nm Isdnd
+expects to find the program below the path
+.Pa /etc/isdn
+which is prepended to the string specified as a parameter to this keyword.
+The programs specified by connect and disconnect will get the following
+command line arguments: -d (device) -f (flag) [ -a (addr) ] where
+.Em device
+is the name of device, e.g. "isp0",
+.Em flag
+will be "up" if connection just got up, or "down" if interface changed to down
+state and
+.Em addr
+the address that got assigned to the interface as a dotted-quad ip address
+(optional, only if it can be figured out by isdnd).
+(optional)
+.It Li dialin-reaction
+Used to specify what to do when an incoming connection request is received.
+The keyword is mandatory.
+The currently supported parameters are:
+.Pp
+.Bl -tag -width calledback -compact
+.It Ar accept
+Accept an incoming call.
+.It Ar reject
+Reject an incoming call.
+.It Ar ignore
+Ignore an incoming call.
+.It Ar answer
+Start telephone answering for an incoming voice call.
+.It Ar callback
+When a remote site calls, hang up and call back the remote site.
+.El
+.It Li dialout-type
+This keyword is used to configure what type of dialout mode is used.
+The keyword is mandatory.
+The currently supported parameters are:
+.Pp
+.Bl -tag -width Ds -compact
+.It Ar normal
+Normal behavior, call the remote site which is supposed to accept the call.
+.It Ar calledback
+Callback behavior, call the remote side which rejects the call and calls
+us back.
+.El
+.It Li dialrandincr
+When dialing or re-dialing and this parameter is set to
+.Em on ,
+the dial retry time is added with a random value (currently 0...3 seconds)
+to minimize the chance of two sites dialing synchronously so each gets a busy
+each time it dials because the other side is also dialing.
+.It Li dialretries
+The number of dialing retries before giving up.
+Setting this to
+.Em -1
+gives an unlimited number of retries!
+(optional)
+.It Li direction
+This keyword is used to configure if incoming and outgoing, incoming-only or
+outgoing only connections are possible.
+The keyword is optional, the default is
+.Em inout .
+.Pp
+The currently supported parameters are:
+.Pp
+.Bl -tag -width Ds -compact
+.It Ar inout
+Normal behavior, connection establishment is possible from remote and local.
+.It Ar in
+Only incoming connections are possible.
+.It Ar out
+Only outgoing connections are possible.
+.El
+.It Li disconnectprog
+specifies a program run every time after a connection was shut down.
+.Nm Isdnd
+expects to find the program below the path
+.Pa /etc/isdn
+which is prepended to the string specified as a parameter to this keyword.
+(optional)
+.It Li downtries
+is used to configure the number of unsuccessful tries (= retry cycles!) before
+the interface is disabled (for
+.Em downtime
+seconds).
+(see also the keyword
+.Em usedown
+further up).
+This keyword is optional.
+.It Li downtime
+is used to configure the time in seconds an interface is disabled
+after the configured number of
+.Em downtries .
+(see also the keyword
+.Em usedown
+further up).
+This keyword is optional and is set to 60 seconds by default.
+.It Li earlyhangup
+A (safety) time in seconds which specifies the time to hang up before an
+expected next charging unit will occur.
+(optional)
+.It Li idle-algorithm-outgoing
+The algorithm used to determine when to hang up an outgoing call when the
+line becomes idle.
+The current algorithms are:
+.Pp
+.Bl -tag -width calledback -compact
+.It Ar fix-unit-size
+idle algorithm which assumes fixed sized charging units during the whole call.
+.It Ar var-unit-size
+idle algorithm which assumes that the charging is time based after the first
+units time has expired.
+.El
+.It Li idletime-outgoing
+The time in seconds an outgoing connection must be idle before hanging up.
+An idle timeout of zero disables this functionality.
+(optional)
+.It Li idletime-incoming
+The time in seconds an incoming connection must be idle before hanging up.
+An idle timeout of zero disables this functionality.
+(optional)
+.It Li isdncontroller
+The ISDN controller number to be used for connections for this entry.
+(mandatory)
+.It Li isdnchannel
+The ISDN controller channel number to be used for connections for this entry.
+In case a channel is explicitly selected here, the SETUP message will request
+this channel but mark the request as
+.Em preferred
+(the indicated channel is preferred) instead of exclusive (only the indicated
+channel is acceptable).
+Thus the exchange is still free to select another
+than the requested channel!
+(mandatory)
+.It Li isdntxdel-incoming
+A delay value suitable for the
+.Xr timeout 9
+kernel subroutine to delay the transmission of the first packet after a
+successful connection is made by this value for
+.Em incoming
+ISDN connections.
+The specification unit is 1/100 second.
+A zero (0) disables
+this feature and is the default value.
+This feature is implemented (and makes
+sense only) for the
+.Xr i4bipr 4
+IP over raw HDLC ISDN driver.
+(optional)
+.It Li isdntxdel-outgoing
+A delay value suitable for the
+.Xr timeout 9
+kernel subroutine to delay the transmission of the first packet after a
+successful connection is made by this value for
+.Em outgoing
+ISDN connections.
+The specification unit is 1/100 second.
+A zero (0) disables
+this feature and is the default value.
+This feature is implemented (and makes
+sense only) for the
+.Xr i4bipr 4
+IP over raw HDLC ISDN driver.
+(optional)
+.It Li local-phone-dialout
+The local telephone number used when the local site dials out.
+When dialing
+out to a remote site, the number specified here is put into the
+.Em "Calling Party Number Information Element" .
+.Pp
+This keyword is mandatory for the
+.Em ipr
+user-land interfaces.
+.It Li local-subaddr-dialout
+The local subaddress used when the local site dials out.
+When dialing
+out to a remote site, the subaddress specified here is put into the
+.Em "Calling Party Subaddress Information Element" .
+.Pp
+This keyword is mandatory for the
+.Em ipr
+user-land interfaces.
+.It Li local-phone-incoming
+The local telephone number used for verifying the destination of incoming
+calls.
+When a remote site dials in, this number is used to verify that it
+is the local site which the remote site wants to connect to.
+It is compared
+with the
+.Em "Called Party Number Information Element"
+got from the telephone exchange.
+.Pp
+This keyword is mandatory for the
+.Em ipr
+interfaces.
+.It Li local-subaddr-incoming
+The local subaddress used for verifying the destination of incoming
+calls.
+When a remote site dials in, this subaddress is used to verify that it
+is the local site which the remote site wants to connect to.
+It is compared
+with the
+.Em "Called Party Subaddress Information Element"
+got from the telephone exchange.
+.Pp
+This keyword is mandatory for the
+.Em ipr
+interfaces.
+.It Li name
+Defines a symbolic name for this configuration entry.
+Its purpose is to
+use this name in the full-screen display for easy identification of a link
+to a remote site and for accounting purposes.
+(mandatory)
+.It Li maxconnecttime
+Specify a maximum connection time in seconds.
+Use this to define an absolute
+upper limit for a connection on the B-channel to last.
+.Em CAUTION:
+This feature is used to limit the connection time, _not_ number of attempts
+to establish a connection: when using this please take care to also enable
+the use of budgets to limit the connection establish attempts (otherwise
+the line will cycle thru an endless loop of connections and reconnections
+which will have an undesired effect on your telco bill)!
+.It Li ppp-auth-paranoid
+If set to
+.Em no ,
+the remote site is not required to prove its authentity for connections
+that are initiated by the local site.
+The default is
+.Em yes
+and requires the remote site to always authenticate.
+.Pp
+This keyword is only used if
+.Em ppp-send-auth
+has been set to pap or chap for an
+.Em isp
+PPP interface.
+(optional)
+.It Li ppp-auth-rechallenge
+Set to
+.Em no ,
+if the other side does not support re-challenging for chap.
+The default is
+.Em yes ,
+which causes verification of the remote site's authentity once in a while.
+.Pp
+This keyword is only used if
+.Em ppp-expect-auth
+has been set to chap for an
+.Em isp
+PPP interface.
+(optional)
+.It Li ppp-expect-auth
+The local site expects the authentity of the remote site to be proved by
+the specified method.
+The supported methods are:
+.Pp
+.Bl -tag -width Ds -compact
+.It Ar none
+Do not require the other side to authenticate.
+Typical uses are dial-out to an ISP
+(many ISPs do not authenticate themselves to clients)
+or offering anonymous dial-in at the local site.
+.It Ar chap
+The preferred authentication method, which does not require a password to be sent
+in the clear.
+.It Ar pap
+The unprotected authentication method, which allows anybody watching the wire
+to grab name and password.
+.El
+.Pp
+If
+.Em ppp-auth-paranoid
+is set to
+.Em no
+(the default is
+.Em yes )
+outgoing connections will not require the remote site to authenticate itself.
+.Pp
+This keyword is only used for the
+.Em isp
+PPP interfaces.
+(optional)
+.It Li ppp-expect-name
+The name that has to be provided by the remote site to prove its authentity.
+.Pp
+This keyword is only used if
+.Em ppp-expect-auth
+has been set to pap or chap for an
+.Em isp
+PPP interface.
+(optional)
+.It Li ppp-expect-password
+The secret that has to be provided by the remote site to prove its authentity.
+.Pp
+This keyword is only used if
+.Em ppp-expect-auth
+has been set to pap or chap for an
+.Em isp
+PPP interface.
+(optional)
+.It Li ppp-send-auth
+The authentication method required by the remote site.
+The currently supported parameters are:
+.Pp
+.Bl -tag -width Ds -compact
+.It Ar none
+The remote site does not expect or support authentication.
+.It Ar chap
+The preferred authentication method, which does not require a password to be sent
+in the clear.
+.It Ar pap
+The unprotected authentication method, which allows anybody watching the wire
+to grab name and password.
+.El
+.Pp
+This keyword is only used for the
+.Em isp
+PPP interfaces.
+(optional)
+.It Li ppp-send-name
+The authentication name sent to the remote site.
+.Pp
+This keyword is only used if
+.Em ppp-send-auth
+has been set to pap or chap for an
+.Em isp
+PPP interface.
+(optional)
+.It Li ppp-send-password
+The secret used to prove the local site's authentity to the remote site.
+.Pp
+This keyword is only used if
+.Em ppp-send-auth
+has been set to pap or chap for an
+.Em isp
+PPP interface.
+(optional)
+.It Li ratetype
+The rate entry used from the rates file.
+(optional)
+.Pp
+For example, ratetype=0 selects lines beginning "ra0" in /etc/isdn/isdnd.rates;
+(typically ra0 lines are a set of tables for local call rates on different
+days of the week & times per day).
+.It Li recoverytime
+The time in seconds to wait between dial retries.
+(optional)
+.It Li remdial-handling
+is used to specify the dialout behavior in case more than one outgoing
+number is specified.
+The currently supported parameters are:
+.Pp
+.Bl -tag -width Ds -compact
+.It Ar first
+For every new (non-retry) call setup, start with the first number.
+.It Ar last
+For every new (non-retry) call setup, start with the last number with
+which a successful connection was made.
+.It Ar next
+For every new (non-retry) call setup, start with the next number which
+follows the last one used.
+.El
+.It Li remote-phone-dialout
+The remote telephone number used when the local site dials out.
+When dialing
+out to a remote site, the number specified here is put into the
+.Em "Called Party Number Information Element" .
+.Pp
+This keyword is mandatory for the
+.Em ipr
+interfaces.
+It may be specified more than once to try to dial to several
+numbers until one succeeds.
+.It Li remote-subaddr-dialout
+The remote subaddress used when the local site dials out.
+When dialing
+out to a remote site, the subaddress specified here is put into the
+.Em "Called Party Subaddress Information Element" .
+.Pp
+This keyword is mandatory for the
+.Em ipr
+interfaces.
+It may be specified more than once to linked it to the
+remote-phone-dialout numbers until one succeeds.
+.It Li remote-phone-incoming
+The remote telephone number used to verify an incoming call.
+When a remote site
+dials in, this number is used to verify that it is the correct remote site
+which is herewith authorized to connect into the local system.
+This parameter
+is compared against the
+.Em "Calling Party Number Information Element"
+got from the telephone exchange.
+.Pp
+This keyword is mandatory for the ipr interfaces.
+.Pp
+This keyword may have a wildcard parameter '*' to permit anyone dialing in.
+.It Li remote-subaddr-incoming
+The remote subaddress used to verify an incoming call.
+When a remote site
+dials in, this subaddress is used to verify that it is the correct remote site
+which is herewith authorized to connect into the local system.
+This parameter
+is compared against the
+.Em "Calling Party Subaddress Information Element"
+got from the telephone exchange.
+.Pp
+This keyword is mandatory for the ipr interfaces.
+.Pp
+This keyword may have a wildcard parameter '*' to permit anyone dialing in.
+.It Li unitlength
+The length of a charging unit in seconds.
+This is used in conjunction with
+the idletime to decide when to hang up a connection.
+(optional)
+.It Li unitlengthsrc
+This keyword is used to specify from which source
+.Xr isdnd 8
+takes the unitlength for short-hold mode.
+The currently configurable values are:
+.Pp
+.Bl -tag -width Ds -compact
+.It Ar none
+Then unitlength is not specified anywhere.
+.It Ar cmdl
+Use the unitlength specified on the command line.
+.It Ar conf
+Use the unitlength specified in the configuration file with the keyword
+.Em unitlength .
+.It Ar rate
+Use the unitlength from the ratesfile specified in the configuration
+file with the keyword
+.Em ratetype .
+.It Ar aocd
+Use a dynamically calculated unitlength in case AOCD is subscribed on
+the ISDN line.
+(AOCD is an acronym for ``Advice Of Charge During the call''
+which is a service provided by the telecommunications (ie phone) provider,
+to indicate billable units).
+.El
+.It Li usrdevicename
+Specifies the user-land interface which is used for interfacing ISDN B channel
+data to the user-land.
+The keyword is mandatory.
+This keyword accepts the following parameters:
+.Pp
+.Bl -tag -width Ds -compact
+.It Ar ipr
+This parameter configures a raw HDLC IP over ISDN interface.
+.It Ar isp
+This parameter configures a synchronous PPP over ISDN interface.
+.It Ar rbch
+This specifies a Raw B CHannel access interface.
+.It Ar tel
+ISDN telephony.
+.It Ar ing
+configures a ISDN B-channel to NetGraph interface.
+.El
+.It Li usrdeviceunit
+Specifies the unit number for the device which is specified with
+usrdevicename.
+.It Li usedown
+is used to enable the use of the keywords
+.Em downtries
+and
+.Em downtime
+in the entries section(s).
+It is used in the
+.Nm isdnd
+daemon to dynamically enable and disable the IP interfaces to avoid excessive
+dialing activities in case of transient failures (such as busy lines).
+This parameter is optional and is set to
+.Em off
+by default.
+.It Li usesubaddr
+is used to enable the use of subaddresses.
+This parameter is optional and is set to
+.Em off
+by default.
+.It Li valid
+.Em Note :
+this feature is considered experimental!
+The parameter to this keyword is a string specifying a time range within
+which this entry is valid.
+The time specification consists of a list of weekdays and/or a holiday
+indicator ( see also the
+.Em holidayfile
+keyword in the system section ) separated by commas followed by an optional
+daytime range specification in the form hh:mm-hh:mm.
+The weekdays are specified as numbers from 0 to 6 and the number 7 for
+holidays:
+.Pp
+.Bl -tag -width Ds -compact
+.It Ar 0
+Sunday
+.It Ar 1
+Monday
+.It Ar 2
+Tuesday
+.It Ar 3
+Wednesday
+.It Ar 4
+Thursday
+.It Ar 5
+Friday
+.It Ar 6
+Saturday
+.It Ar 7
+a Holiday
+.El
+.Pp
+The following examples describe the "T-ISDN xxl" tariff of the german Telekom:
+.Bl -tag -width Ds -compact
+.It Ar 1,2,3,4,5,6,09:00-18:00
+Monday through Saturday, daytime 9:00 to 18:00
+.It Ar 1,2,3,4,5,6,18:00-9:00
+Monday through Saturday, nighttime 18:00 to 9:00
+.It Ar 0,7
+Sunday and on holidays, all 24 hours
+.El
+.Pp
+The use of this keyword is optional.
+.El
+.El
+.Sh IDLETIME CALCULATION AND SHORT-HOLD MODE
+.Bl -tag -width "incoming calls
+.It Li incoming calls
+It is assumed that the calling side knows most about charging structures and
+such and as a consequence only the keyword
+.Em idletime-incoming
+has a function for incoming calls.
+.Pp
+For incoming calls the line is constantly monitored, and in case there was
+not traffic taking place for the time in seconds specified by
+.Em idletime-incoming
+the call is closed.
+.Pp
+Typically,
+.Em idletime-incoming
+is used as a last resort and is therefore set much higher than a charging
+unit time: typical values are one to five minutes.
+.It Li outgoing calls
+Outgoing call disconnect time can be setup in one of three ways:
+.Bl -tag -width "shorthold mode
+.It Li simple mode
+For simple mode, the
+.Em idle-algorithm-outgoing
+must be
+.Em fix-unit-size
+and the selected
+.Em unitlength
+must be 0 (zero) and
+.Em idletime-outgoing
+greater zero.
+.Pp
+The outgoing traffic is constantly monitored, and in case there was
+not traffic taking place for the time in seconds specified by
+.Em idletime-outgoing
+the call is closed.
+.Pp
+Typical values in simple mode are 10 to 30 seconds.
+.It Li shorthold mode for fixed unit charging
+For shorthold mode, the
+.Em idle-algorithm-outgoing
+must be
+.Em fix-unit-size
+and the selected
+.Em unitlength
+and
+.Em idletime-outgoing
+must be greater than 0 (zero);
+.Em earlyhangup
+must be >= 0 (zero).
+.Bd -literal
+|<unchecked-window>|<checkwindow>|<safetywindow>|
+| | | |
++------------------+-------------+--------------+
+| | | |
+| |<-idle-time->|<earlyhangup->|
+|<--------------unitlength--------------------->|
+.Ed
+.Pp
+During the unchecked window which is (unitlength - (idle-time+earlyhangup))
+in length, no idle check is done.
+After the unchecked window has ended,
+the line is checked for idle-time length if no traffic takes place.
+In case
+there was traffic detected in the check-window, the same procedure is restarted
+at the beginning of the next unit.
+In case no traffic was detected during
+the check-window, the line is closed at the end of the check window.
+.Pp
+Notice:
+.Em unitlength
+must (!) be greater than the sum of
+.Em idletime-outgoing
+and
+.Em earlyhangup !
+.It Li shorthold mode for variable unit charging
+For shorthold mode, the
+.Em idle-algorithm-outgoing
+must be
+.Em var-unit-size
+and the selected
+.Em unitlength
+and
+.Em idletime-outgoing
+must be greater than 0 (zero);
+.Pp
+This shorthold mode is suitable when your calls are billed on
+the elapse time of the call plus a fixed connection charge.
+For example British Telecom bill this way.
+.Pp
+Each call is divided into two periods, the first is the
+.Em unchecked
+period and the second is the
+.Em checked .
+The
+.Em checked
+period starts 1 second before the first units time expires.
+.Pp
+During the
+.Em checked
+period if there is no traffic for
+.Em idle-time
+seconds the call is disconnected.
+.Pp
+.Bd -literal
+|<---unchecked------------------>|<------checked------>
++------------------+-------------+
+| |<-idle-time->|
+|<--------------unitlength------->|
+.Ed
+.Pp
+Experience shows that useful values for idle-time are from 15 to 30 seconds.
+.Pp
+If idle-time is too short an application that is not yet finished with the
+network will cause a new call to be placed.
+.Pp
+.El
+.El
+.Sh FILES
+.Bl -tag -width /etc/isdn/isdnd.rc -compact
+.It Pa /etc/isdn/isdnd.rc
+The default configuration file for the
+.Nm isdnd
+ISDN daemon.
+.El
+.Sh SEE ALSO
+.Xr regex 3 ,
+.Xr re_format 7 ,
+.Xr isdnd 8 ,
+.Xr isdnmonitor 8
+.Sh AUTHORS
+.An -nosplit
+The
+.Xr isdnd 8
+daemon and this manual page were written by
+.An Hellmuth Michaelis Aq hm@FreeBSD.org .
+.Pp
+Additions to this manual page by
+.An Barry Scott Aq barry@scottb.demon.co.uk .
diff --git a/usr.sbin/i4b/isdnd/log.c b/usr.sbin/i4b/isdnd/log.c
new file mode 100644
index 0000000..7876f13
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/log.c
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 1997, 2001 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * i4b daemon - logging routines
+ * -----------------------------
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Wed Dec 26 12:49:45 2001]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "isdnd.h"
+
+#define LOGBUFLEN 256
+
+extern int do_monitor;
+extern int accepted;
+extern FILE *logfp;
+
+static void check_reg(char *logstring);
+
+struct logtab {
+ char *text;
+ int pri;
+};
+
+/*---------------------------------------------------------------------------*
+ * table for converting internal log levels into syslog levels
+ *---------------------------------------------------------------------------*/
+static struct logtab logtab[] = {
+ {"ERR", LOG_ERR}, /* error conditions */
+ {"WRN", LOG_WARNING}, /* warning conditions, nonfatal */
+ {"DMN", LOG_NOTICE}, /* significant conditions of the daemon */
+ {"CHD", LOG_INFO}, /* informational, call handling */
+ {"DBG", LOG_DEBUG}, /* debug messages */
+ {"MER", LOG_ERR}, /* monitor error conditions */
+ {"PKT", LOG_INFO} /* packet logging */
+};
+
+/*---------------------------------------------------------------------------*
+ * initialize logging
+ *---------------------------------------------------------------------------*/
+void
+init_log(void)
+{
+ int i;
+
+ if(uselogfile)
+ {
+ if((logfp = fopen(logfile, "a")) == NULL)
+ {
+ fprintf(stderr, "ERROR, cannot open logfile %s: %s\n",
+ logfile, strerror(errno));
+ exit(1);
+ }
+
+ /* set unbuffered operation */
+
+ setvbuf(logfp, (char *)NULL, _IONBF, 0);
+ }
+ else
+ {
+#if DEBUG
+ if(do_debug && do_fork == 0 && do_fullscreen == 0)
+ (void)openlog("isdnd",
+ LOG_PID|LOG_CONS|LOG_NDELAY|LOG_PERROR,
+ logfacility);
+ else
+#endif
+ (void)openlog("isdnd", LOG_PID|LOG_CONS|LOG_NDELAY,
+ logfacility);
+ }
+
+ /* initialize the regexp array */
+
+ for(i = 0; i < MAX_RE; i++)
+ {
+ char *p;
+ char buf[64];
+
+ snprintf(buf, sizeof(buf), "%s%d", REGPROG_DEF, i);
+
+ rarr[i].re_flg = 0;
+
+ if((p = malloc(strlen(buf) + 1)) == NULL)
+ {
+ log(LL_DBG, "init_log: malloc failed: %s", strerror(errno));
+ do_exit(1);
+ }
+
+ strcpy(p, buf);
+
+ rarr[i].re_prog = p;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * finish logging
+ *---------------------------------------------------------------------------*/
+void
+finish_log(void)
+{
+ if(uselogfile && logfp)
+ {
+ fflush(logfp);
+ fclose(logfp);
+ }
+ else
+ {
+ (void)closelog();
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * place entry into logfile
+ *---------------------------------------------------------------------------*/
+void
+log(int what, const char *fmt, ...)
+{
+ char buffer[LOGBUFLEN];
+ register char *dp;
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(buffer, LOGBUFLEN-1, fmt, ap);
+ va_end(ap);
+
+ dp = getlogdatetime(); /* get time string ptr */
+
+#ifdef USE_CURSES
+
+ /* put log on screen ? */
+
+ if((do_fullscreen && curses_ready) &&
+ ((!debug_noscreen) || (debug_noscreen && (what != LL_DBG))))
+ {
+ wprintw(lower_w, "%s %s %-.*s\n", dp, logtab[what].text,
+
+/*
+ * FreeBSD-current integrated ncurses. Since then it is no longer possible
+ * to write to the last column in the logfilewindow without causing an
+ * automatic newline to occur resulting in a blank line in that window.
+ */
+#ifdef __FreeBSD__
+#include <osreldate.h>
+#endif
+#if defined(__FreeBSD_version) && __FreeBSD_version >= 400009
+#warning "FreeBSD ncurses is buggy: write to last column = auto newline!"
+ COLS-((strlen(dp))+(strlen(logtab[what].text))+3), buffer);
+#else
+ (int)(COLS-((strlen(dp))+(strlen(logtab[what].text))+2)), buffer);
+#endif
+ wrefresh(lower_w);
+ }
+#endif
+
+#ifdef I4B_EXTERNAL_MONITOR
+ if(what != LL_MER) /* don't send monitor errs, endless loop !!! */
+ monitor_evnt_log(logtab[what].pri, logtab[what].text, buffer);
+#endif
+
+ if(uselogfile)
+ {
+ fprintf(logfp, "%s %s %s\n", dp, logtab[what].text, buffer);
+ }
+ else
+ {
+ register char *s = buffer;
+
+ /* strip leading spaces from syslog output */
+
+ while(*s && (*s == ' '))
+ s++;
+
+ syslog(logtab[what].pri, "%s %s", logtab[what].text, s);
+ }
+
+
+#if DEBUG
+ if(what != LL_DBG) /* don't check debug logs, endless loop !!! */
+#endif
+ check_reg(buffer);
+}
+
+/*---------------------------------------------------------------------------*
+ * return ptr to static area containing date/time
+ *---------------------------------------------------------------------------*/
+char *
+getlogdatetime()
+{
+ static char logdatetime[41];
+ time_t tim;
+ register struct tm *tp;
+
+ tim = time(NULL);
+ tp = localtime(&tim);
+ strftime(logdatetime,40,I4B_TIME_FORMAT,tp);
+ return(logdatetime);
+}
+
+/*---------------------------------------------------------------------------*
+ * check for a match in the regexp array
+ *---------------------------------------------------------------------------*/
+static void
+check_reg(char *logstring)
+{
+ register int i;
+
+ for(i = 0; i < MAX_RE; i++)
+ {
+ if(rarr[i].re_flg && (!regexec(&(rarr[i].re), logstring, (size_t) 0, NULL, 0)))
+ {
+ char* argv[3];
+ argv[0] = rarr[i].re_prog;
+ argv[1] = logstring;
+ argv[2] = NULL;
+
+ exec_prog(rarr[i].re_prog, argv);
+ break;
+ }
+ }
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnd/main.c b/usr.sbin/i4b/isdnd/main.c
new file mode 100644
index 0000000..74e1e63
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/main.c
@@ -0,0 +1,840 @@
+/*
+ * Copyright (c) 1997, 2001 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * i4b daemon - main program entry
+ * -------------------------------
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Wed Dec 26 12:51:00 2001]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <locale.h>
+#include <paths.h>
+
+#ifdef I4B_EXTERNAL_MONITOR
+#include "monitor.h"
+#endif
+
+#define MAIN
+#include "isdnd.h"
+#undef MAIN
+
+#ifdef I4B_EXTERNAL_MONITOR
+
+#ifdef I4B_NOTCPIP_MONITOR
+/* monitor via local socket */
+static void mloop(int sockfd);
+#else /* I4B_NOTCPIP_MONITOR */
+/* monitor via local and tcp/ip socket */
+static void mloop(int localsock, int remotesock);
+#endif /* I4B_NOTCPIP_MONITOR */
+
+#else /* I4B_EXTERNAL_MONITOR */
+/* no monitoring at all */
+static void mloop();
+#endif /* I4B_EXTERNAL_MONITOR */
+
+#ifdef USE_CURSES
+static void kbdrdhdl(void);
+#endif
+
+static void isdnrdhdl(void);
+static void usage(void);
+
+#define MSG_BUF_SIZ 1024 /* message buffer size */
+
+/*---------------------------------------------------------------------------*
+ * usage display and exit
+ *---------------------------------------------------------------------------*/
+static void
+usage(void)
+{
+ fprintf(stderr, "\n");
+ fprintf(stderr, "isdnd - i4b ISDN manager daemon, version %02d.%02d.%d, %s %s\n", VERSION, REL, STEP, __DATE__, __TIME__);
+#ifdef DEBUG
+ fprintf(stderr, " usage: isdnd [-c file] [-d level] [-F] [-f [-r dev] [-t termtype]]\n");
+#else
+ fprintf(stderr, " usage: isdnd [-c file] [-F] [-f [-r dev] [-t termtype]]\n");
+#endif
+ fprintf(stderr, " [-l] [-L file] [-m] [-s facility] [-u time]\n");
+ fprintf(stderr, " -c <filename> configuration file name (def: %s)\n", CONFIG_FILE_DEF);
+#ifdef DEBUG
+ fprintf(stderr, " -d <level> set debug flag bits:\n");
+ fprintf(stderr, " general = 0x%04x, rates = 0x%04x, timing = 0x%04x\n", DL_MSG, DL_RATES, DL_TIME);
+ fprintf(stderr, " state = 0x%04x, retry = 0x%04x, dial = 0x%04x\n", DL_STATE, DL_RCVRY, DL_DIAL);
+ fprintf(stderr, " process = 0x%04x, kernio = 0x%04x, ctrlstat = 0x%04x\n", DL_PROC, DL_DRVR, DL_CNST);
+ fprintf(stderr, " rc-file = 0x%04x, budget = 0x%04x, valid = 0x%04x\n", DL_RCCF, DL_BDGT, DL_VALID);
+ fprintf(stderr, " -dn no debug output on fullscreen display\n");
+#endif
+ fprintf(stderr, " -f fullscreen status display\n");
+ fprintf(stderr, " -F do not become a daemon process\n");
+ fprintf(stderr, " -l use a logfile instead of syslog\n");
+ fprintf(stderr, " -L <file> use file instead of %s for logging\n", LOG_FILE_DEF);
+ fprintf(stderr, " -P pretty print real config to stdout and exit\n");
+ fprintf(stderr, " -r <device> redirect output to other device (for -f)\n");
+ fprintf(stderr, " -s <facility> use facility instead of %d for syslog logging\n", LOG_LOCAL0 >> 3);
+ fprintf(stderr, " -t <termtype> terminal type of redirected screen (for -f)\n");
+ fprintf(stderr, " -u <time> length of a charging unit in seconds\n");
+#ifdef I4B_EXTERNAL_MONITOR
+ fprintf(stderr, " -m inhibit network/local monitoring (protocol %02d.%02d)\n", MPROT_VERSION, MPROT_REL);
+#endif
+ fprintf(stderr, "\n");
+ exit(1);
+}
+
+/*---------------------------------------------------------------------------*
+ * program entry
+ *---------------------------------------------------------------------------*/
+int
+main(int argc, char **argv)
+{
+ int i;
+ msg_vr_req_t mvr;
+
+#ifdef I4B_EXTERNAL_MONITOR
+ int sockfd = -1; /* local monitor socket */
+#ifndef I4B_NOTCPIP_MONITOR
+ int remotesockfd = -1; /* tcp/ip monitor socket */
+#endif
+#endif
+
+ setlocale (LC_ALL, "");
+
+ while ((i = getopt(argc, argv, "mc:d:fFlL:Pr:s:t:u:")) != -1)
+ {
+ switch (i)
+ {
+#ifdef I4B_EXTERNAL_MONITOR
+ case 'm':
+ inhibit_monitor = 1;
+ break;
+#endif
+
+ case 'c':
+ configfile = optarg;
+ break;
+
+#ifdef DEBUG
+ case 'd':
+ if(*optarg == 'n')
+ debug_noscreen = 1;
+ else if((sscanf(optarg, "%i", &debug_flags)) == 1)
+ do_debug = 1;
+ else
+ usage();
+ break;
+#endif
+
+ case 'f':
+ do_fullscreen = 1;
+ do_fork = 0;
+#ifndef USE_CURSES
+ fprintf(stderr, "Sorry, no fullscreen mode available - daemon compiled without USE_CURSES\n");
+ exit(1);
+#endif
+ break;
+
+ case 'F':
+ do_fork = 0;
+ break;
+
+ case 'l':
+ uselogfile = 1;
+ break;
+
+ case 'L':
+ strlcpy(logfile, optarg, sizeof(logfile));
+ break;
+
+ case 'P':
+ do_print = 1;
+ break;
+
+ case 'r':
+ rdev = optarg;
+ do_rdev = 1;
+ break;
+
+ case 's':
+ if(isdigit(*optarg))
+ {
+ int facility;
+ logfacility = strtoul(optarg, NULL, 10);
+ facility = logfacility << 3;
+
+ if((facility < LOG_KERN) ||
+ (facility > LOG_FTP && facility < LOG_LOCAL0) ||
+ (facility > LOG_LOCAL7))
+ {
+ fprintf(stderr, "Error, option -s has invalid logging facility %d", logfacility);
+ usage();
+ }
+ logfacility = facility;
+ }
+ else
+ {
+ fprintf(stderr, "Error: option -s requires a numeric argument!\n");
+ usage();
+ }
+ break;
+
+ case 't':
+ ttype = optarg;
+ do_ttytype = 1;
+ break;
+
+ case 'u':
+ if(isdigit(*optarg))
+ {
+ unit_length = strtoul(optarg, NULL, 10);
+ if(unit_length < ULSRC_CMDLMIN)
+ unit_length = ULSRC_CMDLMIN;
+ else if(unit_length > ULSRC_CMDLMAX)
+ unit_length = ULSRC_CMDLMAX;
+ got_unitlen = 1;
+ }
+ else
+ {
+ fprintf(stderr, "Error: option -T requires a numeric argument!\n");
+ usage();
+ }
+ break;
+
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+#ifdef DEBUG
+ if(!do_debug)
+ debug_noscreen = 0;
+#endif
+
+ if(!do_print)
+ {
+ umask(UMASK); /* set our umask ... */
+
+ init_log(); /* initialize the logging subsystem */
+ }
+
+ check_pid(); /* check if we are already running */
+
+ if(!do_print)
+ {
+ if(do_fork || (do_fullscreen && do_rdev)) /* daemon mode ? */
+ daemonize();
+
+ write_pid(); /* write our pid to file */
+
+ /* set signal handler(s) */
+
+ signal(SIGCHLD, sigchild_handler); /* process handling */
+ signal(SIGHUP, rereadconfig); /* reread configuration */
+ signal(SIGUSR1, reopenfiles); /* reopen acct/log files*/
+ signal(SIGPIPE, SIG_IGN); /* handled manually */
+ signal(SIGINT, do_exit); /* clean up on SIGINT */
+ signal(SIGTERM, do_exit); /* clean up on SIGTERM */
+ signal(SIGQUIT, do_exit); /* clean up on SIGQUIT */
+ }
+
+ /* open isdn device */
+
+ if((isdnfd = open(I4BDEVICE, O_RDWR)) < 0)
+ {
+ log(LL_ERR, "main: cannot open %s: %s", I4BDEVICE, strerror(errno));
+ exit(1);
+ }
+
+ /* check kernel and userland have same version/release numbers */
+
+ if((ioctl(isdnfd, I4B_VR_REQ, &mvr)) < 0)
+ {
+ log(LL_ERR, "main: ioctl I4B_VR_REQ failed: %s", strerror(errno));
+ do_exit(1);
+ }
+
+ if(mvr.version != VERSION)
+ {
+ log(LL_ERR, "main: version mismatch, kernel %d, daemon %d", mvr.version, VERSION);
+ do_exit(1);
+ }
+
+ if(mvr.release != REL)
+ {
+ log(LL_ERR, "main: release mismatch, kernel %d, daemon %d", mvr.release, REL);
+ do_exit(1);
+ }
+
+ if(mvr.step != STEP)
+ {
+ log(LL_ERR, "main: step mismatch, kernel %d, daemon %d", mvr.step, STEP);
+ do_exit(1);
+ }
+
+ /* init controller state array */
+
+ init_controller();
+
+ /* read runtime configuration file and configure ourselves */
+
+ configure(configfile, 0);
+
+ if(config_error_flag)
+ {
+ log(LL_ERR, "there were %d error(s) in the configuration file, terminating!", config_error_flag);
+ exit(1);
+ }
+
+ /* set controller ISDN protocol */
+
+ init_controller_protocol();
+
+ /* init active controllers, if any */
+
+ signal(SIGCHLD, SIG_IGN); /*XXX*/
+
+ init_active_controller();
+
+ signal(SIGCHLD, sigchild_handler); /*XXX*/
+
+ /* handle the rates stuff */
+
+ if((i = readrates(ratesfile)) == ERROR)
+ {
+ if(rate_error != NULL)
+ log(LL_ERR, "%s", rate_error);
+ exit(1);
+ }
+
+ if(i == GOOD)
+ {
+ got_rate = 1; /* flag, ratesfile read and ok */
+ DBGL(DL_RCCF, (log(LL_DBG, "ratesfile %s read successfully", ratesfile)));
+ }
+ else
+ {
+ if(rate_error != NULL)
+ log(LL_WRN, "%s", rate_error);
+ }
+
+ /* if writing accounting info, open file, set unbuffered */
+
+ if(useacctfile)
+ {
+ if((acctfp = fopen(acctfile, "a")) == NULL)
+ {
+ log(LL_ERR, "ERROR, can't open acctfile %s for writing, terminating!", acctfile);
+ exit(1);
+ }
+ setvbuf(acctfp, (char *)NULL, _IONBF, 0);
+ }
+
+ /* initialize alias processing */
+
+ if(aliasing)
+ init_alias(aliasfile);
+
+ /* init holidays */
+
+ init_holidays(holidayfile);
+
+ /* init remote monitoring */
+
+#ifdef I4B_EXTERNAL_MONITOR
+ if(do_monitor)
+ {
+ monitor_init();
+ sockfd = monitor_create_local_socket();
+#ifndef I4B_NOTCPIP_MONITOR
+ remotesockfd = monitor_create_remote_socket(monitorport);
+#endif
+ }
+#endif
+
+ /* in case fullscreendisplay, initialize */
+
+#ifdef USE_CURSES
+ if(do_fullscreen)
+ {
+ init_screen();
+ }
+#endif
+
+ /* init realtime priority */
+
+#ifdef USE_RTPRIO
+ if(rt_prio != RTPRIO_NOTUSED)
+ {
+ struct rtprio rtp;
+
+ rtp.type = RTP_PRIO_REALTIME;
+ rtp.prio = rt_prio;
+
+ if((rtprio(RTP_SET, getpid(), &rtp)) == -1)
+ {
+ log(LL_ERR, "rtprio failed: %s", strerror(errno));
+ do_exit(1);
+ }
+ }
+#endif
+
+ starttime = time(NULL); /* get starttime */
+
+ srandom(580403); /* init random number gen */
+
+ mloop( /* enter loop of no return .. */
+#ifdef I4B_EXTERNAL_MONITOR
+ sockfd
+#ifndef I4B_NOTCPIP_MONITOR
+ , remotesockfd
+#endif
+#endif
+ );
+ do_exit(0);
+ return(0);
+}
+
+/*---------------------------------------------------------------------------*
+ * program exit
+ *---------------------------------------------------------------------------*/
+void
+do_exit(int exitval)
+{
+ close_allactive();
+
+ unlink(PIDFILE);
+
+ log(LL_DMN, "daemon terminating, exitval = %d", exitval);
+
+#ifdef USE_CURSES
+ if(do_fullscreen)
+ endwin();
+#endif
+
+#ifdef I4B_EXTERNAL_MONITOR
+ monitor_exit();
+#endif
+
+ exit(exitval);
+}
+
+/*---------------------------------------------------------------------------*
+ * program exit
+ *---------------------------------------------------------------------------*/
+void
+error_exit(int exitval, const char *fmt, ...)
+{
+ close_allactive();
+
+ unlink(PIDFILE);
+
+ log(LL_DMN, "fatal error, daemon terminating, exitval = %d", exitval);
+
+#ifdef USE_CURSES
+ if(do_fullscreen)
+ endwin();
+#endif
+
+#ifdef I4B_EXTERNAL_MONITOR
+ monitor_exit();
+#endif
+
+ if(mailto[0] && mailer[0])
+ {
+
+#define EXITBL 2048
+
+ char ebuffer[EXITBL];
+ char sbuffer[EXITBL];
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(ebuffer, EXITBL-1, fmt, ap);
+ va_end(ap);
+
+ signal(SIGCHLD, SIG_IGN); /* remove handler */
+
+ snprintf(sbuffer, sizeof(sbuffer), "%s%s%s%s%s%s%s%s",
+ "cat << ENDOFDATA | ",
+ mailer,
+ " -s \"i4b isdnd: fatal error, terminating\" ",
+ mailto,
+ "\nThe isdnd terminated because of a fatal error:\n\n",
+ ebuffer,
+ "\n\nYours sincerely,\n the isdnd\n",
+ "\nENDOFDATA\n");
+ system(sbuffer);
+ }
+
+ exit(exitval);
+}
+
+/*---------------------------------------------------------------------------*
+ * main loop
+ *---------------------------------------------------------------------------*/
+static void
+mloop(
+#ifdef I4B_EXTERNAL_MONITOR
+ int localmonitor
+#ifndef I4B_NOTCPIP_MONITOR
+ , int remotemonitor
+#endif
+#endif
+)
+{
+ fd_set set;
+ struct timeval timeout;
+ int ret;
+ int high_selfd;
+
+ /* go into loop */
+
+ log(LL_DMN, "i4b isdn daemon started (pid = %d)", getpid());
+
+ for(;;)
+ {
+ FD_ZERO(&set);
+
+#ifdef USE_CURSES
+ if(do_fullscreen)
+ FD_SET(fileno(stdin), &set);
+#endif
+
+ FD_SET(isdnfd, &set);
+
+ high_selfd = isdnfd;
+
+#ifdef I4B_EXTERNAL_MONITOR
+ if(do_monitor)
+ {
+ if (localmonitor != -1) {
+ /* always watch for new connections */
+ FD_SET(localmonitor, &set);
+ if(localmonitor > high_selfd)
+ high_selfd = localmonitor;
+ }
+#ifndef I4B_NOTCPIP_MONITOR
+ if (remotemonitor != -1) {
+ FD_SET(remotemonitor, &set);
+ if(remotemonitor > high_selfd)
+ high_selfd = remotemonitor;
+ }
+#endif
+
+ /* if there are client connections, let monitor module
+ * enter them into the fdset */
+ if(accepted)
+ {
+ monitor_prepselect(&set, &high_selfd);
+ }
+ }
+#endif
+
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 0;
+
+ ret = select(high_selfd + 1, &set, NULL, NULL, &timeout);
+
+ if(ret > 0)
+ {
+ if(FD_ISSET(isdnfd, &set))
+ isdnrdhdl();
+
+#ifdef USE_CURSES
+ if(FD_ISSET(fileno(stdin), &set))
+ kbdrdhdl();
+#endif
+
+#ifdef I4B_EXTERNAL_MONITOR
+ if(do_monitor)
+ {
+ if(localmonitor != -1 && FD_ISSET(localmonitor, &set))
+ monitor_handle_connect(localmonitor, 1);
+
+#ifndef I4B_NOTCPIP_MONITOR
+ if(remotemonitor != -1 && FD_ISSET(remotemonitor, &set))
+ monitor_handle_connect(remotemonitor, 0);
+#endif
+ if(accepted)
+ monitor_handle_input(&set);
+ }
+#endif
+ }
+ else if(ret == -1)
+ {
+ if(errno != EINTR)
+ {
+ log(LL_ERR, "mloop: ERROR, select error on isdn device, errno = %d!", errno);
+ error_exit(1, "mloop: ERROR, select error on isdn device, errno = %d!", errno);
+ }
+ }
+
+ /* handle timeout and recovery */
+
+ handle_recovery();
+ }
+}
+
+#ifdef USE_CURSES
+/*---------------------------------------------------------------------------*
+ * data from keyboard available, read and process it
+ *---------------------------------------------------------------------------*/
+static void
+kbdrdhdl(void)
+{
+ int ch = getch();
+
+ if(ch == ERR)
+ {
+ log(LL_ERR, "kbdrdhdl: ERROR, read error on controlling tty, errno = %d!", errno);
+ error_exit(1, "kbdrdhdl: ERROR, read error on controlling tty, errno = %d!", errno);
+ }
+
+ switch(ch)
+ {
+ case 0x0c: /* control L */
+ wrefresh(curscr);
+ break;
+
+ case '\n':
+ case '\r':
+ do_menu();
+ break;
+ }
+}
+#endif
+
+/*---------------------------------------------------------------------------*
+ * data from /dev/isdn available, read and process them
+ *---------------------------------------------------------------------------*/
+static void
+isdnrdhdl(void)
+{
+ static unsigned char msg_rd_buf[MSG_BUF_SIZ];
+ msg_hdr_t *hp = (msg_hdr_t *)&msg_rd_buf[0];
+
+ register int len;
+
+ if((len = read(isdnfd, msg_rd_buf, MSG_BUF_SIZ)) > 0)
+ {
+ switch(hp->type)
+ {
+ case MSG_CONNECT_IND:
+ msg_connect_ind((msg_connect_ind_t *)msg_rd_buf);
+ break;
+
+ case MSG_CONNECT_ACTIVE_IND:
+ msg_connect_active_ind((msg_connect_active_ind_t *)msg_rd_buf);
+ break;
+
+ case MSG_DISCONNECT_IND:
+ msg_disconnect_ind((msg_disconnect_ind_t *)msg_rd_buf);
+ break;
+
+ case MSG_DIALOUT_IND:
+ msg_dialout((msg_dialout_ind_t *)msg_rd_buf);
+ break;
+
+ case MSG_ACCT_IND:
+ msg_accounting((msg_accounting_ind_t *)msg_rd_buf);
+ break;
+
+ case MSG_IDLE_TIMEOUT_IND:
+ msg_idle_timeout_ind((msg_idle_timeout_ind_t *)msg_rd_buf);
+ break;
+
+ case MSG_CHARGING_IND:
+ msg_charging_ind((msg_charging_ind_t *)msg_rd_buf);
+ break;
+
+ case MSG_PROCEEDING_IND:
+ msg_proceeding_ind((msg_proceeding_ind_t *)msg_rd_buf);
+ break;
+
+ case MSG_ALERT_IND:
+ msg_alert_ind((msg_alert_ind_t *)msg_rd_buf);
+ break;
+
+ case MSG_DRVRDISC_REQ:
+ msg_drvrdisc_req((msg_drvrdisc_req_t *)msg_rd_buf);
+ break;
+
+ case MSG_L12STAT_IND:
+ msg_l12stat_ind((msg_l12stat_ind_t *)msg_rd_buf);
+ break;
+
+ case MSG_TEIASG_IND:
+ msg_teiasg_ind((msg_teiasg_ind_t *)msg_rd_buf);
+ break;
+
+ case MSG_PDEACT_IND:
+ msg_pdeact_ind((msg_pdeact_ind_t *)msg_rd_buf);
+ break;
+
+ case MSG_NEGCOMP_IND:
+ msg_negcomplete_ind((msg_negcomplete_ind_t *)msg_rd_buf);
+ break;
+
+ case MSG_IFSTATE_CHANGED_IND:
+ msg_ifstatechg_ind((msg_ifstatechg_ind_t *)msg_rd_buf);
+ break;
+
+ case MSG_DIALOUTNUMBER_IND:
+ msg_dialoutnumber((msg_dialoutnumber_ind_t *)msg_rd_buf);
+ break;
+
+ case MSG_PACKET_IND:
+ msg_packet_ind((msg_packet_ind_t *)msg_rd_buf);
+ break;
+
+ case MSG_KEYPAD_IND:
+ msg_keypad((msg_keypad_ind_t *)msg_rd_buf);
+ break;
+
+ default:
+ log(LL_WRN, "ERROR, unknown message received from %sisdn (0x%x)", _PATH_DEV, msg_rd_buf[0]);
+ break;
+ }
+ }
+ else
+ {
+ log(LL_WRN, "ERROR, read error on isdn device, errno = %d, length = %d", errno, len);
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * re-read the config file on SIGHUP or menu command
+ *---------------------------------------------------------------------------*/
+void
+rereadconfig(int dummy)
+{
+ extern int entrycount;
+
+ log(LL_DMN, "re-reading configuration file");
+
+ close_allactive();
+
+#if I4B_EXTERNAL_MONITOR
+ monitor_clear_rights();
+#endif
+
+ entrycount = -1;
+ nentries = 0;
+
+ /* read runtime configuration file and configure ourselves */
+
+ configure(configfile, 1);
+
+ if(config_error_flag)
+ {
+ log(LL_ERR, "rereadconfig: there were %d error(s) in the configuration file, terminating!", config_error_flag);
+ error_exit(1, "rereadconfig: there were %d error(s) in the configuration file, terminating!", config_error_flag);
+ }
+
+ if(aliasing)
+ {
+ /* reread alias database */
+ free_aliases();
+ init_alias(aliasfile);
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * re-open the log/acct files on SIGUSR1
+ *---------------------------------------------------------------------------*/
+void
+reopenfiles(int dummy)
+{
+ if(useacctfile)
+ {
+ /* close file */
+
+ if(acctfp)
+ {
+ fflush(acctfp);
+ fclose(acctfp);
+ }
+
+ /* if user specified a suffix, rename the old file */
+
+ if(rotatesuffix[0] != '\0')
+ {
+ char filename[MAXPATHLEN];
+
+ snprintf(filename, sizeof(filename), "%s%s", acctfile, rotatesuffix);
+
+ if((rename(acctfile, filename)) != 0)
+ {
+ log(LL_ERR, "reopenfiles: acct rename failed, cause = %s", strerror(errno));
+ error_exit(1, "reopenfiles: acct rename failed, cause = %s", strerror(errno));
+ }
+ }
+
+ if((acctfp = fopen(acctfile, "a")) == NULL)
+ {
+ log(LL_ERR, "ERROR, can't open acctfile %s for writing, terminating!", acctfile);
+ error_exit(1, "ERROR, can't open acctfile %s for writing, terminating!", acctfile);
+ }
+ setvbuf(acctfp, (char *)NULL, _IONBF, 0);
+ }
+
+ if(uselogfile)
+ {
+ finish_log();
+
+ /* if user specified a suffix, rename the old file */
+
+ if(rotatesuffix[0] != '\0')
+ {
+ char filename[MAXPATHLEN];
+
+ snprintf(filename, sizeof(filename), "%s%s", logfile, rotatesuffix);
+
+ if((rename(logfile, filename)) != 0)
+ {
+ log(LL_ERR, "reopenfiles: log rename failed, cause = %s", strerror(errno));
+ error_exit(1, "reopenfiles: log rename failed, cause = %s", strerror(errno));
+ }
+ }
+
+ if((logfp = fopen(logfile, "a")) == NULL)
+ {
+ fprintf(stderr, "ERROR, cannot open logfile %s: %s\n",
+ logfile, strerror(errno));
+ error_exit(1, "reopenfiles: ERROR, cannot open logfile %s: %s\n",
+ logfile, strerror(errno));
+ }
+
+ /* set unbuffered operation */
+
+ setvbuf(logfp, (char *)NULL, _IONBF, 0);
+ }
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnd/monitor.c b/usr.sbin/i4b/isdnd/monitor.c
new file mode 100644
index 0000000..45952eb
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/monitor.c
@@ -0,0 +1,1284 @@
+/*
+ * Copyright (c) 1998, 1999 Martin Husemann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ * 4. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software and/or documentation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * i4b daemon - network monitor server module
+ * ------------------------------------------
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Tue Mar 26 14:37:03 2002]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "isdnd.h"
+
+#ifndef I4B_EXTERNAL_MONITOR
+
+/*
+ * dummy version of routines needed by config file parser
+ * (config files should be valid with and without external montioring
+ * support compiled into the daemon)
+ */
+
+void monitor_clear_rights()
+{ }
+
+int monitor_start_rights(const char *clientspec)
+{ return I4BMAR_OK; }
+
+void monitor_add_rights(int rights_mask)
+{ }
+
+void monitor_fixup_rights()
+{ }
+
+#else
+
+#include "monitor.h"
+#include <sys/socket.h>
+#include <sys/un.h>
+#ifndef I4B_NOTCPIP_MONITOR
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#endif
+
+
+static TAILQ_HEAD(rights_q, monitor_rights) rights = TAILQ_HEAD_INITIALIZER(rights);
+
+static struct monitor_rights * local_rights = NULL; /* entry for local socket */
+
+/* for each active monitor connection we have one of this: */
+
+struct monitor_connection {
+ TAILQ_ENTRY(monitor_connection) connections;
+ int sock; /* socket for this connection */
+ int rights; /* active rights for this connection */
+ int events; /* bitmask of events client is interested in */
+ char source[FILENAME_MAX];
+};
+
+static TAILQ_HEAD(connections_tq, monitor_connection) connections = TAILQ_HEAD_INITIALIZER(connections);
+
+/* local prototypes */
+static int cmp_rights(const struct monitor_rights *pa, const struct monitor_rights *pb);
+static int monitor_command(struct monitor_connection *con, int fd, int rights);
+static void cmd_dump_rights(int fd, int rights, u_int8_t *cmd, const char * source);
+static void cmd_dump_mcons(int fd, int rights, u_int8_t *cmd, const char * source);
+static void cmd_reread_cfg(int fd, int rights, u_int8_t *cmd, const char * source);
+static void cmd_hangup(int fd, int rights, u_int8_t *cmd, const char * source);
+static void monitor_broadcast(int mask, u_int8_t *pkt, size_t bytes);
+static int anybody(int mask);
+static void hangup_channel(int controller, int channel, const char *source);
+static ssize_t sock_read(int fd, void *buf, size_t nbytes);
+static ssize_t sock_write(int fd, void *buf, size_t nbytes);
+
+/*
+ * Due to the way we structure config files, the rights for an external
+ * monitor might be stated in multiple steps. First a call to
+ * monitor_start_rights opens an entry. Further (optional) calls to
+ * montior_add_rights assemble additional rights for this "current"
+ * entry. When closing the sys-file section of the config file, the
+ * "current" entry becomes invalid.
+ */
+static struct monitor_rights * cur_add_entry = NULL;
+
+/*---------------------------------------------------------------------------
+ * Initialize the monitor server module. This affects only active
+ * connections, the access rights are not modified here!
+ *---------------------------------------------------------------------------*/
+void
+monitor_init(void)
+{
+ struct monitor_connection * con;
+ accepted = 0;
+ while ((con = TAILQ_FIRST(&connections)) != NULL)
+ {
+ TAILQ_REMOVE(&connections, con, connections);
+ free(con);
+ }
+}
+
+/*---------------------------------------------------------------------------
+ * Prepare for exit
+ *---------------------------------------------------------------------------*/
+void
+monitor_exit(void)
+{
+ struct monitor_connection *c;
+
+ /* Close all open connections. */
+ while((c = TAILQ_FIRST(&connections)) != NULL) {
+ close(c->sock);
+ TAILQ_REMOVE(&connections, c, connections);
+ free(c);
+ }
+}
+
+/*---------------------------------------------------------------------------
+ * Initialize access rights. No active connections are affected!
+ *---------------------------------------------------------------------------*/
+void
+monitor_clear_rights(void)
+{
+ struct monitor_rights *r;
+ while ((r = TAILQ_FIRST(&rights)) != NULL) {
+ TAILQ_REMOVE(&rights, r, list);
+ free(r);
+ }
+ cur_add_entry = NULL;
+ local_rights = NULL;
+}
+
+/*---------------------------------------------------------------------------
+ * Add an entry to the access lists. The clientspec either is
+ * the name of the local socket or a host- or networkname or
+ * numeric ip/host-bit-len spec.
+ *---------------------------------------------------------------------------*/
+int
+monitor_start_rights(const char *clientspec)
+{
+ struct monitor_rights r;
+
+ /* initialize the new rights entry */
+
+ memset(&r, 0, sizeof r);
+
+ /* check clientspec */
+
+ if (*clientspec == '/')
+ {
+ struct sockaddr_un sa;
+
+ /* this is a local socket spec, check if we already have one */
+
+ if (local_rights != NULL)
+ return I4BMAR_DUP;
+
+ /* does it fit in a local socket address? */
+
+ if (strlen(clientspec) > sizeof sa.sun_path)
+ return I4BMAR_LENGTH;
+
+ r.local = 1;
+ strcpy(r.name, clientspec);
+
+#ifndef I4B_NOTCPIP_MONITOR
+
+ }
+ else
+ {
+ /* remote entry, parse host/net and cidr */
+
+ struct monitor_rights * rp;
+ char hostname[FILENAME_MAX];
+ char *p;
+
+ p = strchr(clientspec, '/');
+
+ if (!p)
+ {
+ struct hostent *host;
+ u_int32_t hn;
+
+ /* must be a host spec */
+
+ r.mask = ~0;
+ host = gethostbyname(clientspec);
+
+ if (!host)
+ return I4BMAR_NOIP;
+
+ memcpy(&hn, host->h_addr_list[0], sizeof hn);
+ r.net = (u_int32_t)ntohl(hn);
+ }
+ else if(p[1])
+ {
+ /* must be net/cidr spec */
+
+ int l;
+ struct netent *net;
+ u_int32_t s = ~0U;
+ int num = strtol(p+1, NULL, 10);
+
+ if (num < 0 || num > 32)
+ return I4BMAR_CIDR;
+
+ s >>= num;
+ s ^= ~0U;
+ l = p - clientspec;
+
+ if (l >= sizeof hostname)
+ return I4BMAR_LENGTH;
+
+ strncpy(hostname, clientspec, l);
+
+ hostname[l] = '\0';
+
+ net = getnetbyname(hostname);
+
+ if (net == NULL)
+ r.net = (u_int32_t)inet_network(hostname);
+ else
+ r.net = (u_int32_t)net->n_net;
+
+ r.mask = s;
+ r.net &= s;
+ }
+ else
+ {
+ return I4BMAR_CIDR;
+ }
+
+ /* check for duplicate entry */
+
+ for (rp = TAILQ_FIRST(&rights); rp != NULL; rp = TAILQ_NEXT(rp, list))
+ {
+ if (rp->mask == r.mask &&
+ rp->net == r.net &&
+ rp->local == r.local)
+ {
+ return I4BMAR_DUP;
+ }
+ }
+#endif
+ }
+
+ r.rights = 0;
+
+ /* entry ok, add it to the collection */
+
+ cur_add_entry = malloc(sizeof(r));
+ memcpy(cur_add_entry, &r, sizeof(r));
+ TAILQ_INSERT_TAIL(&rights, cur_add_entry, list);
+
+ if(r.local)
+ local_rights = cur_add_entry;
+
+ DBGL(DL_RCCF, (log(LL_DBG, "system: monitor = %s", clientspec)));
+
+ return I4BMAR_OK;
+}
+
+/*---------------------------------------------------------------------------
+ * Add rights to the currently constructed entry - if any.
+ *---------------------------------------------------------------------------*/
+void
+monitor_add_rights(int rights_mask)
+{
+ if(cur_add_entry == NULL)
+ return; /* noone under construction */
+
+ cur_add_entry->rights |= rights_mask;
+
+ DBGL(DL_RCCF, (log(LL_DBG, "system: monitor-access = 0x%x", rights_mask)));
+}
+
+/*---------------------------------------------------------------------------
+ * All rights have been added now. Sort the to get most specific
+ * host/net masks first, so we can travel the list and use the first
+ * match for actual rights.
+ *---------------------------------------------------------------------------*/
+void
+monitor_fixup_rights(void)
+{
+ struct monitor_rights * cur, * test, * next;
+
+ /* no more rights may be added to the current entry */
+
+ cur_add_entry = NULL;
+
+ /* sort the rights */
+ for (next = NULL, cur = TAILQ_FIRST(&rights); cur != NULL; cur = next)
+ {
+ next = TAILQ_NEXT(cur, list);
+ for (test = TAILQ_FIRST(&rights); test != NULL && test != cur; test = TAILQ_NEXT(test, list))
+ {
+ if (cmp_rights(cur, test) > 0) {
+ /* move cur up the list and insert before test */
+ TAILQ_REMOVE(&rights, cur, list);
+ if (test == TAILQ_FIRST(&rights))
+ TAILQ_INSERT_HEAD(&rights, cur, list);
+ else
+ TAILQ_INSERT_BEFORE(test, cur, list);
+ break;
+ }
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------
+ * comparator for rights
+ *---------------------------------------------------------------------------*/
+static int
+cmp_rights(const struct monitor_rights *pa, const struct monitor_rights *pb)
+{
+ u_int32_t mask;
+
+ /* local sorts first */
+
+ if (pa->local)
+ return -1;
+
+ /* which is the less specific netmask? */
+
+ mask = pa->mask;
+
+ if ((pb->mask & mask) == 0)
+ mask = pb->mask;
+
+ /* are the entries disjunct? */
+
+ if ((pa->net & mask) != (pb->net & mask))
+ {
+ /* simply compare net part of address */
+ return ((pa->net & mask) < (pb->net & mask)) ? -1 : 1;
+ }
+
+ /* One entry is part of the others net. We already now "mask" is
+ * the netmask of the less specific (i.e. greater) one */
+
+ return (pa->mask == mask) ? 1 : -1;
+}
+
+#ifndef I4B_NOTCPIP_MONITOR
+/*---------------------------------------------------------------------------
+ * Check if access rights for a remote socket are specified and
+ * create this socket. Return -1 otherwise.
+ *---------------------------------------------------------------------------*/
+int
+monitor_create_remote_socket(int portno)
+{
+ struct sockaddr_in sa;
+ int val;
+ int remotesockfd;
+
+ remotesockfd = socket(AF_INET, SOCK_STREAM, 0);
+
+ if(remotesockfd == -1)
+ {
+ log(LL_MER, "could not create remote monitor socket: %s", strerror(errno));
+ return(-1);
+ }
+
+ val = 1;
+
+ if(setsockopt(remotesockfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof val))
+ {
+ log(LL_MER, "could not setsockopt: %s", strerror(errno));
+ return(-1);
+ }
+
+ memset(&sa, 0, sizeof sa);
+ sa.sin_len = sizeof sa;
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons(portno);
+ sa.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ if(bind(remotesockfd, (struct sockaddr *)&sa, sizeof sa) == -1)
+ {
+ log(LL_MER, "could not bind remote monitor socket to port %d: %s", portno, strerror(errno));
+ return(-1);
+ }
+
+ if(listen(remotesockfd, 0))
+ {
+ log(LL_MER, "could not listen on monitor socket: %s", strerror(errno));
+ return(-1);
+ }
+
+ return(remotesockfd);
+}
+#endif
+
+/*---------------------------------------------------------------------------
+ * Check if access rights for a local socket are specified and
+ * create this socket. Return -1 otherwise.
+ *---------------------------------------------------------------------------*/
+int
+monitor_create_local_socket(void)
+{
+ int s;
+ struct sockaddr_un sa;
+
+ /* check for a local entry */
+
+ if (local_rights == NULL)
+ return(-1);
+
+ /* create and setup socket */
+
+ s = socket(AF_LOCAL, SOCK_STREAM, 0);
+
+ if (s == -1)
+ {
+ log(LL_MER, "could not create local monitor socket, errno = %d", errno);
+ return(-1);
+ }
+
+ unlink(local_rights->name);
+
+ memset(&sa, 0, sizeof sa);
+ sa.sun_len = sizeof sa;
+ sa.sun_family = AF_LOCAL;
+ strcpy(sa.sun_path, local_rights->name);
+
+ if (bind(s, (struct sockaddr *)&sa, SUN_LEN(&sa)))
+ {
+ log(LL_MER, "could not bind local monitor socket [%s], errno = %d", local_rights->name, errno);
+ return(-1);
+ }
+
+ chmod(local_rights->name, 0500);
+
+ if (listen(s, 0))
+ {
+ log(LL_MER, "could not listen on local monitor socket, errno = %d", errno);
+ return(-1);
+ }
+
+ return(s);
+}
+
+/*---------------------------------------------------------------------------
+ * Prepare a fd_set for a select call. Add all our local
+ * filedescriptors to the set, increment max_fd if appropriate.
+ *---------------------------------------------------------------------------*/
+void
+monitor_prepselect(fd_set *selset, int *max_fd)
+{
+ struct monitor_connection * con;
+
+ for (con = TAILQ_FIRST(&connections); con != NULL; con = TAILQ_NEXT(con, connections))
+ {
+ int fd = con->sock;
+
+ if (fd > *max_fd)
+ *max_fd = fd;
+
+ FD_SET(fd, selset);
+ }
+}
+
+/*---------------------------------------------------------------------------
+ * Check if the result from a select call indicates something
+ * to do for us.
+ *---------------------------------------------------------------------------*/
+void
+monitor_handle_input(fd_set *selset)
+{
+ struct monitor_connection * con, * next;
+
+ for (next = NULL, con = TAILQ_FIRST(&connections); con != NULL; con = next)
+ {
+ int fd = con->sock;
+ next = TAILQ_NEXT(con, connections);
+
+ if (FD_ISSET(fd, selset))
+ {
+ /* handle command from this client */
+
+ if (monitor_command(con, fd, con->rights) != 0)
+ {
+ /* broken or closed connection */
+
+ char source[FILENAME_MAX];
+
+ strcpy(source, con->source);
+ TAILQ_REMOVE(&connections, con, connections);
+ free(con);
+ log(LL_DMN, "monitor closed from %s", source );
+ }
+ }
+ }
+
+ /* all connections gone? */
+
+ if (TAILQ_FIRST(&connections) == NULL)
+ accepted = 0;
+}
+
+/*---------------------------------------------------------------------------
+ * Try new incoming connection on the given socket.
+ * Setup client descriptor and send initial data.
+ *---------------------------------------------------------------------------*/
+void
+monitor_handle_connect(int sockfd, int is_local)
+{
+ struct monitor_connection *con;
+ struct monitor_rights *rp;
+
+#ifndef I4B_NOTCPIP_MONITOR
+ struct sockaddr_in ia;
+ u_int32_t ha = 0;
+#endif
+
+ struct sockaddr_un ua;
+ u_int8_t idata[I4B_MON_IDATA_SIZE];
+ int fd = -1, s, i, r_mask, t_events;
+ char source[FILENAME_MAX];
+
+ /* accept the connection */
+
+ if(is_local)
+ {
+ s = sizeof ua;
+ fd = accept(sockfd, (struct sockaddr *)&ua, &s);
+ strcpy(source, "local");
+
+#ifndef I4B_NOTCPIP_MONITOR
+ }
+ else
+ {
+ struct hostent *hp;
+
+ s = sizeof ia;
+ fd = accept(sockfd, (struct sockaddr *)&ia, &s);
+
+ hp = gethostbyaddr((char *)&ia.sin_addr, 4, AF_INET);
+
+ if(hp == NULL)
+ snprintf(source, sizeof source, "%s (%s)", inet_ntoa(ia.sin_addr), inet_ntoa(ia.sin_addr));
+ else
+ snprintf(source, sizeof source, "%s (%s)", hp->h_name, inet_ntoa(ia.sin_addr));
+
+ memcpy(&ha, &ia.sin_addr.s_addr, sizeof ha);
+
+ ha = ntohl(ha);
+#endif
+ }
+
+ /* check the access rights of this connection */
+
+ r_mask = 0;
+
+ for (rp = TAILQ_FIRST(&rights); rp != NULL; rp = TAILQ_NEXT(rp, list))
+ {
+ if(rp->local)
+ {
+ if(is_local)
+ {
+ r_mask = rp->rights;
+ break;
+ }
+
+#ifndef I4B_NOTCPIP_MONITOR
+ }
+ else
+ {
+ if((ha & rp->mask) == rp->net)
+ {
+ r_mask = rp->rights;
+ break;
+ }
+#endif
+ }
+ }
+
+ if(r_mask == 0)
+ {
+ /* no rights - go away */
+ log(LL_MER, "monitor access denied from %s", source);
+ close(fd);
+ return;
+ }
+
+ accepted = 1;
+
+ con = malloc(sizeof(struct monitor_connection));
+ memset(con, 0, sizeof *con);
+ TAILQ_INSERT_TAIL(&connections, con, connections);
+ con->sock = fd;
+ con->rights = r_mask;
+ strcpy(con->source, source);
+
+ log(LL_DMN, "monitor opened from %s rights 0x%x", source, r_mask);
+
+ /* send initial data */
+ I4B_PREP_CMD(idata, I4B_MON_IDATA_CODE);
+ I4B_PUT_2B(idata, I4B_MON_IDATA_VERSMAJOR, MPROT_VERSION);
+ I4B_PUT_2B(idata, I4B_MON_IDATA_VERSMINOR, MPROT_REL);
+ I4B_PUT_2B(idata, I4B_MON_IDATA_NUMCTRL, ncontroller);
+ I4B_PUT_2B(idata, I4B_MON_IDATA_NUMENTR, nentries);
+ I4B_PUT_4B(idata, I4B_MON_IDATA_CLACCESS, r_mask);
+
+ if((sock_write(fd, idata, sizeof idata)) == -1)
+ {
+ log(LL_MER, "monitor_handle_connect: sock_write 1 error - %s", strerror(errno));
+ }
+
+ for (i = 0; i < ncontroller; i++)
+ {
+ u_int8_t ictrl[I4B_MON_ICTRL_SIZE];
+
+ I4B_PREP_CMD(ictrl, I4B_MON_ICTRL_CODE);
+ I4B_PUT_STR(ictrl, I4B_MON_ICTRL_NAME, name_of_controller(isdn_ctrl_tab[i].ctrl_type, isdn_ctrl_tab[i].card_type));
+ I4B_PUT_2B(ictrl, I4B_MON_ICTRL_BUSID, 0);
+ I4B_PUT_4B(ictrl, I4B_MON_ICTRL_FLAGS, 0);
+ I4B_PUT_4B(ictrl, I4B_MON_ICTRL_NCHAN, 2);
+
+ if((sock_write(fd, ictrl, sizeof ictrl)) == -1)
+ {
+ log(LL_MER, "monitor_handle_connect: sock_write 2 error - %s", strerror(errno));
+ }
+
+ }
+
+ /* send device names from entries */
+
+ for(i=0; i < nentries; i++) /* walk thru all entries */
+ {
+ u_int8_t ictrl[I4B_MON_IDEV_SIZE];
+ cfg_entry_t *p;
+ char nbuf[64];
+ p = &cfg_entry_tab[i]; /* get ptr to enry */
+
+ snprintf(nbuf, sizeof(nbuf), "%s%d ", bdrivername(p->usrdevicename), p->usrdeviceunit);
+
+ I4B_PREP_CMD(ictrl, I4B_MON_IDEV_CODE);
+/*XXX*/ I4B_PUT_2B(ictrl, I4B_MON_IDEV_STATE, 1);
+ I4B_PUT_STR(ictrl, I4B_MON_IDEV_NAME, nbuf);
+
+ if((sock_write(fd, ictrl, sizeof ictrl)) == -1)
+ {
+ log(LL_MER, "monitor_handle_connect: sock_write 3 error - %s", strerror(errno));
+ }
+ }
+
+/*XXX*/ t_events = con->events;
+/*XXX*/ con->events = -1;
+
+ /* current state of controller(s) */
+
+ for(i=0; i < ncontroller; i++)
+ {
+ monitor_evnt_tei(i, isdn_ctrl_tab[i].tei);
+ monitor_evnt_l12stat(i, LAYER_ONE, isdn_ctrl_tab[i].l1stat);
+ monitor_evnt_l12stat(i, LAYER_TWO, isdn_ctrl_tab[i].l2stat);
+ }
+
+ /* current state of entries */
+
+ for(i=0; i < nentries; i++)
+ {
+ cfg_entry_t *cep = &cfg_entry_tab[i];
+
+ if(cep->state == ST_CONNECTED)
+ {
+ monitor_evnt_connect(cep);
+ monitor_evnt_acct(cep);
+ monitor_evnt_charge(cep, cep->charge, 1);
+ }
+ }
+
+/*XXX*/ con->events = t_events;
+
+}
+
+/*---------------------------------------------------------------------------
+ * dump all monitor rights
+ *---------------------------------------------------------------------------*/
+static void
+cmd_dump_rights(int fd, int r_mask, u_int8_t *cmd, const char *source)
+{
+ struct monitor_rights * r;
+ int num_rights;
+ u_int8_t drini[I4B_MON_DRINI_SIZE];
+ u_int8_t dr[I4B_MON_DR_SIZE];
+
+ for (num_rights = 0, r = TAILQ_FIRST(&rights); r != NULL; r = TAILQ_NEXT(r, list))
+ num_rights++;
+
+ I4B_PREP_EVNT(drini, I4B_MON_DRINI_CODE);
+ I4B_PUT_2B(drini, I4B_MON_DRINI_COUNT, num_rights);
+
+ if((sock_write(fd, drini, sizeof drini)) == -1)
+ {
+ log(LL_MER, "cmd_dump_rights: sock_write 1 error - %s", strerror(errno));
+ }
+
+ for (r = TAILQ_FIRST(&rights); r != NULL; r = TAILQ_NEXT(r, list))
+ {
+ I4B_PREP_EVNT(dr, I4B_MON_DR_CODE);
+ I4B_PUT_4B(dr, I4B_MON_DR_RIGHTS, r->rights);
+ I4B_PUT_4B(dr, I4B_MON_DR_NET, r->net);
+ I4B_PUT_4B(dr, I4B_MON_DR_MASK, r->mask);
+ I4B_PUT_1B(dr, I4B_MON_DR_LOCAL, r->local);
+ if((sock_write(fd, dr, sizeof dr)) == -1)
+ {
+ log(LL_MER, "cmd_dump_rights: sock_write 2 error - %s", strerror(errno));
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------
+ * rescan config file
+ *---------------------------------------------------------------------------*/
+static void
+cmd_reread_cfg(int fd, int rights, u_int8_t *cmd, const char * source)
+{
+ rereadconfig(42);
+}
+
+/*---------------------------------------------------------------------------
+ * drop one connection
+ *---------------------------------------------------------------------------*/
+static void
+cmd_hangup(int fd, int rights, u_int8_t *cmd, const char * source)
+{
+ int channel = I4B_GET_4B(cmd, I4B_MON_HANGUP_CHANNEL);
+ int ctrl = I4B_GET_4B(cmd, I4B_MON_HANGUP_CTRL);
+
+ hangup_channel(ctrl, channel, source);
+}
+
+/*---------------------------------------------------------------------------
+ * dump all active monitor connections
+ *---------------------------------------------------------------------------*/
+static void
+cmd_dump_mcons(int fd, int rights, u_int8_t *cmd, const char * source)
+{
+ int num_connections;
+ struct monitor_connection *con;
+ u_int8_t dcini[I4B_MON_DCINI_SIZE];
+
+ for (num_connections = 0, con = TAILQ_FIRST(&connections); con != NULL; con = TAILQ_NEXT(con, connections))
+ num_connections++;
+
+ I4B_PREP_EVNT(dcini, I4B_MON_DCINI_CODE);
+ I4B_PUT_2B(dcini, I4B_MON_DCINI_COUNT, num_connections);
+
+ if((sock_write(fd, dcini, sizeof dcini)) == -1)
+ {
+ log(LL_MER, "cmd_dump_mcons: sock_write 1 error - %s", strerror(errno));
+ }
+
+ for (con = TAILQ_FIRST(&connections); con != NULL; con = TAILQ_NEXT(con, connections))
+ {
+#ifndef I4B_NOTCPIP_MONITOR
+ int namelen;
+ struct sockaddr_in name;
+#endif
+ u_int8_t dc[I4B_MON_DC_SIZE];
+
+ I4B_PREP_EVNT(dc, I4B_MON_DC_CODE);
+ I4B_PUT_4B(dc, I4B_MON_DC_RIGHTS, con->rights);
+
+#ifndef I4B_NOTCPIP_MONITOR
+ namelen = sizeof name;
+
+ if (getpeername(con->sock, (struct sockaddr*)&name, &namelen) == 0)
+ memcpy(dc+I4B_MON_DC_WHO, &name.sin_addr, sizeof name.sin_addr);
+#endif
+ if((sock_write(fd, dc, sizeof dc)) == -1)
+ {
+ log(LL_MER, "cmd_dump_mcons: sock_write 2 error - %s", strerror(errno));
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------
+ * Handle a command from the given socket. The client
+ * has rights as specified in the rights parameter.
+ * Return non-zero if connection is closed.
+ *---------------------------------------------------------------------------*/
+static int
+monitor_command(struct monitor_connection * con, int fd, int rights)
+{
+ char cmd[I4B_MAX_MON_CLIENT_CMD];
+ u_int code;
+
+ /* command dispatch table */
+ typedef void (*cmd_func_t)(int fd, int rights, u_int8_t *cmd, const char *source);
+
+ static struct {
+ cmd_func_t call; /* function to execute */
+ u_int rights; /* necessary rights */
+ } cmd_tab[] =
+ {
+ /* 0 */ { NULL, 0 },
+ /* 1 */ { cmd_dump_rights, I4B_CA_COMMAND_FULL },
+ /* 2 */ { cmd_dump_mcons, I4B_CA_COMMAND_FULL },
+ /* 3 */ { cmd_reread_cfg, I4B_CA_COMMAND_FULL },
+ /* 4 */ { cmd_hangup, I4B_CA_COMMAND_FULL },
+ };
+#define NUMCMD (sizeof cmd_tab / sizeof cmd_tab[0])
+
+ u_long u;
+ int bytes;
+
+ /* Network transfer may deliver two or more packets concatenated.
+ * Peek at the header and read only one event at a time... */
+
+ ioctl(fd, FIONREAD, &u);
+
+ if (u < I4B_MON_CMD_HDR)
+ {
+ if (u == 0)
+ {
+ /* log(LL_MER, "monitor read 0 bytes"); */
+ /* socket closed by peer */
+ close(fd);
+ return 1;
+ }
+ return 0; /* not enough data there yet */
+ }
+
+ bytes = recv(fd, cmd, I4B_MON_CMD_HDR, MSG_PEEK);
+
+ if (bytes < I4B_MON_CMD_HDR)
+ {
+ log(LL_MER, "monitor read only %d bytes", bytes);
+ return 0; /* errh? something must be wrong... */
+ }
+
+ bytes = I4B_GET_2B(cmd, I4B_MON_CMD_LEN);
+
+ if (bytes >= sizeof cmd)
+ {
+ close(fd);
+ log(LL_MER, "monitor: garbage on connection");
+ return 1;
+ }
+
+ /* now we know the size, it fits, so lets read it! */
+
+ if(sock_read(fd, cmd, bytes) <= 0)
+ {
+ log(LL_MER, "monitor: sock_read <= 0");
+ close(fd);
+ return 1;
+ }
+
+ /* decode command */
+ code = I4B_GET_2B(cmd, I4B_MON_CMD);
+
+ /* special case: may modify our connection descriptor, is
+ * beyound all rights checks */
+
+ if (code == I4B_MON_CCMD_SETMASK)
+ {
+/*XXX*/
+ /*
+ u_int major = I4B_GET_2B(cmd, I4B_MON_ICLIENT_VERMAJOR);
+ u_int minor = I4B_GET_2B(cmd, I4B_MON_ICLIENT_VERMINOR);
+ */
+
+ int events = I4B_GET_4B(cmd, I4B_MON_ICLIENT_EVENTS);
+ con->events = events & rights;
+ return 0;
+ }
+
+ if (code < 0 || code >= NUMCMD)
+ {
+ log(LL_MER, "illegal command from client, code = %d\n",
+ code);
+ return 0;
+ }
+
+ if (cmd_tab[code].call == NULL)
+ return 0;
+
+ if ((cmd_tab[code].rights & rights) == cmd_tab[code].rights)
+ cmd_tab[code].call(fd, rights, cmd, con->source);
+
+ return 0;
+}
+
+/*---------------------------------------------------------------------------
+ * Check if somebody would receive an event with this mask.
+ * We are lazy and try to avoid assembling unneccesary packets.
+ * Return 0 if no one interested, nonzero otherwise.
+ *---------------------------------------------------------------------------*/
+static int
+anybody(int mask)
+{
+ struct monitor_connection * con;
+
+ for (con = TAILQ_FIRST(&connections); con != NULL; con = TAILQ_NEXT(con, connections))
+ {
+ if ((con->events & mask) == mask)
+ return 1;
+ }
+ return 0;
+}
+
+/*---------------------------------------------------------------------------
+ * exec hangup command
+ *---------------------------------------------------------------------------*/
+static void
+hangup_channel(int controller, int channel, const char *source)
+{
+ cfg_entry_t * cep = NULL;
+ int i;
+
+ if(controller < ncontroller)
+ {
+ if(isdn_ctrl_tab[controller].state != CTRL_UP)
+ return;
+ for (i = 0; i < isdn_ctrl_tab[controller].nbch; i++)
+ {
+ if(isdn_ctrl_tab[controller].stateb[i] != CHAN_IDLE)
+ {
+ cep = get_cep_by_cc(controller, i);
+ if (cep != NULL && cep->isdnchannelused == channel &&
+ cep->isdncontrollerused == controller)
+ goto found;
+ }
+ }
+ }
+ /* not found */
+ return;
+
+found:
+ log(LL_CHD, "%05d %s manual disconnect (remote from %s)", cep->cdid, cep->name, source);
+ cep->hangup = 1;
+ return;
+}
+
+/*---------------------------------------------------------------------------
+ * Send an event to every connection interested in this kind of
+ * event
+ *---------------------------------------------------------------------------*/
+static void
+monitor_broadcast(int mask, u_int8_t *pkt, size_t bytes)
+{
+ struct monitor_connection *con;
+
+ for (con = TAILQ_FIRST(&connections); con != NULL; con = TAILQ_NEXT(con, connections))
+ {
+ if ((con->events & mask) == mask)
+ {
+ int fd = con->sock;
+
+ if((sock_write(fd, pkt, bytes)) == -1)
+ {
+ log(LL_MER, "monitor_broadcast: sock_write error - %s", strerror(errno));
+ }
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------
+ * Post a logfile event
+ *---------------------------------------------------------------------------*/
+void
+monitor_evnt_log(int prio, const char * what, const char * msg)
+{
+ u_int8_t evnt[I4B_MON_LOGEVNT_SIZE];
+ time_t now;
+
+ if (!anybody(I4B_CA_EVNT_I4B))
+ return;
+
+ time(&now);
+
+ I4B_PREP_EVNT(evnt, I4B_MON_LOGEVNT_CODE);
+ I4B_PUT_4B(evnt, I4B_MON_LOGEVNT_TSTAMP, (long)now);
+ I4B_PUT_4B(evnt, I4B_MON_LOGEVNT_PRIO, prio);
+ I4B_PUT_STR(evnt, I4B_MON_LOGEVNT_WHAT, what);
+ I4B_PUT_STR(evnt, I4B_MON_LOGEVNT_MSG, msg);
+
+ monitor_broadcast(I4B_CA_EVNT_I4B, evnt, sizeof evnt);
+}
+
+/*---------------------------------------------------------------------------
+ * Post a charging event on the connection described
+ * by the given config entry.
+ *---------------------------------------------------------------------------*/
+void
+monitor_evnt_charge(cfg_entry_t *cep, int units, int estimate)
+{
+ int mask;
+ time_t now;
+ u_int8_t evnt[I4B_MON_CHRG_SIZE];
+
+ mask = (cep->direction == DIR_IN) ? I4B_CA_EVNT_CALLIN : I4B_CA_EVNT_CALLOUT;
+
+ if(!anybody(mask))
+ return;
+
+ time(&now);
+
+ I4B_PREP_EVNT(evnt, I4B_MON_CHRG_CODE);
+ I4B_PUT_4B(evnt, I4B_MON_CHRG_TSTAMP, (long)now);
+ I4B_PUT_4B(evnt, I4B_MON_CHRG_CTRL, cep->isdncontrollerused);
+ I4B_PUT_4B(evnt, I4B_MON_CHRG_CHANNEL, cep->isdnchannelused);
+ I4B_PUT_4B(evnt, I4B_MON_CHRG_UNITS, units);
+ I4B_PUT_4B(evnt, I4B_MON_CHRG_ESTIMATED, estimate ? 1 : 0);
+
+ monitor_broadcast(mask, evnt, sizeof evnt);
+}
+
+/*---------------------------------------------------------------------------
+ * Post a connection event
+ *---------------------------------------------------------------------------*/
+void
+monitor_evnt_connect(cfg_entry_t *cep)
+{
+ u_int8_t evnt[I4B_MON_CONNECT_SIZE];
+ char devname[I4B_MAX_MON_STRING];
+ int mask;
+ time_t now;
+
+ mask = (cep->direction == DIR_IN) ? I4B_CA_EVNT_CALLIN : I4B_CA_EVNT_CALLOUT;
+
+ if (!anybody(mask))
+ return;
+
+ time(&now);
+
+ snprintf(devname, sizeof devname, "%s%d", bdrivername(cep->usrdevicename), cep->usrdeviceunit);
+
+ I4B_PREP_EVNT(evnt, I4B_MON_CONNECT_CODE);
+ I4B_PUT_4B(evnt, I4B_MON_CONNECT_TSTAMP, (long)now);
+ I4B_PUT_4B(evnt, I4B_MON_CONNECT_DIR, cep->direction == DIR_OUT ? 1 : 0);
+ I4B_PUT_4B(evnt, I4B_MON_CONNECT_CTRL, cep->isdncontrollerused);
+ I4B_PUT_4B(evnt, I4B_MON_CONNECT_CHANNEL, cep->isdnchannelused);
+ I4B_PUT_STR(evnt, I4B_MON_CONNECT_CFGNAME, cep->name);
+ I4B_PUT_STR(evnt, I4B_MON_CONNECT_DEVNAME, devname);
+
+ if(cep->direction == DIR_OUT)
+ {
+ I4B_PUT_STR(evnt, I4B_MON_CONNECT_REMPHONE, cep->remote_phone_dialout.number);
+ I4B_PUT_STR(evnt, I4B_MON_CONNECT_LOCPHONE, cep->local_phone_dialout.number);
+ }
+ else
+ {
+ I4B_PUT_STR(evnt, I4B_MON_CONNECT_REMPHONE, cep->real_phone_incoming.number);
+ I4B_PUT_STR(evnt, I4B_MON_CONNECT_LOCPHONE, cep->local_phone_incoming.number);
+ }
+ monitor_broadcast(mask, evnt, sizeof evnt);
+}
+
+/*---------------------------------------------------------------------------
+ * Post a disconnect event
+ *---------------------------------------------------------------------------*/
+void
+monitor_evnt_disconnect(cfg_entry_t *cep)
+{
+ u_int8_t evnt[I4B_MON_DISCONNECT_SIZE];
+ int mask;
+ time_t now;
+
+ mask = (cep->direction == DIR_IN) ? I4B_CA_EVNT_CALLIN : I4B_CA_EVNT_CALLOUT;
+
+ if (!anybody(mask))
+ return;
+
+ time(&now);
+
+ I4B_PREP_EVNT(evnt, I4B_MON_DISCONNECT_CODE);
+ I4B_PUT_4B(evnt, I4B_MON_DISCONNECT_TSTAMP, (long)now);
+ I4B_PUT_4B(evnt, I4B_MON_DISCONNECT_CTRL, cep->isdncontrollerused);
+ I4B_PUT_4B(evnt, I4B_MON_DISCONNECT_CHANNEL, cep->isdnchannelused);
+
+ monitor_broadcast(mask, evnt, sizeof evnt);
+}
+
+/*---------------------------------------------------------------------------
+ * Post an up/down event
+ *---------------------------------------------------------------------------*/
+void
+monitor_evnt_updown(cfg_entry_t *cep, int up)
+{
+ u_int8_t evnt[I4B_MON_UPDOWN_SIZE];
+ int mask;
+ time_t now;
+
+ mask = (cep->direction == DIR_IN) ? I4B_CA_EVNT_CALLIN : I4B_CA_EVNT_CALLOUT;
+
+ if (!anybody(mask))
+ return;
+
+ time(&now);
+
+ I4B_PREP_EVNT(evnt, I4B_MON_UPDOWN_CODE);
+ I4B_PUT_4B(evnt, I4B_MON_UPDOWN_TSTAMP, (long)now);
+ I4B_PUT_4B(evnt, I4B_MON_UPDOWN_CTRL, cep->isdncontrollerused);
+ I4B_PUT_4B(evnt, I4B_MON_UPDOWN_CHANNEL, cep->isdnchannelused);
+ I4B_PUT_4B(evnt, I4B_MON_UPDOWN_ISUP, up);
+
+ monitor_broadcast(mask, evnt, sizeof evnt);
+}
+
+/*---------------------------------------------------------------------------
+ * Post a Layer1/2 status change event
+ *---------------------------------------------------------------------------*/
+void
+monitor_evnt_l12stat(int controller, int layer, int state)
+{
+ u_int8_t evnt[I4B_MON_L12STAT_SIZE];
+ time_t now;
+
+ if(!anybody(I4B_CA_EVNT_I4B))
+ return;
+
+ time(&now);
+
+ I4B_PREP_EVNT(evnt, I4B_MON_L12STAT_CODE);
+ I4B_PUT_4B(evnt, I4B_MON_L12STAT_TSTAMP, (long)now);
+ I4B_PUT_4B(evnt, I4B_MON_L12STAT_CTRL, controller);
+ I4B_PUT_4B(evnt, I4B_MON_L12STAT_LAYER, layer);
+ I4B_PUT_4B(evnt, I4B_MON_L12STAT_STATE, state);
+
+ monitor_broadcast(I4B_CA_EVNT_I4B, evnt, sizeof evnt);
+}
+
+/*---------------------------------------------------------------------------
+ * Post a TEI change event
+ *---------------------------------------------------------------------------*/
+void
+monitor_evnt_tei(int controller, int tei)
+{
+ u_int8_t evnt[I4B_MON_TEI_SIZE];
+ time_t now;
+
+ if(!anybody(I4B_CA_EVNT_I4B))
+ return;
+
+ time(&now);
+
+ I4B_PREP_EVNT(evnt, I4B_MON_TEI_CODE);
+ I4B_PUT_4B(evnt, I4B_MON_TEI_TSTAMP, (long)now);
+ I4B_PUT_4B(evnt, I4B_MON_TEI_CTRL, controller);
+ I4B_PUT_4B(evnt, I4B_MON_TEI_TEI, tei);
+
+ monitor_broadcast(I4B_CA_EVNT_I4B, evnt, sizeof evnt);
+}
+
+/*---------------------------------------------------------------------------
+ * Post an accounting event
+ *---------------------------------------------------------------------------*/
+void
+monitor_evnt_acct(cfg_entry_t *cep)
+{
+ u_int8_t evnt[I4B_MON_ACCT_SIZE];
+ time_t now;
+
+ if(!anybody(I4B_CA_EVNT_I4B))
+ return;
+
+ time(&now);
+
+ I4B_PREP_EVNT(evnt, I4B_MON_ACCT_CODE);
+ I4B_PUT_4B(evnt, I4B_MON_ACCT_TSTAMP, (long)now);
+
+ I4B_PUT_4B(evnt, I4B_MON_ACCT_CTRL, cep->isdncontrollerused);
+ I4B_PUT_4B(evnt, I4B_MON_ACCT_CHAN, cep->isdnchannelused);
+ I4B_PUT_4B(evnt, I4B_MON_ACCT_OBYTES, cep->outbytes);
+ I4B_PUT_4B(evnt, I4B_MON_ACCT_OBPS, cep->outbps);
+ I4B_PUT_4B(evnt, I4B_MON_ACCT_IBYTES, cep->inbytes);
+ I4B_PUT_4B(evnt, I4B_MON_ACCT_IBPS, cep->inbps);
+
+ monitor_broadcast(I4B_CA_EVNT_I4B, evnt, sizeof evnt);
+}
+
+/*---------------------------------------------------------------------------
+ * read from a socket
+ *---------------------------------------------------------------------------*/
+static ssize_t
+sock_read(int fd, void *buf, size_t nbytes)
+{
+ size_t nleft;
+ ssize_t nread;
+ unsigned char *ptr;
+
+ ptr = buf;
+ nleft = nbytes;
+
+ while(nleft > 0)
+ {
+ if((nread = read(fd, ptr, nleft)) < 0)
+ {
+ if(errno == EINTR)
+ {
+ nread = 0;
+ }
+ else
+ {
+ return(-1);
+ }
+ }
+ else if(nread == 0)
+ {
+ break; /* EOF */
+ }
+
+ nleft -= nread;
+ ptr += nread;
+ }
+ return(nbytes - nleft);
+}
+
+/*---------------------------------------------------------------------------
+ * write to a socket
+ *---------------------------------------------------------------------------*/
+static ssize_t
+sock_write(int fd, void *buf, size_t nbytes)
+{
+ size_t nleft;
+ ssize_t nwritten;
+ unsigned char *ptr;
+
+ ptr = buf;
+ nleft = nbytes;
+
+ while(nleft > 0)
+ {
+ if((nwritten = write(fd, ptr, nleft)) <= 0)
+ {
+ if(errno == EINTR)
+ {
+ nwritten = 0;
+ }
+ else
+ {
+ return(-1);
+ }
+ }
+
+ nleft -= nwritten;
+ ptr += nwritten;
+ }
+ return(nbytes);
+}
+
+struct monitor_rights * monitor_next_rights(const struct monitor_rights *r)
+{
+ if (r == NULL)
+ return TAILQ_FIRST(&rights);
+ else
+ return TAILQ_NEXT(r, list);
+}
+
+#endif /* I4B_EXTERNAL_MONITOR */
diff --git a/usr.sbin/i4b/isdnd/msghdl.c b/usr.sbin/i4b/isdnd/msghdl.c
new file mode 100644
index 0000000..0d5364a
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/msghdl.c
@@ -0,0 +1,1340 @@
+/*
+ * Copyright (c) 1997, 2002 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * i4b daemon - message from kernel handling routines
+ * --------------------------------------------------
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Tue Mar 26 14:37:25 2002]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "isdnd.h"
+
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/if_types.h>
+
+#if defined(__FreeBSD__)
+#include <net/if_var.h>
+#endif
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+#include <netinet/ip_icmp.h>
+
+/*---------------------------------------------------------------------------*
+ * handle incoming CONNECT_IND (=SETUP) message
+ *---------------------------------------------------------------------------*/
+void
+msg_connect_ind(msg_connect_ind_t *mp)
+{
+ cfg_entry_t *cep;
+ char *src_tela = "ERROR-src_tela";
+ char *dst_tela = "ERROR-dst_tela";
+
+#define SRC (aliasing == 0 ? mp->src_telno : src_tela)
+#define DST (aliasing == 0 ? mp->dst_telno : dst_tela)
+
+ if(aliasing)
+ {
+ src_tela = get_alias(mp->src_telno);
+ dst_tela = get_alias(mp->dst_telno);
+ }
+
+ if((cep = find_matching_entry_incoming(mp)) == NULL)
+ {
+ /* log message generated in find_matching_entry_incoming() */
+ sendm_connect_resp(NULL, mp->header.cdid, SETUP_RESP_DNTCRE, 0);
+ handle_scrprs(mp->header.cdid, mp->scr_ind, mp->prs_ind, SRC);
+ return;
+ }
+
+ if(cep->cdid != CDID_UNUSED && cep->cdid != CDID_RESERVED)
+ {
+ /*
+ * This is an incoming call on a number we just dialed out.
+ * Stop our dial-out and accept the incoming call.
+ */
+ if(cep->saved_call.cdid != CDID_UNUSED &&
+ cep->saved_call.cdid != CDID_RESERVED)
+ {
+ int cdid;
+
+ /* disconnect old, not new */
+
+ cdid = cep->cdid;
+ cep->cdid = cep->saved_call.cdid;
+ sendm_disconnect_req(cep, (CAUSET_I4B << 8) | CAUSE_I4B_NORMAL);
+ cep->cdid = cdid;
+
+ /*
+ * Shortcut the state machine and mark this
+ * entry as free
+ */
+/* XXX */ cep->state = ST_IDLE; /* this is an invalid */
+ /* transition, */
+ /* so no next_state() */
+ /* we have to wait here for an incoming */
+ /* disconnect message !!! (-hm) */
+ }
+ }
+
+ if(cep->inout == DIR_OUTONLY)
+ {
+ log(LL_CHD, "%05d %s incoming call from %s to %s not allowed by configuration!",
+ mp->header.cdid, cep->name, SRC, DST);
+ sendm_connect_resp(NULL, mp->header.cdid, SETUP_RESP_DNTCRE, 0);
+ handle_scrprs(mp->header.cdid, mp->scr_ind, mp->prs_ind, SRC);
+ return;
+ }
+
+ cep->charge = 0;
+ cep->last_charge = 0;
+
+ switch(cep->dialin_reaction)
+ {
+ case REACT_ACCEPT:
+ log(LL_CHD, "%05d %s accepting: incoming call from %s to %s",
+ mp->header.cdid, cep->name, SRC, DST);
+ decr_free_channels(mp->controller);
+ next_state(cep, EV_MCI);
+ break;
+
+ case REACT_REJECT:
+ log(LL_CHD, "%05d %s rejecting: incoming call from %s to %s",
+ mp->header.cdid, cep->name, SRC, DST);
+ sendm_connect_resp(cep, mp->header.cdid, SETUP_RESP_REJECT,
+ (CAUSET_I4B << 8) | CAUSE_I4B_REJECT);
+ cep->cdid = CDID_UNUSED;
+ break;
+
+ case REACT_IGNORE:
+ log(LL_CHD, "%05d %s ignoring: incoming call from %s to %s",
+ mp->header.cdid, cep->name, SRC, DST);
+ sendm_connect_resp(NULL, mp->header.cdid, SETUP_RESP_DNTCRE, 0);
+ cep->cdid = CDID_UNUSED;
+ break;
+
+ case REACT_ANSWER:
+ decr_free_channels(mp->controller);
+ if(cep->alert)
+ {
+ if(mp->display)
+ {
+ log(LL_CHD, "%05d %s alerting: incoming call from %s to %s (%s)",
+ mp->header.cdid, cep->name, SRC, DST, mp->display);
+ }
+ else
+ {
+ log(LL_CHD, "%05d %s alerting: incoming call from %s to %s",
+ mp->header.cdid, cep->name, SRC, DST);
+ }
+ next_state(cep, EV_ALRT);
+ }
+ else
+ {
+ if(mp->display)
+ {
+ log(LL_CHD, "%05d %s answering: incoming call from %s to %s (%s)",
+ mp->header.cdid, cep->name, SRC, DST, mp->display);
+ }
+ else
+ {
+ log(LL_CHD, "%05d %s answering: incoming call from %s to %s",
+ mp->header.cdid, cep->name, SRC, DST);
+ }
+ next_state(cep, EV_MCI);
+ }
+ break;
+
+ case REACT_CALLBACK:
+
+#ifdef NOTDEF
+/*XXX reserve channel ??? */ decr_free_channels(mp->controller);
+#endif
+ if(cep->cdid == CDID_RESERVED)
+ {
+ log(LL_CHD, "%05d %s reserved: incoming call from %s to %s",
+ mp->header.cdid, cep->name, SRC, DST);
+ sendm_connect_resp(cep, mp->header.cdid, SETUP_RESP_REJECT,
+#if 0
+ (CAUSET_I4B << 8) | CAUSE_I4B_NORMAL);
+#else
+ (CAUSET_I4B << 8) | CAUSE_I4B_REJECT);
+#endif
+ /* no state change */
+ }
+ else
+ {
+ sendm_connect_resp(cep, mp->header.cdid, SETUP_RESP_REJECT,
+#if 0
+ (CAUSET_I4B << 8) | CAUSE_I4B_NORMAL);
+#else
+ (CAUSET_I4B << 8) | CAUSE_I4B_REJECT);
+#endif
+ if(cep->budget_callbackperiod && cep->budget_callbackncalls)
+ {
+ cep->budget_callback_req++;
+ cep->budget_calltype = 0;
+ if(cep->budget_callbackncalls_cnt == 0)
+ {
+ log(LL_CHD, "%05d %s no budget: call from %s to %s",
+ mp->header.cdid, cep->name, SRC, DST);
+ cep->cdid = CDID_UNUSED;
+ cep->budget_callback_rej++;
+ break;
+ }
+ else
+ {
+ cep->budget_calltype = BUDGET_TYPE_CBACK;
+ }
+ }
+
+ log(LL_CHD, "%05d %s callback: incoming call from %s to %s",
+ mp->header.cdid, cep->name, SRC, DST);
+
+ cep->last_release_time = time(NULL);
+ cep->cdid = CDID_RESERVED;
+ next_state(cep, EV_CBRQ);
+ }
+ break;
+
+ default:
+ log(LL_WRN, "msg_connect_ind: unknown response type, tx SETUP_RESP_DNTCRE");
+ sendm_connect_resp(NULL, mp->header.cdid, SETUP_RESP_DNTCRE, 0);
+ break;
+ }
+ handle_scrprs(mp->header.cdid, mp->scr_ind, mp->prs_ind, SRC);
+#undef SRC
+#undef DST
+}
+
+/*---------------------------------------------------------------------------*
+ * handle incoming CONNECT_ACTIVE_IND message
+ *---------------------------------------------------------------------------*/
+void
+msg_connect_active_ind(msg_connect_active_ind_t *mp)
+{
+ cfg_entry_t *cep;
+ char *device;
+
+ if((cep = get_cep_by_cdid(mp->header.cdid)) == NULL)
+ {
+ log(LL_WRN, "msg_connect_active_ind: cdid not found!");
+ return;
+ }
+
+ cep->isdncontrollerused = mp->controller;
+ cep->isdnchannelused = mp->channel;
+
+ cep->aoc_now = cep->connect_time = time(NULL);
+ cep->aoc_last = 0;
+ cep->aoc_diff = 0;
+ cep->aoc_valid = AOC_INVALID;
+
+ cep->local_disconnect = DISCON_REM;
+
+ cep->inbytes = INVALID;
+ cep->outbytes = INVALID;
+ cep->hangup = 0;
+
+ device = bdrivername(cep->usrdevicename);
+
+ /* set the B-channel to active */
+
+ if((set_channel_busy(cep->isdncontrollerused, cep->isdnchannelused)) == ERROR)
+ log(LL_ERR, "msg_connect_active_ind: set_channel_busy failed!");
+
+ if(cep->direction == DIR_OUT)
+ {
+ log(LL_CHD, "%05d %s outgoing call active (ctl %d, ch %d, %s%d)",
+ cep->cdid, cep->name,
+ cep->isdncontrollerused, cep->isdnchannelused,
+ bdrivername(cep->usrdevicename), cep->usrdeviceunit);
+
+ if(cep->budget_calltype)
+ {
+ if(cep->budget_calltype == BUDGET_TYPE_CBACK)
+ {
+ cep->budget_callback_done++;
+ cep->budget_callbackncalls_cnt--;
+ DBGL(DL_BDGT, (log(LL_DBG, "%s: new cback-budget = %d",
+ cep->name, cep->budget_callbackncalls_cnt)));
+ if(cep->budget_callbacks_file != NULL)
+ upd_callstat_file(cep->budget_callbacks_file, cep->budget_callbacksfile_rotate);
+ }
+ else if(cep->budget_calltype == BUDGET_TYPE_COUT)
+ {
+ cep->budget_callout_done++;
+ cep->budget_calloutncalls_cnt--;
+ DBGL(DL_BDGT, (log(LL_DBG, "%s: new cout-budget = %d",
+ cep->name, cep->budget_calloutncalls_cnt)));
+ if(cep->budget_callouts_file != NULL)
+ upd_callstat_file(cep->budget_callouts_file, cep->budget_calloutsfile_rotate);
+ }
+ cep->budget_calltype = 0;
+ }
+ }
+ else
+ {
+ log(LL_CHD, "%05d %s incoming call active (ctl %d, ch %d, %s%d)",
+ cep->cdid, cep->name,
+ cep->isdncontrollerused, cep->isdnchannelused,
+ bdrivername(cep->usrdevicename), cep->usrdeviceunit);
+ }
+
+#ifdef USE_CURSES
+ if(do_fullscreen)
+ display_connect(cep);
+#endif
+#ifdef I4B_EXTERNAL_MONITOR
+ if(do_monitor && accepted)
+ monitor_evnt_connect(cep);
+#endif
+
+ if(isdntime && (mp->datetime[0] != '\0'))
+ {
+ log(LL_DMN, "date/time from exchange = %s", mp->datetime);
+ }
+
+ next_state(cep, EV_MCAI);
+}
+
+/*---------------------------------------------------------------------------*
+ * handle incoming PROCEEDING_IND message
+ *---------------------------------------------------------------------------*/
+void
+msg_proceeding_ind(msg_proceeding_ind_t *mp)
+{
+ cfg_entry_t *cep;
+
+ if((cep = get_cep_by_cdid(mp->header.cdid)) == NULL)
+ {
+ log(LL_WRN, "msg_proceeding_ind: cdid not found!");
+ return;
+ }
+
+ cep->isdncontrollerused = mp->controller;
+ cep->isdnchannelused = mp->channel;
+
+ /* set the B-channels active */
+
+ if((set_channel_busy(cep->isdncontrollerused, cep->isdnchannelused)) == ERROR)
+ log(LL_ERR, "msg_proceeding_ind: set_channel_busy failed!");
+
+ log(LL_CHD, "%05d %s outgoing call proceeding (ctl %d, ch %d)",
+ cep->cdid, cep->name,
+ cep->isdncontrollerused, cep->isdnchannelused);
+}
+
+/*---------------------------------------------------------------------------*
+ * handle incoming ALERT_IND message
+ *---------------------------------------------------------------------------*/
+void
+msg_alert_ind(msg_alert_ind_t *mp)
+{
+ cfg_entry_t *cep;
+
+ if((cep = get_cep_by_cdid(mp->header.cdid)) == NULL)
+ {
+ log(LL_WRN, "msg_alert_ind: cdid not found!");
+ return;
+ }
+#ifdef NOTDEF
+ log(LL_CHD, "%05d %s incoming alert", cep->cdid, cep->name);
+#endif
+}
+
+/*---------------------------------------------------------------------------*
+ * handle incoming L12STAT_IND message
+ *---------------------------------------------------------------------------*/
+void
+msg_l12stat_ind(msg_l12stat_ind_t *ml)
+{
+ if((ml->controller < 0) || (ml->controller >= ncontroller))
+ {
+ log(LL_ERR, "msg_l12stat_ind: invalid controller number [%d]!", ml->controller);
+ return;
+ }
+
+#ifdef USE_CURSES
+ if(do_fullscreen)
+ display_l12stat(ml->controller, ml->layer, ml->state);
+#endif
+#ifdef I4B_EXTERNAL_MONITOR
+ if(do_monitor && accepted)
+ monitor_evnt_l12stat(ml->controller, ml->layer, ml->state);
+#endif
+
+ DBGL(DL_CNST, (log(LL_DBG, "msg_l12stat_ind: unit %d, layer %d, state %d",
+ ml->controller, ml->layer, ml->state)));
+
+ if(ml->layer == LAYER_ONE)
+ {
+ if(ml->state == LAYER_IDLE)
+ isdn_ctrl_tab[ml->controller].l2stat = ml->state;
+ isdn_ctrl_tab[ml->controller].l1stat = ml->state;
+ }
+ else if(ml->layer == LAYER_TWO)
+ {
+ if(ml->state == LAYER_ACTIVE)
+ isdn_ctrl_tab[ml->controller].l1stat = ml->state;
+ isdn_ctrl_tab[ml->controller].l2stat = ml->state;
+ }
+ else
+ {
+ log(LL_ERR, "msg_l12stat_ind: invalid layer number [%d]!", ml->layer);
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * handle incoming TEIASG_IND message
+ *---------------------------------------------------------------------------*/
+void
+msg_teiasg_ind(msg_teiasg_ind_t *mt)
+{
+ if((mt->controller < 0) || (mt->controller >= ncontroller))
+ {
+ log(LL_ERR, "msg_teiasg_ind: invalid controller number [%d]!", mt->controller);
+ return;
+ }
+
+#ifdef USE_CURSES
+ if(do_fullscreen)
+ display_tei(mt->controller, mt->tei);
+#endif
+#ifdef I4B_EXTERNAL_MONITOR
+ if(do_monitor && accepted)
+ monitor_evnt_tei(mt->controller, mt->tei);
+#endif
+
+ DBGL(DL_CNST, (log(LL_DBG, "msg_teiasg_ind: unit %d, tei = %d",
+ mt->controller, mt->tei)));
+
+ isdn_ctrl_tab[mt->controller].tei = mt->tei;
+}
+
+/*---------------------------------------------------------------------------*
+ * handle incoming PDEACT_IND message
+ *---------------------------------------------------------------------------*/
+void
+msg_pdeact_ind(msg_pdeact_ind_t *md)
+{
+ int i;
+ int ctrl = md->controller;
+ cfg_entry_t *cep;
+
+#ifdef USE_CURSES
+ if(do_fullscreen)
+ {
+ display_l12stat(ctrl, LAYER_ONE, LAYER_IDLE);
+ display_l12stat(ctrl, LAYER_TWO, LAYER_IDLE);
+ display_tei(ctrl, -1);
+ }
+#endif
+#ifdef I4B_EXTERNAL_MONITOR
+ if(do_monitor && accepted)
+ {
+ monitor_evnt_l12stat(ctrl, LAYER_ONE, LAYER_IDLE);
+ monitor_evnt_l12stat(ctrl, LAYER_TWO, LAYER_IDLE);
+ monitor_evnt_tei(ctrl, -1);
+ }
+#endif
+
+ DBGL(DL_CNST, (log(LL_DBG, "msg_pdeact_ind: unit %d, persistent deactivation", ctrl)));
+
+ isdn_ctrl_tab[ctrl].l1stat = LAYER_IDLE;
+ isdn_ctrl_tab[ctrl].l2stat = LAYER_IDLE;
+ isdn_ctrl_tab[ctrl].tei = -1;
+
+ for(i=0; i < nentries; i++)
+ {
+ if((cfg_entry_tab[i].cdid != CDID_UNUSED) &&
+ (cfg_entry_tab[i].isdncontrollerused == ctrl))
+ {
+ cep = &cfg_entry_tab[i];
+
+ if(cep->cdid == CDID_RESERVED)
+ {
+ cep->state = ST_IDLE;
+ cep->cdid = CDID_UNUSED;
+ continue;
+ }
+
+ cep->cdid = CDID_UNUSED;
+
+ cep->last_release_time = time(NULL);
+
+ SET_CAUSE_TYPE(cep->disc_cause, CAUSET_I4B);
+ SET_CAUSE_VAL(cep->disc_cause, CAUSE_I4B_L1ERROR);
+
+ if(cep->direction == DIR_OUT)
+ {
+ log(LL_CHD, "%05d %s outgoing call disconnected (local)",
+ cep->cdid, cep->name);
+ }
+ else
+ {
+ log(LL_CHD, "%05d %s incoming call disconnected (local)",
+ cep->cdid, cep->name);
+ }
+
+ log(LL_CHD, "%05d %s cause %s",
+ cep->cdid, cep->name, print_i4b_cause(cep->disc_cause));
+
+#ifdef USE_CURSES
+ if(do_fullscreen && (cep->connect_time > 0))
+ display_disconnect(cep);
+#endif
+#ifdef I4B_EXTERNAL_MONITOR
+ if(do_monitor && accepted)
+ monitor_evnt_disconnect(cep);
+#endif
+
+ if(cep->disconnectprog)
+ exec_connect_prog(cep, cep->disconnectprog, 1);
+
+ if(cep->connect_time > 0)
+ {
+ if(cep->direction == DIR_OUT)
+ {
+ log(LL_CHD, "%05d %s charging: %d units, %d seconds",
+ cep->cdid, cep->name, cep->charge,
+ (int)difftime(time(NULL), cep->connect_time));
+ }
+ else
+ {
+ log(LL_CHD, "%05d %s connected %d seconds",
+ cep->cdid, cep->name,
+ (int)difftime(time(NULL), cep->connect_time));
+ }
+
+ if((cep->inbytes != INVALID) && (cep->outbytes != INVALID))
+ {
+ if((cep->ioutbytes != cep->outbytes) ||
+ (cep->iinbytes != cep->inbytes))
+ {
+ log(LL_CHD, "%05d %s accounting: in %d, out %d (in %d, out %d)",
+ cep->cdid, cep->name,
+ cep->inbytes, cep->outbytes,
+ cep->iinbytes, cep->ioutbytes);
+ }
+ else
+ {
+ log(LL_CHD, "%05d %s accounting: in %d, out %d",
+ cep->cdid, cep->name,
+ cep->inbytes, cep->outbytes);
+ }
+ }
+ }
+
+ if(useacctfile && (cep->connect_time > 0))
+ {
+ int con_secs;
+ char logdatetime[41];
+ struct tm *tp;
+
+ con_secs = difftime(time(NULL), cep->connect_time);
+
+ tp = localtime(&cep->connect_time);
+
+ strftime(logdatetime,40,I4B_TIME_FORMAT,tp);
+
+ if(cep->inbytes != INVALID && cep->outbytes != INVALID)
+ {
+ fprintf(acctfp, "%s - %s %s %d (%d) (%d/%d)\n",
+ logdatetime, getlogdatetime(),
+ cep->name, cep->charge, con_secs,
+ cep->inbytes, cep->outbytes);
+ }
+ else
+ {
+ fprintf(acctfp, "%s - %s %s %d (%d)\n",
+ logdatetime, getlogdatetime(),
+ cep->name, cep->charge, con_secs);
+ }
+ }
+
+ /* set the B-channel inactive */
+
+ if((set_channel_idle(cep->isdncontrollerused, cep->isdnchannelused)) == ERROR)
+ log(LL_ERR, "msg_pdeact_ind: set_channel_idle failed!");
+
+ incr_free_channels(cep->isdncontrollerused);
+
+ cep->connect_time = 0;
+
+ cep->state = ST_IDLE;
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * handle incoming NEGCOMP_IND message
+ *---------------------------------------------------------------------------*/
+void
+msg_negcomplete_ind(msg_negcomplete_ind_t *mp)
+{
+ cfg_entry_t *cep;
+
+ if((cep = get_cep_by_cdid(mp->header.cdid)) == NULL)
+ {
+ log(LL_WRN, "msg_negcomp_ind: cdid not found");
+ return;
+ }
+
+ if(cep->connectprog)
+ exec_connect_prog(cep, cep->connectprog, 0);
+}
+
+/*---------------------------------------------------------------------------*
+ * handle incoming IFSTATE_CHANGED indication
+ *---------------------------------------------------------------------------*/
+void
+msg_ifstatechg_ind(msg_ifstatechg_ind_t *mp)
+{
+ cfg_entry_t *cep;
+ char *device;
+
+ if((cep = get_cep_by_cdid(mp->header.cdid)) == NULL)
+ {
+ log(LL_WRN, "msg_negcomp_ind: cdid not found");
+ return;
+ }
+
+ device = bdrivername(cep->usrdevicename);
+ log(LL_DBG, "%s%d: switched to state %d", device, cep->usrdeviceunit, mp->state);
+}
+
+/*---------------------------------------------------------------------------*
+ * handle incoming DISCONNECT_IND message
+ *---------------------------------------------------------------------------*/
+void
+msg_disconnect_ind(msg_disconnect_ind_t *mp)
+{
+ cfg_entry_t *cep;
+
+ if((cep = get_cep_by_cdid(mp->header.cdid)) == NULL)
+ {
+ log(LL_WRN, "msg_disconnect_ind: cdid not found");
+ return;
+ }
+
+ /* is this an aborted out-call prematurely called back? */
+ if (cep->saved_call.cdid == mp->header.cdid)
+ {
+ DBGL(DL_CNST, (log(LL_DBG, "aborted outcall %05d disconnected",
+ mp->header.cdid)));
+ cep->saved_call.cdid = CDID_UNUSED;
+
+ set_channel_idle(cep->saved_call.controller, cep->saved_call.channel);
+
+ incr_free_channels(cep->saved_call.controller);
+ return;
+ }
+
+ cep->last_release_time = time(NULL);
+ cep->disc_cause = mp->cause;
+
+ if(cep->direction == DIR_OUT)
+ {
+ log(LL_CHD, "%05d %s outgoing call disconnected %s",
+ cep->cdid, cep->name,
+ cep->local_disconnect == DISCON_LOC ?
+ "(local)" : "(remote)");
+ }
+ else
+ {
+ log(LL_CHD, "%05d %s incoming call disconnected %s",
+ cep->cdid, cep->name,
+ cep->local_disconnect == DISCON_LOC ?
+ "(local)" : "(remote)");
+ }
+
+ log(LL_CHD, "%05d %s cause %s",
+ cep->cdid, cep->name, print_i4b_cause(mp->cause));
+
+#ifdef USE_CURSES
+ if(do_fullscreen && (cep->connect_time > 0))
+ display_disconnect(cep);
+#endif
+#ifdef I4B_EXTERNAL_MONITOR
+ if(do_monitor && accepted)
+ monitor_evnt_disconnect(cep);
+#endif
+
+ if(cep->disconnectprog)
+ exec_connect_prog(cep, cep->disconnectprog, 1);
+
+ if(cep->connect_time > 0)
+ {
+ if(cep->direction == DIR_OUT)
+ {
+ log(LL_CHD, "%05d %s charging: %d units, %d seconds",
+ cep->cdid, cep->name, cep->charge,
+ (int)difftime(time(NULL), cep->connect_time));
+ }
+ else
+ {
+ log(LL_CHD, "%05d %s connected %d seconds",
+ cep->cdid, cep->name,
+ (int)difftime(time(NULL), cep->connect_time));
+ }
+
+ if((cep->inbytes != INVALID) && (cep->outbytes != INVALID))
+ {
+ if((cep->ioutbytes != cep->outbytes) ||
+ (cep->iinbytes != cep->inbytes))
+ {
+ log(LL_CHD, "%05d %s accounting: in %d, out %d (in %d, out %d)",
+ cep->cdid, cep->name,
+ cep->inbytes, cep->outbytes,
+ cep->iinbytes, cep->ioutbytes);
+ }
+ else
+ {
+ log(LL_CHD, "%05d %s accounting: in %d, out %d",
+ cep->cdid, cep->name,
+ cep->inbytes, cep->outbytes);
+ }
+ }
+ }
+
+ if(useacctfile && (cep->connect_time > 0))
+ {
+ int con_secs;
+ char logdatetime[41];
+ struct tm *tp;
+
+ con_secs = difftime(time(NULL), cep->connect_time);
+
+ tp = localtime(&cep->connect_time);
+
+ strftime(logdatetime,40,I4B_TIME_FORMAT,tp);
+
+ if(cep->inbytes != INVALID && cep->outbytes != INVALID)
+ {
+ fprintf(acctfp, "%s - %s %s %d (%d) (%d/%d)\n",
+ logdatetime, getlogdatetime(),
+ cep->name, cep->charge, con_secs,
+ cep->inbytes, cep->outbytes);
+ }
+ else
+ {
+ fprintf(acctfp, "%s - %s %s %d (%d)\n",
+ logdatetime, getlogdatetime(),
+ cep->name, cep->charge, con_secs);
+ }
+ }
+
+ /* set the B-channel inactive */
+
+ set_channel_idle(cep->isdncontrollerused, cep->isdnchannelused);
+
+ incr_free_channels(cep->isdncontrollerused);
+
+ cep->connect_time = 0;
+ cep->cdid = CDID_UNUSED;
+
+ next_state(cep, EV_MDI);
+}
+
+/*---------------------------------------------------------------------------*
+ * handle incoming DIALOUT message
+ *---------------------------------------------------------------------------*/
+void
+msg_dialout(msg_dialout_ind_t *mp)
+{
+ cfg_entry_t *cep;
+
+ DBGL(DL_DRVR, (log(LL_DBG, "msg_dialout: dial req from %s, unit %d", bdrivername(mp->driver), mp->driver_unit)));
+
+ if((cep = find_by_device_for_dialout(mp->driver, mp->driver_unit)) == NULL)
+ {
+ DBGL(DL_DRVR, (log(LL_DBG, "msg_dialout: config entry reserved or no match")));
+ return;
+ }
+
+ if(cep->inout == DIR_INONLY)
+ {
+ dialresponse(cep, DSTAT_INONLY);
+ return;
+ }
+
+ if(cep->budget_calloutperiod && cep->budget_calloutncalls)
+ {
+ cep->budget_calltype = 0;
+ cep->budget_callout_req++;
+
+ if(cep->budget_calloutncalls_cnt == 0)
+ {
+ log(LL_CHD, "%05d %s no budget for calling out", 0, cep->name);
+ cep->budget_callout_rej++;
+ dialresponse(cep, DSTAT_TFAIL);
+ return;
+ }
+ else
+ {
+ cep->budget_calltype = BUDGET_TYPE_COUT;
+ }
+ }
+
+ if((cep->cdid = get_cdid()) == 0)
+ {
+ DBGL(DL_DRVR, (log(LL_DBG, "msg_dialout: get_cdid() returned 0!")));
+ return;
+ }
+
+ cep->charge = 0;
+ cep->last_charge = 0;
+ cep->hangup = 0;
+
+ next_state(cep, EV_MDO);
+}
+
+/*---------------------------------------------------------------------------*
+ * handle incoming DIALOUTNUMBER message
+ *---------------------------------------------------------------------------*/
+void
+msg_dialoutnumber(msg_dialoutnumber_ind_t *mp)
+{
+ cfg_entry_t *cep;
+
+ DBGL(DL_DRVR, (log(LL_DBG, "msg_dialoutnumber: dial req from %s, unit %d", bdrivername(mp->driver), mp->driver_unit)));
+
+ if((cep = find_by_device_for_dialoutnumber(mp)) == NULL)
+ {
+ DBGL(DL_DRVR, (log(LL_DBG, "msg_dialoutnumber: config entry reserved or no match")));
+ return;
+ }
+
+ if(cep->inout == DIR_INONLY)
+ {
+ dialresponse(cep, DSTAT_INONLY);
+ return;
+ }
+
+ if(cep->budget_calloutperiod && cep->budget_calloutncalls)
+ {
+ cep->budget_calltype = 0;
+ cep->budget_callout_req++;
+
+ if(cep->budget_calloutncalls_cnt == 0)
+ {
+ log(LL_CHD, "%05d %s no budget for calling out", 0, cep->name);
+ cep->budget_callout_rej++;
+ dialresponse(cep, DSTAT_TFAIL);
+ return;
+ }
+ else
+ {
+ cep->budget_calltype = BUDGET_TYPE_COUT;
+ }
+ }
+
+ if((cep->cdid = get_cdid()) == 0)
+ {
+ DBGL(DL_DRVR, (log(LL_DBG, "msg_dialoutnumber: get_cdid() returned 0!")));
+ return;
+ }
+
+ cep->keypad[0] = '\0';
+ cep->charge = 0;
+ cep->last_charge = 0;
+ cep->hangup = 0;
+
+ next_state(cep, EV_MDO);
+}
+
+/*---------------------------------------------------------------------------*
+ * handle incoming KEYPAD message
+ *---------------------------------------------------------------------------*/
+void
+msg_keypad(msg_keypad_ind_t *mp)
+{
+ cfg_entry_t *cep;
+
+ DBGL(DL_DRVR, (log(LL_DBG, "msg_keypad: dial req from %s, unit %d", bdrivername(mp->driver), mp->driver_unit)));
+
+ if((cep = find_by_device_for_keypad(mp->driver, mp->driver_unit, mp->cmdlen, mp->cmd)) == NULL)
+ {
+ DBGL(DL_DRVR, (log(LL_DBG, "msg_keypad: config entry reserved or no match")));
+ return;
+ }
+
+ if(cep->inout == DIR_INONLY)
+ {
+ dialresponse(cep, DSTAT_INONLY);
+ return;
+ }
+
+ if(cep->budget_calloutperiod && cep->budget_calloutncalls)
+ {
+ cep->budget_calltype = 0;
+ cep->budget_callout_req++;
+
+ if(cep->budget_calloutncalls_cnt == 0)
+ {
+ log(LL_CHD, "%05d %s no budget for calling out", 0, cep->name);
+ cep->budget_callout_rej++;
+ dialresponse(cep, DSTAT_TFAIL);
+ return;
+ }
+ else
+ {
+ cep->budget_calltype = BUDGET_TYPE_COUT;
+ }
+ }
+
+ if((cep->cdid = get_cdid()) == 0)
+ {
+ DBGL(DL_DRVR, (log(LL_DBG, "msg_keypad: get_cdid() returned 0!")));
+ return;
+ }
+
+ cep->charge = 0;
+ cep->last_charge = 0;
+ cep->hangup = 0;
+
+ next_state(cep, EV_MDO);
+}
+
+/*---------------------------------------------------------------------------*
+ * handle incoming DRVRDISC_REQ message
+ *---------------------------------------------------------------------------*/
+void
+msg_drvrdisc_req(msg_drvrdisc_req_t *mp)
+{
+ cfg_entry_t *cep;
+
+ DBGL(DL_DRVR, (log(LL_DBG, "msg_drvrdisc_req: req from %s, unit %d", bdrivername(mp->driver), mp->driver_unit)));
+
+ if((cep = get_cep_by_driver(mp->driver, mp->driver_unit)) == NULL)
+ {
+ DBGL(DL_DRVR, (log(LL_DBG, "msg_drvrdisc_req: config entry not found")));
+ return;
+ }
+ next_state(cep, EV_DRQ);
+}
+
+/*---------------------------------------------------------------------------*
+ * handle incoming ACCOUNTING message
+ *---------------------------------------------------------------------------*/
+void
+msg_accounting(msg_accounting_ind_t *mp)
+{
+ cfg_entry_t *cep;
+
+ if((cep = find_active_entry_by_driver(mp->driver, mp->driver_unit)) == NULL)
+ {
+ log(LL_WRN, "msg_accounting: no config entry found!");
+ return;
+ }
+
+ cep->inbytes = mp->inbytes;
+ cep->iinbytes = mp->iinbytes;
+ cep->outbytes = mp->outbytes;
+ cep->ioutbytes = mp->ioutbytes;
+ cep->inbps = mp->inbps;
+ cep->outbps = mp->outbps;
+
+ if(mp->accttype == ACCT_DURING)
+ {
+#ifdef USE_CURSES
+ if(do_fullscreen)
+ display_acct(cep);
+#endif
+#ifdef I4B_EXTERNAL_MONITOR
+ if(do_monitor && accepted)
+ monitor_evnt_acct(cep);
+#endif
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * handle incoming CHARGING message
+ *---------------------------------------------------------------------------*/
+void
+msg_charging_ind(msg_charging_ind_t *mp)
+{
+ static char *cttab[] = {
+ "invalid",
+ "AOCD",
+ "AOCE",
+ "estimated" };
+
+ cfg_entry_t *cep;
+
+ if((cep = get_cep_by_cdid(mp->header.cdid)) == NULL)
+ {
+ log(LL_WRN, "msg_charging_ind: cdid not found");
+ return;
+ }
+
+ if(mp->units_type < CHARGE_INVALID || mp->units_type > CHARGE_CALC)
+ {
+ log(LL_ERR, "msg_charging: units_type %d out of range!", mp->units_type);
+ error_exit(1, "msg_charging: units_type %d out of range!", mp->units_type);
+ }
+
+ DBGL(DL_DRVR, (log(LL_DBG, "msg_charging: %d unit(s) (%s)",
+ mp->units, cttab[mp->units_type])));
+
+ cep->charge = mp->units;
+
+ switch(mp->units_type)
+ {
+ case CHARGE_AOCD:
+ if((cep->unitlengthsrc == ULSRC_DYN) &&
+ (cep->charge != cep->last_charge))
+ {
+ cep->last_charge = cep->charge;
+ handle_charge(cep);
+ }
+ break;
+
+ case CHARGE_CALC:
+#ifdef USE_CURSES
+ if(do_fullscreen)
+ display_ccharge(cep, mp->units);
+#endif
+#ifdef I4B_EXTERNAL_MONITOR
+ if(do_monitor && accepted)
+ monitor_evnt_charge(cep, mp->units, 1);
+#endif
+ break;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * handle incoming IDLE_TIMEOUT_IND message
+ *---------------------------------------------------------------------------*/
+void
+msg_idle_timeout_ind(msg_idle_timeout_ind_t *mp)
+{
+ cfg_entry_t *cep;
+
+ if((cep = get_cep_by_cdid(mp->header.cdid)) == NULL)
+ {
+ log(LL_WRN, "msg_idle_timeout_ind: cdid not found!");
+ return;
+ }
+
+ cep->local_disconnect = DISCON_LOC;
+
+ DBGL(DL_DRVR, (log(LL_DBG, "msg_idle_timeout_ind: idletimeout, kernel sent disconnect!")));
+
+ check_and_kill(cep);
+}
+
+/*---------------------------------------------------------------------------*
+ * handle incoming MSG_PACKET_IND message
+ *---------------------------------------------------------------------------*/
+static char *
+strapp(char *buf, const char *txt)
+{
+ while(*txt)
+ *buf++ = *txt++;
+ *buf = '\0';
+ return buf;
+}
+
+/*---------------------------------------------------------------------------*
+ * handle incoming MSG_PACKET_IND message
+ *---------------------------------------------------------------------------*/
+static char *
+ipapp(char *buf, unsigned long a )
+{
+ unsigned long ma = ntohl( a );
+
+ buf += sprintf(buf, "%lu.%lu.%lu.%lu",
+ (ma>>24)&0xFF,
+ (ma>>16)&0xFF,
+ (ma>>8)&0xFF,
+ (ma)&0xFF);
+ return buf;
+}
+
+/*---------------------------------------------------------------------------*
+ * handle incoming MSG_PACKET_IND message
+ *---------------------------------------------------------------------------*/
+void
+msg_packet_ind(msg_packet_ind_t *mp)
+{
+ cfg_entry_t *cep;
+ struct ip *ip;
+ u_char *proto_hdr;
+ char tmp[80];
+ char *cptr = tmp;
+ char *name = "???";
+ int i;
+
+ for(i=0; i < nentries; i++)
+ {
+ cep = &cfg_entry_tab[i]; /* ptr to config entry */
+
+ if(cep->usrdevicename == mp->driver &&
+ cep->usrdeviceunit == mp->driver_unit)
+ {
+ name = cep->name;
+ break;
+ }
+ }
+
+ ip = (struct ip*)mp->pktdata;
+ proto_hdr = mp->pktdata + ((ip->ip_hl)<<2);
+
+ if( ip->ip_p == IPPROTO_TCP )
+ {
+ struct tcphdr* tcp = (struct tcphdr*)proto_hdr;
+
+ cptr = strapp( cptr, "TCP " );
+ cptr = ipapp( cptr, ip->ip_src.s_addr );
+ cptr += sprintf( cptr, ":%u -> ", ntohs( tcp->th_sport ) );
+ cptr = ipapp( cptr, ip->ip_dst.s_addr );
+ cptr += sprintf( cptr, ":%u", ntohs( tcp->th_dport ) );
+
+ if(tcp->th_flags & TH_FIN) cptr = strapp( cptr, " FIN" );
+ if(tcp->th_flags & TH_SYN) cptr = strapp( cptr, " SYN" );
+ if(tcp->th_flags & TH_RST) cptr = strapp( cptr, " RST" );
+ if(tcp->th_flags & TH_PUSH) cptr = strapp( cptr, " PUSH" );
+ if(tcp->th_flags & TH_ACK) cptr = strapp( cptr, " ACK" );
+ if(tcp->th_flags & TH_URG) cptr = strapp( cptr, " URG" );
+ }
+ else if( ip->ip_p == IPPROTO_UDP )
+ {
+ struct udphdr* udp = (struct udphdr*)proto_hdr;
+
+ cptr = strapp( cptr, "UDP " );
+ cptr = ipapp( cptr, ip->ip_src.s_addr );
+ cptr += sprintf( cptr, ":%u -> ", ntohs( udp->uh_sport ) );
+ cptr = ipapp( cptr, ip->ip_dst.s_addr );
+ cptr += sprintf( cptr, ":%u", ntohs( udp->uh_dport ) );
+ }
+ else if( ip->ip_p == IPPROTO_ICMP )
+ {
+ struct icmp* icmp = (struct icmp*)proto_hdr;
+
+ cptr += sprintf( cptr, "ICMP:%u.%u", icmp->icmp_type, icmp->icmp_code);
+ cptr = ipapp( cptr, ip->ip_src.s_addr );
+ cptr = strapp( cptr, " -> " );
+ cptr = ipapp( cptr, ip->ip_dst.s_addr );
+ }
+ else
+ {
+ cptr += sprintf( cptr, "PROTO=%u ", ip->ip_p);
+ cptr = ipapp( cptr, ip->ip_src.s_addr);
+ cptr = strapp( cptr, " -> " );
+ cptr = ipapp( cptr, ip->ip_dst.s_addr);
+ }
+
+ log(LL_PKT, "%s %s %u %s",
+ name, mp->direction ? "send" : "recv",
+ ntohs( ip->ip_len ), tmp );
+}
+
+/*---------------------------------------------------------------------------*
+ * get a cdid from kernel
+ *---------------------------------------------------------------------------*/
+int
+get_cdid(void)
+{
+ msg_cdid_req_t mcr;
+
+ mcr.cdid = 0;
+
+ if((ioctl(isdnfd, I4B_CDID_REQ, &mcr)) < 0)
+ {
+ log(LL_ERR, "get_cdid: ioctl I4B_CDID_REQ failed: %s", strerror(errno));
+ error_exit(1, "get_cdid: ioctl I4B_CDID_REQ failed: %s", strerror(errno));
+ }
+
+ return(mcr.cdid);
+}
+
+/*---------------------------------------------------------------------------*
+ * send message "connect request" to kernel
+ *---------------------------------------------------------------------------*/
+int
+sendm_connect_req(cfg_entry_t *cep)
+{
+ msg_connect_req_t mcr;
+ int ret;
+
+ cep->local_disconnect = DISCON_REM;
+
+ cep->unitlength = get_current_rate(cep, 1);
+
+ mcr.cdid = cep->cdid;
+
+ mcr.controller = cep->isdncontrollerused;
+ mcr.channel = cep->isdnchannelused;
+ mcr.txdelay = cep->isdntxdelout;
+
+ mcr.bprot = cep->b1protocol;
+
+ mcr.driver = cep->usrdevicename;
+ mcr.driver_unit = cep->usrdeviceunit;
+
+ /* setup the shorthold data */
+ mcr.shorthold_data.shorthold_algorithm = cep->shorthold_algorithm;
+ mcr.shorthold_data.unitlen_time = cep->unitlength;
+ mcr.shorthold_data.idle_time = cep->idle_time_out;
+ mcr.shorthold_data.earlyhup_time = cep->earlyhangup;
+
+ if(cep->unitlengthsrc == ULSRC_DYN)
+ mcr.unitlen_method = ULEN_METHOD_DYNAMIC;
+ else
+ mcr.unitlen_method = ULEN_METHOD_STATIC;
+
+ strcpy(mcr.dst_telno, cep->remote_phone_dialout.number);
+ if(cep->usesubaddr)
+ strcpy(mcr.dst_subaddr, cep->remote_phone_dialout.subaddr);
+ strcpy(mcr.src_telno, cep->local_phone_dialout.number);
+ if(cep->usesubaddr)
+ strcpy(mcr.src_subaddr, cep->local_phone_dialout.subaddr);
+ strcpy(mcr.keypad, cep->keypad);
+
+ cep->last_dial_time = time(NULL);
+ cep->direction = DIR_OUT;
+
+ DBGL(DL_CNST, (log(LL_DBG, "sendm_connect_req: ctrl = %d, chan = %d", cep->isdncontrollerused, cep->isdnchannelused)));
+
+ if((ret = ioctl(isdnfd, I4B_CONNECT_REQ, &mcr)) < 0)
+ {
+ log(LL_ERR, "sendm_connect_req: ioctl I4B_CONNECT_REQ failed: %s", strerror(errno));
+ error_exit(1, "sendm_connect_req: ioctl I4B_CONNECT_REQ failed: %s", strerror(errno));
+ }
+
+ decr_free_channels(cep->isdncontrollerused);
+
+ log(LL_CHD, "%05d %s dialing out from %s to %s",
+ cep->cdid,
+ cep->name,
+ aliasing ? get_alias(cep->local_phone_dialout.number) : cep->local_phone_dialout.number,
+ aliasing ? get_alias(cep->remote_phone_dialout.number) : cep->remote_phone_dialout.number);
+
+ return(ret);
+}
+
+/*---------------------------------------------------------------------------*
+ * send message "connect response" to kernel
+ *---------------------------------------------------------------------------*/
+int
+sendm_connect_resp(cfg_entry_t *cep, int cdid, int response, cause_t cause)
+{
+ msg_connect_resp_t mcr;
+ int ret;
+
+ mcr.cdid = cdid;
+
+ mcr.response = response;
+
+ if(response == SETUP_RESP_REJECT)
+ {
+ mcr.cause = cause;
+ DBGL(DL_DRVR, (log(LL_DBG, "sendm_connect_resp: reject, cause=0x%x", cause)));
+ }
+ else if(response == SETUP_RESP_ACCEPT)
+ {
+ cep->direction = DIR_IN;
+
+ mcr.txdelay = cep->isdntxdelin;
+
+ mcr.bprot = cep->b1protocol;
+
+ mcr.driver = cep->usrdevicename;
+ mcr.driver_unit = cep->usrdeviceunit;
+
+ mcr.max_idle_time = cep->idle_time_in;
+
+ DBGL(DL_DRVR, (log(LL_DBG, "sendm_connect_resp: accept")));
+ }
+
+ if((ret = ioctl(isdnfd, I4B_CONNECT_RESP, &mcr)) < 0)
+ {
+ log(LL_ERR, "sendm_connect_resp: ioctl I4B_CONNECT_RESP failed: %s", strerror(errno));
+ error_exit(1, "sendm_connect_resp: ioctl I4B_CONNECT_RESP failed: %s", strerror(errno));
+ }
+ return(ret);
+}
+
+/*---------------------------------------------------------------------------*
+ * send message "disconnect request" to kernel
+ *---------------------------------------------------------------------------*/
+int
+sendm_disconnect_req(cfg_entry_t *cep, cause_t cause)
+{
+ msg_discon_req_t mcr;
+ int ret = 0;
+
+ mcr.cdid = cep->cdid;
+
+ mcr.cause = cause;
+
+ cep->local_disconnect = DISCON_LOC;
+
+ if((ret = ioctl(isdnfd, I4B_DISCONNECT_REQ, &mcr)) < 0)
+ {
+ log(LL_ERR, "sendm_disconnect_req: ioctl I4B_DISCONNECT_REQ failed: %s", strerror(errno));
+ }
+ else
+ {
+ DBGL(DL_DRVR, (log(LL_DBG, "sendm_disconnect_req: sent DISCONNECT_REQ")));
+ }
+ return(ret);
+}
+
+/*---------------------------------------------------------------------------*
+ * send message "alert request" to kernel
+ *---------------------------------------------------------------------------*/
+int
+sendm_alert_req(cfg_entry_t *cep)
+{
+ msg_alert_req_t mar;
+ int ret;
+
+ mar.cdid = cep->cdid;
+
+ if((ret = ioctl(isdnfd, I4B_ALERT_REQ, &mar)) < 0)
+ {
+ log(LL_ERR, "sendm_alert_req: ioctl I4B_ALERT_REQ failed: %s", strerror(errno));
+ error_exit(1, "sendm_alert_req: ioctl I4B_ALERT_REQ failed: %s", strerror(errno));
+ }
+ else
+ {
+ DBGL(DL_DRVR, (log(LL_DBG, "sendm_alert_req: sent ALERT_REQ")));
+ }
+ return(ret);
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnd/pathnames.h b/usr.sbin/i4b/isdnd/pathnames.h
new file mode 100644
index 0000000..44f9c6d
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/pathnames.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * i4b daemon - location of files
+ * ------------------------------
+ *
+ * $Id: pathnames.h,v 1.11 2000/10/09 11:17:07 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Oct 2 22:55:28 2000]
+ *
+ *---------------------------------------------------------------------------*/
+
+#ifndef _PATHNAMES_H_
+#define _PATHNAMES_H_
+
+#define I4BDEVICE "/dev/i4b"
+
+#define ETCPATH "/etc/isdn"
+
+#define CONFIG_FILE_DEF "/etc/isdn/isdnd.rc"
+
+#define RATES_FILE_DEF "/etc/isdn/isdnd.rates"
+
+#define HOLIDAY_FILE_DEF "/etc/isdn/holidays"
+
+#define TINA_FILE_DEF "/etc/isdn/tinainitprog"
+
+#define LOG_FILE_DEF "/var/log/isdnd.log"
+
+#define ACCT_FILE_DEF "/var/log/isdnd.acct"
+
+#define PIDFILE "/var/run/isdnd.pid"
+
+#endif /* _PATHNAMES_H_ */
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnd/pcause.c b/usr.sbin/i4b/isdnd/pcause.c
new file mode 100644
index 0000000..99aa089
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/pcause.c
@@ -0,0 +1,230 @@
+/*
+ * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * printing cause values
+ * ---------------------
+ *
+ * $Id: pcause.c,v 1.13 2000/10/09 12:53:29 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:48:07 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "isdnd.h"
+
+static char *cause_i4b_tab[CAUSE_I4B_MAX+1];
+static char *cause_q850_tab[CAUSE_Q850_MAX];
+
+char *
+print_i4b_cause(cause_t code)
+{
+ static char error_message[128];
+
+ snprintf(error_message, sizeof(error_message), "%d: ", GET_CAUSE_VAL(code));
+
+ switch(GET_CAUSE_TYPE(code))
+ {
+ case CAUSET_Q850:
+ strcat(error_message, cause_q850_tab[GET_CAUSE_VAL(code)]);
+ strcat(error_message, " (Q.850)");
+ break;
+
+ case CAUSET_I4B:
+ if((GET_CAUSE_VAL(code) < CAUSE_I4B_NORMAL) ||
+ (GET_CAUSE_VAL(code) >= CAUSE_I4B_MAX))
+ {
+ SET_CAUSE_VAL(code, CAUSE_I4B_MAX);
+ }
+ strcat(error_message, cause_i4b_tab[GET_CAUSE_VAL(code)]);
+ strcat(error_message, " (I4B)");
+ break;
+
+ default:
+ strcat(error_message, "ERROR: unknown cause type!");
+ break;
+ }
+ return(error_message);
+}
+
+static char *cause_i4b_tab[CAUSE_I4B_MAX+1] = {
+ "normal call clearing",
+ "user busy",
+ "channel not available",
+ "incompatible source or destination",
+ "call rejected",
+ "destination out of order",
+ "temporary failure",
+ "layer 1 error / persistent deactivation",
+ "dialing impossible on leased line",
+ "ERROR, invalid I4B cause value!"
+};
+
+static char *cause_q850_tab[CAUSE_Q850_MAX] = {
+ "Normal D-channel shutdown",
+ "Unallocated (unassigned) number",
+ "No route to specified transit network (national use)",
+ "No route to destination",
+ "Send special information tone",
+ "Misdialled trunk prefix (national use)",
+ "Channel unacceptable",
+ "Call awarded and being delivered in an established channel",
+ "Preemption",
+ "Preemption - circuit reserved for reuse",
+
+/*10*/ "cause code 10: error, unassigned in Q.850 (03/93)",
+ "cause code 11: error, unassigned in Q.850 (03/93)",
+ "cause code 12: error, unassigned in Q.850 (03/93)",
+ "cause code 13: error, unassigned in Q.850 (03/93)",
+ "cause code 14: error, unassigned in Q.850 (03/93)",
+ "cause code 15: error, unassigned in Q.850 (03/93)",
+ "Normal call clearing",
+ "User busy",
+ "No user responding",
+ "No answer from user (user alerted)",
+
+/*20*/ "Subscriber absent",
+ "Call rejected",
+ "Number changed",
+ "cause code 23: error, unassigned in Q.850 (03/93)",
+ "cause code 24: error, unassigned in Q.850 (03/93)",
+ "cause code 25: error, unassigned in Q.850 (03/93)",
+ "Non-selected user clearing",
+ "Destination out of order",
+ "Invalid number format",
+ "Facility rejected",
+
+/*30*/ "Response to STATUS ENQUIRY",
+ "Normal, unspecified",
+ "cause code 32: error, unassigned in Q.850 (03/93)",
+ "cause code 33: error, unassigned in Q.850 (03/93)",
+ "No circuit / channel available",
+ "cause code 35: error, unassigned in Q.850 (03/93)",
+ "cause code 36: error, unassigned in Q.850 (03/93)",
+ "cause code 37: error, unassigned in Q.850 (03/93)",
+ "Network out of order",
+ "Permanent frame mode connection out of service",
+
+/*40*/ "Permanent frame mode connection operational",
+ "Temporary failure",
+ "Switching equipment congestion",
+ "Access information discarded",
+ "Requested circuit/channel not available",
+ "cause code 45: error, unassigned in Q.850 (03/93)",
+ "Precedence call blocked",
+ "Resources unavailable, unspecified",
+ "cause code 48: error, unassigned in Q.850 (03/93)",
+ "Quality of service unavailable",
+
+/*50*/ "Requested facility not subscribed",
+ "cause code 51: error, unassigned in Q.850 (03/93)",
+ "cause code 52: error, unassigned in Q.850 (03/93)",
+ "Outgoing calls barred within CUG",
+ "cause code 54: error, unassigned in Q.850 (03/93)",
+ "Incoming calls barred within CUG",
+ "cause code 56: error, unassigned in Q.850 (03/93)",
+ "Bearer capability not authorized",
+ "Bearer capability not presently available",
+ "cause code 59: error, unassigned in Q.850 (03/93)",
+
+/*60*/ "cause code 60: error, unassigned in Q.850 (03/93)",
+ "cause code 61: error, unassigned in Q.850 (03/93)",
+ "Inconsistenciy in designated outg. access info and subscriber class",
+ "Service or option not available, unspecified",
+ "cause code 64: error, unassigned in Q.850 (03/93)",
+ "Bearer capability not implemented",
+ "Channel type not implemented",
+ "cause code 67: error, unassigned in Q.850 (03/93)",
+ "cause code 68: error, unassigned in Q.850 (03/93)",
+ "Requested facility not implemented",
+
+/*70*/ "Only restricted digital information bearer capability is available",
+ "cause code 71: error, unassigned in Q.850 (03/93)",
+ "cause code 72: error, unassigned in Q.850 (03/93)",
+ "cause code 73: error, unassigned in Q.850 (03/93)",
+ "cause code 74: error, unassigned in Q.850 (03/93)",
+ "cause code 75: error, unassigned in Q.850 (03/93)",
+ "cause code 76: error, unassigned in Q.850 (03/93)",
+ "cause code 77: error, unassigned in Q.850 (03/93)",
+ "cause code 78: error, unassigned in Q.850 (03/93)",
+ "Service or option not implemented, unspecified",
+
+/*80*/ "cause code 80: error, unassigned in Q.850 (03/93)",
+ "Invalid call reference value",
+ "Identified channel does not exist",
+ "A suspended call exists, but this call identity does not",
+ "Call identity in use",
+ "No call suspended",
+ "Call having the requested call identity has been cleared",
+ "User not member of CUG",
+ "Incompatible destination",
+ "cause code 89: error, unassigned in Q.850 (03/93)",
+
+/*90*/ "Non-existent CUG",
+ "Invalid transit network selection",
+ "cause code 92: error, unassigned in Q.850 (03/93)",
+ "cause code 93: error, unassigned in Q.850 (03/93)",
+ "cause code 94: error, unassigned in Q.850 (03/93)",
+ "Invalid message, unspecified",
+ "Mandatory information element is missing",
+ "Message type non-existent or not implemented",
+ "Message not compatible with call state or message type non-existent or not implemented",
+ "Information element/parameter non-existent or not implemented",
+
+/*100*/ "Invalid information element contents",
+ "Message not compatible with call state",
+ "Recovery on timer expiry",
+ "Parameter non-existent or not implemented, passed on",
+ "cause code 104: error, unassigned in Q.850 (03/93)",
+ "cause code 105: error, unassigned in Q.850 (03/93)",
+ "cause code 106: error, unassigned in Q.850 (03/93)",
+ "cause code 107: error, unassigned in Q.850 (03/93)",
+ "cause code 108: error, unassigned in Q.850 (03/93)",
+ "cause code 109: error, unassigned in Q.850 (03/93)",
+
+/*110*/ "Message with unrecognized parameter, discarded",
+ "Protocol error, unspecified",
+ "cause code 112: error, unassigned in Q.850 (03/93)",
+ "cause code 113: error, unassigned in Q.850 (03/93)",
+ "cause code 114: error, unassigned in Q.850 (03/93)",
+ "cause code 115: error, unassigned in Q.850 (03/93)",
+ "cause code 116: error, unassigned in Q.850 (03/93)",
+ "cause code 117: error, unassigned in Q.850 (03/93)",
+ "cause code 118: error, unassigned in Q.850 (03/93)",
+ "cause code 119: error, unassigned in Q.850 (03/93)",
+
+/*120*/ "cause code 120: error, unassigned in Q.850 (03/93)",
+ "cause code 121: error, unassigned in Q.850 (03/93)",
+ "cause code 122: error, unassigned in Q.850 (03/93)",
+ "cause code 123: error, unassigned in Q.850 (03/93)",
+ "cause code 124: error, unassigned in Q.850 (03/93)",
+ "cause code 125: error, unassigned in Q.850 (03/93)",
+ "cause code 126: error, unassigned in Q.850 (03/93)",
+ "Interworking, unspecified"
+};
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnd/process.c b/usr.sbin/i4b/isdnd/process.c
new file mode 100644
index 0000000..f546043
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/process.c
@@ -0,0 +1,219 @@
+/*
+ * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * i4b daemon - process handling routines
+ * --------------------------------------
+ *
+ * $Id: process.c,v 1.8 1999/12/13 21:25:25 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:48:19 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "isdnd.h"
+
+/*---------------------------------------------------------------------------*
+ * check if another instance of us is already running
+ *---------------------------------------------------------------------------*/
+void
+check_pid(void)
+{
+ FILE *fp;
+
+ /* check if another lock-file already exists */
+
+ if((fp = fopen(PIDFILE, "r")) != NULL)
+ {
+ /* lockfile found, check */
+
+ int oldpid;
+
+ /* read pid from file */
+
+ if((fscanf(fp, "%d", &oldpid)) != 1)
+ {
+ log(LL_ERR, "ERROR, reading pid from lockfile failed, terminating!");
+ exit(1);
+ }
+
+ /* check if process got from file is still alive */
+
+ if((kill(oldpid, 0)) != 0)
+ {
+ /* process does not exist */
+
+ /* close file */
+
+ fclose(fp);
+
+ DBGL(DL_PROC, (log(LL_DBG, "removing old lock-file %s", PIDFILE)));
+
+ /* remove file */
+
+ unlink(PIDFILE);
+ }
+ else
+ {
+ /* process is still alive */
+
+ log(LL_ERR, "ERROR, another daemon is already running, pid = %d, terminating!", oldpid);
+ exit(1);
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * establish and init process lock file
+ *---------------------------------------------------------------------------*/
+void
+write_pid(void)
+{
+ FILE *fp;
+
+ /* write my pid into lock-file */
+
+ if((fp = fopen(PIDFILE, "w")) == NULL)
+ {
+ log(LL_ERR, "ERROR, can't open lockfile for writing, terminating");
+ do_exit(1);
+ }
+
+ if((fprintf(fp, "%d", (int)getpid())) == EOF)
+ {
+ log(LL_ERR, "ERROR, can't write pid to lockfile, terminating");
+ do_exit(1);
+ }
+
+ fsync(fileno(fp));
+
+ fclose(fp);
+}
+
+/*---------------------------------------------------------------------------*
+ * become a daemon
+ *---------------------------------------------------------------------------*/
+void
+daemonize(void)
+{
+ int fd;
+
+ switch (fork())
+ {
+ case -1: /* error */
+ log(LL_ERR, "ERROR, daemonize/fork: %s", strerror(errno));
+ exit(1);
+ case 0: /* child */
+ break;
+ default: /* parent */
+ exit(0);
+ }
+
+ /* new session / no control tty */
+
+ if(setsid() == -1)
+ {
+ log(LL_ERR, "ERROR, setsid returns: %s", strerror(errno));
+ exit(1);
+ }
+
+ /* go away from mounted dir */
+
+ chdir("/");
+
+ /* move i/o to another device ? */
+
+ if(do_fullscreen && do_rdev)
+ {
+ char *tp;
+
+ if((fd = open(rdev, O_RDWR, 0)) != -1)
+ {
+ if(!isatty(fd))
+ {
+ log(LL_ERR, "ERROR, device %s is not a tty!", rdev);
+ exit(1);
+ }
+ if((dup2(fd, STDIN_FILENO)) == -1)
+ {
+ log(LL_ERR, "ERROR, dup2 stdin: %s", strerror(errno));
+ exit(1);
+ }
+ if((dup2(fd, STDOUT_FILENO)) == -1)
+ {
+ log(LL_ERR, "ERROR, dup2 stdout: %s", strerror(errno));
+ exit(1);
+ }
+ if((dup2(fd, STDERR_FILENO)) == -1)
+ {
+ log(LL_ERR, "ERROR, dup2 stderr: %s", strerror(errno));
+ exit(1);
+ }
+ }
+ else
+ {
+ log(LL_ERR, "ERROR, cannot open redirected device: %s", strerror(errno));
+ exit(1);
+ }
+
+ if(fd > 2)
+ {
+ if((close(fd)) == -1)
+ {
+ log(LL_ERR, "ERROR, close in daemonize: %s", strerror(errno));
+ exit(1);
+ }
+ }
+
+ /* curses output && fork NEEDS controlling tty */
+
+ if((ioctl(STDIN_FILENO, TIOCSCTTY, (char *)NULL)) < 0)
+ {
+ log(LL_ERR, "ERROR, cannot setup tty as controlling terminal: %s", strerror(errno));
+ exit(1);
+ }
+
+ /* in case there is no environment ... */
+
+ if(((tp = getenv("TERM")) == NULL) || (*tp == '\0'))
+ {
+ if(do_ttytype == 0)
+ {
+ log(LL_ERR, "ERROR, no environment variable TERM found and -t not specified!");
+ exit(1);
+ }
+
+ if((setenv("TERM", ttype, 1)) != 0)
+ {
+ log(LL_ERR, "ERROR, setenv TERM=%s failed: %s", ttype, strerror(errno));
+ exit(1);
+ }
+ }
+ }
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnd/rates.c b/usr.sbin/i4b/isdnd/rates.c
new file mode 100644
index 0000000..29aaef4
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/rates.c
@@ -0,0 +1,509 @@
+/*
+ * Copyright (c) 1997 Gary Jennejohn. All rights reserved.
+ *
+ * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ * 4. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software and/or documentation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * i4b daemon - charging rates description file handling
+ * -----------------------------------------------------
+ *
+ * $Id: rates.c,v 1.11 2000/10/09 12:53:29 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:48:31 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+static char error[256];
+
+static int getrate(int rate_type);
+
+#ifdef PARSE_DEBUG_MAIN
+
+#include <stdio.h>
+
+#define MAIN
+
+#define ERROR (-1)
+
+extern int got_rate;
+
+int main( int argc, char **argv )
+{
+ int ret;
+ ret = readrates("/etc/isdn/isdnd.rates");
+ if(ret == ERROR)
+ fprintf(stderr, "readrates returns [%d], [%s]\n", ret, error);
+ else
+ {
+ int type = 0;
+
+ got_rate = 1;
+
+ fprintf(stderr, "readrates returns [%d]\n", ret);
+
+ for( type=0; type<4; type++ )
+ {
+ int unit = getrate( type );
+ fprintf(stderr, "getrate(%d) => %d\n", type, unit );
+ }
+ }
+
+ return(ret);
+}
+
+#endif
+
+#include "isdnd.h"
+
+/*---------------------------------------------------------------------------*
+ * parse rates file
+ *---------------------------------------------------------------------------*/
+int
+readrates(char *filename)
+{
+ char buffer[MAXPATHLEN];
+ register char *bp;
+ struct rates *rt, *ort;
+ int rateindx;
+ int indx;
+ int line = 0;
+ FILE *fp;
+ int first;
+#if DEBUG
+ int i, j;
+#endif
+
+ indx = 0;
+ rt = ort = NULL;
+
+ if((fp = fopen(filename, "r")) == NULL)
+ {
+ snprintf(error, sizeof(error), "error open %s: %s", filename, sys_errlist[errno]);
+ rate_error = error;
+ return(WARNING);
+ }
+
+ while((fgets(buffer, MAXPATHLEN, fp)) != NULL)
+ {
+ line++;
+
+/* comments */
+ if(buffer[0] == '#' || buffer[0] == ' ' ||
+ buffer[0] == '\t' || buffer[0] == '\n')
+ {
+ continue;
+ }
+
+ bp = &buffer[0];
+
+ /* rate type */
+
+ if (*bp == 'r' && *(bp+1) == 'a' && isdigit(*(bp+2)))
+ {
+ rateindx = *(bp+2) - '0';
+ bp += 3;
+
+ /* eat space delimiter */
+
+ while(isspace(*bp))
+ bp++;
+ }
+ else
+ {
+ snprintf(error, sizeof(error), "rates: invalid rate type %c%c%c in line %d", *bp, *(bp+1), *(bp+2), line);
+ goto rate_error;
+ }
+ if (rateindx >= NRATES)
+ {
+ snprintf(error, sizeof(error), "rates: invalid rate index %d in line %d", rateindx, line);
+ goto rate_error;
+ }
+
+ /* day */
+
+ if(isdigit(*bp) && *bp >= '0' && *bp <= '6')
+ {
+ indx = *bp - '0';
+
+ DBGL(DL_RATES, (log(LL_DBG, "rates: index = %d", indx)));
+ }
+ else
+ {
+ snprintf(error, sizeof(error), "rates: invalid day digit %c in line %d", *bp, line);
+ goto rate_error;
+ }
+
+ if(rates[rateindx][indx] == NULL)
+ {
+ rt = (struct rates *)malloc(sizeof (struct rates));
+ if (rt == NULL)
+ {
+ snprintf(error, sizeof(error), "rates: cannot malloc space for rate structure");
+ goto rate_error;
+ }
+ rt->next = NULL;
+ rates[rateindx][indx] = rt;
+ }
+
+ bp++;
+
+ /* eat space delimiter */
+
+ while(isspace(*bp))
+ bp++;
+
+ /* now loop to get the rates entries */
+
+ first = 1;
+
+ while(*bp && isdigit(*bp))
+ {
+ int hour = 0;
+ int min = 0;
+
+ if(first)
+ {
+ first = 0;
+ }
+ else
+ {
+ ort = rt;
+
+ rt = (struct rates *)malloc(sizeof (struct rates));
+ if (rt == NULL)
+ {
+ snprintf(error, sizeof(error), "rates: cannot malloc space2 for rate structure");
+ goto rate_error;
+ }
+ ort->next = rt;
+ rt->next = NULL;
+ }
+
+ /* start hour */
+
+ if(isdigit(*bp) && isdigit(*(bp+1)))
+ {
+ hour = atoi(bp);
+ bp += 2;
+ }
+ else
+ {
+ snprintf(error, sizeof(error), "rates: start_hr error in line %d", line);
+ goto rate_error;
+ }
+
+ /* point */
+
+ if(*bp == '.')
+ {
+ bp++;
+ }
+ else
+ {
+ snprintf(error, sizeof(error), "rates: no '.' after start_hr in line %d", line);
+ goto rate_error;
+ }
+
+ /* start minute */
+
+ if(isdigit(*bp) && isdigit(*(bp+1)))
+ {
+ min = atoi(bp);
+ bp += 2;
+ }
+ else
+ {
+ snprintf(error, sizeof(error), "rates: start_min error in line %d", line);
+ goto rate_error;
+ }
+
+ rt->start_time = hour*60 + min;
+
+ /* minus */
+
+ if(*bp == '-')
+ {
+ bp++;
+ }
+ else
+ {
+ snprintf(error, sizeof(error), "rates: no '-' after start_min in line %d", line);
+ goto rate_error;
+ }
+
+ /* end hour */
+
+ if(isdigit(*bp) && isdigit(*(bp+1)))
+ {
+ hour = atoi(bp);
+ bp += 2;
+ }
+ else
+ {
+ snprintf(error, sizeof(error), "rates: end_hr error in line %d", line);
+ goto rate_error;
+ }
+
+ /* point */
+
+ if(*bp == '.')
+ {
+ bp++;
+ }
+ else
+ {
+ snprintf(error, sizeof(error), "rates: no '.' after end_hr in line %d", line);
+ goto rate_error;
+ }
+
+ /* end minute */
+
+ if(isdigit(*bp) && isdigit(*(bp+1)))
+ {
+ min = atoi(bp);
+ bp += 2;
+ }
+ else
+ {
+ snprintf(error, sizeof(error), "rates: end_min error in line %d", line);
+ goto rate_error;
+ }
+
+ /* if hour is 0 assume it means midnight */
+ if( hour == 0 )
+ hour = 24;
+ rt->end_time = hour * 60 + min;
+
+ if( rt->end_time <= rt->start_time )
+ {
+ snprintf(error, sizeof(error), "rates: end_time must be greater then start_time %d", line);
+ goto rate_error;
+ }
+
+ /* colon */
+
+ if(*bp == ':')
+ {
+ bp++;
+ }
+ else
+ {
+ snprintf(error, sizeof(error), "rates: no ':' after end_min in line %d", line);
+ goto rate_error;
+ }
+
+ /* time */
+
+ if(isdigit(*bp))
+ {
+ rt->rate = atoi(bp);
+ while(!isspace(*bp))
+ bp++;
+ }
+ else
+ {
+ snprintf(error, sizeof(error), "rates: first rate digit error in line %d", line);
+ goto rate_error;
+ }
+
+ /* eat space delimiter */
+
+ while(isspace(*bp))
+ bp++;
+ }
+ }
+
+#if DEBUG
+ if(debug_flags & DL_RATES)
+ {
+ for (j = 0; j < NRATES; j++)
+ {
+ for (i = 0; i < NDAYS; i++)
+ {
+ if (rates [j][i] != NULL)
+ {
+ rt = rates [j][i];
+ for (; rt; rt = rt->next)
+ {
+ log(LL_DBG, "rates: index %d day %d = %d.%2.2d-%d.%2.2d:%d",
+ j, i, rt->start_time/60, rt->start_time%60,
+ rt->end_time/60,rt->end_time%60,rt->rate);
+ }
+ }
+ else
+ {
+ log(LL_DBG, "rates: NO entry for day %d !!\n", i);
+ }
+ }
+ }
+ }
+#endif
+ fclose(fp);
+ return(GOOD);
+
+rate_error:
+ fclose(fp);
+ rate_error = error;
+ return(ERROR);
+}
+
+#ifndef PARSE_DEBUG_MAIN
+
+/*---------------------------------------------------------------------------*
+ * get unit length time from configured source
+ *---------------------------------------------------------------------------*/
+int
+get_current_rate(cfg_entry_t *cep, int logit)
+{
+ int rt;
+
+ switch(cep->unitlengthsrc)
+ {
+ case ULSRC_CMDL: /* specified on commandline */
+ if(logit)
+ log(LL_CHD, "%05d %s rate %d sec/unit (cmdl)",
+ cep->cdid, cep->name, unit_length);
+ return(unit_length);
+ break;
+
+ case ULSRC_CONF: /* get it from config file */
+ if(logit)
+ log(LL_CHD, "%05d %s rate %d sec/unit (conf)",
+ cep->cdid, cep->name, cep->unitlength);
+ return(cep->unitlength);
+
+ case ULSRC_RATE: /* get it dynamic from ratesfile*/
+ if(!got_rate) /* got valid rates struct ?? */
+ {
+ if(logit)
+ log(LL_CHD, "%05d %s rate %d sec/unit (no ratefile)",
+ cep->cdid, cep->name, UNITLENGTH_DEFAULT);
+ return(UNITLENGTH_DEFAULT);
+ }
+ if((cep->ratetype >= NRATES) ||
+ (cep->ratetype == INVALID_RATE))
+ {
+ if(logit)
+ log(LL_CHD, "%05d %s rate %d sec/unit (rate out of range)",
+ cep->cdid, cep->name, UNITLENGTH_DEFAULT);
+ return(UNITLENGTH_DEFAULT);
+ }
+
+ if((rt = getrate(cep->ratetype)) != -1)
+ {
+ if(logit)
+ log(LL_CHD, "%05d %s rate %d sec/unit (rate)",
+ cep->cdid, cep->name, rt);
+ return(rt);
+ }
+
+ if(logit)
+ log(LL_CHD, "%05d %s rate %d sec/unit (ratescan fail)",
+ cep->cdid, cep->name, UNITLENGTH_DEFAULT);
+
+ return(UNITLENGTH_DEFAULT);
+ break;
+
+ case ULSRC_DYN: /* dynamically calculated from AOC */
+ if((rt = getrate(cep->ratetype)) != -1)
+ {
+ if(logit)
+ log(LL_CHD, "%05d %s rate %d sec/unit (aocd, rate)",
+ cep->cdid, cep->name, rt);
+ return(rt);
+ }
+ if(logit)
+ log(LL_CHD, "%05d %s rate %d sec/unit (aocd, default)",
+ cep->cdid, cep->name, UNITLENGTH_DEFAULT);
+
+ return(UNITLENGTH_DEFAULT);
+ break;
+
+ default:
+ if(logit)
+ log(LL_CHD, "%05d %s rate %d sec/unit (unitlen unknown)",
+ cep->cdid, cep->name, UNITLENGTH_DEFAULT);
+
+ return(UNITLENGTH_DEFAULT);
+ break;
+ }
+}
+#endif /* PARSE_DEBUG_MAIN */
+
+
+/*---------------------------------------------------------------------------*
+ * get the currently active rate
+ *---------------------------------------------------------------------------*/
+static int
+getrate(int rate_type )
+{
+ struct tm *ptr;
+ time_t now;
+ register struct rates *hd;
+ int time_now;
+
+ if((!got_rate) ||
+ (rate_type >= NRATES) ||
+ (rate_type == INVALID_RATE))
+ {
+ return -1;
+ }
+
+ time(&now); /* get current time */
+
+ ptr = localtime(&now);
+
+ time_now = ptr->tm_hour*60 + ptr->tm_min;
+
+ /* walk thru the rates for weekday until rate for current time found */
+
+ for (hd = rates[rate_type][ptr->tm_wday]; hd; hd = hd->next)
+ {
+ /* current time within window ? */
+ if((time_now >= hd->start_time ) &&
+ (time_now < hd->end_time ))
+ {
+ DBGL(DL_RATES, (log(LL_DBG, "rate=%d sec/unit (day=%d, beg=%d:%2.2d, end=%d:2.2d, current=%d:%2.2d)",
+ hd->rate,
+ ptr->tm_wday,
+ hd->start_time/60, hd->start_time%60,
+ hd->end_time/60, hd->end_time%60,
+ time_now/60, time_now%60)));
+
+ return hd->rate;
+ }
+ }
+ return -1;
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnd/rc_config.c b/usr.sbin/i4b/isdnd/rc_config.c
new file mode 100644
index 0000000..09a2c2e
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/rc_config.c
@@ -0,0 +1,1843 @@
+/*
+ * Copyright (c) 1997, 2002 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * i4b daemon - config file processing
+ * -----------------------------------
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Tue Mar 26 14:37:38 2002]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <sys/ioctl.h>
+
+#include <net/if.h>
+
+#include "isdnd.h"
+#include "y.tab.h"
+
+#include "monitor.h"
+
+extern int entrycount;
+extern int controllercount;
+extern int lineno;
+extern char *yytext;
+
+extern FILE *yyin;
+extern int yyparse();
+
+static void set_config_defaults(void);
+static void check_config(void);
+static void print_config(void);
+static void parse_valid(int entrycount, char *dt);
+
+static int nregexpr = 0;
+static int nregprog = 0;
+
+/*---------------------------------------------------------------------------*
+ * called from main to read and process config file
+ *---------------------------------------------------------------------------*/
+void
+configure(char *filename, int reread)
+{
+ extern void reset_scanner(FILE *inputfile);
+
+ set_config_defaults();
+
+ yyin = fopen(filename, "r");
+
+ if(yyin == NULL)
+ {
+ log(LL_ERR, "cannot fopen file [%s]", filename);
+ exit(1);
+ }
+
+ if(reread)
+ {
+ reset_scanner(yyin);
+ }
+
+ yyparse();
+
+ monitor_fixup_rights();
+
+ check_config(); /* validation and consistency check */
+
+ fclose(yyin);
+
+ if(do_print)
+ {
+ if(config_error_flag)
+ {
+ log(LL_ERR, "there were %d error(s) in the configuration file, terminating!", config_error_flag);
+ exit(1);
+ }
+ print_config();
+ do_exit(0);
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * yacc error routine
+ *---------------------------------------------------------------------------*/
+void
+yyerror(const char *msg)
+{
+ log(LL_ERR, "configuration error: %s at line %d, token \"%s\"", msg, lineno+1, yytext);
+ config_error_flag++;
+}
+
+/*---------------------------------------------------------------------------*
+ * fill all config entries with default values
+ *---------------------------------------------------------------------------*/
+static void
+set_config_defaults(void)
+{
+ cfg_entry_t *cep = &cfg_entry_tab[0]; /* ptr to config entry */
+ int i;
+
+ /* system section cleanup */
+
+ nregprog = nregexpr = 0;
+
+ rt_prio = RTPRIO_NOTUSED;
+
+ mailer[0] = '\0';
+ mailto[0] = '\0';
+
+ /* clean regular expression table */
+
+ for(i=0; i < MAX_RE; i++)
+ {
+ if(rarr[i].re_expr)
+ free(rarr[i].re_expr);
+ rarr[i].re_expr = NULL;
+
+ if(rarr[i].re_prog)
+ free(rarr[i].re_prog);
+ rarr[i].re_prog = NULL;
+
+ rarr[i].re_flg = 0;
+ }
+
+ strcpy(rotatesuffix, "");
+
+ /*
+ * controller table cleanup, beware: has already
+ * been setup in main, init_controller() !
+ */
+
+ for(i=0; i < ncontroller; i++)
+ {
+ isdn_ctrl_tab[i].protocol = PROTOCOL_DSS1;
+ isdn_ctrl_tab[i].firmware = NULL;
+ }
+
+ /* entry section cleanup */
+
+ for(i=0; i < CFG_ENTRY_MAX; i++, cep++)
+ {
+ bzero(cep, sizeof(cfg_entry_t));
+
+ /* ====== filled in at startup configuration, then static */
+
+ sprintf(cep->name, "ENTRY%d", i);
+
+ cep->isdncontroller = INVALID;
+ cep->isdnchannel = CHAN_ANY;
+
+ cep->usrdevicename = INVALID;
+ cep->usrdeviceunit = INVALID;
+
+ cep->remote_numbers_handling = RNH_LAST;
+
+ cep->dialin_reaction = REACT_IGNORE;
+
+ cep->b1protocol = BPROT_NONE;
+
+ cep->unitlength = UNITLENGTH_DEFAULT;
+
+ cep->earlyhangup = EARLYHANGUP_DEFAULT;
+
+ cep->ratetype = INVALID_RATE;
+
+ cep->unitlengthsrc = ULSRC_NONE;
+
+ cep->answerprog = ANSWERPROG_DEF;
+
+ cep->callbackwait = CALLBACKWAIT_MIN;
+
+ cep->calledbackwait = CALLEDBACKWAIT_MIN;
+
+ cep->dialretries = DIALRETRIES_DEF;
+
+ cep->recoverytime = RECOVERYTIME_MIN;
+
+ cep->dialouttype = DIALOUT_NORMAL;
+
+ cep->inout = DIR_INOUT;
+
+ cep->ppp_expect_auth = AUTH_UNDEF;
+
+ cep->ppp_send_auth = AUTH_UNDEF;
+
+ cep->ppp_auth_flags = AUTH_RECHALLENGE | AUTH_REQUIRED;
+
+ /* ======== filled in after start, then dynamic */
+
+ cep->cdid = CDID_UNUSED;
+
+ cep->state = ST_IDLE;
+
+ cep->aoc_valid = AOC_INVALID;
+
+ cep->usesubaddr = 0;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * internaly set values for ommitted controler sectin
+ *---------------------------------------------------------------------------*/
+void
+cfg_set_controller_default()
+{
+ controllercount = 0;
+ DBGL(DL_RCCF, (log(LL_DBG, "[defaults, no controller section] controller %d: protocol = dss1", controllercount)));
+ isdn_ctrl_tab[controllercount].protocol = PROTOCOL_DSS1;
+}
+
+#define PPP_PAP 0xc023
+#define PPP_CHAP 0xc223
+
+static void
+set_isppp_auth(int entry)
+{
+ cfg_entry_t *cep = &cfg_entry_tab[entry]; /* ptr to config entry */
+
+ struct ifreq ifr;
+ struct spppreq spr;
+ int s;
+ int doioctl = 0;
+
+ if(cep->usrdevicename != BDRV_ISPPP)
+ return;
+
+ if(cep->ppp_expect_auth == AUTH_UNDEF
+ && cep->ppp_send_auth == AUTH_UNDEF)
+ return;
+
+ if(cep->ppp_expect_auth == AUTH_NONE
+ || cep->ppp_send_auth == AUTH_NONE)
+ doioctl = 1;
+
+ if ((cep->ppp_expect_auth == AUTH_CHAP
+ || cep->ppp_expect_auth == AUTH_PAP)
+ && cep->ppp_expect_name[0] != 0
+ && cep->ppp_expect_password[0] != 0)
+ doioctl = 1;
+
+ if ((cep->ppp_send_auth == AUTH_CHAP || cep->ppp_send_auth == AUTH_PAP)
+ && cep->ppp_send_name[0] != 0
+ && cep->ppp_send_password[0] != 0)
+ doioctl = 1;
+
+ if(!doioctl)
+ return;
+
+ snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "isp%d", cep->usrdeviceunit);
+
+ /* use a random AF to create the socket */
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ log(LL_ERR, "ERROR opening control socket at line %d!", lineno);
+ config_error_flag++;
+ return;
+ }
+ spr.cmd = (int)SPPPIOGDEFS;
+ ifr.ifr_data = (caddr_t)&spr;
+
+ if (ioctl(s, SIOCGIFGENERIC, &ifr) == -1) {
+ log(LL_ERR, "ERROR fetching active PPP authentication info for %s at line %d!", ifr.ifr_name, lineno);
+ close(s);
+ config_error_flag++;
+ return;
+ }
+ if (cep->ppp_expect_auth != AUTH_UNDEF)
+ {
+ if(cep->ppp_expect_auth == AUTH_NONE)
+ {
+ spr.defs.hisauth.proto = 0;
+ }
+ else if ((cep->ppp_expect_auth == AUTH_CHAP
+ || cep->ppp_expect_auth == AUTH_PAP)
+ && cep->ppp_expect_name[0] != 0
+ && cep->ppp_expect_password[0] != 0)
+ {
+ spr.defs.hisauth.proto = cep->ppp_expect_auth == AUTH_PAP ? PPP_PAP : PPP_CHAP;
+ strncpy(spr.defs.hisauth.name, cep->ppp_expect_name, AUTHNAMELEN);
+ strncpy(spr.defs.hisauth.secret, cep->ppp_expect_password, AUTHKEYLEN);
+ }
+ }
+ if (cep->ppp_send_auth != AUTH_UNDEF)
+ {
+ if(cep->ppp_send_auth == AUTH_NONE)
+ {
+ spr.defs.myauth.proto = 0;
+ }
+ else if ((cep->ppp_send_auth == AUTH_CHAP
+ || cep->ppp_send_auth == AUTH_PAP)
+ && cep->ppp_send_name[0] != 0
+ && cep->ppp_send_password[0] != 0)
+ {
+ spr.defs.myauth.proto = cep->ppp_send_auth == AUTH_PAP ? PPP_PAP : PPP_CHAP;
+ strncpy(spr.defs.myauth.name, cep->ppp_send_name, AUTHNAMELEN);
+ strncpy(spr.defs.myauth.secret, cep->ppp_send_password, AUTHKEYLEN);
+
+ if(cep->ppp_auth_flags & AUTH_REQUIRED)
+ spr.defs.hisauth.flags &= ~AUTHFLAG_NOCALLOUT;
+ else
+ spr.defs.hisauth.flags |= AUTHFLAG_NOCALLOUT;
+
+ if(cep->ppp_auth_flags & AUTH_RECHALLENGE)
+ spr.defs.hisauth.flags &= ~AUTHFLAG_NORECHALLENGE;
+ else
+ spr.defs.hisauth.flags |= AUTHFLAG_NORECHALLENGE;
+ }
+ }
+
+ spr.cmd = (int)SPPPIOSDEFS;
+
+ if (ioctl(s, SIOCSIFGENERIC, &ifr) == -1) {
+ log(LL_ERR, "ERROR setting new PPP authentication parameters for %s at line %d!", ifr.ifr_name, lineno);
+ config_error_flag++;
+ }
+ close(s);
+}
+
+/*---------------------------------------------------------------------------*
+ * extract values from config and fill table
+ *---------------------------------------------------------------------------*/
+void
+cfg_setval(int keyword)
+{
+ int i;
+
+ switch(keyword)
+ {
+ case ACCTALL:
+ acct_all = yylval.booln;
+ DBGL(DL_RCCF, (log(LL_DBG, "system: acctall = %d", yylval.booln)));
+ break;
+
+ case ACCTFILE:
+ strcpy(acctfile, yylval.str);
+ DBGL(DL_RCCF, (log(LL_DBG, "system: acctfile = %s", yylval.str)));
+ break;
+
+ case 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 BUDGETCALLBACKPERIOD:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: budget-callbackperiod = %d", entrycount, yylval.num)));
+ cfg_entry_tab[entrycount].budget_callbackperiod = yylval.num;
+ break;
+
+ case BUDGETCALLBACKNCALLS:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: budget-callbackncalls = %d", entrycount, yylval.num)));
+ cfg_entry_tab[entrycount].budget_callbackncalls = yylval.num;
+ break;
+
+ case BUDGETCALLOUTPERIOD:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: budget-calloutperiod = %d", entrycount, yylval.num)));
+ cfg_entry_tab[entrycount].budget_calloutperiod = yylval.num;
+ break;
+
+ case BUDGETCALLOUTNCALLS:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: budget-calloutncalls = %d", entrycount, yylval.num)));
+ cfg_entry_tab[entrycount].budget_calloutncalls = yylval.num;
+ break;
+
+ case BUDGETCALLBACKSFILEROTATE:
+ cfg_entry_tab[entrycount].budget_callbacksfile_rotate = yylval.booln;
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: budget-callbacksfile-rotate = %d", entrycount, yylval.booln)));
+ break;
+
+ case BUDGETCALLBACKSFILE:
+ {
+ FILE *fp;
+ int s, l;
+ int n;
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: budget-callbacksfile = %s", entrycount, yylval.str)));
+ fp = fopen(yylval.str, "r");
+ if(fp != NULL)
+ {
+ if((fscanf(fp, "%d %d %d", (int *)&s, (int *)&l, &n)) != 3)
+ {
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: initializing budget-callbacksfile %s", entrycount, yylval.str)));
+ fclose(fp);
+ fp = fopen(yylval.str, "w");
+ if(fp != NULL)
+ {
+ fprintf(fp, "%d %d %d", (int)time(NULL), (int)time(NULL), 0);
+ fclose(fp);
+ }
+ }
+ }
+ else
+ {
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: creating budget-callbacksfile %s", entrycount, yylval.str)));
+ fp = fopen(yylval.str, "w");
+ if(fp != NULL)
+ {
+ fprintf(fp, "%d %d %d", (int)time(NULL), (int)time(NULL), 0);
+ fclose(fp);
+ }
+ }
+
+ fp = fopen(yylval.str, "r");
+ if(fp != NULL)
+ {
+ if((fscanf(fp, "%d %d %d", (int *)&s, (int *)&l, &n)) == 3)
+ {
+ if((cfg_entry_tab[entrycount].budget_callbacks_file = malloc(strlen(yylval.str)+1)) == NULL)
+ {
+ log(LL_ERR, "entry %d: budget-callbacksfile, malloc failed!", entrycount);
+ do_exit(1);
+ }
+ strcpy(cfg_entry_tab[entrycount].budget_callbacks_file, yylval.str);
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: using callbacksfile %s", entrycount, yylval.str)));
+ }
+ fclose(fp);
+ }
+ }
+ break;
+
+ case BUDGETCALLOUTSFILEROTATE:
+ cfg_entry_tab[entrycount].budget_calloutsfile_rotate = yylval.booln;
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: budget-calloutsfile-rotate = %d", entrycount, yylval.booln)));
+ break;
+
+ case BUDGETCALLOUTSFILE:
+ {
+ FILE *fp;
+ int s, l;
+ int n;
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: budget-calloutsfile = %s", entrycount, yylval.str)));
+ fp = fopen(yylval.str, "r");
+ if(fp != NULL)
+ {
+ if((fscanf(fp, "%d %d %d", (int *)&s, (int *)&l, &n)) != 3)
+ {
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: initializing budget-calloutsfile %s", entrycount, yylval.str)));
+ fclose(fp);
+ fp = fopen(yylval.str, "w");
+ if(fp != NULL)
+ {
+ fprintf(fp, "%d %d %d", (int)time(NULL), (int)time(NULL), 0);
+ fclose(fp);
+ }
+ }
+ }
+ else
+ {
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: creating budget-calloutsfile %s", entrycount, yylval.str)));
+ fp = fopen(yylval.str, "w");
+ if(fp != NULL)
+ {
+ fprintf(fp, "%d %d %d", (int)time(NULL), (int)time(NULL), 0);
+ fclose(fp);
+ }
+ }
+
+ fp = fopen(yylval.str, "r");
+ if(fp != NULL)
+ {
+ if((fscanf(fp, "%d %d %d", (int *)&s, (int *)&l, &n)) == 3)
+ {
+ if((cfg_entry_tab[entrycount].budget_callouts_file = malloc(strlen(yylval.str)+1)) == NULL)
+ {
+ log(LL_ERR, "entry %d: budget-calloutsfile, malloc failed!", entrycount);
+ do_exit(1);
+ }
+ strcpy(cfg_entry_tab[entrycount].budget_callouts_file, yylval.str);
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: using calloutsfile %s", entrycount, yylval.str)));
+ }
+ fclose(fp);
+ }
+ }
+ break;
+
+ case CALLBACKWAIT:
+ if(yylval.num < CALLBACKWAIT_MIN)
+ {
+ yylval.num = CALLBACKWAIT_MIN;
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: callbackwait < %d, min = %d", entrycount, CALLBACKWAIT_MIN, yylval.num)));
+ }
+
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: callbackwait = %d", entrycount, yylval.num)));
+ cfg_entry_tab[entrycount].callbackwait = yylval.num;
+ break;
+
+ case CALLEDBACKWAIT:
+ if(yylval.num < CALLEDBACKWAIT_MIN)
+ {
+ yylval.num = CALLEDBACKWAIT_MIN;
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: calledbackwait < %d, min = %d", entrycount, CALLEDBACKWAIT_MIN, yylval.num)));
+ }
+
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: calledbackwait = %d", entrycount, yylval.num)));
+ cfg_entry_tab[entrycount].calledbackwait = yylval.num;
+ break;
+
+ case CLONE:
+ /*
+ * clone = <entryname>
+ * Loads the entry from the named, existing one.
+ * Fields such as name and usrdeviceunit should
+ * always be specified after clone as they must be
+ * unique.
+ *
+ * NOTE: all malloc()'d fields must be dup()'d here,
+ * we can't have multiple references to same storage.
+ */
+ for (i = 0; i < entrycount; i++)
+ if (!strcmp(cfg_entry_tab[i].name, yylval.str))
+ break;
+ if (i == entrycount) {
+ log(LL_ERR, "entry %d: clone, unknown entry %s!", entrycount, yylval.str);
+ do_exit(1);
+ }
+
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: clone = %s", entrycount, yylval.str)));
+
+ memcpy(&cfg_entry_tab[entrycount], &cfg_entry_tab[i],
+ sizeof(cfg_entry_tab[0]));
+
+ if (cfg_entry_tab[entrycount].answerprog)
+ cfg_entry_tab[entrycount].answerprog = strdup(cfg_entry_tab[entrycount].answerprog);
+ if (cfg_entry_tab[entrycount].budget_callbacks_file)
+ cfg_entry_tab[entrycount].budget_callbacks_file = strdup(cfg_entry_tab[entrycount].budget_callbacks_file);
+ if (cfg_entry_tab[entrycount].budget_callouts_file)
+ cfg_entry_tab[entrycount].budget_callouts_file = strdup(cfg_entry_tab[entrycount].budget_callouts_file);
+ if (cfg_entry_tab[entrycount].connectprog)
+ cfg_entry_tab[entrycount].connectprog = strdup(cfg_entry_tab[entrycount].connectprog);
+ if (cfg_entry_tab[entrycount].disconnectprog)
+ cfg_entry_tab[entrycount].disconnectprog = strdup(cfg_entry_tab[entrycount].disconnectprog);
+ break;
+
+ case CONNECTPROG:
+ if((cfg_entry_tab[entrycount].connectprog = malloc(strlen(yylval.str)+1)) == NULL)
+ {
+ log(LL_ERR, "entry %d: connectprog, malloc failed!", entrycount);
+ do_exit(1);
+ }
+ strcpy(cfg_entry_tab[entrycount].connectprog, yylval.str);
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: connectprog = %s", entrycount, yylval.str)));
+ break;
+
+ case DIALOUTTYPE:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: dialouttype = %s", entrycount, yylval.str)));
+ if(!(strcmp(yylval.str, "normal")))
+ cfg_entry_tab[entrycount].dialouttype = DIALOUT_NORMAL;
+ else if(!(strcmp(yylval.str, "calledback")))
+ cfg_entry_tab[entrycount].dialouttype = DIALOUT_CALLEDBACK;
+ else
+ {
+ log(LL_ERR, "ERROR parsing config file: unknown parameter for keyword \"dialout-type\" at line %d!", lineno);
+ config_error_flag++;
+ }
+ break;
+
+ case DIALRETRIES:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: dialretries = %d", entrycount, yylval.num)));
+ cfg_entry_tab[entrycount].dialretries = yylval.num;
+ break;
+
+ case DIALRANDINCR:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: dialrandincr = %d", entrycount, yylval.booln)));
+ cfg_entry_tab[entrycount].dialrandincr = yylval.booln;
+ break;
+
+ case DIRECTION:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: direction = %s", entrycount, yylval.str)));
+
+ if(!(strcmp(yylval.str, "inout")))
+ cfg_entry_tab[entrycount].inout = DIR_INOUT;
+ else if(!(strcmp(yylval.str, "in")))
+ cfg_entry_tab[entrycount].inout = DIR_INONLY;
+ else if(!(strcmp(yylval.str, "out")))
+ cfg_entry_tab[entrycount].inout = DIR_OUTONLY;
+ else
+ {
+ log(LL_ERR, "ERROR parsing config file: unknown parameter for keyword \"direction\" at line %d!", lineno);
+ config_error_flag++;
+ }
+ break;
+
+ case DISCONNECTPROG:
+ if((cfg_entry_tab[entrycount].disconnectprog = malloc(strlen(yylval.str)+1)) == NULL)
+ {
+ log(LL_ERR, "entry %d: disconnectprog, malloc failed!", entrycount);
+ do_exit(1);
+ }
+ strcpy(cfg_entry_tab[entrycount].disconnectprog, yylval.str);
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: disconnectprog = %s", entrycount, yylval.str)));
+ break;
+
+ case DOWNTRIES:
+ if(yylval.num > DOWN_TRIES_MAX)
+ yylval.num = DOWN_TRIES_MAX;
+ else if(yylval.num < DOWN_TRIES_MIN)
+ yylval.num = DOWN_TRIES_MIN;
+
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: downtries = %d", entrycount, yylval.num)));
+ cfg_entry_tab[entrycount].downtries = yylval.num;
+ break;
+
+ case DOWNTIME:
+ if(yylval.num > DOWN_TIME_MAX)
+ yylval.num = DOWN_TIME_MAX;
+ else if(yylval.num < DOWN_TIME_MIN)
+ yylval.num = DOWN_TIME_MIN;
+
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: downtime = %d", entrycount, yylval.num)));
+ cfg_entry_tab[entrycount].downtime = yylval.num;
+ break;
+
+ case EARLYHANGUP:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: earlyhangup = %d", entrycount, yylval.num)));
+ cfg_entry_tab[entrycount].earlyhangup = yylval.num;
+ break;
+
+ case EXTCALLATTR:
+ DBGL(DL_RCCF, (log(LL_DBG, "system: extcallattr = %d", yylval.booln)));
+ extcallattr = yylval.booln;
+ break;
+
+ case FIRMWARE:
+ DBGL(DL_RCCF, (log(LL_DBG, "controller %d: firmware = %s", controllercount, yylval.str)));
+ isdn_ctrl_tab[controllercount].firmware = strdup(yylval.str);
+ break;
+
+ case HOLIDAYFILE:
+ strcpy(holidayfile, yylval.str);
+ DBGL(DL_RCCF, (log(LL_DBG, "system: holidayfile = %s", yylval.str)));
+ break;
+
+ case IDLE_ALG_OUT:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: idle-algorithm-outgoing = %s", entrycount, yylval.str)));
+
+ if(!(strcmp(yylval.str, "fix-unit-size")))
+ {
+ cfg_entry_tab[entrycount].shorthold_algorithm = SHA_FIXU;
+ }
+ else if(!(strcmp(yylval.str, "var-unit-size")))
+ {
+ cfg_entry_tab[entrycount].shorthold_algorithm = SHA_VARU;
+ }
+ else
+ {
+ log(LL_ERR, "ERROR parsing config file: unknown parameter for keyword \"idle-algorithm-outgoing\" at line %d!", lineno);
+ config_error_flag++;
+ }
+ break;
+
+ case IDLETIME_IN:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: idle_time_in = %d", entrycount, yylval.num)));
+ cfg_entry_tab[entrycount].idle_time_in = yylval.num;
+ break;
+
+ case IDLETIME_OUT:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: idle_time_out = %d", entrycount, yylval.num)));
+ cfg_entry_tab[entrycount].idle_time_out = yylval.num;
+ break;
+
+ case ISDNCONTROLLER:
+ cfg_entry_tab[entrycount].isdncontroller = yylval.num;
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: isdncontroller = %d", entrycount, yylval.num)));
+ break;
+
+ case ISDNCHANNEL:
+ if (yylval.num == 0 || yylval.num == -1)
+ {
+ cfg_entry_tab[entrycount].isdnchannel = CHAN_ANY;
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: isdnchannel = any", entrycount)));
+ }
+ else if (yylval.num > MAX_BCHAN)
+ {
+ log(LL_DBG, "entry %d: isdnchannel value out of range", entrycount);
+ config_error_flag++;
+ }
+ else
+ {
+ cfg_entry_tab[entrycount].isdnchannel = yylval.num-1;
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: isdnchannel = B%d", entrycount, yylval.num)));
+ }
+ break;
+
+ case ISDNTIME:
+ DBGL(DL_RCCF, (log(LL_DBG, "system: isdntime = %d", yylval.booln)));
+ isdntime = yylval.booln;
+ break;
+
+ case ISDNTXDELIN:
+ cfg_entry_tab[entrycount].isdntxdelin = yylval.num;
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: isdntxdel-incoming = %d", entrycount, yylval.num)));
+ break;
+
+ case ISDNTXDELOUT:
+ cfg_entry_tab[entrycount].isdntxdelout = yylval.num;
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: isdntxdel-outgoing = %d", entrycount, yylval.num)));
+ break;
+
+ case LOCAL_PHONE_DIALOUT:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: local_phone_dialout = %s", entrycount, yylval.str)));
+ strcpy(cfg_entry_tab[entrycount].local_phone_dialout.number, yylval.str);
+ break;
+
+ case LOCAL_SUBADDR_DIALOUT:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: local_subaddr_dialout = %s", entrycount, yylval.str)));
+ strcpy(cfg_entry_tab[entrycount].local_phone_dialout.subaddr, yylval.str);
+ break;
+
+ case LOCAL_PHONE_INCOMING:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: local_phone_incoming = %s", entrycount, yylval.str)));
+ strcpy(cfg_entry_tab[entrycount].local_phone_incoming.number, yylval.str);
+ break;
+
+ case LOCAL_SUBADDR_INCOMING:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: local_subaddr_incoming = %s", entrycount, yylval.str)));
+ strcpy(cfg_entry_tab[entrycount].local_phone_incoming.subaddr, yylval.str);
+ break;
+
+ case MAILER:
+ strcpy(mailer, yylval.str);
+ DBGL(DL_RCCF, (log(LL_DBG, "system: mailer = %s", yylval.str)));
+ break;
+
+ case MAILTO:
+ strcpy(mailto, yylval.str);
+ DBGL(DL_RCCF, (log(LL_DBG, "system: mailto = %s", yylval.str)));
+ break;
+
+ case MAXCONNECTTIME:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: maxconnecttime = %d", entrycount, yylval.num)));
+ cfg_entry_tab[entrycount].maxconnecttime = yylval.num;
+ break;
+
+ case MONITORPORT:
+ monitorport = yylval.num;
+ DBGL(DL_RCCF, (log(LL_DBG, "system: monitorport = %d", yylval.num)));
+ break;
+
+ case MONITORSW:
+ if (yylval.booln && inhibit_monitor)
+ {
+ do_monitor = 0;
+ DBGL(DL_RCCF, (log(LL_DBG, "system: monitor-enable overriden by command line flag")));
+ }
+ else
+ {
+ do_monitor = yylval.booln;
+ DBGL(DL_RCCF, (log(LL_DBG, "system: monitor-enable = %d", yylval.booln)));
+ }
+ break;
+
+ case NAME:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: name = %s", entrycount, yylval.str)));
+ strcpy(cfg_entry_tab[entrycount].name, yylval.str);
+ break;
+
+ case PPP_AUTH_RECHALLENGE:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: ppp-auth-rechallenge = %d", entrycount, yylval.booln)));
+ if(yylval.booln)
+ cfg_entry_tab[entrycount].ppp_auth_flags |= AUTH_RECHALLENGE;
+ else
+ cfg_entry_tab[entrycount].ppp_auth_flags &= ~AUTH_RECHALLENGE;
+ set_isppp_auth(entrycount);
+ break;
+
+ case PPP_AUTH_PARANOID:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: ppp-auth-paranoid = %d", entrycount, yylval.booln)));
+ if(yylval.booln)
+ cfg_entry_tab[entrycount].ppp_auth_flags |= AUTH_REQUIRED;
+ else
+ cfg_entry_tab[entrycount].ppp_auth_flags &= ~AUTH_REQUIRED;
+ set_isppp_auth(entrycount);
+ break;
+
+ case PPP_EXPECT_AUTH:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: ppp-expect-auth = %s", entrycount, yylval.str)));
+ if(!(strcmp(yylval.str, "none")))
+ cfg_entry_tab[entrycount].ppp_expect_auth = AUTH_NONE;
+ else if(!(strcmp(yylval.str, "pap")))
+ cfg_entry_tab[entrycount].ppp_expect_auth = AUTH_PAP;
+ else if(!(strcmp(yylval.str, "chap")))
+ cfg_entry_tab[entrycount].ppp_expect_auth = AUTH_CHAP;
+ else
+ {
+ log(LL_ERR, "ERROR parsing config file: unknown parameter for keyword \"ppp-expect-auth\" at line %d!", lineno);
+ config_error_flag++;
+ break;
+ }
+ set_isppp_auth(entrycount);
+ break;
+
+ case PPP_EXPECT_NAME:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: ppp-expect-name = %s", entrycount, yylval.str)));
+ strncpy(cfg_entry_tab[entrycount].ppp_expect_name, yylval.str, sizeof(cfg_entry_tab[entrycount].ppp_expect_name) -1);
+ set_isppp_auth(entrycount);
+ break;
+
+ case PPP_EXPECT_PASSWORD:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: ppp-expect-password = %s", entrycount, yylval.str)));
+ strncpy(cfg_entry_tab[entrycount].ppp_expect_password, yylval.str, sizeof(cfg_entry_tab[entrycount].ppp_expect_password) -1);
+ set_isppp_auth(entrycount);
+ break;
+
+ case PPP_SEND_AUTH:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: ppp-send-auth = %s", entrycount, yylval.str)));
+ if(!(strcmp(yylval.str, "none")))
+ cfg_entry_tab[entrycount].ppp_send_auth = AUTH_NONE;
+ else if(!(strcmp(yylval.str, "pap")))
+ cfg_entry_tab[entrycount].ppp_send_auth = AUTH_PAP;
+ else if(!(strcmp(yylval.str, "chap")))
+ cfg_entry_tab[entrycount].ppp_send_auth = AUTH_CHAP;
+ else
+ {
+ log(LL_ERR, "ERROR parsing config file: unknown parameter for keyword \"ppp-send-auth\" at line %d!", lineno);
+ config_error_flag++;
+ break;
+ }
+ set_isppp_auth(entrycount);
+ break;
+
+ case PPP_SEND_NAME:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: ppp-send-name = %s", entrycount, yylval.str)));
+ strncpy(cfg_entry_tab[entrycount].ppp_send_name, yylval.str, sizeof(cfg_entry_tab[entrycount].ppp_send_name) -1);
+ set_isppp_auth(entrycount);
+ break;
+
+ case PPP_SEND_PASSWORD:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: ppp-send-password = %s", entrycount, yylval.str)));
+ strncpy(cfg_entry_tab[entrycount].ppp_send_password, yylval.str, sizeof(cfg_entry_tab[entrycount].ppp_send_password) -1);
+ set_isppp_auth(entrycount);
+ break;
+
+ case PROTOCOL:
+ DBGL(DL_RCCF, (log(LL_DBG, "controller %d: protocol = %s", controllercount, yylval.str)));
+ if(!(strcmp(yylval.str, "dss1")))
+ isdn_ctrl_tab[controllercount].protocol = PROTOCOL_DSS1;
+ else if(!(strcmp(yylval.str, "d64s")))
+ isdn_ctrl_tab[controllercount].protocol = PROTOCOL_D64S;
+ else
+ {
+ log(LL_ERR, "ERROR parsing config file: unknown parameter for keyword \"protocol\" at line %d!", lineno);
+ config_error_flag++;
+ }
+ break;
+
+ case REACTION:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: dialin_reaction = %s", entrycount, yylval.str)));
+ if(!(strcmp(yylval.str, "accept")))
+ cfg_entry_tab[entrycount].dialin_reaction = REACT_ACCEPT;
+ else if(!(strcmp(yylval.str, "reject")))
+ cfg_entry_tab[entrycount].dialin_reaction = REACT_REJECT;
+ else if(!(strcmp(yylval.str, "ignore")))
+ cfg_entry_tab[entrycount].dialin_reaction = REACT_IGNORE;
+ else if(!(strcmp(yylval.str, "answer")))
+ cfg_entry_tab[entrycount].dialin_reaction = REACT_ANSWER;
+ else if(!(strcmp(yylval.str, "callback")))
+ cfg_entry_tab[entrycount].dialin_reaction = REACT_CALLBACK;
+ else
+ {
+ log(LL_ERR, "ERROR parsing config file: unknown parameter for keyword \"dialin_reaction\" at line %d!", lineno);
+ config_error_flag++;
+ }
+ break;
+
+ case REMOTE_PHONE_DIALOUT:
+ if(cfg_entry_tab[entrycount].remote_numbers_count >= MAXRNUMBERS)
+ {
+ log(LL_ERR, "ERROR parsing config file: too many remote numbers at line %d!", lineno);
+ config_error_flag++;
+ break;
+ }
+
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: remote_phone_dialout #%d = %s",
+ entrycount, cfg_entry_tab[entrycount].remote_numbers_count, yylval.str)));
+
+ strcpy(cfg_entry_tab[entrycount].remote_numbers[cfg_entry_tab[entrycount].remote_numbers_count].number, yylval.str);
+ cfg_entry_tab[entrycount].remote_numbers[cfg_entry_tab[entrycount].remote_numbers_count].flag = 0;
+
+ cfg_entry_tab[entrycount].remote_numbers_count++;
+
+ break;
+
+ case REMOTE_SUBADDR_DIALOUT:
+ if(cfg_entry_tab[entrycount].remote_subaddr_count >= MAXRNUMBERS)
+ {
+ log(LL_ERR, "ERROR parsing config file: too many remote subaddresses at line %d!", lineno);
+ config_error_flag++;
+ break;
+ }
+
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: remote_subaddr_dialout #%d = %s",
+ entrycount, cfg_entry_tab[entrycount].remote_numbers_count, yylval.str)));
+
+ strcpy(cfg_entry_tab[entrycount].remote_numbers[cfg_entry_tab[entrycount].remote_numbers_count].subaddr, yylval.str);
+
+ break;
+
+ case REMOTE_NUMBERS_HANDLING:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: remdial_handling = %s", entrycount, yylval.str)));
+ if(!(strcmp(yylval.str, "next")))
+ cfg_entry_tab[entrycount].remote_numbers_handling = RNH_NEXT;
+ else if(!(strcmp(yylval.str, "last")))
+ cfg_entry_tab[entrycount].remote_numbers_handling = RNH_LAST;
+ else if(!(strcmp(yylval.str, "first")))
+ cfg_entry_tab[entrycount].remote_numbers_handling = RNH_FIRST;
+ else
+ {
+ log(LL_ERR, "ERROR parsing config file: unknown parameter for keyword \"remdial_handling\" at line %d!", lineno);
+ config_error_flag++;
+ }
+ break;
+
+ case REMOTE_PHONE_INCOMING:
+ {
+ int n;
+ n = cfg_entry_tab[entrycount].incoming_numbers_count;
+ if (n >= MAX_INCOMING)
+ {
+ log(LL_ERR, "ERROR parsing config file: too many \"remote_phone_incoming\" entries at line %d!", lineno);
+ config_error_flag++;
+ break;
+ }
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: remote_phone_incoming #%d = %s", entrycount, n, yylval.str)));
+ strcpy(cfg_entry_tab[entrycount].remote_phone_incoming[n].number, yylval.str);
+ cfg_entry_tab[entrycount].incoming_numbers_count++;
+ }
+ break;
+
+ case REMOTE_SUBADDR_INCOMING:
+ {
+ int n;
+ n = cfg_entry_tab[entrycount].incoming_numbers_count;
+ if (n >= MAX_INCOMING)
+ {
+ log(LL_ERR, "ERROR parsing config file: too many \"remote_subaddr_incoming\" entries at line %d!", lineno);
+ config_error_flag++;
+ break;
+ }
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: remote_subaddr_incoming #%d = %s", entrycount, n, yylval.str)));
+ strcpy(cfg_entry_tab[entrycount].remote_phone_incoming[n].subaddr, yylval.str);
+ }
+ break;
+
+ case RATESFILE:
+ strcpy(ratesfile, yylval.str);
+ DBGL(DL_RCCF, (log(LL_DBG, "system: ratesfile = %s", yylval.str)));
+ break;
+
+ case RATETYPE:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: ratetype = %d", entrycount, yylval.num)));
+ cfg_entry_tab[entrycount].ratetype = yylval.num;
+ break;
+
+ case RECOVERYTIME:
+ if(yylval.num < RECOVERYTIME_MIN)
+ {
+ yylval.num = RECOVERYTIME_MIN;
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: recoverytime < %d, min = %d", entrycount, RECOVERYTIME_MIN, yylval.num)));
+ }
+
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: recoverytime = %d", entrycount, yylval.num)));
+ cfg_entry_tab[entrycount].recoverytime = yylval.num;
+ break;
+
+ case REGEXPR:
+ if(nregexpr >= MAX_RE)
+ {
+ log(LL_ERR, "system: regexpr #%d >= MAX_RE", nregexpr);
+ config_error_flag++;
+ break;
+ }
+
+ if((i = regcomp(&(rarr[nregexpr].re), yylval.str, REG_EXTENDED|REG_NOSUB)) != 0)
+ {
+ char buf[256];
+ regerror(i, &(rarr[nregexpr].re), buf, sizeof(buf));
+ log(LL_ERR, "system: regcomp error for %s: [%s]", yylval.str, buf);
+ config_error_flag++;
+ break;
+ }
+ else
+ {
+ if((rarr[nregexpr].re_expr = malloc(strlen(yylval.str)+1)) == NULL)
+ {
+ log(LL_ERR, "system: regexpr malloc error error for %s", yylval.str);
+ config_error_flag++;
+ break;
+ }
+ strcpy(rarr[nregexpr].re_expr, yylval.str);
+
+ DBGL(DL_RCCF, (log(LL_DBG, "system: regexpr %s stored into slot %d", yylval.str, nregexpr)));
+
+ if(rarr[nregexpr].re_prog != NULL)
+ rarr[nregexpr].re_flg = 1;
+
+ nregexpr++;
+
+ }
+ break;
+
+ case REGPROG:
+ if(nregprog >= MAX_RE)
+ {
+ log(LL_ERR, "system: regprog #%d >= MAX_RE", nregprog);
+ config_error_flag++;
+ break;
+ }
+ if((rarr[nregprog].re_prog = malloc(strlen(yylval.str)+1)) == NULL)
+ {
+ log(LL_ERR, "system: regprog malloc error error for %s", yylval.str);
+ config_error_flag++;
+ break;
+ }
+ strcpy(rarr[nregprog].re_prog, yylval.str);
+
+ DBGL(DL_RCCF, (log(LL_DBG, "system: regprog %s stored into slot %d", yylval.str, nregprog)));
+
+ if(rarr[nregprog].re_expr != NULL)
+ rarr[nregprog].re_flg = 1;
+
+ nregprog++;
+ break;
+
+ case ROTATESUFFIX:
+ strcpy(rotatesuffix, yylval.str);
+ DBGL(DL_RCCF, (log(LL_DBG, "system: rotatesuffix = %s", yylval.str)));
+ break;
+
+ case RTPRIO:
+#ifdef USE_RTPRIO
+ rt_prio = yylval.num;
+ if(rt_prio < RTP_PRIO_MIN || rt_prio > RTP_PRIO_MAX)
+ {
+ config_error_flag++;
+ log(LL_ERR, "system: error, rtprio (%d) out of range!", yylval.num);
+ }
+ else
+ {
+ DBGL(DL_RCCF, (log(LL_DBG, "system: rtprio = %d", yylval.num)));
+ }
+#else
+ rt_prio = RTPRIO_NOTUSED;
+#endif
+ break;
+
+ case TINAINITPROG:
+ strcpy(tinainitprog, yylval.str);
+ DBGL(DL_RCCF, (log(LL_DBG, "system: tinainitprog = %s", yylval.str)));
+ break;
+
+ case UNITLENGTH:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: unitlength = %d", entrycount, yylval.num)));
+ cfg_entry_tab[entrycount].unitlength = yylval.num;
+ break;
+
+ case UNITLENGTHSRC:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: unitlengthsrc = %s", entrycount, yylval.str)));
+ if(!(strcmp(yylval.str, "none")))
+ cfg_entry_tab[entrycount].unitlengthsrc = ULSRC_NONE;
+ else if(!(strcmp(yylval.str, "cmdl")))
+ cfg_entry_tab[entrycount].unitlengthsrc = ULSRC_CMDL;
+ else if(!(strcmp(yylval.str, "conf")))
+ cfg_entry_tab[entrycount].unitlengthsrc = ULSRC_CONF;
+ else if(!(strcmp(yylval.str, "rate")))
+ cfg_entry_tab[entrycount].unitlengthsrc = ULSRC_RATE;
+ else if(!(strcmp(yylval.str, "aocd")))
+ cfg_entry_tab[entrycount].unitlengthsrc = ULSRC_DYN;
+ else
+ {
+ log(LL_ERR, "ERROR parsing config file: unknown parameter for keyword \"unitlengthsrc\" at line %d!", lineno);
+ config_error_flag++;
+ }
+ break;
+
+ case USRDEVICENAME:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: usrdevicename = %s", entrycount, yylval.str)));
+ if(!strcmp(yylval.str, "rbch"))
+ cfg_entry_tab[entrycount].usrdevicename = BDRV_RBCH;
+ else if(!strcmp(yylval.str, "tel"))
+ cfg_entry_tab[entrycount].usrdevicename = BDRV_TEL;
+ else if(!strcmp(yylval.str, "ipr"))
+ cfg_entry_tab[entrycount].usrdevicename = BDRV_IPR;
+ else if(!strcmp(yylval.str, "isp"))
+ cfg_entry_tab[entrycount].usrdevicename = BDRV_ISPPP;
+#ifdef __bsdi__
+ else if(!strcmp(yylval.str, "ibc"))
+ cfg_entry_tab[entrycount].usrdevicename = BDRV_IBC;
+#endif
+ else if(!strcmp(yylval.str, "ing"))
+ cfg_entry_tab[entrycount].usrdevicename = BDRV_ING;
+ else
+ {
+ log(LL_ERR, "ERROR parsing config file: unknown parameter for keyword \"usrdevicename\" at line %d!", lineno);
+ config_error_flag++;
+ }
+ break;
+
+ case USRDEVICEUNIT:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: usrdeviceunit = %d", entrycount, yylval.num)));
+ cfg_entry_tab[entrycount].usrdeviceunit = yylval.num;
+ break;
+
+ case USEACCTFILE:
+ useacctfile = yylval.booln;
+ DBGL(DL_RCCF, (log(LL_DBG, "system: useacctfile = %d", yylval.booln)));
+ break;
+
+ case USESUBADDR:
+ cfg_entry_tab[entrycount].usesubaddr = yylval.booln;
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: usesubaddr = %d", entrycount, yylval.booln)));
+ break;
+
+ case USEDOWN:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: usedown = %d", entrycount, yylval.booln)));
+ cfg_entry_tab[entrycount].usedown = yylval.booln;
+ break;
+
+ case VALID:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: valid = %s", entrycount, yylval.str)));
+ parse_valid(entrycount, yylval.str);
+ break;
+
+ default:
+ log(LL_ERR, "ERROR parsing config file: unknown keyword at line %d!", lineno);
+ config_error_flag++;
+ break;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * parse a date/time range
+ *---------------------------------------------------------------------------*/
+static void
+parse_valid(int entrycount, char *dt)
+{
+ /* a valid string consists of some days of week separated by
+ * commas, where 0=sunday, 1=monday .. 6=saturday and a special
+ * value of 7 which is a holiday from the holiday file.
+ * after the days comes an optional (!) time range in the form
+ * aa:bb-cc:dd, this format is fixed to be parsable by sscanf.
+ * Valid specifications looks like this:
+ * 1,2,3,4,5,09:00-18:00 Monday-Friday 9-18h
+ * 1,2,3,4,5,18:00-09:00 Monday-Friday 18-9h
+ * 6 Saturday (whole day)
+ * 0,7 Sunday and Holidays
+ */
+
+ int day = 0;
+ int fromhr = 0;
+ int frommin = 0;
+ int tohr = 0;
+ int tomin = 0;
+ int ret;
+
+ for(;;)
+ {
+ if( ( ((*dt >= '0') && (*dt <= '9')) && (*(dt+1) == ':') ) ||
+ ( ((*dt >= '0') && (*dt <= '2')) && ((*(dt+1) >= '0') && (*(dt+1) <= '9')) && (*(dt+2) == ':') ) )
+ {
+ /* dt points to time spec */
+ ret = sscanf(dt, "%d:%d-%d:%d", &fromhr, &frommin, &tohr, &tomin);
+ if(ret !=4)
+ {
+ log(LL_ERR, "ERROR parsing config file: timespec [%s] error at line %d!", *dt, lineno);
+ config_error_flag++;
+ return;
+ }
+
+ if(fromhr < 0 || fromhr > 24 || tohr < 0 || tohr > 24 ||
+ frommin < 0 || frommin > 59 || tomin < 0 || tomin > 59)
+ {
+ log(LL_ERR, "ERROR parsing config file: invalid time [%s] at line %d!", *dt, lineno);
+ config_error_flag++;
+ return;
+ }
+ break;
+ }
+ else if ((*dt >= '0') && (*dt <= '7'))
+ {
+ /* dt points to day spec */
+ day |= 1 << (*dt - '0');
+ dt++;
+ continue;
+ }
+ else if (*dt == ',')
+ {
+ /* dt points to delimiter */
+ dt++;
+ continue;
+ }
+ else if (*dt == '\0')
+ {
+ /* dt points to end of string */
+ break;
+ }
+ else
+ {
+ /* dt points to illegal character */
+ log(LL_ERR, "ERROR parsing config file: illegal character [%c=0x%x] in date/time spec at line %d!", *dt, *dt, lineno);
+ config_error_flag++;
+ return;
+ }
+ }
+ cfg_entry_tab[entrycount].day = day;
+ cfg_entry_tab[entrycount].fromhr = fromhr;
+ cfg_entry_tab[entrycount].frommin = frommin;
+ cfg_entry_tab[entrycount].tohr = tohr;
+ cfg_entry_tab[entrycount].tomin = tomin;
+}
+
+/*---------------------------------------------------------------------------*
+ * configuration validation and consistency check
+ *---------------------------------------------------------------------------*/
+static void
+check_config(void)
+{
+ cfg_entry_t *cep = &cfg_entry_tab[0]; /* ptr to config entry */
+ int i;
+ int error = 0;
+
+ /* regular expression table */
+
+ for(i=0; i < MAX_RE; i++)
+ {
+ if((rarr[i].re_expr != NULL) && (rarr[i].re_prog == NULL))
+ {
+ log(LL_ERR, "check_config: regular expression %d without program!", i);
+ error++;
+ }
+ if((rarr[i].re_prog != NULL) && (rarr[i].re_expr == NULL))
+ {
+ log(LL_ERR, "check_config: regular expression program %d without expression!", i);
+ error++;
+ }
+ }
+
+ /* entry sections */
+
+ for(i=0; i <= entrycount; i++, cep++)
+ {
+ /* isdn controller number */
+
+ if((cep->isdncontroller < -1) || (cep->isdncontroller > (ncontroller-1)))
+ {
+ log(LL_ERR, "check_config: WARNING, isdncontroller out of range in entry %d!", i);
+ }
+
+ /* numbers used for dialout */
+
+ if((cep->inout != DIR_INONLY) && (cep->dialin_reaction != REACT_ANSWER))
+ {
+ if(cep->remote_numbers_count == 0)
+ {
+ log(LL_ERR, "check_config: remote-phone-dialout not set in entry %d!", i);
+ error++;
+ }
+ if(strlen(cep->local_phone_dialout.number) == 0)
+ {
+ log(LL_ERR, "check_config: local-phone-dialout not set in entry %d!", i);
+ error++;
+ }
+ }
+
+ /* numbers used for incoming calls */
+
+ if(cep->inout != DIR_OUTONLY)
+ {
+ if(strlen(cep->local_phone_incoming.number) == 0)
+ {
+ log(LL_ERR, "check_config: local-phone-incoming not set in entry %d!", i);
+ error++;
+ }
+ if(cep->incoming_numbers_count == 0)
+ {
+ log(LL_ERR, "check_config: remote-phone-incoming not set in entry %d!", i);
+ error++;
+ }
+ }
+
+ if((cep->dialin_reaction == REACT_ANSWER) && (cep->b1protocol != BPROT_NONE))
+ {
+ log(LL_ERR, "check_config: b1protocol not raw for telephony in entry %d!", i);
+ error++;
+ }
+
+ if((cep->ppp_send_auth == AUTH_PAP) || (cep->ppp_send_auth == AUTH_CHAP))
+ {
+ if(cep->ppp_send_name[0] == 0)
+ {
+ log(LL_ERR, "check_config: no remote authentification name in entry %d!", i);
+ error++;
+ }
+ if(cep->ppp_send_password[0] == 0)
+ {
+ log(LL_ERR, "check_config: no remote authentification password in entry %d!", i);
+ error++;
+ }
+ }
+ if((cep->ppp_expect_auth == AUTH_PAP) || (cep->ppp_expect_auth == AUTH_CHAP))
+ {
+ if(cep->ppp_expect_name[0] == 0)
+ {
+ log(LL_ERR, "check_config: no local authentification name in entry %d!", i);
+ error++;
+ }
+ if(cep->ppp_expect_password[0] == 0)
+ {
+ log(LL_ERR, "check_config: no local authentification secret in entry %d!", i);
+ error++;
+ }
+ }
+ }
+ if(error)
+ {
+ log(LL_ERR, "check_config: %d error(s) in configuration file, exit!", error);
+ do_exit(1);
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * print the configuration
+ *---------------------------------------------------------------------------*/
+static void
+print_config(void)
+{
+#define PFILE stdout
+
+#ifdef I4B_EXTERNAL_MONITOR
+ extern struct monitor_rights * monitor_next_rights(const struct monitor_rights *r);
+ struct monitor_rights *m_rights;
+#endif
+ cfg_entry_t *cep = &cfg_entry_tab[0]; /* ptr to config entry */
+ int i, j;
+ time_t clock;
+ char mytime[64];
+
+ time(&clock);
+ strcpy(mytime, ctime(&clock));
+ mytime[strlen(mytime)-1] = '\0';
+
+ fprintf(PFILE, "#---------------------------------------------------------------------------\n");
+ fprintf(PFILE, "# system section (generated %s)\n", mytime);
+ fprintf(PFILE, "#---------------------------------------------------------------------------\n");
+ fprintf(PFILE, "system\n");
+ fprintf(PFILE, "useacctfile = %s\n", useacctfile ? "on\t\t\t\t# update accounting information file" : "off\t\t\t\t# don't update accounting information file");
+ fprintf(PFILE, "acctall = %s\n", acct_all ? "on\t\t\t\t# put all events into accounting file" : "off\t\t\t\t# put only charged events into accounting file");
+ fprintf(PFILE, "acctfile = %s\t\t# accounting information file\n", acctfile);
+ fprintf(PFILE, "ratesfile = %s\t\t# charging rates database file\n", ratesfile);
+
+#ifdef USE_RTPRIO
+ if(rt_prio == RTPRIO_NOTUSED)
+ fprintf(PFILE, "# rtprio is unused\n");
+ else
+ fprintf(PFILE, "rtprio = %d\t\t\t\t# isdnd runs at realtime priority\n", rt_prio);
+#endif
+
+ /* regular expression table */
+
+ for(i=0; i < MAX_RE; i++)
+ {
+ if(rarr[i].re_expr != NULL)
+ {
+ fprintf(PFILE, "regexpr = \"%s\"\t\t# scan logfile for this expression\n", rarr[i].re_expr);
+ }
+ if(rarr[i].re_prog != NULL)
+ {
+ fprintf(PFILE, "regprog = %s\t\t# program to run when expression is matched\n", rarr[i].re_prog);
+ }
+ }
+
+#ifdef I4B_EXTERNAL_MONITOR
+
+ fprintf(PFILE, "monitor-allowed = %s\n", do_monitor ? "on\t\t\t\t# remote isdnd monitoring allowed" : "off\t\t\t\t# remote isdnd monitoring disabled");
+ fprintf(PFILE, "monitor-port = %d\t\t\t\t# TCP/IP port number used for remote monitoring\n", monitorport);
+
+ m_rights = monitor_next_rights(NULL);
+ if(m_rights != NULL)
+ {
+ char *s = "error\n";
+ char b[512];
+
+ for ( ; m_rights != NULL; m_rights = monitor_next_rights(m_rights))
+ {
+ if(m_rights->local)
+ {
+ fprintf(PFILE, "monitor = \"%s\"\t\t# local socket name for monitoring\n", m_rights->name);
+ }
+ else
+ {
+ struct in_addr ia;
+ ia.s_addr = ntohl(m_rights->net);
+
+ switch(m_rights->mask)
+ {
+ case 0xffffffff:
+ s = "32";
+ break;
+ case 0xfffffffe:
+ s = "31";
+ break;
+ case 0xfffffffc:
+ s = "30";
+ break;
+ case 0xfffffff8:
+ s = "29";
+ break;
+ case 0xfffffff0:
+ s = "28";
+ break;
+ case 0xffffffe0:
+ s = "27";
+ break;
+ case 0xffffffc0:
+ s = "26";
+ break;
+ case 0xffffff80:
+ s = "25";
+ break;
+ case 0xffffff00:
+ s = "24";
+ break;
+ case 0xfffffe00:
+ s = "23";
+ break;
+ case 0xfffffc00:
+ s = "22";
+ break;
+ case 0xfffff800:
+ s = "21";
+ break;
+ case 0xfffff000:
+ s = "20";
+ break;
+ case 0xffffe000:
+ s = "19";
+ break;
+ case 0xffffc000:
+ s = "18";
+ break;
+ case 0xffff8000:
+ s = "17";
+ break;
+ case 0xffff0000:
+ s = "16";
+ break;
+ case 0xfffe0000:
+ s = "15";
+ break;
+ case 0xfffc0000:
+ s = "14";
+ break;
+ case 0xfff80000:
+ s = "13";
+ break;
+ case 0xfff00000:
+ s = "12";
+ break;
+ case 0xffe00000:
+ s = "11";
+ break;
+ case 0xffc00000:
+ s = "10";
+ break;
+ case 0xff800000:
+ s = "9";
+ break;
+ case 0xff000000:
+ s = "8";
+ break;
+ case 0xfe000000:
+ s = "7";
+ break;
+ case 0xfc000000:
+ s = "6";
+ break;
+ case 0xf8000000:
+ s = "5";
+ break;
+ case 0xf0000000:
+ s = "4";
+ break;
+ case 0xe0000000:
+ s = "3";
+ break;
+ case 0xc0000000:
+ s = "2";
+ break;
+ case 0x80000000:
+ s = "1";
+ break;
+ case 0x00000000:
+ s = "0";
+ break;
+ }
+ fprintf(PFILE, "monitor = \"%s/%s\"\t\t# host (net/mask) allowed to connect for monitoring\n", inet_ntoa(ia), s);
+ }
+ b[0] = '\0';
+
+ if((m_rights->rights) & I4B_CA_COMMAND_FULL)
+ strcat(b, "fullcmd,");
+ if((m_rights->rights) & I4B_CA_COMMAND_RESTRICTED)
+ strcat(b, "restrictedcmd,");
+ if((m_rights->rights) & I4B_CA_EVNT_CHANSTATE)
+ strcat(b, "channelstate,");
+ if((m_rights->rights) & I4B_CA_EVNT_CALLIN)
+ strcat(b, "callin,");
+ if((m_rights->rights) & I4B_CA_EVNT_CALLOUT)
+ strcat(b, "callout,");
+ if((m_rights->rights) & I4B_CA_EVNT_I4B)
+ strcat(b, "logevents,");
+
+ if(b[strlen(b)-1] == ',')
+ b[strlen(b)-1] = '\0';
+
+ fprintf(PFILE, "monitor-access = %s\t\t# monitor access rights\n", b);
+ }
+ }
+
+#endif
+ /* entry sections */
+
+ for(i=0; i <= entrycount; i++, cep++)
+ {
+ fprintf(PFILE, "\n");
+ fprintf(PFILE, "#---------------------------------------------------------------------------\n");
+ fprintf(PFILE, "# entry section %d\n", i);
+ fprintf(PFILE, "#---------------------------------------------------------------------------\n");
+ fprintf(PFILE, "entry\n");
+
+ fprintf(PFILE, "name = %s\t\t# name for this entry section\n", cep->name);
+
+ fprintf(PFILE, "isdncontroller = %d\t\t# ISDN card number used for this entry\n", cep->isdncontroller);
+ fprintf(PFILE, "isdnchannel = ");
+ switch(cep->isdnchannel)
+ {
+ case CHAN_ANY:
+ fprintf(PFILE, "-1\t\t# any ISDN B-channel may be used\n");
+ break;
+ default:
+ fprintf(PFILE, "%d\t\t# only ISDN B-channel %d may be used\n", cep->isdnchannel+1, cep->isdnchannel+1);
+ break;
+ }
+
+ fprintf(PFILE, "usrdevicename = %s\t\t# name of userland ISDN B-channel device\n", bdrivername(cep->usrdevicename));
+ fprintf(PFILE, "usrdeviceunit = %d\t\t# unit number of userland ISDN B-channel device\n", cep->usrdeviceunit);
+
+ fprintf(PFILE, "b1protocol = %s\n", cep->b1protocol ? "hdlc\t\t# B-channel layer 1 protocol is HDLC" : "raw\t\t# No B-channel layer 1 protocol used");
+
+ if(!(cep->usrdevicename == BDRV_TEL))
+ {
+ fprintf(PFILE, "direction = ");
+ switch(cep->inout)
+ {
+ case DIR_INONLY:
+ fprintf(PFILE, "in\t\t# only incoming connections allowed\n");
+ break;
+ case DIR_OUTONLY:
+ fprintf(PFILE, "out\t\t# only outgoing connections allowed\n");
+ break;
+ case DIR_INOUT:
+ fprintf(PFILE, "inout\t\t# incoming and outgoing connections allowed\n");
+ break;
+ }
+ }
+
+ if(!((cep->usrdevicename == BDRV_TEL) || (cep->inout == DIR_INONLY)))
+ {
+ if(cep->remote_numbers_count > 1)
+ {
+ for(j=0; j<cep->remote_numbers_count; j++)
+ fprintf(PFILE, "remote-phone-dialout = %s\t\t# telephone number %d for dialing out to remote\n", cep->remote_numbers[j].number, j+1);
+
+ fprintf(PFILE, "remdial-handling = ");
+
+ switch(cep->remote_numbers_handling)
+ {
+ case RNH_NEXT:
+ fprintf(PFILE, "next\t\t# use next number after last successfull for new dial\n");
+ break;
+ case RNH_LAST:
+ fprintf(PFILE, "last\t\t# use last successfull number for new dial\n");
+ break;
+ case RNH_FIRST:
+ fprintf(PFILE, "first\t\t# always start with first number for new dial\n");
+ break;
+ }
+ }
+ else
+ {
+ fprintf(PFILE, "remote-phone-dialout = %s\t\t# telephone number for dialing out to remote\n", cep->remote_numbers[0].number);
+ }
+
+ fprintf(PFILE, "local-phone-dialout = %s\t\t# show this number to remote when dialling out\n", cep->local_phone_dialout.number);
+ fprintf(PFILE, "dialout-type = %s\n", cep->dialouttype ? "calledback\t\t# i am called back by remote" : "normal\t\t# i am not called back by remote");
+ }
+
+ if(!(cep->inout == DIR_OUTONLY))
+ {
+ int n;
+
+ fprintf(PFILE, "local-phone-incoming = %s\t\t# incoming calls must match this (mine) telephone number\n", cep->local_phone_incoming.number);
+ for (n = 0; n < cep->incoming_numbers_count; n++)
+ fprintf(PFILE, "remote-phone-incoming = %s\t\t# this is a valid remote number to call me\n",
+ cep->remote_phone_incoming[n].number);
+
+ fprintf(PFILE, "dialin-reaction = ");
+ switch(cep->dialin_reaction)
+ {
+ case REACT_ACCEPT:
+ fprintf(PFILE, "accept\t\t# i accept a call from remote and connect\n");
+ break;
+ case REACT_REJECT:
+ fprintf(PFILE, "reject\t\t# i reject the call from remote\n");
+ break;
+ case REACT_IGNORE:
+ fprintf(PFILE, "ignore\t\t# i ignore the call from remote\n");
+ break;
+ case REACT_ANSWER:
+ fprintf(PFILE, "answer\t\t# i will start telephone answering when remote calls in\n");
+ break;
+ case REACT_CALLBACK:
+ fprintf(PFILE, "callback\t\t# when remote calls in, i will hangup and call back\n");
+ break;
+ }
+ }
+
+ if(cep->usrdevicename == BDRV_ISPPP)
+ {
+ char *s;
+ switch(cep->ppp_expect_auth)
+ {
+ case AUTH_NONE:
+ s = "none";
+ break;
+ case AUTH_PAP:
+ s = "pap";
+ break;
+ case AUTH_CHAP:
+ s = "chap";
+ break;
+ default:
+ s = NULL;
+ break;
+ }
+ if(s != NULL)
+ {
+ fprintf(PFILE, "ppp-expect-auth = %s\t\t# the auth protocol we expect to receive on dial-in (none,pap,chap)\n", s);
+ if(cep->ppp_expect_auth != AUTH_NONE)
+ {
+ fprintf(PFILE, "ppp-expect-name = %s\t\t# the user name allowed in\n", cep->ppp_expect_name);
+ fprintf(PFILE, "ppp-expect-password = %s\t\t# the key expected from the other side\n", cep->ppp_expect_password);
+ fprintf(PFILE, "ppp-auth-paranoid = %s\t\t# do we require remote to authenticate even if we dial out\n", cep->ppp_auth_flags & AUTH_REQUIRED ? "yes" : "no");
+ }
+ }
+ switch(cep->ppp_send_auth)
+ {
+ case AUTH_NONE:
+ s = "none";
+ break;
+ case AUTH_PAP:
+ s = "pap";
+ break;
+ case AUTH_CHAP:
+ s = "chap";
+ break;
+ default:
+ s = NULL;
+ break;
+ }
+ if(s != NULL)
+ {
+ fprintf(PFILE, "ppp-send-auth = %s\t\t# the auth protocol we use when dialing out (none,pap,chap)\n", s);
+ if(cep->ppp_send_auth != AUTH_NONE)
+ {
+ fprintf(PFILE, "ppp-send-name = %s\t\t# our PPP account used for dial-out\n", cep->ppp_send_name);
+ fprintf(PFILE, "ppp-send-password = %s\t\t# the key sent to the other side\n", cep->ppp_send_password);
+ }
+ }
+ if(cep->ppp_send_auth == AUTH_CHAP ||
+ cep->ppp_expect_auth == AUTH_CHAP) {
+ fprintf(PFILE, "ppp-auth-rechallenge = %s\t\t# rechallenge CHAP connections once in a while\n", cep->ppp_auth_flags & AUTH_RECHALLENGE ? "yes" : "no");
+ }
+ }
+
+ if(!((cep->inout == DIR_INONLY) || (cep->usrdevicename == BDRV_TEL)))
+ {
+ char *s;
+ fprintf(PFILE, "idletime-outgoing = %d\t\t# outgoing call idle timeout\n", cep->idle_time_out);
+
+ switch( cep->shorthold_algorithm )
+ {
+ case SHA_FIXU:
+ s = "fix-unit-size";
+ break;
+ case SHA_VARU:
+ s = "var-unit-size";
+ break;
+ default:
+ s = "error!!!";
+ break;
+ }
+
+ fprintf(PFILE, "idle-algorithm-outgoing = %s\t\t# outgoing call idle algorithm\n", s);
+ }
+
+ if(!(cep->inout == DIR_OUTONLY))
+ fprintf(PFILE, "idletime-incoming = %d\t\t# incoming call idle timeout\n", cep->idle_time_in);
+
+ if(!(cep->usrdevicename == BDRV_TEL))
+ {
+ fprintf(PFILE, "unitlengthsrc = ");
+ switch(cep->unitlengthsrc)
+ {
+ case ULSRC_NONE:
+ fprintf(PFILE, "none\t\t# no unit length specified, using default\n");
+ break;
+ case ULSRC_CMDL:
+ fprintf(PFILE, "cmdl\t\t# using unit length specified on commandline\n");
+ break;
+ case ULSRC_CONF:
+ fprintf(PFILE, "conf\t\t# using unitlength specified by unitlength-keyword\n");
+ fprintf(PFILE, "unitlength = %d\t\t# fixed unitlength\n", cep->unitlength);
+ break;
+ case ULSRC_RATE:
+ fprintf(PFILE, "rate\t\t# using unitlength specified in rate database\n");
+ fprintf(PFILE, "ratetype = %d\t\t# type of rate from rate database\n", cep->ratetype);
+ break;
+ case ULSRC_DYN:
+ fprintf(PFILE, "aocd\t\t# using dynamically calculated unitlength based on AOCD subscription\n");
+ fprintf(PFILE, "ratetype = %d\t\t# type of rate from rate database\n", cep->ratetype);
+ break;
+ }
+
+ fprintf(PFILE, "earlyhangup = %d\t\t# early hangup safety time\n", cep->earlyhangup);
+
+ }
+
+ if(cep->usrdevicename == BDRV_TEL)
+ {
+ fprintf(PFILE, "answerprog = %s\t\t# program used to answer incoming telephone calls\n", cep->answerprog);
+ fprintf(PFILE, "alert = %d\t\t# number of seconds to wait before accepting a call\n", cep->alert);
+ }
+
+ if(!(cep->usrdevicename == BDRV_TEL))
+ {
+ if(cep->dialin_reaction == REACT_CALLBACK)
+ fprintf(PFILE, "callbackwait = %d\t\t# i am waiting this time before calling back remote\n", cep->callbackwait);
+
+ if(cep->dialouttype == DIALOUT_CALLEDBACK)
+ fprintf(PFILE, "calledbackwait = %d\t\t# i am waiting this time for a call back from remote\n", cep->calledbackwait);
+
+ if(!(cep->inout == DIR_INONLY))
+ {
+ fprintf(PFILE, "dialretries = %d\t\t# number of dialing retries\n", cep->dialretries);
+ fprintf(PFILE, "recoverytime = %d\t\t# time to wait between dialling retries\n", cep->recoverytime);
+ fprintf(PFILE, "dialrandincr = %s\t\t# use random dialing time addon\n", cep->dialrandincr ? "on" : "off");
+
+ fprintf(PFILE, "usedown = %s\n", cep->usedown ? "on\t\t# ISDN device switched off on excessive dial failures" : "off\t\t# no device switchoff on excessive dial failures");
+ if(cep->usedown)
+ {
+ fprintf(PFILE, "downtries = %d\t\t# number of dialretries failures before switching off\n", cep->downtries);
+ fprintf(PFILE, "downtime = %d\t\t# time device is switched off\n", cep->downtime);
+ }
+ }
+ }
+ }
+ fprintf(PFILE, "\n");
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnd/rc_parse.y b/usr.sbin/i4b/isdnd/rc_parse.y
new file mode 100644
index 0000000..6630de9
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/rc_parse.y
@@ -0,0 +1,531 @@
+/*
+ * Copyright (c) 1997 Joerg Wunsch. All rights reserved.
+ *
+ * Copyright (c) 1997, 2002 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * i4b daemon - runtime configuration parser
+ * -----------------------------------------
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Tue Mar 26 14:37:50 2002]
+ *
+ *---------------------------------------------------------------------------*/
+
+%{
+
+/* #define YYDEBUG 1 */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "monitor.h" /* monitor access rights bit definitions */
+#include "isdnd.h"
+
+#ifndef FALSE
+# define FALSE 0
+#endif
+
+#ifndef TRUE
+# define TRUE 1
+#endif
+
+extern void cfg_setval(int keyword);
+extern void cfg_set_controller_default();
+extern void reset_scanner(FILE *infile);
+extern void yyerror(const char *msg);
+extern int yylex();
+
+extern int lineno;
+extern char *yytext;
+extern int nentries;
+
+int saw_system = 0;
+int entrycount = -1;
+int controllercount = -1;
+
+%}
+
+%token ACCTALL
+%token ACCTFILE
+%token ALERT
+%token ALIASFNAME
+%token ALIASING
+%token ANSWERPROG
+%token B1PROTOCOL
+%token BEEPCONNECT
+%token BUDGETCALLOUTPERIOD
+%token BUDGETCALLOUTNCALLS
+%token BUDGETCALLOUTSFILE
+%token BUDGETCALLOUTSFILEROTATE
+%token BUDGETCALLBACKPERIOD
+%token BUDGETCALLBACKNCALLS
+%token BUDGETCALLBACKSFILE
+%token BUDGETCALLBACKSFILEROTATE
+%token CALLBACKWAIT
+%token CALLEDBACKWAIT
+%token CALLIN
+%token CALLOUT
+%token CHANNELSTATE
+%token CLONE
+%token CONNECTPROG
+%token CONTROLLER
+%token DIALOUTTYPE
+%token DIALRANDINCR
+%token DIALRETRIES
+%token DIRECTION
+%token DISCONNECTPROG
+%token DOWNTIME
+%token DOWNTRIES
+%token EARLYHANGUP
+%token ENTRY
+%token EXTCALLATTR
+%token FIRMWARE
+%token FULLCMD
+%token HOLIDAYFILE
+%token IDLETIME_IN
+%token IDLETIME_OUT
+%token IDLE_ALG_OUT
+%token ISDNCHANNEL
+%token ISDNCONTROLLER
+%token ISDNTIME
+%token ISDNTXDELIN
+%token ISDNTXDELOUT
+%token LOCAL_PHONE_DIALOUT
+%token LOCAL_SUBADDR_DIALOUT
+%token LOCAL_PHONE_INCOMING
+%token LOCAL_SUBADDR_INCOMING
+%token LOGEVENTS
+%token MAILER
+%token MAILTO
+%token MAXCONNECTTIME
+%token MONITOR
+%token MONITORACCESS
+%token MONITORPORT
+%token MONITORSW
+%token NAME
+%token NO
+%token OFF
+%token ON
+%token PPP_AUTH_RECHALLENGE
+%token PPP_AUTH_PARANOID
+%token PPP_EXPECT_AUTH
+%token PPP_EXPECT_NAME
+%token PPP_EXPECT_PASSWORD
+%token PPP_SEND_AUTH
+%token PPP_SEND_NAME
+%token PPP_SEND_PASSWORD
+%token PROTOCOL
+%token RATESFILE
+%token RATETYPE
+%token REACTION
+%token RECOVERYTIME
+%token REGEXPR
+%token REGPROG
+%token REMOTE_NUMBERS_HANDLING
+%token REMOTE_PHONE_DIALOUT
+%token REMOTE_SUBADDR_DIALOUT
+%token REMOTE_PHONE_INCOMING
+%token REMOTE_SUBADDR_INCOMING
+%token RESTRICTEDCMD
+%token ROTATESUFFIX
+%token RTPRIO
+%token SYSTEM
+%token TINAINITPROG
+%token UNITLENGTH
+%token UNITLENGTHSRC
+%token USEACCTFILE
+%token USESUBADDR
+%token USEDOWN
+%token USRDEVICENAME
+%token USRDEVICEUNIT
+%token VALID
+%token YES
+
+
+%token <str> NUMBERSTR
+
+%token <str> STRING
+
+%type <booln> boolean
+
+%type <num> sysfilekeyword sysnumkeyword sysstrkeyword sysboolkeyword
+%type <num> filekeyword numkeyword strkeyword boolkeyword monrights monright
+%type <num> cstrkeyword cfilekeyword
+%type <str> filename
+
+%union {
+ int booln;
+ int num;
+ char *str;
+}
+
+%%
+
+config: sections
+ ;
+
+sections: possible_nullentries
+ syssect
+ optcontrollersects
+ entrysects
+ ;
+
+possible_nullentries:
+ /* lambda */
+ | possible_nullentries error '\n'
+ | possible_nullentries nullentry
+ ;
+
+nullentry: '\n'
+ ;
+
+entrysects: entrysect
+ | entrysects entrysect
+ ;
+
+optcontrollersects:
+ controllersects
+ |
+ {
+ cfg_set_controller_default();
+ }
+ ;
+
+controllersects: controllersect
+ | controllersects controllersect
+ ;
+
+/* ============== */
+/* system section */
+/* ============== */
+
+syssect: SYSTEM sysentries
+ ;
+
+sysentries: sysentry
+ {
+ saw_system = 1;
+ monitor_clear_rights();
+ }
+ | sysentries sysentry
+ ;
+
+sysentry: sysfileentry
+ | sysboolentry
+ | sysnumentry
+ | sysstrentry
+ | sysmonitorstart
+ | sysmonitorrights
+ | nullentry
+ | error '\n'
+ ;
+
+
+sysmonitorstart:
+ MONITOR '=' STRING '\n'
+ {
+ char *err = NULL;
+ switch (monitor_start_rights($3)) {
+ case I4BMAR_OK:
+ break;
+ case I4BMAR_LENGTH:
+ err = "local socket name too long: %s";
+ break;
+ case I4BMAR_DUP:
+ err = "duplicate entry: %s";
+ break;
+ case I4BMAR_CIDR:
+ err = "invalid CIDR specification: %s";
+ break;
+ case I4BMAR_NOIP:
+ err = "could not resolve host or net specification: %s";
+ break;
+ }
+ if (err) {
+ char msg[1024];
+ snprintf(msg, sizeof msg, err, $3);
+ yyerror(msg);
+ }
+ }
+ ;
+
+sysmonitorrights:
+ MONITORACCESS '=' monrights '\n'
+ { monitor_add_rights($3); }
+ ;
+
+monrights: monrights ',' monright { $$ = $1 | $3; }
+ | monright { $$ = $1; }
+ ;
+
+monright: FULLCMD { $$ = I4B_CA_COMMAND_FULL; }
+ | RESTRICTEDCMD { $$ = I4B_CA_COMMAND_RESTRICTED; }
+ | CHANNELSTATE { $$ = I4B_CA_EVNT_CHANSTATE; }
+ | CALLIN { $$ = I4B_CA_EVNT_CALLIN; }
+ | CALLOUT { $$ = I4B_CA_EVNT_CALLOUT; }
+ | LOGEVENTS { $$ = I4B_CA_EVNT_I4B; }
+ ;
+
+sysfileentry: sysfilekeyword '=' filename '\n'
+ {
+ cfg_setval($1);
+ }
+ ;
+
+sysboolentry: sysboolkeyword '=' boolean '\n'
+ {
+ yylval.booln = $3;
+ cfg_setval($1);
+ }
+ ;
+
+sysnumentry: sysnumkeyword '=' NUMBERSTR '\n'
+ {
+ yylval.num = atoi($3);
+ cfg_setval($1);
+ }
+ ;
+
+sysstrentry: sysstrkeyword '=' STRING '\n'
+ {
+ cfg_setval($1);
+ }
+ | sysstrkeyword '=' NUMBERSTR '\n'
+ {
+ cfg_setval($1);
+ }
+ ;
+
+filename: STRING {
+ if ($1[0] != '/')
+ {
+ yyerror("filename doesn't start with a slash");
+ YYERROR;
+ }
+ $$ = $1;
+ }
+ ;
+
+boolean: NO { $$ = FALSE; }
+ | OFF { $$ = FALSE; }
+ | ON { $$ = TRUE; }
+ | YES { $$ = TRUE; }
+ ;
+
+sysfilekeyword: RATESFILE { $$ = RATESFILE; }
+ | ACCTFILE { $$ = ACCTFILE; }
+ | ALIASFNAME { $$ = ALIASFNAME; }
+ | HOLIDAYFILE { $$ = HOLIDAYFILE; }
+ | TINAINITPROG { $$ = TINAINITPROG; }
+ ;
+
+sysboolkeyword: USEACCTFILE { $$ = USEACCTFILE; }
+ | ALIASING { $$ = ALIASING; }
+ | ACCTALL { $$ = ACCTALL; }
+ | BEEPCONNECT { $$ = BEEPCONNECT; }
+ | EXTCALLATTR { $$ = EXTCALLATTR; }
+ | ISDNTIME { $$ = ISDNTIME; }
+ | MONITORSW { $$ = MONITORSW; }
+ ;
+
+sysnumkeyword: MONITORPORT { $$ = MONITORPORT; }
+ | RTPRIO { $$ = RTPRIO; }
+ ;
+
+sysstrkeyword: MAILER { $$ = MAILER; }
+ | MAILTO { $$ = MAILTO; }
+ | ROTATESUFFIX { $$ = ROTATESUFFIX; }
+ | REGEXPR { $$ = REGEXPR; }
+ | REGPROG { $$ = REGPROG; }
+ ;
+
+/* ============= */
+/* entry section */
+/* ============= */
+
+entrysect: ENTRY
+ {
+ entrycount++;
+ nentries++;
+ }
+ entries
+ ;
+
+entries: entry
+ | entries entry
+ ;
+
+entry: fileentry
+ | strentry
+ | numentry
+ | boolentry
+ | nullentry
+ | error '\n'
+ ;
+
+fileentry: filekeyword '=' filename '\n'
+ {
+ cfg_setval($1);
+ }
+ ;
+
+
+strentry: strkeyword '=' STRING '\n'
+ {
+ cfg_setval($1);
+ }
+ | strkeyword '=' NUMBERSTR '\n'
+ {
+ cfg_setval($1);
+ }
+ ;
+
+boolentry: boolkeyword '=' boolean '\n'
+ {
+ yylval.booln = $3;
+ cfg_setval($1);
+ }
+ ;
+
+numentry: numkeyword '=' NUMBERSTR '\n'
+ {
+ yylval.num = atoi($3);
+ cfg_setval($1);
+ }
+ ;
+
+filekeyword: BUDGETCALLBACKSFILE { $$ = BUDGETCALLBACKSFILE; }
+ | BUDGETCALLOUTSFILE { $$ = BUDGETCALLOUTSFILE; }
+ ;
+
+strkeyword: ANSWERPROG { $$ = ANSWERPROG; }
+ | B1PROTOCOL { $$ = B1PROTOCOL; }
+ | CONNECTPROG { $$ = CONNECTPROG; }
+ | DIALOUTTYPE { $$ = DIALOUTTYPE; }
+ | DIRECTION { $$ = DIRECTION; }
+ | DISCONNECTPROG { $$ = DISCONNECTPROG; }
+ | IDLE_ALG_OUT { $$ = IDLE_ALG_OUT; }
+ | LOCAL_PHONE_INCOMING { $$ = LOCAL_PHONE_INCOMING; }
+ | LOCAL_SUBADDR_INCOMING { $$ = LOCAL_SUBADDR_INCOMING; }
+ | LOCAL_PHONE_DIALOUT { $$ = LOCAL_PHONE_DIALOUT; }
+ | LOCAL_SUBADDR_DIALOUT { $$ = LOCAL_SUBADDR_DIALOUT; }
+ | NAME { $$ = NAME; }
+ | PPP_EXPECT_AUTH { $$ = PPP_EXPECT_AUTH; }
+ | PPP_EXPECT_NAME { $$ = PPP_EXPECT_NAME; }
+ | PPP_EXPECT_PASSWORD { $$ = PPP_EXPECT_PASSWORD; }
+ | PPP_SEND_AUTH { $$ = PPP_SEND_AUTH; }
+ | PPP_SEND_NAME { $$ = PPP_SEND_NAME; }
+ | PPP_SEND_PASSWORD { $$ = PPP_SEND_PASSWORD; }
+ | REACTION { $$ = REACTION; }
+ | REMOTE_NUMBERS_HANDLING { $$ = REMOTE_NUMBERS_HANDLING; }
+ | REMOTE_PHONE_INCOMING { $$ = REMOTE_PHONE_INCOMING; }
+ | REMOTE_SUBADDR_INCOMING { $$ = REMOTE_SUBADDR_INCOMING; }
+ | REMOTE_PHONE_DIALOUT { $$ = REMOTE_PHONE_DIALOUT; }
+ | REMOTE_SUBADDR_DIALOUT { $$ = REMOTE_SUBADDR_DIALOUT; }
+ | UNITLENGTHSRC { $$ = UNITLENGTHSRC; }
+ | USRDEVICENAME { $$ = USRDEVICENAME; }
+ | VALID { $$ = VALID; }
+ | CLONE { $$ = CLONE; }
+ ;
+
+numkeyword: ALERT { $$ = ALERT; }
+ | BUDGETCALLBACKPERIOD { $$ = BUDGETCALLBACKPERIOD; }
+ | BUDGETCALLBACKNCALLS { $$ = BUDGETCALLBACKNCALLS; }
+ | BUDGETCALLOUTPERIOD { $$ = BUDGETCALLOUTPERIOD; }
+ | BUDGETCALLOUTNCALLS { $$ = BUDGETCALLOUTNCALLS; }
+ | CALLBACKWAIT { $$ = CALLBACKWAIT; }
+ | CALLEDBACKWAIT { $$ = CALLEDBACKWAIT; }
+ | DIALRETRIES { $$ = DIALRETRIES; }
+ | EARLYHANGUP { $$ = EARLYHANGUP; }
+ | IDLETIME_IN { $$ = IDLETIME_IN; }
+ | IDLETIME_OUT { $$ = IDLETIME_OUT; }
+ | ISDNCONTROLLER { $$ = ISDNCONTROLLER; }
+ | ISDNCHANNEL { $$ = ISDNCHANNEL; }
+ | ISDNTXDELIN { $$ = ISDNTXDELIN; }
+ | ISDNTXDELOUT { $$ = ISDNTXDELOUT; }
+ | RATETYPE { $$ = RATETYPE; }
+ | RECOVERYTIME { $$ = RECOVERYTIME; }
+ | UNITLENGTH { $$ = UNITLENGTH; }
+ | USRDEVICEUNIT { $$ = USRDEVICEUNIT; }
+ | DOWNTIME { $$ = DOWNTIME; }
+ | DOWNTRIES { $$ = DOWNTRIES; }
+ | MAXCONNECTTIME { $$ = MAXCONNECTTIME; }
+ ;
+
+boolkeyword: BUDGETCALLBACKSFILEROTATE { $$ = BUDGETCALLBACKSFILEROTATE; }
+ | BUDGETCALLOUTSFILEROTATE { $$ = BUDGETCALLOUTSFILEROTATE; }
+ | DIALRANDINCR { $$ = DIALRANDINCR; }
+ | PPP_AUTH_RECHALLENGE { $$ = PPP_AUTH_RECHALLENGE; }
+ | PPP_AUTH_PARANOID { $$ = PPP_AUTH_PARANOID; }
+ | USEDOWN { $$ = USEDOWN; }
+ | USESUBADDR { $$ = USESUBADDR; }
+ ;
+
+/* ================== */
+/* controller section */
+/* ================== */
+
+controllersect: CONTROLLER
+ {
+ controllercount++;
+ }
+ controllers
+ ;
+
+controllers: controller
+ | controllers controller
+ ;
+
+controller: strcontroller
+ | nullentry
+ | error '\n'
+ ;
+
+strcontroller: cstrkeyword '=' STRING '\n'
+ {
+ cfg_setval($1);
+ }
+ | cstrkeyword '=' NUMBERSTR '\n'
+ {
+ cfg_setval($1);
+ }
+ | cfilekeyword '=' filename '\n'
+ {
+ cfg_setval($1);
+ }
+ ;
+
+cstrkeyword: PROTOCOL { $$ = PROTOCOL; }
+ ;
+
+cfilekeyword: FIRMWARE { $$ = FIRMWARE; }
+ ;
+
+
+%%
diff --git a/usr.sbin/i4b/isdnd/rc_scan.l b/usr.sbin/i4b/isdnd/rc_scan.l
new file mode 100644
index 0000000..d8cdfb6
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/rc_scan.l
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 1997 Joerg Wunsch. All rights reserved.
+ *
+ * Copyright (c) 1997, 2002 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * i4b daemon - runtime configuration lexical analyzer
+ * ---------------------------------------------------
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Tue Mar 26 14:37:59 2002]
+ *
+ *---------------------------------------------------------------------------*/
+
+%{
+
+#include <err.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <sysexits.h>
+
+#include "y.tab.h"
+
+int lineno;
+
+%}
+
+%option noyywrap
+%option nounput
+
+%%
+
+#.*$ { /*
+ * Drop comment. NB: this prevents a hash
+ * sign from appearing inside a quoted string.
+ */
+ }
+
+["][^"]*["] {
+ char *str;
+ if ((str = malloc(yyleng - 1)) == 0)
+ errx(EX_OSERR, "Out of virtual memory");
+ memcpy(str, yytext + 1, yyleng - 2);
+ str[yyleng - 2] = 0;
+ yylval.str = str;
+ return STRING;
+ }
+
+(-*[0-9]+)|\* {
+ char *str;
+ char *p = yytext;
+ int i = 0;
+ if ((str = malloc(128)) == 0)
+ errx(EX_OSERR, "Out of virtual memory");
+ while(*p == '-' || isdigit(*p) || *p == '*')
+ str[i++] = *p++;
+ str[i] = '\0';
+ yylval.str = str;
+ return NUMBERSTR;
+ }
+
+acctall { return ACCTALL; }
+acctfile { return ACCTFILE; }
+alert { return ALERT; }
+aliasing { return ALIASING; }
+aliasfile { return ALIASFNAME; }
+answerprog { return ANSWERPROG; }
+b1protocol { return B1PROTOCOL; }
+beepconnect { return BEEPCONNECT; }
+budget-callbackperiod { return BUDGETCALLBACKPERIOD; }
+budget-callbackncalls { return BUDGETCALLBACKNCALLS; }
+budget-callbacksfile { return BUDGETCALLBACKSFILE; }
+budget-callbacksfile-rotate { return BUDGETCALLBACKSFILEROTATE; }
+budget-calloutperiod { return BUDGETCALLOUTPERIOD; }
+budget-calloutncalls { return BUDGETCALLOUTNCALLS; }
+budget-calloutsfile { return BUDGETCALLOUTSFILE; }
+budget-calloutsfile-rotate { return BUDGETCALLOUTSFILEROTATE; }
+callbackwait { return CALLBACKWAIT; }
+calledbackwait { return CALLEDBACKWAIT; }
+clone { return CLONE; }
+connectprog { return CONNECTPROG; }
+controller { return CONTROLLER; }
+dialin-reaction { return REACTION; }
+dialout-type { return DIALOUTTYPE; }
+dialrandincr { return DIALRANDINCR; }
+dialretries { return DIALRETRIES; }
+direction { return DIRECTION; }
+disconnectprog { return DISCONNECTPROG; }
+downtries { return DOWNTRIES; }
+downtime { return DOWNTIME; }
+earlyhangup { return EARLYHANGUP; }
+entry { return ENTRY; }
+extcallattr { return EXTCALLATTR; }
+firmware { return FIRMWARE; }
+holidayfile { return HOLIDAYFILE; }
+idletime-incoming { return IDLETIME_IN; }
+idletime-outgoing { return IDLETIME_OUT; }
+idle-algorithm-outgoing { return IDLE_ALG_OUT; }
+isdncontroller { return ISDNCONTROLLER; }
+isdnchannel { return ISDNCHANNEL; }
+isdntime { return ISDNTIME; }
+isdntxdel-incoming { return ISDNTXDELIN; }
+isdntxdel-outgoing { return ISDNTXDELOUT; }
+local-phone-dialout { return LOCAL_PHONE_DIALOUT; }
+local-subaddr-dialout { return LOCAL_SUBADDR_DIALOUT; }
+local-phone-incoming { return LOCAL_PHONE_INCOMING; }
+local-subaddr-incoming { return LOCAL_SUBADDR_INCOMING; }
+mailer { return MAILER; }
+mailto { return MAILTO; }
+maxconnecttime { return MAXCONNECTTIME; }
+monitor-allowed { return MONITORSW; }
+monitor-port { return MONITORPORT; }
+monitor { return MONITOR; }
+monitor-access { return MONITORACCESS; }
+fullcmd { return FULLCMD; }
+restrictedcmd { return RESTRICTEDCMD; }
+channelstate { return CHANNELSTATE; }
+callin { return CALLIN; }
+callout { return CALLOUT; }
+logevents { return LOGEVENTS; }
+name { return NAME; }
+no { return NO; }
+off { return OFF; }
+on { return ON; }
+ppp-auth-rechallenge { return PPP_AUTH_RECHALLENGE; }
+ppp-auth-paranoid { return PPP_AUTH_PARANOID; }
+ppp-expect-auth { return PPP_EXPECT_AUTH; }
+ppp-expect-name { return PPP_EXPECT_NAME; }
+ppp-expect-password { return PPP_EXPECT_PASSWORD; }
+ppp-send-auth { return PPP_SEND_AUTH; }
+ppp-send-name { return PPP_SEND_NAME; }
+ppp-send-password { return PPP_SEND_PASSWORD; }
+protocol { return PROTOCOL; }
+ratesfile { return RATESFILE; }
+ratetype { return RATETYPE; }
+recoverytime { return RECOVERYTIME; }
+regexpr { return REGEXPR; }
+regprog { return REGPROG; }
+remdial-handling { return REMOTE_NUMBERS_HANDLING; }
+remote-phone-dialout { return REMOTE_PHONE_DIALOUT; }
+remote-subaddr-dialout { return REMOTE_SUBADDR_DIALOUT; }
+remote-phone-incoming { return REMOTE_PHONE_INCOMING; }
+remote-subaddr-incoming { return REMOTE_SUBADDR_INCOMING; }
+rotatesuffix { return ROTATESUFFIX; }
+rtprio { return RTPRIO; }
+system { return SYSTEM; }
+tinainitprog { return TINAINITPROG; }
+unitlength { return UNITLENGTH; }
+unitlengthsrc { return UNITLENGTHSRC; }
+useacctfile { return USEACCTFILE; }
+usesubaddr { return USESUBADDR; }
+usrdevicename { return USRDEVICENAME; }
+usrdeviceunit { return USRDEVICEUNIT; }
+usedown { return USEDOWN; }
+valid { return VALID; }
+yes { return YES; }
+
+\n { lineno++; return '\n'; }
+
+[A-Za-z/.][-A-Za-z0-9_/.]* {
+ char *str;
+ if ((str = strdup(yytext)) == 0)
+ err(EX_OSERR, "Out of virtual memory");
+ yylval.str = str;
+ return STRING;
+ }
+
+[ \t] { /* drop white space */ }
+
+. { return yytext[0]; }
+
+%%
+
+void
+reset_scanner(FILE *infile)
+{
+ yyrestart(infile);
+ lineno = 1;
+}
diff --git a/usr.sbin/i4b/isdnd/support.c b/usr.sbin/i4b/isdnd/support.c
new file mode 100644
index 0000000..cd5e36d
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/support.c
@@ -0,0 +1,1138 @@
+/*
+ * Copyright (c) 1997, 2002 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * i4b daemon - misc support routines
+ * ----------------------------------
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Tue Mar 26 14:38:11 2002]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "isdnd.h"
+
+static int isvalidtime(cfg_entry_t *cep);
+
+/*---------------------------------------------------------------------------*
+ * find an active entry by driver type and driver unit
+ *---------------------------------------------------------------------------*/
+cfg_entry_t *
+find_active_entry_by_driver(int drivertype, int driverunit)
+{
+ cfg_entry_t *cep = NULL;
+ int i;
+
+ for(i=0; i < nentries; i++)
+ {
+ cep = &cfg_entry_tab[i]; /* ptr to config entry */
+
+ if(!((cep->usrdevicename == drivertype) &&
+ (cep->usrdeviceunit == driverunit)))
+ {
+ continue;
+ }
+
+ /* check time interval */
+
+ if(isvalidtime(cep) == 0)
+ {
+ DBGL(DL_VALID, (log(LL_DBG, "find_active_entry_by_driver: entry %d, time not valid!", i)));
+ continue;
+ }
+
+ /* found */
+
+ if(cep->cdid == CDID_UNUSED)
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_active_entry_by_driver: entry %d [%s%d], cdid=CDID_UNUSED !",
+ i, bdrivername(drivertype), driverunit)));
+ return(NULL);
+ }
+ else if(cep->cdid == CDID_RESERVED)
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_active_entry_by_driver: entry %d [%s%d], cdid=CDID_RESERVED!",
+ i, bdrivername(drivertype), driverunit)));
+ return(NULL);
+ }
+ return(cep);
+ }
+ return(NULL);
+}
+
+/*---------------------------------------------------------------------------*
+ * find entry by drivertype and driverunit and setup for dialing out
+ *---------------------------------------------------------------------------*/
+cfg_entry_t *
+find_by_device_for_dialout(int drivertype, int driverunit)
+{
+ cfg_entry_t *cep = NULL;
+ int i;
+
+ for(i=0; i < nentries; i++)
+ {
+ cep = &cfg_entry_tab[i]; /* ptr to config entry */
+
+ /* compare driver type and unit */
+
+ if(!((cep->usrdevicename == drivertype) &&
+ (cep->usrdeviceunit == driverunit)))
+ {
+ continue;
+ }
+
+ /* check time interval */
+
+ if(isvalidtime(cep) == 0)
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_dialout: entry %d, time not valid!", i)));
+ continue;
+ }
+
+ /* found, check if already reserved */
+
+ if(cep->cdid == CDID_RESERVED)
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_dialout: entry %d, cdid reserved!", i)));
+ return(NULL);
+ }
+
+ /* check if this entry is already in use ? */
+
+ if(cep->cdid != CDID_UNUSED)
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_dialout: entry %d, cdid in use", i)));
+ return(NULL);
+ }
+
+ if((setup_dialout(cep)) == GOOD)
+ {
+ /* found an entry to be used for calling out */
+
+ DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_dialout: found entry %d!", i)));
+ return(cep);
+ }
+ else
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_dialout: entry %d, setup_dialout() failed!", i)));
+ return(NULL);
+ }
+ }
+
+ DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_dialout: no entry found!")));
+ return(NULL);
+}
+
+/*---------------------------------------------------------------------------*
+ * find entry by drivertype and driverunit and setup for dialing out
+ *---------------------------------------------------------------------------*/
+cfg_entry_t *
+find_by_device_for_dialoutnumber(msg_dialoutnumber_ind_t *mp)
+{
+ cfg_entry_t *cep = NULL;
+ int i, j;
+
+ for(i=0; i < nentries; i++)
+ {
+ cep = &cfg_entry_tab[i]; /* ptr to config entry */
+
+ /* compare driver type and unit */
+
+ if(!((cep->usrdevicename == mp->driver) &&
+ (cep->usrdeviceunit == mp->driver_unit)))
+ {
+ continue;
+ }
+
+ /* check time interval */
+
+ if(isvalidtime(cep) == 0)
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_dialoutnumber: entry %d, time not valid!", i)));
+ continue;
+ }
+
+ /* found, check if already reserved */
+
+ if(cep->cdid == CDID_RESERVED)
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_dialoutnumber: entry %d, cdid reserved!", i)));
+ return(NULL);
+ }
+
+ /* check if this entry is already in use ? */
+
+ if(cep->cdid != CDID_UNUSED)
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_dialoutnumber: entry %d, cdid in use", i)));
+ return(NULL);
+ }
+
+ cep->keypad[0] = '\0';
+
+ /* check number and copy to cep->remote_numbers[] */
+
+ for(j = 0; j < mp->cmdlen; j++)
+ {
+ if(!(isdigit(*(mp->cmd+j))))
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_dialoutnumber: entry %d, dial string contains non-digit at pos %d", i, j)));
+ return(NULL);
+ }
+ /* fill in number to dial */
+ cep->remote_numbers[0].number[j] = *(mp->cmd+j);
+ }
+ cep->remote_numbers[0].number[j] = '\0';
+
+/* XXX subaddr does not have to be a digit! isgraph() would be a better idea */
+ for(j = 0; j < mp->subaddrlen; j++)
+ {
+ if(!(isdigit(*(mp->subaddr+j))))
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_dialoutnumber: entry %d, subaddr string contains non-digit at pos %d", i, j)));
+ return(NULL);
+ }
+ /* fill in number to dial */
+ cep->remote_numbers[0].subaddr[j] = *(mp->subaddr+j);
+ }
+ cep->remote_numbers[0].subaddr[j] = '\0';
+
+ cep->remote_numbers_count = 1;
+
+ if((setup_dialout(cep)) == GOOD)
+ {
+ /* found an entry to be used for calling out */
+
+ DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_dialoutnumber: found entry %d!", i)));
+ return(cep);
+ }
+ else
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_dialoutnumber: entry %d, setup_dialout() failed!", i)));
+ return(NULL);
+ }
+ }
+
+ DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_dialoutnumber: no entry found!")));
+ return(NULL);
+}
+
+/*---------------------------------------------------------------------------*
+ * find entry by drivertype and driverunit and setup for send keypad
+ *---------------------------------------------------------------------------*/
+cfg_entry_t *
+find_by_device_for_keypad(int drivertype, int driverunit, int cmdlen, char *cmd)
+{
+ cfg_entry_t *cep = NULL;
+ int i, j;
+
+ for(i=0; i < nentries; i++)
+ {
+ cep = &cfg_entry_tab[i]; /* ptr to config entry */
+
+ /* compare driver type and unit */
+
+ if(!((cep->usrdevicename == drivertype) &&
+ (cep->usrdeviceunit == driverunit)))
+ {
+ continue;
+ }
+
+ /* check time interval */
+
+ if(isvalidtime(cep) == 0)
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_keypad: entry %d, time not valid!", i)));
+ continue;
+ }
+
+ /* found, check if already reserved */
+
+ if(cep->cdid == CDID_RESERVED)
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_keypad: entry %d, cdid reserved!", i)));
+ return(NULL);
+ }
+
+ /* check if this entry is already in use ? */
+
+ if(cep->cdid != CDID_UNUSED)
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_keypad: entry %d, cdid in use", i)));
+ return(NULL);
+ }
+
+ cep->remote_numbers[0].number[0] = '\0';
+ cep->remote_numbers_count = 0;
+ cep->remote_phone_dialout.number[0] = '\0';
+
+ bzero(cep->keypad, KEYPAD_MAX);
+ strncpy(cep->keypad, cmd, cmdlen);
+
+ DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_keypad: entry %d, keypad string is %s", i, cep->keypad)));
+
+ if((setup_dialout(cep)) == GOOD)
+ {
+ /* found an entry to be used for calling out */
+
+ DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_keypad: found entry %d!", i)));
+ return(cep);
+ }
+ else
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_keypad: entry %d, setup_dialout() failed!", i)));
+ return(NULL);
+ }
+ }
+
+ DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_keypad: no entry found!")));
+ return(NULL);
+}
+
+/*---------------------------------------------------------------------------*
+ * find entry by drivertype and driverunit and setup for dialing out
+ *---------------------------------------------------------------------------*/
+int
+setup_dialout(cfg_entry_t *cep)
+{
+ int i;
+
+ /* check controller operational */
+
+ if((get_controller_state(cep->isdncontroller)) != CTRL_UP)
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "setup_dialout: entry %s, controller is down", cep->name)));
+ return(ERROR);
+ }
+
+ cep->isdncontrollerused = cep->isdncontroller;
+
+ /* check channel available */
+
+ switch(cep->isdnchannel)
+ {
+ case CHAN_ANY:
+ for (i = 0; i < isdn_ctrl_tab[cep->isdncontroller].nbch; i++)
+ {
+ if(ret_channel_state(cep->isdncontroller, i) == CHAN_IDLE)
+ break;
+ }
+
+ if (i == isdn_ctrl_tab[cep->isdncontroller].nbch)
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "setup_dialout: entry %s, no channel free", cep->name)));
+ return(ERROR);
+ }
+ cep->isdnchannelused = CHAN_ANY;
+ break;
+
+ default:
+ if((ret_channel_state(cep->isdncontroller, cep->isdnchannel)) != CHAN_IDLE)
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "setup_dialout: entry %s, channel not free", cep->name)));
+ return(ERROR);
+ }
+ cep->isdnchannelused = cep->isdnchannel;
+ break;
+ }
+
+ DBGL(DL_MSG, (log(LL_DBG, "setup_dialout: entry %s ok!", cep->name)));
+
+ /* preset disconnect cause */
+
+ SET_CAUSE_TYPE(cep->disc_cause, CAUSET_I4B);
+ SET_CAUSE_VAL(cep->disc_cause, CAUSE_I4B_NORMAL);
+
+ return(GOOD);
+}
+
+/*---------------------------------------------------------------------------*
+ * find entry by drivertype and driverunit
+ *---------------------------------------------------------------------------*/
+cfg_entry_t *
+get_cep_by_driver(int drivertype, int driverunit)
+{
+ cfg_entry_t *cep = NULL;
+ int i;
+
+ for(i=0; i < nentries; i++)
+ {
+ cep = &cfg_entry_tab[i]; /* ptr to config entry */
+
+ if(!((cep->usrdevicename == drivertype) &&
+ (cep->usrdeviceunit == driverunit)))
+ {
+ continue;
+ }
+
+ /* check time interval */
+
+ if(isvalidtime(cep) == 0)
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "get_cep_by_driver: entry %d, time not valid!", i)));
+ continue;
+ }
+
+ DBGL(DL_MSG, (log(LL_DBG, "get_cep_by_driver: found entry %d!", i)));
+ return(cep);
+ }
+ return(NULL);
+}
+
+/*---------------------------------------------------------------------------*
+ * find a matching entry for an incoming call
+ *
+ * - not found/no match: log output with LL_CHD and return NULL
+ * - found/match: make entry in free cep, return address
+ *---------------------------------------------------------------------------*/
+cfg_entry_t *
+find_matching_entry_incoming(msg_connect_ind_t *mp)
+{
+ cfg_entry_t *cep = NULL;
+ int i;
+
+ /* check for CW (call waiting) early */
+
+ if(mp->channel == CHAN_NO)
+ {
+ if(aliasing)
+ {
+ char *src_tela = "ERROR-src_tela";
+ char *dst_tela = "ERROR-dst_tela";
+
+ src_tela = get_alias(mp->src_telno);
+ dst_tela = get_alias(mp->dst_telno);
+
+ log(LL_CHD, "%05d <unknown> CW from %s to %s (no channel free)",
+ mp->header.cdid, src_tela, dst_tela);
+ }
+ else
+ {
+ log(LL_CHD, "%05d <unknown> call waiting from %s to %s (no channel free)",
+ mp->header.cdid, mp->src_telno, mp->dst_telno);
+ }
+ return(NULL);
+ }
+
+ for(i=0; i < nentries; i++)
+ {
+ int n;
+ cep = &cfg_entry_tab[i]; /* ptr to config entry */
+
+ /* check my number */
+
+ if(strncmp(cep->local_phone_incoming.number, mp->dst_telno, strlen(cep->local_phone_incoming.number)))
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_matching_entry_incoming: entry %d, myno %s != incomingno %s", i,
+ cep->local_phone_incoming.number, mp->dst_telno)));
+ continue;
+ }
+
+ if (cep->usesubaddr && strncmp(cep->local_phone_incoming.subaddr, mp->dst_subaddr, strlen(cep->local_phone_incoming.subaddr)))
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_matching_entry_incoming: entry %d, mysubno %s != incomingsubno %s", i,
+ cep->local_phone_incoming.subaddr, mp->dst_subaddr)));
+ continue;
+ }
+
+ /* check all allowed remote 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] == '*' && cep->usesubaddr && in->subaddr[0] == '*')
+ break;
+
+ if(in->number[0] == '*' && !cep->usesubaddr)
+ break;
+
+ if(in->number[0] == '*' && cep->usesubaddr && !strncmp(in->subaddr, mp->src_subaddr, strlen(in->subaddr)))
+ break;
+
+ if(strncmp(in->number, mp->src_telno, strlen(in->number)) && !cep->usesubaddr)
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_matching_entry_incoming: entry %d, remno %s != incomingfromno %s", i,
+ in->number, mp->src_telno)));
+ }
+
+ if(strncmp(in->number, mp->src_telno, strlen(in->number)) && cep->usesubaddr && in->subaddr[0] == '*')
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_matching_entry_incoming: entry %d, remno %s != incomingfromno %s", i,
+ in->number, mp->src_telno)));
+ }
+
+ if(strncmp(in->number, mp->src_telno, strlen(in->number)) && cep->usesubaddr && strncmp(in->subaddr, mp->src_subaddr, strlen(in->subaddr)))
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_matching_entry_incoming: entry %d, remno %s != incomingfromno %s", i,
+ in->number, mp->src_telno)));
+ }
+
+ break;
+ }
+
+ if (n >= cep->incoming_numbers_count)
+ continue;
+
+ /* check b protocol */
+
+ if(cep->b1protocol != mp->bprot)
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_matching_entry_incoming: entry %d, bprot %d != incomingprot %d", i,
+ cep->b1protocol, mp->bprot)));
+ continue;
+ }
+
+ /* is this entry currently in use ? */
+
+ if(cep->cdid != CDID_UNUSED)
+ {
+ if(cep->cdid == CDID_RESERVED)
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_matching_entry_incoming: entry %d, cdid is reserved", i)));
+ }
+ else if (cep->dialin_reaction == REACT_ACCEPT
+ && cep->dialouttype == DIALOUT_CALLEDBACK)
+ {
+ /*
+ * We might consider doing this even if this is
+ * not a calledback config entry - BUT: there are
+ * severe race conditions and timinig problems
+ * ex. if both sides run I4B with no callback
+ * delay - both may shutdown the outgoing call
+ * and never be able to establish a connection.
+ * In the called-back case this should not happen.
+ */
+ DBGL(DL_MSG, (log(LL_DBG, "find_matching_entry_incoming: entry %d, incoming call for callback in progress (cdid %05d)", i, cep->cdid)));
+
+ /* save the current call state, we're going to overwrite it with the
+ * new incoming state below... */
+ cep->saved_call.cdid = cep->cdid;
+ cep->saved_call.controller = cep->isdncontrollerused;
+ cep->saved_call.channel = cep->isdnchannelused;
+ }
+ else
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_matching_entry_incoming: entry %d, cdid in use", i)));
+ continue; /* yes, next */
+ }
+ }
+
+ /* check controller value ok */
+
+ if(mp->controller > ncontroller)
+ {
+ log(LL_CHD, "%05d %s incoming call with invalid controller %d",
+ mp->header.cdid, cep->name, mp->controller);
+ return(NULL);
+ }
+
+ /* check controller marked up */
+
+ if((get_controller_state(mp->controller)) != CTRL_UP)
+ {
+ log(LL_CHD, "%05d %s incoming call, controller %d DOWN!",
+ mp->header.cdid, cep->name, mp->controller);
+ return(NULL);
+ }
+
+ /*
+ * check controller he wants, check for any
+ * controller or specific controller
+ */
+
+ if( (mp->controller != -1) &&
+ (mp->controller != cep->isdncontroller) )
+ {
+ log(LL_CHD, "%05d %s incoming call, controller %d != incoming %d",
+ mp->header.cdid, cep->name,
+ cep->isdncontroller, mp->controller);
+ continue;
+ }
+
+ /* check channel he wants */
+
+ switch(mp->channel)
+ {
+ case CHAN_ANY:
+ for (i = 0; i < isdn_ctrl_tab[mp->controller].nbch; i++)
+ {
+ if(ret_channel_state(mp->controller, i) == CHAN_IDLE)
+ break;
+ }
+
+ if (i == isdn_ctrl_tab[mp->controller].nbch)
+ {
+ log(LL_CHD, "%05d %s incoming call, no channel free!",
+ mp->header.cdid, cep->name);
+ return(NULL);
+ }
+ break;
+
+ case CHAN_NO:
+ log(LL_CHD, "%05d %s incoming call, call waiting (no channel available)!",
+ mp->header.cdid, cep->name);
+ return(NULL);
+ break;
+
+ default:
+ if((ret_channel_state(mp->controller, mp->channel)) != CHAN_IDLE)
+ {
+ log(LL_CHD, "%05d %s incoming call, channel B%d not free!",
+ mp->header.cdid, cep->name, mp->channel+1);
+ return(NULL);
+ }
+ break;
+ }
+
+ /* check time interval */
+
+ if(isvalidtime(cep) == 0)
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_matching_entry_incoming: entry %d, time not valid!", i)));
+ continue;
+ }
+
+ /* found a matching entry */
+
+ cep->cdid = mp->header.cdid;
+ cep->isdncontrollerused = mp->controller;
+ cep->isdnchannelused = mp->channel;
+/*XXX*/ cep->disc_cause = 0;
+
+ /* cp number to real one used */
+
+ strcpy(cep->real_phone_incoming.number, mp->src_telno);
+
+ /* copy display string */
+
+ strcpy(cep->display, mp->display);
+
+ /* entry currently down ? */
+
+ if(cep->state == ST_DOWN)
+ {
+ msg_updown_ind_t mui;
+
+ /* set interface up */
+
+ DBGL(DL_MSG, (log(LL_DBG, "find_matching_entry_incoming: entry %d, ", i)));
+
+ mui.driver = cep->usrdevicename;
+ mui.driver_unit = cep->usrdeviceunit;
+ mui.updown = SOFT_ENA;
+
+ if((ioctl(isdnfd, I4B_UPDOWN_IND, &mui)) < 0)
+ {
+ log(LL_ERR, "find_matching_entry_incoming: ioctl I4B_UPDOWN_IND failed: %s", strerror(errno));
+ error_exit(1, "find_matching_entry_incoming: ioctl I4B_UPDOWN_IND failed: %s", strerror(errno));
+ }
+
+ cep->down_retry_count = 0;
+ cep->state = ST_IDLE;
+ }
+ return(cep);
+ }
+
+ if(aliasing)
+ {
+ char *src_tela = "ERROR-src_tela";
+ char *dst_tela = "ERROR-dst_tela";
+
+ src_tela = get_alias(mp->src_telno);
+ dst_tela = get_alias(mp->dst_telno);
+
+ log(LL_CHD, "%05d Call from %s to %s",
+ mp->header.cdid, src_tela, dst_tela);
+ }
+ else
+ {
+ log(LL_CHD, "%05d <unknown> incoming call from %s to %s ctrl %d",
+ mp->header.cdid, mp->src_telno, mp->dst_telno, mp->controller);
+ }
+ return(NULL);
+}
+
+/*---------------------------------------------------------------------------*
+ * return address of ACTIVE config entry by controller and channel
+ *---------------------------------------------------------------------------*/
+cfg_entry_t *
+get_cep_by_cc(int ctrlr, int chan)
+{
+ int i;
+
+ if((chan < 0) || (chan >= isdn_ctrl_tab[ctrlr].nbch))
+ return(NULL);
+
+ for(i=0; i < nentries; i++)
+ {
+ if((cfg_entry_tab[i].cdid != CDID_UNUSED) &&
+ (cfg_entry_tab[i].cdid != CDID_RESERVED) &&
+ (cfg_entry_tab[i].isdnchannelused == chan) &&
+ (cfg_entry_tab[i].isdncontrollerused == ctrlr) &&
+ ((ret_channel_state(ctrlr, chan)) == CHAN_RUN))
+ {
+ return(&cfg_entry_tab[i]);
+ }
+ }
+ return(NULL);
+}
+
+/*---------------------------------------------------------------------------*
+ * return address of config entry identified by cdid
+ *---------------------------------------------------------------------------*/
+cfg_entry_t *
+get_cep_by_cdid(int cdid)
+{
+ int i;
+
+ for(i=0; i < nentries; i++)
+ {
+ if(cfg_entry_tab[i].cdid == cdid
+ || cfg_entry_tab[i].saved_call.cdid == cdid)
+ return(&cfg_entry_tab[i]);
+ }
+ return(NULL);
+}
+
+/*---------------------------------------------------------------------------*
+ * return b channel driver type name string
+ *---------------------------------------------------------------------------*/
+char *
+bdrivername(int drivertype)
+{
+ static char *bdtab[] = {
+ "rbch",
+ "tel",
+ "ipr",
+ "isp",
+ "ibc",
+ "ing"
+ };
+
+ if(drivertype >= BDRV_RBCH && drivertype <= BDRV_ING)
+ return(bdtab[drivertype]);
+ else
+ return("unknown");
+}
+
+/*---------------------------------------------------------------------------*
+ * process AOCD charging messages
+ *---------------------------------------------------------------------------*/
+void
+handle_charge(cfg_entry_t *cep)
+{
+ time_t now = time(NULL);
+
+ if(cep->aoc_last == 0) /* no last timestamp yet ? */
+ {
+ cep->aoc_last = now; /* add time stamp */
+ }
+ else if(cep->aoc_now == 0) /* no current timestamp yet ? */
+ {
+ cep->aoc_now = now; /* current timestamp */
+ }
+ else
+ {
+ cep->aoc_last = cep->aoc_now;
+ cep->aoc_now = now;
+ cep->aoc_diff = cep->aoc_now - cep->aoc_last;
+ cep->aoc_valid = AOC_VALID;
+ }
+
+#ifdef USE_CURSES
+ if(do_fullscreen)
+ display_charge(cep);
+#endif
+
+#ifdef I4B_EXTERNAL_MONITOR
+ if(do_monitor && accepted)
+ monitor_evnt_charge(cep, cep->charge, 0);
+#endif
+
+ if(cep->aoc_valid == AOC_VALID)
+ {
+ if(cep->aoc_diff != cep->unitlength)
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "handle_charge: AOCD unit length updated %d -> %d secs", cep->unitlength, cep->aoc_diff)));
+
+ cep->unitlength = cep->aoc_diff;
+
+ unitlen_chkupd(cep);
+ }
+ else
+ {
+#ifdef NOTDEF
+ DBGL(DL_MSG, (log(LL_DBG, "handle_charge: AOCD unit length still %d secs", cep->unitlength)));
+#endif
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * update kernel idle_time, earlyhup_time and unitlen_time
+ *---------------------------------------------------------------------------*/
+void
+unitlen_chkupd(cfg_entry_t *cep)
+{
+ msg_timeout_upd_t tupd;
+
+ tupd.cdid = cep->cdid;
+
+ /* init the short hold data based on the shorthold algorithm type */
+
+ switch(cep->shorthold_algorithm)
+ {
+ case SHA_FIXU:
+ tupd.shorthold_data.shorthold_algorithm = SHA_FIXU;
+ tupd.shorthold_data.unitlen_time = cep->unitlength;
+ tupd.shorthold_data.idle_time = cep->idle_time_out;
+ tupd.shorthold_data.earlyhup_time = cep->earlyhangup;
+ break;
+
+ case SHA_VARU:
+ tupd.shorthold_data.shorthold_algorithm = SHA_VARU;
+ tupd.shorthold_data.unitlen_time = cep->unitlength;
+ tupd.shorthold_data.idle_time = cep->idle_time_out;
+ tupd.shorthold_data.earlyhup_time = 0;
+ break;
+ default:
+ log(LL_ERR, "unitlen_chkupd bad shorthold_algorithm %d", cep->shorthold_algorithm );
+ return;
+ break;
+ }
+
+ if((ioctl(isdnfd, I4B_TIMEOUT_UPD, &tupd)) < 0)
+ {
+ log(LL_ERR, "ioctl I4B_TIMEOUT_UPD failed: %s", strerror(errno));
+ error_exit(1, "ioctl I4B_TIMEOUT_UPD failed: %s", strerror(errno));
+ }
+}
+
+/*--------------------------------------------------------------------------*
+ * this is intended to be called by do_exit and closes down all
+ * active connections before the daemon exits or is reconfigured.
+ *--------------------------------------------------------------------------*/
+void
+close_allactive(void)
+{
+ int i, j, k;
+ cfg_entry_t *cep = NULL;
+
+ j = 0;
+
+ for (i = 0; i < ncontroller; i++)
+ {
+ if((get_controller_state(i)) != CTRL_UP)
+ continue;
+
+ for (k = 0; k < isdn_ctrl_tab[i].nbch; k++)
+ {
+ if((ret_channel_state(i, k)) == CHAN_RUN)
+ {
+ if((cep = get_cep_by_cc(i, k)) != NULL)
+ {
+#ifdef USE_CURSES
+ if(do_fullscreen)
+ display_disconnect(cep);
+#endif
+#ifdef I4B_EXTERNAL_MONITOR
+ monitor_evnt_disconnect(cep);
+#endif
+ next_state(cep, EV_DRQ);
+ j++;
+ }
+ }
+ }
+ }
+
+ if(j)
+ {
+ log(LL_DMN, "close_allactive: waiting for all connections terminated");
+ sleep(5);
+ }
+}
+
+/*--------------------------------------------------------------------------*
+ * set an interface up
+ *--------------------------------------------------------------------------*/
+void
+if_up(cfg_entry_t *cep)
+{
+ msg_updown_ind_t mui;
+
+ /* set interface up */
+
+ DBGL(DL_MSG, (log(LL_DBG, "if_up: taking %s%d up", bdrivername(cep->usrdevicename), cep->usrdeviceunit)));
+
+ mui.driver = cep->usrdevicename;
+ mui.driver_unit = cep->usrdeviceunit;
+ mui.updown = SOFT_ENA;
+
+ if((ioctl(isdnfd, I4B_UPDOWN_IND, &mui)) < 0)
+ {
+ log(LL_ERR, "if_up: ioctl I4B_UPDOWN_IND failed: %s", strerror(errno));
+ error_exit(1, "if_up: ioctl I4B_UPDOWN_IND failed: %s", strerror(errno));
+ }
+ cep->down_retry_count = 0;
+
+#ifdef USE_CURSES
+ if(do_fullscreen)
+ display_updown(cep, 1);
+#endif
+#ifdef I4B_EXTERNAL_MONITOR
+ monitor_evnt_updown(cep, 1);
+#endif
+
+}
+
+/*--------------------------------------------------------------------------*
+ * set an interface down
+ *--------------------------------------------------------------------------*/
+void
+if_down(cfg_entry_t *cep)
+{
+ msg_updown_ind_t mui;
+
+ /* set interface up */
+
+ DBGL(DL_MSG, (log(LL_DBG, "if_down: taking %s%d down", bdrivername(cep->usrdevicename), cep->usrdeviceunit)));
+
+ mui.driver = cep->usrdevicename;
+ mui.driver_unit = cep->usrdeviceunit;
+ mui.updown = SOFT_DIS;
+
+ if((ioctl(isdnfd, I4B_UPDOWN_IND, &mui)) < 0)
+ {
+ log(LL_ERR, "if_down: ioctl I4B_UPDOWN_IND failed: %s", strerror(errno));
+ error_exit(1, "if_down: ioctl I4B_UPDOWN_IND failed: %s", strerror(errno));
+ }
+ cep->went_down_time = time(NULL);
+ cep->down_retry_count = 0;
+
+#ifdef USE_CURSES
+ if(do_fullscreen)
+ display_updown(cep, 0);
+#endif
+#ifdef I4B_EXTERNAL_MONITOR
+ monitor_evnt_updown(cep, 0);
+#endif
+
+}
+
+/*--------------------------------------------------------------------------*
+ * send a dial response to (an interface in) the kernel
+ *--------------------------------------------------------------------------*/
+void
+dialresponse(cfg_entry_t *cep, int dstat)
+{
+ msg_dialout_resp_t mdr;
+
+ static char *stattab[] = {
+ "normal condition",
+ "temporary failure",
+ "permanent failure",
+ "dialout not allowed"
+ };
+
+ if(dstat < DSTAT_NONE || dstat > DSTAT_INONLY)
+ {
+ log(LL_ERR, "dialresponse: dstat out of range %d!", dstat);
+ return;
+ }
+
+ mdr.driver = cep->usrdevicename;
+ mdr.driver_unit = cep->usrdeviceunit;
+ mdr.stat = dstat;
+ mdr.cause = cep->disc_cause;
+
+ if((ioctl(isdnfd, I4B_DIALOUT_RESP, &mdr)) < 0)
+ {
+ log(LL_ERR, "dialresponse: ioctl I4B_DIALOUT_RESP failed: %s", strerror(errno));
+ error_exit(1, "dialresponse: ioctl I4B_DIALOUT_RESP failed: %s", strerror(errno));
+ }
+
+ DBGL(DL_DRVR, (log(LL_DBG, "dialresponse: sent [%s]", stattab[dstat])));
+}
+
+/*--------------------------------------------------------------------------*
+ * screening/presentation indicator
+ *--------------------------------------------------------------------------*/
+void
+handle_scrprs(int cdid, int scr, int prs, char *caller)
+{
+ /* screening indicator */
+
+ if(scr < SCR_NONE || scr > SCR_NET)
+ {
+ log(LL_ERR, "msg_connect_ind: invalid screening indicator value %d!", scr);
+ }
+ else
+ {
+ static char *scrtab[] = {
+ "no screening indicator",
+ "sreening user provided, not screened",
+ "screening user provided, verified & passed",
+ "screening user provided, verified & failed",
+ "screening network provided", };
+
+ if(extcallattr)
+ {
+ log(LL_CHD, "%05d %s %s", cdid, caller, scrtab[scr]);
+ }
+ else
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "%s - %s", caller, scrtab[scr])));
+ }
+ }
+
+ /* presentation indicator */
+
+ if(prs < PRS_NONE || prs > PRS_RESERVED)
+ {
+ log(LL_ERR, "msg_connect_ind: invalid presentation indicator value %d!", prs);
+ }
+ else
+ {
+ static char *prstab[] = {
+ "no presentation indicator",
+ "presentation allowed",
+ "presentation restricted",
+ "number not available due to interworking",
+ "reserved presentation value" };
+
+ if(extcallattr)
+ {
+ log(LL_CHD, "%05d %s %s", cdid, caller, prstab[prs]);
+ }
+ else
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "%s - %s", caller, prstab[prs])));
+ }
+ }
+}
+
+/*--------------------------------------------------------------------------*
+ * check if the time is valid for an entry
+ *--------------------------------------------------------------------------*/
+static int
+isvalidtime(cfg_entry_t *cep)
+{
+ time_t t;
+ struct tm *tp;
+
+ if(cep->day == 0)
+ return(1);
+
+ t = time(NULL);
+ tp = localtime(&t);
+
+ if(cep->day & HD)
+ {
+ if(isholiday(tp->tm_mday, (tp->tm_mon)+1, (tp->tm_year)+1900))
+ {
+ DBGL(DL_VALID, (log(LL_DBG, "isvalidtime: holiday %d.%d.%d", tp->tm_mday, (tp->tm_mon)+1, (tp->tm_year)+1900)));
+ goto dayok;
+ }
+ }
+
+ if(cep->day & (1 << tp->tm_wday))
+ {
+ DBGL(DL_VALID, (log(LL_DBG, "isvalidtime: day match")));
+ goto dayok;
+ }
+
+ return(0);
+
+dayok:
+ if(cep->fromhr==0 && cep->frommin==0 && cep->tohr==0 && cep->tomin==0)
+ {
+ DBGL(DL_VALID, (log(LL_DBG, "isvalidtime: no time specified, match!")));
+ return(1);
+ }
+
+ if(cep->tohr < cep->fromhr)
+ {
+ /* before 00:00 */
+
+ if( (tp->tm_hour > cep->fromhr) ||
+ (tp->tm_hour == cep->fromhr && tp->tm_min > cep->frommin) )
+ {
+ DBGL(DL_VALID, (log(LL_DBG, "isvalidtime: t<f-1, spec=%02d:%02d-%02d:%02d, curr=%02d:%02d, match!",
+ cep->fromhr, cep->frommin,
+ cep->tohr, cep->tomin,
+ tp->tm_hour, tp->tm_min)));
+
+ return(1);
+ }
+
+ /* after 00:00 */
+
+ if( (tp->tm_hour < cep->tohr) ||
+ (tp->tm_hour == cep->tohr && tp->tm_min < cep->tomin) )
+ {
+ DBGL(DL_VALID, (log(LL_DBG, "isvalidtime: t<f-2, spec=%02d:%02d-%02d:%02d, curr=%02d:%02d, match!",
+ cep->fromhr, cep->frommin,
+ cep->tohr, cep->tomin,
+ tp->tm_hour, tp->tm_min)));
+
+ return(1);
+ }
+ }
+ else if(cep->fromhr == cep->tohr)
+ {
+ if(tp->tm_min >= cep->frommin && tp->tm_min < cep->tomin)
+ {
+ DBGL(DL_VALID, (log(LL_DBG, "isvalidtime: f=t, spec=%02d:%02d-%02d:%02d, curr=%02d:%02d, match!",
+ cep->fromhr, cep->frommin,
+ cep->tohr, cep->tomin,
+ tp->tm_hour, tp->tm_min)));
+
+ return(1);
+ }
+ }
+ else
+ {
+ if((tp->tm_hour > cep->fromhr && tp->tm_hour < cep->tohr) ||
+ (tp->tm_hour == cep->fromhr && tp->tm_min >= cep->frommin) ||
+ (tp->tm_hour == cep->tohr && tp->tm_min < cep->tomin) )
+ {
+ DBGL(DL_VALID, (log(LL_DBG, "isvalidtime: t>f, spec=%02d:%02d-%02d:%02d, curr=%02d:%02d, match!",
+ cep->fromhr, cep->frommin,
+ cep->tohr, cep->tomin,
+ tp->tm_hour, tp->tm_min)));
+ return(1);
+ }
+ }
+ DBGL(DL_VALID, (log(LL_DBG, "isvalidtime: spec=%02d:%02d-%02d:%02d, curr=%02d:%02d, no match!",
+ cep->fromhr, cep->frommin,
+ cep->tohr, cep->tomin,
+ tp->tm_hour, tp->tm_min)));
+
+ return(0);
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnd/timer.c b/usr.sbin/i4b/isdnd/timer.c
new file mode 100644
index 0000000..34c0c43
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/timer.c
@@ -0,0 +1,447 @@
+/*
+ * Copyright (c) 1997, 2002 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * i4b daemon - timer/timing support routines
+ * ------------------------------------------
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Tue Mar 26 14:38:24 2002]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "isdnd.h"
+
+static int hr_callgate(void);
+static void handle_reserved(cfg_entry_t *cep, time_t now);
+static void handle_active(cfg_entry_t *cep, time_t now);
+static void recover_illegal(cfg_entry_t *cep);
+
+/*---------------------------------------------------------------------------*
+ * recover from illegal state
+ *---------------------------------------------------------------------------*/
+static void
+recover_illegal(cfg_entry_t *cep)
+{
+ log(LL_ERR, "recover_illegal: ERROR, entry %s attempting disconnect!", cep->name);
+ sendm_disconnect_req(cep, (CAUSET_I4B << 8) | CAUSE_I4B_NORMAL);
+ log(LL_ERR, "recover_illegal: ERROR, entry %s - reset state/cdid!", cep->name);
+ cep->state = ST_IDLE;
+ cep->cdid = CDID_UNUSED;
+}
+
+/*---------------------------------------------------------------------------*
+ * start the timer
+ *---------------------------------------------------------------------------*/
+void
+start_timer(cfg_entry_t *cep, int seconds)
+{
+ cep->timerval = cep->timerremain = seconds;
+}
+
+/*---------------------------------------------------------------------------*
+ * stop the timer
+ *---------------------------------------------------------------------------*/
+void
+stop_timer(cfg_entry_t *cep)
+{
+ cep->timerval = cep->timerremain = 0;
+}
+
+/*---------------------------------------------------------------------------*
+ * callgate for handle_recovery()
+ *---------------------------------------------------------------------------*/
+static int
+hr_callgate(void)
+{
+ static int tv_first = 1;
+ static struct timeval tv_last;
+ struct timeval tv_now;
+
+ /* there must be 1 sec minimum between calls to this section */
+
+ if(tv_first)
+ {
+ gettimeofday(&tv_last, NULL);
+ tv_first = 0;
+ }
+
+ gettimeofday(&tv_now, NULL);
+
+ if((tv_now.tv_sec - tv_last.tv_sec) < 1)
+ {
+
+ DBGL(DL_TIME, (log(LL_DBG, "time < 1 - last %ld:%ld now %ld:%ld",
+ tv_last.tv_sec, tv_last.tv_usec,
+ tv_now.tv_sec, tv_now.tv_usec)));
+ return(1);
+ }
+ else if((tv_now.tv_sec - tv_last.tv_sec) == 1)
+ {
+ if(((1000000 - tv_last.tv_usec) + tv_now.tv_usec) < 900000)
+ {
+ DBGL(DL_TIME, (log(LL_DBG, "time < 900000us - last %ld:%ld now %ld:%ld",
+ tv_last.tv_sec, tv_last.tv_usec,
+ tv_now.tv_sec, tv_now.tv_usec)));
+ return(1);
+ }
+ }
+
+ DBGL(DL_TIME, (log(LL_DBG, "time OK! - last %ld:%ld now %ld:%ld",
+ tv_last.tv_sec, tv_last.tv_usec,
+ tv_now.tv_sec, tv_now.tv_usec)));
+
+ gettimeofday(&tv_last, NULL);
+
+ return(0);
+}
+
+/*---------------------------------------------------------------------------*
+ * timeout, recovery and retry handling
+ *---------------------------------------------------------------------------*/
+void
+handle_recovery(void)
+{
+ cfg_entry_t *cep = NULL;
+ int i;
+ time_t now;
+
+ if(hr_callgate()) /* last call to handle_recovery < 1 sec ? */
+ return; /* yes, exit */
+
+ now = time(NULL); /* get current time */
+
+ /* walk thru all entries, look for work to do */
+
+ for(i=0; i < nentries; i++)
+ {
+ cep = &cfg_entry_tab[i]; /* ptr to config entry */
+
+ if(cep->budget_callbackperiod && cep->budget_callbackncalls)
+ {
+ if(cep->budget_callbackperiod_time <= now)
+ {
+ DBGL(DL_BDGT, (log(LL_DBG, "%s: new cback-budget-period (%d s, %d left)",
+ cep->name, cep->budget_callbackperiod, cep->budget_callbackncalls_cnt)));
+ cep->budget_callbackperiod_time = now + cep->budget_callbackperiod;
+ cep->budget_callbackncalls_cnt = cep->budget_callbackncalls;
+ }
+ }
+
+ if(cep->budget_calloutperiod && cep->budget_calloutncalls)
+ {
+ if(cep->budget_calloutperiod_time <= now)
+ {
+ DBGL(DL_BDGT, (log(LL_DBG, "%s: new cout-budget-period (%d s, %d left)",
+ cep->name, cep->budget_calloutperiod, cep->budget_calloutncalls_cnt)));
+ cep->budget_calloutperiod_time = now + cep->budget_calloutperiod;
+ cep->budget_calloutncalls_cnt = cep->budget_calloutncalls;
+ }
+ }
+
+ switch(cep->cdid)
+ {
+ case CDID_UNUSED: /* entry unused */
+ continue;
+ break;
+
+ case CDID_RESERVED: /* entry reserved */
+ handle_reserved(cep, now);
+ break;
+
+ default: /* entry in use */
+ handle_active(cep, now);
+ break;
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * timeout, recovery and retry handling for active entry
+ *---------------------------------------------------------------------------*/
+static void
+handle_active(cfg_entry_t *cep, time_t now)
+{
+ switch(cep->state)
+ {
+ case ST_ACCEPTED:
+ if(cep->timerval && (--(cep->timerremain)) <= 0)
+ {
+ DBGL(DL_RCVRY, (log(LL_DBG, "handle_active: entry %s, TIMEOUT !!!", cep->name)));
+ cep->timerval = cep->timerremain = 0;
+ next_state(cep, EV_TIMO);
+ }
+ break;
+
+ case ST_ALERT:
+ if(cep->alert_time > 0)
+ {
+ cep->alert_time--;
+ }
+ else
+ {
+ log(LL_CHD, "%05d %s answering: incoming call from %s to %s",
+ cep->cdid, cep->name,
+ cep->real_phone_incoming.number,
+ cep->local_phone_incoming.number);
+ next_state(cep, EV_MCI);
+ }
+ break;
+
+ case ST_ILL:
+ recover_illegal(cep);
+ break;
+
+ default:
+ /* check hangup flag: if active, close connection */
+
+ if(cep->hangup)
+ {
+ DBGL(DL_RCVRY, (log(LL_DBG, "handle_active: entry %s, hangup request!", cep->name)));
+ cep->hangup = 0;
+ next_state(cep, EV_DRQ);
+ }
+
+ /* check maximum connect time reached */
+
+ if(cep->maxconnecttime > 0 && cep->connect_time > 0)
+ {
+ int connecttime = (int)difftime(now, cep->connect_time);
+ if(connecttime > cep->maxconnecttime)
+ {
+ DBGL(DL_RCVRY, (log(LL_DBG,
+ "handle_active: entry %s, maxconnecttime %d reached!",
+ cep->name, cep->maxconnecttime)));
+ next_state(cep, EV_DRQ);
+ }
+ }
+
+ /*
+ * if shorthold mode is rates based, check if
+ * we entered a time with a new unit length
+ */
+
+ if(cep->unitlengthsrc == ULSRC_RATE)
+ {
+ int connecttime = (int)difftime(now, cep->connect_time);
+
+ if((connecttime > 1) &&
+ (connecttime % 60))
+ {
+ int newrate = get_current_rate(cep, 0);
+
+ if(newrate != cep->unitlength)
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "handle_active: rates unit length updated %d -> %d", cep->unitlength, newrate)));
+
+ cep->unitlength = newrate;
+
+ unitlen_chkupd(cep);
+ }
+ }
+ }
+ break;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * timeout, recovery and retry handling for reserved entry
+ *---------------------------------------------------------------------------*/
+static void
+handle_reserved(cfg_entry_t *cep, time_t now)
+{
+ time_t waittime;
+
+ switch(cep->state)
+ {
+ case ST_DIALRTMRCHD: /* wait for dial retry time reached */
+
+ if(cep->dialrandincr)
+ waittime = cep->randomtime;
+ else
+ waittime = cep->recoverytime;
+
+
+ if(now > (cep->last_dial_time + waittime))
+ {
+ DBGL(DL_RCVRY, (log(LL_DBG, "handle_reserved: entry %s, dial retry request!", cep->name)));
+ cep->state = ST_DIALRETRY;
+
+ if((cep->cdid = get_cdid()) == 0)
+ {
+ log(LL_ERR, "handle_reserved: dialretry get_cdid() returned 0!");
+ cep->state = ST_IDLE;
+ cep->cdid = CDID_UNUSED;
+ return;
+ }
+
+ if((setup_dialout(cep)) == GOOD)
+ {
+ sendm_connect_req(cep);
+ }
+ else
+ {
+ log(LL_ERR, "handle_reserved: dialretry setup_dialout returned ERROR!");
+ cep->state = ST_IDLE;
+ cep->cdid = CDID_UNUSED;
+ return;
+ }
+ }
+ break;
+
+
+ case ST_ACB_WAITDIAL: /* active callback wait for time between disconnect and dial */
+
+ if(now > (cep->last_release_time + cep->callbackwait))
+ {
+ DBGL(DL_RCVRY, (log(LL_DBG, "handle_reserved: entry %s, callback dial!", cep->name)));
+ cep->state = ST_ACB_DIAL;
+
+ if((cep->cdid = get_cdid()) == 0)
+ {
+ log(LL_ERR, "handle_reserved: callback get_cdid() returned 0!");
+ cep->state = ST_IDLE;
+ cep->cdid = CDID_UNUSED;
+ return;
+ }
+
+ select_first_dialno(cep);
+
+ if((setup_dialout(cep)) == GOOD)
+ {
+ sendm_connect_req(cep);
+ }
+ else
+ {
+ log(LL_ERR, "handle_reserved: callback setup_dialout returned ERROR!");
+ cep->state = ST_IDLE;
+ cep->cdid = CDID_UNUSED;
+ return;
+ }
+ }
+ break;
+
+ case ST_ACB_DIALFAIL: /* callback to remote failed */
+
+ if(cep->dialrandincr)
+ waittime = cep->randomtime + cep->recoverytime;
+ else
+ waittime = cep->recoverytime;
+
+ if(now > (cep->last_release_time + waittime))
+ {
+ DBGL(DL_RCVRY, (log(LL_DBG, "handle_reserved: entry %s, callback dial retry request!", cep->name)));
+ cep->state = ST_ACB_DIAL;
+
+ if((cep->cdid = get_cdid()) == 0)
+ {
+ log(LL_ERR, "handle_reserved: callback dialretry get_cdid() returned 0!");
+ cep->state = ST_IDLE;
+ cep->cdid = CDID_UNUSED;
+ return;
+ }
+
+ if((setup_dialout(cep)) == GOOD)
+ {
+ sendm_connect_req(cep);
+ }
+ else
+ {
+ log(LL_ERR, "handle_reserved: callback dialretry setup_dialout returned ERROR!");
+ cep->state = ST_IDLE;
+ cep->cdid = CDID_UNUSED;
+ return;
+ }
+ }
+ break;
+
+ case ST_PCB_WAITCALL: /* wait for remote calling back */
+
+ if(now > (cep->last_release_time + cep->calledbackwait))
+ {
+ cep->dial_count++;
+
+ if(cep->dial_count < cep->dialretries)
+ {
+ /* inside normal retry cycle */
+
+ DBGL(DL_RCVRY, (log(LL_DBG, "handle_reserved: entry %s, retry calledback dial #%d!",
+ cep->name, cep->dial_count)));
+ cep->state = ST_PCB_DIAL;
+
+ if((cep->cdid = get_cdid()) == 0)
+ {
+ log(LL_ERR, "handle_reserved: calledback get_cdid() returned 0!");
+ cep->state = ST_IDLE;
+ cep->cdid = CDID_UNUSED;
+ return;
+ }
+ select_next_dialno(cep);
+
+ if((setup_dialout(cep)) == GOOD)
+ {
+ sendm_connect_req(cep);
+ }
+ else
+ {
+ log(LL_ERR, "handle_reserved: calledback setup_dialout returned ERROR!");
+ cep->state = ST_IDLE;
+ cep->cdid = CDID_UNUSED;
+ return;
+ }
+ }
+ else
+ {
+ /* retries exhausted */
+
+ DBGL(DL_RCVRY, (log(LL_DBG, "handle_reserved: calledback dial retries exhausted")));
+ dialresponse(cep, DSTAT_TFAIL);
+ cep->cdid = CDID_UNUSED;
+ cep->dial_count = 0;
+ cep->state = ST_IDLE;
+ }
+ }
+ break;
+
+ case ST_DOWN: /* interface was taken down */
+
+ if(now > (cep->went_down_time + cep->downtime))
+ {
+ DBGL(DL_RCVRY, (log(LL_DBG, "handle_reserved: taking %s%d up", bdrivername(cep->usrdevicename), cep->usrdeviceunit)));
+ if_up(cep);
+ cep->state = ST_IDLE;
+ cep->cdid = CDID_UNUSED;
+ }
+ break;
+
+ case ST_ILL: /* illegal state reached, recover ! */
+
+ recover_illegal(cep);
+ break;
+ }
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdndebug/Makefile b/usr.sbin/i4b/isdndebug/Makefile
new file mode 100644
index 0000000..5a31de9
--- /dev/null
+++ b/usr.sbin/i4b/isdndebug/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+PROG= isdndebug
+MAN= isdndebug.8
+SRCS= main.c
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/i4b/isdndebug/isdndebug.8 b/usr.sbin/i4b/isdndebug/isdndebug.8
new file mode 100644
index 0000000..0db47d0
--- /dev/null
+++ b/usr.sbin/i4b/isdndebug/isdndebug.8
@@ -0,0 +1,108 @@
+.\"
+.\" Copyright (c) 1997, 2000 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: isdndebug.8,v 1.11 2000/05/31 08:15:29 hm Exp $
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Wed May 31 10:15:07 2000]
+.\"
+.Dd May 31, 2000
+.Dt ISDNDEBUG 8
+.Os
+.Sh NAME
+.Nm isdndebug
+.Nd display and control isdn4bsd kernel variables and statistics
+.Sh SYNOPSIS
+.Nm
+.Op Fl c
+.Op Fl e
+.Op Fl g
+.Op Fl l Ar layer
+.Op Fl m
+.Op Fl q
+.Op Fl r
+.Op Fl s Ar value
+.Op Fl u Ar unit
+.Op Fl z
+.Op Fl C
+.Op Fl Q
+.Sh DESCRIPTION
+.Nm
+is part of the isdn4bsd package and is used to control the level of
+debugging output of the isdn4bsd kernel part.
+Every layer of the isdn4bsd kernel uses a debugging mask which can be
+manipulated using this utility.
+.Pp
+A second usage of
+.Nm
+is to display and reset chipset specific statistics and/or error counters
+and and to display and reset the D-channel layer 2
+(Q.921 LAPD protocol) statistics and error counters.
+.Pp
+The following options are available:
+.Bl -tag -width Ds
+.It Fl c
+Display chipset specific statistics and/or error counters.
+.It Fl e
+Set debugging mask for the selected layer(s) to display errors only.
+.It Fl g
+Get the debugging mask for the selected layer(s).
+.It Fl l
+Specify the layer for which a command applies. Default is all layers.
+.It Fl m
+Set debugging mask for the selected layer(s) to display all possible
+debugging messages (maximum output).
+.It Fl q
+Display the Q.921 (D-channel layer 2) frame receive/transmit statistics.
+.It Fl r
+Set debugging mask for the selected layer(s) to the compiled in default
+(reset).
+.It Fl s
+Set debugging mask for the selected layer(s) to value. Value can be
+specified in any number base supported by
+.Xr sscanf 3 .
+.It Fl u
+Set the unit numbers for the -c, -q, -C and -Q flags.
+.It Fl z
+Set debugging mask for the selected layer(s) to no output at all (zero).
+.It Fl H
+Reset the chipset specific statistics and/or error counters to zero.
+.It Fl Q
+Reset the Q.921 (D-channel layer 2) frame receive/transmit statistics to zero.
+.El
+.Sh FILES
+/dev/i4bctl
+.Sh EXAMPLES
+The command:
+.Bd -literal -offset indent
+isdndebug -g
+.Ed
+.Pp
+displays the current debugging level for all ISDN layers
+.Sh AUTHORS
+The
+.Nm
+utility and this manpage were written by
+.An Hellmuth Michaelis Aq hm@kts.org .
diff --git a/usr.sbin/i4b/isdndebug/main.c b/usr.sbin/i4b/isdndebug/main.c
new file mode 100644
index 0000000..0620b3b
--- /dev/null
+++ b/usr.sbin/i4b/isdndebug/main.c
@@ -0,0 +1,633 @@
+/*
+ * Copyright (c) 1997, 2001 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * main.c - i4b set debug options
+ * ------------------------------
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon May 21 10:09:23 2001]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <signal.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <machine/i4b_debug.h>
+#include <machine/i4b_ioctl.h>
+
+char *bin_str(unsigned long val, int length);
+
+static void usage ( void );
+void printl1(unsigned long val);
+void printl2(unsigned long val);
+void printl3(unsigned long val);
+void printl4(unsigned long val);
+
+static int isdnfd;
+
+#define I4BCTLDEVICE "/dev/i4bctl"
+
+int opt_get = 0;
+int opt_layer = -1;
+int opt_set = 0;
+int opt_setval;
+int opt_reset = 0;
+int opt_max = 0;
+int opt_err = 0;
+int opt_zero = 0;
+int opt_unit = 0;
+int opt_lapd = 0;
+int opt_rlapd = 0;
+int opt_chipstat = 0;
+
+/*---------------------------------------------------------------------------*
+ * usage display and exit
+ *---------------------------------------------------------------------------*/
+static void
+usage(void)
+{
+ fprintf(stderr, "\n");
+ fprintf(stderr, "isdndebug - i4b set debug level, version %d.%d.%d, compiled %s %s\n", VERSION, REL, STEP, __DATE__, __TIME__);
+ fprintf(stderr, "usage: isdndebug -c -e -g -l <layer> -m -q -r -s <value> -u <unit> -z -C -Q\n");
+ fprintf(stderr, " -c get chipset statistics\n");
+ fprintf(stderr, " -e set error only debugging output\n");
+ fprintf(stderr, " -g get current debugging values\n");
+ fprintf(stderr, " -l layer specify layer (1...4)\n");
+ fprintf(stderr, " -m set maximum debugging output\n");
+ fprintf(stderr, " -q get Q.921 statistics\n");
+ fprintf(stderr, " -r reset values(s) to compiled in default\n");
+ fprintf(stderr, " -s value set new debugging value for layer\n");
+ fprintf(stderr, " -u unit unit number for -c, -q, -C and -Q commands\n");
+ fprintf(stderr, " -z set zero (=no) debugging output\n");
+ fprintf(stderr, " -C reset chipset statistics\n");
+ fprintf(stderr, " -Q reset Q.921 statistics\n");
+ fprintf(stderr, "\n");
+ exit(1);
+}
+
+/*---------------------------------------------------------------------------*
+ * program entry
+ *---------------------------------------------------------------------------*/
+int
+main(int argc, char **argv)
+{
+ int c;
+ ctl_debug_t cdbg;
+ int ret;
+
+ while ((c = getopt(argc, argv, "ceghl:mqrs:u:zCHQ")) != -1)
+ {
+ switch(c)
+ {
+ case 'c':
+ opt_chipstat = 1;
+ break;
+
+ case 'e':
+ opt_err = 1;
+ break;
+
+ case 'g':
+ opt_get = 1;
+ break;
+
+ case 'q':
+ opt_lapd = 1;
+ break;
+
+ case 'r':
+ opt_reset = 1;
+ break;
+
+ case 'm':
+ opt_max = 1;
+ break;
+
+ case 'l':
+ opt_layer = atoi(optarg);
+ if(opt_layer < 1 || opt_layer > 4)
+ usage();
+ break;
+
+ case 's':
+ if((sscanf(optarg, "%i", &opt_setval)) != 1)
+ usage();
+ opt_set = 1;
+ break;
+
+ case 'u':
+ opt_unit = atoi(optarg);
+ if(opt_unit < 0 || opt_unit > 9)
+ usage();
+ break;
+
+ case 'z':
+ opt_zero = 1;
+ break;
+
+ case 'Q':
+ opt_rlapd = 1;
+ break;
+
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if(opt_get == 0 && opt_set == 0 && opt_reset == 0 && opt_max == 0 &&
+ opt_err == 0 && opt_zero == 0 && opt_lapd == 0 && opt_rlapd == 0 &&
+ opt_chipstat == 0)
+ {
+ usage();
+ }
+
+ if((opt_get + opt_set + opt_reset + opt_max + opt_err + opt_zero +
+ opt_lapd + opt_rlapd + opt_chipstat) > 1)
+ {
+ usage();
+ }
+
+ if((isdnfd = open(I4BCTLDEVICE, O_RDWR)) < 0)
+ {
+ fprintf(stderr, "i4bctl: cannot open %s: %s\n", I4BCTLDEVICE, strerror(errno));
+ exit(1);
+ }
+
+ if(opt_chipstat)
+ {
+ struct chipstat cst;
+ u_char *name;
+
+ cst.driver_unit = opt_unit;
+ cst.driver_bchannel = 0;
+
+ if((ret = ioctl(isdnfd, I4B_CTL_GET_CHIPSTAT, &cst)) < 0)
+ {
+ fprintf(stderr, "ioctl I4B_CTL_GET_CHIPSTAT failed: %s", strerror(errno));
+ exit(1);
+ }
+
+ switch(cst.driver_type)
+ {
+ case L1DRVR_ISIC:
+ name = "isic";
+ printf("\nisic-driver\nHSCX events: VFR RDO CRC RAB XDU RFO\n");
+
+ printf("unit %d chan %d: %6d %6d %6d %6d %6d %6d\n",
+ cst.stats.hscxstat.unit,
+ cst.stats.hscxstat.chan,
+ cst.stats.hscxstat.vfr,
+ cst.stats.hscxstat.rdo,
+ cst.stats.hscxstat.crc,
+ cst.stats.hscxstat.rab,
+ cst.stats.hscxstat.xdu,
+ cst.stats.hscxstat.rfo);
+
+ cst.driver_unit = opt_unit;
+ cst.driver_bchannel = 1;
+
+ if((ret = ioctl(isdnfd, I4B_CTL_GET_CHIPSTAT, &cst)) < 0)
+ {
+ fprintf(stderr, "ioctl I4B_CTL_GET_CHIPSTAT failed: %s", strerror(errno));
+ exit(1);
+ }
+
+ printf("HSCX events: VFR RDO CRC RAB XDU RFO\n");
+
+ printf("unit %d chan %d: %6d %6d %6d %6d %6d %6d\n",
+ cst.stats.hscxstat.unit,
+ cst.stats.hscxstat.chan,
+ cst.stats.hscxstat.vfr,
+ cst.stats.hscxstat.rdo,
+ cst.stats.hscxstat.crc,
+ cst.stats.hscxstat.rab,
+ cst.stats.hscxstat.xdu,
+ cst.stats.hscxstat.rfo);
+
+ break;
+
+ case L1DRVR_IWIC:
+ name = "iwic";
+ break;
+
+ case L1DRVR_IFPI:
+ name = "ifpi";
+ break;
+
+ case L1DRVR_IHFC:
+ name = "ihfc";
+ break;
+
+ case L1DRVR_IFPNP:
+ name = "ifpnp";
+ break;
+
+ default:
+ fprintf(stderr, "ioctl I4B_CTL_GET_CHIPSTAT, unknown driver %d\n",cst.driver_type);
+ exit(1);
+ break;
+ }
+ exit(0);
+ }
+
+ if(opt_lapd)
+ {
+ l2stat_t l2s;
+
+ l2s.unit = opt_unit;
+
+ if((ret = ioctl(isdnfd, I4B_CTL_GET_LAPDSTAT, &l2s)) < 0)
+ {
+ fprintf(stderr, "ioctl I4B_CTL_GET_LAPDSTAT failed: %s", strerror(errno));
+ exit(1);
+ }
+
+ printf("unit %d Q.921 statistics: receive transmit\n", opt_unit);
+ printf("---------------------------------------------\n");
+ printf("# of I-frames %12lu %12lu\n", l2s.lapdstat.rx_i, l2s.lapdstat.tx_i);
+ printf("# of RR-frames %12lu %12lu\n", l2s.lapdstat.rx_rr, l2s.lapdstat.tx_rr);
+ printf("# of RNR-frames %12lu %12lu\n", l2s.lapdstat.rx_rnr, l2s.lapdstat.tx_rnr);
+ printf("# of REJ-frames %12lu %12lu\n", l2s.lapdstat.rx_rej, l2s.lapdstat.tx_rej);
+ printf("# of SABME-frames %12lu %12lu\n", l2s.lapdstat.rx_sabme, l2s.lapdstat.tx_sabme);
+ printf("# of DM-frames %12lu %12lu\n", l2s.lapdstat.rx_dm, l2s.lapdstat.tx_dm);
+ printf("# of DISC-frames %12lu %12lu\n", l2s.lapdstat.rx_disc, l2s.lapdstat.tx_disc);
+ printf("# of UA-frames %12lu %12lu\n", l2s.lapdstat.rx_ua, l2s.lapdstat.tx_ua);
+ printf("# of FRMR-frames %12lu %12lu\n", l2s.lapdstat.rx_frmr, l2s.lapdstat.tx_frmr);
+ printf("# of TEI-frames %12lu %12lu\n", l2s.lapdstat.rx_tei, l2s.lapdstat.tx_tei);
+ printf("# of UI-frames %12lu \n", l2s.lapdstat.rx_ui);
+ printf("# of XID-frames %12lu \n", l2s.lapdstat.rx_xid);
+ printf(" errors\n");
+ printf("---------------------------------------------\n");
+ printf("# of frames with incorrect length%12lu\n", l2s.lapdstat.err_rx_len);
+ printf("# of frames with bad frame type %12lu\n", l2s.lapdstat.err_rx_badf);
+ printf("# of bad S frames %12lu\n", l2s.lapdstat.err_rx_bads);
+ printf("# of bad U frames %12lu\n", l2s.lapdstat.err_rx_badu);
+ printf("# of bad UI frames %12lu\n", l2s.lapdstat.err_rx_badui);
+
+ exit(0);
+ }
+
+ if(opt_rlapd)
+ {
+ int unit;
+
+ unit = opt_unit;
+
+ if((ret = ioctl(isdnfd, I4B_CTL_CLR_LAPDSTAT, &unit)) < 0)
+ {
+ fprintf(stderr, "ioctl I4B_CTL_CLR_LAPDSTAT failed: %s", strerror(errno));
+ exit(1);
+ }
+
+ printf("Q.921 statistics counters unit %d reset to zero!\n", unit);
+ exit(0);
+ }
+
+ if((ret = ioctl(isdnfd, I4B_CTL_GET_DEBUG, &cdbg)) < 0)
+ {
+ fprintf(stderr, "ioctl I4B_CTL_GET_DEBUG failed: %s", strerror(errno));
+ exit(1);
+ }
+
+ if(opt_get)
+ {
+ switch(opt_layer)
+ {
+ case -1:
+ printl1(cdbg.l1);
+ printl2(cdbg.l2);
+ printl3(cdbg.l3);
+ printl4(cdbg.l4);
+ break;
+
+ case 1:
+ printl1(cdbg.l1);
+ break;
+
+ case 2:
+ printl2(cdbg.l2);
+ break;
+
+ case 3:
+ printl3(cdbg.l3);
+ break;
+
+ case 4:
+ printl4(cdbg.l4);
+ break;
+ }
+ printf("\n");
+ return(0);
+ }
+ else if(opt_set)
+ {
+ switch(opt_layer)
+ {
+ case -1:
+ usage();
+ break;
+
+ case 1:
+ cdbg.l1 = opt_setval;
+ break;
+
+ case 2:
+ cdbg.l2 = opt_setval;
+ break;
+
+ case 3:
+ cdbg.l3 = opt_setval;
+ break;
+
+ case 4:
+ cdbg.l4 = opt_setval;
+ break;
+ }
+ }
+ else if(opt_reset)
+ {
+ switch(opt_layer)
+ {
+ case -1:
+ cdbg.l1 = L1_DEBUG_DEFAULT;
+ cdbg.l2 = L2_DEBUG_DEFAULT;
+ cdbg.l3 = L3_DEBUG_DEFAULT;
+ cdbg.l4 = L4_DEBUG_DEFAULT;
+ break;
+
+ case 1:
+ cdbg.l1 = L1_DEBUG_DEFAULT;
+ break;
+
+ case 2:
+ cdbg.l2 = L2_DEBUG_DEFAULT;
+ break;
+
+ case 3:
+ cdbg.l3 = L3_DEBUG_DEFAULT;
+ break;
+
+ case 4:
+ cdbg.l4 = L4_DEBUG_DEFAULT;
+ break;
+ }
+ }
+ else if(opt_max)
+ {
+ switch(opt_layer)
+ {
+ case -1:
+ cdbg.l1 = L1_DEBUG_MAX;
+ cdbg.l2 = L2_DEBUG_MAX;
+ cdbg.l3 = L3_DEBUG_MAX;
+ cdbg.l4 = L4_DEBUG_MAX;
+ break;
+
+ case 1:
+ cdbg.l1 = L1_DEBUG_MAX;
+ break;
+
+ case 2:
+ cdbg.l2 = L2_DEBUG_MAX;
+ break;
+
+ case 3:
+ cdbg.l3 = L3_DEBUG_MAX;
+ break;
+
+ case 4:
+ cdbg.l4 = L4_DEBUG_MAX;
+ break;
+ }
+ }
+ else if(opt_err)
+ {
+ switch(opt_layer)
+ {
+ case -1:
+ cdbg.l1 = L1_DEBUG_ERR;
+ cdbg.l2 = L2_DEBUG_ERR;
+ cdbg.l3 = L3_DEBUG_ERR;
+ cdbg.l4 = L4_DEBUG_ERR;
+ break;
+
+ case 1:
+ cdbg.l1 = L1_DEBUG_ERR;
+ break;
+
+ case 2:
+ cdbg.l2 = L2_DEBUG_ERR;
+ break;
+
+ case 3:
+ cdbg.l3 = L3_DEBUG_ERR;
+ break;
+
+ case 4:
+ cdbg.l4 = L4_DEBUG_ERR;
+ break;
+ }
+ }
+ else if(opt_zero)
+ {
+ switch(opt_layer)
+ {
+ case -1:
+ cdbg.l1 = 0;
+ cdbg.l2 = 0;
+ cdbg.l3 = 0;
+ cdbg.l4 = 0;
+ break;
+
+ case 1:
+ cdbg.l1 = 0;
+ break;
+
+ case 2:
+ cdbg.l2 = 0;
+ break;
+
+ case 3:
+ cdbg.l3 = 0;
+ break;
+
+ case 4:
+ cdbg.l4 = 0;
+ break;
+ }
+ }
+ else
+ {
+ exit(1);
+ }
+
+ if((ret = ioctl(isdnfd, I4B_CTL_SET_DEBUG, &cdbg)) < 0)
+ {
+ fprintf(stderr, "ioctl I4B_CTL_SET_DEBUG failed: %s", strerror(errno));
+ exit(1);
+ }
+ return(0);
+}
+
+/*---------------------------------------------------------------------------*
+ * return ptr to string of 1's and 0's for value
+ *---------------------------------------------------------------------------*/
+char *
+bin_str(unsigned long val, int length)
+{
+ static char buffer[80];
+ int i = 0;
+
+ if (length > 32)
+ length = 32;
+
+ val = val << (32 - length);
+
+ while (length--)
+ {
+ if (val & 0x80000000)
+ buffer[i++] = '1';
+ else
+ buffer[i++] = '0';
+ if ((length % 4) == 0 && length)
+ buffer[i++] = '.';
+ val = val << 1;
+ }
+ return (buffer);
+}
+
+/*---------------------------------------------------------------------------*
+ * print l1 info
+ *---------------------------------------------------------------------------*/
+void
+printl1(unsigned long val)
+{
+ printf("\nLayer 1: %s = 0x%lX\n", bin_str(val, 32), val);
+ printf(" | |||| |||| |||| ||||\n"),
+ printf(" | |||| |||| |||| |||+- general error messages\n");
+ printf(" | |||| |||| |||| ||+-- PH primitives exchanged\n");
+ printf(" | |||| |||| |||| |+--- B channel actions\n");
+ printf(" | |||| |||| |||| +---- HSCX error messages\n");
+ printf(" | |||| |||| |||+------ HSCX IRQ messages\n");
+ printf(" | |||| |||| ||+------- ISAC error messages\n");
+ printf(" | |||| |||| |+-------- ISAC messages\n");
+ printf(" | |||| |||| +--------- ISAC setup messages\n");
+ printf(" | |||| |||+----------- FSM general messages\n");
+ printf(" | |||| ||+------------ FSM error messages\n");
+ printf(" | |||| |+------------- timer general messages\n");
+ printf(" | |||| +-------------- timer error messages\n");
+ printf(" | |||+---------------- HSCX data xfer errors msgs\n");
+ printf(" | ||+----------------- ISAC CICO messages\n");
+ printf(" | |+------------------ silent messages (soft-HDLC)\n");
+ printf(" | +------------------- error messages (soft-HDLC)\n");
+ printf(" +--------------------- HFC-S PCI debug messages\n");
+ printf(" ++++-++++-++++-+++---------------------- unassigned\n");
+}
+
+/*---------------------------------------------------------------------------*
+ * print l2 info
+ *---------------------------------------------------------------------------*/
+void
+printl2(unsigned long val)
+{
+ printf("\nLayer 2: %s = 0x%lX\n", bin_str(val, 32), val);
+ printf(" || |||| |||| ||||\n"),
+ printf(" || |||| |||| |||+- general error messages\n");
+ printf(" || |||| |||| ||+-- DL primitives exchanged\n");
+ printf(" || |||| |||| |+--- U frame messages\n");
+ printf(" || |||| |||| +---- U frame error messages\n");
+ printf(" || |||| |||+------ S frame messages\n");
+ printf(" || |||| ||+------- S frame error messages\n");
+ printf(" || |||| |+-------- I frame messages\n");
+ printf(" || |||| +--------- I frame error messages\n");
+ printf(" || |||+----------- FSM general messages\n");
+ printf(" || ||+------------ FSM error messages\n");
+ printf(" || |+------------- timer general messages\n");
+ printf(" || +-------------- timer error messages\n");
+ printf(" |+---------------- TEI general messages\n");
+ printf(" +----------------- TEI error messages\n");
+ printf(" ++++-++++-++++-++++-++------------------ unassigned\n");
+}
+
+/*---------------------------------------------------------------------------*
+ * print l3 info
+ *---------------------------------------------------------------------------*/
+void
+printl3(unsigned long val)
+{
+ printf("\nLayer 3: %s = 0x%lX\n", bin_str(val, 32), val);
+ printf(" ||| |||| ||||\n"),
+ printf(" ||| |||| |||+- general error messages\n");
+ printf(" ||| |||| ||+-- general messages\n");
+ printf(" ||| |||| |+--- FSM messages\n");
+ printf(" ||| |||| +---- FSM error messages\n");
+ printf(" ||| |||+------ timer messages\n");
+ printf(" ||| ||+------- timer error messages\n");
+ printf(" ||| |+-------- protocol messages\n");
+ printf(" ||| +--------- protocol error messages\n");
+ printf(" ||+----------- facility messages\n");
+ printf(" |+------------ facility error messages\n");
+ printf(" +------------- Q.931 messages exchanged\n");
+ printf(" ++++-++++-++++-++++-++++-+-------------- unassigned\n");
+}
+
+/*---------------------------------------------------------------------------*
+ * print l4 info
+ *---------------------------------------------------------------------------*/
+void
+printl4(unsigned long val)
+{
+ printf("\nLayer 4: %s = 0x%lX\n", bin_str(val, 32), val);
+ printf(" ||| |||| ||||\n"),
+ printf(" ||| |||| |||+- general error messages\n");
+ printf(" ||| |||| ||+-- general messages\n");
+ printf(" ||| |||| |+--- B-ch timeout messages\n");
+ printf(" ||| |||| +---- network driver dial state\n");
+ printf(" ||| |||+------ ipr driver debug messages\n");
+ printf(" ||| ||+------- rbch driver debug messages\n");
+ printf(" ||| |+-------- isp driver debug messages\n");
+ printf(" ||| +--------- tel driver debug messages\n");
+ printf(" ||+----------- ing driver debug messages\n");
+ printf(" |+------------ iavc driver debug messages\n");
+ printf(" +------------- capi driver debug messages\n");
+ printf(" ++++-++++-++++-++++-++++---------------- unassigned\n");
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdndecode/Makefile b/usr.sbin/i4b/isdndecode/Makefile
new file mode 100644
index 0000000..9dbd971
--- /dev/null
+++ b/usr.sbin/i4b/isdndecode/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= isdndecode
+MAN= isdndecode.8
+SRCS= main.c layer1.c layer2.c layer3.c layer3_subr.c facility.c pcause.c
+
+.include <bsd.prog.mk>
+
diff --git a/usr.sbin/i4b/isdndecode/decode.h b/usr.sbin/i4b/isdndecode/decode.h
new file mode 100644
index 0000000..83bab00
--- /dev/null
+++ b/usr.sbin/i4b/isdndecode/decode.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * decode.h - isdndecode header file
+ * ---------------------------------
+ *
+ * $Id: decode.h,v 1.6 1999/12/13 21:25:25 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:49:50 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/uio.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+
+#include <machine/i4b_ioctl.h>
+#include <machine/i4b_trace.h>
+
+#include "pcause.h"
+
+#define I4BTRC_DEVICE "/dev/i4btrc" /* trace device file */
+#define DECODE_FILE_NAME "isdndecode" /* default output filename */
+#define DECODE_FILE_NAME_BAK ".last" /* backup filename trailer */
+#define BIN_FILE_NAME "isdntracebin" /* default binary filename */
+
+#define BSIZE 4096 /* read buffer size */
+#define NCOLS 80 /* screen width */
+
+#define RxUDEF 0 /* analyze mode, default unit for receiver side */
+#define TxUDEF 1 /* analyze mode, default unit for transmitter side */
+
+void layer1(char *pbuf, unsigned char *buf);
+int layer2(char *pbuf, unsigned char *buf, int is_te, int printit);
+void layer3(char *pbuf, int n, int off, unsigned char *buf);
+int q932_facility(char *pbuf, unsigned char *buf);
+void sprintline(int, char *, int, int, int, const char *, ...);
+void extension(int, char *, int, unsigned char, unsigned char);
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdndecode/facility.c b/usr.sbin/i4b/isdndecode/facility.c
new file mode 100644
index 0000000..c4502a8
--- /dev/null
+++ b/usr.sbin/i4b/isdndecode/facility.c
@@ -0,0 +1,1111 @@
+/*
+ * Copyright (c) 1997, 2000 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * facility.c - decode Q.932 facilities
+ * ------------------------------------
+ *
+ * $Id: facility.c,v 1.5 2000/02/21 15:17:17 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Feb 21 16:15:43 2000]
+ *
+ *---------------------------------------------------------------------------
+ *
+ * - Q.932 (03/93) Generic Procedures for the Control of
+ * ISDN Supplementaty Services
+ * - Q.950 (03/93) Supplementary Services Protocols, Structure and
+ * General Principles
+ * - ETS 300 179 (10/92) Advice Of Charge: charging information during
+ * the call (AOC-D) supplementary service Service description
+ * - ETS 300 180 (10/92) Advice Of Charge: charging information at the
+ * end of call (AOC-E) supplementary service Service description
+ * - ETS 300 181 (04/93) Advice Of Charge (AOC) supplementary service
+ * Functional capabilities and information flows
+ * - ETS 300 182 (04/93) Advice Of Charge (AOC) supplementary service
+ * Digital Subscriber Signalling System No. one (DSS1) protocol
+ * - X.208 Specification of Abstract Syntax Notation One (ASN.1)
+ * - X.209 Specification of Basic Encoding Rules for
+ * Abstract Syntax Notation One (ASN.1)
+ * - "ASN.1 Abstract Syntax Notation One", Walter Gora, DATACOM-Verlag
+ * 1992, 3rd Edition (ISBN 3-89238-062-7) (german !)
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "decode.h"
+#include "facility.h"
+
+static int do_component(int length, char *pbuf);
+static char *uni_str(int code);
+static char *opval_str(int val);
+static char *bid_str(int val);
+static void next_state(char *pbuf, int class, int form, int code, int val);
+
+static int byte_len;
+static unsigned char *byte_buf;
+static int state;
+
+/*---------------------------------------------------------------------------*
+ * decode Q.931/Q.932 facility info element
+ *---------------------------------------------------------------------------*/
+int
+q932_facility(char *pbuf, unsigned char *buf)
+{
+ int len;
+
+ sprintf((pbuf+strlen(pbuf)), "[facility (Q.932): ");
+
+ buf++; /* length */
+
+ len = *buf;
+
+ buf++; /* protocol profile */
+
+ sprintf((pbuf+strlen(pbuf)), "Protocol=");
+
+ switch(*buf & 0x1f)
+ {
+ case FAC_PROTO_ROP:
+ sprintf((pbuf+strlen(pbuf)), "Remote Operations Protocol\n");
+ break;
+
+ case FAC_PROTO_CMIP:
+ sprintf((pbuf+strlen(pbuf)), "CMIP Protocol (Q.941), UNSUPPORTED!\n");
+ return(len+2);
+ break;
+
+ case FAC_PROTO_ACSE:
+ sprintf((pbuf+strlen(pbuf)), "ACSE Protocol (X.217/X.227), UNSUPPORTED!\n");
+ return(len+2);
+ break;
+
+ default:
+ sprintf((pbuf+strlen(pbuf)), "Unknown Protocol (val = 0x%x), UNSUPPORTED!\n", *buf & 0x1f);
+ return(len+2);
+ break;
+ }
+
+ /* next byte */
+
+ buf++;
+ len--;
+
+ /* initialize variables for do_component */
+
+ byte_len = 0;
+ byte_buf = buf;
+ state = ST_EXP_COMP_TYP;
+
+ /* decode facility */
+
+ do_component(len, pbuf);
+
+ sprintf((pbuf+(strlen(pbuf)-1)), "]"); /* XXX replace last newline */
+
+ return(len+3);
+}
+
+/*---------------------------------------------------------------------------*
+ * handle a component recursively
+ *---------------------------------------------------------------------------*/
+static int
+do_component(int length, char *pbuf)
+{
+ int comp_tag_class; /* component tag class */
+ int comp_tag_form; /* component form: constructor or primitive */
+ int comp_tag_code; /* component code depending on class */
+ int comp_length = 0; /* component length */
+
+#ifdef FAC_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "ENTER - comp_length = %d, byte_len = %d, length =%d\n", comp_length, byte_len, length);
+#endif
+
+again:
+
+#ifdef FAC_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "AGAIN - comp_length = %d, byte_len = %d, length =%d\n", comp_length, byte_len, length);
+#endif
+
+ /*----------------------------------------*/
+ /* first component element: component tag */
+ /*----------------------------------------*/
+
+ /* tag class bits */
+
+ sprintf((pbuf+strlen(pbuf)), "\t0x%02x Tag: ", *byte_buf);
+
+ comp_tag_class = (*byte_buf & 0xc0) >> 6;
+
+ switch(comp_tag_class)
+ {
+ case FAC_TAGCLASS_UNI:
+ sprintf((pbuf+strlen(pbuf)), "Universal");
+ break;
+ case FAC_TAGCLASS_APW:
+ sprintf((pbuf+strlen(pbuf)), "Applic-wide");
+ break;
+ case FAC_TAGCLASS_COS:
+ sprintf((pbuf+strlen(pbuf)), "Context-spec");
+ break;
+ case FAC_TAGCLASS_PRU:
+ sprintf((pbuf+strlen(pbuf)), "Private");
+ break;
+ }
+
+ /* tag form bit */
+
+ comp_tag_form = (*byte_buf & 0x20) > 5;
+
+ sprintf((pbuf+strlen(pbuf)), ", ");
+
+ if(comp_tag_form == FAC_TAGFORM_CON)
+ {
+ sprintf((pbuf+strlen(pbuf)), "Constructor");
+ }
+ else
+ {
+ sprintf((pbuf+strlen(pbuf)), "Primitive");
+ }
+
+ /* tag code bits */
+
+ comp_tag_code = *byte_buf & 0x1f;
+
+ sprintf((pbuf+strlen(pbuf)), ", ");
+
+ if(comp_tag_code == 0x1f)
+ {
+ comp_tag_code = 0;
+
+ byte_buf++;
+ byte_len++;
+
+ while(*byte_buf & 0x80)
+ {
+ comp_tag_code += (*byte_buf & 0x7f);
+ byte_buf++;
+ byte_len++;
+ }
+ comp_tag_code += (*byte_buf & 0x7f);
+ sprintf((pbuf+strlen(pbuf)), "%d (ext)\n", comp_tag_code);
+ }
+ else
+ {
+ comp_tag_code = (*byte_buf & 0x1f);
+
+ if(comp_tag_class == FAC_TAGCLASS_UNI)
+ {
+ sprintf((pbuf+strlen(pbuf)), "%s (%d)\n", uni_str(comp_tag_code), comp_tag_code);
+ }
+ else
+ {
+ sprintf((pbuf+strlen(pbuf)), "code = %d\n", comp_tag_code);
+ }
+ }
+
+ byte_buf++;
+ byte_len++;
+
+ /*--------------------------------------------*/
+ /* second component element: component length */
+ /*--------------------------------------------*/
+
+ sprintf((pbuf+strlen(pbuf)), "\t0x%02x Len: ", *byte_buf);
+
+ comp_length = 0;
+
+ if(*byte_buf & 0x80)
+ {
+ int i = *byte_buf & 0x7f;
+
+ byte_len += i;
+
+ for(;i > 0;i++)
+ {
+ byte_buf++;
+ comp_length += (*byte_buf * (i*256));
+ }
+ sprintf((pbuf+strlen(pbuf)), "%d (long form)\n", comp_length);
+ }
+ else
+ {
+ comp_length = *byte_buf & 0x7f;
+ sprintf((pbuf+strlen(pbuf)), "%d (short form)\n", comp_length);
+ }
+
+ next_state(pbuf, comp_tag_class, comp_tag_form, comp_tag_code, -1);
+
+ byte_len++;
+ byte_buf++;
+
+ if(comp_length)
+ {
+
+ /*---------------------------------------------*/
+ /* third component element: component contents */
+ /*---------------------------------------------*/
+
+ if(comp_tag_form) /* == constructor */
+ {
+ do_component(comp_length, pbuf);
+ }
+ else
+ {
+ int val = 0;
+ if(comp_tag_class == FAC_TAGCLASS_UNI)
+ {
+ switch(comp_tag_code)
+ {
+ case FAC_CODEUNI_INT:
+ case FAC_CODEUNI_ENUM:
+ case FAC_CODEUNI_BOOL:
+ if(comp_length)
+ {
+ int i;
+
+ sprintf((pbuf+strlen(pbuf)), "\t");
+
+ for(i = comp_length-1; i >= 0; i--)
+ {
+ sprintf((pbuf+strlen(pbuf)), "0x%02x ", *byte_buf);
+ val += (*byte_buf + (i*255));
+ byte_buf++;
+ byte_len++;
+ if(i)
+ sprintf((pbuf+strlen(pbuf)), "\n\t");
+ }
+ sprintf((pbuf+strlen(pbuf)), "Val: %d\n", val);
+ }
+ break;
+ default:
+ if(comp_length)
+ {
+ int i;
+
+ sprintf((pbuf+strlen(pbuf)), "\t");
+
+ for(i = comp_length-1; i >= 0; i--)
+ {
+ sprintf((pbuf+strlen(pbuf)), "0x%02x = %d", *byte_buf, *byte_buf);
+ if(isprint(*byte_buf))
+ sprintf((pbuf+strlen(pbuf)), " = '%c'", *byte_buf);
+ byte_buf++;
+ byte_len++;
+ if(i)
+ sprintf((pbuf+strlen(pbuf)), "\n\t");
+ }
+ }
+ break;
+ }
+ }
+
+ else /* comp_tag_class != FAC_TAGCLASS_UNI */
+ {
+ if(comp_length)
+ {
+ int i;
+
+ sprintf((pbuf+strlen(pbuf)), "\t");
+
+ for(i = comp_length-1; i >= 0; i--)
+ {
+ sprintf((pbuf+strlen(pbuf)), "0x%02x", *byte_buf);
+ val += (*byte_buf + (i*255));
+ byte_buf++;
+ byte_len++;
+ if(i)
+ sprintf((pbuf+strlen(pbuf)), "\n\t");
+ }
+ sprintf((pbuf+strlen(pbuf)), "\n");
+ }
+ }
+ next_state(pbuf, comp_tag_class, comp_tag_form, comp_tag_code, val);
+ }
+ }
+
+#ifdef FAC_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "PREGOTO - comp_length = %d, byte_len = %d, length =%d\n", comp_length, byte_len, length);
+#endif
+ if(byte_len < length)
+ goto again;
+#ifdef FAC_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "RETURN - comp_length = %d, byte_len = %d, length =%d\n", comp_length, byte_len, length);
+#endif
+ return(byte_len);
+}
+
+/*---------------------------------------------------------------------------*
+ * print universal id type
+ *---------------------------------------------------------------------------*/
+static char *uni_str(int code)
+{
+ static char *tbl[] = {
+ "BOOLEAN",
+ "INTEGER",
+ "BIT STRING",
+ "OCTET STRING",
+ "NULL",
+ "OBJECT IDENTIFIER",
+ "OBJECT DESCRIPTOR",
+ "EXTERNAL",
+ "REAL",
+ "ENUMERATED",
+ "RESERVED11",
+ "RESERVED12",
+ "RESERVED13",
+ "RESERVED14",
+ "RESERVED15",
+ "SEQUENCE",
+ "SET",
+ "NUMERIC STRING",
+ "PRINTABLE STRING",
+ "TELETEX STRING",
+ "ISO646 STRING",
+ "IA5 STRING",
+ "GRAPHIC STRING",
+ "GENERAL STRING"
+ };
+
+ if(code >= 1 && code <= FAC_CODEUNI_GNSTR)
+ return(tbl[code-1]);
+ else
+ return("ERROR, Value out of Range!");
+}
+
+/*---------------------------------------------------------------------------*
+ * print operation value
+ *---------------------------------------------------------------------------*/
+static char *opval_str(int val)
+{
+ static char buffer[80];
+ char *r;
+
+ switch(val)
+ {
+ case FAC_OPVAL_UUS:
+ r = "uUs";
+ break;
+ case FAC_OPVAL_CUG:
+ r = "cUGCall";
+ break;
+ case FAC_OPVAL_MCID:
+ r = "mCIDRequest";
+ break;
+ case FAC_OPVAL_BTPY:
+ r = "beginTPY";
+ break;
+ case FAC_OPVAL_ETPY:
+ r = "endTPY";
+ break;
+ case FAC_OPVAL_ECT:
+ r = "eCTRequest";
+ break;
+ case FAC_OPVAL_DIV_ACT:
+ r = "activationDiversion";
+ break;
+ case FAC_OPVAL_DIV_DEACT:
+ r = "deactivationDiversion";
+ break;
+ case FAC_OPVAL_DIV_ACTSN:
+ r = "activationStatusNotificationDiv";
+ break;
+ case FAC_OPVAL_DIV_DEACTSN:
+ r = "deactivationStatusNotificationDiv";
+ break;
+ case FAC_OPVAL_DIV_INTER:
+ r = "interrogationDiversion";
+ break;
+ case FAC_OPVAL_DIV_INFO:
+ r = "diversionInformation";
+ break;
+ case FAC_OPVAL_DIV_CALLDEF:
+ r = "callDeflection";
+ break;
+ case FAC_OPVAL_DIV_CALLRER:
+ r = "callRerouting";
+ break;
+ case FAC_OPVAL_DIV_LINF2:
+ r = "divertingLegInformation2";
+ break;
+ case FAC_OPVAL_DIV_INVS:
+ r = "invokeStatus";
+ break;
+ case FAC_OPVAL_DIV_INTER1:
+ r = "interrogationDiversion1";
+ break;
+ case FAC_OPVAL_DIV_LINF1:
+ r = "divertingLegInformation1";
+ break;
+ case FAC_OPVAL_DIV_LINF3:
+ r = "divertingLegInformation3";
+ break;
+ case FAC_OPVAL_ER_CRCO:
+ r = "explicitReservationCreationControl";
+ break;
+ case FAC_OPVAL_ER_MGMT:
+ r = "explicitReservationManagement";
+ break;
+ case FAC_OPVAL_ER_CANC:
+ r = "explicitReservationCancel";
+ break;
+ case FAC_OPVAL_MLPP_QUERY:
+ r = "mLPP lfb Query";
+ break;
+ case FAC_OPVAL_MLPP_CALLR:
+ r = "mLPP Call Request";
+ break;
+ case FAC_OPVAL_MLPP_CALLP:
+ r = "mLPP Call Preemption";
+ break;
+ case FAC_OPVAL_AOC_REQ:
+ r = "chargingRequest";
+ break;
+ case FAC_OPVAL_AOC_S_CUR:
+ r = "aOCSCurrency";
+ break;
+ case FAC_OPVAL_AOC_S_SPC:
+ r = "aOCSSpecialArrangement";
+ break;
+ case FAC_OPVAL_AOC_D_CUR:
+ r = "aOCDCurrency";
+ break;
+ case FAC_OPVAL_AOC_D_UNIT:
+ r = "aOCDChargingUnit";
+ break;
+ case FAC_OPVAL_AOC_E_CUR:
+ r = "aOCECurrency";
+ break;
+ case FAC_OPVAL_AOC_E_UNIT:
+ r = "aOCEChargingUnit";
+ break;
+ case FAC_OPVAL_AOC_IDOFCRG:
+ r = "identificationOfCharge";
+ break;
+ case FAC_OPVAL_CONF_BEG:
+ r = "beginConf";
+ break;
+ case FAC_OPVAL_CONF_ADD:
+ r = "addConf";
+ break;
+ case FAC_OPVAL_CONF_SPLIT:
+ r = "splitConf";
+ break;
+ case FAC_OPVAL_CONF_DROP:
+ r = "dropConf";
+ break;
+ case FAC_OPVAL_CONF_ISOLATE:
+ r = "isolateConf";
+ break;
+ case FAC_OPVAL_CONF_REATT:
+ r = "reattachConf";
+ break;
+ case FAC_OPVAL_CONF_PDISC:
+ r = "partyDISC";
+ break;
+ case FAC_OPVAL_CONF_FCONF:
+ r = "floatConf";
+ break;
+ case FAC_OPVAL_CONF_END:
+ r = "endConf";
+ break;
+ case FAC_OPVAL_CONF_IDCFE:
+ r = "indentifyConferee";
+ break;
+ case FAC_OPVAL_REVC_REQ:
+ r = "requestREV";
+ break;
+ default:
+ sprintf(buffer, "unknown operation value %d!", val);
+ r = buffer;
+ }
+ return(r);
+}
+
+/*---------------------------------------------------------------------------*
+ * billing id string
+ *---------------------------------------------------------------------------*/
+static char *bid_str(int val)
+{
+ static char buffer[80];
+ char *r;
+
+ switch(val)
+ {
+ case 0:
+ r = "normalCharging";
+ break;
+ case 1:
+ r = "reverseCharging";
+ break;
+ case 2:
+ r = "creditCardCharging";
+ break;
+ case 3:
+ r = "callForwardingUnconditional";
+ break;
+ case 4:
+ r = "callForwardingBusy";
+ break;
+ case 5:
+ r = "callForwardingNoReply";
+ break;
+ case 6:
+ r = "callDeflection";
+ break;
+ case 7:
+ r = "callTransfer";
+ break;
+ default:
+ sprintf(buffer, "unknown billing-id value %d!", val);
+ r = buffer;
+ }
+ return(r);
+}
+
+/*---------------------------------------------------------------------------*
+ * invoke component
+ *---------------------------------------------------------------------------*/
+static void
+F_1_1(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_1_1, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t invokeComponent\n");
+ state = ST_EXP_INV_ID;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * return result
+ *---------------------------------------------------------------------------*/
+static void
+F_1_2(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_1_2, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t returnResult\n");
+ state = ST_EXP_RR_INV_ID;
+ }
+}
+/*---------------------------------------------------------------------------*
+ * return error
+ *---------------------------------------------------------------------------*/
+static void
+F_1_3(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_1_3, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t returnError\n");
+ state = ST_EXP_NIX;
+ }
+}
+/*---------------------------------------------------------------------------*
+ * reject
+ *---------------------------------------------------------------------------*/
+static void
+F_1_4(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_1_4, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t reject\n");
+ state = ST_EXP_REJ_INV_ID;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * return result: invoke id
+ *---------------------------------------------------------------------------*/
+static void
+F_RJ2(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_RJ2, val = %d\n", val);
+#endif
+ if(val != -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t InvokeIdentifier = %d\n", val);
+ state = ST_EXP_REJ_OP_VAL;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * reject, general problem
+ *---------------------------------------------------------------------------*/
+static void
+F_RJ30(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_RJ30, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t General problem\n");
+ }
+ else
+ {
+ switch(val)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unrecognized component\n");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = mistyped component\n");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = badly structured component\n");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unknown problem code 0x%x\n", val);
+ break;
+ }
+ state = ST_EXP_NIX;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * reject, invoke problem
+ *---------------------------------------------------------------------------*/
+static void
+F_RJ31(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_RJ31, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t Invoke problem\n");
+ }
+ else
+ {
+ switch(val)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = duplicate invocation\n");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unrecognized operation\n");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = mistyped argument\n");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = resource limitation\n");
+ break;
+ case 4:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = initiator releasing\n");
+ break;
+ case 5:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unrecognized linked identifier\n");
+ break;
+ case 6:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = linked resonse unexpected\n");
+ break;
+ case 7:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unexpected child operation\n");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unknown problem code 0x%x\n", val);
+ break;
+ }
+ state = ST_EXP_NIX;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * reject, return result problem
+ *---------------------------------------------------------------------------*/
+static void
+F_RJ32(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_RJ32, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t Return result problem\n");
+ }
+ else
+ {
+ switch(val)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unrecognized invocation\n");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = return response unexpected\n");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = mistyped result\n");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unknown problem code 0x%x\n", val);
+ break;
+ }
+ state = ST_EXP_NIX;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * reject, return error problem
+ *---------------------------------------------------------------------------*/
+static void
+F_RJ33(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_RJ33, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t Return error problem\n");
+ }
+ else
+ {
+ switch(val)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unrecognized invocation\n");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = error response unexpected\n");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unrecognized error\n");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unexpected error\n");
+ break;
+ case 4:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = mistyped parameter\n");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unknown problem code 0x%x\n", val);
+ break;
+ }
+ state = ST_EXP_NIX;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * invoke component: invoke id
+ *---------------------------------------------------------------------------*/
+static void
+F_2(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_2, val = %d\n", val);
+#endif
+ if(val != -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t InvokeIdentifier = %d\n", val);
+ state = ST_EXP_OP_VAL;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * return result: invoke id
+ *---------------------------------------------------------------------------*/
+static void
+F_RR2(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_RR2, val = %d\n", val);
+#endif
+ if(val != -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t InvokeIdentifier = %d\n", val);
+ state = ST_EXP_RR_OP_VAL;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * invoke component: operation value
+ *---------------------------------------------------------------------------*/
+static void
+F_3(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_3, val = %d\n", val);
+#endif
+ if(val != -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t Operation Value = %s (%d)\n", opval_str(val), val);
+ state = ST_EXP_INFO;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * return result: operation value
+ *---------------------------------------------------------------------------*/
+static void
+F_RR3(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_RR3, val = %d\n", val);
+#endif
+ if(val != -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t Operation Value = %s (%d)\n", opval_str(val), val);
+ state = ST_EXP_RR_RESULT;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * return result: RESULT
+ *---------------------------------------------------------------------------*/
+static void
+F_RRR(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_RRR, val = %d\n", val);
+#endif
+ state = ST_EXP_NIX;
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+static void
+F_4(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_4, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t specificChargingUnits\n");
+ state = ST_EXP_RUL;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+static void
+F_4_1(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_4_1, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t freeOfCharge\n");
+ state = ST_EXP_NIX;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+static void
+F_4_2(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_4_2, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t chargeNotAvailable\n");
+ state = ST_EXP_NIX;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+static void
+F_5(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_5, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t recordedUnitsList [1]\n");
+ state = ST_EXP_RU;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+static void
+F_6(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_6, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t RecordedUnits\n");
+ state = ST_EXP_RNOU;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+static void
+F_7(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_7, val = %d\n", val);
+#endif
+ if(val != -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t NumberOfUnits = %d\n", val);
+ state = ST_EXP_TOCI;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+static void
+F_8(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_8, val = %d\n", val);
+#endif
+ if(val != -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t typeOfChargingInfo = %s\n", val == 0 ? "subTotal" : "total");
+ state = ST_EXP_DBID;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+static void
+F_9(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_9, val = %d\n", val);
+#endif
+ if(val != -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t AOCDBillingId = %s (%d)\n", bid_str(val), val);
+ state = ST_EXP_NIX;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * state table
+ *---------------------------------------------------------------------------*/
+static struct statetab {
+ int currstate; /* input: current state we are in */
+ int form; /* input: current tag form */
+ int class; /* input: current tag class */
+ int code; /* input: current tag code */
+ void (*func)(char *,int); /* output: func to exec */
+} statetab[] = {
+
+/* current state tag form tag class tag code function */
+/* --------------------- ---------------------- ---------------------- ---------------------- ----------------*/
+
+/* invoke */
+
+ {ST_EXP_COMP_TYP, FAC_TAGFORM_CON, FAC_TAGCLASS_COS, 1, F_1_1 },
+ {ST_EXP_COMP_TYP, FAC_TAGFORM_CON, FAC_TAGCLASS_COS, 2, F_1_2 },
+ {ST_EXP_COMP_TYP, FAC_TAGFORM_CON, FAC_TAGCLASS_COS, 3, F_1_3 },
+ {ST_EXP_COMP_TYP, FAC_TAGFORM_CON, FAC_TAGCLASS_COS, 4, F_1_4 },
+ {ST_EXP_INV_ID, FAC_TAGFORM_PRI, FAC_TAGCLASS_UNI, FAC_CODEUNI_INT, F_2 },
+ {ST_EXP_OP_VAL, FAC_TAGFORM_PRI, FAC_TAGCLASS_UNI, FAC_CODEUNI_INT, F_3 },
+ {ST_EXP_INFO, FAC_TAGFORM_CON, FAC_TAGCLASS_UNI, FAC_CODEUNI_SEQ, F_4 },
+ {ST_EXP_INFO, FAC_TAGFORM_PRI, FAC_TAGCLASS_UNI, FAC_CODEUNI_NULL, F_4_1 },
+ {ST_EXP_INFO, FAC_TAGFORM_PRI, FAC_TAGCLASS_COS, 1, F_4_2 },
+ {ST_EXP_RUL, FAC_TAGFORM_CON, FAC_TAGCLASS_COS, 1, F_5 },
+ {ST_EXP_RU, FAC_TAGFORM_CON, FAC_TAGCLASS_UNI, FAC_CODEUNI_SEQ, F_6 },
+ {ST_EXP_RNOU, FAC_TAGFORM_PRI, FAC_TAGCLASS_UNI, FAC_CODEUNI_INT, F_7 },
+ {ST_EXP_TOCI, FAC_TAGFORM_PRI, FAC_TAGCLASS_COS, 2, F_8 },
+ {ST_EXP_DBID, FAC_TAGFORM_PRI, FAC_TAGCLASS_COS, 3, F_9 },
+
+/* return result */
+
+ {ST_EXP_RR_INV_ID, FAC_TAGFORM_PRI, FAC_TAGCLASS_UNI, FAC_CODEUNI_INT, F_RR2 },
+ {ST_EXP_RR_OP_VAL, FAC_TAGFORM_PRI, FAC_TAGCLASS_UNI, FAC_CODEUNI_INT, F_RR3 },
+ {ST_EXP_RR_RESULT, FAC_TAGFORM_CON, FAC_TAGCLASS_UNI, FAC_CODEUNI_SET, F_RRR },
+
+/* current state tag form tag class tag code function */
+/* --------------------- ---------------------- ---------------------- ---------------------- ----------------*/
+/* reject */
+
+ {ST_EXP_REJ_INV_ID, FAC_TAGFORM_PRI, FAC_TAGCLASS_UNI, FAC_CODEUNI_INT, F_RJ2 },
+ {ST_EXP_REJ_OP_VAL, FAC_TAGFORM_PRI, FAC_TAGCLASS_COS, 0, F_RJ30 },
+ {ST_EXP_REJ_OP_VAL, FAC_TAGFORM_PRI, FAC_TAGCLASS_COS, 1, F_RJ31 },
+ {ST_EXP_REJ_OP_VAL, FAC_TAGFORM_PRI, FAC_TAGCLASS_COS, 2, F_RJ32 },
+ {ST_EXP_REJ_OP_VAL, FAC_TAGFORM_PRI, FAC_TAGCLASS_COS, 3, F_RJ33 },
+
+/* end */
+
+ {-1, -1, -1, -1, NULL }
+};
+
+/*---------------------------------------------------------------------------*
+ * state decode for do_component
+ *---------------------------------------------------------------------------*/
+static void
+next_state(char *pbuf, int class, int form, int code, int val)
+{
+ int i;
+
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: class=%d, form=%d, code=%d, val=%d\n", class, form, code, val);
+#endif
+
+ for(i=0; ; i++)
+ {
+ if((statetab[i].currstate > state) ||
+ (statetab[i].currstate == -1))
+ {
+ break;
+ }
+
+ if((statetab[i].currstate == state) &&
+ (statetab[i].form == form) &&
+ (statetab[i].class == class) &&
+ (statetab[i].code == code))
+ {
+ (*statetab[i].func)(pbuf, val);
+ break;
+ }
+ }
+}
+
+/* EOF */
+
diff --git a/usr.sbin/i4b/isdndecode/facility.h b/usr.sbin/i4b/isdndecode/facility.h
new file mode 100644
index 0000000..f4cfe10
--- /dev/null
+++ b/usr.sbin/i4b/isdndecode/facility.h
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 1997, 2000 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * facility.h - facility header file
+ * ---------------------------------
+ *
+ * $Id: facility.h,v 1.5 2000/02/21 15:17:17 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Feb 21 16:16:04 2000]
+ *
+ *---------------------------------------------------------------------------
+ *
+ * - Q.932 (03/93) Generic Procedures for the Control of
+ * ISDN Supplementaty Services
+ * - Q.950 (03/93) Supplementary Services Protocols, Structure and
+ * General Principles
+ * - ETS 300 179 (10/92) Advice Of Charge: charging information during
+ * the call (AOC-D) supplementary service Service description
+ * - ETS 300 180 (10/92) Advice Of Charge: charging information at the
+ * end of call (AOC-E) supplementary service Service description
+ * - ETS 300 181 (04/93) Advice Of Charge (AOC) supplementary service
+ * Functional capabilities and information flows
+ * - ETS 300 182 (04/93) Advice Of Charge (AOC) supplementary service
+ * Digital Subscriber Signalling System No. one (DSS1) protocol
+ * - X.208 Specification of Abstract Syntax Notation One (ASN.1)
+ * - X.209 Specification of Basic Encoding Rules for
+ * Abstract Syntax Notation One (ASN.1)
+ * - "ASN.1 Abstract Syntax Notation One", Walter Gora, DATACOM-Verlag
+ * 1992, 3rd Edition (ISBN 3-89238-062-7) (german !)
+ *
+ *---------------------------------------------------------------------------*/
+
+/* #define FAC_DEBUG */
+/* #define ST_DEBUG */
+
+/* protocols */
+#define FAC_PROTO_ROP 0x11
+#define FAC_PROTO_CMIP 0x12
+#define FAC_PROTO_ACSE 0x13
+
+/* tag classes */
+#define FAC_TAGCLASS_UNI 0x00
+#define FAC_TAGCLASS_APW 0x01
+#define FAC_TAGCLASS_COS 0x02
+#define FAC_TAGCLASS_PRU 0x03
+
+/* tag forms */
+#define FAC_TAGFORM_PRI 0x00
+#define FAC_TAGFORM_CON 0x01
+
+/* class UNIVERSAL values */
+#define FAC_CODEUNI_BOOL 1
+#define FAC_CODEUNI_INT 2
+#define FAC_CODEUNI_BITS 3
+#define FAC_CODEUNI_OCTS 4
+#define FAC_CODEUNI_NULL 5
+#define FAC_CODEUNI_OBJI 6
+#define FAC_CODEUNI_OBJD 7
+#define FAC_CODEUNI_EXT 8
+#define FAC_CODEUNI_REAL 9
+#define FAC_CODEUNI_ENUM 10
+#define FAC_CODEUNI_R11 11
+#define FAC_CODEUNI_R12 12
+#define FAC_CODEUNI_R13 13
+#define FAC_CODEUNI_R14 14
+#define FAC_CODEUNI_R15 15
+#define FAC_CODEUNI_SEQ 16
+#define FAC_CODEUNI_SET 17
+#define FAC_CODEUNI_NSTR 18
+#define FAC_CODEUNI_PSTR 19
+#define FAC_CODEUNI_TSTR 20
+#define FAC_CODEUNI_VSTR 21
+#define FAC_CODEUNI_ISTR 22
+#define FAC_CODEUNI_UTIME 23
+#define FAC_CODEUNI_GTIME 24
+#define FAC_CODEUNI_GSTR 25
+#define FAC_CODEUNI_VISTR 26
+#define FAC_CODEUNI_GNSTR 27
+
+/* operation values */
+#define FAC_OPVAL_UUS 1
+#define FAC_OPVAL_CUG 2
+#define FAC_OPVAL_MCID 3
+#define FAC_OPVAL_BTPY 4
+#define FAC_OPVAL_ETPY 5
+#define FAC_OPVAL_ECT 6
+
+#define FAC_OPVAL_DIV_ACT 7
+#define FAC_OPVAL_DIV_DEACT 8
+#define FAC_OPVAL_DIV_ACTSN 9
+#define FAC_OPVAL_DIV_DEACTSN 10
+#define FAC_OPVAL_DIV_INTER 11
+#define FAC_OPVAL_DIV_INFO 12
+#define FAC_OPVAL_DIV_CALLDEF 13
+#define FAC_OPVAL_DIV_CALLRER 14
+#define FAC_OPVAL_DIV_LINF2 15
+#define FAC_OPVAL_DIV_INVS 16
+#define FAC_OPVAL_DIV_INTER1 17
+#define FAC_OPVAL_DIV_LINF1 18
+#define FAC_OPVAL_DIV_LINF3 19
+
+#define FAC_OPVAL_ER_CRCO 20
+#define FAC_OPVAL_ER_MGMT 21
+#define FAC_OPVAL_ER_CANC 22
+
+#define FAC_OPVAL_MLPP_QUERY 24
+#define FAC_OPVAL_MLPP_CALLR 25
+#define FAC_OPVAL_MLPP_CALLP 26
+
+#define FAC_OPVAL_AOC_REQ 30
+#define FAC_OPVAL_AOC_S_CUR 31
+#define FAC_OPVAL_AOC_S_SPC 32
+#define FAC_OPVAL_AOC_D_CUR 33
+#define FAC_OPVAL_AOC_D_UNIT 34
+#define FAC_OPVAL_AOC_E_CUR 35
+#define FAC_OPVAL_AOC_E_UNIT 36
+#define FAC_OPVAL_AOC_IDOFCRG 37
+
+#define FAC_OPVAL_CONF_BEG 40
+#define FAC_OPVAL_CONF_ADD 41
+#define FAC_OPVAL_CONF_SPLIT 42
+#define FAC_OPVAL_CONF_DROP 43
+#define FAC_OPVAL_CONF_ISOLATE 44
+#define FAC_OPVAL_CONF_REATT 45
+#define FAC_OPVAL_CONF_PDISC 46
+#define FAC_OPVAL_CONF_FCONF 47
+#define FAC_OPVAL_CONF_END 48
+#define FAC_OPVAL_CONF_IDCFE 49
+
+#define FAC_OPVAL_REVC_REQ 60
+
+enum states {
+ ST_EXP_COMP_TYP,
+ ST_EXP_INV_ID,
+ ST_EXP_OP_VAL,
+ ST_EXP_INFO,
+ ST_EXP_RUL,
+ ST_EXP_RU,
+ ST_EXP_RNOU,
+ ST_EXP_TOCI,
+ ST_EXP_DBID,
+
+ ST_EXP_RR_INV_ID,
+ ST_EXP_RR_OP_VAL,
+ ST_EXP_RR_RESULT,
+
+ ST_EXP_REJ_INV_ID,
+ ST_EXP_REJ_OP_VAL,
+ ST_EXP_REJ_RESULT,
+
+ ST_EXP_NIX
+};
+
+/* EOF */
+
diff --git a/usr.sbin/i4b/isdndecode/isdndecode.8 b/usr.sbin/i4b/isdndecode/isdndecode.8
new file mode 100644
index 0000000..86b0496
--- /dev/null
+++ b/usr.sbin/i4b/isdndecode/isdndecode.8
@@ -0,0 +1,172 @@
+.\"
+.\" Copyright (c) 1998, 2000 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: isdndecode.8,v 1.9 2000/02/21 15:17:17 hm Exp $
+.\"
+.\" last edit-date: [Mon Feb 21 16:15:09 2000]
+.\"
+.\" $FreeBSD$
+.\"
+.Dd February 21, 2000
+.Dt ISDNDECODE 8
+.Os
+.Sh NAME
+.Nm isdndecode
+.Nd isdn4bsd ISDN protocol decode utility
+.Sh SYNOPSIS
+.Nm
+.Op Fl a
+.Op Fl b
+.Op Fl d
+.Op Fl f Ar filename
+.Op Fl h
+.Op Fl i
+.Op Fl l
+.Op Fl o
+.Op Fl p Ar filename
+.Op Fl u Ar number
+.Op Fl x
+.Op Fl B
+.Op Fl P
+.Op Fl R Ar unit
+.Op Fl T Ar unit
+.Sh DESCRIPTION
+.Nm
+is part of the isdn4bsd package and is used to provide the user with a
+detailed mnemonic display of the layers 1, 2 and 3 protocol activities on
+the D channel and hex dump of the B channel(s) activities.
+.Pp
+Together with two passive supported cards and an easy to build cable it can
+also be used to monitor the complete traffic on a S0 bus providing S0 bus
+analyzer features.
+.Pp
+The
+.Nm
+utility is only available for passive supported cards.
+.Pp
+The following options can be used:
+.Bl -tag -width Ds
+.It Fl a
+Run
+.Nm
+in analyzer mode by using two passive cards and a custom cable which can
+be build as described in the file
+.Em cable.txt
+in the isdn4bsd source distribution. One card acts as a receiver for the
+transmitting direction on the S0 bus while the other card acts as a receiver
+for the receiving direction on the S0 bus. Complete traffic monitoring is
+possible using this setup.
+.It Fl b
+switch B channel tracing on (default off).
+.It Fl d
+switch D channel tracing off (default on).
+.It Fl f
+Use
+.Ar filename
+as the name of a file into which to write tracing output (default filename is
+isdndecode<n> where n is the number of the unit to decode).
+.It Fl h
+switch display of header off (default on).
+.It Fl i
+print layer 1 (I.430) INFO signals to monitor layer 1 activity (default off).
+.It Fl l
+switch displaying of Layer 2 (Q.921) frames off (default on).
+.It Fl o
+switch off writing decode output to a file (default on).
+.It Fl p
+Use
+.Ar filename
+as the name of a file used for the -B and -P options (default filename
+is isdntracebin<n> where n is the number of the unit to decode).
+.It Fl u
+Use
+.Ar number
+as the unit number of the controller card to decode (default 0).
+.It Fl x
+Decode Layer 3 packets with an unknown protocol discriminator.
+.It Fl B
+Write undecoded binary decode data to a file for later or remote
+analyzing (default off).
+.It Fl P
+Read undecoded binary decode data from file instead from device (default off).
+.It Fl R
+Use
+.Ar unit
+as the receiving interface unit number in analyze mode.
+.It Fl T
+Use
+.Ar unit
+as the transmitting interface unit number in analyze mode.
+.El
+.Pp
+When the USR1 signal is sent to a
+.Nm
+process, the currently used logfiles are reopened, so that logfile
+rotation becomes possible.
+.Pp
+The decode output should be obvious. It is very handy to have the following
+standard texts available when tracing ISDN protocols:
+.Pp
+.Bl -tag -width Ds -compact -offset indent
+.It Ar I.430
+ISDN BRI layer 1 protocol description.
+.It Ar Q.921
+ISDN D-channel layer 2 protocol description.
+.It Ar Q.931
+ISDN D-channel layer 3 protocol description.
+.El
+.Sh FILES
+.Bl -tag -width daddeldi -compact
+.It Pa /dev/i4btrc<n>
+The devicefile(s) used to get the decode messages for ISDN card unit <n>
+out of the kernel.
+.El
+.Sh EXAMPLES
+The command:
+.Bd -literal -offset indent
+isdndecode -f /var/tmp/isdn.decode
+.Ed
+.Pp
+will start D channel tracing on passive controller 0 with all except B
+channel tracing enabled and logs everything into the output file
+/var/tmp/isdn.decode.
+.Sh SEE ALSO
+.Xr isdnd 8
+.Sh BUGS
+Still one left.
+.Sh STANDARDS
+ITU Recommendations I.430, Q.920, Q.921, Q.930, Q.931
+.Pp
+ITU Recommendation Q.932 (03/93), Q.950 (03/93)
+.Pp
+ETSI Recommendation ETS 300 179 (10/92), ETS 300 180 (10/92)
+.Pp
+ETSI Recommendation ETS 300 181 (04/93), ETS 300 182 (04/93)
+.Pp
+ITU Recommendation X.208, X.209
+.Sh AUTHORS
+The
+.Nm
+utility and this manual page were written by
+.An Hellmuth Michaelis Aq hm@kts.org .
diff --git a/usr.sbin/i4b/isdndecode/layer1.c b/usr.sbin/i4b/isdndecode/layer1.c
new file mode 100644
index 0000000..c08b628
--- /dev/null
+++ b/usr.sbin/i4b/isdndecode/layer1.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * layer1.c - isdndecode, decode and print layer 1 information
+ * -----------------------------------------------------------
+ *
+ * $Id: layer1.c,v 1.4 1999/12/13 21:25:25 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:50:34 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "decode.h"
+
+/*---------------------------------------------------------------------------*
+ * decode layer 1 information
+ *---------------------------------------------------------------------------*/
+void
+layer1(char *buffer, unsigned char *buf)
+{
+ switch(*buf)
+ {
+ case INFO0:
+ strcpy(buffer,"L1 INFO0 (No Signal)\n");
+ break;
+
+ case INFO1_8:
+ strcpy(buffer,"L1 INFO1 (Activation Request, Priority = 8)\n");
+ break;
+
+ case INFO1_10:
+ strcpy(buffer,"L1 INFO1 (Activation Request, Priority = 10)\n");
+ break;
+
+ case INFO2:
+ strcpy(buffer,"L1 INFO2 (Pending Activation)\n");
+ break;
+
+ case INFO3:
+ strcpy(buffer,"L1 INFO3 (Synchronized)\n");
+ break;
+
+ case INFO4_8:
+ strcpy(buffer,"L1 INFO4 (Activated, Priority = 8/9)\n");
+ break;
+
+ case INFO4_10:
+ strcpy(buffer,"L1 INFO4 (Activated, Priority = 10/11)\n");
+ break;
+
+ default:
+ sprintf(buffer,"L1 ERROR, invalid INFO value 0x%x!\n", *buf);
+ break;
+ }
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdndecode/layer2.c b/usr.sbin/i4b/isdndecode/layer2.c
new file mode 100644
index 0000000..1e53986
--- /dev/null
+++ b/usr.sbin/i4b/isdndecode/layer2.c
@@ -0,0 +1,300 @@
+/*
+ * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * layer2.c - decode and print layer 2 (Q.921) information
+ * -------------------------------------------------------
+ *
+ * $Id: layer2.c,v 1.5 1999/12/13 21:25:25 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:50:41 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "decode.h"
+
+/*---------------------------------------------------------------------------*
+ * decode poll bit
+ *---------------------------------------------------------------------------*/
+static void
+poll(int layer, char *buffer, int cnt, unsigned char value, unsigned char mask)
+{
+ sprintline(layer, buffer, cnt, value, mask, "P/F, Poll = %s", (value & mask) ? "Immediate Response Required" : "No Immediate Response Required");
+}
+
+/*---------------------------------------------------------------------------*
+ * decode final bit
+ *---------------------------------------------------------------------------*/
+static void
+final(int layer, char *buffer, int cnt, unsigned char value, unsigned char mask)
+{
+ sprintline(layer, buffer, cnt, value, mask, "P/F, Final = %s", (value & mask) ? "Result of Poll" : "No Result of Poll");
+}
+
+/*---------------------------------------------------------------------------*
+ * decode protocol specified in Q.921
+ *---------------------------------------------------------------------------*/
+int
+layer2(char *pbuf, unsigned char *buf, int dir, int printit)
+{
+ int sap, tei, cmd;
+ int cnt = 0;
+ char locbuf[32000];
+ char *lbufp = &locbuf[0];
+ char buffer[80];
+
+ *lbufp = '\0';
+ *pbuf = '\0';
+
+ /* address high */
+
+ sap = (buf[0] >> 2) & 0x3f;
+
+ if(sap == 0)
+ strcpy(buffer, "Call Control");
+ else if((sap >= 1) && (sap <= 15))
+ strcpy(buffer, "Reserved");
+ else if(sap == 16)
+ strcpy(buffer, "X.25");
+ else if((sap >= 17) && (sap <= 31))
+ strcpy(buffer, "Reserved");
+ else if(sap == 63)
+ strcpy(buffer, "Layer 2 Management");
+ else
+ strcpy(buffer, "Not available for Q.921");
+ sprintline(2, lbufp+strlen(lbufp), cnt, buf[0], 0xfc, "SAPI = %d (%s)", sap, buffer);
+
+ if(dir == FROM_TE)
+ cmd = !(buf[0] & 0x02);
+ else
+ cmd = buf[0] & 0x02;
+
+ sprintline(2, lbufp+strlen(lbufp), cnt, buf[0], 0x02, "C/R = %s", cmd ? "Command" : "Response");
+ extension(2, lbufp+strlen(lbufp), cnt, buf[0], 0x01);
+ cnt++;
+
+ /* address low */
+
+ tei = buf[1] >> 1;
+
+ if((tei >= 0) && (tei <= 63))
+ strcpy(buffer, "Non-automatic TEI");
+ else if((tei >= 64) && (tei <= 126))
+ strcpy(buffer, "Automatic TEI");
+ if(tei == 127)
+ strcpy(buffer, "Group TEI");
+
+ sprintline(2, lbufp+strlen(lbufp), cnt, buf[1], 0xfe, "TEI = %d (%s)", tei, buffer);
+ extension(2, lbufp+strlen(lbufp), cnt, buf[1], 0x01);
+ cnt++;
+
+ /* control 1 */
+
+ if((buf[2] & 0x03) == 0x03)
+ {
+ /* U-frame */
+
+ if((buf[2] & 0xef) == 0x6f)
+ {
+ /* SABME */
+
+ sprintline(2, lbufp+strlen(lbufp), cnt, buf[2], 0xef, "U-Frame: SABME (Set Asynchonous Balanced Mode)");
+ poll(2, lbufp+strlen(lbufp), cnt, buf[2], 0x10);
+ cnt++;
+ }
+ else if((buf[2] & 0xef) == 0x0f)
+ {
+ /* DM */
+
+ sprintline(2, lbufp+strlen(lbufp), cnt, buf[2], 0xef, "U-Frame: DM (Disconnected Mode)");
+ final(2, lbufp+strlen(lbufp), cnt, buf[2], 0x10);
+ cnt++;
+ }
+ else if((buf[2] & 0xef) == 0x03)
+ {
+ /* UI */
+
+ sprintline(2, lbufp+strlen(lbufp), cnt, buf[2], 0xef, "U-Frame: UI (Unnumbered Information)");
+ poll(2, lbufp+strlen(lbufp), cnt, buf[2], 0x10);
+ cnt++;
+
+ if(sap == 63 && (buf[3] == 0x0f)) /* TEI management */
+ {
+ sprintline(2, lbufp+strlen(lbufp), cnt, buf[3], 0xff, "MEI (Management Entity Identifier)");
+ cnt++;
+ sprintline(2, lbufp+strlen(lbufp), cnt, buf[4], 0xff, "Ri = 0x%04x (Reference number high)", (buf[4] << 8) | buf[5]);
+ cnt++;
+ sprintline(2, lbufp+strlen(lbufp), cnt, buf[5], 0xff, "Ri (Reference Number low)");
+ cnt++;
+
+ switch(buf[6])
+ {
+ case 0x01:
+ strcpy(buffer, "Identity Request");
+ break;
+ case 0x02:
+ strcpy(buffer, "Identity Assigned");
+ break;
+ case 0x03:
+ strcpy(buffer, "Identity denied");
+ break;
+ case 0x04:
+ strcpy(buffer, "Identity Check Request");
+ break;
+ case 0x05:
+ strcpy(buffer, "Identity Check Response");
+ break;
+ case 0x06:
+ strcpy(buffer, "Identity Remove");
+ break;
+ case 0x07:
+ strcpy(buffer, "Identity Verify");
+ break;
+ default:
+ strcpy(buffer, "undefined");
+ break;
+ }
+
+ sprintline(2, lbufp+strlen(lbufp), cnt, buf[6], 0xff, "TEI %s (Message Type %d)", buffer, buf[6]);
+ cnt++;
+
+ switch(buf[6])
+ {
+ case 0x01:
+ strcpy(buffer, "Any TEI value acceptable");
+ break;
+ case 0x02:
+ strcpy(buffer, "");
+ break;
+ case 0x03:
+ strcpy(buffer, "No TEI Value available");
+ break;
+ case 0x04:
+ strcpy(buffer, "Check all TEI values");
+ break;
+ case 0x05:
+ strcpy(buffer, "");
+ break;
+ case 0x06:
+ strcpy(buffer, "Request for removal of all TEI values");
+ break;
+ case 0x07:
+ strcpy(buffer, "");
+ break;
+ default:
+ strcpy(buffer, "");
+ break;
+ }
+ if(((buf[7] >> 1) & 0x7f) == 127)
+ sprintline(2, lbufp+strlen(lbufp), cnt, buf[7], 0xfe, "Ai = %d (Action Indicator = %s)", (buf[7] >> 1) & 0x7f, buffer);
+ else
+ sprintline(2, lbufp+strlen(lbufp), cnt, buf[7], 0xfe, "Ai = %d (Action Indicator)", (buf[7] >> 1) & 0x7f);
+ extension(2, lbufp+strlen(lbufp), cnt, buf[7], 0x01);
+ cnt++;
+ }
+ }
+ else if((buf[2] & 0xef) == 0x43)
+ {
+ /* DISC */
+
+ sprintline(2, lbufp+strlen(lbufp), cnt, buf[2], 0xef, "U-Frame: DISC (Disconnect)");
+ poll(2, lbufp+strlen(lbufp), cnt, buf[2], 0x10);
+ cnt++;
+ }
+ else if((buf[2] & 0xef) == 0x63)
+ {
+ /* UA */
+
+ sprintline(2, lbufp+strlen(lbufp), cnt, buf[2], 0xef, "U-Frame: UA (Unnumbered Acknowledge)");
+ final(2, lbufp+strlen(lbufp), cnt, buf[2], 0x10);
+ cnt++;
+ }
+ else if((buf[2] & 0xef) == 0x87)
+ {
+ /* FRMR */
+
+ sprintline(2, lbufp+strlen(lbufp), cnt, buf[2], 0xef, "U-Frame: FRMR (Frame Reject)");
+ final(2, lbufp+strlen(lbufp), cnt, buf[2], 0x10);
+ cnt++;
+ }
+ else if((buf[2] & 0xef) == 0x9f)
+ {
+ /* XID */
+
+ sprintline(2, lbufp+strlen(lbufp), cnt, buf[2], 0xef, "U-Frame: XID (Exchange Identification)");
+ if(cmd)
+ poll(2, lbufp+strlen(lbufp), cnt, buf[2], 0x10);
+ else
+ final(2, lbufp+strlen(lbufp), cnt, buf[2], 0x10);
+ cnt++;
+ }
+
+ }
+ else if((buf[2] & 0x03) == 0x01)
+ {
+ /* S-frame */
+
+ if(buf[2] == 0x01)
+ strcpy(buffer, "RR (Receiver Ready)");
+ else if(buf[2] == 0x05)
+ strcpy(buffer, "RNR (Receiver Not Ready)");
+ else if(buf[2] == 0x09)
+ strcpy(buffer, "REJ (Reject)");
+ else
+ strcpy(buffer, "Unknown");
+
+ sprintline(2, lbufp+strlen(lbufp), cnt, buf[2], 0xff, "S-Frame: %s", buffer);
+ cnt++;
+
+ sprintline(2, lbufp+strlen(lbufp), cnt, buf[3], 0xfe, "N(R) = %d (receive sequence number)", (buf[3] >> 1) & 0x7f);
+ if(cmd)
+ poll(2, lbufp+strlen(lbufp), cnt, buf[3], 0x01);
+ else
+ final(2, lbufp+strlen(lbufp), cnt, buf[3], 0x01);
+ cnt++;
+
+ }
+ else if((buf[2] & 0x01) == 0x00)
+ {
+ /* I-frame */
+
+ sprintline(2, lbufp+strlen(lbufp), cnt, buf[2], 0xfe, "N(S) = %d (send sequence number)", (buf[2] >> 1) & 0x7f);
+ sprintline(2, lbufp+strlen(lbufp), cnt, buf[2], 0x01, "I-Frame: Information transfer");
+ cnt++;
+
+ sprintf(buffer, "N(R) = %d", (buf[3] >> 1) & 0x7f);
+ sprintline(2, lbufp+strlen(lbufp), cnt, buf[3], 0xfe, "N(R) = %d (receive sequence number)", (buf[3] >> 1) & 0x7f);
+ poll(2, lbufp+strlen(lbufp), cnt, buf[3], 0x01);
+ cnt++;
+
+ }
+
+ sprintf((pbuf+strlen(pbuf)),"%s", &locbuf[0]);
+ return (cnt);
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdndecode/layer3.c b/usr.sbin/i4b/isdndecode/layer3.c
new file mode 100644
index 0000000..12a7633
--- /dev/null
+++ b/usr.sbin/i4b/isdndecode/layer3.c
@@ -0,0 +1,516 @@
+/*
+ * Copyright (c) 1997, 2002 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * layer3.c - decode and print layer 3 (Q.931) information
+ * -------------------------------------------------------
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Tue Mar 26 14:39:02 2002]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "decode.h"
+
+char *mttab[] = {
+
+/* 0x00 */ /* call establishment group */
+
+ "ESCAPE",
+ "ALERTING",
+ "CALL PROCEEDING",
+ "PROGRESS",
+ "undefined (0x04)",
+ "SETUP",
+ "undefined (0x06)",
+ "CONNECT",
+ "undefined (0x08)",
+ "undefined (0x09)",
+ "undefined (0x0a)",
+ "undefined (0x0b)",
+ "undefined (0x0c)",
+ "SETUP ACKNOWLEDGE",
+ "undefined (0x0e)",
+ "CONNECT ACKNOWLEDGE",
+
+/* 0x10 */
+ "undefined (0x10)",
+ "undefined (0x11)",
+ "undefined (0x12)",
+ "undefined (0x13)",
+ "undefined (0x14)",
+ "undefined (0x15)",
+ "undefined (0x16)",
+ "undefined (0x17)",
+ "undefined (0x18)",
+ "undefined (0x19)",
+ "undefined (0x1a)",
+ "undefined (0x1b)",
+ "undefined (0x1c)",
+ "undefined (0x1d)",
+ "undefined (0x1e)",
+ "undefined (0x1f)",
+
+/* 0x20 */
+
+ "USER INFORMATION", /* call information phase */
+ "SUSPEND REJECT",
+ "RESUME REJECT",
+ "undefined (0x23)",
+ "HOLD",
+ "SUSPEND",
+ "RESUME",
+ "undefined (0x27)",
+ "HOLD ACKNOWLEDGE",
+ "undefined (0x29)",
+ "undefined (0x2a)",
+ "undefined (0x2b)",
+ "undefined (0x2c)",
+ "SUSPEND ACKNOWLEDGE",
+ "RESUME ACKNOWLEDGE",
+ "undefined (0x2f)",
+
+/* 0x30 */
+
+ "HOLD REJECT",
+ "RETRIEVE",
+ "undefined (0x32)",
+ "RETRIEVE ACKNOWLEDGE",
+ "undefined (0x34)",
+ "undefined (0x35)",
+ "undefined (0x36)",
+ "RETRIEVE REJECT",
+ "undefined (0x38)",
+ "undefined (0x39)",
+ "undefined (0x3a)",
+ "undefined (0x3b)",
+ "undefined (0x3c)",
+ "undefined (0x3d)",
+ "undefined (0x3e)",
+ "undefined (0x3f)",
+
+/* 0x40 */
+
+ "DETACH", /* call clearing */
+ "undefined (0x41)",
+ "undefined (0x42)",
+ "undefined (0x43)",
+ "undefined (0x44)",
+ "DISCONNECT",
+ "RESTART",
+ "undefined (0x47)",
+ "DETACH ACKNOWLEDGE",
+ "undefined (0x49)",
+ "undefined (0x4a)",
+ "undefined (0x4b)",
+ "undefined (0x4c)",
+ "RELEASE",
+ "RESTART ACKNOWLEDGE",
+ "undefined (0x4f)",
+
+/* 0x50 */
+
+ "undefined (0x50)",
+ "undefined (0x51)",
+ "undefined (0x52)",
+ "undefined (0x53)",
+ "undefined (0x54)",
+ "undefined (0x55)",
+ "undefined (0x56)",
+ "undefined (0x57)",
+ "undefined (0x58)",
+ "undefined (0x59)",
+ "RELEASE COMPLETE",
+ "undefined (0x5b)",
+ "undefined (0x5c)",
+ "undefined (0x5d)",
+ "undefined (0x5e)",
+ "undefined (0x5f)",
+
+/* 0x60 */
+
+ "SEGMENT", /* misc messages */
+ "undefined (0x61)",
+ "FACILITY",
+ "undefined (0x63)",
+ "REGISTER",
+ "undefined (0x65)",
+ "undefined (0x66)",
+ "undefined (0x67)",
+ "CANCEL ACKNOWLEDGE",
+ "undefined (0x69)",
+ "FACILITY ACKNOWLEDGE",
+ "undefined (0x6b)",
+ "REGISTER ACKNOWLEDGE",
+ "undefined (0x6d)",
+ "NOTIFY",
+ "undefined (0x6f)",
+
+/* 0x70 */
+
+ "CANCEL REJECT",
+ "undefined (0x71)",
+ "FACILITY REJECT",
+ "undefined (0x73)",
+ "REGISTER REJECT",
+ "STATUS ENQIRY",
+ "undefined (0x76)",
+ "undefined (0x77)",
+ "undefined (0x78)",
+ "CONGESTION CONTROL",
+ "undefined (0x7a)",
+ "INFORMATION",
+ "undefined (0x7c)",
+ "STATUS",
+ "undefined (0x7e)",
+ "undefined (0x7f)",
+};
+
+#define MTTAB_MAX 0x7f
+
+extern int f_null(char *pbuf, unsigned char *buf, int off);
+extern int f_bc(char *pbuf, unsigned char *buf, int off);
+extern int f_cause(char *pbuf, unsigned char *buf, int off);
+extern int f_cstat(char *pbuf, unsigned char *buf, int off);
+extern int f_chid(char *pbuf, unsigned char *buf, int off);
+extern int f_fac(char *pbuf, unsigned char *buf, int off);
+extern int f_progi(char *pbuf, unsigned char *buf, int off);
+extern int f_displ(char *pbuf, unsigned char *buf, int off);
+extern int f_date(char *pbuf, unsigned char *buf, int off);
+extern int f_cnu(char *pbuf, unsigned char *buf, int off);
+extern int f_cgpn(char *pbuf, unsigned char *buf, int off);
+extern int f_cdpn(char *pbuf, unsigned char *buf, int off);
+extern int f_hlc(char *pbuf, unsigned char *buf, int off);
+extern int f_uu(char *pbuf, unsigned char *buf, int off);
+
+struct ie {
+ unsigned char code; /* information element identifier code */
+ char *name; /* ie name */
+ int (*func) (char *pbuf, unsigned char *buf, int off); /* decode function */
+} ietab[] = {
+ { 0x00, "segmented message", f_null },
+ { 0x04, "bearer capability", f_bc },
+ { 0x08, "cause", f_cause },
+ { 0x0c, "connected address", f_null },
+ { 0x0d, "extended facility", f_null },
+ { 0x10, "call identity", f_null },
+ { 0x14, "call state", f_cstat },
+ { 0x18, "channel id", f_chid },
+ { 0x19, "data link connection id", f_null },
+ { 0x1c, "facility", f_fac },
+ { 0x1e, "progress indicator", f_progi },
+ { 0x20, "network specific facilities", f_null },
+ { 0x24, "terminal capabilities", f_null },
+ { 0x27, "notification indicator", f_null },
+ { 0x28, "display", f_displ },
+ { 0x29, "date/time", f_date },
+ { 0x2c, "keypad", f_null },
+ { 0x30, "keypad echo", f_null },
+ { 0x32, "information request", f_null },
+ { 0x34, "signal", f_null },
+ { 0x36, "switchhook", f_null },
+ { 0x38, "feature activation", f_null },
+ { 0x39, "feature indication", f_null },
+ { 0x3a, "service profile id", f_null },
+ { 0x3b, "endpoint identifier", f_null },
+ { 0x40, "information rate", f_null },
+ { 0x41, "precedence level", f_null },
+ { 0x42, "end-to-end transit delay", f_null },
+ { 0x43, "transit delay detection", f_null },
+ { 0x44, "packet layer binary parms", f_null },
+ { 0x45, "packet layer window size", f_null },
+ { 0x46, "packet size", f_null },
+ { 0x47, "closed user group", f_null },
+ { 0x48, "link layer core parameters", f_null },
+ { 0x49, "link layer protocol parms", f_null },
+ { 0x4a, "reverse charging information", f_null },
+ { 0x4c, "connected number", f_cnu },
+ { 0x4d, "connected subaddress", f_null },
+ { 0x50, "X.213 priority", f_null },
+ { 0x51, "report type", f_null },
+ { 0x53, "link integrity verification", f_null },
+ { 0x57, "PVC status", f_null },
+ { 0x6c, "calling party number", f_cnu },
+ { 0x6d, "calling party subaddress", f_null },
+ { 0x70, "called party number", f_cnu },
+ { 0x71, "called party subaddress", f_null },
+ { 0x74, "redirecting number", f_null },
+ { 0x78, "transit network selection", f_null },
+ { 0x79, "restart indicator", f_null },
+ { 0x7c, "low layer compatibility", f_null },
+ { 0x7d, "high layer compatibility", f_hlc },
+ { 0x7e, "user-user", f_uu },
+ { 0x7f, "escape for extension", f_null },
+ { 0xff, "unknown information element", f_null }
+};
+
+/*---------------------------------------------------------------------------*
+ * decode Q.931 protocol
+ *---------------------------------------------------------------------------*/
+void
+layer3(char *pbuf, int n, int off, unsigned char *buf)
+{
+ char buffer[256];
+ int codeset = 0;
+ int codelock = 0;
+ int oldcodeset = 0;
+
+ int pd;
+ int len;
+ int j;
+ int i;
+
+ if(n <= 0)
+ return;
+
+ *pbuf = '\0';
+
+ i = 0;
+
+ /* protocol discriminator */
+
+ pd = buf[i];
+
+ if(pd >= 0x00 && pd <= 0x07)
+ sprintf(buffer, "User-User IE (0x%02x)",pd);
+ else if(pd == 0x08)
+ sprintf(buffer, "Q.931/I.451");
+ else if(pd >= 0x10 && pd <= 0x3f)
+ sprintf(buffer, "Other Layer 3 or X.25 (0x%02x)",pd);
+ else if(pd >= 0x40 && pd <= 0x4f)
+ sprintf(buffer, "National Use (0x%02x)",pd);
+ else if(pd >= 0x50 && pd <= 0xfe)
+ sprintf(buffer, "Other Layer 3 or X.25 (0x%02x)",pd);
+ else
+ sprintf(buffer, "Reserved (0x%02x)",pd);
+
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, pd, 0xff, "Protocol discriminator = %s", buffer);
+ i++;
+
+ if(pd != 0x08)
+ {
+ for (; i < n;i++)
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0xff, "-");
+ return;
+ }
+
+ /* call reference */
+
+ len = buf[i] & 0x0f;
+
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0xf0, "Call Reference");
+
+ switch(len)
+ {
+ case 0:
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x0f, "Length of Call Reference = 0 (Dummy CR)");
+ break;
+ case 1:
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x0f, "Length of Call Reference = 1");
+ i++;
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x80, "Call Reference sent %s origination side", (buf[i] & 0x80) ? "to" : "from");
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x7f, "Call Reference = %d = 0x%02x", (buf[i] & 0x7f), (buf[i] & 0x7f));
+ break;
+ case 2:
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x0f, "Length of Call Reference = 2");
+ i++;
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x80, "Call Reference sent %s origination side", (buf[i] & 0x80) ? "to" : "from");
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x7f, "Call reference = %d = %02x", (buf[i] & 0x7f), (buf[i] & 0x7f));
+ i++;
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0xff, "Call reference = %d = %02x", (buf[i]), (buf[i]));
+ break;
+ }
+ i++;
+
+ /* message type */
+
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x80, "Message type extension = %d", buf[i] & 0x80 ? 1 : 0);
+
+ if(buf[i] <= MTTAB_MAX)
+ strcpy(buffer, mttab[buf[i]]);
+ else
+ sprintf(buffer, "unknown (0x%02x)", buf[i]);
+
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x7f, "Message type = %s", buffer);
+ i++;
+
+ /* information elements */
+
+ for (; i < n;)
+ {
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x80, "%s Information element", buf[i] & 0x80 ? "Single octet" : "Variable length");
+
+ if(buf[i] & 0x80)
+ {
+ /* single octett info element type 1 */
+
+ if((buf[i] & 0x70) == 0x00)
+ {
+ strcpy(buffer, "Reserved");
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x70, "Reserved");
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x0f, "Reserved, content of IE");
+ }
+ else if((buf[i] & 0x70) == 0x10)
+ {
+ strcpy(buffer, "Shift");
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x70, "Shift");
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x08, "%s shift", buf[i] & 0x08 ? "Non-locking" : "Locking");
+
+ switch(buf[i] & 0x07)
+ {
+ case 0:
+ strcpy(buffer, "Not applicable");
+ break;
+ case 1:
+ case 2:
+ case 3:
+ sprintf(buffer, "Reserved (%d)", buf[i] & 0x07);
+ break;
+ case 4:
+ strcpy(buffer, "Codeset 4 (ISO/IEC)");
+ break;
+ case 5:
+ strcpy(buffer, "Codeset 5 (National use)");
+ break;
+ case 6:
+ strcpy(buffer, "Codeset 6 (Local network specific)");
+ break;
+ case 7:
+ strcpy(buffer, "Codeset 7 (User specific)");
+ break;
+ }
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x07, "%s", buffer);
+ break;
+ }
+ else if((buf[i] & 0x70) == 0x30)
+ {
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x70, "Congestion Level");
+ switch(buf[i] & 0x0f)
+ {
+ case 0x00:
+ strcpy(buffer, "receiver ready");
+ break;
+ case 0x0f:
+ strcpy(buffer, "receiver not ready");
+ break;
+ default:
+ sprintf(buffer, "reserved (0x%02x)", buf[i] & 0x0f);
+ break;
+ }
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x0f, "Congestion Level = ", buffer);
+ break;
+ }
+ else if((buf[i] & 0x70) == 0x50)
+ {
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x70, "Repeat Indicator");
+ switch(buf[i] & 0x0f)
+ {
+ case 0x02:
+ strcpy(buffer, "Prioritized list for selecting one possibility");
+ break;
+ default:
+ sprintf(buffer, "reserved (0x%02x)", buf[i] & 0x0f);
+ break;
+ }
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x0f, "Repeat indication = ", buffer);
+ break;
+ }
+
+ /* single octett info element type 2 */
+
+ else if((buf[i] & 0x7f) == 0x20)
+ {
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x7f, "More data");
+ }
+ else if((buf[i] & 0x7f) == 0x21)
+ {
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x7f, "Sending complete");
+ }
+ else
+ {
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0xff, "UNKNOWN single octet IE = 0x%02x", buf[i]);
+ }
+ i++; /* next */
+ }
+ else
+ {
+ if(codeset == 0)
+ {
+ struct ie *iep = &ietab[0];
+
+ for(;;)
+ {
+ if((iep->code == buf[i]) ||
+ (iep->code == 0xff))
+ break;
+ iep++;
+ }
+
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x7f, "IE = %s", iep->name);
+ sprintline(3, pbuf+strlen(pbuf), off+i+1, buf[i+1], 0xff, "IE Length = %d", buf[i+1]);
+
+ if(iep->func == f_null)
+ {
+ }
+ else
+ {
+ i += (iep->func)(pbuf, &buf[i], off+i);
+ goto next;
+ }
+ }
+ else
+ {
+ sprintf((pbuf+strlen(pbuf)), "UNKNOWN CODESET=%d, IE=0x%02x", codeset, buf[i]);
+ }
+
+ i++; /* index -> length */
+
+ len = buf[i];
+
+ sprintf((pbuf+strlen(pbuf)), "LEN=0x%02x, DATA=", len);
+
+ i++; /* index -> 1st param */
+
+ for(j = 0; j < len; j++)
+ {
+ sprintf((pbuf+strlen(pbuf)),"0x%02x ", buf[j+i]);
+ }
+
+ sprintf((pbuf+strlen(pbuf)),"]\n");
+
+ i += len;
+
+next:
+
+ if(!codelock && (codeset != oldcodeset))
+ codeset = oldcodeset;
+ }
+ }
+/* sprintf((pbuf+strlen(pbuf)),"\n"); */
+}
+
+/* EOF */
+
diff --git a/usr.sbin/i4b/isdndecode/layer3_subr.c b/usr.sbin/i4b/isdndecode/layer3_subr.c
new file mode 100644
index 0000000..6fcedd2
--- /dev/null
+++ b/usr.sbin/i4b/isdndecode/layer3_subr.c
@@ -0,0 +1,1122 @@
+/*
+ * Copyright (c) 1997, 2000 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * layer3_subr.c - subroutines for IE decoding
+ * -------------------------------------------
+ *
+ * $Id: layer3_subr.c,v 1.8 2000/02/21 15:17:17 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Feb 21 15:45:16 2000]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "decode.h"
+
+/*---------------------------------------------------------------------------*
+ * dummy function
+ *---------------------------------------------------------------------------*/
+int
+f_null(char *pbuf, unsigned char *buf, int off)
+{
+ return(0);
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+int
+f_cstat(char *pbuf, unsigned char *buf, int off)
+{
+ int i = 0;
+ int len = 0;
+ char buffer[256];
+
+ i++;
+ len = buf[i];
+ i++;
+ sprintf((pbuf+strlen(pbuf)), "Std=");
+ switch((buf[i] & 0x60) >> 5)
+ {
+ case 0:
+ strcpy(buffer, "CCITT");
+ break;
+ case 1:
+ strcpy(buffer, "ISO/IEC");
+ break;
+ case 2:
+ strcpy(buffer, "National");
+ break;
+ case 3:
+ strcpy(buffer, "Special");
+ break;
+ }
+ sprintf((pbuf+strlen(pbuf)), ", State=");
+
+ switch((buf[i] & 0x3f))
+ {
+ case 0:
+ strcpy(buffer, "Null");
+ break;
+ case 1:
+ strcpy(buffer, "Call initiated");
+ break;
+ case 2:
+ strcpy(buffer, "Overlap sending");
+ break;
+ case 3:
+ strcpy(buffer, "Outgoing call proceeding");
+ break;
+ case 4:
+ strcpy(buffer, "Call delivered");
+ break;
+ case 6:
+ strcpy(buffer, "Call present");
+ break;
+ case 7:
+ strcpy(buffer, "Call received");
+ break;
+ case 8:
+ strcpy(buffer, "Connect request");
+ break;
+ case 9:
+ strcpy(buffer, "Incoming call proceeding");
+ break;
+ case 10:
+ strcpy(buffer, "Active");
+ break;
+ case 11:
+ strcpy(buffer, "Disconnect request");
+ break;
+ case 12:
+ strcpy(buffer, "Disconnect indication");
+ break;
+ case 15:
+ strcpy(buffer, "Suspend request");
+ break;
+ case 17:
+ strcpy(buffer, "Resume request");
+ break;
+ case 19:
+ strcpy(buffer, "Release request");
+ break;
+ case 22:
+ strcpy(buffer, "Call abort");
+ break;
+ case 25:
+ strcpy(buffer, "Overlap receiving");
+ break;
+ case 0x3d:
+ strcpy(buffer, "Restart request");
+ break;
+ case 0x3e:
+ strcpy(buffer, "Restart");
+ break;
+ default:
+ strcpy(buffer, "ERROR: undefined/reserved");
+ break;
+ }
+ sprintf((pbuf+strlen(pbuf)), "]");
+ i++;
+ return(i);
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+int
+f_chid(char *pbuf, unsigned char *buf, int off)
+{
+ int i = 0;
+ int len = 0;
+ char buffer[256];
+
+ i++;
+ len = buf[i];
+ i++;
+
+ extension(3, pbuf+strlen(pbuf), off+i, buf[i], 0x80);
+
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x40, "Interface Id present = %s", buf[i] & 0x40 ? "Yes" : "No");
+
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x20, "Interface Type = %s", buf[i] & 0x20 ? "Other (PRI)" : "BRI");
+
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x10, "Spare");
+
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x08, "Channel = %s", buf[i] & 0x08 ? "exclusive" : "preferred");
+
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x04, "Channel is%s the D-Channel", buf[i] & 0x04 ? "" : " not");
+
+ switch(buf[i] & 0x03)
+ {
+ case 0:
+ strcpy(buffer, "no channel");
+ break;
+ case 1:
+ strcpy(buffer, "B-1");
+ break;
+ case 2:
+ strcpy(buffer, "B-2");
+ break;
+ case 3:
+ strcpy(buffer, "any channel");
+ break;
+ }
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x03, "Channel = %s", buffer);
+
+ i++;
+ return(i);
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+int
+f_fac(char *pbuf, unsigned char *buf, int off)
+{
+ return(q932_facility(pbuf, buf));
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+int
+f_progi(char *pbuf, unsigned char *buf, int off)
+{
+ int i = 0;
+ int len = 0;
+ char buffer[256];
+
+ i++;
+ len = buf[i];
+ i++;
+ sprintf((pbuf+strlen(pbuf)), "Std=");
+ switch((buf[i] & 0x60) >> 5)
+ {
+ case 0:
+ strcpy(buffer, "CCITT");
+ break;
+ case 1:
+ strcpy(buffer, "ISO/IEC");
+ break;
+ case 2:
+ strcpy(buffer, "National");
+ break;
+ case 3:
+ strcpy(buffer, "Local");
+ break;
+ }
+ sprintf((pbuf+strlen(pbuf)), ", Loc=");
+
+ switch((buf[i] & 0x0f))
+ {
+ case 0:
+ strcpy(buffer, "User");
+ break;
+ case 1:
+ strcpy(buffer, "Private network serving local user");
+ break;
+ case 2:
+ strcpy(buffer, "Public network serving local user");
+ break;
+ case 3:
+ strcpy(buffer, "Transit network");
+ break;
+ case 4:
+ strcpy(buffer, "Public network serving remote user");
+ break;
+ case 5:
+ strcpy(buffer, "Private network serving remote user");
+ break;
+ case 6:
+ strcpy(buffer, "Network beyond interworking point");
+ break;
+ default:
+ strcpy(buffer, "ERROR: undefined/reserved");
+ break;
+ }
+
+ i++;
+
+ sprintf((pbuf+strlen(pbuf)), "\n Description");
+
+ switch((buf[i] & 0x7f))
+ {
+ case 1:
+ strcpy(buffer, "Call is not end-to-end ISDN");
+ break;
+ case 2:
+ strcpy(buffer, "Destination address is non-ISDN");
+ break;
+ case 3:
+ strcpy(buffer, "Origination address is non-ISDN");
+ break;
+ case 4:
+ strcpy(buffer, "Call has returned to the ISDN");
+ break;
+ case 5:
+ strcpy(buffer, "Interworking occured, Service change");
+ break;
+ case 8:
+ strcpy(buffer, "In-band info or appropriate pattern now available");
+ break;
+ default:
+ strcpy(buffer, "ERROR: undefined/reserved");
+ break;
+ }
+ sprintf((pbuf+strlen(pbuf)), "]");
+ i++;
+
+ return(i);
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+int
+f_displ(char *pbuf, unsigned char *buf, int off)
+{
+ int i = 0;
+ int j = 0;
+ int len = 0;
+
+ i++;
+ len = buf[i];
+ i++;
+ for(j = 0; j < len; j++)
+ {
+ sprintf((pbuf+strlen(pbuf)),"%c", buf[j+i]);
+ }
+ sprintf((pbuf+strlen(pbuf)),"]");
+ i += j;
+
+ return(i);
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+int
+f_date(char *pbuf, unsigned char *buf, int off)
+{
+ int i = 0;
+ int j = 0;
+ int len = 0;
+
+ i++;
+ len = buf[i];
+ i++;
+
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0xff, "Year = %02d", buf[i]);
+ i++;
+
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0xff, "Month = %02d", buf[i]);
+ i++;
+
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0xff, "Day = %02d", buf[i]);
+ i++;
+
+ j=3;
+ if(j < len)
+ {
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0xff, "Hour = %02d", buf[i]);
+ i++;
+ j++;
+ }
+ if(j < len)
+ {
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0xff, "Minute = %02d", buf[i]);
+ i++;
+ j++;
+ }
+ if(j < len)
+ {
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0xff, "Second = %02d", buf[i]);
+ i++;
+ j++;
+ }
+ i += len;
+ return(i);
+}
+
+/*---------------------------------------------------------------------------*
+ * decode and print the cause
+ *---------------------------------------------------------------------------*/
+int
+f_cause(char *pbuf, unsigned char *buf, int off)
+{
+ int j;
+ int len;
+ int i = 0;
+ int ls;
+ char buffer[256];
+
+ i++; /* index -> length */
+
+ len = buf[i];
+
+ i++; /* coding/location */
+ len--;
+
+ ls = buf[i];
+
+ extension(3, pbuf+strlen(pbuf), off+i, buf[i], 0x80);
+
+ switch((ls & 0x60) >> 5)
+ {
+ case 0:
+ strcpy(buffer, "CCITT");
+ break;
+ case 1:
+ strcpy(buffer, "ISO/IEC");
+ break;
+ case 2:
+ strcpy(buffer, "National");
+ break;
+ case 3:
+ strcpy(buffer, "Local");
+ break;
+ }
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x60, "Coding Standard = %s", buffer);
+
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x10, "Spare");
+
+ switch(ls & 0x0f)
+ {
+ case 0x00:
+ strcpy(buffer, "user");
+ break;
+ case 0x01:
+ strcpy(buffer, "private network serving local user");
+ break;
+ case 0x02:
+ strcpy(buffer, "public network serving local user");
+ break;
+ case 0x03:
+ strcpy(buffer, "transit network");
+ break;
+ case 0x04:
+ strcpy(buffer, "public network serving remote user");
+ break;
+ case 0x05:
+ strcpy(buffer, "private network serving remote user");
+ break;
+ case 0x07:
+ strcpy(buffer, "international network");
+ break;
+ case 0x0a:
+ strcpy(buffer, "network beyond interworking point");
+ break;
+ default:
+ sprintf(buffer, "reserved (0x%02x)", ls & 0x0f);
+ break;
+ }
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x0f, "Location = %s", buffer);
+
+ i++;
+ len--;
+
+ if(!(ls & 0x80))
+ {
+ extension(3, pbuf+strlen(pbuf), off+i, buf[i], 0x80);
+
+ switch(buf[i] & 0x7f)
+ {
+ case 0:
+ strcpy(buffer, "Q.931");
+ break;
+ case 3:
+ strcpy(buffer, "X.21");
+ break;
+ case 4:
+ strcpy(buffer, "X.25");
+ break;
+ case 5:
+ strcpy(buffer, "Q.1031/Q.1051");
+ break;
+ default:
+ strcpy(buffer, "Reserved");
+ break;
+ }
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x7f, "Recommendation = %s", buffer);
+ i++;
+ len--;
+ }
+
+ extension(3, pbuf+strlen(pbuf), off+i, buf[i], 0x80);
+
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x7f, "Cause = %s", print_cause_q850(buf[i] & 0x7f));
+
+ i++;
+ len--;
+
+ for(j = 0; j < len; j++)
+ sprintline(3, (pbuf+strlen(pbuf)), off+i+j, buf[i+j], 0xff, "Diagnostics = %02d %s", buf[i+j], buf[i+j]);
+
+ i += (len+1);
+
+ return(i);
+}
+
+/*---------------------------------------------------------------------------*
+ * decode and print the bearer capability
+ *---------------------------------------------------------------------------*/
+int
+f_bc(char *pbuf, unsigned char *buf, int off)
+{
+ int len;
+ int i = 0;
+ int mr = 0;
+ char buffer[256];
+
+ i++; /* index -> length */
+
+ len = buf[i];
+ i++;
+
+ extension(3, pbuf+strlen(pbuf), off+i, buf[i], 0x80);
+
+ switch((buf[i] & 0x60) >> 5)
+ {
+ case 0:
+ strcpy(buffer, "CCITT");
+ break;
+ case 1:
+ strcpy(buffer, "ISO/IEC");
+ break;
+ case 2:
+ strcpy(buffer, "National");
+ break;
+ case 3:
+ strcpy(buffer, "NSI Std");
+ break;
+ }
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x60, "Coding standard = %s", buffer);
+
+ switch(buf[i] & 0x1f)
+ {
+ case 0x00:
+ strcpy(buffer, "speech");
+ break;
+ case 0x08:
+ strcpy(buffer, "unrestricted digital information");
+ break;
+ case 0x09:
+ strcpy(buffer, "restricted digital information");
+ break;
+ case 0x10:
+ strcpy(buffer, "3.1 kHz audio");
+ break;
+ case 0x11:
+ strcpy(buffer, "unrestricted digital information with tones");
+ break;
+ case 0x18:
+ strcpy(buffer, "video");
+ break;
+ default:
+ sprintf(buffer, "reserved (0x%02x)", buf[i] & 0x0f);
+ break;
+ }
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x1f, "Capability = %s", buffer);
+
+ i++;
+ len--;
+
+ extension(3, pbuf+strlen(pbuf), off+i, buf[i], 0x80);
+
+ switch((buf[i] & 0x60) >> 5)
+ {
+ case 0:
+ strcpy(buffer, "circuit");
+ break;
+ case 2:
+ strcpy(buffer, "packet");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "reserved (0x%02x)", ((buf[i] & 0x60) >> 5));
+ break;
+ }
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x60, "Mode = %s", buffer);
+
+ switch(buf[i] & 0x1f)
+ {
+ case 0x00:
+ strcpy(buffer, "packet mode");
+ break;
+ case 0x10:
+ strcpy(buffer, "64 kbit/s");
+ break;
+ case 0x11:
+ strcpy(buffer, "2 x 64 kbit/s");
+ break;
+ case 0x13:
+ strcpy(buffer, "384 kbit/s");
+ break;
+ case 0x15:
+ strcpy(buffer, "1536 kbit/s");
+ break;
+ case 0x17:
+ strcpy(buffer, "1920 kbit/s");
+ break;
+ case 0x18:
+ strcpy(buffer, "Multirate");
+ mr = 1;
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "reserved (0x%02x)", buf[i] & 0x0f);
+ break;
+ }
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x1f, "Rate = %s", buffer);
+
+ i++;
+ len--;
+
+ if(!len)
+ goto exit;
+
+ if(mr)
+ {
+ extension(3, pbuf+strlen(pbuf), off+i, buf[i], 0x80);
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x60, "Rate multiplier = %d", buf[i] & 0x7f);
+ i++;
+ len--;
+ }
+
+ if(!len)
+ goto exit;
+
+ switch(buf[i] & 0x1f)
+ {
+ case 0x01:
+ strcpy(buffer, "V.110/X.30");
+ break;
+ case 0x02:
+ strcpy(buffer, "G.711 u-Law");
+ break;
+ case 0x03:
+ strcpy(buffer, "G.711 a-Law");
+ break;
+ case 0x04:
+ strcpy(buffer, "G.721 ADPCM/I.460");
+ break;
+ case 0x05:
+ strcpy(buffer, "H.221/H.242");
+ break;
+ case 0x07:
+ strcpy(buffer, "non-CCITT rate adaption");
+ break;
+ case 0x08:
+ strcpy(buffer, "V.120");
+ break;
+ case 0x09:
+ strcpy(buffer, "X.31");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "reserved (0x%02x)", buf[i] & 0x1f);
+ break;
+ }
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x1f, "Layer 1 Protocol = %s", buffer);
+
+ i++;
+ len--;
+
+ if(!len)
+ goto exit;
+
+/* work to do ahead !!! */
+
+ if(!(buf[i-1] & 0x80))
+ {
+ sprintf((pbuf+strlen(pbuf)), "\n user rate=0x%02x ", buf[i] & 0x1f);
+
+ if(buf[i] & 0x40)
+ sprintf((pbuf+strlen(pbuf)), "(async,");
+ else
+ sprintf((pbuf+strlen(pbuf)), "(sync,");
+
+ if(buf[i] & 0x20)
+ sprintf((pbuf+strlen(pbuf)), "in-band neg. possible)");
+ else
+ sprintf((pbuf+strlen(pbuf)), "in-band neg not possible)");
+
+ i++;
+ len--;
+ }
+
+ if(!len)
+ goto exit;
+
+ if(!(buf[i-1] & 0x80))
+ {
+ sprintf((pbuf+strlen(pbuf)), "\n clk/flow=0x%02x", buf[i] & 0x1f);
+
+ sprintf((pbuf+strlen(pbuf)), "\n intermediate rate=");
+
+ switch((buf[i] & 0x60) >> 5)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "not used");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "8 kbit/s");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "16 kbit/s");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "32 kbit/s");
+ break;
+ }
+ i++;
+ len--;
+ }
+
+ if(!len)
+ goto exit;
+
+ if(!(buf[i-1] & 0x80))
+ {
+ sprintf((pbuf+strlen(pbuf)), "\n hdr/mfrm/etc.=0x%02x", buf[i]);
+ i++;
+ len--;
+ }
+
+ if(!len)
+ goto exit;
+
+ if(!(buf[i-1] & 0x80))
+ {
+ sprintf((pbuf+strlen(pbuf)), "\n stop/data/parity=0x%02x", buf[i]);
+ i++;
+ len--;
+ }
+
+ if(!len)
+ goto exit;
+
+ if(!(buf[i-1] & 0x80))
+ {
+ sprintf((pbuf+strlen(pbuf)), "\n modemtype=0x%02x", buf[i]);
+ i++;
+ len--;
+ }
+
+ if(!len)
+ goto exit;
+
+ switch(buf[i] & 0x7f)
+ {
+ case 0x42:
+ sprintf((pbuf+strlen(pbuf)), "\n layer2=Q.921/I.441");
+ break;
+ case 0x46:
+ sprintf((pbuf+strlen(pbuf)), "\n layer2=X.25 link");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "\n layer2=0x%02x",(buf[i] & 0x7f));
+ break;
+ }
+ i++;
+ len--;
+
+ if(!len)
+ goto exit;
+
+ switch(buf[i] & 0x7f)
+ {
+ case 0x62:
+ sprintf((pbuf+strlen(pbuf)), "\n layer3=Q.921/I.441");
+ break;
+ case 0x66:
+ sprintf((pbuf+strlen(pbuf)), "\n layer3=X.25 packet");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "\n layer3=0x%02x",(buf[i] & 0x7f));
+ break;
+ }
+ i++;
+ len--;
+
+exit:
+
+ return(i);
+}
+
+/*---------------------------------------------------------------------------*
+ * decode and print the ISDN (telephone) number
+ *---------------------------------------------------------------------------*/
+int
+f_cnu(char *pbuf, unsigned char *buf, int off)
+{
+ int j;
+ int len;
+ int i = 0;
+ int tp;
+ int ind = 0;
+ char buffer[256];
+
+ i++; /* index -> length */
+ len = buf[i];
+
+ i++; /* index -> type/plan */
+ tp = buf[i];
+
+ extension(3, pbuf+strlen(pbuf), off+i, buf[i], 0x80);
+
+ switch((tp & 0x70) >> 4)
+ {
+ case 0:
+ strcpy(buffer, "Unknown");
+ break;
+ case 1:
+ strcpy(buffer, "International number");
+ break;
+ case 2:
+ strcpy(buffer, "National number");
+ break;
+ case 3:
+ strcpy(buffer, "Network specific number");
+ break;
+ case 4:
+ strcpy(buffer, "Subscriber number");
+ break;
+ case 6:
+ strcpy(buffer, "Abbreviated number");
+ break;
+ default:
+ sprintf(buffer, "Reserved (%d), ", ((tp & 0x70) >> 4));
+ break;
+ }
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x60, "Type = %s", buffer);
+
+ switch(tp & 0x0f)
+ {
+ case 0:
+ strcpy(buffer, "Unknown");
+ break;
+ case 1:
+ strcpy(buffer, "ISDN (E.164)");
+ break;
+ case 3:
+ strcpy(buffer, "Data (X.121)");
+ break;
+ case 4:
+ strcpy(buffer, "Telex (F.69)");
+ break;
+ case 8:
+ strcpy(buffer, "National");
+ break;
+ case 9:
+ strcpy(buffer, "Private");
+ break;
+ default:
+ sprintf(buffer, "Reserved (%d)", (tp & 0x0f));
+ break;
+ }
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x0f, "Plan = %s", buffer);
+
+ i++;
+ len--;
+
+ if(!(tp & 0x80))
+ {
+ extension(3, pbuf+strlen(pbuf), off+i, buf[i], 0x80);
+
+ switch((buf[i] & 0x60) >> 5)
+ {
+ case 0:
+ strcpy(buffer, "allowed");
+ break;
+ case 1:
+ strcpy(buffer, "restricted");
+ break;
+ case 2:
+ strcpy(buffer, "number not available");
+ break;
+ case 3:
+ strcpy(buffer, "reserved");
+ break;
+ }
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x60, "Presentation = %s", buffer);
+
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x1c, "Spare");
+
+ switch(ind & 0x03)
+ {
+ case 0:
+ strcpy(buffer, "user provided, not screened");
+ break;
+ case 1:
+ strcpy(buffer, "user provided, verified & passed");
+ break;
+ case 2:
+ strcpy(buffer, "user provided, verified & failed");
+ break;
+ case 3:
+ strcpy(buffer, "network provided");
+ break;
+ }
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x03, "Screening = %s", buffer);
+ i++;
+ len--;
+ }
+
+ for(j = 0; j < len; j++)
+ {
+ sprintline(3, (pbuf+strlen(pbuf)), off+i+j, buf[i+j], 0xff, "Number digit = %c", buf[i+j]);
+ }
+
+ i += j;
+
+ return(i);
+}
+
+/*---------------------------------------------------------------------------*
+ * decode and print HL comatibility
+ *---------------------------------------------------------------------------*/
+int
+f_hlc(char *pbuf, unsigned char *buf, int off)
+{
+ int i = 0;
+ int len = 0;
+ char buffer[256];
+
+ i++;
+ len = buf[i];
+
+ i++;
+ extension(3, pbuf+strlen(pbuf), off+i, buf[i], 0x80);
+
+ switch((buf[i] >> 5) & 0x03)
+ {
+ case 0: strcpy(buffer, "CCITT");
+ break;
+ case 1: strcpy(buffer, "ISO/IEC");
+ break;
+ case 2: strcpy(buffer, "National");
+ break;
+ case 3: strcpy(buffer, "Network");
+ }
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x60, "Coding standard = %s", buffer);
+
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x1c, "Interpretation = %s", ((buf[i] >> 2) & 0x07) == 0x04 ? "first" : "reserved");
+
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x03, "Presentation = %s", ((buf[i]) & 0x03) == 0x01 ? "High layer protocol profile" : "reserved");
+
+ i++;
+ len--;
+
+ extension(3, pbuf+strlen(pbuf), off+i, buf[i], 0x80);
+
+ switch(buf[i] & 0x7f)
+ {
+ case 0x01:
+ strcpy(buffer, "Telephony");
+ break;
+ case 0x04:
+ strcpy(buffer, "Fax Group 2/3 (F.182)");
+ break;
+ case 0x21:
+ strcpy(buffer, "Fax Group 4 I (F.184)");
+ break;
+ case 0x24:
+ strcpy(buffer, "Teletex (F.230) or Fax Group 4 II/III (F.184)");
+ break;
+ case 0x28:
+ strcpy(buffer, "Teletex (F.220)");
+ break;
+ case 0x31:
+ strcpy(buffer, "Teletex (F.200)");
+ break;
+ case 0x32:
+ strcpy(buffer, "Videotex (F.300/T.102)");
+ break;
+ case 0x33:
+ strcpy(buffer, "Videotex (F.300/T.101)");
+ break;
+ case 0x35:
+ strcpy(buffer, "Telex (F.60)");
+ break;
+ case 0x38:
+ strcpy(buffer, "MHS (X.400)");
+ break;
+ case 0x41:
+ strcpy(buffer, "OSI (X.200)");
+ break;
+ case 0x5e:
+ strcpy(buffer, "Maintenance");
+ break;
+ case 0x5f:
+ strcpy(buffer, "Management");
+ break;
+ case 0x60:
+ strcpy(buffer, "Audio visual (F.721)");
+ break;
+ default:
+ sprintf(buffer, "Reserved (0x%02x)", buf[i] & 0x7f);
+ break;
+ }
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x7f, "Characteristics = %s", buffer);
+ i++;
+ len--;
+
+ if(buf[i-1] & 0x80)
+ {
+ return(i);
+ }
+
+ extension(3, pbuf+strlen(pbuf), off+i, buf[i], 0x80);
+
+ switch(buf[i] & 0x7f)
+ {
+ case 0x01:
+ strcpy(buffer, "Telephony");
+ break;
+ case 0x04:
+ strcpy(buffer, "Fax Group 2/3 (F.182)");
+ break;
+ case 0x21:
+ strcpy(buffer, "Fax Group 4 I (F.184)");
+ break;
+ case 0x24:
+ strcpy(buffer, "Teletex (F.230) or Fax Group 4 II/III (F.184)");
+ break;
+ case 0x28:
+ strcpy(buffer, "Teletex (F.220)");
+ break;
+ case 0x31:
+ strcpy(buffer, "Teletex (F.200)");
+ break;
+ case 0x32:
+ strcpy(buffer, "Videotex (F.300/T.102)");
+ break;
+ case 0x33:
+ strcpy(buffer, "Videotex (F.300/T.101)");
+ break;
+ case 0x35:
+ strcpy(buffer, "Telex (F.60)");
+ break;
+ case 0x38:
+ strcpy(buffer, "MHS (X.400)");
+ break;
+ case 0x41:
+ strcpy(buffer, "OSI (X.200)");
+ break;
+ case 0x5e:
+ strcpy(buffer, "Maintenance");
+ break;
+ case 0x5f:
+ strcpy(buffer, "Management");
+ break;
+ case 0x60:
+ strcpy(buffer, "Audio visual (F.721)");
+ break;
+ default:
+ sprintf(buffer, "Reserved (0x%02x)", buf[i] & 0x7f);
+ break;
+
+ }
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x7f, "Ext. characteristics = %s", buffer);
+ i++;
+ return(i);
+}
+
+/*---------------------------------------------------------------------------*
+ * user-user
+ *---------------------------------------------------------------------------*/
+int
+f_uu(char *pbuf, unsigned char *buf, int off)
+{
+ int j;
+ int len;
+ int i = 0;
+ int pd;
+ char buffer[256];
+
+ i++; /* index -> length */
+ len = buf[i];
+
+ i++; /* index -> PD */
+ pd = buf[i];
+
+ switch(pd)
+ {
+ case 0:
+ strcpy(buffer, "user-specific");
+ break;
+ case 1:
+ strcpy(buffer, "OSI high layer");
+ break;
+ case 2:
+ strcpy(buffer, "X.244");
+ break;
+ case 3:
+ strcpy(buffer, "reserved for sys mgmt");
+ break;
+ case 4:
+ strcpy(buffer, "IA5 characters");
+ break;
+ case 5:
+ strcpy(buffer, "X.208/X.209");
+ break;
+ case 7:
+ strcpy(buffer, "V.120");
+ break;
+ case 8:
+ strcpy(buffer, "Q.931/I.451");
+ break;
+ default:
+ if(pd >= 0x10 && pd <= 0x3f)
+ sprintf(buffer, "reserved incl X.31 (0x%2x)", pd);
+ else if (pd >= 0x40 && pd <= 0x4f)
+ sprintf(buffer, "national use (0x%2x)", pd);
+ else if (pd >= 0x50 && pd <= 0xfe)
+ sprintf(buffer, "reserved incl X.31 (0x%2x)", pd);
+ else
+ sprintf(buffer, "reserved (0x%2x)", pd);
+ break;
+ }
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0xff, "protocol = %s", buffer);
+
+ i++;
+ len--;
+
+ for(j = 0; j < len; j++)
+ {
+ if(isprint(buf[i+j]))
+ sprintline(3, (pbuf+strlen(pbuf)), off+i+j, buf[i+j], 0xff, "user information = %c", buf[i+j]);
+ else
+ sprintline(3, (pbuf+strlen(pbuf)), off+i+j, buf[i+j], 0xff, "user information = 0x%2x", buf[i+j]);
+ }
+
+ i += j;
+
+ return(i);
+}
+
+/* EOF */
+
diff --git a/usr.sbin/i4b/isdndecode/main.c b/usr.sbin/i4b/isdndecode/main.c
new file mode 100644
index 0000000..c4c9e7f
--- /dev/null
+++ b/usr.sbin/i4b/isdndecode/main.c
@@ -0,0 +1,796 @@
+/*
+ * Copyright (c) 1997, 2000 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * main.c - isdndecode main program file
+ * -------------------------------------
+ *
+ * $Id: main.c,v 1.13 2000/02/21 15:17:17 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Feb 21 16:19:30 2000]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "decode.h"
+
+unsigned char buf[BSIZE];
+FILE *Fout = NULL;
+FILE *BP = NULL;
+int outflag = 1;
+int header = 1;
+int print_q921 = 1;
+int unit = 0;
+int dchan = 0;
+int bchan = 0;
+int traceon = 0;
+int analyze = 0;
+int Rx = RxUDEF;
+int Tx = TxUDEF;
+int f;
+int Bopt = 0;
+int Popt = 0;
+int bpopt = 0;
+int info = 0;
+int xflag = 0;
+
+int enable_trace = TRACE_D_RX | TRACE_D_TX;
+
+static char outfilename[1024];
+static char BPfilename[1024];
+
+static void dumpbuf( int n, unsigned char *buf, i4b_trace_hdr_t *hdr);
+static int switch_driver( int value, int rx, int tx );
+static void usage( void );
+static void exit_hdl( void );
+static void reopenfiles( int );
+
+
+/*---------------------------------------------------------------------------*
+ * usage intructions
+ *---------------------------------------------------------------------------*/
+void
+usage(void)
+{
+ fprintf(stderr,"\n");
+ fprintf(stderr,"isdndecode - isdn4bsd package ISDN decoder for passive cards (%d.%d.%d)\n", VERSION, REL, STEP);
+ fprintf(stderr,"usage: isdntrace -a -b -d -f <file> -h -i -l -n <val> -o -p <file> -r -u <unit>\n");
+ fprintf(stderr," -x -B -P -R <unit> -T <unit>\n");
+ fprintf(stderr," -a analyzer mode ................................... (default off)\n");
+ fprintf(stderr," -b switch B channel trace on ....................... (default off)\n");
+ fprintf(stderr," -d switch D channel trace off ....................... (default on)\n");
+ fprintf(stderr," -f <file> write output to file filename ........... (default %s0)\n", DECODE_FILE_NAME);
+ fprintf(stderr," -h don't print header for each message ............. (default off)\n");
+ fprintf(stderr," -i print I.430 (layer 1) INFO signals .............. (default off)\n");
+ fprintf(stderr," -l don't decode low layer Q.921 messages ........... (default off)\n");
+ fprintf(stderr," -o don't write output to a file .................... (default off)\n");
+ fprintf(stderr," -p <file> specify filename for -B and -P ........ (default %s0)\n", BIN_FILE_NAME);
+ fprintf(stderr," -u <unit> specify controller unit number ............... (default unit 0)\n");
+ fprintf(stderr," -x print packets with unknown protocoldiscriminator (default off)\n");
+ fprintf(stderr," -B write binary trace data to file filename ........ (default off)\n");
+ fprintf(stderr," -P playback from binary trace data file ............ (default off)\n");
+ fprintf(stderr," -R <unit> analyze Rx controller unit number (for -a) ... (default unit %d)\n", RxUDEF);
+ fprintf(stderr," -T <unit> analyze Tx controller unit number (for -a) ... (default unit %d)\n", TxUDEF);
+ fprintf(stderr,"\n");
+ exit(1);
+}
+
+/*---------------------------------------------------------------------------*
+ * main
+ *---------------------------------------------------------------------------*/
+int
+main(int argc, char *argv[])
+{
+ extern int optind;
+ extern int opterr;
+ extern char *optarg;
+ char devicename[80];
+ char headerbuf[256];
+
+ int n;
+ int c;
+ char *b;
+
+ char *outfile = DECODE_FILE_NAME;
+ char *binfile = BIN_FILE_NAME;
+ int outfileset = 0;
+ time_t tm;
+
+ i4b_trace_hdr_t *ithp = NULL;
+ int l;
+
+ b = &buf[sizeof(i4b_trace_hdr_t)];
+
+ while( (c = getopt(argc, argv, "abdf:hiln:op:u:xBPR:T:")) != -1)
+ {
+ switch(c)
+ {
+ case 'a':
+ analyze = 1;
+ break;
+
+ case 'b':
+ enable_trace |= (TRACE_B_RX | TRACE_B_TX);
+ break;
+
+ case 'd':
+ enable_trace &= (~(TRACE_D_TX | TRACE_D_RX));
+ break;
+
+ case 'o':
+ outflag = 0;
+ break;
+
+ case 'f':
+ outfile = optarg;
+ outfileset = 1;
+ break;
+
+ case 'h':
+ header = 0;
+ break;
+
+ case 'i':
+ enable_trace |= TRACE_I;
+ info = 1;
+ break;
+
+ case 'l':
+ print_q921 = 0;
+ break;
+
+ case 'p':
+ binfile = optarg;
+ bpopt = 1;
+ break;
+
+ case 'u':
+ unit = atoi(optarg);
+ if(unit < 0 || unit >= MAX_CONTROLLERS)
+ usage();
+ break;
+
+ case 'x':
+ xflag = 1;
+ break;
+
+ case 'B':
+ Bopt = 1;
+ break;
+
+ case 'P':
+ Popt = 1;
+ break;
+
+ case 'R':
+ Rx = atoi(optarg);
+ if(Rx < 0 || Rx >= MAX_CONTROLLERS)
+ usage();
+ break;
+
+ case 'T':
+ Tx = atoi(optarg);
+ if(Tx < 0 || Tx >= MAX_CONTROLLERS)
+ usage();
+ break;
+
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if(enable_trace == 0)
+ usage();
+
+ if(Bopt && Popt)
+ usage();
+
+ atexit(exit_hdl);
+
+ if(Bopt)
+ {
+ if(bpopt)
+ sprintf(BPfilename, "%s", binfile);
+ else
+ sprintf(BPfilename, "%s%d", BIN_FILE_NAME, unit);
+
+ if((BP = fopen(BPfilename, "r")) != NULL)
+ {
+ char buffer[1024];
+ fclose(BP);
+ sprintf(buffer, "%s%s", BPfilename, DECODE_FILE_NAME_BAK);
+ rename(BPfilename, buffer);
+ }
+ if((BP = fopen(BPfilename, "w")) == NULL)
+ {
+ char buffer[80];
+
+ sprintf(buffer, "Error opening file [%s]", BPfilename);
+ perror(buffer);
+ exit(1);
+ }
+
+ if((setvbuf(BP, (char *)NULL, _IONBF, 0)) != 0)
+ {
+ char buffer[80];
+
+ sprintf(buffer, "Error setting file [%s] to unbuffered", BPfilename);
+ perror(buffer);
+ exit(1);
+ }
+ }
+
+ if(Popt)
+ {
+ if(bpopt)
+ sprintf(BPfilename, "%s", binfile);
+ else
+ sprintf(BPfilename, "%s%d", BIN_FILE_NAME, unit);
+
+ if((BP = fopen(BPfilename, "r")) == NULL)
+ {
+ char buffer[80];
+
+ sprintf(buffer, "Error opening file [%s]", BPfilename);
+ perror(buffer);
+ exit(1);
+ }
+ }
+ else
+ {
+ sprintf(devicename, "%s%d", I4BTRC_DEVICE, unit);
+
+ if((f = open(devicename, O_RDWR)) < 0)
+ {
+ char buffer[80];
+
+ sprintf(buffer, "Error opening trace device [%s]", devicename);
+ perror(buffer);
+ exit(1);
+ }
+ }
+
+ if(outflag)
+ {
+ if(outfileset == 0)
+ sprintf(outfilename, "%s%d", DECODE_FILE_NAME, unit);
+ else
+ strcpy(outfilename, outfile);
+
+
+ if((Fout = fopen(outfilename, "r")) != NULL)
+ {
+ char buffer[1024];
+ fclose(Fout);
+ sprintf(buffer, "%s%s", outfilename, DECODE_FILE_NAME_BAK);
+ rename(outfilename, buffer);
+ }
+
+ if((Fout = fopen(outfilename, "w")) == NULL)
+ {
+ char buffer[80];
+
+ sprintf(buffer, "Error opening file [%s]", outfilename);
+ perror(buffer);
+ exit(1);
+ }
+
+ if((setvbuf(Fout, (char *)NULL, _IONBF, 0)) != 0)
+ {
+ char buffer[80];
+
+ sprintf(buffer, "Error setting file [%s] to unbuffered", outfile);
+ perror(buffer);
+ exit(1);
+ }
+ }
+
+ if((setvbuf(stdout, (char *)NULL, _IOLBF, 0)) != 0)
+ {
+ char buffer[80];
+
+ sprintf(buffer, "Error setting stdout to line-buffered");
+ perror(buffer);
+ exit(1);
+ }
+
+ if(!Popt)
+ {
+ if((switch_driver(enable_trace, Rx, Tx)) == -1)
+ exit(1);
+ else
+ traceon = 1;
+ }
+
+ signal(SIGHUP, SIG_IGN); /* ignore hangup signal */
+ signal(SIGUSR1, reopenfiles); /* rotate logfile(s) */
+
+ time(&tm);
+
+ if(analyze)
+ {
+ sprintf(headerbuf, "\n==== isdnanalyze controller rx #%d - tx #%d ==== started %s",
+ Rx, Tx, ctime(&tm));
+ }
+ else
+ {
+ sprintf(headerbuf, "\n=========== isdntrace controller #%d =========== started %s",
+ unit, ctime(&tm));
+ }
+
+ printf("%s", headerbuf);
+
+ if(outflag)
+ fprintf(Fout, "%s", headerbuf);
+
+ for (;;)
+ {
+ if(Popt == 0)
+ {
+ n = read(f, buf, BSIZE);
+
+ if(Bopt)
+ {
+ if((fwrite(buf, 1, n, BP)) != n)
+ {
+ char buffer[80];
+ sprintf(buffer, "Error writing file [%s]", BPfilename);
+ perror(buffer);
+ exit(1);
+ }
+ }
+
+ n -= sizeof(i4b_trace_hdr_t);
+ }
+ else
+ {
+ if((fread(buf, 1, sizeof(i4b_trace_hdr_t), BP)) != sizeof(i4b_trace_hdr_t))
+ {
+ if(feof(BP))
+ {
+ printf("\nEnd of playback input file reached.\n");
+ exit(0);
+ }
+ else
+ {
+ char buffer[80];
+ sprintf(buffer, "Error reading hdr from file [%s]", BPfilename);
+ perror(buffer);
+ exit(1);
+ }
+ }
+
+ ithp = (i4b_trace_hdr_t *)buf;
+ l = ithp->length - sizeof(i4b_trace_hdr_t);
+
+ if((n = fread(buf+sizeof(i4b_trace_hdr_t), 1, l , BP)) != l)
+ {
+ char buffer[80];
+ sprintf(buffer, "Error reading data from file [%s]", BPfilename);
+ perror(buffer);
+ exit(1);
+ }
+
+ }
+
+ if(n > 0)
+ {
+ dumpbuf(n, b, (i4b_trace_hdr_t *)buf);
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * format header into static buffer, return buffer address
+ *---------------------------------------------------------------------------*/
+char *
+fmt_hdr(i4b_trace_hdr_t *hdr, int frm_len)
+{
+ struct tm *s;
+ static char hbuf[256];
+ int i = 0;
+
+ s = localtime((time_t *)&(hdr->time.tv_sec));
+
+ if(hdr->type == TRC_CH_I) /* Layer 1 INFO's */
+ {
+ sprintf(hbuf,"\n-- %s - unit:%d ---------------- time:%2.2d.%2.2d %2.2d:%2.2d:%2.2d.%06u ",
+ ((hdr->dir) ? "NT->TE" : "TE->NT"),
+ hdr->unit,
+ s->tm_mday,
+ s->tm_mon + 1,
+ s->tm_hour,
+ s->tm_min,
+ s->tm_sec,
+ (u_int32_t)hdr->time.tv_usec);
+ }
+ else
+ {
+ if(hdr->trunc > 0)
+ {
+ sprintf(hbuf,"\n-- %s - unit:%d - frame:%6.6u - time:%2.2d.%2.2d %2.2d:%2.2d:%2.2d.%06u - length:%d (%d) ",
+ ((hdr->dir) ? "NT->TE" : "TE->NT"),
+ hdr->unit,
+ hdr->count,
+ s->tm_mday,
+ s->tm_mon + 1,
+ s->tm_hour,
+ s->tm_min,
+ s->tm_sec,
+ (u_int32_t)hdr->time.tv_usec,
+ frm_len,
+ hdr->trunc);
+ }
+ else
+ {
+ sprintf(hbuf,"\n-- %s - unit:%d - frame:%6.6u - time:%2.2d.%2.2d %2.2d:%2.2d:%2.2d.%06u - length:%d ",
+ ((hdr->dir) ? "NT->TE" : "TE->NT"),
+ hdr->unit,
+ hdr->count,
+ s->tm_mday,
+ s->tm_mon + 1,
+ s->tm_hour,
+ s->tm_min,
+ s->tm_sec,
+ (u_int32_t)hdr->time.tv_usec,
+ frm_len);
+ }
+ }
+
+ for(i=strlen(hbuf); i <= NCOLS;)
+ hbuf[i++] = '-';
+
+ hbuf[i++] = '\n';
+ hbuf[i] = '\0';
+
+ return(hbuf);
+}
+
+/*---------------------------------------------------------------------------*
+ * decode protocol and output to file(s)
+ *---------------------------------------------------------------------------*/
+static void
+dumpbuf(int n, unsigned char *buf, i4b_trace_hdr_t *hdr)
+{
+ static char l1buf[128];
+ static unsigned char l2buf[32000];
+ static unsigned char l3buf[32000];
+ int cnt;
+ int nsave = n;
+ char *pbuf;
+ int i, j;
+
+ l1buf[0] = '\0';
+ l2buf[0] = '\0';
+ l3buf[0] = '\0';
+
+ switch(hdr->type)
+ {
+ case TRC_CH_I: /* Layer 1 INFO's */
+ if(enable_trace & TRACE_I)
+ layer1(l1buf, buf);
+ break;
+
+ case TRC_CH_D: /* D-channel data */
+ cnt = layer2(l2buf, buf, hdr->dir, print_q921);
+
+ if(print_q921 == 0)
+ l2buf[0] = '\0';
+
+ n -= cnt;
+ buf += cnt;
+
+ if(n)
+ {
+ if((*buf != 0x08) && (xflag == 0))
+ {
+ l2buf[0] = '\0';
+ l3buf[0] = '\0';
+ break;
+ }
+ layer3(l3buf, n, cnt, buf);
+ }
+ break;
+
+ default: /* B-channel data */
+
+ pbuf = &l2buf[0];
+
+ for (i = 0; i < n; i += 16)
+ {
+ sprintf((pbuf+strlen(pbuf)),"B%d:%.3x ", hdr->type, i);
+
+ for (j = 0; j < 16; j++)
+ if (i + j < n)
+ sprintf((pbuf+strlen(pbuf)),"%02x ", buf[i + j]);
+ else
+ sprintf((pbuf+strlen(pbuf))," ");
+
+ sprintf((pbuf+strlen(pbuf))," ");
+
+ for (j = 0; j < 16 && i + j < n; j++)
+ if (isprint(buf[i + j]))
+ sprintf((pbuf+strlen(pbuf)),"%c", buf[i + j]);
+ else
+ sprintf((pbuf+strlen(pbuf)),".");
+
+ sprintf((pbuf+strlen(pbuf)),"\n");
+ }
+ break;
+ }
+
+ if(header && ((l1buf[0] != '\0' || l2buf[0] != '\0') || (l3buf[0] != 0)))
+ {
+ char *p;
+ p = fmt_hdr(hdr, nsave);
+ printf("%s", p);
+ if(outflag)
+ fprintf(Fout, "%s", p);
+ }
+
+ if(l1buf[0] != '\0')
+ {
+ printf("%s", l1buf);
+ if(outflag)
+ fprintf(Fout, "%s", l1buf);
+ }
+
+ if(l2buf[0] != '\0')
+ {
+ printf("%s", l2buf);
+ if(outflag)
+ fprintf(Fout, "%s", l2buf);
+ }
+
+ if(l3buf[0] != '\0')
+ {
+ printf("%s", l3buf);
+ if(outflag)
+ fprintf(Fout, "%s", l3buf);
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * exit handler function to be called at program exit
+ *---------------------------------------------------------------------------*/
+void
+exit_hdl()
+{
+ if(traceon)
+ switch_driver(TRACE_OFF, Rx, Tx);
+}
+
+/*---------------------------------------------------------------------------*
+ * switch driver debugging output on/off
+ *---------------------------------------------------------------------------*/
+static int
+switch_driver(int value, int rx, int tx)
+{
+ char buffer[80];
+ int v = value;
+
+ if(analyze == 0)
+ {
+ if(ioctl(f, I4B_TRC_SET, &v) < 0)
+ {
+ sprintf(buffer, "Error ioctl I4B_TRC_SET, val = %d", v);
+ perror(buffer);
+ return(-1);
+ }
+ }
+ else
+ {
+ if(value == TRACE_OFF)
+ {
+ if(ioctl(f, I4B_TRC_RESETA, &v) < 0)
+ {
+ sprintf(buffer, "Error ioctl I4B_TRC_RESETA - ");
+ perror(buffer);
+ return(-1);
+ }
+ }
+ else
+ {
+ i4b_trace_setupa_t tsa;
+
+ tsa.rxunit = rx;
+ tsa.rxflags = value;
+ tsa.txunit = tx;
+ tsa.txflags = value;
+
+ if(ioctl(f, I4B_TRC_SETA, &tsa) < 0)
+ {
+ sprintf(buffer, "Error ioctl I4B_TRC_SETA, val = %d", v);
+ perror(buffer);
+ return(-1);
+ }
+ }
+ }
+ return(0);
+}
+
+/*---------------------------------------------------------------------------*
+ * reopen files to support rotating logfile(s) on SIGUSR1
+ *
+ * based on an idea from Ripley (ripley@nostromo.in-berlin.de)
+ *
+ * close file and reopen it for append. this will be a nop
+ * if the previously opened file hasn't moved but will open
+ * a new one otherwise, thus enabling a rotation...
+ *
+ *---------------------------------------------------------------------------*/
+static void
+reopenfiles(int dummy)
+{
+ if(outflag)
+ {
+ fclose(Fout);
+
+ if((Fout = fopen(outfilename, "a")) == NULL)
+ {
+ char buffer[80];
+
+ sprintf(buffer, "Error re-opening file [%s]", outfilename);
+ perror(buffer);
+ exit(1);
+ }
+
+ if((setvbuf(Fout, (char *)NULL, _IONBF, 0)) != 0)
+ {
+ char buffer[80];
+
+ sprintf(buffer, "Error re-setting file [%s] to unbuffered", outfilename);
+ perror(buffer);
+ exit(1);
+ }
+ }
+
+ if(Bopt)
+ {
+
+ fclose(BP);
+
+ if((BP = fopen(BPfilename, "a")) == NULL)
+ {
+ char buffer[80];
+
+ sprintf(buffer, "Error re-opening file [%s]", BPfilename);
+ perror(buffer);
+ exit(1);
+ }
+
+ if((setvbuf(BP, (char *)NULL, _IONBF, 0)) != 0)
+ {
+ char buffer[80];
+
+ sprintf(buffer, "Error re-setting file [%s] to unbuffered", BPfilename);
+ perror(buffer);
+ exit(1);
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * decode extension bit
+ *---------------------------------------------------------------------------*/
+void
+extension(int layer, char *buffer, int cnt, unsigned char value, unsigned char mask)
+{
+ sprintline(layer, buffer, cnt, value, mask, "Extension Bit = %c (%s)",
+ (value & mask) ? '1' : '0',
+ (value & mask) ? "no extension, final octet" : "with extension, octet follows");
+}
+
+/*---------------------------------------------------------------------------*
+ * print bits as 0/1 available for mask
+ *---------------------------------------------------------------------------*/
+static char *
+print_bits(unsigned char val, unsigned char mask)
+{
+ static char buffer[10];
+ int i = 0;
+ int length = 8;
+
+ while(length--)
+ {
+ if(mask & 0x80)
+ {
+ if(val & 0x80)
+ buffer[i++] = '1';
+ else
+ buffer[i++] = '0';
+ }
+ else
+ {
+ buffer[i++] = '-';
+ }
+ val = val << 1;
+ mask = mask << 1;
+ }
+ buffer[i] = '\0';
+ return(buffer);
+}
+
+/*---------------------------------------------------------------------------*
+ * print one decoded output line
+ *---------------------------------------------------------------------------*/
+void
+sprintline(int layer, char *buffer, int oct_count, int oct_val,
+ int oct_mask, const char *fmt, ...)
+{
+ char lbuffer[256];
+ static int lastcount = -1;
+ char *ptr;
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ if(oct_count != lastcount)
+ {
+ lastcount = oct_count;
+
+ sprintf(lbuffer, "L%d %2d %02X %s ",
+ layer,
+ oct_count,
+ oct_val,
+ print_bits(oct_val, oct_mask));
+ }
+ else
+ {
+ sprintf(lbuffer, " %s ",
+ print_bits(oct_val, oct_mask));
+ }
+
+ vsprintf(lbuffer+strlen(lbuffer), fmt, ap);
+
+ va_end(ap);
+
+ sprintf(lbuffer+strlen(lbuffer), "\n");
+
+ if((ptr = rindex(lbuffer, '(')) != NULL)
+ {
+ char *s = lbuffer;
+ char *b = buffer;
+ int len = strlen(lbuffer);
+ int i;
+
+ for(s = lbuffer; s < ptr; *b++ = *s++)
+ ;
+ for(i = 0;(i+len) <= NCOLS; *b++ = ' ', i++)
+ ;
+ for(; *s; *b++ = *s++)
+ ;
+ *b = '\0';
+ }
+ else
+ {
+ strcpy(buffer, lbuffer);
+ }
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdndecode/pcause.c b/usr.sbin/i4b/isdndecode/pcause.c
new file mode 100644
index 0000000..610a960
--- /dev/null
+++ b/usr.sbin/i4b/isdndecode/pcause.c
@@ -0,0 +1,330 @@
+/*
+ * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * pcause.c - printing cause values
+ * --------------------------------
+ *
+ * $Id: pcause.c,v 1.5 1999/12/13 21:25:25 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:51:20 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "decode.h"
+#include "pcause.h"
+
+char *
+print_cause_q850(unsigned char code)
+{
+ static char error_message[120];
+ char *e;
+
+ switch(code)
+ {
+ case CAUSE_Q850_SHUTDN:
+ e = "normal D-channel shutdown";
+ break;
+
+ case CAUSE_Q850_NUNALLC:
+ e = "Unallocated (unassigned) number";
+ break;
+
+ case CAUSE_Q850_NRTTN:
+ e = "No route to specified transit network";
+ break;
+
+ case CAUSE_Q850_NRTDST:
+ e = "No route to destination";
+ break;
+
+ case CAUSE_Q850_SSINFTN:
+ e = "Send special information tone";
+ break;
+
+ case CAUSE_Q850_MDIALTP:
+ e = "Misdialled trunk prefix";
+ break;
+
+ case CAUSE_Q850_CHUNACC:
+ e = "Channel unacceptable";
+ break;
+
+ case CAUSE_Q850_CALLAWD:
+ e = "Call awarded and being delivered in an established channel";
+ break;
+
+ case CAUSE_Q850_PREEMPT:
+ e = "Preemption";
+ break;
+
+ case CAUSE_Q850_PREECRR:
+ e = "Preemption - circuit reserved for reuse";
+ break;
+
+ case CAUSE_Q850_NCCLR:
+ e = "Normal call clearing";
+ break;
+
+ case CAUSE_Q850_USRBSY:
+ e = "User busy";
+ break;
+
+ case CAUSE_Q850_NOUSRRSP:
+ e = "No user responding";
+ break;
+
+ case CAUSE_Q850_NOANSWR:
+ e = "No answer from user (user alerted)";
+ break;
+
+ case CAUSE_Q850_SUBSABS:
+ e = "Subscriber absent";
+ break;
+
+ case CAUSE_Q850_CALLREJ:
+ e = "Call rejected";
+ break;
+
+ case CAUSE_Q850_NUCHNG:
+ e = "Number changed";
+ break;
+
+ case CAUSE_Q850_NONSELUC:
+ e = "Non-selected user clearing";
+ break;
+
+ case CAUSE_Q850_DSTOOORDR:
+ e = "Destination out of order";
+ break;
+
+ case CAUSE_Q850_INVNUFMT:
+ e = "Invalid number format";
+ break;
+
+ case CAUSE_Q850_FACREJ:
+ e = "Facility rejected";
+ break;
+
+ case CAUSE_Q850_STENQRSP:
+ e = "Response to STATUS ENQUIRY";
+ break;
+
+ case CAUSE_Q850_NORMUNSP:
+ e = "Normal, unspecified";
+ break;
+
+ case CAUSE_Q850_NOCAVAIL:
+ e = "No circuit / channel available";
+ break;
+
+ case CAUSE_Q850_NETOOORDR:
+ e = "Network out of order";
+ break;
+
+ case CAUSE_Q850_PFMCDOOSERV:
+ e = "Permanent frame mode connection out of service";
+ break;
+
+ case CAUSE_Q850_PFMCOPER:
+ e = "Permanent frame mode connection operational";
+ break;
+
+ case CAUSE_Q850_TMPFAIL:
+ e = "Temporary failure";
+ break;
+
+ case CAUSE_Q850_SWEQCONG:
+ e = "Switching equipment congestion";
+ break;
+
+ case CAUSE_Q850_ACCINFDIS:
+ e = "Access information discarded";
+ break;
+
+ case CAUSE_Q850_REQCNOTAV:
+ e = "Requested circuit/channel not available";
+ break;
+
+ case CAUSE_Q850_PRECALBLK:
+ e = "Precedence call blocked";
+ break;
+
+ case CAUSE_Q850_RESUNAVAIL:
+ e = "Resources unavailable, unspecified";
+ break;
+
+ case CAUSE_Q850_QOSUNAVAIL:
+ e = "Quality of service unavailable";
+ break;
+
+ case CAUSE_Q850_REQSERVNS:
+ e = "Requested facility not subscribed";
+ break;
+
+ case CAUSE_Q850_OCBARRCUG:
+ e = "Outgoing calls barred within CUG";
+ break;
+
+ case CAUSE_Q850_ICBARRCUG:
+ e = "Incoming calls barred within CUG";
+ break;
+
+ case CAUSE_Q850_BCAPNAUTH:
+ e = "Bearer capability not authorized";
+ break;
+
+ case CAUSE_Q850_BCAPNAVAIL:
+ e = "Bearer capability not presently available";
+ break;
+
+ case CAUSE_Q850_INCSTOACISC:
+ e = "Inconsistenciy in designated outg. access info and subscriber class";
+ break;
+
+ case CAUSE_Q850_SOONOTAVAIL:
+ e = "Service or option not available, unspecified";
+ break;
+
+ case CAUSE_Q850_BCAPNOTIMPL:
+ e = "Bearer capability not implemented";
+ break;
+
+ case CAUSE_Q850_CHTYPNIMPL:
+ e = "Channel type not implemented";
+ break;
+
+ case CAUSE_Q850_REQFACNIMPL:
+ e = "Requested facility not implemented";
+ break;
+
+ case CAUSE_Q850_ORDINBCAVL:
+ e = "Only restricted digital information bearer capability is available";
+ break;
+
+ case CAUSE_Q850_SOONOTIMPL:
+ e = "Service or option not implemented, unspecified";
+ break;
+
+ case CAUSE_Q850_INVCLRFVAL:
+ e = "Invalid call reference value";
+ break;
+
+ case CAUSE_Q850_IDCHDNOEX:
+ e = "Identified channel does not exist";
+ break;
+
+ case CAUSE_Q850_SUSCAEXIN:
+ e = "A suspended call exists, but this call identity does not";
+ break;
+
+ case CAUSE_Q850_CLIDINUSE:
+ e = "Call identity in use";
+ break;
+
+ case CAUSE_Q850_NOCLSUSP:
+ e = "No call suspended";
+ break;
+
+ case CAUSE_Q850_CLIDCLRD:
+ e = "Call having the requested call identity has been cleared";
+ break;
+
+ case CAUSE_Q850_UNOTMEMCUG:
+ e = "User not member of CUG";
+ break;
+
+ case CAUSE_Q850_INCDEST:
+ e = "Incompatible destination";
+ break;
+
+ case CAUSE_Q850_NONEXCUG:
+ e = "Non-existent CUG";
+ break;
+
+ case CAUSE_Q850_INVNTWSEL:
+ e = "Invalid transit network selection";
+ break;
+
+ case CAUSE_Q850_INVMSG:
+ e = "Invalid message, unspecified";
+ break;
+
+ case CAUSE_Q850_MIEMISS:
+ e = "Mandatory information element is missing";
+ break;
+
+ case CAUSE_Q850_MSGTNI:
+ e = "Message type non-existent or not implemented";
+ break;
+
+ case CAUSE_Q850_MSGNCMPT:
+ e = "Msg incompatible with call state/message type non-existent/not implemented";
+ break;
+
+ case CAUSE_Q850_IENENI:
+ e = "Information element/parameter non-existent or not implemented";
+ break;
+
+ case CAUSE_Q850_INVIEC:
+ e = "Invalid information element contents";
+ break;
+
+ case CAUSE_Q850_MSGNCWCS:
+ e = "Message not compatible with call state";
+ break;
+
+ case CAUSE_Q850_RECOTIMEXP:
+ e = "Recovery on timer expiry";
+ break;
+
+ case CAUSE_Q850_PARMNENIPO:
+ e = "Parameter non-existent or not implemented, passed on";
+ break;
+
+ case CAUSE_Q850_MSGUNRDPRM:
+ e = "Message with unrecognized parameter, discarded";
+ break;
+
+ case CAUSE_Q850_PROTERR:
+ e = "Protocol error, unspecified";
+ break;
+
+ case CAUSE_Q850_INTWRKU:
+ e = "Interworking, unspecified";
+ break;
+
+ default:
+ e = "ERROR, unknown cause value!";
+ break;
+ }
+
+ sprintf(error_message, "%d: %s (Q.850)", code, e);
+ return(error_message);
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdndecode/pcause.h b/usr.sbin/i4b/isdndecode/pcause.h
new file mode 100644
index 0000000..2c1c7f4
--- /dev/null
+++ b/usr.sbin/i4b/isdndecode/pcause.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * pcause.h - Q.850 causes definitions
+ * -----------------------------------
+ *
+ * $Id: pcause.h,v 1.4 1999/12/13 21:25:25 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:51:32 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+char *print_cause_q850(unsigned char code);
+
+/* Q.850 causes */
+
+#define CAUSE_Q850_SHUTDN 0x00 /* normal D-channel shutdown */
+#define CAUSE_Q850_NUNALLC 0x01 /* Unallocated (unassigned) number */
+#define CAUSE_Q850_NRTTN 0x02 /* No route to specified transit network */
+#define CAUSE_Q850_NRTDST 0x03 /* No route to destination */
+#define CAUSE_Q850_SSINFTN 0x04 /* Send special information tone */
+#define CAUSE_Q850_MDIALTP 0x05 /* Misdialled trunk prefix */
+#define CAUSE_Q850_CHUNACC 0x06 /* Channel unacceptable */
+#define CAUSE_Q850_CALLAWD 0x07 /* Call awarded and being delivered in an established channel */
+#define CAUSE_Q850_PREEMPT 0x08 /* Preemption */
+#define CAUSE_Q850_PREECRR 0x09 /* Preemption - circuit reserved for reuse */
+#define CAUSE_Q850_NCCLR 0x10 /* Normal call clearing */
+#define CAUSE_Q850_USRBSY 0x11 /* User busy */
+#define CAUSE_Q850_NOUSRRSP 0x12 /* No user responding */
+#define CAUSE_Q850_NOANSWR 0x13 /* No answer from user (user alerted) */
+#define CAUSE_Q850_SUBSABS 0x14 /* Subscriber absent */
+#define CAUSE_Q850_CALLREJ 0x15 /* Call rejected */
+#define CAUSE_Q850_NUCHNG 0x16 /* Number changed */
+#define CAUSE_Q850_NONSELUC 0x1A /* Non-selected user clearing */
+#define CAUSE_Q850_DSTOOORDR 0x1B /* Destination out of order */
+#define CAUSE_Q850_INVNUFMT 0x1C /* Invalid number format */
+#define CAUSE_Q850_FACREJ 0x1D /* Facility rejected */
+#define CAUSE_Q850_STENQRSP 0x1E /* Response to STATUS ENQUIRY */
+#define CAUSE_Q850_NORMUNSP 0x1F /* Normal, unspecified */
+#define CAUSE_Q850_NOCAVAIL 0x22 /* No circuit / channel available */
+#define CAUSE_Q850_NETOOORDR 0x26 /* Network out of order */
+#define CAUSE_Q850_PFMCDOOSERV 0x27 /* Permanent frame mode connection out of service */
+#define CAUSE_Q850_PFMCOPER 0x28 /* Permanent frame mode connection operational */
+#define CAUSE_Q850_TMPFAIL 0x29 /* Temporary failure */
+#define CAUSE_Q850_SWEQCONG 0x2A /* Switching equipment congestion */
+#define CAUSE_Q850_ACCINFDIS 0x2B /* Access information discarded */
+#define CAUSE_Q850_REQCNOTAV 0x2C /* Requested circuit/channel not available */
+#define CAUSE_Q850_PRECALBLK 0x2E /* Precedence call blocked */
+#define CAUSE_Q850_RESUNAVAIL 0x2F /* Resources unavailable, unspecified */
+#define CAUSE_Q850_QOSUNAVAIL 0x31 /* Quality of service unavailable */
+#define CAUSE_Q850_REQSERVNS 0x32 /* Requested facility not subscribed */
+#define CAUSE_Q850_OCBARRCUG 0x35 /* Outgoing calls barred within CUG */
+#define CAUSE_Q850_ICBARRCUG 0x36 /* Incoming calls barred within CUG */
+#define CAUSE_Q850_BCAPNAUTH 0x39 /* Bearer capability not authorized */
+#define CAUSE_Q850_BCAPNAVAIL 0x3A /* Bearer capability not presently available */
+#define CAUSE_Q850_INCSTOACISC 0x3E /* Inconsistenciy in designated outgoing access information and subscriber class */
+#define CAUSE_Q850_SOONOTAVAIL 0x3F /* Service or option not available, unspecified */
+#define CAUSE_Q850_BCAPNOTIMPL 0x41 /* Bearer capability not implemented */
+#define CAUSE_Q850_CHTYPNIMPL 0x42 /* Channel type not implemented */
+#define CAUSE_Q850_REQFACNIMPL 0x45 /* Requested facility not implemented */
+#define CAUSE_Q850_ORDINBCAVL 0x46 /* Only restricted digital information bearer capability is available */
+#define CAUSE_Q850_SOONOTIMPL 0x4F /* Service or option not implemented, unspecified */
+#define CAUSE_Q850_INVCLRFVAL 0x51 /* Invalid call reference value */
+#define CAUSE_Q850_IDCHDNOEX 0x52 /* Identified channel does not exist */
+#define CAUSE_Q850_SUSCAEXIN 0x53 /* A suspended call exists, but this call identity does not */
+#define CAUSE_Q850_CLIDINUSE 0x54 /* Call identity in use */
+#define CAUSE_Q850_NOCLSUSP 0x55 /* No call suspended */
+#define CAUSE_Q850_CLIDCLRD 0x56 /* Call having the requested call identity has been cleared */
+#define CAUSE_Q850_UNOTMEMCUG 0x57 /* User not member of CUG */
+#define CAUSE_Q850_INCDEST 0x58 /* Incompatible destination */
+#define CAUSE_Q850_NONEXCUG 0x5A /* Non-existent CUG */
+#define CAUSE_Q850_INVNTWSEL 0x5B /* Invalid transit network selection */
+#define CAUSE_Q850_INVMSG 0x5F /* Invalid message, unspecified */
+#define CAUSE_Q850_MIEMISS 0x60 /* Mandatory information element is missing */
+#define CAUSE_Q850_MSGTNI 0x61 /* Message type non-existent or not implemented */
+#define CAUSE_Q850_MSGNCMPT 0x62 /* Message not compatible with call state or message type non-existent or not implemented */
+#define CAUSE_Q850_IENENI 0x63 /* Information element/parameter non-existent or not implemented */
+#define CAUSE_Q850_INVIEC 0x64 /* Invalid information element contents */
+#define CAUSE_Q850_MSGNCWCS 0x65 /* Message not compatible with call state */
+#define CAUSE_Q850_RECOTIMEXP 0x66 /* Recovery on timer expiry */
+#define CAUSE_Q850_PARMNENIPO 0x67 /* Parameter non-existent or not implemented, passed on */
+#define CAUSE_Q850_MSGUNRDPRM 0x6E /* Message with unrecognized parameter, discarded */
+#define CAUSE_Q850_PROTERR 0x6F /* Protocol error, unspecified */
+#define CAUSE_Q850_INTWRKU 0x7F /* Interworking, unspecified */
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnmonitor/Makefile b/usr.sbin/i4b/isdnmonitor/Makefile
new file mode 100644
index 0000000..84f5c16
--- /dev/null
+++ b/usr.sbin/i4b/isdnmonitor/Makefile
@@ -0,0 +1,13 @@
+# $FreeBSD$
+
+PROG= isdnmonitor
+SRCS= main.c curses.c
+MAN= isdnmonitor.8
+
+# compile debug support
+COPTS+= -DDEBUG
+
+DPADD= ${LIBCURSES}
+LDADD= -lcurses
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/i4b/isdnmonitor/curses.c b/usr.sbin/i4b/isdnmonitor/curses.c
new file mode 100644
index 0000000..1e7eec1
--- /dev/null
+++ b/usr.sbin/i4b/isdnmonitor/curses.c
@@ -0,0 +1,624 @@
+/*
+ * Copyright (c) 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * i4b daemon - curses fullscreen output
+ * -------------------------------------
+ *
+ * $Id: curses.c,v 1.10 1999/12/13 21:25:25 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:51:47 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "monprivate.h"
+
+#ifndef WIN32
+
+static void display_bell(void);
+static void display_chans(void);
+
+/*---------------------------------------------------------------------------*
+ * program exit
+ *---------------------------------------------------------------------------*/
+void
+do_exit(int exitval)
+{
+ if(curses_ready)
+ endwin();
+ exit(exitval);
+}
+
+/*---------------------------------------------------------------------------*
+ * init curses fullscreen display
+ *---------------------------------------------------------------------------*/
+void
+init_screen(void)
+{
+ char buffer[512];
+ int uheight, lheight;
+ int i, j;
+
+ initscr(); /* curses init */
+
+ if((COLS < 80) || (LINES < 24))
+ {
+ endwin();
+ fprintf(stderr, "ERROR, minimal screensize must be 80x24, is %dx%d, terminating!",COLS, LINES);
+ exit(1);
+ }
+
+ noecho();
+ raw();
+
+ uheight = nctrl * 2; /* cards * b-channels */
+ lheight = LINES - uheight - 6 + 1; /* rest of display */
+
+ if((upper_w = newwin(uheight, COLS, UPPER_B, 0)) == NULL)
+ {
+ endwin();
+ fprintf(stderr, "ERROR, curses init upper window, terminating!");
+ exit(1);
+ }
+
+ if((mid_w = newwin(1, COLS, UPPER_B+uheight+1, 0)) == NULL)
+ {
+ endwin();
+ fprintf(stderr, "ERROR, curses init mid window, terminating!");
+ exit(1);
+ }
+
+ if((lower_w = newwin(lheight, COLS, UPPER_B+uheight+3, 0)) == NULL)
+ {
+ endwin();
+ fprintf(stderr, "ERROR, curses init lower window, LINES = %d, lheight = %d, uheight = %d, terminating!", LINES, lheight, uheight);
+ exit(1);
+ }
+
+ scrollok(lower_w, 1);
+
+ sprintf(buffer, "----- isdn controller channel state ------------- isdnmonitor %02d.%02d.%d -", VERSION, REL, STEP);
+
+ while(strlen(buffer) < COLS)
+ strcat(buffer, "-");
+
+ move(0, 0);
+ standout();
+ addstr(buffer);
+ standend();
+
+ move(1, 0);
+ /* 01234567890123456789012345678901234567890123456789012345678901234567890123456789 */
+ addstr("c tei b remote iface dir outbytes obps inbytes ibps units");
+
+ if(hostname)
+ sprintf(buffer, "----- isdn userland interface state ------------- %s:%d -", hostname, portno);
+ else
+ sprintf(buffer, "----- isdn userland interface state ------------- %s -", sockpath);
+
+ while(strlen(buffer) < COLS)
+ strcat(buffer, "-");
+
+ move(uheight+2, 0);
+ standout();
+ addstr(buffer);
+ standend();
+
+ sprintf(buffer, "----- isdnd logfile display --------------------------------------------------");
+ while(strlen(buffer) < COLS)
+ strcat(buffer, "-");
+
+ move(uheight+4, 0);
+ standout();
+ addstr(buffer);
+ standend();
+
+ refresh();
+
+ for(i=0, j=0; i <= nctrl; i++, j+=2)
+ {
+ mvwprintw(upper_w, j, H_CNTL, "%d --- 1 ", i); /*TEI*/
+ mvwprintw(upper_w, j+1, H_CNTL, " L12 2 ");
+ }
+ wrefresh(upper_w);
+
+#ifdef NOTDEF
+ for(i=0, j=0; i < nentries; i++) /* walk thru all entries */
+ {
+ p = &cfg_entry_tab[i]; /* get ptr to enry */
+
+ mvwprintw(mid_w, 0, j, "%s%d ", bdrivername(p->usrdevicename), p->usrdeviceunit);
+
+ p->fs_position = j;
+
+ j += ((strlen(bdrivername(p->usrdevicename)) + (p->usrdeviceunit > 9 ? 2 : 1) + 1));
+ }
+#else
+ mvwprintw(mid_w, 0, 0, "%s", devbuf);
+#endif
+ wrefresh(mid_w);
+
+ wmove(lower_w, 0, 0);
+ wrefresh(lower_w);
+
+ curses_ready = 1;
+}
+
+/*---------------------------------------------------------------------------*
+ * display the charge in units
+ *---------------------------------------------------------------------------*/
+void
+display_charge(int pos, int charge)
+{
+ mvwprintw(upper_w, pos, H_UNITS, "%d", charge);
+ wclrtoeol(upper_w);
+ wrefresh(upper_w);
+}
+
+/*---------------------------------------------------------------------------*
+ * display the calculated charge in units
+ *---------------------------------------------------------------------------*/
+void
+display_ccharge(int pos, int units)
+{
+ mvwprintw(upper_w, pos, H_UNITS, "(%d)", units);
+ wclrtoeol(upper_w);
+ wrefresh(upper_w);
+}
+
+/*---------------------------------------------------------------------------*
+ * display accounting information
+ *---------------------------------------------------------------------------*/
+void
+display_acct(int pos, int obyte, int obps, int ibyte, int ibps)
+{
+ mvwprintw(upper_w, pos, H_OUT, "%-10d", obyte);
+ mvwprintw(upper_w, pos, H_OUTBPS, "%-4d", obps);
+ mvwprintw(upper_w, pos, H_IN, "%-10d", ibyte);
+ mvwprintw(upper_w, pos, H_INBPS, "%-4d", ibps);
+ wrefresh(upper_w);
+}
+
+/*---------------------------------------------------------------------------*
+ * erase line at disconnect time
+ *---------------------------------------------------------------------------*/
+void
+display_disconnect(int pos)
+{
+ wmove(upper_w, pos, H_TELN);
+ wclrtoeol(upper_w);
+ wrefresh(upper_w);
+
+ if(do_bell)
+ display_bell();
+}
+
+/*---------------------------------------------------------------------------*
+ * display interface up/down information
+ *---------------------------------------------------------------------------*/
+void
+display_updown(int pos, int updown, char *device)
+{
+ if(updown)
+ wstandend(mid_w);
+ else
+ wstandout(mid_w);
+
+ mvwprintw(mid_w, 0, pos, "%s ", device);
+
+ wstandend(mid_w);
+ wrefresh(mid_w);
+}
+
+/*---------------------------------------------------------------------------*
+ * display interface up/down information
+ *---------------------------------------------------------------------------*/
+void
+display_l12stat(int controller, int layer, int state)
+{
+ if(controller > nctrl)
+ return;
+
+ if(!(layer == 1 || layer == 2))
+ return;
+
+ if(state)
+ wstandout(upper_w);
+ else
+ wstandend(upper_w);
+
+ if(layer == 1)
+ {
+ mvwprintw(upper_w, (controller*2)+1, H_TEI+1, "1");
+
+ if(!state)
+ mvwprintw(upper_w, (controller*2)+1, H_TEI+2, "2");
+ }
+ else if(layer == 2)
+ {
+ mvwprintw(upper_w, (controller*2)+1, H_TEI+2, "2");
+ if(state)
+ mvwprintw(upper_w, (controller*2)+1, H_TEI+1, "1");
+ }
+
+ wstandend(upper_w);
+ wrefresh(upper_w);
+}
+
+/*---------------------------------------------------------------------------*
+ * display TEI
+ *---------------------------------------------------------------------------*/
+void
+display_tei(int controller, int tei)
+{
+ if(controller > nctrl)
+ return;
+
+ if(tei == -1)
+ mvwprintw(upper_w, controller*2, H_TEI, "---");
+ else
+ mvwprintw(upper_w, controller*2, H_TEI, "%3d", tei);
+
+ wrefresh(upper_w);
+}
+
+/*---------------------------------------------------------------------------*
+ * display bell :-)
+ *---------------------------------------------------------------------------*/
+static void
+display_bell(void)
+{
+ static char bell[1] = { 0x07 };
+ write(STDOUT_FILENO, &bell[0], 1);
+}
+
+/*---------------------------------------------------------------------------*
+ * curses menu for fullscreen command mode
+ *---------------------------------------------------------------------------*/
+void
+do_menu(void)
+{
+ static char *menu[WMITEMS] =
+ {
+ "1 - (D)isplay refresh",
+ "2 - (H)angup (choose a channel)",
+ "3 - (R)eread config file",
+ "4 - (Q)uit the program",
+ };
+
+ WINDOW *menu_w;
+ int c;
+ int mpos;
+ fd_set set;
+ struct timeval timeout;
+
+ /* create a new window in the lower screen area */
+
+ if((menu_w = newwin(WMENU_HGT, WMENU_LEN, WMENU_POSLN, WMENU_POSCO )) == NULL)
+ {
+ return;
+ }
+
+ /* create a border around the window */
+
+ box(menu_w, '|', '-');
+
+ /* add a title */
+
+ wstandout(menu_w);
+ mvwaddstr(menu_w, 0, (WMENU_LEN / 2) - (strlen(WMENU_TITLE) / 2), WMENU_TITLE);
+ wstandend(menu_w);
+
+ /* fill the window with the menu options */
+
+ for(mpos=0; mpos <= (WMITEMS-1); mpos++)
+ mvwaddstr(menu_w, mpos + 2, 2, menu[mpos]);
+
+ /* highlight the first menu option */
+
+ mpos = 0;
+ wstandout(menu_w);
+ mvwaddstr(menu_w, mpos + 2, 2, menu[mpos]);
+ wstandend(menu_w);
+
+ /* input loop */
+
+ for(;;)
+ {
+ wrefresh(menu_w);
+
+ FD_ZERO(&set);
+ FD_SET(STDIN_FILENO, &set);
+ timeout.tv_sec = WMTIMEOUT;
+ timeout.tv_usec = 0;
+
+ /* if no char is available within timeout, exit menu*/
+
+ if((select(STDIN_FILENO + 1, &set, NULL, NULL, &timeout)) <= 0)
+ goto mexit;
+
+ c = wgetch(menu_w);
+
+ switch(c)
+ {
+ case ' ':
+ case '\t': /* hilite next option */
+ mvwaddstr(menu_w, mpos + 2, 2, menu[mpos]);
+ mpos++;
+ if(mpos >= WMITEMS)
+ mpos = 0;
+ wstandout(menu_w);
+ mvwaddstr(menu_w, mpos + 2, 2, menu[mpos]);
+ wstandend(menu_w);
+ break;
+
+ case ('0'+WREFRESH+1): /* display refresh */
+ case 'D':
+ case 'd':
+ wrefresh(curscr);
+ goto mexit;
+
+ case ('0'+WQUIT+1): /* quit program */
+ case 'Q':
+ case 'q':
+ do_exit(0);
+ goto mexit;
+
+
+ case ('0'+WHANGUP+1): /* hangup connection */
+ case 'H':
+ case 'h':
+ display_chans();
+ goto mexit;
+
+ case ('0'+WREREAD+1): /* reread config file */
+ case 'R':
+ case 'r':
+ reread();
+ goto mexit;
+
+ case '\n':
+ case '\r': /* exec highlighted option */
+ switch(mpos)
+ {
+ case WREFRESH:
+ wrefresh(curscr);
+ break;
+
+ case WQUIT:
+ do_exit(0);
+ break;
+
+ case WHANGUP:
+ display_chans();
+ break;
+
+ case WREREAD:
+ reread();
+ break;
+ }
+ goto mexit;
+ break;
+
+ default:
+ goto mexit;
+ break;
+ }
+ }
+
+mexit:
+ /* delete the menu window */
+
+ delwin(menu_w);
+
+ /* re-display the original lower window contents */
+
+ touchwin(lower_w);
+ wrefresh(lower_w);
+}
+
+/*---------------------------------------------------------------------------*
+ * display connect information
+ *---------------------------------------------------------------------------*/
+void
+display_connect(int pos, int dir, char *name, char *remtel, char *dev)
+{
+ char buffer[256];
+
+ /* remote telephone number */
+
+ sprintf(buffer, "%s/%s", name, remtel);
+
+ buffer[H_IFN - H_TELN - 1] = '\0';
+
+ mvwprintw(upper_w, pos, H_TELN, "%s", buffer);
+
+ /* interface */
+
+ mvwprintw(upper_w, pos, H_IFN, "%s ", dev);
+
+ mvwprintw(upper_w, pos, H_IO, dir ? "out" : "in");
+
+ mvwprintw(upper_w, pos, H_OUT, "-");
+ mvwprintw(upper_w, pos, H_OUTBPS, "-");
+ mvwprintw(upper_w, pos, H_IN, "-");
+ mvwprintw(upper_w, pos, H_INBPS, "-");
+
+ if(do_bell)
+ display_bell();
+
+ wrefresh(upper_w);
+}
+
+/*---------------------------------------------------------------------------*
+ * display channel information for shutdown
+ *---------------------------------------------------------------------------*/
+static void
+display_chans(void)
+{
+ char buffer[80];
+ int i;
+ int cnt = 0;
+ WINDOW *chan_w;
+ int nlines, ncols, pos_x, pos_y;
+ fd_set set;
+ struct timeval timeout;
+
+ /* need this later to close the connection */
+ struct ctlr_chan {
+ int cntl;
+ int chn;
+ } *cc = NULL;
+
+ for(i = 0; i < nctrl; i++)
+ {
+ if(remstate[i].ch1state)
+ cnt++;
+ if(remstate[i].ch2state)
+ cnt++;
+ }
+
+ if(cnt > 0)
+ {
+ if ((cc = (struct ctlr_chan *)malloc (cnt *
+ sizeof (struct ctlr_chan))) == NULL)
+ {
+ return;
+ }
+ nlines = cnt + 4;
+ ncols = 60;
+ }
+ else
+ {
+ nlines = 5;
+ ncols = 22;
+ }
+
+ pos_y = WMENU_POSLN + 4;
+ pos_x = WMENU_POSCO + 10;
+
+ /* create a new window in the lower screen area */
+
+ if((chan_w = newwin(nlines, ncols, pos_y, pos_x )) == NULL)
+ {
+ if (cnt > 0)
+ free(cc);
+ return;
+ }
+
+ /* create a border around the window */
+
+ box(chan_w, '|', '-');
+
+ /* add a title */
+
+ wstandout(chan_w);
+ mvwaddstr(chan_w, 0, (ncols / 2) - (strlen("Channels") / 2), "Channels");
+ wstandend(chan_w);
+
+ /* no active channels */
+ if (cnt == 0)
+ {
+ mvwaddstr(chan_w, 2, 2, "No active channels");
+ wrefresh(chan_w);
+ sleep(1);
+
+ /* delete the channels window */
+
+ delwin(chan_w);
+ return;
+ }
+
+ nlines = 2;
+ ncols = 1;
+
+ for (i = 0; i < nctrl; i++)
+ {
+ if(remstate[i].ch1state)
+ {
+ sprintf(buffer, "%d - Controller %d channel %s", ncols, i, "B1");
+ mvwaddstr(chan_w, nlines, 2, buffer);
+ cc[ncols - 1].cntl = i;
+ cc[ncols - 1].chn = CHAN_B1;
+ nlines++;
+ ncols++;
+ }
+ if(remstate[i].ch2state)
+ {
+ sprintf(buffer, "%d - Controller %d channel %s", ncols, i, "B2");
+ mvwaddstr(chan_w, nlines, 2, buffer);
+ cc[ncols - 1].cntl = i;
+ cc[ncols - 1].chn = CHAN_B2;
+ nlines++;
+ ncols++;
+ }
+ }
+
+ for(;;)
+ {
+ wrefresh(chan_w);
+
+ FD_ZERO(&set);
+ FD_SET(STDIN_FILENO, &set);
+ timeout.tv_sec = WMTIMEOUT;
+ timeout.tv_usec = 0;
+
+ /* if no char is available within timeout, exit menu*/
+
+ if((select(STDIN_FILENO + 1, &set, NULL, NULL, &timeout)) <= 0)
+ break;
+
+ ncols = wgetch(chan_w);
+
+ if (!(isdigit(ncols)))
+ {
+ display_bell();
+ continue;
+ }
+
+ nlines = ncols - '0';
+
+ if ((nlines == 0) || (nlines > cnt))
+ {
+ display_bell();
+ continue;
+ }
+
+ hangup(cc[nlines-1].cntl, cc[nlines-1].chn);
+ break;
+ }
+
+ free(cc);
+
+ /* delete the channels window */
+
+ delwin(chan_w);
+}
+
+#endif /* !WIN32*/
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnmonitor/isdnmonitor.8 b/usr.sbin/i4b/isdnmonitor/isdnmonitor.8
new file mode 100644
index 0000000..9a102a7
--- /dev/null
+++ b/usr.sbin/i4b/isdnmonitor/isdnmonitor.8
@@ -0,0 +1,173 @@
+.\"
+.\" Copyright (c) 1999 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: isdnmonitor.8,v 1.8 1999/12/13 22:11:55 hm Exp $
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Mon Dec 13 23:04:25 1999]
+.\"
+.Dd September 25, 1999
+.Dt ISDNMONITOR 8
+.Os
+.Sh NAME
+.Nm isdnmonitor
+.Nd isdn4bsd / isdnd remote monitoring tool
+.Sh SYNOPSIS
+.Nm
+.Op Fl c
+.Op Fl d Ar debuglevel
+.Op Fl f Ar filename
+.Op Fl h Ar hostspec
+.Op Fl l Ar pathname
+.Op Fl p Ar portspec
+.Sh DESCRIPTION
+.Nm Isdnmonitor
+is used to remotely monitor the operation of the isdn daemon,
+.Xr isdnd 8 ,
+which manages all ISDN related connection and disconnection of ISDN
+devices supported by the isdn4bsd package.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl c
+Switch to (curses-) fullscreen mode of operation. In this mode,
+.Nm
+behaves nearly exactly as
+.Xr isdnd 8
+in fullscreen mode. In fullscreen mode, entering the control character
+.Em Control-L
+causes the display to be refreshed and entering
+.Em Carriage-Return
+or
+.Em Enter
+will pop-up a command window. Because
+.Nm
+will not listen to messages while the command window is active,
+this command window will disappear automatically after 5 seconds without
+any command key press.
+.Pp
+While the command window is active,
+.Em Tab
+or
+.Em Space
+advances to the next menu item. To execute a command, press
+.Em Return
+or
+.Em Enter
+for the highlighted menu item, or enter the number corresponding to the
+item to be executed or enter the capitalized character in the menu item
+description.
+.It Fl d
+If debugging support is compiled into
+.Nm
+this option is used to specify the debugging level.
+.\" The debugging level is the sum of the
+.\" following values:
+.\" .Pp
+.\" .Bl -tag -width Ds -compact -offset indent
+.\" .It Ar 0x001
+.\" general debugging.
+.\" .It Ar 0x002
+.\" rates calculation.
+.\" .It Ar 0x004
+.\" timing calculations.
+.\" .It Ar 0x008
+.\" state transitions.
+.\" .It Ar 0x010
+.\" retry handling.
+.\" .It Ar 0x020
+.\" dialing.
+.\" .It Ar 0x040
+.\" process handling.
+.\" .It Ar 0x080
+.\" isdn4bsd kernel i/o calls.
+.\" .It Ar 0x100
+.\" controller and channel busy/free messages.
+.\" .It Ar 0x200
+.\" isdnmonitor.rc configuration file processing.
+.\" .El
+.\" .Pp
+.\" The value can be specified in any number base supported by the
+.\" .Xr sscanf 3
+.\" library routine.
+.Pp
+In addition, this option accepts also the character 'n' as an argument to
+disable displaying debug messages on the full-screen display.
+.Pp
+.It Fl f
+Specifying this option causes
+.Nm
+to write its normal output and - if enabled - debugging output to a file
+which name is specified as the argument.
+.It Fl l
+is used to specify a Unix local domain socket name to be used for communication
+between
+.Xr isdnd 8
+and
+.Nm .
+.It Fl h
+is used to specify a hostname or a dotted-quad IP address of a machine
+where an
+.Xr isdnd 8
+is running which should be monitored.
+.It Fl p
+This option may be used to specify a remote port number in conjunction
+with the
+.Fl h
+option.
+.El
+.Sh ENVIRONMENT
+The following environment variables affect the execution of
+.Nm :
+.Bl -tag -width Ds
+.It Ev TERM
+The terminal type when running in full-screen display mode.
+See
+.Xr environ 7
+for more information.
+.El
+.Sh EXAMPLES
+For a first try, the following command should be used to start
+.Nm
+to monitor a locally running isdnd:
+.Bd -literal -offset indent
+isdnmonitor -h localhost
+.Ed
+.Sh DIAGNOSTICS
+Exit status is 0 on success, 1 on error.
+.Sh SEE ALSO
+.Xr isdnd 8
+.Sh BUGS
+Still one (or) more left.
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+utility was written by
+.An Martin Husemann
+and
+.An Hellmuth Michaelis Aq hm@kts.org .
+This manual page was written by
+.An Hellmuth Michaelis .
diff --git a/usr.sbin/i4b/isdnmonitor/main.c b/usr.sbin/i4b/isdnmonitor/main.c
new file mode 100644
index 0000000..1a0a1e89
--- /dev/null
+++ b/usr.sbin/i4b/isdnmonitor/main.c
@@ -0,0 +1,1196 @@
+/*
+ * Copyright (c) 1998,1999 Martin Husemann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ * 4. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software and/or documentation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * i4b daemon - network monitor client
+ * -----------------------------------
+ *
+ * $Id: main.c,v 1.35 2000/08/24 11:48:57 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:52:11 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <signal.h>
+#include <time.h>
+#include <errno.h>
+#ifndef WIN32
+#include <unistd.h>
+#include <netdb.h>
+#endif
+#include <sys/types.h>
+#ifndef WIN32
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#else
+#include <stdarg.h>
+#include <windows.h>
+extern char *optarg;
+int getopt(int nargc, char * const nargv[], const char *ostr);
+#define close(f) closesocket(f)
+#define sleep(s) Sleep(s*1000)
+#define vsnprintf _vsnprintf
+#define ssize_t long
+#endif
+#ifdef ERROR
+#undef ERROR
+#endif
+
+#define MAIN
+#include "monprivate.h"
+#undef MAIN
+
+#ifndef AF_LOCAL
+#define AF_LOCAL AF_UNIX
+#endif
+
+#ifdef DEBUG
+#include <ctype.h>
+#endif
+
+#include "monitor.h"
+
+/*
+ * Local function prototypes
+ */
+static int connect_local(char *sockpath);
+static int connect_remote(char *host, int portno);
+static void usage();
+static void mloop();
+static void handle_input();
+static void print_menu();
+static void print_logevent(time_t tstamp, int prio, char * what, char * msg);
+static void print_charge(time_t tstamp, int controller, int channel, int units, int estimated);
+static void print_connect(time_t tstamp, int dir, int controller, int channel, char * cfgname, char * devname, char * remphone, char * locphone);
+static void print_disconnect(time_t tstamp, int controller, int channel);
+static void print_updown(time_t tstamp, int contoller, int channel, int isup);
+static void handle_event(u_int8_t *msg, int len);
+#ifdef DEBUG
+static void dump_event(u_int8_t *msg, int len, int readflag);
+#endif
+
+static ssize_t sock_read(int fd, void *buf, size_t nbytes);
+static ssize_t sock_write(int fd, void *buf, size_t nbytes);
+
+static void mprintf(char *fmt, ...);
+
+/*
+ * Global variables
+ */
+static int debug = 0;
+#define DBG_DUMPALL 0x01
+#define DBG_PSEND 0x02
+
+static int monsock = -1;
+static int state = ST_INIT;
+static int sub_state = 0;
+static int sub_state_count = 0;
+
+static int isdn_major = 0;
+static int isdn_minor = 0;
+static u_int32_t rights = 0;
+
+static char *logfilename = NULL;
+static FILE *lfp = NULL;
+
+/*---------------------------------------------------------------------------
+ * Display usage and exit
+ *---------------------------------------------------------------------------*/
+static void
+usage()
+{
+ fprintf(stderr, "\n");
+ fprintf(stderr, "isdnmonitor - version %02d.%02d.%d, %s %s (protocol %02d.%02d)\n", VERSION, REL, STEP, __DATE__, __TIME__, MPROT_VERSION, MPROT_REL);
+#ifdef FOREIGN
+ fprintf(stderr, " usage: isdnmonitor [-c] [-d val] [-f name] [-h host] [-p port]\n");
+#else
+ fprintf(stderr, " usage: isdnmonitor [-c] [-d val] [-f name] [-h host] [-l path] [-p port]\n");
+#endif
+ fprintf(stderr, " -c switch to curses fullscreen output\n");
+ fprintf(stderr, " -d <val> debug flags (see source ...)\n");
+ fprintf(stderr, " -dn no debug output on fullscreen display\n");
+ fprintf(stderr, " -f <name> filename to log output to\n");
+ fprintf(stderr, " -h <host> hostname/address to connect to\n");
+#ifndef FOREIGN
+ fprintf(stderr, " -l <path> pathname to local domain socket to connect to\n");
+#endif
+ fprintf(stderr, " -p <port> portnumber to use to connect to remote host\n");
+ exit(1);
+}
+
+/*---------------------------------------------------------------------------
+ * Parse command line, startup monitor client
+ *---------------------------------------------------------------------------*/
+int main(int argc, char **argv)
+{
+ int i;
+
+#ifdef WIN32
+ WSADATA wsCaps;
+ WSAStartup(MAKEWORD(2, 0), &wsCaps);
+#endif
+
+ portno = DEF_MONPORT;
+ devbuf[0] = '\0';
+
+#ifndef FOREIGN
+ while((i = getopt(argc, argv, "cd:f:h:p:l:")) != -1)
+#else
+ while((i = getopt(argc, argv, "cd:f:h:p:")) != -1)
+#endif
+ {
+ switch(i)
+ {
+ case 'c':
+ fullscreen = 1;
+ break;
+ case 'd':
+ if(*optarg == 'n')
+ {
+ debug_noscreen = 1;
+ }
+ else
+ {
+ if((sscanf(optarg, "%i", &debug)) != 1)
+ usage();
+ }
+ break;
+ case 'f':
+ logfilename = optarg;
+ break;
+ case 'h':
+ hostname = optarg;
+ break;
+#ifndef FOREIGN
+ case 'l':
+ sockpath = optarg;
+ break;
+#endif
+ case 'p':
+ if((sscanf(optarg, "%i", &portno)) != 1)
+ usage();
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+
+#ifndef FOREIGN
+ if(hostname && sockpath)
+ {
+ fprintf(stderr, "Error: can not use local socket path on remote machine\n"
+ "conflicting options -h and -l!\n");
+ return 1;
+ }
+
+ if(sockpath)
+ {
+ monsock = connect_local(sockpath);
+ }
+ else if(hostname)
+#else
+ if(hostname)
+#endif
+
+ {
+ monsock = connect_remote(hostname, portno);
+ }
+ else
+ {
+ usage();
+ }
+
+ if(monsock == -1)
+ {
+ fprintf(stderr, "Could not connect to i4b isdn daemon.\n");
+ return 1;
+ }
+
+ if(logfilename != NULL)
+ {
+ if((lfp = fopen(logfilename, "w")) == NULL)
+ {
+ fprintf(stderr, "could not open logfile [%s], %s\n", logfilename, strerror(errno));
+ exit(1);
+ }
+ }
+
+#ifndef WIN32
+ signal(SIGPIPE, SIG_IGN);
+#endif
+
+ mloop();
+
+ close(monsock);
+
+ return 0;
+}
+
+/*---------------------------------------------------------------------------
+ * Connect via tcp/ip.
+ * Return socket if successfull, -1 on error.
+ ---------------------------------------------------------------------------*/
+static int
+connect_remote(char *host, int portno)
+{
+ struct sockaddr_in sa;
+ struct hostent *h;
+ int remotesockfd;
+
+ h = gethostbyname(host);
+
+ if(!h)
+ {
+ fprintf(stderr, "could not resolve hostname '%s'\n", host);
+ exit(1);
+ }
+
+ remotesockfd = socket(AF_INET, SOCK_STREAM, 0);
+
+ if(remotesockfd == -1)
+ {
+ fprintf(stderr, "could not create remote monitor socket: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ memset(&sa, 0, sizeof(sa));
+
+#ifdef BSD4_4
+ sa.sin_len = sizeof(sa);
+#endif
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons(portno);
+
+ memcpy(&sa.sin_addr.s_addr, h->h_addr_list[0], sizeof(sa.sin_addr.s_addr));
+
+ if(connect(remotesockfd, (struct sockaddr *)&sa, sizeof(sa)) == -1)
+ {
+ fprintf(stderr, "could not connect remote monitor: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ return remotesockfd;
+}
+
+#ifndef FOREIGN
+/*---------------------------------------------------------------------------
+ * Connect local.
+ * Return socket on success, -1 on failure.
+ *---------------------------------------------------------------------------*/
+static int
+connect_local(char *sockpath)
+{
+ int s;
+ struct sockaddr_un sa;
+
+ /* check path length */
+ if(strlen(sockpath) >= sizeof(sa.sun_path))
+ {
+ fprintf(stderr, "pathname to long for local socket: %s\n",
+ sockpath);
+ exit(1);
+ }
+
+ /* create and setup socket */
+ s = socket(AF_LOCAL, SOCK_STREAM, 0);
+
+ if(s == -1)
+ {
+ fprintf(stderr, "could not create local monitor socket:%s\n", strerror(errno));
+ exit(1);
+ }
+
+ memset(&sa, 0, sizeof(sa));
+
+ sa.sun_len = sizeof(sa);
+ sa.sun_family = AF_LOCAL;
+ strcpy(sa.sun_path, sockpath);
+
+ if(connect(s, (struct sockaddr *)&sa, sizeof(sa)))
+ {
+ fprintf(stderr, "could not connect local monitor socket [%s]: %s\n", sockpath, strerror(errno));
+ }
+
+ return s;
+}
+#endif
+
+/*---------------------------------------------------------------------------*
+ * data from keyboard available, read and process it
+ *---------------------------------------------------------------------------*/
+#ifndef WIN32
+static void
+kbdrdhdl(void)
+{
+ int ch = getch();
+
+ switch(ch)
+ {
+ case 0x0c: /* control L */
+ wrefresh(curscr);
+ break;
+
+ case '\n':
+ case '\r':
+ do_menu();
+ break;
+ }
+}
+#endif
+
+/*---------------------------------------------------------------------------
+ * main event loop
+ *---------------------------------------------------------------------------*/
+static void
+mloop()
+{
+ for(;;)
+ {
+ fd_set rd, wr, ex;
+
+ FD_ZERO(&rd);
+ FD_ZERO(&wr);
+ FD_ZERO(&ex);
+ FD_SET(fileno(stdin), &rd);
+ FD_SET(monsock, &rd);
+
+ select(monsock+1, &rd, &wr, &ex, NULL);
+
+ if(FD_ISSET(fileno(stdin), &rd))
+ {
+#ifndef WIN32
+ if(fullscreen && curses_ready)
+ kbdrdhdl();
+ else
+#endif
+ if(!fullscreen)
+ handle_input();
+ else
+ getchar();
+ }
+
+ if(FD_ISSET(monsock, &rd))
+ {
+ u_int8_t buf[8192];
+ int bytes, ret;
+
+ /* Network transfer may deliver two or more packets concatenated.
+ * Peek at the header and read only one event at a time... */
+
+ bytes = recv(monsock, buf, I4B_MON_EVNT_HDR, MSG_PEEK);
+
+ if(bytes == 0)
+ {
+ close(monsock);
+
+#ifndef WIN32
+ if(curses_ready)
+ {
+ endwin();
+ curses_ready = 0;
+ }
+#endif
+
+ mprintf("remote isdnd has closed our connection\n");
+ exit(0);
+ }
+ else if(bytes < 0)
+ {
+ fprintf(stderr, "recv error: %s\n", strerror(errno));
+ close(monsock);
+ exit(1);
+ }
+
+ if (bytes < I4B_MON_EVNT_HDR)
+ continue; /* errh? something must be wrong... */
+
+ bytes = I4B_GET_2B(buf, I4B_MON_EVNT_LEN);
+
+ if(bytes >= sizeof(buf))
+ {
+ fprintf(stderr, "mloop: socket recv buffer overflow %d!\n", bytes);
+ break;
+ }
+
+ /* now we know the size, it fits, so lets read it! */
+
+ ret = sock_read(monsock, buf, bytes);
+
+ if(ret == 0)
+ {
+ close(monsock);
+#ifndef WIN32
+ if(curses_ready)
+ endwin();
+#endif
+ mprintf("remote isdnd has closed our connection\n");
+ exit(0);
+ }
+ else if(ret < 0)
+ {
+ mprintf("error reading from isdnd: %s", strerror(errno));
+ break;
+ }
+#ifdef DEBUG
+ if(debug & DBG_DUMPALL)
+ dump_event(buf, ret, 1);
+#endif
+ handle_event(buf, ret);
+ }
+ }
+}
+
+#ifdef DEBUG
+/*
+ * Dump a complete event packet.
+ */
+static void dump_event(u_int8_t *msg, int len, int read)
+{
+ int i;
+
+ if(read)
+ mprintf("read from socket:");
+ else
+ mprintf("write to socket:");
+
+ for(i = 0; i < len; i++)
+ {
+ if(i % 8 == 0)
+ mprintf("\n%02d: ", i);
+ mprintf("0x%02x %c ", msg[i], isprint(msg[i]) ? msg[i] : '.');
+ }
+ mprintf("\n");
+}
+#endif
+
+static void
+print_logevent(time_t tstamp, int prio, char * what, char * msg)
+{
+ char buf[256];
+ strftime(buf, sizeof(buf), I4B_TIME_FORMAT, localtime(&tstamp));
+ mprintf("log: %s prio %d what=%s msg=%s\n", buf, prio, what, msg);
+
+#ifndef WIN32
+ if(fullscreen)
+ {
+ if((!debug_noscreen) || (debug_noscreen && (((strcmp(what, "DBG"))) != 0)))
+ {
+/*
+ * FreeBSD-current integrated ncurses. Since then it is no longer possible
+ * to write to the last column in the logfilewindow without causing an
+ * automatic newline to occur resulting in a blank line in that window.
+ */
+#ifdef __FreeBSD__
+#include <osreldate.h>
+#endif
+#if defined(__FreeBSD_version) && __FreeBSD_version >= 400009
+#warning "FreeBSD ncurses is buggy: write to last column = auto newline!"
+ wprintw(lower_w, "%s %s %-.*s\n", buf, what,
+ COLS-((strlen(buf))+(strlen(what))+3), msg);
+#else
+ wprintw(lower_w, "%s %s %-.*s\n", buf, what,
+ (int)(COLS-((strlen(buf))+(strlen(what))+2)), msg);
+#endif
+ wrefresh(lower_w);
+ }
+ }
+#endif
+}
+
+static void
+print_charge(time_t tstamp, int controller, int channel, int units, int estimated)
+{
+ char buf[256];
+ strftime(buf, sizeof(buf), I4B_TIME_FORMAT, localtime(&tstamp));
+ mprintf("%s: controller %d, channel %d, charge = %d%s\n",
+ buf, controller, channel, units, estimated ? " (estimated)" : "");
+#ifndef WIN32
+ if(fullscreen)
+ {
+ if(estimated)
+ display_ccharge(CHPOS(controller, channel), units);
+ else
+ display_charge(CHPOS(controller, channel), units);
+ }
+#endif
+}
+
+/*
+ * Print a connect event.
+ * A real monitor would allocate state info for "channel" on this
+ * event.
+ */
+static void print_connect(
+ time_t tstamp, /* server time of event */
+ int outgoing, /* 0 = incoming, 1 = outgoing */
+ int controller, /* controller number */
+ int channel, /* channel no, used to identify this connection until disconnect */
+ char * cfgname, /* name of config entry/connection */
+ char * devname, /* device used (e.g. isp0) */
+ char * remphone, /* phone no of remote side */
+ char * locphone) /* local phone no */
+{
+ char buf[256];
+
+ if(channel == 0)
+ remstate[controller].ch1state = 1;
+ else
+ remstate[controller].ch2state = 1;
+
+ strftime(buf, sizeof(buf), I4B_TIME_FORMAT, localtime(&tstamp));
+
+ if(outgoing)
+ mprintf("%s: calling out to '%s' [from msn: '%s']",
+ buf, remphone, locphone);
+ else
+ mprintf("%s: incoming call from '%s' [to msn: '%s']",
+ buf, remphone, locphone);
+ mprintf(", controller %d, channel %d, config '%s' on device '%s'\n",
+ controller, channel, cfgname, devname);
+
+#ifndef WIN32
+ if(fullscreen)
+ display_connect(CHPOS(controller, channel), outgoing, cfgname, remphone, devname);
+#endif
+}
+
+/*
+ * Print a disconnect event.
+ * A real monitor could free the "per connection" state
+ * for this channel now
+ */
+static void
+print_disconnect(time_t tstamp, int controller, int channel)
+{
+ char buf[256];
+
+ if(channel == 0)
+ remstate[controller].ch1state = 0;
+ else
+ remstate[controller].ch2state = 0;
+
+ strftime(buf, sizeof(buf), I4B_TIME_FORMAT, localtime(&tstamp));
+
+ mprintf("%s: controller %d, channel %d disconnected\n",
+ buf, controller, channel);
+
+#ifndef WIN32
+ if(fullscreen)
+ display_disconnect(CHPOS(controller, channel));
+#endif
+}
+
+/*
+ * Print an up- or down event
+ */
+static void
+print_updown(time_t tstamp, int controller, int channel, int isup)
+{
+ char buf[256];
+ strftime(buf, sizeof(buf), I4B_TIME_FORMAT, localtime(&tstamp));
+ mprintf("%s: channel %d is %s\n",
+ buf, channel, isup ? "up" : "down");
+}
+
+/*
+ * Print l1 / l2 status
+ */
+static void
+print_l12stat(time_t tstamp, int controller, int layer, int state)
+{
+ char buf[256];
+ strftime(buf, sizeof(buf), I4B_TIME_FORMAT, localtime(&tstamp));
+
+ mprintf("%s: layer %d change on controller %d: %s\n",
+ buf, layer, controller, state ? "up" : "down");
+#ifndef WIN32
+ if(fullscreen)
+ display_l12stat(controller, layer, state);
+#endif
+}
+
+/*
+ * Print TEI
+ */
+static void
+print_tei(time_t tstamp, int controller, int tei)
+{
+ char buf[256];
+ strftime(buf, sizeof(buf), I4B_TIME_FORMAT, localtime(&tstamp));
+
+ mprintf("%s: controller %d, TEI is %d\n",
+ buf, controller, tei);
+
+#ifndef WIN32
+ if(fullscreen)
+ display_tei(controller, tei);
+#endif
+}
+
+/*
+ * Print accounting information
+ */
+static void
+print_acct(time_t tstamp, int controller, int channel, int obytes, int obps,
+ int ibytes, int ibps)
+{
+ char buf[256];
+ strftime(buf, sizeof(buf), I4B_TIME_FORMAT, localtime(&tstamp));
+
+ mprintf("%s: controller %d, channel %d: %d obytes, %d obps, %d ibytes, %d ibps\n",
+ buf, controller, channel, obytes, obps, ibytes, ibps);
+#ifndef WIN32
+ if(fullscreen)
+ display_acct(CHPOS(controller, channel), obytes, obps, ibytes, ibps);
+#endif
+}
+
+static void
+print_initialization(void)
+{
+#ifndef WIN32
+ if(fullscreen)
+ {
+ if(curses_ready == 0)
+ init_screen();
+ }
+ else
+#endif
+ {
+ print_menu();
+ }
+}
+
+/*
+ * Dispatch one message received from the daemon.
+ */
+static void
+handle_event(u_int8_t *msg, int len)
+{
+ u_int8_t cmd[I4B_MON_ICLIENT_SIZE];
+ int local;
+ u_int32_t net;
+ u_int32_t mask;
+ u_int32_t who;
+ static int first = 1;
+
+ switch(state)
+ {
+ case ST_INIT: /* initial data */
+
+ isdn_major = I4B_GET_2B(msg, I4B_MON_IDATA_VERSMAJOR);
+ isdn_minor = I4B_GET_2B(msg, I4B_MON_IDATA_VERSMINOR);
+ nctrl = I4B_GET_2B(msg, I4B_MON_IDATA_NUMCTRL);
+ nentries = I4B_GET_2B(msg, I4B_MON_IDATA_NUMENTR);
+ rights = I4B_GET_4B(msg, I4B_MON_IDATA_CLACCESS);
+
+ mprintf("remote protocol version is %02d.%02d\n", isdn_major, isdn_minor);
+
+ if(isdn_major != MPROT_VERSION || isdn_minor != MPROT_REL)
+ {
+ fprintf(stderr, "ERROR, remote protocol version mismatch:\n");
+ fprintf(stderr, "\tremote major version is %02d, local major version is %02d\n", isdn_major, MPROT_VERSION);
+ fprintf(stderr, "\tremote minor version is %02d, local minor version is %02d\n", isdn_minor, MPROT_REL);
+ exit(1);
+ }
+
+ mprintf("our rights = 0x%x\n", rights);
+
+ sub_state = 0;
+ first = 1;
+
+ if(nctrl > 0)
+ {
+ state = ST_ICTRL;
+ }
+ else if(nentries > 0)
+ {
+ state = ST_IDEV;
+ }
+ else
+ {
+ state = ST_ANYEV;
+ sleep(2);
+ print_initialization();
+ }
+
+ /* set maximum event mask */
+ I4B_PREP_CMD(cmd, I4B_MON_CCMD_SETMASK);
+ I4B_PUT_2B(cmd, I4B_MON_ICLIENT_VERMAJOR, MPROT_VERSION);
+ I4B_PUT_2B(cmd, I4B_MON_ICLIENT_VERMINOR, MPROT_REL);
+ I4B_PUT_4B(cmd, I4B_MON_ICLIENT_EVENTS, ~0U);
+
+#ifdef DEBUG
+ if(debug & DBG_DUMPALL)
+ dump_event(cmd, sizeof(cmd), 0);
+#endif
+
+ if((sock_write(monsock, cmd, sizeof(cmd))) == -1)
+ {
+ fprintf(stderr, "sock_write failed: %s\n", strerror(errno));
+ exit(1);
+ }
+ break;
+
+ case ST_ICTRL: /* initial controller list */
+ if(first)
+ {
+ first = 0;
+ mprintf("%d controller(s) found:\n", nctrl);
+ }
+ mprintf("\tcontroller %d: %s\n", sub_state++, msg+I4B_MON_ICTRL_NAME);
+
+ if(sub_state >= nctrl)
+ {
+ sub_state = 0;
+ first = 1;
+ if(nentries > 0)
+ {
+ state = ST_IDEV; /* end of list reached */
+ }
+ else
+ {
+ state = ST_ANYEV;
+ sleep(2);
+ print_initialization();
+ }
+ }
+ break;
+
+ case ST_IDEV: /* initial entry devicename list */
+ if(first)
+ {
+ first = 0;
+ mprintf("%d entries found:\n", nentries);
+ }
+
+ mprintf("\tentry %d: device %s\n", sub_state++, msg+I4B_MON_IDEV_NAME);
+
+ strcat(devbuf, msg+I4B_MON_IDEV_NAME);
+ /* strcat(devbuf, " "); */
+
+ if(sub_state >= nentries)
+ {
+ first = 1;
+ state = ST_ANYEV; /* end of list reached */
+ sub_state = 0;
+ sleep(2);
+ print_initialization();
+ }
+ break;
+
+ case ST_ANYEV: /* any event */
+ switch(I4B_GET_2B(msg, I4B_MON_EVNT))
+ {
+ case I4B_MON_DRINI_CODE:
+ state = ST_RIGHT; /* list of rights entries will follow */
+ sub_state = 0;
+ sub_state_count = I4B_GET_2B(msg, I4B_MON_DRINI_COUNT);
+ mprintf("monitor rights:\n");
+ break;
+
+ case I4B_MON_DCINI_CODE:
+ state = ST_CONNS;
+ sub_state = 0;
+ sub_state_count = I4B_GET_2B(msg, I4B_MON_DCINI_COUNT);
+ mprintf("monitor connections:\n");
+ break;
+
+ case I4B_MON_LOGEVNT_CODE:
+ print_logevent(I4B_GET_4B(msg, I4B_MON_LOGEVNT_TSTAMP),
+ I4B_GET_4B(msg, I4B_MON_LOGEVNT_PRIO),
+ msg+I4B_MON_LOGEVNT_WHAT,
+ msg+I4B_MON_LOGEVNT_MSG);
+ break;
+
+ case I4B_MON_CHRG_CODE:
+ print_charge(I4B_GET_4B(msg, I4B_MON_CHRG_TSTAMP),
+ I4B_GET_4B(msg, I4B_MON_CHRG_CTRL),
+ I4B_GET_4B(msg, I4B_MON_CHRG_CHANNEL),
+ I4B_GET_4B(msg, I4B_MON_CHRG_UNITS),
+ I4B_GET_4B(msg, I4B_MON_CHRG_ESTIMATED));
+ break;
+
+ case I4B_MON_CONNECT_CODE:
+ print_connect(
+ I4B_GET_4B(msg, I4B_MON_CONNECT_TSTAMP),
+ I4B_GET_4B(msg, I4B_MON_CONNECT_DIR),
+ I4B_GET_4B(msg, I4B_MON_CONNECT_CTRL),
+ I4B_GET_4B(msg, I4B_MON_CONNECT_CHANNEL),
+ msg+I4B_MON_CONNECT_CFGNAME,
+ msg+I4B_MON_CONNECT_DEVNAME,
+ msg+I4B_MON_CONNECT_REMPHONE,
+ msg+I4B_MON_CONNECT_LOCPHONE);
+ break;
+
+ case I4B_MON_DISCONNECT_CODE:
+ print_disconnect(
+ I4B_GET_4B(msg, I4B_MON_DISCONNECT_TSTAMP),
+ I4B_GET_4B(msg, I4B_MON_DISCONNECT_CTRL),
+ I4B_GET_4B(msg, I4B_MON_DISCONNECT_CHANNEL));
+ break;
+
+ case I4B_MON_UPDOWN_CODE:
+ print_updown(
+ I4B_GET_4B(msg, I4B_MON_UPDOWN_TSTAMP),
+ I4B_GET_4B(msg, I4B_MON_UPDOWN_CTRL),
+ I4B_GET_4B(msg, I4B_MON_UPDOWN_CHANNEL),
+ I4B_GET_4B(msg, I4B_MON_UPDOWN_ISUP));
+ break;
+ case I4B_MON_L12STAT_CODE:
+ print_l12stat(
+ I4B_GET_4B(msg, I4B_MON_L12STAT_TSTAMP),
+ I4B_GET_4B(msg, I4B_MON_L12STAT_CTRL),
+ I4B_GET_4B(msg, I4B_MON_L12STAT_LAYER),
+ I4B_GET_4B(msg, I4B_MON_L12STAT_STATE));
+ break;
+ case I4B_MON_TEI_CODE:
+ print_tei(
+ I4B_GET_4B(msg, I4B_MON_TEI_TSTAMP),
+ I4B_GET_4B(msg, I4B_MON_TEI_CTRL),
+ I4B_GET_4B(msg, I4B_MON_TEI_TEI));
+ break;
+ case I4B_MON_ACCT_CODE:
+ print_acct(
+ I4B_GET_4B(msg, I4B_MON_ACCT_TSTAMP),
+ I4B_GET_4B(msg, I4B_MON_ACCT_CTRL),
+ I4B_GET_4B(msg, I4B_MON_ACCT_CHAN),
+ I4B_GET_4B(msg, I4B_MON_ACCT_OBYTES),
+ I4B_GET_4B(msg, I4B_MON_ACCT_OBPS),
+ I4B_GET_4B(msg, I4B_MON_ACCT_IBYTES),
+ I4B_GET_4B(msg, I4B_MON_ACCT_IBPS));
+ break;
+ default:
+ mprintf("unknown event code: %d\n", I4B_GET_2B(msg, I4B_MON_EVNT));
+ }
+ break;
+
+ case ST_RIGHT: /* one record in a list of monitor rights */
+ rights = I4B_GET_4B(msg, I4B_MON_DR_RIGHTS);
+ net = I4B_GET_4B(msg, I4B_MON_DR_NET);
+ mask = I4B_GET_4B(msg, I4B_MON_DR_MASK);
+ local = I4B_GET_1B(msg, I4B_MON_DR_LOCAL);
+
+ if(local)
+ {
+ mprintf("\tlocal: rights = %x\n", rights);
+ }
+ else
+ {
+ mprintf("\tfrom: %d.%d.%d.%d, mask %d.%d.%d.%d, rights = %x\n",
+ (net >> 24) & 0x00ff, (net >> 16) & 0x00ff, (net >> 8) & 0x00ff, net & 0x00ff,
+ (mask >> 24) & 0x00ff, (mask >> 16) & 0x00ff, (mask >> 8) & 0x00ff, mask & 0x00ff,
+ rights);
+ }
+
+ sub_state++;
+
+ if(sub_state >= sub_state_count)
+ {
+ state = ST_ANYEV;
+ print_initialization();
+ }
+ break;
+
+ case ST_CONNS:
+ who = I4B_GET_4B(msg, I4B_MON_DC_WHO);
+ rights = I4B_GET_4B(msg, I4B_MON_DC_RIGHTS);
+
+ mprintf("\tfrom: %d.%d.%d.%d, rights = %x\n",
+ (who >> 24) & 0x00ff, (who >> 16) & 0x00ff, (who >> 8) & 0x00ff, who & 0x00ff,
+ rights);
+
+ sub_state++;
+
+ if(sub_state >= sub_state_count)
+ {
+ state = ST_ANYEV;
+ print_initialization();
+ }
+ break;
+
+ default:
+ mprintf("unknown event from remote: local state = %d, evnt = %x, len = %d\n",
+ state, I4B_GET_2B(msg, I4B_MON_EVNT), len);
+ }
+}
+
+/*
+ * Process input from user
+ */
+static void
+handle_input()
+{
+ char buf[1024];
+ int channel, controller;
+
+ fgets(buf, sizeof(buf), stdin);
+
+ switch(atoi(buf))
+ {
+ case 1:
+ {
+ u_int8_t cmd[I4B_MON_DUMPRIGHTS_SIZE];
+ I4B_PREP_CMD(cmd, I4B_MON_DUMPRIGHTS_CODE);
+#ifdef DEBUG
+ if(debug & DBG_DUMPALL)
+ dump_event(cmd, I4B_MON_DUMPRIGHTS_SIZE, 0);
+#endif
+
+ if((sock_write(monsock, cmd, I4B_MON_DUMPRIGHTS_SIZE)) == -1)
+ {
+ fprintf(stderr, "sock_write failed: %s\n", strerror(errno));
+ exit(1);
+ }
+ }
+ break;
+
+ case 2:
+ {
+ u_int8_t cmd[I4B_MON_DUMPMCONS_SIZE];
+ I4B_PREP_CMD(cmd, I4B_MON_DUMPMCONS_CODE);
+#ifdef DEBUG
+ if(debug & DBG_DUMPALL)
+ dump_event(cmd, I4B_MON_DUMPMCONS_CODE, 0);
+#endif
+
+ if((sock_write(monsock, cmd, I4B_MON_DUMPMCONS_SIZE)) == -1)
+ {
+ fprintf(stderr, "sock_write failed: %s\n", strerror(errno));
+ exit(1);
+ }
+ }
+ break;
+
+ case 3:
+ {
+ u_int8_t cmd[I4B_MON_CFGREREAD_SIZE];
+ I4B_PREP_CMD(cmd, I4B_MON_CFGREREAD_CODE);
+#ifdef DEBUG
+ if(debug & DBG_DUMPALL)
+ dump_event(cmd, I4B_MON_CFGREREAD_CODE, 0);
+#endif
+
+ if((sock_write(monsock, cmd, I4B_MON_CFGREREAD_SIZE)) == -1)
+ {
+ fprintf(stderr, "sock_write failed: %s\n", strerror(errno));
+ exit(1);
+ }
+ }
+ break;
+
+ case 4:
+ {
+ u_int8_t cmd[I4B_MON_HANGUP_SIZE];
+ I4B_PREP_CMD(cmd, I4B_MON_HANGUP_CODE);
+
+ printf("Which controller you wish to hangup? ");
+ fgets(buf, sizeof(buf), stdin);
+ controller = atoi(buf);
+ I4B_PUT_4B(cmd, I4B_MON_HANGUP_CTRL, controller);
+
+ printf("Which channel do you wish to hangup? ");
+ fgets(buf, sizeof(buf), stdin);
+ channel = atoi(buf);
+ I4B_PUT_4B(cmd, I4B_MON_HANGUP_CHANNEL, channel);
+
+#ifdef DEBUG
+ if(debug & DBG_DUMPALL)
+ dump_event(cmd, I4B_MON_HANGUP_CHANNEL, 0);
+#endif
+
+ if((sock_write(monsock, cmd, I4B_MON_HANGUP_SIZE)) == -1)
+ {
+ fprintf(stderr, "sock_write failed: %s\n", strerror(errno));
+ exit(1);
+ }
+ }
+ break;
+
+ case 9:
+ close(monsock);
+ exit(0);
+ break;
+
+ default:
+ print_menu();
+ break;
+ }
+}
+
+void
+reread(void)
+{
+ u_int8_t cmd[I4B_MON_CFGREREAD_SIZE];
+ I4B_PREP_CMD(cmd, I4B_MON_CFGREREAD_CODE);
+#ifdef DEBUG
+ if(debug & DBG_DUMPALL)
+ dump_event(cmd, I4B_MON_CFGREREAD_CODE, 0);
+#endif
+ if((sock_write(monsock, cmd, I4B_MON_CFGREREAD_SIZE)) == -1)
+ {
+ fprintf(stderr, "sock_write failed: %s\n", strerror(errno));
+ exit(1);
+ }
+}
+
+void
+hangup(int ctrl, int chan)
+{
+ u_int8_t cmd[I4B_MON_HANGUP_SIZE];
+
+ I4B_PREP_CMD(cmd, I4B_MON_HANGUP_CODE);
+ I4B_PUT_4B(cmd, I4B_MON_HANGUP_CTRL, ctrl);
+ I4B_PUT_4B(cmd, I4B_MON_HANGUP_CHANNEL, chan);
+
+#ifdef DEBUG
+ if(debug & DBG_DUMPALL)
+ dump_event(cmd, I4B_MON_HANGUP_CHANNEL, 0);
+#endif
+
+ if((sock_write(monsock, cmd, I4B_MON_HANGUP_SIZE)) == -1)
+ {
+ fprintf(stderr, "sock_write failed: %s\n", strerror(errno));
+ exit(1);
+ }
+}
+
+/*
+ * Display menu
+ */
+static void
+print_menu()
+{
+ if(!fullscreen)
+ {
+ printf("Menu: <1> display rights, <2> display monitor connections,\n");
+ printf(" <3> reread config file, <4> hangup \n");
+ printf(" <9> quit isdnmonitor\n");
+ fflush(stdout);
+ }
+}
+
+static ssize_t
+sock_read(int fd, void *buf, size_t nbytes)
+{
+ size_t nleft;
+ ssize_t nread;
+ unsigned char *ptr;
+
+ ptr = buf;
+ nleft = nbytes;
+
+ while(nleft > 0)
+ {
+ if((nread = read(fd, ptr, nleft)) < 0)
+ {
+ if(errno == EINTR)
+ {
+ nread = 0;
+ }
+ else
+ {
+ return(-1);
+ }
+ }
+ else if(nread == 0)
+ {
+ break; /* EOF */
+ }
+
+ nleft -= nread;
+ ptr += nread;
+ }
+ return(nbytes - nleft);
+}
+
+static ssize_t
+sock_write(int fd, void *buf, size_t nbytes)
+{
+ size_t nleft;
+ ssize_t nwritten;
+ unsigned char *ptr;
+
+ ptr = buf;
+ nleft = nbytes;
+
+ while(nleft > 0)
+ {
+ if((nwritten = write(fd, ptr, nleft)) <= 0)
+ {
+ if(errno == EINTR)
+ {
+ nwritten = 0;
+ }
+ else
+ {
+ return(-1);
+ }
+ }
+
+ nleft -= nwritten;
+ ptr += nwritten;
+ }
+ return(nbytes);
+}
+
+static void
+mprintf(char *fmt, ...)
+{
+#define PRBUFLEN 1024
+ char buffer[PRBUFLEN];
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(buffer, PRBUFLEN-1, fmt, ap);
+ va_end(ap);
+
+ if(!fullscreen || (fullscreen && (!curses_ready)))
+ printf("%s", buffer);
+
+ if(logfilename != NULL)
+ {
+ fprintf(lfp, "%s", buffer);
+ fflush(lfp);
+ }
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnmonitor/monitor.h b/usr.sbin/i4b/isdnmonitor/monitor.h
new file mode 100644
index 0000000..f5d532f
--- /dev/null
+++ b/usr.sbin/i4b/isdnmonitor/monitor.h
@@ -0,0 +1,299 @@
+/*
+ * Copyright (c) 1998,1999 Martin Husemann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ * 4. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software and/or documentation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * i4b daemon - network monitor protocol definition
+ * ------------------------------------------------
+ *
+ * $Id: monitor.h,v 1.16 1999/12/13 21:25:26 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:52:18 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#ifndef _MONITOR_H_
+#define _MONITOR_H_
+
+#define DEF_MONPORT 451 /* default monitor TCP port */
+
+#ifdef __hpux
+#define u_int8_t ubit8
+#define u_int32_t ubit32
+#endif
+#ifdef WIN32
+#define u_int8_t BYTE
+#define u_int32_t DWORD
+#endif
+
+/*
+ * The monitor client connects to the isdnd daemon process via a tcp/ip
+ * connection from a remote machine or via a local (unix domain) socket.
+ * The daemon accepts multiple connections and verifies access rights.
+ * On connection establishment the daemon sends initial data telling
+ * the client the current configuration: number and type of available
+ * controllers, current connections, channel and interface states
+ * and the clients access privileges. The client sends an event mask
+ * telling the daemon which events it is interested in. If the client
+ * has appropriate rights he may send commands to the daemon.
+ *
+ * All multi-byte values are in network byte order!
+ */
+
+/* All data packets transfered are declared as arrays of u_int8_t */
+
+/* max stringlength used in this protocol */
+#define I4B_MAX_MON_STRING 256
+
+/* max command size from client to server */
+#define I4B_MAX_MON_CLIENT_CMD 16
+
+/* Version of the monitor protocol described here */
+#define MPROT_VERSION 0 /* major version no */
+#define MPROT_REL 5 /* release no */
+
+/*
+ * Client access rights
+ */
+#define I4B_CA_COMMAND_FULL 1 /* may send any command */
+#define I4B_CA_COMMAND_RESTRICTED 2 /* may send 'harmless' commands */
+#define I4B_CA_EVNT_CHANSTATE 16 /* may watch b-channel states */
+#define I4B_CA_EVNT_CALLIN 32 /* may watch incoming calls */
+#define I4B_CA_EVNT_CALLOUT 64 /* may watch outgoing calls */
+#define I4B_CA_EVNT_I4B 128 /* may watch isdnd actions */
+
+/*
+ * General layout of a command packet. All commands have this common
+ * prefix. It is prepared by the macro I4B_PREP_CMD (s.b.)
+ */
+#define I4B_MON_CMD 0 /* 2 byte: command code */
+#define I4B_MON_CMD_LEN 2 /* 2 byte: packet length */
+#define I4B_MON_CMD_HDR 4 /* size of header */
+
+/*
+ * Currently events look the same as commands. We do not make
+ * any guarantee this will remain the same, so a different set
+ * of macros is used when describing events. Events are prepared
+ * by I4B_PREP_EVNT (s.b.)
+ */
+#define I4B_MON_EVNT 0 /* 2 byte: event code */
+#define I4B_MON_EVNT_LEN 2 /* 2 byte: packet length */
+#define I4B_MON_EVNT_HDR 4 /* size of header */
+
+/* Initial data send by daemon after connection is established */
+#define I4B_MON_IDATA_SIZE I4B_MON_EVNT_HDR+12
+#define I4B_MON_IDATA_CODE 0 /* event code */
+#define I4B_MON_IDATA_VERSMAJOR I4B_MON_EVNT_HDR+0 /* 2 byte: isdnd major version */
+#define I4B_MON_IDATA_VERSMINOR I4B_MON_EVNT_HDR+2 /* 2 byte: isdnd minor version */
+#define I4B_MON_IDATA_NUMCTRL I4B_MON_EVNT_HDR+4 /* 2 byte: number of controllers */
+#define I4B_MON_IDATA_NUMENTR I4B_MON_EVNT_HDR+6 /* 2 byte: number of controllers */
+#define I4B_MON_IDATA_CLACCESS I4B_MON_EVNT_HDR+8 /* 4 byte: client rights */
+
+/* followed by this for every controller */
+#define I4B_MON_ICTRL_SIZE I4B_MON_EVNT_HDR+I4B_MAX_MON_STRING+8
+#define I4B_MON_ICTRL_CODE 1 /* event code */
+#define I4B_MON_ICTRL_NAME I4B_MON_EVNT_HDR+0 /* string: name of controller */
+#define I4B_MON_ICTRL_BUSID I4B_MON_EVNT_HDR+I4B_MAX_MON_STRING+0 /* 2 byte: isdn bus id (reservered) */
+#define I4B_MON_ICTRL_FLAGS I4B_MON_EVNT_HDR+I4B_MAX_MON_STRING+2 /* 4 byte: controller flags (not yet defined) */
+#define I4B_MON_ICTRL_NCHAN I4B_MON_EVNT_HDR+I4B_MAX_MON_STRING+6 /* 2 byte: number of b channels on this controller */
+
+/* followed by this for every entry */
+#define I4B_MON_IDEV_SIZE I4B_MON_EVNT_HDR+I4B_MAX_MON_STRING+2
+#define I4B_MON_IDEV_CODE 2 /* event code */
+#define I4B_MON_IDEV_NAME I4B_MON_EVNT_HDR+0 /* string: name of device */
+#define I4B_MON_IDEV_STATE I4B_MON_EVNT_HDR+I4B_MAX_MON_STRING+0 /* 2 byte: state of device */
+
+/*
+ * The client sets it's protocol version and event mask (usually once after
+ * connection establishement)
+ */
+#define I4B_MON_CCMD_SETMASK 0x7e /* command code */
+#define I4B_MON_ICLIENT_SIZE I4B_MON_CMD_HDR+8
+#define I4B_MON_ICLIENT_VERMAJOR I4B_MON_CMD_HDR+0 /* 2 byte: protocol major version (always 0 for now) */
+#define I4B_MON_ICLIENT_VERMINOR I4B_MON_CMD_HDR+2 /* 2 byte: protocol minor version (always 0 for now) */
+#define I4B_MON_ICLIENT_EVENTS I4B_MON_CMD_HDR+4 /* 4 byte: client event mask */
+
+/* The client requests a list of monitor rights */
+#define I4B_MON_DUMPRIGHTS_CODE 1
+#define I4B_MON_DUMPRIGHTS_SIZE I4B_MON_CMD_HDR /* no parameters */
+
+/*
+ * in response to 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+8
+#define I4B_MON_HANGUP_CTRL I4B_MON_CMD_HDR+0 /* controller */
+#define I4B_MON_HANGUP_CHANNEL I4B_MON_CMD_HDR+4 /* channel */
+
+/* The daemon sends a logfile event */
+#define I4B_MON_LOGEVNT_CODE 6
+#define I4B_MON_LOGEVNT_SIZE I4B_MON_EVNT_HDR+8+2*I4B_MAX_MON_STRING
+#define I4B_MON_LOGEVNT_TSTAMP I4B_MON_EVNT_HDR+0 /* 4 byte: timestamp */
+#define I4B_MON_LOGEVNT_PRIO I4B_MON_EVNT_HDR+4 /* 4 byte: syslog priority */
+#define I4B_MON_LOGEVNT_WHAT I4B_MON_EVNT_HDR+8 /* followed by 2 strings: 'what' and 'message' */
+#define I4B_MON_LOGEVNT_MSG I4B_MON_EVNT_HDR+8+I4B_MAX_MON_STRING
+
+/* The daemon sends a charge event */
+#define I4B_MON_CHRG_CODE 7
+#define I4B_MON_CHRG_SIZE I4B_MON_EVNT_HDR+20
+#define I4B_MON_CHRG_TSTAMP I4B_MON_EVNT_HDR+0 /* 4 byte: timestamp */
+#define I4B_MON_CHRG_CTRL I4B_MON_EVNT_HDR+4 /* 4 byte: channel charged */
+#define I4B_MON_CHRG_CHANNEL I4B_MON_EVNT_HDR+8 /* 4 byte: channel charged */
+#define I4B_MON_CHRG_UNITS I4B_MON_EVNT_HDR+12 /* 4 byte: new charge value */
+#define I4B_MON_CHRG_ESTIMATED I4B_MON_EVNT_HDR+16 /* 4 byte: 0 = charge by network, 1 = calculated estimate */
+
+/* The daemon sends a connect event */
+#define I4B_MON_CONNECT_CODE 8
+#define I4B_MON_CONNECT_SIZE I4B_MON_EVNT_HDR+16+4*I4B_MAX_MON_STRING
+#define I4B_MON_CONNECT_TSTAMP I4B_MON_EVNT_HDR+0 /* 4 byte: time stamp */
+#define I4B_MON_CONNECT_DIR I4B_MON_EVNT_HDR+4 /* 4 byte: direction (0 = incoming, 1 = outgoing) */
+#define I4B_MON_CONNECT_CTRL I4B_MON_EVNT_HDR+8 /* 4 byte: channel connected */
+#define I4B_MON_CONNECT_CHANNEL I4B_MON_EVNT_HDR+12 /* 4 byte: channel connected */
+#define I4B_MON_CONNECT_CFGNAME I4B_MON_EVNT_HDR+16 /* name of config entry */
+#define I4B_MON_CONNECT_DEVNAME I4B_MON_EVNT_HDR+16+I4B_MAX_MON_STRING /* name of device used for connection */
+#define I4B_MON_CONNECT_REMPHONE I4B_MON_EVNT_HDR+16+2*I4B_MAX_MON_STRING /* remote phone no. */
+#define I4B_MON_CONNECT_LOCPHONE I4B_MON_EVNT_HDR+16+3*I4B_MAX_MON_STRING /* local phone no. */
+
+/* The daemon sends a disconnect event */
+#define I4B_MON_DISCONNECT_CODE 9
+#define I4B_MON_DISCONNECT_SIZE I4B_MON_EVNT_HDR+12
+#define I4B_MON_DISCONNECT_TSTAMP I4B_MON_EVNT_HDR+0 /* 4 byte: time stamp */
+#define I4B_MON_DISCONNECT_CTRL I4B_MON_EVNT_HDR+4 /* 4 byte: channel disconnected */
+#define I4B_MON_DISCONNECT_CHANNEL I4B_MON_EVNT_HDR+8 /* 4 byte: channel disconnected */
+
+/* The daemon sends an up/down event */
+#define I4B_MON_UPDOWN_CODE 10
+#define I4B_MON_UPDOWN_SIZE I4B_MON_EVNT_HDR+16
+#define I4B_MON_UPDOWN_TSTAMP I4B_MON_EVNT_HDR+0 /* 4 byte: time stamp */
+#define I4B_MON_UPDOWN_CTRL I4B_MON_EVNT_HDR+4 /* 4 byte: channel disconnected */
+#define I4B_MON_UPDOWN_CHANNEL I4B_MON_EVNT_HDR+8 /* 4 byte: channel disconnected */
+#define I4B_MON_UPDOWN_ISUP I4B_MON_EVNT_HDR+12 /* 4 byte: interface is up */
+
+/* The daemon sends a L1/L2 status change event */
+#define I4B_MON_L12STAT_CODE 11
+#define I4B_MON_L12STAT_SIZE I4B_MON_EVNT_HDR+16
+#define I4B_MON_L12STAT_TSTAMP I4B_MON_EVNT_HDR+0 /* 4 byte: time stamp */
+#define I4B_MON_L12STAT_CTRL I4B_MON_EVNT_HDR+4 /* 4 byte: controller */
+#define I4B_MON_L12STAT_LAYER I4B_MON_EVNT_HDR+8 /* 4 byte: layer */
+#define I4B_MON_L12STAT_STATE I4B_MON_EVNT_HDR+12 /* 4 byte: state */
+
+/* The daemon sends a TEI change event */
+#define I4B_MON_TEI_CODE 12
+#define I4B_MON_TEI_SIZE I4B_MON_EVNT_HDR+12
+#define I4B_MON_TEI_TSTAMP I4B_MON_EVNT_HDR+0 /* 4 byte: time stamp */
+#define I4B_MON_TEI_CTRL I4B_MON_EVNT_HDR+4 /* 4 byte: controller */
+#define I4B_MON_TEI_TEI I4B_MON_EVNT_HDR+8 /* 4 byte: tei */
+
+/* The daemon sends an accounting message event */
+#define I4B_MON_ACCT_CODE 13
+#define I4B_MON_ACCT_SIZE I4B_MON_EVNT_HDR+28
+#define I4B_MON_ACCT_TSTAMP I4B_MON_EVNT_HDR+0 /* 4 byte: time stamp */
+#define I4B_MON_ACCT_CTRL I4B_MON_EVNT_HDR+4 /* 4 byte: controller */
+#define I4B_MON_ACCT_CHAN I4B_MON_EVNT_HDR+8 /* 4 byte: channel */
+#define I4B_MON_ACCT_OBYTES I4B_MON_EVNT_HDR+12 /* 4 byte: outbytes */
+#define I4B_MON_ACCT_OBPS I4B_MON_EVNT_HDR+16 /* 4 byte: outbps */
+#define I4B_MON_ACCT_IBYTES I4B_MON_EVNT_HDR+20 /* 4 byte: inbytes */
+#define I4B_MON_ACCT_IBPS I4B_MON_EVNT_HDR+24 /* 4 byte: inbps */
+
+/* macros for setup/decoding of protocol packets */
+
+/* clear a record */
+#define I4B_CLEAR(r) memset(&(r), 0, sizeof(r));
+
+/* prepare a record as event or command */
+#define I4B_PREP_EVNT(r, e) { \
+ I4B_CLEAR(r); \
+ I4B_PUT_2B(r, I4B_MON_EVNT, e); \
+ I4B_PUT_2B(r, I4B_MON_EVNT_LEN, sizeof(r)); \
+}
+#define I4B_PREP_CMD(r, c) { \
+ I4B_CLEAR(r); \
+ I4B_PUT_2B(r, I4B_MON_CMD, c); \
+ I4B_PUT_2B(r, I4B_MON_CMD_LEN, sizeof(r)); \
+}
+
+/* put 1, 2 or 4 bytes in network byte order into a record at offset off */
+#define I4B_PUT_1B(r, off, val) { ((u_int8_t*)(r))[off] = (val) & 0x00ff; }
+#define I4B_PUT_2B(r, off, val) { I4B_PUT_1B(r, off, val >> 8); I4B_PUT_1B(r, off+1, val); }
+#define I4B_PUT_4B(r, off, val) { I4B_PUT_1B(r, off, val >> 24); I4B_PUT_1B(r, off+1, val >> 16); I4B_PUT_1B(r, off+2, val >> 8); I4B_PUT_1B(r, off+3, val); }
+
+/* get 1, 2 or 4 bytes in network byte order from a record at offset off */
+#define I4B_GET_1B(r, off) (((u_int8_t*)(r))[off])
+#define I4B_GET_2B(r, off) ((((u_int8_t*)(r))[off]) << 8) | (((u_int8_t*)(r))[off+1])
+#define I4B_GET_4B(r, off) ((((u_int8_t*)(r))[off]) << 24) | ((((u_int8_t*)(r))[off+1]) << 16) | ((((u_int8_t*)(r))[off+2]) << 8) | (((u_int8_t*)(r))[off+3])
+
+/*
+ * put a string into record r at offset off, make sure it's not to long
+ * and proper terminate it
+ */
+#define I4B_PUT_STR(r, off, str) { \
+ strncpy((r)+(off), (str), I4B_MAX_MON_STRING); \
+ (r)[(off)+I4B_MAX_MON_STRING-1] = (u_int8_t)0; }
+
+#endif /* _MONITOR_H_ */
+
diff --git a/usr.sbin/i4b/isdnmonitor/monprivate.h b/usr.sbin/i4b/isdnmonitor/monprivate.h
new file mode 100644
index 0000000..ec6ec4d
--- /dev/null
+++ b/usr.sbin/i4b/isdnmonitor/monprivate.h
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * i4b remote monitor - private header
+ * -----------------------------------
+ *
+ * $Id: monprivate.h,v 1.10 1999/12/13 21:25:26 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:52:25 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <signal.h>
+
+#ifndef WIN32
+#include <unistd.h>
+#include <syslog.h>
+#include <regex.h>
+#include <curses.h>
+#include <fcntl.h>
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#endif
+
+/*---------------------------------------------------------------------------*
+ * definitions in i4b_ioctl.h, do something for other systems
+ *---------------------------------------------------------------------------*/
+#if defined (__FreeBSD__) || defined(__NetBSD__) || \
+ defined (__OpenBSD__) || defined(__bsdi__)
+
+#include <machine/i4b_ioctl.h>
+
+#else
+
+#define FOREIGN 1 /* we are running on a "foreign" OS */
+
+#define I4B_TIME_FORMAT "%d.%m.%Y %H:%M:%S"
+#define VERSION 0
+#define REL 0
+#define STEP 0
+
+#endif
+
+/*---------------------------------------------------------------------------*
+ * some general definitions
+ *---------------------------------------------------------------------------*/
+#define GOOD 0 /* general "good" or "ok" return*/
+#define ERROR (-1) /* general error return */
+#define WARNING (-2) /* warning return */
+#define INVALID (-1) /* an invalid integer */
+
+/*---------------------------------------------------------------------------*
+ * state definitions
+ *---------------------------------------------------------------------------*/
+#define ST_INIT 0 /* initial data */
+#define ST_ICTRL 1 /* initial controller list */
+#define ST_IDEV 2 /* initial entry devicename list */
+#define ST_ANYEV 3 /* any event */
+#define ST_RIGHT 4 /* one record in a list of monitor rights */
+#define ST_CONNS 5 /* monitor connections */
+
+/*---------------------------------------------------------------------------*
+ * curses fullscreen display definitions
+ *---------------------------------------------------------------------------*/
+/* window dimensions */
+#define UPPER_B 2 /* upper window start */
+
+/* horizontal positions for upper window */
+#define H_CNTL 0 /* controller */
+#define H_TEI 2 /* TEI */
+#define H_CHAN (H_TEI+4) /* channel */
+#define H_TELN (H_CHAN+2) /* telephone number */
+#define H_IFN (H_TELN+23) /* interfacename */
+#define H_IO (H_IFN+7) /* incoming or outgoing */
+#define H_OUT (H_IO+4) /* # of bytes out */
+#define H_OUTBPS (H_OUT+11) /* bytes per second out */
+#define H_IN (H_OUTBPS+5) /* # of bytes in */
+#define H_INBPS (H_IN+11) /* bytes per second in */
+#define H_UNITS (H_INBPS+6) /* # of charging units */
+
+/* fullscreen mode menu window */
+#define WMENU_LEN 35 /* width of menu window */
+#define WMENU_TITLE "Command" /* title string */
+#define WMENU_POSLN 10 /* menu position, line */
+#define WMENU_POSCO 5 /* menu position, col */
+#define WMITEMS 4 /* no of menu items */
+#define WMENU_HGT (WMITEMS + 4) /* menu window height */
+
+#define WREFRESH 0
+#define WHANGUP 1
+#define WREREAD 2
+#define WQUIT 3
+
+#define WMTIMEOUT 5 /* timeout in seconds */
+
+/*---------------------------------------------------------------------------*
+ * misc
+ *---------------------------------------------------------------------------*/
+#define CHPOS(uctlr, uchan) (((uctlr)*2) + (uchan))
+
+/*---------------------------------------------------------------------------*
+ * remote state
+ *---------------------------------------------------------------------------*/
+
+#define MAX_CTRL 4
+
+typedef struct remstate {
+ int ch1state;
+ int ch2state;
+} remstate_t;
+
+/*---------------------------------------------------------------------------*
+ * global variables
+ *---------------------------------------------------------------------------*/
+#ifdef MAIN
+
+remstate_t remstate[MAX_CTRL];
+
+int nctrl = 0; /* # of controllers available */
+int curses_ready = 0; /* curses initialized */
+int do_bell = 0;
+int nentries = 0;
+int fullscreen = 0;
+int debug_noscreen = 0;
+
+#ifndef WIN32
+WINDOW *upper_w; /* curses upper window pointer */
+WINDOW *mid_w; /* curses mid window pointer */
+WINDOW *lower_w; /* curses lower window pointer */
+#endif
+
+char devbuf[256];
+
+char *sockpath = NULL;
+char *hostname = NULL;
+int portno;
+
+#else /* !MAIN */
+
+remstate_t remstate[MAX_CTRL];
+
+int nctrl;
+int curses_ready;
+int do_bell;
+int nentries;
+int fullscreen;
+int debug_noscreen;
+
+WINDOW *upper_w;
+WINDOW *mid_w;
+WINDOW *lower_w;
+
+char devbuf[256];
+
+char *sockpath;
+char *hostname;
+int portno;
+
+#endif
+
+extern void do_exit ( int exitval );
+extern void do_menu ( void );
+extern void init_screen ( void );
+extern void display_charge ( int pos, int charge );
+extern void display_ccharge ( int pos, int units );
+extern void display_connect(int pos, int dir, char *name, char *remtel, char *dev);
+extern void display_acct ( int pos, int obyte, int obps, int ibyte, int ibps );
+extern void display_disconnect ( int pos );
+extern void display_updown ( int pos, int updown, char *device );
+extern void display_l12stat ( int controller, int layer, int state );
+extern void display_tei ( int controller, int tei );
+
+extern void reread(void);
+extern void hangup(int ctrl, int chan);
+
+
diff --git a/usr.sbin/i4b/isdnphone/Makefile b/usr.sbin/i4b/isdnphone/Makefile
new file mode 100644
index 0000000..3527e8c
--- /dev/null
+++ b/usr.sbin/i4b/isdnphone/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+PROG= isdnphone
+MAN= isdnphone.8
+SRCS= main.c display.c audio.c isdn.c
+
+DPADD= ${LIBCURSES}
+LDADD= -lcurses
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/i4b/isdnphone/audio.c b/usr.sbin/i4b/isdnphone/audio.c
new file mode 100644
index 0000000..2dc88d6
--- /dev/null
+++ b/usr.sbin/i4b/isdnphone/audio.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * isdnphone - audio operations
+ * ============================
+ *
+ * $Id: audio.c,v 1.5 1999/12/13 21:25:26 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:52:39 1999]
+ *
+ *----------------------------------------------------------------------------*/
+
+#include "defs.h"
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+int
+init_audio(char *audiodevice)
+{
+ int ret;
+ int fd;
+ u_long fmt = 0;
+
+ snd_chan_param pa;
+ struct snd_size sz;
+ snd_capabilities soundcaps;
+
+ if((fd = open(audiodevice, O_RDWR)) < 0)
+ {
+ fprintf(stderr, "unable to open %s: %s\n", audiodevice, strerror(errno));
+ return(-1);
+ }
+
+ ret = ioctl(fd, AIOGCAP, &soundcaps);
+
+ if(ret == -1)
+ {
+ fprintf(stderr, "ERROR: ioctl AIOGCAP %s: %s\n", audiodevice, strerror(errno));
+ return(-1);
+ }
+
+ fmt = soundcaps.formats;
+
+ if((fmt & AFMT_FULLDUPLEX) && (!(fmt & AFMT_WEIRD)))
+ {
+#ifdef NOTDEF
+ if(fmt & AFMT_A_LAW)
+ {
+ play_fmt = rec_fmt = AFMT_A_LAW;
+ }
+ else
+#endif
+ if(fmt & AFMT_MU_LAW)
+ {
+ play_fmt = rec_fmt = AFMT_MU_LAW;
+ }
+ else
+ {
+ printf("sorry, A-law or u-law not supported!\n");
+ close(fd);
+ return(-1);
+ }
+ }
+ else
+ {
+ printf("no full-duplex available!\n");
+ close (fd);
+ return(-1);
+ }
+
+ pa.play_format = play_fmt;
+ pa.rec_format = rec_fmt;
+ pa.play_rate = pa.rec_rate = AUDIORATE;
+
+ ret = ioctl(fd, AIOSFMT, &pa);
+
+ if(ret == -1)
+ {
+ fprintf(stderr, "ERROR: ioctl AIOSFMT %s: %s\n", audiodevice, strerror(errno));
+ return(-1);
+ }
+
+ sz.play_size = BCH_MAX_DATALEN;
+ sz.rec_size = BCH_MAX_DATALEN;
+
+ ret = ioctl(fd, AIOSSIZE, &sz);
+
+ if(ret == -1)
+ {
+ fprintf(stderr, "ERROR: ioctl AIOSSIZE %s: %s\n", audiodevice, strerror(errno));
+ return(-1);
+ }
+
+ return(fd);
+}
+
+/*---------------------------------------------------------------------------*
+ * audio device has speech data from microphone
+ *---------------------------------------------------------------------------*/
+void
+audio_hdlr(void)
+{
+ unsigned char buffer[BCH_MAX_DATALEN];
+ int ret;
+
+ ret = read(audiofd, buffer, BCH_MAX_DATALEN);
+
+ if(ret < 0)
+ {
+ fatal("read audio failed: %s", strerror(errno));
+ }
+
+ debug("audio_hdlr: read %d bytes\n", ret);
+
+ if(ret > 0)
+ {
+ telwrite(ret, buffer);
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * write audio data to loudspeaker
+ *---------------------------------------------------------------------------*/
+void
+audiowrite(int len, unsigned char *buf)
+{
+ if((write(audiofd, buf, len)) < 0)
+ {
+ fatal("write audio failed: %s", strerror(errno));
+ }
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnphone/defs.h b/usr.sbin/i4b/isdnphone/defs.h
new file mode 100644
index 0000000..18da037
--- /dev/null
+++ b/usr.sbin/i4b/isdnphone/defs.h
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * isdnphone - header file
+ * =======================
+ *
+ * $Id: defs.h,v 1.6 1999/12/13 21:25:26 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:52:46 1999]
+ *
+ *----------------------------------------------------------------------------*/
+
+#include <ncurses.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <signal.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+#include <sys/soundcard.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/param.h>
+
+#include <machine/i4b_ioctl.h>
+#include <machine/i4b_tel_ioctl.h>
+
+/* device file prefixes */
+
+#define I4BTELDEVICE "/dev/i4btel"
+#define I4BTELDDEVICE "/dev/i4bteld"
+#define AUDIODEVICE "/dev/audio"
+
+#define GOOD 0
+#define ERROR (-1)
+#define WARNING (-2)
+
+/* main window dimensions */
+
+#define MW_ROW 5
+#define MW_COL 8
+
+#define MW_WIDTH 60
+#define MW_HEIGHT 8
+
+#define DB_ROW 15
+#define DB_COL 1
+#define DB_WID 79
+#define DB_HGT 9
+
+#define MW_STATEY 2
+#define MW_STATEX 1
+#define MW_STX 10
+
+#define MW_NUMY 4
+#define MW_NUMX 1
+#define MW_NUX 10
+
+#define MW_MSGY 6
+#define MW_MSGX 1
+#define MW_MSX 10
+
+/* fullscreen mode menu window */
+
+#define WMITEMS 4 /* no of items */
+#define WMENU_LEN 18 /* window width */
+#define WMENU_HGT (WMITEMS+4) /* window height */
+#define WMENU_TITLE "Command"
+#define WMENU_POSLN 8 /* window position: lines */
+#define WMENU_POSCO 20 /* window position: columns */
+
+#define CR 0x0d
+#define LF 0x0a
+#define TAB 0x09
+#define CNTRL_D 0x04
+#define CNTRL_L 0x0c
+
+#define ST_IDLE 0
+#define ST_DIALING 1
+#define ST_ACTIVE 2
+#define ST_MAX 2
+
+#define AUDIORATE 8000
+
+#ifdef MAIN
+
+WINDOW *main_w; /* curses main window pointer */
+WINDOW *dbg_w;
+
+int curses_ready = 0; /* flag, curses display is initialized */
+int state = ST_IDLE;
+
+char *states[] = {
+ "IDLE",
+ "DIALING",
+ "ACTIVE"
+};
+
+int dialerfd = -1;
+int audiofd = -1;
+int telfd = -1;
+int curx;
+char numberbuffer[TELNO_MAX];
+
+int play_fmt = AFMT_MU_LAW;
+int rec_fmt = AFMT_MU_LAW;
+
+int opt_unit = 0;
+int opt_d = 0;
+#else
+
+extern WINDOW *main_w;
+extern WINDOW *dbg_w;
+
+extern int curses_ready;
+extern int state;
+
+extern char *states[];
+
+extern int dialerfd;
+extern int audiofd;
+extern int telfd;
+extern int curx;
+extern char numberbuffer[];
+
+extern int play_fmt;
+extern int rec_fmt;
+
+int opt_unit;
+int opt_d;
+
+#endif
+
+extern void audio_hdlr ( void );
+extern void tel_hdlr ( void );
+extern void init_mainw ( void );
+extern int init_audio ( char * );
+extern void do_menu ( void );
+extern int main ( int argc, char **argv );
+extern void do_quit ( int exitval );
+extern void fatal ( char *fmt, ... );
+extern void message ( char *fmt, ... );
+extern void do_dial ( char *number );
+extern void do_hangup ( void );
+
+extern void audiowrite ( int, unsigned char * );
+extern void telwrite ( int, unsigned char * );
+
+extern void newstate ( int newstate );
+
+int init_dial(char *device);
+void dial_hdlr(void);
+int init_tel(char *device);
+
+extern void debug ( char *fmt, ... );
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnphone/display.c b/usr.sbin/i4b/isdnphone/display.c
new file mode 100644
index 0000000..1dff9a1
--- /dev/null
+++ b/usr.sbin/i4b/isdnphone/display.c
@@ -0,0 +1,246 @@
+/*
+ * Copyright (c) 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * isdnphone - some display operations
+ * ===================================
+ *
+ * $Id: display.c,v 1.4 1999/12/13 21:25:26 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:52:55 1999]
+ *
+ *----------------------------------------------------------------------------*/
+
+#include "defs.h"
+
+/*---------------------------------------------------------------------------*
+ * init curses fullscreen display
+ *---------------------------------------------------------------------------*/
+void
+init_mainw(void)
+{
+ char buffer[512];
+
+ initscr(); /* curses init */
+
+ if((COLS < 80) || (LINES < 24))
+ fatal(0, "ERROR, minimal screensize must be 80x24, is %dx%d, terminating!", COLS, LINES);
+
+
+ if((main_w = newwin(MW_HEIGHT, MW_WIDTH, MW_ROW, MW_COL)) == NULL)
+ fatal("ERROR, curses init main window, terminating!");
+
+ if(opt_d)
+ {
+ if((dbg_w = newwin(DB_HGT, DB_WID, DB_ROW, DB_COL)) == NULL)
+ fatal("ERROR, curses init debug window, terminating!");
+ scrollok(dbg_w, TRUE);
+ }
+
+ raw(); /* raw input */
+ noecho(); /* do not echo input */
+ keypad(stdscr, TRUE); /* use special keys */
+ keypad(main_w, TRUE); /* use special keys */
+
+ box(main_w, 0, 0);
+
+ sprintf(buffer, "isdnphone %d.%d ", VERSION, REL);
+
+ wstandout(main_w);
+ mvwaddstr(main_w, 0, (MW_WIDTH / 2) - (strlen(buffer) / 2), buffer);
+ wstandend(main_w);
+
+ mvwaddstr(main_w, MW_STATEY, MW_STATEX, " state: ");
+ mvwprintw(main_w, MW_STATEY, MW_STX, "%s", states[state]);
+ wmove(main_w, MW_STATEY+1, 1);
+ whline(main_w, 0, MW_WIDTH-2);
+
+ mvwaddstr(main_w, MW_NUMY, MW_NUMX, " number: ");
+ wmove(main_w, MW_NUMY+1, 1);
+ whline(main_w, 0, MW_WIDTH-2);
+
+ mvwaddstr(main_w, MW_MSGY, MW_MSGX, "message: ");
+
+ wrefresh(main_w);
+
+ curses_ready = 1;
+}
+
+/*---------------------------------------------------------------------------*
+ * curses menu for fullscreen command mode
+ *---------------------------------------------------------------------------*/
+void
+do_menu(void)
+{
+ static char *menu[WMITEMS] =
+ {
+ "Hangup",
+#define HANGUP 0
+ "Dial",
+#define DIAL 1
+ "Refresh",
+#define REFRESH 2
+ "Exit",
+#define EXIT 3
+ };
+
+ WINDOW *menu_w;
+ int c;
+ int mpos;
+
+ /* create a new window in the lower screen area */
+
+ if((menu_w = newwin(WMENU_HGT, WMENU_LEN, WMENU_POSLN, WMENU_POSCO )) == NULL)
+ return;
+
+ keypad(menu_w, TRUE); /* use special keys */
+
+ /* draw border around the window */
+
+ box(menu_w, 0, 0);
+
+ /* add a title */
+
+ wstandout(menu_w);
+ mvwaddstr(menu_w, 0, (WMENU_LEN / 2) - (strlen(WMENU_TITLE) / 2), WMENU_TITLE);
+ wstandend(menu_w);
+
+ /* fill the window with the menu options */
+
+ for(mpos=0; mpos <= (WMITEMS-1); mpos++)
+ mvwaddstr(menu_w, mpos + 2, 2, menu[mpos]);
+
+ /* highlight the first menu option */
+
+ mpos = 0;
+ wstandout(menu_w);
+ mvwaddstr(menu_w, mpos + 2, 2, menu[mpos]);
+ wstandend(menu_w);
+
+ /* input loop */
+
+ for(;;)
+ {
+ wrefresh(menu_w);
+
+ c = wgetch(menu_w);
+
+ switch(c)
+ {
+ case TAB:
+ case KEY_DOWN: /* down-move cursor */
+ case ' ':
+ mvwaddstr(menu_w, mpos + 2, 2, menu[mpos]);
+ mpos++;
+ if(mpos >= WMITEMS)
+ mpos = 0;
+ wstandout(menu_w);
+ mvwaddstr(menu_w, mpos + 2, 2, menu[mpos]);
+ wstandend(menu_w);
+ break;
+
+ case KEY_UP: /* up-move cursor */
+ mvwaddstr(menu_w, mpos + 2, 2, menu[mpos]);
+ if(mpos)
+ mpos--;
+ else
+ mpos = WMITEMS-1;
+ wstandout(menu_w);
+ mvwaddstr(menu_w, mpos + 2, 2, menu[mpos]);
+ wstandend(menu_w);
+ break;
+
+ case 'R':
+ case 'r':
+ wrefresh(curscr);
+ goto mexit;
+
+ case 'E':
+ case 'e':
+ case 'Q':
+ case 'q':
+ case 'X':
+ case 'x':
+ do_quit(0);
+ goto mexit;
+ break;
+
+ case 'H':
+ case 'h':
+ do_hangup();
+ goto mexit;
+ break;
+
+ case 'D':
+ case 'd':
+ goto mexit;
+ break;
+
+ case CR:
+ case LF: /* exec highlighted option */
+#ifdef KEY_ENTER
+ case KEY_ENTER:
+#endif
+ switch(mpos)
+ {
+ case DIAL:
+ goto mexit;
+ break;
+ case HANGUP:
+ do_hangup();
+ goto mexit;
+ break;
+ case REFRESH:
+ wrefresh(curscr);
+ break;
+ case EXIT:
+ do_quit(0);
+ break;
+ }
+ goto mexit;
+ break;
+
+ default:
+ goto mexit;
+ break;
+ }
+ }
+
+mexit:
+ /* delete the menu window */
+
+ wclear(menu_w);
+ wrefresh(menu_w);
+ delwin(menu_w);
+
+ /* re-display the original lower window contents */
+
+ touchwin(main_w);
+ wrefresh(main_w);
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnphone/isdn.c b/usr.sbin/i4b/isdnphone/isdn.c
new file mode 100644
index 0000000..34719c7
--- /dev/null
+++ b/usr.sbin/i4b/isdnphone/isdn.c
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * isdnphone - isdn (i4b) handling
+ * ===============================
+ *
+ * $Id: isdn.c,v 1.4 1999/12/13 21:25:26 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:53:05 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "defs.h"
+
+/*---------------------------------------------------------------------------*
+ * dialer init
+ *---------------------------------------------------------------------------*/
+int
+init_dial(char *device)
+{
+ int ret;
+
+ if((ret = open(device, O_RDWR)) < 0)
+ {
+ fprintf(stderr, "unable to open %s: %s\n", device, strerror(errno));
+ return(-1);
+ }
+ return(ret);
+}
+
+/*---------------------------------------------------------------------------*
+ * i4bteld data available handler
+ *---------------------------------------------------------------------------*/
+void
+dial_hdlr(void)
+{
+ char result;
+
+ if((read (dialerfd, &result, 1) < 0))
+ {
+ fatal("read failed: %s", strerror(errno));
+ }
+
+ switch(result)
+ {
+ case RSP_CONN:
+ newstate(ST_ACTIVE);
+ message("connected to remote!");
+ break;
+
+ case RSP_BUSY:
+ message("remote is busy!");
+ break;
+
+ case RSP_HUP:
+ newstate(ST_IDLE);
+ message("disconnected from remote!");
+ break;
+
+ case RSP_NOA:
+ message("no answer from remote!");
+ break;
+
+ default:
+ message("unknown response = 0x%2x!", result);
+ break;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * telephone init
+ *---------------------------------------------------------------------------*/
+int
+init_tel(char *device)
+{
+ int ret;
+ int format;
+
+ if(play_fmt == AFMT_MU_LAW)
+ format = CVT_ALAW2ULAW;
+ else
+ format = CVT_NONE;
+
+ if((ret = open(device, O_RDWR)) < 0)
+ fatal("unable to open %s: %s\n", device, strerror(errno));
+
+ if((ioctl(ret, I4B_TEL_SETAUDIOFMT, &format)) < 0)
+ fatal("ioctl I4B_TEL_SETAUDIOFMT failed: %s", strerror(errno));
+
+ return(ret);
+}
+
+/*---------------------------------------------------------------------------*
+ * dial number
+ *---------------------------------------------------------------------------*/
+void
+do_dial(char *number)
+{
+ char commandbuffer[80];
+ sprintf(commandbuffer, "D%s", number);
+
+ if((write(dialerfd, commandbuffer, strlen(commandbuffer))) < 0)
+ {
+ fatal("write commandbuffer failed: %s", strerror(errno));
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * hangup
+ *---------------------------------------------------------------------------*/
+void
+do_hangup(void)
+{
+ char commandbuffer[80];
+
+ if(state == ST_IDLE)
+ {
+ message("tried hangup while ST_IDLE");
+ return;
+ }
+
+ sprintf(commandbuffer, "H");
+
+ if((write(dialerfd, commandbuffer, strlen(commandbuffer))) < 0)
+ {
+ fatal("write commandbuffer failed: %s", strerror(errno));
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * i4btel speech data available handler
+ *---------------------------------------------------------------------------*/
+void
+tel_hdlr(void)
+{
+ unsigned char buffer[BCH_MAX_DATALEN];
+ int ret;
+
+ ret = read(telfd, buffer, BCH_MAX_DATALEN);
+
+ if(ret < 0)
+ {
+ fatal("read telfd failed: %s", strerror(errno));
+ }
+
+ debug("tel_hdlr: read %d bytes\n", ret);
+
+ if(ret > 0)
+ {
+ audiowrite(ret, buffer);
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * write audio data to ISDN
+ *---------------------------------------------------------------------------*/
+void
+telwrite(int len, unsigned char *buf)
+{
+ if((write(telfd, buf, len)) < 0)
+ {
+ fatal("write tel failed: %s", strerror(errno));
+ }
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnphone/isdnphone.8 b/usr.sbin/i4b/isdnphone/isdnphone.8
new file mode 100644
index 0000000..7eb5ee6
--- /dev/null
+++ b/usr.sbin/i4b/isdnphone/isdnphone.8
@@ -0,0 +1,80 @@
+.\"
+.\" Copyright (c) 1999, 2001 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Thu Oct 18 13:36:52 2001]
+.\"
+.\"
+.Dd October 18, 2001
+.Dt ISDNPHONE 8
+.Os
+.Sh NAME
+.Nm isdnphone
+.Nd telephone dialing and more for isdn4bsd
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Op Fl h
+.Op Fl k Ar string
+.Op Fl n Ar number
+.Op Fl u Ar unit
+.Sh DESCRIPTION
+.Nm
+is part of the isdn4bsd package and is used to handle dialing and hangup
+for the telephone control interfaces
+.Pa /dev/i4bteld Ns Aq Ar n .
+.Pp
+Options are provided to dial out or hang up using command line parameters
+(for use in scripts) or, if none of those options are specified, bring up
+a curses-based full-screen interface.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl d
+Enable debugging message display.
+.It Fl h
+Hang up a possibly open telephone connection on the selected interface.
+.It Fl k
+Send the specified string using the keypad facility information element.
+.It Fl n
+Dial the specified number on the selected interface.
+.It Fl u
+Set the unit number to specify the interface used.
+.El
+.Sh FILES
+.Bl -tag -width indent -compact
+.It Pa /dev/i4bteld Ns Aq Ar n
+.El
+.Sh EXAMPLES
+The command:
+.Dl "isdnphone -n 1234"
+.Pp
+dials calls the number 1234 to establish a call on
+.Pa /dev/i4btel0
+.Sh AUTHORS
+The
+.Nm
+utility and this manpage were written by
+.An Hellmuth Michaelis Aq hm@FreeBSD.org .
diff --git a/usr.sbin/i4b/isdnphone/main.c b/usr.sbin/i4b/isdnphone/main.c
new file mode 100644
index 0000000..7be5118
--- /dev/null
+++ b/usr.sbin/i4b/isdnphone/main.c
@@ -0,0 +1,471 @@
+/*
+ * Copyright (c) 1999, 2002 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * isdnphone - main module
+ * =======================
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Tue Mar 26 14:39:27 2002]
+ *
+ *---------------------------------------------------------------------------*/
+
+#define MAIN
+#include "defs.h"
+
+static void kbd_hdlr(void);
+
+/*---------------------------------------------------------------------------*
+ * usage display and exit
+ *---------------------------------------------------------------------------*/
+static void
+usage(void)
+{
+ fprintf(stderr, "\n");
+ fprintf(stderr, "isdnphone - i4b phone program, version %d.%d.%d, compiled %s %s\n",VERSION, REL, STEP, __DATE__, __TIME__);
+ fprintf(stderr, "usage: isdnphone -d -h -k <string> -n <number> -u <unit>\n");
+ fprintf(stderr, " -d debug\n");
+ fprintf(stderr, " -h hangup\n");
+ fprintf(stderr, " -k string keypad string\n");
+ fprintf(stderr, " -n number dial number\n");
+ fprintf(stderr, " -u unit set unit number\n");
+ fprintf(stderr, "\n");
+ exit(1);
+}
+
+/*---------------------------------------------------------------------------*
+ * program entry
+ *---------------------------------------------------------------------------*/
+int
+main(int argc, char **argv)
+{
+ int c;
+ char namebuffer[128];
+ int bschar;
+ int ret;
+ int opt_n = 0;
+ int opt_s = 0;
+ int opt_h = 0;
+ int opt_k = 0;
+ char *number = "";
+ char *subaddr = "";
+
+ numberbuffer[0] = '\0';
+
+ while ((c = getopt(argc, argv, "dhk:n:s:u:")) != -1)
+ {
+ switch(c)
+ {
+ case 'd':
+ opt_d = 1;
+ break;
+
+ case 'h':
+ opt_h = 1;
+ break;
+
+ case 'k':
+ number = optarg;
+ opt_k = 1;
+ break;
+
+ case 'n':
+ number = optarg;
+ opt_n = 1;
+ break;
+
+ case 's':
+ subaddr = optarg;
+ opt_s = 1;
+ break;
+
+ case 'u':
+ opt_unit = atoi(optarg);
+ if(opt_unit < 0 || opt_unit > 9)
+ usage();
+ break;
+
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ sprintf(namebuffer,"%s%d", I4BTELDDEVICE, opt_unit);
+
+ if((dialerfd = init_dial(namebuffer)) == -1)
+ exit(1);
+
+ if(opt_n || opt_h || opt_k)
+ {
+ char commandbuffer[80];
+
+ /* commandline operation goes here */
+
+ if(opt_n)
+ {
+ if (opt_s)
+ sprintf(commandbuffer, "D%s*%s", number, subaddr);
+ else
+ sprintf(commandbuffer, "D%s", number);
+
+ }
+ else if(opt_k)
+ {
+ sprintf(commandbuffer, "K%s", number);
+
+ }
+ else if(opt_h)
+ {
+ sprintf(commandbuffer, "H");
+ }
+
+ if((ret = write(dialerfd, commandbuffer, strlen(commandbuffer))) < 0)
+ {
+ fprintf(stderr, "write commandbuffer failed: %s", strerror(errno));
+ exit(1);
+ }
+
+ close(dialerfd);
+
+ exit(0);
+ }
+
+ if((audiofd = init_audio(AUDIODEVICE)) == -1)
+ exit(1);
+
+ /* fullscreen operation here */
+
+ init_mainw();
+
+ bschar = erasechar();
+ curx = 0;
+
+ wmove(main_w, MW_NUMY, MW_NUX + curx);
+
+ /* go into loop */
+
+ for (;;)
+ {
+ int maxfd = 0;
+ fd_set set;
+ struct timeval timeout;
+
+ FD_ZERO(&set);
+
+ FD_SET(STDIN_FILENO, &set);
+ if(STDIN_FILENO > maxfd)
+ maxfd = STDIN_FILENO;
+
+ FD_SET(dialerfd, &set);
+ if(dialerfd > maxfd)
+ maxfd = dialerfd;
+
+ if(state == ST_ACTIVE)
+ {
+ if(audiofd != -1)
+ {
+ FD_SET(audiofd, &set);
+ if(audiofd > maxfd)
+ maxfd = audiofd;
+ }
+
+ if(telfd != -1)
+ {
+ FD_SET(telfd, &set);
+ if(telfd > maxfd)
+ maxfd = telfd;
+ }
+ }
+
+ timeout.tv_sec = 2;
+ timeout.tv_usec = 0;
+
+ wrefresh(main_w);
+
+ /* if no char is available within timeout, do something */
+
+#ifdef NOTDEF
+ ret = select(maxfd+1, &set, NULL, NULL, &timeout);
+#else
+ ret = select(maxfd+1, &set, NULL, NULL, NULL);
+#endif
+
+ if(ret > 0)
+ {
+ if((telfd != -1) && (FD_ISSET(telfd, &set)))
+ {
+ message("select from ISDN");
+ tel_hdlr();
+ }
+ if((audiofd != -1) && (FD_ISSET(audiofd, &set)))
+ {
+ message("select from audio");
+ audio_hdlr();
+ }
+ if(FD_ISSET(dialerfd, &set))
+ {
+ message("select from tel");
+ dial_hdlr();
+ }
+ if(FD_ISSET(STDIN_FILENO, &set))
+ {
+ message("select from kbd");
+ kbd_hdlr();
+ }
+ }
+ }
+ do_quit(0);
+
+ return(0);
+}
+
+/*---------------------------------------------------------------------------*
+ * keyboard character available handler
+ *---------------------------------------------------------------------------*/
+static void
+kbd_hdlr(void)
+{
+ int kchar;
+
+ kchar = wgetch(main_w); /* get char */
+
+ switch (kchar)
+ {
+ case CR:
+ case LF:
+#ifdef KEY_ENTER
+ case KEY_ENTER:
+#endif
+ if((state == ST_IDLE) &&
+ (numberbuffer[0] != '\0'))
+ {
+ message("dialing .....");
+ do_dial(&numberbuffer[0]);
+ }
+ else
+ {
+ do_menu();
+ }
+ break;
+
+ case CNTRL_D:
+ if(state == ST_IDLE)
+ {
+ do_quit(0);
+ }
+ else
+ {
+ message("cannot exit while not idle!");
+ beep();
+ }
+
+ break;
+
+ case CNTRL_L: /* refresh */
+ touchwin(curscr);
+ wrefresh(curscr);
+ break;
+
+ case KEY_BACKSPACE:
+ case KEY_DC:
+ if (curx == 0)
+ break;
+
+ curx--;
+ mvwaddch(main_w, MW_NUMY, MW_NUX + curx, ' ');
+ numberbuffer[curx] = '\0';
+ wmove(main_w, MW_NUMY, MW_NUX + curx);
+
+ if(curx == 0)
+ message(" ");
+
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if(curx > (TELNO_MAX-1))
+ break;
+
+ mvwaddch(main_w, MW_NUMY, MW_NUX + curx, kchar);
+
+ numberbuffer[curx] = kchar;
+
+ curx++;
+
+ numberbuffer[curx] = '\0';
+
+ message("press ENTER to dial number .....");
+ break;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * exit program
+ *---------------------------------------------------------------------------*/
+void
+do_quit(int exitval)
+{
+ close(dialerfd);
+ move(LINES-1, 0);
+ clrtoeol();
+ refresh();
+ endwin();
+ exit(exitval);
+}
+
+/*---------------------------------------------------------------------------*
+ * fatal error exit
+ *---------------------------------------------------------------------------*/
+void
+fatal(char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ do_hangup(); /* failsafe */
+
+ if(curses_ready)
+ {
+ close(dialerfd);
+ move(LINES-1, 0);
+ clrtoeol();
+ refresh();
+ endwin();
+ }
+
+ fprintf(stderr, "\nFatal error: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n\n");
+
+ va_end(ap);
+
+ exit(1);
+}
+
+/*---------------------------------------------------------------------------*
+ * message printing
+ *---------------------------------------------------------------------------*/
+void
+message(char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ if(curses_ready)
+ {
+ int i;
+ char sbuf[MW_WIDTH];
+
+ wmove(main_w, MW_MSGY, MW_MSX);
+ vsnprintf(sbuf, MW_WIDTH-MW_MSX-1, fmt, ap);
+ waddstr(main_w, sbuf);
+ for(i=strlen(sbuf);i < MW_WIDTH-MW_MSX-2; i++)
+ waddch(main_w, ' ');
+ wmove(main_w, MW_NUMY, MW_NUX + curx);
+ wrefresh(main_w);
+ }
+ else
+ {
+ fprintf(stderr, "ERROR: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ }
+
+ va_end(ap);
+}
+
+/*---------------------------------------------------------------------------*
+ * message printing
+ *---------------------------------------------------------------------------*/
+void
+debug(char *fmt, ...)
+{
+ va_list ap;
+
+ if(opt_d == 0)
+ return;
+
+ va_start(ap, fmt);
+
+ vwprintw(dbg_w, fmt, ap);
+ wrefresh(dbg_w);
+
+ va_end(ap);
+}
+
+/*---------------------------------------------------------------------------*
+ * go to new state
+ *---------------------------------------------------------------------------*/
+void
+newstate(int newstate)
+{
+ int i;
+
+ if(newstate < 0 || newstate > ST_MAX)
+ {
+ message("newstate %d undefined!", newstate);
+ return;
+ }
+
+ state = newstate;
+
+ if(newstate == ST_ACTIVE)
+ {
+ char namebuffer[128];
+
+ sprintf(namebuffer,"%s%d", I4BTELDEVICE, opt_unit);
+ telfd = init_tel(namebuffer);
+ }
+
+ if(newstate == ST_IDLE)
+ {
+ close(telfd);
+ telfd = -1;
+ }
+
+ wmove(main_w, MW_STATEY, MW_STX);
+ waddstr(main_w, states[newstate]);
+
+ for(i=strlen(states[newstate]);i < MW_WIDTH-MW_STX-2; i++)
+ waddch(main_w, ' ');
+
+ wmove(main_w, MW_NUMY, MW_NUX + curx);
+ wrefresh(main_w);
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdntel/Makefile b/usr.sbin/i4b/isdntel/Makefile
new file mode 100644
index 0000000..e3aff09
--- /dev/null
+++ b/usr.sbin/i4b/isdntel/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+PROG= isdntel
+MAN= isdntel.8
+SRCS= main.c display.c files.c alias.c
+
+DPADD= ${LIBNCURSES}
+LDADD= -lncurses
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/i4b/isdntel/alias.c b/usr.sbin/i4b/isdntel/alias.c
new file mode 100644
index 0000000..0f4dae7
--- /dev/null
+++ b/usr.sbin/i4b/isdntel/alias.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * isdntel - isdn4bsd telephone answering machine support
+ * ======================================================
+ *
+ * $Id: alias.c,v 1.9 1999/12/13 21:25:26 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:53:37 1999]
+ *
+ *----------------------------------------------------------------------------*/
+
+#include "defs.h"
+#include "alias.h"
+
+static struct alias *firsta = NULL;
+
+#define MAXBUFSZ 256
+
+/*---------------------------------------------------------------------------*
+ * read in and init aliases
+ *---------------------------------------------------------------------------*/
+void
+init_alias(char *filename)
+{
+ FILE *fp;
+ unsigned char buffer[MAXBUFSZ + 1];
+ unsigned char number[MAXBUFSZ + 1];
+ unsigned char name[MAXBUFSZ + 1];
+ unsigned char *s, *d;
+ struct alias *newa = NULL;
+ struct alias *lasta = NULL;
+
+ if((fp = fopen(filename, "r")) == NULL)
+ return;
+
+ while((fgets(buffer, MAXBUFSZ, fp)) != NULL)
+ {
+ if(buffer[0] == '#' || buffer[0] == ' ' ||
+ buffer[0] == '\t' || buffer[0] == '\n')
+ {
+ continue;
+ }
+
+ s = buffer;
+ d = number;
+
+ while(*s && (isdigit(*s)))
+ *d++ = *s++;
+
+ *d = '\0';
+
+ while(*s && (isspace(*s)))
+ s++;
+
+ d = name;
+
+ while(*s && (isprint(*s)))
+ *d++ = *s++;
+
+ *d = '\0';
+
+ if((strlen(number) > 1) && (strlen(name) > 1))
+ {
+ if((newa = (struct alias *) malloc(sizeof(struct alias))) == NULL)
+ fatal("malloc failed for struct alias");
+ if((newa->number = (char *) malloc(strlen(number)+1)) == NULL)
+ fatal("malloc failed for number alias");
+ if((newa->name = (char *) malloc(strlen(name)+1)) == NULL)
+ fatal("malloc failed for name alias");
+ strcpy(newa->name, name);
+ strcpy(newa->number, number);
+ newa->next = NULL;
+
+ if(firsta == NULL)
+ {
+ firsta = newa;
+ }
+ else
+ {
+ lasta->next = newa;
+ }
+ lasta = newa;
+ }
+ }
+ fclose(fp);
+}
+
+/*---------------------------------------------------------------------------*
+ * read in and init aliases
+ *---------------------------------------------------------------------------*/
+char *
+get_alias(char *number)
+{
+ struct alias *ca = NULL;
+
+ if(firsta == NULL)
+ return(NULL);
+
+ ca = firsta;
+
+ for(;;)
+ {
+ if(strlen(number) == strlen(ca->number))
+ {
+ if(!(strcmp(number, ca->number)))
+ return(ca->name);
+ }
+ if(ca->next == NULL)
+ break;
+ ca = ca->next;
+ }
+ return(NULL);
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdntel/alias.h b/usr.sbin/i4b/isdntel/alias.h
new file mode 100644
index 0000000..813d5bd
--- /dev/null
+++ b/usr.sbin/i4b/isdntel/alias.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * isdn4bsd common alias file handling header
+ * ==========================================
+ *
+ * $Id: alias.h,v 1.5 1999/12/13 21:25:26 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:53:44 1999]
+ *
+ *----------------------------------------------------------------------------*/
+
+#ifndef _ALIAS_H_
+#define _ALIAS_H_
+
+#define ALIASFILE "/etc/isdn/isdntel.alias"
+
+struct alias {
+ char *number; /* telephone number string */
+ char *name; /* name string */
+ struct alias *next; /* ptr to next alias */
+};
+
+#endif /* _ALIAS_H_ */
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdntel/defs.h b/usr.sbin/i4b/isdntel/defs.h
new file mode 100644
index 0000000..9b5fd69
--- /dev/null
+++ b/usr.sbin/i4b/isdntel/defs.h
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * isdntel - isdn4bsd telephone answering support
+ * ==============================================
+ *
+ * $Id: defs.h,v 1.10 1999/12/13 21:25:26 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:53:50 1999]
+ *
+ *----------------------------------------------------------------------------*/
+
+#include <ncurses.h>
+#include <stdio.h>
+#include <signal.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/time.h>
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+#include <dirent.h>
+#else
+#include <sys/dir.h>
+#endif
+#include <sys/param.h>
+
+#include <machine/i4b_ioctl.h>
+
+#define GOOD 0
+#define ERROR (-1)
+#define WARNING (-2)
+
+#define SPOOLDIR "/var/isdn"
+#define PLAYCMD "cat %s | g711conv -a >/dev/audio"
+
+/* reread timeout in seconds */
+
+#define REREADTIMEOUT 60
+
+/* window dimensions */
+
+#define START_O 3 /* main window start */
+
+#define DAT_POS 0
+#define TIM_POS (DAT_POS+10)
+#define DST_POS (TIM_POS+8)
+#define SRC_POS (DST_POS+17)
+#define ALI_POS (SRC_POS+17)
+#define SEC_POS (ALI_POS+21)
+#define LAST_POS (SEC_POS+5)
+
+/* fullscreen mode menu window */
+
+#define WMITEMS 5 /* no of items */
+#define WMENU_LEN 18 /* window width */
+#define WMENU_HGT (WMITEMS+4) /* window height */
+#define WMENU_TITLE "Command"
+#define WMENU_POSLN 8 /* window position: lines */
+#define WMENU_POSCO 20 /* window position: columns */
+
+#define CR 0x0d
+#define LF 0x0a
+#define TAB 0x09
+#define CNTRL_D 0x04
+#define CNTRL_L 0x0c
+
+struct onefile {
+ char *fname; /* filename */
+ char *date;
+ char *time;
+ char *srcnumber;
+ char *dstnumber;
+ char *seconds;
+ char *alias;
+ int len;
+ struct onefile *next; /* ptr to next entry */
+ struct onefile *prev; /* prt to previous entry */
+};
+
+#ifdef MAIN
+
+int curses_ready = 0; /* flag, curses display is initialized */
+
+struct onefile *cur_file = NULL;/* the CURRENT filename */
+struct onefile *first = NULL; /* init dir-list head-ptr */
+struct onefile *last = NULL; /* init dir-list tail-ptr */
+
+WINDOW *main_w; /* curses main window pointer */
+
+int nofiles = 0;
+int cur_pos = 0;
+
+char *spooldir = SPOOLDIR;
+char *playstring = PLAYCMD;
+
+#else
+
+extern int curses_ready;
+
+extern struct onefile *cur_file;
+extern struct onefile *first;
+extern struct onefile *last;
+
+extern WINDOW *main_w;
+
+extern int nofiles;
+extern int cur_pos;
+
+extern char *spooldir;
+extern char *playstring;
+
+#endif
+
+extern void init_alias( char *filename );
+extern void init_files( int inipos );
+extern void init_screen ( void );
+extern void do_menu ( void );
+extern int fill_list( void );
+extern char *get_alias( char *number );
+extern int main ( int argc, char **argv );
+extern void do_quit ( int exitval );
+extern void fatal ( char *fmt, ... );
+extern void error ( char *fmt, ... );
+extern void play ( struct onefile * );
+extern void delete ( struct onefile * );
+extern void reread( void );
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdntel/display.c b/usr.sbin/i4b/isdntel/display.c
new file mode 100644
index 0000000..53e6637
--- /dev/null
+++ b/usr.sbin/i4b/isdntel/display.c
@@ -0,0 +1,254 @@
+/*
+ * Copyright (c) 1997, 2000 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * isdntel - isdn4bsd telephone answering machine support
+ * ======================================================
+ *
+ * $Id: display.c,v 1.9 2000/07/19 08:56:24 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Wed Jul 19 10:08:06 2000]
+ *
+ *----------------------------------------------------------------------------*/
+
+#include "defs.h"
+
+static char *helpstr = "Enter Control-D to exit program or RETURN for command window";
+
+/*---------------------------------------------------------------------------*
+ * init curses fullscreen display
+ *---------------------------------------------------------------------------*/
+void
+init_screen(void)
+{
+ char buffer[512];
+
+ initscr(); /* curses init */
+
+ curses_ready = 1;
+
+ if((COLS < 80) || (LINES < 24))
+ fatal("ERROR, minimal screensize must be 80x24, is %dx%d, terminating!", COLS, LINES);
+
+
+ if((main_w = newwin(LINES-START_O-2, COLS, START_O, 0)) == NULL)
+ fatal("ERROR, curses init main window, terminating!");
+
+ raw(); /* raw input */
+ noecho(); /* do not echo input */
+ keypad(stdscr, TRUE); /* use special keys */
+ keypad(main_w, TRUE); /* use special keys */
+ scrollok(main_w, TRUE);
+
+ sprintf(buffer, " isdntel %d.%d.%d ", VERSION, REL, STEP);
+
+ move(0, 0);
+ standout();
+ hline(ACS_HLINE, 5);
+ move(0, 5);
+ addstr(buffer);
+ move(0, 5 + strlen(buffer));
+ hline(ACS_HLINE, 256);
+ standend();
+
+ move(1, 0);
+ addstr("Date Time Called Party Calling Party Alias Length");
+ /* 31.12.96 16:45:12 1234567890123456 1234567890123456 12345678901234567890 123456 */
+
+ move(2, 0);
+ hline(ACS_HLINE, 256);
+
+ move(LINES-2, 0);
+ hline(ACS_HLINE, 256);
+
+ mvaddstr(LINES-1, (COLS / 2) - (strlen(helpstr) / 2), helpstr);
+
+ refresh();
+
+ wrefresh(main_w);
+}
+
+/*---------------------------------------------------------------------------*
+ * curses menu for fullscreen command mode
+ *---------------------------------------------------------------------------*/
+void
+do_menu(void)
+{
+ static char *menu[WMITEMS] =
+ {
+ "Play File",
+#define PLAY 0
+ "Delete File",
+#define DELETE 1
+ "Re-Read Spool",
+#define REREAD 2
+ "Refresh Screen",
+#define REFRESH 3
+ "Exit Program",
+#define EXIT 4
+ };
+
+ WINDOW *menu_w;
+ int c;
+ int mpos;
+
+ /* create a new window in the lower screen area */
+
+ if((menu_w = newwin(WMENU_HGT, WMENU_LEN, WMENU_POSLN, WMENU_POSCO )) == NULL)
+ return;
+
+ keypad(menu_w, TRUE); /* use special keys */
+
+ /* draw border around the window */
+
+ box(menu_w, 0, 0);
+
+ /* add a title */
+
+ wstandout(menu_w);
+ mvwaddstr(menu_w, 0, (WMENU_LEN / 2) - (strlen(WMENU_TITLE) / 2), WMENU_TITLE);
+ wstandend(menu_w);
+
+ /* fill the window with the menu options */
+
+ for(mpos=0; mpos <= (WMITEMS-1); mpos++)
+ mvwaddstr(menu_w, mpos + 2, 2, menu[mpos]);
+
+ /* highlight the first menu option */
+
+ mpos = 0;
+ wstandout(menu_w);
+ mvwaddstr(menu_w, mpos + 2, 2, menu[mpos]);
+ wstandend(menu_w);
+
+ /* input loop */
+
+ for(;;)
+ {
+ wrefresh(menu_w);
+
+ c = wgetch(menu_w);
+
+ switch(c)
+ {
+ case TAB:
+ case KEY_DOWN: /* down-move cursor */
+ case ' ':
+ mvwaddstr(menu_w, mpos + 2, 2, menu[mpos]);
+ mpos++;
+ if(mpos >= WMITEMS)
+ mpos = 0;
+ wstandout(menu_w);
+ mvwaddstr(menu_w, mpos + 2, 2, menu[mpos]);
+ wstandend(menu_w);
+ break;
+
+ case KEY_UP: /* up-move cursor */
+ mvwaddstr(menu_w, mpos + 2, 2, menu[mpos]);
+ if(mpos)
+ mpos--;
+ else
+ mpos = WMITEMS-1;
+ wstandout(menu_w);
+ mvwaddstr(menu_w, mpos + 2, 2, menu[mpos]);
+ wstandend(menu_w);
+ break;
+
+ case 'R':
+ case 'r':
+ wrefresh(curscr);
+ goto mexit;
+
+ case 'E':
+ case 'e':
+ case 'Q':
+ case 'q':
+ case 'X':
+ case 'x':
+ do_quit(0);
+ goto mexit;
+ break;
+
+ case 'P':
+ case 'p':
+ play(cur_file);
+ goto mexit;
+ break;
+
+ case 'D':
+ case 'd':
+ delete(cur_file);
+ goto mexit;
+ break;
+
+ case CR:
+ case LF: /* exec highlighted option */
+#ifdef KEY_ENTER
+ case KEY_ENTER:
+#endif
+ switch(mpos)
+ {
+ case PLAY:
+ play(cur_file);
+ goto mexit;
+ break;
+ case DELETE:
+ delete(cur_file);
+ goto mexit;
+ break;
+ case REREAD:
+ reread();
+ goto mexit;
+ break;
+ case REFRESH:
+ wrefresh(curscr);
+ break;
+ case EXIT:
+ do_quit(0);
+ break;
+ }
+ goto mexit;
+ break;
+
+ default:
+ goto mexit;
+ break;
+ }
+ }
+
+mexit:
+ /* delete the menu window */
+
+ delwin(menu_w);
+
+ /* re-display the original lower window contents */
+
+ touchwin(main_w);
+ wrefresh(main_w);
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdntel/files.c b/usr.sbin/i4b/isdntel/files.c
new file mode 100644
index 0000000..54b2074
--- /dev/null
+++ b/usr.sbin/i4b/isdntel/files.c
@@ -0,0 +1,308 @@
+/*
+ * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * isdntel - isdn4bsd telephone answering machine support
+ * ======================================================
+ *
+ * $Id: files.c,v 1.8 1999/12/13 21:25:26 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:54:06 1999]
+ *
+ *----------------------------------------------------------------------------*/
+
+#include "defs.h"
+
+/*---------------------------------------------------------------------------*
+ * create a doubly linked list in sorted order, return pointer to new
+ * first element of list
+ *---------------------------------------------------------------------------*/
+struct onefile *store
+ (register struct onefile *new, /* new entry to store into list */
+ register struct onefile *top) /* current first entry in list */
+{
+ register struct onefile *old, *p;
+
+ if (last == NULL) /* enter very first element ? */
+ {
+ new->next = NULL;
+ new->prev = NULL;
+ last = new; /* init last */
+ return (new); /* return new first */
+ }
+ p = top; /* p = old first element */
+ old = NULL;
+ while (p)
+ {
+ if ((strcmp(p->fname, new->fname)) < 0) /* current less new ? */
+ {
+ old = p;
+ p = p->next;
+ }
+ else
+ { /* current >= new */
+
+ if (p->prev)
+ {
+ p->prev->next = new;
+ new->next = p;
+ new->prev = p->prev;
+ p->prev = new;
+ return (top);
+ }
+ new->next = p;
+ new->prev = NULL;
+ p->prev = new;
+ return (new);
+ }
+ }
+ old->next = new;
+ new->next = NULL;
+ new->prev = old;
+ last = new;
+ return (first);
+}
+
+/*---------------------------------------------------------------------------*
+ * read current directory and build up a doubly linked sorted list
+ *---------------------------------------------------------------------------*/
+int
+fill_list(void)
+{
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ register struct dirent *dp;
+#else
+ register struct direct *dp;
+#endif
+ register struct onefile *new_entry;
+ register DIR *dirp;
+ int flcnt = 0;
+ char tmp[80];
+ char *s, *d;
+
+ if ((dirp = opendir(spooldir)) == NULL)
+ fatal("cannot open spooldirectory %s!\n", spooldir);
+
+ for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp))
+ {
+ if(!isdigit(*(dp->d_name)))
+ continue;
+
+ if ((new_entry = (struct onefile *) malloc(sizeof(struct onefile))) == NULL)
+ {
+ fatal("files.c, fill_list(): structure onefile malloc failed");
+ }
+
+ /* alloc filename memory and copy name into it */
+
+ if ((new_entry->fname = (char *) malloc(strlen(dp->d_name) + 1)) == NULL)
+ {
+ fatal("files.c, fill_list(): malloc filename string memory failed");
+ }
+
+ strcpy(new_entry->fname, dp->d_name);
+
+ /* fill in remaining fields from filename */
+
+ tmp[0] = dp->d_name[4]; /* day msb */
+ tmp[1] = dp->d_name[5]; /* day lsb */
+ tmp[2] = '.';
+ tmp[3] = dp->d_name[2]; /* month msb */
+ tmp[4] = dp->d_name[3]; /* month lsb */
+ tmp[5] = '.';
+ tmp[6] = dp->d_name[0]; /* year msb */
+ tmp[7] = dp->d_name[1]; /* year lsb */
+ tmp[8] = '\0';
+
+ if((new_entry->date = (char *) malloc(strlen(tmp) + 1)) == NULL)
+ {
+ fatal("files.c, fill_list(): malloc date string memory failed");
+ }
+
+ strcpy(new_entry->date, tmp);
+
+ tmp[0] = dp->d_name[6]; /* hour msb */
+ tmp[1] = dp->d_name[7]; /* hour lsb */
+ tmp[2] = ':';
+ tmp[3] = dp->d_name[8]; /* minute msb */
+ tmp[4] = dp->d_name[9]; /* minute lsb */
+ tmp[5] = ':';
+ tmp[6] = dp->d_name[10]; /* second msb */
+ tmp[7] = dp->d_name[11]; /* second lsb */
+ tmp[8] = '\0';
+
+ if((new_entry->time = (char *) malloc(strlen(tmp) + 1)) == NULL)
+ {
+ fatal("files.c, fill_list(): malloc time string memory failed");
+ }
+
+ strcpy(new_entry->time, tmp);
+
+ /* destination number */
+
+ s = &dp->d_name[13];
+ d = &tmp[0];
+
+ while(*s && (*s != '-'))
+ *d++ = *s++;
+
+ *d = '\0';
+
+ if((new_entry->dstnumber = (char *) malloc(strlen(tmp) + 1)) == NULL)
+ {
+ fatal("files.c, fill_list(): malloc dstnumber string memory failed");
+ }
+
+ strcpy(new_entry->dstnumber, tmp);
+
+ /* source number */
+
+ s++;
+ d = &tmp[0];
+
+ while(*s && (*s != '-'))
+ *d++ = *s++;
+
+ *d = '\0';
+
+ if((new_entry->srcnumber = (char *) malloc(strlen(tmp) + 1)) == NULL)
+ {
+ fatal("files.c, fill_list(): malloc srcnumber string memory failed");
+ }
+
+ strcpy(new_entry->srcnumber, tmp);
+
+ /* length in seconds */
+
+ s++;
+ d = &tmp[0];
+
+ while(*s && (*s != '-'))
+ *d++ = *s++;
+
+ *d = '\0';
+
+ if((new_entry->seconds = (char *) malloc(strlen(tmp) + 1)) == NULL)
+ {
+ fatal("files.c, fill_list(): malloc seconds string memory failed");
+ }
+
+ strcpy(new_entry->seconds, tmp);
+
+ /* search for alias and add if found */
+
+ new_entry->alias = get_alias(new_entry->srcnumber);
+
+ /* sort entry into linked list */
+
+ first = store(new_entry, first);
+
+ flcnt++; /* increment file count */
+ }
+ closedir(dirp); /* close current dir */
+ return(flcnt); /* ok return */
+}
+
+/*---------------------------------------------------------------------------*
+ * free the current malloc'ed list
+ *---------------------------------------------------------------------------*/
+void
+free_list(void)
+{
+ register struct onefile *dir;
+ register struct onefile *tmp;
+
+ dir = first; /* start of linked list */
+
+ while (dir) /* free all */
+ {
+ tmp = dir->next; /* save ptr to next entry */
+ free(dir->fname); /* free filename space */
+ free(dir->date);
+ free(dir->time);
+ free(dir->srcnumber);
+ free(dir->dstnumber);
+ free(dir->seconds);
+ free(dir); /* free struct space */
+ dir = tmp; /* ptr = ptr to next entry */
+ }
+ first = NULL; /* first ptr = NULL */
+ last = NULL; /* last ptr = NULL */
+}
+
+/*---------------------------------------------------------------------------*
+ * delete a file
+ *---------------------------------------------------------------------------*/
+void
+delete(struct onefile *this)
+{
+ char buffer[MAXPATHLEN+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..eef8450
--- /dev/null
+++ b/usr.sbin/i4b/isdntel/isdntel.8
@@ -0,0 +1,96 @@
+.\"
+.\" Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" last edit-date: [Mon Dec 13 23:05:59 1999]
+.\"
+.\" $FreeBSD$
+.\"
+.\" $Id: isdntel.8,v 1.9 1999/12/13 22:11:55 hm Exp $
+.\"
+.Dd July 11, 1998
+.Dt ISDNTEL 8
+.Os
+.Sh NAME
+.Nm isdntel
+.Nd isdn4bsd telephone answering management utility
+.Sh SYNOPSIS
+.Nm
+.Op Fl a Ar aliasfile
+.Op Fl d Ar spooldir
+.Op Fl p Ar playcommand
+.Op Fl t Ar timeout
+.Sh DESCRIPTION
+.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
+.Dq Li cat %s \&| alaw2ulaw >/dev/audio .
+.It Fl t
+The value for
+.Ar timeout
+specifies the time in seconds the program rereads the spool directory
+when there is no keyboard activity.
+.El
+.Pp
+The screen output should be obvious. If in doubt, consult the source.
+.Sh SEE ALSO
+.Xr i4btel 4 ,
+.Xr isdnd.rc 5 ,
+.Xr isdnd 8
+.Sh BUGS
+Still two or more left.
+.Sh AUTHORS
+The
+.Nm
+utility and this manual page were written by
+.An Hellmuth Michaelis Aq hm@kts.org .
diff --git a/usr.sbin/i4b/isdntel/main.c b/usr.sbin/i4b/isdntel/main.c
new file mode 100644
index 0000000..da5ec82
--- /dev/null
+++ b/usr.sbin/i4b/isdntel/main.c
@@ -0,0 +1,400 @@
+/*
+ * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * isdntel - isdn4bsd telephone answering machine support
+ * ======================================================
+ *
+ * $Id: main.c,v 1.12 1999/12/13 21:25:26 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:54:26 1999]
+ *
+ *----------------------------------------------------------------------------*/
+
+#define MAIN
+#include <locale.h>
+#include "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;
+
+ setlocale( LC_ALL, "");
+
+ while ((i = getopt(argc, argv, "a:d:p:t:")) != -1)
+ {
+ switch (i)
+ {
+ case 'a':
+ aliasfile = optarg;
+ break;
+
+ case 'd':
+ spooldir = optarg;
+ break;
+
+ case 'p':
+ playstring = optarg;
+ break;
+
+ case 't':
+ if(isdigit(*optarg))
+ {
+ rrtimeout = strtoul(optarg, NULL, 10);
+ }
+ else
+ {
+ usage();
+ }
+ break;
+
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if(rrtimeout < 10)
+ rrtimeout = 10;
+
+ if((chdir(spooldir)) != 0)
+ fatal("cannot change directory to spooldir %s!", spooldir);
+
+ init_alias(aliasfile);
+
+ init_screen();
+
+ init_files(0);
+
+ /* go into loop */
+
+ for (;;)
+ {
+ fd_set set;
+ struct timeval timeout;
+
+ FD_ZERO(&set);
+ FD_SET(STDIN_FILENO, &set);
+ timeout.tv_sec = rrtimeout;
+ timeout.tv_usec = 0;
+
+ /* if no char is available within timeout, reread spool */
+
+ if((select(STDIN_FILENO + 1, &set, NULL, NULL, &timeout)) <= 0)
+ {
+ reread();
+ continue;
+ }
+
+ kchar = wgetch(main_w); /* get char */
+
+ switch (kchar)
+ {
+ case CR:
+ case LF:
+#ifdef KEY_ENTER
+ case KEY_ENTER:
+#endif
+ do_menu();
+ break;
+
+ case KEY_UP: /* up-move cursor */
+ if(cur_file && cur_file->prev)
+ {
+ cur_file = cur_file->prev;
+ cur_pos--;
+ }
+ break;
+
+
+ case TAB:
+ case KEY_DOWN: /* down-move cursor */
+ if(cur_file && cur_file->next)
+ {
+ cur_file = cur_file->next;
+ cur_pos++;
+ }
+ break;
+
+ case KEY_HOME: /* move cursor to first dir */
+ break;
+
+ case KEY_LL: /* move cursor to last file */
+ break;
+
+ case CNTRL_D:
+ do_quit(0);
+ break;
+
+ case CNTRL_L: /* refresh */
+ touchwin(curscr);
+ wrefresh(curscr);
+ break;
+
+ }
+ makecurrent(cur_pos, cur_file, 0);
+ }
+
+ do_quit(0);
+
+ return(0);
+}
+
+/*---------------------------------------------------------------------------*
+ * handle horizontal selection bar movement
+ *---------------------------------------------------------------------------*/
+static void
+makecurrent(int cur_pos, struct onefile *cur_file, int cold)
+{
+ static int lastpos;
+ static struct onefile *lastfile;
+ char buffer[256];
+
+ /* un-higlight current horizontal bar */
+
+ if(!cold && lastfile && cur_file)
+ {
+ sprintf(buffer, "%s %s %-16s %-16s %-20s %-6s%*s",
+ lastfile->date, lastfile->time,
+ lastfile->dstnumber, lastfile->srcnumber,
+ lastfile->alias == NULL ? "-/-" : lastfile->alias,
+ lastfile->seconds,
+ COLS - LAST_POS - 2, "");
+
+ wattroff(main_w, A_REVERSE);
+ mvwprintw(main_w, lastpos, 0, "%s", buffer);
+ wattroff(main_w, A_REVERSE);
+ }
+
+ if(cur_file == NULL)
+ {
+ lastpos = cur_pos_scr;
+ lastfile = cur_file;
+ return;
+ }
+
+ /* have to scroll up or down ? */
+
+ if(cur_pos >= bot_dis)
+ {
+ /* scroll up */
+
+ wscrl(main_w, 1);
+
+ bot_dis++;
+ top_dis++;
+ cur_pos_scr = LINES-START_O-3;
+ }
+ else if(cur_pos < top_dis)
+ {
+ /* scroll down */
+
+ wscrl(main_w, -1);
+
+ bot_dis--;
+ top_dis--;
+ cur_pos_scr = 0;
+ }
+ else
+ {
+ cur_pos_scr = cur_pos - top_dis;
+ }
+
+ sprintf(buffer, "%s %s %-16s %-16s %-20s %-6s%*s",
+ cur_file->date, cur_file->time,
+ cur_file->dstnumber, cur_file->srcnumber,
+ cur_file->alias == NULL ? "-/-" : cur_file->alias,
+ cur_file->seconds,
+ COLS - LAST_POS - 2, "");
+
+ wattron(main_w, A_REVERSE);
+ mvwprintw(main_w, cur_pos_scr, 0, "%s", buffer);
+ wattroff(main_w, A_REVERSE);
+
+ lastpos = cur_pos_scr;
+ lastfile = cur_file;
+
+ wrefresh(main_w);
+}
+
+/*---------------------------------------------------------------------------*
+ * exit program
+ *---------------------------------------------------------------------------*/
+void
+do_quit(int exitval)
+{
+ move(LINES-1, 0);
+ clrtoeol();
+ refresh();
+ endwin();
+ exit(exitval);
+}
+
+/*---------------------------------------------------------------------------*
+ * usage display and exit
+ *---------------------------------------------------------------------------*/
+static void
+usage(void)
+{
+ fprintf(stderr, "\n");
+ fprintf(stderr, "isdntel - isdn telephone answering management support utility (version %d.%d.%d)\n", VERSION, REL, STEP);
+ fprintf(stderr, " usage: isdntel -a <filename> -d <directory> -p <command> -t <timeout>\n");
+ fprintf(stderr, " -a <filename> use filename as alias file\n");
+ fprintf(stderr, " -d <directory> use directory as spool directory\n");
+ fprintf(stderr, " -p <command> specify commandline for play command\n");
+ fprintf(stderr, " -t <timeout> spool directory reread timeout in seconds\n");
+ fprintf(stderr, "\n");
+ exit(1);
+}
+
+/*---------------------------------------------------------------------------*
+ * fatal error exit
+ *---------------------------------------------------------------------------*/
+void
+fatal(char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ if(curses_ready)
+ {
+ move(LINES-1, 0);
+ clrtoeol();
+ refresh();
+ endwin();
+ }
+
+ fprintf(stderr, "\nFatal error: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n\n");
+
+ va_end(ap);
+
+ exit(1);
+}
+
+/*---------------------------------------------------------------------------*
+ * error printing
+ *---------------------------------------------------------------------------*/
+void
+error(char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ if(curses_ready)
+ {
+ wprintw(main_w, "ERROR: ");
+ vwprintw(main_w, fmt, ap);
+ wprintw(main_w, "\n");
+ wrefresh(main_w);
+ }
+ else
+ {
+ fprintf(stderr, "ERROR: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ }
+
+ va_end(ap);
+}
+
+/*---------------------------------------------------------------------------*
+ * read files and fill display
+ *---------------------------------------------------------------------------*/
+void
+init_files(int inipos)
+{
+ int i;
+
+ nofiles = fill_list();
+
+ top_dis = 0;
+ bot_dis = 0;
+
+ cur_file = first;
+
+ cur_pos = 0;
+ cur_pos_scr = 0;
+
+ if(nofiles == 0)
+ return;
+
+ for(i=0; (i < nofiles) && (i < (LINES-START_O-2)); i++)
+ {
+ mvwprintw(main_w, i, 0, "%s %s", cur_file->date, cur_file->time);
+ mvwprintw(main_w, i, DST_POS, "%s", cur_file->dstnumber);
+ mvwprintw(main_w, i, SRC_POS, "%s", cur_file->srcnumber);
+ mvwprintw(main_w, i, ALI_POS,"%s", cur_file->alias == NULL ? "-/-" : cur_file->alias);
+ mvwprintw(main_w, i, SEC_POS,"%s", cur_file->seconds);
+
+ bot_dis++;
+
+ if((cur_file = cur_file->next) == NULL)
+ break;
+ }
+
+ cur_file = first;
+
+ if(inipos)
+ {
+ for(i=0; i < inipos; i++)
+ {
+ if(cur_file->next != NULL)
+ cur_file = cur_file->next;
+ else
+ break;
+ }
+ }
+ makecurrent(cur_pos, cur_file, 1);
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdntelctl/Makefile b/usr.sbin/i4b/isdntelctl/Makefile
new file mode 100644
index 0000000..41b771e
--- /dev/null
+++ b/usr.sbin/i4b/isdntelctl/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+PROG= isdntelctl
+MAN= isdntelctl.8
+SRCS= main.c
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/i4b/isdntelctl/isdntelctl.8 b/usr.sbin/i4b/isdntelctl/isdntelctl.8
new file mode 100644
index 0000000..7e5ac24
--- /dev/null
+++ b/usr.sbin/i4b/isdntelctl/isdntelctl.8
@@ -0,0 +1,96 @@
+.\"
+.\" Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: isdntelctl.8,v 1.9 1999/12/13 22:11:55 hm Exp $
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Mon Dec 13 23:06:45 1999]
+.\"
+.Dd April 21, 1999
+.Dt ISDNTELCTL 8
+.Os
+.Sh NAME
+.Nm isdntelctl
+.Nd control isdn4bsd telephone sound format conversion
+.Sh SYNOPSIS
+.Nm
+.Op Fl c
+.Op Fl g
+.Op Fl u Ar unit
+.Op Fl A
+.Op Fl U
+.Op Fl N
+.Sh DESCRIPTION
+.Nm
+is part of the isdn4bsd package and is used to configure the sound format
+conversion facilities of the /dev/i4btel interfaces.
+.Pp
+The following options are available:
+.Bl -tag -width Ds
+.It Fl c
+Clear the telephone input queue.
+.It Fl g
+Get the sound format currently in use.
+.It Fl u
+Set the /dev/i4btel unit number. The default value is zero to access
+device /dev/i4btel0.
+.It Fl A
+Do A-law (ISDN line) -> u-law (userland) conversion.
+.It Fl U
+Do u-law (ISDN line) -> A-law (userland) conversion.
+.It Fl N
+Set sound conversion to do no format conversion.
+.El
+.Pp
+The telephony data stream comes out of the line in a bit-reversed format,
+so the
+.Xr i4btel 4
+driver does the bit-reversion process in any case.
+.Pp
+Additionally, the user can specify to do A-law to u-law, u-law to A-law
+or no conversion at all in the i4btel driver by using the
+.Nm
+utility.
+.Sh FILES
+/dev/i4btel<n>
+.Sh STANDARDS
+A-Law and u-Law are specified in ITU Recommendation G.711.
+.Sh EXAMPLES
+The command:
+.Bd -literal -offset indent
+isdntelctl -g
+.Ed
+.Pp
+displays the currently used sound format for device /dev/i4btel0.
+.Sh SEE ALSO
+.Xr g711conv 1 ,
+.Xr i4btel 4 ,
+.Xr isdnd.rc 5 ,
+.Xr isdnd 8
+.Sh AUTHORS
+The
+.Nm
+utility and this manpage were written by
+.An Hellmuth Michaelis Aq hm@kts.org .
diff --git a/usr.sbin/i4b/isdntelctl/main.c b/usr.sbin/i4b/isdntelctl/main.c
new file mode 100644
index 0000000..3d75663
--- /dev/null
+++ b/usr.sbin/i4b/isdntelctl/main.c
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * isdntelctl - i4b set telephone interface options
+ * ------------------------------------------------
+ *
+ * $Id: main.c,v 1.12 1999/12/13 21:25:26 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:54:50 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <signal.h>
+#include <errno.h>
+#include <paths.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <machine/i4b_ioctl.h>
+#include <machine/i4b_tel_ioctl.h>
+
+static void usage ( void );
+
+#define I4BTELDEVICE "/dev/i4btel"
+
+int opt_get = 0;
+int opt_unit = 0;
+int opt_U = 0;
+int opt_A = 0;
+int opt_C = 0;
+int opt_N = 0;
+
+/*---------------------------------------------------------------------------*
+ * program entry
+ *---------------------------------------------------------------------------*/
+int
+main(int argc, char **argv)
+{
+ int c;
+ int ret;
+ int telfd;
+ char namebuffer[128];
+
+ while ((c = getopt(argc, argv, "cgu:AUN")) != -1)
+ {
+ switch(c)
+ {
+ case 'c':
+ opt_C = 1;
+ break;
+
+ case 'g':
+ opt_get = 1;
+ break;
+
+ case 'u':
+ opt_unit = atoi(optarg);
+ if(opt_unit < 0 || opt_unit > 9)
+ usage();
+ break;
+
+ case 'A':
+ opt_A = 1;
+ break;
+
+ case 'U':
+ opt_U = 1;
+ break;
+
+ case 'N':
+ opt_N = 1;
+ break;
+
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if(opt_get == 0 && opt_N == 0 && opt_U == 0 && opt_A == 0 && opt_C == 0)
+ {
+ opt_get = 1;
+ }
+
+ if((opt_get + opt_N + opt_U + opt_A + opt_C) > 1)
+ {
+ usage();
+ }
+
+ sprintf(namebuffer,"%s%d", I4BTELDEVICE, opt_unit);
+
+ if((telfd = open(namebuffer, O_RDWR)) < 0)
+ {
+ fprintf(stderr, "isdntelctl: cannot open %s: %s\n", namebuffer, strerror(errno));
+ exit(1);
+ }
+
+ if(opt_get)
+ {
+ int format;
+
+ if((ret = ioctl(telfd, I4B_TEL_GETAUDIOFMT, &format)) < 0)
+ {
+ fprintf(stderr, "ioctl I4B_TEL_GETAUDIOFMT failed: %s", strerror(errno));
+ exit(1);
+ }
+
+ if(format == CVT_NONE)
+ {
+ printf("device %s does not do A-law/u-law format conversion\n", namebuffer);
+ }
+ else if(format == CVT_ALAW2ULAW)
+ {
+ printf("device %s does ISDN: A-law -> user: u-law format conversion\n", namebuffer);
+ }
+ else if(format == CVT_ULAW2ALAW)
+ {
+ printf("device %s does ISDN: u-law -> user: A-law format conversion\n", namebuffer);
+ }
+ else
+ {
+ printf("ERROR, device %s uses unknown format %d!\n", namebuffer, format);
+ }
+ exit(0);
+ }
+
+ if(opt_A)
+ {
+ int format = CVT_ALAW2ULAW;
+
+ if((ret = ioctl(telfd, I4B_TEL_SETAUDIOFMT, &format)) < 0)
+ {
+ fprintf(stderr, "ioctl I4B_TEL_SETAUDIOFMT failed: %s", strerror(errno));
+ exit(1);
+ }
+ exit(0);
+ }
+
+ if(opt_U)
+ {
+ int format = CVT_ULAW2ALAW;
+
+ if((ret = ioctl(telfd, I4B_TEL_SETAUDIOFMT, &format)) < 0)
+ {
+ fprintf(stderr, "ioctl I4B_TEL_SETAUDIOFMT failed: %s", strerror(errno));
+ exit(1);
+ }
+ exit(0);
+ }
+ if(opt_N)
+ {
+ int format = CVT_NONE;
+
+ if((ret = ioctl(telfd, I4B_TEL_SETAUDIOFMT, &format)) < 0)
+ {
+ fprintf(stderr, "ioctl I4B_TEL_SETAUDIOFMT failed: %s", strerror(errno));
+ exit(1);
+ }
+ exit(0);
+ }
+ if(opt_C)
+ {
+ int dummy;
+ if((ret = ioctl(telfd, I4B_TEL_EMPTYINPUTQUEUE, &dummy)) < 0)
+ {
+ fprintf(stderr, "ioctl I4B_TEL_EMPTYINPUTQUEUE failed: %s", strerror(errno));
+ exit(1);
+ }
+ exit(0);
+ }
+ return(0);
+}
+
+/*---------------------------------------------------------------------------*
+ * usage display and exit
+ *---------------------------------------------------------------------------*/
+static void
+usage(void)
+{
+ fprintf(stderr, "\n");
+ fprintf(stderr, "isdntelctl - %si4btel control, version %d.%d.%d (%s %s)\n", _PATH_DEV, VERSION, REL, STEP, __DATE__, __TIME__);
+ fprintf(stderr, "usage: isdntelctl -c -g -u <unit> -A -N -U\n");
+ fprintf(stderr, " -c clear input queue\n");
+ fprintf(stderr, " -g get current settings\n");
+ fprintf(stderr, " -u unit specify unit number\n");
+ fprintf(stderr, " -A set conversion ISDN: A-law -> user: u-law\n");
+ fprintf(stderr, " -U set conversion ISDN: u-law -> user: A-law\n");
+ fprintf(stderr, " -N set conversion to no A-law/u-law conversion\n");
+ fprintf(stderr, "\n");
+ exit(1);
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdntest/Makefile b/usr.sbin/i4b/isdntest/Makefile
new file mode 100644
index 0000000..2a677e9
--- /dev/null
+++ b/usr.sbin/i4b/isdntest/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+PROG= isdntest
+MAN= isdntest.8
+SRCS= main.c
+
+install:
+ @echo isdntest is not installed automatically
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/i4b/isdntest/isdntest.8 b/usr.sbin/i4b/isdntest/isdntest.8
new file mode 100644
index 0000000..172a0ff
--- /dev/null
+++ b/usr.sbin/i4b/isdntest/isdntest.8
@@ -0,0 +1,111 @@
+.\"
+.\" Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: isdntest.8,v 1.10 1999/12/13 22:11:55 hm Exp $
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Mon Dec 13 23:07:23 1999]
+.\"
+.Dd December 10, 1999
+.Dt ISDNTEST 8
+.Os
+.Sh NAME
+.Nm isdntest
+.Nd isdn4bsd debugging and verification tool
+.Sh SYNOPSIS
+.Nm
+.Op Fl c Ar unit
+.Op Fl d Ar level
+.Op Fl i Ar number
+.Op Fl h
+.Op Fl o Ar number
+.Op Fl t Ar num
+.Op Fl w
+.Sh DESCRIPTION
+.Nm
+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 cannot be used while the daemon runs.
+.Pp
+The following options are available:
+.Bl -tag -width Ds
+.It Fl c
+Use controller unit number for test.
+.It Fl d
+Set the debugging level
+.It Fl h
+Use HDLC as the B channel layer 1 protocol instead of no protocol.
+.It Fl i
+Use number to verify the incoming number.
+.It Fl o
+Use number as the outgoing number to dial.
+.It Fl t
+Set number of times the test pattern on the B-channel is exchanged
+.It Fl w
+Wait for keyboard input for terminating the call.
+.El
+.Pp
+The
+.Nm
+utility is currently of not much use for end users, it is primarily a debugging
+tool for development and is part of the release in the hope, that someone
+enhances it as a real self test and setup-verification tool!
+.Pp
+.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.
+.Sh FILES
+.Bl -tag -width indent
+.It Pa /dev/i4b
+.El
+.Sh AUTHORS
+The
+.Nm
+utility and this manpage were written by
+.An Hellmuth Michaelis Aq hm@kts.org .
diff --git a/usr.sbin/i4b/isdntest/main.c b/usr.sbin/i4b/isdntest/main.c
new file mode 100644
index 0000000..2d80e2e
--- /dev/null
+++ b/usr.sbin/i4b/isdntest/main.c
@@ -0,0 +1,745 @@
+/*
+ * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * main.c - i4b selftest utility
+ * -----------------------------
+ *
+ * $Id: main.c,v 1.16 2000/03/13 16:18:38 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Mar 13 17:19:26 2000]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <signal.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <machine/i4b_ioctl.h>
+#include <machine/i4b_cause.h>
+
+static void kbdrdhdl ( void );
+static void isdnrdhdl (int isdnfd );
+
+void handle_connect_ind(unsigned char *ptr);
+void handle_disconnect(unsigned char *ptr);
+void handle_connect_active_ind(unsigned char *ptr);
+
+int connect_response(int isdnfd, unsigned int cdid, int response);
+int disconnect_request(int isdnfd, unsigned int cdid);
+unsigned int get_cdid(int isdnfd);
+int connect_request(int isdnfd, unsigned int cdid);
+int do_test(void);
+static void cleanup(void);
+static void usage(void);
+void setup_wrfix(int len, unsigned char *buf);
+int check_rd(int len, unsigned char *wbuf, unsigned char *rdbuf);
+
+static int isdnfd;
+char outgoingnumber[32];
+char incomingnumber[32];
+int debug_level = 0;
+
+#define I4BDEVICE "/dev/i4b"
+#define DATADEV0 "/dev/i4brbch0"
+#define DATAUNIT0 0
+#define DATADEV1 "/dev/i4brbch1"
+#define DATAUNIT1 1
+
+unsigned int out_cdid = CDID_UNUSED;
+unsigned int in_cdid = CDID_UNUSED;
+
+int waitchar = 0;
+int usehdlc = 0;
+int controller = 0;
+int dotest = 0;
+
+/*---------------------------------------------------------------------------*
+ * usage display and exit
+ *---------------------------------------------------------------------------*/
+static void
+usage(void)
+{
+ fprintf(stderr, "\n");
+ fprintf(stderr, "isdntest - i4b selftest, version %d.%d.%d, compiled %s %s\n",VERSION, REL, STEP, __DATE__, __TIME__);
+ fprintf(stderr, "usage: isdntest [-c ctrl] [-d level] [-h] [-i telno] [-o telno] [-t num] [-w]\n");
+ fprintf(stderr, " -c <ctrl> specify controller to use\n");
+ fprintf(stderr, " -d <level> set debug level\n");
+ fprintf(stderr, " -h use HDLC as Bchannel protocol\n");
+ fprintf(stderr, " -i <telno> incoming telephone number\n");
+ fprintf(stderr, " -o <telno> outgoing telephone number\n");
+ fprintf(stderr, " -t <num> send test pattern num times\n");
+ fprintf(stderr, " -w wait for keyboard entry to disconnect\n");
+ fprintf(stderr, "\n");
+ exit(1);
+}
+
+/*---------------------------------------------------------------------------*
+ * program entry
+ *---------------------------------------------------------------------------*/
+int
+main(int argc, char **argv)
+{
+ int i;
+ int c;
+ fd_set set;
+ int ret;
+ char *ptr;
+
+ incomingnumber[0] = '\0';
+ outgoingnumber[0] = '\0';
+
+ while ((c = getopt(argc, argv, "c:d:hi:o:t:w")) != -1)
+ {
+ switch(c)
+ {
+ case 'c':
+ if(isdigit(*optarg))
+ {
+ controller = strtoul(optarg, NULL, 10);
+ }
+ else
+ {
+ fprintf(stderr, "Error: option -c requires a numeric argument!\n");
+ usage();
+ }
+ break;
+
+ case 'd':
+ if(isdigit(*optarg))
+ {
+ debug_level = strtoul(optarg, NULL, 10);
+ }
+ else
+ {
+ fprintf(stderr, "Error: option -d requires a numeric argument!\n");
+ usage();
+ }
+ break;
+
+ case 'o':
+ i = 0;
+ ptr = optarg;
+
+ while(*ptr)
+ {
+ if(isdigit(*ptr))
+ {
+ outgoingnumber[i++] = *ptr++;
+ }
+ else
+ {
+ fprintf(stderr, "Error: option -o requires a numeric argument!\n");
+ usage();
+ }
+ }
+ outgoingnumber[i] = '\0';
+ break;
+
+ case 'i':
+ i = 0;
+ ptr = optarg;
+
+ while(*ptr)
+ {
+ if(isdigit(*ptr))
+ {
+ incomingnumber[i++] = *ptr++;
+ }
+ else
+ {
+ fprintf(stderr, "Error: option -i requires a numeric argument!\n");
+ usage();
+ }
+ }
+ incomingnumber[i] = '\0';
+ break;
+
+ case 'w':
+ waitchar = 1;
+ break;
+
+ case 'h':
+ usehdlc = 1;
+ break;
+
+ case 't':
+ if(isdigit(*optarg))
+ {
+ dotest = strtoul(optarg, NULL, 10);
+ }
+ else
+ {
+ fprintf(stderr, "Error: option -t requires a numeric argument!\n");
+ usage();
+ }
+ break;
+
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if((strlen(incomingnumber) == 0) || (strlen(outgoingnumber) == 0))
+ usage();
+
+ fprintf(stderr, "isdntest: accepting calls from telephone number [%s] \n", incomingnumber);
+ fprintf(stderr, "isdntest: calling out telephone number [%s] \n", outgoingnumber);
+
+ if((atexit(cleanup)) != 0)
+ {
+ fprintf(stderr, "isdntest: atexit error: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ /* open isdn device */
+
+ if((isdnfd = open(I4BDEVICE, O_RDWR)) < 0)
+ {
+ fprintf(stderr, "\nisdntest: cannot open %s: %s\n", I4BDEVICE, strerror(errno));
+ fprintf(stderr, " isdnd is probably running, to use isdntest,\n");
+ fprintf(stderr, " terminate isdnd and then run isdntest again!\n");
+ exit(1);
+ }
+
+ if((out_cdid = get_cdid(isdnfd)) == 0)
+ {
+ fprintf(stderr, "isdntest: error getting cdid: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ if((connect_request(isdnfd, out_cdid)) == -1)
+ {
+ fprintf(stderr, "isdntest: error, outgoing call failed!\n");
+ exit(1);
+ }
+
+ for(;;)
+ {
+ FD_ZERO(&set);
+
+ FD_SET(0, &set);
+
+ FD_SET(isdnfd, &set);
+
+ ret = select(isdnfd + 1, &set, NULL, NULL, NULL);
+
+ if(ret > 0)
+ {
+ if(FD_ISSET(isdnfd, &set))
+ isdnrdhdl(isdnfd);
+
+ if(FD_ISSET(0, &set))
+ kbdrdhdl();
+ }
+ else
+ {
+ fprintf(stderr, "isdntest: select error: %s\n", strerror(errno));
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * data from keyboard available
+ *---------------------------------------------------------------------------*/
+static void
+kbdrdhdl(void)
+{
+ cleanup();
+ exit(2);
+}
+
+/*---------------------------------------------------------------------------*
+ * data from /dev/isdn available, read and process them
+ *---------------------------------------------------------------------------*/
+static void
+isdnrdhdl(int isdnfd)
+{
+ static unsigned char buf[1024];
+ int len;
+
+ if((len = read(isdnfd, buf, 1024 - 1)) > 0)
+ {
+ switch (buf[0])
+ {
+ case MSG_CONNECT_IND:
+ handle_connect_ind(&buf[0]);
+ break;
+
+ case MSG_CONNECT_ACTIVE_IND:
+ handle_connect_active_ind(&buf[0]);
+ break;
+
+ case MSG_DISCONNECT_IND:
+ handle_disconnect(&buf[0]);
+ break;
+
+ default:
+ if(debug_level)
+ fprintf(stderr, "isdntest: unknown message 0x%x = %c\n", buf[0], buf[0]);
+ break;
+ }
+ }
+ else
+ {
+ fprintf(stderr, "isdntest: read error, errno = %d, length = %d", errno, len);
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * initiate an outgoing connection
+ *---------------------------------------------------------------------------*/
+int
+connect_request(int isdnfd, unsigned int cdid)
+{
+ msg_connect_req_t mcr;
+ int ret;
+
+ bzero(&mcr, sizeof(msg_connect_req_t));
+
+ mcr.controller = controller;
+ mcr.channel = CHAN_ANY; /* any channel */
+ mcr.cdid = cdid; /* cdid from get_cdid() */
+
+ if(usehdlc)
+ mcr.bprot = BPROT_RHDLC;/* b channel protocol */
+ else
+ mcr.bprot = BPROT_NONE; /* b channel protocol */
+
+ mcr.driver = BDRV_RBCH; /* raw b channel driver */
+ mcr.driver_unit = DATAUNIT0; /* raw b channel driver unit */
+
+ strcpy(mcr.dst_telno, outgoingnumber);
+ strcpy(mcr.src_telno, incomingnumber);
+
+ if((ret = ioctl(isdnfd, I4B_CONNECT_REQ, &mcr)) < 0)
+ {
+ fprintf(stderr, "ioctl I4B_CONNECT_REQ failed: %s", strerror(errno));
+ return(-1);
+ }
+ fprintf(stderr, "isdntest: calling out to telephone number [%s] \n", outgoingnumber);
+ return(0);
+}
+
+/*---------------------------------------------------------------------------*
+ * handle setup indicator
+ *---------------------------------------------------------------------------*/
+void
+handle_connect_ind(unsigned char *ptr)
+{
+ msg_connect_ind_t *msi = (msg_connect_ind_t *)ptr;
+
+ fprintf(stderr, "isdntest: incoming SETUP: from %s to %s\n",
+ msi->src_telno,
+ msi->dst_telno);
+
+ fprintf(stderr, " channel %d, controller %d, bprot %d, cdid %d\n",
+ msi->channel,
+ msi->controller,
+ msi->bprot,
+ msi->header.cdid);
+
+ in_cdid = msi->header.cdid;
+
+ if(strcmp(msi->dst_telno, outgoingnumber))
+ {
+ msg_connect_resp_t msr;
+ int ret;
+
+ fprintf(stderr, "isdntest: ignoring incoming SETUP: my number [%s] != outgoing [%s]\n",
+ msi->dst_telno, outgoingnumber);
+
+ msr.cdid = in_cdid;
+ msr.response = SETUP_RESP_DNTCRE;
+
+ if((ret = ioctl(isdnfd, I4B_CONNECT_RESP, &msr)) < 0)
+ {
+ fprintf(stderr, "ioctl I4B_CONNECT_RESP ignore failed: %s", strerror(errno));
+ exit(1);
+ }
+
+ }
+ else
+ {
+ msg_connect_resp_t msr;
+ int ret;
+
+ fprintf(stderr, "isdntest: accepting call, sending CONNECT_RESPONSE .....\n");
+
+ msr.cdid = in_cdid;
+ msr.response = SETUP_RESP_ACCEPT;
+
+ if(usehdlc)
+ msr.bprot = BPROT_RHDLC;
+ else
+ msr.bprot = BPROT_NONE;
+
+ msr.driver = BDRV_RBCH;
+ msr.driver_unit = DATAUNIT1;
+
+ if((ret = ioctl(isdnfd, I4B_CONNECT_RESP, &msr)) < 0)
+ {
+ fprintf(stderr, "ioctl I4B_CONNECT_RESP accept failed: %s", strerror(errno));
+ exit(1);
+ }
+ }
+}
+
+#define SLEEPTIME 5
+
+/*---------------------------------------------------------------------------*
+ * handle connection active
+ *---------------------------------------------------------------------------*/
+void
+handle_connect_active_ind(unsigned char *ptr)
+{
+ msg_connect_active_ind_t *msi = (msg_connect_active_ind_t *)ptr;
+ int i;
+
+ fprintf(stderr, "isdntest: connection active, cdid %d\n", msi->header.cdid);
+
+ if(out_cdid == msi->header.cdid)
+ {
+ if(waitchar)
+ {
+ fprintf(stderr, "isdntest: press any key to disconnect ...%c%c%c\n", 0x07, 0x07, 0x07);
+ getchar();
+ }
+ else
+ {
+ if(dotest)
+ {
+ do_test();
+ }
+ else
+ {
+ fprintf(stderr, "isdntest: %d secs delay until disconnect:", SLEEPTIME);
+
+ for(i=0; i < SLEEPTIME;i++)
+ {
+ fprintf(stderr, " .");
+ sleep(1);
+ }
+ fprintf(stderr, "\n");
+ }
+ cleanup();
+ exit(0);
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * handle disconnect indication
+ *---------------------------------------------------------------------------*/
+void
+handle_disconnect(unsigned char *ptr)
+{
+ msg_disconnect_ind_t *mdi = (msg_disconnect_ind_t *)ptr;
+
+ if(mdi->header.cdid == out_cdid)
+ {
+ fprintf(stderr, "isdntest: incoming disconnect indication, cdid %d (out_cdid), cause %d\n",
+ mdi->header.cdid, mdi->cause);
+
+ out_cdid = CDID_UNUSED;
+ }
+ else if(mdi->header.cdid == in_cdid)
+ {
+ fprintf(stderr, "isdntest: incoming disconnect indication, cdid %d (in_cdid), cause %d\n",
+ mdi->header.cdid, mdi->cause);
+ in_cdid = CDID_UNUSED;
+ }
+ else
+ {
+ fprintf(stderr, "isdntest: incoming disconnect indication, cdid %d (???), cause %d\n",
+ mdi->header.cdid, mdi->cause);
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * hang up
+ *---------------------------------------------------------------------------*/
+int
+disconnect_request(int isdnfd, unsigned int cdid)
+{
+ msg_discon_req_t mdr;
+ int ret;
+
+ mdr.cdid = cdid;
+ mdr.cause = (CAUSET_I4B << 8) | CAUSE_I4B_NORMAL;
+
+ if((ret = ioctl(isdnfd, I4B_DISCONNECT_REQ, &mdr)) < 0)
+ {
+ fprintf(stderr, "ioctl I4B_DISCONNECT_REQ failed: %s", strerror(errno));
+ return(-1);
+ }
+ fprintf(stderr, "isdntest: sending disconnect request\n");
+ return(0);
+}
+
+/*---------------------------------------------------------------------------*
+ * get cdid from kernel
+ *---------------------------------------------------------------------------*/
+unsigned int
+get_cdid(int isdnfd)
+{
+ msg_cdid_req_t mcr;
+ int ret;
+
+ mcr.cdid = 0;
+
+ if((ret = ioctl(isdnfd, I4B_CDID_REQ, &mcr)) < 0)
+ {
+ fprintf(stderr, "ioctl I4B_CDID_REQ failed: %s", strerror(errno));
+ return(0);
+ }
+ fprintf(stderr, "isdntest: got cdid %d from kernel\n", mcr.cdid);
+ return(mcr.cdid);
+}
+
+/*---------------------------------------------------------------------------*
+ * make shure all cdid's are inactive before leaving
+ *---------------------------------------------------------------------------*/
+void cleanup(void)
+{
+ int len;
+ char buf[1024];
+
+ if(out_cdid != CDID_UNUSED)
+ {
+ fprintf(stderr, "isdntest: cleanup, send disconnect req for out_cdid %d, in_cdid %d\n", out_cdid, in_cdid);
+ disconnect_request(isdnfd, out_cdid);
+ }
+
+ while((out_cdid != CDID_UNUSED) || (in_cdid != CDID_UNUSED))
+ {
+ if(debug_level)
+ fprintf(stderr, "isdntest: cleanup, out_cdid %d, in_cdid %d\n", out_cdid, in_cdid);
+
+ if((len = read(isdnfd, buf, 1024 - 1)) > 0)
+ {
+ switch (buf[0])
+ {
+ case MSG_CONNECT_IND:
+ handle_connect_ind(&buf[0]);
+ break;
+
+ case MSG_CONNECT_ACTIVE_IND:
+ handle_connect_active_ind(&buf[0]);
+ break;
+
+ case MSG_DISCONNECT_IND:
+ handle_disconnect(&buf[0]);
+ break;
+
+ default:
+ if(debug_level)
+ fprintf(stderr, "isdntest: unknown message 0x%x = %c\n", buf[0], buf[0]);
+ break;
+ }
+ }
+ else
+ {
+ fprintf(stderr, "isdntest: read error, errno = %d, length = %d", errno, len);
+ }
+ }
+ if(debug_level)
+ fprintf(stderr, "isdntest: exit cleanup, out_cdid %d, in_cdid %d\n", out_cdid, in_cdid);
+}
+
+/*---------------------------------------------------------------------------*
+ * test the b-channels
+ *---------------------------------------------------------------------------*/
+int do_test(void)
+{
+
+#define FPH 0x3c
+#define FPL 0x66
+
+ int fd0, fd1;
+ unsigned char wrbuf[2048];
+ unsigned char rdbuf[2048];
+ int sz;
+ fd_set set;
+ struct timeval timeout;
+ int ret;
+ int frame;
+ int errcnt;
+ int frm_len;
+ int bytecnt = 0;
+ time_t start_time;
+ time_t cur_time;
+ time_t run_time;
+
+ if((fd0 = open(DATADEV0, O_RDWR)) == -1)
+ {
+ fprintf(stderr, "open of %s failed: %s", DATADEV0, strerror(errno));
+ return(-1);
+ }
+
+ if((fd1 = open(DATADEV1, O_RDWR)) == -1)
+ {
+ fprintf(stderr, "open of %s failed: %s", DATADEV1, strerror(errno));
+ return(-1);
+ }
+
+ printf("\n");
+ frame = 0;
+ errcnt = 0;
+
+ frm_len = 2;
+
+ start_time = time(NULL);
+
+ printf(" frame size errors totalbytes bps elapsedtime\n");
+
+ for(;dotest > 0; dotest--)
+ {
+ setup_wrfix(frm_len, &wrbuf[0]);
+
+ frame++;
+
+ bytecnt += frm_len;
+
+ printf("%6d %4d", frame, frm_len);
+ fflush(stdout);
+
+ if((sz = write(fd0, wrbuf, frm_len)) != frm_len)
+ {
+ fprintf(stderr, "write (%d of %d bytes) to %s failed: %s\n", sz, frm_len, DATADEV0, strerror(errno));
+ }
+
+ timeout.tv_sec = 2;
+ timeout.tv_usec = 0;
+
+ FD_ZERO(&set);
+
+ FD_SET(0, &set);
+ FD_SET(fd1, &set);
+
+ ret = select(fd1+1, &set, NULL, NULL, &timeout);
+
+ if(ret > 0)
+ {
+ if(FD_ISSET(fd1, &set))
+ {
+ if((sz = read(fd1, rdbuf, 2048)) != frm_len)
+ {
+ fprintf(stderr, "read (%d bytes) from %s failed: %s\n", sz, DATADEV1, strerror(errno));
+ }
+
+ cur_time = time(NULL);
+ run_time = difftime(cur_time, start_time);
+
+ if(run_time == 0)
+ run_time = 1;
+
+ printf(" %6d %10d %4d %2.2d:%2.2d:%2.2d \r",
+ errcnt, bytecnt,
+ (int)((int)bytecnt/(int)run_time),
+ (int)run_time/3600, (int)run_time/60, (int)run_time%60);
+ fflush(stdout);
+
+ errcnt += check_rd(frm_len, &wrbuf[0], &rdbuf[0]);
+
+#ifdef NOTDEF
+ for(i=0; i<sz; i++)
+ {
+ printf("0x%02x ", (unsigned char)rdbuf[i]);
+ }
+ printf("\n");
+#endif
+ }
+
+ if(FD_ISSET(0, &set))
+ {
+ return(0);
+ printf("\n\n");
+ }
+ }
+ else
+ {
+ fprintf(stderr, "isdntest, do_test: select error: %s\n", strerror(errno));
+ }
+
+ frm_len = frm_len*2;
+ if(frm_len > 2048)
+ frm_len = 2;
+
+ }
+ printf("\n\n");
+ return(0);
+}
+
+void
+setup_wrfix(int len, unsigned char *buf)
+{
+ register int i;
+
+ for(i=0; i<len;)
+ {
+ *buf = FPH;
+ buf++;
+ *buf = FPL;
+ buf++;
+ i+=2;
+ }
+}
+
+int
+check_rd(int len, unsigned char *wbuf, unsigned char *rbuf)
+{
+ register int i;
+ int ret = 0;
+
+ for(i=0; i<len; i++)
+ {
+ if(*wbuf != *rbuf)
+ {
+ fprintf(stderr, "\nERROR, byte %d, written 0x%02x, read 0x%02x\n", i, *wbuf, *rbuf);
+ ret++;
+ }
+ wbuf++;
+ rbuf++;
+ }
+ return(ret);
+}
+
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdntrace/1tr6.c b/usr.sbin/i4b/isdntrace/1tr6.c
new file mode 100644
index 0000000..db2bc71
--- /dev/null
+++ b/usr.sbin/i4b/isdntrace/1tr6.c
@@ -0,0 +1,756 @@
+/*
+ * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * 1tr6.c - print 1TR6 protocol traces
+ * -----------------------------------
+ *
+ * $Id: 1tr6.c,v 1.6 1999/12/13 21:25:26 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:55:31 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "trace.h"
+
+static int p_1tr6address(char *pbuf, unsigned char buf[]);
+static int p_1tr6cause(char *pbuf, unsigned char buf[]);
+
+/*---------------------------------------------------------------------------*
+ * decode the (german) national specific 1TR6 protocol
+ *---------------------------------------------------------------------------*/
+void
+decode_1tr6(char *pbuf, int n, int off, unsigned char *buf, int raw)
+{
+ int codeset = 0;
+ int oldcodeset = 0;
+ int codelock = 0;
+
+ int pd;
+ int len;
+ int j;
+ int i;
+
+ if(n <= 0)
+ return;
+
+ *pbuf = '\0';
+
+ if(raw)
+ {
+ for (i = 0; i < n; i += 16)
+ {
+ sprintf((pbuf+strlen(pbuf)),"Dump:%.3d ", i+off);
+ for (j = 0; j < 16; j++)
+ if (i + j < n)
+ sprintf((pbuf+strlen(pbuf)),"%02x ", buf[i + j]);
+ else
+ sprintf((pbuf+strlen(pbuf))," ");
+ sprintf((pbuf+strlen(pbuf))," ");
+ for (j = 0; j < 16 && i + j < n; j++)
+ if (isprint(buf[i + j]))
+ sprintf((pbuf+strlen(pbuf)),"%c", buf[i + j]);
+ else
+ sprintf((pbuf+strlen(pbuf)),".");
+ sprintf((pbuf+strlen(pbuf)),"\n");
+ }
+ }
+
+ sprintf((pbuf+strlen(pbuf)), "1TR6: ");
+
+ /* protocol discriminator */
+
+ i = 0;
+
+ pd = buf[i];
+
+ switch(pd)
+ {
+ case 0x40:
+ sprintf((pbuf+strlen(pbuf)), "pd=N0, ");
+ break;
+ case 0x41:
+ sprintf((pbuf+strlen(pbuf)), "pd=N1, ");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "pd=UNDEF (0x%02x), ",pd);
+ break;
+ }
+
+ /* call reference */
+
+ i++;
+
+ len = buf[i] & 0x0f;
+
+ switch(len)
+ {
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "cr=0x%02x %s, ", (buf[i+1] & 0x7f), (buf[i+1] & 0x80) ? "(from destination)" : "(from origination)");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "cr: LEN=%d %s 0x%02x 0x%02x, ", len, (buf[i+1] & 0x80) ? "org" : "dst", (buf[i+1] & 0x7f), (buf[i+2] & 0x7f));
+ break;
+ }
+
+ i += (len+1);
+
+ /* message type */
+
+ sprintf((pbuf+strlen(pbuf)), "message=");
+
+ if(pd == 0x40) /* protocol discriminator N0 */
+ {
+ switch(buf[i])
+ {
+ case 0x61:
+ sprintf((pbuf+strlen(pbuf)), "REGISTER INDICATION: ");
+ break;
+ case 0x62:
+ sprintf((pbuf+strlen(pbuf)), "CANCEL INDICATION: ");
+ break;
+ case 0x63:
+ sprintf((pbuf+strlen(pbuf)), "FACILITY STATUS: ");
+ break;
+ case 0x64:
+ sprintf((pbuf+strlen(pbuf)), "STATUS ACKNOWLEDGE: ");
+ break;
+ case 0x65:
+ sprintf((pbuf+strlen(pbuf)), "STATUS REJECT: ");
+ break;
+ case 0x66:
+ sprintf((pbuf+strlen(pbuf)), "FACILITY INFORMATION: ");
+ break;
+ case 0x67:
+ sprintf((pbuf+strlen(pbuf)), "INFORMATION ACKNOWLEDGE: ");
+ break;
+ case 0x68:
+ sprintf((pbuf+strlen(pbuf)), "INFORMATION REJECT: ");
+ break;
+ case 0x75:
+ sprintf((pbuf+strlen(pbuf)), "CLOSE: ");
+ break;
+ case 0x77:
+ sprintf((pbuf+strlen(pbuf)), "CLOSE ACKNOWLEDGE: ");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "ERROR: PD=0x40 MSG=0x%02x, ", buf[i]);
+ break;
+ }
+ }
+ else if(pd == 0x41)
+ {
+ switch(buf[i])
+ {
+ case 0x00:
+ sprintf((pbuf+strlen(pbuf)), "ESCAPE: ");
+ break;
+ case 0x01:
+ sprintf((pbuf+strlen(pbuf)), "ALERT: ");
+ break;
+ case 0x02:
+ sprintf((pbuf+strlen(pbuf)), "CALL SENT: ");
+ break;
+ case 0x07:
+ sprintf((pbuf+strlen(pbuf)), "CONNECT: ");
+ break;
+ case 0x0f:
+ sprintf((pbuf+strlen(pbuf)), "CONNECT ACKNOWLEDGE: ");
+ break;
+ case 0x05:
+ sprintf((pbuf+strlen(pbuf)), "SETUP: ");
+ break;
+ case 0x0d:
+ sprintf((pbuf+strlen(pbuf)), "SETUP ACKNOWLEDGE: ");
+ break;
+
+ case 0x26:
+ sprintf((pbuf+strlen(pbuf)), "RESUME: ");
+ break;
+ case 0x2e:
+ sprintf((pbuf+strlen(pbuf)), "RESUME ACKNOWLEDGE: ");
+ break;
+ case 0x22:
+ sprintf((pbuf+strlen(pbuf)), "RESUME REJECT: ");
+ break;
+ case 0x25:
+ sprintf((pbuf+strlen(pbuf)), "SUSPEND: ");
+ break;
+ case 0x2d:
+ sprintf((pbuf+strlen(pbuf)), "SUSPEND ACKNOWLEDGE: ");
+ break;
+ case 0x21:
+ sprintf((pbuf+strlen(pbuf)), "SUSPEND REJECT: ");
+ break;
+ case 0x20:
+ sprintf((pbuf+strlen(pbuf)), "USER INFORMATION: ");
+ break;
+
+ case 0x40:
+ sprintf((pbuf+strlen(pbuf)), "DETACH");
+ break;
+ case 0x45:
+ sprintf((pbuf+strlen(pbuf)), "DISCONNECT: ");
+ break;
+ case 0x4d:
+ sprintf((pbuf+strlen(pbuf)), "RELEASE: ");
+ break;
+ case 0x5a:
+ sprintf((pbuf+strlen(pbuf)), "RELEASE ACKNOWLEDGE");
+ break;
+
+ case 0x6e:
+ sprintf((pbuf+strlen(pbuf)), "CANCEL ACKNOWLEDGE: ");
+ break;
+ case 0x67:
+ sprintf((pbuf+strlen(pbuf)), "CANCEL REJECT: ");
+ break;
+ case 0x69:
+ sprintf((pbuf+strlen(pbuf)), "CONGESTION CONTROL: ");
+ break;
+ case 0x60:
+ sprintf((pbuf+strlen(pbuf)), "FACILITY: ");
+ break;
+ case 0x68:
+ sprintf((pbuf+strlen(pbuf)), "FACILITY ACKNOWLEDGE: ");
+ break;
+ case 0x66:
+ sprintf((pbuf+strlen(pbuf)), "FACILITY CANCEL: ");
+ break;
+ case 0x64:
+ sprintf((pbuf+strlen(pbuf)), "FACILITY REGISTER: ");
+ break;
+ case 0x65:
+ sprintf((pbuf+strlen(pbuf)), "FACILITY REJECT: ");
+ break;
+ case 0x6d:
+ sprintf((pbuf+strlen(pbuf)), "INFORMATION: ");
+ break;
+ case 0x6c:
+ sprintf((pbuf+strlen(pbuf)), "REGISTER ACKNOWLEDGE: ");
+ break;
+ case 0x6f:
+ sprintf((pbuf+strlen(pbuf)), "REGISTER REJECT: ");
+ break;
+ case 0x63:
+ sprintf((pbuf+strlen(pbuf)), "STATUS: ");
+ break;
+
+ default:
+ sprintf((pbuf+strlen(pbuf)), "ERROR: PD=0x41 MSG=0x%02x, ", buf[i]);
+ break;
+ }
+ }
+ else
+ {
+ sprintf((pbuf+strlen(pbuf)), "ERROR: PD=0x%02x MSG=0x%02x, ", pd, buf[i]);
+ }
+
+ /* other information elements */
+
+ i++;
+
+ for (; i < n;)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\n ");
+
+ if(buf[i] & 0x80)
+ {
+ /* single octett info element */
+
+ switch(buf[i] & 0x70)
+ {
+ case 0x00: /* reserved */
+ sprintf((pbuf+strlen(pbuf)), "[reserved single octett info]");
+ break;
+
+ case 0x10: /* shift */
+ oldcodeset = codeset;
+ codeset = buf[i] & 0x07;
+ if(buf[i] & 0x08)
+ codelock = 0;
+ else
+ codelock = 1;
+ sprintf((pbuf+strlen(pbuf)), "[shift: codeset=%d lock=%d]", codeset, codelock);
+ break;
+
+ case 0x20: /* more data */
+ sprintf((pbuf+strlen(pbuf)), "[more data]");
+ break;
+
+ case 0x30: /* congestion level */
+ sprintf((pbuf+strlen(pbuf)), "[congestion level = %d]", buf[i] & 0x0f);
+ break;
+
+ default:
+ sprintf((pbuf+strlen(pbuf)), "[UNDEF SINGLE OCTET ELEMENT 0x%02x]", buf[i]);
+ break;
+ }
+
+ i++; /* next */
+
+ }
+ else
+ {
+ /* variable length info element */
+
+ if(codeset == 0)
+ {
+ switch(buf[i])
+ {
+ case 0x08:
+ sprintf((pbuf+strlen(pbuf)), "[cause: ");
+ i += p_1tr6cause(pbuf, &buf[i]);
+ goto next;
+ break;
+
+ case 0x0c:
+ sprintf((pbuf+strlen(pbuf)), "[connected address: ");
+ i += p_1tr6address(pbuf, &buf[i]);
+ goto next;
+ break;
+
+ case 0x10:
+ sprintf((pbuf+strlen(pbuf)), "[call identity: ");
+ break;
+ case 0x18:
+ sprintf((pbuf+strlen(pbuf)), "[channel id: channel=");
+ i += 2;
+ switch(buf[i] & 0x03)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "no channel");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "B-1");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "B-2");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "any channel");
+ break;
+ }
+ if(buf[i] & 0x08)
+ sprintf((pbuf+strlen(pbuf)), " (exclusive)]");
+ else
+ sprintf((pbuf+strlen(pbuf)), " (preferred)]");
+ i++;
+ goto next;
+ break;
+ case 0x20:
+ sprintf((pbuf+strlen(pbuf)), "[network specific facilities: ");
+ i++;
+ len = buf[i];
+ i+=2;
+ switch(buf[i])
+ {
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "Sperre");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "AWS 1");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "AWS 2");
+ break;
+ case 0xe:
+ sprintf((pbuf+strlen(pbuf)), "Konferenz");
+ break;
+ case 0xf:
+ sprintf((pbuf+strlen(pbuf)), "B-Kan uebern.");
+ break;
+ case 0x10:
+ sprintf((pbuf+strlen(pbuf)), "aktvrg. ghlt. Vbdg.");
+ break;
+ case 0x11:
+ sprintf((pbuf+strlen(pbuf)), "3er Konf");
+ break;
+ case 0x12:
+ sprintf((pbuf+strlen(pbuf)), "1seitg D/G Wechsel");
+ break;
+ case 0x13:
+ sprintf((pbuf+strlen(pbuf)), "2seitig D/G Wechsel");
+ break;
+ case 0x14:
+ sprintf((pbuf+strlen(pbuf)), "Rufnr. identifiz.");
+ break;
+ case 0x15:
+ sprintf((pbuf+strlen(pbuf)), "GBG");
+ break;
+ case 0x17:
+ sprintf((pbuf+strlen(pbuf)), "ueberg. Ruf");
+ break;
+ case 0x1a:
+ sprintf((pbuf+strlen(pbuf)), "um/weitergel. Ruf");
+ break;
+ case 0x1b:
+ sprintf((pbuf+strlen(pbuf)), "unterdr. A-Rufnr.");
+ break;
+ case 0x1e:
+ sprintf((pbuf+strlen(pbuf)), "Verbdg. deaktivieren");
+ break;
+ case 0x1d:
+ sprintf((pbuf+strlen(pbuf)), "Verbdg. aktivieren");
+ break;
+ case 0x1f:
+ sprintf((pbuf+strlen(pbuf)), "SPV");
+ break;
+ case 0x23:
+ sprintf((pbuf+strlen(pbuf)), "Rueckw. 2seitg. DW");
+ break;
+ case 0x24:
+ sprintf((pbuf+strlen(pbuf)), "Anrufumltg. priv. Netz");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "undefined");
+ break;
+ }
+ i++;
+ sprintf((pbuf+strlen(pbuf)), ", serv=%d", buf[i]);
+ i++;
+ sprintf((pbuf+strlen(pbuf)), ", ainfo=%d", buf[i]);
+ i++;
+ len-=4;
+ for(j = 0; j < len; j++)
+ {
+ sprintf((pbuf+strlen(pbuf))," 0x%02x", buf[j+i]);
+ }
+ sprintf((pbuf+strlen(pbuf)),"]");
+ i += j;
+ goto next;
+ break;
+ case 0x28:
+ sprintf((pbuf+strlen(pbuf)), "[display: ");
+ break;
+ case 0x2c:
+ sprintf((pbuf+strlen(pbuf)), "[keypad: ");
+ break;
+ case 0x6c:
+ sprintf((pbuf+strlen(pbuf)), "[origination address: ");
+ i += p_1tr6address(pbuf, &buf[i]);
+ goto next;
+ break;
+ case 0x70:
+ sprintf((pbuf+strlen(pbuf)), "[destination address: ");
+ i += p_1tr6address(pbuf, &buf[i]);
+ goto next;
+ break;
+ case 0x7e:
+ sprintf((pbuf+strlen(pbuf)), "[user-user information: ");
+ break;
+ case 0x7f:
+ sprintf((pbuf+strlen(pbuf)), "[reserved: ");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "[UNKNOWN INFO-ELEMENT-ID");
+ break;
+ }
+ }
+ else if(codeset == 6)
+ {
+ switch(buf[i])
+ {
+ case 0x01:
+ sprintf((pbuf+strlen(pbuf)), "[service ind: serv=");
+ i+= 2;
+ switch(buf[i])
+ {
+ case 0x01:
+ sprintf((pbuf+strlen(pbuf)), "phone");
+ break;
+ case 0x02:
+ sprintf((pbuf+strlen(pbuf)), "a/b");
+ break;
+ case 0x03:
+ sprintf((pbuf+strlen(pbuf)), "X.21");
+ break;
+ case 0x04:
+ sprintf((pbuf+strlen(pbuf)), "fax g4");
+ break;
+ case 0x05:
+ sprintf((pbuf+strlen(pbuf)), "btx");
+ break;
+ case 0x07:
+ sprintf((pbuf+strlen(pbuf)), "64k data");
+ break;
+ case 0x08:
+ sprintf((pbuf+strlen(pbuf)), "X.25");
+ break;
+ case 0x09:
+ sprintf((pbuf+strlen(pbuf)), "teletex");
+ break;
+ case 0x0a:
+ sprintf((pbuf+strlen(pbuf)), "mixed");
+ break;
+ case 0x0d:
+ sprintf((pbuf+strlen(pbuf)), "temex");
+ break;
+ case 0x0e:
+ sprintf((pbuf+strlen(pbuf)), "picturephone");
+ break;
+ case 0x0f:
+ sprintf((pbuf+strlen(pbuf)), "btx (new)");
+ break;
+ case 0x10:
+ sprintf((pbuf+strlen(pbuf)), "videophone");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "undefined");
+ break;
+ }
+ i++;
+ sprintf((pbuf+strlen(pbuf)), ", ainfo=0x%02x]", buf[i]);
+ i++;
+ goto next;
+ break;
+ case 0x02:
+ sprintf((pbuf+strlen(pbuf)), "[charging information: ");
+ break;
+ case 0x03:
+ sprintf((pbuf+strlen(pbuf)), "[date: ");
+ i++;
+ len = buf[i];
+ i++;
+ for(j = 0; j < len; j++)
+ {
+ sprintf((pbuf+strlen(pbuf)),"%c", buf[j+i]);
+ }
+ sprintf((pbuf+strlen(pbuf)),"]");
+ i += j;
+ goto next;
+ break;
+ case 0x05:
+ sprintf((pbuf+strlen(pbuf)), "[facility select: ");
+ break;
+ case 0x06:
+ sprintf((pbuf+strlen(pbuf)), "[status of facilities: ");
+ break;
+ case 0x07:
+ sprintf((pbuf+strlen(pbuf)), "[status of called party: ");
+ i+=2;
+ switch(buf[i])
+ {
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "no information]");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "is being called]");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "undefined (0x%02x)]", buf[i]);
+ break;
+ }
+ i++;
+ goto next;
+ break;
+ case 0x08:
+ sprintf((pbuf+strlen(pbuf)), "[additional tx attributes: ");
+ i++;
+ len = buf[i];
+ i++;
+ for(j = 0; j < len; j++)
+ {
+ switch(buf[j+i] &0x70)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "no satellite link");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "one satellite link");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "two satellite links");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "three satellite links");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "undefined value");
+ break;
+ }
+ if(buf[j+i] & 0x80)
+ sprintf((pbuf+strlen(pbuf)),"(flag=req)]");
+ else
+ sprintf((pbuf+strlen(pbuf)),"(flag=ind)]");
+ }
+ i += j;
+ goto next;
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "[UNKNOWN INFO-ELEMENT-ID");
+ break;
+ }
+ }
+ else
+ {
+ sprintf((pbuf+strlen(pbuf)), "[ILLEGAL CODESET = 0x%02x", codeset);
+ }
+
+ i++; /* index -> length */
+
+ len = buf[i];
+
+ i++; /* index -> 1st param */
+
+ for(j = 0; j < len; j++)
+ {
+ sprintf((pbuf+strlen(pbuf))," 0x%02x", buf[j+i]);
+ }
+
+ sprintf((pbuf+strlen(pbuf)),"]");
+
+ i += len;
+
+next:
+
+ if(!codelock && (codeset != oldcodeset))
+ codeset = oldcodeset;
+ }
+ }
+ sprintf((pbuf+strlen(pbuf)),"\n");
+}
+
+/*---------------------------------------------------------------------------*
+ * decode and print the cause
+ *---------------------------------------------------------------------------*/
+static int
+p_1tr6cause(char *pbuf, unsigned char buf[])
+{
+ int j;
+ int len;
+ int i = 0;
+
+ i++; /* index -> length */
+
+ len = buf[i];
+
+ switch(len)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "%s", print_cause_1tr6(0));
+ break;
+ case 1:
+ i++;
+ sprintf((pbuf+strlen(pbuf)), "%s", print_cause_1tr6(buf[i] & 0x7f));
+ break;
+ case 2:
+ i++;
+ sprintf((pbuf+strlen(pbuf)), "%s, location: ", print_cause_1tr6(buf[i] & 0x7f));
+ i++;
+ switch(buf[i] & 0x0f)
+ {
+ case 0x04:
+ sprintf((pbuf+strlen(pbuf)), "public network");
+ break;
+ case 0x05:
+ sprintf((pbuf+strlen(pbuf)), "private network");
+ break;
+ case 0x0f:
+ sprintf((pbuf+strlen(pbuf)), "no information");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "reserved (0x%02x)", buf[i] & 0x0f);
+ break;
+ }
+ break;
+ default:
+ i++; /* index -> length */
+ len = buf[i];
+ i++; /* index -> 1st param */
+ for(j = 0; j < len; j++)
+ {
+ sprintf((pbuf+strlen(pbuf))," 0x%02x", buf[j+i]);
+ }
+ break;
+ }
+ i++;
+ sprintf((pbuf+strlen(pbuf)),"]");
+ return(i);
+}
+
+/*---------------------------------------------------------------------------*
+ * decode and print the ISDN (telephone) number
+ *---------------------------------------------------------------------------*/
+static int
+p_1tr6address(char *pbuf, unsigned char buf[])
+{
+ int j;
+ int len;
+ int i = 0;
+ int tp;
+
+ i++; /* index -> length */
+ len = buf[i];
+ i++; /* index -> 1st param */
+ tp = buf[i];
+
+ i++;
+ len--;
+
+ for(j = 0; j < len; j++)
+ {
+ sprintf((pbuf+strlen(pbuf)),"%c", buf[j+i]);
+ }
+
+ switch((tp & 0x70) >> 4)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), " (type=unknown, ");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), " (type=international, ");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), " (type=national, ");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), " (type=%d, ", ((tp & 0x70) >> 4));
+ break;
+ }
+
+ switch(tp & 0x0f)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "plan=unknown)");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "plan=ISDN)");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "plan=%d)", (tp & 0x0f));
+ break;
+ }
+
+ sprintf((pbuf+strlen(pbuf)),"]");
+
+ i += j;
+
+ return(i);
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdntrace/Makefile b/usr.sbin/i4b/isdntrace/Makefile
new file mode 100644
index 0000000..eae0919
--- /dev/null
+++ b/usr.sbin/i4b/isdntrace/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PROG= isdntrace
+MAN= isdntrace.8
+SRCS= q921.c q931.c q931_util.c q932_fac.c 1tr6.c trace.c \
+ pcause_1tr6.c pcause_q850.c unknownl3.c
+
+.include <bsd.prog.mk>
+
diff --git a/usr.sbin/i4b/isdntrace/cable.txt b/usr.sbin/i4b/isdntrace/cable.txt
new file mode 100644
index 0000000..21a1cdb
--- /dev/null
+++ b/usr.sbin/i4b/isdntrace/cable.txt
@@ -0,0 +1,62 @@
+ *---------------------------------------------------------------------------
+ *
+ * Custom cable to trace an ISDN S0 bus with two passive (!) ISDN boards
+ * ---------------------------------------------------------------------
+ *
+ * $Id: cable.txt,v 1.4 1999/12/13 21:25:26 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:55:42 1999]
+ *
+ * -hm documentation of analyze mode
+ *
+ *---------------------------------------------------------------------------*/
+
+The cable consists of a RJ-45 plug with both tx and rx connected and
+two jacks; the tx cables from the plug are wired to one jack and the
+rx cables from the plug are wired to the other jack.
+
+The computer must be equipped with two (!) supported passive cards and
+the cable from one card is plugged into one of the jacks while the cable
+to from the other card is plugged into the other jack.
+
+Now one card monitors the tx part of the S0 bus and the other card
+monitors the rx part.
+
+Which card functions as the rx side and which as the tx side can be
+specified as options to the isdntrace utility (-R and -T) which has
+to be run in analyzer mode (-a) to support this configuration.
+
+
+ 1
+ 2
+ 3
+ +--------------4 receiving-side board
+ S0-bus +--|--------------5 in computer (jack for
+ to analyze | | 6 cable to passive controller)
+ 8 | | 7
+ 7 | | 8
+ transmit - 6------------|--|--+
+ receive - 5------------+ | |
+ receive + 4---------------+ |
+ transmit + 3------------+ |
+ 2 | | 1
+ 1 | | 2
+ plug into | | 3
+ S0 bus +-----|-----------4 transmitting-side board
+ +-----------5 in computer (jack for
+ 6 cable to passive controller)
+ 7
+ 8
+
+
+
+ RJ-45 plug RJ-45 jack
+ view from the front view from the front
+ cable goes out to the rear
+
+ /--------- / ----------
+ | 87654321 | | 12345678 |
+ |__ __|/ |/_ /_|
+ |____|/ |/___|
diff --git a/usr.sbin/i4b/isdntrace/isdntrace.8 b/usr.sbin/i4b/isdntrace/isdntrace.8
new file mode 100644
index 0000000..4017dd1
--- /dev/null
+++ b/usr.sbin/i4b/isdntrace/isdntrace.8
@@ -0,0 +1,219 @@
+.\"
+.\" Copyright (c) 1997, 2000 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: isdntrace.8,v 1.14 2000/02/13 15:26:52 hm Exp $
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Wed Nov 1 15:52:28 2000]
+.\"
+.Dd November 1, 2000
+.Dt ISDNTRACE 8
+.Os
+.Sh NAME
+.Nm isdntrace
+.Nd isdn4bsd ISDN protocol trace utility
+.Sh SYNOPSIS
+.Nm
+.Op Fl a
+.Op Fl b
+.Op Fl d
+.Op Fl f Ar filename
+.Op Fl h
+.Op Fl i
+.Op Fl l
+.Op Fl n Ar number
+.Op Fl o
+.Op Fl p Ar filename
+.Op Fl r
+.Op Fl u Ar number
+.Op Fl x
+.Op Fl B
+.Op Fl F
+.Op Fl P
+.Op Fl R Ar unit
+.Op Fl T Ar unit
+.Sh DESCRIPTION
+.Nm
+is part of the isdn4bsd package and is used to provide the user with a
+mnemonic display of the layers 1, 2 and 3 protocol activities on
+the D channel and hex dump of the B channel(s) activities.
+.Pp
+Together with two passive supported cards and an easy to build cable it can
+also be used to monitor the complete traffic on a S0 bus providing S0 bus
+analyzer features.
+.Pp
+The
+.Nm
+utility is only available for passive supported cards.
+.Pp
+.Em Note
+.Pp
+All filenames, user specified or default, get a date and time stamp string
+added in the form -yyyymmdd-hhmmss: a hyphen, four digits year, two digits
+month and day, a hyphen and two digits hour, minutes and seconds.
+Tracefiles no longer get overwritten.
+In case a new filename is needed within a second, the filename-generating
+mechanism sleeps one second.
+.Pp
+In case the program is sent a USR1 signal, a new user specified or default
+filename with a new date and timestamp is generated and opened.
+.Pp
+The following options can be used:
+.Bl -tag -width Ds
+.It Fl a
+Run
+.Nm
+in analyzer mode by using two passive cards and a custom cable which can
+be build as described in the file
+.Em cable.txt
+in the isdn4bsd source distribution. One card acts as a receiver for the
+transmitting direction on the S0 bus while the other card acts as a receiver
+for the receiving direction on the S0 bus. Complete traffic monitoring is
+possible using this setup.
+.It Fl b
+switch B channel tracing on (default off).
+.It Fl d
+switch D channel tracing off (default on).
+.It Fl f
+Use
+.Ar filename
+as the name of a file into which to write tracing output (default filename is
+isdntrace<n> where n is the number of the unit to trace).
+.It Fl h
+switch display of header off (default on).
+.It Fl i
+print layer 1 (I.430) INFO signals to monitor layer 1 activity (default off).
+.It Fl l
+switch displaying of Layer 2 (Q.921) frames off (default on).
+.It Fl n
+This option takes a numeric argument specifying the minimum
+frame size in 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 x
+Switch on printing of packets with a non-Q.931 protocol discriminator.
+(default off).
+.It Fl B
+Write undecoded binary trace data to a file for later or remote
+analyzing (default off).
+.It Fl F
+This option can only be used when option -P (playback from binary data file)
+is used. The -F option causes playback not to stop at end of file but rather
+to wait for additional data to be available from the input file.
+.Pp
+This option is useful when trace data is accumulated in binary format (to
+save disk space) but a monitoring functionality is desired.
+(default off).
+.It Fl P
+Read undecoded binary trace data from file instead from device (default off).
+.It Fl R
+Use
+.Ar unit
+as the receiving interface unit number in analyze mode.
+.It Fl T
+Use
+.Ar unit
+as the transmitting interface unit number in analyze mode.
+.El
+.Pp
+When the USR1 signal is sent to a
+.Nm
+process, the currently used logfiles are reopened, so that logfile
+rotation becomes possible.
+.Pp
+The trace output should be obvious. It is very handy to have the following
+standard texts available when tracing ISDN protocols:
+.Pp
+.Bl -tag -width Ds -compact -offset indent
+.It Ar I.430
+ISDN BRI layer 1 protocol description.
+.It Ar Q.921
+ISDN D-channel layer 2 protocol description.
+.It Ar Q.931
+ISDN D-channel layer 3 protocol description.
+.It Ar 1TR6
+German-specific ISDN layer 3 protocol description. (NOTICE: decoding
+of the 1TR6 protocol is included but not supported since i dont have
+any longer access to a 1TR6 based ISDN installation.)
+.El
+.Pp
+.Nm Isdntrace
+automatically detects the layer 3 protocol being used by looking at the
+Protocol Discriminator (see: Q.931/1993 pp. 53).
+.Sh FILES
+.Bl -tag -width daddeldi -compact
+.It Pa /dev/i4btrc<n>
+The devicefile(s) used to get the trace messages for ISDN card unit <n>
+out of the kernel.
+.El
+.Sh EXAMPLES
+The command:
+.Bd -literal -offset indent
+isdntrace -f /var/tmp/isdn.trace
+.Ed
+.Pp
+will start D channel tracing on passive controller 0 with all except B
+channel tracing enabled and logs everything into the output file
+/var/tmp/isdn.trace-yyyymmdd-hhmmss (where yyyymmdd and hhmmss are replaced
+by the current date and time values).
+.Sh SEE ALSO
+.Xr isdnd 8
+.Sh BUGS
+Still some or more left.
+.Sh STANDARDS
+ITU Recommendations I.430, Q.920, Q.921, Q.930, Q.931
+.Pp
+FTZ Richtlinie 1TR3, Band III
+.Pp
+ITU Recommendation Q.932 (03/93), Q.950 (03/93)
+.Pp
+ETSI Recommendation ETS 300 179 (10/92), ETS 300 180 (10/92)
+.Pp
+ETSI Recommendation ETS 300 181 (04/93), ETS 300 182 (04/93)
+.Pp
+ITU Recommendation X.208, X.209
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+utility was written by
+.An Gary Jennejohn Aq gj@FreeBSD.org
+and
+.An Hellmuth Michaelis Aq hm@FreeBSD.org .
+.Pp
+This manual page was written by
+.An Hellmuth Michaelis .
diff --git a/usr.sbin/i4b/isdntrace/pcause_1tr6.c b/usr.sbin/i4b/isdntrace/pcause_1tr6.c
new file mode 100644
index 0000000..b319d74
--- /dev/null
+++ b/usr.sbin/i4b/isdntrace/pcause_1tr6.c
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * printing cause values
+ * ---------------------
+ *
+ * $Id: pcause_1tr6.c,v 1.6 1999/12/13 21:25:26 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:56:03 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "trace.h"
+#include "pcause_1tr6.h"
+
+char *
+print_cause_1tr6(unsigned char code)
+{
+ static char error_message[120];
+ char *e;
+
+ switch(code)
+ {
+ case CAUSE_1TR6_SHUTDN:
+ e = "normal D-channel shutdown";
+ break;
+
+ case CAUSE_1TR6_ICRV:
+ e = "invalid call reference value";
+ break;
+
+ case CAUSE_1TR6_BSNI:
+ e = "bearer service not implemented";
+ break;
+
+ case CAUSE_1TR6_CIDNE:
+ e = "call identity does not exist";
+ break;
+
+ case CAUSE_1TR6_CIIU:
+ e = "call identity in use";
+ break;
+
+ case CAUSE_1TR6_NCA:
+ e = "no channel available";
+ break;
+
+ case CAUSE_1TR6_RFNI:
+ e = "requested facility not implemented";
+ break;
+
+ case CAUSE_1TR6_RFNS:
+ e = "requested facility not subscribed";
+ break;
+
+ case CAUSE_1TR6_OCB:
+ e = "outgoing calls barred";
+ break;
+
+ case CAUSE_1TR6_UAB:
+ e = "user access busy";
+ break;
+
+ case CAUSE_1TR6_NECUG:
+ e = "non existent CUG";
+ break;
+
+ case CAUSE_1TR6_NECUG1:
+ e = "non existent CUG";
+ break;
+
+ case CAUSE_1TR6_SPV:
+ e = "kommunikationsbeziehung als SPV nicht erlaubt";
+ break;
+
+ case CAUSE_1TR6_DNO:
+ e = "destination not obtainable";
+ break;
+
+ case CAUSE_1TR6_NC:
+ e = "number changed";
+ break;
+
+ case CAUSE_1TR6_OOO:
+ e = "out of order";
+ break;
+
+ case CAUSE_1TR6_NUR:
+ e = "no user responding";
+ break;
+
+ case CAUSE_1TR6_UB:
+ e = "user busy";
+ break;
+
+ case CAUSE_1TR6_ICB:
+ e = "incoming calls barred";
+ break;
+
+ case CAUSE_1TR6_CR:
+ e = "call rejected";
+ break;
+
+ case CAUSE_1TR6_NCO:
+ e = "network congestion";
+ break;
+
+ case CAUSE_1TR6_RUI:
+ e = "remote user initiated";
+ break;
+
+ case CAUSE_1TR6_LPE:
+ e = "local procedure error";
+ break;
+
+ case CAUSE_1TR6_RPE:
+ e = "remote procedure error";
+ break;
+
+ case CAUSE_1TR6_RUS:
+ e = "remote user suspended";
+ break;
+
+ case CAUSE_1TR6_RUR:
+ e = "remote user resumed";
+ break;
+
+ case CAUSE_1TR6_UIDL:
+ e = "user info discharded locally";
+ break;
+
+ default:
+ e = "UNKNOWN error occured";
+ break;
+ }
+
+ sprintf(error_message, "0x%02x: %s", code & 0x7f, e);
+ return(error_message);
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdntrace/pcause_1tr6.h b/usr.sbin/i4b/isdntrace/pcause_1tr6.h
new file mode 100644
index 0000000..67f50de
--- /dev/null
+++ b/usr.sbin/i4b/isdntrace/pcause_1tr6.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * pcause1tr6.h - 1TR6 causes definitions
+ * --------------------------------------
+ *
+ * $Id: pcause_1tr6.h,v 1.5 1999/12/13 21:25:26 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:56:10 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+char *print_cause_1tr6(unsigned char code);
+
+/* 1TR6 protocol causes */
+
+#define CAUSE_1TR6_SHUTDN 0x00 /* normal D-channel shutdown */
+#define CAUSE_1TR6_ICRV 0x01 /* invalid call reference value */
+#define CAUSE_1TR6_BSNI 0x03 /* bearer service not implemented */
+#define CAUSE_1TR6_CIDNE 0x07 /* call identity does not exist */
+#define CAUSE_1TR6_CIIU 0x08 /* call identity in use */
+#define CAUSE_1TR6_NCA 0x0A /* no channel available */
+#define CAUSE_1TR6_RFNI 0x10 /* requested facility not implemented */
+#define CAUSE_1TR6_RFNS 0x11 /* requested facility not subscribed */
+#define CAUSE_1TR6_OCB 0x20 /* outgoing calls barred */
+#define CAUSE_1TR6_UAB 0x21 /* user access busy */
+#define CAUSE_1TR6_NECUG 0x22 /* non existent CUG */
+#define CAUSE_1TR6_NECUG1 0x23 /* non existent CUG */
+#define CAUSE_1TR6_SPV 0x25 /* kommunikationsbeziehung als SPV nicht erlaubt */
+#define CAUSE_1TR6_DNO 0x35 /* destination not obtainable */
+#define CAUSE_1TR6_NC 0x38 /* number changed */
+#define CAUSE_1TR6_OOO 0x39 /* out of order */
+#define CAUSE_1TR6_NUR 0x3A /* no user responding */
+#define CAUSE_1TR6_UB 0x3B /* user busy */
+#define CAUSE_1TR6_ICB 0x3D /* incoming calls barred */
+#define CAUSE_1TR6_CR 0x3E /* call rejected */
+#define CAUSE_1TR6_NCO 0x59 /* network congestion */
+#define CAUSE_1TR6_RUI 0x5A /* remote user initiated */
+#define CAUSE_1TR6_LPE 0x70 /* local procedure error */
+#define CAUSE_1TR6_RPE 0x71 /* remote procedure error */
+#define CAUSE_1TR6_RUS 0x72 /* remote user suspended */
+#define CAUSE_1TR6_RUR 0x73 /* remote user resumed */
+#define CAUSE_1TR6_UIDL 0x7F /* user info discharded locally */
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdntrace/pcause_q850.c b/usr.sbin/i4b/isdntrace/pcause_q850.c
new file mode 100644
index 0000000..57eaf32
--- /dev/null
+++ b/usr.sbin/i4b/isdntrace/pcause_q850.c
@@ -0,0 +1,330 @@
+/*
+ * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * printing cause values
+ * ---------------------
+ *
+ * $Id: pcause_q850.c,v 1.6 1999/12/13 21:25:26 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:56:18 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "trace.h"
+#include "pcause_q850.h"
+
+char *
+print_cause_q850(unsigned char code)
+{
+ static char error_message[120];
+ char *e;
+
+ switch(code)
+ {
+ case CAUSE_Q850_SHUTDN:
+ e = "normal D-channel shutdown";
+ break;
+
+ case CAUSE_Q850_NUNALLC:
+ e = "Unallocated (unassigned) number";
+ break;
+
+ case CAUSE_Q850_NRTTN:
+ e = "No route to specified transit network";
+ break;
+
+ case CAUSE_Q850_NRTDST:
+ e = "No route to destination";
+ break;
+
+ case CAUSE_Q850_SSINFTN:
+ e = "Send special information tone";
+ break;
+
+ case CAUSE_Q850_MDIALTP:
+ e = "Misdialled trunk prefix";
+ break;
+
+ case CAUSE_Q850_CHUNACC:
+ e = "Channel unacceptable";
+ break;
+
+ case CAUSE_Q850_CALLAWD:
+ e = "Call awarded and being delivered in an established channel";
+ break;
+
+ case CAUSE_Q850_PREEMPT:
+ e = "Preemption";
+ break;
+
+ case CAUSE_Q850_PREECRR:
+ e = "Preemption - circuit reserved for reuse";
+ break;
+
+ case CAUSE_Q850_NCCLR:
+ e = "Normal call clearing";
+ break;
+
+ case CAUSE_Q850_USRBSY:
+ e = "User busy";
+ break;
+
+ case CAUSE_Q850_NOUSRRSP:
+ e = "No user responding";
+ break;
+
+ case CAUSE_Q850_NOANSWR:
+ e = "No answer from user (user alerted)";
+ break;
+
+ case CAUSE_Q850_SUBSABS:
+ e = "Subscriber absent";
+ break;
+
+ case CAUSE_Q850_CALLREJ:
+ e = "Call rejected";
+ break;
+
+ case CAUSE_Q850_NUCHNG:
+ e = "Number changed";
+ break;
+
+ case CAUSE_Q850_NONSELUC:
+ e = "Non-selected user clearing";
+ break;
+
+ case CAUSE_Q850_DSTOOORDR:
+ e = "Destination out of order";
+ break;
+
+ case CAUSE_Q850_INVNUFMT:
+ e = "Invalid number format";
+ break;
+
+ case CAUSE_Q850_FACREJ:
+ e = "Facility rejected";
+ break;
+
+ case CAUSE_Q850_STENQRSP:
+ e = "Response to STATUS ENQUIRY";
+ break;
+
+ case CAUSE_Q850_NORMUNSP:
+ e = "Normal, unspecified";
+ break;
+
+ case CAUSE_Q850_NOCAVAIL:
+ e = "No circuit / channel available";
+ break;
+
+ case CAUSE_Q850_NETOOORDR:
+ e = "Network out of order";
+ break;
+
+ case CAUSE_Q850_PFMCDOOSERV:
+ e = "Permanent frame mode connection out of service";
+ break;
+
+ case CAUSE_Q850_PFMCOPER:
+ e = "Permanent frame mode connection operational";
+ break;
+
+ case CAUSE_Q850_TMPFAIL:
+ e = "Temporary failure";
+ break;
+
+ case CAUSE_Q850_SWEQCONG:
+ e = "Switching equipment congestion";
+ break;
+
+ case CAUSE_Q850_ACCINFDIS:
+ e = "Access information discarded";
+ break;
+
+ case CAUSE_Q850_REQCNOTAV:
+ e = "Requested circuit/channel not available";
+ break;
+
+ case CAUSE_Q850_PRECALBLK:
+ e = "Precedence call blocked";
+ break;
+
+ case CAUSE_Q850_RESUNAVAIL:
+ e = "Resources unavailable, unspecified";
+ break;
+
+ case CAUSE_Q850_QOSUNAVAIL:
+ e = "Quality of service unavailable";
+ break;
+
+ case CAUSE_Q850_REQSERVNS:
+ e = "Requested facility not subscribed";
+ break;
+
+ case CAUSE_Q850_OCBARRCUG:
+ e = "Outgoing calls barred within CUG";
+ break;
+
+ case CAUSE_Q850_ICBARRCUG:
+ e = "Incoming calls barred within CUG";
+ break;
+
+ case CAUSE_Q850_BCAPNAUTH:
+ e = "Bearer capability not authorized";
+ break;
+
+ case CAUSE_Q850_BCAPNAVAIL:
+ e = "Bearer capability not presently available";
+ break;
+
+ case CAUSE_Q850_INCSTOACISC:
+ e = "Inconsistenciy in designated outg. access info and subscriber class";
+ break;
+
+ case CAUSE_Q850_SOONOTAVAIL:
+ e = "Service or option not available, unspecified";
+ break;
+
+ case CAUSE_Q850_BCAPNOTIMPL:
+ e = "Bearer capability not implemented";
+ break;
+
+ case CAUSE_Q850_CHTYPNIMPL:
+ e = "Channel type not implemented";
+ break;
+
+ case CAUSE_Q850_REQFACNIMPL:
+ e = "Requested facility not implemented";
+ break;
+
+ case CAUSE_Q850_ORDINBCAVL:
+ e = "Only restricted digital information bearer capability is available";
+ break;
+
+ case CAUSE_Q850_SOONOTIMPL:
+ e = "Service or option not implemented, unspecified";
+ break;
+
+ case CAUSE_Q850_INVCLRFVAL:
+ e = "Invalid call reference value";
+ break;
+
+ case CAUSE_Q850_IDCHDNOEX:
+ e = "Identified channel does not exist";
+ break;
+
+ case CAUSE_Q850_SUSCAEXIN:
+ e = "A suspended call exists, but this call identity does not";
+ break;
+
+ case CAUSE_Q850_CLIDINUSE:
+ e = "Call identity in use";
+ break;
+
+ case CAUSE_Q850_NOCLSUSP:
+ e = "No call suspended";
+ break;
+
+ case CAUSE_Q850_CLIDCLRD:
+ e = "Call having the requested call identity has been cleared";
+ break;
+
+ case CAUSE_Q850_UNOTMEMCUG:
+ e = "User not member of CUG";
+ break;
+
+ case CAUSE_Q850_INCDEST:
+ e = "Incompatible destination";
+ break;
+
+ case CAUSE_Q850_NONEXCUG:
+ e = "Non-existent CUG";
+ break;
+
+ case CAUSE_Q850_INVNTWSEL:
+ e = "Invalid transit network selection";
+ break;
+
+ case CAUSE_Q850_INVMSG:
+ e = "Invalid message, unspecified";
+ break;
+
+ case CAUSE_Q850_MIEMISS:
+ e = "Mandatory information element is missing";
+ break;
+
+ case CAUSE_Q850_MSGTNI:
+ e = "Message type non-existent or not implemented";
+ break;
+
+ case CAUSE_Q850_MSGNCMPT:
+ e = "Msg incompatible with call state/message type non-existent/not implemented";
+ break;
+
+ case CAUSE_Q850_IENENI:
+ e = "Information element/parameter non-existent or not implemented";
+ break;
+
+ case CAUSE_Q850_INVIEC:
+ e = "Invalid information element contents";
+ break;
+
+ case CAUSE_Q850_MSGNCWCS:
+ e = "Message not compatible with call state";
+ break;
+
+ case CAUSE_Q850_RECOTIMEXP:
+ e = "Recovery on timer expiry";
+ break;
+
+ case CAUSE_Q850_PARMNENIPO:
+ e = "Parameter non-existent or not implemented, passed on";
+ break;
+
+ case CAUSE_Q850_MSGUNRDPRM:
+ e = "Message with unrecognized parameter, discarded";
+ break;
+
+ case CAUSE_Q850_PROTERR:
+ e = "Protocol error, unspecified";
+ break;
+
+ case CAUSE_Q850_INTWRKU:
+ e = "Interworking, unspecified";
+ break;
+
+ default:
+ e = "ERROR, unknown cause value!";
+ break;
+ }
+
+ sprintf(error_message, "%d: %s (Q.850)", code, e);
+ return(error_message);
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdntrace/pcause_q850.h b/usr.sbin/i4b/isdntrace/pcause_q850.h
new file mode 100644
index 0000000..ea21770
--- /dev/null
+++ b/usr.sbin/i4b/isdntrace/pcause_q850.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * pcauseq850.h - Q.850 causes definitions
+ * ---------------------------------------
+ *
+ * $Id: pcause_q850.h,v 1.5 1999/12/13 21:25:26 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:56:25 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+char *print_cause_q850(unsigned char code);
+
+/* Q.850 causes */
+
+#define CAUSE_Q850_SHUTDN 0x00 /* normal D-channel shutdown */
+#define CAUSE_Q850_NUNALLC 0x01 /* Unallocated (unassigned) number */
+#define CAUSE_Q850_NRTTN 0x02 /* No route to specified transit network */
+#define CAUSE_Q850_NRTDST 0x03 /* No route to destination */
+#define CAUSE_Q850_SSINFTN 0x04 /* Send special information tone */
+#define CAUSE_Q850_MDIALTP 0x05 /* Misdialled trunk prefix */
+#define CAUSE_Q850_CHUNACC 0x06 /* Channel unacceptable */
+#define CAUSE_Q850_CALLAWD 0x07 /* Call awarded and being delivered in an established channel */
+#define CAUSE_Q850_PREEMPT 0x08 /* Preemption */
+#define CAUSE_Q850_PREECRR 0x09 /* Preemption - circuit reserved for reuse */
+#define CAUSE_Q850_NCCLR 0x10 /* Normal call clearing */
+#define CAUSE_Q850_USRBSY 0x11 /* User busy */
+#define CAUSE_Q850_NOUSRRSP 0x12 /* No user responding */
+#define CAUSE_Q850_NOANSWR 0x13 /* No answer from user (user alerted) */
+#define CAUSE_Q850_SUBSABS 0x14 /* Subscriber absent */
+#define CAUSE_Q850_CALLREJ 0x15 /* Call rejected */
+#define CAUSE_Q850_NUCHNG 0x16 /* Number changed */
+#define CAUSE_Q850_NONSELUC 0x1A /* Non-selected user clearing */
+#define CAUSE_Q850_DSTOOORDR 0x1B /* Destination out of order */
+#define CAUSE_Q850_INVNUFMT 0x1C /* Invalid number format */
+#define CAUSE_Q850_FACREJ 0x1D /* Facility rejected */
+#define CAUSE_Q850_STENQRSP 0x1E /* Response to STATUS ENQUIRY */
+#define CAUSE_Q850_NORMUNSP 0x1F /* Normal, unspecified */
+#define CAUSE_Q850_NOCAVAIL 0x22 /* No circuit / channel available */
+#define CAUSE_Q850_NETOOORDR 0x26 /* Network out of order */
+#define CAUSE_Q850_PFMCDOOSERV 0x27 /* Permanent frame mode connection out of service */
+#define CAUSE_Q850_PFMCOPER 0x28 /* Permanent frame mode connection operational */
+#define CAUSE_Q850_TMPFAIL 0x29 /* Temporary failure */
+#define CAUSE_Q850_SWEQCONG 0x2A /* Switching equipment congestion */
+#define CAUSE_Q850_ACCINFDIS 0x2B /* Access information discarded */
+#define CAUSE_Q850_REQCNOTAV 0x2C /* Requested circuit/channel not available */
+#define CAUSE_Q850_PRECALBLK 0x2E /* Precedence call blocked */
+#define CAUSE_Q850_RESUNAVAIL 0x2F /* Resources unavailable, unspecified */
+#define CAUSE_Q850_QOSUNAVAIL 0x31 /* Quality of service unavailable */
+#define CAUSE_Q850_REQSERVNS 0x32 /* Requested facility not subscribed */
+#define CAUSE_Q850_OCBARRCUG 0x35 /* Outgoing calls barred within CUG */
+#define CAUSE_Q850_ICBARRCUG 0x36 /* Incoming calls barred within CUG */
+#define CAUSE_Q850_BCAPNAUTH 0x39 /* Bearer capability not authorized */
+#define CAUSE_Q850_BCAPNAVAIL 0x3A /* Bearer capability not presently available */
+#define CAUSE_Q850_INCSTOACISC 0x3E /* Inconsistenciy in designated outgoing access information and subscriber class */
+#define CAUSE_Q850_SOONOTAVAIL 0x3F /* Service or option not available, unspecified */
+#define CAUSE_Q850_BCAPNOTIMPL 0x41 /* Bearer capability not implemented */
+#define CAUSE_Q850_CHTYPNIMPL 0x42 /* Channel type not implemented */
+#define CAUSE_Q850_REQFACNIMPL 0x45 /* Requested facility not implemented */
+#define CAUSE_Q850_ORDINBCAVL 0x46 /* Only restricted digital information bearer capability is available */
+#define CAUSE_Q850_SOONOTIMPL 0x4F /* Service or option not implemented, unspecified */
+#define CAUSE_Q850_INVCLRFVAL 0x51 /* Invalid call reference value */
+#define CAUSE_Q850_IDCHDNOEX 0x52 /* Identified channel does not exist */
+#define CAUSE_Q850_SUSCAEXIN 0x53 /* A suspended call exists, but this call identity does not */
+#define CAUSE_Q850_CLIDINUSE 0x54 /* Call identity in use */
+#define CAUSE_Q850_NOCLSUSP 0x55 /* No call suspended */
+#define CAUSE_Q850_CLIDCLRD 0x56 /* Call having the requested call identity has been cleared */
+#define CAUSE_Q850_UNOTMEMCUG 0x57 /* User not member of CUG */
+#define CAUSE_Q850_INCDEST 0x58 /* Incompatible destination */
+#define CAUSE_Q850_NONEXCUG 0x5A /* Non-existent CUG */
+#define CAUSE_Q850_INVNTWSEL 0x5B /* Invalid transit network selection */
+#define CAUSE_Q850_INVMSG 0x5F /* Invalid message, unspecified */
+#define CAUSE_Q850_MIEMISS 0x60 /* Mandatory information element is missing */
+#define CAUSE_Q850_MSGTNI 0x61 /* Message type non-existent or not implemented */
+#define CAUSE_Q850_MSGNCMPT 0x62 /* Message not compatible with call state or message type non-existent or not implemented */
+#define CAUSE_Q850_IENENI 0x63 /* Information element/parameter non-existent or not implemented */
+#define CAUSE_Q850_INVIEC 0x64 /* Invalid information element contents */
+#define CAUSE_Q850_MSGNCWCS 0x65 /* Message not compatible with call state */
+#define CAUSE_Q850_RECOTIMEXP 0x66 /* Recovery on timer expiry */
+#define CAUSE_Q850_PARMNENIPO 0x67 /* Parameter non-existent or not implemented, passed on */
+#define CAUSE_Q850_MSGUNRDPRM 0x6E /* Message with unrecognized parameter, discarded */
+#define CAUSE_Q850_PROTERR 0x6F /* Protocol error, unspecified */
+#define CAUSE_Q850_INTWRKU 0x7F /* Interworking, unspecified */
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdntrace/q921.c b/usr.sbin/i4b/isdntrace/q921.c
new file mode 100644
index 0000000..e8c14dc
--- /dev/null
+++ b/usr.sbin/i4b/isdntrace/q921.c
@@ -0,0 +1,268 @@
+/*
+ * Copyright (c) 1996 Gary Jennejohn. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------*
+ *
+ * q.921.c - print Q.921 traces
+ * ----------------------------
+ *
+ * $Id: q921.c,v 1.4 1999/12/13 21:25:26 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:56:46 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "trace.h"
+
+/*---------------------------------------------------------------------------*
+ * decode LAPD (Q.921) protocol
+ *---------------------------------------------------------------------------*/
+int
+decode_lapd(char *pbuf, int n, unsigned char *buf, int dir, int raw, int printit)
+{
+ int sap, tei, cmd, p_f;
+ int cnt = 0;
+ int i;
+ char locbuf[32000];
+ char *lbufp = &locbuf[0];
+
+ *lbufp = '\0';
+ *pbuf = '\0';
+
+ sap = (buf[0] >> 2) & 0x3f;
+ cnt++;
+
+ tei = buf[1] >> 1;
+ cnt++;
+
+ if(dir == FROM_TE)
+ cmd = !(buf[0] & 2);
+ else
+ cmd = buf[0] & 2;
+
+ switch (sap)
+ {
+ /* SAPI control procedures */
+ case 0:
+ {
+ if(printit)
+ sprintf((lbufp+strlen(lbufp)), "Q921: SAP=%d (Call Control), %c, TEI=%d, ", sap, cmd?'C':'R', tei);
+
+ if((buf[2] & 0x01) == 0)
+ {
+ if(printit)
+ sprintf((lbufp+strlen(lbufp)), "I-Frame: ");
+
+ p_f = buf [3] & 1;
+
+ if(printit)
+ sprintf((lbufp+strlen(lbufp)), "N(S) %d N(R) %d P %d ", buf [2] >> 1, buf [3] >> 1, p_f);
+
+ cnt += 2;
+ }
+ else if((buf[2] & 0x03) == 0x01)
+ {
+ if(printit)
+ sprintf((lbufp+strlen(lbufp)), "S-Frame: ");
+
+ p_f = buf [3] & 1;
+ cmd = buf [2] & 0x0c;
+
+ if(printit)
+ {
+ if (cmd == 0)
+ sprintf((lbufp+strlen(lbufp)), "RR N(R) %d PF %d ", buf [3] >> 1, p_f);
+ if (cmd == 4)
+ sprintf((lbufp+strlen(lbufp)), "RNR N(R) %d PF %d ", buf [3] >> 1, p_f);
+ if (cmd == 8)
+ sprintf((lbufp+strlen(lbufp)), "REJ N(R) %d PF %d ", buf [3] >> 1, p_f);
+ }
+ cnt += 2;
+ }
+ else if((buf[2] & 0x03) == 0x03)
+ {
+ if(printit)
+ sprintf((lbufp+strlen(lbufp)), "U-Frame: ");
+
+ p_f = (buf [2] & 0x10) >> 4;
+ cmd = buf [2] & 0xec;
+
+ if(printit)
+ {
+ if (cmd == 0x6c)
+ sprintf((lbufp+strlen(lbufp)), "SABME PF %d ", p_f);
+ if (cmd == 0x0c)
+ sprintf((lbufp+strlen(lbufp)), "DM PF %d ", p_f);
+ if (cmd == 0)
+ sprintf((lbufp+strlen(lbufp)), "UI PF %d ", p_f);
+ if (cmd == 0x40)
+ sprintf((lbufp+strlen(lbufp)), "DISC PF %d ", p_f);
+ if (cmd == 0x60)
+ sprintf((lbufp+strlen(lbufp)), "UA PF %d ", p_f);
+ if (cmd == 0x84)
+ sprintf((lbufp+strlen(lbufp)), "FRMR PF %d ", p_f);
+ if (cmd == 0xac)
+ sprintf((lbufp+strlen(lbufp)), "XID PF %d ", p_f);
+ /* information field ??? */
+ }
+ cnt++;
+ }
+ break;
+ }
+
+ /* D channel X.25 */
+
+ case 16:
+ if(printit)
+ sprintf((lbufp+strlen(lbufp)), "Q921: SAP=%d (X.25), %c, TEI=%d, ", sap, cmd?'C':'R', tei);
+ cnt = n;
+ goto dump;
+
+ /* Loopback test */
+
+ case 32:
+ if(printit)
+ sprintf((lbufp+strlen(lbufp)), "Q921: SAP=%d (Loopbacktest), %c, TEI=%d, ", sap, cmd?'C':'R', tei);
+ cnt = n;
+ goto dump;
+
+ /* SAPI layer 2 management functions */
+
+ case 63:
+ {
+ if(printit)
+ sprintf((lbufp+strlen(lbufp)), "Q921: SAP=%d (TEI-Management), %c, TEI=%d, ", sap, cmd?'C':'R', tei);
+
+ if (tei != 127)
+ {
+ if(printit)
+ sprintf((lbufp+strlen(lbufp)), "ILLEGAL TEI\n");
+ cnt = n;
+ goto dump;
+ }
+
+ if (buf [2] != 3 && buf [3] != 0xf)
+ {
+ if(printit)
+ sprintf((lbufp+strlen(lbufp)), "invalid format!\n");
+ cnt = n;
+ goto dump;
+ }
+ cnt+= 2; /* UI + MEI */
+
+ if(printit)
+ sprintf((lbufp+strlen(lbufp)), "Ri=0x%04hx, ", *(short *)&buf[4]);
+ cnt += 2; /* Ri */
+
+ switch (buf[6])
+ {
+ case 1:
+ if(printit)
+ sprintf((lbufp+strlen(lbufp)), "IdRequest, Ai=%d", (buf [7] >> 1));
+ cnt += 2;
+ break;
+ case 2:
+ if(printit)
+ sprintf((lbufp+strlen(lbufp)), "IdAssign, Ai=%d", (buf [7] >> 1));
+ cnt += 2;
+ break;
+ case 3:
+ if(printit)
+ sprintf((lbufp+strlen(lbufp)), "IdDenied, Ai=%d", (buf [7] >> 1));
+ cnt += 2;
+ break;
+ case 4:
+ if(printit)
+ sprintf((lbufp+strlen(lbufp)), "IdCheckReq, Ai=%d", (buf [7] >> 1));
+ cnt += 2;
+ break;
+ case 5:
+ if(printit)
+ sprintf((lbufp+strlen(lbufp)), "IdCheckResp, Ai=%d", (buf [7] >> 1));
+ cnt += 2;
+ break;
+ case 6:
+ if(printit)
+ sprintf((lbufp+strlen(lbufp)), "IdRemove, Ai=%d", (buf [7] >> 1));
+ cnt += 2;
+ break;
+ case 7:
+ if(printit)
+ sprintf((lbufp+strlen(lbufp)), "IdVerify, Ai=%d", (buf [7] >> 1));
+ cnt += 2;
+ break;
+ default:
+ if(printit)
+ sprintf((lbufp+strlen(lbufp)), "Unknown Msg Type\n");
+ cnt = n;
+ goto dump;
+ }
+ break;
+ }
+
+ /* Illegal SAPI */
+
+ default:
+ if(printit)
+ sprintf((lbufp+strlen(lbufp)), "Q921: ERROR, SAP=%d (Illegal SAPI), %c, TEI=%d\n", sap, cmd?'C':'R', tei);
+ cnt = n;
+ goto dump;
+ }
+
+dump:
+ if(printit)
+ sprintf((lbufp+strlen(lbufp)), "\n");
+
+ if(raw && printit)
+ {
+ int j;
+ for (i = 0; i < cnt; i += 16)
+ {
+ sprintf((pbuf+strlen(pbuf)),"Dump:%.3d ", i);
+ for (j = 0; j < 16; j++)
+ if (i + j < cnt)
+ sprintf((pbuf+strlen(pbuf)),"%02x ", buf[i + j]);
+ else
+ sprintf((pbuf+strlen(pbuf))," ");
+ sprintf((pbuf+strlen(pbuf))," ");
+ for (j = 0; j < 16 && i + j < cnt; j++)
+ if (isprint(buf[i + j]))
+ sprintf((pbuf+strlen(pbuf)),"%c", buf[i + j]);
+ else
+ sprintf((pbuf+strlen(pbuf)),".");
+ sprintf((pbuf+strlen(pbuf)),"\n");
+ }
+ }
+
+ sprintf((pbuf+strlen(pbuf)),"%s", &locbuf[0]);
+
+ return (cnt);
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdntrace/q931.c b/usr.sbin/i4b/isdntrace/q931.c
new file mode 100644
index 0000000..d69453e
--- /dev/null
+++ b/usr.sbin/i4b/isdntrace/q931.c
@@ -0,0 +1,800 @@
+/*
+ * Copyright (c) 1997, 2001 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * q931.c - print Q.931 traces
+ * ---------------------------
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Wed Oct 17 14:49:16 2001]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "trace.h"
+
+/*---------------------------------------------------------------------------*
+ * decode Q.931 protocol
+ *---------------------------------------------------------------------------*/
+void
+decode_q931(char *pbuf, int n, int off, unsigned char *buf, int raw)
+{
+ int codeset = 0;
+ int codelock = 0;
+ int oldcodeset = 0;
+
+ int pd;
+ int len;
+ int j;
+ int i;
+
+ if(n <= 0)
+ return;
+
+ *pbuf = '\0';
+
+ if(raw)
+ {
+ for (i = 0; i < n; i += 16)
+ {
+ sprintf((pbuf+strlen(pbuf)),"Dump:%.3d ", i+off);
+ for (j = 0; j < 16; j++)
+ if (i + j < n)
+ sprintf((pbuf+strlen(pbuf)),"%02x ", buf[i + j]);
+ else
+ sprintf((pbuf+strlen(pbuf))," ");
+ sprintf((pbuf+strlen(pbuf))," ");
+ for (j = 0; j < 16 && i + j < n; j++)
+ if (isprint(buf[i + j]))
+ sprintf((pbuf+strlen(pbuf)),"%c", buf[i + j]);
+ else
+ sprintf((pbuf+strlen(pbuf)),".");
+ sprintf((pbuf+strlen(pbuf)),"\n");
+ }
+ }
+
+ i = 0;
+
+ sprintf((pbuf+strlen(pbuf)), "Q931: ");
+
+ /* protocol discriminator */
+
+ pd = buf[i];
+
+ if(pd >= 0x00 && pd <= 0x07)
+ sprintf((pbuf+strlen(pbuf)), "pd=User-User (0x%02x)\n",pd);
+ else if(pd == 0x08)
+ sprintf((pbuf+strlen(pbuf)), "pd=Q.931/I.451, ");
+ else if(pd >= 0x10 && pd <= 0x3f)
+ sprintf((pbuf+strlen(pbuf)), "pd=Other Layer 3 or X.25 (0x%02x)\n",pd);
+ else if(pd >= 0x40 && pd <= 0x4f)
+ sprintf((pbuf+strlen(pbuf)), "pd=National Use (0x%02x)\n",pd);
+ else if(pd >= 0x50 && pd <= 0xfe)
+ sprintf((pbuf+strlen(pbuf)), "pd=Other Layer 3 or X.25 (0x%02x)\n",pd);
+ else
+ sprintf((pbuf+strlen(pbuf)), "pd=Reserved (0x%02x)\n",pd);
+
+ /* call reference */
+
+ i++;
+
+ len = buf[i] & 0x0f;
+
+ switch(len)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "cr=Dummy, ");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "cr=0x%02x %s, ", (buf[i+1] & 0x7f), (buf[i+1] & 0x80) ? "(from destination)" : "(from origination)");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "cr=0x%02x 0x%02x %s, ", (buf[i+1] & 0x7f), (buf[i+2] & 0x7f), (buf[i+1] & 0x80) ? "(org)" : "(dst)");
+ break;
+ }
+
+ i += (len+1);
+
+ /* message type */
+
+ sprintf((pbuf+strlen(pbuf)), "message=");
+
+ switch(buf[i])
+ {
+ /* escape to nationally specific message type */
+
+ case 0x00:
+ sprintf((pbuf+strlen(pbuf)), "ESCAPE: ");
+ break;
+
+ /* call establishment */
+
+ case 0x01:
+ sprintf((pbuf+strlen(pbuf)), "ALERTING: ");
+ break;
+ case 0x02:
+ sprintf((pbuf+strlen(pbuf)), "CALL PROCEEDING: ");
+ break;
+ case 0x03:
+ sprintf((pbuf+strlen(pbuf)), "PROGRESS: ");
+ break;
+ case 0x05:
+ sprintf((pbuf+strlen(pbuf)), "SETUP: ");
+ break;
+ case 0x07:
+ sprintf((pbuf+strlen(pbuf)), "CONNECT: ");
+ break;
+ case 0x0d:
+ sprintf((pbuf+strlen(pbuf)), "SETUP ACKNOWLEDGE: ");
+ break;
+ case 0x0f:
+ sprintf((pbuf+strlen(pbuf)), "CONNECT ACKNOWLEDGE: ");
+ break;
+
+ /* call information phase */
+
+ case 0x20:
+ sprintf((pbuf+strlen(pbuf)), "USER INFORMATION: ");
+ break;
+ case 0x21:
+ sprintf((pbuf+strlen(pbuf)), "SUSPEND REJECT: ");
+ break;
+ case 0x22:
+ sprintf((pbuf+strlen(pbuf)), "RESUME REJECT: ");
+ break;
+ case 0x24:
+ sprintf((pbuf+strlen(pbuf)), "HOLD: ");
+ break;
+ case 0x25:
+ sprintf((pbuf+strlen(pbuf)), "SUSPEND: ");
+ break;
+ case 0x26:
+ sprintf((pbuf+strlen(pbuf)), "RESUME: ");
+ break;
+ case 0x28:
+ sprintf((pbuf+strlen(pbuf)), "HOLD ACKNOWLEDGE: ");
+ break;
+ case 0x2d:
+ sprintf((pbuf+strlen(pbuf)), "SUSPEND ACKNOWLEDGE: ");
+ break;
+ case 0x2e:
+ sprintf((pbuf+strlen(pbuf)), "RESUME ACKNOWLEDGE: ");
+ break;
+ case 0x30:
+ sprintf((pbuf+strlen(pbuf)), "HOLD REJECT (Q.932): ");
+ break;
+ case 0x31:
+ sprintf((pbuf+strlen(pbuf)), "RETRIEVE (Q.932): ");
+ break;
+ case 0x32:
+ sprintf((pbuf+strlen(pbuf)), "RETRIEVE ACKNOWLEDGE (Q.932): ");
+ break;
+ case 0x37:
+ sprintf((pbuf+strlen(pbuf)), "RETRIEVE REJECT (Q.932): ");
+ break;
+
+ /* call clearing */
+
+ case 0x40:
+ sprintf((pbuf+strlen(pbuf)), "DETACH: ");
+ break;
+ case 0x45:
+ sprintf((pbuf+strlen(pbuf)), "DISCONNECT: ");
+ break;
+ case 0x46:
+ sprintf((pbuf+strlen(pbuf)), "RESTART: ");
+ break;
+ case 0x48:
+ sprintf((pbuf+strlen(pbuf)), "DETACH ACKNOWLEDGE: ");
+ break;
+ case 0x4d:
+ sprintf((pbuf+strlen(pbuf)), "RELEASE: ");
+ break;
+ case 0x4e:
+ sprintf((pbuf+strlen(pbuf)), "RESTART ACKNOWLEDGE: ");
+ break;
+ case 0x5a:
+ sprintf((pbuf+strlen(pbuf)), "RELEASE COMPLETE: ");
+ break;
+
+ /* misc messages */
+
+ case 0x60:
+ sprintf((pbuf+strlen(pbuf)), "SEGMENT: ");
+ break;
+ case 0x62:
+ sprintf((pbuf+strlen(pbuf)), "FACILITY (Q.932): ");
+ break;
+ case 0x64:
+ sprintf((pbuf+strlen(pbuf)), "REGISTER (Q.932): ");
+ break;
+ case 0x68:
+ sprintf((pbuf+strlen(pbuf)), "CANCEL ACKNOWLEDGE: ");
+ break;
+ case 0x6a:
+ sprintf((pbuf+strlen(pbuf)), "FACILITY ACKNOWLEDGE: ");
+ break;
+ case 0x6c:
+ sprintf((pbuf+strlen(pbuf)), "REGISTER ACKNOWLEDGE: ");
+ break;
+ case 0x6e:
+ sprintf((pbuf+strlen(pbuf)), "NOTIFY: ");
+ break;
+ case 0x70:
+ sprintf((pbuf+strlen(pbuf)), "CANCEL REJECT: ");
+ break;
+ case 0x72:
+ sprintf((pbuf+strlen(pbuf)), "FACILITY REJECT: ");
+ break;
+ case 0x74:
+ sprintf((pbuf+strlen(pbuf)), "REGISTER REJECT: ");
+ break;
+ case 0x75:
+ sprintf((pbuf+strlen(pbuf)), "STATUS ENQIRY: ");
+ break;
+ case 0x79:
+ sprintf((pbuf+strlen(pbuf)), "CONGESTION CONTROL: ");
+ break;
+ case 0x7b:
+ sprintf((pbuf+strlen(pbuf)), "INFORMATION: ");
+ break;
+ case 0x7d:
+ sprintf((pbuf+strlen(pbuf)), "STATUS: ");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "UNDEFINED, TYPE=0x%02x, ", buf[i]);
+ break;
+ }
+
+ /* other information elements */
+
+ i++;
+
+ for (; i < n;)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\n ");
+
+ if(buf[i] & 0x80)
+ {
+ /* single octett info element */
+
+ switch(buf[i] & 0x70)
+ {
+ case 0x00: /* reserved */
+ sprintf((pbuf+strlen(pbuf)), "[reserved single octett info]");
+ break;
+
+ case 0x10: /* shift */
+ oldcodeset = codeset;
+ codeset = buf[i] & 0x07;
+ if(buf[i] & 0x08)
+ codelock = 0;
+ else
+ codelock = 1;
+ sprintf((pbuf+strlen(pbuf)), "[shift: codeset=%d lock=%d]", codeset, codelock);
+ break;
+
+ case 0x20: /* more data */
+ if(buf[i] & 0x01)
+ sprintf((pbuf+strlen(pbuf)), "[sending complete]");
+ else
+ sprintf((pbuf+strlen(pbuf)), "[more data]");
+ break;
+
+ case 0x30: /* congestion level */
+ sprintf((pbuf+strlen(pbuf)), "[congestion level=");
+ switch(buf[i] & 0x0f)
+ {
+ case 0x00:
+ sprintf((pbuf+strlen(pbuf)), "rx-ready]");
+ break;
+ case 0x0f:
+ sprintf((pbuf+strlen(pbuf)), "rx-not-ready]");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "reserved (0x%02x)]", buf[i] & 0x0f);
+ break;
+ }
+ break;
+
+ case 0x50: /* repeat ind */
+ sprintf((pbuf+strlen(pbuf)), "[repeat indicator]");
+ break;
+
+ default:
+ sprintf((pbuf+strlen(pbuf)), "[UNKNOWN SINGLE OCTET ELEMENT 0x%02x]", buf[i]);
+ break;
+ }
+
+ i++; /* next */
+
+ }
+ else
+ {
+ /* variable length info element */
+
+ if(codeset == 0)
+ {
+ switch(buf[i])
+ {
+ case 0x00:
+ sprintf((pbuf+strlen(pbuf)), "[segmented message: ");
+ break;
+ case 0x04:
+ sprintf((pbuf+strlen(pbuf)), "[bearer capability: ");
+ i += p_q931bc(pbuf, &buf[i]);
+ goto next;
+ break;
+ case 0x08:
+ sprintf((pbuf+strlen(pbuf)), "[cause: ");
+ i += p_q931cause(pbuf, &buf[i]);
+ goto next;
+ break;
+ case 0x0c:
+ sprintf((pbuf+strlen(pbuf)), "[connected address (old): ");
+ break;
+ case 0x0d:
+ sprintf((pbuf+strlen(pbuf)), "[extended facility (Q.932: )");
+ break;
+ case 0x10:
+ sprintf((pbuf+strlen(pbuf)), "[call identity: ");
+ break;
+ case 0x14:
+ sprintf((pbuf+strlen(pbuf)), "[call state: ");
+ i++;
+ len = buf[i];
+ i++;
+ sprintf((pbuf+strlen(pbuf)), "Std=");
+ switch((buf[i] & 0x60) >> 5)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "CCITT");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "ISO/IEC");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "National");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "Special");
+ break;
+ }
+ sprintf((pbuf+strlen(pbuf)), ", State=");
+
+ switch((buf[i] & 0x3f))
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "Null");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "Call initiated");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "Overlap sending");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "Outgoing call proceeding");
+ break;
+ case 4:
+ sprintf((pbuf+strlen(pbuf)), "Call delivered");
+ break;
+ case 6:
+ sprintf((pbuf+strlen(pbuf)), "Call present");
+ break;
+ case 7:
+ sprintf((pbuf+strlen(pbuf)), "Call received");
+ break;
+ case 8:
+ sprintf((pbuf+strlen(pbuf)), "Connect request");
+ break;
+ case 9:
+ sprintf((pbuf+strlen(pbuf)), "Incoming call proceeding");
+ break;
+ case 10:
+ sprintf((pbuf+strlen(pbuf)), "Active");
+ break;
+ case 11:
+ sprintf((pbuf+strlen(pbuf)), "Disconnect request");
+ break;
+ case 12:
+ sprintf((pbuf+strlen(pbuf)), "Disconnect indication");
+ break;
+ case 15:
+ sprintf((pbuf+strlen(pbuf)), "Suspend request");
+ break;
+ case 17:
+ sprintf((pbuf+strlen(pbuf)), "Resume request");
+ break;
+ case 19:
+ sprintf((pbuf+strlen(pbuf)), "Release request");
+ break;
+ case 22:
+ sprintf((pbuf+strlen(pbuf)), "Call abort");
+ break;
+ case 25:
+ sprintf((pbuf+strlen(pbuf)), "Overlap receiving");
+ break;
+ case 0x3d:
+ sprintf((pbuf+strlen(pbuf)), "Restart request");
+ break;
+ case 0x3e:
+ sprintf((pbuf+strlen(pbuf)), "Restart");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "ERROR: undefined/reserved");
+ break;
+ }
+ sprintf((pbuf+strlen(pbuf)), "]");
+ i++;
+ goto next;
+ break;
+ case 0x18:
+ sprintf((pbuf+strlen(pbuf)), "[channel id: channel=");
+ i++;
+ len = buf[i];
+ i++;
+ switch(buf[i] & 0x03)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "no channel");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "B-1");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "B-2");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "any channel");
+ break;
+ }
+ if(buf[i] & 0x08)
+ sprintf((pbuf+strlen(pbuf)), " (exclusive)]");
+ else
+ sprintf((pbuf+strlen(pbuf)), " (preferred)]");
+ i++;
+ goto next;
+ break;
+ case 0x19:
+ sprintf((pbuf+strlen(pbuf)), "[data link connection id (Q.933): ");
+ break;
+ case 0x1c:
+ i += q932_facility(pbuf, &buf[i]);
+ goto next;
+ break;
+ case 0x1e:
+ sprintf((pbuf+strlen(pbuf)), "[progress ind: ");
+ i++;
+ len = buf[i];
+ i++;
+ sprintf((pbuf+strlen(pbuf)), "Std=");
+ switch((buf[i] & 0x60) >> 5)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "CCITT");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "ISO/IEC");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "National");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "Local");
+ break;
+ }
+ sprintf((pbuf+strlen(pbuf)), ", Loc=");
+
+ switch((buf[i] & 0x0f))
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "User");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "Private network serving local user");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "Public network serving local user");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "Transit network");
+ break;
+ case 4:
+ sprintf((pbuf+strlen(pbuf)), "Public network serving remote user");
+ break;
+ case 5:
+ sprintf((pbuf+strlen(pbuf)), "Private network serving remote user");
+ break;
+ case 6:
+ sprintf((pbuf+strlen(pbuf)), "Network beyond interworking point");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "ERROR: undefined/reserved");
+ break;
+ }
+
+ i++;
+
+ sprintf((pbuf+strlen(pbuf)), "\n Description: ");
+
+ switch((buf[i] & 0x7f))
+ {
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "Call is not end-to-end ISDN");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "Destination address is non-ISDN");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "Origination address is non-ISDN");
+ break;
+ case 4:
+ sprintf((pbuf+strlen(pbuf)), "Call has returned to the ISDN");
+ break;
+ case 5:
+ sprintf((pbuf+strlen(pbuf)), "Interworking occured, Service change");
+ break;
+ case 8:
+ sprintf((pbuf+strlen(pbuf)), "In-band info or appropriate pattern now available");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "ERROR: undefined/reserved");
+ break;
+ }
+ sprintf((pbuf+strlen(pbuf)), "]");
+ i++;
+ goto next;
+ break;
+ case 0x20:
+ sprintf((pbuf+strlen(pbuf)), "[network specific facilities: ");
+ break;
+ case 0x24:
+ sprintf((pbuf+strlen(pbuf)), "[terminal capabilities: ");
+ break;
+ case 0x27:
+ sprintf((pbuf+strlen(pbuf)), "[notification indicator: ");
+ i += p_q931notification(pbuf, &buf[i]);
+ goto next;
+ break;
+ case 0x28:
+ sprintf((pbuf+strlen(pbuf)), "[display: ");
+ i++;
+ len = buf[i];
+ i++;
+ for(j = 0; j < len; j++)
+ {
+ sprintf((pbuf+strlen(pbuf)),"%c", buf[j+i]);
+ }
+ sprintf((pbuf+strlen(pbuf)),"]");
+ i += j;
+ goto next;
+ break;
+ case 0x29:
+ sprintf((pbuf+strlen(pbuf)), "[date/time: ");
+ i++;
+ len = buf[i];
+ i++;
+ j = 0;
+ sprintf((pbuf+strlen(pbuf)),"%.2d.%.2d.%.2d",
+ buf[i+2], buf[i+1], buf[i]);
+ j+=3;
+ if(j < len)
+ {
+ sprintf((pbuf+strlen(pbuf))," %.2d", buf[i+3]);
+ j++;
+ }
+ if(j < len)
+ {
+ sprintf((pbuf+strlen(pbuf)),":%.2d", buf[i+4]);
+ j++;
+ }
+ if(j < len)
+ {
+ sprintf((pbuf+strlen(pbuf)),":%.2d", buf[i+5]);
+ j++;
+ }
+ sprintf((pbuf+strlen(pbuf)),"]");
+ i += len;
+ goto next;
+ break;
+ case 0x2c:
+ sprintf((pbuf+strlen(pbuf)), "[keypad: ");
+ i++;
+ len = buf[i];
+ i++;
+ for(j = 0; j < len; j++)
+ {
+ sprintf((pbuf+strlen(pbuf)),"%c", buf[j+i]);
+ }
+ sprintf((pbuf+strlen(pbuf)),"]");
+ i += j;
+ goto next;
+ break;
+ case 0x30:
+ sprintf((pbuf+strlen(pbuf)), "[keypad echo: ");
+ break;
+ case 0x32:
+ sprintf((pbuf+strlen(pbuf)), "[information req (Q.932): ");
+ break;
+ case 0x34:
+ sprintf((pbuf+strlen(pbuf)), "[signal: ");
+ break;
+ case 0x36:
+ sprintf((pbuf+strlen(pbuf)), "[switchhook: ");
+ break;
+ case 0x38:
+ sprintf((pbuf+strlen(pbuf)), "[feature activation (Q.932): ");
+ break;
+ case 0x39:
+ sprintf((pbuf+strlen(pbuf)), "[feature ind (Q.932): ");
+ break;
+ case 0x3a:
+ sprintf((pbuf+strlen(pbuf)), "[service profile id (Q.932): ");
+ break;
+ case 0x3b:
+ sprintf((pbuf+strlen(pbuf)), "[endpoint id (Q.932): ");
+ break;
+ case 0x40:
+ sprintf((pbuf+strlen(pbuf)), "[information rate: ");
+ break;
+ case 0x41:
+ sprintf((pbuf+strlen(pbuf)), "[precedence level (Q.955): ");
+ break;
+ case 0x42:
+ sprintf((pbuf+strlen(pbuf)), "[end-to-end transit delay: ");
+ break;
+ case 0x43:
+ sprintf((pbuf+strlen(pbuf)), "[transit delay detection and indication: ");
+ break;
+ case 0x44:
+ sprintf((pbuf+strlen(pbuf)), "[packet layer binary parameters: ");
+ break;
+ case 0x45:
+ sprintf((pbuf+strlen(pbuf)), "[packet layer window size: ");
+ break;
+ case 0x46:
+ sprintf((pbuf+strlen(pbuf)), "[packet size: ");
+ break;
+ case 0x47:
+ sprintf((pbuf+strlen(pbuf)), "[closed user group: ");
+ break;
+ case 0x48:
+ sprintf((pbuf+strlen(pbuf)), "[link layer core parameters (Q.933): ");
+ break;
+ case 0x49:
+ sprintf((pbuf+strlen(pbuf)), "[link layer protocol parameters (Q.933): ");
+ break;
+ case 0x4a:
+ sprintf((pbuf+strlen(pbuf)), "[reverse charging information: ");
+ break;
+ case 0x4c:
+ sprintf((pbuf+strlen(pbuf)), "[connected number (Q.951): ");
+ i += p_q931address(pbuf, &buf[i]);
+ goto next;
+ break;
+
+ break;
+ case 0x4d:
+ sprintf((pbuf+strlen(pbuf)), "[connected subaddress (Q.951): ");
+ break;
+ case 0x50:
+ sprintf((pbuf+strlen(pbuf)), "[X.213 priority (Q.933): ");
+ break;
+ case 0x51:
+ sprintf((pbuf+strlen(pbuf)), "[report type (Q.933): ");
+ break;
+ case 0x53:
+ sprintf((pbuf+strlen(pbuf)), "[link integrity verification (Q.933): ");
+ break;
+ case 0x57:
+ sprintf((pbuf+strlen(pbuf)), "[PVC status (Q.933): ");
+ break;
+ case 0x6c:
+ sprintf((pbuf+strlen(pbuf)), "[calling party number: ");
+ i += p_q931address(pbuf, &buf[i]);
+ goto next;
+ break;
+ case 0x6d:
+ sprintf((pbuf+strlen(pbuf)), "[calling party subaddress: ");
+ break;
+ case 0x70:
+ sprintf((pbuf+strlen(pbuf)), "[called party number: ");
+ i += p_q931address(pbuf, &buf[i]);
+ goto next;
+ break;
+ case 0x71:
+ sprintf((pbuf+strlen(pbuf)), "[called party subaddress: ");
+ break;
+ case 0x74:
+ sprintf((pbuf+strlen(pbuf)), "[redirecting number: ");
+ i += p_q931redir(pbuf, &buf[i]);
+ goto next;
+ break;
+ case 0x76:
+ sprintf((pbuf+strlen(pbuf)), "[redirection number: ");
+ i += p_q931redir(pbuf, &buf[i]);
+ goto next;
+ break;
+ case 0x78:
+ sprintf((pbuf+strlen(pbuf)), "[transit network selection: ");
+ break;
+ case 0x79:
+ sprintf((pbuf+strlen(pbuf)), "[restart indicator: ");
+ break;
+ case 0x7c:
+ sprintf((pbuf+strlen(pbuf)), "[low layer compatibility: ");
+ break;
+ case 0x7d:
+ sprintf((pbuf+strlen(pbuf)), "[high layer compatibility:");
+ i += p_q931high_compat(pbuf, &buf[i]);
+ goto next;
+ break;
+ case 0x7e:
+ sprintf((pbuf+strlen(pbuf)), "[user-user: ");
+ i += p_q931user_user(pbuf, &buf[i]);
+ goto next;
+ break;
+ case 0x7f:
+ sprintf((pbuf+strlen(pbuf)), "[escape for extension: ");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "[UNKNOWN INFO-ELEMENT-ID=0x%02x: ", buf[i]);
+ break;
+ }
+ }
+ else
+ {
+ sprintf((pbuf+strlen(pbuf)), "[UNKNOWN CODESET=%d, IE=0x%02x: ", codeset, buf[i]);
+ }
+
+ i++; /* index -> length */
+
+ len = buf[i];
+
+ sprintf((pbuf+strlen(pbuf)), "LEN=0x%02x, DATA=", len);
+
+ i++; /* index -> 1st param */
+
+ for(j = 0; j < len; j++)
+ {
+ sprintf((pbuf+strlen(pbuf)),"0x%02x ", buf[j+i]);
+ }
+
+ sprintf((pbuf+strlen(pbuf)),"]");
+
+ i += len;
+
+next:
+
+ if(!codelock && (codeset != oldcodeset))
+ codeset = oldcodeset;
+ }
+ }
+ sprintf((pbuf+strlen(pbuf)),"\n");
+}
+
+/* EOF */
+
diff --git a/usr.sbin/i4b/isdntrace/q931_util.c b/usr.sbin/i4b/isdntrace/q931_util.c
new file mode 100644
index 0000000..9f82283
--- /dev/null
+++ b/usr.sbin/i4b/isdntrace/q931_util.c
@@ -0,0 +1,1047 @@
+/*
+ * Copyright (c) 1997, 2000 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * q931_util.c - utility functions to print Q.931 traces
+ * -----------------------------------------------------
+ *
+ * $Id: q931_util.c,v 1.11 2000/02/15 12:48:14 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Tue Feb 15 13:52:09 2000]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "trace.h"
+
+/*---------------------------------------------------------------------------*
+ * decode and print the cause
+ *---------------------------------------------------------------------------*/
+int
+p_q931cause(char *pbuf, unsigned char *buf)
+{
+ int j;
+ int len;
+ int i = 0;
+ int ls;
+ int r = 0;
+ int rflag = 0;
+
+ i++; /* index -> length */
+
+ len = buf[i];
+
+ i++; /* coding/location */
+ len--;
+
+ ls = buf[i];
+
+ i++;
+ len--;
+
+ if(!(buf[i-1] & 0x80))
+ {
+ r = buf[i];
+ rflag = 1;
+ i++;
+ len--;
+ }
+
+ sprintf((pbuf+strlen(pbuf)), "%s ", print_cause_q850(buf[i] & 0x7f));
+
+ sprintf((pbuf+strlen(pbuf)), "\n (location=");
+
+ switch(ls & 0x0f)
+ {
+ case 0x00:
+ sprintf((pbuf+strlen(pbuf)), "user");
+ break;
+ case 0x01:
+ sprintf((pbuf+strlen(pbuf)), "private network serving local user");
+ break;
+ case 0x02:
+ sprintf((pbuf+strlen(pbuf)), "public network serving local user");
+ break;
+ case 0x03:
+ sprintf((pbuf+strlen(pbuf)), "transit network");
+ break;
+ case 0x04:
+ sprintf((pbuf+strlen(pbuf)), "public network serving remote user");
+ break;
+ case 0x05:
+ sprintf((pbuf+strlen(pbuf)), "private network serving remote user");
+ break;
+ case 0x07:
+ sprintf((pbuf+strlen(pbuf)), "international network");
+ break;
+ case 0x0a:
+ sprintf((pbuf+strlen(pbuf)), "network beyond interworking point");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "reserved (0x%02x)", ls & 0x0f);
+ break;
+ }
+
+ sprintf((pbuf+strlen(pbuf)), ", std=");
+
+ switch((ls & 0x60) >> 5)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "CCITT");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "ISO/IEC");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "National");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "Local");
+ break;
+ }
+
+ if(rflag)
+ {
+ sprintf((pbuf+strlen(pbuf)), ", rec=");
+
+ switch(r & 0x7f)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "Q.931");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "X.21");
+ break;
+ case 4:
+ sprintf((pbuf+strlen(pbuf)), "X.25");
+ break;
+ case 5:
+ sprintf((pbuf+strlen(pbuf)), "Q.1031/Q.1051");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "Reserved");
+ break;
+ }
+ }
+
+ sprintf((pbuf+strlen(pbuf)),")");
+
+ i++;
+ len--;
+
+ for(j = 0; j < len; j++)
+ sprintf((pbuf+strlen(pbuf))," 0x%02x", buf[j+i]);
+
+ sprintf((pbuf+strlen(pbuf)),"]");
+
+ i += (len+1);
+
+ return(i);
+}
+
+/*---------------------------------------------------------------------------*
+ * decode and print the bearer capability
+ *---------------------------------------------------------------------------*/
+int
+p_q931bc(char *pbuf, unsigned char *buf)
+{
+ int len;
+ int i = 0;
+ int mr = 0;
+
+ i++; /* index -> length */
+
+ len = buf[i];
+
+ i++;
+
+ sprintf((pbuf+strlen(pbuf)), "\n cap=");
+
+ switch(buf[i] & 0x1f)
+ {
+ case 0x00:
+ sprintf((pbuf+strlen(pbuf)), "speech");
+ break;
+ case 0x08:
+ sprintf((pbuf+strlen(pbuf)), "unrestricted digital information");
+ break;
+ case 0x09:
+ sprintf((pbuf+strlen(pbuf)), "restricted digital information");
+ break;
+ case 0x10:
+ sprintf((pbuf+strlen(pbuf)), "3.1 kHz audio");
+ break;
+ case 0x11:
+ sprintf((pbuf+strlen(pbuf)), "unrestricted digital information with tones");
+ break;
+ case 0x18:
+ sprintf((pbuf+strlen(pbuf)), "video");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "reserved (0x%02x)", buf[i] & 0x0f);
+ break;
+ }
+
+ sprintf((pbuf+strlen(pbuf)), "\n std=");
+
+ switch((buf[i] & 0x60) >> 5)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "CCITT");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "ISO/IEC");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "National");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "NSI Std");
+ break;
+ }
+
+ i++;
+ len--;
+
+ sprintf((pbuf+strlen(pbuf)), "\n rate=");
+
+ switch(buf[i] & 0x1f)
+ {
+ case 0x00:
+ sprintf((pbuf+strlen(pbuf)), "packet mode");
+ break;
+ case 0x10:
+ sprintf((pbuf+strlen(pbuf)), "64 kbit/s");
+ break;
+ case 0x11:
+ sprintf((pbuf+strlen(pbuf)), "2 x 64 kbit/s");
+ break;
+ case 0x13:
+ sprintf((pbuf+strlen(pbuf)), "384 kbit/s");
+ break;
+ case 0x15:
+ sprintf((pbuf+strlen(pbuf)), "1536 kbit/s");
+ break;
+ case 0x17:
+ sprintf((pbuf+strlen(pbuf)), "1920 kbit/s");
+ break;
+ case 0x18:
+ sprintf((pbuf+strlen(pbuf)), "Multirate");
+ mr = 1;
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "reserved (0x%02x)", buf[i] & 0x0f);
+ break;
+ }
+
+ sprintf((pbuf+strlen(pbuf)), "\n mode=");
+
+ switch((buf[i] & 0x60) >> 5)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "circuit");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "packet");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "reserved (0x%02x)", ((buf[i] & 0x60) >> 5));
+ break;
+ }
+
+ i++;
+ len--;
+
+ if(!len)
+ goto exit;
+
+ if(mr)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\n rate multiplier=%d", buf[i] & 0x7f);
+ i++;
+ len--;
+ }
+
+ if(!len)
+ goto exit;
+
+ sprintf((pbuf+strlen(pbuf)), "\n layer1=");
+
+ switch(buf[i] & 0x1f)
+ {
+ case 0x01:
+ sprintf((pbuf+strlen(pbuf)), "V.110");
+ break;
+ case 0x02:
+ sprintf((pbuf+strlen(pbuf)), "G.711 u-law");
+ break;
+ case 0x03:
+ sprintf((pbuf+strlen(pbuf)), "G.711 A-law");
+ break;
+ case 0x04:
+ sprintf((pbuf+strlen(pbuf)), "G.721");
+ break;
+ case 0x05:
+ sprintf((pbuf+strlen(pbuf)), "H.221/H.242");
+ break;
+ case 0x07:
+ sprintf((pbuf+strlen(pbuf)), "Non-Std");
+ break;
+ case 0x08:
+ sprintf((pbuf+strlen(pbuf)), "V.120");
+ break;
+ case 0x09:
+ sprintf((pbuf+strlen(pbuf)), "X.31");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "reserved (0x%02x)", buf[i] & 0x0f);
+ break;
+ }
+ i++;
+ len--;
+
+ if(!len)
+ goto exit;
+
+ if(!(buf[i-1] & 0x80))
+ {
+ sprintf((pbuf+strlen(pbuf)), "\n user rate=0x%02x ", buf[i] & 0x1f);
+
+ if(buf[i] & 0x40)
+ sprintf((pbuf+strlen(pbuf)), "(async,");
+ else
+ sprintf((pbuf+strlen(pbuf)), "(sync,");
+
+ if(buf[i] & 0x20)
+ sprintf((pbuf+strlen(pbuf)), "in-band neg. possible)");
+ else
+ sprintf((pbuf+strlen(pbuf)), "in-band neg not possible)");
+
+ i++;
+ len--;
+ }
+
+ if(!len)
+ goto exit;
+
+ if(!(buf[i-1] & 0x80))
+ {
+ sprintf((pbuf+strlen(pbuf)), "\n clk/flow=0x%02x", buf[i] & 0x1f);
+
+ sprintf((pbuf+strlen(pbuf)), "\n intermediate rate=");
+
+ switch((buf[i] & 0x60) >> 5)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "not used");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "8 kbit/s");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "16 kbit/s");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "32 kbit/s");
+ break;
+ }
+ i++;
+ len--;
+ }
+
+ if(!len)
+ goto exit;
+
+ if(!(buf[i-1] & 0x80))
+ {
+ sprintf((pbuf+strlen(pbuf)), "\n hdr/mfrm/etc.=0x%02x", buf[i]);
+ i++;
+ len--;
+ }
+
+ if(!len)
+ goto exit;
+
+ if(!(buf[i-1] & 0x80))
+ {
+ sprintf((pbuf+strlen(pbuf)), "\n stop/data/parity=0x%02x", buf[i]);
+ i++;
+ len--;
+ }
+
+ if(!len)
+ goto exit;
+
+ if(!(buf[i-1] & 0x80))
+ {
+ sprintf((pbuf+strlen(pbuf)), "\n modemtype=0x%02x", buf[i]);
+ i++;
+ len--;
+ }
+
+ if(!len)
+ goto exit;
+
+ switch(buf[i] & 0x7f)
+ {
+ case 0x42:
+ sprintf((pbuf+strlen(pbuf)), "\n layer2=Q.921/I.441");
+ break;
+ case 0x46:
+ sprintf((pbuf+strlen(pbuf)), "\n layer2=X.25 link");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "\n layer2=0x%02x",(buf[i] & 0x7f));
+ break;
+ }
+ i++;
+ len--;
+
+ if(!len)
+ goto exit;
+
+ switch(buf[i] & 0x7f)
+ {
+ case 0x62:
+ sprintf((pbuf+strlen(pbuf)), "\n layer3=Q.921/I.441");
+ break;
+ case 0x66:
+ sprintf((pbuf+strlen(pbuf)), "\n layer3=X.25 packet");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "\n layer3=0x%02x",(buf[i] & 0x7f));
+ break;
+ }
+ i++;
+ len--;
+
+exit:
+ sprintf((pbuf+strlen(pbuf)), "]");
+ return(i);
+}
+
+/*---------------------------------------------------------------------------*
+ * decode and print the ISDN (telephone) number
+ *---------------------------------------------------------------------------*/
+int
+p_q931address(char *pbuf, unsigned char *buf)
+{
+ int j;
+ int len;
+ int i = 0;
+ int tp;
+ int ind = 0;
+ int indflag = 0;
+
+ i++; /* index -> length */
+ len = buf[i];
+
+ i++; /* index -> type/plan */
+ tp = buf[i];
+
+ i++;
+ len--;
+
+ if(!(tp & 0x80))
+ {
+ ind = buf[i];
+ indflag = 1;
+ i++;
+ len--;
+ }
+
+ for(j = 0; j < len; j++)
+ {
+ sprintf((pbuf+strlen(pbuf)),"%c", buf[j+i]);
+ }
+
+ switch((tp & 0x70) >> 4)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), " (type=unknown, ");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), " (type=international, ");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), " (type=national, ");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), " (type=network specific, ");
+ break;
+ case 4:
+ sprintf((pbuf+strlen(pbuf)), " (type=subscriber, ");
+ break;
+ case 6:
+ sprintf((pbuf+strlen(pbuf)), " (type=abbreviated, ");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), " (type=reserved (%d), ", ((tp & 0x70) >> 4));
+ break;
+ }
+
+ switch(tp & 0x0f)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "plan=unknown");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "plan=ISDN");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "plan=Data");
+ break;
+ case 4:
+ sprintf((pbuf+strlen(pbuf)), "plan=Telex");
+ break;
+ case 8:
+ sprintf((pbuf+strlen(pbuf)), "plan=National");
+ break;
+ case 9:
+ sprintf((pbuf+strlen(pbuf)), "plan=private");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "plan=reserved (%d)", (tp & 0x0f));
+ break;
+ }
+
+ if(indflag)
+ {
+ sprintf((pbuf+strlen(pbuf)), ",\n ");
+ switch((ind & 0x60) >> 5)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "presentation allowed, ");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "presentation restricted, ");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "number not available, ");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "reserved, ");
+ break;
+ }
+
+ switch(ind & 0x03)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "screening user provided: not screened");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "screening user provided: verified & passed");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "screening user provided: verified & failed");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "screening network provided");
+ break;
+ }
+ }
+
+ sprintf((pbuf+strlen(pbuf)),")]");
+
+ i += j;
+
+ return(i);
+}
+
+/*---------------------------------------------------------------------------*
+ * decode and print HL comatibility
+ *---------------------------------------------------------------------------*/
+int
+p_q931high_compat(char *pbuf, unsigned char *buf)
+{
+ int len = buf[1];
+
+ sprintf(pbuf+strlen(pbuf), " standard=");
+
+ switch ((buf[2] >> 5) & 0x03)
+ {
+ case 0: sprintf(pbuf+strlen(pbuf), "CCITT");
+ break;
+ case 1: sprintf(pbuf+strlen(pbuf), "unknown international standard");
+ break;
+ case 2: sprintf(pbuf+strlen(pbuf), "unknown national standard");
+ break;
+ case 3: sprintf(pbuf+strlen(pbuf), "local network standard");
+ }
+
+ len--;
+
+ sprintf(pbuf+strlen(pbuf), ", characteristics=");
+
+ switch (buf[3] & 0x7f)
+ {
+ case 0x01:
+ sprintf(pbuf+strlen(pbuf), "Telephony");
+ break;
+ case 0x04:
+ sprintf(pbuf+strlen(pbuf), "Fax Group 2/3");
+ break;
+ case 0x21:
+ sprintf(pbuf+strlen(pbuf), "Fax Group 4 Class I (F.184)");
+ break;
+ case 0x24:
+ sprintf(pbuf+strlen(pbuf), "Teletex basic/mixed (F.230) or Fax Group 4 Class II/III (F.184)");
+ break;
+ case 0x28:
+ sprintf(pbuf+strlen(pbuf), "Teletex basic/processable (F.220)");
+ break;
+ case 0x31:
+ sprintf(pbuf+strlen(pbuf), "Teletex basic mode (F.200)");
+ break;
+ case 0x32:
+ sprintf(pbuf+strlen(pbuf), "Videotex (F.300 and T.101)");
+ break;
+ case 0x35:
+ sprintf(pbuf+strlen(pbuf), "Telex (F.60)");
+ break;
+ case 0x38:
+ sprintf(pbuf+strlen(pbuf), "MHS (X.400 series)");
+ break;
+ case 0x41:
+ sprintf(pbuf+strlen(pbuf), "OSI application (X.200 series)");
+ break;
+ case 0x5e:
+ sprintf(pbuf+strlen(pbuf), "Maintenance");
+ break;
+ case 0x5f:
+ sprintf(pbuf+strlen(pbuf), "Management");
+ break;
+ case 0x7f:
+ sprintf(pbuf+strlen(pbuf), "reserved");
+ break;
+ default:
+ sprintf(pbuf+strlen(pbuf), "UNKNOWN (0x%02x)", buf[3]);
+ break;
+ }
+
+ len--;
+
+ if (!len)
+ {
+ sprintf(pbuf+strlen(pbuf), "]");
+ return 4;
+ }
+
+ sprintf(pbuf+strlen(pbuf), " of ");
+
+ switch (buf[4] & 0x7f)
+ {
+ case 0x01:
+ sprintf(pbuf+strlen(pbuf), "Telephony");
+ break;
+ case 0x04:
+ sprintf(pbuf+strlen(pbuf), "Fax Group 2/3");
+ break;
+ case 0x21:
+ sprintf(pbuf+strlen(pbuf), "Fax Group 4 Class I (F.184)");
+ break;
+ case 0x24:
+ sprintf(pbuf+strlen(pbuf), "Teletex basic/mixed (F.230) or Fax Group 4 Class II/III (F.184)");
+ break;
+ case 0x28:
+ sprintf(pbuf+strlen(pbuf), "Teletex basic/processable (F.220)");
+ break;
+ case 0x31:
+ sprintf(pbuf+strlen(pbuf), "Teletex basic mode (F.200)");
+ break;
+ case 0x32:
+ sprintf(pbuf+strlen(pbuf), "Videotex (F.300 and T.101)");
+ break;
+ case 0x35:
+ sprintf(pbuf+strlen(pbuf), "Telex (F.60)");
+ break;
+ case 0x38:
+ sprintf(pbuf+strlen(pbuf), "MHS (X.400 series)");
+ break;
+ case 0x41:
+ sprintf(pbuf+strlen(pbuf), "OSI application (X.200 series)");
+ break;
+ case 0x7f:
+ sprintf(pbuf+strlen(pbuf), "reserved");
+ break;
+ default:
+ sprintf(pbuf+strlen(pbuf), "UNKNOWN (0x%02x)", buf[3]);
+ break;
+ }
+ sprintf(pbuf+strlen(pbuf), "]");
+ return 5;
+}
+
+/*---------------------------------------------------------------------------*
+ * decode and print user-user IE
+ *---------------------------------------------------------------------------*/
+int
+p_q931user_user(char *pbuf, unsigned char *buf)
+{
+ int j;
+ int len;
+ int i = 0;
+ int pd;
+
+ i++; /* index -> length */
+ len = buf[i];
+
+ i++; /* index -> protocoldiscriminator */
+ pd = buf[i];
+
+ switch(pd)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "(pd=user-specific");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "(pd=OSI high-layer protocols");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "(pd=X.244");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "(pd=reserved for system management");
+ break;
+ case 4:
+ sprintf((pbuf+strlen(pbuf)), "(pd=IA5 characters");
+ break;
+ case 6:
+ sprintf((pbuf+strlen(pbuf)), "(pd=X.208/X.209 coded user info");
+ break;
+ case 7:
+ sprintf((pbuf+strlen(pbuf)), "(pd=V.120 rate adaption");
+ break;
+ case 8:
+ sprintf((pbuf+strlen(pbuf)), "(pd=Q.931/I.451 user network call control messages");
+ break;
+ default:
+ if(pd >= 0x10 && pd <= 0x3f)
+ sprintf((pbuf+strlen(pbuf)), "(pd=0x%x=reserved for other L3 protocols incl. X.25", pd);
+ else if(pd >= 0x40 && pd <= 0x47)
+ sprintf((pbuf+strlen(pbuf)), "(pd=0x%x=national use", pd);
+ else if(pd >= 0x48 && pd <= 0x4f)
+ sprintf((pbuf+strlen(pbuf)), "(pd=0x%x=reserved for ETSI", pd);
+ else if(pd >= 0x50 && pd <= 0xfe)
+ sprintf((pbuf+strlen(pbuf)), "(pd=0x%x=reserved for other L3 protocols incl. X.25", pd);
+ else
+ sprintf((pbuf+strlen(pbuf)), "(pd=0x%x=reserved", pd);
+ break;
+ }
+
+ i++;
+ len--;
+
+ sprintf((pbuf+strlen(pbuf)),": (");
+
+ for(j = 0; j < len; j++)
+ {
+ if(pd == 4)
+ sprintf((pbuf+strlen(pbuf)),"%c", buf[j+i]);
+ else
+ sprintf((pbuf+strlen(pbuf)),"0x%2x", buf[j+i]);
+ }
+
+ sprintf((pbuf+strlen(pbuf)),"))]");
+
+ i += j;
+
+ return(i);
+}
+
+/*---------------------------------------------------------------------------*
+ * decode and notification indicator IE (Q.932, p44)
+ *---------------------------------------------------------------------------*/
+int
+p_q931notification(char *pbuf, unsigned char *buf)
+{
+ int j = 0;
+ int len;
+ int i = 0;
+ int nd;
+
+ i++; /* index -> length */
+ len = buf[i];
+
+ i++; /* index -> notification description */
+ nd = buf[i];
+
+ switch(nd)
+ {
+ case 0x80:
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, user suspended", nd);
+ break;
+ case 0x81:
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, user resumed", nd);
+ break;
+ case 0x82:
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, bearer service changed", nd);
+ break;
+
+ case 0x83:
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, BER coded information", nd);
+ break;
+
+ case 0xc2:
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, conference established", nd);
+ break;
+ case 0xc3:
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, conference disconnected", nd);
+ break;
+ case 0xc4:
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, other party added", nd);
+ break;
+ case 0xc5:
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, isolated", nd);
+ break;
+ case 0xc6:
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, reattached", nd);
+ break;
+ case 0xc7:
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, other party isolated", nd);
+ break;
+ case 0xc8:
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, other party reattached", nd);
+ break;
+ case 0xc9:
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, other party split", nd);
+ break;
+ case 0xca:
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, other party disconnected", nd);
+ break;
+ case 0xcb:
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, conference floating", nd);
+ break;
+ case 0xcc:
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, conference disconnected, preemption", nd);
+ break;
+ case 0xcf:
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, conference floating, server user preempted", nd);
+ break;
+
+ case 0xe0:
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, call is a waiting call", nd);
+ break;
+ case 0xe8:
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, diversion activated", nd);
+ break;
+ case 0xe9: /* ECT, EN 300 369-1 V1.2.4 p12) */
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, call transferred, alerting", nd);
+ break;
+ case 0xea: /* ECT, EN 300 369-1 V1.2.4 p12) */
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, call transferred, active", nd);
+ break;
+ case 0xee:
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, reverse charging", nd);
+ break;
+
+ case 0xf9:
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, remote hold", nd);
+ break;
+ case 0xfa:
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, remote retrieval", nd);
+ break;
+ case 0xfb:
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, call is diverting", nd);
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, undefined", nd);
+ break;
+ }
+
+ i++;
+ len--;
+
+ if(len)
+ {
+ sprintf((pbuf+strlen(pbuf)),": (");
+
+ for(; j < len; j++)
+ {
+ if(nd == 4)
+ sprintf((pbuf+strlen(pbuf)),"%c", buf[j+i]);
+ else
+ sprintf((pbuf+strlen(pbuf)),"0x%2x", buf[j+i]);
+ }
+
+ sprintf((pbuf+strlen(pbuf)),")");
+ }
+ sprintf((pbuf+strlen(pbuf)),"]");
+ i += j;
+
+ return(i);
+}
+
+/*---------------------------------------------------------------------------*
+ * decode and print redirecting/redirection number
+ *---------------------------------------------------------------------------*/
+int
+p_q931redir(char *pbuf, unsigned char *buf)
+{
+ int j;
+ int len;
+ int i = 0;
+ int tp;
+ int ind = 0;
+ int indflag = 0;
+ int reas = 0;
+ int reasflag = 0;
+
+ i++; /* index -> length */
+ len = buf[i];
+
+ i++; /* index -> type/plan */
+ tp = buf[i];
+
+ i++;
+ len--;
+
+ if(!(tp & 0x80))
+ {
+ ind = buf[i];
+ indflag = 1;
+ i++;
+ len--;
+
+ if(!(ind & 0x80))
+ {
+ reas = buf[i];
+ reasflag = 1;
+ i++;
+ len--;
+ }
+ }
+
+ for(j = 0; j < len; j++)
+ {
+ sprintf((pbuf+strlen(pbuf)),"%c", buf[j+i]);
+ }
+
+ switch((tp & 0x70) >> 4)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), " (type=unknown, ");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), " (type=international, ");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), " (type=national, ");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), " (type=network specific, ");
+ break;
+ case 4:
+ sprintf((pbuf+strlen(pbuf)), " (type=subscriber, ");
+ break;
+ case 6:
+ sprintf((pbuf+strlen(pbuf)), " (type=abbreviated, ");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), " (type=reserved (%d), ", ((tp & 0x70) >> 4));
+ break;
+ }
+
+ switch(tp & 0x0f)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "plan=unknown");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "plan=ISDN");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "plan=Data");
+ break;
+ case 4:
+ sprintf((pbuf+strlen(pbuf)), "plan=Telex");
+ break;
+ case 8:
+ sprintf((pbuf+strlen(pbuf)), "plan=National");
+ break;
+ case 9:
+ sprintf((pbuf+strlen(pbuf)), "plan=private");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "plan=reserved (%d)", (tp & 0x0f));
+ break;
+ }
+
+ if(indflag)
+ {
+ sprintf((pbuf+strlen(pbuf)), ",\n ");
+ switch((ind & 0x60) >> 5)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "presentation allowed");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "presentation restricted");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "number not available");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "reserved");
+ break;
+ }
+ }
+
+ if(reasflag)
+ {
+ sprintf((pbuf+strlen(pbuf)), ",\n ");
+ switch(reas & 0x0f)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "reason for diversion: unknown");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "reason for diversion: call forwarding busy");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "reason for diversion: call forwarding unconditional");
+ break;
+ case 0xa:
+ sprintf((pbuf+strlen(pbuf)), "reason for diversion: called DTE");
+ break;
+ case 0xf:
+ sprintf((pbuf+strlen(pbuf)), "reason for diversion: call forwarding unconditional");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "reason for diversion: reserved (0x%2x)",reas & 0x0f);
+ break;
+ }
+ }
+
+ sprintf((pbuf+strlen(pbuf)),")]");
+
+ i += j;
+
+ return(i);
+}
+
+/* EOF */
+
diff --git a/usr.sbin/i4b/isdntrace/q932_fac.c b/usr.sbin/i4b/isdntrace/q932_fac.c
new file mode 100644
index 0000000..0a0099d
--- /dev/null
+++ b/usr.sbin/i4b/isdntrace/q932_fac.c
@@ -0,0 +1,1236 @@
+/*
+ * Copyright (c) 1997, 2000 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * q932_fac.c - decode Q.932 facilities
+ * ------------------------------------
+ *
+ * $Id: q932_fac.c,v 1.8 2000/02/24 16:32:46 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Thu Feb 24 17:36:47 2000]
+ *
+ *---------------------------------------------------------------------------
+ *
+ * - Q.932 (03/93) Generic Procedures for the Control of
+ * ISDN Supplementaty Services
+ * - Q.950 (03/93) Supplementary Services Protocols, Structure and
+ * General Principles
+ * - ETS 300 179 (10/92) Advice Of Charge: charging information during
+ * the call (AOC-D) supplementary service Service description
+ * - ETS 300 180 (10/92) Advice Of Charge: charging information at the
+ * end of call (AOC-E) supplementary service Service description
+ * - ETS 300 181 (04/93) Advice Of Charge (AOC) supplementary service
+ * Functional capabilities and information flows
+ * - ETS 300 182 (04/93) Advice Of Charge (AOC) supplementary service
+ * Digital Subscriber Signalling System No. one (DSS1) protocol
+ * - X.208 Specification of Abstract Syntax Notation One (ASN.1)
+ * - X.209 Specification of Basic Encoding Rules for
+ * Abstract Syntax Notation One (ASN.1)
+ * - "ASN.1 Abstract Syntax Notation One", Walter Gora, DATACOM-Verlag
+ * 1992, 3rd Edition (ISBN 3-89238-062-7) (german !)
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "trace.h"
+#include "q932_fac.h"
+
+static int do_component(int length, char *pbuf);
+static char *uni_str(int code);
+static char *opval_str(int val);
+static char *bid_str(int val);
+static void next_state(char *pbuf, int class, int form, int code, int val);
+
+static void object_id(int comp_length, unsigned char *pbuf);
+
+static int byte_len;
+static unsigned char *byte_buf;
+static int state;
+
+/*---------------------------------------------------------------------------*
+ * decode Q.931/Q.932 facility info element
+ *---------------------------------------------------------------------------*/
+int
+q932_facility(char *pbuf, unsigned char *buf)
+{
+ int len;
+
+ sprintf((pbuf+strlen(pbuf)), "[facility (Q.932): ");
+
+ buf++; /* length */
+
+ len = *buf;
+
+ buf++; /* protocol profile */
+
+ sprintf((pbuf+strlen(pbuf)), "Protocol=");
+
+ switch(*buf & 0x1f)
+ {
+ case FAC_PROTO_ROP:
+ sprintf((pbuf+strlen(pbuf)), "Remote Operations Protocol\n");
+ break;
+
+ case FAC_PROTO_CMIP:
+ sprintf((pbuf+strlen(pbuf)), "CMIP Protocol (Q.941), UNSUPPORTED!\n");
+ return(len+2);
+ break;
+
+ case FAC_PROTO_ACSE:
+ sprintf((pbuf+strlen(pbuf)), "ACSE Protocol (X.217/X.227), UNSUPPORTED!\n");
+ return(len+2);
+ break;
+
+ default:
+ sprintf((pbuf+strlen(pbuf)), "Unknown Protocol (val = 0x%x), UNSUPPORTED!\n", *buf & 0x1f);
+ return(len+2);
+ break;
+ }
+
+ /* next byte */
+
+ buf++;
+ len--;
+
+ /* initialize variables for do_component */
+
+ byte_len = 0;
+ byte_buf = buf;
+ state = ST_EXP_COMP_TYP;
+
+ /* decode facility */
+
+ do_component(len, pbuf);
+
+ sprintf((pbuf+(strlen(pbuf)-1)), "]"); /* XXX replace last newline */
+
+ return(len+3);
+}
+
+/*---------------------------------------------------------------------------*
+ * handle a component recursively
+ *---------------------------------------------------------------------------*/
+static int
+do_component(int length, char *pbuf)
+{
+ int comp_tag_class; /* component tag class */
+ int comp_tag_form; /* component form: constructor or primitive */
+ int comp_tag_code; /* component code depending on class */
+ int comp_length = 0; /* component length */
+
+#ifdef FAC_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "ENTER - comp_length = %d, byte_len = %d, length =%d\n", comp_length, byte_len, length);
+#endif
+
+again:
+
+#ifdef FAC_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "AGAIN - comp_length = %d, byte_len = %d, length =%d\n", comp_length, byte_len, length);
+#endif
+
+ /*----------------------------------------*/
+ /* first component element: component tag */
+ /*----------------------------------------*/
+
+ /* tag class bits */
+
+ sprintf((pbuf+strlen(pbuf)), "\t0x%02x Tag: ", *byte_buf);
+
+ comp_tag_class = (*byte_buf & 0xc0) >> 6;
+
+ switch(comp_tag_class)
+ {
+ case FAC_TAGCLASS_UNI:
+ sprintf((pbuf+strlen(pbuf)), "Universal");
+ break;
+ case FAC_TAGCLASS_APW:
+ sprintf((pbuf+strlen(pbuf)), "Applic-wide");
+ break;
+ case FAC_TAGCLASS_COS:
+ sprintf((pbuf+strlen(pbuf)), "Context-spec");
+ break;
+ case FAC_TAGCLASS_PRU:
+ sprintf((pbuf+strlen(pbuf)), "Private");
+ break;
+ }
+
+ /* tag form bit */
+
+ comp_tag_form = (*byte_buf & 0x20) > 5;
+
+ sprintf((pbuf+strlen(pbuf)), ", ");
+
+ if(comp_tag_form == FAC_TAGFORM_CON)
+ {
+ sprintf((pbuf+strlen(pbuf)), "Constructor");
+ }
+ else
+ {
+ sprintf((pbuf+strlen(pbuf)), "Primitive");
+ }
+
+ /* tag code bits */
+
+ comp_tag_code = *byte_buf & 0x1f;
+
+ sprintf((pbuf+strlen(pbuf)), ", ");
+
+ if(comp_tag_code == 0x1f)
+ {
+ comp_tag_code = 0;
+
+ byte_buf++;
+ byte_len++;
+
+ while(*byte_buf & 0x80)
+ {
+ comp_tag_code += (*byte_buf & 0x7f);
+ byte_buf++;
+ byte_len++;
+ }
+ comp_tag_code += (*byte_buf & 0x7f);
+ sprintf((pbuf+strlen(pbuf)), "%d (ext)\n", comp_tag_code);
+ }
+ else
+ {
+ comp_tag_code = (*byte_buf & 0x1f);
+
+ if(comp_tag_class == FAC_TAGCLASS_UNI)
+ {
+ sprintf((pbuf+strlen(pbuf)), "%s (%d)\n", uni_str(comp_tag_code), comp_tag_code);
+ }
+ else
+ {
+ sprintf((pbuf+strlen(pbuf)), "code = %d\n", comp_tag_code);
+ }
+ }
+
+ byte_buf++;
+ byte_len++;
+
+ /*--------------------------------------------*/
+ /* second component element: component length */
+ /*--------------------------------------------*/
+
+ sprintf((pbuf+strlen(pbuf)), "\t0x%02x Len: ", *byte_buf);
+
+ comp_length = 0;
+
+ if(*byte_buf & 0x80)
+ {
+ int i = *byte_buf & 0x7f;
+
+ byte_len += i;
+
+ for(;i > 0;i++)
+ {
+ byte_buf++;
+ comp_length += (*byte_buf * (i*256));
+ }
+ sprintf((pbuf+strlen(pbuf)), "%d (long form)\n", comp_length);
+ }
+ else
+ {
+ comp_length = *byte_buf & 0x7f;
+ sprintf((pbuf+strlen(pbuf)), "%d (short form)\n", comp_length);
+ }
+
+ next_state(pbuf, comp_tag_class, comp_tag_form, comp_tag_code, -1);
+
+ byte_len++;
+ byte_buf++;
+
+ if(comp_length)
+ {
+
+ /*---------------------------------------------*/
+ /* third component element: component contents */
+ /*---------------------------------------------*/
+
+ if(comp_tag_form) /* == constructor */
+ {
+ do_component(comp_length, pbuf);
+ }
+ else
+ {
+ int val = 0;
+ if(comp_tag_class == FAC_TAGCLASS_UNI)
+ {
+ switch(comp_tag_code)
+ {
+ case FAC_CODEUNI_INT:
+ case FAC_CODEUNI_ENUM:
+ case FAC_CODEUNI_BOOL:
+ if(comp_length)
+ {
+ int i;
+
+ sprintf((pbuf+strlen(pbuf)), "\t");
+
+ for(i = comp_length-1; i >= 0; i--)
+ {
+ sprintf((pbuf+strlen(pbuf)), "0x%02x ", *byte_buf);
+ val += (*byte_buf + (i*255));
+ byte_buf++;
+ byte_len++;
+ if(i)
+ sprintf((pbuf+strlen(pbuf)), "\n\t");
+ }
+ sprintf((pbuf+strlen(pbuf)), "Val: %d\n", val);
+ }
+ break;
+
+ case FAC_CODEUNI_OBJI: /* object id */
+
+ if(comp_length)
+ object_id(comp_length, pbuf);
+ break;
+
+ default:
+ if(comp_length)
+ {
+ int i;
+
+ sprintf((pbuf+strlen(pbuf)), "\t");
+
+ for(i = comp_length-1; i >= 0; i--)
+ {
+ sprintf((pbuf+strlen(pbuf)), "0x%02x = %d", *byte_buf, *byte_buf);
+ if(isprint(*byte_buf))
+ sprintf((pbuf+strlen(pbuf)), " = '%c'", *byte_buf);
+ byte_buf++;
+ byte_len++;
+ if(i)
+ sprintf((pbuf+strlen(pbuf)), "\n\t");
+ }
+ }
+ break;
+ }
+ }
+
+ else /* comp_tag_class != FAC_TAGCLASS_UNI */
+ {
+ if(comp_length)
+ {
+ int i;
+
+ sprintf((pbuf+strlen(pbuf)), "\t");
+
+ for(i = comp_length-1; i >= 0; i--)
+ {
+ sprintf((pbuf+strlen(pbuf)), "0x%02x", *byte_buf);
+ val += (*byte_buf + (i*255));
+ byte_buf++;
+ byte_len++;
+ if(i)
+ sprintf((pbuf+strlen(pbuf)), "\n\t");
+ }
+ sprintf((pbuf+strlen(pbuf)), "\n");
+ }
+ }
+ next_state(pbuf, comp_tag_class, comp_tag_form, comp_tag_code, val);
+ }
+ }
+
+#ifdef FAC_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "PREGOTO - comp_length = %d, byte_len = %d, length =%d\n", comp_length, byte_len, length);
+#endif
+ if(byte_len < length)
+ goto again;
+#ifdef FAC_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "RETURN - comp_length = %d, byte_len = %d, length =%d\n", comp_length, byte_len, length);
+#endif
+ return(byte_len);
+}
+
+/*---------------------------------------------------------------------------*
+ * print universal id type
+ *---------------------------------------------------------------------------*/
+static char *uni_str(int code)
+{
+ static char *tbl[] = {
+ "BOOLEAN",
+ "INTEGER",
+ "BIT STRING",
+ "OCTET STRING",
+ "NULL",
+ "OBJECT IDENTIFIER",
+ "OBJECT DESCRIPTOR",
+ "EXTERNAL",
+ "REAL",
+ "ENUMERATED",
+ "RESERVED11",
+ "RESERVED12",
+ "RESERVED13",
+ "RESERVED14",
+ "RESERVED15",
+ "SEQUENCE",
+ "SET",
+ "NUMERIC STRING",
+ "PRINTABLE STRING",
+ "TELETEX STRING",
+ "ISO646 STRING",
+ "IA5 STRING",
+ "GRAPHIC STRING",
+ "GENERAL STRING"
+ };
+
+ if(code >= 1 && code <= FAC_CODEUNI_GNSTR)
+ return(tbl[code-1]);
+ else
+ return("ERROR, Value out of Range!");
+}
+
+/*---------------------------------------------------------------------------*
+ * print operation value
+ *---------------------------------------------------------------------------*/
+static char *opval_str(int val)
+{
+ static char buffer[80];
+ char *r;
+
+ switch(val)
+ {
+ case FAC_OPVAL_UUS:
+ r = "uUs";
+ break;
+ case FAC_OPVAL_CUG:
+ r = "cUGCall";
+ break;
+ case FAC_OPVAL_MCID:
+ r = "mCIDRequest";
+ break;
+ case FAC_OPVAL_BTPY:
+ r = "beginTPY";
+ break;
+ case FAC_OPVAL_ETPY:
+ r = "endTPY";
+ break;
+ case FAC_OPVAL_ECT:
+ r = "eCTRequest";
+ break;
+ case FAC_OPVAL_DIV_ACT:
+ r = "activationDiversion";
+ break;
+ case FAC_OPVAL_DIV_DEACT:
+ r = "deactivationDiversion";
+ break;
+ case FAC_OPVAL_DIV_ACTSN:
+ r = "activationStatusNotificationDiv";
+ break;
+ case FAC_OPVAL_DIV_DEACTSN:
+ r = "deactivationStatusNotificationDiv";
+ break;
+ case FAC_OPVAL_DIV_INTER:
+ r = "interrogationDiversion";
+ break;
+ case FAC_OPVAL_DIV_INFO:
+ r = "diversionInformation";
+ break;
+ case FAC_OPVAL_DIV_CALLDEF:
+ r = "callDeflection";
+ break;
+ case FAC_OPVAL_DIV_CALLRER:
+ r = "callRerouting";
+ break;
+ case FAC_OPVAL_DIV_LINF2:
+ r = "divertingLegInformation2";
+ break;
+ case FAC_OPVAL_DIV_INVS:
+ r = "invokeStatus";
+ break;
+ case FAC_OPVAL_DIV_INTER1:
+ r = "interrogationDiversion1";
+ break;
+ case FAC_OPVAL_DIV_LINF1:
+ r = "divertingLegInformation1";
+ break;
+ case FAC_OPVAL_DIV_LINF3:
+ r = "divertingLegInformation3";
+ break;
+ case FAC_OPVAL_ER_CRCO:
+ r = "explicitReservationCreationControl";
+ break;
+ case FAC_OPVAL_ER_MGMT:
+ r = "explicitReservationManagement";
+ break;
+ case FAC_OPVAL_ER_CANC:
+ r = "explicitReservationCancel";
+ break;
+ case FAC_OPVAL_MLPP_QUERY:
+ r = "mLPP lfb Query";
+ break;
+ case FAC_OPVAL_MLPP_CALLR:
+ r = "mLPP Call Request";
+ break;
+ case FAC_OPVAL_MLPP_CALLP:
+ r = "mLPP Call Preemption";
+ break;
+ case FAC_OPVAL_AOC_REQ:
+ r = "chargingRequest";
+ break;
+ case FAC_OPVAL_AOC_S_CUR:
+ r = "aOCSCurrency";
+ break;
+ case FAC_OPVAL_AOC_S_SPC:
+ r = "aOCSSpecialArrangement";
+ break;
+ case FAC_OPVAL_AOC_D_CUR:
+ r = "aOCDCurrency";
+ break;
+ case FAC_OPVAL_AOC_D_UNIT:
+ r = "aOCDChargingUnit";
+ break;
+ case FAC_OPVAL_AOC_E_CUR:
+ r = "aOCECurrency";
+ break;
+ case FAC_OPVAL_AOC_E_UNIT:
+ r = "aOCEChargingUnit";
+ break;
+ case FAC_OPVAL_AOC_IDOFCRG:
+ r = "identificationOfCharge";
+ break;
+ case FAC_OPVAL_CONF_BEG:
+ r = "beginConf";
+ break;
+ case FAC_OPVAL_CONF_ADD:
+ r = "addConf";
+ break;
+ case FAC_OPVAL_CONF_SPLIT:
+ r = "splitConf";
+ break;
+ case FAC_OPVAL_CONF_DROP:
+ r = "dropConf";
+ break;
+ case FAC_OPVAL_CONF_ISOLATE:
+ r = "isolateConf";
+ break;
+ case FAC_OPVAL_CONF_REATT:
+ r = "reattachConf";
+ break;
+ case FAC_OPVAL_CONF_PDISC:
+ r = "partyDISC";
+ break;
+ case FAC_OPVAL_CONF_FCONF:
+ r = "floatConf";
+ break;
+ case FAC_OPVAL_CONF_END:
+ r = "endConf";
+ break;
+ case FAC_OPVAL_CONF_IDCFE:
+ r = "indentifyConferee";
+ break;
+ case FAC_OPVAL_REVC_REQ:
+ r = "requestREV";
+ break;
+ default:
+ sprintf(buffer, "unknown operation value %d!", val);
+ r = buffer;
+ }
+ return(r);
+}
+
+/*---------------------------------------------------------------------------*
+ * billing id string
+ *---------------------------------------------------------------------------*/
+static char *bid_str(int val)
+{
+ static char buffer[80];
+ char *r;
+
+ switch(val)
+ {
+ case 0:
+ r = "normalCharging";
+ break;
+ case 1:
+ r = "reverseCharging";
+ break;
+ case 2:
+ r = "creditCardCharging";
+ break;
+ case 3:
+ r = "callForwardingUnconditional";
+ break;
+ case 4:
+ r = "callForwardingBusy";
+ break;
+ case 5:
+ r = "callForwardingNoReply";
+ break;
+ case 6:
+ r = "callDeflection";
+ break;
+ case 7:
+ r = "callTransfer";
+ break;
+ default:
+ sprintf(buffer, "unknown billing-id value %d!", val);
+ r = buffer;
+ }
+ return(r);
+}
+
+/*---------------------------------------------------------------------------*
+ * invoke component
+ *---------------------------------------------------------------------------*/
+static void
+F_1_1(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_1_1, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t invokeComponent\n");
+ state = ST_EXP_INV_ID;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * return result
+ *---------------------------------------------------------------------------*/
+static void
+F_1_2(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_1_2, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t returnResult\n");
+ state = ST_EXP_RR_INV_ID;
+ }
+}
+/*---------------------------------------------------------------------------*
+ * return error
+ *---------------------------------------------------------------------------*/
+static void
+F_1_3(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_1_3, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t returnError\n");
+ state = ST_EXP_NIX;
+ }
+}
+/*---------------------------------------------------------------------------*
+ * reject
+ *---------------------------------------------------------------------------*/
+static void
+F_1_4(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_1_4, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t reject\n");
+ state = ST_EXP_REJ_INV_ID;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * return result: invoke id
+ *---------------------------------------------------------------------------*/
+static void
+F_RJ2(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_RJ2, val = %d\n", val);
+#endif
+ if(val != -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t InvokeIdentifier = %d\n", val);
+ state = ST_EXP_REJ_OP_VAL;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * reject, general problem
+ *---------------------------------------------------------------------------*/
+static void
+F_RJ30(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_RJ30, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t General problem\n");
+ }
+ else
+ {
+ switch(val)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unrecognized component\n");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = mistyped component\n");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = badly structured component\n");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unknown problem code 0x%x\n", val);
+ break;
+ }
+ state = ST_EXP_NIX;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * reject, invoke problem
+ *---------------------------------------------------------------------------*/
+static void
+F_RJ31(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_RJ31, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t Invoke problem\n");
+ }
+ else
+ {
+ switch(val)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = duplicate invocation\n");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unrecognized operation\n");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = mistyped argument\n");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = resource limitation\n");
+ break;
+ case 4:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = initiator releasing\n");
+ break;
+ case 5:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unrecognized linked identifier\n");
+ break;
+ case 6:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = linked resonse unexpected\n");
+ break;
+ case 7:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unexpected child operation\n");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unknown problem code 0x%x\n", val);
+ break;
+ }
+ state = ST_EXP_NIX;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * reject, return result problem
+ *---------------------------------------------------------------------------*/
+static void
+F_RJ32(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_RJ32, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t Return result problem\n");
+ }
+ else
+ {
+ switch(val)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unrecognized invocation\n");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = return response unexpected\n");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = mistyped result\n");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unknown problem code 0x%x\n", val);
+ break;
+ }
+ state = ST_EXP_NIX;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * reject, return error problem
+ *---------------------------------------------------------------------------*/
+static void
+F_RJ33(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_RJ33, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t Return error problem\n");
+ }
+ else
+ {
+ switch(val)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unrecognized invocation\n");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = error response unexpected\n");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unrecognized error\n");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unexpected error\n");
+ break;
+ case 4:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = mistyped parameter\n");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unknown problem code 0x%x\n", val);
+ break;
+ }
+ state = ST_EXP_NIX;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * invoke component: invoke id
+ *---------------------------------------------------------------------------*/
+static void
+F_2(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_2, val = %d\n", val);
+#endif
+ if(val != -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t InvokeIdentifier = %d\n", val);
+ state = ST_EXP_OP_VAL;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * return result: invoke id
+ *---------------------------------------------------------------------------*/
+static void
+F_RR2(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_RR2, val = %d\n", val);
+#endif
+ if(val != -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t InvokeIdentifier = %d\n", val);
+ state = ST_EXP_RR_OP_VAL;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * invoke component: operation value
+ *---------------------------------------------------------------------------*/
+static void
+F_3(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_3, val = %d\n", val);
+#endif
+ if(val != -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t Operation Value = %s (%d)\n", opval_str(val), val);
+ state = ST_EXP_INFO;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * return result: operation value
+ *---------------------------------------------------------------------------*/
+static void
+F_RR3(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_RR3, val = %d\n", val);
+#endif
+ if(val != -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t Operation Value = %s (%d)\n", opval_str(val), val);
+ state = ST_EXP_RR_RESULT;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * return result: RESULT
+ *---------------------------------------------------------------------------*/
+static void
+F_RRR(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_RRR, val = %d\n", val);
+#endif
+ state = ST_EXP_NIX;
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+static void
+F_4(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_4, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t specificChargingUnits\n");
+ state = ST_EXP_RUL;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+static void
+F_4_1(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_4_1, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t freeOfCharge\n");
+ state = ST_EXP_NIX;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+static void
+F_4_2(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_4_2, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t chargeNotAvailable\n");
+ state = ST_EXP_NIX;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+static void
+F_5(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_5, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t recordedUnitsList [1]\n");
+ state = ST_EXP_RU;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+static void
+F_6(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_6, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t RecordedUnits\n");
+ state = ST_EXP_RNOU;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+static void
+F_7(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_7, val = %d\n", val);
+#endif
+ if(val != -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t NumberOfUnits = %d\n", val);
+ state = ST_EXP_TOCI;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+static void
+F_8(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_8, val = %d\n", val);
+#endif
+ if(val != -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t typeOfChargingInfo = %s\n", val == 0 ? "subTotal" : "total");
+ state = ST_EXP_DBID;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+static void
+F_9(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_9, val = %d\n", val);
+#endif
+ if(val != -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t AOCDBillingId = %s (%d)\n", bid_str(val), val);
+ state = ST_EXP_NIX;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * state table
+ *---------------------------------------------------------------------------*/
+static struct statetab {
+ int currstate; /* input: current state we are in */
+ int form; /* input: current tag form */
+ int class; /* input: current tag class */
+ int code; /* input: current tag code */
+ void (*func)(char *,int); /* output: func to exec */
+} statetab[] = {
+
+/* current state tag form tag class tag code function */
+/* --------------------- ---------------------- ---------------------- ---------------------- ----------------*/
+
+/* invoke */
+
+ {ST_EXP_COMP_TYP, FAC_TAGFORM_CON, FAC_TAGCLASS_COS, 1, F_1_1 },
+ {ST_EXP_COMP_TYP, FAC_TAGFORM_CON, FAC_TAGCLASS_COS, 2, F_1_2 },
+ {ST_EXP_COMP_TYP, FAC_TAGFORM_CON, FAC_TAGCLASS_COS, 3, F_1_3 },
+ {ST_EXP_COMP_TYP, FAC_TAGFORM_CON, FAC_TAGCLASS_COS, 4, F_1_4 },
+ {ST_EXP_INV_ID, FAC_TAGFORM_PRI, FAC_TAGCLASS_UNI, FAC_CODEUNI_INT, F_2 },
+ {ST_EXP_OP_VAL, FAC_TAGFORM_PRI, FAC_TAGCLASS_UNI, FAC_CODEUNI_INT, F_3 },
+ {ST_EXP_INFO, FAC_TAGFORM_CON, FAC_TAGCLASS_UNI, FAC_CODEUNI_SEQ, F_4 },
+ {ST_EXP_INFO, FAC_TAGFORM_PRI, FAC_TAGCLASS_UNI, FAC_CODEUNI_NULL, F_4_1 },
+ {ST_EXP_INFO, FAC_TAGFORM_PRI, FAC_TAGCLASS_COS, 1, F_4_2 },
+ {ST_EXP_RUL, FAC_TAGFORM_CON, FAC_TAGCLASS_COS, 1, F_5 },
+ {ST_EXP_RU, FAC_TAGFORM_CON, FAC_TAGCLASS_UNI, FAC_CODEUNI_SEQ, F_6 },
+ {ST_EXP_RNOU, FAC_TAGFORM_PRI, FAC_TAGCLASS_UNI, FAC_CODEUNI_INT, F_7 },
+ {ST_EXP_TOCI, FAC_TAGFORM_PRI, FAC_TAGCLASS_COS, 2, F_8 },
+ {ST_EXP_DBID, FAC_TAGFORM_PRI, FAC_TAGCLASS_COS, 3, F_9 },
+
+/* return result */
+
+ {ST_EXP_RR_INV_ID, FAC_TAGFORM_PRI, FAC_TAGCLASS_UNI, FAC_CODEUNI_INT, F_RR2 },
+ {ST_EXP_RR_OP_VAL, FAC_TAGFORM_PRI, FAC_TAGCLASS_UNI, FAC_CODEUNI_INT, F_RR3 },
+ {ST_EXP_RR_RESULT, FAC_TAGFORM_CON, FAC_TAGCLASS_UNI, FAC_CODEUNI_SET, F_RRR },
+
+/* current state tag form tag class tag code function */
+/* --------------------- ---------------------- ---------------------- ---------------------- ----------------*/
+/* reject */
+
+ {ST_EXP_REJ_INV_ID, FAC_TAGFORM_PRI, FAC_TAGCLASS_UNI, FAC_CODEUNI_INT, F_RJ2 },
+ {ST_EXP_REJ_OP_VAL, FAC_TAGFORM_PRI, FAC_TAGCLASS_COS, 0, F_RJ30 },
+ {ST_EXP_REJ_OP_VAL, FAC_TAGFORM_PRI, FAC_TAGCLASS_COS, 1, F_RJ31 },
+ {ST_EXP_REJ_OP_VAL, FAC_TAGFORM_PRI, FAC_TAGCLASS_COS, 2, F_RJ32 },
+ {ST_EXP_REJ_OP_VAL, FAC_TAGFORM_PRI, FAC_TAGCLASS_COS, 3, F_RJ33 },
+
+/* end */
+
+ {-1, -1, -1, -1, NULL }
+};
+
+/*---------------------------------------------------------------------------*
+ * state decode for do_component
+ *---------------------------------------------------------------------------*/
+static void
+next_state(char *pbuf, int class, int form, int code, int val)
+{
+ int i;
+
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: class=%d, form=%d, code=%d, val=%d\n", class, form, code, val);
+#endif
+
+ for(i=0; ; i++)
+ {
+ if((statetab[i].currstate > state) ||
+ (statetab[i].currstate == -1))
+ {
+ break;
+ }
+
+ if((statetab[i].currstate == state) &&
+ (statetab[i].form == form) &&
+ (statetab[i].class == class) &&
+ (statetab[i].code == code))
+ {
+ (*statetab[i].func)(pbuf, val);
+ break;
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * decode OBJECT IDENTIFIER
+ *---------------------------------------------------------------------------*/
+static void
+object_id(int comp_length, unsigned char *pbuf)
+{
+ int x;
+ int i;
+ int j = 0;
+ int id_org = 0;
+ int etsi = 0;
+
+ sprintf((pbuf+strlen(pbuf)), "\t");
+
+ for(i = comp_length-1; i >= 0; i--, j++)
+ {
+ sprintf((pbuf+strlen(pbuf)), "0x%02x = %d", *byte_buf, *byte_buf);
+
+ if(j == 0)
+ {
+ x = *byte_buf;
+
+ if(x >= 0 && x <= 39)
+ {
+ sprintf((pbuf+strlen(pbuf)), " ccitt/itu-t (0)");
+ switch(x)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), " recommendation (0)");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), " question (1)");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), " administration (2)");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), " network-operator (3)");
+ break;
+ case 4:
+ sprintf((pbuf+strlen(pbuf)), " identified-organization (4)");
+ id_org = 1;
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), " error: undefined-identifier (%d)", x);
+ break;
+ }
+ }
+ else if(x >= 40 && x <= 79)
+ {
+ sprintf((pbuf+strlen(pbuf)), " iso (1)");
+ x -= 40;
+ switch(x)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), " standard (0)");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), " registration-authority (1)");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), " member-body (2)");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), " identified-organization (3)");
+ id_org = 1;
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), " error: undefined-identifier (%d)", x);
+ break;
+ }
+ }
+ else
+ {
+ x -= 80;
+ sprintf((pbuf+strlen(pbuf)), " joint-iso-ccitt (3) ??? (%d)", x);
+ }
+ }
+
+ if(j == 1)
+ {
+ if(id_org == 1)
+ {
+ if(*byte_buf == 0)
+ {
+ sprintf((pbuf+strlen(pbuf)), " etsi (0)");
+ etsi = 1;
+ }
+ }
+ }
+
+ if(j == 2)
+ {
+ if(etsi == 1)
+ {
+ if(*byte_buf == 0)
+ {
+ sprintf((pbuf+strlen(pbuf)), " mobileDomain (0)");
+ }
+ if(*byte_buf == 1)
+ {
+ sprintf((pbuf+strlen(pbuf)), " inDomain (1)");
+ }
+ }
+ }
+
+ byte_buf++;
+ byte_len++;
+
+ if(i)
+ sprintf((pbuf+strlen(pbuf)), "\n\t");
+ else
+ sprintf((pbuf+strlen(pbuf)), "\n");
+ }
+}
+
+/* EOF */
+
diff --git a/usr.sbin/i4b/isdntrace/q932_fac.h b/usr.sbin/i4b/isdntrace/q932_fac.h
new file mode 100644
index 0000000..563de3a
--- /dev/null
+++ b/usr.sbin/i4b/isdntrace/q932_fac.h
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 1997, 2000 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * q932_fac.h - facility header file
+ * ---------------------------------
+ *
+ * $Id: q932_fac.h,v 1.7 2000/02/18 16:27:39 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Fri Feb 18 17:26:07 2000]
+ *
+ *---------------------------------------------------------------------------
+ *
+ * - Q.932 (03/93) Generic Procedures for the Control of
+ * ISDN Supplementaty Services
+ * - Q.950 (03/93) Supplementary Services Protocols, Structure and
+ * General Principles
+ * - ETS 300 179 (10/92) Advice Of Charge: charging information during
+ * the call (AOC-D) supplementary service Service description
+ * - ETS 300 180 (10/92) Advice Of Charge: charging information at the
+ * end of call (AOC-E) supplementary service Service description
+ * - ETS 300 181 (04/93) Advice Of Charge (AOC) supplementary service
+ * Functional capabilities and information flows
+ * - ETS 300 182 (04/93) Advice Of Charge (AOC) supplementary service
+ * Digital Subscriber Signalling System No. one (DSS1) protocol
+ * - X.208 Specification of Abstract Syntax Notation One (ASN.1)
+ * - X.209 Specification of Basic Encoding Rules for
+ * Abstract Syntax Notation One (ASN.1)
+ * - "ASN.1 Abstract Syntax Notation One", Walter Gora, DATACOM-Verlag
+ * 1992, 3rd Edition (ISBN 3-89238-062-7) (german !)
+ *
+ *---------------------------------------------------------------------------*/
+
+/* #define FAC_DEBUG */
+/* #define ST_DEBUG */
+
+/* protocols */
+#define FAC_PROTO_ROP 0x11
+#define FAC_PROTO_CMIP 0x12
+#define FAC_PROTO_ACSE 0x13
+
+/* tag classes */
+#define FAC_TAGCLASS_UNI 0x00
+#define FAC_TAGCLASS_APW 0x01
+#define FAC_TAGCLASS_COS 0x02
+#define FAC_TAGCLASS_PRU 0x03
+
+/* tag forms */
+#define FAC_TAGFORM_PRI 0x00
+#define FAC_TAGFORM_CON 0x01
+
+/* class UNIVERSAL values */
+#define FAC_CODEUNI_BOOL 1
+#define FAC_CODEUNI_INT 2
+#define FAC_CODEUNI_BITS 3
+#define FAC_CODEUNI_OCTS 4
+#define FAC_CODEUNI_NULL 5
+#define FAC_CODEUNI_OBJI 6
+#define FAC_CODEUNI_OBJD 7
+#define FAC_CODEUNI_EXT 8
+#define FAC_CODEUNI_REAL 9
+#define FAC_CODEUNI_ENUM 10
+#define FAC_CODEUNI_R11 11
+#define FAC_CODEUNI_R12 12
+#define FAC_CODEUNI_R13 13
+#define FAC_CODEUNI_R14 14
+#define FAC_CODEUNI_R15 15
+#define FAC_CODEUNI_SEQ 16
+#define FAC_CODEUNI_SET 17
+#define FAC_CODEUNI_NSTR 18
+#define FAC_CODEUNI_PSTR 19
+#define FAC_CODEUNI_TSTR 20
+#define FAC_CODEUNI_VSTR 21
+#define FAC_CODEUNI_ISTR 22
+#define FAC_CODEUNI_UTIME 23
+#define FAC_CODEUNI_GTIME 24
+#define FAC_CODEUNI_GSTR 25
+#define FAC_CODEUNI_VISTR 26
+#define FAC_CODEUNI_GNSTR 27
+
+/* operation values */
+#define FAC_OPVAL_UUS 1
+#define FAC_OPVAL_CUG 2
+#define FAC_OPVAL_MCID 3
+#define FAC_OPVAL_BTPY 4
+#define FAC_OPVAL_ETPY 5
+#define FAC_OPVAL_ECT 6
+
+#define FAC_OPVAL_DIV_ACT 7
+#define FAC_OPVAL_DIV_DEACT 8
+#define FAC_OPVAL_DIV_ACTSN 9
+#define FAC_OPVAL_DIV_DEACTSN 10
+#define FAC_OPVAL_DIV_INTER 11
+#define FAC_OPVAL_DIV_INFO 12
+#define FAC_OPVAL_DIV_CALLDEF 13
+#define FAC_OPVAL_DIV_CALLRER 14
+#define FAC_OPVAL_DIV_LINF2 15
+#define FAC_OPVAL_DIV_INVS 16
+#define FAC_OPVAL_DIV_INTER1 17
+#define FAC_OPVAL_DIV_LINF1 18
+#define FAC_OPVAL_DIV_LINF3 19
+
+#define FAC_OPVAL_ER_CRCO 20
+#define FAC_OPVAL_ER_MGMT 21
+#define FAC_OPVAL_ER_CANC 22
+
+#define FAC_OPVAL_MLPP_QUERY 24
+#define FAC_OPVAL_MLPP_CALLR 25
+#define FAC_OPVAL_MLPP_CALLP 26
+
+#define FAC_OPVAL_AOC_REQ 30
+#define FAC_OPVAL_AOC_S_CUR 31
+#define FAC_OPVAL_AOC_S_SPC 32
+#define FAC_OPVAL_AOC_D_CUR 33
+#define FAC_OPVAL_AOC_D_UNIT 34
+#define FAC_OPVAL_AOC_E_CUR 35
+#define FAC_OPVAL_AOC_E_UNIT 36
+#define FAC_OPVAL_AOC_IDOFCRG 37
+
+#define FAC_OPVAL_CONF_BEG 40
+#define FAC_OPVAL_CONF_ADD 41
+#define FAC_OPVAL_CONF_SPLIT 42
+#define FAC_OPVAL_CONF_DROP 43
+#define FAC_OPVAL_CONF_ISOLATE 44
+#define FAC_OPVAL_CONF_REATT 45
+#define FAC_OPVAL_CONF_PDISC 46
+#define FAC_OPVAL_CONF_FCONF 47
+#define FAC_OPVAL_CONF_END 48
+#define FAC_OPVAL_CONF_IDCFE 49
+
+#define FAC_OPVAL_REVC_REQ 60
+
+enum states {
+ ST_EXP_COMP_TYP,
+ ST_EXP_INV_ID,
+ ST_EXP_OP_VAL,
+ ST_EXP_INFO,
+ ST_EXP_RUL,
+ ST_EXP_RU,
+ ST_EXP_RNOU,
+ ST_EXP_TOCI,
+ ST_EXP_DBID,
+
+ ST_EXP_RR_INV_ID,
+ ST_EXP_RR_OP_VAL,
+ ST_EXP_RR_RESULT,
+
+ ST_EXP_REJ_INV_ID,
+ ST_EXP_REJ_OP_VAL,
+ ST_EXP_REJ_RESULT,
+
+ ST_EXP_NIX
+};
+
+/* EOF */
+
diff --git a/usr.sbin/i4b/isdntrace/trace.c b/usr.sbin/i4b/isdntrace/trace.c
new file mode 100644
index 0000000..2f1d71f
--- /dev/null
+++ b/usr.sbin/i4b/isdntrace/trace.c
@@ -0,0 +1,850 @@
+/*
+ * Copyright (c) 1996, 2000 Hellmuth Michaelis. All rights reserved.
+ *
+ * Copyright (c) 1996 Gary Jennejohn. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ * 4. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software and/or documentation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------*
+ *
+ * trace.c - print traces of D (B) channel activity for isdn4bsd
+ * -------------------------------------------------------------
+ *
+ * $Id: trace.c,v 1.19 2000/08/28 07:06:42 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Aug 28 09:03:46 2000]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "trace.h"
+
+unsigned char buf[BSIZE];
+FILE *Fout = NULL;
+FILE *BP = NULL;
+int outflag = 1;
+int header = 1;
+int print_q921 = 1;
+int unit = 0;
+int dchan = 0;
+int bchan = 0;
+int traceon = 0;
+int analyze = 0;
+int Rx = RxUDEF;
+int Tx = TxUDEF;
+int f;
+int Bopt = 0;
+int Popt = 0;
+int bpopt = 0;
+int info = 0;
+int Fopt = 0;
+int xopt = 1;
+
+int enable_trace = TRACE_D_RX | TRACE_D_TX;
+
+static char outfilename[MAXPATHLEN];
+static char routfilename[MAXPATHLEN];
+static char BPfilename[MAXPATHLEN];
+static char rBPfilename[MAXPATHLEN];
+
+static struct stat fst;
+
+static void dumpbuf( int n, unsigned char *buf, i4b_trace_hdr_t *hdr, int raw );
+static int switch_driver( int value, int rx, int tx );
+static void usage( void );
+static void exit_hdl( void );
+static void reopenfiles( int );
+void add_datetime(char *filename, char *rfilename);
+
+/*---------------------------------------------------------------------------*
+ * usage instructions
+ *---------------------------------------------------------------------------*/
+void
+usage(void)
+{
+ fprintf(stderr,"\n");
+ fprintf(stderr,"isdntrace - i4b package ISDN trace facility for passive cards (%02d.%02d.%d)\n", VERSION, REL, STEP);
+ fprintf(stderr,"usage: isdntrace -a -b -d -f <file> -h -i -l -n <val> -o -p <file> -r -u <unit>\n");
+ fprintf(stderr," -x -B -F -P -R <unit> -T <unit>\n");
+ fprintf(stderr," -a analyzer mode ................................... (default off)\n");
+ fprintf(stderr," -b switch B channel trace on ....................... (default off)\n");
+ fprintf(stderr," -d switch D channel trace off ....................... (default on)\n");
+ fprintf(stderr," -f <file> write output to file filename ............ (default %s0)\n", TRACE_FILE_NAME);
+ fprintf(stderr," -h don't print header for each message ............. (default off)\n");
+ fprintf(stderr," -i print I.430 (layer 1) INFO signals .............. (default off)\n");
+ fprintf(stderr," -l don't decode low layer Q.921 messages ........... (default off)\n");
+ fprintf(stderr," -n <val> process packet if it is longer than <val> octetts . (default 0)\n");
+ fprintf(stderr," -o don't write output to a file .................... (default off)\n");
+ fprintf(stderr," -p <file> specify filename for -B and -P ........ (default %s0)\n", BIN_FILE_NAME);
+ fprintf(stderr," -r don't print raw hex/ASCII dump of protocol ...... (default off)\n");
+ fprintf(stderr," -u <unit> specify controller unit number ............... (default unit 0)\n");
+ fprintf(stderr," -x show packets with unknown protocol discriminator (default off)\n");
+ fprintf(stderr," -B write binary trace data to file filename ........ (default off)\n");
+ fprintf(stderr," -F with -P and -p: wait for more data at EOF ....... (default off)\n");
+ fprintf(stderr," -P playback from binary trace data file ............ (default off)\n");
+ fprintf(stderr," -R <unit> analyze Rx controller unit number (for -a) ... (default unit %d)\n", RxUDEF);
+ fprintf(stderr," -T <unit> analyze Tx controller unit number (for -a) ... (default unit %d)\n", TxUDEF);
+ fprintf(stderr,"\n");
+ exit(1);
+}
+
+/*---------------------------------------------------------------------------*
+ * main
+ *---------------------------------------------------------------------------*/
+int
+main(int argc, char *argv[])
+{
+ extern int optind;
+ extern int opterr;
+ extern char *optarg;
+ char devicename[80];
+ char headerbuf[256];
+
+ int n;
+ int c;
+ char *b;
+
+ char *outfile = TRACE_FILE_NAME;
+ char *binfile = BIN_FILE_NAME;
+ int outfileset = 0;
+ int raw = 1;
+ int noct = -1;
+ time_t tm;
+ i4b_trace_hdr_t *ithp = NULL;
+ int l;
+ static struct stat fstnew;
+
+ b = &buf[sizeof(i4b_trace_hdr_t)];
+
+ while( (c = getopt(argc, argv, "abdf:hiln:op:ru:xBFPR:T:")) != -1)
+ {
+ switch(c)
+ {
+ case 'a':
+ analyze = 1;
+ break;
+
+ case 'b':
+ enable_trace |= (TRACE_B_RX | TRACE_B_TX);
+ break;
+
+ case 'd':
+ enable_trace &= (~(TRACE_D_TX | TRACE_D_RX));
+ break;
+
+ case 'o':
+ outflag = 0;
+ break;
+
+ case 'f':
+ outfile = optarg;
+ outfileset = 1;
+ break;
+
+ case 'n':
+ noct = atoi(optarg);
+ break;
+
+ case 'h':
+ header = 0;
+ break;
+
+ case 'i':
+ enable_trace |= TRACE_I;
+ info = 1;
+ break;
+
+ case 'l':
+ print_q921 = 0;
+ break;
+
+ case 'p':
+ binfile = optarg;
+ bpopt = 1;
+ break;
+
+ case 'r':
+ raw = 0;
+ break;
+
+ case 'u':
+ unit = atoi(optarg);
+ if(unit < 0 || unit >= MAX_CONTROLLERS)
+ usage();
+ break;
+
+ case 'x':
+ xopt = 0;
+ break;
+
+ case 'B':
+ Bopt = 1;
+ break;
+
+ case 'F':
+ Fopt = 1;
+ break;
+
+ case 'P':
+ Popt = 1;
+ break;
+
+ case 'R':
+ Rx = atoi(optarg);
+ if(Rx < 0 || Rx >= MAX_CONTROLLERS)
+ usage();
+ break;
+
+ case 'T':
+ Tx = atoi(optarg);
+ if(Tx < 0 || Tx >= MAX_CONTROLLERS)
+ usage();
+ break;
+
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if(enable_trace == 0)
+ usage();
+
+ if(Bopt && Popt)
+ usage();
+
+ atexit(exit_hdl);
+
+ if(Bopt)
+ {
+ if(bpopt)
+ sprintf(BPfilename, "%s", binfile);
+ else
+ sprintf(BPfilename, "%s%d", BIN_FILE_NAME, unit);
+
+ add_datetime(BPfilename, rBPfilename);
+
+ if((BP = fopen(rBPfilename, "w")) == NULL)
+ {
+ char buffer[80];
+
+ sprintf(buffer, "Error opening file [%s]", rBPfilename);
+ perror(buffer);
+ exit(1);
+ }
+
+ if((setvbuf(BP, (char *)NULL, _IONBF, 0)) != 0)
+ {
+ char buffer[80];
+
+ sprintf(buffer, "Error setting file [%s] to unbuffered", rBPfilename);
+ perror(buffer);
+ exit(1);
+ }
+ }
+
+ if(Popt)
+ {
+ if(bpopt)
+ sprintf(BPfilename, "%s", binfile);
+ else
+ sprintf(BPfilename, "%s%d", BIN_FILE_NAME, unit);
+
+ strcpy(rBPfilename, BPfilename);
+
+ if((BP = fopen(BPfilename, "r")) == NULL)
+ {
+ char buffer[80];
+
+ sprintf(buffer, "Error opening file [%s]", BPfilename);
+ perror(buffer);
+ exit(1);
+ }
+ if(Fopt)
+ {
+ if(fstat(fileno(BP), &fst))
+ {
+ char buffer[80];
+ sprintf(buffer, "Error fstat file [%s]", BPfilename);
+ perror(buffer);
+ exit(1);
+ }
+ }
+ }
+ else
+ {
+ sprintf(devicename, "%s%d", I4BTRC_DEVICE, unit);
+
+ if((f = open(devicename, O_RDWR)) < 0)
+ {
+ char buffer[80];
+
+ sprintf(buffer, "Error opening trace device [%s]", devicename);
+ perror(buffer);
+ exit(1);
+ }
+ }
+
+ if(outflag)
+ {
+ if(outfileset == 0)
+ sprintf(outfilename, "%s%d", TRACE_FILE_NAME, unit);
+ else
+ strcpy(outfilename, outfile);
+
+ add_datetime(outfilename, routfilename);
+
+ if((Fout = fopen(routfilename, "w")) == NULL)
+ {
+ char buffer[80];
+
+ sprintf(buffer, "Error opening file [%s]", routfilename);
+ perror(buffer);
+ exit(1);
+ }
+
+ if((setvbuf(Fout, (char *)NULL, _IONBF, 0)) != 0)
+ {
+ char buffer[80];
+
+ sprintf(buffer, "Error setting file [%s] to unbuffered", routfilename);
+ perror(buffer);
+ exit(1);
+ }
+ }
+
+ if((setvbuf(stdout, (char *)NULL, _IOLBF, 0)) != 0)
+ {
+ char buffer[80];
+
+ sprintf(buffer, "Error setting stdout to line-buffered");
+ perror(buffer);
+ exit(1);
+ }
+
+ if(!Popt)
+ {
+ if((switch_driver(enable_trace, Rx, Tx)) == -1)
+ exit(1);
+ else
+ traceon = 1;
+ }
+
+ signal(SIGHUP, SIG_IGN); /* ignore hangup signal */
+ signal(SIGUSR1, reopenfiles); /* rotate logfile(s) */
+
+ time(&tm);
+
+ if(analyze)
+ {
+ sprintf(headerbuf, "\n==== isdnanalyze controller rx #%d - tx #%d ==== started %s",
+ Rx, Tx, ctime(&tm));
+ }
+ else
+ {
+ sprintf(headerbuf, "\n=========== isdntrace controller #%d =========== started %s",
+ unit, ctime(&tm));
+ }
+
+ printf("%s", headerbuf);
+
+ if(outflag)
+ fprintf(Fout, "%s", headerbuf);
+
+ for (;;)
+ {
+ if(Popt == 0)
+ {
+ n = read(f, buf, BSIZE);
+
+ if(Bopt)
+ {
+ if((fwrite(buf, 1, n, BP)) != n)
+ {
+ char buffer[80];
+ sprintf(buffer, "Error writing file [%s]", rBPfilename);
+ perror(buffer);
+ exit(1);
+ }
+ }
+
+ n -= sizeof(i4b_trace_hdr_t);
+ }
+ else
+ {
+again:
+ if((fread(buf, 1, sizeof(i4b_trace_hdr_t), BP)) != sizeof(i4b_trace_hdr_t))
+ {
+ if(feof(BP))
+ {
+ if(Fopt)
+ {
+ if(ferror(BP))
+ {
+ char buffer[80];
+ sprintf(buffer, "Error reading hdr from file [%s]", rBPfilename);
+ perror(buffer);
+ exit(1);
+ }
+
+ usleep(250000);
+ clearerr(BP);
+
+ if(stat(rBPfilename, &fstnew) != -1)
+ {
+ if((fst.st_ino != fstnew.st_ino) ||
+ (fstnew.st_nlink == 0))
+ {
+ if((BP = freopen(rBPfilename, "r", BP)) == NULL)
+ {
+ char buffer[80];
+ sprintf(buffer, "Error reopening file [%s]", rBPfilename);
+ perror(buffer);
+ exit(1);
+ }
+ stat(rBPfilename, &fst);
+ }
+ }
+ goto again;
+ }
+ else
+ {
+ printf("\nEnd of playback input file reached.\n");
+ exit(0);
+ }
+ }
+ else
+ {
+ char buffer[80];
+ sprintf(buffer, "Error reading hdr from file [%s]", rBPfilename);
+ perror(buffer);
+ exit(1);
+ }
+ }
+
+ ithp = (i4b_trace_hdr_t *)buf;
+ l = ithp->length - sizeof(i4b_trace_hdr_t);
+
+ if((n = fread(buf+sizeof(i4b_trace_hdr_t), 1, l , BP)) != l)
+ {
+ char buffer[80];
+ sprintf(buffer, "Error reading data from file [%s]", rBPfilename);
+ perror(buffer);
+ exit(1);
+ }
+
+ }
+
+ if((n > 0) && (n > noct))
+ {
+ dumpbuf(n, b, (i4b_trace_hdr_t *)buf, raw);
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * format header into static buffer, return buffer address
+ *---------------------------------------------------------------------------*/
+char *
+fmt_hdr(i4b_trace_hdr_t *hdr, int frm_len)
+{
+ struct tm *s;
+ static char hbuf[256];
+ int i = 0;
+
+ s = localtime((time_t *)&(hdr->time.tv_sec));
+
+ if(hdr->type == TRC_CH_I) /* Layer 1 INFO's */
+ {
+ sprintf(hbuf,"\n-- %s - unit:%d ---------------- time:%2.2d.%2.2d %2.2d:%2.2d:%2.2d.%06u ",
+ ((hdr->dir) ? "NT->TE" : "TE->NT"),
+ hdr->unit,
+ s->tm_mday,
+ s->tm_mon + 1,
+ s->tm_hour,
+ s->tm_min,
+ s->tm_sec,
+ (u_int32_t)hdr->time.tv_usec);
+ }
+ else
+ {
+ if(hdr->trunc > 0)
+ {
+ sprintf(hbuf,"\n-- %s - unit:%d - frame:%6.6u - time:%2.2d.%2.2d %2.2d:%2.2d:%2.2d.%06u - length:%d (%d) ",
+ ((hdr->dir) ? "NT->TE" : "TE->NT"),
+ hdr->unit,
+ hdr->count,
+ s->tm_mday,
+ s->tm_mon + 1,
+ s->tm_hour,
+ s->tm_min,
+ s->tm_sec,
+ (u_int32_t)hdr->time.tv_usec,
+ frm_len,
+ hdr->trunc);
+ }
+ else
+ {
+ sprintf(hbuf,"\n-- %s - unit:%d - frame:%6.6u - time:%2.2d.%2.2d %2.2d:%2.2d:%2.2d.%06u - length:%d ",
+ ((hdr->dir) ? "NT->TE" : "TE->NT"),
+ hdr->unit,
+ hdr->count,
+ s->tm_mday,
+ s->tm_mon + 1,
+ s->tm_hour,
+ s->tm_min,
+ s->tm_sec,
+ (u_int32_t)hdr->time.tv_usec,
+ frm_len);
+ }
+ }
+
+ for(i=strlen(hbuf); i <= NCOLS;)
+ hbuf[i++] = '-';
+
+ hbuf[i++] = '\n';
+ hbuf[i] = '\0';
+
+ return(hbuf);
+}
+
+/*---------------------------------------------------------------------------*
+ * decode protocol and output to file(s)
+ *---------------------------------------------------------------------------*/
+static void
+dumpbuf(int n, unsigned char *buf, i4b_trace_hdr_t *hdr, int raw)
+{
+ static char l1buf[128];
+ static unsigned char l2buf[32000];
+ static unsigned char l3buf[32000];
+ int cnt;
+ int nsave = n;
+ char *pbuf;
+ int i, j;
+
+ l1buf[0] = '\0';
+ l2buf[0] = '\0';
+ l3buf[0] = '\0';
+
+ switch(hdr->type)
+ {
+ case TRC_CH_I: /* Layer 1 INFO's */
+
+ /* on playback, don't display layer 1 if -i ! */
+ if(!(enable_trace & TRACE_I))
+ break;
+
+ pbuf = &l1buf[0];
+
+ switch(buf[0])
+ {
+ case INFO0:
+ sprintf((pbuf+strlen(pbuf)),"I430: INFO0 (No Signal)\n");
+ break;
+
+ case INFO1_8:
+ sprintf((pbuf+strlen(pbuf)),"I430: INFO1 (Activation Request, Priority = 8, from TE)\n");
+ break;
+
+ case INFO1_10:
+ sprintf((pbuf+strlen(pbuf)),"I430: INFO1 (Activation Request, Priority = 10, from TE)\n");
+ break;
+
+ case INFO2:
+ sprintf((pbuf+strlen(pbuf)),"I430: INFO2 (Pending Activation, from NT)\n");
+ break;
+
+ case INFO3:
+ sprintf((pbuf+strlen(pbuf)),"I430: INFO3 (Synchronized, from TE)\n");
+ break;
+
+ case INFO4_8:
+ sprintf((pbuf+strlen(pbuf)),"I430: INFO4 (Activated, Priority = 8/9, from NT)\n");
+ break;
+
+ case INFO4_10:
+ sprintf((pbuf+strlen(pbuf)),"I430: INFO4 (Activated, Priority = 10/11, from NT)\n");
+ break;
+
+ default:
+ sprintf((pbuf+strlen(pbuf)),"I430: ERROR, invalid INFO value 0x%x!\n", buf[0]);
+ break;
+ }
+ break;
+
+ case TRC_CH_D: /* D-channel data */
+
+ cnt = decode_lapd(l2buf, n, buf, hdr->dir, raw, print_q921);
+
+ n -= cnt;
+ buf += cnt;
+
+ if(n)
+ {
+ switch(*buf)
+ {
+ case 0x40:
+ case 0x41:
+ decode_1tr6(l3buf, n, cnt, buf, raw);
+ break;
+
+ case 0x08:
+ decode_q931(l3buf, n, cnt, buf, raw);
+ break;
+
+ default:
+ if(xopt)
+ {
+ l2buf[0] = '\0';
+ l3buf[0] = '\0';
+ }
+ else
+ {
+ decode_unknownl3(l3buf, n, cnt, buf, raw);
+ }
+ break;
+ }
+ }
+ break;
+
+ default: /* B-channel data */
+
+ pbuf = &l2buf[0];
+
+ for (i = 0; i < n; i += 16)
+ {
+ sprintf((pbuf+strlen(pbuf)),"B%d:%.3x ", hdr->type, i);
+
+ for (j = 0; j < 16; j++)
+ if (i + j < n)
+ sprintf((pbuf+strlen(pbuf)),"%02x ", buf[i + j]);
+ else
+ sprintf((pbuf+strlen(pbuf))," ");
+
+ sprintf((pbuf+strlen(pbuf))," ");
+
+ for (j = 0; j < 16 && i + j < n; j++)
+ if (isprint(buf[i + j]))
+ sprintf((pbuf+strlen(pbuf)),"%c", buf[i + j]);
+ else
+ sprintf((pbuf+strlen(pbuf)),".");
+
+ sprintf((pbuf+strlen(pbuf)),"\n");
+ }
+ break;
+ }
+
+ if(header && ((l1buf[0] != '\0' || l2buf[0] != '\0') || (l3buf[0] != 0)))
+ {
+ char *p;
+ p = fmt_hdr(hdr, nsave);
+ printf("%s", p);
+ if(outflag)
+ fprintf(Fout, "%s", p);
+ }
+
+ if(l1buf[0] != '\0')
+ {
+ printf("%s", l1buf);
+ if(outflag)
+ fprintf(Fout, "%s", l1buf);
+ }
+
+ if(l2buf[0] != '\0')
+ {
+ printf("%s", l2buf);
+ if(outflag)
+ fprintf(Fout, "%s", l2buf);
+ }
+
+ if(l3buf[0] != '\0')
+ {
+ printf("%s", l3buf);
+ if(outflag)
+ fprintf(Fout, "%s", l3buf);
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * exit handler function to be called at program exit
+ *---------------------------------------------------------------------------*/
+void
+exit_hdl()
+{
+ if(traceon)
+ switch_driver(TRACE_OFF, Rx, Tx);
+}
+
+/*---------------------------------------------------------------------------*
+ * switch driver debugging output on/off
+ *---------------------------------------------------------------------------*/
+static int
+switch_driver(int value, int rx, int tx)
+{
+ char buffer[80];
+ int v = value;
+
+ if(analyze == 0)
+ {
+ if(ioctl(f, I4B_TRC_SET, &v) < 0)
+ {
+ sprintf(buffer, "Error ioctl I4B_TRC_SET, val = %d", v);
+ perror(buffer);
+ return(-1);
+ }
+ }
+ else
+ {
+ if(value == TRACE_OFF)
+ {
+ if(ioctl(f, I4B_TRC_RESETA, &v) < 0)
+ {
+ sprintf(buffer, "Error ioctl I4B_TRC_RESETA - ");
+ perror(buffer);
+ return(-1);
+ }
+ }
+ else
+ {
+ i4b_trace_setupa_t tsa;
+
+ tsa.rxunit = rx;
+ tsa.rxflags = value;
+ tsa.txunit = tx;
+ tsa.txflags = value;
+
+ if(ioctl(f, I4B_TRC_SETA, &tsa) < 0)
+ {
+ sprintf(buffer, "Error ioctl I4B_TRC_SETA, val = %d", v);
+ perror(buffer);
+ return(-1);
+ }
+ }
+ }
+ return(0);
+}
+
+/*---------------------------------------------------------------------------*
+ * reopen files to support rotating logfile(s) on SIGUSR1
+ *
+ * based on an idea from Ripley (ripley@nostromo.in-berlin.de)
+ *
+ * close file and reopen it for append. this will be a nop
+ * if the previously opened file hasn't moved but will open
+ * a new one otherwise, thus enabling a rotation...
+ *
+ *---------------------------------------------------------------------------*/
+static void
+reopenfiles(int dummy)
+{
+ if(outflag)
+ {
+ fclose(Fout);
+
+ add_datetime(outfilename, routfilename);
+
+ if((Fout = fopen(routfilename, "a")) == NULL)
+ {
+ char buffer[80];
+
+ sprintf(buffer, "Error re-opening file [%s]", routfilename);
+ perror(buffer);
+ exit(1);
+ }
+
+ if((setvbuf(Fout, (char *)NULL, _IONBF, 0)) != 0)
+ {
+ char buffer[80];
+
+ sprintf(buffer, "Error re-setting file [%s] to unbuffered", routfilename);
+ perror(buffer);
+ exit(1);
+ }
+ }
+
+ if(Bopt)
+ {
+
+ fclose(BP);
+
+ add_datetime(BPfilename, rBPfilename);
+
+ if((BP = fopen(rBPfilename, "a")) == NULL)
+ {
+ char buffer[80];
+
+ sprintf(buffer, "Error re-opening file [%s]", rBPfilename);
+ perror(buffer);
+ exit(1);
+ }
+
+ if((setvbuf(BP, (char *)NULL, _IONBF, 0)) != 0)
+ {
+ char buffer[80];
+
+ sprintf(buffer, "Error re-setting file [%s] to unbuffered", rBPfilename);
+ perror(buffer);
+ exit(1);
+ }
+ }
+}
+
+void
+add_datetime(char *filename, char *rfilename)
+{
+ time_t timeb;
+ struct tm *tmp;
+ FILE *fx;
+
+ time(&timeb);
+ tmp = localtime(&timeb);
+
+ sprintf(rfilename, "%s-", filename);
+
+ strftime(rfilename+strlen(rfilename), MAXPATHLEN-strlen(rfilename)-1,
+ "%Y%m%d-%H%M%S", tmp);
+
+ if((fx = fopen(rfilename, "r")) != NULL)
+ {
+ fclose(fx);
+
+ sleep(1);
+
+ time(&timeb);
+ tmp = localtime(&timeb);
+
+ sprintf(rfilename, "%s-", filename);
+
+ strftime(rfilename+strlen(rfilename), MAXPATHLEN-strlen(rfilename)-1,
+ "%Y%m%d-%H%M%S", tmp);
+ }
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdntrace/trace.h b/usr.sbin/i4b/isdntrace/trace.h
new file mode 100644
index 0000000..81fa415
--- /dev/null
+++ b/usr.sbin/i4b/isdntrace/trace.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 1996, 2000 Hellmuth Michaelis. All rights reserved.
+ *
+ * Copyright (c) 1996 Gary Jennejohn. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ * 4. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software and/or documentation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * trace.h - header file for isdn trace
+ * ------------------------------------
+ *
+ * $Id: trace.h,v 1.12 2000/02/14 16:25:22 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Feb 14 14:43:40 2000]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/uio.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+
+#include <machine/i4b_ioctl.h>
+#include <machine/i4b_trace.h>
+
+#include "pcause_1tr6.h" /* obsolete german national ISDN */
+#include "pcause_q850.h"
+
+#define I4BTRC_DEVICE "/dev/i4btrc" /* trace device file */
+#define TRACE_FILE_NAME "isdntrace" /* default output filename */
+#define BIN_FILE_NAME "isdntracebin" /* default binary filename */
+
+#define BSIZE 4096 /* read buffer size */
+#define NCOLS 80 /* screen width */
+
+#define RxUDEF 0 /* analyze mode, default unit for receiver side */
+#define TxUDEF 1 /* analyze mode, default unit for transmitter side */
+
+int decode_lapd(char *pbuf, int n, unsigned char *buf, int is_te, int raw, int printit);
+void decode_q931(char *pbuf, int n, int off, unsigned char *buf, int raw);
+void decode_unknownl3(char *pbuf, int n, int off, unsigned char *buf, int raw);
+void decode_1tr6(char *pbuf, int n, int off, unsigned char *buf, int raw);
+char *print_error(int prot, unsigned char code);
+int q931_facility(char *pbuf, unsigned char *buf);
+int p_q931cause(char *pbuf, unsigned char *buf);
+int p_q931address(char *pbuf, unsigned char *buf);
+int p_q931bc(char *pbuf, unsigned char *buf);
+int p_q931high_compat(char *pbuf, unsigned char *buf);
+int q932_facility(char *pbuf, unsigned char *buf);
+int p_q931user_user(char *pbuf, unsigned char *buf);
+int p_q931notification(char *pbuf, unsigned char *buf);
+int p_q931redir(char *pbuf, unsigned char *buf);
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdntrace/unknownl3.c b/usr.sbin/i4b/isdntrace/unknownl3.c
new file mode 100644
index 0000000..8967a6a9
--- /dev/null
+++ b/usr.sbin/i4b/isdntrace/unknownl3.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2000 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * unknownl3.c - print L3 packets with unknown PD
+ * ----------------------------------------------
+ *
+ * $Id: unknownl3.c,v 1.2 2000/02/13 15:26:52 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Sun Feb 13 14:16:44 2000]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "trace.h"
+
+/*---------------------------------------------------------------------------*
+ * decode unknown protocol
+ *---------------------------------------------------------------------------*/
+void
+decode_unknownl3(char *pbuf, int n, int off, unsigned char *buf, int raw)
+{
+ int pd;
+ int j;
+ int i;
+
+ if(n <= 0)
+ return;
+
+ *pbuf = '\0';
+
+ if(raw)
+ {
+ for (i = 0; i < n; i += 16)
+ {
+ sprintf((pbuf+strlen(pbuf)),"Dump:%.3d ", i+off);
+ for (j = 0; j < 16; j++)
+ if (i + j < n)
+ sprintf((pbuf+strlen(pbuf)),"%02x ", buf[i + j]);
+ else
+ sprintf((pbuf+strlen(pbuf))," ");
+ sprintf((pbuf+strlen(pbuf))," ");
+ for (j = 0; j < 16 && i + j < n; j++)
+ if (isprint(buf[i + j]))
+ sprintf((pbuf+strlen(pbuf)),"%c", buf[i + j]);
+ else
+ sprintf((pbuf+strlen(pbuf)),".");
+ sprintf((pbuf+strlen(pbuf)),"\n");
+ }
+ }
+
+ i = 0;
+
+ /* protocol discriminator */
+
+ pd = buf[i];
+
+ sprintf((pbuf+strlen(pbuf)), "PD%02X: ", pd);
+
+ if(pd >= 0x00 && pd <= 0x07)
+ sprintf((pbuf+strlen(pbuf)), "pd=User-User (0x%02x)",pd);
+ else if(pd == 0x08)
+ sprintf((pbuf+strlen(pbuf)), "pd=Q.931/I.451");
+ else if(pd >= 0x10 && pd <= 0x3f)
+ sprintf((pbuf+strlen(pbuf)), "pd=Other Layer 3 or X.25 (0x%02x)",pd);
+ else if(pd >= 0x40 && pd <= 0x4f)
+ sprintf((pbuf+strlen(pbuf)), "pd=National Use (0x%02x)",pd);
+ else if(pd >= 0x50 && pd <= 0xfe)
+ sprintf((pbuf+strlen(pbuf)), "pd=Other Layer 3 or X.25 (0x%02x)",pd);
+ else
+ sprintf((pbuf+strlen(pbuf)), "pd=Reserved (0x%02x)",pd);
+
+ sprintf((pbuf+strlen(pbuf)), "\n [");
+ for(j = 0; j < (n-i); j++)
+ {
+ sprintf((pbuf+strlen(pbuf)),"0x%02x ", buf[j+i]);
+ }
+
+ sprintf((pbuf+strlen(pbuf)),"]\n");
+}
+
+/* EOF */
+
diff --git a/usr.sbin/i4b/man/Makefile b/usr.sbin/i4b/man/Makefile
new file mode 100644
index 0000000..561e5fe
--- /dev/null
+++ b/usr.sbin/i4b/man/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+MAN= i4b.4 i4bcapi.4 i4bctl.4 i4bing.4 i4bipr.4 i4bisppp.4 i4bq921.4 \
+ i4bq931.4 i4brbch.4 i4btel.4 i4btrc.4 iavc.4 isic.4 ifpi.4 \
+ ifpi2.4 ifpnp.4 ihfc.4 itjc.4 iwic.4
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/i4b/man/i4b.4 b/usr.sbin/i4b/man/i4b.4
new file mode 100644
index 0000000..78b95ff
--- /dev/null
+++ b/usr.sbin/i4b/man/i4b.4
@@ -0,0 +1,110 @@
+.\"
+.\" 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.9 1999/12/13 22:11:55 hm Exp $
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Mon Dec 13 23:09:12 1999]
+.\"
+.Dd February 3, 1998
+.Dt I4B 4
+.Os
+.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
+.Sh SEE ALSO
+.Xr isdnd 8
+.Sh AUTHORS
+The
+.Nm
+device driver and this manpage were written by
+.An Hellmuth Michaelis Aq hm@kts.org .
diff --git a/usr.sbin/i4b/man/i4bcapi.4 b/usr.sbin/i4b/man/i4bcapi.4
new file mode 100644
index 0000000..cca62c0
--- /dev/null
+++ b/usr.sbin/i4b/man/i4bcapi.4
@@ -0,0 +1,59 @@
+.\"
+.\" Copyright (c) 2001 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Fri May 25 09:38:45 2001]
+.\"
+.Dd May 21, 2001
+.Dt I4BCAPI 4
+.Os
+.Sh NAME
+.Nm i4bcapi
+.Nd CAPI driver for the isdn4bsd kernel part
+.Sh SYNOPSIS
+.Cd "device \*[q]i4bcapi\*[q]"
+.Sh DESCRIPTION
+.Nm
+is a CAPI driver for the
+.Em isdn4bsd
+package.
+It sits between layer\~4 of isdn4bsd and a driver for an active
+ISDN card; currently only the
+.Xr iavc 4
+driver for the AVM B1 and T1 family of active cards is supported.
+.Sh STANDARDS
+CAPI 2.0
+.Pq Pa http://www.capi.org/
+.Sh SEE ALSO
+.Xr iavc 4
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+device driver was written by
+.An Juha-Matti Liukkonen Aq jml@cubical.fi
+(Cubical Solutions Ltd, Finland).
+This manpage was written by
+.An Hellmuth Michaelis Aq hm@FreeBSD.org .
diff --git a/usr.sbin/i4b/man/i4bctl.4 b/usr.sbin/i4b/man/i4bctl.4
new file mode 100644
index 0000000..77983fe
--- /dev/null
+++ b/usr.sbin/i4b/man/i4bctl.4
@@ -0,0 +1,53 @@
+.\"
+.\" 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.7 1999/12/13 22:11:55 hm Exp $
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Mon Dec 13 23:09:46 1999]
+.\"
+.Dd February 3, 1998
+.Dt I4BCTL 4
+.Os
+.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 AUTHORS
+The
+.Nm
+device driver and this manpage were written by
+.An Hellmuth Michaelis Aq hm@kts.org .
diff --git a/usr.sbin/i4b/man/i4bing.4 b/usr.sbin/i4b/man/i4bing.4
new file mode 100644
index 0000000..b846d2b
--- /dev/null
+++ b/usr.sbin/i4b/man/i4bing.4
@@ -0,0 +1,69 @@
+.\"
+.\" Copyright (c) 2000 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: i4bing.4,v 1.1 2000/01/10 19:14:18 hm Exp $
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Mon Jan 10 20:13:11 2000]
+.\"
+.Dd January 10, 2000
+.Dt I4BING 4
+.Os
+.Sh NAME
+.Nm i4bing
+.Nd isdn4bsd NetGraph ISDN B-channel interface driver
+.Sh SYNOPSIS
+.Cd pseudo-device \&"i4bing\&" Op count
+.Sh DESCRIPTION
+The
+.Nm
+driver interfaces the
+.Fx
+NetGraph subsystem with the
+isdn4bsd package.
+.Pp
+The driver just packs packets received from the NetGraph subsystem without
+anything appended or prepended into raw HDLC packets on the B channel and
+transfers them to a remote site.
+Packets received from the remote site are queued into the NetGraph
+subsystem
+.Pp
+The format of the resulting packet on the B channel is:
+.Pp
+.Dl (HDLC opening flag) (Packet) (CRC) (HDLC closing flag)
+.Pp
+In the case where a Packet for a remote site arrives in the driver and no
+connection has been established yet, the driver communicates with the
+.Xr isdnd 8
+daemon to establish a connection.
+.Sh SEE ALSO
+.Xr netgraph 4 ,
+.Xr isdnd.rc 5 ,
+.Xr isdnd 8
+.Sh AUTHORS
+The
+.Nm
+device driver and this manpage were written by
+.An Hellmuth Michaelis Aq hm@kts.org .
diff --git a/usr.sbin/i4b/man/i4bipr.4 b/usr.sbin/i4b/man/i4bipr.4
new file mode 100644
index 0000000..a0b3589
--- /dev/null
+++ b/usr.sbin/i4b/man/i4bipr.4
@@ -0,0 +1,100 @@
+.\"
+.\" 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.11 1999/12/13 22:11:55 hm Exp $
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Mon Dec 13 23:10:25 1999]
+.\"
+.Dd July 6, 1998
+.Dt I4BIPR 4
+.Os
+.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 tcpdump 1 ,
+.Xr bpf 4 ,
+.Xr isdnd.rc 5 ,
+.Xr isdnd 8
+.Sh AUTHORS
+The
+.Nm
+device driver and this manpage were written by
+.An Hellmuth Michaelis Aq hm@kts.org .
diff --git a/usr.sbin/i4b/man/i4bisppp.4 b/usr.sbin/i4b/man/i4bisppp.4
new file mode 100644
index 0000000..aea1b67
--- /dev/null
+++ b/usr.sbin/i4b/man/i4bisppp.4
@@ -0,0 +1,131 @@
+.\"
+.\" Copyright (c) 1997, 2000 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: i4bisppp.4,v 1.13 2000/08/31 08:45:07 hm Exp $
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Thu Aug 31 10:40:17 2000]
+.\"
+.Dd August 31, 2000
+.Dt I4BISPPP 4
+.Os
+.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
+Beginning with i4b version 0.95 (and only for
+.Fx ) ,
+the sppp
+subsystem was integrated into the
+.Nm
+driver to support more i4b/ISDN specific options. As a consequence it is no
+longer necessary to add a "options sppp" to your kernel
+.Xr config 8
+file. For configuration of the i4bsppp driver, either the
+.Xr ispppcontrol 8
+utility is used or it is configured via
+.Xr isdnd 1
+and its associated
+.Xr isdnd.rc 5
+file.
+.Pp
+In case an IP packet for a remote side arrives in the driver and no
+connection is established yet, the driver communicates with the
+.Xr isdnd 8
+daemon to establish a connection.
+.Pp
+The driver has support for interfacing to the
+.Xr bpf 4
+subsystem for using
+.Xr tcpdump 1
+with the
+.Nm isp
+interfaces.
+.Pp
+The
+.Xr ispppcontrol 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,
+.Sh SEE ALSO
+.Xr tcpdump 1 ,
+.Xr bpf 4 ,
+.Xr sppp 4 ,
+.Xr isdnd.rc 5 ,
+.Xr isdnd 8 ,
+.Xr ispppcontrol 8
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+device driver was written by
+.An Joerg Wunsch Aq joerg@FreeBSD.org
+and then added to ISDN4BSD by
+.An Gary Jennejohn Aq gj@FreeBSD.org .
+.Pp
+This manpage was written by
+.An Hellmuth Michaelis Aq hm@kts.org .
diff --git a/usr.sbin/i4b/man/i4bq921.4 b/usr.sbin/i4b/man/i4bq921.4
new file mode 100644
index 0000000..1c499a9
--- /dev/null
+++ b/usr.sbin/i4b/man/i4bq921.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: i4bq921.4,v 1.8 1999/12/13 22:11:55 hm Exp $
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Mon Dec 13 23:11:55 1999]
+.\"
+.Dd February 3, 1998
+.Dt I4BQ921 4
+.Os
+.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 AUTHORS
+The
+.Nm
+driver and this manpage were written by
+.An Hellmuth Michaelis Aq 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..49c12ec
--- /dev/null
+++ b/usr.sbin/i4b/man/i4bq931.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: i4bq931.4,v 1.8 1999/12/13 22:11:55 hm Exp $
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Mon Dec 13 23:12:33 1999]
+.\"
+.Dd February 3, 1998
+.Dt I4BQ931 4
+.Os
+.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 AUTHORS
+The
+.Nm
+driver and this manpage were written by
+.An Hellmuth Michaelis Aq 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..90d5138
--- /dev/null
+++ b/usr.sbin/i4b/man/i4brbch.4
@@ -0,0 +1,53 @@
+.\"
+.\" 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.8 1999/12/13 22:11:55 hm Exp $
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Mon Dec 13 23:13:04 1999]
+.\"
+.Dd February 3, 1998
+.Dt I4BRBCH 4
+.Os
+.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.rc 5 ,
+.Xr isdnd 8
+.Sh AUTHORS
+The
+.Nm
+device driver and this manpage were written by
+.An Hellmuth Michaelis Aq hm@kts.org .
diff --git a/usr.sbin/i4b/man/i4btel.4 b/usr.sbin/i4b/man/i4btel.4
new file mode 100644
index 0000000..b1075e9
--- /dev/null
+++ b/usr.sbin/i4b/man/i4btel.4
@@ -0,0 +1,136 @@
+.\"
+.\" 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.9 1999/12/13 22:11:55 hm Exp $
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Mon Dec 13 23:13:39 1999]
+.\"
+.Dd April 21, 1999
+.Dt I4BTEL 4
+.Os
+.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.
+.Pp
+The lower six bits of the drivers's minor number is used to specify a
+unit number, wheras the upper two bits specify a functionality.
+.Pp
+Functionality zero is the usual telephony data stream i/o driver.
+.Pp
+Functionality one is used to enable commands to dial out and hang up and
+receive responses about the state of the dial out progress and status.
+This commands may change in the future, for details see the file
+.Em i4b_tel_ioctl.h
+and the
+.Xr isdnphone 1
+utility.
+.Pp
+The telephony data stream comes out of the line in a bit-reversed format,
+so the
+.Nm
+driver does the bit-reversion process in any case.
+.Pp
+Additionally, the user can specify to do A-law to u-law, u-law to A-law
+or no conversion at all in the i4btel driver by using the
+.Xr isdntelctl 8
+utility.
+.Pp
+The driver is able to process several ioctl's:
+.Pp
+.Bl -tag -width Ds -compact -offset indent
+.It Ar I4B_TEL_GETAUDIOFMT
+get currently used audio format conversion.
+.It Ar I4B_TEL_SETAUDIOFMT
+set currently used audio format conversion.
+.It Ar I4B_TEL_EMPTYINPUTQUEUE
+clear the input queue.
+.El
+.Pp
+For the I4B_TEL_GETAUDIOFMT and I4B_TEL_SETAUDIOFMT, the following
+parameters are available:
+.Pp
+.Bl -tag -width Ds -compact -offset indent
+.It Ar CVT_NONE
+do no A-law/u-law audio format conversion. The conversion path looks like
+this:
+.Pp
+USER <--> bitreversing <--> ISDN-line
+.Pp
+.It Ar CVT_ALAW2ULAW
+set set audio format conversion to do an audio conversion from A-law
+(on the ISDN line) to u-law (in the userland).
+The
+.Xr read 2
+conversion path looks like this:
+.Pp
+USER <-- u-law/A-law <-- bitreversing <-- ISDN-line
+.Pp
+and the
+.Xr write 2
+conversion path looks like this:
+.Pp
+USER --> u-law/A-law --> bitreversing --> ISDN-line
+.Pp
+.It Ar CVT_ULAW2ALAW
+set set audio format conversion to do an audio conversion from u-law
+(on the ISDN line) to A-law (in the userland).
+The
+.Xr read 2
+conversion path looks like this:
+.Pp
+USER <-- A-law/u-law <-- bitreversing <-- ISDN-line
+.Pp
+and the
+.Xr write 2
+conversion path looks like this:
+.Pp
+USER --> A-law/u-law --> bitreversing --> ISDN-line
+.Pp
+.El
+.Sh STANDARDS
+A-Law and u-Law are specified in ITU Recommendation G.711.
+.Sh SEE ALSO
+.Xr g711conv 1 ,
+.Xr isdnphone 1 ,
+.Xr isdnd.rc 5 ,
+.Xr isdnd 8 ,
+.Xr isdntelctl 8
+.Sh AUTHORS
+The
+.Nm
+device driver and this manpage were written by
+.An Hellmuth Michaelis Aq hm@kts.org .
diff --git a/usr.sbin/i4b/man/i4btrc.4 b/usr.sbin/i4b/man/i4btrc.4
new file mode 100644
index 0000000..a97b8c6
--- /dev/null
+++ b/usr.sbin/i4b/man/i4btrc.4
@@ -0,0 +1,55 @@
+.\"
+.\" 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.9 1999/12/13 22:11:55 hm Exp $
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Mon Dec 13 23:14:19 1999]
+.\"
+.Dd July 30, 1999
+.Dt I4BTRC 4
+.Os
+.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 AUTHORS
+The
+.Nm
+device driver and this manpage were written by
+.An Hellmuth Michaelis Aq hm@kts.org .
diff --git a/usr.sbin/i4b/man/iavc.4 b/usr.sbin/i4b/man/iavc.4
new file mode 100644
index 0000000..575859b
--- /dev/null
+++ b/usr.sbin/i4b/man/iavc.4
@@ -0,0 +1,72 @@
+.\"
+.\" Copyright (c) 2001 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Fri May 25 09:45:35 2001]
+.\"
+.Dd May 22, 2001
+.Dt IAVC 4
+.Os
+.Sh NAME
+.Nm iavc
+.Nd isdn4bsd AVM B1/T1 driver
+.Sh SYNOPSIS
+.Cd "device iavc"
+.Pp
+NOTE:
+For the B1 ISA card
+.Pp
+.Bd -literal -offset indent
+hint.iavc.0.at="isa"
+hint.iavc.0.port="0x150"
+hint.iavc.0.irq="5"
+.Ed
+.Pp
+must be added to
+.Pa /boot/device.hints
+.Sh DESCRIPTION
+The
+.Nm
+driver is used to access the AVM family of active cards to the
+.Xr i4bcapi 4
+driver and the
+.Em isdn4bsd
+package.
+Currently the AVM B1 PCI, the AVM B1 ISA and the AVM T1 PCI
+cards are supported.
+.Sh STANDARDS
+CAPI 2.0
+.Pq Pa http://www.capi.org/
+.Sh SEE ALSO
+.Xr i4bcapi 4
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+device driver was written by
+.An Juha-Matti Liukkonen Aq jml@cubical.fi
+(Cubical Solutions Ltd, Finland).
+This manpage was written by
+.An Hellmuth Michaelis Aq hm@FreeBSD.org .
diff --git a/usr.sbin/i4b/man/ifpi.4 b/usr.sbin/i4b/man/ifpi.4
new file mode 100644
index 0000000..4003231
--- /dev/null
+++ b/usr.sbin/i4b/man/ifpi.4
@@ -0,0 +1,64 @@
+.\"
+.\" Copyright (c) 2000 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: ifpi.4,v 1.1 2000/03/16 15:25:05 hm Exp $
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Thu Mar 16 16:27:40 2000]
+.\"
+.Dd July 30, 1999
+.Dt IFPI 4
+.Os
+.Sh NAME
+.Nm ifpi
+.Nd isdn4bsd AVM Fritz!Card PCI driver
+.Sh SYNOPSIS
+.Cd "device ifpi0"
+.Sh DESCRIPTION
+The
+.Nm
+driver provides D-channel layer 1 supports as specified in ITU Recommendation
+I.430 and layer 1 support for the B-channel.
+.Pp
+The driver supports passive PCI ISDN cards from AVM based on the proprietary
+B-channel controller and PCI interface chip from AVM, Berlin, Germany.
+.Sh CAVEATS
+The driver is still in a somewhat experimental state.
+.Sh BUGS
+Always at least one left.
+.Sh STANDARDS
+CCITT Recommendation I.430
+.Sh SEE ALSO
+.Xr i4bq921 4 ,
+.Xr i4bq931 4
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was written by
+.An Gary Jennejohn Aq gj@FreeBSD.org .
+.Pp
+This manpage was written by
+.An Hellmuth Michaelis Aq hm@kts.org .
diff --git a/usr.sbin/i4b/man/ifpi2.4 b/usr.sbin/i4b/man/ifpi2.4
new file mode 100644
index 0000000..3d80106
--- /dev/null
+++ b/usr.sbin/i4b/man/ifpi2.4
@@ -0,0 +1,60 @@
+.\"
+.\" Copyright (c) 2001 Gary Jennejohn. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id$
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Wed Dec 19 21:23:10 2001]
+.\"
+.Dd December 19, 2001
+.Dt IFPI2 4
+.Os
+.Sh NAME
+.Nm ifpi2
+.Nd isdn4bsd AVM Fritz!Card PCI Version 2 driver
+.Sh SYNOPSIS
+.Cd device \&"ifpi2\&"
+.Sh DESCRIPTION
+The
+.Nm
+driver provides D-channel layer 1 supports as specified in ITU Recommendation
+I.430 and layer 1 support for the B-channel.
+.Pp
+The driver supports version 2 passive PCI ISDN cards from AVM based on the proprietary
+B-channel controller and PCI interface chip from AVM, Berlin, Germany.
+.Sh CAVEATS
+The driver is still in a somewhat experimental state.
+.Sh BUGS
+Always at least one left.
+.Sh STANDARDS
+CCITT Recommendation I.430
+.Sh SEE ALSO
+.Xr i4bq921 4 ,
+.Xr i4bq931 4
+.Sh AUTHORS
+The
+.Nm
+driver and manpage were written by
+.An Gary Jennejohn Aq gj@FreeBSD.org .
diff --git a/usr.sbin/i4b/man/ifpnp.4 b/usr.sbin/i4b/man/ifpnp.4
new file mode 100644
index 0000000..647c154
--- /dev/null
+++ b/usr.sbin/i4b/man/ifpnp.4
@@ -0,0 +1,68 @@
+.\"
+.\" Copyright (c) 2000 Udo Schweigert. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: ifpnp.4,v 1.2 2000/04/25 13:01:20 hm Exp $
+.\" $Ust: src/i4b/man/ifpnp.4,v 1.4 2000/04/18 08:26:31 ust Exp $
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Tue Apr 25 15:03:27 2000]
+.\"
+.Dd April 18, 2000
+.Dt IFPNP 4
+.Os
+.Sh NAME
+.Nm ifpnp
+.Nd isdn4bsd AVM Fritz!Card PnP driver
+.Sh SYNOPSIS
+.Cd "device ifpnp"
+.Sh DESCRIPTION
+The
+.Nm
+driver provides D-channel layer 1 supports as specified in ITU Recommendation
+I.430 and layer 1 support for the B-channel.
+.Pp
+The driver supports passive ISA PnP ISDN cards from AVM, based on the
+proprietary B-channel controller and ISA PnP interface chip from
+AVM, Berlin, Germany.
+.Sh CAVEATS
+The driver is still in a somewhat experimental state.
+.Sh BUGS
+Always at least one left.
+.Sh STANDARDS
+CCITT Recommendation I.430
+.Sh SEE ALSO
+.Xr i4bq921 4 ,
+.Xr i4bq931 4
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was written by
+.An Udo Schweigert Aq ust@cert.siemens.de .
+.Pp
+This manpage was written by
+.An Hellmuth Michaelis Aq hm@kts.org
+and
+.An Udo Schweigert .
diff --git a/usr.sbin/i4b/man/ihfc.4 b/usr.sbin/i4b/man/ihfc.4
new file mode 100644
index 0000000..5135102
--- /dev/null
+++ b/usr.sbin/i4b/man/ihfc.4
@@ -0,0 +1,82 @@
+.\"
+.\" Copyright (c) 2000 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Fri Jan 12 09:54:31 2001]
+.\"
+.Dd January 12, 2001
+.Dt IHFC 4
+.Os
+.Sh NAME
+.Nm ihfc
+.Nd isdn4bsd Cologne Chip Designs HFC[-S][-SP] 2B[DS0] driver
+.Sh SYNOPSIS
+All PnP cards (AcerISDN P10, Teles 16.3c PnP),
+.Fx
+4.x/5.x:
+.Cd "device ihfc"
+.Pp
+Non-PnP cards (TELEINT ISDN SPEED No.1):
+.Pp
+.Fx
+4.x:
+.Cd "device ihfc0 at isa?"
+.Pp
+.Fx
+5.x:
+.Cd "device ihfc"
+NOTE:
+.Li hint.ihfc.0.at="isa"
+must be added to
+.Pa /boot/device.hints
+.Pp
+NOTE: The driver will automatically probe for io-base and irq.
+.Sh DESCRIPTION
+The
+.Nm
+driver provides D-channel layer 1 supports as specified in ITU Recommendation
+I.430 and layer 1 support for the B-channel.
+.Pp
+The driver supports passive ISA ISDN cards based on the HFC[-S][-SP] 2B[DS0]
+chips from Cologne Chip Designs such as the Teles 16.3c and
+the AcerISDN P10.
+.Sh CAVEATS
+The driver is still in a somewhat experimental state.
+.Sh BUGS
+Always at least one left.
+.Sh STANDARDS
+CCITT Recommendation I.430
+.Sh SEE ALSO
+.Xr i4bq921 4 ,
+.Xr i4bq931 4
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was written by
+.An Hans Petter Selasky Aq hselasky@c2i.net .
+.Pp
+This manpage was written by
+.An Hellmuth Michaelis Aq hm@FreeBSD.org .
diff --git a/usr.sbin/i4b/man/isic.4 b/usr.sbin/i4b/man/isic.4
new file mode 100644
index 0000000..fd3a3c1
--- /dev/null
+++ b/usr.sbin/i4b/man/isic.4
@@ -0,0 +1,388 @@
+.\"
+.\" Copyright (c) 1997, 2001 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Fri Jan 26 14:32:33 2001]
+.\"
+.Dd January 26, 2001
+.Dt ISIC 4
+.Os
+.Sh NAME
+.Nm isic
+.Nd isdn4bsd Siemens ISDN Chipset device driver
+.Sh SYNOPSIS
+.Ss FreeBSD (FreeBSD >= 4.0)
+.Pp
+Asuscom ISDNLink 128K (ISA PnP):
+.Cd options \&"ASUSCOM_IPAC\&"
+.Cd "device isic0"
+.Pp
+AVM A1 or AVM Fritz!Card classic:
+.Cd options \&"AVM_A1\&"
+.Cd "device isic0 at isa? port 0x340 irq 5 flags 4"
+.Pp
+Dr. Neuhaus Niccy Go@ (PnP):
+.Cd options \&"DRN_NGO\&"
+.Cd "device isic0"
+.Pp
+ELSA QuickStep 1000pro ISA (PnP):
+.Cd options \&"ELSA_QS1ISA\&"
+.Cd "device isic0"
+.Pp
+ELSA MicroLink ISDN/PCI (ELSA QuickStep 1000pro PCI):
+.Cd options \&"ELSA_QS1PCI\&"
+.Cd "device isic0"
+.Pp
+ELSA PCC-16:
+.Cd options \&"ELSA_PCC16\&"
+.Cd "device isic0 at isa? port 0x360 irq 10 flags 20"
+.Pp
+Sedlbauer Win Speed card (PnP):
+.Cd options \&"SEDLBAUER\&"
+.Cd "device isic0"
+.Pp
+Siemens I-Surf 2.0 PnP:
+.Cd options \&"SIEMENS_ISURF2\&"
+.Cd "device isic0"
+.Pp
+Teles S0/8 or Niccy 1008 card:
+.Cd options \&"TEL_S0_8\&"
+.Cd "device isic0 at isa? iomem 0xd0000 irq 5 flags 1"
+.Pp
+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 irq 5 flags 2"
+.Pp
+Teles S0/16.3 card:
+.Cd options \&"TEL_S0_16_3\&"
+.Cd "device isic0 at isa? port 0xd80 irq 5 flags 3"
+.Pp
+Teles S0/16.3 PnP card (PnP) or
+Creatix ISDN-S0 P&P card (PnP):
+.Cd options \&"TEL_S0_16_3_P\&"
+.Cd "device isic0"
+.Pp
+USRobotics Sportster ISDN TA internal or Stollmann Tina-pp card:
+.Cd options \&"USR_STI\&"
+.Cd "device isic0 at isa? port 0x268 irq 5 flags 7"
+.Pp
+Eicon.Diehl DIVA 2.0 and 2.02 ISA PnP card:
+.Cd options \&"EICON_DIVA\&"
+.Cd "device isic0"
+.Pp
+.Ss NetBSD
+On the ISA bus:
+.Pp
+Teles S0/8 or Niccy 1008 card:
+.Cd options \&"TEL_S0_8\&"
+.Cd "isic0 at isa? iomem 0xd0000 irq 5"
+.Pp
+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
+Teles S0/16.3 card:
+.Cd options \&"TEL_S0_16_3\&"
+.Cd "isic0 at isa? port 0xd80 irq 5"
+.Pp
+AVM A1 or AVM Fritz card:
+.Cd options \&"AVM_A1\&"
+.Cd "isic0 at isa? port 0x340 irq 5"
+.Pp
+USRobotics Sportster ISDN TA internal or Stollmann Tina-pp card:
+.Cd options \&"USR_STI\&"
+.Cd "isic0 at isa? port 0x268 irq 5"
+.Pp
+ITK ix1 micro card:
+.Cd options \&"ITKIX1\&"
+.Cd "isic0 at isa? port 0x398 irq 10"
+.Pp
+On the ISAPNP bus:
+.Pp
+Teles S0/16.3 PnP card
+.Cd options \&"TEL_S0_16_3_P\&"
+.Cd "isic* at isapnp?"
+.Pp
+Creatix ISDN-S0 P&P card
+.Cd options \&"CRTX_S0_P\&"
+.Cd "isic* at isapnp?"
+.Pp
+Dr. Neuhaus Niccy GO@
+.Cd options \&"DRN_NGO\&"
+.Cd "isic* at isapnp?"
+.Pp
+ELSA QuickStep 1000pro (ISA version):
+.Cd options \&"ELSA_QS1ISA\&"
+.Cd "isic* at isapnp?"
+.Pp
+Sedlbauer WinSpeed:
+.Cd options \&"SEDLBAUER\&"
+.Cd "isic* at isapnp?"
+.Pp
+Dynalink IS64PH:
+.Cd options \&"DYNALINK\&"
+.Cd "isic* at isapnp?"
+.Pp
+Cards on the PCI bus:
+.Pp
+ELSA QuickStep 1000pro (PCI version)
+.Cd options \&"ELSA_QS1PCI\&"
+.Cd "isic* at pci?"
+.Pp
+Cards on the PCMCIA or PCCARD bus:
+.Pp
+AVM Fritz!Card PCMCIA
+.Cd options \&"AVM_A1_PCMCIA\&"
+.Cd "isic* at pcmcia? function ?"
+.Pp
+ELSA MicroLink ISDN/MC
+.Cd options \&"ELSA_ISDNMC\&"
+.Cd "isic* at pcmcia? function ?"
+.Pp
+ELSA MicroLink MC/all
+.Cd options \&"ELSA_MCALL\&"
+.Cd "isic* at pcmcia? function ?"
+.Pp
+Cards on the Amiga Zorro bus:
+.Pp
+BSC/ITH ISDN Master, ITH ISDN MasterII or VMC ISDN Blaster
+.Cd "aster* at zbus?"
+.Cd "isic* at aster? port ?"
+.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.
+.Sh SUPPORTED CARDS
+.Bl -tag -width Ds -compact
+.It Ar Teles S0/8, Dr. Neuhaus Niccy 1008, Creatix ISDN-S0/8
+.Pp
+The required (optional for
+.Nx )
+.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
+.Nx )
+.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
+.Nx )
+.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
+.Nx )
+.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
+.Nx )
+.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.
+.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
+.Nx )
+.Em flag
+value is 18.
+.Pp
+Valid i/o port values must be in the range (<unknown>).
+.Pp
+Valid interrupt configurations are (<unknown>).
+.Pp
+.It Ar "BSC ISDN Master"
+.It Ar "ITH ISDN MasterII"
+.It Ar "VMC ISDN Blaster"
+.Pp
+The card addresses are auto-configured by the Zorro bus kernel subsystem.
+The ISDN functions of the boards are at known (to the driver) relative
+addresses.
+.Pp
+Note that currently, you have to jumper the card interupt for
+.Em IPL 2
+instead of IPL 6 (which is used by most AmigaOS software).
+.Pp
+Note that the ITH ISDN MasterII doesn't work in the DraCo Zorro bus. This
+is no
+.Nx
+problem, but general.
+.El
+.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.
+.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 AUTHORS
+.An -nosplit
+The
+.Nm
+driver and this manpage were written by
+.An Hellmuth Michaelis Aq hm@kts.org .
+It is based on earlier work of
+.An Arne Helme ,
+.An Andrew Gordon
+and
+.An Gary Jennejohn .
+.Pp
+The complete porting to and maintenance of
+.Nx
+was done by
+.An Martin Husemann Aq martin@rumolt.teuto.de .
+.Pp
+The
+.Nx Ns /Amiga
+ISDN Blaster/Master/MasterII driver was written by
+.An Ignatios Souvatzis Aq is@netbsd.org .
diff --git a/usr.sbin/i4b/man/itjc.4 b/usr.sbin/i4b/man/itjc.4
new file mode 100644
index 0000000..8469fe9
--- /dev/null
+++ b/usr.sbin/i4b/man/itjc.4
@@ -0,0 +1,66 @@
+.\"
+.\" Copyright (c) 2001 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Fri Jan 12 10:07:04 2001]
+.\"
+.Dd January 12, 2001
+.Dt ITJC 4
+.Os
+.Sh NAME
+.Nm itjc
+.Nd isdn4bsd NETjet-S / Teles PCI-TJ hardware driver
+.Sh SYNOPSIS
+.Cd "device itjc"
+.Sh DESCRIPTION
+The
+.Nm
+driver provides D-channel layer 1 supports as specified in ITU Recommendation
+I.430 and layer 1 support for the B-channel.
+.Pp
+The driver supports passive PCI ISDN cards based on the combination of
+the Siemens/Infineon ISAC chip and the Tiger Jet Network Inc. Tiger 300/320
+chip.
+.Pp
+Currently supported cards are the Traverse Technologies NETjet-S PCI ISDN
+card and the Teles PCI-TJ.
+.Sh CAVEATS
+The driver is in an experimental state.
+.Sh BUGS
+Always at least one left.
+.Sh STANDARDS
+CCITT Recommendation I.430
+.Sh SEE ALSO
+.Xr i4bq921 4 ,
+.Xr i4bq931 4
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was written by
+.An Sergio de Souza Prallon Aq prallon@tmp.com.br .
+.Pp
+This manpage was written by
+.An Hellmuth Michaelis Aq hm@FreeBSD.org .
diff --git a/usr.sbin/i4b/man/iwic.4 b/usr.sbin/i4b/man/iwic.4
new file mode 100644
index 0000000..4eac928
--- /dev/null
+++ b/usr.sbin/i4b/man/iwic.4
@@ -0,0 +1,70 @@
+.\"
+.\" Copyright (c) 2000 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: iwic.4,v 1.2 2000/03/16 15:25:05 hm Exp $
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Thu Mar 16 16:27:46 2000]
+.\"
+.Dd July 30, 1999
+.Dt IWIC 4
+.Os
+.Sh NAME
+.Nm iwic
+.Nd isdn4bsd Winbond ISDN Chip device driver
+.Sh SYNOPSIS
+.Cd "device iwic0"
+.Sh DESCRIPTION
+The
+.Nm
+driver provides D-channel layer 1 supports as specified in ITU Recommendation
+I.430 and layer 1 support for the B-channel.
+.Pp
+The driver supports passive PCI ISDN cards from various manufacturers
+based on the Winbond W6692 chip.
+.Sh SUPPORTED CARDS
+.Bl -tag -width Ds -compact
+.It Ar ASUSCOM P-IN100-ST-D
+.Pp
+.It Ar Dynalink IS64PPH
+.El
+.Sh CAVEATS
+The driver is still in a somewhat experimental state.
+.Sh BUGS
+Layer 1 persistent deactivation not yet implemented.
+.Sh STANDARDS
+CCITT Recommendation I.430
+.Sh SEE ALSO
+.Xr i4bq921 4 ,
+.Xr i4bq931 4
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was written by
+.An Dave Boyce Aq dave@abyss.demon.co.uk .
+.Pp
+This manpage was written by
+.An Hellmuth Michaelis Aq hm@kts.org .
diff --git a/usr.sbin/ifmcstat/Makefile b/usr.sbin/ifmcstat/Makefile
new file mode 100644
index 0000000..bda9b68
--- /dev/null
+++ b/usr.sbin/ifmcstat/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+# $FreeBSD$
+
+PROG= ifmcstat
+MAN= ifmcstat.8
+BINMODE= 550
+
+LDADD= -lkvm
+DPADD= ${LIBKVM}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ifmcstat/ifmcstat.8 b/usr.sbin/ifmcstat/ifmcstat.8
new file mode 100644
index 0000000..5f5a37f
--- /dev/null
+++ b/usr.sbin/ifmcstat/ifmcstat.8
@@ -0,0 +1,58 @@
+.\" $KAME: ifmcstat.8,v 1.3 2000/10/15 11:43:57 itojun Exp $
+.\"
+.\" Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the project nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd May 21, 1998
+.Dt IFMCSTAT 8
+.Os
+.Sh NAME
+.Nm ifmcstat
+.Nd dump multicast group management statistics per interface
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+The
+.Nm
+program dumps multicast group information from the kernel.
+It is similar but not identical to the output from
+.Nm netstat Fl ina .
+.Pp
+There are no command line options.
+.Pp
+Only root can use
+.Nm .
+.Sh SEE ALSO
+.Xr netstat 1
+.Rs
+.%A G. Malkin
+.%A R. Minnear
+.%R IPng for IPv6
+.%N RFC2080
+.Re
diff --git a/usr.sbin/ifmcstat/ifmcstat.c b/usr.sbin/ifmcstat/ifmcstat.c
new file mode 100644
index 0000000..4097c59
--- /dev/null
+++ b/usr.sbin/ifmcstat/ifmcstat.c
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <kvm.h>
+#include <nlist.h>
+#include <string.h>
+#include <limits.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_types.h>
+#include <net/if_dl.h>
+#include <netinet/in.h>
+#define _KERNEL
+#include <netinet/if_ether.h>
+#undef _KERNEL
+#include <netinet/in_var.h>
+#include <arpa/inet.h>
+
+#include <netdb.h>
+
+kvm_t *kvmd;
+
+struct nlist nl[] = {
+#define N_IFNET 0
+ { "_ifnet" },
+ { "" },
+};
+
+const char *inet6_n2a __P((struct in6_addr *));
+int main __P((void));
+char *ifname __P((struct ifnet *));
+void kread __P((u_long, void *, int));
+void if6_addrlist __P((struct ifaddr *));
+void in6_multilist __P((struct in6_multi *));
+struct in6_multi * in6_multientry __P((struct in6_multi *));
+
+#define KREAD(addr, buf, type) \
+ kread((u_long)addr, (void *)buf, sizeof(type))
+
+#ifdef N_IN6_MK
+struct multi6_kludge {
+ LIST_ENTRY(multi6_kludge) mk_entry;
+ struct ifnet *mk_ifp;
+ struct in6_multihead mk_head;
+};
+#endif
+
+const char *inet6_n2a(p)
+ struct in6_addr *p;
+{
+ static char buf[NI_MAXHOST];
+ struct sockaddr_in6 sin6;
+ u_int32_t scopeid;
+#ifdef NI_WITHSCOPEID
+ const int niflags = NI_NUMERICHOST | NI_WITHSCOPEID;
+#else
+ const int niflags = NI_NUMERICHOST;
+#endif
+
+ memset(&sin6, 0, sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_len = sizeof(struct sockaddr_in6);
+ sin6.sin6_addr = *p;
+ if (IN6_IS_ADDR_LINKLOCAL(p) || IN6_IS_ADDR_MC_LINKLOCAL(p)) {
+ scopeid = ntohs(*(u_int16_t *)&sin6.sin6_addr.s6_addr[2]);
+ if (scopeid) {
+ sin6.sin6_scope_id = scopeid;
+ sin6.sin6_addr.s6_addr[2] = 0;
+ sin6.sin6_addr.s6_addr[3] = 0;
+ }
+ }
+ if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len,
+ buf, sizeof(buf), NULL, 0, niflags) == 0)
+ return buf;
+ else
+ return "(invalid)";
+}
+
+int main()
+{
+ char buf[_POSIX2_LINE_MAX], ifname[IFNAMSIZ];
+ struct ifnet *ifp, *nifp, ifnet;
+
+ if ((kvmd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, buf)) == NULL) {
+ perror("kvm_openfiles");
+ exit(1);
+ }
+ if (kvm_nlist(kvmd, nl) < 0) {
+ perror("kvm_nlist");
+ exit(1);
+ }
+ if (nl[N_IFNET].n_value == 0) {
+ printf("symbol %s not found\n", nl[N_IFNET].n_name);
+ exit(1);
+ }
+ KREAD(nl[N_IFNET].n_value, &ifp, struct ifnet *);
+ while (ifp) {
+ KREAD(ifp, &ifnet, struct ifnet);
+ printf("%s:\n", if_indextoname(ifnet.if_index, ifname));
+
+ if6_addrlist(TAILQ_FIRST(&ifnet.if_addrhead));
+ nifp = TAILQ_NEXT(&ifnet, if_link);
+
+ /* not supported */
+
+ ifp = nifp;
+ }
+
+ exit(0);
+ /*NOTREACHED*/
+}
+
+char *ifname(ifp)
+ struct ifnet *ifp;
+{
+ static char buf[BUFSIZ];
+ struct ifnet ifnet;
+ char ifnamebuf[IFNAMSIZ];
+
+ KREAD(ifp, &ifnet, struct ifnet);
+ KREAD(ifnet.if_name, ifnamebuf, sizeof(ifnamebuf));
+ snprintf(buf, sizeof(buf), "%s%d", ifnamebuf,
+ ifnet.if_unit); /* does snprintf allow overlap copy?? */
+ return buf;
+}
+
+void kread(addr, buf, len)
+ u_long addr;
+ void *buf;
+ int len;
+{
+ if (kvm_read(kvmd, addr, buf, len) != len) {
+ perror("kvm_read");
+ exit(1);
+ }
+}
+
+void
+if6_addrlist(ifap)
+ struct ifaddr *ifap;
+{
+ struct ifaddr ifa;
+ struct sockaddr sa;
+ struct in6_ifaddr if6a;
+ struct ifaddr *ifap0;
+
+ ifap0 = ifap;
+ while (ifap) {
+ KREAD(ifap, &ifa, struct ifaddr);
+ if (ifa.ifa_addr == NULL)
+ goto nextifap;
+ KREAD(ifa.ifa_addr, &sa, struct sockaddr);
+ if (sa.sa_family != PF_INET6)
+ goto nextifap;
+ KREAD(ifap, &if6a, struct in6_ifaddr);
+ printf("\tinet6 %s\n", inet6_n2a(&if6a.ia_addr.sin6_addr));
+ nextifap:
+ ifap = TAILQ_NEXT(&ifa, ifa_link);
+ }
+ if (ifap0) {
+ struct ifnet ifnet;
+ struct ifmultiaddr ifm, *ifmp = 0;
+ struct sockaddr_dl sdl;
+
+ KREAD(ifap0, &ifa, struct ifaddr);
+ KREAD(ifa.ifa_ifp, &ifnet, struct ifnet);
+ if (TAILQ_FIRST(&ifnet.if_multiaddrs))
+ ifmp = TAILQ_FIRST(&ifnet.if_multiaddrs);
+ while (ifmp) {
+ KREAD(ifmp, &ifm, struct ifmultiaddr);
+ if (ifm.ifma_addr == NULL)
+ goto nextmulti;
+ KREAD(ifm.ifma_addr, &sa, struct sockaddr);
+ if (sa.sa_family != AF_INET6)
+ goto nextmulti;
+ (void)in6_multientry((struct in6_multi *)
+ ifm.ifma_protospec);
+ if (ifm.ifma_lladdr == 0)
+ goto nextmulti;
+ KREAD(ifm.ifma_lladdr, &sdl, struct sockaddr_dl);
+ printf("\t\t\tmcast-macaddr %s multicnt %d\n",
+ ether_ntoa((struct ether_addr *)LLADDR(&sdl)),
+ ifm.ifma_refcount);
+ nextmulti:
+ ifmp = TAILQ_NEXT(&ifm, ifma_link);
+ }
+ }
+#ifdef N_IN6_MK
+ if (nl[N_IN6_MK].n_value != 0) {
+ LIST_HEAD(in6_mktype, multi6_kludge) in6_mk;
+ struct multi6_kludge *mkp, mk;
+ char *nam;
+
+ KREAD(nl[N_IN6_MK].n_value, &in6_mk, struct in6_mktype);
+ KREAD(ifap0, &ifa, struct ifaddr);
+
+ nam = strdup(ifname(ifa.ifa_ifp));
+ if (!nam) {
+ fprintf(stderr, "ifmcstat: not enough core\n");
+ exit(1);
+ }
+
+ LIST_FOREACH(mkp, &in6_mk, mk_entry) {
+ KREAD(mkp, &mk, struct multi6_kludge);
+ if (strcmp(nam, ifname(mk.mk_ifp)) == 0 &&
+ LIST_FIRST(&mk.mk_head)) {
+ printf("\t(on kludge entry for %s)\n", nam);
+ in6_multilist(LIST_FIRST(&mk.mk_head));
+ }
+ }
+
+ free(nam);
+ }
+#endif
+}
+
+struct in6_multi *
+in6_multientry(mc)
+ struct in6_multi *mc;
+{
+ struct in6_multi multi;
+
+ KREAD(mc, &multi, struct in6_multi);
+ printf("\t\tgroup %s", inet6_n2a(&multi.in6m_addr));
+ printf(" refcnt %u\n", multi.in6m_refcount);
+ return(LIST_NEXT(&multi, in6m_entry));
+}
+
+void
+in6_multilist(mc)
+ struct in6_multi *mc;
+{
+ while (mc)
+ mc = in6_multientry(mc);
+}
diff --git a/usr.sbin/inetd/Makefile b/usr.sbin/inetd/Makefile
new file mode 100644
index 0000000..cf82f76
--- /dev/null
+++ b/usr.sbin/inetd/Makefile
@@ -0,0 +1,24 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+MAINTAINER= dwmalone
+
+PROG= inetd
+MAN= inetd.8
+MLINKS= inetd.8 inetd.conf.5
+SRCS= inetd.c builtins.c
+
+#WARNS?= 2
+CFLAGS+= -DLOGIN_CAP
+#CFLAGS+= -DSANITY_CHECK
+
+DPADD= ${LIBUTIL} ${LIBWRAP}
+LDADD= -lutil -lwrap
+
+.if !defined(RELEASE_CRUNCH)
+CFLAGS+= -DINET6 -DIPSEC
+DPADD+= ${LIBIPSEC}
+LDADD+= -lipsec
+.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/inetd/builtins.c b/usr.sbin/inetd/builtins.c
new file mode 100644
index 0000000..a28a2f3
--- /dev/null
+++ b/usr.sbin/inetd/builtins.c
@@ -0,0 +1,817 @@
+/*-
+ * Copyright (c) 1983, 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/filio.h>
+#include <sys/ioccom.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/ucred.h>
+#include <sys/uio.h>
+#include <sys/utsname.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "inetd.h"
+
+void chargen_dg(int, struct servtab *);
+void chargen_stream(int, struct servtab *);
+void daytime_dg(int, struct servtab *);
+void daytime_stream(int, struct servtab *);
+void discard_dg(int, struct servtab *);
+void discard_stream(int, struct servtab *);
+void echo_dg(int, struct servtab *);
+void echo_stream(int, struct servtab *);
+static int getline(int, char *, int);
+void iderror(int, int, int, const char *);
+void ident_stream(int, struct servtab *);
+void initring(void);
+unsigned long machtime(void);
+void machtime_dg(int, struct servtab *);
+void machtime_stream(int, struct servtab *);
+
+char ring[128];
+char *endring;
+
+
+struct biltin biltins[] = {
+ /* Echo received data */
+ { "echo", SOCK_STREAM, 1, -1, echo_stream },
+ { "echo", SOCK_DGRAM, 0, 1, echo_dg },
+
+ /* Internet /dev/null */
+ { "discard", SOCK_STREAM, 1, -1, discard_stream },
+ { "discard", SOCK_DGRAM, 0, 1, discard_dg },
+
+ /* Return 32 bit time since 1900 */
+ { "time", SOCK_STREAM, 0, -1, machtime_stream },
+ { "time", SOCK_DGRAM, 0, 1, machtime_dg },
+
+ /* Return human-readable time */
+ { "daytime", SOCK_STREAM, 0, -1, daytime_stream },
+ { "daytime", SOCK_DGRAM, 0, 1, daytime_dg },
+
+ /* Familiar character generator */
+ { "chargen", SOCK_STREAM, 1, -1, chargen_stream },
+ { "chargen", SOCK_DGRAM, 0, 1, chargen_dg },
+
+ { "tcpmux", SOCK_STREAM, 1, -1, (bi_fn_t *)tcpmux },
+
+ { "auth", SOCK_STREAM, 1, -1, ident_stream },
+
+ { NULL, 0, 0, 0, NULL }
+};
+
+/*
+ * RFC864 Character Generator Protocol. Generates character data without
+ * any regard for input.
+ */
+
+void
+initring(void)
+{
+ int i;
+
+ endring = ring;
+
+ for (i = 0; i <= 128; ++i)
+ if (isprint(i))
+ *endring++ = i;
+}
+
+/* Character generator */
+/* ARGSUSED */
+void
+chargen_dg(int s, struct servtab *sep)
+{
+ struct sockaddr_storage ss;
+ static char *rs;
+ int len;
+ socklen_t size;
+ char text[LINESIZ+2];
+
+ if (endring == 0) {
+ initring();
+ rs = ring;
+ }
+
+ size = sizeof(ss);
+ if (recvfrom(s, text, sizeof(text), 0,
+ (struct sockaddr *)&ss, &size) < 0)
+ return;
+
+ if (check_loop((struct sockaddr *)&ss, sep))
+ return;
+
+ if ((len = endring - rs) >= LINESIZ)
+ memmove(text, rs, LINESIZ);
+ else {
+ memmove(text, rs, len);
+ memmove(text + len, ring, LINESIZ - len);
+ }
+ if (++rs == endring)
+ rs = ring;
+ text[LINESIZ] = '\r';
+ text[LINESIZ + 1] = '\n';
+ (void) sendto(s, text, sizeof(text), 0, (struct sockaddr *)&ss, size);
+}
+
+/* Character generator */
+/* ARGSUSED */
+void
+chargen_stream(int s, struct servtab *sep)
+{
+ int len;
+ char *rs, text[LINESIZ+2];
+
+ inetd_setproctitle(sep->se_service, s);
+
+ if (!endring) {
+ initring();
+ rs = ring;
+ }
+
+ text[LINESIZ] = '\r';
+ text[LINESIZ + 1] = '\n';
+ for (rs = ring;;) {
+ if ((len = endring - rs) >= LINESIZ)
+ memmove(text, rs, LINESIZ);
+ else {
+ memmove(text, rs, len);
+ memmove(text + len, ring, LINESIZ - len);
+ }
+ if (++rs == endring)
+ rs = ring;
+ if (write(s, text, sizeof(text)) != sizeof(text))
+ break;
+ }
+ exit(0);
+}
+
+/*
+ * RFC867 Daytime Protocol. Sends the current date and time as an ascii
+ * character string without any regard for input.
+ */
+
+/* Return human-readable time of day */
+/* ARGSUSED */
+void
+daytime_dg(int s, struct servtab *sep)
+{
+ char buffer[256];
+ time_t now;
+ struct sockaddr_storage ss;
+ socklen_t size;
+
+ now = time((time_t *) 0);
+
+ size = sizeof(ss);
+ if (recvfrom(s, buffer, sizeof(buffer), 0,
+ (struct sockaddr *)&ss, &size) < 0)
+ return;
+
+ if (check_loop((struct sockaddr *)&ss, sep))
+ return;
+
+ (void) sprintf(buffer, "%.24s\r\n", ctime(&now));
+ (void) sendto(s, buffer, strlen(buffer), 0,
+ (struct sockaddr *)&ss, size);
+}
+
+/* Return human-readable time of day */
+/* ARGSUSED */
+void
+daytime_stream(int s, struct servtab *sep __unused)
+{
+ char buffer[256];
+ time_t now;
+
+ now = time((time_t *) 0);
+
+ (void) sprintf(buffer, "%.24s\r\n", ctime(&now));
+ (void) send(s, buffer, strlen(buffer), MSG_EOF);
+}
+
+/*
+ * RFC863 Discard Protocol. Any data received is thrown away and no response
+ * is sent.
+ */
+
+/* Discard service -- ignore data */
+/* ARGSUSED */
+void
+discard_dg(int s, struct servtab *sep __unused)
+{
+ char buffer[BUFSIZE];
+
+ (void) read(s, buffer, sizeof(buffer));
+}
+
+/* Discard service -- ignore data */
+/* ARGSUSED */
+void
+discard_stream(int s, struct servtab *sep)
+{
+ int ret;
+ char buffer[BUFSIZE];
+
+ inetd_setproctitle(sep->se_service, s);
+ while (1) {
+ while ((ret = read(s, buffer, sizeof(buffer))) > 0)
+ ;
+ if (ret == 0 || errno != EINTR)
+ break;
+ }
+ exit(0);
+}
+
+/*
+ * RFC862 Echo Protocol. Any data received is sent back to the sender as
+ * received.
+ */
+
+/* Echo service -- echo data back */
+/* ARGSUSED */
+void
+echo_dg(int s, struct servtab *sep)
+{
+ char buffer[65536]; /* Should be sizeof(max datagram). */
+ int i;
+ socklen_t size;
+ struct sockaddr_storage ss;
+
+ size = sizeof(ss);
+ if ((i = recvfrom(s, buffer, sizeof(buffer), 0,
+ (struct sockaddr *)&ss, &size)) < 0)
+ return;
+
+ if (check_loop((struct sockaddr *)&ss, sep))
+ return;
+
+ (void) sendto(s, buffer, i, 0, (struct sockaddr *)&ss, size);
+}
+
+/* Echo service -- echo data back */
+/* ARGSUSED */
+void
+echo_stream(int s, struct servtab *sep)
+{
+ char buffer[BUFSIZE];
+ int i;
+
+ inetd_setproctitle(sep->se_service, s);
+ while ((i = read(s, buffer, sizeof(buffer))) > 0 &&
+ write(s, buffer, i) > 0)
+ ;
+ exit(0);
+}
+
+/*
+ * RFC1413 Identification Protocol. Given a TCP port number pair, return a
+ * character string which identifies the owner of that connection on the
+ * server's system. Extended to allow for ~/.fakeid support and ~/.noident
+ * support.
+ */
+
+/* RFC 1413 says the following are the only errors you can return. */
+#define ID_INVALID "INVALID-PORT" /* Port number improperly specified. */
+#define ID_NOUSER "NO-USER" /* Port not in use/not identifable. */
+#define ID_HIDDEN "HIDDEN-USER" /* Hiden at user's request. */
+#define ID_UNKNOWN "UNKNOWN-ERROR" /* Everything else. */
+
+/* Generic ident_stream error-sending func */
+/* ARGSUSED */
+void
+iderror(int lport, int fport, int s, const char *er)
+{
+ char *p;
+
+ asprintf(&p, "%d , %d : ERROR : %s\r\n", lport, fport, er);
+ if (p == NULL) {
+ syslog(LOG_ERR, "asprintf: %m");
+ exit(EX_OSERR);
+ }
+ send(s, p, strlen(p), MSG_EOF);
+ free(p);
+
+ exit(0);
+}
+
+/* Ident service (AKA "auth") */
+/* ARGSUSED */
+void
+ident_stream(int s, struct servtab *sep)
+{
+ struct utsname un;
+ struct stat sb;
+ struct sockaddr_in sin4[2];
+#ifdef INET6
+ struct sockaddr_in6 sin6[2];
+#endif
+ struct sockaddr_storage ss[2];
+ struct xucred uc;
+ struct timeval tv = {
+ 10,
+ 0
+ }, to;
+ struct passwd *pw = NULL;
+ fd_set fdset;
+ char buf[BUFSIZE], *p, **av, *osname = NULL, e;
+ char idbuf[MAXLOGNAME] = ""; /* Big enough to hold uid in decimal. */
+ socklen_t socklen;
+ ssize_t ssize;
+ size_t size, bufsiz;
+ int c, fflag = 0, nflag = 0, rflag = 0, argc = 0;
+ int gflag = 0, iflag = 0, Fflag = 0, getcredfail = 0, onreadlen;
+ u_short lport, fport;
+
+ inetd_setproctitle(sep->se_service, s);
+ /*
+ * Reset getopt() since we are a fork() but not an exec() from
+ * a parent which used getopt() already.
+ */
+ optind = 1;
+ optreset = 1;
+ /*
+ * Take the internal argument vector and count it out to make an
+ * argument count for getopt. This can be used for any internal
+ * service to read arguments and use getopt() easily.
+ */
+ for (av = sep->se_argv; *av; av++)
+ argc++;
+ if (argc) {
+ int sec, usec;
+ size_t i;
+ u_int32_t rnd32;
+
+ while ((c = getopt(argc, sep->se_argv, "d:fFgino:rt:")) != -1)
+ switch (c) {
+ case 'd':
+ if (!gflag)
+ strlcpy(idbuf, optarg, sizeof(idbuf));
+ break;
+ case 'f':
+ fflag = 1;
+ break;
+ case 'F':
+ fflag = 1;
+ Fflag=1;
+ break;
+ case 'g':
+ gflag = 1;
+ rnd32 = 0; /* Shush, compiler. */
+ /*
+ * The number of bits in "rnd32" divided
+ * by the number of bits needed per iteration
+ * gives a more optimal way to reload the
+ * random number only when necessary.
+ *
+ * 32 bits from arc4random corrisponds to
+ * about 6 base-36 digits, so we reseed evey 6.
+ */
+ for (i = 0; i < sizeof(idbuf) - 1; i++) {
+ static const char *const base36 =
+ "0123456789"
+ "abcdefghijklmnopqrstuvwxyz";
+ if (i % 6 == 0)
+ rnd32 = arc4random();
+ idbuf[i] = base36[rnd32 % 36];
+ rnd32 /= 36;
+ }
+ idbuf[i] = '\0';
+ break;
+ case 'i':
+ iflag = 1;
+ break;
+ case 'n':
+ nflag = 1;
+ break;
+ case 'o':
+ osname = optarg;
+ break;
+ case 'r':
+ rflag = 1;
+ break;
+ case 't':
+ switch (sscanf(optarg, "%d.%d", &sec, &usec)) {
+ case 2:
+ tv.tv_usec = usec;
+ /* FALLTHROUGH */
+ case 1:
+ tv.tv_sec = sec;
+ break;
+ default:
+ if (debug)
+ warnx("bad -t argument");
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ if (osname == NULL) {
+ if (uname(&un) == -1)
+ iderror(0, 0, s, ID_UNKNOWN);
+ osname = un.sysname;
+ }
+
+ /*
+ * We're going to prepare for and execute reception of a
+ * packet of data from the user. The data is in the format
+ * "local_port , foreign_port\r\n" (with local being the
+ * server's port and foreign being the client's.)
+ */
+ gettimeofday(&to, NULL);
+ to.tv_sec += tv.tv_sec;
+ to.tv_usec += tv.tv_usec;
+ if (to.tv_usec >= 1000000) {
+ to.tv_usec -= 1000000;
+ to.tv_sec++;
+ }
+
+ size = 0;
+ bufsiz = sizeof(buf) - 1;
+ FD_ZERO(&fdset);
+ while (bufsiz > 0) {
+ gettimeofday(&tv, NULL);
+ tv.tv_sec = to.tv_sec - tv.tv_sec;
+ tv.tv_usec = to.tv_usec - tv.tv_usec;
+ if (tv.tv_usec < 0) {
+ tv.tv_usec += 1000000;
+ tv.tv_sec--;
+ }
+ if (tv.tv_sec < 0)
+ break;
+ FD_SET(s, &fdset);
+ if (select(s + 1, &fdset, NULL, NULL, &tv) == -1)
+ iderror(0, 0, s, ID_UNKNOWN);
+ if (ioctl(s, FIONREAD, &onreadlen) == -1)
+ iderror(0, 0, s, ID_UNKNOWN);
+ if ((size_t)onreadlen > bufsiz)
+ onreadlen = bufsiz;
+ ssize = read(s, &buf[size], (size_t)onreadlen);
+ if (ssize == -1)
+ iderror(0, 0, s, ID_UNKNOWN);
+ else if (ssize == 0)
+ break;
+ bufsiz -= ssize;
+ size += ssize;
+ if (memchr(&buf[size - ssize], '\n', ssize) != NULL)
+ break;
+ }
+ buf[size] = '\0';
+ /* Read two characters, and check for a delimiting character */
+ if (sscanf(buf, "%hu , %hu%c", &lport, &fport, &e) != 3 || isdigit(e))
+ iderror(0, 0, s, ID_INVALID);
+
+ /* Send garbage? */
+ if (gflag)
+ goto printit;
+
+ /*
+ * If not "real" (-r), send a HIDDEN-USER error for everything.
+ * If -d is used to set a fallback username, this is used to
+ * override it, and the fallback is returned instead.
+ */
+ if (!rflag) {
+ if (*idbuf == '\0')
+ iderror(lport, fport, s, ID_HIDDEN);
+ goto printit;
+ }
+
+ /*
+ * We take the input and construct an array of two sockaddr_ins
+ * which contain the local address information and foreign
+ * address information, respectively, used to look up the
+ * credentials for the socket (which are returned by the
+ * sysctl "net.inet.tcp.getcred" when we call it.)
+ */
+ socklen = sizeof(ss[0]);
+ if (getsockname(s, (struct sockaddr *)&ss[0], &socklen) == -1)
+ iderror(lport, fport, s, ID_UNKNOWN);
+ socklen = sizeof(ss[1]);
+ if (getpeername(s, (struct sockaddr *)&ss[1], &socklen) == -1)
+ iderror(lport, fport, s, ID_UNKNOWN);
+ if (ss[0].ss_family != ss[1].ss_family)
+ iderror(lport, fport, s, ID_UNKNOWN);
+ size = sizeof(uc);
+ switch (ss[0].ss_family) {
+ case AF_INET:
+ sin4[0] = *(struct sockaddr_in *)&ss[0];
+ sin4[0].sin_port = htons(lport);
+ sin4[1] = *(struct sockaddr_in *)&ss[1];
+ sin4[1].sin_port = htons(fport);
+ if (sysctlbyname("net.inet.tcp.getcred", &uc, &size, sin4,
+ sizeof(sin4)) == -1)
+ getcredfail = errno;
+ break;
+#ifdef INET6
+ case AF_INET6:
+ sin6[0] = *(struct sockaddr_in6 *)&ss[0];
+ sin6[0].sin6_port = htons(lport);
+ sin6[1] = *(struct sockaddr_in6 *)&ss[1];
+ sin6[1].sin6_port = htons(fport);
+ if (sysctlbyname("net.inet6.tcp6.getcred", &uc, &size, sin6,
+ sizeof(sin6)) == -1)
+ getcredfail = errno;
+ break;
+#endif
+ default: /* should not reach here */
+ getcredfail = EAFNOSUPPORT;
+ break;
+ }
+ if (getcredfail != 0 || uc.cr_version != XUCRED_VERSION) {
+ if (*idbuf == '\0')
+ iderror(lport, fport, s,
+ getcredfail == ENOENT ? ID_NOUSER : ID_UNKNOWN);
+ goto printit;
+ }
+
+ /* Look up the pw to get the username and home directory*/
+ errno = 0;
+ pw = getpwuid(uc.cr_uid);
+ if (pw == NULL)
+ iderror(lport, fport, s, errno == 0 ? ID_NOUSER : ID_UNKNOWN);
+
+ if (iflag)
+ snprintf(idbuf, sizeof(idbuf), "%u", (unsigned)pw->pw_uid);
+ else
+ strlcpy(idbuf, pw->pw_name, sizeof(idbuf));
+
+ /*
+ * If enabled, we check for a file named ".noident" in the user's
+ * home directory. If found, we return HIDDEN-USER.
+ */
+ if (nflag) {
+ if (asprintf(&p, "%s/.noident", pw->pw_dir) == -1)
+ iderror(lport, fport, s, ID_UNKNOWN);
+ if (lstat(p, &sb) == 0) {
+ free(p);
+ iderror(lport, fport, s, ID_HIDDEN);
+ }
+ free(p);
+ }
+
+ /*
+ * Here, if enabled, we read a user's ".fakeid" file in their
+ * home directory. It consists of a line containing the name
+ * they want.
+ */
+ if (fflag) {
+ int fakeid_fd;
+
+ /*
+ * Here we set ourself to effectively be the user, so we don't
+ * open any files we have no permission to open, especially
+ * symbolic links to sensitive root-owned files or devices.
+ */
+ if (initgroups(pw->pw_name, pw->pw_gid) == -1)
+ iderror(lport, fport, s, ID_UNKNOWN);
+ if (seteuid(pw->pw_uid) == -1)
+ iderror(lport, fport, s, ID_UNKNOWN);
+ /*
+ * We can't stat() here since that would be a race
+ * condition.
+ * Therefore, we open the file we have permissions to open
+ * and if it's not a regular file, we close it and end up
+ * returning the user's real username.
+ */
+ if (asprintf(&p, "%s/.fakeid", pw->pw_dir) == -1)
+ iderror(lport, fport, s, ID_UNKNOWN);
+ fakeid_fd = open(p, O_RDONLY | O_NONBLOCK);
+ free(p);
+ if (fakeid_fd == -1 || fstat(fakeid_fd, &sb) == -1 ||
+ !S_ISREG(sb.st_mode))
+ goto fakeid_fail;
+
+ if ((ssize = read(fakeid_fd, buf, sizeof(buf) - 1)) < 0)
+ goto fakeid_fail;
+ buf[ssize] = '\0';
+
+ /*
+ * Usually, the file will have the desired identity
+ * in the form "identity\n". Allow for leading white
+ * space and trailing white space/end of line.
+ */
+ p = buf;
+ p += strspn(p, " \t");
+ p[strcspn(p, " \t\r\n")] = '\0';
+ if (strlen(p) > MAXLOGNAME - 1) /* Too long (including nul)? */
+ p[MAXLOGNAME - 1] = '\0';
+
+ /*
+ * If the name is a zero-length string or matches it
+ * the id or name of another user (unless permitted by -F)
+ * then it is invalid.
+ */
+ if (*p == '\0')
+ goto fakeid_fail;
+ if (!Fflag) {
+ if (iflag) {
+ if (p[strspn(p, "0123456789")] == '\0' &&
+ getpwuid(atoi(p)) != NULL)
+ goto fakeid_fail;
+ } else {
+ if (getpwnam(p) != NULL)
+ goto fakeid_fail;
+ }
+ }
+
+ strlcpy(idbuf, p, sizeof(idbuf));
+
+fakeid_fail:
+ if (fakeid_fd != -1)
+ close(fakeid_fd);
+ }
+
+printit:
+ /* Finally, we make and send the reply. */
+ if (asprintf(&p, "%d , %d : USERID : %s : %s\r\n", lport, fport, osname,
+ idbuf) == -1) {
+ syslog(LOG_ERR, "asprintf: %m");
+ exit(EX_OSERR);
+ }
+ send(s, p, strlen(p), MSG_EOF);
+ free(p);
+
+ exit(0);
+}
+
+/*
+ * RFC738 Time Server.
+ * Return a machine readable date and time, in the form of the
+ * number of seconds since midnight, Jan 1, 1900. Since gettimeofday
+ * returns the number of seconds since midnight, Jan 1, 1970,
+ * we must add 2208988800 seconds to this figure to make up for
+ * some seventy years Bell Labs was asleep.
+ */
+
+unsigned long
+machtime(void)
+{
+ struct timeval tv;
+
+ if (gettimeofday(&tv, (struct timezone *)NULL) < 0) {
+ if (debug)
+ warnx("unable to get time of day");
+ return (0L);
+ }
+#define OFFSET ((u_long)25567 * 24*60*60)
+ return (htonl((long)(tv.tv_sec + OFFSET)));
+#undef OFFSET
+}
+
+/* ARGSUSED */
+void
+machtime_dg(int s, struct servtab *sep)
+{
+ unsigned long result;
+ struct sockaddr_storage ss;
+ socklen_t size;
+
+ size = sizeof(ss);
+ if (recvfrom(s, (char *)&result, sizeof(result), 0,
+ (struct sockaddr *)&ss, &size) < 0)
+ return;
+
+ if (check_loop((struct sockaddr *)&ss, sep))
+ return;
+
+ result = machtime();
+ (void) sendto(s, (char *) &result, sizeof(result), 0,
+ (struct sockaddr *)&ss, size);
+}
+
+/* ARGSUSED */
+void
+machtime_stream(int s, struct servtab *sep __unused)
+{
+ unsigned long result;
+
+ result = machtime();
+ (void) send(s, (char *) &result, sizeof(result), MSG_EOF);
+}
+
+/*
+ * RFC1078 TCP Port Service Multiplexer (TCPMUX). Service connections to
+ * services based on the service name sent.
+ *
+ * Based on TCPMUX.C by Mark K. Lottor November 1988
+ * sri-nic::ps:<mkl>tcpmux.c
+ */
+
+#define MAX_SERV_LEN (256+2) /* 2 bytes for \r\n */
+#define strwrite(fd, buf) (void) write(fd, buf, sizeof(buf)-1)
+
+static int /* # of characters upto \r,\n or \0 */
+getline(int fd, char *buf, int len)
+{
+ int count = 0, n;
+ struct sigaction sa;
+
+ sa.sa_flags = 0;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_handler = SIG_DFL;
+ sigaction(SIGALRM, &sa, (struct sigaction *)0);
+ do {
+ alarm(10);
+ n = read(fd, buf, len-count);
+ alarm(0);
+ if (n == 0)
+ return (count);
+ if (n < 0)
+ return (-1);
+ while (--n >= 0) {
+ if (*buf == '\r' || *buf == '\n' || *buf == '\0')
+ return (count);
+ count++;
+ buf++;
+ }
+ } while (count < len);
+ return (count);
+}
+
+struct servtab *
+tcpmux(int s)
+{
+ struct servtab *sep;
+ char service[MAX_SERV_LEN+1];
+ int len;
+
+ /* Get requested service name */
+ if ((len = getline(s, service, MAX_SERV_LEN)) < 0) {
+ strwrite(s, "-Error reading service name\r\n");
+ return (NULL);
+ }
+ service[len] = '\0';
+
+ if (debug)
+ warnx("tcpmux: someone wants %s", service);
+
+ /*
+ * Help is a required command, and lists available services,
+ * one per line.
+ */
+ if (!strcasecmp(service, "help")) {
+ for (sep = servtab; sep; sep = sep->se_next) {
+ if (!ISMUX(sep))
+ continue;
+ (void)write(s,sep->se_service,strlen(sep->se_service));
+ strwrite(s, "\r\n");
+ }
+ return (NULL);
+ }
+
+ /* Try matching a service in inetd.conf with the request */
+ for (sep = servtab; sep; sep = sep->se_next) {
+ if (!ISMUX(sep))
+ continue;
+ if (!strcasecmp(service, sep->se_service)) {
+ if (ISMUXPLUS(sep)) {
+ strwrite(s, "+Go\r\n");
+ }
+ return (sep);
+ }
+ }
+ strwrite(s, "-Service not available\r\n");
+ return (NULL);
+}
diff --git a/usr.sbin/inetd/inetd.8 b/usr.sbin/inetd/inetd.8
new file mode 100644
index 0000000..f9d70f2
--- /dev/null
+++ b/usr.sbin/inetd/inetd.8
@@ -0,0 +1,906 @@
+.\" Copyright (c) 1985, 1991, 1993, 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" from: @(#)inetd.8 8.3 (Berkeley) 4/13/94
+.\" $FreeBSD$
+.\"
+.Dd February 7, 1996
+.Dt INETD 8
+.Os
+.Sh NAME
+.Nm inetd
+.Nd internet
+.Dq super-server
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Op Fl l
+.Op Fl w
+.Op Fl W
+.Op Fl c Ar maximum
+.Op Fl C Ar rate
+.Op Fl a Ar address | hostname
+.Op Fl p Ar filename
+.Op Fl R Ar rate
+.Op 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 of successful connections.
+.It Fl w
+Turn on TCP Wrapping for external services.
+See the
+.Sx "IMPLEMENTATION NOTES"
+section for more information on TCP Wrappers support.
+.It Fl W
+Turn on TCP Wrapping for internal services which are built in to
+.Nm .
+.It Fl c Ar maximum
+Specify the default maximum number of
+simultaneous invocations of each service;
+the default is unlimited.
+May be overridden on a per-service basis with the "max-child"
+parameter.
+.It Fl C Ar rate
+Specify the default maximum number of times a service can be invoked
+from a single IP address in one minute; the default is unlimited.
+May be overridden on a per-service basis with the
+"max-connections-per-ip-per-minute" parameter.
+.It Fl R Ar rate
+Specify the maximum number of times a service can be invoked
+in one minute; the default is 256.
+A rate of 0 allows an unlimited number of invocations.
+.It Fl a
+Specify one specific IP address to bind to.
+Alternatively, a hostname can be specified,
+in which case the IPv4 or IPv6 address
+which corresponds to that hostname is used.
+Usually a hostname is specified when
+.Nm
+is run inside a
+.Xr jail 8 ,
+in which case the hostname corresponds to the
+.Xr jail 8
+environment.
+.Pp
+When hostname specification is used
+and both IPv4 and IPv6 bindings are desired,
+one entry with the appropriate
+.Em protocol
+type for each binding
+is required for each service in
+.Pa /etc/inetd.conf .
+For example,
+a TCP-based service would need two entries,
+one using
+.Dq tcp4
+for the
+.Em protocol
+and the other using
+.Dq tcp6 .
+See the explanation of the
+.Pa /etc/inetd.conf
+.Em protocol
+field below.
+.It Fl p
+Specify an alternate file in which to store the process ID.
+.El
+.Pp
+Upon execution,
+.Nm
+reads its configuration information from a configuration
+file which, by default, is
+.Pa /etc/inetd.conf .
+There must be an entry for each field of the configuration
+file, with entries for each field separated by a tab or
+a space. Comments are denoted by a
+.Dq #
+at the beginning
+of a line. There must be an entry for each field. The
+fields of the configuration file are as follows:
+.Pp
+.Bd -unfilled -offset indent -compact
+service name
+socket type
+protocol
+{wait|nowait}[/max-child[/max-connections-per-ip-per-minute]]
+user[:group][/login-class]
+server program
+server program arguments
+.Ed
+.Pp
+To specify an
+.Tn "ONC RPC" Ns -based
+service, the entry would contain these fields:
+.Pp
+.Bd -unfilled -offset indent -compact
+service name/version
+socket type
+rpc/protocol
+user[:group][/login-class]
+server program
+server program arguments
+.Ed
+.Pp
+There are two types of services that
+.Nm
+can start: standard and TCPMUX.
+A standard service has a well-known port assigned to it;
+it may be a service that implements an official Internet standard or is a
+.Bx Ns -specific
+service.
+As described in
+.Tn RFC 1078 ,
+TCPMUX services are nonstandard services that do not have a
+well-known port assigned to them.
+They are invoked from
+.Nm
+when a program connects to the
+.Dq tcpmux
+well-known port and specifies
+the service name.
+This feature is useful for adding locally-developed servers.
+TCPMUX requests are only accepted when the multiplexor service itself
+is enabled, above and beyond and specific TCPMUX-based servers; see the
+discussion of internal services below.
+.Pp
+The
+.Em service-name
+entry is the name of a valid service in
+the file
+.Pa /etc/services ,
+or the specification of a
+.Ux
+domain socket (see below).
+For
+.Dq internal
+services (discussed below), the service
+name
+should
+be the official name of the service (that is, the first entry in
+.Pa /etc/services ) .
+When used to specify an
+.Tn "ONC RPC" Ns -based
+service, this field is a valid RPC service name in
+the file
+.Pa /etc/rpc .
+The part on the right of the
+.Dq /
+is the RPC version number.
+This
+can simply be a single numeric argument or a range of versions.
+A range is bounded by the low version to the high version -
+.Dq rusers/1-3 .
+For TCPMUX services, the value of the
+.Em service-name
+field consists of the string
+.Dq tcpmux
+followed by a slash and the
+locally-chosen service name.
+The service names listed in
+.Pa /etc/services
+and the name
+.Dq help
+are reserved.
+Try to choose unique names for your TCPMUX services by prefixing them with
+your organization's name and suffixing them with a version number.
+.Pp
+The
+.Em socket-type
+should be one of
+.Dq stream ,
+.Dq dgram ,
+.Dq raw ,
+.Dq rdm ,
+or
+.Dq seqpacket ,
+depending on whether the socket is a stream, datagram, raw,
+reliably delivered message, or sequenced packet socket.
+TCPMUX services must use
+.Dq stream .
+.Pp
+The
+.Em protocol
+must be a valid protocol or
+.Dq unix .
+Examples are
+.Dq tcp
+or
+.Dq udp ,
+both of which imply IPv4 for backward compatibility.
+The names
+.Dq tcp4
+and
+.Dq udp4
+specify IPv4 only.
+The names
+.Dq tcp6
+and
+.Dq udp6
+specify IPv6 only.
+The names
+.Dq tcp46
+and
+.Dq udp46
+specify that the entry accepts both IPv4 and IPv6 connections
+via a wildcard
+.Dv AF_INET6
+socket.
+If it is desired that the service is reachable via T/TCP, one should
+specify
+.Dq tcp/ttcp ,
+which implies IPv4 for backward compatibility.
+The name
+.Dq tcp4/ttcp
+specifies IPv4 only, while
+.Dq tcp6/ttcp
+specifies IPv6 only.
+The name
+.Dq tcp46/ttcp
+specify that the entry accepts both IPv6 and IPv6 connections
+via a wildcard
+.Dv AF_INET6
+socket.
+Rpc based services
+(for which only IPv4 is supported at this time)
+are specified with the
+.Dq rpc/tcp
+or
+.Dq rpc/udp
+service type.
+TCPMUX services must use
+.Dq tcp ,
+.Dq tcp4 ,
+.Dq tcp6
+or
+.Dq tcp46 .
+.Pp
+The
+.Em wait/nowait
+entry specifies whether the server that is invoked by
+.Nm
+will take over
+the socket associated with the service access point, and thus whether
+.Nm
+should wait for the server to exit before listening for new service
+requests.
+Datagram servers must use
+.Dq wait ,
+as they are always invoked with the original datagram socket bound
+to the specified service address.
+These servers must read at least one datagram from the socket
+before exiting.
+If a datagram server connects
+to its peer, freeing the socket so
+.Nm
+can receive further messages on the socket, it is said to be
+a
+.Dq multi-threaded
+server;
+it should read one datagram from the socket and create a new socket
+connected to the peer.
+It should fork, and the parent should then exit
+to allow
+.Nm
+to check for new service requests to spawn new servers.
+Datagram servers which process all incoming datagrams
+on a socket and eventually time out are said to be
+.Dq single-threaded .
+.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 ,
+and the server is given only the newly-accepted socket connected
+to a client of the service.
+Most stream-based services operate in this manner.
+Stream-based servers that use
+.Dq wait
+are started with the listening service socket, and must accept
+at least one connection request before exiting.
+Such a server would normally accept and process incoming connection
+requests until a timeout.
+TCPMUX services must use
+.Dq nowait .
+.Pp
+The maximum number of outstanding child processes (or
+.Dq threads )
+for a
+.Dq nowait
+service may be explicitly specified by appending a
+.Dq /
+followed by the number to the
+.Dq nowait
+keyword.
+Normally
+(or if a value of zero is specified) there is no maximum.
+Otherwise,
+once the maximum is reached, further connection attempts will be
+queued up until an existing child process exits.
+This also works
+in the case of
+.Dq wait
+mode, although a value other than one (the
+default) might not make sense in some cases.
+You can also specify the maximum number of connections per minute
+for a given IP address by appending
+a
+.Dq /
+followed by the number to the maximum number of
+outstanding child processes.
+Once the maximum is reached, further
+connections from this IP address will be dropped until the end of the
+minute.
+.Pp
+The
+.Em user
+entry should contain the user name of the user as whom the server
+should run. This allows for servers to be given less permission
+than root.
+Optional
+.Em group
+part separated by
+.Dq \&:
+allows to specify group name different
+than default group for this user.
+Optional
+.Em login-class
+part separated by
+.Dq /
+allows to specify login class different
+than default
+.Dq daemon
+login class.
+.Pp
+The
+.Em server-program
+entry should contain the pathname of the program which is to be
+executed by
+.Nm
+when a request is found on its socket. If
+.Nm
+provides this service internally, this entry should
+be
+.Dq internal .
+.Pp
+The
+.Em server program arguments
+should be just as arguments
+normally are, starting with argv[0], which is the name of
+the program. If the service is provided internally, the
+.Em service-name
+of the service (and any arguments to it) or the word
+.Dq internal
+should take the place of this entry.
+.Pp
+Currently, the only internal service to take arguments is
+.Dq auth .
+Without options, the service will always return
+.Dq ERROR\ : HIDDEN-USER .
+The available arguments to this service that alter its behavior are:
+.Bl -tag -width indent
+.It Fl d Ar fallback
+Provide a
+.Ar fallback
+username.
+If the real
+.Dq auth
+service is enabled
+(with the
+.Fl r
+option discussed below),
+return this username instead of an error
+when lookups fail
+for either socket credentials or the username.
+If the real
+.Dq auth
+service is disabled,
+return this username for every request.
+This is primarily useful when running this service on a NAT machine.
+.It Fl g
+Instead of returning
+the user's name to the ident requester,
+report a
+username made up of random alphanumeric characters,
+e.g.\&
+.Dq c0c993 .
+The
+.Fl g
+flag overrides not only the user names,
+but also any fallback name,
+.Pa .fakeid
+or
+.Pa .noident
+files.
+.It Fl t Xo
+.Ar sec Ns Op . Ns Ar usec
+.Xc
+Specify a timeout for the service.
+The default timeout is 10.0 seconds.
+.It Fl r
+Offer a real
+.Dq auth
+service, as per RFC 1413.
+All the remaining flags apply only in this case.
+.It Fl i
+Return numeric user IDs instead of usernames.
+.It Fl f
+If the file
+.Pa .fakeid
+exists in the home directory of the identified user, report the username
+found in that file instead of the real username.
+If the username found in
+.Pa .fakeid
+is that of an existing user,
+then the real username is reported.
+If the
+.Fl i
+flag is also given then the username in
+.Pa .fakeid
+is checked against existing user IDs instead.
+.It Fl F
+same as
+.Fl f
+but without the restriction that the username in
+.Pa .fakeid
+must not match an existing user.
+.It Fl n
+If the file
+.Pa .noident
+exists in the home directory of the identified user, return
+.Dq ERROR\ : HIDDEN-USER .
+This overrides any
+.Pa fakeid
+file which might exist.
+.It Fl o Ar osname
+Use
+.Ar osname
+instead of the name of the system as reported by
+.Xr uname 3 .
+.El
+.Pp
+The
+.Nm
+program
+also provides several other
+.Dq trivial
+services internally by use of
+routines within itself. These services are
+.Dq echo ,
+.Dq discard ,
+.Dq chargen
+(character generator),
+.Dq daytime
+(human readable time), and
+.Dq time
+(machine readable time, in the form of the number of seconds since
+midnight, January 1, 1900). All of these services are available in
+both TCP and UDP versions; the UDP versions will refuse service if the
+request specifies a reply port corresponding to any internal service.
+(This is done as a defense against looping attacks; the remote IP address
+is logged.)
+For details of these services, consult the
+appropriate
+.Tn RFC
+document.
+.Pp
+The TCPMUX-demultiplexing service is also implemented as an internal service.
+For any TCPMUX-based service to function, the following line must be included
+in
+.Pa inetd.conf :
+.Bd -literal -offset indent
+tcpmux stream tcp nowait root internal
+.Ed
+.Pp
+When given the
+.Fl l
+option
+.Nm
+will log an entry to syslog each time a connection is accepted, noting the
+service selected and the IP-number of the remote requester if available.
+Unless otherwise specified in the configuration file,
+and in the absence of the
+.Fl W
+and
+.Fl w
+options,
+.Nm
+will log to the
+.Dq daemon
+facility.
+.Pp
+The
+.Nm
+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.
+.Sh IMPLEMENTATION NOTES
+.Ss TCP Wrappers
+When given the
+.Fl w
+option,
+.Nm
+will wrap all services specified as
+.Dq stream nowait
+or
+.Dq dgram
+except for
+.Dq internal
+services.
+If the
+.Fl W
+option is given, such
+.Dq internal
+services will be wrapped.
+If both options are given, wrapping for both
+internal and external services will be enabled.
+Either wrapping option
+will cause failed connections to be logged to the
+.Dq auth
+syslog facility.
+Adding the
+.Fl l
+flag to the wrapping options will include successful connections in the
+logging to the
+.Dq auth
+facility.
+.Pp
+Note that
+.Nm
+only wraps requests for a
+.Dq wait
+service while no servers are available to service requests.
+Once a
+connection to such a service has been allowed,
+.Nm
+has no control
+over subsequent connections to the service until no more servers
+are left listening for connection requests.
+.Pp
+When wrapping is enabled, the
+.Pa tcpd
+daemon is not required, as that functionality is builtin.
+For more information on TCP Wrappers, see the relevant documentation
+.Pq Xr hosts_access 5 .
+When reading that document, keep in mind that
+.Dq internal
+services have no associated daemon name.
+Therefore, the service name
+as specified in
+.Pa inetd.conf
+should be used as the daemon name for
+.Dq internal
+services.
+.Ss TCPMUX
+.Tn RFC 1078
+describes the TCPMUX protocol:
+``A TCP client connects to a foreign host on TCP port 1. It sends the
+service name followed by a carriage-return line-feed <CRLF>. The
+service name is never case sensitive. The server replies with a
+single character indicating positive (+) or negative (\-)
+acknowledgment, immediately followed by an optional message of
+explanation, terminated with a <CRLF>. If the reply was positive,
+the selected protocol begins; otherwise the connection is closed.''
+The program is passed the TCP connection as file descriptors 0 and 1.
+.Pp
+If the TCPMUX service name begins with a
+.Dq + ,
+.Nm
+returns the positive reply for the program.
+This allows you to invoke programs that use stdin/stdout
+without putting any special server code in them.
+.Pp
+The special service name
+.Dq help
+causes
+.Nm
+to list TCPMUX services in
+.Pa inetd.conf .
+.Ss IPsec
+The implementation includes a tiny hack
+to support IPsec policy settings for each socket.
+A special form of comment line, starting with
+.Dq Li #@ ,
+is interpreted as a policy specifier.
+Everything after the
+.Dq Li #@
+will be used as an IPsec policy string,
+as described in
+.Xr ipsec_set_policy 3 .
+Each
+policy specifier is applied to all the following lines in
+.Pa inetd.conf
+until the next policy specifier.
+An empty policy specifier resets the IPsec policy.
+.Pp
+If an invalid IPsec policy specifier appears in
+.Pa inetd.conf ,
+.Nm
+will provide an error message via the
+.Xr syslog 3
+interface and abort execution.
+.Ss Ux Domain Sockets
+In addition to running services on IP sockets,
+.Nm
+can also manage
+.Ux
+domain sockets.
+To do this you specify a
+.Em protocol
+of
+.Dq unix
+and specify the
+.Ux
+domain socket as the
+.Em service-name .
+The
+.Em service-type
+may be
+.Dq stream
+or
+.Dq dgram .
+The specification of the socket must be
+an absolute path name,
+optionally prefixed by an owner and mode
+of the form
+.Em :user:group:mode: .
+The specification:
+.Pp
+.Dl ":news:daemon:220:/var/run/sock"
+.Pp
+creates a socket owned
+by user
+.Dq news
+in group
+.Dq daemon
+with permissions allowing only that user and group to connect.
+The default owner is the user that
+.Nm
+is running as.
+The default mode only allows the socket's owner to connect.
+.Pp
+.Sy WARNING :
+while creating
+.Ux
+domain socket,
+.Nm
+must change the ownership and permissions on the socket.
+This can only be done securely if
+the directory in which the socket is created
+is writable only by root.
+Do
+.Em NOT
+use
+.Nm
+to create sockets in world writable directories,
+such as
+.Pa /tmp ,
+instead use
+.Pa /var/run
+or a similar directory.
+.Pp
+Internal services may be run on
+.Ux
+domain sockets, in the usual way.
+In this case
+the name of the internal service
+is determined using
+the last component of the socket's pathname.
+.Sh "FILES"
+.Bl -tag -width /var/run/inetd.pid -compact
+.It Pa /etc/inetd.conf
+configuration file
+.It Pa /etc/rpc
+translation of service names to RPC program numbers
+.It Pa /etc/services
+translation of service names to port numbers
+.It Pa /var/run/inetd.pid
+the pid of the currently running
+.Nm
+.El
+.Sh "EXAMPLES"
+Here are several example service entries for the various types of services:
+.Bd -literal
+ftp stream tcp nowait root /usr/libexec/ftpd ftpd -l
+ntalk dgram udp wait root /usr/libexec/ntalkd ntalkd
+telnet stream tcp6 nowait root /usr/libexec/telnetd telnetd
+shell stream tcp46 nowait root /usr/libexec/rshd rshd
+tcpmux/+date stream tcp nowait guest /bin/date date
+tcpmux/phonebook stream tcp nowait guest /usr/local/bin/phonebook phonebook
+rstatd/1-3 dgram rpc/udp wait root /usr/libexec/rpc.rstatd rpc.rstatd
+/var/run/echo stream unix nowait root internal
+#@ ipsec ah/require
+chargen stream tcp nowait root internal
+#@
+.Ed
+.Sh "ERROR MESSAGES"
+The
+.Nm
+server
+logs error messages using
+.Xr syslog 3 .
+Important error messages and their explanations are:
+.Pp
+.Bl -ohang -compact
+.It Xo
+.Ar service Ns / Ns Ar protocol
+.No "server failing (looping), service terminated."
+.Xc
+The number of requests for the specified service in the past minute
+exceeded the limit.
+The limit exists to prevent a broken program
+or a malicious user from swamping the system.
+This message may occur for several reasons:
+.Bl -enum -offset indent
+.It
+There are many hosts requesting the service within a short time period.
+.It
+A broken client program is requesting the service too frequently.
+.It
+A malicious user is running a program to invoke the service in
+a denial-of-service attack.
+.It
+The invoked service program has an error that causes clients
+to retry quickly.
+.El
+.Pp
+Use the
+.Fl R Ar rate
+option,
+as described above, to change the rate limit.
+Once the limit is reached, the service will be
+reenabled automatically in 10 minutes.
+.Pp
+.It Xo
+.Ar service Ns / Ns Ar protocol :
+.No \&No such user
+.Ar user ,
+.No service ignored
+.Xc
+.It Xo
+.Ar service Ns / Ns Ar protocol :
+.No getpwnam :
+.Ar user :
+.No \&No such user
+.Xc
+No entry for
+.Ar user
+exists in the
+.Xr passwd 5
+database.
+The first message
+occurs when
+.Nm
+(re)reads the configuration file.
+The second message occurs when the
+service is invoked.
+.Pp
+.It Xo
+.Ar service :
+.No can't set uid
+.Ar uid
+.Xc
+.It Xo
+.Ar service :
+.No can't set gid
+.Ar gid
+.Xc
+The user or group ID for the entry's
+.Ar user
+field is invalid.
+.Pp
+.It "setsockopt(SO_PRIVSTATE): Operation not supported"
+The
+.Nm
+program attempted to renounce the privileged state associated with a
+socket but was unable to.
+.El
+.Sh SEE ALSO
+.Xr ipsec_set_policy 3 ,
+.Xr hosts_access 5 ,
+.Xr hosts_options 5 ,
+.Xr login.conf 5 ,
+.Xr passwd 5 ,
+.Xr rpc 5 ,
+.Xr services 5 ,
+.Xr comsat 8 ,
+.Xr fingerd 8 ,
+.Xr ftpd 8 ,
+.Xr rexecd 8 ,
+.Xr rlogind 8 ,
+.Xr rpcbind 8 ,
+.Xr rshd 8 ,
+.Xr telnetd 8 ,
+.Xr tftpd 8
+.Rs
+.%A Michael C. St. Johns
+.%T Identification Protocol
+.%O RFC1413
+.Re
+.Sh HISTORY
+The
+.Nm
+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.
+The IPsec hack was contributed by the KAME project in 1999.
+The
+.Fx
+TCP Wrappers support first appeared in
+.Fx 3.2 .
diff --git a/usr.sbin/inetd/inetd.c b/usr.sbin/inetd/inetd.c
new file mode 100644
index 0000000..759dd16
--- /dev/null
+++ b/usr.sbin/inetd/inetd.c
@@ -0,0 +1,2190 @@
+/*
+ * Copyright (c) 1983, 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1983, 1991, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)from: inetd.c 8.4 (Berkeley) 4/13/94";
+#endif
+#endif /* not lint */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Inetd - Internet super-server
+ *
+ * This program invokes all internet services as needed. Connection-oriented
+ * services are invoked each time a connection is made, by creating a process.
+ * This process is passed the connection as file descriptor 0 and is expected
+ * to do a getpeername to find out the source host and port.
+ *
+ * Datagram oriented services are invoked when a datagram
+ * arrives; a process is created and passed a pending message
+ * on file descriptor 0. Datagram servers may either connect
+ * to their peer, freeing up the original socket for inetd
+ * to receive further messages on, or ``take over the socket'',
+ * processing all arriving datagrams and, eventually, timing
+ * out. The first type of server is said to be ``multi-threaded'';
+ * the second type of server ``single-threaded''.
+ *
+ * Inetd uses a configuration file which is read at startup
+ * and, possibly, at some later time in response to a hangup signal.
+ * The configuration file is ``free format'' with fields given in the
+ * order shown below. Continuation lines for an entry must begin with
+ * a space or tab. All fields must be present in each entry.
+ *
+ * service name must be in /etc/services
+ * or name a tcpmux service
+ * or specify a unix domain socket
+ * socket type stream/dgram/raw/rdm/seqpacket
+ * protocol tcp[4][6][/faith,ttcp], udp[4][6], unix
+ * wait/nowait single-threaded/multi-threaded
+ * user user to run daemon as
+ * server program full path name
+ * server program arguments maximum of MAXARGS (20)
+ *
+ * TCP services without official port numbers are handled with the
+ * RFC1078-based tcpmux internal service. Tcpmux listens on port 1 for
+ * requests. When a connection is made from a foreign host, the service
+ * requested is passed to tcpmux, which looks it up in the servtab list
+ * and returns the proper entry for the service. Tcpmux returns a
+ * negative reply if the service doesn't exist, otherwise the invoked
+ * server is expected to return the positive reply if the service type in
+ * inetd.conf file has the prefix "tcpmux/". If the service type has the
+ * prefix "tcpmux/+", tcpmux will return the positive reply for the
+ * process; this is for compatibility with older server code, and also
+ * allows you to invoke programs that use stdin/stdout without putting any
+ * special server code in them. Services that use tcpmux are "nowait"
+ * because they do not have a well-known port and hence cannot listen
+ * for new requests.
+ *
+ * For RPC services
+ * service name/version must be in /etc/rpc
+ * socket type stream/dgram/raw/rdm/seqpacket
+ * protocol rpc/tcp, rpc/udp
+ * wait/nowait single-threaded/multi-threaded
+ * user user to run daemon as
+ * server program full path name
+ * server program arguments maximum of MAXARGS
+ *
+ * Comment lines are indicated by a `#' in column 1.
+ *
+ * #ifdef IPSEC
+ * Comment lines that start with "#@" denote IPsec policy string, as described
+ * in ipsec_set_policy(3). This will affect all the following items in
+ * inetd.conf(8). To reset the policy, just use "#@" line. By default,
+ * there's no IPsec policy.
+ * #endif
+ */
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+
+#include <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 <tcpd.h>
+#include <unistd.h>
+#include <libutil.h>
+#include <sysexits.h>
+#include <ctype.h>
+
+#include "inetd.h"
+#include "pathnames.h"
+
+#ifdef IPSEC
+#include <netinet6/ipsec.h>
+#ifndef IPSEC_POLICY_IPSEC /* no ipsec support on old ipsec */
+#undef IPSEC
+#endif
+#endif
+
+/* wrapper for KAME-special getnameinfo() */
+#ifndef NI_WITHSCOPEID
+#define NI_WITHSCOPEID 0
+#endif
+
+#ifndef LIBWRAP_ALLOW_FACILITY
+# define LIBWRAP_ALLOW_FACILITY LOG_AUTH
+#endif
+#ifndef LIBWRAP_ALLOW_SEVERITY
+# define LIBWRAP_ALLOW_SEVERITY LOG_INFO
+#endif
+#ifndef LIBWRAP_DENY_FACILITY
+# define LIBWRAP_DENY_FACILITY LOG_AUTH
+#endif
+#ifndef LIBWRAP_DENY_SEVERITY
+# define LIBWRAP_DENY_SEVERITY LOG_WARNING
+#endif
+
+#define ISWRAP(sep) \
+ ( ((wrap_ex && !(sep)->se_bi) || (wrap_bi && (sep)->se_bi)) \
+ && (sep->se_family == AF_INET || sep->se_family == AF_INET6) \
+ && ( ((sep)->se_accept && (sep)->se_socktype == SOCK_STREAM) \
+ || (sep)->se_socktype == SOCK_DGRAM))
+
+#ifdef LOGIN_CAP
+#include <login_cap.h>
+
+/* see init.c */
+#define RESOURCE_RC "daemon"
+
+#endif
+
+#ifndef MAXCHILD
+#define MAXCHILD -1 /* maximum number of this service
+ < 0 = no limit */
+#endif
+
+#ifndef MAXCPM
+#define MAXCPM -1 /* rate limit invocations from a
+ single remote address,
+ < 0 = no limit */
+#endif
+
+#ifndef TOOMANY
+#define TOOMANY 256 /* don't start more than TOOMANY */
+#endif
+#define CNT_INTVL 60 /* servers in CNT_INTVL sec. */
+#define RETRYTIME (60*10) /* retry after bind or server fail */
+#define MAX_MAXCHLD 32767 /* max allowable max children */
+
+#define SIGBLOCK (sigmask(SIGCHLD)|sigmask(SIGHUP)|sigmask(SIGALRM))
+
+void close_sep(struct servtab *);
+void flag_signal(int);
+void flag_config(int);
+void config(void);
+int cpmip(const struct servtab *, int);
+void endconfig(void);
+struct servtab *enter(struct servtab *);
+void freeconfig(struct servtab *);
+struct servtab *getconfigent(void);
+int matchservent(const char *, const char *, const char *);
+char *nextline(FILE *);
+void addchild(struct servtab *, int);
+void flag_reapchild(int);
+void reapchild(void);
+void enable(struct servtab *);
+void disable(struct servtab *);
+void flag_retry(int);
+void retry(void);
+int setconfig(void);
+void setup(struct servtab *);
+#ifdef IPSEC
+void ipsecsetup(struct servtab *);
+#endif
+void unregisterrpc(register struct servtab *sep);
+
+int allow_severity;
+int deny_severity;
+int wrap_ex = 0;
+int wrap_bi = 0;
+int debug = 0;
+int log = 0;
+int maxsock; /* highest-numbered descriptor */
+fd_set allsock;
+int options;
+int timingout;
+int toomany = TOOMANY;
+int maxchild = MAXCHILD;
+int maxcpm = MAXCPM;
+struct servent *sp;
+struct rpcent *rpc;
+char *hostname = NULL;
+struct sockaddr_in *bind_sa4;
+int no_v4bind = 1;
+#ifdef INET6
+struct sockaddr_in6 *bind_sa6;
+int no_v6bind = 1;
+#endif
+int signalpipe[2];
+#ifdef SANITY_CHECK
+int nsock;
+#endif
+uid_t euid;
+gid_t egid;
+mode_t mask;
+
+struct servtab *servtab;
+
+extern struct biltin biltins[];
+
+const char *CONFIG = _PATH_INETDCONF;
+const char *pid_file = _PATH_INETDPID;
+
+int
+getvalue(const char *arg, int *value, const char *whine)
+{
+ int tmp;
+ char *p;
+
+ tmp = strtol(arg, &p, 0);
+ if (tmp < 0 || *p) {
+ syslog(LOG_ERR, whine, arg);
+ return 1; /* failure */
+ }
+ *value = tmp;
+ return 0; /* success */
+}
+
+int
+main(int argc, char **argv)
+{
+ struct servtab *sep;
+ struct passwd *pwd;
+ struct group *grp;
+ struct sigaction sa, saalrm, sachld, sahup, sapipe;
+ int tmpint, ch, dofork;
+ pid_t pid;
+ char buf[50];
+#ifdef LOGIN_CAP
+ login_cap_t *lc = NULL;
+#endif
+ struct request_info req;
+ int denied;
+ char *service = NULL;
+ union {
+ struct sockaddr peer_un;
+ struct sockaddr_in peer_un4;
+ struct sockaddr_in6 peer_un6;
+ struct sockaddr_storage peer_max;
+ } p_un;
+#define peer p_un.peer_un
+#define peer4 p_un.peer_un4
+#define peer6 p_un.peer_un6
+#define peermax p_un.peer_max
+ int i;
+ struct addrinfo hints, *res;
+ const char *servname;
+ int error;
+
+ openlog("inetd", LOG_PID | LOG_NOWAIT | LOG_PERROR, LOG_DAEMON);
+
+ while ((ch = getopt(argc, argv, "dlwWR: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':
+ hostname = optarg;
+ break;
+ case 'p':
+ pid_file = optarg;
+ break;
+ case 'w':
+ wrap_ex++;
+ break;
+ case 'W':
+ wrap_bi++;
+ break;
+ case '?':
+ default:
+ syslog(LOG_ERR,
+ "usage: inetd [-dlwW] [-a address] [-R rate]"
+ " [-c maximum] [-C rate]"
+ " [-p pidfile] [conf-file]");
+ exit(EX_USAGE);
+ }
+ /*
+ * Initialize Bind Addrs.
+ * When hostname is NULL, wild card bind addrs are obtained from
+ * getaddrinfo(). But getaddrinfo() requires at least one of
+ * hostname or servname is non NULL.
+ * So when hostname is NULL, set dummy value to servname.
+ */
+ servname = (hostname == NULL) ? "discard" /* dummy */ : NULL;
+
+ bzero(&hints, sizeof(struct addrinfo));
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = AF_UNSPEC;
+ error = getaddrinfo(hostname, servname, &hints, &res);
+ if (error != 0) {
+ syslog(LOG_ERR, "-a %s: %s", hostname, gai_strerror(error));
+ if (error == EAI_SYSTEM)
+ syslog(LOG_ERR, "%s", strerror(errno));
+ exit(EX_USAGE);
+ }
+ do {
+ if (res->ai_addr == NULL) {
+ syslog(LOG_ERR, "-a %s: getaddrinfo failed", hostname);
+ exit(EX_USAGE);
+ }
+ switch (res->ai_addr->sa_family) {
+ case AF_INET:
+ if (no_v4bind == 0)
+ continue;
+ bind_sa4 = (struct sockaddr_in *)res->ai_addr;
+ /* init port num in case servname is dummy */
+ bind_sa4->sin_port = 0;
+ no_v4bind = 0;
+ continue;
+#ifdef INET6
+ case AF_INET6:
+ if (no_v6bind == 0)
+ continue;
+ bind_sa6 = (struct sockaddr_in6 *)res->ai_addr;
+ /* init port num in case servname is dummy */
+ bind_sa6->sin6_port = 0;
+ no_v6bind = 0;
+ continue;
+#endif
+ }
+ if (no_v4bind == 0
+#ifdef INET6
+ && no_v6bind == 0
+#endif
+ )
+ break;
+ } while ((res = res->ai_next) != NULL);
+ if (no_v4bind != 0
+#ifdef INET6
+ && no_v6bind != 0
+#endif
+ ) {
+ syslog(LOG_ERR, "-a %s: unknown address family", hostname);
+ exit(EX_USAGE);
+ }
+
+ euid = geteuid();
+ egid = getegid();
+ umask(mask = umask(0777));
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc > 0)
+ CONFIG = argv[0];
+ if (debug == 0) {
+ FILE *fp;
+ if (daemon(0, 0) < 0) {
+ syslog(LOG_WARNING, "daemon(0,0) failed: %m");
+ }
+ /* From now on we don't want syslog messages going to stderr. */
+ closelog();
+ openlog("inetd", LOG_PID | LOG_NOWAIT, LOG_DAEMON);
+ /*
+ * In case somebody has started inetd manually, we need to
+ * clear the logname, so that old servers run as root do not
+ * get the user's logname..
+ */
+ if (setlogin("") < 0) {
+ syslog(LOG_WARNING, "cannot clear logname: %m");
+ /* no big deal if it fails.. */
+ }
+ pid = getpid();
+ fp = fopen(pid_file, "w");
+ if (fp) {
+ fprintf(fp, "%ld\n", (long)pid);
+ fclose(fp);
+ } else {
+ syslog(LOG_WARNING, "%s: %m", pid_file);
+ }
+ }
+ sa.sa_flags = 0;
+ sigemptyset(&sa.sa_mask);
+ sigaddset(&sa.sa_mask, SIGALRM);
+ sigaddset(&sa.sa_mask, SIGCHLD);
+ sigaddset(&sa.sa_mask, SIGHUP);
+ sa.sa_handler = flag_retry;
+ sigaction(SIGALRM, &sa, &saalrm);
+ config();
+ sa.sa_handler = flag_config;
+ sigaction(SIGHUP, &sa, &sahup);
+ sa.sa_handler = flag_reapchild;
+ sigaction(SIGCHLD, &sa, &sachld);
+ sa.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &sa, &sapipe);
+
+ {
+ /* space for daemons to overwrite environment for ps */
+#define DUMMYSIZE 100
+ char dummy[DUMMYSIZE];
+
+ (void)memset(dummy, 'x', DUMMYSIZE - 1);
+ dummy[DUMMYSIZE - 1] = '\0';
+ (void)setenv("inetd_dummy", dummy, 1);
+ }
+
+ if (pipe(signalpipe) != 0) {
+ syslog(LOG_ERR, "pipe: %m");
+ exit(EX_OSERR);
+ }
+ FD_SET(signalpipe[0], &allsock);
+#ifdef SANITY_CHECK
+ nsock++;
+#endif
+ if (signalpipe[0] > maxsock)
+ maxsock = signalpipe[0];
+ if (signalpipe[1] > maxsock)
+ maxsock = signalpipe[1];
+
+ for (;;) {
+ int n, ctrl;
+ fd_set readable;
+
+#ifdef SANITY_CHECK
+ if (nsock == 0) {
+ syslog(LOG_ERR, "%s: nsock=0", __FUNCTION__);
+ exit(EX_SOFTWARE);
+ }
+#endif
+ readable = allsock;
+ if ((n = select(maxsock + 1, &readable, (fd_set *)0,
+ (fd_set *)0, (struct timeval *)0)) <= 0) {
+ if (n < 0 && errno != EINTR) {
+ syslog(LOG_WARNING, "select: %m");
+ sleep(1);
+ }
+ continue;
+ }
+ /* handle any queued signal flags */
+ if (FD_ISSET(signalpipe[0], &readable)) {
+ int nsig;
+ if (ioctl(signalpipe[0], FIONREAD, &nsig) != 0) {
+ syslog(LOG_ERR, "ioctl: %m");
+ exit(EX_OSERR);
+ }
+ while (--nsig >= 0) {
+ char c;
+ if (read(signalpipe[0], &c, 1) != 1) {
+ syslog(LOG_ERR, "read: %m");
+ exit(EX_OSERR);
+ }
+ if (debug)
+ warnx("handling signal flag %c", c);
+ switch(c) {
+ case 'A': /* sigalrm */
+ retry();
+ break;
+ case 'C': /* sigchld */
+ reapchild();
+ break;
+ case 'H': /* sighup */
+ config();
+ break;
+ }
+ }
+ }
+ for (sep = servtab; n && sep; sep = sep->se_next)
+ if (sep->se_fd != -1 && FD_ISSET(sep->se_fd, &readable)) {
+ n--;
+ if (debug)
+ warnx("someone wants %s", sep->se_service);
+ if (sep->se_accept && sep->se_socktype == SOCK_STREAM) {
+ i = 1;
+ if (ioctl(sep->se_fd, FIONBIO, &i) < 0)
+ syslog(LOG_ERR, "ioctl (FIONBIO, 1): %m");
+ ctrl = accept(sep->se_fd, (struct sockaddr *)0,
+ (socklen_t *)0);
+ if (debug)
+ warnx("accept, ctrl %d", ctrl);
+ if (ctrl < 0) {
+ if (errno != EINTR)
+ syslog(LOG_WARNING,
+ "accept (for %s): %m",
+ sep->se_service);
+ if (sep->se_accept &&
+ sep->se_socktype == SOCK_STREAM)
+ close(ctrl);
+ continue;
+ }
+ i = 0;
+ if (ioctl(sep->se_fd, FIONBIO, &i) < 0)
+ syslog(LOG_ERR, "ioctl1(FIONBIO, 0): %m");
+ if (ioctl(ctrl, FIONBIO, &i) < 0)
+ syslog(LOG_ERR, "ioctl2(FIONBIO, 0): %m");
+ if (cpmip(sep, ctrl) < 0) {
+ close(ctrl);
+ continue;
+ }
+ } else
+ ctrl = sep->se_fd;
+ if (log && !ISWRAP(sep)) {
+ char pname[INET6_ADDRSTRLEN] = "unknown";
+ socklen_t sl;
+ sl = sizeof peermax;
+ if (getpeername(ctrl, (struct sockaddr *)
+ &peermax, &sl)) {
+ sl = sizeof peermax;
+ if (recvfrom(ctrl, buf, sizeof(buf),
+ MSG_PEEK,
+ (struct sockaddr *)&peermax,
+ &sl) >= 0) {
+ getnameinfo((struct sockaddr *)&peermax,
+ peer.sa_len,
+ pname, sizeof(pname),
+ NULL, 0,
+ NI_NUMERICHOST|
+ NI_WITHSCOPEID);
+ }
+ } else {
+ getnameinfo((struct sockaddr *)&peermax,
+ peer.sa_len,
+ pname, sizeof(pname),
+ NULL, 0,
+ NI_NUMERICHOST|
+ NI_WITHSCOPEID);
+ }
+ syslog(LOG_INFO,"%s from %s", sep->se_service, pname);
+ }
+ (void) sigblock(SIGBLOCK);
+ pid = 0;
+ /*
+ * Fork for all external services, builtins which need to
+ * fork and anything we're wrapping (as wrapping might
+ * block or use hosts_options(5) twist).
+ */
+ dofork = !sep->se_bi || sep->se_bi->bi_fork || ISWRAP(sep);
+ if (dofork) {
+ if (sep->se_count++ == 0)
+ (void)gettimeofday(&sep->se_time, (struct timezone *)NULL);
+ else if (toomany > 0 && sep->se_count >= toomany) {
+ struct timeval now;
+
+ (void)gettimeofday(&now, (struct timezone *)NULL);
+ if (now.tv_sec - sep->se_time.tv_sec >
+ CNT_INTVL) {
+ sep->se_time = now;
+ sep->se_count = 1;
+ } else {
+ syslog(LOG_ERR,
+ "%s/%s server failing (looping), service terminated",
+ sep->se_service, sep->se_proto);
+ if (sep->se_accept &&
+ sep->se_socktype == SOCK_STREAM)
+ close(ctrl);
+ close_sep(sep);
+ 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);
+ sigaction(SIGALRM, &saalrm, (struct sigaction *)0);
+ sigaction(SIGCHLD, &sachld, (struct sigaction *)0);
+ sigaction(SIGHUP, &sahup, (struct sigaction *)0);
+ /* SIGPIPE reset before exec */
+ }
+ /*
+ * Call tcpmux to find the real service to exec.
+ */
+ if (sep->se_bi &&
+ sep->se_bi->bi_fn == (bi_fn_t *) tcpmux) {
+ sep = tcpmux(ctrl);
+ if (sep == NULL) {
+ close(ctrl);
+ _exit(0);
+ }
+ }
+ if (ISWRAP(sep)) {
+ inetd_setproctitle("wrapping", ctrl);
+ service = sep->se_server_name ?
+ sep->se_server_name : sep->se_service;
+ request_init(&req, RQ_DAEMON, service, RQ_FILE, ctrl, NULL);
+ fromhost(&req);
+ deny_severity = LIBWRAP_DENY_FACILITY|LIBWRAP_DENY_SEVERITY;
+ allow_severity = LIBWRAP_ALLOW_FACILITY|LIBWRAP_ALLOW_SEVERITY;
+ denied = !hosts_access(&req);
+ if (denied) {
+ syslog(deny_severity,
+ "refused connection from %.500s, service %s (%s%s)",
+ eval_client(&req), service, sep->se_proto,
+ (((struct sockaddr *)req.client->sin)->sa_family == AF_INET6 && !IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)req.client->sin)->sin6_addr)) ? "6" : "");
+ if (sep->se_socktype != SOCK_STREAM)
+ recv(ctrl, buf, sizeof (buf), 0);
+ if (dofork) {
+ sleep(1);
+ _exit(0);
+ }
+ }
+ if (log) {
+ syslog(allow_severity,
+ "connection from %.500s, service %s (%s%s)",
+ eval_client(&req), service, sep->se_proto,
+ (((struct sockaddr *)req.client->sin)->sa_family == AF_INET6 && !IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)req.client->sin)->sin6_addr)) ? "6" : "");
+ }
+ }
+ if (sep->se_bi) {
+ (*sep->se_bi->bi_fn)(ctrl, sep);
+ } 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);
+ if (sep->se_socktype != SOCK_STREAM)
+ recv(0, buf, sizeof (buf), 0);
+ }
+ if (dofork)
+ _exit(0);
+ }
+ if (sep->se_accept && sep->se_socktype == SOCK_STREAM)
+ close(ctrl);
+ }
+ }
+}
+
+/*
+ * Add a signal flag to the signal flag queue for later handling
+ */
+
+void
+flag_signal(int c)
+{
+ char ch = c;
+
+ if (write(signalpipe[1], &ch, 1) != 1) {
+ syslog(LOG_ERR, "write: %m");
+ _exit(EX_OSERR);
+ }
+}
+
+/*
+ * Record a new child pid for this service. If we've reached the
+ * limit on children, then stop accepting incoming requests.
+ */
+
+void
+addchild(struct servtab *sep, pid_t pid)
+{
+ if (sep->se_maxchild <= 0)
+ return;
+#ifdef SANITY_CHECK
+ if (sep->se_numchild >= sep->se_maxchild) {
+ syslog(LOG_ERR, "%s: %d >= %d",
+ __FUNCTION__, sep->se_numchild, sep->se_maxchild);
+ exit(EX_SOFTWARE);
+ }
+#endif
+ sep->se_pids[sep->se_numchild++] = pid;
+ if (sep->se_numchild == sep->se_maxchild)
+ disable(sep);
+}
+
+/*
+ * Some child process has exited. See if it's on somebody's list.
+ */
+
+void
+flag_reapchild(int signo __unused)
+{
+ flag_signal('C');
+}
+
+void
+reapchild(void)
+{
+ int k, status;
+ pid_t pid;
+ struct servtab *sep;
+
+ for (;;) {
+ pid = wait3(&status, WNOHANG, (struct rusage *)0);
+ if (pid <= 0)
+ break;
+ if (debug)
+ warnx("%d reaped, 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(int signo __unused)
+{
+ flag_signal('H');
+}
+
+void
+config(void)
+{
+ 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 &&
+ sep->se_socktype == new->se_socktype &&
+ sep->se_family == new->se_family)
+ break;
+ if (sep != 0) {
+ int i;
+
+#define SWAP(t,a, b) { t c = a; a = b; b = c; }
+ omask = sigblock(SIGBLOCK);
+ if (sep->se_nomapped != new->se_nomapped) {
+ sep->se_nomapped = new->se_nomapped;
+ sep->se_reset = 1;
+ }
+ /* copy over outstanding child pids */
+ if (sep->se_maxchild > 0 && new->se_maxchild > 0) {
+ new->se_numchild = sep->se_numchild;
+ if (new->se_numchild > new->se_maxchild)
+ new->se_numchild = new->se_maxchild;
+ memcpy(new->se_pids, sep->se_pids,
+ new->se_numchild * sizeof(*new->se_pids));
+ }
+ SWAP(pid_t *, sep->se_pids, new->se_pids);
+ sep->se_maxchild = new->se_maxchild;
+ sep->se_numchild = new->se_numchild;
+ sep->se_maxcpm = new->se_maxcpm;
+ sep->se_bi = new->se_bi;
+ /* might need to turn on or off service now */
+ if (sep->se_fd >= 0) {
+ if (sep->se_maxchild > 0
+ && sep->se_numchild == sep->se_maxchild) {
+ if (FD_ISSET(sep->se_fd, &allsock))
+ disable(sep);
+ } else {
+ if (!FD_ISSET(sep->se_fd, &allsock))
+ enable(sep);
+ }
+ }
+ sep->se_accept = new->se_accept;
+ SWAP(char *, sep->se_user, new->se_user);
+ SWAP(char *, sep->se_group, new->se_group);
+#ifdef LOGIN_CAP
+ SWAP(char *, sep->se_class, new->se_class);
+#endif
+ SWAP(char *, sep->se_server, new->se_server);
+ SWAP(char *, sep->se_server_name, new->se_server_name);
+ for (i = 0; i < MAXARGV; i++)
+ SWAP(char *, sep->se_argv[i], new->se_argv[i]);
+#ifdef IPSEC
+ SWAP(char *, sep->se_policy, new->se_policy);
+ ipsecsetup(sep);
+#endif
+ sigsetmask(omask);
+ freeconfig(new);
+ if (debug)
+ print_service("REDO", sep);
+ } else {
+ sep = enter(new);
+ if (debug)
+ print_service("ADD ", sep);
+ }
+ sep->se_checked = 1;
+ if (ISMUX(sep)) {
+ sep->se_fd = -1;
+ continue;
+ }
+ switch (sep->se_family) {
+ case AF_INET:
+ if (no_v4bind != 0) {
+ sep->se_fd = -1;
+ continue;
+ }
+ break;
+#ifdef INET6
+ case AF_INET6:
+ if (no_v6bind != 0) {
+ sep->se_fd = -1;
+ continue;
+ }
+ break;
+#endif
+ }
+ if (!sep->se_rpc) {
+ if (sep->se_family != AF_UNIX) {
+ sp = getservbyname(sep->se_service, sep->se_proto);
+ if (sp == 0) {
+ syslog(LOG_ERR, "%s/%s: unknown service",
+ sep->se_service, sep->se_proto);
+ sep->se_checked = 0;
+ continue;
+ }
+ }
+ switch (sep->se_family) {
+ case AF_INET:
+ if (sp->s_port != sep->se_ctrladdr4.sin_port) {
+ sep->se_ctrladdr4.sin_port =
+ sp->s_port;
+ sep->se_reset = 1;
+ }
+ break;
+#ifdef INET6
+ case AF_INET6:
+ if (sp->s_port !=
+ sep->se_ctrladdr6.sin6_port) {
+ sep->se_ctrladdr6.sin6_port =
+ sp->s_port;
+ sep->se_reset = 1;
+ }
+ break;
+#endif
+ }
+ if (sep->se_reset != 0 && sep->se_fd >= 0)
+ close_sep(sep);
+ } else {
+ rpc = getrpcbyname(sep->se_service);
+ if (rpc == 0) {
+ syslog(LOG_ERR, "%s/%s unknown RPC service",
+ sep->se_service, sep->se_proto);
+ if (sep->se_fd != -1)
+ (void) close(sep->se_fd);
+ sep->se_fd = -1;
+ continue;
+ }
+ if (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(sep);
+ }
+ (void) sigsetmask(omask);
+}
+
+void
+unregisterrpc(struct servtab *sep)
+{
+ u_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(int signo __unused)
+{
+ flag_signal('A');
+}
+
+void
+retry(void)
+{
+ struct servtab *sep;
+
+ timingout = 0;
+ for (sep = servtab; sep; sep = sep->se_next)
+ if (sep->se_fd == -1 && !ISMUX(sep))
+ setup(sep);
+}
+
+void
+setup(struct servtab *sep)
+{
+ int on = 1;
+
+ if ((sep->se_fd = socket(sep->se_family, sep->se_socktype, 0)) < 0) {
+ if (debug)
+ warn("socket failed on %s/%s",
+ sep->se_service, sep->se_proto);
+ syslog(LOG_ERR, "%s/%s: socket: %m",
+ sep->se_service, sep->se_proto);
+ return;
+ }
+#define turnon(fd, opt) \
+setsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on))
+ if (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) &&
+ turnon(sep->se_fd, SO_DEBUG) < 0)
+ syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
+ if (turnon(sep->se_fd, SO_REUSEADDR) < 0)
+ syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m");
+#ifdef SO_PRIVSTATE
+ if (turnon(sep->se_fd, SO_PRIVSTATE) < 0)
+ syslog(LOG_ERR, "setsockopt (SO_PRIVSTATE): %m");
+#endif
+ /* tftpd opens a new connection then needs more infos */
+ if ((sep->se_family == AF_INET6) &&
+ (strcmp(sep->se_proto, "udp") == 0) &&
+ (sep->se_accept == 0) &&
+ (setsockopt(sep->se_fd, IPPROTO_IPV6, IPV6_PKTINFO,
+ (char *)&on, sizeof (on)) < 0))
+ syslog(LOG_ERR, "setsockopt (IPV6_RECVPKTINFO): %m");
+#ifdef IPV6_BINDV6ONLY
+ if (sep->se_family == AF_INET6) {
+ int flag = sep->se_nomapped ? 1 : 0;
+ if (setsockopt(sep->se_fd, IPPROTO_IPV6, IPV6_BINDV6ONLY,
+ (char *)&flag, sizeof (flag)) < 0)
+ syslog(LOG_ERR, "setsockopt (IPV6_BINDV6ONLY): %m");
+ }
+#endif /* IPV6_BINDV6ONLY */
+#undef turnon
+ if (sep->se_type == TTCP_TYPE)
+ if (setsockopt(sep->se_fd, IPPROTO_TCP, TCP_NOPUSH,
+ (char *)&on, sizeof (on)) < 0)
+ syslog(LOG_ERR, "setsockopt (TCP_NOPUSH): %m");
+#ifdef IPV6_FAITH
+ if (sep->se_type == FAITH_TYPE) {
+ if (setsockopt(sep->se_fd, IPPROTO_IPV6, IPV6_FAITH, &on,
+ sizeof(on)) < 0) {
+ syslog(LOG_ERR, "setsockopt (IPV6_FAITH): %m");
+ }
+ }
+#endif
+#ifdef IPSEC
+ ipsecsetup(sep);
+#endif
+ if (sep->se_family == AF_UNIX) {
+ (void) unlink(sep->se_ctrladdr_un.sun_path);
+ umask(0777); /* Make socket with conservative permissions */
+ }
+ if (bind(sep->se_fd, (struct sockaddr *)&sep->se_ctrladdr,
+ sep->se_ctrladdr_size) < 0) {
+ if (debug)
+ warn("bind failed on %s/%s",
+ sep->se_service, sep->se_proto);
+ syslog(LOG_ERR, "%s/%s: bind: %m",
+ sep->se_service, sep->se_proto);
+ (void) close(sep->se_fd);
+ sep->se_fd = -1;
+ if (!timingout) {
+ timingout = 1;
+ alarm(RETRYTIME);
+ }
+ if (sep->se_family == AF_UNIX)
+ umask(mask);
+ return;
+ }
+ if (sep->se_family == AF_UNIX) {
+ /* Ick - fch{own,mod} don't work on Unix domain sockets */
+ if (chown(sep->se_service, sep->se_sockuid, sep->se_sockgid) < 0)
+ syslog(LOG_ERR, "chown socket: %m");
+ if (chmod(sep->se_service, sep->se_sockmode) < 0)
+ syslog(LOG_ERR, "chmod socket: %m");
+ umask(mask);
+ }
+ if (sep->se_rpc) {
+ u_int i;
+ socklen_t len = sep->se_ctrladdr_size;
+
+ if (sep->se_family != AF_INET) {
+ syslog(LOG_ERR,
+ "%s/%s: unsupported address family for rpc",
+ sep->se_service, sep->se_proto);
+ (void) close(sep->se_fd);
+ sep->se_fd = -1;
+ return;
+ }
+ 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_ctrladdr4.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);
+ }
+}
+
+#ifdef IPSEC
+void
+ipsecsetup(sep)
+ struct servtab *sep;
+{
+ char *buf;
+ char *policy_in = NULL;
+ char *policy_out = NULL;
+ int level;
+ int opt;
+
+ switch (sep->se_family) {
+ case AF_INET:
+ level = IPPROTO_IP;
+ opt = IP_IPSEC_POLICY;
+ break;
+#ifdef INET6
+ case AF_INET6:
+ level = IPPROTO_IPV6;
+ opt = IPV6_IPSEC_POLICY;
+ break;
+#endif
+ default:
+ return;
+ }
+
+ if (!sep->se_policy || sep->se_policy[0] == '\0') {
+ static char def_in[] = "in entrust", def_out[] = "out entrust";
+ policy_in = def_in;
+ policy_out = def_out;
+ } else {
+ if (!strncmp("in", sep->se_policy, 2))
+ policy_in = sep->se_policy;
+ else if (!strncmp("out", sep->se_policy, 3))
+ policy_out = sep->se_policy;
+ else {
+ syslog(LOG_ERR, "invalid security policy \"%s\"",
+ sep->se_policy);
+ return;
+ }
+ }
+
+ if (policy_in != NULL) {
+ buf = ipsec_set_policy(policy_in, strlen(policy_in));
+ if (buf != NULL) {
+ if (setsockopt(sep->se_fd, level, opt,
+ buf, ipsec_get_policylen(buf)) < 0 &&
+ debug != 0)
+ warnx("%s/%s: ipsec initialization failed; %s",
+ sep->se_service, sep->se_proto,
+ policy_in);
+ free(buf);
+ } else
+ syslog(LOG_ERR, "invalid security policy \"%s\"",
+ policy_in);
+ }
+ if (policy_out != NULL) {
+ buf = ipsec_set_policy(policy_out, strlen(policy_out));
+ if (buf != NULL) {
+ if (setsockopt(sep->se_fd, level, opt,
+ buf, ipsec_get_policylen(buf)) < 0 &&
+ debug != 0)
+ warnx("%s/%s: ipsec initialization failed; %s",
+ sep->se_service, sep->se_proto,
+ policy_out);
+ free(buf);
+ } else
+ syslog(LOG_ERR, "invalid security policy \"%s\"",
+ policy_out);
+ }
+}
+#endif
+
+/*
+ * Finish with a service and its socket.
+ */
+void
+close_sep(struct servtab *sep)
+{
+ if (sep->se_fd >= 0) {
+ if (FD_ISSET(sep->se_fd, &allsock))
+ disable(sep);
+ (void) close(sep->se_fd);
+ sep->se_fd = -1;
+ }
+ sep->se_count = 0;
+ sep->se_numchild = 0; /* forget about any existing children */
+}
+
+int
+matchservent(const char *name1, const char *name2, const char *proto)
+{
+ char **alias, *p;
+ struct servent *se;
+
+ if (strcmp(proto, "unix") == 0) {
+ if ((p = strrchr(name1, '/')) != NULL)
+ name1 = p + 1;
+ if ((p = strrchr(name2, '/')) != NULL)
+ name2 = p + 1;
+ }
+ if (strcmp(name1, name2) == 0)
+ return(1);
+ if ((se = getservbyname(name1, proto)) != NULL) {
+ if (strcmp(name2, se->s_name) == 0)
+ return(1);
+ for (alias = se->s_aliases; *alias; alias++)
+ if (strcmp(name2, *alias) == 0)
+ return(1);
+ }
+ return(0);
+}
+
+struct servtab *
+enter(struct servtab *cp)
+{
+ struct servtab *sep;
+ long omask;
+
+ sep = (struct servtab *)malloc(sizeof (*sep));
+ if (sep == (struct servtab *)0) {
+ syslog(LOG_ERR, "malloc: %m");
+ exit(EX_OSERR);
+ }
+ *sep = *cp;
+ sep->se_fd = -1;
+ omask = sigblock(SIGBLOCK);
+ sep->se_next = servtab;
+ servtab = sep;
+ sigsetmask(omask);
+ return (sep);
+}
+
+void
+enable(struct servtab *sep)
+{
+ if (debug)
+ warnx(
+ "enabling %s, fd %d", sep->se_service, sep->se_fd);
+#ifdef SANITY_CHECK
+ if (sep->se_fd < 0) {
+ syslog(LOG_ERR,
+ "%s: %s: bad fd", __FUNCTION__, sep->se_service);
+ exit(EX_SOFTWARE);
+ }
+ if (ISMUX(sep)) {
+ syslog(LOG_ERR,
+ "%s: %s: is mux", __FUNCTION__, sep->se_service);
+ exit(EX_SOFTWARE);
+ }
+ if (FD_ISSET(sep->se_fd, &allsock)) {
+ syslog(LOG_ERR,
+ "%s: %s: not off", __FUNCTION__, sep->se_service);
+ exit(EX_SOFTWARE);
+ }
+ nsock++;
+#endif
+ FD_SET(sep->se_fd, &allsock);
+ if (sep->se_fd > maxsock)
+ maxsock = sep->se_fd;
+}
+
+void
+disable(struct servtab *sep)
+{
+ if (debug)
+ warnx(
+ "disabling %s, fd %d", sep->se_service, sep->se_fd);
+#ifdef SANITY_CHECK
+ if (sep->se_fd < 0) {
+ syslog(LOG_ERR,
+ "%s: %s: bad fd", __FUNCTION__, sep->se_service);
+ exit(EX_SOFTWARE);
+ }
+ if (ISMUX(sep)) {
+ syslog(LOG_ERR,
+ "%s: %s: is mux", __FUNCTION__, sep->se_service);
+ exit(EX_SOFTWARE);
+ }
+ if (!FD_ISSET(sep->se_fd, &allsock)) {
+ syslog(LOG_ERR,
+ "%s: %s: not on", __FUNCTION__, sep->se_service);
+ exit(EX_SOFTWARE);
+ }
+ if (nsock == 0) {
+ syslog(LOG_ERR, "%s: nsock=0", __FUNCTION__);
+ exit(EX_SOFTWARE);
+ }
+ nsock--;
+#endif
+ FD_CLR(sep->se_fd, &allsock);
+ if (sep->se_fd == maxsock)
+ maxsock--;
+}
+
+FILE *fconfig = NULL;
+struct servtab serv;
+char line[LINE_MAX];
+
+int
+setconfig(void)
+{
+
+ if (fconfig != NULL) {
+ fseek(fconfig, 0L, SEEK_SET);
+ return (1);
+ }
+ fconfig = fopen(CONFIG, "r");
+ return (fconfig != NULL);
+}
+
+void
+endconfig(void)
+{
+ if (fconfig) {
+ (void) fclose(fconfig);
+ fconfig = NULL;
+ }
+}
+
+struct servtab *
+getconfigent(void)
+{
+ struct servtab *sep = &serv;
+ int argc;
+ char *cp, *arg, *s;
+ char *versp;
+ static char TCPMUX_TOKEN[] = "tcpmux/";
+#define MUX_LEN (sizeof(TCPMUX_TOKEN)-1)
+#ifdef IPSEC
+ char *policy = NULL;
+#endif
+ int v4bind = 0;
+#ifdef INET6
+ int v6bind = 0;
+#endif
+
+more:
+ while ((cp = nextline(fconfig)) != NULL) {
+#ifdef IPSEC
+ /* lines starting with #@ is not a comment, but the policy */
+ if (cp[0] == '#' && cp[1] == '@') {
+ char *p;
+ for (p = cp + 2; p && *p && isspace(*p); p++)
+ ;
+ if (*p == '\0') {
+ if (policy)
+ free(policy);
+ policy = NULL;
+ } else if (ipsec_get_policylen(p) >= 0) {
+ if (policy)
+ free(policy);
+ policy = newstr(p);
+ } else {
+ syslog(LOG_ERR,
+ "%s: invalid ipsec policy \"%s\"",
+ CONFIG, p);
+ exit(EX_CONFIG);
+ }
+ }
+#endif
+ if (*cp == '#' || *cp == '\0')
+ continue;
+ break;
+ }
+ if (cp == NULL)
+ return ((struct servtab *)0);
+ /*
+ * clear the static buffer, since some fields (se_ctrladdr,
+ * for example) don't get initialized here.
+ */
+ memset(sep, 0, sizeof *sep);
+ arg = skip(&cp);
+ if (cp == NULL) {
+ /* got an empty line containing just blanks/tabs. */
+ goto more;
+ }
+ if (arg[0] == ':') { /* :user:group:perm: */
+ char *user, *group, *perm;
+ struct passwd *pw;
+ struct group *gr;
+ user = arg+1;
+ if ((group = strchr(user, ':')) == NULL) {
+ syslog(LOG_ERR, "no group after user '%s'", user);
+ goto more;
+ }
+ *group++ = '\0';
+ if ((perm = strchr(group, ':')) == NULL) {
+ syslog(LOG_ERR, "no mode after group '%s'", group);
+ goto more;
+ }
+ *perm++ = '\0';
+ if ((pw = getpwnam(user)) == NULL) {
+ syslog(LOG_ERR, "no such user '%s'", user);
+ goto more;
+ }
+ sep->se_sockuid = pw->pw_uid;
+ if ((gr = getgrnam(group)) == NULL) {
+ syslog(LOG_ERR, "no such user '%s'", group);
+ goto more;
+ }
+ sep->se_sockgid = gr->gr_gid;
+ sep->se_sockmode = strtol(perm, &arg, 8);
+ if (*arg != ':') {
+ syslog(LOG_ERR, "bad mode '%s'", perm);
+ goto more;
+ }
+ *arg++ = '\0';
+ } else {
+ sep->se_sockuid = euid;
+ sep->se_sockgid = egid;
+ sep->se_sockmode = 0200;
+ }
+ if (strncmp(arg, TCPMUX_TOKEN, MUX_LEN) == 0) {
+ char *c = arg + MUX_LEN;
+ if (*c == '+') {
+ sep->se_type = MUXPLUS_TYPE;
+ c++;
+ } else
+ sep->se_type = MUX_TYPE;
+ sep->se_service = newstr(c);
+ } else {
+ sep->se_service = newstr(arg);
+ sep->se_type = NORM_TYPE;
+ }
+ arg = sskip(&cp);
+ if (strcmp(arg, "stream") == 0)
+ sep->se_socktype = SOCK_STREAM;
+ else if (strcmp(arg, "dgram") == 0)
+ sep->se_socktype = SOCK_DGRAM;
+ else if (strcmp(arg, "rdm") == 0)
+ sep->se_socktype = SOCK_RDM;
+ else if (strcmp(arg, "seqpacket") == 0)
+ sep->se_socktype = SOCK_SEQPACKET;
+ else if (strcmp(arg, "raw") == 0)
+ sep->se_socktype = SOCK_RAW;
+ else
+ sep->se_socktype = -1;
+
+ arg = sskip(&cp);
+ if (strncmp(arg, "tcp", 3) == 0) {
+ sep->se_proto = newstr(strsep(&arg, "/"));
+ if (arg != NULL) {
+ if (strcmp(arg, "ttcp") == 0)
+ sep->se_type = TTCP_TYPE;
+ else if (strcmp(arg, "faith") == 0)
+ sep->se_type = FAITH_TYPE;
+ }
+ } else {
+ if (sep->se_type == NORM_TYPE &&
+ strncmp(arg, "faith/", 6) == 0) {
+ arg += 6;
+ sep->se_type = FAITH_TYPE;
+ }
+ sep->se_proto = newstr(arg);
+ }
+ if (strncmp(sep->se_proto, "rpc/", 4) == 0) {
+ if (no_v4bind != 0) {
+ syslog(LOG_NOTICE, "IPv4 bind is ignored for %s",
+ sep->se_service);
+ freeconfig(sep);
+ goto more;
+ }
+ memmove(sep->se_proto, sep->se_proto + 4,
+ strlen(sep->se_proto) + 1 - 4);
+ sep->se_rpc = 1;
+ sep->se_rpc_prog = sep->se_rpc_lowvers =
+ sep->se_rpc_lowvers = 0;
+ memcpy(&sep->se_ctrladdr4, bind_sa4,
+ sizeof(sep->se_ctrladdr4));
+ if ((versp = rindex(sep->se_service, '/'))) {
+ *versp++ = '\0';
+ switch (sscanf(versp, "%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",
+ sep->se_service);
+ freeconfig(sep);
+ goto more;
+ }
+ }
+ else {
+ sep->se_rpc_lowvers =
+ sep->se_rpc_highvers = 1;
+ }
+ }
+ sep->se_nomapped = 0;
+ while (isdigit(sep->se_proto[strlen(sep->se_proto) - 1])) {
+#ifdef INET6
+ if (sep->se_proto[strlen(sep->se_proto) - 1] == '6') {
+ if (no_v6bind != 0) {
+ syslog(LOG_NOTICE, "IPv6 bind is ignored for %s",
+ sep->se_service);
+ freeconfig(sep);
+ goto more;
+ }
+ sep->se_proto[strlen(sep->se_proto) - 1] = '\0';
+ v6bind = 1;
+ continue;
+ }
+#endif
+ if (sep->se_proto[strlen(sep->se_proto) - 1] == '4') {
+ sep->se_proto[strlen(sep->se_proto) - 1] = '\0';
+ v4bind = 1;
+ continue;
+ }
+ /* illegal version num */
+ syslog(LOG_ERR, "bad IP version for %s", sep->se_proto);
+ freeconfig(sep);
+ goto more;
+ }
+ if (strcmp(sep->se_proto, "unix") == 0) {
+ sep->se_family = AF_UNIX;
+ } else
+#ifdef INET6
+ if (v6bind != 0) {
+ sep->se_family = AF_INET6;
+ if (v4bind == 0 || no_v4bind != 0)
+ sep->se_nomapped = 1;
+ } else
+#endif
+ { /* default to v4 bind if not v6 bind */
+ if (no_v4bind != 0) {
+ syslog(LOG_NOTICE, "IPv4 bind is ignored for %s",
+ sep->se_service);
+ freeconfig(sep);
+ goto more;
+ }
+ sep->se_family = AF_INET;
+ }
+ /* init ctladdr */
+ switch(sep->se_family) {
+ case AF_INET:
+ memcpy(&sep->se_ctrladdr4, bind_sa4,
+ sizeof(sep->se_ctrladdr4));
+ sep->se_ctrladdr_size = sizeof(sep->se_ctrladdr4);
+ break;
+#ifdef INET6
+ case AF_INET6:
+ memcpy(&sep->se_ctrladdr6, bind_sa6,
+ sizeof(sep->se_ctrladdr6));
+ sep->se_ctrladdr_size = sizeof(sep->se_ctrladdr6);
+ break;
+#endif
+ case AF_UNIX:
+ if (strlen(sep->se_service) >= sizeof(sep->se_ctrladdr_un.sun_path)) {
+ syslog(LOG_ERR,
+ "domain socket pathname too long for service %s",
+ sep->se_service);
+ goto more;
+ }
+ memset(&sep->se_ctrladdr, 0, sizeof(sep->se_ctrladdr));
+ sep->se_ctrladdr_un.sun_family = sep->se_family;
+ sep->se_ctrladdr_un.sun_len = strlen(sep->se_service);
+ strcpy(sep->se_ctrladdr_un.sun_path, sep->se_service);
+ sep->se_ctrladdr_size = SUN_LEN(&sep->se_ctrladdr_un);
+ }
+ arg = sskip(&cp);
+ if (!strncmp(arg, "wait", 4))
+ sep->se_accept = 0;
+ else if (!strncmp(arg, "nowait", 6))
+ sep->se_accept = 1;
+ else {
+ syslog(LOG_ERR,
+ "%s: bad wait/nowait for service %s",
+ CONFIG, sep->se_service);
+ goto more;
+ }
+ sep->se_maxchild = -1;
+ sep->se_maxcpm = -1;
+ if ((s = strchr(arg, '/')) != NULL) {
+ char *eptr;
+ u_long val;
+
+ val = strtoul(s + 1, &eptr, 10);
+ if (eptr == s + 1 || val > MAX_MAXCHLD) {
+ syslog(LOG_ERR,
+ "%s: bad max-child for service %s",
+ CONFIG, sep->se_service);
+ goto more;
+ }
+ if (debug)
+ if (!sep->se_accept && val != 1)
+ warnx("maxchild=%lu for wait service %s"
+ " not recommended", val, sep->se_service);
+ sep->se_maxchild = val;
+ if (*eptr == '/')
+ sep->se_maxcpm = strtol(eptr + 1, &eptr, 10);
+ /*
+ * explicitly do not check for \0 for future expansion /
+ * backwards compatibility
+ */
+ }
+ if (ISMUX(sep)) {
+ /*
+ * Silently enforce "nowait" mode for TCPMUX services
+ * since they don't have an assigned port to listen on.
+ */
+ sep->se_accept = 1;
+ if (strcmp(sep->se_proto, "tcp")) {
+ syslog(LOG_ERR,
+ "%s: bad protocol for tcpmux service %s",
+ CONFIG, sep->se_service);
+ goto more;
+ }
+ if (sep->se_socktype != SOCK_STREAM) {
+ syslog(LOG_ERR,
+ "%s: bad socket type for tcpmux service %s",
+ CONFIG, sep->se_service);
+ goto more;
+ }
+ }
+ sep->se_user = newstr(sskip(&cp));
+#ifdef LOGIN_CAP
+ if ((s = strrchr(sep->se_user, '/')) != NULL) {
+ *s = '\0';
+ sep->se_class = newstr(s + 1);
+ } else
+ sep->se_class = newstr(RESOURCE_RC);
+#endif
+ if ((s = strrchr(sep->se_user, ':')) != NULL) {
+ *s = '\0';
+ sep->se_group = newstr(s + 1);
+ } else
+ sep->se_group = NULL;
+ sep->se_server = newstr(sskip(&cp));
+ if ((sep->se_server_name = rindex(sep->se_server, '/')))
+ sep->se_server_name++;
+ if (strcmp(sep->se_server, "internal") == 0) {
+ struct biltin *bi;
+
+ for (bi = biltins; bi->bi_service; bi++)
+ if (bi->bi_socktype == sep->se_socktype &&
+ matchservent(bi->bi_service, sep->se_service,
+ sep->se_proto))
+ break;
+ if (bi->bi_service == 0) {
+ syslog(LOG_ERR, "internal service %s unknown",
+ sep->se_service);
+ goto more;
+ }
+ sep->se_accept = 1; /* force accept mode for built-ins */
+ sep->se_bi = bi;
+ } else
+ sep->se_bi = NULL;
+ if (sep->se_maxcpm < 0)
+ sep->se_maxcpm = maxcpm;
+ if (sep->se_maxchild < 0) { /* apply default max-children */
+ if (sep->se_bi && sep->se_bi->bi_maxchild >= 0)
+ sep->se_maxchild = sep->se_bi->bi_maxchild;
+ else if (sep->se_accept)
+ sep->se_maxchild = maxchild > 0 ? maxchild : 0;
+ else
+ sep->se_maxchild = 1;
+ }
+ if (sep->se_maxchild > 0) {
+ sep->se_pids = malloc(sep->se_maxchild * sizeof(*sep->se_pids));
+ if (sep->se_pids == NULL) {
+ syslog(LOG_ERR, "malloc: %m");
+ exit(EX_OSERR);
+ }
+ }
+ argc = 0;
+ for (arg = skip(&cp); cp; arg = skip(&cp))
+ if (argc < MAXARGV) {
+ sep->se_argv[argc++] = newstr(arg);
+ } else {
+ syslog(LOG_ERR,
+ "%s: too many arguments for service %s",
+ CONFIG, sep->se_service);
+ goto more;
+ }
+ while (argc <= MAXARGV)
+ sep->se_argv[argc++] = NULL;
+#ifdef IPSEC
+ sep->se_policy = policy ? newstr(policy) : NULL;
+#endif
+ return (sep);
+}
+
+void
+freeconfig(struct servtab *cp)
+{
+ int i;
+
+ if (cp->se_service)
+ free(cp->se_service);
+ if (cp->se_proto)
+ free(cp->se_proto);
+ if (cp->se_user)
+ free(cp->se_user);
+ if (cp->se_group)
+ free(cp->se_group);
+#ifdef LOGIN_CAP
+ if (cp->se_class)
+ free(cp->se_class);
+#endif
+ if (cp->se_server)
+ free(cp->se_server);
+ if (cp->se_pids)
+ free(cp->se_pids);
+ for (i = 0; i < MAXARGV; i++)
+ if (cp->se_argv[i])
+ free(cp->se_argv[i]);
+#ifdef IPSEC
+ if (cp->se_policy)
+ free(cp->se_policy);
+#endif
+}
+
+
+/*
+ * Safe skip - if skip returns null, log a syntax error in the
+ * configuration file and exit.
+ */
+char *
+sskip(char **cpp)
+{
+ char *cp;
+
+ cp = skip(cpp);
+ if (cp == NULL) {
+ syslog(LOG_ERR, "%s: syntax error", CONFIG);
+ exit(EX_DATAERR);
+ }
+ return (cp);
+}
+
+char *
+skip(char **cpp)
+{
+ char *cp = *cpp;
+ char *start;
+ char quote = '\0';
+
+again:
+ while (*cp == ' ' || *cp == '\t')
+ cp++;
+ if (*cp == '\0') {
+ int c;
+
+ c = getc(fconfig);
+ (void) ungetc(c, fconfig);
+ if (c == ' ' || c == '\t')
+ if ((cp = nextline(fconfig)))
+ goto again;
+ *cpp = (char *)0;
+ return ((char *)0);
+ }
+ if (*cp == '"' || *cp == '\'')
+ quote = *cp++;
+ start = cp;
+ if (quote)
+ while (*cp && *cp != quote)
+ cp++;
+ else
+ while (*cp && *cp != ' ' && *cp != '\t')
+ cp++;
+ if (*cp != '\0')
+ *cp++ = '\0';
+ *cpp = cp;
+ return (start);
+}
+
+char *
+nextline(FILE *fd)
+{
+ char *cp;
+
+ if (fgets(line, sizeof (line), fd) == NULL)
+ return ((char *)0);
+ cp = strchr(line, '\n');
+ if (cp)
+ *cp = '\0';
+ return (line);
+}
+
+char *
+newstr(const char *cp)
+{
+ char *cr;
+
+ if ((cr = strdup(cp != NULL ? cp : "")))
+ return (cr);
+ syslog(LOG_ERR, "strdup: %m");
+ exit(EX_OSERR);
+}
+
+void
+inetd_setproctitle(const char *a, int s)
+{
+ socklen_t size;
+ struct sockaddr_storage ss;
+ char buf[80], pbuf[INET6_ADDRSTRLEN];
+
+ size = sizeof(ss);
+ if (getpeername(s, (struct sockaddr *)&ss, &size) == 0) {
+ getnameinfo((struct sockaddr *)&ss, size, pbuf, sizeof(pbuf),
+ NULL, 0, NI_NUMERICHOST|NI_WITHSCOPEID);
+ (void) sprintf(buf, "%s [%s]", a, pbuf);
+ } else
+ (void) sprintf(buf, "%s", a);
+ setproctitle("%s", buf);
+}
+
+int
+check_loop(const struct sockaddr *sa, const struct servtab *sep)
+{
+ struct servtab *se2;
+ char pname[INET6_ADDRSTRLEN];
+
+ for (se2 = servtab; se2; se2 = se2->se_next) {
+ if (!se2->se_bi || se2->se_socktype != SOCK_DGRAM)
+ continue;
+
+ switch (se2->se_family) {
+ case AF_INET:
+ if (((const struct sockaddr_in *)sa)->sin_port ==
+ se2->se_ctrladdr4.sin_port)
+ goto isloop;
+ continue;
+#ifdef INET6
+ case AF_INET6:
+ if (((const struct sockaddr_in *)sa)->sin_port ==
+ se2->se_ctrladdr4.sin_port)
+ goto isloop;
+ continue;
+#endif
+ default:
+ continue;
+ }
+ isloop:
+ getnameinfo(sa, sa->sa_len, pname, sizeof(pname), NULL, 0,
+ NI_NUMERICHOST|NI_WITHSCOPEID);
+ syslog(LOG_WARNING, "%s/%s:%s/%s loop request REFUSED from %s",
+ sep->se_service, sep->se_proto,
+ se2->se_service, se2->se_proto,
+ pname);
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * print_service:
+ * Dump relevant information to stderr
+ */
+void
+print_service(const char *action, const struct servtab *sep)
+{
+ fprintf(stderr,
+ "%s: %s proto=%s accept=%d max=%d user=%s group=%s"
+#ifdef LOGIN_CAP
+ "class=%s"
+#endif
+ " builtin=%p server=%s"
+#ifdef IPSEC
+ " policy=\"%s\""
+#endif
+ "\n",
+ action, sep->se_service, sep->se_proto,
+ sep->se_accept, sep->se_maxchild, sep->se_user, sep->se_group,
+#ifdef LOGIN_CAP
+ sep->se_class,
+#endif
+ (void *) sep->se_bi, sep->se_server
+#ifdef IPSEC
+ , (sep->se_policy ? sep->se_policy : "")
+#endif
+ );
+}
+
+#define CPMHSIZE 256
+#define CPMHMASK (CPMHSIZE-1)
+#define CHTGRAN 10
+#define CHTSIZE 6
+
+typedef struct CTime {
+ unsigned long ct_Ticks;
+ int ct_Count;
+} CTime;
+
+typedef struct CHash {
+ union {
+ struct in_addr c4_Addr;
+ struct in6_addr c6_Addr;
+ } cu_Addr;
+#define ch_Addr4 cu_Addr.c4_Addr
+#define ch_Addr6 cu_Addr.c6_Addr
+ int ch_Family;
+ time_t ch_LTime;
+ char *ch_Service;
+ CTime ch_Times[CHTSIZE];
+} CHash;
+
+CHash CHashAry[CPMHSIZE];
+
+int
+cpmip(const struct servtab *sep, int ctrl)
+{
+ struct sockaddr_storage rss;
+ socklen_t rssLen = sizeof(rss);
+ int r = 0;
+
+ /*
+ * If getpeername() fails, just let it through (if logging is
+ * enabled the condition is caught elsewhere)
+ */
+
+ if (sep->se_maxcpm > 0 &&
+ getpeername(ctrl, (struct sockaddr *)&rss, &rssLen) == 0 ) {
+ time_t t = time(NULL);
+ int hv = 0xABC3D20F;
+ int i;
+ int cnt = 0;
+ CHash *chBest = NULL;
+ unsigned int ticks = t / CHTGRAN;
+ struct sockaddr_in *sin4;
+#ifdef INET6
+ struct sockaddr_in6 *sin6;
+#endif
+
+ sin4 = (struct sockaddr_in *)&rss;
+#ifdef INET6
+ sin6 = (struct sockaddr_in6 *)&rss;
+#endif
+ {
+ char *p;
+ int addrlen;
+
+ switch (rss.ss_family) {
+ case AF_INET:
+ p = (char *)&sin4->sin_addr;
+ addrlen = sizeof(struct in_addr);
+ break;
+#ifdef INET6
+ case AF_INET6:
+ p = (char *)&sin6->sin6_addr;
+ addrlen = sizeof(struct in6_addr);
+ break;
+#endif
+ default:
+ /* should not happen */
+ return -1;
+ }
+
+ for (i = 0; i < addrlen; ++i, ++p) {
+ hv = (hv << 5) ^ (hv >> 23) ^ *p;
+ }
+ hv = (hv ^ (hv >> 16));
+ }
+ for (i = 0; i < 5; ++i) {
+ CHash *ch = &CHashAry[(hv + i) & CPMHMASK];
+
+ if (rss.ss_family == AF_INET &&
+ ch->ch_Family == AF_INET &&
+ sin4->sin_addr.s_addr == ch->ch_Addr4.s_addr &&
+ ch->ch_Service && strcmp(sep->se_service,
+ ch->ch_Service) == 0) {
+ chBest = ch;
+ break;
+ }
+#ifdef INET6
+ if (rss.ss_family == AF_INET6 &&
+ ch->ch_Family == AF_INET6 &&
+ IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr,
+ &ch->ch_Addr6) != 0 &&
+ ch->ch_Service && strcmp(sep->se_service,
+ ch->ch_Service) == 0) {
+ chBest = ch;
+ break;
+ }
+#endif
+ if (chBest == NULL || ch->ch_LTime == 0 ||
+ ch->ch_LTime < chBest->ch_LTime) {
+ chBest = ch;
+ }
+ }
+ if ((rss.ss_family == AF_INET &&
+ (chBest->ch_Family != AF_INET ||
+ sin4->sin_addr.s_addr != chBest->ch_Addr4.s_addr)) ||
+ chBest->ch_Service == NULL ||
+ strcmp(sep->se_service, chBest->ch_Service) != 0) {
+ chBest->ch_Family = sin4->sin_family;
+ chBest->ch_Addr4 = sin4->sin_addr;
+ if (chBest->ch_Service)
+ free(chBest->ch_Service);
+ chBest->ch_Service = strdup(sep->se_service);
+ bzero(chBest->ch_Times, sizeof(chBest->ch_Times));
+ }
+#ifdef INET6
+ if ((rss.ss_family == AF_INET6 &&
+ (chBest->ch_Family != AF_INET6 ||
+ IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr,
+ &chBest->ch_Addr6) == 0)) ||
+ chBest->ch_Service == NULL ||
+ strcmp(sep->se_service, chBest->ch_Service) != 0) {
+ chBest->ch_Family = sin6->sin6_family;
+ chBest->ch_Addr6 = sin6->sin6_addr;
+ if (chBest->ch_Service)
+ free(chBest->ch_Service);
+ chBest->ch_Service = strdup(sep->se_service);
+ bzero(chBest->ch_Times, sizeof(chBest->ch_Times));
+ }
+#endif
+ chBest->ch_LTime = t;
+ {
+ CTime *ct = &chBest->ch_Times[ticks % CHTSIZE];
+ if (ct->ct_Ticks != ticks) {
+ ct->ct_Ticks = ticks;
+ ct->ct_Count = 0;
+ }
+ ++ct->ct_Count;
+ }
+ for (i = 0; i < CHTSIZE; ++i) {
+ CTime *ct = &chBest->ch_Times[i];
+ if (ct->ct_Ticks <= ticks &&
+ ct->ct_Ticks >= ticks - CHTSIZE) {
+ cnt += ct->ct_Count;
+ }
+ }
+ if (cnt * (CHTSIZE * CHTGRAN) / 60 > sep->se_maxcpm) {
+ char pname[INET6_ADDRSTRLEN];
+
+ getnameinfo((struct sockaddr *)&rss,
+ ((struct sockaddr *)&rss)->sa_len,
+ pname, sizeof(pname), NULL, 0,
+ NI_NUMERICHOST|NI_WITHSCOPEID);
+ r = -1;
+ syslog(LOG_ERR,
+ "%s from %s exceeded counts/min (limit %d/min)",
+ sep->se_service, pname,
+ sep->se_maxcpm);
+ }
+ }
+ return(r);
+}
diff --git a/usr.sbin/inetd/inetd.h b/usr.sbin/inetd/inetd.h
new file mode 100644
index 0000000..1e0f088
--- /dev/null
+++ b/usr.sbin/inetd/inetd.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 1983, 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <netinet/in.h>
+
+#include <stdio.h>
+
+#define BUFSIZE 8192
+#define LINESIZ 72
+
+#define NORM_TYPE 0
+#define MUX_TYPE 1
+#define MUXPLUS_TYPE 2
+#define TTCP_TYPE 3
+#define FAITH_TYPE 4
+#define ISMUX(sep) (((sep)->se_type == MUX_TYPE) || \
+ ((sep)->se_type == MUXPLUS_TYPE))
+#define ISMUXPLUS(sep) ((sep)->se_type == MUXPLUS_TYPE)
+#define ISTTCP(sep) ((sep)->se_type == TTCP_TYPE)
+
+struct servtab {
+ char *se_service; /* name of service */
+ int se_socktype; /* type of socket to use */
+ int se_family; /* address family */
+ char *se_proto; /* protocol used */
+ int se_maxchild; /* max number of children */
+ int se_maxcpm; /* max connects per IP per minute */
+ int se_numchild; /* current number of children */
+ pid_t *se_pids; /* array of child pids */
+ char *se_user; /* user name to run as */
+ char *se_group; /* group name to run as */
+#ifdef LOGIN_CAP
+ char *se_class; /* login class name to run with */
+#endif
+ struct biltin *se_bi; /* if built-in, description */
+ char *se_server; /* server program */
+ char *se_server_name; /* server program without path */
+#define MAXARGV 20
+ char *se_argv[MAXARGV+1]; /* program arguments */
+#ifdef IPSEC
+ char *se_policy; /* IPsec policy string */
+#endif
+ int se_fd; /* open descriptor */
+ union { /* bound address */
+ struct sockaddr se_un_ctrladdr;
+ struct sockaddr_in se_un_ctrladdr4;
+ struct sockaddr_in6 se_un_ctrladdr6;
+ struct sockaddr_un se_un_ctrladdr_un;
+ } se_un;
+#define se_ctrladdr se_un.se_un_ctrladdr
+#define se_ctrladdr4 se_un.se_un_ctrladdr4
+#define se_ctrladdr6 se_un.se_un_ctrladdr6
+#define se_ctrladdr_un se_un.se_un_ctrladdr_un
+ socklen_t se_ctrladdr_size;
+ uid_t se_sockuid; /* Owner for unix domain socket */
+ gid_t se_sockgid; /* Group for unix domain socket */
+ mode_t se_sockmode; /* Mode for unix domain socket */
+ u_char se_type; /* type: normal, mux, or mux+ */
+ u_char se_checked; /* looked at during merge */
+ u_char se_accept; /* i.e., wait/nowait mode */
+ u_char se_rpc; /* ==1 if RPC service */
+ int se_rpc_prog; /* RPC program number */
+ u_int se_rpc_lowvers; /* RPC low version */
+ u_int se_rpc_highvers; /* RPC high version */
+ int se_count; /* number started since se_time */
+ struct timeval se_time; /* start of se_count */
+ struct servtab *se_next;
+ struct se_flags {
+ u_int se_nomapped : 1;
+ u_int se_reset : 1;
+ } se_flags;
+};
+
+#define se_nomapped se_flags.se_nomapped
+#define se_reset se_flags.se_reset
+
+int check_loop(const struct sockaddr *, const struct servtab *sep);
+int getvalue(const char *, int *, const char *);
+char *newstr(const char *);
+void inetd_setproctitle(const char *, int);
+void print_service(const char *, const struct servtab *);
+char *sskip(char **);
+char *skip(char **);
+struct servtab *tcpmux(int);
+
+extern int debug;
+extern struct servtab *servtab;
+
+typedef void (bi_fn_t)(int, struct servtab *);
+
+struct biltin {
+ const char *bi_service; /* internally provided service name */
+ int bi_socktype; /* type of socket supported */
+ short bi_fork; /* 1 if should fork before call */
+ int bi_maxchild; /* max number of children, -1=default */
+ bi_fn_t *bi_fn; /* function which performs it */
+};
diff --git a/usr.sbin/inetd/pathnames.h b/usr.sbin/inetd/pathnames.h
new file mode 100644
index 0000000..fc4ff5a
--- /dev/null
+++ b/usr.sbin/inetd/pathnames.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/6/93
+ * $FreeBSD$
+ */
+
+#include <paths.h>
+
+#define _PATH_INETDCONF "/etc/inetd.conf"
+#define _PATH_INETDPID _PATH_VARRUN "inetd.pid"
diff --git a/usr.sbin/iostat/Makefile b/usr.sbin/iostat/Makefile
new file mode 100644
index 0000000..002a250
--- /dev/null
+++ b/usr.sbin/iostat/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+MAINTAINER= ken@FreeBSD.ORG
+
+PROG= iostat
+DPADD= ${LIBKVM} ${LIBDEVSTAT} ${LIBM}
+LDADD= -lkvm -ldevstat -lm
+MAN= iostat.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/iostat/iostat.8 b/usr.sbin/iostat/iostat.8
new file mode 100644
index 0000000..c93dcb4
--- /dev/null
+++ b/usr.sbin/iostat/iostat.8
@@ -0,0 +1,427 @@
+.\"
+.\" Copyright (c) 1997 Kenneth D. Merry.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.\" Copyright (c) 1985, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)iostat.8 8.1 (Berkeley) 6/6/93
+.\"
+.Dd December 22, 1997
+.Dt IOSTAT 8
+.Os
+.Sh NAME
+.Nm iostat
+.Nd report
+.Tn I/O
+statistics
+.Sh SYNOPSIS
+.Nm
+.Op Fl CdhKIoT?\&
+.Op Fl c Ar count
+.Op Fl M Ar core
+.Op Fl n Ar devs
+.Op Fl N Ar system
+.Oo
+.Fl t
+.Sm off
+.Ar type , if , pass
+.Sm on
+.Oc
+.Op Fl w Ar wait
+.Op Ar drives
+.Sh DESCRIPTION
+.Nm Iostat
+displays kernel
+.Tn I/O
+statistics on terminal, device and cpu operations.
+The first statistics that are printed are averaged over the system uptime.
+To get information about the current activity, a suitable wait time should
+be specified, so that the subsequent sets of printed statistics will be
+averaged over that time.
+.Pp
+The options are as follows:
+.Bl -tag -width flag
+.It Fl c
+Repeat the display
+.Ar count
+times.
+If no
+.Ar wait
+interval is specified, the default is 1 second.
+.It Fl C
+Display CPU statistics.
+This is on by default, unless
+.Fl d
+is specified.
+.It Fl d
+Display only device statistics.
+If this flag is turned on, only device statistics will be displayed, unless
+.Fl C
+or
+.Fl T
+is also specfied to enable the display of CPU or TTY statistics.
+.It Fl h
+Put
+.Nm
+in
+.Sq top
+mode.
+In this mode,
+.Nm
+will show devices in order from highest to lowest bytes
+per measurement cycle.
+.It Fl I
+Display total 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
+will display fewer devices if there aren't
+.Ar devs
+devices present.
+.It Fl N
+Extract the name list from the specified system instead of the default
+.Dq Pa /boot/kernel/kernel .
+.It Fl o
+Display old-style
+.Nm
+device statistics.
+Sectors per second, transfers per second, and 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:
+.Pp
+.Bl -tag -width indent -compact
+.It device type:
+.Bl -tag -width 9n -compact
+.It da
+Direct Access devices
+.It sa
+Sequential Access devices
+.It printer
+Printers
+.It proc
+Processor devices
+.It worm
+Write Once Read Multiple devices
+.It cd
+CD devices
+.It scanner
+Scanner devices
+.It optical
+Optical Memory devices
+.It changer
+Medium Changer devices
+.It comm
+Communication devices
+.It array
+Storage Array devices
+.It enclosure
+Enclosure Services devices
+.It floppy
+Floppy devices
+.El
+.Pp
+.It interface:
+.Bl -tag -width 9n -compact
+.It IDE
+Integrated Drive Electronics devices
+.It SCSI
+Small Computer System Interface devices
+.It other
+Any other device interface
+.El
+.Pp
+.It passthrough:
+.Bl -tag -width 9n -compact
+.It pass
+Passthrough devices
+.El
+.El
+.Pp
+The user must specify at least one device type, and may specify at most
+one device type from each category.
+Multiple device types in a single device type statement must be separated by
+commas.
+.Pp
+Any number of
+.Fl t
+arguments may be specified on the command line.
+All
+.Fl t
+arguments are ORed together to form a matching expression against which
+all devices in the system are compared.
+Any device that fully matches any
+.Fl t
+argument will be included in the
+.Nm
+output, up to the number of devices that can be displayed in
+80 columns, or the maximum number of devices specified by the user.
+.It Fl T
+Display TTY statistics.
+This is on by default, unless
+.Fl d
+is specified.
+.It Fl w
+Pause
+.Ar wait
+seconds between each display.
+If no repeat
+.Ar count
+is specified, the default is infinity.
+.It Fl ?\&
+Display a usage statement and exit.
+.El
+.Pp
+.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
+will display as many devices as will fit in a standard 80 column screen, or
+the maximum number of devices in the system, whichever is smaller.
+If
+.Fl n
+is specified on the command line,
+.Nm
+will display the smaller of the
+requested number of devices, and the maximum number of devices in the system.
+To force
+.Nm
+to display specific drives, their names may be supplied on the command
+line.
+.Nm
+will not display more devices than will fit in an 80 column screen, unless
+the
+.Fl n
+argument is given on the command line to specify a maximum number of
+devices to display.
+If fewer devices are specified on the command line than will fit in an 80
+column screen,
+.Nm
+will show only the specified devices.
+.Pp
+The standard
+.Nm
+device display shows the following statistics:
+.Pp
+.Bl -tag -width indent -compact
+.It KB/t
+kilobytes per transfer
+.It tps
+transfers per second
+.It MB/s
+megabytes per second
+.El
+.Pp
+The standard
+.Nm
+device display, with the
+.Fl I
+flag specified, shows the following statistics:
+.Pp
+.Bl -tag -width indent -compact
+.It KB/t
+kilobytes per transfer
+.It xfrs
+total number of transfers
+.It MB
+total number of megabytes transferred
+.El
+.Pp
+The old-style
+.Nm
+display (using
+.Fl o )
+shows the following statistics:
+.Pp
+.Bl -tag -width indent -compact
+.It sps
+sectors transferred per second
+.It tps
+transfers per second
+.It msps
+average milliseconds per transaction
+.El
+.Pp
+The old-style
+.Nm
+display, with the
+.Fl I
+flag specified, shows the following statistics:
+.Pp
+.Bl -tag -width indent -compact
+.It blk
+total blocks/sectors transferred
+.It xfr
+total transfers
+.It msps
+average milliseconds per transaction
+.El
+.It cpu
+.Bl -tag -width indent -compact
+.It \&us
+% of cpu time in user mode
+.It \&ni
+% of cpu time in user mode running niced processes
+.It \&sy
+% of cpu time in system mode
+.It \&in
+% of cpu time in interrupt mode
+.It \&id
+% of cpu time in idle mode
+.El
+.El
+.Sh FILES
+.Bl -tag -width /boot/kernel/kernel -compact
+.It Pa /boot/kernel/kernel
+Default kernel namelist.
+.It Pa /dev/kmem
+Default memory file.
+.El
+.Sh EXAMPLES
+.Dl iostat -w 1 da0 da1 cd0
+.Pp
+Display statistics for the first two Direct Access devices and the first
+CDROM device every second ad infinitum.
+.Pp
+.Dl iostat -c 2
+.Pp
+Display the statistics for the first four devices in the system twice, with
+a one second display interval.
+.Pp
+.Dl iostat -t da -t cd -w 1
+.Pp
+Display statistics for all CDROM and Direct Access devices every second
+ad infinitum.
+.Pp
+.Dl iostat -t da,scsi,pass -t cd,scsi,pass
+.Pp
+Display statistics once for all SCSI passthrough devices that provide access
+to either Direct Access or CDROM devices.
+.Pp
+.Dl iostat -h -n 8 -w 1
+.Pp
+Display up to 8 devices with the most I/O every second ad infinitum.
+.Pp
+.Dl iostat -dh -t da -w 1
+.Pp
+Omit the TTY and CPU displays, show devices in order of performance and
+show only Direct Access devices every second ad infinitum.
+.Pp
+.Dl iostat -Iw 3
+.Pp
+Display total statistics every three seconds ad infinitum.
+.Pp
+.Dl iostat -odICTw 2 -c 9
+.Pp
+Display total statistics using the old-style output format 9 times, with
+a two second interval between each measurement/display.
+The
+.Fl d
+flag generally disables the TTY and CPU displays, but since the
+.Fl T
+and
+.Fl C
+flags are given, the TTY and CPU displays will be displayed.
+.Sh SEE ALSO
+.Xr fstat 1 ,
+.Xr netstat 1 ,
+.Xr nfsstat 1 ,
+.Xr ps 1 ,
+.Xr systat 1 ,
+.Xr pstat 8 ,
+.Xr vmstat 8
+.Pp
+The sections starting with ``Interpreting system activity'' in
+.%T "Installing and Operating 4.3BSD" .
+.Sh HISTORY
+This version of
+.Nm
+first appeared in
+.Fx 3.0 .
+.Sh BUGS
+The use of
+.Nm
+as a debugging tool for crash dumps is probably limited because there is
+currently no way to get statistics that only cover the time immediately before
+the crash.
+.Sh AUTHORS
+.An Kenneth Merry Aq ken@FreeBSD.org
diff --git a/usr.sbin/iostat/iostat.c b/usr.sbin/iostat/iostat.c
new file mode 100644
index 0000000..d11909a
--- /dev/null
+++ b/usr.sbin/iostat/iostat.c
@@ -0,0 +1,764 @@
+/*
+ * Copyright (c) 1997, 1998, 2000, 2001 Kenneth D. Merry
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Parts of this program are derived from the original FreeBSD iostat
+ * program:
+ */
+/*-
+ * Copyright (c) 1986, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * Ideas for the new iostat statistics output modes taken from the NetBSD
+ * version of iostat:
+ */
+/*
+ * Copyright (c) 1996 John M. Vinopal
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the NetBSD Project
+ * by John M. Vinopal.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/dkstat.h>
+#include <sys/sysctl.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>
+#include <math.h>
+
+struct nlist namelist[] = {
+#define X_TK_NIN 0
+ { "_tk_nin" },
+#define X_TK_NOUT 1
+ { "_tk_nout" },
+#define X_CP_TIME 2
+ { "_cp_time" },
+#define X_BOOTTIME 3
+ { "_boottime" },
+#define X_END 3
+ { NULL },
+};
+
+struct statinfo cur, last;
+int num_devices;
+struct device_selection *dev_select;
+int maxshowdevs;
+volatile sig_atomic_t headercount;
+int dflag = 0, Iflag = 0, Cflag = 0, Tflag = 0, oflag = 0, Kflag = 0;
+
+/* local function declarations */
+static void usage(void);
+static void needhdr(int signo);
+static void phdr(void);
+static void devstats(int perf_select, long double etime, int havelast);
+static void cpustats(void);
+static int readvar(kvm_t *kd, const char *name, int nlid, void *ptr,
+ size_t len);
+
+static void
+usage(void)
+{
+ /*
+ * We also support the following 'traditional' syntax:
+ * iostat [drives] [wait [count]]
+ * This isn't mentioned in the man page, or the usage statement,
+ * but it is supported.
+ */
+ fprintf(stderr, "usage: iostat [-CdhIKoT?] [-c count] [-M core]"
+ " [-n devs] [-N system]\n"
+ "\t [-t type,if,pass] [-w wait] [drives]\n");
+}
+
+int
+main(int argc, char **argv)
+{
+ int c;
+ register int i;
+ int tflag = 0, hflag = 0, cflag = 0, wflag = 0, nflag = 0;
+ int count = 0, waittime = 0;
+ char *memf = NULL, *nlistf = NULL;
+ struct devstat_match *matches;
+ int num_matches = 0;
+ char errbuf[_POSIX2_LINE_MAX];
+ kvm_t *kd = NULL;
+ long generation;
+ int num_devices_specified;
+ int num_selected, num_selections;
+ long select_generation;
+ char **specified_devices;
+ devstat_select_mode select_mode;
+ int havelast = 0;
+
+ matches = NULL;
+ maxshowdevs = 3;
+
+ while ((c = getopt(argc, argv, "c:CdhIKM:n:N:ot:Tw:?")) != -1) {
+ switch(c) {
+ case 'c':
+ cflag++;
+ count = atoi(optarg);
+ if (count < 1)
+ errx(1, "count %d is < 1", count);
+ break;
+ case 'C':
+ Cflag++;
+ break;
+ case 'd':
+ dflag++;
+ break;
+ case 'h':
+ hflag++;
+ break;
+ case 'I':
+ Iflag++;
+ break;
+ case 'K':
+ Kflag++;
+ break;
+ case 'M':
+ memf = optarg;
+ break;
+ case 'n':
+ nflag++;
+ maxshowdevs = atoi(optarg);
+ if (maxshowdevs < 0)
+ errx(1, "number of devices %d is < 0",
+ maxshowdevs);
+ break;
+ case 'N':
+ nlistf = optarg;
+ break;
+ case 'o':
+ oflag++;
+ break;
+ case 't':
+ tflag++;
+ if (devstat_buildmatch(optarg, &matches,
+ &num_matches) != 0)
+ errx(1, "%s", devstat_errbuf);
+ break;
+ case 'T':
+ Tflag++;
+ break;
+ case 'w':
+ wflag++;
+ waittime = atoi(optarg);
+ if (waittime < 1)
+ errx(1, "wait time is < 1");
+ break;
+ default:
+ usage();
+ exit(1);
+ break;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (nlistf != NULL || memf != NULL) {
+ kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf);
+
+ if (kd == NULL)
+ errx(1, "kvm_openfiles: %s", errbuf);
+
+ if (kvm_nlist(kd, namelist) == -1)
+ errx(1, "kvm_nlist: %s", kvm_geterr(kd));
+ }
+
+ /*
+ * Make sure that the userland devstat version matches the kernel
+ * devstat version. If not, exit and print a message informing
+ * the user of his mistake.
+ */
+ if (devstat_checkversion(kd) < 0)
+ errx(1, "%s", devstat_errbuf);
+
+ /*
+ * Make sure Tflag and/or Cflag are set if dflag == 0. If dflag is
+ * greater than 0, they may be 0 or non-zero.
+ */
+ if (dflag == 0) {
+ Cflag = 1;
+ Tflag = 1;
+ }
+
+ /*
+ * Figure out how many devices we should display.
+ */
+ if (nflag == 0) {
+ if (oflag > 0) {
+ if ((dflag > 0) && (Cflag == 0) && (Tflag == 0))
+ maxshowdevs = 5;
+ else if ((dflag > 0) && (Tflag > 0) && (Cflag == 0))
+ maxshowdevs = 5;
+ else
+ maxshowdevs = 4;
+ } else {
+ if ((dflag > 0) && (Cflag == 0))
+ maxshowdevs = 4;
+ else
+ maxshowdevs = 3;
+ }
+ }
+
+ /* find out how many devices we have */
+ if ((num_devices = devstat_getnumdevs(kd)) < 0)
+ err(1, "can't get number of devices");
+
+ cur.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo));
+ if (cur.dinfo == NULL)
+ err(1, "malloc failed");
+
+ last.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo));
+ if (last.dinfo == NULL)
+ err(1, "malloc failed");
+
+ bzero(cur.dinfo, sizeof(struct devinfo));
+ bzero(last.dinfo, sizeof(struct devinfo));
+
+ /*
+ * Grab all the devices. We don't look to see if the list has
+ * changed here, since it almost certainly has. We only look for
+ * errors.
+ */
+ if (devstat_getdevs(kd, &cur) == -1)
+ errx(1, "%s", devstat_errbuf);
+
+ num_devices = cur.dinfo->numdevs;
+ generation = cur.dinfo->generation;
+
+ /*
+ * If the user specified any devices on the command line, see if
+ * they are in the list of devices we have now.
+ */
+ specified_devices = (char **)malloc(sizeof(char *));
+ if (specified_devices == NULL)
+ err(1, "malloc failed");
+
+ for (num_devices_specified = 0; *argv; ++argv) {
+ if (isdigit(**argv))
+ break;
+ num_devices_specified++;
+ specified_devices = (char **)realloc(specified_devices,
+ sizeof(char *) *
+ num_devices_specified);
+ if (specified_devices == NULL)
+ err(1, "realloc failed");
+
+ specified_devices[num_devices_specified - 1] = *argv;
+
+ }
+ if (nflag == 0 && maxshowdevs < num_devices_specified)
+ maxshowdevs = num_devices_specified;
+
+ dev_select = NULL;
+
+ if ((num_devices_specified == 0) && (num_matches == 0))
+ select_mode = DS_SELECT_ADD;
+ else
+ select_mode = DS_SELECT_ONLY;
+
+ /*
+ * At this point, selectdevs will almost surely indicate that the
+ * device list has changed, so we don't look for return values of 0
+ * or 1. If we get back -1, though, there is an error.
+ */
+ if (devstat_selectdevs(&dev_select, &num_selected,
+ &num_selections, &select_generation, generation,
+ cur.dinfo->devices, num_devices, matches,
+ num_matches, specified_devices,
+ num_devices_specified, select_mode, maxshowdevs,
+ hflag) == -1)
+ errx(1, "%s", devstat_errbuf);
+
+ /*
+ * Look for the traditional wait time and count arguments.
+ */
+ if (*argv) {
+ waittime = atoi(*argv);
+
+ /* Let the user know he goofed, but keep going anyway */
+ if (wflag != 0)
+ warnx("discarding previous wait interval, using"
+ " %d instead", waittime);
+ wflag++;
+
+ if (*++argv) {
+ count = atoi(*argv);
+ if (cflag != 0)
+ warnx("discarding previous count, using %d"
+ " instead", count);
+ cflag++;
+ } else
+ count = -1;
+ }
+
+ /*
+ * If the user specified a count, but not an interval, we default
+ * to an interval of 1 second.
+ */
+ if ((wflag == 0) && (cflag > 0))
+ waittime = 1;
+
+ /*
+ * If the user specified a wait time, but not a count, we want to
+ * go on ad infinitum. This can be redundant if the user uses the
+ * traditional method of specifying the wait, since in that case we
+ * already set count = -1 above. Oh well.
+ */
+ if ((wflag > 0) && (cflag == 0))
+ count = -1;
+
+ bzero(&cur.cp_time, sizeof(cur.cp_time));
+ cur.tk_nout = 0;
+ cur.tk_nin = 0;
+
+ /*
+ * Set the busy time to the system boot time, so the stats are
+ * calculated since system boot.
+ */
+ if (readvar(kd, "kern.boottime", X_BOOTTIME, &cur.busy_time,
+ sizeof(cur.busy_time)) != 0)
+ exit(1);
+
+ /*
+ * If the user stops the program (control-Z) and then resumes it,
+ * print out the header again.
+ */
+ (void)signal(SIGCONT, needhdr);
+
+ for (headercount = 1;;) {
+ struct devinfo *tmp_dinfo;
+ long tmp;
+ long double etime;
+
+ if (Tflag > 0) {
+ if ((readvar(kd, "kern.tty_nin", X_TK_NIN, &cur.tk_nin,
+ sizeof(cur.tk_nin)) != 0)
+ || (readvar(kd, "kern.tty_nout", X_TK_NOUT,
+ &cur.tk_nout, sizeof(cur.tk_nout))!= 0)) {
+ Tflag = 0;
+ warnx("disabling TTY statistics");
+ }
+ }
+
+ if (Cflag > 0) {
+ if (readvar(kd, "kern.cp_time", X_CP_TIME,
+ &cur.cp_time, sizeof(cur.cp_time)) != 0) {
+ Cflag = 0;
+ warnx("disabling CPU time statistics");
+ }
+ }
+
+ if (!--headercount) {
+ phdr();
+ headercount = 20;
+ }
+
+ tmp_dinfo = last.dinfo;
+ last.dinfo = cur.dinfo;
+ cur.dinfo = tmp_dinfo;
+
+ last.busy_time = cur.busy_time;
+
+ /*
+ * Here what we want to do is refresh our device stats.
+ * devstat_getdevs() returns 1 when the device list has changed.
+ * If the device list has changed, we want to go through
+ * the selection process again, in case a device that we
+ * were previously displaying has gone away.
+ */
+ switch (devstat_getdevs(kd, &cur)) {
+ case -1:
+ errx(1, "%s", devstat_errbuf);
+ break;
+ case 1: {
+ int retval;
+
+ num_devices = cur.dinfo->numdevs;
+ generation = cur.dinfo->generation;
+ retval = devstat_selectdevs(&dev_select, &num_selected,
+ &num_selections,
+ &select_generation,
+ generation,
+ cur.dinfo->devices,
+ num_devices, matches,
+ num_matches,
+ specified_devices,
+ num_devices_specified,
+ select_mode, maxshowdevs,
+ hflag);
+ switch(retval) {
+ case -1:
+ errx(1, "%s", devstat_errbuf);
+ break;
+ case 1:
+ phdr();
+ headercount = 20;
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ /*
+ * We only want to re-select devices if we're in 'top'
+ * mode. This is the only mode where the devices selected
+ * could actually change.
+ */
+ if (hflag > 0) {
+ int retval;
+ retval = devstat_selectdevs(&dev_select, &num_selected,
+ &num_selections,
+ &select_generation,
+ generation,
+ cur.dinfo->devices,
+ num_devices, matches,
+ num_matches,
+ specified_devices,
+ num_devices_specified,
+ select_mode, maxshowdevs,
+ hflag);
+ switch(retval) {
+ case -1:
+ errx(1,"%s", devstat_errbuf);
+ break;
+ case 1:
+ phdr();
+ headercount = 20;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (Tflag > 0) {
+ tmp = cur.tk_nin;
+ cur.tk_nin -= last.tk_nin;
+ last.tk_nin = tmp;
+ tmp = cur.tk_nout;
+ cur.tk_nout -= last.tk_nout;
+ last.tk_nout = tmp;
+ }
+
+ etime = devstat_compute_etime(cur.busy_time, last.busy_time);
+
+ if (etime == 0.0)
+ etime = 1.0;
+
+ for (i = 0; i < CPUSTATES; i++) {
+ tmp = cur.cp_time[i];
+ cur.cp_time[i] -= last.cp_time[i];
+ last.cp_time[i] = tmp;
+ }
+
+ if (Tflag > 0)
+ printf("%4.0Lf%5.0Lf", cur.tk_nin / etime,
+ cur.tk_nout/etime);
+
+ devstats(hflag, etime, havelast);
+
+ if (Cflag > 0)
+ cpustats();
+
+ printf("\n");
+ fflush(stdout);
+
+ if (count >= 0 && --count <= 0)
+ break;
+
+ sleep(waittime);
+ havelast = 1;
+ }
+
+ exit(0);
+}
+
+/*
+ * Force a header to be prepended to the next output.
+ */
+void
+needhdr(int signo)
+{
+
+ headercount = 1;
+}
+
+static void
+phdr(void)
+{
+ register int i;
+ int printed;
+
+ if (Tflag > 0)
+ (void)printf(" tty");
+ for (i = 0, printed=0;(i < num_devices) && (printed < maxshowdevs);i++){
+ int di;
+ if ((dev_select[i].selected != 0)
+ && (dev_select[i].selected <= maxshowdevs)) {
+ di = dev_select[i].position;
+ if (oflag > 0)
+ (void)printf("%12.6s%d ",
+ cur.dinfo->devices[di].device_name,
+ cur.dinfo->devices[di].unit_number);
+ else
+ printf("%15.6s%d ",
+ cur.dinfo->devices[di].device_name,
+ cur.dinfo->devices[di].unit_number);
+ printed++;
+ }
+ }
+ if (Cflag > 0)
+ (void)printf(" cpu\n");
+ else
+ (void)printf("\n");
+
+ if (Tflag > 0)
+ (void)printf(" tin tout");
+
+ for (i=0, printed = 0;(i < num_devices) && (printed < maxshowdevs);i++){
+ if ((dev_select[i].selected != 0)
+ && (dev_select[i].selected <= maxshowdevs)) {
+ if (oflag > 0) {
+ if (Iflag == 0)
+ (void)printf(" sps tps msps ");
+ else
+ (void)printf(" blk xfr msps ");
+ } else {
+ if (Iflag == 0)
+ printf(" KB/t tps MB/s ");
+ else
+ printf(" KB/t xfrs MB ");
+ }
+ printed++;
+ }
+ }
+ if (Cflag > 0)
+ (void)printf(" us ni sy in id\n");
+ else
+ printf("\n");
+
+}
+
+static void
+devstats(int perf_select, long double etime, int havelast)
+{
+ register int dn;
+ long double transfers_per_second;
+ long double kb_per_transfer, mb_per_second;
+ u_int64_t total_bytes, total_transfers, total_blocks;
+ long double total_mb;
+ long double blocks_per_second, ms_per_transaction;
+
+ for (dn = 0; dn < num_devices; dn++) {
+ int di;
+
+ if (((perf_select == 0) && (dev_select[dn].selected == 0))
+ || (dev_select[dn].selected > maxshowdevs))
+ continue;
+
+ di = dev_select[dn].position;
+
+ if (devstat_compute_statistics(&cur.dinfo->devices[di],
+ havelast ? &last.dinfo->devices[di] : NULL, etime,
+ DSM_TOTAL_BYTES, &total_bytes,
+ DSM_TOTAL_TRANSFERS, &total_transfers,
+ DSM_TOTAL_BLOCKS, &total_blocks,
+ DSM_KB_PER_TRANSFER, &kb_per_transfer,
+ DSM_TRANSFERS_PER_SECOND, &transfers_per_second,
+ DSM_MB_PER_SECOND, &mb_per_second,
+ DSM_BLOCKS_PER_SECOND, &blocks_per_second,
+ DSM_MS_PER_TRANSACTION, &ms_per_transaction,
+ DSM_NONE) != 0)
+ errx(1, "%s", devstat_errbuf);
+
+ if (perf_select != 0) {
+ dev_select[dn].bytes = total_bytes;
+ if ((dev_select[dn].selected == 0)
+ || (dev_select[dn].selected > maxshowdevs))
+ continue;
+ }
+
+ if (Kflag) {
+ int block_size = cur.dinfo->devices[di].block_size;
+ total_blocks = total_blocks * (block_size ?
+ block_size : 512) / 1024;
+ }
+
+ if (oflag > 0) {
+ int msdig = (ms_per_transaction < 100.0) ? 1 : 0;
+
+ if (Iflag == 0)
+ printf("%4.0Lf%4.0Lf%5.*Lf ",
+ blocks_per_second,
+ transfers_per_second,
+ msdig,
+ ms_per_transaction);
+ else
+ printf("%4.1qu%4.1qu%5.*Lf ",
+ total_blocks,
+ total_transfers,
+ msdig,
+ ms_per_transaction);
+ } else {
+ if (Iflag == 0)
+ printf(" %5.2Lf %3.0Lf %5.2Lf ",
+ kb_per_transfer,
+ transfers_per_second,
+ mb_per_second);
+ else {
+ total_mb = total_bytes;
+ total_mb /= 1024 * 1024;
+
+ printf(" %5.2Lf %3.1qu %5.2Lf ",
+ kb_per_transfer,
+ total_transfers,
+ total_mb);
+ }
+ }
+ }
+}
+
+static void
+cpustats(void)
+{
+ register int state;
+ double time;
+
+ time = 0.0;
+
+ for (state = 0; state < CPUSTATES; ++state)
+ time += cur.cp_time[state];
+ for (state = 0; state < CPUSTATES; ++state)
+ printf("%3.0f",
+ rint(100. * cur.cp_time[state] / (time ? time : 1)));
+}
+
+static int
+readvar(kvm_t *kd, const char *name, int nlid, void *ptr, size_t len)
+{
+ if (kd != NULL) {
+ ssize_t nbytes;
+
+ nbytes = kvm_read(kd, nlid, ptr, len);
+
+ if (nbytes == 0) {
+ warnx("kvm_read(%s): %s", name, kvm_geterr(kd));
+ return (1);
+ }
+ if (nbytes != len) {
+ warnx("kvm_read(%s): expected %lu bytes, got %ld bytes",
+ name, (unsigned long)len, (long)nbytes);
+ return (1);
+ }
+ } else {
+ size_t nlen = len;
+
+ if (sysctlbyname(name, ptr, &nlen, NULL, 0) == -1) {
+ warn("sysctl(%s...) failed", name);
+ return (1);
+ }
+ if (nlen != len) {
+ warnx("sysctl(%s...): expected %lu, got %lu", name,
+ (unsigned long)len, (unsigned long)nlen);
+ return (1);
+ }
+ }
+ return (0);
+}
diff --git a/usr.sbin/ipftest/Makefile b/usr.sbin/ipftest/Makefile
new file mode 100644
index 0000000..db4efba
--- /dev/null
+++ b/usr.sbin/ipftest/Makefile
@@ -0,0 +1,19 @@
+# $FreeBSD$
+
+MAINTAINER= guido@freebsd.org
+
+.PATH: ${.CURDIR}/../../sys/contrib/ipfilter/netinet \
+ ${.CURDIR}/../../contrib/ipfilter ${.CURDIR}/../../contrib/ipfilter/man
+
+PROG= ipftest
+SRCS= ipt.c parse.c fil.c ipft_sn.c ipft_ef.c ipft_td.c ipft_pc.c opt.c \
+ ipft_tx.c misc.c ip_frag.c ip_state.c ip_nat.c ip_proxy.c \
+ ip_log.c ip_auth.c ipft_hx.c ip_fil.c natparse.c facpri.c common.c \
+ printstate.c printnat.c
+
+CFLAGS+= -DUSE_INET6 -DIPL_NAME=\"/dev/ipl\" -DIPFILTER_LOG
+CFLAGS+= -I${.CURDIR}/../../sys/contrib/ipfilter/netinet
+CFLAGS+= -I${.CURDIR}/../../sys/contrib/ipfilter
+CFLAGS+= -I${.CURDIR}/../../contrib/ipfilter
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ipresend/Makefile b/usr.sbin/ipresend/Makefile
new file mode 100644
index 0000000..feb0b9a
--- /dev/null
+++ b/usr.sbin/ipresend/Makefile
@@ -0,0 +1,17 @@
+# $FreeBSD$
+
+MAINTAINER= guido@freebsd.org
+
+.PATH: ${.CURDIR}/../../contrib/ipfilter/ipsend \
+ ${.CURDIR}/../../contrib/ipfilter
+
+PROG= ipresend
+SRCS= ipresend.c ip.c resend.c opt.c ipft_ef.c ipft_hx.c ipft_sn.c ipft_td.c \
+ ipft_tx.c sbpf.c 44arp.c ipft_pc.c
+
+CFLAGS+= -DDOSOCKET -DIPL_NAME=\"/dev/ipl\" -DUSE_INET6
+CFLAGS+= -I${.CURDIR}/../../sys/contrib/ipfilter/netinet
+CFLAGS+= -I${.CURDIR}/../../contrib/ipfilter/ipsend \
+ -I${.CURDIR}/../../contrib/ipfilter
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ipsend/Makefile b/usr.sbin/ipsend/Makefile
new file mode 100644
index 0000000..ee7085f
--- /dev/null
+++ b/usr.sbin/ipsend/Makefile
@@ -0,0 +1,23 @@
+# $FreeBSD$
+
+MAINTAINER= guido@freebsd.org
+
+.PATH: ${.CURDIR}/../../contrib/ipfilter/ipsend \
+ ${.CURDIR}/../../contrib/ipfilter/iplang \
+ ${.CURDIR}/../../contrib/ipfilter
+
+PROG= ipsend
+MAN= ipsend.1 ipsend.5
+SRCS= ipsend.c ip.c ipsopt.c sbpf.c sock.c 44arp.c iplang_y.y iplang_l.l \
+ y.tab.h
+
+CFLAGS+= -DDOSOCKET -DIPL_NAME=\"/dev/ipl\" -DUSE_INET6
+CFLAGS+= -I${.CURDIR}/../../sys/contrib/ipfilter/netinet
+CFLAGS+= -I. -I${.CURDIR}/../../contrib/ipfilter/ipsend \
+ -I${.CURDIR}/../../contrib/ipfilter/iplang \
+ -I${.CURDIR}/../../contrib/ipfilter
+
+DPADD= ${LIBL}
+LDADD= -ll
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/iptest/Makefile b/usr.sbin/iptest/Makefile
new file mode 100644
index 0000000..f7a965d
--- /dev/null
+++ b/usr.sbin/iptest/Makefile
@@ -0,0 +1,16 @@
+# $FreeBSD$
+
+MAINTAINER= guido@freebsd.org
+
+.PATH: ${.CURDIR}/../../contrib/ipfilter/ipsend \
+ ${.CURDIR}/../../contrib/ipfilter
+
+PROG= iptest
+SRCS= iptest.c iptests.c ip.c sbpf.c 44arp.c sock.c
+
+CFLAGS+= -DDOSOCKET -DIPL_NAME=\"/dev/ipl\" -DUSE_INET6
+CFLAGS+= -I${.CURDIR}/../../sys/contrib/ipfilter/netinet
+CFLAGS+= -I${.CURDIR}/../../contrib/ipfilter/ipsend \
+ -I${.CURDIR}/../../contrib/ipfilter
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/jail/Makefile b/usr.sbin/jail/Makefile
new file mode 100644
index 0000000..0d18ff4
--- /dev/null
+++ b/usr.sbin/jail/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= jail
+MAN= jail.8
+
+WARNS?= 2
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/jail/jail.8 b/usr.sbin/jail/jail.8
new file mode 100644
index 0000000..2abe498
--- /dev/null
+++ b/usr.sbin/jail/jail.8
@@ -0,0 +1,424 @@
+.\"
+.\" Copyright (c) 2000 Robert N. M. Watson
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"
+.\" ----------------------------------------------------------------------------
+.\" "THE BEER-WARE LICENSE" (Revision 42):
+.\" <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you
+.\" can do whatever you want with this stuff. If we meet some day, and you think
+.\" this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+.\" ----------------------------------------------------------------------------
+.\"
+.\" $FreeBSD$
+.\"
+.Dd December 12, 2001
+.Dt JAIL 8
+.Os
+.Sh NAME
+.Nm jail
+.Nd "imprison process and its descendants"
+.Sh SYNOPSIS
+.Nm
+.Ar path hostname ip-number command ...
+.Sh DESCRIPTION
+The
+.Nm
+command imprisons a process and all future descendants.
+.Pp
+Please see the
+.Xr jail 2
+man page for further details.
+.Sh EXAMPLES
+.Ss "Setting up a Jail Directory Tree"
+This example shows how to setup a jail directory tree
+containing an entire
+.Fx
+distribution:
+.Bd -literal
+D=/here/is/the/jail
+cd /usr/src
+mkdir -p $D
+make world DESTDIR=$D
+cd etc
+make distribution DESTDIR=$D -DNO_MAKEDEV_RUN
+cd $D/dev
+sh MAKEDEV jail
+cd $D
+ln -sf dev/null kernel
+.Ed
+.Pp
+In many cases this example would put far more stuff in the jail than is needed.
+In the other extreme case a jail might contain only one single file:
+the executable to be run in the jail.
+.Pp
+We recommend experimentation and caution that it is a lot easier to
+start with a
+.Dq fat
+jail and remove things until it stops working,
+than it is to start with a
+.Dq thin
+jail and add things until it works.
+.Ss "Setting Up a Jail"
+Do what was described in
+.Sx "Setting Up a Jail Directory Tree"
+to build the jail directory tree.
+For the sake of this example, we will
+assume you built it in
+.Pa /data/jail/192.168.11.100 ,
+named for the jailed IP address.
+Substitute below as needed with your
+own directory, IP address, and hostname.
+.Pp
+First, you will want to set up your real system's environment to be
+.Dq jail-friendly .
+For consistency, we will refer to the parent box as the
+.Dq "host environment" ,
+and to the jailed virtual machine as the
+.Dq "jail environment" .
+Because jail is implemented using IP aliases, one of the first things to do
+is to disable IP services on the host system that listen on all local
+IP addresses for a service.
+This means changing
+.Xr inetd 8
+to only listen on the
+appropriate IP address, and so forth.
+Add the following to
+.Pa /etc/rc.conf
+in the host environment:
+.Bd -literal -offset indent
+sendmail_enable="NO"
+inetd_flags="-wW -a 192.168.11.23"
+portmap_enable="NO"
+.Ed
+.Pp
+.Li 192.168.11.23
+is the native IP address for the host system, in this example.
+Daemons that run out of
+.Xr inetd 8
+can be easily set to use only the specified host IP address.
+Other daemons
+will need to be manually configured\(emfor some this is possible through
+the
+.Xr rc.conf 5
+flags entries, for others it is not possible without munging
+the per-application configuration files, or even recompiling.
+For those
+applications that cannot specify the IP they run on, it is better to disable
+them, if possible.
+.Pp
+A number of daemons ship with the base system that may have problems when
+run from outside of a jail in a jail-centric environment.
+This includes
+.Xr sendmail 8 ,
+.Xr named 8 ,
+and
+.Xr rpcbind 8 .
+While
+.Xr sendmail 8
+and
+.Xr named 8
+can be configured to listen only on a specific
+IP using their configuration files, in most cases it is easier to simply
+run the daemons in jails only, and not in the host environment.
+Attempting to serve
+NFS from the host environment may also cause confusion, and cannot be
+easily reconfigured to use only specific IPs, as some NFS services are
+hosted directly from the kernel.
+Any third party network software running
+in the host environment should also be checked and configured so that it
+does not bind all IP addresses, which would result in those services also
+appearing to be offered by the jail environments.
+.Pp
+Once
+these daemons have been disabled or fixed in the host environment, it is
+best to reboot so that all daemons are in a known state, to reduce the
+potential for confusion later (such as finding that when you send mail
+to a jail, and its sendmail is down, the mail is delivered to the host,
+etc.)
+.Pp
+Start any jails for the first time without configuring the network
+interface so that you can clean it up a little and set up accounts.
+As
+with any machine (virtual or not) you will need to set a root password, time
+zone, etc.
+Before beginning, you may want to copy
+.Xr sysinstall 8
+into the tree so that you can use it to set things up easily.
+Do this using:
+.Bd -literal -offset indent
+mkdir /data/jail/192.168.11.100/stand
+cp /stand/sysinstall /data/jail/192.168.11.100/stand
+.Ed
+.Pp
+Now start the jail:
+.Pp
+.Dl "jail /data/jail/192.168.11.100 testhostname 192.168.11.100 /bin/sh"
+.Pp
+You will end up with a shell prompt, assuming no errors, within the jail.
+You can now run
+.Pa /stand/sysinstall
+and do the post-install configuration to set various configuration options,
+or perform these actions manually by editing
+.Pa /etc/rc.conf ,
+etc.
+.Pp
+.Bl -bullet -offset indent -compact
+.It
+Create an empty
+.Pa /etc/fstab
+to quell startup warnings about missing fstab
+.It
+Disable the port mapper
+.Pa ( /etc/rc.conf :
+.Li portmap_enable="NO" )
+.It
+Run
+.Xr newaliases 1
+to quell
+.Xr sendmail 8
+warnings.
+.It
+Disable interface configuration to quell startup warnings about
+.Xr ifconfig 8
+.Pq Li network_interfaces=""
+.It
+Configure
+.Pa /etc/resolv.conf
+so that name resolution within the jail will work correctly
+.It
+Set a root password, probably different from the real host system
+.It
+Set the timezone
+.It
+Add accounts for users in the jail environment
+.It
+Install any packages that you think the environment requires
+.El
+.Pp
+You may also want to perform any package-specific configuration (web servers,
+SSH servers, etc), patch up
+.Pa /etc/syslog.conf
+so it logs as you would like, etc.
+.Pp
+Exit from the shell, and the jail will be shut down.
+.Ss "Starting the Jail"
+You are now ready to restart the jail and bring up the environment with
+all of its daemons and other programs.
+To do this, first bring up the
+virtual host interface, and then start the jail's
+.Pa /etc/rc
+script from within the jail.
+.Pp
+NOTE: If you plan to allow untrusted users to have root access inside the
+jail, you may wish to consider setting the
+.Va security.jail.set_hostname_allowed
+to 0.
+Please see the management reasons why this is a good idea.
+If you do decide to set this variable,
+it must be set before starting any jails, and once each boot.
+.Bd -literal -offset indent
+ifconfig ed0 inet alias 192.168.11.100/32
+mount -t procfs proc /data/jail/192.168.11.100/proc
+jail /data/jail/192.168.11.100 testhostname 192.168.11.100 \\
+ /bin/sh /etc/rc
+.Ed
+.Pp
+A few warnings will be produced, because most
+.Xr sysctl 8
+configuration variables cannot be set from within the jail, as they are
+global across all jails and the host environment.
+However, it should all
+work properly.
+You should be able to see
+.Xr inetd 8 ,
+.Xr syslogd 8 ,
+and other processes running within the jail using
+.Xr ps 1 ,
+with the
+.Ql J
+flag appearing beside jailed processes.
+You should also be able to
+.Xr telnet 1
+to the hostname or IP address of the jailed environment, and log
+in using the accounts you created previously.
+.Ss "Managing the Jail"
+Normal machine shutdown commands, such as
+.Xr halt 8 ,
+.Xr reboot 8 ,
+and
+.Xr shutdown 8 ,
+cannot be used successfully within the jail.
+To kill all processes in a
+jail, you may log into the jail and, as root, use one of the following
+commands, depending on what you want to accomplish:
+.Pp
+.Bd -literal -offset indent
+kill -TERM -1
+kill -KILL -1
+.Ed
+.Pp
+This will send the
+.Dv SIGTERM
+or
+.Dv SIGKILL
+signals to all processes in the jail from within the jail.
+Depending on
+the intended use of the jail, you may also want to run
+.Pa /etc/rc.shutdown
+from within the jail.
+Currently there is no way to insert new processes
+into a jail, so you must first log into the jail before performing these
+actions.
+.Pp
+To kill processes from outside the jail, you must individually identify the
+PID of each process to be killed.
+The
+.Pa /proc/ Ns Ar pid Ns Pa /status
+file contains, as its last field, the hostname of the jail in which the
+process runs, or
+.Dq Li -
+to indicate that the process is not running within a jail.
+The
+.Xr ps 1
+command also shows a
+.Ql J
+flag for processes in a jail.
+However, the hostname for a jail may be, by
+default, modified from within the jail, so the
+.Pa /proc
+status entry is unreliable by default.
+To disable the setting of the hostname
+from within a jail, set the
+.Va security.jail.set_hostname_allowed
+sysctl variable in the host environment to 0, which will affect all jails.
+You can have this sysctl set on each boot using
+.Xr sysctl.conf 5 .
+Just add the following line to
+.Pa /etc/sysctl.conf :
+.Pp
+.Dl security.jail.set_hostname_allowed=0
+.Pp
+In a future version of
+.Fx ,
+the mechanisms for managing jails will be
+more refined.
+.Ss "Sysctl MIB Entries"
+Certain aspects of the jail containments environment may be modified from
+the host environment using
+.Xr sysctl 8
+MIB variables.
+Currently, these variables affect all jails on the system, although in
+the future this functionality may be finer grained.
+.Bl -tag -width XXX
+.It Va security.jail.set_hostname_allowed
+This MIB entry determines whether or not processes within a jail are
+allowed to change their hostname via
+.Xr hostname 1
+or
+.Xr sethostname 3 .
+In the current jail implementation, the ability to set the hostname from
+within the jail can impact management tools relying on the accuracy of jail
+information in
+.Pa /proc .
+As such, this should be disabled in environments where privileged access to
+jails is given out to untrusted parties.
+.It Va security.jail.socket_unixiproute_only
+The jail functionality binds an IPv4 address to each jail, and limits
+access to other network addresses in the IPv4 space that may be available
+in the host environment.
+However, jail is not currently able to limit access to other network
+protocol stacks that have not had jail functionality added to them.
+As such, by default, processes within jails may only access protocols
+in the following domains:
+.Dv PF_LOCAL , PF_INET ,
+and
+.Dv PF_ROUTE ,
+permitting them access to
+.Ux
+domain sockets,
+IPv4 addresses, and routing sockets.
+To enable access to other domains, this MIB variable may be set to
+0.
+.It Va security.jail.sysvipc_allowed
+This MIB entry determines whether or not processes within a jail have access
+to System V IPC primitives.
+In the current jail implementation, System V primitives share a single
+namespace across the host and jail environments, meaning that processes
+within a jail would be able to communicate with (and potentially interfere
+with) processes outside of the jail, and in other jails.
+As such, this functionality is disabled by default, but can be enabled
+by setting this MIB entry to 1.
+.El
+.Sh SEE ALSO
+.Xr newaliases 1 ,
+.Xr ps 1 ,
+.Xr chroot 2 ,
+.Xr jail 2 ,
+.Xr procfs 5 ,
+.Xr rc.conf 5 ,
+.Xr sysctl.conf 5 ,
+.Xr halt 8 ,
+.Xr inetd 8 ,
+.Xr named 8 ,
+.Xr reboot 8 ,
+.Xr rpcbind 8 ,
+.Xr sendmail 8 ,
+.Xr shutdown 8 ,
+.Xr sysctl 8 ,
+.Xr syslogd 8
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Fx 4.0 .
+.Sh AUTHORS
+.An -nosplit
+The jail feature was written by
+.An Poul-Henning Kamp
+for R&D Associates
+.Pa http://www.rndassociates.com/
+who contributed it to
+.Fx .
+.Pp
+.An Robert Watson
+wrote the extended documentation, found a few bugs, added
+a few new features, and cleaned up the userland jail environment.
+.Sh BUGS
+Jail currently lacks strong management functionality, such as the ability
+to deliver signals to all processes in a jail, and to allow access to
+specific jail information via
+.Xr ps 1
+as opposed to
+.Xr procfs 5 .
+Similarly, it might be a good idea to add an
+address alias flag such that daemons listening on all IPs
+.Pq Dv INADDR_ANY
+will not bind on that address, which would facilitate building a safe
+host environment such that host daemons do not impose on services offered
+from within jails.
+Currently, the simplist answer is to minimize services
+offered on the host, possibly limiting it to services offered from
+.Xr inetd 8
+which is easily configurable.
diff --git a/usr.sbin/jail/jail.c b/usr.sbin/jail/jail.c
new file mode 100644
index 0000000..dded501
--- /dev/null
+++ b/usr.sbin/jail/jail.c
@@ -0,0 +1,53 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ *
+ * $FreeBSD$
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/jail.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+int
+main(int argc, char **argv)
+{
+ struct jail j;
+ int i;
+ struct in_addr in;
+
+ if (argc < 5)
+ errx(1, "usage: %s path hostname ip-number command ...\n",
+ argv[0]);
+ i = chdir(argv[1]);
+ if (i)
+ err(1, "chdir %s", argv[1]);
+ memset(&j, 0, sizeof(j));
+ j.version = 0;
+ j.path = argv[1];
+ j.hostname = argv[2];
+ i = inet_aton(argv[3], &in);
+ if (!i)
+ errx(1, "Couldn't make sense of ip-number\n");
+ j.ip_number = ntohl(in.s_addr);
+ i = jail(&j);
+ if (i)
+ err(1, "Imprisonment failed");
+ i = execv(argv[4], argv + 4);
+ if (i)
+ err(1, "execv(%s)", argv[4]);
+ exit (0);
+}
diff --git a/usr.sbin/kbdcontrol/Makefile b/usr.sbin/kbdcontrol/Makefile
new file mode 100644
index 0000000..ad6540a
--- /dev/null
+++ b/usr.sbin/kbdcontrol/Makefile
@@ -0,0 +1,13 @@
+# $FreeBSD$
+
+PROG= kbdcontrol
+MAN= kbdcontrol.1 kbdmap.5
+MLINKS= kbdmap.5 keymap.5
+SRCS= kbdcontrol.c lex.l
+
+CFLAGS+= -I${.CURDIR}
+
+DPADD= ${LIBL}
+LDADD= -ll
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/kbdcontrol/kbdcontrol.1 b/usr.sbin/kbdcontrol/kbdcontrol.1
new file mode 100644
index 0000000..8ef617f
--- /dev/null
+++ b/usr.sbin/kbdcontrol/kbdcontrol.1
@@ -0,0 +1,235 @@
+.\"
+.\" kbdcontrol - a utility for manipulating the syscons keyboard driver section
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" @(#)kbdcontrol.1
+.\" $FreeBSD$
+.\"
+.Dd May 27, 2001
+.Dt KBDCONTROL 1
+.Os
+.Sh NAME
+.Nm kbdcontrol
+.Nd keyboard control and configuration utility
+.Sh SYNOPSIS
+.Nm
+.Op Fl dFKix
+.Oo
+.Fl b
+.Ar duration . Ns Ar pitch | Ar belltype
+.Oc
+.Oo
+.Fl r
+.Ar delay . Ns Ar repeat | Ar speed
+.Oc
+.Op Fl l Ar keymap_file
+.Op Fl f Ar # Ar string
+.Op Fl k Ar keyboard_device
+.Op Fl L Ar keymap_file
+.Sh DESCRIPTION
+The
+.Nm
+command is used to set various keyboard related options for the
+.Xr syscons 4
+console driver and the keyboard drivers,
+such as key map, keyboard repeat and delay rates, bell
+characteristics etc.
+.Pp
+Keyboard options may be automatically configured at system boot time by
+setting variables in
+.Pa /etc/rc.conf .
+See
+.Sx Boot Time Configuration
+below.
+.Pp
+The following command line options are supported:
+.Bl -tag -width indent
+.It Fl b Xo
+.Ar duration . Ns Ar pitch | Ar belltype
+.Xc
+Set the bell duration in milliseconds and pitch in hertz.
+If a
+.Ar belltype
+argument is specified, it may be one of
+.Cm normal
+which sets sound parameters back to normal values,
+.Cm off
+which disables the bell entirely, or
+.Cm visual
+which sets the bell to visual mode, i.e. flashes the screen instead.
+If
+.Ar belltype
+is preceded by the word
+.Cm quiet. ,
+the bell will not be rung when the ringing process is in the background vty.
+.It Fl r Xo
+.Ar delay . Ns Ar repeat | Ar speed
+.Xc
+Set keyboard
+.Ar delay
+(250, 500, 750, 1000)
+and
+.Ar repeat
+(34, 38, 42, 46, 50, 55, 59, 63, 68, 76, 84, 92, 100, 110, 118, 126,
+136, 152, 168, 184, 200, 220, 236, 252, 272, 304, 336, 368, 400, 440,
+472, 504)
+rates, or if a
+.Ar speed
+argument is specified, it may be one of
+.Cm slow
+(1000.504),
+.Cm fast
+(250.34)
+or
+.Cm normal
+(500.126).
+.It Fl l Ar keymap_file
+Install keyboard map file from
+.Ar keymap_file .
+You may load the keyboard map file from a menu-driven command,
+.Xr kbdmap 1 .
+.It Fl d
+Dump the current keyboard map onto stdout.
+The output may be redirected to a file and can be loaded
+back to the kernel later by the
+.Fl l
+option above.
+.It Fl f Ar # Ar string
+Set function key number
+.Ar #
+to send
+.Ar string .
+Refer to the man page for the keyboard driver
+(e.g.\&
+.Xr atkbd 4 )
+for available function keys and their numbers.
+.It Fl F
+Set function keys back to the standard definitions.
+.It Fl x
+Use hexadecimal numbers in keyboard map dump.
+.It Fl i
+Print brief information about the keyboard.
+.It Fl K
+Disconnect the keyboard from the console.
+You need to use the
+.Fl k
+option below to associate a keyboard with the console again.
+.It Fl k Ar keyboard_device
+Use the specified device as the console keyboard.
+When using this option, the standard input of the
+.Nm
+process should be redirected from
+.Pa /dev/console
+if you are not working on the system console
+(see the
+.Sx EXAMPLES
+section).
+.It Fl L Ar keymap_file
+Load keyboard map file from
+.Ar keymap_file
+and write the
+.Ft "struct keymap"
+compiled from it to stdout.
+This option is primarily intended for programmers and is probably
+of little use under normal circumstances.
+.El
+.Sh ENVIRONMENT
+The environment variable
+.Ev KEYMAP_PATH
+can hold an alternative path to the keyboard map files.
+.Sh KEYBOARD CONFIGURATION
+.Ss Boot Time Configuration
+You may set variables in
+.Pa /etc/rc.conf
+or
+.Pa /etc/rc.conf.local
+in order to configure the keyboard at boot time.
+The following is the list of relevant variables.
+.Pp
+.Bl -tag -width foo_bar_var -compact
+.It Ar keymap
+Specifies a keyboard map file for the
+.Fl l
+option.
+.It Ar keyrate
+Sets the keyboard repeat rate for the
+.Fl r
+option.
+.It Ar keychange
+Lists function key strings for the
+.Fl f
+option.
+.El
+.Pp
+See
+.Xr rc.conf 5
+for details.
+.Ss Driver Configuration
+The keyboard device driver may let you change default configuration
+options, such as the default keyboard map, so that you do not need to set up
+the options at boot time.
+See keyboard driver manuals
+(e.g.\&
+.Xr atkbd 4 ,
+.Xr ukbd 4 )
+for details.
+.Sh FILES
+.Bl -tag -width /usr/share/syscons/keymaps/foo_bar -compact
+.It Pa /usr/share/syscons/keymaps/*
+keyboard map files
+.El
+.Sh EXAMPLES
+The following command will load the keyboard map file
+.Pa /usr/share/syscons/keymaps/ru.koi8-r.kbd .
+.Pp
+.Dl kbdcontrol -l /usr/share/syscons/keymaps/ru.koi8-r.kbd
+.Pp
+So long as the keyboard map file resides in
+.Pa /usr/share/syscons/keymaps ,
+you may abbreviate the file name as
+.Pa ru.koi8-r .
+.Pp
+.Dl kbdcontrol -l ru.koi8-r
+.Pp
+The following command will make the function key 10 emit "telnet myhost".
+.Pp
+.Dl kbdcontrol -f 10 \&"telnet myhost\&"
+.Pp
+In order to get the visual effect for bell, but prevent the screen
+from flushing if the bell is to ring in the background screen,
+run the following command.
+.Pp
+.Dl kbdcontrol -b quiet.visual
+.Pp
+To change the default console keyboard to the another keyboard,
+for example the first USB keyboard (see
+.Xr ukbd 4 ) ,
+use the following commands.
+.Pp
+.Dl kbdcontrol -k /dev/kbd1 < /dev/console
+.Pp
+To switch back to the default keyboard, use this command.
+.Pp
+.Dl kbdcontrol -k /dev/kbd0
+.Sh BUGS
+Report when found.
+.Sh SEE ALSO
+.Xr kbdmap 1 ,
+.Xr vidcontrol 1 ,
+.Xr atkbd 4 ,
+.Xr keyboard 4 ,
+.Xr screen 4 ,
+.Xr syscons 4 ,
+.Xr ukbd 4 ,
+.Xr kbdmap 5 ,
+.Xr rc.conf 5
+.Sh AUTHORS
+.An S\(/oren Schmidt Aq sos@FreeBSD.org
diff --git a/usr.sbin/kbdcontrol/kbdcontrol.c b/usr.sbin/kbdcontrol/kbdcontrol.c
new file mode 100644
index 0000000..0a03ce7
--- /dev/null
+++ b/usr.sbin/kbdcontrol/kbdcontrol.c
@@ -0,0 +1,1130 @@
+/*-
+ * Copyright (c) 1994-1995 Søren Schmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#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 <sys/kbio.h>
+#include <sys/consio.h>
+#include "path.h"
+#include "lex.h"
+
+/*
+ * HALT, PDWN, and PASTE aren't defined in 4.x, but we need them to bridge
+ * to 5.0-current so define them here as a stop gap transition measure.
+ */
+#ifndef HALT
+#define HALT 0xa1 /* halt machine */
+#endif
+#ifndef PDWN
+#define PDWN 0xa2 /* halt machine and power down */
+#endif
+#ifndef PASTE
+#define PASTE 0xa3 /* paste from cut-paste buffer */
+#endif
+
+char ctrl_names[32][4] = {
+ "nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel",
+ "bs ", "ht ", "nl ", "vt ", "ff ", "cr ", "so ", "si ",
+ "dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb",
+ "can", "em ", "sub", "esc", "fs ", "gs ", "rs ", "us "
+ };
+
+char acc_names[15][5] = {
+ "dgra", "dacu", "dcir", "dtil", "dmac", "dbre", "ddot",
+ "duml", "dsla", "drin", "dced", "dapo", "ddac", "dogo",
+ "dcar",
+ };
+
+char acc_names_u[15][5] = {
+ "DGRA", "DACU", "DCIR", "DTIL", "DMAC", "DBRE", "DDOT",
+ "DUML", "DSLA", "DRIN", "DCED", "DAPO", "DDAC", "DOGO",
+ "DCAR",
+ };
+
+char fkey_table[96][MAXFK] = {
+/* 01-04 */ "\033[M", "\033[N", "\033[O", "\033[P",
+/* 05-08 */ "\033[Q", "\033[R", "\033[S", "\033[T",
+/* 09-12 */ "\033[U", "\033[V", "\033[W", "\033[X",
+/* 13-16 */ "\033[Y", "\033[Z", "\033[a", "\033[b",
+/* 17-20 */ "\033[c", "\033[d", "\033[e", "\033[f",
+/* 21-24 */ "\033[g", "\033[h", "\033[i", "\033[j",
+/* 25-28 */ "\033[k", "\033[l", "\033[m", "\033[n",
+/* 29-32 */ "\033[o", "\033[p", "\033[q", "\033[r",
+/* 33-36 */ "\033[s", "\033[t", "\033[u", "\033[v",
+/* 37-40 */ "\033[w", "\033[x", "\033[y", "\033[z",
+/* 41-44 */ "\033[@", "\033[[", "\033[\\","\033[]",
+/* 45-48 */ "\033[^", "\033[_", "\033[`", "\033[{",
+/* 49-52 */ "\033[H", "\033[A", "\033[I", "-" ,
+/* 53-56 */ "\033[D", "\033[E", "\033[C", "+" ,
+/* 57-60 */ "\033[F", "\033[B", "\033[G", "\033[L",
+/* 61-64 */ "\177", "\033[J", "\033[~", "\033[}",
+/* 65-68 */ "" , "" , "" , "" ,
+/* 69-72 */ "" , "" , "" , "" ,
+/* 73-76 */ "" , "" , "" , "" ,
+/* 77-80 */ "" , "" , "" , "" ,
+/* 81-84 */ "" , "" , "" , "" ,
+/* 85-88 */ "" , "" , "" , "" ,
+/* 89-92 */ "" , "" , "" , "" ,
+/* 93-96 */ "" , "" , "" , "" ,
+ };
+
+const int delays[] = {250, 500, 750, 1000};
+const int repeats[] = { 34, 38, 42, 46, 50, 55, 59, 63,
+ 68, 76, 84, 92, 100, 110, 118, 126,
+ 136, 152, 168, 184, 200, 220, 236, 252,
+ 272, 304, 336, 368, 400, 440, 472, 504};
+const int ndelays = (sizeof(delays) / sizeof(int));
+const int nrepeats = (sizeof(repeats) / sizeof(int));
+int hex = 0;
+int number;
+char letter;
+int token;
+
+int get_accent_definition_line __P((accentmap_t *));
+int get_key_definition_line __P((keymap_t *));
+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 TPREV:
+ return PREV | 0x100;
+ case TRCTR:
+ return RCTR | 0x100;
+ case TRALT:
+ return RALT | 0x100;
+ case TALK:
+ return ALK | 0x100;
+ case TASH:
+ return ASH | 0x100;
+ case TMETA:
+ return META | 0x100;
+ case TRBT:
+ return RBT | 0x100;
+ case TDBG:
+ return DBG | 0x100;
+ case TSUSP:
+ return SUSP | 0x100;
+ case TSPSC:
+ return SPSC | 0x100;
+ case TPANIC:
+ return PNC | 0x100;
+ case TLSHA:
+ return LSHA | 0x100;
+ case TRSHA:
+ return RSHA | 0x100;
+ case TLCTRA:
+ return LCTRA | 0x100;
+ case TRCTRA:
+ return RCTRA | 0x100;
+ case TLALTA:
+ return LALTA | 0x100;
+ case TRALTA:
+ return RALTA | 0x100;
+ case THALT:
+ return HALT | 0x100;
+ case TPDWN:
+ return PDWN | 0x100;
+ case TPASTE:
+ return PASTE | 0x100;
+ case TACC:
+ if (ACC(number) > L_ACC)
+ return -1;
+ return ACC(number) | 0x100;
+ case TFUNC:
+ if (F(number) > L_FN)
+ return -1;
+ return F(number) | 0x100;
+ case TSCRN:
+ if (S(number) > L_SCR)
+ return -1;
+ return S(number) | 0x100;
+ case TLET:
+ return (unsigned char)letter;
+ case TNUM:
+ if (number < 0 || number > 255)
+ return -1;
+ return number;
+ default:
+ return -1;
+ }
+}
+
+static int
+get_definition_line(FILE *fd, keymap_t *keymap, accentmap_t *accentmap)
+{
+ int c;
+
+ yyin = fd;
+
+ if (token < 0)
+ token = yylex();
+ switch (token) {
+ case TNUM:
+ c = get_key_definition_line(keymap);
+ if (c < 0)
+ errx(1, "invalid key definition");
+ if (c > keymap->n_keys)
+ keymap->n_keys = c;
+ break;
+ case TACC:
+ c = get_accent_definition_line(accentmap);
+ if (c < 0)
+ errx(1, "invalid accent key definition");
+ if (c > accentmap->n_accs)
+ accentmap->n_accs = c;
+ break;
+ case 0:
+ /* EOF */
+ return -1;
+ default:
+ errx(1, "illegal definition line");
+ }
+ return c;
+}
+
+int
+get_key_definition_line(keymap_t *map)
+{
+ int i, def, scancode;
+
+ /* check scancode number */
+ if (number < 0 || number >= NUM_KEYS)
+ return -1;
+ scancode = number;
+
+ /* get key definitions */
+ map->key[scancode].spcl = 0;
+ for (i=0; i<NUM_STATES; i++) {
+ if ((def = get_entry()) == -1)
+ return -1;
+ if (def & 0x100)
+ map->key[scancode].spcl |= (0x80 >> i);
+ map->key[scancode].map[i] = def & 0xFF;
+ }
+ /* get lock state key def */
+ if ((token = yylex()) != TFLAG)
+ return -1;
+ map->key[scancode].flgs = number;
+ token = yylex();
+ return (scancode + 1);
+}
+
+int
+get_accent_definition_line(accentmap_t *map)
+{
+ int accent;
+ int c1, c2;
+ int i;
+
+ if (ACC(number) < F_ACC || ACC(number) > L_ACC)
+ /* number out of range */
+ return -1;
+ accent = number;
+ if (map->acc[accent].accchar != 0) {
+ /* this entry has already been defined before! */
+ errx(1, "duplicated accent key definition");
+ }
+
+ switch ((token = yylex())) {
+ case TLET:
+ map->acc[accent].accchar = letter;
+ break;
+ case TNUM:
+ map->acc[accent].accchar = number;
+ break;
+ default:
+ return -1;
+ }
+
+ for (i = 0; (token = yylex()) == '(';) {
+ switch ((token = yylex())) {
+ case TLET:
+ c1 = letter;
+ break;
+ case TNUM:
+ c1 = number;
+ break;
+ default:
+ return -1;
+ }
+ switch ((token = yylex())) {
+ case TLET:
+ c2 = letter;
+ break;
+ case TNUM:
+ c2 = number;
+ break;
+ default:
+ return -1;
+ }
+ if ((token = yylex()) != ')')
+ return -1;
+ if (i >= NUM_ACCENTCHARS) {
+ warnx("too many accented characters, ignored");
+ continue;
+ }
+ map->acc[accent].map[i][0] = c1;
+ map->acc[accent].map[i][1] = c2;
+ ++i;
+ }
+ return (accent + 1);
+}
+
+void
+print_entry(FILE *fp, int value)
+{
+ int val = value & 0xFF;
+
+ switch (value) {
+ case NOP | 0x100:
+ fprintf(fp, " nop ");
+ break;
+ case LSH | 0x100:
+ fprintf(fp, " lshift");
+ break;
+ case RSH | 0x100:
+ fprintf(fp, " rshift");
+ break;
+ case CLK | 0x100:
+ fprintf(fp, " clock ");
+ break;
+ case NLK | 0x100:
+ fprintf(fp, " nlock ");
+ break;
+ case SLK | 0x100:
+ fprintf(fp, " slock ");
+ break;
+ case BTAB | 0x100:
+ fprintf(fp, " btab ");
+ break;
+ case LALT | 0x100:
+ fprintf(fp, " lalt ");
+ break;
+ case LCTR | 0x100:
+ fprintf(fp, " lctrl ");
+ break;
+ case NEXT | 0x100:
+ fprintf(fp, " nscr ");
+ break;
+ case PREV | 0x100:
+ fprintf(fp, " pscr ");
+ break;
+ case RCTR | 0x100:
+ fprintf(fp, " rctrl ");
+ break;
+ case RALT | 0x100:
+ fprintf(fp, " ralt ");
+ break;
+ case ALK | 0x100:
+ fprintf(fp, " alock ");
+ break;
+ case ASH | 0x100:
+ fprintf(fp, " ashift");
+ break;
+ case META | 0x100:
+ fprintf(fp, " meta ");
+ break;
+ case RBT | 0x100:
+ fprintf(fp, " boot ");
+ break;
+ case DBG | 0x100:
+ fprintf(fp, " debug ");
+ break;
+ case SUSP | 0x100:
+ fprintf(fp, " susp ");
+ break;
+ case SPSC | 0x100:
+ fprintf(fp, " saver ");
+ break;
+ case PNC | 0x100:
+ fprintf(fp, " panic ");
+ break;
+ case LSHA | 0x100:
+ fprintf(fp, " lshifta");
+ break;
+ case RSHA | 0x100:
+ fprintf(fp, " rshifta");
+ break;
+ case LCTRA | 0x100:
+ fprintf(fp, " lctrla");
+ break;
+ case RCTRA | 0x100:
+ fprintf(fp, " rctrla");
+ break;
+ case LALTA | 0x100:
+ fprintf(fp, " lalta ");
+ break;
+ case RALTA | 0x100:
+ fprintf(fp, " ralta ");
+ break;
+ case HALT | 0x100:
+ fprintf(fp, " halt ");
+ break;
+ case PDWN | 0x100:
+ fprintf(fp, " pdwn ");
+ break;
+ case PASTE | 0x100:
+ fprintf(fp, " paste ");
+ break;
+ default:
+ if (value & 0x100) {
+ if (val >= F_FN && val <= L_FN)
+ fprintf(fp, " fkey%02d", val - F_FN + 1);
+ else if (val >= F_SCR && val <= L_SCR)
+ fprintf(fp, " scr%02d ", val - F_SCR + 1);
+ else if (val >= F_ACC && val <= L_ACC)
+ fprintf(fp, " %-6s", acc_names[val - F_ACC]);
+ else if (hex)
+ fprintf(fp, " 0x%02x ", val);
+ else
+ fprintf(fp, " %3d ", val);
+ }
+ else {
+ if (val < ' ')
+ fprintf(fp, " %s ", ctrl_names[val]);
+ else if (val == 127)
+ fprintf(fp, " del ");
+ else if (isascii(val) && isprint(val))
+ fprintf(fp, " '%c' ", val);
+ else if (hex)
+ fprintf(fp, " 0x%02x ", val);
+ else
+ fprintf(fp, " %3d ", val);
+ }
+ }
+}
+
+
+void
+print_key_definition_line(FILE *fp, int scancode, struct keyent_t *key)
+{
+ int i;
+
+ /* print scancode number */
+ if (hex)
+ fprintf(fp, " 0x%02x ", scancode);
+ else
+ fprintf(fp, " %03d ", scancode);
+
+ /* print key definitions */
+ for (i=0; i<NUM_STATES; i++) {
+ if (key->spcl & (0x80 >> i))
+ print_entry(fp, key->map[i] | 0x100);
+ else
+ print_entry(fp, key->map[i]);
+ }
+
+ /* print lock state key def */
+ switch (key->flgs) {
+ case 0:
+ fprintf(fp, " O\n");
+ break;
+ case 1:
+ fprintf(fp, " C\n");
+ break;
+ case 2:
+ fprintf(fp, " N\n");
+ break;
+ case 3:
+ fprintf(fp, " B\n");
+ break;
+ }
+}
+
+void
+print_accent_definition_line(FILE *fp, int accent, struct acc_t *key)
+{
+ int c;
+ int i;
+
+ if (key->accchar == 0)
+ return;
+
+ /* print accent number */
+ fprintf(fp, " %-6s", acc_names[accent]);
+ if (isascii(key->accchar) && isprint(key->accchar))
+ fprintf(fp, "'%c' ", key->accchar);
+ else if (hex)
+ fprintf(fp, "0x%02x ", key->accchar);
+ else
+ fprintf(fp, "%03d ", key->accchar);
+
+ for (i = 0; i < NUM_ACCENTCHARS; ++i) {
+ c = key->map[i][0];
+ if (c == 0)
+ break;
+ if ((i > 0) && ((i % 4) == 0))
+ fprintf(fp, "\n ");
+ if (isascii(c) && isprint(c))
+ fprintf(fp, "( '%c' ", c);
+ else if (hex)
+ fprintf(fp, "(0x%02x ", c);
+ else
+ fprintf(fp, "( %03d ", c);
+ c = key->map[i][1];
+ if (isascii(c) && isprint(c))
+ fprintf(fp, "'%c' ) ", c);
+ else if (hex)
+ fprintf(fp, "0x%02x) ", c);
+ else
+ fprintf(fp, "%03d ) ", c);
+ }
+ fprintf(fp, "\n");
+}
+
+void
+dump_entry(int value)
+{
+ if (value & 0x100) {
+ value &= 0x00ff;
+ switch (value) {
+ case NOP:
+ printf(" NOP, ");
+ break;
+ case LSH:
+ printf(" LSH, ");
+ break;
+ case RSH:
+ printf(" RSH, ");
+ break;
+ case CLK:
+ printf(" CLK, ");
+ break;
+ case NLK:
+ printf(" NLK, ");
+ break;
+ case SLK:
+ printf(" SLK, ");
+ break;
+ case BTAB:
+ printf(" BTAB, ");
+ break;
+ case LALT:
+ printf(" LALT, ");
+ break;
+ case LCTR:
+ printf(" LCTR, ");
+ break;
+ case NEXT:
+ printf(" NEXT, ");
+ break;
+ case PREV:
+ printf(" PREV, ");
+ break;
+ case RCTR:
+ printf(" RCTR, ");
+ break;
+ case RALT:
+ printf(" RALT, ");
+ break;
+ case ALK:
+ printf(" ALK, ");
+ break;
+ case ASH:
+ printf(" ASH, ");
+ break;
+ case META:
+ printf(" META, ");
+ break;
+ case RBT:
+ printf(" RBT, ");
+ break;
+ case DBG:
+ printf(" DBG, ");
+ break;
+ case SUSP:
+ printf(" SUSP, ");
+ break;
+ case SPSC:
+ printf(" SPSC, ");
+ break;
+ case PNC:
+ printf(" PNC, ");
+ break;
+ case LSHA:
+ printf(" LSHA, ");
+ break;
+ case RSHA:
+ printf(" RSHA, ");
+ break;
+ case LCTRA:
+ printf("LCTRA, ");
+ break;
+ case RCTRA:
+ printf("RCTRA, ");
+ break;
+ case LALTA:
+ printf("LALTA, ");
+ break;
+ case RALTA:
+ printf("RALTA, ");
+ break;
+ case HALT:
+ printf(" HALT, ");
+ break;
+ case PDWN:
+ printf(" PDWN, ");
+ break;
+ case PASTE:
+ printf("PASTE, ");
+ break;
+ default:
+ if (value >= F_FN && value <= L_FN)
+ printf(" F(%2d),", value - F_FN + 1);
+ else if (value >= F_SCR && value <= L_SCR)
+ printf(" S(%2d),", value - F_SCR + 1);
+ else if (value >= F_ACC && value <= L_ACC)
+ printf(" %-4s, ", acc_names_u[value - F_ACC]);
+ else
+ printf(" 0x%02X, ", value);
+ break;
+ }
+ } else if (value == '\'') {
+ printf(" '\\'', ");
+ } else if (value == '\\') {
+ printf(" '\\\\', ");
+ } else if (isascii(value) && isprint(value)) {
+ printf(" '%c', ", value);
+ } else {
+ printf(" 0x%02X, ", value);
+ }
+}
+
+void
+dump_key_definition(char *name, keymap_t *keymap)
+{
+ int i, j;
+
+ printf("static keymap_t keymap_%s = { 0x%02x, {\n",
+ name, (unsigned)keymap->n_keys);
+ printf(
+"/* alt\n"
+" * scan cntrl alt alt cntrl\n"
+" * code base shift cntrl shift alt shift cntrl shift spcl flgs\n"
+" * ---------------------------------------------------------------------------\n"
+" */\n");
+ for (i = 0; i < keymap->n_keys; i++) {
+ printf("/*%02x*/{{", i);
+ for (j = 0; j < NUM_STATES; j++) {
+ if (keymap->key[i].spcl & (0x80 >> j))
+ dump_entry(keymap->key[i].map[j] | 0x100);
+ else
+ dump_entry(keymap->key[i].map[j]);
+ }
+ printf("}, 0x%02X,0x%02X },\n",
+ (unsigned)keymap->key[i].spcl,
+ (unsigned)keymap->key[i].flgs);
+ }
+ printf("} };\n\n");
+}
+
+void
+dump_accent_definition(char *name, accentmap_t *accentmap)
+{
+ int i, j;
+ int c;
+
+ printf("static accentmap_t accentmap_%s = { %d",
+ name, accentmap->n_accs);
+ if (accentmap->n_accs <= 0) {
+ printf(" };\n\n");
+ return;
+ }
+ printf(", {\n");
+ for (i = 0; i < NUM_DEADKEYS; i++) {
+ printf(" /* %s=%d */\n {", acc_names[i], i);
+ c = accentmap->acc[i].accchar;
+ if (c == '\'')
+ printf(" '\\'', {");
+ else if (c == '\\')
+ printf(" '\\\\', {");
+ else if (isascii(c) && isprint(c))
+ printf(" '%c', {", c);
+ else if (c == 0) {
+ printf(" 0x00 }, \n");
+ continue;
+ } else
+ printf(" 0x%02x, {", c);
+ for (j = 0; j < NUM_ACCENTCHARS; j++) {
+ c = accentmap->acc[i].map[j][0];
+ if (c == 0)
+ break;
+ if ((j > 0) && ((j % 4) == 0))
+ printf("\n\t ");
+ if (isascii(c) && isprint(c))
+ printf(" { '%c',", c);
+ else
+ printf(" { 0x%02x,", c);
+ printf("0x%02x },", accentmap->acc[i].map[j][1]);
+ }
+ printf(" }, },\n");
+ }
+ printf("} };\n\n");
+}
+
+void
+load_keymap(char *opt, int dumponly)
+{
+ keymap_t keymap;
+ accentmap_t accentmap;
+ FILE *fd;
+ int i, j;
+ char *name, *cp;
+ char *prefix[] = {"", "", KEYMAP_PATH, NULL};
+ char *postfix[] = {"", ".kbd", NULL};
+
+ cp = getenv("KEYMAP_PATH");
+ if (cp != NULL)
+ asprintf(&(prefix[0]), "%s/", cp);
+
+ fd = NULL;
+ for (i=0; prefix[i] && fd == NULL; i++) {
+ for (j=0; postfix[j] && fd == NULL; j++) {
+ name = mkfullname(prefix[i], opt, postfix[j]);
+ fd = fopen(name, "r");
+ }
+ }
+ if (fd == NULL) {
+ warn("keymap file \"%s\" not found", opt);
+ return;
+ }
+ memset(&keymap, 0, sizeof(keymap));
+ memset(&accentmap, 0, sizeof(accentmap));
+ token = -1;
+ while (1) {
+ if (get_definition_line(fd, &keymap, &accentmap) < 0)
+ break;
+ }
+ if (dumponly) {
+ /* fix up the filename to make it a valid C identifier */
+ for (cp = opt; *cp; cp++)
+ if (!isalpha(*cp) && !isdigit(*cp)) *cp = '_';
+ printf("/*\n"
+ " * Automatically generated from %s.\n"
+ " * DO NOT EDIT!\n"
+ " */\n", name);
+ dump_key_definition(opt, &keymap);
+ dump_accent_definition(opt, &accentmap);
+ return;
+ }
+ if ((keymap.n_keys > 0) && (ioctl(0, PIO_KEYMAP, &keymap) < 0)) {
+ warn("setting keymap");
+ fclose(fd);
+ return;
+ }
+ if ((accentmap.n_accs > 0)
+ && (ioctl(0, PIO_DEADKEYMAP, &accentmap) < 0)) {
+ warn("setting accentmap");
+ fclose(fd);
+ return;
+ }
+}
+
+void
+print_keymap()
+{
+ 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 if (!strcmp(opt, "off"))
+ duration = 0, pitch = 0;
+ else {
+ char *v1;
+
+ bell = 0;
+ duration = strtol(opt, &v1, 0);
+ if ((duration < 0) || (*v1 != '.'))
+ goto badopt;
+ opt = ++v1;
+ pitch = strtol(opt, &v1, 0);
+ if ((pitch < 0) || (*opt == '\0') || (*v1 != '\0')) {
+badopt:
+ warnx("argument to -b must be duration.pitch or [quiet.]visual|normal|off");
+ return;
+ }
+ if (pitch != 0)
+ pitch = 1193182 / pitch; /* in Hz */
+ duration /= 10; /* in 10 m sec */
+ }
+
+ ioctl(0, CONS_BELLTYPE, &bell);
+ if ((bell & ~2) == 0)
+ fprintf(stderr, "[=%d;%dB", pitch, duration);
+}
+
+
+void
+set_keyrates(char *opt)
+{
+ int arg[2];
+ int repeat;
+ int delay;
+ int r, d;
+
+ if (!strcmp(opt, "slow")) {
+ delay = 1000, repeat = 500;
+ d = 3, r = 31;
+ } else if (!strcmp(opt, "normal")) {
+ delay = 500, repeat = 125;
+ d = 1, r = 15;
+ } else if (!strcmp(opt, "fast")) {
+ delay = repeat = 0;
+ d = r = 0;
+ } else {
+ int n;
+ char *v1;
+
+ delay = strtol(opt, &v1, 0);
+ if ((delay < 0) || (*v1 != '.'))
+ goto badopt;
+ opt = ++v1;
+ repeat = strtol(opt, &v1, 0);
+ if ((repeat < 0) || (*opt == '\0') || (*v1 != '\0')) {
+badopt:
+ warnx("argument to -r must be delay.repeat or slow|normal|fast");
+ return;
+ }
+ for (n = 0; n < ndelays - 1; n++)
+ if (delay <= delays[n])
+ break;
+ d = n;
+ for (n = 0; n < nrepeats - 1; n++)
+ if (repeat <= repeats[n])
+ break;
+ r = n;
+ }
+
+ arg[0] = delay;
+ arg[1] = repeat;
+ if (ioctl(0, KDSETREPEAT, arg)) {
+ if (ioctl(0, KDSETRAD, (d << 5) | r))
+ warn("setting keyboard rate");
+ }
+}
+
+static 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",
+ (int)sizeof(info.kb_name), info.kb_name, info.kb_unit,
+ get_kbd_type_name(info.kb_type), info.kb_type);
+}
+
+
+void
+set_keyboard(char *device)
+{
+ keyboard_info_t info;
+ int fd;
+
+ fd = open(device, O_RDONLY);
+ if (fd < 0) {
+ warn("cannot open %s", device);
+ return;
+ }
+ if (ioctl(fd, KDGKBINFO, &info) == -1) {
+ warn("unable to obtain keyboard information");
+ close(fd);
+ return;
+ }
+ /*
+ * The keyboard device driver won't release the keyboard by
+ * the following ioctl, but it automatically will, when the device
+ * is closed. So, we don't check error here.
+ */
+ ioctl(fd, CONS_RELKBD, 0);
+ close(fd);
+#if 1
+ printf("kbd%d\n", info.kb_index);
+ printf(" %.*s%d, type:%s (%d)\n",
+ (int)sizeof(info.kb_name), info.kb_name, info.kb_unit,
+ get_kbd_type_name(info.kb_type), info.kb_type);
+#endif
+
+ if (ioctl(0, CONS_SETKBD, info.kb_index) == -1)
+ warn("unable to set keyboard");
+}
+
+
+void
+release_keyboard(void)
+{
+ keyboard_info_t info;
+
+ /*
+ * If stdin is not associated with a keyboard, the following ioctl
+ * will fail.
+ */
+ if (ioctl(0, KDGKBINFO, &info) == -1) {
+ warn("unable to obtain keyboard information");
+ return;
+ }
+#if 1
+ printf("kbd%d\n", info.kb_index);
+ printf(" %.*s%d, type:%s (%d)\n",
+ (int)sizeof(info.kb_name), info.kb_name, info.kb_unit,
+ get_kbd_type_name(info.kb_type), info.kb_type);
+#endif
+ if (ioctl(0, CONS_RELKBD, 0) == -1)
+ warn("unable to release the keyboard");
+}
+
+
+void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n%s\n",
+"usage: kbdcontrol [-dFKix] [-b duration.pitch | [quiet.]belltype]",
+" [-r delay.repeat | speed] [-l mapfile] [-f # string]",
+" [-k device] [-L mapfile]");
+ exit(1);
+}
+
+
+int
+main(int argc, char **argv)
+{
+ int opt;
+
+ while((opt = getopt(argc, argv, "b:df:iKk:Fl:L:r:x")) != -1)
+ switch(opt) {
+ case 'b':
+ set_bell_values(optarg);
+ break;
+ case 'd':
+ print_keymap();
+ break;
+ case 'l':
+ load_keymap(optarg, 0);
+ break;
+ case 'L':
+ load_keymap(optarg, 1);
+ break;
+ case 'f':
+ set_functionkey(optarg,
+ nextarg(argc, argv, &optind, 'f'));
+ break;
+ case 'F':
+ load_default_functionkeys();
+ break;
+ case 'i':
+ show_kbd_info();
+ break;
+ case 'K':
+ release_keyboard();
+ break;
+ case 'k':
+ set_keyboard(optarg);
+ break;
+ case 'r':
+ set_keyrates(optarg);
+ break;
+ case 'x':
+ hex = 1;
+ break;
+ default:
+ usage();
+ }
+ if ((optind != argc) || (argc == 1))
+ usage();
+ exit(0);
+}
diff --git a/usr.sbin/kbdcontrol/kbdmap.5 b/usr.sbin/kbdcontrol/kbdmap.5
new file mode 100644
index 0000000..bd3c179
--- /dev/null
+++ b/usr.sbin/kbdcontrol/kbdmap.5
@@ -0,0 +1,313 @@
+.\" Copyright (c) 2000
+.\" David Malone
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd September 11, 2000
+.Dt KBDMAP 5
+.Os
+.Sh NAME
+.Nm kbdmap
+.Nd keyboard map file format for kbdcontrol
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+A
+.Nm
+file describes how the keys on a keyboard should behave.
+These files can be loaded
+using
+.Xr kbdcontrol 1 ,
+or
+.Xr kbdmap 1
+can be used to select one of the default
+.Nm
+files interactively.
+A
+.Nm
+file can be specified in
+.Xr rc.conf 5 ,
+to be loaded
+at boot time.
+The current keymap may also be printed using
+.Xr kbdcontrol 1 .
+.Pp
+Each line in the file
+can describe a key or an accent.
+A
+.Ql #
+character begins a comment,
+which extends to the end of the line.
+.Pp
+The description of a key
+begins with the scancode for that key.
+Then the effect of the key
+under combinations of
+shift,
+control
+and alt
+are listed in the following order:
+no modifier,
+shift,
+control,
+control and shift,
+alt,
+alt and shift,
+alt and control,
+alt and control and shift.
+The action of the key
+under each modifier can be:
+.Bl -tag -width Ar
+.It ' Ns Ar symbol Ns No '
+The symbol the key should produce,
+in single quotes.
+.It Ar decnum
+The
+.Tn ASCII
+value to produce
+as a decimal number
+(see
+.Xr ascii 7 ) .
+For example, 32 for space.
+.It 0x Ns Ar hexnum
+The
+.Tn ASCII
+value to produce
+as a hexadecimal number.
+For example, 0x20 for space.
+.It Ar ctrlname
+One of the standard names
+for the
+.Tn ASCII
+control characters:
+nul,
+soh,
+stx,
+etx,
+eot,
+enq,
+ack,
+bel,
+bs,
+ht,
+nl,
+vt,
+np,
+cr,
+so,
+si,
+dle,
+dc1,
+dc2,
+dc3,
+dc4,
+nak,
+syn,
+etb,
+can,
+em,
+sub,
+esc,
+fs,
+gs,
+rs,
+ns,
+us,
+sp,
+del.
+.It Ar accentname
+By giving one of the accent names,
+the next key pressed will produce
+an accented character
+in accordance with that accent.
+See the description of accents below.
+The accent names are:
+dgra,
+dacu,
+dcir,
+dtil,
+dmac,
+dbre,
+ddot,
+duml,
+ddia,
+dsla,
+drin,
+dced,
+dapo,
+ddac,
+dogo,
+dcar.
+.It fkey Ns Ar N
+Act as the
+.Ar N Ns No th
+function key,
+where
+.Ar N
+is a decimal number.
+.It lshift
+Act as left shift key.
+.It rshift
+Act as right shift key.
+.It clock
+Act as caps lock key.
+.It nlock
+Act as num lock key.
+.It slock
+Act as scroll lock key.
+.It lalt|alt
+Act as left alt key.
+.It btab
+Act as backwards tab.
+.It lctrl|ctrl
+Act as left control key.
+.It rctrl
+Act as right control key.
+.It ralt
+Act as right alt (altgr) key.
+.It alock
+Act as alt lock key.
+.It ashift
+Act as alt shift key.
+.It meta
+Act as meta key.
+.It lshifta|shifta
+Act as left shift key / alt lock.
+.It rshifta
+Act as right shift key / alt lock.
+.It lctrla|ctrla
+Act as left ctrl key / alt lock.
+.It rctrla
+Act as right ctrl key / alt lock.
+.It lalta|alta
+Act as left alt key / alt lock.
+.It ralta
+Act as right alt key / alt lock.
+.It nscr
+Act as switch to next screen.
+.It pscr
+Act as switch to previous screen.
+.It scr Ns Ar N
+Switch to screen
+.Ar N ,
+where
+.Ar N
+is a decimal number.
+.It boot
+Reboot the machine.
+.It halt
+Halt the machine.
+.It pdwn
+Halt the machine
+and attempt to power it down.
+.It debug
+Call the debugger.
+.It susp
+Use APM to suspend power.
+.It saver
+Activate screen saver
+by toggling between splash/text screen.
+.It panic
+Panic the system.
+.It paste
+Act as mouse buffer paste.
+.El
+.Pp
+Finally,
+to complete the description of a key,
+a flag which describes
+the effect of caps lock and num lock
+on that key is given.
+The flag can be
+.Ql C
+to indicate that caps lock affects the key,
+.Ql N
+to indicate that num lock affects the key,
+.Ql B
+to indicate that both
+caps lock and num lock affects the key,
+or
+.Ql O
+to indicate that neither affects the key.
+.Pp
+An accent key works
+by modifying the behavior
+of the next key pressed.
+The description of an accent begins
+with one of the accent names
+given above.
+This is followed
+by the symbol for the accent,
+given in single quotes or
+as a decimal or hexadecimal
+.Tn ASCII
+value.
+This symbol will be produced
+if the accent key is pressed and
+then the space key is pressed.
+.Pp
+The description of the accent key
+continues with a list showing
+how it modifies various symbols,
+by giving pairs made up of the normal symbol and
+the modified symbol
+enclosed in parentheses.
+Both symbols in a pair can be given
+in either single quotes or
+as decimal or
+hexadecimal
+.Tn ASCII
+values.
+.Pp
+For example,
+consider the following extract from a
+.Nm :
+.Bd -literal -offset indent
+ 041 dgra 172 nop nop '|' '|' nop nop O
+ dgra '`' ( 'a' 224 ) ( 'A' 192 ) ( 'e' 232 ) ( 'E' 200 )
+ ( 'i' 236 ) ( 'I' 204 ) ( 'o' 242 ) ( 'O' 210 )
+ ( 'u' 249 ) ( 'U' 217 )
+.Ed
+This extract
+configures the backtick key on a UK keyboard
+to act as a grave accent key.
+Pressing backtick followed by space
+produces a backtick, and
+pressing a backtick followed by a vowel
+produces the ISO-8859-1 symbol
+for that vowel with a grave accent.
+.Sh FILES
+.Bl -tag -width /usr/share/syscons/keymaps/* -compact
+.It Pa /usr/share/syscons/keymaps/*
+standard keyboard map files
+.El
+.Sh SEE ALSO
+.Xr kbdcontrol 1 ,
+.Xr kbdmap 1 ,
+.Xr keyboard 4 ,
+.Xr syscons 4 ,
+.Xr ascii 7
+.Sh HISTORY
+This manual page first appeared in
+.Fx 4.2 .
diff --git a/usr.sbin/kbdcontrol/lex.h b/usr.sbin/kbdcontrol/lex.h
new file mode 100644
index 0000000..91e1e54
--- /dev/null
+++ b/usr.sbin/kbdcontrol/lex.h
@@ -0,0 +1,72 @@
+/*-
+ * Copyright (c) 1994-1995 Søren Schmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define TNOP 256
+#define TLSH 257
+#define TRSH 258
+#define TCLK 259
+#define TNLK 260
+#define TSLK 261
+#define TLALT 262
+#define TLCTR 263
+#define TNEXT 264
+#define TRCTR 265
+#define TRALT 266
+#define TALK 267
+#define TASH 268
+#define TMETA 269
+#define TRBT 270
+#define TDBG 271
+#define TFUNC 272
+#define TSCRN 273
+#define TLET 274
+#define TNUM 275
+#define TFLAG 276
+#define TBTAB 277
+#define TSUSP 278
+#define TACC 279
+#define TSPSC 280
+#define TPREV 281
+#define TPANIC 282
+#define TLSHA 283
+#define TRSHA 284
+#define TLCTRA 285
+#define TRCTRA 286
+#define TLALTA 287
+#define TRALTA 288
+#define THALT 289
+#define TPDWN 290
+#define TPASTE 291
+
+extern int number;
+extern char letter;
+extern FILE *yyin;
+
+extern int yylex __P((void));
diff --git a/usr.sbin/kbdcontrol/lex.l b/usr.sbin/kbdcontrol/lex.l
new file mode 100644
index 0000000..67223c1
--- /dev/null
+++ b/usr.sbin/kbdcontrol/lex.l
@@ -0,0 +1,148 @@
+/*-
+ * Copyright (c) 1994-1995 Søren Schmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+%{
+
+#include "lex.h"
+
+%}
+
+D [0-9]
+X [0-9a-fA-F]
+A .
+L [OCNB]
+
+%%
+
+nop { return TNOP; }
+lshift { return TLSH; }
+rshift { return TRSH; }
+clock { return TCLK; }
+nlock { return TNLK; }
+slock { return TSLK; }
+lalt|alt { return TLALT; }
+btab { return TBTAB; }
+lctrl|ctrl { return TLCTR; }
+nscr { return TNEXT; }
+pscr { return TPREV; }
+rctrl { return TRCTR; }
+ralt { return TRALT; }
+alock { return TALK; }
+ashift { return TASH; }
+meta { return TMETA; }
+boot { return TRBT; }
+debug { return TDBG; }
+susp { return TSUSP; }
+saver { return TSPSC; }
+panic { return TPANIC; }
+lshifta|shifta { return TLSHA; }
+rshifta { return TRSHA; }
+lctrla|ctrla { return TLCTRA; }
+rctrla { return TRCTRA; }
+lalta|alta { return TLALTA; }
+ralta { return TRALTA; }
+halt { return THALT; }
+pdwn { return TPDWN; }
+paste { return TPASTE; }
+
+NUL|nul { number = 0; return TNUM; }
+SOH|soh { number = 1; return TNUM; }
+STX|stx { number = 2; return TNUM; }
+ETX|etx { number = 3; return TNUM; }
+EOT|eot { number = 4; return TNUM; }
+ENQ|enq { number = 5; return TNUM; }
+ACK|ack { number = 6; return TNUM; }
+BEL|bel { number = 7; return TNUM; }
+BS|bs { number = 8; return TNUM; }
+HT|ht { number = 9; return TNUM; }
+LF|lf|NL|nl { number = 10; return TNUM; }
+VT|vt { number = 11; return TNUM; }
+FF|ff|NP|np { number = 12; return TNUM; }
+CR|cr { number = 13; return TNUM; }
+SO|so { number = 14; return TNUM; }
+SI|si { number = 15; return TNUM; }
+DLE|dle { number = 16; return TNUM; }
+DC1|dc1 { number = 17; return TNUM; }
+DC2|dc2 { number = 18; return TNUM; }
+DC3|dc3 { number = 19; return TNUM; }
+DC4|dc4 { number = 20; return TNUM; }
+NAK|nak { number = 21; return TNUM; }
+SYN|syn { number = 22; return TNUM; }
+ETB|etb { number = 23; return TNUM; }
+CAN|can { number = 24; return TNUM; }
+EM|em { number = 25; return TNUM; }
+SUB|sub { number = 26; return TNUM; }
+ESC|esc { number = 27; return TNUM; }
+FS|fs { number = 28; return TNUM; }
+GS|gs { number = 29; return TNUM; }
+RS|rs { number = 30; return TNUM; }
+NS|ns { number = 31; return TNUM; }
+US|us { number = 31; return TNUM; }
+SP|sp { number = 32; return TNUM; }
+DEL|del { number = 127; return TNUM; }
+
+dgra|DGRA { number = 0; return TACC; }
+dacu|DACU { number = 1; return TACC; }
+dcir|DCIR { number = 2; return TACC; }
+dtil|DTIL { number = 3; return TACC; }
+dmac|DMAC { number = 4; return TACC; }
+dbre|DBRE { number = 5; return TACC; }
+ddot|DDOT { number = 6; return TACC; }
+duml|DUML { number = 7; return TACC; }
+ddia|DDIA { number = 7; return TACC; }
+dsla|DSLA { number = 8; return TACC; }
+drin|DRIN { number = 9; return TACC; }
+dced|DCED { number = 10; return TACC; }
+dapo|DAPO { number = 11; return TACC; }
+ddac|DDAC { number = 12; return TACC; }
+dogo|DOGO { number = 13; return TACC; }
+dcar|DCAR { number = 14; return TACC; }
+
+fkey{D}({D}*) {
+ sscanf(yytext+4, "%d", &number);
+ return TFUNC;
+ }
+scr{D}({D}*) {
+ sscanf(yytext+3, "%d", &number);
+ return TSCRN;
+ }
+'{A}' { letter = *(yytext+1); return TLET; }
+#({A}*) { /* ignore */ }
+0x{X}({X}*) { sscanf(yytext, "%x", &number); return TNUM; }
+{D}({D}*) { sscanf(yytext, "%d", &number); return TNUM; }
+{L} {
+ if (*yytext == 'O') number = 0;
+ if (*yytext == 'C') number = 1;
+ if (*yytext == 'N') number = 2;
+ if (*yytext == 'B') number = 3;
+ return TFLAG;
+ }
+[ \t\n] { /* ignore */ }
+. { return *yytext; }
diff --git a/usr.sbin/kbdcontrol/path.h b/usr.sbin/kbdcontrol/path.h
new file mode 100644
index 0000000..709acbc
--- /dev/null
+++ b/usr.sbin/kbdcontrol/path.h
@@ -0,0 +1,4 @@
+#define KEYMAP_PATH "/usr/share/syscons/keymaps/"
+#define FONT_PATH "/usr/share/syscons/fonts/"
+#define SCRNMAP_PATH "/usr/share/syscons/scrnmaps/"
+
diff --git a/usr.sbin/kbdmap/Languages.phrases b/usr.sbin/kbdmap/Languages.phrases
new file mode 100644
index 0000000..8bf7922
--- /dev/null
+++ b/usr.sbin/kbdmap/Languages.phrases
@@ -0,0 +1,37 @@
+#
+# Please use only iso-latin character set, which available
+# at console *and* X11 (IBM cp850 does not)
+#
+# missing: Norwegian, Russian, Danish, Swedish
+#
+
+1. Chose your keyboard font
+
+2. Codepage
+
+3. English
+
+4. thin
+
+5. Multilingual Latin I
+
+6. Norwegian
+
+7. Russian
+
+8. West European
+
+9. Chose your keyboard language
+
+10. Danish
+
+11. French
+
+12. German
+
+13. Swedish
+
+14. United Kingdom
+
+15. United States of America
+
diff --git a/usr.sbin/kbdmap/Makefile b/usr.sbin/kbdmap/Makefile
new file mode 100644
index 0000000..38d0212
--- /dev/null
+++ b/usr.sbin/kbdmap/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= kbdmap
+LINKS= ${BINDIR}/kbdmap ${BINDIR}/vidfont
+MAN= kbdmap.1
+MLINKS= kbdmap.1 vidfont.1
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/kbdmap/TODO b/usr.sbin/kbdmap/TODO
new file mode 100644
index 0000000..cef30f7
--- /dev/null
+++ b/usr.sbin/kbdmap/TODO
@@ -0,0 +1,6 @@
+$FreeBSD$
+
+o remember some hackers to translate Languages.phrases into
+ Norwegian, Russian, Danish, Swedish
+
+95/04/03 Wolfram
diff --git a/usr.sbin/kbdmap/kbdmap.1 b/usr.sbin/kbdmap/kbdmap.1
new file mode 100644
index 0000000..d3cc094
--- /dev/null
+++ b/usr.sbin/kbdmap/kbdmap.1
@@ -0,0 +1,152 @@
+.\" Copyright (c) March 1995 Wolfram Schneider <wosch@FreeBSD.org>. Berlin.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.Dd March 25, 1995
+.Dt KBDMAP 1
+.Os
+.Sh NAME
+.Nm kbdmap ,
+.Nm vidfont
+.Nd front end for syscons
+.Sh SYNOPSIS
+.Nm
+.Op Fl K
+.Op Fl V
+.Op Fl d | Fl default
+.Op Fl h | Fl help
+.Op Fl l | Fl lang Ar language
+.Op Fl p | Fl print
+.Op Fl r | Fl restore
+.Op Fl s | Fl show
+.Op Fl v | Fl verbose
+.Sh DESCRIPTION
+The
+.Nm
+utility allows easy setting of available keymaps.
+The
+.Nm vidfont
+command allows the setting of fonts.
+Both examine a database for the keymaps and fonts.
+Descriptions are in English by default, but may also be
+in other languages.
+These programs are interactive and expect to run
+in a terminal to get required input (e.g., a keymap selection)
+from the user.
+.Pp
+It is strongly recommended to not choose
+.Tn MSDOS
+codepage keymaps
+or fonts.
+Use the
+.Tn ISO
+standard version if available!
+.Tn X11
+does not
+support
+.Tn MSDOS
+codepage.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl K
+Run as command
+.Nm .
+.It Fl V
+Run as command
+.Nm vidfont .
+.It Fl d , Fl default
+Use default language.
+Ignore
+.Ev LANG
+environment variable.
+.It Fl h , Fl help
+Print options and exit.
+.It Fl l , Fl lang Ar language
+Use
+.Ar language
+for description and menu
+.It Fl p , Fl print
+Print description of available keymaps or fonts
+to stdout and exit.
+.It Fl r , Fl restore
+Load default font from
+.Pa /etc/rc.conf .
+.It Fl s , Fl show
+Show currently supported languages and exit.
+.It Fl v , Fl verbose
+More warnings.
+.El
+.Sh ENVIRONMENT
+.Bl -tag -width LANG -compact
+.It Ev LANG
+preferred language
+.El
+.Sh FILES
+.Bl -tag -width /usr/share/syscons/keymaps/INDEX.keymaps -compact
+.It Pa /usr/share/syscons/keymaps/INDEX.keymaps
+database for keymaps
+.It Pa /usr/share/syscons/fonts/INDEX.fonts
+database for fonts
+.It Pa /etc/rc.conf
+default font
+.It Pa /usr/X11/lib/X11/locale/locale.alias
+describe common
+.Ev LANG
+values
+.El
+.Sh BUGS
+.\" .Nm kbdmap/vidfont
+.\" does not know which font is in use. E.g. if the current font
+.\" is iso-8859-1 and you chose lang 'ru' (for Russian)
+.\" you get funny latin1 characters and not russkij shrift.
+.\"
+The
+.Nm
+and
+.Nm vidfont
+utilities work only on a (virtual) console and not with
+.Tn X11 .
+.Sh SEE ALSO
+.Xr dialog 1 ,
+.Xr kbdcontrol 1 ,
+.Xr vidcontrol 1 ,
+.Xr kbdmap 5 ,
+.Xr rc.conf 5
+.Sh HISTORY
+The
+.Nm
+and
+.Nm vidfont
+commands appeared in
+.Fx 2.1 .
+.Sh AUTHORS
+.An Wolfram Schneider
+.Aq wosch@FreeBSD.org
+wrote the original Perl version.
+The current version was rewritten in C by
+.An Jonathan Belson
+.Aq jon@witchspace.com
+for
+.Fx 5.0 .
diff --git a/usr.sbin/kbdmap/kbdmap.c b/usr.sbin/kbdmap/kbdmap.c
new file mode 100644
index 0000000..423a13c
--- /dev/null
+++ b/usr.sbin/kbdmap/kbdmap.c
@@ -0,0 +1,842 @@
+/*-
+ * Copyright (c) 2002 Jonathan Belson <jon@witchspace.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/syslimits.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stringlist.h>
+#include <unistd.h>
+
+#include "kbdmap.h"
+
+
+static const char *lang_default = DEFAULT_LANG;
+static const char *font;
+static const char *lang;
+static const char *program;
+static const char *keymapdir = DEFAULT_KEYMAP_DIR;
+static const char *fontdir = DEFAULT_FONT_DIR;
+static const char *sysconfig = DEFAULT_SYSCONFIG;
+static const char *font_default = DEFAULT_FONT;
+static const char *font_current;
+static const char *dir;
+static const char *menu = "";
+
+static int x11;
+static int show;
+static int verbose;
+static int print;
+
+
+struct keymap {
+ char *desc;
+ char *keym;
+ int mark;
+ SLIST_ENTRY(keymap) entries;
+};
+static SLIST_HEAD(slisthead, keymap) head = SLIST_HEAD_INITIALIZER(head);
+
+
+/*
+ * Get keymap entry for 'key', or NULL of not found
+ */
+static struct keymap *
+get_keymap(const char *key)
+{
+ struct keymap *km;
+
+ SLIST_FOREACH(km, &head, entries)
+ if (!strcmp(km->keym, key))
+ return km;
+
+ return NULL;
+}
+
+/*
+ * Count the number of keymaps we found
+ */
+static int
+get_num_keymaps(void)
+{
+ struct keymap *km;
+ int count = 0;
+
+ SLIST_FOREACH(km, &head, entries)
+ count++;
+
+ return count;
+}
+
+/*
+ * Remove any keymap with given keym
+ */
+static void
+remove_keymap(const char *keym)
+{
+ struct keymap *km;
+
+ SLIST_FOREACH(km, &head, entries) {
+ if (!strcmp(keym, km->keym)) {
+ SLIST_REMOVE(&head, km, keymap, entries);
+ free(km);
+ break;
+ }
+ }
+}
+
+/*
+ * Add to hash with 'key'
+ */
+static void
+add_keymap(const char *desc, int mark, const char *keym)
+{
+ struct keymap *km, *km_new;
+
+ /* Is there already an entry with this key? */
+ SLIST_FOREACH(km, &head, entries) {
+ if (!strcmp(km->keym, keym)) {
+ /* Reuse this entry */
+ free(km->desc);
+ km->desc = strdup(desc);
+ km->mark = mark;
+ return;
+ }
+ }
+
+ km_new = (struct keymap *) malloc (sizeof(struct keymap));
+ km_new->desc = strdup(desc);
+ km_new->keym = strdup(keym);
+ km_new->mark = mark;
+
+ /* Add to keymap list */
+ SLIST_INSERT_HEAD(&head, km_new, entries);
+}
+
+/*
+ * Figure out the default language to use.
+ */
+static const char *
+get_locale(void)
+{
+ const char *locale;
+
+ if ((locale = getenv("LC_ALL")) == NULL &&
+ (locale = getenv("LC_CTYPE")) == NULL &&
+ (locale = getenv("LANG")) == NULL)
+ locale = lang_default;
+
+ /* Check for alias */
+ if (!strcmp(locale, "C"))
+ locale = DEFAULT_LANG;
+
+ return locale;
+}
+
+/*
+ * Extract filename part
+ */
+static const char *
+extract_name(const char *name)
+{
+ char *p;
+
+ p = strrchr(name, '/');
+ if (p != NULL && p[1] != '\0')
+ return p + 1;
+
+ return name;
+}
+
+/*
+ * Return file extension or NULL
+ */
+static char *
+get_extension(const char *name)
+{
+ char *p;
+
+ p = strrchr(name, '.');
+
+ if (p != NULL && p[1] != '\0')
+ return p;
+
+ return NULL;
+}
+
+/*
+ * Read font from /etc/rc.conf else return default.
+ * Freeing the memory is the caller's responsibility.
+ */
+static char *
+get_font(void)
+{
+ char line[256], buf[20];
+ char *fnt = NULL;
+
+ FILE *fp = fopen(sysconfig, "r");
+ if (fp) {
+ while (fgets(line, sizeof(line), fp)) {
+ int a, b, matches;
+
+ if (line[0] == '#')
+ continue;
+
+ matches = sscanf(line,
+ " font%dx%d = \"%20[-.0-9a-zA-Z_]",
+ &a, &b, buf);
+ if (matches==3) {
+ if (strcmp(buf, "NO")) {
+ if (fnt)
+ free(fnt);
+ fnt = (char *) malloc(strlen(buf) + 1);
+ strcpy(fnt, buf);
+ }
+ }
+ }
+ } else
+ fprintf(stderr, "Could not open %s for reading\n", sysconfig);
+
+ return fnt;
+}
+
+/*
+ * Set a font using 'vidcontrol'
+ */
+static void
+vidcontrol(const char *fnt)
+{
+ char *tmp, *p, *q;
+ char ch;
+ int i;
+
+ /* syscons test failed */
+ if (x11)
+ return;
+
+ tmp = strdup(fnt);
+
+ /* Extract font size */
+ p = strrchr(tmp, '-');
+ if (p && p[1] != '\0') {
+ p++;
+ /* Remove any '.fnt' extension */
+ if ((q = strstr(p, ".fnt")))
+ *q = '\0';
+
+ /*
+ * Check font size is valid, with no trailing characters
+ * ('&ch' should not be matched)
+ */
+ if (sscanf(p, "%dx%d%c", &i, &i, &ch) != 2)
+ fprintf(stderr, "Which font size? %s\n", fnt);
+ else {
+ char *cmd;
+ asprintf(&cmd, "vidcontrol -f %s %s", p, fnt);
+ if (verbose)
+ fprintf(stderr, "%s\n", cmd);
+ system(cmd);
+ free(cmd);
+ }
+ } else
+ fprintf(stderr, "Which font size? %s\n", fnt);
+
+ free(tmp);
+}
+
+/*
+ * Execute 'kbdcontrol' with the appropriate arguments
+ */
+static void
+do_kbdcontrol(struct keymap *km)
+{
+ char *kbd_cmd;
+ asprintf(&kbd_cmd, "kbdcontrol -l %s/%s", dir, km->keym);
+
+ if (!x11)
+ system(kbd_cmd);
+
+ printf("keymap=%s\n", km->keym);
+ free(kbd_cmd);
+}
+
+/*
+ * Call 'vidcontrol' with the appropriate arguments
+ */
+static void
+do_vidfont(struct keymap *km)
+{
+ char *vid_cmd, *tmp, *p, *q;
+
+ asprintf(&vid_cmd, "%s/%s", dir, km->keym);
+ vidcontrol(vid_cmd);
+ free(vid_cmd);
+
+ tmp = strdup(km->keym);
+ p = strrchr(tmp, '-');
+ if (p && p[1]!='\0') {
+ p++;
+ q = get_extension(p);
+ if (q) {
+ *q = '\0';
+ printf("font%s=%s\n", p, km->keym);
+ }
+ }
+ free(tmp);
+}
+
+/*
+ * Display dialog from 'keymaps[]'
+ */
+static void
+show_dialog(struct keymap **km_sorted, int num_keymaps)
+{
+ FILE *fp;
+ char *cmd, *dialog;
+ char tmp_name[] = "/tmp/_kbd_lang.XXXX";
+ const char *ext;
+ int fd, i, size;
+
+ fd = mkstemp(tmp_name);
+ if (fd == -1) {
+ fprintf(stderr, "Could not open temporary file \"%s\"\n",
+ tmp_name);
+ exit(1);
+ }
+ asprintf(&dialog, "/usr/bin/dialog --clear --title \"Keyboard Menu\" "
+ "--menu \"%s\" -1 -1 10", menu);
+
+ ext = extract_name(dir);
+
+ /* start right font, assume that current font is equal
+ * to default font in /etc/rc.conf
+ *
+ * $font is the font which require the language $lang; e.g.
+ * russian *need* a koi8 font
+ * $font_current is the current font from /etc/rc.conf
+ */
+ if (font && strcmp(font, font_current))
+ vidcontrol(font);
+
+ /* Build up the command */
+ size = 0;
+ for (i=0; i<num_keymaps; i++) {
+ /*
+ * Each 'font' is passed as ' "font" ""', so allow the
+ * extra space
+ */
+ size += strlen(km_sorted[i]->desc) + 6;
+ }
+
+ /* Allow the space for '2> tmpfilename' redirection */
+ size += strlen(tmp_name) + 3;
+
+ cmd = (char *) malloc(strlen(dialog) + size + 1);
+ strcpy(cmd, dialog);
+
+ for (i=0; i<num_keymaps; i++) {
+ strcat(cmd, " \"");
+ strcat(cmd, km_sorted[i]->desc);
+ strcat(cmd, "\"");
+ strcat(cmd, " \"\"");
+ }
+
+ strcat(cmd, " 2>");
+ strcat(cmd, tmp_name);
+
+ /* Show the dialog.. */
+ system(cmd);
+
+ fp = fopen(tmp_name, "r");
+ if (fp) {
+ char choice[64];
+ if (fgets(choice, 64, fp) != NULL) {
+ /* Find key for desc */
+ for (i=0; i<num_keymaps; i++) {
+ if (!strcmp(choice, km_sorted[i]->desc)) {
+ if (!strcmp(program, "kbdmap"))
+ do_kbdcontrol(km_sorted[i]);
+ else
+ do_vidfont(km_sorted[i]);
+ break;
+ }
+ }
+ } else {
+ if (font != NULL && strcmp(font, font_current))
+ /* Cancelled, restore old font */
+ vidcontrol(font_current);
+ }
+ fclose(fp);
+ } else
+ fprintf(stderr, "Failed to open temporary file");
+
+ /* Tidy up */
+ remove(tmp_name);
+ free(cmd);
+ free(dialog);
+ close(fd);
+}
+
+/*
+ * Search for 'token' in comma delimited array 'buffer'.
+ * Return true for found, false for not found.
+ */
+static int
+find_token(const char *buffer, const char *token)
+{
+ char *buffer_tmp, *buffer_copy, *inputstring;
+ char **ap;
+ int found;
+
+ buffer_copy = strdup(buffer);
+ buffer_tmp = buffer_copy;
+ inputstring = buffer_copy;
+ ap = &buffer_tmp;
+
+ found = 0;
+
+ while ((*ap = strsep(&inputstring, ",")) != NULL) {
+ if (strcmp(buffer_tmp, token) == 0) {
+ found = 1;
+ break;
+ }
+ }
+
+ free(buffer_copy);
+
+ return found;
+}
+
+/*
+ * Compare function for qsort
+ */
+static int
+compare_keymap(const void *a, const void *b)
+{
+
+ /* We've been passed pointers to pointers, so: */
+ const struct keymap *km1 = *((const struct keymap * const *) a);
+ const struct keymap *km2 = *((const struct keymap * const *) b);
+
+ return strcmp(km1->desc, km2->desc);
+}
+
+/*
+ * Compare function for qsort
+ */
+static int
+compare_lang(const void *a, const void *b)
+{
+ const char *l1 = *((const char * const *) a);
+ const char *l2 = *((const char * const *) b);
+
+ return strcmp(l1, l2);
+}
+
+/*
+ * Change '8x8' to '8x08' so qsort will put it before eg. '8x14'
+ */
+static void
+kludge_desc(struct keymap **km_sorted, int num_keymaps)
+{
+ int i;
+
+ for (i=0; i<num_keymaps; i++) {
+ char *p;
+ char *km = km_sorted[i]->desc;
+ if ((p = strstr(km, "8x8")) != NULL) {
+ int len;
+ int j;
+ int offset;
+
+ offset = p - km;
+
+ /* Make enough space for the extra '0' */
+ len = strlen(km);
+ km = realloc(km, len + 2);
+
+ for (j=len; j!=offset+1; j--)
+ km[j + 1] = km[j];
+
+ km[offset+2] = '0';
+
+ km_sorted[i]->desc = km;
+ }
+ }
+}
+
+/*
+ * Reverse 'kludge_desc()' - change '8x08' back to '8x8'
+ */
+static void
+unkludge_desc(struct keymap **km_sorted, int num_keymaps)
+{
+ int i;
+
+ for (i=0; i<num_keymaps; i++) {
+ char *p;
+ char *km = km_sorted[i]->desc;
+ if ((p = strstr(km, "8x08")) != NULL) {
+ p += 2;
+ while (*p++)
+ p[-1] = p[0];
+
+ km = realloc(km, p - km - 1);
+ km_sorted[i]->desc = km;
+ }
+ }
+}
+
+/*
+ * Return 0 if file exists and is readable, else -1
+ */
+static int
+check_file(const char *keym)
+{
+ int status = 0;
+
+ if (access(keym, R_OK) == -1) {
+ char *fn;
+ asprintf(&fn, "%s/%s", dir, keym);
+ if (access(fn, R_OK) == -1) {
+ if (verbose)
+ fprintf(stderr, "%s not found!\n", fn);
+ status = -1;
+ }
+ free(fn);
+ } else {
+ if (verbose)
+ fprintf(stderr, "No read permission for %s!\n", keym);
+ status = -1;
+ }
+
+ return status;
+}
+
+/*
+ * Read options from the relevent configuration file, then
+ * present to user.
+ */
+static void
+menu_read(void)
+{
+ const char *lg;
+ char *p;
+ int mark, num_keymaps, items, i;
+ char buffer[256], filename[PATH_MAX];
+ char keym[64], lng[64], desc[64];
+ char dialect[64], lang_abk[64];
+ struct keymap *km;
+ struct keymap **km_sorted;
+ struct dirent *dp;
+ StringList *lang_list;
+ FILE *fp;
+ DIR *dirp;
+
+ lang_list = sl_init();
+
+ sprintf(filename, "%s/INDEX.%s", dir, extract_name(dir));
+
+ /* en_US.ISO8859-1 -> en_..\.ISO8859-1 */
+ strlcpy(dialect, lang, sizeof(dialect));
+ if (strlen(dialect) >= 6 && dialect[2] == '-') {
+ dialect[3] = '.';
+ dialect[4] = '.';
+ }
+
+
+ /* en_US.ISO8859-1 -> en */
+ strlcpy(lang_abk, lang, sizeof(lang_abk));
+ if (strlen(lang_abk) >= 3 && lang_abk[2] == '-')
+ lang_abk[2] = '.';
+
+ fprintf(stderr, "lang_default = %s\n", lang_default);
+ fprintf(stderr, "dialect = %s\n", dialect);
+ fprintf(stderr, "lang_abk = %s\n", lang_abk);
+
+ fp = fopen(filename, "r");
+ if (fp) {
+ int matches;
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ p = buffer;
+ if (p[0] == '#')
+ continue;
+
+ while (isspace(*p))
+ p++;
+
+ if (*p == '\0')
+ continue;
+
+ /* Parse input, removing newline */
+ matches = sscanf(p, "%64[^:]:%64[^:]:%64[^:\n]",
+ keym, lng, desc);
+ if (matches == 3) {
+ if (strcmp(keym, "FONT")
+ && strcmp(keym, "MENU")) {
+ /* Check file exists & is readable */
+ if (check_file(keym) == -1)
+ continue;
+ }
+ }
+
+ if (show) {
+ /*
+ * Take note of supported languages, which
+ * might be in a comma-delimited list
+ */
+ char *tmp = strdup(lng);
+ char *delim = tmp;
+
+ for (delim = tmp; ; ) {
+ char ch = *delim++;
+ if (ch == ',' || ch == '\0') {
+ delim[-1] = '\0';
+ if (!sl_find(lang_list, tmp))
+ sl_add(lang_list, tmp);
+ if (ch == '\0')
+ break;
+ tmp = delim;
+ }
+ }
+ }
+ /* Set empty language to default language */
+ if (lng[0] == '\0')
+ lg = lang_default;
+ else
+ lg = lng;
+
+
+ /* 4) Your choice if it exists
+ * 3) Long match eg. en_GB.ISO8859-1 is equal to
+ * en_..\.ISO8859-1
+ * 2) short match 'de'
+ * 1) default langlist 'en'
+ * 0) any language
+ *
+ * Language may be a comma separated list
+ * A higher match overwrites a lower
+ * A later entry overwrites a previous if it exists
+ * twice in the database
+ */
+
+ /* Check for favoured language */
+ km = get_keymap(keym);
+ mark = (km) ? km->mark : 0;
+
+ if (find_token(lg, lang))
+ add_keymap(desc, 4, keym);
+ else if (mark <= 3 && find_token(lg, dialect))
+ add_keymap(desc, 3, keym);
+ else if (mark <= 2 && find_token(lg, lang_abk))
+ add_keymap(desc, 2, keym);
+ else if (mark <= 1 && find_token(lg, lang_default))
+ add_keymap(desc, 1, keym);
+ else if (mark <= 0)
+ add_keymap(desc, 0, keym);
+ }
+ fclose(fp);
+
+ } else
+ printf("Could not open file\n");
+
+ if (show) {
+ qsort(lang_list->sl_str, lang_list->sl_cur, sizeof(char*),
+ compare_lang);
+ printf("Currently supported languages: ");
+ for (i=0; i< (int) lang_list->sl_cur; i++)
+ printf("%s ", lang_list->sl_str[i]);
+ puts("");
+ exit(0);
+ }
+
+ km = get_keymap("MENU");
+ if (km)
+ /* Take note of menu title */
+ menu = strdup(km->desc);
+ km = get_keymap("FONT");
+ if (km)
+ /* Take note of language font */
+ font = strdup(km->desc);
+
+ /* Remove unwanted items from list */
+ remove_keymap("MENU");
+ remove_keymap("FONT");
+
+ /* Look for keymaps not in database */
+ dirp = opendir(dir);
+ if (dirp) {
+ while ((dp = readdir(dirp)) != NULL) {
+ const char *ext = get_extension(dp->d_name);
+ if (ext) {
+ if ((!strcmp(ext, ".fnt") ||
+ !strcmp(ext, ".kbd")) &&
+ !get_keymap(dp->d_name)) {
+ char *q;
+
+ /* Remove any .fnt or .kbd extension */
+ q = strdup(dp->d_name);
+ *(get_extension(q)) = '\0';
+ add_keymap(q, 0, dp->d_name);
+ free(q);
+
+ if (verbose)
+ fprintf(stderr,
+ "'%s' not in database\n",
+ dp->d_name);
+ }
+ }
+ }
+ closedir(dirp);
+ } else
+ fprintf(stderr, "Could not open directory '%s'\n", dir);
+
+ /* Sort items in keymap */
+ num_keymaps = get_num_keymaps();
+
+ km_sorted = (struct keymap **)
+ malloc(num_keymaps*sizeof(struct keymap *));
+
+ /* Make array of pointers to items in hash */
+ items = 0;
+ SLIST_FOREACH(km, &head, entries)
+ km_sorted[items++] = km;
+
+ /* Change '8x8' to '8x08' so sort works as we might expect... */
+ kludge_desc(km_sorted, num_keymaps);
+
+ qsort(km_sorted, num_keymaps, sizeof(struct keymap *), compare_keymap);
+
+ /* ...change back again */
+ unkludge_desc(km_sorted, num_keymaps);
+
+ if (print) {
+ for (i=0; i<num_keymaps; i++)
+ printf("%s\n", km_sorted[i]->desc);
+ exit(0);
+ }
+
+ show_dialog(km_sorted, num_keymaps);
+
+ free(km_sorted);
+}
+
+/*
+ * Display usage information and exit
+ */
+static void
+usage(void)
+{
+
+ fprintf(stderr, "usage: %s\t[-K] [-V] [-d|-default] [-h|-help] "
+ "[-l|-lang language]\n\t\t[-p|-print] [-r|-restore] [-s|-show] "
+ "[-v|-verbose]\n", program);
+ exit(1);
+}
+
+static void
+parse_args(int argc, char **argv)
+{
+ int i;
+
+ for (i=1; i<argc; i++) {
+ if (argv[i][0] != '-')
+ usage();
+ else if (!strcmp(argv[i], "-help") || !strcmp(argv[i], "-h"))
+ usage();
+ else if (!strcmp(argv[i], "-verbose") || !strcmp(argv[i], "-v"))
+ verbose = 1;
+ else if (!strcmp(argv[i], "-lang") || !strcmp(argv[i], "-l"))
+ if (i + 1 == argc)
+ usage();
+ else
+ lang = argv[++i];
+ else if (!strcmp(argv[i], "-default") || !strcmp(argv[i], "-d"))
+ lang = lang_default;
+ else if (!strcmp(argv[i], "-show") || !strcmp(argv[i], "-s"))
+ show = 1;
+ else if (!strcmp(argv[i], "-print") || !strcmp(argv[i], "-p"))
+ print = 1;
+ else if (!strcmp(argv[i], "-restore") ||
+ !strcmp(argv[i], "-r")) {
+ vidcontrol(font_current);
+ exit(0);
+ } else if (!strcmp(argv[i], "-K"))
+ dir = keymapdir;
+ else if (!strcmp(argv[i], "-V"))
+ dir = fontdir;
+ else
+ usage();
+ }
+}
+
+/*
+ * A front-end for the 'vidfont' and 'kbdmap' programs.
+ */
+int
+main(int argc, char **argv)
+{
+
+ x11 = system("kbdcontrol -d >/dev/null");
+
+ if (x11) {
+ fprintf(stderr, "You are not on a virtual console - "
+ "expect certain strange side-effects\n");
+ sleep(2);
+ }
+
+ SLIST_INIT(&head);
+
+ lang = get_locale();
+
+ program = extract_name(argv[0]);
+
+ /* Parse command line arguments */
+ parse_args(argc, argv);
+
+ font_current = get_font();
+ if (font_current == NULL)
+ font_current = font_default;
+
+ if (strcmp(program, "kbdmap"))
+ dir = fontdir;
+ else
+ dir = keymapdir;
+
+ /* Read and display options */
+ menu_read();
+
+ return 0;
+}
diff --git a/usr.sbin/kbdmap/kbdmap.h b/usr.sbin/kbdmap/kbdmap.h
new file mode 100644
index 0000000..e57fa8c
--- /dev/null
+++ b/usr.sbin/kbdmap/kbdmap.h
@@ -0,0 +1,34 @@
+/*-
+ * Copyright (c) 2002 Jonathan Belson <jon@witchspace.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+
+#define DEFAULT_LANG "en"
+#define DEFAULT_KEYMAP_DIR "/usr/share/syscons/keymaps"
+#define DEFAULT_FONT_DIR "/usr/share/syscons/fonts"
+#define DEFAULT_SYSCONFIG "/etc/rc.conf"
+#define DEFAULT_FONT "cp437-8x16.fnt"
diff --git a/usr.sbin/kernbb/Makefile b/usr.sbin/kernbb/Makefile
new file mode 100644
index 0000000..1947305
--- /dev/null
+++ b/usr.sbin/kernbb/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+PROG= kernbb
+MAN= 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..fa3b287
--- /dev/null
+++ b/usr.sbin/kernbb/kernbb.8
@@ -0,0 +1,72 @@
+.\" Copyright (c) 1983, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd May 22, 1995
+.Dt KERNBB 8
+.Os
+.Sh NAME
+.Nm kernbb
+.Nd generate a dump of the kernels basic-block profile buffers
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+.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
+.Tn 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 /boot/kernel/kernel -compact
+.It Pa /boot/kernel/kernel
+the default system
+.It Pa /dev/kmem
+the default memory
+.El
+.Sh SEE ALSO
+.Xr cc 1
+.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..ed3b372
--- /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[] =
+ "$FreeBSD$";
+#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,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..c068d89
--- /dev/null
+++ b/usr.sbin/keyadmin/Makefile
@@ -0,0 +1,7 @@
+# $ANA: Makefile,v 1.2 1996/06/13 20:11:05 wollman Exp $
+# $FreeBSD$
+
+PROG= keyadmin
+MAN= 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..4b3f374
--- /dev/null
+++ b/usr.sbin/keyadmin/keyadmin.8
@@ -0,0 +1,246 @@
+.\"# @(#)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 $
+.\" $FreeBSD$
+.\"
+.Dd June 13, 1996
+.Dt KEY 8
+.Os
+.Sh NAME
+.Nm keyadmin
+.Nd manually manipulate the kernel key management database
+.Sh SYNOPSIS
+.Nm
+.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 .
+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 Ic 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 Ic 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 Ic dump
+.Pp
+Display the entire security association table. WARNING: This prints a lot
+of data.
+.It Ic 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 Ic 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
+.Ic dump
+command.)
+NOTE: The
+.Ic 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 Ic help Op command
+.Pp
+Offer brief help without an argument, or slightly more specific help on a
+particular command.
+.It Ic 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 Ic 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 malicious user
+of the
+.Xr ps 1
+command could determine security keys if
+.Ic 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 Ic exit
+.It Ic quit
+.Pp
+Exit interaction with
+.Nm .
+An EOF will also end interaction with
+.Nm .
+.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
+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
+.Ic dump
+and
+.Ic 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..170fa64
--- /dev/null
+++ b/usr.sbin/keyserv/Makefile
@@ -0,0 +1,27 @@
+# $FreeBSD$
+
+PROG= keyserv
+MAN= keyserv.8
+SRCS= keyserv.c setkey.c crypt_svc.c crypt_server.c crypt.h
+
+CFLAGS+= -DKEYSERV_RANDOM -DBROKEN_DES -I.
+.if $(OBJFORMAT) == elf
+CFLAGS+= -DOBJFORMAT_ELF
+.endif
+
+DPADD= ${LIBMP} ${LIBCRYPTO} ${LIBRPCSVC}
+LDADD= -lmp -lcrypto -lrpcsvc
+
+RPCDIR= ${DESTDIR}/usr/include/rpcsvc
+
+CLEANFILES= crypt_svc.c crypt.h
+
+RPCGEN= rpcgen -C
+
+crypt_svc.c: ${RPCDIR}/crypt.x
+ ${RPCGEN} -m -o ${.TARGET} ${RPCDIR}/crypt.x
+
+crypt.h: ${RPCDIR}/crypt.x
+ ${RPCGEN} -h -o ${.TARGET} ${RPCDIR}/crypt.x
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/keyserv/crypt_server.c b/usr.sbin/keyserv/crypt_server.c
new file mode 100644
index 0000000..47ffa8f
--- /dev/null
+++ b/usr.sbin/keyserv/crypt_server.c
@@ -0,0 +1,305 @@
+/*
+ * Copyright (c) 1996
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <dirent.h>
+#include <dlfcn.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <rpc/des_crypt.h>
+#include <rpc/des.h>
+#include "crypt.h"
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/*
+ * The U.S. government stupidly believes that a) it can keep strong
+ * crypto code a secret and b) that doing so somehow protects national
+ * interests. It's wrong on both counts, but until it listens to reason
+ * we have to make certain compromises so it doesn't have an excuse to
+ * throw us in federal prison.
+ *
+ * Consequently, the core OS ships without DES support, and keyserv
+ * defaults to using ARCFOUR with only a 40 bit key, just like nutscrape.
+ * This breaks compatibility with Secure RPC on other systems, but it
+ * allows Secure RPC to work between FreeBSD systems that don't have the
+ * DES package installed without throwing security totally out the window.
+ *
+ * In order to avoid having to supply two versions of keyserv (one with
+ * DES and one without), we use dlopen() and friends to load libdes.so
+ * into our address space at runtime. We check for the presence of
+ * /usr/lib/libdes.so.3.0 at startup and load it if we find it. If we
+ * can't find it, or the __des_crypt symbol doesn't exist, we fall back
+ * to the ARCFOUR encryption code. The user can specify another path using
+ * the -p flag.
+ */
+
+ /* arcfour.h */
+typedef struct arcfour_key
+{
+ unsigned char state[256];
+ unsigned char x;
+ unsigned char y;
+} arcfour_key;
+
+static void prepare_key(unsigned char *key_data_ptr,int key_data_len,
+ arcfour_key *key);
+static void arcfour(unsigned char *buffer_ptr,int buffer_len,arcfour_key * key);
+static void swap_byte(unsigned char *a, unsigned char *b);
+
+static void prepare_key(unsigned char *key_data_ptr, int key_data_len,
+ arcfour_key *key)
+{
+ unsigned char index1;
+ unsigned char index2;
+ unsigned char* state;
+ short counter;
+
+ state = &key->state[0];
+ for(counter = 0; counter < 256; counter++)
+ state[counter] = counter;
+ key->x = 0;
+ key->y = 0;
+ index1 = 0;
+ index2 = 0;
+ for(counter = 0; counter < 256; counter++)
+ {
+ index2 = (key_data_ptr[index1] + state[counter] +
+ index2) % 256;
+ swap_byte(&state[counter], &state[index2]);
+
+ index1 = (index1 + 1) % key_data_len;
+ }
+}
+
+static void arcfour(unsigned char *buffer_ptr, int buffer_len, arcfour_key *key)
+{
+ unsigned char x;
+ unsigned char y;
+ unsigned char* state;
+ unsigned char xorIndex;
+ short counter;
+
+ x = key->x;
+ y = key->y;
+
+ state = &key->state[0];
+ for(counter = 0; counter < buffer_len; counter ++)
+ {
+ x = (x + 1) % 256;
+ y = (state[x] + y) % 256;
+ swap_byte(&state[x], &state[y]);
+
+ xorIndex = (state[x] + state[y]) % 256;
+
+ buffer_ptr[counter] ^= state[xorIndex];
+ }
+ key->x = x;
+ key->y = y;
+}
+
+static void swap_byte(unsigned char *a, unsigned char *b)
+{
+ unsigned char swapByte;
+
+ swapByte = *a;
+ *a = *b;
+ *b = swapByte;
+}
+
+/* Dummy _des_crypt function that uses ARCFOUR with a 40 bit key */
+int _arcfour_crypt(buf, len, desp)
+ char *buf;
+ int len;
+ struct desparams *desp;
+{
+ struct arcfour_key arcfourk;
+
+ /*
+ * U.S. government anti-crypto weasels take
+ * note: although we are supplied with a 64 bit
+ * key, we're only passing 40 bits to the ARCFOUR
+ * encryption code. So there.
+ */
+ prepare_key(desp->des_key, 5, &arcfourk);
+ arcfour(buf, len, &arcfourk);
+
+ return(DESERR_NOHWDEVICE);
+}
+
+int (*_my_crypt)__P((char *, int, struct desparams *)) = NULL;
+
+static void *dlhandle;
+
+#ifndef _PATH_USRLIB
+#define _PATH_USRLIB "/usr/lib"
+#endif
+
+#ifndef LIBCRYPTO
+#ifdef OBJFORMAT_ELF
+#define LIBCRYPTO "libcrypto.so.1"
+#else
+#define LIBCRYPTO "libcrypto.so.1."
+#endif /* OBJFORMAT_ELF */
+#endif
+
+void load_des(warn, libpath)
+ int warn;
+ char *libpath;
+{
+ char dlpath[MAXPATHLEN];
+
+ if (libpath == NULL) {
+#ifdef OBJFORMAT_ELF
+ snprintf(dlpath, sizeof(dlpath), "%s/%s", _PATH_USRLIB, LIBCRYPTO);
+#else
+ len = strlen(LIBCRYPTO);
+ 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, LIBCRYPTO, 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..b54e5e4
--- /dev/null
+++ b/usr.sbin/keyserv/keyserv.8
@@ -0,0 +1,78 @@
+.\" @(#)keyserv.1m 1.21 93/07/14 SMI; from SVr4
+.\"macro stdmacro
+.\" Copyright 1989 AT&T
+.\" @(#)keyserv.8c 1.8 89/03/29 SMI;
+.\" $FreeBSD$
+.\".TH KEYSERV 8C "9 September 1987"
+.Dd September 14, 1992
+.Dt KEYSERV 8
+.Os
+.Sh NAME
+.Nm keyserv
+.Nd server for storing private encryption keys
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Op Fl D
+.Op Fl n
+.Op Fl p Ar path
+.Op Fl v
+.Sh DESCRIPTION
+.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 ,
+then the key of user
+.Em nobody
+is used instead as the default key.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl d
+Disable the use of default keys for
+.Em nobody .
+.It Fl D
+Run in debugging mode and log all requests to
+.Nm .
+.It Fl n
+Root's secret key is not read from
+.Pa /etc/.rootkey .
+Instead,
+.Nm
+prompts the user for the password to decrypt
+root's key stored in the
+.Pa /etc/publickey
+database and then stores the decrypted key in
+.Pa /etc/.rootkey
+for future use.
+This option is useful if the
+.Pa /etc/.rootkey
+file ever gets out of date or corrupted.
+.It Fl p Ar path
+Specify where to search for
+.Pa libdes.so.3 .
+Default is
+.Pa /usr/lib .
+.It Fl v
+Display status of DES support (enabled/disabled).
+.El
+.Sh FILES
+.Bl -tag -width /usr/lib/libdes.so.3. -compact
+.It Pa /etc/.rootkey
+.It Pa /usr/lib/libdes.so.3
+.El
+.Sh "SEE ALSO"
+.Xr keylogin 1 ,
+.Xr keylogout 1 ,
+.Xr publickey 5
diff --git a/usr.sbin/keyserv/keyserv.c b/usr.sbin/keyserv/keyserv.c
new file mode 100644
index 0000000..797549a
--- /dev/null
+++ b/usr.sbin/keyserv/keyserv.c
@@ -0,0 +1,812 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)keyserv.c 1.15 94/04/25 SMI";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/*
+ * Copyright (c) 1986 - 1991 by Sun Microsystems, Inc.
+ */
+
+/*
+ * Keyserver
+ * Store secret keys per uid. Do public key encryption and decryption
+ * operations. Generate "random" keys.
+ * Do not talk to anything but a local root
+ * process on the local transport only
+ */
+
+#include <err.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <rpc/rpc.h>
+#include <sys/param.h>
+#include <sys/file.h>
+#include <rpc/des_crypt.h>
+#include <rpc/des.h>
+#include <rpc/key_prot.h>
+#include <rpcsvc/crypt.h>
+#include "keyserv.h"
+
+#ifndef NGROUPS
+#define NGROUPS 16
+#endif
+
+#ifndef KEYSERVSOCK
+#define KEYSERVSOCK "/var/run/keyservsock"
+#endif
+
+static void randomize __P(( des_block * ));
+static void usage __P(( void ));
+static int getrootkey __P(( des_block *, int ));
+static int root_auth __P(( SVCXPRT *, struct svc_req * ));
+
+#ifdef DEBUG
+static int debugging = 1;
+#else
+static int debugging = 0;
+#endif
+
+static void keyprogram();
+static des_block masterkey;
+char *getenv();
+static char ROOTKEY[] = "/etc/.rootkey";
+
+/*
+ * Hack to allow the keyserver to use AUTH_DES (for authenticated
+ * NIS+ calls, for example). The only functions that get called
+ * are key_encryptsession_pk, key_decryptsession_pk, and key_gendes.
+ *
+ * The approach is to have the keyserver fill in pointers to local
+ * implementations of these functions, and to call those in key_call().
+ */
+
+extern cryptkeyres *(*__key_encryptsession_pk_LOCAL)();
+extern cryptkeyres *(*__key_decryptsession_pk_LOCAL)();
+extern des_block *(*__key_gendes_LOCAL)();
+extern int (*__des_crypt_LOCAL)();
+
+cryptkeyres *key_encrypt_pk_2_svc_prog __P(( uid_t, cryptkeyarg2 * ));
+cryptkeyres *key_decrypt_pk_2_svc_prog __P(( uid_t, cryptkeyarg2 * ));
+des_block *key_gen_1_svc_prog __P(( void *, struct svc_req * ));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int nflag = 0;
+ int c;
+ int warn = 0;
+ char *path = NULL;
+ void *localhandle;
+ register SVCXPRT *transp;
+ struct netconfig *nconf = NULL;
+
+ __key_encryptsession_pk_LOCAL = &key_encrypt_pk_2_svc_prog;
+ __key_decryptsession_pk_LOCAL = &key_decrypt_pk_2_svc_prog;
+ __key_gendes_LOCAL = &key_gen_1_svc_prog;
+
+ while ((c = getopt(argc, argv, "ndDvp:")) != -1)
+ switch (c) {
+ case 'n':
+ nflag++;
+ break;
+ case 'd':
+ pk_nodefaultkeys();
+ break;
+ case 'D':
+ debugging = 1;
+ break;
+ case 'v':
+ warn = 1;
+ break;
+ case 'p':
+ path = optarg;
+ break;
+ default:
+ usage();
+ }
+
+ load_des(warn, path);
+ __des_crypt_LOCAL = _my_crypt;
+ if (svc_auth_reg(AUTH_DES, _svcauth_des) == -1)
+ errx(1, "failed to register AUTH_DES authenticator");
+
+ if (optind != argc) {
+ usage();
+ }
+
+ /*
+ * Initialize
+ */
+ (void) umask(S_IXUSR|S_IXGRP|S_IXOTH);
+ if (geteuid() != 0)
+ errx(1, "keyserv must be run as root");
+ setmodulus(HEXMODULUS);
+ getrootkey(&masterkey, nflag);
+
+ rpcb_unset(KEY_PROG, KEY_VERS, NULL);
+ rpcb_unset(KEY_PROG, KEY_VERS2, NULL);
+
+ if (svc_create(keyprogram, KEY_PROG, KEY_VERS,
+ "netpath") == 0) {
+ (void) fprintf(stderr,
+ "%s: unable to create service\n", argv[0]);
+ exit(1);
+ }
+
+ if (svc_create(keyprogram, KEY_PROG, KEY_VERS2,
+ "netpath") == 0) {
+ (void) fprintf(stderr,
+ "%s: unable to create service\n", argv[0]);
+ exit(1);
+ }
+
+ localhandle = setnetconfig();
+ while ((nconf = getnetconfig(localhandle)) != NULL) {
+ if (nconf->nc_protofmly != NULL &&
+ strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0)
+ break;
+ }
+
+ if (nconf == NULL)
+ errx(1, "getnetconfig: %s", nc_sperror());
+
+ unlink(KEYSERVSOCK);
+ rpcb_unset(CRYPT_PROG, CRYPT_VERS, nconf);
+ transp = svcunix_create(RPC_ANYSOCK, 0, 0, KEYSERVSOCK);
+ if (transp == NULL)
+ errx(1, "cannot create AF_LOCAL service");
+ if (!svc_reg(transp, KEY_PROG, KEY_VERS, keyprogram, nconf))
+ errx(1, "unable to register (KEY_PROG, KEY_VERS, unix)");
+ if (!svc_reg(transp, KEY_PROG, KEY_VERS2, keyprogram, nconf))
+ errx(1, "unable to register (KEY_PROG, KEY_VERS2, unix)");
+ if (!svc_reg(transp, CRYPT_PROG, CRYPT_VERS, crypt_prog_1, nconf))
+ errx(1, "unable to register (CRYPT_PROG, CRYPT_VERS, unix)");
+
+ endnetconfig(localhandle);
+
+ (void) umask(066); /* paranoia */
+
+ if (!debugging) {
+ daemon(0,0);
+ }
+
+ signal(SIGPIPE, SIG_IGN);
+
+ svc_run();
+ abort();
+ /* NOTREACHED */
+}
+
+/*
+ * In the event that we don't get a root password, we try to
+ * randomize the master key the best we can
+ */
+static void
+randomize(master)
+ des_block *master;
+{
+ 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;
+ xdrproc_t xdr_argument, xdr_result;
+ char *(*local) ();
+ uid_t uid = -1;
+ int check_auth;
+
+ switch (rqstp->rq_proc) {
+ case NULLPROC:
+ svc_sendreply(transp, (xdrproc_t)xdr_void, NULL);
+ return;
+
+ case KEY_SET:
+ xdr_argument = (xdrproc_t)xdr_keybuf;
+ xdr_result = (xdrproc_t)xdr_int;
+ local = (char *(*)()) key_set_1_svc_prog;
+ check_auth = 1;
+ break;
+
+ case KEY_ENCRYPT:
+ xdr_argument = (xdrproc_t)xdr_cryptkeyarg;
+ xdr_result = (xdrproc_t)xdr_cryptkeyres;
+ local = (char *(*)()) key_encrypt_1_svc_prog;
+ check_auth = 1;
+ break;
+
+ case KEY_DECRYPT:
+ xdr_argument = (xdrproc_t)xdr_cryptkeyarg;
+ xdr_result = (xdrproc_t)xdr_cryptkeyres;
+ local = (char *(*)()) key_decrypt_1_svc_prog;
+ check_auth = 1;
+ break;
+
+ case KEY_GEN:
+ xdr_argument = (xdrproc_t)xdr_void;
+ xdr_result = (xdrproc_t)xdr_des_block;
+ local = (char *(*)()) key_gen_1_svc_prog;
+ check_auth = 0;
+ break;
+
+ case KEY_GETCRED:
+ xdr_argument = (xdrproc_t)xdr_netnamestr;
+ xdr_result = (xdrproc_t)xdr_getcredres;
+ local = (char *(*)()) key_getcred_1_svc_prog;
+ check_auth = 0;
+ break;
+
+ case KEY_ENCRYPT_PK:
+ xdr_argument = (xdrproc_t)xdr_cryptkeyarg2;
+ xdr_result = (xdrproc_t)xdr_cryptkeyres;
+ local = (char *(*)()) key_encrypt_pk_2_svc_prog;
+ check_auth = 1;
+ break;
+
+ case KEY_DECRYPT_PK:
+ xdr_argument = (xdrproc_t)xdr_cryptkeyarg2;
+ xdr_result = (xdrproc_t)xdr_cryptkeyres;
+ local = (char *(*)()) key_decrypt_pk_2_svc_prog;
+ check_auth = 1;
+ break;
+
+
+ case KEY_NET_PUT:
+ xdr_argument = (xdrproc_t)xdr_key_netstarg;
+ xdr_result = (xdrproc_t)xdr_keystatus;
+ local = (char *(*)()) key_net_put_2_svc_prog;
+ check_auth = 1;
+ break;
+
+ case KEY_NET_GET:
+ xdr_argument = (xdrproc_t) xdr_void;
+ xdr_result = (xdrproc_t)xdr_key_netstres;
+ local = (char *(*)()) key_net_get_2_svc_prog;
+ check_auth = 1;
+ break;
+
+ case KEY_GET_CONV:
+ xdr_argument = (xdrproc_t) xdr_keybuf;
+ xdr_result = (xdrproc_t)xdr_cryptkeyres;
+ local = (char *(*)()) key_get_conv_2_svc_prog;
+ check_auth = 1;
+ break;
+
+ default:
+ svcerr_noproc(transp);
+ return;
+ }
+ if (check_auth) {
+ if (root_auth(transp, rqstp) == 0) {
+ if (debugging) {
+ (void) fprintf(stderr,
+ "not local privileged process\n");
+ }
+ svcerr_weakauth(transp);
+ return;
+ }
+ if (rqstp->rq_cred.oa_flavor != AUTH_SYS) {
+ if (debugging) {
+ (void) fprintf(stderr,
+ "not unix authentication\n");
+ }
+ svcerr_weakauth(transp);
+ return;
+ }
+ uid = ((struct authsys_parms *)rqstp->rq_clntcred)->aup_uid;
+ }
+
+ memset(&argument, 0, sizeof (argument));
+ if (!svc_getargs(transp, xdr_argument, &argument)) {
+ svcerr_decode(transp);
+ return;
+ }
+ result = (*local) (uid, &argument);
+ if (!svc_sendreply(transp, xdr_result, result)) {
+ if (debugging)
+ (void) fprintf(stderr, "unable to reply\n");
+ svcerr_systemerr(transp);
+ }
+ if (!svc_freeargs(transp, xdr_argument, &argument)) {
+ if (debugging)
+ (void) fprintf(stderr,
+ "unable to free arguments\n");
+ exit(1);
+ }
+ return;
+}
+
+static int
+root_auth(trans, rqstp)
+ SVCXPRT *trans;
+ struct svc_req *rqstp;
+{
+ uid_t uid;
+ struct sockaddr_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(trans, &uid) < 0) {
+ if (debugging)
+ fprintf(stderr, "__rpc_get_local_uid failed\n");
+ return (0);
+ }
+
+ if (debugging)
+ fprintf(stderr, "local_uid %ld\n", uid);
+ if (uid == 0)
+ return (1);
+ if (rqstp->rq_cred.oa_flavor == AUTH_SYS) {
+ if (((uid_t) ((struct authunix_parms *)
+ rqstp->rq_clntcred)->aup_uid)
+ == uid) {
+ return (1);
+ } else {
+ if (debugging)
+ fprintf(stderr,
+ "local_uid %ld mismatches auth %ld\n", uid,
+((uid_t) ((struct authunix_parms *)rqstp->rq_clntcred)->aup_uid));
+ return (0);
+ }
+ } else {
+ if (debugging)
+ fprintf(stderr, "Not auth sys\n");
+ return (0);
+ }
+}
+
+static void
+usage()
+{
+ (void) fprintf(stderr,
+ "usage: keyserv [-n] [-D] [-d] [-v] [-p path]\n");
+ (void) fprintf(stderr, "-d disables the use of default keys\n");
+ exit(1);
+}
diff --git a/usr.sbin/keyserv/keyserv.h b/usr.sbin/keyserv/keyserv.h
new file mode 100644
index 0000000..0cc6a36
--- /dev/null
+++ b/usr.sbin/keyserv/keyserv.h
@@ -0,0 +1,20 @@
+/*
+ * $FreeBSD$
+ */
+extern void setmodulus __P((char *modx));
+
+extern keystatus pk_setkey __P(( uid_t, keybuf ));;
+extern keystatus pk_encrypt __P(( uid_t, char *, netobj *, des_block * ));
+extern keystatus pk_decrypt __P(( uid_t, char *, netobj *, des_block * ));
+extern keystatus pk_netput __P(( uid_t, key_netstarg * ));
+extern keystatus pk_netget __P(( uid_t, key_netstarg * ));
+extern keystatus pk_get_conv_key __P(( uid_t, keybuf, cryptkeyres * ));
+extern void pk_nodefaultkeys __P(( void ));
+
+extern void crypt_prog_1 __P(( struct svc_req *, register SVCXPRT * ));
+extern void load_des __P(( int, char * ));
+
+extern int (*_my_crypt)__P(( char *, int, struct desparams * ));
+
+extern char ROOTKEY[];
+
diff --git a/usr.sbin/keyserv/setkey.c b/usr.sbin/keyserv/setkey.c
new file mode 100644
index 0000000..566b1c7
--- /dev/null
+++ b/usr.sbin/keyserv/setkey.c
@@ -0,0 +1,550 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)setkey.c 1.11 94/04/25 SMI";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/*
+ * Copyright (c) 1986 - 1991 by Sun Microsystems, Inc.
+ */
+
+/*
+ * Do the real work of the keyserver.
+ * Store secret keys. Compute common keys,
+ * and use them to decrypt and encrypt DES keys.
+ * Cache the common keys, so the expensive computation is avoided.
+ */
+#include <mp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <rpc/rpc.h>
+#include <rpc/key_prot.h>
+#include <rpc/des_crypt.h>
+#include <rpc/des.h>
+#include <sys/errno.h>
+#include "keyserv.h"
+
+static MINT *MODULUS;
+static char *fetchsecretkey __P(( uid_t ));
+static void writecache __P(( char *, char *, des_block * ));
+static int readcache __P(( char *, char *, des_block * ));
+static void extractdeskey __P (( MINT *, des_block * ));
+static int storesecretkey __P(( uid_t, keybuf ));
+static keystatus pk_crypt __P(( uid_t, char *, netobj *, des_block *, int));
+static int nodefaultkeys = 0;
+
+
+/*
+ * prohibit the nobody key on this machine k (the -d flag)
+ */
+void
+pk_nodefaultkeys()
+{
+ nodefaultkeys = 1;
+}
+
+/*
+ * Set the modulus for all our Diffie-Hellman operations
+ */
+void
+setmodulus(modx)
+ char *modx;
+{
+ MODULUS = xtom(modx);
+}
+
+/*
+ * Set the secretkey key for this uid
+ */
+keystatus
+pk_setkey(uid, skey)
+ uid_t uid;
+ keybuf skey;
+{
+ if (!storesecretkey(uid, skey)) {
+ return (KEY_SYSTEMERR);
+ }
+ return (KEY_SUCCESS);
+}
+
+/*
+ * Encrypt the key using the public key associated with remote_name and the
+ * secret key associated with uid.
+ */
+keystatus
+pk_encrypt(uid, remote_name, remote_key, key)
+ uid_t uid;
+ char *remote_name;
+ netobj *remote_key;
+ des_block *key;
+{
+ return (pk_crypt(uid, remote_name, remote_key, key, DES_ENCRYPT));
+}
+
+/*
+ * Decrypt the key using the public key associated with remote_name and the
+ * secret key associated with uid.
+ */
+keystatus
+pk_decrypt(uid, remote_name, remote_key, key)
+ uid_t uid;
+ char *remote_name;
+ netobj *remote_key;
+ des_block *key;
+{
+ return (pk_crypt(uid, remote_name, remote_key, key, DES_DECRYPT));
+}
+
+static int store_netname __P(( uid_t, key_netstarg * ));
+static int fetch_netname __P(( uid_t, key_netstarg * ));
+
+keystatus
+pk_netput(uid, netstore)
+ uid_t uid;
+ key_netstarg *netstore;
+{
+ if (!store_netname(uid, netstore)) {
+ return (KEY_SYSTEMERR);
+ }
+ return (KEY_SUCCESS);
+}
+
+keystatus
+pk_netget(uid, netstore)
+ uid_t uid;
+ key_netstarg *netstore;
+{
+ if (!fetch_netname(uid, netstore)) {
+ return (KEY_SYSTEMERR);
+ }
+ return (KEY_SUCCESS);
+}
+
+
+/*
+ * Do the work of pk_encrypt && pk_decrypt
+ */
+static keystatus
+pk_crypt(uid, remote_name, remote_key, key, mode)
+ uid_t uid;
+ char *remote_name;
+ netobj *remote_key;
+ des_block *key;
+ int mode;
+{
+ char *xsecret;
+ char xpublic[1024];
+ char xsecret_hold[1024];
+ des_block deskey;
+ int err;
+ MINT *public;
+ MINT *secret;
+ MINT *common;
+ char zero[8];
+
+ xsecret = fetchsecretkey(uid);
+ if (xsecret == NULL || xsecret[0] == 0) {
+ memset(zero, 0, sizeof (zero));
+ xsecret = xsecret_hold;
+ if (nodefaultkeys)
+ return (KEY_NOSECRET);
+
+ if (!getsecretkey("nobody", xsecret, zero) || xsecret[0] == 0) {
+ return (KEY_NOSECRET);
+ }
+ }
+ if (remote_key) {
+ memcpy(xpublic, remote_key->n_bytes, remote_key->n_len);
+ } else {
+ bzero((char *)&xpublic, sizeof(xpublic));
+ if (!getpublickey(remote_name, xpublic)) {
+ if (nodefaultkeys || !getpublickey("nobody", xpublic))
+ return (KEY_UNKNOWN);
+ }
+ }
+
+ if (!readcache(xpublic, xsecret, &deskey)) {
+ public = xtom(xpublic);
+ secret = xtom(xsecret);
+ /* Sanity Check on public and private keys */
+ if ((public == NULL) || (secret == NULL))
+ return (KEY_SYSTEMERR);
+
+ common = itom(0);
+ pow(public, secret, MODULUS, common);
+ extractdeskey(common, &deskey);
+ writecache(xpublic, xsecret, &deskey);
+ mfree(secret);
+ mfree(public);
+ mfree(common);
+ }
+ err = ecb_crypt((char *)&deskey, (char *)key, sizeof (des_block),
+ DES_HW | mode);
+ if (DES_FAILED(err)) {
+ return (KEY_SYSTEMERR);
+ }
+ return (KEY_SUCCESS);
+}
+
+keystatus
+pk_get_conv_key(uid, xpublic, result)
+ uid_t uid;
+ keybuf xpublic;
+ cryptkeyres *result;
+{
+ char *xsecret;
+ char xsecret_hold[1024];
+ MINT *public;
+ MINT *secret;
+ MINT *common;
+ char zero[8];
+
+
+ xsecret = fetchsecretkey(uid);
+
+ if (xsecret == NULL || xsecret[0] == 0) {
+ memset(zero, 0, sizeof (zero));
+ xsecret = xsecret_hold;
+ if (nodefaultkeys)
+ return (KEY_NOSECRET);
+
+ if (!getsecretkey("nobody", xsecret, zero) ||
+ xsecret[0] == 0)
+ return (KEY_NOSECRET);
+ }
+
+ if (!readcache(xpublic, xsecret, &result->cryptkeyres_u.deskey)) {
+ public = xtom(xpublic);
+ secret = xtom(xsecret);
+ /* Sanity Check on public and private keys */
+ if ((public == NULL) || (secret == NULL))
+ return (KEY_SYSTEMERR);
+
+ common = itom(0);
+ pow(public, secret, MODULUS, common);
+ extractdeskey(common, &result->cryptkeyres_u.deskey);
+ writecache(xpublic, xsecret, &result->cryptkeyres_u.deskey);
+ mfree(secret);
+ mfree(public);
+ mfree(common);
+ }
+
+ return (KEY_SUCCESS);
+}
+
+/*
+ * Choose middle 64 bits of the common key to use as our des key, possibly
+ * overwriting the lower order bits by setting parity.
+ */
+static void
+extractdeskey(ck, deskey)
+ MINT *ck;
+ des_block *deskey;
+{
+ MINT *a;
+ short r;
+ int i;
+ short base = (1 << 8);
+ char *k;
+
+ a = itom(0);
+#ifdef SOLARIS_MP
+ _mp_move(ck, a);
+#else
+ move(ck, a);
+#endif
+ for (i = 0; i < ((KEYSIZE - 64) / 2) / 8; i++) {
+ sdiv(a, base, a, &r);
+ }
+ k = deskey->c;
+ for (i = 0; i < 8; i++) {
+ sdiv(a, base, a, &r);
+ *k++ = r;
+ }
+ mfree(a);
+ des_setparity((char *)deskey);
+}
+
+/*
+ * Key storage management
+ */
+
+#define KEY_ONLY 0
+#define KEY_NAME 1
+struct secretkey_netname_list {
+ uid_t uid;
+ key_netstarg keynetdata;
+ u_char sc_flag;
+ struct secretkey_netname_list *next;
+};
+
+
+
+static struct secretkey_netname_list *g_secretkey_netname;
+
+/*
+ * Store the keys and netname for this uid
+ */
+static int
+store_netname(uid, netstore)
+ uid_t uid;
+ key_netstarg *netstore;
+{
+ struct secretkey_netname_list *new;
+ struct secretkey_netname_list **l;
+
+ for (l = &g_secretkey_netname; *l != NULL && (*l)->uid != uid;
+ l = &(*l)->next) {
+ }
+ if (*l == NULL) {
+ new = (struct secretkey_netname_list *)malloc(sizeof (*new));
+ if (new == NULL) {
+ return (0);
+ }
+ new->uid = uid;
+ new->next = NULL;
+ *l = new;
+ } else {
+ new = *l;
+ if (new->keynetdata.st_netname)
+ (void) free (new->keynetdata.st_netname);
+ }
+ memcpy(new->keynetdata.st_priv_key, netstore->st_priv_key,
+ HEXKEYBYTES);
+ memcpy(new->keynetdata.st_pub_key, netstore->st_pub_key, HEXKEYBYTES);
+
+ if (netstore->st_netname)
+ new->keynetdata.st_netname = strdup(netstore->st_netname);
+ else
+ new->keynetdata.st_netname = (char *)NULL;
+ new->sc_flag = KEY_NAME;
+ return (1);
+
+}
+
+/*
+ * Fetch the keys and netname for this uid
+ */
+
+static int
+fetch_netname(uid, key_netst)
+ uid_t uid;
+ struct key_netstarg *key_netst;
+{
+ struct secretkey_netname_list *l;
+
+ for (l = g_secretkey_netname; l != NULL; l = l->next) {
+ if ((l->uid == uid) && (l->sc_flag == KEY_NAME)){
+
+ memcpy(key_netst->st_priv_key,
+ l->keynetdata.st_priv_key, HEXKEYBYTES);
+
+ memcpy(key_netst->st_pub_key,
+ l->keynetdata.st_pub_key, HEXKEYBYTES);
+
+ if (l->keynetdata.st_netname)
+ key_netst->st_netname =
+ strdup(l->keynetdata.st_netname);
+ else
+ key_netst->st_netname = NULL;
+ return (1);
+ }
+ }
+
+ return (0);
+}
+
+static char *
+fetchsecretkey(uid)
+ uid_t uid;
+{
+ struct secretkey_netname_list *l;
+
+ for (l = g_secretkey_netname; l != NULL; l = l->next) {
+ if (l->uid == uid) {
+ return (l->keynetdata.st_priv_key);
+ }
+ }
+ return (NULL);
+}
+
+/*
+ * Store the secretkey for this uid
+ */
+static int
+storesecretkey(uid, key)
+ uid_t uid;
+ keybuf key;
+{
+ struct secretkey_netname_list *new;
+ struct secretkey_netname_list **l;
+
+ for (l = &g_secretkey_netname; *l != NULL && (*l)->uid != uid;
+ l = &(*l)->next) {
+ }
+ if (*l == NULL) {
+ new = (struct secretkey_netname_list *) malloc(sizeof (*new));
+ if (new == NULL) {
+ return (0);
+ }
+ new->uid = uid;
+ new->sc_flag = KEY_ONLY;
+ memset(new->keynetdata.st_pub_key, 0, HEXKEYBYTES);
+ new->keynetdata.st_netname = NULL;
+ new->next = NULL;
+ *l = new;
+ } else {
+ new = *l;
+ }
+
+ memcpy(new->keynetdata.st_priv_key, key,
+ HEXKEYBYTES);
+ return (1);
+}
+
+static int
+hexdigit(val)
+ int val;
+{
+ return ("0123456789abcdef"[val]);
+}
+
+void
+bin2hex(bin, hex, size)
+ unsigned char *bin;
+ unsigned char *hex;
+ int size;
+{
+ int i;
+
+ for (i = 0; i < size; i++) {
+ *hex++ = hexdigit(*bin >> 4);
+ *hex++ = hexdigit(*bin++ & 0xf);
+ }
+}
+
+static int
+hexval(dig)
+ char dig;
+{
+ if ('0' <= dig && dig <= '9') {
+ return (dig - '0');
+ } else if ('a' <= dig && dig <= 'f') {
+ return (dig - 'a' + 10);
+ } else if ('A' <= dig && dig <= 'F') {
+ return (dig - 'A' + 10);
+ } else {
+ return (-1);
+ }
+}
+
+void
+hex2bin(hex, bin, size)
+ unsigned char *hex;
+ unsigned char *bin;
+ int size;
+{
+ int i;
+
+ for (i = 0; i < size; i++) {
+ *bin = hexval(*hex++) << 4;
+ *bin++ |= hexval(*hex++);
+ }
+}
+
+/*
+ * Exponential caching management
+ */
+struct cachekey_list {
+ keybuf secret;
+ keybuf public;
+ des_block deskey;
+ struct cachekey_list *next;
+};
+static struct cachekey_list *g_cachedkeys;
+
+/*
+ * cache result of expensive multiple precision exponential operation
+ */
+static void
+writecache(pub, sec, deskey)
+ char *pub;
+ char *sec;
+ des_block *deskey;
+{
+ struct cachekey_list *new;
+
+ new = (struct cachekey_list *) malloc(sizeof (struct cachekey_list));
+ if (new == NULL) {
+ return;
+ }
+ memcpy(new->public, pub, sizeof (keybuf));
+ memcpy(new->secret, sec, sizeof (keybuf));
+ new->deskey = *deskey;
+ new->next = g_cachedkeys;
+ g_cachedkeys = new;
+}
+
+/*
+ * Try to find the common key in the cache
+ */
+static int
+readcache(pub, sec, deskey)
+ char *pub;
+ char *sec;
+ des_block *deskey;
+{
+ struct cachekey_list *found;
+ register struct cachekey_list **l;
+
+#define cachehit(pub, sec, list) \
+ (memcmp(pub, (list)->public, sizeof (keybuf)) == 0 && \
+ memcmp(sec, (list)->secret, sizeof (keybuf)) == 0)
+
+ for (l = &g_cachedkeys; (*l) != NULL && !cachehit(pub, sec, *l);
+ l = &(*l)->next)
+ ;
+ if ((*l) == NULL) {
+ return (0);
+ }
+ found = *l;
+ (*l) = (*l)->next;
+ found->next = g_cachedkeys;
+ g_cachedkeys = found;
+ *deskey = found->deskey;
+ return (1);
+}
diff --git a/usr.sbin/kgmon/Makefile b/usr.sbin/kgmon/Makefile
new file mode 100644
index 0000000..964c79b
--- /dev/null
+++ b/usr.sbin/kgmon/Makefile
@@ -0,0 +1,16 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+PROG= kgmon
+MAN= kgmon.8
+
+# This program may safely be run setuid-root to allow non-root
+# users to start, stop, and reset profiling buffers.
+#
+#BINOWN=root
+#BINMODE=4555
+
+DPADD= ${LIBKVM}
+LDADD= -lkvm
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/kgmon/kgmon.8 b/usr.sbin/kgmon/kgmon.8
new file mode 100644
index 0000000..8dc7881
--- /dev/null
+++ b/usr.sbin/kgmon/kgmon.8
@@ -0,0 +1,132 @@
+.\" Copyright (c) 1983, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)kgmon.8 8.1 (Berkeley) 6/6/93
+.\" $FreeBSD$
+.\"
+.Dd June 6, 1993
+.Dt KGMON 8
+.Os
+.Sh NAME
+.Nm kgmon
+.Nd generate a dump of the operating system's profile buffers
+.Sh SYNOPSIS
+.Nm
+.Op Fl Bbhpr
+.Op Fl M 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
+.Pa /dev/kmem .
+.It Fl N
+Extract the name list from the specified system instead of the
+default
+.Pa /boot/kernel/kernel .
+.El
+.Pp
+If neither
+.Fl B
+nor
+.Fl b
+nor
+.Fl h
+is specified, the state of profiling collection remains unchanged.
+For example, if the
+.Fl p
+flag is specified and profile data is being collected,
+profiling will be momentarily suspended,
+the operating system profile buffers will be dumped,
+and profiling will be immediately resumed.
+.Pp
+The profile buffers should be reset when the resolution
+of the profile data is changed.
+.Sh FILES
+.Bl -tag -width /dev/kmemx -compact
+.It Pa /boot/kernel/kernel
+the default system
+.It Pa /dev/kmem
+the default memory
+.El
+.Sh SEE ALSO
+.Xr gprof 1 ,
+.Xr config 8
+.Sh DIAGNOSTICS
+Users with only read permission on
+.Pa /dev/kmem
+cannot change the state
+of profiling collection.
+They can get a
+.Pa gmon.out
+file with the warning that the data may be
+inconsistent if profiling is in progress.
+.Sh HISTORY
+The
+.Nm
+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..3ee5a73
--- /dev/null
+++ b/usr.sbin/kgmon/kgmon.c
@@ -0,0 +1,533 @@
+/*
+ * Copyright (c) 1983, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1983, 1992, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)kgmon.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/sysctl.h>
+#include <sys/gmon.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <kvm.h>
+#include <limits.h>
+#include <nlist.h>
+#include <paths.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+struct nlist nl[] = {
+#define N_GMONPARAM 0
+ { "__gmonparam" },
+#define N_PROFHZ 1
+ { "_profhz" },
+ { NULL },
+};
+
+struct kvmvars {
+ kvm_t *kd;
+ struct gmonparam gpm;
+};
+
+int Bflag, bflag, hflag, kflag, rflag, pflag;
+int debug = 0;
+int getprof __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;
+ }
+
+ /*
+ * Accept certain undersized "structs" from old kernels. We need
+ * everything up to hashfraction, and want profrate and
+ * histcounter_type. Assume that the kernel doesn't put garbage
+ * in any padding that is returned instead of profrate and
+ * histcounter_type. This is a bad assumption for dead kernels,
+ * since kvm_read() will normally return garbage for bytes beyond
+ * the end of the actual kernel struct, if any.
+ */
+ if (size < offsetof(struct gmonparam, hashfraction) +
+ sizeof(kvp->gpm.hashfraction) || size > sizeof(kvp->gpm))
+ errx(4, "cannot get gmonparam: %s",
+ kflag ? kvm_geterr(kvp->kd) : strerror(errno));
+ bzero((char *)&kvp->gpm + size, sizeof(kvp->gpm) - size);
+ if (kvp->gpm.profrate == 0)
+ kvp->gpm.profrate = getprofhz(kvp);
+#ifdef __i386__
+ if (kvp->gpm.histcounter_type == 0) {
+ /*
+ * This fixup only works for not-so-old i386 kernels. The
+ * magic 16 is the kernel FUNCTION_ALIGNMENT. 64-bit
+ * counters are signed; smaller counters are unsigned.
+ */
+ kvp->gpm.histcounter_type = 16 /
+ (kvp->gpm.textsize / kvp->gpm.kcountsize) * CHAR_BIT;
+ if (kvp->gpm.histcounter_type == 64)
+ kvp->gpm.histcounter_type = -64;
+ }
+#endif
+
+ return (kvp->gpm.state);
+}
+
+/*
+ * Enable or disable kernel profiling according to the state variable.
+ */
+void
+setprof(kvp, state)
+ struct kvmvars *kvp;
+ int state;
+{
+ struct gmonparam *p = (struct gmonparam *)nl[N_GMONPARAM].n_value;
+ 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;
+ h.histcounter_type = kvp->gpm.histcounter_type;
+ fwrite((char *)&h, sizeof(h), 1, fp);
+
+ /*
+ * Write out the tick buffer.
+ */
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROF;
+ if ((tickbuf = (u_short *)malloc(kvp->gpm.kcountsize)) == NULL)
+ errx(5, "cannot allocate kcount space");
+ if (kflag) {
+ i = kvm_read(kvp->kd, (u_long)kvp->gpm.kcount, (void *)tickbuf,
+ kvp->gpm.kcountsize);
+ } else {
+ mib[2] = GPROF_COUNT;
+ i = kvp->gpm.kcountsize;
+ if (sysctl(mib, 3, tickbuf, &i, NULL, 0) < 0)
+ i = 0;
+ }
+ if (i != kvp->gpm.kcountsize)
+ errx(6, "read ticks: read %u, got %d: %s",
+ kvp->gpm.kcountsize, i,
+ kflag ? kvm_geterr(kvp->kd) : strerror(errno));
+ if ((fwrite(tickbuf, kvp->gpm.kcountsize, 1, fp)) != 1)
+ err(7, "writing tocks to gmon.out");
+ free(tickbuf);
+
+ /*
+ * Write out the arc info.
+ */
+ if ((froms = (u_short *)malloc(kvp->gpm.fromssize)) == NULL)
+ errx(8, "cannot allocate froms space");
+ if (kflag) {
+ i = kvm_read(kvp->kd, (u_long)kvp->gpm.froms, (void *)froms,
+ kvp->gpm.fromssize);
+ } else {
+ mib[2] = GPROF_FROMS;
+ i = kvp->gpm.fromssize;
+ if (sysctl(mib, 3, froms, &i, NULL, 0) < 0)
+ i = 0;
+ }
+ if (i != kvp->gpm.fromssize)
+ errx(9, "read froms: read %u, got %d: %s",
+ kvp->gpm.fromssize, i,
+ kflag ? kvm_geterr(kvp->kd) : strerror(errno));
+ if ((tos = (struct tostruct *)malloc(kvp->gpm.tossize)) == NULL)
+ errx(10, "cannot allocate tos space");
+ if (kflag) {
+ i = kvm_read(kvp->kd, (u_long)kvp->gpm.tos, (void *)tos,
+ kvp->gpm.tossize);
+ } else {
+ mib[2] = GPROF_TOS;
+ i = kvp->gpm.tossize;
+ if (sysctl(mib, 3, tos, &i, NULL, 0) < 0)
+ i = 0;
+ }
+ if (i != kvp->gpm.tossize)
+ errx(11, "read tos: read %u, got %d: %s",
+ kvp->gpm.tossize, i,
+ kflag ? kvm_geterr(kvp->kd) : strerror(errno));
+ if (debug)
+ warnx("lowpc 0x%x, textsize 0x%x",
+ kvp->gpm.lowpc, kvp->gpm.textsize);
+ endfrom = kvp->gpm.fromssize / sizeof(*froms);
+ for (fromindex = 0; fromindex < endfrom; ++fromindex) {
+ if (froms[fromindex] == 0)
+ continue;
+ frompc = (u_long)kvp->gpm.lowpc +
+ (fromindex * kvp->gpm.hashfraction * sizeof(*froms));
+ for (toindex = froms[fromindex]; toindex != 0;
+ toindex = tos[toindex].link) {
+ if (debug)
+ warnx("[mcleanup] frompc 0x%x selfpc 0x%x count %d",
+ frompc, tos[toindex].selfpc,
+ tos[toindex].count);
+ rawarc.raw_frompc = frompc;
+ rawarc.raw_selfpc = (u_long)tos[toindex].selfpc;
+ rawarc.raw_count = tos[toindex].count;
+ fwrite((char *)&rawarc, sizeof(rawarc), 1, fp);
+ }
+ }
+ fclose(fp);
+}
+
+/*
+ * Get the profiling rate.
+ */
+int
+getprofhz(kvp)
+ struct kvmvars *kvp;
+{
+ 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/kgzip/Makefile b/usr.sbin/kgzip/Makefile
new file mode 100644
index 0000000..4c8cb07
--- /dev/null
+++ b/usr.sbin/kgzip/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PROG= kgzip
+MAN= kgzip.8
+SRCS= kgzip.c aouthdr.c elfhdr.c kgzcmp.c kgzld.c xio.c
+
+CFLAGS+= ${BDECFLAGS}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/kgzip/aouthdr.c b/usr.sbin/kgzip/aouthdr.c
new file mode 100644
index 0000000..27541e6
--- /dev/null
+++ b/usr.sbin/kgzip/aouthdr.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2000 Robert Nordier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <stddef.h>
+#include "aouthdr.h"
+
+#define KGZ_FIX_NSIZE 0 /* Run-time fixup */
+
+const struct kgz_aouthdr0 aouthdr0 = {
+ /* a.out header */
+ {
+ MID_I386 << 020 | OMAGIC, /* a_midmag */
+ 0, /* a_text */
+ sizeof(struct kgz_hdr) + KGZ_FIX_NSIZE, /* a_data */
+ 0, /* a_bss */
+ sizeof(struct nlist) * KGZ__STNUM, /* a_syms */
+ 0, /* a_entry */
+ 0, /* a_trsize */
+ 0 /* a_drsize */
+ }
+};
+
+const struct kgz_aouthdr1 aouthdr1 = {
+ /* Symbol table */
+ {
+ {
+ {
+ (char *)offsetof(struct kgz__strtab,
+ kgz) /* n_un */
+ },
+ N_DATA | N_EXT, /* n_type */
+ AUX_OBJECT, /* n_other */
+ 0, /* n_desc */
+ 0 /* n_value */
+ },
+ {
+ {
+ (char *)offsetof(struct kgz__strtab,
+ kgz_ndata) /* n_un */
+ },
+ N_DATA | N_EXT, /* n_type */
+ AUX_OBJECT, /* n_other */
+ 0, /* n_desc */
+ sizeof(struct kgz_hdr) /* n_value */
+ }
+ },
+ /* String table */
+ {
+ sizeof(struct kgz__strtab), /* length */
+ KGZ__STR_KGZ, /* kgz */
+ KGZ__STR_KGZ_NDATA /* kgz_ndata */
+ }
+};
diff --git a/usr.sbin/kgzip/aouthdr.h b/usr.sbin/kgzip/aouthdr.h
new file mode 100644
index 0000000..63c2dff
--- /dev/null
+++ b/usr.sbin/kgzip/aouthdr.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2000 Robert Nordier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <a.out.h>
+#include "kgz.h"
+
+/* Relocatable header: part 0 */
+struct kgz_aouthdr0 {
+ struct exec a;
+};
+
+/* Symbol table entries */
+#define KGZ__STNUM 2
+
+/* Symbol table strings */
+#define KGZ__STR_KGZ "_kgz"
+#define KGZ__STR_KGZ_NDATA "_kgz_ndata"
+
+/* String table */
+struct kgz__strtab {
+ unsigned long length;
+ char kgz[sizeof(KGZ__STR_KGZ)];
+ char kgz_ndata[sizeof(KGZ__STR_KGZ_NDATA)];
+};
+
+/* Relocatable header: part 1 */
+struct kgz_aouthdr1 {
+ struct nlist st[KGZ__STNUM];
+ struct kgz__strtab strtab;
+};
+
+extern const struct kgz_aouthdr0 aouthdr0;
+extern const struct kgz_aouthdr1 aouthdr1;
diff --git a/usr.sbin/kgzip/elfhdr.c b/usr.sbin/kgzip/elfhdr.c
new file mode 100644
index 0000000..d3abec7
--- /dev/null
+++ b/usr.sbin/kgzip/elfhdr.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 1999 Global Technology Associates, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <stddef.h>
+#include "elfhdr.h"
+
+#define KGZ_FIX_NSIZE 0 /* Run-time fixup */
+
+/*
+ * Relocatable header template.
+ */
+const struct kgz_elfhdr elfhdr = {
+ /* ELF header */
+ {
+ {
+ ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3, /* e_ident */
+ ELFCLASS32, ELFDATA2LSB, EV_CURRENT, 0,
+ 'F', 'r', 'e', 'e', 'B', 'S', 'D', 0
+ },
+ ET_EXEC, /* e_type */
+ EM_386, /* e_machine */
+ EV_CURRENT, /* e_version */
+ 0, /* e_entry */
+ 0, /* e_phoff */
+ offsetof(struct kgz_elfhdr, sh), /* e_shoff */
+ 0, /* e_flags */
+ sizeof(Elf32_Ehdr), /* e_ehsize */
+ 0, /* e_phentsize */
+ 0, /* e_phnum */
+ sizeof(Elf32_Shdr), /* e_shentsize */
+ KGZ_SHNUM, /* e_shnum */
+ KGZ_SH_SHSTRTAB /* e_shstrndx */
+ },
+ /* Section header */
+ {
+ {
+ 0, /* sh_name */
+ SHT_NULL, /* sh_type */
+ 0, /* sh_flags */
+ 0, /* sh_addr */
+ 0, /* sh_offset */
+ 0, /* sh_size */
+ SHN_UNDEF, /* sh_link */
+ 0, /* sh_info */
+ 0, /* sh_addralign */
+ 0 /* sh_entsize */
+ },
+ {
+ offsetof(struct kgz_shstrtab, symtab), /* sh_name */
+ SHT_SYMTAB, /* sh_type */
+ 0, /* sh_flags */
+ 0, /* sh_addr */
+ offsetof(struct kgz_elfhdr, st), /* sh_offset */
+ sizeof(Elf32_Sym) * KGZ_STNUM, /* sh_size */
+ KGZ_SH_STRTAB, /* sh_link */
+ 1, /* sh_info */
+ 4, /* sh_addralign */
+ sizeof(Elf32_Sym) /* sh_entsize */
+ },
+ {
+ offsetof(struct kgz_shstrtab, shstrtab), /* sh_name */
+ SHT_STRTAB, /* sh_type */
+ 0, /* sh_flags */
+ 0, /* sh_addr */
+ offsetof(struct kgz_elfhdr, shstrtab), /* sh_offset */
+ sizeof(struct kgz_shstrtab), /* sh_size */
+ SHN_UNDEF, /* sh_link */
+ 0, /* sh_info */
+ 1, /* sh_addralign */
+ 0 /* sh_entsize */
+ },
+ {
+ offsetof(struct kgz_shstrtab, strtab), /* sh_name */
+ SHT_STRTAB, /* sh_type */
+ 0, /* sh_flags */
+ 0, /* sh_addr */
+ offsetof(struct kgz_elfhdr, strtab), /* sh_offset */
+ sizeof(struct kgz_strtab), /* sh_size */
+ SHN_UNDEF, /* sh_link */
+ 0, /* sh_info */
+ 1, /* sh_addralign */
+ 0 /* sh_entsize */
+ },
+ {
+ offsetof(struct kgz_shstrtab, data), /* sh_name */
+ SHT_PROGBITS, /* sh_type */
+ SHF_ALLOC | SHF_WRITE, /* sh_flags */
+ 0, /* sh_addr */
+ sizeof(struct kgz_elfhdr), /* sh_offset */
+ sizeof(struct kgz_hdr) + KGZ_FIX_NSIZE, /* sh_size */
+ SHN_UNDEF, /* sh_link */
+ 0, /* sh_info */
+ 4, /* sh_addralign */
+ 0 /* sh_entsize */
+ }
+ },
+ /* Symbol table */
+ {
+ {
+ 0, /* st_name */
+ 0, /* st_value */
+ 0, /* st_size */
+ 0, /* st_info */
+ 0, /* st_other */
+ SHN_UNDEF /* st_shndx */
+ },
+ {
+ offsetof(struct kgz_strtab, kgz), /* st_name */
+ 0, /* st_value */
+ sizeof(struct kgz_hdr), /* st_size */
+ ELF32_ST_INFO(STB_GLOBAL, STT_OBJECT), /* st_info */
+ 0, /* st_other */
+ KGZ_SH_DATA /* st_shndx */
+ },
+ {
+ offsetof(struct kgz_strtab, kgz_ndata), /* st_name */
+ sizeof(struct kgz_hdr), /* st_value */
+ KGZ_FIX_NSIZE, /* st_size */
+ ELF32_ST_INFO(STB_GLOBAL, STT_OBJECT), /* st_info */
+ 0, /* st_other */
+ KGZ_SH_DATA /* st_shndx */
+ }
+ },
+ /* Section header string table */
+ {
+ KGZ_SHSTR_ZERO, /* zero */
+ KGZ_SHSTR_SYMTAB, /* symtab */
+ KGZ_SHSTR_SHSTRTAB, /* shstrtab */
+ KGZ_SHSTR_STRTAB, /* strtab */
+ KGZ_SHSTR_DATA /* data */
+ },
+ /* String table */
+ {
+ KGZ_STR_ZERO, /* zero */
+ KGZ_STR_KGZ, /* kgz */
+ KGZ_STR_KGZ_NDATA /* kgz_ndata */
+ }
+};
diff --git a/usr.sbin/kgzip/elfhdr.h b/usr.sbin/kgzip/elfhdr.h
new file mode 100644
index 0000000..f689119
--- /dev/null
+++ b/usr.sbin/kgzip/elfhdr.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 1999 Global Technology Associates, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <machine/elf.h>
+#include "kgz.h"
+
+/* Section header indices */
+#define KGZ_SH_SYMTAB 1
+#define KGZ_SH_SHSTRTAB 2
+#define KGZ_SH_STRTAB 3
+#define KGZ_SH_DATA 4
+#define KGZ_SHNUM 5
+
+/* Section header strings */
+#define KGZ_SHSTR_ZERO ""
+#define KGZ_SHSTR_SYMTAB ".symtab"
+#define KGZ_SHSTR_SHSTRTAB ".shstrtab"
+#define KGZ_SHSTR_STRTAB ".strtab"
+#define KGZ_SHSTR_DATA ".data"
+
+/* Section header string table */
+struct kgz_shstrtab {
+ char zero[sizeof(KGZ_SHSTR_ZERO)];
+ char symtab[sizeof(KGZ_SHSTR_SYMTAB)];
+ char shstrtab[sizeof(KGZ_SHSTR_SHSTRTAB)];
+ char strtab[sizeof(KGZ_SHSTR_STRTAB)];
+ char data[sizeof(KGZ_SHSTR_DATA)];
+};
+
+/* Symbol table indices */
+#define KGZ_ST_KGZ 1
+#define KGZ_ST_KGZ_NDATA 2
+#define KGZ_STNUM 3
+
+/* Symbol table strings */
+#define KGZ_STR_ZERO ""
+#define KGZ_STR_KGZ "kgz"
+#define KGZ_STR_KGZ_NDATA "kgz_ndata"
+
+/* String table */
+struct kgz_strtab {
+ char zero[sizeof(KGZ_STR_ZERO)];
+ char kgz[sizeof(KGZ_STR_KGZ)];
+ char kgz_ndata[sizeof(KGZ_STR_KGZ_NDATA)];
+};
+
+/* Relocatable header format */
+struct kgz_elfhdr {
+ Elf32_Ehdr e;
+ Elf32_Shdr sh[KGZ_SHNUM];
+ Elf32_Sym st[KGZ_STNUM];
+ struct kgz_shstrtab shstrtab;
+ struct kgz_strtab strtab;
+};
+
+extern const struct kgz_elfhdr elfhdr;
diff --git a/usr.sbin/kgzip/kgz.h b/usr.sbin/kgzip/kgz.h
new file mode 100644
index 0000000..3cc8e13
--- /dev/null
+++ b/usr.sbin/kgzip/kgz.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 1999 Global Technology Associates, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _KGZ_H_
+#define _KGZ_H_
+
+#include <sys/types.h>
+
+/*
+ * KGZ definitions: kgzip(8) output.
+ */
+
+/* Values for ident[]. */
+#define KGZ_ID0 'K'
+#define KGZ_ID1 'G'
+#define KGZ_ID2 'Z'
+#define KGZ_ID3 '\0'
+
+/* KGZ header. */
+struct kgz_hdr {
+ char ident[4]; /* identification */
+ uint32_t dload; /* decoded image load address */
+ uint32_t dsize; /* decoded image size */
+ uint32_t isize; /* image size in memory */
+ uint32_t entry; /* program entry point */
+ uint32_t nsize; /* encoded image size */
+};
+
+extern struct kgz_hdr kgz; /* header */
+extern uint8_t kgz_ndata[]; /* encoded image */
+
+#endif /* !_KGZ_H_ */
diff --git a/usr.sbin/kgzip/kgzcmp.c b/usr.sbin/kgzip/kgzcmp.c
new file mode 100644
index 0000000..5def730
--- /dev/null
+++ b/usr.sbin/kgzip/kgzcmp.c
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 1999 Global Technology Associates, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <machine/elf.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <a.out.h>
+
+#include "aouthdr.h"
+#include "elfhdr.h"
+#include "kgzip.h"
+
+static void mk_data(const struct iodesc *i, const struct iodesc *,
+ struct kgz_hdr *, size_t);
+static int ld_elf(const struct iodesc *, const struct iodesc *,
+ struct kgz_hdr *, const Elf32_Ehdr *);
+static int ld_aout(const struct iodesc *, const struct iodesc *,
+ struct kgz_hdr *, const struct exec *);
+
+/*
+ * Compress executable and output it in relocatable object format.
+ */
+void
+kgzcmp(struct kgz_hdr *kh, const char *f1, const char *f2)
+{
+ struct iodesc idi, ido;
+
+ if ((idi.fd = open(idi.fname = f1, O_RDONLY)) == -1)
+ err(1, "%s", idi.fname);
+ if ((ido.fd = open(ido.fname = f2, O_CREAT | O_TRUNC | O_WRONLY,
+ 0666)) == -1)
+ err(1, "%s", ido.fname);
+ kh->ident[0] = KGZ_ID0;
+ kh->ident[1] = KGZ_ID1;
+ kh->ident[2] = KGZ_ID2;
+ kh->ident[3] = KGZ_ID3;
+ mk_data(&idi, &ido, kh,
+ (format == F_AOUT ? sizeof(struct kgz_aouthdr0) :
+ sizeof(struct kgz_elfhdr)) +
+ sizeof(struct kgz_hdr));
+ kh->dload &= 0xffffff;
+ kh->entry &= 0xffffff;
+ if (format == F_AOUT) {
+ struct kgz_aouthdr0 ahdr0 = aouthdr0;
+ struct kgz_aouthdr1 ahdr1 = aouthdr1;
+ unsigned x = (sizeof(struct kgz_hdr) + kh->nsize) & (16 - 1);
+ if (x) {
+ x = 16 - x;
+ xzero(&ido, x);
+ }
+ xwrite(&ido, &ahdr1, sizeof(ahdr1));
+ ahdr0.a.a_data += kh->nsize + x;
+ xseek(&ido, 0);
+ xwrite(&ido, &ahdr0, sizeof(ahdr0));
+ } else {
+ struct kgz_elfhdr ehdr = elfhdr;
+ ehdr.st[KGZ_ST_KGZ_NDATA].st_size = kh->nsize;
+ ehdr.sh[KGZ_SH_DATA].sh_size += kh->nsize;
+ xseek(&ido, 0);
+ xwrite(&ido, &ehdr, sizeof(ehdr));
+ }
+ xwrite(&ido, kh, sizeof(*kh));
+ xclose(&ido);
+ xclose(&idi);
+}
+
+/*
+ * Make encoded (compressed) data.
+ */
+static void
+mk_data(const struct iodesc * idi, const struct iodesc * ido,
+ struct kgz_hdr * kh, size_t off)
+{
+ union {
+ struct exec ex;
+ Elf32_Ehdr ee;
+ } hdr;
+ struct stat sb;
+ struct iodesc idp;
+ int fd[2];
+ pid_t pid;
+ size_t n;
+ int fmt, status, e;
+
+ n = xread(idi, &hdr, sizeof(hdr), 0);
+ fmt = 0;
+ if (n >= sizeof(hdr.ee) && IS_ELF(hdr.ee))
+ fmt = F_ELF;
+ else if (n >= sizeof(hdr.ex) && N_GETMAGIC(hdr.ex) == ZMAGIC)
+ fmt = F_AOUT;
+ if (!fmt)
+ errx(1, "%s: Format not supported", idi->fname);
+ xseek(ido, off);
+ if (pipe(fd))
+ err(1, NULL);
+ switch (pid = fork()) {
+ case -1:
+ err(1, NULL);
+ case 0:
+ close(fd[1]);
+ dup2(fd[0], STDIN_FILENO);
+ close(fd[0]);
+ close(idi->fd);
+ dup2(ido->fd, STDOUT_FILENO);
+ close(ido->fd);
+ execlp("gzip", "gzip", "-9", (char *)NULL);
+ warn(NULL);
+ _exit(1);
+ default:
+ close(fd[0]);
+ idp.fname = "(pipe)";
+ idp.fd = fd[1];
+ e = fmt == F_ELF ? ld_elf(idi, &idp, kh, &hdr.ee) :
+ fmt == F_AOUT ? ld_aout(idi, &idp, kh, &hdr.ex) : -1;
+ close(fd[1]);
+ if ((pid = waitpid(pid, &status, 0)) == -1)
+ err(1, NULL);
+ if (WIFSIGNALED(status) || WEXITSTATUS(status))
+ exit(1);
+ }
+ if (e)
+ errx(1, "%s: Invalid format", idi->fname);
+ if (fstat(ido->fd, &sb))
+ err(1, "%s", ido->fname);
+ kh->nsize = sb.st_size - off;
+}
+
+/*
+ * "Load" an ELF-format executable.
+ */
+static int
+ld_elf(const struct iodesc * idi, const struct iodesc * ido,
+ struct kgz_hdr * kh, const Elf32_Ehdr * e)
+{
+ Elf32_Phdr p;
+ size_t load, addr, n;
+ unsigned x, i;
+
+ load = addr = n = 0;
+ for (x = i = 0; i < e->e_phnum; i++) {
+ if (xread(idi, &p, sizeof(p),
+ e->e_phoff + i * e->e_phentsize) != e->e_phentsize)
+ return -1;
+ if (p.p_type != PT_LOAD)
+ continue;
+ if (!x)
+ load = addr = p.p_vaddr;
+ else {
+ if (p.p_vaddr < addr)
+ return -1;
+ n = p.p_vaddr - addr;
+ if (n) {
+ xzero(ido, n);
+ addr += n;
+ }
+ }
+ if (p.p_memsz < p.p_filesz)
+ return -1;
+ n = p.p_memsz - p.p_filesz;
+ xcopy(idi, ido, p.p_filesz, p.p_offset);
+ addr += p.p_filesz;
+ x++;
+ }
+ if (!x)
+ return -1;
+ kh->dload = load;
+ kh->dsize = addr - load;
+ kh->isize = kh->dsize + n;
+ kh->entry = e->e_entry;
+ return 0;
+}
+
+/*
+ * "Load" an a.out-format executable.
+ */
+static int
+ld_aout(const struct iodesc * idi, const struct iodesc * ido,
+ struct kgz_hdr * kh, const struct exec * a)
+{
+ size_t load, addr;
+
+ load = addr = N_TXTADDR(*a);
+ xcopy(idi, ido, a->a_text, N_TXTOFF(*a));
+ addr += a->a_text;
+ if (N_DATADDR(*a) != addr)
+ return -1;
+ xcopy(idi, ido, a->a_data, N_DATOFF(*a));
+ addr += a->a_data;
+ kh->dload = load;
+ kh->dsize = addr - load;
+ kh->isize = kh->dsize + a->a_bss;
+ kh->entry = a->a_entry;
+ return 0;
+}
diff --git a/usr.sbin/kgzip/kgzip.8 b/usr.sbin/kgzip/kgzip.8
new file mode 100644
index 0000000..653a2ad
--- /dev/null
+++ b/usr.sbin/kgzip/kgzip.8
@@ -0,0 +1,141 @@
+.\" Copyright (c) 1999 Global Technology Associates, Inc.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+.\" OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+.\" OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+.\" BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+.\" WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+.\" OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+.\" EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd July 19, 1999
+.Dt KGZIP 8
+.Os
+.Sh NAME
+.Nm kgzip
+.Nd compress a kernel
+.Sh SYNOPSIS
+.Nm
+.Op Fl cv
+.Op Fl f Ar format
+.Op Fl l Ar loader
+.Op Fl o Ar output
+.Ar file
+.Sh DESCRIPTION
+The
+.Nm
+utility compresses a kernel or some other bootable binary. Operation
+is in two phases as follows:
+.Bl -enum
+.It
+A load image of the executable file is built which omits all but
+the
+.Sq text
+and
+.Sq data
+segments. This image is compressed using
+.Xr gzip 1
+and output as data in relocatable object format.
+.It
+The object file is linked with a special self-hosting loader, producing
+an executable suitable for booting with either the second- or
+third-level bootstraps.
+.El
+.Pp
+Supported object formats are 32-bit ELF and a.out ZMAGIC.
+.Pp
+If the
+.Ar file
+operand has a
+.Sq .o
+suffix, input is assumed to be for the link phase, and the first phase
+is omitted.
+.Pp
+The options are:
+.Bl -tag -width Fl
+.It Fl c
+Omit the link phase.
+.It Fl v
+Display object file information.
+.It Fl f Ar format
+Use
+.Ar format
+as the output format, where
+.Ar format
+is
+.Sq aout
+or
+.Sq elf .
+The default format is ELF.
+.It Fl l Ar loader
+Link
+.Ar loader
+as the loader.
+.It Fl o Ar output
+Name the output file
+.Ar output .
+The default is to use the input name with the suffix
+.Sq .o
+(for relocatables) or
+.Sq .kgz
+(for executables).
+.El
+.Sh NOTES
+Global variables equivalent to the following are defined in the output:
+.Bd -literal
+struct kgz_hdr {
+ char ident[4]; /* identification: "KGZ" */
+ uint32_t dload; /* decoded image load address */
+ uint32_t dsize; /* decoded image size */
+ uint32_t isize; /* image size in memory */
+ uint32_t entry; /* entry point */
+ uint32_t nsize; /* encoded image size */
+} kgz;
+
+uint8_t kgz_ndata[]; /* encoded data */
+.Ed
+.Pp
+The encoded data is simply
+.Xr gzip 1
+output: a header (with no optional fields); compressed data; and 32-bit
+CRC and size values.
+.Sh FILES
+.Bl -tag -width /usr/lib/kgzldr.o -compact
+.It Pa /usr/lib/kgzldr.o
+The default loader
+.El
+.Sh SEE ALSO
+.Xr gzip 1 ,
+.Xr ld 1 ,
+.Xr a.out 5 ,
+.Xr elf 5 ,
+.Xr boot 8 ,
+.Xr loader 8
+.Sh DIAGNOSTICS
+.Ex -std
+.Sh AUTHORS
+.An Robert Nordier Aq rnordier@FreeBSD.org .
+.Sh BUGS
+As symbols are lost, the usefulness of this utility for compressing
+kernels is limited to situations where
+.Xr loader 8
+cannot be used; otherwise the preferred method of compressing a kernel
+is simply to
+.Xr gzip 1
+it.
diff --git a/usr.sbin/kgzip/kgzip.c b/usr.sbin/kgzip/kgzip.c
new file mode 100644
index 0000000..788825e
--- /dev/null
+++ b/usr.sbin/kgzip/kgzip.c
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 1999 Global Technology Associates, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <err.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "kgzip.h"
+
+#define FN_SRC 0 /* Filename: source */
+#define FN_OBJ 1 /* Filename: relocatable */
+#define FN_KGZ 2 /* Filename: executable */
+#define FN_CNT 3 /* Number of filenames */
+
+#define SFX_OBJ ".o" /* Filename suffix: relocatable */
+#define SFX_KGZ ".kgz" /* Filename suffix: executable */
+#define SFX_MAX 5 /* Size of larger filename suffix */
+
+const char *loader = "/usr/lib/kgzldr.o"; /* Default loader */
+int format; /* Output format */
+
+char *tname; /* Name of temporary file */
+
+static void cleanup(void);
+static void mk_fn(int, const char *, const char *, char *[]);
+static void usage(void);
+
+/*
+ * Compress a kernel.
+ */
+int
+main(int argc, char *argv[])
+{
+ static char *fn[FN_CNT];
+ struct kgz_hdr kh;
+ const char *output;
+ char *tmpdir;
+ int cflag, vflag, c;
+
+ tmpdir = getenv("TMPDIR");
+ if (asprintf(&tname, "%s/kgzXXXXXXXXXX",
+ tmpdir == NULL ? _PATH_TMP : tmpdir) == -1)
+ errx(1, "Out of memory");
+ output = NULL;
+ cflag = vflag = 0;
+ while ((c = getopt(argc, argv, "cvf:l:o:")) != -1)
+ switch (c) {
+ case 'c':
+ cflag = 1;
+ break;
+ case 'v':
+ vflag = 1;
+ break;
+ case 'f':
+ if (!strcmp(optarg, "aout"))
+ format = F_AOUT;
+ else if (!strcmp(optarg, "elf"))
+ format = F_ELF;
+ else
+ errx(1, "%s: Unknown format", optarg);
+ break;
+ case 'l':
+ loader = optarg;
+ break;
+ case 'o':
+ output = optarg;
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc != 1)
+ usage();
+ atexit(cleanup);
+ mk_fn(cflag, *argv, output, fn);
+ memset(&kh, 0, sizeof(kh));
+ if (fn[FN_SRC]) {
+ if (!format)
+ format = F_ELF;
+ kgzcmp(&kh, fn[FN_SRC], fn[FN_OBJ]);
+ }
+ if (!cflag)
+ kgzld(&kh, fn[FN_OBJ], fn[FN_KGZ]);
+ if (vflag)
+ printf("dload=%#x dsize=%#x isize=%#x entry=%#x nsize=%#x\n",
+ kh.dload, kh.dsize, kh.isize, kh.entry, kh.nsize);
+ return 0;
+}
+
+/*
+ * Clean up after processing.
+ */
+static void
+cleanup(void)
+{
+ if (tname)
+ unlink(tname);
+}
+
+/*
+ * Make the required filenames.
+ */
+static void
+mk_fn(int cflag, const char *f1, const char *f2, char *fn[])
+{
+ const char *p, *s;
+ size_t n;
+ int i, fd;
+
+ i = 0;
+ s = strrchr(f1, 0);
+ n = sizeof(SFX_OBJ) - 1;
+ if ((size_t)(s - f1) > n && !memcmp(s - n, SFX_OBJ, n)) {
+ s -= n;
+ i++;
+ }
+ fn[i++] = (char *)f1;
+ if (i == FN_OBJ && !cflag) {
+ if ((fd = mkstemp(tname)) == -1)
+ err(1, NULL);
+ close(fd);
+ fn[i++] = (char *)tname;
+ }
+ if (!(fn[i] = (char *)f2)) {
+ p = (p = strrchr(f1, '/')) ? p + 1 : f1;
+ n = (size_t)(s - p);
+ if (!(fn[i] = malloc(n + SFX_MAX)))
+ err(1, NULL);
+ memcpy(fn[i], p, n);
+ strcpy(fn[i] + n, i == FN_OBJ ? SFX_OBJ : SFX_KGZ);
+ }
+}
+
+/*
+ * Display usage information.
+ */
+static void
+usage(void)
+{
+ fprintf(stderr,
+ "usage: kgzip [-cv] [-f format] [-l file] [-o filename] file\n");
+ exit(1);
+}
diff --git a/usr.sbin/kgzip/kgzip.h b/usr.sbin/kgzip/kgzip.h
new file mode 100644
index 0000000..f04f2a4
--- /dev/null
+++ b/usr.sbin/kgzip/kgzip.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 1999 Global Technology Associates, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "kgz.h"
+
+#define F_AOUT 1 /* Format: a.out */
+#define F_ELF 2 /* Format: ELF32 */
+
+/* Used by I/O routines */
+struct iodesc {
+ const char *fname; /* File name */
+ int fd; /* File descriptor */
+};
+
+extern const char *loader; /* Default loader */
+extern int format; /* Output format */
+
+void kgzcmp(struct kgz_hdr *, const char *, const char *);
+void kgzld(struct kgz_hdr *, const char *, const char *);
+
+void xclose(const struct iodesc *);
+void xcopy(const struct iodesc *, const struct iodesc *, size_t, off_t);
+void xzero(const struct iodesc *, size_t);
+size_t xread(const struct iodesc *, void *, size_t, off_t);
+void xwrite(const struct iodesc *, const void *, size_t);
+void xseek(const struct iodesc *, off_t);
diff --git a/usr.sbin/kgzip/kgzld.c b/usr.sbin/kgzip/kgzld.c
new file mode 100644
index 0000000..4f6acc8
--- /dev/null
+++ b/usr.sbin/kgzip/kgzld.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 1999 Global Technology Associates, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "aouthdr.h"
+#include "elfhdr.h"
+#include "kgzip.h"
+
+#define align(x, y) (((x) + (y) - 1) & ~((y) - 1))
+
+/*
+ * Link KGZ file and loader.
+ */
+void
+kgzld(struct kgz_hdr * kh, const char *f1, const char *f2)
+{
+ char addr[16];
+ struct iodesc idi;
+ pid_t pid;
+ size_t n;
+ int status;
+
+ if (strcmp(kh->ident, "KGZ")) {
+ if ((idi.fd = open(idi.fname = f1, O_RDONLY)) == -1)
+ err(1, "%s", idi.fname);
+ if (!format) {
+ union {
+ struct exec ex;
+ Elf32_Ehdr ee;
+ } hdr;
+ n = xread(&idi, &hdr, sizeof(hdr), 0);
+ if (n >= sizeof(hdr.ee) && IS_ELF(hdr.ee))
+ format = F_ELF;
+ else if (n >= sizeof(hdr.ex) &&
+ N_GETMAGIC(hdr.ex) == OMAGIC)
+ format = F_AOUT;
+ if (!format)
+ errx(1, "%s: Format not supported", idi.fname);
+ }
+ n = xread(&idi, kh, sizeof(*kh),
+ format == F_AOUT ? sizeof(struct kgz_aouthdr0)
+ : sizeof(struct kgz_elfhdr));
+ xclose(&idi);
+ if (n != sizeof(*kh) || strcmp(kh->ident, "KGZ"))
+ errx(1, "%s: Invalid format", idi.fname);
+ }
+ sprintf(addr, "%#x", align(kh->dload + kh->dsize, 0x1000));
+ switch (pid = fork()) {
+ case -1:
+ err(1, NULL);
+ case 0:
+ if (format == F_AOUT)
+ execlp("ld", "ld", "-aout", "-Z", "-T", addr, "-o", f2,
+ loader, f1, (char *)NULL);
+ else
+ execlp("ld", "ld", "-Ttext", addr, "-o", f2, loader, f1,
+ (char *)NULL);
+ warn(NULL);
+ _exit(1);
+ default:
+ if ((pid = waitpid(pid, &status, 0)) == -1)
+ err(1, NULL);
+ if (WIFSIGNALED(status) || WEXITSTATUS(status))
+ exit(1);
+ }
+}
diff --git a/usr.sbin/kgzip/xio.c b/usr.sbin/kgzip/xio.c
new file mode 100644
index 0000000..64481ff
--- /dev/null
+++ b/usr.sbin/kgzip/xio.c
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 1999 Global Technology Associates, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <err.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "kgzip.h"
+
+/*
+ * Close a file.
+ */
+void
+xclose(const struct iodesc *id)
+{
+ if (close(id->fd))
+ err(1, "%s", id->fname);
+}
+
+/*
+ * Copy bytes from one file to another.
+ */
+void
+xcopy(const struct iodesc * idi, const struct iodesc * ido,
+ size_t nbyte, off_t offset)
+{
+ char buf[8192];
+ size_t n;
+
+ while (nbyte) {
+ if ((n = sizeof(buf)) > nbyte)
+ n = nbyte;
+ if (xread(idi, buf, n, offset) != n)
+ errx(1, "%s: Short read", idi->fname);
+ xwrite(ido, buf, n);
+ nbyte -= n;
+ offset = -1;
+ }
+}
+
+/*
+ * Write binary zeroes to a file.
+ */
+void
+xzero(const struct iodesc * id, size_t nbyte)
+{
+ char buf[8192];
+ size_t n;
+
+ memset(buf, 0, sizeof(buf));
+ while (nbyte) {
+ if ((n = sizeof(buf)) > nbyte)
+ n = nbyte;
+ xwrite(id, buf, n);
+ nbyte -= n;
+ }
+}
+
+/*
+ * Read from a file.
+ */
+size_t
+xread(const struct iodesc * id, void *buf, size_t nbyte, off_t offset)
+{
+ ssize_t n;
+
+ if (offset != -1 && lseek(id->fd, offset, SEEK_SET) != offset)
+ err(1, "%s", id->fname);
+ if ((n = read(id->fd, buf, nbyte)) == -1)
+ err(1, "%s", id->fname);
+ return (size_t)n;
+}
+
+/*
+ * Write to a file.
+ */
+void
+xwrite(const struct iodesc * id, const void *buf, size_t nbyte)
+{
+ ssize_t n;
+
+ if ((n = write(id->fd, buf, nbyte)) == -1)
+ err(1, "%s", id->fname);
+ if ((size_t)n != nbyte)
+ errx(1, "%s: Short write", id->fname);
+}
+
+/*
+ * Reposition within a file.
+ */
+void
+xseek(const struct iodesc *id, off_t offset)
+{
+ if (lseek(id->fd, offset, SEEK_SET) != offset)
+ err(1, "%s", id->fname);
+}
diff --git a/usr.sbin/kldxref/Makefile b/usr.sbin/kldxref/Makefile
new file mode 100644
index 0000000..a7ad8d2
--- /dev/null
+++ b/usr.sbin/kldxref/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= kldxref
+SRCS= kldxref.c ef.c
+WARNS?= 2
+MAN= kldxref.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/kldxref/ef.c b/usr.sbin/kldxref/ef.c
new file mode 100644
index 0000000..4437eff
--- /dev/null
+++ b/usr.sbin/kldxref/ef.c
@@ -0,0 +1,385 @@
+/*
+ * Copyright (c) 2000, Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/exec.h>
+#include <sys/queue.h>
+#include <sys/kernel.h>
+#include <sys/reboot.h>
+#include <sys/linker.h>
+#include <string.h>
+#include <machine/elf.h>
+#include <stand.h>
+#define FREEBSD_ELF
+#include <link.h>
+
+#include <err.h>
+
+#include "ef.h"
+
+static void ef_print_phdr(Elf_Phdr *);
+static u_long ef_get_offset(elf_file_t, Elf_Off);
+static int ef_parse_dynamic(elf_file_t);
+
+void
+ef_print_phdr(Elf_Phdr *phdr)
+{
+
+ if ((phdr->p_flags & PF_W) == 0) {
+ printf("text=0x%lx ", (long)phdr->p_filesz);
+ } else {
+ printf("data=0x%lx", (long)phdr->p_filesz);
+ if (phdr->p_filesz < phdr->p_memsz)
+ printf("+0x%lx", (long)(phdr->p_memsz - phdr->p_filesz));
+ printf(" ");
+ }
+}
+
+u_long
+ef_get_offset(elf_file_t ef, Elf_Off off)
+{
+ Elf_Phdr *ph;
+ int i;
+
+ for (i = 0; i < ef->ef_nsegs; i++) {
+ ph = ef->ef_segs[i];
+ if (off >= ph->p_vaddr && off < ph->p_vaddr + ph->p_memsz) {
+ return ph->p_offset + (off - ph->p_vaddr);
+ }
+ }
+ return 0;
+}
+
+/*
+ * next three functions copied from link_elf.c
+ */
+static unsigned long
+elf_hash(const char *name)
+{
+ const unsigned char *p = (const unsigned char *) name;
+ unsigned long h = 0;
+ unsigned long g;
+
+ while (*p != '\0') {
+ h = (h << 4) + *p++;
+ if ((g = h & 0xf0000000) != 0)
+ h ^= g >> 24;
+ h &= ~g;
+ }
+ return h;
+}
+
+int
+ef_lookup_symbol(elf_file_t ef, const char* name, Elf_Sym** sym)
+{
+ unsigned long symnum;
+ Elf_Sym* symp;
+ char *strp;
+ unsigned long hash;
+
+ /* First, search hashed global symbols */
+ hash = elf_hash(name);
+ symnum = ef->ef_buckets[hash % ef->ef_nbuckets];
+
+ while (symnum != STN_UNDEF) {
+ if (symnum >= ef->ef_nchains) {
+ warnx("ef_lookup_symbol: file %s have corrupted symbol table\n",
+ ef->ef_name);
+ return ENOENT;
+ }
+
+ symp = ef->ef_symtab + symnum;
+ if (symp->st_name == 0) {
+ warnx("ef_lookup_symbol: file %s have corrupted symbol table\n",
+ ef->ef_name);
+ return ENOENT;
+ }
+
+ strp = ef->ef_strtab + symp->st_name;
+
+ if (strcmp(name, strp) == 0) {
+ if (symp->st_shndx != SHN_UNDEF ||
+ (symp->st_value != 0 &&
+ ELF_ST_TYPE(symp->st_info) == STT_FUNC)) {
+ *sym = symp;
+ return 0;
+ } else
+ return ENOENT;
+ }
+
+ symnum = ef->ef_chains[symnum];
+ }
+
+ return ENOENT;
+}
+
+int
+ef_parse_dynamic(elf_file_t ef)
+{
+ Elf_Dyn *dp;
+ Elf_Hashelt hashhdr[2];
+/* int plttype = DT_REL;*/
+ int error;
+
+ for (dp = ef->ef_dyn; dp->d_tag != DT_NULL; dp++) {
+ switch (dp->d_tag) {
+ case DT_HASH:
+ error = ef_read(ef, ef_get_offset(ef, dp->d_un.d_ptr),
+ sizeof(hashhdr), hashhdr);
+ if (error) {
+ warnx("can't read hash header (%lx)",
+ ef_get_offset(ef, dp->d_un.d_ptr));
+ return error;
+ }
+ ef->ef_nbuckets = hashhdr[0];
+ ef->ef_nchains = hashhdr[1];
+ error = ef_read_entry(ef, -1,
+ (hashhdr[0] + hashhdr[1]) * sizeof(Elf_Hashelt),
+ (void**)&ef->ef_hashtab);
+ if (error) {
+ warnx("can't read hash table");
+ return error;
+ }
+ ef->ef_buckets = ef->ef_hashtab;
+ ef->ef_chains = ef->ef_buckets + ef->ef_nbuckets;
+ break;
+ case DT_STRTAB:
+ ef->ef_stroff = dp->d_un.d_ptr;
+ break;
+ case DT_STRSZ:
+ ef->ef_strsz = dp->d_un.d_val;
+ break;
+ case DT_SYMTAB:
+ ef->ef_symoff = dp->d_un.d_ptr;
+ break;
+ case DT_SYMENT:
+ if (dp->d_un.d_val != sizeof(Elf_Sym))
+ return EFTYPE;
+ break;
+ }
+ }
+ if (ef->ef_symoff == 0) {
+ warnx("%s: no .dynsym section found\n", ef->ef_name);
+ return EFTYPE;
+ }
+ if (ef->ef_stroff == 0) {
+ warnx("%s: no .dynstr section found\n", ef->ef_name);
+ return EFTYPE;
+ }
+ if (ef_read_entry(ef, ef_get_offset(ef, ef->ef_symoff),
+ ef->ef_nchains * sizeof(Elf_Sym),
+ (void**)&ef->ef_symtab) != 0) {
+ if (ef->ef_verbose)
+ warnx("%s: can't load .dynsym section (0x%lx)",
+ ef->ef_name, (long)ef->ef_symoff);
+ return EIO;
+ }
+ if (ef_read_entry(ef, ef_get_offset(ef, ef->ef_stroff), ef->ef_strsz,
+ (void**)&ef->ef_strtab) != 0) {
+ warnx("can't load .dynstr section");
+ return EIO;
+ }
+ return 0;
+}
+
+int
+ef_read(elf_file_t ef, Elf_Off offset, size_t len, void*dest)
+{
+ ssize_t r;
+
+ if (offset != (Elf_Off)-1) {
+ if (lseek(ef->ef_fd, offset, SEEK_SET) == -1)
+ return EIO;
+ }
+
+ r = read(ef->ef_fd, dest, len);
+ if (r != -1 && (size_t)r == len)
+ return 0;
+ else
+ return EIO;
+}
+
+int
+ef_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void**ptr)
+{
+ int error;
+
+ *ptr = malloc(len);
+ if (*ptr == NULL)
+ return ENOMEM;
+ error = ef_read(ef, offset, len, *ptr);
+ if (error)
+ free(*ptr);
+ return error;
+}
+
+int
+ef_seg_read(elf_file_t ef, Elf_Off offset, size_t len, void*dest)
+{
+ u_long ofs = ef_get_offset(ef, offset);
+
+ if (ofs == 0) {
+ if (ef->ef_verbose)
+ warnx("ef_seg_read(%s): zero offset (%lx:%ld)",
+ ef->ef_name, (long)offset, ofs);
+ return EFAULT;
+ }
+ return ef_read(ef, ofs, len, dest);
+}
+
+int
+ef_seg_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void**ptr)
+{
+ int error;
+
+ *ptr = malloc(len);
+ if (*ptr == NULL)
+ return ENOMEM;
+ error = ef_seg_read(ef, offset, len, *ptr);
+ if (error)
+ free(*ptr);
+ return error;
+}
+
+int
+ef_open(const char *filename, elf_file_t ef, int verbose)
+{
+ Elf_Ehdr *hdr;
+ int fd;
+ int error;
+ int phlen, res;
+ int nsegs;
+ Elf_Phdr *phdr, *phdyn, *phphdr, *phlimit;
+
+ bzero(ef, sizeof(*ef));
+ if (filename == NULL)
+ return EFTYPE;
+ ef->ef_verbose = verbose;
+ if ((fd = open(filename, O_RDONLY)) == -1)
+ return errno;
+ ef->ef_fd = fd;
+ ef->ef_name = strdup(filename);
+ hdr = (Elf_Ehdr *)&ef->ef_hdr;
+ do {
+ res = read(fd, hdr, sizeof(*hdr));
+ error = EFTYPE;
+ if (res != sizeof(*hdr))
+ break;
+ if (!IS_ELF(*hdr))
+ break;
+ if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS ||
+ hdr->e_ident[EI_DATA] != ELF_TARG_DATA ||
+ hdr->e_ident[EI_VERSION] != EV_CURRENT ||
+ hdr->e_version != EV_CURRENT ||
+ hdr->e_machine != ELF_TARG_MACH ||
+ hdr->e_phentsize != sizeof(Elf_Phdr))
+ break;
+ phlen = hdr->e_phnum * sizeof(Elf_Phdr);
+ if (ef_read_entry(ef, hdr->e_phoff, phlen,
+ (void**)&ef->ef_ph) != 0)
+ break;
+ phdr = ef->ef_ph;
+ phlimit = phdr + hdr->e_phnum;
+ nsegs = 0;
+ phdyn = NULL;
+ phphdr = NULL;
+ while (phdr < phlimit) {
+ if (verbose > 1)
+ ef_print_phdr(phdr);
+ switch (phdr->p_type) {
+ case PT_LOAD:
+ if (nsegs == 2) {
+ warnx("%s: too many sections",
+ filename);
+ break;
+ }
+ ef->ef_segs[nsegs++] = phdr;
+ break;
+ case PT_PHDR:
+ phphdr = phdr;
+ break;
+ case PT_DYNAMIC:
+ phdyn = phdr;
+ break;
+ }
+ phdr++;
+ }
+ if (verbose > 1)
+ printf("\n");
+ ef->ef_nsegs = nsegs;
+ if (phdyn == NULL) {
+ warnx("file isn't dynamically-linked");
+ break;
+ }
+ if (ef_read_entry(ef, phdyn->p_offset,
+ phdyn->p_filesz, (void**)&ef->ef_dyn) != 0) {
+ printf("ef_read_entry failed\n");
+ break;
+ }
+ error = ef_parse_dynamic(ef);
+ if (error)
+ break;
+ if (hdr->e_type == ET_DYN) {
+ ef->ef_type = EFT_KLD;
+/* pad = (u_int)dest & PAGE_MASK;
+ if (pad)
+ dest += PAGE_SIZE - pad;*/
+ error = 0;
+ } else if (hdr->e_type == ET_EXEC) {
+/* dest = hdr->e_entry;
+ if (dest == 0)
+ break;*/
+ ef->ef_type = EFT_KERNEL;
+ error = 0;
+ } else
+ break;
+ } while(0);
+ if (error) {
+ ef_close(ef);
+ if (ef->ef_verbose)
+ warnc(error, "elf_open(%s)", filename);
+ }
+ return error;
+}
+
+int
+ef_close(elf_file_t ef)
+{
+ close(ef->ef_fd);
+/* if (ef->ef_fpage)
+ free(ef->ef_fpage);*/
+ if (ef->ef_name)
+ free(ef->ef_name);
+ return 0;
+}
diff --git a/usr.sbin/kldxref/ef.h b/usr.sbin/kldxref/ef.h
new file mode 100644
index 0000000..1609bb8
--- /dev/null
+++ b/usr.sbin/kldxref/ef.h
@@ -0,0 +1,43 @@
+/* $FreeBSD$ */
+
+#ifndef _EF_H_
+#define _EF_H_
+
+#define EFT_KLD 1
+#define EFT_KERNEL 2
+
+typedef struct elf_file {
+ char* ef_name;
+ Elf_Phdr * ef_ph;
+ int ef_fd;
+ int ef_type;
+ Elf_Ehdr ef_hdr;
+ void* ef_fpage; /* First block of the file */
+ int ef_fplen; /* length of first block */
+ Elf_Dyn* ef_dyn; /* Symbol table etc. */
+ Elf_Hashelt ef_nbuckets;
+ Elf_Hashelt ef_nchains;
+ Elf_Hashelt* ef_buckets;
+ Elf_Hashelt* ef_chains;
+ Elf_Hashelt* ef_hashtab;
+ Elf_Off ef_stroff;
+ caddr_t ef_strtab;
+ int ef_strsz;
+ Elf_Off ef_symoff;
+ Elf_Sym* ef_symtab;
+ int ef_nsegs;
+ Elf_Phdr * ef_segs[2];
+ int ef_verbose;
+} *elf_file_t;
+
+__BEGIN_DECLS
+int ef_open(const char *, elf_file_t, int);
+int ef_close(elf_file_t ef);
+int ef_read(elf_file_t ef, Elf_Off offset, size_t len, void* dest);
+int ef_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void **ptr);
+int ef_seg_read(elf_file_t ef, Elf_Off offset, size_t len, void *dest);
+int ef_seg_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void**ptr);
+int ef_lookup_symbol(elf_file_t ef, const char* name, Elf_Sym** sym);
+__END_DECLS
+
+#endif /* _EF_H_*/
diff --git a/usr.sbin/kldxref/fileformat b/usr.sbin/kldxref/fileformat
new file mode 100644
index 0000000..2eddcca
--- /dev/null
+++ b/usr.sbin/kldxref/fileformat
@@ -0,0 +1,40 @@
+$FreeBSD$
+
+ linker.hints file consists from the one or more records. First record of
+file is special and determines its version:
+
+int version;
+
+ All subsequent records have following format:
+
+struct record {
+ int length; /* length of following data */
+ char data[length];
+};
+
+ Each record is aligned on sizeof(int) boundary. First integer of the field
+'data' determines its type:
+
+struct data {
+ int type; /* type of data. currently MTD_* values */
+};
+
+ The rest of record depends on the type.
+
+struct string {
+ int length; /* length of string */
+ char val[]; /* string itself (no terminating zero) */
+};
+
+struct data_mdt_version {
+ int type = MDT_VERSION;
+ struct string modname;
+ int version;
+ struct string kldname;
+};
+
+struct data_mdt_module {
+ int type = MDT_VERSION;
+ struct string modname;
+ struct string kldname;
+};
diff --git a/usr.sbin/kldxref/kldxref.8 b/usr.sbin/kldxref/kldxref.8
new file mode 100644
index 0000000..bcbb3ed
--- /dev/null
+++ b/usr.sbin/kldxref/kldxref.8
@@ -0,0 +1,95 @@
+.\"-
+.\" Copyright (c) 2001 Boris Popov
+.\" Copyright (c) 2001 Dag-Erling Coïdan Smørgrav
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd October 9, 2001
+.Dt KLDXREF 8
+.Os
+.Sh NAME
+.Nm kldxref
+.Nd generate hints for the kernel loader
+.Sh SYNOPSIS
+.Nm
+.Op Fl Rdv
+.Op Fl f Ar file
+.Op Ar path ...
+.Sh DESCRIPTION
+The
+.Nm
+command is used to generate hint files which list modules, their
+version numbers, and the files that contain them.
+These hints are used by the kernel loader to determine where to find a
+particular KLD module.
+.Pp
+A separate hint file is generated for each directory listed on the
+command line that contains modules.
+If no hint records are generated for a particular directory, no hint
+file is created, and the preexisting hint file (if there was one in
+that directory) is removed.
+.Pp
+The following options are available:
+.Bl -tag -width ".Fl f Ar file"
+.It Fl R
+Recurse into subdirectories.
+.It Fl d
+Do not generate a hint file, but print module metadata on standard
+output.
+.It Fl f Ar file
+Specify a different name for the hint files than
+.Pa linker.hints .
+.It Fl v
+Operate in verbose mode.
+.El
+.Sh EXAMPLES
+To build hint files for both standard and add-on modules:
+.Pp
+.Dl "kldxref /boot/kernel /modules"
+.Pp
+To build hint files for all installed kernels:
+.Pp
+.Dl "kldxref -R /boot"
+.Sh SEE ALSO
+.Xr kld 4 ,
+.Xr kldconfig 8 ,
+.Xr kldload 8 ,
+.Xr kldstat 8 ,
+.Xr kldunload 8
+.Sh HISTORY
+The
+.Nm
+command first appeared in
+.Fx 5.0 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+command was implemented by
+.An Boris Popov Aq bp@FreeBSD.org .
+This manual page was written by
+.An Boris Popov Aq bp@FreeBSD.org
+and
+.An Dag-Erling Co\(:idan Sm\(/orgrav Aq des@FreeBSD.org .
diff --git a/usr.sbin/kldxref/kldxref.c b/usr.sbin/kldxref/kldxref.c
new file mode 100644
index 0000000..e631ecc
--- /dev/null
+++ b/usr.sbin/kldxref/kldxref.c
@@ -0,0 +1,346 @@
+/*
+ * Copyright (c) 2000, Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/exec.h>
+#include <sys/queue.h>
+#include <sys/kernel.h>
+#include <sys/reboot.h>
+#include <sys/linker.h>
+#include <sys/stat.h>
+#include <sys/module.h>
+#define FREEBSD_ELF
+#include <link.h>
+#include <err.h>
+#include <fts.h>
+#include <string.h>
+#include <machine/elf.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "ef.h"
+
+#define MAXRECSIZE 1024
+#define check(val) if ((error = (val)) != 0) break
+
+#ifndef min
+#define min(a,b) (((a)<(b)) ? (a) : (b))
+#endif
+
+struct mod_info {
+ char* mi_name;
+ int mi_ver;
+ SLIST_ENTRY(mod_info) mi_next;
+};
+
+#ifdef notnow
+struct kld_info {
+ char* k_filename;
+ SLIST_HEAD(mod_list_head, mod_info) k_modules;
+ SLIST_ENTRY(kld_info) k_next;
+};
+
+SLIST_HEAD(kld_list_head, kld_info) kldlist;
+#endif
+
+static int dflag, verbose;
+
+FILE *fxref;
+
+static const char *xref_file = "linker.hints";
+
+static char recbuf[MAXRECSIZE];
+static int recpos, reccnt;
+
+void maketempfile(char *, const char *);
+static void usage(void);
+
+static void
+intalign(void)
+{
+ recpos = (recpos + sizeof(int) - 1) & ~(sizeof(int) - 1);
+}
+
+static void
+record_start(void)
+{
+ recpos = 0;
+ memset(recbuf, 0, MAXRECSIZE);
+}
+
+static int
+record_end(void)
+{
+ if (dflag || recpos == 0)
+ return 0;
+ reccnt++;
+ intalign();
+ fwrite(&recpos, sizeof(recpos), 1, fxref);
+ return fwrite(recbuf, recpos, 1, fxref) != 1 ? errno : 0;
+}
+
+static int
+record_buf(const void *buf, int size)
+{
+ if (MAXRECSIZE - recpos < size)
+ errx(1, "record buffer overflow");
+ memcpy(recbuf + recpos, buf, size);
+ recpos += size;
+ return 0;
+}
+
+static int
+record_int(int val)
+{
+ intalign();
+ return record_buf(&val, sizeof(val));
+}
+
+static int
+record_byte(u_char val)
+{
+ return record_buf(&val, sizeof(val));
+}
+
+static int
+record_string(const char *str)
+{
+ int len = strlen(str);
+ int error;
+
+ if (dflag)
+ return 0;
+ error = record_byte(len);
+ if (error)
+ return error;
+ return record_buf(str, len);
+}
+
+static int
+parse_entry(struct mod_metadata *md, const char *cval,
+ struct elf_file *ef, const char *kldname)
+{
+ struct mod_depend mdp;
+ struct mod_version mdv;
+ Elf_Off data = (Elf_Off)md->md_data;
+ int error = 0;
+
+ record_start();
+ switch (md->md_type) {
+ case MDT_DEPEND:
+ if (!dflag)
+ break;
+ check(ef_seg_read(ef, data, sizeof(mdp), (void**)&mdp));
+ printf(" depends on %s.%d (%d,%d)\n", cval,
+ mdp.md_ver_preferred, mdp.md_ver_minimum, mdp.md_ver_maximum);
+ break;
+ case MDT_VERSION:
+ check(ef_seg_read(ef, data, sizeof(mdv), (void**)&mdv));
+ record_int(MDT_VERSION);
+ record_string(cval);
+ record_int(mdv.mv_version);
+ record_string(kldname);
+ if (!dflag)
+ break;
+ printf(" interface %s.%d\n", cval, mdv.mv_version);
+ break;
+ case MDT_MODULE:
+ record_int(MDT_MODULE);
+ record_string(cval);
+ record_string(kldname);
+ if (!dflag)
+ break;
+ printf(" module %s\n", cval);
+ break;
+ default:
+ warnx("unknown metdata record %d in file %s", md->md_type, kldname);
+ }
+ if (!error)
+ record_end();
+ return error;
+}
+
+static int
+read_kld(char *filename, char *kldname)
+{
+ struct mod_metadata md;
+ struct elf_file ef;
+/* struct kld_info *kip;
+ struct mod_info *mip;*/
+ void **p, **orgp;
+ int error, nmlen;
+ long start, finish, entries;
+ Elf_Sym *sym;
+ char kldmodname[MAXMODNAME + 1], cval[MAXMODNAME + 1], *cp;
+
+ if (verbose || dflag)
+ printf("%s\n", filename);
+ error = ef_open(filename, &ef, verbose);
+ if (error)
+ return error;
+ if (ef.ef_type != EFT_KLD && ef.ef_type != EFT_KERNEL) {
+ ef_close(&ef);
+ return 0;
+ }
+ if (!dflag) {
+ cp = strrchr(kldname, '.');
+ nmlen = cp ? min(MAXMODNAME, cp - kldname) :
+ min(MAXMODNAME, strlen(kldname));
+ strncpy(kldmodname, kldname, nmlen);
+ kldmodname[nmlen] = '\0';
+/* fprintf(fxref, "%s:%s:%d\n", kldmodname, kldname, 0);*/
+ }
+ do {
+ check(ef_lookup_symbol(&ef, "__start_set_" MDT_SETNAME, &sym));
+ start = sym->st_value;
+ check(ef_lookup_symbol(&ef, "__stop_set_" MDT_SETNAME, &sym));
+ finish = sym->st_value;
+ entries = (finish - start) / sizeof(void *);
+ check(ef_seg_read_entry(&ef, start, sizeof(*p) * entries, (void**)&p));
+ orgp = p;
+ while(entries--) {
+ check(ef_seg_read(&ef, (Elf_Off)*p, sizeof(md), &md));
+ p++;
+ check(ef_seg_read(&ef, (Elf_Off)md.md_cval, sizeof(cval), cval));
+ cval[MAXMODNAME] = '\0';
+ parse_entry(&md, cval, &ef, kldname);
+ }
+ if (error)
+ warnc(error, "error while reading %s", filename);
+ free(orgp);
+ } while(0);
+ ef_close(&ef);
+ return error;
+}
+
+void
+maketempfile(char *dest, const char *root)
+{
+ char *p;
+
+ strncpy(dest, root, MAXPATHLEN - 1);
+ dest[MAXPATHLEN] = '\0';
+
+ if ((p = strrchr(dest, '/')) != 0)
+ p++;
+ else
+ p = dest;
+ strcpy(p, "lhint.XXXXXX");
+ if (mkstemp(dest) == -1)
+ err(1, "%s", dest);
+}
+
+static char xrefname[MAXPATHLEN], tempname[MAXPATHLEN];
+
+int
+main(int argc, char *argv[])
+{
+ FTS *ftsp;
+ FTSENT *p;
+ int opt, fts_options, ival;
+
+ fts_options = FTS_PHYSICAL;
+/* SLIST_INIT(&kldlist);*/
+
+ while ((opt = getopt(argc, argv, "Rdf:v")) != -1) {
+ switch (opt) {
+ case 'd':
+ dflag = 1;
+ break;
+ case 'f':
+ xref_file = optarg;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 'R':
+ fts_options |= FTS_COMFOLLOW;
+ break;
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+ if (argc - optind < 1)
+ usage();
+ argc -= optind;
+ argv += optind;
+
+ ftsp = fts_open(argv, fts_options, 0);
+ if (ftsp == NULL)
+ exit(1);
+
+ for (;;) {
+ p = fts_read(ftsp);
+ if ((p == NULL || p->fts_info == FTS_D) && !dflag && fxref) {
+ fclose(fxref);
+ if (reccnt) {
+ rename(tempname, xrefname);
+ } else {
+ unlink(tempname);
+ unlink(xrefname);
+ }
+ }
+ if (p == NULL)
+ break;
+ if (p && p->fts_info == FTS_D && !dflag) {
+ snprintf(xrefname, sizeof(xrefname), "%s/%s",
+ ftsp->fts_path, xref_file);
+ maketempfile(tempname, ftsp->fts_path);
+ fxref = fopen(tempname, "w+t");
+ if (fxref == NULL)
+ err(1, "can't create %s", tempname);
+ ival = 1;
+ fwrite(&ival, sizeof(ival), 1, fxref);
+ reccnt = 0;
+ }
+ if (p->fts_info != FTS_F)
+ continue;
+ read_kld(p->fts_path, p->fts_name);
+ }
+ fts_close(ftsp);
+ return 0;
+}
+
+static void
+usage(void)
+{
+
+ fprintf(stderr, "%s\n",
+ "usage: kldxref [-Rdv] [-f hintfile] path [path..]"
+ );
+ exit(1);
+}
diff --git a/usr.sbin/lastlogin/Makefile b/usr.sbin/lastlogin/Makefile
new file mode 100644
index 0000000..017aadf
--- /dev/null
+++ b/usr.sbin/lastlogin/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= lastlogin
+MAN= lastlogin.8
+
+WARNS?= 2
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/lastlogin/lastlogin.8 b/usr.sbin/lastlogin/lastlogin.8
new file mode 100644
index 0000000..f2792b8
--- /dev/null
+++ b/usr.sbin/lastlogin/lastlogin.8
@@ -0,0 +1,76 @@
+.\" $FreeBSD$
+.\" $NetBSD: lastlogin.8,v 1.3 1998/02/06 06:20:39 perry Exp $
+.\"
+.\" Copyright (c) 1996 John M. Vinopal
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed for the NetBSD Project
+.\" by John M. Vinopal.
+.\" 4. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+.\" AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+.\" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd January 11, 1996
+.Dt LASTLOGIN 8
+.Os
+.Sh NAME
+.Nm lastlogin
+.Nd indicate last login time of users
+.Sh SYNOPSIS
+.Nm
+.Op Ar user ...
+.Sh DESCRIPTION
+.Nm
+will list the last login session of each specified
+.Ar user ,
+or for all users by default. Each line of output contains
+the user name, the tty from which the session was conducted, any
+hostname, and the start time for the session.
+.Pp
+If more than one
+.Ar user
+is given, the session information for each user is printed in
+the order given on the command line. Otherwise, information
+for all users is printed, sorted by uid.
+.Pp
+.Nm
+differs from
+.Xr last 1
+in that it only prints information regarding the very last login session.
+The last login database is never turned over or deleted in standard usage.
+.Sh FILES
+.Bl -tag -width /var/log/lastlog -compact
+.It Pa /var/log/lastlog
+last login database
+.El
+.Sh SEE ALSO
+.Xr last 1 ,
+.Xr lastlog 5 ,
+.Xr ac 8
+.Sh AUTHORS
+.An John M. Vinopal
+wrote this program in January 1996 and contributed it
+to the
+.Nx
+project.
diff --git a/usr.sbin/lastlogin/lastlogin.c b/usr.sbin/lastlogin/lastlogin.c
new file mode 100644
index 0000000..9f08377
--- /dev/null
+++ b/usr.sbin/lastlogin/lastlogin.c
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 1996 John M. Vinopal
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the NetBSD Project
+ * by John M. Vinopal.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$FreeBSD$");
+__RCSID("$NetBSD: lastlogin.c,v 1.4 1998/02/03 04:45:35 perry Exp $");
+#endif
+
+#include <sys/types.h>
+#include <err.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <timeconv.h>
+#include <utmp.h>
+#include <unistd.h>
+
+static const char *logfile = _PATH_LASTLOG;
+
+ int main __P((int, char **));
+static void output __P((struct passwd *, struct lastlog *));
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ch, i;
+ FILE *fp;
+ struct passwd *passwd;
+ struct lastlog last;
+
+ while ((ch = getopt(argc, argv, "")) != -1) {
+ usage();
+ }
+
+ fp = fopen(logfile, "r");
+ if (fp == NULL)
+ err(1, "%s", logfile);
+
+ setpassent(1); /* Keep passwd file pointers open */
+
+ /* Process usernames given on the command line. */
+ if (argc > 1) {
+ long offset;
+ for (i = 1; i < argc; ++i) {
+ if ((passwd = getpwnam(argv[i])) == NULL) {
+ warnx("user '%s' not found", argv[i]);
+ continue;
+ }
+ /* Calculate the offset into the lastlog file. */
+ offset = (long)(passwd->pw_uid * sizeof(last));
+ if (fseek(fp, offset, SEEK_SET)) {
+ warn("fseek error");
+ continue;
+ }
+ if (fread(&last, sizeof(last), 1, fp) != 1) {
+ warnx("fread error on '%s'", passwd->pw_name);
+ clearerr(fp);
+ continue;
+ }
+ output(passwd, &last);
+ }
+ }
+ /* Read all lastlog entries, looking for active ones */
+ else {
+ for (i = 0; fread(&last, sizeof(last), 1, fp) == 1; i++) {
+ if (last.ll_time == 0)
+ continue;
+ if ((passwd = getpwuid((uid_t)i)) != NULL)
+ output(passwd, &last);
+ }
+ if (ferror(fp))
+ warnx("fread error");
+ }
+
+ setpassent(0); /* Close passwd file pointers */
+
+ fclose(fp);
+ exit(0);
+}
+
+/* Duplicate the output of last(1) */
+static void
+output(p, l)
+ struct passwd *p;
+ struct lastlog *l;
+{
+ time_t t = _int_to_time(l->ll_time);
+ printf("%-*.*s %-*.*s %-*.*s %s",
+ UT_NAMESIZE, UT_NAMESIZE, p->pw_name,
+ UT_LINESIZE, UT_LINESIZE, l->ll_line,
+ UT_HOSTSIZE, UT_HOSTSIZE, l->ll_host,
+ (l->ll_time) ? ctime(&t) : "Never logged in\n");
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: lastlogin [user ...]\n");
+ exit(1);
+}
diff --git a/usr.sbin/lpr/Makefile b/usr.sbin/lpr/Makefile
new file mode 100644
index 0000000..8581271
--- /dev/null
+++ b/usr.sbin/lpr/Makefile
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+MAINTAINER= wollman@FreeBSD.org
+MAINTAINER+= gad@FreeBSD.org
+
+SUBDIR= common_source chkprintcap lp lpc lpd lpq lpr lprm lptest pac \
+ filters filters.ru SMM.doc
+
+# Questions/ideas for lpr & friends could also be sent to:
+# freebsd-print@bostonradio.org
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/lpr/Makefile.inc b/usr.sbin/lpr/Makefile.inc
new file mode 100644
index 0000000..bd8272f
--- /dev/null
+++ b/usr.sbin/lpr/Makefile.inc
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+CFLAGS+= -DINET6
+CWARNFLAGS= ${BDECFLAGS}
+
+.if exists(${.OBJDIR}/../common_source)
+LIBLPR= ${.OBJDIR}/../common_source/liblpr.a
+.else
+LIBLPR= ${.CURDIR}/../common_source/liblpr.a
+.endif
+
+.include "../Makefile.inc"
diff --git a/usr.sbin/lpr/SMM.doc/0.t b/usr.sbin/lpr/SMM.doc/0.t
new file mode 100644
index 0000000..65ecd4e
--- /dev/null
+++ b/usr.sbin/lpr/SMM.doc/0.t
@@ -0,0 +1,68 @@
+.\" Copyright (c) 1983, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)0.t 8.1 (Berkeley) 6/8/93
+.\"
+.if n .ND
+.TL
+4.3BSD Line Printer Spooler Manual
+.EH 'SMM:7-%''4.3BSD Line Printer Spooler Manual'
+.OH '4.3BSD Line Printer Spooler Manual''SMM:7-%'
+.AU
+Ralph Campbell
+.AI
+Computer Systems Research Group
+Computer Science Division
+Department of Electrical Engineering and Computer Science
+University of California, Berkeley
+Berkeley, CA 94720
+.AB
+.FS
+* UNIX is a trademark of Bell Laboratories.
+.FE
+This document describes the structure and installation procedure
+for the line printer spooling system
+developed for the 4.3BSD version
+of the UNIX* operating system.
+.de D?
+.ie \\n(.$>1 Revised \\$1 \\$2 \\$3
+.el DRAFT of \n(mo/\n(dy/\n(yr
+..
+.sp 2
+.LP
+.D? June 8, 1993
+.AE
+.de IR
+\fI\\$1\fP\\$2
+..
+.de DT
+.TA 8 16 24 32 40 48 56 64 72 80
+..
diff --git a/usr.sbin/lpr/SMM.doc/1.t b/usr.sbin/lpr/SMM.doc/1.t
new file mode 100644
index 0000000..1d34e9e
--- /dev/null
+++ b/usr.sbin/lpr/SMM.doc/1.t
@@ -0,0 +1,77 @@
+.\" Copyright (c) 1983, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)1.t 8.1 (Berkeley) 6/8/93
+.\"
+.NH 1
+Overview
+.PP
+The line printer system supports:
+.IP \(bu 3
+multiple printers,
+.IP \(bu 3
+multiple spooling queues,
+.IP \(bu 3
+both local and remote
+printers, and
+.IP \(bu 3
+printers attached via serial lines that require
+line initialization such as the baud rate.
+.LP
+Raster output devices
+such as a Varian or Versatec, and laser printers such as an Imagen,
+are also supported by the line printer system.
+.PP
+The line printer system consists mainly of the
+following files and commands:
+.DS
+.TS
+l l.
+/etc/printcap printer configuration and capability data base
+/usr/lib/lpd line printer daemon, does all the real work
+/usr/ucb/lpr program to enter a job in a printer queue
+/usr/ucb/lpq spooling queue examination program
+/usr/ucb/lprm program to delete jobs from a queue
+/etc/lpc program to administer printers and spooling queues
+/dev/printer socket on which lpd listens
+.TE
+.DE
+The file /etc/printcap is a master data base describing line
+printers directly attached to a machine and, also, printers
+accessible across a network. The manual page entry
+.IR printcap (5)
+provides the authoritative definition of
+the format of this data base, as well as
+specifying default values for important items
+such as the directory in which spooling is performed.
+This document introduces some of the
+information that may be placed
+.IR printcap .
diff --git a/usr.sbin/lpr/SMM.doc/2.t b/usr.sbin/lpr/SMM.doc/2.t
new file mode 100644
index 0000000..9da2ae2
--- /dev/null
+++ b/usr.sbin/lpr/SMM.doc/2.t
@@ -0,0 +1,141 @@
+.\" Copyright (c) 1983, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)2.t 8.1 (Berkeley) 6/8/93
+.\"
+.NH 1
+Commands
+.NH 2
+lpd \- line printer daemon
+.PP
+The program
+.IR lpd (8),
+usually invoked at boot time from the /etc/rc file, acts as
+a master server for coordinating and controlling
+the spooling queues configured in the printcap file.
+When
+.I lpd
+is started it makes a single pass through the
+.I printcap
+database restarting any printers that have jobs.
+In normal operation
+.I lpd
+listens for service requests on multiple sockets,
+one in the UNIX domain (named ``/dev/printer'') for
+local requests, and one in the Internet domain
+(under the ``printer'' service specification)
+for requests for printer access from off machine;
+see \fIsocket\fP\|(2) and \fIservices\fP\|(5)
+for more information on sockets and service
+specifications, respectively.
+.I Lpd
+spawns a copy of itself to process the request; the master daemon
+continues to listen for new requests.
+.PP
+Clients communicate with
+.I lpd
+using a simple transaction oriented protocol.
+Authentication of remote clients is done based
+on the ``privilege port'' scheme employed by
+\fIrshd\fP\|(8C) and \fIrcmd\fP\|(3X).
+The following table shows the requests
+understood by
+.IR lpd .
+In each request the first byte indicates the
+``meaning'' of the request, followed by the name
+of the printer to which it should be applied. Additional
+qualifiers may follow, depending on the request.
+.DS
+.TS
+l l.
+Request Interpretation
+_
+^Aprinter\en check the queue for jobs and print any found
+^Bprinter\en receive and queue a job from another machine
+^Cprinter [users ...] [jobs ...]\en return short list of current queue state
+^Dprinter [users ...] [jobs ...]\en return long list of current queue state
+^Eprinter person [users ...] [jobs ...]\en remove jobs from a queue
+.TE
+.DE
+.PP
+The \fIlpr\fP\|(1) command
+is used by users to enter a print job in a local queue and to notify
+the local
+.I lpd
+that there are new jobs in the spooling area.
+.I Lpd
+either schedules the job to be printed locally, or if
+printing remotely, attempts to forward
+the job to the appropriate machine.
+If the printer cannot be opened or the destination
+machine is unreachable, the job will remain queued until it is
+possible to complete the work.
+.NH 2
+lpq \- show line printer queue
+.PP
+The \fIlpq\fP\|(1)
+program works recursively backwards displaying the queue of the machine with
+the printer and then the queue(s) of the machine(s) that lead to it.
+.I Lpq
+has two forms of output: in the default, short, format it
+gives a single line of output per queued job; in the long
+format it shows the list of files, and their sizes, that
+comprise a job.
+.NH 2
+lprm \- remove jobs from a queue
+.PP
+The \fIlprm\fP\|(1) command deletes jobs from a spooling
+queue. If necessary, \fIlprm\fP will first kill off a
+running daemon that is servicing the queue and restart
+it after the required files are removed. When removing
+jobs destined for a remote printer, \fIlprm\fP acts
+similarly to \fIlpq\fP except it first checks locally
+for jobs to remove and then
+tries to remove files in queues off-machine.
+.NH 2
+lpc \- line printer control program
+.PP
+The
+.IR lpc (8)
+program is used by the system administrator to control the
+operation of the line printer system.
+For each line printer configured in /etc/printcap,
+.I lpc
+may be used to:
+.IP \(bu
+disable or enable a printer,
+.IP \(bu
+disable or enable a printer's spooling queue,
+.IP \(bu
+rearrange the order of jobs in a spooling queue,
+.IP \(bu
+find the status of printers, and their associated
+spooling queues and printer daemons.
diff --git a/usr.sbin/lpr/SMM.doc/3.t b/usr.sbin/lpr/SMM.doc/3.t
new file mode 100644
index 0000000..8c950a9
--- /dev/null
+++ b/usr.sbin/lpr/SMM.doc/3.t
@@ -0,0 +1,73 @@
+.\" Copyright (c) 1983, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)3.t 8.1 (Berkeley) 6/8/93
+.\"
+.NH 1
+Access control
+.PP
+The printer system maintains protected spooling areas so that
+users cannot circumvent printer accounting or
+remove files other than their own.
+The strategy used to maintain protected
+spooling areas is as follows:
+.IP \(bu 3
+The spooling area is writable only by a \fIdaemon\fP user
+and \fIdaemon\fP group.
+.IP \(bu 3
+The \fIlpr\fP program runs set-user-id to \fIroot\fP and
+set-group-id to group \fIdaemon\fP. The \fIroot\fP access permits
+reading any file required. Accessibility is verified
+with an \fIaccess\fP\|(2) call. The group ID
+is used in setting up proper ownership of files
+in the spooling area for \fIlprm\fP.
+.IP \(bu 3
+Control files in a spooling area are made with \fIdaemon\fP
+ownership and group ownership \fIdaemon\fP. Their mode is 0660.
+This insures control files are not modified by a user
+and that no user can remove files except through \fIlprm\fP.
+.IP \(bu 3
+The spooling programs,
+\fIlpd\fP, \fIlpq\fP, and \fIlprm\fP run set-user-id to \fIroot\fP
+and set-group-id to group \fIdaemon\fP to access spool files and printers.
+.IP \(bu 3
+The printer server, \fIlpd\fP,
+uses the same verification procedures as \fIrshd\fP\|(8C)
+in authenticating remote clients. The host on which a client
+resides must be present in the file /etc/hosts.equiv or /etc/hosts.lpd and
+the request message must come from a reserved port number.
+.PP
+In practice, none of \fIlpd\fP, \fIlpq\fP, or
+\fIlprm\fP would have to run as user \fIroot\fP if remote
+spooling were not supported. In previous incarnations of
+the printer system \fIlpd\fP ran set-user-id to \fIdaemon\fP,
+set-group-id to group \fIspooling\fP, and \fIlpq\fP and \fIlprm\fP ran
+set-group-id to group \fIspooling\fP.
diff --git a/usr.sbin/lpr/SMM.doc/4.t b/usr.sbin/lpr/SMM.doc/4.t
new file mode 100644
index 0000000..8800bc0
--- /dev/null
+++ b/usr.sbin/lpr/SMM.doc/4.t
@@ -0,0 +1,206 @@
+.\" Copyright (c) 1983, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)4.t 8.1 (Berkeley) 6/8/93
+.\"
+.NH 1
+Setting up
+.PP
+The 4.3BSD release comes with the necessary programs
+installed and with the default line printer queue
+created. If the system must be modified, the
+makefile in the directory /usr/src/usr.lib/lpr
+should be used in recompiling and reinstalling
+the necessary programs.
+.PP
+The real work in setting up is to create the
+.I printcap
+file and any printer filters for printers not supported
+in the distribution system.
+.NH 2
+Creating a printcap file
+.PP
+The
+.I printcap
+database contains one or more entries per printer.
+A printer should have a separate spooling directory;
+otherwise, jobs will be printed on
+different printers depending on which printer daemon starts first.
+This section describes how to create entries for printers that do not
+conform to the default printer description (an LP-11 style interface to a
+standard, band printer).
+.NH 3
+Printers on serial lines
+.PP
+When a printer is connected via a serial communication line
+it must have the proper baud rate and terminal modes set.
+The following example is for a DecWriter III printer connected
+locally via a 1200 baud serial line.
+.DS
+.DT
+lp|LA-180 DecWriter III:\e
+ :lp=/dev/lp:br#1200:fs#06320:\e
+ :tr=\ef:of=/usr/lib/lpf:lf=/usr/adm/lpd-errs:
+.DE
+The
+.B lp
+entry specifies the file name to open for output. Here it could
+be left out since ``/dev/lp'' is the default.
+The
+.B br
+entry sets the baud rate for the tty line and the
+.B fs
+entry sets CRMOD, no parity, and XTABS (see \fItty\fP\|(4)).
+The
+.B tr
+entry indicates that a form-feed should be printed when the queue
+empties so the paper can be torn off without turning the printer off-line and
+pressing form feed.
+The
+.B of
+entry specifies the filter program
+.I lpf
+should be used for printing the files;
+more will be said about filters later.
+The last entry causes errors
+to be written to the file ``/usr/adm/lpd-errs''
+instead of the console. Most errors from \fIlpd\fP are logged using
+\fIsyslogd\fP\|(8) and will not be logged in the specified file. The
+filters should use \fIsyslogd\fP to report errors; only those that
+write to standard error output will end up with errors in the \fBlf\fP file.
+(Occasionally errors sent to standard error output have not appeared
+in the log file; the use of \fIsyslogd\fP is highly recommended.)
+.NH 3
+Remote printers
+.PP
+Printers that reside on remote hosts should have an empty
+.B lp
+entry.
+For example, the following printcap entry would send output to the printer
+named ``lp'' on the machine ``ucbvax''.
+.DS
+.DT
+lp|default line printer:\e
+ :lp=:rm=ucbvax:rp=lp:sd=/usr/spool/vaxlpd:
+.DE
+The
+.B rm
+entry is the name of the remote machine to connect to; this name must
+be a known host name for a machine on the network.
+The
+.B rp
+capability indicates
+the name of the printer on the remote machine is ``lp'';
+here it could be left out since this is the default value.
+The
+.B sd
+entry specifies ``/usr/spool/vaxlpd''
+as the spooling directory instead of the
+default value of ``/usr/spool/lpd''.
+.NH 2
+Output filters
+.PP
+Filters are used to handle device dependencies and to
+do accounting functions. The output filtering of
+.B of
+is used when accounting is
+not being done or when all text data must be passed through a filter.
+It is not intended to do accounting since it is started only once,
+all text files are filtered through it, and no provision is made for passing
+owners' login name, identifying the beginning and ending of jobs, etc.
+The other filters (if specified) are started for each file
+printed and do accounting if there is an
+.B af
+entry.
+If entries for both
+.B of
+and other filters are specified,
+the output filter is used only to print the banner page;
+it is then stopped to allow other filters access to the printer.
+An example of a printer that requires output filters
+is the Benson-Varian.
+.DS
+.DT
+va|varian|Benson-Varian:\e
+ :lp=/dev/va0:sd=/usr/spool/vad:of=/usr/lib/vpf:\e
+ :tf=/usr/lib/rvcat:mx#2000:pl#58:px=2112:py=1700:tr=\ef:
+.DE
+The
+.B tf
+entry specifies ``/usr/lib/rvcat'' as the filter to be
+used in printing \fItroff\fP\|(1) output.
+This filter is needed to set the device into print mode
+for text, and plot mode for printing
+.I troff
+files and raster images (see \fIva\fP\|(4V)).
+Note that the page length is set to 58 lines by the
+.B pl
+entry for 8.5" by 11" fan-fold paper.
+To enable accounting, the varian entry would be
+augmented with an
+.B af
+filter as shown below.
+.DS
+.DT
+va|varian|Benson-Varian:\e
+ :lp=/dev/va0:sd=/usr/spool/vad:of=/usr/lib/vpf:\e
+ :if=/usr/lib/vpf:tf=/usr/lib/rvcat:af=/usr/adm/vaacct:\e
+ :mx#2000:pl#58:px=2112:py=1700:tr=\ef:
+.DE
+.NH 2
+Access Control
+.PP
+Local access to printer queues is controlled with the
+.B rg
+printcap entry.
+.DS
+ :rg=lprgroup:
+.DE
+Users must be in the group
+.I lprgroup
+to submit jobs to the specified printer.
+The default is to allow all users access.
+Note that once the files are in the local queue, they can be printed
+locally or forwarded to another host depending on the configuration.
+.PP
+Remote access is controlled by listing the hosts in either the file
+/etc/hosts.equiv or /etc/hosts.lpd, one host per line. Note that
+.IR rsh (1)
+and
+.IR rlogin (1)
+use /etc/hosts.equiv to determine which hosts are equivalent for allowing logins
+without passwords. The file /etc/hosts.lpd is only used to control
+which hosts have line printer access.
+Remote access can be further restricted to only allow remote users with accounts
+on the local host to print jobs by using the \fBrs\fP printcap entry.
+.DS
+ :rs:
+.DE
diff --git a/usr.sbin/lpr/SMM.doc/5.t b/usr.sbin/lpr/SMM.doc/5.t
new file mode 100644
index 0000000..137a342
--- /dev/null
+++ b/usr.sbin/lpr/SMM.doc/5.t
@@ -0,0 +1,116 @@
+.\" Copyright (c) 1983, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)5.t 8.1 (Berkeley) 6/8/93
+.\"
+.NH 1
+Output filter specifications
+.PP
+The filters supplied with 4.3BSD
+handle printing and accounting for most common
+line printers, the Benson-Varian, the wide (36") and
+narrow (11") Versatec printer/plotters. For other devices or accounting
+methods, it may be necessary to create a new filter.
+.PP
+Filters are spawned by \fIlpd\fP
+with their standard input the data to be printed, and standard output
+the printer. The standard error is attached to the
+.B lf
+file for logging errors or \fIsyslogd\fP may be used for logging errors.
+A filter must return a 0 exit
+code if there were no errors, 1 if the job should be reprinted,
+and 2 if the job should be thrown away.
+When \fIlprm\fP
+sends a kill signal to the \fIlpd\fP process controlling
+printing, it sends a SIGINT signal
+to all filters and descendents of filters.
+This signal can be trapped by filters that need
+to do cleanup operations such as
+deleting temporary files.
+.PP
+Arguments passed to a filter depend on its type.
+The
+.B of
+filter is called with the following arguments.
+.DS
+\fIfilter\fP \fB\-w\fPwidth \fB\-l\fPlength
+.DE
+The \fIwidth\fP and \fIlength\fP values come from the
+.B pw
+and
+.B pl
+entries in the printcap database.
+The
+.B if
+filter is passed the following parameters.
+.DS
+\fIfilter\fP [\|\fB\-c\fP\|] \fB\-w\fPwidth \fB\-l\fPlength \fB\-i\fPindent \fB\-n\fP login \fB\-h\fP host accounting_file
+.DE
+The
+.B \-c
+flag is optional, and only supplied when control characters
+are to be passed uninterpreted to the printer (when using the
+.B \-l
+option of
+.I lpr
+to print the file).
+The
+.B \-w
+and
+.B \-l
+parameters are the same as for the
+.B of
+filter.
+The
+.B \-n
+and
+.B \-h
+parameters specify the login name and host name of the job owner.
+The last argument is the name of the accounting file from
+.IR printcap .
+.PP
+All other filters are called with the following arguments:
+.DS
+\fIfilter\fP \fB\-x\fPwidth \fB\-y\fPlength \fB\-n\fP login \fB\-h\fP host accounting_file
+.DE
+The
+.B \-x
+and
+.B \-y
+options specify the horizontal and vertical page
+size in pixels (from the
+.B px
+and
+.B py
+entries in the printcap file).
+The rest of the arguments are the same as for the
+.B if
+filter.
diff --git a/usr.sbin/lpr/SMM.doc/6.t b/usr.sbin/lpr/SMM.doc/6.t
new file mode 100644
index 0000000..7087790
--- /dev/null
+++ b/usr.sbin/lpr/SMM.doc/6.t
@@ -0,0 +1,94 @@
+.\" Copyright (c) 1983, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)6.t 8.1 (Berkeley) 6/8/93
+.\"
+.NH 1
+Line printer Administration
+.PP
+The
+.I lpc
+program provides local control over line printer activity.
+The major commands and their intended use will be described.
+The command format and remaining commands are described in
+.IR lpc (8).
+.LP
+\fBabort\fP and \fBstart\fP
+.IP
+.I Abort
+terminates an active spooling daemon on the local host immediately and
+then disables printing (preventing new daemons from being started by
+.IR lpr ).
+This is normally used to forcibly restart a hung line printer daemon
+(i.e., \fIlpq\fP reports that there is a daemon present but nothing is
+happening). It does not remove any jobs from the queue
+(use the \fIlprm\fP command instead).
+.I Start
+enables printing and requests \fIlpd\fP to start printing jobs.
+.LP
+\fBenable\fP and \fBdisable\fP
+.IP
+\fIEnable\fP and \fIdisable\fP allow spooling in the local queue to be
+turned on/off.
+This will allow/prevent
+.I lpr
+from putting new jobs in the spool queue. It is frequently convenient
+to turn spooling off while testing new line printer filters since the
+.I root
+user can still use
+.I lpr
+to put jobs in the queue but no one else can.
+The other main use is to prevent users from putting jobs in the queue
+when the printer is expected to be unavailable for a long time.
+.LP
+\fBrestart\fP
+.IP
+.I Restart
+allows ordinary users to restart printer daemons when
+.I lpq
+reports that there is no daemon present.
+.LP
+\fBstop\fP
+.IP
+.I Stop
+halts a spooling daemon after the current job completes;
+this also disables printing. This is a clean way to shutdown a
+printer to do maintenance, etc. Note that users can still enter jobs in a
+spool queue while a printer is
+.IR stopped .
+.LP
+\fBtopq\fP
+.IP
+.I Topq
+places jobs at the top of a printer queue. This can be used
+to reorder high priority jobs since
+.I lpr
+only provides first-come-first-serve ordering of jobs.
diff --git a/usr.sbin/lpr/SMM.doc/7.t b/usr.sbin/lpr/SMM.doc/7.t
new file mode 100644
index 0000000..a6f6bea
--- /dev/null
+++ b/usr.sbin/lpr/SMM.doc/7.t
@@ -0,0 +1,226 @@
+.\" Copyright (c) 1983, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)7.t 8.1 (Berkeley) 6/8/93
+.\"
+.NH 1
+Troubleshooting
+.PP
+There are several messages that may be generated by the
+the line printer system. This section
+categorizes the most common and explains the cause
+for their generation. Where the message implies a failure,
+directions are given to remedy the problem.
+.PP
+In the examples below, the name
+.I printer
+is the name of the printer from the
+.I printcap
+database.
+.NH 2
+LPR
+.SH
+lpr: \fIprinter\fP\|: unknown printer
+.IP
+The
+.I printer
+was not found in the
+.I printcap
+database. Usually this is a typing mistake; however, it may indicate
+a missing or incorrect entry in the /etc/printcap file.
+.SH
+lpr: \fIprinter\fP\|: jobs queued, but cannot start daemon.
+.IP
+The connection to
+.I lpd
+on the local machine failed.
+This usually means the printer server started at
+boot time has died or is hung. Check the local socket
+/dev/printer to be sure it still exists (if it does not exist,
+there is no
+.I lpd
+process running).
+Usually it is enough to get a super-user to type the following to
+restart
+.IR lpd .
+.DS
+% /usr/lib/lpd
+.DE
+You can also check the state of the master printer daemon with the following.
+.DS
+% ps l`cat /usr/spool/lpd.lock`
+.DE
+.IP
+Another possibility is that the
+.I lpr
+program is not set-user-id to \fIroot\fP, set-group-id to group \fIdaemon\fP.
+This can be checked with
+.DS
+% ls \-lg /usr/ucb/lpr
+.DE
+.SH
+lpr: \fIprinter\fP\|: printer queue is disabled
+.IP
+This means the queue was turned off with
+.DS
+% lpc disable \fIprinter\fP
+.DE
+to prevent
+.I lpr
+from putting files in the queue. This is normally
+done by the system manager when a printer is
+going to be down for a long time. The
+printer can be turned back on by a super-user with
+.IR lpc .
+.NH 2
+LPQ
+.SH
+waiting for \fIprinter\fP to become ready (offline ?)
+.IP
+The printer device could not be opened by the daemon.
+This can happen for several reasons,
+the most common is that the printer is turned off-line.
+This message can also be generated if the printer is out
+of paper, the paper is jammed, etc.
+The actual reason is dependent on the meaning
+of error codes returned by system device driver.
+Not all printers supply enough information
+to distinguish when a printer is off-line or having
+trouble (e.g. a printer connected through a serial line).
+Another possible cause of this message is
+some other process, such as an output filter,
+has an exclusive open on the device. Your only recourse
+here is to kill off the offending program(s) and
+restart the printer with
+.IR lpc .
+.SH
+\fIprinter\fP is ready and printing
+.IP
+The
+.I lpq
+program checks to see if a daemon process exists for
+.I printer
+and prints the file \fIstatus\fP located in the spooling directory.
+If the daemon is hung, a super user can use
+.I lpc
+to abort the current daemon and start a new one.
+.SH
+waiting for \fIhost\fP to come up
+.IP
+This implies there is a daemon trying to connect to the remote
+machine named
+.I host
+to send the files in the local queue.
+If the remote machine is up,
+.I lpd
+on the remote machine is probably dead or
+hung and should be restarted as mentioned for
+.IR lpr .
+.SH
+sending to \fIhost\fP
+.IP
+The files should be in the process of being transferred to the remote
+.IR host .
+If not, the local daemon should be aborted and started with
+.IR lpc .
+.SH
+Warning: \fIprinter\fP is down
+.IP
+The printer has been marked as being unavailable with
+.IR lpc .
+.SH
+Warning: no daemon present
+.IP
+The \fIlpd\fP process overseeing
+the spooling queue, as specified in the ``lock'' file
+in that directory, does not exist. This normally occurs
+only when the daemon has unexpectedly died.
+The error log file for the printer and the \fIsyslogd\fP logs
+should be checked for a
+diagnostic from the deceased process.
+To restart an \fIlpd\fP, use
+.DS
+% lpc restart \fIprinter\fP
+.DE
+.SH
+no space on remote; waiting for queue to drain
+.IP
+This implies that there is insufficient disk space on the remote.
+If the file is large enough, there will never be enough space on
+the remote (even after the queue on the remote is empty). The solution here
+is to move the spooling queue or make more free space on the remote.
+.NH 2
+LPRM
+.SH
+lprm: \fIprinter\fP\|: cannot restart printer daemon
+.IP
+This case is the same as when
+.I lpr
+prints that the daemon cannot be started.
+.NH 2
+LPD
+.PP
+The
+.I lpd
+program can log many different messages using \fIsyslogd\fP\|(8).
+Most of these messages are about files that can not
+be opened and usually imply that the
+.I printcap
+file or the protection modes of the files are
+incorrect. Files may also be inaccessible if people
+manually manipulate the line printer system (i.e. they
+bypass the
+.I lpr
+program).
+.PP
+In addition to messages generated by
+.IR lpd ,
+any of the filters that
+.I lpd
+spawns may log messages using \fIsyslogd\fP or to the error log file
+(the file specified in the \fBlf\fP entry in \fIprintcap\fP\|).
+.NH 2
+LPC
+.PP
+.SH
+couldn't start printer
+.IP
+This case is the same as when
+.I lpr
+reports that the daemon cannot be started.
+.SH
+cannot examine spool directory
+.IP
+Error messages beginning with ``cannot ...'' are usually because of
+incorrect ownership or protection mode of the lock file, spooling
+directory or the
+.I lpc
+program.
diff --git a/usr.sbin/lpr/SMM.doc/Makefile b/usr.sbin/lpr/SMM.doc/Makefile
new file mode 100644
index 0000000..faeb266
--- /dev/null
+++ b/usr.sbin/lpr/SMM.doc/Makefile
@@ -0,0 +1,12 @@
+# From: @(#)Makefile 8.1 (Berkeley) 6/8/93
+# $FreeBSD$
+
+BINDIR= /usr/share/doc
+VOLUME= smm/07.lpd
+SRCS= 0.t 1.t 2.t 3.t 4.t 5.t 6.t 7.t
+MACROS= -ms
+
+USE_TBL= yes
+SRCDIR= ${.CURDIR}
+
+.include <bsd.doc.mk>
diff --git a/usr.sbin/lpr/SMM.doc/spell.ok b/usr.sbin/lpr/SMM.doc/spell.ok
new file mode 100644
index 0000000..bf31319
--- /dev/null
+++ b/usr.sbin/lpr/SMM.doc/spell.ok
@@ -0,0 +1,70 @@
+Aprinter
+Bprinter
+CRMOD
+Cprinter
+DecWriter
+Dprinter
+Eprinter
+LPC
+LPD
+Lpd
+Manual''SMM:5
+SIGINT
+SMM:5
+Topq
+XTABS
+adm
+af
+br
+daemon
+daemons
+dev
+f:of
+fs
+hosts.equiv
+hosts.lpd
+lf
+lg
+lib
+lp:br
+lp:sd
+lpc
+lpd
+lpd.lock
+lpf
+lpf:lf
+lprgroup
+makefile
+mx
+offline
+pl
+printcap
+pw
+py
+rc
+rcmd
+rg
+rlogin
+rp
+rs
+rsh
+rshd
+rvcat
+rvcat:af
+rvcat:mx
+sd
+src
+syslogd
+tf
+topq
+ucb
+ucbvax
+ucbvax:rp
+usr.lib
+va0:sd
+vaacct
+vad:of
+varian
+vaxlpd
+vpf
+vpf:tf
diff --git a/usr.sbin/lpr/chkprintcap/Makefile b/usr.sbin/lpr/chkprintcap/Makefile
new file mode 100644
index 0000000..ffffffb
--- /dev/null
+++ b/usr.sbin/lpr/chkprintcap/Makefile
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../common_source
+
+PROG= chkprintcap
+MAN= chkprintcap.8
+SRCS= chkprintcap.c skimprintcap.c
+
+CFLAGS+= -I${.CURDIR}/../common_source
+
+DPADD= ${LIBLPR}
+LDADD= ${LIBLPR}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/lpr/chkprintcap/chkprintcap.8 b/usr.sbin/lpr/chkprintcap/chkprintcap.8
new file mode 100644
index 0000000..44b47cb
--- /dev/null
+++ b/usr.sbin/lpr/chkprintcap/chkprintcap.8
@@ -0,0 +1,94 @@
+.\" Copyright 1997 Massachusetts Institute of Technology
+.\"
+.\" Permission to use, copy, modify, and distribute this software and
+.\" its documentation for any purpose and without fee is hereby
+.\" granted, provided that both the above copyright notice and this
+.\" permission notice appear in all copies, that both the above
+.\" copyright notice and this permission notice appear in all
+.\" supporting documentation, and that the name of M.I.T. not be used
+.\" in advertising or publicity pertaining to distribution of the
+.\" software without specific, written prior permission. M.I.T. makes
+.\" no representations about the suitability of this software for any
+.\" purpose. It is provided "as is" without express or implied
+.\" warranty.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
+.\" ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
+.\" INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+.\" SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+.\" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+.\" ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+.\" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+.\" OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.Dd November 30, 1997
+.Dt CHKPRINTCAP 8
+.Os
+.Sh NAME
+.Nm chkprintcap
+.Nd check validity of entries in the print spooler database
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Op Fl f Ar printcap
+.Sh DESCRIPTION
+.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
+.Sq ( Li sd=
+capability).
+.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
+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
+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..5a5592b
--- /dev/null
+++ b/usr.sbin/lpr/chkprintcap/chkprintcap.c
@@ -0,0 +1,317 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <grp.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <sys/param.h> /* needed for lp.h but not used here */
+#include <dirent.h> /* ditto */
+#include "lp.h"
+#include "lp.local.h"
+#include "pathnames.h"
+#include "skimprintcap.h"
+
+static void check_spool_dirs(void);
+static int interpret_error(const struct printer *pp, int error);
+static void make_spool_dir(const struct printer *pp);
+static void note_spool_dir(const struct printer *pp, const struct stat *st);
+static void usage(void) __dead2;
+
+static int problems; /* number of problems encountered */
+
+/*
+ * chkprintcap - check the printcap file for syntactic and semantic errors
+ * Returns the number of problems found.
+ */
+int
+main(int argc, char **argv)
+{
+ struct skiminfo *skres;
+ char *pcap_fname;
+ int c, error, makedirs, more, queuecnt, verbosity;
+ struct printer myprinter, *pp;
+
+ makedirs = 0;
+ queuecnt = 0;
+ verbosity = 0;
+ pcap_fname = NULL;
+ pp = &myprinter;
+
+ while ((c = getopt(argc, argv, "df:v")) != -1) {
+ switch (c) {
+ case 'd':
+ makedirs = 1;
+ break;
+
+ case 'f':
+ pcap_fname = strdup(optarg);
+ setprintcap(pcap_fname);
+ break;
+
+ case 'v':
+ verbosity++;
+ break;
+
+ default:
+ usage();
+ }
+ }
+
+ if (optind != argc)
+ usage();
+
+ if (pcap_fname == NULL)
+ pcap_fname = strdup(_PATH_PRINTCAP);
+
+ /*
+ * Skim through the printcap file looking for simple user-mistakes
+ * which will produce the wrong result for the user, but which may
+ * be pretty hard for the user to notice. Such user-mistakes will
+ * only generate warning messages. The (fatal-) problem count will
+ * only be incremented if there is a system problem trying to read
+ * the printcap file.
+ */
+ skres = skim_printcap(pcap_fname, verbosity);
+ if (skres->fatalerr)
+ return (skres->fatalerr);
+
+ /*
+ * Now use the standard capability-db routines to check the values
+ * in each of the queues defined in the printcap file.
+ */
+ more = firstprinter(pp, &error);
+ if (interpret_error(pp, error) && more)
+ goto next;
+
+ while (more) {
+ struct stat stab;
+
+ queuecnt++;
+ errno = 0;
+ if (stat(pp->spool_dir, &stab) < 0) {
+ if (errno == ENOENT && makedirs) {
+ make_spool_dir(pp);
+ } else {
+ problems++;
+ warn("%s: %s", pp->printer, pp->spool_dir);
+ }
+ } else {
+ note_spool_dir(pp, &stab);
+ }
+
+ /* Make other queue-specific validity checks here... */
+
+next:
+ more = nextprinter(pp, &error);
+ if (interpret_error(pp, error) && more)
+ goto next;
+ }
+
+ check_spool_dirs();
+
+ if (queuecnt != skres->entries) {
+ warnx("WARNING: found %d entries when skimming %s,",
+ skres->entries, pcap_fname);
+ warnx("WARNING: but only found %d queues to process!",
+ queuecnt);
+ }
+ return (problems);
+}
+
+/*
+ * Interpret the error code. Returns 1 if we should skip to the next
+ * record (as this record is unlikely to make sense). If the problem
+ * is very severe, exit. Otherwise, return zero.
+ */
+static int
+interpret_error(const struct printer *pp, int error)
+{
+ switch(error) {
+ case PCAPERR_OSERR:
+ err(++problems, "reading printer database");
+ case PCAPERR_TCLOOP:
+ ++problems;
+ warnx("%s: loop detected in tc= expansion", pp->printer);
+ return 1;
+ case PCAPERR_TCOPEN:
+ warnx("%s: unresolved tc= expansion", pp->printer);
+ return 1;
+ case PCAPERR_SUCCESS:
+ break;
+ default:
+ errx(++problems, "unknown printcap library error %d", error);
+ }
+ return 0;
+}
+
+/*
+ * Keep the list of spool directories. Note that we don't whine
+ * until all spool directories are noted, so that all of the more serious
+ * problems are noted first. We keep the list sorted by st_dev and
+ * st_ino, so that the problem spool directories can be noted in
+ * a single loop.
+ */
+struct dirlist {
+ LIST_ENTRY(dirlist) link;
+ struct stat stab;
+ char *path;
+ char *printer;
+};
+
+static LIST_HEAD(, dirlist) dirlist;
+
+static int
+lessp(const struct dirlist *a, const struct dirlist *b)
+{
+ if (a->stab.st_dev == b->stab.st_dev)
+ return a->stab.st_ino < b->stab.st_ino;
+ return a->stab.st_dev < b->stab.st_dev;
+}
+
+static int
+equal(const struct dirlist *a, const struct dirlist *b)
+{
+ return ((a->stab.st_dev == b->stab.st_dev)
+ && (a->stab.st_ino == b->stab.st_ino));
+}
+
+static void
+note_spool_dir(const struct printer *pp, const struct stat *st)
+{
+ struct dirlist *dp, *dp2, *last;
+
+ dp = malloc(sizeof *dp);
+ if (dp == 0)
+ err(++problems, "malloc(%lu)", (u_long)sizeof *dp);
+
+ dp->stab = *st;
+ dp->printer = strdup(pp->printer);
+ if (dp->printer == 0)
+ err(++problems, "malloc(%lu)", strlen(pp->printer) + 1UL);
+ dp->path = strdup(pp->spool_dir);
+ if (dp->path == 0)
+ err(++problems, "malloc(%lu)", strlen(pp->spool_dir) + 1UL);
+
+ last = 0;
+ LIST_FOREACH(dp2, &dirlist, link) {
+ if(!lessp(dp, dp2))
+ break;
+ last = dp2;
+ }
+
+ if (last) {
+ LIST_INSERT_AFTER(last, dp, link);
+ } else {
+ LIST_INSERT_HEAD(&dirlist, dp, link);
+ }
+}
+
+static void
+check_spool_dirs(void)
+{
+ struct dirlist *dp, *dp2;
+
+ for (dp = LIST_FIRST(&dirlist); dp; dp = dp2) {
+ dp2 = LIST_NEXT(dp, link);
+
+ if (dp2 != 0 && equal(dp, dp2)) {
+ ++problems;
+ if (strcmp(dp->path, dp2->path) == 0) {
+ warnx("%s and %s share the same spool, %s",
+ dp->printer, dp2->printer, dp->path);
+ } else {
+ warnx("%s (%s) and %s (%s) are the same "
+ "directory", dp->path, dp->printer,
+ dp2->path, dp2->printer);
+ }
+ continue;
+ }
+ /* Should probably check owners and modes here. */
+ }
+}
+
+#ifndef SPOOL_DIR_MODE
+#define SPOOL_DIR_MODE (S_IRUSR | S_IWUSR | S_IXUSR \
+ | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)
+#endif
+
+static void
+make_spool_dir(const struct printer *pp)
+{
+ char *sd = pp->spool_dir;
+ struct group *gr;
+ struct stat stab;
+
+ if (mkdir(sd, S_IRUSR | S_IXUSR) < 0) {
+ problems++;
+ warn("%s: mkdir %s", pp->printer, sd);
+ return;
+ }
+ gr = getgrnam("daemon");
+ if (gr == 0)
+ errx(++problems, "cannot locate daemon group");
+
+ if (chown(sd, pp->daemon_user, gr->gr_gid) < 0) {
+ ++problems;
+ warn("%s: cannot change ownership to %ld:%ld", sd,
+ (long)pp->daemon_user, (long)gr->gr_gid);
+ return;
+ }
+
+ if (chmod(sd, SPOOL_DIR_MODE) < 0) {
+ ++problems;
+ warn("%s: cannot change mode to %lo", sd, (long)SPOOL_DIR_MODE);
+ return;
+ }
+ if (stat(sd, &stab) < 0)
+ err(++problems, "stat: %s", sd);
+
+ note_spool_dir(pp, &stab);
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage:\n\tchkprintcap [-dv] [-f printcapfile]\n");
+ exit(1);
+}
diff --git a/usr.sbin/lpr/chkprintcap/skimprintcap.c b/usr.sbin/lpr/chkprintcap/skimprintcap.c
new file mode 100644
index 0000000..e3cb485
--- /dev/null
+++ b/usr.sbin/lpr/chkprintcap/skimprintcap.c
@@ -0,0 +1,262 @@
+/*
+ * ------+---------+---------+---------+---------+---------+---------+---------*
+ * Copyright (c) 2001 - Garance Alistair Drosehn <gad@FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation
+ * are those of the authors and should not be interpreted as representing
+ * official policies, either expressed or implied, of the FreeBSD Project.
+ *
+ * ------+---------+---------+---------+---------+---------+---------+---------*
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <grp.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <sys/param.h> /* needed for lp.h but not used here */
+#include <dirent.h> /* ditto */
+#include "lp.h"
+#include "lp.local.h"
+#include "skimprintcap.h"
+
+/*
+ * Save the canonical queue name of the entry that is currently being
+ * scanned, in case a warning message is printed for the current queue.
+ * Only the first 'QENTRY_MAXLEN' characters will be saved, since this
+ * is only for warning messages. The variable includes space for the
+ * string " (entry " and a trailing ")", when the scanner is in the
+ * middle of an entry. When the scanner is not in a specific entry,
+ * the variable will be the a null string.
+ */
+#define QENTRY_MAXLEN 30
+#define QENTRY_PREFIX " (entry "
+static char skim_entryname[sizeof(QENTRY_PREFIX) + QENTRY_MAXLEN + 2];
+
+/*
+ * isgraph is defined to work on an 'int', in the range 0 to 255, plus EOF.
+ * Define a wrapper which can take 'char', either signed or unsigned.
+ */
+#define isgraphch(Anychar) isgraph(((int) Anychar) & 255)
+
+struct skiminfo *
+skim_printcap(const char *pcap_fname, int verbosity)
+{
+ struct skiminfo *skinf;
+ char buff[BUFSIZ];
+ char *ch, *curline, *endfield, *lastchar;
+ FILE *pc_file;
+ int missing_nl;
+ enum {NO_CONTINUE, WILL_CONTINUE, BAD_CONTINUE} is_cont, had_cont;
+ enum {CMNT_LINE, ENTRY_LINE, TAB_LINE, TABERR_LINE} is_type, had_type;
+
+ skinf = malloc(sizeof(struct skiminfo));
+ memset(skinf, 0, sizeof(struct skiminfo));
+
+ pc_file = fopen(pcap_fname, "r");
+ if (pc_file == NULL) {
+ warn("fopen(%s)", pcap_fname);
+ skinf->fatalerr++;
+ return (skinf); /* fatal error */
+ }
+
+ skim_entryname[0] = '0';
+
+ is_cont = NO_CONTINUE;
+ is_type = CMNT_LINE;
+ errno = 0;
+ curline = fgets(buff, sizeof(buff), pc_file);
+ while (curline != NULL) {
+ skinf->lines++;
+
+ /* Check for the expected newline char, and remove it */
+ missing_nl = 0;
+ lastchar = strchr(curline, '\n');
+ if (lastchar != NULL)
+ *lastchar = '\0';
+ else {
+ lastchar = strchr(curline, '\0');
+ missing_nl = 1;
+ }
+ if (curline < lastchar)
+ lastchar--;
+
+ /*
+ * Check for `\' (continuation-character) at end of line.
+ * If there is none, then trim off spaces and check again.
+ * This would be a bad line because it looks like it is
+ * continued, but it will not be treated that way.
+ */
+ had_cont = is_cont;
+ is_cont = NO_CONTINUE;
+ if (*lastchar == '\\') {
+ is_cont = WILL_CONTINUE;
+ lastchar--;
+ } else {
+ while ((curline < lastchar) && !isgraphch(*lastchar))
+ lastchar--;
+ if (*lastchar == '\\')
+ is_cont = BAD_CONTINUE;
+ }
+
+ had_type = is_type;
+ is_type = CMNT_LINE;
+ switch (*curline) {
+ case '\0': /* treat zero-length line as comment */
+ case '#':
+ skinf->comments++;
+ break;
+ case ' ':
+ case '\t':
+ is_type = TAB_LINE;
+ break;
+ default:
+ is_type = ENTRY_LINE;
+ skinf->entries++;
+
+ /* pick up the queue name, to use in warning messages */
+ ch = curline;
+ while ((ch <= lastchar) && (*ch != ':') && (*ch != '|'))
+ ch++;
+ ch--; /* last char of queue name */
+ strcpy(skim_entryname, QENTRY_PREFIX);
+ if ((ch - curline) > QENTRY_MAXLEN) {
+ strncat(skim_entryname, curline, QENTRY_MAXLEN
+ - 1);
+ strcat(skim_entryname, "+");
+ } else {
+ strncat(skim_entryname, curline, (ch - curline
+ + 1));
+ }
+ strlcat(skim_entryname, ")", sizeof(skim_entryname));
+ break;
+ }
+
+ /*
+ * Check to see if the previous line was a bad contination
+ * line. The check is delayed until now so a warning message
+ * is not printed when a "bad continuation" is on a comment
+ * line, and it just "continues" into another comment line.
+ */
+ if (had_cont == BAD_CONTINUE) {
+ if ((had_type != CMNT_LINE) || (is_type != CMNT_LINE) ||
+ (verbosity > 1)) {
+ skinf->warnings++;
+ warnx("Warning: blanks after trailing '\\',"
+ " at line %d%s", skinf->lines - 1,
+ skim_entryname);
+ }
+ }
+
+ /* If we are no longer in an entry, then forget the name */
+ if ((had_cont != WILL_CONTINUE) && (is_type != ENTRY_LINE)) {
+ skim_entryname[0] = '\0';
+ }
+
+ /*
+ * Print out warning for missing newline, done down here
+ * so we are sure to have the right entry-name for it.
+ */
+ if (missing_nl) {
+ skinf->warnings++;
+ warnx("Warning: No newline at end of line %d%s",
+ skinf->lines, skim_entryname);
+ }
+
+ /*
+ * Check for start-of-entry lines which do not include a
+ * ":" character (to indicate the end of the name field).
+ * This can cause standard printcap processing to ignore
+ * ALL of the following lines.
+ * XXXXX - May need to allow for the list-of-names to
+ * continue on to the following line...
+ */
+ if (is_type == ENTRY_LINE) {
+ endfield = strchr(curline, ':');
+ if (endfield == NULL) {
+ skinf->warnings++;
+ warnx("Warning: No ':' to terminate name-field"
+ " at line %d%s", skinf->lines,
+ skim_entryname);
+ }
+ }
+
+ /*
+ * Now check for cases where this line is (or is-not) a
+ * continuation of the previous line, and a person skimming
+ * the file would assume it is not (or is) a continuation.
+ */
+ switch (had_cont) {
+ case NO_CONTINUE:
+ case BAD_CONTINUE:
+ if (is_type == TAB_LINE) {
+ skinf->warnings++;
+ warnx("Warning: values-line after line with"
+ " NO trailing '\\', at line %d%s",
+ skinf->lines, skim_entryname);
+ }
+ break;
+
+ case WILL_CONTINUE:
+ if (is_type == ENTRY_LINE) {
+ skinf->warnings++;
+ warnx("Warning: new entry starts after line"
+ " with trailing '\\', at line %d%s",
+ skinf->lines, skim_entryname);
+ }
+ break;
+ }
+
+ /* get another line from printcap and repeat loop */
+ curline = fgets(buff, sizeof(buff), pc_file);
+ }
+
+ if (errno != 0) {
+ warn("fgets(%s)", pcap_fname);
+ skinf->fatalerr++; /* fatal error */
+ }
+
+ if (skinf->warnings > 0)
+ warnx("%4d warnings from skimming %s", skinf->warnings,
+ pcap_fname);
+
+ if (verbosity)
+ warnx("%4d lines (%d comments), %d entries for %s",
+ skinf->lines, skinf->comments, skinf->entries, pcap_fname);
+
+ fclose(pc_file);
+ return (skinf);
+}
diff --git a/usr.sbin/lpr/chkprintcap/skimprintcap.h b/usr.sbin/lpr/chkprintcap/skimprintcap.h
new file mode 100644
index 0000000..af74b48
--- /dev/null
+++ b/usr.sbin/lpr/chkprintcap/skimprintcap.h
@@ -0,0 +1,44 @@
+/*
+ * ------+---------+---------+---------+---------+---------+---------+---------*
+ * Copyright (c) 2001 - Garance Alistair Drosehn <gad@FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation
+ * are those of the authors and should not be interpreted as representing
+ * official policies, either expressed or implied, of the FreeBSD Project.
+ *
+ * ------+---------+---------+---------+---------+---------+---------+---------*
+ * $FreeBSD$
+ * ------+---------+---------+---------+---------+---------+---------+---------*
+ */
+
+struct skiminfo {
+ int comments;
+ int entries;
+ int fatalerr; /* fatal error, msg already printed */
+ int lines;
+ int warnings;
+};
+
+struct skiminfo *skim_printcap(const char *_pcap, int _verbosity);
diff --git a/usr.sbin/lpr/common_source/Makefile b/usr.sbin/lpr/common_source/Makefile
new file mode 100644
index 0000000..0f03941
--- /dev/null
+++ b/usr.sbin/lpr/common_source/Makefile
@@ -0,0 +1,13 @@
+# $FreeBSD$
+
+#
+# Library of internal routines for the print spooler suite.
+# Originally these were compiled separately into each program,
+# but the library makes it much easier to modularize them.
+#
+LIB= lpr
+INTERNALLIB= YES
+SRCS= common.c ctlinfo.c displayq.c net.c printcap.c request.c \
+ rmjob.c startdaemon.c
+
+.include <bsd.lib.mk>
diff --git a/usr.sbin/lpr/common_source/common.c b/usr.sbin/lpr/common_source/common.c
new file mode 100644
index 0000000..257a740
--- /dev/null
+++ b/usr.sbin/lpr/common_source/common.c
@@ -0,0 +1,703 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "lp.h"
+#include "lp.local.h"
+#include "pathnames.h"
+
+/*
+ * Routines and data common to all the line printer functions.
+ */
+char line[BUFSIZ];
+const char *progname; /* program name */
+
+extern uid_t uid, euid;
+
+static int compar(const void *_p1, const void *_p2);
+
+/*
+ * Getline reads a line from the control file cfp, removes tabs, converts
+ * new-line to null and leaves it in line.
+ * Returns 0 at EOF or the number of characters read.
+ */
+int
+getline(FILE *cfp)
+{
+ register int linel = 0;
+ register char *lp = line;
+ register int c;
+
+ while ((c = getc(cfp)) != '\n' && (size_t)(linel+1) < sizeof(line)) {
+ if (c == EOF)
+ return(0);
+ if (c == '\t') {
+ do {
+ *lp++ = ' ';
+ linel++;
+ } while ((linel & 07) != 0 && (size_t)(linel+1) <
+ sizeof(line));
+ continue;
+ }
+ *lp++ = c;
+ linel++;
+ }
+ *lp++ = '\0';
+ return(linel);
+}
+
+/*
+ * Scan the current directory and make a list of daemon files sorted by
+ * creation time.
+ * Return the number of entries and a pointer to the list.
+ */
+int
+getq(const struct printer *pp, struct jobqueue *(*namelist[]))
+{
+ register struct dirent *d;
+ register struct jobqueue *q, **queue;
+ size_t arraysz, nitems;
+ struct stat stbuf;
+ DIR *dirp;
+ int statres;
+
+ seteuid(euid);
+ if ((dirp = opendir(pp->spool_dir)) == NULL) {
+ seteuid(uid);
+ return (-1);
+ }
+ if (fstat(dirp->dd_fd, &stbuf) < 0)
+ goto errdone;
+ seteuid(uid);
+
+ /*
+ * Estimate the array size by taking the size of the directory file
+ * and dividing it by a multiple of the minimum size entry.
+ */
+ arraysz = (stbuf.st_size / 24);
+ queue = (struct jobqueue **)malloc(arraysz * sizeof(struct jobqueue *));
+ if (queue == NULL)
+ goto errdone;
+
+ nitems = 0;
+ while ((d = readdir(dirp)) != NULL) {
+ if (d->d_name[0] != 'c' || d->d_name[1] != 'f')
+ continue; /* daemon control files only */
+ seteuid(euid);
+ statres = stat(d->d_name, &stbuf);
+ seteuid(uid);
+ if (statres < 0)
+ continue; /* Doesn't exist */
+ q = (struct jobqueue *)malloc(sizeof(time_t) + strlen(d->d_name)
+ + 1);
+ if (q == NULL)
+ goto errdone;
+ q->job_time = stbuf.st_mtime;
+ strcpy(q->job_cfname, d->d_name);
+ /*
+ * Check to make sure the array has space left and
+ * realloc the maximum size.
+ */
+ if (++nitems > arraysz) {
+ arraysz *= 2;
+ queue = (struct jobqueue **)realloc((char *)queue,
+ arraysz * sizeof(struct jobqueue *));
+ if (queue == NULL)
+ goto errdone;
+ }
+ queue[nitems-1] = q;
+ }
+ closedir(dirp);
+ if (nitems)
+ qsort(queue, nitems, sizeof(struct jobqueue *), compar);
+ *namelist = queue;
+ return(nitems);
+
+errdone:
+ closedir(dirp);
+ seteuid(uid);
+ return (-1);
+}
+
+/*
+ * Compare modification times.
+ */
+static int
+compar(const void *p1, const void *p2)
+{
+ const struct jobqueue *qe1, *qe2;
+
+ qe1 = *(const struct jobqueue * const *)p1;
+ qe2 = *(const struct jobqueue * const *)p2;
+
+ if (qe1->job_time < qe2->job_time)
+ return (-1);
+ if (qe1->job_time > qe2->job_time)
+ return (1);
+ /*
+ * At this point, the two files have the same last-modification time.
+ * return a result based on filenames, so that 'cfA001some.host' will
+ * come before 'cfA002some.host'. Since the jobid ('001') will wrap
+ * around when it gets to '999', we also assume that '9xx' jobs are
+ * older than '0xx' jobs.
+ */
+ if ((qe1->job_cfname[3] == '9') && (qe2->job_cfname[3] == '0'))
+ return (-1);
+ if ((qe1->job_cfname[3] == '0') && (qe2->job_cfname[3] == '9'))
+ return (1);
+ return (strcmp(qe1->job_cfname, qe2->job_cfname));
+}
+
+/* sleep n milliseconds */
+void
+delay(int millisec)
+{
+ struct timeval tdelay;
+
+ if (millisec <= 0 || millisec > 10000)
+ fatal((struct printer *)0, /* fatal() knows how to deal */
+ "unreasonable delay period (%d)", millisec);
+ tdelay.tv_sec = millisec / 1000;
+ tdelay.tv_usec = millisec * 1000 % 1000000;
+ (void) select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &tdelay);
+}
+
+char *
+lock_file_name(const struct printer *pp, char *buf, size_t len)
+{
+ static char staticbuf[MAXPATHLEN];
+
+ if (buf == 0)
+ buf = staticbuf;
+ if (len == 0)
+ len = MAXPATHLEN;
+
+ if (pp->lock_file[0] == '/')
+ strlcpy(buf, pp->lock_file, len);
+ else
+ snprintf(buf, len, "%s/%s", pp->spool_dir, pp->lock_file);
+
+ return buf;
+}
+
+char *
+status_file_name(const struct printer *pp, char *buf, size_t len)
+{
+ static char staticbuf[MAXPATHLEN];
+
+ if (buf == 0)
+ buf = staticbuf;
+ if (len == 0)
+ len = MAXPATHLEN;
+
+ if (pp->status_file[0] == '/')
+ strlcpy(buf, pp->status_file, len);
+ else
+ snprintf(buf, len, "%s/%s", pp->spool_dir, pp->status_file);
+
+ return buf;
+}
+
+/*
+ * Routine to change operational state of a print queue. The operational
+ * state is indicated by the access bits on the lock file for the queue.
+ * At present, this is only called from various routines in lpc/cmds.c.
+ *
+ * XXX - Note that this works by changing access-bits on the
+ * file, and you can only do that if you are the owner of
+ * the file, or root. Thus, this won't really work for
+ * userids in the "LPR_OPER" group, unless lpc is running
+ * setuid to root (or maybe setuid to daemon).
+ * Generally lpc is installed setgid to daemon, but does
+ * not run setuid.
+ */
+int
+set_qstate(int action, const char *lfname)
+{
+ struct stat stbuf;
+ mode_t chgbits, newbits, oldmask;
+ const char *failmsg, *okmsg;
+ int chres, errsav, fd, res, statres;
+
+ /*
+ * Find what the current access-bits are.
+ */
+ memset(&stbuf, 0, sizeof(stbuf));
+ seteuid(euid);
+ statres = stat(lfname, &stbuf);
+ errsav = errno;
+ seteuid(uid);
+ if ((statres < 0) && (errsav != ENOENT)) {
+ printf("\tcannot stat() lock file\n");
+ return (SQS_STATFAIL);
+ /* NOTREACHED */
+ }
+
+ /*
+ * Determine which bit(s) should change for the requested action.
+ */
+ chgbits = stbuf.st_mode;
+ newbits = LOCK_FILE_MODE;
+ okmsg = NULL;
+ failmsg = NULL;
+ if (action & SQS_DISABLEQ) {
+ chgbits |= LFM_QUEUE_DIS;
+ newbits |= LFM_QUEUE_DIS;
+ okmsg = "queuing disabled";
+ failmsg = "disable queuing";
+ }
+ if (action & SQS_STOPP) {
+ chgbits |= LFM_PRINT_DIS;
+ newbits |= LFM_PRINT_DIS;
+ okmsg = "printing disabled";
+ failmsg = "disable printing";
+ if (action & SQS_DISABLEQ) {
+ okmsg = "printer and queuing disabled";
+ failmsg = "disable queuing and printing";
+ }
+ }
+ if (action & SQS_ENABLEQ) {
+ chgbits &= ~LFM_QUEUE_DIS;
+ newbits &= ~LFM_QUEUE_DIS;
+ okmsg = "queuing enabled";
+ failmsg = "enable queuing";
+ }
+ if (action & SQS_STARTP) {
+ chgbits &= ~LFM_PRINT_DIS;
+ newbits &= ~LFM_PRINT_DIS;
+ okmsg = "printing enabled";
+ failmsg = "enable printing";
+ }
+ if (okmsg == NULL) {
+ /* This routine was called with an invalid action. */
+ printf("\t<error in set_qstate!>\n");
+ return (SQS_PARMERR);
+ /* NOTREACHED */
+ }
+
+ res = 0;
+ if (statres >= 0) {
+ /* The file already exists, so change the access. */
+ seteuid(euid);
+ chres = chmod(lfname, chgbits);
+ errsav = errno;
+ seteuid(uid);
+ res = SQS_CHGOK;
+ if (res < 0)
+ res = SQS_CHGFAIL;
+ } else if (newbits == LOCK_FILE_MODE) {
+ /*
+ * The file does not exist, but the state requested is
+ * the same as the default state when no file exists.
+ * Thus, there is no need to create the file.
+ */
+ res = SQS_SKIPCREOK;
+ } else {
+ /*
+ * The file did not exist, so create it with the
+ * appropriate access bits for the requested action.
+ * Push a new umask around that create, to make sure
+ * all the read/write bits are set as desired.
+ */
+ oldmask = umask(S_IWOTH);
+ seteuid(euid);
+ fd = open(lfname, O_WRONLY|O_CREAT, newbits);
+ errsav = errno;
+ seteuid(uid);
+ umask(oldmask);
+ res = SQS_CREFAIL;
+ if (fd >= 0) {
+ res = SQS_CREOK;
+ close(fd);
+ }
+ }
+
+ switch (res) {
+ case SQS_CHGOK:
+ case SQS_CREOK:
+ case SQS_SKIPCREOK:
+ printf("\t%s\n", okmsg);
+ break;
+ case SQS_CREFAIL:
+ printf("\tcannot create lock file: %s\n",
+ strerror(errsav));
+ break;
+ default:
+ printf("\tcannot %s: %s\n", failmsg, strerror(errsav));
+ break;
+ }
+
+ return (res);
+}
+
+/* routine to get a current timestamp, optionally in a standard-fmt string */
+void
+lpd_gettime(struct timespec *tsp, char *strp, size_t strsize)
+{
+ struct timespec local_ts;
+ struct timeval btime;
+ char tempstr[TIMESTR_SIZE];
+#ifdef STRFTIME_WRONG_z
+ char *destp;
+#endif
+
+ if (tsp == NULL)
+ tsp = &local_ts;
+
+ /* some platforms have a routine called clock_gettime, but the
+ * routine does nothing but return "not implemented". */
+ memset(tsp, 0, sizeof(struct timespec));
+ if (clock_gettime(CLOCK_REALTIME, tsp)) {
+ /* nanosec-aware rtn failed, fall back to microsec-aware rtn */
+ memset(tsp, 0, sizeof(struct timespec));
+ gettimeofday(&btime, NULL);
+ tsp->tv_sec = btime.tv_sec;
+ tsp->tv_nsec = btime.tv_usec * 1000;
+ }
+
+ /* caller may not need a character-ized version */
+ if ((strp == NULL) || (strsize < 1))
+ return;
+
+ strftime(tempstr, TIMESTR_SIZE, LPD_TIMESTAMP_PATTERN,
+ localtime(&tsp->tv_sec));
+
+ /*
+ * This check is for implementations of strftime which treat %z
+ * (timezone as [+-]hhmm ) like %Z (timezone as characters), or
+ * completely ignore %z. This section is not needed on freebsd.
+ * I'm not sure this is completely right, but it should work OK
+ * for EST and EDT...
+ */
+#ifdef STRFTIME_WRONG_z
+ destp = strrchr(tempstr, ':');
+ if (destp != NULL) {
+ destp += 3;
+ if ((*destp != '+') && (*destp != '-')) {
+ char savday[6];
+ int tzmin = timezone / 60;
+ int tzhr = tzmin / 60;
+ if (daylight)
+ tzhr--;
+ strcpy(savday, destp + strlen(destp) - 4);
+ snprintf(destp, (destp - tempstr), "%+03d%02d",
+ (-1*tzhr), tzmin % 60);
+ strcat(destp, savday);
+ }
+ }
+#endif
+
+ if (strsize > TIMESTR_SIZE) {
+ strsize = TIMESTR_SIZE;
+ strp[TIMESTR_SIZE+1] = '\0';
+ }
+ strlcpy(strp, tempstr, strsize);
+}
+
+/* routines for writing transfer-statistic records */
+void
+trstat_init(struct printer *pp, const char *fname, int filenum)
+{
+ register const char *srcp;
+ register char *destp, *endp;
+
+ /*
+ * Figure out the job id of this file. The filename should be
+ * 'cf', 'df', or maybe 'tf', followed by a letter (or sometimes
+ * two), followed by the jobnum, followed by a hostname.
+ * The jobnum is usually 3 digits, but might be as many as 5.
+ * Note that some care has to be taken parsing this, as the
+ * filename could be coming from a remote-host, and thus might
+ * not look anything like what is expected...
+ */
+ memset(pp->jobnum, 0, sizeof(pp->jobnum));
+ pp->jobnum[0] = '0';
+ srcp = strchr(fname, '/');
+ if (srcp == NULL)
+ srcp = fname;
+ destp = &(pp->jobnum[0]);
+ endp = destp + 5;
+ while (*srcp != '\0' && (*srcp < '0' || *srcp > '9'))
+ srcp++;
+ while (*srcp >= '0' && *srcp <= '9' && destp < endp)
+ *(destp++) = *(srcp++);
+
+ /* get the starting time in both numeric and string formats, and
+ * save those away along with the file-number */
+ pp->jobdfnum = filenum;
+ lpd_gettime(&pp->tr_start, pp->tr_timestr, (size_t)TIMESTR_SIZE);
+
+ return;
+}
+
+void
+trstat_write(struct printer *pp, tr_sendrecv sendrecv, size_t bytecnt,
+ const char *userid, const char *otherhost, const char *orighost)
+{
+#define STATLINE_SIZE 1024
+ double trtime;
+ size_t remspace;
+ int statfile;
+ char thishost[MAXHOSTNAMELEN], statline[STATLINE_SIZE];
+ char *eostat;
+ const char *lprhost, *recvdev, *recvhost, *rectype;
+ const char *sendhost, *statfname;
+#define UPD_EOSTAT(xStr) do { \
+ eostat = strchr(xStr, '\0'); \
+ remspace = eostat - xStr; \
+} while(0)
+
+ lpd_gettime(&pp->tr_done, NULL, (size_t)0);
+ trtime = DIFFTIME_TS(pp->tr_done, pp->tr_start);
+
+ gethostname(thishost, sizeof(thishost));
+ lprhost = sendhost = recvhost = recvdev = NULL;
+ switch (sendrecv) {
+ case TR_SENDING:
+ rectype = "send";
+ statfname = pp->stat_send;
+ sendhost = thishost;
+ recvhost = otherhost;
+ break;
+ case TR_RECVING:
+ rectype = "recv";
+ statfname = pp->stat_recv;
+ sendhost = otherhost;
+ recvhost = thishost;
+ break;
+ case TR_PRINTING:
+ /*
+ * This case is for copying to a device (presumably local,
+ * though filters using things like 'net/CAP' can confuse
+ * this assumption...).
+ */
+ rectype = "prnt";
+ statfname = pp->stat_send;
+ sendhost = thishost;
+ recvdev = _PATH_DEFDEVLP;
+ if (pp->lp) recvdev = pp->lp;
+ break;
+ default:
+ /* internal error... should we syslog/printf an error? */
+ return;
+ }
+ if (statfname == NULL)
+ return;
+
+ /*
+ * the original-host and userid are found out by reading thru the
+ * cf (control-file) for the job. Unfortunately, on incoming jobs
+ * the df's (data-files) are sent before the matching cf, so the
+ * orighost & userid are generally not-available for incoming jobs.
+ *
+ * (it would be nice to create a work-around for that..)
+ */
+ if (orighost && (*orighost != '\0'))
+ lprhost = orighost;
+ else
+ lprhost = ".na.";
+ if (*userid == '\0')
+ userid = NULL;
+
+ /*
+ * Format of statline.
+ * Some of the keywords listed here are not implemented here, but
+ * they are listed to reserve the meaning for a given keyword.
+ * Fields are separated by a blank. The fields in statline are:
+ * <tstamp> - time the transfer started
+ * <ptrqueue> - name of the printer queue (the short-name...)
+ * <hname> - hostname the file originally came from (the
+ * 'lpr host'), if known, or "_na_" if not known.
+ * <xxx> - id of job from that host (generally three digits)
+ * <n> - file count (# of file within job)
+ * <rectype> - 4-byte field indicating the type of transfer
+ * statistics record. "send" means it's from the
+ * host sending a datafile, "recv" means it's from
+ * a host as it receives a datafile.
+ * user=<userid> - user who sent the job (if known)
+ * secs=<n> - seconds it took to transfer the file
+ * bytes=<n> - number of bytes transfered (ie, "bytecount")
+ * bps=<n.n>e<n> - Bytes/sec (if the transfer was "big enough"
+ * for this to be useful)
+ * ! top=<str> - type of printer (if the type is defined in
+ * printcap, and if this statline is for sending
+ * a file to that ptr)
+ * ! qls=<n> - queue-length at start of send/print-ing a job
+ * ! qle=<n> - queue-length at end of send/print-ing a job
+ * sip=<addr> - IP address of sending host, only included when
+ * receiving a job.
+ * shost=<hname> - sending host (if that does != the original host)
+ * rhost=<hname> - hostname receiving the file (ie, "destination")
+ * rdev=<dev> - device receiving the file, when the file is being
+ * send to a device instead of a remote host.
+ *
+ * Note: A single print job may be transferred multiple times. The
+ * original 'lpr' occurs on one host, and that original host might
+ * send to some interim host (or print server). That interim host
+ * might turn around and send the job to yet another host (most likely
+ * the real printer). The 'shost=' parameter is only included if the
+ * sending host for this particular transfer is NOT the same as the
+ * host which did the original 'lpr'.
+ *
+ * Many values have 'something=' tags before them, because they are
+ * in some sense "optional", or their order may vary. "Optional" may
+ * mean in the sense that different SITES might choose to have other
+ * fields in the record, or that some fields are only included under
+ * some circumstances. Programs processing these records should not
+ * assume the order or existence of any of these keyword fields.
+ */
+ snprintf(statline, STATLINE_SIZE, "%s %s %s %s %03ld %s",
+ pp->tr_timestr, pp->printer, lprhost, pp->jobnum,
+ pp->jobdfnum, rectype);
+ UPD_EOSTAT(statline);
+
+ if (userid != NULL) {
+ snprintf(eostat, remspace, " user=%s", userid);
+ UPD_EOSTAT(statline);
+ }
+ snprintf(eostat, remspace, " secs=%#.2f bytes=%lu", trtime,
+ (unsigned long)bytecnt);
+ UPD_EOSTAT(statline);
+
+ /*
+ * The bps field duplicates info from bytes and secs, so do
+ * not bother to include it for very small files.
+ */
+ if ((bytecnt > 25000) && (trtime > 1.1)) {
+ snprintf(eostat, remspace, " bps=%#.2e",
+ ((double)bytecnt/trtime));
+ UPD_EOSTAT(statline);
+ }
+
+ if (sendrecv == TR_RECVING) {
+ if (remspace > 5+strlen(from_ip) ) {
+ snprintf(eostat, remspace, " sip=%s", from_ip);
+ UPD_EOSTAT(statline);
+ }
+ }
+ if (0 != strcmp(lprhost, sendhost)) {
+ if (remspace > 7+strlen(sendhost) ) {
+ snprintf(eostat, remspace, " shost=%s", sendhost);
+ UPD_EOSTAT(statline);
+ }
+ }
+ if (recvhost) {
+ if (remspace > 7+strlen(recvhost) ) {
+ snprintf(eostat, remspace, " rhost=%s", recvhost);
+ UPD_EOSTAT(statline);
+ }
+ }
+ if (recvdev) {
+ if (remspace > 6+strlen(recvdev) ) {
+ snprintf(eostat, remspace, " rdev=%s", recvdev);
+ UPD_EOSTAT(statline);
+ }
+ }
+ if (remspace > 1) {
+ strcpy(eostat, "\n");
+ } else {
+ /* probably should back up to just before the final " x=".. */
+ strcpy(statline+STATLINE_SIZE-2, "\n");
+ }
+ statfile = open(statfname, O_WRONLY|O_APPEND, 0664);
+ if (statfile < 0) {
+ /* statfile was given, but we can't open it. should we
+ * syslog/printf this as an error? */
+ return;
+ }
+ write(statfile, statline, strlen(statline));
+ close(statfile);
+
+ return;
+#undef UPD_EOSTAT
+}
+
+#include <stdarg.h>
+
+void
+fatal(const struct printer *pp, const char *msg, ...)
+{
+ va_list ap;
+ va_start(ap, msg);
+ /* this error message is being sent to the 'from_host' */
+ if (from_host != local_host)
+ (void)printf("%s: ", local_host);
+ (void)printf("%s: ", progname);
+ if (pp && pp->printer)
+ (void)printf("%s: ", pp->printer);
+ (void)vprintf(msg, ap);
+ va_end(ap);
+ (void)putchar('\n');
+ exit(1);
+}
+
+/*
+ * Close all file descriptors from START on up.
+ * This is a horrific kluge, since getdtablesize() might return
+ * ``infinity'', in which case we will be spending a long time
+ * closing ``files'' which were never open. Perhaps it would
+ * be better to close the first N fds, for some small value of N.
+ */
+void
+closeallfds(int start)
+{
+ int stop = getdtablesize();
+ for (; start < stop; start++)
+ close(start);
+}
+
diff --git a/usr.sbin/lpr/common_source/ctlinfo.c b/usr.sbin/lpr/common_source/ctlinfo.c
new file mode 100644
index 0000000..085db04
--- /dev/null
+++ b/usr.sbin/lpr/common_source/ctlinfo.c
@@ -0,0 +1,868 @@
+/*
+ * ------+---------+---------+---------+---------+---------+---------+---------*
+ * Copyright (c) 2001 - Garance Alistair Drosehn <gad@FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation
+ * are those of the authors and should not be interpreted as representing
+ * official policies, either expressed or implied, of the FreeBSD Project.
+ *
+ * ------+---------+---------+---------+---------+---------+---------+---------*
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/*
+ * ctlinfo - This collection of routines will know everything there is to
+ * know about the information inside a control file ('cf*') which is used
+ * to describe a print job in lpr & friends. The eventual goal is that it
+ * will be the ONLY source file to know what's inside these control-files.
+ */
+
+/*
+ * Some define's useful for debuging.
+ * TRIGGERTEST_FNAME and DEBUGREADCF_FNAME, allow us to do testing on
+ * a per-spool-directory basis.
+ */
+/* #define TRIGGERTEST_FNAME "LpdTestRenameTF" */
+/* #define DEBUGREADCF_FNAME "LpdDebugReadCF" */
+/* #define LEAVE_TMPCF_FILES 1 */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include "ctlinfo.h"
+
+struct cjprivate {
+ struct cjobinfo pub;
+ char *cji_buff; /* buffer for getline */
+ char *cji_eobuff; /* last byte IN the buffer */
+ FILE *cji_fstream;
+ int cji_buffsize; /* # bytes in the buffer */
+ int cji_dumpit;
+};
+
+#define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
+
+/*
+ * This has to be large enough to fit the maximum length of a single line
+ * in a control-file, including the leading 'command id', a trailing '\n'
+ * and ending '\0'. The max size of an 'U'nlink line, for instance, is
+ * 1 ('U') + PATH_MAX (filename) + 2 ('\n\0'). The maximum 'H'ost line is
+ * 1 ('H') + NI_MAXHOST (remote hostname) + 2 ('\n\0'). Other lines can be
+ * even longer than those. So, pick some nice, large, arbitrary value.
+ */
+#define CTI_LINEMAX PATH_MAX+NI_MAXHOST+5
+
+extern const char *from_host; /* client's machine name */
+extern const char *from_ip; /* client machine's IP address */
+
+__BEGIN_DECLS
+void ctl_dumpcji(FILE *_dbg_stream, const char *_heading,
+ struct cjobinfo *_cjinf);
+void ctl_freeinf(struct cjobinfo *_cjinf);
+static char *ctl_getline(struct cjobinfo *_cjinf);
+struct cjobinfo *ctl_readcf(const char *_ptrname, const char *_cfname);
+static void ctl_rewindcf(struct cjobinfo *_cjinf);
+char *ctl_rmjob(const char *_ptrname, const char *_cfname);
+__END_DECLS
+
+/*
+ * Here are some things which might be needed when compiling this under
+ * platforms other than FreeBSD.
+ */
+#ifndef __FreeBSD__
+# ifndef NAME_MAX
+# define NAME_MAX 255
+# endif
+# ifndef NI_MAXHOST
+# define NI_MAXHOST 1025
+# endif
+# ifndef PATH_MAX
+# define PATH_MAX 1024
+# endif
+__BEGIN_DECLS
+char *strdup(const char *_src);
+size_t strlcpy(char *_dst, const char *_src, size_t _siz);
+__END_DECLS
+#endif
+
+/*
+ * Control-files (cf*) have the following format.
+ *
+ * Each control-file describes a single job. It will list one or more
+ * "datafiles" (df*) which should be copied to some printer. Usually
+ * there is only one datafile per job. For the curious, RFC 1179 is an
+ * informal and out-of-date description of lpr/lpd circa 1990.
+ *
+ * Each line in the file gives an attribute of the job as a whole, or one
+ * of the datafiles in the job, or a "command" indicating something to do
+ * with one of the datafiles. Each line starts with an 'id' that indicates
+ * what that line is there for. The 'id' is historically a single byte,
+ * but may be multiple bytes (obviously it would be best if multi-byte ids
+ * started with some letter not already used as a single-byte id!).
+ * After the 'id', the remainder of the line will be the value of the
+ * indicated attribute, or a name of the datafile to be operated on.
+ *
+ * In the following lists of ids, the ids with a '!' in front of them are
+ * NOT explicitly supported by this version of lpd, or at least "not yet
+ * supported". They are only listed for reference purposes, so people
+ * won't be tempted to reuse the same id for a different purpose.
+ *
+ * The following are attributes of the job which should not appear more
+ * than once in a control file. Only the 'H' and 'P' lines are required
+ * by the RFC, but some implementations of lpr won't even get that right.
+ *
+ * ! A - [used by lprNG]
+ * B - As far as I know, this is never used as a single-byte id.
+ * Therefore, I intend to use it for multi-byte id codes.
+ * C - "class name" to display on banner page (this is sometimes
+ * used to hold options for print filters)
+ * ! D - [in lprNG, "timestamp" of when the job was submitted]
+ * ! E - "environment variables" to set [some versions of linux]
+ * H - "host name" of machine where the original 'lpr' was done
+ * I - "indent", the amount to indent output
+ * J - "job name" to display on banner page
+ * L - "literal" user's name as it should be displayed on the
+ * banner page (it is the existence of an 'L' line which
+ * indicates that a job should have a banner page).
+ * M - "mail", userid to mail to when done printing (with email
+ * going to 'M'@'H', so to speak).
+ * P - "person", the user's login name (e.g. for accounting)
+ * ! Q - [used by lprNG for queue-name]
+ * R - "resolution" in dpi, for some laser printer queues
+ * T - "title" for files sent thru 'pr'
+ * W - "width" to use for printing plain-text files
+ * Z - In BSD, "locale" to use for datafiles sent thru 'pr'.
+ * (this BSD usage should move to a different id...)
+ * [in lprNG - this line holds the "Z options"]
+ * 1 - "R font file" for files sent thru troff
+ * 2 - "I font file" for files sent thru troff
+ * 3 - "B font file" for files sent thru troff
+ * 4 - "S font file" for files sent thru troff
+ *
+ * The following are attributes attached to a datafile, and thus may
+ * appear multiple times in a control file (once per datafile):
+ *
+ * N - "name" of file (for display purposes, used by 'lpq')
+ * S - "stat() info" used for symbolic link ('lpr -s')
+ * security checks.
+ *
+ * The following indicate actions to take on a given datafile. The same
+ * datafile may appear on more than one "print this file" command in the
+ * control file. Note that ALL ids with lowercase letters are expected
+ * to be actions to "print this file":
+ *
+ * c - "file name", cifplot file to print. This action appears
+ * when the user has requested 'lpr -c'.
+ * d - "file name", dvi file to print, user requested 'lpr -d'
+ * f - "file name", a plain-text file to print = "standard"
+ * g - "file name", plot(1G) file to print, ie 'lpr -g'
+ * l - "file name", text file with control chars which should
+ * be printed literally, ie 'lpr -l' (note: some printers
+ * take this id as a request to print a postscript file,
+ * and because of *that* some OS's use 'l' to indicate
+ * that a datafile is a postscript file)
+ * n - "file name", ditroff(1) file to print, ie 'lpr -n'
+ * o - "file name", a postscript file to print. This id is
+ * described in the original RFC, but not much has been
+ * done with it. This 'lpr' does not generate control
+ * lines with 'o'-actions, but lpd's printjob processing
+ * will treat it the same as 'l'.
+ * p - "file name", text file to print with pr(1), ie 'lpr -p'
+ * t - "file name", troff(1) file to print, ie 'lpr -t'
+ * v - "file name", plain raster file to print
+ *
+ * U - "file name" of datafile to unlink (ie, remove file
+ * from spool directory. To be done in a 'Pass 2',
+ * AFTER having processed all datafiles in the job).
+ *
+ */
+
+void
+ctl_freeinf(struct cjobinfo *cjinf)
+{
+#define FREESTR(xStr) \
+ if (xStr != NULL) { \
+ free(xStr); \
+ xStr = NULL;\
+ }
+
+ struct cjprivate *cpriv;
+
+ if (cjinf == NULL)
+ return;
+ cpriv = cjinf->cji_priv;
+ if ((cpriv == NULL) || (cpriv != cpriv->pub.cji_priv)) {
+ syslog(LOG_ERR, "in ctl_freeinf(%p): invalid cjinf (cpriv %p)",
+ (void *)cjinf, (void *)cpriv);
+ return;
+ }
+
+ FREESTR(cpriv->pub.cji_accthost);
+ FREESTR(cpriv->pub.cji_acctuser);
+ FREESTR(cpriv->pub.cji_class);
+ FREESTR(cpriv->pub.cji_curqueue);
+ /* [cpriv->pub.cji_fname is part of cpriv-malloced area] */
+ FREESTR(cpriv->pub.cji_jobname);
+ FREESTR(cpriv->pub.cji_mailto);
+ FREESTR(cpriv->pub.cji_username);
+
+ if (cpriv->cji_fstream != NULL) {
+ fclose(cpriv->cji_fstream);
+ cpriv->cji_fstream = NULL;
+ }
+
+ cjinf->cji_priv = NULL;
+ free(cpriv);
+#undef FREESTR
+}
+
+#ifdef DEBUGREADCF_FNAME
+static FILE *ctl_dbgfile = NULL;
+static struct stat ctl_dbgstat;
+#endif
+static int ctl_dbgline = 0;
+
+struct cjobinfo *
+ctl_readcf(const char *ptrname, const char *cfname)
+{
+ int id;
+ char *lbuff;
+ void *cstart;
+ FILE *cfile;
+ struct cjprivate *cpriv;
+ struct cjobinfo *cjinf;
+ size_t msize, sroom, sroom2;
+
+ cfile = fopen(cfname, "r");
+ if (cfile == NULL) {
+ syslog(LOG_ERR, "%s: ctl_readcf error fopen(%s): %s",
+ ptrname, cfname, strerror(errno));
+ return NULL;
+ }
+
+ sroom = roundup(sizeof(struct cjprivate), 8);
+ sroom2 = sroom + strlen(cfname) + 1;
+ sroom2 = roundup(sroom2, 8);
+ msize = sroom2 + CTI_LINEMAX;
+ msize = roundup(msize, 8);
+ cstart = malloc(msize);
+ if (cstart == NULL)
+ return NULL;
+ memset(cstart, 0, msize);
+ cpriv = (struct cjprivate *)cstart;
+ cpriv->pub.cji_priv = cpriv;
+
+ cpriv->pub.cji_fname = (char *)cstart + sroom;
+ strcpy(cpriv->pub.cji_fname, cfname);
+ cpriv->cji_buff = (char *)cstart + sroom2;
+ cpriv->cji_buffsize = (int)(msize - sroom2);
+ cpriv->cji_eobuff = (char *)cstart + msize - 1;
+
+ cpriv->cji_fstream = cfile;
+ cpriv->pub.cji_curqueue = strdup(ptrname);
+
+ ctl_dbgline = 0;
+#ifdef DEBUGREADCF_FNAME
+ ctl_dbgfile = NULL;
+ id = stat(DEBUGREADCF_FNAME, &ctl_dbgstat);
+ if (id != -1) {
+ /* the file exists in this spool directory, write some simple
+ * debugging info to it */
+ ctl_dbgfile = fopen(DEBUGREADCF_FNAME, "a");
+ if (ctl_dbgfile != NULL) {
+ fprintf(ctl_dbgfile, "%s: s=%p r=%ld e=%p %p->%s\n",
+ ptrname, (void *)cpriv, (long)sroom,
+ cpriv->cji_eobuff, cpriv->pub.cji_fname,
+ cpriv->pub.cji_fname);
+ }
+ }
+#endif
+ /*
+ * Copy job-attribute values from control file to the struct of
+ * "public" information. In some cases, it is invalid for the
+ * value to be a null-string, so that is ignored.
+ */
+ cjinf = &(cpriv->pub);
+ lbuff = ctl_getline(cjinf);
+ while (lbuff != NULL) {
+ id = *lbuff++;
+ switch (id) {
+ case 'C':
+ cpriv->pub.cji_class = strdup(lbuff);
+ break;
+ case 'H':
+ if (*lbuff == '\0')
+ break;
+ cpriv->pub.cji_accthost = strdup(lbuff);
+ break;
+ case 'J':
+ cpriv->pub.cji_jobname = strdup(lbuff);
+ break;
+ case 'L':
+ cpriv->pub.cji_username = strdup(lbuff);
+ break;
+ case 'M':
+ /*
+ * No valid mail-to address would start with a minus.
+ * If this one does, it is probably some trickster who
+ * is trying to trigger options on sendmail. Ignore.
+ */
+ if (*lbuff == '-')
+ break;
+ if (*lbuff == '\0')
+ break;
+ cpriv->pub.cji_mailto = strdup(lbuff);
+ break;
+ case 'P':
+ /* don't allow userid's with a leading minus, either */
+ if (*lbuff == '-')
+ break;
+ if (*lbuff == '\0')
+ break;
+ cpriv->pub.cji_acctuser = strdup(lbuff);
+ break;
+ default:
+ if (islower(id)) {
+ cpriv->pub.cji_dfcount++;
+ }
+ break;
+ }
+ lbuff = ctl_getline(cjinf);
+ }
+
+ /* the 'H'ost and 'P'erson fields are *always* supposed to be there */
+ if (cpriv->pub.cji_accthost == NULL)
+ cpriv->pub.cji_accthost = strdup(".na.");
+ if (cpriv->pub.cji_acctuser == NULL)
+ cpriv->pub.cji_acctuser = strdup(".na.");
+
+#ifdef DEBUGREADCF_FNAME
+ if (ctl_dbgfile != NULL) {
+ if (cpriv->cji_dumpit)
+ ctl_dumpcji(ctl_dbgfile, "end readcf", &(cpriv->pub));
+ fclose(ctl_dbgfile);
+ ctl_dbgfile = NULL;
+ }
+#endif
+ return &(cpriv->pub);
+}
+
+/*
+ * This routine renames the temporary control file as received from some
+ * other (remote) host. That file will almost always with `tfA*', because
+ * recvjob.c creates the file by changing `c' to `t' in the original name
+ * for the control file. Now if you read the RFC, you would think that all
+ * control filenames start with `cfA*'. However, it seems there are some
+ * implementations which send control filenames which start with `cf'
+ * followed by *any* letter, so this routine can not assume what the third
+ * letter will (or will not) be. Sigh.
+ *
+ * So this will rewrite the temporary file to `rf*' (correcting any lines
+ * which need correcting), rename that `rf*' file to `cf*', and then remove
+ * the original `tf*' temporary file.
+ *
+ * The *main* purpose of this routine is to be paranoid about the contents
+ * of that control file. It is partially meant to protect against people
+ * TRYING to cause trouble (perhaps after breaking into root of some host
+ * that this host will accept print jobs from). The fact that we're willing
+ * to print jobs from some remote host does not mean that we should blindly
+ * do anything that host tells us to do.
+ *
+ * This is also meant to protect us from errors in other implementations of
+ * lpr, particularly since we may want to use some values from the control
+ * file as environment variables when it comes time to print, or as parameters
+ * to commands which will be exec'ed, or values in statistics records.
+ *
+ * This may also do some "conversions" between how different versions of
+ * lpr or lprNG define the contents of various lines in a control file.
+ *
+ * If there is an error, it returns a pointer to a descriptive error message.
+ * Error messages which are RETURNED (as opposed to syslog-ed) do not include
+ * the printer-queue name. Let the caller add that if it is wanted.
+ */
+char *
+ctl_renametf(const char *ptrname, const char *tfname)
+{
+ int chk3rd, newfd, nogood, res;
+ FILE *newcf;
+ struct cjobinfo *cjinf;
+ char *lbuff, *slash, *cp;
+ char tfname2[NAME_MAX+1], cfname2[NAME_MAX+1];
+ char errm[CTI_LINEMAX];
+
+#ifdef TRIGGERTEST_FNAME
+ struct stat tstat;
+ res = stat(TRIGGERTEST_FNAME, &tstat);
+ if (res == -1) {
+ /*
+ * if the trigger file does NOT exist in this spool directory,
+ * then do the exact same steps that the pre-ctlinfo code had
+ * been doing. Ie, very little.
+ */
+ strlcpy(cfname2, tfname, sizeof(cfname2));
+ cfname2[0] = 'c';
+ res = link(tfname, cfname2);
+ if (res < 0) {
+ snprintf(errm, sizeof(errm),
+ "ctl_renametf error link(%s,%s): %s", tfname,
+ cfname2, strerror(errno));
+ return strdup(errm);
+ }
+ unlink(tfname);
+ return NULL;
+ }
+#endif
+ cjinf = NULL; /* in case of early jump to error_ret */
+ newcf = NULL; /* in case of early jump to error_ret */
+ *errm = '\0'; /* in case of early jump to error_ret */
+
+ chk3rd = tfname[2];
+ if ((tfname[0] != 't') || (tfname[1] != 'f') || (!isalpha(chk3rd))) {
+ snprintf(errm, sizeof(errm),
+ "ctl_renametf invalid filename: %s", tfname);
+ goto error_ret;
+ }
+
+ cjinf = ctl_readcf(ptrname, tfname);
+ if (cjinf == NULL) {
+ snprintf(errm, sizeof(errm),
+ "ctl_renametf error cti_readcf(%s)", tfname);
+ goto error_ret;
+ }
+
+ /*
+ * This uses open+fdopen instead of fopen because that combination
+ * gives us greater control over file-creation issues.
+ */
+ strlcpy(tfname2, tfname, sizeof(tfname2));
+ tfname2[0] = 'r'; /* rf<letter><job><hostname> */
+ newfd = open(tfname2, O_WRONLY|O_CREAT|O_TRUNC, 0660);
+ if (newfd == -1) {
+ snprintf(errm, sizeof(errm),
+ "ctl_renametf error open(%s): %s", tfname2,
+ strerror(errno));
+ goto error_ret;
+ }
+ newcf = fdopen(newfd, "w");
+ if (newcf == NULL) {
+ close(newfd);
+ snprintf(errm, sizeof(errm),
+ "ctl_renametf error fopen(%s): %s", tfname2,
+ strerror(errno));
+ goto error_ret;
+ }
+
+ /*
+ * Do extra sanity checks on some key job-attribute fields, and
+ * write them out first (thus making sure they are written in the
+ * order we generally expect them to be in).
+ */
+ /*
+ * Some lpr implementations on PC's set a null-string for their
+ * hostname. A MacOS 10 system which has not correctly setup
+ * /etc/hostconfig will claim a hostname of 'localhost'. Anything
+ * with blanks in it would be an invalid value for hostname. For
+ * any of these invalid hostname values, replace the given value
+ * with the name of the host that this job is coming from.
+ */
+ nogood = 0;
+ if (cjinf->cji_accthost == NULL)
+ nogood = 1;
+ else if (strcmp(cjinf->cji_accthost, ".na.") == 0)
+ nogood = 1;
+ else if (strcmp(cjinf->cji_accthost, "localhost") == 0)
+ nogood = 1;
+ else {
+ for (cp = cjinf->cji_accthost; *cp != '\0'; cp++) {
+ if (*cp <= ' ') {
+ nogood = 1;
+ break;
+ }
+ }
+ }
+ if (nogood)
+ fprintf(newcf, "H%s\n", from_host);
+ else
+ fprintf(newcf, "H%s\n", cjinf->cji_accthost);
+
+ /*
+ * Now do some sanity checks on the 'P' (original userid) value. Note
+ * that the 'P'erson line is the second line which is ALWAYS supposed
+ * to be present in a control file.
+ *
+ * There is no particularly good value to use for replacements, but
+ * at least make sure the value is something reasonable to use in
+ * environment variables and statistics records. Again, some PC
+ * implementations send a null-string for a value. Various Mac
+ * implementations will set whatever string the user has set for
+ * their 'Owner Name', which usually includes blanks, etc.
+ */
+ nogood = 0;
+ if (cjinf->cji_acctuser == NULL)
+ nogood = 1;
+ else {
+ for (cp = cjinf->cji_acctuser; *cp != '\0'; cp++) {
+ if (*cp <= ' ')
+ *cp = '_';
+ }
+ }
+ if (nogood)
+ fprintf(newcf, "P%s\n", ".na.");
+ else
+ fprintf(newcf, "P%s\n", cjinf->cji_acctuser);
+
+ /* No need for sanity checks on class, jobname, "literal" user. */
+ if (cjinf->cji_class != NULL)
+ fprintf(newcf, "C%s\n", cjinf->cji_class);
+ if (cjinf->cji_jobname != NULL)
+ fprintf(newcf, "J%s\n", cjinf->cji_jobname);
+ if (cjinf->cji_username != NULL)
+ fprintf(newcf, "L%s\n", cjinf->cji_username);
+
+ /*
+ * This should probably add more sanity checks on mailto value.
+ * Note that if the mailto value is "wrong", then there's no good
+ * way to know what the "correct" value would be, and we should not
+ * semd email to some random address. At least for now, just ignore
+ * any invalid values.
+ */
+ nogood = 0;
+ if (cjinf->cji_mailto == NULL)
+ nogood = 1;
+ else {
+ for (cp = cjinf->cji_acctuser; *cp != '\0'; cp++) {
+ if (*cp <= ' ') {
+ nogood = 1;
+ break;
+ }
+ }
+ }
+ if (!nogood)
+ fprintf(newcf, "M%s\n", cjinf->cji_mailto);
+
+ /*
+ * Now go thru the old control file, copying all information which
+ * hasn't already been written into the new file.
+ */
+ ctl_rewindcf(cjinf);
+ lbuff = ctl_getline(cjinf);
+ while (lbuff != NULL) {
+ switch (lbuff[0]) {
+ case 'H':
+ case 'P':
+ case 'C':
+ case 'J':
+ case 'L':
+ case 'M':
+ /* already wrote values for these to the newcf */
+ break;
+ case 'N':
+ /* see comments under 'U'... */
+ if (cjinf->cji_dfcount == 0) {
+ /* in this case, 'N's will be done in 'U' */
+ break;
+ }
+ fprintf(newcf, "%s\n", lbuff);
+ break;
+ case 'U':
+ /*
+ * check for the very common case where the remote
+ * host had to process 'lpr -s -r', but it did not
+ * remove the Unlink line from the control file.
+ * Such Unlink lines will legitimately have a '/' in
+ * them, but it is the original lpr host which would
+ * have done the unlink of such files, and not any
+ * host receiving that job.
+ */
+ slash = strchr(lbuff, '/');
+ if (slash != NULL) {
+ break; /* skip this line */
+ }
+ /*
+ * Okay, another kind of broken lpr implementation
+ * is one which send datafiles, and Unlink's those
+ * datafiles, but never includes any PRINT request
+ * for those files. Experimentation shows that one
+ * copy of those datafiles should be printed with a
+ * format of 'f'. If this is an example of such a
+ * screwed-up control file, fix it here.
+ */
+ if (cjinf->cji_dfcount == 0) {
+ lbuff++;
+ if (strncmp(lbuff, "df", (size_t)2) == 0) {
+ fprintf(newcf, "f%s\n", lbuff);
+ fprintf(newcf, "U%s\n", lbuff);
+ fprintf(newcf, "N%s\n", lbuff);
+ }
+ break;
+ }
+ fprintf(newcf, "%s\n", lbuff);
+ break;
+ default:
+ fprintf(newcf, "%s\n", lbuff);
+ break;
+ }
+ lbuff = ctl_getline(cjinf);
+ }
+
+ ctl_freeinf(cjinf);
+ cjinf = NULL;
+
+ res = fclose(newcf);
+ newcf = NULL;
+ if (res != 0) {
+ snprintf(errm, sizeof(errm),
+ "ctl_renametf error fclose(%s): %s", tfname2,
+ strerror(errno));
+ goto error_ret;
+ }
+
+ strlcpy(cfname2, tfname, sizeof(cfname2));
+ cfname2[0] = 'c'; /* rename new file to 'cfA*' */
+ res = link(tfname2, cfname2);
+ if (res != 0) {
+ snprintf(errm, sizeof(errm),
+ "ctl_renametf error link(%s,%s): %s", tfname2, cfname2,
+ strerror(errno));
+ goto error_ret;
+ }
+
+ /* All the important work is done. Now just remove temp files */
+#ifdef LEAVE_TMPCF_FILES
+ {
+ struct stat tfstat;
+ size_t size1;
+ tfstat.st_size = 1; /* certainly invalid value */
+ res = stat(tfname, &tfstat);
+ size1 = tfstat.st_size;
+ tfstat.st_size = 2; /* certainly invalid value */
+ res = stat(tfname2, &tfstat);
+ /* if the sizes do not match, or either stat call failed,
+ * then do not remove the temp files, but return "all OK".
+ * This is just so I can see what this routine had changed.
+ */
+ if (size1 != tfstat.st_size)
+ return NULL;
+ }
+#endif
+ unlink(tfname);
+ unlink(tfname2);
+
+ return NULL;
+
+error_ret:
+ if (cjinf != NULL)
+ ctl_freeinf(cjinf);
+ if (newcf != NULL)
+ fclose(newcf);
+
+ if (*errm != '\0')
+ return strdup(errm);
+ return strdup("ctl_renametf internal (missed) error");
+}
+
+void
+ctl_rewindcf(struct cjobinfo *cjinf)
+{
+ struct cjprivate *cpriv;
+
+ if (cjinf == NULL)
+ return;
+ cpriv = cjinf->cji_priv;
+ if ((cpriv == NULL) || (cpriv != cpriv->pub.cji_priv)) {
+ syslog(LOG_ERR, "in ctl_rewindcf(%p): invalid cjinf (cpriv %p)",
+ (void *)cjinf, (void *)cpriv);
+ return;
+ }
+
+ rewind(cpriv->cji_fstream); /* assume no errors... :-) */
+}
+
+char *
+ctl_rmjob(const char *ptrname, const char *cfname)
+{
+ struct cjobinfo *cjinf;
+ char *lbuff;
+ char errm[CTI_LINEMAX];
+
+ cjinf = ctl_readcf(ptrname, cfname);
+ if (cjinf == NULL) {
+ snprintf(errm, sizeof(errm),
+ "ctl_renametf error cti_readcf(%s)", cfname);
+ return strdup(errm);
+ }
+
+ ctl_rewindcf(cjinf);
+ lbuff = ctl_getline(cjinf);
+ while (lbuff != NULL) {
+ /* obviously we need to fill in the following... */
+ switch (lbuff[0]) {
+ case 'S':
+ break;
+ case 'U':
+ break;
+ default:
+ break;
+ }
+ lbuff = ctl_getline(cjinf);
+ }
+
+ ctl_freeinf(cjinf);
+ cjinf = NULL;
+
+ return NULL;
+}
+
+/*
+ * The following routine was originally written to pin down a bug. It is
+ * no longer needed for that problem, but may be useful to keep around for
+ * other debugging.
+ */
+void
+ctl_dumpcji(FILE *dbg_stream, const char *heading, struct cjobinfo *cjinf)
+{
+#define PRINTSTR(xHdr,xStr) \
+ astr = xStr; \
+ ctl_dbgline++; \
+ fprintf(dbg_stream, "%4d] %12s = ", ctl_dbgline, xHdr); \
+ if (astr == NULL) \
+ fprintf(dbg_stream, "NULL\n"); \
+ else \
+ fprintf(dbg_stream, "%p -> %s\n", astr, astr)
+
+ struct cjprivate *cpriv;
+ char *astr;
+
+ if (cjinf == NULL) {
+ fprintf(dbg_stream,
+ "ctl_dumpcji: ptr to cjobinfo for '%s' is NULL\n",
+ heading);
+ return;
+ }
+ cpriv = cjinf->cji_priv;
+
+ fprintf(dbg_stream, "ctl_dumpcji: Dump '%s' of cjobinfo at %p->%p\n",
+ heading, (void *)cjinf, cpriv->cji_buff);
+
+ PRINTSTR("accthost.H", cpriv->pub.cji_accthost);
+ PRINTSTR("acctuser.P", cpriv->pub.cji_acctuser);
+ PRINTSTR("class.C", cpriv->pub.cji_class);
+ PRINTSTR("cf-qname", cpriv->pub.cji_curqueue);
+ PRINTSTR("cf-fname", cpriv->pub.cji_fname);
+ PRINTSTR("jobname.J", cpriv->pub.cji_jobname);
+ PRINTSTR("mailto.M", cpriv->pub.cji_mailto);
+ PRINTSTR("hdruser.L", cpriv->pub.cji_username);
+
+ ctl_dbgline++;
+ fprintf(dbg_stream, "%4d] %12s = ", ctl_dbgline, "*cjprivate");
+ if (cpriv->pub.cji_priv == NULL)
+ fprintf(dbg_stream, "NULL !!\n");
+ else
+ fprintf(dbg_stream, "%p\n", (void *)cpriv->pub.cji_priv);
+
+ fprintf(dbg_stream, "|- - - - --> Dump '%s' complete\n", heading);
+
+ /* flush output for the benefit of anyone doing a 'tail -f' */
+ fflush(dbg_stream);
+
+#undef PRINTSTR
+}
+
+/*
+ * This routine reads in the next line from the control-file, and removes
+ * the trailing newline character.
+ *
+ * Historical note: Earlier versions of this routine did tab-expansion for
+ * ALL lines read in, which did not make any sense for most of the lines
+ * in a control file. For the lines where tab-expansion is useful, it will
+ * now have to be done by the calling routine.
+ */
+static char *
+ctl_getline(struct cjobinfo *cjinf)
+{
+ char *strp, *nl;
+ struct cjprivate *cpriv;
+
+ if (cjinf == NULL)
+ return NULL;
+ cpriv = cjinf->cji_priv;
+ if ((cpriv == NULL) || (cpriv != cpriv->pub.cji_priv)) {
+ syslog(LOG_ERR, "in ctl_getline(%p): invalid cjinf (cpriv %p)",
+ (void *)cjinf, (void *)cpriv);
+ return NULL;
+ }
+
+ errno = 0;
+ strp = fgets(cpriv->cji_buff, cpriv->cji_buffsize, cpriv->cji_fstream);
+ if (strp == NULL) {
+ if (errno != 0)
+ syslog(LOG_ERR, "%s: ctl_getline error fgets(%s): %s",
+ cpriv->pub.cji_curqueue, cpriv->pub.cji_fname,
+ strerror(errno));
+ return NULL;
+ }
+ nl = strchr(strp, '\n');
+ if (nl != NULL)
+ *nl = '\0';
+
+#ifdef DEBUGREADCF_FNAME
+ /* I'd like to find out if the previous work to expand tabs was ever
+ * really used, and if so, on what lines and for what reason.
+ * Yes, all this work probably means I'm obsessed about this 'tab'
+ * issue, but isn't programming a matter of obsession?
+ */
+ {
+ int tabcnt;
+ char *ch;
+
+ tabcnt = 0;
+ ch = strp;
+ for (ch = strp; *ch != '\0'; ch++) {
+ if (*ch == '\t')
+ tabcnt++;
+ }
+
+ if (tabcnt && (ctl_dbgfile != NULL)) {
+ cpriv->cji_dumpit++;
+ fprintf(ctl_dbgfile, "%s: tabs=%d '%s'\n",
+ cpriv->pub.cji_fname, tabcnt, cpriv->cji_buff);
+ }
+ }
+#endif
+ return strp;
+}
diff --git a/usr.sbin/lpr/common_source/ctlinfo.h b/usr.sbin/lpr/common_source/ctlinfo.h
new file mode 100644
index 0000000..b79f861
--- /dev/null
+++ b/usr.sbin/lpr/common_source/ctlinfo.h
@@ -0,0 +1,71 @@
+/*
+ * ------+---------+---------+---------+---------+---------+---------+---------*
+ * Copyright (c) 2001 - Garance Alistair Drosehn <gad@FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation
+ * are those of the authors and should not be interpreted as representing
+ * official policies, either expressed or implied, of the FreeBSD Project.
+ *
+ * ------+---------+---------+---------+---------+---------+---------+---------*
+ * $FreeBSD$
+ * ------+---------+---------+---------+---------+---------+---------+---------*
+ */
+
+/*
+ * ctlinfo - This collection of routines will know everything there is to
+ * know about the information inside a control file ('cf*') which is used
+ * to describe a print job in lpr & friends. The eventual goal is that it
+ * will be the ONLY source file to know what's inside these control-files.
+ */
+
+struct cjprivate; /* used internal to ctl* routines */
+
+struct cjobinfo {
+ int cji_dfcount; /* number of data files to print */
+ int cji_uncount; /* number of unlink-file requests */
+ char *cji_accthost; /* the host that this job came from,
+ * for accounting purposes (usually
+ * the host where the original 'lpr'
+ * was done) */
+ char *cji_acctuser; /* userid who should be charged for
+ * this job (usually, the userid which
+ * did the original 'lpr') */
+ char *cji_class; /* class-name */
+ char *cji_curqueue; /* printer-queue that this cf-file is
+ * curently sitting in (mainly used
+ * in syslog error messages) */
+ char *cji_fname; /* filename of the control file */
+ char *cji_jobname; /* job-name (for banner) */
+ char *cji_mailto; /* userid to send email to (or null) */
+ char *cji_username; /* "literal" user-name (for banner) or
+ * NULL if no banner-page is wanted */
+ struct cjprivate *cji_priv;
+};
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+char *ctl_renametf(const char *_ptrname, const char *_tfname);
+__END_DECLS
diff --git a/usr.sbin/lpr/common_source/displayq.c b/usr.sbin/lpr/common_source/displayq.c
new file mode 100644
index 0000000..f7e61d8
--- /dev/null
+++ b/usr.sbin/lpr/common_source/displayq.c
@@ -0,0 +1,538 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#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[MAXNAMLEN+1]; /* current file being printed */
+static char file[MAXNAMLEN+1]; /* print file name */
+static int first; /* first file in ``files'' column? */
+static int garbage; /* # of garbage cf files */
+static int lflag; /* long output option */
+static int rank; /* order to be printed (-1=none, 0=active) */
+static long totsize; /* total print job size in bytes */
+
+static const char *head0 = "Rank Owner Job Files";
+static const char *head1 = "Total Size\n";
+
+static void alarmhandler(int _signo);
+static void warn(const struct printer *_pp);
+
+/*
+ * Display the current state of the queue. Format = 1 if long format.
+ */
+void
+displayq(struct printer *pp, int format)
+{
+ register struct jobqueue *q;
+ register int i, nitems, fd, ret;
+ char *cp, *endp;
+ struct jobqueue **queue;
+ struct stat statb;
+ FILE *fp;
+ void (*savealrm)(int);
+
+ lflag = format;
+ totsize = 0;
+ rank = -1;
+
+ if ((cp = checkremote(pp))) {
+ printf("Warning: %s\n", cp);
+ free(cp);
+ }
+
+ /*
+ * Print out local queue
+ * Find all the control files in the spooling directory
+ */
+ seteuid(euid);
+ if (chdir(pp->spool_dir) < 0)
+ fatal(pp, "cannot chdir to spooling directory: %s",
+ strerror(errno));
+ seteuid(uid);
+ if ((nitems = getq(pp, &queue)) < 0)
+ fatal(pp, "cannot examine spooling area\n");
+ seteuid(euid);
+ ret = stat(pp->lock_file, &statb);
+ seteuid(uid);
+ if (ret >= 0) {
+ if (statb.st_mode & LFM_PRINT_DIS) {
+ if (pp->remote)
+ printf("%s: ", local_host);
+ printf("Warning: %s is down: ", pp->printer);
+ seteuid(euid);
+ fd = open(pp->status_file, O_RDONLY|O_SHLOCK);
+ seteuid(uid);
+ if (fd >= 0) {
+ while ((i = read(fd, line, sizeof(line))) > 0)
+ (void) fwrite(line, 1, i, stdout);
+ (void) close(fd); /* unlocks as well */
+ } else
+ putchar('\n');
+ }
+ if (statb.st_mode & LFM_QUEUE_DIS) {
+ if (pp->remote)
+ printf("%s: ", local_host);
+ printf("Warning: %s queue is turned off\n",
+ pp->printer);
+ }
+ }
+
+ if (nitems) {
+ seteuid(euid);
+ fp = fopen(pp->lock_file, "r");
+ seteuid(uid);
+ if (fp == NULL)
+ warn(pp);
+ else {
+ /* get daemon pid */
+ cp = current;
+ endp = cp + sizeof(current) - 1;
+ while ((i = getc(fp)) != EOF && i != '\n') {
+ if (cp < endp)
+ *cp++ = i;
+ }
+ *cp = '\0';
+ i = atoi(current);
+ if (i <= 0) {
+ ret = -1;
+ } else {
+ seteuid(euid);
+ ret = kill(i, 0);
+ seteuid(uid);
+ }
+ if (ret < 0) {
+ warn(pp);
+ } else {
+ /* read current file name */
+ cp = current;
+ endp = cp + sizeof(current) - 1;
+ while ((i = getc(fp)) != EOF && i != '\n') {
+ if (cp < endp)
+ *cp++ = i;
+ }
+ *cp = '\0';
+ /*
+ * Print the status file.
+ */
+ if (pp->remote)
+ printf("%s: ", local_host);
+ seteuid(euid);
+ fd = open(pp->status_file, O_RDONLY|O_SHLOCK);
+ seteuid(uid);
+ if (fd >= 0) {
+ while ((i = read(fd, line,
+ sizeof(line))) > 0)
+ fwrite(line, 1, i, stdout);
+ close(fd); /* unlocks as well */
+ } else
+ putchar('\n');
+ }
+ (void) fclose(fp);
+ }
+ /*
+ * Now, examine the control files and print out the jobs to
+ * be done for each user.
+ */
+ if (!lflag)
+ header();
+ for (i = 0; i < nitems; i++) {
+ q = queue[i];
+ inform(pp, q->job_cfname);
+ free(q);
+ }
+ free(queue);
+ }
+ if (!pp->remote) {
+ if (nitems == 0)
+ puts("no entries");
+ return;
+ }
+
+ /*
+ * Print foreign queue
+ * Note that a file in transit may show up in either queue.
+ */
+ if (nitems)
+ putchar('\n');
+ (void) snprintf(line, sizeof(line), "%c%s", format ? '\4' : '\3',
+ pp->remote_queue);
+ cp = line;
+ for (i = 0; i < requests && cp-line+10 < sizeof(line) - 1; i++) {
+ cp += strlen(cp);
+ (void) sprintf(cp, " %d", requ[i]);
+ }
+ for (i = 0; i < users && cp - line + 1 + strlen(user[i]) <
+ sizeof(line) - 1; i++) {
+ cp += strlen(cp);
+ *cp++ = ' ';
+ (void) strcpy(cp, user[i]);
+ }
+ strcat(line, "\n");
+ savealrm = signal(SIGALRM, alarmhandler);
+ alarm(pp->conn_timeout);
+ fd = getport(pp, pp->remote_host, 0);
+ alarm(0);
+ (void)signal(SIGALRM, savealrm);
+ if (fd < 0) {
+ if (from_host != local_host)
+ printf("%s: ", local_host);
+ printf("connection to %s is down\n", pp->remote_host);
+ }
+ else {
+ i = strlen(line);
+ if (write(fd, line, i) != i)
+ fatal(pp, "Lost connection");
+ while ((i = read(fd, line, sizeof(line))) > 0)
+ (void) fwrite(line, 1, i, stdout);
+ (void) close(fd);
+ }
+}
+
+/*
+ * Print a warning message if there is no daemon present.
+ */
+static void
+warn(const struct printer *pp)
+{
+ if (pp->remote)
+ printf("%s: ", local_host);
+ puts("Warning: no daemon present");
+ current[0] = '\0';
+}
+
+/*
+ * Print the header for the short listing format
+ */
+void
+header(void)
+{
+ printf("%s", head0);
+ col = strlen(head0)+1;
+ blankfill(SIZCOL);
+ printf("%s", head1);
+}
+
+void
+inform(const struct printer *pp, char *cf)
+{
+ register int copycnt;
+ char savedname[MAXPATHLEN+1];
+ FILE *cfp;
+
+ /*
+ * There's a chance the control file has gone away
+ * in the meantime; if this is the case just keep going
+ */
+ seteuid(euid);
+ if ((cfp = fopen(cf, "r")) == NULL)
+ return;
+ seteuid(uid);
+
+ if (rank < 0)
+ rank = 0;
+ if (pp->remote || garbage || strcmp(cf, current))
+ rank++;
+
+ /*
+ * The cf-file may include commands to print more than one datafile
+ * from the user. For each datafile, the cf-file contains at least
+ * one line which starts with some format-specifier ('a'-'z'), and
+ * a second line ('N'ame) which indicates the original name the user
+ * specified for that file. There can be multiple format-spec lines
+ * for a single Name-line, if the user requested multiple copies of
+ * that file. Standard lpr puts the format-spec line(s) before the
+ * Name-line, while lprNG puts the Name-line before the format-spec
+ * line(s). This section needs to handle the lines in either order.
+ */
+ copycnt = 0;
+ file[0] = '\0';
+ savedname[0] = '\0';
+ while (getline(cfp)) {
+ switch (line[0]) {
+ case 'P': /* Was this file specified in the user's list? */
+ if (!inlist(line+1, cf)) {
+ fclose(cfp);
+ return;
+ }
+ if (lflag) {
+ printf("\n%s: ", line+1);
+ col = strlen(line+1) + 2;
+ prank(rank);
+ blankfill(JOBCOL);
+ printf(" [job %s]\n", cf+3);
+ } else {
+ col = 0;
+ prank(rank);
+ blankfill(OWNCOL);
+ printf("%-10s %-3d ", line+1, atoi(cf+3));
+ col += 16;
+ first = 1;
+ }
+ continue;
+ default: /* some format specifer and file name? */
+ if (line[0] < 'a' || line[0] > 'z')
+ break;
+ if (copycnt == 0 || strcmp(file, line+1) != 0) {
+ strlcpy(file, line + 1, sizeof(file));
+ }
+ copycnt++;
+ /*
+ * deliberately 'continue' to another getline(), so
+ * all format-spec lines for this datafile are read
+ * in and counted before calling show()
+ */
+ continue;
+ case 'N':
+ strlcpy(savedname, line + 1, sizeof(savedname));
+ break;
+ }
+ if ((file[0] != '\0') && (savedname[0] != '\0')) {
+ show(savedname, file, copycnt);
+ copycnt = 0;
+ file[0] = '\0';
+ savedname[0] = '\0';
+ }
+ }
+ fclose(cfp);
+ /* check for a file which hasn't been shown yet */
+ if (file[0] != '\0') {
+ if (savedname[0] == '\0') {
+ /* a safeguard in case the N-ame line is missing */
+ strlcpy(savedname, file, sizeof(savedname));
+ }
+ show(savedname, file, copycnt);
+ }
+ if (!lflag) {
+ blankfill(SIZCOL);
+ printf("%ld bytes\n", totsize);
+ totsize = 0;
+ }
+}
+
+int
+inlist(char *uname, char *cfile)
+{
+ register int *r, n;
+ register char **u, *cp;
+
+ if (users == 0 && requests == 0)
+ return(1);
+ /*
+ * Check to see if it's in the user list
+ */
+ for (u = user; u < &user[users]; u++)
+ if (!strcmp(*u, uname))
+ return(1);
+ /*
+ * Check the request list
+ */
+ for (n = 0, cp = cfile+3; isdigit(*cp); )
+ n = n * 10 + (*cp++ - '0');
+ for (r = requ; r < &requ[requests]; r++)
+ if (*r == n && !strcmp(cp, from_host))
+ return(1);
+ return(0);
+}
+
+void
+show(const char *nfile, const char *datafile, int copies)
+{
+ if (strcmp(nfile, " ") == 0)
+ nfile = "(standard input)";
+ if (lflag)
+ ldump(nfile, datafile, copies);
+ else
+ dump(nfile, datafile, copies);
+}
+
+/*
+ * Fill the line with blanks to the specified column
+ */
+void
+blankfill(int tocol)
+{
+ while (col++ < tocol)
+ putchar(' ');
+}
+
+/*
+ * Give the abbreviated dump of the file names
+ */
+void
+dump(const char *nfile, const char *datafile, int copies)
+{
+ struct stat lbuf;
+ const char etctmpl[] = ", ...";
+ char etc[sizeof(etctmpl)];
+ char *lastsep;
+ short fill, nlen;
+ short rem, remetc;
+
+ /*
+ * Print as many filenames as will fit
+ * (leaving room for the 'total size' field)
+ */
+ fill = first ? 0 : 2; /* fill space for ``, '' */
+ nlen = strlen(nfile);
+ rem = SIZCOL - 1 - col;
+ if (nlen + fill > rem) {
+ if (first) {
+ /* print the right-most part of the name */
+ printf("...%s ", &nfile[3+nlen-rem]);
+ col = SIZCOL;
+ } else if (rem > 0) {
+ /* fit as much of the etc-string as we can */
+ remetc = rem;
+ if (rem > strlen(etctmpl))
+ remetc = strlen(etctmpl);
+ etc[0] = '\0';
+ strncat(etc, etctmpl, remetc);
+ printf("%s", etc);
+ col += remetc;
+ rem -= remetc;
+ /* room for the last segment of this filename? */
+ lastsep = strrchr(nfile, '/');
+ if ((lastsep != NULL) && (rem > strlen(lastsep))) {
+ /* print the right-most part of this name */
+ printf("%s", lastsep);
+ col += strlen(lastsep);
+ } else {
+ /* do not pack any more names in here */
+ blankfill(SIZCOL);
+ }
+ }
+ } else {
+ if (!first)
+ printf(", ");
+ printf("%s", nfile);
+ col += nlen + fill;
+ }
+ first = 0;
+
+ seteuid(euid);
+ if (*datafile && !stat(datafile, &lbuf))
+ totsize += copies * lbuf.st_size;
+ seteuid(uid);
+}
+
+/*
+ * Print the long info about the file
+ */
+void
+ldump(const char *nfile, const char *datafile, int copies)
+{
+ struct stat lbuf;
+
+ putchar('\t');
+ if (copies > 1)
+ printf("%-2d copies of %-19s", copies, nfile);
+ else
+ printf("%-32s", nfile);
+ if (*datafile && !stat(datafile, &lbuf))
+ printf(" %qd bytes", (long long) lbuf.st_size);
+ else
+ printf(" ??? bytes");
+ putchar('\n');
+}
+
+/*
+ * Print the job's rank in the queue,
+ * update col for screen management
+ */
+void
+prank(int n)
+{
+ char rline[100];
+ static const char *r[] = {
+ "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th"
+ };
+
+ if (n == 0) {
+ printf("active");
+ col += 6;
+ return;
+ }
+ if ((n/10)%10 == 1)
+ (void)snprintf(rline, sizeof(rline), "%dth", n);
+ else
+ (void)snprintf(rline, sizeof(rline), "%d%s", n, r[n%10]);
+ col += strlen(rline);
+ printf("%s", rline);
+}
+
+void
+alarmhandler(int signo __unused)
+{
+ /* the signal is ignored */
+ /* (the '__unused' is just to avoid a compile-time warning) */
+}
diff --git a/usr.sbin/lpr/common_source/lp.h b/usr.sbin/lpr/common_source/lp.h
new file mode 100644
index 0000000..81ac7ae
--- /dev/null
+++ b/usr.sbin/lpr/common_source/lp.h
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * From: @(#)lp.h 8.2 (Berkeley) 4/28/95
+ * $FreeBSD$
+ */
+
+#include <sys/queue.h>
+#include <time.h>
+#include <netdb.h>
+
+/*
+ * All this information used to be in global static variables shared
+ * mysteriously by various parts of the lpr/lpd suite.
+ * This structure attempts to centralize all these declarations in the
+ * hope that they can later be made more dynamic.
+ */
+enum lpd_filters { LPF_CIFPLOT, LPF_DVI, LPF_GRAPH, LPF_INPUT,
+ LPF_DITROFF, LPF_OUTPUT, LPF_FORTRAN, LPF_TROFF,
+ LPF_RASTER, LPF_COUNT };
+/* NB: there is a table in common.c giving the mapping from capability names */
+
+struct printer {
+ char *printer; /* printer name */
+ int remote; /* true if RM points to a remote host */
+ int rp_matches_local; /* true if rp has same name as us */
+ int tof; /* true if we are at top-of-form */
+ /* ------------------------------------------------------ */
+ char *acct_file; /* AF: accounting file */
+ long baud_rate; /* BR: baud rate if lp is a tty */
+ char *filters[LPF_COUNT]; /* CF, DF, GF, IF, NF, OF, RF, TF, VF */
+ long conn_timeout; /* CT: TCP connection timeout */
+ long daemon_user; /* DU: daemon user id -- XXX belongs ???? */
+ char *form_feed; /* FF: form feed */
+ long header_last; /* HL: print header last */
+ char *log_file; /* LF: log file */
+ char *lock_file; /* LO: lock file */
+ char *lp; /* LP: device name or network address */
+ long max_copies; /* MC: maximum number of copies allowed */
+ long max_blocks; /* MX: maximum number of blocks to copy */
+ long price100; /* PC: price per 100 units of output */
+ long page_length; /* PL: page length */
+ long page_width; /* PW: page width */
+ long page_pwidth; /* PX: page width in pixels */
+ long page_plength; /* PY: page length in pixels */
+ long resend_copies; /* RC: resend copies to remote host */
+ char *restrict_grp; /* RG: restricted group */
+ char *remote_host; /* RM: remote machine name */
+ char *remote_queue; /* RP: remote printer name */
+ long restricted; /* RS: restricted to those with local accts */
+ long rw; /* RW: open LP for reading and writing */
+ long short_banner; /* SB: short banner */
+ long no_copies; /* SC: suppress multiple copies */
+ char *spool_dir; /* SD: spool directory */
+ long no_formfeed; /* SF: suppress FF on each print job */
+ long no_header; /* SH: suppress header page */
+ char *stat_recv; /* SR: statistics file, receiving jobs */
+ char *stat_send; /* SS: statistics file, sending jobs */
+ char *status_file; /* ST: status file name */
+ char *trailer; /* TR: trailer string send when Q empties */
+ char *mode_set; /* MS: mode set, a la stty */
+
+ /* variables used by trstat*() to keep statistics on file transfers */
+#define JOBNUM_SIZE 8
+ char jobnum[JOBNUM_SIZE];
+ long jobdfnum; /* current datafile number within job */
+ struct timespec tr_start, tr_done;
+#define TIMESTR_SIZE 40 /* holds result from LPD_TIMESTAMP_PATTERN */
+ char tr_timestr[TIMESTR_SIZE];
+#define DIFFTIME_TS(endTS,startTS) \
+ ((double)(endTS.tv_sec - startTS.tv_sec) \
+ + (endTS.tv_nsec - startTS.tv_nsec) * 1.0e-9)
+};
+
+/*
+ * Lists of user names and job numbers, for the benefit of the structs
+ * defined below. We use TAILQs so that requests don't get mysteriously
+ * reversed in process.
+ */
+struct req_user {
+ TAILQ_ENTRY(req_user) ru_link; /* macro glue */
+ char ru_uname[1]; /* name of user */
+};
+TAILQ_HEAD(req_user_head, req_user);
+
+struct req_file {
+ TAILQ_ENTRY(req_file) rf_link; /* macro glue */
+ char rf_type; /* type (lowercase cf file letter) of file */
+ char *rf_prettyname; /* user-visible name of file */
+ char rf_fname[1]; /* name of file */
+};
+TAILQ_HEAD(req_file_head, req_file);
+
+struct req_jobid {
+ TAILQ_ENTRY(req_jobid) rj_link; /* macro glue */
+ int rj_job; /* job number */
+};
+TAILQ_HEAD(req_jobid_head, req_jobid);
+
+/*
+ * Encapsulate all the information relevant to a request in the
+ * lpr/lpd protocol.
+ */
+enum req_type { REQ_START, REQ_RECVJOB, REQ_LIST, REQ_DELETE };
+
+struct request {
+ enum req_type type; /* what sort of request is this? */
+ struct printer prtr; /* which printer is it for? */
+ int remote; /* did request arrive over network? */
+ char *logname; /* login name of requesting user */
+ char *authname; /* authenticated identity of requesting user */
+ char *prettyname; /* ``pretty'' name of requesting user */
+ int privileged; /* was the request from a privileged user? */
+ void *authinfo; /* authentication information */
+ int authentic; /* was the request securely authenticated? */
+
+ /* Information for queries and deletes... */
+ int nusers; /* length of following list... */
+ struct req_user_head users; /* list of users to query/delete */
+ int njobids; /* length of following list... */
+ struct req_jobid_head jobids; /* list of jobids to query/delete */
+};
+
+/*
+ * Global definitions for the line printer system.
+ */
+extern char line[BUFSIZ];
+extern const char *progname; /* program name (lpr, lpq, etc) */
+
+ /*
+ * 'local_host' is the name of the machine that lpd (lpr, whatever)
+ * is actually running on.
+ *
+ * 'from_host' will point to the 'host' variable when receiving a job
+ * from a user on the same host, or "somewhere else" when receiving a
+ * job from a remote host. If 'from_host != local_host', then 'from_ip'
+ * is the character representation of the IP address of from_host (note
+ * that string could be an IPv6 address).
+ *
+ * Also note that when 'from_host' is not pointing at 'local_host', the
+ * string it is pointing at may be as long as NI_MAXHOST (which is very
+ * likely to be much longer than MAXHOSTNAMELEN).
+ */
+extern char local_host[MAXHOSTNAMELEN];
+extern const char *from_host; /* client's machine name */
+extern const char *from_ip; /* client machine's IP address */
+
+extern int requ[]; /* job number of spool entries */
+extern int requests; /* # of spool requests */
+extern char *user[]; /* users to process */
+extern int users; /* # of users in user array */
+extern char *person; /* name of person doing lprm */
+extern u_char family; /* address family */
+
+/*
+ * Structure used for building a sorted list of control files.
+ */
+struct jobqueue {
+ time_t job_time; /* last-mod time of cf-file */
+ char job_cfname[MAXNAMLEN+1]; /* control file name */
+};
+
+/* lpr/lpd generates readable timestamps for logfiles, etc. Have all those
+ * timestamps be in the same format wrt strftime(). This is ISO 8601 format,
+ * with the addition of an easy-readable day-of-the-week field. Note that
+ * '%T' = '%H:%M:%S', and that '%z' is not available on all platforms.
+ */
+#define LPD_TIMESTAMP_PATTERN "%Y-%m-%dT%T%z %a"
+
+/*
+ * Codes to indicate which statistic records trstat_write should write.
+ */
+typedef enum { TR_SENDING, TR_RECVING, TR_PRINTING } tr_sendrecv;
+
+/*
+ * Error codes for our mini printcap library.
+ */
+#define PCAPERR_TCLOOP (-3)
+#define PCAPERR_OSERR (-2)
+#define PCAPERR_NOTFOUND (-1)
+#define PCAPERR_SUCCESS 0
+#define PCAPERR_TCOPEN 1
+
+/*
+ * File modes for the various status files maintained by lpd.
+ */
+#define LOCK_FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH)
+#define LFM_PRINT_DIS (S_IXUSR)
+#define LFM_QUEUE_DIS (S_IXGRP)
+#define LFM_RESET_QUE (S_IXOTH)
+
+#define STAT_FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH)
+#define LOG_FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH)
+#define TEMP_FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH)
+
+/*
+ * Bit-flags for set_qstate() actions, followed by the return values.
+ */
+#define SQS_DISABLEQ 0x01 /* Disable the queuing of new jobs */
+#define SQS_STOPP 0x02 /* Stop the printing of jobs */
+#define SQS_ENABLEQ 0x10 /* Enable the queuing of new jobs */
+#define SQS_STARTP 0x20 /* Start the printing of jobs */
+
+#define SQS_PARMERR -9 /* Invalid parameters from caller */
+#define SQS_CREFAIL -3 /* File did not exist, and create failed */
+#define SQS_CHGFAIL -2 /* File exists, but unable to change state */
+#define SQS_STATFAIL -1 /* Unable to stat() the lock file */
+#define SQS_CHGOK 1 /* File existed, and the state was changed */
+#define SQS_CREOK 2 /* File did not exist, but was created OK */
+#define SQS_SKIPCREOK 3 /* File did not exist, and there was */
+ /* no need to create it */
+
+/*
+ * Command codes used in the protocol.
+ */
+#define CMD_CHECK_QUE '\1'
+#define CMD_TAKE_THIS '\2'
+#define CMD_SHOWQ_SHORT '\3'
+#define CMD_SHOWQ_LONG '\4'
+#define CMD_RMJOB '\5'
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+struct dirent;
+
+void blankfill(int _tocol);
+char *checkremote(struct printer *_pp);
+int chk(char *_file);
+void closeallfds(int _start);
+void delay(int _millisec);
+void displayq(struct printer *_pp, int _format);
+void dump(const char *_nfile, const char *_datafile, int _copies);
+void fatal(const struct printer *_pp, const char *_msg, ...)
+ __printflike(2, 3);
+int firstprinter(struct printer *_pp, int *_error);
+void free_printer(struct printer *_pp);
+void free_request(struct request *_rp);
+int getline(FILE *_cfp);
+int getport(const struct printer *_pp, const char *_rhost, int _rport);
+int getprintcap(const char *_printer, struct printer *_pp);
+int getq(const struct printer *_pp, struct jobqueue *(*_namelist[]));
+void header(void);
+void inform(const struct printer *_pp, char *_cf);
+void init_printer(struct printer *_pp);
+void init_request(struct request *_rp);
+int inlist(char *_uname, char *_cfile);
+int iscf(struct dirent *_d);
+int isowner(char *_owner, char *_file);
+void ldump(const char *_nfile, const char *_datafile, int _copies);
+void lastprinter(void);
+int lockchk(struct printer *_pp, char *_slockf);
+char *lock_file_name(const struct printer *_pp, char *_buf, size_t _len);
+void lpd_gettime(struct timespec *_tsp, char *_strp, size_t _strsize);
+int nextprinter(struct printer *_pp, int *_error);
+const
+char *pcaperr(int _error);
+void prank(int _n);
+void process(const struct printer *_pp, char *_file);
+void rmjob(const char *_printer);
+void rmremote(const struct printer *_pp);
+void setprintcap(char *_newfile);
+int set_qstate(int _action, const char *_lfname);
+void show(const char *_nfile, const char *_datafile, int _copies);
+int startdaemon(const struct printer *_pp);
+char *status_file_name(const struct printer *_pp, char *_buf,
+ size_t _len);
+void trstat_init(struct printer *_pp, const char *_fname, int _filenum);
+void trstat_write(struct printer *_pp, tr_sendrecv _sendrecv,
+ size_t _bytecnt, const char *_userid, const char *_otherhost,
+ const char *_orighost);
+ssize_t writel(int _strm, ...);
+__END_DECLS
diff --git a/usr.sbin/lpr/common_source/lp.local.h b/usr.sbin/lpr/common_source/lp.local.h
new file mode 100644
index 0000000..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..df70ef4
--- /dev/null
+++ b/usr.sbin/lpr/common_source/net.c
@@ -0,0 +1,301 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#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"
+
+/*
+ * 'local_host' is always the hostname of the machine which is running
+ * lpr (lpd, whatever), while 'from_host' either points at 'local_host'
+ * or points at a different buffer when receiving a job from a remote
+ * machine (and that buffer has the hostname of that remote machine).
+ */
+char local_host[MAXHOSTNAMELEN]; /* host running lpd/lpr */
+const char *from_host = local_host; /* client's machine name */
+const char *from_ip = ""; /* client machine's IP address */
+
+#ifdef INET6
+u_char family = PF_UNSPEC;
+#else
+u_char family = PF_INET;
+#endif
+
+extern uid_t uid, euid;
+
+/*
+ * Create a TCP connection to host "rhost" at port "rport".
+ * If rport == 0, then use the printer service port.
+ * Most of this code comes from rcmd.c.
+ */
+int
+getport(const struct printer *pp, const char *rhost, int rport)
+{
+ struct addrinfo hints, *res, *ai;
+ int s, timo = 1, lport = IPPORT_RESERVED - 1;
+ int err, refused = 0;
+
+ /*
+ * Get the host address and port number to connect to.
+ */
+ if (rhost == NULL)
+ fatal(pp, "no remote host to connect to");
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = family;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = 0;
+ err = getaddrinfo(rhost, (rport == 0 ? "printer" : NULL),
+ &hints, &res);
+ if (err)
+ fatal(pp, "%s\n", gai_strerror(err));
+ if (rport != 0)
+ ((struct sockaddr_in *) res->ai_addr)->sin_port = htons(rport);
+
+ /*
+ * Try connecting to the server.
+ */
+ ai = res;
+retry:
+ seteuid(euid);
+ s = rresvport_af(&lport, ai->ai_family);
+ seteuid(uid);
+ if (s < 0) {
+ if (errno != EAGAIN) {
+ if (ai->ai_next) {
+ ai = ai->ai_next;
+ goto retry;
+ }
+ if (refused && timo <= 16) {
+ sleep(timo);
+ timo *= 2;
+ refused = 0;
+ ai = res;
+ goto retry;
+ }
+ }
+ freeaddrinfo(res);
+ return(-1);
+ }
+ if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
+ err = errno;
+ (void) close(s);
+ errno = err;
+ /*
+ * This used to decrement lport, but the current semantics
+ * of rresvport do not provide such a function (in fact,
+ * rresvport should guarantee that the chosen port will
+ * never result in an EADDRINUSE).
+ */
+ if (errno == EADDRINUSE) {
+ goto retry;
+ }
+
+ if (errno == ECONNREFUSED)
+ refused++;
+
+ if (ai->ai_next != NULL) {
+ ai = ai->ai_next;
+ goto retry;
+ }
+ if (refused && timo <= 16) {
+ sleep(timo);
+ timo *= 2;
+ refused = 0;
+ ai = res;
+ goto retry;
+ }
+ freeaddrinfo(res);
+ return(-1);
+ }
+ freeaddrinfo(res);
+ return(s);
+}
+
+/*
+ * Figure out whether the local machine is the same
+ * as the remote machine (RM) entry (if it exists).
+ * We do this by counting the intersection of our
+ * address list and theirs. This is better than the
+ * old method (comparing the canonical names), as it
+ * allows load-sharing between multiple print servers.
+ * The return value is an error message which must be
+ * free()d.
+ */
+char *
+checkremote(struct printer *pp)
+{
+ char lclhost[MAXHOSTNAMELEN];
+ struct addrinfo hints, *local_res, *remote_res, *lr, *rr;
+ char *err;
+ int ncommonaddrs, error;
+ char h1[NI_MAXHOST], h2[NI_MAXHOST];
+
+ if (!pp->rp_matches_local) { /* Remote printer doesn't match local */
+ pp->remote = 1;
+ return NULL;
+ }
+
+ pp->remote = 0; /* assume printer is local */
+ if (pp->remote_host == NULL)
+ return NULL;
+
+ /* get the addresses of the local host */
+ gethostname(lclhost, sizeof(lclhost));
+ lclhost[sizeof(lclhost) - 1] = '\0';
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = family;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_PASSIVE;
+ if ((error = getaddrinfo(lclhost, NULL, &hints, &local_res)) != 0) {
+ asprintf(&err, "unable to get official name "
+ "for local machine %s: %s",
+ lclhost, gai_strerror(error));
+ return err;
+ }
+
+ /* get the official name of RM */
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = family;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_PASSIVE;
+ if ((error = getaddrinfo(pp->remote_host, NULL,
+ &hints, &remote_res)) != 0) {
+ asprintf(&err, "unable to get address list for "
+ "remote machine %s: %s",
+ pp->remote_host, gai_strerror(error));
+ freeaddrinfo(local_res);
+ return err;
+ }
+
+ ncommonaddrs = 0;
+ for (lr = local_res; lr; lr = lr->ai_next) {
+ h1[0] = '\0';
+ if (getnameinfo(lr->ai_addr, lr->ai_addrlen, h1, sizeof(h1),
+ NULL, 0, NI_NUMERICHOST | NI_WITHSCOPEID) != 0)
+ continue;
+ for (rr = remote_res; rr; rr = rr->ai_next) {
+ h2[0] = '\0';
+ if (getnameinfo(rr->ai_addr, rr->ai_addrlen,
+ h2, sizeof(h2), NULL, 0,
+ NI_NUMERICHOST | NI_WITHSCOPEID) != 0)
+ continue;
+ if (strcmp(h1, h2) == 0)
+ ncommonaddrs++;
+ }
+ }
+
+ /*
+ * if the two hosts do not share at least one IP address
+ * then the printer must be remote.
+ */
+ if (ncommonaddrs == 0)
+ pp->remote = 1;
+ freeaddrinfo(local_res);
+ freeaddrinfo(remote_res);
+ return NULL;
+}
+
+/*
+ * This isn't really network-related, but it's used here to write
+ * multi-part strings onto sockets without using stdio. Return
+ * values are as for writev(2).
+ */
+ssize_t
+writel(int strm, ...)
+{
+ va_list ap;
+ int i, n;
+ const char *cp;
+#define NIOV 12
+ struct iovec iov[NIOV], *iovp = iov;
+ ssize_t retval;
+
+ /* first count them */
+ va_start(ap, strm);
+ n = 0;
+ do {
+ cp = va_arg(ap, char *);
+ n++;
+ } while (cp);
+ va_end(ap);
+ n--; /* correct for count of trailing null */
+
+ if (n > NIOV) {
+ iovp = malloc(n * sizeof *iovp);
+ if (iovp == 0)
+ return -1;
+ }
+
+ /* now make up iovec and send */
+ va_start(ap, strm);
+ for (i = 0; i < n; i++) {
+ iovp[i].iov_base = va_arg(ap, char *);
+ iovp[i].iov_len = strlen(iovp[i].iov_base);
+ }
+ va_end(ap);
+ retval = writev(strm, iovp, n);
+ if (iovp != iov)
+ free(iovp);
+ return retval;
+}
diff --git a/usr.sbin/lpr/common_source/pathnames.h b/usr.sbin/lpr/common_source/pathnames.h
new file mode 100644
index 0000000..78ccc7b
--- /dev/null
+++ b/usr.sbin/lpr/common_source/pathnames.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/6/93
+ */
+
+#include <paths.h>
+
+#define _PATH_DEFDEVLP "/dev/lp"
+#define _PATH_DEFSPOOL "/var/spool/output/lpd"
+#define _PATH_HOSTSEQUIV "/etc/hosts.equiv"
+#define _PATH_HOSTSLPD "/etc/hosts.lpd"
+#define _PATH_MASTERLOCK "/var/spool/output/lpd.lock"
+#define _PATH_PR "/usr/bin/pr"
+#define _PATH_PRINTCAP "/etc/printcap"
+#define _PATH_SOCKETNAME "/var/run/printer"
+#define _PATH_VFONT "/usr/libdata/vfont/"
+#define _PATH_VFONTB "/usr/libdata/vfont/B"
+#define _PATH_VFONTI "/usr/libdata/vfont/I"
+#define _PATH_VFONTR "/usr/libdata/vfont/R"
+#define _PATH_VFONTS "/usr/libdata/vfont/S"
+#define _PATH_CHKPRINTCAP "/usr/sbin/chkprintcap"
diff --git a/usr.sbin/lpr/common_source/printcap.c b/usr.sbin/lpr/common_source/printcap.c
new file mode 100644
index 0000000..d949893
--- /dev/null
+++ b/usr.sbin/lpr/common_source/printcap.c
@@ -0,0 +1,449 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#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 *_bp);
+static int capdb_getaltlog(char *_bp, const char *_shrt,
+ const char *_lng);
+static int capdb_getaltnum(char *_bp, const char *_shrt,
+ const char *_lng, long _dflt, long *_result);
+static int capdb_getaltstr(char *_bp, const char *_shrt,
+ const char *lng, const char *_dflt, char **_result);
+static int getprintcap_int(char *_bp, struct printer *_pp);
+
+/*
+ * Change the name of the printcap file. Used by chkprintcap(8),
+ * but could be used by other members of the suite with appropriate
+ * security measures.
+ */
+void
+setprintcap(char *newfile)
+{
+ printcapdb[0] = newfile;
+}
+
+/*
+ * Read the printcap database for printer `printer' into the
+ * struct printer pointed by `pp'. Return values are as for
+ * cgetent(3): -1 means we could not find what we wanted, -2
+ * means a system error occurred (and errno is set), -3 if a
+ * reference (`tc=') loop was detected, and 0 means success.
+ *
+ * Copied from lpr; should add additional capabilities as they
+ * are required by the other programs in the suite so that
+ * printcap-reading is consistent across the entire family.
+ */
+int
+getprintcap(const char *printer, struct printer *pp)
+{
+ int status;
+ char *XXX;
+ char *bp;
+
+ /*
+ * A bug in the declaration of cgetent(3) means that we have
+ * to hide the constness of its third argument.
+ */
+ XXX = (char *)printer;
+ if ((status = cgetent(&bp, printcapdb, XXX)) < 0)
+ return status;
+ status = getprintcap_int(bp, pp);
+ free(bp);
+ return status;
+}
+
+/*
+ * Map the status values returned by cgetfirst/cgetnext into those
+ * used by cgetent, returning truth if there are more records to
+ * examine. This points out what is arguably a bug in the cget*
+ * interface (or at least a nasty wart).
+ */
+static int
+firstnextmap(int *status)
+{
+ switch (*status) {
+ case 0:
+ return 0;
+ case 1:
+ *status = 0;
+ return 1;
+ case 2:
+ *status = 1;
+ return 1;
+ case -1:
+ *status = -2;
+ return 0;
+ case -2:
+ *status = -3;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+/*
+ * Scan through the database of printers using cgetfirst/cgetnext.
+ * Return false of error or end-of-database; else true.
+ */
+int
+firstprinter(struct printer *pp, int *error)
+{
+ int status;
+ char *bp;
+
+ init_printer(pp);
+ status = cgetfirst(&bp, printcapdb);
+ if (firstnextmap(&status) == 0) {
+ if (error)
+ *error = status;
+ return 0;
+ }
+ if (error)
+ *error = status;
+ status = getprintcap_int(bp, pp);
+ free(bp);
+ if (error && status)
+ *error = status;
+ return 1;
+}
+
+int
+nextprinter(struct printer *pp, int *error)
+{
+ int status;
+ char *bp;
+
+ free_printer(pp);
+ status = cgetnext(&bp, printcapdb);
+ if (firstnextmap(&status) == 0) {
+ if (error)
+ *error = status;
+ return 0;
+ }
+ if (error)
+ *error = status;
+ status = getprintcap_int(bp, pp);
+ free(bp);
+ if (error && status)
+ *error = status;
+ return 1;
+}
+
+void
+lastprinter(void)
+{
+ cgetclose();
+}
+
+/*
+ * This must match the order of declaration of enum filter in lp.h.
+ */
+static const char *filters[] = {
+ "cf", "df", "gf", "if", "nf", "of", "rf", "tf", "vf"
+};
+
+static const char *longfilters[] = {
+ "filt.cifplot", "filt.dvi", "filt.plot", "filt.input", "filt.ditroff",
+ "filt.output", "filt.fortran", "filt.troff", "filt.raster"
+};
+
+/*
+ * Internal routine for both getprintcap() and nextprinter().
+ * Actually parse the printcap entry using cget* functions.
+ * Also attempt to figure out the canonical name of the printer
+ * and store a malloced copy of it in pp->printer.
+ */
+static int
+getprintcap_int(char *bp, struct printer *pp)
+{
+ enum lpd_filters filt;
+ char *rp_name;
+ int error;
+
+ if ((pp->printer = capdb_canonical_name(bp)) == 0)
+ return PCAPERR_OSERR;
+
+#define CHK(x) do {if ((x) == PCAPERR_OSERR) return PCAPERR_OSERR;}while(0)
+ CHK(capdb_getaltstr(bp, "af", "acct.file", 0, &pp->acct_file));
+ CHK(capdb_getaltnum(bp, "br", "tty.rate", 0, &pp->baud_rate));
+ CHK(capdb_getaltnum(bp, "ct", "remote.timeout", DEFTIMEOUT,
+ &pp->conn_timeout));
+ CHK(capdb_getaltnum(bp, "du", "daemon.user", DEFUID,
+ &pp->daemon_user));
+ CHK(capdb_getaltstr(bp, "ff", "job.formfeed", DEFFF, &pp->form_feed));
+ CHK(capdb_getaltstr(bp, "lf", "spool.log", _PATH_CONSOLE,
+ &pp->log_file));
+ CHK(capdb_getaltstr(bp, "lo", "spool.lock", DEFLOCK, &pp->lock_file));
+ CHK(capdb_getaltstr(bp, "lp", "tty.device", _PATH_DEFDEVLP, &pp->lp));
+ CHK(capdb_getaltnum(bp, "mc", "max.copies", DEFMAXCOPIES,
+ &pp->max_copies));
+ CHK(capdb_getaltstr(bp, "ms", "tty.mode", 0, &pp->mode_set));
+ CHK(capdb_getaltnum(bp, "mx", "max.blocks", DEFMX, &pp->max_blocks));
+ CHK(capdb_getaltnum(bp, "pc", "acct.price", 0, &pp->price100));
+ CHK(capdb_getaltnum(bp, "pl", "page.length", DEFLENGTH,
+ &pp->page_length));
+ CHK(capdb_getaltnum(bp, "pw", "page.width", DEFWIDTH,
+ &pp->page_width));
+ CHK(capdb_getaltnum(bp, "px", "page.pwidth", 0, &pp->page_pwidth));
+ CHK(capdb_getaltnum(bp, "py", "page.plength", 0, &pp->page_plength));
+ CHK(capdb_getaltstr(bp, "rg", "daemon.restrictgrp", 0,
+ &pp->restrict_grp));
+ CHK(capdb_getaltstr(bp, "rm", "remote.host", 0, &pp->remote_host));
+ CHK(capdb_getaltstr(bp, "rp", "remote.queue", DEFLP,
+ &pp->remote_queue));
+ CHK(capdb_getaltstr(bp, "sd", "spool.dir", _PATH_DEFSPOOL,
+ &pp->spool_dir));
+ CHK(capdb_getaltstr(bp, "sr", "stat.recv", 0, &pp->stat_recv));
+ CHK(capdb_getaltstr(bp, "ss", "stat.send", 0, &pp->stat_send));
+ CHK(capdb_getaltstr(bp, "st", "spool.status", DEFSTAT,
+ &pp->status_file));
+ CHK(capdb_getaltstr(bp, "tr", "job.trailer", 0, &pp->trailer));
+
+ pp->resend_copies = capdb_getaltlog(bp, "rc", "remote.resend_copies");
+ pp->restricted = capdb_getaltlog(bp, "rs", "daemon.restricted");
+ pp->short_banner = capdb_getaltlog(bp, "sb", "banner.short");
+ pp->no_copies = capdb_getaltlog(bp, "sc", "job.no_copies");
+ pp->no_formfeed = capdb_getaltlog(bp, "sf", "job.no_formfeed");
+ pp->no_header = capdb_getaltlog(bp, "sh", "banner.disable");
+ pp->header_last = capdb_getaltlog(bp, "hl", "banner.last");
+ pp->rw = capdb_getaltlog(bp, "rw", "tty.rw");
+ pp->tof = !capdb_getaltlog(bp, "fo", "job.topofform");
+
+ /*
+ * Decide if the remote printer name matches the local printer name.
+ * If no name is given then we assume they mean them to match.
+ * If a name is given see if the rp_name is one of the names for
+ * this printer.
+ */
+ pp->rp_matches_local = 1;
+ CHK((error = capdb_getaltstr(bp, "rp", "remote.queue", 0, &rp_name)));
+ if (error != PCAPERR_NOTFOUND && rp_name != NULL) {
+ if (cgetmatch(bp,rp_name) != 0)
+ pp->rp_matches_local = 0;
+ free(rp_name);
+ }
+
+ /*
+ * Filters:
+ */
+ for (filt = 0; filt < LPF_COUNT; filt++) {
+ CHK(capdb_getaltstr(bp, filters[filt], longfilters[filt], 0,
+ &pp->filters[filt]));
+ }
+
+ return 0;
+}
+
+/*
+ * Decode the error codes returned by cgetent() using the names we
+ * made up for them from "lp.h".
+ * This would have been much better done with Common Error, >sigh<.
+ * Perhaps this can be fixed in the next incarnation of cget*.
+ */
+const char *
+pcaperr(int error)
+{
+ switch(error) {
+ case PCAPERR_TCOPEN:
+ return "unresolved tc= expansion";
+ case PCAPERR_SUCCESS:
+ return "no error";
+ case PCAPERR_NOTFOUND:
+ return "printer not found";
+ case PCAPERR_OSERR:
+ return strerror(errno);
+ case PCAPERR_TCLOOP:
+ return "loop detected in tc= expansion";
+ default:
+ return "unknown printcap error";
+ }
+}
+
+/*
+ * Initialize a `struct printer' to contain values harmless to
+ * the other routines in liblpr.
+ */
+void
+init_printer(struct printer *pp)
+{
+ static struct printer zero;
+ *pp = zero;
+}
+
+/*
+ * Free the dynamically-allocated strings in a `struct printer'.
+ * Idempotent.
+ */
+void
+free_printer(struct printer *pp)
+{
+ enum lpd_filters filt;
+#define cfree(x) do { if (x) free(x); } while(0)
+ cfree(pp->printer);
+ cfree(pp->acct_file);
+ for (filt = 0; filt < LPF_COUNT; filt++)
+ cfree(pp->filters[filt]);
+ cfree(pp->form_feed);
+ cfree(pp->log_file);
+ cfree(pp->lock_file);
+ cfree(pp->lp);
+ cfree(pp->restrict_grp);
+ cfree(pp->remote_host);
+ cfree(pp->remote_queue);
+ cfree(pp->spool_dir);
+ cfree(pp->stat_recv);
+ cfree(pp->stat_send);
+ cfree(pp->status_file);
+ cfree(pp->trailer);
+ cfree(pp->mode_set);
+
+ init_printer(pp);
+}
+
+
+/*
+ * The following routines are part of what would be a sensible library
+ * interface to capability databases. Maybe someday this will become
+ * the default.
+ */
+
+/*
+ * It provides similar functionality to cgetstr(),
+ * except that it provides for both a long and a short
+ * capability name and allows for a default to be specified.
+ */
+static int
+capdb_getaltstr(char *bp, const char *shrt, const char *lng,
+ const char *dflt, char **result)
+{
+ int status;
+
+ status = cgetstr(bp, (char *)/*XXX*/lng, result);
+ if (status >= 0 || status == PCAPERR_OSERR)
+ return status;
+ status = cgetstr(bp, (char *)/*XXX*/shrt, result);
+ if (status >= 0 || status == PCAPERR_OSERR)
+ return status;
+ if (dflt) {
+ *result = strdup(dflt);
+ if (*result == 0)
+ return PCAPERR_OSERR;
+ return strlen(*result);
+ }
+ return PCAPERR_NOTFOUND;
+}
+
+/*
+ * The same, only for integers.
+ */
+static int
+capdb_getaltnum(char *bp, const char *shrt, const char *lng, long dflt,
+ long *result)
+{
+ int status;
+
+ status = cgetnum(bp, (char *)/*XXX*/lng, result);
+ if (status >= 0)
+ return status;
+ status = cgetnum(bp, (char *)/*XXX*/shrt, result);
+ if (status >= 0)
+ return status;
+ *result = dflt;
+ return 0;
+}
+
+/*
+ * Likewise for logical values. There's no need for a default parameter
+ * because the default is always false.
+ */
+static int
+capdb_getaltlog(char *bp, const char *shrt, const char *lng)
+{
+ if (cgetcap(bp, (char *)/*XXX*/lng, ':'))
+ return 1;
+ if (cgetcap(bp, (char *)/*XXX*/shrt, ':'))
+ return 1;
+ return 0;
+}
+
+/*
+ * Also should be a part of a better cget* library.
+ * Given a capdb entry, attempt to figure out what its canonical name
+ * is, and return a malloced copy of it. The canonical name is
+ * considered to be the first one listed.
+ */
+static char *
+capdb_canonical_name(const char *bp)
+{
+ char *retval;
+ const char *nameend;
+
+ nameend = strpbrk(bp, "|:");
+ if (nameend == 0)
+ nameend = bp + 1;
+ if ((retval = malloc(nameend - bp + 1)) != 0) {
+ retval[0] = '\0';
+ strncat(retval, bp, nameend - bp);
+ }
+ return retval;
+}
+
+
diff --git a/usr.sbin/lpr/common_source/request.c b/usr.sbin/lpr/common_source/request.c
new file mode 100644
index 0000000..79ea90b
--- /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[] =
+ "$FreeBSD$";
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <sys/param.h> /* needed for lp.h but not used here */
+#include <dirent.h> /* ditto */
+#include <stdio.h> /* ditto */
+#include "lp.h"
+#include "lp.local.h"
+
+void
+init_request(struct request *rp)
+{
+ static struct request zero;
+
+ *rp = zero;
+ TAILQ_INIT(&rp->users);
+ TAILQ_INIT(&rp->jobids);
+}
+
+void
+free_request(struct request *rp)
+{
+ struct req_user *ru;
+ struct req_jobid *rj;
+
+ if (rp->logname)
+ free(rp->logname);
+ if (rp->authname)
+ free(rp->authname);
+ if (rp->prettyname)
+ free(rp->prettyname);
+ if (rp->authinfo)
+ free(rp->authinfo);
+ while ((ru = TAILQ_FIRST(&rp->users)) != 0) {
+ TAILQ_REMOVE(&rp->users, ru, ru_link);
+ free(ru);
+ }
+ while ((rj = TAILQ_FIRST(&rp->jobids)) != 0) {
+ TAILQ_REMOVE(&rp->jobids, rj, rj_link);
+ free(rj);
+ }
+ init_request(rp);
+}
diff --git a/usr.sbin/lpr/common_source/rmjob.c b/usr.sbin/lpr/common_source/rmjob.c
new file mode 100644
index 0000000..3a60efb
--- /dev/null
+++ b/usr.sbin/lpr/common_source/rmjob.c
@@ -0,0 +1,395 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#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[7+MAXHOSTNAMELEN]; /* active control file name */
+
+extern uid_t uid, euid; /* real and effective user id's */
+
+static void alarmhandler(int _signo);
+static void do_unlink(char *_file);
+
+void
+rmjob(const char *printer)
+{
+ register int i, nitems;
+ int assasinated = 0;
+ struct dirent **files;
+ char *cp;
+ struct printer myprinter, *pp = &myprinter;
+
+ init_printer(pp);
+ if ((i = getprintcap(printer, pp)) < 0)
+ fatal(pp, "getprintcap: %s", pcaperr(i));
+ if ((cp = checkremote(pp))) {
+ printf("Warning: %s\n", cp);
+ free(cp);
+ }
+
+ /*
+ * If the format was `lprm -' and the user isn't the super-user,
+ * then fake things to look like he said `lprm user'.
+ */
+ if (users < 0) {
+ if (getuid() == 0)
+ all = 1; /* all files in local queue */
+ else {
+ user[0] = person;
+ users = 1;
+ }
+ }
+ if (!strcmp(person, "-all")) {
+ if (from_host == local_host)
+ fatal(pp, "The login name \"-all\" is reserved");
+ all = 1; /* all those from 'from_host' */
+ person = root;
+ }
+
+ seteuid(euid);
+ if (chdir(pp->spool_dir) < 0)
+ fatal(pp, "cannot chdir to spool directory");
+ if ((nitems = scandir(".", &files, iscf, NULL)) < 0)
+ fatal(pp, "cannot access spool directory");
+ seteuid(uid);
+
+ if (nitems) {
+ /*
+ * Check for an active printer daemon (in which case we
+ * kill it if it is reading our file) then remove stuff
+ * (after which we have to restart the daemon).
+ */
+ if (lockchk(pp, pp->lock_file) && chk(current)) {
+ seteuid(euid);
+ assasinated = kill(cur_daemon, SIGINT) == 0;
+ seteuid(uid);
+ if (!assasinated)
+ fatal(pp, "cannot kill printer daemon");
+ }
+ /*
+ * process the files
+ */
+ for (i = 0; i < nitems; i++)
+ process(pp, files[i]->d_name);
+ }
+ rmremote(pp);
+ /*
+ * Restart the printer daemon if it was killed
+ */
+ if (assasinated && !startdaemon(pp))
+ fatal(pp, "cannot restart printer daemon\n");
+ exit(0);
+}
+
+/*
+ * Process a lock file: collect the pid of the active
+ * daemon and the file name of the active spool entry.
+ * Return boolean indicating existence of a lock file.
+ */
+int
+lockchk(struct printer *pp, char *slockf)
+{
+ register FILE *fp;
+ register int i, n;
+
+ seteuid(euid);
+ if ((fp = fopen(slockf, "r")) == NULL) {
+ if (errno == EACCES)
+ fatal(pp, "%s: %s", slockf, strerror(errno));
+ else
+ return(0);
+ }
+ seteuid(uid);
+ if (!getline(fp)) {
+ (void) fclose(fp);
+ return(0); /* no daemon present */
+ }
+ cur_daemon = atoi(line);
+ if (kill(cur_daemon, 0) < 0 && errno != EPERM) {
+ (void) fclose(fp);
+ return(0); /* no daemon present */
+ }
+ for (i = 1; (n = fread(current, sizeof(char), sizeof(current), fp)) <= 0; i++) {
+ if (i > 5) {
+ n = 1;
+ break;
+ }
+ sleep(i);
+ }
+ current[n-1] = '\0';
+ (void) fclose(fp);
+ return(1);
+}
+
+/*
+ * Process a control file.
+ */
+void
+process(const struct printer *pp, char *file)
+{
+ FILE *cfp;
+
+ if (!chk(file))
+ return;
+ seteuid(euid);
+ if ((cfp = fopen(file, "r")) == NULL)
+ fatal(pp, "cannot open %s", file);
+ seteuid(uid);
+ while (getline(cfp)) {
+ switch (line[0]) {
+ case 'U': /* unlink associated files */
+ if (strchr(line+1, '/') || strncmp(line+1, "df", 2))
+ break;
+ do_unlink(line+1);
+ }
+ }
+ (void) fclose(cfp);
+ do_unlink(file);
+}
+
+static void
+do_unlink(char *file)
+{
+ int ret;
+
+ if (from_host != local_host)
+ printf("%s: ", local_host);
+ seteuid(euid);
+ ret = unlink(file);
+ seteuid(uid);
+ printf(ret ? "cannot dequeue %s\n" : "%s dequeued\n", file);
+}
+
+/*
+ * Do the dirty work in checking
+ */
+int
+chk(char *file)
+{
+ register int *r, n;
+ register char **u, *cp;
+ FILE *cfp;
+
+ /*
+ * Check for valid cf file name (mostly checking current).
+ */
+ if (strlen(file) < 7 || file[0] != 'c' || file[1] != 'f')
+ return(0);
+
+ if (all && (from_host == local_host || !strcmp(from_host, file+6)))
+ return(1);
+
+ /*
+ * get the owner's name from the control file.
+ */
+ seteuid(euid);
+ if ((cfp = fopen(file, "r")) == NULL)
+ return(0);
+ seteuid(uid);
+ while (getline(cfp)) {
+ if (line[0] == 'P')
+ break;
+ }
+ (void) fclose(cfp);
+ if (line[0] != 'P')
+ return(0);
+
+ if (users == 0 && requests == 0)
+ return(!strcmp(file, current) && isowner(line+1, file));
+ /*
+ * Check the request list
+ */
+ for (n = 0, cp = file+3; isdigit(*cp); )
+ n = n * 10 + (*cp++ - '0');
+ for (r = requ; r < &requ[requests]; r++)
+ if (*r == n && isowner(line+1, file))
+ return(1);
+ /*
+ * Check to see if it's in the user list
+ */
+ for (u = user; u < &user[users]; u++)
+ if (!strcmp(*u, line+1) && isowner(line+1, file))
+ return(1);
+ return(0);
+}
+
+/*
+ * If root is removing a file on the local machine, allow it.
+ * If root is removing a file from a remote machine, only allow
+ * files sent from the remote machine to be removed.
+ * Normal users can only remove the file from where it was sent.
+ */
+int
+isowner(char *owner, char *file)
+{
+ if (!strcmp(person, root) && (from_host == local_host ||
+ !strcmp(from_host, file+6)))
+ return (1);
+ if (!strcmp(person, owner) && !strcmp(from_host, file+6))
+ return (1);
+ if (from_host != local_host)
+ printf("%s: ", local_host);
+ printf("%s: Permission denied\n", file);
+ return(0);
+}
+
+/*
+ * Check to see if we are sending files to a remote machine. If we are,
+ * then try removing files on the remote machine.
+ */
+void
+rmremote(const struct printer *pp)
+{
+ int i, elem, firstreq, niov, rem, totlen;
+ char buf[BUFSIZ];
+ void (*savealrm)(int);
+ struct iovec *iov;
+
+ if (!pp->remote)
+ return; /* not sending to a remote machine */
+
+ /*
+ * Flush stdout so the user can see what has been deleted
+ * while we wait (possibly) for the connection.
+ */
+ fflush(stdout);
+
+ /*
+ * Counting:
+ * 4 == "\5" + remote_queue + " " + person
+ * 2 * users == " " + user[i] for each user
+ * requests == asprintf results for each request
+ * 1 == "\n"
+ * Although laborious, doing it this way makes it possible for
+ * us to process requests of indeterminate length without
+ * applying an arbitrary limit. Arbitrary Limits Are Bad (tm).
+ */
+ if (users > 0)
+ niov = 4 + 2 * users + requests + 1;
+ else
+ niov = 4 + requests + 1;
+ iov = malloc(niov * sizeof *iov);
+ if (iov == 0)
+ fatal(pp, "out of memory in rmremote()");
+ iov[0].iov_base = "\5";
+ iov[1].iov_base = pp->remote_queue;
+ iov[2].iov_base = " ";
+ iov[3].iov_base = all ? "-all" : person;
+ elem = 4;
+ for (i = 0; i < users; i++) {
+ iov[elem].iov_base = " ";
+ iov[elem + 1].iov_base = user[i];
+ elem += 2;
+ }
+ firstreq = elem;
+ for (i = 0; i < requests; i++) {
+ asprintf(&iov[elem].iov_base, " %d", requ[i]);
+ if (iov[elem].iov_base == 0)
+ fatal(pp, "out of memory in rmremote()");
+ elem++;
+ }
+ iov[elem++].iov_base = "\n";
+ for (totlen = i = 0; i < niov; i++)
+ totlen += (iov[i].iov_len = strlen(iov[i].iov_base));
+
+ savealrm = signal(SIGALRM, alarmhandler);
+ alarm(pp->conn_timeout);
+ rem = getport(pp, pp->remote_host, 0);
+ (void)signal(SIGALRM, savealrm);
+ if (rem < 0) {
+ if (from_host != local_host)
+ printf("%s: ", local_host);
+ printf("connection to %s is down\n", pp->remote_host);
+ } else {
+ if (writev(rem, iov, niov) != totlen)
+ fatal(pp, "Lost connection");
+ while ((i = read(rem, buf, sizeof(buf))) > 0)
+ (void) fwrite(buf, 1, i, stdout);
+ (void) close(rem);
+ }
+ for (i = 0; i < requests; i++)
+ free(iov[firstreq + i].iov_base);
+ free(iov);
+}
+
+/*
+ * Return 1 if the filename begins with 'cf'
+ */
+int
+iscf(struct dirent *d)
+{
+ return(d->d_name[0] == 'c' && d->d_name[1] == 'f');
+}
+
+void
+alarmhandler(int signo __unused)
+{
+ /* the signal is ignored */
+ /* (the '__unused' is just to avoid a compile-time warning) */
+}
diff --git a/usr.sbin/lpr/common_source/startdaemon.c b/usr.sbin/lpr/common_source/startdaemon.c
new file mode 100644
index 0000000..16ec7ce
--- /dev/null
+++ b/usr.sbin/lpr/common_source/startdaemon.c
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 1983, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)startdaemon.c 8.2 (Berkeley) 4/17/94";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#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(const struct printer *pp)
+{
+ struct sockaddr_un un;
+ register int s, n;
+ int connectres;
+ char c;
+
+ s = socket(PF_LOCAL, SOCK_STREAM, 0);
+ if (s < 0) {
+ warn("socket");
+ return(0);
+ }
+ memset(&un, 0, sizeof(un));
+ un.sun_family = AF_LOCAL;
+ strcpy(un.sun_path, _PATH_SOCKETNAME);
+#ifndef SUN_LEN
+#define SUN_LEN(unp) (strlen((unp)->sun_path) + 2)
+#endif
+ seteuid(euid);
+ connectres = connect(s, (struct sockaddr *)&un, SUN_LEN(&un));
+ seteuid(uid);
+ if (connectres < 0) {
+ warn("Unable to connect to %s", _PATH_SOCKETNAME);
+ warnx("Check to see if the master 'lpd' process is running.");
+ (void) close(s);
+ return(0);
+ }
+
+ /*
+ * Avoid overruns without putting artificial limitations on
+ * the length.
+ */
+ if (writel(s, "\1", pp->printer, "\n", (char *)0) <= 0) {
+ warn("write");
+ (void) close(s);
+ return(0);
+ }
+ if (read(s, &c, 1) == 1) {
+ if (c == '\0') { /* everything is OK */
+ (void) close(s);
+ return(1);
+ }
+ putchar(c);
+ }
+ while ((n = read(s, &c, 1)) > 0)
+ putchar(c);
+ (void) close(s);
+ return(0);
+}
diff --git a/usr.sbin/lpr/filters.ru/Makefile b/usr.sbin/lpr/filters.ru/Makefile
new file mode 100644
index 0000000..ee15541
--- /dev/null
+++ b/usr.sbin/lpr/filters.ru/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+SUBDIR= koi2alt koi2855
+
+FILES= bjc-240.sh.sample
+
+.include "Makefile.inc"
+.include <bsd.prog.mk>
diff --git a/usr.sbin/lpr/filters.ru/Makefile.inc b/usr.sbin/lpr/filters.ru/Makefile.inc
new file mode 100644
index 0000000..a312e73
--- /dev/null
+++ b/usr.sbin/lpr/filters.ru/Makefile.inc
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+BINDIR= /usr/libexec/lpr/ru
diff --git a/usr.sbin/lpr/filters.ru/bjc-240.sh.sample b/usr.sbin/lpr/filters.ru/bjc-240.sh.sample
new file mode 100644
index 0000000..1bc907a
--- /dev/null
+++ b/usr.sbin/lpr/filters.ru/bjc-240.sh.sample
@@ -0,0 +1,64 @@
+#!/bin/sh
+#
+# Canon BJC-240 Setup script
+#
+# Settings are:
+# Epson LQ emulation, A4, Code Page 866, 66 lines, Roman font, Smoothing,
+# HQ mode, no CR translation, power off in 10min, auto power on
+#
+
+printf "\033[K\00200\037" | tr 0 "\0"
+
+cat << EOF1
+BJLSTART
+ControlMode=BJ
+Font=Roman
+PageLength=12
+CodePage=866
+AutoLF=Off
+TextScaleMode=On
+AutoCR=Off
+CharacterSet=Set2
+AGM=Off
+BJLEND
+EOF1
+
+printf "\033[K\00200\037" | tr 0 "\0"
+
+cat << EOF2
+BJLSTART
+ControlMode=LQ
+Font=Roman
+PageLength=12
+CodePage=866
+AutoLF=Off
+TextScaleMode=On
+CharacterSet=Graphics
+International=USA
+BJLEND
+EOF2
+
+printf "\033[K\00200\037" | tr 0 "\0"
+
+cat << EOF3
+BJLSTART
+@SetControlMode=LQ
+BJLEND
+EOF3
+
+printf "\033[K\00200\037" | tr 0 "\0"
+
+cat << EOF4
+BJLSTART
+ControlMode=Common
+PrintMode=HQ
+Reduction=Off
+Smoothing=On
+PaperSelect=A4
+I/D-Buffer=Input
+AutoPowerOff=10
+AutoPowerOn=Enable
+BJLEND
+EOF4
+
+exec /usr/libexec/lpr/ru/koi2alt $*
diff --git a/usr.sbin/lpr/filters.ru/koi2855/Makefile b/usr.sbin/lpr/filters.ru/koi2855/Makefile
new file mode 100644
index 0000000..9bc28cd
--- /dev/null
+++ b/usr.sbin/lpr/filters.ru/koi2855/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+PROG= koi2855
+NOMAN= #true
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/lpr/filters.ru/koi2855/koi2855.c b/usr.sbin/lpr/filters.ru/koi2855/koi2855.c
new file mode 100644
index 0000000..67a9bc5
--- /dev/null
+++ b/usr.sbin/lpr/filters.ru/koi2855/koi2855.c
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 1999 by Andrey A. Chernov, Moscow, Russia.
+ * (C) 18 Sep 1999, Alex G. Bulushev (bag@demos.su)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * KOI8-R -> CP855 conversion filter (Russian character sets)
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <signal.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int length = 66;
+int lines;
+
+unsigned char koi2855 [] = {
+0xc4, 0xb3, 0xda, 0xbf, 0xc0, 0xd9, 0xc3, 0xb4,
+0xc2, 0xc1, 0xc5, 0xdf, 0xdc, 0xdb, 0xdb, 0xdb,
+0xb0, 0xb1, 0xb2, 0xda, 0xfe, 0x2e, 0xad, 0x3d,
+0xae, 0xaf, 0xff, 0xd9, 0xcf, 0x32, 0x2e, 0x25,
+0xcd, 0xba, 0xc9, 0x84, 0xc9, 0xc9, 0xbb, 0xbb,
+0xbb, 0xc8, 0xc8, 0xc8, 0xbc, 0xbc, 0xbc, 0xcc,
+0xcc, 0xcc, 0xb9, 0x85, 0xb9, 0xb9, 0xcb, 0xcb,
+0xcb, 0xca, 0xca, 0xca, 0xce, 0xce, 0xce, 0x43,
+0x9c, 0xa0, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xac,
+0xb5, 0xb7, 0xbd, 0xc6, 0xd0, 0xd2, 0xd4, 0xd6,
+0xd8, 0xde, 0xe1, 0xe3, 0xe5, 0xe7, 0xe9, 0xeb,
+0xed, 0xf1, 0xf3, 0xf5, 0xf7, 0xf9, 0xfb, 0x9e,
+0x9d, 0xa1, 0xa3, 0xa5, 0xa7, 0xa9, 0xab, 0xad,
+0xb6, 0xb8, 0xbe, 0xc7, 0xd1, 0xd3, 0xd5, 0xd7,
+0xdd, 0xe0, 0xe2, 0xe4, 0xe6, 0xe8, 0xea, 0xec,
+0xee, 0xf2, 0xf4, 0xf6, 0xf8, 0xfa, 0xfc, 0x9f
+};
+
+int main(int argc, char *argv[])
+{
+ int c, i;
+ char *cp;
+
+ while (--argc) {
+ if (*(cp = *++argv) == '-') {
+ switch (*++cp) {
+ case 'l':
+ if ((i = atoi(++cp)) > 0)
+ length = i;
+ break;
+ }
+ }
+ }
+
+ while ((c = getchar()) != EOF) {
+ if (c == '\031') {
+ if ((c = getchar()) == '\1') {
+ lines = 0;
+ fflush(stdout);
+ kill(getpid(), SIGSTOP);
+ continue;
+ } else {
+ ungetc(c, stdin);
+ c = '\031';
+ }
+ } else if (c & 0x80) {
+ putchar(koi2855[c & 0x7F]);
+ continue;
+ } else if (c == '\n')
+ lines++;
+ else if (c == '\f')
+ lines = length;
+ putchar(c);
+ if (lines >= length) {
+ lines = 0;
+ fflush(stdout);
+ }
+ }
+ return 0;
+}
diff --git a/usr.sbin/lpr/filters.ru/koi2alt/Makefile b/usr.sbin/lpr/filters.ru/koi2alt/Makefile
new file mode 100644
index 0000000..3af814f
--- /dev/null
+++ b/usr.sbin/lpr/filters.ru/koi2alt/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+PROG= koi2alt
+NOMAN= #true
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/lpr/filters.ru/koi2alt/koi2alt.c b/usr.sbin/lpr/filters.ru/koi2alt/koi2alt.c
new file mode 100644
index 0000000..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..c8bdca7
--- /dev/null
+++ b/usr.sbin/lpr/filters/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+BINDIR= /usr/libexec/lpr
+
+PROG= lpf
+NOMAN= noman
+
+.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..7c759bb
--- /dev/null
+++ b/usr.sbin/lpr/filters/lpf.c
@@ -0,0 +1,220 @@
+/*
+ * 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[] = "From: @(#)lpf.c 8.1 (Berkeley) 6/6/93";
+*/
+static const char rcsid[] =
+ "$FreeBSD$";
+#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(int argc, char *argv[])
+{
+ register FILE *p = stdin, *o = stdout;
+ register int i, col;
+ register char *cp;
+ int done, linedone, maxrep;
+ char *limit;
+ int ch;
+
+ while (--argc) {
+ if (*(cp = *++argv) == '-') {
+ switch (cp[1]) {
+ case 'n':
+ argc--;
+ name = *++argv;
+ break;
+
+ case 'h':
+ argc--;
+ host = *++argv;
+ break;
+
+ case 'w':
+ if ((i = atoi(&cp[2])) > 0 && i <= MAXWIDTH)
+ width = i;
+ break;
+
+ case 'l':
+ length = atoi(&cp[2]);
+ break;
+
+ case 'i':
+ indent = atoi(&cp[2]);
+ break;
+
+ case 'c': /* Print control chars */
+ literal++;
+ break;
+ }
+ } else
+ acctfile = cp;
+ }
+
+ for (cp = buf[0], limit = buf[MAXREP]; cp < limit; *cp++ = ' ');
+ done = 0;
+
+ while (!done) {
+ col = indent;
+ maxrep = -1;
+ linedone = 0;
+ while (!linedone) {
+ switch (ch = getc(p)) {
+ case EOF:
+ linedone = done = 1;
+ ch = '\n';
+ break;
+
+ case '\f':
+ lineno = length;
+ case '\n':
+ if (maxrep < 0)
+ maxrep = 0;
+ linedone = 1;
+ break;
+
+ case '\b':
+ if (--col < indent)
+ col = indent;
+ break;
+
+ case '\r':
+ col = indent;
+ break;
+
+ case '\t':
+ col = ((col - indent) | 07) + indent + 1;
+ break;
+
+ case '\031':
+ /*
+ * lpd needs to use a different filter to
+ * print data so stop what we are doing and
+ * wait for lpd to restart us.
+ */
+ if ((ch = getchar()) == '\1') {
+ fflush(stdout);
+ kill(getpid(), SIGSTOP);
+ break;
+ } else {
+ ungetc(ch, stdin);
+ ch = '\031';
+ }
+
+ default:
+ if (col >= width || (!literal && ch < ' ')) {
+ col++;
+ break;
+ }
+ cp = &buf[0][col];
+ for (i = 0; i < MAXREP; i++) {
+ if (i > maxrep)
+ maxrep = i;
+ if (*cp == ' ') {
+ *cp = ch;
+ if (col > maxcol[i])
+ maxcol[i] = col;
+ break;
+ }
+ cp += MAXWIDTH;
+ }
+ col++;
+ break;
+ }
+ }
+
+ /* print out lines */
+ for (i = 0; i <= maxrep; i++) {
+ for (cp = buf[i], limit = cp+maxcol[i]; cp <= limit;) {
+ putc(*cp, o);
+ *cp++ = ' ';
+ }
+ if (i < maxrep)
+ putc('\r', o);
+ else
+ putc(ch, o);
+ if (++lineno >= length) {
+ fflush(o);
+ npages++;
+ lineno = 0;
+ }
+ maxcol[i] = -1;
+ }
+ }
+ if (lineno) { /* be sure to end on a page boundary */
+ putchar('\f');
+ npages++;
+ }
+ if (name && acctfile && access(acctfile, 02) >= 0 &&
+ freopen(acctfile, "a", stdout) != NULL) {
+ printf("%7.2f\t%s:%s\n", (float)npages, host, name);
+ }
+ exit(0);
+}
diff --git a/usr.sbin/lpr/lp/Makefile b/usr.sbin/lpr/lp/Makefile
new file mode 100644
index 0000000..2fcec43
--- /dev/null
+++ b/usr.sbin/lpr/lp/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+BINDIR= /usr/bin
+
+SCRIPTS=lp.sh
+MAN= lp.1
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/lpr/lp/lp.1 b/usr.sbin/lpr/lp/lp.1
new file mode 100644
index 0000000..0269971
--- /dev/null
+++ b/usr.sbin/lpr/lp/lp.1
@@ -0,0 +1,117 @@
+.\"
+.\" Copyright (c) 1995 Joerg Wunsch
+.\"
+.\" All rights reserved.
+.\"
+.\" This program is free software.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Joerg Wunsch
+.\" 4. The name of the developer may not be used to endorse or promote
+.\" products derived from this software without specific prior written
+.\" permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd January 22, 1995
+.Dt LP 1
+.Os
+.Sh NAME
+.Nm lp
+.Nd front-end to the print spooler
+.Sh SYNOPSIS
+.Nm
+.Op Fl cs
+.Op Fl o Ar option
+.Op Fl d Ar printer
+.Op Fl n Ar num
+.Op Ar name ...
+.Sh DESCRIPTION
+The
+.Nm
+utility is a front-end to the print spooler as required by the
+.St -p1003.2
+specification. It effectively invokes
+.Xr lpr 1
+with the proper set of arguments.
+.Pp
+It generally prints the named files on the destination printer.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl c
+Make the
+.Nm
+command exit only after further access to any of the input files is no
+longer required. The application can then safely delete or modify the
+files without affecting the output operation.
+.It Fl d Ar dest
+Specify a particular printer.
+If no
+.Fl d
+is provided on the command line, the contents of the environment
+variables
+.Ev LPDEST
+or
+.Ev PRINTER
+(with this precedence)
+are taken as the destination printer.
+.It Fl n Ar num
+Specify that
+.Ar num
+copies of each of the named files shall be printed.
+.It Fl o Ar option
+Printer specific options.
+Not supported, provided only as a compatibility
+option for SVR.
+.It Fl s
+Silent operation.
+Not supported,
+provided only as a compatibility option for
+.St -susv2 .
+.El
+.Sh ENVIRONMENT
+As described above, the variables
+.Ev LPDEST
+and
+.Ev PRINTER
+are examined to select the destination printer.
+.Sh SEE ALSO
+.Xr lpr 1
+.Sh STANDARDS
+The
+.Nm
+command is expected to comply with the
+.St -p1003.2
+specification.
+.Sh AUTHORS
+This implementation of the
+.Nm
+command has been written by
+.An J\(:org Wunsch .
+.Sh BUGS
+The
+.St -p1003.2
+specification does not provide any means to print non-text files. It
+rather requires the files to be printed to be text files limited to
+reasonable line lengths and printable characters.
diff --git a/usr.sbin/lpr/lp/lp.sh b/usr.sbin/lpr/lp/lp.sh
new file mode 100644
index 0000000..e7c0688
--- /dev/null
+++ b/usr.sbin/lpr/lp/lp.sh
@@ -0,0 +1,75 @@
+#!/bin/sh
+#
+#
+# Copyright (c) 1995 Joerg Wunsch
+#
+# All rights reserved.
+#
+# This program is free software.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by Joerg Wunsch
+# 4. The name of the developer may not be used to endorse or promote
+# products derived from this software without specific prior written
+# permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+#
+# Posix 1003.2 compliant print spooler interface.
+#
+# $FreeBSD$
+#
+
+ncopies=""
+symlink="-s"
+
+# Posix says LPDEST gets precedence over PRINTER
+dest=${LPDEST:-${PRINTER:-lp}}
+
+#
+# XXX We include the -o flag as a dummy. Posix 1003.2 does not require
+# it, but the rationale mentions it as a possible future extension.
+# XXX We include the -s flag as a dummy. SUSv2 requires it,
+# although we do not yet emit the affected messages.
+#
+while getopts "cd:n:o:s" option
+do
+ case $option in
+
+ c) # copy files before printing
+ symlink="";;
+ d) # destination
+ dest="${OPTARG}";;
+ n) # number of copies
+ ncopies="-#${OPTARG}";;
+ o) # (printer option)
+ : ;;
+ s) # (silent option)
+ : ;;
+ *) # (error msg printed by getopts)
+ exit 2;;
+ esac
+done
+
+shift $(($OPTIND - 1))
+
+exec /usr/bin/lpr "-P${dest}" ${symlink} ${ncopies} "$@"
diff --git a/usr.sbin/lpr/lpc/Makefile b/usr.sbin/lpr/lpc/Makefile
new file mode 100644
index 0000000..7ce0f4c
--- /dev/null
+++ b/usr.sbin/lpr/lpc/Makefile
@@ -0,0 +1,17 @@
+# From: @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../common_source
+
+PROG= lpc
+MAN= lpc.8
+SRCS= lpc.c cmds.c cmdtab.c
+BINGRP= daemon
+BINMODE= 2555
+
+CFLAGS+= -I${.CURDIR}/../common_source
+
+DPADD= ${LIBLPR} ${LIBEDIT} ${LIBTERMCAP}
+LDADD= ${LIBLPR} -ledit -ltermcap
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/lpr/lpc/cmds.c b/usr.sbin/lpr/lpc/cmds.c
new file mode 100644
index 0000000..db98422
--- /dev/null
+++ b/usr.sbin/lpr/lpc/cmds.c
@@ -0,0 +1,1305 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#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"
+
+/*
+ * Return values from kill_qtask().
+ */
+#define KQT_LFERROR -2
+#define KQT_KILLFAIL -1
+#define KQT_NODAEMON 0
+#define KQT_KILLOK 1
+
+static char *args2line(int argc, char **argv);
+static int doarg(char *_job);
+static int doselect(struct dirent *_d);
+static int kill_qtask(const char *lf);
+static int sortq(const void *_a, const void *_b);
+static int touch(struct jobqueue *_jq);
+static void unlinkf(char *_name);
+static void upstat(struct printer *_pp, const char *_msg, int _notify);
+static void wrapup_clean(int _laststatus);
+
+/*
+ * generic framework for commands which operate on all or a specified
+ * set of printers
+ */
+enum qsel_val { /* how a given ptr was selected */
+ QSEL_UNKNOWN = -1, /* ... not selected yet */
+ QSEL_BYNAME = 0, /* ... user specifed it by name */
+ QSEL_ALL = 1 /* ... user wants "all" printers */
+ /* (with more to come) */
+};
+
+static enum qsel_val generic_qselect; /* indicates how ptr was selected */
+static int generic_initerr; /* result of initrtn processing */
+static char *generic_cmdname;
+static char *generic_msg; /* if a -msg was specified */
+static char *generic_nullarg;
+static void (*generic_wrapup)(int _last_status); /* perform rtn wrap-up */
+
+void
+generic(void (*specificrtn)(struct printer *_pp), int cmdopts,
+ void (*initrtn)(int _argc, char *_argv[]), int argc, char *argv[])
+{
+ int cmdstatus, more, targc;
+ struct printer myprinter, *pp;
+ char **margv, **targv;
+
+ if (argc == 1) {
+ /*
+ * Usage needs a special case for 'down': The user must
+ * either include `-msg', or only the first parameter
+ * that they give will be processed as a printer name.
+ */
+ printf("usage: %s {all | printer ...}", argv[0]);
+ if (strcmp(argv[0], "down") == 0) {
+ printf(" -msg [<text> ...]\n");
+ printf(" or: down {all | printer} [<text> ...]");
+ } else if (cmdopts & LPC_MSGOPT)
+ printf(" [-msg <text> ...]");
+ printf("\n");
+ return;
+ }
+
+ /* The first argument is the command name. */
+ generic_cmdname = *argv++;
+ argc--;
+
+ /*
+ * The initialization routine for a command might set a generic
+ * "wrapup" routine, which should be called after processing all
+ * the printers in the command. This might print summary info.
+ *
+ * Note that the initialization routine may also parse (and
+ * nullify) some of the parameters given on the command, leaving
+ * only the parameters which have to do with printer names.
+ */
+ pp = &myprinter;
+ generic_wrapup = NULL;
+ generic_qselect = QSEL_UNKNOWN;
+ cmdstatus = 0;
+ /* this just needs to be a distinct value of type 'char *' */
+ if (generic_nullarg == NULL)
+ generic_nullarg = strdup("");
+
+ /*
+ * Some commands accept a -msg argument, which indicates that
+ * all remaining arguments should be combined into a string.
+ */
+ generic_msg = NULL;
+ if (cmdopts & LPC_MSGOPT) {
+ targc = argc;
+ targv = argv;
+ for (; targc > 0; targc--, targv++) {
+ if (strcmp(*targv, "-msg") == 0) {
+ argc -= targc;
+ generic_msg = args2line(targc - 1, targv + 1);
+ break;
+ }
+ }
+ }
+
+ /* call initialization routine, if there is one for this cmd */
+ if (initrtn != NULL) {
+ generic_initerr = 0;
+ (*initrtn)(argc, argv);
+ if (generic_initerr)
+ return;
+ /*
+ * The initrtn may have null'ed out some of the parameters.
+ * Compact the parameter list to remove those nulls, and
+ * correct the arg-count.
+ */
+ targc = argc;
+ targv = argv;
+ margv = argv;
+ argc = 0;
+ for (; targc > 0; targc--, targv++) {
+ if (*targv != generic_nullarg) {
+ if (targv != margv)
+ *margv = *targv;
+ margv++;
+ argc++;
+ }
+ }
+ }
+
+ if (argc == 1 && strcmp(*argv, "all") == 0) {
+ generic_qselect = QSEL_ALL;
+ more = firstprinter(pp, &cmdstatus);
+ if (cmdstatus)
+ goto looperr;
+ while (more) {
+ (*specificrtn)(pp);
+ do {
+ more = nextprinter(pp, &cmdstatus);
+looperr:
+ switch (cmdstatus) {
+ case PCAPERR_TCOPEN:
+ printf("warning: %s: unresolved "
+ "tc= reference(s) ",
+ pp->printer);
+ case PCAPERR_SUCCESS:
+ break;
+ default:
+ fatal(pp, "%s", pcaperr(cmdstatus));
+ }
+ } while (more && cmdstatus);
+ }
+ goto wrapup;
+ }
+
+ generic_qselect = QSEL_BYNAME; /* specifically-named ptrs */
+ for (; argc > 0; argc--, argv++) {
+ init_printer(pp);
+ cmdstatus = getprintcap(*argv, pp);
+ switch (cmdstatus) {
+ default:
+ fatal(pp, "%s", pcaperr(cmdstatus));
+ case PCAPERR_NOTFOUND:
+ printf("unknown printer %s\n", *argv);
+ continue;
+ case PCAPERR_TCOPEN:
+ printf("warning: %s: unresolved tc= reference(s)\n",
+ *argv);
+ break;
+ case PCAPERR_SUCCESS:
+ break;
+ }
+ (*specificrtn)(pp);
+ }
+
+wrapup:
+ if (generic_wrapup) {
+ (*generic_wrapup)(cmdstatus);
+ }
+ if (generic_msg)
+ free(generic_msg);
+}
+
+/*
+ * Convert an argv-array of character strings into a single string.
+ */
+static char *
+args2line(int argc, char **argv)
+{
+ char *cp1, *cend;
+ const char *cp2;
+ char buf[1024];
+
+ if (argc <= 0)
+ return strdup("\n");
+
+ cp1 = buf;
+ cend = buf + sizeof(buf) - 1; /* save room for '\0' */
+ while (--argc >= 0) {
+ cp2 = *argv++;
+ while ((cp1 < cend) && (*cp1++ = *cp2++))
+ ;
+ cp1[-1] = ' ';
+ }
+ cp1[-1] = '\n';
+ *cp1 = '\0';
+ return strdup(buf);
+}
+
+/*
+ * Kill the current daemon, to stop printing of the active job.
+ */
+static int
+kill_qtask(const char *lf)
+{
+ FILE *fp;
+ pid_t pid;
+ int errsav, killres, lockres, res;
+
+ seteuid(euid);
+ fp = fopen(lf, "r");
+ errsav = errno;
+ seteuid(uid);
+ res = KQT_NODAEMON;
+ if (fp == NULL) {
+ /*
+ * If there is no lock file, then there is no daemon to
+ * kill. Any other error return means there is some
+ * kind of problem with the lock file.
+ */
+ if (errsav != ENOENT)
+ res = KQT_LFERROR;
+ goto killdone;
+ }
+
+ /* If the lock file is empty, then there is no daemon to kill */
+ if (getline(fp) == 0)
+ goto killdone;
+
+ /*
+ * If the file can be locked without blocking, then there
+ * no daemon to kill, or we should not try to kill it.
+ *
+ * XXX - not sure I understand the reasoning behind this...
+ */
+ lockres = flock(fileno(fp), LOCK_SH|LOCK_NB);
+ (void) fclose(fp);
+ if (lockres == 0)
+ goto killdone;
+
+ pid = atoi(line);
+ if (pid < 0) {
+ /*
+ * If we got a negative pid, then the contents of the
+ * lock file is not valid.
+ */
+ res = KQT_LFERROR;
+ goto killdone;
+ }
+
+ seteuid(uid);
+ killres = kill(pid, SIGTERM);
+ errsav = errno;
+ seteuid(uid);
+ if (killres == 0) {
+ res = KQT_KILLOK;
+ printf("\tdaemon (pid %d) killed\n", pid);
+ } else if (errno == ESRCH) {
+ res = KQT_NODAEMON;
+ } else {
+ res = KQT_KILLFAIL;
+ printf("\tWarning: daemon (pid %d) not killed:\n", pid);
+ printf("\t %s\n", strerror(errsav));
+ }
+
+killdone:
+ switch (res) {
+ case KQT_LFERROR:
+ printf("\tcannot open lock file: %s\n",
+ strerror(errsav));
+ break;
+ case KQT_NODAEMON:
+ printf("\tno daemon to abort\n");
+ break;
+ case KQT_KILLFAIL:
+ case KQT_KILLOK:
+ /* These two already printed messages to the user. */
+ break;
+ default:
+ printf("\t<internal error in kill_qtask>\n");
+ break;
+ }
+
+ return (res);
+}
+
+/*
+ * Write a message into the status file.
+ */
+static void
+upstat(struct printer *pp, const char *msg, int notifyuser)
+{
+ int fd;
+ char statfile[MAXPATHLEN];
+
+ status_file_name(pp, statfile, sizeof statfile);
+ umask(0);
+ seteuid(euid);
+ fd = open(statfile, O_WRONLY|O_CREAT|O_EXLOCK, STAT_FILE_MODE);
+ seteuid(uid);
+ if (fd < 0) {
+ printf("\tcannot create status file: %s\n", strerror(errno));
+ return;
+ }
+ (void) ftruncate(fd, 0);
+ if (msg == (char *)NULL)
+ (void) write(fd, "\n", 1);
+ else
+ (void) write(fd, msg, strlen(msg));
+ (void) close(fd);
+ if (notifyuser) {
+ if ((msg == (char *)NULL) || (strcmp(msg, "\n") == 0))
+ printf("\tstatus message is now set to nothing.\n");
+ else
+ printf("\tstatus message is now: %s", msg);
+ }
+}
+
+/*
+ * kill an existing daemon and disable printing.
+ */
+void
+abort_q(struct printer *pp)
+{
+ int killres, setres;
+ char lf[MAXPATHLEN];
+
+ lock_file_name(pp, lf, sizeof lf);
+ printf("%s:\n", pp->printer);
+
+ /*
+ * Turn on the owner execute bit of the lock file to disable printing.
+ */
+ setres = set_qstate(SQS_STOPP, lf);
+
+ /*
+ * If set_qstate found that there already was a lock file, then
+ * call a routine which will read that lock file and kill the
+ * lpd-process which is listed in that lock file. If the lock
+ * file did not exist, then either there is no daemon running
+ * for this queue, or there is one running but *it* could not
+ * write a lock file (which means we can not determine the
+ * process id of that lpd-process).
+ */
+ switch (setres) {
+ case SQS_CHGOK:
+ case SQS_CHGFAIL:
+ /* Kill the process */
+ killres = kill_qtask(lf);
+ break;
+ case SQS_CREOK:
+ case SQS_CREFAIL:
+ printf("\tno daemon to abort\n");
+ break;
+ case SQS_STATFAIL:
+ printf("\tassuming no daemon to abort\n");
+ break;
+ default:
+ printf("\t<unexpected result (%d) from set_qstate>\n",
+ setres);
+ break;
+ }
+
+ if (setres >= 0)
+ upstat(pp, "printing disabled\n", 0);
+}
+
+/*
+ * "global" variables for all the routines related to 'clean' and 'tclean'
+ */
+static time_t cln_now; /* current time */
+static double cln_minage; /* minimum age before file is removed */
+static long cln_sizecnt; /* amount of space freed up */
+static int cln_debug; /* print extra debugging msgs */
+static int cln_filecnt; /* number of files destroyed */
+static int cln_foundcore; /* found a core file! */
+static int cln_queuecnt; /* number of queues checked */
+static int cln_testonly; /* remove-files vs just-print-info */
+
+static int
+doselect(struct dirent *d)
+{
+ int c = d->d_name[0];
+
+ if ((c == 'c' || c == 'd' || c == 'r' || c == 't') &&
+ d->d_name[1] == 'f')
+ return 1;
+ if (c == 'c') {
+ if (!strcmp(d->d_name, "core"))
+ cln_foundcore = 1;
+ }
+ if (c == 'e') {
+ if (!strncmp(d->d_name, "errs.", 5))
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Comparison routine that clean_q() uses for scandir.
+ *
+ * The purpose of this sort is to have all `df' files end up immediately
+ * after the matching `cf' file. For files matching `cf', `df', `rf', or
+ * `tf', it sorts by job number and machine, then by `cf', `df', `rf', or
+ * `tf', and then by the sequence letter (which is A-Z, or a-z). This
+ * routine may also see filenames which do not start with `cf', `df', `rf',
+ * or `tf' (such as `errs.*'), and those are simply sorted by the full
+ * filename.
+ *
+ * XXX
+ * This assumes that all control files start with `cfA*', and it turns
+ * out there are a few implementations of lpr which will create `cfB*'
+ * filenames (they will have datafile names which start with `dfB*').
+ */
+static int
+sortq(const void *a, const void *b)
+{
+ const int a_lt_b = -1, a_gt_b = 1, cat_other = 10;
+ const char *fname_a, *fname_b, *jnum_a, *jnum_b;
+ int cat_a, cat_b, ch, res, seq_a, seq_b;
+
+ fname_a = (*(const struct dirent * const *)a)->d_name;
+ fname_b = (*(const struct dirent * const *)b)->d_name;
+
+ /*
+ * First separate filenames into cagatories. Catagories are
+ * legitimate `cf', `df', `rf' & `tf' filenames, and "other" - in
+ * that order. It is critical that the mapping be exactly the
+ * same for 'a' vs 'b', so define a macro for the job.
+ *
+ * [aside: the standard `cf' file has the jobnumber start in
+ * position 4, but some implementations have that as an extra
+ * file-sequence letter, and start the job number in position 5.]
+ */
+#define MAP_TO_CAT(fname_X,cat_X,jnum_X,seq_X) do { \
+ cat_X = cat_other; \
+ ch = *(fname_X + 2); \
+ jnum_X = fname_X + 3; \
+ seq_X = 0; \
+ if ((*(fname_X + 1) == 'f') && (isalpha(ch))) { \
+ seq_X = ch; \
+ if (*fname_X == 'c') \
+ cat_X = 1; \
+ else if (*fname_X == 'd') \
+ cat_X = 2; \
+ else if (*fname_X == 'r') \
+ cat_X = 3; \
+ else if (*fname_X == 't') \
+ cat_X = 4; \
+ if (cat_X != cat_other) { \
+ ch = *jnum_X; \
+ if (!isdigit(ch)) { \
+ if (isalpha(ch)) { \
+ jnum_X++; \
+ ch = *jnum_X; \
+ seq_X = (seq_X << 8) + ch; \
+ } \
+ if (!isdigit(ch)) \
+ cat_X = cat_other; \
+ } \
+ } \
+ } \
+} while (0)
+
+ MAP_TO_CAT(fname_a, cat_a, jnum_a, seq_a);
+ MAP_TO_CAT(fname_b, cat_b, jnum_b, seq_b);
+
+#undef MAP_TO_CAT
+
+ /* First handle all cases which have "other" files */
+ if ((cat_a >= cat_other) || (cat_b >= cat_other)) {
+ /* for two "other" files, just compare the full name */
+ if (cat_a == cat_b)
+ res = strcmp(fname_a, fname_b);
+ else if (cat_a < cat_b)
+ res = a_lt_b;
+ else
+ res = a_gt_b;
+ goto have_res;
+ }
+
+ /*
+ * At this point, we know both files are legitimate `cf', `df', `rf',
+ * or `tf' files. Compare them by job-number and machine name.
+ */
+ res = strcmp(jnum_a, jnum_b);
+ if (res != 0)
+ goto have_res;
+
+ /*
+ * We have two files which belong to the same job. Sort based
+ * on the catagory of file (`c' before `d', etc).
+ */
+ if (cat_a < cat_b) {
+ res = a_lt_b;
+ goto have_res;
+ } else if (cat_a > cat_b) {
+ res = a_gt_b;
+ goto have_res;
+ }
+
+ /*
+ * Two files in the same catagory for a single job. Sort based
+ * on the sequence letter(s). (usually `A' thru `Z', etc).
+ */
+ if (seq_a < seq_b) {
+ res = a_lt_b;
+ goto have_res;
+ } else if (seq_a > seq_b) {
+ res = a_gt_b;
+ goto have_res;
+ }
+
+ /*
+ * Given that the filenames in a directory are unique, this SHOULD
+ * never happen (unless there are logic errors in this routine).
+ * But if it does happen, we must return "is equal" or the caller
+ * might see inconsistent results in the sorting order, and that
+ * can trigger other problems.
+ */
+ printf("\t*** Error in sortq: %s == %s !\n", fname_a, fname_b);
+ printf("\t*** cat %d == %d ; seq = %d %d\n", cat_a, cat_b,
+ seq_a, seq_b);
+ res = 0;
+
+have_res:
+ return res;
+}
+
+/*
+ * Remove all spool files and temporaries from the spooling area.
+ * Or, perhaps:
+ * Remove incomplete jobs from spooling area.
+ */
+
+void
+clean_gi(int argc, char *argv[])
+{
+
+ /* init some fields before 'clean' is called for each queue */
+ cln_queuecnt = 0;
+ cln_now = time(NULL);
+ cln_minage = 3600.0; /* only delete files >1h old */
+ cln_filecnt = 0;
+ cln_sizecnt = 0;
+ cln_debug = 0;
+ cln_testonly = 0;
+ generic_wrapup = &wrapup_clean;
+
+ /* see if there are any options specified before the ptr list */
+ for (; argc > 0; argc--, argv++) {
+ if (**argv != '-')
+ break;
+ if (strcmp(*argv, "-d") == 0) {
+ /* just an example of an option... */
+ cln_debug++;
+ *argv = generic_nullarg; /* "erase" it */
+ } else {
+ printf("Invalid option '%s'\n", *argv);
+ generic_initerr = 1;
+ }
+ }
+
+ return;
+}
+
+void
+tclean_gi(int argc, char *argv[])
+{
+
+ /* only difference between 'clean' and 'tclean' is one value */
+ /* (...and the fact that 'clean' is priv and 'tclean' is not) */
+ clean_gi(argc, argv);
+ cln_testonly = 1;
+
+ return;
+}
+
+void
+clean_q(struct printer *pp)
+{
+ char *cp, *cp1, *lp;
+ struct dirent **queue;
+ size_t linerem;
+ int didhead, i, n, nitems, rmcp;
+
+ cln_queuecnt++;
+
+ didhead = 0;
+ if (generic_qselect == QSEL_BYNAME) {
+ printf("%s:\n", pp->printer);
+ didhead = 1;
+ }
+
+ lp = line;
+ cp = pp->spool_dir;
+ while (lp < &line[sizeof(line) - 1]) {
+ if ((*lp++ = *cp++) == 0)
+ break;
+ }
+ lp[-1] = '/';
+ linerem = sizeof(line) - (lp - line);
+
+ cln_foundcore = 0;
+ seteuid(euid);
+ nitems = scandir(pp->spool_dir, &queue, doselect, sortq);
+ seteuid(uid);
+ if (nitems < 0) {
+ if (!didhead) {
+ printf("%s:\n", pp->printer);
+ didhead = 1;
+ }
+ printf("\tcannot examine spool directory\n");
+ return;
+ }
+ if (cln_foundcore) {
+ if (!didhead) {
+ printf("%s:\n", pp->printer);
+ didhead = 1;
+ }
+ printf("\t** found a core file in %s !\n", pp->spool_dir);
+ }
+ if (nitems == 0)
+ return;
+ if (!didhead)
+ printf("%s:\n", pp->printer);
+ if (cln_debug) {
+ printf("\t** ----- Sorted list of files being checked:\n");
+ i = 0;
+ do {
+ cp = queue[i]->d_name;
+ printf("\t** [%3d] = %s\n", i, cp);
+ } while (++i < nitems);
+ printf("\t** ----- end of sorted list\n");
+ }
+ i = 0;
+ do {
+ cp = queue[i]->d_name;
+ rmcp = 0;
+ if (*cp == 'c') {
+ /*
+ * A control file. Look for matching data-files.
+ */
+ /* XXX
+ * Note the logic here assumes that the hostname
+ * part of cf-filenames match the hostname part
+ * in df-filenames, and that is not necessarily
+ * true (eg: for multi-homed hosts). This needs
+ * some further thought...
+ */
+ n = 0;
+ while (i + 1 < nitems) {
+ cp1 = queue[i + 1]->d_name;
+ if (*cp1 != 'd' || strcmp(cp + 3, cp1 + 3))
+ break;
+ i++;
+ n++;
+ }
+ if (n == 0) {
+ rmcp = 1;
+ }
+ } else if (*cp == 'e') {
+ /*
+ * Must be an errrs or email temp file.
+ */
+ rmcp = 1;
+ } else {
+ /*
+ * Must be a df with no cf (otherwise, it would have
+ * been skipped above) or an rf or tf file (which can
+ * always be removed if it is old enough).
+ */
+ rmcp = 1;
+ }
+ if (rmcp) {
+ if (strlen(cp) >= linerem) {
+ printf("\t** internal error: 'line' overflow!\n");
+ printf("\t** spooldir = %s\n", pp->spool_dir);
+ printf("\t** cp = %s\n", cp);
+ return;
+ }
+ strlcpy(lp, cp, linerem);
+ unlinkf(line);
+ }
+ } while (++i < nitems);
+}
+
+static void
+wrapup_clean(int laststatus __unused)
+{
+
+ printf("Checked %d queues, and ", cln_queuecnt);
+ if (cln_filecnt < 1) {
+ printf("no cruft was found\n");
+ return;
+ }
+ if (cln_testonly) {
+ printf("would have ");
+ }
+ printf("removed %d files (%ld bytes).\n", cln_filecnt, cln_sizecnt);
+}
+
+static void
+unlinkf(char *name)
+{
+ struct stat stbuf;
+ double agemod, agestat;
+ int res;
+ char linkbuf[BUFSIZ];
+
+ /*
+ * We have to use lstat() instead of stat(), in case this is a df*
+ * "file" which is really a symlink due to 'lpr -s' processing. In
+ * that case, we need to check the last-mod time of the symlink, and
+ * not the file that the symlink is pointed at.
+ */
+ seteuid(euid);
+ res = lstat(name, &stbuf);
+ seteuid(uid);
+ if (res < 0) {
+ printf("\terror return from stat(%s):\n", name);
+ printf("\t %s\n", strerror(errno));
+ return;
+ }
+
+ agemod = difftime(cln_now, stbuf.st_mtime);
+ agestat = difftime(cln_now, stbuf.st_ctime);
+ if (cln_debug > 1) {
+ /* this debugging-aid probably is not needed any more... */
+ printf("\t\t modify age=%g secs, stat age=%g secs\n",
+ agemod, agestat);
+ }
+ if ((agemod <= cln_minage) && (agestat <= cln_minage))
+ return;
+
+ /*
+ * if this file is a symlink, then find out the target of the
+ * symlink before unlink-ing the file itself
+ */
+ if (S_ISLNK(stbuf.st_mode)) {
+ seteuid(euid);
+ res = readlink(name, linkbuf, sizeof(linkbuf));
+ seteuid(uid);
+ if (res < 0) {
+ printf("\terror return from readlink(%s):\n", name);
+ printf("\t %s\n", strerror(errno));
+ return;
+ }
+ if (res == sizeof(linkbuf))
+ res--;
+ linkbuf[res] = '\0';
+ }
+
+ cln_filecnt++;
+ cln_sizecnt += stbuf.st_size;
+
+ if (cln_testonly) {
+ printf("\twould remove %s\n", name);
+ if (S_ISLNK(stbuf.st_mode)) {
+ printf("\t (which is a symlink to %s)\n", linkbuf);
+ }
+ } else {
+ seteuid(euid);
+ res = unlink(name);
+ seteuid(uid);
+ if (res < 0)
+ printf("\tcannot remove %s (!)\n", name);
+ else
+ printf("\tremoved %s\n", name);
+ /* XXX
+ * Note that for a df* file, this code should also check to see
+ * if it is a symlink to some other file, and if the original
+ * lpr command included '-r' ("remove file"). Of course, this
+ * code would not be removing the df* file unless there was no
+ * matching cf* file, and without the cf* file it is currently
+ * impossible to determine if '-r' had been specified...
+ *
+ * As a result of this quandry, we may be leaving behind a
+ * user's file that was supposed to have been removed after
+ * being printed. This may effect services such as CAP or
+ * samba, if they were configured to use 'lpr -r', and if
+ * datafiles are not being properly removed.
+ */
+ if (S_ISLNK(stbuf.st_mode)) {
+ printf("\t (which was a symlink to %s)\n", linkbuf);
+ }
+ }
+}
+
+/*
+ * Enable queuing to the printer (allow lpr to add new jobs to the queue).
+ */
+void
+enable_q(struct printer *pp)
+{
+ int setres;
+ char lf[MAXPATHLEN];
+
+ lock_file_name(pp, lf, sizeof lf);
+ printf("%s:\n", pp->printer);
+
+ setres = set_qstate(SQS_ENABLEQ, lf);
+}
+
+/*
+ * Disable queuing.
+ */
+void
+disable_q(struct printer *pp)
+{
+ int setres;
+ char lf[MAXPATHLEN];
+
+ lock_file_name(pp, lf, sizeof lf);
+ printf("%s:\n", pp->printer);
+
+ setres = set_qstate(SQS_DISABLEQ, lf);
+}
+
+/*
+ * Disable queuing and printing and put a message into the status file
+ * (reason for being down). If the user specified `-msg', then use
+ * everything after that as the message for the status file. If the
+ * user did NOT specify `-msg', then the command should take the first
+ * parameter as the printer name, and all remaining parameters as the
+ * message for the status file. (This is to be compatible with the
+ * original definition of 'down', which was implemented long before
+ * `-msg' was around).
+ */
+void
+down_gi(int argc, char *argv[])
+{
+
+ /* If `-msg' was specified, then this routine has nothing to do. */
+ if (generic_msg != NULL)
+ return;
+
+ /*
+ * If the user only gave one parameter, then use a default msg.
+ * (if argc == 1 at this point, then *argv == name of printer).
+ */
+ if (argc == 1) {
+ generic_msg = strdup("printing disabled\n");
+ return;
+ }
+
+ /*
+ * The user specified multiple parameters, and did not specify
+ * `-msg'. Build a message from all the parameters after the
+ * first one (and nullify those parameters so generic-processing
+ * will not process them as printer-queue names).
+ */
+ argc--;
+ argv++;
+ generic_msg = args2line(argc, argv);
+ for (; argc > 0; argc--, argv++)
+ *argv = generic_nullarg; /* "erase" it */
+}
+
+void
+down_q(struct printer *pp)
+{
+ int setres;
+ char lf[MAXPATHLEN];
+
+ lock_file_name(pp, lf, sizeof lf);
+ printf("%s:\n", pp->printer);
+
+ setres = set_qstate(SQS_DISABLEQ+SQS_STOPP, lf);
+ if (setres >= 0)
+ upstat(pp, generic_msg, 1);
+}
+
+/*
+ * Exit lpc
+ */
+void
+quit(int argc __unused, char *argv[] __unused)
+{
+ exit(0);
+}
+
+/*
+ * Kill and restart the daemon.
+ */
+void
+restart_q(struct printer *pp)
+{
+ int killres, setres, startok;
+ char lf[MAXPATHLEN];
+
+ lock_file_name(pp, lf, sizeof lf);
+ printf("%s:\n", pp->printer);
+
+ killres = kill_qtask(lf);
+
+ /*
+ * XXX - if the kill worked, we should probably sleep for
+ * a second or so before trying to restart the queue.
+ */
+
+ /* make sure the queue is set to print jobs */
+ setres = set_qstate(SQS_STARTP, lf);
+
+ seteuid(euid);
+ startok = startdaemon(pp);
+ seteuid(uid);
+ if (!startok)
+ printf("\tcouldn't restart daemon\n");
+ else
+ printf("\tdaemon restarted\n");
+}
+
+/*
+ * Set the status message of each queue listed. Requires a "-msg"
+ * parameter to indicate the end of the queue list and start of msg text.
+ */
+void
+setstatus_gi(int argc __unused, char *argv[] __unused)
+{
+
+ if (generic_msg == NULL) {
+ printf("You must specify '-msg' before the text of the new status message.\n");
+ generic_initerr = 1;
+ }
+}
+
+void
+setstatus_q(struct printer *pp)
+{
+ char lf[MAXPATHLEN];
+
+ lock_file_name(pp, lf, sizeof lf);
+ printf("%s:\n", pp->printer);
+
+ upstat(pp, generic_msg, 1);
+}
+
+/*
+ * Enable printing on the specified printer and startup the daemon.
+ */
+void
+start_q(struct printer *pp)
+{
+ int setres, startok;
+ char lf[MAXPATHLEN];
+
+ lock_file_name(pp, lf, sizeof lf);
+ printf("%s:\n", pp->printer);
+
+ setres = set_qstate(SQS_STARTP, lf);
+
+ seteuid(euid);
+ startok = startdaemon(pp);
+ seteuid(uid);
+ if (!startok)
+ printf("\tcouldn't start daemon\n");
+ else
+ printf("\tdaemon started\n");
+ seteuid(uid);
+}
+
+/*
+ * Print the status of the printer queue.
+ */
+void
+status(struct printer *pp)
+{
+ struct stat stbuf;
+ register int fd, i;
+ register struct dirent *dp;
+ DIR *dirp;
+ char file[MAXPATHLEN];
+
+ printf("%s:\n", pp->printer);
+ lock_file_name(pp, file, sizeof file);
+ if (stat(file, &stbuf) >= 0) {
+ printf("\tqueuing is %s\n",
+ ((stbuf.st_mode & LFM_QUEUE_DIS) ? "disabled"
+ : "enabled"));
+ printf("\tprinting is %s\n",
+ ((stbuf.st_mode & LFM_PRINT_DIS) ? "disabled"
+ : "enabled"));
+ } else {
+ printf("\tqueuing is enabled\n");
+ printf("\tprinting is enabled\n");
+ }
+ if ((dirp = opendir(pp->spool_dir)) == NULL) {
+ printf("\tcannot examine spool directory\n");
+ return;
+ }
+ i = 0;
+ while ((dp = readdir(dirp)) != NULL) {
+ if (*dp->d_name == 'c' && dp->d_name[1] == 'f')
+ i++;
+ }
+ closedir(dirp);
+ if (i == 0)
+ printf("\tno entries in spool area\n");
+ else if (i == 1)
+ printf("\t1 entry in spool area\n");
+ else
+ printf("\t%d entries in spool area\n", i);
+ fd = open(file, O_RDONLY);
+ if (fd < 0 || flock(fd, LOCK_SH|LOCK_NB) == 0) {
+ (void) close(fd); /* unlocks as well */
+ printf("\tprinter idle\n");
+ return;
+ }
+ (void) close(fd);
+ /* print out the contents of the status file, if it exists */
+ status_file_name(pp, file, sizeof file);
+ fd = open(file, O_RDONLY|O_SHLOCK);
+ if (fd >= 0) {
+ (void) fstat(fd, &stbuf);
+ if (stbuf.st_size > 0) {
+ putchar('\t');
+ while ((i = read(fd, line, sizeof(line))) > 0)
+ (void) fwrite(line, 1, i, stdout);
+ }
+ (void) close(fd); /* unlocks as well */
+ }
+}
+
+/*
+ * Stop the specified daemon after completing the current job and disable
+ * printing.
+ */
+void
+stop_q(struct printer *pp)
+{
+ int setres;
+ char lf[MAXPATHLEN];
+
+ lock_file_name(pp, lf, sizeof lf);
+ printf("%s:\n", pp->printer);
+
+ setres = set_qstate(SQS_STOPP, lf);
+
+ if (setres >= 0)
+ upstat(pp, "printing disabled\n", 0);
+}
+
+struct jobqueue **queue;
+int nitems;
+time_t mtime;
+
+/*
+ * Put the specified jobs at the top of printer queue.
+ */
+void
+topq(int argc, char *argv[])
+{
+ register int i;
+ struct stat stbuf;
+ int cmdstatus, changed;
+ struct printer myprinter, *pp = &myprinter;
+
+ if (argc < 3) {
+ printf("usage: topq printer [jobnum ...] [user ...]\n");
+ return;
+ }
+
+ --argc;
+ ++argv;
+ init_printer(pp);
+ cmdstatus = getprintcap(*argv, pp);
+ switch(cmdstatus) {
+ default:
+ fatal(pp, "%s", pcaperr(cmdstatus));
+ case PCAPERR_NOTFOUND:
+ printf("unknown printer %s\n", *argv);
+ return;
+ case PCAPERR_TCOPEN:
+ printf("warning: %s: unresolved tc= reference(s)", *argv);
+ break;
+ case PCAPERR_SUCCESS:
+ break;
+ }
+ printf("%s:\n", pp->printer);
+
+ seteuid(euid);
+ if (chdir(pp->spool_dir) < 0) {
+ printf("\tcannot chdir to %s\n", pp->spool_dir);
+ goto out;
+ }
+ seteuid(uid);
+ nitems = getq(pp, &queue);
+ if (nitems == 0)
+ return;
+ changed = 0;
+ mtime = queue[0]->job_time;
+ for (i = argc; --i; ) {
+ if (doarg(argv[i]) == 0) {
+ printf("\tjob %s is not in the queue\n", argv[i]);
+ continue;
+ } else
+ changed++;
+ }
+ for (i = 0; i < nitems; i++)
+ free(queue[i]);
+ free(queue);
+ if (!changed) {
+ printf("\tqueue order unchanged\n");
+ return;
+ }
+ /*
+ * Turn on the public execute bit of the lock file to
+ * get lpd to rebuild the queue after the current job.
+ */
+ seteuid(euid);
+ if (changed && stat(pp->lock_file, &stbuf) >= 0)
+ (void) chmod(pp->lock_file, stbuf.st_mode | LFM_RESET_QUE);
+
+out:
+ seteuid(uid);
+}
+
+/*
+ * Reposition the job by changing the modification time of
+ * the control file.
+ */
+static int
+touch(struct jobqueue *jq)
+{
+ struct timeval tvp[2];
+ int ret;
+
+ tvp[0].tv_sec = tvp[1].tv_sec = --mtime;
+ tvp[0].tv_usec = tvp[1].tv_usec = 0;
+ seteuid(euid);
+ ret = utimes(jq->job_cfname, tvp);
+ seteuid(uid);
+ return (ret);
+}
+
+/*
+ * Checks if specified job name is in the printer's queue.
+ * Returns: negative (-1) if argument name is not in the queue.
+ */
+static int
+doarg(char *job)
+{
+ register struct jobqueue **qq;
+ register int jobnum, n;
+ register char *cp, *machine;
+ int cnt = 0;
+ FILE *fp;
+
+ /*
+ * Look for a job item consisting of system name, colon, number
+ * (example: ucbarpa:114)
+ */
+ if ((cp = strchr(job, ':')) != NULL) {
+ machine = job;
+ *cp++ = '\0';
+ job = cp;
+ } else
+ machine = NULL;
+
+ /*
+ * Check for job specified by number (example: 112 or 235ucbarpa).
+ */
+ if (isdigit(*job)) {
+ jobnum = 0;
+ do
+ jobnum = jobnum * 10 + (*job++ - '0');
+ while (isdigit(*job));
+ for (qq = queue + nitems; --qq >= queue; ) {
+ n = 0;
+ for (cp = (*qq)->job_cfname+3; isdigit(*cp); )
+ n = n * 10 + (*cp++ - '0');
+ if (jobnum != n)
+ continue;
+ if (*job && strcmp(job, cp) != 0)
+ continue;
+ if (machine != NULL && strcmp(machine, cp) != 0)
+ continue;
+ if (touch(*qq) == 0) {
+ printf("\tmoved %s\n", (*qq)->job_cfname);
+ cnt++;
+ }
+ }
+ return(cnt);
+ }
+ /*
+ * Process item consisting of owner's name (example: henry).
+ */
+ for (qq = queue + nitems; --qq >= queue; ) {
+ seteuid(euid);
+ fp = fopen((*qq)->job_cfname, "r");
+ seteuid(uid);
+ if (fp == NULL)
+ continue;
+ while (getline(fp) > 0)
+ if (line[0] == 'P')
+ break;
+ (void) fclose(fp);
+ if (line[0] != 'P' || strcmp(job, line+1) != 0)
+ continue;
+ if (touch(*qq) == 0) {
+ printf("\tmoved %s\n", (*qq)->job_cfname);
+ cnt++;
+ }
+ }
+ return(cnt);
+}
+
+/*
+ * Enable both queuing & printing, and start printer (undo `down').
+ */
+void
+up_q(struct printer *pp)
+{
+ int setres, startok;
+ char lf[MAXPATHLEN];
+
+ lock_file_name(pp, lf, sizeof lf);
+ printf("%s:\n", pp->printer);
+
+ setres = set_qstate(SQS_ENABLEQ+SQS_STARTP, lf);
+
+ seteuid(euid);
+ startok = startdaemon(pp);
+ seteuid(uid);
+ if (!startok)
+ printf("\tcouldn't start daemon\n");
+ else
+ printf("\tdaemon started\n");
+}
diff --git a/usr.sbin/lpr/lpc/cmdtab.c b/usr.sbin/lpr/lpc/cmdtab.c
new file mode 100644
index 0000000..2d9b17d
--- /dev/null
+++ b/usr.sbin/lpr/lpc/cmdtab.c
@@ -0,0 +1,92 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#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 setstatushelp[] = "set the status message of a queue, requires\n"
+ "\t\t\"-msg\" before the text of the new message";
+char starthelp[] = "enable printing and start a spooling daemon";
+char statushelp[] = "show status of daemon and queue";
+char stophelp[] = "stop a spooling daemon after current job completes and disable printing";
+char tcleanhelp[] = "test to see what files a clean cmd would remove";
+char topqhelp[] = "put job at top of printer queue";
+char uphelp[] = "enable everything and restart spooling daemon";
+
+/* Use some abbreviations so entries won't need to wrap */
+#define PR LPC_PRIVCMD
+#define M LPC_MSGOPT
+
+struct cmd cmdtab[] = {
+ { "abort", aborthelp, PR, 0, abort_q },
+ { "clean", cleanhelp, PR, clean_gi, clean_q },
+ { "enable", enablehelp, PR, 0, enable_q },
+ { "exit", quithelp, 0, quit, 0 },
+ { "disable", disablehelp, PR, 0, disable_q },
+ { "down", downhelp, PR|M, down_gi, down_q },
+ { "help", helphelp, 0, help, 0 },
+ { "quit", quithelp, 0, quit, 0 },
+ { "restart", restarthelp, 0, 0, restart_q },
+ { "start", starthelp, PR, 0, start_q },
+ { "status", statushelp, 0, 0, status },
+ { "setstatus", setstatushelp, PR|M, setstatus_gi, setstatus_q },
+ { "stop", stophelp, PR, 0, stop_q },
+ { "tclean", tcleanhelp, 0, tclean_gi, clean_q },
+ { "topq", topqhelp, PR, topq, 0 },
+ { "up", uphelp, PR, 0, up_q },
+ { "?", helphelp, 0, help, 0 },
+ { 0, 0, 0, 0, 0},
+};
+
+int NCMDS = sizeof (cmdtab) / sizeof (cmdtab[0]);
diff --git a/usr.sbin/lpr/lpc/extern.h b/usr.sbin/lpr/lpc/extern.h
new file mode 100644
index 0000000..c2b3c7c
--- /dev/null
+++ b/usr.sbin/lpr/lpc/extern.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)extern.h 8.1 (Berkeley) 6/6/93
+ *
+ * $FreeBSD$
+ */
+
+
+#include <sys/types.h>
+#include <sys/cdefs.h>
+
+
+__BEGIN_DECLS
+void abort_q(struct printer *_pp);
+void clean_gi(int _argc, char *_argv[]);
+void clean_q(struct printer *_pp);
+void disable_q(struct printer *_pp);
+void down_gi(int _argc, char *_argv[]);
+void down_q(struct printer *_pp);
+void enable_q(struct printer *_pp);
+void generic(void (*_specificrtn)(struct printer *_pp), int _cmdopts,
+ void (*_initcmd)(int _argc, char *_argv[]),
+ int _argc, char *_argv[]);
+void help(int _argc, char *_argv[]);
+void quit(int _argc, char *_argv[]);
+void restart_q(struct printer *_pp);
+void setstatus_gi(int _argc, char *_argv[]);
+void setstatus_q(struct printer *_pp);
+void start_q(struct printer *_pp);
+void status(struct printer *_pp);
+void stop_q(struct printer *_pp);
+void tclean_gi(int _argc, char *_argv[]);
+void topq(int _argc, char *_argv[]);
+void up_q(struct printer *_pp);
+__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..aebbf0e
--- /dev/null
+++ b/usr.sbin/lpr/lpc/lpc.8
@@ -0,0 +1,229 @@
+.\" Copyright (c) 1983, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)lpc.8 8.5 (Berkeley) 4/28/95
+.\" $FreeBSD$
+.\"
+.Dd June 15, 2002
+.Dt LPC 8
+.Os
+.Sh NAME
+.Nm lpc
+.Nd line printer control program
+.Sh SYNOPSIS
+.Nm
+.Op Ar command Op Ar argument ...
+.Sh DESCRIPTION
+.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
+may be used to:
+.Bl -bullet -offset indent
+.It
+disable or enable a printer,
+.It
+disable or enable a printer's spooling queue,
+.It
+rearrange the order of jobs in a spooling queue,
+.It
+find the status of printers, and their associated
+spooling queues and printer daemons,
+.It
+change the status message for printer queues (the status message
+may be seen by users as part of the output of the
+.Xr lpq 1 utility).
+.El
+.Pp
+Without any arguments,
+.Nm
+will prompt for commands from the standard input.
+If arguments are supplied,
+.Nm
+interprets the first argument as a command and the remaining
+arguments as parameters to the command.
+The standard input
+may be redirected causing
+.Nm
+to read commands from file.
+Commands may be abbreviated;
+the following is the list of recognized commands.
+.Pp
+.Bl -tag -width indent -compact
+.It Ic \&? Op Ar command ...
+.It Ic help Op Ar command ...
+Print a short description of each command specified in the argument list,
+or, if no argument is given, a list of the recognized commands.
+.Pp
+.It Ic abort Brq Cm all | Ar printer
+Terminate an active spooling daemon on the local host immediately and
+then disable printing (preventing new daemons from being started by
+.Xr lpr 1 )
+for the specified printers.
+.Pp
+.It Ic clean Brq Cm all | Ar printer
+Remove any temporary files, data files, and control files that cannot
+be printed (i.e., do not form a complete printer job)
+from the specified printer queue(s) on the local machine.
+This command will also look for
+.Pa core
+files in spool directory
+for each printer queue, and list any that are found.
+It will not remove any
+.Pa core
+files.
+See also the
+.Ic tclean
+command.
+.Pp
+.It Ic disable Brq Cm all | Ar printer
+Turn the specified printer queues off.
+This prevents new
+printer jobs from being entered into the queue by
+.Xr lpr 1 .
+.Pp
+.It Ic down Bro Cm all | Ar printer ... Brc Cm -msg Ar message ...
+.It Ic down Bro Cm all | Ar printer Brc Ar message ...
+Turn the specified printer queue off, disable printing and put
+.Ar message
+in the printer status file.
+When specifying more than one printer queue, the
+.Ic -msg
+argument is required to separate the list of printers from the text
+that will be the new status message.
+The message doesn't need to be quoted, the
+remaining arguments are treated like
+.Xr echo 1 .
+This is normally used to take a printer down, and let other users
+find out why it is down (the
+.Xr lpq 1
+utility will indicate that the printer is down and will print the
+status message).
+.Pp
+.It Ic enable Brq Cm all | Ar printer
+Enable spooling on the local queue for the listed printers.
+This will allow
+.Xr lpr 1
+to put new jobs in the spool queue.
+.Pp
+.It Ic exit
+.It Ic quit
+Exit from
+.Nm .
+.Pp
+.It Ic restart Brq Cm all | Ar printer
+Attempt to start a new printer daemon.
+This is useful when some abnormal condition causes the daemon to
+die unexpectedly, leaving jobs in the queue.
+.Xr lpq 1
+will report that there is no daemon present when this condition occurs.
+If the user is the super-user,
+try to abort the current daemon first (i.e., kill and restart a stuck daemon).
+.Pp
+.It Ic setstatus Bro Cm all | Ar printer Brc Cm -msg Ar message ...
+Set the status message for the specified printers.
+The
+.Ic -msg
+argument is required to separate the list of printers from the text
+that will be the new status message.
+This is normally used to change the status message when the printer
+queue is no longer active after printing has been disabled, and you
+want to change what users will see in the output of the
+.Xr lpq 1 utility.
+.Pp
+.It Ic start Brq Cm all | Ar printer
+Enable printing and start a spooling daemon for the listed printers.
+.Pp
+.It Ic status Brq Cm all | Ar printer
+Display the status of daemons and queues on the local machine.
+.Pp
+.It Ic stop Brq Cm all | Ar printer
+Stop a spooling daemon after the current job completes and disable
+printing.
+.Pp
+.It Ic tclean Brq Cm all | Ar printer
+This will do a test-run of the
+.Ic clean
+command.
+All the same checking is done, but the command will only print out
+messages saying what a similar
+.Ic clean
+command would do if the user typed it in.
+It will not remove any files.
+Note that the
+.Ic clean
+command is a privileged command, while the
+.Ic tclean
+command is not restricted.
+.Pp
+.It Ic topq Ar printer Xo
+.Op Ar jobnum ...
+.Op Ar user ...
+.Xc
+Place the jobs in the order listed at the top of the printer queue.
+.Pp
+.It Ic up Brq Cm all | Ar printer
+Enable everything and start a new printer daemon.
+Undoes the effects of
+.Ic down .
+.El
+.Sh FILES
+.Bl -tag -width /var/spool/*/lockx -compact
+.It Pa /etc/printcap
+printer description file
+.It Pa /var/spool/*
+spool directories
+.It Pa /var/spool/*/lock
+lock file for queue control
+.El
+.Sh SEE ALSO
+.Xr lpq 1 ,
+.Xr lpr 1 ,
+.Xr lprm 1 ,
+.Xr printcap 5 ,
+.Xr chkprintcap 8 ,
+.Xr lpd 8
+.Sh DIAGNOSTICS
+.Bl -diag
+.It "?Ambiguous command"
+abbreviation matches more than one command
+.It "?Invalid command"
+no match was found
+.It "?Privileged command"
+you must be a member of group "operator" or root to execute this command
+.El
+.Sh HISTORY
+The
+.Nm
+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..357e4ad
--- /dev/null
+++ b/usr.sbin/lpr/lpc/lpc.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[] = "@(#)lpc.c 8.3 (Berkeley) 4/28/95";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#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 <histedit.h>
+
+#include "lp.h"
+#include "lpc.h"
+#include "extern.h"
+
+#ifndef LPR_OPER
+#define LPR_OPER "operator" /* group name of lpr operators */
+#endif
+
+/*
+ * lpc -- line printer control program
+ */
+
+#define MAX_CMDLINE 200
+#define MAX_MARGV 20
+static int fromatty;
+
+static char cmdline[MAX_CMDLINE];
+static int margc;
+static char *margv[MAX_MARGV];
+uid_t uid, euid;
+
+int main(int _argc, char *_argv[]);
+static void cmdscanner(void);
+static struct cmd *getcmd(const char *_name);
+static void intr(int _signo);
+static void makeargv(void);
+static int ingroup(const char *_grname);
+
+int
+main(int argc, char *argv[])
+{
+ register struct cmd *c;
+
+ euid = geteuid();
+ uid = getuid();
+ seteuid(uid);
+ progname = argv[0];
+ openlog("lpd", 0, LOG_LPR);
+
+ if (--argc > 0) {
+ c = getcmd(*++argv);
+ if (c == (struct cmd *)-1) {
+ printf("?Ambiguous command\n");
+ exit(1);
+ }
+ if (c == 0) {
+ printf("?Invalid command\n");
+ exit(1);
+ }
+ if ((c->c_opts & LPC_PRIVCMD) && getuid() &&
+ ingroup(LPR_OPER) == 0) {
+ printf("?Privileged command\n");
+ exit(1);
+ }
+ if (c->c_generic != 0)
+ generic(c->c_generic, c->c_opts, c->c_handler,
+ argc, argv);
+ else
+ (*c->c_handler)(argc, argv);
+ exit(0);
+ }
+ fromatty = isatty(fileno(stdin));
+ if (!fromatty)
+ signal(SIGINT, intr);
+ for (;;) {
+ cmdscanner();
+ }
+}
+
+static void
+intr(int signo __unused)
+{
+ /* (the '__unused' is just to avoid a compile-time warning) */
+ exit(0);
+}
+
+static const char *
+lpc_prompt(void)
+{
+ return ("lpc> ");
+}
+
+/*
+ * Command parser.
+ */
+static void
+cmdscanner(void)
+{
+ register struct cmd *c;
+ static EditLine *el;
+ static History *hist;
+ HistEvent he;
+ size_t len;
+ int num;
+ const char *bp;
+
+ num = 0;
+ bp = NULL;
+ el = NULL;
+ hist = NULL;
+ for (;;) {
+ if (fromatty) {
+ if (!el) {
+ el = el_init("lpc", stdin, stdout, stderr);
+ hist = history_init();
+ history(hist, &he, H_EVENT, 100);
+ el_set(el, EL_HIST, history, hist);
+ el_set(el, EL_EDITOR, "emacs");
+ el_set(el, EL_PROMPT, lpc_prompt);
+ el_set(el, EL_SIGNAL, 1);
+ el_source(el, NULL);
+ /*
+ * EditLine init may call 'cgetset()' to set a
+ * capability-db meant for termcap (eg: to set
+ * terminal type 'xterm'). Reset that now, or
+ * that same db-information will be used for
+ * printcap (giving us an "xterm" printer, with
+ * all kinds of invalid capabilities...).
+ */
+ cgetset(NULL);
+ }
+ if ((bp = el_gets(el, &num)) == NULL || num == 0)
+ quit(0, NULL);
+
+ len = (num > MAX_CMDLINE) ? MAX_CMDLINE : num;
+ memcpy(cmdline, bp, len);
+ cmdline[len] = 0;
+ history(hist, &he, H_ENTER, bp);
+
+ } else {
+ if (fgets(cmdline, MAX_CMDLINE, stdin) == 0)
+ quit(0, NULL);
+ if (cmdline[0] == 0 || cmdline[0] == '\n')
+ break;
+ }
+
+ makeargv();
+ if (margc == 0)
+ continue;
+ if (el_parse(el, margc, margv) != -1)
+ continue;
+
+ c = getcmd(margv[0]);
+ if (c == (struct cmd *)-1) {
+ printf("?Ambiguous command\n");
+ continue;
+ }
+ if (c == 0) {
+ printf("?Invalid command\n");
+ continue;
+ }
+ if ((c->c_opts & LPC_PRIVCMD) && getuid() &&
+ ingroup(LPR_OPER) == 0) {
+ printf("?Privileged command\n");
+ continue;
+ }
+
+ /*
+ * Two different commands might have the same generic rtn
+ * (eg: "clean" and "tclean"), and just use different
+ * handler routines for distinct command-setup. The handler
+ * routine might also be set on a generic routine for
+ * initial parameter processing.
+ */
+ if (c->c_generic != 0)
+ generic(c->c_generic, c->c_opts, c->c_handler,
+ margc, margv);
+ else
+ (*c->c_handler)(margc, margv);
+ }
+}
+
+static struct cmd *
+getcmd(const char *name)
+{
+ register const char *p, *q;
+ register struct cmd *c, *found;
+ register int nmatches, longest;
+
+ longest = 0;
+ nmatches = 0;
+ found = 0;
+ for (c = cmdtab; (p = c->c_name); c++) {
+ for (q = name; *q == *p++; q++)
+ if (*q == 0) /* exact match? */
+ return(c);
+ if (!*q) { /* the name was a prefix */
+ if (q - name > longest) {
+ longest = q - name;
+ nmatches = 1;
+ found = c;
+ } else if (q - name == longest)
+ nmatches++;
+ }
+ }
+ if (nmatches > 1)
+ return((struct cmd *)-1);
+ return(found);
+}
+
+/*
+ * Slice a string up into argc/argv.
+ */
+static void
+makeargv(void)
+{
+ register char *cp;
+ register char **argp = margv;
+ register int n = 0;
+
+ margc = 0;
+ for (cp = cmdline; *cp && (size_t)(cp - cmdline) < sizeof(cmdline) &&
+ n < MAX_MARGV; n++) {
+ while (isspace(*cp))
+ cp++;
+ if (*cp == '\0')
+ break;
+ *argp++ = cp;
+ margc += 1;
+ while (*cp != '\0' && !isspace(*cp))
+ cp++;
+ if (*cp == '\0')
+ break;
+ *cp++ = '\0';
+ }
+ *argp++ = 0;
+}
+
+#define HELPINDENT (sizeof ("directory"))
+
+/*
+ * Help command.
+ */
+void
+help(int argc, char *argv[])
+{
+ register struct cmd *c;
+
+ if (argc == 1) {
+ register int i, j, w;
+ int columns, width = 0, lines;
+
+ printf("Commands may be abbreviated. Commands are:\n\n");
+ for (c = cmdtab; c->c_name; c++) {
+ int len = strlen(c->c_name);
+
+ if (len > width)
+ width = len;
+ }
+ width = (width + 8) &~ 7;
+ columns = 80 / width;
+ if (columns == 0)
+ columns = 1;
+ lines = (NCMDS + columns - 1) / columns;
+ for (i = 0; i < lines; i++) {
+ for (j = 0; j < columns; j++) {
+ c = cmdtab + j * lines + i;
+ if (c->c_name)
+ printf("%s", c->c_name);
+ if (c + lines >= &cmdtab[NCMDS]) {
+ printf("\n");
+ break;
+ }
+ w = strlen(c->c_name);
+ while (w < width) {
+ w = (w + 8) &~ 7;
+ putchar('\t');
+ }
+ }
+ }
+ return;
+ }
+ while (--argc > 0) {
+ register char *arg;
+ arg = *++argv;
+ c = getcmd(arg);
+ if (c == (struct cmd *)-1)
+ printf("?Ambiguous help command %s\n", arg);
+ else if (c == (struct cmd *)0)
+ printf("?Invalid help command %s\n", arg);
+ else
+ printf("%-*s\t%s\n", (int) HELPINDENT,
+ c->c_name, c->c_help);
+ }
+}
+
+/*
+ * return non-zero if the user is a member of the given group
+ */
+static int
+ingroup(const char *grname)
+{
+ static struct group *gptr=NULL;
+ static int ngroups = 0;
+ static gid_t groups[NGROUPS];
+ register gid_t gid;
+ register int i;
+
+ if (gptr == NULL) {
+ if ((gptr = getgrnam(grname)) == NULL) {
+ warnx("warning: unknown group '%s'", grname);
+ return(0);
+ }
+ ngroups = getgroups(NGROUPS, groups);
+ if (ngroups < 0)
+ err(1, "getgroups");
+ }
+ gid = gptr->gr_gid;
+ for (i = 0; i < ngroups; i++)
+ if (gid == groups[i])
+ return(1);
+ return(0);
+}
diff --git a/usr.sbin/lpr/lpc/lpc.h b/usr.sbin/lpr/lpc/lpc.h
new file mode 100644
index 0000000..73e4cc5
--- /dev/null
+++ b/usr.sbin/lpr/lpc/lpc.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)lpc.h 8.1 (Berkeley) 6/6/93
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Line Printer Control (lpc) program.
+ */
+struct printer;
+
+#define LPC_PRIVCMD 0x0001 /* a privileged command */
+#define LPC_MSGOPT 0x0002 /* command recognizes -msg option */
+
+struct cmd {
+ const char *c_name; /* command name */
+ const char *c_help; /* help message */
+ const int c_opts; /* flags (eg: privileged command) */
+ /* routine to do all the work for plain cmds, or
+ * initialization work for generic-printer cmds: */
+ void (*c_handler)(int, char *[]);
+ /* routine to do the work for generic-printer cmds: */
+ void (*c_generic)(struct printer *);
+};
diff --git a/usr.sbin/lpr/lpd/Makefile b/usr.sbin/lpr/lpd/Makefile
new file mode 100644
index 0000000..9af3655
--- /dev/null
+++ b/usr.sbin/lpr/lpd/Makefile
@@ -0,0 +1,13 @@
+# From: @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+PROG= lpd
+MAN= lpd.8
+SRCS= lpd.c printjob.c recvjob.c lpdchar.c modes.c
+
+CFLAGS+= -I${.CURDIR}/../common_source
+
+DPADD= ${LIBLPR}
+LDADD= ${LIBLPR}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/lpr/lpd/extern.h b/usr.sbin/lpr/lpd/extern.h
new file mode 100644
index 0000000..497594e
--- /dev/null
+++ b/usr.sbin/lpr/lpd/extern.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * From: @(#)extern.h 8.1 (Berkeley) 6/6/93
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+
+extern char scnkey[][HEIGHT]; /* in lpdchar.c */
+extern int lflag; /* in lpd.c */
+
+struct printer;
+struct termios;
+
+__BEGIN_DECLS
+void printjob(struct printer *_pp);
+void startprinting(const char *_printer);
+void recvjob(const char *_printer);
+int msearch(char *_str, struct termios *_ip);
+__END_DECLS
diff --git a/usr.sbin/lpr/lpd/lpd.8 b/usr.sbin/lpr/lpd/lpd.8
new file mode 100644
index 0000000..f27b36e
--- /dev/null
+++ b/usr.sbin/lpr/lpd/lpd.8
@@ -0,0 +1,317 @@
+.\" Copyright (c) 1983, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)lpd.8 8.3 (Berkeley) 4/19/94
+.\" $FreeBSD$
+.\"
+.Dd June 06, 2001
+.Dt LPD 8
+.Os
+.Sh NAME
+.Nm lpd
+.Nd line printer spooler daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl cdlpsW46
+.Op Ar port#
+.Sh DESCRIPTION
+.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 c
+By default, if some remote host has a connection error while trying to
+send a print request to
+.Nm
+on a local host,
+.Nm
+will only send error message to that remote host.
+The
+.Fl c
+flag causes
+.Nm
+to also log all of those connection errors via
+.Xr syslog 3 .
+.It Fl d
+Turn on
+.Dv SO_DEBUG
+on the Internet listening socket (see
+.Xr setsockopt 2 ) .
+.It Fl l
+The
+.Fl l
+flag causes
+.Nm
+to log valid requests received from the network.
+This can be useful
+for debugging purposes.
+.It Fl p
+The
+.Fl p
+flag is a synonym for the
+.Fl s
+flag.
+It is being depreciated, and may be removed in a
+future version of
+.Nm .
+.It Fl s
+The
+.Fl s
+(secure) flag causes
+.Nm
+not to open an Internet listening socket.
+This means that
+.Nm
+will not accept any connections from any remote
+hosts, although it will still accept print requests
+from all local users.
+.It Fl W
+By default, the
+.Nm
+daemon will only accept connections which originate
+from a reserved-port (<1024) on the remote host.
+The
+.Fl W
+flag causes
+.Nm
+to accept connections coming from any port.
+This is can be useful when you want to accept print jobs
+from certain implementations of lpr written for Windows.
+.It Fl 4
+Inet only.
+.It Fl 6
+Inet6 only.
+.It Fl 46
+Inet and inet6 (default).
+.It Ar "port#"
+The Internet port number used to rendezvous
+with other processes is normally obtained with
+.Xr getservbyname 3
+but can be changed with the
+.Ar port#
+argument.
+.El
+.Pp
+Access control is provided by two means.
+First, all requests must come from
+one of the machines listed in the file
+.Pa /etc/hosts.equiv
+or
+.Pa /etc/hosts.lpd .
+Second, if the
+.Li rs
+capability is specified in the
+.Xr printcap 5
+entry for the printer being accessed,
+.Em lpr
+requests will only be honored for those users with accounts on the
+machine with the printer.
+.Pp
+The file
+.Em minfree
+in each spool directory contains the number of disk blocks to leave free
+so that the line printer queue won't completely fill the disk.
+The
+.Em minfree
+file can be edited with your favorite text editor.
+.Pp
+The daemon begins processing files
+after it has successfully set the lock for exclusive
+access (described a bit later),
+and scans the spool directory
+for files beginning with
+.Em cf .
+Lines in each
+.Em cf
+file specify files to be printed or non-printing actions to be
+performed. Each such line begins with a key character
+to specify what to do with the remainder of the line.
+.Bl -tag -width Ds
+.It J
+Job Name. String to be used for the job name on the burst page.
+.It C
+Classification. String to be used for the classification line
+on the burst page.
+.It L
+Literal. The line contains identification info from
+the password file and causes the banner page to be printed.
+.It T
+Title. String to be used as the title for
+.Xr pr 1 .
+.It H
+Host Name. Name of the machine where
+.Xr lpr
+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).
+.It Z
+Locale. String to be used as the locale for
+.Xr pr 1 .
+.El
+.Pp
+If a file cannot be opened, a message will be logged via
+.Xr syslog 3
+using the
+.Em LOG_LPR
+facility.
+.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 chkprintcap 8 ,
+.Xr lpc 8 ,
+.Xr pac 8
+.Rs
+.%T "4.2 BSD Line Printer Spooler Manual"
+.Re
+.Sh HISTORY
+An
+.Nm
+daemon appeared in Version 6 AT&T UNIX.
diff --git a/usr.sbin/lpr/lpd/lpd.c b/usr.sbin/lpr/lpd/lpd.c
new file mode 100644
index 0000000..3420b65
--- /dev/null
+++ b/usr.sbin/lpr/lpd/lpd.c
@@ -0,0 +1,947 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#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 sflag; /* no incoming port flag */
+int from_remote; /* from remote socket */
+
+int main(int argc, char **_argv);
+static void reapchild(int _signo);
+static void mcleanup(int _signo);
+static void doit(void);
+static void startup(void);
+static void chkhost(struct sockaddr *_f, int _ch_opts);
+static int ckqueue(struct printer *_pp);
+static void fhosterr(int _ch_opts, char *_sysmsg, char *_usermsg);
+static int *socksetup(int _af, int _debuglvl);
+static void usage(void);
+
+/* XXX from libc/net/rcmd.c */
+extern int __ivaliduser_sa __P((FILE *, struct sockaddr *, socklen_t,
+ const char *, const char *));
+
+uid_t uid, euid;
+
+#define LPD_NOPORTCHK 0001 /* skip reserved-port check */
+#define LPD_LOGCONNERR 0002 /* (sys)log connection errors */
+#define LPD_ADDFROMLINE 0004 /* just used for fhosterr() */
+
+int
+main(int argc, char **argv)
+{
+ int ch_options, errs, f, funix, *finet, i, lfd, socket_debug;
+ fd_set defreadfds;
+ struct sockaddr_un un, fromunix;
+ struct sockaddr_storage frominet;
+ socklen_t fromlen;
+ sigset_t omask, nmask;
+ struct servent *sp, serv;
+ int inet_flag = 0, inet6_flag = 0;
+
+ euid = geteuid(); /* these shouldn't be different */
+ uid = getuid();
+
+ ch_options = 0;
+ socket_debug = 0;
+ gethostname(local_host, sizeof(local_host));
+
+ progname = "lpd";
+
+ if (euid != 0)
+ errx(EX_NOPERM,"must run as root");
+
+ errs = 0;
+ while ((i = getopt(argc, argv, "cdlpswW46")) != -1)
+ switch (i) {
+ case 'c':
+ /* log all kinds of connection-errors to syslog */
+ ch_options |= LPD_LOGCONNERR;
+ break;
+ case 'd':
+ socket_debug++;
+ break;
+ case 'l':
+ lflag++;
+ break;
+ case 'p': /* letter initially used for -s */
+ /*
+ * This will probably be removed with 5.0-release.
+ */
+ /* FALLTHROUGH */
+ case 's': /* secure (no inet) */
+ sflag++;
+ break;
+ case 'w': /* netbsd uses -w for maxwait */
+ /*
+ * This will be removed after the release of 4.4, as
+ * it conflicts with -w in netbsd's lpd. For now it
+ * is just a warning, so we won't suddenly break lpd
+ * for anyone who is currently using the option.
+ */
+ syslog(LOG_WARNING,
+ "NOTE: the -w option has been renamed -W");
+ syslog(LOG_WARNING,
+ "NOTE: please change your lpd config to use -W");
+ /* FALLTHROUGH */
+ case 'W':
+ /* allow connections coming from a non-reserved port */
+ /* (done by some lpr-implementations for MS-Windows) */
+ ch_options |= LPD_NOPORTCHK;
+ break;
+ case '4':
+ family = PF_INET;
+ inet_flag++;
+ break;
+ case '6':
+#ifdef INET6
+ family = PF_INET6;
+ inet6_flag++;
+#else
+ errx(EX_USAGE, "lpd compiled sans INET6 (IPv6 support)");
+#endif
+ break;
+ /*
+ * The following options are not in FreeBSD (yet?), but are
+ * listed here to "reserve" them, because the option-letters
+ * are used by either NetBSD or OpenBSD (as of July 2001).
+ */
+ case 'b': /* set bind-addr */
+ case 'n': /* set max num of children */
+ case 'r': /* allow 'of' for remote ptrs */
+ /* ...[not needed in freebsd] */
+ /* FALLTHROUGH */
+ default:
+ errs++;
+ }
+ if (inet_flag && inet6_flag)
+ family = PF_UNSPEC;
+ argc -= optind;
+ argv += optind;
+ if (errs)
+ usage();
+
+ if (argc == 1) {
+ if ((i = atoi(argv[0])) == 0)
+ usage();
+ if (i < 0 || i > USHRT_MAX)
+ errx(EX_USAGE, "port # %d is invalid", i);
+
+ serv.s_port = htons(i);
+ sp = &serv;
+ argc--;
+ } else {
+ sp = getservbyname("printer", "tcp");
+ if (sp == NULL)
+ errx(EX_OSFILE, "printer/tcp: unknown service");
+ }
+
+ if (argc != 0)
+ usage();
+
+ /*
+ * We run chkprintcap right away to catch any errors and blat them
+ * to stderr while we still have it open, rather than sending them
+ * to syslog and leaving the user wondering why lpd started and
+ * then stopped. There should probably be a command-line flag to
+ * ignore errors from chkprintcap.
+ */
+ {
+ pid_t pid;
+ int status;
+ pid = fork();
+ if (pid < 0) {
+ err(EX_OSERR, "cannot fork");
+ } else if (pid == 0) { /* child */
+ execl(_PATH_CHKPRINTCAP, _PATH_CHKPRINTCAP, (char *)0);
+ err(EX_OSERR, "cannot execute %s", _PATH_CHKPRINTCAP);
+ }
+ if (waitpid(pid, &status, 0) < 0) {
+ err(EX_OSERR, "cannot wait");
+ }
+ if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
+ errx(EX_OSFILE, "%d errors in printcap file, exiting",
+ WEXITSTATUS(status));
+ }
+
+#ifndef DEBUG
+ /*
+ * Set up standard environment by detaching from the parent.
+ */
+ daemon(0, 0);
+#endif
+
+ openlog("lpd", LOG_PID, LOG_LPR);
+ syslog(LOG_INFO, "lpd startup: logging=%d%s%s", lflag,
+ socket_debug ? " dbg" : "", sflag ? " net-secure" : "");
+ (void) umask(0);
+ /*
+ * NB: This depends on O_NONBLOCK semantics doing the right thing;
+ * i.e., applying only to the O_EXLOCK and not to the rest of the
+ * open/creation. As of 1997-12-02, this is the case for commonly-
+ * used filesystems. There are other places in this code which
+ * make the same assumption.
+ */
+ lfd = open(_PATH_MASTERLOCK, O_WRONLY|O_CREAT|O_EXLOCK|O_NONBLOCK,
+ LOCK_FILE_MODE);
+ if (lfd < 0) {
+ if (errno == EWOULDBLOCK) /* active daemon present */
+ exit(0);
+ syslog(LOG_ERR, "%s: %m", _PATH_MASTERLOCK);
+ exit(1);
+ }
+ fcntl(lfd, F_SETFL, 0); /* turn off non-blocking mode */
+ ftruncate(lfd, 0);
+ /*
+ * write process id for others to know
+ */
+ sprintf(line, "%u\n", getpid());
+ f = strlen(line);
+ if (write(lfd, line, f) != f) {
+ syslog(LOG_ERR, "%s: %m", _PATH_MASTERLOCK);
+ exit(1);
+ }
+ signal(SIGCHLD, reapchild);
+ /*
+ * Restart all the printers.
+ */
+ startup();
+ (void) unlink(_PATH_SOCKETNAME);
+ funix = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (funix < 0) {
+ syslog(LOG_ERR, "socket: %m");
+ exit(1);
+ }
+
+ sigemptyset(&nmask);
+ sigaddset(&nmask, SIGHUP);
+ sigaddset(&nmask, SIGINT);
+ sigaddset(&nmask, SIGQUIT);
+ sigaddset(&nmask, SIGTERM);
+ sigprocmask(SIG_BLOCK, &nmask, &omask);
+
+ (void) umask(07);
+ signal(SIGHUP, mcleanup);
+ signal(SIGINT, mcleanup);
+ signal(SIGQUIT, mcleanup);
+ signal(SIGTERM, mcleanup);
+ memset(&un, 0, sizeof(un));
+ un.sun_family = AF_UNIX;
+ strcpy(un.sun_path, _PATH_SOCKETNAME);
+#ifndef SUN_LEN
+#define SUN_LEN(unp) (strlen((unp)->sun_path) + 2)
+#endif
+ if (bind(funix, (struct sockaddr *)&un, SUN_LEN(&un)) < 0) {
+ syslog(LOG_ERR, "ubind: %m");
+ exit(1);
+ }
+ (void) umask(0);
+ sigprocmask(SIG_SETMASK, &omask, (sigset_t *)0);
+ FD_ZERO(&defreadfds);
+ FD_SET(funix, &defreadfds);
+ listen(funix, 5);
+ if (sflag == 0) {
+ finet = socksetup(family, socket_debug);
+ } else
+ finet = NULL; /* pretend we couldn't open TCP socket. */
+ if (finet) {
+ for (i = 1; i <= *finet; i++) {
+ FD_SET(finet[i], &defreadfds);
+ listen(finet[i], 5);
+ }
+ }
+ /*
+ * Main loop: accept, do a request, continue.
+ */
+ memset(&frominet, 0, sizeof(frominet));
+ memset(&fromunix, 0, sizeof(fromunix));
+ if (lflag)
+ syslog(LOG_INFO, "lpd startup: ready to accept requests");
+ /*
+ * XXX - should be redone for multi-protocol
+ */
+ for (;;) {
+ int domain, nfds, s;
+ fd_set readfds;
+
+ FD_COPY(&defreadfds, &readfds);
+ nfds = select(20, &readfds, 0, 0, 0);
+ if (nfds <= 0) {
+ if (nfds < 0 && errno != EINTR)
+ syslog(LOG_WARNING, "select: %m");
+ continue;
+ }
+ domain = -1; /* avoid compile-time warning */
+ s = -1; /* avoid compile-time warning */
+ if (FD_ISSET(funix, &readfds)) {
+ domain = AF_UNIX, fromlen = sizeof(fromunix);
+ s = accept(funix,
+ (struct sockaddr *)&fromunix, &fromlen);
+ } else {
+ for (i = 1; i <= *finet; i++)
+ if (FD_ISSET(finet[i], &readfds)) {
+ domain = AF_INET;
+ fromlen = sizeof(frominet);
+ s = accept(finet[i],
+ (struct sockaddr *)&frominet,
+ &fromlen);
+ }
+ }
+ if (s < 0) {
+ if (errno != EINTR)
+ syslog(LOG_WARNING, "accept: %m");
+ continue;
+ }
+ if (fork() == 0) {
+ /*
+ * Note that printjob() also plays around with
+ * signal-handling routines, and may need to be
+ * changed when making changes to signal-handling.
+ */
+ signal(SIGCHLD, SIG_DFL);
+ signal(SIGHUP, SIG_IGN);
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGTERM, SIG_IGN);
+ (void) close(funix);
+ if (sflag == 0 && finet) {
+ for (i = 1; i <= *finet; i++)
+ (void)close(finet[i]);
+ }
+ dup2(s, 1);
+ (void) close(s);
+ if (domain == AF_INET) {
+ /* for both AF_INET and AF_INET6 */
+ from_remote = 1;
+ chkhost((struct sockaddr *)&frominet,
+ ch_options);
+ } else
+ from_remote = 0;
+ doit();
+ exit(0);
+ }
+ (void) close(s);
+ }
+}
+
+static void
+reapchild(int signo __unused)
+{
+ int status;
+
+ while (wait3(&status, WNOHANG, 0) > 0)
+ ;
+}
+
+static void
+mcleanup(int signo)
+{
+ /*
+ * XXX syslog(3) is not signal-safe.
+ */
+ if (lflag) {
+ if (signo)
+ syslog(LOG_INFO, "exiting on signal %d", signo);
+ else
+ syslog(LOG_INFO, "exiting");
+ }
+ unlink(_PATH_SOCKETNAME);
+ exit(0);
+}
+
+/*
+ * Stuff for handling job specifications
+ */
+char *user[MAXUSERS]; /* users to process */
+int users; /* # of users in user array */
+int requ[MAXREQUESTS]; /* job number of spool entries */
+int requests; /* # of spool requests */
+char *person; /* name of person doing lprm */
+
+ /* buffer to hold the client's machine-name */
+static char frombuf[MAXHOSTNAMELEN];
+char cbuf[BUFSIZ]; /* command line buffer */
+const char *cmdnames[] = {
+ "null",
+ "printjob",
+ "recvjob",
+ "displayq short",
+ "displayq long",
+ "rmjob"
+};
+
+static void
+doit(void)
+{
+ char *cp, *printer;
+ int n;
+ int status;
+ struct printer myprinter, *pp = &myprinter;
+
+ init_printer(&myprinter);
+
+ for (;;) {
+ cp = cbuf;
+ do {
+ if (cp >= &cbuf[sizeof(cbuf) - 1])
+ fatal(0, "Command line too long");
+ if ((n = read(STDOUT_FILENO, cp, 1)) != 1) {
+ if (n < 0)
+ fatal(0, "Lost connection");
+ return;
+ }
+ } while (*cp++ != '\n');
+ *--cp = '\0';
+ cp = cbuf;
+ if (lflag) {
+ if (*cp >= '\1' && *cp <= '\5')
+ syslog(LOG_INFO, "%s requests %s %s",
+ from_host, cmdnames[(u_char)*cp], cp+1);
+ else
+ syslog(LOG_INFO, "bad request (%d) from %s",
+ *cp, from_host);
+ }
+ switch (*cp++) {
+ case CMD_CHECK_QUE: /* check the queue, print any jobs there */
+ startprinting(cp);
+ break;
+ case CMD_TAKE_THIS: /* receive files to be queued */
+ if (!from_remote) {
+ syslog(LOG_INFO, "illegal request (%d)", *cp);
+ exit(1);
+ }
+ recvjob(cp);
+ break;
+ case CMD_SHOWQ_SHORT: /* display the queue (short form) */
+ case CMD_SHOWQ_LONG: /* display the queue (long form) */
+ /* XXX - this all needs to be redone. */
+ printer = cp;
+ while (*cp) {
+ if (*cp != ' ') {
+ cp++;
+ continue;
+ }
+ *cp++ = '\0';
+ while (isspace(*cp))
+ cp++;
+ if (*cp == '\0')
+ break;
+ if (isdigit(*cp)) {
+ if (requests >= MAXREQUESTS)
+ fatal(0, "Too many requests");
+ requ[requests++] = atoi(cp);
+ } else {
+ if (users >= MAXUSERS)
+ fatal(0, "Too many users");
+ user[users++] = cp;
+ }
+ }
+ status = getprintcap(printer, pp);
+ if (status < 0)
+ fatal(pp, "%s", pcaperr(status));
+ displayq(pp, cbuf[0] == CMD_SHOWQ_LONG);
+ exit(0);
+ case CMD_RMJOB: /* remove a job from the queue */
+ if (!from_remote) {
+ syslog(LOG_INFO, "illegal request (%d)", *cp);
+ exit(1);
+ }
+ printer = cp;
+ while (*cp && *cp != ' ')
+ cp++;
+ if (!*cp)
+ break;
+ *cp++ = '\0';
+ person = cp;
+ while (*cp) {
+ if (*cp != ' ') {
+ cp++;
+ continue;
+ }
+ *cp++ = '\0';
+ while (isspace(*cp))
+ cp++;
+ if (*cp == '\0')
+ break;
+ if (isdigit(*cp)) {
+ if (requests >= MAXREQUESTS)
+ fatal(0, "Too many requests");
+ requ[requests++] = atoi(cp);
+ } else {
+ if (users >= MAXUSERS)
+ fatal(0, "Too many users");
+ user[users++] = cp;
+ }
+ }
+ rmjob(printer);
+ break;
+ }
+ fatal(0, "Illegal service request");
+ }
+}
+
+/*
+ * Make a pass through the printcap database and start printing any
+ * files left from the last time the machine went down.
+ */
+static void
+startup(void)
+{
+ int pid, status, more;
+ struct printer myprinter, *pp = &myprinter;
+
+ more = firstprinter(pp, &status);
+ if (status)
+ goto errloop;
+ while (more) {
+ if (ckqueue(pp) <= 0) {
+ goto next;
+ }
+ if (lflag)
+ syslog(LOG_INFO, "lpd startup: work for %s",
+ pp->printer);
+ if ((pid = fork()) < 0) {
+ syslog(LOG_WARNING, "lpd startup: cannot fork for %s",
+ pp->printer);
+ mcleanup(0);
+ }
+ if (pid == 0) {
+ lastprinter();
+ printjob(pp);
+ /* NOTREACHED */
+ }
+ do {
+next:
+ more = nextprinter(pp, &status);
+errloop:
+ if (status)
+ syslog(LOG_WARNING,
+ "lpd startup: printcap entry for %s has errors, skipping",
+ pp->printer ? pp->printer : "<noname?>");
+ } while (more && status);
+ }
+}
+
+/*
+ * Make sure there's some work to do before forking off a child
+ */
+static int
+ckqueue(struct printer *pp)
+{
+ register struct dirent *d;
+ DIR *dirp;
+ char *spooldir;
+
+ spooldir = pp->spool_dir;
+ if ((dirp = opendir(spooldir)) == NULL)
+ return (-1);
+ while ((d = readdir(dirp)) != NULL) {
+ if (d->d_name[0] != 'c' || d->d_name[1] != 'f')
+ continue; /* daemon control files only */
+ closedir(dirp);
+ return (1); /* found something */
+ }
+ closedir(dirp);
+ return (0);
+}
+
+#define DUMMY ":nobody::"
+
+/*
+ * Check to see if the host connecting to this host has access to any
+ * lpd services on this host.
+ */
+static void
+chkhost(struct sockaddr *f, int ch_opts)
+{
+ struct addrinfo hints, *res, *r;
+ register FILE *hostf;
+ char hostbuf[NI_MAXHOST], ip[NI_MAXHOST];
+ char serv[NI_MAXSERV];
+ char *syserr, *usererr;
+ int error, errsav, fpass, good, wantsl;
+
+ wantsl = 0;
+ if (ch_opts & LPD_LOGCONNERR)
+ wantsl = 1; /* also syslog the errors */
+
+ from_host = ".na.";
+
+ /* Need real hostname for temporary filenames */
+ error = getnameinfo(f, f->sa_len, hostbuf, sizeof(hostbuf), NULL, 0,
+ NI_NAMEREQD);
+ if (error) {
+ errsav = error;
+ error = getnameinfo(f, f->sa_len, hostbuf, sizeof(hostbuf),
+ NULL, 0, NI_NUMERICHOST | NI_WITHSCOPEID);
+ if (error) {
+ asprintf(&syserr,
+ "can not determine hostname for remote host (%d,%d)",
+ errsav, error);
+ asprintf(&usererr,
+ "Host name for your address is not known");
+ fhosterr(ch_opts, syserr, usererr);
+ /* NOTREACHED */
+ }
+ asprintf(&syserr,
+ "Host name for remote host (%s) not known (%d)",
+ hostbuf, errsav);
+ asprintf(&usererr,
+ "Host name for your address (%s) is not known",
+ hostbuf);
+ fhosterr(ch_opts, syserr, usererr);
+ /* NOTREACHED */
+ }
+
+ strlcpy(frombuf, hostbuf, sizeof(frombuf));
+ from_host = frombuf;
+ ch_opts |= LPD_ADDFROMLINE;
+
+ /* Need address in stringform for comparison (no DNS lookup here) */
+ error = getnameinfo(f, f->sa_len, hostbuf, sizeof(hostbuf), NULL, 0,
+ NI_NUMERICHOST | NI_WITHSCOPEID);
+ if (error) {
+ asprintf(&syserr, "Cannot print IP address (error %d)",
+ error);
+ asprintf(&usererr, "Cannot print IP address for your host");
+ fhosterr(ch_opts, syserr, usererr);
+ /* NOTREACHED */
+ }
+ from_ip = strdup(hostbuf);
+
+ /* Reject numeric addresses */
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = family;
+ hints.ai_socktype = SOCK_DGRAM; /*dummy*/
+ hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
+ if (getaddrinfo(from_host, NULL, &hints, &res) == 0) {
+ freeaddrinfo(res);
+ /* This syslog message already includes from_host */
+ ch_opts &= ~LPD_ADDFROMLINE;
+ asprintf(&syserr, "reverse lookup results in non-FQDN %s",
+ from_host);
+ /* same message to both syslog and remote user */
+ fhosterr(ch_opts, syserr, syserr);
+ /* NOTREACHED */
+ }
+
+ /* Check for spoof, ala rlogind */
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = family;
+ hints.ai_socktype = SOCK_DGRAM; /*dummy*/
+ error = getaddrinfo(from_host, NULL, &hints, &res);
+ if (error) {
+ asprintf(&syserr, "dns lookup for address %s failed: %s",
+ from_ip, gai_strerror(error));
+ asprintf(&usererr, "hostname for your address (%s) unknown: %s",
+ from_ip, gai_strerror(error));
+ fhosterr(ch_opts, syserr, usererr);
+ /* NOTREACHED */
+ }
+ good = 0;
+ for (r = res; good == 0 && r; r = r->ai_next) {
+ error = getnameinfo(r->ai_addr, r->ai_addrlen, ip, sizeof(ip),
+ NULL, 0, NI_NUMERICHOST | NI_WITHSCOPEID);
+ if (!error && !strcmp(from_ip, ip))
+ good = 1;
+ }
+ if (res)
+ freeaddrinfo(res);
+ if (good == 0) {
+ asprintf(&syserr, "address for remote host (%s) not matched",
+ from_ip);
+ asprintf(&usererr,
+ "address for your hostname (%s) not matched", from_ip);
+ fhosterr(ch_opts, syserr, usererr);
+ /* NOTREACHED */
+ }
+
+ fpass = 1;
+ hostf = fopen(_PATH_HOSTSEQUIV, "r");
+again:
+ if (hostf) {
+ if (__ivaliduser_sa(hostf, f, f->sa_len, DUMMY, DUMMY) == 0) {
+ (void) fclose(hostf);
+ goto foundhost;
+ }
+ (void) fclose(hostf);
+ }
+ if (fpass == 1) {
+ fpass = 2;
+ hostf = fopen(_PATH_HOSTSLPD, "r");
+ goto again;
+ }
+ /* This syslog message already includes from_host */
+ ch_opts &= ~LPD_ADDFROMLINE;
+ asprintf(&syserr, "refused connection from %s, sip=%s", from_host,
+ from_ip);
+ asprintf(&usererr,
+ "Print-services are not available to your host (%s).", from_host);
+ fhosterr(ch_opts, syserr, usererr);
+ /* NOTREACHED */
+
+foundhost:
+ if (ch_opts & LPD_NOPORTCHK)
+ return; /* skip the reserved-port check */
+
+ error = getnameinfo(f, f->sa_len, NULL, 0, serv, sizeof(serv),
+ NI_NUMERICSERV);
+ if (error) {
+ /* same message to both syslog and remote user */
+ asprintf(&syserr, "malformed from-address (%d)", error);
+ fhosterr(ch_opts, syserr, syserr);
+ /* NOTREACHED */
+ }
+
+ if (atoi(serv) >= IPPORT_RESERVED) {
+ /* same message to both syslog and remote user */
+ asprintf(&syserr, "connected from invalid port (%s)", serv);
+ fhosterr(ch_opts, syserr, syserr);
+ /* NOTREACHED */
+ }
+}
+
+/*
+ * Handle fatal errors in chkhost. The first message will optionally be
+ * sent to syslog, the second one is sent to the connecting host.
+ *
+ * The idea is that the syslog message is meant for an administrator of a
+ * print server (the host receiving connections), while the usermsg is meant
+ * for a remote user who may or may not be clueful, and may or may not be
+ * doing something nefarious. Some remote users (eg, MS-Windows...) may not
+ * even see whatever message is sent, which is why there's the option to
+ * start 'lpd' with the connection-errors also sent to syslog.
+ *
+ * Given that hostnames can theoretically be fairly long (well, over 250
+ * bytes), it would probably be helpful to have the 'from_host' field at
+ * the end of any error messages which include that info.
+ *
+ * These are Fatal host-connection errors, so this routine does not return.
+ */
+static void
+fhosterr(int ch_opts, char *sysmsg, char *usermsg)
+{
+
+ /*
+ * If lpd was started up to print connection errors, then write
+ * the syslog message before the user message.
+ * And for many of the syslog messages, it is helpful to first
+ * write the from_host (if it is known) as a separate syslog
+ * message, since the hostname may be so long.
+ */
+ if (ch_opts & LPD_LOGCONNERR) {
+ if (ch_opts & LPD_ADDFROMLINE) {
+ syslog(LOG_WARNING, "for connection from %s:", from_host);
+ }
+ syslog(LOG_WARNING, "%s", sysmsg);
+ }
+
+ /*
+ * Now send the error message to the remote host which is trying
+ * to make the connection.
+ */
+ printf("%s [@%s]: %s\n", progname, local_host, usermsg);
+ fflush(stdout);
+
+ /*
+ * Add a minimal delay before exiting (and disconnecting from the
+ * sending-host). This is just in case that machine responds by
+ * INSTANTLY retrying (and instantly re-failing...). This may also
+ * give the other side more time to read the error message.
+ */
+ sleep(2); /* a paranoid throttling measure */
+ exit(1);
+}
+
+/* setup server socket for specified address family */
+/* if af is PF_UNSPEC more than one socket may be returned */
+/* the returned list is dynamically allocated, so caller needs to free it */
+static int *
+socksetup(int af, int debuglvl)
+{
+ struct addrinfo hints, *res, *r;
+ int error, maxs, *s, *socks;
+ const int on = 1;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = af;
+ hints.ai_socktype = SOCK_STREAM;
+ error = getaddrinfo(NULL, "printer", &hints, &res);
+ if (error) {
+ syslog(LOG_ERR, "%s", gai_strerror(error));
+ mcleanup(0);
+ }
+
+ /* Count max number of sockets we may open */
+ for (maxs = 0, r = res; r; r = r->ai_next, maxs++)
+ ;
+ socks = malloc((maxs + 1) * sizeof(int));
+ if (!socks) {
+ syslog(LOG_ERR, "couldn't allocate memory for sockets");
+ mcleanup(0);
+ }
+
+ *socks = 0; /* num of sockets counter at start of array */
+ s = socks + 1;
+ for (r = res; r; r = r->ai_next) {
+ *s = socket(r->ai_family, r->ai_socktype, r->ai_protocol);
+ if (*s < 0) {
+ syslog(LOG_DEBUG, "socket(): %m");
+ continue;
+ }
+ if (setsockopt(*s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))
+ < 0) {
+ syslog(LOG_ERR, "setsockopt(SO_REUSEADDR): %m");
+ close(*s);
+ continue;
+ }
+ if (debuglvl)
+ if (setsockopt(*s, SOL_SOCKET, SO_DEBUG, &debuglvl,
+ sizeof(debuglvl)) < 0) {
+ syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
+ close(*s);
+ continue;
+ }
+#ifdef IPV6_BINDV6ONLY
+ if (r->ai_family == AF_INET6) {
+ if (setsockopt(*s, IPPROTO_IPV6, IPV6_BINDV6ONLY,
+ &on, sizeof(on)) < 0) {
+ syslog(LOG_ERR,
+ "setsockopt (IPV6_BINDV6ONLY): %m");
+ close(*s);
+ continue;
+ }
+ }
+#endif
+ if (bind(*s, r->ai_addr, r->ai_addrlen) < 0) {
+ syslog(LOG_DEBUG, "bind(): %m");
+ close(*s);
+ continue;
+ }
+ (*socks)++;
+ s++;
+ }
+
+ if (res)
+ freeaddrinfo(res);
+
+ if (*socks == 0) {
+ syslog(LOG_ERR, "Couldn't bind to any socket");
+ free(socks);
+ mcleanup(0);
+ }
+ return(socks);
+}
+
+static void
+usage(void)
+{
+#ifdef INET6
+ fprintf(stderr, "usage: lpd [-cdlsW46] [port#]\n");
+#else
+ fprintf(stderr, "usage: lpd [-cdlsW] [port#]\n");
+#endif
+ exit(EX_USAGE);
+}
diff --git a/usr.sbin/lpr/lpd/lpdchar.c b/usr.sbin/lpr/lpd/lpdchar.c
new file mode 100644
index 0000000..ba39a9f
--- /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[] =
+ "$FreeBSD$";
+#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..2abac5a
--- /dev/null
+++ b/usr.sbin/lpr/lpd/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[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <stddef.h>
+#include <string.h>
+#include <termios.h>
+#include "lp.local.h"
+#include "extern.h"
+
+struct modes {
+ const char *name;
+ const long set;
+ const long unset;
+};
+
+/*
+ * The code in optlist() depends on minus options following regular
+ * options, i.e. "foo" must immediately precede "-foo".
+ */
+struct modes cmodes[] = {
+ { "cs5", CS5, CSIZE },
+ { "cs6", CS6, CSIZE },
+ { "cs7", CS7, CSIZE },
+ { "cs8", CS8, CSIZE },
+ { "cstopb", CSTOPB, 0 },
+ { "-cstopb", 0, CSTOPB },
+ { "cread", CREAD, 0 },
+ { "-cread", 0, CREAD },
+ { "parenb", PARENB, 0 },
+ { "-parenb", 0, PARENB },
+ { "parodd", PARODD, 0 },
+ { "-parodd", 0, PARODD },
+ { "parity", PARENB | CS7, PARODD | CSIZE },
+ { "-parity", CS8, PARODD | PARENB | CSIZE },
+ { "evenp", PARENB | CS7, PARODD | CSIZE },
+ { "-evenp", CS8, PARODD | PARENB | CSIZE },
+ { "oddp", PARENB | CS7 | PARODD, CSIZE },
+ { "-oddp", CS8, PARODD | PARENB | CSIZE },
+ { "pass8", CS8, PARODD | PARENB | CSIZE },
+ { "-pass8", PARENB | CS7, PARODD | CSIZE },
+ { "hupcl", HUPCL, 0 },
+ { "-hupcl", 0, HUPCL },
+ { "hup", HUPCL, 0 },
+ { "-hup", 0, HUPCL },
+ { "clocal", CLOCAL, 0 },
+ { "-clocal", 0, CLOCAL },
+ { "crtscts", CRTSCTS, 0 },
+ { "-crtscts", 0, CRTSCTS },
+ { "ctsflow", CCTS_OFLOW, 0 },
+ { "-ctsflow", 0, CCTS_OFLOW },
+ { "dsrflow", CDSR_OFLOW, 0 },
+ { "-dsrflow", 0, CDSR_OFLOW },
+ { "dtrflow", CDTR_IFLOW, 0 },
+ { "-dtrflow", 0, CDTR_IFLOW },
+ { "rtsflow", CRTS_IFLOW, 0 },
+ { "-rtsflow", 0, CRTS_IFLOW },
+ { "mdmbuf", MDMBUF, 0 },
+ { "-mdmbuf", 0, MDMBUF },
+ { NULL, 0, 0},
+};
+
+struct modes imodes[] = {
+ { "ignbrk", IGNBRK, 0 },
+ { "-ignbrk", 0, IGNBRK },
+ { "brkint", BRKINT, 0 },
+ { "-brkint", 0, BRKINT },
+ { "ignpar", IGNPAR, 0 },
+ { "-ignpar", 0, IGNPAR },
+ { "parmrk", PARMRK, 0 },
+ { "-parmrk", 0, PARMRK },
+ { "inpck", INPCK, 0 },
+ { "-inpck", 0, INPCK },
+ { "istrip", ISTRIP, 0 },
+ { "-istrip", 0, ISTRIP },
+ { "inlcr", INLCR, 0 },
+ { "-inlcr", 0, INLCR },
+ { "igncr", IGNCR, 0 },
+ { "-igncr", 0, IGNCR },
+ { "icrnl", ICRNL, 0 },
+ { "-icrnl", 0, ICRNL },
+ { "ixon", IXON, 0 },
+ { "-ixon", 0, IXON },
+ { "flow", IXON, 0 },
+ { "-flow", 0, IXON },
+ { "ixoff", IXOFF, 0 },
+ { "-ixoff", 0, IXOFF },
+ { "tandem", IXOFF, 0 },
+ { "-tandem", 0, IXOFF },
+ { "ixany", IXANY, 0 },
+ { "-ixany", 0, IXANY },
+ { "decctlq", 0, IXANY },
+ { "-decctlq", IXANY, 0 },
+ { "imaxbel", IMAXBEL, 0 },
+ { "-imaxbel", 0, IMAXBEL },
+ { NULL, 0, 0},
+};
+
+struct modes lmodes[] = {
+ { "echo", ECHO, 0 },
+ { "-echo", 0, ECHO },
+ { "echoe", ECHOE, 0 },
+ { "-echoe", 0, ECHOE },
+ { "crterase", ECHOE, 0 },
+ { "-crterase", 0, ECHOE },
+ { "crtbs", ECHOE, 0 }, /* crtbs not supported, close enough */
+ { "-crtbs", 0, ECHOE },
+ { "echok", ECHOK, 0 },
+ { "-echok", 0, ECHOK },
+ { "echoke", ECHOKE, 0 },
+ { "-echoke", 0, ECHOKE },
+ { "crtkill", ECHOKE, 0 },
+ { "-crtkill", 0, ECHOKE },
+ { "altwerase", ALTWERASE, 0 },
+ { "-altwerase", 0, ALTWERASE },
+ { "iexten", IEXTEN, 0 },
+ { "-iexten", 0, IEXTEN },
+ { "echonl", ECHONL, 0 },
+ { "-echonl", 0, ECHONL },
+ { "echoctl", ECHOCTL, 0 },
+ { "-echoctl", 0, ECHOCTL },
+ { "ctlecho", ECHOCTL, 0 },
+ { "-ctlecho", 0, ECHOCTL },
+ { "echoprt", ECHOPRT, 0 },
+ { "-echoprt", 0, ECHOPRT },
+ { "prterase", ECHOPRT, 0 },
+ { "-prterase", 0, ECHOPRT },
+ { "isig", ISIG, 0 },
+ { "-isig", 0, ISIG },
+ { "icanon", ICANON, 0 },
+ { "-icanon", 0, ICANON },
+ { "noflsh", NOFLSH, 0 },
+ { "-noflsh", 0, NOFLSH },
+ { "tostop", TOSTOP, 0 },
+ { "-tostop", 0, TOSTOP },
+ { "flusho", FLUSHO, 0 },
+ { "-flusho", 0, FLUSHO },
+ { "pendin", PENDIN, 0 },
+ { "-pendin", 0, PENDIN },
+ { "crt", ECHOE|ECHOKE|ECHOCTL, ECHOK|ECHOPRT },
+ { "-crt", ECHOK, ECHOE|ECHOKE|ECHOCTL },
+ { "newcrt", ECHOE|ECHOKE|ECHOCTL, ECHOK|ECHOPRT },
+ { "-newcrt", ECHOK, ECHOE|ECHOKE|ECHOCTL },
+ { "nokerninfo", NOKERNINFO, 0 },
+ { "-nokerninfo",0, NOKERNINFO },
+ { "kerninfo", 0, NOKERNINFO },
+ { "-kerninfo", NOKERNINFO, 0 },
+ { NULL, 0, 0},
+};
+
+struct modes omodes[] = {
+ { "opost", OPOST, 0 },
+ { "-opost", 0, OPOST },
+ { "litout", 0, OPOST },
+ { "-litout", OPOST, 0 },
+ { "onlcr", ONLCR, 0 },
+ { "-onlcr", 0, ONLCR },
+ { "tabs", 0, OXTABS }, /* "preserve" tabs */
+ { "-tabs", OXTABS, 0 },
+ { "oxtabs", OXTABS, 0 },
+ { "-oxtabs", 0, OXTABS },
+ { NULL, 0, 0},
+};
+
+#define CHK(name, s) (*name == s[0] && !strcmp(name, s))
+
+int
+msearch(char *str, struct termios *ip)
+{
+ struct modes *mp;
+
+ for (mp = cmodes; mp->name; ++mp)
+ if (CHK(str, mp->name)) {
+ ip->c_cflag &= ~mp->unset;
+ ip->c_cflag |= mp->set;
+ return (1);
+ }
+ for (mp = imodes; mp->name; ++mp)
+ if (CHK(str, mp->name)) {
+ ip->c_iflag &= ~mp->unset;
+ ip->c_iflag |= mp->set;
+ return (1);
+ }
+ for (mp = lmodes; mp->name; ++mp)
+ if (CHK(str, mp->name)) {
+ ip->c_lflag &= ~mp->unset;
+ ip->c_lflag |= mp->set;
+ return (1);
+ }
+ for (mp = omodes; mp->name; ++mp)
+ if (CHK(str, mp->name)) {
+ ip->c_oflag &= ~mp->unset;
+ ip->c_oflag |= mp->set;
+ return (1);
+ }
+ return (0);
+}
diff --git a/usr.sbin/lpr/lpd/printjob.c b/usr.sbin/lpr/lpd/printjob.c
new file mode 100644
index 0000000..feab6bb
--- /dev/null
+++ b/usr.sbin/lpr/lpd/printjob.c
@@ -0,0 +1,1892 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#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 /* dofork should return "can't fork" error */
+#define DOABORT 1 /* dofork should just die if fork() 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 pid_t of_pid; /* process id of output filter, if any */
+static int child; /* id of any filters */
+static int job_dfcnt; /* count of datafiles in current user job */
+static int lfd; /* lock file descriptor */
+static int ofd; /* output filter file descriptor */
+static int tfd = -1; /* output filter temp file output */
+static int pfd; /* prstatic inter file descriptor */
+static int prchild; /* id of pr process */
+static char title[80]; /* ``pr'' title */
+static char locale[80]; /* ``pr'' locale */
+
+/* these two are set from pp->daemon_user, but only if they are needed */
+static char *daemon_uname; /* set from pwd->pw_name */
+static int daemon_defgid;
+
+static char class[32]; /* classification field */
+static char origin_host[MAXHOSTNAMELEN]; /* user's host machine */
+ /* indentation size in static characters */
+static char indent[10] = "-i0";
+static char jobname[100]; /* job or file name */
+static char length[10] = "-l"; /* page length in lines */
+static char logname[32]; /* user's login name */
+static char pxlength[10] = "-y"; /* page length in pixels */
+static char pxwidth[10] = "-x"; /* page width in pixels */
+/* tempstderr is the filename used to catch stderr from exec-ing filters */
+static char tempstderr[] = "errs.XXXXXXX";
+static char width[10] = "-w"; /* page width in static characters */
+#define TFILENAME "fltXXXXXX"
+static char tfile[] = TFILENAME; /* file name for filter output */
+
+static void abortpr(int _signo);
+static void alarmhandler(int _signo);
+static void banner(struct printer *_pp, char *_name1, char *_name2);
+static int dofork(const struct printer *_pp, int _action);
+static int dropit(int _c);
+static int execfilter(struct printer *_pp, char *_f_cmd, char **_f_av,
+ int _infd, int _outfd);
+static void init(struct printer *_pp);
+static void openpr(const struct printer *_pp);
+static void opennet(const struct printer *_pp);
+static void opentty(const struct printer *_pp);
+static void openrem(const struct printer *pp);
+static int print(struct printer *_pp, int _format, char *_file);
+static int printit(struct printer *_pp, char *_file);
+static void pstatus(const struct printer *_pp, const char *_msg, ...)
+ __printflike(2, 3);
+static char response(const struct printer *_pp);
+static void scan_out(struct printer *_pp, int _scfd, char *_scsp,
+ int _dlm);
+static char *scnline(int _key, char *_p, int _c);
+static int sendfile(struct printer *_pp, int _type, char *_file,
+ char _format, int _copyreq);
+static int sendit(struct printer *_pp, char *_file);
+static void sendmail(struct printer *_pp, char *_userid, int _bombed);
+static void setty(const struct printer *_pp);
+
+void
+printjob(struct printer *pp)
+{
+ struct stat stb;
+ register struct jobqueue *q, **qp;
+ struct jobqueue **queue;
+ register int i, nitems;
+ off_t pidoff;
+ pid_t printpid;
+ int errcnt, jobcount, tempfd;
+
+ jobcount = 0;
+ init(pp); /* set up capabilities */
+ (void) write(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: open(%s): %m", pp->printer,
+ pp->log_file);
+ (void) open(_PATH_DEVNULL, O_WRONLY);
+ }
+ setgid(getegid());
+ printpid = getpid(); /* for use with lprm */
+ setpgrp(0, printpid);
+
+ /*
+ * At initial lpd startup, printjob may be called with various
+ * signal handlers in effect. After that initial startup, any
+ * calls to printjob will have a *different* set of signal-handlers
+ * in effect. Make sure all handlers are the ones we want.
+ */
+ signal(SIGCHLD, SIG_DFL);
+ signal(SIGHUP, abortpr);
+ signal(SIGINT, abortpr);
+ signal(SIGQUIT, abortpr);
+ signal(SIGTERM, abortpr);
+
+ /*
+ * uses short form file names
+ */
+ if (chdir(pp->spool_dir) < 0) {
+ syslog(LOG_ERR, "%s: chdir(%s): %m", pp->printer,
+ pp->spool_dir);
+ exit(1);
+ }
+ if (stat(pp->lock_file, &stb) == 0 && (stb.st_mode & LFM_PRINT_DIS))
+ exit(0); /* printing disabled */
+ lfd = open(pp->lock_file, O_WRONLY|O_CREAT|O_EXLOCK|O_NONBLOCK,
+ LOCK_FILE_MODE);
+ if (lfd < 0) {
+ if (errno == EWOULDBLOCK) /* active daemon present */
+ exit(0);
+ syslog(LOG_ERR, "%s: open(%s): %m", pp->printer,
+ pp->lock_file);
+ exit(1);
+ }
+ /* turn off non-blocking mode (was turned on for lock effects only) */
+ if (fcntl(lfd, F_SETFL, 0) < 0) {
+ syslog(LOG_ERR, "%s: fcntl(%s): %m", pp->printer,
+ pp->lock_file);
+ exit(1);
+ }
+ ftruncate(lfd, 0);
+ /*
+ * write process id for others to know
+ */
+ sprintf(line, "%u\n", printpid);
+ pidoff = i = strlen(line);
+ if (write(lfd, line, i) != i) {
+ syslog(LOG_ERR, "%s: write(%s): %m", pp->printer,
+ pp->lock_file);
+ exit(1);
+ }
+ /*
+ * search the spool directory for work and sort by queue order.
+ */
+ if ((nitems = getq(pp, &queue)) < 0) {
+ syslog(LOG_ERR, "%s: can't scan %s", pp->printer,
+ pp->spool_dir);
+ exit(1);
+ }
+ if (nitems == 0) /* no work to do */
+ exit(0);
+ if (stb.st_mode & LFM_RESET_QUE) { /* reset queue flag */
+ if (fchmod(lfd, stb.st_mode & ~LFM_RESET_QUE) < 0)
+ syslog(LOG_ERR, "%s: fchmod(%s): %m", pp->printer,
+ pp->lock_file);
+ }
+
+ /* create a file which will be used to hold stderr from filters */
+ if ((tempfd = mkstemp(tempstderr)) == -1) {
+ syslog(LOG_ERR, "%s: mkstemp(%s): %m", pp->printer,
+ tempstderr);
+ exit(1);
+ }
+ if ((i = fchmod(tempfd, 0664)) == -1) {
+ syslog(LOG_ERR, "%s: fchmod(%s): %m", pp->printer,
+ tempstderr);
+ exit(1);
+ }
+ /* lpd doesn't need it to be open, it just needs it to exist */
+ close(tempfd);
+
+ openpr(pp); /* open printer or remote */
+again:
+ /*
+ * we found something to do now do it --
+ * write the name of the current control file into the lock file
+ * so the spool queue program can tell what we're working on
+ */
+ for (qp = queue; nitems--; free((char *) q)) {
+ q = *qp++;
+ if (stat(q->job_cfname, &stb) < 0)
+ continue;
+ errcnt = 0;
+ restart:
+ (void) lseek(lfd, pidoff, 0);
+ (void) snprintf(line, sizeof(line), "%s\n", q->job_cfname);
+ i = strlen(line);
+ if (write(lfd, line, i) != i)
+ syslog(LOG_ERR, "%s: write(%s): %m", pp->printer,
+ pp->lock_file);
+ if (!pp->remote)
+ i = printit(pp, q->job_cfname);
+ else
+ i = sendit(pp, q->job_cfname);
+ /*
+ * Check to see if we are supposed to stop printing or
+ * if we are to rebuild the queue.
+ */
+ if (fstat(lfd, &stb) == 0) {
+ /* stop printing before starting next job? */
+ if (stb.st_mode & LFM_PRINT_DIS)
+ goto done;
+ /* rebuild queue (after lpc topq) */
+ if (stb.st_mode & LFM_RESET_QUE) {
+ for (free(q); nitems--; free(q))
+ q = *qp++;
+ if (fchmod(lfd, stb.st_mode & ~LFM_RESET_QUE)
+ < 0)
+ syslog(LOG_WARNING,
+ "%s: fchmod(%s): %m",
+ pp->printer, pp->lock_file);
+ break;
+ }
+ }
+ if (i == OK) /* all files of this job printed */
+ jobcount++;
+ else if (i == REPRINT && ++errcnt < 5) {
+ /* try reprinting the job */
+ syslog(LOG_INFO, "restarting %s", pp->printer);
+ if (of_pid > 0) {
+ kill(of_pid, SIGCONT); /* to be sure */
+ (void) close(ofd);
+ while ((i = wait(NULL)) > 0 && i != of_pid)
+ ;
+ if (i < 0)
+ syslog(LOG_WARNING, "%s: after kill(of=%d), wait() returned: %m",
+ pp->printer, of_pid);
+ of_pid = 0;
+ }
+ (void) close(pfd); /* close printer */
+ if (ftruncate(lfd, pidoff) < 0)
+ syslog(LOG_WARNING, "%s: ftruncate(%s): %m",
+ pp->printer, pp->lock_file);
+ openpr(pp); /* try to reopen printer */
+ goto restart;
+ } else {
+ syslog(LOG_WARNING, "%s: job could not be %s (%s)",
+ pp->printer,
+ pp->remote ? "sent to remote host" : "printed",
+ q->job_cfname);
+ if (i == REPRINT) {
+ /* ensure we don't attempt this job again */
+ (void) unlink(q->job_cfname);
+ q->job_cfname[0] = 'd';
+ (void) unlink(q->job_cfname);
+ if (logname[0])
+ sendmail(pp, logname, FATALERR);
+ }
+ }
+ }
+ free(queue);
+ /*
+ * search the spool directory for more work.
+ */
+ if ((nitems = getq(pp, &queue)) < 0) {
+ syslog(LOG_ERR, "%s: can't scan %s", pp->printer,
+ pp->spool_dir);
+ exit(1);
+ }
+ if (nitems == 0) { /* no more work to do */
+ done:
+ if (jobcount > 0) { /* jobs actually printed */
+ if (!pp->no_formfeed && !pp->tof)
+ (void) write(ofd, pp->form_feed,
+ strlen(pp->form_feed));
+ if (pp->trailer != NULL) /* output trailer */
+ (void) write(ofd, pp->trailer,
+ strlen(pp->trailer));
+ }
+ (void) close(ofd);
+ (void) wait(NULL);
+ (void) unlink(tempstderr);
+ exit(0);
+ }
+ goto again;
+}
+
+char fonts[4][50]; /* fonts for troff */
+
+char ifonts[4][40] = {
+ _PATH_VFONTR,
+ _PATH_VFONTI,
+ _PATH_VFONTB,
+ _PATH_VFONTS,
+};
+
+/*
+ * The remaining part is the reading of the control file (cf)
+ * and performing the various actions.
+ */
+static int
+printit(struct printer *pp, char *file)
+{
+ register int i;
+ char *cp;
+ int bombed, didignorehdr;
+
+ bombed = OK;
+ didignorehdr = 0;
+ /*
+ * open control file; ignore if no longer there.
+ */
+ if ((cfp = fopen(file, "r")) == NULL) {
+ syslog(LOG_INFO, "%s: fopen(%s): %m", pp->printer, file);
+ return (OK);
+ }
+ /*
+ * Reset troff fonts.
+ */
+ for (i = 0; i < 4; i++)
+ strcpy(fonts[i], ifonts[i]);
+ sprintf(&width[2], "%ld", pp->page_width);
+ strcpy(indent+2, "0");
+
+ /* initialize job-specific count of datafiles processed */
+ job_dfcnt = 0;
+
+ /*
+ * read the control file for work to do
+ *
+ * file format -- first character in the line is a command
+ * rest of the line is the argument.
+ * valid commands are:
+ *
+ * S -- "stat info" for symbolic link protection
+ * J -- "job name" on banner page
+ * C -- "class name" on banner page
+ * L -- "literal" user's name to print on banner
+ * T -- "title" for pr
+ * H -- "host name" of machine where lpr was done
+ * P -- "person" user's login name
+ * I -- "indent" amount to indent output
+ * R -- laser dpi "resolution"
+ * f -- "file name" name of text file to print
+ * l -- "file name" text file with control chars
+ * o -- "file name" postscript file, according to
+ * the RFC. Here it is treated like an 'f'.
+ * p -- "file name" text file to print with pr(1)
+ * t -- "file name" troff(1) file to print
+ * n -- "file name" ditroff(1) file to print
+ * d -- "file name" dvi file to print
+ * g -- "file name" plot(1G) file to print
+ * v -- "file name" plain raster file to print
+ * c -- "file name" cifplot file to print
+ * 1 -- "R font file" for troff
+ * 2 -- "I font file" for troff
+ * 3 -- "B font file" for troff
+ * 4 -- "S font file" for troff
+ * N -- "name" of file (used by lpq)
+ * U -- "unlink" name of file to remove
+ * (after we print it. (Pass 2 only)).
+ * M -- "mail" to user when done printing
+ * Z -- "locale" for pr
+ *
+ * getline reads a line and expands tabs to blanks
+ */
+
+ /* pass 1 */
+
+ while (getline(cfp))
+ switch (line[0]) {
+ case 'H':
+ strlcpy(origin_host, line + 1, sizeof(origin_host));
+ if (class[0] == '\0') {
+ strlcpy(class, line+1, sizeof(class));
+ }
+ continue;
+
+ case 'P':
+ strlcpy(logname, line + 1, sizeof(logname));
+ if (pp->restricted) { /* restricted */
+ if (getpwnam(logname) == NULL) {
+ bombed = NOACCT;
+ sendmail(pp, line+1, bombed);
+ goto pass2;
+ }
+ }
+ continue;
+
+ case 'S':
+ cp = line+1;
+ i = 0;
+ while (*cp >= '0' && *cp <= '9')
+ i = i * 10 + (*cp++ - '0');
+ fdev = i;
+ cp++;
+ i = 0;
+ while (*cp >= '0' && *cp <= '9')
+ i = i * 10 + (*cp++ - '0');
+ fino = i;
+ continue;
+
+ case 'J':
+ if (line[1] != '\0') {
+ strlcpy(jobname, line + 1, sizeof(jobname));
+ } else
+ strcpy(jobname, " ");
+ continue;
+
+ case 'C':
+ if (line[1] != '\0')
+ strlcpy(class, line + 1, sizeof(class));
+ else if (class[0] == '\0') {
+ /* XXX - why call gethostname instead of
+ * just strlcpy'ing local_host? */
+ gethostname(class, sizeof(class));
+ class[sizeof(class) - 1] = '\0';
+ }
+ continue;
+
+ case 'T': /* header title for pr */
+ strlcpy(title, line + 1, sizeof(title));
+ continue;
+
+ case 'L': /* identification line */
+ if (!pp->no_header && !pp->header_last)
+ banner(pp, line+1, jobname);
+ continue;
+
+ case '1': /* troff fonts */
+ case '2':
+ case '3':
+ case '4':
+ if (line[1] != '\0') {
+ strlcpy(fonts[line[0]-'1'], line + 1,
+ (size_t)50);
+ }
+ continue;
+
+ case 'W': /* page width */
+ strlcpy(width+2, line + 1, sizeof(width) - 2);
+ continue;
+
+ case 'I': /* indent amount */
+ strlcpy(indent+2, line + 1, sizeof(indent) - 2);
+ continue;
+
+ case 'Z': /* locale for pr */
+ strlcpy(locale, line + 1, sizeof(locale));
+ continue;
+
+ default: /* some file to print */
+ /* only lowercase cmd-codes include a file-to-print */
+ if ((line[0] < 'a') || (line[0] > 'z')) {
+ /* ignore any other lines */
+ if (lflag <= 1)
+ continue;
+ if (!didignorehdr) {
+ syslog(LOG_INFO, "%s: in %s :",
+ pp->printer, file);
+ didignorehdr = 1;
+ }
+ syslog(LOG_INFO, "%s: ignoring line: '%c' %s",
+ pp->printer, line[0], &line[1]);
+ continue;
+ }
+ i = print(pp, line[0], line+1);
+ switch (i) {
+ case ERROR:
+ if (bombed == OK)
+ bombed = FATALERR;
+ break;
+ case REPRINT:
+ (void) fclose(cfp);
+ return (REPRINT);
+ case FILTERERR:
+ case ACCESS:
+ bombed = i;
+ sendmail(pp, logname, bombed);
+ }
+ title[0] = '\0';
+ continue;
+
+ case 'N':
+ case 'U':
+ case 'M':
+ case 'R':
+ continue;
+ }
+
+ /* pass 2 */
+
+pass2:
+ fseek(cfp, 0L, 0);
+ while (getline(cfp))
+ switch (line[0]) {
+ case 'L': /* identification line */
+ if (!pp->no_header && pp->header_last)
+ banner(pp, line+1, jobname);
+ continue;
+
+ case 'M':
+ if (bombed < NOACCT) /* already sent if >= NOACCT */
+ sendmail(pp, line+1, bombed);
+ continue;
+
+ case 'U':
+ if (strchr(line+1, '/'))
+ continue;
+ (void) unlink(line+1);
+ }
+ /*
+ * clean-up in case another control file exists
+ */
+ (void) fclose(cfp);
+ (void) unlink(file);
+ return (bombed == OK ? OK : ERROR);
+}
+
+/*
+ * Print a file.
+ * Set up the chain [ PR [ | {IF, OF} ] ] or {IF, RF, TF, NF, DF, CF, VF}.
+ * Return -1 if a non-recoverable error occured,
+ * 2 if the filter detected some errors (but printed the job anyway),
+ * 1 if we should try to reprint this job and
+ * 0 if all is well.
+ * Note: all filters take stdin as the file, stdout as the printer,
+ * stderr as the log file, and must not ignore SIGINT.
+ */
+static int
+print(struct printer *pp, int format, char *file)
+{
+ register int n, i;
+ register char *prog;
+ int fi, fo;
+ FILE *fp;
+ char *av[15], buf[BUFSIZ];
+ pid_t wpid;
+ int p[2], retcode, stopped, wstatus, wstatus_set;
+ struct stat stb;
+
+ if (lstat(file, &stb) < 0 || (fi = open(file, O_RDONLY)) < 0) {
+ syslog(LOG_INFO, "%s: unable to open %s ('%c' line)",
+ pp->printer, file, format);
+ return (ERROR);
+ }
+ /*
+ * Check to see if data file is a symbolic link. If so, it should
+ * still point to the same file or someone is trying to print
+ * something he shouldn't.
+ */
+ if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(fi, &stb) == 0 &&
+ (stb.st_dev != fdev || stb.st_ino != fino))
+ return (ACCESS);
+
+ job_dfcnt++; /* increment datafile counter for this job */
+ stopped = 0; /* output filter is not stopped */
+
+ /* everything seems OK, start it up */
+ if (!pp->no_formfeed && !pp->tof) { /* start on a fresh page */
+ (void) write(ofd, pp->form_feed, strlen(pp->form_feed));
+ pp->tof = 1;
+ }
+ if (pp->filters[LPF_INPUT] == NULL
+ && (format == 'f' || format == 'l' || format == 'o')) {
+ pp->tof = 0;
+ while ((n = read(fi, buf, BUFSIZ)) > 0)
+ if (write(ofd, buf, n) != n) {
+ (void) close(fi);
+ return (REPRINT);
+ }
+ (void) close(fi);
+ return (OK);
+ }
+ switch (format) {
+ case 'p': /* print file using 'pr' */
+ if (pp->filters[LPF_INPUT] == NULL) { /* use output filter */
+ prog = _PATH_PR;
+ i = 0;
+ av[i++] = "pr";
+ av[i++] = width;
+ av[i++] = length;
+ av[i++] = "-h";
+ av[i++] = *title ? title : " ";
+ av[i++] = "-L";
+ av[i++] = *locale ? locale : "C";
+ av[i++] = "-F";
+ av[i] = 0;
+ fo = ofd;
+ goto start;
+ }
+ pipe(p);
+ if ((prchild = dofork(pp, DORETURN)) == 0) { /* child */
+ dup2(fi, 0); /* file is stdin */
+ dup2(p[1], 1); /* pipe is stdout */
+ closelog();
+ closeallfds(3);
+ execl(_PATH_PR, "pr", width, length,
+ "-h", *title ? title : " ",
+ "-L", *locale ? locale : "C",
+ "-F", (char *)0);
+ syslog(LOG_ERR, "cannot execl %s", _PATH_PR);
+ exit(2);
+ }
+ (void) close(p[1]); /* close output side */
+ (void) close(fi);
+ if (prchild < 0) {
+ prchild = 0;
+ (void) close(p[0]);
+ return (ERROR);
+ }
+ fi = p[0]; /* use pipe for input */
+ case 'f': /* print plain text file */
+ prog = pp->filters[LPF_INPUT];
+ av[1] = width;
+ av[2] = length;
+ av[3] = indent;
+ n = 4;
+ break;
+ case 'o': /* print postscript file */
+ /*
+ * Treat this as a "plain file with control characters", and
+ * assume the standard LPF_INPUT filter will recognize that
+ * the data is postscript and know what to do with it. These
+ * 'o'-file requests could come from MacOS 10.1 systems.
+ * (later versions of MacOS 10 will explicitly use 'l')
+ * A postscript file can contain binary data, which is why 'l'
+ * is somewhat more appropriate than 'f'.
+ */
+ /* FALLTHROUGH */
+ case 'l': /* like 'f' but pass control characters */
+ prog = pp->filters[LPF_INPUT];
+ av[1] = "-c";
+ av[2] = width;
+ av[3] = length;
+ av[4] = indent;
+ n = 5;
+ break;
+ case 'r': /* print a fortran text file */
+ prog = pp->filters[LPF_FORTRAN];
+ av[1] = width;
+ av[2] = length;
+ n = 3;
+ break;
+ case 't': /* print troff output */
+ case 'n': /* print ditroff output */
+ case 'd': /* print tex output */
+ (void) unlink(".railmag");
+ if ((fo = creat(".railmag", FILMOD)) < 0) {
+ syslog(LOG_ERR, "%s: cannot create .railmag",
+ pp->printer);
+ (void) unlink(".railmag");
+ } else {
+ for (n = 0; n < 4; n++) {
+ if (fonts[n][0] != '/')
+ (void) write(fo, _PATH_VFONT,
+ sizeof(_PATH_VFONT) - 1);
+ (void) write(fo, fonts[n], strlen(fonts[n]));
+ (void) write(fo, "\n", 1);
+ }
+ (void) close(fo);
+ }
+ prog = (format == 't') ? pp->filters[LPF_TROFF]
+ : ((format == 'n') ? pp->filters[LPF_DITROFF]
+ : pp->filters[LPF_DVI]);
+ av[1] = pxwidth;
+ av[2] = pxlength;
+ n = 3;
+ break;
+ case 'c': /* print cifplot output */
+ prog = pp->filters[LPF_CIFPLOT];
+ av[1] = pxwidth;
+ av[2] = pxlength;
+ n = 3;
+ break;
+ case 'g': /* print plot(1G) output */
+ prog = pp->filters[LPF_GRAPH];
+ av[1] = pxwidth;
+ av[2] = pxlength;
+ n = 3;
+ break;
+ case 'v': /* print raster output */
+ prog = pp->filters[LPF_RASTER];
+ av[1] = pxwidth;
+ av[2] = pxlength;
+ n = 3;
+ break;
+ default:
+ (void) close(fi);
+ syslog(LOG_ERR, "%s: illegal format character '%c'",
+ pp->printer, format);
+ return (ERROR);
+ }
+ if (prog == NULL) {
+ (void) close(fi);
+ syslog(LOG_ERR,
+ "%s: no filter found in printcap for format character '%c'",
+ pp->printer, format);
+ return (ERROR);
+ }
+ if ((av[0] = strrchr(prog, '/')) != NULL)
+ av[0]++;
+ else
+ av[0] = prog;
+ av[n++] = "-n";
+ av[n++] = logname;
+ av[n++] = "-h";
+ av[n++] = origin_host;
+ av[n++] = pp->acct_file;
+ av[n] = 0;
+ fo = pfd;
+ if (of_pid > 0) { /* stop output filter */
+ write(ofd, "\031\1", 2);
+ while ((wpid =
+ wait3(&wstatus, WUNTRACED, 0)) > 0 && wpid != of_pid)
+ ;
+ if (wpid < 0)
+ syslog(LOG_WARNING,
+ "%s: after stopping 'of', wait3() returned: %m",
+ pp->printer);
+ else if (!WIFSTOPPED(wstatus)) {
+ (void) close(fi);
+ syslog(LOG_WARNING, "%s: output filter died "
+ "(pid=%d retcode=%d termsig=%d)",
+ pp->printer, of_pid, WEXITSTATUS(wstatus),
+ WTERMSIG(wstatus));
+ return (REPRINT);
+ }
+ stopped++;
+ }
+start:
+ if ((child = dofork(pp, DORETURN)) == 0) { /* child */
+ dup2(fi, 0);
+ dup2(fo, 1);
+ /* setup stderr for the filter (child process)
+ * so it goes to our temporary errors file */
+ n = open(tempstderr, O_WRONLY|O_TRUNC, 0664);
+ if (n >= 0)
+ dup2(n, 2);
+ closelog();
+ closeallfds(3);
+ execv(prog, av);
+ syslog(LOG_ERR, "%s: cannot execv(%s): %m", pp->printer,
+ prog);
+ exit(2);
+ }
+ (void) close(fi);
+ wstatus_set = 0;
+ if (child < 0)
+ retcode = 100;
+ else {
+ while ((wpid = wait(&wstatus)) > 0 && wpid != child)
+ ;
+ if (wpid < 0) {
+ retcode = 100;
+ syslog(LOG_WARNING,
+ "%s: after execv(%s), wait() returned: %m",
+ pp->printer, prog);
+ } else {
+ wstatus_set = 1;
+ retcode = WEXITSTATUS(wstatus);
+ }
+ }
+ child = 0;
+ prchild = 0;
+ if (stopped) { /* restart output filter */
+ if (kill(of_pid, SIGCONT) < 0) {
+ syslog(LOG_ERR, "cannot restart output filter");
+ exit(1);
+ }
+ }
+ pp->tof = 0;
+
+ /* Copy the filter's output to "lf" logfile */
+ if ((fp = fopen(tempstderr, "r"))) {
+ while (fgets(buf, sizeof(buf), fp))
+ fputs(buf, stderr);
+ fclose(fp);
+ }
+
+ if (wstatus_set && !WIFEXITED(wstatus)) {
+ syslog(LOG_WARNING, "%s: filter '%c' terminated (termsig=%d)",
+ pp->printer, format, WTERMSIG(wstatus));
+ return (ERROR);
+ }
+ switch (retcode) {
+ case 0:
+ pp->tof = 1;
+ return (OK);
+ case 1:
+ return (REPRINT);
+ case 2:
+ return (ERROR);
+ default:
+ syslog(LOG_WARNING, "%s: filter '%c' exited (retcode=%d)",
+ pp->printer, format, retcode);
+ return (FILTERERR);
+ }
+}
+
+/*
+ * Send the daemon control file (cf) and any data files.
+ * Return -1 if a non-recoverable error occured, 1 if a recoverable error and
+ * 0 if all is well.
+ */
+static int
+sendit(struct printer *pp, char *file)
+{
+ int dfcopies, err, i;
+ char *cp, last[BUFSIZ];
+
+ /*
+ * open control file
+ */
+ if ((cfp = fopen(file, "r")) == NULL)
+ return (OK);
+
+ /* initialize job-specific count of datafiles processed */
+ job_dfcnt = 0;
+
+ /*
+ * read the control file for work to do
+ *
+ * file format -- first character in the line is a command
+ * rest of the line is the argument.
+ * commands of interest are:
+ *
+ * a-z -- "file name" name of file to print
+ * U -- "unlink" name of file to remove
+ * (after we print it. (Pass 2 only)).
+ */
+
+ /*
+ * pass 1
+ */
+ err = OK;
+ while (getline(cfp)) {
+ again:
+ if (line[0] == 'S') {
+ cp = line+1;
+ i = 0;
+ while (*cp >= '0' && *cp <= '9')
+ i = i * 10 + (*cp++ - '0');
+ fdev = i;
+ cp++;
+ i = 0;
+ while (*cp >= '0' && *cp <= '9')
+ i = i * 10 + (*cp++ - '0');
+ fino = i;
+ } else if (line[0] == 'H') {
+ strlcpy(origin_host, line + 1, sizeof(origin_host));
+ if (class[0] == '\0') {
+ strlcpy(class, line + 1, sizeof(class));
+ }
+ } else if (line[0] == 'P') {
+ strlcpy(logname, line + 1, sizeof(logname));
+ if (pp->restricted) { /* restricted */
+ if (getpwnam(logname) == NULL) {
+ sendmail(pp, line+1, NOACCT);
+ err = ERROR;
+ break;
+ }
+ }
+ } else if (line[0] == 'I') {
+ strlcpy(indent+2, line + 1, sizeof(indent) - 2);
+ } else if (line[0] >= 'a' && line[0] <= 'z') {
+ dfcopies = 1;
+ strcpy(last, line);
+ while ((i = getline(cfp)) != 0) {
+ if (strcmp(last, line) != 0)
+ break;
+ dfcopies++;
+ }
+ switch (sendfile(pp, '\3', last+1, *last, dfcopies)) {
+ case OK:
+ if (i)
+ goto again;
+ break;
+ case REPRINT:
+ (void) fclose(cfp);
+ return (REPRINT);
+ case ACCESS:
+ sendmail(pp, logname, ACCESS);
+ case ERROR:
+ err = ERROR;
+ }
+ break;
+ }
+ }
+ if (err == OK && sendfile(pp, '\2', file, '\0', 1) > 0) {
+ (void) fclose(cfp);
+ return (REPRINT);
+ }
+ /*
+ * pass 2
+ */
+ fseek(cfp, 0L, 0);
+ while (getline(cfp))
+ if (line[0] == 'U' && !strchr(line+1, '/'))
+ (void) unlink(line+1);
+ /*
+ * clean-up in case another control file exists
+ */
+ (void) fclose(cfp);
+ (void) unlink(file);
+ return (err);
+}
+
+/*
+ * Send a data file to the remote machine and spool it.
+ * Return positive if we should try resending.
+ */
+static int
+sendfile(struct printer *pp, int type, char *file, char format, int copyreq)
+{
+ int i, amt;
+ struct stat stb;
+ char *av[15], *filtcmd;
+ char buf[BUFSIZ], opt_c[4], opt_h[4], opt_n[4];
+ int copycnt, filtstat, narg, resp, sfd, sfres, sizerr, statrc;
+
+ statrc = lstat(file, &stb);
+ if (statrc < 0) {
+ syslog(LOG_ERR, "%s: error from lstat(%s): %m",
+ pp->printer, file);
+ return (ERROR);
+ }
+ sfd = open(file, O_RDONLY);
+ if (sfd < 0) {
+ syslog(LOG_ERR, "%s: error from open(%s,O_RDONLY): %m",
+ pp->printer, file);
+ return (ERROR);
+ }
+ /*
+ * Check to see if data file is a symbolic link. If so, it should
+ * still point to the same file or someone is trying to print something
+ * he shouldn't.
+ */
+ if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(sfd, &stb) == 0 &&
+ (stb.st_dev != fdev || stb.st_ino != fino)) {
+ close(sfd);
+ return (ACCESS);
+ }
+
+ /* Everything seems OK for reading the file, now to send it */
+ filtcmd = NULL;
+ sizerr = 0;
+ tfd = -1;
+ if (type == '\3') {
+ /*
+ * Type == 3 means this is a datafile, not a control file.
+ * Increment the counter of data-files in this job, and
+ * then check for input or output filters (which are only
+ * applied to datafiles, not control files).
+ */
+ job_dfcnt++;
+
+ /*
+ * Note that here we are filtering datafiles, one at a time,
+ * as they are sent to the remote machine. Here, the *only*
+ * difference between an input filter (`if=') and an output
+ * filter (`of=') is the argument list that the filter is
+ * started up with. Here, the output filter is executed
+ * for each individual file as it is sent. This is not the
+ * same as local print queues, where the output filter is
+ * started up once, and then all jobs are passed thru that
+ * single invocation of the output filter.
+ *
+ * Also note that a queue for a remote-machine can have an
+ * input filter or an output filter, but not both.
+ */
+ if (pp->filters[LPF_INPUT]) {
+ filtcmd = pp->filters[LPF_INPUT];
+ av[0] = filtcmd;
+ narg = 0;
+ strcpy(opt_c, "-c");
+ strcpy(opt_h, "-h");
+ strcpy(opt_n, "-n");
+ if (format == 'l')
+ av[++narg] = opt_c;
+ av[++narg] = width;
+ av[++narg] = length;
+ av[++narg] = indent;
+ av[++narg] = opt_n;
+ av[++narg] = logname;
+ av[++narg] = opt_h;
+ av[++narg] = origin_host;
+ av[++narg] = pp->acct_file;
+ av[++narg] = NULL;
+ } else if (pp->filters[LPF_OUTPUT]) {
+ filtcmd = pp->filters[LPF_OUTPUT];
+ av[0] = filtcmd;
+ narg = 0;
+ av[++narg] = width;
+ av[++narg] = length;
+ av[++narg] = NULL;
+ }
+ }
+ if (filtcmd) {
+ /*
+ * If there is an input or output filter, we have to run
+ * the datafile thru that filter and store the result as
+ * a temporary spool file, because the protocol requires
+ * that we send the remote host the file-size before we
+ * start to send any of the data.
+ */
+ strcpy(tfile, TFILENAME);
+ tfd = mkstemp(tfile);
+ if (tfd == -1) {
+ syslog(LOG_ERR, "%s: mkstemp(%s): %m", pp->printer,
+ TFILENAME);
+ sfres = ERROR;
+ goto return_sfres;
+ }
+ filtstat = execfilter(pp, filtcmd, av, sfd, tfd);
+
+ /* process the return-code from the filter */
+ switch (filtstat) {
+ case 0:
+ break;
+ case 1:
+ sfres = REPRINT;
+ goto return_sfres;
+ case 2:
+ sfres = ERROR;
+ goto return_sfres;
+ default:
+ syslog(LOG_WARNING,
+ "%s: filter '%c' exited (retcode=%d)",
+ pp->printer, format, filtstat);
+ sfres = FILTERERR;
+ goto return_sfres;
+ }
+ statrc = fstat(tfd, &stb); /* to find size of tfile */
+ if (statrc < 0) {
+ syslog(LOG_ERR,
+ "%s: error processing 'if', fstat(%s): %m",
+ pp->printer, tfile);
+ sfres = ERROR;
+ goto return_sfres;
+ }
+ close(sfd);
+ sfd = tfd;
+ lseek(sfd, 0, SEEK_SET);
+ }
+
+ copycnt = 0;
+sendagain:
+ copycnt++;
+
+ if (copycnt < 2)
+ (void) sprintf(buf, "%c%qd %s\n", type, stb.st_size, file);
+ else
+ (void) sprintf(buf, "%c%qd %s_c%d\n", type, stb.st_size,
+ file, copycnt);
+ amt = strlen(buf);
+ for (i = 0; ; i++) {
+ if (write(pfd, buf, amt) != amt ||
+ (resp = response(pp)) < 0 || resp == '\1') {
+ sfres = REPRINT;
+ goto return_sfres;
+ } else if (resp == '\0')
+ break;
+ if (i == 0)
+ pstatus(pp,
+ "no space on remote; waiting for queue to drain");
+ if (i == 10)
+ syslog(LOG_ALERT, "%s: can't send to %s; queue full",
+ pp->printer, pp->remote_host);
+ sleep(5 * 60);
+ }
+ if (i)
+ pstatus(pp, "sending to %s", pp->remote_host);
+ /*
+ * XXX - we should change trstat_init()/trstat_write() to include
+ * the copycnt in the statistics record it may write.
+ */
+ if (type == '\3')
+ trstat_init(pp, file, job_dfcnt);
+ for (i = 0; i < stb.st_size; i += BUFSIZ) {
+ amt = BUFSIZ;
+ if (i + amt > stb.st_size)
+ amt = stb.st_size - i;
+ if (sizerr == 0 && read(sfd, buf, amt) != amt)
+ sizerr = 1;
+ if (write(pfd, buf, amt) != amt) {
+ sfres = REPRINT;
+ goto return_sfres;
+ }
+ }
+
+ if (sizerr) {
+ syslog(LOG_INFO, "%s: %s: changed size", pp->printer, file);
+ /* tell recvjob to ignore this file */
+ (void) write(pfd, "\1", 1);
+ sfres = ERROR;
+ goto return_sfres;
+ }
+ if (write(pfd, "", 1) != 1 || response(pp)) {
+ sfres = REPRINT;
+ goto return_sfres;
+ }
+ if (type == '\3') {
+ trstat_write(pp, TR_SENDING, stb.st_size, logname,
+ pp->remote_host, origin_host);
+ /*
+ * Usually we only need to send one copy of a datafile,
+ * because the control-file will simply print the same
+ * file multiple times. However, some printers ignore
+ * the control file, and simply print each data file as
+ * it arrives. For such "remote hosts", we need to
+ * transfer the same data file multiple times. Such a
+ * a host is indicated by adding 'rc' to the printcap
+ * entry.
+ * XXX - Right now this ONLY works for remote hosts which
+ * do ignore the name of the data file, because
+ * this sends the file multiple times with slight
+ * changes to the filename. To do this right would
+ * require that we also rewrite the control file
+ * to match those filenames.
+ */
+ if (pp->resend_copies && (copycnt < copyreq)) {
+ lseek(sfd, 0, SEEK_SET);
+ goto sendagain;
+ }
+ }
+ sfres = OK;
+
+return_sfres:
+ (void)close(sfd);
+ if (tfd != -1) {
+ /*
+ * If tfd is set, then it is the same value as sfd, and
+ * therefore it is already closed at this point. All
+ * we need to do is remove the temporary file.
+ */
+ tfd = -1;
+ unlink(tfile);
+ }
+ return (sfres);
+}
+
+/*
+ * This routine is called to execute one of the filters as was
+ * specified in a printcap entry. While the child-process will read
+ * all of 'infd', it is up to the caller to close that file descriptor
+ * in the parent process.
+ */
+static int
+execfilter(struct printer *pp, char *f_cmd, char *f_av[], int infd, int outfd)
+{
+ pid_t fpid, wpid;
+ int errfd, retcode, wstatus;
+ FILE *errfp;
+ char buf[BUFSIZ], *slash;
+
+ fpid = dofork(pp, DORETURN);
+ if (fpid != 0) {
+ /*
+ * This is the parent process, which just waits for the child
+ * to complete and then returns the result. Note that it is
+ * the child process which reads the input stream.
+ */
+ if (fpid < 0)
+ retcode = 100;
+ else {
+ while ((wpid = wait(&wstatus)) > 0 &&
+ wpid != fpid)
+ ;
+ if (wpid < 0) {
+ retcode = 100;
+ syslog(LOG_WARNING,
+ "%s: after execv(%s), wait() returned: %m",
+ pp->printer, f_cmd);
+ } else
+ retcode = WEXITSTATUS(wstatus);
+ }
+
+ /*
+ * Copy everything the filter wrote to stderr from our
+ * temporary errors file to the "lf=" logfile.
+ */
+ errfp = fopen(tempstderr, "r");
+ if (errfp) {
+ while (fgets(buf, sizeof(buf), errfp))
+ fputs(buf, stderr);
+ fclose(errfp);
+ }
+
+ return (retcode);
+ }
+
+ /*
+ * This is the child process, which is the one that executes the
+ * given filter.
+ */
+ /*
+ * If the first parameter has any slashes in it, then change it
+ * to point to the first character after the last slash.
+ */
+ slash = strrchr(f_av[0], '/');
+ if (slash != NULL)
+ f_av[0] = slash + 1;
+ /*
+ * XXX - in the future, this should setup an explicit list of
+ * environment variables and use execve()!
+ */
+
+ /*
+ * Setup stdin, stdout, and stderr as we want them when the filter
+ * is running. Stderr is setup so it points to a temporary errors
+ * file, and the parent process will copy that temporary file to
+ * the real logfile after the filter completes.
+ */
+ dup2(infd, 0);
+ dup2(outfd, 1);
+ errfd = open(tempstderr, O_WRONLY|O_TRUNC, 0664);
+ if (errfd >= 0)
+ dup2(errfd, 2);
+ closelog();
+ closeallfds(3);
+ execv(f_cmd, f_av);
+ syslog(LOG_ERR, "%s: cannot execv(%s): %m", pp->printer, f_cmd);
+ exit(2);
+ /* NOTREACHED */
+}
+
+/*
+ * Check to make sure there have been no errors and that both programs
+ * are in sync with eachother.
+ * Return non-zero if the connection was lost.
+ */
+static char
+response(const struct printer *pp)
+{
+ char resp;
+
+ if (read(pfd, &resp, 1) != 1) {
+ syslog(LOG_INFO, "%s: lost connection", pp->printer);
+ return (-1);
+ }
+ return (resp);
+}
+
+/*
+ * Banner printing stuff
+ */
+static void
+banner(struct printer *pp, char *name1, char *name2)
+{
+ time_t tvec;
+
+ time(&tvec);
+ if (!pp->no_formfeed && !pp->tof)
+ (void) write(ofd, pp->form_feed, strlen(pp->form_feed));
+ if (pp->short_banner) { /* short banner only */
+ if (class[0]) {
+ (void) write(ofd, class, strlen(class));
+ (void) write(ofd, ":", 1);
+ }
+ (void) write(ofd, name1, strlen(name1));
+ (void) write(ofd, " Job: ", 7);
+ (void) write(ofd, name2, strlen(name2));
+ (void) write(ofd, " Date: ", 8);
+ (void) write(ofd, ctime(&tvec), 24);
+ (void) write(ofd, "\n", 1);
+ } else { /* normal banner */
+ (void) write(ofd, "\n\n\n", 3);
+ scan_out(pp, ofd, name1, '\0');
+ (void) write(ofd, "\n\n", 2);
+ scan_out(pp, ofd, name2, '\0');
+ if (class[0]) {
+ (void) write(ofd,"\n\n\n",3);
+ scan_out(pp, ofd, class, '\0');
+ }
+ (void) write(ofd, "\n\n\n\n\t\t\t\t\tJob: ", 15);
+ (void) write(ofd, name2, strlen(name2));
+ (void) write(ofd, "\n\t\t\t\t\tDate: ", 12);
+ (void) write(ofd, ctime(&tvec), 24);
+ (void) write(ofd, "\n", 1);
+ }
+ if (!pp->no_formfeed)
+ (void) write(ofd, pp->form_feed, strlen(pp->form_feed));
+ pp->tof = 1;
+}
+
+static char *
+scnline(int key, char *p, int c)
+{
+ register int scnwidth;
+
+ for (scnwidth = WIDTH; --scnwidth;) {
+ key <<= 1;
+ *p++ = key & 0200 ? c : BACKGND;
+ }
+ return (p);
+}
+
+#define TRC(q) (((q)-' ')&0177)
+
+static void
+scan_out(struct printer *pp, int scfd, char *scsp, int dlm)
+{
+ register char *strp;
+ register int nchrs, j;
+ char outbuf[LINELEN+1], *sp, c, cc;
+ int d, scnhgt;
+
+ for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) {
+ strp = &outbuf[0];
+ sp = scsp;
+ for (nchrs = 0; ; ) {
+ d = dropit(c = TRC(cc = *sp++));
+ if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d))
+ for (j = WIDTH; --j;)
+ *strp++ = BACKGND;
+ else
+ strp = scnline(scnkey[(int)c][scnhgt-1-d], strp, cc);
+ if (*sp == dlm || *sp == '\0' ||
+ nchrs++ >= pp->page_width/(WIDTH+1)-1)
+ break;
+ *strp++ = BACKGND;
+ *strp++ = BACKGND;
+ }
+ while (*--strp == BACKGND && strp >= outbuf)
+ ;
+ strp++;
+ *strp++ = '\n';
+ (void) write(scfd, outbuf, strp-outbuf);
+ }
+}
+
+static int
+dropit(int c)
+{
+ switch(c) {
+
+ case TRC('_'):
+ case TRC(';'):
+ case TRC(','):
+ case TRC('g'):
+ case TRC('j'):
+ case TRC('p'):
+ case TRC('q'):
+ case TRC('y'):
+ return (DROP);
+
+ default:
+ return (0);
+ }
+}
+
+/*
+ * sendmail ---
+ * tell people about job completion
+ */
+static void
+sendmail(struct printer *pp, char *userid, int bombed)
+{
+ register int i;
+ int p[2], s;
+ register const char *cp;
+ struct stat stb;
+ FILE *fp;
+
+ pipe(p);
+ if ((s = dofork(pp, DORETURN)) == 0) { /* child */
+ dup2(p[0], 0);
+ closelog();
+ closeallfds(3);
+ if ((cp = strrchr(_PATH_SENDMAIL, '/')) != NULL)
+ cp++;
+ else
+ cp = _PATH_SENDMAIL;
+ execl(_PATH_SENDMAIL, cp, "-t", (char *)0);
+ _exit(0);
+ } else if (s > 0) { /* parent */
+ dup2(p[1], 1);
+ printf("To: %s@%s\n", userid, origin_host);
+ printf("Subject: %s printer job \"%s\"\n", pp->printer,
+ *jobname ? jobname : "<unknown>");
+ printf("Reply-To: root@%s\n\n", local_host);
+ printf("Your printer job ");
+ if (*jobname)
+ printf("(%s) ", jobname);
+
+ switch (bombed) {
+ case OK:
+ cp = "OK";
+ printf("\ncompleted successfully\n");
+ break;
+ default:
+ case FATALERR:
+ cp = "FATALERR";
+ printf("\ncould not be printed\n");
+ break;
+ case NOACCT:
+ cp = "NOACCT";
+ printf("\ncould not be printed without an account on %s\n",
+ local_host);
+ break;
+ case FILTERERR:
+ cp = "FILTERERR";
+ if (stat(tempstderr, &stb) < 0 || stb.st_size == 0
+ || (fp = fopen(tempstderr, "r")) == NULL) {
+ printf("\nhad some errors and may not have printed\n");
+ break;
+ }
+ printf("\nhad the following errors and may not have printed:\n");
+ while ((i = getc(fp)) != EOF)
+ putchar(i);
+ (void) fclose(fp);
+ break;
+ case ACCESS:
+ cp = "ACCESS";
+ printf("\nwas not printed because it was not linked to the original file\n");
+ }
+ fflush(stdout);
+ (void) close(1);
+ } else {
+ syslog(LOG_WARNING, "unable to send mail to %s: %m", userid);
+ return;
+ }
+ (void) close(p[0]);
+ (void) close(p[1]);
+ wait(NULL);
+ syslog(LOG_INFO, "mail sent to user %s about job %s on printer %s (%s)",
+ userid, *jobname ? jobname : "<unknown>", pp->printer, cp);
+}
+
+/*
+ * dofork - fork with retries on failure
+ */
+static int
+dofork(const struct printer *pp, int action)
+{
+ pid_t forkpid;
+ int i, fail;
+ struct passwd *pwd;
+
+ forkpid = -1;
+ if (daemon_uname == NULL) {
+ pwd = getpwuid(pp->daemon_user);
+ if (pwd == NULL) {
+ syslog(LOG_ERR, "%s: Can't lookup default daemon uid (%ld) in password file",
+ pp->printer, pp->daemon_user);
+ goto error_ret;
+ }
+ daemon_uname = strdup(pwd->pw_name);
+ daemon_defgid = pwd->pw_gid;
+ }
+
+ for (i = 0; i < 20; i++) {
+ forkpid = fork();
+ if (forkpid < 0) {
+ sleep((unsigned)(i*i));
+ continue;
+ }
+ /*
+ * Child should run as daemon instead of root
+ */
+ if (forkpid == 0) {
+ errno = 0;
+ fail = initgroups(daemon_uname, daemon_defgid);
+ if (fail) {
+ syslog(LOG_ERR, "%s: initgroups(%s,%u): %m",
+ pp->printer, daemon_uname, daemon_defgid);
+ break;
+ }
+ fail = setgid(daemon_defgid);
+ if (fail) {
+ syslog(LOG_ERR, "%s: setgid(%u): %m",
+ pp->printer, daemon_defgid);
+ break;
+ }
+ fail = setuid(pp->daemon_user);
+ if (fail) {
+ syslog(LOG_ERR, "%s: setuid(%ld): %m",
+ pp->printer, pp->daemon_user);
+ break;
+ }
+ }
+ return (forkpid);
+ }
+
+ /*
+ * An error occurred. If the error is in the child process, then
+ * this routine MUST always exit(). DORETURN only effects how
+ * errors should be handled in the parent process.
+ */
+error_ret:
+ if (forkpid == 0) {
+ syslog(LOG_ERR, "%s: dofork(): aborting child process...",
+ pp->printer);
+ exit(1);
+ }
+ syslog(LOG_ERR, "%s: dofork(): failure in fork", pp->printer);
+
+ sleep(1); /* throttle errors, as a safety measure */
+ switch (action) {
+ case DORETURN:
+ return (-1);
+ default:
+ syslog(LOG_ERR, "bad action (%d) to dofork", action);
+ /* FALLTHROUGH */
+ case DOABORT:
+ exit(1);
+ }
+ /*NOTREACHED*/
+}
+
+/*
+ * Kill child processes to abort current job.
+ */
+static void
+abortpr(int signo __unused)
+{
+
+ (void) unlink(tempstderr);
+ kill(0, SIGINT);
+ if (of_pid > 0)
+ kill(of_pid, SIGCONT);
+ while (wait(NULL) > 0)
+ ;
+ if (of_pid > 0 && tfd != -1)
+ unlink(tfile);
+ exit(0);
+}
+
+static void
+init(struct printer *pp)
+{
+ char *s;
+
+ sprintf(&width[2], "%ld", pp->page_width);
+ sprintf(&length[2], "%ld", pp->page_length);
+ sprintf(&pxwidth[2], "%ld", pp->page_pwidth);
+ sprintf(&pxlength[2], "%ld", pp->page_plength);
+ if ((s = checkremote(pp)) != 0) {
+ syslog(LOG_WARNING, "%s", s);
+ free(s);
+ }
+}
+
+void
+startprinting(const char *printer)
+{
+ struct printer myprinter, *pp = &myprinter;
+ int status;
+
+ init_printer(pp);
+ status = getprintcap(printer, pp);
+ switch(status) {
+ case PCAPERR_OSERR:
+ syslog(LOG_ERR, "can't open printer description file: %m");
+ exit(1);
+ case PCAPERR_NOTFOUND:
+ syslog(LOG_ERR, "unknown printer: %s", printer);
+ exit(1);
+ case PCAPERR_TCLOOP:
+ fatal(pp, "potential reference loop detected in printcap file");
+ default:
+ break;
+ }
+ printjob(pp);
+}
+
+/*
+ * Acquire line printer or remote connection.
+ */
+static void
+openpr(const struct printer *pp)
+{
+ int p[2];
+ char *cp;
+
+ if (pp->remote) {
+ openrem(pp);
+ /*
+ * Lpd does support the setting of 'of=' filters for
+ * jobs going to remote machines, but that does not
+ * have the same meaning as 'of=' does when handling
+ * local print queues. For remote machines, all 'of='
+ * filter processing is handled in sendfile(), and that
+ * does not use these global "output filter" variables.
+ */
+ ofd = -1;
+ of_pid = 0;
+ return;
+ } else if (*pp->lp) {
+ if ((cp = strchr(pp->lp, '@')) != NULL)
+ opennet(pp);
+ else
+ opentty(pp);
+ } else {
+ syslog(LOG_ERR, "%s: no line printer device or host name",
+ pp->printer);
+ exit(1);
+ }
+
+ /*
+ * Start up an output filter, if needed.
+ */
+ if (pp->filters[LPF_OUTPUT] && !pp->filters[LPF_INPUT] && !of_pid) {
+ pipe(p);
+ if (pp->remote) {
+ strcpy(tfile, TFILENAME);
+ tfd = mkstemp(tfile);
+ }
+ if ((of_pid = dofork(pp, DOABORT)) == 0) { /* child */
+ dup2(p[0], 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,
+ (char *)0);
+ syslog(LOG_ERR, "%s: execl(%s): %m", pp->printer,
+ pp->filters[LPF_OUTPUT]);
+ exit(1);
+ }
+ (void) close(p[0]); /* close input side */
+ ofd = p[1]; /* use pipe for output */
+ } else {
+ ofd = pfd;
+ of_pid = 0;
+ }
+}
+
+/*
+ * Printer connected directly to the network
+ * or to a terminal server on the net
+ */
+static void
+opennet(const struct printer *pp)
+{
+ register int i;
+ int resp;
+ u_long port;
+ char *ep;
+ void (*savealrm)(int);
+
+ port = strtoul(pp->lp, &ep, 0);
+ if (*ep != '@' || port > 65535) {
+ syslog(LOG_ERR, "%s: bad port number: %s", pp->printer,
+ pp->lp);
+ exit(1);
+ }
+ ep++;
+
+ for (i = 1; ; i = i < 256 ? i << 1 : i) {
+ resp = -1;
+ savealrm = signal(SIGALRM, alarmhandler);
+ alarm(pp->conn_timeout);
+ pfd = getport(pp, ep, port);
+ alarm(0);
+ (void)signal(SIGALRM, savealrm);
+ if (pfd < 0 && errno == ECONNREFUSED)
+ resp = 1;
+ else if (pfd >= 0) {
+ /*
+ * need to delay a bit for rs232 lines
+ * to stabilize in case printer is
+ * connected via a terminal server
+ */
+ delay(500);
+ break;
+ }
+ if (i == 1) {
+ if (resp < 0)
+ pstatus(pp, "waiting for %s to come up",
+ pp->lp);
+ else
+ pstatus(pp,
+ "waiting for access to printer on %s",
+ pp->lp);
+ }
+ sleep(i);
+ }
+ pstatus(pp, "sending to %s port %lu", ep, port);
+}
+
+/*
+ * Printer is connected to an RS232 port on this host
+ */
+static void
+opentty(const struct printer *pp)
+{
+ register int i;
+
+ for (i = 1; ; i = i < 32 ? i << 1 : i) {
+ pfd = open(pp->lp, pp->rw ? O_RDWR : O_WRONLY);
+ if (pfd >= 0) {
+ delay(500);
+ break;
+ }
+ if (errno == ENOENT) {
+ syslog(LOG_ERR, "%s: %m", pp->lp);
+ exit(1);
+ }
+ if (i == 1)
+ pstatus(pp,
+ "waiting for %s to become ready (offline?)",
+ pp->printer);
+ sleep(i);
+ }
+ if (isatty(pfd))
+ setty(pp);
+ pstatus(pp, "%s is ready and printing", pp->printer);
+}
+
+/*
+ * Printer is on a remote host
+ */
+static void
+openrem(const struct printer *pp)
+{
+ register int i;
+ int resp;
+ void (*savealrm)(int);
+
+ for (i = 1; ; i = i < 256 ? i << 1 : i) {
+ resp = -1;
+ savealrm = signal(SIGALRM, alarmhandler);
+ alarm(pp->conn_timeout);
+ pfd = getport(pp, pp->remote_host, 0);
+ alarm(0);
+ (void)signal(SIGALRM, savealrm);
+ if (pfd >= 0) {
+ if ((writel(pfd, "\2", pp->remote_queue, "\n",
+ (char *)0)
+ == 2 + strlen(pp->remote_queue))
+ && (resp = response(pp)) == 0)
+ break;
+ (void) close(pfd);
+ }
+ if (i == 1) {
+ if (resp < 0)
+ pstatus(pp, "waiting for %s to come up",
+ pp->remote_host);
+ else {
+ pstatus(pp,
+ "waiting for queue to be enabled on %s",
+ pp->remote_host);
+ i = 256;
+ }
+ }
+ sleep(i);
+ }
+ pstatus(pp, "sending to %s", pp->remote_host);
+}
+
+/*
+ * setup tty lines.
+ */
+static void
+setty(const struct printer *pp)
+{
+ struct termios ttybuf;
+
+ if (ioctl(pfd, TIOCEXCL, (char *)0) < 0) {
+ syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", pp->printer);
+ exit(1);
+ }
+ if (tcgetattr(pfd, &ttybuf) < 0) {
+ syslog(LOG_ERR, "%s: tcgetattr: %m", pp->printer);
+ exit(1);
+ }
+ if (pp->baud_rate > 0)
+ cfsetspeed(&ttybuf, pp->baud_rate);
+ if (pp->mode_set) {
+ char *s = strdup(pp->mode_set), *tmp;
+
+ while ((tmp = strsep(&s, ",")) != NULL) {
+ (void) msearch(tmp, &ttybuf);
+ }
+ }
+ if (pp->mode_set != 0 || pp->baud_rate > 0) {
+ if (tcsetattr(pfd, TCSAFLUSH, &ttybuf) == -1) {
+ syslog(LOG_ERR, "%s: tcsetattr: %m", pp->printer);
+ }
+ }
+}
+
+#include <stdarg.h>
+
+static void
+pstatus(const struct printer *pp, const char *msg, ...)
+{
+ int fd;
+ char *buf;
+ va_list ap;
+ va_start(ap, msg);
+
+ umask(0);
+ fd = open(pp->status_file, O_WRONLY|O_CREAT|O_EXLOCK, STAT_FILE_MODE);
+ if (fd < 0) {
+ syslog(LOG_ERR, "%s: open(%s): %m", pp->printer,
+ pp->status_file);
+ exit(1);
+ }
+ ftruncate(fd, 0);
+ vasprintf(&buf, msg, ap);
+ va_end(ap);
+ writel(fd, buf, "\n", (char *)0);
+ close(fd);
+ free(buf);
+}
+
+void
+alarmhandler(int signo __unused)
+{
+ /* the signal is ignored */
+ /* (the '__unused' is just to avoid a compile-time warning) */
+}
diff --git a/usr.sbin/lpr/lpd/recvjob.c b/usr.sbin/lpr/lpd/recvjob.c
new file mode 100644
index 0000000..69a654d
--- /dev/null
+++ b/usr.sbin/lpr/lpd/recvjob.c
@@ -0,0 +1,403 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#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 <errno.h>
+#include <syslog.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "lp.h"
+#include "lp.local.h"
+#include "ctlinfo.h"
+#include "extern.h"
+#include "pathnames.h"
+
+#define ack() (void) write(STDOUT_FILENO, sp, (size_t)1);
+
+static char dfname[NAME_MAX]; /* data files */
+static int minfree; /* keep at least minfree blocks available */
+static const char *sp = "";
+static char tfname[NAME_MAX]; /* tmp copy of cf before linking */
+
+static int chksize(int _size);
+static void frecverr(const char *_msg, ...) __printf0like(1, 2);
+static int noresponse(void);
+static void rcleanup(int _signo);
+static int read_number(const char *_fn);
+static int readfile(struct printer *_pp, char *_file, size_t _size);
+static int readjob(struct printer *_pp);
+
+
+void
+recvjob(const char *printer)
+{
+ struct stat stb;
+ int status;
+ struct printer myprinter, *pp = &myprinter;
+
+ /*
+ * Perform lookup for printer name or abbreviation
+ */
+ init_printer(pp);
+ status = getprintcap(printer, pp);
+ switch (status) {
+ case PCAPERR_OSERR:
+ frecverr("cannot open printer description file");
+ break;
+ case PCAPERR_NOTFOUND:
+ frecverr("unknown printer %s", printer);
+ break;
+ case PCAPERR_TCLOOP:
+ fatal(pp, "potential reference loop detected in printcap file");
+ default:
+ break;
+ }
+
+ (void) close(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: chdir(%s): %s", pp->printer, pp->spool_dir,
+ strerror(errno));
+ if (stat(pp->lock_file, &stb) == 0) {
+ if (stb.st_mode & 010) {
+ /* queue is disabled */
+ putchar('\1'); /* return error code */
+ exit(1);
+ }
+ } else if (stat(pp->spool_dir, &stb) < 0)
+ frecverr("%s: stat(%s): %s", pp->printer, pp->spool_dir,
+ strerror(errno));
+ minfree = 2 * read_number("minfree"); /* scale KB to 512 blocks */
+ signal(SIGTERM, rcleanup);
+ signal(SIGPIPE, rcleanup);
+
+ if (readjob(pp))
+ printjob(pp);
+}
+
+/*
+ * Read printer jobs sent by lpd and copy them to the spooling directory.
+ * Return the number of jobs successfully transfered.
+ */
+static int
+readjob(struct printer *pp)
+{
+ register int size;
+ int cfcnt, dfcnt;
+ char *cp, *clastp, *errmsg;
+ char givenid[32], givenhost[MAXHOSTNAMELEN];
+
+ ack();
+ cfcnt = 0;
+ dfcnt = 0;
+ for (;;) {
+ /*
+ * Read a command to tell us what to do
+ */
+ cp = line;
+ clastp = line + sizeof(line) - 1;
+ do {
+ size = read(STDOUT_FILENO, cp, (size_t)1);
+ if (size != (ssize_t)1) {
+ if (size < (ssize_t)0) {
+ frecverr("%s: lost connection",
+ pp->printer);
+ /*NOTREACHED*/
+ }
+ return (cfcnt);
+ }
+ } while ((*cp++ != '\n') && (cp <= clastp));
+ if (cp > clastp) {
+ frecverr("%s: readjob overflow", pp->printer);
+ /*NOTREACHED*/
+ }
+ *--cp = '\0';
+ cp = line;
+ switch (*cp++) {
+ case '\1': /* cleanup because data sent was bad */
+ rcleanup(0);
+ continue;
+
+ case '\2': /* read cf file */
+ size = 0;
+ dfcnt = 0;
+ while (*cp >= '0' && *cp <= '9')
+ size = size * 10 + (*cp++ - '0');
+ if (*cp++ != ' ')
+ break;
+ /*
+ * host name has been authenticated, we use our
+ * view of the host name since we may be passed
+ * something different than what gethostbyaddr()
+ * returns
+ */
+ strlcpy(cp + 6, from_host, sizeof(line)
+ + (size_t)(line - cp - 6));
+ if (strchr(cp, '/')) {
+ frecverr("readjob: %s: illegal path name", cp);
+ /*NOTREACHED*/
+ }
+ strlcpy(tfname, cp, sizeof(tfname));
+ tfname[sizeof (tfname) - 1] = '\0';
+ tfname[0] = 't';
+ if (!chksize(size)) {
+ (void) write(STDOUT_FILENO, "\2", (size_t)1);
+ continue;
+ }
+ if (!readfile(pp, tfname, (size_t)size)) {
+ rcleanup(0);
+ continue;
+ }
+ errmsg = ctl_renametf(pp->printer, tfname);
+ tfname[0] = '\0';
+ if (errmsg != NULL) {
+ frecverr("%s: %s", pp->printer, errmsg);
+ /*NOTREACHED*/
+ }
+ cfcnt++;
+ continue;
+
+ case '\3': /* read df file */
+ *givenid = '\0';
+ *givenhost = '\0';
+ size = 0;
+ while (*cp >= '0' && *cp <= '9')
+ size = size * 10 + (*cp++ - '0');
+ if (*cp++ != ' ')
+ break;
+ if (strchr(cp, '/')) {
+ frecverr("readjob: %s: illegal path name", cp);
+ /*NOTREACHED*/
+ }
+ if (!chksize(size)) {
+ (void) write(STDOUT_FILENO, "\2", (size_t)1);
+ continue;
+ }
+ strlcpy(dfname, cp, sizeof(dfname));
+ dfcnt++;
+ trstat_init(pp, dfname, dfcnt);
+ (void) readfile(pp, dfname, (size_t)size);
+ trstat_write(pp, TR_RECVING, (size_t)size, givenid,
+ from_host, givenhost);
+ continue;
+ }
+ frecverr("protocol screwup: %s", line);
+ /*NOTREACHED*/
+ }
+}
+
+/*
+ * Read files send by lpd and copy them to the spooling directory.
+ */
+static int
+readfile(struct printer *pp, char *file, size_t size)
+{
+ register char *cp;
+ char buf[BUFSIZ];
+ size_t amt, i;
+ int err, fd, j;
+
+ fd = open(file, O_CREAT|O_EXCL|O_WRONLY, FILMOD);
+ if (fd < 0) {
+ frecverr("%s: readfile: error on open(%s): %s",
+ pp->printer, file, strerror(errno));
+ /*NOTREACHED*/
+ }
+ ack();
+ err = 0;
+ for (i = 0; i < size; i += BUFSIZ) {
+ amt = BUFSIZ;
+ cp = buf;
+ if (i + amt > size)
+ amt = size - i;
+ do {
+ j = read(STDOUT_FILENO, cp, amt);
+ if (j <= 0) {
+ frecverr("%s: lost connection", pp->printer);
+ /*NOTREACHED*/
+ }
+ amt -= j;
+ cp += j;
+ } while (amt > 0);
+ amt = BUFSIZ;
+ if (i + amt > size)
+ amt = size - i;
+ if (write(fd, buf, amt) != (ssize_t)amt) {
+ err++;
+ break;
+ }
+ }
+ (void) close(fd);
+ if (err) {
+ frecverr("%s: write error on close(%s)", pp->printer, file);
+ /*NOTREACHED*/
+ }
+ if (noresponse()) { /* file sent had bad data in it */
+ if (strchr(file, '/') == NULL)
+ (void) unlink(file);
+ return (0);
+ }
+ ack();
+ return (1);
+}
+
+static int
+noresponse(void)
+{
+ char resp;
+
+ if (read(STDOUT_FILENO, &resp, (size_t)1) != 1) {
+ frecverr("lost connection in noresponse()");
+ /*NOTREACHED*/
+ }
+ if (resp == '\0')
+ return(0);
+ return(1);
+}
+
+/*
+ * Check to see if there is enough space on the disk for size bytes.
+ * 1 == OK, 0 == Not OK.
+ */
+static int
+chksize(int size)
+{
+ int spacefree;
+ struct statfs sfb;
+
+ if (statfs(".", &sfb) < 0) {
+ syslog(LOG_ERR, "%s: %m", "statfs(\".\")");
+ return (1);
+ }
+ spacefree = sfb.f_bavail * (sfb.f_bsize / 512);
+ size = (size + 511) / 512;
+ if (minfree + size > spacefree)
+ return(0);
+ return(1);
+}
+
+static int
+read_number(const char *fn)
+{
+ char lin[80];
+ register FILE *fp;
+
+ if ((fp = fopen(fn, "r")) == NULL)
+ return (0);
+ if (fgets(lin, 80, fp) == NULL) {
+ fclose(fp);
+ return (0);
+ }
+ fclose(fp);
+ return (atoi(lin));
+}
+
+/*
+ * Remove all the files associated with the current job being transfered.
+ */
+static void
+rcleanup(int signo __unused)
+{
+ if (tfname[0] && strchr(tfname, '/') == NULL)
+ (void) unlink(tfname);
+ if (dfname[0] && strchr(dfname, '/') == NULL) {
+ do {
+ do
+ (void) unlink(dfname);
+ while (dfname[2]-- != 'A');
+ dfname[2] = 'z';
+ } while (dfname[0]-- != 'd');
+ }
+ dfname[0] = '\0';
+}
+
+#include <stdarg.h>
+
+static void
+frecverr(const char *msg, ...)
+{
+ va_list ap;
+ va_start(ap, msg);
+ syslog(LOG_ERR, "Error receiving job from %s:", from_host);
+ vsyslog(LOG_ERR, msg, ap);
+ va_end(ap);
+ /*
+ * rcleanup is not called until AFTER logging the error message,
+ * because rcleanup will zap some variables which may have been
+ * supplied as parameters for that msg...
+ */
+ rcleanup(0);
+ /*
+ * Add a minimal delay before returning the final error code to
+ * the sending host. This just in case that machine responds
+ * this error by INSTANTLY retrying (and instantly re-failing...).
+ * It would be stupid of the sending host to do that, but if there
+ * was a broken implementation which did it, the result might be
+ * obscure performance problems and a flood of syslog messages on
+ * the receiving host.
+ */
+ sleep(2); /* a paranoid throttling measure */
+ putchar('\1'); /* return error code */
+ exit(1);
+}
diff --git a/usr.sbin/lpr/lpq/Makefile b/usr.sbin/lpr/lpq/Makefile
new file mode 100644
index 0000000..4df437e
--- /dev/null
+++ b/usr.sbin/lpr/lpq/Makefile
@@ -0,0 +1,16 @@
+# From: @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+BINDIR= /usr/bin
+
+PROG= lpq
+BINOWN= root
+BINGRP= daemon
+BINMODE= 6555
+
+CFLAGS+= -I${.CURDIR}/../common_source
+
+DPADD= ${LIBLPR}
+LDADD= ${LIBLPR}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/lpr/lpq/lpq.1 b/usr.sbin/lpr/lpq/lpq.1
new file mode 100644
index 0000000..170e9e2
--- /dev/null
+++ b/usr.sbin/lpr/lpq/lpq.1
@@ -0,0 +1,140 @@
+.\" Copyright (c) 1983, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)lpq.1 8.2 (Berkeley) 4/28/95
+.\" $FreeBSD$
+.\"
+.Dd April 28, 1995
+.Dt LPQ 1
+.Os
+.Sh NAME
+.Nm lpq
+.Nd spool queue examination program
+.Sh SYNOPSIS
+.Nm
+.Op Fl a
+.Op Fl l
+.Op Fl P Ns Ar printer
+.Op job # ...\&
+.Op user ...\&
+.Sh DESCRIPTION
+The
+.Nm
+utility examines the spooling area used by
+.Xr lpd 8
+for printing files on the line printer, and reports the status of the
+specified jobs or all jobs associated with a user.
+The
+.Nm
+utility invoked
+without any arguments reports on any jobs currently in the queue.
+.Pp
+Options:
+.Pp
+.Bl -tag -width indent
+.It Fl P
+Specify a particular printer, otherwise the default
+line printer is used (or the value of the
+.Ev PRINTER
+variable in the
+environment). All other arguments supplied are interpreted as user
+names or job numbers to filter out only those jobs of interest.
+.It Fl l
+Information about each of the files comprising the job entry
+is printed.
+Normally, only as much information as will fit on one line is displayed.
+.It Fl a
+Report on the local queues for all printers,
+rather than just the specified printer.
+.El
+.Pp
+For each job submitted (i.e. invocation of
+.Xr lpr 1 )
+.Nm
+reports the user's name, current rank in the queue, the
+names of files comprising the job, the job identifier (a number which
+may be supplied to
+.Xr lprm 1
+for removing a specific job), and the total size in bytes.
+Job ordering is dependent on
+the algorithm used to scan the spooling directory and is supposed
+to be
+.Tn FIFO
+(First in First Out).
+File names comprising a job may be unavailable
+(when
+.Xr lpr 1
+is used as a sink in a pipeline) in which case the file
+is indicated as ``(standard input)''.
+.Pp
+If
+.Nm
+warns that there is no daemon present (i.e. due to some malfunction),
+the
+.Xr lpc 8
+command can be used to restart the printer daemon.
+.Sh ENVIRONMENT
+If the following environment variable exists, it is used by
+.Nm :
+.Bl -tag -width PRINTER
+.It Ev PRINTER
+Specifies an alternate default printer.
+.El
+.Sh FILES
+.Bl -tag -width "/var/spool/*/lock" -compact
+.It Pa /etc/printcap
+To determine printer characteristics.
+.It Pa /var/spool/*
+The spooling directory, as determined from printcap.
+.It Pa /var/spool/*/cf*
+Control files specifying jobs.
+.It Pa /var/spool/*/lock
+The lock file to obtain the currently active job.
+.El
+.Sh SEE ALSO
+.Xr lpr 1 ,
+.Xr lprm 1 ,
+.Xr lpc 8 ,
+.Xr lpd 8
+.Sh HISTORY
+A
+.Nm
+utility appeared in
+.Bx 3 .
+.Sh BUGS
+Due to the dynamic nature of the information in the spooling directory
+.Nm
+may report unreliably.
+Output formatting is sensitive to the line length of the terminal;
+this can results in widely spaced columns.
+.Sh DIAGNOSTICS
+Unable to open various files. The lock file being malformed. Garbage
+files when there is no daemon active, but files in the spooling directory.
diff --git a/usr.sbin/lpr/lpq/lpq.c b/usr.sbin/lpr/lpq/lpq.c
new file mode 100644
index 0000000..734750d
--- /dev/null
+++ b/usr.sbin/lpr/lpq/lpq.c
@@ -0,0 +1,198 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#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(const struct printer *_pp);
+static void usage(void);
+int main(int _argc, char **_argv);
+
+int
+main(int argc, char **argv)
+{
+ int ch, aflag, lflag;
+ const char *printer;
+ struct printer myprinter, *pp = &myprinter;
+
+ printer = NULL;
+ euid = geteuid();
+ uid = getuid();
+ seteuid(uid);
+ progname = *argv;
+ if (gethostname(local_host, sizeof(local_host)))
+ err(1, "gethostname");
+ openlog("lpd", 0, LOG_LPR);
+
+ aflag = lflag = 0;
+ while ((ch = getopt(argc, argv, "alP:")) != -1)
+ switch((char)ch) {
+ case 'a':
+ ++aflag;
+ break;
+ case 'l': /* long output */
+ ++lflag;
+ break;
+ case 'P': /* printer name */
+ printer = optarg;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+
+ if (!aflag && printer == NULL && (printer = getenv("PRINTER")) == NULL)
+ printer = DEFLP;
+
+ for (argc -= optind, argv += optind; argc; --argc, ++argv)
+ if (isdigit(argv[0][0])) {
+ if (requests >= MAXREQUESTS)
+ fatal(0, "too many requests");
+ requ[requests++] = atoi(*argv);
+ }
+ else {
+ if (users >= MAXUSERS)
+ fatal(0, "too many users");
+ user[users++] = *argv;
+ }
+
+ if (aflag) {
+ int more, status;
+
+ more = firstprinter(pp, &status);
+ if (status)
+ goto looperr;
+ while (more) {
+ if (ckqueue(pp) > 0) {
+ printf("%s:\n", pp->printer);
+ displayq(pp, lflag);
+ printf("\n");
+ }
+ do {
+ more = nextprinter(pp, &status);
+looperr:
+ switch (status) {
+ case PCAPERR_TCOPEN:
+ printf("warning: %s: unresolved "
+ "tc= reference(s) ",
+ pp->printer);
+ case PCAPERR_SUCCESS:
+ break;
+ default:
+ fatal(pp, "%s", pcaperr(status));
+ }
+ } while (more && status);
+ }
+ } else {
+ int status;
+
+ init_printer(pp);
+ status = getprintcap(printer, pp);
+ if (status < 0)
+ fatal(pp, "%s", pcaperr(status));
+
+ displayq(pp, lflag);
+ }
+ exit(0);
+}
+
+static int
+ckqueue(const struct printer *pp)
+{
+ register struct dirent *d;
+ DIR *dirp;
+ char *spooldir;
+
+ spooldir = pp->spool_dir;
+ if ((dirp = opendir(spooldir)) == NULL)
+ return (-1);
+ while ((d = readdir(dirp)) != NULL) {
+ if (d->d_name[0] != 'c' || d->d_name[1] != 'f')
+ continue; /* daemon control files only */
+ closedir(dirp);
+ return (1); /* found something */
+ }
+ closedir(dirp);
+ return (0);
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr,
+ "usage: lpq [-a] [-l] [-Pprinter] [user ...] [job ...]\n");
+ exit(1);
+}
diff --git a/usr.sbin/lpr/lpr/Makefile b/usr.sbin/lpr/lpr/Makefile
new file mode 100644
index 0000000..1ff376d
--- /dev/null
+++ b/usr.sbin/lpr/lpr/Makefile
@@ -0,0 +1,19 @@
+# From: @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../common_source
+
+BINDIR= /usr/bin
+
+PROG= lpr
+MAN= lpr.1 printcap.5
+BINOWN= root
+BINGRP= daemon
+BINMODE= 6555
+
+CFLAGS+= -I${.CURDIR}/../common_source
+
+DPADD= ${LIBLPR}
+LDADD= ${LIBLPR}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/lpr/lpr/lpr.1 b/usr.sbin/lpr/lpr/lpr.1
new file mode 100644
index 0000000..2418485
--- /dev/null
+++ b/usr.sbin/lpr/lpr/lpr.1
@@ -0,0 +1,318 @@
+.\" Copyright (c) 1980, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" From @(#)lpr.1 8.1 (Berkeley) 6/6/93
+.\" $FreeBSD$
+.\" "
+.Dd June 6, 1993
+.Dt LPR 1
+.Os
+.Sh NAME
+.Nm lpr
+.Nd off line print
+.Sh SYNOPSIS
+.Nm
+.Op Fl P Ns Ar printer
+.Op Fl \&# Ns Ar num
+.Op Fl C Ar class
+.Op Fl J Ar job
+.Op Fl L Ar locale
+.Op Fl T Ar title
+.Op Fl U Ar user
+.Op Fl Z Ar daemon-options
+.Op Fl i Ar numcols
+.Op Fl 1234 Ar font
+.Op Fl w Ar num
+.Op Fl cdfghlnmprstv
+.Op Ar name ...
+.Sh DESCRIPTION
+The
+.Nm
+utility uses a spooling daemon to print the named files when facilities
+become available. If no names appear, the standard input is assumed.
+.Pp
+The following single letter options are used to notify the line printer
+spooler that the files are not standard text files.
+The spooling daemon will
+use the appropriate filters to print the data accordingly.
+Note that not all spoolers implement filters for all data types,
+and some sites may use these types for other purposes than the ones
+described here.
+.Bl -tag -width indent
+.It Fl d
+The files are assumed to contain data in
+.Tn DVI
+format from the
+.Tn TeX
+typesetting system.
+.It Fl f
+Use a filter which interprets the first character of each line as a
+standard
+.Tn FORTRAN
+carriage control character.
+.It Fl l
+Use a filter which allows control characters to be printed and suppresses
+page breaks.
+.It Fl p
+Use
+.Xr pr 1
+to format the files.
+.El
+.Pp
+The following options are historical and not directly supported by any
+software included in
+.Fx .
+.Bl -tag -width indent
+.It Fl c
+The files are assumed to contain data produced by
+.Xr cifplot 1 .
+.It Fl g
+The files are assumed to contain standard plot data as produced by the
+.Ux
+.Xr plot 3
+routines.
+.It Fl n
+The files are assumed to contain data from
+.Em ditroff
+(device independent troff).
+.It Fl t
+The files are assumed to contain
+.Tn C/A/T
+phototypesetter commands from ancient versions of
+.Ux
+.Xr troff 1 .
+.It Fl v
+The files are assumed to contain a raster image for devices like the
+Benson Varian.
+.El
+.Pp
+These options apply to the handling of
+the print job:
+.Bl -tag -width indent
+.It Fl P
+Force output to a specific printer. Normally,
+the default printer is used (site dependent), or the value of the
+environment variable
+.Ev PRINTER
+is used.
+.It Fl h
+Suppress the printing of the burst page.
+.It Fl m
+Send mail upon completion.
+.It Fl r
+Remove the file upon completion of spooling or upon completion of
+printing (with the
+.Fl s
+option).
+.It Fl s
+Use symbolic links. Usually files are copied to the spool directory.
+The
+.Fl s
+option will use
+.Xr symlink 2
+to link data files rather than trying to copy them so large files can be
+printed. This means the files should
+not be modified or removed until they have been printed.
+.El
+.Pp
+The remaining options apply to copies, the page display, and headers:
+.Bl -tag -width indent
+.It Fl \&# Ns Ar num
+The quantity
+.Ar num
+is the number of copies desired of each file named. For example,
+.Bd -literal -offset indent
+lpr \-#3 foo.c bar.c more.c
+.Ed
+would result in 3 copies of the file foo.c, followed by 3 copies
+of the file bar.c, etc. On the other hand,
+.Bd -literal -offset indent
+cat foo.c bar.c more.c \&| lpr \-#3
+.Ed
+.Pp
+will give three copies of the concatenation of the files.
+Often
+a site will disable this feature to encourage use of a photocopier
+instead.
+.It Xo
+.Fl Ns Op Cm 1234
+.Ar font
+.Xc
+Specifies a
+.Ar font
+to be mounted on font position
+.Ar i .
+The daemon
+will construct a
+.Li .railmag
+file referencing
+the font pathname.
+.It Fl C Ar class
+Job classification
+to use on the burst page. For example,
+.Bd -literal -offset indent
+lpr \-C EECS foo.c
+.Ed
+.Pp
+causes the system name (the name returned by
+.Xr hostname 1 )
+to be replaced on the burst page by
+.Tn EECS ,
+and the file foo.c to be printed.
+.It Fl J Ar job
+Job name to print on the burst page.
+Normally, the first file's name is used.
+.It Fl L Ar locale
+Use
+.Ar locale
+specified as argument instead of one found in environment.
+(Only effective when filtering through
+.Xr pr 1
+is requested using the
+.Fl p
+option.)
+.It Fl T Ar title
+Title name for
+.Xr pr 1 ,
+instead of the file name.
+.It Fl U Ar user
+User name to print on the burst page,
+also for accounting purposes.
+This option is only honored if the real user-id is daemon
+(or that specified in the printcap file instead of daemon),
+and is intended for those instances where print filters wish to requeue jobs.
+.It Fl Z Ar daemon-options
+Some spoolers, such as
+.Tn LPRng ,
+accept additional per-job options using a
+.Ql Z
+control line.
+When
+.Fl Z
+is specified, and
+.Fl p
+.Pq Xr pr 1
+is not requested, the specified
+.Ar daemon-options
+will be passed to the remote
+.Tn LPRng
+spooler.
+.It Fl i Ar numcols
+The output is indented by
+.Pq Ar numcols .
+.It Fl w Ar num
+Uses
+.Ar num
+as the page width for
+.Xr pr 1 .
+.El
+.Sh ENVIRONMENT
+If the following environment variable exists, it is used by
+.Nm :
+.Bl -tag -width PRINTER
+.It Ev PRINTER
+Specifies an alternate default printer.
+.El
+.Sh FILES
+.Bl -tag -width /var/spool/output/*/tf* -compact
+.It Pa /etc/passwd
+Personal identification.
+.It Pa /etc/printcap
+Printer capabilities data base.
+.It Pa /usr/sbin/lpd
+Line printer daemons.
+.It Pa /var/spool/output/*
+Directories used for spooling.
+.It Pa /var/spool/output/*/cf*
+Daemon control files.
+.It Pa /var/spool/output/*/df*
+Data files specified in "cf" files.
+.It Pa /var/spool/output/*/tf*
+Temporary copies of "cf" files.
+.El
+.Sh SEE ALSO
+.Xr lpq 1 ,
+.Xr lprm 1 ,
+.Xr pr 1 ,
+.Xr symlink 2 ,
+.Xr printcap 5 ,
+.Xr lpc 8 ,
+.Xr lpd 8
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 3 .
+.Sh DIAGNOSTICS
+If you try to spool too large a file, it will be truncated.
+The
+.Nm
+utility will object to printing binary files.
+If a user other than root prints a file and spooling is disabled,
+.Nm
+will print a message saying so and will not put jobs in the queue.
+If a connection to
+.Xr lpd 8
+on the local machine cannot be made,
+.Nm
+will say that the daemon cannot be started.
+Diagnostics may be printed in the daemon's log file
+regarding missing spool files by
+.Xr lpd 8 .
+.Sh BUGS
+Fonts for
+.Xr troff 1
+and
+.Tn TeX
+reside on the host with the printer.
+It is currently not possible to
+use local font libraries.
+.Pp
+The
+.Ql Z
+control file line is used for two different purposes; for
+standard
+.Fx
+.Xr lpd 8 ,
+it specifies a locale to be passed to
+.Xr pr 1 .
+For
+.Tn LPRng
+.Xr lpd 8 ,
+it specifies additional options to be interpreted by the spooler's
+input and output filters.
+When submitting jobs via
+.Nm ,
+.Fl p
+.Fl L Ar locale
+is used in the former context, and
+.Fl Z Ar daemon-options
+is used in the latter.
diff --git a/usr.sbin/lpr/lpr/lpr.c b/usr.sbin/lpr/lpr/lpr.c
new file mode 100644
index 0000000..04caa8c
--- /dev/null
+++ b/usr.sbin/lpr/lpr/lpr.c
@@ -0,0 +1,890 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#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 <netinet/in.h> /* N_BADMAG uses ntohl() */
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <a.out.h>
+#include <err.h>
+#include <locale.h>
+#include <signal.h>
+#include <syslog.h>
+#include <pwd.h>
+#include <grp.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include "lp.h"
+#include "lp.local.h"
+#include "pathnames.h"
+
+static char *cfname; /* daemon control files, linked from tf's */
+static char *class = local_host; /* class title on header page */
+static char *dfname; /* data files */
+static char *fonts[4]; /* troff font names */
+static char format = 'f'; /* format char for printing files */
+static int hdr = 1; /* print header or not (default is yes) */
+static int iflag; /* indentation wanted */
+static int inchar; /* location to increment char in file names */
+static int indent; /* amount to indent */
+static const char *jobname; /* job name on header page */
+static int mailflg; /* send mail */
+static int nact; /* number of jobs to act on */
+static int ncopies = 1; /* # of copies to make */
+static char *lpr_username; /* person sending the print job(s) */
+static int qflag; /* q job, but don't exec daemon */
+static int rflag; /* remove files upon completion */
+static int sflag; /* symbolic link flag */
+static int tfd; /* control file descriptor */
+static char *tfname; /* tmp copy of cf before linking */
+static char *title; /* pr'ing title */
+static char *locale; /* pr'ing locale */
+static int userid; /* user id */
+static char *Uflag; /* user name specified with -U flag */
+static char *width; /* width for versatec printing */
+static char *Zflag; /* extra filter options for LPRng servers */
+
+static struct stat statb;
+
+static void card(int _c, const char *_p2);
+static int checkwriteperm(const char *_file, const char *_directory);
+static void chkprinter(const char *_ptrname, struct printer *_pp);
+static void cleanup(int _signo);
+static void copy(const struct printer *_pp, int _f, const char _n[]);
+static char *itoa(int _i);
+static const char *linked(const char *_file);
+int main(int _argc, char *_argv[]);
+static char *lmktemp(const struct printer *_pp, const char *_id,
+ int _num, int len);
+static void mktemps(const struct printer *_pp);
+static int nfile(char *_n);
+static int test(const char *_file);
+static void usage(void);
+
+uid_t uid, euid;
+
+int
+main(int argc, char *argv[])
+{
+ struct passwd *pw;
+ struct group *gptr;
+ const char *arg, *cp, *printer;
+ char *p;
+ char buf[BUFSIZ];
+ int c, i, f, errs;
+ int ret, didlink;
+ struct stat stb;
+ struct stat statb1, statb2;
+ struct printer myprinter, *pp = &myprinter;
+
+ printer = NULL;
+ euid = geteuid();
+ uid = getuid();
+ seteuid(uid);
+ if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
+ signal(SIGHUP, cleanup);
+ if (signal(SIGINT, SIG_IGN) != SIG_IGN)
+ signal(SIGINT, cleanup);
+ if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
+ signal(SIGQUIT, cleanup);
+ if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
+ signal(SIGTERM, cleanup);
+
+ progname = argv[0];
+ gethostname(local_host, sizeof(local_host));
+ openlog("lpd", 0, LOG_LPR);
+
+ errs = 0;
+ while ((c = getopt(argc, argv,
+ ":#:1:2:3:4:C:J:L:P:T:U:Z:cdfghi:lnmprstvw:"))
+ != -1)
+ switch (c) {
+ case '#': /* n copies */
+ i = strtol(optarg, &p, 10);
+ if (*p)
+ errx(1, "Bad argument to -#, number expected");
+ if (i > 0)
+ ncopies = i;
+ break;
+
+ case '1': /* troff fonts */
+ case '2':
+ case '3':
+ case '4':
+ fonts[optopt - '1'] = optarg;
+ break;
+
+ case 'C': /* classification spec */
+ hdr++;
+ class = optarg;
+ break;
+
+ case 'J': /* job name */
+ hdr++;
+ jobname = optarg;
+ break;
+
+ case 'P': /* specifiy printer name */
+ printer = optarg;
+ break;
+
+ case 'L': /* pr's locale */
+ locale = optarg;
+ break;
+
+ case 'T': /* pr's title line */
+ title = optarg;
+ break;
+
+ case 'U': /* user name */
+ hdr++;
+ Uflag = optarg;
+ break;
+
+ case 'Z':
+ Zflag = optarg;
+ break;
+
+ case 'c': /* print cifplot output */
+ case 'd': /* print tex output (dvi files) */
+ case 'g': /* print graph(1G) output */
+ case 'l': /* literal output */
+ case 'n': /* print ditroff output */
+ case 't': /* print troff output (cat files) */
+ case 'p': /* print using ``pr'' */
+ case 'v': /* print vplot output */
+ format = optopt;
+ break;
+
+ case 'f': /* print fortran output */
+ format = 'r';
+ break;
+
+ case 'h': /* nulifiy header page */
+ hdr = 0;
+ break;
+
+ case 'i': /* indent output */
+ iflag++;
+ indent = strtol(optarg, &p, 10);
+ if (*p)
+ errx(1, "Bad argument to -i, number expected");
+ break;
+
+ case 'm': /* send mail when done */
+ mailflg++;
+ break;
+
+ case 'q': /* just queue job */
+ qflag++;
+ break;
+
+ case 'r': /* remove file when done */
+ rflag++;
+ break;
+
+ case 's': /* try to link files */
+ sflag++;
+ break;
+
+ case 'w': /* versatec page width */
+ width = optarg;
+ break;
+
+ case ':': /* catch "missing argument" error */
+ if (optopt == 'i') {
+ iflag++; /* -i without args is valid */
+ indent = 8;
+ } else
+ errs++;
+ break;
+
+ default:
+ errs++;
+ }
+ argc -= optind;
+ argv += optind;
+ if (errs)
+ usage();
+ if (printer == NULL && (printer = getenv("PRINTER")) == NULL)
+ printer = DEFLP;
+ chkprinter(printer, pp);
+ if (pp->no_copies && ncopies > 1)
+ errx(1, "multiple copies are not allowed");
+ if (pp->max_copies > 0 && ncopies > pp->max_copies)
+ errx(1, "only %ld copies are allowed", pp->max_copies);
+ /*
+ * Get the identity of the person doing the lpr using the same
+ * algorithm as lprm. Actually, not quite -- lprm will override
+ * the login name with "root" if the user is running as root;
+ * the daemon actually checks for the string "root" in its
+ * permission checking. Sigh.
+ */
+ userid = getuid();
+ if (Uflag) {
+ if (userid != 0 && userid != pp->daemon_user)
+ errx(1, "only privileged users may use the `-U' flag");
+ lpr_username = Uflag; /* -U person doing 'lpr' */
+ } else {
+ lpr_username = getlogin(); /* person doing 'lpr' */
+ if (userid != pp->daemon_user || lpr_username == 0) {
+ if ((pw = getpwuid(userid)) == NULL)
+ errx(1, "Who are you?");
+ lpr_username = pw->pw_name;
+ }
+ }
+
+ /*
+ * Check for restricted group access.
+ */
+ if (pp->restrict_grp != NULL && userid != pp->daemon_user) {
+ if ((gptr = getgrnam(pp->restrict_grp)) == NULL)
+ errx(1, "Restricted group specified incorrectly");
+ if (gptr->gr_gid != getgid()) {
+ while (*gptr->gr_mem != NULL) {
+ if ((strcmp(lpr_username, *gptr->gr_mem)) == 0)
+ break;
+ gptr->gr_mem++;
+ }
+ if (*gptr->gr_mem == NULL)
+ errx(1, "Not a member of the restricted group");
+ }
+ }
+ /*
+ * Check to make sure queuing is enabled if userid is not root.
+ */
+ lock_file_name(pp, buf, sizeof buf);
+ if (userid && stat(buf, &stb) == 0 && (stb.st_mode & LFM_QUEUE_DIS))
+ errx(1, "Printer queue is disabled");
+ /*
+ * Initialize the control file.
+ */
+ mktemps(pp);
+ tfd = nfile(tfname);
+ seteuid(euid);
+ (void) fchown(tfd, pp->daemon_user, -1);
+ /* owned by daemon for protection */
+ seteuid(uid);
+ card('H', local_host);
+ card('P', lpr_username);
+ card('C', class);
+ if (hdr && !pp->no_header) {
+ if (jobname == NULL) {
+ if (argc == 0)
+ jobname = "stdin";
+ else
+ jobname = ((arg = strrchr(argv[0], '/'))
+ ? arg + 1 : argv[0]);
+ }
+ card('J', jobname);
+ card('L', lpr_username);
+ }
+ if (format != 'p' && Zflag != 0)
+ card('Z', Zflag);
+ if (iflag)
+ card('I', itoa(indent));
+ if (mailflg)
+ card('M', lpr_username);
+ if (format == 't' || format == 'n' || format == 'd')
+ for (i = 0; i < 4; i++)
+ if (fonts[i] != NULL)
+ card('1'+i, fonts[i]);
+ if (width != NULL)
+ card('W', width);
+ /*
+ * XXX
+ * Our use of `Z' here is incompatible with LPRng's
+ * use. We assume that the only use of our existing
+ * `Z' card is as shown for `p' format (pr) files.
+ */
+ if (format == 'p') {
+ char *s;
+
+ if (locale)
+ card('Z', locale);
+ else if ((s = setlocale(LC_TIME, "")) != NULL)
+ card('Z', s);
+ }
+
+ /*
+ * Read the files and spool them.
+ */
+ if (argc == 0)
+ copy(pp, 0, " ");
+ else while (argc--) {
+ if (argv[0][0] == '-' && argv[0][1] == '\0') {
+ /* use stdin */
+ copy(pp, 0, " ");
+ argv++;
+ continue;
+ }
+ if ((f = test(arg = *argv++)) < 0)
+ continue; /* file unreasonable */
+
+ if (sflag && (cp = linked(arg)) != NULL) {
+ (void) snprintf(buf, sizeof(buf), "%d %d", statb.st_dev,
+ statb.st_ino);
+ card('S', buf);
+ if (format == 'p')
+ card('T', title ? title : arg);
+ for (i = 0; i < ncopies; i++)
+ card(format, &dfname[inchar-2]);
+ card('U', &dfname[inchar-2]);
+ if (f)
+ card('U', cp);
+ card('N', arg);
+ dfname[inchar]++;
+ nact++;
+ continue;
+ }
+ if (sflag)
+ printf("%s: %s: not linked, copying instead\n",
+ progname, arg);
+
+ if (f) {
+ /*
+ * The user wants the file removed after it is copied
+ * to the spool area, so see if the file can be moved
+ * instead of copy/unlink'ed. This is much faster and
+ * uses less spool space than copying the file. This
+ * can be very significant when running services like
+ * samba, pcnfs, CAP, et al.
+ */
+ seteuid(euid);
+ didlink = 0;
+ /*
+ * There are several things to check to avoid any
+ * security issues. Some of these are redundant
+ * under BSD's, but are necessary when lpr is built
+ * under some other OS's (which I do do...)
+ */
+ if (lstat(arg, &statb1) < 0)
+ goto nohardlink;
+ if (S_ISLNK(statb1.st_mode))
+ goto nohardlink;
+ if (link(arg, dfname) != 0)
+ goto nohardlink;
+ didlink = 1;
+ /*
+ * Make sure the user hasn't tried to trick us via
+ * any race conditions
+ */
+ if (lstat(dfname, &statb2) < 0)
+ goto nohardlink;
+ if (statb1.st_dev != statb2.st_dev)
+ goto nohardlink;
+ if (statb1.st_ino != statb2.st_ino)
+ goto nohardlink;
+ /*
+ * Skip if the file already had multiple hard links,
+ * because changing the owner and access-bits would
+ * change ALL versions of the file
+ */
+ if (statb2.st_nlink > 2)
+ goto nohardlink;
+ /*
+ * If we can access and remove the original file
+ * without special setuid-ness then this method is
+ * safe. Otherwise, abandon the move and fall back
+ * to the (usual) copy method.
+ */
+ seteuid(uid);
+ ret = access(dfname, R_OK);
+ if (ret == 0)
+ ret = unlink(arg);
+ seteuid(euid);
+ if (ret != 0)
+ goto nohardlink;
+ /*
+ * Unlink of user file was successful. Change the
+ * owner and permissions, add entries to the control
+ * file, and skip the file copying step.
+ */
+ chown(dfname, pp->daemon_user, getegid());
+ chmod(dfname, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+ seteuid(uid);
+ if (format == 'p')
+ card('T', title ? title : arg);
+ for (i = 0; i < ncopies; i++)
+ card(format, &dfname[inchar-2]);
+ card('U', &dfname[inchar-2]);
+ card('N', arg);
+ nact++;
+ continue;
+ nohardlink:
+ if (didlink)
+ unlink(dfname);
+ seteuid(uid); /* restore old uid */
+ } /* end: if (f) */
+
+ if ((i = open(arg, O_RDONLY)) < 0) {
+ printf("%s: cannot open %s\n", progname, arg);
+ } else {
+ copy(pp, i, arg);
+ (void) close(i);
+ if (f && unlink(arg) < 0)
+ printf("%s: %s: not removed\n", progname, arg);
+ }
+ }
+
+ if (nact) {
+ (void) close(tfd);
+ tfname[inchar]--;
+ /*
+ * Touch the control file to fix position in the queue.
+ */
+ seteuid(euid);
+ if ((tfd = open(tfname, O_RDWR)) >= 0) {
+ char touch_c;
+
+ if (read(tfd, &touch_c, 1) == 1 &&
+ lseek(tfd, (off_t)0, 0) == 0 &&
+ write(tfd, &touch_c, 1) != 1) {
+ printf("%s: cannot touch %s\n", progname,
+ tfname);
+ tfname[inchar]++;
+ cleanup(0);
+ }
+ (void) close(tfd);
+ }
+ if (link(tfname, cfname) < 0) {
+ printf("%s: cannot rename %s\n", progname, cfname);
+ tfname[inchar]++;
+ cleanup(0);
+ }
+ unlink(tfname);
+ seteuid(uid);
+ if (qflag) /* just q things up */
+ exit(0);
+ if (!startdaemon(pp))
+ printf("jobs queued, but cannot start daemon.\n");
+ exit(0);
+ }
+ cleanup(0);
+ return (1);
+ /* NOTREACHED */
+}
+
+/*
+ * Create the file n and copy from file descriptor f.
+ */
+static void
+copy(const struct printer *pp, int f, const char n[])
+{
+ register int fd, i, nr, nc;
+ char buf[BUFSIZ];
+
+ if (format == 'p')
+ card('T', title ? title : n);
+ for (i = 0; i < ncopies; i++)
+ card(format, &dfname[inchar-2]);
+ card('U', &dfname[inchar-2]);
+ card('N', n);
+ fd = nfile(dfname);
+ nr = nc = 0;
+ while ((i = read(f, buf, BUFSIZ)) > 0) {
+ if (write(fd, buf, i) != i) {
+ printf("%s: %s: temp file write error\n", progname, n);
+ break;
+ }
+ nc += i;
+ if (nc >= BUFSIZ) {
+ nc -= BUFSIZ;
+ nr++;
+ if (pp->max_blocks > 0 && nr > pp->max_blocks) {
+ printf("%s: %s: copy file is too large\n",
+ progname, n);
+ break;
+ }
+ }
+ }
+ (void) close(fd);
+ if (nc==0 && nr==0)
+ printf("%s: %s: empty input file\n", progname,
+ f ? n : "stdin");
+ else
+ nact++;
+}
+
+/*
+ * Try and link the file to dfname. Return a pointer to the full
+ * path name if successful.
+ */
+static const char *
+linked(const char *file)
+{
+ register char *cp;
+ static char buf[MAXPATHLEN];
+ register int ret;
+
+ if (*file != '/') {
+ if (getcwd(buf, sizeof(buf)) == NULL)
+ return(NULL);
+ while (file[0] == '.') {
+ switch (file[1]) {
+ case '/':
+ file += 2;
+ continue;
+ case '.':
+ if (file[2] == '/') {
+ if ((cp = strrchr(buf, '/')) != NULL)
+ *cp = '\0';
+ file += 3;
+ continue;
+ }
+ }
+ break;
+ }
+ strncat(buf, "/", sizeof(buf) - strlen(buf) - 1);
+ strncat(buf, file, sizeof(buf) - strlen(buf) - 1);
+ file = buf;
+ }
+ seteuid(euid);
+ ret = symlink(file, dfname);
+ seteuid(uid);
+ return(ret ? NULL : file);
+}
+
+/*
+ * Put a line into the control file.
+ */
+static void
+card(int c, const char *p2)
+{
+ char buf[BUFSIZ];
+ register char *p1 = buf;
+ size_t len = 2;
+
+ *p1++ = c;
+ while ((c = *p2++) != '\0' && len < sizeof(buf)) {
+ *p1++ = (c == '\n') ? ' ' : c;
+ len++;
+ }
+ *p1++ = '\n';
+ write(tfd, buf, len);
+}
+
+/*
+ * Create a new file in the spool directory.
+ */
+static int
+nfile(char *n)
+{
+ register int f;
+ int oldumask = umask(0); /* should block signals */
+
+ seteuid(euid);
+ f = open(n, O_WRONLY | O_EXCL | O_CREAT, FILMOD);
+ (void) umask(oldumask);
+ if (f < 0) {
+ printf("%s: cannot create %s\n", progname, n);
+ cleanup(0);
+ }
+ if (fchown(f, userid, -1) < 0) {
+ printf("%s: cannot chown %s\n", progname, n);
+ cleanup(0); /* cleanup does exit */
+ }
+ seteuid(uid);
+ if (++n[inchar] > 'z') {
+ if (++n[inchar-2] == 't') {
+ printf("too many files - break up the job\n");
+ cleanup(0);
+ }
+ n[inchar] = 'A';
+ } else if (n[inchar] == '[')
+ n[inchar] = 'a';
+ return(f);
+}
+
+/*
+ * Cleanup after interrupts and errors.
+ */
+static void
+cleanup(int signo __unused)
+{
+ register int i;
+
+ signal(SIGHUP, SIG_IGN);
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGTERM, SIG_IGN);
+ i = inchar;
+ seteuid(euid);
+ if (tfname)
+ do
+ unlink(tfname);
+ while (tfname[i]-- != 'A');
+ if (cfname)
+ do
+ unlink(cfname);
+ while (cfname[i]-- != 'A');
+ if (dfname)
+ do {
+ do
+ unlink(dfname);
+ while (dfname[i]-- != 'A');
+ dfname[i] = 'z';
+ } while (dfname[i-2]-- != 'd');
+ exit(1);
+}
+
+/*
+ * Test to see if this is a printable file.
+ * Return -1 if it is not, 0 if its printable, and 1 if
+ * we should remove it after printing.
+ */
+static int
+test(const char *file)
+{
+ struct exec execb;
+ size_t dlen;
+ int fd;
+ char *cp, *dirpath;
+
+ if (access(file, 4) < 0) {
+ printf("%s: cannot access %s\n", progname, file);
+ return(-1);
+ }
+ if (stat(file, &statb) < 0) {
+ printf("%s: cannot stat %s\n", progname, file);
+ return(-1);
+ }
+ if ((statb.st_mode & S_IFMT) == S_IFDIR) {
+ printf("%s: %s is a directory\n", progname, file);
+ return(-1);
+ }
+ if (statb.st_size == 0) {
+ printf("%s: %s is an empty file\n", progname, file);
+ return(-1);
+ }
+ if ((fd = open(file, O_RDONLY)) < 0) {
+ printf("%s: cannot open %s\n", progname, file);
+ return(-1);
+ }
+ /*
+ * XXX Shall we add a similar test for ELF?
+ */
+ if (read(fd, &execb, sizeof(execb)) == sizeof(execb) &&
+ !N_BADMAG(execb)) {
+ printf("%s: %s is an executable program", progname, file);
+ goto error1;
+ }
+ (void) close(fd);
+ if (rflag) {
+ /*
+ * aside: note that 'cp' is technically a 'const char *'
+ * (because it points into 'file'), even though strrchr
+ * returns a value of type 'char *'.
+ */
+ if ((cp = strrchr(file, '/')) == NULL) {
+ if (checkwriteperm(file,".") == 0)
+ return(1);
+ } else {
+ if (cp == file) {
+ fd = checkwriteperm(file,"/");
+ } else {
+ /* strlcpy will change the '/' to '\0' */
+ dlen = cp - file + 1;
+ dirpath = malloc(dlen);
+ strlcpy(dirpath, file, dlen);
+ fd = checkwriteperm(file, dirpath);
+ free(dirpath);
+ }
+ if (fd == 0)
+ return(1);
+ }
+ printf("%s: %s: is not removable by you\n", progname, file);
+ }
+ return(0);
+
+error1:
+ printf(" and is unprintable\n");
+ (void) close(fd);
+ return(-1);
+}
+
+static int
+checkwriteperm(const char *file, const char *directory)
+{
+ struct stat stats;
+ if (access(directory, W_OK) == 0) {
+ stat(directory, &stats);
+ if (stats.st_mode & S_ISVTX) {
+ stat(file, &stats);
+ if(stats.st_uid == userid) {
+ return(0);
+ }
+ } else return(0);
+ }
+ return(-1);
+}
+
+/*
+ * itoa - integer to string conversion
+ */
+static char *
+itoa(int i)
+{
+ static char b[10] = "########";
+ register char *p;
+
+ p = &b[8];
+ do
+ *p-- = i%10 + '0';
+ while (i /= 10);
+ return(++p);
+}
+
+/*
+ * Perform lookup for printer name or abbreviation --
+ */
+static void
+chkprinter(const char *ptrname, struct printer *pp)
+{
+ int status;
+
+ init_printer(pp);
+ status = getprintcap(ptrname, pp);
+ switch(status) {
+ case PCAPERR_OSERR:
+ case PCAPERR_TCLOOP:
+ errx(1, "%s: %s", ptrname, pcaperr(status));
+ case PCAPERR_NOTFOUND:
+ errx(1, "%s: unknown printer", ptrname);
+ case PCAPERR_TCOPEN:
+ warnx("%s: unresolved tc= reference(s)", ptrname);
+ }
+}
+
+/*
+ * Tell the user what we wanna get.
+ */
+static void
+usage(void)
+{
+ fprintf(stderr, "%s\n",
+"usage: lpr [-Pprinter] [-#num] [-C class] [-J job] [-T title] [-U user]\n"
+ "\t[-Z daemon-options] [-i[numcols]] [-i[numcols]] [-1234 font]\n"
+ "\t[-L locale] [-wnum] [-cdfghlnmprstv] [name ...]");
+ exit(1);
+}
+
+
+/*
+ * Make the temp files.
+ */
+static void
+mktemps(const struct printer *pp)
+{
+ register int len, fd, n;
+ register char *cp;
+ char buf[BUFSIZ];
+
+ (void) snprintf(buf, sizeof(buf), "%s/.seq", pp->spool_dir);
+ seteuid(euid);
+ if ((fd = open(buf, O_RDWR|O_CREAT, 0661)) < 0) {
+ printf("%s: cannot create %s\n", progname, buf);
+ exit(1);
+ }
+ if (flock(fd, LOCK_EX)) {
+ printf("%s: cannot lock %s\n", progname, buf);
+ exit(1);
+ }
+ seteuid(uid);
+ n = 0;
+ if ((len = read(fd, buf, sizeof(buf))) > 0) {
+ for (cp = buf; len--; ) {
+ if (*cp < '0' || *cp > '9')
+ break;
+ n = n * 10 + (*cp++ - '0');
+ }
+ }
+ len = strlen(pp->spool_dir) + strlen(local_host) + 8;
+ tfname = lmktemp(pp, "tf", n, len);
+ cfname = lmktemp(pp, "cf", n, len);
+ dfname = lmktemp(pp, "df", n, len);
+ inchar = strlen(pp->spool_dir) + 3;
+ n = (n + 1) % 1000;
+ (void) lseek(fd, (off_t)0, 0);
+ snprintf(buf, sizeof(buf), "%03d\n", n);
+ (void) write(fd, buf, strlen(buf));
+ (void) close(fd); /* unlocks as well */
+}
+
+/*
+ * Make a temp file name.
+ */
+static char *
+lmktemp(const struct printer *pp, const char *id, int num, int len)
+{
+ register char *s;
+
+ if ((s = malloc(len)) == NULL)
+ errx(1, "out of memory");
+ (void) snprintf(s, len, "%s/%sA%03d%s", pp->spool_dir, id, num,
+ local_host);
+ return(s);
+}
diff --git a/usr.sbin/lpr/lpr/printcap.5 b/usr.sbin/lpr/lpr/printcap.5
new file mode 100644
index 0000000..7131a41
--- /dev/null
+++ b/usr.sbin/lpr/lpr/printcap.5
@@ -0,0 +1,426 @@
+.\" Copyright (c) 1983, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)printcap.5 8.2 (Berkeley) 12/11/93
+.\" $FreeBSD$
+.\"
+.Dd October 11, 2000
+.Dt PRINTCAP 5
+.Os
+.Sh NAME
+.Nm printcap
+.Nd printer capability data base
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+The
+.Nm Printcap
+function
+is a simplified version of the
+.Xr termcap 5
+data base
+used to describe line printers. The spooling system accesses the
+.Nm
+file every time it is used, allowing dynamic
+addition and deletion of printers. Each entry in the data base
+is used to describe one printer. This data base may not be
+substituted for, as is possible for
+.Xr termcap 5 ,
+because it may allow accounting to be bypassed.
+.Pp
+The default printer is normally
+.Em lp ,
+though the environment variable
+.Ev PRINTER
+may be used to override this. Each spooling utility supports an option,
+.Fl P Ar printer ,
+to allow explicit naming of a destination printer.
+.Pp
+Refer to the
+.%T "4.3 BSD Line Printer Spooler Manual"
+for a complete discussion on how to setup the database for a given printer.
+.Sh CAPABILITIES
+Refer to
+.Xr termcap 5
+for a description of the file layout.
+.Bl -column Namexxx Typexx "/var/spool/lpdxxxxx"
+.Sy "Name Type Default Description
+.It "af str" Ta Dv NULL Ta No "name of accounting file"
+.It "br num none if lp is a tty, set the baud rate"
+.Xr ( ioctl 2
+call)
+.It "cf str" Ta Dv NULL Ta No "cifplot data filter"
+.It "ct num 120 TCP connection timeout in seconds"
+.It "df str" Ta Dv NULL Ta No "tex data filter"
+.Tn ( DVI
+format)
+.It "ff str" Ta So Li \ef Sc Ta No "string to send for a form feed"
+.It "fo bool false print a form feed when device is opened"
+.It "gf str" Ta Dv NULL Ta No "graph data filter"
+.Xr ( plot 3
+format
+.It "hl bool false print the burst header page last"
+.It "ic bool false driver supports (non standard) ioctl to indent printout"
+.It "if str" Ta Dv NULL Ta No "name of text filter which does accounting"
+.It "lf str" Ta Pa /dev/console Ta No "error logging file name"
+.It "lo str" Ta Pa lock Ta No "name of lock file"
+.It "lp str" Ta Pa /dev/lp Ta No "device name to open for output"
+.It "mc num 0 maximum number of copies which can be requested on"
+.Xr lpr 1 ,
+zero = unlimited
+.It "ms str" Ta Dv NULL Ta No "if lp is a tty, a comma-separated,"
+.Xr stty 1 Ns -like
+list describing the tty modes
+.It "mx num 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 "rc bool false when sending to a remote host, resend copies (see below)"
+.It "rf str" Ta Dv NULL Ta No "filter for printing"
+.Tn FORTRAN
+style text files
+.It "rg str" Ta Dv NULL Ta No "restricted group. Only members of group allowed access"
+.It "rm str" Ta Dv NULL Ta No "machine name for remote printer"
+.It "rp str" Ta Pa lp Ta No "remote printer name argument"
+.It "rs bool false restrict remote users to those with local accounts"
+.It "rw bool false open the printer device for reading and writing"
+.It "sb bool false short banner (one line only)"
+.It "sc bool false suppress multiple copies"
+.It "sd str" Ta Pa /var/spool/lpd Ta No "spool directory"
+.It "sf bool false suppress form feeds"
+.It "sh bool false suppress printing of burst page header"
+.It "sr str" Ta Dv NULL Ta No "file name to hold statistics of each datafile as it is received"
+.It "ss str" Ta Dv NULL Ta No "file name to hold statistics of each datafile as it is sent"
+.It "st str" Ta Pa status Ta No "status file name"
+.It "tf str" Ta Dv NULL Ta No "troff data filter (cat phototypesetter)"
+.It "tr str" Ta Dv NULL Ta No "trailer string to print when queue empties"
+.It "vf str" Ta Dv NULL Ta No "raster image filter"
+.El
+.Pp
+Each two-letter capability has a human-readable alternate name.
+.Bl -column "Short form" "Long form"
+.Sy "Short form Long form"
+.It "af acct.file"
+.It "br tty.rate"
+.It "cf filt.cifplot"
+.It "ct remote.timeout"
+.It "df filt.dvi"
+.It "du daemon.user"
+.It "ff job.formfeed"
+.It "fo job.topofform"
+.It "gf filt.plot"
+.It "hl banner.last"
+.It "if filt.input"
+.It "lf spool.log"
+.It "lo spool.lock"
+.It "lp tty.device"
+.It "mc max.copies"
+.It "ms tty.mode"
+.It "mx max.blocks"
+.It "nf filt.ditroff"
+.It "of filt.output"
+.It "pc acct.price"
+.It "pl page.length"
+.It "pw page.width"
+.It "px page.pwidth"
+.It "py page.plength"
+.It "rc remote.resend_copies"
+.It "rf filt.fortran"
+.It "rg daemon.restrictgrp"
+.It "rm remote.host"
+.It "rp remote.queue"
+.It "rs daemon.restricted"
+.It "rw tty.rw"
+.It "sb banner.short"
+.It "sc job.no_copies"
+.It "sd spool.dir"
+.It "sf job.no_formfeed"
+.It "sh banner.disable"
+.It "sr stat.recv"
+.It "ss stat.send"
+.It "st spool.status"
+.It "tf filt.troff"
+.It "tr job.trailer"
+.It "vf filt.raster"
+.El
+.Pp
+If the local line printer driver supports indentation, the daemon
+must understand how to invoke it.
+.Sh FILTERS
+The
+.Xr lpd 8
+daemon creates a pipeline of
+.Em filters
+to process files for various printer types.
+The filters selected depend on the flags passed to
+.Xr lpr 1 .
+The pipeline set up is:
+.Bd -literal -offset indent
+p pr | if regular text + pr(1)
+none if regular text
+c cf cifplot
+d df DVI (tex)
+g gf plot(3)
+n nf ditroff
+f rf Fortran
+t tf troff
+v vf raster image
+.Ed
+.Pp
+The
+.Sy if
+filter is invoked with arguments:
+.Bd -ragged -offset indent
+.Cm if
+.Op Fl c
+.Fl w Ns Ar width
+.Fl l Ns Ar length
+.Fl i Ns Ar indent
+.Fl n Ar login
+.Fl h Ar host acct-file
+.Ed
+.Pp
+The
+.Fl c
+flag is passed only if the
+.Fl l
+flag (pass control characters literally)
+is specified to
+.Xr lpr 1 .
+The
+.Ar Width
+function
+and
+.Ar length
+specify the page width and length
+(from
+.Cm pw
+and
+.Cm pl
+respectively) in characters.
+The
+.Fl n
+and
+.Fl h
+parameters specify the login name and host name of the owner
+of the job respectively.
+The
+.Ar Acct-file
+function
+is passed from the
+.Cm af
+.Nm
+entry.
+.Pp
+If no
+.Cm if
+is specified,
+.Cm of
+is used instead,
+with the distinction that
+.Cm of
+is opened only once,
+while
+.Cm if
+is opened for every individual job.
+Thus,
+.Cm if
+is better suited to performing accounting.
+The
+.Cm of
+is only given the
+.Ar width
+and
+.Ar length
+flags.
+.Pp
+All other filters are called as:
+.Bd -ragged -offset indent
+.Nm filter
+.Fl x Ns Ar width
+.Fl y Ns Ar length
+.Fl n Ar login
+.Fl h Ar host acct-file
+.Ed
+.Pp
+where
+.Ar width
+and
+.Ar length
+are represented in pixels,
+specified by the
+.Cm px
+and
+.Cm py
+entries respectively.
+.Pp
+All filters take
+.Em stdin
+as the file,
+.Em stdout
+as the printer,
+may log either to
+.Em stderr
+or using
+.Xr syslog 3 ,
+and must not ignore
+.Dv SIGINT .
+.Sh REMOTE PRINTING
+When printing to a remote printer using
+.Cm rm ,
+it is possible to use either
+.Cm if
+or
+.Cm of .
+If both are specified,
+.Cm of
+is ignored. Both filters behave the same except that they are passed
+different arguments as above. Specifically, the output filter is
+terminated and restarted for each file transmitted. This is necessary
+in order to pass the resulting size to the remote
+.Xr lpd 8 .
+.Pp
+If the
+.Fl p
+flag was passed to
+.Xr lpr 1 ,
+.Xr pr 1
+is not executed locally, but is requested of the remote
+.Xr lpd 8 .
+Any input filtering via
+.Cm if
+will therefore happen before
+.Xr pr 1
+is executed rather than afterwards.
+.Pp
+There are some models of network printers which accept jobs from
+.Xr lpd 8 ,
+but they ignore the control file for a job and simply print
+each data file as it arrives at the printer.
+One side-effect of this behavior is that the printer will ignore any request
+for multiple copies as given with the
+.Fl #
+flag on the
+.Xr lpr 1
+command.
+The
+.Cm rc
+entry will cause
+.Xr lpd 8
+to resend each data file for each copy that the user
+originally requested.
+Note that the
+.Cm rc
+entry should only be specified on hosts which send jobs directly to
+the printer.
+.Pp
+If
+.Cm lp
+is specified as
+.Em port Ns 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 TRANSFER STATISTICS
+When a print job is transfered to a remote machine (which might be
+another unix box, or may be a network printer), it may be useful
+to keep statistics on each transfer. The
+.Cm sr
+and
+.Cm ss
+options indicate filenames that lpd should use to store such
+statistics. A statistics line is written for each datafile of a
+job as the file is successfully transferred. The format of the
+line is the same for both the sending and receiving side of a
+transfer.
+.Pp
+Statistics on datafiles being received would be used on a print
+server, if you are interested in network performance between a
+variety of machines which are sending jobs to that print server.
+The print server could collect statistics on the speed of each
+print job as it arrived on the server.
+.Pp
+Statistics on datafiles being sent might be used as a minimal
+accounting record, when you want to know who sent which jobs to a
+remote printer, when they were sent, and how large (in bytes) the
+files were. This will not give include any idea of how many pages
+were printed, because there is no standard way to get that information
+back from a remote (network) printer in this case.
+.Sh LOGGING
+Error messages generated by the line printer programs themselves
+(that is, the
+.Xr lpd 8
+and related programs)
+are logged by
+.Xr syslog 3
+using the
+.Dv LPR
+facility.
+Messages printed on
+.Em stderr
+of one of the filters
+are sent to the corresponding
+.Cm lf
+file.
+The filters may, of course, use
+.Xr syslogd 8
+themselves.
+.Pp
+Error messages sent to the console have a carriage return and a line
+feed appended to them, rather than just a line feed.
+.Sh SEE ALSO
+.Xr lpq 1 ,
+.Xr lpr 1 ,
+.Xr lprm 1 ,
+.Xr hosts.lpd 5 ,
+.Xr termcap 5 ,
+.Xr chkprintcap 8 ,
+.Xr lpc 8 ,
+.Xr lpd 8 ,
+.Xr pac 8
+.Rs
+.%T "4.3 BSD Line Printer Spooler Manual"
+.Re
+.Sh HISTORY
+The
+.Nm
+file format appeared in
+.Bx 4.2 .
diff --git a/usr.sbin/lpr/lprm/Makefile b/usr.sbin/lpr/lprm/Makefile
new file mode 100644
index 0000000..44bc93a
--- /dev/null
+++ b/usr.sbin/lpr/lprm/Makefile
@@ -0,0 +1,18 @@
+# From: @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../common_source
+
+BINDIR= /usr/bin
+
+PROG= lprm
+BINOWN= root
+BINGRP= daemon
+BINMODE= 6555
+
+CFLAGS+= -I${.CURDIR}/../common_source
+
+DPADD= ${LIBLPR}
+LDADD= ${LIBLPR}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/lpr/lprm/lprm.1 b/usr.sbin/lpr/lprm/lprm.1
new file mode 100644
index 0000000..04cfbe0
--- /dev/null
+++ b/usr.sbin/lpr/lprm/lprm.1
@@ -0,0 +1,149 @@
+.\" Copyright (c) 1983, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)lprm.1 8.1 (Berkeley) 6/6/93
+.\" $FreeBSD$
+.\"
+.Dd June 6, 1993
+.Dt LPRM 1
+.Os
+.Sh NAME
+.Nm lprm
+.Nd remove jobs from the line printer spooling queue
+.Sh SYNOPSIS
+.Nm
+.Op Fl P Ns Ar printer
+.Op Fl
+.Op job # ...\&
+.Op Ar user ...\&
+.Sh DESCRIPTION
+The
+.Nm
+utility will remove a job, or jobs, from a printer's spool queue.
+Since the spooling directory is protected from users, using
+.Nm
+is normally the only method by which a user may remove a job.
+The owner of a job is determined by the user's login name
+and host name on the machine where the
+.Xr lpr 1
+command was invoked.
+.Pp
+Options and arguments:
+.Bl -tag -width indent
+.It Fl P Ns Ar printer
+Specify the queue associated with a specific
+.Ar printer
+(otherwise the default printer is used).
+.It Fl
+If a single
+.Sq Fl
+is given,
+.Nm
+will remove all jobs which a user
+owns. If the super-user employs this flag, the spool queue will
+be emptied entirely.
+.It Ar user
+Cause
+.Nm
+to attempt to remove any jobs queued belonging to that user
+(or users). This form of invoking
+.Nm
+is useful only to the super-user.
+.It Ar job\ \&#
+A user may dequeue an individual job by specifying its job number.
+This number may be obtained from the
+.Xr lpq 1
+program, e.g.\&
+.Pp
+.Bd -literal -offset indent
+\&% lpq \-l
+
+1st:ken [job #013ucbarpa]
+ (standard input) 100 bytes
+% lprm 13
+.Ed
+.El
+.Pp
+If neither arguments or options are given,
+.Nm
+will delete the currently active job if it is
+owned by the user who invoked
+.Nm .
+.Pp
+The
+.Nm
+utility announces the names of any files it removes and is silent if
+there are no jobs in the queue which match the request list.
+.Pp
+The
+.Nm
+utility will kill off an active daemon, if necessary, before removing
+any spooling files. If a daemon is killed, a new one is
+automatically restarted upon completion of file removals.
+.Sh ENVIRONMENT
+If the following environment variable exists, it is utilized by
+.Nm .
+.Bl -tag -width PRINTER
+.It Ev PRINTER
+If the environment variable
+.Ev PRINTER
+exists,
+and a printer has not been specified with the
+.Fl P
+option,
+the default printer is assumed from
+.Ev PRINTER .
+.El
+.Sh FILES
+.Bl -tag -width /var/spool/*/lock/ -compact
+.It Pa /etc/printcap
+Printer characteristics file.
+.It Pa /var/spool/*
+Spooling directories.
+.It Pa /var/spool/*/lock
+Lock file used to obtain the pid of the current
+daemon and the job number of the currently active job.
+.El
+.Sh SEE ALSO
+.Xr lpq 1 ,
+.Xr lpr 1 ,
+.Xr lpd 8
+.Sh DIAGNOSTICS
+``Permission denied" if the user tries to remove files other than his
+own.
+.Sh BUGS
+Since there are race conditions possible in the update of the lock file,
+the currently active job may be incorrectly identified.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 3.0 .
diff --git a/usr.sbin/lpr/lprm/lprm.c b/usr.sbin/lpr/lprm/lprm.c
new file mode 100644
index 0000000..7c27a26
--- /dev/null
+++ b/usr.sbin/lpr/lprm/lprm.c
@@ -0,0 +1,164 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#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(int argc, char *_argv[]);
+static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ char *arg;
+ const char *printer;
+ struct passwd *p;
+ static char root[] = "root";
+
+ printer = NULL;
+ uid = getuid();
+ euid = geteuid();
+ seteuid(uid); /* be safe */
+ progname = argv[0];
+ gethostname(local_host, sizeof(local_host));
+ openlog("lpd", 0, LOG_LPR);
+
+ /*
+ * Bogus code later checks for string equality between
+ * `person' and "root", so if we are root, better make sure
+ * that code will succeed.
+ */
+ if (getuid() == 0) {
+ person = root;
+ } else if ((person = getlogin()) == NULL) {
+ if ((p = getpwuid(getuid())) == NULL)
+ fatal(0, "Who are you?");
+ if (strlen(p->pw_name) >= sizeof(luser))
+ fatal(0, "Your name is too long");
+ strcpy(luser, p->pw_name);
+ person = luser;
+ }
+ while (--argc) {
+ if ((arg = *++argv)[0] == '-')
+ switch (arg[1]) {
+ case 'P':
+ if (arg[2])
+ printer = &arg[2];
+ else if (argc > 1) {
+ argc--;
+ printer = *++argv;
+ }
+ break;
+ case '\0':
+ if (!users) {
+ users = -1;
+ break;
+ }
+ default:
+ usage();
+ }
+ else {
+ if (users < 0)
+ usage();
+ if (isdigit(arg[0])) {
+ if (requests >= MAXREQUESTS)
+ fatal(0, "Too many requests");
+ requ[requests++] = atoi(arg);
+ } else {
+ if (users >= MAXUSERS)
+ fatal(0, "Too many users");
+ user[users++] = arg;
+ }
+ }
+ }
+ if (printer == NULL && (printer = getenv("PRINTER")) == NULL)
+ printer = DEFLP;
+
+ rmjob(printer);
+ exit(0);
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: lprm [-] [-Pprinter] [[job #] [user] ...]\n");
+ exit(2);
+}
diff --git a/usr.sbin/lpr/lptest/Makefile b/usr.sbin/lpr/lptest/Makefile
new file mode 100644
index 0000000..c02d7bd
--- /dev/null
+++ b/usr.sbin/lpr/lptest/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+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..c34934e
--- /dev/null
+++ b/usr.sbin/lpr/lptest/lptest.1
@@ -0,0 +1,77 @@
+.\" Copyright (c) 1985, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)lptest.1 8.2 (Berkeley) 12/30/93
+.\" $FreeBSD$
+.\"
+.Dd December 30, 1993
+.Dt LPTEST 1
+.Os
+.Sh NAME
+.Nm lptest
+.Nd generate lineprinter ripple pattern
+.Sh SYNOPSIS
+.Nm
+.Op Ar length
+.Op Ar count
+.Sh DESCRIPTION
+The
+.Nm
+utility writes the traditional "ripple test" pattern on standard output.
+In 96 lines,
+this pattern will print all 96 printable
+.Tn ASCII
+characters
+in each position.
+While originally created to test printers, it is quite
+useful for testing terminals,
+driving terminal ports for debugging purposes,
+or any other task where a quick supply of random data is needed.
+.Pp
+The
+.Ar length
+argument specifies the output line length if the default
+length of 79 is inappropriate.
+.Pp
+The
+.Ar count
+argument specifies the number of output lines to be generated if
+the default count of 200 is inappropriate.
+Note that if
+.Ar count
+is to be specified,
+.Ar length
+must be also be specified.
+.Sh HISTORY
+A
+.Nm
+utility appeared in
+.Bx 4.3 .
diff --git a/usr.sbin/lpr/lptest/lptest.c b/usr.sbin/lpr/lptest/lptest.c
new file mode 100644
index 0000000..9136d2c
--- /dev/null
+++ b/usr.sbin/lpr/lptest/lptest.c
@@ -0,0 +1,85 @@
+/*
+ * 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[] = "@(#)lptest.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+/*
+ * lptest -- line printer test program (and other devices).
+ */
+int
+main(int argc, char **argv)
+{
+ int len, count;
+ register int i, j, fc, nc;
+ char outbuf[BUFSIZ];
+
+ setbuf(stdout, outbuf);
+ if (argc >= 2)
+ len = atoi(argv[1]);
+ else
+ len = 79;
+ if (argc >= 3)
+ count = atoi(argv[2]);
+ else
+ count = 200;
+ fc = ' ';
+ for (i = 0; i < count; i++) {
+ if (++fc == 0177)
+ fc = ' ';
+ nc = fc;
+ for (j = 0; j < len; j++) {
+ putchar(nc);
+ if (++nc == 0177)
+ nc = ' ';
+ }
+ putchar('\n');
+ }
+ (void) fflush(stdout);
+ exit(0);
+}
diff --git a/usr.sbin/lpr/pac/Makefile b/usr.sbin/lpr/pac/Makefile
new file mode 100644
index 0000000..bd895a7
--- /dev/null
+++ b/usr.sbin/lpr/pac/Makefile
@@ -0,0 +1,14 @@
+# From: @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../common_source
+
+PROG= pac
+MAN= pac.8
+
+CFLAGS+= -I${.CURDIR}/../common_source
+
+DPADD= ${LIBLPR}
+LDADD= ${LIBLPR}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/lpr/pac/pac.8 b/usr.sbin/lpr/pac/pac.8
new file mode 100644
index 0000000..2c94214
--- /dev/null
+++ b/usr.sbin/lpr/pac/pac.8
@@ -0,0 +1,107 @@
+.\" Copyright (c) 1983, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)pac.8 8.1 (Berkeley) 6/6/93
+.\" $FreeBSD$
+.\"
+.Dd June 6, 1993
+.Dt PAC 8
+.Os
+.Sh NAME
+.Nm pac
+.Nd printer/plotter accounting information
+.Sh SYNOPSIS
+.Nm
+.Op Fl P Ns Ar printer
+.Op Fl c
+.Op Fl m
+.Op Fl p Ns Ar price
+.Op Fl s
+.Op Fl r
+.Op Ar name ...
+.Sh DESCRIPTION
+.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..c948f24
--- /dev/null
+++ b/usr.sbin/lpr/pac/pac.c
@@ -0,0 +1,453 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#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 size_t hcount; /* Count of hash entries */
+static int mflag = 0; /* disregard machine names */
+static int pflag = 0; /* 1 if -p on cmd line */
+static float price = 0.02; /* cost per page (or what ever) */
+static int reverse; /* Reverse sort order */
+static int sort; /* Sort by cost */
+static char *sumfile; /* summary file */
+static int summarize; /* Compress accounting file */
+
+uid_t uid, euid;
+
+/*
+ * Grossness follows:
+ * Names to be accumulated are hashed into the following
+ * table.
+ */
+
+#define HSHSIZE 97 /* Number of hash buckets */
+
+struct hent {
+ struct hent *h_link; /* Forward hash link */
+ char *h_name; /* Name of this user */
+ float h_feetpages; /* Feet or pages of paper */
+ int h_count; /* Number of runs */
+};
+
+static struct hent *hashtab[HSHSIZE]; /* Hash table proper */
+
+int main(int argc, char **_argv);
+static void account(FILE *_acctf);
+static int any(int _ch, const char _str[]);
+static int chkprinter(const char *_ptrname);
+static void dumpit(void);
+static int hash(const char _name[]);
+static struct hent *enter(const char _name[]);
+static struct hent *lookup(const char _name[]);
+static int qucmp(const void *_a, const void *_b);
+static void rewrite(void);
+static void usage(void);
+
+int
+main(int argc, char **argv)
+{
+ FILE *acctf;
+ const char *cp, *printer;
+
+ printer = NULL;
+ euid = geteuid(); /* these aren't used in pac(1) */
+ uid = getuid();
+ while (--argc) {
+ cp = *++argv;
+ if (*cp++ == '-') {
+ switch(*cp++) {
+ case 'P':
+ /*
+ * Printer name.
+ */
+ printer = cp;
+ continue;
+
+ case 'p':
+ /*
+ * get the price.
+ */
+ price = atof(cp);
+ pflag = 1;
+ continue;
+
+ case 's':
+ /*
+ * Summarize and compress accounting file.
+ */
+ summarize++;
+ continue;
+
+ case 'c':
+ /*
+ * Sort by cost.
+ */
+ sort++;
+ continue;
+
+ case 'm':
+ /*
+ * disregard machine names for each user
+ */
+ mflag = 1;
+ continue;
+
+ case 'r':
+ /*
+ * Reverse sorting order.
+ */
+ reverse++;
+ continue;
+
+ default:
+ usage();
+ }
+ }
+ (void) enter(--cp);
+ allflag = 0;
+ }
+ if (printer == NULL && (printer = getenv("PRINTER")) == NULL)
+ printer = DEFLP;
+ if (!chkprinter(printer)) {
+ printf("pac: unknown printer %s\n", printer);
+ exit(2);
+ }
+
+ if ((acctf = fopen(acctfile, "r")) == NULL) {
+ perror(acctfile);
+ exit(1);
+ }
+ account(acctf);
+ fclose(acctf);
+ if ((acctf = fopen(sumfile, "r")) != NULL) {
+ account(acctf);
+ fclose(acctf);
+ }
+ if (summarize)
+ rewrite();
+ else
+ dumpit();
+ exit(errs);
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr,
+ "usage: pac [-Pprinter] [-pprice] [-s] [-c] [-r] [-m] [user ...]\n");
+ exit(1);
+}
+
+/*
+ * Read the entire accounting file, accumulating statistics
+ * for the users that we have in the hash table. If allflag
+ * is set, then just gather the facts on everyone.
+ * Note that we must accomodate both the active and summary file
+ * formats here.
+ * Host names are ignored if the -m flag is present.
+ */
+static void
+account(FILE *acctf)
+{
+ char linebuf[BUFSIZ];
+ double t;
+ register char *cp, *cp2;
+ register struct hent *hp;
+ register int ic;
+
+ while (fgets(linebuf, BUFSIZ, acctf) != NULL) {
+ cp = linebuf;
+ while (any(*cp, " \t"))
+ cp++;
+ t = atof(cp);
+ while (any(*cp, ".0123456789"))
+ cp++;
+ while (any(*cp, " \t"))
+ cp++;
+ for (cp2 = cp; !any(*cp2, " \t\n"); cp2++)
+ ;
+ ic = atoi(cp2);
+ *cp2 = '\0';
+ if (mflag && strchr(cp, ':'))
+ cp = strchr(cp, ':') + 1;
+ hp = lookup(cp);
+ if (hp == NULL) {
+ if (!allflag)
+ continue;
+ hp = enter(cp);
+ }
+ hp->h_feetpages += t;
+ if (ic)
+ hp->h_count += ic;
+ else
+ hp->h_count++;
+ }
+}
+
+/*
+ * Sort the hashed entries by name or footage
+ * and print it all out.
+ */
+static void
+dumpit(void)
+{
+ struct hent **base;
+ register struct hent *hp, **ap;
+ register int hno, runs;
+ size_t c;
+ float feet;
+
+ hp = hashtab[0];
+ hno = 1;
+ base = (struct hent **) calloc(sizeof hp, hcount);
+ for (ap = base, c = hcount; c--; ap++) {
+ while (hp == NULL)
+ hp = hashtab[hno++];
+ *ap = hp;
+ hp = hp->h_link;
+ }
+ qsort(base, hcount, sizeof hp, qucmp);
+ printf(" Login pages/feet runs price\n");
+ feet = 0.0;
+ runs = 0;
+ for (ap = base, c = hcount; c--; ap++) {
+ hp = *ap;
+ runs += hp->h_count;
+ feet += hp->h_feetpages;
+ printf("%-24s %7.2f %4d $%6.2f\n", hp->h_name,
+ hp->h_feetpages, hp->h_count, hp->h_feetpages * price);
+ }
+ if (allflag) {
+ printf("\n");
+ printf("%-24s %7.2f %4d $%6.2f\n", "total", feet,
+ runs, feet * price);
+ }
+}
+
+/*
+ * Rewrite the summary file with the summary information we have accumulated.
+ */
+static void
+rewrite(void)
+{
+ register struct hent *hp;
+ register int i;
+ FILE *acctf;
+
+ if ((acctf = fopen(sumfile, "w")) == NULL) {
+ warn("%s", sumfile);
+ errs++;
+ return;
+ }
+ for (i = 0; i < HSHSIZE; i++) {
+ hp = hashtab[i];
+ while (hp != NULL) {
+ fprintf(acctf, "%7.2f\t%s\t%d\n", hp->h_feetpages,
+ hp->h_name, hp->h_count);
+ hp = hp->h_link;
+ }
+ }
+ fflush(acctf);
+ if (ferror(acctf)) {
+ warn("%s", sumfile);
+ errs++;
+ }
+ fclose(acctf);
+ if ((acctf = fopen(acctfile, "w")) == NULL)
+ warn("%s", acctfile);
+ else
+ fclose(acctf);
+}
+
+/*
+ * Hashing routines.
+ */
+
+/*
+ * Enter the name into the hash table and return the pointer allocated.
+ */
+
+static struct hent *
+enter(const char name[])
+{
+ register struct hent *hp;
+ register int h;
+
+ if ((hp = lookup(name)) != NULL)
+ return(hp);
+ h = hash(name);
+ hcount++;
+ hp = (struct hent *) calloc(sizeof *hp, (size_t)1);
+ hp->h_name = (char *) calloc(sizeof(char), strlen(name)+1);
+ strcpy(hp->h_name, name);
+ hp->h_feetpages = 0.0;
+ hp->h_count = 0;
+ hp->h_link = hashtab[h];
+ hashtab[h] = hp;
+ return(hp);
+}
+
+/*
+ * Lookup a name in the hash table and return a pointer
+ * to it.
+ */
+
+static struct hent *
+lookup(const char name[])
+{
+ register int h;
+ register struct hent *hp;
+
+ h = hash(name);
+ for (hp = hashtab[h]; hp != NULL; hp = hp->h_link)
+ if (strcmp(hp->h_name, name) == 0)
+ return(hp);
+ return(NULL);
+}
+
+/*
+ * Hash the passed name and return the index in
+ * the hash table to begin the search.
+ */
+static int
+hash(const char name[])
+{
+ register int h;
+ register const char *cp;
+
+ for (cp = name, h = 0; *cp; h = (h << 2) + *cp++)
+ ;
+ return((h & 0x7fffffff) % HSHSIZE);
+}
+
+/*
+ * Other stuff
+ */
+static int
+any(int ch, const char str[])
+{
+ register int c = ch;
+ register const char *cp = str;
+
+ while (*cp)
+ if (*cp++ == c)
+ return(1);
+ return(0);
+}
+
+/*
+ * The qsort comparison routine.
+ * The comparison is ascii collating order
+ * or by feet of typesetter film, according to sort.
+ */
+static int
+qucmp(const void *a, const void *b)
+{
+ register const struct hent *h1, *h2;
+ register int r;
+
+ h1 = *(const struct hent * const *)a;
+ h2 = *(const struct hent * const *)b;
+ if (sort)
+ r = h1->h_feetpages < h2->h_feetpages ?
+ -1 : h1->h_feetpages > h2->h_feetpages;
+ else
+ r = strcmp(h1->h_name, h2->h_name);
+ return(reverse ? -r : r);
+}
+
+/*
+ * Perform lookup for printer name or abbreviation --
+ */
+static int
+chkprinter(const char *ptrname)
+{
+ int stat;
+ struct printer myprinter, *pp = &myprinter;
+
+ init_printer(&myprinter);
+ stat = getprintcap(ptrname, pp);
+ switch(stat) {
+ case PCAPERR_OSERR:
+ printf("pac: getprintcap: %s\n", pcaperr(stat));
+ exit(3);
+ case PCAPERR_NOTFOUND:
+ return 0;
+ case PCAPERR_TCLOOP:
+ fatal(pp, "%s", pcaperr(stat));
+ }
+ if ((acctfile = pp->acct_file) == NULL)
+ errx(3, "accounting not enabled for printer %s", ptrname);
+ if (!pflag && pp->price100)
+ price = pp->price100/10000.0;
+ sumfile = (char *) calloc(sizeof(char), strlen(acctfile)+5);
+ if (sumfile == NULL)
+ errx(1, "calloc failed");
+ strcpy(sumfile, acctfile);
+ strcat(sumfile, "_sum");
+ return(1);
+}
diff --git a/usr.sbin/lptcontrol/Makefile b/usr.sbin/lptcontrol/Makefile
new file mode 100644
index 0000000..76f4f2d
--- /dev/null
+++ b/usr.sbin/lptcontrol/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= lptcontrol
+MAN= lptcontrol.8
+
+WARNS?= 2
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/lptcontrol/lptcontrol.8 b/usr.sbin/lptcontrol/lptcontrol.8
new file mode 100644
index 0000000..ec165cc
--- /dev/null
+++ b/usr.sbin/lptcontrol/lptcontrol.8
@@ -0,0 +1,83 @@
+.\"
+.\" lptcontrol - a utility for manipulating the lpt driver
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" $FreeBSD$
+.Dd September 3, 1994
+.Dt LPTCONTROL 8
+.Os
+.Sh NAME
+.Nm lptcontrol
+.Nd a utility for manipulating the lpt printer driver
+.Sh SYNOPSIS
+.Nm
+.Fl i | p | e | s
+.Op Fl d Ar device
+.Sh DESCRIPTION
+The
+.Nm
+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.
+.Pp
+The following command line options are supported:
+.Bl -tag -width indent
+.It Fl i
+Turn on interrupt-driven mode.
+.It Fl p
+Turn on polled mode.
+.It Fl e
+Turn on extended mode.
+.It Fl s
+Turn on standard mode, i.e. turn off extended mode.
+.It Fl d Ar device
+Set the mode of the printer device specified by
+.Ar device .
+The default value for
+.Ar device
+is
+.Pa /dev/lpt0 .
+.El
+.Pp
+One of
+.Fl i , p
+or
+.Fl e
+must be specified.
+.Sh FILES
+.Bl -tag -width /sys/i386/conf/GENERIC -compact
+.It Pa /dev/lpt?
+printer devices
+.It Pa /dev/lpctl?
+printer control devices
+.It Pa /sys/i386/conf/GENERIC
+kernel configuration file
+.El
+.Sh BUGS
+Sure to be some.
+.Sh SEE ALSO
+.Xr lpt 4
+.Sh AUTHORS
+.An Geoffrey M. Rehmet
+.Sh HISTORY
+The
+.Nm
+command
+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..e1b78bc
--- /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 without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <ctype.h>
+#include <err.h>
+#include <limits.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <dev/ppbus/lptio.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+
+
+#define PATH_LPCTL _PATH_DEV "lpctl"
+#define DEFAULT_DEVICE _PATH_DEV "lpt0"
+#define IRQ_INVALID -1
+#define DO_POLL 0
+#define USE_IRQ 1
+#define USE_EXT_MODE 2
+#define USE_STD_MODE 3
+
+static void usage(void)
+{
+ fprintf(stderr, "usage: lptcontrol -i | -p | -s | -e [-d device]\n");
+ exit(1);
+}
+
+static void set_interrupt_status(int irq_status, const char * file)
+{
+ int fd;
+
+ if((fd = open(file, O_WRONLY, 0660)) < 0)
+ err(1, "open");
+ if(ioctl(fd, LPT_IRQ, &irq_status) < 0)
+ err(1, "ioctl");
+ close(fd);
+}
+
+int main (int argc, char * argv[])
+{
+ int opt;
+ int irq_status = IRQ_INVALID;
+ const char *device = DEFAULT_DEVICE;
+
+ while((opt = getopt(argc, argv, "ipesd:")) != -1)
+ switch(opt) {
+ case 'i': irq_status = USE_IRQ; break;
+ case 'p': irq_status = DO_POLL; break;
+ case 'e': irq_status = USE_EXT_MODE; break;
+ case 's': irq_status = USE_STD_MODE; break;
+ case 'd': device = optarg; break;
+ default : usage();
+ }
+ if(irq_status == IRQ_INVALID)
+ usage();
+
+ set_interrupt_status(irq_status, device);
+
+ exit(0);
+}
diff --git a/usr.sbin/mailstats/Makefile b/usr.sbin/mailstats/Makefile
new file mode 100644
index 0000000..301691d
--- /dev/null
+++ b/usr.sbin/mailstats/Makefile
@@ -0,0 +1,45 @@
+# @(#)Makefile 8.2 (Berkeley) 9/21/96
+# $FreeBSD$
+
+MAINTAINER= gshapiro@FreeBSD.org
+
+SENDMAIL_DIR= ${.CURDIR}/../../contrib/sendmail
+.PATH: ${SENDMAIL_DIR}/mailstats
+
+PROG= mailstats
+SRCS= mailstats.c
+MAN= mailstats.8
+
+CFLAGS+= -I${SENDMAIL_DIR}/src -I${SENDMAIL_DIR}/include -I.
+CFLAGS+= -DNOT_SENDMAIL
+
+.if exists(${.OBJDIR}/../../lib/libsm)
+LIBSMDIR:= ${.OBJDIR}/../../lib/libsm
+.else
+LIBSMDIR!= cd ${.CURDIR}/../../lib/libsm; make -V .OBJDIR
+.endif
+LIBSM:= ${LIBSMDIR}/libsm.a
+
+.if exists(${.OBJDIR}/../../lib/libsmutil)
+LIBSMUTILDIR:= ${.OBJDIR}/../../lib/libsmutil
+.else
+LIBSMUTILDIR!= cd ${.CURDIR}/../../lib/libsmutil; make -V .OBJDIR
+.endif
+LIBSMUTIL:= ${LIBSMUTILDIR}/libsmutil.a
+
+DPADD= ${LIBSMUTIL} ${LIBSM}
+LDADD= ${LIBSMUTIL} ${LIBSM}
+
+SRCS+= sm_os.h
+CLEANFILES+=sm_os.h
+
+# User customizations to the sendmail build environment
+CFLAGS+= ${SENDMAIL_CFLAGS}
+DPADD+= ${SENDMAIL_DPADD}
+LDADD+= ${SENDMAIL_LDADD}
+LDFLAGS+= ${SENDMAIL_LDFLAGS}
+
+sm_os.h:
+ ln -sf ${SENDMAIL_DIR}/include/sm/os/sm_os_freebsd.h sm_os.h
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mailwrapper/Makefile b/usr.sbin/mailwrapper/Makefile
new file mode 100644
index 0000000..b4089f9
--- /dev/null
+++ b/usr.sbin/mailwrapper/Makefile
@@ -0,0 +1,21 @@
+# $FreeBSD$
+
+PROG= mailwrapper
+MAN= mailwrapper.8
+
+DPADD= ${LIBUTIL}
+LDADD= -lutil
+
+SYMLINKS= ${BINDIR}/mailwrapper /usr/sbin/sendmail \
+ ${BINDIR}/mailwrapper /usr/sbin/hoststat \
+ ${BINDIR}/mailwrapper /usr/sbin/purgestat \
+ ${BINDIR}/mailwrapper /usr/bin/newaliases \
+ ${BINDIR}/mailwrapper /usr/bin/mailq
+
+afterinstall:
+.if !exists(${DESTDIR}/etc/mail/mailer.conf)
+ ${INSTALL} ${COPY} -o ${BINOWN} -g ${BINGRP} -m 644 \
+ ${.CURDIR}/../../etc/mail/mailer.conf ${DESTDIR}/etc/mail
+.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mailwrapper/mailwrapper.8 b/usr.sbin/mailwrapper/mailwrapper.8
new file mode 100644
index 0000000..7f45e28
--- /dev/null
+++ b/usr.sbin/mailwrapper/mailwrapper.8
@@ -0,0 +1,151 @@
+.\" $NetBSD: mailwrapper.8,v 1.6 1999/03/25 16:40:17 is Exp $
+.\" $FreeBSD$
+.\"
+.\" Copyright (c) 1998
+.\" Perry E. Metzger. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgment:
+.\" This product includes software developed for the NetBSD Project
+.\" by Perry E. Metzger.
+.\" 4. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" The following requests are required for all man pages.
+.Dd December 16, 1998
+.Dt MAILWRAPPER 8
+.Os
+.Sh NAME
+.Nm mailwrapper
+.Nd invoke appropriate MTA software based on configuration file
+.Sh SYNOPSIS
+Special.
+See below.
+.Sh DESCRIPTION
+At one time, the only Mail Transfer Agent (MTA) software easily available
+was
+.Xr sendmail 8 .
+As a result of this, most Mail User Agents (MUAs) such as
+.Xr mail 1
+had the path and calling conventions expected by
+.Xr sendmail 8
+compiled in.
+.Pp
+Times have changed, however.
+On a modern
+.Ux
+system, the administrator may wish to use one of several
+available MTAs.
+.Pp
+It would be difficult to modify all MUA software typically available
+on a system, so most of the authors of alternative MTAs have written
+their front end message submission programs so that they use the same
+calling conventions as
+.Xr sendmail 8
+and may be put into place instead of
+.Xr sendmail 8
+in
+.Pa /usr/sbin/sendmail .
+.Pp
+.Xr sendmail 8
+also typically has aliases named
+.Xr mailq 1
+and
+.Xr newaliases 1
+linked to it.
+The program knows to behave differently when its
+.Va argv[0]
+is
+.Dq mailq
+or
+.Dq newaliases
+and behaves appropriately.
+Typically, replacement MTAs provide similar
+functionality, either through a program that also switches behavior
+based on calling name, or through a set of programs that provide
+similar functionality.
+.Pp
+Although having replacement programs that plug replace
+.Xr sendmail 8
+helps in installing alternative MTAs, it essentially makes the
+configuration of the system depend on hard installing new programs in
+.Pa /usr .
+This leads to configuration problems for many administrators, since
+they may wish to install a new MTA without altering the system
+provided
+.Pa /usr .
+(This may be, for example, to avoid having upgrade problems when a new
+version of the system is installed over the old.)
+They may also have a shared
+.Pa /usr
+among several
+machines, and may wish to avoid placing implicit configuration
+information in a read-only
+.Pa /usr .
+.Pp
+The
+.Nm
+program is designed to replace
+.Pa /usr/sbin/sendmail
+and to invoke an appropriate MTA instead of
+.Xr sendmail 8
+based on configuration information placed in
+.Pa /etc/mail/mailer.conf .
+This permits the administrator to configure which MTA is to be invoked on
+the system at run time.
+.Sh FILES
+Configuration for
+.Nm
+is kept in
+.Pa /etc/mail/mailer.conf .
+.Pa /usr/sbin/sendmail
+is typically set up as a symbolic link to
+.Nm
+which is not usually invoked on its own.
+.Sh DIAGNOSTICS
+.Nm
+will return an error value and print a diagnostic if its configuration
+file is missing or malformed, or does not contain a mapping for the
+name under which
+.Nm
+was invoked.
+.Sh SEE ALSO
+.Xr mail 1 ,
+.Xr mailq 1 ,
+.Xr newaliases 1 ,
+.Xr mailer.conf 5 ,
+.Xr sendmail 8
+.Sh HISTORY
+.Nm
+first appeared in
+.Nx 1.4
+and then
+.Fx 4.0 .
+.Sh AUTHORS
+Perry E. Metzger <perry@piermont.com>
+.Sh BUGS
+The entire reason this program exists is a crock.
+Instead, a command
+for how to submit mail should be standardized, and all the "behave
+differently if invoked with a different name" behavior of things like
+.Xr mailq 1
+should go away.
diff --git a/usr.sbin/mailwrapper/mailwrapper.c b/usr.sbin/mailwrapper/mailwrapper.c
new file mode 100644
index 0000000..4eef31c
--- /dev/null
+++ b/usr.sbin/mailwrapper/mailwrapper.c
@@ -0,0 +1,188 @@
+/* $OpenBSD: mailwrapper.c,v 1.6 1999/12/17 05:06:28 mickey Exp $ */
+/* $NetBSD: mailwrapper.c,v 1.3 1999/05/29 18:18:15 christos Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (c) 1998
+ * Perry E. Metzger. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgment:
+ * This product includes software developed for the NetBSD Project
+ * by Perry E. Metzger.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <err.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <libutil.h>
+#include <syslog.h>
+#include <stdarg.h>
+
+#include "pathnames.h"
+
+struct arglist {
+ size_t argc, maxc;
+ char **argv;
+};
+
+int main __P((int, char *[], char *[]));
+
+static void initarg __P((struct arglist *));
+static void addarg __P((struct arglist *, const char *, int));
+static void freearg __P((struct arglist *, int));
+
+extern const char *__progname; /* from crt0.o */
+
+static void
+initarg(al)
+ struct arglist *al;
+{
+ al->argc = 0;
+ al->maxc = 10;
+ if ((al->argv = malloc(al->maxc * sizeof(char *))) == NULL)
+ err(1, NULL);
+}
+
+static void
+addarg(al, arg, copy)
+ struct arglist *al;
+ const char *arg;
+ int copy;
+{
+ char **argv2;
+
+ if (al->argc == al->maxc) {
+ al->maxc <<= 1;
+
+ if ((argv2 = realloc(al->argv,
+ al->maxc * sizeof(char *))) == NULL) {
+ if (al->argv)
+ free(al->argv);
+ al->argv = NULL;
+ err(1, NULL);
+ } else {
+ al->argv = argv2;
+ }
+ }
+ if (copy) {
+ if ((al->argv[al->argc++] = strdup(arg)) == NULL)
+ err(1, NULL);
+ } else
+ al->argv[al->argc++] = (char *)arg;
+}
+
+static void
+freearg(al, copy)
+ struct arglist *al;
+ int copy;
+{
+ size_t i;
+ if (copy)
+ for (i = 0; i < al->argc; i++)
+ free(al->argv[i]);
+ free(al->argv);
+}
+
+int
+main(argc, argv, envp)
+ int argc;
+ char *argv[];
+ char *envp[];
+{
+ FILE *config;
+ char *line, *cp, *from, *to, *ap;
+ size_t len, lineno = 0;
+ struct arglist al;
+
+ initarg(&al);
+ for (len = 0; len < argc; len++)
+ addarg(&al, argv[len], 0);
+
+ if ((config = fopen(_PATH_MAILERCONF, "r")) == NULL) {
+ addarg(&al, NULL, 0);
+ openlog("mailwrapper", LOG_PID, LOG_MAIL);
+ syslog(LOG_INFO, "can't open %s, using %s as default MTA",
+ _PATH_MAILERCONF, _PATH_DEFAULTMTA);
+ closelog();
+ execve(_PATH_DEFAULTMTA, al.argv, envp);
+ freearg(&al, 0);
+ err(1, "execing %s", _PATH_DEFAULTMTA);
+ /*NOTREACHED*/
+ }
+
+ for (;;) {
+ if ((line = fparseln(config, &len, &lineno, NULL, 0)) == NULL) {
+ if (feof(config))
+ errx(1, "no mapping in %s", _PATH_MAILERCONF);
+ err(1, "can't parse line %lu", (u_long)lineno);
+ }
+
+#define WS " \t\n"
+ cp = line;
+
+ cp += strspn(cp, WS);
+ if (cp[0] == '\0') {
+ /* empty line */
+ free(line);
+ continue;
+ }
+
+ if ((from = strsep(&cp, WS)) == NULL)
+ goto parse_error;
+
+ cp += strspn(cp, WS);
+
+ if ((to = strsep(&cp, WS)) == NULL)
+ goto parse_error;
+
+ if (strcmp(from, __progname) == 0) {
+ for (ap = strsep(&cp, WS); ap != NULL;
+ ap = strsep(&cp, WS))
+ if (*ap)
+ addarg(&al, ap, 0);
+ break;
+ }
+
+ free(line);
+ }
+
+ (void)fclose(config);
+
+ addarg(&al, NULL, 0);
+ execve(to, al.argv, envp);
+ freearg(&al, 0);
+ warn("execing %s", to);
+ free(line);
+ exit(1);
+ /*NOTREACHED*/
+parse_error:
+ freearg(&al, 0);
+ free(line);
+ errx(1, "parse error in %s at line %lu",
+ _PATH_MAILERCONF, (u_long)lineno);
+ /*NOTREACHED*/
+}
diff --git a/usr.sbin/mailwrapper/pathnames.h b/usr.sbin/mailwrapper/pathnames.h
new file mode 100644
index 0000000..d8ae498
--- /dev/null
+++ b/usr.sbin/mailwrapper/pathnames.h
@@ -0,0 +1,35 @@
+/* $FreeBSD$ */
+
+/*
+ * Copyright (c) 1998
+ * Perry E. Metzger. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgment:
+ * This product includes software developed for the NetBSD Project
+ * by Perry E. Metzger.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _PATH_MAILERCONF "/etc/mail/mailer.conf"
+#define _PATH_DEFAULTMTA "/usr/libexec/sendmail/sendmail"
diff --git a/usr.sbin/makemap/Makefile b/usr.sbin/makemap/Makefile
new file mode 100644
index 0000000..15219fd
--- /dev/null
+++ b/usr.sbin/makemap/Makefile
@@ -0,0 +1,52 @@
+# @(#)Makefile 8.4 (Berkeley) 6/10/97
+# $FreeBSD$
+
+MAINTAINER= gshapiro@FreeBSD.org
+
+SENDMAIL_DIR=${.CURDIR}/../../contrib/sendmail
+.PATH: ${SENDMAIL_DIR}/makemap
+
+PROG= makemap
+SRCS= makemap.c
+MAN= makemap.8
+
+CFLAGS+= -I${SENDMAIL_DIR}/src -I${SENDMAIL_DIR}/include -I.
+CFLAGS+= -DNEWDB -DNOT_SENDMAIL
+
+.if exists(${.OBJDIR}/../../lib/libsm)
+LIBSMDIR:= ${.OBJDIR}/../../lib/libsm
+.else
+LIBSMDIR!= cd ${.CURDIR}/../../lib/libsm; make -V .OBJDIR
+.endif
+LIBSM:= ${LIBSMDIR}/libsm.a
+
+.if exists(${.OBJDIR}/../../lib/libsmdb)
+LIBSMDBDIR:= ${.OBJDIR}/../../lib/libsmdb
+.else
+LIBSMDBDIR!= cd ${.CURDIR}/../../lib/libsmdb; make -V .OBJDIR
+.endif
+LIBSMDB:= ${LIBSMDBDIR}/libsmdb.a
+
+.if exists(${.OBJDIR}/../../lib/libsmutil)
+LIBSMUTILDIR:= ${.OBJDIR}/../../lib/libsmutil
+.else
+LIBSMUTILDIR!= cd ${.CURDIR}/../../lib/libsmutil; make -V .OBJDIR
+.endif
+LIBSMUTIL:= ${LIBSMUTILDIR}/libsmutil.a
+
+DPADD= ${LIBSMDB} ${LIBSMUTIL} ${LIBSM}
+LDADD= ${LIBSMDB} ${LIBSMUTIL} ${LIBSM}
+
+SRCS+= sm_os.h
+CLEANFILES+=sm_os.h
+
+# User customizations to the sendmail build environment
+CFLAGS+=${SENDMAIL_CFLAGS}
+DPADD+=${SENDMAIL_DPADD}
+LDADD+=${SENDMAIL_LDADD}
+LDFLAGS+=${SENDMAIL_LDFLAGS}
+
+sm_os.h:
+ ln -sf ${SENDMAIL_DIR}/include/sm/os/sm_os_freebsd.h sm_os.h
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/manctl/Makefile b/usr.sbin/manctl/Makefile
new file mode 100644
index 0000000..9e0c12e
--- /dev/null
+++ b/usr.sbin/manctl/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+SCRIPTS=manctl.sh
+MAN= manctl.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/manctl/manctl.8 b/usr.sbin/manctl/manctl.8
new file mode 100644
index 0000000..b244d30
--- /dev/null
+++ b/usr.sbin/manctl/manctl.8
@@ -0,0 +1,57 @@
+.\" Copyright (c) 1996 Wolfram Schneider <wosch@FreeBSD.org>. Berlin.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.Dd January 1, 1996
+.Dt MANCTL 8
+.Os
+.Sh NAME
+.Nm manctl
+.Nd manipulating manual pages
+.Sh SYNOPSIS
+.Nm
+.Op Fl compress
+.Op Fl uncompress
+.Op Fl purge
+.Op Fl help
+.Ar path ...
+.Sh DESCRIPTION
+.Nm Manctl
+compress or uncompress manual pages in directory path.
+If possible, .so's are replaced with hard links.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl help
+Print options and exit.
+.It Fl compress
+Compress uncompressed man pages (eliminating .so's).
+.It Fl uncompress
+Uncompress compressed man pages.
+.It Fl purge
+Purge old formatted man pages (not implemented yet).
+.El
+.Sh SEE ALSO
+.Xr catman 1 ,
+.Xr man 1
diff --git a/usr.sbin/manctl/manctl.sh b/usr.sbin/manctl/manctl.sh
new file mode 100644
index 0000000..23e2087
--- /dev/null
+++ b/usr.sbin/manctl/manctl.sh
@@ -0,0 +1,380 @@
+#!/bin/sh
+#
+# Copyright (c) 1994 Geoffrey M. Rehmet, Rhodes University
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by Geoffrey M. Rehmet
+# 4. Neither the name of Geoffrey M. Rehmet nor that of Rhodes University
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL GEOFFREY M. REHMET OR RHODES UNIVERSITY BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+# manctl:
+# a utility for manipulating manual pages
+# functions:
+# compress uncompressed man pages (elliminating .so's)
+# this is now two-pass. If possible, .so's
+# are replaced with hard links
+# uncompress compressed man pages
+# purge old formatted man pages (not implemented yet)
+# Things to watch out for:
+# Hard links - careful with g(un)zipping!
+# .so's - throw everything through soelim before gzip!
+# symlinks - ignore these - eg: expn is its own man page:
+# don't want to compress this!
+#
+PATH=/bin:/sbin:/usr/bin:/usr/sbin; export PATH
+
+#
+# purge cat? directories
+#
+do_purge()
+{
+ echo "purge $@" 2>&1
+ echo "not implemented yet\n" 2>&1
+}
+
+
+#
+# Uncompress one page
+#
+uncompress_page()
+{
+ local pname
+ local fname
+ local sect
+ local ext
+
+ # break up file name
+ pname=$1
+ IFS='.' ; set $pname
+ # less than 3 fields - don't know what to do with this
+ if [ $# -lt 3 ] ; then
+ IFS=" " ; echo ignoring $pname 1>&2 ; return 0 ;
+ fi
+ # construct name and section
+ fname=$1 ; shift
+ while [ $# -gt 2 ] ; do
+ fname=$fname.$1
+ shift
+ done
+ sect=$1
+ ext=$2
+
+ IFS=" "
+ case "$ext" in
+ gz|Z) {
+ IFS=" " ; set `file $pname`
+ if [ $2 != "gzip" ] ; then
+ echo moving hard link $pname 1>&2
+ mv $pname $fname.$ext # link
+ else
+ if [ $2 != "symbolic" ] ; then
+ echo gunzipping page $pname 1>&2
+ temp=`mktemp -t manager` || exit 1
+ gunzip -c $pname > $temp
+ chmod u+w $pname
+ cp $temp $pname
+ chmod 444 $pname
+ mv $pname $fname.$sect
+ rm -f $temp
+ else
+ # skip symlinks - this can be
+ # a program like expn, which is
+ # its own man page !
+ echo skipping symlink $pname 1>&2
+ fi
+ fi };;
+ *) {
+ IFS=" "
+ echo skipping file $pname 1>&2
+ } ;;
+ esac
+ # reset IFS - this is important!
+ IFS=" "
+}
+
+
+#
+# Uncompress manpages in paths
+#
+do_uncompress()
+{
+ local i
+ local dir
+ local workdir
+
+ workdir=`pwd`
+ while [ $# != 0 ] ; do
+ if [ -d $1 ] ; then
+ dir=$1
+ cd $dir
+ for i in * ; do
+ case $i in
+ *cat?) ;; # ignore cat directories
+ *) {
+ if [ -d $i ] ; then
+ do_uncompress $i
+ else
+ if [ -e $i ] ; then
+ uncompress_page $i
+ fi
+ fi } ;;
+ esac
+ done
+ cd $workdir
+ else
+ echo "directory $1 not found" 1>&2
+ fi
+ shift
+ done
+}
+
+#
+# Remove .so's from one file
+#
+so_purge_page()
+{
+ local so_entries
+ local lines
+ local fname
+
+ so_entries=`grep "^\.so" $1 | wc -l`
+ if [ $so_entries -eq 0 ] ; then return 0 ; fi
+
+ # we have a page with a .so in it
+ echo $1 contains a .so entry 2>&1
+
+ # now check how many lines in the file
+ lines=`wc -l < $1`
+
+ # if the file is only one line long, we can replace it
+ # with a hard link!
+ if [ $lines -eq 1 ] ; then
+ fname=$1;
+ echo replacing $fname with a hard link
+ set `cat $fname`;
+ rm -f $fname
+ ln ../$2 $fname
+ else
+ echo inlining page $fname 1>&2
+ temp=`mktemp -t manager` || exit 1
+ cat $fname | \
+ (cd .. ; soelim ) > $temp
+ chmod u+w $fname
+ cp $temp $fname
+ chmod 444 $fname
+ rm -f $temp
+ fi
+}
+
+#
+# Remove .so entries from man pages
+# If a page consists of just one line with a .so,
+# replace it with a hard link
+#
+remove_so()
+{
+ local pname
+ local fname
+ local sect
+
+ # break up file name
+ pname=$1
+ IFS='.' ; set $pname
+ if [ $# -lt 2 ] ; then
+ IFS=" " ; echo ignoring $pname 1>&2 ; return 0 ;
+ fi
+ # construct name and section
+ fname=$1 ; shift
+ while [ $# -gt 1 ] ; do
+ fname=$fname.$1
+ shift
+ done
+ sect=$1
+
+ IFS=" "
+ case "$sect" in
+ gz) { echo file $pname already gzipped 1>&2 ; } ;;
+ Z) { echo file $pname already compressed 1>&2 ; } ;;
+ [12345678ln]*){
+ IFS=" " ; set `file $pname`
+ if [ $2 = "gzip" ] ; then
+ echo moving hard link $pname 1>&2
+ mv $pname $pname.gz # link
+ else
+ if [ $2 != "symbolic" ] ; then
+ echo "removing .so's in page $pname" 1>&2
+ so_purge_page $pname
+ else
+ # skip symlink - this can be
+ # a program like expn, which is
+ # its own man page !
+ echo skipping symlink $pname 1>&2
+ fi
+ fi };;
+ *) {
+ IFS=" "
+ echo skipping file $pname 1>&2
+ } ;;
+ esac
+ # reset IFS - this is important!
+ IFS=" "
+}
+
+
+#
+# compress one page
+# We need to watch out for hard links here.
+#
+compress_page()
+{
+ local pname
+ local fname
+ local sect
+
+ # break up file name
+ pname=$1
+ IFS='.' ; set $pname
+ if [ $# -lt 2 ] ; then
+ IFS=" " ; echo ignoring $pname 1>&2 ; return 0 ;
+ fi
+ # construct name and section
+ fname=$1 ; shift
+ while [ $# -gt 1 ] ; do
+ fname=$fname.$1
+ shift
+ done
+ sect=$1
+
+ IFS=" "
+ case "$sect" in
+ gz) { echo file $pname already gzipped 1>&2 ; } ;;
+ Z) { echo file $pname already compressed 1>&2 ; } ;;
+ [12345678ln]*){
+ IFS=" " ; set `file $pname`
+ if [ $2 = "gzip" ] ; then
+ echo moving hard link $pname 1>&2
+ mv $pname $pname.gz # link
+ else
+ if [ $2 != "symbolic" ] ; then
+ echo gzipping page $pname 1>&2
+ temp=`mktemp -t manager` || exit 1
+ cat $pname | \
+ (cd .. ; soelim )| gzip -c -- > $temp
+ chmod u+w $pname
+ cp $temp $pname
+ chmod 444 $pname
+ mv $pname $pname.gz
+ rm -f $temp
+ else
+ # skip symlink - this can be
+ # a program like expn, which is
+ # its own man page !
+ echo skipping symlink $pname 1>&2
+ fi
+ fi };;
+ *) {
+ IFS=" "
+ echo skipping file $pname 1>&2
+ } ;;
+ esac
+ # reset IFS - this is important!
+ IFS=" "
+}
+
+#
+# Compress man pages in paths
+#
+do_compress_so()
+{
+ local i
+ local dir
+ local workdir
+ local what
+
+ what=$1
+ shift
+ workdir=`pwd`
+ while [ $# != 0 ] ; do
+ if [ -d $1 ] ; then
+ dir=$1
+ cd $dir
+ for i in * ; do
+ case $i in
+ *cat?) ;; # ignore cat directories
+ *) {
+ if [ -d $i ] ; then
+ do_compress_so $what $i
+ else
+ if [ -e $i ] ; then
+ $what $i
+ fi
+ fi } ;;
+ esac
+ done
+ cd $workdir
+ else
+ echo "directory $1 not found" 1>&2
+ fi
+ shift
+ done
+}
+
+#
+# Display a usage message
+#
+ctl_usage()
+{
+ echo "usage: $1 -compress <path> ... " 1>&2
+ echo " $1 -uncompress <path> ... " 1>&2
+ echo " $1 -purge <days> <path> ... " 1>&2
+ echo " $1 -purge expire <path> ... " 1>&2
+ exit 1
+}
+
+#
+# remove .so's and do compress
+#
+do_compress()
+{
+ # First remove all so's from the pages to be compressed
+ do_compress_so remove_so "$@"
+ # now do ahead and compress the pages
+ do_compress_so compress_page "$@"
+}
+
+#
+# dispatch options
+#
+if [ $# -lt 2 ] ; then ctl_usage $0 ; fi ;
+
+case "$1" in
+ -compress) shift ; do_compress "$@" ;;
+ -uncompress) shift ; do_uncompress "$@" ;;
+ -purge) shift ; do_purge "$@" ;;
+ *) ctl_usage $0 ;;
+esac
diff --git a/usr.sbin/memcontrol/Makefile b/usr.sbin/memcontrol/Makefile
new file mode 100644
index 0000000..d465d53
--- /dev/null
+++ b/usr.sbin/memcontrol/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+PROG= memcontrol
+MAN= memcontrol.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/memcontrol/memcontrol.8 b/usr.sbin/memcontrol/memcontrol.8
new file mode 100644
index 0000000..998d9cd
--- /dev/null
+++ b/usr.sbin/memcontrol/memcontrol.8
@@ -0,0 +1,113 @@
+.\" Copyright (c) 1999 Chris Costello
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd September 28, 1999
+.Dt MEMCONTROL 8
+.Os
+.Sh NAME
+.Nm memcontrol
+.Nd "control system cache behaviour with respect to memory"
+.Sh SYNOPSIS
+.Nm
+.Ar list
+.Op Fl a
+.Pp
+.Nm
+.Ar set
+.Fl b Ar base
+.Fl l Ar length
+.Fl o Ar owner
+.Ar attribute
+.Pp
+.Nm
+.Ar clear
+.Fl o Ar owner
+.Pp
+.Nm
+.Ar clear
+.Fl b Ar base
+.Fl l Ar length
+.Sh DESCRIPTION
+A number of supported system architectures allow the behaviour of the CPU
+cache to be programmed to behave differently depending on the region being
+written.
+.Pp
+.Nm Memcontrol
+provides an interface to this facility, allowing CPU cache behavior to
+be altered for ranges of system physical memory.
+.Pp
+These ranges are typically power-of-2 aligned and sized, however the specific
+rules governing their layout vary between architectures. The
+.Nm
+program does not attempt to enforce these rules, however the system will
+reject any attempt to set an illegal combination.
+.Bl -tag -width clear
+.It Ar list
+List range slots.
+.Bl -tag -width xxxxxx
+.It Op Fl a
+List all range slots, even those that are inactive
+.El
+.It Ar set
+Set memory range attributes.
+.Bl -tag -width xxxxxx
+.It Fl b Ar base
+Memory range base address
+.It Fl l Ar length
+Length of memory range in bytes, power of 2
+.It Fl o Ar owner
+Text identifier for this setting (7 char max)
+.It Ar attribute
+Attributes applied to this range; one of
+.Ar uncacheable ,
+.Ar write-combine ,
+.Ar write-through ,
+.Ar write-back ,
+.Ar write-protect
+.El
+.It Ar clear
+Clear memory range attributes.
+Ranges may be cleared by owner or by
+base/length combination.
+.Pp
+To clear based on ownership:
+.Bl -tag -width xxxxxx
+.It Fl o Ar owner
+All ranges with this owner will be cleared
+.El
+.Pp
+To clear based on the base/length combination:
+.Bl -tag -width xxxxxx
+.It Fl b Ar base
+Memory range base address
+.It Fl l Ar length
+Length of memory range in bytes, power of 2
+.El
+.Pp
+Base and length must exactly match an existing range.
+.El
+.Sh SEE ALSO
+.Xr mem 4
diff --git a/usr.sbin/memcontrol/memcontrol.c b/usr.sbin/memcontrol/memcontrol.c
new file mode 100644
index 0000000..2554723
--- /dev/null
+++ b/usr.sbin/memcontrol/memcontrol.c
@@ -0,0 +1,342 @@
+/*-
+ * Copyright (c) 1999 Michael Smith <msmith@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/memrange.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+struct
+{
+ char *name;
+ int val;
+ int kind;
+#define MDF_SETTABLE (1<<0)
+} attrnames[] = {
+ {"uncacheable", MDF_UNCACHEABLE, MDF_SETTABLE},
+ {"write-combine", MDF_WRITECOMBINE, MDF_SETTABLE},
+ {"write-through", MDF_WRITETHROUGH, MDF_SETTABLE},
+ {"write-back", MDF_WRITEBACK, MDF_SETTABLE},
+ {"write-protect", MDF_WRITEPROTECT, MDF_SETTABLE},
+ {"unknown", MDF_UNKNOWN, 0},
+ {"fixed-base", MDF_FIXBASE, 0},
+ {"fixed-length", MDF_FIXLEN, 0},
+ {"set-by-firmware", MDF_FIRMWARE, 0},
+ {"active", MDF_ACTIVE, MDF_SETTABLE},
+ {"bogus", MDF_BOGUS, 0},
+ {NULL, 0, 0}
+};
+
+static void listfunc(int memfd, int argc, char *argv[]);
+static void setfunc(int memfd, int argc, char *argv[]);
+static void clearfunc(int memfd, int argc, char *argv[]);
+static void helpfunc(int memfd, int argc, char *argv[]);
+static void help(char *what);
+
+struct
+{
+ char *cmd;
+ char *desc;
+ void (*func)(int memfd, int argc, char *argv[]);
+} functions[] = {
+ {"list",
+ "List current memory range attributes\n"
+ " list [-a]\n"
+ " -a list all range slots, even those that are inactive",
+ listfunc},
+ {"set",
+ "Set memory range attributes\n"
+ " set -b <base> -l <length> -o <owner> <attribute>\n"
+ " <base> memory range base address\n"
+ " <length> length of memory range in bytes, power of 2\n"
+ " <owner> text identifier for this setting (7 char max)\n"
+ " <attribute> attribute(s) to be applied to this range:\n"
+ " uncacheable\n"
+ " write-combine\n"
+ " write-through\n"
+ " write-back\n"
+ " write-protect",
+ setfunc},
+ {"clear",
+ "Clear memory range attributes\n"
+ " clear -o <owner>\n"
+ " <owner> all ranges with this owner will be cleared\n"
+ " clear -b <base> -l <length>\n"
+ " <base> memory range base address\n"
+ " <length> length of memory range in bytes, power of 2\n"
+ " Base and length must exactly match an existing range",
+ clearfunc},
+ {NULL, NULL, helpfunc}
+};
+
+int
+main(int argc, char *argv[])
+{
+ int i, memfd;
+
+ if (argc < 2) {
+ help(NULL);
+ } else {
+ if ((memfd = open(_PATH_MEM, O_RDONLY)) == -1)
+ err(1, "can't open %s", _PATH_MEM);
+
+ for (i = 0; functions[i].cmd != NULL; i++)
+ if (!strcmp(argv[1], functions[i].cmd))
+ break;
+ functions[i].func(memfd, argc - 1, argv + 1);
+ close(memfd);
+ }
+ return(0);
+}
+
+static struct mem_range_desc *
+mrgetall(int memfd, int *nmr)
+{
+ struct mem_range_desc *mrd;
+ struct mem_range_op mro;
+
+ mro.mo_arg[0] = 0;
+ if (ioctl(memfd, MEMRANGE_GET, &mro))
+ err(1, "can't size range descriptor array");
+
+ *nmr = mro.mo_arg[0];
+ mrd = malloc(*nmr * sizeof(struct mem_range_desc));
+ if (mrd == NULL)
+ errx(1, "can't allocate %d bytes for %d range descriptors",
+ *nmr * sizeof(struct mem_range_desc), *nmr);
+
+ mro.mo_arg[0] = *nmr;
+ mro.mo_desc = mrd;
+ if (ioctl(memfd, MEMRANGE_GET, &mro))
+ err(1, "can't fetch range descriptor array");
+
+ return(mrd);
+}
+
+
+static void
+listfunc(int memfd, int argc, char *argv[])
+{
+ struct mem_range_desc *mrd;
+ int nd, i, j;
+ int ch;
+ int showall = 0;
+ char *owner;
+
+ owner = NULL;
+ while ((ch = getopt(argc, argv, "ao:")) != -1)
+ switch(ch) {
+ case 'a':
+ showall = 1;
+ break;
+ case 'o':
+ owner = strdup(optarg);
+ break;
+ case '?':
+ default:
+ help("list");
+ }
+
+ mrd = mrgetall(memfd, &nd);
+
+ for (i = 0; i < nd; i++) {
+ if (!showall && !(mrd[i].mr_flags & MDF_ACTIVE))
+ continue;
+ if (owner && strcmp(mrd[i].mr_owner, owner))
+ continue;
+ printf("%qx/%qx %.8s ", mrd[i].mr_base, mrd[i].mr_len,
+ mrd[i].mr_owner[0] ? mrd[i].mr_owner : "-");
+ for (j = 0; attrnames[j].name != NULL; j++)
+ if (mrd[i].mr_flags & attrnames[j].val)
+ printf("%s ", attrnames[j].name);
+ printf("\n");
+ }
+ free(mrd);
+ if (owner)
+ free(owner);
+}
+
+static void
+setfunc(int memfd, int argc, char *argv[])
+{
+ struct mem_range_desc mrd;
+ struct mem_range_op mro;
+ int i;
+ int ch;
+ char *ep;
+
+ mrd.mr_base = 0;
+ mrd.mr_len = 0;
+ mrd.mr_flags = 0;
+ strcpy(mrd.mr_owner, "user");
+ while ((ch = getopt(argc, argv, "b:l:o:")) != -1)
+ switch(ch) {
+ case 'b':
+ mrd.mr_base = strtouq(optarg, &ep, 0);
+ if ((ep == optarg) || (*ep != 0))
+ help("set");
+ break;
+ case 'l':
+ mrd.mr_len = strtouq(optarg, &ep, 0);
+ if ((ep == optarg) || (*ep != 0))
+ help("set");
+ break;
+ case 'o':
+ if ((*optarg == 0) || (strlen(optarg) > 7))
+ help("set");
+ strcpy(mrd.mr_owner, optarg);
+ break;
+
+ case '?':
+ default:
+ help("set");
+ }
+
+ if (mrd.mr_len == 0)
+ help("set");
+
+ argc -= optind;
+ argv += optind;
+
+ while(argc--) {
+ for (i = 0; attrnames[i].name != NULL; i++) {
+ if (!strcmp(attrnames[i].name, argv[0])) {
+ if (!attrnames[i].kind & MDF_SETTABLE)
+ help("flags");
+ mrd.mr_flags |= attrnames[i].val;
+ break;
+ }
+ }
+ if (attrnames[i].name == NULL)
+ help("flags");
+ argv++;
+ }
+
+ mro.mo_desc = &mrd;
+ mro.mo_arg[0] = 0;
+ if (ioctl(memfd, MEMRANGE_SET, &mro))
+ err(1, "can't set range");
+}
+
+static void
+clearfunc(int memfd, int argc, char *argv[])
+{
+ struct mem_range_desc mrd, *mrdp;
+ struct mem_range_op mro;
+ int i, nd;
+ int ch;
+ char *ep, *owner;
+
+ mrd.mr_base = 0;
+ mrd.mr_len = 0;
+ owner = NULL;
+ while ((ch = getopt(argc, argv, "b:l:o:")) != -1)
+ switch(ch) {
+ case 'b':
+ mrd.mr_base = strtouq(optarg, &ep, 0);
+ if ((ep == optarg) || (*ep != 0))
+ help("clear");
+ break;
+ case 'l':
+ mrd.mr_len = strtouq(optarg, &ep, 0);
+ if ((ep == optarg) || (*ep != 0))
+ help("clear");
+ break;
+ case 'o':
+ if ((*optarg == 0) || (strlen(optarg) > 7))
+ help("clear");
+ owner = strdup(optarg);
+ break;
+
+ case '?':
+ default:
+ help("clear");
+ }
+
+ if (owner != NULL) {
+ /* clear-by-owner */
+ if ((mrd.mr_base != 0) || (mrd.mr_len != 0))
+ help("clear");
+
+ mrdp = mrgetall(memfd, &nd);
+ mro.mo_arg[0] = MEMRANGE_SET_REMOVE;
+ for (i = 0; i < nd; i++) {
+ if (!strcmp(owner, mrdp[i].mr_owner) &&
+ (mrdp[i].mr_flags & MDF_ACTIVE) &&
+ !(mrdp[i].mr_flags & MDF_FIXACTIVE)) {
+
+ mro.mo_desc = mrdp + i;
+ if (ioctl(memfd, MEMRANGE_SET, &mro))
+ warn("couldn't clear range owned by '%s'", owner);
+ }
+ }
+ } else if (mrd.mr_len != 0) {
+ /* clear-by-base/len */
+ mro.mo_arg[0] = MEMRANGE_SET_REMOVE;
+ mro.mo_desc = &mrd;
+ if (ioctl(memfd, MEMRANGE_SET, &mro))
+ err(1, "couldn't clear range");
+ } else {
+ help("clear");
+ }
+}
+
+static void
+helpfunc(int memfd, int argc, char *argv[])
+{
+ help(argv[1]);
+}
+
+static void
+help(char *what)
+{
+ int i;
+
+ if (what != NULL) {
+ /* find a function that matches */
+ for (i = 0; functions[i].cmd != NULL; i++)
+ if (!strcmp(what, functions[i].cmd)) {
+ fprintf(stderr, "%s\n", functions[i].desc);
+ return;
+ }
+ fprintf(stderr, "Unknown command '%s'\n", what);
+ }
+
+ /* print general help */
+ fprintf(stderr, "Valid commands are :\n");
+ for (i = 0; functions[i].cmd != NULL; i++)
+ fprintf(stderr, " %s\n", functions[i].cmd);
+ fprintf(stderr, "Use help <command> for command-specific help\n");
+}
diff --git a/usr.sbin/mergemaster/Makefile b/usr.sbin/mergemaster/Makefile
new file mode 100644
index 0000000..92d9dd9
--- /dev/null
+++ b/usr.sbin/mergemaster/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+MAINTAINER= dougb@FreeBSD.org
+
+SCRIPTS= mergemaster.sh
+MAN= mergemaster.8
+
+.include <bsd.prog.mk>
+
diff --git a/usr.sbin/mergemaster/mergemaster.8 b/usr.sbin/mergemaster/mergemaster.8
new file mode 100644
index 0000000..3bb7558
--- /dev/null
+++ b/usr.sbin/mergemaster/mergemaster.8
@@ -0,0 +1,380 @@
+.\" Copyright (c) 1998-2002 Douglas Barton
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd February 5, 2001
+.Dt MERGEMASTER 8
+.Os
+.Sh NAME
+.Nm mergemaster
+.Nd merge configuration files, et al during an upgrade
+.Sh SYNOPSIS
+.Nm
+.Op Fl scrvahipC
+.Op Fl m Ar /path/to/sources
+.Op Fl t Ar /path/to/temp/root
+.Op Fl d
+.Op Fl u Ar N
+.Op Fl w Ar N
+.Op Fl D Ar /path
+.Sh DESCRIPTION
+.Nm
+is a Bourne shell script which is designed to aid you
+in updating the various configuration and other files
+associated with
+.Fx .
+It is
+.Sy HIGHLY
+recommended that you back up your
+.Pa /etc
+directory before beginning this process.
+.Pp
+The script uses
+.Pa /usr/src/etc/Makefile
+to build a temporary root environment from
+.Pa /
+down, populating that environment with the various
+files.
+You can specify a different source directory
+with the
+.Op Fl m
+command line option, or specify the destination
+directory with the
+.Op Fl D
+option.
+It then compares each file in that environment
+to its installed counterpart.
+When the script finds a
+change in the new file, or there is no installed
+version of the new file it gives you four options to
+deal with it.
+You can install the new file as is,
+delete the new file, merge the old and new
+files (as appropriate) using
+.Xr sdiff 1
+or leave the file in the temporary root environment to
+merge by hand later.
+.Pp
+By default it creates the temporary root in
+.Pa /var/tmp/temproot
+and compares the
+.Xr cvs 1
+version $Id/$FreeBSD strings for files that have them, deleting
+the temporary file if the strings match.
+If there is
+no $Id string, or if the strings are different it
+compares the files themselves.
+You can
+also specify that the script ignore the $Id strings and
+compare every file.
+.Pp
+.Nm
+checks your umask and issues a warning for anything
+other than 022. While it is not mandatory to grant
+world read permissions for most configuration files, you
+may run into problems without them.
+If you choose a
+umask other than 022 and experience trouble later this
+could be the cause.
+.Pa /etc/master.passwd
+is treated as a special case.
+If you choose to install
+this file or a merged version of it the file permissions
+are always 600 (rw-------) for security reasons.
+After
+installing an updated version of this file you should
+probably run
+.Xr pwd_mkdb 8
+with the -p option to rebuild your password databases
+and recreate
+.Pa /etc/passwd .
+.Pp
+The script uses the owner and group id's
+that the files are created with by
+.Pa /usr/src/etc/Makefile ,
+and file permissions as specified by the umask.
+Unified diffs are used by default to display any
+differences unless you choose context diffs.
+.Pp
+.Nm
+will source scripts that you specify right before
+it starts the comparison, and after it's done running.
+The easiest way to handle this is to place the path
+to the script(s) in the appropriate variables in your
+.Pa .mergemasterrc
+file.
+The script sourced before comparison is named in
+.Ev MM_PRE_COMPARE_SCRIPT ,
+and the one sourced after the script is done is
+.Ev MM_EXIT_SCRIPT .
+This is the recommended way to specify local modifications,
+or files that you want to give special handling to.
+This includes files that you want to be deleted without
+being compared.
+Because the named scripts are sourced from within
+.Nm ,
+all of the script's variables are available for use in
+your custom script.
+You can also use
+.Pa /etc/mergemaster.rc
+which will be read before
+.Pa .mergemasterrc .
+Options specified on the command line are updated last,
+and therefore can override both files.
+.Pp
+The options are as follows:
+.Bl -tag -width Fl
+.It Fl s
+Perform a strict comparison, diff'ing every pair of files.
+This comparison is performed line by line,
+without regard to CVS $Id's.
+.It Fl c
+Use context diffs instead of unified diffs.
+.It Fl r
+Re-run
+.Nm
+on a previously cleaned directory, skipping the creation of
+the temporary root environment.
+This option is compatible
+with all other options.
+.It Fl v
+Be more verbose about the process.
+You should probably use
+this option the first time you run
+.Nm .
+This option also gives you a list of files that exist
+only in the installed version of
+.Pa /etc .
+.It Fl a
+Run automatically.
+This option will leave all the files that
+differ from the installed versions in the temporary directory
+to be dealt with by hand.
+If the
+.Pa temproot
+directory exists, it creates a new one in a previously
+non-existent directory.
+This option unsets the verbose flag,
+but is compatible with all other options.
+Setting -a makes
+-w superfluous.
+.It Fl h
+Display usage and help information.
+.It Fl i
+Automatically install any files that do not exist in the
+destination directory.
+.It Fl p
+Pre-buildworld mode.
+Compares only files known to be essential to the success of
+{build|install}world,
+including
+.Pa /etc/make.conf .
+.It Fl C
+After a standard
+.Nm
+run,
+compares your rc.conf[.local] options to the defaults.
+.It Fl m Ar /path/to/sources
+Specify the path to the directory where you want to do the
+.Xr make 1 .
+(In other words, where your sources are, but -s was already
+taken.)
+.It Fl t Ar /path/to/temp/root
+Create the temporary root environment in
+.Pa /path/to/temp/root
+instead of the default
+.Pa /var/tmp/temproot .
+.It Fl d
+Add the date and time to the name of the temporary
+root directory.
+If -t is specified, this option must
+follow it if you want the date added too.
+.It Fl u Ar N
+Specify a numeric umask.
+The default is 022.
+.It Fl w Ar N
+Supply an alternate screen width to the
+.Xr sdiff 1
+command in numbers of columns.
+The default is 80.
+.It Fl D Ar /path
+Specify the destination directory for the installed files.
+.El
+.Sh ENVIRONMENT
+The
+.Nm
+script uses the
+.Ev PAGER
+environment variable if set.
+Otherwise it uses
+.Xr more 1 .
+If
+.Ev PAGER
+specifies a program outside
+its
+limited
+.Ev PATH
+without specifying the full path,
+.Nm
+prompts you with options on how to proceed.
+The
+.Ev MM_PRE_COMPARE_SCRIPT
+and
+.Ev MM_EXIT_SCRIPT
+variables are used as described above.
+Other variables that are used by the script internally
+can be specified in
+.Pa .mergemasterrc
+as described in more detail below.
+.Sh EXAMPLES
+Typically all you will need to do is type
+.Nm
+at the prompt and the script will do all the work for you.
+.Pp
+To use context diff's and have
+.Nm
+explain more things as it goes along, use:
+.Pp
+.Dl # mergemaster -cv
+.Pp
+To specify that
+.Nm
+put the temporary root environment in
+.Pa /usr/tmp/root ,
+use:
+.Pp
+.Dl # mergemaster -t /usr/tmp/root
+.Pp
+To specify a 110 column screen with a strict
+comparison, use:
+.Pp
+.Dl # mergemaster -sw 110
+.Sh FILES
+.Bl -tag -width $HOME/.mergemasterrc -compact
+.It Pa /etc/mergemaster.rc
+.It Pa $HOME/.mergemasterrc
+.El
+.Pp
+.Nm
+will . (source) these files if they exist.
+Command line options
+will override rc file options.
+.Pa $HOME/.mergemasterrc
+overrides
+.Pa /etc/mergemaster.rc .
+Here is an example
+with all values commented out:
+.Pp
+.Bd -literal
+# These are options for mergemaster, with their default values listed
+# The following options have command line overrides
+#
+# Directory to install the temporary root environment into
+#TEMPROOT='/var/tmp/temproot'
+#
+# Strict comparison bypasses the CVS $Id tests and compares every file
+#STRICT=no
+#
+# Flag(s) to use for diff displayed when files differ
+#DIFF_FLAG='-u'
+#
+# Verbose mode includes more details and additional checks
+#VERBOSE=
+#
+# Automatically install files that do not exist on the system already
+#AUTO_INSTALL=
+#
+# Compare /etc/rc.conf[.local] to /etc/defaults/rc.conf
+#COMP_CONFS=yes
+#
+# Sourcedir is the directory to do the 'make' in (where the new files are)
+#SOURCEDIR='/usr/src/etc'
+#
+# The umask for mergemaster to compare the default file's modes to
+#NEW_UMASK=022
+#
+# Specify the destination directory for the installed files
+#DESTDIR=
+#
+# The following options have no command line overrides
+# For those who just cannot stand including the full path to PAGER
+#DONT_CHECK_PAGER=
+#
+# If you set 'yes' above, make sure to include the PATH to your pager
+#PATH=/bin:/usr/bin:/usr/sbin
+#
+# Don't compare the old and new motd files
+#IGNORE_MOTD=yes
+#
+# Specify the path to scripts to run before the comparison starts,
+# and/or after the script has finished its work
+#MM_PRE_COMPARE_SCRIPT=
+#MM_EXIT_SCRIPT=
+.Ed
+.Sh SEE ALSO
+.Xr cvs 1 ,
+.Xr diff 1 ,
+.Xr make 1 ,
+.Xr more 1 ,
+.Xr sdiff 1 ,
+.Xr pwd_mkdb 8
+.Pp
+.Pa /usr/src/etc/Makefile
+.Rs
+.%O http://www.FreeBSD.org/handbook/makeworld.html
+.%T The Cutting Edge (using make world)
+.%A Nik Clayton
+.Re
+.Sh DIAGNOSTICS
+Exit status is 0 on successful completion, or if the user bails out
+manually at some point during execution.
+.Pp
+Exit status is 1 if it fails for one of the following reasons:
+.Pp
+Invalid command line option
+.Pp
+Failure to create the temporary root environment
+.Pp
+Failure to populate the temporary root
+.Sh HISTORY
+The
+.Nm
+script was first publicly available on one of my
+web pages in a much simpler form under the name
+.Pa comproot
+on 13 March 1998. The idea for creating the
+temporary root environment comes from Nik Clayton's
+make world tutorial which is referenced above.
+.Sh AUTHORS
+This manual page and the script itself were written by
+.An Douglas Barton Aq DougB@FreeBSD.org .
+.Sh BUGS
+There are no known bugs.
+Please report any problems,
+comments or suggestions to the author.
+Several of the
+improvements to this program have come from user
+suggestions.
+Thank you.
diff --git a/usr.sbin/mergemaster/mergemaster.sh b/usr.sbin/mergemaster/mergemaster.sh
new file mode 100755
index 0000000..ecf134c
--- /dev/null
+++ b/usr.sbin/mergemaster/mergemaster.sh
@@ -0,0 +1,1032 @@
+#!/bin/sh
+
+# mergemaster
+
+# Compare files created by /usr/src/etc/Makefile (or the directory
+# the user specifies) with the currently installed copies.
+
+# Copyright 1998-2002 Douglas Barton
+# DougB@FreeBSD.org
+
+# $FreeBSD$
+
+PATH=/bin:/usr/bin:/usr/sbin
+
+display_usage () {
+ VERSION_NUMBER=`grep "[$]FreeBSD:" $0 | cut -d ' ' -f 4`
+ echo "mergemaster version ${VERSION_NUMBER}"
+ echo 'Usage: mergemaster [-scrvahipC] [-m /path]'
+ echo ' [-t /path] [-d] [-u N] [-w N] [-D /path]'
+ echo "Options:"
+ echo " -s Strict comparison (diff every pair of files)"
+ echo " -c Use context diff instead of unified diff"
+ echo " -r Re-run on a previously cleaned directory (skip temproot creation)"
+ echo " -v Be more verbose about the process, include additional checks"
+ echo " -a Leave all files that differ to merge by hand"
+ echo " -h Display more complete help"
+ echo ' -i Automatically install files that do not exist in destination directory'
+ echo ' -p Pre-buildworld mode, only compares crucial files'
+ echo ' -C Compare local rc.conf variables to the defaults'
+ echo " -m /path/directory Specify location of source to do the make in"
+ echo " -t /path/directory Specify temp root directory"
+ echo " -d Add date and time to directory name (e.g., /var/tmp/temproot.`date +%m%d.%H.%M`)"
+ echo " -u N Specify a numeric umask"
+ echo " -w N Specify a screen width in columns to sdiff"
+ echo ' -D /path/directory Specify the destination directory to install files to'
+ echo ''
+}
+
+display_help () {
+ echo "* To specify a directory other than /var/tmp/temproot for the"
+ echo " temporary root environment, use -t /path/to/temp/root"
+ echo "* The -w option takes a number as an argument for the column width"
+ echo " of the screen. The default is 80."
+ echo '* The -a option causes mergemaster to run without prompting.'
+}
+
+# Loop allowing the user to use sdiff to merge files and display the merged
+# file.
+merge_loop () {
+ case "${VERBOSE}" in
+ '') ;;
+ *)
+ echo " *** Type h at the sdiff prompt (%) to get usage help"
+ ;;
+ esac
+ echo ''
+ MERGE_AGAIN=yes
+ while [ "${MERGE_AGAIN}" = "yes" ]; do
+ # Prime file.merged so we don't blat the owner/group id's
+ cp -p "${COMPFILE}" "${COMPFILE}.merged"
+ sdiff -o "${COMPFILE}.merged" --text --suppress-common-lines \
+ --width=${SCREEN_WIDTH:-80} "${DESTDIR}${COMPFILE#.}" "${COMPFILE}"
+ INSTALL_MERGED=V
+ while [ "${INSTALL_MERGED}" = "v" -o "${INSTALL_MERGED}" = "V" ]; do
+ echo ''
+ echo " Use 'i' to install merged file"
+ echo " Use 'r' to re-do the merge"
+ echo " Use 'v' to view the merged file"
+ echo " Default is to leave the temporary file to deal with by hand"
+ echo ''
+ echo -n " *** How should I deal with the merged file? [Leave it for later] "
+ read INSTALL_MERGED
+
+ case "${INSTALL_MERGED}" in
+ [iI])
+ mv "${COMPFILE}.merged" "${COMPFILE}"
+ echo ''
+ if mm_install "${COMPFILE}"; then
+ echo " *** Merged version of ${COMPFILE} installed successfully"
+ else
+ echo " *** Problem installing ${COMPFILE}, it will remain to merge by hand later"
+ fi
+ unset MERGE_AGAIN
+ ;;
+ [rR])
+ rm "${COMPFILE}.merged"
+ ;;
+ [vV])
+ ${PAGER} "${COMPFILE}.merged"
+ ;;
+ '')
+ echo " *** ${COMPFILE} will remain for your consideration"
+ unset MERGE_AGAIN
+ ;;
+ *)
+ echo "invalid choice: ${INSTALL_MERGED}"
+ INSTALL_MERGED=V
+ ;;
+ esac
+ done
+ done
+}
+
+# Loop showing user differences between files, allow merge, skip or install
+# options
+diff_loop () {
+
+ HANDLE_COMPFILE=v
+
+ while [ "${HANDLE_COMPFILE}" = "v" -o "${HANDLE_COMPFILE}" = "V" -o \
+ "${HANDLE_COMPFILE}" = "NOT V" ]; do
+ if [ -f "${DESTDIR}${COMPFILE#.}" -a -f "${COMPFILE}" ]; then
+ if [ "${HANDLE_COMPFILE}" = "v" -o "${HANDLE_COMPFILE}" = "V" ]; then
+ echo ''
+ echo ' ====================================================================== '
+ echo ''
+ (
+ echo ''
+ echo " *** Displaying differences between ${COMPFILE} and installed version:"
+ echo ''
+ diff "${DIFF_FLAG}" "${DESTDIR}${COMPFILE#.}" "${COMPFILE}"
+ ) | ${PAGER}
+ echo ''
+ fi
+ else
+ echo ''
+ echo " *** There is no installed version of ${COMPFILE}"
+ echo ''
+ case "${AUTO_INSTALL}" in
+ [Yy][Ee][Ss])
+ echo ''
+ if mm_install "${COMPFILE}"; then
+ echo " *** ${COMPFILE} installed successfully"
+ echo ''
+ # Make the list print one file per line
+ AUTO_INSTALLED_FILES="${AUTO_INSTALLED_FILES} ${DESTDIR}${COMPFILE#.}
+"
+ else
+ echo " *** Problem installing ${COMPFILE}, it will remain to merge by hand"
+ fi
+ return
+ ;;
+ *)
+ NO_INSTALLED=yes
+ ;;
+ esac
+ fi
+
+ echo " Use 'd' to delete the temporary ${COMPFILE}"
+ echo " Use 'i' to install the temporary ${COMPFILE}"
+ case "${NO_INSTALLED}" in
+ '')
+ echo " Use 'm' to merge the temporary and installed versions"
+ echo " Use 'v' to view the diff results again"
+ ;;
+ esac
+ echo ''
+ echo " Default is to leave the temporary file to deal with by hand"
+ echo ''
+ echo -n "How should I deal with this? [Leave it for later] "
+ read HANDLE_COMPFILE
+
+ case "${HANDLE_COMPFILE}" in
+ [dD])
+ rm "${COMPFILE}"
+ echo ''
+ echo " *** Deleting ${COMPFILE}"
+ ;;
+ [iI])
+ echo ''
+ if mm_install "${COMPFILE}"; then
+ echo " *** ${COMPFILE} installed successfully"
+ else
+ echo " *** Problem installing ${COMPFILE}, it will remain to merge by hand"
+ fi
+ ;;
+ [mM])
+ case "${NO_INSTALLED}" in
+ '')
+ # interact with user to merge files
+ merge_loop
+ ;;
+ *)
+ echo ''
+ echo " *** There is no installed version of ${COMPFILE}"
+ echo ''
+ HANDLE_COMPFILE="NOT V"
+ ;;
+ esac # End of "No installed version of file but user selected merge" test
+ ;;
+ [vV])
+ continue
+ ;;
+ '')
+ echo ''
+ echo " *** ${COMPFILE} will remain for your consideration"
+ ;;
+ *)
+ # invalid choice, show menu again.
+ echo "invalid choice: ${HANDLE_COMPFILE}"
+ echo ''
+ HANDLE_COMPFILE="NOT V"
+ continue
+ ;;
+ esac # End of "How to handle files that are different"
+ done
+ unset NO_INSTALLED
+ echo ''
+ case "${VERBOSE}" in
+ '') ;;
+ *)
+ sleep 3
+ ;;
+ esac
+}
+
+press_to_continue () {
+ local DISCARD
+ echo -n ' *** Press the [Enter] or [Return] key to continue '
+ read DISCARD
+}
+
+# Set the default path for the temporary root environment
+#
+TEMPROOT='/var/tmp/temproot'
+
+# Read /etc/mergemaster.rc first so the one in $HOME can override
+#
+if [ -r /etc/mergemaster.rc ]; then
+ . /etc/mergemaster.rc
+fi
+
+# Read .mergemasterrc before command line so CLI can override
+#
+if [ -r "$HOME/.mergemasterrc" ]; then
+ . "$HOME/.mergemasterrc"
+fi
+
+# Check the command line options
+#
+while getopts ":ascrvhipCm:t:du:w:D:" COMMAND_LINE_ARGUMENT ; do
+ case "${COMMAND_LINE_ARGUMENT}" in
+ s)
+ STRICT=yes
+ ;;
+ c)
+ DIFF_FLAG='-c'
+ ;;
+ r)
+ RERUN=yes
+ ;;
+ v)
+ case "${AUTO_RUN}" in
+ '') VERBOSE=yes ;;
+ esac
+ ;;
+ a)
+ AUTO_RUN=yes
+ unset VERBOSE
+ ;;
+ h)
+ display_usage
+ display_help
+ exit 0
+ ;;
+ i)
+ AUTO_INSTALL=yes
+ ;;
+ C)
+ COMP_CONFS=yes
+ ;;
+ p)
+ PRE_WORLD=yes
+ unset COMP_CONFS
+ unset AUTO_RUN
+ ;;
+ m)
+ SOURCEDIR=${OPTARG}
+ ;;
+ t)
+ TEMPROOT=${OPTARG}
+ ;;
+ d)
+ TEMPROOT=${TEMPROOT}.`date +%m%d.%H.%M`
+ ;;
+ u)
+ NEW_UMASK=${OPTARG}
+ ;;
+ w)
+ SCREEN_WIDTH=${OPTARG}
+ ;;
+ D)
+ DESTDIR=${OPTARG}
+ ;;
+ *)
+ display_usage
+ exit 1
+ ;;
+ esac
+done
+
+echo ''
+
+# If the user has a pager defined, make sure we can run it
+#
+case "${DONT_CHECK_PAGER}" in
+'')
+ while ! type "${PAGER%% *}" >/dev/null && [ -n "${PAGER}" ]; do
+ echo " *** Your PAGER environment variable specifies '${PAGER}', but"
+ echo " due to the limited PATH that I use for security reasons,"
+ echo " I cannot execute it. So, what would you like to do?"
+ echo ''
+ echo " Use 'e' to exit mergemaster and fix your PAGER variable"
+ if [ -x /usr/bin/less -o -x /usr/local/bin/less ]; then
+ echo " Use 'l' to set PAGER to 'less' for this run"
+ fi
+ echo " Use 'm' to use plain old 'more' as your PAGER for this run"
+ echo ''
+ echo " Default is to use plain old 'more' "
+ echo ''
+ echo -n "What should I do? [Use 'more'] "
+ read FIXPAGER
+
+ case "${FIXPAGER}" in
+ [eE])
+ exit 0
+ ;;
+ [lL])
+ if [ -x /usr/bin/less ]; then
+ PAGER=/usr/bin/less
+ elif [ -x /usr/local/bin/less ]; then
+ PAGER=/usr/local/bin/less
+ else
+ echo ''
+ echo " *** Fatal Error:"
+ echo " You asked to use 'less' as your pager, but I can't"
+ echo " find it in /usr/bin or /usr/local/bin"
+ exit 1
+ fi
+ ;;
+ [mM]|'')
+ PAGER=more
+ ;;
+ *)
+ echo ''
+ echo "invalid choice: ${FIXPAGER}"
+ esac
+ echo ''
+ done
+ ;;
+esac
+
+# If user has a pager defined, or got assigned one above, use it.
+# If not, use more.
+#
+PAGER=${PAGER:-more}
+
+if [ -n "${VERBOSE}" -a ! "${PAGER}" = "more" ]; then
+ echo " *** You have ${PAGER} defined as your pager so we will use that"
+ echo ''
+ sleep 3
+fi
+
+# Assign the diff flag once so we will not have to keep testing it
+#
+DIFF_FLAG=${DIFF_FLAG:--u}
+
+# Assign the source directory
+#
+SOURCEDIR=${SOURCEDIR:-/usr/src/etc}
+
+# Check the width of the user's terminal
+#
+if [ -t 0 ]; then
+ w=$(stty -a | sed -ne 's/.* \([0-9][0-9]*\) columns.*/\1/p')
+ case "${w}" in
+ 0|'') ;; # No-op, since the input is not valid
+ *)
+ case "${SCREEN_WIDTH}" in
+ '') SCREEN_WIDTH="${w}" ;;
+ "${w}") ;; # No-op, since they are the same
+ *)
+ echo -n "*** You entered ${SCREEN_WIDTH} as your screen width, but stty "
+ echo "thinks it is ${w}."
+ echo ''
+ echo -n "What would you like to use? [${w}] "
+ read SCREEN_WIDTH
+ case "${SCREEN_WIDTH}" in
+ '') SCREEN_WIDTH="${w}" ;;
+ esac
+ ;;
+ esac
+ esac
+fi
+
+# Define what CVS $Id tag to look for to aid portability.
+#
+CVS_ID_TAG=FreeBSD
+
+delete_temproot () {
+ rm -rf "${TEMPROOT}"
+ chflags -R 0 "${TEMPROOT}"
+ rm -rf "${TEMPROOT}"
+}
+
+case "${RERUN}" in
+'')
+ # Set up the loop to test for the existence of the
+ # temp root directory.
+ #
+ TEST_TEMP_ROOT=yes
+ while [ "${TEST_TEMP_ROOT}" = "yes" ]; do
+ if [ -d "${TEMPROOT}" ]; then
+ echo "*** The directory specified for the temporary root environment,"
+ echo " ${TEMPROOT}, exists. This can be a security risk if untrusted"
+ echo " users have access to the system."
+ echo ''
+ case "${AUTO_RUN}" in
+ '')
+ echo " Use 'd' to delete the old ${TEMPROOT} and continue"
+ echo " Use 't' to select a new temporary root directory"
+ echo " Use 'e' to exit mergemaster"
+ echo ''
+ echo " Default is to use ${TEMPROOT} as is"
+ echo ''
+ echo -n "How should I deal with this? [Use the existing ${TEMPROOT}] "
+ read DELORNOT
+
+ case "${DELORNOT}" in
+ [dD])
+ echo ''
+ echo " *** Deleting the old ${TEMPROOT}"
+ echo ''
+ delete_temproot || exit 1
+ unset TEST_TEMP_ROOT
+ ;;
+ [tT])
+ echo " *** Enter new directory name for temporary root environment"
+ read TEMPROOT
+ ;;
+ [eE])
+ exit 0
+ ;;
+ '')
+ echo ''
+ echo " *** Leaving ${TEMPROOT} intact"
+ echo ''
+ unset TEST_TEMP_ROOT
+ ;;
+ *)
+ echo ''
+ echo "invalid choice: ${DELORNOT}"
+ echo ''
+ ;;
+ esac
+ ;;
+ *)
+ # If this is an auto-run, try a hopefully safe alternative then
+ # re-test anyway.
+ TEMPROOT=/var/tmp/temproot.`date +%m%d.%H.%M.%S`
+ ;;
+ esac
+ else
+ unset TEST_TEMP_ROOT
+ fi
+ done
+
+ echo "*** Creating the temporary root environment in ${TEMPROOT}"
+
+ if mkdir -p "${TEMPROOT}"; then
+ echo " *** ${TEMPROOT} ready for use"
+ fi
+
+ if [ ! -d "${TEMPROOT}" ]; then
+ echo ''
+ echo " *** FATAL ERROR: Cannot create ${TEMPROOT}"
+ echo ''
+ exit 1
+ fi
+
+ echo " *** Creating and populating directory structure in ${TEMPROOT}"
+ echo ''
+
+ case "${VERBOSE}" in
+ '') ;;
+ *)
+ press_to_continue
+ ;;
+ esac
+
+ case "${PRE_WORLD}" in
+ '')
+ { cd ${SOURCEDIR} &&
+ case "${DESTDIR}" in
+ '') ;;
+ *)
+ make DESTDIR=${DESTDIR} distrib-dirs
+ ;;
+ esac
+ make DESTDIR=${TEMPROOT} distrib-dirs &&
+ make MAKEOBJDIRPREFIX=${TEMPROOT}/usr/obj obj &&
+ make MAKEOBJDIRPREFIX=${TEMPROOT}/usr/obj DESTDIR=${TEMPROOT} \
+ -DNO_MAKEDEV_RUN distribution;} ||
+ { echo '';
+ echo " *** FATAL ERROR: Cannot 'cd' to ${SOURCEDIR} and install files to";
+ echo " the temproot environment";
+ echo '';
+ exit 1;}
+ ;;
+ *)
+ # Only set up files that are crucial to {build|install}world
+ { mkdir -p ${TEMPROOT}/etc &&
+ cp -p ${SOURCEDIR}/master.passwd ${TEMPROOT}/etc &&
+ cp -p ${SOURCEDIR}/group ${TEMPROOT}/etc;} ||
+ { echo '';
+ echo ' *** FATAL ERROR: Cannot copy files to the temproot environment';
+ echo '';
+ exit 1;}
+ ;;
+ esac
+
+ # Doing the inventory and removing files that we don't want to compare only
+ # makes sense if we are not doing a rerun, since we have no way of knowing
+ # what happened to the files during previous incarnations.
+ case "${VERBOSE}" in
+ '') ;;
+ *)
+ echo ''
+ echo ' *** The following files exist only in the installed version of'
+ echo " ${DESTDIR}/etc. In the vast majority of cases these files"
+ echo ' are necessary parts of the system and should not be deleted.'
+ echo ' However because these files are not updated by this process you'
+ echo ' might want to verify their status before rebooting your system.'
+ echo ''
+ press_to_continue
+ diff -qr ${DESTDIR}/etc ${TEMPROOT}/etc | grep "^Only in /etc" | ${PAGER}
+ echo ''
+ press_to_continue
+ ;;
+ esac
+
+ # Avoid comparing the motd if the user specifies it in .mergemasterrc
+ case "${IGNORE_MOTD}" in
+ '') ;;
+ *) rm -f ${TEMPROOT}/etc/motd
+ ;;
+ esac
+
+ # Avoid trying to update MAKEDEV if /dev is on a devfs
+ if /sbin/sysctl vfs.devfs.generation > /dev/null 2>&1 ; then
+ rm -f ${TEMPROOT}/dev/MAKEDEV ${TEMPROOT}/dev/MAKEDEV.local
+ fi
+
+ ;; # End of the "RERUN" test
+esac
+
+# We really don't want to have to deal with these files, since
+# master.passwd is the real file that should be compared, then
+# the user should run pwd_mkdb if necessary.
+#
+rm -f ${TEMPROOT}/etc/spwd.db ${TEMPROOT}/etc/passwd ${TEMPROOT}/etc/pwd.db
+
+# We only need to compare things like freebsd.cf once
+find ${TEMPROOT}/usr/obj -type f -delete 2>/dev/null
+
+# Get ready to start comparing files
+
+# Check umask if not specified on the command line,
+# and we are not doing an autorun
+#
+if [ -z "${NEW_UMASK}" -a -z "${AUTO_RUN}" ]; then
+ USER_UMASK=`umask`
+ case "${USER_UMASK}" in
+ 0022|022) ;;
+ *)
+ echo ''
+ echo " *** Your umask is currently set to ${USER_UMASK}. By default, this script"
+ echo " installs all files with the same user, group and modes that"
+ echo " they are created with by ${SOURCEDIR}/Makefile, compared to"
+ echo " a umask of 022. This umask allows world read permission when"
+ echo " the file's default permissions have it."
+ echo ''
+ echo " No world permissions can sometimes cause problems. A umask of"
+ echo " 022 will restore the default behavior, but is not mandatory."
+ echo " /etc/master.passwd is a special case. Its file permissions"
+ echo " will be 600 (rw-------) if installed."
+ echo ''
+ echo -n "What umask should I use? [${USER_UMASK}] "
+ read NEW_UMASK
+
+ NEW_UMASK="${NEW_UMASK:-$USER_UMASK}"
+ ;;
+ esac
+ echo ''
+fi
+
+CONFIRMED_UMASK=${NEW_UMASK:-0022}
+
+# Warn users who still have ${DESTDIR}/etc/sysconfig
+#
+if [ -e "${DESTDIR}/etc/sysconfig" ]; then
+ echo ''
+ echo " *** There is a sysconfig file on this system in ${DESTDIR}/etc/."
+ echo ''
+ echo ' Starting with FreeBSD version 2.2.2 those settings moved from'
+ echo ' /etc/sysconfig to /etc/rc.conf. If you are upgrading an older'
+ echo ' system make sure that you transfer your settings by hand from'
+ echo ' sysconfig to rc.conf and install the rc.conf file. If you'
+ echo ' have already made this transition, you should consider'
+ echo ' renaming or deleting the sysconfig file.'
+ echo ''
+ case "${AUTO_RUN}" in
+ '')
+ echo -n "Continue with the merge process? [yes] "
+ read CONT_OR_NOT
+
+ case "${CONT_OR_NOT}" in
+ [nN]*)
+ exit 0
+ ;;
+ *)
+ echo " *** Continuing"
+ echo ''
+ ;;
+ esac
+ ;;
+ *) ;;
+ esac
+fi
+
+# Use the umask/mode information to install the files
+# Create directories as needed
+#
+do_install_and_rm () {
+ install -m "${1}" "${2}" "${3}" &&
+ rm -f "${2}"
+}
+
+# 4095 = "obase=10;ibase=8;07777" | bc
+find_mode () {
+ local OCTAL
+ OCTAL=$(( ~$(echo "obase=10; ibase=8; ${CONFIRMED_UMASK}" | bc) & 4095 &
+ $(echo "obase=10; ibase=8; $(stat -f "%OMp%OLp" ${1})" | bc) ))
+ printf "%04o\n" ${OCTAL}
+}
+
+mm_install () {
+ local INSTALL_DIR
+ INSTALL_DIR=${1#.}
+ INSTALL_DIR=${INSTALL_DIR%/*}
+
+ case "${INSTALL_DIR}" in
+ '')
+ INSTALL_DIR=/
+ ;;
+ esac
+
+ if [ -n "${DESTDIR}${INSTALL_DIR}" -a ! -d "${DESTDIR}${INSTALL_DIR}" ]; then
+ DIR_MODE=`find_mode "${TEMPROOT}/${INSTALL_DIR}"`
+ install -d -o root -g wheel -m "${DIR_MODE}" "${DESTDIR}${INSTALL_DIR}"
+ fi
+
+ FILE_MODE=`find_mode "${1}"`
+
+ if [ ! -x "${1}" ]; then
+ case "${1#.}" in
+ /etc/mail/aliases)
+ NEED_NEWALIASES=yes
+ ;;
+ /etc/login.conf)
+ NEED_CAP_MKDB=yes
+ ;;
+ /etc/master.passwd)
+ do_install_and_rm 600 "${1}" "${DESTDIR}${INSTALL_DIR}"
+ NEED_PWD_MKDB=yes
+ DONT_INSTALL=yes
+ ;;
+ /.cshrc | /.profile)
+ case "${AUTO_INSTALL}" in
+ '')
+ case "${LINK_EXPLAINED}" in
+ '')
+ echo " *** Historically BSD derived systems have had a"
+ echo " hard link from /.cshrc and /.profile to"
+ echo " their namesakes in /root. Please indicate"
+ echo " your preference below for bringing your"
+ echo " installed files up to date."
+ echo ''
+ LINK_EXPLAINED=yes
+ ;;
+ esac
+
+ echo " Use 'd' to delete the temporary ${COMPFILE}"
+ echo " Use 'l' to delete the existing ${DESTDIR}${COMPFILE#.} and create the link"
+ echo ''
+ echo " Default is to leave the temporary file to deal with by hand"
+ echo ''
+ echo -n " How should I handle ${COMPFILE}? [Leave it to install later] "
+ read HANDLE_LINK
+ ;;
+ *) # Part of AUTO_INSTALL
+ HANDLE_LINK=l
+ ;;
+ esac
+
+ case "${HANDLE_LINK}" in
+ [dD]*)
+ rm "${COMPFILE}"
+ echo ''
+ echo " *** Deleting ${COMPFILE}"
+ ;;
+ [lL]*)
+ echo ''
+ rm -f "${DESTDIR}${COMPFILE#.}"
+ if ln "${DESTDIR}/root/${COMPFILE##*/}" "${DESTDIR}${COMPFILE#.}"; then
+ echo " *** Link from ${DESTDIR}${COMPFILE#.} to ${DESTDIR}/root/${COMPFILE##*/} installed successfully"
+ rm "${COMPFILE}"
+ else
+ echo " *** Error linking ${DESTDIR}${COMPFILE#.} to ${DESTDIR}/root/${COMPFILE##*/}, ${COMPFILE} will remain to install by hand"
+ fi
+ ;;
+ *)
+ echo " *** ${COMPFILE} will remain for your consideration"
+ ;;
+ esac
+ DONT_INSTALL=yes
+ ;;
+ esac
+
+ case "${DONT_INSTALL}" in
+ '')
+ do_install_and_rm "${FILE_MODE}" "${1}" "${DESTDIR}${INSTALL_DIR}"
+ ;;
+ *)
+ unset DONT_INSTALL
+ ;;
+ esac
+ else # File matched -x
+ case "${1#.}" in
+ /dev/MAKEDEV)
+ NEED_MAKEDEV=yes
+ ;;
+ esac
+ do_install_and_rm "${FILE_MODE}" "${1}" "${DESTDIR}${INSTALL_DIR}"
+ fi
+ return $?
+}
+
+echo ''
+echo "*** Beginning comparison"
+echo ''
+
+cd "${TEMPROOT}"
+
+if [ -r "${MM_PRE_COMPARE_SCRIPT}" ]; then
+ . "${MM_PRE_COMPARE_SCRIPT}"
+fi
+
+# Using -size +0 avoids uselessly checking the empty log files created
+# by ${SOURCEDIR}/Makefile and the device entries in ./dev, but does
+# check the scripts in ./dev, as we'd like (assuming no devfs of course).
+#
+for COMPFILE in `find . -type f -size +0`; do
+
+ # First, check to see if the file exists in DESTDIR. If not, the
+ # diff_loop function knows how to handle it.
+ #
+ if [ ! -e "${DESTDIR}${COMPFILE#.}" ]; then
+ case "${AUTO_RUN}" in
+ '')
+ diff_loop
+ ;;
+ *)
+ case "${AUTO_INSTALL}" in
+ '')
+ # If this is an auto run, make it official
+ echo " *** ${COMPFILE} will remain for your consideration"
+ ;;
+ *)
+ diff_loop
+ ;;
+ esac
+ ;;
+ esac # Auto run test
+ continue
+ fi
+
+ case "${STRICT}" in
+ '' | [Nn][Oo])
+ # Compare CVS $Id's first so if the file hasn't been modified
+ # local changes will be ignored.
+ # If the files have the same $Id, delete the one in temproot so the
+ # user will have less to wade through if files are left to merge by hand.
+ #
+ CVSID1=`grep "[$]${CVS_ID_TAG}:" ${DESTDIR}${COMPFILE#.} 2>/dev/null`
+ CVSID2=`grep "[$]${CVS_ID_TAG}:" ${COMPFILE} 2>/dev/null` || CVSID2=none
+
+ case "${CVSID2}" in
+ "${CVSID1}")
+ echo " *** Temp ${COMPFILE} and installed have the same CVS Id, deleting"
+ rm "${COMPFILE}"
+ ;;
+ esac
+ ;;
+ esac
+
+ # If the file is still here either because the $Ids are different, the
+ # file doesn't have an $Id, or we're using STRICT mode; look at the diff.
+ #
+ if [ -f "${COMPFILE}" ]; then
+
+ # Do an absolute diff first to see if the files are actually different.
+ # If they're not different, delete the one in temproot.
+ #
+ if diff -q "${DESTDIR}${COMPFILE#.}" "${COMPFILE}" > /dev/null 2>&1; then
+ echo " *** Temp ${COMPFILE} and installed are the same, deleting"
+ rm "${COMPFILE}"
+ else
+ # Ok, the files are different, so show the user where they differ.
+ # Use user's choice of diff methods; and user's pager if they have one.
+ # Use more if not.
+ # Use unified diffs by default. Context diffs give me a headache. :)
+ #
+ case "${AUTO_RUN}" in
+ '')
+ # prompt user to install/delete/merge changes
+ diff_loop
+ ;;
+ *)
+ # If this is an auto run, make it official
+ echo " *** ${COMPFILE} will remain for your consideration"
+ ;;
+ esac # Auto run test
+ fi # Yes, the files are different
+ fi # Yes, the file still remains to be checked
+done # This is for the do way up there at the beginning of the comparison
+
+echo ''
+echo "*** Comparison complete"
+echo ''
+
+TEST_FOR_FILES=`find ${TEMPROOT} -type f -size +0 2>/dev/null`
+if [ -n "${TEST_FOR_FILES}" ]; then
+ echo "*** Files that remain for you to merge by hand:"
+ find "${TEMPROOT}" -type f -size +0
+ echo ''
+fi
+
+case "${AUTO_RUN}" in
+'')
+ echo -n "Do you wish to delete what is left of ${TEMPROOT}? [no] "
+ read DEL_TEMPROOT
+
+ case "${DEL_TEMPROOT}" in
+ [yY]*)
+ if delete_temproot; then
+ echo " *** ${TEMPROOT} has been deleted"
+ else
+ echo " *** Unable to delete ${TEMPROOT}"
+ fi
+ ;;
+ *)
+ echo " *** ${TEMPROOT} will remain"
+ ;;
+ esac
+ ;;
+*) ;;
+esac
+
+case "${AUTO_INSTALLED_FILES}" in
+'') ;;
+*)
+ case "${AUTO_RUN}" in
+ '')
+ (
+ echo ''
+ echo '*** You chose the automatic install option for files that did not'
+ echo ' exist on your system. The following were installed for you:'
+ echo "${AUTO_INSTALLED_FILES}"
+ ) | ${PAGER}
+ ;;
+ *)
+ echo ''
+ echo '*** You chose the automatic install option for files that did not'
+ echo ' exist on your system. The following were installed for you:'
+ echo "${AUTO_INSTALLED_FILES}"
+ ;;
+ esac
+ ;;
+esac
+
+run_it_now () {
+ case "${AUTO_RUN}" in
+ '')
+ unset YES_OR_NO
+ echo ''
+ echo -n ' Would you like to run it now? y or n [n] '
+ read YES_OR_NO
+
+ case "${YES_OR_NO}" in
+ y)
+ echo " Running ${1}"
+ echo ''
+ eval "${1}"
+ ;;
+ ''|n)
+ echo ''
+ echo " *** Cancelled"
+ echo ''
+ echo " Make sure to run ${1} yourself"
+ ;;
+ *)
+ echo ''
+ echo " *** Sorry, I do not understand your answer (${YES_OR_NO})"
+ echo ''
+ echo " Make sure to run ${1} yourself"
+ esac
+ ;;
+ *) ;;
+ esac
+}
+
+case "${NEED_MAKEDEV}" in
+'') ;;
+*)
+ echo ''
+ echo "*** You installed a new ${DESTDIR}/dev/MAKEDEV script, so make sure that you run"
+ echo " 'cd ${DESTDIR}/dev && /bin/sh MAKEDEV all' to rebuild your devices"
+ run_it_now "cd ${DESTDIR}/dev && /bin/sh MAKEDEV all"
+ ;;
+esac
+
+case "${NEED_NEWALIASES}" in
+'') ;;
+*)
+ echo ''
+ if [ -n "${DESTDIR}" ]; then
+ echo "*** You installed a new aliases file into ${DESTDIR}/etc/mail, but"
+ echo " the newaliases command is limited to the directories configured"
+ echo " in sendmail.cf. Make sure to create your aliases database by"
+ echo " hand when your sendmail configuration is done."
+ else
+ echo "*** You installed a new aliases file, so make sure that you run"
+ echo " '/usr/bin/newaliases' to rebuild your aliases database"
+ run_it_now '/usr/bin/newaliases'
+ fi
+ ;;
+esac
+
+case "${NEED_CAP_MKDB}" in
+'') ;;
+*)
+ echo ''
+ echo "*** You installed a login.conf file, so make sure that you run"
+ echo " '/usr/bin/cap_mkdb ${DESTDIR}/etc/login.conf'"
+ echo " to rebuild your login.conf database"
+ run_it_now "/usr/bin/cap_mkdb ${DESTDIR}/etc/login.conf"
+ ;;
+esac
+
+case "${NEED_PWD_MKDB}" in
+'') ;;
+*)
+ echo ''
+ echo "*** You installed a new master.passwd file, so make sure that you run"
+ if [ -n "${DESTDIR}" ]; then
+ echo " '/usr/sbin/pwd_mkdb -d ${DESTDIR}/etc -p ${DESTDIR}/etc/master.passwd'"
+ echo " to rebuild your password files"
+ run_it_now "/usr/sbin/pwd_mkdb -d ${DESTDIR}/etc -p ${DESTDIR}/etc/master.passwd"
+ else
+ echo " '/usr/sbin/pwd_mkdb -p /etc/master.passwd'"
+ echo " to rebuild your password files"
+ run_it_now '/usr/sbin/pwd_mkdb -p /etc/master.passwd'
+ fi
+ ;;
+esac
+
+echo ''
+
+if [ -r "${MM_EXIT_SCRIPT}" ]; then
+ . "${MM_EXIT_SCRIPT}"
+fi
+
+case "${COMP_CONFS}" in
+'') ;;
+*)
+ . ${DESTDIR}/etc/defaults/rc.conf
+
+ (echo ''
+ echo "*** Comparing conf files: ${rc_conf_files}"
+
+ for CONF_FILE in ${rc_conf_files}; do
+ if [ -r "${DESTDIR}${CONF_FILE}" ]; then
+ echo ''
+ echo "*** From ${DESTDIR}${CONF_FILE}"
+ echo "*** From ${DESTDIR}/etc/defaults/rc.conf"
+
+ for RC_CONF_VAR in `grep -i ^[a-z] ${DESTDIR}${CONF_FILE} |
+ cut -d '=' -f 1`; do
+ echo ''
+ grep -w ^${RC_CONF_VAR} ${DESTDIR}${CONF_FILE}
+ grep -w ^${RC_CONF_VAR} ${DESTDIR}/etc/defaults/rc.conf ||
+ echo ' * No default variable with this name'
+ done
+ fi
+ done) | ${PAGER}
+ echo ''
+ ;;
+esac
+
+case "${PRE_WORLD}" in
+'') ;;
+*)
+ MAKE_CONF="${SOURCEDIR%etc}share/examples/etc/make.conf"
+
+ (echo ''
+ echo '*** Comparing make variables'
+ echo ''
+ echo "*** From ${DESTDIR}/etc/make.conf"
+ echo "*** From ${MAKE_CONF}"
+
+ for MAKE_VAR in `grep -i ^[a-z] /etc/make.conf | cut -d '=' -f 1`; do
+ echo ''
+ grep -w ^${MAKE_VAR} ${DESTDIR}/etc/make.conf
+ grep -w ^#${MAKE_VAR} ${MAKE_CONF} ||
+ echo ' * No example variable with this name'
+ done) | ${PAGER}
+ ;;
+esac
+
+exit 0
+
diff --git a/usr.sbin/mixer/Makefile b/usr.sbin/mixer/Makefile
new file mode 100644
index 0000000..e86fcee
--- /dev/null
+++ b/usr.sbin/mixer/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= mixer
+MAN= mixer.8
+
+WARNS?= 2
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mixer/mixer.8 b/usr.sbin/mixer/mixer.8
new file mode 100644
index 0000000..9c725a8
--- /dev/null
+++ b/usr.sbin/mixer/mixer.8
@@ -0,0 +1,151 @@
+.\" Copyright (c) 1997
+.\" Mike Pritchard <mpp@FreeBSD.ORG>. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the author nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY MIKE PRITCHARD AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd January 9, 1997
+.Dt MIXER 8
+.Os
+.Sh NAME
+.Nm mixer
+.Nd set/display soundcard mixer values
+.Sh SYNOPSIS
+.Nm
+.Op Fl f Ar device
+.Op Fl s
+.Oo
+.Oo Ar dev
+.Op Ar lvol Ns Op : Ns Ar rvol
+.Cm | recsrc |
+.Sm off
+.Eo \&{
+.Cm ^ | + | - | =
+.Sm on
+.Ec \&} Ns Cm rec
+.Ar 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 : Ns Ar rvol
+may be specified. The
+.Ar lvol
+and
+.Ar rvol
+arguments may be from 0 - 100. Omitting
+.Ar dev
+and including only the channel settings will change the main volume level.
+.Pp
+If the
+.Fl s
+flag is used, the current mixer values will be displayed in a format suitable
+for use as the command-line arguments to a future invocation of
+.Nm
+(as above).
+.Pp
+To change the recording device you use one of:
+.Bl -tag -width =rec -offset indent
+.It Cm ^rec
+toggles
+.Ar rdev
+of possible recording devices
+.It Cm +rec
+adds
+.Ar rdev
+to possible recording devices
+.It Cm -rec
+removes
+.Ar rdev
+from possible recording devices
+.It Cm =rec
+sets the recording device to
+.Ar rdev
+.El
+.Pp
+The above commands work on an internal mask. After all the options
+have been parsed, it will set then read the mask from the sound card.
+This will let you see EXACTLY what the soundcard is using for the
+recording device(s).
+.Pp
+The option recsrc will display the current recording devices.
+.Pp
+The option
+.Fl f
+.Ar device
+will open
+.Ar device
+as the mixer device.
+.Sh FILES
+.Bl -tag -width /dev/mixer -compact
+.It Pa /dev/mixer
+the default mixer device
+.El
+.Sh SEE ALSO
+.Xr cdcontrol 1
+.Sh HISTORY
+The
+.Nm
+command first appeared in
+.Fx 2.0.5 .
+.Sh AUTHORS
+.An -nosplit
+Original source by
+.An Craig Metz Aq cmetz@thor.tjhsst.edu
+and
+.An Hannu Savolainen .
+Mostly rewritten by
+.An John-Mark Gurney Aq jmg@FreeBSD.org .
+This
+manual page was written by
+.An Mike Pritchard Aq mpp@FreeBSD.org .
diff --git a/usr.sbin/mixer/mixer.c b/usr.sbin/mixer/mixer.c
new file mode 100644
index 0000000..68cb40e
--- /dev/null
+++ b/usr.sbin/mixer/mixer.c
@@ -0,0 +1,251 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/soundcard.h>
+
+const char *names[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
+
+void usage(int devmask, int recmask);
+int res_name(const char *name, int mask);
+void print_recsrc(int recsrc);
+
+void
+usage(int devmask, int recmask)
+{
+ int i, n;
+
+ printf("usage: mixer [-f device] [-s] [[dev [voll[:volr]] | recsrc | {^|+|-|=}rec recdev] ... ]\n");
+ printf(" devices: ");
+ for (i = 0, n = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ if ((1 << i) & devmask) {
+ if (n)
+ printf(", ");
+ printf("%s", names[i]);
+ n = 1;
+ }
+ printf("\n rec devices: ");
+ for (i = 0, n = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ if ((1 << i) & recmask) {
+ if (n)
+ printf(", ");
+ printf("%s", names[i]);
+ n = 1;
+ }
+ printf("\n");
+ exit(1);
+}
+
+int
+res_name(const char *name, int mask)
+{
+ int foo;
+
+ for (foo = 0; foo < SOUND_MIXER_NRDEVICES; foo++)
+ if ((1 << foo) & mask && !strcmp(names[foo], name))
+ break;
+
+ return foo == SOUND_MIXER_NRDEVICES ? -1 : foo;
+}
+
+void
+print_recsrc(int recsrc)
+{
+ int i, n = 0;
+ fprintf(stderr, "Recording source: ");
+
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ if ((1 << i) & recsrc) {
+ if (n)
+ fprintf(stderr, ", ");
+ fprintf(stderr, "%s", names[i]);
+ n = 1;
+ }
+ fprintf(stderr, "\n");
+}
+
+int
+main(int argc, char *argv[])
+{
+ int foo, bar, baz, dev;
+ int devmask = 0, recmask = 0, recsrc = 0, orecsrc;
+ int dusage = 0, drecsrc = 0, shortflag = 0;
+ int l = 0, r = 0, t = 0;
+ char ch;
+
+ char *name;
+
+ name = strdup("/dev/mixer");
+
+ if (!strcmp(argv[0], "mixer2"))
+ name = strdup("/dev/mixer1");
+ else if (!strcmp(argv[0], "mixer3"))
+ name = strdup("/dev/mixer2");
+
+ while ((ch = getopt(argc, argv, "f:s")) != -1)
+ switch (ch) {
+ case 'f':
+ name = strdup(optarg);
+ break;
+ case 's':
+ shortflag = 1;
+ break;
+ default:
+ dusage = 1;
+ }
+ argc -= (optind - 1);
+ argv += (optind - 1);
+
+ if ((baz = open(name, O_RDWR)) < 0)
+ err(1, "%s", name);
+ free(name);
+ if (ioctl(baz, SOUND_MIXER_READ_DEVMASK, &devmask) == -1)
+ err(1, "SOUND_MIXER_READ_DEVMASK");
+ if (ioctl(baz, SOUND_MIXER_READ_RECMASK, &recmask) == -1)
+ err(1, "SOUND_MIXER_READ_RECMASK");
+ if (ioctl(baz, SOUND_MIXER_READ_RECSRC, &recsrc) == -1)
+ err(1, "SOUND_MIXER_READ_RECSRC");
+ orecsrc = recsrc;
+
+ if ((argc == 1) && (dusage == 0)) {
+ for (foo = 0; foo < SOUND_MIXER_NRDEVICES; foo++) {
+ if (!((1 << foo) & devmask))
+ continue;
+ if (ioctl(baz, MIXER_READ(foo),&bar)== -1) {
+ warn("MIXER_READ");
+ continue;
+ }
+ if (shortflag)
+ printf("%s %d:%d ", names[foo], bar & 0x7f, (bar >> 8) & 0x7f);
+ else
+ printf("Mixer %-8s is currently set to %3d:%d\n", names[foo], bar & 0x7f, (bar >> 8) & 0x7f);
+ }
+ return(0);
+ }
+
+ argc--; argv++;
+
+ while ((argc > 0) && (dusage == 0)) {
+ if (!strcmp("recsrc", *argv)) {
+ drecsrc = 1;
+ argc--; argv++;
+ continue;
+ } else if (argc > 1 && !strcmp("rec", *argv + 1)) {
+ if (**argv != '+' && **argv != '-' &&
+ **argv != '=' && **argv != '^') {
+ warnx("unknown modifier: %c", **argv);
+ dusage = 1;
+ break;
+ }
+ if ((dev = res_name(argv[1], recmask)) == -1) {
+ warnx("unknown recording device: %s", argv[1]);
+ dusage = 1;
+ break;
+ }
+ switch(**argv) {
+ case '+':
+ recsrc |= (1 << dev);
+ break;
+ case '-':
+ recsrc &= ~(1 << dev);
+ break;
+ case '=':
+ recsrc = (1 << dev);
+ break;
+ case '^':
+ recsrc ^= (1 << dev);
+ break;
+ }
+ drecsrc = 1;
+ argc -= 2; argv += 2;
+ continue;
+ }
+
+ if ((t = sscanf(*argv, "%d:%d", &l, &r)) > 0) {
+ dev = 0;
+ }
+ else if((dev = res_name(*argv, devmask)) == -1) {
+ warnx("unknown device: %s", *argv);
+ dusage = 1;
+ break;
+ }
+
+ switch(argc > 1 ? sscanf(argv[1], "%d:%d", &l, &r) : t) {
+ case 0:
+ if (ioctl(baz, MIXER_READ(dev),&bar)== -1) {
+ warn("MIXER_READ");
+ argc--; argv++;
+ continue;
+ }
+ if (shortflag)
+ printf("%s %d:%d ", names[dev], bar & 0x7f, (bar >> 8) & 0x7f);
+ else
+ printf("Mixer %-8s is currently set to %3d:%d\n",
+ names[dev], bar & 0x7f, (bar >> 8) & 0x7f);
+
+ argc--; argv++;
+ break;
+ case 1:
+ r = l;
+ case 2:
+ if (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 (dusage) {
+ close(baz);
+ usage(devmask, recmask);
+ /* Not reached */
+ }
+
+ if (orecsrc != recsrc)
+ if (ioctl(baz, SOUND_MIXER_WRITE_RECSRC, &recsrc) == -1)
+ err(1, "SOUND_MIXER_WRITE_RECSRC");
+
+ if (drecsrc) {
+ if (ioctl(baz, SOUND_MIXER_READ_RECSRC, &recsrc) == -1)
+ err(1, "SOUND_MIXER_READ_RECSRC");
+ print_recsrc(recsrc);
+ }
+
+ close(baz);
+
+ exit(0);
+}
diff --git a/usr.sbin/mld6query/Makefile b/usr.sbin/mld6query/Makefile
new file mode 100644
index 0000000..66f2a5e
--- /dev/null
+++ b/usr.sbin/mld6query/Makefile
@@ -0,0 +1,23 @@
+# Copyright (c) 1996 WIDE Project. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modifications, are permitted provided that the above copyright notice
+# and this paragraph are duplicated in all such forms and that any
+# documentation, advertising materials, and other materials related to
+# such distribution and use acknowledge that the software was developed
+# by the WIDE Project, Japan. The name of the Project may not be used to
+# endorse or promote products derived from this software without
+# specific prior written permission. THIS SOFTWARE IS PROVIDED ``AS IS''
+# AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+# LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE.
+#
+# $FreeBSD$
+
+PROG= mld6query
+MAN= mld6query.8
+SRCS= mld6.c
+
+CFLAGS+= -DINET6 -DIPSEC
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mld6query/mld6.c b/usr.sbin/mld6query/mld6.c
new file mode 100644
index 0000000..9b0b30a
--- /dev/null
+++ b/usr.sbin/mld6query/mld6.c
@@ -0,0 +1,270 @@
+/* $KAME: mld6.c,v 1.11 2001/05/13 15:45:07 suz Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (C) 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <sys/param.h>
+#include <sys/uio.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <signal.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+
+#include <netinet/in.h>
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+
+#include <arpa/inet.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <err.h>
+
+struct msghdr m;
+struct sockaddr_in6 dst;
+struct mld6_hdr mldh;
+struct in6_addr maddr = IN6ADDR_ANY_INIT, any = IN6ADDR_ANY_INIT;
+struct ipv6_mreq mreq;
+u_short ifindex;
+int s;
+
+#define QUERY_RESPONSE_INTERVAL 10000
+
+void make_msg(int index, struct in6_addr *addr, u_int type);
+void usage(void);
+void dump(int);
+void quit(int);
+
+int
+main(int argc, char *argv[])
+{
+ int i;
+ struct icmp6_filter filt;
+ u_int hlim = 1;
+ fd_set fdset;
+ struct itimerval itimer;
+ u_int type;
+ int ch;
+
+ type = MLD6_LISTENER_QUERY;
+ while ((ch = getopt(argc, argv, "dr")) != -1) {
+ switch (ch) {
+ case 'd':
+ type = MLD6_LISTENER_DONE;
+ break;
+ case 'r':
+ type = MLD6_LISTENER_REPORT;
+ break;
+ default:
+ usage();
+ /*NOTREACHED*/
+ }
+ }
+
+ argv += optind;
+ argc -= optind;
+
+ if (argc != 1 && argc != 2)
+ usage();
+
+ ifindex = (u_short)if_nametoindex(argv[0]);
+ if (ifindex == 0)
+ usage();
+ if (argc == 2 && inet_pton(AF_INET6, argv[1], &maddr) != 1)
+ usage();
+
+ if ((s = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0)
+ err(1, "socket");
+
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hlim,
+ sizeof(hlim)) == -1)
+ err(1, "setsockopt(IPV6_MULTICAST_HOPS)");
+
+ mreq.ipv6mr_multiaddr = any;
+ mreq.ipv6mr_interface = ifindex;
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq,
+ sizeof(mreq)) == -1)
+ err(1, "setsockopt(IPV6_JOIN_GROUP)");
+
+ ICMP6_FILTER_SETBLOCKALL(&filt);
+ ICMP6_FILTER_SETPASS(ICMP6_MEMBERSHIP_QUERY, &filt);
+ ICMP6_FILTER_SETPASS(ICMP6_MEMBERSHIP_REPORT, &filt);
+ ICMP6_FILTER_SETPASS(ICMP6_MEMBERSHIP_REDUCTION, &filt);
+ if (setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
+ sizeof(filt)) < 0)
+ err(1, "setsockopt(ICMP6_FILTER)");
+
+ make_msg(ifindex, &maddr, type);
+
+ if (sendmsg(s, &m, 0) < 0)
+ err(1, "sendmsg");
+
+ itimer.it_value.tv_sec = QUERY_RESPONSE_INTERVAL / 1000;
+ itimer.it_interval.tv_sec = 0;
+ itimer.it_interval.tv_usec = 0;
+ itimer.it_value.tv_usec = 0;
+
+ (void)signal(SIGALRM, quit);
+ (void)setitimer(ITIMER_REAL, &itimer, NULL);
+
+ FD_ZERO(&fdset);
+ for (;;) {
+ FD_SET(s, &fdset);
+ if ((i = select(s + 1, &fdset, NULL, NULL, NULL)) < 0)
+ perror("select");
+ if (i == 0)
+ continue;
+ else
+ dump(s);
+ }
+}
+
+void
+make_msg(int index, struct in6_addr *addr, u_int type)
+{
+ static struct iovec iov[2];
+ static u_char *cmsgbuf;
+ int cmsglen, hbhlen = 0;
+ u_int8_t raopt[IP6OPT_RTALERT_LEN];
+ struct in6_pktinfo *pi;
+ struct cmsghdr *cmsgp;
+ u_short rtalert_code = htons(IP6OPT_RTALERT_MLD);
+
+ dst.sin6_len = sizeof(dst);
+ dst.sin6_family = AF_INET6;
+ if (IN6_IS_ADDR_UNSPECIFIED(addr)) {
+ if (inet_pton(AF_INET6, "ff02::1", &dst.sin6_addr) != 1)
+ errx(1, "inet_pton failed");
+ }
+ else
+ dst.sin6_addr = *addr;
+ m.msg_name = (caddr_t)&dst;
+ m.msg_namelen = dst.sin6_len;
+ iov[0].iov_base = (caddr_t)&mldh;
+ iov[0].iov_len = sizeof(mldh);
+ m.msg_iov = iov;
+ m.msg_iovlen = 1;
+
+ bzero(&mldh, sizeof(mldh));
+ mldh.mld6_type = type & 0xff;
+ mldh.mld6_maxdelay = htons(QUERY_RESPONSE_INTERVAL);
+ mldh.mld6_addr = *addr;
+
+ hbhlen = sizeof(raopt);
+ cmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) +
+ inet6_option_space(hbhlen);
+
+ if ((cmsgbuf = malloc(cmsglen)) == NULL)
+ errx(1, "can't allocate enough memory for cmsg");
+ cmsgp = (struct cmsghdr *)cmsgbuf;
+ m.msg_control = (caddr_t)cmsgbuf;
+ m.msg_controllen = cmsglen;
+ /* specify the outgoing interface */
+ cmsgp->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
+ cmsgp->cmsg_level = IPPROTO_IPV6;
+ cmsgp->cmsg_type = IPV6_PKTINFO;
+ pi = (struct in6_pktinfo *)CMSG_DATA(cmsgp);
+ pi->ipi6_ifindex = index;
+ memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr));
+ /* specifiy to insert router alert option in a hop-by-hop opt hdr. */
+ cmsgp = CMSG_NXTHDR(&m, cmsgp);
+ if (inet6_option_init((void *)cmsgp, &cmsgp, IPV6_HOPOPTS))
+ errx(1, "inet6_option_init failed\n");
+ raopt[0] = IP6OPT_RTALERT;
+ raopt[1] = IP6OPT_RTALERT_LEN - 2;
+ memcpy(&raopt[2], (caddr_t)&rtalert_code, sizeof(u_short));
+ if (inet6_option_append(cmsgp, raopt, 4, 0))
+ errx(1, "inet6_option_append failed\n");
+}
+
+void
+dump(int s)
+{
+ int i;
+ struct mld6_hdr *mld;
+ u_char buf[1024];
+ struct sockaddr_in6 from;
+ int from_len = sizeof(from);
+ char ntop_buf[256];
+
+ if ((i = recvfrom(s, buf, sizeof(buf), 0,
+ (struct sockaddr *)&from,
+ &from_len)) < 0)
+ return;
+
+ if (i < sizeof(struct mld6_hdr)) {
+ printf("too short!\n");
+ return;
+ }
+
+ mld = (struct mld6_hdr *)buf;
+
+ printf("from %s, ", inet_ntop(AF_INET6, &from.sin6_addr,
+ ntop_buf, sizeof(ntop_buf)));
+
+ switch (mld->mld6_type) {
+ case ICMP6_MEMBERSHIP_QUERY:
+ printf("type=Multicast Listener Query, ");
+ break;
+ case ICMP6_MEMBERSHIP_REPORT:
+ printf("type=Multicast Listener Report, ");
+ break;
+ case ICMP6_MEMBERSHIP_REDUCTION:
+ printf("type=Multicast Listener Done, ");
+ break;
+ }
+ printf("addr=%s\n", inet_ntop(AF_INET6, &mld->mld6_addr,
+ ntop_buf, sizeof(ntop_buf)));
+
+ fflush(stdout);
+}
+
+/* ARGSUSED */
+void
+quit(int signum) {
+ mreq.ipv6mr_multiaddr = any;
+ mreq.ipv6mr_interface = ifindex;
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &mreq,
+ sizeof(mreq)) == -1)
+ err(1, "setsockopt(IPV6_LEAVE_GROUP)");
+
+ exit(0);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "usage: mld6query ifname [addr]\n");
+ exit(1);
+}
diff --git a/usr.sbin/mld6query/mld6query.8 b/usr.sbin/mld6query/mld6query.8
new file mode 100644
index 0000000..8b2f7c0
--- /dev/null
+++ b/usr.sbin/mld6query/mld6query.8
@@ -0,0 +1,89 @@
+.\" $KAME: mld6query.8,v 1.5 2000/12/04 06:28:23 itojun Exp $
+.\"
+.\" Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the project nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd May 17, 1998
+.Dt MLD6QUERY 8
+.Os
+.\"
+.Sh NAME
+.Nm mld6query
+.Nd send multicast listener query
+.\"
+.Sh SYNOPSIS
+.Nm
+.Op Fl dr
+.Ar intface
+.Op Ar maddr
+.\"
+.Sh DESCRIPTION
+.Nm
+sends an IPv6 multicast listener discovery (MLD) query packet toward
+the specified multicast address,
+.Ar maddr ,
+toward interface
+.Ar intface .
+If you omit
+.Ar maddr ,
+linklocal all nodes multicast address(ff02::1) is used.
+.Pp
+After sending a query,
+.Nm
+waits for replies for at most 10 seconds.
+If a reply is returned,
+.Nm
+prints it with its type and then waits for another reply.
+.Pp
+This program is provided only for debugging.
+It is not necessary for normal use.
+.Pp
+With
+.Fl d ,
+.Nm
+will transmit MLD done packet instead of MLD query packet.
+With
+.Fl r ,
+similarly, MLD report packet will be transmitted.
+.Fl dr
+options are for debugging purposes only.
+.\"
+.Sh RETURN VALUES
+The program exits with 0 on success, non-zero on failures.
+.\"
+.\" .Sh SEE ALSO
+.\"
+.Sh HISTORY
+The
+.Nm
+command first appeared in WIDE/KAME IPv6 protocol stack kit.
+.Sh BUGS
+.Nm Mld6query
+does not take care of multicast addresses which have non link-local
+scope.
diff --git a/usr.sbin/mlxcontrol/Makefile b/usr.sbin/mlxcontrol/Makefile
new file mode 100644
index 0000000..95964a5
--- /dev/null
+++ b/usr.sbin/mlxcontrol/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PROG= mlxcontrol
+MAN= mlxcontrol.8
+SRCS= command.c config.c interface.c util.c
+
+CFLAGS+= -I${.CURDIR}/../../sys
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mlxcontrol/command.c b/usr.sbin/mlxcontrol/command.c
new file mode 100644
index 0000000..a364767
--- /dev/null
+++ b/usr.sbin/mlxcontrol/command.c
@@ -0,0 +1,712 @@
+/*-
+ * Copyright (c) 1999 Michael Smith
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <err.h>
+
+#include <dev/mlx/mlxio.h>
+#include <dev/mlx/mlxreg.h>
+
+#include "mlxcontrol.h"
+
+static int cmd_status(int argc, char *argv[]);
+static int cmd_rescan(int argc, char *argv[]);
+static int cmd_detach(int argc, char *argv[]);
+static int cmd_check(int argc, char *argv[]);
+static int cmd_rebuild(int argc, char *argv[]);
+#ifdef SUPPORT_PAUSE
+static int cmd_pause(int argc, char *argv[]);
+#endif
+static int cmd_help(int argc, char *argv[]);
+
+extern int cmd_config(int argc, char *argv[]);
+
+
+struct
+{
+ char *cmd;
+ int (*func)(int argc, char *argv[]);
+ char *desc;
+ char *text;
+} commands[] = {
+ {"status", cmd_status,
+ "displays device status",
+ " status [-qv] [<drive>...]\n"
+ " Display status for <drive> or all drives if none is listed\n"
+ " -q Suppress output.\n"
+ " -v Display verbose information.\n"
+ " Returns 0 if all drives tested are online, 1 if one or more are\n"
+ " critical, and 2 if one or more are offline."},
+ {"rescan", cmd_rescan,
+ "scan for new system drives",
+ " rescan <controller> [<controller>...]\n"
+ " Rescan <controller> for system drives.\n"
+ " rescan -a\n"
+ " Rescan all controllers for system drives."},
+ {"detach", cmd_detach,
+ "detach system drives",
+ " detach <drive> [<drive>...]\n"
+ " Detaches <drive> from the controller.\n"
+ " detach -a <controller>\n"
+ " Detaches all drives on <controller>."},
+ {"check", cmd_check,
+ "consistency-check a system drive",
+ " check <drive>\n"
+ " Requests a check and rebuild of the parity information on <drive>.\n"
+ " Note that each controller can only check one system drive at a time."},
+ {"rebuild", cmd_rebuild,
+ "initiate a rebuild of a dead physical drive",
+ " rebuild <controller> <physdrive>\n"
+ " All system drives using space on the physical drive <physdrive>\n"
+ " are rebuilt, reconstructing all data on the drive.\n"
+ " Note that each controller can only perform one rebuild at a time."},
+#ifdef SUPPORT_PAUSE
+ {"pause", cmd_pause,
+ "pauses controller channels",
+ " pause [-t <howlong>] [-d <delay>] <controller> [<channel>...]\n"
+ " Pauses SCSI I/O on <channel> and <controller>. If no channel is specified,\n"
+ " all channels are paused.\n"
+ " <howlong> How long (seconds) to pause for (default 30).\n"
+ " <delay> How long (seconds) to wait before pausing (default 30).\n"
+ " pause <controller> -c\n"
+ " Cancels any pending pause operation on <controller>."},
+#endif
+ {"config", cmd_config,
+ "examine and update controller configuration",
+ " config <controller>\n"
+ " Print configuration for <controller>."},
+ {"help", cmd_help,
+ "give help on usage",
+ ""},
+ {NULL, NULL, NULL, NULL}
+};
+
+/********************************************************************************
+ * Command dispatch and global options parsing.
+ */
+
+int
+main(int argc, char *argv[])
+{
+ int ch, i, oargc;
+ char **oargv;
+
+ oargc = argc;
+ oargv = argv;
+ while ((ch = getopt(argc, argv, "")) != -1)
+ switch(ch) {
+ default:
+ return(cmd_help(0, NULL));
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc > 0)
+ for (i = 0; commands[i].cmd != NULL; i++)
+ if (!strcmp(argv[0], commands[i].cmd))
+ return(commands[i].func(argc, argv));
+
+ return(cmd_help(oargc, oargv));
+}
+
+/********************************************************************************
+ * Helptext output
+ */
+static int
+cmd_help(int argc, char *argv[])
+{
+ int i;
+
+ if (argc > 1)
+ for (i = 0; commands[i].cmd != NULL; i++)
+ if (!strcmp(argv[1], commands[i].cmd)) {
+ fprintf(stderr, "%s\n", commands[i].text);
+ fflush(stderr);
+ return(0);
+ }
+
+ if (argv != NULL)
+ fprintf(stderr, "Unknown command '%s'.\n", argv[1]);
+ fprintf(stderr, "Valid commands are:\n");
+ for (i = 0; commands[i].cmd != NULL; i++)
+ fprintf(stderr, " %-20s %s\n", commands[i].cmd, commands[i].desc);
+ fflush(stderr);
+ return(0);
+}
+
+/********************************************************************************
+ * Status output
+ *
+ * status [-qv] [<device> ...]
+ * Prints status for <device>, or all if none listed.
+ *
+ * -q Suppresses output, command returns 0 if devices are OK, 1 if one or
+ * more devices are critical, 2 if one or more devices are offline.
+ */
+static struct mlx_rebuild_status rs;
+static int rs_ctrlr = -1;
+static int status_result = 0;
+
+/* XXX more verbosity! */
+static void
+status_print(int unit, void *arg)
+{
+ int verbosity = *(int *)arg;
+ int fd, result, ctrlr, sysdrive, statvalid;
+
+ /* Find which controller and what system drive we are */
+ statvalid = 0;
+ if (mlxd_find_ctrlr(unit, &ctrlr, &sysdrive)) {
+ warnx("couldn't get controller/drive for %s", drivepath(unit));
+ } else {
+ /* If we don't have rebuild stats for this controller, get them */
+ if (rs_ctrlr == ctrlr) {
+ statvalid = 1;
+ } else {
+ if ((fd = open(ctrlrpath(ctrlr), 0)) < 0) {
+ warn("can't open %s", ctrlrpath(ctrlr));
+ } else {
+ if (ioctl(fd, MLX_REBUILDSTAT, &rs) < 0) {
+ warn("ioctl MLX_REBUILDSTAT");
+ } else {
+ rs_ctrlr = ctrlr;
+ statvalid = 1;
+ }
+ close(fd);
+ }
+ }
+ }
+
+ /* Get the device */
+ if ((fd = open(drivepath(unit), 0)) < 0) {
+ warn("can't open %s", drivepath(unit));
+ return;
+ }
+
+ /* Get its status */
+ if (ioctl(fd, MLXD_STATUS, &result) < 0) {
+ warn("ioctl MLXD_STATUS");
+ } else {
+ switch(result) {
+ case MLX_SYSD_ONLINE:
+ if (verbosity > 0)
+ printf("%s: online", drivename(unit));
+ break;
+ case MLX_SYSD_CRITICAL:
+ if (verbosity > 0)
+ printf("%s: critical", drivename(unit));
+ if (status_result < 1)
+ status_result = 1;
+ break;
+ case MLX_SYSD_OFFLINE:
+ if (verbosity > 0)
+ printf("%s: offline", drivename(unit));
+ if (status_result < 2)
+ status_result = 2;
+ break;
+ default:
+ if (verbosity > 0) {
+ printf("%s: unknown status 0x%x", drivename(unit), result);
+ }
+ }
+ if (verbosity > 0) {
+ /* rebuild/check in progress on this drive? */
+ if (statvalid && (rs_ctrlr == ctrlr) &&
+ (rs.rs_drive == sysdrive) && (rs.rs_code != MLX_REBUILDSTAT_IDLE)) {
+ switch(rs.rs_code) {
+ case MLX_REBUILDSTAT_REBUILDCHECK:
+ printf(" [consistency check");
+ break;
+ case MLX_REBUILDSTAT_ADDCAPACITY:
+ printf(" [add capacity");
+ break;
+ case MLX_REBUILDSTAT_ADDCAPACITYINIT:
+ printf(" [add capacity init");
+ break;
+ default:
+ printf(" [unknown operation");
+ }
+ printf(": %d/%d, %d%% complete]",
+ rs.rs_remaining, rs.rs_size,
+ ((rs.rs_size - rs.rs_remaining) / (rs.rs_size / 100)));
+ }
+ printf("\n");
+ }
+ }
+ close(fd);
+}
+
+static struct
+{
+ int hwid;
+ char *name;
+} mlx_controller_names[] = {
+ {0x01, "960P/PD"},
+ {0x02, "960PL"},
+ {0x10, "960PG"},
+ {0x11, "960PJ"},
+ {0x12, "960PR"},
+ {0x13, "960PT"},
+ {0x14, "960PTL0"},
+ {0x15, "960PRL"},
+ {0x16, "960PTL1"},
+ {0x20, "1100PVX"},
+ {-1, NULL}
+};
+
+static void
+controller_print(int unit, void *arg)
+{
+ struct mlx_enquiry2 enq;
+ struct mlx_phys_drv pd;
+ int verbosity = *(int *)arg;
+ static char buf[80];
+ char *model;
+ int i, channel, target;
+
+ if (verbosity == 0)
+ return;
+
+ /* fetch and print controller data */
+ if (mlx_enquiry(unit, &enq)) {
+ printf("mlx%d: error submitting ENQUIRY2\n", unit);
+ } else {
+
+ for (i = 0, model = NULL; mlx_controller_names[i].name != NULL; i++) {
+ if ((enq.me_hardware_id & 0xff) == mlx_controller_names[i].hwid) {
+ model = mlx_controller_names[i].name;
+ break;
+ }
+ }
+ if (model == NULL) {
+ sprintf(buf, " model 0x%x", enq.me_hardware_id & 0xff);
+ model = buf;
+ }
+
+ printf("mlx%d: DAC%s, %d channel%s, firmware %d.%02d-%c-%02d, %dMB RAM\n",
+ unit, model,
+ enq.me_actual_channels,
+ enq.me_actual_channels > 1 ? "s" : "",
+ enq.me_firmware_id & 0xff,
+ (enq.me_firmware_id >> 8) & 0xff,
+ (enq.me_firmware_id >> 16),
+ (enq.me_firmware_id >> 24) & 0xff,
+ enq.me_mem_size / (1024 * 1024));
+
+ if (verbosity > 1) {
+ printf(" Hardware ID 0x%08x\n", enq.me_hardware_id);
+ printf(" Firmware ID 0x%08x\n", enq.me_firmware_id);
+ printf(" Configured/Actual channels %d/%d\n", enq.me_configured_channels,
+ enq.me_actual_channels);
+ printf(" Max Targets %d\n", enq.me_max_targets);
+ printf(" Max Tags %d\n", enq.me_max_tags);
+ printf(" Max System Drives %d\n", enq.me_max_sys_drives);
+ printf(" Max Arms %d\n", enq.me_max_arms);
+ printf(" Max Spans %d\n", enq.me_max_spans);
+ printf(" DRAM/cache/flash/NVRAM size %d/%d/%d/%d\n", enq.me_mem_size,
+ enq.me_cache_size, enq.me_flash_size, enq.me_nvram_size);
+ printf(" DRAM type %d\n", enq.me_mem_type);
+ printf(" Clock Speed %dns\n", enq.me_clock_speed);
+ printf(" Hardware Speed %dns\n", enq.me_hardware_speed);
+ printf(" Max Commands %d\n", enq.me_max_commands);
+ printf(" Max SG Entries %d\n", enq.me_max_sg);
+ printf(" Max DP %d\n", enq.me_max_dp);
+ printf(" Max IOD %d\n", enq.me_max_iod);
+ printf(" Max Comb %d\n", enq.me_max_comb);
+ printf(" Latency %ds\n", enq.me_latency);
+ printf(" SCSI Timeout %ds\n", enq.me_scsi_timeout);
+ printf(" Min Free Lines %d\n", enq.me_min_freelines);
+ printf(" Rate Constant %d\n", enq.me_rate_const);
+ printf(" MAXBLK %d\n", enq.me_maxblk);
+ printf(" Blocking Factor %d sectors\n", enq.me_blocking_factor);
+ printf(" Cache Line Size %d blocks\n", enq.me_cacheline);
+ printf(" SCSI Capability %s%dMHz, %d bit\n",
+ enq.me_scsi_cap & (1<<4) ? "differential " : "",
+ (1 << ((enq.me_scsi_cap >> 2) & 3)) * 10,
+ 8 << (enq.me_scsi_cap & 0x3));
+ printf(" Firmware Build Number %d\n", enq.me_firmware_build);
+ printf(" Fault Management Type %d\n", enq.me_fault_mgmt_type);
+#if 0
+ printf(" Features %b\n", enq.me_firmware_features,
+ "\20\4Background Init\3Read Ahead\2MORE\1Cluster\n");
+#endif
+ }
+
+ /* fetch and print physical drive data */
+ for (channel = 0; channel < enq.me_configured_channels; channel++) {
+ for (target = 0; target < enq.me_max_targets; target++) {
+ if ((mlx_get_device_state(unit, channel, target, &pd) == 0) &&
+ (pd.pd_flags1 & MLX_PHYS_DRV_PRESENT)) {
+ mlx_print_phys_drv(&pd, channel, target, " ", verbosity - 1);
+ if (verbosity > 1) {
+ /* XXX print device statistics? */
+ }
+ }
+ }
+ }
+ }
+}
+
+static int
+cmd_status(int argc, char *argv[])
+{
+ int ch, verbosity = 1, i, unit;
+
+ optreset = 1;
+ optind = 1;
+ while ((ch = getopt(argc, argv, "qv")) != -1)
+ switch(ch) {
+ case 'q':
+ verbosity = 0;
+ break;
+ case 'v':
+ verbosity = 2;
+ break;
+ default:
+ return(cmd_help(argc, argv));
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ mlx_foreach(controller_print, &verbosity);
+ mlxd_foreach(status_print, &verbosity);
+ } else {
+ for (i = 0; i < argc; i++) {
+ if ((unit = driveunit(argv[i])) == -1) {
+ warnx("'%s' is not a valid drive", argv[i]);
+ } else {
+ status_print(unit, &verbosity);
+ }
+ }
+ }
+ return(status_result);
+}
+
+/********************************************************************************
+ * Recscan for system drives on one or more controllers.
+ *
+ * rescan <controller> [<controller>...]
+ * rescan -a
+ */
+static void
+rescan_ctrlr(int unit, void *junk)
+{
+ int fd;
+
+ /* Get the device */
+ if ((fd = open(ctrlrpath(unit), 0)) < 0) {
+ warn("can't open %s", ctrlrpath(unit));
+ return;
+ }
+
+ if (ioctl(fd, MLX_RESCAN_DRIVES) < 0)
+ warn("can't rescan %s", ctrlrname(unit));
+ close(fd);
+}
+
+static int
+cmd_rescan(int argc, char *argv[])
+{
+ int all = 0, i, ch, unit;
+
+ optreset = 1;
+ optind = 1;
+ while ((ch = getopt(argc, argv, "a")) != -1)
+ switch(ch) {
+ case 'a':
+ all = 1;
+ break;
+ default:
+ return(cmd_help(argc, argv));
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (all) {
+ mlx_foreach(rescan_ctrlr, NULL);
+ } else {
+ for (i = 0; i < argc; i++) {
+ if ((unit = ctrlrunit(argv[i])) == -1) {
+ warnx("'%s' is not a valid controller", argv[i]);
+ } else {
+ rescan_ctrlr(unit, NULL);
+ }
+ }
+ }
+ return(0);
+}
+
+/********************************************************************************
+ * Detach one or more system drives from a controller.
+ *
+ * detach <drive> [<drive>...]
+ * Detach <drive>.
+ *
+ * detach -a <controller> [<controller>...]
+ * Detach all drives on <controller>.
+ *
+ */
+static void
+detach_drive(int unit, void *arg)
+{
+ int fd;
+
+ /* Get the device */
+ if ((fd = open(ctrlrpath(unit), 0)) < 0) {
+ warn("can't open %s", ctrlrpath(unit));
+ return;
+ }
+
+ if (ioctl(fd, MLX_DETACH_DRIVE, &unit) < 0)
+ warn("can't detach %s", drivename(unit));
+ close(fd);
+}
+
+static int
+cmd_detach(int argc, char *argv[])
+{
+ struct mlxd_foreach_action ma;
+ int all = 0, i, ch, unit;
+
+ optreset = 1;
+ optind = 1;
+ while ((ch = getopt(argc, argv, "a")) != -1)
+ switch(ch) {
+ case 'a':
+ all = 1;
+ break;
+ default:
+ return(cmd_help(argc, argv));
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (all) {
+ ma.func = detach_drive;
+ ma.arg = &unit;
+ for (i = 0; i < argc; i++) {
+ if ((unit = ctrlrunit(argv[i])) == -1) {
+ warnx("'%s' is not a valid controller", argv[i]);
+ } else {
+ mlxd_foreach_ctrlr(unit, &ma);
+ }
+ }
+ } else {
+ for (i = 0; i < argc; i++) {
+ if ((unit = driveunit(argv[i])) == -1) {
+ warnx("'%s' is not a valid drive", argv[i]);
+ } else {
+ /* run across all controllers to find this drive */
+ mlx_foreach(detach_drive, &unit);
+ }
+ }
+ }
+ return(0);
+}
+
+/********************************************************************************
+ * Initiate a consistency check on a system drive.
+ *
+ * check [<drive>]
+ * Start a check of <drive>
+ *
+ */
+static int
+cmd_check(int argc, char *argv[])
+{
+ int unit, fd, result;
+
+ if (argc != 2)
+ return(cmd_help(argc, argv));
+
+ if ((unit = driveunit(argv[1])) == -1) {
+ warnx("'%s' is not a valid drive", argv[1]);
+ } else {
+
+ /* Get the device */
+ if ((fd = open(drivepath(unit), 0)) < 0) {
+ warn("can't open %s", drivepath(unit));
+ } else {
+ /* Try to start the check */
+ if ((ioctl(fd, MLXD_CHECKASYNC, &result)) < 0) {
+ switch(result) {
+ case 0x0002:
+ warnx("one or more of the SCSI disks on which the drive '%s' depends is DEAD", argv[1]);
+ break;
+ case 0x0105:
+ warnx("drive %s is invalid, or not a drive which can be checked", argv[1]);
+ break;
+ case 0x0106:
+ warnx("drive rebuild or consistency check is already in progress on this controller");
+ break;
+ default:
+ warn("ioctl MLXD_CHECKASYNC");
+ }
+ }
+ }
+ }
+ return(0);
+}
+
+/********************************************************************************
+ * Initiate a physical drive rebuild
+ *
+ * rebuild <controller> <channel>:<target>
+ * Start a rebuild of <controller>:<channel>:<target>
+ *
+ */
+static int
+cmd_rebuild(int argc, char *argv[])
+{
+ struct mlx_rebuild_request rb;
+ int unit, fd;
+
+ if (argc != 3)
+ return(cmd_help(argc, argv));
+
+ /* parse arguments */
+ if ((unit = ctrlrunit(argv[1])) == -1) {
+ warnx("'%s' is not a valid controller", argv[1]);
+ return(1);
+ }
+ /* try diskXXXX and unknownXXXX as we report the latter for a dead drive ... */
+ if ((sscanf(argv[2], "disk%2d%2d", &rb.rr_channel, &rb.rr_target) != 2) &&
+ (sscanf(argv[2], "unknown%2d%2d", &rb.rr_channel, &rb.rr_target) != 2)) {
+ warnx("'%s' is not a valid physical drive", argv[2]);
+ return(1);
+ }
+ /* get the device */
+ if ((fd = open(ctrlrpath(unit), 0)) < 0) {
+ warn("can't open %s", ctrlrpath(unit));
+ return(1);
+ }
+ /* try to start the rebuild */
+ if ((ioctl(fd, MLX_REBUILDASYNC, &rb)) < 0) {
+ switch(rb.rr_status) {
+ case 0x0002:
+ warnx("the drive at %d:%d is already ONLINE", rb.rr_channel, rb.rr_target);
+ break;
+ case 0x0004:
+ warnx("drive failed during rebuild");
+ break;
+ case 0x0105:
+ warnx("there is no drive at channel %d, target %d", rb.rr_channel, rb.rr_target);
+ break;
+ case 0x0106:
+ warnx("drive rebuild or consistency check is already in progress on this controller");
+ break;
+ default:
+ warn("ioctl MLXD_CHECKASYNC");
+ }
+ }
+ return(0);
+}
+
+#ifdef SUPPORT_PAUSE
+/********************************************************************************
+ * Pause one or more channels on a controller
+ *
+ * pause [-d <delay>] [-t <time>] <controller> [<channel>...]
+ * Pauses <channel> (or all channels) for <time> seconds after a
+ * delay of <delay> seconds.
+ * pause <controller> -c
+ * Cancels pending pause
+ */
+static int
+cmd_pause(int argc, char *argv[])
+{
+ struct mlx_pause mp;
+ int unit, i, ch, fd, cancel = 0;
+ char *cp;
+ int oargc = argc;
+ char **oargv = argv;
+
+ mp.mp_which = 0;
+ mp.mp_when = 30;
+ mp.mp_howlong = 30;
+ optreset = 1;
+ optind = 1;
+ while ((ch = getopt(argc, argv, "cd:t:")) != -1)
+ switch(ch) {
+ case 'c':
+ cancel = 1;
+ break;
+ case 'd':
+ mp.mp_when = strtol(optarg, &cp, 0);
+ if (*cp != 0)
+ return(cmd_help(argc, argv));
+ break;
+ case 't':
+ mp.mp_howlong = strtol(optarg, &cp, 0);
+ if (*cp != 0)
+ return(cmd_help(argc, argv));
+ break;
+ default:
+ return(cmd_help(argc, argv));
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* get controller unit number that we're working on */
+ if ((argc < 1) || ((unit = ctrlrunit(argv[0])) == -1))
+ return(cmd_help(oargc, oargv));
+
+ /* Get the device */
+ if ((fd = open(ctrlrpath(unit), 0)) < 0) {
+ warn("can't open %s", ctrlrpath(unit));
+ return(1);
+ }
+
+ if (argc == 1) {
+ /* controller-wide pause/cancel */
+ mp.mp_which = cancel ? MLX_PAUSE_CANCEL : MLX_PAUSE_ALL;
+ } else {
+ for (i = 1; i < argc; i++) {
+ ch = strtol(argv[i], &cp, 0);
+ if (*cp != 0) {
+ warnx("bad channel number '%s'", argv[i]);
+ continue;
+ } else {
+ mp.mp_which |= (1 << ch);
+ }
+ }
+ }
+ if ((ioctl(fd, MLX_PAUSE_CHANNEL, &mp)) < 0)
+ warn("couldn't %s %s", cancel ? "cancel pause on" : "pause", ctrlrname(unit));
+ close(fd);
+ return(0);
+}
+#endif /* SUPPORT_PAUSE */
+
diff --git a/usr.sbin/mlxcontrol/config.c b/usr.sbin/mlxcontrol/config.c
new file mode 100644
index 0000000..a525658
--- /dev/null
+++ b/usr.sbin/mlxcontrol/config.c
@@ -0,0 +1,157 @@
+/*-
+ * Copyright (c) 1999 Michael Smith
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <err.h>
+
+#include <dev/mlx/mlxio.h>
+#include <dev/mlx/mlxreg.h>
+
+#include "mlxcontrol.h"
+
+static void print_span(struct mlx_sys_drv_span *span, int arms);
+static void print_sys_drive(struct conf_config *conf, int drvno);
+static void print_phys_drive(struct conf_config *conf, int chn, int targ);
+
+/********************************************************************************
+ * Get the configuration from the selected controller.
+ *
+ * config <controller>
+ * Print the configuration for <controller>
+ *
+ * XXX update to support adding/deleting drives.
+ */
+
+int
+cmd_config(int argc, char *argv[])
+{
+ struct conf_config conf;
+ int unit = 0; /* XXX */
+ int i, j;
+
+ bzero(&conf.cc_cfg, sizeof(conf.cc_cfg));
+ if (mlx_read_configuration(unit, &conf.cc_cfg)) {
+ printf("mlx%d: error submitting READ CONFIGURATION\n", unit);
+ } else {
+
+ printf("# Controller <INSERT DETAILS HERE>\n");
+ printf("#\n# Physical devices connected:\n");
+ for (i = 0; i < 5; i++)
+ for (j = 0; j < 16; j++)
+ print_phys_drive(&conf, i, j);
+ printf("#\n# System Drives defined:\n");
+
+ for (i = 0; i < conf.cc_cfg.cc_num_sys_drives; i++)
+ print_sys_drive(&conf, i);
+ }
+ return(0);
+}
+
+
+/********************************************************************************
+ * Print details for the system drive (drvno) in a format that we will be
+ * able to parse later.
+ *
+ * drive?? <raidlevel> <writemode>
+ * span? 0x????????-0x???????? ????MB on <disk> [...]
+ * ...
+ */
+static void
+print_span(struct mlx_sys_drv_span *span, int arms)
+{
+ int i;
+
+ printf("0x%08x-0x%08x %uMB on", span->sp_start_lba, span->sp_start_lba + span->sp_nblks, span->sp_nblks / 2048);
+ for (i = 0; i < arms; i++)
+ printf(" disk%02d%02d", span->sp_arm[i] >> 4, span->sp_arm[i] & 0x0f);
+ printf("\n");
+}
+
+static void
+print_sys_drive(struct conf_config *conf, int drvno)
+{
+ struct mlx_sys_drv *drv = &conf->cc_cfg.cc_sys_drives[drvno];
+ int i;
+
+ printf("drive%02d ", drvno);
+ switch(drv->sd_raidlevel & 0xf) {
+ case MLX_SYS_DRV_RAID0:
+ printf("RAID0");
+ break;
+ case MLX_SYS_DRV_RAID1:
+ printf("RAID1");
+ break;
+ case MLX_SYS_DRV_RAID3:
+ printf("RAID3");
+ break;
+ case MLX_SYS_DRV_RAID5:
+ printf("RAID5");
+ break;
+ case MLX_SYS_DRV_RAID6:
+ printf("RAID6");
+ break;
+ case MLX_SYS_DRV_JBOD:
+ printf("JBOD");
+ break;
+ default:
+ printf("RAID?");
+ }
+ printf(" write%s\n", drv->sd_raidlevel & MLX_SYS_DRV_WRITEBACK ? "back" : "through");
+
+ for (i = 0; i < drv->sd_valid_spans; i++) {
+ printf(" span%d ", i);
+ print_span(&drv->sd_span[i], drv->sd_valid_arms);
+ }
+}
+
+/********************************************************************************
+ * Print details for the physical drive at chn/targ in a format suitable for
+ * human consumption.
+ *
+ * <type>CCTT (<state>) "<vendor>/<model>"
+ * ????MB <features>
+ *
+ */
+static void
+print_phys_drive(struct conf_config *conf, int chn, int targ)
+{
+ struct mlx_phys_drv *drv = &conf->cc_cfg.cc_phys_drives[chn * 16 + targ];
+
+ /* if the drive isn't present, don't print it */
+ if (!(drv->pd_flags1 & MLX_PHYS_DRV_PRESENT))
+ return;
+
+ mlx_print_phys_drv(drv, chn, targ, "# ", 1);
+}
+
+
diff --git a/usr.sbin/mlxcontrol/interface.c b/usr.sbin/mlxcontrol/interface.c
new file mode 100644
index 0000000..5cbf7b6
--- /dev/null
+++ b/usr.sbin/mlxcontrol/interface.c
@@ -0,0 +1,287 @@
+/*-
+ * Copyright (c) 1999 Michael Smith
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <cam/scsi/scsi_all.h>
+
+#include <dev/mlx/mlxio.h>
+#include <dev/mlx/mlxreg.h>
+
+#include "mlxcontrol.h"
+
+/********************************************************************************
+ * Iterate over all mlx devices, call (func) with each ones' path and (arg)
+ */
+void
+mlx_foreach(void (*func)(int unit, void *arg), void *arg)
+{
+ int i, fd;
+
+ /* limit total count for sanity */
+ for (i = 0; i < 64; i++) {
+ /* verify we can open it */
+ if ((fd = open(ctrlrpath(i), 0)) >= 0)
+ close(fd);
+ /* if we can, do */
+ if (fd >= 0) {
+ func(i, arg);
+ }
+ }
+}
+
+/********************************************************************************
+ * Open the controller (unit) and give the fd to (func) along with (arg)
+ */
+void
+mlx_perform(int unit, void (*func)(int fd, void *arg), void *arg)
+{
+ int fd;
+
+ if ((fd = open(ctrlrpath(unit), 0)) >= 0) {
+ func(fd, arg);
+ close(fd);
+ }
+}
+
+/********************************************************************************
+ * Iterate over all mlxd devices, call (func) with each ones' path and (arg)
+ */
+void
+mlxd_foreach_ctrlr(int unit, void *arg)
+{
+ struct mlxd_foreach_action *ma = (struct mlxd_foreach_action *)arg;
+ int i, fd;
+
+ /* Get the device */
+ if ((fd = open(ctrlrpath(unit), 0)) < 0)
+ return;
+
+ for (i = -1; ;) {
+ /* Get the unit number of the next child device */
+ if (ioctl(fd, MLX_NEXT_CHILD, &i) < 0)
+ return;
+
+ /* check that we can open this unit */
+ if ((fd = open(drivepath(i), 0)) >= 0)
+ close(fd);
+ /* if we can, do */
+ if (fd >= 0) {
+ ma->func(i, ma->arg);
+ }
+ }
+}
+
+void
+mlxd_foreach(void (*func)(int unit, void *arg), void *arg)
+{
+ struct mlxd_foreach_action ma;
+
+ ma.func = func;
+ ma.arg = arg;
+ mlx_foreach(mlxd_foreach_ctrlr, &ma);
+}
+
+/********************************************************************************
+ * Find the controller that manages the drive (unit), return controller number
+ * and system drive number on that controller.
+ */
+static struct
+{
+ int unit;
+ int ctrlr;
+ int sysdrive;
+} mlxd_find_ctrlr_param;
+
+static void
+mlxd_find_ctrlr_search(int unit, void *arg)
+{
+ int i, fd;
+
+ /* Get the device */
+ if ((fd = open(ctrlrpath(unit), 0)) >= 0) {
+ for (i = -1; ;) {
+ /* Get the unit number of the next child device */
+ if (ioctl(fd, MLX_NEXT_CHILD, &i) < 0)
+ break;
+
+ /* is this child the unit we want? */
+ if (i == mlxd_find_ctrlr_param.unit) {
+ mlxd_find_ctrlr_param.ctrlr = unit;
+ if (ioctl(fd, MLX_GET_SYSDRIVE, &i) == 0)
+ mlxd_find_ctrlr_param.sysdrive = i;
+ }
+ }
+ close(fd);
+ }
+}
+
+int
+mlxd_find_ctrlr(int unit, int *ctrlr, int *sysdrive)
+{
+ mlxd_find_ctrlr_param.unit = unit;
+ mlxd_find_ctrlr_param.ctrlr = -1;
+ mlxd_find_ctrlr_param.sysdrive = -1;
+
+ mlx_foreach(mlxd_find_ctrlr_search, NULL);
+ if ((mlxd_find_ctrlr_param.ctrlr != -1) && (mlxd_find_ctrlr_param.sysdrive != -1)) {
+ *ctrlr = mlxd_find_ctrlr_param.ctrlr;
+ *sysdrive = mlxd_find_ctrlr_param.sysdrive;
+ return(0);
+ }
+ return(1);
+}
+
+
+/********************************************************************************
+ * Send a command to the controller on (fd)
+ */
+
+void
+mlx_command(int fd, void *arg)
+{
+ struct mlx_usercommand *cmd = (struct mlx_usercommand *)arg;
+ int error;
+
+ error = ioctl(fd, MLX_COMMAND, cmd);
+ if (error != 0)
+ cmd->mu_error = error;
+}
+
+/********************************************************************************
+ * Perform an ENQUIRY2 command and return information related to the controller
+ * (unit)
+ */
+int
+mlx_enquiry(int unit, struct mlx_enquiry2 *enq)
+{
+ struct mlx_usercommand cmd;
+
+ /* build the command */
+ cmd.mu_datasize = sizeof(*enq);
+ cmd.mu_buf = enq;
+ cmd.mu_bufptr = 8;
+ cmd.mu_command[0] = MLX_CMD_ENQUIRY2;
+
+ /* hand it off for processing */
+ mlx_perform(unit, mlx_command, (void *)&cmd);
+
+ return(cmd.mu_status != 0);
+}
+
+
+/********************************************************************************
+ * Perform a READ CONFIGURATION command and return information related to the controller
+ * (unit)
+ */
+int
+mlx_read_configuration(int unit, struct mlx_core_cfg *cfg)
+{
+ struct mlx_usercommand cmd;
+
+ /* build the command */
+ cmd.mu_datasize = sizeof(*cfg);
+ cmd.mu_buf = cfg;
+ cmd.mu_bufptr = 8;
+ cmd.mu_command[0] = MLX_CMD_READ_CONFIG;
+
+ /* hand it off for processing */
+ mlx_perform(unit, mlx_command, (void *)&cmd);
+
+ return(cmd.mu_status != 0);
+}
+
+/********************************************************************************
+ * Perform a SCSI INQUIRY command and return pointers to the relevant data.
+ */
+int
+mlx_scsi_inquiry(int unit, int channel, int target, char **vendor, char **device, char **revision)
+{
+ struct mlx_usercommand cmd;
+ static struct {
+ struct mlx_dcdb dcdb;
+ union {
+ struct scsi_inquiry_data inq;
+ u_int8_t pad[SHORT_INQUIRY_LENGTH];
+ } d;
+ } __attribute__ ((packed)) dcdb_cmd;
+ struct scsi_inquiry *inq_cmd = (struct scsi_inquiry *)&dcdb_cmd.dcdb.dcdb_cdb[0];
+
+ /* build the command */
+ cmd.mu_datasize = sizeof(dcdb_cmd);
+ cmd.mu_buf = &dcdb_cmd;
+ cmd.mu_command[0] = MLX_CMD_DIRECT_CDB;
+
+ /* build the DCDB */
+ bzero(&dcdb_cmd, sizeof(dcdb_cmd));
+ dcdb_cmd.dcdb.dcdb_channel = channel;
+ dcdb_cmd.dcdb.dcdb_target = target;
+ dcdb_cmd.dcdb.dcdb_flags = MLX_DCDB_DATA_IN | MLX_DCDB_TIMEOUT_10S;
+ dcdb_cmd.dcdb.dcdb_datasize = SHORT_INQUIRY_LENGTH;
+ dcdb_cmd.dcdb.dcdb_cdb_length = 6;
+ dcdb_cmd.dcdb.dcdb_sense_length = SSD_FULL_SIZE;
+
+ /* build the cdb */
+ inq_cmd->opcode = INQUIRY;
+ inq_cmd->length = SHORT_INQUIRY_LENGTH;
+
+ /* hand it off for processing */
+ mlx_perform(unit, mlx_command, &cmd);
+
+ if (cmd.mu_status == 0) {
+ *vendor = &dcdb_cmd.d.inq.vendor[0];
+ *device = &dcdb_cmd.d.inq.product[0];
+ *revision = &dcdb_cmd.d.inq.revision[0];
+ }
+ return(cmd.mu_status);
+}
+
+/********************************************************************************
+ * Perform a GET DEVICE STATE command and return pointers to the relevant data.
+ */
+int
+mlx_get_device_state(int unit, int channel, int target, struct mlx_phys_drv *drv)
+{
+ struct mlx_usercommand cmd;
+
+ /* build the command */
+ cmd.mu_datasize = sizeof(*drv);
+ cmd.mu_buf = drv;
+ cmd.mu_bufptr = 8;
+ cmd.mu_command[0] = MLX_CMD_DEVICE_STATE;
+ cmd.mu_command[2] = channel;
+ cmd.mu_command[3] = target;
+
+ /* hand it off for processing */
+ mlx_perform(unit, mlx_command, (void *)&cmd);
+
+ return(cmd.mu_status != 0);
+}
diff --git a/usr.sbin/mlxcontrol/mlxcontrol.8 b/usr.sbin/mlxcontrol/mlxcontrol.8
new file mode 100644
index 0000000..3803fb6
--- /dev/null
+++ b/usr.sbin/mlxcontrol/mlxcontrol.8
@@ -0,0 +1,148 @@
+.\"
+.\" Copyright (c) 2000 Michael Smith
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd April 10, 2000
+.Dt MLXCONTROL 8
+.Os
+.Sh NAME
+.Nm mlxcontrol
+.Nd Mylex DAC-family RAID management utility
+.Sh SYNOPSIS
+.Nm
+.Aq command
+.Op args
+.Nm
+status
+.Op Fl qv
+.Op Ar drive
+.Nm
+rescan
+.Ar controller
+.Op Ar controller ...
+.Nm
+detach
+.Ar drive
+.Op Ar drive ...
+.Nm
+detach
+.Fl a
+.Nm
+check
+.Ar drive
+.Nm
+config
+.Ar controller
+.Nm
+help
+.Ar command
+.Sh DESCRIPTION
+The
+.Nm
+utility provides status monitoring and management functions
+for devices attached
+to the
+.Xr mlx 4
+driver.
+.Pp
+Controller names are of the form "mlxN"
+where N is the unit number of the controller.
+Drive names are of the form "mlxdN"
+where N is the unit number of the drive.
+Do not specify the path to a device node.
+.Pp
+.Bl -tag -width rebuild
+.It status
+Print the status of controllers and system drives.
+If one or more drives are specified,
+only print information about these drives,
+otherwise print information
+about all controllers and drives in the system.
+With the
+.Fl v
+flag, display much more verbose information.
+With the
+.Fl q
+flag, do not print any output.
+This command returns
+0 if all drives tested are online,
+1 if one or more drives are critical and
+2 if one or more are offline.
+.It rescan
+Rescan one or more controllers for non-attached system drives
+(eg. drives that have been
+detached or created subsequent to driver initialisation).
+If the
+.Fl a
+flag is supplied, rescan all controllers in the system.
+.It detach
+Detach one or more system drives.
+Drives must be unmounted
+and not opened by any other utility for this command to succeed.
+If the
+.Fl a
+flag is supplied, detach all system drives from the nominated controller.
+.It check
+Initiate a consistency check and repair pass on a redundant system drive
+(eg. RAID1 or RAID5).
+The controller will scan the system drive and repair any inconsistencies.
+This command returns immediately;
+use the
+.Ar status
+command to monitor the progress of the check.
+.It rebuild
+Requires two arguments,
+.Ar controller
+and
+.Ar physdrive
+as specified in the
+output of the
+.Ar status
+command.
+All system drives using space on the physical drive
+.Ar physdrive
+are rebuilt, reconstructing all data on the drive.
+Note that each controller can only perform one rebuild at a time.
+This command returns immediately; use the
+.Ar status
+command to monitor the progress of the rebuild.
+.It config
+Print the current configuration from the nominated controller.
+This command will be updated
+to allow addition/deletion of system drives from a configuration
+in a future release.
+.It help
+Print usage information for
+.Ar command .
+.El
+.Sh BUGS
+The
+.Ar config
+command does not yet support modifying system drive configuration.
+.Pp
+Error log extraction is not yet supported.
+.Sh AUTHORS
+The mlxcontrol utility was written by
+.An Michael Smith
+.Aq msmith@FreeBSD.org .
diff --git a/usr.sbin/mlxcontrol/mlxcontrol.h b/usr.sbin/mlxcontrol/mlxcontrol.h
new file mode 100644
index 0000000..f0225b5
--- /dev/null
+++ b/usr.sbin/mlxcontrol/mlxcontrol.h
@@ -0,0 +1,89 @@
+/*-
+ * Copyright (c) 1999 Michael Smith
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/queue.h>
+
+#define debug(fmt, args...) printf("%s: " fmt "\n", __FUNCTION__ , ##args)
+
+struct mlxd_foreach_action
+{
+ void (*func)(int unit, void *arg);
+ void *arg;
+};
+
+extern void mlx_foreach(void (*func)(int unit, void *arg), void *arg);
+void mlxd_foreach_ctrlr(int unit, void *arg);
+extern void mlxd_foreach(void (*func)(int unit, void *arg), void *arg);
+extern int mlxd_find_ctrlr(int unit, int *ctrlr, int *sysdrive);
+extern void mlx_perform(int unit, void (*func)(int fd, void *arg), void *arg);
+extern void mlx_command(int fd, void *arg);
+extern int mlx_enquiry(int unit, struct mlx_enquiry2 *enq);
+extern int mlx_read_configuration(int unit, struct mlx_core_cfg *cfg);
+extern int mlx_scsi_inquiry(int unit, int bus, int target, char **vendor, char **device, char **revision);
+extern int mlx_get_device_state(int fd, int channel, int target, struct mlx_phys_drv *drv);
+
+extern char *ctrlrpath(int unit);
+extern char *ctrlrname(int unit);
+extern char *drivepath(int unit);
+extern char *drivename(int unit);
+extern int ctrlrunit(char *str);
+extern int driveunit(char *str);
+
+extern void mlx_print_phys_drv(struct mlx_phys_drv *drv, int channel, int target, char *prefix, int verbose);
+
+struct conf_phys_drv
+{
+ TAILQ_ENTRY(conf_phys_drv) pd_link;
+ int pd_bus;
+ int pd_target;
+ struct mlx_phys_drv pd_drv;
+};
+
+struct conf_span
+{
+ TAILQ_ENTRY(conf_span) s_link;
+ struct conf_phys_drv *s_drvs[8];
+ struct mlx_sys_drv_span s_span;
+};
+
+struct conf_sys_drv
+{
+ TAILQ_ENTRY(conf_sys_drv) sd_link;
+ struct conf_span *sd_spans[4];
+ struct mlx_sys_drv sd_drv;
+};
+
+struct conf_config
+{
+ TAILQ_HEAD(,conf_phys_drv) cc_phys_drvs;
+ TAILQ_HEAD(,conf_span) cc_spans;
+ TAILQ_HEAD(,conf_sys_drv) cc_sys_drvs;
+ struct conf_sys_drv *cc_drives[32];
+ struct mlx_core_cfg cc_cfg;
+};
+
diff --git a/usr.sbin/mlxcontrol/util.c b/usr.sbin/mlxcontrol/util.c
new file mode 100644
index 0000000..37cd2b7
--- /dev/null
+++ b/usr.sbin/mlxcontrol/util.c
@@ -0,0 +1,172 @@
+/*-
+ * Copyright (c) 1999 Michael Smith
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <paths.h>
+#include <string.h>
+
+#include <dev/mlx/mlxio.h>
+#include <dev/mlx/mlxreg.h>
+
+#include "mlxcontrol.h"
+
+/********************************************************************************
+ * Various name-producing and -parsing functions
+ */
+
+/* return path of controller (unit) */
+char *
+ctrlrpath(int unit)
+{
+ static char buf[32];
+
+ sprintf(buf, "%s%s", _PATH_DEV, ctrlrname(unit));
+ return(buf);
+}
+
+/* return name of controller (unit) */
+char *
+ctrlrname(int unit)
+{
+ static char buf[32];
+
+ sprintf(buf, "mlx%d", unit);
+ return(buf);
+}
+
+/* return path of drive (unit) */
+char *
+drivepath(int unit)
+{
+ static char buf[32];
+
+ sprintf(buf, "%s%s", _PATH_DEV, drivename(unit));
+ return(buf);
+}
+
+/* return name of drive (unit) */
+char *
+drivename(int unit)
+{
+ static char buf[32];
+
+ sprintf(buf, "mlxd%d", unit);
+ return(buf);
+}
+
+/* get controller unit number from name in (str) */
+int
+ctrlrunit(char *str)
+{
+ int unit;
+
+ if (sscanf(str, "mlx%d", &unit) == 1)
+ return(unit);
+ return(-1);
+}
+
+/* get drive unit number from name in (str) */
+int
+driveunit(char *str)
+{
+ int unit;
+
+ if (sscanf(str, "mlxd%d", &unit) == 1)
+ return(unit);
+ return(-1);
+}
+
+/********************************************************************************
+ * Standardised output of various data structures.
+ */
+
+void
+mlx_print_phys_drv(struct mlx_phys_drv *drv, int chn, int targ, char *prefix, int verbose)
+{
+ char *type, *device, *vendor, *revision;
+
+ switch(drv->pd_flags2 & 0x03) {
+ case MLX_PHYS_DRV_DISK:
+ type = "disk";
+ break;
+ case MLX_PHYS_DRV_SEQUENTIAL:
+ type = "tape";
+ break;
+ case MLX_PHYS_DRV_CDROM:
+ type= "cdrom";
+ break;
+ case MLX_PHYS_DRV_OTHER:
+ default:
+ type = "unknown";
+ break;
+ }
+ printf("%s%s%02d%02d ", prefix, type, chn, targ);
+ switch(drv->pd_status) {
+ case MLX_PHYS_DRV_DEAD:
+ printf(" (dead) ");
+ break;
+ case MLX_PHYS_DRV_WRONLY:
+ printf(" (write-only) ");
+ break;
+ case MLX_PHYS_DRV_ONLINE:
+ printf(" (online) ");
+ break;
+ case MLX_PHYS_DRV_STANDBY:
+ printf(" (standby) ");
+ break;
+ default:
+ printf(" (0x%02x) ", drv->pd_status);
+ }
+ printf("\n");
+
+ if (verbose) {
+
+ printf("%s ", prefix);
+ if (!mlx_scsi_inquiry(0, chn, targ, &vendor, &device, &revision)) {
+ printf("'%8.8s' '%16.16s' '%4.4s'", vendor, device, revision);
+ } else {
+ printf("<IDENTIFY FAILED>");
+ }
+
+ printf(" %dMB ", drv->pd_config_size / 2048);
+
+ if (drv->pd_flags2 & MLX_PHYS_DRV_FAST20) {
+ printf(" fast20");
+ } else if (drv->pd_flags2 & MLX_PHYS_DRV_FAST) {
+ printf(" fast");
+ }
+ if (drv->pd_flags2 & MLX_PHYS_DRV_WIDE)
+ printf(" wide");
+ if (drv->pd_flags2 & MLX_PHYS_DRV_SYNC)
+ printf(" sync");
+ if (drv->pd_flags2 & MLX_PHYS_DRV_TAG)
+ printf(" tag-enabled");
+ printf("\n");
+ }
+}
diff --git a/usr.sbin/mount_nwfs/Makefile b/usr.sbin/mount_nwfs/Makefile
new file mode 100644
index 0000000..00fed85
--- /dev/null
+++ b/usr.sbin/mount_nwfs/Makefile
@@ -0,0 +1,16 @@
+# $FreeBSD$
+
+PROG= mount_nwfs
+SRCS= mount_nwfs.c getmntopts.c
+MAN= mount_nwfs.8
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+= -DNWFS -I${MOUNT}
+WARNS= 0
+
+.PATH: ${MOUNT}
+
+DPADD= ${LIBNCP} ${LIBIPX}
+LDADD= -lncp -lipx
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mount_nwfs/mount_nwfs.8 b/usr.sbin/mount_nwfs/mount_nwfs.8
new file mode 100644
index 0000000..341ec78
--- /dev/null
+++ b/usr.sbin/mount_nwfs/mount_nwfs.8
@@ -0,0 +1,230 @@
+.\" $FreeBSD$
+.Dd October 14, 1999
+.Dt MOUNT_NWFS 8
+.Os
+.Sh NAME
+.Nm mount_nwfs
+.Nd mount NetWare volume from a NetWare file server
+.Sh SYNOPSIS
+.Nm
+.Op Fl Chv
+.Fl S Ar server
+.Fl U Ar user
+.Op Fl connection\ options
+.Fl V Ar volume
+.Op Fl M Ar mode
+.Op Fl c Ar case
+.Op Fl d Ar mode
+.Op Fl f Ar mode
+.Op Fl g Ar gid
+.Op Fl l Ar locale
+.Op Fl n Ar os2
+.Op Fl u Ar uid
+.Op Fl w Ar scheme
+.Ar node
+.Nm
+.Op Fl options
+.Ar /server:user/volume[/path]
+.Ar node
+.Sh DESCRIPTION
+The
+.Nm
+utility allows to mount volume from a NetWare server.
+It may use either
+existing connection or create new: if no usable connection was found
+it will try to establish a new one.
+Connection has count of references to it,
+so when last mount will be dismounted connection will be closed.
+It is
+possible to create connection without any mounts (but use it for them) with
+.Xr ncplogin 1 .
+.Pp
+Note two forms of command line.
+In the first form, server and user specified
+via
+.Fl S
+and
+.Fl U
+options respectively.
+In the second form server and user specified in
+.Ar special
+part of
+.Xr mount 8
+command line arguments (the
+.Fl S ,
+.Fl U
+and
+.Fl V
+options aren't used in this case).
+This allows use of
+.Xr fstab 5
+file (see
+.Sx EXAMPLES
+below).
+.Pp
+The options are:
+.Bl -tag -width indent
+.It Fl S Ar server
+Name of NetWare server to connect.
+For native IP you will need also
+.Fl A
+option.
+.It Fl U Ar user
+Name of user used in login sequence.
+.It Fl connection\ options
+See
+.Xr ncplogin 1
+for details.
+.It Fl V Ar volume
+Volume name to mount.
+Volume name can also be specified after all options and
+before
+.Ar mount-point .
+.It Ar node
+Path to mount volume.
+.It Fl c Ar case
+Select a
+.Ar case
+option which affects on name representation.
+.Ar Case
+can be one of the following:
+.Bl -tag -width "ValueXX"
+.It Em Value
+.Em Meaning
+.It l
+All existing file names converted to lower case.
+Newly created file gets a lower case under OS2 name space.
+This is the default when mounting volumes with DOS name space.
+.It L
+Same as 'l' but filesystem tries to be case insensitive.
+May not work well.
+.It n
+No case conversion is performed.
+.Em Warning !
+Use this option with DOS name space only as a last resort,
+because creating a lower case name in the DOS name space
+can lead to unpredictable results.
+This is the default when mounting volumes with OS2 name space.
+.It u
+All existing file names converted to upper case.
+Newly created file gets an upper case under OS2 name space.
+.It U
+Same as 'u' but filesystem tries to be case insensitive.
+May not work well.
+.El
+.It Fl f Ar mode , Fl d Ar mode
+Specify permissions that should be assigned to files and directories.
+The values must be specified as octal numbers.
+Default value for the file mode
+is taken from mount point, default value for the dir mode adds execute
+permission where the file mode gives read permission.
+.Pp
+Note that these permissions can differ from the rights granted by NetWare
+server.
+.It Fl n Ar namespace
+Don't use
+.Ar namespace .
+Currently only
+.Ar OS2
+can be here.
+.It Fl v
+Print version number.
+.It Fl u Ar uid , Fl g Ar gid
+User id and group id assigned to files.
+The default is owner and group id from
+directory where volume is mounted.
+.It Fl l Ar locale
+Set the locale for case conversion.
+By default
+.Nm
+tries to use an environment variable
+.Ev LC_* .
+.It Fl w Ar scheme
+Select a
+.Ar scheme
+used to convert file names between NetWare and
+.Fx .
+Supported conversion schemes are:
+.Bl -tag -width ".Cm koi2cp866"
+.It Cm asis
+Characters passed as is without any alteration.
+.It Cm koi2cp866
+koi8-r <-> CP866
+.It Cm se
+Suits for setups used in Sweden.
+.El
+.It Fl M Ar mode
+See
+.Xr ncplogin 1
+for details.
+If this option is omitted, connection permissions
+assumed the same as directory mode
+.Pq Fl d
+option.
+.El
+.Sh FILES
+.Bl -tag -width /var/log/wtmp -compact
+.It Pa ~/.nwfsrc
+keeps static parameters for connections and other information.
+See
+.Pa /usr/share/examples/nwclient/dot.nwfsrc
+for details.
+.El
+.Sh NOTES
+Before any NCP connection can be established kernel must be configured
+for IPX support, IPXrouted and KLD nwfs.ko should be loaded.
+.Sh EXAMPLES
+Next examples illustrates how to connect to NetWare server
+.Em nwserv
+as user
+.Em GUEST
+and mount volumes
+.Em SYS
+and
+.Em VOL1 :
+.Bd -literal -offset indent
+mount_nwfs -S nwserv -U guest -V sys /nw/s1/sys
+mount_nwfs /nwserv:guest/sys /nw/s1/sys
+mount -t nwfs /nwserv:guest/vol1 /nw/s1/vol1
+mount -t nwfs /nwserv:boris/sys/home/boris /home/boris/nw/home
+.Ed
+.Pp
+The last example mounts only subdirectory on a volume and equivalent
+to NetWare 'map root' command.
+.Pp
+It is possible to use
+.Xr fstab 5
+for nwfs mounts:
+.Bd -literal -offset indent
+/nwserv:guest/sys /nw/s1/sys nwfs rw,noauto 0 0
+/nwserv:guest/vol1 /nw/s1/vol2 nwfs rw,noauto 0 0
+.Ed
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 4.0 .
+.Sh BUGS
+to number a few
+.Sh CREDITS
+In development of NetWare client for
+.Fx ,
+the following sources were used:
+.Pp
+Documentation from NetWare NDK.
+.Pp
+Ncpfs for Linux - written by
+.An Volker Lendecke Aq lendecke@math.uni\-goettingen.de .
+He granted me permission to publish parts of his code under
+.Bx Ns -style
+license,
+.Pp
+"Interrupt List" from
+.An Ralf Brown ,
+.Pp
+Many files from
+.Pa /sys
+directory.
+.Sh AUTHORS
+.An Boris Popov Aq bp@butya.kz ,
+.Aq rbp@chat.ru
diff --git a/usr.sbin/mount_nwfs/mount_nwfs.c b/usr.sbin/mount_nwfs/mount_nwfs.c
new file mode 100644
index 0000000..8e37b09
--- /dev/null
+++ b/usr.sbin/mount_nwfs/mount_nwfs.c
@@ -0,0 +1,376 @@
+/*
+ * Copyright (c) 1999, Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <sys/mount.h>
+#include <sys/sysctl.h>
+#include <machine/cpu.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <pwd.h>
+#include <grp.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <err.h>
+#include <sysexits.h>
+#include <time.h>
+
+#include <netncp/ncp_lib.h>
+#include <netncp/ncp_rcfile.h>
+#include <fs/nwfs/nwfs_mount.h>
+#include "mntopts.h"
+
+#define NWFS_VFSNAME "nwfs"
+
+static char mount_point[MAXPATHLEN + 1];
+static void usage(void);
+static int parsercfile(struct ncp_conn_loginfo *li, struct nwfs_args *mdata);
+
+static struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ { NULL }
+};
+
+static int
+parsercfile(struct ncp_conn_loginfo *li, struct nwfs_args *mdata) {
+ return 0;
+}
+
+int
+main(int argc, char *argv[]) {
+ NWCONN_HANDLE connHandle;
+ struct nwfs_args mdata;
+ struct ncp_conn_loginfo li;
+ struct stat st;
+ struct vfsconf vfc;
+ struct nw_entry_info einfo;
+ struct tm *tm;
+ time_t ltime;
+ int opt, error, mntflags, nlsopt, wall_clock, len;
+ int mib[2];
+ char *p, *p1, tmp[1024];
+ u_char *pv;
+
+ if (argc < 2)
+ usage();
+ if (argc == 2) {
+ if (strcmp(argv[1], "-h") == 0) {
+ usage();
+ } else if (strcmp(argv[1], "-v") == 0) {
+ errx(EX_OK, "version %d.%d.%d", NWFS_VERSION / 100000,
+ (NWFS_VERSION % 10000) / 1000,
+ (NWFS_VERSION % 1000) / 100);
+ }
+ }
+
+ error = getvfsbyname(NWFS_VFSNAME, &vfc);
+ if (error && vfsisloadable(NWFS_VFSNAME)) {
+ if(vfsload(NWFS_VFSNAME))
+ err(EX_OSERR, "vfsload("NWFS_VFSNAME")");
+ endvfsent();
+ error = getvfsbyname(NWFS_VFSNAME, &vfc);
+ }
+ if (error)
+ errx(EX_OSERR, "NetWare filesystem is not available");
+
+ if(ncp_initlib()) exit(1);
+
+ mntflags = error = 0;
+ bzero(&mdata,sizeof(mdata));
+ mdata.uid = mdata.gid = -1;
+ nlsopt = 0;
+
+ if (ncp_li_init(&li, argc, argv)) return 1;
+ /*
+ * A little bit weird, but I should figure out which server/user to use
+ * _before_ reading .rc file
+ */
+ if (argc >= 3 && argv[argc-1][0] != '-' && argv[argc-2][0] != '-' &&
+ argv[argc-2][0] == '/') {
+ p = argv[argc-2];
+ error = 1;
+ do {
+ if (*p++ != '/') break;
+ p1 = tmp;
+ while (*p != ':' && *p != 0) *p1++ = *p++;
+ if (*p++ == 0) break;
+ *p1 = 0;
+ if (ncp_li_setserver(&li, tmp)) break;
+ p1 = tmp;
+ while (*p != '/' && *p != 0) *p1++ = *p++;
+ if (*p++ == 0) break;
+ *p1 = 0;
+ if (ncp_li_setuser(&li, tmp)) break;
+ p1 = tmp;
+ while (*p != '/' && *p != 0) *p1++ = *p++;
+ *p1 = 0;
+ if (strlen(tmp) > NCP_VOLNAME_LEN) {
+ warnx("volume name too long: %s", tmp);
+ break;
+ }
+ ncp_str_upper(strcpy(mdata.mounted_vol,tmp));
+ if (*p == '/')
+ p++;
+ p1 = mdata.root_path + 2;
+ pv = mdata.root_path + 1;
+ for(;*p;) {
+ *pv = 0;
+ while (*p != '/' && *p) {
+ *p1++ = *p++;
+ (*pv)++;
+ }
+ if (*pv) {
+ ncp_nls_mem_u2n(pv + 1, pv + 1, *pv);
+ pv += (*pv) + 1;
+ mdata.root_path[0]++;
+ }
+ if (*p++ == 0) break;
+ p1++;
+ }
+ error = 0;
+ } while(0);
+ if (error)
+ errx(EX_DATAERR,
+ "an error occurred while parsing '%s'",
+ argv[argc - 2]);
+ }
+ if (ncp_li_readrc(&li)) return 1;
+ if (ncp_rc) {
+ parsercfile(&li,&mdata);
+ rc_close(ncp_rc);
+ }
+ while ((opt = getopt(argc, argv, STDPARAM_OPT"V:c:d:f:g:l:n:o:u:w:")) != -1) {
+ switch (opt) {
+ case STDPARAM_ARGS:
+ if (ncp_li_arg(&li, opt, optarg)) {
+ return 1;
+ }
+ break;
+ case 'V':
+ if (strlen(optarg) > NCP_VOLNAME_LEN)
+ errx(EX_DATAERR, "volume too long: %s", optarg);
+ ncp_str_upper(strcpy(mdata.mounted_vol,optarg));
+ break;
+ case 'u': {
+ struct passwd *pwd;
+
+ pwd = isdigit(optarg[0]) ?
+ getpwuid(atoi(optarg)) : getpwnam(optarg);
+ if (pwd == NULL)
+ errx(EX_NOUSER, "unknown user '%s'", optarg);
+ mdata.uid = pwd->pw_uid;
+ break;
+ }
+ case 'g': {
+ struct group *grp;
+
+ grp = isdigit(optarg[0]) ?
+ getgrgid(atoi(optarg)) : getgrnam(optarg);
+ if (grp == NULL)
+ errx(EX_NOUSER, "unknown group '%s'", optarg);
+ mdata.gid = grp->gr_gid;
+ break;
+ }
+ case 'd':
+ errno = 0;
+ mdata.dir_mode = strtol(optarg, &p, 8);
+ if (errno || *p != 0)
+ errx(EX_DATAERR, "invalid value for directory mode");
+ break;
+ case 'f':
+ errno = 0;
+ mdata.file_mode = strtol(optarg, &p, 8);
+ if (errno || *p != 0)
+ errx(EX_DATAERR, "invalid value for file mode");
+ break;
+ case '?':
+ usage();
+ /*NOTREACHED*/
+ case 'n': {
+ char *inp, *nsp;
+
+ nsp = inp = optarg;
+ while ((nsp = strsep(&inp, ",;:")) != NULL) {
+ if (strcasecmp(nsp, "OS2") == 0)
+ mdata.flags |= NWFS_MOUNT_NO_OS2;
+ else if (strcasecmp(nsp, "LONG") == 0)
+ mdata.flags |= NWFS_MOUNT_NO_LONG;
+ else if (strcasecmp(nsp, "NFS") == 0)
+ mdata.flags |= NWFS_MOUNT_NO_NFS;
+ else
+ errx(EX_DATAERR, "unknown namespace '%s'", nsp);
+ }
+ break;
+ };
+ case 'l':
+ if (ncp_nls_setlocale(optarg) != 0) return 1;
+ mdata.flags |= NWFS_MOUNT_HAVE_NLS;
+ break;
+ case 'o':
+ getmntopts(optarg, mopts, &mntflags, 0);
+ break;
+ case 'c':
+ switch (optarg[0]) {
+ case 'l':
+ nlsopt |= NWHP_LOWER;
+ break;
+ case 'u':
+ nlsopt |= NWHP_UPPER;
+ break;
+ case 'n':
+ nlsopt |= NWHP_LOWER | NWHP_UPPER;
+ break;
+ case 'L':
+ nlsopt |= NWHP_LOWER | NWHP_NOSTRICT;
+ break;
+ case 'U':
+ nlsopt |= NWHP_UPPER | NWHP_NOSTRICT;
+ break;
+ default:
+ errx(EX_DATAERR, "invalid suboption '%c' for -c",
+ optarg[0]);
+ }
+ break;
+ case 'w':
+ if (ncp_nls_setrecodebyname(optarg) != 0)
+ return 1;
+ mdata.flags |= NWFS_MOUNT_HAVE_NLS;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ if (optind == argc - 2) {
+ optind++;
+ } else if (mdata.mounted_vol[0] == 0)
+ errx(EX_USAGE, "volume name should be specified");
+
+ if (optind != argc - 1)
+ usage();
+ realpath(argv[optind], mount_point);
+
+ if (stat(mount_point, &st) == -1)
+ err(EX_OSERR, "could not find mount point %s", mount_point);
+ if (!S_ISDIR(st.st_mode)) {
+ errno = ENOTDIR;
+ err(EX_OSERR, "can't mount on %s", mount_point);
+ }
+ if (ncp_geteinfo(mount_point, &einfo) == 0)
+ errx(EX_OSERR, "can't mount on %s twice", mount_point);
+
+ if (mdata.uid == -1) {
+ mdata.uid = st.st_uid;
+ }
+ if (mdata.gid == -1) {
+ mdata.gid = st.st_gid;
+ }
+ if (mdata.file_mode == 0 ) {
+ mdata.file_mode = st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
+ }
+ if (mdata.dir_mode == 0) {
+ mdata.dir_mode = mdata.file_mode;
+ if ((mdata.dir_mode & S_IRUSR) != 0)
+ mdata.dir_mode |= S_IXUSR;
+ if ((mdata.dir_mode & S_IRGRP) != 0)
+ mdata.dir_mode |= S_IXGRP;
+ if ((mdata.dir_mode & S_IROTH) != 0)
+ mdata.dir_mode |= S_IXOTH;
+ }
+ if (li.access_mode == 0) {
+ li.access_mode = mdata.dir_mode;
+ }
+/* if (mdata.flags & NWFS_MOUNT_HAVE_NLS) {*/
+ mdata.nls = ncp_nls;
+/* }*/
+ mdata.nls.opt = nlsopt;
+
+ mib[0] = CTL_MACHDEP;
+ mib[1] = CPU_WALLCLOCK;
+ len = sizeof(wall_clock);
+ if (sysctl(mib, 2, &wall_clock, &len, NULL, 0) == -1)
+ err(EX_OSERR, "get wall_clock");
+ if (wall_clock == 0) {
+ time(&ltime);
+ tm = localtime(&ltime);
+ mdata.tz = -(tm->tm_gmtoff / 60);
+ }
+
+ error = ncp_li_check(&li);
+ if (error)
+ return 1;
+ li.opt |= NCP_OPT_WDOG;
+ /* well, now we can try to login, or use already established connection */
+ error = ncp_li_login(&li, &connHandle);
+ if (error) {
+ ncp_error("cannot login to server %s", error, li.server);
+ exit(1);
+ }
+ error = ncp_conn2ref(connHandle, &mdata.connRef);
+ if (error) {
+ ncp_error("could not convert handle to reference", error);
+ ncp_disconnect(connHandle);
+ exit(1);
+ }
+ strcpy(mdata.mount_point,mount_point);
+ mdata.version = NWFS_VERSION;
+ error = mount(NWFS_VFSNAME, mdata.mount_point, mntflags, (void*)&mdata);
+ if (error) {
+ ncp_error("mount error: %s", error, mdata.mount_point);
+ ncp_disconnect(connHandle);
+ exit(1);
+ }
+ /*
+ * I'm leave along my handle, but kernel should keep own ...
+ */
+ ncp_disconnect(connHandle);
+ /* we are done ?, impossible ... */
+ return 0;
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n",
+ "usage: mount_nwfs [-Chv] -S server -U user [-connection options]",
+ " -V volume [-M mode] [-c case] [-d mode] [-f mode]",
+ " [-g gid] [-l locale] [-n os2] [-u uid] [-w scheme]",
+ " node",
+ " mount_nwfs [-options] /server:user/volume[/path] node");
+
+ exit (1);
+}
diff --git a/usr.sbin/mount_portalfs/Makefile b/usr.sbin/mount_portalfs/Makefile
new file mode 100644
index 0000000..90161dc
--- /dev/null
+++ b/usr.sbin/mount_portalfs/Makefile
@@ -0,0 +1,15 @@
+# From: @(#)Makefile 8.3 (Berkeley) 3/27/94
+# $FreeBSD$
+
+PROG= mount_portalfs
+SRCS= mount_portalfs.c activate.c conf.c getmntopts.c pt_conf.c \
+ pt_exec.c pt_file.c pt_tcp.c pt_tcplisten.c
+MAN= mount_portalfs.8
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+=-I${MOUNT}
+WARNS= 0
+
+.PATH: ${MOUNT}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mount_portalfs/activate.c b/usr.sbin/mount_portalfs/activate.c
new file mode 100644
index 0000000..6143620
--- /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[] =
+ "$FreeBSD$";
+#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;
+ union {
+ struct cmsghdr cmsg;
+ char control[CMSG_SPACE(sizeof(int))];
+ } ctl;
+
+ /*
+ * Line up error code. Don't worry about byte ordering
+ * because we must be sending to the local machine.
+ */
+ iov.iov_base = (caddr_t) &error;
+ iov.iov_len = sizeof(error);
+
+ /*
+ * Build a msghdr
+ */
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ /*
+ * If there is a file descriptor to send then
+ * construct a suitable rights control message.
+ */
+ if (fd >= 0) {
+ ctl.cmsg.cmsg_len = CMSG_LEN(sizeof(int));
+ ctl.cmsg.cmsg_level = SOL_SOCKET;
+ ctl.cmsg.cmsg_type = SCM_RIGHTS;
+ *((int *)CMSG_DATA(&ctl.cmsg)) = fd;
+ msg.msg_control = (caddr_t) &ctl;
+ msg.msg_controllen = ctl.cmsg.cmsg_len;
+ }
+
+ /*
+ * Send to kernel...
+ */
+ if ((n = sendmsg(so, &msg, 0)) < 0)
+ syslog(LOG_ERR, "send: %s", strerror(errno));
+#ifdef DEBUG
+ fprintf(stderr, "sent %d bytes\n", n);
+#endif
+ sleep(1); /*XXX*/
+#ifdef notdef
+ if (shutdown(so, 2) < 0)
+ syslog(LOG_ERR, "shutdown: %s", strerror(errno));
+#endif
+ /*
+ * Throw away the open file descriptor
+ */
+ (void) close(fd);
+}
+
+void activate(q, so)
+qelem *q;
+int so;
+{
+ struct portal_cred pcred;
+ char key[MAXPATHLEN+1];
+ int error;
+ char **v;
+ int fd = -1;
+
+ /*
+ * Read the key from the socket
+ */
+ error = get_request(so, &pcred, key, sizeof(key));
+ if (error) {
+ syslog(LOG_ERR, "activate: recvmsg: %s", strerror(error));
+ goto drop;
+ }
+
+#ifdef DEBUG
+ fprintf(stderr, "lookup key %s\n", key);
+#endif
+
+ /*
+ * Find a match in the configuration file
+ */
+ v = conf_match(q, key);
+
+ /*
+ * If a match existed, then find an appropriate portal
+ * otherwise simply return ENOENT.
+ */
+ if (v) {
+ error = activate_argv(&pcred, key, v, so, &fd);
+ if (error)
+ fd = -1;
+ else if (fd < 0)
+ error = -1;
+ } else {
+ error = ENOENT;
+ }
+
+ if (error >= 0)
+ send_reply(so, fd, error);
+
+drop:;
+ close(so);
+}
diff --git a/usr.sbin/mount_portalfs/conf.c b/usr.sbin/mount_portalfs/conf.c
new file mode 100644
index 0000000..d5833e4
--- /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[] =
+ "$FreeBSD$";
+#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..daaed15
--- /dev/null
+++ b/usr.sbin/mount_portalfs/mount_portalfs.8
@@ -0,0 +1,152 @@
+.\"
+.\" Copyright (c) 1993, 1994
+.\" The Regents of the University of California. All rights reserved.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software donated to Berkeley by
+.\" Jan-Simon Pendry.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)mount_portal.8 8.3 (Berkeley) 3/27/94
+.\" $FreeBSD$
+.\"
+.Dd March 27, 1994
+.Dt MOUNT_PORTALFS 8
+.Os
+.Sh NAME
+.Nm mount_portalfs
+.Nd mount the portal daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl o Ar options
+.Ar /etc/portal.conf
+.Ar mount_point
+.Sh DESCRIPTION
+The
+.Nm
+utility attaches an instance of the portal daemon
+to the global 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 2
+with the returned file descriptor.
+Privileged ports can only be bound to
+by the super-user.
+The
+.Pa tcp
+namespace takes a hostname and a port (slash separated) and
+creates an open TCP/IP connection.
+The
+.Pa fs
+namespace opens the named file, starting back at the root directory.
+This can be used to provide a controlled escape path from
+a chrooted environment.
+.Sh "CONFIGURATION FILE"
+The configuration file contains a list of rules.
+Each rule takes one line and consists of two or more
+whitespace separated fields.
+A hash (``#'') character causes the remainder of a line to
+be ignored. Blank lines are ignored.
+.Pp
+The first field is a pathname prefix to match
+against the requested pathname.
+If a match is found, the second field
+tells the daemon what type of object to create.
+Subsequent fields are passed to the creation function.
+.Bd -literal
+# @(#)portal.conf 5.1 (Berkeley) 7/13/92
+tcplisten/ tcplisten tcplisten/
+tcp/ tcp tcp/
+fs/ file fs/
+.Ed
+.Sh FILES
+.Bl -tag -width /p/* -compact
+.It Pa /p/*
+.El
+.Sh SEE ALSO
+.Xr mount 2 ,
+.Xr unmount 2 ,
+.Xr fstab 5 ,
+.Xr mount 8
+.Sh CAVEATS
+This 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..c07eed9
--- /dev/null
+++ b/usr.sbin/mount_portalfs/mount_portalfs.c
@@ -0,0 +1,295 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#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(void) __dead2;
+
+static sig_atomic_t readcf; /* Set when SIGHUP received */
+
+static void sighup(sig)
+int sig;
+{
+ readcf ++;
+}
+
+static void sigchld(sig)
+int sig;
+{
+ pid_t pid;
+
+ while ((pid = waitpid((pid_t) -1, (int *) 0, WNOHANG)) > 0)
+ ;
+ /* wrtp - waitpid _doesn't_ return 0 when no children! */
+#ifdef notdef
+ if (pid < 0 && errno != ECHILD)
+ syslog(LOG_WARNING, "waitpid: %s", strerror(errno));
+#endif
+}
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct portal_args args;
+ struct sockaddr_un un;
+ char *conf;
+ char mountpt[MAXPATHLEN];
+ int mntflags = 0;
+ char tag[32];
+ 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];
+
+ /* resolve the mountpoint with realpath(3) */
+ (void)checkpath(argv[optind+1], mountpt);
+
+ /*
+ * Construct the listening socket
+ */
+ un.sun_family = AF_UNIX;
+ if (sizeof(_PATH_TMPPORTAL) >= sizeof(un.sun_path)) {
+ errx(EX_SOFTWARE, "portal socket name too long");
+ }
+ strcpy(un.sun_path, _PATH_TMPPORTAL);
+ mktemp(un.sun_path);
+ un.sun_len = strlen(un.sun_path);
+
+ so = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (so < 0) {
+ err(EX_OSERR, "socket");
+ }
+ um = umask(077);
+ (void) unlink(un.sun_path);
+ if (bind(so, (struct sockaddr *) &un, sizeof(un)) < 0)
+ err(1, NULL);
+
+ (void) unlink(un.sun_path);
+ (void) umask(um);
+
+ (void) listen(so, 5);
+
+ args.pa_socket = so;
+ sprintf(tag, "portal:%d", getpid());
+ args.pa_config = tag;
+
+ error = getvfsbyname("portalfs", &vfc);
+ if (error && vfsisloadable("portalfs")) {
+ if (vfsload("portalfs"))
+ err(EX_OSERR, "vfsload(portalfs)");
+ endvfsent();
+ error = getvfsbyname("portalfs", &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_portalfs [-o options] config mount-point\n");
+ exit(EX_USAGE);
+}
diff --git a/usr.sbin/mount_portalfs/pathnames.h b/usr.sbin/mount_portalfs/pathnames.h
new file mode 100644
index 0000000..1672085
--- /dev/null
+++ b/usr.sbin/mount_portalfs/pathnames.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ * All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/5/93
+ *
+ * $FreeBSD$
+ */
+
+#include <paths.h>
+
+#define _PATH_TMPPORTAL "/tmp/portalXXXXXXXXXX" /* Scratch socket name */
diff --git a/usr.sbin/mount_portalfs/portal.conf b/usr.sbin/mount_portalfs/portal.conf
new file mode 100644
index 0000000..53a07a9
--- /dev/null
+++ b/usr.sbin/mount_portalfs/portal.conf
@@ -0,0 +1,7 @@
+# @(#)portal.conf 8.1 (Berkeley) 6/5/93
+# $FreeBSD$
+tcplisten/ tcplisten tcplisten/
+tcp/ tcp tcp/
+fs/ file fs/
+pipe/ pipe
+foo/ exec ./bar bar baz
diff --git a/usr.sbin/mount_portalfs/portald.h b/usr.sbin/mount_portalfs/portald.h
new file mode 100644
index 0000000..46c6afe
--- /dev/null
+++ b/usr.sbin/mount_portalfs/portald.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ * All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)portald.h 8.1 (Berkeley) 6/5/93
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+#include <fs/portalfs/portal.h>
+
+/*
+ * Meta-chars in an RE. Paths in the config file containing
+ * any of these characters will be matched using regexec, other
+ * paths will be prefix-matched.
+ */
+#define RE_CHARS ".|()[]*+?\\^$"
+
+typedef struct qelem qelem;
+
+struct qelem {
+ qelem *q_forw;
+ qelem *q_back;
+};
+
+typedef struct provider provider;
+struct provider {
+ char *pr_match;
+ int (*pr_func)(struct portal_cred *,
+ char *key, char **v, int so, int *fdp);
+};
+extern provider providers[];
+
+/*
+ * Portal providers
+ */
+extern int portal_exec(struct portal_cred *,
+ char *key, char **v, int so, int *fdp);
+extern int portal_file(struct portal_cred *,
+ char *key, char **v, int so, int *fdp);
+extern int portal_tcp(struct portal_cred *,
+ char *key, char **v, int so, int *fdp);
+extern int portal_tcplisten(struct portal_cred *,
+ char *key, char **v, int so, int *fdp);
+
+/*
+ * Global functions
+ */
+extern void activate(qelem *q, int so);
+extern char **conf_match(qelem *q, char *key);
+extern void conf_read(qelem *q, char *conf);
diff --git a/usr.sbin/mount_portalfs/pt_conf.c b/usr.sbin/mount_portalfs/pt_conf.c
new file mode 100644
index 0000000..f158a73
--- /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[] =
+ "$FreeBSD$";
+#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..359c78e
--- /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[] =
+ "$FreeBSD$";
+#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..f22ebd7
--- /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[] =
+ "$FreeBSD$";
+#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..4905616
--- /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[] =
+ "$FreeBSD$";
+#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/mount_smbfs/Makefile b/usr.sbin/mount_smbfs/Makefile
new file mode 100644
index 0000000..01252fe
--- /dev/null
+++ b/usr.sbin/mount_smbfs/Makefile
@@ -0,0 +1,23 @@
+# $FreeBSD$
+
+PROG= mount_smbfs
+SRCS= mount_smbfs.c getmntopts.c
+WARNS?= 2
+NO_WERROR= yes
+MAN= mount_smbfs.8
+
+MOUNTDIR= ${.CURDIR}/../mount
+CONTRIBDIR= ${.CURDIR}/../../contrib/smbfs
+CFLAGS+= -DSMBFS -I${MOUNTDIR} -I${CONTRIBDIR}/include
+
+LDADD+= -lsmb
+DPADD+= ${LIBSMB}
+
+# Needs to be dynamically linked for optional dlopen() access to
+# userland libiconv (see the -E option).
+#
+NOSHARED?= NO
+
+.PATH: ${CONTRIBDIR}/mount_smbfs
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mountd/Makefile b/usr.sbin/mountd/Makefile
new file mode 100644
index 0000000..04cd28f
--- /dev/null
+++ b/usr.sbin/mountd/Makefile
@@ -0,0 +1,8 @@
+# From: @(#)Makefile 8.3 (Berkeley) 1/25/94
+# $FreeBSD$
+
+PROG= mountd
+WARNS= 0
+MAN= exports.5 netgroup.5 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..98e1f24
--- /dev/null
+++ b/usr.sbin/mountd/exports.5
@@ -0,0 +1,323 @@
+.\" Copyright (c) 1989, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)exports.5 8.3 (Berkeley) 3/29/95
+.\" $FreeBSD$
+.\"
+.Dd March 29, 1995
+.Dt EXPORTS 5
+.Os
+.Sh NAME
+.Nm exports
+.Nd define remote mount points for
+.Tn NFS
+mount requests
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+The
+.Nm
+file specifies remote mount points for the
+.Tn NFS
+mount protocol per the
+.Tn NFS
+server specification; see
+.%T "Network File System Protocol Specification" ,
+RFC1094, Appendix A and
+.%T "NFS: Network File System Version 3 Specification" ,
+Appendix I.
+.Pp
+Each line in the file
+(other than comment lines that begin with a #)
+specifies the mount point(s) and export flags within one local server
+filesystem for one or more hosts.
+A long line may be split over several lines by ending all but the
+last line with a backslash
+.Pq Ql \e .
+A host may be specified only once for each local 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
+.Xr mountd 8 .
+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.
+The use of this option will prevent the kernel from compiling
+unless calls to the appropriate Kerberos encryption routines
+are provided in the NFS source.
+.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
+.Pq Tn WebNFS .
+This is to mimic the behavior of URLs.
+If no
+.Fl index
+option is specified, a directory filehandle will be returned as usual.
+The
+.Fl index
+option only makes sense in combination with the
+.Fl public
+or
+.Fl webnfs
+flags.
+.Pp
+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.
+.Pp
+The
+.Xr mountd 8
+utility can be made to re-read the
+.Nm
+file by sending it a hangup signal as follows:
+.Bd -literal -offset indent
+kill -s HUP `cat /var/run/mountd.pid`
+.Ed
+.Pp
+After sending the
+.Dv SIGHUP ,
+check the
+.Xr syslogd 8
+output to see whether
+.Xr mountd 8
+logged any parsing errors in the
+.Nm
+file.
+.Sh FILES
+.Bl -tag -width /etc/exports -compact
+.It Pa /etc/exports
+the default remote mount-point file
+.El
+.Sh 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..0be0e73
--- /dev/null
+++ b/usr.sbin/mountd/mountd.8
@@ -0,0 +1,150 @@
+.\" Copyright (c) 1989, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)mountd.8 8.4 (Berkeley) 4/28/95
+.\" $FreeBSD$
+.\"
+.Dd April 28, 1995
+.Dt MOUNTD 8
+.Os
+.Sh NAME
+.Nm mountd
+.Nd service remote
+.Tn NFS
+mount requests
+.Sh SYNOPSIS
+.Nm
+.Op Fl 2dlnr
+.Op Ar exportsfile
+.Sh DESCRIPTION
+The
+.Nm
+utility is the server for
+.Tn NFS
+mount requests from other client machines.
+It listens for service requests at the port indicated in the
+.Tn NFS
+server specification; see
+.%T "Network File System Protocol Specification" ,
+RFC1094, Appendix A and
+.%T "NFS: Network File System Version 3 Protocol Specification" ,
+Appendix I.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl 2
+Allow the administrator to force clients to use only the
+version 2
+.Tn NFS
+protocol to mount 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.nfsrv.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 filesystem in which the swapfiles resides
+will have to be exported with the
+.Fl alldirs
+flag.
+.It Ar exportsfile
+Specify an alternate location
+for the exports file.
+.El
+.Pp
+When
+.Nm
+is started,
+it loads the export host addresses and options into the kernel
+using the
+.Xr mount 2
+system call.
+After changing the exports file,
+a hangup signal should be sent to the
+.Nm
+daemon
+to get it to reload the export information.
+After sending the SIGHUP
+(kill \-s HUP `cat /var/run/mountd.pid`),
+check the syslog output to see if
+.Nm
+logged any parsing
+errors in the exports file.
+.Pp
+If
+.Nm
+detects that the running kernel does not include
+.Tn NFS
+support, it will attempt to load a loadable kernel module containing
+.Tn NFS
+code, using
+.Xr kldload 8
+by way of
+.Xr vfsload 3 .
+If this fails, or no
+.Tn NFS
+KLD was available,
+.Nm
+exits with an error.
+.Sh FILES
+.Bl -tag -width /var/run/mountd.pid -compact
+.It Pa /etc/exports
+the list of exported 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 kldload 8 ,
+.Xr nfsd 8 ,
+.Xr rpcbind 8 ,
+.Xr showmount 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Bx 4.4 .
diff --git a/usr.sbin/mountd/mountd.c b/usr.sbin/mountd/mountd.c
new file mode 100644
index 0000000..5940c4d
--- /dev/null
+++ b/usr.sbin/mountd/mountd.c
@@ -0,0 +1,2444 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#endif /*not lint*/
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/syslog.h>
+#include <sys/sysctl.h>
+#include <sys/linker.h>
+#include <sys/module.h>
+
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+#include <rpc/pmap_prot.h>
+#include <rpcsvc/mount.h>
+#include <nfs/rpcv2.h>
+#include <nfs/nfsproto.h>
+#include <nfsserver/nfs.h>
+#include <ufs/ufs/ufsmount.h>
+#include <fs/msdosfs/msdosfsmount.h>
+#include <fs/ntfs/ntfsmount.h>
+#include <isofs/cd9660/cd9660_mount.h> /* XXX need isofs in include */
+
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <grp.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "pathnames.h"
+
+#ifdef DEBUG
+#include <stdarg.h>
+#endif
+
+#ifndef MOUNTDLOCK
+#define MOUNTDLOCK "/var/run/mountd.lock"
+#endif
+
+/*
+ * Structures for keeping the mount list and export list
+ */
+struct mountlist {
+ struct mountlist *ml_next;
+ char ml_host[RPCMNT_NAMELEN+1];
+ char ml_dirp[RPCMNT_PATHLEN+1];
+};
+
+struct dirlist {
+ struct dirlist *dp_left;
+ struct dirlist *dp_right;
+ int dp_flag;
+ struct hostlist *dp_hosts; /* List of hosts this dir exported to */
+ char dp_dirp[1]; /* Actually malloc'd to size of dir */
+};
+/* dp_flag bits */
+#define DP_DEFSET 0x1
+#define DP_HOSTSET 0x2
+
+struct exportlist {
+ struct exportlist *ex_next;
+ struct dirlist *ex_dirl;
+ struct dirlist *ex_defdir;
+ int ex_flag;
+ fsid_t ex_fs;
+ char *ex_fsdir;
+ char *ex_indexfile;
+};
+/* ex_flag bits */
+#define EX_LINKED 0x1
+
+struct netmsk {
+ struct sockaddr_storage nt_net;
+ struct sockaddr_storage nt_mask;
+ char *nt_name;
+};
+
+union grouptypes {
+ struct addrinfo *gt_addrinfo;
+ struct netmsk gt_net;
+};
+
+struct grouplist {
+ int gr_type;
+ union grouptypes gr_ptr;
+ struct grouplist *gr_next;
+};
+/* Group types */
+#define GT_NULL 0x0
+#define GT_HOST 0x1
+#define GT_NET 0x2
+#define GT_DEFAULT 0x3
+#define GT_IGNORE 0x5
+
+struct hostlist {
+ int ht_flag; /* Uses DP_xx bits */
+ struct grouplist *ht_grp;
+ struct hostlist *ht_next;
+};
+
+struct fhreturn {
+ int fhr_flag;
+ int fhr_vers;
+ nfsfh_t fhr_fh;
+};
+
+/* Global defs */
+char *add_expdir(struct dirlist **, char *, int);
+void add_dlist(struct dirlist **, struct dirlist *,
+ struct grouplist *, int);
+void add_mlist(char *, char *);
+int check_dirpath(char *);
+int check_options(struct dirlist *);
+int checkmask(struct sockaddr *sa);
+int chk_host(struct dirlist *, struct sockaddr *, int *, int *);
+void del_mlist(char *hostp, char *dirp);
+struct dirlist *dirp_search(struct dirlist *, char *);
+int do_mount(struct exportlist *, struct grouplist *, int,
+ struct xucred *, char *, int, struct statfs *);
+int do_opt(char **, char **, struct exportlist *, struct grouplist *,
+ int *, int *, struct xucred *);
+struct exportlist *ex_search(fsid_t *);
+struct exportlist *get_exp(void);
+void free_dir(struct dirlist *);
+void free_exp(struct exportlist *);
+void free_grp(struct grouplist *);
+void free_host(struct hostlist *);
+void get_exportlist(void);
+int get_host(char *, struct grouplist *, struct grouplist *);
+struct hostlist *get_ht(void);
+int get_line(void);
+void get_mountlist(void);
+int get_net(char *, struct netmsk *, int);
+void getexp_err(struct exportlist *, struct grouplist *);
+struct grouplist *get_grp(void);
+void hang_dirp(struct dirlist *, struct grouplist *,
+ struct exportlist *, int);
+void huphandler(int sig);
+int makemask(struct sockaddr_storage *ssp, int bitlen);
+void mntsrv(struct svc_req *, SVCXPRT *);
+void nextfield(char **, char **);
+void out_of_mem(void);
+void parsecred(char *, struct xucred *);
+int put_exlist(struct dirlist *, XDR *, struct dirlist *, int *);
+void *sa_rawaddr(struct sockaddr *sa, int *nbytes);
+int sacmp(struct sockaddr *sa1, struct sockaddr *sa2,
+ struct sockaddr *samask);
+int scan_tree(struct dirlist *, struct sockaddr *);
+static void usage(void);
+int xdr_dir(XDR *, char *);
+int xdr_explist(XDR *, caddr_t);
+int xdr_fhs(XDR *, caddr_t);
+int xdr_mlist(XDR *, caddr_t);
+void terminate(int);
+
+struct exportlist *exphead;
+struct mountlist *mlhead;
+struct grouplist *grphead;
+char exname[MAXPATHLEN];
+struct xucred def_anon = {
+ XUCRED_VERSION,
+ (uid_t)-2,
+ 1,
+ { (gid_t)-2 },
+ NULL
+};
+int force_v2 = 0;
+int resvport_only = 1;
+int dir_only = 1;
+int log = 0;
+int got_sighup = 0;
+
+int opt_flags;
+static int have_v6 = 1;
+#ifdef NI_WITHSCOPEID
+static const int ninumeric = NI_NUMERICHOST | NI_WITHSCOPEID;
+#else
+static const int ninumeric = NI_NUMERICHOST;
+#endif
+
+int mountdlockfd;
+/* Bits for opt_flags above */
+#define OP_MAPROOT 0x01
+#define OP_MAPALL 0x02
+/* 0x4 free */
+#define OP_MASK 0x08
+#define OP_NET 0x10
+#define OP_ALLDIRS 0x40
+#define OP_HAVEMASK 0x80 /* A mask was specified or inferred. */
+#define OP_MASKLEN 0x200
+
+#ifdef DEBUG
+int debug = 1;
+void SYSLOG(int, const char *, ...) __printflike(2, 3);
+#define syslog SYSLOG
+#else
+int debug = 0;
+#endif
+
+/*
+ * Mountd server for NFS mount protocol as described in:
+ * NFS: Network File System Protocol Specification, RFC1094, Appendix A
+ * The optional arguments are the exports file name
+ * default: _PATH_EXPORTS
+ * and "-n" to allow nonroot mount.
+ */
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ fd_set readfds;
+ SVCXPRT *udptransp, *tcptransp, *udp6transp, *tcp6transp;
+ struct netconfig *udpconf, *tcpconf, *udp6conf, *tcp6conf;
+ int udpsock, tcpsock, udp6sock, tcp6sock;
+ int xcreated = 0, s;
+ int one = 1;
+ int c;
+
+ udp6conf = tcp6conf = NULL;
+ udp6sock = tcp6sock = NULL;
+
+ /* Check that another mountd isn't already running. */
+ if ((mountdlockfd = (open(MOUNTDLOCK, O_RDONLY|O_CREAT, 0444))) == -1)
+ err(1, "%s", MOUNTDLOCK);
+
+ if(flock(mountdlockfd, LOCK_EX|LOCK_NB) == -1 && errno == EWOULDBLOCK)
+ errx(1, "another rpc.mountd is already running. Aborting");
+ s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+ if (s < 0)
+ have_v6 = 0;
+ else
+ close(s);
+ if (modfind("nfsserver") < 0) {
+ /* Not present in kernel, try loading it */
+ if (kldload("nfsserver") < 0 || modfind("nfsserver") < 0)
+ errx(1, "NFS server is not available or loadable");
+ }
+
+ while ((c = getopt(argc, argv, "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, huphandler);
+ signal(SIGTERM, terminate);
+ { FILE *pidfile = fopen(_PATH_MOUNTDPID, "w");
+ if (pidfile != NULL) {
+ fprintf(pidfile, "%d\n", getpid());
+ fclose(pidfile);
+ }
+ }
+ rpcb_unset(RPCPROG_MNT, RPCMNT_VER1, NULL);
+ rpcb_unset(RPCPROG_MNT, RPCMNT_VER3, NULL);
+ udpsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ tcpsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ udpconf = getnetconfigent("udp");
+ tcpconf = getnetconfigent("tcp");
+ if (!have_v6)
+ goto skip_v6;
+ udp6sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+ tcp6sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
+ /*
+ * We're doing host-based access checks here, so don't allow
+ * v4-in-v6 to confuse things. The kernel will disable it
+ * by default on NFS sockets too.
+ */
+ if (udp6sock != -1 && setsockopt(udp6sock, IPPROTO_IPV6,
+ IPV6_BINDV6ONLY, &one, sizeof one) < 0){
+ syslog(LOG_ERR, "can't disable v4-in-v6 on UDP socket");
+ exit(1);
+ }
+ if (tcp6sock != -1 && setsockopt(tcp6sock, IPPROTO_IPV6,
+ IPV6_BINDV6ONLY, &one, sizeof one) < 0){
+ syslog(LOG_ERR, "can't disable v4-in-v6 on UDP socket");
+ exit(1);
+ }
+ udp6conf = getnetconfigent("udp6");
+ tcp6conf = getnetconfigent("tcp6");
+
+skip_v6:
+ if (!resvport_only) {
+ if (sysctlbyname("vfs.nfsrv.nfs_privport", NULL, NULL,
+ &resvport_only, sizeof(resvport_only)) != 0 &&
+ errno != ENOENT) {
+ syslog(LOG_ERR, "sysctl: %m");
+ exit(1);
+ }
+ }
+ if (udpsock != -1 && udpconf != NULL) {
+ bindresvport(udpsock, NULL);
+ udptransp = svc_dg_create(udpsock, 0, 0);
+ if (udptransp != NULL) {
+ if (!svc_reg(udptransp, RPCPROG_MNT, RPCMNT_VER1,
+ mntsrv, udpconf))
+ syslog(LOG_WARNING, "can't register UDP RPCMNT_VER1 service");
+ else
+ xcreated++;
+ if (!force_v2) {
+ if (!svc_reg(udptransp, RPCPROG_MNT, RPCMNT_VER3,
+ mntsrv, udpconf))
+ syslog(LOG_WARNING, "can't register UDP RPCMNT_VER3 service");
+ else
+ xcreated++;
+ }
+ } else
+ syslog(LOG_WARNING, "can't create UDP services");
+
+ }
+ if (tcpsock != -1 && tcpconf != NULL) {
+ bindresvport(tcpsock, NULL);
+ listen(tcpsock, SOMAXCONN);
+ tcptransp = svc_vc_create(tcpsock, 0, 0);
+ if (tcptransp != NULL) {
+ if (!svc_reg(tcptransp, RPCPROG_MNT, RPCMNT_VER1,
+ mntsrv, tcpconf))
+ syslog(LOG_WARNING, "can't register TCP RPCMNT_VER1 service");
+ else
+ xcreated++;
+ if (!force_v2) {
+ if (!svc_reg(tcptransp, RPCPROG_MNT, RPCMNT_VER3,
+ mntsrv, tcpconf))
+ syslog(LOG_WARNING, "can't register TCP RPCMNT_VER3 service");
+ else
+ xcreated++;
+ }
+ } else
+ syslog(LOG_WARNING, "can't create TCP service");
+
+ }
+ if (have_v6 && udp6sock != -1 && udp6conf != NULL) {
+ bindresvport(udp6sock, NULL);
+ udp6transp = svc_dg_create(udp6sock, 0, 0);
+ if (udp6transp != NULL) {
+ if (!svc_reg(udp6transp, RPCPROG_MNT, RPCMNT_VER1,
+ mntsrv, udp6conf))
+ syslog(LOG_WARNING, "can't register UDP6 RPCMNT_VER1 service");
+ else
+ xcreated++;
+ if (!force_v2) {
+ if (!svc_reg(udp6transp, RPCPROG_MNT, RPCMNT_VER3,
+ mntsrv, udp6conf))
+ syslog(LOG_WARNING, "can't register UDP6 RPCMNT_VER3 service");
+ else
+ xcreated++;
+ }
+ } else
+ syslog(LOG_WARNING, "can't create UDP6 service");
+
+ }
+ if (have_v6 && tcp6sock != -1 && tcp6conf != NULL) {
+ bindresvport(tcp6sock, NULL);
+ listen(tcp6sock, SOMAXCONN);
+ tcp6transp = svc_vc_create(tcp6sock, 0, 0);
+ if (tcp6transp != NULL) {
+ if (!svc_reg(tcp6transp, RPCPROG_MNT, RPCMNT_VER1,
+ mntsrv, tcp6conf))
+ syslog(LOG_WARNING, "can't register TCP6 RPCMNT_VER1 service");
+ else
+ xcreated++;
+ if (!force_v2) {
+ if (!svc_reg(tcp6transp, RPCPROG_MNT, RPCMNT_VER3,
+ mntsrv, tcp6conf))
+ syslog(LOG_WARNING, "can't register TCP6 RPCMNT_VER3 service");
+ else
+ xcreated++;
+ }
+ } else
+ syslog(LOG_WARNING, "can't create TCP6 service");
+
+ }
+ if (xcreated == 0) {
+ syslog(LOG_ERR, "could not create any services");
+ exit(1);
+ }
+
+ /* Expand svc_run() here so that we can call get_exportlist(). */
+ for (;;) {
+ if (got_sighup) {
+ get_exportlist();
+ got_sighup = 0;
+ }
+ readfds = svc_fdset;
+ switch (select(svc_maxfd + 1, &readfds, NULL, NULL, NULL)) {
+ case -1:
+ if (errno == EINTR)
+ continue;
+ syslog(LOG_ERR, "mountd died: select: %m");
+ exit(1);
+ case 0:
+ continue;
+ default:
+ svc_getreqset(&readfds);
+ }
+ }
+}
+
+static void
+usage()
+{
+ fprintf(stderr,
+ "usage: mountd [-2] [-d] [-l] [-n] [-r] [export_file]\n");
+ exit(1);
+}
+
+/*
+ * The mount rpc service
+ */
+void
+mntsrv(rqstp, transp)
+ struct svc_req *rqstp;
+ SVCXPRT *transp;
+{
+ struct exportlist *ep;
+ struct dirlist *dp;
+ struct fhreturn fhr;
+ struct stat stb;
+ struct statfs fsb;
+ struct addrinfo *ai;
+ char host[NI_MAXHOST], numerichost[NI_MAXHOST];
+ int lookup_failed = 1;
+ struct sockaddr *saddr;
+ u_short sport;
+ char rpcpath[RPCMNT_PATHLEN + 1], dirpath[MAXPATHLEN];
+ int bad = 0, defset, hostset;
+ sigset_t sighup_mask;
+
+ sigemptyset(&sighup_mask);
+ sigaddset(&sighup_mask, SIGHUP);
+ saddr = svc_getrpccaller(transp)->buf;
+ switch (saddr->sa_family) {
+ case AF_INET6:
+ sport = ntohs(((struct sockaddr_in6 *)saddr)->sin6_port);
+ break;
+ case AF_INET:
+ sport = ntohs(((struct sockaddr_in *)saddr)->sin_port);
+ break;
+ default:
+ syslog(LOG_ERR, "request from unknown address family");
+ return;
+ }
+ lookup_failed = getnameinfo(saddr, saddr->sa_len, host, sizeof host,
+ NULL, 0, 0);
+ getnameinfo(saddr, saddr->sa_len, numerichost,
+ sizeof numerichost, NULL, 0, NI_NUMERICHOST);
+ ai = NULL;
+ switch (rqstp->rq_proc) {
+ case NULLPROC:
+ if (!svc_sendreply(transp, 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",
+ numerichost);
+ svcerr_weakauth(transp);
+ return;
+ }
+ if (!svc_getargs(transp, xdr_dir, rpcpath)) {
+ syslog(LOG_NOTICE, "undecodable mount request from %s",
+ numerichost);
+ svcerr_decode(transp);
+ return;
+ }
+
+ /*
+ * Get the real pathname and make sure it is a directory
+ * or a regular file if the -r option was specified
+ * and it exists.
+ */
+ if (realpath(rpcpath, dirpath) == NULL ||
+ stat(dirpath, &stb) < 0 ||
+ (!S_ISDIR(stb.st_mode) &&
+ (dir_only || !S_ISREG(stb.st_mode))) ||
+ statfs(dirpath, &fsb) < 0) {
+ chdir("/"); /* Just in case realpath doesn't */
+ syslog(LOG_NOTICE,
+ "mount request from %s for non existent path %s",
+ numerichost, dirpath);
+ if (debug)
+ warnx("stat failed on %s", dirpath);
+ bad = ENOENT; /* We will send error reply later */
+ }
+
+ /* Check in the exports list */
+ sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
+ ep = ex_search(&fsb.f_fsid);
+ hostset = defset = 0;
+ if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset) ||
+ ((dp = dirp_search(ep->ex_dirl, dirpath)) &&
+ chk_host(dp, saddr, &defset, &hostset)) ||
+ (defset && scan_tree(ep->ex_defdir, saddr) == 0 &&
+ scan_tree(ep->ex_dirl, saddr) == 0))) {
+ if (bad) {
+ if (!svc_sendreply(transp, 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 (!lookup_failed)
+ add_mlist(host, dirpath);
+ else
+ add_mlist(numerichost, dirpath);
+ if (debug)
+ warnx("mount successful");
+ if (log)
+ syslog(LOG_NOTICE,
+ "mount request succeeded from %s for %s",
+ numerichost, dirpath);
+ } else {
+ bad = EACCES;
+ syslog(LOG_NOTICE,
+ "mount request denied from %s for %s",
+ numerichost, dirpath);
+ }
+
+ if (bad && !svc_sendreply(transp, 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",
+ numerichost);
+ return;
+ case RPCMNT_UMOUNT:
+ if (sport >= IPPORT_RESERVED && resvport_only) {
+ syslog(LOG_NOTICE,
+ "umount request from %s from unprivileged port",
+ numerichost);
+ svcerr_weakauth(transp);
+ return;
+ }
+ if (!svc_getargs(transp, xdr_dir, rpcpath)) {
+ syslog(LOG_NOTICE, "undecodable umount request from %s",
+ numerichost);
+ svcerr_decode(transp);
+ return;
+ }
+ if (realpath(rpcpath, dirpath) == NULL) {
+ syslog(LOG_NOTICE, "umount request from %s "
+ "for non existent path %s",
+ numerichost, dirpath);
+ }
+ if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL))
+ syslog(LOG_ERR, "can't send reply");
+ if (!lookup_failed)
+ del_mlist(host, dirpath);
+ del_mlist(numerichost, dirpath);
+ if (log)
+ syslog(LOG_NOTICE,
+ "umount request succeeded from %s for %s",
+ numerichost, dirpath);
+ return;
+ case RPCMNT_UMNTALL:
+ if (sport >= IPPORT_RESERVED && resvport_only) {
+ syslog(LOG_NOTICE,
+ "umountall request from %s from unprivileged port",
+ numerichost);
+ svcerr_weakauth(transp);
+ return;
+ }
+ if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL))
+ syslog(LOG_ERR, "can't send reply");
+ if (!lookup_failed)
+ del_mlist(host, NULL);
+ del_mlist(numerichost, NULL);
+ if (log)
+ syslog(LOG_NOTICE,
+ "umountall request succeeded from %s",
+ numerichost);
+ 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",
+ numerichost);
+ return;
+ default:
+ svcerr_noproc(transp);
+ return;
+ }
+}
+
+/*
+ * Xdr conversion for a dirpath string
+ */
+int
+xdr_dir(xdrsp, dirp)
+ XDR *xdrsp;
+ char *dirp;
+{
+ return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
+}
+
+/*
+ * Xdr routine to generate file handle reply
+ */
+int
+xdr_fhs(xdrsp, cp)
+ XDR *xdrsp;
+ caddr_t cp;
+{
+ struct fhreturn *fhrp = (struct fhreturn *)cp;
+ u_long ok = 0, len, auth;
+
+ if (!xdr_long(xdrsp, &ok))
+ return (0);
+ switch (fhrp->fhr_vers) {
+ case 1:
+ return (xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, NFSX_V2FH));
+ case 3:
+ len = NFSX_V3FH;
+ if (!xdr_long(xdrsp, &len))
+ return (0);
+ if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len))
+ return (0);
+ auth = RPCAUTH_UNIX;
+ len = 1;
+ if (!xdr_long(xdrsp, &len))
+ return (0);
+ return (xdr_long(xdrsp, &auth));
+ };
+ return (0);
+}
+
+int
+xdr_mlist(xdrsp, cp)
+ XDR *xdrsp;
+ caddr_t cp;
+{
+ struct mountlist *mlp;
+ int true = 1;
+ int false = 0;
+ char *strp;
+
+ mlp = mlhead;
+ while (mlp) {
+ if (!xdr_bool(xdrsp, &true))
+ return (0);
+ strp = &mlp->ml_host[0];
+ if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN))
+ return (0);
+ strp = &mlp->ml_dirp[0];
+ if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
+ return (0);
+ mlp = mlp->ml_next;
+ }
+ if (!xdr_bool(xdrsp, &false))
+ return (0);
+ return (1);
+}
+
+/*
+ * Xdr conversion for export list
+ */
+int
+xdr_explist(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_addrinfo->ai_canonname;
+ if (!xdr_string(xdrsp, &strp,
+ RPCMNT_NAMELEN))
+ return (1);
+ } else if (grp->gr_type == GT_NET) {
+ if (!xdr_bool(xdrsp, &true))
+ return (1);
+ strp = grp->gr_ptr.gt_net.nt_name;
+ if (!xdr_string(xdrsp, &strp,
+ RPCMNT_NAMELEN))
+ return (1);
+ }
+ hp = hp->ht_next;
+ if (gotalldir && hp == (struct hostlist *)NULL) {
+ hp = adp->dp_hosts;
+ gotalldir = 0;
+ }
+ }
+ }
+ if (!xdr_bool(xdrsp, &false))
+ return (1);
+ if (put_exlist(dp->dp_right, xdrsp, adp, putdefp))
+ return (1);
+ }
+ return (0);
+}
+
+char *line;
+int linesize;
+FILE *exp_file;
+
+/*
+ * Get the export list
+ */
+void
+get_exportlist()
+{
+ struct exportlist *ep, *ep2;
+ struct grouplist *grp, *tgrp;
+ struct exportlist **epp;
+ struct dirlist *dirhead;
+ struct statfs fsb, *fsp;
+ struct xucred anon;
+ char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc;
+ int len, has_host, exflags, got_nondir, dirplen, num, i, netgrp;
+
+ dirp = NULL;
+ dirplen = 0;
+
+ /*
+ * First, get rid of the old list
+ */
+ ep = exphead;
+ while (ep) {
+ ep2 = ep;
+ ep = ep->ex_next;
+ free_exp(ep2);
+ }
+ exphead = (struct exportlist *)NULL;
+
+ grp = grphead;
+ while (grp) {
+ tgrp = grp;
+ grp = grp->gr_next;
+ free_grp(tgrp);
+ }
+ grphead = (struct grouplist *)NULL;
+
+ /*
+ * And delete exports that are in the kernel for all local
+ * filesystems.
+ * XXX: Should know how to handle all local exportable filesystems
+ * instead of just "ufs".
+ */
+ num = getmntinfo(&fsp, MNT_NOWAIT);
+ for (i = 0; i < num; i++) {
+ union {
+ struct ufs_args ua;
+ struct iso_args ia;
+ struct msdosfs_args da;
+ struct ntfs_args na;
+ } targs;
+
+ if (!strcmp(fsp->f_fstypename, "ufs") ||
+ !strcmp(fsp->f_fstypename, "msdosfs") ||
+ !strcmp(fsp->f_fstypename, "ntfs") ||
+ !strcmp(fsp->f_fstypename, "cd9660")) {
+ targs.ua.fspec = NULL;
+ targs.ua.export.ex_flags = MNT_DELEXPORT;
+ if (mount(fsp->f_fstypename, fsp->f_mntonname,
+ fsp->f_flags | MNT_UPDATE, (caddr_t)&targs) < 0 &&
+ errno != ENOENT)
+ syslog(LOG_ERR,
+ "can't delete exports for %s: %m",
+ fsp->f_mntonname);
+ }
+ fsp++;
+ }
+
+ /*
+ * Read in the exports file and build the list, calling
+ * mount() as we go along to push the export rules into the kernel.
+ */
+ if ((exp_file = fopen(exname, "r")) == NULL) {
+ syslog(LOG_ERR, "can't open %s", exname);
+ exit(2);
+ }
+ dirhead = (struct dirlist *)NULL;
+ while (get_line()) {
+ if (debug)
+ warnx("got line %s", line);
+ cp = line;
+ nextfield(&cp, &endcp);
+ if (*cp == '#')
+ goto nextline;
+
+ /*
+ * Set defaults.
+ */
+ has_host = FALSE;
+ anon = def_anon;
+ exflags = MNT_EXPORTED;
+ got_nondir = 0;
+ opt_flags = 0;
+ ep = (struct exportlist *)NULL;
+
+ /*
+ * Create new exports list entry
+ */
+ len = endcp-cp;
+ tgrp = grp = get_grp();
+ while (len > 0) {
+ if (len > RPCMNT_NAMELEN) {
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+ if (*cp == '-') {
+ if (ep == (struct exportlist *)NULL) {
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+ if (debug)
+ warnx("doing opt %s", cp);
+ got_nondir = 1;
+ if (do_opt(&cp, &endcp, ep, grp, &has_host,
+ &exflags, &anon)) {
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+ } else if (*cp == '/') {
+ savedc = *endcp;
+ *endcp = '\0';
+ if (check_dirpath(cp) &&
+ statfs(cp, &fsb) >= 0) {
+ if (got_nondir) {
+ syslog(LOG_ERR, "dirs must be first");
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+ if (ep) {
+ if (ep->ex_fs.val[0] != fsb.f_fsid.val[0] ||
+ ep->ex_fs.val[1] != fsb.f_fsid.val[1]) {
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+ } else {
+ /*
+ * See if this directory is already
+ * in the list.
+ */
+ ep = ex_search(&fsb.f_fsid);
+ if (ep == (struct exportlist *)NULL) {
+ ep = get_exp();
+ ep->ex_fs = fsb.f_fsid;
+ ep->ex_fsdir = (char *)
+ malloc(strlen(fsb.f_mntonname) + 1);
+ if (ep->ex_fsdir)
+ strcpy(ep->ex_fsdir,
+ fsb.f_mntonname);
+ else
+ out_of_mem();
+ if (debug)
+ warnx("making new ep fs=0x%x,0x%x",
+ fsb.f_fsid.val[0],
+ fsb.f_fsid.val[1]);
+ } else if (debug)
+ warnx("found ep fs=0x%x,0x%x",
+ fsb.f_fsid.val[0],
+ fsb.f_fsid.val[1]);
+ }
+
+ /*
+ * Add dirpath to export mount point.
+ */
+ dirp = add_expdir(&dirhead, cp, len);
+ dirplen = len;
+ } else {
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+ *endcp = savedc;
+ } else {
+ savedc = *endcp;
+ *endcp = '\0';
+ got_nondir = 1;
+ if (ep == (struct exportlist *)NULL) {
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+
+ /*
+ * Get the host or netgroup.
+ */
+ setnetgrent(cp);
+ netgrp = getnetgrent(&hst, &usr, &dom);
+ do {
+ if (has_host) {
+ grp->gr_next = get_grp();
+ grp = grp->gr_next;
+ }
+ if (netgrp) {
+ if (hst == 0) {
+ syslog(LOG_ERR,
+ "null hostname in netgroup %s, skipping", cp);
+ grp->gr_type = GT_IGNORE;
+ } else if (get_host(hst, grp, tgrp)) {
+ syslog(LOG_ERR,
+ "bad host %s in netgroup %s, skipping", hst, cp);
+ grp->gr_type = GT_IGNORE;
+ }
+ } else if (get_host(cp, grp, tgrp)) {
+ syslog(LOG_ERR, "bad host %s, skipping", cp);
+ grp->gr_type = GT_IGNORE;
+ }
+ has_host = TRUE;
+ } while (netgrp && getnetgrent(&hst, &usr, &dom));
+ endnetgrent();
+ *endcp = savedc;
+ }
+ cp = endcp;
+ nextfield(&cp, &endcp);
+ len = endcp - cp;
+ }
+ if (check_options(dirhead)) {
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+ if (!has_host) {
+ grp->gr_type = GT_DEFAULT;
+ if (debug)
+ warnx("adding a default entry");
+
+ /*
+ * Don't allow a network export coincide with a list of
+ * host(s) on the same line.
+ */
+ } else if ((opt_flags & OP_NET) && tgrp->gr_next) {
+ syslog(LOG_ERR, "network/host conflict");
+ getexp_err(ep, tgrp);
+ goto nextline;
+
+ /*
+ * If an export list was specified on this line, make sure
+ * that we have at least one valid entry, otherwise skip it.
+ */
+ } else {
+ grp = tgrp;
+ while (grp && grp->gr_type == GT_IGNORE)
+ grp = grp->gr_next;
+ if (! grp) {
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+ }
+
+ /*
+ * Loop through hosts, pushing the exports into the kernel.
+ * After loop, tgrp points to the start of the list and
+ * grp points to the last entry in the list.
+ */
+ grp = tgrp;
+ do {
+ if (do_mount(ep, grp, exflags, &anon, dirp, dirplen,
+ &fsb)) {
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+ } while (grp->gr_next && (grp = grp->gr_next));
+
+ /*
+ * Success. Update the data structures.
+ */
+ if (has_host) {
+ hang_dirp(dirhead, tgrp, ep, opt_flags);
+ grp->gr_next = grphead;
+ grphead = tgrp;
+ } else {
+ hang_dirp(dirhead, (struct grouplist *)NULL, ep,
+ opt_flags);
+ free_grp(grp);
+ }
+ dirhead = (struct dirlist *)NULL;
+ if ((ep->ex_flag & EX_LINKED) == 0) {
+ ep2 = exphead;
+ epp = &exphead;
+
+ /*
+ * Insert in the list in alphabetical order.
+ */
+ while (ep2 && strcmp(ep2->ex_fsdir, ep->ex_fsdir) < 0) {
+ epp = &ep2->ex_next;
+ ep2 = ep2->ex_next;
+ }
+ if (ep2)
+ ep->ex_next = ep2;
+ *epp = ep;
+ ep->ex_flag |= EX_LINKED;
+ }
+nextline:
+ if (dirhead) {
+ free_dir(dirhead);
+ dirhead = (struct dirlist *)NULL;
+ }
+ }
+ fclose(exp_file);
+}
+
+/*
+ * Allocate an export list element
+ */
+struct exportlist *
+get_exp()
+{
+ struct exportlist *ep;
+
+ ep = (struct exportlist *)malloc(sizeof (struct exportlist));
+ if (ep == (struct exportlist *)NULL)
+ out_of_mem();
+ memset(ep, 0, sizeof(struct exportlist));
+ return (ep);
+}
+
+/*
+ * Allocate a group list element
+ */
+struct grouplist *
+get_grp()
+{
+ struct grouplist *gp;
+
+ gp = (struct grouplist *)malloc(sizeof (struct grouplist));
+ if (gp == (struct grouplist *)NULL)
+ out_of_mem();
+ memset(gp, 0, sizeof(struct grouplist));
+ return (gp);
+}
+
+/*
+ * Clean up upon an error in get_exportlist().
+ */
+void
+getexp_err(ep, grp)
+ struct exportlist *ep;
+ struct grouplist *grp;
+{
+ struct grouplist *tgrp;
+
+ syslog(LOG_ERR, "bad exports list line %s", line);
+ if (ep && (ep->ex_flag & EX_LINKED) == 0)
+ free_exp(ep);
+ while (grp) {
+ tgrp = grp;
+ grp = grp->gr_next;
+ free_grp(tgrp);
+ }
+}
+
+/*
+ * Search the export list for a matching fs.
+ */
+struct exportlist *
+ex_search(fsid)
+ fsid_t *fsid;
+{
+ struct exportlist *ep;
+
+ ep = exphead;
+ while (ep) {
+ if (ep->ex_fs.val[0] == fsid->val[0] &&
+ ep->ex_fs.val[1] == fsid->val[1])
+ return (ep);
+ ep = ep->ex_next;
+ }
+ return (ep);
+}
+
+/*
+ * Add a directory path to the list.
+ */
+char *
+add_expdir(dpp, cp, len)
+ struct dirlist **dpp;
+ char *cp;
+ int len;
+{
+ struct dirlist *dp;
+
+ dp = (struct dirlist *)malloc(sizeof (struct dirlist) + len);
+ if (dp == (struct dirlist *)NULL)
+ out_of_mem();
+ dp->dp_left = *dpp;
+ dp->dp_right = (struct dirlist *)NULL;
+ dp->dp_flag = 0;
+ dp->dp_hosts = (struct hostlist *)NULL;
+ strcpy(dp->dp_dirp, cp);
+ *dpp = dp;
+ return (dp->dp_dirp);
+}
+
+/*
+ * Hang the dir list element off the dirpath binary tree as required
+ * and update the entry for host.
+ */
+void
+hang_dirp(dp, grp, ep, flags)
+ struct dirlist *dp;
+ struct grouplist *grp;
+ struct exportlist *ep;
+ int flags;
+{
+ struct hostlist *hp;
+ struct dirlist *dp2;
+
+ if (flags & OP_ALLDIRS) {
+ if (ep->ex_defdir)
+ free((caddr_t)dp);
+ else
+ ep->ex_defdir = dp;
+ if (grp == (struct grouplist *)NULL) {
+ ep->ex_defdir->dp_flag |= DP_DEFSET;
+ } else while (grp) {
+ hp = get_ht();
+ hp->ht_grp = grp;
+ hp->ht_next = ep->ex_defdir->dp_hosts;
+ ep->ex_defdir->dp_hosts = hp;
+ grp = grp->gr_next;
+ }
+ } else {
+
+ /*
+ * Loop through the directories adding them to the tree.
+ */
+ while (dp) {
+ dp2 = dp->dp_left;
+ add_dlist(&ep->ex_dirl, dp, grp, flags);
+ dp = dp2;
+ }
+ }
+}
+
+/*
+ * Traverse the binary tree either updating a node that is already there
+ * for the new directory or adding the new node.
+ */
+void
+add_dlist(dpp, newdp, grp, flags)
+ struct dirlist **dpp;
+ struct dirlist *newdp;
+ struct grouplist *grp;
+ int flags;
+{
+ struct dirlist *dp;
+ struct hostlist *hp;
+ int cmp;
+
+ dp = *dpp;
+ if (dp) {
+ cmp = strcmp(dp->dp_dirp, newdp->dp_dirp);
+ if (cmp > 0) {
+ add_dlist(&dp->dp_left, newdp, grp, flags);
+ return;
+ } else if (cmp < 0) {
+ add_dlist(&dp->dp_right, newdp, grp, flags);
+ return;
+ } else
+ free((caddr_t)newdp);
+ } else {
+ dp = newdp;
+ dp->dp_left = (struct dirlist *)NULL;
+ *dpp = dp;
+ }
+ if (grp) {
+
+ /*
+ * Hang all of the host(s) off of the directory point.
+ */
+ do {
+ hp = get_ht();
+ hp->ht_grp = grp;
+ hp->ht_next = dp->dp_hosts;
+ dp->dp_hosts = hp;
+ grp = grp->gr_next;
+ } while (grp);
+ } else {
+ dp->dp_flag |= DP_DEFSET;
+ }
+}
+
+/*
+ * Search for a dirpath on the export point.
+ */
+struct dirlist *
+dirp_search(dp, dirp)
+ struct dirlist *dp;
+ char *dirp;
+{
+ int cmp;
+
+ if (dp) {
+ cmp = strcmp(dp->dp_dirp, dirp);
+ if (cmp > 0)
+ return (dirp_search(dp->dp_left, dirp));
+ else if (cmp < 0)
+ return (dirp_search(dp->dp_right, dirp));
+ else
+ return (dp);
+ }
+ return (dp);
+}
+
+/*
+ * Scan for a host match in a directory tree.
+ */
+int
+chk_host(dp, saddr, defsetp, hostsetp)
+ struct dirlist *dp;
+ struct sockaddr *saddr;
+ int *defsetp;
+ int *hostsetp;
+{
+ struct hostlist *hp;
+ struct grouplist *grp;
+ struct addrinfo *ai;
+
+ if (dp) {
+ if (dp->dp_flag & DP_DEFSET)
+ *defsetp = dp->dp_flag;
+ hp = dp->dp_hosts;
+ while (hp) {
+ grp = hp->ht_grp;
+ switch (grp->gr_type) {
+ case GT_HOST:
+ ai = grp->gr_ptr.gt_addrinfo;
+ for (; ai; ai = ai->ai_next) {
+ if (!sacmp(ai->ai_addr, saddr, NULL)) {
+ *hostsetp =
+ (hp->ht_flag | DP_HOSTSET);
+ return (1);
+ }
+ }
+ break;
+ case GT_NET:
+ if (!sacmp(saddr, (struct sockaddr *)
+ &grp->gr_ptr.gt_net.nt_net,
+ (struct sockaddr *)
+ &grp->gr_ptr.gt_net.nt_mask)) {
+ *hostsetp = (hp->ht_flag | DP_HOSTSET);
+ return (1);
+ }
+ break;
+ }
+ hp = hp->ht_next;
+ }
+ }
+ return (0);
+}
+
+/*
+ * Scan tree for a host that matches the address.
+ */
+int
+scan_tree(dp, saddr)
+ struct dirlist *dp;
+ struct sockaddr *saddr;
+{
+ int defset, hostset;
+
+ if (dp) {
+ if (scan_tree(dp->dp_left, saddr))
+ return (1);
+ if (chk_host(dp, saddr, &defset, &hostset))
+ return (1);
+ if (scan_tree(dp->dp_right, saddr))
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Traverse the dirlist tree and free it up.
+ */
+void
+free_dir(dp)
+ struct dirlist *dp;
+{
+
+ if (dp) {
+ free_dir(dp->dp_left);
+ free_dir(dp->dp_right);
+ free_host(dp->dp_hosts);
+ free((caddr_t)dp);
+ }
+}
+
+/*
+ * Parse the option string and update fields.
+ * Option arguments may either be -<option>=<value> or
+ * -<option> <value>
+ */
+int
+do_opt(cpp, endcpp, ep, grp, has_hostp, exflagsp, cr)
+ char **cpp, **endcpp;
+ struct exportlist *ep;
+ struct grouplist *grp;
+ int *has_hostp;
+ int *exflagsp;
+ struct xucred *cr;
+{
+ char *cpoptarg, *cpoptend;
+ char *cp, *endcp, *cpopt, savedc, savedc2;
+ int allflag, usedarg;
+
+ savedc2 = '\0';
+ cpopt = *cpp;
+ cpopt++;
+ cp = *endcpp;
+ savedc = *cp;
+ *cp = '\0';
+ while (cpopt && *cpopt) {
+ allflag = 1;
+ usedarg = -2;
+ if ((cpoptend = strchr(cpopt, ','))) {
+ *cpoptend++ = '\0';
+ if ((cpoptarg = strchr(cpopt, '=')))
+ *cpoptarg++ = '\0';
+ } else {
+ if ((cpoptarg = strchr(cpopt, '=')))
+ *cpoptarg++ = '\0';
+ else {
+ *cp = savedc;
+ nextfield(&cp, &endcp);
+ **endcpp = '\0';
+ if (endcp > cp && *cp != '-') {
+ cpoptarg = cp;
+ savedc2 = *endcp;
+ *endcp = '\0';
+ usedarg = 0;
+ }
+ }
+ }
+ if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) {
+ *exflagsp |= MNT_EXRDONLY;
+ } else if (cpoptarg && (!strcmp(cpopt, "maproot") ||
+ !(allflag = strcmp(cpopt, "mapall")) ||
+ !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) {
+ usedarg++;
+ parsecred(cpoptarg, cr);
+ if (allflag == 0) {
+ *exflagsp |= MNT_EXPORTANON;
+ opt_flags |= OP_MAPALL;
+ } else
+ opt_flags |= OP_MAPROOT;
+ } else if (cpoptarg && (!strcmp(cpopt, "mask") ||
+ !strcmp(cpopt, "m"))) {
+ if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) {
+ syslog(LOG_ERR, "bad mask: %s", cpoptarg);
+ return (1);
+ }
+ usedarg++;
+ opt_flags |= OP_MASK;
+ } else if (cpoptarg && (!strcmp(cpopt, "network") ||
+ !strcmp(cpopt, "n"))) {
+ if (strchr(cpoptarg, '/') != NULL) {
+ if (debug)
+ fprintf(stderr, "setting OP_MASKLEN\n");
+ opt_flags |= OP_MASKLEN;
+ }
+ if (grp->gr_type != GT_NULL) {
+ syslog(LOG_ERR, "network/host conflict");
+ return (1);
+ } else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) {
+ syslog(LOG_ERR, "bad net: %s", cpoptarg);
+ return (1);
+ }
+ grp->gr_type = GT_NET;
+ *has_hostp = 1;
+ usedarg++;
+ opt_flags |= OP_NET;
+ } else if (!strcmp(cpopt, "alldirs")) {
+ opt_flags |= OP_ALLDIRS;
+ } else if (!strcmp(cpopt, "public")) {
+ *exflagsp |= MNT_EXPUBLIC;
+ } else if (!strcmp(cpopt, "webnfs")) {
+ *exflagsp |= (MNT_EXPUBLIC|MNT_EXRDONLY|MNT_EXPORTANON);
+ opt_flags |= OP_MAPALL;
+ } else if (cpoptarg && !strcmp(cpopt, "index")) {
+ ep->ex_indexfile = strdup(cpoptarg);
+ } else {
+ syslog(LOG_ERR, "bad opt %s", cpopt);
+ return (1);
+ }
+ if (usedarg >= 0) {
+ *endcp = savedc2;
+ **endcpp = savedc;
+ if (usedarg > 0) {
+ *cpp = cp;
+ *endcpp = endcp;
+ }
+ return (0);
+ }
+ cpopt = cpoptend;
+ }
+ **endcpp = savedc;
+ return (0);
+}
+
+/*
+ * Translate a character string to the corresponding list of network
+ * addresses for a hostname.
+ */
+int
+get_host(cp, grp, tgrp)
+ char *cp;
+ struct grouplist *grp;
+ struct grouplist *tgrp;
+{
+ struct grouplist *checkgrp;
+ struct addrinfo *ai, *tai, hints;
+ int ecode;
+ char host[NI_MAXHOST];
+
+ if (grp->gr_type != GT_NULL) {
+ syslog(LOG_ERR, "Bad netgroup type for ip host %s", cp);
+ return (1);
+ }
+ memset(&hints, 0, sizeof hints);
+ hints.ai_flags = AI_CANONNAME;
+ hints.ai_protocol = IPPROTO_UDP;
+ ecode = getaddrinfo(cp, NULL, &hints, &ai);
+ if (ecode != 0) {
+ syslog(LOG_ERR,"can't get address info for host %s", cp);
+ return 1;
+ }
+ grp->gr_ptr.gt_addrinfo = ai;
+ while (ai != NULL) {
+ if (ai->ai_canonname == NULL) {
+ if (getnameinfo(ai->ai_addr, ai->ai_addrlen, host,
+ sizeof host, NULL, 0, ninumeric) != 0)
+ strlcpy(host, "?", sizeof(host));
+ ai->ai_canonname = strdup(host);
+ ai->ai_flags |= AI_CANONNAME;
+ }
+ if (debug)
+ fprintf(stderr, "got host %s\n", ai->ai_canonname);
+ /*
+ * Sanity check: make sure we don't already have an entry
+ * for this host in the grouplist.
+ */
+ for (checkgrp = tgrp; checkgrp != NULL;
+ checkgrp = checkgrp->gr_next) {
+ if (checkgrp->gr_type != GT_HOST)
+ continue;
+ for (tai = checkgrp->gr_ptr.gt_addrinfo; tai != NULL;
+ tai = tai->ai_next) {
+ if (sacmp(tai->ai_addr, ai->ai_addr, NULL) != 0)
+ continue;
+ if (debug)
+ fprintf(stderr,
+ "ignoring duplicate host %s\n",
+ ai->ai_canonname);
+ grp->gr_type = GT_IGNORE;
+ return (0);
+ }
+ }
+ ai = ai->ai_next;
+ }
+ grp->gr_type = GT_HOST;
+ return (0);
+}
+
+/*
+ * Free up an exports list component
+ */
+void
+free_exp(ep)
+ struct exportlist *ep;
+{
+
+ if (ep->ex_defdir) {
+ free_host(ep->ex_defdir->dp_hosts);
+ free((caddr_t)ep->ex_defdir);
+ }
+ if (ep->ex_fsdir)
+ free(ep->ex_fsdir);
+ if (ep->ex_indexfile)
+ free(ep->ex_indexfile);
+ free_dir(ep->ex_dirl);
+ free((caddr_t)ep);
+}
+
+/*
+ * Free hosts.
+ */
+void
+free_host(hp)
+ struct hostlist *hp;
+{
+ struct hostlist *hp2;
+
+ while (hp) {
+ hp2 = hp;
+ hp = hp->ht_next;
+ free((caddr_t)hp2);
+ }
+}
+
+struct hostlist *
+get_ht()
+{
+ struct hostlist *hp;
+
+ hp = (struct hostlist *)malloc(sizeof (struct hostlist));
+ if (hp == (struct hostlist *)NULL)
+ out_of_mem();
+ hp->ht_next = (struct hostlist *)NULL;
+ hp->ht_flag = 0;
+ return (hp);
+}
+
+/*
+ * Out of memory, fatal
+ */
+void
+out_of_mem()
+{
+
+ syslog(LOG_ERR, "out of memory");
+ exit(2);
+}
+
+/*
+ * Do the mount syscall with the update flag to push the export info into
+ * the kernel.
+ */
+int
+do_mount(ep, grp, exflags, anoncrp, dirp, dirplen, fsb)
+ struct exportlist *ep;
+ struct grouplist *grp;
+ int exflags;
+ struct xucred *anoncrp;
+ char *dirp;
+ int dirplen;
+ struct statfs *fsb;
+{
+ struct statfs fsb1;
+ struct addrinfo *ai;
+ struct export_args *eap;
+ char *cp = NULL;
+ int done;
+ char savedc = '\0';
+ union {
+ struct ufs_args ua;
+ struct iso_args ia;
+ struct msdosfs_args da;
+ struct ntfs_args na;
+ } args;
+
+ bzero(&args, sizeof args);
+ /* XXX, we assume that all xx_args look like ufs_args. */
+ args.ua.fspec = 0;
+ eap = &args.ua.export;
+
+ eap->ex_flags = exflags;
+ eap->ex_anon = *anoncrp;
+ eap->ex_indexfile = ep->ex_indexfile;
+ if (grp->gr_type == GT_HOST)
+ ai = grp->gr_ptr.gt_addrinfo;
+ else
+ ai = NULL;
+ done = FALSE;
+ while (!done) {
+ switch (grp->gr_type) {
+ case GT_HOST:
+ if (ai->ai_addr->sa_family == AF_INET6 && have_v6 == 0)
+ goto skip;
+ eap->ex_addr = ai->ai_addr;
+ eap->ex_addrlen = ai->ai_addrlen;
+ eap->ex_masklen = 0;
+ break;
+ case GT_NET:
+ if (grp->gr_ptr.gt_net.nt_net.ss_family == AF_INET6 &&
+ have_v6 == 0)
+ goto skip;
+ eap->ex_addr =
+ (struct sockaddr *)&grp->gr_ptr.gt_net.nt_net;
+ eap->ex_addrlen = args.ua.export.ex_addr->sa_len;
+ eap->ex_mask =
+ (struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask;
+ eap->ex_masklen = args.ua.export.ex_mask->sa_len;
+ break;
+ case GT_DEFAULT:
+ eap->ex_addr = NULL;
+ eap->ex_addrlen = 0;
+ eap->ex_mask = NULL;
+ eap->ex_masklen = 0;
+ break;
+ case GT_IGNORE:
+ return(0);
+ break;
+ default:
+ syslog(LOG_ERR, "bad grouptype");
+ if (cp)
+ *cp = savedc;
+ return (1);
+ };
+
+ /*
+ * XXX:
+ * Maybe I should just use the fsb->f_mntonname path instead
+ * of looping back up the dirp to the mount point??
+ * Also, needs to know how to export all types of local
+ * exportable filesystems and not just "ufs".
+ */
+ while (mount(fsb->f_fstypename, dirp,
+ fsb->f_flags | MNT_UPDATE, (caddr_t)&args) < 0) {
+ if (cp)
+ *cp-- = savedc;
+ else
+ cp = dirp + dirplen - 1;
+ if (errno == EPERM) {
+ if (debug)
+ warnx("can't change attributes for %s",
+ dirp);
+ syslog(LOG_ERR,
+ "can't change attributes for %s", dirp);
+ return (1);
+ }
+ if (opt_flags & OP_ALLDIRS) {
+ syslog(LOG_ERR, "could not remount %s: %m",
+ dirp);
+ return (1);
+ }
+ /* back up over the last component */
+ while (*cp == '/' && cp > dirp)
+ cp--;
+ while (*(cp - 1) != '/' && cp > dirp)
+ cp--;
+ if (cp == dirp) {
+ if (debug)
+ warnx("mnt unsucc");
+ syslog(LOG_ERR, "can't export %s", dirp);
+ return (1);
+ }
+ savedc = *cp;
+ *cp = '\0';
+ /* Check that we're still on the same filesystem. */
+ if (statfs(dirp, &fsb1) != 0 || bcmp(&fsb1.f_fsid,
+ &fsb->f_fsid, sizeof(fsb1.f_fsid)) != 0) {
+ *cp = savedc;
+ syslog(LOG_ERR, "can't export %s", dirp);
+ return (1);
+ }
+ }
+skip:
+ if (ai != NULL)
+ ai = ai->ai_next;
+ if (ai == NULL)
+ done = TRUE;
+ }
+ if (cp)
+ *cp = savedc;
+ return (0);
+}
+
+/*
+ * Translate a net address.
+ *
+ * If `maskflg' is nonzero, then `cp' is a netmask, not a network address.
+ */
+int
+get_net(cp, net, maskflg)
+ char *cp;
+ struct netmsk *net;
+ int maskflg;
+{
+ struct netent *np = NULL;
+ char *name, *p, *prefp;
+ struct sockaddr_in sin;
+ struct sockaddr *sa = NULL;
+ struct addrinfo hints, *ai = NULL;
+ char netname[NI_MAXHOST];
+ long preflen;
+
+ p = prefp = NULL;
+ if ((opt_flags & OP_MASKLEN) && !maskflg) {
+ p = strchr(cp, '/');
+ *p = '\0';
+ prefp = p + 1;
+ }
+
+ /*
+ * Check for a numeric address first. We wish to avoid
+ * possible DNS lookups in getnetbyname().
+ */
+ if (isxdigit(*cp) || *cp == ':') {
+ memset(&hints, 0, sizeof hints);
+ /* Ensure the mask and the network have the same family. */
+ if (maskflg && (opt_flags & OP_NET))
+ hints.ai_family = net->nt_net.ss_family;
+ else if (!maskflg && (opt_flags & OP_HAVEMASK))
+ hints.ai_family = net->nt_mask.ss_family;
+ else
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_flags = AI_NUMERICHOST;
+ if (getaddrinfo(cp, NULL, &hints, &ai) == 0)
+ sa = ai->ai_addr;
+ if (sa != NULL && ai->ai_family == AF_INET) {
+ /*
+ * The address in `cp' is really a network address, so
+ * use inet_network() to re-interpret this correctly.
+ * e.g. "127.1" means 127.1.0.0, not 127.0.0.1.
+ */
+ bzero(&sin, sizeof sin);
+ sin.sin_family = AF_INET;
+ sin.sin_len = sizeof sin;
+ sin.sin_addr = inet_makeaddr(inet_network(cp), 0);
+ if (debug)
+ fprintf(stderr, "get_net: v4 addr %s\n",
+ inet_ntoa(sin.sin_addr));
+ sa = (struct sockaddr *)&sin;
+ }
+ }
+ if (sa == NULL && (np = getnetbyname(cp)) != NULL) {
+ bzero(&sin, sizeof sin);
+ sin.sin_family = AF_INET;
+ sin.sin_len = sizeof sin;
+ sin.sin_addr = inet_makeaddr(np->n_net, 0);
+ sa = (struct sockaddr *)&sin;
+ }
+ if (sa == NULL)
+ goto fail;
+
+ if (maskflg) {
+ /* The specified sockaddr is a mask. */
+ if (checkmask(sa) != 0)
+ goto fail;
+ bcopy(sa, &net->nt_mask, sa->sa_len);
+ opt_flags |= OP_HAVEMASK;
+ } else {
+ /* The specified sockaddr is a network address. */
+ bcopy(sa, &net->nt_net, sa->sa_len);
+
+ /* Get a network name for the export list. */
+ if (np) {
+ name = np->n_name;
+ } else if (getnameinfo(sa, sa->sa_len, netname, sizeof netname,
+ NULL, 0, ninumeric) == 0) {
+ name = netname;
+ } else {
+ goto fail;
+ }
+ if ((net->nt_name = strdup(name)) == NULL)
+ out_of_mem();
+
+ /*
+ * Extract a mask from either a "/<masklen>" suffix, or
+ * from the class of an IPv4 address.
+ */
+ if (opt_flags & OP_MASKLEN) {
+ preflen = strtol(prefp, NULL, 10);
+ if (preflen < 0L || preflen == LONG_MAX)
+ goto fail;
+ bcopy(sa, &net->nt_mask, sa->sa_len);
+ if (makemask(&net->nt_mask, (int)preflen) != 0)
+ goto fail;
+ opt_flags |= OP_HAVEMASK;
+ *p = '/';
+ } else if (sa->sa_family == AF_INET &&
+ (opt_flags & OP_MASK) == 0) {
+ in_addr_t addr;
+
+ addr = ((struct sockaddr_in *)sa)->sin_addr.s_addr;
+ if (IN_CLASSA(addr))
+ preflen = 8;
+ else if (IN_CLASSB(addr))
+ preflen = 16;
+ else if (IN_CLASSC(addr))
+ preflen = 24;
+ else if (IN_CLASSD(addr))
+ preflen = 28;
+ else
+ preflen = 32; /* XXX */
+
+ bcopy(sa, &net->nt_mask, sa->sa_len);
+ makemask(&net->nt_mask, (int)preflen);
+ opt_flags |= OP_HAVEMASK;
+ }
+ }
+
+ if (ai)
+ freeaddrinfo(ai);
+ return 0;
+
+fail:
+ if (ai)
+ freeaddrinfo(ai);
+ return 1;
+}
+
+/*
+ * Parse out the next white space separated field
+ */
+void
+nextfield(cp, endcp)
+ char **cp;
+ char **endcp;
+{
+ char *p;
+
+ p = *cp;
+ while (*p == ' ' || *p == '\t')
+ p++;
+ if (*p == '\n' || *p == '\0')
+ *cp = *endcp = p;
+ else {
+ *cp = p++;
+ while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
+ p++;
+ *endcp = p;
+ }
+}
+
+/*
+ * Get an exports file line. Skip over blank lines and handle line
+ * continuations.
+ */
+int
+get_line()
+{
+ char *p, *cp;
+ size_t len;
+ int totlen, cont_line;
+
+ /*
+ * Loop around ignoring blank lines and getting all continuation lines.
+ */
+ p = line;
+ totlen = 0;
+ do {
+ if ((p = fgetln(exp_file, &len)) == NULL)
+ return (0);
+ cp = p + len - 1;
+ cont_line = 0;
+ while (cp >= p &&
+ (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\\')) {
+ if (*cp == '\\')
+ cont_line = 1;
+ cp--;
+ len--;
+ }
+ if (cont_line) {
+ *++cp = ' ';
+ len++;
+ }
+ if (linesize < len + totlen + 1) {
+ linesize = len + totlen + 1;
+ line = realloc(line, linesize);
+ if (line == NULL)
+ out_of_mem();
+ }
+ memcpy(line + totlen, p, len);
+ totlen += len;
+ line[totlen] = '\0';
+ } while (totlen == 0 || cont_line);
+ return (1);
+}
+
+/*
+ * Parse a description of a credential.
+ */
+void
+parsecred(namelist, cr)
+ char *namelist;
+ struct xucred *cr;
+{
+ char *name;
+ int cnt;
+ char *names;
+ struct passwd *pw;
+ struct group *gr;
+ int ngroups, groups[NGROUPS + 1];
+
+ cr->cr_version = XUCRED_VERSION;
+ /*
+ * Set up the unprivileged user.
+ */
+ cr->cr_uid = -2;
+ cr->cr_groups[0] = -2;
+ cr->cr_ngroups = 1;
+ /*
+ * Get the user's password table entry.
+ */
+ names = strsep(&namelist, " \t\n");
+ name = strsep(&names, ":");
+ if (isdigit(*name) || *name == '-')
+ pw = getpwuid(atoi(name));
+ else
+ pw = getpwnam(name);
+ /*
+ * Credentials specified as those of a user.
+ */
+ if (names == NULL) {
+ if (pw == NULL) {
+ syslog(LOG_ERR, "unknown user: %s", name);
+ return;
+ }
+ cr->cr_uid = pw->pw_uid;
+ ngroups = NGROUPS + 1;
+ if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups))
+ syslog(LOG_ERR, "too many groups");
+ /*
+ * Convert from int's to gid_t's and compress out duplicate
+ */
+ cr->cr_ngroups = ngroups - 1;
+ cr->cr_groups[0] = groups[0];
+ for (cnt = 2; cnt < ngroups; cnt++)
+ cr->cr_groups[cnt - 1] = groups[cnt];
+ return;
+ }
+ /*
+ * Explicit credential specified as a colon separated list:
+ * uid:gid:gid:...
+ */
+ if (pw != NULL)
+ cr->cr_uid = pw->pw_uid;
+ else if (isdigit(*name) || *name == '-')
+ cr->cr_uid = atoi(name);
+ else {
+ syslog(LOG_ERR, "unknown user: %s", name);
+ return;
+ }
+ cr->cr_ngroups = 0;
+ while (names != NULL && *names != '\0' && cr->cr_ngroups < NGROUPS) {
+ name = strsep(&names, ":");
+ if (isdigit(*name) || *name == '-') {
+ cr->cr_groups[cr->cr_ngroups++] = atoi(name);
+ } else {
+ if ((gr = getgrnam(name)) == NULL) {
+ syslog(LOG_ERR, "unknown group: %s", name);
+ continue;
+ }
+ cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid;
+ }
+ }
+ if (names != NULL && *names != '\0' && cr->cr_ngroups == NGROUPS)
+ syslog(LOG_ERR, "too many groups");
+}
+
+#define STRSIZ (RPCMNT_NAMELEN+RPCMNT_PATHLEN+50)
+/*
+ * Routines that maintain the remote mounttab
+ */
+void
+get_mountlist()
+{
+ struct mountlist *mlp, **mlpp;
+ char *host, *dirp, *cp;
+ char str[STRSIZ];
+ FILE *mlfile;
+
+ if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) {
+ if (errno == ENOENT)
+ return;
+ else {
+ syslog(LOG_ERR, "can't open %s", _PATH_RMOUNTLIST);
+ return;
+ }
+ }
+ mlpp = &mlhead;
+ while (fgets(str, STRSIZ, mlfile) != NULL) {
+ cp = str;
+ host = strsep(&cp, " \t\n");
+ dirp = strsep(&cp, " \t\n");
+ if (host == NULL || dirp == NULL)
+ continue;
+ mlp = (struct mountlist *)malloc(sizeof (*mlp));
+ if (mlp == (struct mountlist *)NULL)
+ out_of_mem();
+ strncpy(mlp->ml_host, host, RPCMNT_NAMELEN);
+ mlp->ml_host[RPCMNT_NAMELEN] = '\0';
+ strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN);
+ mlp->ml_dirp[RPCMNT_PATHLEN] = '\0';
+ mlp->ml_next = (struct mountlist *)NULL;
+ *mlpp = mlp;
+ mlpp = &mlp->ml_next;
+ }
+ fclose(mlfile);
+}
+
+void
+del_mlist(char *hostp, char *dirp)
+{
+ struct mountlist *mlp, **mlpp;
+ struct mountlist *mlp2;
+ FILE *mlfile;
+ int fnd = 0;
+
+ mlpp = &mlhead;
+ mlp = mlhead;
+ while (mlp) {
+ if (!strcmp(mlp->ml_host, hostp) &&
+ (!dirp || !strcmp(mlp->ml_dirp, dirp))) {
+ fnd = 1;
+ mlp2 = mlp;
+ *mlpp = mlp = mlp->ml_next;
+ free((caddr_t)mlp2);
+ } else {
+ mlpp = &mlp->ml_next;
+ mlp = mlp->ml_next;
+ }
+ }
+ if (fnd) {
+ if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) {
+ syslog(LOG_ERR,"can't update %s", _PATH_RMOUNTLIST);
+ return;
+ }
+ mlp = mlhead;
+ while (mlp) {
+ fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
+ mlp = mlp->ml_next;
+ }
+ fclose(mlfile);
+ }
+}
+
+void
+add_mlist(hostp, dirp)
+ char *hostp, *dirp;
+{
+ struct mountlist *mlp, **mlpp;
+ FILE *mlfile;
+
+ mlpp = &mlhead;
+ mlp = mlhead;
+ while (mlp) {
+ if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp))
+ return;
+ mlpp = &mlp->ml_next;
+ mlp = mlp->ml_next;
+ }
+ mlp = (struct mountlist *)malloc(sizeof (*mlp));
+ if (mlp == (struct mountlist *)NULL)
+ out_of_mem();
+ strncpy(mlp->ml_host, hostp, RPCMNT_NAMELEN);
+ mlp->ml_host[RPCMNT_NAMELEN] = '\0';
+ strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN);
+ mlp->ml_dirp[RPCMNT_PATHLEN] = '\0';
+ mlp->ml_next = (struct mountlist *)NULL;
+ *mlpp = mlp;
+ if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) {
+ syslog(LOG_ERR, "can't update %s", _PATH_RMOUNTLIST);
+ return;
+ }
+ fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
+ fclose(mlfile);
+}
+
+/*
+ * Free up a group list.
+ */
+void
+free_grp(grp)
+ struct grouplist *grp;
+{
+ if (grp->gr_type == GT_HOST) {
+ if (grp->gr_ptr.gt_addrinfo != NULL)
+ freeaddrinfo(grp->gr_ptr.gt_addrinfo);
+ } else if (grp->gr_type == GT_NET) {
+ if (grp->gr_ptr.gt_net.nt_name)
+ free(grp->gr_ptr.gt_net.nt_name);
+ }
+ free((caddr_t)grp);
+}
+
+#ifdef DEBUG
+void
+SYSLOG(int pri, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+}
+#endif /* DEBUG */
+
+/*
+ * Check options for consistency.
+ */
+int
+check_options(dp)
+ struct dirlist *dp;
+{
+
+ if (dp == (struct dirlist *)NULL)
+ return (1);
+ if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL)) {
+ syslog(LOG_ERR, "-mapall and -maproot mutually exclusive");
+ return (1);
+ }
+ if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) {
+ syslog(LOG_ERR, "-mask requires -network");
+ return (1);
+ }
+ if ((opt_flags & OP_NET) && (opt_flags & OP_HAVEMASK) == 0) {
+ syslog(LOG_ERR, "-network requires mask specification");
+ return (1);
+ }
+ if ((opt_flags & OP_MASK) && (opt_flags & OP_MASKLEN)) {
+ syslog(LOG_ERR, "-mask and /masklen are mutually exclusive");
+ return (1);
+ }
+ if ((opt_flags & OP_ALLDIRS) && dp->dp_left) {
+ syslog(LOG_ERR, "-alldirs has multiple directories");
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Check an absolute directory path for any symbolic links. Return true
+ */
+int
+check_dirpath(dirp)
+ char *dirp;
+{
+ char *cp;
+ int ret = 1;
+ struct stat sb;
+
+ cp = dirp + 1;
+ while (*cp && ret) {
+ if (*cp == '/') {
+ *cp = '\0';
+ if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
+ ret = 0;
+ *cp = '/';
+ }
+ cp++;
+ }
+ if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
+ ret = 0;
+ return (ret);
+}
+
+/*
+ * Make a netmask according to the specified prefix length. The ss_family
+ * and other non-address fields must be initialised before calling this.
+ */
+int
+makemask(struct sockaddr_storage *ssp, int bitlen)
+{
+ u_char *p;
+ int bits, i, len;
+
+ if ((p = sa_rawaddr((struct sockaddr *)ssp, &len)) == NULL)
+ return (-1);
+ if (bitlen > len * NBBY)
+ return (-1);
+
+ for (i = 0; i < len; i++) {
+ bits = (bitlen > NBBY) ? NBBY : bitlen;
+ *p++ = (1 << bits) - 1;
+ bitlen -= bits;
+ }
+ return 0;
+}
+
+/*
+ * Check that the sockaddr is a valid netmask. Returns 0 if the mask
+ * is acceptable (i.e. of the form 1...10....0).
+ */
+int
+checkmask(struct sockaddr *sa)
+{
+ u_char *mask;
+ int i, len;
+
+ if ((mask = sa_rawaddr(sa, &len)) == NULL)
+ return (-1);
+
+ for (i = 0; i < len; i++)
+ if (mask[i] != 0xff)
+ break;
+ if (i < len) {
+ if (~mask[i] & (u_char)(~mask[i] + 1))
+ return (-1);
+ i++;
+ }
+ for (; i < len; i++)
+ if (mask[i] != 0)
+ return (-1);
+ return (0);
+}
+
+/*
+ * Compare two sockaddrs according to a specified mask. Return zero if
+ * `sa1' matches `sa2' when filtered by the netmask in `samask'.
+ * If samask is NULL, perform a full comparision.
+ */
+int
+sacmp(struct sockaddr *sa1, struct sockaddr *sa2, struct sockaddr *samask)
+{
+ unsigned char *p1, *p2, *mask;
+ int len, i;
+
+ if (sa1->sa_family != sa2->sa_family ||
+ (p1 = sa_rawaddr(sa1, &len)) == NULL ||
+ (p2 = sa_rawaddr(sa2, NULL)) == NULL)
+ return (1);
+
+ switch (sa1->sa_family) {
+ case AF_INET6:
+ if (((struct sockaddr_in6 *)sa1)->sin6_scope_id !=
+ ((struct sockaddr_in6 *)sa2)->sin6_scope_id)
+ return (1);
+ break;
+ }
+
+ /* Simple binary comparison if no mask specified. */
+ if (samask == NULL)
+ return (memcmp(p1, p2, len));
+
+ /* Set up the mask, and do a mask-based comparison. */
+ if (sa1->sa_family != samask->sa_family ||
+ (mask = sa_rawaddr(samask, NULL)) == NULL)
+ return (1);
+
+ for (i = 0; i < len; i++)
+ if ((p1[i] & mask[i]) != (p2[i] & mask[i]))
+ return (1);
+ return (0);
+}
+
+/*
+ * Return a pointer to the part of the sockaddr that contains the
+ * raw address, and set *nbytes to its length in bytes. Returns
+ * NULL if the address family is unknown.
+ */
+void *
+sa_rawaddr(struct sockaddr *sa, int *nbytes) {
+ void *p;
+ int len;
+
+ switch (sa->sa_family) {
+ case AF_INET:
+ len = sizeof(((struct sockaddr_in *)sa)->sin_addr);
+ p = &((struct sockaddr_in *)sa)->sin_addr;
+ break;
+ case AF_INET6:
+ len = sizeof(((struct sockaddr_in6 *)sa)->sin6_addr);
+ p = &((struct sockaddr_in6 *)sa)->sin6_addr;
+ break;
+ default:
+ p = NULL;
+ len = 0;
+ }
+
+ if (nbytes != NULL)
+ *nbytes = len;
+ return (p);
+}
+
+void
+huphandler(int sig)
+{
+ got_sighup = 1;
+}
+
+void terminate(sig)
+int sig;
+{
+ close(mountdlockfd);
+ unlink(MOUNTDLOCK);
+ rpcb_unset(RPCPROG_MNT, RPCMNT_VER1, NULL);
+ rpcb_unset(RPCPROG_MNT, RPCMNT_VER3, NULL);
+ exit (0);
+}
diff --git a/usr.sbin/mountd/netgroup.5 b/usr.sbin/mountd/netgroup.5
new file mode 100644
index 0000000..ba58b2c
--- /dev/null
+++ b/usr.sbin/mountd/netgroup.5
@@ -0,0 +1,194 @@
+.\" Copyright (c) 1992, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)netgroup.5 8.2 (Berkeley) 12/11/93
+.\" $FreeBSD$
+.\"
+.Dd December 11, 1993
+.Dt NETGROUP 5
+.Os
+.Sh NAME
+.Nm netgroup
+.Nd defines network groups
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+The
+.Nm
+file
+specifies ``netgroups'', which are sets of
+.Sy (host, user, domain)
+tuples that are to be given similar network access.
+.Pp
+Each line in the file
+consists of a netgroup name followed by a list of the members of the
+netgroup.
+Each member can be either the name of another netgroup or a specification
+of a tuple as follows:
+.Bd -literal -offset indent
+(host, user, domain)
+.Ed
+.Pp
+where the
+.Sy host ,
+.Sy user ,
+and
+.Sy domain
+are character string names for the corresponding component.
+Any of the comma separated fields may be empty to specify a ``wildcard'' value
+or may consist of the string ``-'' to specify ``no valid value''.
+The members of the list may be separated by whitespace and/or commas;
+the ``\e'' character may be used at the end of a line to specify
+line continuation.
+Lines are limited to 1024 characters.
+The functions specified in
+.Xr getnetgrent 3
+should normally be used to access the
+.Nm
+database.
+.Pp
+Lines that begin with a # are treated as comments.
+.Sh NIS/YP INTERACTION
+On most other platforms,
+.Nm Ns s
+are only used in conjunction with
+.Tn NIS
+and local
+.Pa /etc/netgroup
+files are ignored.
+With
+.Fx ,
+.Nm Ns s
+can be used with either
+.Tn NIS
+or local files, but there are certain
+caveats to consider.
+The existing
+.Nm
+system is extremely inefficient where
+.Fn innetgr 3
+lookups are concerned since
+.Nm
+memberships are computed on the fly.
+By contrast, the
+.Tn NIS
+.Nm
+database consists of three separate maps (netgroup, netgroup.byuser
+and netgroup.byhost) that are keyed to allow
+.Fn innetgr 3
+lookups to be done quickly.
+The
+.Fx
+.Nm
+system can interact with the
+.Tn NIS
+.Nm
+maps in the following ways:
+.Bl -bullet -offset indent
+.It
+If the
+.Pa /etc/netgroup
+file does not exist, or it exists and is empty, or
+it exists and contains only a
+.Sq + ,
+and
+.Tn NIS
+is running,
+.Nm
+lookups will be done exclusively through
+.Tn NIS ,
+with
+.Fn innetgr 3
+taking advantage of the netgroup.byuser and
+netgroup.byhost maps to speed up searches.
+(This
+is more or less compatible with the behavior of SunOS and
+similar platforms.)
+.It
+If the
+.Pa /etc/netgroup
+exists and contains only local
+.Nm
+information (with no
+.Tn NIS
+.Sq +
+token), then only the local
+.Nm
+information will be processed (and
+.Tn NIS
+will be ignored).
+.It
+If
+.Pa /etc/netgroup
+exists and contains both local netgroup data
+.Pa and
+the
+.Tn NIS
+.Sq +
+token, the local data and the
+.Tn NIS
+netgroup
+map will be processed as a single combined
+.Nm
+database.
+While this configuration is the most flexible, it
+is also the least efficient: in particular,
+.Fn innetgr 3
+lookups will be especially slow if the
+database is large.
+.El
+.Sh FILES
+.Bl -tag -width /etc/netgroup -compact
+.It Pa /etc/netgroup
+the netgroup database
+.El
+.Sh SEE ALSO
+.Xr getnetgrent 3 ,
+.Xr exports 5
+.Sh COMPATIBILITY
+The file format is compatible with that of various vendors, however it
+appears that not all vendors use an identical format.
+.Sh BUGS
+The interpretation of access restrictions based on the member tuples of a
+netgroup is left up to the various network applications.
+Also, it is not obvious how the domain specification
+applies to the
+.Bx
+environment.
+.Pp
+The
+.Nm
+database should be stored in the form of a
+hashed
+.Xr db 3
+database just like the
+.Xr passwd 5
+database to speed up reverse lookups.
diff --git a/usr.sbin/mountd/pathnames.h b/usr.sbin/mountd/pathnames.h
new file mode 100644
index 0000000..aa1c555
--- /dev/null
+++ b/usr.sbin/mountd/pathnames.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/5/93
+ */
+#include <paths.h>
+
+#define _PATH_EXPORTS "/etc/exports"
+#define _PATH_RMOUNTLIST "/var/db/mountdtab"
+#define _PATH_MOUNTDPID "/var/run/mountd.pid"
diff --git a/usr.sbin/moused/Makefile b/usr.sbin/moused/Makefile
new file mode 100644
index 0000000..c9c1b31
--- /dev/null
+++ b/usr.sbin/moused/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PROG= moused
+MAN= moused.8
+
+#BINMODE=4555
+#INSTALLFLAGS=-fschg
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/moused/moused.8 b/usr.sbin/moused/moused.8
new file mode 100644
index 0000000..2b0bc3b
--- /dev/null
+++ b/usr.sbin/moused/moused.8
@@ -0,0 +1,679 @@
+.\" Copyright (c) 1996
+.\" Mike Pritchard <mpp@FreeBSD.org>. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Mike Pritchard.
+.\" 4. Neither the name of the author nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd April 1, 2000
+.Dt MOUSED 8
+.Os
+.Sh NAME
+.Nm moused
+.Nd pass mouse data to the console driver
+.Sh SYNOPSIS
+.Nm
+.Op Fl DPRacdfs
+.Op Fl I Ar file
+.Op Fl F Ar rate
+.Op Fl r Ar resolution
+.Op Fl S Ar baudrate
+.Op Fl a Ar X Ns Op , Ns Ar Y
+.Op Fl C Ar threshold
+.Op Fl m Ar N=M
+.Op Fl w Ar N
+.Op Fl z Ar target
+.Op Fl t Ar mousetype
+.Op Fl 3 Op Fl E Ar timeout
+.Fl p Ar port
+.Pp
+.Nm
+.Op Fl Pd
+.Fl p Ar port
+.Fl i Ar info
+.Sh DESCRIPTION
+The 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
+(see
+.Xr sysmouse 4 ) .
+.Pp
+The mouse daemon listens to the specified port for mouse data,
+interprets and then passes it via ioctls to the console driver.
+The mouse daemon
+reports translation movement, button press/release
+events and movement of the roller or the wheel if available.
+The roller/wheel movement is reported as ``Z'' axis movement.
+.Pp
+The console driver will display the mouse pointer on the screen
+and provide cut and paste functions if the mouse pointer is enabled
+in the virtual console via
+.Xr vidcontrol 1 .
+If
+.Xr sysmouse 4
+is opened by the user program, the console driver also passes the mouse
+data to the device so that the user program will see it.
+.Pp
+If the mouse daemon receives the signal
+.Dv SIGHUP ,
+it will reopen the mouse port and 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
+will not be affected.
+.It Fl D
+Lower DTR on the serial port.
+This option is valid only if
+.Ar mousesystems
+is selected as the protocol type.
+The DTR line may need to be dropped for a 3-button mouse
+to operate in the
+.Ar mousesystems
+mode.
+.It Fl E Ar timeout
+When the third button emulation is enabled
+(see above),
+the
+.Nm
+daemon waits
+.Ar timeout
+msec at most before deciding whether two buttons are being pressed
+simultaneously.
+The default timeout is 100 msec.
+.It Fl F Ar rate
+Set the report rate (reports/sec) of the device if supported.
+.It Fl I Ar file
+Write the process id of the
+.Nm
+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 will not be able to print useful information for the serial mouse.
+.It Fl R
+Lower RTS on the serial port.
+This option is valid only if
+.Ar mousesystems
+is selected as the protocol type by the
+.Fl t
+option below.
+It is often used with the
+.Fl D
+option above.
+Both RTS and DTR lines may need to be dropped for
+a 3-button mouse to operate in the
+.Ar mousesystems
+mode.
+.It Fl S Ar baudrate
+Select the baudrate for the serial port (1200 to 9600).
+Not all serial mice support this option.
+.It Fl a Ar X Ns Op , Ns Ar Y
+Accelerate or decelerate the mouse input.
+This is a linear acceleration only.
+Values less than 1.0 slow down movement, values greater than 1.0 speed it
+up.
+Specifying only one value sets the acceleration for both axes.
+.It Fl c
+Some mice report middle button down events
+as if the left and right buttons are being pressed.
+This option handles this.
+.It Fl d
+Enable debugging messages.
+.It Fl f
+Do not become a daemon and instead run as a foreground process.
+Useful for testing and debugging.
+.It Fl i Ar info
+Print specified information and quit. Available pieces of
+information are:
+.Pp
+.Bl -tag -compact -width modelxxx
+.It Ar port
+Port (device file) name, i.e.\&
+.Pa /dev/cuaa0 ,
+.Pa /dev/mse0
+and
+.Pa /dev/psm0 .
+.It Ar if
+Interface type: serial, bus, inport or ps/2.
+.It Ar type
+Protocol type.
+It is one of the types listed under the
+.Fl t
+option below or
+.Ar sysmouse
+if the driver supports the
+.Ar sysmouse
+data format standard.
+.It Ar model
+Mouse model. The
+.Nm
+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
+(see the
+.Sx "Configuring Mouse Daemon" ) .
+.Pp
+Note that if a protocol type is specified with this option, the
+.Fl P
+option above is implied and Plug and Play COM device enumeration
+procedure will be disabled.
+.Pp
+Also note that if your mouse is attached to the PS/2 mouse port, you should
+always choose
+.Ar auto
+or
+.Ar ps/2 ,
+regardless of the brand and model of the mouse. Likewise, if your
+mouse is attached to the bus mouse port, choose
+.Ar auto
+or
+.Ar busmouse .
+Serial mouse protocols will not work with these mice.
+.Pp
+For the USB mouse, the protocol must be
+.Ar auto .
+No other protocol will work with the USB mouse.
+.Pp
+Valid types for this option are
+listed below.
+.Pp
+For the serial mouse:
+.Bl -tag -compact -width mousesystemsxxx
+.It Ar microsoft
+Microsoft serial mouse protocol. Most 2-button serial mice use this protocol.
+.It Ar intellimouse
+Microsoft IntelliMouse protocol. Genius NetMouse, ASCII Mie Mouse,
+Logitech MouseMan+ and FirstMouse+ use this protocol too.
+Other mice with a roller/wheel may be compatible with this protocol.
+.It Ar mousesystems
+MouseSystems 5-byte protocol. 3-button mice may use this protocol.
+.It Ar mmseries
+MM Series mouse protocol.
+.It Ar logitech
+Logitech mouse protocol. Note that this is for old Logitech models.
+.Ar mouseman
+or
+.Ar intellimouse
+should be specified for newer models.
+.It Ar mouseman
+Logitech MouseMan and TrackMan protocol. Some 3-button mice may be compatible
+with this protocol. Note that MouseMan+ and FirstMouse+ use
+.Ar intellimouse
+protocol rather than this one.
+.It Ar glidepoint
+ALPS GlidePoint protocol.
+.It Ar thinkingmouse
+Kensington ThinkingMouse protocol.
+.It Ar mmhitab
+Hitachi tablet protocol.
+.It Ar x10mouseremote
+X10 MouseRemote.
+.It Ar kidspad
+Genius Kidspad and Easypad protocol.
+.It Ar versapad
+Interlink VersaPad protocol.
+.El
+.Pp
+For the bus and InPort mouse:
+.Bl -tag -compact -width mousesystemsxxx
+.It Ar busmouse
+This is the only protocol type available for
+the bus and InPort mouse and should be specified for any bus mice
+and InPort mice, regardless of the brand.
+.El
+.Pp
+For the PS/2 mouse:
+.Bl -tag -compact -width mousesystemsxxx
+.It Ar ps/2
+This is the only protocol type available for the PS/2 mouse
+and should be specified for any PS/2 mice, regardless of the brand.
+.El
+.Pp
+For the USB mouse,
+.Ar auto
+is the only protocol type available for the USB mouse
+and should be specified for any USB mice, regardless of the brand.
+.It Fl w Ar N
+Make the physical button
+.Ar N
+act as the wheel mode button.
+While this button is pressed, X and Y axis movement is reported to be zero
+and the Y axis movement is mapped to Z axis.
+You may further map the Z axis movement to virtual buttons by the
+.Fl z
+option below.
+.It Fl z Ar target
+Map Z axis (roller/wheel) movement to another axis or to virtual buttons.
+Valid
+.Ar target
+maybe:
+.Bl -tag -compact -width x__
+.It Ar x
+.It Ar y
+X or Y axis movement will be reported when the Z axis movement is detected.
+.It Ar N
+Report down events for the virtual buttons
+.Ar N
+and
+.Ar N+1
+respectively when negative and positive Z axis movement
+is detected.
+There do not need to be physical buttons
+.Ar N
+and
+.Ar N+1 .
+Note that mapping to logical buttons is carried out after mapping
+from the Z axis movement to the virtual buttons is done.
+.It Ar N1 N2
+Report down events for the virtual buttons
+.Ar N1
+and
+.Ar N2
+respectively when negative and positive Z axis movement
+is detected.
+.It Ar N1 N2 N3 N4
+This is useful for the mouse with two wheels of which
+the second wheel is used to generate horizontal scroll action,
+and for the mouse which has a knob or a stick which can detect
+the horizontal force applied by the user.
+.Pp
+The motion of the second wheel will be mapped to the buttons
+.Ar N3 ,
+for the negative direction, and
+.Ar N4 ,
+for the positive direction.
+If the buttons
+.Ar N3
+and
+.Ar N4
+actually exist in this mouse, their actions will not be detected.
+.Pp
+Note that horizontal movement or second roller/wheel movement may not
+always be detected,
+because there appears to be no accepted standard as to how it is encoded.
+.Pp
+Note also that some mice think left is the negative horizontal direction,
+others may think otherwise.
+Moreover, there are some mice whose two wheels are both mounted vertically,
+and the direction of the second vertical wheel does not match the
+first one's.
+.El
+.El
+.Ss Configuring Mouse Daemon
+The first thing you need to know is the interface type
+of the mouse you are going to use.
+It can be determined by looking at the connector of the mouse.
+The serial mouse has a D-Sub female 9- or 25-pin connector.
+The bus and InPort mice have either a D-Sub male 9-pin connector
+or a round DIN 9-pin connector.
+The PS/2 mouse is equipped with a small, round DIN 6-pin connector.
+Some mice come with adapters with which the connector can
+be converted to another. If you are to use such an adapter,
+remember the connector at the very end of the mouse/adapter pair is
+what matters.
+The USB mouse has a flat rectangular connector.
+.Pp
+The next thing to decide is a port to use for the given interface.
+For the bus, InPort and PS/2 mice, there is little choice:
+the bus and InPort mice always use
+.Pa /dev/mse0 ,
+and the PS/2 mouse is always at
+.Pa /dev/psm0 .
+There may be more than one serial port to which the serial
+mouse can be attached. Many people often assign the first, built-in
+serial port
+.Pa /dev/cuaa0
+to the mouse.
+You can attach multiple USB mice to your system or to your USB hub.
+They are accessible as
+.Pa /dev/ums0 , /dev/ums1 ,
+and so on.
+.Pa
+You may want to create a symbolic link
+.Pa /dev/mouse
+pointing to the real port to which the mouse is connected, so that you
+can easily distinguish which is your ``mouse'' port later.
+.Pp
+The next step is to guess the appropriate protocol type for the mouse.
+The
+.Nm
+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
+(see
+.Sx EXAMPLES ) .
+.Pp
+The command may print
+.Ar sysmouse
+if the mouse driver supports this protocol type.
+.Pp
+Note that the
+.Dv type
+and
+.Dv model
+printed by the
+.Fl i
+option do not necessarily match the product name of the pointing device
+in question, but they may give the name of the device with which it is
+compatible.
+.Pp
+If the
+.Fl i
+option yields nothing, you need to specify a protocol type to the
+.Nm
+command by the
+.Fl t
+option.
+You have to make a guess and try.
+There is rule of thumb:
+.Pp
+.Bl -enum -compact -width 1.X
+.It
+The bus and InPort mice always use
+.Ar busmouse
+protocol regardless of the brand of the mouse.
+.It
+The
+.Ar ps/2
+protocol should always be specified for the PS/2 mouse
+regardless of the brand of the mouse.
+.It
+You must specify the
+.Ar auto
+protocol for the USB mouse.
+.It
+Most 2-button serial mice support the
+.Ar microsoft
+protocol.
+.It
+3-button serial mice may work with the
+.Ar mousesystems
+protocol.
+If it does not, it may work with the
+.Ar microsoft
+protocol although
+the third (middle) button will not function.
+3-button serial mice may also work with the
+.Ar mouseman
+protocol under which the third button may function as expected.
+.It
+3-button serial mice may have a small switch to choose between ``MS''
+and ``PC'', or ``2'' and ``3''.
+``MS'' or ``2'' usually mean the
+.Ar microsoft
+protocol.
+``PC'' or ``3'' will choose the
+.Ar mousesystems
+protocol.
+.It
+If the mouse has a roller or a wheel, it may be compatible with the
+.Ar intellimouse
+protocol.
+.El
+.Pp
+To test if the selected protocol type is correct for the given mouse,
+enable the mouse pointer in the current virtual console,
+.Pp
+.Dl vidcontrol -m on
+.Pp
+start the mouse daemon in the foreground mode,
+.Pp
+.Dl moused -f -p Ar _selected_port_ -t Ar _selected_protocol_
+.Pp
+and see if the mouse pointer travels correctly
+according to the mouse movement.
+Then try cut & paste features by
+clicking the left, right and middle buttons.
+Type ^C to stop
+the command.
+.Ss Multiple Mice
+As many instances of the mouse daemon as the number of mice attached to
+the system may be run simultaneously; one
+instance for each mouse.
+This is useful if the user wants to use the built-in PS/2 pointing device
+of a laptop computer while on the road, but wants to use a serial
+mouse when s/he attaches the system to the docking station in the office.
+Run two mouse daemons and tell the application program
+(such as the
+.Tn "X\ Window System" )
+to use
+.Xr sysmouse ,
+then the application program will always see mouse data from either mice.
+When the serial mouse is not attached, the corresponding mouse daemon
+will not detect any movement or button state change and the application
+program will only see mouse data coming from the daemon for the
+PS/2 mouse.
+In contrast when both mice are attached and both of them
+are moved at the same time in this configuration,
+the mouse pointer will travel across the screen just as if movement of
+the mice is combined all together.
+.Sh FILES
+.Bl -tag -width /dev/consolectl -compact
+.It Pa /dev/consolectl
+device to control the console
+.It Pa /dev/mse%d
+bus and InPort mouse driver
+.It Pa /dev/psm%d
+PS/2 mouse driver
+.It Pa /dev/sysmouse
+virtualized mouse driver
+.It Pa /dev/ttyv%d
+virtual consoles
+.It Pa /dev/ums%d
+USB mouse driver
+.It Pa /var/run/moused.pid
+process id of the currently running
+.Nm
+daemon
+.It Pa /var/run/MouseRemote
+UNIX-domain stream socket for X10 MouseRemote events
+.El
+.Sh EXAMPLES
+.Dl moused -p /dev/cuaa0 -i type
+.Pp
+Let the
+.Nm
+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 and Interlink VersaPad models
+treat the tapping action
+as fourth button events.
+Use the option ``-m 1=4'' for these models
+to obtain the same effect as the other pad devices.
+.Pp
+Cut and paste functions in the virtual console assume that there
+are three buttons on the mouse.
+The logical button 1 (logical left) selects a region of text in the
+console and copies it to the cut buffer.
+The logical button 3 (logical right) extends the selected region.
+The logical button 2 (logical middle) pastes the selected text
+at the text cursor position.
+If the mouse has only two buttons, the middle, `paste' button
+is not available.
+To obtain the paste function, use the
+.Fl 3
+option to emulate the middle button, or use the
+.Fl m
+option to assign the physical right button to the logical middle button:
+``-m 2=3''.
+.Sh SEE ALSO
+.Xr kill 1 ,
+.Xr vidcontrol 1 ,
+.Xr keyboard 4 ,
+.Xr mse 4 ,
+.Xr pcvt 4 ,
+.Xr psm 4 ,
+.Xr screen 4 ,
+.Xr sysmouse 4 ,
+.Xr ums 4
+.Sh STANDARDS
+The
+.Nm
+command partially supports
+.Dq Plug and Play External COM Device Specification
+in order to support PnP serial mice.
+However, due to various degrees of conformance to the specification by
+existing serial mice, it does not strictly follow the version 1.0 of the
+standard.
+Even with this less strict approach,
+it may not always determine an appropriate protocol type
+for the given serial mouse.
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+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..78f655e
--- /dev/null
+++ b/usr.sbin/moused/moused.c
@@ -0,0 +1,2976 @@
+/**
+ ** 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[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <ctype.h>
+#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 <sys/mouse.h>
+#include <sys/consio.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#define MAX_CLICKTHRESHOLD 2000 /* 2 seconds */
+#define MAX_BUTTON2TIMEOUT 2000 /* 2 seconds */
+#define DFLT_CLICKTHRESHOLD 500 /* 0.5 second */
+#define DFLT_BUTTON2TIMEOUT 100 /* 0.1 second */
+
+/* Abort 3-button emulation delay after this many movement events. */
+#define BUTTON2_MAXMOVE 3
+
+#define TRUE 1
+#define FALSE 0
+
+#define MOUSE_XAXIS (-1)
+#define MOUSE_YAXIS (-2)
+
+/* Logitech PS2++ protocol */
+#define MOUSE_PS2PLUS_CHECKBITS(b) \
+ ((((b[2] & 0x03) << 2) | 0x02) == (b[1] & 0x0f))
+#define MOUSE_PS2PLUS_PACKET_TYPE(b) \
+ (((b[0] & 0x30) >> 2) | ((b[1] & 0x30) >> 4))
+
+#define ChordMiddle 0x0001
+#define Emulate3Button 0x0002
+#define ClearDTR 0x0004
+#define ClearRTS 0x0008
+#define NoPnP 0x0010
+
+#define ID_NONE 0
+#define ID_PORT 1
+#define ID_IF 2
+#define ID_TYPE 4
+#define ID_MODEL 8
+#define ID_ALL (ID_PORT | ID_IF | ID_TYPE | ID_MODEL)
+
+#define debug(fmt, args...) do { \
+ if (debug && nodaemon) \
+ warnx(fmt, ##args); \
+} while (0)
+
+#define logerr(e, fmt, args...) do { \
+ log_or_warn(LOG_DAEMON | LOG_ERR, errno, fmt, ##args); \
+ exit(e); \
+} while (0)
+
+#define logerrx(e, fmt, args...) do { \
+ log_or_warn(LOG_DAEMON | LOG_ERR, 0, fmt, ##args); \
+ exit(e); \
+} while (0)
+
+#define logwarn(fmt, args...) \
+ log_or_warn(LOG_DAEMON | LOG_WARNING, errno, fmt, ##args)
+
+#define logwarnx(fmt, args...) \
+ log_or_warn(LOG_DAEMON | LOG_WARNING, 0, fmt, ##args)
+
+/* structures */
+
+/* symbol table entry */
+typedef struct {
+ char *name;
+ int val;
+ int val2;
+} symtab_t;
+
+/* serial PnP ID string */
+typedef struct {
+ int revision; /* PnP revision, 100 for 1.00 */
+ char *eisaid; /* EISA ID including mfr ID and product ID */
+ char *serial; /* serial No, optional */
+ char *class; /* device class, optional */
+ char *compat; /* list of compatible drivers, optional */
+ char *description; /* product description, optional */
+ int neisaid; /* length of the above fields... */
+ int nserial;
+ int nclass;
+ int ncompat;
+ int ndescription;
+} pnpid_t;
+
+/* global variables */
+
+int debug = 0;
+int nodaemon = FALSE;
+int background = FALSE;
+int identify = ID_NONE;
+int extioctl = FALSE;
+char *pidfile = "/var/run/moused.pid";
+
+/* local variables */
+
+/* interface (the table must be ordered by MOUSE_IF_XXX in mouse.h) */
+static symtab_t rifs[] = {
+ { "serial", MOUSE_IF_SERIAL },
+ { "bus", MOUSE_IF_BUS },
+ { "inport", MOUSE_IF_INPORT },
+ { "ps/2", MOUSE_IF_PS2 },
+ { "sysmouse", MOUSE_IF_SYSMOUSE },
+ { "usb", MOUSE_IF_USB },
+ { NULL, MOUSE_IF_UNKNOWN },
+};
+
+/* types (the table must be ordered by MOUSE_PROTO_XXX in mouse.h) */
+static char *rnames[] = {
+ "microsoft",
+ "mousesystems",
+ "logitech",
+ "mmseries",
+ "mouseman",
+ "busmouse",
+ "inportmouse",
+ "ps/2",
+ "mmhitab",
+ "glidepoint",
+ "intellimouse",
+ "thinkingmouse",
+ "sysmouse",
+ "x10mouseremote",
+ "kidspad",
+ "versapad",
+ "jogdial",
+#if notyet
+ "mariqua",
+#endif
+ NULL
+};
+
+/* models */
+static symtab_t rmodels[] = {
+ { "NetScroll", MOUSE_MODEL_NETSCROLL },
+ { "NetMouse/NetScroll Optical", MOUSE_MODEL_NET },
+ { "GlidePoint", MOUSE_MODEL_GLIDEPOINT },
+ { "ThinkingMouse", MOUSE_MODEL_THINK },
+ { "IntelliMouse", MOUSE_MODEL_INTELLI },
+ { "EasyScroll/SmartScroll", MOUSE_MODEL_EASYSCROLL },
+ { "MouseMan+", MOUSE_MODEL_MOUSEMANPLUS },
+ { "Kidspad", MOUSE_MODEL_KIDSPAD },
+ { "VersaPad", MOUSE_MODEL_VERSAPAD },
+ { "IntelliMouse Explorer", MOUSE_MODEL_EXPLORER },
+ { "4D Mouse", MOUSE_MODEL_4D },
+ { "4D+ Mouse", MOUSE_MODEL_4DPLUS },
+ { "generic", MOUSE_MODEL_GENERIC },
+ { NULL, MOUSE_MODEL_UNKNOWN },
+};
+
+/* PnP EISA/product IDs */
+static symtab_t pnpprod[] = {
+ /* Kensignton ThinkingMouse */
+ { "KML0001", MOUSE_PROTO_THINK, MOUSE_MODEL_THINK },
+ /* MS IntelliMouse */
+ { "MSH0001", MOUSE_PROTO_INTELLI, MOUSE_MODEL_INTELLI },
+ /* MS IntelliMouse TrackBall */
+ { "MSH0004", MOUSE_PROTO_INTELLI, MOUSE_MODEL_INTELLI },
+ /* Tremon Wheel Mouse MUSD */
+ { "HTK0001", MOUSE_PROTO_INTELLI, MOUSE_MODEL_INTELLI },
+ /* Genius PnP Mouse */
+ { "KYE0001", MOUSE_PROTO_MS, MOUSE_MODEL_GENERIC },
+ /* MouseSystems SmartScroll Mouse (OEM from Genius?) */
+ { "KYE0002", MOUSE_PROTO_MS, MOUSE_MODEL_EASYSCROLL },
+ /* Genius NetMouse */
+ { "KYE0003", MOUSE_PROTO_INTELLI, MOUSE_MODEL_NET },
+ /* Genius Kidspad, Easypad and other tablets */
+ { "KYE0005", MOUSE_PROTO_KIDSPAD, MOUSE_MODEL_KIDSPAD },
+ /* Genius EZScroll */
+ { "KYEEZ00", MOUSE_PROTO_MS, MOUSE_MODEL_EASYSCROLL },
+ /* Logitech Cordless MouseMan Wheel */
+ { "LGI8033", MOUSE_PROTO_INTELLI, MOUSE_MODEL_MOUSEMANPLUS },
+ /* Logitech MouseMan (new 4 button model) */
+ { "LGI800C", MOUSE_PROTO_INTELLI, MOUSE_MODEL_MOUSEMANPLUS },
+ /* Logitech MouseMan+ */
+ { "LGI8050", MOUSE_PROTO_INTELLI, MOUSE_MODEL_MOUSEMANPLUS },
+ /* Logitech FirstMouse+ */
+ { "LGI8051", MOUSE_PROTO_INTELLI, MOUSE_MODEL_MOUSEMANPLUS },
+ /* Logitech serial */
+ { "LGI8001", MOUSE_PROTO_LOGIMOUSEMAN, MOUSE_MODEL_GENERIC },
+ /* A4 Tech 4D/4D+ Mouse */
+ { "A4W0005", MOUSE_PROTO_INTELLI, MOUSE_MODEL_4D },
+ /* 8D Scroll Mouse */
+ { "PEC9802", MOUSE_PROTO_INTELLI, MOUSE_MODEL_INTELLI },
+ /* Mitsumi Wireless Scroll Mouse */
+ { "MTM6401", MOUSE_PROTO_INTELLI, MOUSE_MODEL_INTELLI },
+
+ /* MS bus */
+ { "PNP0F00", MOUSE_PROTO_BUS, MOUSE_MODEL_GENERIC },
+ /* MS serial */
+ { "PNP0F01", MOUSE_PROTO_MS, MOUSE_MODEL_GENERIC },
+ /* MS InPort */
+ { "PNP0F02", MOUSE_PROTO_INPORT, MOUSE_MODEL_GENERIC },
+ /* MS PS/2 */
+ { "PNP0F03", MOUSE_PROTO_PS2, MOUSE_MODEL_GENERIC },
+ /*
+ * EzScroll returns PNP0F04 in the compatible device field; but it
+ * doesn't look compatible... XXX
+ */
+ /* MouseSystems */
+ { "PNP0F04", MOUSE_PROTO_MSC, MOUSE_MODEL_GENERIC },
+ /* MouseSystems */
+ { "PNP0F05", MOUSE_PROTO_MSC, MOUSE_MODEL_GENERIC },
+#if notyet
+ /* Genius Mouse */
+ { "PNP0F06", MOUSE_PROTO_???, 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
+ /* Interlink VersaPad */
+ { "LNK0001", MOUSE_PROTO_VERSAPAD, MOUSE_MODEL_VERSAPAD },
+
+ { NULL, MOUSE_PROTO_UNKNOWN, MOUSE_MODEL_GENERIC },
+};
+
+/* the table must be ordered by MOUSE_PROTO_XXX in mouse.h */
+static unsigned short rodentcflags[] =
+{
+ (CS7 | CREAD | CLOCAL | HUPCL ), /* MicroSoft */
+ (CS8 | CSTOPB | CREAD | CLOCAL | HUPCL ), /* MouseSystems */
+ (CS8 | CSTOPB | CREAD | CLOCAL | HUPCL ), /* Logitech */
+ (CS8 | PARENB | PARODD | CREAD | CLOCAL | HUPCL ), /* MMSeries */
+ (CS7 | CREAD | CLOCAL | HUPCL ), /* MouseMan */
+ 0, /* Bus */
+ 0, /* InPort */
+ 0, /* PS/2 */
+ (CS8 | CREAD | CLOCAL | HUPCL ), /* MM HitTablet */
+ (CS7 | CREAD | CLOCAL | HUPCL ), /* GlidePoint */
+ (CS7 | CREAD | CLOCAL | HUPCL ), /* IntelliMouse */
+ (CS7 | CREAD | CLOCAL | HUPCL ), /* Thinking Mouse */
+ (CS8 | CSTOPB | CREAD | CLOCAL | HUPCL ), /* sysmouse */
+ (CS7 | CREAD | CLOCAL | HUPCL ), /* X10 MouseRemote */
+ (CS8 | PARENB | PARODD | CREAD | CLOCAL | HUPCL ), /* kidspad etc. */
+ (CS8 | CREAD | CLOCAL | HUPCL ), /* VersaPad */
+ 0, /* JogDial */
+#if notyet
+ (CS8 | CSTOPB | CREAD | CLOCAL | HUPCL ), /* Mariqua */
+#endif
+};
+
+static struct rodentparam {
+ int flags;
+ char *portname; /* /dev/XXX */
+ int rtype; /* MOUSE_PROTO_XXX */
+ int level; /* operation level: 0 or greater */
+ int baudrate;
+ int rate; /* report rate */
+ int resolution; /* MOUSE_RES_XXX or a positive number */
+ int zmap[4]; /* MOUSE_{X|Y}AXIS or a button number */
+ int wmode; /* wheel mode button number */
+ int mfd; /* mouse file descriptor */
+ int cfd; /* /dev/consolectl file descriptor */
+ int mremsfd; /* mouse remote server file descriptor */
+ int mremcfd; /* mouse remote client file descriptor */
+ long clickthreshold; /* double click speed in msec */
+ long button2timeout; /* 3 button emulation timeout */
+ mousehw_t hw; /* mouse device hardware information */
+ mousemode_t mode; /* protocol information */
+ float accelx; /* Acceleration in the X axis */
+ float accely; /* Acceleration in the Y axis */
+} rodent = {
+ flags : 0,
+ portname : NULL,
+ rtype : MOUSE_PROTO_UNKNOWN,
+ level : -1,
+ baudrate : 1200,
+ rate : 0,
+ resolution : MOUSE_RES_UNKNOWN,
+ zmap: { 0, 0, 0, 0 },
+ wmode: 0,
+ mfd : -1,
+ cfd : -1,
+ mremsfd : -1,
+ mremcfd : -1,
+ clickthreshold : DFLT_CLICKTHRESHOLD,
+ button2timeout : DFLT_BUTTON2TIMEOUT,
+ accelx : 1.0,
+ accely : 1.0,
+};
+
+/* button status */
+struct button_state {
+ int count; /* 0: up, 1: single click, 2: double click,... */
+ struct timeval tv; /* timestamp on the last button event */
+};
+static struct button_state bstate[MOUSE_MAXBUTTON]; /* button state */
+static struct button_state *mstate[MOUSE_MAXBUTTON];/* mapped button st.*/
+static struct button_state zstate[4]; /* Z/W axis state */
+
+/* state machine for 3 button emulation */
+
+#define S0 0 /* start */
+#define S1 1 /* button 1 delayed down */
+#define S2 2 /* button 3 delayed down */
+#define S3 3 /* both buttons down -> button 2 down */
+#define S4 4 /* button 1 delayed up */
+#define S5 5 /* button 1 down */
+#define S6 6 /* button 3 down */
+#define S7 7 /* both buttons down */
+#define S8 8 /* button 3 delayed up */
+#define S9 9 /* button 1 or 3 up after S3 */
+
+#define A(b1, b3) (((b1) ? 2 : 0) | ((b3) ? 1 : 0))
+#define A_TIMEOUT 4
+#define S_DELAYED(st) (states[st].s[A_TIMEOUT] != (st))
+
+static struct {
+ int s[A_TIMEOUT + 1];
+ int buttons;
+ int mask;
+ int timeout;
+} states[10] = {
+ /* S0 */
+ { { S0, S2, S1, S3, S0 }, 0, ~(MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN), FALSE },
+ /* S1 */
+ { { S4, S2, S1, S3, S5 }, 0, ~MOUSE_BUTTON1DOWN, FALSE },
+ /* S2 */
+ { { S8, S2, S1, S3, S6 }, 0, ~MOUSE_BUTTON3DOWN, FALSE },
+ /* S3 */
+ { { S0, S9, S9, S3, S3 }, MOUSE_BUTTON2DOWN, ~0, FALSE },
+ /* S4 */
+ { { S0, S2, S1, S3, S0 }, MOUSE_BUTTON1DOWN, ~0, TRUE },
+ /* S5 */
+ { { S0, S2, S5, S7, S5 }, MOUSE_BUTTON1DOWN, ~0, FALSE },
+ /* S6 */
+ { { S0, S6, S1, S7, S6 }, MOUSE_BUTTON3DOWN, ~0, FALSE },
+ /* S7 */
+ { { S0, S6, S5, S7, S7 }, MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN, ~0, FALSE },
+ /* S8 */
+ { { S0, S2, S1, S3, S0 }, MOUSE_BUTTON3DOWN, ~0, TRUE },
+ /* S9 */
+ { { S0, S9, S9, S3, S9 }, 0, ~(MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN), FALSE },
+};
+static int mouse_button_state;
+static struct timeval mouse_button_state_tv;
+static int mouse_move_delayed;
+
+static jmp_buf env;
+
+/* function prototypes */
+
+static void moused(void);
+static void hup(int sig);
+static void cleanup(int sig);
+static void usage(void);
+static void log_or_warn(int log_pri, int errnum, const char *fmt, ...)
+ __printflike(3, 4);
+
+static int r_identify(void);
+static char *r_if(int type);
+static char *r_name(int type);
+static char *r_model(int model);
+static void r_init(void);
+static int r_protocol(u_char b, mousestatus_t *act);
+static int r_statetrans(mousestatus_t *a1, mousestatus_t *a2, int trans);
+static int r_installmap(char *arg);
+static void r_map(mousestatus_t *act1, mousestatus_t *act2);
+static void r_timestamp(mousestatus_t *act);
+static int r_timeout(void);
+static void r_click(mousestatus_t *act);
+static void setmousespeed(int old, int new, unsigned cflag);
+
+static int pnpwakeup1(void);
+static int pnpwakeup2(void);
+static int pnpgets(char *buf);
+static int pnpparse(pnpid_t *id, char *buf, int len);
+static symtab_t *pnpproto(pnpid_t *id);
+
+static symtab_t *gettoken(symtab_t *tab, char *s, int len);
+static char *gettokenname(symtab_t *tab, int val);
+
+static void mremote_serversetup();
+static void mremote_clientchg(int add);
+
+static int kidspad(u_char rxc, mousestatus_t *act);
+
+int
+main(int argc, char *argv[])
+{
+ int c;
+ int i;
+ int j;
+
+ for (i = 0; i < MOUSE_MAXBUTTON; ++i)
+ mstate[i] = &bstate[i];
+
+ while((c = getopt(argc,argv,"3C:DE:F:I:PRS:a:cdfhi:l:m:p:r:st:w:z:")) != -1)
+ switch(c) {
+
+ case '3':
+ rodent.flags |= Emulate3Button;
+ break;
+
+ case 'E':
+ rodent.button2timeout = atoi(optarg);
+ if ((rodent.button2timeout < 0) ||
+ (rodent.button2timeout > MAX_BUTTON2TIMEOUT)) {
+ warnx("invalid argument `%s'", optarg);
+ usage();
+ }
+ break;
+
+ case 'a':
+ i = sscanf(optarg, "%f,%f", &rodent.accelx, &rodent.accely);
+ if (i == 0) {
+ warnx("invalid acceleration argument '%s'", optarg);
+ usage();
+ }
+
+ if (i == 1)
+ rodent.accely = rodent.accelx;
+
+ break;
+
+ case 'c':
+ rodent.flags |= ChordMiddle;
+ break;
+
+ case 'd':
+ ++debug;
+ break;
+
+ case 'f':
+ nodaemon = TRUE;
+ break;
+
+ case 'i':
+ if (strcmp(optarg, "all") == 0)
+ identify = ID_ALL;
+ else if (strcmp(optarg, "port") == 0)
+ identify = ID_PORT;
+ else if (strcmp(optarg, "if") == 0)
+ identify = ID_IF;
+ else if (strcmp(optarg, "type") == 0)
+ identify = ID_TYPE;
+ else if (strcmp(optarg, "model") == 0)
+ identify = ID_MODEL;
+ else {
+ warnx("invalid argument `%s'", optarg);
+ usage();
+ }
+ nodaemon = TRUE;
+ break;
+
+ case 'l':
+ rodent.level = atoi(optarg);
+ if ((rodent.level < 0) || (rodent.level > 4)) {
+ warnx("invalid argument `%s'", optarg);
+ usage();
+ }
+ break;
+
+ case 'm':
+ if (!r_installmap(optarg)) {
+ warnx("invalid argument `%s'", optarg);
+ usage();
+ }
+ break;
+
+ case 'p':
+ rodent.portname = optarg;
+ break;
+
+ case 'r':
+ if (strcmp(optarg, "high") == 0)
+ rodent.resolution = MOUSE_RES_HIGH;
+ else if (strcmp(optarg, "medium-high") == 0)
+ rodent.resolution = MOUSE_RES_HIGH;
+ else if (strcmp(optarg, "medium-low") == 0)
+ rodent.resolution = MOUSE_RES_MEDIUMLOW;
+ else if (strcmp(optarg, "low") == 0)
+ rodent.resolution = MOUSE_RES_LOW;
+ else if (strcmp(optarg, "default") == 0)
+ rodent.resolution = MOUSE_RES_DEFAULT;
+ else {
+ rodent.resolution = atoi(optarg);
+ if (rodent.resolution <= 0) {
+ warnx("invalid argument `%s'", optarg);
+ usage();
+ }
+ }
+ break;
+
+ case 's':
+ rodent.baudrate = 9600;
+ break;
+
+ case 'w':
+ i = atoi(optarg);
+ if ((i <= 0) || (i > MOUSE_MAXBUTTON)) {
+ warnx("invalid argument `%s'", optarg);
+ usage();
+ }
+ rodent.wmode = 1 << (i - 1);
+ break;
+
+ case 'z':
+ if (strcmp(optarg, "x") == 0)
+ rodent.zmap[0] = MOUSE_XAXIS;
+ else if (strcmp(optarg, "y") == 0)
+ rodent.zmap[0] = MOUSE_YAXIS;
+ else {
+ i = atoi(optarg);
+ /*
+ * Use button i for negative Z axis movement and
+ * button (i + 1) for positive Z axis movement.
+ */
+ if ((i <= 0) || (i > MOUSE_MAXBUTTON - 1)) {
+ warnx("invalid argument `%s'", optarg);
+ usage();
+ }
+ rodent.zmap[0] = i;
+ rodent.zmap[1] = i + 1;
+ debug("optind: %d, optarg: '%s'", optind, optarg);
+ for (j = 1; j < 4; ++j) {
+ if ((optind >= argc) || !isdigit(*argv[optind]))
+ break;
+ i = atoi(argv[optind]);
+ if ((i <= 0) || (i > MOUSE_MAXBUTTON - 1)) {
+ warnx("invalid argument `%s'", argv[optind]);
+ usage();
+ }
+ rodent.zmap[j] = i;
+ ++optind;
+ }
+ if ((rodent.zmap[2] != 0) && (rodent.zmap[3] == 0))
+ rodent.zmap[3] = rodent.zmap[2] + 1;
+ }
+ break;
+
+ case 'C':
+ rodent.clickthreshold = atoi(optarg);
+ if ((rodent.clickthreshold < 0) ||
+ (rodent.clickthreshold > MAX_CLICKTHRESHOLD)) {
+ warnx("invalid argument `%s'", optarg);
+ usage();
+ }
+ break;
+
+ case 'D':
+ rodent.flags |= ClearDTR;
+ break;
+
+ case 'F':
+ rodent.rate = atoi(optarg);
+ if (rodent.rate <= 0) {
+ warnx("invalid argument `%s'", optarg);
+ usage();
+ }
+ break;
+
+ case 'I':
+ pidfile = optarg;
+ break;
+
+ case 'P':
+ rodent.flags |= NoPnP;
+ break;
+
+ case 'R':
+ rodent.flags |= ClearRTS;
+ break;
+
+ case 'S':
+ rodent.baudrate = atoi(optarg);
+ if (rodent.baudrate <= 0) {
+ warnx("invalid argument `%s'", optarg);
+ usage();
+ }
+ debug("rodent baudrate %d", rodent.baudrate);
+ break;
+
+ case 't':
+ if (strcmp(optarg, "auto") == 0) {
+ rodent.rtype = MOUSE_PROTO_UNKNOWN;
+ rodent.flags &= ~NoPnP;
+ rodent.level = -1;
+ break;
+ }
+ for (i = 0; rnames[i]; i++)
+ if (strcmp(optarg, rnames[i]) == 0) {
+ rodent.rtype = i;
+ rodent.flags |= NoPnP;
+ rodent.level = (i == MOUSE_PROTO_SYSMOUSE) ? 1 : 0;
+ break;
+ }
+ if (rnames[i])
+ break;
+ warnx("no such mouse type `%s'", optarg);
+ usage();
+
+ case 'h':
+ case '?':
+ default:
+ usage();
+ }
+
+ /* fix Z axis mapping */
+ for (i = 0; i < 4; ++i) {
+ if (rodent.zmap[i] > 0) {
+ for (j = 0; j < MOUSE_MAXBUTTON; ++j) {
+ if (mstate[j] == &bstate[rodent.zmap[i] - 1])
+ mstate[j] = &zstate[i];
+ }
+ rodent.zmap[i] = 1 << (rodent.zmap[i] - 1);
+ }
+ }
+
+ /* the default port name */
+ switch(rodent.rtype) {
+
+ case MOUSE_PROTO_INPORT:
+ /* INPORT and BUS are the same... */
+ rodent.rtype = MOUSE_PROTO_BUS;
+ /* 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 action0; /* original mouse action */
+ mousestatus_t action; /* interrim buffer */
+ mousestatus_t action2; /* mapped action */
+ struct timeval timeout;
+ fd_set fds;
+ u_char b;
+ FILE *fp;
+ int flags;
+ int c;
+ int i;
+
+ if ((rodent.cfd = open("/dev/consolectl", O_RDWR, 0)) == -1)
+ logerr(1, "cannot open /dev/consolectl");
+
+ if (!nodaemon && !background) {
+ if (daemon(0, 0)) {
+ logerr(1, "failed to become a daemon");
+ } else {
+ background = TRUE;
+ fp = fopen(pidfile, "w");
+ if (fp != NULL) {
+ fprintf(fp, "%d\n", getpid());
+ fclose(fp);
+ }
+ }
+ }
+
+ /* clear mouse data */
+ bzero(&action0, sizeof(action0));
+ bzero(&action, sizeof(action));
+ bzero(&action2, sizeof(action2));
+ bzero(&mouse, sizeof(mouse));
+ mouse_button_state = S0;
+ gettimeofday(&mouse_button_state_tv, NULL);
+ mouse_move_delayed = 0;
+ for (i = 0; i < MOUSE_MAXBUTTON; ++i) {
+ bstate[i].count = 0;
+ bstate[i].tv = mouse_button_state_tv;
+ }
+ for (i = 0; i < sizeof(zstate)/sizeof(zstate[0]); ++i) {
+ zstate[i].count = 0;
+ zstate[i].tv = mouse_button_state_tv;
+ }
+
+ /* choose which ioctl command to use */
+ mouse.operation = MOUSE_MOTION_EVENT;
+ extioctl = (ioctl(rodent.cfd, CONS_MOUSECTL, &mouse) == 0);
+
+ /* process mouse data */
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 20000; /* 20 msec */
+ for (;;) {
+
+ FD_ZERO(&fds);
+ FD_SET(rodent.mfd, &fds);
+ if (rodent.mremsfd >= 0)
+ FD_SET(rodent.mremsfd, &fds);
+ if (rodent.mremcfd >= 0)
+ FD_SET(rodent.mremcfd, &fds);
+
+ c = select(FD_SETSIZE, &fds, NULL, NULL,
+ (rodent.flags & Emulate3Button) ? &timeout : NULL);
+ if (c < 0) { /* error */
+ logwarn("failed to read from mouse");
+ continue;
+ } else if (c == 0) { /* timeout */
+ /* assert(rodent.flags & Emulate3Button) */
+ action0.button = action0.obutton;
+ action0.dx = action0.dy = action0.dz = 0;
+ action0.flags = flags = 0;
+ if (r_timeout() && r_statetrans(&action0, &action, A_TIMEOUT)) {
+ if (debug > 2)
+ debug("flags:%08x buttons:%08x obuttons:%08x",
+ action.flags, action.button, action.obutton);
+ } else {
+ action0.obutton = action0.button;
+ continue;
+ }
+ } else {
+ /* MouseRemote client connect/disconnect */
+ if ((rodent.mremsfd >= 0) && FD_ISSET(rodent.mremsfd, &fds)) {
+ mremote_clientchg(TRUE);
+ continue;
+ }
+ if ((rodent.mremcfd >= 0) && FD_ISSET(rodent.mremcfd, &fds)) {
+ mremote_clientchg(FALSE);
+ continue;
+ }
+ /* mouse movement */
+ if (read(rodent.mfd, &b, 1) == -1) {
+ if (errno == EWOULDBLOCK)
+ continue;
+ else
+ return;
+ }
+ if ((flags = r_protocol(b, &action0)) == 0)
+ continue;
+ r_timestamp(&action0);
+ r_statetrans(&action0, &action,
+ A(action0.button & MOUSE_BUTTON1DOWN,
+ action0.button & MOUSE_BUTTON3DOWN));
+ debug("flags:%08x buttons:%08x obuttons:%08x", action.flags,
+ action.button, action.obutton);
+ }
+ action0.obutton = action0.button;
+ flags &= MOUSE_POSCHANGED;
+ flags |= action.obutton ^ action.button;
+ action.flags = flags;
+
+ if (flags) { /* handler detected action */
+ r_map(&action, &action2);
+ debug("activity : buttons 0x%08x dx %d dy %d dz %d",
+ action2.button, action2.dx, action2.dy, action2.dz);
+
+ if (extioctl) {
+ r_click(&action2);
+ if (action2.flags & MOUSE_POSCHANGED) {
+ mouse.operation = MOUSE_MOTION_EVENT;
+ mouse.u.data.buttons = action2.button;
+ mouse.u.data.x = action2.dx * rodent.accelx;
+ mouse.u.data.y = action2.dy * rodent.accely;
+ mouse.u.data.z = action2.dz;
+ if (debug < 2)
+ ioctl(rodent.cfd, CONS_MOUSECTL, &mouse);
+ }
+ } else {
+ mouse.operation = MOUSE_ACTION;
+ mouse.u.data.buttons = action2.button;
+ mouse.u.data.x = action2.dx * rodent.accelx;
+ mouse.u.data.y = action2.dy * rodent.accely;
+ mouse.u.data.z = action2.dz;
+ if (debug < 2)
+ ioctl(rodent.cfd, CONS_MOUSECTL, &mouse);
+ }
+
+ /*
+ * If the Z axis movement is mapped to a imaginary physical
+ * button, we need to cook up a corresponding button `up' event
+ * after sending a button `down' event.
+ */
+ if ((rodent.zmap[0] > 0) && (action.dz != 0)) {
+ action.obutton = action.button;
+ action.dx = action.dy = action.dz = 0;
+ r_map(&action, &action2);
+ debug("activity : buttons 0x%08x dx %d dy %d dz %d",
+ action2.button, action2.dx, action2.dy, action2.dz);
+
+ if (extioctl) {
+ r_click(&action2);
+ } else {
+ mouse.operation = MOUSE_ACTION;
+ mouse.u.data.buttons = action2.button;
+ mouse.u.data.x = mouse.u.data.y = mouse.u.data.z = 0;
+ if (debug < 2)
+ ioctl(rodent.cfd, CONS_MOUSECTL, &mouse);
+ }
+ }
+ }
+ }
+ /* NOT REACHED */
+}
+
+static void
+hup(int sig)
+{
+ longjmp(env, 1);
+}
+
+static void
+cleanup(int sig)
+{
+ if (rodent.rtype == MOUSE_PROTO_X10MOUSEREM)
+ unlink(_PATH_MOUSEREMOTE);
+ exit(0);
+}
+
+/**
+ ** usage
+ **
+ ** Complain, and free the CPU for more worthy tasks
+ **/
+static void
+usage(void)
+{
+ fprintf(stderr, "%s\n%s\n%s\n%s\n",
+ "usage: moused [-DRcdfs] [-I file] [-F rate] [-r resolution] [-S baudrate]",
+ " [-a X [,Y]] [-C threshold] [-m N=M] [-w N] [-z N]",
+ " [-t <mousetype>] [-3 [-E timeout]] -p <port>",
+ " moused [-d] -i <port|if|type|model|all> -p <port>");
+ exit(1);
+}
+
+/*
+ * Output an error message to syslog or stderr as appropriate. If
+ * `errnum' is non-zero, append its string form to the message.
+ */
+static void
+log_or_warn(int log_pri, int errnum, const char *fmt, ...)
+{
+ va_list ap;
+ char buf[256];
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ if (errnum) {
+ strlcat(buf, ": ", sizeof(buf));
+ strlcat(buf, strerror(errnum), sizeof(buf));
+ }
+
+ if (background)
+ syslog(log_pri, "%s", buf);
+ else
+ warnx("%s", buf);
+}
+
+/**
+ ** Mouse interface code, courtesy of XFree86 3.1.2.
+ **
+ ** Note: Various bits have been trimmed, and in my shortsighted enthusiasm
+ ** to clean, reformat and rationalise naming, it's quite possible that
+ ** some things in here have been broken.
+ **
+ ** I hope not 8)
+ **
+ ** The following code is derived from a module marked :
+ **/
+
+/* $XConsortium: xf86_Mouse.c,v 1.2 94/10/12 20:33:21 kaleb Exp $ */
+/* $XFree86: xc/programs/Xserver/hw/xfree86/common/xf86_Mouse.c,v 3.2 1995/01/28
+ 17:03:40 dawes Exp $ */
+/*
+ *
+ * Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany.
+ * Copyright 1993 by David Dawes <dawes@physics.su.oz.au>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the names of Thomas Roell and David Dawes not be
+ * used in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission. Thomas Roell
+ * and David Dawes makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * THOMAS ROELL AND DAVID DAWES DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THOMAS ROELL OR DAVID DAWES BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+/**
+ ** GlidePoint support from XFree86 3.2.
+ ** Derived from the module:
+ **/
+
+/* $XFree86: xc/programs/Xserver/hw/xfree86/common/xf86_Mouse.c,v 3.19 1996/10/16 14:40:51 dawes Exp $ */
+/* $XConsortium: xf86_Mouse.c /main/10 1996/01/30 15:16:12 kaleb $ */
+
+/* the following table must be ordered by MOUSE_PROTO_XXX in mouse.h */
+static unsigned char proto[][7] = {
+ /* hd_mask hd_id dp_mask dp_id bytes b4_mask b4_id */
+ { 0x40, 0x40, 0x40, 0x00, 3, ~0x23, 0x00 }, /* MicroSoft */
+ { 0xf8, 0x80, 0x00, 0x00, 5, 0x00, 0xff }, /* MouseSystems */
+ { 0xe0, 0x80, 0x80, 0x00, 3, 0x00, 0xff }, /* Logitech */
+ { 0xe0, 0x80, 0x80, 0x00, 3, 0x00, 0xff }, /* MMSeries */
+ { 0x40, 0x40, 0x40, 0x00, 3, ~0x33, 0x00 }, /* MouseMan */
+ { 0xf8, 0x80, 0x00, 0x00, 5, 0x00, 0xff }, /* Bus */
+ { 0xf8, 0x80, 0x00, 0x00, 5, 0x00, 0xff }, /* InPort */
+ { 0xc0, 0x00, 0x00, 0x00, 3, 0x00, 0xff }, /* PS/2 mouse */
+ { 0xe0, 0x80, 0x80, 0x00, 3, 0x00, 0xff }, /* MM HitTablet */
+ { 0x40, 0x40, 0x40, 0x00, 3, ~0x33, 0x00 }, /* GlidePoint */
+ { 0x40, 0x40, 0x40, 0x00, 3, ~0x3f, 0x00 }, /* IntelliMouse */
+ { 0x40, 0x40, 0x40, 0x00, 3, ~0x33, 0x00 }, /* ThinkingMouse */
+ { 0xf8, 0x80, 0x00, 0x00, 5, 0x00, 0xff }, /* sysmouse */
+ { 0x40, 0x40, 0x40, 0x00, 3, ~0x23, 0x00 }, /* X10 MouseRem */
+ { 0x80, 0x80, 0x00, 0x00, 5, 0x00, 0xff }, /* KIDSPAD */
+ { 0xc3, 0xc0, 0x00, 0x00, 6, 0x00, 0xff }, /* VersaPad */
+ { 0x00, 0x00, 0x00, 0x00, 1, 0x00, 0xff }, /* JogDial */
+#if notyet
+ { 0xf8, 0x80, 0x00, 0x00, 5, ~0x2f, 0x10 }, /* Mariqua */
+#endif
+};
+static unsigned char cur_proto[7];
+
+static int
+r_identify(void)
+{
+ char pnpbuf[256]; /* PnP identifier string may be up to 256 bytes long */
+ pnpid_t pnpid;
+ symtab_t *t;
+ int level;
+ int len;
+
+ /* set the driver operation level, if applicable */
+ if (rodent.level < 0)
+ rodent.level = 1;
+ ioctl(rodent.mfd, MOUSE_SETLEVEL, &rodent.level);
+ rodent.level = (ioctl(rodent.mfd, MOUSE_GETLEVEL, &level) == 0) ? level : 0;
+
+ /*
+ * Interrogate the driver and get some intelligence on the device...
+ * The following ioctl functions are not always supported by device
+ * drivers. When the driver doesn't support them, we just trust the
+ * user to supply valid information.
+ */
+ rodent.hw.iftype = MOUSE_IF_UNKNOWN;
+ rodent.hw.model = MOUSE_MODEL_GENERIC;
+ ioctl(rodent.mfd, MOUSE_GETHWINFO, &rodent.hw);
+
+ if (rodent.rtype != MOUSE_PROTO_UNKNOWN)
+ bcopy(proto[rodent.rtype], cur_proto, sizeof(cur_proto));
+ rodent.mode.protocol = MOUSE_PROTO_UNKNOWN;
+ rodent.mode.rate = -1;
+ rodent.mode.resolution = MOUSE_RES_UNKNOWN;
+ rodent.mode.accelfactor = 0;
+ rodent.mode.level = 0;
+ if (ioctl(rodent.mfd, MOUSE_GETMODE, &rodent.mode) == 0) {
+ if ((rodent.mode.protocol == MOUSE_PROTO_UNKNOWN)
+ || (rodent.mode.protocol >= sizeof(proto)/sizeof(proto[0]))) {
+ logwarnx("unknown mouse protocol (%d)", rodent.mode.protocol);
+ return MOUSE_PROTO_UNKNOWN;
+ } else {
+ /* INPORT and BUS are the same... */
+ if (rodent.mode.protocol == MOUSE_PROTO_INPORT)
+ rodent.mode.protocol = MOUSE_PROTO_BUS;
+ if (rodent.mode.protocol != rodent.rtype) {
+ /* Hmm, the driver doesn't agree with the user... */
+ if (rodent.rtype != MOUSE_PROTO_UNKNOWN)
+ logwarnx("mouse type mismatch (%s != %s), %s is assumed",
+ r_name(rodent.mode.protocol), r_name(rodent.rtype),
+ r_name(rodent.mode.protocol));
+ rodent.rtype = rodent.mode.protocol;
+ bcopy(proto[rodent.rtype], cur_proto, sizeof(cur_proto));
+ }
+ }
+ cur_proto[4] = rodent.mode.packetsize;
+ cur_proto[0] = rodent.mode.syncmask[0]; /* header byte bit mask */
+ cur_proto[1] = rodent.mode.syncmask[1]; /* header bit pattern */
+ }
+
+ /* maybe this is 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)
+{
+ unsigned char buf[16]; /* scrach buffer */
+ fd_set fds;
+ char *s;
+ char c;
+ int i;
+
+ /**
+ ** This comment is a little out of context here, but it contains
+ ** some useful information...
+ ********************************************************************
+ **
+ ** The following lines take care of the Logitech MouseMan protocols.
+ **
+ ** NOTE: There are different versions of both MouseMan and TrackMan!
+ ** Hence I add another protocol P_LOGIMAN, which the user can
+ ** specify as MouseMan in his XF86Config file. This entry was
+ ** formerly handled as a special case of P_MS. However, people
+ ** who don't have the middle button problem, can still specify
+ ** Microsoft and use P_MS.
+ **
+ ** By default, these mice should use a 3 byte Microsoft protocol
+ ** plus a 4th byte for the middle button. However, the mouse might
+ ** have switched to a different protocol before we use it, so I send
+ ** the proper sequence just in case.
+ **
+ ** NOTE: - all commands to (at least the European) MouseMan have to
+ ** be sent at 1200 Baud.
+ ** - each command starts with a '*'.
+ ** - whenever the MouseMan receives a '*', it will switch back
+ ** to 1200 Baud. Hence I have to select the desired protocol
+ ** first, then select the baud rate.
+ **
+ ** The protocols supported by the (European) MouseMan are:
+ ** - 5 byte packed binary protocol, as with the Mouse Systems
+ ** mouse. Selected by sequence "*U".
+ ** - 2 button 3 byte MicroSoft compatible protocol. Selected
+ ** by sequence "*V".
+ ** - 3 button 3+1 byte MicroSoft compatible protocol (default).
+ ** Selected by sequence "*X".
+ **
+ ** The following baud rates are supported:
+ ** - 1200 Baud (default). Selected by sequence "*n".
+ ** - 9600 Baud. Selected by sequence "*q".
+ **
+ ** Selecting a sample rate is no longer supported with the MouseMan!
+ ** Some additional lines in xf86Config.c take care of ill configured
+ ** baud rates and sample rates. (The user will get an error.)
+ */
+
+ switch (rodent.rtype) {
+
+ case MOUSE_PROTO_LOGI:
+ /*
+ * The baud rate selection command must be sent at the current
+ * baud rate; try all likely settings
+ */
+ setmousespeed(9600, rodent.baudrate, rodentcflags[rodent.rtype]);
+ setmousespeed(4800, rodent.baudrate, rodentcflags[rodent.rtype]);
+ setmousespeed(2400, rodent.baudrate, rodentcflags[rodent.rtype]);
+ setmousespeed(1200, rodent.baudrate, rodentcflags[rodent.rtype]);
+ /* select MM series data format */
+ write(rodent.mfd, "S", 1);
+ setmousespeed(rodent.baudrate, rodent.baudrate,
+ rodentcflags[MOUSE_PROTO_MM]);
+ /* select report rate/frequency */
+ if (rodent.rate <= 0) write(rodent.mfd, "O", 1);
+ else if (rodent.rate <= 15) write(rodent.mfd, "J", 1);
+ else if (rodent.rate <= 27) write(rodent.mfd, "K", 1);
+ else if (rodent.rate <= 42) write(rodent.mfd, "L", 1);
+ else if (rodent.rate <= 60) write(rodent.mfd, "R", 1);
+ else if (rodent.rate <= 85) write(rodent.mfd, "M", 1);
+ else if (rodent.rate <= 125) write(rodent.mfd, "Q", 1);
+ else write(rodent.mfd, "N", 1);
+ break;
+
+ case MOUSE_PROTO_LOGIMOUSEMAN:
+ /* The command must always be sent at 1200 baud */
+ setmousespeed(1200, 1200, rodentcflags[rodent.rtype]);
+ write(rodent.mfd, "*X", 2);
+ setmousespeed(1200, rodent.baudrate, rodentcflags[rodent.rtype]);
+ break;
+
+ case MOUSE_PROTO_HITTAB:
+ setmousespeed(1200, rodent.baudrate, rodentcflags[rodent.rtype]);
+
+ /*
+ * Initialize Hitachi PUMA Plus - Model 1212E to desired settings.
+ * The tablet must be configured to be in MM mode, NO parity,
+ * Binary Format. xf86Info.sampleRate controls the sensativity
+ * of the tablet. We only use this tablet for it's 4-button puck
+ * so we don't run in "Absolute Mode"
+ */
+ write(rodent.mfd, "z8", 2); /* Set Parity = "NONE" */
+ usleep(50000);
+ write(rodent.mfd, "zb", 2); /* Set Format = "Binary" */
+ usleep(50000);
+ write(rodent.mfd, "@", 1); /* Set Report Mode = "Stream" */
+ usleep(50000);
+ write(rodent.mfd, "R", 1); /* Set Output Rate = "45 rps" */
+ usleep(50000);
+ write(rodent.mfd, "I\x20", 2); /* Set Incrememtal Mode "20" */
+ usleep(50000);
+ write(rodent.mfd, "E", 1); /* Set Data Type = "Relative */
+ usleep(50000);
+
+ /* Resolution is in 'lines per inch' on the Hitachi tablet */
+ if (rodent.resolution == MOUSE_RES_LOW) c = 'g';
+ else if (rodent.resolution == MOUSE_RES_MEDIUMLOW) c = 'e';
+ else if (rodent.resolution == MOUSE_RES_MEDIUMHIGH) c = 'h';
+ else if (rodent.resolution == MOUSE_RES_HIGH) c = 'd';
+ else if (rodent.resolution <= 40) c = 'g';
+ else if (rodent.resolution <= 100) c = 'd';
+ else if (rodent.resolution <= 200) c = 'e';
+ else if (rodent.resolution <= 500) c = 'h';
+ else if (rodent.resolution <= 1000) c = 'j';
+ else c = 'd';
+ write(rodent.mfd, &c, 1);
+ usleep(50000);
+
+ write(rodent.mfd, "\021", 1); /* Resume DATA output */
+ break;
+
+ case MOUSE_PROTO_THINK:
+ setmousespeed(1200, rodent.baudrate, rodentcflags[rodent.rtype]);
+ /* the PnP ID string may be sent again, discard it */
+ usleep(200000);
+ i = FREAD;
+ ioctl(rodent.mfd, TIOCFLUSH, &i);
+ /* send the command to initialize the beast */
+ for (s = "E5E5"; *s; ++s) {
+ write(rodent.mfd, s, 1);
+ FD_ZERO(&fds);
+ FD_SET(rodent.mfd, &fds);
+ if (select(FD_SETSIZE, &fds, NULL, NULL, NULL) <= 0)
+ break;
+ read(rodent.mfd, &c, 1);
+ debug("%c", c);
+ if (c != *s)
+ break;
+ }
+ break;
+
+ case MOUSE_PROTO_JOGDIAL:
+ break;
+ case MOUSE_PROTO_MSC:
+ setmousespeed(1200, rodent.baudrate, rodentcflags[rodent.rtype]);
+ if (rodent.flags & ClearDTR) {
+ i = TIOCM_DTR;
+ ioctl(rodent.mfd, TIOCMBIC, &i);
+ }
+ if (rodent.flags & ClearRTS) {
+ i = TIOCM_RTS;
+ ioctl(rodent.mfd, TIOCMBIC, &i);
+ }
+ break;
+
+ case MOUSE_PROTO_SYSMOUSE:
+ if (rodent.hw.iftype == MOUSE_IF_SYSMOUSE)
+ setmousespeed(1200, rodent.baudrate, rodentcflags[rodent.rtype]);
+ /* 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;
+
+
+ case MOUSE_PROTO_VERSAPAD:
+ tcsendbreak(rodent.mfd, 0); /* send break for 400 msec */
+ i = FREAD;
+ ioctl(rodent.mfd, TIOCFLUSH, &i);
+ for (i = 0; i < 7; ++i) {
+ FD_ZERO(&fds);
+ FD_SET(rodent.mfd, &fds);
+ if (select(FD_SETSIZE, &fds, NULL, NULL, NULL) <= 0)
+ break;
+ read(rodent.mfd, &c, 1);
+ buf[i] = c;
+ }
+ debug("%s\n", buf);
+ if ((buf[0] != 'V') || (buf[1] != 'P')|| (buf[7] != '\r'))
+ break;
+ setmousespeed(9600, rodent.baudrate, rodentcflags[rodent.rtype]);
+ tcsendbreak(rodent.mfd, 0); /* send break for 400 msec again */
+ for (i = 0; i < 7; ++i) {
+ FD_ZERO(&fds);
+ FD_SET(rodent.mfd, &fds);
+ if (select(FD_SETSIZE, &fds, NULL, NULL, NULL) <= 0)
+ break;
+ read(rodent.mfd, &c, 1);
+ debug("%c", c);
+ if (c != buf[i])
+ break;
+ }
+ i = FREAD;
+ ioctl(rodent.mfd, TIOCFLUSH, &i);
+ break;
+
+ default:
+ setmousespeed(1200, rodent.baudrate, rodentcflags[rodent.rtype]);
+ break;
+ }
+}
+
+static int
+r_protocol(u_char rBuf, mousestatus_t *act)
+{
+ /* MOUSE_MSS_BUTTON?DOWN -> MOUSE_BUTTON?DOWN */
+ static int butmapmss[4] = { /* Microsoft, MouseMan, GlidePoint,
+ IntelliMouse, Thinking Mouse */
+ 0,
+ MOUSE_BUTTON3DOWN,
+ MOUSE_BUTTON1DOWN,
+ MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
+ };
+ static int butmapmss2[4] = { /* Microsoft, MouseMan, GlidePoint,
+ Thinking Mouse */
+ 0,
+ MOUSE_BUTTON4DOWN,
+ MOUSE_BUTTON2DOWN,
+ MOUSE_BUTTON2DOWN | MOUSE_BUTTON4DOWN,
+ };
+ /* MOUSE_INTELLI_BUTTON?DOWN -> MOUSE_BUTTON?DOWN */
+ static int butmapintelli[4] = { /* IntelliMouse, NetMouse, Mie Mouse,
+ MouseMan+ */
+ 0,
+ MOUSE_BUTTON2DOWN,
+ MOUSE_BUTTON4DOWN,
+ MOUSE_BUTTON2DOWN | MOUSE_BUTTON4DOWN,
+ };
+ /* MOUSE_MSC_BUTTON?UP -> MOUSE_BUTTON?DOWN */
+ static int butmapmsc[8] = { /* MouseSystems, MMSeries, Logitech,
+ Bus, sysmouse */
+ 0,
+ MOUSE_BUTTON3DOWN,
+ MOUSE_BUTTON2DOWN,
+ MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN,
+ MOUSE_BUTTON1DOWN,
+ MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
+ MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN,
+ MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN
+ };
+ /* MOUSE_PS2_BUTTON?DOWN -> MOUSE_BUTTON?DOWN */
+ static int butmapps2[8] = { /* PS/2 */
+ 0,
+ MOUSE_BUTTON1DOWN,
+ MOUSE_BUTTON3DOWN,
+ MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
+ MOUSE_BUTTON2DOWN,
+ MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN,
+ MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN,
+ MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN
+ };
+ /* for Hitachi tablet */
+ static int butmaphit[8] = { /* MM HitTablet */
+ 0,
+ MOUSE_BUTTON3DOWN,
+ MOUSE_BUTTON2DOWN,
+ MOUSE_BUTTON1DOWN,
+ MOUSE_BUTTON4DOWN,
+ MOUSE_BUTTON5DOWN,
+ MOUSE_BUTTON6DOWN,
+ MOUSE_BUTTON7DOWN,
+ };
+ /* for serial VersaPad */
+ static int butmapversa[8] = { /* VersaPad */
+ 0,
+ 0,
+ MOUSE_BUTTON3DOWN,
+ MOUSE_BUTTON3DOWN,
+ MOUSE_BUTTON1DOWN,
+ MOUSE_BUTTON1DOWN,
+ MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
+ MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
+ };
+ /* for PS/2 VersaPad */
+ static int butmapversaps2[8] = { /* VersaPad */
+ 0,
+ MOUSE_BUTTON3DOWN,
+ 0,
+ MOUSE_BUTTON3DOWN,
+ MOUSE_BUTTON1DOWN,
+ MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
+ MOUSE_BUTTON1DOWN,
+ MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
+ };
+ static int pBufP = 0;
+ static unsigned char pBuf[8];
+ static int prev_x, prev_y;
+ static int on = FALSE;
+ int x, y;
+
+ debug("received char 0x%x",(int)rBuf);
+ if (rodent.rtype == MOUSE_PROTO_KIDSPAD)
+ return kidspad(rBuf, act) ;
+
+ /*
+ * Hack for resyncing: We check here for a package that is:
+ * a) illegal (detected by wrong data-package header)
+ * b) invalid (0x80 == -128 and that might be wrong for MouseSystems)
+ * c) bad header-package
+ *
+ * NOTE: b) is a voilation of the MouseSystems-Protocol, since values of
+ * -128 are allowed, but since they are very seldom we can easily
+ * use them as package-header with no button pressed.
+ * NOTE/2: On a PS/2 mouse any byte is valid as a data byte. Furthermore,
+ * 0x80 is not valid as a header byte. For a PS/2 mouse we skip
+ * checking data bytes.
+ * For resyncing a PS/2 mouse we require the two most significant
+ * bits in the header byte to be 0. These are the overflow bits,
+ * and in case of an overflow we actually lose sync. Overflows
+ * are very rare, however, and we quickly gain sync again after
+ * an overflow condition. This is the best we can do. (Actually,
+ * we could use bit 0x08 in the header byte for resyncing, since
+ * that bit is supposed to be always on, but nobody told
+ * Microsoft...)
+ */
+
+ if (pBufP != 0 && rodent.rtype != MOUSE_PROTO_PS2 &&
+ ((rBuf & cur_proto[2]) != cur_proto[3] || rBuf == 0x80))
+ {
+ pBufP = 0; /* skip package */
+ }
+
+ if (pBufP == 0 && (rBuf & cur_proto[0]) != cur_proto[1])
+ return 0;
+
+ /* is there an extra data byte? */
+ if (pBufP >= cur_proto[4] && (rBuf & cur_proto[0]) != cur_proto[1])
+ {
+ /*
+ * Hack for Logitech MouseMan Mouse - Middle button
+ *
+ * Unfortunately this mouse has variable length packets: the standard
+ * Microsoft 3 byte packet plus an optional 4th byte whenever the
+ * middle button status changes.
+ *
+ * We have already processed the standard packet with the movement
+ * and button info. Now post an event message with the old status
+ * of the left and right buttons and the updated middle button.
+ */
+
+ /*
+ * Even worse, different MouseMen and TrackMen differ in the 4th
+ * byte: some will send 0x00/0x20, others 0x01/0x21, or even
+ * 0x02/0x22, so I have to strip off the lower bits.
+ *
+ * [JCH-96/01/21]
+ * HACK for ALPS "fourth button". (It's bit 0x10 of the "fourth byte"
+ * and it is activated by tapping the glidepad with the finger! 8^)
+ * We map it to bit bit3, and the reverse map in xf86Events just has
+ * to be extended so that it is identified as Button 4. The lower
+ * half of the reverse-map may remain unchanged.
+ */
+
+ /*
+ * [KY-97/08/03]
+ * Receive the fourth byte only when preceding three bytes have
+ * been detected (pBufP >= cur_proto[4]). In the previous
+ * versions, the test was pBufP == 0; thus, we may have mistakingly
+ * received a byte even if we didn't see anything preceding
+ * the byte.
+ */
+
+ if ((rBuf & cur_proto[5]) != cur_proto[6]) {
+ pBufP = 0;
+ return 0;
+ }
+
+ switch (rodent.rtype) {
+#if notyet
+ case MOUSE_PROTO_MARIQUA:
+ /*
+ * This mouse has 16! buttons in addition to the standard
+ * three of them. They return 0x10 though 0x1f in the
+ * so-called `ten key' mode and 0x30 though 0x3f in the
+ * `function key' mode. As there are only 31 bits for
+ * button state (including the standard three), we ignore
+ * the bit 0x20 and don't distinguish the two modes.
+ */
+ act->dx = act->dy = act->dz = 0;
+ act->obutton = act->button;
+ rBuf &= 0x1f;
+ act->button = (1 << (rBuf - 13))
+ | (act->obutton & (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN));
+ /*
+ * FIXME: this is a button "down" event. There needs to be
+ * a corresponding button "up" event... XXX
+ */
+ break;
+#endif /* notyet */
+ case MOUSE_PROTO_JOGDIAL:
+ break;
+
+ /*
+ * IntelliMouse, NetMouse (including NetMouse Pro) and Mie Mouse
+ * always send the fourth byte, whereas the fourth byte is
+ * optional for GlidePoint and ThinkingMouse. The fourth byte
+ * is also optional for MouseMan+ and FirstMouse+ in their
+ * native mode. It is always sent if they are in the IntelliMouse
+ * compatible mode.
+ */
+ case MOUSE_PROTO_INTELLI: /* IntelliMouse, NetMouse, Mie Mouse,
+ MouseMan+ */
+ act->dx = act->dy = 0;
+ act->dz = (rBuf & 0x08) ? (rBuf & 0x0f) - 16 : (rBuf & 0x0f);
+ if ((act->dz >= 7) || (act->dz <= -7))
+ act->dz = 0;
+ act->obutton = act->button;
+ act->button = butmapintelli[(rBuf & MOUSE_MSS_BUTTONS) >> 4]
+ | (act->obutton & (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN));
+ break;
+
+ default:
+ act->dx = act->dy = act->dz = 0;
+ act->obutton = act->button;
+ act->button = butmapmss2[(rBuf & MOUSE_MSS_BUTTONS) >> 4]
+ | (act->obutton & (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN));
+ break;
+ }
+
+ act->flags = ((act->dx || act->dy || act->dz) ? MOUSE_POSCHANGED : 0)
+ | (act->obutton ^ act->button);
+ pBufP = 0;
+ return act->flags;
+ }
+
+ if (pBufP >= cur_proto[4])
+ pBufP = 0;
+ pBuf[pBufP++] = rBuf;
+ if (pBufP != cur_proto[4])
+ return 0;
+
+ /*
+ * assembly full package
+ */
+
+ debug("assembled full packet (len %d) %x,%x,%x,%x,%x,%x,%x,%x",
+ cur_proto[4],
+ pBuf[0], pBuf[1], pBuf[2], pBuf[3],
+ pBuf[4], pBuf[5], pBuf[6], pBuf[7]);
+
+ act->dz = 0;
+ act->obutton = act->button;
+ switch (rodent.rtype)
+ {
+ case MOUSE_PROTO_MS: /* Microsoft */
+ case MOUSE_PROTO_LOGIMOUSEMAN: /* MouseMan/TrackMan */
+ case MOUSE_PROTO_X10MOUSEREM: /* X10 MouseRemote */
+ act->button = act->obutton & MOUSE_BUTTON4DOWN;
+ if (rodent.flags & ChordMiddle)
+ act->button |= ((pBuf[0] & MOUSE_MSS_BUTTONS) == MOUSE_MSS_BUTTONS)
+ ? MOUSE_BUTTON2DOWN
+ : butmapmss[(pBuf[0] & MOUSE_MSS_BUTTONS) >> 4];
+ else
+ act->button |= (act->obutton & MOUSE_BUTTON2DOWN)
+ | butmapmss[(pBuf[0] & MOUSE_MSS_BUTTONS) >> 4];
+
+ /* Send X10 btn events to remote client (ensure -128-+127 range) */
+ if ((rodent.rtype == MOUSE_PROTO_X10MOUSEREM) &&
+ ((pBuf[0] & 0xFC) == 0x44) && (pBuf[2] == 0x3F)) {
+ if (rodent.mremcfd >= 0) {
+ unsigned char key = (signed char)(((pBuf[0] & 0x03) << 6) |
+ (pBuf[1] & 0x3F));
+ write( rodent.mremcfd, &key, 1 );
+ }
+ return 0;
+ }
+
+ act->dx = (signed char)(((pBuf[0] & 0x03) << 6) | (pBuf[1] & 0x3F));
+ act->dy = (signed char)(((pBuf[0] & 0x0C) << 4) | (pBuf[2] & 0x3F));
+ break;
+
+ case MOUSE_PROTO_GLIDEPOINT: /* GlidePoint */
+ case MOUSE_PROTO_THINK: /* ThinkingMouse */
+ case MOUSE_PROTO_INTELLI: /* IntelliMouse, NetMouse, Mie Mouse,
+ MouseMan+ */
+ act->button = (act->obutton & (MOUSE_BUTTON2DOWN | MOUSE_BUTTON4DOWN))
+ | butmapmss[(pBuf[0] & MOUSE_MSS_BUTTONS) >> 4];
+ act->dx = (signed char)(((pBuf[0] & 0x03) << 6) | (pBuf[1] & 0x3F));
+ act->dy = (signed char)(((pBuf[0] & 0x0C) << 4) | (pBuf[2] & 0x3F));
+ break;
+
+ case MOUSE_PROTO_MSC: /* MouseSystems Corp */
+#if notyet
+ case MOUSE_PROTO_MARIQUA: /* Mariqua */
+#endif
+ act->button = butmapmsc[(~pBuf[0]) & MOUSE_MSC_BUTTONS];
+ act->dx = (signed char)(pBuf[1]) + (signed char)(pBuf[3]);
+ act->dy = - ((signed char)(pBuf[2]) + (signed char)(pBuf[4]));
+ break;
+
+ case MOUSE_PROTO_JOGDIAL: /* JogDial */
+ if (rBuf == 0x6c)
+ act->dz = -1;
+ if (rBuf == 0x72)
+ act->dz = 1;
+ if (rBuf == 0x64)
+ act->button = MOUSE_BUTTON1DOWN;
+ if (rBuf == 0x75)
+ act->button = 0;
+ break;
+
+ case MOUSE_PROTO_HITTAB: /* MM HitTablet */
+ act->button = butmaphit[pBuf[0] & 0x07];
+ act->dx = (pBuf[0] & MOUSE_MM_XPOSITIVE) ? pBuf[1] : - pBuf[1];
+ act->dy = (pBuf[0] & MOUSE_MM_YPOSITIVE) ? - pBuf[2] : pBuf[2];
+ break;
+
+ case MOUSE_PROTO_MM: /* MM Series */
+ case MOUSE_PROTO_LOGI: /* Logitech Mice */
+ act->button = butmapmsc[pBuf[0] & MOUSE_MSC_BUTTONS];
+ act->dx = (pBuf[0] & MOUSE_MM_XPOSITIVE) ? pBuf[1] : - pBuf[1];
+ act->dy = (pBuf[0] & MOUSE_MM_YPOSITIVE) ? - pBuf[2] : pBuf[2];
+ break;
+
+ case MOUSE_PROTO_VERSAPAD: /* VersaPad */
+ act->button = butmapversa[(pBuf[0] & MOUSE_VERSA_BUTTONS) >> 3];
+ act->button |= (pBuf[0] & MOUSE_VERSA_TAP) ? MOUSE_BUTTON4DOWN : 0;
+ act->dx = act->dy = 0;
+ if (!(pBuf[0] & MOUSE_VERSA_IN_USE)) {
+ on = FALSE;
+ break;
+ }
+ x = (pBuf[2] << 6) | pBuf[1];
+ if (x & 0x800)
+ x -= 0x1000;
+ y = (pBuf[4] << 6) | pBuf[3];
+ if (y & 0x800)
+ y -= 0x1000;
+ if (on) {
+ act->dx = prev_x - x;
+ act->dy = prev_y - y;
+ } else {
+ on = TRUE;
+ }
+ prev_x = x;
+ prev_y = y;
+ break;
+
+ case MOUSE_PROTO_BUS: /* Bus */
+ case MOUSE_PROTO_INPORT: /* InPort */
+ act->button = butmapmsc[(~pBuf[0]) & MOUSE_MSC_BUTTONS];
+ act->dx = (signed char)pBuf[1];
+ act->dy = - (signed char)pBuf[2];
+ break;
+
+ case MOUSE_PROTO_PS2: /* PS/2 */
+ act->button = butmapps2[pBuf[0] & MOUSE_PS2_BUTTONS];
+ act->dx = (pBuf[0] & MOUSE_PS2_XNEG) ? pBuf[1] - 256 : pBuf[1];
+ act->dy = (pBuf[0] & MOUSE_PS2_YNEG) ? -(pBuf[2] - 256) : -pBuf[2];
+ /*
+ * Moused usually operates the psm driver at the operation level 1
+ * which sends mouse data in MOUSE_PROTO_SYSMOUSE protocol.
+ * The following code takes effect only when the user explicitly
+ * requets the level 2 at which wheel movement and additional button
+ * actions are encoded in model-dependent formats. At the level 0
+ * the following code is no-op because the psm driver says the model
+ * is MOUSE_MODEL_GENERIC.
+ */
+ switch (rodent.hw.model) {
+ case MOUSE_MODEL_EXPLORER:
+ /* wheel and additional button data is in the fourth byte */
+ act->dz = (pBuf[3] & MOUSE_EXPLORER_ZNEG)
+ ? (pBuf[3] & 0x0f) - 16 : (pBuf[3] & 0x0f);
+ act->button |= (pBuf[3] & MOUSE_EXPLORER_BUTTON4DOWN)
+ ? MOUSE_BUTTON4DOWN : 0;
+ act->button |= (pBuf[3] & MOUSE_EXPLORER_BUTTON5DOWN)
+ ? MOUSE_BUTTON5DOWN : 0;
+ break;
+ case MOUSE_MODEL_INTELLI:
+ case MOUSE_MODEL_NET:
+ /* wheel data is in the fourth byte */
+ act->dz = (signed char)pBuf[3];
+ if ((act->dz >= 7) || (act->dz <= -7))
+ act->dz = 0;
+ /* some compatible mice may have additional buttons */
+ act->button |= (pBuf[0] & MOUSE_PS2INTELLI_BUTTON4DOWN)
+ ? MOUSE_BUTTON4DOWN : 0;
+ act->button |= (pBuf[0] & MOUSE_PS2INTELLI_BUTTON5DOWN)
+ ? MOUSE_BUTTON5DOWN : 0;
+ break;
+ case MOUSE_MODEL_MOUSEMANPLUS:
+ if (((pBuf[0] & MOUSE_PS2PLUS_SYNCMASK) == MOUSE_PS2PLUS_SYNC)
+ && (abs(act->dx) > 191)
+ && MOUSE_PS2PLUS_CHECKBITS(pBuf)) {
+ /* the extended data packet encodes button and wheel events */
+ switch (MOUSE_PS2PLUS_PACKET_TYPE(pBuf)) {
+ case 1:
+ /* wheel data packet */
+ act->dx = act->dy = 0;
+ if (pBuf[2] & 0x80) {
+ /* horizontal roller count - ignore it XXX*/
+ } else {
+ /* vertical roller count */
+ act->dz = (pBuf[2] & MOUSE_PS2PLUS_ZNEG)
+ ? (pBuf[2] & 0x0f) - 16 : (pBuf[2] & 0x0f);
+ }
+ act->button |= (pBuf[2] & MOUSE_PS2PLUS_BUTTON4DOWN)
+ ? MOUSE_BUTTON4DOWN : 0;
+ act->button |= (pBuf[2] & MOUSE_PS2PLUS_BUTTON5DOWN)
+ ? MOUSE_BUTTON5DOWN : 0;
+ break;
+ case 2:
+ /* this packet type is reserved by Logitech */
+ /*
+ * IBM ScrollPoint Mouse uses this packet type to
+ * encode both vertical and horizontal scroll movement.
+ */
+ act->dx = act->dy = 0;
+ /* horizontal roller count */
+ if (pBuf[2] & 0x0f)
+ act->dz = (pBuf[2] & MOUSE_SPOINT_WNEG) ? -2 : 2;
+ /* vertical roller count */
+ if (pBuf[2] & 0xf0)
+ act->dz = (pBuf[2] & MOUSE_SPOINT_ZNEG) ? -1 : 1;
+#if 0
+ /* vertical roller count */
+ act->dz = (pBuf[2] & MOUSE_SPOINT_ZNEG)
+ ? ((pBuf[2] >> 4) & 0x0f) - 16
+ : ((pBuf[2] >> 4) & 0x0f);
+ /* horizontal roller count */
+ act->dw = (pBuf[2] & MOUSE_SPOINT_WNEG)
+ ? (pBuf[2] & 0x0f) - 16 : (pBuf[2] & 0x0f);
+#endif
+ break;
+ case 0:
+ /* device type packet - shouldn't happen */
+ /* FALL THROUGH */
+ default:
+ act->dx = act->dy = 0;
+ act->button = act->obutton;
+ debug("unknown PS2++ packet type %d: 0x%02x 0x%02x 0x%02x\n",
+ MOUSE_PS2PLUS_PACKET_TYPE(pBuf),
+ pBuf[0], pBuf[1], pBuf[2]);
+ break;
+ }
+ } else {
+ /* preserve button states */
+ act->button |= act->obutton & MOUSE_EXTBUTTONS;
+ }
+ break;
+ case MOUSE_MODEL_GLIDEPOINT:
+ /* `tapping' action */
+ act->button |= ((pBuf[0] & MOUSE_PS2_TAP)) ? 0 : MOUSE_BUTTON4DOWN;
+ break;
+ case MOUSE_MODEL_NETSCROLL:
+ /* three addtional bytes encode buttons and wheel events */
+ act->button |= (pBuf[3] & MOUSE_PS2_BUTTON3DOWN)
+ ? MOUSE_BUTTON4DOWN : 0;
+ act->button |= (pBuf[3] & MOUSE_PS2_BUTTON1DOWN)
+ ? MOUSE_BUTTON5DOWN : 0;
+ act->dz = (pBuf[3] & MOUSE_PS2_XNEG) ? pBuf[4] - 256 : pBuf[4];
+ break;
+ case MOUSE_MODEL_THINK:
+ /* the fourth button state in the first byte */
+ act->button |= (pBuf[0] & MOUSE_PS2_TAP) ? MOUSE_BUTTON4DOWN : 0;
+ break;
+ case MOUSE_MODEL_VERSAPAD:
+ act->button = butmapversaps2[pBuf[0] & MOUSE_PS2VERSA_BUTTONS];
+ act->button |=
+ (pBuf[0] & MOUSE_PS2VERSA_TAP) ? MOUSE_BUTTON4DOWN : 0;
+ act->dx = act->dy = 0;
+ if (!(pBuf[0] & MOUSE_PS2VERSA_IN_USE)) {
+ on = FALSE;
+ break;
+ }
+ x = ((pBuf[4] << 8) & 0xf00) | pBuf[1];
+ if (x & 0x800)
+ x -= 0x1000;
+ y = ((pBuf[4] << 4) & 0xf00) | pBuf[2];
+ if (y & 0x800)
+ y -= 0x1000;
+ if (on) {
+ act->dx = prev_x - x;
+ act->dy = prev_y - y;
+ } else {
+ on = TRUE;
+ }
+ prev_x = x;
+ prev_y = y;
+ break;
+ case MOUSE_MODEL_4D:
+ act->dx = (pBuf[1] & 0x80) ? pBuf[1] - 256 : pBuf[1];
+ act->dy = (pBuf[2] & 0x80) ? -(pBuf[2] - 256) : -pBuf[2];
+ switch (pBuf[0] & MOUSE_4D_WHEELBITS) {
+ case 0x10:
+ act->dz = 1;
+ break;
+ case 0x30:
+ act->dz = -1;
+ break;
+ case 0x40: /* 2nd wheel rolling right XXX */
+ act->dz = 2;
+ break;
+ case 0xc0: /* 2nd wheel rolling left XXX */
+ act->dz = -2;
+ break;
+ }
+ break;
+ case MOUSE_MODEL_4DPLUS:
+ if ((act->dx < 16 - 256) && (act->dy > 256 - 16)) {
+ act->dx = act->dy = 0;
+ if (pBuf[2] & MOUSE_4DPLUS_BUTTON4DOWN)
+ act->button |= MOUSE_BUTTON4DOWN;
+ act->dz = (pBuf[2] & MOUSE_4DPLUS_ZNEG)
+ ? ((pBuf[2] & 0x07) - 8) : (pBuf[2] & 0x07);
+ } else {
+ /* preserve previous button states */
+ act->button |= act->obutton & MOUSE_EXTBUTTONS;
+ }
+ break;
+ case MOUSE_MODEL_GENERIC:
+ default:
+ break;
+ }
+ break;
+
+ case MOUSE_PROTO_SYSMOUSE: /* sysmouse */
+ act->button = butmapmsc[(~pBuf[0]) & MOUSE_SYS_STDBUTTONS];
+ act->dx = (signed char)(pBuf[1]) + (signed char)(pBuf[3]);
+ act->dy = - ((signed char)(pBuf[2]) + (signed char)(pBuf[4]));
+ if (rodent.level == 1) {
+ act->dz = ((signed char)(pBuf[5] << 1) + (signed char)(pBuf[6] << 1)) >> 1;
+ act->button |= ((~pBuf[7] & MOUSE_SYS_EXTBUTTONS) << 3);
+ }
+ break;
+
+ default:
+ return 0;
+ }
+ /*
+ * We don't reset pBufP here yet, as there may be an additional data
+ * byte in some protocols. See above.
+ */
+
+ /* has something changed? */
+ act->flags = ((act->dx || act->dy || act->dz) ? MOUSE_POSCHANGED : 0)
+ | (act->obutton ^ act->button);
+
+ return act->flags;
+}
+
+static int
+r_statetrans(mousestatus_t *a1, mousestatus_t *a2, int trans)
+{
+ int changed;
+ int flags;
+
+ a2->dx = a1->dx;
+ a2->dy = a1->dy;
+ a2->dz = a1->dz;
+ a2->obutton = a2->button;
+ a2->button = a1->button;
+ a2->flags = a1->flags;
+ changed = FALSE;
+
+ if (rodent.flags & Emulate3Button) {
+ if (debug > 2)
+ debug("state:%d, trans:%d -> state:%d",
+ mouse_button_state, trans,
+ states[mouse_button_state].s[trans]);
+ /*
+ * Avoid re-ordering button and movement events. While a button
+ * event is deferred, throw away up to BUTTON2_MAXMOVE movement
+ * events to allow for mouse jitter. If more movement events
+ * occur, then complete the deferred button events immediately.
+ */
+ if ((a2->dx != 0 || a2->dy != 0) &&
+ S_DELAYED(states[mouse_button_state].s[trans])) {
+ if (++mouse_move_delayed > BUTTON2_MAXMOVE) {
+ mouse_move_delayed = 0;
+ mouse_button_state =
+ states[mouse_button_state].s[A_TIMEOUT];
+ changed = TRUE;
+ } else
+ a2->dx = a2->dy = 0;
+ } else
+ mouse_move_delayed = 0;
+ if (mouse_button_state != states[mouse_button_state].s[trans])
+ changed = TRUE;
+ if (changed)
+ gettimeofday(&mouse_button_state_tv, NULL);
+ mouse_button_state = states[mouse_button_state].s[trans];
+ a2->button &=
+ ~(MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN);
+ a2->button &= states[mouse_button_state].mask;
+ a2->button |= states[mouse_button_state].buttons;
+ flags = a2->flags & MOUSE_POSCHANGED;
+ flags |= a2->obutton ^ a2->button;
+ if (flags & MOUSE_BUTTON2DOWN) {
+ a2->flags = flags & MOUSE_BUTTON2DOWN;
+ r_timestamp(a2);
+ }
+ a2->flags = flags;
+ }
+ return changed;
+}
+
+/* phisical to logical button mapping */
+static int p2l[MOUSE_MAXBUTTON] = {
+ MOUSE_BUTTON1DOWN, MOUSE_BUTTON2DOWN, MOUSE_BUTTON3DOWN, MOUSE_BUTTON4DOWN,
+ MOUSE_BUTTON5DOWN, MOUSE_BUTTON6DOWN, MOUSE_BUTTON7DOWN, MOUSE_BUTTON8DOWN,
+ 0x00000100, 0x00000200, 0x00000400, 0x00000800,
+ 0x00001000, 0x00002000, 0x00004000, 0x00008000,
+ 0x00010000, 0x00020000, 0x00040000, 0x00080000,
+ 0x00100000, 0x00200000, 0x00400000, 0x00800000,
+ 0x01000000, 0x02000000, 0x04000000, 0x08000000,
+ 0x10000000, 0x20000000, 0x40000000,
+};
+
+static char *
+skipspace(char *s)
+{
+ while(isspace(*s))
+ ++s;
+ return s;
+}
+
+static int
+r_installmap(char *arg)
+{
+ int pbutton;
+ int lbutton;
+ char *s;
+
+ while (*arg) {
+ arg = skipspace(arg);
+ s = arg;
+ while (isdigit(*arg))
+ ++arg;
+ arg = skipspace(arg);
+ if ((arg <= s) || (*arg != '='))
+ return FALSE;
+ lbutton = atoi(s);
+
+ arg = skipspace(++arg);
+ s = arg;
+ while (isdigit(*arg))
+ ++arg;
+ if ((arg <= s) || (!isspace(*arg) && (*arg != '\0')))
+ return FALSE;
+ pbutton = atoi(s);
+
+ if ((lbutton <= 0) || (lbutton > MOUSE_MAXBUTTON))
+ return FALSE;
+ if ((pbutton <= 0) || (pbutton > MOUSE_MAXBUTTON))
+ return FALSE;
+ p2l[pbutton - 1] = 1 << (lbutton - 1);
+ mstate[lbutton - 1] = &bstate[pbutton - 1];
+ }
+
+ return TRUE;
+}
+
+static void
+r_map(mousestatus_t *act1, mousestatus_t *act2)
+{
+ register int pb;
+ register int pbuttons;
+ int lbuttons;
+
+ pbuttons = act1->button;
+ lbuttons = 0;
+
+ act2->obutton = act2->button;
+ if (pbuttons & rodent.wmode) {
+ pbuttons &= ~rodent.wmode;
+ act1->dz = act1->dy;
+ act1->dx = 0;
+ act1->dy = 0;
+ }
+ act2->dx = act1->dx;
+ act2->dy = act1->dy;
+ act2->dz = act1->dz;
+
+ switch (rodent.zmap[0]) {
+ case 0: /* do nothing */
+ break;
+ case MOUSE_XAXIS:
+ if (act1->dz != 0) {
+ act2->dx = act1->dz;
+ act2->dz = 0;
+ }
+ break;
+ case MOUSE_YAXIS:
+ if (act1->dz != 0) {
+ act2->dy = act1->dz;
+ act2->dz = 0;
+ }
+ break;
+ default: /* buttons */
+ pbuttons &= ~(rodent.zmap[0] | rodent.zmap[1]
+ | rodent.zmap[2] | rodent.zmap[3]);
+ if ((act1->dz < -1) && rodent.zmap[2]) {
+ pbuttons |= rodent.zmap[2];
+ zstate[2].count = 1;
+ } else if (act1->dz < 0) {
+ pbuttons |= rodent.zmap[0];
+ zstate[0].count = 1;
+ } else if ((act1->dz > 1) && rodent.zmap[3]) {
+ pbuttons |= rodent.zmap[3];
+ zstate[3].count = 1;
+ } else if (act1->dz > 0) {
+ pbuttons |= rodent.zmap[1];
+ zstate[1].count = 1;
+ }
+ act2->dz = 0;
+ break;
+ }
+
+ for (pb = 0; (pb < MOUSE_MAXBUTTON) && (pbuttons != 0); ++pb) {
+ lbuttons |= (pbuttons & 1) ? p2l[pb] : 0;
+ pbuttons >>= 1;
+ }
+ act2->button = lbuttons;
+
+ act2->flags = ((act2->dx || act2->dy || act2->dz) ? MOUSE_POSCHANGED : 0)
+ | (act2->obutton ^ act2->button);
+}
+
+static void
+r_timestamp(mousestatus_t *act)
+{
+ struct timeval tv;
+ struct timeval tv1;
+ struct timeval tv2;
+ struct timeval tv3;
+ int button;
+ int mask;
+ int i;
+
+ mask = act->flags & MOUSE_BUTTONS;
+#if 0
+ if (mask == 0)
+ return;
+#endif
+
+ gettimeofday(&tv1, NULL);
+
+ /* double click threshold */
+ tv2.tv_sec = rodent.clickthreshold/1000;
+ tv2.tv_usec = (rodent.clickthreshold%1000)*1000;
+ timersub(&tv1, &tv2, &tv);
+ debug("tv: %ld %ld", tv.tv_sec, tv.tv_usec);
+
+ /* 3 button emulation timeout */
+ tv2.tv_sec = rodent.button2timeout/1000;
+ tv2.tv_usec = (rodent.button2timeout%1000)*1000;
+ timersub(&tv1, &tv2, &tv3);
+
+ button = MOUSE_BUTTON1DOWN;
+ for (i = 0; (i < MOUSE_MAXBUTTON) && (mask != 0); ++i) {
+ if (mask & 1) {
+ if (act->button & button) {
+ /* the button is down */
+ debug(" : %ld %ld",
+ bstate[i].tv.tv_sec, bstate[i].tv.tv_usec);
+ if (timercmp(&tv, &bstate[i].tv, >)) {
+ bstate[i].count = 1;
+ } else {
+ ++bstate[i].count;
+ }
+ bstate[i].tv = tv1;
+ } else {
+ /* the button is up */
+ bstate[i].tv = tv1;
+ }
+ } else {
+ if (act->button & button) {
+ /* the button has been down */
+ if (timercmp(&tv3, &bstate[i].tv, >)) {
+ bstate[i].count = 1;
+ bstate[i].tv = tv1;
+ act->flags |= button;
+ debug("button %d timeout", i + 1);
+ }
+ } else {
+ /* the button has been up */
+ }
+ }
+ button <<= 1;
+ mask >>= 1;
+ }
+}
+
+static int
+r_timeout(void)
+{
+ struct timeval tv;
+ struct timeval tv1;
+ struct timeval tv2;
+
+ if (states[mouse_button_state].timeout)
+ return TRUE;
+ gettimeofday(&tv1, NULL);
+ tv2.tv_sec = rodent.button2timeout/1000;
+ tv2.tv_usec = (rodent.button2timeout%1000)*1000;
+ timersub(&tv1, &tv2, &tv);
+ return timercmp(&tv, &mouse_button_state_tv, >);
+}
+
+static void
+r_click(mousestatus_t *act)
+{
+ struct mouse_info mouse;
+ int button;
+ int mask;
+ int i;
+
+ mask = act->flags & MOUSE_BUTTONS;
+ if (mask == 0)
+ return;
+
+ button = MOUSE_BUTTON1DOWN;
+ for (i = 0; (i < MOUSE_MAXBUTTON) && (mask != 0); ++i) {
+ if (mask & 1) {
+ debug("mstate[%d]->count:%d", i, mstate[i]->count);
+ if (act->button & button) {
+ /* the button is down */
+ mouse.u.event.value = mstate[i]->count;
+ } else {
+ /* the button is up */
+ mouse.u.event.value = 0;
+ }
+ mouse.operation = MOUSE_BUTTON_EVENT;
+ mouse.u.event.id = button;
+ if (debug < 2)
+ ioctl(rodent.cfd, CONS_MOUSECTL, &mouse);
+ debug("button %d count %d", i + 1, mouse.u.event.value);
+ }
+ button <<= 1;
+ mask >>= 1;
+ }
+}
+
+/* $XConsortium: posix_tty.c,v 1.3 95/01/05 20:42:55 kaleb Exp $ */
+/* $XFree86: xc/programs/Xserver/hw/xfree86/os-support/shared/posix_tty.c,v 3.4 1995/01/28 17:05:03 dawes Exp $ */
+/*
+ * Copyright 1993 by David Dawes <dawes@physics.su.oz.au>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of David Dawes
+ * not be used in advertising or publicity pertaining to distribution of
+ * the software without specific, written prior permission.
+ * David Dawes makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * DAVID DAWES DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL DAVID DAWES BE LIABLE FOR
+ * ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+
+static void
+setmousespeed(int old, int new, unsigned cflag)
+{
+ struct termios tty;
+ char *c;
+
+ if (tcgetattr(rodent.mfd, &tty) < 0)
+ {
+ logwarn("unable to get status of mouse fd");
+ return;
+ }
+
+ tty.c_iflag = IGNBRK | IGNPAR;
+ tty.c_oflag = 0;
+ tty.c_lflag = 0;
+ tty.c_cflag = (tcflag_t)cflag;
+ tty.c_cc[VTIME] = 0;
+ tty.c_cc[VMIN] = 1;
+
+ switch (old)
+ {
+ case 9600:
+ cfsetispeed(&tty, B9600);
+ cfsetospeed(&tty, B9600);
+ break;
+ case 4800:
+ cfsetispeed(&tty, B4800);
+ cfsetospeed(&tty, B4800);
+ break;
+ case 2400:
+ cfsetispeed(&tty, B2400);
+ cfsetospeed(&tty, B2400);
+ break;
+ case 1200:
+ default:
+ cfsetispeed(&tty, B1200);
+ cfsetospeed(&tty, B1200);
+ }
+
+ if (tcsetattr(rodent.mfd, TCSADRAIN, &tty) < 0)
+ {
+ logwarn("unable to set status of mouse fd");
+ return;
+ }
+
+ switch (new)
+ {
+ case 9600:
+ c = "*q";
+ cfsetispeed(&tty, B9600);
+ cfsetospeed(&tty, B9600);
+ break;
+ case 4800:
+ c = "*p";
+ cfsetispeed(&tty, B4800);
+ cfsetospeed(&tty, B4800);
+ break;
+ case 2400:
+ c = "*o";
+ cfsetispeed(&tty, B2400);
+ cfsetospeed(&tty, B2400);
+ break;
+ case 1200:
+ default:
+ c = "*n";
+ cfsetispeed(&tty, B1200);
+ cfsetospeed(&tty, B1200);
+ }
+
+ if (rodent.rtype == MOUSE_PROTO_LOGIMOUSEMAN
+ || rodent.rtype == MOUSE_PROTO_LOGI)
+ {
+ if (write(rodent.mfd, c, 2) != 2)
+ {
+ logwarn("unable to write to mouse fd");
+ return;
+ }
+ }
+ usleep(100000);
+
+ if (tcsetattr(rodent.mfd, TCSADRAIN, &tty) < 0)
+ logwarn("unable to set status of mouse fd");
+}
+
+/*
+ * PnP COM device support
+ *
+ * It's a simplistic implementation, but it works :-)
+ * KY, 31/7/97.
+ */
+
+/*
+ * Try to elicit a PnP ID as described in
+ * Microsoft, Hayes: "Plug and Play External COM Device Specification,
+ * rev 1.00", 1995.
+ *
+ * The routine does not fully implement the COM Enumerator as par Section
+ * 2.1 of the document. In particular, we don't have idle state in which
+ * the driver software monitors the com port for dynamic connection or
+ * removal of a device at the port, because `moused' simply quits if no
+ * device is found.
+ *
+ * In addition, as PnP COM device enumeration procedure slightly has
+ * changed since its first publication, devices which follow earlier
+ * revisions of the above spec. may fail to respond if the rev 1.0
+ * procedure is used. XXX
+ */
+static int
+pnpwakeup1(void)
+{
+ struct timeval timeout;
+ fd_set fds;
+ int i;
+
+ /*
+ * This is the procedure described in rev 1.0 of PnP COM device spec.
+ * Unfortunately, some devices which comform to earlier revisions of
+ * the spec gets confused and do not return the ID string...
+ */
+ debug("PnP COM device rev 1.0 probe...");
+
+ /* port initialization (2.1.2) */
+ ioctl(rodent.mfd, TIOCMGET, &i);
+ i |= TIOCM_DTR; /* DTR = 1 */
+ i &= ~TIOCM_RTS; /* RTS = 0 */
+ ioctl(rodent.mfd, TIOCMSET, &i);
+ usleep(240000);
+
+ /*
+ * The PnP COM device spec. dictates that the mouse must set DSR
+ * in response to DTR (by hardware or by software) and that if DSR is
+ * not asserted, the host computer should think that there is no device
+ * at this serial port. But some mice just don't do that...
+ */
+ ioctl(rodent.mfd, TIOCMGET, &i);
+ debug("modem status 0%o", i);
+ if ((i & TIOCM_DSR) == 0)
+ return FALSE;
+
+ /* port setup, 1st phase (2.1.3) */
+ setmousespeed(1200, 1200, (CS7 | CREAD | CLOCAL | HUPCL));
+ i = TIOCM_DTR | TIOCM_RTS; /* DTR = 0, RTS = 0 */
+ ioctl(rodent.mfd, TIOCMBIC, &i);
+ usleep(240000);
+ i = TIOCM_DTR; /* DTR = 1, RTS = 0 */
+ ioctl(rodent.mfd, TIOCMBIS, &i);
+ usleep(240000);
+
+ /* wait for response, 1st phase (2.1.4) */
+ i = FREAD;
+ ioctl(rodent.mfd, TIOCFLUSH, &i);
+ i = TIOCM_RTS; /* DTR = 1, RTS = 1 */
+ ioctl(rodent.mfd, TIOCMBIS, &i);
+
+ /* try to read something */
+ FD_ZERO(&fds);
+ FD_SET(rodent.mfd, &fds);
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 240000;
+ if (select(FD_SETSIZE, &fds, NULL, NULL, &timeout) > 0) {
+ debug("pnpwakeup1(): valid response in first phase.");
+ return TRUE;
+ }
+
+ /* port setup, 2nd phase (2.1.5) */
+ i = TIOCM_DTR | TIOCM_RTS; /* DTR = 0, RTS = 0 */
+ ioctl(rodent.mfd, TIOCMBIC, &i);
+ usleep(240000);
+
+ /* wait for respose, 2nd phase (2.1.6) */
+ i = FREAD;
+ ioctl(rodent.mfd, TIOCFLUSH, &i);
+ i = TIOCM_DTR | TIOCM_RTS; /* DTR = 1, RTS = 1 */
+ ioctl(rodent.mfd, TIOCMBIS, &i);
+
+ /* try to read something */
+ FD_ZERO(&fds);
+ FD_SET(rodent.mfd, &fds);
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 240000;
+ if (select(FD_SETSIZE, &fds, NULL, NULL, &timeout) > 0) {
+ debug("pnpwakeup1(): valid response in second phase.");
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static int
+pnpwakeup2(void)
+{
+ struct timeval timeout;
+ fd_set fds;
+ int i;
+
+ /*
+ * This is a simplified procedure; it simply toggles RTS.
+ */
+ debug("alternate probe...");
+
+ ioctl(rodent.mfd, TIOCMGET, &i);
+ i |= TIOCM_DTR; /* DTR = 1 */
+ i &= ~TIOCM_RTS; /* RTS = 0 */
+ ioctl(rodent.mfd, TIOCMSET, &i);
+ usleep(240000);
+
+ setmousespeed(1200, 1200, (CS7 | CREAD | CLOCAL | HUPCL));
+
+ /* wait for respose */
+ i = FREAD;
+ ioctl(rodent.mfd, TIOCFLUSH, &i);
+ i = TIOCM_DTR | TIOCM_RTS; /* DTR = 1, RTS = 1 */
+ ioctl(rodent.mfd, TIOCMBIS, &i);
+
+ /* try to read something */
+ FD_ZERO(&fds);
+ FD_SET(rodent.mfd, &fds);
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 240000;
+ if (select(FD_SETSIZE, &fds, NULL, NULL, &timeout) > 0) {
+ debug("pnpwakeup2(): valid response.");
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static int
+pnpgets(char *buf)
+{
+ struct timeval timeout;
+ fd_set fds;
+ int begin;
+ int i;
+ char c;
+
+ if (!pnpwakeup1() && !pnpwakeup2()) {
+ /*
+ * According to PnP spec, we should set DTR = 1 and RTS = 0 while
+ * in idle state. But, `moused' shall set DTR = RTS = 1 and proceed,
+ * assuming there is something at the port even if it didn't
+ * respond to the PnP enumeration procedure.
+ */
+ i = TIOCM_DTR | TIOCM_RTS; /* DTR = 1, RTS = 1 */
+ ioctl(rodent.mfd, TIOCMBIS, &i);
+ return 0;
+ }
+
+ /* collect PnP COM device ID (2.1.7) */
+ begin = -1;
+ i = 0;
+ usleep(240000); /* the mouse must send `Begin ID' within 200msec */
+ while (read(rodent.mfd, &c, 1) == 1) {
+ /* we may see "M", or "M3..." before `Begin ID' */
+ buf[i++] = c;
+ if ((c == 0x08) || (c == 0x28)) { /* Begin ID */
+ debug("begin-id %02x", c);
+ begin = i - 1;
+ break;
+ }
+ debug("%c %02x", c, c);
+ if (i >= 256)
+ break;
+ }
+ if (begin < 0) {
+ /* we haven't seen `Begin ID' in time... */
+ goto connect_idle;
+ }
+
+ ++c; /* make it `End ID' */
+ for (;;) {
+ FD_ZERO(&fds);
+ FD_SET(rodent.mfd, &fds);
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 240000;
+ if (select(FD_SETSIZE, &fds, NULL, NULL, &timeout) <= 0)
+ break;
+
+ read(rodent.mfd, &buf[i], 1);
+ if (buf[i++] == c) /* End ID */
+ break;
+ if (i >= 256)
+ break;
+ }
+ if (begin > 0) {
+ i -= begin;
+ bcopy(&buf[begin], &buf[0], i);
+ }
+ /* string may not be human readable... */
+ debug("len:%d, '%-*.*s'", i, i, i, buf);
+
+ if (buf[i - 1] == c)
+ return i; /* a valid PnP string */
+
+ /*
+ * According to PnP spec, we should set DTR = 1 and RTS = 0 while
+ * in idle state. But, `moused' shall leave the modem control lines
+ * as they are. See above.
+ */
+connect_idle:
+
+ /* we may still have something in the buffer */
+ return ((i > 0) ? i : 0);
+}
+
+static int
+pnpparse(pnpid_t *id, char *buf, int len)
+{
+ char s[3];
+ int offset;
+ int sum = 0;
+ int i, j;
+
+ id->revision = 0;
+ id->eisaid = NULL;
+ id->serial = NULL;
+ id->class = NULL;
+ id->compat = NULL;
+ id->description = NULL;
+ id->neisaid = 0;
+ id->nserial = 0;
+ id->nclass = 0;
+ id->ncompat = 0;
+ id->ndescription = 0;
+
+ if ((buf[0] != 0x28) && (buf[0] != 0x08)) {
+ /* non-PnP mice */
+ switch(buf[0]) {
+ default:
+ return FALSE;
+ case 'M': /* Microsoft */
+ id->eisaid = "PNP0F01";
+ break;
+ case 'H': /* MouseSystems */
+ id->eisaid = "PNP0F04";
+ break;
+ }
+ id->neisaid = strlen(id->eisaid);
+ id->class = "MOUSE";
+ id->nclass = strlen(id->class);
+ debug("non-PnP mouse '%c'", buf[0]);
+ return TRUE;
+ }
+
+ /* PnP mice */
+ offset = 0x28 - buf[0];
+
+ /* calculate checksum */
+ for (i = 0; i < len - 3; ++i) {
+ sum += buf[i];
+ buf[i] += offset;
+ }
+ sum += buf[len - 1];
+ for (; i < len; ++i)
+ buf[i] += offset;
+ debug("PnP ID string: '%*.*s'", len, len, buf);
+
+ /* revision */
+ buf[1] -= offset;
+ buf[2] -= offset;
+ id->revision = ((buf[1] & 0x3f) << 6) | (buf[2] & 0x3f);
+ debug("PnP rev %d.%02d", id->revision / 100, id->revision % 100);
+
+ /* EISA vender and product ID */
+ id->eisaid = &buf[3];
+ id->neisaid = 7;
+
+ /* option strings */
+ i = 10;
+ if (buf[i] == '\\') {
+ /* device serial # */
+ for (j = ++i; i < len; ++i) {
+ if (buf[i] == '\\')
+ break;
+ }
+ if (i >= len)
+ i -= 3;
+ if (i - j == 8) {
+ id->serial = &buf[j];
+ id->nserial = 8;
+ }
+ }
+ if (buf[i] == '\\') {
+ /* PnP class */
+ for (j = ++i; i < len; ++i) {
+ if (buf[i] == '\\')
+ break;
+ }
+ if (i >= len)
+ i -= 3;
+ if (i > j + 1) {
+ id->class = &buf[j];
+ id->nclass = i - j;
+ }
+ }
+ if (buf[i] == '\\') {
+ /* compatible driver */
+ for (j = ++i; i < len; ++i) {
+ if (buf[i] == '\\')
+ break;
+ }
+ /*
+ * PnP COM spec prior to v0.96 allowed '*' in this field,
+ * it's not allowed now; just igore it.
+ */
+ if (buf[j] == '*')
+ ++j;
+ if (i >= len)
+ i -= 3;
+ if (i > j + 1) {
+ id->compat = &buf[j];
+ id->ncompat = i - j;
+ }
+ }
+ if (buf[i] == '\\') {
+ /* product description */
+ for (j = ++i; i < len; ++i) {
+ if (buf[i] == ';')
+ break;
+ }
+ if (i >= len)
+ i -= 3;
+ if (i > j + 1) {
+ id->description = &buf[j];
+ id->ndescription = i - j;
+ }
+ }
+
+ /* checksum exists if there are any optional fields */
+ if ((id->nserial > 0) || (id->nclass > 0)
+ || (id->ncompat > 0) || (id->ndescription > 0)) {
+ debug("PnP checksum: 0x%X", sum);
+ sprintf(s, "%02X", sum & 0x0ff);
+ if (strncmp(s, &buf[len - 3], 2) != 0) {
+#if 0
+ /*
+ * I found some mice do not comply with the PnP COM device
+ * spec regarding checksum... XXX
+ */
+ logwarnx("PnP checksum error", 0);
+ return FALSE;
+#endif
+ }
+ }
+
+ return TRUE;
+}
+
+static symtab_t *
+pnpproto(pnpid_t *id)
+{
+ symtab_t *t;
+ int i, j;
+
+ if (id->nclass > 0)
+ if ( strncmp(id->class, "MOUSE", id->nclass) != 0 &&
+ strncmp(id->class, "TABLET", id->nclass) != 0)
+ /* this is not a mouse! */
+ return NULL;
+
+ if (id->neisaid > 0) {
+ t = gettoken(pnpprod, id->eisaid, id->neisaid);
+ if (t->val != MOUSE_PROTO_UNKNOWN)
+ return t;
+ }
+
+ /*
+ * The 'Compatible drivers' field may contain more than one
+ * ID separated by ','.
+ */
+ if (id->ncompat <= 0)
+ return NULL;
+ for (i = 0; i < id->ncompat; ++i) {
+ for (j = i; id->compat[i] != ','; ++i)
+ if (i >= id->ncompat)
+ break;
+ if (i > j) {
+ t = gettoken(pnpprod, id->compat + j, i - j);
+ if (t->val != MOUSE_PROTO_UNKNOWN)
+ return t;
+ }
+ }
+
+ return NULL;
+}
+
+/* name/val mapping */
+
+static symtab_t *
+gettoken(symtab_t *tab, char *s, int len)
+{
+ int i;
+
+ for (i = 0; tab[i].name != NULL; ++i) {
+ if (strncmp(tab[i].name, s, len) == 0)
+ break;
+ }
+ return &tab[i];
+}
+
+static char *
+gettokenname(symtab_t *tab, int val)
+{
+ int i;
+
+ for (i = 0; tab[i].name != NULL; ++i) {
+ if (tab[i].val == val)
+ return tab[i].name;
+ }
+ return NULL;
+}
+
+
+/*
+ * code to read from the Genius Kidspad tablet.
+
+The tablet responds to the COM PnP protocol 1.0 with EISA-ID KYE0005,
+and to pre-pnp probes (RTS toggle) with 'T' (tablet ?)
+9600, 8 bit, parity odd.
+
+The tablet puts out 5 bytes. b0 (mask 0xb8, value 0xb8) contains
+the proximity, tip and button info:
+ (byte0 & 0x1) true = tip pressed
+ (byte0 & 0x2) true = button pressed
+ (byte0 & 0x40) false = pen in proximity of tablet.
+
+The next 4 bytes are used for coordinates xl, xh, yl, yh (7 bits valid).
+
+Only absolute coordinates are returned, so we use the following approach:
+we store the last coordinates sent when the pen went out of the tablet,
+
+
+ *
+ */
+
+typedef enum {
+ S_IDLE, S_PROXY, S_FIRST, S_DOWN, S_UP
+} k_status ;
+
+static int
+kidspad(u_char rxc, mousestatus_t *act)
+{
+ static int buf[5];
+ static int buflen = 0, b_prev = 0 , x_prev = -1, y_prev = -1 ;
+ static k_status status = S_IDLE ;
+ static struct timeval old, now ;
+
+ int x, y ;
+
+ if (buflen > 0 && (rxc & 0x80) ) {
+ fprintf(stderr, "invalid code %d 0x%x\n", buflen, rxc);
+ buflen = 0 ;
+ }
+ if (buflen == 0 && (rxc & 0xb8) != 0xb8 ) {
+ fprintf(stderr, "invalid code 0 0x%x\n", rxc);
+ return 0 ; /* invalid code, no action */
+ }
+ buf[buflen++] = rxc ;
+ if (buflen < 5)
+ return 0 ;
+
+ buflen = 0 ; /* for next time... */
+
+ x = buf[1]+128*(buf[2] - 7) ;
+ if (x < 0) x = 0 ;
+ y = 28*128 - (buf[3] + 128* (buf[4] - 7)) ;
+ if (y < 0) y = 0 ;
+
+ x /= 8 ;
+ y /= 8 ;
+
+ act->flags = 0 ;
+ act->obutton = act->button ;
+ act->dx = act->dy = act->dz = 0 ;
+ gettimeofday(&now, NULL);
+ if ( buf[0] & 0x40 ) /* pen went out of reach */
+ status = S_IDLE ;
+ else if (status == S_IDLE) { /* pen is newly near the tablet */
+ act->flags |= MOUSE_POSCHANGED ; /* force update */
+ status = S_PROXY ;
+ x_prev = x ;
+ y_prev = y ;
+ }
+ old = now ;
+ act->dx = x - x_prev ;
+ act->dy = y - y_prev ;
+ if (act->dx || act->dy)
+ act->flags |= MOUSE_POSCHANGED ;
+ x_prev = x ;
+ y_prev = y ;
+ if (b_prev != 0 && b_prev != buf[0]) { /* possibly record button change */
+ act->button = 0 ;
+ if ( buf[0] & 0x01 ) /* tip pressed */
+ act->button |= MOUSE_BUTTON1DOWN ;
+ if ( buf[0] & 0x02 ) /* button pressed */
+ act->button |= MOUSE_BUTTON2DOWN ;
+ act->flags |= MOUSE_BUTTONSCHANGED ;
+ }
+ b_prev = buf[0] ;
+ return act->flags ;
+}
+
+static void
+mremote_serversetup()
+{
+ struct sockaddr_un ad;
+
+ /* Open a UNIX domain stream socket to listen for mouse remote clients */
+ unlink(_PATH_MOUSEREMOTE);
+
+ if ( (rodent.mremsfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
+ logerrx(1, "unable to create unix domain socket %s",_PATH_MOUSEREMOTE);
+
+ umask(0111);
+
+ bzero(&ad, sizeof(ad));
+ ad.sun_family = AF_UNIX;
+ strcpy(ad.sun_path, _PATH_MOUSEREMOTE);
+#ifndef SUN_LEN
+#define SUN_LEN(unp) ( ((char *)(unp)->sun_path - (char *)(unp)) + \
+ strlen((unp)->path) )
+#endif
+ if (bind(rodent.mremsfd, (struct sockaddr *) &ad, SUN_LEN(&ad)) < 0)
+ logerrx(1, "unable to bind unix domain socket %s", _PATH_MOUSEREMOTE);
+
+ listen(rodent.mremsfd, 1);
+}
+
+static void
+mremote_clientchg(int add)
+{
+ struct sockaddr_un ad;
+ int ad_len, fd;
+
+ if (rodent.rtype != MOUSE_PROTO_X10MOUSEREM)
+ return;
+
+ if ( add ) {
+ /* Accept client connection, if we don't already have one */
+ ad_len = sizeof(ad);
+ fd = accept(rodent.mremsfd, (struct sockaddr *) &ad, &ad_len);
+ if (fd < 0)
+ logwarnx("failed accept on mouse remote socket");
+
+ if ( rodent.mremcfd < 0 ) {
+ rodent.mremcfd = fd;
+ debug("remote client connect...accepted");
+ }
+ else {
+ close(fd);
+ debug("another remote client connect...disconnected");
+ }
+ }
+ else {
+ /* Client disconnected */
+ debug("remote client disconnected");
+ close( rodent.mremcfd );
+ rodent.mremcfd = -1;
+ }
+}
+
+
diff --git a/usr.sbin/mptable/Makefile b/usr.sbin/mptable/Makefile
new file mode 100644
index 0000000..cff7602
--- /dev/null
+++ b/usr.sbin/mptable/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+PROG= mptable
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mptable/mptable.1 b/usr.sbin/mptable/mptable.1
new file mode 100644
index 0000000..e8ff5ad
--- /dev/null
+++ b/usr.sbin/mptable/mptable.1
@@ -0,0 +1,69 @@
+.\" Copyright (c) 1996
+.\" Steve Passe <fsmp@FreeBSD.ORG>. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. The name of the developer may NOT be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd April 28, 1997
+.Dt MPTABLE 1
+.Os
+.Sh NAME
+.Nm mptable
+.Nd display MP configuration table
+.Sh SYNOPSIS
+.Nm
+.Op Fl dmesg
+.Op Fl verbose
+.Op Fl grope
+.Op Fl help
+.Sh DESCRIPTION
+The
+.Nm
+command finds and analyzes the MP configuration table on
+an Intel(tm) MP spec capable motherboard.
+It is useful for debugging an SMP kernel that will not boot, as well
+as examining the configuration of a system.
+It can be run with a UniProcessor kernel.
+.Pp
+It must be run with gid kmem privileges.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl dmesg
+Include a dmesg dump.
+.It Fl grope
+Look in areas it shouldn't NEED to, use ONLY as a last resort.
+.It Fl help
+Print a usage message and exits.
+.It Fl verbose
+Print extra info.
+.El
+.Sh SEE ALSO
+.Xr smp 4 ,
+.Xr dmesg 8
+.Sh HISTORY
+The
+.Nm
+command first appeared in
+.Fx 3.0 .
+.Sh AUTHORS
+.An Steve Passe Aq fsmp@FreeBSD.org
diff --git a/usr.sbin/mptable/mptable.c b/usr.sbin/mptable/mptable.c
new file mode 100644
index 0000000..6dd24d7
--- /dev/null
+++ b/usr.sbin/mptable/mptable.c
@@ -0,0 +1,1115 @@
+/*
+ * Copyright (c) 1996, by Steve Passe
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. The name of the developer may NOT be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * mptable.c
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#define VMAJOR 2
+#define VMINOR 0
+#define VDELTA 15
+
+/*
+ * this will cause the raw mp table to be dumped to /tmp/mpdump
+ *
+#define RAW_DUMP
+ */
+
+#define MP_SIG 0x5f504d5f /* _MP_ */
+#define EXTENDED_PROCESSING_READY
+#define OEM_PROCESSING_READY_NOT
+
+#include <sys/types.h>
+#include <err.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define SEP_LINE \
+"\n-------------------------------------------------------------------------------\n"
+
+#define SEP_LINE2 \
+"\n===============================================================================\n"
+
+/* EBDA is @ 40:0e in real-mode terms */
+#define EBDA_POINTER 0x040e /* location of EBDA pointer */
+
+/* CMOS 'top of mem' is @ 40:13 in real-mode terms */
+#define TOPOFMEM_POINTER 0x0413 /* BIOS: base memory size */
+
+#define DEFAULT_TOPOFMEM 0xa0000
+
+#define BIOS_BASE 0xf0000
+#define BIOS_BASE2 0xe0000
+#define BIOS_SIZE 0x10000
+#define ONE_KBYTE 1024
+
+#define GROPE_AREA1 0x80000
+#define GROPE_AREA2 0x90000
+#define GROPE_SIZE 0x10000
+
+#define PROCENTRY_FLAG_EN 0x01
+#define PROCENTRY_FLAG_BP 0x02
+#define IOAPICENTRY_FLAG_EN 0x01
+
+#define MAXPNSTR 132
+
+enum busTypes {
+ CBUS = 1,
+ CBUSII = 2,
+ EISA = 3,
+ ISA = 6,
+ PCI = 13,
+ XPRESS = 18,
+ MAX_BUSTYPE = 18,
+ UNKNOWN_BUSTYPE = 0xff
+};
+
+typedef struct BUSTYPENAME {
+ u_char type;
+ char name[ 7 ];
+} busTypeName;
+
+static busTypeName busTypeTable[] =
+{
+ { CBUS, "CBUS" },
+ { CBUSII, "CBUSII" },
+ { EISA, "EISA" },
+ { UNKNOWN_BUSTYPE, "---" },
+ { UNKNOWN_BUSTYPE, "---" },
+ { ISA, "ISA" },
+ { UNKNOWN_BUSTYPE, "---" },
+ { UNKNOWN_BUSTYPE, "---" },
+ { UNKNOWN_BUSTYPE, "---" },
+ { UNKNOWN_BUSTYPE, "---" },
+ { UNKNOWN_BUSTYPE, "---" },
+ { UNKNOWN_BUSTYPE, "---" },
+ { PCI, "PCI" },
+ { UNKNOWN_BUSTYPE, "---" },
+ { UNKNOWN_BUSTYPE, "---" },
+ { UNKNOWN_BUSTYPE, "---" },
+ { UNKNOWN_BUSTYPE, "---" },
+ { UNKNOWN_BUSTYPE, "---" },
+ { UNKNOWN_BUSTYPE, "---" }
+};
+
+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 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:")) != -1) {
+ switch(ch) {
+ case 'd':
+ if ( strcmp( optarg, "mesg") == 0 )
+ dmesg = 1;
+ else
+ dmesg = 0;
+ break;
+ case 'h':
+ if ( strcmp( optarg, "elp") == 0 )
+ usage();
+ break;
+ case 'g':
+ if ( strcmp( optarg, "rope") == 0 )
+ grope = 1;
+ break;
+ case 'v':
+ if ( strcmp( optarg, "erbose") == 0 )
+ verbose = 1;
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+ optreset = 1;
+ optind = 0;
+ }
+
+ /* open physical memory for access to MP structures */
+ if ( (pfd = open( _PATH_MEM, O_RDONLY )) < 0 )
+ err( 1, "mem open" );
+
+ /* probe for MP structures */
+ apic_probe( &paddr, &where );
+ if ( where <= 0 ) {
+ fprintf( stderr, "\n MP FPS NOT found,\n" );
+ fprintf( stderr, " suggest trying -grope option!!!\n\n" );
+ return 1;
+ }
+
+ if ( verbose )
+ printf( "\n MP FPS found in %s @ physical addr: 0x%08x\n",
+ whereStrings[ where - 1 ], paddr );
+
+ puts( SEP_LINE );
+
+ /* analyze the MP Floating Pointer Structure */
+ MPFloatingPointer( paddr, where, &mpfps );
+
+ puts( SEP_LINE );
+
+ /* check whether an MP config table exists */
+ if ( (defaultConfig = mpfps.mpfb1) )
+ MPConfigDefault( defaultConfig );
+ else
+ MPConfigTableHeader( mpfps.pap );
+
+ /* do a dmesg output */
+ if ( dmesg )
+ doDmesg();
+
+ puts( SEP_LINE2 );
+
+ return 0;
+}
+
+
+/*
+ * set PHYSICAL address of MP floating pointer structure
+ */
+#define NEXT(X) ((X) += 4)
+static void
+apic_probe( 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;
+ int count, c;
+ int type;
+ int oldtype, entrytype;
+
+ if ( pap == 0 ) {
+ printf( "MP Configuration Table Header MISSING!\n" );
+ exit( 1 );
+ }
+
+ /* convert physical address to virtual address */
+ paddr = (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;
+
+ oldtype = -1;
+ for (c = count; c; c--) {
+ entrytype = readType();
+ if (entrytype != oldtype)
+ printf("--\n");
+ if (entrytype < oldtype)
+ printf("MPTABLE OUT OF ORDER!\n");
+ switch (entrytype) {
+ case 0:
+ if (oldtype != 0)
+ printf( "Processors:\tAPIC ID\tVersion\tState"
+ "\t\tFamily\tModel\tStep\tFlags\n" );
+ oldtype = 0;
+ processorEntry();
+ break;
+
+ case 1:
+ if (oldtype != 1)
+ printf( "Bus:\t\tBus ID\tType\n" );
+ oldtype = 1;
+ busEntry();
+ break;
+
+ case 2:
+ if (oldtype != 2)
+ printf( "I/O APICs:\tAPIC ID\tVersion\tState\t\tAddress\n" );
+ oldtype = 2;
+ ioApicEntry();
+ break;
+
+ case 3:
+ if (oldtype != 3)
+ printf( "I/O Ints:\tType\tPolarity Trigger\tBus ID\t IRQ\tAPIC ID\tPIN#\n" );
+ oldtype = 3;
+ intEntry();
+ break;
+
+ case 4:
+ if (oldtype != 4)
+ printf( "Local Ints:\tType\tPolarity Trigger\tBus ID\t IRQ\tAPIC ID\tPIN#\n" );
+ oldtype = 4;
+ intEntry();
+ break;
+
+ default:
+ printf("MPTABLE HOSED! record type = %d\n", entrytype);
+ exit(1);
+ }
+ }
+
+
+#if defined( EXTENDED_PROCESSING_READY )
+ /* process any extended data */
+ if ( (totalSize = cth.extended_table_length) ) {
+ puts( SEP_LINE );
+
+ printf( "MP Config Extended Table Entries:\n\n" );
+
+ while ( totalSize > 0 ) {
+ switch ( type = readType() ) {
+ case 128:
+ sasEntry();
+ break;
+ case 129:
+ bhdEntry();
+ break;
+ case 130:
+ cbasmEntry();
+ break;
+ default:
+ printf( "Extended Table HOSED!\n" );
+ exit( 1 );
+ }
+
+ totalSize -= extendedtableEntryTypes[ type-128 ].length;
+ }
+ }
+#endif /* EXTENDED_PROCESSING_READY */
+
+ /* process any OEM data */
+ if ( cth.oem_table_pointer && (cth.oem_table_size > 0) ) {
+#if defined( OEM_PROCESSING_READY )
+# error your on your own here!
+ /* convert OEM table pointer to virtual address */
+ poemtp = (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, "%s seek", _PATH_MEM );
+}
+
+
+/*
+ *
+ */
+static void
+readEntry( void* entry, int size )
+{
+ if ( read( pfd, entry, size ) != size )
+ err( 1, "readEntry" );
+}
+
+
+static void
+processorEntry( void )
+{
+ ProcEntry entry;
+
+ /* read it into local memory */
+ readEntry( &entry, sizeof( entry ) );
+
+ /* count it */
+ ++ncpu;
+
+ printf( "\t\t%2d", entry.apicID );
+ printf( "\t 0x%2x", entry.apicVersion );
+
+ printf( "\t %s, %s",
+ (entry.cpuFlags & PROCENTRY_FLAG_BP) ? "BSP" : "AP",
+ (entry.cpuFlags & PROCENTRY_FLAG_EN) ? "usable" : "unusable" );
+
+ printf( "\t %d\t %d\t %d",
+ (entry.cpuSignature >> 8) & 0x0f,
+ (entry.cpuSignature >> 4) & 0x0f,
+ entry.cpuSignature & 0x0f );
+
+ printf( "\t 0x%04x\n", entry.featureFlags );
+}
+
+
+/*
+ *
+ */
+static int
+lookupBusType( char* name )
+{
+ int x;
+
+ for ( x = 0; x < MAX_BUSTYPE; ++x )
+ if ( strcmp( busTypeTable[ x ].name, name ) == 0 )
+ return busTypeTable[ x ].type;
+
+ return UNKNOWN_BUSTYPE;
+}
+
+
+static void
+busEntry( void )
+{
+ int x;
+ char name[ 8 ];
+ char c;
+ BusEntry entry;
+
+ /* read it into local memory */
+ readEntry( &entry, sizeof( entry ) );
+
+ /* count it */
+ ++nbus;
+
+ printf( "\t\t%2d", entry.busID );
+ printf( "\t " ); pnstr( entry.busType, 6 ); printf( "\n" );
+
+ for ( x = 0; x < 6; ++x ) {
+ if ( (c = entry.busType[ x ]) == ' ' )
+ break;
+ name[ x ] = c;
+ }
+ name[ x ] = '\0';
+ busses[ entry.busID ] = lookupBusType( name );
+}
+
+
+static void
+ioApicEntry( void )
+{
+ IOApicEntry entry;
+
+ /* read it into local memory */
+ readEntry( &entry, sizeof( entry ) );
+
+ /* count it */
+ ++napic;
+
+ printf( "\t\t%2d", entry.apicID );
+ printf( "\t 0x%02x", entry.apicVersion );
+ printf( "\t %s",
+ (entry.apicFlags & IOAPICENTRY_FLAG_EN) ? "usable" : "unusable" );
+ printf( "\t\t 0x%x\n", entry.apicAddress );
+
+ apics[ entry.apicID ] = entry.apicID;
+}
+
+
+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 - 128].name );
+ printf( " bus ID: %d", entry.busID );
+ printf( " address type: " );
+ switch ( entry.addressType ) {
+ case 0:
+ printf( "I/O address\n" );
+ break;
+ case 1:
+ printf( "memory address\n" );
+ break;
+ case 2:
+ printf( "prefetch address\n" );
+ break;
+ default:
+ printf( "UNKNOWN type\n" );
+ break;
+ }
+
+ printf( " address base: 0x%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 - 128].name );
+ printf( " bus ID: %d", entry.busID );
+ printf( " bus info: 0x%02x", entry.busInfo );
+ printf( " parent bus ID: %d\n", entry.busParent );
+}
+
+
+static void
+cbasmEntry( void )
+{
+ CbasmEntry entry;
+
+ /* read it into local memory */
+ readEntry( &entry, sizeof( entry ) );
+
+ printf( "--\n%s\n", extendedtableEntryTypes[entry.type - 128].name );
+ printf( " bus ID: %d", entry.busID );
+ printf( " address modifier: %s\n", (entry.addressMod & 0x01) ?
+ "subtract" : "add" );
+ printf( " predefined range: 0x%08x\n", entry.predefinedRange );
+}
+
+
+/*
+ * do a dmesg output
+ */
+static void
+doDmesg( void )
+{
+ puts( SEP_LINE );
+
+ printf( "dmesg output:\n\n" );
+ fflush( stdout );
+ system( "dmesg" );
+}
+
+
+/*
+ *
+ */
+static void
+pnstr( char* s, int c )
+{
+ char string[ MAXPNSTR + 1 ];
+
+ if ( c > MAXPNSTR )
+ c = MAXPNSTR;
+ strncpy( string, s, c );
+ string[ c ] = '\0';
+ printf( "%s", string );
+}
diff --git a/usr.sbin/mrouted/LICENSE b/usr.sbin/mrouted/LICENSE
new file mode 100644
index 0000000..ef7da47
--- /dev/null
+++ b/usr.sbin/mrouted/LICENSE
@@ -0,0 +1,48 @@
+
+The mrouted program is covered by the following license. Use of the
+mrouted program represents acceptance of these terms and conditions.
+
+1. STANFORD grants to LICENSEE a nonexclusive and nontransferable license
+to use, copy and modify the computer software ``mrouted'' (hereinafter
+called the ``Program''), upon the terms and conditions hereinafter set
+out and until Licensee discontinues use of the Licensed Program.
+
+2. LICENSEE acknowledges that the Program is a research tool still in
+the development state, that it is being supplied ``as is,'' without any
+accompanying services from STANFORD, and that this license is entered
+into in order to encourage scientific collaboration aimed at further
+development and application of the Program.
+
+3. LICENSEE may copy the Program and may sublicense others to use object
+code copies of the Program or any derivative version of the Program.
+All copies must contain all copyright and other proprietary notices found
+in the Program as provided by STANFORD. Title to copyright to the
+Program remains with STANFORD.
+
+4. LICENSEE may create derivative versions of the Program. LICENSEE
+hereby grants STANFORD a royalty-free license to use, copy, modify,
+distribute and sublicense any such derivative works. At the time
+LICENSEE provides a copy of a derivative version of the Program to a
+third party, LICENSEE shall provide STANFORD with one copy of the source
+code of the derivative version at no charge to STANFORD.
+
+5. STANFORD MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED.
+By way of example, but not limitation, STANFORD MAKES NO REPRESENTATION
+OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR
+THAT THE USE OF THE LICENSED PROGRAM WILL NOT INFRINGE ANY PATENTS,
+COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. STANFORD shall not be held liable
+for any liability nor for any direct, indirect or consequential damages
+with respect to any claim by LICENSEE or any third party on account of or
+arising from this Agreement or use of the Program.
+
+6. This agreement shall be construed, interpreted and applied in
+accordance with the State of California and any legal action arising
+out of this Agreement or use of the Program shall be filed in a court
+in the State of California.
+
+7. Nothing in this Agreement shall be construed as conferring rights to
+use in advertising, publicity or otherwise any trademark or the name
+of ``Stanford''.
+
+The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+Leland Stanford Junior University.
diff --git a/usr.sbin/mrouted/Makefile b/usr.sbin/mrouted/Makefile
new file mode 100644
index 0000000..43fe8b1
--- /dev/null
+++ b/usr.sbin/mrouted/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+SUBDIR= common mrouted mrinfo map-mbone mtrace testrsrr
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/mrouted/Makefile.inc b/usr.sbin/mrouted/Makefile.inc
new file mode 100644
index 0000000..a8e134e
--- /dev/null
+++ b/usr.sbin/mrouted/Makefile.inc
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+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..8b8ea9c
--- /dev/null
+++ b/usr.sbin/mrouted/callout.c
@@ -0,0 +1,250 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * callout.c,v 3.8.4.8 1998/01/06 01:58:45 fenner Exp
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include "defs.h"
+
+/* the code below implements a callout queue */
+static int id = 0;
+static struct timeout_q *Q = 0; /* pointer to the beginning of timeout queue */
+
+struct timeout_q {
+ struct timeout_q *next; /* next event */
+ int id;
+ cfunc_t func; /* function to call */
+ void *data; /* func's data */
+ int time; /* time offset to next event*/
+};
+
+#ifdef IGMP_DEBUG
+static void print_Q __P((void));
+#else
+#define print_Q()
+#endif
+
+void
+callout_init()
+{
+ Q = (struct timeout_q *) 0;
+}
+
+void
+free_all_callouts()
+{
+ struct timeout_q *p;
+
+ while (Q) {
+ p = Q;
+ Q = Q->next;
+ free(p);
+ }
+}
+
+
+/*
+ * elapsed_time seconds have passed; perform all the events that should
+ * happen.
+ */
+void
+age_callout_queue(elapsed_time)
+ int elapsed_time;
+{
+ struct timeout_q *ptr;
+ int i = 0;
+
+ for (ptr = Q; ptr; ptr = Q, i++) {
+ if (ptr->time > elapsed_time) {
+ ptr->time -= elapsed_time;
+ return;
+ } else {
+ elapsed_time -= ptr->time;
+ Q = Q->next;
+ IF_DEBUG(DEBUG_TIMEOUT)
+ log(LOG_DEBUG, 0, "about to call timeout %d (#%d)", ptr->id, i);
+ if (ptr->func)
+ ptr->func(ptr->data);
+ free(ptr);
+ }
+ }
+}
+
+/*
+ * Return in how many seconds age_callout_queue() would like to be called.
+ * Return -1 if there are no events pending.
+ */
+int
+timer_nextTimer()
+{
+ if (Q) {
+ if (Q->time < 0) {
+ log(LOG_WARNING, 0, "timer_nextTimer top of queue says %d",
+ Q->time);
+ return 0;
+ }
+ return Q->time;
+ }
+ return -1;
+}
+
+/*
+ * sets the timer
+ */
+int
+timer_setTimer(delay, action, data)
+ int delay; /* number of units for timeout */
+ cfunc_t action; /* function to be called on timeout */
+ void *data; /* what to call the timeout function with */
+{
+ struct timeout_q *ptr, *node, *prev;
+ int i = 0;
+
+ /* create a node */
+ node = (struct timeout_q *)malloc(sizeof(struct timeout_q));
+ if (node == 0) {
+ log(LOG_WARNING, 0, "Malloc Failed in timer_settimer\n");
+ return -1;
+ }
+ node->func = action;
+ node->data = data;
+ node->time = delay;
+ node->next = 0;
+ node->id = ++id;
+
+ prev = ptr = Q;
+
+ /* insert node in the queue */
+
+ /* if the queue is empty, insert the node and return */
+ if (!Q)
+ Q = node;
+ else {
+ /* chase the pointer looking for the right place */
+ while (ptr) {
+
+ if (delay < ptr->time) {
+ /* right place */
+
+ node->next = ptr;
+ if (ptr == Q)
+ Q = node;
+ else
+ prev->next = node;
+ ptr->time -= node->time;
+ print_Q();
+ IF_DEBUG(DEBUG_TIMEOUT)
+ log(LOG_DEBUG, 0, "created timeout %d (#%d)", node->id, i);
+ return node->id;
+ } else {
+ /* keep moving */
+
+ delay -= ptr->time; node->time = delay;
+ prev = ptr;
+ ptr = ptr->next;
+ }
+ i++;
+ }
+ prev->next = node;
+ }
+ print_Q();
+ IF_DEBUG(DEBUG_TIMEOUT)
+ log(LOG_DEBUG, 0, "created timeout %d (#%d)", node->id, i);
+ return node->id;
+}
+
+/* returns the time until the timer is scheduled */
+int
+timer_leftTimer(timer_id)
+ int timer_id;
+{
+ struct timeout_q *ptr;
+ int left = 0;
+
+ if (!timer_id)
+ return -1;
+
+ for (ptr = Q; ptr; ptr = ptr->next) {
+ left += ptr->time;
+ if (ptr->id == timer_id)
+ return left;
+ }
+ return -1;
+}
+
+/* clears the associated timer. Returns 1 if succeeded. */
+int
+timer_clearTimer(timer_id)
+ int timer_id;
+{
+ struct timeout_q *ptr, *prev;
+ int i = 0;
+
+ if (!timer_id)
+ return 0;
+
+ prev = ptr = Q;
+
+ /*
+ * find the right node, delete it. the subsequent node's time
+ * gets bumped up
+ */
+
+ print_Q();
+ while (ptr) {
+ if (ptr->id == timer_id) {
+ /* got the right node */
+
+ /* unlink it from the queue */
+ if (ptr == Q)
+ Q = Q->next;
+ else
+ prev->next = ptr->next;
+
+ /* increment next node if any */
+ if (ptr->next != 0)
+ (ptr->next)->time += ptr->time;
+
+ if (ptr->data)
+ free(ptr->data);
+ IF_DEBUG(DEBUG_TIMEOUT)
+ log(LOG_DEBUG, 0, "deleted timer %d (#%d)", ptr->id, i);
+ free(ptr);
+ print_Q();
+ return 1;
+ }
+ prev = ptr;
+ ptr = ptr->next;
+ i++;
+ }
+ IF_DEBUG(DEBUG_TIMEOUT)
+ log(LOG_DEBUG, 0, "failed to delete timer %d (#%d)", timer_id, i);
+ print_Q();
+ return 0;
+}
+
+#ifdef IGMP_DEBUG
+/*
+ * debugging utility
+ */
+static void
+print_Q()
+{
+ struct timeout_q *ptr;
+
+ IF_DEBUG(DEBUG_TIMEOUT)
+ for (ptr = Q; ptr; ptr = ptr->next)
+ log(LOG_DEBUG, 0, "(%d,%d) ", ptr->id, ptr->time);
+}
+#endif /* IGMP_DEBUG */
diff --git a/usr.sbin/mrouted/cfparse.y b/usr.sbin/mrouted/cfparse.y
new file mode 100644
index 0000000..fae52087
--- /dev/null
+++ b/usr.sbin/mrouted/cfparse.y
@@ -0,0 +1,932 @@
+%{
+/*
+ * Configuration file parser for mrouted.
+ *
+ * Written by Bill Fenner, NRL, 1994
+ *
+ * $FreeBSD$
+ * cfparse.y,v 3.8.4.30 1998/03/01 01:48:58 fenner Exp
+ */
+#include <stdio.h>
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+#include "defs.h"
+#include <netdb.h>
+
+/*
+ * Local function declarations
+ */
+static void fatal __P((char *fmt, ...)) __printflike(1, 2);
+static void warn __P((char *fmt, ...)) __printflike(1, 2);;
+static void yyerror __P((char *s));
+static char * next_word __P((void));
+static int yylex __P((void));
+static u_int32 valid_if __P((char *s));
+static struct ifreq * ifconfaddr __P((struct ifconf *ifcp, u_int32 a));
+int yyparse __P((void));
+
+static FILE *f;
+
+char *configfilename = _PATH_MROUTED_CONF;
+
+extern int cache_lifetime;
+extern int prune_lifetime;
+
+/* imported from config.c, with slight memory leak */
+extern struct ifconf ifc;
+
+int allow_black_holes = 0;
+
+static int lineno;
+
+static struct uvif *v;
+
+static int order, state;
+static int noflood = 0;
+static int rexmit = VIFF_REXMIT_PRUNES;
+
+struct addrmask {
+ u_int32 addr;
+ int mask;
+};
+
+struct boundnam {
+ char *name;
+ struct addrmask bound;
+};
+
+#define MAXBOUNDS 20
+
+struct boundnam boundlist[MAXBOUNDS]; /* Max. of 20 named boundaries */
+int numbounds = 0; /* Number of named boundaries */
+
+%}
+
+%union
+{
+ int num;
+ char *ptr;
+ struct addrmask addrmask;
+ u_int32 addr;
+ struct vf_element *filterelem;
+};
+
+%token CACHE_LIFETIME PRUNE_LIFETIME PRUNING BLACK_HOLE NOFLOOD
+%token PHYINT TUNNEL NAME
+%token DISABLE IGMPV1 SRCRT BESIDE
+%token METRIC THRESHOLD RATE_LIMIT BOUNDARY NETMASK ALTNET ADVERT_METRIC
+%token FILTER ACCEPT DENY EXACT BIDIR REXMIT_PRUNES REXMIT_PRUNES2
+%token PASSIVE ALLOW_NONPRUNERS
+%token NOTRANSIT BLASTER FORCE_LEAF
+%token PRUNE_LIFETIME2 NOFLOOD2
+%token SYSNAM SYSCONTACT SYSVERSION SYSLOCATION
+%token <num> BOOLEAN
+%token <num> NUMBER
+%token <ptr> STRING
+%token <addrmask> ADDRMASK
+%token <addr> ADDR
+
+%type <addr> interface addrname
+%type <addrmask> bound boundary addrmask
+%type <filterelem> filter filtlist filtelement filtelem
+
+%start conf
+
+%%
+
+conf : stmts
+ ;
+
+stmts : /* Empty */
+ | stmts stmt
+ ;
+
+stmt : error
+ | PHYINT interface {
+
+ vifi_t vifi;
+
+ state++;
+
+ if (order)
+ fatal("phyints must appear before tunnels");
+
+ for (vifi = 0, v = uvifs;
+ vifi < numvifs;
+ ++vifi, ++v)
+ if (!(v->uv_flags & VIFF_TUNNEL) &&
+ $2 == v->uv_lcl_addr)
+ break;
+
+ if (vifi == numvifs)
+ fatal("%s is not a configured interface",
+ inet_fmt($2,s1));
+
+ }
+ ifmods
+ | TUNNEL interface addrname {
+
+ struct ifreq *ifr;
+ struct ifreq ffr;
+ vifi_t vifi;
+
+ order++;
+
+ ifr = ifconfaddr(&ifc, $2);
+ if (ifr == 0)
+ fatal("Tunnel local address %s is not mine",
+ inet_fmt($2, s1));
+
+ if (((ntohl($2) & IN_CLASSA_NET) >> IN_CLASSA_NSHIFT) ==
+ IN_LOOPBACKNET)
+ fatal("Tunnel local address %s is a loopback address",
+ inet_fmt($2, s1));
+
+ if (ifconfaddr(&ifc, $3) != 0)
+ fatal("Tunnel remote address %s is one of mine",
+ inet_fmt($3, s1));
+
+ for (vifi = 0, v = uvifs;
+ vifi < numvifs;
+ ++vifi, ++v)
+ if (v->uv_flags & VIFF_TUNNEL) {
+ if ($3 == v->uv_rmt_addr)
+ fatal("Duplicate tunnel to %s",
+ inet_fmt($3, s1));
+ } else if (!(v->uv_flags & VIFF_DISABLED)) {
+ if (($3 & v->uv_subnetmask) == v->uv_subnet)
+ fatal("Unnecessary tunnel to %s, same subnet as vif %d (%s)",
+ inet_fmt($3,s1), vifi, v->uv_name);
+ }
+
+ if (numvifs == MAXVIFS)
+ fatal("too many vifs");
+
+ strncpy(ffr.ifr_name, ifr->ifr_name, IFNAMSIZ);
+ if (ioctl(udp_socket, SIOCGIFFLAGS, (char *)&ffr)<0)
+ fatal("ioctl SIOCGIFFLAGS on %s", ffr.ifr_name);
+
+ v = &uvifs[numvifs];
+ zero_vif(v, 1);
+ v->uv_flags = VIFF_TUNNEL | rexmit | noflood;
+ v->uv_flags |= VIFF_OTUNNEL; /*XXX*/
+ v->uv_lcl_addr = $2;
+ v->uv_rmt_addr = $3;
+ v->uv_dst_addr = $3;
+ strncpy(v->uv_name, ffr.ifr_name, IFNAMSIZ);
+ v->uv_name[IFNAMSIZ-1]='\0';
+
+ if (!(ffr.ifr_flags & IFF_UP)) {
+ v->uv_flags |= VIFF_DOWN;
+ vifs_down = TRUE;
+ }
+ }
+ tunnelmods
+ {
+
+ if (!(v->uv_flags & VIFF_OTUNNEL)) {
+ init_ipip_on_vif(v);
+ }
+
+ log(LOG_INFO, 0,
+ "installing tunnel from %s to %s as vif #%u - rate=%d",
+ inet_fmt($2, s1), inet_fmt($3, s2),
+ numvifs, v->uv_rate_limit);
+
+ ++numvifs;
+
+ }
+ | CACHE_LIFETIME NUMBER {
+
+ if ($2 < MIN_CACHE_LIFETIME) {
+ warn("cache_lifetime %d must be at least %d",
+ $2, MIN_CACHE_LIFETIME);
+ } else {
+ cache_lifetime = $2;
+ }
+
+ }
+ | PRUNE_LIFETIME NUMBER {
+
+ if ($2 < MIN_PRUNE_LIFETIME) {
+ warn("prune_lifetime %d must be at least %d",
+ $2, MIN_PRUNE_LIFETIME);
+ } else {
+ prune_lifetime = $2;
+ }
+
+ }
+ | PRUNING BOOLEAN {
+
+ if ($2 != 1) {
+ warn("Disabling pruning is no longer supported");
+ }
+
+ }
+ | BLACK_HOLE {
+#ifdef ALLOW_BLACK_HOLES
+ allow_black_holes = 1;
+#endif
+ }
+ /*
+ * Turn off initial flooding (until subordinateness is learned
+ * via route exchange) on all phyints and set the default for
+ * all further tunnels.
+ */
+ | NOFLOOD {
+
+ vifi_t vifi;
+
+ noflood = VIFF_NOFLOOD;
+ for (vifi = 0, v = uvifs;
+ vifi < numvifs;
+ ++vifi, ++v)
+ v->uv_flags |= VIFF_NOFLOOD;
+
+ }
+ /*
+ * Turn on prune retransmission on all interfaces.
+ * Tunnels default to retransmitting, so this just
+ * needs to turn on phyints.
+ */
+ | REXMIT_PRUNES {
+
+ vifi_t vifi;
+
+ for (vifi = 0, v = uvifs;
+ vifi < numvifs;
+ ++vifi, ++v)
+ v->uv_flags |= VIFF_REXMIT_PRUNES;
+
+ }
+ /*
+ * If true, do as above. If false, no need to turn
+ * it off for phyints since they default to not
+ * rexmit; need to set flag to not rexmit on tunnels.
+ */
+ | REXMIT_PRUNES BOOLEAN {
+
+ if ($2) {
+ vifi_t vifi;
+
+ for (vifi = 0, v = uvifs;
+ vifi < numvifs;
+ ++vifi, ++v)
+ v->uv_flags |= VIFF_REXMIT_PRUNES;
+ } else {
+ rexmit = 0;
+ }
+
+ }
+ | NAME STRING boundary { if (numbounds >= MAXBOUNDS) {
+ fatal("Too many named boundaries (max %d)", MAXBOUNDS);
+ }
+
+ boundlist[numbounds].name = malloc(strlen($2) + 1);
+ strcpy(boundlist[numbounds].name, $2);
+ boundlist[numbounds++].bound = $3;
+ }
+ | SYSNAM STRING {
+#ifdef SNMP
+ set_sysName($2);
+#endif /* SNMP */
+ }
+ | SYSCONTACT STRING {
+#ifdef SNMP
+ set_sysContact($2);
+#endif /* SNMP */
+ }
+ | SYSVERSION STRING {
+#ifdef SNMP
+ set_sysVersion($2);
+#endif /* SNMP */
+ }
+ | SYSLOCATION STRING {
+#ifdef SNMP
+ set_sysLocation($2);
+#endif /* SNMP */
+ }
+ ;
+
+tunnelmods : /* empty */
+ | tunnelmods tunnelmod
+ ;
+
+tunnelmod : mod
+ | BESIDE { v->uv_flags |= VIFF_OTUNNEL; }
+ | BESIDE BOOLEAN {
+
+ if ($2) {
+ v->uv_flags |= VIFF_OTUNNEL;
+ } else {
+ v->uv_flags &= ~VIFF_OTUNNEL;
+ }
+
+ }
+ | SRCRT { fatal("Source-route tunnels not supported"); }
+ ;
+
+ifmods : /* empty */
+ | ifmods ifmod
+ ;
+
+ifmod : mod
+ | DISABLE { v->uv_flags |= VIFF_DISABLED; }
+ | IGMPV1 { v->uv_flags |= VIFF_IGMPV1; }
+ | NETMASK addrname {
+ u_int32 subnet, mask;
+
+ mask = $2;
+ subnet = v->uv_lcl_addr & mask;
+ if (!inet_valid_subnet(subnet, mask))
+ fatal("Invalid netmask");
+ v->uv_subnet = subnet;
+ v->uv_subnetmask = mask;
+ v->uv_subnetbcast = subnet | ~mask;
+ }
+ | NETMASK {
+
+ warn("Expected address after netmask keyword, ignored");
+
+ }
+ | ALTNET addrmask {
+
+ struct phaddr *ph;
+
+ ph = (struct phaddr *)malloc(sizeof(struct phaddr));
+ if (ph == NULL)
+ fatal("out of memory");
+ if ($2.mask) {
+ VAL_TO_MASK(ph->pa_subnetmask, $2.mask);
+ } else
+ ph->pa_subnetmask = v->uv_subnetmask;
+ ph->pa_subnet = $2.addr & ph->pa_subnetmask;
+ ph->pa_subnetbcast = ph->pa_subnet | ~ph->pa_subnetmask;
+ if ($2.addr & ~ph->pa_subnetmask)
+ warn("Extra subnet %s/%d has host bits set",
+ inet_fmt($2.addr,s1), $2.mask);
+ ph->pa_next = v->uv_addrs;
+ v->uv_addrs = ph;
+
+ }
+ | ALTNET {
+
+ warn("Expected address after altnet keyword, ignored");
+
+ }
+ | FORCE_LEAF {
+
+ v->uv_flags |= VIFF_FORCE_LEAF;
+
+ }
+ | FORCE_LEAF BOOLEAN {
+
+ if ($2) {
+ v->uv_flags |= VIFF_FORCE_LEAF;
+ } else {
+ v->uv_flags &= ~VIFF_FORCE_LEAF;
+ }
+
+ }
+ ;
+
+mod : THRESHOLD NUMBER { if ($2 < 1 || $2 > 255)
+ fatal("Invalid threshold %d",$2);
+ v->uv_threshold = $2;
+ }
+ | THRESHOLD {
+
+ warn("Expected number after threshold keyword, ignored");
+
+ }
+ | METRIC NUMBER { if ($2 < 1 || $2 > UNREACHABLE)
+ fatal("Invalid metric %d",$2);
+ v->uv_metric = $2;
+ }
+ | METRIC {
+
+ warn("Expected number after metric keyword, ignored");
+
+ }
+ | ADVERT_METRIC NUMBER { if ($2 < 0 || $2 > UNREACHABLE - 1)
+ fatal("Invalid advert_metric %d", $2);
+ v->uv_admetric = $2;
+ }
+ | ADVERT_METRIC {
+
+ warn("Expected number after advert_metric keyword, ignored");
+
+ }
+ | RATE_LIMIT NUMBER { if ($2 > MAX_RATE_LIMIT)
+ fatal("Invalid rate_limit %d",$2);
+ v->uv_rate_limit = $2;
+ }
+ | RATE_LIMIT {
+
+ warn("Expected number after rate_limit keyword, ignored");
+
+ }
+ | BOUNDARY bound {
+
+ struct vif_acl *v_acl;
+
+ v_acl = (struct vif_acl *)malloc(sizeof(struct vif_acl));
+ if (v_acl == NULL)
+ fatal("out of memory");
+ VAL_TO_MASK(v_acl->acl_mask, $2.mask);
+ v_acl->acl_addr = $2.addr & v_acl->acl_mask;
+ if ($2.addr & ~v_acl->acl_mask)
+ warn("Boundary spec %s/%d has host bits set",
+ inet_fmt($2.addr,s1),$2.mask);
+ v_acl->acl_next = v->uv_acl;
+ v->uv_acl = v_acl;
+
+ }
+ | BOUNDARY {
+
+ warn("Expected boundary spec after boundary keyword, ignored");
+
+ }
+ | REXMIT_PRUNES2 {
+
+ v->uv_flags |= VIFF_REXMIT_PRUNES;
+
+ }
+ | REXMIT_PRUNES2 BOOLEAN {
+
+ if ($2) {
+ v->uv_flags |= VIFF_REXMIT_PRUNES;
+ } else {
+ v->uv_flags &= ~VIFF_REXMIT_PRUNES;
+ }
+
+ }
+ | PASSIVE {
+
+ v->uv_flags |= VIFF_PASSIVE;
+
+ }
+ | NOFLOOD2 {
+
+ v->uv_flags |= VIFF_NOFLOOD;
+
+ }
+ | NOTRANSIT {
+
+ v->uv_flags |= VIFF_NOTRANSIT;
+
+ }
+ | BLASTER {
+
+ v->uv_flags |= VIFF_BLASTER;
+ blaster_alloc(v - uvifs);
+
+ }
+ | ALLOW_NONPRUNERS {
+
+ v->uv_flags |= VIFF_ALLOW_NONPRUNERS;
+
+ }
+ | PRUNE_LIFETIME2 NUMBER {
+
+ if ($2 < MIN_PRUNE_LIFETIME) {
+ warn("prune_lifetime %d must be at least %d",
+ $2, MIN_PRUNE_LIFETIME);
+ } else {
+ v->uv_prune_lifetime = $2;
+ }
+
+ }
+ | ACCEPT filter {
+
+ if (v->uv_filter == NULL) {
+ struct vif_filter *v_filter;
+
+ v_filter = (struct vif_filter *)malloc(sizeof(struct vif_filter));
+ if (v_filter == NULL)
+ fatal("out of memory");
+ v_filter->vf_flags = 0;
+ v_filter->vf_type = VFT_ACCEPT;
+ v_filter->vf_filter = $2;
+ v->uv_filter = v_filter;
+ } else if (v->uv_filter->vf_type != VFT_ACCEPT) {
+ fatal("can't accept and deny");
+ } else {
+ struct vf_element *p;
+
+ p = v->uv_filter->vf_filter;
+ while (p->vfe_next)
+ p = p->vfe_next;
+ p->vfe_next = $2;
+ }
+
+ }
+ | ACCEPT {
+
+ warn("Expected filter spec after accept keyword, ignored");
+
+ }
+ | DENY filter {
+
+ if (v->uv_filter == NULL) {
+ struct vif_filter *v_filter;
+
+ v_filter = (struct vif_filter *)malloc(sizeof(struct vif_filter));
+ if (v_filter == NULL)
+ fatal("out of memory");
+ v_filter->vf_flags = 0;
+ v_filter->vf_type = VFT_DENY;
+ v_filter->vf_filter = $2;
+ v->uv_filter = v_filter;
+ } else if (v->uv_filter->vf_type != VFT_DENY) {
+ fatal("can't accept and deny");
+ } else {
+ struct vf_element *p;
+
+ p = v->uv_filter->vf_filter;
+ while (p->vfe_next)
+ p = p->vfe_next;
+ p->vfe_next = $2;
+ }
+
+ }
+ | DENY {
+
+ warn("Expected filter spec after deny keyword, ignored");
+
+ }
+ | BIDIR {
+
+ if (v->uv_filter == NULL) {
+ fatal("bidir goes after filters");
+ }
+ v->uv_filter->vf_flags |= VFF_BIDIR;
+
+ }
+ ;
+
+interface : ADDR { $$ = $1; }
+ | STRING {
+ $$ = valid_if($1);
+ if ($$ == 0)
+ fatal("Invalid interface name %s",$1);
+ }
+ ;
+
+addrname : ADDR { $$ = $1; }
+ | STRING { struct hostent *hp;
+
+ if ((hp = gethostbyname($1)) == NULL ||
+ hp->h_length != sizeof($$))
+ fatal("No such host %s", $1);
+
+ if (hp->h_addr_list[1])
+ fatal("Hostname %s does not %s",
+ $1, "map to a unique address");
+
+ bcopy(hp->h_addr_list[0], &$$,
+ hp->h_length);
+ }
+
+bound : boundary { $$ = $1; }
+ | STRING { int i;
+
+ for (i=0; i < numbounds; i++) {
+ if (!strcmp(boundlist[i].name, $1)) {
+ $$ = boundlist[i].bound;
+ break;
+ }
+ }
+ if (i == numbounds) {
+ fatal("Invalid boundary name %s",$1);
+ }
+ }
+ ;
+
+boundary : ADDRMASK {
+
+#ifdef ALLOW_BLACK_HOLES
+ if (!allow_black_holes)
+#endif
+ if ((ntohl($1.addr) & 0xff000000) != 0xef000000) {
+ fatal("Boundaries must be 239.x.x.x, not %s/%d",
+ inet_fmt($1.addr, s1), $1.mask);
+ }
+ $$ = $1;
+
+ }
+ ;
+
+addrmask : ADDRMASK { $$ = $1; }
+ | ADDR { $$.addr = $1; $$.mask = 0; }
+ ;
+
+filter : filtlist { $$ = $1; }
+ | STRING { fatal("named filters no implemented yet"); }
+ ;
+
+filtlist : filtelement { $$ = $1; }
+ | filtelement filtlist { $1->vfe_next = $2; $$ = $1; }
+ ;
+
+filtelement : filtelem { $$ = $1; }
+ | filtelem EXACT { $1->vfe_flags |= VFEF_EXACT; $$ = $1; }
+ ;
+
+filtelem : ADDRMASK {
+
+ struct vf_element *vfe;
+
+ vfe = (struct vf_element *)malloc(sizeof(struct vf_element));
+ if (vfe == NULL)
+ fatal("out of memory");
+
+ vfe->vfe_addr = $1.addr;
+ VAL_TO_MASK(vfe->vfe_mask, $1.mask);
+ vfe->vfe_flags = 0;
+ vfe->vfe_next = NULL;
+
+ $$ = vfe;
+
+ }
+%%
+#ifdef __STDC__
+static void
+fatal(char *fmt, ...)
+{
+ va_list ap;
+ char buf[MAXHOSTNAMELEN + 100];
+
+ va_start(ap, fmt);
+#else
+/*VARARGS1*/
+static void
+fatal(fmt, va_alist)
+char *fmt;
+va_dcl
+{
+ va_list ap;
+ char buf[MAXHOSTNAMELEN + 100];
+
+ va_start(ap);
+#endif
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ log(LOG_ERR,0,"%s: %s near line %d", configfilename, buf, lineno);
+}
+
+#ifdef __STDC__
+static void
+warn(char *fmt, ...)
+{
+ va_list ap;
+ char buf[200];
+
+ va_start(ap, fmt);
+#else
+/*VARARGS1*/
+static void
+warn(fmt, va_alist)
+char *fmt;
+va_dcl
+{
+ va_list ap;
+ char buf[200];
+
+ va_start(ap);
+#endif
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ log(LOG_WARNING,0,"%s: %s near line %d", configfilename, buf, lineno);
+}
+
+static void
+yyerror(s)
+char *s;
+{
+ log(LOG_ERR, 0, "%s: %s near line %d", configfilename, s, lineno);
+}
+
+static char *
+next_word()
+{
+ static char buf[1024];
+ static char *p=NULL;
+ char *q;
+
+ while (1) {
+ if (!p || !*p) {
+ lineno++;
+ if (fgets(buf, sizeof(buf), f) == NULL)
+ return NULL;
+ p = buf;
+ }
+ while (*p && (*p == ' ' || *p == '\t')) /* skip whitespace */
+ p++;
+ if (*p == '#') {
+ p = NULL; /* skip comments */
+ continue;
+ }
+ q = p;
+#ifdef SNMP
+ if (*p == '"') {
+ p++;
+ while (*p && *p != '"' && *p != '\n')
+ p++; /* find next whitespace */
+ if (*p == '"')
+ p++;
+ } else
+#endif
+ while (*p && *p != ' ' && *p != '\t' && *p != '\n')
+ p++; /* find next whitespace */
+ *p++ = '\0'; /* null-terminate string */
+
+ if (!*q) {
+ p = NULL;
+ continue; /* if 0-length string, read another line */
+ }
+
+ return q;
+ }
+}
+
+/*
+ * List of keywords. Must have an empty record at the end to terminate
+ * list. If a second value is specified, the first is used at the beginning
+ * of the file and the second is used while parsing interfaces (e.g. after
+ * the first "phyint" or "tunnel" keyword).
+ */
+static struct keyword {
+ char *word;
+ int val1;
+ int val2;
+} words[] = {
+ { "cache_lifetime", CACHE_LIFETIME },
+ { "prune_lifetime", PRUNE_LIFETIME, PRUNE_LIFETIME2 },
+ { "pruning", PRUNING },
+ { "phyint", PHYINT },
+ { "tunnel", TUNNEL },
+ { "disable", DISABLE },
+ { "metric", METRIC },
+ { "advert_metric", ADVERT_METRIC },
+ { "threshold", THRESHOLD },
+ { "rate_limit", RATE_LIMIT },
+ { "force_leaf", FORCE_LEAF },
+ { "srcrt", SRCRT },
+ { "sourceroute", SRCRT },
+ { "boundary", BOUNDARY },
+ { "netmask", NETMASK },
+ { "igmpv1", IGMPV1 },
+ { "altnet", ALTNET },
+ { "name", NAME },
+ { "accept", ACCEPT },
+ { "deny", DENY },
+ { "exact", EXACT },
+ { "bidir", BIDIR },
+ { "allow_nonpruners", ALLOW_NONPRUNERS },
+#ifdef ALLOW_BLACK_HOLES
+ { "allow_black_holes", BLACK_HOLE },
+#endif
+ { "noflood", NOFLOOD, NOFLOOD2},
+ { "notransit", NOTRANSIT },
+ { "blaster", BLASTER },
+ { "rexmit_prunes", REXMIT_PRUNES, REXMIT_PRUNES2 },
+ { "passive", PASSIVE },
+ { "beside", BESIDE },
+#ifdef SNMP
+ { "sysName", SYSNAM },
+ { "sysContact", SYSCONTACT },
+ { "sysVersion", SYSVERSION },
+ { "sysLocation", SYSLOCATION },
+#endif
+ { NULL, 0 }
+};
+
+
+static int
+yylex()
+{
+ int n;
+ u_int32 addr;
+ char *q;
+ struct keyword *w;
+
+ if ((q = next_word()) == NULL) {
+ return 0;
+ }
+
+ for (w = words; w->word; w++)
+ if (!strcmp(q, w->word))
+ return (state && w->val2) ? w->val2 : w->val1;
+
+ if (!strcmp(q,"on") || !strcmp(q,"yes")) {
+ yylval.num = 1;
+ return BOOLEAN;
+ }
+ if (!strcmp(q,"off") || !strcmp(q,"no")) {
+ yylval.num = 0;
+ return BOOLEAN;
+ }
+ if (!strcmp(q,"default")) {
+ yylval.addrmask.mask = 0;
+ yylval.addrmask.addr = 0;
+ return ADDRMASK;
+ }
+ if (sscanf(q,"%[.0-9]/%d%c",s1,&n,s2) == 2) {
+ if ((addr = inet_parse(s1,1)) != 0xffffffff) {
+ yylval.addrmask.mask = n;
+ yylval.addrmask.addr = addr;
+ return ADDRMASK;
+ }
+ /* fall through to returning STRING */
+ }
+ if (sscanf(q,"%[.0-9]%c",s1,s2) == 1) {
+ if ((addr = inet_parse(s1,4)) != 0xffffffff &&
+ inet_valid_host(addr)) {
+ yylval.addr = addr;
+ return ADDR;
+ }
+ }
+ if (sscanf(q,"0x%8x%c",&n,s1) == 1) {
+ yylval.addr = n;
+ return ADDR;
+ }
+ if (sscanf(q,"%d%c",&n,s1) == 1) {
+ yylval.num = n;
+ return NUMBER;
+ }
+#ifdef SNMP
+ if (*q=='"') {
+ if (q[ strlen(q)-1 ]=='"')
+ q[ strlen(q)-1 ]='\0'; /* trash trailing quote */
+ yylval.ptr = q+1;
+ return STRING;
+ }
+#endif
+ yylval.ptr = q;
+ return STRING;
+}
+
+void
+config_vifs_from_file()
+{
+ order = 0;
+ state = 0;
+ numbounds = 0;
+ lineno = 0;
+
+ if ((f = fopen(configfilename, "r")) == NULL) {
+ if (errno != ENOENT)
+ log(LOG_ERR, errno, "can't open %s", configfilename);
+ return;
+ }
+
+ yyparse();
+
+ fclose(f);
+}
+
+static u_int32
+valid_if(s)
+char *s;
+{
+ register vifi_t vifi;
+ register struct uvif *v;
+
+ for (vifi=0, v=uvifs; vifi<numvifs; vifi++, v++)
+ if (!strcmp(v->uv_name, s))
+ return v->uv_lcl_addr;
+
+ return 0;
+}
+
+static struct ifreq *
+ifconfaddr(ifcp, a)
+ struct ifconf *ifcp;
+ u_int32 a;
+{
+ int n;
+ struct ifreq *ifrp = (struct ifreq *)ifcp->ifc_buf;
+ struct ifreq *ifend = (struct ifreq *)((char *)ifrp + ifcp->ifc_len);
+
+ while (ifrp < ifend) {
+ if (ifrp->ifr_addr.sa_family == AF_INET &&
+ ((struct sockaddr_in *)&ifrp->ifr_addr)->sin_addr.s_addr == a)
+ return (ifrp);
+#ifdef HAVE_SA_LEN
+ n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name);
+ if (n < sizeof(*ifrp))
+ ++ifrp;
+ else
+ ifrp = (struct ifreq *)((char *)ifrp + n);
+#else
+ ++ifrp;
+#endif
+ }
+ return (0);
+}
diff --git a/usr.sbin/mrouted/common/Makefile b/usr.sbin/mrouted/common/Makefile
new file mode 100644
index 0000000..4a286c6
--- /dev/null
+++ b/usr.sbin/mrouted/common/Makefile
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+S= ${.CURDIR}/..
+.PATH: $S
+
+LIB= mrouted
+INTERNALLIB= YES
+SRCS= igmp.c inet.c kern.c
+
+CFLAGS+= -I$S
+
+.include <bsd.lib.mk>
diff --git a/usr.sbin/mrouted/config.c b/usr.sbin/mrouted/config.c
new file mode 100644
index 0000000..5b436e6
--- /dev/null
+++ b/usr.sbin/mrouted/config.c
@@ -0,0 +1,175 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * config.c,v 3.8.4.10 1998/01/06 01:57:41 fenner Exp
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include "defs.h"
+
+
+struct ifconf ifc;
+
+/*
+ * Query the kernel to find network interfaces that are multicast-capable
+ * and install them in the uvifs array.
+ */
+void
+config_vifs_from_kernel()
+{
+ struct ifreq *ifrp, *ifend;
+ register struct uvif *v;
+ register vifi_t vifi;
+ int n;
+ u_int32 addr, mask, subnet;
+ 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..267c158
--- /dev/null
+++ b/usr.sbin/mrouted/defs.h
@@ -0,0 +1,413 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $FreeBSD$
+ * defs.h,v 3.8.4.15 1998/03/01 02:51:42 fenner Exp
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <errno.h>
+#include <syslog.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#ifdef SYSV
+#include <sys/sockio.h>
+#endif
+#ifdef _AIX
+#include <time.h>
+#endif
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <net/if.h>
+#define rtentry kern_rtentry /* XXX !!! UGH */
+#include <net/route.h>
+#undef rtentry
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/igmp.h>
+#ifdef __FreeBSD__ /* sigh */
+#include <osreldate.h>
+#if __FreeBSD_version >= 220000
+#define rtentry kernel_rtentry
+#include <net/route.h>
+#undef rtentry
+#endif
+#endif
+#include <netinet/ip_mroute.h>
+#ifdef RSRR
+#include <sys/un.h>
+#endif /* RSRR */
+
+/*XXX*/
+typedef u_int u_int32;
+
+#ifndef __P
+#ifdef __STDC__
+#define __P(x) x
+#else
+#define __P(x) ()
+#endif
+#endif
+
+typedef void (*cfunc_t) __P((void *));
+typedef void (*ihfunc_t) __P((int, fd_set *));
+
+#include "dvmrp.h"
+#include "igmpv2.h"
+#include "vif.h"
+#include "route.h"
+#include "prune.h"
+#include "pathnames.h"
+#ifdef RSRR
+#include "rsrr.h"
+#include "rsrr_var.h"
+#endif /* RSRR */
+
+/*
+ * Miscellaneous constants and macros.
+ */
+#define FALSE 0
+#define TRUE 1
+
+#define EQUAL(s1, s2) (strcmp((s1), (s2)) == 0)
+
+#define TIMER_INTERVAL ROUTE_MAX_REPORT_DELAY
+
+#define PROTOCOL_VERSION 3 /* increment when packet format/content changes */
+#define MROUTED_VERSION 9 /* not in DVMRP packets at all */
+
+#define DVMRP_CONSTANT 0x000eff00 /* constant portion of 'group' field */
+
+#define MROUTED_LEVEL (DVMRP_CONSTANT | PROTOCOL_VERSION)
+ /* for IGMP 'group' field of DVMRP messages */
+
+#define LEAF_FLAGS (( vifs_with_neighbors == 1 ) ? 0x010000 : 0)
+ /* more for IGMP 'group' field of DVMRP messages */
+
+#define DEL_RTE_GROUP 0
+#define DEL_ALL_ROUTES 1
+ /* for Deleting kernel table entries */
+
+/* obnoxious gcc gives an extraneous warning about this constant... */
+#if defined(__STDC__) || defined(__GNUC__)
+#define JAN_1970 2208988800UL /* 1970 - 1900 in seconds */
+#else
+#define JAN_1970 2208988800L /* 1970 - 1900 in seconds */
+#define const /**/
+#endif
+
+#ifdef RSRR
+#define BIT_ZERO(X) ((X) = 0)
+#define BIT_SET(X,n) ((X) |= 1 << (n))
+#define BIT_CLR(X,n) ((X) &= ~(1 << (n)))
+#define BIT_TST(X,n) ((X) & 1 << (n))
+#endif /* RSRR */
+
+#ifdef SYSV
+#define bcopy(a, b, c) memcpy(b, a, c)
+#define bzero(s, n) memset((s), 0, (n))
+#define setlinebuf(s) setvbuf(s, NULL, _IOLBF, 0)
+#endif
+
+#if defined(_AIX) || (defined(BSD) && (BSD >= 199103))
+#define HAVE_SA_LEN
+#endif
+
+/*
+ * External declarations for global variables and functions.
+ */
+#define RECV_BUF_SIZE 8192
+extern char *recv_buf;
+extern char *send_buf;
+extern int igmp_socket;
+#ifdef RSRR
+extern int rsrr_socket;
+#endif /* RSRR */
+extern u_int32 allhosts_group;
+extern u_int32 allrtrs_group;
+extern u_int32 dvmrp_group;
+extern u_int32 dvmrp_genid;
+
+#define IF_DEBUG(l) if (debug && debug & (l))
+
+#define DEBUG_PKT 0x0001
+#define DEBUG_PRUNE 0x0002
+#define DEBUG_ROUTE 0x0004
+#define DEBUG_PEER 0x0008
+#define DEBUG_CACHE 0x0010
+#define DEBUG_TIMEOUT 0x0020
+#define DEBUG_IF 0x0040
+#define DEBUG_MEMBER 0x0080
+#define DEBUG_TRACE 0x0100
+#define DEBUG_IGMP 0x0200
+#define DEBUG_RTDETAIL 0x0400
+#define DEBUG_KERN 0x0800
+#define DEBUG_RSRR 0x1000
+#define DEBUG_ICMP 0x2000
+
+#define DEFAULT_DEBUG 0x02de /* default if "-d" given without value */
+
+extern int debug;
+extern int did_final_init;
+
+extern int routes_changed;
+extern int delay_change_reports;
+extern unsigned nroutes;
+
+extern struct uvif uvifs[MAXVIFS];
+extern vifi_t numvifs;
+extern int vifs_down;
+extern int udp_socket;
+extern int vifs_with_neighbors;
+
+extern char s1[];
+extern char s2[];
+extern char s3[];
+extern char s4[];
+
+#if !(defined(BSD) && (BSD >= 199103))
+extern int errno;
+extern int sys_nerr;
+extern char * sys_errlist[];
+#endif
+
+#ifdef OLD_KERNEL
+#define MRT_INIT DVMRP_INIT
+#define MRT_DONE DVMRP_DONE
+#define MRT_ADD_VIF DVMRP_ADD_VIF
+#define MRT_DEL_VIF DVMRP_DEL_VIF
+#define MRT_ADD_MFC DVMRP_ADD_MFC
+#define MRT_DEL_MFC DVMRP_DEL_MFC
+#endif
+
+#ifndef IGMP_PIM
+#define IGMP_PIM 0x14
+#endif
+#ifndef IPPROTO_IPIP
+#define IPPROTO_IPIP 4
+#endif
+
+/*
+ * The original multicast releases defined
+ * IGMP_HOST_{MEMBERSHIP_QUERY,MEMBERSHIP_REPORT,NEW_MEMBERSHIP_REPORT
+ * ,LEAVE_MESSAGE}. Later releases removed the HOST and inserted
+ * the IGMP version number. NetBSD inserted the version number in
+ * a different way. mrouted uses the new names, so we #define them
+ * to the old ones if needed.
+ */
+#if !defined(IGMP_MEMBERSHIP_QUERY) && defined(IGMP_HOST_MEMBERSHIP_QUERY)
+#define IGMP_MEMBERSHIP_QUERY IGMP_HOST_MEMBERSHIP_QUERY
+#define IGMP_V2_LEAVE_GROUP IGMP_HOST_LEAVE_MESSAGE
+#endif
+#ifndef IGMP_V1_MEMBERSHIP_REPORT
+#ifdef IGMP_HOST_MEMBERSHIP_REPORT
+#define IGMP_V1_MEMBERSHIP_REPORT IGMP_HOST_MEMBERSHIP_REPORT
+#define IGMP_V2_MEMBERSHIP_REPORT IGMP_HOST_NEW_MEMBERSHIP_REPORT
+#endif
+#ifdef IGMP_v1_HOST_MEMBERSHIP_REPORT
+#define IGMP_V1_MEMBERSHIP_REPORT IGMP_v1_HOST_MEMBERSHIP_REPORT
+#define IGMP_V2_MEMBERSHIP_REPORT IGMP_v2_HOST_MEMBERSHIP_REPORT
+#endif
+#endif
+
+/*
+ * NetBSD also renamed the mtrace types.
+ */
+#if !defined(IGMP_MTRACE_RESP) && defined(IGMP_MTRACE_REPLY)
+#define IGMP_MTRACE_RESP IGMP_MTRACE_REPLY
+#define IGMP_MTRACE IGMP_MTRACE_QUERY
+#endif
+
+/* main.c */
+extern char * scaletime __P((u_long));
+extern void log __P((int, int, char *, ...)) __printflike(3, 4);
+extern int register_input_handler __P((int, ihfunc_t));
+
+/* igmp.c */
+extern void init_igmp __P((void));
+extern void accept_igmp __P((int));
+extern void build_igmp __P((u_int32, u_int32, int, int, u_int32,
+ int));
+extern void send_igmp __P((u_int32, u_int32, int, int, u_int32,
+ int));
+extern char * igmp_packet_kind __P((u_int, u_int));
+extern int igmp_debug_kind __P((u_int, u_int));
+
+/* icmp.c */
+extern void init_icmp __P((void));
+
+/* ipip.c */
+extern void init_ipip __P((void));
+extern void init_ipip_on_vif __P((struct uvif *));
+extern void send_ipip __P((u_int32, u_int32, int, int, u_int32,
+ int, struct uvif *));
+
+/* callout.c */
+extern void callout_init __P((void));
+extern void free_all_callouts __P((void));
+extern void age_callout_queue __P((int));
+extern int timer_nextTimer __P((void));
+extern int timer_setTimer __P((int, cfunc_t, void *));
+extern int timer_clearTimer __P((int));
+extern int timer_leftTimer __P((int));
+
+/* route.c */
+extern void init_routes __P((void));
+extern void start_route_updates __P((void));
+extern void update_route __P((u_int32, u_int32, u_int, u_int32,
+ vifi_t, struct listaddr *));
+extern void age_routes __P((void));
+extern void expire_all_routes __P((void));
+extern void free_all_routes __P((void));
+extern void accept_probe __P((u_int32, u_int32, char *, int,
+ u_int32));
+extern void accept_report __P((u_int32, u_int32, char *, int,
+ u_int32));
+extern struct rtentry * determine_route __P((u_int32 src));
+extern void report __P((int, vifi_t, u_int32));
+extern void report_to_all_neighbors __P((int));
+extern int report_next_chunk __P((void));
+extern void blaster_alloc __P((vifi_t));
+extern void add_vif_to_routes __P((vifi_t));
+extern void delete_vif_from_routes __P((vifi_t));
+extern void add_neighbor_to_routes __P((vifi_t, int));
+extern void delete_neighbor_from_routes __P((u_int32,
+ vifi_t, int));
+extern void dump_routes __P((FILE *fp));
+
+/* vif.c */
+extern void init_vifs __P((void));
+extern void zero_vif __P((struct uvif *, int));
+extern void init_installvifs __P((void));
+extern void check_vif_state __P((void));
+extern void send_on_vif __P((struct uvif *, u_int32, int, int));
+extern vifi_t find_vif __P((u_int32, u_int32));
+extern void age_vifs __P((void));
+extern void dump_vifs __P((FILE *));
+extern void stop_all_vifs __P((void));
+extern struct listaddr *neighbor_info __P((vifi_t, u_int32));
+extern void accept_group_report __P((u_int32, u_int32,
+ u_int32, int));
+extern void query_groups __P((void));
+extern void probe_for_neighbors __P((void));
+extern struct listaddr *update_neighbor __P((vifi_t, u_int32, int, char *, int,
+ u_int32));
+extern void accept_neighbor_request __P((u_int32, u_int32));
+extern void accept_neighbor_request2 __P((u_int32, u_int32));
+extern void accept_info_request __P((u_int32, u_int32,
+ u_char *, int));
+extern void accept_info_reply __P((u_int32, u_int32,
+ u_char *, int));
+extern void accept_neighbors __P((u_int32, u_int32,
+ u_char *, int, u_int32));
+extern void accept_neighbors2 __P((u_int32, u_int32,
+ u_char *, int, u_int32));
+extern void accept_leave_message __P((u_int32, u_int32,
+ u_int32));
+extern void accept_membership_query __P((u_int32, u_int32,
+ u_int32, int));
+
+/* config.c */
+extern void config_vifs_from_kernel __P((void));
+
+/* cfparse.y */
+extern void config_vifs_from_file __P((void));
+
+/* inet.c */
+extern int inet_valid_host __P((u_int32));
+extern int inet_valid_mask __P((u_int32));
+extern int inet_valid_subnet __P((u_int32, u_int32));
+extern char * inet_fmt __P((u_int32, char *));
+extern char * inet_fmts __P((u_int32, u_int32, char *));
+extern u_int32 inet_parse __P((char *, int));
+extern int inet_cksum __P((u_short *, u_int));
+
+/* prune.c */
+extern unsigned kroutes;
+extern void determine_forwvifs __P((struct gtable *));
+extern void send_prune_or_graft __P((struct gtable *));
+extern void add_table_entry __P((u_int32, u_int32));
+extern void del_table_entry __P((struct rtentry *,
+ u_int32, u_int));
+extern void update_table_entry __P((struct rtentry *, u_int32));
+extern int find_src_grp __P((u_int32, u_int32, u_int32));
+extern void init_ktable __P((void));
+extern void steal_sources __P((struct rtentry *));
+extern void reset_neighbor_state __P((vifi_t, u_int32));
+extern int grplst_mem __P((vifi_t, u_int32));
+extern void free_all_prunes __P((void));
+extern void age_table_entry __P((void));
+extern void dump_cache __P((FILE *));
+extern void update_lclgrp __P((vifi_t, u_int32));
+extern void delete_lclgrp __P((vifi_t, u_int32));
+extern void chkgrp_graft __P((vifi_t, u_int32));
+extern void accept_prune __P((u_int32, u_int32, char *, int));
+extern void accept_graft __P((u_int32, u_int32, char *, int));
+extern void accept_g_ack __P((u_int32, u_int32, char *, int));
+/* u_int is promoted u_char */
+extern void accept_mtrace __P((u_int32, u_int32,
+ u_int32, char *, u_int, int));
+
+/* kern.c */
+extern void k_set_rcvbuf __P((int, int));
+extern void k_hdr_include __P((int));
+extern void k_set_ttl __P((int));
+extern void k_set_loop __P((int));
+extern void k_set_if __P((u_int32));
+extern void k_join __P((u_int32, u_int32));
+extern void k_leave __P((u_int32, u_int32));
+extern void k_init_dvmrp __P((void));
+extern void k_stop_dvmrp __P((void));
+extern void k_add_vif __P((vifi_t, struct uvif *));
+extern void k_del_vif __P((vifi_t));
+extern void k_add_rg __P((u_int32, struct gtable *));
+extern int k_del_rg __P((u_int32, struct gtable *));
+extern int k_get_version __P((void));
+
+#ifdef SNMP
+/* prune.c */
+extern struct gtable * find_grp __P((u_int32));
+extern struct stable * find_grp_src __P((struct gtable *, u_int32));
+extern int next_grp_src_mask __P((struct gtable **,
+ struct stable **, u_int32,
+ u_int32, u_int32));
+extern void refresh_sg __P((struct sioc_sg_req *, struct gtable *,
+ struct stable *));
+extern int next_child __P((struct gtable **, struct stable **,
+ u_int32, u_int32, u_int32,
+ vifi_t *));
+
+/* route.c */
+extern struct rtentry * snmp_find_route __P((u_int32, u_int32));
+extern int next_route __P((struct rtentry **, u_int32, u_int32));
+extern int next_route_child __P((struct rtentry **,
+ u_int32, u_int32, vifi_t *));
+#endif
+
+#ifdef RSRR
+/* prune.c */
+extern struct gtable *kernel_table;
+extern struct gtable *gtp;
+
+/* rsrr.c */
+extern void rsrr_init __P((void));
+extern void rsrr_clean __P((void));
+extern void rsrr_cache_send __P((struct gtable *, int));
+extern void rsrr_cache_clean __P((struct gtable *));
+#endif /* RSRR */
diff --git a/usr.sbin/mrouted/dvmrp.h b/usr.sbin/mrouted/dvmrp.h
new file mode 100644
index 0000000..2f11872
--- /dev/null
+++ b/usr.sbin/mrouted/dvmrp.h
@@ -0,0 +1,172 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $FreeBSD$
+ * dvmrp.h,v 3.8.4.5 1997/11/18 23:25:57 fenner Exp
+ */
+
+/*
+ * A DVMRP message consists of an IP header + an IGMP header + (for some types)
+ * zero or more bytes of data.
+ *
+ * For REPORT messages, the data is route information; the route information
+ * consists of one or more lists of the following form:
+ *
+ * (mask, (origin, metric), (origin, metric), ...)
+ *
+ * where:
+ *
+ * "mask" is the subnet mask for all the origins in the list.
+ * It is always THREE bytes long, containing the low-order
+ * three bytes of the mask (the high-order byte is always
+ * 0xff and therefore need not be transmitted).
+ *
+ * "origin" is the number of a subnet from which multicast datagrams
+ * may originate. It is from one to four bytes long,
+ * depending on the value of "mask":
+ * if all bytes of the mask are zero
+ * the subnet number is one byte long
+ * else if the low-order two bytes of the mask are zero
+ * the subnet number is two bytes long
+ * else if the lowest-order byte of the mask is zero
+ * the subnet number is three bytes long,
+ * else
+ * the subnet number is four bytes long.
+ *
+ * "metric" is a one-byte value consisting of two subfields:
+ * - the high-order bit is a flag which, when set, indicates
+ * the last (origin, metric) pair of a list.
+ * - the low-order seven bits contain the routing metric for
+ * the corresponding origin, relative to the sender of the
+ * DVMRP report. The metric may have the value of UNREACHABLE
+ * added to it as a "split horizon" indication (so called
+ * "poisoned reverse").
+ *
+ * Within a list, the origin subnet numbers must be in ascending order, and
+ * the lists themselves are in order of increasing mask value. A message may
+ * not exceed 576 bytes, the default maximum IP reassembly size, including
+ * the IP and IGMP headers; the route information may be split across more
+ * than one message if necessary, by terminating a list in one message and
+ * starting a new list in the next message (repeating the same mask value,
+ * if necessary).
+ *
+ * For NEIGHBORS messages, the data is neighboring-router information
+ * consisting of one or more lists of the following form:
+ *
+ * (local-addr, metric, threshold, ncount, neighbor, neighbor, ...)
+ *
+ * where:
+ *
+ * "local-addr" is the sending router's address as seen by the neighbors
+ * in this list; it is always four bytes long.
+ * "metric" is a one-byte unsigned value, the TTL `cost' of forwarding
+ * packets to any of the neighbors on this list.
+ * "threshold" is a one-byte unsigned value, a lower bound on the TTL a
+ * packet must have to be forwarded to any of the neighbors on
+ * this list.
+ * "ncount" is the number of neighbors in this list.
+ * "neighbor" is the address of a neighboring router, four bytes long.
+ *
+ * As with REPORT messages, NEIGHBORS messages should not exceed 576 bytes,
+ * including the IP and IGMP headers; split longer messages by terminating the
+ * list in one and continuing in another, repeating the local-addr, etc., if
+ * necessary.
+ *
+ * For NEIGHBORS2 messages, the data is identical to NEIGHBORS except
+ * there is a flags byte before the neighbor count:
+ *
+ * (local-addr, metric, threshold, flags, ncount, neighbor, neighbor, ...)
+ */
+
+/*
+ * DVMRP message types (carried in the "code" field of an IGMP header)
+ */
+#define DVMRP_PROBE 1 /* for finding neighbors */
+#define DVMRP_REPORT 2 /* for reporting some or all routes */
+#define DVMRP_ASK_NEIGHBORS 3 /* sent by mapper, asking for a list */
+ /* of this router's neighbors. */
+#define DVMRP_NEIGHBORS 4 /* response to such a request */
+#define DVMRP_ASK_NEIGHBORS2 5 /* as above, want new format reply */
+#define DVMRP_NEIGHBORS2 6
+#define DVMRP_PRUNE 7 /* prune message */
+#define DVMRP_GRAFT 8 /* graft message */
+#define DVMRP_GRAFT_ACK 9 /* graft acknowledgement */
+#define DVMRP_INFO_REQUEST 10 /* information request */
+#define DVMRP_INFO_REPLY 11 /* information reply */
+
+/*
+ * 'flags' byte values in DVMRP_NEIGHBORS2 reply.
+ */
+#define DVMRP_NF_TUNNEL 0x01 /* neighbors reached via tunnel */
+#define DVMRP_NF_SRCRT 0x02 /* tunnel uses IP source routing */
+#define DVMRP_NF_PIM 0x04 /* neighbor is a PIM neighbor */
+#define DVMRP_NF_DOWN 0x10 /* kernel state of interface */
+#define DVMRP_NF_DISABLED 0x20 /* administratively disabled */
+#define DVMRP_NF_QUERIER 0x40 /* I am the subnet's querier */
+#define DVMRP_NF_LEAF 0x80 /* Neighbor reports that it is a leaf */
+
+/*
+ * Request/reply types for info queries/replies
+ */
+#define DVMRP_INFO_VERSION 1 /* version string */
+#define DVMRP_INFO_NEIGHBORS 2 /* neighbors2 data */
+
+/*
+ * Limit on length of route data
+ */
+#define MAX_IP_PACKET_LEN 576
+#define MIN_IP_HEADER_LEN 20
+#define MAX_IP_HEADER_LEN 60
+#define MAX_DVMRP_DATA_LEN \
+ ( MAX_IP_PACKET_LEN - MAX_IP_HEADER_LEN - IGMP_MINLEN )
+
+/*
+ * Various protocol constants (all times in seconds)
+ */
+ /* address for multicast DVMRP msgs */
+#define INADDR_DVMRP_GROUP (u_int32)0xe0000004 /* 224.0.0.4 */
+/*
+ * The IGMPv2 <netinet/in.h> defines INADDR_ALLRTRS_GROUP, but earlier
+ * ones don't, so we define it conditionally here.
+ */
+#ifndef INADDR_ALLRTRS_GROUP
+ /* address for multicast mtrace msg */
+#define INADDR_ALLRTRS_GROUP (u_int32)0xe0000002 /* 224.0.0.2 */
+#endif
+
+#define ROUTE_MAX_REPORT_DELAY 5 /* max delay for reporting changes */
+ /* (This is the timer interrupt */
+ /* interval; all times must be */
+ /* multiples of this value.) */
+
+#define ROUTE_REPORT_INTERVAL 60 /* periodic route report interval */
+#define ROUTE_SWITCH_TIME 140 /* time to switch to equivalent gw */
+#define ROUTE_EXPIRE_TIME 200 /* time to mark route invalid */
+#define ROUTE_DISCARD_TIME 340 /* time to garbage collect route */
+
+#define LEAF_CONFIRMATION_TIME 200 /* time to consider subnet a leaf */
+
+#define NEIGHBOR_PROBE_INTERVAL 10 /* periodic neighbor probe interval */
+#define NEIGHBOR_EXPIRE_TIME 30 /* time to consider neighbor gone */
+#define OLD_NEIGHBOR_EXPIRE_TIME 140 /* time to consider neighbor gone */
+
+#define UNREACHABLE 32 /* "infinity" metric, must be <= 64 */
+#define DEFAULT_METRIC 1 /* default subnet/tunnel metric */
+#define DEFAULT_THRESHOLD 1 /* default subnet/tunnel threshold */
+
+#define MAX_RATE_LIMIT 100000 /* max rate limit */
+#define DEFAULT_PHY_RATE_LIMIT 0 /* default phyint rate limit */
+#define DEFAULT_TUN_RATE_LIMIT 0 /* default tunnel rate limit */
+
+#define DEFAULT_CACHE_LIFETIME 300 /* kernel route entry discard time */
+#define MIN_CACHE_LIFETIME 60 /* minimum allowed cache lifetime */
+#define AVERAGE_PRUNE_LIFETIME 7200 /* average lifetime of prunes sent */
+#define MIN_PRUNE_LIFETIME 120 /* minimum allowed prune lifetime */
+#define GRAFT_TIMEOUT_VAL 5 /* retransmission time for grafts */
+#define PRUNE_REXMIT_VAL 3 /* initial time for prune rexmission*/
diff --git a/usr.sbin/mrouted/icmp.c b/usr.sbin/mrouted/icmp.c
new file mode 100644
index 0000000..72efa0a
--- /dev/null
+++ b/usr.sbin/mrouted/icmp.c
@@ -0,0 +1,225 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * icmp.c,v 3.8.4.2 1998/01/06 01:57:42 fenner Exp
+ */
+
+#include "defs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#) $Id: \
+icmp.c,v 3.8.4.2 1998/01/06 01:57:42 fenner Exp $";
+#endif
+
+static int icmp_socket;
+
+static void icmp_handler __P((int, fd_set *));
+static char * icmp_name __P((struct icmp *));
+
+void
+init_icmp()
+{
+ if ((icmp_socket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0)
+ log(LOG_ERR, errno, "ICMP socket");
+
+ register_input_handler(icmp_socket, icmp_handler);
+
+ IF_DEBUG(DEBUG_ICMP)
+ log(LOG_DEBUG, 0, "registering icmp socket fd %d\n", icmp_socket);
+}
+
+static void
+icmp_handler(fd, rfds)
+ int fd;
+ fd_set *rfds;
+{
+ u_char icmp_buf[RECV_BUF_SIZE];
+ struct sockaddr_in from;
+ int fromlen, recvlen, iphdrlen, ipdatalen;
+ struct icmp *icmp;
+ struct ip *ip;
+ vifi_t i;
+ struct uvif *v;
+ u_int32 src;
+
+ fromlen = sizeof(from);
+ recvlen = recvfrom(icmp_socket, icmp_buf, RECV_BUF_SIZE, 0,
+ (struct sockaddr *)&from, &fromlen);
+ if (recvlen < 0) {
+ if (errno != EINTR)
+ log(LOG_WARNING, errno, "icmp_socket recvfrom");
+ return;
+ }
+ ip = (struct ip *)icmp_buf;
+ iphdrlen = ip->ip_hl << 2;
+#ifdef RAW_INPUT_IS_RAW
+ ipdatalen = ntohs(ip->ip_len) - iphdrlen;
+#else
+ ipdatalen = ip->ip_len;
+#endif
+ if (iphdrlen + ipdatalen != recvlen) {
+ IF_DEBUG(DEBUG_ICMP)
+ log(LOG_DEBUG, 0, "hdr %d data %d != rcv %d", iphdrlen, ipdatalen, recvlen);
+ /* Malformed ICMP, just return. */
+ return;
+ }
+ if (ipdatalen < ICMP_MINLEN + sizeof(struct ip)) {
+ /* Not enough data for us to be interested in it. */
+ return;
+ }
+ src = ip->ip_src.s_addr;
+ icmp = (struct icmp *)(icmp_buf + iphdrlen);
+ IF_DEBUG(DEBUG_ICMP)
+ log(LOG_DEBUG, 0, "got ICMP type %d from %s",
+ icmp->icmp_type, inet_fmt(src, s1));
+ /*
+ * Eventually:
+ * have registry of ICMP listeners, by type, code and ICMP_ID
+ * (and maybe fields of the original packet too -- maybe need a
+ * generalized packet filter!) to allow ping and traceroute
+ * from the monitoring tool.
+ */
+ switch (icmp->icmp_type) {
+ case ICMP_UNREACH:
+ case ICMP_TIMXCEED:
+ /* Look at returned packet to see if it's us sending on a tunnel */
+ ip = &icmp->icmp_ip;
+ if (ip->ip_p != IPPROTO_IGMP && ip->ip_p != IPPROTO_IPIP)
+ return;
+ for (v = uvifs, i = 0; i < numvifs; v++, i++) {
+ if (ip->ip_src.s_addr == v->uv_lcl_addr &&
+ ip->ip_dst.s_addr == v->uv_dst_addr) {
+ char *p;
+ int n;
+ /*
+ * I sent this packet on this vif.
+ */
+ n = ++v->uv_icmp_warn;
+ while (n && !(n & 1))
+ n >>= 1;
+ if (n == 1 && ((p = icmp_name(icmp)) != NULL))
+ log(LOG_WARNING, 0, "Received ICMP %s from %s %s %s on vif %d",
+ p, inet_fmt(src, s1), "for traffic sent to",
+ inet_fmt(ip->ip_dst.s_addr, s2),
+ i);
+
+ break;
+ }
+ }
+ break;
+ }
+}
+
+/*
+ * Return NULL for ICMP informational messages.
+ * Return string describing the error for ICMP errors.
+ */
+static char *
+icmp_name(icmp)
+ struct icmp *icmp;
+{
+ static char retval[30];
+
+ switch (icmp->icmp_type) {
+ case ICMP_UNREACH:
+ switch (icmp->icmp_code) {
+ case ICMP_UNREACH_NET:
+ return "network unreachable";
+ case ICMP_UNREACH_HOST:
+ return "host unreachable";
+ case ICMP_UNREACH_PROTOCOL:
+ return "protocol unreachable";
+ case ICMP_UNREACH_PORT:
+ return "port unreachable";
+ case ICMP_UNREACH_NEEDFRAG:
+ return "needs fragmentation";
+ case ICMP_UNREACH_SRCFAIL:
+ return "source route failed";
+#ifndef ICMP_UNREACH_NET_UNKNOWN
+#define ICMP_UNREACH_NET_UNKNOWN 6
+#endif
+ case ICMP_UNREACH_NET_UNKNOWN:
+ return "network unknown";
+#ifndef ICMP_UNREACH_HOST_UNKNOWN
+#define ICMP_UNREACH_HOST_UNKNOWN 7
+#endif
+ case ICMP_UNREACH_HOST_UNKNOWN:
+ return "host unknown";
+#ifndef ICMP_UNREACH_ISOLATED
+#define ICMP_UNREACH_ISOLATED 8
+#endif
+ case ICMP_UNREACH_ISOLATED:
+ return "source host isolated";
+#ifndef ICMP_UNREACH_NET_PROHIB
+#define ICMP_UNREACH_NET_PROHIB 9
+#endif
+ case ICMP_UNREACH_NET_PROHIB:
+ return "network access prohibited";
+#ifndef ICMP_UNREACH_HOST_PROHIB
+#define ICMP_UNREACH_HOST_PROHIB 10
+#endif
+ case ICMP_UNREACH_HOST_PROHIB:
+ return "host access prohibited";
+#ifndef ICMP_UNREACH_TOSNET
+#define ICMP_UNREACH_TOSNET 11
+#endif
+ case ICMP_UNREACH_TOSNET:
+ return "bad TOS for net";
+#ifndef ICMP_UNREACH_TOSHOST
+#define ICMP_UNREACH_TOSHOST 12
+#endif
+ case ICMP_UNREACH_TOSHOST:
+ return "bad TOS for host";
+#ifndef ICMP_UNREACH_FILTER_PROHIB
+#define ICMP_UNREACH_FILTER_PROHIB 13
+#endif
+ case ICMP_UNREACH_FILTER_PROHIB:
+ return "prohibited by filter";
+#ifndef ICMP_UNREACH_HOST_PRECEDENCE
+#define ICMP_UNREACH_HOST_PRECEDENCE 14
+#endif
+ case ICMP_UNREACH_HOST_PRECEDENCE:
+ return "host precedence violation";
+#ifndef ICMP_UNREACH_PRECEDENCE_CUTOFF
+#define ICMP_UNREACH_PRECEDENCE_CUTOFF 15
+#endif
+ case ICMP_UNREACH_PRECEDENCE_CUTOFF:
+ return "precedence cutoff";
+ default:
+ sprintf(retval, "unreachable code %d", icmp->icmp_code);
+ return retval;
+ }
+ case ICMP_SOURCEQUENCH:
+ return "source quench";
+ case ICMP_REDIRECT:
+ return NULL; /* XXX */
+ case ICMP_TIMXCEED:
+ switch (icmp->icmp_code) {
+ case ICMP_TIMXCEED_INTRANS:
+ return "time exceeded in transit";
+ case ICMP_TIMXCEED_REASS:
+ return "time exceeded in reassembly";
+ default:
+ sprintf(retval, "time exceeded code %d", icmp->icmp_code);
+ return retval;
+ }
+ case ICMP_PARAMPROB:
+ switch (icmp->icmp_code) {
+#ifndef ICMP_PARAMPROB_OPTABSENT
+#define ICMP_PARAMPROB_OPTABSENT 1
+#endif
+ case ICMP_PARAMPROB_OPTABSENT:
+ return "required option absent";
+ default:
+ sprintf(retval, "parameter problem code %d", icmp->icmp_code);
+ return retval;
+ }
+ }
+ return NULL;
+}
diff --git a/usr.sbin/mrouted/igmp.c b/usr.sbin/mrouted/igmp.c
new file mode 100644
index 0000000..f343035
--- /dev/null
+++ b/usr.sbin/mrouted/igmp.c
@@ -0,0 +1,447 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * igmp.c,v 3.8.4.19 1998/01/06 01:57:43 fenner Exp
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include "defs.h"
+
+/*
+ * Exported variables.
+ */
+char *recv_buf; /* input packet buffer */
+char *send_buf; /* output packet buffer */
+int igmp_socket; /* socket for all network I/O */
+u_int32 allhosts_group; /* All hosts addr in net order */
+u_int32 allrtrs_group; /* All-Routers " in net order */
+u_int32 dvmrp_group; /* DVMRP grp addr in net order */
+u_int32 dvmrp_genid; /* IGMP generation id */
+
+/*
+ * Local function definitions.
+ */
+/* u_char promoted to u_int */
+static int igmp_log_level __P((u_int type, u_int code));
+
+/*
+ * Open and initialize the igmp socket, and fill in the non-changing
+ * IP header fields in the output packet buffer.
+ */
+void
+init_igmp()
+{
+ struct ip *ip;
+
+ recv_buf = malloc(RECV_BUF_SIZE);
+ send_buf = malloc(RECV_BUF_SIZE);
+
+ if ((igmp_socket = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP)) < 0)
+ log(LOG_ERR, errno, "IGMP socket");
+
+ k_hdr_include(TRUE); /* include IP header when sending */
+ k_set_rcvbuf(256*1024,48*1024); /* lots of input buffering */
+ k_set_ttl(1); /* restrict multicasts to one hop */
+ k_set_loop(FALSE); /* disable multicast loopback */
+
+ ip = (struct ip *)send_buf;
+ bzero(ip, sizeof(struct ip));
+ /*
+ * Fields zeroed that aren't filled in later:
+ * - IP ID (let the kernel fill it in)
+ * - Offset (we don't send fragments)
+ * - Checksum (let the kernel fill it in)
+ */
+ ip->ip_v = IPVERSION;
+ ip->ip_hl = sizeof(struct ip) >> 2;
+ ip->ip_tos = 0xc0; /* Internet Control */
+ ip->ip_ttl = MAXTTL; /* applies to unicasts only */
+ ip->ip_p = IPPROTO_IGMP;
+
+ allhosts_group = htonl(INADDR_ALLHOSTS_GROUP);
+ dvmrp_group = htonl(INADDR_DVMRP_GROUP);
+ allrtrs_group = htonl(INADDR_ALLRTRS_GROUP);
+}
+
+#define PIM_QUERY 0
+#define PIM_REGISTER 1
+#define PIM_REGISTER_STOP 2
+#define PIM_JOIN_PRUNE 3
+#define PIM_RP_REACHABLE 4
+#define PIM_ASSERT 5
+#define PIM_GRAFT 6
+#define PIM_GRAFT_ACK 7
+
+char *
+igmp_packet_kind(type, code)
+ u_int type, code;
+{
+ static char unknown[20];
+
+ switch (type) {
+ case IGMP_MEMBERSHIP_QUERY: return "membership query ";
+ case IGMP_V1_MEMBERSHIP_REPORT: return "V1 member report ";
+ case IGMP_V2_MEMBERSHIP_REPORT: return "V2 member report ";
+ case IGMP_V2_LEAVE_GROUP: return "leave message ";
+ case IGMP_DVMRP:
+ switch (code) {
+ case DVMRP_PROBE: return "neighbor probe ";
+ case DVMRP_REPORT: return "route report ";
+ case DVMRP_ASK_NEIGHBORS: return "neighbor request ";
+ case DVMRP_NEIGHBORS: return "neighbor list ";
+ case DVMRP_ASK_NEIGHBORS2: return "neighbor request 2";
+ case DVMRP_NEIGHBORS2: return "neighbor list 2 ";
+ case DVMRP_PRUNE: return "prune message ";
+ case DVMRP_GRAFT: return "graft message ";
+ case DVMRP_GRAFT_ACK: return "graft message ack ";
+ case DVMRP_INFO_REQUEST: return "info request ";
+ case DVMRP_INFO_REPLY: return "info reply ";
+ default:
+ sprintf(unknown, "unknown DVMRP %3d ", code);
+ return unknown;
+ }
+ case IGMP_PIM:
+ switch (code) {
+ case PIM_QUERY: return "PIM Router-Query ";
+ case PIM_REGISTER: return "PIM Register ";
+ case PIM_REGISTER_STOP: return "PIM Register-Stop ";
+ case PIM_JOIN_PRUNE: return "PIM Join/Prune ";
+ case PIM_RP_REACHABLE: return "PIM RP-Reachable ";
+ case PIM_ASSERT: return "PIM Assert ";
+ case PIM_GRAFT: return "PIM Graft ";
+ case PIM_GRAFT_ACK: return "PIM Graft-Ack ";
+ default:
+ sprintf(unknown, "unknown PIM msg%3d", code);
+ return unknown;
+ }
+ case IGMP_MTRACE: return "IGMP trace query ";
+ case IGMP_MTRACE_RESP: return "IGMP trace reply ";
+ default:
+ sprintf(unknown, "unk: 0x%02x/0x%02x ", type, code);
+ return unknown;
+ }
+}
+
+int
+igmp_debug_kind(type, code)
+ u_int type, code;
+{
+ switch (type) {
+ case IGMP_MEMBERSHIP_QUERY: return DEBUG_IGMP;
+ case IGMP_V1_MEMBERSHIP_REPORT: return DEBUG_IGMP;
+ case IGMP_V2_MEMBERSHIP_REPORT: return DEBUG_IGMP;
+ case IGMP_V2_LEAVE_GROUP: return DEBUG_IGMP;
+ case IGMP_DVMRP:
+ switch (code) {
+ case DVMRP_PROBE: return DEBUG_PEER;
+ case DVMRP_REPORT: return DEBUG_ROUTE;
+ case DVMRP_ASK_NEIGHBORS: return 0;
+ case DVMRP_NEIGHBORS: return 0;
+ case DVMRP_ASK_NEIGHBORS2: return 0;
+ case DVMRP_NEIGHBORS2: return 0;
+ case DVMRP_PRUNE: return DEBUG_PRUNE;
+ case DVMRP_GRAFT: return DEBUG_PRUNE;
+ case DVMRP_GRAFT_ACK: return DEBUG_PRUNE;
+ case DVMRP_INFO_REQUEST: return 0;
+ case DVMRP_INFO_REPLY: return 0;
+ default: return 0;
+ }
+ case IGMP_PIM:
+ switch (code) {
+ case PIM_QUERY: return 0;
+ case PIM_REGISTER: return 0;
+ case PIM_REGISTER_STOP: return 0;
+ case PIM_JOIN_PRUNE: return 0;
+ case PIM_RP_REACHABLE: return 0;
+ case PIM_ASSERT: return 0;
+ case PIM_GRAFT: return 0;
+ case PIM_GRAFT_ACK: return 0;
+ default: return 0;
+ }
+ case IGMP_MTRACE: return DEBUG_TRACE;
+ case IGMP_MTRACE_RESP: return DEBUG_TRACE;
+ default: return DEBUG_IGMP;
+ }
+}
+
+/*
+ * Process a newly received IGMP packet that is sitting in the input
+ * packet buffer.
+ */
+void
+accept_igmp(recvlen)
+ int recvlen;
+{
+ register u_int32 src, dst, group;
+ struct ip *ip;
+ struct igmp *igmp;
+ int ipdatalen, iphdrlen, igmpdatalen;
+
+ if (recvlen < sizeof(struct ip)) {
+ log(LOG_WARNING, 0,
+ "received packet too short (%u bytes) for IP header", recvlen);
+ return;
+ }
+
+ ip = (struct ip *)recv_buf;
+ src = ip->ip_src.s_addr;
+ dst = ip->ip_dst.s_addr;
+
+ /*
+ * this is most likely a message from the kernel indicating that
+ * a new src grp pair message has arrived and so, it would be
+ * necessary to install a route into the kernel for this.
+ */
+ if (ip->ip_p == 0) {
+ if (src == 0 || dst == 0)
+ log(LOG_WARNING, 0, "kernel request not accurate");
+ else
+ add_table_entry(src, dst);
+ return;
+ }
+
+ iphdrlen = ip->ip_hl << 2;
+#ifdef RAW_INPUT_IS_RAW
+ ipdatalen = ntohs(ip->ip_len) - iphdrlen;
+#else
+ ipdatalen = ip->ip_len;
+#endif
+ if (iphdrlen + ipdatalen != recvlen) {
+ log(LOG_WARNING, 0,
+ "received packet from %s shorter (%u bytes) than hdr+data length (%u+%u)",
+ inet_fmt(src, s1), recvlen, iphdrlen, ipdatalen);
+ return;
+ }
+
+ igmp = (struct igmp *)(recv_buf + iphdrlen);
+ group = igmp->igmp_group.s_addr;
+ igmpdatalen = ipdatalen - IGMP_MINLEN;
+ if (igmpdatalen < 0) {
+ log(LOG_WARNING, 0,
+ "received IP data field too short (%u bytes) for IGMP, from %s",
+ ipdatalen, inet_fmt(src, s1));
+ return;
+ }
+
+ IF_DEBUG(DEBUG_PKT|igmp_debug_kind(igmp->igmp_type, igmp->igmp_code))
+ log(LOG_DEBUG, 0, "RECV %s from %-15s to %s",
+ igmp_packet_kind(igmp->igmp_type, igmp->igmp_code),
+ inet_fmt(src, s1), inet_fmt(dst, s2));
+
+ switch (igmp->igmp_type) {
+
+ case IGMP_MEMBERSHIP_QUERY:
+ accept_membership_query(src, dst, group, igmp->igmp_code);
+ return;
+
+ case IGMP_V1_MEMBERSHIP_REPORT:
+ case IGMP_V2_MEMBERSHIP_REPORT:
+ accept_group_report(src, dst, group, igmp->igmp_type);
+ return;
+
+ case IGMP_V2_LEAVE_GROUP:
+ accept_leave_message(src, dst, group);
+ return;
+
+ case IGMP_DVMRP:
+ group = ntohl(group);
+
+ switch (igmp->igmp_code) {
+ case DVMRP_PROBE:
+ accept_probe(src, dst,
+ (char *)(igmp+1), igmpdatalen, group);
+ return;
+
+ case DVMRP_REPORT:
+ accept_report(src, dst,
+ (char *)(igmp+1), igmpdatalen, group);
+ return;
+
+ case DVMRP_ASK_NEIGHBORS:
+ accept_neighbor_request(src, dst);
+ return;
+
+ case DVMRP_ASK_NEIGHBORS2:
+ accept_neighbor_request2(src, dst);
+ return;
+
+ case DVMRP_NEIGHBORS:
+ accept_neighbors(src, dst, (u_char *)(igmp+1), igmpdatalen,
+ group);
+ return;
+
+ case DVMRP_NEIGHBORS2:
+ accept_neighbors2(src, dst, (u_char *)(igmp+1), igmpdatalen,
+ group);
+ return;
+
+ case DVMRP_PRUNE:
+ accept_prune(src, dst, (char *)(igmp+1), igmpdatalen);
+ return;
+
+ case DVMRP_GRAFT:
+ accept_graft(src, dst, (char *)(igmp+1), igmpdatalen);
+ return;
+
+ case DVMRP_GRAFT_ACK:
+ accept_g_ack(src, dst, (char *)(igmp+1), igmpdatalen);
+ return;
+
+ case DVMRP_INFO_REQUEST:
+ accept_info_request(src, dst, (char *)(igmp+1),
+ igmpdatalen);
+ return;
+
+ case DVMRP_INFO_REPLY:
+ accept_info_reply(src, dst, (char *)(igmp+1), igmpdatalen);
+ return;
+
+ default:
+ log(LOG_INFO, 0,
+ "ignoring unknown DVMRP message code %u from %s to %s",
+ igmp->igmp_code, inet_fmt(src, s1),
+ inet_fmt(dst, s2));
+ return;
+ }
+
+ case IGMP_PIM:
+ return;
+
+ case IGMP_MTRACE_RESP:
+ return;
+
+ case IGMP_MTRACE:
+ accept_mtrace(src, dst, group, (char *)(igmp+1),
+ igmp->igmp_code, igmpdatalen);
+ return;
+
+ default:
+ log(LOG_INFO, 0,
+ "ignoring unknown IGMP message type %x from %s to %s",
+ igmp->igmp_type, inet_fmt(src, s1),
+ inet_fmt(dst, s2));
+ return;
+ }
+}
+
+/*
+ * Some IGMP messages are more important than others. This routine
+ * determines the logging level at which to log a send error (often
+ * "No route to host"). This is important when there is asymmetric
+ * reachability and someone is trying to, i.e., mrinfo me periodically.
+ */
+static int
+igmp_log_level(type, code)
+ u_int type, code;
+{
+ switch (type) {
+ case IGMP_MTRACE_RESP:
+ return LOG_INFO;
+
+ case IGMP_DVMRP:
+ switch (code) {
+ case DVMRP_NEIGHBORS:
+ case DVMRP_NEIGHBORS2:
+ return LOG_INFO;
+ }
+ }
+ return LOG_WARNING;
+}
+
+/*
+ * Construct an IGMP message in the output packet buffer. The caller may
+ * have already placed data in that buffer, of length 'datalen'.
+ */
+void
+build_igmp(src, dst, type, code, group, datalen)
+ u_int32 src, dst;
+ int type, code;
+ u_int32 group;
+ int datalen;
+{
+ struct ip *ip;
+ struct igmp *igmp;
+ extern int curttl;
+
+ ip = (struct ip *)send_buf;
+ ip->ip_src.s_addr = src;
+ ip->ip_dst.s_addr = dst;
+ ip->ip_len = MIN_IP_HEADER_LEN + IGMP_MINLEN + datalen;
+#ifdef RAW_OUTPUT_IS_RAW
+ ip->ip_len = htons(ip->ip_len);
+#endif
+ if (IN_MULTICAST(ntohl(dst))) {
+ ip->ip_ttl = curttl;
+ } else {
+ ip->ip_ttl = MAXTTL;
+ }
+
+ igmp = (struct igmp *)(send_buf + MIN_IP_HEADER_LEN);
+ igmp->igmp_type = type;
+ igmp->igmp_code = code;
+ igmp->igmp_group.s_addr = group;
+ igmp->igmp_cksum = 0;
+ igmp->igmp_cksum = inet_cksum((u_short *)igmp,
+ IGMP_MINLEN + datalen);
+}
+
+/*
+ * Call build_igmp() to build an IGMP message in the output packet buffer.
+ * Then send the message from the interface with IP address 'src' to
+ * destination 'dst'.
+ */
+void
+send_igmp(src, dst, type, code, group, datalen)
+ u_int32 src, dst;
+ int type, code;
+ u_int32 group;
+ int datalen;
+{
+ struct sockaddr_in sdst;
+ int setloop = 0;
+
+ build_igmp(src, dst, type, code, group, datalen);
+
+ if (IN_MULTICAST(ntohl(dst))) {
+ k_set_if(src);
+ if (type != IGMP_DVMRP || dst == allhosts_group) {
+ setloop = 1;
+ k_set_loop(TRUE);
+ }
+ }
+
+ bzero(&sdst, sizeof(sdst));
+ sdst.sin_family = AF_INET;
+#ifdef HAVE_SA_LEN
+ sdst.sin_len = sizeof(sdst);
+#endif
+ sdst.sin_addr.s_addr = dst;
+ if (sendto(igmp_socket, send_buf,
+ MIN_IP_HEADER_LEN + IGMP_MINLEN + datalen, 0,
+ (struct sockaddr *)&sdst, sizeof(sdst)) < 0) {
+ if (errno == ENETDOWN)
+ check_vif_state();
+ else
+ log(igmp_log_level(type, code), errno,
+ "sendto to %s on %s",
+ inet_fmt(dst, s1), inet_fmt(src, s2));
+ }
+
+ if (setloop)
+ k_set_loop(FALSE);
+
+ IF_DEBUG(DEBUG_PKT|igmp_debug_kind(type, code))
+ log(LOG_DEBUG, 0, "SENT %s from %-15s to %s",
+ igmp_packet_kind(type, code), src == INADDR_ANY ? "INADDR_ANY" :
+ inet_fmt(src, s1), inet_fmt(dst, s2));
+}
diff --git a/usr.sbin/mrouted/igmpv2.h b/usr.sbin/mrouted/igmpv2.h
new file mode 100644
index 0000000..5f5ae27
--- /dev/null
+++ b/usr.sbin/mrouted/igmpv2.h
@@ -0,0 +1,42 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * igmpv2.h,v 3.8.4.1 1997/11/18 23:25:58 fenner Exp
+ */
+
+/*
+ * Constants for IGMP Version 2. Several of these, especially the
+ * robustness variable, should be variables and not constants.
+ */
+#define IGMP_ROBUSTNESS_VARIABLE 2
+#define IGMP_QUERY_INTERVAL 125
+#define IGMP_QUERY_RESPONSE_INTERVAL 10
+#define IGMP_GROUP_MEMBERSHIP_INTERVAL (IGMP_ROBUSTNESS_VARIABLE * \
+ IGMP_QUERY_INTERVAL + \
+ IGMP_QUERY_RESPONSE_INTERVAL)
+#define IGMP_OTHER_QUERIER_PRESENT_INTERVAL (IGMP_ROBUSTNESS_VARIABLE * \
+ IGMP_QUERY_INTERVAL + \
+ IGMP_QUERY_RESPONSE_INTERVAL / 2)
+ /* Round to the nearest TIMER_INTERVAL */
+#define IGMP_STARTUP_QUERY_INTERVAL (((IGMP_QUERY_INTERVAL / 4) \
+ / TIMER_INTERVAL) * TIMER_INTERVAL)
+#define IGMP_STARTUP_QUERY_COUNT IGMP_ROBUSTNESS_VARIABLE
+#define IGMP_LAST_MEMBER_QUERY_INTERVAL 1
+#define IGMP_LAST_MEMBER_QUERY_COUNT IGMP_ROBUSTNESS_VARIABLE
+
+/*
+ * OLD_AGE_THRESHOLD is the number of IGMP_QUERY_INTERVAL's to remember the
+ * presence of an IGMPv1 group member. According to the IGMPv2 specification,
+ * routers remember this presence for [Robustness Variable] * [Query Interval] +
+ * [Query Response Interval]. However, OLD_AGE_THRESHOLD is in units of
+ * [Query Interval], so doesn't have sufficient resolution to represent
+ * [Query Response Interval]. When the timer mechanism gets an efficient
+ * method of refreshing timers, this should get fixed.
+ */
+#define OLD_AGE_THRESHOLD IGMP_ROBUSTNESS_VARIABLE
diff --git a/usr.sbin/mrouted/inet.c b/usr.sbin/mrouted/inet.c
new file mode 100644
index 0000000..53aad7c
--- /dev/null
+++ b/usr.sbin/mrouted/inet.c
@@ -0,0 +1,235 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * inet.c,v 3.8.4.2 1998/01/06 01:57:44 fenner Exp
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include "defs.h"
+
+/*
+ * Exported variables.
+ */
+char s1[19]; /* buffers to hold the string representations */
+char s2[19]; /* of IP addresses, to be passed to inet_fmt() */
+char s3[19]; /* or inet_fmts(). */
+char s4[19];
+
+
+/*
+ * Verify that a given IP address is credible as a host address.
+ * (Without a mask, cannot detect addresses of the form {subnet,0} or
+ * {subnet,-1}.)
+ */
+int
+inet_valid_host(naddr)
+ u_int32 naddr;
+{
+ register u_int32 addr;
+
+ addr = ntohl(naddr);
+
+ return (!(IN_MULTICAST(addr) ||
+ IN_BADCLASS (addr) ||
+ (addr & 0xff000000) == 0));
+}
+
+/*
+ * Verify that a given netmask is plausible;
+ * make sure that it is a series of 1's followed by
+ * a series of 0's with no discontiguous 1's.
+ */
+int
+inet_valid_mask(mask)
+ u_int32 mask;
+{
+ if (~(((mask & -mask) - 1) | mask) != 0) {
+ /* Mask is not contiguous */
+ return (FALSE);
+ }
+
+ return (TRUE);
+}
+
+/*
+ * Verify that a given subnet number and mask pair are credible.
+ *
+ * With CIDR, almost any subnet and mask are credible. mrouted still
+ * can't handle aggregated class A's, so we still check that, but
+ * otherwise the only requirements are that the subnet address is
+ * within the [ABC] range and that the host bits of the subnet
+ * are all 0.
+ */
+int
+inet_valid_subnet(nsubnet, nmask)
+ u_int32 nsubnet, nmask;
+{
+ register u_int32 subnet, mask;
+
+ subnet = ntohl(nsubnet);
+ mask = ntohl(nmask);
+
+ if ((subnet & mask) != subnet) return (FALSE);
+
+ if (subnet == 0)
+ return (mask == 0);
+
+ if (IN_CLASSA(subnet)) {
+ if (mask < 0xff000000 ||
+ (subnet & 0xff000000) == 0x7f000000 ||
+ (subnet & 0xff000000) == 0x00000000) return (FALSE);
+ }
+ else if (IN_CLASSD(subnet) || IN_BADCLASS(subnet)) {
+ /* Above Class C address space */
+ return (FALSE);
+ }
+ if (subnet & ~mask) {
+ /* Host bits are set in the subnet */
+ return (FALSE);
+ }
+ if (!inet_valid_mask(mask)) {
+ /* Netmask is not contiguous */
+ return (FALSE);
+ }
+
+ return (TRUE);
+}
+
+
+/*
+ * Convert an IP address in u_long (network) format into a printable string.
+ */
+char *
+inet_fmt(addr, s)
+ u_int32 addr;
+ char *s;
+{
+ register u_char *a;
+
+ a = (u_char *)&addr;
+ sprintf(s, "%u.%u.%u.%u", a[0], a[1], a[2], a[3]);
+ return (s);
+}
+
+
+/*
+ * Convert an IP subnet number in u_long (network) format into a printable
+ * string including the netmask as a number of bits.
+ */
+char *
+inet_fmts(addr, mask, s)
+ u_int32 addr, mask;
+ char *s;
+{
+ register u_char *a, *m;
+ int bits;
+
+ if ((addr == 0) && (mask == 0)) {
+ sprintf(s, "default");
+ return (s);
+ }
+ a = (u_char *)&addr;
+ m = (u_char *)&mask;
+ bits = 33 - ffs(ntohl(mask));
+
+ if (m[3] != 0) sprintf(s, "%u.%u.%u.%u/%d", a[0], a[1], a[2], a[3],
+ bits);
+ else if (m[2] != 0) sprintf(s, "%u.%u.%u/%d", a[0], a[1], a[2], bits);
+ else if (m[1] != 0) sprintf(s, "%u.%u/%d", a[0], a[1], bits);
+ else sprintf(s, "%u/%d", a[0], bits);
+
+ return (s);
+}
+
+/*
+ * Convert the printable string representation of an IP address into the
+ * u_long (network) format. Return 0xffffffff on error. (To detect the
+ * legal address with that value, you must explicitly compare the string
+ * with "255.255.255.255".)
+ */
+u_int32
+inet_parse(s,n)
+ char *s;
+ int n;
+{
+ u_int32 a = 0;
+ u_int a0 = 0, a1 = 0, a2 = 0, a3 = 0;
+ int i;
+ char c;
+
+ i = sscanf(s, "%u.%u.%u.%u%c", &a0, &a1, &a2, &a3, &c);
+ if (i < n || i > 4 || a0 > 255 || a1 > 255 || a2 > 255 || a3 > 255)
+ return (0xffffffff);
+
+ ((u_char *)&a)[0] = a0;
+ ((u_char *)&a)[1] = a1;
+ ((u_char *)&a)[2] = a2;
+ ((u_char *)&a)[3] = a3;
+
+ return (a);
+}
+
+
+/*
+ * inet_cksum extracted from:
+ * P I N G . C
+ *
+ * Author -
+ * Mike Muuss
+ * U. S. Army Ballistic Research Laboratory
+ * December, 1983
+ * Modified at Uc Berkeley
+ *
+ * (ping.c) Status -
+ * Public Domain. Distribution Unlimited.
+ *
+ * I N _ C K S U M
+ *
+ * Checksum routine for Internet Protocol family headers (C Version)
+ *
+ */
+int
+inet_cksum(addr, len)
+ u_short *addr;
+ u_int len;
+{
+ register int nleft = (int)len;
+ register u_short *w = addr;
+ u_short answer = 0;
+ register int sum = 0;
+
+ /*
+ * Our algorithm is simple, using a 32 bit accumulator (sum),
+ * we add sequential 16 bit words to it, and at the end, fold
+ * back all the carry bits from the top 16 bits into the lower
+ * 16 bits.
+ */
+ while (nleft > 1) {
+ sum += *w++;
+ nleft -= 2;
+ }
+
+ /* mop up an odd byte, if necessary */
+ if (nleft == 1) {
+ *(u_char *) (&answer) = *(u_char *)w ;
+ sum += answer;
+ }
+
+ /*
+ * add back carry outs from top 16 bits to low 16 bits
+ */
+ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
+ sum += (sum >> 16); /* add carry */
+ answer = ~sum; /* truncate to 16 bits */
+ return (answer);
+}
diff --git a/usr.sbin/mrouted/ipip.c b/usr.sbin/mrouted/ipip.c
new file mode 100644
index 0000000..6e88d93
--- /dev/null
+++ b/usr.sbin/mrouted/ipip.c
@@ -0,0 +1,145 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * ipip.c,v 3.8.4.6 1998/01/06 01:57:45 fenner Exp
+ */
+
+
+#include "defs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#) $Id: \
+ipip.c,v 3.8.4.6 1998/01/06 01:57:45 fenner Exp $";
+#endif
+
+/*
+ * Exported variables.
+ */
+#ifdef notyet
+int raw_socket; /* socket for raw network I/O */
+#endif
+/*
+ *XXX For now, we just use the IGMP socket to send packets.
+ * This is legal in BSD, because the protocol # is not checked
+ * on raw sockets. The k_* interfaces need to gain a socket
+ * argument so that we can call them on the raw_socket also.
+ */
+#define raw_socket igmp_socket
+
+/*
+ * Private variables.
+ */
+static int rawid = 0;
+
+/*
+ * Open and initialize the raw socket.
+ */
+void
+init_ipip()
+{
+#ifdef notyet
+ if ((raw_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
+ log(LOG_ERR, errno, "Raw IP socket");
+#endif
+}
+
+/*
+ * Allocate and fill in static IP header for encapsulating on a tunnel.
+ */
+void
+init_ipip_on_vif(v)
+ struct uvif *v;
+{
+ struct ip *ip;
+
+ ip = v->uv_encap_hdr = (struct ip *)malloc(sizeof(struct ip));
+ if (ip == NULL)
+ log(LOG_ERR, 0, "out of memory");
+ bzero(ip, sizeof(struct ip));
+ /*
+ * Fields zeroed that aren't filled in later:
+ * - IP ID (let the kernel fill it in)
+ * - Offset (we don't send fragments)
+ * - Checksum (let the kernel fill it in)
+ */
+ ip->ip_v = IPVERSION;
+ ip->ip_hl = sizeof(struct ip) >> 2;
+ ip->ip_tos = 0xc0; /* Internet Control */
+ ip->ip_ttl = MAXTTL; /* applies to unicasts only */
+ ip->ip_p = IPPROTO_IPIP;
+ ip->ip_src.s_addr = v->uv_lcl_addr;
+ ip->ip_dst.s_addr = v->uv_rmt_addr;
+}
+
+/*
+ * Call build_igmp() to build an IGMP message in the output packet buffer.
+ * Then fill in the fields of the IP packet that build_igmp() left for the
+ * kernel to fill in, and encapsulate the original packet with the
+ * pre-created ip header for this vif.
+ */
+void
+send_ipip(src, dst, type, code, group, datalen, v)
+ u_int32 src, dst;
+ int type, code;
+ u_int32 group;
+ int datalen;
+ struct uvif *v;
+{
+ struct msghdr msg;
+ struct iovec iov[2];
+ struct sockaddr_in sdst;
+ struct ip *ip;
+
+ build_igmp(src, dst, type, code, group, datalen);
+ ip = (struct ip *)send_buf;
+#ifndef RAW_OUTPUT_IS_RAW
+ ip->ip_len = htons(ip->ip_len);
+#endif
+ ip->ip_id = htons(rawid++);
+ ip->ip_sum = 0;
+ ip->ip_sum = inet_cksum((u_short *)ip, ip->ip_hl << 2);
+
+ ip = v->uv_encap_hdr;
+ ip->ip_len = 2 * MIN_IP_HEADER_LEN + IGMP_MINLEN + datalen;
+#ifdef RAW_OUTPUT_IS_RAW
+ ip->ip_len = htons(ip->ip_len);
+#endif
+
+ bzero(&sdst, sizeof(sdst));
+ sdst.sin_family = AF_INET;
+#ifdef HAVE_SA_LEN
+ sdst.sin_len = sizeof(sdst);
+#endif
+ sdst.sin_addr = ip->ip_dst;
+
+ iov[0].iov_base = (caddr_t)v->uv_encap_hdr;
+ iov[0].iov_len = sizeof(struct ip);
+ iov[1].iov_base = (caddr_t)send_buf;
+ iov[1].iov_len = MIN_IP_HEADER_LEN + IGMP_MINLEN + datalen;
+
+ bzero(&msg, sizeof(msg));
+ msg.msg_name = (caddr_t)&sdst;
+ msg.msg_namelen = sizeof(sdst);
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 2;
+ if (sendmsg(raw_socket, &msg, 0) < 0) {
+ if (errno == ENETDOWN)
+ check_vif_state();
+ else
+ log(LOG_WARNING, errno,
+ "sendmsg to %s on %s",
+ inet_fmt(sdst.sin_addr.s_addr, s1), inet_fmt(src, s2));
+ }
+
+ IF_DEBUG(DEBUG_PKT|igmp_debug_kind(type, code))
+ log(LOG_DEBUG, 0, "SENT %s from %-15s to %s encaped to %s",
+ igmp_packet_kind(type, code), src == INADDR_ANY ? "INADDR_ANY" :
+ inet_fmt(src, s1), inet_fmt(dst, s2),
+ inet_fmt(sdst.sin_addr.s_addr, s3));
+}
diff --git a/usr.sbin/mrouted/kern.c b/usr.sbin/mrouted/kern.c
new file mode 100644
index 0000000..716b479
--- /dev/null
+++ b/usr.sbin/mrouted/kern.c
@@ -0,0 +1,344 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * kern.c,v 3.8.4.10 1998/01/06 02:00:51 fenner Exp
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include "defs.h"
+
+int curttl = 0;
+
+void k_set_rcvbuf(bufsize, minsize)
+ int bufsize;
+ int minsize;
+{
+ int delta = bufsize / 2;
+ int iter = 0;
+
+ /*
+ * Set the socket buffer. If we can't set it as large as we
+ * want, search around to try to find the highest acceptable
+ * value. The highest acceptable value being smaller than
+ * minsize is a fatal error.
+ */
+ if (setsockopt(igmp_socket, SOL_SOCKET, SO_RCVBUF,
+ (char *)&bufsize, sizeof(bufsize)) < 0) {
+ bufsize -= delta;
+ while (1) {
+ iter++;
+ if (delta > 1)
+ delta /= 2;
+
+ if (setsockopt(igmp_socket, SOL_SOCKET, SO_RCVBUF,
+ (char *)&bufsize, sizeof(bufsize)) < 0) {
+ bufsize -= delta;
+ } else {
+ if (delta < 1024)
+ break;
+ bufsize += delta;
+ }
+ }
+ if (bufsize < minsize) {
+ log(LOG_ERR, 0, "OS-allowed buffer size %u < app min %u",
+ bufsize, minsize);
+ /*NOTREACHED*/
+ }
+ }
+ IF_DEBUG(DEBUG_KERN)
+ log(LOG_DEBUG, 0, "Got %d byte buffer size in %d iterations",
+ bufsize, iter);
+}
+
+
+void k_hdr_include(bool)
+ int bool;
+{
+#ifdef IP_HDRINCL
+ if (setsockopt(igmp_socket, IPPROTO_IP, IP_HDRINCL,
+ (char *)&bool, sizeof(bool)) < 0)
+ log(LOG_ERR, errno, "setsockopt IP_HDRINCL %u", bool);
+#endif
+}
+
+
+void k_set_ttl(t)
+ int t;
+{
+#ifndef RAW_OUTPUT_IS_RAW
+ u_char ttl;
+
+ ttl = t;
+ if (setsockopt(igmp_socket, IPPROTO_IP, IP_MULTICAST_TTL,
+ (char *)&ttl, sizeof(ttl)) < 0)
+ log(LOG_ERR, errno, "setsockopt IP_MULTICAST_TTL %u", ttl);
+#endif
+ curttl = t;
+}
+
+
+void k_set_loop(l)
+ int l;
+{
+ u_char loop;
+
+ loop = l;
+ if (setsockopt(igmp_socket, IPPROTO_IP, IP_MULTICAST_LOOP,
+ (char *)&loop, sizeof(loop)) < 0)
+ log(LOG_ERR, errno, "setsockopt IP_MULTICAST_LOOP %u", loop);
+}
+
+
+void k_set_if(ifa)
+ u_int32 ifa;
+{
+ struct in_addr adr;
+
+ adr.s_addr = ifa;
+ if (setsockopt(igmp_socket, IPPROTO_IP, IP_MULTICAST_IF,
+ (char *)&adr, sizeof(adr)) < 0)
+ log(LOG_ERR, errno, "setsockopt IP_MULTICAST_IF %s",
+ inet_fmt(ifa, s1));
+}
+
+
+void k_join(grp, ifa)
+ u_int32 grp;
+ u_int32 ifa;
+{
+ struct ip_mreq mreq;
+
+ mreq.imr_multiaddr.s_addr = grp;
+ mreq.imr_interface.s_addr = ifa;
+
+ if (setsockopt(igmp_socket, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ (char *)&mreq, sizeof(mreq)) < 0)
+ log(LOG_WARNING, errno, "can't join group %s on interface %s",
+ inet_fmt(grp, s1), inet_fmt(ifa, s2));
+}
+
+
+void k_leave(grp, ifa)
+ u_int32 grp;
+ u_int32 ifa;
+{
+ struct ip_mreq mreq;
+
+ mreq.imr_multiaddr.s_addr = grp;
+ mreq.imr_interface.s_addr = ifa;
+
+ if (setsockopt(igmp_socket, IPPROTO_IP, IP_DROP_MEMBERSHIP,
+ (char *)&mreq, sizeof(mreq)) < 0)
+ log(LOG_WARNING, errno, "can't leave group %s on interface %s",
+ inet_fmt(grp, s1), inet_fmt(ifa, s2));
+}
+
+
+void k_init_dvmrp()
+{
+#ifdef OLD_KERNEL
+ if (setsockopt(igmp_socket, IPPROTO_IP, MRT_INIT,
+ (char *)NULL, 0) < 0)
+#else
+ int v=1;
+
+ if (setsockopt(igmp_socket, IPPROTO_IP, MRT_INIT,
+ (char *)&v, sizeof(int)) < 0)
+#endif
+ log(LOG_ERR, errno, "can't enable Multicast routing in kernel");
+}
+
+
+void k_stop_dvmrp()
+{
+ if (setsockopt(igmp_socket, IPPROTO_IP, MRT_DONE,
+ (char *)NULL, 0) < 0)
+ log(LOG_WARNING, errno, "can't disable Multicast routing in kernel");
+}
+
+
+void k_add_vif(vifi, v)
+ vifi_t vifi;
+ struct uvif *v;
+{
+ struct vifctl vc;
+
+ vc.vifc_vifi = vifi;
+ vc.vifc_flags = v->uv_flags & VIFF_KERNEL_FLAGS;
+ vc.vifc_threshold = v->uv_threshold;
+ vc.vifc_rate_limit = v->uv_rate_limit;
+ vc.vifc_lcl_addr.s_addr = v->uv_lcl_addr;
+ vc.vifc_rmt_addr.s_addr = v->uv_rmt_addr;
+
+ if (setsockopt(igmp_socket, IPPROTO_IP, MRT_ADD_VIF,
+ (char *)&vc, sizeof(vc)) < 0)
+ log(LOG_ERR, errno, "setsockopt MRT_ADD_VIF on vif %d", vifi);
+}
+
+
+void k_del_vif(vifi)
+ vifi_t vifi;
+{
+ if (setsockopt(igmp_socket, IPPROTO_IP, MRT_DEL_VIF,
+ (char *)&vifi, sizeof(vifi)) < 0)
+ log(LOG_ERR, errno, "setsockopt MRT_DEL_VIF on vif %d", vifi);
+}
+
+
+/*
+ * Adds a (source, mcastgrp) entry to the kernel
+ */
+void k_add_rg(origin, g)
+ u_int32 origin;
+ struct gtable *g;
+{
+ struct mfcctl mc;
+ vifi_t i;
+
+#ifdef DEBUG_MFC
+ md_log(MD_ADD, origin, g->gt_mcastgrp);
+#endif
+ /* copy table values so that setsockopt can process it */
+ mc.mfcc_origin.s_addr = origin;
+#ifdef OLD_KERNEL
+ mc.mfcc_originmask.s_addr = 0xffffffff;
+#endif
+ mc.mfcc_mcastgrp.s_addr = g->gt_mcastgrp;
+ mc.mfcc_parent = g->gt_route ? g->gt_route->rt_parent : NO_VIF;
+ for (i = 0; i < numvifs; i++)
+ mc.mfcc_ttls[i] = g->gt_ttls[i];
+
+ /* write to kernel space */
+ if (setsockopt(igmp_socket, IPPROTO_IP, MRT_ADD_MFC,
+ (char *)&mc, sizeof(mc)) < 0) {
+#ifdef DEBUG_MFC
+ md_log(MD_ADD_FAIL, origin, g->gt_mcastgrp);
+#endif
+ log(LOG_WARNING, errno, "setsockopt MRT_ADD_MFC",
+ inet_fmt(origin, s1), inet_fmt(g->gt_mcastgrp, s2));
+ }
+}
+
+
+/*
+ * Deletes a (source, mcastgrp) entry from the kernel
+ */
+int k_del_rg(origin, g)
+ u_int32 origin;
+ struct gtable *g;
+{
+ struct mfcctl mc;
+ int retval;
+
+#ifdef DEBUG_MFC
+ md_log(MD_DEL, origin, g->gt_mcastgrp);
+#endif
+ /* copy table values so that setsockopt can process it */
+ mc.mfcc_origin.s_addr = origin;
+#ifdef OLD_KERNEL
+ mc.mfcc_originmask.s_addr = 0xffffffff;
+#endif
+ mc.mfcc_mcastgrp.s_addr = g->gt_mcastgrp;
+
+ /* write to kernel space */
+ if ((retval = setsockopt(igmp_socket, IPPROTO_IP, MRT_DEL_MFC,
+ (char *)&mc, sizeof(mc))) < 0) {
+#ifdef DEBUG_MFC
+ md_log(MD_DEL_FAIL, origin, g->gt_mcastgrp);
+#endif
+ log(LOG_WARNING, errno, "setsockopt MRT_DEL_MFC of (%s %s)",
+ inet_fmt(origin, s1), inet_fmt(g->gt_mcastgrp, s2));
+ }
+
+ return retval;
+}
+
+/*
+ * Get the kernel's idea of what version of mrouted needs to run with it.
+ */
+int k_get_version()
+{
+#ifdef OLD_KERNEL
+ return -1;
+#else
+ int vers;
+ int len = sizeof(vers);
+
+ if (getsockopt(igmp_socket, IPPROTO_IP, MRT_VERSION,
+ (char *)&vers, &len) < 0)
+ log(LOG_ERR, errno,
+ "getsockopt MRT_VERSION: perhaps your kernel is too old");
+
+ return vers;
+#endif
+}
+
+#if 0
+/*
+ * Get packet counters
+ */
+int
+k_get_vif_count(vifi, icount, ocount, ibytes, obytes)
+ vifi_t vifi;
+ int *icount, *ocount, *ibytes, *obytes;
+{
+ struct sioc_vif_req vreq;
+ int retval = 0;
+
+ vreq.vifi = vifi;
+ if (ioctl(udp_socket, SIOCGETVIFCNT, (char *)&vreq) < 0) {
+ log(LOG_WARNING, errno, "SIOCGETVIFCNT on vif %d", vifi);
+ vreq.icount = vreq.ocount = vreq.ibytes =
+ vreq.obytes = 0xffffffff;
+ retval = 1;
+ }
+ if (icount)
+ *icount = vreq.icount;
+ if (ocount)
+ *ocount = vreq.ocount;
+ if (ibytes)
+ *ibytes = vreq.ibytes;
+ if (obytes)
+ *obytes = vreq.obytes;
+ return retval;
+}
+
+/*
+ * Get counters for a desired source and group.
+ */
+int
+k_get_sg_count(src, grp, pktcnt, bytecnt, wrong_if)
+ u_int32 src;
+ u_int32 grp;
+ struct sg_count *retval;
+{
+ struct sioc_sg_req sgreq;
+ int retval = 0;
+
+ sgreq.src.s_addr = src;
+ sgreq.grp.s_addr = grp;
+ if (ioctl(udp_socket, SIOCGETSGCNT, (char *)&sgreq) < 0) {
+ log(LOG_WARNING, errno, "SIOCGETSGCNT on (%s %s)",
+ inet_fmt(src, s1), inet_fmt(grp, s2));
+ sgreq.pktcnt = sgreq.bytecnt = sgreq.wrong_if = 0xffffffff;
+ return 1;
+ }
+ if (pktcnt)
+ *pktcnt = sgreq.pktcnt;
+ if (bytecnt)
+ *bytecnt = sgreq.bytecnt;
+ if (wrong_if)
+ *wrong_if = sgreq.wrong_if;
+ return retval;
+}
+#endif
diff --git a/usr.sbin/mrouted/main.c b/usr.sbin/mrouted/main.c
new file mode 100644
index 0000000..87ab3ff
--- /dev/null
+++ b/usr.sbin/mrouted/main.c
@@ -0,0 +1,1060 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * main.c,v 3.8.4.29 1998/03/01 01:49:00 fenner Exp
+ */
+
+/*
+ * Written by Steve Deering, Stanford University, February 1989.
+ *
+ * (An earlier version of DVMRP was implemented by David Waitzman of
+ * BBN STC by extending Berkeley's routed program. Some of Waitzman's
+ * extensions have been incorporated into mrouted, but none of the
+ * original routed code has been adopted.)
+ */
+
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif
+
+#include <err.h>
+#include "defs.h"
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+#include <fcntl.h>
+#include <paths.h>
+
+#ifdef SNMP
+#include "snmp.h"
+#endif
+
+extern char *configfilename;
+char versionstring[100];
+
+static char pidfilename[] = _PATH_MROUTED_PID;
+static char dumpfilename[] = _PATH_MROUTED_DUMP;
+static char cachefilename[] = _PATH_MROUTED_CACHE;
+static char genidfilename[] = _PATH_MROUTED_GENID;
+
+static int haveterminal = 1;
+int did_final_init = 0;
+
+static int sighandled = 0;
+#define GOT_SIGINT 0x01
+#define GOT_SIGHUP 0x02
+#define GOT_SIGUSR1 0x04
+#define GOT_SIGUSR2 0x08
+
+int cache_lifetime = DEFAULT_CACHE_LIFETIME;
+int prune_lifetime = AVERAGE_PRUNE_LIFETIME;
+
+int debug = 0;
+char *progname;
+time_t mrouted_init_time;
+
+#ifdef SNMP
+#define NHANDLERS 34
+#else
+#define NHANDLERS 2
+#endif
+
+static struct ihandler {
+ int fd; /* File descriptor */
+ ihfunc_t func; /* Function to call with &fd_set */
+} ihandlers[NHANDLERS];
+static int nhandlers = 0;
+
+static struct debugname {
+ char *name;
+ int level;
+ int nchars;
+} debugnames[] = {
+ { "packet", DEBUG_PKT, 2 },
+ { "pkt", DEBUG_PKT, 3 },
+ { "pruning", DEBUG_PRUNE, 1 },
+ { "prunes", DEBUG_PRUNE, 1 },
+ { "routing", DEBUG_ROUTE, 1 },
+ { "routes", DEBUG_ROUTE, 1 },
+ { "route_detail", DEBUG_RTDETAIL, 6 },
+ { "rtdetail", DEBUG_RTDETAIL, 2 },
+ { "peers", DEBUG_PEER, 2 },
+ { "neighbors", DEBUG_PEER, 1 },
+ { "cache", DEBUG_CACHE, 1 },
+ { "timeout", DEBUG_TIMEOUT, 1 },
+ { "callout", DEBUG_TIMEOUT, 2 },
+ { "interface", DEBUG_IF, 2 },
+ { "vif", DEBUG_IF, 1 },
+ { "membership", DEBUG_MEMBER, 1 },
+ { "groups", DEBUG_MEMBER, 1 },
+ { "traceroute", DEBUG_TRACE, 2 },
+ { "mtrace", DEBUG_TRACE, 2 },
+ { "igmp", DEBUG_IGMP, 1 },
+ { "icmp", DEBUG_ICMP, 2 },
+ { "rsrr", DEBUG_RSRR, 2 },
+ { "3", 0xffffffff, 1 } /* compat. */
+};
+
+/*
+ * Forward declarations.
+ */
+static void final_init __P((void *));
+static void fasttimer __P((void *));
+static void timer __P((void *));
+static void dump __P((void));
+static void dump_version __P((FILE *));
+static void fdump __P((void));
+static void cdump __P((void));
+static void restart __P((void));
+static void handler __P((int));
+static void cleanup __P((void));
+static void resetlogging __P((void *));
+static void usage __P((void));
+
+/* To shut up gcc -Wstrict-prototypes */
+int main __P((int argc, char **argv));
+
+int
+register_input_handler(fd, func)
+ int fd;
+ ihfunc_t func;
+{
+ if (nhandlers >= NHANDLERS)
+ return -1;
+
+ ihandlers[nhandlers].fd = fd;
+ ihandlers[nhandlers++].func = func;
+
+ return 0;
+}
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register int recvlen;
+ int dummy;
+ FILE *fp;
+ struct timeval tv, difftime, curtime, lasttime, *timeout;
+ u_int32 prev_genid;
+ int vers;
+ fd_set rfds, readers;
+ int nfds, n, i, secs;
+ extern char todaysversion[];
+ struct sigaction sa;
+#ifdef SNMP
+ struct timeval timeout, *tvp = &timeout;
+ struct timeval sched, *svp = &sched, now, *nvp = &now;
+ int index, block;
+#endif
+
+ setlinebuf(stderr);
+
+ if (geteuid() != 0)
+ errx(1, "must be root");
+
+ progname = strrchr(argv[0], '/');
+ if (progname)
+ progname++;
+ else
+ progname = argv[0];
+
+ argv++, argc--;
+ while (argc > 0 && *argv[0] == '-') {
+ if (strcmp(*argv, "-d") == 0) {
+ if (argc > 1 && *(argv + 1)[0] != '-') {
+ char *p,*q;
+ int i, len;
+ struct debugname *d;
+
+ argv++, argc--;
+ debug = 0;
+ p = *argv; q = NULL;
+ while (p) {
+ q = strchr(p, ',');
+ if (q)
+ *q++ = '\0';
+ len = strlen(p);
+ for (i = 0, d = debugnames;
+ i < sizeof(debugnames) / sizeof(debugnames[0]);
+ i++, d++)
+ if (len >= d->nchars && strncmp(d->name, p, len) == 0)
+ break;
+ if (i == sizeof(debugnames) / sizeof(debugnames[0])) {
+ int j = 0xffffffff;
+ int k = 0;
+ fprintf(stderr, "Valid debug levels: ");
+ for (i = 0, d = debugnames;
+ i < sizeof(debugnames) / sizeof(debugnames[0]);
+ i++, d++) {
+ if ((j & d->level) == d->level) {
+ if (k++)
+ putc(',', stderr);
+ fputs(d->name, stderr);
+ j &= ~d->level;
+ }
+ }
+ putc('\n', stderr);
+ usage();
+ }
+ debug |= d->level;
+ p = q;
+ }
+ } else
+ debug = DEFAULT_DEBUG;
+ } else if (strcmp(*argv, "-c") == 0) {
+ if (argc > 1) {
+ argv++, argc--;
+ configfilename = *argv;
+ } else
+ usage();
+ } else if (strcmp(*argv, "-p") == 0) {
+ log(LOG_WARNING, 0, "disabling pruning is no longer supported");
+#ifdef SNMP
+ } else if (strcmp(*argv, "-P") == 0) {
+ if (argc > 1 && isdigit(*(argv + 1)[0])) {
+ argv++, argc--;
+ dest_port = atoi(*argv);
+ } else
+ dest_port = DEFAULT_PORT;
+#endif
+ } else
+ usage();
+ argv++, argc--;
+ }
+
+ if (argc > 0)
+ usage();
+
+ if (debug != 0) {
+ struct debugname *d;
+ char c;
+ int tmpd = debug;
+
+ fprintf(stderr, "debug level 0x%x ", debug);
+ c = '(';
+ for (d = debugnames; d < debugnames +
+ sizeof(debugnames) / sizeof(debugnames[0]); d++) {
+ if ((tmpd & d->level) == d->level) {
+ tmpd &= ~d->level;
+ fprintf(stderr, "%c%s", c, d->name);
+ c = ',';
+ }
+ }
+ fprintf(stderr, ")\n");
+ }
+
+#ifdef LOG_DAEMON
+ (void)openlog("mrouted", LOG_PID, LOG_DAEMON);
+ (void)setlogmask(LOG_UPTO(LOG_NOTICE));
+#else
+ (void)openlog("mrouted", LOG_PID);
+#endif
+ sprintf(versionstring, "mrouted version %s", todaysversion);
+
+ log(LOG_DEBUG, 0, "%s starting", versionstring);
+
+#ifdef SYSV
+ srand48(time(NULL));
+#endif
+
+ /*
+ * Get generation id
+ */
+ gettimeofday(&tv, 0);
+ dvmrp_genid = tv.tv_sec;
+
+ fp = fopen(genidfilename, "r");
+ if (fp != NULL) {
+ fscanf(fp, "%d", &prev_genid);
+ if (prev_genid == dvmrp_genid)
+ dvmrp_genid++;
+ (void) fclose(fp);
+ }
+
+ fp = fopen(genidfilename, "w");
+ if (fp != NULL) {
+ fprintf(fp, "%d", dvmrp_genid);
+ (void) fclose(fp);
+ }
+
+ /* Start up the log rate-limiter */
+ resetlogging(NULL);
+
+ callout_init();
+ init_igmp();
+ init_icmp();
+ init_ipip();
+ init_routes();
+ init_ktable();
+#ifndef OLD_KERNEL
+ /*
+ * Unfortunately, you can't k_get_version() unless you've
+ * k_init_dvmrp()'d. Now that we want to move the
+ * k_init_dvmrp() to later in the initialization sequence,
+ * we have to do the disgusting hack of initializing,
+ * getting the version, then stopping the kernel multicast
+ * forwarding.
+ */
+ k_init_dvmrp();
+ vers = k_get_version();
+ k_stop_dvmrp();
+ /*XXX
+ * This function must change whenever the kernel version changes
+ */
+ if ((((vers >> 8) & 0xff) != 3) ||
+ ((vers & 0xff) != 5))
+ log(LOG_ERR, 0, "kernel (v%d.%d)/mrouted (v%d.%d) version mismatch",
+ (vers >> 8) & 0xff, vers & 0xff,
+ PROTOCOL_VERSION, MROUTED_VERSION);
+#endif
+
+#ifdef SNMP
+ if (i = snmp_init())
+ return i;
+
+ gettimeofday(nvp, 0);
+ if (nvp->tv_usec < 500000L){
+ svp->tv_usec = nvp->tv_usec + 500000L;
+ svp->tv_sec = nvp->tv_sec;
+ } else {
+ svp->tv_usec = nvp->tv_usec - 500000L;
+ svp->tv_sec = nvp->tv_sec + 1;
+ }
+#endif /* SNMP */
+
+ init_vifs();
+
+#ifdef RSRR
+ rsrr_init();
+#endif /* RSRR */
+
+ sa.sa_handler = handler;
+ sa.sa_flags = 0; /* Interrupt system calls */
+ sigemptyset(&sa.sa_mask);
+ sigaction(SIGHUP, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGUSR1, &sa, NULL);
+ sigaction(SIGUSR2, &sa, NULL);
+
+ 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(_PATH_TTY, 2);
+ if (t >= 0) {
+ (void)ioctl(t, TIOCNOTTY, (char *)0);
+ (void)close(t);
+ }
+#else
+ if (setsid() < 0)
+ perror("setsid");
+#endif
+#endif
+ }
+
+ fp = fopen(pidfilename, "w");
+ if (fp != NULL) {
+ fprintf(fp, "%d\n", (int)getpid());
+ (void) fclose(fp);
+ }
+
+ /* XXX HACK
+ * This will cause black holes for the first few seconds after startup,
+ * since we are exchanging routes but not actually forwarding.
+ * However, it eliminates much of the startup transient.
+ *
+ * It's possible that we can set a flag which says not to report any
+ * routes (just accept reports) until this timer fires, and then
+ * do a report_to_all_neighbors(ALL_ROUTES) immediately before
+ * turning on DVMRP.
+ */
+ timer_setTimer(10, final_init, NULL);
+
+ /*
+ * Main receive loop.
+ */
+ dummy = 0;
+ difftime.tv_usec = 0;
+ gettimeofday(&curtime, NULL);
+ lasttime = curtime;
+ for(;;) {
+ bcopy((char *)&readers, (char *)&rfds, sizeof(rfds));
+ secs = timer_nextTimer();
+ if (secs == -1)
+ timeout = NULL;
+ else {
+ timeout = &tv;
+ timeout->tv_sec = secs;
+ timeout->tv_usec = 0;
+ }
+#ifdef SNMP
+ THIS IS BROKEN
+ if (nvp->tv_sec > svp->tv_sec
+ || (nvp->tv_sec == svp->tv_sec && nvp->tv_usec > svp->tv_usec)){
+ alarmTimer(nvp);
+ eventTimer(nvp);
+ if (nvp->tv_usec < 500000L){
+ svp->tv_usec = nvp->tv_usec + 500000L;
+ svp->tv_sec = nvp->tv_sec;
+ } else {
+ svp->tv_usec = nvp->tv_usec - 500000L;
+ svp->tv_sec = nvp->tv_sec + 1;
+ }
+ }
+
+ tvp = &timeout;
+ tvp->tv_sec = 0;
+ tvp->tv_usec = 500000L;
+
+ block = 0;
+ snmp_select_info(&nfds, &rfds, tvp, &block);
+ if (block == 1)
+ tvp = NULL; /* block without timeout */
+ if ((n = select(nfds, &rfds, NULL, NULL, tvp)) < 0)
+#endif
+ if (sighandled) {
+ if (sighandled & GOT_SIGINT) {
+ sighandled &= ~GOT_SIGINT;
+ break;
+ }
+ if (sighandled & GOT_SIGHUP) {
+ sighandled &= ~GOT_SIGHUP;
+ restart();
+ }
+ if (sighandled & GOT_SIGUSR1) {
+ sighandled &= ~GOT_SIGUSR1;
+ fdump();
+ }
+ if (sighandled & GOT_SIGUSR2) {
+ sighandled &= ~GOT_SIGUSR2;
+ cdump();
+ }
+ }
+ if ((n = select(nfds, &rfds, NULL, NULL, timeout)) < 0) {
+ if (errno != EINTR)
+ log(LOG_WARNING, errno, "select failed");
+ continue;
+ }
+
+ if (n > 0) {
+ if (FD_ISSET(igmp_socket, &rfds)) {
+ recvlen = recvfrom(igmp_socket, recv_buf, RECV_BUF_SIZE,
+ 0, NULL, &dummy);
+ if (recvlen < 0) {
+ if (errno != EINTR) log(LOG_ERR, errno, "recvfrom");
+ continue;
+ }
+ accept_igmp(recvlen);
+ }
+
+ for (i = 0; i < nhandlers; i++) {
+ if (FD_ISSET(ihandlers[i].fd, &rfds)) {
+ (*ihandlers[i].func)(ihandlers[i].fd, &rfds);
+ }
+ }
+ }
+
+#ifdef SNMP
+ THIS IS BROKEN
+ snmp_read(&rfds);
+ snmp_timeout(); /* poll */
+#endif
+ /*
+ * Handle timeout queue.
+ *
+ * If select + packet processing took more than 1 second,
+ * or if there is a timeout pending, age the timeout queue.
+ *
+ * If not, collect usec in difftime to make sure that the
+ * time doesn't drift too badly.
+ *
+ * If the timeout handlers took more than 1 second,
+ * age the timeout queue again. XXX This introduces the
+ * potential for infinite loops!
+ */
+ do {
+ /*
+ * If the select timed out, then there's no other
+ * activity to account for and we don't need to
+ * call gettimeofday.
+ */
+ if (n == 0) {
+ curtime.tv_sec = lasttime.tv_sec + secs;
+ curtime.tv_usec = lasttime.tv_usec;
+ n = -1; /* don't do this next time through the loop */
+ } else
+ gettimeofday(&curtime, NULL);
+ difftime.tv_sec = curtime.tv_sec - lasttime.tv_sec;
+ difftime.tv_usec += curtime.tv_usec - lasttime.tv_usec;
+ while (difftime.tv_usec >= 1000000) {
+ difftime.tv_sec++;
+ difftime.tv_usec -= 1000000;
+ }
+ if (difftime.tv_usec < 0) {
+ difftime.tv_sec--;
+ difftime.tv_usec += 1000000;
+ }
+ lasttime = curtime;
+ if (secs == 0 || difftime.tv_sec > 0)
+ age_callout_queue(difftime.tv_sec);
+ secs = -1;
+ } while (difftime.tv_sec > 0);
+ }
+ log(LOG_NOTICE, 0, "%s exiting", versionstring);
+ cleanup();
+ exit(0);
+}
+
+static void
+usage()
+{
+ fprintf(stderr,
+ "usage: mrouted [-p] [-c configfile] [-d [debug_level]]\n");
+ exit(1);
+}
+
+static void
+final_init(i)
+ void *i;
+{
+ char *s = (char *)i;
+
+ log(LOG_NOTICE, 0, "%s%s", versionstring, s ? s : "");
+ if (s)
+ free(s);
+
+ k_init_dvmrp(); /* enable DVMRP routing in kernel */
+
+ /*
+ * Install the vifs in the kernel as late as possible in the
+ * initialization sequence.
+ */
+ init_installvifs();
+
+ time(&mrouted_init_time);
+ did_final_init = 1;
+}
+
+/*
+ * routine invoked every second. Its main goal is to cycle through
+ * the routing table and send partial updates to all neighbors at a
+ * rate that will cause the entire table to be sent in ROUTE_REPORT_INTERVAL
+ * seconds. Also, every TIMER_INTERVAL seconds it calls timer() to
+ * do all the other time-based processing.
+ */
+static void
+fasttimer(i)
+ void *i;
+{
+ static unsigned int tlast;
+ static unsigned int nsent;
+ register unsigned int t = tlast + 1;
+ register int n;
+
+ /*
+ * if we're in the last second, send everything that's left.
+ * otherwise send at least the fraction we should have sent by now.
+ */
+ if (t >= ROUTE_REPORT_INTERVAL) {
+ register int nleft = nroutes - nsent;
+ while (nleft > 0) {
+ if ((n = report_next_chunk()) <= 0)
+ break;
+ nleft -= n;
+ }
+ tlast = 0;
+ nsent = 0;
+ } else {
+ register unsigned int ncum = nroutes * t / ROUTE_REPORT_INTERVAL;
+ while (nsent < ncum) {
+ if ((n = report_next_chunk()) <= 0)
+ break;
+ nsent += n;
+ }
+ tlast = t;
+ }
+
+ timer_setTimer(1, fasttimer, NULL);
+}
+
+/*
+ * The 'virtual_time' variable is initialized to a value that will cause the
+ * first invocation of timer() to send a probe or route report to all vifs
+ * and send group membership queries to all subnets for which this router is
+ * querier. This first invocation occurs approximately TIMER_INTERVAL seconds
+ * after the router starts up. Note that probes for neighbors and queries
+ * for group memberships are also sent at start-up time, as part of initial-
+ * ization. This repetition after a short interval is desirable for quickly
+ * building up topology and membership information in the presence of possible
+ * packet loss.
+ *
+ * 'virtual_time' advances at a rate that is only a crude approximation of
+ * real time, because it does not take into account any time spent processing,
+ * and because the timer intervals are sometimes shrunk by a random amount to
+ * avoid unwanted synchronization with other routers.
+ */
+
+u_long virtual_time = 0;
+
+
+/*
+ * Timer routine. Performs periodic neighbor probing, route reporting, and
+ * group querying duties, and drives various timers in routing entries and
+ * virtual interface data structures.
+ */
+static void
+timer(i)
+ void *i;
+{
+ age_routes(); /* Advance the timers in the route entries */
+ age_vifs(); /* Advance the timers for neighbors */
+ age_table_entry(); /* Advance the timers for the cache entries */
+
+ if (virtual_time % IGMP_QUERY_INTERVAL == 0) {
+ /*
+ * Time to query the local group memberships on all subnets
+ * for which this router is the elected querier.
+ */
+ query_groups();
+ }
+
+ if (virtual_time % NEIGHBOR_PROBE_INTERVAL == 0) {
+ /*
+ * Time to send a probe on all vifs from which no neighbors have
+ * been heard. Also, check if any inoperative interfaces have now
+ * come up. (If they have, they will also be probed as part of
+ * their initialization.)
+ */
+ probe_for_neighbors();
+
+ if (vifs_down)
+ check_vif_state();
+ }
+
+ delay_change_reports = FALSE;
+ if (routes_changed) {
+ /*
+ * Some routes have changed since the last timer interrupt, but
+ * have not been reported yet. Report the changed routes to all
+ * neighbors.
+ */
+ report_to_all_neighbors(CHANGED_ROUTES);
+ }
+
+#ifdef SNMP
+ sync_timer();
+#endif
+
+ /*
+ * Advance virtual time
+ */
+ virtual_time += TIMER_INTERVAL;
+ timer_setTimer(TIMER_INTERVAL, timer, NULL);
+}
+
+
+static void
+cleanup()
+{
+ static in_cleanup = 0;
+
+ if (!in_cleanup) {
+ in_cleanup++;
+#ifdef RSRR
+ rsrr_clean();
+#endif /* RSRR */
+ expire_all_routes();
+ report_to_all_neighbors(ALL_ROUTES);
+ if (did_final_init)
+ k_stop_dvmrp();
+ }
+}
+
+/*
+ * Signal handler. Take note of the fact that the signal arrived
+ * so that the main loop can take care of it.
+ */
+static void
+handler(sig)
+ int sig;
+{
+ switch (sig) {
+ case SIGINT:
+ case SIGTERM:
+ sighandled |= GOT_SIGINT;
+ break;
+
+ case SIGHUP:
+ sighandled |= GOT_SIGHUP;
+ break;
+
+ case SIGUSR1:
+ sighandled |= GOT_SIGUSR1;
+ break;
+
+ case SIGUSR2:
+ sighandled |= GOT_SIGUSR2;
+ break;
+ }
+}
+
+/*
+ * Dump internal data structures to stderr.
+ */
+static void
+dump()
+{
+ dump_vifs(stderr);
+ dump_routes(stderr);
+}
+
+static void
+dump_version(fp)
+ FILE *fp;
+{
+ time_t t;
+
+ time(&t);
+ fprintf(fp, "%s ", versionstring);
+ if (did_final_init)
+ fprintf(fp, "up %s",
+ scaletime(t - mrouted_init_time));
+ else
+ fprintf(fp, "(not yet initialized)");
+ fprintf(fp, " %s\n", ctime(&t));
+}
+
+/*
+ * Dump internal data structures to a file.
+ */
+static void
+fdump()
+{
+ FILE *fp;
+
+ fp = fopen(dumpfilename, "w");
+ if (fp != NULL) {
+ dump_version(fp);
+ dump_vifs(fp);
+ dump_routes(fp);
+ (void) fclose(fp);
+ }
+}
+
+
+/*
+ * Dump local cache contents to a file.
+ */
+static void
+cdump()
+{
+ FILE *fp;
+
+ fp = fopen(cachefilename, "w");
+ if (fp != NULL) {
+ dump_version(fp);
+ dump_cache(fp);
+ (void) fclose(fp);
+ }
+}
+
+
+/*
+ * Restart mrouted
+ */
+static void
+restart()
+{
+ char *s;
+
+ s = (char *)malloc(sizeof(" restart"));
+ if (s == NULL)
+ log(LOG_ERR, 0, "out of memory");
+ strcpy(s, " restart");
+
+ /*
+ * reset all the entries
+ */
+ free_all_prunes();
+ free_all_routes();
+ free_all_callouts();
+ stop_all_vifs();
+ k_stop_dvmrp();
+ close(igmp_socket);
+ close(udp_socket);
+ did_final_init = 0;
+
+ /*
+ * start processing again
+ */
+ dvmrp_genid++;
+
+ init_igmp();
+ init_routes();
+ init_ktable();
+ init_vifs();
+ /*XXX Schedule final_init() as main does? */
+ final_init(s);
+
+ /* schedule timer interrupts */
+ timer_setTimer(1, fasttimer, NULL);
+ timer_setTimer(TIMER_INTERVAL, timer, NULL);
+}
+
+#define LOG_MAX_MSGS 20 /* if > 20/minute then shut up for a while */
+#define LOG_SHUT_UP 600 /* shut up for 10 minutes */
+static int log_nmsgs = 0;
+
+static void
+resetlogging(arg)
+ void *arg;
+{
+ int nxttime = 60;
+ void *narg = NULL;
+
+ if (arg == NULL && log_nmsgs > LOG_MAX_MSGS) {
+ nxttime = LOG_SHUT_UP;
+ narg = (void *)&log_nmsgs; /* just need some valid void * */
+ syslog(LOG_WARNING, "logging too fast, shutting up for %d minutes",
+ LOG_SHUT_UP / 60);
+ } else {
+ log_nmsgs = 0;
+ }
+
+ timer_setTimer(nxttime, resetlogging, narg);
+}
+
+char *
+scaletime(t)
+ u_long t;
+{
+#define SCALETIMEBUFLEN 20
+ static char buf1[20];
+ static char buf2[20];
+ static char *buf = buf1;
+ char *p;
+
+ p = buf;
+ if (buf == buf1)
+ buf = buf2;
+ else
+ buf = buf1;
+
+ /* XXX snprintf */
+ sprintf(p, "%2ld:%02ld:%02ld", t / 3600, (t % 3600) / 60, t % 60);
+ p[SCALETIMEBUFLEN - 1] = '\0';
+ return p;
+}
+
+#ifdef RINGBUFFER
+#define NLOGMSGS 10000
+#define LOGMSGSIZE 200
+char *logmsg[NLOGMSGS];
+static int logmsgno = 0;
+
+void
+printringbuf()
+{
+ FILE *f;
+ int i;
+
+ f = fopen("/var/tmp/mrouted.log", "a");
+ if (f == NULL) {
+ log(LOG_ERR, errno, "can't open /var/tmp/mrouted.log");
+ /*NOTREACHED*/
+ }
+ fprintf(f, "--------------------------------------------\n");
+
+ i = (logmsgno + 1) % NLOGMSGS;
+
+ while (i != logmsgno) {
+ if (*logmsg[i]) {
+ fprintf(f, "%s\n", logmsg[i]);
+ *logmsg[i] = '\0';
+ }
+ i = (i + 1) % NLOGMSGS;
+ }
+
+ fclose(f);
+}
+#endif
+
+/*
+ * Log errors and other messages to the system log daemon and to stderr,
+ * according to the severity of the message and the current debug level.
+ * For errors of severity LOG_ERR or worse, terminate the program.
+ */
+#ifdef __STDC__
+void
+log(int severity, int syserr, char *format, ...)
+{
+ va_list ap;
+ static char fmt[211] = "warning - ";
+ char *msg;
+ struct timeval now;
+ time_t now_sec;
+ struct tm *thyme;
+#ifdef RINGBUFFER
+ static int ringbufinit = 0;
+#endif
+
+ va_start(ap, format);
+#else
+/*VARARGS3*/
+void
+log(severity, syserr, format, va_alist)
+ int severity, syserr;
+ char *format;
+ va_dcl
+{
+ va_list ap;
+ static char fmt[311] = "warning - ";
+ char *msg;
+ char tbuf[20];
+ struct timeval now;
+ time_t now_sec;
+ struct tm *thyme;
+#ifdef RINGBUFFER
+ static int ringbufinit = 0;
+#endif
+
+ va_start(ap);
+#endif
+ vsnprintf(&fmt[10], sizeof(fmt) - 10, format, ap);
+ va_end(ap);
+ msg = (severity == LOG_WARNING) ? fmt : &fmt[10];
+
+#ifdef RINGBUFFER
+ if (!ringbufinit) {
+ int i;
+
+ for (i = 0; i < NLOGMSGS; i++) {
+ logmsg[i] = malloc(LOGMSGSIZE);
+ if (logmsg[i] == 0) {
+ syslog(LOG_ERR, "out of memory");
+ exit(-1);
+ }
+ *logmsg[i] = 0;
+ }
+ ringbufinit = 1;
+ }
+ gettimeofday(&now,NULL);
+ now_sec = now.tv_sec;
+ thyme = localtime(&now_sec);
+ snprintf(logmsg[logmsgno++], LOGMSGSIZE, "%02d:%02d:%02d.%03ld %s err %d",
+ thyme->tm_hour, thyme->tm_min, thyme->tm_sec,
+ now.tv_usec / 1000, msg, syserr);
+ logmsgno %= NLOGMSGS;
+ if (severity <= LOG_NOTICE)
+#endif
+ /*
+ * Log to stderr if we haven't forked yet and it's a warning or worse,
+ * or if we're debugging.
+ */
+ if (haveterminal && (debug || severity <= LOG_WARNING)) {
+ gettimeofday(&now,NULL);
+ now_sec = now.tv_sec;
+ thyme = localtime(&now_sec);
+ if (!debug)
+ fprintf(stderr, "%s: ", progname);
+ fprintf(stderr, "%02d:%02d:%02d.%03ld %s", thyme->tm_hour,
+ thyme->tm_min, thyme->tm_sec, now.tv_usec / 1000, msg);
+ if (syserr == 0)
+ fprintf(stderr, "\n");
+ else if (syserr < sys_nerr)
+ fprintf(stderr, ": %s\n", sys_errlist[syserr]);
+ else
+ fprintf(stderr, ": errno %d\n", syserr);
+ }
+
+ /*
+ * Always log things that are worse than warnings, no matter what
+ * the log_nmsgs rate limiter says.
+ * Only count things worse than debugging in the rate limiter
+ * (since if you put daemon.debug in syslog.conf you probably
+ * actually want to log the debugging messages so they shouldn't
+ * be rate-limited)
+ */
+ if ((severity < LOG_WARNING) || (log_nmsgs < LOG_MAX_MSGS)) {
+ if (severity < LOG_DEBUG)
+ log_nmsgs++;
+ if (syserr != 0) {
+ errno = syserr;
+ syslog(severity, "%s: %m", msg);
+ } else
+ syslog(severity, "%s", msg);
+ }
+
+ if (severity <= LOG_ERR) exit(-1);
+}
+
+#ifdef DEBUG_MFC
+void
+md_log(what, origin, mcastgrp)
+ int what;
+ u_int32 origin, mcastgrp;
+{
+ static FILE *f = NULL;
+ struct timeval tv;
+ u_int32 buf[4];
+
+ if (!f) {
+ if ((f = fopen("/tmp/mrouted.clog", "w")) == NULL) {
+ log(LOG_ERR, errno, "open /tmp/mrouted.clog");
+ }
+ }
+
+ gettimeofday(&tv, NULL);
+ buf[0] = tv.tv_sec;
+ buf[1] = what;
+ buf[2] = origin;
+ buf[3] = mcastgrp;
+
+ fwrite(buf, sizeof(u_int32), 4, f);
+}
+#endif
diff --git a/usr.sbin/mrouted/map-mbone.8 b/usr.sbin/mrouted/map-mbone.8
new file mode 100644
index 0000000..5260042
--- /dev/null
+++ b/usr.sbin/mrouted/map-mbone.8
@@ -0,0 +1,91 @@
+.\" $FreeBSD$
+.\"
+.Dd May 8, 1995
+.Dt MAP-MBONE 8
+.Os
+.Sh NAME
+.Nm map-mbone
+.Nd multicast connection mapper
+.Sh SYNOPSIS
+.Nm
+.Op Fl d Ar debug_level
+.Op Fl f
+.Op Fl g
+.Op Fl n
+.Op Fl r Ar retry_count
+.Op Fl t Ar timeout_count
+.Op Ar starting_router
+.Sh DESCRIPTION
+.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..f99247b
--- /dev/null
+++ b/usr.sbin/mrouted/map-mbone/Makefile
@@ -0,0 +1,15 @@
+# $FreeBSD$
+
+S= ${.CURDIR}/..
+.PATH: $S
+
+PROG= map-mbone
+MAN= map-mbone.8
+SRCS= mapper.c
+
+CFLAGS+= -I$S
+
+DPADD= ${LIBMROUTED}
+LDADD= ${LIBMROUTED}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mrouted/mapper.c b/usr.sbin/mrouted/mapper.c
new file mode 100644
index 0000000..0f09835
--- /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[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <err.h>
+#include <string.h>
+#include <netdb.h>
+#include <sys/time.h>
+#include "defs.h"
+#include <arpa/inet.h>
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#define DEFAULT_TIMEOUT 2 /* How long to wait before retrying requests */
+#define DEFAULT_RETRIES 1 /* How many times to ask each router */
+
+
+/* All IP addresses are stored in the data structure in NET order. */
+
+typedef struct neighbor {
+ struct neighbor *next;
+ u_int32 addr; /* IP address in NET order */
+ u_char metric; /* TTL cost of forwarding */
+ u_char threshold; /* TTL threshold to forward */
+ u_short flags; /* flags on connection */
+#define NF_PRESENT 0x8000 /* True if flags are meaningful */
+} Neighbor;
+
+typedef struct interface {
+ struct interface *next;
+ u_int32 addr; /* IP address of the interface in NET order */
+ Neighbor *neighbors; /* List of neighbors' IP addresses */
+} Interface;
+
+typedef struct node {
+ u_int32 addr; /* IP address of this entry in NET order */
+ u_int32 version; /* which mrouted version is running */
+ int tries; /* How many requests sent? -1 for aliases */
+ union {
+ struct node *alias; /* If alias, to what? */
+ struct interface *interfaces; /* Else, neighbor data */
+ } u;
+ struct node *left, *right;
+} Node;
+
+
+Node *routers = 0;
+u_int32 our_addr, target_addr = 0; /* in NET order */
+int debug = 0;
+int retries = DEFAULT_RETRIES;
+int timeout = DEFAULT_TIMEOUT;
+int show_names = TRUE;
+vifi_t numvifs; /* to keep loader happy */
+ /* (see COPY_TABLES macro called in kern.c) */
+
+Node * find_node __P((u_int32 addr, Node **ptr));
+Interface * find_interface __P((u_int32 addr, Node *node));
+Neighbor * find_neighbor __P((u_int32 addr, Node *node));
+int main __P((int argc, char *argv[]));
+void ask __P((u_int32 dst));
+void ask2 __P((u_int32 dst));
+int retry_requests __P((Node *node));
+char * inet_name __P((u_int32 addr));
+void print_map __P((Node *node));
+char * graph_name __P((u_int32 addr, char *buf, int len));
+void graph_edges __P((Node *node));
+void elide_aliases __P((Node *node));
+void graph_map __P((void));
+int get_number __P((int *var, int deflt, char ***pargv,
+ int *pargc));
+u_int32 host_addr __P((char *name));
+static void usage __P((void));
+
+
+Node *find_node(addr, ptr)
+ u_int32 addr;
+ Node **ptr;
+{
+ Node *n = *ptr;
+
+ if (!n) {
+ *ptr = n = (Node *) malloc(sizeof(Node));
+ n->addr = addr;
+ n->version = 0;
+ n->tries = 0;
+ n->u.interfaces = 0;
+ n->left = n->right = 0;
+ return n;
+ } else if (addr == n->addr)
+ return n;
+ else if (addr < n->addr)
+ return find_node(addr, &(n->left));
+ else
+ return find_node(addr, &(n->right));
+}
+
+
+Interface *find_interface(addr, node)
+ u_int32 addr;
+ Node *node;
+{
+ Interface *ifc;
+
+ for (ifc = node->u.interfaces; ifc; ifc = ifc->next)
+ if (ifc->addr == addr)
+ return ifc;
+
+ ifc = (Interface *) malloc(sizeof(Interface));
+ ifc->addr = addr;
+ ifc->next = node->u.interfaces;
+ node->u.interfaces = ifc;
+ ifc->neighbors = 0;
+
+ return ifc;
+}
+
+
+Neighbor *find_neighbor(addr, node)
+ u_int32 addr;
+ Node *node;
+{
+ Interface *ifc;
+
+ for (ifc = node->u.interfaces; ifc; ifc = ifc->next) {
+ Neighbor *nb;
+
+ for (nb = ifc->neighbors; nb; nb = nb->next)
+ if (nb->addr == addr)
+ return nb;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Log errors and other messages to stderr, according to the severity of the
+ * message and the current debug level. For errors of severity LOG_ERR or
+ * worse, terminate the program.
+ */
+#ifdef __STDC__
+void
+log(int severity, int syserr, char *format, ...)
+{
+ va_list ap;
+ char fmt[100];
+
+ va_start(ap, format);
+#else
+/*VARARGS3*/
+void
+log(severity, syserr, format, va_alist)
+ int severity, syserr;
+ char *format;
+ va_dcl
+{
+ va_list ap;
+ char fmt[100];
+
+ va_start(ap);
+#endif
+
+ switch (debug) {
+ case 0: if (severity > LOG_WARNING) return;
+ case 1: if (severity > LOG_NOTICE ) return;
+ case 2: if (severity > LOG_INFO ) return;
+ default:
+ fmt[0] = '\0';
+ if (severity == LOG_WARNING)
+ strcpy(fmt, "warning - ");
+ strncat(fmt, format, sizeof(fmt)-strlen(fmt));
+ fmt[sizeof(fmt)-1]='\0';
+ vfprintf(stderr, fmt, ap);
+ if (syserr == 0)
+ fprintf(stderr, "\n");
+ else if (syserr < sys_nerr)
+ fprintf(stderr, ": %s\n", sys_errlist[syserr]);
+ else
+ fprintf(stderr, ": errno %d\n", syserr);
+ }
+
+ if (severity <= LOG_ERR)
+ exit(1);
+}
+
+
+/*
+ * Send a neighbors-list request.
+ */
+void ask(dst)
+ u_int32 dst;
+{
+ send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS,
+ htonl(MROUTED_LEVEL), 0);
+}
+
+void ask2(dst)
+ u_int32 dst;
+{
+ send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS2,
+ htonl(MROUTED_LEVEL), 0);
+}
+
+
+/*
+ * Process an incoming group membership report.
+ */
+void accept_group_report(src, dst, group, r_type)
+ u_int32 src, dst, group;
+ int r_type;
+{
+ log(LOG_INFO, 0, "ignoring IGMP group membership report from %s to %s",
+ inet_fmt(src, s1), inet_fmt(dst, s2));
+}
+
+
+/*
+ * Process an incoming neighbor probe message.
+ */
+void accept_probe(src, dst, p, datalen, level)
+ u_int32 src, dst, level;
+ char *p;
+ int datalen;
+{
+ log(LOG_INFO, 0, "ignoring DVMRP probe from %s to %s",
+ inet_fmt(src, s1), inet_fmt(dst, s2));
+}
+
+
+/*
+ * Process an incoming route report message.
+ */
+void accept_report(src, dst, p, datalen, level)
+ u_int32 src, dst, level;
+ char *p;
+ int datalen;
+{
+ log(LOG_INFO, 0, "ignoring DVMRP routing report from %s to %s",
+ inet_fmt(src, s1), inet_fmt(dst, s2));
+}
+
+
+/*
+ * Process an incoming neighbor-list request message.
+ */
+void accept_neighbor_request(src, dst)
+ u_int32 src, dst;
+{
+ if (src != our_addr)
+ log(LOG_INFO, 0,
+ "ignoring spurious DVMRP neighbor request from %s to %s",
+ inet_fmt(src, s1), inet_fmt(dst, s2));
+}
+
+void accept_neighbor_request2(src, dst)
+ u_int32 src, dst;
+{
+ if (src != our_addr)
+ log(LOG_INFO, 0,
+ "ignoring spurious DVMRP neighbor request2 from %s to %s",
+ inet_fmt(src, s1), inet_fmt(dst, s2));
+}
+
+
+/*
+ * Process an incoming neighbor-list message.
+ */
+void accept_neighbors(src, dst, p, datalen, level)
+ u_int32 src, dst, level;
+ u_char *p;
+ int datalen;
+{
+ Node *node = find_node(src, &routers);
+
+ if (node->tries == 0) /* Never heard of 'em; must have hit them at */
+ node->tries = 1; /* least once, though...*/
+ else if (node->tries == -1) /* follow alias link */
+ node = node->u.alias;
+
+#define GET_ADDR(a) (a = ((u_int32)*p++ << 24), a += ((u_int32)*p++ << 16),\
+ a += ((u_int32)*p++ << 8), a += *p++)
+
+ /* if node is running a recent mrouted, ask for additional info */
+ if (level != 0) {
+ node->version = level;
+ node->tries = 1;
+ ask2(src);
+ return;
+ }
+
+ if (debug > 3) {
+ int i;
+
+ fprintf(stderr, " datalen = %d\n", datalen);
+ for (i = 0; i < datalen; i++) {
+ if ((i & 0xF) == 0)
+ fprintf(stderr, " ");
+ fprintf(stderr, " %02x", p[i]);
+ if ((i & 0xF) == 0xF)
+ fprintf(stderr, "\n");
+ }
+ if ((datalen & 0xF) != 0xF)
+ fprintf(stderr, "\n");
+ }
+
+ while (datalen > 0) { /* loop through interfaces */
+ u_int32 ifc_addr;
+ u_char metric, threshold, ncount;
+ Node *ifc_node;
+ Interface *ifc;
+ Neighbor *old_neighbors;
+
+ if (datalen < 4 + 3) {
+ log(LOG_WARNING, 0, "received truncated interface record from %s",
+ inet_fmt(src, s1));
+ return;
+ }
+
+ GET_ADDR(ifc_addr);
+ ifc_addr = htonl(ifc_addr);
+ metric = *p++;
+ threshold = *p++;
+ ncount = *p++;
+ datalen -= 4 + 3;
+
+ /* Fix up any alias information */
+ ifc_node = find_node(ifc_addr, &routers);
+ if (ifc_node->tries == 0) { /* new node */
+ ifc_node->tries = -1;
+ ifc_node->u.alias = node;
+ } else if (ifc_node != node
+ && (ifc_node->tries > 0 || ifc_node->u.alias != node)) {
+ /* must merge two hosts' nodes */
+ Interface *ifc_i, *next_ifc_i;
+
+ if (ifc_node->tries == -1) {
+ Node *tmp = ifc_node->u.alias;
+
+ ifc_node->u.alias = node;
+ ifc_node = tmp;
+ }
+
+ /* Merge ifc_node (foo_i) into node (foo_n) */
+
+ if (ifc_node->tries > node->tries)
+ node->tries = ifc_node->tries;
+
+ for (ifc_i = ifc_node->u.interfaces; ifc_i; ifc_i = next_ifc_i) {
+ Neighbor *nb_i, *next_nb_i, *nb_n;
+ Interface *ifc_n = find_interface(ifc_i->addr, node);
+
+ old_neighbors = ifc_n->neighbors;
+ for (nb_i = ifc_i->neighbors; nb_i; nb_i = next_nb_i) {
+ next_nb_i = nb_i->next;
+ for (nb_n = old_neighbors; nb_n; nb_n = nb_n->next)
+ if (nb_i->addr == nb_n->addr) {
+ if (nb_i->metric != nb_n->metric
+ || nb_i->threshold != nb_n->threshold)
+ log(LOG_WARNING, 0,
+ "inconsistent %s for neighbor %s of %s",
+ "metric/threshold",
+ inet_fmt(nb_i->addr, s1),
+ inet_fmt(node->addr, s2));
+ free(nb_i);
+ break;
+ }
+ if (!nb_n) { /* no match for this neighbor yet */
+ nb_i->next = ifc_n->neighbors;
+ ifc_n->neighbors = nb_i;
+ }
+ }
+
+ next_ifc_i = ifc_i->next;
+ free(ifc_i);
+ }
+
+ ifc_node->tries = -1;
+ ifc_node->u.alias = node;
+ }
+
+ ifc = find_interface(ifc_addr, node);
+ old_neighbors = ifc->neighbors;
+
+ /* Add the neighbors for this interface */
+ while (ncount--) {
+ u_int32 neighbor;
+ Neighbor *nb;
+ Node *n_node;
+
+ if (datalen < 4) {
+ log(LOG_WARNING, 0, "received truncated neighbor list from %s",
+ inet_fmt(src, s1));
+ return;
+ }
+
+ GET_ADDR(neighbor);
+ neighbor = htonl(neighbor);
+ datalen -= 4;
+
+ for (nb = old_neighbors; nb; nb = nb->next)
+ if (nb->addr == neighbor) {
+ if (metric != nb->metric || threshold != nb->threshold)
+ log(LOG_WARNING, 0,
+ "inconsistent %s for neighbor %s of %s",
+ "metric/threshold",
+ inet_fmt(nb->addr, s1), inet_fmt(node->addr, s2));
+ goto next_neighbor;
+ }
+
+ nb = (Neighbor *) malloc(sizeof(Neighbor));
+ nb->next = ifc->neighbors;
+ ifc->neighbors = nb;
+ nb->addr = neighbor;
+ nb->metric = metric;
+ nb->threshold = threshold;
+ nb->flags = 0;
+
+ n_node = find_node(neighbor, &routers);
+ if (n_node->tries == 0 && !target_addr) { /* it's a new router */
+ ask(neighbor);
+ n_node->tries = 1;
+ }
+
+ next_neighbor: ;
+ }
+ }
+}
+
+void accept_neighbors2(src, dst, p, datalen, level)
+ u_int32 src, dst, level;
+ u_char *p;
+ int datalen;
+{
+ Node *node = find_node(src, &routers);
+ u_int broken_cisco = ((level & 0xffff) == 0x020a); /* 10.2 */
+ /* well, only possibly_broken_cisco, but that's too long to type. */
+
+ if (node->tries == 0) /* Never heard of 'em; must have hit them at */
+ node->tries = 1; /* least once, though...*/
+ else if (node->tries == -1) /* follow alias link */
+ node = node->u.alias;
+
+ while (datalen > 0) { /* loop through interfaces */
+ u_int32 ifc_addr;
+ u_char metric, threshold, ncount, flags;
+ Node *ifc_node;
+ Interface *ifc;
+ Neighbor *old_neighbors;
+
+ if (datalen < 4 + 4) {
+ log(LOG_WARNING, 0, "received truncated interface record from %s",
+ inet_fmt(src, s1));
+ return;
+ }
+
+ ifc_addr = *(u_int32*)p;
+ p += 4;
+ metric = *p++;
+ threshold = *p++;
+ flags = *p++;
+ ncount = *p++;
+ datalen -= 4 + 4;
+
+ if (broken_cisco && ncount == 0) /* dumb Ciscos */
+ ncount = 1;
+ if (broken_cisco && ncount > 15) /* dumb Ciscos */
+ ncount = ncount & 0xf;
+
+ /* Fix up any alias information */
+ ifc_node = find_node(ifc_addr, &routers);
+ if (ifc_node->tries == 0) { /* new node */
+ ifc_node->tries = -1;
+ ifc_node->u.alias = node;
+ } else if (ifc_node != node
+ && (ifc_node->tries > 0 || ifc_node->u.alias != node)) {
+ /* must merge two hosts' nodes */
+ Interface *ifc_i, *next_ifc_i;
+
+ if (ifc_node->tries == -1) {
+ Node *tmp = ifc_node->u.alias;
+
+ ifc_node->u.alias = node;
+ ifc_node = tmp;
+ }
+
+ /* Merge ifc_node (foo_i) into node (foo_n) */
+
+ if (ifc_node->tries > node->tries)
+ node->tries = ifc_node->tries;
+
+ for (ifc_i = ifc_node->u.interfaces; ifc_i; ifc_i = next_ifc_i) {
+ Neighbor *nb_i, *next_nb_i, *nb_n;
+ Interface *ifc_n = find_interface(ifc_i->addr, node);
+
+ old_neighbors = ifc_n->neighbors;
+ for (nb_i = ifc_i->neighbors; nb_i; nb_i = next_nb_i) {
+ next_nb_i = nb_i->next;
+ for (nb_n = old_neighbors; nb_n; nb_n = nb_n->next)
+ if (nb_i->addr == nb_n->addr) {
+ if (nb_i->metric != nb_n->metric
+ || nb_i->threshold != nb_i->threshold)
+ log(LOG_WARNING, 0,
+ "inconsistent %s for neighbor %s of %s",
+ "metric/threshold",
+ inet_fmt(nb_i->addr, s1),
+ inet_fmt(node->addr, s2));
+ free(nb_i);
+ break;
+ }
+ if (!nb_n) { /* no match for this neighbor yet */
+ nb_i->next = ifc_n->neighbors;
+ ifc_n->neighbors = nb_i;
+ }
+ }
+
+ next_ifc_i = ifc_i->next;
+ free(ifc_i);
+ }
+
+ ifc_node->tries = -1;
+ ifc_node->u.alias = node;
+ }
+
+ ifc = find_interface(ifc_addr, node);
+ old_neighbors = ifc->neighbors;
+
+ /* Add the neighbors for this interface */
+ while (ncount-- && datalen > 0) {
+ u_int32 neighbor;
+ Neighbor *nb;
+ Node *n_node;
+
+ if (datalen < 4) {
+ log(LOG_WARNING, 0, "received truncated neighbor list from %s",
+ inet_fmt(src, s1));
+ return;
+ }
+
+ neighbor = *(u_int32*)p;
+ p += 4;
+ datalen -= 4;
+ if (neighbor == 0)
+ /* make leaf nets point to themselves */
+ neighbor = ifc_addr;
+
+ for (nb = old_neighbors; nb; nb = nb->next)
+ if (nb->addr == neighbor) {
+ if (metric != nb->metric || threshold != nb->threshold)
+ log(LOG_WARNING, 0,
+ "inconsistent %s for neighbor %s of %s",
+ "metric/threshold",
+ inet_fmt(nb->addr, s1), inet_fmt(node->addr, s2));
+ goto next_neighbor;
+ }
+
+ nb = (Neighbor *) malloc(sizeof(Neighbor));
+ nb->next = ifc->neighbors;
+ ifc->neighbors = nb;
+ nb->addr = neighbor;
+ nb->metric = metric;
+ nb->threshold = threshold;
+ nb->flags = flags | NF_PRESENT;
+
+ n_node = find_node(neighbor, &routers);
+ if (n_node->tries == 0 && !target_addr) { /* it's a new router */
+ ask(neighbor);
+ n_node->tries = 1;
+ }
+
+ next_neighbor: ;
+ }
+ }
+}
+
+
+void check_vif_state()
+{
+ log(LOG_NOTICE, 0, "network marked down...");
+}
+
+
+int retry_requests(node)
+ Node *node;
+{
+ int result;
+
+ if (node) {
+ result = retry_requests(node->left);
+ if (node->tries > 0 && node->tries < retries) {
+ if (node->version)
+ ask2(node->addr);
+ else
+ ask(node->addr);
+ node->tries++;
+ result = 1;
+ }
+ return retry_requests(node->right) || result;
+ } else
+ return 0;
+}
+
+
+char *inet_name(addr)
+ u_int32 addr;
+{
+ struct hostent *e;
+
+ e = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET);
+
+ return e ? e->h_name : 0;
+}
+
+
+void print_map(node)
+ Node *node;
+{
+ if (node) {
+ char *name, *addr;
+
+ print_map(node->left);
+
+ addr = inet_fmt(node->addr, s1);
+ if (!target_addr
+ || (node->tries >= 0 && node->u.interfaces)
+ || (node->tries == -1
+ && node->u.alias->tries >= 0
+ && node->u.alias->u.interfaces)) {
+ if (show_names && (name = inet_name(node->addr)))
+ printf("%s (%s):", addr, name);
+ else
+ printf("%s:", addr);
+ if (node->tries < 0)
+ printf(" alias for %s\n\n", inet_fmt(node->u.alias->addr, s1));
+ else if (!node->u.interfaces)
+ printf(" no response to query\n\n");
+ else {
+ Interface *ifc;
+
+ if (node->version)
+ printf(" <v%d.%d>", node->version & 0xff,
+ (node->version >> 8) & 0xff);
+ printf("\n");
+ for (ifc = node->u.interfaces; ifc; ifc = ifc->next) {
+ Neighbor *nb;
+ char *ifc_name = inet_fmt(ifc->addr, s1);
+ int ifc_len = strlen(ifc_name);
+ int count = 0;
+
+ printf(" %s:", ifc_name);
+ for (nb = ifc->neighbors; nb; nb = nb->next) {
+ if (count > 0)
+ printf("%*s", ifc_len + 5, "");
+ printf(" %s", inet_fmt(nb->addr, s1));
+ if (show_names && (name = inet_name(nb->addr)))
+ printf(" (%s)", name);
+ printf(" [%d/%d", nb->metric, nb->threshold);
+ if (nb->flags) {
+ u_short flags = nb->flags;
+ if (flags & DVMRP_NF_TUNNEL)
+ printf("/tunnel");
+ if (flags & DVMRP_NF_SRCRT)
+ printf("/srcrt");
+ if (flags & DVMRP_NF_QUERIER)
+ printf("/querier");
+ if (flags & DVMRP_NF_DISABLED)
+ printf("/disabled");
+ if (flags & DVMRP_NF_DOWN)
+ printf("/down");
+ }
+ printf("]\n");
+ count++;
+ }
+ }
+ printf("\n");
+ }
+ }
+ print_map(node->right);
+ }
+}
+
+
+char *graph_name(addr, buf, len)
+ u_int32 addr;
+ char *buf;
+ int len;
+{
+ char *name;
+
+ if (len < sizeof("255.255.255.255")) {
+ fprintf(stderr,
+"Buffer too small in graph_name, provided %d bytes, but needed %d.\n",
+ len, sizeof("255.255.255.255"));
+ return NULL;
+ }
+ if (show_names && (name = inet_name(addr))) {
+ strncpy(buf, name, len - 1);
+ buf[len - 1] = '\0';
+ } else
+ inet_fmt(addr, buf);
+
+ return buf;
+}
+
+
+void graph_edges(node)
+ Node *node;
+{
+ Interface *ifc;
+ Neighbor *nb;
+ char name[100];
+
+ if (node) {
+ graph_edges(node->left);
+ if (node->tries >= 0) {
+ printf(" %d {$ NP %d0 %d0 $} \"%s%s\" \n",
+ (int) node->addr,
+ node->addr & 0xFF, (node->addr >> 8) & 0xFF,
+ graph_name(node->addr, name, sizeof(name)),
+ node->u.interfaces ? "" : "*");
+ for (ifc = node->u.interfaces; ifc; ifc = ifc->next)
+ for (nb = ifc->neighbors; nb; nb = nb->next) {
+ Node *nb_node = find_node(nb->addr, &routers);
+ Neighbor *nb2;
+
+ if (nb_node->tries < 0)
+ nb_node = nb_node->u.alias;
+
+ if (node != nb_node &&
+ (!(nb2 = find_neighbor(node->addr, nb_node))
+ || node->addr < nb_node->addr)) {
+ printf(" %d \"%d/%d",
+ nb_node->addr, nb->metric, nb->threshold);
+ if (nb2 && (nb2->metric != nb->metric
+ || nb2->threshold != nb->threshold))
+ printf(",%d/%d", nb2->metric, nb2->threshold);
+ if (nb->flags & NF_PRESENT)
+ printf("%s%s",
+ nb->flags & DVMRP_NF_SRCRT ? "" :
+ nb->flags & DVMRP_NF_TUNNEL ? "E" : "P",
+ nb->flags & DVMRP_NF_DOWN ? "D" : "");
+ printf("\"\n");
+ }
+ }
+ printf(" ;\n");
+ }
+ graph_edges(node->right);
+ }
+}
+
+void elide_aliases(node)
+ Node *node;
+{
+ if (node) {
+ elide_aliases(node->left);
+ if (node->tries >= 0) {
+ Interface *ifc;
+
+ for (ifc = node->u.interfaces; ifc; ifc = ifc->next) {
+ Neighbor *nb;
+
+ for (nb = ifc->neighbors; nb; nb = nb->next) {
+ Node *nb_node = find_node(nb->addr, &routers);
+
+ if (nb_node->tries < 0)
+ nb->addr = nb_node->u.alias->addr;
+ }
+ }
+ }
+ elide_aliases(node->right);
+ }
+}
+
+void graph_map()
+{
+ time_t now = time(0);
+ char *nowstr = ctime(&now);
+
+ nowstr[24] = '\0'; /* Kill the newline at the end */
+ elide_aliases(routers);
+ printf("GRAPH \"Multicast Router Connectivity: %s\" = UNDIRECTED\n",
+ nowstr);
+ graph_edges(routers);
+ printf("END\n");
+}
+
+
+int get_number(var, deflt, pargv, pargc)
+ int *var, *pargc, deflt;
+ char ***pargv;
+{
+ if ((*pargv)[0][2] == '\0') { /* Get the value from the next argument */
+ if (*pargc > 1 && isdigit((*pargv)[1][0])) {
+ (*pargv)++, (*pargc)--;
+ *var = atoi((*pargv)[0]);
+ return 1;
+ } else if (deflt >= 0) {
+ *var = deflt;
+ return 1;
+ } else
+ return 0;
+ } else { /* Get value from the rest of this argument */
+ if (isdigit((*pargv)[0][2])) {
+ *var = atoi((*pargv)[0] + 2);
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+}
+
+
+u_int32 host_addr(name)
+ char *name;
+{
+ struct hostent *e = gethostbyname(name);
+ int addr;
+
+ if (e && e->h_length == sizeof(addr))
+ memcpy(&addr, e->h_addr_list[0], e->h_length);
+ else {
+ addr = inet_addr(name);
+ if (addr == -1)
+ addr = 0;
+ }
+
+ return addr;
+}
+
+
+int main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int flood = FALSE, graph = FALSE;
+
+ if (geteuid() != 0)
+ errx(1, "must be root");
+
+ init_igmp();
+ setuid(getuid());
+
+ setlinebuf(stderr);
+
+ argv++, argc--;
+ while (argc > 0 && argv[0][0] == '-') {
+ switch (argv[0][1]) {
+ case 'd':
+ if (!get_number(&debug, DEFAULT_DEBUG, &argv, &argc))
+ usage();
+ break;
+ case 'f':
+ flood = TRUE;
+ break;
+ case 'g':
+ graph = TRUE;
+ break;
+ case 'n':
+ show_names = FALSE;
+ break;
+ case 'r':
+ if (!get_number(&retries, -1, &argv, &argc))
+ usage();
+ break;
+ case 't':
+ if (!get_number(&timeout, -1, &argv, &argc))
+ usage();
+ break;
+ default:
+ usage();
+ }
+ argv++, argc--;
+ }
+
+ if (argc > 1) {
+ usage();
+ } else if (argc == 1 && !(target_addr = host_addr(argv[0])))
+ errx(2, "unknown host: %s", argv[0]);
+
+ if (debug)
+ fprintf(stderr, "Debug level %u\n", debug);
+
+ { /* Find a good local address for us. */
+ int udp;
+ struct sockaddr_in addr;
+ int addrlen = sizeof(addr);
+
+ addr.sin_family = AF_INET;
+#ifdef HAVE_SA_LEN
+ addr.sin_len = sizeof addr;
+#endif
+ addr.sin_addr.s_addr = dvmrp_group;
+ addr.sin_port = htons(2000); /* any port over 1024 will do... */
+ if ((udp = socket(AF_INET, SOCK_DGRAM, 0)) < 0
+ || connect(udp, (struct sockaddr *) &addr, sizeof(addr)) < 0
+ || getsockname(udp, (struct sockaddr *) &addr, &addrlen) < 0)
+ err(-1, "determining local address");
+ close(udp);
+ our_addr = addr.sin_addr.s_addr;
+ }
+
+ /* Send initial seed message to all local routers */
+ ask(target_addr ? target_addr : allhosts_group);
+
+ if (target_addr) {
+ Node *n = find_node(target_addr, &routers);
+
+ n->tries = 1;
+
+ if (flood)
+ target_addr = 0;
+ }
+
+ /* Main receive loop */
+ for(;;) {
+ fd_set fds;
+ struct timeval tv;
+ int count, recvlen, dummy = 0;
+
+ 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..b06b4b9
--- /dev/null
+++ b/usr.sbin/mrouted/mrinfo.8
@@ -0,0 +1,87 @@
+.\" $FreeBSD$
+.\"
+.Dd May 8, 1995
+.Dt MRINFO 8
+.Os
+.Sh NAME
+.Nm mrinfo
+.Nd display configuration info from a multicast router
+.Sh SYNOPSIS
+.Nm
+.Op Fl d Ar debug_level
+.Op Fl r Ar retry_count
+.Op Fl t Ar timeout_count
+.Ar multicast_router
+.Sh DESCRIPTION
+.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
+.Bd -literal
+.Nm mrinfo Ar mbone.phony.dom.net
+127.148.176.10 (mbone.phony.dom.net) [version 3.3]:
+ 127.148.176.10 -> 0.0.0.0 (?) [1/1/querier]
+ 127.148.176.10 -> 127.0.8.4 (mbone2.phony.dom.net) [1/45/tunnel]
+ 127.148.176.10 -> 105.1.41.9 (momoney.com) [1/32/tunnel/down]
+ 127.148.176.10 -> 143.192.152.119 (mbone.dipu.edu) [1/32/tunnel]
+.Ed
+.Pp
+For each neighbor of the queried multicast router, the IP of the queried router
+is displayed, followed by the IP and name of the neighbor.
+In square brackets
+the metric (cost of connection), the 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..a9f9dd9
--- /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[] =
+ "$FreeBSD$";
+/* original rcsid:
+ "@(#) Header: mrinfo.c,v 1.6 93/04/08 15:14:16 van Exp (LBL)";
+*/
+#endif
+
+#include <err.h>
+#include <netdb.h>
+#include <sys/time.h>
+#include "defs.h"
+#include <arpa/inet.h>
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#define DEFAULT_TIMEOUT 4 /* How long to wait before retrying requests */
+#define DEFAULT_RETRIES 3 /* How many times to ask each router */
+
+u_int32 our_addr, target_addr = 0; /* in NET order */
+int debug = 0;
+int nflag = 0;
+int retries = DEFAULT_RETRIES;
+int timeout = DEFAULT_TIMEOUT;
+int target_level = 0;
+vifi_t numvifs; /* to keep loader happy */
+ /* (see COPY_TABLES macro called in kern.c) */
+
+char * inet_name __P((u_int32 addr));
+void ask __P((u_int32 dst));
+void ask2 __P((u_int32 dst));
+int get_number __P((int *var, int deflt, char ***pargv,
+ int *pargc));
+u_int32 host_addr __P((char *name));
+static void usage __P((void));
+
+/* to shut up -Wstrict-prototypes */
+int main __P((int argc, char *argv[]));
+
+
+char *
+inet_name(addr)
+ u_int32 addr;
+{
+ struct hostent *e;
+ struct in_addr in;
+
+ if (addr == 0)
+ return "local";
+
+ if (nflag ||
+ (e = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET)) == NULL) {
+ in.s_addr = addr;
+ return (inet_ntoa(in));
+ }
+ return (e->h_name);
+}
+
+/*
+ * Log errors and other messages to stderr, according to the severity of the
+ * message and the current debug level. For errors of severity LOG_ERR or
+ * worse, terminate the program.
+ */
+#ifdef __STDC__
+void
+log(int severity, int syserr, char *format, ...)
+{
+ va_list ap;
+ char fmt[100];
+
+ va_start(ap, format);
+#else
+void
+log(severity, syserr, format, va_alist)
+ int severity, syserr;
+ char *format;
+ va_dcl
+{
+ va_list ap;
+ char fmt[100];
+
+ va_start(ap);
+#endif
+ switch (debug) {
+ case 0:
+ if (severity > LOG_WARNING)
+ return;
+ case 1:
+ if (severity > LOG_NOTICE)
+ return;
+ case 2:
+ if (severity > LOG_INFO)
+ return;
+ default:
+ fmt[0] = '\0';
+ if (severity == LOG_WARNING)
+ strcpy(fmt, "warning - ");
+ strncat(fmt, format, sizeof(fmt)-strlen(fmt));
+ fmt[sizeof(fmt)-1]='\0';
+ vfprintf(stderr, fmt, ap);
+ if (syserr == 0)
+ fprintf(stderr, "\n");
+ else if (syserr < sys_nerr)
+ fprintf(stderr, ": %s\n", sys_errlist[syserr]);
+ else
+ fprintf(stderr, ": errno %d\n", syserr);
+ }
+
+ if (severity <= LOG_ERR)
+ exit(1);
+}
+
+/*
+ * Send a neighbors-list request.
+ */
+void
+ask(dst)
+ u_int32 dst;
+{
+ send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS,
+ htonl(MROUTED_LEVEL), 0);
+}
+
+void
+ask2(dst)
+ u_int32 dst;
+{
+ send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS2,
+ htonl(MROUTED_LEVEL), 0);
+}
+
+/*
+ * Process an incoming neighbor-list message.
+ */
+void
+accept_neighbors(src, dst, p, datalen, level)
+ u_int32 src, dst, level;
+ u_char *p;
+ int datalen;
+{
+ u_char *ep = p + datalen;
+#define GET_ADDR(a) (a = ((u_int32)*p++ << 24), a += ((u_int32)*p++ << 16),\
+ a += ((u_int32)*p++ << 8), a += *p++)
+
+ printf("%s (%s):\n", inet_fmt(src, s1), inet_name(src));
+ while (p < ep) {
+ register u_int32 laddr;
+ register u_char metric;
+ register u_char thresh;
+ register int ncount;
+
+ GET_ADDR(laddr);
+ laddr = htonl(laddr);
+ metric = *p++;
+ thresh = *p++;
+ ncount = *p++;
+ while (--ncount >= 0) {
+ register u_int32 neighbor;
+ GET_ADDR(neighbor);
+ neighbor = htonl(neighbor);
+ printf(" %s -> ", inet_fmt(laddr, s1));
+ printf("%s (%s) [%d/%d]\n", inet_fmt(neighbor, s1),
+ inet_name(neighbor), metric, thresh);
+ }
+ }
+}
+
+void
+accept_neighbors2(src, dst, p, datalen, level)
+ u_int32 src, dst, level;
+ u_char *p;
+ int datalen;
+{
+ u_char *ep = p + datalen;
+ u_int broken_cisco = ((level & 0xffff) == 0x020a); /* 10.2 */
+ /* well, only possibly_broken_cisco, but that's too long to type. */
+ u_int majvers = level & 0xff;
+ u_int minvers = (level >> 8) & 0xff;
+
+ printf("%s (%s) [", inet_fmt(src, s1), inet_name(src));
+ if (majvers == 3 && minvers == 0xff)
+ printf("DVMRPv3 compliant");
+ else
+ printf("version %d.%d", majvers, minvers);
+ printf ("]:\n");
+
+ while (p < ep) {
+ register u_char metric;
+ register u_char thresh;
+ register u_char flags;
+ register int ncount;
+ register u_int32 laddr = *(u_int32*)p;
+
+ p += 4;
+ metric = *p++;
+ thresh = *p++;
+ flags = *p++;
+ ncount = *p++;
+ if (broken_cisco && ncount == 0) /* dumb Ciscos */
+ ncount = 1;
+ if (broken_cisco && ncount > 15) /* dumb Ciscos */
+ ncount = ncount & 0xf;
+ while (--ncount >= 0 && p < ep) {
+ register u_int32 neighbor = *(u_int32*)p;
+ p += 4;
+ printf(" %s -> ", inet_fmt(laddr, s1));
+ printf("%s (%s) [%d/%d", inet_fmt(neighbor, s1),
+ inet_name(neighbor), metric, thresh);
+ if (flags & DVMRP_NF_TUNNEL)
+ printf("/tunnel");
+ if (flags & DVMRP_NF_SRCRT)
+ printf("/srcrt");
+ if (flags & DVMRP_NF_PIM)
+ printf("/pim");
+ if (flags & DVMRP_NF_QUERIER)
+ printf("/querier");
+ if (flags & DVMRP_NF_DISABLED)
+ printf("/disabled");
+ if (flags & DVMRP_NF_DOWN)
+ printf("/down");
+ if (flags & DVMRP_NF_LEAF)
+ printf("/leaf");
+ printf("]\n");
+ }
+ }
+}
+
+int
+get_number(var, deflt, pargv, pargc)
+ int *var, *pargc, deflt;
+ char ***pargv;
+{
+ if ((*pargv)[0][2] == '\0') { /* Get the value from the next
+ * argument */
+ if (*pargc > 1 && isdigit((*pargv)[1][0])) {
+ (*pargv)++, (*pargc)--;
+ *var = atoi((*pargv)[0]);
+ return 1;
+ } else if (deflt >= 0) {
+ *var = deflt;
+ return 1;
+ } else
+ return 0;
+ } else { /* Get value from the rest of this argument */
+ if (isdigit((*pargv)[0][2])) {
+ *var = atoi((*pargv)[0] + 2);
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+}
+
+static void
+usage()
+{
+ fprintf(stderr,
+ "usage: mrinfo [-n] [-t timeout] [-r retries] [router]\n");
+ exit(1);
+}
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int tries;
+ int trynew;
+ struct timeval et;
+ struct hostent *hp;
+ struct hostent bogus;
+ char *host;
+ int curaddr;
+
+ if (geteuid() != 0)
+ errx(1, "must be root");
+
+ init_igmp();
+ setuid(getuid());
+
+ setlinebuf(stderr);
+
+ argv++, argc--;
+ while (argc > 0 && argv[0][0] == '-') {
+ switch (argv[0][1]) {
+ case 'd':
+ if (!get_number(&debug, DEFAULT_DEBUG, &argv, &argc))
+ usage();
+ break;
+ case 'n':
+ ++nflag;
+ break;
+ case 'r':
+ if (!get_number(&retries, -1, &argv, &argc))
+ usage();
+ break;
+ case 't':
+ if (!get_number(&timeout, -1, &argv, &argc))
+ usage();
+ break;
+ default:
+ usage();
+ }
+ argv++, argc--;
+ }
+ if (argc > 1)
+ usage();
+ if (argc == 1)
+ host = argv[0];
+ else
+ host = "127.0.0.1";
+
+ if ((target_addr = inet_addr(host)) != -1) {
+ hp = &bogus;
+ hp->h_length = sizeof(target_addr);
+ hp->h_addr_list = (char **)malloc(2 * sizeof(char *));
+ hp->h_addr_list[0] = malloc(hp->h_length);
+ memcpy(hp->h_addr_list[0], &target_addr, hp->h_length);
+ hp->h_addr_list[1] = 0;
+ } else
+ hp = gethostbyname(host);
+
+ if (hp == NULL || hp->h_length != sizeof(target_addr))
+ errx(1, "%s: no such host", argv[0]);
+ if (debug)
+ fprintf(stderr, "Debug level %u\n", debug);
+
+ /* Check all addresses; mrouters often have unreachable interfaces */
+ for (curaddr = 0; hp->h_addr_list[curaddr] != NULL; curaddr++) {
+ memcpy(&target_addr, hp->h_addr_list[curaddr], hp->h_length);
+ { /* Find a good local address for us. */
+ int udp;
+ struct sockaddr_in addr;
+ int addrlen = sizeof(addr);
+
+ addr.sin_family = AF_INET;
+#ifdef HAVE_SA_LEN
+ addr.sin_len = sizeof addr;
+#endif
+ addr.sin_addr.s_addr = target_addr;
+ addr.sin_port = htons(2000); /* any port over 1024 will
+ * do... */
+ if ((udp = socket(AF_INET, SOCK_DGRAM, 0)) < 0
+ || connect(udp, (struct sockaddr *) & addr, sizeof(addr)) < 0
+ || getsockname(udp, (struct sockaddr *) & addr, &addrlen) < 0)
+ err(-1, "determining local address");
+ close(udp);
+ our_addr = addr.sin_addr.s_addr;
+ }
+
+ tries = 0;
+ trynew = 1;
+ /*
+ * New strategy: send 'ask2' for two timeouts, then fall back
+ * to 'ask', since it's not very likely that we are going to
+ * find someone who only responds to 'ask' these days
+ */
+ ask2(target_addr);
+
+ gettimeofday(&et, 0);
+ et.tv_sec += timeout;
+
+ /* Main receive loop */
+ for (;;) {
+ fd_set fds;
+ struct timeval tv, now;
+ int count, recvlen, dummy = 0;
+ register u_int32 src, dst, group;
+ struct ip *ip;
+ struct igmp *igmp;
+ int ipdatalen, iphdrlen, igmpdatalen;
+
+ FD_ZERO(&fds);
+ FD_SET(igmp_socket, &fds);
+
+ gettimeofday(&now, 0);
+ tv.tv_sec = et.tv_sec - now.tv_sec;
+ tv.tv_usec = et.tv_usec - now.tv_usec;
+
+ if (tv.tv_usec < 0) {
+ tv.tv_usec += 1000000L;
+ --tv.tv_sec;
+ }
+ if (tv.tv_sec < 0)
+ tv.tv_sec = tv.tv_usec = 0;
+
+ count = select(igmp_socket + 1, &fds, 0, 0, &tv);
+
+ if (count < 0) {
+ if (errno != EINTR)
+ warn("select");
+ continue;
+ } else if (count == 0) {
+ log(LOG_DEBUG, 0, "Timed out receiving neighbor lists");
+ if (++tries > retries)
+ break;
+ /* If we've tried ASK_NEIGHBORS2 twice with
+ * no response, fall back to ASK_NEIGHBORS
+ */
+ if (tries == 2 && target_level == 0)
+ trynew = 0;
+ if (target_level == 0 && trynew == 0)
+ ask(target_addr);
+ else
+ ask2(target_addr);
+ gettimeofday(&et, 0);
+ et.tv_sec += timeout;
+ continue;
+ }
+ recvlen = recvfrom(igmp_socket, recv_buf, RECV_BUF_SIZE,
+ 0, NULL, &dummy);
+ if (recvlen <= 0) {
+ if (recvlen && errno != EINTR)
+ warn("recvfrom");
+ continue;
+ }
+
+ if (recvlen < sizeof(struct ip)) {
+ log(LOG_WARNING, 0,
+ "packet too short (%u bytes) for IP header",
+ recvlen);
+ continue;
+ }
+ ip = (struct ip *) recv_buf;
+ if (ip->ip_p == 0)
+ continue; /* Request to install cache entry */
+ src = ip->ip_src.s_addr;
+ dst = ip->ip_dst.s_addr;
+ iphdrlen = ip->ip_hl << 2;
+#ifdef RAW_INPUT_IS_RAW
+ ipdatalen = ntohs(ip->ip_len) - iphdrlen;
+#else
+ ipdatalen = ip->ip_len;
+#endif
+ if (iphdrlen + ipdatalen != recvlen) {
+ log(LOG_WARNING, 0,
+ "packet shorter (%u bytes) than hdr+data length (%u+%u)",
+ recvlen, iphdrlen, ipdatalen);
+ continue;
+ }
+ igmp = (struct igmp *) (recv_buf + iphdrlen);
+ group = igmp->igmp_group.s_addr;
+ igmpdatalen = ipdatalen - IGMP_MINLEN;
+ if (igmpdatalen < 0) {
+ log(LOG_WARNING, 0,
+ "IP data field too short (%u bytes) for IGMP, from %s",
+ ipdatalen, inet_fmt(src, s1));
+ continue;
+ }
+ if (igmp->igmp_type != IGMP_DVMRP)
+ continue;
+
+ switch (igmp->igmp_code) {
+ case DVMRP_NEIGHBORS:
+ case DVMRP_NEIGHBORS2:
+ if (src != target_addr) {
+ warnx("got reply from %s instead of %s",
+ inet_fmt(src, s1), inet_fmt(target_addr, s1));
+ /*continue;*/
+ }
+ break;
+ default:
+ continue; /* ignore all other DVMRP messages */
+ }
+
+ switch (igmp->igmp_code) {
+
+ case DVMRP_NEIGHBORS:
+ if (group) {
+ /* knows about DVMRP_NEIGHBORS2 msg */
+ if (target_level == 0) {
+ target_level = ntohl(group);
+ ask2(target_addr);
+ }
+ } else {
+ accept_neighbors(src, dst, (u_char *)(igmp + 1),
+ igmpdatalen, ntohl(group));
+ exit(0);
+ }
+ break;
+
+ case DVMRP_NEIGHBORS2:
+ accept_neighbors2(src, dst, (u_char *)(igmp + 1),
+ igmpdatalen, ntohl(group));
+ exit(0);
+ }
+ }
+ }
+ exit(1);
+}
+
+/* dummies */
+void accept_probe(src, dst, p, datalen, level)
+ u_int32 src, dst, level;
+ char *p;
+ int datalen;
+{
+}
+void accept_group_report(src, dst, group, r_type)
+ u_int32 src, dst, group;
+ int r_type;
+{
+}
+void accept_neighbor_request2(src, dst)
+ u_int32 src, dst;
+{
+}
+void accept_report(src, dst, p, datalen, level)
+ u_int32 src, dst, level;
+ char *p;
+ int datalen;
+{
+}
+void accept_neighbor_request(src, dst)
+ u_int32 src, dst;
+{
+}
+void accept_prune(src, dst, p, datalen)
+ u_int32 src, dst;
+ char *p;
+ int datalen;
+{
+}
+void accept_graft(src, dst, p, datalen)
+ u_int32 src, dst;
+ char *p;
+ int datalen;
+{
+}
+void accept_g_ack(src, dst, p, datalen)
+ u_int32 src, dst;
+ char *p;
+ int datalen;
+{
+}
+void add_table_entry(origin, mcastgrp)
+ u_int32 origin, mcastgrp;
+{
+}
+void check_vif_state()
+{
+}
+void accept_leave_message(src, dst, group)
+ u_int32 src, dst, group;
+{
+}
+void accept_mtrace(src, dst, group, data, no, datalen)
+ u_int32 src, dst, group;
+ char *data;
+ u_int no;
+ int datalen;
+{
+}
+void accept_membership_query(src, dst, group, tmo)
+ u_int32 src, dst, group;
+ int tmo;
+{
+}
+void accept_info_request(src, dst, p, datalen)
+ u_int32 src, dst;
+ u_char *p;
+ int datalen;
+{
+}
+void accept_info_reply(src, dst, p, datalen)
+ u_int32 src, dst;
+ u_char *p;
+ int datalen;
+{
+}
diff --git a/usr.sbin/mrouted/mrinfo/Makefile b/usr.sbin/mrouted/mrinfo/Makefile
new file mode 100644
index 0000000..f111aa9
--- /dev/null
+++ b/usr.sbin/mrouted/mrinfo/Makefile
@@ -0,0 +1,16 @@
+# $FreeBSD$
+
+S= ${.CURDIR}/..
+.PATH: $S
+
+PROG= mrinfo
+MAN= mrinfo.8
+BINOWN= root
+BINMODE= 4555
+
+CFLAGS+= -I$S
+
+DPADD= ${LIBMROUTED}
+LDADD= ${LIBMROUTED}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mrouted/mrouted.8 b/usr.sbin/mrouted/mrouted.8
new file mode 100644
index 0000000..695ae64
--- /dev/null
+++ b/usr.sbin/mrouted/mrouted.8
@@ -0,0 +1,598 @@
+.\"COPYRIGHT 1989 by The Board of Trustees of Leland Stanford Junior University.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd May 8, 1995
+.Dt MROUTED 8
+.Os
+.Sh NAME
+.Nm mrouted
+.Nd IP multicast routing daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl c Ar config_file
+.Op Fl d Op Ar debug_level
+.Op Fl p
+.Sh DESCRIPTION
+.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 .
+With the use of tunnels, it
+is not necessary for
+.Nm
+to have access to more than one physical subnet
+in order to perform multicast forwarding.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl c Ar config_file
+Specify an alternative file for configuration commands.
+Default is
+.Pa /etc/mrouted.conf .
+.It Fl d Op Ar debug_level
+If no
+.Fl d
+option is given, or if the debug level is specified as 0,
+.Nm
+detaches from the invoking terminal. Otherwise, it remains attached to the
+invoking terminal and responsive to signals from that terminal.
+Regardless of the debug level,
+.Nm
+always writes warning and error messages to the system
+log daemon. The
+.Fl debug-level
+argument is a comma-separated list of any of the following:
+.Bl -tag -width indent
+.It "packet"
+Display the type, source and destination of all packets sent or received.
+.It "pruning"
+Display more information about prunes sent or received.
+.It "routing"
+Display more information about routing update packets sent or received.
+.It "route_detail"
+Display routing updates in excruciating detail. This is generally way too
+much information.
+.It "neighbors"
+Display information about neighbor discovery.
+.It "cache"
+Display insertions, deletions and refreshes of entries in
+the kernel forwarding cache.
+.It "timeout"
+Debug timeouts and periodic processes.
+.It "interface"
+Display information about interfaces and their configuration.
+.It "membership"
+Display information about group memberships on physical interfaces.
+.It "traceroute"
+Display information about multicast traceroute requests
+passing through this router.
+.It "igmp"
+Display IGMP operation including group membership and querier election.
+.It "icmp"
+Monitor ICMP handling.
+.It "rsrr"
+Monitor RSRR operation.
+.El
+.Pp
+Upon startup,
+.Nm
+writes its pid to the file
+.Pa /var/run/mrouted.pid .
+.El
+.Sh CONFIGURATION
+.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 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.\&
+.Nm
+<3.3
+and all currently known versions of cisco's IOS) do not use the
+DVMRP generation ID to determine that a neighbor has rebooted. Prunes
+sent towards these neighbors should be kept short, in order to shorten
+the time to recover from a reboot. For use in this situation, the
+prune_lifetime keyword may be specified on an interface as described
+below.
+.It noflood
+.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 Ns '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.
+.Nm
+will never send or accept neighbor probes or
+route reports on this interface.
+.El
+.Pp
+In addition, the common vif commands described later may all be used on
+a phyint.
+.Pp
+The third section of the configuration file, also optional, describes
+the configuration of any DVMRP tunnels this router might have.
+.Bl -tag -width indent
+.It tunnel Ar "local-addr|ifname" Ar "remote-addr|remote-hostname"
+This command establishes a DVMRP tunnel between this host (on the interface
+described by
+.Ar local-addr
+or
+.Ar ifname )
+and a remote host (identified by
+.Ar remote-addr
+or
+.Ar remote-hostname ) .
+A remote hostname may only be used if
+it maps to a single IP address.
+A tunnel must be configured on both routers before it can be used.
+.Pp
+Be careful that the unicast route to the remote address goes out the
+interface specified by the
+.Ar "local-addr|ifname"
+argument. Some UNIX
+kernels rewrite the source address of
+.Nm Ns 's
+packets on their way out to contain the address of the transmission
+interface. This is best assured via a static host route.
+.El
+.Pp
+The common vif commands described below
+may all be used on tunnels or phyints.
+.Bl -tag -width indent
+.It metric Ar m
+The metric is the "cost" associated with receiving a datagram on the given
+interface or tunnel; it may be used to influence the choice of routes.
+The metric defaults to 1. Metrics should be kept as small as possible,
+because DVMRP cannot route along paths with a sum of metrics greater
+than 31.
+.It advert_metric Ar m
+The advert_metric is the "cost" associated with sending a datagram
+on the given interface or tunnel; it may be used to influence the choice
+of routes. The advert_metric defaults to 0. Note that the effective
+metric of a link is one end's metric plus the other end's advert_metric.
+.It threshold Ar t
+The threshold is the minimum IP time-to-live required for a multicast datagram
+to be forwarded to the given interface or tunnel. It is used to control the
+scope of multicast datagrams. (The TTL of forwarded packets is only compared
+to the threshold, it is not decremented by the threshold. Every multicast
+router decrements the TTL by exactly 1.) The default threshold is 1.
+.Pp
+In general, all multicast routers
+connected to a particular subnet or tunnel should
+use the same metric and threshold for that subnet or tunnel.
+.It rate_limit Ar r
+The rate_limit option allows the network administrator to specify a
+certain bandwidth in Kbits/second which would be allocated to multicast
+traffic. It defaults 0 (unlimited).
+.It boundary Ar "boundary-name|scoped-addr/mask-len"
+The boundary option allows an interface
+to be configured as an administrative boundary for the specified
+scoped address.
+Packets belonging to this address will not
+be forwarded on a scoped interface. The boundary option accepts either
+a name or a boundary spec. This command may be specified several times
+on an interface in order to describe multiple boundaries.
+.It passive
+No packets will be sent on this link or tunnel until we hear from the other
+end. This is useful for the "server" end of a tunnel that goes over
+a dial-on-demand link; configure the "server" end as passive and
+it will not send its periodic probes until it hears one from the other
+side, so will not keep the link up. If this option is specified on both
+ends of a tunnel, the tunnel will never come up.
+.It noflood
+As described above, but only applicable to this interface/tunnel.
+.It prune_lifetime Ar secs
+As described above, but only applicable to this interface/tunnel.
+.It rexmit_prunes Ar "[on|off]"
+As described above, but only applicable to this interface/tunnel.
+Recall that prune retransmission
+defaults to on 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.
+.Pp
+The list of routes follows the
+.Li accept
+or
+.Li deny
+keyword. If the keyword
+.Ar exact
+follows a route, then only that route is matched; otherwise, that route
+and any more specific route is matched. For example,
+.Li deny 0/0
+denys all routes, while
+.Li deny 0/0 exact
+denys only the default route. The default route may also be specified
+with the
+.Li default
+keyword.
+.Pp
+The
+.Ar bidir
+keyword enables bidirectional route filtering; the filter will be applied
+to routes on both output and input. Without the
+.Ar bidir
+keyword,
+.Li accept
+and
+.Li deny
+filters are only applied on input. Poison reverse routes are never
+filtered out.
+.El
+.Pp
+.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.
+.Pp
+.Bd -literal
+#
+# mrouted.conf example
+#
+# Name our boundaries to make it easier
+name LOCAL 239.255.0.0/16
+name EE 239.254.0.0/16
+#
+# le1 is our gateway to compsci, don't forward our
+# local groups to them
+phyint le1 boundary EE
+#
+# le2 is our interface on the classroom net, it has four
+# different length subnets on it.
+# note that you can use either an ip address or an
+# interface name
+phyint 172.16.12.38 boundary EE altnet 172.16.15.0/26
+ altnet 172.16.15.128/26 altnet 172.16.48.0/24
+#
+# atm0 is our ATM interface, which doesn't properly
+# support multicasting.
+phyint atm0 disable
+#
+# This is an internal tunnel to another EE subnet
+# Remove the default tunnel rate limit, since this
+# tunnel is over ethernets
+tunnel 192.168.5.4 192.168.55.101 metric 1 threshold 1
+ rate_limit 0
+#
+# This is our tunnel to the outside world.
+# Careful with those boundaries, Eugene.
+tunnel 192.168.5.4 10.11.12.13 metric 1 threshold 32
+ boundary LOCAL boundary EE
+.Ed
+.Sh SIGNALS
+.Nm Mrouted
+responds to the following signals:
+.Bl -tag -width indent
+.It HUP
+Restarts
+.Nm .
+The configuration file is reread every time this signal is evoked.
+.It INT
+Terminate execution gracefully (i.e., by sending
+good-bye messages to all neighboring routers).
+.It TERM
+Same as INT.
+.It USR1
+Dump the internal routing tables to
+.Pa /var/tmp/mrouted.dump .
+.It USR2
+Dump the internal cache tables to
+.Pa /var/tmp/mrouted.cache .
+.It QUIT
+Dump the internal routing tables to stderr (only if
+.Nm
+was invoked with a non-zero debug level).
+.El
+.Pp
+For convenience in sending signals,
+.Nm
+writes its pid to
+.Pa /var/run/mrouted.pid
+upon startup.
+.Sh EXAMPLES
+The routing tables look like this:
+.Pp
+.Bd -literal
+Virtual Interface Table
+ Vif Local-Address Metric Thresh Flags
+ 0 36.2.0.8 subnet: 36.2/16 1 1 querier
+ groups: 224.0.2.1
+ 224.0.0.4
+ pkts in: 3456
+ pkts out: 2322323
+
+ 1 36.11.0.1 subnet: 36.11/16 1 1 querier
+ groups: 224.0.2.1
+ 224.0.1.0
+ 224.0.0.4
+ pkts in: 345
+ pkts out: 3456
+
+ 2 36.2.0.8 tunnel: 36.8.0.77 3 1
+ peers: 36.8.0.77 (3.255)
+ boundaries: 239.0.1/24
+ : 239.1.2/24
+ pkts in: 34545433
+ pkts out: 234342
+
+ 3 36.2.0.8 tunnel: 36.6.8.23 3 16
+
+Multicast Routing Table (1136 entries)
+ Origin-Subnet From-Gateway Metric Tmr In-Vif Out-Vifs
+ 36.2 1 45 0 1* 2 3*
+ 36.8 36.8.0.77 4 15 2 0* 1* 3*
+ 36.11 1 20 1 0* 2 3*
+ .
+ .
+ .
+.Ed
+.Pp
+In this example, there are four vifs connecting to two subnets and two
+tunnels. The vif 3 tunnel is not in use (no peer address). The vif 0 and
+vif 1 subnets have some groups present; tunnels never have any groups. This
+instance of
+.Nm
+is the one responsible for sending periodic group
+membership queries on the vif 0 and vif 1 subnets, as indicated by the
+"querier" flags.
+The list of boundaries indicate the scoped addresses on that
+interface.
+A count of the no. of incoming and outgoing packets is also
+shown at each interface.
+.Pp
+Associated with each subnet from which a multicast datagram can originate
+is the address of the previous hop router (unless the subnet is directly-
+connected), the metric of the path back to the origin, the amount of time
+since we last received an update for this subnet, the incoming vif for
+multicasts from that origin, and a list of outgoing vifs. "*" means that
+the outgoing vif is connected to a leaf of the broadcast tree rooted at the
+origin, and a multicast datagram from that origin will be forwarded on that
+outgoing vif only if there are members of the destination group on that leaf.
+.Pp
+.Nm Mrouted
+also maintains a copy of the kernel forwarding cache table.
+Entries
+are created and deleted by
+.Nm .
+.Pp
+The cache tables look like this:
+.Pp
+.Bd -literal
+Multicast Routing Cache Table (147 entries)
+ Origin Mcast-group CTmr Age Ptmr IVif Forwvifs
+ 13.2.116/22 224.2.127.255 3m 2m - 0 1
+>13.2.116.19
+>13.2.116.196
+ 138.96.48/21 224.2.127.255 5m 2m - 0 1
+>138.96.48.108
+ 128.9.160/20 224.2.127.255 3m 2m - 0 1
+>128.9.160.45
+ 198.106.194/24 224.2.135.190 9m 28s 9m 0P
+>198.106.194.22
+.Ed
+.Pp
+Each entry is characterized by the origin subnet number and mask and the
+destination multicast group.
+.Pp
+The 'CTmr' field indicates the lifetime
+of the entry. The entry is deleted from the cache table
+(or refreshed, if traffic is flowing)
+when the timer decrements to zero. The 'Age' field is the time since
+this cache entry was originally created. Since cache entries get refreshed
+if traffic is flowing, routing entries can grow very old.
+.Pp
+The 'Ptmr' field is simply a dash if no prune was sent upstream, or the
+amount of time until the upstream prune will time out.
+.Pp
+The 'Ivif' field indicates the
+incoming vif for multicast packets from that origin. Each router also
+maintains a record of the number of prunes received from neighboring
+routers for a particular source and group.
+If there are no members of
+a multicast group on any downward link of the multicast tree for a
+subnet, a prune message is sent to the upstream router.
+They are
+indicated by a "P" after the vif number.
+.Pp
+The Forwvifs field shows the
+interfaces along which datagrams belonging to the source-group are
+forwarded.
+A "p" indicates that no datagrams are being forwarded along
+that interface.
+An unlisted interface is a leaf subnet with no
+members of the particular group on that subnet.
+A "b" on an interface
+indicates that it is a boundary interface, i.e. traffic will not be
+forwarded on the scoped address on that interface.
+.Pp
+An additional line with a ">" as the first character is printed for
+each source on the subnet. Note that there can be many sources in
+one subnet.
+An additional line with a "<" as the first character is printed
+describing any prunes received from downstream dependent neighbors
+for this subnet and group.
+.Sh FILES
+.Bl -tag -width /var/tmp/mrouted.cache -compact
+.It Pa /etc/mrouted.conf
+.It Pa /var/run/mrouted.pid
+.It Pa /var/tmp/mrouted.dump
+.It Pa /var/tmp/mrouted.cache
+.El
+.Sh SEE ALSO
+.Xr map-mbone 8 ,
+.Xr mrinfo 8 ,
+.Xr mtrace 8
+.Pp
+DVMRP is described, along with other multicast routing algorithms, in the
+paper "Multicast Routing in Internetworks and Extended LANs" by S. Deering,
+in the Proceedings of the ACM SIGCOMM '88 Conference.
+.Sh AUTHORS
+.An Steve Deering ,
+.An Ajit Thyagarajan ,
+.An Bill Fenner .
diff --git a/usr.sbin/mrouted/mrouted.conf b/usr.sbin/mrouted/mrouted.conf
new file mode 100644
index 0000000..e9cb04c
--- /dev/null
+++ b/usr.sbin/mrouted/mrouted.conf
@@ -0,0 +1,44 @@
+# $FreeBSD$
+# mrouted.conf,v 3.8 1995/11/29 22:40:47 fenner Rel
+#
+# This is the configuration file for "mrouted", an IP multicast router.
+# mrouted looks for it in "/etc/mrouted.conf".
+#
+# Command formats:
+#
+# name <boundname> <scoped-addr>/<mask-len>
+# cache_lifetime 3600 # seconds
+# pruning on
+#
+# phyint <local-addr> [disable] [metric <m>] [threshold <t>] [rate_limit <b>]
+# [boundary (<boundname>|<scoped-addr>/<mask-len>)]
+# [altnet (<subnet>/<mask-len>|<subnet>)]
+# tunnel <local-addr> <remote-addr> [srcrt] [metric <m>]
+# [threshold <t>] [rate_limit <b>]
+# [boundary (<boundname>|<scoped-addr>/<mask-len>)]
+#
+# NOTE: any phyint commands MUST precede any tunnel commands
+# NOTE: the mask-len is the no. of leading 1's in the mask
+# NOTE: rate_limit is in kilobits, and defaults to 500 for tunnels
+#
+# Example of named bounary:
+#name LOCAL 239.255.0.0/16
+#name EE 239.254.0.0/16 # i.e. the EE dept wants local groups
+#
+# Example of use of named boundary
+#phyint le1 boundary EE # le1 is our interface to comp sci,
+# # keep them away from our local groups
+#
+#
+# Template tunnel for mcast_install
+tunnel 128.4.0.77 128.4.0.8 metric 1 threshold 64 rate_limit 500 # <-- REPLACE
+# boundary LOCAL
+#
+# You might want to specify a boundary on your tunnel to the outside world,
+# as above.
+#
+# NOTE: ONLY uncomment the following if you are running mrouted.snmp!
+#sysName "mymrouter"
+#sysContact "Me <me@me.com> +x.yyy.zzz-zzzz"
+#sysVersion "MyOS 4.1.3 and mrouted"
+#sysLocation "The MBONE"
diff --git a/usr.sbin/mrouted/mrouted/Makefile b/usr.sbin/mrouted/mrouted/Makefile
new file mode 100644
index 0000000..afd3af8
--- /dev/null
+++ b/usr.sbin/mrouted/mrouted/Makefile
@@ -0,0 +1,19 @@
+# $FreeBSD$
+
+S= ${.CURDIR}/..
+.PATH: $S
+
+PROG= mrouted
+MAN= mrouted.8
+SRCS= config.c cfparse.y main.c route.c vif.c prune.c callout.c rsrr.c \
+ ipip.c icmp.c vers.c igmp.c inet.c kern.c
+CLEANFILES+= vers.c
+
+CFLAGS+= -I$S
+YFLAGS=
+
+vers.c: $S/VERSION
+ rm -f vers.c ; \
+ sed 's/.*/char todaysversion[] = "&";/' $S/VERSION > vers.c
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mrouted/mtrace.8 b/usr.sbin/mrouted/mtrace.8
new file mode 100644
index 0000000..f4d1a44
--- /dev/null
+++ b/usr.sbin/mrouted/mtrace.8
@@ -0,0 +1,542 @@
+.\" Copyright (c) 1995 by the University of Southern California
+.\" All rights reserved.
+.\"
+.\" Permission to use, copy, modify, and distribute this software and its
+.\" documentation in source and binary forms for non-commercial purposes
+.\" and without fee is hereby granted, provided that the above copyright
+.\" notice appear in all copies and that both the copyright notice and
+.\" this permission notice appear in supporting documentation, and that
+.\" any documentation, advertising materials, and other materials related
+.\" to such distribution and use acknowledge that the software was
+.\" developed by the University of Southern California, Information
+.\" Sciences Institute. The name of the University may not be used to
+.\" endorse or promote products derived from this software without
+.\" specific prior written permission.
+.\"
+.\" THE UNIVERSITY OF SOUTHERN CALIFORNIA makes no representations about
+.\" the suitability of this software for any purpose. THIS SOFTWARE IS
+.\" PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+.\" INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+.\"
+.\" Other copyrights might apply to parts of this software and are so
+.\" noted when applicable.
+.\"
+.\" This manual page (but not the software) was derived from the
+.\" manual page for the traceroute program which bears the following
+.\" copyright notice:
+.\"
+.\" Copyright (c) 1988 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd May 8, 1995
+.Dt MTRACE 8
+.Os
+.Sh NAME
+.Nm mtrace
+.Nd print multicast path from a source to a receiver
+.Sh SYNOPSIS
+.Nm
+.Op Fl e Ar extrahops
+.Op Fl g Ar gateway
+.Op Fl i Ar if_addr
+.Op Fl l
+.Op Fl M
+.Op Fl m Ar max_hops
+.Op Fl n
+.Op Fl O
+.Op Fl p
+.Op Fl P
+.Op Fl q Ar nqueries
+.Op Fl r Ar resp_dest
+.Op Fl s
+.Op Fl S Ar stat_int
+.Op Fl t Ar ttl
+.Op Fl T
+.Op Fl U
+.Op Fl v
+.Op Fl w Ar waittime
+.Ar source
+.Op Ar receiver
+.Op Ar group
+.Sh DESCRIPTION
+Assessing problems in the distribution of IP multicast traffic
+can be difficult.
+.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 ,
+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 .
+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
+.Bd -literal
+oak.isi.edu 80# mtrace -l caraway.lcs.mit.edu 224.2.0.3
+Mtrace from 18.26.0.170 to 128.9.160.100 via group 224.2.0.3
+Querying full reverse path...
+ 0 oak.isi.edu (128.9.160.100)
+ -1 cub.isi.edu (128.9.160.153) DVMRP thresh^ 1 3 ms
+ -2 la.dart.net (140.173.128.1) DVMRP thresh^ 1 14 ms
+ -3 dc.dart.net (140.173.64.1) DVMRP thresh^ 1 50 ms
+ -4 bbn.dart.net (140.173.32.1) DVMRP thresh^ 1 63 ms
+ -5 mit.dart.net (140.173.48.2) DVMRP thresh^ 1 71 ms
+ -6 caraway.lcs.mit.edu (18.26.0.170)
+Round trip time 124 ms; total ttl of 6 required.
+.Ed
+.Pp
+If a hop reports that it is using the default route to forward packets,
+the word
+.Em [default]
+is printed after that hop. If the
+.Fl v
+flag is supplied, the route being used to forward packets is printed
+in the form
+.Em [18.26.0/24] .
+.Pp
+The second section provides a pictorial view of the path in the
+forward direction with data flow indicated by arrows pointing downward
+and the query path indicated by arrows pointing upward. For each hop,
+both the entry and exit addresses of the router are shown if
+different, along with the initial ttl required on the packet in order
+to be forwarded at this hop and the propagation delay across the hop
+assuming that the routers at both ends have synchronized clocks.
+The right half of this section is composed of two sets of statistics.
+The first column contains the average packet rate for all traffic at
+each hop.
+The remaining columns are the
+number of packets lost, the number of packets sent, the percentage
+lost, and the average packet rate at each hop. These statistics are
+calculated from differences between traces and from hop to hop as
+explained above. The first group shows the statistics for all traffic
+flowing out the interface at one hop and in the interface at the next
+hop. The second group shows the statistics only for traffic forwarded
+from the specified
+.Ar source
+to the specified
+.Ar group .
+The first group of statistics may be expanded to include loss rates
+using the
+.Fl T
+option. However, these numbers can be extremely misleading and require
+detailed knowledge of the routers involved to be interpreted properly.
+.Pp
+These statistics are shown on one or two lines for each hop. Without
+any options, this second section of the output is printed only once,
+approximately 10 seconds after the initial trace. One line is shown
+for each hop showing the statistics over that 10-second period. If
+the
+.Fl l
+option is given, the second section is repeated every 10 seconds and
+two lines are shown for each hop. The first line shows the statistics
+for the last 10 seconds, and the second line shows the cumulative
+statistics over the period since the initial trace, which is 101
+seconds in the example below. The second section of the output is
+omitted if the
+.Fl s
+option is set or if no multicast group is specified.
+.Pp
+.Bd -literal
+Waiting to accumulate statistics... Results after 101 seconds:
+
+ Source Response Dest Overall Packet Statistics For Traffic From
+18.26.0.170 128.9.160.100 Packet 18.26.0.170 To 224.2.0.3
+ | __/ rtt 125 ms Rate Lost/Sent = Pct Rate
+ v / hop 65 ms ------- ---------------------
+18.26.0.144
+140.173.48.2 mit.dart.net
+ | ^ ttl 1 0 pps 0/2 = --% 0 pps
+ v | hop 8 ms 0 pps 0/18 = 0% 0 pps
+140.173.48.1
+140.173.32.1 bbn.dart.net
+ | ^ ttl 2 0 pps 0/2 = --% 0 pps
+ v | hop 12 ms 0 pps 0/18 = 0% 0 pps
+140.173.32.2
+140.173.64.1 dc.dart.net
+ | ^ ttl 3 27 pps 0/2 = --% 0 pps
+ v | hop 34 ms 26 pps 0/18 = 0% 0 pps
+140.173.64.2
+140.173.128.1 la.dart.net
+ | ^ ttl 4 83 pps 0/2 = --% 0 pps
+ v | hop 11 ms 79 pps 0/18 = 0% 0 pps
+140.173.128.2
+128.9.160.153 cub.isi.edu
+ | \\__ ttl 5 83 pps ?/2 0 pps
+ v \\ hop -8 ms 79 pps ?/18 0 pps
+128.9.160.100 128.9.160.100
+ Receiver Query Source
+.Ed
+.Pp
+Because the packet counts may be changing as the trace query is
+propagating, there may be small errors (off by 1 or 2) in these
+statistics. However, those errors should not accumulate, so the
+cumulative statistics line should increase in accuracy as a new trace
+is run every 10 seconds. There are two sources of larger errors, both
+of which show up as negative losses:
+.Pp
+If the input to a node is from a multi-access network with more than
+one other node attached, then the input count will be (close to) the
+sum of the output counts from all the attached nodes, but the output
+count from the previous hop on the traced path will be only part of
+that. Hence the output count minus the input count will be negative.
+.Pp
+In release 3.3 of the DVMRP multicast forwarding software for SunOS
+and other systems, a multicast packet generated on a router will be
+counted as having come in an interface even though it did not. This
+creates the negative loss that can be seen in the example above.
+.Pp
+Note that these negative losses may mask positive losses.
+.Pp
+In the example, there is also one negative hop time. This simply
+indicates a lack of synchronization between the system clocks across
+that hop. This example also illustrates how the percentage loss is
+shown as two dashes when the number of packets sent is less than 10
+because the percentage would not be statistically valid.
+.Pp
+A second example shows a trace to a receiver that is not local; the
+query is sent to the last-hop router with the
+.Fl g
+option. In this example, the trace of the full reverse path resulted
+in no response because there was a node running an old version of
+.Nm mrouted
+that did not implement the multicast traceroute function, so
+.Nm
+switched to hop-by-hop mode. The
+.Dq Output pruned
+error code
+indicates that traffic for group 224.2.143.24 would not be forwarded.
+.Pp
+.Bd -literal
+oak.isi.edu 108# mtrace -g 140.173.48.2 204.62.246.73 \\
+ butter.lcs.mit.edu 224.2.143.24
+Mtrace from 204.62.246.73 to 18.26.0.151 via group 224.2.143.24
+Querying full reverse path... * switching to hop-by-hop:
+ 0 butter.lcs.mit.edu (18.26.0.151)
+ -1 jam.lcs.mit.edu (18.26.0.144) DVMRP thresh^ 1 33 ms Output pruned
+ -2 bbn.dart.net (140.173.48.1) DVMRP thresh^ 1 36 ms
+ -3 dc.dart.net (140.173.32.2) DVMRP thresh^ 1 44 ms
+ -4 darpa.dart.net (140.173.240.2) DVMRP thresh^ 16 47 ms
+ -5 * * * noc.hpc.org (192.187.8.2) [mrouted 2.2] didn't respond
+Round trip time 95 ms
+.Ed
+.Sh AUTHORS
+.An -nosplit
+Implemented by
+.An Steve Casner
+based on an initial prototype written by
+.An Ajit Thyagarajan .
+The multicast traceroute mechanism was designed by
+.An Van Jacobson
+with help from
+.An Steve Casner ,
+.An Steve Deering ,
+.An Dino Farinacci ,
+and
+.An Deb Agrawal ;
+it was implemented in
+.Nm mrouted
+by
+.An Ajit Thyagarajan
+and
+.An Bill Fenner .
+The option syntax and the output format of
+.Nm
+are modeled after the unicast
+.Nm traceroute
+program written by
+.An Van Jacobson .
+.Sh SEE ALSO
+.Xr map-mbone 8 ,
+.Xr mrinfo 8 ,
+.Xr mrouted 8 ,
+.Xr traceroute 8
+.Sh BUGS
+Statistics collection in passive mode doesn't always produce the same output
+as when actively collecting data.
diff --git a/usr.sbin/mrouted/mtrace.c b/usr.sbin/mrouted/mtrace.c
new file mode 100644
index 0000000..86655af
--- /dev/null
+++ b/usr.sbin/mrouted/mtrace.c
@@ -0,0 +1,3175 @@
+/*
+ * mtrace.c
+ *
+ * This tool traces the branch of a multicast tree from a source to a
+ * receiver for a particular multicast group and gives statistics
+ * about packet rate and loss for each hop along the path. It can
+ * usually be invoked just as
+ *
+ * mtrace source
+ *
+ * to trace the route from that source to the local host for a default
+ * group when only the route is desired and not group-specific packet
+ * counts. See the usage line for more complex forms.
+ *
+ *
+ * Released 4 Apr 1995. This program was adapted by Steve Casner
+ * (USC/ISI) from a prototype written by Ajit Thyagarajan (UDel and
+ * Xerox PARC). It attempts to parallel in command syntax and output
+ * format the unicast traceroute program written by Van Jacobson (LBL)
+ * for the parts where that makes sense.
+ *
+ * Copyright (c) 1995 by the University of Southern California
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation in source and binary forms for any purposes and without
+ * fee is hereby granted, provided that the above copyright notice
+ * appear in all copies and that both the copyright notice and this
+ * permission notice appear in supporting documentation, and that any
+ * documentation, advertising materials, and other materials related to
+ * such distribution and use acknowledge that the software was developed
+ * by the University of Southern California, Information Sciences
+ * Institute. The name of the University may not be used to endorse or
+ * promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA makes no representations about
+ * the suitability of this software for any purpose. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ *
+ * Parts of this software are derived from mrouted, which has the
+ * following license:
+ *
+ * The mrouted program is covered by the following license. Use of the
+ * mrouted program represents acceptance of these terms and conditions.
+ *
+ * 1. STANFORD grants to LICENSEE a nonexclusive and nontransferable
+ * license to use, copy and modify the computer software ``mrouted''
+ * (hereinafter called the ``Program''), upon the terms and conditions
+ * hereinafter set out and until Licensee discontinues use of the Licensed
+ * Program.
+ *
+ * 2. LICENSEE acknowledges that the Program is a research tool still in
+ * the development state, that it is being supplied ``as is,'' without any
+ * accompanying services from STANFORD, and that this license is entered
+ * into in order to encourage scientific collaboration aimed at further
+ * development and application of the Program.
+ *
+ * 3. LICENSEE may copy the Program and may sublicense others to use
+ * object code copies of the Program or any derivative version of the
+ * Program. All copies must contain all copyright and other proprietary
+ * notices found in the Program as provided by STANFORD. Title to
+ * copyright to the Program remains with STANFORD.
+ *
+ * 4. LICENSEE may create derivative versions of the Program. LICENSEE
+ * hereby grants STANFORD a royalty-free license to use, copy, modify,
+ * distribute and sublicense any such derivative works. At the time
+ * LICENSEE provides a copy of a derivative version of the Program to a
+ * third party, LICENSEE shall provide STANFORD with one copy of the
+ * source code of the derivative version at no charge to STANFORD.
+ *
+ * 5. STANFORD MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
+ * IMPLIED. By way of example, but not limitation, STANFORD MAKES NO
+ * REPRESENTATION OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY
+ * PARTICULAR PURPOSE OR THAT THE USE OF THE LICENSED PROGRAM WILL NOT
+ * INFRINGE ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. STANFORD
+ * shall not be held liable for any liability nor for any direct, indirect
+ * or consequential damages with respect to any claim by LICENSEE or any
+ * third party on account of or arising from this Agreement or use of the
+ * Program.
+ *
+ * 6. This agreement shall be construed, interpreted and applied in
+ * accordance with the State of California and any legal action arising
+ * out of this Agreement or use of the Program shall be filed in a court
+ * in the State of California.
+ *
+ * 7. Nothing in this Agreement shall be construed as conferring rights to
+ * use in advertising, publicity or otherwise any trademark or the name
+ * of ``Stanford''.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * The mtrace program has been modified and improved by Xerox
+ * Corporation. Xerox grants to LICENSEE a non-exclusive and
+ * non-transferable license to use, copy, and modify the Xerox modified
+ * and improved mrouted software on the same terms and conditions which
+ * govern the license Stanford and ISI grant with respect to the mtrace
+ * program. These terms and conditions are incorporated in this grant
+ * by reference and shall be deemed to have been accepted by LICENSEE
+ * to cover its relationship with Xerox Corporation with respect to any
+ * use of the Xerox improved program.
+ *
+ * The mtrace program is COPYRIGHT 1998 by Xerox Corporation.
+ *
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <memory.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/igmp.h>
+#include <sys/ioctl.h>
+#ifdef SYSV
+#include <sys/sockio.h>
+#endif
+#include <arpa/inet.h>
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+#ifdef SUNOS5
+#include <sys/systeminfo.h>
+#endif
+
+typedef unsigned int u_int32; /* XXX */
+#include "mtrace.h"
+
+#define DEFAULT_TIMEOUT 3 /* How long to wait before retrying requests */
+#define DEFAULT_RETRIES 3 /* How many times to try */
+#define DEFAULT_EXTRAHOPS 3 /* How many hops past a non-responding rtr */
+#define MAXHOPS 60 /* Don't need more hops than this */
+#define UNICAST_TTL 255 /* TTL for unicast response */
+#define MULTICAST_TTL1 127 /* Default TTL for multicast query/response */
+#define MULTICAST_TTL_INC 32 /* TTL increment for increase after timeout */
+#define MULTICAST_TTL_MAX 192 /* Maximum TTL allowed (protect low-BW links */
+
+#define TRUE 1
+#define FALSE 0
+#define DVMRP_ASK_NEIGHBORS2 5 /* DVMRP msg requesting neighbors */
+#define DVMRP_NEIGHBORS2 6 /* reply to above */
+#define DVMRP_NF_DOWN 0x10 /* kernel state of interface */
+#define DVMRP_NF_DISABLED 0x20 /* administratively disabled */
+#define MAX_IP_PACKET_LEN 576
+#define MIN_IP_HEADER_LEN 20
+#define MAX_IP_HEADER_LEN 60
+#define MAX_DVMRP_DATA_LEN \
+ ( MAX_IP_PACKET_LEN - MAX_IP_HEADER_LEN - IGMP_MINLEN )
+
+struct resp_buf {
+ u_long qtime; /* Time query was issued */
+ u_long rtime; /* Time response was received */
+ int len; /* Number of reports or length of data */
+ struct igmp igmp; /* IGMP header */
+ union {
+ struct {
+ struct tr_query q; /* Query/response header */
+ struct tr_resp r[MAXHOPS]; /* Per-hop reports */
+ } t;
+ char d[MAX_DVMRP_DATA_LEN]; /* Neighbor data */
+ } u;
+} base, incr[2];
+
+#define qhdr u.t.q
+#define resps u.t.r
+#define ndata u.d
+
+char *names[MAXHOPS];
+
+/*
+ * In mrouted 3.3 and 3.4 (and in some Cisco IOS releases),
+ * cache entries can get deleted even if there is traffic
+ * flowing, which will reset the per-source/group counters.
+ */
+#define BUG_RESET 0x01
+
+/*
+ * Also in mrouted 3.3 and 3.4, there's a bug in neighbor
+ * version processing which can cause them to believe that
+ * the neighbor is constantly resetting. This causes them
+ * to constantly delete all their state.
+ */
+#define BUG_RESET2X 0x02
+
+/*
+ * Pre-3.7 mrouted's forget to byte-swap their reports.
+ */
+#define BUG_SWAP 0x04
+
+/*
+ * Pre-3.9 mrouted's forgot a parenthesis in the htonl()
+ * on the time calculation so supply bogus times.
+ */
+#define BUG_BOGUSTIME 0x08
+
+#define BUG_NOPRINT (BUG_RESET | BUG_RESET2X)
+
+int bugs[MAXHOPS]; /* List of bugs noticed at each hop */
+
+struct mtrace {
+ struct mtrace *next;
+ struct resp_buf base, incr[2];
+ struct resp_buf *new, *prev;
+ int nresp;
+ struct timeval last;
+ int bugs[MAXHOPS];
+ char *names[MAXHOPS];
+ int lastqid;
+};
+
+int timeout = DEFAULT_TIMEOUT;
+int nqueries = DEFAULT_RETRIES;
+int numeric = FALSE;
+int debug = 0;
+int passive = FALSE;
+int multicast = FALSE;
+int unicast = FALSE;
+int statint = 10;
+int verbose = FALSE;
+int tunstats = FALSE;
+int weak = FALSE;
+int extrahops = DEFAULT_EXTRAHOPS;
+int printstats = TRUE;
+int sendopts = TRUE;
+int lossthresh = 0;
+int fflag = FALSE;
+int staticqid = 0;
+
+u_int32 defgrp; /* Default group if not specified */
+u_int32 query_cast; /* All routers multicast addr */
+u_int32 resp_cast; /* Mtrace response multicast addr */
+
+u_int32 lcl_addr = 0; /* This host address, in NET order */
+u_int32 dst_netmask = 0; /* netmask to go with qdst */
+
+/*
+ * Query/response parameters, all initialized to zero and set later
+ * to default values or from options.
+ */
+u_int32 qsrc = 0; /* Source address in the query */
+u_int32 qgrp = 0; /* Group address in the query */
+u_int32 qdst = 0; /* Destination (receiver) address in query */
+u_char qno = 0; /* Max number of hops to query */
+u_int32 raddr = 0; /* Address where response should be sent */
+int qttl = 0; /* TTL for the query packet */
+u_char rttl = 0; /* TTL for the response packet */
+u_int32 gwy = 0; /* User-supplied last-hop router address */
+u_int32 tdst = 0; /* Address where trace is sent (last-hop) */
+
+char s1[19]; /* buffers to hold the string representations */
+char s2[19]; /* of IP addresses, to be passed to inet_fmt() */
+char s3[19]; /* or inet_fmts(). */
+
+#if !(defined(BSD) && (BSD >= 199103))
+extern int errno;
+extern int sys_nerr;
+extern char * sys_errlist[];
+#endif
+
+#define RECV_BUF_SIZE 8192
+char *send_buf, *recv_buf;
+int igmp_socket;
+u_int32 allrtrs_group;
+char router_alert[4]; /* Router Alert IP Option */
+#ifndef IPOPT_RA
+#define IPOPT_RA 148
+#endif
+#ifdef SUNOS5
+char eol[4]; /* EOL IP Option */
+int ip_addlen = 0; /* Workaround for Option bug #2 */
+#endif
+
+/*
+ * max macro, with weird case to avoid conflicts
+ */
+#define MaX(a,b) ((a) > (b) ? (a) : (b))
+
+#ifndef __P
+#ifdef __STDC__
+#define __P(x) x
+#else
+#define __P(x) ()
+#endif
+#endif
+
+typedef int (*callback_t) __P((int, u_char *, int, struct igmp *, int,
+ struct sockaddr *, int *, struct timeval *));
+
+void init_igmp __P((void));
+void send_igmp __P((u_int32 src, u_int32 dst, int type,
+ int code, u_int32 group,
+ int datalen));
+int inet_cksum __P((u_short *addr, u_int len));
+void k_set_rcvbuf __P((int bufsize));
+void k_hdr_include __P((int bool));
+void k_set_ttl __P((int t));
+void k_set_loop __P((int l));
+void k_set_if __P((u_int32 ifa));
+void k_join __P((u_int32 grp, u_int32 ifa));
+void k_leave __P((u_int32 grp, u_int32 ifa));
+char * inet_fmt __P((u_int32 addr, char *s));
+char * inet_fmts __P((u_int32 addr, u_int32 mask, char *s));
+char * inet_name __P((u_int32 addr));
+u_int32 host_addr __P((char *name));
+/* u_int is promoted u_char */
+char * proto_type __P((u_int type));
+char * flag_type __P((u_int type));
+
+u_int32 get_netmask __P((int s, u_int32 *dst));
+int get_ttl __P((struct resp_buf *buf));
+int t_diff __P((u_long a, u_long b));
+u_long byteswap __P((u_long v));
+int mtrace_callback __P((int, u_char *, int, struct igmp *,
+ int, struct sockaddr *, int *,
+ struct timeval *));
+int send_recv __P((u_int32 dst, int type, int code,
+ int tries, struct resp_buf *save,
+ callback_t callback));
+void passive_mode __P((void));
+char * print_host __P((u_int32 addr));
+char * print_host2 __P((u_int32 addr1, u_int32 addr2));
+void print_trace __P((int idx, struct resp_buf *buf,
+ char **names));
+int what_kind __P((struct resp_buf *buf, char *why));
+char * scale __P((int *hop));
+void stat_line __P((struct tr_resp *r, struct tr_resp *s,
+ int have_next, int *res));
+void fixup_stats __P((struct resp_buf *base,
+ struct resp_buf *prev,
+ struct resp_buf *new,
+ int *bugs));
+int check_thresh __P((int thresh,
+ struct resp_buf *base,
+ struct resp_buf *prev,
+ struct resp_buf *new));
+int print_stats __P((struct resp_buf *base,
+ struct resp_buf *prev,
+ struct resp_buf *new,
+ int *bugs,
+ char **names));
+int path_changed __P((struct resp_buf *base,
+ struct resp_buf *new));
+void check_vif_state __P((void));
+
+int main __P((int argc, char *argv[]));
+void log __P((int, int, char *, ...));
+static void usage __P((void));
+
+
+/*
+ * Open and initialize the igmp socket, and fill in the non-changing
+ * IP header fields in the output packet buffer.
+ */
+void
+init_igmp()
+{
+ struct ip *ip;
+
+ recv_buf = (char *)malloc(RECV_BUF_SIZE);
+ if (recv_buf == 0)
+ log(LOG_ERR, 0, "Out of memory allocating recv_buf!");
+ send_buf = (char *)malloc(RECV_BUF_SIZE);
+ if (send_buf == 0)
+ log(LOG_ERR, 0, "Out of memory allocating send_buf!");
+
+ if ((igmp_socket = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP)) < 0)
+ log(LOG_ERR, errno, "IGMP socket");
+
+ k_hdr_include(TRUE); /* include IP header when sending */
+ k_set_rcvbuf(48*1024); /* lots of input buffering */
+ k_set_ttl(1); /* restrict multicasts to one hop */
+ k_set_loop(FALSE); /* disable multicast loopback */
+
+ ip = (struct ip *)send_buf;
+ ip->ip_hl = sizeof(struct ip) >> 2;
+ ip->ip_v = IPVERSION;
+ ip->ip_tos = 0;
+ ip->ip_off = 0;
+ ip->ip_p = IPPROTO_IGMP;
+ ip->ip_ttl = MAXTTL; /* applies to unicasts only */
+
+#ifndef INADDR_ALLRTRS_GROUP
+#define INADDR_ALLRTRS_GROUP 0xe0000002 /* 224.0.0.2 */
+#endif
+ allrtrs_group = htonl(INADDR_ALLRTRS_GROUP);
+
+ router_alert[0] = IPOPT_RA; /* Router Alert */
+ router_alert[1] = 4; /* 4 bytes */
+ router_alert[2] = 0;
+ router_alert[3] = 0;
+}
+
+#ifdef SUNOS5
+void
+checkforsolarisbug()
+{
+ u_int32 localhost = htonl(0x7f000001);
+
+ eol[0] = IPOPT_EOL;
+ eol[1] = IPOPT_EOL;
+ eol[2] = IPOPT_EOL;
+ eol[3] = IPOPT_EOL;
+
+ setsockopt(igmp_socket, IPPROTO_IP, IP_OPTIONS, eol, sizeof(eol));
+ /*
+ * Check if the kernel adds the options length to the packet
+ * length. Send myself an IGMP packet of type 0 (illegal),
+ * with 4 IPOPT_EOL options, my PID (for collision detection)
+ * and 4 bytes of zero (so that the checksum works whether
+ * the 4 bytes of zero get truncated or not).
+ */
+ bzero(send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN, 8);
+ *(int *)(send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN) = getpid();
+ send_igmp(localhost, localhost, 0, 0, 0, 8);
+ while (1) {
+ int recvlen, dummy = 0;
+
+ recvlen = recvfrom(igmp_socket, recv_buf, RECV_BUF_SIZE,
+ 0, NULL, &dummy);
+ /* 8 == 4 bytes of options and 4 bytes of PID */
+ if (recvlen >= MIN_IP_HEADER_LEN + IGMP_MINLEN + 8) {
+ struct ip *ip = (struct ip *)recv_buf;
+ struct igmp *igmp;
+ int *p;
+
+ if (ip->ip_hl != 6 ||
+ ip->ip_p != IPPROTO_IGMP ||
+ ip->ip_src.s_addr != localhost ||
+ ip->ip_dst.s_addr != localhost)
+ continue;
+
+ igmp = (struct igmp *)(recv_buf + (ip->ip_hl << 2));
+ if (igmp->igmp_group.s_addr != 0)
+ continue;
+ if (igmp->igmp_type != 0 || igmp->igmp_code != 0)
+ continue;
+
+ p = (int *)((char *)igmp + IGMP_MINLEN);
+ if (*p != getpid())
+ continue;
+
+#ifdef RAW_INPUT_IS_RAW
+ ip->ip_len = ntohs(ip->ip_len);
+#endif
+ if (ip->ip_len == IGMP_MINLEN + 4)
+ ip_addlen = 4;
+ else if (ip->ip_len == IGMP_MINLEN + 8)
+ ip_addlen = 0;
+ else
+ log(LOG_ERR, 0, "while checking for Solaris bug: Sent %d bytes and got back %d!", IGMP_MINLEN + 8, ip->ip_len);
+
+ break;
+ }
+ }
+}
+#endif
+
+/*
+ * Construct an IGMP message in the output packet buffer. The caller may
+ * have already placed data in that buffer, of length 'datalen'. Then send
+ * the message from the interface with IP address 'src' to destination 'dst'.
+ */
+void
+send_igmp(src, dst, type, code, group, datalen)
+ u_int32 src, dst;
+ int type, code;
+ u_int32 group;
+ int datalen;
+{
+ struct sockaddr_in sdst;
+ struct ip *ip;
+ struct igmp *igmp;
+ int setloop = 0;
+ static int raset = 0;
+ int sendra = 0;
+ int sendlen;
+
+ ip = (struct ip *)send_buf;
+ ip->ip_src.s_addr = src;
+ ip->ip_dst.s_addr = dst;
+ ip->ip_len = MIN_IP_HEADER_LEN + IGMP_MINLEN + datalen;
+ sendlen = ip->ip_len;
+#ifdef SUNOS5
+ ip->ip_len += ip_addlen;
+#endif
+#ifdef RAW_OUTPUT_IS_RAW
+ ip->ip_len = htons(ip->ip_len);
+#endif
+
+ igmp = (struct igmp *)(send_buf + MIN_IP_HEADER_LEN);
+ igmp->igmp_type = type;
+ igmp->igmp_code = code;
+ igmp->igmp_group.s_addr = group;
+ igmp->igmp_cksum = 0;
+ igmp->igmp_cksum = inet_cksum((u_short *)igmp,
+ IGMP_MINLEN + datalen);
+
+ if (IN_MULTICAST(ntohl(dst))) {
+ k_set_if(src);
+ setloop = 1;
+ k_set_loop(TRUE);
+ if (dst != allrtrs_group)
+ sendra = 1;
+ }
+
+ if (sendopts && sendra && !raset) {
+ setsockopt(igmp_socket, IPPROTO_IP, IP_OPTIONS,
+ router_alert, sizeof(router_alert));
+ raset = 1;
+ } else if (!sendra && raset) {
+#ifdef SUNOS5
+ /*
+ * SunOS5 < 5.6 cannot properly reset the IP_OPTIONS "socket"
+ * option. Instead, set up a string of 4 EOL's.
+ */
+ setsockopt(igmp_socket, IPPROTO_IP, IP_OPTIONS,
+ eol, sizeof(eol));
+#else
+ setsockopt(igmp_socket, IPPROTO_IP, IP_OPTIONS,
+ NULL, 0);
+#endif
+ raset = 0;
+ }
+
+ bzero(&sdst, sizeof(sdst));
+ sdst.sin_family = AF_INET;
+#if (defined(BSD) && (BSD >= 199103))
+ sdst.sin_len = sizeof(sdst);
+#endif
+ sdst.sin_addr.s_addr = dst;
+ if (sendto(igmp_socket, send_buf, sendlen, 0,
+ (struct sockaddr *)&sdst, sizeof(sdst)) < 0) {
+ log(LOG_WARNING, errno, "sendto to %s on %s",
+ inet_fmt(dst, s1), inet_fmt(src, s2));
+ }
+
+ if (setloop)
+ k_set_loop(FALSE);
+
+ log(LOG_DEBUG, 0, "SENT %s from %-15s to %s",
+ type == IGMP_MTRACE ? "mtrace request" : "ask_neighbors",
+ src == INADDR_ANY ? "INADDR_ANY" : inet_fmt(src, s1),
+ inet_fmt(dst, s2));
+}
+
+/*
+ * inet_cksum extracted from:
+ * P I N G . C
+ *
+ * Author -
+ * Mike Muuss
+ * U. S. Army Ballistic Research Laboratory
+ * December, 1983
+ * Modified at Uc Berkeley
+ *
+ * (ping.c) Status -
+ * Public Domain. Distribution Unlimited.
+ *
+ * I N _ C K S U M
+ *
+ * Checksum routine for Internet Protocol family headers (C Version)
+ *
+ */
+int
+inet_cksum(addr, len)
+ u_short *addr;
+ u_int len;
+{
+ register int nleft = (int)len;
+ register u_short *w = addr;
+ u_short answer = 0;
+ register int sum = 0;
+
+ /*
+ * Our algorithm is simple, using a 32 bit accumulator (sum),
+ * we add sequential 16 bit words to it, and at the end, fold
+ * back all the carry bits from the top 16 bits into the lower
+ * 16 bits.
+ */
+ while (nleft > 1) {
+ sum += *w++;
+ nleft -= 2;
+ }
+
+ /* mop up an odd byte, if necessary */
+ if (nleft == 1) {
+ *(u_char *) (&answer) = *(u_char *)w ;
+ sum += answer;
+ }
+
+ /*
+ * add back carry outs from top 16 bits to low 16 bits
+ */
+ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
+ sum += (sum >> 16); /* add carry */
+ answer = ~sum; /* truncate to 16 bits */
+ return (answer);
+}
+
+void
+k_set_rcvbuf(bufsize)
+ int bufsize;
+{
+ if (setsockopt(igmp_socket, SOL_SOCKET, SO_RCVBUF,
+ (char *)&bufsize, sizeof(bufsize)) < 0)
+ log(LOG_ERR, errno, "setsockopt SO_RCVBUF %u", bufsize);
+}
+
+
+void
+k_hdr_include(bool)
+ int bool;
+{
+#ifdef IP_HDRINCL
+ if (setsockopt(igmp_socket, IPPROTO_IP, IP_HDRINCL,
+ (char *)&bool, sizeof(bool)) < 0)
+ log(LOG_ERR, errno, "setsockopt IP_HDRINCL %u", bool);
+#endif
+}
+
+void
+k_set_ttl(t)
+ int t;
+{
+ u_char ttl;
+
+ ttl = t;
+ if (setsockopt(igmp_socket, IPPROTO_IP, IP_MULTICAST_TTL,
+ (char *)&ttl, sizeof(ttl)) < 0)
+ log(LOG_ERR, errno, "setsockopt IP_MULTICAST_TTL %u", ttl);
+}
+
+
+void
+k_set_loop(l)
+ int l;
+{
+ u_char loop;
+
+ loop = l;
+ if (setsockopt(igmp_socket, IPPROTO_IP, IP_MULTICAST_LOOP,
+ (char *)&loop, sizeof(loop)) < 0)
+ log(LOG_ERR, errno, "setsockopt IP_MULTICAST_LOOP %u", loop);
+}
+
+void
+k_set_if(ifa)
+ u_int32 ifa;
+{
+ struct in_addr adr;
+
+ adr.s_addr = ifa;
+ if (setsockopt(igmp_socket, IPPROTO_IP, IP_MULTICAST_IF,
+ (char *)&adr, sizeof(adr)) < 0)
+ log(LOG_ERR, errno, "setsockopt IP_MULTICAST_IF %s",
+ inet_fmt(ifa, s1));
+}
+
+void
+k_join(grp, ifa)
+ u_int32 grp;
+ u_int32 ifa;
+{
+ struct ip_mreq mreq;
+
+ mreq.imr_multiaddr.s_addr = grp;
+ mreq.imr_interface.s_addr = ifa;
+
+ if (setsockopt(igmp_socket, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ (char *)&mreq, sizeof(mreq)) < 0)
+ log(LOG_WARNING, errno, "can't join group %s on interface %s",
+ inet_fmt(grp, s1), inet_fmt(ifa, s2));
+}
+
+
+void
+k_leave(grp, ifa)
+ u_int32 grp;
+ u_int32 ifa;
+{
+ struct ip_mreq mreq;
+
+ mreq.imr_multiaddr.s_addr = grp;
+ mreq.imr_interface.s_addr = ifa;
+
+ if (setsockopt(igmp_socket, IPPROTO_IP, IP_DROP_MEMBERSHIP,
+ (char *)&mreq, sizeof(mreq)) < 0)
+ log(LOG_WARNING, errno, "can't leave group %s on interface %s",
+ inet_fmt(grp, s1), inet_fmt(ifa, s2));
+}
+
+/*
+ * Convert an IP address in u_long (network) format into a printable string.
+ */
+char *
+inet_fmt(addr, s)
+ u_int32 addr;
+ char *s;
+{
+ register u_char *a;
+
+ a = (u_char *)&addr;
+ sprintf(s, "%u.%u.%u.%u", a[0], a[1], a[2], a[3]);
+ return (s);
+}
+
+
+/*
+ * Convert an IP subnet number in u_long (network) format into a printable
+ * string including the netmask as a number of bits.
+ */
+char *
+inet_fmts(addr, mask, s)
+ u_int32 addr, mask;
+ char *s;
+{
+ register u_char *a, *m;
+ int bits;
+
+ if ((addr == 0) && (mask == 0)) {
+ sprintf(s, "default");
+ return (s);
+ }
+ a = (u_char *)&addr;
+ m = (u_char *)&mask;
+ bits = 33 - ffs(ntohl(mask));
+
+ if (m[3] != 0) sprintf(s, "%u.%u.%u.%u/%d", a[0], a[1], a[2], a[3],
+ bits);
+ else if (m[2] != 0) sprintf(s, "%u.%u.%u/%d", a[0], a[1], a[2], bits);
+ else if (m[1] != 0) sprintf(s, "%u.%u/%d", a[0], a[1], bits);
+ else sprintf(s, "%u/%d", a[0], bits);
+
+ return (s);
+}
+
+char *
+inet_name(addr)
+ u_int32 addr;
+{
+ struct hostent *e;
+
+ e = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET);
+
+ return e ? e->h_name : "?";
+}
+
+
+u_int32
+host_addr(name)
+ char *name;
+{
+ struct hostent *e = (struct hostent *)0;
+ u_int32 addr;
+ int i, dots = 3;
+ char buf[40];
+ char *ip = name;
+ char *op = buf;
+
+ /*
+ * Undo BSD's favor -- take fewer than 4 octets as net/subnet address
+ * if the name is all numeric.
+ */
+ for (i = sizeof(buf) - 7; i > 0; --i) {
+ if (*ip == '.') --dots;
+ else if (*ip == '\0') break;
+ else if (!isdigit(*ip)) dots = 0; /* Not numeric, don't add zeroes */
+ *op++ = *ip++;
+ }
+ for (i = 0; i < dots; ++i) {
+ *op++ = '.';
+ *op++ = '0';
+ }
+ *op = '\0';
+
+ if (dots <= 0)
+ e = gethostbyname(name);
+ if (e && (e->h_length == sizeof(addr))) {
+ memcpy((char *)&addr, e->h_addr_list[0], e->h_length);
+ if (e->h_addr_list[1])
+ fprintf(stderr, "Warning: %s has multiple addresses, using %s\n",
+ name, inet_fmt(addr, s1));
+ } else {
+ addr = inet_addr(buf);
+ if (addr == -1 || (IN_MULTICAST(addr) && dots)) {
+ addr = 0;
+ printf("Could not parse %s as host name or address\n", name);
+ }
+ }
+ return addr;
+}
+
+
+char *
+proto_type(type)
+ u_int type;
+{
+ static char buf[80];
+
+ switch (type) {
+ case PROTO_DVMRP:
+ return ("DVMRP");
+ case PROTO_MOSPF:
+ return ("MOSPF");
+ case PROTO_PIM:
+ return ("PIM");
+ case PROTO_CBT:
+ return ("CBT");
+ case PROTO_PIM_SPECIAL:
+ return ("PIM/Special");
+ case PROTO_PIM_STATIC:
+ return ("PIM/Static");
+ case PROTO_DVMRP_STATIC:
+ return ("DVMRP/Static");
+ case PROTO_PIM_BGP4PLUS:
+ return ("PIM/BGP4+");
+ case PROTO_CBT_SPECIAL:
+ return ("CBT/Special");
+ case PROTO_CBT_STATIC:
+ return ("CBT/Static");
+ case PROTO_PIM_ASSERT:
+ return ("PIM/Assert");
+ case 0:
+ return ("None");
+ default:
+ (void) sprintf(buf, "Unknown protocol code %d", type);
+ return (buf);
+ }
+}
+
+
+char *
+flag_type(type)
+ u_int type;
+{
+ static char buf[80];
+
+ switch (type) {
+ case TR_NO_ERR:
+ return ("");
+ case TR_WRONG_IF:
+ return ("Wrong interface");
+ case TR_PRUNED:
+ return ("Prune sent upstream");
+ case TR_OPRUNED:
+ return ("Output pruned");
+ case TR_SCOPED:
+ return ("Hit scope boundary");
+ case TR_NO_RTE:
+ return ("No route");
+ case TR_NO_FWD:
+ return ("Not forwarding");
+ case TR_HIT_RP:
+ return ("Reached RP/Core");
+ case TR_RPF_IF:
+ return ("RPF Interface");
+ case TR_NO_MULTI:
+ return ("Multicast disabled");
+ case TR_OLD_ROUTER:
+ return ("Next router no mtrace");
+ case TR_NO_SPACE:
+ return ("No space in packet");
+ case TR_ADMIN_PROHIB:
+ return ("Admin. Prohibited");
+ default:
+ (void) sprintf(buf, "Unknown error code %d", type);
+ return (buf);
+ }
+}
+
+/*
+ * If destination is on a local net, get the netmask, else set the
+ * netmask to all ones. There are two side effects: if the local
+ * address was not explicitly set, and if the destination is on a
+ * local net, use that one; in either case, verify that the local
+ * address is valid.
+ */
+u_int32
+get_netmask(s, dst)
+ int s;
+ u_int32 *dst;
+{
+ unsigned int n;
+ struct ifconf ifc;
+ struct ifreq *ifrp, *ifend;
+ u_int32 if_addr, if_mask;
+ u_int32 retval = 0xFFFFFFFF;
+ int found = FALSE;
+ int num_ifreq = 32;
+
+ ifc.ifc_len = num_ifreq * sizeof(struct ifreq);
+ ifc.ifc_buf = malloc(ifc.ifc_len);
+ while (ifc.ifc_buf) {
+ if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0) {
+ perror("ioctl SIOCGIFCONF");
+ return retval;
+ }
+
+ /*
+ * If the buffer was large enough to hold all the addresses
+ * then break out, otherwise increase the buffer size and
+ * try again.
+ *
+ * The only way to know that we definitely had enough space
+ * is to know that there was enough space for at least one
+ * more struct ifreq. ???
+ */
+ if ((num_ifreq * sizeof(struct ifreq)) >=
+ ifc.ifc_len + sizeof(struct ifreq))
+ break;
+
+ num_ifreq *= 2;
+ ifc.ifc_len = num_ifreq * sizeof(struct ifreq);
+ ifc.ifc_buf = realloc(ifc.ifc_buf, ifc.ifc_len);
+ }
+ if (ifc.ifc_buf == NULL) {
+ fprintf(stderr, "getting interface list: ran out of memory");
+ exit(1);
+ }
+
+ ifrp = (struct ifreq *)ifc.ifc_buf;
+ ifend = (struct ifreq *)(ifc.ifc_buf + ifc.ifc_len);
+ /*
+ * Loop through all of the interfaces.
+ */
+ for (; ifrp < ifend && !found; ifrp = (struct ifreq *)((char *)ifrp + n)) {
+#if BSD >= 199006
+ n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name);
+ if (n < sizeof(*ifrp))
+ n = sizeof(*ifrp);
+#else
+ n = sizeof(*ifrp);
+#endif
+ /*
+ * Ignore any interface for an address family other than IP.
+ */
+ if (ifrp->ifr_addr.sa_family != AF_INET)
+ continue;
+
+ if_addr = ((struct sockaddr_in *)&(ifrp->ifr_addr))->sin_addr.s_addr;
+ if (ioctl(s, SIOCGIFFLAGS, (char *)ifrp) < 0) {
+ fprintf(stderr, "SIOCGIFFLAGS on ");
+ perror(ifrp->ifr_name);
+ continue;
+ }
+ if ((ifrp->ifr_flags & (IFF_MULTICAST|IFF_UP|IFF_LOOPBACK)) !=
+ (IFF_MULTICAST|IFF_UP))
+ continue;
+ if (*dst == 0)
+ *dst = if_addr;
+ if (ioctl(s, SIOCGIFNETMASK, (char *)ifrp) >= 0) {
+ if_mask = ((struct sockaddr_in *)&(ifrp->ifr_addr))->sin_addr.s_addr;
+ if (if_mask != 0 && (*dst & if_mask) == (if_addr & if_mask)) {
+ retval = if_mask;
+ if (lcl_addr == 0) lcl_addr = if_addr; /* XXX what about aliases? */
+ }
+ }
+ if (lcl_addr == if_addr) found = TRUE;
+ }
+ if (!found && lcl_addr != 0) {
+ printf("Interface address is not valid\n");
+ exit(1);
+ }
+ return (retval);
+}
+
+
+/*
+ * Try to pick a TTL that will get past all the thresholds in the path.
+ */
+int
+get_ttl(buf)
+ struct resp_buf *buf;
+{
+ int rno;
+ struct tr_resp *b;
+ u_int ttl;
+
+ if (buf && (rno = buf->len) > 0) {
+ b = buf->resps + rno - 1;
+ ttl = b->tr_fttl;
+
+ while (--rno > 0) {
+ --b;
+ if (ttl < b->tr_fttl) ttl = b->tr_fttl;
+ else ++ttl;
+ }
+ ttl += MULTICAST_TTL_INC;
+ if (ttl < MULTICAST_TTL1) ttl = MULTICAST_TTL1;
+ if (ttl > MULTICAST_TTL_MAX) ttl = MULTICAST_TTL_MAX;
+ return (ttl);
+ } else return(MULTICAST_TTL1);
+}
+
+/*
+ * Calculate the difference between two 32-bit NTP timestamps and return
+ * the result in milliseconds.
+ */
+int
+t_diff(a, b)
+ u_long a, b;
+{
+ int d = a - b;
+
+ return ((d * 125) >> 13);
+}
+
+/*
+ * Swap bytes for poor little-endian machines that don't byte-swap
+ */
+u_long
+byteswap(v)
+ u_long v;
+{
+ return ((v << 24) | ((v & 0xff00) << 8) |
+ ((v >> 8) & 0xff00) | (v >> 24));
+}
+
+#if 0
+/*
+ * XXX incomplete - need private callback data, too?
+ * XXX since dst doesn't get passed through?
+ */
+int
+neighbors_callback(tmo, buf, buflen, igmp, igmplen, addr, addrlen, ts)
+ int tmo;
+ u_char *buf;
+ int buflen;
+ struct igmp *igmp;
+ int igmplen;
+ struct sockaddr *addr;
+ int *addrlen;
+ struct timeval *ts;
+{
+ int len;
+ u_int32 dst;
+ struct ip *ip = (struct ip *)buf;
+
+ if (tmo)
+ return 0;
+
+ if (igmp->igmp_code != DVMRP_NEIGHBORS2)
+ return 0;
+ len = igmplen;
+ /*
+ * Accept DVMRP_NEIGHBORS2 response if it comes from the
+ * address queried or if that address is one of the local
+ * addresses in the response.
+ */
+ if (ip->ip_src.s_addr != dst) {
+ u_int32 *p = (u_int32 *)(igmp + 1);
+ u_int32 *ep = p + (len >> 2);
+ while (p < ep) {
+ u_int32 laddr = *p++;
+ int n = ntohl(*p++) & 0xFF;
+ if (laddr == dst) {
+ ep = p + 1; /* ensure p < ep after loop */
+ break;
+ }
+ p += n;
+ }
+ if (p >= ep)
+ return 0;
+ }
+ return buflen;
+}
+#endif
+
+int
+mtrace_callback(tmo, buf, buflen, igmp, igmplen, addr, addrlen, ts)
+ int tmo;
+ u_char *buf;
+ int buflen;
+ struct igmp *igmp;
+ int igmplen;
+ struct sockaddr *addr;
+ int *addrlen;
+ struct timeval *ts;
+{
+ static u_char *savbuf = NULL;
+ static int savbuflen;
+ static struct sockaddr *savaddr;
+ static int savaddrlen;
+ static struct timeval savts;
+
+ int len = (igmplen - QLEN) / RLEN;
+ struct tr_resp *r = (struct tr_resp *)((struct tr_query *)(igmp + 1) + 1);
+
+ if (tmo == 1) {
+ /*
+ * If we timed out with a packet saved, then return that packet.
+ * send_recv won't send this same packet to the callback again.
+ */
+ if (savbuf) {
+ bcopy(savbuf, buf, savbuflen);
+ free(savbuf);
+ savbuf = NULL;
+ bcopy(savaddr, addr, savaddrlen);
+ free(savaddr);
+ *addrlen = savaddrlen;
+ bcopy(&savts, ts, sizeof(savts));
+ return savbuflen;
+ }
+ return 0;
+ }
+ if (savbuf) {
+ free(savbuf);
+ savbuf = NULL;
+ free(savaddr);
+ }
+ /*
+ * Check for IOS bug described in CSCdi68628, where a router that does
+ * not have multicast enabled responds to an mtrace request with a 1-hop
+ * error packet.
+ * Heuristic is:
+ * If there is only one hop reported in the packet,
+ * And the protocol code is 0,
+ * And there is no previous hop,
+ * And the forwarding information is "Not Forwarding",
+ * And the router is not on the same subnet as the destination of the
+ * trace,
+ * then drop this packet. The "#if 0"'d code saves it and returns
+ * it on timeout, but timeouts are too common (e.g. routers with
+ * limited unicast routing tables, etc).
+ */
+ if (len == 1 && r->tr_rproto == 0 && r->tr_rmtaddr == 0 &&
+ r->tr_rflags == TR_NO_FWD) {
+ u_int32 smask;
+
+ VAL_TO_MASK(smask, r->tr_smask);
+ if ((r->tr_outaddr & smask) != (qdst & smask)) {
+#if 0
+ /* XXX should do this silently? */
+ fprintf(stderr, "mtrace: probably IOS-buggy packet from %s\n",
+ inet_fmt(((struct sockaddr_in *)addr)->sin_addr.s_addr, s1));
+ /* Save the packet to return if a timeout occurs. */
+ savbuf = (u_char *)malloc(buflen);
+ if (savbuf != NULL) {
+ bcopy(buf, savbuf, buflen);
+ savbuflen = buflen;
+ savaddr = (struct sockaddr *)malloc(*addrlen);
+ if (savaddr != NULL) {
+ bcopy(addr, savaddr, *addrlen);
+ savaddrlen = *addrlen;
+ bcopy(ts, &savts, sizeof(savts));
+ } else {
+ free(savbuf);
+ savbuf = NULL;
+ }
+ }
+#endif
+ return 0;
+ }
+ }
+ return buflen;
+}
+
+int
+send_recv(dst, type, code, tries, save, callback)
+ u_int32 dst;
+ int type, code, tries;
+ struct resp_buf *save;
+ callback_t callback;
+{
+ fd_set fds;
+ struct timeval tq, tr, tv;
+ struct ip *ip;
+ struct igmp *igmp;
+ struct tr_query *query, *rquery;
+ struct tr_resp *r;
+ struct sockaddr_in recvaddr;
+ u_int32 local, group;
+ int ipdatalen, iphdrlen, igmpdatalen;
+ int datalen;
+ int count, recvlen, socklen = sizeof(recvaddr);
+ int len;
+ int i;
+
+ if (type == IGMP_MTRACE) {
+ group = qgrp;
+ datalen = sizeof(struct tr_query);
+ } else {
+ group = htonl(0xff03);
+ datalen = 0;
+ }
+ if (IN_MULTICAST(ntohl(dst))) local = lcl_addr;
+ else local = INADDR_ANY;
+
+ /*
+ * If the reply address was not explictly specified, start off
+ * with the standard multicast reply address, or the unicast
+ * address of this host if the unicast flag was specified.
+ * Then, if there is no response after trying half the tries
+ * with multicast, switch to the unicast address of this host
+ * if the multicast flag was not specified. If the TTL was
+ * also not specified, set a multicast TTL and increase it
+ * for every try.
+ */
+ query = (struct tr_query *)(send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN);
+ query->tr_raddr = raddr ? raddr : unicast ? lcl_addr : resp_cast;
+ TR_SETTTL(query->tr_rttlqid, rttl ? rttl :
+ IN_MULTICAST(ntohl(query->tr_raddr)) ? get_ttl(save) : UNICAST_TTL);
+ query->tr_src = qsrc;
+ query->tr_dst = qdst;
+
+ for (i = tries ; i > 0; --i) {
+ int oqid;
+
+ if (tries == nqueries && raddr == 0) {
+ if (i == (nqueries >> 1)) {
+ if (multicast && unicast) {
+ query->tr_raddr = resp_cast;
+ if (!rttl)
+ TR_SETTTL(query->tr_rttlqid, get_ttl(save));
+ } else if (!multicast) {
+ query->tr_raddr = lcl_addr;
+ TR_SETTTL(query->tr_rttlqid, UNICAST_TTL);
+ }
+ }
+ if (i < tries && IN_MULTICAST(ntohl(query->tr_raddr)) &&
+ rttl == 0) {
+ TR_SETTTL(query->tr_rttlqid,
+ TR_GETTTL(query->tr_rttlqid) + MULTICAST_TTL_INC);
+ if (TR_GETTTL(query->tr_rttlqid) > MULTICAST_TTL_MAX)
+ TR_SETTTL(query->tr_rttlqid, MULTICAST_TTL_MAX);
+ }
+ }
+
+ /*
+ * Change the qid for each request sent to avoid being confused
+ * by duplicate responses
+ */
+ oqid = TR_GETQID(query->tr_rttlqid);
+ if (staticqid)
+ TR_SETQID(query->tr_rttlqid, staticqid);
+ else
+#ifdef SYSV
+ TR_SETQID(query->tr_rttlqid, ((u_int32)lrand48() >> 8));
+#else
+ TR_SETQID(query->tr_rttlqid, ((u_int32)arc4random() >> 8));
+#endif
+
+ /*
+ * Set timer to calculate delays, then send query
+ */
+ gettimeofday(&tq, 0);
+ send_igmp(local, dst, type, code, group, datalen);
+
+ /*
+ * Wait for response, discarding false alarms
+ */
+ while (TRUE) {
+ 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';
+ /* FALLTHROUGH */
+
+ case OUTS:
+ if (tunstats)
+ printf(" %c%-5d ", whochar, v_out);
+ else
+ printf(" %c", whochar);
+ printf("%4d pps", v_pps);
+ if (v_pps && badtime)
+ printf("?");
+
+ break;
+
+ case NEITHER:
+ if (ghave != NEITHER)
+ if (tunstats)
+ printf(" ");
+ else
+ printf(" ");
+
+ break;
+ }
+
+ whochar = have_next ? '^' : ' ';
+ switch (ghave) {
+ case BOTH:
+ g_lost = g_out - (ntohl(s->tr_pktcnt) - ntohl(r->tr_pktcnt));
+ if (g_out) g_pct = g_lost * 100 / g_out;
+ else g_pct = 0;
+ if (-20 < g_pct && g_pct < 101 && g_out > 10)
+ sprintf(g_str, "%3d%%", g_pct);
+ else if (g_pct < -900 && g_out > 10)
+ sprintf(g_str, "%3dx", (int)(-g_pct / 100. + 1.));
+ else if (g_pct <= -20 && g_out > 10)
+ sprintf(g_str, "%1.1fx", -g_pct / 100. + 1.);
+ else
+ memcpy(g_str, " -- ", 5);
+
+ printf("%s%6d/%-5d=%s%4d pps",
+ tunstats ? "" : " ", g_lost, g_out, g_str, g_pps);
+ if (g_pps && badtime)
+ printf("?");
+ printf("\n");
+ break;
+
+#if 0
+ case INS:
+ g_out = ntohl(s->tr_pktcnt) - ntohl(r->tr_pktcnt);
+ g_pps = g_out / timediff;
+ whochar = 'v';
+ /* FALLTHROUGH */
+#endif
+
+ case OUTS:
+ printf("%s ?/%-5d %4d pps",
+ tunstats ? "" : " ", g_out, g_pps);
+ if (badtime)
+ printf("?");
+ printf("\n");
+ break;
+
+ case INS:
+ case NEITHER:
+ printf("\n");
+ break;
+ }
+
+
+ if (debug > 2) {
+ printf("\t\t\t\tv_in: %ld ", (long)ntohl(s->tr_vifin));
+ printf("v_out: %ld ", (long)ntohl(s->tr_vifout));
+ printf("pkts: %ld\n", (long)ntohl(s->tr_pktcnt));
+ printf("\t\t\t\tv_in: %ld ", (long)ntohl(r->tr_vifin));
+ printf("v_out: %ld ", (long)ntohl(r->tr_vifout));
+ printf("pkts: %ld\n", (long)ntohl(r->tr_pktcnt));
+ printf("\t\t\t\tv_in: %ld ",
+ (long)(ntohl(s->tr_vifin) - ntohl(r->tr_vifin)));
+ printf("v_out: %ld ",
+ (long)(ntohl(s->tr_vifout) - ntohl(r->tr_vifout)));
+ printf("pkts: %ld ", (long)(ntohl(s->tr_pktcnt) - ntohl(r->tr_pktcnt)));
+ printf("time: %d\n", timediff);
+ printf("\t\t\t\treset: %x hoptime: %lx\n", *rst, ntohl(s->tr_qarr));
+ }
+}
+
+/*
+ * A fixup to check if any pktcnt has been reset, and to fix the
+ * byteorder bugs in mrouted 3.6 on little-endian machines.
+ *
+ * XXX Since periodic traffic sources are likely to have their
+ * pktcnt periodically reset, should we save old values when
+ * the reset occurs to keep slightly better statistics over
+ * the long term? (e.g. SAP)
+ */
+void
+fixup_stats(base, prev, new, bugs)
+ struct resp_buf *base, *prev, *new;
+ int *bugs;
+{
+ int rno = base->len;
+ struct tr_resp *b = base->resps + rno;
+ struct tr_resp *p = prev->resps + rno;
+ struct tr_resp *n = new->resps + rno;
+ int *r = bugs + rno;
+ int res;
+ int cleanup = 0;
+
+ /* Check for byte-swappers. Only check on the first trace,
+ * since long-running traces can wrap around and falsely trigger. */
+ while (--rno >= 0) {
+#ifdef TEST_ONLY
+ u_int32 nvifout = ntohl(n->tr_vifout);
+ u_int32 pvifout = ntohl(p->tr_vifout);
+#endif
+ --n; --p; --b;
+#ifdef TEST_ONLY /*XXX this is still buggy, so disable it for release */
+ if ((*r & BUG_SWAP) ||
+ ((base == prev) &&
+ (nvifout - pvifout) > (byteswap(nvifout) - byteswap(pvifout)))) {
+ if (1 || debug > 2) {
+ printf("ip %s swaps; b %08x p %08x n %08x\n",
+ inet_fmt(n->tr_inaddr, s1),
+ ntohl(b->tr_vifout), pvifout, nvifout);
+ }
+ /* This host sends byteswapped reports; swap 'em */
+ if (!(*r & BUG_SWAP)) {
+ *r |= BUG_SWAP;
+ b->tr_qarr = byteswap(b->tr_qarr);
+ b->tr_vifin = byteswap(b->tr_vifin);
+ b->tr_vifout = byteswap(b->tr_vifout);
+ b->tr_pktcnt = byteswap(b->tr_pktcnt);
+ }
+
+ n->tr_qarr = byteswap(n->tr_qarr);
+ n->tr_vifin = byteswap(n->tr_vifin);
+ n->tr_vifout = byteswap(n->tr_vifout);
+ n->tr_pktcnt = byteswap(n->tr_pktcnt);
+ }
+#endif
+ /*
+ * A missing parenthesis in mrouted 3.5-3.8's prune.c
+ * causes extremely bogus time diff's.
+ * One half of the time calculation was
+ * inside an htonl() and one half wasn't. Therefore, on
+ * a little-endian machine, both halves of the calculation
+ * would get added together in the little end. Thus, the
+ * low-order 2 bytes are either 0000 (no overflow) or
+ * 0100 (overflow from the addition).
+ *
+ * Odds are against these particular bit patterns
+ * happening in both prev and new for actual time values.
+ */
+ if ((*r & BUG_BOGUSTIME) || (((ntohl(n->tr_qarr) & 0xfeff) == 0x0000) &&
+ ((ntohl(p->tr_qarr) & 0xfeff) == 0x0000))) {
+ *r |= BUG_BOGUSTIME;
+ n->tr_qarr = new->rtime;
+ p->tr_qarr = prev->rtime;
+ b->tr_qarr = base->rtime;
+ }
+ }
+
+ rno = base->len;
+ b = base->resps + rno;
+ p = prev->resps + rno;
+ n = new->resps + rno;
+ r = bugs + rno;
+
+ while (--rno >= 0) {
+ --n; --p; --b; --r;
+ /*
+ * This hop has reset if:
+ * - There were statistics in the base AND previous pass, AND
+ * - There are less packets this time than the first time and
+ * we didn't reset last time, OR
+ * - There are less packets this time than last time, OR
+ * - There are no statistics on this pass.
+ *
+ * The "and we didn't reset last time" is necessary in the
+ * first branch of the OR because if the base is large and
+ * we reset last time but the constant-resetter-avoidance
+ * code kicked in so we delayed the copy of prev to base,
+ * new could still be below base so we trigger the
+ * constant-resetter code even though it was really only
+ * a single reset.
+ */
+ res = ((b->tr_pktcnt != 0xFFFFFFFF) && (p->tr_pktcnt != 0xFFFFFFFF) &&
+ ((!(*r & BUG_RESET) && ntohl(n->tr_pktcnt) < ntohl(b->tr_pktcnt)) ||
+ (ntohl(n->tr_pktcnt) < ntohl(p->tr_pktcnt)) ||
+ (n->tr_pktcnt == 0xFFFFFFFF)));
+ if (debug > 2) {
+ printf("\t\tip=%s, r=%d, res=%d\n", inet_fmt(b->tr_inaddr, s1), *r, res);
+ if (res)
+ printf("\t\tbase=%ld, prev=%ld, new=%ld\n", ntohl(b->tr_pktcnt),
+ ntohl(p->tr_pktcnt), ntohl(n->tr_pktcnt));
+ }
+ if (*r & BUG_RESET) {
+ if (res || (*r & BUG_RESET2X)) {
+ /*
+ * This router appears to be a 3.4 with that nasty ol'
+ * neighbor version bug, which causes it to constantly
+ * reset. Just nuke the statistics for this node, and
+ * don't even bother giving it the benefit of the
+ * doubt from now on.
+ */
+ p->tr_pktcnt = b->tr_pktcnt = n->tr_pktcnt;
+ *r |= BUG_RESET2X;
+ } else {
+ /*
+ * This is simply the situation that the original
+ * fixup_stats was meant to deal with -- that a
+ * 3.3 or 3.4 router deleted a cache entry while
+ * traffic was still active.
+ */
+ *r &= ~BUG_RESET;
+ cleanup = 1;
+ }
+ } else
+ if (res)
+ *r |= BUG_RESET;
+ }
+
+ if (cleanup == 0) return;
+
+ /*
+ * If some hop reset its counters and didn't continue to
+ * reset, then we pretend that the previous
+ * trace was the first one.
+ */
+ rno = base->len;
+ b = base->resps + rno;
+ p = prev->resps + rno;
+
+ while (--rno >= 0) (--b)->tr_pktcnt = (--p)->tr_pktcnt;
+ base->qtime = prev->qtime;
+ base->rtime = prev->rtime;
+}
+
+/*
+ * Check per-source losses along path and compare with threshold.
+ */
+int
+check_thresh(thresh, base, prev, new)
+ int thresh;
+ struct resp_buf *base, *prev, *new;
+{
+ int rno = base->len - 1;
+ struct tr_resp *b = base->resps + rno;
+ struct tr_resp *p = prev->resps + rno;
+ struct tr_resp *n = new->resps + rno;
+ int g_out, g_lost;
+
+ while (TRUE) {
+ if ((n->tr_inaddr != b->tr_inaddr) ||
+ (n->tr_outaddr != b->tr_outaddr) ||
+ (n->tr_rmtaddr != b->tr_rmtaddr))
+ return 1; /* Route changed */
+
+ if (rno-- < 1) break;
+ g_out = ntohl(n->tr_pktcnt) - ntohl(p->tr_pktcnt);
+ b--; n--; p--;
+ g_lost = g_out - (ntohl(n->tr_pktcnt) - ntohl(p->tr_pktcnt));
+ if (g_out && ((g_lost * 100 + (g_out >> 1))/ g_out) > thresh) {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/*
+ * Print responses with statistics for forward path (from src to dst)
+ */
+int
+print_stats(base, prev, new, bugs, names)
+ struct resp_buf *base, *prev, *new;
+ int *bugs;
+ char **names;
+{
+ int rtt, hop;
+ char *ms;
+ u_int32 smask;
+ int rno = base->len - 1;
+ struct tr_resp *b = base->resps + rno;
+ struct tr_resp *p = prev->resps + rno;
+ struct tr_resp *n = new->resps + rno;
+ int *r = bugs + rno;
+ u_long resptime = new->rtime;
+ u_long qarrtime = ntohl(n->tr_qarr);
+ u_int ttl = MaX(1, n->tr_fttl) + 1;
+ int first = (base == prev);
+
+ VAL_TO_MASK(smask, b->tr_smask);
+ printf(" Source Response Dest ");
+ if (tunstats)
+ printf("Packet Statistics For Only For Traffic\n");
+ else
+ printf("Overall Packet Statistics For Traffic From\n");
+ (void)inet_fmt(base->qhdr.tr_src, s1);
+ printf("%-15s %-15s ",
+ ((b->tr_inaddr & smask) == (base->qhdr.tr_src & smask)) ?
+ s1 : " * * * ",
+ inet_fmt(base->qhdr.tr_raddr, s2));
+ (void)inet_fmt(base->igmp.igmp_group.s_addr, s2);
+ if (tunstats)
+ printf("All Multicast Traffic From %s\n", s1);
+ else
+ printf("Packet %s To %s\n", s1, s2);
+ rtt = t_diff(resptime, new->qtime);
+ ms = scale(&rtt);
+ printf(" %c __/ rtt%5d%s ",
+ (first && !verbose) ? 'v' : '|', rtt, ms);
+ if (tunstats)
+ printf("Lost/Sent = Pct Rate To %s\n", s2);
+ else
+ printf(" Rate Lost/Sent = Pct Rate\n");
+ if (!first || verbose) {
+ hop = t_diff(resptime, qarrtime);
+ ms = scale(&hop);
+ printf(" v / hop%5d%s ", hop, ms);
+ if (tunstats)
+ printf("--------------------- --------------------\n");
+ else
+ printf("------- ---------------------\n");
+ }
+ if ((b->tr_inaddr & smask) != (base->qhdr.tr_src & smask) &&
+ b->tr_rmtaddr != 0) {
+ printf("%-15s %-14s is the previous hop\n", inet_fmt(b->tr_rmtaddr, s1),
+ inet_name(b->tr_rmtaddr));
+ printf(" v ^\n");
+ }
+ if (debug > 2) {
+ printf("\t\t\t\tv_in: %ld ", (long)ntohl(n->tr_vifin));
+ printf("v_out: %ld ", (long)ntohl(n->tr_vifout));
+ printf("pkts: %ld\n", (long)ntohl(n->tr_pktcnt));
+ printf("\t\t\t\tv_in: %ld ", (long)ntohl(b->tr_vifin));
+ printf("v_out: %ld ", (long)ntohl(b->tr_vifout));
+ printf("pkts: %ld\n", (long)ntohl(b->tr_pktcnt));
+ printf("\t\t\t\tv_in: %ld ",
+ (long)(ntohl(n->tr_vifin) - ntohl(b->tr_vifin)));
+ printf("v_out: %ld ",
+ (long)(ntohl(n->tr_vifout) - ntohl(b->tr_vifout)));
+ printf("pkts: %ld\n",
+ (long)(ntohl(n->tr_pktcnt) - ntohl(b->tr_pktcnt)));
+ printf("\t\t\t\treset: %x hoptime: %lx\n", *r, (long)ntohl(n->tr_qarr));
+ }
+
+ while (TRUE) {
+ if ((n->tr_inaddr != b->tr_inaddr) ||
+ (n->tr_outaddr != b->tr_outaddr) ||
+ (n->tr_rmtaddr != b->tr_rmtaddr))
+ return 1; /* Route changed */
+
+ if ((n->tr_inaddr != n->tr_outaddr) && n->tr_inaddr)
+ printf("%-15s\n", inet_fmt(n->tr_inaddr, s1));
+ printf("%-15s %-14s %s%s\n", inet_fmt(n->tr_outaddr, s1), names[rno],
+ flag_type(n->tr_rflags),
+ (*r & BUG_NOPRINT) ? " [reset counters]" : "");
+
+ if (rno-- < 1) break;
+
+ printf(" %c ^ ttl%5d ", (first && !verbose) ? 'v' : '|',
+ ttl);
+ stat_line(p, n, TRUE, r);
+ if (!first || verbose) {
+ resptime = qarrtime;
+ qarrtime = ntohl((n-1)->tr_qarr);
+ hop = t_diff(resptime, qarrtime);
+ ms = scale(&hop);
+ printf(" v | hop%5d%s", hop, ms);
+ if (first)
+ printf("\n");
+ else
+ stat_line(b, n, TRUE, r);
+ }
+
+ --b, --p, --n, --r;
+ ttl = MaX(ttl, MaX(1, n->tr_fttl) + base->len - rno);
+ }
+
+ printf(" %c \\__ ttl%5d ", (first && !verbose) ? 'v' : '|',
+ ttl);
+ stat_line(p, n, FALSE, r);
+ if (!first || verbose) {
+ hop = t_diff(qarrtime, new->qtime);
+ ms = scale(&hop);
+ printf(" v \\ hop%5d%s", hop, ms);
+ if (first)
+ printf("\n");
+ else
+ stat_line(b, n, FALSE, r);
+ }
+ printf("%-15s %s\n", inet_fmt(base->qhdr.tr_dst, s1),
+ !passive ? inet_fmt(lcl_addr, s2) : " * * * ");
+ printf(" Receiver Query Source\n\n");
+ return 0;
+}
+
+/*
+ * Determine whether or not the path has changed.
+ */
+int
+path_changed(base, new)
+ struct resp_buf *base, *new;
+{
+ int rno = base->len - 1;
+ struct tr_resp *b = base->resps + rno;
+ struct tr_resp *n = new->resps + rno;
+
+ while (rno-- >= 0) {
+ if ((n->tr_inaddr != b->tr_inaddr) ||
+ (n->tr_outaddr != b->tr_outaddr) ||
+ (n->tr_rmtaddr != b->tr_rmtaddr))
+ return 1; /* Route changed */
+ if ((b->tr_rflags == TR_NO_RTE) &&
+ (n->tr_rflags != TR_NO_RTE))
+ return 1; /* Route got longer? */
+ --n;
+ --b;
+ }
+ return 0;
+}
+
+
+/***************************************************************************
+ * main
+ ***************************************************************************/
+
+int
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ int udp;
+ struct sockaddr_in addr;
+ int addrlen = sizeof(addr);
+ int recvlen;
+ struct timeval tv;
+ struct resp_buf *prev, *new;
+ struct tr_resp *r;
+ u_int32 smask;
+ int rno;
+ int hops, nexthop, tries;
+ u_int32 lastout = 0;
+ int numstats = 1;
+ int waittime;
+ int seed;
+ int hopbyhop;
+ int i;
+ int printed = 1;
+
+ if (geteuid() != 0)
+ errx(1, "must be root");
+
+ /*
+ * We might get spawned by vat with the audio device open.
+ * Close everything but stdin, stdout, stderr.
+ */
+ for (i = 3; i < 255; i++)
+ close(i);
+
+ init_igmp();
+ setuid(getuid());
+
+ argv++, argc--;
+ if (argc == 0) usage();
+
+ while (argc > 0 && *argv[0] == '-') {
+ char *p = *argv++; argc--;
+ p++;
+ do {
+ char c = *p++;
+ char *arg = (char *) 0;
+ if (isdigit(*p)) {
+ arg = p;
+ p = "";
+ } else if (argc > 0) arg = argv[0];
+ switch (c) {
+ case 'd': /* Unlisted debug print option */
+ if (arg && isdigit(*arg)) {
+ debug = atoi(arg);
+ if (debug < 0) debug = 0;
+ if (debug > 3) debug = 3;
+ if (arg == argv[0]) argv++, argc--;
+ break;
+ } else
+ usage();
+ case 'M': /* Use multicast for reponse */
+ multicast = TRUE;
+ break;
+ case 'U': /* Use unicast for response */
+ unicast = TRUE;
+ break;
+ case 'L': /* Trace w/ loss threshold */
+ if (arg && isdigit(*arg)) {
+ lossthresh = atoi(arg);
+ if (lossthresh < 0)
+ lossthresh = 0;
+ numstats = 3153600;
+ if (arg == argv[0]) argv++, argc--;
+ break;
+ } else
+ usage();
+ break;
+ case 'O': /* Don't use IP options */
+ sendopts = FALSE;
+ break;
+ case 'P': /* Just watch the path */
+ printstats = FALSE;
+ numstats = 3153600;
+ break;
+ case 'Q': /* (undoc.) always use this QID */
+ if (arg && isdigit(*arg)) {
+ staticqid = atoi(arg);
+ if (staticqid < 0)
+ staticqid = 0;
+ if (arg == argv[0]) argv++, argc--;
+ break;
+ } else
+ usage();
+ break;
+ case 'T': /* Print confusing tunnel stats */
+ tunstats = TRUE;
+ break;
+ case 'W': /* Cisco's "weak" mtrace */
+ weak = TRUE;
+ break;
+ case 'V': /* Print version and exit */
+ /*
+ * FreeBSD wants to have its own Id string, so
+ * determination of the version number has to change.
+ * XXX Note that this must be changed by hand on importing
+ * XXX new versions!
+ */
+ {
+ char *r = strdup(rcsid);
+ char *s = strchr(r, ',');
+
+ while (s && *(s+1) != 'v')
+ s = strchr(s + 1, ',');
+
+ if (s) {
+ char *q;
+
+ s += 3; /* , v sp */
+ q = strchr(s, ' ');
+ if (q)
+ *q = '\0';
+ fprintf(stderr, "mtrace version 5.2/%s\n", s);
+ } else {
+ fprintf(stderr, "mtrace could not determine version number!?\n");
+ }
+ exit(1);
+ }
+ break;
+ case 'l': /* Loop updating stats indefinitely */
+ numstats = 3153600;
+ break;
+ case 'n': /* Don't reverse map host addresses */
+ numeric = TRUE;
+ break;
+ case 'p': /* Passive listen for traces */
+ passive = TRUE;
+ break;
+ case 'v': /* Verbosity */
+ verbose = TRUE;
+ break;
+ case 's': /* Short form, don't wait for stats */
+ numstats = 0;
+ break;
+ case 'w': /* Time to wait for packet arrival */
+ if (arg && isdigit(*arg)) {
+ timeout = atoi(arg);
+ if (timeout < 1) timeout = 1;
+ if (arg == argv[0]) argv++, argc--;
+ break;
+ } else
+ usage();
+ case 'f': /* first hop */
+ if (arg && isdigit(*arg)) {
+ qno = atoi(arg);
+ if (qno > MAXHOPS) qno = MAXHOPS;
+ else if (qno < 1) qno = 0;
+ if (arg == argv[0]) argv++, argc--;
+ fflag++;
+ break;
+ } else
+ usage();
+ case 'm': /* Max number of hops to trace */
+ if (arg && isdigit(*arg)) {
+ qno = atoi(arg);
+ if (qno > MAXHOPS) qno = MAXHOPS;
+ else if (qno < 1) qno = 0;
+ if (arg == argv[0]) argv++, argc--;
+ break;
+ } else
+ usage();
+ case 'q': /* Number of query retries */
+ if (arg && isdigit(*arg)) {
+ nqueries = atoi(arg);
+ if (nqueries < 1) nqueries = 1;
+ if (arg == argv[0]) argv++, argc--;
+ break;
+ } else
+ usage();
+ case 'g': /* Last-hop gateway (dest of query) */
+ if (arg && (gwy = host_addr(arg))) {
+ if (arg == argv[0]) argv++, argc--;
+ break;
+ } else
+ usage();
+ case 't': /* TTL for query packet */
+ if (arg && isdigit(*arg)) {
+ qttl = atoi(arg);
+ if (qttl < 1) qttl = 1;
+ rttl = qttl;
+ if (arg == argv[0]) argv++, argc--;
+ break;
+ } else
+ usage();
+ case 'e': /* Extra hops past non-responder */
+ if (arg && isdigit(*arg)) {
+ extrahops = atoi(arg);
+ if (extrahops < 0) extrahops = 0;
+ if (arg == argv[0]) argv++, argc--;
+ break;
+ } else
+ usage();
+ case 'r': /* Dest for response packet */
+ if (arg && (raddr = host_addr(arg))) {
+ if (arg == argv[0]) argv++, argc--;
+ break;
+ } else
+ usage();
+ case 'i': /* Local interface address */
+ if (arg && (lcl_addr = host_addr(arg))) {
+ if (arg == argv[0]) argv++, argc--;
+ break;
+ } else
+ usage();
+ case 'S': /* Stat accumulation interval */
+ if (arg && isdigit(*arg)) {
+ statint = atoi(arg);
+ if (statint < 1) statint = 1;
+ if (arg == argv[0]) argv++, argc--;
+ break;
+ } else
+ usage();
+ default:
+ usage();
+ }
+ } while (*p);
+ }
+
+ if (argc > 0 && (qsrc = host_addr(argv[0]))) { /* Source of path */
+ if (IN_MULTICAST(ntohl(qsrc))) {
+ if (gwy) {
+ /* Should probably rewrite arg parsing at some point, as
+ * this makes "mtrace -g foo 224.1.2.3 224.2.3.4" valid!... */
+ qgrp = qsrc;
+ qsrc = 0;
+ } else {
+ usage();
+ }
+ }
+ argv++, argc--;
+ if (argc > 0 && (qdst = host_addr(argv[0]))) { /* Dest of path */
+ argv++, argc--;
+ if (argc > 0 && (qgrp = host_addr(argv[0]))) { /* Path via group */
+ argv++, argc--;
+ }
+ if (IN_MULTICAST(ntohl(qdst))) {
+ u_int32 temp = qdst;
+ qdst = qgrp;
+ qgrp = temp;
+ if (IN_MULTICAST(ntohl(qdst))) usage();
+ } else if (qgrp && !IN_MULTICAST(ntohl(qgrp))) usage();
+ }
+ }
+
+ if (passive) {
+ passive_mode();
+ return(0);
+ }
+
+ if (argc > 0) {
+ usage();
+ }
+
+#ifdef SUNOS5
+ if (sendopts)
+ checkforsolarisbug();
+#endif
+
+ /*
+ * Set useful defaults for as many parameters as possible.
+ */
+
+ defgrp = 0; /* Default to no group */
+ query_cast = htonl(0xE0000002); /* All routers multicast addr */
+ resp_cast = htonl(0xE0000120); /* Mtrace response multicast addr */
+ if (qgrp == 0) {
+ if (!weak)
+ qgrp = defgrp;
+ if (printstats && numstats != 0 && !tunstats) {
+ /* Stats are useless without a group */
+ warnx(
+ "WARNING: no multicast group specified, so no statistics printed");
+ numstats = 0;
+ }
+ } else {
+ if (weak)
+ warnx(
+ "WARNING: group was specified so not performing \"weak\" mtrace");
+ }
+
+ /*
+ * Get default local address for multicasts to use in setting defaults.
+ */
+ addr.sin_family = AF_INET;
+#if (defined(BSD) && (BSD >= 199103))
+ addr.sin_len = sizeof(addr);
+#endif
+ addr.sin_addr.s_addr = qgrp ? qgrp : query_cast;
+ addr.sin_port = htons(2000); /* Any port above 1024 will do */
+
+ /*
+ * Note that getsockname() can return 0 on some systems
+ * (notably SunOS 5.x, x < 6). This is taken care of in
+ * get_netmask(). If the default multicast interface (set
+ * with the route for 224.0.0.0) is not the same as the
+ * hostname, mtrace -i [if_addr] will have to be used.
+ */
+ if (((udp = socket(AF_INET, SOCK_DGRAM, 0)) < 0) ||
+ (connect(udp, (struct sockaddr *) &addr, sizeof(addr)) < 0) ||
+ getsockname(udp, (struct sockaddr *) &addr, &addrlen) < 0)
+ err(-1, "determining local address");
+
+#ifdef SUNOS5
+ /*
+ * SunOS 5.X prior to SunOS 2.6, getsockname returns 0 for udp socket.
+ * This call to sysinfo will return the hostname.
+ * If the default multicast interfface (set with the route
+ * for 224.0.0.0) is not the same as the hostname,
+ * mtrace -i [if_addr] will have to be used.
+ */
+ if (addr.sin_addr.s_addr == 0) {
+ char myhostname[MAXHOSTNAMELEN];
+ struct hostent *hp;
+ int error;
+
+ error = sysinfo(SI_HOSTNAME, myhostname, sizeof(myhostname));
+ if (error == -1)
+ err(1, "getting my hostname");
+
+ hp = gethostbyname(myhostname);
+ if (hp == NULL || hp->h_addrtype != AF_INET ||
+ hp->h_length != sizeof(addr.sin_addr))
+ err(1, "finding IP address for my hostname");
+
+ memcpy((char *)&addr.sin_addr.s_addr, hp->h_addr, hp->h_length);
+ }
+#endif
+
+ /*
+ * Default destination for path to be queried is the local host.
+ * When gateway specified, default destination is that gateway
+ * and default source is local host.
+ */
+ if (qdst == 0) {
+ qdst = lcl_addr ? lcl_addr : addr.sin_addr.s_addr;
+ dst_netmask = get_netmask(udp, &qdst);
+ if (gwy && (gwy & dst_netmask) != (qdst & dst_netmask) &&
+ !IN_MULTICAST(ntohl(gwy)))
+ qdst = gwy;
+ }
+ if (qsrc == 0 && gwy)
+ qsrc = lcl_addr ? lcl_addr : addr.sin_addr.s_addr;
+ if (qsrc == 0)
+ usage();
+ if (!dst_netmask)
+ dst_netmask = get_netmask(udp, &qdst);
+ close(udp);
+ if (lcl_addr == 0) lcl_addr = addr.sin_addr.s_addr;
+
+ /*
+ * Initialize the seed for random query identifiers.
+ */
+ gettimeofday(&tv, 0);
+ seed = tv.tv_usec ^ lcl_addr;
+#ifdef SYSV
+ srand48(seed);
+#endif
+
+ /*
+ * Protect against unicast queries to mrouted versions that might crash.
+ * Also use the obsolete "can mtrace" neighbor bit to warn about
+ * older implementations.
+ */
+ if (gwy && !IN_MULTICAST(ntohl(gwy)))
+ if (send_recv(gwy, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS2, 1, &incr[0], NULL)) {
+ int flags = ntohl(incr[0].igmp.igmp_group.s_addr);
+ int version = flags & 0xFFFF;
+ int info = (flags & 0xFF0000) >> 16;
+
+ if (version == 0x0303 || version == 0x0503) {
+ printf("Don't use -g to address an mrouted 3.%d, it might crash\n",
+ (version >> 8) & 0xFF);
+ exit(0);
+ }
+ if ((info & 0x08) == 0) {
+ printf("mtrace: ");
+ print_host(gwy);
+ printf(" probably doesn't support mtrace, trying anyway...\n");
+ }
+ }
+
+ printf("Mtrace from %s to %s via group %s\n",
+ inet_fmt(qsrc, s1), inet_fmt(qdst, s2), inet_fmt(qgrp, s3));
+
+ if ((qdst & dst_netmask) == (qsrc & dst_netmask))
+ fprintf(stderr, "mtrace: Source & receiver appear to be directly connected\n");
+
+ /*
+ * If the response is to be a multicast address, make sure we
+ * are listening on that multicast address.
+ */
+ if (raddr) {
+ if (IN_MULTICAST(ntohl(raddr))) k_join(raddr, lcl_addr);
+ } else k_join(resp_cast, lcl_addr);
+
+ memset(&base, 0, sizeof(base));
+
+ /*
+ * If the destination is on the local net, the last-hop router can
+ * be found by multicast to the all-routers multicast group.
+ * Otherwise, use the group address that is the subject of the
+ * query since by definition the last-hop router will be a member.
+ * Set default TTLs for local remote multicasts.
+ */
+ if (gwy == 0)
+ if ((qdst & dst_netmask) == (lcl_addr & dst_netmask)) tdst = query_cast;
+ else tdst = qgrp;
+ else tdst = gwy;
+ if (tdst == 0 && qgrp == 0)
+ errx(1, "mtrace: weak mtrace requires -g if destination is not local.\n");
+
+ if (IN_MULTICAST(ntohl(tdst))) {
+ k_set_loop(1); /* If I am running on a router, I need to hear this */
+ if (tdst == query_cast) k_set_ttl(qttl ? qttl : 1);
+ else k_set_ttl(qttl ? qttl : MULTICAST_TTL1);
+ }
+
+ /*
+ * Try a query at the requested number of hops or MAXHOPS if unspecified.
+ */
+ if (qno == 0) {
+ hops = MAXHOPS;
+ tries = 1;
+ printf("Querying full reverse path... ");
+ fflush(stdout);
+ } else {
+ hops = qno;
+ tries = nqueries;
+ if (fflag)
+ printf("Querying full reverse path, starting at hop %d...", qno);
+ else
+ printf("Querying reverse path, maximum %d hops... ", qno);
+ fflush(stdout);
+ }
+ base.rtime = 0;
+ base.len = 0;
+ hopbyhop = FALSE;
+
+ recvlen = send_recv(tdst, IGMP_MTRACE, hops, tries, &base, mtrace_callback);
+
+ /*
+ * If the initial query was successful, print it. Otherwise, if
+ * the query max hop count is the default of zero, loop starting
+ * from one until there is no response for extrahops more hops. The
+ * extra hops allow getting past an mtrace-capable mrouter that can't
+ * send multicast packets because all phyints are disabled.
+ */
+ if (recvlen) {
+ printf("\n 0 ");
+ print_host(qdst);
+ printf("\n");
+ print_trace(1, &base, names);
+ r = base.resps + base.len - 1;
+ if (r->tr_rflags == TR_OLD_ROUTER || r->tr_rflags == TR_NO_SPACE ||
+ (qno != 0 && r->tr_rmtaddr != 0 && !fflag)) {
+ printf("%3d ", -(base.len+1));
+ what_kind(&base, r->tr_rflags == TR_OLD_ROUTER ?
+ "doesn't support mtrace"
+ : "is the next hop");
+ } else {
+ if (fflag) {
+ nexthop = hops = qno;
+ goto continuehop;
+ }
+ VAL_TO_MASK(smask, r->tr_smask);
+ if ((r->tr_inaddr & smask) == (qsrc & smask)) {
+ printf("%3d ", -(base.len+1));
+ print_host(qsrc);
+ printf("\n");
+ }
+ }
+ } else if (qno == 0) {
+ hopbyhop = TRUE;
+ printf("switching to hop-by-hop:\n 0 ");
+ print_host(qdst);
+ printf("\n");
+
+ for (hops = 1, nexthop = 1; hops <= MAXHOPS; ++hops) {
+ printf("%3d ", -hops);
+ fflush(stdout);
+
+ /*
+ * After a successful first hop, try switching to the unicast
+ * address of the last-hop router instead of multicasting the
+ * trace query. This should be safe for mrouted versions 3.3
+ * and 3.5 because there is a long route timeout with metric
+ * infinity before a route disappears. Switching to unicast
+ * reduces the amount of multicast traffic and avoids a bug
+ * with duplicate suppression in mrouted 3.5.
+ */
+ if (hops == 2 && gwy == 0 && lastout != 0 &&
+ (recvlen = send_recv(lastout, IGMP_MTRACE, hops, 1, &base, mtrace_callback)))
+ tdst = lastout;
+ else recvlen = send_recv(tdst, IGMP_MTRACE, hops, nqueries, &base, mtrace_callback);
+
+ if (recvlen == 0) {
+ /*if (hops == 1) break;*/
+ if (hops == nexthop) {
+ if (hops == 1) {
+ printf("\n");
+ } else if (what_kind(&base, "didn't respond")) {
+ /* the ask_neighbors determined that the
+ * not-responding router is the first-hop. */
+ break;
+ }
+ if (extrahops == 0)
+ break;
+ } else if (hops < nexthop + extrahops) {
+ printf("\n");
+ } else {
+ printf("...giving up\n");
+ break;
+ }
+ continue;
+ }
+ if (base.len == hops &&
+ (hops == 1 || (base.resps+nexthop-2)->tr_outaddr == lastout)) {
+ if (hops == nexthop) {
+ print_trace(-hops, &base, names);
+ } else {
+ printf("\nResuming...\n");
+ print_trace(nexthop, &base, names);
+ }
+ } else {
+ if (base.len < hops) {
+ /*
+ * A shorter trace than requested means a fatal error
+ * occurred along the path, or that the route changed
+ * to a shorter one.
+ *
+ * If the trace is longer than the last one we received,
+ * then we are resuming from a skipped router (but there
+ * is still probably a problem).
+ *
+ * If the trace is shorter than the last one we
+ * received, then the route must have changed (and
+ * there is still probably a problem).
+ */
+ if (nexthop <= base.len) {
+ printf("\nResuming...\n");
+ print_trace(nexthop, &base, names);
+ } else if (nexthop > base.len + 1) {
+ hops = base.len;
+ printf("\nRoute must have changed...\n");
+ print_trace(1, &base, names);
+ }
+ } else {
+ /*
+ * The last hop address is not the same as it was.
+ * If we didn't know the last hop then we just
+ * got the first response from a hop-by-hop trace;
+ * if we did know the last hop then
+ * the route probably changed underneath us.
+ */
+ hops = base.len;
+ if (lastout != 0)
+ printf("\nRoute must have changed...\n");
+ else
+ printf("\nResuming...\n");
+ print_trace(1, &base, names);
+ }
+ }
+continuehop:
+ r = base.resps + base.len - 1;
+ lastout = r->tr_outaddr;
+
+ if (base.len < hops ||
+ r->tr_rmtaddr == 0 ||
+ (r->tr_rflags & 0x80)) {
+ VAL_TO_MASK(smask, r->tr_smask);
+ if (r->tr_rmtaddr) {
+ if (hops != nexthop) {
+ printf("\n%3d ", -(base.len+1));
+ }
+ what_kind(&base, r->tr_rflags == TR_OLD_ROUTER ?
+ "doesn't support mtrace" :
+ "would be the next hop");
+ /* XXX could do segmented trace if TR_NO_SPACE */
+ } else if (r->tr_rflags == TR_NO_ERR &&
+ (r->tr_inaddr & smask) == (qsrc & smask)) {
+ printf("%3d ", -(hops + 1));
+ print_host(qsrc);
+ printf("\n");
+ }
+ break;
+ }
+
+ nexthop = hops + 1;
+ }
+ }
+
+ if (base.rtime == 0) {
+ printf("Timed out receiving responses\n");
+ if (IN_MULTICAST(ntohl(tdst)))
+ if (tdst == query_cast)
+ printf("Perhaps no local router has a route for source %s\n",
+ inet_fmt(qsrc, s1));
+ else
+ printf("Perhaps receiver %s is not a member of group %s,\n\
+or no router local to it has a route for source %s,\n\
+or multicast at ttl %d doesn't reach its last-hop router for that source\n",
+ inet_fmt(qdst, s2), inet_fmt(qgrp, s3), inet_fmt(qsrc, s1),
+ qttl ? qttl : MULTICAST_TTL1);
+ exit(1);
+ }
+
+ printf("Round trip time %d ms; ", t_diff(base.rtime, base.qtime));
+ {
+ struct tr_resp *n = base.resps + base.len - 1;
+ u_int ttl = n->tr_fttl + 1;
+
+ rno = base.len - 1;
+ while (--rno > 0) {
+ --n;
+ ttl = MaX(ttl, MaX(1, n->tr_fttl) + base.len - rno);
+ }
+ printf("total ttl of %d required.\n\n",ttl);
+ }
+
+ /*
+ * Use the saved response which was the longest one received,
+ * and make additional probes after delay to measure loss.
+ */
+ raddr = base.qhdr.tr_raddr;
+ rttl = TR_GETTTL(base.qhdr.tr_rttlqid);
+ gettimeofday(&tv, 0);
+ waittime = statint - (((tv.tv_sec + JAN_1970) & 0xFFFF) - (base.qtime >> 16));
+ prev = &base;
+ new = &incr[numstats&1];
+
+ /*
+ * Zero out bug-avoidance counters
+ */
+ memset(bugs, 0, sizeof(bugs));
+
+ if (!printstats)
+ printf("Monitoring path..");
+
+ while (numstats--) {
+ if (waittime < 1) printf("\n");
+ else {
+ if (printstats && (lossthresh == 0 || printed)) {
+ printf("Waiting to accumulate statistics...");
+ } else {
+ printf(".");
+ }
+ fflush(stdout);
+ sleep((unsigned)waittime);
+ }
+ printed = 0;
+ rno = hopbyhop ? base.len : qno ? qno : MAXHOPS;
+ recvlen = send_recv(tdst, IGMP_MTRACE, rno, nqueries, new, mtrace_callback);
+
+ if (recvlen == 0) {
+ printf("Timed out.\n");
+ if (numstats) {
+ numstats++;
+ continue;
+ } else
+ exit(1);
+ }
+
+ if (base.len != new->len || path_changed(&base, new)) {
+ printf("%s", base.len == new->len ? "Route changed" :
+ "Trace length doesn't match");
+ if (!printstats)
+ printf(" after %d seconds",
+ (int)((new->qtime - base.qtime) >> 16));
+ printf(":\n");
+printandcontinue:
+ print_trace(1, new, names);
+ numstats++;
+ bcopy(new, &base, sizeof(base));
+ nexthop = hops = new->len;
+ printf("Continuing with hop-by-hop...\n");
+ goto continuehop;
+ }
+
+ if (printstats) {
+ if (new->igmp.igmp_group.s_addr != qgrp ||
+ new->qhdr.tr_src != qsrc || new->qhdr.tr_dst != qdst)
+ printf("\nWARNING: trace modified en route; statistics may be incorrect\n");
+ fixup_stats(&base, prev, new, bugs);
+ if ((lossthresh == 0) || check_thresh(lossthresh, &base, prev, new)) {
+ printf("Results after %d seconds",
+ (int)((new->qtime - base.qtime) >> 16));
+ if (lossthresh)
+ printf(" (this trace %d seconds)",
+ (int)((new->qtime - prev->qtime) >> 16));
+ if (verbose) {
+ time_t t = time(0);
+ struct tm *qr = localtime(&t);
+
+ printf(" qid 0x%06x at %2d:%02d:%02d",
+ TR_GETQID(base.qhdr.tr_rttlqid),
+ qr->tm_hour, qr->tm_min, qr->tm_sec);
+ }
+ printf(":\n\n");
+ printed = 1;
+ if (print_stats(&base, prev, new, bugs, names)) {
+ printf("This should have been detected earlier, but ");
+ printf("Route changed:\n");
+ goto printandcontinue;
+ }
+ }
+ }
+ prev = new;
+ new = &incr[numstats&1];
+ waittime = statint;
+ }
+
+ /*
+ * If the response was multicast back, leave the group
+ */
+ if (raddr) {
+ if (IN_MULTICAST(ntohl(raddr))) k_leave(raddr, lcl_addr);
+ } else k_leave(resp_cast, lcl_addr);
+
+ return (0);
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n%s\n",
+ "usage: mtrace [-MUOPTWVlnpvs] [-e extra_hops] [-f first_hop] [-i if_addr]",
+ " [-g gateway] [-m max_hops] [-q nqueries] [-r resp_dest]",
+ " [-S statint] [-t ttl] [-w wait] source [receiver] [group]");
+ exit(1);
+}
+
+void
+check_vif_state()
+{
+ log(LOG_WARNING, errno, "sendto");
+}
+
+/*
+ * Log errors and other messages to stderr, according to the severity
+ * of the message and the current debug level. For errors of severity
+ * LOG_ERR or worse, terminate the program.
+ */
+#ifdef __STDC__
+void
+log(int severity, int syserr, char *format, ...)
+{
+ va_list ap;
+ char fmt[100];
+
+ va_start(ap, format);
+#else
+/*VARARGS3*/
+void
+log(severity, syserr, format, va_alist)
+ int severity, syserr;
+ char *format;
+ va_dcl
+{
+ va_list ap;
+ char fmt[100];
+
+ va_start(ap);
+#endif
+
+ switch (debug) {
+ case 0: if (severity > LOG_WARNING) return;
+ case 1: if (severity > LOG_NOTICE) return;
+ case 2: if (severity > LOG_INFO ) return;
+ default:
+ fmt[0] = '\0';
+ if (severity == LOG_WARNING)
+ strcpy(fmt, "warning - ");
+ strncat(fmt, format, sizeof(fmt)-strlen(fmt));
+ fmt[sizeof(fmt)-1]='\0';
+ vfprintf(stderr, fmt, ap);
+ if (syserr == 0)
+ fprintf(stderr, "\n");
+ else if (syserr < sys_nerr)
+ fprintf(stderr, ": %s\n", sys_errlist[syserr]);
+ else
+ fprintf(stderr, ": errno %d\n", syserr);
+ }
+ if (severity <= LOG_ERR) exit(1);
+}
diff --git a/usr.sbin/mrouted/mtrace.h b/usr.sbin/mrouted/mtrace.h
new file mode 100644
index 0000000..e67dbd0
--- /dev/null
+++ b/usr.sbin/mrouted/mtrace.h
@@ -0,0 +1,89 @@
+/*
+ * Multicast traceroute related definitions
+ *
+ * mtrace.h,v 5.2 1998/12/04 04:48:21 fenner Exp
+ */
+
+/*
+ * NetBSD renamed the mtrace packet types.
+ */
+#if !defined(IGMP_MTRACE_RESP) && defined(IGMP_MTRACE_REPLY)
+#define IGMP_MTRACE_RESP IGMP_MTRACE_REPLY
+#define IGMP_MTRACE IGMP_MTRACE_QUERY
+#endif
+
+/*
+ * The packet format for a traceroute request.
+ */
+struct tr_query {
+ u_int32 tr_src; /* traceroute source */
+ u_int32 tr_dst; /* traceroute destination */
+ u_int32 tr_raddr; /* traceroute response address */
+ u_int32 tr_rttlqid; /* response ttl and qid */
+};
+
+#define TR_SETTTL(x, ttl) (x = (x & 0x00ffffff) | ((ttl) << 24))
+#define TR_GETTTL(x) (((x) >> 24) & 0xff)
+#define TR_SETQID(x, qid) (x = (x & 0xff000000) | ((qid) & 0x00ffffff))
+#define TR_GETQID(x) ((x) & 0x00ffffff)
+
+/*
+ * Traceroute response format. A traceroute response has a tr_query at the
+ * beginning, followed by one tr_resp for each hop taken.
+ */
+struct tr_resp {
+ u_int32 tr_qarr; /* query arrival time */
+ u_int32 tr_inaddr; /* incoming interface address */
+ u_int32 tr_outaddr; /* outgoing interface address */
+ u_int32 tr_rmtaddr; /* parent address in source tree */
+ u_int32 tr_vifin; /* input packet count on interface */
+ u_int32 tr_vifout; /* output packet count on interface */
+ u_int32 tr_pktcnt; /* total incoming packets for src-grp */
+ u_char tr_rproto; /* routing protocol deployed on router */
+ u_char tr_fttl; /* ttl required to forward on outvif */
+ u_char tr_smask; /* subnet mask for src addr */
+ u_char tr_rflags; /* forwarding error codes */
+};
+
+/* defs within mtrace */
+#define QLEN sizeof(struct tr_query)
+#define RLEN sizeof(struct tr_resp)
+
+/* fields for tr_rflags (forwarding error codes) */
+#define TR_NO_ERR 0
+#define TR_WRONG_IF 1
+#define TR_PRUNED 2
+#define TR_OPRUNED 3
+#define TR_SCOPED 4
+#define TR_NO_RTE 5
+#define TR_NO_FWD 7
+#define TR_HIT_RP 8
+#define TR_RPF_IF 9
+#define TR_NO_MULTI 10
+#define TR_NO_SPACE 0x81
+#define TR_OLD_ROUTER 0x82
+#define TR_ADMIN_PROHIB 0x83
+
+/* fields for tr_rproto (routing protocol) */
+#define PROTO_DVMRP 1
+#define PROTO_MOSPF 2
+#define PROTO_PIM 3
+#define PROTO_CBT 4
+#define PROTO_PIM_SPECIAL 5
+#define PROTO_PIM_STATIC 6
+#define PROTO_DVMRP_STATIC 7
+#define PROTO_PIM_BGP4PLUS 8
+#define PROTO_CBT_SPECIAL 9
+#define PROTO_CBT_STATIC 10
+#define PROTO_PIM_ASSERT 11
+
+#define VAL_TO_MASK(x, i) { \
+ x = htonl(~((1 << (32 - (i))) - 1)); \
+ };
+
+#if defined(__STDC__) || defined(__GNUC__)
+#define JAN_1970 2208988800UL /* 1970 - 1900 in seconds */
+#else
+#define JAN_1970 2208988800L /* 1970 - 1900 in seconds */
+#define const /**/
+#endif
diff --git a/usr.sbin/mrouted/mtrace/Makefile b/usr.sbin/mrouted/mtrace/Makefile
new file mode 100644
index 0000000..ee9adb7
--- /dev/null
+++ b/usr.sbin/mrouted/mtrace/Makefile
@@ -0,0 +1,13 @@
+# $FreeBSD$
+
+S= ${.CURDIR}/..
+.PATH: $S
+
+PROG= mtrace
+MAN= mtrace.8
+BINOWN= root
+BINMODE= 4555
+
+CFLAGS+= -I$S
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mrouted/pathnames.h b/usr.sbin/mrouted/pathnames.h
new file mode 100644
index 0000000..d4fb01d
--- /dev/null
+++ b/usr.sbin/mrouted/pathnames.h
@@ -0,0 +1,26 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $FreeBSD$
+ * pathnames.h,v 3.8 1995/11/29 22:36:57 fenner Rel
+ */
+
+#define _PATH_MROUTED_CONF "/etc/mrouted.conf"
+
+#if (defined(BSD) && (BSD >= 199103))
+#define _PATH_MROUTED_PID "/var/run/mrouted.pid"
+#define _PATH_MROUTED_GENID "/var/run/mrouted.genid"
+#define _PATH_MROUTED_DUMP "/var/tmp/mrouted.dump"
+#define _PATH_MROUTED_CACHE "/var/tmp/mrouted.cache"
+#else
+#define _PATH_MROUTED_PID "/etc/mrouted.pid"
+#define _PATH_MROUTED_GENID "/etc/mrouted.genid"
+#define _PATH_MROUTED_DUMP "/usr/tmp/mrouted.dump"
+#define _PATH_MROUTED_CACHE "/usr/tmp/mrouted.cache"
+#endif
diff --git a/usr.sbin/mrouted/prune.c b/usr.sbin/mrouted/prune.c
new file mode 100644
index 0000000..acefaf9
--- /dev/null
+++ b/usr.sbin/mrouted/prune.c
@@ -0,0 +1,2619 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * prune.c,v 3.8.4.59 1998/03/01 02:06:32 fenner Exp
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include "defs.h"
+
+extern int cache_lifetime;
+extern int prune_lifetime;
+extern struct rtentry *routing_table;
+
+extern int phys_vif;
+
+extern int allow_black_holes;
+
+/*
+ * randomize value to obtain a value between .5x and 1.5x
+ * in order to prevent synchronization
+ */
+#ifdef SYSV
+#define JITTERED_VALUE(x) ((x)/2 + (lrand48() % (x)))
+#else
+#define JITTERED_VALUE(x) ((x)/2 + (arc4random() % (x)))
+#endif
+#define CACHE_LIFETIME(x) JITTERED_VALUE(x) /* XXX */
+
+struct gtable *kernel_table; /* ptr to list of kernel grp entries*/
+static struct gtable *kernel_no_route; /* list of grp entries w/o routes */
+struct gtable *gtp; /* pointer for kernel rt entries */
+unsigned int kroutes; /* current number of cache entries */
+
+/****************************************************************************
+ Functions that are local to prune.c
+****************************************************************************/
+static int scoped_addr __P((vifi_t vifi, u_int32 addr));
+static void prun_add_ttls __P((struct gtable *gt));
+static int pruning_neighbor __P((vifi_t vifi, u_int32 addr));
+static int can_mtrace __P((vifi_t vifi, u_int32 addr));
+static struct ptable * find_prune_entry __P((u_int32 vr, struct ptable *pt));
+static void remove_sources __P((struct gtable *gt));
+static void rexmit_prune __P((void *arg));
+static void expire_prune __P((vifi_t vifi, struct gtable *gt));
+static void send_prune __P((struct gtable *gt));
+static void send_graft __P((struct gtable *gt));
+static void send_graft_ack __P((u_int32 src, u_int32 dst,
+ u_int32 origin, u_int32 grp,
+ vifi_t vifi));
+static void update_kernel __P((struct gtable *g));
+
+/*
+ * Updates the ttl values for each vif.
+ */
+static void
+prun_add_ttls(gt)
+ struct gtable *gt;
+{
+ struct uvif *v;
+ vifi_t vifi;
+
+ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+ if (VIFM_ISSET(vifi, gt->gt_grpmems))
+ gt->gt_ttls[vifi] = v->uv_threshold;
+ else
+ gt->gt_ttls[vifi] = 0;
+ }
+}
+
+/*
+ * checks for scoped multicast addresses
+ * XXX I want to make the check of allow_black_holes based on ALLOW_BLACK_HOLES
+ * but macros are not functions.
+ */
+#define GET_SCOPE(gt) { \
+ register vifi_t _i; \
+ VIFM_CLRALL((gt)->gt_scope); \
+ if (allow_black_holes || \
+ (ntohl((gt)->gt_mcastgrp) & 0xff000000) == 0xef000000) \
+ for (_i = 0; _i < numvifs; _i++) \
+ if (scoped_addr(_i, (gt)->gt_mcastgrp)) \
+ VIFM_SET(_i, (gt)->gt_scope); \
+ } \
+ if ((gt)->gt_route == NULL || ((gt)->gt_route->rt_parent != NO_VIF && \
+ VIFM_ISSET((gt)->gt_route->rt_parent, (gt)->gt_scope))) \
+ VIFM_SETALL((gt)->gt_scope);
+
+#define APPLY_SCOPE(gt) VIFM_CLR_MASK((gt)->gt_grpmems, (gt)->gt_scope)
+
+#define GET_MEMBERSHIP(gt, vifi) { \
+ if ((gt)->gt_route && \
+ VIFM_ISSET((vifi), (gt)->gt_route->rt_children) && \
+ (!SUBS_ARE_PRUNED((gt)->gt_route->rt_subordinates, \
+ uvifs[vifi].uv_nbrmap, (gt)->gt_prunes) || \
+ grplst_mem((vifi), (gt)->gt_mcastgrp))) \
+ VIFM_SET((vifi), (gt)->gt_grpmems); \
+ }
+
+static int
+scoped_addr(vifi, addr)
+ vifi_t vifi;
+ u_int32 addr;
+{
+ struct vif_acl *acl;
+
+ for (acl = uvifs[vifi].uv_acl; acl; acl = acl->acl_next)
+ if ((addr & acl->acl_mask) == acl->acl_addr)
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Determine the list of outgoing vifs, based upon
+ * route subordinates, prunes received, and group
+ * memberships.
+ */
+void
+determine_forwvifs(gt)
+ struct gtable *gt;
+{
+ vifi_t i;
+
+ VIFM_CLRALL(gt->gt_grpmems);
+ for (i = 0; i < numvifs; i++) {
+ GET_MEMBERSHIP(gt, i);
+ }
+ GET_SCOPE(gt);
+ APPLY_SCOPE(gt);
+}
+
+/*
+ * Send a prune or a graft if necessary.
+ */
+void
+send_prune_or_graft(gt)
+ struct gtable *gt;
+{
+ if (VIFM_ISEMPTY(gt->gt_grpmems))
+ send_prune(gt);
+ else if (gt->gt_prsent_timer)
+ send_graft(gt);
+}
+
+/*
+ * Determine if mcastgrp has a listener on vifi
+ */
+int
+grplst_mem(vifi, mcastgrp)
+ vifi_t vifi;
+ u_int32 mcastgrp;
+{
+ register struct listaddr *g;
+ register struct uvif *v;
+
+ v = &uvifs[vifi];
+
+ for (g = v->uv_groups; g != NULL; g = g->al_next)
+ if (mcastgrp == g->al_addr)
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Finds the group entry with the specified source and netmask.
+ * If netmask is 0, it uses the route's netmask.
+ *
+ * Returns TRUE if found a match, and the global variable gtp is left
+ * pointing to entry before the found entry.
+ * Returns FALSE if no exact match found, gtp is left pointing to before
+ * the entry in question belongs, or is NULL if the it belongs at the
+ * head of the list.
+ */
+int
+find_src_grp(src, mask, grp)
+ u_int32 src;
+ u_int32 mask;
+ u_int32 grp;
+{
+ struct gtable *gt;
+
+ gtp = NULL;
+ gt = kernel_table;
+ while (gt != NULL) {
+ if (grp == gt->gt_mcastgrp &&
+ (mask ? (gt->gt_route->rt_origin == src &&
+ gt->gt_route->rt_originmask == mask) :
+ ((src & gt->gt_route->rt_originmask) ==
+ gt->gt_route->rt_origin)))
+ return TRUE;
+ if (ntohl(grp) > ntohl(gt->gt_mcastgrp) ||
+ (grp == gt->gt_mcastgrp &&
+ (ntohl(mask) < ntohl(gt->gt_route->rt_originmask) ||
+ (mask == gt->gt_route->rt_originmask &&
+ (ntohl(src) > ntohl(gt->gt_route->rt_origin)))))) {
+ gtp = gt;
+ gt = gt->gt_gnext;
+ }
+ else break;
+ }
+ return FALSE;
+}
+
+/*
+ * Check if the neighbor supports pruning
+ */
+static int
+pruning_neighbor(vifi, addr)
+ vifi_t vifi;
+ u_int32 addr;
+{
+ struct listaddr *n = neighbor_info(vifi, addr);
+ int vers;
+
+ if (n == NULL)
+ return 0;
+
+ vers = NBR_VERS(n);
+ return (vers >= 0x0300 && ((vers & 0xff00) != 0x0a00));
+}
+
+/*
+ * Can the neighbor in question handle multicast traceroute?
+ */
+static int
+can_mtrace(vifi, addr)
+ vifi_t vifi;
+ u_int32 addr;
+{
+ struct listaddr *n = neighbor_info(vifi, addr);
+ int vers;
+
+ if (n == NULL)
+ return 1; /* fail "safe" */
+
+ vers = NBR_VERS(n);
+ return (vers >= 0x0303 && ((vers & 0xff00) != 0x0a00));
+}
+
+/*
+ * Returns the prune entry of the router, or NULL if none exists
+ */
+static struct ptable *
+find_prune_entry(vr, pt)
+ u_int32 vr;
+ struct ptable *pt;
+{
+ while (pt) {
+ if (pt->pt_router == vr)
+ return pt;
+ pt = pt->pt_next;
+ }
+
+ return NULL;
+}
+
+/*
+ * Remove all the sources hanging off the group table entry from the kernel
+ * cache. Remember the packet counts wherever possible, to keep the mtrace
+ * counters consistent. This prepares for possible prune retransmission,
+ * either on a multi-access network or when a prune that we sent upstream
+ * has expired.
+ */
+static void
+remove_sources(gt)
+ struct gtable *gt;
+{
+ struct stable *st;
+ struct sioc_sg_req sg_req;
+
+ sg_req.grp.s_addr = gt->gt_mcastgrp;
+
+ /*
+ * call k_del_rg() on every one of the gt->gt_srctbl entries
+ * but first save the packet count so that the mtrace packet
+ * counters can remain approximately correct. There's a race
+ * here but it's minor.
+ */
+ for (st = gt->gt_srctbl; st; st = st->st_next) {
+ if (st->st_ctime == 0)
+ continue;
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "rexmit_prune deleting (%s %s) (next is %d sec)",
+ inet_fmt(st->st_origin, s1),
+ inet_fmt(gt->gt_mcastgrp, s2),
+ gt->gt_prune_rexmit);
+ sg_req.src.s_addr = st->st_origin;
+ if (ioctl(udp_socket, SIOCGETSGCNT, (char *)&sg_req) < 0) {
+ sg_req.pktcnt = 0;
+ }
+ k_del_rg(st->st_origin, gt);
+ st->st_ctime = 0; /* flag that it's not in the kernel any more */
+ st->st_savpkt += sg_req.pktcnt;
+ kroutes--;
+ }
+
+ /*
+ * Now, add_table_entry will prune when asked to add a cache entry.
+ */
+}
+
+/*
+ * Prepare for possible prune retransmission
+ */
+static void
+rexmit_prune(arg)
+ void *arg;
+{
+ struct gtable *gt = *(struct gtable **)arg;
+
+ free(arg);
+
+ gt->gt_rexmit_timer = 0;
+
+ /* Make sure we're still not forwarding traffic */
+ if (!VIFM_ISEMPTY(gt->gt_grpmems)) {
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "rexmit_prune (%s %s): gm:%x",
+ RT_FMT(gt->gt_route, s1), inet_fmt(gt->gt_mcastgrp, s2),
+ gt->gt_grpmems);
+ return;
+ }
+
+ remove_sources(gt);
+}
+
+/*
+ * Send a prune message to the dominant router for
+ * this source.
+ *
+ * Record an entry that a prune was sent for this group
+ */
+static void
+send_prune(gt)
+ struct gtable *gt;
+{
+ struct ptable *pt;
+ char *p;
+ int i;
+ int datalen;
+ u_int32 dst;
+ u_int32 tmp;
+ int rexmitting = 0;
+ struct uvif *v;
+
+ /*
+ * Can't process a prune if we don't have an associated route
+ * or if the route points to a local interface.
+ */
+ if (gt->gt_route == NULL || gt->gt_route->rt_parent == NO_VIF ||
+ gt->gt_route->rt_gateway == 0)
+ return;
+
+ /* Don't send a prune to a non-pruning router */
+ if (!pruning_neighbor(gt->gt_route->rt_parent, gt->gt_route->rt_gateway))
+ return;
+
+ v = &uvifs[gt->gt_route->rt_parent];
+ /*
+ * sends a prune message to the router upstream.
+ */
+#if 0
+ dst = v->uv_flags & VIFF_TUNNEL ? dvmrp_group : gt->gt_route->rt_gateway; /*XXX*/
+#else
+ dst = gt->gt_route->rt_gateway;
+#endif
+
+ p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
+ datalen = 0;
+
+ /*
+ * determine prune lifetime, if this isn't a retransmission.
+ *
+ * Use interface-specified lifetime if there is one.
+ */
+ if (gt->gt_prsent_timer == 0) {
+ int l = prune_lifetime;
+
+ if (v->uv_prune_lifetime != 0)
+ l = v->uv_prune_lifetime;
+
+ gt->gt_prsent_timer = JITTERED_VALUE(l);
+ for (pt = gt->gt_pruntbl; pt; pt = pt->pt_next)
+ if (pt->pt_timer < gt->gt_prsent_timer)
+ gt->gt_prsent_timer = pt->pt_timer;
+ } else if (gt->gt_prsent_timer < 0) {
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "asked to rexmit? (%s,%s)/%d on vif %d to %s with negative time",
+ RT_FMT(gt->gt_route, s1), inet_fmt(gt->gt_mcastgrp, s2),
+ gt->gt_prsent_timer, gt->gt_route->rt_parent,
+ inet_fmt(gt->gt_route->rt_gateway, s3));
+ return;
+ } else
+ rexmitting = 1;
+
+ if (rexmitting && !(v->uv_flags & VIFF_REXMIT_PRUNES)) {
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "not rexmitting prune for (%s %s)/%d on vif %d to %s",
+ RT_FMT(gt->gt_route, s1), inet_fmt(gt->gt_mcastgrp, s2),
+ gt->gt_prsent_timer, gt->gt_route->rt_parent,
+ inet_fmt(gt->gt_route->rt_gateway, s3));
+ return;
+ }
+ if (gt->gt_prsent_timer <= MIN_PRUNE_LIFE) {
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "not bothering to send prune for (%s,%s)/%d on vif %d to %s because it's too short",
+ RT_FMT(gt->gt_route, s1), inet_fmt(gt->gt_mcastgrp, s2),
+ gt->gt_prsent_timer, gt->gt_route->rt_parent,
+ inet_fmt(gt->gt_route->rt_gateway, s3));
+ return;
+ }
+
+ /*
+ * If we have a graft pending, cancel graft retransmission
+ */
+ gt->gt_grftsnt = 0;
+
+ for (i = 0; i < 4; i++)
+ *p++ = ((char *)&(gt->gt_route->rt_origin))[i];
+ for (i = 0; i < 4; i++)
+ *p++ = ((char *)&(gt->gt_mcastgrp))[i];
+ tmp = htonl(gt->gt_prsent_timer);
+ for (i = 0; i < 4; i++)
+ *p++ = ((char *)&(tmp))[i];
+ datalen += 12;
+
+ send_on_vif(v, dst, DVMRP_PRUNE, datalen);
+
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "%s prune for (%s %s)/%d on vif %d to %s",
+ rexmitting ? "rexmitted" : "sent",
+ RT_FMT(gt->gt_route, s1), inet_fmt(gt->gt_mcastgrp, s2),
+ gt->gt_prsent_timer, gt->gt_route->rt_parent,
+ inet_fmt(gt->gt_route->rt_gateway, s3));
+
+ if ((v->uv_flags & VIFF_REXMIT_PRUNES) &&
+ gt->gt_rexmit_timer == 0 &&
+ gt->gt_prsent_timer > gt->gt_prune_rexmit) {
+ struct gtable **arg =
+ (struct gtable **)malloc(sizeof (struct gtable **));
+
+ *arg = gt;
+ gt->gt_rexmit_timer = timer_setTimer(
+ JITTERED_VALUE(gt->gt_prune_rexmit),
+ rexmit_prune, arg);
+ gt->gt_prune_rexmit *= 2;
+ }
+}
+
+/*
+ * a prune was sent upstream
+ * so, a graft has to be sent to annul the prune
+ * set up a graft timer so that if an ack is not
+ * heard within that time, another graft request
+ * is sent out.
+ */
+static void
+send_graft(gt)
+ struct gtable *gt;
+{
+ register char *p;
+ register int i;
+ int datalen;
+ u_int32 dst;
+
+ /* Can't send a graft without an associated route */
+ if (gt->gt_route == NULL || gt->gt_route->rt_parent == NO_VIF) {
+ gt->gt_grftsnt = 0;
+ return;
+ }
+
+ gt->gt_prsent_timer = 0;
+ gt->gt_prune_rexmit = PRUNE_REXMIT_VAL;
+ if (gt->gt_rexmit_timer)
+ timer_clearTimer(gt->gt_rexmit_timer);
+
+ if (gt->gt_grftsnt == 0)
+ gt->gt_grftsnt = 1;
+
+#if 0
+ dst = uvifs[gt->gt_route->rt_parent].uv_flags & VIFF_TUNNEL ? dvmrp_group : gt->gt_route->rt_gateway; /*XXX*/
+#else
+ dst = gt->gt_route->rt_gateway;
+#endif
+
+ p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
+ datalen = 0;
+
+ for (i = 0; i < 4; i++)
+ *p++ = ((char *)&(gt->gt_route->rt_origin))[i];
+ for (i = 0; i < 4; i++)
+ *p++ = ((char *)&(gt->gt_mcastgrp))[i];
+ datalen += 8;
+
+ send_on_vif(&uvifs[gt->gt_route->rt_parent], dst, DVMRP_GRAFT, datalen);
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "sent graft for (%s %s) to %s on vif %d",
+ RT_FMT(gt->gt_route, s1), inet_fmt(gt->gt_mcastgrp, s2),
+ inet_fmt(gt->gt_route->rt_gateway, s3), gt->gt_route->rt_parent);
+}
+
+/*
+ * Send an ack that a graft was received
+ */
+static void
+send_graft_ack(src, dst, origin, grp, vifi)
+ u_int32 src;
+ u_int32 dst;
+ u_int32 origin;
+ u_int32 grp;
+ vifi_t vifi;
+{
+ register char *p;
+ register int i;
+ int datalen;
+
+ p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
+ datalen = 0;
+
+ for (i = 0; i < 4; i++)
+ *p++ = ((char *)&(origin))[i];
+ for (i = 0; i < 4; i++)
+ *p++ = ((char *)&(grp))[i];
+ datalen += 8;
+
+ if (vifi == NO_VIF)
+ send_igmp(src, dst, IGMP_DVMRP, DVMRP_GRAFT_ACK,
+ htonl(MROUTED_LEVEL), datalen);
+ else {
+#if 0
+ if (uvifs[vifi].uv_flags & VIFF_TUNNEL)
+ dst = dvmrp_group; /* XXX */
+#endif
+ send_on_vif(&uvifs[vifi], dst, DVMRP_GRAFT_ACK, datalen);
+ }
+
+ IF_DEBUG(DEBUG_PRUNE)
+ if (vifi == NO_VIF)
+ log(LOG_DEBUG, 0, "sent graft ack for (%s, %s) to %s",
+ inet_fmt(origin, s1), inet_fmt(grp, s2), inet_fmt(dst, s3));
+ else
+ log(LOG_DEBUG, 0, "sent graft ack for (%s, %s) to %s on vif %d",
+ inet_fmt(origin, s1), inet_fmt(grp, s2), inet_fmt(dst, s3), vifi);
+}
+
+/*
+ * Update the kernel cache with all the routes hanging off the group entry
+ */
+static void
+update_kernel(g)
+ struct gtable *g;
+{
+ struct stable *st;
+
+ for (st = g->gt_srctbl; st; st = st->st_next)
+ if (st->st_ctime != 0)
+ k_add_rg(st->st_origin, g);
+}
+
+/****************************************************************************
+ Functions that are used externally
+****************************************************************************/
+
+#ifdef SNMP
+#include <sys/types.h>
+#include "snmp.h"
+
+/*
+ * Find a specific group entry in the group table
+ */
+struct gtable *
+find_grp(grp)
+ u_int32 grp;
+{
+ struct gtable *gt;
+
+ for (gt = kernel_table; gt; gt = gt->gt_gnext) {
+ if (ntohl(grp) < ntohl(gt->gt_mcastgrp))
+ break;
+ if (gt->gt_mcastgrp == grp)
+ return gt;
+ }
+ return NULL;
+}
+
+/*
+ * Given a group entry and source, find the corresponding source table
+ * entry
+ */
+struct stable *
+find_grp_src(gt, src)
+ struct gtable *gt;
+ u_int32 src;
+{
+ struct stable *st;
+ u_long grp = gt->gt_mcastgrp;
+ struct gtable *gtcurr;
+
+ for (gtcurr = gt; gtcurr->gt_mcastgrp == grp; gtcurr = gtcurr->gt_gnext) {
+ for (st = gtcurr->gt_srctbl; st; st = st->st_next)
+ if (st->st_origin == src)
+ return st;
+ }
+ return NULL;
+}
+
+/*
+ * Find next entry > specification
+ */
+int
+next_grp_src_mask(gtpp, stpp, grp, src, mask)
+ struct gtable **gtpp; /* ordered by group */
+ struct stable **stpp; /* ordered by source */
+ u_int32 grp;
+ u_int32 src;
+ u_int32 mask;
+{
+ struct gtable *gt, *gbest = NULL;
+ struct stable *st, *sbest = NULL;
+
+ /* Find first group entry >= grp spec */
+ (*gtpp) = kernel_table;
+ while ((*gtpp) && ntohl((*gtpp)->gt_mcastgrp) < ntohl(grp))
+ (*gtpp)=(*gtpp)->gt_gnext;
+ if (!(*gtpp))
+ return 0; /* no more groups */
+
+ for (gt = kernel_table; gt; gt=gt->gt_gnext) {
+ /* Since grps are ordered, we can stop when group changes from gbest */
+ if (gbest && gbest->gt_mcastgrp != gt->gt_mcastgrp)
+ break;
+ for (st = gt->gt_srctbl; st; st=st->st_next) {
+
+ /* Among those entries > spec, find "lowest" one */
+ if (((ntohl(gt->gt_mcastgrp)> ntohl(grp))
+ || (ntohl(gt->gt_mcastgrp)==ntohl(grp)
+ && ntohl(st->st_origin)> ntohl(src))
+ || (ntohl(gt->gt_mcastgrp)==ntohl(grp)
+ && ntohl(st->st_origin)==src && 0xFFFFFFFF>ntohl(mask)))
+ && (!gbest
+ || (ntohl(gt->gt_mcastgrp)< ntohl(gbest->gt_mcastgrp))
+ || (ntohl(gt->gt_mcastgrp)==ntohl(gbest->gt_mcastgrp)
+ && ntohl(st->st_origin)< ntohl(sbest->st_origin)))) {
+ gbest = gt;
+ sbest = st;
+ }
+ }
+ }
+ (*gtpp) = gbest;
+ (*stpp) = sbest;
+ return (*gtpp)!=0;
+}
+
+/*
+ * Ensure that sg contains current information for the given group,source.
+ * This is fetched from the kernel as a unit so that counts for the entry
+ * are consistent, i.e. packet and byte counts for the same entry are
+ * read at the same time.
+ */
+void
+refresh_sg(sg, gt, st)
+ struct sioc_sg_req *sg;
+ struct gtable *gt;
+ struct stable *st;
+{
+ static int lastq = -1;
+
+ if (quantum != lastq || sg->src.s_addr!=st->st_origin
+ || sg->grp.s_addr!=gt->gt_mcastgrp) {
+ lastq = quantum;
+ sg->src.s_addr = st->st_origin;
+ sg->grp.s_addr = gt->gt_mcastgrp;
+ ioctl(udp_socket, SIOCGETSGCNT, (char *)sg);
+ }
+}
+
+/*
+ * Given a routing table entry, and a vifi, find the next entry
+ * equal to or greater than those
+ */
+int
+next_child(gtpp, stpp, grp, src, mask, vifi)
+ struct gtable **gtpp;
+ struct stable **stpp;
+ u_int32 grp;
+ u_int32 src;
+ u_int32 mask;
+ vifi_t *vifi; /* vif at which to start looking */
+{
+ /* Get (G,S,M) entry */
+ if (mask!=0xFFFFFFFF
+ || !((*gtpp) = find_grp(grp))
+ || !((*stpp) = find_grp_src((*gtpp),src)))
+ if (!next_grp_src_mask(gtpp, stpp, grp, src, mask))
+ return 0;
+
+ /* Continue until we get one with a valid next vif */
+ do {
+ for (; (*gtpp)->gt_route->rt_children && *vifi<numvifs; (*vifi)++)
+ if (VIFM_ISSET(*vifi, (*gtpp)->gt_route->rt_children))
+ return 1;
+ *vifi = 0;
+ } while (next_grp_src_mask(gtpp, stpp, (*gtpp)->gt_mcastgrp,
+ (*stpp)->st_origin, 0xFFFFFFFF) );
+
+ return 0;
+}
+#endif /* SNMP */
+
+/*
+ * Initialize the kernel table structure
+ */
+void
+init_ktable()
+{
+ kernel_table = NULL;
+ kernel_no_route = NULL;
+ kroutes = 0;
+}
+
+/*
+ * Add a new table entry for (origin, mcastgrp)
+ */
+void
+add_table_entry(origin, mcastgrp)
+ u_int32 origin;
+ u_int32 mcastgrp;
+{
+ struct rtentry *r;
+ struct gtable *gt,**gtnp,*prev_gt;
+ struct stable *st,**stnp;
+
+ /*
+ * Since we have to enable mrouting to get the version number,
+ * some cache creation requests can sneak through. Ignore them
+ * since we're not going to do useful stuff until we've performed
+ * final initialization.
+ */
+ if (!did_final_init)
+ return;
+
+#ifdef DEBUG_MFC
+ md_log(MD_MISS, origin, mcastgrp);
+#endif
+
+ r = determine_route(origin);
+ prev_gt = NULL;
+ if (r == NULL) {
+ /*
+ * Look for it on the no_route table; if it is found then
+ * it will be detected as a duplicate below.
+ */
+ for (gt = kernel_no_route; gt; gt = gt->gt_next)
+ if (mcastgrp == gt->gt_mcastgrp &&
+ gt->gt_srctbl && gt->gt_srctbl->st_origin == origin)
+ break;
+ gtnp = &kernel_no_route;
+ } else {
+ gtnp = &r->rt_groups;
+ while ((gt = *gtnp) != NULL) {
+ if (gt->gt_mcastgrp >= mcastgrp)
+ break;
+ gtnp = &gt->gt_next;
+ prev_gt = gt;
+ }
+ }
+
+ if (gt == NULL || gt->gt_mcastgrp != mcastgrp) {
+ gt = (struct gtable *)malloc(sizeof(struct gtable));
+ if (gt == NULL)
+ log(LOG_ERR, 0, "ran out of memory");
+
+ gt->gt_mcastgrp = mcastgrp;
+ gt->gt_timer = CACHE_LIFETIME(cache_lifetime);
+ time(&gt->gt_ctime);
+ gt->gt_prsent_timer = 0;
+ gt->gt_grftsnt = 0;
+ gt->gt_srctbl = NULL;
+ gt->gt_pruntbl = NULL;
+ gt->gt_route = r;
+ gt->gt_rexmit_timer = 0;
+ NBRM_CLRALL(gt->gt_prunes);
+ gt->gt_prune_rexmit = PRUNE_REXMIT_VAL;
+#ifdef RSRR
+ gt->gt_rsrr_cache = NULL;
+#endif
+
+ /* Calculate forwarding vifs */
+ determine_forwvifs(gt);
+
+ /* update ttls */
+ prun_add_ttls(gt);
+
+ gt->gt_next = *gtnp;
+ *gtnp = gt;
+ if (gt->gt_next)
+ gt->gt_next->gt_prev = gt;
+ gt->gt_prev = prev_gt;
+
+ if (r) {
+ if (find_src_grp(r->rt_origin, r->rt_originmask, gt->gt_mcastgrp)) {
+ struct gtable *g;
+
+ g = gtp ? gtp->gt_gnext : kernel_table;
+ log(LOG_WARNING, 0, "Entry for (%s %s) (rt:%x) exists (rt:%x)",
+ RT_FMT(r, s1), inet_fmt(g->gt_mcastgrp, s2),
+ r, g->gt_route);
+ } else {
+ if (gtp) {
+ gt->gt_gnext = gtp->gt_gnext;
+ gt->gt_gprev = gtp;
+ gtp->gt_gnext = gt;
+ } else {
+ gt->gt_gnext = kernel_table;
+ gt->gt_gprev = NULL;
+ kernel_table = gt;
+ }
+ if (gt->gt_gnext)
+ gt->gt_gnext->gt_gprev = gt;
+ }
+ } else {
+ gt->gt_gnext = gt->gt_gprev = NULL;
+ }
+ }
+
+ stnp = &gt->gt_srctbl;
+ while ((st = *stnp) != NULL) {
+ if (ntohl(st->st_origin) >= ntohl(origin))
+ break;
+ stnp = &st->st_next;
+ }
+
+ if (st == NULL || st->st_origin != origin) {
+ st = (struct stable *)malloc(sizeof(struct stable));
+ if (st == NULL)
+ log(LOG_ERR, 0, "ran out of memory");
+
+ st->st_origin = origin;
+ st->st_pktcnt = 0;
+ st->st_savpkt = 0;
+ time(&st->st_ctime);
+ st->st_next = *stnp;
+ *stnp = st;
+ } else {
+ if (st->st_ctime == 0) {
+ /* An old source which we're keeping around for statistics */
+ time(&st->st_ctime);
+ } else {
+#ifdef DEBUG_MFC
+ md_log(MD_DUPE, origin, mcastgrp);
+#endif
+ /* Ignore kernel->mrouted retransmissions */
+ if (time(0) - st->st_ctime > 5)
+ log(LOG_WARNING, 0, "kernel entry already exists for (%s %s)",
+ inet_fmt(origin, s1), inet_fmt(mcastgrp, s2));
+ k_add_rg(origin, gt);
+ return;
+ }
+ }
+
+ kroutes++;
+ k_add_rg(origin, gt);
+
+ IF_DEBUG(DEBUG_CACHE)
+ log(LOG_DEBUG, 0, "add cache entry (%s %s) gm:%x, parent-vif:%d",
+ inet_fmt(origin, s1),
+ inet_fmt(mcastgrp, s2),
+ gt->gt_grpmems, r ? r->rt_parent : -1);
+
+ /*
+ * If there are no downstream routers that want traffic for
+ * this group, send (or retransmit) a prune upstream.
+ */
+ if (VIFM_ISEMPTY(gt->gt_grpmems))
+ send_prune(gt);
+}
+
+/*
+ * A router has gone down. Remove prune state pertinent to that router.
+ */
+void
+reset_neighbor_state(vifi, addr)
+ vifi_t vifi;
+ u_int32 addr;
+{
+ struct rtentry *r;
+ struct gtable *g;
+ struct ptable *pt, **ptnp;
+ struct stable *st;
+
+ for (g = kernel_table; g; g = g->gt_gnext) {
+ r = g->gt_route;
+
+ /*
+ * If neighbor was the parent, remove the prune sent state
+ * and all of the source cache info so that prunes get
+ * regenerated.
+ */
+ if (vifi == r->rt_parent) {
+ if (addr == r->rt_gateway) {
+ IF_DEBUG(DEBUG_PEER)
+ log(LOG_DEBUG, 0, "reset_neighbor_state parent reset (%s %s)",
+ RT_FMT(r, s1), inet_fmt(g->gt_mcastgrp, s2));
+
+ g->gt_prsent_timer = 0;
+ g->gt_grftsnt = 0;
+ while ((st = g->gt_srctbl) != NULL) {
+ g->gt_srctbl = st->st_next;
+ if (st->st_ctime != 0) {
+ k_del_rg(st->st_origin, g);
+ kroutes--;
+ }
+ free(st);
+ }
+ }
+ } else {
+ /*
+ * Remove any prunes that this router has sent us.
+ */
+ ptnp = &g->gt_pruntbl;
+ while ((pt = *ptnp) != NULL) {
+ if (pt->pt_vifi == vifi && pt->pt_router == addr) {
+ NBRM_CLR(pt->pt_index, g->gt_prunes);
+ *ptnp = pt->pt_next;
+ free(pt);
+ } else
+ ptnp = &pt->pt_next;
+ }
+
+ /*
+ * And see if we want to forward again.
+ */
+ if (!VIFM_ISSET(vifi, g->gt_grpmems)) {
+ GET_MEMBERSHIP(g, vifi);
+ APPLY_SCOPE(g);
+ prun_add_ttls(g);
+
+ /* Update kernel state */
+ update_kernel(g);
+#ifdef RSRR
+ /* Send route change notification to reservation protocol. */
+ rsrr_cache_send(g,1);
+#endif /* RSRR */
+
+ /*
+ * If removing this prune causes us to start forwarding
+ * (e.g. the neighbor rebooted), and we sent a prune upstream,
+ * send a graft to cancel the prune.
+ */
+ if (!VIFM_ISEMPTY(g->gt_grpmems) && g->gt_prsent_timer)
+ send_graft(g);
+
+ IF_DEBUG(DEBUG_PEER)
+ log(LOG_DEBUG, 0, "reset neighbor state (%s %s) gm:%x",
+ RT_FMT(r, s1),
+ inet_fmt(g->gt_mcastgrp, s2), g->gt_grpmems);
+ }
+ }
+ }
+}
+
+/*
+ * Delete table entry from the kernel
+ * del_flag determines how many entries to delete
+ */
+void
+del_table_entry(r, mcastgrp, del_flag)
+ struct rtentry *r;
+ u_int32 mcastgrp;
+ u_int del_flag;
+{
+ struct gtable *g, *prev_g;
+ struct stable *st, *prev_st;
+ struct ptable *pt, *prev_pt;
+
+ if (del_flag == DEL_ALL_ROUTES) {
+ g = r->rt_groups;
+ while (g) {
+ IF_DEBUG(DEBUG_CACHE)
+ log(LOG_DEBUG, 0, "del_table_entry deleting (%s %s)",
+ RT_FMT(r, s1), inet_fmt(g->gt_mcastgrp, s2));
+ st = g->gt_srctbl;
+ while (st) {
+ if (st->st_ctime != 0) {
+ if (k_del_rg(st->st_origin, g) < 0) {
+ log(LOG_WARNING, errno,
+ "del_table_entry trying to delete (%s, %s)",
+ inet_fmt(st->st_origin, s1),
+ inet_fmt(g->gt_mcastgrp, s2));
+ }
+ kroutes--;
+ }
+ prev_st = st;
+ st = st->st_next;
+ free(prev_st);
+ }
+ g->gt_srctbl = NULL;
+
+ pt = g->gt_pruntbl;
+ while (pt) {
+ prev_pt = pt;
+ pt = pt->pt_next;
+ free(prev_pt);
+ }
+ g->gt_pruntbl = NULL;
+
+ if (g->gt_gnext)
+ g->gt_gnext->gt_gprev = g->gt_gprev;
+ if (g->gt_gprev)
+ g->gt_gprev->gt_gnext = g->gt_gnext;
+ else
+ kernel_table = g->gt_gnext;
+
+#ifdef RSRR
+ /* Send route change notification to reservation protocol. */
+ rsrr_cache_send(g,0);
+ rsrr_cache_clean(g);
+#endif /* RSRR */
+ if (g->gt_rexmit_timer)
+ timer_clearTimer(g->gt_rexmit_timer);
+
+ prev_g = g;
+ g = g->gt_next;
+ free(prev_g);
+ }
+ r->rt_groups = NULL;
+ }
+
+ /*
+ * Dummy routine - someday this may be needed, so it is just there
+ */
+ if (del_flag == DEL_RTE_GROUP) {
+ prev_g = (struct gtable *)&r->rt_groups;
+ for (g = r->rt_groups; g; g = g->gt_next) {
+ if (g->gt_mcastgrp == mcastgrp) {
+ IF_DEBUG(DEBUG_CACHE)
+ log(LOG_DEBUG, 0, "del_table_entry deleting (%s %s)",
+ RT_FMT(r, s1), inet_fmt(g->gt_mcastgrp, s2));
+ st = g->gt_srctbl;
+ while (st) {
+ if (st->st_ctime != 0) {
+ if (k_del_rg(st->st_origin, g) < 0) {
+ log(LOG_WARNING, errno,
+ "del_table_entry trying to delete (%s, %s)",
+ inet_fmt(st->st_origin, s1),
+ inet_fmt(g->gt_mcastgrp, s2));
+ }
+ kroutes--;
+ }
+ prev_st = st;
+ st = st->st_next;
+ free(prev_st);
+ }
+ g->gt_srctbl = NULL;
+
+ pt = g->gt_pruntbl;
+ while (pt) {
+ prev_pt = pt;
+ pt = pt->pt_next;
+ free(prev_pt);
+ }
+ g->gt_pruntbl = NULL;
+
+ if (g->gt_gnext)
+ g->gt_gnext->gt_gprev = g->gt_gprev;
+ if (g->gt_gprev)
+ g->gt_gprev->gt_gnext = g->gt_gnext;
+ else
+ kernel_table = g->gt_gnext;
+
+ if (prev_g != (struct gtable *)&r->rt_groups)
+ g->gt_next->gt_prev = prev_g;
+ else
+ g->gt_next->gt_prev = NULL;
+ prev_g->gt_next = g->gt_next;
+
+ if (g->gt_rexmit_timer)
+ timer_clearTimer(g->gt_rexmit_timer);
+#ifdef RSRR
+ /* Send route change notification to reservation protocol. */
+ rsrr_cache_send(g,0);
+ rsrr_cache_clean(g);
+#endif /* RSRR */
+ free(g);
+ g = prev_g;
+ } else {
+ prev_g = g;
+ }
+ }
+ }
+}
+
+/*
+ * update kernel table entry when a route entry changes
+ */
+void
+update_table_entry(r, old_parent_gw)
+ struct rtentry *r;
+ u_int32 old_parent_gw;
+{
+ struct gtable *g;
+ struct ptable *pt, **ptnp;
+
+ for (g = r->rt_groups; g; g = g->gt_next) {
+ ptnp = &g->gt_pruntbl;
+ /*
+ * Delete prune entries from non-children, or non-subordinates.
+ */
+ while ((pt = *ptnp)) {
+ if (!VIFM_ISSET(pt->pt_vifi, r->rt_children) ||
+ !NBRM_ISSET(pt->pt_index, r->rt_subordinates)) {
+
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "update_table_entry deleting prune for (%s %s) from %s on vif %d -%s%s",
+ RT_FMT(r, s1), inet_fmt(g->gt_mcastgrp, s2),
+ inet_fmt(pt->pt_router, s3), pt->pt_vifi,
+ VIFM_ISSET(pt->pt_vifi, r->rt_children) ? "" : " not a child",
+ NBRM_ISSET(pt->pt_index, r->rt_subordinates) ? "" : " not a subordinate");
+
+ if (!NBRM_ISSET(pt->pt_index, g->gt_prunes)) {
+ log(LOG_WARNING, 0,
+ "gt_prunes lost track of (%s %s) from %s on vif %d",
+ RT_FMT(r, s1), inet_fmt(g->gt_mcastgrp, s2),
+ inet_fmt(pt->pt_router, s3), pt->pt_vifi);
+ }
+
+ NBRM_CLR(pt->pt_index, g->gt_prunes);
+ *ptnp = pt->pt_next;
+ free(pt);
+ continue;
+ }
+ ptnp = &((*ptnp)->pt_next);
+ }
+
+ IF_DEBUG(DEBUG_CACHE)
+ log(LOG_DEBUG, 0, "updating cache entries (%s %s) old gm:%x",
+ RT_FMT(r, s1), inet_fmt(g->gt_mcastgrp, s2),
+ g->gt_grpmems);
+
+ /*
+ * Forget about a prune or graft that we sent previously if we
+ * have a new parent router (since the new parent router will
+ * know nothing about what I sent to the previous parent). The
+ * old parent will forget any prune state it is keeping for us.
+ */
+ if (old_parent_gw != r->rt_gateway) {
+ g->gt_prsent_timer = 0;
+ g->gt_grftsnt = 0;
+ }
+
+ /* Recalculate membership */
+ determine_forwvifs(g);
+ /* send a prune or graft if needed. */
+ send_prune_or_graft(g);
+
+ IF_DEBUG(DEBUG_CACHE)
+ log(LOG_DEBUG, 0, "updating cache entries (%s %s) new gm:%x",
+ RT_FMT(r, s1), inet_fmt(g->gt_mcastgrp, s2),
+ g->gt_grpmems);
+
+ /* update ttls and add entry into kernel */
+ prun_add_ttls(g);
+ update_kernel(g);
+#ifdef RSRR
+ /* Send route change notification to reservation protocol. */
+ rsrr_cache_send(g,1);
+#endif /* RSRR */
+ }
+}
+
+/*
+ * set the forwarding flag for all mcastgrps on this vifi
+ */
+void
+update_lclgrp(vifi, mcastgrp)
+ vifi_t vifi;
+ u_int32 mcastgrp;
+{
+ struct rtentry *r;
+ struct gtable *g;
+
+ IF_DEBUG(DEBUG_MEMBER)
+ log(LOG_DEBUG, 0, "group %s joined on vif %d",
+ inet_fmt(mcastgrp, s1), vifi);
+
+ for (g = kernel_table; g; g = g->gt_gnext) {
+ if (ntohl(mcastgrp) < ntohl(g->gt_mcastgrp))
+ break;
+
+ r = g->gt_route;
+ if (g->gt_mcastgrp == mcastgrp &&
+ VIFM_ISSET(vifi, r->rt_children)) {
+
+ VIFM_SET(vifi, g->gt_grpmems);
+ APPLY_SCOPE(g);
+ if (VIFM_ISEMPTY(g->gt_grpmems))
+ continue;
+
+ prun_add_ttls(g);
+ IF_DEBUG(DEBUG_CACHE)
+ log(LOG_DEBUG, 0, "update lclgrp (%s %s) gm:%x",
+ RT_FMT(r, s1),
+ inet_fmt(g->gt_mcastgrp, s2), g->gt_grpmems);
+
+ update_kernel(g);
+#ifdef RSRR
+ /* Send route change notification to reservation protocol. */
+ rsrr_cache_send(g,1);
+#endif /* RSRR */
+ }
+ }
+}
+
+/*
+ * reset forwarding flag for all mcastgrps on this vifi
+ */
+void
+delete_lclgrp(vifi, mcastgrp)
+ vifi_t vifi;
+ u_int32 mcastgrp;
+{
+ struct gtable *g;
+
+ IF_DEBUG(DEBUG_MEMBER)
+ log(LOG_DEBUG, 0, "group %s left on vif %d",
+ inet_fmt(mcastgrp, s1), vifi);
+
+ for (g = kernel_table; g; g = g->gt_gnext) {
+ if (ntohl(mcastgrp) < ntohl(g->gt_mcastgrp))
+ break;
+
+ if (g->gt_mcastgrp == mcastgrp && VIFM_ISSET(vifi, g->gt_grpmems)) {
+ if (g->gt_route == NULL ||
+ SUBS_ARE_PRUNED(g->gt_route->rt_subordinates,
+ uvifs[vifi].uv_nbrmap, g->gt_prunes)) {
+ VIFM_CLR(vifi, g->gt_grpmems);
+ IF_DEBUG(DEBUG_CACHE)
+ log(LOG_DEBUG, 0, "delete lclgrp (%s %s) gm:%x",
+ RT_FMT(g->gt_route, s1),
+ inet_fmt(g->gt_mcastgrp, s2), g->gt_grpmems);
+
+ prun_add_ttls(g);
+ update_kernel(g);
+#ifdef RSRR
+ /* Send route change notification to reservation protocol. */
+ rsrr_cache_send(g,1);
+#endif /* RSRR */
+
+ /*
+ * If there are no more members of this particular group,
+ * send prune upstream
+ */
+ if (VIFM_ISEMPTY(g->gt_grpmems) && g->gt_route->rt_gateway)
+ send_prune(g);
+ }
+ }
+ }
+}
+
+/*
+ * Takes the prune message received and then strips it to
+ * determine the (src, grp) pair to be pruned.
+ *
+ * Adds the router to the (src, grp) entry then.
+ *
+ * Determines if further packets have to be sent down that vif
+ *
+ * Determines if a corresponding prune message has to be generated
+ */
+void
+accept_prune(src, dst, p, datalen)
+ u_int32 src;
+ u_int32 dst;
+ char *p;
+ int datalen;
+{
+ u_int32 prun_src;
+ u_int32 prun_grp;
+ u_int32 prun_tmr;
+ vifi_t vifi;
+ int i;
+ struct rtentry *r;
+ struct gtable *g;
+ struct ptable *pt;
+
+ if ((vifi = find_vif(src, dst)) == NO_VIF) {
+ log(LOG_INFO, 0,
+ "ignoring prune report from non-neighbor %s",
+ inet_fmt(src, s1));
+ return;
+ }
+
+ /* Check if enough data is present */
+ if (datalen < 12)
+ {
+ log(LOG_WARNING, 0,
+ "non-decipherable prune from %s",
+ inet_fmt(src, s1));
+ return;
+ }
+
+ for (i = 0; i< 4; i++)
+ ((char *)&prun_src)[i] = *p++;
+ for (i = 0; i< 4; i++)
+ ((char *)&prun_grp)[i] = *p++;
+ for (i = 0; i< 4; i++)
+ ((char *)&prun_tmr)[i] = *p++;
+ prun_tmr = ntohl(prun_tmr);
+
+ if (prun_tmr <= MIN_PRUNE_LIFE) {
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "ignoring prune from %s on vif %d for (%s %s)/%d because its lifetime is too short",
+ inet_fmt(src, s1), vifi,
+ inet_fmt(prun_src, s2), inet_fmt(prun_grp, s3), prun_tmr);
+ return;
+ }
+
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "%s on vif %d prunes (%s %s)/%d",
+ inet_fmt(src, s1), vifi,
+ inet_fmt(prun_src, s2), inet_fmt(prun_grp, s3), prun_tmr);
+
+ /*
+ * Find the subnet for the prune
+ */
+ if (find_src_grp(prun_src, 0, prun_grp)) {
+ g = gtp ? gtp->gt_gnext : kernel_table;
+ r = g->gt_route;
+
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "found grp state, (%s %s), metric is %d, children are %x, subords are %08x%08x",
+ RT_FMT(r, s1), inet_fmt(g->gt_mcastgrp, s2), r->rt_metric,
+ r->rt_children, r->rt_subordinates.hi, r->rt_subordinates.lo);
+ if (!VIFM_ISSET(vifi, r->rt_children)) {
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_WARNING, 0, "prune received from non-child %s for (%s %s) (dominant on vif %d is %s)",
+ inet_fmt(src, s1), inet_fmt(prun_src, s2),
+ inet_fmt(prun_grp, s3), vifi,
+ inet_fmt(r->rt_dominants[vifi], s4));
+#ifdef RINGBUFFER
+ printringbuf();
+#endif
+ return;
+ }
+ if (VIFM_ISSET(vifi, g->gt_scope)) {
+ log(LOG_WARNING, 0, "prune received from %s on scoped grp (%s %s)",
+ inet_fmt(src, s1), inet_fmt(prun_src, s2),
+ inet_fmt(prun_grp, s3));
+ return;
+ }
+ if ((pt = find_prune_entry(src, g->gt_pruntbl)) != NULL) {
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "%s %d from %s for (%s %s)/%d %s %d %s %x",
+ "duplicate prune received on vif",
+ vifi, inet_fmt(src, s1), inet_fmt(prun_src, s2),
+ inet_fmt(prun_grp, s3), prun_tmr,
+ "old timer:", pt->pt_timer, "cur gm:", g->gt_grpmems);
+ pt->pt_timer = prun_tmr;
+ } else {
+ struct listaddr *n = neighbor_info(vifi, src);
+
+ if (!n) {
+ log(LOG_WARNING, 0, "Prune from non-neighbor %s on vif %d!?",
+ inet_fmt(src, s1), vifi);
+ return;
+ }
+
+ /* allocate space for the prune structure */
+ pt = (struct ptable *)(malloc(sizeof(struct ptable)));
+ if (pt == NULL)
+ log(LOG_ERR, 0, "pt: ran out of memory");
+
+ pt->pt_vifi = vifi;
+ pt->pt_router = src;
+ pt->pt_timer = prun_tmr;
+
+ pt->pt_next = g->gt_pruntbl;
+ g->gt_pruntbl = pt;
+
+ if (n) {
+ pt->pt_index = n->al_index;
+ NBRM_SET(n->al_index, g->gt_prunes);
+ }
+ }
+
+ /*
+ * check if any more packets need to be sent on the
+ * vif which sent this message
+ */
+ if (SUBS_ARE_PRUNED(r->rt_subordinates,
+ uvifs[vifi].uv_nbrmap, g->gt_prunes) &&
+ !grplst_mem(vifi, prun_grp)) {
+ nbrbitmap_t tmp;
+
+ VIFM_CLR(vifi, g->gt_grpmems);
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "vifnbrs=0x%08x%08x, subord=0x%08x%08x prunes=0x%08x%08x",
+ uvifs[vifi].uv_nbrmap.hi,uvifs[vifi].uv_nbrmap.lo,
+ r->rt_subordinates.hi, r->rt_subordinates.lo,
+ g->gt_prunes.hi, g->gt_prunes.lo);
+ /* XXX debugging */
+ NBRM_COPY(r->rt_subordinates, tmp);
+ NBRM_MASK(tmp, uvifs[vifi].uv_nbrmap);
+ if (!NBRM_ISSETALLMASK(g->gt_prunes, tmp))
+ log(LOG_WARNING, 0, "subordinate error");
+ /* XXX end debugging */
+ IF_DEBUG(DEBUG_PRUNE|DEBUG_CACHE)
+ log(LOG_DEBUG, 0, "prune (%s %s), stop sending on vif %d, gm:%x",
+ RT_FMT(r, s1),
+ inet_fmt(g->gt_mcastgrp, s2), vifi, g->gt_grpmems);
+
+ prun_add_ttls(g);
+ update_kernel(g);
+#ifdef RSRR
+ /* Send route change notification to reservation protocol. */
+ rsrr_cache_send(g,1);
+#endif /* RSRR */
+ }
+
+ /*
+ * check if all the child routers have expressed no interest
+ * in this group and if this group does not exist in the
+ * interface
+ * Send a prune message then upstream
+ */
+ if (VIFM_ISEMPTY(g->gt_grpmems) && r->rt_gateway) {
+ send_prune(g);
+ }
+ } else {
+ /*
+ * There is no kernel entry for this group. Therefore, we can
+ * simply ignore the prune, as we are not forwarding this traffic
+ * downstream.
+ */
+ IF_DEBUG(DEBUG_PRUNE|DEBUG_CACHE)
+ log(LOG_DEBUG, 0, "%s (%s %s)/%d from %s",
+ "prune message received with no kernel entry for",
+ inet_fmt(prun_src, s1), inet_fmt(prun_grp, s2),
+ prun_tmr, inet_fmt(src, s3));
+ return;
+ }
+}
+
+/*
+ * Checks if this mcastgrp is present in the kernel table
+ * If so and if a prune was sent, it sends a graft upwards
+ */
+void
+chkgrp_graft(vifi, mcastgrp)
+ vifi_t vifi;
+ u_int32 mcastgrp;
+{
+ struct rtentry *r;
+ struct gtable *g;
+
+ for (g = kernel_table; g; g = g->gt_gnext) {
+ if (ntohl(mcastgrp) < ntohl(g->gt_mcastgrp))
+ break;
+
+ r = g->gt_route;
+ if (g->gt_mcastgrp == mcastgrp && VIFM_ISSET(vifi, r->rt_children))
+ if (g->gt_prsent_timer) {
+ VIFM_SET(vifi, g->gt_grpmems);
+
+ /*
+ * If the vif that was joined was a scoped vif,
+ * ignore it ; don't graft back
+ */
+ APPLY_SCOPE(g);
+ if (VIFM_ISEMPTY(g->gt_grpmems))
+ continue;
+
+ /* send graft upwards */
+ send_graft(g);
+
+ /* update cache timer*/
+ g->gt_timer = CACHE_LIFETIME(cache_lifetime);
+
+ IF_DEBUG(DEBUG_PRUNE|DEBUG_CACHE)
+ log(LOG_DEBUG, 0, "chkgrp graft (%s %s) gm:%x",
+ RT_FMT(r, s1),
+ inet_fmt(g->gt_mcastgrp, s2), g->gt_grpmems);
+
+ prun_add_ttls(g);
+ update_kernel(g);
+#ifdef RSRR
+ /* Send route change notification to reservation protocol. */
+ rsrr_cache_send(g,1);
+#endif /* RSRR */
+ }
+ }
+}
+
+/* determine the multicast group and src
+ *
+ * if it does, then determine if a prune was sent
+ * upstream.
+ * if prune sent upstream, send graft upstream and send
+ * ack downstream.
+ *
+ * if no prune sent upstream, change the forwarding bit
+ * for this interface and send ack downstream.
+ *
+ * if no entry exists for this group send ack downstream.
+ */
+void
+accept_graft(src, dst, p, datalen)
+ u_int32 src;
+ u_int32 dst;
+ char *p;
+ int datalen;
+{
+ vifi_t vifi;
+ u_int32 graft_src;
+ u_int32 graft_grp;
+ int i;
+ struct rtentry *r;
+ struct gtable *g;
+ struct ptable *pt, **ptnp;
+
+ if (datalen < 8) {
+ log(LOG_WARNING, 0,
+ "received non-decipherable graft from %s",
+ inet_fmt(src, s1));
+ return;
+ }
+
+ for (i = 0; i< 4; i++)
+ ((char *)&graft_src)[i] = *p++;
+ for (i = 0; i< 4; i++)
+ ((char *)&graft_grp)[i] = *p++;
+
+ vifi = find_vif(src, dst);
+ send_graft_ack(dst, src, graft_src, graft_grp, vifi);
+
+ if (vifi == NO_VIF) {
+ log(LOG_INFO, 0,
+ "ignoring graft for (%s %s) from non-neighbor %s",
+ inet_fmt(graft_src, s2), inet_fmt(graft_grp, s3),
+ inet_fmt(src, s1));
+ return;
+ }
+
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "%s on vif %d grafts (%s %s)",
+ inet_fmt(src, s1), vifi,
+ inet_fmt(graft_src, s2), inet_fmt(graft_grp, s3));
+
+ /*
+ * Find the subnet for the graft
+ */
+ if (find_src_grp(graft_src, 0, graft_grp)) {
+ g = gtp ? gtp->gt_gnext : kernel_table;
+ r = g->gt_route;
+
+ if (VIFM_ISSET(vifi, g->gt_scope)) {
+ log(LOG_WARNING, 0, "graft received from %s on scoped grp (%s %s)",
+ inet_fmt(src, s1), inet_fmt(graft_src, s2),
+ inet_fmt(graft_grp, s3));
+ return;
+ }
+
+ ptnp = &g->gt_pruntbl;
+ while ((pt = *ptnp) != NULL) {
+ if ((pt->pt_vifi == vifi) && (pt->pt_router == src)) {
+ NBRM_CLR(pt->pt_index, g->gt_prunes);
+ *ptnp = pt->pt_next;
+ free(pt);
+
+ VIFM_SET(vifi, g->gt_grpmems);
+ IF_DEBUG(DEBUG_PRUNE|DEBUG_CACHE)
+ log(LOG_DEBUG, 0, "accept graft (%s %s) gm:%x",
+ RT_FMT(r, s1),
+ inet_fmt(g->gt_mcastgrp, s2), g->gt_grpmems);
+
+ prun_add_ttls(g);
+ update_kernel(g);
+#ifdef RSRR
+ /* Send route change notification to reservation protocol. */
+ rsrr_cache_send(g,1);
+#endif /* RSRR */
+ break;
+ } else {
+ ptnp = &pt->pt_next;
+ }
+ }
+
+ g->gt_timer = CACHE_LIFETIME(cache_lifetime);
+
+ if (g->gt_prsent_timer)
+ /* send graft upwards */
+ send_graft(g);
+ } else {
+ /*
+ * We have no state for the source and group in question.
+ * This is fine, since we know that we have no prune state, and
+ * grafts are requests to remove prune state.
+ */
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "%s (%s %s) from %s",
+ "graft received with no kernel entry for",
+ inet_fmt(graft_src, s1), inet_fmt(graft_grp, s2),
+ inet_fmt(src, s3));
+ return;
+ }
+}
+
+/*
+ * find out which group is involved first of all
+ * then determine if a graft was sent.
+ * if no graft sent, ignore the message
+ * if graft was sent and the ack is from the right
+ * source, remove the graft timer so that we don't
+ * have send a graft again
+ */
+void
+accept_g_ack(src, dst, p, datalen)
+ u_int32 src;
+ u_int32 dst;
+ char *p;
+ int datalen;
+{
+ struct gtable *g;
+ vifi_t vifi;
+ u_int32 grft_src;
+ u_int32 grft_grp;
+ int i;
+
+ if ((vifi = find_vif(src, dst)) == NO_VIF) {
+ log(LOG_INFO, 0,
+ "ignoring graft ack from non-neighbor %s",
+ inet_fmt(src, s1));
+ return;
+ }
+
+ if (datalen < 0 || datalen > 8) {
+ log(LOG_WARNING, 0,
+ "received non-decipherable graft ack from %s",
+ inet_fmt(src, s1));
+ return;
+ }
+
+ for (i = 0; i< 4; i++)
+ ((char *)&grft_src)[i] = *p++;
+ for (i = 0; i< 4; i++)
+ ((char *)&grft_grp)[i] = *p++;
+
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "%s on vif %d acks graft (%s, %s)",
+ inet_fmt(src, s1), vifi,
+ inet_fmt(grft_src, s2), inet_fmt(grft_grp, s3));
+
+ /*
+ * Find the subnet for the graft ack
+ */
+ if (find_src_grp(grft_src, 0, grft_grp)) {
+ g = gtp ? gtp->gt_gnext : kernel_table;
+ g->gt_grftsnt = 0;
+ } else {
+ log(LOG_WARNING, 0, "%s (%s, %s) from %s",
+ "rcvd graft ack with no kernel entry for",
+ inet_fmt(grft_src, s1), inet_fmt(grft_grp, s2),
+ inet_fmt(src, s3));
+#ifdef RINGBUFFER
+ printringbuf();
+#endif
+ return;
+ }
+}
+
+
+/*
+ * free all prune entries and kernel routes
+ * normally, this should inform the kernel that all of its routes
+ * are going away, but this is only called by restart(), which is
+ * about to call MRT_DONE which does that anyway.
+ */
+void
+free_all_prunes()
+{
+ register struct rtentry *r;
+ register struct gtable *g, *prev_g;
+ register struct stable *s, *prev_s;
+ register struct ptable *p, *prev_p;
+
+ for (r = routing_table; r; r = r->rt_next) {
+ g = r->rt_groups;
+ while (g) {
+ s = g->gt_srctbl;
+ while (s) {
+ prev_s = s;
+ s = s->st_next;
+ free(prev_s);
+ }
+
+ p = g->gt_pruntbl;
+ while (p) {
+ prev_p = p;
+ p = p->pt_next;
+ free(prev_p);
+ }
+
+ prev_g = g;
+ g = g->gt_next;
+ if (prev_g->gt_rexmit_timer)
+ timer_clearTimer(prev_g->gt_rexmit_timer);
+ free(prev_g);
+ }
+ r->rt_groups = NULL;
+ }
+ kernel_table = NULL;
+
+ g = kernel_no_route;
+ while (g) {
+ if (g->gt_srctbl)
+ free(g->gt_srctbl);
+
+ prev_g = g;
+ g = g->gt_next;
+ if (prev_g->gt_rexmit_timer)
+ timer_clearTimer(prev_g->gt_rexmit_timer);
+ free(prev_g);
+ }
+ kernel_no_route = NULL;
+}
+
+/*
+ * When a new route is created, search
+ * a) The less-specific part of the routing table
+ * b) The route-less kernel table
+ * for sources that the new route might want to handle.
+ *
+ * "Inheriting" these sources might be cleanest, but simply deleting
+ * them is easier, and letting the kernel re-request them.
+ */
+void
+steal_sources(rt)
+ struct rtentry *rt;
+{
+ register struct rtentry *rp;
+ register struct gtable *gt, **gtnp;
+ register struct stable *st, **stnp;
+
+ for (rp = rt->rt_next; rp; rp = rp->rt_next) {
+ if (rp->rt_groups == NULL)
+ continue;
+ if ((rt->rt_origin & rp->rt_originmask) == rp->rt_origin) {
+ IF_DEBUG(DEBUG_ROUTE)
+ log(LOG_DEBUG, 0, "Route for %s stealing sources from %s",
+ RT_FMT(rt, s1), RT_FMT(rp, s2));
+ for (gt = rp->rt_groups; gt; gt = gt->gt_next) {
+ stnp = &gt->gt_srctbl;
+ while ((st = *stnp) != NULL) {
+ if ((st->st_origin & rt->rt_originmask) == rt->rt_origin) {
+ IF_DEBUG(DEBUG_ROUTE)
+ log(LOG_DEBUG, 0, "%s stealing (%s %s) from %s",
+ RT_FMT(rt, s1),
+ inet_fmt(st->st_origin, s3),
+ inet_fmt(gt->gt_mcastgrp, s4),
+ RT_FMT(rp, s2));
+ if (st->st_ctime != 0) {
+ if (k_del_rg(st->st_origin, gt) < 0) {
+ log(LOG_WARNING, errno, "%s (%s, %s)",
+ "steal_sources trying to delete",
+ inet_fmt(st->st_origin, s1),
+ inet_fmt(gt->gt_mcastgrp, s2));
+ }
+ kroutes--;
+ }
+ *stnp = st->st_next;
+ free(st);
+ } else {
+ stnp = &st->st_next;
+ }
+ }
+ }
+ }
+ }
+
+ gtnp = &kernel_no_route;
+ while ((gt = *gtnp) != NULL) {
+ if (gt->gt_srctbl && ((gt->gt_srctbl->st_origin & rt->rt_originmask)
+ == rt->rt_origin)) {
+ IF_DEBUG(DEBUG_ROUTE)
+ log(LOG_DEBUG, 0, "%s stealing (%s %s) from %s",
+ RT_FMT(rt, s1),
+ inet_fmt(gt->gt_srctbl->st_origin, s3),
+ inet_fmt(gt->gt_mcastgrp, s4),
+ "no_route table");
+ if (gt->gt_srctbl->st_ctime != 0) {
+ if (k_del_rg(gt->gt_srctbl->st_origin, gt) < 0) {
+ log(LOG_WARNING, errno, "%s (%s %s)",
+ "steal_sources trying to delete",
+ inet_fmt(gt->gt_srctbl->st_origin, s1),
+ inet_fmt(gt->gt_mcastgrp, s2));
+ }
+ kroutes--;
+ }
+ free(gt->gt_srctbl);
+ *gtnp = gt->gt_next;
+ if (gt->gt_next)
+ gt->gt_next->gt_prev = gt->gt_prev;
+ if (gt->gt_rexmit_timer)
+ timer_clearTimer(gt->gt_rexmit_timer);
+ free(gt);
+ } else {
+ gtnp = &gt->gt_next;
+ }
+ }
+}
+
+/*
+ * Advance the timers on all the cache entries.
+ * If there are any entries whose timers have expired,
+ * remove these entries from the kernel cache.
+ */
+void
+age_table_entry()
+{
+ struct rtentry *r;
+ struct gtable *gt, **gtnptr;
+ struct stable *st, **stnp;
+ struct ptable *pt, **ptnp;
+ struct sioc_sg_req sg_req;
+
+ IF_DEBUG(DEBUG_PRUNE|DEBUG_CACHE)
+ log(LOG_DEBUG, 0, "aging forwarding cache entries");
+
+ gtnptr = &kernel_table;
+ while ((gt = *gtnptr) != NULL) {
+ vifi_t i; /* XXX Debugging */
+ int fixit = 0; /* XXX Debugging */
+
+ r = gt->gt_route;
+
+ /* XXX Debugging... */
+ for (i = 0; i < numvifs; i++) {
+ /*
+ * If we're not sending on this vif,
+ * And this group isn't scoped on this vif,
+ * And I'm the parent for this route on this vif,
+ * And there are subordinates on this vif,
+ * And all of the subordinates haven't pruned,
+ * YELL LOUDLY
+ * and remember to fix it up later
+ */
+ if (!VIFM_ISSET(i, gt->gt_grpmems) &&
+ !VIFM_ISSET(i, gt->gt_scope) &&
+ VIFM_ISSET(i, r->rt_children) &&
+ NBRM_ISSETMASK(uvifs[i].uv_nbrmap, r->rt_subordinates) &&
+ !SUBS_ARE_PRUNED(r->rt_subordinates, uvifs[i].uv_nbrmap, gt->gt_prunes)) {
+ log(LOG_WARNING, 0, "(%s %s) is blackholing on vif %d",
+ RT_FMT(r, s1), inet_fmt(gt->gt_mcastgrp, s2), i);
+ fixit = 1;
+ }
+ }
+ if (fixit) {
+ log(LOG_WARNING, 0, "fixing membership for (%s %s) gm:%x",
+ RT_FMT(r, s1), inet_fmt(gt->gt_mcastgrp, s2), gt->gt_grpmems);
+ determine_forwvifs(gt);
+ send_prune_or_graft(gt);
+ log(LOG_WARNING, 0, "fixed membership for (%s %s) gm:%x",
+ RT_FMT(r, s1), inet_fmt(gt->gt_mcastgrp, s2), gt->gt_grpmems);
+#ifdef RINGBUFFER
+ printringbuf();
+#endif
+ }
+ /*DEBUG2*/
+ /* If there are group members,
+ * and there are recent sources,
+ * and we have a route,
+ * and it's not directly connected,
+ * and we haven't sent a prune,
+ * if there are any cache entries in the kernel
+ * [if there aren't we're probably waiting to rexmit],
+ * YELL LOUDLY
+ * and send a prune
+ */
+ if (VIFM_ISEMPTY(gt->gt_grpmems) && gt->gt_srctbl && r && r->rt_gateway && gt->gt_prsent_timer == 0) {
+ for (st = gt->gt_srctbl; st; st = st->st_next)
+ if (st->st_ctime != 0)
+ break;
+ if (st != NULL) {
+ log(LOG_WARNING, 0, "grpmems for (%s %s) is empty but no prune state!", RT_FMT(r, s1), inet_fmt(gt->gt_mcastgrp, s2));
+ send_prune_or_graft(gt);
+#ifdef RINGBUFFER
+ printringbuf();
+#endif
+ }
+ }
+ /* XXX ...Debugging */
+
+ /* advance the timer for the kernel entry */
+ gt->gt_timer -= TIMER_INTERVAL;
+
+ /* decrement prune timer if need be */
+ if (gt->gt_prsent_timer > 0) {
+ gt->gt_prsent_timer -= TIMER_INTERVAL;
+ if (gt->gt_prsent_timer <= 0) {
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "upstream prune tmo (%s %s)",
+ RT_FMT(r, s1),
+ inet_fmt(gt->gt_mcastgrp, s2));
+ gt->gt_prsent_timer = -1;
+ /* Reset the prune retransmission timer to its initial value */
+ gt->gt_prune_rexmit = PRUNE_REXMIT_VAL;
+ }
+ }
+
+ /* retransmit graft with exponential backoff */
+ if (gt->gt_grftsnt) {
+ register int y;
+
+ y = ++gt->gt_grftsnt;
+ while (y && !(y & 1))
+ y >>= 1;
+ if (y == 1)
+ send_graft(gt);
+ }
+
+ /*
+ * Age prunes
+ *
+ * If a prune expires, forward again on that vif.
+ */
+ ptnp = &gt->gt_pruntbl;
+ while ((pt = *ptnp) != NULL) {
+ if ((pt->pt_timer -= TIMER_INTERVAL) <= 0) {
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "expire prune (%s %s) from %s on vif %d",
+ RT_FMT(r, s1),
+ inet_fmt(gt->gt_mcastgrp, s2),
+ inet_fmt(pt->pt_router, s3),
+ pt->pt_vifi);
+ if (gt->gt_prsent_timer > 0) {
+ log(LOG_WARNING, 0, "prune (%s %s) from %s on vif %d expires with %d left on prsent timer",
+ RT_FMT(r, s1),
+ inet_fmt(gt->gt_mcastgrp, s2),
+ inet_fmt(pt->pt_router, s3),
+ pt->pt_vifi, gt->gt_prsent_timer);
+ /* Send a graft to heal the tree. */
+ send_graft(gt);
+ }
+
+ NBRM_CLR(pt->pt_index, gt->gt_prunes);
+ expire_prune(pt->pt_vifi, gt);
+
+ /* remove the router's prune entry and await new one */
+ *ptnp = pt->pt_next;
+ free(pt);
+ } else {
+ ptnp = &pt->pt_next;
+ }
+ }
+
+ /*
+ * If the cache entry has expired, delete source table entries for
+ * silent sources. If there are no source entries left, and there
+ * are no downstream prunes, then the entry is deleted.
+ * Otherwise, the cache entry's timer is refreshed.
+ */
+ if (gt->gt_timer <= 0) {
+ IF_DEBUG(DEBUG_CACHE)
+ log(LOG_DEBUG, 0, "(%s %s) timed out, checking for traffic",
+ RT_FMT(gt->gt_route, s1),
+ inet_fmt(gt->gt_mcastgrp, s2));
+ /* Check for traffic before deleting source entries */
+ sg_req.grp.s_addr = gt->gt_mcastgrp;
+ stnp = &gt->gt_srctbl;
+ while ((st = *stnp) != NULL) {
+ /*
+ * Source entries with no ctime are not actually in the
+ * kernel; they have been removed by rexmit_prune() so
+ * are safe to remove from the list at this point.
+ */
+ if (st->st_ctime) {
+ sg_req.src.s_addr = st->st_origin;
+ if (ioctl(udp_socket, SIOCGETSGCNT, (char *)&sg_req) < 0) {
+ log(LOG_WARNING, errno, "%s (%s %s)",
+ "age_table_entry: SIOCGETSGCNT failing for",
+ inet_fmt(st->st_origin, s1),
+ inet_fmt(gt->gt_mcastgrp, s2));
+ /* Make sure it gets deleted below */
+ sg_req.pktcnt = st->st_pktcnt;
+ }
+ } else {
+ sg_req.pktcnt = st->st_pktcnt;
+ }
+ if (sg_req.pktcnt == st->st_pktcnt) {
+ *stnp = st->st_next;
+ IF_DEBUG(DEBUG_CACHE)
+ log(LOG_DEBUG, 0, "age_table_entry deleting (%s %s)",
+ inet_fmt(st->st_origin, s1),
+ inet_fmt(gt->gt_mcastgrp, s2));
+ if (st->st_ctime != 0) {
+ if (k_del_rg(st->st_origin, gt) < 0) {
+ log(LOG_WARNING, errno,
+ "age_table_entry trying to delete (%s %s)",
+ inet_fmt(st->st_origin, s1),
+ inet_fmt(gt->gt_mcastgrp, s2));
+ }
+ kroutes--;
+ }
+ free(st);
+ } else {
+ st->st_pktcnt = sg_req.pktcnt;
+ stnp = &st->st_next;
+ }
+ }
+
+ /*
+ * Retain the group entry if we have downstream prunes or if
+ * there is at least one source in the list that still has
+ * traffic, or if our upstream prune timer or graft
+ * retransmission timer is running.
+ */
+ if (gt->gt_pruntbl != NULL || gt->gt_srctbl != NULL ||
+ gt->gt_prsent_timer > 0 || gt->gt_grftsnt > 0) {
+ IF_DEBUG(DEBUG_CACHE)
+ log(LOG_DEBUG, 0, "refresh lifetim of cache entry %s%s%s%s(%s, %s)",
+ gt->gt_pruntbl ? "(dstrm prunes) " : "",
+ gt->gt_srctbl ? "(trfc flow) " : "",
+ gt->gt_prsent_timer > 0 ? "(upstrm prune) " : "",
+ gt->gt_grftsnt > 0 ? "(grft rexmit) " : "",
+ RT_FMT(r, s1),
+ inet_fmt(gt->gt_mcastgrp, s2));
+ gt->gt_timer = CACHE_LIFETIME(cache_lifetime);
+ if (gt->gt_prsent_timer == -1) {
+ /*
+ * The upstream prune timed out. Remove any kernel
+ * state.
+ */
+ gt->gt_prsent_timer = 0;
+ if (gt->gt_pruntbl) {
+ log(LOG_WARNING, 0, "upstream prune for (%s %s) expires with downstream prunes active",
+ RT_FMT(r, s1), inet_fmt(gt->gt_mcastgrp, s2));
+ }
+ remove_sources(gt);
+ }
+ gtnptr = &gt->gt_gnext;
+ continue;
+ }
+
+ IF_DEBUG(DEBUG_CACHE)
+ log(LOG_DEBUG, 0, "timeout cache entry (%s, %s)",
+ RT_FMT(r, s1),
+ inet_fmt(gt->gt_mcastgrp, s2));
+
+ if (gt->gt_prev)
+ gt->gt_prev->gt_next = gt->gt_next;
+ else
+ gt->gt_route->rt_groups = gt->gt_next;
+ if (gt->gt_next)
+ gt->gt_next->gt_prev = gt->gt_prev;
+
+ if (gt->gt_gprev) {
+ gt->gt_gprev->gt_gnext = gt->gt_gnext;
+ gtnptr = &gt->gt_gprev->gt_gnext;
+ } else {
+ kernel_table = gt->gt_gnext;
+ gtnptr = &kernel_table;
+ }
+ if (gt->gt_gnext)
+ gt->gt_gnext->gt_gprev = gt->gt_gprev;
+
+#ifdef RSRR
+ /* Send route change notification to reservation protocol. */
+ rsrr_cache_send(gt,0);
+ rsrr_cache_clean(gt);
+#endif /* RSRR */
+ if (gt->gt_rexmit_timer)
+ timer_clearTimer(gt->gt_rexmit_timer);
+
+ free((char *)gt);
+ } else {
+ if (gt->gt_prsent_timer == -1) {
+ /*
+ * The upstream prune timed out. Remove any kernel
+ * state.
+ */
+ gt->gt_prsent_timer = 0;
+ if (gt->gt_pruntbl) {
+ log(LOG_WARNING, 0, "upstream prune for (%s %s) expires with downstream prunes active",
+ RT_FMT(r, s1), inet_fmt(gt->gt_mcastgrp, s2));
+ }
+ remove_sources(gt);
+ }
+ gtnptr = &gt->gt_gnext;
+ }
+ }
+
+ /*
+ * When traversing the no_route table, the decision is much easier.
+ * Just delete it if it has timed out.
+ */
+ gtnptr = &kernel_no_route;
+ while ((gt = *gtnptr) != NULL) {
+ /* advance the timer for the kernel entry */
+ gt->gt_timer -= TIMER_INTERVAL;
+
+ if (gt->gt_timer < 0) {
+ if (gt->gt_srctbl) {
+ if (gt->gt_srctbl->st_ctime != 0) {
+ if (k_del_rg(gt->gt_srctbl->st_origin, gt) < 0) {
+ log(LOG_WARNING, errno, "%s (%s %s)",
+ "age_table_entry trying to delete no-route",
+ inet_fmt(gt->gt_srctbl->st_origin, s1),
+ inet_fmt(gt->gt_mcastgrp, s2));
+ }
+ kroutes--;
+ }
+ free(gt->gt_srctbl);
+ }
+ *gtnptr = gt->gt_next;
+ if (gt->gt_next)
+ gt->gt_next->gt_prev = gt->gt_prev;
+
+ if (gt->gt_rexmit_timer)
+ timer_clearTimer(gt->gt_rexmit_timer);
+
+ free((char *)gt);
+ } else {
+ gtnptr = &gt->gt_next;
+ }
+ }
+}
+
+/*
+ * Modify the kernel to forward packets when one or multiple prunes that
+ * were received on the vif given by vifi, for the group given by gt,
+ * have expired.
+ */
+static void
+expire_prune(vifi, gt)
+ vifi_t vifi;
+ struct gtable *gt;
+{
+ /*
+ * No need to send a graft, any prunes that we sent
+ * will expire before any prunes that we have received.
+ * However, in the case that we did make a mistake,
+ * send a graft to compensate.
+ */
+ if (gt->gt_prsent_timer >= MIN_PRUNE_LIFE) {
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "prune expired with %d left on %s",
+ gt->gt_prsent_timer, "prsent_timer");
+ gt->gt_prsent_timer = 0;
+ send_graft(gt);
+ }
+
+ /* modify the kernel entry to forward packets */
+ if (!VIFM_ISSET(vifi, gt->gt_grpmems)) {
+ struct rtentry *rt = gt->gt_route;
+ VIFM_SET(vifi, gt->gt_grpmems);
+ IF_DEBUG(DEBUG_CACHE)
+ log(LOG_DEBUG, 0, "forw again (%s %s) gm:%x vif:%d",
+ RT_FMT(rt, s1),
+ inet_fmt(gt->gt_mcastgrp, s2), gt->gt_grpmems, vifi);
+
+ prun_add_ttls(gt);
+ update_kernel(gt);
+#ifdef RSRR
+ /* Send route change notification to reservation protocol. */
+ rsrr_cache_send(gt,1);
+#endif /* RSRR */
+ }
+}
+
+/*
+ * Print the contents of the cache table on file 'fp2'.
+ */
+void
+dump_cache(fp2)
+ FILE *fp2;
+{
+ register struct rtentry *r;
+ register struct gtable *gt;
+ register struct stable *st;
+ register struct ptable *pt;
+ register vifi_t i;
+ char c;
+ register time_t thyme = time(0);
+
+ fprintf(fp2,
+ "Multicast Routing Cache Table (%d entries)\n%s", kroutes,
+ " Origin Mcast-group CTmr Age Ptmr Rx IVif Forwvifs\n");
+ fprintf(fp2,
+ "<(prunesrc:vif[idx]/tmr) prunebitmap\n%s",
+ ">Source Lifetime SavPkt Pkts Bytes RPFf\n");
+
+ for (gt = kernel_no_route; gt; gt = gt->gt_next) {
+ if (gt->gt_srctbl) {
+ fprintf(fp2, " %-18s %-15s %-8s %-8s - -1 (no route)\n",
+ inet_fmts(gt->gt_srctbl->st_origin, 0xffffffff, s1),
+ inet_fmt(gt->gt_mcastgrp, s2), scaletime(gt->gt_timer),
+ scaletime(thyme - gt->gt_ctime));
+ fprintf(fp2, ">%s\n", inet_fmt(gt->gt_srctbl->st_origin, s1));
+ }
+ }
+
+ for (gt = kernel_table; gt; gt = gt->gt_gnext) {
+ r = gt->gt_route;
+ fprintf(fp2, " %-18s %-15s",
+ RT_FMT(r, s1),
+ inet_fmt(gt->gt_mcastgrp, s2));
+
+ fprintf(fp2, " %-8s", scaletime(gt->gt_timer));
+
+ fprintf(fp2, " %-8s %-8s ", scaletime(thyme - gt->gt_ctime),
+ gt->gt_prsent_timer ? scaletime(gt->gt_prsent_timer) :
+ " -");
+
+ if (gt->gt_prune_rexmit) {
+ int i = gt->gt_prune_rexmit;
+ int n = 0;
+
+ while (i > PRUNE_REXMIT_VAL) {
+ n++;
+ i /= 2;
+ }
+ if (n == 0 && gt->gt_prsent_timer == 0)
+ fprintf(fp2, " -");
+ else
+ fprintf(fp2, "%2d", n);
+ } else {
+ fprintf(fp2, " -");
+ }
+
+ fprintf(fp2, " %2u%c%c", r->rt_parent,
+ gt->gt_prsent_timer ? 'P' :
+ gt->gt_grftsnt ? 'G' : ' ',
+ VIFM_ISSET(r->rt_parent, gt->gt_scope) ? 'B' : ' ');
+
+ for (i = 0; i < numvifs; ++i) {
+ if (VIFM_ISSET(i, gt->gt_grpmems))
+ fprintf(fp2, " %u ", i);
+ else if (VIFM_ISSET(i, r->rt_children) &&
+ NBRM_ISSETMASK(uvifs[i].uv_nbrmap, r->rt_subordinates))
+ fprintf(fp2, " %u%c", i,
+ VIFM_ISSET(i, gt->gt_scope) ? 'b' :
+ SUBS_ARE_PRUNED(r->rt_subordinates,
+ uvifs[i].uv_nbrmap, gt->gt_prunes) ? 'p' : '!');
+ }
+ fprintf(fp2, "\n");
+ if (gt->gt_pruntbl) {
+ fprintf(fp2, "<");
+ c = '(';
+ for (pt = gt->gt_pruntbl; pt; pt = pt->pt_next) {
+ fprintf(fp2, "%c%s:%d[%d]/%d", c, inet_fmt(pt->pt_router, s1),
+ pt->pt_vifi, pt->pt_index, pt->pt_timer);
+ c = ',';
+ }
+ fprintf(fp2, ")");
+ fprintf(fp2, " 0x%08lx%08lx\n",/*XXX*/
+ gt->gt_prunes.hi, gt->gt_prunes.lo);
+ }
+ for (st = gt->gt_srctbl; st; st = st->st_next) {
+ fprintf(fp2, ">%-18s %-8s %6ld", inet_fmt(st->st_origin, s1),
+ st->st_ctime ? scaletime(thyme - st->st_ctime) : "-",
+ st->st_savpkt);
+ if (st->st_ctime) {
+ struct sioc_sg_req sg_req;
+
+ sg_req.src.s_addr = st->st_origin;
+ sg_req.grp.s_addr = gt->gt_mcastgrp;
+ if (ioctl(udp_socket, SIOCGETSGCNT, (char *)&sg_req) < 0) {
+ log(LOG_WARNING, errno, "SIOCGETSGCNT on (%s %s)",
+ inet_fmt(st->st_origin, s1),
+ inet_fmt(gt->gt_mcastgrp, s2));
+ } else {
+ fprintf(fp2, " %8ld %8ld %4ld", sg_req.pktcnt,
+ sg_req.bytecnt, sg_req.wrong_if);
+ }
+ }
+ fprintf(fp2, "\n");
+ }
+ }
+}
+
+/*
+ * Traceroute function which returns traceroute replies to the requesting
+ * router. Also forwards the request to downstream routers.
+ */
+void
+accept_mtrace(src, dst, group, data, no, datalen)
+ u_int32 src;
+ u_int32 dst;
+ u_int32 group;
+ char *data;
+ u_int no; /* promoted u_char */
+ int datalen;
+{
+ u_char type;
+ struct rtentry *rt;
+ struct gtable *gt;
+ struct tr_query *qry;
+ struct tr_resp *resp;
+ int vifi;
+ char *p;
+ int rcount;
+ int errcode = TR_NO_ERR;
+ int resptype;
+ struct timeval tp;
+ struct sioc_vif_req v_req;
+ struct sioc_sg_req sg_req;
+
+ /* Remember qid across invocations */
+ static u_int32 oqid = 0;
+
+ /* timestamp the request/response */
+ gettimeofday(&tp, 0);
+
+ /*
+ * Check if it is a query or a response
+ */
+ if (datalen == QLEN) {
+ type = QUERY;
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0, "Initial traceroute query rcvd from %s to %s",
+ inet_fmt(src, s1), inet_fmt(dst, s2));
+ }
+ else if ((datalen - QLEN) % RLEN == 0) {
+ type = RESP;
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0, "In-transit traceroute query rcvd from %s to %s",
+ inet_fmt(src, s1), inet_fmt(dst, s2));
+ if (IN_MULTICAST(ntohl(dst))) {
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0, "Dropping multicast response");
+ return;
+ }
+ }
+ else {
+ log(LOG_WARNING, 0, "%s from %s to %s",
+ "Non decipherable traceroute request recieved",
+ inet_fmt(src, s1), inet_fmt(dst, s2));
+ return;
+ }
+
+ qry = (struct tr_query *)data;
+
+ /*
+ * if it is a packet with all reports filled, drop it
+ */
+ if ((rcount = (datalen - QLEN)/RLEN) == no) {
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0, "packet with all reports filled in");
+ return;
+ }
+
+ IF_DEBUG(DEBUG_TRACE) {
+ log(LOG_DEBUG, 0, "s: %s g: %s d: %s ", inet_fmt(qry->tr_src, s1),
+ inet_fmt(group, s2), inet_fmt(qry->tr_dst, s3));
+ log(LOG_DEBUG, 0, "rttl: %d rd: %s", qry->tr_rttl,
+ inet_fmt(qry->tr_raddr, s1));
+ log(LOG_DEBUG, 0, "rcount:%d, qid:%06x", rcount, qry->tr_qid);
+ }
+
+ /* determine the routing table entry for this traceroute */
+ rt = determine_route(qry->tr_src);
+ IF_DEBUG(DEBUG_TRACE)
+ if (rt) {
+ log(LOG_DEBUG, 0, "rt parent vif: %d rtr: %s metric: %d",
+ rt->rt_parent, inet_fmt(rt->rt_gateway, s1), rt->rt_metric);
+ log(LOG_DEBUG, 0, "rt origin %s",
+ RT_FMT(rt, s1));
+ } else
+ log(LOG_DEBUG, 0, "...no route");
+
+ /*
+ * Query type packet - check if rte exists
+ * Check if the query destination is a vif connected to me.
+ * and if so, whether I should start response back
+ */
+ if (type == QUERY) {
+ if (oqid == qry->tr_qid) {
+ /*
+ * If the multicast router is a member of the group being
+ * queried, and the query is multicasted, then the router can
+ * recieve multiple copies of the same query. If we have already
+ * replied to this traceroute, just ignore it this time.
+ *
+ * This is not a total solution, but since if this fails you
+ * only get N copies, N <= the number of interfaces on the router,
+ * it is not fatal.
+ */
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0, "ignoring duplicate traceroute packet");
+ return;
+ }
+
+ if (rt == NULL) {
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0, "Mcast traceroute: no route entry %s",
+ inet_fmt(qry->tr_src, s1));
+ if (IN_MULTICAST(ntohl(dst)))
+ return;
+ }
+ vifi = find_vif(qry->tr_dst, 0);
+
+ if (vifi == NO_VIF) {
+ /* The traceroute destination is not on one of my subnet vifs. */
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0, "Destination %s not an interface",
+ inet_fmt(qry->tr_dst, s1));
+ if (IN_MULTICAST(ntohl(dst)))
+ return;
+ errcode = TR_WRONG_IF;
+ } else if (rt != NULL && !VIFM_ISSET(vifi, rt->rt_children)) {
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0, "Destination %s not on forwarding tree for src %s",
+ inet_fmt(qry->tr_dst, s1), inet_fmt(qry->tr_src, s2));
+ if (IN_MULTICAST(ntohl(dst)))
+ return;
+ errcode = TR_WRONG_IF;
+ }
+ }
+ else {
+ /*
+ * determine which interface the packet came in on
+ * RESP packets travel hop-by-hop so this either traversed
+ * a tunnel or came from a directly attached mrouter.
+ */
+ if ((vifi = find_vif(src, dst)) == NO_VIF) {
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0, "Wrong interface for packet");
+ errcode = TR_WRONG_IF;
+ }
+ }
+
+ /* Now that we've decided to send a response, save the qid */
+ oqid = qry->tr_qid;
+
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0, "Sending traceroute response");
+
+ /* copy the packet to the sending buffer */
+ p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
+
+ bcopy(data, p, datalen);
+
+ p += datalen;
+
+ /*
+ * If there is no room to insert our reply, coopt the previous hop
+ * error indication to relay this fact.
+ */
+ if (p + sizeof(struct tr_resp) > send_buf + RECV_BUF_SIZE) {
+ resp = (struct tr_resp *)p - 1;
+ resp->tr_rflags = TR_NO_SPACE;
+ rt = NULL;
+ goto sendit;
+ }
+
+ /*
+ * fill in initial response fields
+ */
+ resp = (struct tr_resp *)p;
+ bzero(resp, sizeof(struct tr_resp));
+ datalen += RLEN;
+
+ resp->tr_qarr = htonl(((tp.tv_sec + JAN_1970) << 16) +
+ ((tp.tv_usec << 10) / 15625));
+
+ resp->tr_rproto = PROTO_DVMRP;
+ resp->tr_outaddr = (vifi == NO_VIF) ? dst : uvifs[vifi].uv_lcl_addr;
+ resp->tr_fttl = (vifi == NO_VIF) ? 0 : uvifs[vifi].uv_threshold;
+ resp->tr_rflags = errcode;
+
+ /*
+ * obtain # of packets out on interface
+ */
+ v_req.vifi = vifi;
+ if (vifi != NO_VIF && ioctl(udp_socket, SIOCGETVIFCNT, (char *)&v_req) >= 0)
+ resp->tr_vifout = htonl(v_req.ocount);
+ else
+ resp->tr_vifout = 0xffffffff;
+
+ /*
+ * fill in scoping & pruning information
+ */
+ if (rt)
+ for (gt = rt->rt_groups; gt; gt = gt->gt_next) {
+ if (gt->gt_mcastgrp >= group)
+ break;
+ }
+ else
+ gt = NULL;
+
+ if (gt && gt->gt_mcastgrp == group) {
+ struct stable *st;
+
+ for (st = gt->gt_srctbl; st; st = st->st_next)
+ if (qry->tr_src == st->st_origin)
+ break;
+
+ sg_req.src.s_addr = qry->tr_src;
+ sg_req.grp.s_addr = group;
+ if (st && st->st_ctime != 0 &&
+ ioctl(udp_socket, SIOCGETSGCNT, (char *)&sg_req) >= 0)
+ resp->tr_pktcnt = htonl(sg_req.pktcnt + st->st_savpkt);
+ else
+ resp->tr_pktcnt = htonl(st ? st->st_savpkt : 0xffffffff);
+
+ if (VIFM_ISSET(vifi, gt->gt_scope))
+ resp->tr_rflags = TR_SCOPED;
+ else if (gt->gt_prsent_timer)
+ resp->tr_rflags = TR_PRUNED;
+ else if (!VIFM_ISSET(vifi, gt->gt_grpmems))
+ if (!NBRM_ISEMPTY(uvifs[vifi].uv_nbrmap) &&
+ SUBS_ARE_PRUNED(rt->rt_subordinates,
+ uvifs[vifi].uv_nbrmap, gt->gt_prunes))
+ resp->tr_rflags = TR_OPRUNED;
+ else
+ resp->tr_rflags = TR_NO_FWD;
+ } else {
+ if ((vifi != NO_VIF && scoped_addr(vifi, group)) ||
+ (rt && scoped_addr(rt->rt_parent, group)))
+ resp->tr_rflags = TR_SCOPED;
+ else if (rt && !VIFM_ISSET(vifi, rt->rt_children))
+ resp->tr_rflags = TR_NO_FWD;
+ }
+
+ /*
+ * if no rte exists, set NO_RTE error
+ */
+ if (rt == NULL) {
+ src = dst; /* the dst address of resp. pkt */
+ resp->tr_inaddr = 0;
+ resp->tr_rflags = TR_NO_RTE;
+ resp->tr_rmtaddr = 0;
+ } else {
+ /* get # of packets in on interface */
+ v_req.vifi = rt->rt_parent;
+ if (ioctl(udp_socket, SIOCGETVIFCNT, (char *)&v_req) >= 0)
+ resp->tr_vifin = htonl(v_req.icount);
+ else
+ resp->tr_vifin = 0xffffffff;
+
+ MASK_TO_VAL(rt->rt_originmask, resp->tr_smask);
+ src = uvifs[rt->rt_parent].uv_lcl_addr;
+ resp->tr_inaddr = src;
+ resp->tr_rmtaddr = rt->rt_gateway;
+ if (!VIFM_ISSET(vifi, rt->rt_children)) {
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0, "Destination %s not on forwarding tree for src %s",
+ inet_fmt(qry->tr_dst, s1), inet_fmt(qry->tr_src, s2));
+ resp->tr_rflags = TR_WRONG_IF;
+ }
+ if (rt->rt_metric >= UNREACHABLE) {
+ resp->tr_rflags = TR_NO_RTE;
+ /* Hack to send reply directly */
+ rt = NULL;
+ }
+ }
+
+sendit:
+ /*
+ * if metric is 1 or no. of reports is 1, send response to requestor
+ * else send to upstream router. If the upstream router can't handle
+ * mtrace, set an error code and send to requestor anyway.
+ */
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0, "rcount:%d, no:%d", rcount, no);
+
+ if ((rcount + 1 == no) || (rt == NULL) || (rt->rt_metric == 1)) {
+ resptype = IGMP_MTRACE_RESP;
+ dst = qry->tr_raddr;
+ } else
+ if (!can_mtrace(rt->rt_parent, rt->rt_gateway)) {
+ dst = qry->tr_raddr;
+ resp->tr_rflags = TR_OLD_ROUTER;
+ resptype = IGMP_MTRACE_RESP;
+ } else {
+ dst = rt->rt_gateway;
+ resptype = IGMP_MTRACE;
+ }
+
+ if (IN_MULTICAST(ntohl(dst))) {
+ /*
+ * Send the reply on a known multicast capable vif.
+ * If we don't have one, we can't source any multicasts anyway.
+ */
+ if (phys_vif != -1) {
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0, "Sending reply to %s from %s",
+ inet_fmt(dst, s1), inet_fmt(uvifs[phys_vif].uv_lcl_addr, s2));
+ k_set_ttl(qry->tr_rttl);
+ send_igmp(uvifs[phys_vif].uv_lcl_addr, dst,
+ resptype, no, group,
+ datalen);
+ k_set_ttl(1);
+ } else
+ log(LOG_INFO, 0, "No enabled phyints -- %s",
+ "dropping traceroute reply");
+ } else {
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0, "Sending %s to %s from %s",
+ resptype == IGMP_MTRACE_RESP ? "reply" : "request on",
+ inet_fmt(dst, s1), inet_fmt(src, s2));
+
+ send_igmp(src, dst,
+ resptype, no, group,
+ datalen);
+ }
+ return;
+}
diff --git a/usr.sbin/mrouted/prune.h b/usr.sbin/mrouted/prune.h
new file mode 100644
index 0000000..375fada
--- /dev/null
+++ b/usr.sbin/mrouted/prune.h
@@ -0,0 +1,152 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $FreeBSD$
+ * prune.h,v 3.8.4.5 1998/02/27 22:45:43 fenner Exp
+ */
+
+/*
+ * Group table
+ *
+ * Each group entry is a member of two doubly-linked lists:
+ *
+ * a) A list hanging off of the routing table entry for this source (rt_groups)
+ * sorted by group address under the routing entry (gt_next, gt_prev)
+ * b) An independent list pointed to by kernel_table, which is a list of
+ * active source,group's (gt_gnext, gt_gprev).
+ *
+ */
+struct gtable {
+ struct gtable *gt_next; /* pointer to the next entry */
+ struct gtable *gt_prev; /* back pointer for linked list */
+ struct gtable *gt_gnext; /* fwd pointer for group list */
+ struct gtable *gt_gprev; /* rev pointer for group list */
+ u_int32 gt_mcastgrp; /* multicast group associated */
+ vifbitmap_t gt_scope; /* scoped interfaces */
+ u_char gt_ttls[MAXVIFS]; /* ttl vector for forwarding */
+ vifbitmap_t gt_grpmems; /* forw. vifs for src, grp */
+ int gt_prsent_timer; /* prune timer for this group */
+ int gt_timer; /* timer for this group entry */
+ time_t gt_ctime; /* time of entry creation */
+ u_char gt_grftsnt; /* graft sent/retransmit timer */
+ nbrbitmap_t gt_prunes; /* bitmap of neighbors who pruned */
+ struct stable *gt_srctbl; /* source table */
+ struct ptable *gt_pruntbl; /* prune table */
+ struct rtentry *gt_route; /* parent route */
+ int gt_rexmit_timer; /* timer for prune retransmission */
+ int gt_prune_rexmit; /* time til prune retransmission */
+#ifdef RSRR
+ struct rsrr_cache *gt_rsrr_cache; /* RSRR cache */
+#endif /* RSRR */
+};
+
+/*
+ * Source table
+ *
+ * When source-based prunes exist, there will be a struct ptable here as well.
+ */
+struct stable
+{
+ struct stable *st_next; /* pointer to the next entry */
+ u_int32 st_origin; /* host origin of multicasts */
+ u_long st_pktcnt; /* packet count for src-grp entry */
+ u_long st_savpkt; /* saved pkt cnt when no krnl entry */
+ time_t st_ctime; /* kernel entry creation time */
+};
+
+/*
+ * structure to store incoming prunes. Can hang off of either group or source.
+ */
+struct ptable
+{
+ struct ptable *pt_next; /* pointer to the next entry */
+ u_int32 pt_router; /* router that sent this prune */
+ vifi_t pt_vifi; /* vif prune received on */
+ int pt_index; /* neighbor index of router */
+ int pt_timer; /* timer for prune */
+};
+
+#define MIN_PRUNE_LIFE TIMER_INTERVAL /* min prune lifetime to bother with */
+
+/*
+ * The packet format for a traceroute request.
+ */
+struct tr_query {
+ u_int32 tr_src; /* traceroute source */
+ u_int32 tr_dst; /* traceroute destination */
+ u_int32 tr_raddr; /* traceroute response address */
+#if defined(BYTE_ORDER) && (BYTE_ORDER == LITTLE_ENDIAN)
+ struct {
+ u_int qid : 24; /* traceroute query id */
+ u_int ttl : 8; /* traceroute response ttl */
+ } q;
+#else
+ struct {
+ u_int ttl : 8; /* traceroute response ttl */
+ u_int qid : 24; /* traceroute query id */
+ } q;
+#endif /* BYTE_ORDER */
+};
+
+#define tr_rttl q.ttl
+#define tr_qid q.qid
+
+/*
+ * Traceroute response format. A traceroute response has a tr_query at the
+ * beginning, followed by one tr_resp for each hop taken.
+ */
+struct tr_resp {
+ u_int32 tr_qarr; /* query arrival time */
+ u_int32 tr_inaddr; /* incoming interface address */
+ u_int32 tr_outaddr; /* outgoing interface address */
+ u_int32 tr_rmtaddr; /* parent address in source tree */
+ u_int32 tr_vifin; /* input packet count on interface */
+ u_int32 tr_vifout; /* output packet count on interface */
+ u_int32 tr_pktcnt; /* total incoming packets for src-grp */
+ u_char tr_rproto; /* routing protocol deployed on router */
+ u_char tr_fttl; /* ttl required to forward on outvif */
+ u_char tr_smask; /* subnet mask for src addr */
+ u_char tr_rflags; /* forwarding error codes */
+};
+
+/* defs within mtrace */
+#define QUERY 1
+#define RESP 2
+#define QLEN sizeof(struct tr_query)
+#define RLEN sizeof(struct tr_resp)
+
+/* fields for tr_rflags (forwarding error codes) */
+#define TR_NO_ERR 0
+#define TR_WRONG_IF 1
+#define TR_PRUNED 2
+#define TR_OPRUNED 3
+#define TR_SCOPED 4
+#define TR_NO_RTE 5
+#define TR_NO_FWD 7
+#define TR_NO_SPACE 0x81
+#define TR_OLD_ROUTER 0x82
+
+/* fields for tr_rproto (routing protocol) */
+#define PROTO_DVMRP 1
+#define PROTO_MOSPF 2
+#define PROTO_PIM 3
+#define PROTO_CBT 4
+
+#define MASK_TO_VAL(x, i) { \
+ u_int32 _x = ntohl(x); \
+ (i) = 1; \
+ while ((_x) <<= 1) \
+ (i)++; \
+ };
+
+#define VAL_TO_MASK(x, i) { \
+ x = i ? htonl(~((1 << (32 - (i))) - 1)) : 0; \
+ };
+
+#define NBR_VERS(n) (((n)->al_pv << 8) + (n)->al_mv)
diff --git a/usr.sbin/mrouted/route.c b/usr.sbin/mrouted/route.c
new file mode 100644
index 0000000..b269539
--- /dev/null
+++ b/usr.sbin/mrouted/route.c
@@ -0,0 +1,1475 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * route.c,v 3.8.4.41 1998/01/15 00:08:34 fenner Exp
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include "defs.h"
+
+/*
+ * This define statement saves a lot of space later
+ */
+#define RT_ADDR (struct rtentry *)&routing_table
+
+/*
+ * Exported variables.
+ */
+int routes_changed; /* 1=>some routes have changed */
+int delay_change_reports; /* 1=>postpone change reports */
+
+
+/*
+ * The routing table is shared with prune.c , so must not be static.
+ */
+struct rtentry *routing_table; /* pointer to list of route entries */
+
+/*
+ * Private variables.
+ */
+static struct rtentry *rtp; /* pointer to a route entry */
+static struct rtentry *rt_end; /* pointer to last route entry */
+unsigned int nroutes; /* current number of route entries */
+
+/*
+ * Private functions.
+ */
+static int init_children_and_leaves __P((struct rtentry *r,
+ vifi_t parent, int first));
+static int find_route __P((u_int32 origin, u_int32 mask));
+static void create_route __P((u_int32 origin, u_int32 mask));
+static void discard_route __P((struct rtentry *prev_r));
+static int compare_rts __P((const void *rt1, const void *rt2));
+static int report_chunk __P((int, struct rtentry *start_rt, vifi_t vifi,
+ u_int32 dst));
+static void queue_blaster_report __P((vifi_t, u_int32, u_int32, char *,
+ int, u_int32));
+static void process_blaster_report __P((void *));
+
+#ifdef SNMP
+#include <sys/types.h>
+#include "snmp.h"
+
+/*
+ * Return pointer to a specific route entry. This must be a separate
+ * function from find_route() which modifies rtp.
+ */
+struct rtentry *
+snmp_find_route(src, mask)
+ register u_int32 src, mask;
+{
+ register struct rtentry *rt;
+
+ for (rt = routing_table; rt; rt = rt->rt_next) {
+ if (src == rt->rt_origin && mask == rt->rt_originmask)
+ return rt;
+ }
+ return NULL;
+}
+
+/*
+ * Find next route entry > specification
+ */
+int
+next_route(rtpp, src, mask)
+ struct rtentry **rtpp;
+ u_int32 src;
+ u_int32 mask;
+{
+ struct rtentry *rt, *rbest = NULL;
+
+ /* Among all entries > spec, find "lowest" one in order */
+ for (rt = routing_table; rt; rt=rt->rt_next) {
+ if ((ntohl(rt->rt_origin) > ntohl(src)
+ || (ntohl(rt->rt_origin) == ntohl(src)
+ && ntohl(rt->rt_originmask) > ntohl(mask)))
+ && (!rbest || (ntohl(rt->rt_origin) < ntohl(rbest->rt_origin))
+ || (ntohl(rt->rt_origin) == ntohl(rbest->rt_origin)
+ && ntohl(rt->rt_originmask) < ntohl(rbest->rt_originmask))))
+ rbest = rt;
+ }
+ (*rtpp) = rbest;
+ return (*rtpp)!=0;
+}
+
+/*
+ * Given a routing table entry, and a vifi, find the next vifi/entry
+ */
+int
+next_route_child(rtpp, src, mask, vifi)
+ struct rtentry **rtpp;
+ u_int32 src;
+ u_int32 mask;
+ vifi_t *vifi; /* vif at which to start looking */
+{
+ /* Get (S,M) entry */
+ if (!((*rtpp) = snmp_find_route(src,mask)))
+ if (!next_route(rtpp, src, mask))
+ return 0;
+
+ /* Continue until we get one with a valid next vif */
+ do {
+ for (; (*rtpp)->rt_children && *vifi<numvifs; (*vifi)++)
+ if (VIFM_ISSET(*vifi, (*rtpp)->rt_children))
+ return 1;
+ *vifi = 0;
+ } while( next_route(rtpp, (*rtpp)->rt_origin, (*rtpp)->rt_originmask) );
+
+ return 0;
+}
+#endif
+
+/*
+ * Initialize the routing table and associated variables.
+ */
+void
+init_routes()
+{
+ routing_table = NULL;
+ rt_end = RT_ADDR;
+ nroutes = 0;
+ routes_changed = FALSE;
+ delay_change_reports = FALSE;
+}
+
+
+/*
+ * Initialize the children bits for route 'r', along with the
+ * associated dominant and subordinate data structures.
+ * If first is set, initialize dominants, otherwise keep old
+ * dominants on non-parent interfaces.
+ * XXX Does this need a return value?
+ */
+static int
+init_children_and_leaves(r, parent, first)
+ register struct rtentry *r;
+ register vifi_t parent;
+ int first;
+{
+ register vifi_t vifi;
+ register struct uvif *v;
+ vifbitmap_t old_children;
+ nbrbitmap_t old_subords;
+
+ VIFM_COPY(r->rt_children, old_children);
+ NBRM_COPY(r->rt_subordinates, old_subords);
+
+ VIFM_CLRALL(r->rt_children);
+
+ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+ if (first || vifi == parent)
+ r->rt_dominants [vifi] = 0;
+ if (vifi == parent || uvifs[vifi].uv_flags & VIFF_NOFLOOD ||
+ AVOID_TRANSIT(vifi, r) || (!first && r->rt_dominants[vifi]))
+ NBRM_CLRMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap);
+ else
+ NBRM_SETMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap);
+
+ if (vifi != parent && !(v->uv_flags & (VIFF_DOWN|VIFF_DISABLED)) &&
+ !(!first && r->rt_dominants[vifi])) {
+ VIFM_SET(vifi, r->rt_children);
+ }
+ }
+
+ return (!VIFM_SAME(r->rt_children, old_children) ||
+ !NBRM_SAME(r->rt_subordinates, old_subords));
+}
+
+
+/*
+ * A new vif has come up -- update the children bitmaps in all route
+ * entries to take that into account.
+ */
+void
+add_vif_to_routes(vifi)
+ register vifi_t vifi;
+{
+ register struct rtentry *r;
+ register struct uvif *v;
+
+ v = &uvifs[vifi];
+ for (r = routing_table; r != NULL; r = r->rt_next) {
+ if (r->rt_metric != UNREACHABLE &&
+ !VIFM_ISSET(vifi, r->rt_children)) {
+ VIFM_SET(vifi, r->rt_children);
+ r->rt_dominants [vifi] = 0;
+ /*XXX isn't uv_nbrmap going to be empty?*/
+ NBRM_CLRMASK(r->rt_subordinates, v->uv_nbrmap);
+ update_table_entry(r, r->rt_gateway);
+ }
+ }
+}
+
+
+/*
+ * A vif has gone down -- expire all routes that have that vif as parent,
+ * and update the children bitmaps in all other route entries to take into
+ * account the failed vif.
+ */
+void
+delete_vif_from_routes(vifi)
+ register vifi_t vifi;
+{
+ register struct rtentry *r;
+
+ for (r = routing_table; r != NULL; r = r->rt_next) {
+ if (r->rt_metric != UNREACHABLE) {
+ if (vifi == r->rt_parent) {
+ del_table_entry(r, 0, DEL_ALL_ROUTES);
+ r->rt_timer = ROUTE_EXPIRE_TIME;
+ r->rt_metric = UNREACHABLE;
+ r->rt_flags |= RTF_CHANGED;
+ routes_changed = TRUE;
+ }
+ else if (VIFM_ISSET(vifi, r->rt_children)) {
+ VIFM_CLR(vifi, r->rt_children);
+ NBRM_CLRMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap);
+ update_table_entry(r, r->rt_gateway);
+ }
+ else {
+ r->rt_dominants[vifi] = 0;
+ }
+ }
+ }
+}
+
+
+/*
+ * A new neighbor has come up. If we're flooding on the neighbor's
+ * vif, mark that neighbor as subordinate for all routes whose parent
+ * is not this vif.
+ */
+void
+add_neighbor_to_routes(vifi, index)
+ register vifi_t vifi;
+ register int index;
+{
+ register struct rtentry *r;
+ register struct uvif *v;
+
+ v = &uvifs[vifi];
+ if (v->uv_flags & VIFF_NOFLOOD)
+ return;
+ for (r = routing_table; r != NULL; r = r->rt_next) {
+ if (r->rt_metric != UNREACHABLE && r->rt_parent != vifi &&
+ !AVOID_TRANSIT(vifi, r)) {
+ NBRM_SET(index, r->rt_subordinates);
+ update_table_entry(r, r->rt_gateway);
+ }
+ }
+}
+
+
+/*
+ * A neighbor has failed or become unreachable. If that neighbor was
+ * considered a dominant or subordinate router in any route entries,
+ * take appropriate action. Expire all routes this neighbor advertised
+ * to us.
+ */
+void
+delete_neighbor_from_routes(addr, vifi, index)
+ register u_int32 addr;
+ register vifi_t vifi;
+ int index;
+{
+ register struct rtentry *r;
+ register struct uvif *v;
+
+ v = &uvifs[vifi];
+ for (r = routing_table; r != NULL; r = r->rt_next) {
+ if (r->rt_metric != UNREACHABLE) {
+ if (r->rt_parent == vifi && r->rt_gateway == addr) {
+ del_table_entry(r, 0, DEL_ALL_ROUTES);
+ r->rt_timer = ROUTE_EXPIRE_TIME;
+ r->rt_metric = UNREACHABLE;
+ r->rt_flags |= RTF_CHANGED;
+ routes_changed = TRUE;
+ } else if (r->rt_dominants[vifi] == addr) {
+ VIFM_SET(vifi, r->rt_children);
+ r->rt_dominants[vifi] = 0;
+ if ((uvifs[vifi].uv_flags & VIFF_NOFLOOD) ||
+ AVOID_TRANSIT(vifi, r))
+ NBRM_CLRMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap);
+ else
+ NBRM_SETMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap);
+ update_table_entry(r, r->rt_gateway);
+ } else if (NBRM_ISSET(index, r->rt_subordinates)) {
+ NBRM_CLR(index, r->rt_subordinates);
+ update_table_entry(r, r->rt_gateway);
+ }
+ }
+ }
+}
+
+
+/*
+ * Prepare for a sequence of ordered route updates by initializing a pointer
+ * to the start of the routing table. The pointer is used to remember our
+ * position in the routing table in order to avoid searching from the
+ * beginning for each update; this relies on having the route reports in
+ * a single message be in the same order as the route entries in the routing
+ * table.
+ */
+void
+start_route_updates()
+{
+ rtp = RT_ADDR;
+}
+
+
+/*
+ * Starting at the route entry following the one to which 'rtp' points,
+ * look for a route entry matching the specified origin and mask. If a
+ * match is found, return TRUE and leave 'rtp' pointing at the found entry.
+ * If no match is found, return FALSE and leave 'rtp' pointing to the route
+ * entry preceding the point at which the new origin should be inserted.
+ * This code is optimized for the normal case in which the first entry to
+ * be examined is the matching entry.
+ */
+static int
+find_route(origin, mask)
+ register u_int32 origin, mask;
+{
+ register struct rtentry *r;
+
+ r = rtp->rt_next;
+ while (r != NULL) {
+ if (origin == r->rt_origin && mask == r->rt_originmask) {
+ rtp = r;
+ return (TRUE);
+ }
+ if (ntohl(mask) < ntohl(r->rt_originmask) ||
+ (mask == r->rt_originmask &&
+ ntohl(origin) < ntohl(r->rt_origin))) {
+ rtp = r;
+ r = r->rt_next;
+ }
+ else break;
+ }
+ return (FALSE);
+}
+
+/*
+ * Create a new routing table entry for the specified origin and link it into
+ * the routing table. The shared variable 'rtp' is assumed to point to the
+ * routing entry after which the new one should be inserted. It is left
+ * pointing to the new entry.
+ *
+ * Only the origin, originmask, originwidth and flags fields are initialized
+ * in the new route entry; the caller is responsible for filling in the the
+ * rest.
+ */
+static void
+create_route(origin, mask)
+ u_int32 origin, mask;
+{
+ register struct rtentry *r;
+
+ if ((r = (struct rtentry *) malloc(sizeof(struct rtentry) +
+ (numvifs * sizeof(u_int32)))) == NULL) {
+ log(LOG_ERR, 0, "ran out of memory"); /* fatal */
+ }
+ r->rt_origin = origin;
+ r->rt_originmask = mask;
+ if (((char *)&mask)[3] != 0) r->rt_originwidth = 4;
+ else if (((char *)&mask)[2] != 0) r->rt_originwidth = 3;
+ else if (((char *)&mask)[1] != 0) r->rt_originwidth = 2;
+ else r->rt_originwidth = 1;
+ r->rt_flags = 0;
+ r->rt_dominants = (u_int32 *)(r + 1);
+ bzero(r->rt_dominants, numvifs * sizeof(u_int32));
+ r->rt_groups = NULL;
+ VIFM_CLRALL(r->rt_children);
+ NBRM_CLRALL(r->rt_subordinates);
+ NBRM_CLRALL(r->rt_subordadv);
+
+ r->rt_next = rtp->rt_next;
+ rtp->rt_next = r;
+ r->rt_prev = rtp;
+ if (r->rt_next != NULL)
+ (r->rt_next)->rt_prev = r;
+ else
+ rt_end = r;
+ rtp = r;
+ ++nroutes;
+}
+
+
+/*
+ * Discard the routing table entry following the one to which 'prev_r' points.
+ */
+static void
+discard_route(prev_r)
+ register struct rtentry *prev_r;
+{
+ register struct rtentry *r;
+
+ r = prev_r->rt_next;
+ uvifs[r->rt_parent].uv_nroutes--;
+ /*???nbr???.al_nroutes--;*/
+ prev_r->rt_next = r->rt_next;
+ if (prev_r->rt_next != NULL)
+ (prev_r->rt_next)->rt_prev = prev_r;
+ else
+ rt_end = prev_r;
+ free((char *)r);
+ --nroutes;
+}
+
+
+/*
+ * Process a route report for a single origin, creating or updating the
+ * corresponding routing table entry if necessary. 'src' is either the
+ * address of a neighboring router from which the report arrived, or zero
+ * to indicate a change of status of one of our own interfaces.
+ */
+void
+update_route(origin, mask, metric, src, vifi, n)
+ u_int32 origin, mask;
+ u_int metric;
+ u_int32 src;
+ vifi_t vifi;
+ struct listaddr *n;
+{
+ register struct rtentry *r;
+ u_int adj_metric;
+
+ /*
+ * Compute an adjusted metric, taking into account the cost of the
+ * subnet or tunnel over which the report arrived, and normalizing
+ * all unreachable/poisoned metrics into a single value.
+ */
+ if (src != 0 && (metric < 1 || metric >= 2*UNREACHABLE)) {
+ log(LOG_WARNING, 0,
+ "%s reports out-of-range metric %u for origin %s",
+ inet_fmt(src, s1), metric, inet_fmts(origin, mask, s2));
+ return;
+ }
+ adj_metric = metric + uvifs[vifi].uv_metric;
+ if (adj_metric > UNREACHABLE) adj_metric = UNREACHABLE;
+
+ /*
+ * Look up the reported origin in the routing table.
+ */
+ if (!find_route(origin, mask)) {
+ /*
+ * Not found.
+ * Don't create a new entry if the report says it's unreachable,
+ * or if the reported origin and mask are invalid.
+ */
+ if (adj_metric == UNREACHABLE) {
+ return;
+ }
+ if (src != 0 && !inet_valid_subnet(origin, mask)) {
+ log(LOG_WARNING, 0,
+ "%s reports an invalid origin (%s) and/or mask (%08x)",
+ inet_fmt(src, s1), inet_fmt(origin, s2), ntohl(mask));
+ return;
+ }
+
+ IF_DEBUG(DEBUG_RTDETAIL)
+ log(LOG_DEBUG, 0, "%s advertises new route %s",
+ inet_fmt(src, s1), inet_fmts(origin, mask, s2));
+
+ /*
+ * OK, create the new routing entry. 'rtp' will be left pointing
+ * to the new entry.
+ */
+ create_route(origin, mask);
+ uvifs[vifi].uv_nroutes++;
+ /*n->al_nroutes++;*/
+
+ rtp->rt_metric = UNREACHABLE; /* temporary; updated below */
+ }
+
+ /*
+ * We now have a routing entry for the reported origin. Update it?
+ */
+ r = rtp;
+ if (r->rt_metric == UNREACHABLE) {
+ /*
+ * The routing entry is for a formerly-unreachable or new origin.
+ * If the report claims reachability, update the entry to use
+ * the reported route.
+ */
+ if (adj_metric == UNREACHABLE)
+ return;
+
+ IF_DEBUG(DEBUG_RTDETAIL)
+ log(LOG_DEBUG, 0, "%s advertises %s with adj_metric %d (ours was %d)",
+ inet_fmt(src, s1), inet_fmts(origin, mask, s2),
+ adj_metric, r->rt_metric);
+
+ /*
+ * Now "steal away" any sources that belong under this route
+ * by deleting any cache entries they might have created
+ * and allowing the kernel to re-request them.
+ *
+ * If we haven't performed final initialization yet and are
+ * just collecting the routing table, we can't have any
+ * sources so we don't perform this step.
+ */
+ if (did_final_init)
+ steal_sources(rtp);
+
+ r->rt_parent = vifi;
+ r->rt_gateway = src;
+ init_children_and_leaves(r, vifi, 1);
+
+ r->rt_timer = 0;
+ r->rt_metric = adj_metric;
+ r->rt_flags |= RTF_CHANGED;
+ routes_changed = TRUE;
+ update_table_entry(r, r->rt_gateway);
+ }
+ else if (src == r->rt_gateway) {
+ /*
+ * The report has come either from the interface directly-connected
+ * to the origin subnet (src and r->rt_gateway both equal zero) or
+ * from the gateway we have chosen as the best first-hop gateway back
+ * towards the origin (src and r->rt_gateway not equal zero). Reset
+ * the route timer and, if the reported metric has changed, update
+ * our entry accordingly.
+ */
+ r->rt_timer = 0;
+
+ IF_DEBUG(DEBUG_RTDETAIL)
+ log(LOG_DEBUG, 0, "%s (current parent) advertises %s with adj_metric %d (ours was %d)",
+ inet_fmt(src, s1), inet_fmts(origin, mask, s2),
+ adj_metric, r->rt_metric);
+
+ if (adj_metric == r->rt_metric)
+ return;
+
+ if (adj_metric == UNREACHABLE) {
+ del_table_entry(r, 0, DEL_ALL_ROUTES);
+ r->rt_timer = ROUTE_EXPIRE_TIME;
+ }
+ r->rt_metric = adj_metric;
+ r->rt_flags |= RTF_CHANGED;
+ routes_changed = TRUE;
+ }
+ else if (src == 0 ||
+ (r->rt_gateway != 0 &&
+ (adj_metric < r->rt_metric ||
+ (adj_metric == r->rt_metric &&
+ (ntohl(src) < ntohl(r->rt_gateway) ||
+ r->rt_timer >= ROUTE_SWITCH_TIME))))) {
+ /*
+ * The report is for an origin we consider reachable; the report
+ * comes either from one of our own interfaces or from a gateway
+ * other than the one we have chosen as the best first-hop gateway
+ * back towards the origin. If the source of the update is one of
+ * our own interfaces, or if the origin is not a directly-connected
+ * subnet and the reported metric for that origin is better than
+ * what our routing entry says, update the entry to use the new
+ * gateway and metric. We also switch gateways if the reported
+ * metric is the same as the one in the route entry and the gateway
+ * associated with the route entry has not been heard from recently,
+ * or if the metric is the same but the reporting gateway has a lower
+ * IP address than the gateway associated with the route entry.
+ * Did you get all that?
+ */
+ u_int32 old_gateway;
+ vifi_t old_parent;
+ old_gateway = r->rt_gateway;
+ old_parent = r->rt_parent;
+ r->rt_gateway = src;
+ r->rt_parent = vifi;
+
+ IF_DEBUG(DEBUG_RTDETAIL)
+ log(LOG_DEBUG, 0, "%s (new parent) on vif %d advertises %s with adj_metric %d (old parent was %s on vif %d, metric %d)",
+ inet_fmt(src, s1), vifi, inet_fmts(origin, mask, s2),
+ adj_metric, inet_fmt(old_gateway, s3), old_parent,
+ r->rt_metric);
+
+ if (old_parent != vifi) {
+ init_children_and_leaves(r, vifi, 0);
+ uvifs[old_parent].uv_nroutes--;
+ uvifs[vifi].uv_nroutes++;
+ }
+ if (old_gateway != src) {
+ update_table_entry(r, old_gateway);
+ /*???old_gateway???->al_nroutes--;*/
+ /*n->al_nroutes++;*/
+ }
+ r->rt_timer = 0;
+ r->rt_metric = adj_metric;
+ r->rt_flags |= RTF_CHANGED;
+ routes_changed = TRUE;
+ }
+ else if (vifi != r->rt_parent) {
+ /*
+ * The report came from a vif other than the route's parent vif.
+ * Update the children info, if necessary.
+ */
+ if (AVOID_TRANSIT(vifi, r)) {
+ /*
+ * The route's parent is a vif from which we're not supposed
+ * to transit onto this vif. Simply ignore the update.
+ */
+ IF_DEBUG(DEBUG_RTDETAIL)
+ log(LOG_DEBUG, 0, "%s on vif %d advertises %s with metric %d (ignored due to NOTRANSIT)",
+ inet_fmt(src, s1), vifi, inet_fmts(origin, mask, s2),
+ metric);
+ } else if (VIFM_ISSET(vifi, r->rt_children)) {
+ /*
+ * Vif is a child vif for this route.
+ */
+ if (metric < r->rt_metric ||
+ (metric == r->rt_metric &&
+ ntohl(src) < ntohl(uvifs[vifi].uv_lcl_addr))) {
+ /*
+ * Neighbor has lower metric to origin (or has same metric
+ * and lower IP address) -- it becomes the dominant router,
+ * and vif is no longer a child for me.
+ */
+ VIFM_CLR(vifi, r->rt_children);
+ r->rt_dominants [vifi] = src;
+ /* XXX
+ * We don't necessarily want to forget about subordinateness
+ * so that we can become the dominant quickly if the current
+ * dominant fails.
+ */
+ NBRM_CLRMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap);
+ update_table_entry(r, r->rt_gateway);
+ IF_DEBUG(DEBUG_RTDETAIL)
+ log(LOG_DEBUG, 0, "%s on vif %d becomes dominant for %s with metric %d",
+ inet_fmt(src, s1), vifi, inet_fmts(origin, mask, s2),
+ metric);
+ }
+ else if (metric > UNREACHABLE) { /* "poisoned reverse" */
+ /*
+ * Neighbor considers this vif to be on path to route's
+ * origin; record this neighbor as subordinate
+ */
+ if (!NBRM_ISSET(n->al_index, r->rt_subordinates)) {
+ IF_DEBUG(DEBUG_RTDETAIL)
+ log(LOG_DEBUG, 0, "%s on vif %d becomes subordinate for %s with poison-reverse metric %d",
+ inet_fmt(src, s1), vifi, inet_fmts(origin, mask, s2),
+ metric - UNREACHABLE);
+ NBRM_SET(n->al_index, r->rt_subordinates);
+ update_table_entry(r, r->rt_gateway);
+ } else {
+ IF_DEBUG(DEBUG_RTDETAIL)
+ log(LOG_DEBUG, 0, "%s on vif %d confirms subordinateness for %s with poison-reverse metric %d",
+ inet_fmt(src, s1), vifi, inet_fmts(origin, mask, s2),
+ metric - UNREACHABLE);
+ }
+ NBRM_SET(n->al_index, r->rt_subordadv);
+ }
+ else if (NBRM_ISSET(n->al_index, r->rt_subordinates)) {
+ /*
+ * Current subordinate no longer considers this vif to be on
+ * path to route's origin; it is no longer a subordinate
+ * router.
+ */
+ IF_DEBUG(DEBUG_RTDETAIL)
+ log(LOG_DEBUG, 0, "%s on vif %d is no longer a subordinate for %s with metric %d",
+ inet_fmt(src, s1), vifi, inet_fmts(origin, mask, s2),
+ metric);
+ NBRM_CLR(n->al_index, r->rt_subordinates);
+ update_table_entry(r, r->rt_gateway);
+ }
+
+ }
+ else if (src == r->rt_dominants[vifi] &&
+ (metric > r->rt_metric ||
+ (metric == r->rt_metric &&
+ ntohl(src) > ntohl(uvifs[vifi].uv_lcl_addr)))) {
+ /*
+ * Current dominant no longer has a lower metric to origin
+ * (or same metric and lower IP address); we adopt the vif
+ * as our own child.
+ */
+ IF_DEBUG(DEBUG_RTDETAIL)
+ log(LOG_DEBUG, 0, "%s (current dominant) on vif %d is no longer dominant for %s with metric %d",
+ inet_fmt(src, s1), vifi, inet_fmts(origin, mask, s2),
+ metric);
+ VIFM_SET(vifi, r->rt_children);
+ r->rt_dominants[vifi] = 0;
+ if (uvifs[vifi].uv_flags & VIFF_NOFLOOD)
+ NBRM_CLRMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap);
+ else
+ NBRM_SETMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap);
+ if (metric > UNREACHABLE) {
+ NBRM_SET(n->al_index, r->rt_subordinates);
+ NBRM_SET(n->al_index, r->rt_subordadv);
+ }
+ update_table_entry(r, r->rt_gateway);
+ } else {
+ IF_DEBUG(DEBUG_RTDETAIL)
+ log(LOG_DEBUG, 0, "%s on vif %d advertises %s with metric %d (ignored)",
+ inet_fmt(src, s1), vifi, inet_fmts(origin, mask, s2),
+ metric);
+ }
+ }
+}
+
+
+/*
+ * On every timer interrupt, advance the timer in each routing entry.
+ */
+void
+age_routes()
+{
+ register struct rtentry *r;
+ register struct rtentry *prev_r;
+ extern u_long virtual_time; /* from main.c */
+
+ for (prev_r = RT_ADDR, r = routing_table;
+ r != NULL;
+ prev_r = r, r = r->rt_next) {
+
+ if ((r->rt_timer += TIMER_INTERVAL) >= ROUTE_DISCARD_TIME) {
+ /*
+ * Time to garbage-collect the route entry.
+ */
+ del_table_entry(r, 0, DEL_ALL_ROUTES);
+ discard_route(prev_r);
+ r = prev_r;
+ }
+ else if (r->rt_timer >= ROUTE_EXPIRE_TIME &&
+ r->rt_metric != UNREACHABLE) {
+ /*
+ * Time to expire the route entry. If the gateway is zero,
+ * i.e., it is a route to a directly-connected subnet, just
+ * set the timer back to zero; such routes expire only when
+ * the interface to the subnet goes down.
+ */
+ if (r->rt_gateway == 0) {
+ r->rt_timer = 0;
+ }
+ else {
+ del_table_entry(r, 0, DEL_ALL_ROUTES);
+ r->rt_metric = UNREACHABLE;
+ r->rt_flags |= RTF_CHANGED;
+ routes_changed = TRUE;
+ }
+ }
+ else if (virtual_time % (ROUTE_REPORT_INTERVAL * 2) == 0) {
+ /*
+ * Time out subordinateness that hasn't been reported in
+ * the last 2 intervals.
+ */
+ if (!NBRM_SAME(r->rt_subordinates, r->rt_subordadv)) {
+ IF_DEBUG(DEBUG_ROUTE)
+ log(LOG_DEBUG, 0, "rt %s sub 0x%08x%08x subadv 0x%08x%08x metric %d",
+ RT_FMT(r, s1),
+ r->rt_subordinates.hi, r->rt_subordinates.lo,
+ r->rt_subordadv.hi, r->rt_subordadv.lo, r->rt_metric);
+ NBRM_MASK(r->rt_subordinates, r->rt_subordadv);
+ update_table_entry(r, r->rt_gateway);
+ }
+ NBRM_CLRALL(r->rt_subordadv);
+ }
+ }
+}
+
+
+/*
+ * Mark all routes as unreachable. This function is called only from
+ * hup() in preparation for informing all neighbors that we are going
+ * off the air. For consistency, we ought also to delete all reachable
+ * route entries from the kernel, but since we are about to exit we rely
+ * on the kernel to do its own cleanup -- no point in making all those
+ * expensive kernel calls now.
+ */
+void
+expire_all_routes()
+{
+ register struct rtentry *r;
+
+ for (r = routing_table; r != NULL; r = r->rt_next) {
+ r->rt_metric = UNREACHABLE;
+ r->rt_flags |= RTF_CHANGED;
+ routes_changed = TRUE;
+ }
+}
+
+
+/*
+ * Delete all the routes in the routing table.
+ */
+void
+free_all_routes()
+{
+ register struct rtentry *r;
+
+ r = RT_ADDR;
+
+ while (r->rt_next)
+ discard_route(r);
+}
+
+
+/*
+ * Process an incoming neighbor probe message.
+ */
+void
+accept_probe(src, dst, p, datalen, level)
+ u_int32 src;
+ u_int32 dst;
+ char *p;
+ int datalen;
+ u_int32 level;
+{
+ vifi_t vifi;
+ static struct listaddr *unknowns = NULL;
+
+ if ((vifi = find_vif(src, dst)) == NO_VIF) {
+ struct listaddr *a, **prev;
+ struct listaddr *match = NULL;
+ time_t now = time(0);
+
+ for (prev = &unknowns, a = *prev; a; a = *prev) {
+ if (a->al_addr == src)
+ match = a;
+ if (a->al_ctime + 2 * a->al_timer < now) {
+ /* We haven't heard from it in a long time */
+ *prev = a->al_next;
+ free(a);
+ } else {
+ prev = &a->al_next;
+ }
+ }
+ if (match == NULL) {
+ match = *prev = (struct listaddr *)malloc(sizeof(struct listaddr));
+ match->al_next = NULL;
+ match->al_addr = src;
+ match->al_timer = OLD_NEIGHBOR_EXPIRE_TIME;
+ match->al_ctime = now - match->al_timer;
+ }
+
+ if (match->al_ctime + match->al_timer <= now) {
+ log(LOG_WARNING, 0,
+ "ignoring probe from non-neighbor %s, check for misconfigured tunnel or routing on %s",
+ inet_fmt(src, s1), s1);
+ match->al_timer *= 2;
+ } else
+ IF_DEBUG(DEBUG_PEER)
+ log(LOG_DEBUG, 0,
+ "ignoring probe from non-neighbor %s (%d seconds until next warning)", inet_fmt(src, s1), match->al_ctime + match->al_timer - now);
+ return;
+ }
+
+ update_neighbor(vifi, src, DVMRP_PROBE, p, datalen, level);
+}
+
+struct newrt {
+ u_int32 mask;
+ u_int32 origin;
+ int metric;
+ int pad;
+};
+
+static int
+compare_rts(rt1, rt2)
+ const void *rt1;
+ const void *rt2;
+{
+ register struct newrt *r1 = (struct newrt *)rt1;
+ register struct newrt *r2 = (struct newrt *)rt2;
+ register u_int32 m1 = ntohl(r1->mask);
+ register u_int32 m2 = ntohl(r2->mask);
+ register u_int32 o1, o2;
+
+ if (m1 > m2)
+ return (-1);
+ if (m1 < m2)
+ return (1);
+
+ /* masks are equal */
+ o1 = ntohl(r1->origin);
+ o2 = ntohl(r2->origin);
+ if (o1 > o2)
+ return (-1);
+ if (o1 < o2)
+ return (1);
+ return (0);
+}
+
+void
+blaster_alloc(vifi)
+ vifi_t vifi;
+{
+ register struct uvif *v;
+
+ v = &uvifs[vifi];
+ if (v->uv_blasterbuf)
+ free(v->uv_blasterbuf);
+
+ v->uv_blasterlen = 64*1024;
+ v->uv_blasterbuf = malloc(v->uv_blasterlen);
+ v->uv_blastercur = v->uv_blasterend = v->uv_blasterbuf;
+ if (v->uv_blastertimer)
+ timer_clearTimer(v->uv_blastertimer);
+ v->uv_blastertimer = 0;
+}
+
+struct blaster_hdr {
+ u_int32 bh_src;
+ u_int32 bh_dst;
+ u_int32 bh_level;
+ int bh_datalen;
+};
+
+/*
+ * Queue a route report from a route-blaster.
+ * If the timer isn't running to process these reports,
+ * start it.
+ */
+static void
+queue_blaster_report(vifi, src, dst, p, datalen, level)
+ vifi_t vifi;
+ u_int32 src, dst, level;
+ register char *p;
+ register int datalen;
+{
+ register struct blaster_hdr *bh;
+ register struct uvif *v;
+ int bblen = sizeof(*bh) + ((datalen + 3) & ~3);
+
+ v = &uvifs[vifi];
+ if (v->uv_blasterend - v->uv_blasterbuf +
+ bblen > v->uv_blasterlen) {
+ int end = v->uv_blasterend - v->uv_blasterbuf;
+ int cur = v->uv_blastercur - v->uv_blasterbuf;
+
+ v->uv_blasterlen *= 2;
+ IF_DEBUG(DEBUG_IF)
+ log(LOG_DEBUG, 0, "increasing blasterbuf to %d bytes",
+ v->uv_blasterlen);
+ v->uv_blasterbuf = realloc(v->uv_blasterbuf,
+ v->uv_blasterlen);
+ if (v->uv_blasterbuf == NULL) {
+ log(LOG_WARNING, ENOMEM, "turning off blaster on vif %d", vifi);
+ v->uv_blasterlen = 0;
+ v->uv_blasterend = v->uv_blastercur = NULL;
+ v->uv_flags &= ~VIFF_BLASTER;
+ return;
+ }
+ v->uv_blasterend = v->uv_blasterbuf + end;
+ v->uv_blastercur = v->uv_blasterbuf + cur;
+ }
+ bh = (struct blaster_hdr *)v->uv_blasterend;
+ bh->bh_src = src;
+ bh->bh_dst = dst;
+ bh->bh_level = level;
+ bh->bh_datalen = datalen;
+ bcopy(p, (char *)(bh + 1), datalen);
+ v->uv_blasterend += bblen;
+
+ if (v->uv_blastertimer == 0) {
+ int *i = (int *)malloc(sizeof(int *));
+
+ if (i == NULL)
+ log(LOG_ERR, 0, "out of memory");
+
+ *i = vifi;
+
+ v->uv_blastertimer = timer_setTimer(5,
+ process_blaster_report, i);
+ }
+}
+
+/*
+ * Periodic process; process up to 5 of the routes in the route-blaster
+ * queue. If there are more routes remaining, reschedule myself to run
+ * in 1 second.
+ */
+static void
+process_blaster_report(vifip)
+ void *vifip;
+{
+ vifi_t vifi = *(int *)vifip;
+ register struct uvif *v;
+ register struct blaster_hdr *bh;
+ int i;
+
+ IF_DEBUG(DEBUG_ROUTE)
+ log(LOG_DEBUG, 0, "processing vif %d blasted routes", vifi);
+ v = &uvifs[vifi];
+ for (i = 0; i < 5; i++) {
+ if (v->uv_blastercur >= v->uv_blasterend)
+ break;
+ bh = (struct blaster_hdr *)v->uv_blastercur;
+ v->uv_blastercur += sizeof(*bh) + ((bh->bh_datalen + 3) & ~3);
+ accept_report(bh->bh_src, bh->bh_dst, (char *)(bh + 1),
+ -bh->bh_datalen, bh->bh_level);
+ }
+
+ if (v->uv_blastercur >= v->uv_blasterend) {
+ v->uv_blastercur = v->uv_blasterbuf;
+ v->uv_blasterend = v->uv_blasterbuf;
+ v->uv_blastertimer = 0;
+ free(vifip);
+ IF_DEBUG(DEBUG_ROUTE)
+ log(LOG_DEBUG, 0, "finish processing vif %d blaster", vifi);
+ } else {
+ IF_DEBUG(DEBUG_ROUTE)
+ log(LOG_DEBUG, 0, "more blasted routes to come on vif %d", vifi);
+ v->uv_blastertimer = timer_setTimer(1,
+ process_blaster_report, vifip);
+ }
+}
+
+/*
+ * Process an incoming route report message.
+ * If the report arrived on a vif marked as a "blaster", then just
+ * queue it and return; queue_blaster_report() will schedule it for
+ * processing later. If datalen is negative, then this is actually
+ * a queued report so actually process it instead of queueing it.
+ */
+void
+accept_report(src, dst, p, datalen, level)
+ u_int32 src, dst, level;
+ register char *p;
+ register int datalen;
+{
+ vifi_t vifi;
+ register int width, i, nrt = 0;
+ int metric;
+ u_int32 mask;
+ u_int32 origin;
+ struct newrt rt[4096];
+ struct listaddr *nbr;
+
+ if ((vifi = find_vif(src, dst)) == NO_VIF) {
+ log(LOG_INFO, 0,
+ "ignoring route report from non-neighbor %s", inet_fmt(src, s1));
+ return;
+ }
+
+ if (uvifs[vifi].uv_flags & VIFF_BLASTER)
+ if (datalen > 0) {
+ queue_blaster_report(vifi, src, dst, p, datalen, level);
+ return;
+ } else {
+ datalen = -datalen;
+ }
+
+ if (!(nbr = update_neighbor(vifi, src, DVMRP_REPORT, NULL, 0, level)))
+ return;
+
+ if (datalen > 2*4096) {
+ log(LOG_INFO, 0,
+ "ignoring oversize (%d bytes) route report from %s",
+ datalen, inet_fmt(src, s1));
+ return;
+ }
+
+ while (datalen > 0) { /* Loop through per-mask lists. */
+
+ if (datalen < 3) {
+ log(LOG_WARNING, 0,
+ "received truncated route report from %s",
+ inet_fmt(src, s1));
+ return;
+ }
+ ((u_char *)&mask)[0] = 0xff; width = 1;
+ if ((((u_char *)&mask)[1] = *p++) != 0) width = 2;
+ if ((((u_char *)&mask)[2] = *p++) != 0) width = 3;
+ if ((((u_char *)&mask)[3] = *p++) != 0) width = 4;
+ if (!inet_valid_mask(ntohl(mask))) {
+ log(LOG_WARNING, 0,
+ "%s reports bogus netmask 0x%08x (%s)",
+ inet_fmt(src, s1), ntohl(mask), inet_fmt(mask, s2));
+ return;
+ }
+ datalen -= 3;
+
+ do { /* Loop through (origin, metric) pairs */
+ if (datalen < width + 1) {
+ log(LOG_WARNING, 0,
+ "received truncated route report from %s",
+ inet_fmt(src, s1));
+ return;
+ }
+ origin = 0;
+ for (i = 0; i < width; ++i)
+ ((char *)&origin)[i] = *p++;
+ metric = *p++;
+ datalen -= width + 1;
+ rt[nrt].mask = mask;
+ rt[nrt].origin = origin;
+ rt[nrt].metric = (metric & 0x7f);
+ ++nrt;
+ } while (!(metric & 0x80));
+ }
+
+ qsort((char*)rt, nrt, sizeof(rt[0]), compare_rts);
+ start_route_updates();
+ /*
+ * If the last entry is default, change mask from 0xff000000 to 0
+ */
+ if (rt[nrt-1].origin == 0)
+ rt[nrt-1].mask = 0;
+
+ IF_DEBUG(DEBUG_ROUTE)
+ log(LOG_DEBUG, 0, "Updating %d routes from %s to %s", nrt,
+ inet_fmt(src, s1), inet_fmt(dst, s2));
+ for (i = 0; i < nrt; ++i) {
+ if (i != 0 && rt[i].origin == rt[i-1].origin &&
+ rt[i].mask == rt[i-1].mask) {
+ log(LOG_WARNING, 0, "%s reports duplicate route for %s",
+ inet_fmt(src, s1), inet_fmts(rt[i].origin, rt[i].mask, s2));
+ continue;
+ }
+ /* Only filter non-poisoned updates. */
+ if (uvifs[vifi].uv_filter && rt[i].metric < UNREACHABLE) {
+ struct vf_element *vfe;
+ int match = 0;
+
+ for (vfe = uvifs[vifi].uv_filter->vf_filter; vfe; vfe = vfe->vfe_next) {
+ if (vfe->vfe_flags & VFEF_EXACT) {
+ if ((vfe->vfe_addr == rt[i].origin) &&
+ (vfe->vfe_mask == rt[i].mask)) {
+ match = 1;
+ break;
+ }
+ } else {
+ if ((rt[i].origin & vfe->vfe_mask) == vfe->vfe_addr) {
+ match = 1;
+ break;
+ }
+ }
+ }
+ if ((uvifs[vifi].uv_filter->vf_type == VFT_ACCEPT && match == 0) ||
+ (uvifs[vifi].uv_filter->vf_type == VFT_DENY && match == 1)) {
+ IF_DEBUG(DEBUG_ROUTE)
+ log(LOG_DEBUG, 0, "%s skipped on vif %d because it %s %s",
+ inet_fmts(rt[i].origin, rt[i].mask, s1),
+ vifi,
+ match ? "matches" : "doesn't match",
+ match ? inet_fmts(vfe->vfe_addr, vfe->vfe_mask, s2) :
+ "the filter");
+#if 0
+ rt[i].metric += vfe->vfe_addmetric;
+ if (rt[i].metric > UNREACHABLE)
+#endif
+ rt[i].metric = UNREACHABLE;
+ }
+ }
+ update_route(rt[i].origin, rt[i].mask, rt[i].metric,
+ src, vifi, nbr);
+ }
+
+ if (routes_changed && !delay_change_reports)
+ report_to_all_neighbors(CHANGED_ROUTES);
+}
+
+
+/*
+ * Send a route report message to destination 'dst', via virtual interface
+ * 'vifi'. 'which_routes' specifies ALL_ROUTES or CHANGED_ROUTES.
+ */
+void
+report(which_routes, vifi, dst)
+ int which_routes;
+ vifi_t vifi;
+ u_int32 dst;
+{
+ register struct rtentry *r;
+ register int i;
+
+ r = rt_end;
+ while (r != RT_ADDR) {
+ i = report_chunk(which_routes, r, vifi, dst);
+ while (i-- > 0)
+ r = r->rt_prev;
+ }
+}
+
+
+/*
+ * Send a route report message to all neighboring routers.
+ * 'which_routes' specifies ALL_ROUTES or CHANGED_ROUTES.
+ */
+void
+report_to_all_neighbors(which_routes)
+ int which_routes;
+{
+ register vifi_t vifi;
+ register struct uvif *v;
+ register struct rtentry *r;
+ int routes_changed_before;
+
+ /*
+ * Remember the state of the global routes_changed flag before
+ * generating the reports, and clear the flag.
+ */
+ routes_changed_before = routes_changed;
+ routes_changed = FALSE;
+
+
+ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+ if (!NBRM_ISEMPTY(v->uv_nbrmap)) {
+ report(which_routes, vifi, v->uv_dst_addr);
+ }
+ }
+
+ /*
+ * If there were changed routes before we sent the reports AND
+ * if no new changes occurred while sending the reports, clear
+ * the change flags in the individual route entries. If changes
+ * did occur while sending the reports, new reports will be
+ * generated at the next timer interrupt.
+ */
+ if (routes_changed_before && !routes_changed) {
+ for (r = routing_table; r != NULL; r = r->rt_next) {
+ r->rt_flags &= ~RTF_CHANGED;
+ }
+ }
+
+ /*
+ * Set a flag to inhibit further reports of changed routes until the
+ * next timer interrupt. This is to alleviate update storms.
+ */
+ delay_change_reports = TRUE;
+}
+
+/*
+ * Send a route report message to destination 'dst', via virtual interface
+ * 'vifi'. 'which_routes' specifies ALL_ROUTES or CHANGED_ROUTES.
+ */
+static int
+report_chunk(which_routes, start_rt, vifi, dst)
+ int which_routes;
+ register struct rtentry *start_rt;
+ vifi_t vifi;
+ u_int32 dst;
+{
+ register struct rtentry *r;
+ register char *p;
+ register int i;
+ register int nrt = 0;
+ struct uvif *v = &uvifs[vifi];
+ int datalen = 0;
+ int width = 0;
+ u_int32 mask = 0;
+ u_int32 src;
+ int admetric = v->uv_admetric;
+ int metric;
+
+ src = v->uv_lcl_addr;
+ p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
+
+ for (r = start_rt; r != RT_ADDR; r = r->rt_prev) {
+ if (which_routes == CHANGED_ROUTES && !(r->rt_flags & RTF_CHANGED)) {
+ nrt++;
+ continue;
+ }
+
+ /*
+ * Do not poison-reverse a route for a directly-connected
+ * subnetwork on that subnetwork. This can cause loops when
+ * some router on the subnetwork is misconfigured.
+ */
+ if (r->rt_gateway == 0 && r->rt_parent == vifi) {
+ nrt++;
+ continue;
+ }
+
+ if (v->uv_filter && v->uv_filter->vf_flags & VFF_BIDIR) {
+ struct vf_element *vfe;
+ int match = 0;
+
+ for (vfe = v->uv_filter->vf_filter; vfe; vfe = vfe->vfe_next) {
+ if (vfe->vfe_flags & VFEF_EXACT) {
+ if ((vfe->vfe_addr == r->rt_origin) &&
+ (vfe->vfe_mask == r->rt_originmask)) {
+ match = 1;
+ break;
+ }
+ } else {
+ if ((r->rt_origin & vfe->vfe_mask) == vfe->vfe_addr) {
+ match = 1;
+ break;
+ }
+ }
+ }
+ if ((v->uv_filter->vf_type == VFT_ACCEPT && match == 0) ||
+ (v->uv_filter->vf_type == VFT_DENY && match == 1)) {
+ IF_DEBUG(DEBUG_ROUTE)
+ log(LOG_DEBUG, 0, "%s not reported on vif %d because it %s %s",
+ RT_FMT(r, s1), vifi,
+ match ? "matches" : "doesn't match",
+ match ? inet_fmts(vfe->vfe_addr, vfe->vfe_mask, s2) :
+ "the filter");
+ nrt++;
+ continue;
+ }
+ }
+
+ /*
+ * If there is no room for this route in the current message,
+ * send it & return how many routes we sent.
+ */
+ if (datalen + ((r->rt_originmask == mask) ?
+ (width + 1) :
+ (r->rt_originwidth + 4)) > MAX_DVMRP_DATA_LEN) {
+ *(p-1) |= 0x80;
+ send_on_vif(v, 0, DVMRP_REPORT, datalen);
+ return (nrt);
+ }
+
+ if (r->rt_originmask != mask || datalen == 0) {
+ mask = r->rt_originmask;
+ width = r->rt_originwidth;
+ if (datalen != 0) *(p-1) |= 0x80;
+ *p++ = ((char *)&mask)[1];
+ *p++ = ((char *)&mask)[2];
+ *p++ = ((char *)&mask)[3];
+ datalen += 3;
+ }
+ for (i = 0; i < width; ++i)
+ *p++ = ((char *)&(r->rt_origin))[i];
+
+ metric = r->rt_metric + admetric;
+ if (metric > UNREACHABLE)
+ metric = UNREACHABLE;
+ if (r->rt_parent != vifi && AVOID_TRANSIT(vifi, r))
+ metric = UNREACHABLE;
+ *p++ = (r->rt_parent == vifi && metric != UNREACHABLE) ?
+ (char)(metric + UNREACHABLE) : /* "poisoned reverse" */
+ (char)(metric);
+ ++nrt;
+ datalen += width + 1;
+ }
+ if (datalen != 0) {
+ *(p-1) |= 0x80;
+ send_on_vif(v, 0, DVMRP_REPORT, datalen);
+ }
+ return (nrt);
+}
+
+/*
+ * send the next chunk of our routing table to all neighbors.
+ * return the length of the smallest chunk we sent out.
+ */
+int
+report_next_chunk()
+{
+ register vifi_t vifi;
+ register struct uvif *v;
+ register struct rtentry *sr;
+ register int i, n = 0, min = 20000;
+ static int start_rt;
+
+ if (nroutes <= 0)
+ return (0);
+
+ /*
+ * find this round's starting route.
+ */
+ for (sr = rt_end, i = start_rt; --i >= 0; ) {
+ sr = sr->rt_prev;
+ if (sr == RT_ADDR)
+ sr = rt_end;
+ }
+
+ /*
+ * send one chunk of routes starting at this round's start to
+ * all our neighbors.
+ */
+ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+ if (!NBRM_ISEMPTY(v->uv_nbrmap)) {
+ n = report_chunk(ALL_ROUTES, sr, vifi, v->uv_dst_addr);
+ if (n < min)
+ min = n;
+ }
+ }
+ if (min == 20000)
+ min = 0; /* Neighborless router didn't send any routes */
+
+ n = min;
+ IF_DEBUG(DEBUG_ROUTE)
+ log(LOG_INFO, 0, "update %d starting at %d of %d",
+ n, (nroutes - start_rt), nroutes);
+
+ start_rt = (start_rt + n) % nroutes;
+ return (n);
+}
+
+
+/*
+ * Print the contents of the routing table on file 'fp'.
+ */
+void
+dump_routes(fp)
+ FILE *fp;
+{
+ register struct rtentry *r;
+ register vifi_t i;
+
+
+ fprintf(fp,
+ "Multicast Routing Table (%u %s)\n%s\n",
+ nroutes, (nroutes == 1) ? "entry" : "entries",
+ " Origin-Subnet From-Gateway Metric Tmr Fl In-Vif Out-Vifs");
+
+ for (r = routing_table; r != NULL; r = r->rt_next) {
+
+ fprintf(fp, " %-18s %-15s ",
+ inet_fmts(r->rt_origin, r->rt_originmask, s1),
+ (r->rt_gateway == 0) ? "" : inet_fmt(r->rt_gateway, s2));
+
+ fprintf(fp, (r->rt_metric == UNREACHABLE) ? " NR " : "%4u ",
+ r->rt_metric);
+
+ fprintf(fp, " %3u %c%c %3u ", r->rt_timer,
+ (r->rt_flags & RTF_CHANGED) ? 'C' : '.',
+ (r->rt_flags & RTF_HOLDDOWN) ? 'H' : '.',
+ r->rt_parent);
+
+ for (i = 0; i < numvifs; ++i) {
+ struct listaddr *n;
+ char l = '[';
+
+ if (VIFM_ISSET(i, r->rt_children)) {
+ if ((uvifs[i].uv_flags & VIFF_TUNNEL) &&
+ !NBRM_ISSETMASK(uvifs[i].uv_nbrmap, r->rt_subordinates))
+ /* Don't print out parenthood of a leaf tunnel. */
+ continue;
+ fprintf(fp, " %u", i);
+ if (!NBRM_ISSETMASK(uvifs[i].uv_nbrmap, r->rt_subordinates))
+ fprintf(fp, "*");
+ for (n = uvifs[i].uv_neighbors; n; n = n->al_next) {
+ if (NBRM_ISSET(n->al_index, r->rt_subordinates)) {
+ fprintf(fp, "%c%d", l, n->al_index);
+ l = ',';
+ }
+ }
+ if (l == ',')
+ fprintf(fp, "]");
+ }
+ }
+ fprintf(fp, "\n");
+ }
+ fprintf(fp, "\n");
+}
+
+struct rtentry *
+determine_route(src)
+ u_int32 src;
+{
+ struct rtentry *rt;
+
+ for (rt = routing_table; rt != NULL; rt = rt->rt_next) {
+ if (rt->rt_origin == (src & rt->rt_originmask) &&
+ rt->rt_metric != UNREACHABLE)
+ break;
+ }
+ return rt;
+}
diff --git a/usr.sbin/mrouted/route.h b/usr.sbin/mrouted/route.h
new file mode 100644
index 0000000..300202b
--- /dev/null
+++ b/usr.sbin/mrouted/route.h
@@ -0,0 +1,53 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $FreeBSD$
+ * route.h,v 3.8.4.6 1997/07/01 23:02:35 fenner Exp
+ */
+
+/*
+ * Routing Table Entry, one per subnet from which a multicast could originate.
+ * (Note: all addresses, subnet numbers and masks are kept in NETWORK order.)
+ *
+ * The Routing Table is stored as a doubly-linked list of these structures,
+ * ordered by decreasing value of rt_originmask and, secondarily, by
+ * decreasing value of rt_origin within each rt_originmask value.
+ * This data structure is efficient for generating route reports, whether
+ * full or partial, for processing received full reports, for clearing the
+ * CHANGED flags, and for periodically advancing the timers in all routes.
+ * It is not so efficient for updating a small number of routes in response
+ * to a partial report. In a stable topology, the latter are rare; if they
+ * turn out to be costing a lot, we can add an auxiliary hash table for
+ * faster access to arbitrary route entries.
+ */
+struct rtentry {
+ struct rtentry *rt_next; /* link to next entry MUST BE FIRST */
+ u_int32 rt_origin; /* subnet origin of multicasts */
+ u_int32 rt_originmask; /* subnet mask for origin */
+ short rt_originwidth; /* # bytes of origin subnet number */
+ u_char rt_metric; /* cost of route back to origin */
+ u_char rt_flags; /* RTF_ flags defined below */
+ u_int32 rt_gateway; /* first-hop gateway back to origin */
+ vifi_t rt_parent; /* incoming vif (ie towards origin) */
+ vifbitmap_t rt_children; /* outgoing children vifs */
+ u_int32 *rt_dominants; /* per vif dominant gateways */
+ nbrbitmap_t rt_subordinates; /* bitmap of subordinate gateways */
+ nbrbitmap_t rt_subordadv; /* recently advertised subordinates */
+ u_int rt_timer; /* for timing out the route entry */
+ struct rtentry *rt_prev; /* link to previous entry */
+ struct gtable *rt_groups; /* link to active groups */
+};
+
+#define RTF_CHANGED 0x01 /* route changed but not reported */
+#define RTF_HOLDDOWN 0x04 /* this route is in holddown */
+
+#define ALL_ROUTES 0 /* possible arguments to report() */
+#define CHANGED_ROUTES 1 /* and report_to_all_neighbors() */
+
+#define RT_FMT(r, s) inet_fmts((r)->rt_origin, (r)->rt_originmask, s)
diff --git a/usr.sbin/mrouted/rsrr.c b/usr.sbin/mrouted/rsrr.c
new file mode 100644
index 0000000..86d6e6e
--- /dev/null
+++ b/usr.sbin/mrouted/rsrr.c
@@ -0,0 +1,485 @@
+/*
+ * Copyright (c) 1993 by the University of Southern California
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation in source and binary forms for non-commercial purposes
+ * and without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both the copyright notice and
+ * this permission notice appear in supporting documentation. and that
+ * any documentation, advertising materials, and other materials related
+ * to such distribution and use acknowledge that the software was
+ * developed by the University of Southern California, Information
+ * Sciences Institute. The name of the University may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA makes no representations about
+ * the suitability of this software for any purpose. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ */
+
+/* RSRR code written by Daniel Zappala, USC Information Sciences Institute,
+ * April 1995.
+ */
+
+/* May 1995 -- Added support for Route Change Notification */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#ifdef RSRR
+
+#include "defs.h"
+#include <sys/param.h>
+#ifdef HAVE_SA_LEN
+#include <stddef.h> /* for offsetof */
+#endif
+
+/*
+ * Exported variables.
+ */
+int rsrr_socket; /* interface to reservation protocol */
+
+/*
+ * Global RSRR variables.
+ */
+char rsrr_recv_buf[RSRR_MAX_LEN]; /* RSRR receive buffer */
+char rsrr_send_buf[RSRR_MAX_LEN]; /* RSRR send buffer */
+
+struct sockaddr_un client_addr;
+int client_length = sizeof(client_addr);
+
+
+/*
+ * Procedure definitions needed internally.
+ */
+static void rsrr_accept __P((int recvlen));
+static void rsrr_accept_iq __P((void));
+static int rsrr_accept_rq __P((struct rsrr_rq *route_query, int flags,
+ struct gtable *gt_notify));
+static void rsrr_read __P((int, fd_set *));
+static int rsrr_send __P((int sendlen));
+static void rsrr_cache __P((struct gtable *gt,
+ struct rsrr_rq *route_query));
+
+/* Initialize RSRR socket */
+void
+rsrr_init()
+{
+ int servlen;
+ struct sockaddr_un serv_addr;
+
+ if ((rsrr_socket = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)
+ log(LOG_ERR, errno, "Can't create RSRR socket");
+
+ unlink(RSRR_SERV_PATH);
+ bzero((char *) &serv_addr, sizeof(serv_addr));
+ serv_addr.sun_family = AF_UNIX;
+ strcpy(serv_addr.sun_path, RSRR_SERV_PATH);
+#ifdef HAVE_SA_LEN
+ servlen = offsetof(struct sockaddr_un, sun_path) +
+ strlen(serv_addr.sun_path);
+ serv_addr.sun_len = servlen;
+#else
+ servlen = sizeof(serv_addr.sun_family) + strlen(serv_addr.sun_path);
+#endif
+
+ if (bind(rsrr_socket, (struct sockaddr *) &serv_addr, servlen) < 0)
+ log(LOG_ERR, errno, "Can't bind RSRR socket");
+
+ if (register_input_handler(rsrr_socket, rsrr_read) < 0)
+ log(LOG_WARNING, 0, "Couldn't register RSRR as an input handler");
+}
+
+/* Read a message from the RSRR socket */
+static void
+rsrr_read(f, rfd)
+ int f;
+ fd_set *rfd;
+{
+ register int rsrr_recvlen;
+
+ bzero((char *) &client_addr, sizeof(client_addr));
+ rsrr_recvlen = recvfrom(rsrr_socket, rsrr_recv_buf, sizeof(rsrr_recv_buf),
+ 0, (struct sockaddr *)&client_addr, &client_length);
+ if (rsrr_recvlen < 0) {
+ if (errno != EINTR)
+ log(LOG_ERR, errno, "RSRR recvfrom");
+ return;
+ }
+ rsrr_accept(rsrr_recvlen);
+}
+
+/* Accept a message from the reservation protocol and take
+ * appropriate action.
+ */
+static void
+rsrr_accept(recvlen)
+ int recvlen;
+{
+ struct rsrr_header *rsrr;
+ struct rsrr_rq *route_query;
+
+ if (recvlen < RSRR_HEADER_LEN) {
+ log(LOG_WARNING, 0,
+ "Received RSRR packet of %d bytes, which is less than min size",
+ recvlen);
+ return;
+ }
+
+ rsrr = (struct rsrr_header *) rsrr_recv_buf;
+
+ if (rsrr->version > RSRR_MAX_VERSION) {
+ log(LOG_WARNING, 0,
+ "Received RSRR packet version %d, which I don't understand",
+ rsrr->version);
+ return;
+ }
+
+ switch (rsrr->version) {
+ case 1:
+ switch (rsrr->type) {
+ case RSRR_INITIAL_QUERY:
+ /* Send Initial Reply to client */
+ IF_DEBUG(DEBUG_RSRR)
+ log(LOG_DEBUG, 0, "Received Initial Query\n");
+ rsrr_accept_iq();
+ break;
+ case RSRR_ROUTE_QUERY:
+ /* Check size */
+ if (recvlen < RSRR_RQ_LEN) {
+ log(LOG_WARNING, 0,
+ "Received Route Query of %d bytes, which is too small",
+ recvlen);
+ break;
+ }
+ /* Get the query */
+ route_query = (struct rsrr_rq *) (rsrr_recv_buf + RSRR_HEADER_LEN);
+ IF_DEBUG(DEBUG_RSRR)
+ log(LOG_DEBUG, 0,
+ "Received Route Query for src %s grp %s notification %d",
+ inet_fmt(route_query->source_addr.s_addr, s1),
+ inet_fmt(route_query->dest_addr.s_addr,s2),
+ BIT_TST(rsrr->flags,RSRR_NOTIFICATION_BIT));
+ /* Send Route Reply to client */
+ rsrr_accept_rq(route_query,rsrr->flags,NULL);
+ break;
+ default:
+ log(LOG_WARNING, 0,
+ "Received RSRR packet type %d, which I don't handle",
+ rsrr->type);
+ break;
+ }
+ break;
+
+ default:
+ log(LOG_WARNING, 0,
+ "Received RSRR packet version %d, which I don't understand",
+ rsrr->version);
+ break;
+ }
+}
+
+/* Send an Initial Reply to the reservation protocol. */
+static void
+rsrr_accept_iq()
+{
+ struct rsrr_header *rsrr;
+ struct rsrr_vif *vif_list;
+ struct uvif *v;
+ int vifi, sendlen;
+
+ /* Check for space. There should be room for plenty of vifs,
+ * but we should check anyway.
+ */
+ if (numvifs > RSRR_MAX_VIFS) {
+ log(LOG_WARNING, 0,
+ "Can't send RSRR Route Reply because %d is too many vifs",
+ numvifs);
+ return;
+ }
+
+ /* Set up message */
+ rsrr = (struct rsrr_header *) rsrr_send_buf;
+ rsrr->version = 1;
+ rsrr->type = RSRR_INITIAL_REPLY;
+ rsrr->flags = 0;
+ rsrr->num = numvifs;
+
+ vif_list = (struct rsrr_vif *) (rsrr_send_buf + RSRR_HEADER_LEN);
+
+ /* Include the vif list. */
+ for (vifi=0, v = uvifs; vifi < numvifs; vifi++, v++) {
+ vif_list[vifi].id = vifi;
+ vif_list[vifi].status = 0;
+ if (v->uv_flags & VIFF_DISABLED)
+ BIT_SET(vif_list[vifi].status,RSRR_DISABLED_BIT);
+ vif_list[vifi].threshold = v->uv_threshold;
+ vif_list[vifi].local_addr.s_addr = v->uv_lcl_addr;
+ }
+
+ /* Get the size. */
+ sendlen = RSRR_HEADER_LEN + numvifs*RSRR_VIF_LEN;
+
+ /* Send it. */
+ IF_DEBUG(DEBUG_RSRR)
+ log(LOG_DEBUG, 0, "Send RSRR Initial Reply");
+ rsrr_send(sendlen);
+}
+
+/* Send a Route Reply to the reservation protocol. The Route Query
+ * contains the query to which we are responding. The flags contain
+ * the incoming flags from the query or, for route change
+ * notification, the flags that should be set for the reply. The
+ * kernel table entry contains the routing info to use for a route
+ * change notification.
+ */
+static int
+rsrr_accept_rq(route_query,flags,gt_notify)
+ struct rsrr_rq *route_query;
+ int flags;
+ struct gtable *gt_notify;
+{
+ struct rsrr_header *rsrr;
+ struct rsrr_rr *route_reply;
+ struct gtable *gt,local_g;
+ struct rtentry *r;
+ int sendlen;
+ u_long mcastgrp;
+
+ /* Set up message */
+ rsrr = (struct rsrr_header *) rsrr_send_buf;
+ rsrr->version = 1;
+ rsrr->type = RSRR_ROUTE_REPLY;
+ rsrr->flags = 0;
+ rsrr->num = 0;
+
+ route_reply = (struct rsrr_rr *) (rsrr_send_buf + RSRR_HEADER_LEN);
+ route_reply->dest_addr.s_addr = route_query->dest_addr.s_addr;
+ route_reply->source_addr.s_addr = route_query->source_addr.s_addr;
+ route_reply->query_id = route_query->query_id;
+
+ /* Blank routing entry for error. */
+ route_reply->in_vif = 0;
+ route_reply->reserved = 0;
+ route_reply->out_vif_bm = 0;
+
+ /* Get the size. */
+ sendlen = RSRR_RR_LEN;
+
+ /* If kernel table entry is defined, then we are sending a Route Reply
+ * due to a Route Change Notification event. Use the kernel table entry
+ * to supply the routing info.
+ */
+ if (gt_notify) {
+ /* Set flags */
+ rsrr->flags = flags;
+ /* Include the routing entry. */
+ route_reply->in_vif = gt_notify->gt_route->rt_parent;
+ if (BIT_TST(flags,RSRR_NOTIFICATION_BIT))
+ route_reply->out_vif_bm = gt_notify->gt_grpmems;
+ else
+ route_reply->out_vif_bm = 0;
+ } else if (find_src_grp(route_query->source_addr.s_addr, 0,
+ route_query->dest_addr.s_addr)) {
+
+ /* Found kernel entry. Code taken from add_table_entry() */
+ gt = gtp ? gtp->gt_gnext : kernel_table;
+
+ /* Include the routing entry. */
+ route_reply->in_vif = gt->gt_route->rt_parent;
+ route_reply->out_vif_bm = gt->gt_grpmems;
+
+ /* Cache reply if using route change notification. */
+ if BIT_TST(flags,RSRR_NOTIFICATION_BIT) {
+ rsrr_cache(gt,route_query);
+ BIT_SET(rsrr->flags,RSRR_NOTIFICATION_BIT);
+ }
+
+ } else {
+ /* No kernel entry; use routing table. */
+ r = determine_route(route_query->source_addr.s_addr);
+
+ if (r != NULL) {
+ /* We need to mimic what will happen if a data packet
+ * is forwarded by multicast routing -- the kernel will
+ * make an upcall and mrouted will install a route in the kernel.
+ * Our outgoing vif bitmap should reflect what that table
+ * will look like. Grab code from add_table_entry().
+ * This is gross, but it's probably better to be accurate.
+ */
+
+ gt = &local_g;
+ mcastgrp = route_query->dest_addr.s_addr;
+
+ gt->gt_mcastgrp = mcastgrp;
+ gt->gt_grpmems = 0;
+ gt->gt_scope = 0;
+ gt->gt_route = r;
+
+ /* obtain the multicast group membership list */
+ determine_forwvifs(gt);
+
+ /* Include the routing entry. */
+ route_reply->in_vif = gt->gt_route->rt_parent;
+ route_reply->out_vif_bm = gt->gt_grpmems;
+
+ } else {
+ /* Set error bit. */
+ BIT_SET(rsrr->flags,RSRR_ERROR_BIT);
+ }
+ }
+
+ IF_DEBUG(DEBUG_RSRR)
+ log(LOG_DEBUG, 0, "%sSend RSRR Route Reply for src %s dst %s in vif %d out vif %d\n",
+ gt_notify ? "Route Change: " : "",
+ inet_fmt(route_reply->source_addr.s_addr,s1),
+ inet_fmt(route_reply->dest_addr.s_addr,s2),
+ route_reply->in_vif,route_reply->out_vif_bm);
+
+ /* Send it. */
+ return rsrr_send(sendlen);
+}
+
+/* Send an RSRR message. */
+static int
+rsrr_send(sendlen)
+ int sendlen;
+{
+ int error;
+
+ /* Send it. */
+ error = sendto(rsrr_socket, rsrr_send_buf, sendlen, 0,
+ (struct sockaddr *)&client_addr, client_length);
+
+ /* Check for errors. */
+ if (error < 0) {
+ log(LOG_WARNING, errno, "Failed send on RSRR socket");
+ } else if (error != sendlen) {
+ log(LOG_WARNING, 0,
+ "Sent only %d out of %d bytes on RSRR socket\n", error, sendlen);
+ }
+ return error;
+}
+
+/* Cache a message being sent to a client. Currently only used for
+ * caching Route Reply messages for route change notification.
+ */
+static void
+rsrr_cache(gt,route_query)
+ struct gtable *gt;
+ struct rsrr_rq *route_query;
+{
+ struct rsrr_cache *rc, **rcnp;
+ struct rsrr_header *rsrr;
+
+ rsrr = (struct rsrr_header *) rsrr_send_buf;
+
+ rcnp = &gt->gt_rsrr_cache;
+ while ((rc = *rcnp) != NULL) {
+ if ((rc->route_query.source_addr.s_addr ==
+ route_query->source_addr.s_addr) &&
+ (rc->route_query.dest_addr.s_addr ==
+ route_query->dest_addr.s_addr) &&
+ (!strcmp(rc->client_addr.sun_path,client_addr.sun_path))) {
+ /* Cache entry already exists.
+ * Check if route notification bit has been cleared.
+ */
+ if (!BIT_TST(rsrr->flags,RSRR_NOTIFICATION_BIT)) {
+ /* Delete cache entry. */
+ *rcnp = rc->next;
+ free(rc);
+ } else {
+ /* Update */
+ rc->route_query.query_id = route_query->query_id;
+ IF_DEBUG(DEBUG_RSRR)
+ log(LOG_DEBUG, 0,
+ "Update cached query id %ld from client %s\n",
+ rc->route_query.query_id, rc->client_addr.sun_path);
+ }
+ return;
+ }
+ rcnp = &rc->next;
+ }
+
+ /* Cache entry doesn't already exist. Create one and insert at
+ * front of list.
+ */
+ rc = (struct rsrr_cache *) malloc(sizeof(struct rsrr_cache));
+ if (rc == NULL)
+ log(LOG_ERR, 0, "ran out of memory");
+ rc->route_query.source_addr.s_addr = route_query->source_addr.s_addr;
+ rc->route_query.dest_addr.s_addr = route_query->dest_addr.s_addr;
+ rc->route_query.query_id = route_query->query_id;
+ strcpy(rc->client_addr.sun_path, client_addr.sun_path);
+ rc->client_length = client_length;
+ rc->next = gt->gt_rsrr_cache;
+ gt->gt_rsrr_cache = rc;
+ IF_DEBUG(DEBUG_RSRR)
+ log(LOG_DEBUG, 0, "Cached query id %ld from client %s\n",
+ rc->route_query.query_id,rc->client_addr.sun_path);
+}
+
+/* Send all the messages in the cache. Currently this is used to send
+ * all the cached Route Reply messages for route change notification.
+ */
+void
+rsrr_cache_send(gt,notify)
+ struct gtable *gt;
+ int notify;
+{
+ struct rsrr_cache *rc, **rcnp;
+ int flags = 0;
+
+ if (notify)
+ BIT_SET(flags,RSRR_NOTIFICATION_BIT);
+
+ rcnp = &gt->gt_rsrr_cache;
+ while ((rc = *rcnp) != NULL) {
+ if (rsrr_accept_rq(&rc->route_query,flags,gt) < 0) {
+ IF_DEBUG(DEBUG_RSRR)
+ log(LOG_DEBUG, 0, "Deleting cached query id %ld from client %s\n",
+ rc->route_query.query_id,rc->client_addr.sun_path);
+ /* Delete cache entry. */
+ *rcnp = rc->next;
+ free(rc);
+ } else {
+ rcnp = &rc->next;
+ }
+ }
+}
+
+/* Clean the cache by deleting all entries. */
+void
+rsrr_cache_clean(gt)
+ struct gtable *gt;
+{
+ struct rsrr_cache *rc,*rc_next;
+
+ IF_DEBUG(DEBUG_RSRR)
+ log(LOG_DEBUG, 0, "cleaning cache for group %s\n",
+ inet_fmt(gt->gt_mcastgrp, s1));
+ rc = gt->gt_rsrr_cache;
+ while (rc) {
+ rc_next = rc->next;
+ free(rc);
+ rc = rc_next;
+ }
+ gt->gt_rsrr_cache = NULL;
+}
+
+void
+rsrr_clean()
+{
+ unlink(RSRR_SERV_PATH);
+}
+
+#endif /* RSRR */
diff --git a/usr.sbin/mrouted/rsrr.h b/usr.sbin/mrouted/rsrr.h
new file mode 100644
index 0000000..c88f8ce
--- /dev/null
+++ b/usr.sbin/mrouted/rsrr.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 1993 by the University of Southern California
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation in source and binary forms for non-commercial purposes
+ * and without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both the copyright notice and
+ * this permission notice appear in supporting documentation. and that
+ * any documentation, advertising materials, and other materials related
+ * to such distribution and use acknowledge that the software was
+ * developed by the University of Southern California, Information
+ * Sciences Institute. The name of the University may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA makes no representations about
+ * the suitability of this software for any purpose. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ */
+
+#define RSRR_SERV_PATH "/var/run/rsrr_svr"
+/* Note this needs to be 14 chars for 4.3 BSD compatibility */
+/* Note This appears to be unused */
+#define RSRR_CLI_PATH "/var/run/rsrr_cli"
+
+#define RSRR_MAX_LEN 2048
+#define RSRR_HEADER_LEN (sizeof(struct rsrr_header))
+#define RSRR_RQ_LEN (RSRR_HEADER_LEN + sizeof(struct rsrr_rq))
+#define RSRR_RR_LEN (RSRR_HEADER_LEN + sizeof(struct rsrr_rr))
+#define RSRR_VIF_LEN (sizeof(struct rsrr_vif))
+
+/* Current maximum number of vifs. */
+#define RSRR_MAX_VIFS 32
+
+/* Maximum acceptable version */
+#define RSRR_MAX_VERSION 1
+
+/* RSRR message types */
+#define RSRR_ALL_TYPES 0
+#define RSRR_INITIAL_QUERY 1
+#define RSRR_INITIAL_REPLY 2
+#define RSRR_ROUTE_QUERY 3
+#define RSRR_ROUTE_REPLY 4
+
+/* RSRR Initial Reply (Vif) Status bits
+ * Each definition represents the position of the bit from right to left.
+ *
+ * Right-most bit is the disabled bit, set if the vif is administratively
+ * disabled.
+ */
+#define RSRR_DISABLED_BIT 0
+/* All other bits are zeroes */
+
+/* RSRR Route Query/Reply flag bits
+ * Each definition represents the position of the bit from right to left.
+ *
+ * Right-most bit is the Route Change Notification bit, set if the
+ * reservation protocol wishes to receive notification of
+ * a route change for the source-destination pair listed in the query.
+ * Notification is in the form of an unsolicitied Route Reply.
+ */
+#define RSRR_NOTIFICATION_BIT 0
+/* Next bit indicates an error returning the Route Reply. */
+#define RSRR_ERROR_BIT 1
+/* All other bits are zeroes */
+
+/* Definition of an RSRR message header.
+ * An Initial Query uses only the header, and an Initial Reply uses
+ * the header and a list of vifs.
+ */
+struct rsrr_header {
+ u_char version; /* RSRR Version, currently 1 */
+ u_char type; /* type of message, as defined above */
+ u_char flags; /* flags; defined by type */
+ u_char num; /* number; defined by type */
+};
+
+/* Definition of a vif as seen by the reservation protocol.
+ *
+ * Routing gives the reservation protocol a list of vifs in the
+ * Initial Reply.
+ *
+ * We explicitly list the ID because we can't assume that all routing
+ * protocols will use the same numbering scheme.
+ *
+ * The status is a bitmask of status flags, as defined above. It is the
+ * responsibility of the reservation protocol to perform any status checks
+ * if it uses the MULTICAST_VIF socket option.
+ *
+ * The threshold indicates the ttl an outgoing packet needs in order to
+ * be forwarded. The reservation protocol must perform this check itself if
+ * it uses the MULTICAST_VIF socket option.
+ *
+ * The local address is the address of the physical interface over which
+ * packets are sent.
+ */
+struct rsrr_vif {
+ u_char id; /* vif id */
+ u_char threshold; /* vif threshold ttl */
+ u_short status; /* vif status bitmask */
+ struct in_addr local_addr; /* vif local address */
+};
+
+/* Definition of an RSRR Route Query.
+ *
+ * The query asks routing for the forwarding entry for a particular
+ * source and destination. The query ID uniquely identifies the query
+ * for the reservation protocol. Thus, the combination of the client's
+ * address and the query ID forms a unique identifier for routing.
+ * Flags are defined above.
+ */
+struct rsrr_rq {
+ struct in_addr dest_addr; /* destination */
+ struct in_addr source_addr; /* source */
+ u_long query_id; /* query ID */
+};
+
+/* Definition of an RSRR Route Reply.
+ *
+ * Routing uses the reply to give the reservation protocol the
+ * forwarding entry for a source-destination pair. Routing copies the
+ * query ID from the query and fills in the incoming vif and a bitmask
+ * of the outgoing vifs.
+ * Flags are defined above.
+ */
+struct rsrr_rr {
+ struct in_addr dest_addr; /* destination */
+ struct in_addr source_addr; /* source */
+ u_long query_id; /* query ID */
+ u_short in_vif; /* incoming vif */
+ u_short reserved; /* reserved */
+ u_long out_vif_bm; /* outgoing vif bitmask */
+};
diff --git a/usr.sbin/mrouted/rsrr_var.h b/usr.sbin/mrouted/rsrr_var.h
new file mode 100644
index 0000000..9b1c09c
--- /dev/null
+++ b/usr.sbin/mrouted/rsrr_var.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 1993 by the University of Southern California
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation in source and binary forms for non-commercial purposes
+ * and without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both the copyright notice and
+ * this permission notice appear in supporting documentation. and that
+ * any documentation, advertising materials, and other materials related
+ * to such distribution and use acknowledge that the software was
+ * developed by the University of Southern California, Information
+ * Sciences Institute. The name of the University may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA makes no representations about
+ * the suitability of this software for any purpose. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ */
+
+/* RSRR things that are only needed by mrouted. */
+
+/* Cache of Route Query messages, distinguished by source,
+ * destination, and client addresses. Cache is flushed by RSRR client
+ * -- it sends notification when an unwanted Route Reply is received.
+ * Since this only happens during route changes, it is more likely
+ * that the cache will be flushed when the kernel table entry is
+ * deleted. */
+struct rsrr_cache {
+ struct rsrr_rq route_query; /* Cached Route Query */
+ struct sockaddr_un client_addr; /* Client address */
+ int client_length; /* Length of client */
+ struct rsrr_cache *next; /* next cache item */
+};
+
diff --git a/usr.sbin/mrouted/testrsrr/Makefile b/usr.sbin/mrouted/testrsrr/Makefile
new file mode 100644
index 0000000..131346d
--- /dev/null
+++ b/usr.sbin/mrouted/testrsrr/Makefile
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+S= ${.CURDIR}/..
+.PATH: $S
+
+PROG= testrsrr
+NOMAN= #true
+SRCS= testrsrr.c
+
+CFLAGS+= -I$S
+
+install:
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mrouted/testrsrr/testrsrr.c b/usr.sbin/mrouted/testrsrr/testrsrr.c
new file mode 100644
index 0000000..b18d6b0
--- /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.
+ *
+ * $FreeBSD$
+ */
+#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..423fa53
--- /dev/null
+++ b/usr.sbin/mrouted/vif.c
@@ -0,0 +1,1862 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * vif.c,v 3.8.4.56.2.1 1999/01/20 05:18:50 fenner Exp
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include "defs.h"
+#include <fcntl.h>
+
+/*
+ * Exported variables.
+ */
+struct uvif uvifs[MAXVIFS]; /* array of virtual interfaces */
+vifi_t numvifs; /* number of vifs in use */
+int vifs_down; /* 1=>some interfaces are down */
+int phys_vif; /* An enabled vif */
+int udp_socket; /* Since the honkin' kernel doesn't support */
+ /* ioctls on raw IP sockets, we need a UDP */
+ /* socket as well as our IGMP (raw) socket. */
+ /* How dumb. */
+int vifs_with_neighbors; /* == 1 if I am a leaf */
+
+/*
+ * Private variables.
+ */
+struct listaddr *nbrs[MAXNBRS]; /* array of neighbors */
+
+typedef struct {
+ vifi_t vifi;
+ struct listaddr *g;
+ int q_time;
+} cbk_t;
+
+/*
+ * Forward declarations.
+ */
+static void start_vif __P((vifi_t vifi));
+static void start_vif2 __P((vifi_t vifi));
+static void stop_vif __P((vifi_t vifi));
+static void age_old_hosts __P((void));
+static void send_probe_on_vif __P((struct uvif *v));
+static void send_query __P((struct uvif *v));
+static int info_version __P((char *p, int plen));
+static void DelVif __P((void *arg));
+static int SetTimer __P((int vifi, struct listaddr *g));
+static int DeleteTimer __P((int id));
+static void SendQuery __P((void *arg));
+static int SetQueryTimer __P((struct listaddr *g, vifi_t vifi, int to_expire,
+ int q_time));
+
+
+/*
+ * Initialize the virtual interfaces, but do not install
+ * them in the kernel. Start routing on all vifs that are
+ * not down or disabled.
+ */
+void
+init_vifs()
+{
+ vifi_t vifi;
+ struct uvif *v;
+ int enabled_vifs, enabled_phyints;
+ extern char *configfilename;
+
+ numvifs = 0;
+ vifs_with_neighbors = 0;
+ vifs_down = FALSE;
+
+ /*
+ * Configure the vifs based on the interface configuration of the
+ * the kernel and the contents of the configuration file.
+ * (Open a UDP socket for ioctl use in the config procedures if
+ * the kernel can't handle IOCTL's on the IGMP socket.)
+ */
+#ifdef IOCTL_OK_ON_RAW_SOCKET
+ udp_socket = igmp_socket;
+#else
+ if ((udp_socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+ log(LOG_ERR, errno, "UDP socket");
+#endif
+ log(LOG_INFO,0,"Getting vifs from kernel interfaces");
+ config_vifs_from_kernel();
+ log(LOG_INFO,0,"Getting vifs from %s",configfilename);
+ config_vifs_from_file();
+
+ /*
+ * Quit if there are fewer than two enabled vifs.
+ */
+ enabled_vifs = 0;
+ enabled_phyints = 0;
+ phys_vif = -1;
+ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+ if (!(v->uv_flags & VIFF_DISABLED)) {
+ ++enabled_vifs;
+ if (!(v->uv_flags & VIFF_TUNNEL)) {
+ if (phys_vif == -1)
+ phys_vif = vifi;
+ ++enabled_phyints;
+ }
+ }
+ }
+ if (enabled_vifs < 2)
+ log(LOG_ERR, 0, "can't forward: %s",
+ enabled_vifs == 0 ? "no enabled vifs" : "only one enabled vif");
+
+ if (enabled_phyints == 0)
+ log(LOG_WARNING, 0,
+ "no enabled interfaces, forwarding via tunnels only");
+
+ log(LOG_INFO, 0, "Installing vifs in mrouted...");
+ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+ if (!(v->uv_flags & VIFF_DISABLED)) {
+ if (!(v->uv_flags & VIFF_DOWN)) {
+ if (v->uv_flags & VIFF_TUNNEL)
+ log(LOG_INFO, 0, "vif #%d, tunnel %s -> %s", vifi,
+ inet_fmt(v->uv_lcl_addr, s1),
+ inet_fmt(v->uv_rmt_addr, s2));
+ else
+ log(LOG_INFO, 0, "vif #%d, phyint %s", vifi,
+ inet_fmt(v->uv_lcl_addr, s1));
+ start_vif2(vifi);
+ } else log(LOG_INFO, 0,
+ "%s is not yet up; vif #%u not in service",
+ v->uv_name, vifi);
+ }
+ }
+}
+
+/*
+ * Initialize the passed vif with all appropriate default values.
+ * "t" is true if a tunnel, or false if a phyint.
+ */
+void
+zero_vif(v, t)
+ struct uvif *v;
+ int t;
+{
+ v->uv_flags = 0;
+ v->uv_metric = DEFAULT_METRIC;
+ v->uv_admetric = 0;
+ v->uv_threshold = DEFAULT_THRESHOLD;
+ v->uv_rate_limit = t ? DEFAULT_TUN_RATE_LIMIT : DEFAULT_PHY_RATE_LIMIT;
+ v->uv_lcl_addr = 0;
+ v->uv_rmt_addr = 0;
+ v->uv_dst_addr = t ? 0 : dvmrp_group;
+ v->uv_subnet = 0;
+ v->uv_subnetmask = 0;
+ v->uv_subnetbcast = 0;
+ v->uv_name[0] = '\0';
+ v->uv_groups = NULL;
+ v->uv_neighbors = NULL;
+ NBRM_CLRALL(v->uv_nbrmap);
+ v->uv_querier = NULL;
+ v->uv_igmpv1_warn = 0;
+ v->uv_prune_lifetime = 0;
+ v->uv_leaf_timer = 0;
+ v->uv_acl = NULL;
+ v->uv_addrs = NULL;
+ v->uv_filter = NULL;
+ v->uv_blasterbuf = NULL;
+ v->uv_blastercur = NULL;
+ v->uv_blasterend = NULL;
+ v->uv_blasterlen = 0;
+ v->uv_blastertimer = 0;
+ v->uv_nbrup = 0;
+ v->uv_icmp_warn = 0;
+ v->uv_nroutes = 0;
+}
+
+/*
+ * Start routing on all virtual interfaces that are not down or
+ * administratively disabled.
+ */
+void
+init_installvifs()
+{
+ vifi_t vifi;
+ struct uvif *v;
+
+ log(LOG_INFO, 0, "Installing vifs in kernel...");
+ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+ if (!(v->uv_flags & VIFF_DISABLED)) {
+ if (!(v->uv_flags & VIFF_DOWN)) {
+ if (v->uv_flags & VIFF_TUNNEL)
+ log(LOG_INFO, 0, "vif #%d, tunnel %s -> %s", vifi,
+ inet_fmt(v->uv_lcl_addr, s1),
+ inet_fmt(v->uv_rmt_addr, s2));
+ else
+ log(LOG_INFO, 0, "vif #%d, phyint %s", vifi,
+ inet_fmt(v->uv_lcl_addr, s1));
+ k_add_vif(vifi, &uvifs[vifi]);
+ } else log(LOG_INFO, 0,
+ "%s is not yet up; vif #%u not in service",
+ v->uv_name, vifi);
+ }
+ }
+}
+
+/*
+ * See if any interfaces have changed from up state to down, or vice versa,
+ * including any non-multicast-capable interfaces that are in use as local
+ * tunnel end-points. Ignore interfaces that have been administratively
+ * disabled.
+ */
+void
+check_vif_state()
+{
+ register vifi_t vifi;
+ register struct uvif *v;
+ struct ifreq ifr;
+ static int checking_vifs = 0;
+
+ /*
+ * If we get an error while checking, (e.g. two interfaces go down
+ * at once, and we decide to send a prune out one of the failed ones)
+ * then don't go into an infinite loop!
+ */
+ if (checking_vifs)
+ return;
+
+ vifs_down = FALSE;
+ checking_vifs = 1;
+ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+
+ if (v->uv_flags & VIFF_DISABLED) continue;
+
+ strncpy(ifr.ifr_name, v->uv_name, IFNAMSIZ);
+ if (ioctl(udp_socket, SIOCGIFFLAGS, (char *)&ifr) < 0)
+ log(LOG_ERR, errno,
+ "ioctl SIOCGIFFLAGS for %s", ifr.ifr_name);
+
+ if (v->uv_flags & VIFF_DOWN) {
+ if (ifr.ifr_flags & IFF_UP) {
+ log(LOG_NOTICE, 0,
+ "%s has come up; vif #%u now in service",
+ v->uv_name, vifi);
+ v->uv_flags &= ~VIFF_DOWN;
+ start_vif(vifi);
+ }
+ else vifs_down = TRUE;
+ }
+ else {
+ if (!(ifr.ifr_flags & IFF_UP)) {
+ log(LOG_NOTICE, 0,
+ "%s has gone down; vif #%u taken out of service",
+ v->uv_name, vifi);
+ stop_vif(vifi);
+ v->uv_flags |= VIFF_DOWN;
+ vifs_down = TRUE;
+ }
+ }
+ }
+ checking_vifs = 0;
+}
+
+/*
+ * Send a DVMRP message on the specified vif. If DVMRP messages are
+ * to be encapsulated and sent "inside" the tunnel, use the special
+ * encapsulator. If it's not a tunnel or DVMRP messages are to be
+ * sent "beside" the tunnel, as required by earlier versions of mrouted,
+ * then just send the message.
+ */
+void
+send_on_vif(v, dst, code, datalen)
+ register struct uvif *v;
+ u_int32 dst;
+ int code;
+ int datalen;
+{
+ u_int32 group = htonl(MROUTED_LEVEL |
+ ((v->uv_flags & VIFF_LEAF) ? 0 : LEAF_FLAGS));
+
+ /*
+ * The UNIX kernel will not decapsulate unicasts.
+ * Therefore, we don't send encapsulated unicasts.
+ */
+ if ((v->uv_flags & (VIFF_TUNNEL|VIFF_OTUNNEL)) == VIFF_TUNNEL &&
+ ((dst == 0) || IN_MULTICAST(ntohl(dst))))
+ send_ipip(v->uv_lcl_addr, dst ? dst : dvmrp_group, IGMP_DVMRP,
+ code, group, datalen, v);
+ else
+ send_igmp(v->uv_lcl_addr, dst ? dst : v->uv_dst_addr, IGMP_DVMRP,
+ code, group, datalen);
+}
+
+
+/*
+ * Send a probe message on vif v
+ */
+static void
+send_probe_on_vif(v)
+ register struct uvif *v;
+{
+ register char *p;
+ register int datalen = 0;
+ struct listaddr *nbr;
+ int i;
+
+ if ((v->uv_flags & VIFF_PASSIVE && v->uv_neighbors == NULL) ||
+ (v->uv_flags & VIFF_FORCE_LEAF))
+ return;
+
+ p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
+
+ for (i = 0; i < 4; i++)
+ *p++ = ((char *)&(dvmrp_genid))[i];
+ datalen += 4;
+
+ /*
+ * add the neighbor list on the interface to the message
+ */
+ nbr = v->uv_neighbors;
+
+ while (nbr) {
+ for (i = 0; i < 4; i++)
+ *p++ = ((char *)&nbr->al_addr)[i];
+ datalen +=4;
+ nbr = nbr->al_next;
+ }
+
+ send_on_vif(v, 0, DVMRP_PROBE, datalen);
+}
+
+static void
+send_query(v)
+ register struct uvif *v;
+{
+ IF_DEBUG(DEBUG_IGMP)
+ log(LOG_DEBUG, 0, "sending %squery on vif %d",
+ (v->uv_flags & VIFF_IGMPV1) ? "v1 " : "",
+ v - uvifs);
+ send_igmp(v->uv_lcl_addr, allhosts_group,
+ IGMP_MEMBERSHIP_QUERY,
+ (v->uv_flags & VIFF_IGMPV1) ? 0 :
+ IGMP_MAX_HOST_REPORT_DELAY * IGMP_TIMER_SCALE, 0, 0);
+}
+
+/*
+ * Add a vifi to the kernel and start routing on it.
+ */
+static void
+start_vif(vifi)
+ vifi_t vifi;
+{
+ /*
+ * Install the interface in the kernel's vif structure.
+ */
+ k_add_vif(vifi, &uvifs[vifi]);
+
+ start_vif2(vifi);
+}
+
+/*
+ * Add a vifi to all the user-level data structures but don't add
+ * it to the kernel yet.
+ */
+static void
+start_vif2(vifi)
+ vifi_t vifi;
+{
+ struct uvif *v;
+ u_int32 src;
+ struct phaddr *p;
+
+ v = &uvifs[vifi];
+ src = v->uv_lcl_addr;
+
+ /*
+ * Update the existing route entries to take into account the new vif.
+ */
+ add_vif_to_routes(vifi);
+
+ if (!(v->uv_flags & VIFF_TUNNEL)) {
+ /*
+ * Join the DVMRP multicast group on the interface.
+ * (This is not strictly necessary, since the kernel promiscuously
+ * receives IGMP packets addressed to ANY IP multicast group while
+ * multicast routing is enabled. However, joining the group allows
+ * this host to receive non-IGMP packets as well, such as 'pings'.)
+ */
+ k_join(dvmrp_group, src);
+
+ /*
+ * Join the ALL-ROUTERS multicast group on the interface.
+ * This allows mtrace requests to loop back if they are run
+ * on the multicast router.
+ */
+ k_join(allrtrs_group, src);
+
+ /*
+ * Install an entry in the routing table for the subnet to which
+ * the interface is connected.
+ */
+ start_route_updates();
+ update_route(v->uv_subnet, v->uv_subnetmask, 0, 0, vifi, NULL);
+ for (p = v->uv_addrs; p; p = p->pa_next) {
+ start_route_updates();
+ update_route(p->pa_subnet, p->pa_subnetmask, 0, 0, vifi, NULL);
+ }
+
+ /*
+ * Until neighbors are discovered, assume responsibility for sending
+ * periodic group membership queries to the subnet. Send the first
+ * query.
+ */
+ v->uv_flags |= VIFF_QUERIER;
+ IF_DEBUG(DEBUG_IGMP)
+ log(LOG_DEBUG, 0, "assuming querier duties on vif %d", vifi);
+ send_query(v);
+ }
+
+ v->uv_leaf_timer = LEAF_CONFIRMATION_TIME;
+
+ /*
+ * Send a probe via the new vif to look for neighbors.
+ */
+ send_probe_on_vif(v);
+}
+
+/*
+ * Stop routing on the specified virtual interface.
+ */
+static void
+stop_vif(vifi)
+ vifi_t vifi;
+{
+ struct uvif *v;
+ struct listaddr *a;
+ struct phaddr *p;
+
+ v = &uvifs[vifi];
+
+ if (!(v->uv_flags & VIFF_TUNNEL)) {
+ /*
+ * Depart from the DVMRP multicast group on the interface.
+ */
+ k_leave(dvmrp_group, v->uv_lcl_addr);
+
+ /*
+ * Depart from the ALL-ROUTERS multicast group on the interface.
+ */
+ k_leave(allrtrs_group, v->uv_lcl_addr);
+
+ /*
+ * Update the entry in the routing table for the subnet to which
+ * the interface is connected, to take into account the interface
+ * failure.
+ */
+ start_route_updates();
+ update_route(v->uv_subnet, v->uv_subnetmask, UNREACHABLE, 0, vifi, NULL);
+ for (p = v->uv_addrs; p; p = p->pa_next) {
+ start_route_updates();
+ update_route(p->pa_subnet, p->pa_subnetmask, UNREACHABLE, 0, vifi, NULL);
+ }
+
+ /*
+ * Discard all group addresses. (No need to tell kernel;
+ * the k_del_vif() call, below, will clean up kernel state.)
+ */
+ while (v->uv_groups != NULL) {
+ a = v->uv_groups;
+ v->uv_groups = a->al_next;
+ free((char *)a);
+ }
+
+ IF_DEBUG(DEBUG_IGMP)
+ log(LOG_DEBUG, 0, "releasing querier duties on vif %d", vifi);
+ v->uv_flags &= ~VIFF_QUERIER;
+ }
+
+ /*
+ * Update the existing route entries to take into account the vif failure.
+ */
+ delete_vif_from_routes(vifi);
+
+ /*
+ * Delete the interface from the kernel's vif structure.
+ */
+ k_del_vif(vifi);
+
+ /*
+ * Discard all neighbor addresses.
+ */
+ if (!NBRM_ISEMPTY(v->uv_nbrmap))
+ vifs_with_neighbors--;
+
+ while (v->uv_neighbors != NULL) {
+ a = v->uv_neighbors;
+ v->uv_neighbors = a->al_next;
+ nbrs[a->al_index] = NULL;
+ free((char *)a);
+ }
+ NBRM_CLRALL(v->uv_nbrmap);
+}
+
+
+/*
+ * stop routing on all vifs
+ */
+void
+stop_all_vifs()
+{
+ vifi_t vifi;
+ struct uvif *v;
+ struct listaddr *a;
+ struct vif_acl *acl;
+
+ for (vifi = 0; vifi < numvifs; vifi++) {
+ v = &uvifs[vifi];
+ while (v->uv_groups != NULL) {
+ a = v->uv_groups;
+ v->uv_groups = a->al_next;
+ free((char *)a);
+ }
+ while (v->uv_neighbors != NULL) {
+ a = v->uv_neighbors;
+ v->uv_neighbors = a->al_next;
+ nbrs[a->al_index] = NULL;
+ free((char *)a);
+ }
+ while (v->uv_acl != NULL) {
+ acl = v->uv_acl;
+ v->uv_acl = acl->acl_next;
+ free((char *)acl);
+ }
+ }
+}
+
+
+/*
+ * Find the virtual interface from which an incoming packet arrived,
+ * based on the packet's source and destination IP addresses.
+ */
+vifi_t
+find_vif(src, dst)
+ register u_int32 src;
+ register u_int32 dst;
+{
+ register vifi_t vifi;
+ register struct uvif *v;
+ register struct phaddr *p;
+
+ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+ if (!(v->uv_flags & (VIFF_DOWN|VIFF_DISABLED))) {
+ if (v->uv_flags & VIFF_TUNNEL) {
+ if (src == v->uv_rmt_addr && (dst == v->uv_lcl_addr ||
+ dst == dvmrp_group))
+ return(vifi);
+ }
+ else {
+ if ((src & v->uv_subnetmask) == v->uv_subnet &&
+ ((v->uv_subnetmask == 0xffffffff) ||
+ (src != v->uv_subnetbcast)))
+ return(vifi);
+ for (p=v->uv_addrs; p; p=p->pa_next) {
+ if ((src & p->pa_subnetmask) == p->pa_subnet &&
+ ((p->pa_subnetmask == 0xffffffff) ||
+ (src != p->pa_subnetbcast)))
+ return(vifi);
+ }
+ }
+ }
+ }
+ return (NO_VIF);
+}
+
+static void
+age_old_hosts()
+{
+ register vifi_t vifi;
+ register struct uvif *v;
+ register struct listaddr *g;
+
+ /*
+ * Decrement the old-hosts-present timer for each
+ * active group on each vif.
+ */
+ for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++)
+ for (g = v->uv_groups; g != NULL; g = g->al_next)
+ if (g->al_old)
+ g->al_old--;
+}
+
+
+/*
+ * Send group membership queries on each interface for which I am querier.
+ * Note that technically, there should be a timer per interface, as the
+ * dynamics of querier election can cause the "right" time to send a
+ * query to be different on different interfaces. However, this simple
+ * implementation only ever sends queries sooner than the "right" time,
+ * so can not cause loss of membership (but can send more packets than
+ * necessary)
+ */
+void
+query_groups()
+{
+ register vifi_t vifi;
+ register struct uvif *v;
+
+ for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++) {
+ if (v->uv_flags & VIFF_QUERIER) {
+ send_query(v);
+ }
+ }
+ age_old_hosts();
+}
+
+/*
+ * Process an incoming host membership query. Warn about
+ * IGMP version mismatches, perform querier election, and
+ * handle group-specific queries when we're not the querier.
+ */
+void
+accept_membership_query(src, dst, group, tmo)
+ u_int32 src, dst, group;
+ int tmo;
+{
+ register vifi_t vifi;
+ register struct uvif *v;
+
+ if ((vifi = find_vif(src, dst)) == NO_VIF ||
+ (uvifs[vifi].uv_flags & VIFF_TUNNEL)) {
+ log(LOG_INFO, 0,
+ "ignoring group membership query from non-adjacent host %s",
+ inet_fmt(src, s1));
+ return;
+ }
+
+ v = &uvifs[vifi];
+
+ if ((tmo == 0 && !(v->uv_flags & VIFF_IGMPV1)) ||
+ (tmo != 0 && (v->uv_flags & VIFF_IGMPV1))) {
+ int i;
+
+ /*
+ * Exponentially back-off warning rate
+ */
+ i = ++v->uv_igmpv1_warn;
+ while (i && !(i & 1))
+ i >>= 1;
+ if (i == 1)
+ log(LOG_WARNING, 0, "%s %s on vif %d, %s",
+ tmo == 0 ? "Received IGMPv1 report from"
+ : "Received IGMPv2 report from",
+ inet_fmt(src, s1),
+ vifi,
+ tmo == 0 ? "please configure vif for IGMPv1"
+ : "but I am configured for IGMPv1");
+ }
+
+ if (v->uv_querier == NULL || v->uv_querier->al_addr != src) {
+ /*
+ * This might be:
+ * - A query from a new querier, with a lower source address
+ * than the current querier (who might be me)
+ * - A query from a new router that just started up and doesn't
+ * know who the querier is.
+ */
+ if (ntohl(src) < (v->uv_querier ? ntohl(v->uv_querier->al_addr)
+ : ntohl(v->uv_lcl_addr))) {
+ IF_DEBUG(DEBUG_IGMP)
+ log(LOG_DEBUG, 0, "new querier %s (was %s) on vif %d",
+ inet_fmt(src, s1),
+ v->uv_querier ? inet_fmt(v->uv_querier->al_addr, s2) :
+ "me", vifi);
+ if (!v->uv_querier) {
+ v->uv_querier = (struct listaddr *)
+ malloc(sizeof(struct listaddr));
+ v->uv_flags &= ~VIFF_QUERIER;
+ }
+ time(&v->uv_querier->al_ctime);
+ v->uv_querier->al_addr = src;
+ } else {
+ IF_DEBUG(DEBUG_IGMP)
+ log(LOG_DEBUG, 0, "ignoring query from %s; querier on vif %d is still %s",
+ inet_fmt(src, s1), vifi,
+ v->uv_querier ? inet_fmt(v->uv_querier->al_addr, s2) :
+ "me");
+
+ return;
+ }
+ }
+
+ /*
+ * Reset the timer since we've received a query.
+ */
+ if (v->uv_querier && src == v->uv_querier->al_addr)
+ v->uv_querier->al_timer = 0;
+
+ /*
+ * If this is a Group-Specific query which we did not source,
+ * we must set our membership timer to [Last Member Query Count] *
+ * the [Max Response Time] in the packet.
+ */
+ if (!(v->uv_flags & (VIFF_IGMPV1|VIFF_QUERIER)) && group != 0 &&
+ src != v->uv_lcl_addr) {
+ register struct listaddr *g;
+
+ IF_DEBUG(DEBUG_IGMP)
+ log(LOG_DEBUG, 0,
+ "%s for %s from %s on vif %d, timer %d",
+ "Group-specific membership query",
+ inet_fmt(group, s2), inet_fmt(src, s1), vifi, tmo);
+
+ for (g = v->uv_groups; g != NULL; g = g->al_next) {
+ if (group == g->al_addr && g->al_query == 0) {
+ /* setup a timeout to remove the group membership */
+ if (g->al_timerid)
+ g->al_timerid = DeleteTimer(g->al_timerid);
+ g->al_timer = IGMP_LAST_MEMBER_QUERY_COUNT *
+ tmo / IGMP_TIMER_SCALE;
+ /* use al_query to record our presence in last-member state */
+ g->al_query = -1;
+ g->al_timerid = SetTimer(vifi, g);
+ IF_DEBUG(DEBUG_IGMP)
+ log(LOG_DEBUG, 0,
+ "timer for grp %s on vif %d set to %d",
+ inet_fmt(group, s2), vifi, g->al_timer);
+ break;
+ }
+ }
+ }
+}
+
+/*
+ * Process an incoming group membership report.
+ */
+void
+accept_group_report(src, dst, group, r_type)
+ u_int32 src, dst, group;
+ int r_type;
+{
+ register vifi_t vifi;
+ register struct uvif *v;
+ register struct listaddr *g;
+
+ if ((vifi = find_vif(src, dst)) == NO_VIF ||
+ (uvifs[vifi].uv_flags & VIFF_TUNNEL)) {
+ log(LOG_INFO, 0,
+ "ignoring group membership report from non-adjacent host %s",
+ inet_fmt(src, s1));
+ return;
+ }
+
+ v = &uvifs[vifi];
+
+ /*
+ * Look for the group in our group list; if found, reset its timer.
+ */
+ for (g = v->uv_groups; g != NULL; g = g->al_next) {
+ if (group == g->al_addr) {
+ if (r_type == IGMP_V1_MEMBERSHIP_REPORT)
+ g->al_old = OLD_AGE_THRESHOLD;
+
+ g->al_reporter = src;
+
+ /** delete old timers, set a timer for expiration **/
+ g->al_timer = IGMP_GROUP_MEMBERSHIP_INTERVAL;
+ if (g->al_query)
+ g->al_query = DeleteTimer(g->al_query);
+ if (g->al_timerid)
+ g->al_timerid = DeleteTimer(g->al_timerid);
+ g->al_timerid = SetTimer(vifi, g);
+ break;
+ }
+ }
+
+ /*
+ * If not found, add it to the list and update kernel cache.
+ */
+ if (g == NULL) {
+ g = (struct listaddr *)malloc(sizeof(struct listaddr));
+ if (g == NULL)
+ log(LOG_ERR, 0, "ran out of memory"); /* fatal */
+
+ g->al_addr = group;
+ if (r_type == IGMP_V1_MEMBERSHIP_REPORT)
+ g->al_old = OLD_AGE_THRESHOLD;
+ else
+ g->al_old = 0;
+
+ /** set a timer for expiration **/
+ g->al_query = 0;
+ g->al_timer = IGMP_GROUP_MEMBERSHIP_INTERVAL;
+ g->al_reporter = src;
+ g->al_timerid = SetTimer(vifi, g);
+ g->al_next = v->uv_groups;
+ v->uv_groups = g;
+ time(&g->al_ctime);
+
+ update_lclgrp(vifi, group);
+ }
+
+ /*
+ * Check if a graft is necessary for this group
+ */
+ chkgrp_graft(vifi, group);
+}
+
+/*
+ * Process an incoming IGMPv2 Leave Group message.
+ */
+void
+accept_leave_message(src, dst, group)
+ u_int32 src, dst, group;
+{
+ register vifi_t vifi;
+ register struct uvif *v;
+ register struct listaddr *g;
+
+ if ((vifi = find_vif(src, dst)) == NO_VIF ||
+ (uvifs[vifi].uv_flags & VIFF_TUNNEL)) {
+ log(LOG_INFO, 0,
+ "ignoring group leave report from non-adjacent host %s",
+ inet_fmt(src, s1));
+ return;
+ }
+
+ v = &uvifs[vifi];
+
+ if (!(v->uv_flags & VIFF_QUERIER) || (v->uv_flags & VIFF_IGMPV1))
+ return;
+
+ /*
+ * Look for the group in our group list in order to set up a short-timeout
+ * query.
+ */
+ for (g = v->uv_groups; g != NULL; g = g->al_next) {
+ if (group == g->al_addr) {
+ IF_DEBUG(DEBUG_IGMP)
+ log(LOG_DEBUG, 0,
+ "[vif.c, _accept_leave_message] %d %d \n",
+ g->al_old, g->al_query);
+
+ /* Ignore the leave message if there are old hosts present */
+ if (g->al_old)
+ return;
+
+ /* still waiting for a reply to a query, ignore the leave */
+ if (g->al_query)
+ return;
+
+ /** delete old timer set a timer for expiration **/
+ if (g->al_timerid)
+ g->al_timerid = DeleteTimer(g->al_timerid);
+
+#if IGMP_LAST_MEMBER_QUERY_COUNT != 2
+This code needs to be updated to keep a counter of the number
+of queries remaining.
+#endif
+ /** send a group specific querry **/
+ g->al_timer = IGMP_LAST_MEMBER_QUERY_INTERVAL *
+ (IGMP_LAST_MEMBER_QUERY_COUNT + 1);
+ send_igmp(v->uv_lcl_addr, g->al_addr,
+ IGMP_MEMBERSHIP_QUERY,
+ IGMP_LAST_MEMBER_QUERY_INTERVAL * IGMP_TIMER_SCALE,
+ g->al_addr, 0);
+ g->al_query = SetQueryTimer(g, vifi,
+ IGMP_LAST_MEMBER_QUERY_INTERVAL,
+ IGMP_LAST_MEMBER_QUERY_INTERVAL * IGMP_TIMER_SCALE);
+ g->al_timerid = SetTimer(vifi, g);
+ break;
+ }
+ }
+}
+
+
+/*
+ * Send a periodic probe on all vifs.
+ * Useful to determine one-way interfaces.
+ * Detect neighbor loss faster.
+ */
+void
+probe_for_neighbors()
+{
+ register vifi_t vifi;
+ register struct uvif *v;
+
+ for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++) {
+ if (!(v->uv_flags & (VIFF_DOWN|VIFF_DISABLED))) {
+ send_probe_on_vif(v);
+ }
+ }
+}
+
+
+/*
+ * Send a list of all of our neighbors to the requestor, `src'.
+ */
+void
+accept_neighbor_request(src, dst)
+ u_int32 src, dst;
+{
+ vifi_t vifi;
+ struct uvif *v;
+ u_char *p, *ncount;
+ struct listaddr *la;
+ int datalen;
+ u_int32 temp_addr, them = src;
+
+#define PUT_ADDR(a) temp_addr = ntohl(a); \
+ *p++ = temp_addr >> 24; \
+ *p++ = (temp_addr >> 16) & 0xFF; \
+ *p++ = (temp_addr >> 8) & 0xFF; \
+ *p++ = temp_addr & 0xFF;
+
+ p = (u_char *) (send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN);
+ datalen = 0;
+
+ for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++) {
+ if (v->uv_flags & VIFF_DISABLED)
+ continue;
+
+ ncount = 0;
+
+ for (la = v->uv_neighbors; la; la = la->al_next) {
+
+ /* Make sure that there's room for this neighbor... */
+ if (datalen + (ncount == 0 ? 4 + 3 + 4 : 4) > MAX_DVMRP_DATA_LEN) {
+ send_igmp(INADDR_ANY, them, IGMP_DVMRP, DVMRP_NEIGHBORS,
+ htonl(MROUTED_LEVEL), datalen);
+ p = (u_char *) (send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN);
+ datalen = 0;
+ ncount = 0;
+ }
+
+ /* Put out the header for this neighbor list... */
+ if (ncount == 0) {
+ PUT_ADDR(v->uv_lcl_addr);
+ *p++ = v->uv_metric;
+ *p++ = v->uv_threshold;
+ ncount = p;
+ *p++ = 0;
+ datalen += 4 + 3;
+ }
+
+ PUT_ADDR(la->al_addr);
+ datalen += 4;
+ (*ncount)++;
+ }
+ }
+
+ if (datalen != 0)
+ send_igmp(INADDR_ANY, them, IGMP_DVMRP, DVMRP_NEIGHBORS,
+ htonl(MROUTED_LEVEL), datalen);
+}
+
+/*
+ * Send a list of all of our neighbors to the requestor, `src'.
+ */
+void
+accept_neighbor_request2(src, dst)
+ u_int32 src, dst;
+{
+ vifi_t vifi;
+ struct uvif *v;
+ u_char *p, *ncount;
+ struct listaddr *la;
+ int datalen;
+ u_int32 them = src;
+
+ p = (u_char *) (send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN);
+ datalen = 0;
+
+ for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++) {
+ register u_short vflags = v->uv_flags;
+ register u_char rflags = 0;
+ if (vflags & VIFF_TUNNEL)
+ rflags |= DVMRP_NF_TUNNEL;
+ if (vflags & VIFF_SRCRT)
+ rflags |= DVMRP_NF_SRCRT;
+ if (vflags & VIFF_DOWN)
+ rflags |= DVMRP_NF_DOWN;
+ if (vflags & VIFF_DISABLED)
+ rflags |= DVMRP_NF_DISABLED;
+ if (vflags & VIFF_QUERIER)
+ rflags |= DVMRP_NF_QUERIER;
+ if (vflags & VIFF_LEAF)
+ rflags |= DVMRP_NF_LEAF;
+ ncount = 0;
+ la = v->uv_neighbors;
+ if (la == NULL) {
+ /*
+ * include down & disabled interfaces and interfaces on
+ * leaf nets.
+ */
+ if (rflags & DVMRP_NF_TUNNEL)
+ rflags |= DVMRP_NF_DOWN;
+ if (datalen > MAX_DVMRP_DATA_LEN - 12) {
+ send_igmp(INADDR_ANY, them, IGMP_DVMRP, DVMRP_NEIGHBORS2,
+ htonl(MROUTED_LEVEL), datalen);
+ p = (u_char *) (send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN);
+ datalen = 0;
+ }
+ *(u_int*)p = v->uv_lcl_addr;
+ p += 4;
+ *p++ = v->uv_metric;
+ *p++ = v->uv_threshold;
+ *p++ = rflags;
+ *p++ = 1;
+ *(u_int*)p = v->uv_rmt_addr;
+ p += 4;
+ datalen += 12;
+ } else {
+ for ( ; la; la = la->al_next) {
+ /* Make sure that there's room for this neighbor... */
+ if (datalen + (ncount == 0 ? 4+4+4 : 4) > MAX_DVMRP_DATA_LEN) {
+ send_igmp(INADDR_ANY, them, IGMP_DVMRP, DVMRP_NEIGHBORS2,
+ htonl(MROUTED_LEVEL), datalen);
+ p = (u_char *) (send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN);
+ datalen = 0;
+ ncount = 0;
+ }
+ /* Put out the header for this neighbor list... */
+ if (ncount == 0) {
+ /* If it's a one-way tunnel, mark it down. */
+ if (rflags & DVMRP_NF_TUNNEL && la->al_flags & NBRF_ONEWAY)
+ rflags |= DVMRP_NF_DOWN;
+ *(u_int*)p = v->uv_lcl_addr;
+ p += 4;
+ *p++ = v->uv_metric;
+ *p++ = v->uv_threshold;
+ *p++ = rflags;
+ ncount = p;
+ *p++ = 0;
+ datalen += 4 + 4;
+ }
+ /* Don't report one-way peering on phyint at all */
+ if (!(rflags & DVMRP_NF_TUNNEL) && la->al_flags & NBRF_ONEWAY)
+ continue;
+ *(u_int*)p = la->al_addr;
+ p += 4;
+ datalen += 4;
+ (*ncount)++;
+ }
+ if (*ncount == 0) {
+ *(u_int*)p = v->uv_rmt_addr;
+ p += 4;
+ datalen += 4;
+ (*ncount)++;
+ }
+ }
+ }
+ if (datalen != 0)
+ send_igmp(INADDR_ANY, them, IGMP_DVMRP, DVMRP_NEIGHBORS2,
+ htonl(MROUTED_LEVEL), datalen);
+}
+
+void
+accept_info_request(src, dst, p, datalen)
+ u_int32 src, dst;
+ u_char *p;
+ int datalen;
+{
+ u_char *q;
+ int len;
+ int outlen = 0;
+
+ q = (u_char *) (send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN);
+
+ /* To be general, this must deal properly with breaking up over-sized
+ * packets. That implies passing a length to each function, and
+ * allowing each function to request to be called again. Right now,
+ * we're only implementing the one thing we are positive will fit into
+ * a single packet, so we wimp out.
+ */
+ while (datalen > 0) {
+ len = 0;
+ switch (*p) {
+ case DVMRP_INFO_VERSION:
+ len = info_version(q, RECV_BUF_SIZE-(q-(u_char *)send_buf));
+ break;
+
+ case DVMRP_INFO_NEIGHBORS:
+ default:
+ log(LOG_INFO, 0, "ignoring unknown info type %d", *p);
+ break;
+ }
+ *(q+1) = len++;
+ outlen += len * 4;
+ q += len * 4;
+ len = (*(p+1) + 1) * 4;
+ p += len;
+ datalen -= len;
+ }
+
+ if (outlen != 0)
+ send_igmp(INADDR_ANY, src, IGMP_DVMRP, DVMRP_INFO_REPLY,
+ htonl(MROUTED_LEVEL), outlen);
+}
+
+/*
+ * Information response -- return version string
+ */
+static int
+info_version(p, plen)
+ char *p;
+ int plen;
+{
+ int len;
+ extern char versionstring[];
+
+ *p++ = DVMRP_INFO_VERSION;
+ p++; /* skip over length */
+ *p++ = 0; /* zero out */
+ *p++ = 0; /* reserved fields */
+ strncpy(p, versionstring, plen - 4);
+ p[plen-5] = '\0';
+
+ len = strlen(versionstring);
+ return ((len + 3) / 4);
+}
+
+/*
+ * Process an incoming neighbor-list message.
+ */
+void
+accept_neighbors(src, dst, p, datalen, level)
+ u_int32 src, dst, level;
+ u_char *p;
+ int datalen;
+{
+ log(LOG_INFO, 0, "ignoring spurious DVMRP neighbor list from %s to %s",
+ inet_fmt(src, s1), inet_fmt(dst, s2));
+}
+
+
+/*
+ * Process an incoming neighbor-list message.
+ */
+void
+accept_neighbors2(src, dst, p, datalen, level)
+ u_int32 src, dst, level;
+ u_char *p;
+ int datalen;
+{
+ IF_DEBUG(DEBUG_PKT)
+ log(LOG_DEBUG, 0, "ignoring spurious DVMRP neighbor list2 from %s to %s",
+ inet_fmt(src, s1), inet_fmt(dst, s2));
+}
+
+/*
+ * Process an incoming info reply message.
+ */
+void
+accept_info_reply(src, dst, p, datalen)
+ u_int32 src, dst;
+ u_char *p;
+ int datalen;
+{
+ IF_DEBUG(DEBUG_PKT)
+ log(LOG_DEBUG, 0, "ignoring spurious DVMRP info reply from %s to %s",
+ inet_fmt(src, s1), inet_fmt(dst, s2));
+}
+
+
+/*
+ * Update the neighbor entry for neighbor 'addr' on vif 'vifi'.
+ * 'msgtype' is the type of DVMRP message received from the neighbor.
+ * Return the neighbor entry if 'addr' is a valid neighbor, FALSE otherwise.
+ */
+struct listaddr *
+update_neighbor(vifi, addr, msgtype, p, datalen, level)
+ vifi_t vifi;
+ u_int32 addr;
+ int msgtype;
+ char *p;
+ int datalen;
+ u_int32 level;
+{
+ register struct uvif *v;
+ register struct listaddr *n;
+ int pv = level & 0xff;
+ int mv = (level >> 8) & 0xff;
+ int has_genid = 0;
+ int in_router_list = 0;
+ int dvmrpspec = 0;
+ u_int32 genid;
+ u_int32 send_tables = 0;
+ int i;
+ int do_reset = FALSE;
+
+ v = &uvifs[vifi];
+
+ /*
+ * Confirm that 'addr' is a valid neighbor address on vif 'vifi'.
+ * IT IS ASSUMED that this was preceded by a call to find_vif(), which
+ * checks that 'addr' is either a valid remote tunnel endpoint or a
+ * non-broadcast address belonging to a directly-connected subnet.
+ * Therefore, here we check only that 'addr' is not our own address
+ * (due to an impostor or erroneous loopback) or an address of the form
+ * {subnet,0} ("the unknown host"). These checks are not performed in
+ * find_vif() because those types of address are acceptable for some
+ * types of IGMP message (such as group membership reports).
+ */
+ if (!(v->uv_flags & VIFF_TUNNEL) &&
+ (addr == v->uv_lcl_addr ||
+ addr == v->uv_subnet )) {
+ log(LOG_WARNING, 0,
+ "received DVMRP message from %s: %s",
+ (addr == v->uv_lcl_addr) ? "self (check device loopback)" :
+ "'the unknown host'",
+ inet_fmt(addr, s1));
+ return NULL;
+ }
+
+ /*
+ * Ignore all neighbors on vifs forced into leaf mode
+ */
+ if (v->uv_flags & VIFF_FORCE_LEAF) {
+ return NULL;
+ }
+
+ /*
+ * mrouted's version 3.3 and later include the generation ID
+ * and the list of neighbors on the vif in their probe messages.
+ */
+ if (msgtype == DVMRP_PROBE && ((pv == 3 && mv > 2) ||
+ (pv > 3 && pv < 10))) {
+ u_int32 router;
+
+ IF_DEBUG(DEBUG_PEER)
+ log(LOG_DEBUG, 0, "checking probe from %s (%d.%d) on vif %d",
+ inet_fmt(addr, s1), pv, mv, vifi);
+
+ if (datalen < 4) {
+ log(LOG_WARNING, 0,
+ "received truncated probe message from %s (len %d)",
+ inet_fmt(addr, s1), datalen);
+ return NULL;
+ }
+
+ has_genid = 1;
+
+ for (i = 0; i < 4; i++)
+ ((char *)&genid)[i] = *p++;
+ datalen -= 4;
+
+ while (datalen > 0) {
+ if (datalen < 4) {
+ log(LOG_WARNING, 0,
+ "received truncated probe message from %s (len %d)",
+ inet_fmt(addr, s1), datalen);
+ return NULL;
+ }
+ for (i = 0; i < 4; i++)
+ ((char *)&router)[i] = *p++;
+ datalen -= 4;
+
+ if (router == v->uv_lcl_addr) {
+ in_router_list = 1;
+ break;
+ }
+ }
+ }
+
+ if ((pv == 3 && mv == 255) || (pv > 3 && pv < 10))
+ dvmrpspec = 1;
+
+ /*
+ * Look for addr in list of neighbors.
+ */
+ for (n = v->uv_neighbors; n != NULL; n = n->al_next) {
+ if (addr == n->al_addr) {
+ break;
+ }
+ }
+
+ if (n == NULL) {
+ /*
+ * New neighbor.
+ *
+ * If this neighbor follows the DVMRP spec, start the probe
+ * handshake. If not, then it doesn't require the probe
+ * handshake, so establish the peering immediately.
+ */
+ if (dvmrpspec && (msgtype != DVMRP_PROBE))
+ return NULL;
+
+ for (i = 0; i < MAXNBRS; i++)
+ if (nbrs[i] == NULL)
+ break;
+
+ if (i == MAXNBRS) {
+ /* XXX This is a severe new restriction. */
+ /* XXX want extensible bitmaps! */
+ log(LOG_ERR, 0, "Can't handle %dth neighbor %s on vif %d!",
+ MAXNBRS, inet_fmt(addr, s1), vifi);
+ /*NOTREACHED*/
+ }
+
+ /*
+ * Add it to our list of neighbors.
+ */
+ IF_DEBUG(DEBUG_PEER)
+ log(LOG_DEBUG, 0, "New neighbor %s on vif %d v%d.%d nf 0x%02x idx %d",
+ inet_fmt(addr, s1), vifi, level & 0xff, (level >> 8) & 0xff,
+ (level >> 16) & 0xff, i);
+
+ n = (struct listaddr *)malloc(sizeof(struct listaddr));
+ if (n == NULL)
+ log(LOG_ERR, 0, "ran out of memory"); /* fatal */
+
+ n->al_addr = addr;
+ n->al_pv = pv;
+ n->al_mv = mv;
+ n->al_genid = has_genid ? genid : 0;
+ n->al_index = i;
+ nbrs[i] = n;
+
+ time(&n->al_ctime);
+ n->al_timer = 0;
+ n->al_flags = has_genid ? NBRF_GENID : 0;
+ n->al_next = v->uv_neighbors;
+ v->uv_neighbors = n;
+
+ /*
+ * If we are not configured to peer with non-pruning routers,
+ * check the deprecated "I-know-how-to-prune" bit. This bit
+ * was MBZ in early mrouted implementations (<3.5) and is required
+ * to be set by the DVMRPv3 specification.
+ */
+ if (!(v->uv_flags & VIFF_ALLOW_NONPRUNERS) &&
+ !((level & 0x020000) || (pv == 3 && mv < 5))) {
+ n->al_flags |= NBRF_TOOOLD;
+ }
+
+ /*
+ * If this router implements the DVMRPv3 spec, then don't peer
+ * with him if we haven't yet established a bidirectional connection.
+ */
+ if (dvmrpspec) {
+ if (!in_router_list) {
+ IF_DEBUG(DEBUG_PEER)
+ log(LOG_DEBUG, 0, "waiting for probe from %s with my addr",
+ inet_fmt(addr, s1));
+ n->al_flags |= NBRF_WAITING;
+ return NULL;
+ }
+ }
+
+ if (n->al_flags & NBRF_DONTPEER) {
+ IF_DEBUG(DEBUG_PEER)
+ log(LOG_DEBUG, 0, "not peering with %s on vif %d because %x",
+ inet_fmt(addr, s1), vifi, n->al_flags & NBRF_DONTPEER);
+ return NULL;
+ }
+
+ /*
+ * If we thought that we had no neighbors on this vif, send a route
+ * report to the vif. If this is just a new neighbor on the same
+ * vif, send the route report just to the new neighbor.
+ */
+ if (NBRM_ISEMPTY(v->uv_nbrmap)) {
+ send_tables = v->uv_dst_addr;
+ vifs_with_neighbors++;
+ } else {
+ send_tables = addr;
+ }
+
+
+ NBRM_SET(i, v->uv_nbrmap);
+ add_neighbor_to_routes(vifi, i);
+ } else {
+ /*
+ * Found it. Reset its timer.
+ */
+ n->al_timer = 0;
+
+ if (n->al_flags & NBRF_WAITING && msgtype == DVMRP_PROBE) {
+ n->al_flags &= ~NBRF_WAITING;
+ if (!in_router_list) {
+ log(LOG_WARNING, 0, "possible one-way peering with %s on vif %d",
+ inet_fmt(addr, s1), vifi);
+ n->al_flags |= NBRF_ONEWAY;
+ return NULL;
+ } else {
+ if (NBRM_ISEMPTY(v->uv_nbrmap)) {
+ send_tables = v->uv_dst_addr;
+ vifs_with_neighbors++;
+ } else {
+ send_tables = addr;
+ }
+ NBRM_SET(n->al_index, v->uv_nbrmap);
+ add_neighbor_to_routes(vifi, n->al_index);
+ IF_DEBUG(DEBUG_PEER)
+ log(LOG_DEBUG, 0, "%s on vif %d exits WAITING",
+ inet_fmt(addr, s1), vifi);
+ }
+ }
+
+ if (n->al_flags & NBRF_ONEWAY && msgtype == DVMRP_PROBE) {
+ if (in_router_list) {
+ if (NBRM_ISEMPTY(v->uv_nbrmap))
+ vifs_with_neighbors++;
+ NBRM_SET(n->al_index, v->uv_nbrmap);
+ add_neighbor_to_routes(vifi, n->al_index);
+ log(LOG_NOTICE, 0, "peering with %s on vif %d is no longer one-way",
+ inet_fmt(addr, s1), vifi);
+ n->al_flags &= ~NBRF_ONEWAY;
+ } else {
+ /* XXX rate-limited warning message? */
+ IF_DEBUG(DEBUG_PEER)
+ log(LOG_DEBUG, 0, "%s on vif %d is still ONEWAY",
+ inet_fmt(addr, s1), vifi);
+ }
+ }
+
+ /*
+ * When peering with a genid-capable but pre-DVMRP spec peer,
+ * we might bring up the peering with a route report and not
+ * remember his genid. Assume that he doesn't send a route
+ * report and then reboot before sending a probe.
+ */
+ if (has_genid && !(n->al_flags & NBRF_GENID)) {
+ n->al_flags |= NBRF_GENID;
+ n->al_genid = genid;
+ }
+
+ /*
+ * update the neighbors version and protocol number and genid
+ * if changed => router went down and came up,
+ * so take action immediately.
+ */
+ if ((n->al_pv != pv) ||
+ (n->al_mv != mv) ||
+ (has_genid && n->al_genid != genid)) {
+
+ do_reset = TRUE;
+ IF_DEBUG(DEBUG_PEER)
+ log(LOG_DEBUG, 0,
+ "version/genid change neighbor %s [old:%d.%d/%8x, new:%d.%d/%8x]",
+ inet_fmt(addr, s1),
+ n->al_pv, n->al_mv, n->al_genid, pv, mv, genid);
+
+ n->al_pv = pv;
+ n->al_mv = mv;
+ n->al_genid = genid;
+ time(&n->al_ctime);
+ }
+
+ if ((pv == 3 && mv > 2) || (pv > 3 && pv < 10)) {
+ if (!(n->al_flags & VIFF_ONEWAY) && has_genid && !in_router_list &&
+ (time(NULL) - n->al_ctime > 20)) {
+ if (NBRM_ISSET(n->al_index, v->uv_nbrmap)) {
+ NBRM_CLR(n->al_index, v->uv_nbrmap);
+ if (NBRM_ISEMPTY(v->uv_nbrmap))
+ vifs_with_neighbors--;
+ }
+ delete_neighbor_from_routes(addr, vifi, n->al_index);
+ reset_neighbor_state(vifi, addr);
+ log(LOG_WARNING, 0, "peering with %s on vif %d is one-way",
+ inet_fmt(addr, s1), vifi);
+ n->al_flags |= NBRF_ONEWAY;
+ }
+ }
+
+ if (n->al_flags & NBRF_DONTPEER) {
+ IF_DEBUG(DEBUG_PEER)
+ log(LOG_DEBUG, 0, "not peering with %s on vif %d because %x",
+ inet_fmt(addr, s1), vifi, n->al_flags & NBRF_DONTPEER);
+ return NULL;
+ }
+
+ /* check "leaf" flag */
+ }
+ if (do_reset) {
+ reset_neighbor_state(vifi, addr);
+ if (!send_tables)
+ send_tables = addr;
+ }
+ if (send_tables) {
+ send_probe_on_vif(v);
+ report(ALL_ROUTES, vifi, send_tables);
+ }
+ v->uv_leaf_timer = 0;
+ v->uv_flags &= ~VIFF_LEAF;
+
+ return n;
+}
+
+
+/*
+ * On every timer interrupt, advance the timer in each neighbor and
+ * group entry on every vif.
+ */
+void
+age_vifs()
+{
+ register vifi_t vifi;
+ register struct uvif *v;
+ register struct listaddr *a, *prev_a;
+ register u_int32 addr;
+
+ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v ) {
+ if (v->uv_leaf_timer && (v->uv_leaf_timer -= TIMER_INTERVAL == 0)) {
+ v->uv_flags |= VIFF_LEAF;
+ }
+
+ for (prev_a = (struct listaddr *)&(v->uv_neighbors),
+ a = v->uv_neighbors;
+ a != NULL;
+ prev_a = a, a = a->al_next) {
+ int exp_time;
+ int idx;
+
+ if (((a->al_pv == 3) && (a->al_mv >= 3)) ||
+ ((a->al_pv > 3) && (a->al_pv < 10)))
+ exp_time = NEIGHBOR_EXPIRE_TIME;
+ else
+ exp_time = OLD_NEIGHBOR_EXPIRE_TIME;
+
+ if ((a->al_timer += TIMER_INTERVAL) < exp_time)
+ continue;
+
+ IF_DEBUG(DEBUG_PEER)
+ log(LOG_DEBUG, 0, "Neighbor %s (%d.%d) expired after %d seconds",
+ inet_fmt(a->al_addr, s1), a->al_pv, a->al_mv, exp_time);
+
+ /*
+ * Neighbor has expired; delete it from the neighbor list,
+ * delete it from the 'dominants' and 'subordinates arrays of
+ * any route entries.
+ */
+ NBRM_CLR(a->al_index, v->uv_nbrmap);
+ nbrs[a->al_index] = NULL; /* XXX is it a good idea to reuse indxs? */
+ idx = a->al_index;
+ addr = a->al_addr;
+ prev_a->al_next = a->al_next;
+ free((char *)a);
+ a = prev_a;/*XXX use ** */
+
+ delete_neighbor_from_routes(addr, vifi, idx);
+ reset_neighbor_state(vifi, addr);
+
+ if (NBRM_ISEMPTY(v->uv_nbrmap))
+ vifs_with_neighbors--;
+
+ v->uv_leaf_timer = LEAF_CONFIRMATION_TIME;
+ }
+
+ if (v->uv_querier &&
+ (v->uv_querier->al_timer += TIMER_INTERVAL) >
+ IGMP_OTHER_QUERIER_PRESENT_INTERVAL) {
+ /*
+ * The current querier has timed out. We must become the
+ * querier.
+ */
+ IF_DEBUG(DEBUG_IGMP)
+ log(LOG_DEBUG, 0, "querier %s timed out",
+ inet_fmt(v->uv_querier->al_addr, s1));
+ free(v->uv_querier);
+ v->uv_querier = NULL;
+ v->uv_flags |= VIFF_QUERIER;
+ send_query(v);
+ }
+ }
+}
+
+/*
+ * Returns the neighbor info struct for a given neighbor
+ */
+struct listaddr *
+neighbor_info(vifi, addr)
+ vifi_t vifi;
+ u_int32 addr;
+{
+ struct listaddr *u;
+
+ for (u = uvifs[vifi].uv_neighbors; u; u = u->al_next)
+ if (u->al_addr == addr)
+ return u;
+
+ return NULL;
+}
+
+static struct vnflags {
+ int vn_flag;
+ char *vn_name;
+} vifflags[] = {
+ { VIFF_DOWN, "down" },
+ { VIFF_DISABLED, "disabled" },
+ { VIFF_QUERIER, "querier" },
+ { VIFF_ONEWAY, "one-way" },
+ { VIFF_LEAF, "leaf" },
+ { VIFF_IGMPV1, "IGMPv1" },
+ { VIFF_REXMIT_PRUNES, "rexmit_prunes" },
+ { VIFF_PASSIVE, "passive" },
+ { VIFF_ALLOW_NONPRUNERS,"allow_nonpruners" },
+ { VIFF_NOFLOOD, "noflood" },
+ { VIFF_NOTRANSIT, "notransit" },
+ { VIFF_BLASTER, "blaster" },
+ { VIFF_FORCE_LEAF, "force_leaf" },
+ { VIFF_OTUNNEL, "old-tunnel" },
+};
+
+static struct vnflags nbrflags[] = {
+ { NBRF_LEAF, "leaf" },
+ { NBRF_GENID, "have-genid" },
+ { NBRF_WAITING, "waiting" },
+ { NBRF_ONEWAY, "one-way" },
+ { NBRF_TOOOLD, "too old" },
+ { NBRF_TOOMANYROUTES, "too many routes" },
+ { NBRF_NOTPRUNING, "not pruning?" },
+};
+
+/*
+ * Print the contents of the uvifs array on file 'fp'.
+ */
+void
+dump_vifs(fp)
+ FILE *fp;
+{
+ register vifi_t vifi;
+ register struct uvif *v;
+ register struct listaddr *a;
+ register struct phaddr *p;
+ register struct vif_acl *acl;
+ int i;
+ struct sioc_vif_req v_req;
+ time_t now;
+ char *label;
+
+ time(&now);
+ fprintf(fp, "vifs_with_neighbors = %d\n", vifs_with_neighbors);
+
+ if (vifs_with_neighbors == 1)
+ fprintf(fp,"[This host is a leaf]\n\n");
+
+ fprintf(fp,
+ "\nVirtual Interface Table\n%s",
+ "Vif Name Local-Address ");
+ fprintf(fp,
+ "M Thr Rate Flags\n");
+
+ for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++) {
+
+ fprintf(fp, "%2u %6s %-15s %6s: %-18s %2u %3u %5u ",
+ vifi,
+ v->uv_name,
+ inet_fmt(v->uv_lcl_addr, s1),
+ (v->uv_flags & VIFF_TUNNEL) ?
+ "tunnel":
+ "subnet",
+ (v->uv_flags & VIFF_TUNNEL) ?
+ inet_fmt(v->uv_rmt_addr, s2) :
+ inet_fmts(v->uv_subnet, v->uv_subnetmask, s3),
+ v->uv_metric,
+ v->uv_threshold,
+ v->uv_rate_limit);
+
+ for (i = 0; i < sizeof(vifflags) / sizeof(struct vnflags); i++)
+ if (v->uv_flags & vifflags[i].vn_flag)
+ fprintf(fp, " %s", vifflags[i].vn_name);
+
+ fprintf(fp, "\n");
+ /*
+ fprintf(fp, " #routes: %d\n", v->uv_nroutes);
+ */
+ if (v->uv_admetric != 0)
+ fprintf(fp, " advert-metric %2u\n",
+ v->uv_admetric);
+
+ label = "alternate subnets:";
+ for (p = v->uv_addrs; p; p = p->pa_next) {
+ fprintf(fp, " %18s %s\n", label,
+ inet_fmts(p->pa_subnet, p->pa_subnetmask, s1));
+ label = "";
+ }
+
+ label = "peers:";
+ for (a = v->uv_neighbors; a != NULL; a = a->al_next) {
+ fprintf(fp, " %6s %s (%d.%d) [%d]",
+ label, inet_fmt(a->al_addr, s1), a->al_pv, a->al_mv,
+ a->al_index);
+ for (i = 0; i < sizeof(nbrflags) / sizeof(struct vnflags); i++)
+ if (a->al_flags & nbrflags[i].vn_flag)
+ fprintf(fp, " %s", nbrflags[i].vn_name);
+ fprintf(fp, " up %s\n", scaletime(now - a->al_ctime));
+ /*fprintf(fp, " #routes %d\n", a->al_nroutes);*/
+ label = "";
+ }
+
+ label = "group host (time left):";
+ for (a = v->uv_groups; a != NULL; a = a->al_next) {
+ fprintf(fp, " %23s %-15s %-15s (%s)\n",
+ label,
+ inet_fmt(a->al_addr, s1),
+ inet_fmt(a->al_reporter, s2),
+ scaletime(timer_leftTimer(a->al_timerid)));
+ label = "";
+ }
+ label = "boundaries:";
+ for (acl = v->uv_acl; acl != NULL; acl = acl->acl_next) {
+ fprintf(fp, " %11s %-18s\n", label,
+ inet_fmts(acl->acl_addr, acl->acl_mask, s1));
+ label = "";
+ }
+ if (v->uv_filter) {
+ struct vf_element *vfe;
+ char lbuf[100];
+
+ sprintf(lbuf, "%5s %7s filter:",
+ v->uv_filter->vf_flags & VFF_BIDIR ? "bidir"
+ : " ",
+ v->uv_filter->vf_type == VFT_ACCEPT ? "accept"
+ : "deny");
+ label = lbuf;
+ for (vfe = v->uv_filter->vf_filter;
+ vfe != NULL; vfe = vfe->vfe_next) {
+ fprintf(fp, " %23s %-18s%s\n",
+ label,
+ inet_fmts(vfe->vfe_addr, vfe->vfe_mask, s1),
+ vfe->vfe_flags & VFEF_EXACT ? " (exact)" : "");
+ label = "";
+ }
+ }
+ if (!(v->uv_flags & (VIFF_TUNNEL|VIFF_DOWN|VIFF_DISABLED))) {
+ fprintf(fp, " IGMP querier: ");
+ if (v->uv_querier == NULL)
+ if (v->uv_flags & VIFF_QUERIER)
+ fprintf(fp, "%-18s (this system)\n",
+ inet_fmt(v->uv_lcl_addr, s1));
+ else
+ fprintf(fp, "NONE - querier election failure?\n");
+ else
+ fprintf(fp, "%-18s up %s last heard %s ago\n",
+ inet_fmt(v->uv_querier->al_addr, s1),
+ scaletime(now - v->uv_querier->al_ctime),
+ scaletime(v->uv_querier->al_timer));
+ }
+ if (v->uv_flags & VIFF_BLASTER)
+ fprintf(fp, " blasterbuf size: %dk\n",
+ v->uv_blasterlen / 1024);
+ fprintf(fp, " Nbr bitmaps: 0x%08lx%08lx\n",/*XXX*/
+ v->uv_nbrmap.hi, v->uv_nbrmap.lo);
+ if (v->uv_prune_lifetime != 0)
+ fprintf(fp, " Prune Lifetime: %d seconds\n",
+ v->uv_prune_lifetime);
+
+ v_req.vifi = vifi;
+ if (did_final_init)
+ if (ioctl(udp_socket, SIOCGETVIFCNT, (char *)&v_req) < 0) {
+ log(LOG_WARNING, errno,
+ "SIOCGETVIFCNT fails on vif %d", vifi);
+ } else {
+ fprintf(fp, " pkts/bytes in : %lu/%lu\n",
+ v_req.icount, v_req.ibytes);
+ fprintf(fp, " pkts/bytes out: %lu/%lu\n",
+ v_req.ocount, v_req.obytes);
+ }
+ fprintf(fp, "\n");
+ }
+ fprintf(fp, "\n");
+}
+
+/*
+ * Time out record of a group membership on a vif
+ */
+static void
+DelVif(arg)
+ void *arg;
+{
+ cbk_t *cbk = (cbk_t *)arg;
+ vifi_t vifi = cbk->vifi;
+ struct uvif *v = &uvifs[vifi];
+ struct listaddr *a, **anp, *g = cbk->g;
+
+ /*
+ * Group has expired
+ * delete all kernel cache entries with this group
+ */
+ if (g->al_query)
+ DeleteTimer(g->al_query);
+
+ delete_lclgrp(vifi, g->al_addr);
+
+ anp = &(v->uv_groups);
+ while ((a = *anp) != NULL) {
+ if (a == g) {
+ *anp = a->al_next;
+ free((char *)a);
+ } else {
+ anp = &a->al_next;
+ }
+ }
+
+ free(cbk);
+}
+
+/*
+ * Set a timer to delete the record of a group membership on a vif.
+ */
+static int
+SetTimer(vifi, g)
+ vifi_t vifi;
+ struct listaddr *g;
+{
+ cbk_t *cbk;
+
+ cbk = (cbk_t *) malloc(sizeof(cbk_t));
+ cbk->g = g;
+ cbk->vifi = vifi;
+ return timer_setTimer(g->al_timer, DelVif, cbk);
+}
+
+/*
+ * Delete a timer that was set above.
+ */
+static int
+DeleteTimer(id)
+ int id;
+{
+ timer_clearTimer(id);
+ return 0;
+}
+
+/*
+ * Send a group-specific query.
+ */
+static void
+SendQuery(arg)
+ void *arg;
+{
+ cbk_t *cbk = (cbk_t *)arg;
+ register struct uvif *v = &uvifs[cbk->vifi];
+
+ send_igmp(v->uv_lcl_addr, cbk->g->al_addr,
+ IGMP_MEMBERSHIP_QUERY,
+ cbk->q_time, cbk->g->al_addr, 0);
+ cbk->g->al_query = 0;
+ free(cbk);
+}
+
+/*
+ * Set a timer to send a group-specific query.
+ */
+static int
+SetQueryTimer(g, vifi, to_expire, q_time)
+ struct listaddr *g;
+ vifi_t vifi;
+ int to_expire, q_time;
+{
+ cbk_t *cbk;
+
+ cbk = (cbk_t *) malloc(sizeof(cbk_t));
+ cbk->g = g;
+ cbk->q_time = q_time;
+ cbk->vifi = vifi;
+ return timer_setTimer(to_expire, SendQuery, cbk);
+}
diff --git a/usr.sbin/mrouted/vif.h b/usr.sbin/mrouted/vif.h
new file mode 100644
index 0000000..5be618d
--- /dev/null
+++ b/usr.sbin/mrouted/vif.h
@@ -0,0 +1,237 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $FreeBSD$
+ * vif.h,v 3.8.4.26 1998/01/14 21:21:19 fenner Exp
+ */
+
+
+/*
+ * Bitmap handling functions.
+ * These should be fast but generic. bytes can be slow to zero and compare,
+ * words are hard to make generic. Thus two sets of macros (yuk).
+ */
+
+/*
+ * The VIFM_ functions should migrate out of <netinet/ip_mroute.h>, since
+ * the kernel no longer uses vifbitmaps.
+ */
+#ifndef VIFM_SET
+typedef u_long vifbitmap_t;
+
+#define VIFM_SET(n, m) ((m) |= (1 << (n)))
+#define VIFM_CLR(n, m) ((m) &= ~(1 << (n)))
+#define VIFM_ISSET(n, m) ((m) & (1 << (n)))
+#define VIFM_CLRALL(m) ((m) = 0x00000000)
+#define VIFM_COPY(mfrom, mto) ((mto) = (mfrom))
+#define VIFM_SAME(m1, m2) ((m1) == (m2))
+#endif
+/*
+ * And <netinet/ip_mroute.h> was missing some required functions anyway
+ */
+#ifndef VIFM_SETALL
+#define VIFM_SETALL(m) ((m) = ~0)
+#endif
+#define VIFM_ISSET_ONLY(n, m) ((m) == (1 << (n)))
+#define VIFM_ISEMPTY(m) ((m) == 0)
+#define VIFM_CLR_MASK(m, mask) ((m) &= ~(mask))
+#define VIFM_SET_MASK(m, mask) ((m) |= (mask))
+
+/*
+ * Neighbor bitmaps are, for efficiency, implemented as a struct
+ * containing two variables of a native machine type. If you
+ * have a native type that's bigger than a long, define it below.
+ */
+#define NBRTYPE u_long
+#define NBRBITS sizeof(NBRTYPE) * 8
+
+typedef struct {
+ NBRTYPE hi;
+ NBRTYPE lo;
+} nbrbitmap_t;
+#define MAXNBRS 2 * NBRBITS
+#define NO_NBR MAXNBRS
+
+#define NBRM_SET(n, m) (((n) < NBRBITS) ? ((m).lo |= (1 << (n))) : \
+ ((m).hi |= (1 << (n - NBRBITS))))
+#define NBRM_CLR(n, m) (((n) < NBRBITS) ? ((m).lo &= ~(1 << (n))) : \
+ ((m).hi &= ~(1 << (n - NBRBITS))))
+#define NBRM_ISSET(n, m) (((n) < NBRBITS) ? ((m).lo & (1 << (n))) : \
+ ((m).hi & (1 << ((n) - NBRBITS))))
+#define NBRM_CLRALL(m) ((m).lo = (m).hi = 0)
+#define NBRM_COPY(mfrom, mto) ((mto).lo = (mfrom).lo, (mto).hi = (mfrom).hi)
+#define NBRM_SAME(m1, m2) (((m1).lo == (m2).lo) && ((m1).hi == (m2).hi))
+#define NBRM_ISEMPTY(m) (((m).lo == 0) && ((m).hi == 0))
+#define NBRM_SETMASK(m, mask) (((m).lo |= (mask).lo),((m).hi |= (mask).hi))
+#define NBRM_CLRMASK(m, mask) (((m).lo &= ~(mask).lo),((m).hi &= ~(mask).hi))
+#define NBRM_MASK(m, mask) (((m).lo &= (mask).lo),((m).hi &= (mask).hi))
+#define NBRM_ISSETMASK(m, mask) (((m).lo & (mask).lo) || ((m).hi & (mask).hi))
+#define NBRM_ISSETALLMASK(m, mask)\
+ ((((m).lo & (mask).lo) == (mask).lo) && \
+ (((m).hi & (mask).hi) == (mask).hi))
+/*
+ * This macro is TRUE if all the subordinates have been pruned, or if
+ * there are no subordinates on this vif.
+ * The arguments is the map of subordinates, the map of neighbors on the
+ * vif, and the map of received prunes.
+ */
+#define SUBS_ARE_PRUNED(sub, vifmask, prunes) \
+ (((sub).lo & (vifmask).lo) == ((prunes).lo & (vifmask).lo & (sub).lo) && \
+ ((sub).hi & (vifmask).hi) == ((prunes).hi & (vifmask).hi & (sub).hi))
+
+struct blastinfo {
+ char * bi_buf; /* Pointer to malloced storage */
+ char * bi_cur; /* The update to process next */
+ char * bi_end; /* The place to put the next update */
+ int bi_len; /* Size of malloced storage */
+ int bi_timer; /* Timer to run process_blaster_report */
+};
+
+/*
+ * User level Virtual Interface structure
+ *
+ * A "virtual interface" is either a physical, multicast-capable interface
+ * (called a "phyint") or a virtual point-to-point link (called a "tunnel").
+ * (Note: all addresses, subnet numbers and masks are kept in NETWORK order.)
+ */
+struct uvif {
+ u_int uv_flags; /* VIFF_ flags defined below */
+ u_char uv_metric; /* cost of this vif */
+ u_char uv_admetric; /* advertised cost of this vif */
+ u_char uv_threshold; /* min ttl required to forward on vif */
+ u_int uv_rate_limit; /* rate limit on this vif */
+ u_int32 uv_lcl_addr; /* local address of this vif */
+ u_int32 uv_rmt_addr; /* remote end-point addr (tunnels only) */
+ u_int32 uv_dst_addr; /* destination for DVMRP messages */
+ u_int32 uv_subnet; /* subnet number (phyints only) */
+ u_int32 uv_subnetmask; /* subnet mask (phyints only) */
+ u_int32 uv_subnetbcast;/* subnet broadcast addr (phyints only) */
+ char uv_name[IFNAMSIZ]; /* interface name */
+ struct listaddr *uv_groups; /* list of local groups (phyints only) */
+ struct listaddr *uv_neighbors; /* list of neighboring routers */
+ nbrbitmap_t uv_nbrmap; /* bitmap of active neighboring routers */
+ struct listaddr *uv_querier; /* IGMP querier on vif */
+ int uv_igmpv1_warn;/* To rate-limit IGMPv1 warnings */
+ int uv_prune_lifetime; /* Prune lifetime or 0 for default */
+ struct vif_acl *uv_acl; /* access control list of groups */
+ int uv_leaf_timer; /* time until this vif is considrd leaf */
+ struct phaddr *uv_addrs; /* Additional subnets on this vif */
+ struct vif_filter *uv_filter; /* Route filters on this vif */
+ struct blastinfo uv_blaster; /* Info about route blasters */
+ int uv_nbrup; /* Counter for neighbor up events */
+ int uv_icmp_warn; /* To rate-limit ICMP warnings */
+ u_int uv_nroutes; /* # of routes with this vif as parent */
+ struct ip *uv_encap_hdr; /* Pre-formed header to encapsulate msgs*/
+};
+
+#define uv_blasterbuf uv_blaster.bi_buf
+#define uv_blastercur uv_blaster.bi_cur
+#define uv_blasterend uv_blaster.bi_end
+#define uv_blasterlen uv_blaster.bi_len
+#define uv_blastertimer uv_blaster.bi_timer
+
+#define VIFF_KERNEL_FLAGS (VIFF_TUNNEL|VIFF_SRCRT)
+#define VIFF_DOWN 0x000100 /* kernel state of interface */
+#define VIFF_DISABLED 0x000200 /* administratively disabled */
+#define VIFF_QUERIER 0x000400 /* I am the subnet's querier */
+#define VIFF_ONEWAY 0x000800 /* Maybe one way interface */
+#define VIFF_LEAF 0x001000 /* all neighbors are leaves */
+#define VIFF_IGMPV1 0x002000 /* Act as an IGMPv1 Router */
+#define VIFF_REXMIT_PRUNES 0x004000 /* retransmit prunes */
+#define VIFF_PASSIVE 0x008000 /* passive tunnel */
+#define VIFF_ALLOW_NONPRUNERS 0x010000 /* ok to peer with nonprunrs */
+#define VIFF_NOFLOOD 0x020000 /* don't flood on this vif */
+#define VIFF_NOTRANSIT 0x040000 /* don't transit these vifs */
+#define VIFF_BLASTER 0x080000 /* nbr on vif blasts routes */
+#define VIFF_FORCE_LEAF 0x100000 /* ignore nbrs on this vif */
+#define VIFF_OTUNNEL 0x200000 /* DVMRP msgs "beside" tunnel*/
+
+#define AVOID_TRANSIT(v, r) \
+ (((r)->rt_parent != NO_VIF) && \
+ ((r)->rt_gateway != 0) && \
+ (uvifs[(v)].uv_flags & VIFF_NOTRANSIT) && \
+ (uvifs[(r)->rt_parent].uv_flags & VIFF_NOTRANSIT))
+
+struct phaddr {
+ struct phaddr *pa_next;
+ u_int32 pa_subnet; /* extra subnet */
+ u_int32 pa_subnetmask; /* netmask of extra subnet */
+ u_int32 pa_subnetbcast; /* broadcast of extra subnet */
+};
+
+struct vif_acl {
+ struct vif_acl *acl_next; /* next acl member */
+ u_int32 acl_addr; /* Group address */
+ u_int32 acl_mask; /* Group addr. mask */
+};
+
+struct vif_filter {
+ int vf_type;
+#define VFT_ACCEPT 1
+#define VFT_DENY 2
+ int vf_flags;
+#define VFF_BIDIR 1
+ struct vf_element *vf_filter;
+};
+
+struct vf_element {
+ struct vf_element *vfe_next;
+ u_int32 vfe_addr;
+ u_int32 vfe_mask;
+ int vfe_flags;
+#define VFEF_EXACT 0x0001
+};
+
+struct listaddr {
+ struct listaddr *al_next; /* link to next addr, MUST BE FIRST */
+ u_int32 al_addr; /* local group or neighbor address */
+ u_long al_timer; /* for timing out group or neighbor */
+ time_t al_ctime; /* entry creation time */
+ union {
+ struct {
+ u_int32 alur_genid; /* generation id for neighbor */
+ u_int alur_nroutes; /* # of routes w/ nbr as parent */
+ u_char alur_pv; /* router protocol version */
+ u_char alur_mv; /* router mrouted version */
+ u_char alur_index; /* neighbor index */
+ } alu_router;
+ struct {
+ u_int32 alug_reporter; /* a host which reported membership */
+ u_long alug_timerid; /* timer for group membership */
+ u_long alug_query; /* timer for repeated leave query */
+ u_char alug_old; /* time since heard old report */
+ } alu_group;
+ } al_alu;
+ u_short al_flags; /* flags related to this neighbor */
+};
+#define al_genid al_alu.alu_router.alur_genid
+#define al_nroutes al_alu.alu_router.alur_nroutes
+#define al_pv al_alu.alu_router.alur_pv
+#define al_mv al_alu.alu_router.alur_mv
+#define al_index al_alu.alu_router.alur_index
+#define al_reporter al_alu.alu_group.alug_reporter
+#define al_old al_alu.alu_group.alug_old
+#define al_timerid al_alu.alu_group.alug_timerid
+#define al_query al_alu.alu_group.alug_query
+
+#define NBRF_LEAF 0x0001 /* This neighbor is a leaf */
+#define NBRF_GENID 0x0100 /* I know this neighbor's genid */
+#define NBRF_WAITING 0x0200 /* Waiting for peering to come up */
+#define NBRF_ONEWAY 0x0400 /* One-way peering */
+#define NBRF_TOOOLD 0x0800 /* Too old (policy decision) */
+#define NBRF_TOOMANYROUTES 0x1000 /* Neighbor is spouting routes */
+#define NBRF_NOTPRUNING 0x2000 /* Neighbor doesn't appear to prune */
+
+/*
+ * Don't peer with neighbors with any of these flags set
+ */
+#define NBRF_DONTPEER (NBRF_WAITING|NBRF_ONEWAY|NBRF_TOOOLD| \
+ NBRF_TOOMANYROUTES|NBRF_NOTPRUNING)
+
+#define NO_VIF ((vifi_t)MAXVIFS) /* An invalid vif index */
diff --git a/usr.sbin/mtest/Makefile b/usr.sbin/mtest/Makefile
new file mode 100644
index 0000000..95bbe12
--- /dev/null
+++ b/usr.sbin/mtest/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+PROG= mtest
+MAN= mtest.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mtest/mtest.8 b/usr.sbin/mtest/mtest.8
new file mode 100644
index 0000000..e988e8e
--- /dev/null
+++ b/usr.sbin/mtest/mtest.8
@@ -0,0 +1,55 @@
+.\"
+.\" $FreeBSD$
+.\"
+.\" The following requests are required for all man pages.
+.Dd December 15, 1996
+.Os
+.Dt MTEST 8
+.Sh NAME
+.Nm mtest
+.Nd test multicast membership socket operations and ioctls
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+.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..920f4dd
--- /dev/null
+++ b/usr.sbin/mtest/mtest.c
@@ -0,0 +1,230 @@
+/*
+ * Program to test new [sg]etsockopts and ioctls for manipulating IP and
+ * Ethernet multicast address filters.
+ *
+ * Written by Steve Deering, Stanford University, February 1989.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+
+int
+main( argc, argv )
+ int argc;
+ char **argv;
+ {
+ int so;
+ char line[80];
+ char *lineptr;
+ struct ip_mreq imr;
+ struct ifreq ifr;
+ int n, f;
+ unsigned i1, i2, i3, i4, g1, g2, g3, g4;
+ unsigned e1, e2, e3, e4, e5, e6;
+
+ if( (so = socket( AF_INET, SOCK_DGRAM, 0 )) == -1)
+ err( 1, "can't open socket" );
+
+ printf( "multicast membership test program; " );
+ printf( "enter ? for list of commands\n" );
+
+ while( fgets( line, 79, stdin ) != NULL )
+ {
+ lineptr = line;
+ while( *lineptr == ' ' || *lineptr == '\t' ) ++lineptr;
+ switch( *lineptr )
+ {
+ case '?':
+ {
+ printf( "%s%s%s%s%s%s%s",
+ " j g.g.g.g i.i.i.i - join IP multicast group \n",
+ " l g.g.g.g i.i.i.i - leave IP multicast group \n",
+ " a ifname e.e.e.e.e.e - add ether multicast address \n",
+ " d ifname e.e.e.e.e.e - del ether multicast address \n",
+ " m ifname 1/0 - set/clear ether allmulti flag \n",
+ " p ifname 1/0 - set/clear ether promisc flag \n",
+ " q - quit \n\n" );
+ break;
+ }
+
+ case 'j':
+ {
+ ++lineptr;
+ while( *lineptr == ' ' || *lineptr == '\t' ) ++lineptr;
+ if( (n = sscanf( lineptr, "%u.%u.%u.%u %u.%u.%u.%u",
+ &g1, &g2, &g3, &g4, &i1, &i2, &i3, &i4 )) != 8 )
+ {
+ printf( "bad args\n" );
+ break;
+ }
+ imr.imr_multiaddr.s_addr = (g1<<24) | (g2<<16) | (g3<<8) | g4;
+ imr.imr_multiaddr.s_addr = htonl(imr.imr_multiaddr.s_addr);
+ imr.imr_interface.s_addr = (i1<<24) | (i2<<16) | (i3<<8) | i4;
+ imr.imr_interface.s_addr = htonl(imr.imr_interface.s_addr);
+ if( setsockopt( so, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ &imr, sizeof(struct ip_mreq) ) == -1 )
+ warn( "can't join group" );
+ else printf( "group joined\n" );
+ break;
+ }
+
+ case 'l':
+ {
+ ++lineptr;
+ while( *lineptr == ' ' || *lineptr == '\t' ) ++lineptr;
+ if( (n = sscanf( lineptr, "%u.%u.%u.%u %u.%u.%u.%u",
+ &g1, &g2, &g3, &g4, &i1, &i2, &i3, &i4 )) != 8 )
+ {
+ printf( "bad args\n" );
+ break;
+ }
+ imr.imr_multiaddr.s_addr = (g1<<24) | (g2<<16) | (g3<<8) | g4;
+ imr.imr_multiaddr.s_addr = htonl(imr.imr_multiaddr.s_addr);
+ imr.imr_interface.s_addr = (i1<<24) | (i2<<16) | (i3<<8) | i4;
+ imr.imr_interface.s_addr = htonl(imr.imr_interface.s_addr);
+ if( setsockopt( so, IPPROTO_IP, IP_DROP_MEMBERSHIP,
+ &imr, sizeof(struct ip_mreq) ) == -1 )
+ warn( "can't leave group" );
+ else printf( "group left\n" );
+ break;
+ }
+
+ case 'a':
+ {
+ struct sockaddr_dl *dlp;
+ unsigned char *bp;
+ ++lineptr;
+ while( *lineptr == ' ' || *lineptr == '\t' ) ++lineptr;
+ if( (n = sscanf( lineptr, "%s %x.%x.%x.%x.%x.%x",
+ ifr.ifr_name, &e1, &e2, &e3, &e4, &e5, &e6 )) != 7 )
+ {
+ printf( "bad args\n" );
+ break;
+ }
+ dlp = (struct sockaddr_dl *)&ifr.ifr_addr;
+ dlp->sdl_len = sizeof(struct sockaddr_dl);
+ dlp->sdl_family = AF_LINK;
+ dlp->sdl_index = 0;
+ dlp->sdl_nlen = 0;
+ dlp->sdl_alen = 6;
+ dlp->sdl_slen = 0;
+ bp = LLADDR(dlp);
+ bp[0] = e1;
+ bp[1] = e2;
+ bp[2] = e3;
+ bp[3] = e4;
+ bp[4] = e5;
+ bp[5] = e6;
+ if( ioctl( so, SIOCADDMULTI, &ifr ) == -1 )
+ warn( "can't add ether address" );
+ else printf( "ether address added\n" );
+ break;
+ }
+
+ case 'd':
+ {
+ struct sockaddr_dl *dlp;
+ unsigned char *bp;
+ ++lineptr;
+ while( *lineptr == ' ' || *lineptr == '\t' ) ++lineptr;
+ if( (n = sscanf( lineptr, "%s %x.%x.%x.%x.%x.%x",
+ ifr.ifr_name, &e1, &e2, &e3, &e4, &e5, &e6 )) != 7 )
+ {
+ printf( "bad args\n" );
+ break;
+ }
+ dlp = (struct sockaddr_dl *)&ifr.ifr_addr;
+ dlp->sdl_len = sizeof(struct sockaddr_dl);
+ dlp->sdl_family = AF_LINK;
+ dlp->sdl_index = 0;
+ dlp->sdl_nlen = 0;
+ dlp->sdl_alen = 6;
+ dlp->sdl_slen = 0;
+ bp = LLADDR(dlp);
+ bp[0] = e1;
+ bp[1] = e2;
+ bp[2] = e3;
+ bp[3] = e4;
+ bp[4] = e5;
+ bp[5] = e6;
+ if( ioctl( so, SIOCDELMULTI, &ifr ) == -1 )
+ warn( "can't delete ether address" );
+ else printf( "ether address deleted\n" );
+ break;
+ }
+
+ case 'm':
+ {
+ ++lineptr;
+ while( *lineptr == ' ' || *lineptr == '\t' ) ++lineptr;
+ if( (n = sscanf( lineptr, "%s %u", ifr.ifr_name, &f )) != 2 )
+ {
+ printf( "bad args\n" );
+ break;
+ }
+ if( ioctl( so, SIOCGIFFLAGS, &ifr ) == -1 )
+ {
+ warn( "can't get interface flags" );
+ break;
+ }
+ printf( "interface flags %x, ", ifr.ifr_flags );
+ fflush( stdout );
+ if( f ) ifr.ifr_flags |= IFF_ALLMULTI;
+ else ifr.ifr_flags &= ~IFF_ALLMULTI;
+ if( ioctl( so, SIOCSIFFLAGS, &ifr ) == -1 )
+ warn( "can't set" );
+ else printf( "changed to %x\n", ifr.ifr_flags );
+ break;
+ }
+
+ case 'p':
+ {
+ ++lineptr;
+ while( *lineptr == ' ' || *lineptr == '\t' ) ++lineptr;
+ if( (n = sscanf( lineptr, "%s %u", ifr.ifr_name, &f )) != 2 )
+ {
+ printf( "bad args\n" );
+ break;
+ }
+ if( ioctl( so, SIOCGIFFLAGS, &ifr ) == -1 )
+ {
+ warn( "can't get interface flags" );
+ break;
+ }
+ printf( "interface flags %x, ", ifr.ifr_flags );
+ fflush( stdout );
+ if( f ) ifr.ifr_flags |= IFF_PROMISC;
+ else ifr.ifr_flags &= ~IFF_PROMISC;
+ if( ioctl( so, SIOCSIFFLAGS, &ifr ) == -1 )
+ warn( "can't set" );
+ else printf( "changed to %x\n", ifr.ifr_flags );
+ break;
+ }
+
+ case 'q': exit( 0 );
+
+ case 0:
+ case '\n': break;
+
+ default:
+ {
+ printf( "bad command\n" );
+ break;
+ }
+ }
+ }
+ return(0);
+ }
diff --git a/usr.sbin/mtree/Makefile b/usr.sbin/mtree/Makefile
new file mode 100644
index 0000000..5b5e03d
--- /dev/null
+++ b/usr.sbin/mtree/Makefile
@@ -0,0 +1,19 @@
+# From: @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../usr.bin/cksum
+
+PROG= mtree
+MAN= mtree.8
+SRCS= compare.c crc.c create.c excludes.c misc.c mtree.c spec.c verify.c
+
+.if defined(BOOTSTRAPPING)
+.PATH: ${.CURDIR}/../../lib/libc/gen
+SRCS+= strtofflags.c
+.else
+CFLAGS+= -DMD5 -DSHA1 -DRMD160
+DPADD= ${LIBMD}
+LDADD= -lmd
+.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..19e48cd
--- /dev/null
+++ b/usr.sbin/mtree/compare.c
@@ -0,0 +1,368 @@
+/*-
+ * 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[] =
+ "$FreeBSD$";
+#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 changed\n", RP(p)); \
+ tab = "\t"; \
+ }
+
+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 = "";
+ char *fflags;
+
+ label = 0;
+ switch(s->type) {
+ case F_BLOCK:
+ if (!S_ISBLK(p->fts_statp->st_mode))
+ goto typeerr;
+ break;
+ case F_CHAR:
+ if (!S_ISCHR(p->fts_statp->st_mode))
+ goto typeerr;
+ break;
+ case F_DIR:
+ if (!S_ISDIR(p->fts_statp->st_mode))
+ goto typeerr;
+ break;
+ case F_FIFO:
+ if (!S_ISFIFO(p->fts_statp->st_mode))
+ goto typeerr;
+ break;
+ case F_FILE:
+ if (!S_ISREG(p->fts_statp->st_mode))
+ goto typeerr;
+ break;
+ case F_LINK:
+ if (!S_ISLNK(p->fts_statp->st_mode))
+ goto typeerr;
+ break;
+ case F_SOCK:
+ if (!S_ISSOCK(p->fts_statp->st_mode)) {
+typeerr: LABEL;
+ (void)printf("\ttype expected %s found %s\n",
+ ftype(s->type), inotype(p->fts_statp->st_mode));
+ return (label);
+ }
+ break;
+ }
+ /* Set the uid/gid first, then set the mode. */
+ if (s->flags & (F_UID | F_UNAME) && s->st_uid != p->fts_statp->st_uid) {
+ LABEL;
+ (void)printf("%suser expected %lu found %lu",
+ tab, (u_long)s->st_uid, (u_long)p->fts_statp->st_uid);
+ if (uflag)
+ if (chown(p->fts_accpath, s->st_uid, -1))
+ (void)printf(" not modified: %s\n",
+ strerror(errno));
+ else
+ (void)printf(" modified\n");
+ else
+ (void)printf("\n");
+ tab = "\t";
+ }
+ if (s->flags & (F_GID | F_GNAME) && s->st_gid != p->fts_statp->st_gid) {
+ LABEL;
+ (void)printf("%sgid expected %lu found %lu",
+ tab, (u_long)s->st_gid, (u_long)p->fts_statp->st_gid);
+ if (uflag)
+ if (chown(p->fts_accpath, -1, s->st_gid))
+ (void)printf(" not modified: %s\n",
+ strerror(errno));
+ else
+ (void)printf(" modified\n");
+ else
+ (void)printf("\n");
+ tab = "\t";
+ }
+ if (s->flags & F_MODE &&
+ !S_ISLNK(p->fts_statp->st_mode) &&
+ s->st_mode != (p->fts_statp->st_mode & MBITS)) {
+ LABEL;
+ (void)printf("%spermissions expected %#o found %#o",
+ tab, s->st_mode, p->fts_statp->st_mode & MBITS);
+ if (uflag)
+ if (chmod(p->fts_accpath, s->st_mode))
+ (void)printf(" not modified: %s\n",
+ strerror(errno));
+ else
+ (void)printf(" modified\n");
+ else
+ (void)printf("\n");
+ tab = "\t";
+ }
+ if (s->flags & F_NLINK && s->type != F_DIR &&
+ s->st_nlink != p->fts_statp->st_nlink) {
+ LABEL;
+ (void)printf("%slink_count expected %u found %u\n",
+ tab, s->st_nlink, p->fts_statp->st_nlink);
+ tab = "\t";
+ }
+ if (s->flags & F_SIZE && s->st_size != p->fts_statp->st_size &&
+ !S_ISDIR(p->fts_statp->st_mode)) {
+ LABEL;
+ (void)printf("%ssize expected %qd found %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 expected %.24s ",
+ tab, ctime(&s->st_mtimespec.tv_sec));
+ (void)printf("found %.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 expected %lu found %lu\n",
+ tab, s->cksum, val);
+ }
+ tab = "\t";
+ }
+ }
+ /*
+ * XXX
+ * since chflags(2) will reset file times, the utimes() above
+ * may have been useless! oh well, we'd rather have correct
+ * flags, rather than times?
+ */
+ if ((s->flags & F_FLAGS) && s->st_flags != p->fts_statp->st_flags) {
+ LABEL;
+ fflags = flags_to_string(s->st_flags);
+ (void)printf("%sflags expected \"%s\"", tab, fflags);
+ free(fflags);
+
+ fflags = flags_to_string(p->fts_statp->st_flags);
+ (void)printf(" found \"%s\"", fflags);
+ free(fflags);
+
+ if (uflag)
+ if (chflags(p->fts_accpath, s->st_flags))
+ (void)printf(" not modified: %s\n",
+ strerror(errno));
+ else
+ (void)printf(" modified\n");
+ else
+ (void)printf("\n");
+ tab = "\t";
+ }
+#ifdef MD5
+ if (s->flags & F_MD5) {
+ char *new_digest, buf[33];
+
+ new_digest = MD5File(p->fts_accpath, buf);
+ if (!new_digest) {
+ LABEL;
+ printf("%sMD5: %s: %s\n", tab, p->fts_accpath,
+ strerror(errno));
+ tab = "\t";
+ } else if (strcmp(new_digest, s->md5digest)) {
+ LABEL;
+ printf("%sMD5 expected %s found %s\n", tab, s->md5digest,
+ new_digest);
+ tab = "\t";
+ }
+ }
+#endif /* MD5 */
+#ifdef SHA1
+ if (s->flags & F_SHA1) {
+ char *new_digest, buf[41];
+
+ new_digest = SHA1_File(p->fts_accpath, buf);
+ if (!new_digest) {
+ LABEL;
+ printf("%sSHA-1: %s: %s\n", tab, p->fts_accpath,
+ strerror(errno));
+ tab = "\t";
+ } else if (strcmp(new_digest, s->sha1digest)) {
+ LABEL;
+ printf("%sSHA-1 expected %s found %s\n",
+ tab, s->sha1digest, new_digest);
+ tab = "\t";
+ }
+ }
+#endif /* SHA1 */
+#ifdef RMD160
+ if (s->flags & F_RMD160) {
+ char *new_digest, buf[41];
+
+ new_digest = RIPEMD160_File(p->fts_accpath, buf);
+ if (!new_digest) {
+ LABEL;
+ printf("%sRIPEMD160: %s: %s\n", tab,
+ p->fts_accpath, strerror(errno));
+ tab = "\t";
+ } else if (strcmp(new_digest, s->rmd160digest)) {
+ LABEL;
+ printf("%sRIPEMD160 expected %s found %s\n",
+ tab, s->rmd160digest, new_digest);
+ tab = "\t";
+ }
+ }
+#endif /* RMD160 */
+
+ if (s->flags & F_SLINK &&
+ strcmp(cp = rlink(p->fts_accpath), s->slink)) {
+ LABEL;
+ (void)printf("%slink_ref expected %s found %s\n",
+ tab, 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..2975fb5
--- /dev/null
+++ b/usr.sbin/mtree/create.c
@@ -0,0 +1,425 @@
+/*-
+ * 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[] =
+ "$FreeBSD$";
+#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 u_long flags = 0xffffffff;
+
+static int dsort __P((const FTSENT **, const FTSENT **));
+static void output __P((int, int *, const char *, ...)) __printflike(3, 4);
+static int statd __P((FTS *, FTSENT *, uid_t *, gid_t *, mode_t *,
+ u_long *));
+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;
+ if (check_excludes(p->fts_name, p->fts_path)) {
+ fts_set(t, p, FTS_SKIP);
+ continue;
+ }
+ switch(p->fts_info) {
+ case FTS_D:
+ if (!dflag)
+ (void)printf("\n");
+ if (!nflag)
+ (void)printf("# %s\n", p->fts_path);
+ statd(t, p, &uid, &gid, &mode, &flags);
+ statf(indent, p);
+ break;
+ case FTS_DP:
+ if (!nflag && (p->fts_level > 0))
+ (void)printf("%*s# %s\n", indent, "", p->fts_path);
+ (void)printf("%*s..\n", indent, "");
+ if (!dflag)
+ (void)printf("\n");
+ break;
+ case FTS_DNR:
+ case FTS_ERR:
+ case FTS_NS:
+ warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
+ break;
+ default:
+ if (!dflag)
+ statf(indent, p);
+ break;
+
+ }
+ }
+ (void)fts_close(t);
+ if (sflag && keys & F_CKSUM)
+ warnx("%s checksum: %lu", fullpath, 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 *fflags;
+ char *escaped_name;
+
+ escaped_name = calloc(1, p->fts_namelen * 4 + 1);
+ if (escaped_name == NULL)
+ errx(1, "statf(): calloc() failed");
+ strvis(escaped_name, p->fts_name, VIS_WHITE | VIS_OCTAL);
+
+ 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));
+ if (keys & F_FLAGS && p->fts_statp->st_flags != flags) {
+ fflags = flags_to_string(p->fts_statp->st_flags);
+ output(indent, &offset, "flags=%s", fflags);
+ free(fflags);
+ }
+ (void)putchar('\n');
+}
+
+#define MAXGID 5000
+#define MAXUID 5000
+#define MAXMODE MBITS + 1
+#define MAXFLAGS 256
+#define MAXS 16
+
+static int
+statd(t, parent, puid, pgid, pmode, pflags)
+ FTS *t;
+ FTSENT *parent;
+ uid_t *puid;
+ gid_t *pgid;
+ mode_t *pmode;
+ u_long *pflags;
+{
+ register FTSENT *p;
+ register gid_t sgid;
+ register uid_t suid;
+ register mode_t smode;
+ register u_long sflags;
+ struct group *gr;
+ struct passwd *pw;
+ gid_t savegid = *pgid;
+ uid_t saveuid = *puid;
+ mode_t savemode = *pmode;
+ u_long saveflags = *pflags;
+ u_short maxgid, maxuid, maxmode, maxflags;
+ u_short g[MAXGID], u[MAXUID], m[MAXMODE], f[MAXFLAGS];
+ char *fflags;
+ static int first = 1;
+
+ if ((p = fts_children(t, 0)) == NULL) {
+ if (errno)
+ err(1, "line %d: %s", lineno, RP(parent));
+ return (1);
+ }
+
+ bzero(g, sizeof(g));
+ bzero(u, sizeof(u));
+ bzero(m, sizeof(m));
+ bzero(f, sizeof(f));
+
+ maxuid = maxgid = maxmode = maxflags = 0;
+ for (; p; p = p->fts_link) {
+ if (!dflag || (dflag && S_ISDIR(p->fts_statp->st_mode))) {
+ smode = p->fts_statp->st_mode & MBITS;
+ if (smode < MAXMODE && ++m[smode] > maxmode) {
+ savemode = smode;
+ maxmode = m[smode];
+ }
+ sgid = p->fts_statp->st_gid;
+ if (sgid < MAXGID && ++g[sgid] > maxgid) {
+ savegid = sgid;
+ maxgid = g[sgid];
+ }
+ suid = p->fts_statp->st_uid;
+ if (suid < MAXUID && ++u[suid] > maxuid) {
+ saveuid = suid;
+ maxuid = u[suid];
+ }
+
+ /*
+ * XXX
+ * note that the below will break when file flags
+ * are extended beyond the first 4 bytes of each
+ * half word of the flags
+ */
+#define FLAGS2IDX(f) ((f & 0xf) | ((f >> 12) & 0xf0))
+ sflags = p->fts_statp->st_flags;
+ if (FLAGS2IDX(sflags) < MAXFLAGS &&
+ ++f[FLAGS2IDX(sflags)] > maxflags) {
+ saveflags = sflags;
+ maxflags = f[FLAGS2IDX(sflags)];
+ }
+ }
+ }
+ /*
+ * If the /set record is the same as the last one we do not need to output
+ * a new one. So first we check to see if anything changed. Note that we
+ * always output a /set record for the first directory.
+ */
+ if ((((keys & F_UNAME) | (keys & F_UID)) && (*puid != saveuid)) ||
+ (((keys & F_GNAME) | (keys & F_GID)) && (*pgid != savegid)) ||
+ ((keys & F_MODE) && (*pmode != savemode)) ||
+ ((keys & F_FLAGS) && (*pflags != saveflags)) ||
+ (first)) {
+ first = 0;
+ if (dflag)
+ (void)printf("/set type=dir");
+ else
+ (void)printf("/set type=file");
+ if (keys & F_UNAME) {
+ 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");
+ if (keys & F_FLAGS) {
+ fflags = flags_to_string(saveflags);
+ (void)printf(" flags=%s", fflags);
+ free(fflags);
+ }
+ (void)printf("\n");
+ *puid = saveuid;
+ *pgid = savegid;
+ *pmode = savemode;
+ *pflags = saveflags;
+ }
+ return (0);
+}
+
+static int
+dsort(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));
+}
+
+#include <stdarg.h>
+
+void
+output(int indent, int *offset, const char *fmt, ...)
+{
+ va_list ap;
+ char buf[1024];
+ va_start(ap, fmt);
+ (void)vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ if (*offset + strlen(buf) > MAXLINELEN - 3) {
+ (void)printf(" \\\n%*s", INDENTNAMELEN + indent, "");
+ *offset = INDENTNAMELEN + indent;
+ }
+ *offset += printf(" %s", buf) + 1;
+}
diff --git a/usr.sbin/mtree/excludes.c b/usr.sbin/mtree/excludes.c
new file mode 100644
index 0000000..624b57a
--- /dev/null
+++ b/usr.sbin/mtree/excludes.c
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2000 Massachusetts Institute of Technology
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that both the above copyright notice and this
+ * permission notice appear in all copies, that both the above
+ * copyright notice and this permission notice appear in all
+ * supporting documentation, and that the name of M.I.T. not be used
+ * in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission. M.I.T. makes
+ * no representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
+ * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+static const char rcsid[] =
+ "$FreeBSD$";
+
+#include <sys/types.h>
+#include <sys/time.h> /* XXX for mtree.h */
+#include <sys/queue.h>
+
+#include <err.h>
+#include <fnmatch.h>
+#include <fts.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "mtree.h" /* XXX for extern.h */
+#include "extern.h"
+
+/*
+ * We're assuming that there won't be a whole lot of excludes,
+ * so it's OK to use a stupid algorithm.
+ */
+struct exclude {
+ LIST_ENTRY(exclude) link;
+ const char *glob;
+ int pathname;
+};
+static LIST_HEAD(, exclude) excludes;
+
+void
+init_excludes(void)
+{
+ LIST_INIT(&excludes);
+}
+
+void
+read_excludes_file(const char *name)
+{
+ FILE *fp;
+ char *line, *str;
+ struct exclude *e;
+ size_t len;
+
+ fp = fopen(name, "r");
+ if (fp == 0)
+ err(1, "%s", name);
+
+ while ((line = fgetln(fp, &len)) != 0) {
+ if (line[len - 1] == '\n')
+ len--;
+ if (len == 0)
+ continue;
+
+ str = malloc(len + 1);
+ e = malloc(sizeof *e);
+ if (str == 0 || e == 0)
+ errx(1, "memory allocation error");
+ e->glob = str;
+ memcpy(str, line, len);
+ str[len] = '\0';
+ if (strchr(str, '/'))
+ e->pathname = 1;
+ else
+ e->pathname = 0;
+ LIST_INSERT_HEAD(&excludes, e, link);
+ }
+ fclose(fp);
+}
+
+int
+check_excludes(const char *fname, const char *path)
+{
+ struct exclude *e;
+
+ /* fnmatch(3) has a funny return value convention... */
+#define MATCH(g, n) (fnmatch((g), (n), FNM_PATHNAME) == 0)
+
+ LIST_FOREACH(e, &excludes, link) {
+ if (e->pathname && MATCH(e->glob, path)
+ || MATCH(e->glob, fname))
+ return 1;
+ }
+ return 0;
+}
diff --git a/usr.sbin/mtree/extern.h b/usr.sbin/mtree/extern.h
new file mode 100644
index 0000000..54dafdf
--- /dev/null
+++ b/usr.sbin/mtree/extern.h
@@ -0,0 +1,50 @@
+/*-
+ * 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
+ * $FreeBSD$
+ */
+
+int compare __P((char *, NODE *, FTSENT *));
+int crc __P((int, u_long *, u_long *));
+void cwalk __P((void));
+char *flags_to_string __P((u_long));
+
+char *inotype __P((u_int));
+u_int parsekey __P((char *, int *));
+char *rlink __P((char *));
+NODE *spec __P((void));
+int verify __P((void));
+
+int check_excludes __P((const char *, const char *));
+void init_excludes __P((void));
+void read_excludes_file __P((const char *));
diff --git a/usr.sbin/mtree/misc.c b/usr.sbin/mtree/misc.c
new file mode 100644
index 0000000..7721ff4
--- /dev/null
+++ b/usr.sbin/mtree/misc.c
@@ -0,0 +1,129 @@
+/*-
+ * 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[] =
+ "$FreeBSD$";
+#endif /*not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <err.h>
+#include <fts.h>
+#include <stdio.h>
+#include <unistd.h>
+#include "mtree.h"
+#include "extern.h"
+
+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},
+ {"flags", F_FLAGS, NEEDVALUE},
+ {"gid", F_GID, NEEDVALUE},
+ {"gname", F_GNAME, NEEDVALUE},
+ {"ignore", F_IGN, 0},
+ {"link", F_SLINK, NEEDVALUE},
+#ifdef MD5
+ {"md5digest", F_MD5, NEEDVALUE},
+#endif
+ {"mode", F_MODE, NEEDVALUE},
+ {"nlink", F_NLINK, NEEDVALUE},
+ {"nochange", F_NOCHANGE, 0},
+#ifdef RMD160
+ {"ripemd160digest", F_RMD160, NEEDVALUE},
+#endif
+#ifdef SHA1
+ {"sha1digest", F_SHA1, NEEDVALUE},
+#endif
+ {"size", F_SIZE, NEEDVALUE},
+ {"time", F_TIME, NEEDVALUE},
+ {"type", F_TYPE, NEEDVALUE},
+ {"uid", F_UID, NEEDVALUE},
+ {"uname", F_UNAME, NEEDVALUE},
+};
+
+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));
+}
+
+char *
+flags_to_string(fflags)
+ u_long fflags;
+{
+ char *string;
+
+ string = fflagstostr(fflags);
+ if (string != NULL && *string == '\0') {
+ free(string);
+ string = strdup("none");
+ }
+ if (string == NULL)
+ err(1, NULL);
+
+ return string;
+}
diff --git a/usr.sbin/mtree/mtree.8 b/usr.sbin/mtree/mtree.8
new file mode 100644
index 0000000..6762eda
--- /dev/null
+++ b/usr.sbin/mtree/mtree.8
@@ -0,0 +1,360 @@
+.\" Copyright (c) 1989, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" From: @(#)mtree.8 8.2 (Berkeley) 12/11/93
+.\" $FreeBSD$
+.\"
+.Dd February 26, 1999
+.Dt MTREE 8
+.Os
+.Sh NAME
+.Nm mtree
+.Nd map a directory hierarchy
+.Sh SYNOPSIS
+.Nm
+.Op Fl LPUcdeinqrux
+.Bk -words
+.Op Fl f Ar spec
+.Ek
+.Bk -words
+.Op Fl K Ar keywords
+.Ek
+.Bk -words
+.Op Fl k Ar keywords
+.Ek
+.Bk -words
+.Op Fl p Ar path
+.Ek
+.Bk -words
+.Op Fl s Ar seed
+.Ek
+.Bk -words
+.Op Fl X Ar exclude-list
+.Ek
+.Sh DESCRIPTION
+The utility
+.Nm
+compares the file hierarchy rooted in the current directory against a
+specification read from the standard input.
+Messages are written to the standard output for any files whose
+characteristics do not match the specifications, or which are
+missing from either the file hierarchy or the specification.
+.Pp
+The options are as follows:
+.Bl -tag -width flag
+.It Fl L
+Follow all symbolic links in the file hierarchy.
+.It Fl P
+Don't follow symbolic links in the file hierarchy, instead consider
+the symbolic link itself in any comparisons. This is the default.
+.It Fl U
+Modify the owner, group and permissions of existing files to match
+the specification and create any missing directories or symbolic links.
+User, group and permissions must all be specified for missing directories
+to be created.
+Corrected mismatches are not considered errors.
+.It Fl c
+Print a specification for the file hierarchy to the standard output.
+.It Fl d
+Ignore everything except directory type files.
+.It Fl e
+Don't complain about files that are in the file hierarchy, but not in the
+specification.
+.It Fl i
+Indent the output 4 spaces each time a directory level is descended when
+create a specification with the
+.Fl c
+option.
+This does not affect either the /set statements or the comment before each
+directory.
+It does however affect the comment before the close of each directory.
+.It Fl n
+Do not emit pathname comments when creating a specification. Normally
+a comment is emitted before each directory and before the close of that
+directory when using the
+.Fl c
+option.
+.It Fl q
+Quiet mode. Do not complain when a
+.Dq missing
+directory cannot be created because it already exists.
+This occurs when the directory is a symbolic link.
+.It Fl r
+Remove any files in the file hierarchy that are not described in the
+specification.
+.It Fl u
+Same as
+.Fl U
+except a status of 2 is returned if the file hierarchy did not match
+the specification.
+.It Fl x
+Don't descend below mount points in the file hierarchy.
+.It Fl f Ar file
+Read the specification from
+.Ar file ,
+instead of from the standard input.
+.It Fl K Ar keywords
+Add the specified (whitespace or comma separated)
+.Ar keywords
+to the current set of keywords.
+.It Fl k Ar keywords
+Use the ``type'' keyword plus the specified (whitespace or comma separated)
+.Ar keywords
+instead of the current set of keywords.
+.It Fl p Ar path
+Use the file hierarchy rooted in
+.Ar path ,
+instead of the current directory.
+.It Fl s Ar seed
+Display a single checksum to the standard error output that represents all
+of the files for which the keyword
+.Cm cksum
+was specified.
+The checksum is seeded with the specified value.
+.It Fl X Ar exclude-list
+The specified file contains
+.Xr fnmatch 3
+patterns matching files to be excluded from
+the specification, one to a line.
+If the pattern contains a
+.Ql \&/
+character, it will be matched against entire pathnames (relative to
+the starting directory); otherwise,
+it will be matched against basenames only. No comments are allowed in
+the
+.Ar exclude-list
+file.
+.El
+.Pp
+Specifications are mostly composed of ``keywords'', i.e. strings that
+that specify values relating to files.
+No keywords have default values, and if a keyword has no value set, no
+checks based on it are performed.
+.Pp
+Currently supported keywords are as follows:
+.Bl -tag -width Cm
+.It Cm cksum
+The checksum of the file using the default algorithm specified by
+the
+.Xr cksum 1
+utility.
+.It Cm flags
+The file flags as a symbolic name. See
+.Xr chflags 1
+for information on these names. If no flags are to be set the string
+.Dq none
+may be used to override the current default.
+.It Cm ignore
+Ignore any file hierarchy below this file.
+.It Cm gid
+The file group as a numeric value.
+.It Cm gname
+The file group as a symbolic name.
+.It Cm md5digest
+The MD5 message digest of the file.
+.It Cm sha1digest
+The
+.Tn FIPS
+160-1
+.Pq Dq Tn SHA-1
+message digest of the file.
+.It Cm ripemd160digest
+The
+.Tn RIPEMD160
+message digest of the file.
+.It Cm mode
+The current file's permissions as a numeric (octal) or symbolic
+value.
+.It Cm nlink
+The number of hard links the file is expected to have.
+.It Cm nochange
+Make sure this file or directory exists but otherwise ignore all attributes.
+.It Cm uid
+The file owner as a numeric value.
+.It Cm uname
+The file owner as a symbolic name.
+.It Cm size
+The size, in bytes, of the file.
+.It Cm link
+The file the symbolic link is expected to reference.
+.It Cm time
+The last modification time of the file.
+.It Cm type
+The type of the file; may be set to any one of the following:
+.Pp
+.Bl -tag -width Cm -compact
+.It Cm block
+block special device
+.It Cm char
+character special device
+.It Cm dir
+directory
+.It Cm fifo
+fifo
+.It Cm file
+regular file
+.It Cm link
+symbolic link
+.It Cm socket
+socket
+.El
+.El
+.Pp
+The default set of keywords are
+.Cm flags ,
+.Cm gid ,
+.Cm mode ,
+.Cm nlink ,
+.Cm size ,
+.Cm link ,
+.Cm time ,
+and
+.Cm uid .
+.Pp
+There are four types of lines in a specification.
+.Pp
+The first type of line sets a global value for a keyword, and consists of
+the string ``/set'' followed by whitespace, followed by sets of keyword/value
+pairs, separated by whitespace.
+Keyword/value pairs consist of a keyword, followed by an equals sign
+(``=''), followed by a value, without whitespace characters.
+Once a keyword has been set, its value remains unchanged until either
+reset or unset.
+.Pp
+The second type of line unsets keywords and consists of the string
+``/unset'', followed by whitespace, followed by one or more keywords,
+separated by whitespace.
+.Pp
+The third type of line is a file specification and consists of a file
+name, followed by whitespace, followed by zero or more whitespace
+separated keyword/value pairs.
+The file name may be preceded by whitespace characters.
+The file name may contain any of the standard file name matching
+characters (``['', ``]'', ``?'' or ``*''), in which case files
+in the hierarchy will be associated with the first pattern that
+they match.
+.Pp
+Each of the keyword/value pairs consist of a keyword, followed by an
+equals sign (``=''), followed by the keyword's value, without
+whitespace characters.
+These values override, without changing, the global value of the
+corresponding keyword.
+.Pp
+All paths are relative.
+Specifying a directory will cause subsequent files to be searched
+for in that directory hierarchy.
+Which brings us to the last type of line in a specification: a line
+containing only the string
+.Dq Pa ..\&
+causes the current directory
+path to ascend one level.
+.Pp
+Empty lines and lines whose first non-whitespace character is a hash
+mark (``#'') are ignored.
+.Pp
+The
+.Nm
+utility exits with a status of 0 on success, 1 if any error occurred,
+and 2 if the file hierarchy did not match the specification.
+A status of 2 is converted to a status of 0 if the
+.Fl U
+option is used.
+.Sh EXAMPLES
+To detect system binaries that have been ``trojan horsed'', it is recommended
+that
+.Nm
+.Fl K
+.Cm sha1digest
+be run on the filesystems, and a copy of the results stored on a different
+machine, or, at least, in encrypted form.
+The output file itself should be digested using the
+.Xr md5 1
+utility.
+Then, periodically,
+.Nm
+and
+.Xr md5 1
+should be run against the on-line specifications.
+While it is possible for the bad guys to change the on-line specifications
+to conform to their modified binaries, it is believed to be
+impractical for them to create a modified specification which has
+the same MD5 digest as the original.
+.Pp
+The
+.Fl d
+and
+.Fl u
+options can be used in combination to create directory hierarchies
+for distributions and other such things; the files in
+.Pa /etc/mtree
+were used to create almost all directories in this
+.Fx
+distribution.
+.Sh FILES
+.Bl -tag -width /etc/mtree -compact
+.It Pa /etc/mtree
+system specification directory
+.El
+.Sh DIAGNOSTICS
+.Ex -std
+.Sh SEE ALSO
+.Xr chflags 1 ,
+.Xr chgrp 1 ,
+.Xr chmod 1 ,
+.Xr cksum 1 ,
+.Xr md5 1 ,
+.Xr stat 2 ,
+.Xr fts 3 ,
+.Xr md5 3 ,
+.Xr chown 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.3 Reno .
+The
+.Tn MD5
+digest capability was added in
+.Fx 2.1 ,
+in response to the widespread use of programs which can spoof
+.Xr cksum 1 .
+The
+.Tn SHA-1
+and
+.Tn RIPEMD160
+digests were added in
+.Fx 4.0 ,
+as new attacks have demonstrated weaknesses in
+.Tn MD5 .
+Support for file flags was added in
+.Fx 4.0 ,
+and mostly comes from
+.Nx .
diff --git a/usr.sbin/mtree/mtree.c b/usr.sbin/mtree/mtree.c
new file mode 100644
index 0000000..331e669
--- /dev/null
+++ b/usr.sbin/mtree/mtree.c
@@ -0,0 +1,180 @@
+/*-
+ * 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[] =
+ "$FreeBSD$";
+#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_PHYSICAL;
+int cflag, dflag, eflag, iflag, nflag, qflag, 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;
+ init_excludes();
+
+ while ((ch = getopt(argc, argv, "cdef:iK:k:LnPp:qrs:UuxX:")) != -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 'L':
+ ftsoptions &= ~FTS_PHYSICAL;
+ ftsoptions |= FTS_LOGICAL;
+ break;
+ case 'n':
+ nflag = 1;
+ break;
+ case 'P':
+ ftsoptions &= ~FTS_LOGICAL;
+ ftsoptions |= FTS_PHYSICAL;
+ break;
+ case 'p':
+ dir = optarg;
+ break;
+ case 'q':
+ qflag = 1;
+ break;
+ case 'r':
+ rflag = 1;
+ break;
+ case 's':
+ sflag = 1;
+ crc_total = ~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 'X':
+ read_excludes_file(optarg);
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc)
+ usage();
+
+ if (dir && chdir(dir))
+ err(1, "%s", dir);
+
+ if ((cflag || sflag) && !getwd(fullpath))
+ errx(1, "%s", fullpath);
+
+ if (cflag) {
+ cwalk();
+ exit(0);
+ }
+ status = verify();
+ if (Uflag & (status == MISMATCHEXIT))
+ status = 0;
+ exit(status);
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr,
+"usage: mtree [-LPUcdeinqrux] [-f spec] [-K key] [-k key] [-p path] [-s seed]\n"
+"\t[-X excludes]\n");
+ exit(1);
+}
diff --git a/usr.sbin/mtree/mtree.h b/usr.sbin/mtree/mtree.h
new file mode 100644
index 0000000..9d8af5d
--- /dev/null
+++ b/usr.sbin/mtree/mtree.h
@@ -0,0 +1,99 @@
+/*-
+ * 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
+ * $FreeBSD$
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+#define KEYDEFAULT \
+ (F_GID | F_MODE | F_NLINK | F_SIZE | F_SLINK | F_TIME | F_UID | F_FLAGS)
+
+#define MISMATCHEXIT 2
+
+typedef struct _node {
+ struct _node *parent, *child; /* up, down */
+ struct _node *prev, *next; /* left, right */
+ off_t st_size; /* size */
+ struct timespec st_mtimespec; /* last modification time */
+ u_long cksum; /* check sum */
+ char *md5digest; /* MD5 digest */
+ char *sha1digest; /* SHA-1 digest */
+ char *rmd160digest; /* RIPEMD160 digest */
+ char *slink; /* symbolic link reference */
+ uid_t st_uid; /* uid */
+ gid_t st_gid; /* gid */
+#define MBITS (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO)
+ mode_t st_mode; /* mode */
+ u_long st_flags; /* flags */
+ nlink_t st_nlink; /* link count */
+
+#define F_CKSUM 0x0001 /* check sum */
+#define F_DONE 0x0002 /* directory done */
+#define F_GID 0x0004 /* gid */
+#define F_GNAME 0x0008 /* group name */
+#define F_IGN 0x0010 /* ignore */
+#define F_MAGIC 0x0020 /* name has magic chars */
+#define F_MODE 0x0040 /* mode */
+#define F_NLINK 0x0080 /* number of links */
+#define F_SIZE 0x0100 /* size */
+#define F_SLINK 0x0200 /* link count */
+#define F_TIME 0x0400 /* modification time */
+#define F_TYPE 0x0800 /* file type */
+#define F_UID 0x1000 /* uid */
+#define F_UNAME 0x2000 /* user name */
+#define F_VISIT 0x4000 /* file visited */
+#define F_MD5 0x8000 /* MD5 digest */
+#define F_NOCHANGE 0x10000 /* If owner/mode "wrong", do */
+ /* not change */
+#define F_SHA1 0x20000 /* SHA-1 digest */
+#define F_RMD160 0x40000 /* RIPEMD160 digest */
+#define F_FLAGS 0x80000 /* file flags */
+ u_int flags; /* items set */
+
+#define F_BLOCK 0x001 /* block special */
+#define F_CHAR 0x002 /* char special */
+#define F_DIR 0x004 /* directory */
+#define F_FIFO 0x008 /* fifo */
+#define F_FILE 0x010 /* regular file */
+#define F_LINK 0x020 /* symbolic link */
+#define F_SOCK 0x040 /* socket */
+ u_char type; /* file type */
+
+ char name[1]; /* file name (must be last) */
+} NODE;
+
+#define RP(p) \
+ ((p)->fts_path[0] == '.' && (p)->fts_path[1] == '/' ? \
+ (p)->fts_path + 2 : (p)->fts_path)
diff --git a/usr.sbin/mtree/spec.c b/usr.sbin/mtree/spec.c
new file mode 100644
index 0000000..4cbf80b
--- /dev/null
+++ b/usr.sbin/mtree/spec.c
@@ -0,0 +1,323 @@
+/*-
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE 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[] =
+ "$FreeBSD$";
+#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;
+ NODE *ip;
+{
+ register int type;
+ char *kw, *val = NULL;
+ struct group *gr;
+ struct passwd *pw;
+ mode_t *m;
+ int value;
+ char *ep;
+
+ for (; (kw = strtok(t, "= \t\n")); t = NULL) {
+ ip->flags |= type = parsekey(kw, &value);
+ if (value && (val = strtok(NULL, " \t\n")) == NULL)
+ errx(1, "line %d: missing value", lineno);
+ switch(type) {
+ case F_CKSUM:
+ ip->cksum = strtoul(val, &ep, 10);
+ if (*ep)
+ errx(1, "line %d: invalid checksum %s",
+ lineno, val);
+ break;
+ case F_MD5:
+ ip->md5digest = strdup(val);
+ if(!ip->md5digest) {
+ errx(1, "strdup");
+ }
+ break;
+ case F_SHA1:
+ ip->sha1digest = strdup(val);
+ if(!ip->sha1digest) {
+ errx(1, "strdup");
+ }
+ break;
+ case F_RMD160:
+ ip->rmd160digest = strdup(val);
+ if(!ip->rmd160digest) {
+ errx(1, "strdup");
+ }
+ break;
+ case F_FLAGS:
+ if (strcmp("none", val) == 0)
+ ip->st_flags = 0;
+ else if (strtofflags(&val, &ip->st_flags, NULL) != 0)
+ errx(1, "line %d: invalid flag %s",lineno, val);
+ break;
+ case F_GID:
+ ip->st_gid = strtoul(val, &ep, 10);
+ if (*ep)
+ errx(1, "line %d: invalid gid %s", lineno, val);
+ break;
+ case F_GNAME:
+ if ((gr = getgrnam(val)) == NULL)
+ errx(1, "line %d: unknown group %s", lineno, val);
+ ip->st_gid = gr->gr_gid;
+ break;
+ case F_IGN:
+ /* just set flag bit */
+ break;
+ case F_MODE:
+ if ((m = setmode(val)) == NULL)
+ errx(1, "line %d: invalid file mode %s",
+ lineno, val);
+ ip->st_mode = getmode(m, 0);
+ free(m);
+ break;
+ case F_NLINK:
+ ip->st_nlink = strtoul(val, &ep, 10);
+ if (*ep)
+ errx(1, "line %d: invalid link count %s",
+ lineno, val);
+ break;
+ case F_SIZE:
+ ip->st_size = strtoq(val, &ep, 10);
+ if (*ep)
+ errx(1, "line %d: invalid size %s",
+ lineno, val);
+ break;
+ case F_SLINK:
+ 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..0da0e12
--- /dev/null
+++ b/usr.sbin/mtree/verify.c
@@ -0,0 +1,244 @@
+/*-
+ * 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[] =
+ "$FreeBSD$";
+#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, qflag, 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))) {
+ if (check_excludes(p->fts_name, p->fts_path)) {
+ fts_set(t, p, FTS_SKIP);
+ continue;
+ }
+ switch(p->fts_info) {
+ case FTS_D:
+ case FTS_SL:
+ break;
+ case FTS_DP:
+ if (specdepth > p->fts_level) {
+ for (level = level->parent; level->prev;
+ level = level->prev);
+ --specdepth;
+ }
+ continue;
+ case FTS_DNR:
+ case FTS_ERR:
+ case FTS_NS:
+ warnx("%s: %s", RP(p), strerror(p->fts_errno));
+ continue;
+ default:
+ if (dflag)
+ continue;
+ }
+
+ if (specdepth != p->fts_level)
+ goto extra;
+ for (ep = level; ep; ep = ep->next)
+ if ((ep->flags & F_MAGIC &&
+ !fnmatch(ep->name, p->fts_name, FNM_PATHNAME)) ||
+ !strcmp(ep->name, p->fts_name)) {
+ ep->flags |= F_VISIT;
+ if ((ep->flags & F_NOCHANGE) == 0 &&
+ compare(ep->name, ep, p))
+ rval = MISMATCHEXIT;
+ if (ep->flags & F_IGN)
+ (void)fts_set(t, p, FTS_SKIP);
+ else if (ep->child && ep->type == F_DIR &&
+ p->fts_info == FTS_D) {
+ level = ep->child;
+ ++specdepth;
+ }
+ break;
+ }
+
+ if (ep)
+ continue;
+extra:
+ if (!eflag) {
+ (void)printf("%s extra", RP(p));
+ if (rflag) {
+ if ((S_ISDIR(p->fts_statp->st_mode)
+ ? rmdir : unlink)(p->fts_accpath)) {
+ (void)printf(", not removed: %s",
+ strerror(errno));
+ } else
+ (void)printf(", removed");
+ }
+ (void)putchar('\n');
+ }
+ (void)fts_set(t, p, FTS_SKIP);
+ }
+ (void)fts_close(t);
+ if (sflag)
+ warnx("%s checksum: %lu", fullpath, crc_total);
+ return (rval);
+}
+
+static void
+miss(p, tail)
+ register NODE *p;
+ register char *tail;
+{
+ register int create;
+ register char *tp;
+ const char *type;
+
+ for (; p; p = p->next) {
+ if (p->type != F_DIR && (dflag || p->flags & F_VISIT))
+ continue;
+ (void)strcpy(tail, p->name);
+ if (!(p->flags & F_VISIT)) {
+ /* Don't print missing message if file exists as a
+ symbolic link and the -q flag is set. */
+ struct stat statbuf;
+
+ if (qflag && stat(path, &statbuf) == 0)
+ p->flags |= F_VISIT;
+ else
+ (void)printf("%s missing", path);
+ }
+ if (p->type != F_DIR && p->type != F_LINK) {
+ putchar('\n');
+ continue;
+ }
+
+ create = 0;
+ if (p->type == F_LINK)
+ type = "symlink";
+ else
+ type = "directory";
+ if (!(p->flags & F_VISIT) && uflag) {
+ if (!(p->flags & (F_UID | F_UNAME)))
+ (void)printf(" (%s not created: user not specified)", type);
+ else if (!(p->flags & (F_GID | F_GNAME)))
+ (void)printf(" (%s not created: group not specified)", type);
+ else if (p->type == F_LINK) {
+ if (symlink(p->slink, path))
+ (void)printf(" (symlink not created: %s)\n",
+ strerror(errno));
+ else
+ (void)printf(" (created)\n");
+ if (lchown(path, p->st_uid, p->st_gid))
+ (void)printf("%s: user/group not modified: %s\n",
+ path, strerror(errno));
+ continue;
+ } else if (!(p->flags & F_MODE))
+ (void)printf(" (directory not created: mode not specified)");
+ else if (mkdir(path, S_IRWXU))
+ (void)printf(" (directory not created: %s)",
+ strerror(errno));
+ else {
+ create = 1;
+ (void)printf(" (created)");
+ }
+ }
+ if (!(p->flags & F_VISIT))
+ (void)putchar('\n');
+
+ for (tp = tail; *tp; ++tp);
+ *tp = '/';
+ miss(p->child, tp + 1);
+ *tp = '\0';
+
+ if (!create)
+ continue;
+ if (chown(path, p->st_uid, p->st_gid)) {
+ (void)printf("%s: user/group/mode not modified: %s\n",
+ path, strerror(errno));
+ (void)printf("%s: warning: file mode %snot set\n", path,
+ (p->flags & F_FLAGS) ? "and file flags " : "");
+ continue;
+ }
+ if (chmod(path, p->st_mode))
+ (void)printf("%s: permissions not set: %s\n",
+ path, strerror(errno));
+ if ((p->flags & F_FLAGS) && p->st_flags &&
+ chflags(path, p->st_flags))
+ (void)printf("%s: file flags not set: %s\n",
+ path, strerror(errno));
+ }
+}
diff --git a/usr.sbin/named.reload/Makefile b/usr.sbin/named.reload/Makefile
new file mode 100644
index 0000000..5cb467a1
--- /dev/null
+++ b/usr.sbin/named.reload/Makefile
@@ -0,0 +1,15 @@
+# $FreeBSD$
+
+.include "${.CURDIR}/../named/Makefile.inc"
+
+SCRIPTS= named.reload
+MAN= named.reload.8
+CLEANFILES+= ${SCRIPTS}
+SCRIPTSNAME= ${SCRIPTS}
+
+named.reload: named.reload.sh ${BIND_DIR}/Makefile
+ sed -e "s|%INDOT%|${INDOT}|" \
+ -e "s|%DESTSBIN%|${DESTSBIN}|" \
+ < ${.CURDIR}/named.reload.sh > ${.TARGET}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/named.reload/named.reload.8 b/usr.sbin/named.reload/named.reload.8
new file mode 100644
index 0000000..6bda6c9
--- /dev/null
+++ b/usr.sbin/named.reload/named.reload.8
@@ -0,0 +1,70 @@
+.\" ++Copyright++ 1987, 1993
+.\" -
+.\" Copyright (c) 1987, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\" -
+.\" Portions Copyright (c) 1993 by Digital Equipment Corporation.
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies, and that
+.\" the name of Digital Equipment Corporation not be used in advertising or
+.\" publicity pertaining to distribution of the document or software without
+.\" specific, written prior permission.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
+.\" WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
+.\" CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+.\" DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+.\" PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+.\" ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+.\" SOFTWARE.
+.\" -
+.\" --Copyright--
+.\"
+.\" from hostname.7 6.4 (Berkeley) 1/16/90
+.\" $FreeBSD$
+.\"
+.Dd June 26, 1993
+.Dt @INDOT_U@NAMED.RELOAD @SYS_OPS_EXT_U@
+.Os
+.Sh NAME
+.Nm @INDOT@named.reload
+.Nd "cause the name server to synchronize its database"
+.Sh DESCRIPTION
+This command runs
+.Xr ndc @SYS_OPS_EXT@
+which reloads the running name server.
+.Sh SEE ALSO
+.Xr @INDOT@named @SYS_OPS_EXT@ ,
+.Xr @INDOT@named.restart @SYS_OPS_EXT@ ,
+.Xr @INDOT@ndc @SYS_OPS_EXT@
diff --git a/usr.sbin/named.reload/named.reload.sh b/usr.sbin/named.reload/named.reload.sh
new file mode 100644
index 0000000..4a657a7
--- /dev/null
+++ b/usr.sbin/named.reload/named.reload.sh
@@ -0,0 +1,7 @@
+#!/bin/sh -
+#
+# from named.reload 5.2 (Berkeley) 6/27/89
+# $FreeBSD$
+#
+
+exec %DESTSBIN%/%INDOT%ndc reload
diff --git a/usr.sbin/named.restart/Makefile b/usr.sbin/named.restart/Makefile
new file mode 100644
index 0000000..e974bf8
--- /dev/null
+++ b/usr.sbin/named.restart/Makefile
@@ -0,0 +1,15 @@
+# $FreeBSD$
+
+.include "${.CURDIR}/../named/Makefile.inc"
+
+SCRIPTS= named.restart
+MAN= named.restart.8
+CLEANFILES+= ${SCRIPTS}
+SCRIPTSNAME= ${SCRIPTS}
+
+named.restart: named.restart.sh ${BIND_DIR}/Makefile
+ sed -e "s|%INDOT%|${INDOT}|" \
+ -e "s|%DESTSBIN%|${DESTSBIN}|" \
+ < ${.CURDIR}/named.restart.sh > named.restart
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/named.restart/named.restart.8 b/usr.sbin/named.restart/named.restart.8
new file mode 100644
index 0000000..2270225
--- /dev/null
+++ b/usr.sbin/named.restart/named.restart.8
@@ -0,0 +1,77 @@
+.\" ++Copyright++ 1987, 1993
+.\" -
+.\" Copyright (c) 1987, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\" -
+.\" Portions Copyright (c) 1993 by Digital Equipment Corporation.
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies, and that
+.\" the name of Digital Equipment Corporation not be used in advertising or
+.\" publicity pertaining to distribution of the document or software without
+.\" specific, written prior permission.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
+.\" WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
+.\" CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+.\" DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+.\" PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+.\" ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+.\" SOFTWARE.
+.\" -
+.\" --Copyright--
+.\"
+.\" from hostname.7 6.4 (Berkeley) 1/16/90
+.\" $FreeBSD$
+.\"
+.Dd June 26, 1993
+.Dt @INDOT_U@NAMED.RESTART @SYS_OPS_EXT_U@
+.Os
+.Sh NAME
+.Nm @INDOT@named.restart
+.Nd "stop and restart the name server"
+.Sh DESCRIPTION
+This command runs
+.Xr ndc @SYS_OPS_EXT@
+which restarts the running name server with the command line options
+specified in
+.Xr rc.conf 5 .
+.Sh BUGS
+Does not wait after killing the old server before starting a new one; since
+the server could take some time to die and the new one will experience a
+fatal error if the old one isn't gone by the time it starts, you can be left
+in a situation where you have no name server at all.
+.Sh SEE ALSO
+.Xr @INDOT@named @SYS_OPS_EXT@ ,
+.Xr @INDOT@named.reload @SYS_OPS_EXT@ ,
+.Xr @INDOT@ndc @SYS_OPS_EXT@
diff --git a/usr.sbin/named.restart/named.restart.sh b/usr.sbin/named.restart/named.restart.sh
new file mode 100644
index 0000000..1fa6cf8
--- /dev/null
+++ b/usr.sbin/named.restart/named.restart.sh
@@ -0,0 +1,13 @@
+#!/bin/sh -
+#
+# from named.restart 5.4 (Berkeley) 6/27/89
+# $FreeBSD$
+#
+
+if [ -r /etc/defaults/rc.conf ]; then
+ . /etc/defaults/rc.conf
+ source_rc_confs
+elif [ -r /etc/rc.conf ]; then
+ . /etc/rc.conf
+fi
+exec %DESTSBIN%/%INDOT%ndc -n ${named_program} restart ${named_flags}
diff --git a/usr.sbin/named/Makefile b/usr.sbin/named/Makefile
new file mode 100644
index 0000000..5dda834
--- /dev/null
+++ b/usr.sbin/named/Makefile
@@ -0,0 +1,45 @@
+# $FreeBSD$
+
+USE_LIBBIND= true
+.include "${.CURDIR}/Makefile.inc"
+
+.PATH: ${BIND_DIR}/bin/named
+.PATH: ${BIND_DIR}/doc/man
+
+PROG= named
+MAN= named.conf.5 named.8 named-bootconf.8 nsupdate.8
+SRCS= tmp_version.c pathnames.h \
+ db_dump.c db_load.c db_lookup.c db_save.c db_update.c \
+ db_glue.c db_ixfr.c db_sec.c db_tsig.c \
+ ns_parser.y ns_lexer.c ns_parseutil.c ns_ctl.c \
+ ns_forw.c ns_init.c ns_main.c ns_maint.c ns_req.c \
+ ns_resp.c ns_stats.c ns_ncache.c ns_xfr.c ns_glue.c \
+ ns_udp.c ns_config.c ns_update.c ns_ixfr.c ns_signal.c \
+ ns_sort.c ns_notify.c
+
+.if exists(${.OBJDIR}/../../lib/libisc)
+LIBISCDIR:= ${.OBJDIR}/../../lib/libisc
+.else
+LIBISCDIR!= cd ${.CURDIR}/../../lib/libisc; make -V .OBJDIR
+.endif
+LIBISC:= ${LIBISCDIR}/libisc.a
+
+DPADD+= ${LIBISC}
+LDADD+= ${LIBISC}
+
+HTMLS= acl.html address_list.html comments.html config.html controls.html \
+ docdef.html example.html include.html index.html key.html \
+ logging.html master.html options.html server.html trusted-keys.html \
+ zone.html
+MISCS= DynamicUpdate FAQ.1of2 FAQ.2of2 rfc2317-notes.txt style.txt
+FILES= ${HTMLS} ${MISCS}
+.PATH: ${BIND_DIR}/doc/html ${BIND_DIR}/doc/misc
+
+.for file in ${HTMLS}
+FILESDIR_${file}= ${DOCDIR}/bind/html
+.endfor
+.for file in ${MISCS}
+FILESDIR_${file}= ${DOCDIR}/bind/misc
+.endfor
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/named/Makefile.inc b/usr.sbin/named/Makefile.inc
new file mode 100644
index 0000000..27b8c40
--- /dev/null
+++ b/usr.sbin/named/Makefile.inc
@@ -0,0 +1,59 @@
+# From: Id: Makefile.inc,v 8.4 1996/03/03 17:42:43 vixie Exp
+# $FreeBSD$
+
+.ifndef (Mk.Inc)
+Mk.Inc?=defined
+
+BIND_DIR= ${.CURDIR}/../../contrib/bind
+
+VER!= cat ${BIND_DIR}/Version
+
+PS= ps
+PIDDIR= /var/run
+DESTETC= /etc/namedb
+DESTEXEC= /usr/libexec
+DESTRUN= /var/run
+DESTSBIN= /usr/sbin
+DESTHELP= /usr/share/misc
+
+CFLAGS+= -I${BIND_DIR}/port/freebsd/include \
+ -I${.CURDIR}/../../contrib/bind/bin/named
+
+# This is mostly for named and named-xfer
+.if defined(USE_LIBBIND)
+# Sadly, mkdep doesn't know about -idirafter, which would be ideal here.
+#CFLAGS+= -I${.CURDIR}/../../include -I${BIND_DIR}/include
+CFLAGS+= -I${BIND_DIR}/include
+
+.if exists(${.OBJDIR}/../../lib/libbind)
+LIBBINDDIR:= ${.OBJDIR}/../../lib/libbind
+.else
+LIBBINDDIR!= cd ${.CURDIR}/../../lib/libbind; make -V .OBJDIR
+.endif
+LIBBIND:= ${LIBBINDDIR}/libbind.a
+
+DPADD+= ${LIBBIND}
+LDADD+= ${LIBBIND}
+
+CLEANFILES+= tmp_version.c pathnames.h
+CFLAGS+= -I.
+
+tmp_version.c: version.c ${BIND_DIR}/Version
+ (u=$${USER-root} d=`pwd` h=`hostname` t=`LC_ALL=C date`; \
+ sed -e "s|%WHEN%|$${t}|" -e "s|%VERSION%|"${VER}"|" \
+ -e "s|%WHOANDWHERE%|$${u}@$${h}:$${d}|" \
+ < ${BIND_DIR}/bin/named/version.c > tmp_version.c)
+
+pathnames.h: ${BIND_DIR}/bin/named/pathtemplate.h \
+ ${.CURDIR}/../../usr.sbin/named/Makefile.inc
+ rm -f pathnames.h
+ sed -e "s|%DESTSBIN%|${DESTSBIN}|" \
+ -e "s|%DESTEXEC%|${DESTEXEC}|" \
+ -e "s|%DESTETC%|${DESTETC}|" \
+ -e "s|%DESTRUN%|${DESTRUN}|" \
+ < ${BIND_DIR}/bin/named/pathtemplate.h > pathnames.h
+
+.endif
+
+.include "Makefile.maninc"
+.endif
diff --git a/usr.sbin/named/Makefile.maninc b/usr.sbin/named/Makefile.maninc
new file mode 100644
index 0000000..8e7e07e
--- /dev/null
+++ b/usr.sbin/named/Makefile.maninc
@@ -0,0 +1,58 @@
+# From: Id: Makefile.maninc,v 8.1 1994/12/15 06:23:43 vixie Exp
+# $FreeBSD$
+
+# (the BIND kit's man page Makefile has gotten bad and crazy over the years.
+# this file has to be included AFTER bsd.prog.mk (which includes bsd.man.mk))
+CMD_EXT= 1
+SYSCALL_EXT= 2
+BSD_SYSCALL_EXT=2
+LIB_C_EXT= 3
+LIB_NETWORK_EXT=3
+FORMAT_EXT= 5
+DESC_EXT= 7
+SYS_OPS_EXT= 8
+
+EXT_SED_CMD = INDOT_U=`echo "${INDOT}"|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" \
+ -e "s/^ *$$/.Pp/"
+
+MANFILTER= ${EXT_SED_CMD}
diff --git a/usr.sbin/ndc/Makefile b/usr.sbin/ndc/Makefile
new file mode 100644
index 0000000..99f9784
--- /dev/null
+++ b/usr.sbin/ndc/Makefile
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+USE_LIBBIND= true
+.include "${.CURDIR}/../named/Makefile.inc"
+
+.PATH: ${BIND_DIR}/bin/ndc
+.PATH: ${BIND_DIR}/doc/man
+
+PROG= ndc
+MAN= ndc.8
+SRCS= pathnames.h \
+ ndc.c
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ndp/Makefile b/usr.sbin/ndp/Makefile
new file mode 100644
index 0000000..2af816b
--- /dev/null
+++ b/usr.sbin/ndp/Makefile
@@ -0,0 +1,25 @@
+# Copyright (c) 1996 WIDE Project. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modifications, are permitted provided that the above copyright notice
+# and this paragraph are duplicated in all such forms and that any
+# documentation, advertising materials, and other materials related to
+# such distribution and use acknowledge that the software was developed
+# by the WIDE Project, Japan. The name of the Project may not be used to
+# endorse or promote products derived from this software without
+# specific prior written permission. THIS SOFTWARE IS PROVIDED ``AS IS''
+# AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+# LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE.
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../contrib/tcpdump
+
+PROG= ndp
+MAN= ndp.8
+SRCS= ndp.c gmt2local.c
+
+CFLAGS+= -DINET6
+CFLAGS+= -I. -I${.CURDIR} -I${.CURDIR}/../../contrib/tcpdump
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ndp/gnuc.h b/usr.sbin/ndp/gnuc.h
new file mode 100644
index 0000000..c641296
--- /dev/null
+++ b/usr.sbin/ndp/gnuc.h
@@ -0,0 +1,2 @@
+/* $FreeBSD$ */
+/* this is dummy to pacify gmt2local.c. */
diff --git a/usr.sbin/ndp/ndp.8 b/usr.sbin/ndp/ndp.8
new file mode 100644
index 0000000..1f96902
--- /dev/null
+++ b/usr.sbin/ndp/ndp.8
@@ -0,0 +1,182 @@
+.\" $FreeBSD$
+.\" $KAME: ndp.8,v 1.15 2001/02/08 07:17:03 itojun Exp $
+.\"
+.\" Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the project nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd May 17, 1998
+.Dt NDP 8
+.Os
+.\"
+.Sh NAME
+.Nm ndp
+.Nd control/diagnose IPv6 neighbor discovery protocol
+.\"
+.Sh SYNOPSIS
+.Nm
+.Fl a
+.Op Fl nt
+.Nm
+.Fl A Ar wait
+.Op Fl nt
+.Nm
+.Fl c
+.Op Fl nt
+.Nm
+.Fl d
+.Op Fl nt
+.Ar hostname
+.Nm
+.Fl f
+.Op Fl nt
+.Ar filename
+.Nm
+.Fl H
+.Nm
+.Fl I
+.Op Cm delete | Ar interface
+.Nm
+.Fl i
+.Ar interface
+.Op Ar flags...
+.Nm
+.Fl p
+.Nm
+.Fl P
+.Nm
+.Fl r
+.Nm
+.Fl R
+.Nm
+.Fl s
+.Op Fl nt
+.Ar nodename
+.Ar ether_addr
+.Op Li temp
+.Op Li proxy
+.\"
+.Sh DESCRIPTION
+The
+.Nm
+command manipulates the address mapping table
+used by Neighbor Discovery Protocol (NDP).
+.Bl -tag -width Ds
+.It Fl a
+Dump the currently existing NDP entries.
+.It Fl A Ar wait
+Repeat
+.Fl a
+(dump NDP entries)
+every
+.Ar wait
+seconds.
+.It Fl c
+Erase all the NDP entries.
+.It Fl d
+Delete specified NDP entry.
+.It Fl f
+Parse the file specified by
+.Ar filename .
+.It Fl H
+Harmonize consistency between the routing table and the default router
+list; install the top entry of the list into the kernel routing table.
+.It Fl I Op Cm delete | Ar interface
+Shows or specifies the default interface used as the default route when
+there is no default router.
+If no argument is given to the option,
+the current default interface will be shown.
+If an
+.Ar interface
+is specified, the interface will be used as the default.
+If a special keyword
+.Ic delete
+is specified, the current default interface will be deleted from the kernel.
+.It Fl i Ar interface Op Ar flags...
+View ND information for the specified interface.
+If additional arguments
+.Ar flags
+are given,
+.Nm
+sets or clears the specified flags for the interface.
+Possible flags are as follows.
+All of the flags can begin with the
+special character
+.Ql - ,
+which means the flag should be cleared.
+.\"
+.Bl -tag -width Ds -compact
+.It Xo
+.Ic nud
+.Xc
+turn on or off NUD (Neighbor Unreachability Detection) on the
+interface.
+NUD is usually turned on by default.
+.El
+.It Fl n
+Do not try to resolve numeric address to hostname.
+.It Fl p
+Show prefix list.
+.It Fl P
+Flush all the entries in the prefix list.
+.It Fl r
+Show default router list.
+.It Fl R
+Flush all the entries in the default router list.
+.It Fl s
+Register an NDP entry for a node.
+The entry will be permanent unless the word
+.Li temp
+is given in the command.
+If the word
+.Li proxy
+is given, this system will act as an proxy NDP server,
+responding to requests for
+.Ar hostname
+even though the host address is not its own.
+.It Fl t
+Print timestamp on each entries,
+to make it possible to merge output with
+.Xr tcpdump 1 .
+Most useful when used with
+.Fl A .
+.El
+.\"
+.Sh RETURN VALUES
+The
+.Nm
+command will exit with 0 on success, and non-zero on errors.
+.\"
+.Sh SEE ALSO
+.Xr arp 8
+.\"
+.Sh HISTORY
+The
+.Nm
+command first appeared in WIDE Hydrangea IPv6 protocol stack kit.
+.\"
+.\" .Sh BUGS
+.\" (to be written)
diff --git a/usr.sbin/ndp/ndp.c b/usr.sbin/ndp/ndp.c
new file mode 100644
index 0000000..8e2fb78
--- /dev/null
+++ b/usr.sbin/ndp/ndp.c
@@ -0,0 +1,1528 @@
+/* $FreeBSD$ */
+/* $KAME: ndp.c,v 1.65 2001/05/08 04:36:34 itojun Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) 1984, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Sun Microsystems, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Based on:
+ * "@(#) Copyright (c) 1984, 1993\n\
+ * The Regents of the University of California. All rights reserved.\n";
+ *
+ * "@(#)arp.c 8.2 (Berkeley) 1/2/94";
+ */
+
+/*
+ * ndp - display, set, delete and flush neighbor cache
+ */
+
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+#include <sys/queue.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <netinet/icmp6.h>
+#include <netinet6/in6_var.h>
+#include <netinet6/nd6.h>
+
+#include <arpa/inet.h>
+
+#include <netdb.h>
+#include <errno.h>
+#include <nlist.h>
+#include <stdio.h>
+#include <string.h>
+#include <paths.h>
+#include <err.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include "gmt2local.h"
+
+#ifndef NI_WITHSCOPEID
+#define NI_WITHSCOPEID 0
+#endif
+
+/* packing rule for routing socket */
+#define ROUNDUP(a) \
+ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
+
+static int pid;
+static int cflag;
+static int nflag;
+static int tflag;
+static int32_t thiszone; /* time difference with gmt */
+static int s = -1;
+static int repeat = 0;
+
+char ntop_buf[INET6_ADDRSTRLEN]; /* inet_ntop() */
+char host_buf[NI_MAXHOST]; /* getnameinfo() */
+char ifix_buf[IFNAMSIZ]; /* if_indextoname() */
+
+int main __P((int, char **));
+int file __P((char *));
+void getsocket __P((void));
+int set __P((int, char **));
+void get __P((char *));
+int delete __P((char *));
+void dump __P((struct in6_addr *));
+static struct in6_nbrinfo *getnbrinfo __P((struct in6_addr *addr,
+ int ifindex, int));
+static char *ether_str __P((struct sockaddr_dl *));
+int ndp_ether_aton __P((char *, u_char *));
+void usage __P((void));
+int rtmsg __P((int));
+void ifinfo __P((int, char **));
+void rtrlist __P((void));
+void plist __P((void));
+void pfx_flush __P((void));
+void rtrlist __P((void));
+void rtr_flush __P((void));
+void harmonize_rtr __P((void));
+#ifdef SIOCSDEFIFACE_IN6 /* XXX: check SIOCGDEFIFACE_IN6 as well? */
+static void getdefif __P((void));
+static void setdefif __P((char *));
+#endif
+static char *sec2str __P((time_t t));
+static char *ether_str __P((struct sockaddr_dl *sdl));
+static void ts_print __P((const struct timeval *));
+
+static char *rtpref_str[] = {
+ "medium", /* 00 */
+ "high", /* 01 */
+ "rsv", /* 10 */
+ "low" /* 11 */
+};
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int ch;
+ int aflag = 0, dflag = 0, sflag = 0, Hflag = 0,
+ pflag = 0, rflag = 0, Pflag = 0, Rflag = 0;
+
+ pid = getpid();
+ thiszone = gmt2local(0);
+ while ((ch = getopt(argc, argv, "acndfIilprstA:HPR")) != -1)
+ switch ((char)ch) {
+ case 'a':
+ aflag = 1;
+ break;
+ case 'c':
+ cflag = 1;
+ break;
+ case 'd':
+ dflag = 1;
+ break;
+ case 'I':
+#ifdef SIOCSDEFIFACE_IN6 /* XXX: check SIOCGDEFIFACE_IN6 as well? */
+ if (argc > 2)
+ setdefif(argv[2]);
+ getdefif(); /* always call it to print the result */
+ exit(0);
+#else
+ errx(1, "not supported yet");
+ /*NOTREACHED*/
+#endif
+ case 'i' :
+ argc -= optind;
+ argv += optind;
+ if (argc < 1)
+ usage();
+ ifinfo(argc, argv);
+ exit(0);
+ case 'n':
+ nflag = 1;
+ continue;
+ case 'p':
+ pflag = 1;
+ break;
+ case 'f' :
+ if (argc != 3)
+ usage();
+ file(argv[2]);
+ exit(0);
+ case 'l' :
+ /* obsolete, ignored */
+ break;
+ case 'r' :
+ rflag = 1;
+ break;
+ case 's':
+ sflag = 1;
+ break;
+ case 't':
+ tflag = 1;
+ break;
+ case 'A':
+ aflag = 1;
+ repeat = atoi(optarg);
+ if (repeat < 0)
+ usage();
+ break;
+ case 'H' :
+ Hflag = 1;
+ break;
+ case 'P':
+ Pflag = 1;
+ break;
+ case 'R':
+ Rflag = 1;
+ break;
+ default:
+ usage();
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (aflag || cflag) {
+ dump(0);
+ exit(0);
+ }
+ if (dflag) {
+ if (argc != 1)
+ usage();
+ delete(argv[0]);
+ exit(0);
+ }
+ if (pflag) {
+ plist();
+ exit(0);
+ }
+ if (rflag) {
+ rtrlist();
+ exit(0);
+ }
+ if (sflag) {
+ if (argc < 2 || argc > 4)
+ usage();
+ exit(set(argc, argv) ? 1 : 0);
+ }
+ if (Hflag) {
+ harmonize_rtr();
+ exit(0);
+ }
+ if (Pflag) {
+ pfx_flush();
+ exit(0);
+ }
+ if (Rflag) {
+ rtr_flush();
+ exit(0);
+ }
+
+ if (argc != 1)
+ usage();
+ get(argv[0]);
+ exit(0);
+}
+
+/*
+ * Process a file to set standard ndp entries
+ */
+int
+file(name)
+ char *name;
+{
+ FILE *fp;
+ int i, retval;
+ char line[100], arg[5][50], *args[5];
+
+ if ((fp = fopen(name, "r")) == NULL) {
+ fprintf(stderr, "ndp: cannot open %s\n", name);
+ exit(1);
+ }
+ args[0] = &arg[0][0];
+ args[1] = &arg[1][0];
+ args[2] = &arg[2][0];
+ args[3] = &arg[3][0];
+ args[4] = &arg[4][0];
+ retval = 0;
+ while(fgets(line, 100, fp) != NULL) {
+ i = sscanf(line, "%s %s %s %s %s", arg[0], arg[1], arg[2],
+ arg[3], arg[4]);
+ if (i < 2) {
+ fprintf(stderr, "ndp: bad line: %s\n", line);
+ retval = 1;
+ continue;
+ }
+ if (set(i, args))
+ retval = 1;
+ }
+ fclose(fp);
+ return (retval);
+}
+
+void
+getsocket()
+{
+ if (s < 0) {
+ s = socket(PF_ROUTE, SOCK_RAW, 0);
+ if (s < 0) {
+ perror("ndp: socket");
+ exit(1);
+ }
+ }
+}
+
+struct sockaddr_in6 so_mask = {sizeof(so_mask), AF_INET6 };
+struct sockaddr_in6 blank_sin = {sizeof(blank_sin), AF_INET6 }, sin_m;
+struct sockaddr_dl blank_sdl = {sizeof(blank_sdl), AF_LINK }, sdl_m;
+int expire_time, flags, found_entry;
+struct {
+ struct rt_msghdr m_rtm;
+ char m_space[512];
+} m_rtmsg;
+
+/*
+ * Set an individual neighbor cache entry
+ */
+int
+set(argc, argv)
+ int argc;
+ char **argv;
+{
+ register struct sockaddr_in6 *sin = &sin_m;
+ register struct sockaddr_dl *sdl;
+ register struct rt_msghdr *rtm = &(m_rtmsg.m_rtm);
+ struct addrinfo hints, *res;
+ int gai_error;
+ u_char *ea;
+ char *host = argv[0], *eaddr = argv[1];
+
+ getsocket();
+ argc -= 2;
+ argv += 2;
+ sdl_m = blank_sdl;
+ sin_m = blank_sin;
+
+ bzero(&hints, sizeof(hints));
+ hints.ai_family = AF_INET6;
+ gai_error = getaddrinfo(host, NULL, &hints, &res);
+ if (gai_error) {
+ fprintf(stderr, "ndp: %s: %s\n", host,
+ gai_strerror(gai_error));
+ return 1;
+ }
+ sin->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
+#ifdef __KAME__
+ if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr)) {
+ *(u_int16_t *)&sin->sin6_addr.s6_addr[2] =
+ htons(((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id);
+ }
+#endif
+ ea = (u_char *)LLADDR(&sdl_m);
+ if (ndp_ether_aton(eaddr, ea) == 0)
+ sdl_m.sdl_alen = 6;
+ flags = expire_time = 0;
+ while (argc-- > 0) {
+ if (strncmp(argv[0], "temp", 4) == 0) {
+ struct timeval time;
+ gettimeofday(&time, 0);
+ expire_time = time.tv_sec + 20 * 60;
+ } else if (strncmp(argv[0], "proxy", 5) == 0)
+ flags |= RTF_ANNOUNCE;
+ argv++;
+ }
+ if (rtmsg(RTM_GET) < 0) {
+ perror(host);
+ return (1);
+ }
+ sin = (struct sockaddr_in6 *)(rtm + 1);
+ sdl = (struct sockaddr_dl *)(ROUNDUP(sin->sin6_len) + (char *)sin);
+ if (IN6_ARE_ADDR_EQUAL(&sin->sin6_addr, &sin_m.sin6_addr)) {
+ if (sdl->sdl_family == AF_LINK &&
+ (rtm->rtm_flags & RTF_LLINFO) &&
+ !(rtm->rtm_flags & RTF_GATEWAY)) switch (sdl->sdl_type) {
+ case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023:
+ case IFT_ISO88024: case IFT_ISO88025:
+ goto overwrite;
+ }
+ /*
+ * IPv4 arp command retries with sin_other = SIN_PROXY here.
+ */
+ fprintf(stderr, "set: cannot configure a new entry\n");
+ return 1;
+ }
+
+overwrite:
+ if (sdl->sdl_family != AF_LINK) {
+ printf("cannot intuit interface index and type for %s\n", host);
+ return (1);
+ }
+ sdl_m.sdl_type = sdl->sdl_type;
+ sdl_m.sdl_index = sdl->sdl_index;
+ return (rtmsg(RTM_ADD));
+}
+
+/*
+ * Display an individual neighbor cache entry
+ */
+void
+get(host)
+ char *host;
+{
+ struct sockaddr_in6 *sin = &sin_m;
+ struct addrinfo hints, *res;
+ int gai_error;
+
+ sin_m = blank_sin;
+ bzero(&hints, sizeof(hints));
+ hints.ai_family = AF_INET6;
+ gai_error = getaddrinfo(host, NULL, &hints, &res);
+ if (gai_error) {
+ fprintf(stderr, "ndp: %s: %s\n", host,
+ gai_strerror(gai_error));
+ return;
+ }
+ sin->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
+#ifdef __KAME__
+ if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr)) {
+ *(u_int16_t *)&sin->sin6_addr.s6_addr[2] =
+ htons(((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id);
+ }
+#endif
+ dump(&sin->sin6_addr);
+ if (found_entry == 0) {
+ getnameinfo((struct sockaddr *)sin, sin->sin6_len, host_buf,
+ sizeof(host_buf), NULL ,0,
+ NI_WITHSCOPEID | (nflag ? NI_NUMERICHOST : 0));
+ printf("%s (%s) -- no entry\n", host, host_buf);
+ exit(1);
+ }
+}
+
+/*
+ * Delete a neighbor cache entry
+ */
+int
+delete(host)
+ char *host;
+{
+ struct sockaddr_in6 *sin = &sin_m;
+ register struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
+ struct sockaddr_dl *sdl;
+ struct addrinfo hints, *res;
+ int gai_error;
+
+ getsocket();
+ sin_m = blank_sin;
+
+ bzero(&hints, sizeof(hints));
+ hints.ai_family = AF_INET6;
+ gai_error = getaddrinfo(host, NULL, &hints, &res);
+ if (gai_error) {
+ fprintf(stderr, "ndp: %s: %s\n", host,
+ gai_strerror(gai_error));
+ return 1;
+ }
+ sin->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
+#ifdef __KAME__
+ if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr)) {
+ *(u_int16_t *)&sin->sin6_addr.s6_addr[2] =
+ htons(((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id);
+ }
+#endif
+ if (rtmsg(RTM_GET) < 0) {
+ perror(host);
+ return (1);
+ }
+ sin = (struct sockaddr_in6 *)(rtm + 1);
+ sdl = (struct sockaddr_dl *)(ROUNDUP(sin->sin6_len) + (char *)sin);
+ if (IN6_ARE_ADDR_EQUAL(&sin->sin6_addr, &sin_m.sin6_addr)) {
+ if (sdl->sdl_family == AF_LINK &&
+ (rtm->rtm_flags & RTF_LLINFO) &&
+ !(rtm->rtm_flags & RTF_GATEWAY)) {
+ goto delete;
+ }
+ /*
+ * IPv4 arp command retries with sin_other = SIN_PROXY here.
+ */
+ fprintf(stderr, "delete: cannot delete non-NDP entry\n");
+ return 1;
+ }
+
+delete:
+ if (sdl->sdl_family != AF_LINK) {
+ printf("cannot locate %s\n", host);
+ return (1);
+ }
+ if (rtmsg(RTM_DELETE) == 0) {
+ struct sockaddr_in6 s6 = *sin; /* XXX: for safety */
+
+#ifdef __KAME__
+ if (IN6_IS_ADDR_LINKLOCAL(&s6.sin6_addr)) {
+ s6.sin6_scope_id = ntohs(*(u_int16_t *)&s6.sin6_addr.s6_addr[2]);
+ *(u_int16_t *)&s6.sin6_addr.s6_addr[2] = 0;
+ }
+#endif
+ getnameinfo((struct sockaddr *)&s6,
+ s6.sin6_len, host_buf,
+ sizeof(host_buf), NULL, 0,
+ NI_WITHSCOPEID | (nflag ? NI_NUMERICHOST : 0));
+ printf("%s (%s) deleted\n", host, host_buf);
+ }
+
+ return 0;
+}
+
+#define W_ADDR 31
+#define W_LL 17
+#define W_IF 6
+
+/*
+ * Dump the entire neighbor cache
+ */
+void
+dump(addr)
+ struct in6_addr *addr;
+{
+ int mib[6];
+ size_t needed;
+ char *lim, *buf, *next;
+ struct rt_msghdr *rtm;
+ struct sockaddr_in6 *sin;
+ struct sockaddr_dl *sdl;
+ extern int h_errno;
+ struct in6_nbrinfo *nbi;
+ struct timeval time;
+ int addrwidth;
+ int llwidth;
+ int ifwidth;
+ char flgbuf[8];
+ char *ifname;
+
+ /* Print header */
+ if (!tflag && !cflag)
+ printf("%-*.*s %-*.*s %*.*s %-9.9s %2s %4s %4s\n",
+ W_ADDR, W_ADDR, "Neighbor", W_LL, W_LL, "Linklayer Address",
+ W_IF, W_IF, "Netif", "Expire", "St", "Flgs", "Prbs");
+
+again:;
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = AF_INET6;
+ mib[4] = NET_RT_FLAGS;
+ mib[5] = RTF_LLINFO;
+ if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
+ err(1, "sysctl(PF_ROUTE estimate)");
+ if (needed > 0) {
+ if ((buf = malloc(needed)) == NULL)
+ errx(1, "malloc");
+ if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
+ err(1, "sysctl(PF_ROUTE, NET_RT_FLAGS)");
+ lim = buf + needed;
+ } else
+ buf = lim = NULL;
+
+ for (next = buf; next && next < lim; next += rtm->rtm_msglen) {
+ int isrouter = 0, prbs = 0;
+
+ rtm = (struct rt_msghdr *)next;
+ sin = (struct sockaddr_in6 *)(rtm + 1);
+ sdl = (struct sockaddr_dl *)((char *)sin + ROUNDUP(sin->sin6_len));
+
+ /*
+ * Some OSes can produce a route that has the LINK flag but
+ * has a non-AF_LINK gateway (e.g. fe80::xx%lo0 on FreeBSD
+ * and BSD/OS, where xx is not the interface identifier on
+ * lo0). Such routes entry would annoy getnbrinfo() below,
+ * so we skip them.
+ * XXX: such routes should have the GATEWAY flag, not the
+ * LINK flag. However, there are rotten routing software
+ * that advertises all routes that have the GATEWAY flag.
+ * Thus, KAME kernel intentionally does not set the LINK flag.
+ * What is to be fixed is not ndp, but such routing software
+ * (and the kernel workaround)...
+ */
+ if (sdl->sdl_family != AF_LINK)
+ continue;
+
+ if (addr) {
+ if (!IN6_ARE_ADDR_EQUAL(addr, &sin->sin6_addr))
+ continue;
+ found_entry = 1;
+ } else if (IN6_IS_ADDR_MULTICAST(&sin->sin6_addr))
+ continue;
+ if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr) ||
+ IN6_IS_ADDR_MC_LINKLOCAL(&sin->sin6_addr)) {
+ /* XXX: should scope id be filled in the kernel? */
+ if (sin->sin6_scope_id == 0)
+ sin->sin6_scope_id = sdl->sdl_index;
+#ifdef __KAME__
+ /* KAME specific hack; removed the embedded id */
+ *(u_int16_t *)&sin->sin6_addr.s6_addr[2] = 0;
+#endif
+ }
+ getnameinfo((struct sockaddr *)sin, sin->sin6_len, host_buf,
+ sizeof(host_buf), NULL, 0,
+ NI_WITHSCOPEID | (nflag ? NI_NUMERICHOST : 0));
+ if (cflag == 1) {
+#ifdef RTF_WASCLONED
+ if (rtm->rtm_flags & RTF_WASCLONED)
+ delete(host_buf);
+#else
+ delete(host_buf);
+#endif
+ continue;
+ }
+ gettimeofday(&time, 0);
+ if (tflag)
+ ts_print(&time);
+
+ addrwidth = strlen(host_buf);
+ if (addrwidth < W_ADDR)
+ addrwidth = W_ADDR;
+ llwidth = strlen(ether_str(sdl));
+ if (W_ADDR + W_LL - addrwidth > llwidth)
+ llwidth = W_ADDR + W_LL - addrwidth;
+ ifname = if_indextoname(sdl->sdl_index, ifix_buf);
+ if (!ifname)
+ ifname = "?";
+ ifwidth = strlen(ifname);
+ if (W_ADDR + W_LL + W_IF - addrwidth - llwidth > ifwidth)
+ ifwidth = W_ADDR + W_LL + W_IF - addrwidth - llwidth;
+
+ printf("%-*.*s %-*.*s %*.*s", addrwidth, addrwidth, host_buf,
+ llwidth, llwidth, ether_str(sdl), ifwidth, ifwidth, ifname);
+
+ /* Print neighbor discovery specific informations */
+ nbi = getnbrinfo(&sin->sin6_addr, sdl->sdl_index, 1);
+ if (nbi) {
+ if (nbi->expire > time.tv_sec) {
+ printf(" %-9.9s",
+ sec2str(nbi->expire - time.tv_sec));
+ } else if (nbi->expire == 0)
+ printf(" %-9.9s", "permanent");
+ else
+ printf(" %-9.9s", "expired");
+
+ switch(nbi->state) {
+ case ND6_LLINFO_NOSTATE:
+ printf(" N");
+ break;
+#ifdef ND6_LLINFO_WAITDELETE
+ case ND6_LLINFO_WAITDELETE:
+ printf(" W");
+ break;
+#endif
+ case ND6_LLINFO_INCOMPLETE:
+ printf(" I");
+ break;
+ case ND6_LLINFO_REACHABLE:
+ printf(" R");
+ break;
+ case ND6_LLINFO_STALE:
+ printf(" S");
+ break;
+ case ND6_LLINFO_DELAY:
+ printf(" D");
+ break;
+ case ND6_LLINFO_PROBE:
+ printf(" P");
+ break;
+ default:
+ printf(" ?");
+ break;
+ }
+
+ isrouter = nbi->isrouter;
+ prbs = nbi->asked;
+ } else {
+ warnx("failed to get neighbor information");
+ printf(" ");
+ }
+ putchar(' ');
+
+ /*
+ * other flags. R: router, P: proxy, W: ??
+ */
+ if ((rtm->rtm_addrs & RTA_NETMASK) == 0) {
+ snprintf(flgbuf, sizeof(flgbuf), "%s%s",
+ isrouter ? "R" : "",
+ (rtm->rtm_flags & RTF_ANNOUNCE) ? "p" : "");
+ } else {
+ sin = (struct sockaddr_in6 *)
+ (sdl->sdl_len + (char *)sdl);
+ snprintf(flgbuf, sizeof(flgbuf), "%s%s%s%s",
+ isrouter ? "R" : "",
+ !IN6_IS_ADDR_UNSPECIFIED(&sin->sin6_addr)
+ ? "P" : "",
+ (sin->sin6_len != sizeof(struct sockaddr_in6))
+ ? "W" : "",
+ (rtm->rtm_flags & RTF_ANNOUNCE) ? "p" : "");
+ }
+ printf(" %-4.4s", flgbuf);
+
+ if (prbs)
+ printf(" %4d", prbs);
+
+ printf("\n");
+ }
+ if (buf != NULL)
+ free(buf);
+
+ if (repeat) {
+ printf("\n");
+ sleep(repeat);
+ goto again;
+ }
+}
+
+static struct in6_nbrinfo *
+getnbrinfo(addr, ifindex, warning)
+ struct in6_addr *addr;
+ int ifindex;
+ int warning;
+{
+ static struct in6_nbrinfo nbi;
+ int s;
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
+ err(1, "socket");
+
+ bzero(&nbi, sizeof(nbi));
+ if_indextoname(ifindex, nbi.ifname);
+ nbi.addr = *addr;
+ if (ioctl(s, SIOCGNBRINFO_IN6, (caddr_t)&nbi) < 0) {
+ if (warning)
+ warn("ioctl(SIOCGNBRINFO_IN6)");
+ close(s);
+ return(NULL);
+ }
+
+ close(s);
+ return(&nbi);
+}
+
+static char *
+ether_str(sdl)
+ struct sockaddr_dl *sdl;
+{
+ static char ebuf[32];
+ u_char *cp;
+
+ if (sdl->sdl_alen) {
+ cp = (u_char *)LLADDR(sdl);
+ sprintf(ebuf, "%x:%x:%x:%x:%x:%x",
+ cp[0], cp[1], cp[2], cp[3], cp[4], cp[5]);
+ } else {
+ sprintf(ebuf, "(incomplete)");
+ }
+
+ return(ebuf);
+}
+
+int
+ndp_ether_aton(a, n)
+ char *a;
+ u_char *n;
+{
+ int i, o[6];
+
+ i = sscanf(a, "%x:%x:%x:%x:%x:%x", &o[0], &o[1], &o[2],
+ &o[3], &o[4], &o[5]);
+ if (i != 6) {
+ fprintf(stderr, "ndp: invalid Ethernet address '%s'\n", a);
+ return (1);
+ }
+ for (i=0; i<6; i++)
+ n[i] = o[i];
+ return (0);
+}
+
+void
+usage()
+{
+ printf("usage: ndp hostname\n");
+ printf(" ndp -a[nt]\n");
+ printf(" ndp [-nt] -A wait\n");
+ printf(" ndp -c[nt]\n");
+ printf(" ndp -d[nt] hostname\n");
+ printf(" ndp -f[nt] filename\n");
+ printf(" ndp -i interface [flags...]\n");
+#ifdef SIOCSDEFIFACE_IN6
+ printf(" ndp -I [interface|delete]\n");
+#endif
+ printf(" ndp -p\n");
+ printf(" ndp -r\n");
+ printf(" ndp -s hostname ether_addr [temp] [proxy]\n");
+ printf(" ndp -H\n");
+ printf(" ndp -P\n");
+ printf(" ndp -R\n");
+ exit(1);
+}
+
+int
+rtmsg(cmd)
+ int cmd;
+{
+ static int seq;
+ int rlen;
+ register struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
+ register char *cp = m_rtmsg.m_space;
+ register int l;
+
+ errno = 0;
+ if (cmd == RTM_DELETE)
+ goto doit;
+ bzero((char *)&m_rtmsg, sizeof(m_rtmsg));
+ rtm->rtm_flags = flags;
+ rtm->rtm_version = RTM_VERSION;
+
+ switch (cmd) {
+ default:
+ fprintf(stderr, "ndp: internal wrong cmd\n");
+ exit(1);
+ case RTM_ADD:
+ rtm->rtm_addrs |= RTA_GATEWAY;
+ rtm->rtm_rmx.rmx_expire = expire_time;
+ rtm->rtm_inits = RTV_EXPIRE;
+ rtm->rtm_flags |= (RTF_HOST | RTF_STATIC);
+ if (rtm->rtm_flags & RTF_ANNOUNCE) {
+ rtm->rtm_flags &= ~RTF_HOST;
+ rtm->rtm_flags |= RTA_NETMASK;
+ }
+ /* FALLTHROUGH */
+ case RTM_GET:
+ rtm->rtm_addrs |= RTA_DST;
+ }
+#define NEXTADDR(w, s) \
+ if (rtm->rtm_addrs & (w)) { \
+ bcopy((char *)&s, cp, sizeof(s)); cp += sizeof(s);}
+
+ NEXTADDR(RTA_DST, sin_m);
+ NEXTADDR(RTA_GATEWAY, sdl_m);
+ memset(&so_mask.sin6_addr, 0xff, sizeof(so_mask.sin6_addr));
+ NEXTADDR(RTA_NETMASK, so_mask);
+
+ rtm->rtm_msglen = cp - (char *)&m_rtmsg;
+doit:
+ l = rtm->rtm_msglen;
+ rtm->rtm_seq = ++seq;
+ rtm->rtm_type = cmd;
+ if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) {
+ if (errno != ESRCH || cmd != RTM_DELETE) {
+ perror("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)
+ (void) fprintf(stderr, "ndp: read from routing socket: %s\n",
+ strerror(errno));
+ return (0);
+}
+
+void
+ifinfo(argc, argv)
+ int argc;
+ char **argv;
+{
+ struct in6_ndireq nd;
+ int i, s;
+ char *ifname = argv[0];
+ u_int32_t newflags;
+#ifdef IPV6CTL_USETEMPADDR
+ u_int8_t nullbuf[8];
+#endif
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ perror("ndp: socket");
+ exit(1);
+ }
+ bzero(&nd, sizeof(nd));
+ strcpy(nd.ifname, ifname);
+ if (ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&nd) < 0) {
+ perror("ioctl (SIOCGIFINFO_IN6)");
+ exit(1);
+ }
+#define ND nd.ndi
+ newflags = ND.flags;
+ for (i = 1; i < argc; i++) {
+ int clear = 0;
+ char *cp = argv[i];
+
+ if (*cp == '-') {
+ clear = 1;
+ cp++;
+ }
+
+#define SETFLAG(s, f) \
+ do {\
+ if (strcmp(cp, (s)) == 0) {\
+ if (clear)\
+ newflags &= ~(f);\
+ else\
+ newflags |= (f);\
+ }\
+ } while (0)
+ SETFLAG("nud", ND6_IFF_PERFORMNUD);
+
+ ND.flags = newflags;
+ if (ioctl(s, SIOCSIFINFO_FLAGS, (caddr_t)&nd) < 0) {
+ perror("ioctl(SIOCSIFINFO_FLAGS)");
+ exit(1);
+ }
+#undef SETFLAG
+ }
+
+ printf("linkmtu=%d", ND.linkmtu);
+ printf(", curhlim=%d", ND.chlim);
+ printf(", basereachable=%ds%dms",
+ ND.basereachable / 1000, ND.basereachable % 1000);
+ printf(", reachable=%ds", ND.reachable);
+ printf(", retrans=%ds%dms", ND.retrans / 1000, ND.retrans % 1000);
+#ifdef IPV6CTL_USETEMPADDR
+ memset(nullbuf, 0, sizeof(nullbuf));
+ if (memcmp(nullbuf, ND.randomid, sizeof(nullbuf)) != 0) {
+ int j;
+ u_int8_t *rbuf;
+
+ for (i = 0; i < 3; i++) {
+ switch(i) {
+ case 0:
+ printf("\nRandom seed(0): ");
+ rbuf = ND.randomseed0;
+ break;
+ case 1:
+ printf("\nRandom seed(1): ");
+ rbuf = ND.randomseed1;
+ break;
+ case 2:
+ printf("\nRandom ID: ");
+ rbuf = ND.randomid;
+ break;
+ }
+ for (j = 0; j < 8; j++)
+ printf("%02x", rbuf[j]);
+ }
+ }
+#endif
+ if (ND.flags) {
+ printf("\nFlags: ");
+ if ((ND.flags & ND6_IFF_PERFORMNUD) != 0)
+ printf("PERFORMNUD ");
+ }
+ putc('\n', stdout);
+#undef ND
+
+ close(s);
+}
+
+#ifndef ND_RA_FLAG_RTPREF_MASK /* XXX: just for compilation on *BSD release */
+#define ND_RA_FLAG_RTPREF_MASK 0x18 /* 00011000 */
+#endif
+
+void
+rtrlist()
+{
+#ifdef ICMPV6CTL_ND6_DRLIST
+ int mib[] = { CTL_NET, PF_INET6, IPPROTO_ICMPV6, ICMPV6CTL_ND6_DRLIST };
+ char *buf;
+ struct in6_defrouter *p, *ep;
+ size_t l;
+ struct timeval time;
+
+ if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &l, NULL, 0) < 0) {
+ err(1, "sysctl(ICMPV6CTL_ND6_DRLIST)");
+ /*NOTREACHED*/
+ }
+ buf = malloc(l);
+ if (!buf) {
+ errx(1, "not enough core");
+ /*NOTREACHED*/
+ }
+ if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), buf, &l, NULL, 0) < 0) {
+ err(1, "sysctl(ICMPV6CTL_ND6_DRLIST)");
+ /*NOTREACHED*/
+ }
+
+ ep = (struct in6_defrouter *)(buf + l);
+ for (p = (struct in6_defrouter *)buf; p < ep; p++) {
+ int rtpref;
+
+ if (getnameinfo((struct sockaddr *)&p->rtaddr,
+ p->rtaddr.sin6_len, host_buf, sizeof(host_buf), NULL, 0,
+ NI_WITHSCOPEID | (nflag ? NI_NUMERICHOST : 0)) != 0)
+ strlcpy(host_buf, "?", sizeof(host_buf));
+
+ printf("%s if=%s", host_buf,
+ if_indextoname(p->if_index, ifix_buf));
+ printf(", flags=%s%s",
+ p->flags & ND_RA_FLAG_MANAGED ? "M" : "",
+ p->flags & ND_RA_FLAG_OTHER ? "O" : "");
+ rtpref = ((p->flags & ND_RA_FLAG_RTPREF_MASK) >> 3) & 0xff;
+ printf(", pref=%s", rtpref_str[rtpref]);
+
+ gettimeofday(&time, 0);
+ if (p->expire == 0)
+ printf(", expire=Never\n");
+ else
+ printf(", expire=%s\n",
+ sec2str(p->expire - time.tv_sec));
+ }
+ free(buf);
+#else
+ struct in6_drlist dr;
+ int s, i;
+ struct timeval time;
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ perror("ndp: socket");
+ exit(1);
+ }
+ bzero(&dr, sizeof(dr));
+ strcpy(dr.ifname, "lo0"); /* dummy */
+ if (ioctl(s, SIOCGDRLST_IN6, (caddr_t)&dr) < 0) {
+ perror("ioctl (SIOCGDRLST_IN6)");
+ exit(1);
+ }
+#define DR dr.defrouter[i]
+ for (i = 0 ; DR.if_index && i < DRLSTSIZ ; i++) {
+ struct sockaddr_in6 sin6;
+
+ bzero(&sin6, sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_len = sizeof(sin6);
+ sin6.sin6_addr = DR.rtaddr;
+ getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len, host_buf,
+ sizeof(host_buf), NULL, 0,
+ NI_WITHSCOPEID | (nflag ? NI_NUMERICHOST : 0));
+
+ printf("%s if=%s", host_buf,
+ if_indextoname(DR.if_index, ifix_buf));
+ printf(", flags=%s%s",
+ DR.flags & ND_RA_FLAG_MANAGED ? "M" : "",
+ DR.flags & ND_RA_FLAG_OTHER ? "O" : "");
+ gettimeofday(&time, 0);
+ if (DR.expire == 0)
+ printf(", expire=Never\n");
+ else
+ printf(", expire=%s\n",
+ sec2str(DR.expire - time.tv_sec));
+ }
+#undef DR
+ close(s);
+#endif
+}
+
+void
+plist()
+{
+#ifdef ICMPV6CTL_ND6_PRLIST
+ int mib[] = { CTL_NET, PF_INET6, IPPROTO_ICMPV6, ICMPV6CTL_ND6_PRLIST };
+ char *buf;
+ struct in6_prefix *p, *ep, *n;
+ struct sockaddr_in6 *advrtr;
+ size_t l;
+ struct timeval time;
+#ifdef NI_WITHSCOPEID
+ const int niflags = NI_NUMERICHOST | NI_WITHSCOPEID;
+ int ninflags = (nflag ? NI_NUMERICHOST : 0) | NI_WITHSCOPEID;
+#else
+ const int niflags = NI_NUMERICHOST;
+ int ninflags = nflag ? NI_NUMERICHOST : 0;
+#endif
+ char namebuf[NI_MAXHOST];
+
+ if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &l, NULL, 0) < 0) {
+ err(1, "sysctl(ICMPV6CTL_ND6_PRLIST)");
+ /*NOTREACHED*/
+ }
+ buf = malloc(l);
+ if (!buf) {
+ errx(1, "not enough core");
+ /*NOTREACHED*/
+ }
+ if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), buf, &l, NULL, 0) < 0) {
+ err(1, "sysctl(ICMPV6CTL_ND6_PRLIST)");
+ /*NOTREACHED*/
+ }
+
+ ep = (struct in6_prefix *)(buf + l);
+ for (p = (struct in6_prefix *)buf; p < ep; p = n) {
+ advrtr = (struct sockaddr_in6 *)(p + 1);
+ n = (struct in6_prefix *)&advrtr[p->advrtrs];
+
+ if (getnameinfo((struct sockaddr *)&p->prefix,
+ p->prefix.sin6_len, namebuf, sizeof(namebuf),
+ NULL, 0, niflags) != 0)
+ strlcpy(namebuf, "?", sizeof(namebuf));
+ printf("%s/%d if=%s\n", namebuf, p->prefixlen,
+ if_indextoname(p->if_index, ifix_buf));
+
+ gettimeofday(&time, 0);
+ /*
+ * meaning of fields, especially flags, is very different
+ * by origin. notify the difference to the users.
+ */
+ printf("flags=%s%s%s%s%s",
+ p->raflags.onlink ? "L" : "",
+ p->raflags.autonomous ? "A" : "",
+ (p->flags & NDPRF_ONLINK) != 0 ? "O" : "",
+ (p->flags & NDPRF_DETACHED) != 0 ? "D" : "",
+#ifdef NDPRF_HOME
+ (p->flags & NDPRF_HOME) != 0 ? "H" : ""
+#else
+ ""
+#endif
+ );
+ if (p->vltime == ND6_INFINITE_LIFETIME)
+ printf(" vltime=infinity");
+ else
+ printf(" vltime=%ld", (long)p->vltime);
+ if (p->pltime == ND6_INFINITE_LIFETIME)
+ printf(", pltime=infinity");
+ else
+ printf(", pltime=%ld", (long)p->pltime);
+ if (p->expire == 0)
+ printf(", expire=Never");
+ else if (p->expire >= time.tv_sec)
+ printf(", expire=%s",
+ sec2str(p->expire - time.tv_sec));
+ else
+ printf(", expired");
+ printf(", ref=%d", p->refcnt);
+ printf("\n");
+ /*
+ * "advertising router" list is meaningful only if the prefix
+ * information is from RA.
+ */
+ if (p->advrtrs) {
+ int j;
+ struct sockaddr_in6 *sin6;
+
+ sin6 = (struct sockaddr_in6 *)(p + 1);
+ printf(" advertised by\n");
+ for (j = 0; j < p->advrtrs; j++) {
+ struct in6_nbrinfo *nbi;
+
+ if (getnameinfo((struct sockaddr *)sin6,
+ sin6->sin6_len, namebuf, sizeof(namebuf),
+ NULL, 0, ninflags) != 0)
+ strlcpy(namebuf, "?", sizeof(namebuf));
+ printf(" %s", namebuf);
+
+ nbi = getnbrinfo(&sin6->sin6_addr, p->if_index,
+ 0);
+ if (nbi) {
+ switch(nbi->state) {
+ case ND6_LLINFO_REACHABLE:
+ case ND6_LLINFO_STALE:
+ case ND6_LLINFO_DELAY:
+ case ND6_LLINFO_PROBE:
+ printf(" (reachable)\n");
+ break;
+ default:
+ printf(" (unreachable)\n");
+ }
+ } else
+ printf(" (no neighbor state)\n");
+ sin6++;
+ }
+ } else
+ printf(" No advertising router\n");
+ }
+ free(buf);
+#else
+ struct in6_prlist pr;
+ int s, i;
+ struct timeval time;
+
+ gettimeofday(&time, 0);
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ perror("ndp: socket");
+ exit(1);
+ }
+ bzero(&pr, sizeof(pr));
+ strcpy(pr.ifname, "lo0"); /* dummy */
+ if (ioctl(s, SIOCGPRLST_IN6, (caddr_t)&pr) < 0) {
+ perror("ioctl (SIOCGPRLST_IN6)");
+ exit(1);
+ }
+#define PR pr.prefix[i]
+ for (i = 0; PR.if_index && i < PRLSTSIZ ; i++) {
+ struct sockaddr_in6 p6;
+ char namebuf[NI_MAXHOST];
+ int niflags;
+
+#ifdef NDPRF_ONLINK
+ p6 = PR.prefix;
+#else
+ memset(&p6, 0, sizeof(p6));
+ p6.sin6_family = AF_INET6;
+ p6.sin6_len = sizeof(p6);
+ p6.sin6_addr = PR.prefix;
+#endif
+
+ /*
+ * copy link index to sin6_scope_id field.
+ * XXX: KAME specific.
+ */
+ if (IN6_IS_ADDR_LINKLOCAL(&p6.sin6_addr)) {
+ u_int16_t linkid;
+
+ memcpy(&linkid, &p6.sin6_addr.s6_addr[2],
+ sizeof(linkid));
+ linkid = ntohs(linkid);
+ p6.sin6_scope_id = linkid;
+ p6.sin6_addr.s6_addr[2] = 0;
+ p6.sin6_addr.s6_addr[3] = 0;
+ }
+
+ niflags = NI_NUMERICHOST;
+#ifdef __KAME__
+ niflags |= NI_WITHSCOPEID;
+#endif
+ if (getnameinfo((struct sockaddr *)&p6,
+ sizeof(p6), namebuf, sizeof(namebuf),
+ NULL, 0, niflags)) {
+ warnx("getnameinfo failed");
+ continue;
+ }
+ printf("%s/%d if=%s\n", namebuf, PR.prefixlen,
+ if_indextoname(PR.if_index, ifix_buf));
+
+ gettimeofday(&time, 0);
+ /*
+ * meaning of fields, especially flags, is very different
+ * by origin. notify the difference to the users.
+ */
+#if 0
+ printf(" %s",
+ PR.origin == PR_ORIG_RA ? "" : "advertise: ");
+#endif
+#ifdef NDPRF_ONLINK
+ printf("flags=%s%s%s%s%s",
+ PR.raflags.onlink ? "L" : "",
+ PR.raflags.autonomous ? "A" : "",
+ (PR.flags & NDPRF_ONLINK) != 0 ? "O" : "",
+ (PR.flags & NDPRF_DETACHED) != 0 ? "D" : "",
+#ifdef NDPRF_HOME
+ (PR.flags & NDPRF_HOME) != 0 ? "H" : ""
+#else
+ ""
+#endif
+ );
+#else
+ printf("flags=%s%s",
+ PR.raflags.onlink ? "L" : "",
+ PR.raflags.autonomous ? "A" : "");
+#endif
+ if (PR.vltime == ND6_INFINITE_LIFETIME)
+ printf(" vltime=infinity");
+ else
+ printf(" vltime=%ld", (long)PR.vltime);
+ if (PR.pltime == ND6_INFINITE_LIFETIME)
+ printf(", pltime=infinity");
+ else
+ printf(", pltime=%ld", (long)PR.pltime);
+ if (PR.expire == 0)
+ printf(", expire=Never");
+ else if (PR.expire >= time.tv_sec)
+ printf(", expire=%s",
+ sec2str(PR.expire - time.tv_sec));
+ else
+ printf(", expired");
+#ifdef NDPRF_ONLINK
+ printf(", ref=%d", PR.refcnt);
+#endif
+#if 0
+ switch (PR.origin) {
+ case PR_ORIG_RA:
+ printf(", origin=RA");
+ break;
+ case PR_ORIG_RR:
+ printf(", origin=RR");
+ break;
+ case PR_ORIG_STATIC:
+ printf(", origin=static");
+ break;
+ case PR_ORIG_KERNEL:
+ printf(", origin=kernel");
+ break;
+ default:
+ printf(", origin=?");
+ break;
+ }
+#endif
+ printf("\n");
+ /*
+ * "advertising router" list is meaningful only if the prefix
+ * information is from RA.
+ */
+ if (0 && /* prefix origin is almost obsolted */
+ PR.origin != PR_ORIG_RA)
+ ;
+ else if (PR.advrtrs) {
+ int j;
+ printf(" advertised by\n");
+ for (j = 0; j < PR.advrtrs; j++) {
+ struct sockaddr_in6 sin6;
+ struct in6_nbrinfo *nbi;
+
+ bzero(&sin6, sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_len = sizeof(sin6);
+ sin6.sin6_addr = PR.advrtr[j];
+ sin6.sin6_scope_id = PR.if_index; /* XXX */
+ getnameinfo((struct sockaddr *)&sin6,
+ sin6.sin6_len, host_buf,
+ sizeof(host_buf), NULL, 0,
+ NI_WITHSCOPEID | (nflag ? NI_NUMERICHOST : 0));
+ printf(" %s", host_buf);
+
+ nbi = getnbrinfo(&sin6.sin6_addr, PR.if_index,
+ 0);
+ if (nbi) {
+ switch(nbi->state) {
+ case ND6_LLINFO_REACHABLE:
+ case ND6_LLINFO_STALE:
+ case ND6_LLINFO_DELAY:
+ case ND6_LLINFO_PROBE:
+ printf(" (reachable)\n");
+ break;
+ default:
+ printf(" (unreachable)\n");
+ }
+ } else
+ printf(" (no neighbor state)\n");
+ }
+ if (PR.advrtrs > DRLSTSIZ)
+ printf(" and %d routers\n",
+ PR.advrtrs - DRLSTSIZ);
+ } else
+ printf(" No advertising router\n");
+ }
+#undef PR
+ close(s);
+#endif
+}
+
+void
+pfx_flush()
+{
+ char dummyif[IFNAMSIZ+8];
+ int s;
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
+ err(1, "socket");
+ strcpy(dummyif, "lo0"); /* dummy */
+ if (ioctl(s, SIOCSPFXFLUSH_IN6, (caddr_t)&dummyif) < 0)
+ err(1, "ioctl(SIOCSPFXFLUSH_IN6)");
+}
+
+void
+rtr_flush()
+{
+ char dummyif[IFNAMSIZ+8];
+ int s;
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
+ err(1, "socket");
+ strcpy(dummyif, "lo0"); /* dummy */
+ if (ioctl(s, SIOCSRTRFLUSH_IN6, (caddr_t)&dummyif) < 0)
+ err(1, "ioctl(SIOCSRTRFLUSH_IN6)");
+
+ close(s);
+}
+
+void
+harmonize_rtr()
+{
+ char dummyif[IFNAMSIZ+8];
+ int s;
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
+ err(1, "socket");
+ strcpy(dummyif, "lo0"); /* dummy */
+ if (ioctl(s, SIOCSNDFLUSH_IN6, (caddr_t)&dummyif) < 0)
+ err(1, "ioctl (SIOCSNDFLUSH_IN6)");
+
+ close(s);
+}
+
+#ifdef SIOCSDEFIFACE_IN6 /* XXX: check SIOCGDEFIFACE_IN6 as well? */
+static void
+setdefif(ifname)
+ char *ifname;
+{
+ struct in6_ndifreq ndifreq;
+ unsigned int ifindex;
+
+ if (strcasecmp(ifname, "delete") == 0)
+ ifindex = 0;
+ else {
+ if ((ifindex = if_nametoindex(ifname)) == 0)
+ err(1, "failed to resolve i/f index for %s", ifname);
+ }
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
+ err(1, "socket");
+
+ strcpy(ndifreq.ifname, "lo0"); /* dummy */
+ ndifreq.ifindex = ifindex;
+
+ if (ioctl(s, SIOCSDEFIFACE_IN6, (caddr_t)&ndifreq) < 0)
+ err(1, "ioctl (SIOCSDEFIFACE_IN6)");
+
+ close(s);
+}
+
+static void
+getdefif()
+{
+ struct in6_ndifreq ndifreq;
+ char ifname[IFNAMSIZ+8];
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
+ err(1, "socket");
+
+ memset(&ndifreq, 0, sizeof(ndifreq));
+ strcpy(ndifreq.ifname, "lo0"); /* dummy */
+
+ if (ioctl(s, SIOCGDEFIFACE_IN6, (caddr_t)&ndifreq) < 0)
+ err(1, "ioctl (SIOCGDEFIFACE_IN6)");
+
+ if (ndifreq.ifindex == 0)
+ printf("No default interface.\n");
+ else {
+ if ((if_indextoname(ndifreq.ifindex, ifname)) == NULL)
+ err(1, "failed to resolve ifname for index %lu",
+ ndifreq.ifindex);
+ printf("ND default interface = %s\n", ifname);
+ }
+
+ close(s);
+}
+#endif
+
+static char *
+sec2str(total)
+ time_t total;
+{
+ static char result[256];
+ int days, hours, mins, secs;
+ int first = 1;
+ char *p = result;
+
+ days = total / 3600 / 24;
+ hours = (total / 3600) % 24;
+ mins = (total / 60) % 60;
+ secs = total % 60;
+
+ if (days) {
+ first = 0;
+ p += sprintf(p, "%dd", days);
+ }
+ if (!first || hours) {
+ first = 0;
+ p += sprintf(p, "%dh", hours);
+ }
+ if (!first || mins) {
+ first = 0;
+ p += sprintf(p, "%dm", mins);
+ }
+ sprintf(p, "%ds", secs);
+
+ return(result);
+}
+
+/*
+ * Print the timestamp
+ * from tcpdump/util.c
+ */
+static void
+ts_print(tvp)
+ const struct timeval *tvp;
+{
+ int s;
+
+ /* Default */
+ s = (tvp->tv_sec + thiszone) % 86400;
+ (void)printf("%02d:%02d:%02d.%06u ",
+ s / 3600, (s % 3600) / 60, s % 60, (u_int32_t)tvp->tv_usec);
+}
diff --git a/usr.sbin/newsyslog/Makefile b/usr.sbin/newsyslog/Makefile
new file mode 100644
index 0000000..ab06f3a
--- /dev/null
+++ b/usr.sbin/newsyslog/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= newsyslog
+MAN= newsyslog.8
+
+WARNS?= 2
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/newsyslog/newsyslog.8 b/usr.sbin/newsyslog/newsyslog.8
new file mode 100644
index 0000000..c2b253a
--- /dev/null
+++ b/usr.sbin/newsyslog/newsyslog.8
@@ -0,0 +1,390 @@
+.\" This file contains changes from the Open Software Foundation.
+.\"
+.\" from: @(#)newsyslog.8
+.\" $FreeBSD$
+.\"
+.\" Copyright 1988, 1989 by the Massachusetts Institute of Technology
+.\"
+.\" Permission to use, copy, modify, and distribute this software
+.\" and its documentation for any purpose and without fee is
+.\" hereby granted, provided that the above copyright notice
+.\" appear in all copies and that both that copyright notice and
+.\" this permission notice appear in supporting documentation,
+.\" and that the names of M.I.T. and the M.I.T. S.I.P.B. not be
+.\" used in advertising or publicity pertaining to distribution
+.\" of the software without specific, written prior permission.
+.\" M.I.T. and the M.I.T. S.I.P.B. make no representations about
+.\" the suitability of this software for any purpose. It is
+.\" provided "as is" without express or implied warranty.
+.\"
+.Dd April 4, 2000
+.Dt NEWSYSLOG 8
+.Os
+.Sh NAME
+.Nm newsyslog
+.Nd maintain system log files to manageable sizes
+.Sh SYNOPSIS
+.Nm
+.Op Fl Fnrv
+.Op Fl f Ar config_file
+.Op Fl a Ar directory
+.Op Ar
+.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
+.Pp
+The granularity of
+.Nm
+is dependent on how often it is scheduled to run by
+.Xr cron 8 .
+Since the program is quite fast, it may be scheduled to run every hour
+without any ill effects,
+and mode three (above) assumes that this is so.
+.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 .
+Each line has five mandatory fields and four optional fields, with
+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 : Ns Ar 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
+in kilobytes,
+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 or by an
+.So Li \&$ Sc Ns No -sign
+and a time specification for logfile rotation at a fixed time once
+per day, per week or per month.
+.Pp
+If a time is specified, the log file will only be trimmed if
+.Nm
+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
+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'.
+.Pp
+.Sy ISO 8601 restricted time format
+.Pp
+The lead-in character for a restricted
+.Tn ISO 8601
+time is
+an
+.So Li \&@ Sc Ns No -sign .
+The particular format of the time in restricted
+.Tn ISO 8601
+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
+.Sy Day, week and month time format
+.Pp
+The lead-in character for day, week and month specification is a
+.So Li \&$ Sc Ns No -sign .
+The particular format of day, week and month specification is:
+.Op Va D\&hh ,
+.Op Va W\&w Ns Op Va D\&hh
+and
+.Op Va M\&dd Ns Op Va D\&hh
+respectively.
+Optional time fields default to midnight.
+The ranges for day and hour secifications are:
+.Pp
+.Bl -tag -width Ds -compact -offset indent
+.It Ar hh
+hours, range 0 ... 23
+.It Ar w
+day of week, range 0 ... 6, 0 = Sunday
+.It Ar dd
+day of month, range 1 ... 31, or the letter
+.Em L
+or
+.Em l
+to specify the last day of the month.
+.El
+.Pp
+Some examples:
+.Pp
+.Bl -tag -width Ds -compact -offset indent
+.It Ar $D0
+rotate every night at midnight
+(same as
+.Ar @T00 )
+.It Ar $D23
+rotate every day at 23:00 hr
+(same as
+.Ar @T23 )
+.It Ar $W0D23
+rotate every week on Sunday at 23:00 hr
+.It Ar $W5D16
+rotate every week on Friday at 16:00 hr
+.It Ar $M1D0
+rotate at the first day of every month at midnight
+(i.e., the start of the day; same as
+.Ar @01T00 )
+.It Ar $M5D6
+rotate on every 5th day of month at 6:00 hr
+(same as
+.Ar @05T06 )
+.El
+.Pp
+.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 J
+flag will make the archive files compress to save space by
+using
+.Xr bzip2 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
+.Nm :
+.Bl -tag -width indent
+.It Fl f Ar config_file
+Instruct
+.Nm
+to use
+.Ar config_file
+instead of
+.Pa /etc/newsyslog.conf
+for its configuration file.
+.It Fl a Ar directory
+Specify a
+.Ar directory
+into which archived log files will be written.
+If a relative path is given,
+it is appended to the path of each log file
+and the resulting path is used as the directory
+into which the archived log for that log file will be written.
+If an absolute path is given,
+all archived logs are written into the given
+.Ar directory .
+If any component of the path
+.Ar directory
+does not exist,
+it will be created when
+.Nm
+is run.
+.It Fl v
+Place
+.Nm
+in verbose mode. In this mode it will print out each log and its
+reasons for either trimming that log or skipping it.
+.It Fl n
+Cause
+.Nm
+not to trim the logs, but to print out what it would do if this option
+were not specified.
+.It Fl r
+Remove the restriction that
+.Nm
+must be running as root. Of course,
+.Nm
+will not be able to send a HUP signal to
+.Xr syslogd 8
+so this option should only be used in debugging.
+.It Fl 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
+.Pp
+If additional command line arguments are given,
+.Nm
+will only examine log files that match those arguments; otherwise, it
+will examine all files listed in the configuration file.
+.Sh FILES
+.Bl -tag -width /etc/newsyslog.confxxxx -compact
+.It Pa /etc/newsyslog.conf
+.Nm
+configuration file
+.El
+.Sh BUGS
+Doesn't yet automatically read the logs to find security breaches.
+.Sh AUTHORS
+.An Theodore Ts'o ,
+MIT Project Athena
+.Pp
+Copyright 1987, Massachusetts Institute of Technology
+.Sh COMPATIBILITY
+Previous versions of the
+.Nm
+utility used the dot (``.'') character to
+distinguish the group name.
+Begining with
+.Fx 3.3 ,
+this has been changed to a colon (``:'') character so that user and group
+names may contain the dot character. The dot (``.'') character is still
+accepted for backwards compatibility.
+.Sh "SEE ALSO"
+.Xr gzip 1 ,
+.Xr syslog 3 ,
+.Xr chown 8 ,
+.Xr syslogd 8
diff --git a/usr.sbin/newsyslog/newsyslog.c b/usr.sbin/newsyslog/newsyslog.c
new file mode 100644
index 0000000..5f6b029
--- /dev/null
+++ b/usr.sbin/newsyslog/newsyslog.c
@@ -0,0 +1,1120 @@
+/*
+ * 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[] =
+"$FreeBSD$";
+#endif /* not lint */
+
+#define OSF
+#ifndef COMPRESS_POSTFIX
+#define COMPRESS_POSTFIX ".gz"
+#endif
+#ifndef BZCOMPRESS_POSTFIX
+#define BZCOMPRESS_POSTFIX ".bz2"
+#endif
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.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 "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 */
+#define CE_BZCOMPACT 8 /* Compact the achived log files with bzip2 */
+ /* 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; /* CE_COMPACT, CE_BZCOMPACT, CE_BINARY */
+ int sig; /* Signal to send */
+ struct conf_entry *next;/* Linked list pointer */
+};
+
+int archtodir = 0; /* Archive old logfiles to other directory */
+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 *archdirname; /* Directory path to old logfiles archive */
+const 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]; /* hostname */
+char *daytime; /* timenow in human readable form */
+
+static struct conf_entry *parse_file(char **files);
+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(void);
+static void dotrim(char *log, const 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 void bzcompress_log(char *log);
+static int sizefile(char *file);
+static int age_old_log(char *file);
+static pid_t get_pid(const char *pid_file);
+static time_t parse8601(char *s, char *errline);
+static void movefile(char *from, char *to, int perm, int owner_uid,
+ int group_gid);
+static void createdir(char *dirpart);
+static time_t parseDWM(char *s, char *errline);
+
+int
+main(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(argv + optind);
+
+ while (p) {
+ do_entry(p);
+ p = p->next;
+ free((char *) q);
+ q = p;
+ }
+ while (wait(NULL) > 0 || errno == EINTR)
+ ;
+ return (0);
+}
+
+static void
+do_entry(struct conf_entry * ent)
+{
+ int size, modtime;
+ const char *pid_file;
+
+ if (verbose) {
+ if (ent->flags & CE_COMPACT)
+ printf("%s <%dZ>: ", ent->log, ent->numlogs);
+ else if (ent->flags & CE_BZCOMPACT)
+ printf("%s <%dJ>: ", ent->log, ent->numlogs);
+ else
+ printf("%s <%d>: ", ent->log, ent->numlogs);
+ }
+ 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 && !force) {
+ 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 if (ent->flags & CE_BZCOMPACT)
+ printf("%s <%dJ>: 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(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';
+ }
+ while ((c = getopt(argc, argv, "nrvFf:a:t:")) != -1)
+ switch (c) {
+ case 'n':
+ noaction++;
+ break;
+ case 'a':
+ archtodir++;
+ archdirname = optarg;
+ break;
+ case 'r':
+ needroot = 0;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 'f':
+ conf = optarg;
+ break;
+ case 'F':
+ force++;
+ break;
+ default:
+ usage();
+ }
+}
+
+static void
+usage(void)
+{
+
+ fprintf(stderr,
+ "usage: newsyslog [-Fnrv] [-f config-file] [-a directory]\n");
+ exit(1);
+}
+
+/*
+ * Parse a configuration file and return a linked list of all the logs to
+ * process
+ */
+static struct conf_entry *
+parse_file(char **files)
+{
+ FILE *f;
+ char line[BUFSIZ], *parse, *q;
+ char *errline, *group;
+ char **p;
+ struct conf_entry *first, *working;
+ struct passwd *pass;
+ struct group *grp;
+ int eol;
+
+ first = working = NULL;
+
+ 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);
+
+ q = parse = missing_field(sob(line), errline);
+ parse = son(line);
+ if (!*parse)
+ errx(1, "malformed line (missing fields):\n%s",
+ errline);
+ *parse = '\0';
+
+ if (*files) {
+ for (p = files; *p; ++p)
+ if (strcmp(*p, q) == 0)
+ break;
+ if (!*p)
+ continue;
+ }
+
+ if (!first) {
+ if ((working = malloc(sizeof(struct conf_entry))) ==
+ NULL)
+ err(1, "malloc");
+ first = working;
+ } else {
+ if ((working->next = malloc(sizeof(struct conf_entry)))
+ == NULL)
+ err(1, "malloc");
+ working = working->next;
+ }
+ if ((working->log = strdup(q)) == NULL)
+ err(1, "strdup");
+
+ q = parse = missing_field(sob(++parse), errline);
+ parse = son(parse);
+ if (!*parse)
+ errx(1, "malformed line (missing fields):\n%s",
+ errline);
+ *parse = '\0';
+ if ((group = strchr(q, ':')) != NULL ||
+ (group = strrchr(q, '.')) != NULL) {
+ *group++ = '\0';
+ if (*q) {
+ if (!(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 != '*' &&
+ *ep != '$')
+ errx(1, "malformed interval/at:\n%s", errline);
+ if (*ep == '@') {
+ if ((working->trim_at = parse8601(ep + 1, errline))
+ == (time_t) - 1)
+ errx(1, "malformed at:\n%s", errline);
+ working->flags |= CE_TRIMAT;
+ } else if (*ep == '$') {
+ if ((working->trim_at = parseDWM(ep + 1, errline))
+ == (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 == 'J') || (*q == 'j'))
+ working->flags |= CE_BZCOMPACT;
+ 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(char *p, char *errline)
+{
+
+ if (!p || !*p)
+ errx(1, "missing field in config file:\n%s", errline);
+ return (p);
+}
+
+static void
+dotrim(char *log, const char *pid_file, int numdays, int flags, int perm,
+ int owner_uid, int group_gid, int sig)
+{
+ char dirpart[MAXPATHLEN], namepart[MAXPATHLEN];
+ char file1[MAXPATHLEN], file2[MAXPATHLEN];
+ char zfile1[MAXPATHLEN], zfile2[MAXPATHLEN];
+ char jfile1[MAXPATHLEN];
+ char tfile[MAXPATHLEN];
+ 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
+
+ if (archtodir) {
+ char *p;
+
+ /* build complete name of archive directory into dirpart */
+ if (*archdirname == '/') { /* absolute */
+ strlcpy(dirpart, archdirname, sizeof(dirpart));
+ } else { /* relative */
+ /* get directory part of logfile */
+ strlcpy(dirpart, log, sizeof(dirpart));
+ if ((p = rindex(dirpart, '/')) == NULL)
+ dirpart[0] = '\0';
+ else
+ *(p + 1) = '\0';
+ strlcat(dirpart, archdirname, sizeof(dirpart));
+ }
+
+ /* check if archive directory exists, if not, create it */
+ if (lstat(dirpart, &st))
+ createdir(dirpart);
+
+ /* get filename part of logfile */
+ if ((p = rindex(log, '/')) == NULL)
+ strlcpy(namepart, log, sizeof(namepart));
+ else
+ strlcpy(namepart, p + 1, sizeof(namepart));
+
+ /* name of oldest log */
+ (void) snprintf(file1, sizeof(file1), "%s/%s.%d", dirpart,
+ namepart, numdays);
+ (void) snprintf(zfile1, sizeof(zfile1), "%s%s", file1,
+ COMPRESS_POSTFIX);
+ snprintf(jfile1, sizeof(jfile1), "%s%s", file1,
+ BZCOMPRESS_POSTFIX);
+ } else {
+ /* name of oldest log */
+ (void) snprintf(file1, sizeof(file1), "%s.%d", log, numdays);
+ (void) snprintf(zfile1, sizeof(zfile1), "%s%s", file1,
+ COMPRESS_POSTFIX);
+ snprintf(jfile1, sizeof(jfile1), "%s%s", file1,
+ BZCOMPRESS_POSTFIX);
+ }
+
+ if (noaction) {
+ printf("rm -f %s\n", file1);
+ printf("rm -f %s\n", zfile1);
+ printf("rm -f %s\n", jfile1);
+ } else {
+ (void) unlink(file1);
+ (void) unlink(zfile1);
+ (void) unlink(jfile1);
+ }
+
+ /* Move down log files */
+ _numdays = numdays; /* preserve */
+ while (numdays--) {
+
+ (void) strlcpy(file2, file1, sizeof(file2));
+
+ if (archtodir)
+ (void) snprintf(file1, sizeof(file1), "%s/%s.%d",
+ dirpart, namepart, numdays);
+ else
+ (void) snprintf(file1, sizeof(file1), "%s.%d", log,
+ numdays);
+
+ (void) strlcpy(zfile1, file1, sizeof(zfile1));
+ (void) strlcpy(zfile2, file2, sizeof(zfile2));
+ if (lstat(file1, &st)) {
+ (void) strlcat(zfile1, COMPRESS_POSTFIX,
+ sizeof(zfile1));
+ (void) strlcat(zfile2, COMPRESS_POSTFIX,
+ sizeof(zfile2));
+ if (lstat(zfile1, &st)) {
+ strlcpy(zfile1, file1, sizeof(zfile1));
+ strlcpy(zfile2, file2, sizeof(zfile2));
+ strlcat(zfile1, BZCOMPRESS_POSTFIX,
+ sizeof(zfile1));
+ strlcat(zfile2, BZCOMPRESS_POSTFIX,
+ sizeof(zfile2));
+ if (lstat(zfile1, &st))
+ continue;
+ }
+ }
+ if (noaction) {
+ printf("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 {
+ if (archtodir)
+ movefile(log, file1, perm, owner_uid,
+ group_gid);
+ else
+ (void) rename(log, file1);
+ }
+ }
+
+ if (noaction)
+ printf("Start new log...");
+ else {
+ strlcpy(tfile, log, sizeof(tfile));
+ strlcat(tfile, ".XXXXXX", sizeof(tfile));
+ mkstemp(tfile);
+ fd = creat(tfile, 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(tfile)) /* Add status message */
+ err(1, "can't add status message to log");
+ }
+ if (noaction)
+ printf("chmod %o %s...\n", perm, log);
+ else {
+ (void) chmod(tfile, perm);
+ if (rename(tfile, log) < 0) {
+ err(1, "can't start new log");
+ (void) unlink(tfile);
+ }
+ }
+
+ 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) || (flags & CE_BZCOMPACT)) {
+ if (need_notification && !notified)
+ warnx(
+ "log %s not compressed because daemon not notified",
+ log);
+ 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);
+ }
+ if (archtodir) {
+ (void) snprintf(file1, sizeof(file1), "%s/%s",
+ dirpart, namepart);
+ if (flags & CE_COMPACT)
+ compress_log(file1);
+ else if (flags & CE_BZCOMPACT)
+ bzcompress_log(file1);
+ } else {
+ if (flags & CE_COMPACT)
+ compress_log(log);
+ else if (flags & CE_BZCOMPACT)
+ bzcompress_log(log);
+ }
+ }
+ }
+}
+
+/* Log the fact that the logs were turned over */
+static int
+log_trim(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 gzip to compress the old log file */
+static void
+compress_log(char *log)
+{
+ pid_t pid;
+ char tmp[MAXPATHLEN];
+
+ (void) snprintf(tmp, sizeof(tmp), "%s.0", log);
+ pid = fork();
+ if (pid < 0)
+ err(1, "gzip fork");
+ else if (!pid) {
+ (void) execl(_PATH_GZIP, _PATH_GZIP, "-f", tmp, (char *)0);
+ err(1, _PATH_GZIP);
+ }
+}
+
+/* Fork of bzip2 to compress the old log file */
+static void
+bzcompress_log(char *log)
+{
+ pid_t pid;
+ char tmp[MAXPATHLEN];
+
+ snprintf(tmp, sizeof(tmp), "%s.0", log);
+ pid = fork();
+ if (pid < 0)
+ err(1, "bzip2 fork");
+ else if (!pid) {
+ execl(_PATH_BZIP2, _PATH_BZIP2, "-f", tmp, (char *)0);
+ err(1, _PATH_BZIP2);
+ }
+}
+
+/* Return size in kilobytes of a file */
+static int
+sizefile(char *file)
+{
+ struct stat sb;
+
+ if (stat(file, &sb) < 0)
+ return (-1);
+ return (kbytes(dbtob(sb.st_blocks)));
+}
+
+/* Return the age of old log file (file.0) */
+static int
+age_old_log(char *file)
+{
+ struct stat sb;
+ char tmp[MAXPATHLEN + sizeof(".0") + sizeof(COMPRESS_POSTFIX) + 1];
+
+ if (archtodir) {
+ char *p;
+
+ /* build name of archive directory into tmp */
+ if (*archdirname == '/') { /* absolute */
+ strlcpy(tmp, archdirname, sizeof(tmp));
+ } else { /* relative */
+ /* get directory part of logfile */
+ strlcpy(tmp, file, sizeof(tmp));
+ if ((p = rindex(tmp, '/')) == NULL)
+ tmp[0] = '\0';
+ else
+ *(p + 1) = '\0';
+ strlcat(tmp, archdirname, sizeof(tmp));
+ }
+
+ strlcat(tmp, "/", sizeof(tmp));
+
+ /* get filename part of logfile */
+ if ((p = rindex(file, '/')) == NULL)
+ strlcat(tmp, file, sizeof(tmp));
+ else
+ strlcat(tmp, p + 1, sizeof(tmp));
+ } else {
+ (void) strlcpy(tmp, file, sizeof(tmp));
+ }
+
+ 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(const 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(char *p)
+{
+ while (p && *p && isspace(*p))
+ p++;
+ return (p);
+}
+
+/* Skip Over Non-Blanks */
+char *
+son(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(char *s, char *errline)
+{
+ char *t;
+ time_t tsecs;
+ 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 % 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;
+ }
+ if ((tsecs = mktime(&tm)) == -1)
+ errx(1, "nonexistent time:\n%s", errline);
+ return tsecs;
+}
+
+/* physically move file */
+static void
+movefile(char *from, char *to, int perm, int owner_uid, int group_gid)
+{
+ FILE *src, *dst;
+ int c;
+
+ if ((src = fopen(from, "r")) == NULL)
+ err(1, "can't fopen %s for reading", from);
+ if ((dst = fopen(to, "w")) == NULL)
+ err(1, "can't fopen %s for writing", to);
+ if (fchown(fileno(dst), owner_uid, group_gid))
+ err(1, "can't fchown %s", to);
+ if (fchmod(fileno(dst), perm))
+ err(1, "can't fchmod %s", to);
+
+ while ((c = getc(src)) != EOF) {
+ if ((putc(c, dst)) == EOF)
+ err(1, "error writing to %s", to);
+ }
+
+ if (ferror(src))
+ err(1, "error reading from %s", from);
+ if ((fclose(src)) != 0)
+ err(1, "can't fclose %s", to);
+ if ((fclose(dst)) != 0)
+ err(1, "can't fclose %s", from);
+ if ((unlink(from)) != 0)
+ err(1, "can't unlink %s", from);
+}
+
+/* create one or more directory components of a path */
+static void
+createdir(char *dirpart)
+{
+ char *s, *d;
+ char mkdirpath[MAXPATHLEN];
+ struct stat st;
+
+ s = dirpart;
+ d = mkdirpath;
+
+ for (;;) {
+ *d++ = *s++;
+ if (*s == '/' || *s == '\0') {
+ *d = '\0';
+ if (lstat(mkdirpath, &st))
+ mkdir(mkdirpath, 0755);
+ }
+ if (*s == '\0')
+ break;
+ }
+}
+
+/*-
+ * Parse a cyclic time specification, the format is as follows:
+ *
+ * [Dhh] or [Wd[Dhh]] or [Mdd[Dhh]]
+ *
+ * to rotate a logfile cyclic at
+ *
+ * - every day (D) within a specific hour (hh) (hh = 0...23)
+ * - once a week (W) at a specific day (d) OR (d = 0..6, 0 = Sunday)
+ * - once a month (M) at a specific day (d) (d = 1..31,l|L)
+ *
+ * We don't accept a timezone specification; missing fields
+ * are defaulted to the current date but time zero.
+ */
+static time_t
+parseDWM(char *s, char *errline)
+{
+ char *t;
+ time_t tsecs;
+ struct tm tm, *tmp;
+ long l;
+ int nd;
+ static int mtab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+ int WMseen = 0;
+ int Dseen = 0;
+
+ tmp = localtime(&timenow);
+ tm = *tmp;
+
+ /* set no. of days per month */
+
+ nd = mtab[tm.tm_mon];
+
+ if (tm.tm_mon == 1) {
+ if (((tm.tm_year + 1900) % 4 == 0) &&
+ ((tm.tm_year + 1900) % 100 != 0) &&
+ ((tm.tm_year + 1900) % 400 == 0)) {
+ nd++; /* leap year, 29 days in february */
+ }
+ }
+ tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
+
+ for (;;) {
+ switch (*s) {
+ case 'D':
+ if (Dseen)
+ return -1;
+ Dseen++;
+ s++;
+ l = strtol(s, &t, 10);
+ if (l < 0 || l > 23)
+ return -1;
+ tm.tm_hour = l;
+ break;
+
+ case 'W':
+ if (WMseen)
+ return -1;
+ WMseen++;
+ s++;
+ l = strtol(s, &t, 10);
+ if (l < 0 || l > 6)
+ return -1;
+ if (l != tm.tm_wday) {
+ int save;
+
+ if (l < tm.tm_wday) {
+ save = 6 - tm.tm_wday;
+ save += (l + 1);
+ } else {
+ save = l - tm.tm_wday;
+ }
+
+ tm.tm_mday += save;
+
+ if (tm.tm_mday > nd) {
+ tm.tm_mon++;
+ tm.tm_mday = tm.tm_mday - nd;
+ }
+ }
+ break;
+
+ case 'M':
+ if (WMseen)
+ return -1;
+ WMseen++;
+ s++;
+ if (tolower(*s) == 'l') {
+ tm.tm_mday = nd;
+ s++;
+ t = s;
+ } else {
+ l = strtol(s, &t, 10);
+ if (l < 1 || l > 31)
+ return -1;
+
+ if (l > nd)
+ return -1;
+ tm.tm_mday = l;
+ }
+ break;
+
+ default:
+ return (-1);
+ break;
+ }
+
+ if (*t == '\0' || isspace(*t))
+ break;
+ else
+ s = t;
+ }
+ if ((tsecs = mktime(&tm)) == -1)
+ errx(1, "nonexistent time:\n%s", errline);
+ return tsecs;
+}
diff --git a/usr.sbin/newsyslog/pathnames.h b/usr.sbin/newsyslog/pathnames.h
new file mode 100644
index 0000000..3d2c6ff
--- /dev/null
+++ b/usr.sbin/newsyslog/pathnames.h
@@ -0,0 +1,28 @@
+/*
+ * This file contains changes from the Open Software Foundation.
+ */
+
+/*
+
+Copyright 1988, 1989 by the Massachusetts Institute of Technology
+
+Permission to use, copy, modify, and distribute this software
+and its documentation for any purpose and without fee is
+hereby granted, provided that the above copyright notice
+appear in all copies and that both that copyright notice and
+this permission notice appear in supporting documentation,
+and that the names of M.I.T. and the M.I.T. S.I.P.B. not be
+used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+M.I.T. and the M.I.T. S.I.P.B. make no representations about
+the suitability of this software for any purpose. It is
+provided "as is" without express or implied warranty.
+
+ $FreeBSD$
+
+*/
+
+#define _PATH_CONF "/etc/newsyslog.conf"
+#define _PATH_SYSLOGPID _PATH_VARRUN "syslog.pid"
+#define _PATH_BZIP2 "/usr/bin/bzip2"
+#define _PATH_GZIP "/usr/bin/gzip"
diff --git a/usr.sbin/nfsd/Makefile b/usr.sbin/nfsd/Makefile
new file mode 100644
index 0000000..e43192a
--- /dev/null
+++ b/usr.sbin/nfsd/Makefile
@@ -0,0 +1,8 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+# $FreeBSD$
+
+PROG= nfsd
+WARNS= 0
+MAN= nfsd.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/nfsd/nfsd.8 b/usr.sbin/nfsd/nfsd.8
new file mode 100644
index 0000000..76d4247
--- /dev/null
+++ b/usr.sbin/nfsd/nfsd.8
@@ -0,0 +1,191 @@
+.\" Copyright (c) 1989, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)nfsd.8 8.4 (Berkeley) 3/29/95
+.\" $FreeBSD$
+.\"
+.Dd March 29, 1995
+.Dt NFSD 8
+.Os
+.Sh NAME
+.Nm nfsd
+.Nd remote
+.Tn NFS
+server
+.Sh SYNOPSIS
+.Nm
+.Op Fl ardut
+.Op Fl n Ar num_servers
+.Op Fl h Ar bindip
+.Sh DESCRIPTION
+The
+.Nm
+utility runs on a server machine to service
+.Tn NFS
+requests from client machines.
+At least one
+.Nm
+must be running for a machine to operate as a server.
+.Pp
+Unless otherwise specified, four servers for
+.Tn UDP
+transport are started.
+.Pp
+The following options are available:
+.Bl -tag -width Ds
+.It Fl r
+Register the
+.Tn NFS
+service with
+.Xr rpcbind 8
+without creating any servers.
+This option can be used along with the
+.Fl u
+or
+.Fl t
+options to re-register NFS if the rpcbind server is restarted.
+.It Fl d
+Unregister the
+.Tn NFS
+service with
+.Xr rpcbind 8
+without creating any servers.
+.It Fl n
+Specifies how many servers to create.
+.It Fl h Ar bindip
+Specifies which IP address or hostname to bind to on the local host.
+This option is recommended when a host has multiple interfaces.
+Multiple
+.Fl h
+options may be specified.
+.It Fl a
+Specifies that nfsd should bind to the wildcard IP address.
+This is the default if no
+.Fl h
+options are given. It may also be specified in addition to any
+.Fl h
+options given. Note that NFS/UDP does not operate properly when
+bound to the wildcard IP address whether you use -a or do not use -h.
+.It Fl t
+Serve
+.Tn TCP NFS
+clients.
+.It Fl u
+Serve
+.Tn UDP NFS
+clients.
+.El
+.Pp
+For example,
+.Dq Li "nfsd -u -t -n 6"
+serves
+.Tn UDP
+and
+.Tn TCP
+transports using six daemons.
+.Pp
+A server should run enough daemons to handle
+the maximum level of concurrency from its clients,
+typically four to six.
+.Pp
+The
+.Nm
+utility listens for service requests at the port indicated in the
+.Tn NFS
+server specification; see
+.%T "Network File System Protocol Specification" ,
+RFC1094 and
+.%T "NFS: Network File System Version 3 Protocol Specification" .
+.Pp
+If
+.Nm
+detects that
+.Tn NFS
+is not loaded in the running kernel, it will attempt
+to load a loadable kernel module containing
+.Tn NFS
+support using
+.Xr kldload 8
+by way of
+.Xr vfsload 3 .
+If this fails, or no
+.Tn NFS
+KLD is available,
+.Nm
+will exit with an error.
+.Pp
+If
+.Nm
+is to be run on a host with multiple interfaces or interface aliases, use
+of the
+.Fl h
+option is recommended. If you do not use the option NFS may not respond to
+UDP packets from the same IP address they were sent to. Use of this option
+is also recommended when securing NFS exports on a firewalling machine such
+that the NFS sockets can only be accessed by the inside interface.
+The
+.Nm ipfw
+utility
+would then be used to block nfs-related packets that come in on the outside
+interface.
+.Pp
+The
+.Nm
+utility has to be terminated with
+.Dv SIGUSR1
+and cannot be killed with
+.Dv SIGTERM
+or
+.Dv SIGQUIT .
+The
+.Nm
+utility needs to ignore these signals in order to stay alive as long
+as possible during a shutdown, otherwise loopback mounts will
+not be able to unmount.
+If you have to kill
+.Nm
+just do a
+.Dq Li "kill -USR1 <PID of master nfsd>"
+.Sh DIAGNOSTICS
+.Ex -std
+.Sh SEE ALSO
+.Xr nfsstat 1 ,
+.Xr nfssvc 2 ,
+.Xr ipfw 8 ,
+.Xr kldload 8 ,
+.Xr mountd 8 ,
+.Xr nfsiod 8 ,
+.Xr rpcbind 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Bx 4.4 .
diff --git a/usr.sbin/nfsd/nfsd.c b/usr.sbin/nfsd/nfsd.c
new file mode 100644
index 0000000..39fd0c5
--- /dev/null
+++ b/usr.sbin/nfsd/nfsd.c
@@ -0,0 +1,845 @@
+/*
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1989, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)nfsd.c 8.9 (Berkeley) 3/29/95";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/syslog.h>
+#include <sys/wait.h>
+#include <sys/mount.h>
+#include <sys/linker.h>
+#include <sys/module.h>
+
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <nfs/rpcv2.h>
+#include <nfs/nfsproto.h>
+#include <nfsserver/nfs.h>
+
+#include <err.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <netdb.h>
+
+/* Global defs */
+#ifdef DEBUG
+#define syslog(e, s...) fprintf(stderr,s)
+int debug = 1;
+#else
+int debug = 0;
+#endif
+
+struct nfsd_srvargs nsd;
+
+#define MAXNFSDCNT 20
+#define DEFNFSDCNT 4
+pid_t children[MAXNFSDCNT]; /* PIDs of children */
+int nfsdcnt; /* number of children */
+
+void cleanup(int);
+void child_cleanup(int);
+void killchildren(void);
+void nfsd_exit(int);
+void nonfs(int);
+void reapchild(int);
+int setbindhost(struct addrinfo **ia, const char *bindhost,
+ struct addrinfo hints);
+void start_server(int);
+void unregistration(void);
+void usage(void);
+
+/*
+ * Nfs server daemon mostly just a user context for nfssvc()
+ *
+ * 1 - do file descriptor and signal cleanup
+ * 2 - fork the nfsd(s)
+ * 3 - create server socket(s)
+ * 4 - register socket with rpcbind
+ *
+ * For connectionless protocols, just pass the socket into the kernel via.
+ * nfssvc().
+ * For connection based sockets, loop doing accepts. When you get a new
+ * socket from accept, pass the msgsock into the kernel via. nfssvc().
+ * The arguments are:
+ * -r - reregister with rpcbind
+ * -d - unregister with rpcbind
+ * -t - support tcp nfs clients
+ * -u - support udp nfs clients
+ * followed by "n" which is the number of nfsds' to fork off
+ */
+int
+main(argc, argv, envp)
+ int argc;
+ char *argv[], *envp[];
+{
+ struct nfsd_args nfsdargs;
+ struct addrinfo *ai_udp, *ai_tcp, *ai_udp6, *ai_tcp6, hints;
+ struct netconfig *nconf_udp, *nconf_tcp, *nconf_udp6, *nconf_tcp6;
+ struct netbuf nb_udp, nb_tcp, nb_udp6, nb_tcp6;
+ struct sockaddr_in inetpeer;
+ struct sockaddr_in6 inet6peer;
+ fd_set ready, sockbits;
+ fd_set v4bits, v6bits;
+ int ch, connect_type_cnt, i, len, maxsock, msgsock;
+ int on = 1, unregister, reregister, sock;
+ int tcp6sock, ip6flag, tcpflag, tcpsock;
+ int udpflag, ecode, s, srvcnt;
+ int bindhostc, bindanyflag, rpcbreg, rpcbregcnt;
+ char **bindhost = NULL;
+ pid_t pid;
+
+ if (modfind("nfsserver") < 0) {
+ /* Not present in kernel, try loading it */
+ if (kldload("nfsserver") < 0 || modfind("nfsserver") < 0)
+ errx(1, "NFS server is not available");
+ }
+
+ nfsdcnt = DEFNFSDCNT;
+ unregister = reregister = tcpflag = maxsock = 0;
+ bindanyflag = udpflag = connect_type_cnt = bindhostc = 0;
+#define GETOPT "ah:n:rdtu"
+#define USAGE "[-ardtu] [-n num_servers] [-h bindip]"
+ while ((ch = getopt(argc, argv, GETOPT)) != -1)
+ switch (ch) {
+ case 'a':
+ bindanyflag = 1;
+ break;
+ case 'n':
+ nfsdcnt = atoi(optarg);
+ if (nfsdcnt < 1 || nfsdcnt > MAXNFSDCNT) {
+ warnx("nfsd count %d; reset to %d", nfsdcnt,
+ DEFNFSDCNT);
+ nfsdcnt = DEFNFSDCNT;
+ }
+ break;
+ case 'h':
+ bindhostc++;
+ bindhost = realloc(bindhost,sizeof(char *)*bindhostc);
+ if (bindhost == NULL)
+ errx(1, "Out of memory");
+ bindhost[bindhostc-1] = strdup(optarg);
+ if (bindhost[bindhostc-1] == NULL)
+ errx(1, "Out of memory");
+ break;
+ case 'r':
+ reregister = 1;
+ break;
+ case 'd':
+ unregister = 1;
+ break;
+ case 't':
+ tcpflag = 1;
+ break;
+ case 'u':
+ udpflag = 1;
+ break;
+ default:
+ case '?':
+ usage();
+ };
+ if (!tcpflag && !udpflag)
+ udpflag = 1;
+ argv += optind;
+ argc -= optind;
+
+ /*
+ * XXX
+ * Backward compatibility, trailing number is the count of daemons.
+ */
+ if (argc > 1)
+ usage();
+ if (argc == 1) {
+ nfsdcnt = atoi(argv[0]);
+ if (nfsdcnt < 1 || nfsdcnt > MAXNFSDCNT) {
+ warnx("nfsd count %d; reset to %d", nfsdcnt,
+ DEFNFSDCNT);
+ nfsdcnt = DEFNFSDCNT;
+ }
+ }
+
+ ip6flag = 1;
+ s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+ if (s == -1) {
+ if (errno != EPROTONOSUPPORT)
+ err(1, "socket");
+ ip6flag = 0;
+ } else if (getnetconfigent("udp6") == NULL ||
+ getnetconfigent("tcp6") == NULL) {
+ ip6flag = 0;
+ }
+ if (s != -1)
+ close(s);
+
+ if (bindhostc == 0 || bindanyflag) {
+ bindhostc++;
+ bindhost = realloc(bindhost,sizeof(char *)*bindhostc);
+ if (bindhost == NULL)
+ errx(1, "Out of memory");
+ bindhost[bindhostc-1] = strdup("*");
+ if (bindhost[bindhostc-1] == NULL)
+ errx(1, "Out of memory");
+ }
+
+ if (unregister) {
+ unregistration();
+ exit (0);
+ }
+ if (reregister) {
+ if (udpflag) {
+ memset(&hints, 0, sizeof hints);
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_protocol = IPPROTO_UDP;
+ ecode = getaddrinfo(NULL, "nfs", &hints, &ai_udp);
+ if (ecode != 0)
+ err(1, "getaddrinfo udp: %s", gai_strerror(ecode));
+ nconf_udp = getnetconfigent("udp");
+ if (nconf_udp == NULL)
+ err(1, "getnetconfigent udp failed");
+ nb_udp.buf = ai_udp->ai_addr;
+ nb_udp.len = nb_udp.maxlen = ai_udp->ai_addrlen;
+ if ((!rpcb_set(RPCPROG_NFS, 2, nconf_udp, &nb_udp)) ||
+ (!rpcb_set(RPCPROG_NFS, 3, nconf_udp, &nb_udp)))
+ err(1, "rpcb_set udp failed");
+ freeaddrinfo(ai_udp);
+ }
+ if (udpflag && ip6flag) {
+ memset(&hints, 0, sizeof hints);
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = AF_INET6;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_protocol = IPPROTO_UDP;
+ ecode = getaddrinfo(NULL, "nfs", &hints, &ai_udp6);
+ if (ecode != 0)
+ err(1, "getaddrinfo udp6: %s", gai_strerror(ecode));
+ nconf_udp6 = getnetconfigent("udp6");
+ if (nconf_udp6 == NULL)
+ err(1, "getnetconfigent udp6 failed");
+ nb_udp6.buf = ai_udp6->ai_addr;
+ nb_udp6.len = nb_udp6.maxlen = ai_udp6->ai_addrlen;
+ if ((!rpcb_set(RPCPROG_NFS, 2, nconf_udp6, &nb_udp6)) ||
+ (!rpcb_set(RPCPROG_NFS, 3, nconf_udp6, &nb_udp6)))
+ err(1, "rpcb_set udp6 failed");
+ freeaddrinfo(ai_udp6);
+ }
+ if (tcpflag) {
+ memset(&hints, 0, sizeof hints);
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+ ecode = getaddrinfo(NULL, "nfs", &hints, &ai_tcp);
+ if (ecode != 0)
+ err(1, "getaddrinfo tcp: %s", gai_strerror(ecode));
+ nconf_tcp = getnetconfigent("tcp");
+ if (nconf_tcp == NULL)
+ err(1, "getnetconfigent tcp failed");
+ nb_tcp.buf = ai_tcp->ai_addr;
+ nb_tcp.len = nb_tcp.maxlen = ai_tcp->ai_addrlen;
+ if ((!rpcb_set(RPCPROG_NFS, 2, nconf_tcp, &nb_tcp)) ||
+ (!rpcb_set(RPCPROG_NFS, 3, nconf_tcp, &nb_tcp)))
+ err(1, "rpcb_set tcp failed");
+ freeaddrinfo(ai_tcp);
+ }
+ if (tcpflag && ip6flag) {
+ memset(&hints, 0, sizeof hints);
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = AF_INET6;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+ ecode = getaddrinfo(NULL, "nfs", &hints, &ai_tcp6);
+ if (ecode != 0)
+ err(1, "getaddrinfo tcp6: %s", gai_strerror(ecode));
+ nconf_tcp6 = getnetconfigent("tcp6");
+ if (nconf_tcp6 == NULL)
+ err(1, "getnetconfigent tcp6 failed");
+ nb_tcp6.buf = ai_tcp6->ai_addr;
+ nb_tcp6.len = nb_tcp6.maxlen = ai_tcp6->ai_addrlen;
+ if ((!rpcb_set(RPCPROG_NFS, 2, nconf_tcp6, &nb_tcp6)) ||
+ (!rpcb_set(RPCPROG_NFS, 3, nconf_tcp6, &nb_tcp6)))
+ err(1, "rpcb_set tcp6 failed");
+ freeaddrinfo(ai_tcp6);
+ }
+ exit (0);
+ }
+ if (debug == 0) {
+ daemon(0, 0);
+ (void)signal(SIGHUP, SIG_IGN);
+ (void)signal(SIGINT, SIG_IGN);
+ /*
+ * nfsd sits in the kernel most of the time. It needs
+ * to ignore SIGTERM/SIGQUIT in order to stay alive as long
+ * as possible during a shutdown, otherwise loopback
+ * mounts will not be able to unmount.
+ */
+ (void)signal(SIGTERM, SIG_IGN);
+ (void)signal(SIGQUIT, SIG_IGN);
+ }
+ (void)signal(SIGSYS, nonfs);
+ (void)signal(SIGCHLD, reapchild);
+
+ openlog("nfsd", LOG_PID, LOG_DAEMON);
+
+ /* If we use UDP only, we start the last server below. */
+ srvcnt = tcpflag ? nfsdcnt : nfsdcnt - 1;
+ for (i = 0; i < srvcnt; i++) {
+ switch ((pid = fork())) {
+ case -1:
+ syslog(LOG_ERR, "fork: %m");
+ nfsd_exit(1);
+ case 0:
+ break;
+ default:
+ children[i] = pid;
+ continue;
+ }
+ (void)signal(SIGUSR1, child_cleanup);
+ setproctitle("server");
+
+ start_server(0);
+ }
+
+ (void)signal(SIGUSR1, cleanup);
+ FD_ZERO(&v4bits);
+ FD_ZERO(&v6bits);
+
+ rpcbregcnt = 0;
+ /* Set up the socket for udp and rpcb register it. */
+ if (udpflag) {
+ rpcbreg = 0;
+ for (i = 0; i < bindhostc; i++) {
+ memset(&hints, 0, sizeof hints);
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_protocol = IPPROTO_UDP;
+ if (setbindhost(&ai_udp, bindhost[i], hints) == 0) {
+ rpcbreg = 1;
+ rpcbregcnt++;
+ if ((sock = socket(ai_udp->ai_family,
+ ai_udp->ai_socktype,
+ ai_udp->ai_protocol)) < 0) {
+ syslog(LOG_ERR,
+ "can't create udp socket");
+ nfsd_exit(1);
+ }
+ if (bind(sock, ai_udp->ai_addr,
+ ai_udp->ai_addrlen) < 0) {
+ syslog(LOG_ERR,
+ "can't bind udp addr %s: %m",
+ bindhost[i]);
+ nfsd_exit(1);
+ }
+ freeaddrinfo(ai_udp);
+ nfsdargs.sock = sock;
+ nfsdargs.name = NULL;
+ nfsdargs.namelen = 0;
+ if (nfssvc(NFSSVC_ADDSOCK, &nfsdargs) < 0) {
+ syslog(LOG_ERR, "can't Add UDP socket");
+ nfsd_exit(1);
+ }
+ (void)close(sock);
+ }
+ }
+ if (rpcbreg == 1) {
+ memset(&hints, 0, sizeof hints);
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_protocol = IPPROTO_UDP;
+ ecode = getaddrinfo(NULL, "nfs", &hints, &ai_udp);
+ if (ecode != 0) {
+ syslog(LOG_ERR, "getaddrinfo udp: %s",
+ gai_strerror(ecode));
+ nfsd_exit(1);
+ }
+ nconf_udp = getnetconfigent("udp");
+ if (nconf_udp == NULL)
+ err(1, "getnetconfigent udp failed");
+ nb_udp.buf = ai_udp->ai_addr;
+ nb_udp.len = nb_udp.maxlen = ai_udp->ai_addrlen;
+ if ((!rpcb_set(RPCPROG_NFS, 2, nconf_udp, &nb_udp)) ||
+ (!rpcb_set(RPCPROG_NFS, 3, nconf_udp, &nb_udp)))
+ err(1, "rpcb_set udp failed");
+ freeaddrinfo(ai_udp);
+ }
+ }
+
+ /* Set up the socket for udp6 and rpcb register it. */
+ if (udpflag && ip6flag) {
+ rpcbreg = 0;
+ for (i = 0; i < bindhostc; i++) {
+ memset(&hints, 0, sizeof hints);
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = AF_INET6;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_protocol = IPPROTO_UDP;
+ if (setbindhost(&ai_udp6, bindhost[i], hints) == 0) {
+ rpcbreg = 1;
+ rpcbregcnt++;
+ if ((sock = socket(ai_udp6->ai_family,
+ ai_udp6->ai_socktype,
+ ai_udp6->ai_protocol)) < 0) {
+ syslog(LOG_ERR,
+ "can't create udp6 socket");
+ nfsd_exit(1);
+ }
+ if (setsockopt(sock, IPPROTO_IPV6,
+ IPV6_BINDV6ONLY,
+ &on, sizeof on) < 0) {
+ syslog(LOG_ERR,
+ "can't set v6-only binding for "
+ "udp6 socket: %m");
+ nfsd_exit(1);
+ }
+ if (bind(sock, ai_udp6->ai_addr,
+ ai_udp6->ai_addrlen) < 0) {
+ syslog(LOG_ERR,
+ "can't bind udp6 addr %s: %m",
+ bindhost[i]);
+ nfsd_exit(1);
+ }
+ freeaddrinfo(ai_udp6);
+ nfsdargs.sock = sock;
+ nfsdargs.name = NULL;
+ nfsdargs.namelen = 0;
+ if (nfssvc(NFSSVC_ADDSOCK, &nfsdargs) < 0) {
+ syslog(LOG_ERR,
+ "can't add UDP6 socket");
+ nfsd_exit(1);
+ }
+ (void)close(sock);
+ }
+ }
+ if (rpcbreg == 1) {
+ memset(&hints, 0, sizeof hints);
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = AF_INET6;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_protocol = IPPROTO_UDP;
+ ecode = getaddrinfo(NULL, "nfs", &hints, &ai_udp6);
+ if (ecode != 0) {
+ syslog(LOG_ERR, "getaddrinfo udp6: %s",
+ gai_strerror(ecode));
+ nfsd_exit(1);
+ }
+ nconf_udp6 = getnetconfigent("udp6");
+ if (nconf_udp6 == NULL)
+ err(1, "getnetconfigent udp6 failed");
+ nb_udp6.buf = ai_udp6->ai_addr;
+ nb_udp6.len = nb_udp6.maxlen = ai_udp6->ai_addrlen;
+ if ((!rpcb_set(RPCPROG_NFS, 2, nconf_udp6, &nb_udp6)) ||
+ (!rpcb_set(RPCPROG_NFS, 3, nconf_udp6, &nb_udp6)))
+ err(1, "rpcb_set udp6 failed");
+ freeaddrinfo(ai_udp6);
+ }
+ }
+
+ /* Set up the socket for tcp and rpcb register it. */
+ if (tcpflag) {
+ rpcbreg = 0;
+ for (i = 0; i < bindhostc; i++) {
+ memset(&hints, 0, sizeof hints);
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+ if (setbindhost(&ai_tcp, bindhost[i], hints) == 0) {
+ rpcbreg = 1;
+ rpcbregcnt++;
+ if ((tcpsock = socket(AF_INET, SOCK_STREAM,
+ 0)) < 0) {
+ syslog(LOG_ERR,
+ "can't create tpc socket");
+ nfsd_exit(1);
+ }
+ if (setsockopt(tcpsock, SOL_SOCKET,
+ SO_REUSEADDR,
+ (char *)&on, sizeof(on)) < 0)
+ syslog(LOG_ERR,
+ "setsockopt SO_REUSEADDR: %m");
+ if (bind(tcpsock, ai_tcp->ai_addr,
+ ai_tcp->ai_addrlen) < 0) {
+ syslog(LOG_ERR,
+ "can't bind tcp addr %s: %m",
+ bindhost[i]);
+ nfsd_exit(1);
+ }
+ if (listen(tcpsock, 5) < 0) {
+ syslog(LOG_ERR, "listen failed");
+ nfsd_exit(1);
+ }
+ freeaddrinfo(ai_tcp);
+ FD_SET(tcpsock, &sockbits);
+ FD_SET(tcpsock, &v4bits);
+ maxsock = tcpsock;
+ connect_type_cnt++;
+ }
+ }
+ if (rpcbreg == 1) {
+ memset(&hints, 0, sizeof hints);
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+ ecode = getaddrinfo(NULL, "nfs", &hints,
+ &ai_tcp);
+ if (ecode != 0) {
+ syslog(LOG_ERR, "getaddrinfo tcp: %s",
+ gai_strerror(ecode));
+ nfsd_exit(1);
+ }
+ nconf_tcp = getnetconfigent("tcp");
+ if (nconf_tcp == NULL)
+ err(1, "getnetconfigent tcp failed");
+ nb_tcp.buf = ai_tcp->ai_addr;
+ nb_tcp.len = nb_tcp.maxlen = ai_tcp->ai_addrlen;
+ if ((!rpcb_set(RPCPROG_NFS, 2, nconf_tcp,
+ &nb_tcp)) || (!rpcb_set(RPCPROG_NFS, 3,
+ nconf_tcp, &nb_tcp)))
+ err(1, "rpcb_set tcp failed");
+ freeaddrinfo(ai_tcp);
+ }
+ }
+
+ /* Set up the socket for tcp6 and rpcb register it. */
+ if (tcpflag && ip6flag) {
+ rpcbreg = 0;
+ for (i = 0; i < bindhostc; i++) {
+ memset(&hints, 0, sizeof hints);
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = AF_INET6;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+ if (setbindhost(&ai_tcp6, bindhost[i], hints) == 0) {
+ rpcbreg = 1;
+ rpcbregcnt++;
+ if ((tcp6sock = socket(ai_tcp6->ai_family,
+ ai_tcp6->ai_socktype,
+ ai_tcp6->ai_protocol)) < 0) {
+ syslog(LOG_ERR,
+ "can't create tcp6 socket");
+ nfsd_exit(1);
+ }
+ if (setsockopt(tcp6sock, SOL_SOCKET,
+ SO_REUSEADDR,
+ (char *)&on, sizeof(on)) < 0)
+ syslog(LOG_ERR,
+ "setsockopt SO_REUSEADDR: %m");
+ if (setsockopt(tcp6sock, IPPROTO_IPV6,
+ IPV6_BINDV6ONLY, &on, sizeof on) < 0) {
+ syslog(LOG_ERR,
+ "can't set v6-only binding for tcp6 "
+ "socket: %m");
+ nfsd_exit(1);
+ }
+ if (bind(tcp6sock, ai_tcp6->ai_addr,
+ ai_tcp6->ai_addrlen) < 0) {
+ syslog(LOG_ERR,
+ "can't bind tcp6 addr %s: %m",
+ bindhost[i]);
+ nfsd_exit(1);
+ }
+ if (listen(tcp6sock, 5) < 0) {
+ syslog(LOG_ERR, "listen failed");
+ nfsd_exit(1);
+ }
+ freeaddrinfo(ai_tcp6);
+ FD_SET(tcp6sock, &sockbits);
+ FD_SET(tcp6sock, &v6bits);
+ if (maxsock < tcp6sock)
+ maxsock = tcp6sock;
+ connect_type_cnt++;
+ }
+ }
+ if (rpcbreg == 1) {
+ memset(&hints, 0, sizeof hints);
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = AF_INET6;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+ ecode = getaddrinfo(NULL, "nfs", &hints, &ai_tcp6);
+ if (ecode != 0) {
+ syslog(LOG_ERR, "getaddrinfo tcp6: %s",
+ gai_strerror(ecode));
+ nfsd_exit(1);
+ }
+ nconf_tcp6 = getnetconfigent("tcp6");
+ if (nconf_tcp6 == NULL)
+ err(1, "getnetconfigent tcp6 failed");
+ nb_tcp6.buf = ai_tcp6->ai_addr;
+ nb_tcp6.len = nb_tcp6.maxlen = ai_tcp6->ai_addrlen;
+ if ((!rpcb_set(RPCPROG_NFS, 2, nconf_tcp6, &nb_tcp6)) ||
+ (!rpcb_set(RPCPROG_NFS, 3, nconf_tcp6, &nb_tcp6)))
+ err(1, "rpcb_set tcp6 failed");
+ freeaddrinfo(ai_tcp6);
+ }
+ }
+
+ if (rpcbregcnt == 0) {
+ syslog(LOG_ERR, "rpcb_set() failed, nothing to do: %m");
+ nfsd_exit(1);
+ }
+
+ if (tcpflag && connect_type_cnt == 0) {
+ syslog(LOG_ERR, "tcp connects == 0, nothing to do: %m");
+ nfsd_exit(1);
+ }
+
+ setproctitle("master");
+ /*
+ * We always want a master to have a clean way to to shut nfsd down
+ * (with unregistration): if the master is killed, it unregisters and
+ * kills all children. If we run for UDP only (and so do not have to
+ * loop waiting waiting for accept), we instead make the parent
+ * a "server" too. start_server will not return.
+ */
+ if (!tcpflag)
+ start_server(1);
+
+ /*
+ * Loop forever accepting connections and passing the sockets
+ * into the kernel for the mounts.
+ */
+ for (;;) {
+ ready = sockbits;
+ if (connect_type_cnt > 1) {
+ if (select(maxsock + 1,
+ &ready, NULL, NULL, NULL) < 1) {
+ syslog(LOG_ERR, "select failed: %m");
+ nfsd_exit(1);
+ }
+ }
+ for (tcpsock = 0; tcpsock <= maxsock; tcpsock++) {
+ if (FD_ISSET(tcpsock, &ready)) {
+ if (FD_ISSET(tcpsock, &v4bits)) {
+ len = sizeof(inetpeer);
+ if ((msgsock = accept(tcpsock,
+ (struct sockaddr *)&inetpeer, &len)) < 0) {
+ syslog(LOG_ERR, "accept failed: %m");
+ nfsd_exit(1);
+ }
+ memset(inetpeer.sin_zero, 0,
+ sizeof(inetpeer.sin_zero));
+ if (setsockopt(msgsock, SOL_SOCKET,
+ SO_KEEPALIVE, (char *)&on, sizeof(on)) < 0)
+ syslog(LOG_ERR,
+ "setsockopt SO_KEEPALIVE: %m");
+ nfsdargs.sock = msgsock;
+ nfsdargs.name = (caddr_t)&inetpeer;
+ nfsdargs.namelen = len;
+ nfssvc(NFSSVC_ADDSOCK, &nfsdargs);
+ (void)close(msgsock);
+ } else if (FD_ISSET(tcpsock, &v6bits)) {
+ len = sizeof(inet6peer);
+ if ((msgsock = accept(tcpsock,
+ (struct sockaddr *)&inet6peer,
+ &len)) < 0) {
+ syslog(LOG_ERR,
+ "accept failed: %m");
+ nfsd_exit(1);
+ }
+ if (setsockopt(msgsock, SOL_SOCKET,
+ SO_KEEPALIVE, (char *)&on,
+ sizeof(on)) < 0)
+ syslog(LOG_ERR, "setsockopt "
+ "SO_KEEPALIVE: %m");
+ nfsdargs.sock = msgsock;
+ nfsdargs.name = (caddr_t)&inet6peer;
+ nfsdargs.namelen = len;
+ nfssvc(NFSSVC_ADDSOCK, &nfsdargs);
+ (void)close(msgsock);
+ }
+ }
+ }
+ }
+}
+
+int
+setbindhost(struct addrinfo **ai, const char *bindhost, struct addrinfo hints)
+{
+ int ecode;
+ u_int32_t host_addr[4]; /* IPv4 or IPv6 */
+ const char *hostptr;
+
+ if (bindhost == NULL || strcmp("*", bindhost) == 0)
+ hostptr = NULL;
+ else
+ hostptr = bindhost;
+
+ if (hostptr != NULL) {
+ switch (hints.ai_family) {
+ case AF_INET:
+ if (inet_pton(AF_INET, hostptr, host_addr) == 1) {
+ hints.ai_flags = AI_NUMERICHOST;
+ } else {
+ if (inet_pton(AF_INET6, hostptr,
+ host_addr) == 1)
+ return (1);
+ }
+ break;
+ case AF_INET6:
+ if (inet_pton(AF_INET6, hostptr, host_addr) == 1) {
+ hints.ai_flags = AI_NUMERICHOST;
+ } else {
+ if (inet_pton(AF_INET, hostptr,
+ host_addr) == 1)
+ return (1);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ ecode = getaddrinfo(hostptr, "nfs", &hints, ai);
+ if (ecode != 0) {
+ syslog(LOG_ERR, "getaddrinfo %s: %s", bindhost,
+ gai_strerror(ecode));
+ return (1);
+ }
+ return (0);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "usage: nfsd %s\n", USAGE);
+ exit(1);
+}
+
+void
+nonfs(signo)
+ int signo;
+{
+ syslog(LOG_ERR, "missing system call: NFS not available");
+}
+
+void
+reapchild(signo)
+ int signo;
+{
+ pid_t pid;
+ int i;
+
+ while ((pid = wait3(NULL, WNOHANG, NULL)) > 0) {
+ for (i = 0; i < nfsdcnt; i++)
+ if (pid == children[i])
+ children[i] = -1;
+ }
+}
+
+void
+unregistration()
+{
+ if ((!rpcb_unset(RPCPROG_NFS, 2, NULL)) ||
+ (!rpcb_unset(RPCPROG_NFS, 3, NULL)))
+ syslog(LOG_ERR, "rpcb_unset failed");
+}
+
+void
+killchildren()
+{
+ int i;
+
+ for (i = 0; i < nfsdcnt; i++) {
+ if (children[i] > 0)
+ kill(children[i], SIGKILL);
+ }
+}
+
+/*
+ * Cleanup master after SIGUSR1.
+ */
+void
+cleanup(signo)
+{
+ nfsd_exit(0);
+}
+
+/*
+ * Cleanup child after SIGUSR1.
+ */
+void
+child_cleanup(signo)
+{
+ exit(0);
+}
+
+void
+nfsd_exit(int status)
+{
+ killchildren();
+ unregistration();
+ exit(status);
+}
+
+void
+start_server(int master)
+{
+ int status;
+
+ status = 0;
+ nsd.nsd_nfsd = NULL;
+ if (nfssvc(NFSSVC_NFSD, &nsd) < 0) {
+ syslog(LOG_ERR, "nfssvc: %m");
+ status = 1;
+ }
+ if (master)
+ nfsd_exit(status);
+ else
+ exit(status);
+}
diff --git a/usr.sbin/ngctl/Makefile b/usr.sbin/ngctl/Makefile
new file mode 100644
index 0000000..92d6d27
--- /dev/null
+++ b/usr.sbin/ngctl/Makefile
@@ -0,0 +1,12 @@
+# $FreeBSD$
+# $Whistle: Makefile,v 1.3 1999/01/16 00:10:11 archie Exp $
+
+PROG= ngctl
+MAN= ngctl.8
+SRCS= main.c mkpeer.c config.c connect.c name.c show.c list.c \
+ msg.c debug.c shutdown.c rmhook.c status.c types.c write.c
+
+DPADD= ${LIBNETGRAPH}
+LDADD= -lnetgraph
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ngctl/config.c b/usr.sbin/ngctl/config.c
new file mode 100644
index 0000000..144293a
--- /dev/null
+++ b/usr.sbin/ngctl/config.c
@@ -0,0 +1,103 @@
+/*
+ * config.c
+ *
+ * Copyright (c) 1996-1999 Whistle Communications, Inc.
+ * All rights reserved.
+ *
+ * Subject to the following obligations and disclaimer of warranty, use and
+ * redistribution of this software, in source or object code forms, with or
+ * without modifications are expressly permitted by Whistle Communications;
+ * provided, however, that:
+ * 1. Any and all reproductions of the source or object code must include the
+ * copyright notice above and the following disclaimer of warranties; and
+ * 2. No rights are granted, in any manner or form, to use Whistle
+ * Communications, Inc. trademarks, including the mark "WHISTLE
+ * COMMUNICATIONS" on advertising, endorsements, or otherwise except as
+ * such appears in the above copyright notice or in the software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
+ * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
+ * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+ * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
+ * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
+ * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
+ * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
+ * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
+ * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "ngctl.h"
+
+#define NOCONFIG "<no config>"
+
+static int ConfigCmd(int ac, char **av);
+
+const struct ngcmd config_cmd = {
+ ConfigCmd,
+ "config <path> [arguments]",
+ "get or set configuration of node at <path>",
+ NULL
+};
+
+static int
+ConfigCmd(int ac, char **av)
+{
+ u_char sbuf[sizeof(struct ng_mesg) + NG_TEXTRESPONSE];
+ struct ng_mesg *const resp = (struct ng_mesg *) sbuf;
+ char *const status = (char *) resp->data;
+ char *path;
+ char buf[NG_TEXTRESPONSE];
+ int nostat = 0, i;
+
+ /* Get arguments */
+ if (ac < 2)
+ return(CMDRTN_USAGE);
+ path = av[1];
+
+ *buf = '\0';
+ for (i = 2; i < ac; i++) {
+ if (i != 2)
+ strcat(buf, " ");
+ strcat(buf, av[i]);
+ }
+
+ /* Get node config summary */
+ if (*buf != '\0')
+ i = NgSendMsg(csock, path, NGM_GENERIC_COOKIE,
+ NGM_TEXT_CONFIG, buf, strlen(buf) + 1);
+ else
+ i = NgSendMsg(csock, path, NGM_GENERIC_COOKIE,
+ NGM_TEXT_CONFIG, NULL, 0);
+ if (i < 0) {
+ switch (errno) {
+ case EINVAL:
+ nostat = 1;
+ break;
+ default:
+ warn("send msg");
+ return(CMDRTN_ERROR);
+ }
+ } else {
+ if (NgRecvMsg(csock, resp, sizeof(sbuf), NULL) < 0
+ || (resp->header.flags & NGF_RESP) == 0)
+ nostat = 1;
+ }
+
+ /* Show it */
+ if (nostat)
+ printf("No config available for \"%s\"\n", path);
+ else
+ printf("Config for \"%s\":\n%s\n", path, status);
+ return(CMDRTN_OK);
+}
+
diff --git a/usr.sbin/ngctl/connect.c b/usr.sbin/ngctl/connect.c
new file mode 100644
index 0000000..470468a
--- /dev/null
+++ b/usr.sbin/ngctl/connect.c
@@ -0,0 +1,86 @@
+
+/*
+ * connect.c
+ *
+ * Copyright (c) 1996-1999 Whistle Communications, Inc.
+ * All rights reserved.
+ *
+ * Subject to the following obligations and disclaimer of warranty, use and
+ * redistribution of this software, in source or object code forms, with or
+ * without modifications are expressly permitted by Whistle Communications;
+ * provided, however, that:
+ * 1. Any and all reproductions of the source or object code must include the
+ * copyright notice above and the following disclaimer of warranties; and
+ * 2. No rights are granted, in any manner or form, to use Whistle
+ * Communications, Inc. trademarks, including the mark "WHISTLE
+ * COMMUNICATIONS" on advertising, endorsements, or otherwise except as
+ * such appears in the above copyright notice or in the software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
+ * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
+ * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+ * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
+ * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
+ * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
+ * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
+ * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
+ * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "ngctl.h"
+
+static int ConnectCmd(int ac, char **av);
+
+const struct ngcmd connect_cmd = {
+ ConnectCmd,
+ "connect [path] <relpath> <hook> <peerhook>",
+ "Connects hook <peerhook> of the node at <relpath> to <hook>",
+ "The connect command creates a link between the two nodes at"
+ " \"path\" and \"relpath\" using hooks \"hook\" and \"peerhook\","
+ " respectively. The \"relpath\", if not absolute, is specified"
+ " relative to the node at \"path\"."
+ " If \"path\" is omitted then \".\" is assumed.",
+ { "join" }
+};
+
+static int
+ConnectCmd(int ac, char **av)
+{
+ struct ngm_connect con;
+ char *path = ".";
+
+ /* Get arguments */
+ switch (ac) {
+ case 5:
+ path = av[1];
+ ac--;
+ av++;
+ /* FALLTHROUGH */
+ case 4:
+ snprintf(con.path, sizeof(con.path), "%s", av[1]);
+ snprintf(con.ourhook, sizeof(con.ourhook), "%s", av[2]);
+ snprintf(con.peerhook, sizeof(con.peerhook), "%s", av[3]);
+ break;
+ default:
+ return(CMDRTN_USAGE);
+ }
+
+ /* Send message */
+ if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE,
+ NGM_CONNECT, &con, sizeof(con)) < 0) {
+ warn("send msg");
+ return(CMDRTN_ERROR);
+ }
+ return(CMDRTN_OK);
+}
+
diff --git a/usr.sbin/ngctl/debug.c b/usr.sbin/ngctl/debug.c
new file mode 100644
index 0000000..7b355e5
--- /dev/null
+++ b/usr.sbin/ngctl/debug.c
@@ -0,0 +1,78 @@
+
+/*
+ * debug.c
+ *
+ * Copyright (c) 1996-1999 Whistle Communications, Inc.
+ * All rights reserved.
+ *
+ * Subject to the following obligations and disclaimer of warranty, use and
+ * redistribution of this software, in source or object code forms, with or
+ * without modifications are expressly permitted by Whistle Communications;
+ * provided, however, that:
+ * 1. Any and all reproductions of the source or object code must include the
+ * copyright notice above and the following disclaimer of warranties; and
+ * 2. No rights are granted, in any manner or form, to use Whistle
+ * Communications, Inc. trademarks, including the mark "WHISTLE
+ * COMMUNICATIONS" on advertising, endorsements, or otherwise except as
+ * such appears in the above copyright notice or in the software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
+ * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
+ * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+ * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
+ * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
+ * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
+ * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
+ * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
+ * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "ngctl.h"
+
+static int DebugCmd(int ac, char **av);
+
+const struct ngcmd debug_cmd = {
+ DebugCmd,
+ "debug [level]",
+ "Get/set debugging verbosity level",
+ "Without any argument, this command displays the current"
+ " debugging verbosity level. If the argument is ``+'' or ``-''"
+ " the debug level is incremented or decremented; otherwise,"
+ " it must be an absolute numerical level."
+};
+
+static int
+DebugCmd(int ac, char **av)
+{
+ int level;
+
+ /* Get arguments */
+ switch (ac) {
+ case 2:
+ if (!strcmp(av[1], "+"))
+ level = NgSetDebug(-1) + 1;
+ else if (!strcmp(av[1], "-"))
+ level = NgSetDebug(-1) - 1;
+ else if ((level = atoi(av[1])) < 0)
+ return(CMDRTN_USAGE);
+ NgSetDebug(level);
+ break;
+ case 1:
+ printf("Current debug level is %d\n", NgSetDebug(-1));
+ break;
+ default:
+ return(CMDRTN_USAGE);
+ }
+ return(CMDRTN_OK);
+}
+
diff --git a/usr.sbin/ngctl/list.c b/usr.sbin/ngctl/list.c
new file mode 100644
index 0000000..aeb74fb
--- /dev/null
+++ b/usr.sbin/ngctl/list.c
@@ -0,0 +1,114 @@
+
+/*
+ * list.c
+ *
+ * Copyright (c) 1996-1999 Whistle Communications, Inc.
+ * All rights reserved.
+ *
+ * Subject to the following obligations and disclaimer of warranty, use and
+ * redistribution of this software, in source or object code forms, with or
+ * without modifications are expressly permitted by Whistle Communications;
+ * provided, however, that:
+ * 1. Any and all reproductions of the source or object code must include the
+ * copyright notice above and the following disclaimer of warranties; and
+ * 2. No rights are granted, in any manner or form, to use Whistle
+ * Communications, Inc. trademarks, including the mark "WHISTLE
+ * COMMUNICATIONS" on advertising, endorsements, or otherwise except as
+ * such appears in the above copyright notice or in the software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
+ * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
+ * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+ * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
+ * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
+ * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
+ * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
+ * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
+ * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "ngctl.h"
+
+static int ListCmd(int ac, char **av);
+
+const struct ngcmd list_cmd = {
+ ListCmd,
+ "list [-n]",
+ "Show information about all nodes",
+ "The list command shows information every node that currently"
+ " exists in the netgraph system. The optional -n argument limits"
+ " this list to only those nodes with a global name assignment.",
+ { "ls" }
+};
+
+static int
+ListCmd(int ac, char **av)
+{
+ u_char rbuf[16 * 1024];
+ struct ng_mesg *const resp = (struct ng_mesg *) rbuf;
+ struct namelist *const nlist = (struct namelist *) resp->data;
+ int named_only = 0;
+ int k, ch, rtn = CMDRTN_OK;
+
+ /* Get options */
+ optind = 1;
+ while ((ch = getopt(ac, av, "n")) != EOF) {
+ switch (ch) {
+ case 'n':
+ named_only = 1;
+ break;
+ case '?':
+ default:
+ return(CMDRTN_USAGE);
+ break;
+ }
+ }
+ ac -= optind;
+ av += optind;
+
+ /* Get arguments */
+ switch (ac) {
+ case 0:
+ break;
+ default:
+ return(CMDRTN_USAGE);
+ }
+
+ /* Get list of nodes */
+ if (NgSendMsg(csock, ".", NGM_GENERIC_COOKIE,
+ named_only ? NGM_LISTNAMES : NGM_LISTNODES, NULL, 0) < 0) {
+ warn("send msg");
+ return(CMDRTN_ERROR);
+ }
+ if (NgRecvMsg(csock, resp, sizeof(rbuf), NULL) < 0) {
+ warn("recv msg");
+ return(CMDRTN_ERROR);
+ }
+
+ /* Show each node */
+ printf("There are %d total %snodes:\n",
+ nlist->numnames, named_only ? "named " : "");
+ for (k = 0; k < nlist->numnames; k++) {
+ char path[NG_PATHLEN+1];
+ char *av[3] = { "list", "-n", path };
+
+ snprintf(path, sizeof(path),
+ "[%lx]:", (u_long) nlist->nodeinfo[k].id);
+ if ((rtn = (*show_cmd.func)(3, av)) != CMDRTN_OK)
+ break;
+ }
+
+ /* Done */
+ return (rtn);
+}
+
diff --git a/usr.sbin/ngctl/main.c b/usr.sbin/ngctl/main.c
new file mode 100644
index 0000000..109ac17
--- /dev/null
+++ b/usr.sbin/ngctl/main.c
@@ -0,0 +1,507 @@
+
+/*
+ * main.c
+ *
+ * Copyright (c) 1996-1999 Whistle Communications, Inc.
+ * All rights reserved.
+ *
+ * Subject to the following obligations and disclaimer of warranty, use and
+ * redistribution of this software, in source or object code forms, with or
+ * without modifications are expressly permitted by Whistle Communications;
+ * provided, however, that:
+ * 1. Any and all reproductions of the source or object code must include the
+ * copyright notice above and the following disclaimer of warranties; and
+ * 2. No rights are granted, in any manner or form, to use Whistle
+ * Communications, Inc. trademarks, including the mark "WHISTLE
+ * COMMUNICATIONS" on advertising, endorsements, or otherwise except as
+ * such appears in the above copyright notice or in the software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
+ * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
+ * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+ * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
+ * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
+ * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
+ * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
+ * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
+ * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ * $Whistle: main.c,v 1.12 1999/11/29 19:17:46 archie Exp $
+ */
+
+#include "ngctl.h"
+
+#define PROMPT "+ "
+#define MAX_ARGS 512
+#define WHITESPACE " \t\r\n\v\f"
+#define DUMP_BYTES_PER_LINE 16
+
+/* Internal functions */
+static int ReadFile(FILE *fp);
+static int DoParseCommand(char *line);
+static int DoCommand(int ac, char **av);
+static int DoInteractive(void);
+static const struct ngcmd *FindCommand(const char *string);
+static int MatchCommand(const struct ngcmd *cmd, const char *s);
+static void Usage(const char *msg);
+static int ReadCmd(int ac, char **av);
+static int HelpCmd(int ac, char **av);
+static int QuitCmd(int ac, char **av);
+
+/* List of commands */
+static const struct ngcmd *const cmds[] = {
+ &config_cmd,
+ &connect_cmd,
+ &debug_cmd,
+ &help_cmd,
+ &list_cmd,
+ &mkpeer_cmd,
+ &msg_cmd,
+ &name_cmd,
+ &read_cmd,
+ &rmhook_cmd,
+ &show_cmd,
+ &shutdown_cmd,
+ &status_cmd,
+ &types_cmd,
+ &write_cmd,
+ &quit_cmd,
+ NULL
+};
+
+/* Commands defined in this file */
+const struct ngcmd read_cmd = {
+ ReadCmd,
+ "read <filename>",
+ "Read and execute commands from a file",
+ NULL,
+ { "source", "." }
+};
+const struct ngcmd help_cmd = {
+ HelpCmd,
+ "help [command]",
+ "Show command summary or get more help on a specific command",
+ NULL,
+ { "?" }
+};
+const struct ngcmd quit_cmd = {
+ QuitCmd,
+ "quit",
+ "Exit program",
+ NULL,
+ { "exit" }
+};
+
+/* Our control and data sockets */
+int csock, dsock;
+
+/*
+ * main()
+ */
+int
+main(int ac, char *av[])
+{
+ char name[NG_NODELEN + 1];
+ int interactive = isatty(0) && isatty(1);
+ FILE *fp = NULL;
+ int ch, rtn = 0;
+
+ /* Set default node name */
+ snprintf(name, sizeof(name), "ngctl%d", getpid());
+
+ /* Parse command line */
+ while ((ch = getopt(ac, av, "df:n:")) != EOF) {
+ switch (ch) {
+ case 'd':
+ NgSetDebug(NgSetDebug(-1) + 1);
+ break;
+ case 'f':
+ if (strcmp(optarg, "-") == 0)
+ fp = stdin;
+ else if ((fp = fopen(optarg, "r")) == NULL)
+ err(EX_NOINPUT, "%s", optarg);
+ break;
+ case 'n':
+ snprintf(name, sizeof(name), "%s", optarg);
+ break;
+ case '?':
+ default:
+ Usage((char *)NULL);
+ break;
+ }
+ }
+ ac -= optind;
+ av += optind;
+
+ /* Create a new socket node */
+ if (NgMkSockNode(name, &csock, &dsock) < 0)
+ err(EX_OSERR, "can't create node");
+
+ /* Do commands as requested */
+ if (ac == 0) {
+ if (fp != NULL) {
+ rtn = ReadFile(fp);
+ } else if (interactive) {
+ rtn = DoInteractive();
+ } else
+ Usage("no command specified");
+ } else {
+ rtn = DoCommand(ac, av);
+ }
+
+ /* Convert command return code into system exit code */
+ switch (rtn) {
+ case CMDRTN_OK:
+ case CMDRTN_QUIT:
+ rtn = 0;
+ break;
+ case CMDRTN_USAGE:
+ rtn = EX_USAGE;
+ break;
+ case CMDRTN_ERROR:
+ rtn = EX_OSERR;
+ break;
+ }
+ return(rtn);
+}
+
+/*
+ * Process commands from a file
+ */
+static int
+ReadFile(FILE *fp)
+{
+ char line[LINE_MAX];
+ int num, rtn;
+
+ for (num = 1; fgets(line, sizeof(line), fp) != NULL; num++) {
+ if (*line == '#')
+ continue;
+ if ((rtn = DoParseCommand(line)) != 0) {
+ warnx("line %d: error in file", num);
+ return(rtn);
+ }
+ }
+ return(CMDRTN_OK);
+}
+
+/*
+ * Interactive mode
+ */
+static int
+DoInteractive(void)
+{
+ const int maxfd = MAX(csock, dsock) + 1;
+
+ (*help_cmd.func)(0, NULL);
+ while (1) {
+ struct timeval tv;
+ fd_set rfds;
+
+ /* See if any data or control messages are arriving */
+ FD_ZERO(&rfds);
+ FD_SET(csock, &rfds);
+ FD_SET(dsock, &rfds);
+ memset(&tv, 0, sizeof(tv));
+ if (select(maxfd, &rfds, NULL, NULL, &tv) <= 0) {
+
+ /* Issue prompt and wait for anything to happen */
+ printf("%s", PROMPT);
+ fflush(stdout);
+ FD_ZERO(&rfds);
+ FD_SET(0, &rfds);
+ FD_SET(csock, &rfds);
+ FD_SET(dsock, &rfds);
+ if (select(maxfd, &rfds, NULL, NULL, NULL) < 0)
+ err(EX_OSERR, "select");
+
+ /* If not user input, print a newline first */
+ if (!FD_ISSET(0, &rfds))
+ printf("\n");
+ }
+
+ /* Display any incoming control message */
+ if (FD_ISSET(csock, &rfds))
+ MsgRead();
+
+ /* Display any incoming data packet */
+ if (FD_ISSET(dsock, &rfds)) {
+ u_char buf[8192];
+ char hook[NG_HOOKLEN + 1];
+ int rl;
+
+ /* Read packet from socket */
+ if ((rl = NgRecvData(dsock,
+ buf, sizeof(buf), hook)) < 0)
+ err(EX_OSERR, "reading hook \"%s\"", hook);
+ if (rl == 0)
+ errx(EX_OSERR, "EOF from hook \"%s\"?", hook);
+
+ /* Write packet to stdout */
+ printf("Rec'd data packet on hook \"%s\":\n", hook);
+ DumpAscii(buf, rl);
+ }
+
+ /* Get any user input */
+ if (FD_ISSET(0, &rfds)) {
+ char buf[LINE_MAX];
+
+ if (fgets(buf, sizeof(buf), stdin) == NULL) {
+ printf("\n");
+ break;
+ }
+ if (DoParseCommand(buf) == CMDRTN_QUIT)
+ break;
+ }
+ }
+ return(CMDRTN_QUIT);
+}
+
+/*
+ * Parse a command line and execute the command
+ */
+static int
+DoParseCommand(char *line)
+{
+ char *av[MAX_ARGS];
+ int ac;
+
+ /* Parse line */
+ for (ac = 0, av[0] = strtok(line, WHITESPACE);
+ ac < MAX_ARGS - 1 && av[ac];
+ av[++ac] = strtok(NULL, WHITESPACE));
+
+ /* Do command */
+ return(DoCommand(ac, av));
+}
+
+/*
+ * Execute the command
+ */
+static int
+DoCommand(int ac, char **av)
+{
+ const struct ngcmd *cmd;
+ int rtn;
+
+ if (ac == 0 || *av[0] == 0)
+ return(CMDRTN_OK);
+ if ((cmd = FindCommand(av[0])) == NULL)
+ return(CMDRTN_ERROR);
+ if ((rtn = (*cmd->func)(ac, av)) == CMDRTN_USAGE)
+ warnx("usage: %s", cmd->cmd);
+ return(rtn);
+}
+
+/*
+ * Find a command
+ */
+static const struct ngcmd *
+FindCommand(const char *string)
+{
+ int k, found = -1;
+
+ for (k = 0; cmds[k] != NULL; k++) {
+ if (MatchCommand(cmds[k], string)) {
+ if (found != -1) {
+ warnx("\"%s\": ambiguous command", string);
+ return(NULL);
+ }
+ found = k;
+ }
+ }
+ if (found == -1) {
+ warnx("\"%s\": unknown command", string);
+ return(NULL);
+ }
+ return(cmds[found]);
+}
+
+/*
+ * See if string matches a prefix of "cmd" (or an alias) case insensitively
+ */
+static int
+MatchCommand(const struct ngcmd *cmd, const char *s)
+{
+ int a;
+
+ /* Try to match command, ignoring the usage stuff */
+ if (strlen(s) <= strcspn(cmd->cmd, WHITESPACE)) {
+ if (strncasecmp(s, cmd->cmd, strlen(s)) == 0)
+ return (1);
+ }
+
+ /* Try to match aliases */
+ for (a = 0; a < MAX_CMD_ALIAS && cmd->aliases[a] != NULL; a++) {
+ if (strlen(cmd->aliases[a]) >= strlen(s)) {
+ if (strncasecmp(s, cmd->aliases[a], strlen(s)) == 0)
+ return (1);
+ }
+ }
+
+ /* No match */
+ return (0);
+}
+
+/*
+ * ReadCmd()
+ */
+static int
+ReadCmd(int ac, char **av)
+{
+ FILE *fp;
+ int rtn;
+
+ /* Open file */
+ switch (ac) {
+ case 2:
+ if ((fp = fopen(av[1], "r")) == NULL) {
+ warn("%s", av[1]);
+ return(CMDRTN_ERROR);
+ }
+ break;
+ default:
+ return(CMDRTN_USAGE);
+ }
+
+ /* Process it */
+ rtn = ReadFile(fp);
+ fclose(fp);
+ return(rtn);
+}
+
+/*
+ * HelpCmd()
+ */
+static int
+HelpCmd(int ac, char **av)
+{
+ const struct ngcmd *cmd;
+ int k;
+
+ switch (ac) {
+ case 0:
+ case 1:
+ /* Show all commands */
+ printf("Available commands:\n");
+ for (k = 0; cmds[k] != NULL; k++) {
+ char *s, buf[100];
+
+ cmd = cmds[k];
+ snprintf(buf, sizeof(buf), "%s", cmd->cmd);
+ for (s = buf; *s != '\0' && !isspace(*s); s++);
+ *s = '\0';
+ printf(" %-10s %s\n", buf, cmd->desc);
+ }
+ return(CMDRTN_OK);
+ default:
+ /* Show help on a specific command */
+ if ((cmd = FindCommand(av[1])) != NULL) {
+ printf("usage: %s\n", cmd->cmd);
+ if (cmd->aliases[0] != NULL) {
+ int a = 0;
+
+ printf("Aliases: ");
+ while (1) {
+ printf("%s", cmd->aliases[a++]);
+ if (a == MAX_CMD_ALIAS
+ || cmd->aliases[a] == NULL) {
+ printf("\n");
+ break;
+ }
+ printf(", ");
+ }
+ }
+ printf("Summary: %s\n", cmd->desc);
+ if (cmd->help != NULL) {
+ const char *s;
+ char buf[65];
+ int tot, len, done;
+
+ printf("Description:\n");
+ for (s = cmd->help; *s != '\0'; s += len) {
+ while (isspace(*s))
+ s++;
+ tot = snprintf(buf,
+ sizeof(buf), "%s", s);
+ len = strlen(buf);
+ done = len == tot;
+ if (!done) {
+ while (len > 0
+ && !isspace(buf[len-1]))
+ buf[--len] = '\0';
+ }
+ printf(" %s\n", buf);
+ }
+ }
+ }
+ }
+ return(CMDRTN_OK);
+}
+
+/*
+ * QuitCmd()
+ */
+static int
+QuitCmd(int ac, char **av)
+{
+ return(CMDRTN_QUIT);
+}
+
+/*
+ * Dump data in hex and ASCII form
+ */
+void
+DumpAscii(const u_char *buf, int len)
+{
+ char ch, sbuf[100];
+ int k, count;
+
+ for (count = 0; count < len; count += DUMP_BYTES_PER_LINE) {
+ snprintf(sbuf, sizeof(sbuf), "%04x: ", count);
+ for (k = 0; k < DUMP_BYTES_PER_LINE; k++) {
+ if (count + k < len) {
+ snprintf(sbuf + strlen(sbuf),
+ sizeof(sbuf) - strlen(sbuf),
+ "%02x ", buf[count + k]);
+ } else {
+ snprintf(sbuf + strlen(sbuf),
+ sizeof(sbuf) - strlen(sbuf), " ");
+ }
+ }
+ snprintf(sbuf + strlen(sbuf), sizeof(sbuf) - strlen(sbuf), " ");
+ for (k = 0; k < DUMP_BYTES_PER_LINE; k++) {
+ if (count + k < len) {
+ ch = isprint(buf[count + k]) ?
+ buf[count + k] : '.';
+ snprintf(sbuf + strlen(sbuf),
+ sizeof(sbuf) - strlen(sbuf), "%c", ch);
+ } else {
+ snprintf(sbuf + strlen(sbuf),
+ sizeof(sbuf) - strlen(sbuf), " ");
+ }
+ }
+ printf("%s\n", sbuf);
+ }
+}
+
+/*
+ * Usage()
+ */
+static void
+Usage(const char *msg)
+{
+ if (msg)
+ warnx("%s", msg);
+ errx(EX_USAGE, "usage: ngctl [-d] [-f file] [-n name] [command ...]");
+}
+
diff --git a/usr.sbin/ngctl/mkpeer.c b/usr.sbin/ngctl/mkpeer.c
new file mode 100644
index 0000000..c5735e1
--- /dev/null
+++ b/usr.sbin/ngctl/mkpeer.c
@@ -0,0 +1,85 @@
+
+/*
+ * mkpeer.c
+ *
+ * Copyright (c) 1996-1999 Whistle Communications, Inc.
+ * All rights reserved.
+ *
+ * Subject to the following obligations and disclaimer of warranty, use and
+ * redistribution of this software, in source or object code forms, with or
+ * without modifications are expressly permitted by Whistle Communications;
+ * provided, however, that:
+ * 1. Any and all reproductions of the source or object code must include the
+ * copyright notice above and the following disclaimer of warranties; and
+ * 2. No rights are granted, in any manner or form, to use Whistle
+ * Communications, Inc. trademarks, including the mark "WHISTLE
+ * COMMUNICATIONS" on advertising, endorsements, or otherwise except as
+ * such appears in the above copyright notice or in the software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
+ * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
+ * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+ * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
+ * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
+ * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
+ * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
+ * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
+ * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "ngctl.h"
+
+static int MkPeerCmd(int ac, char **av);
+
+const struct ngcmd mkpeer_cmd = {
+ MkPeerCmd,
+ "mkpeer [path] <type> <hook> <peerhook>",
+ "Create and connect a new node to the node at \"path\"",
+ "The mkpeer command atomically creates a new node of type \"type\""
+ " and connects it to the node at \"path\". The hooks used for the"
+ " connection are \"hook\" on the original node and \"peerhook\""
+ " on the new node."
+ " If \"path\" is omitted then \".\" is assumed."
+};
+
+static int
+MkPeerCmd(int ac, char **av)
+{
+ struct ngm_mkpeer mkp;
+ char *path = ".";
+
+ /* Get arguments */
+ switch (ac) {
+ case 5:
+ path = av[1];
+ ac--;
+ av++;
+ /* FALLTHROUGH */
+ case 4:
+ snprintf(mkp.type, sizeof(mkp.type), "%s", av[1]);
+ snprintf(mkp.ourhook, sizeof(mkp.ourhook), "%s", av[2]);
+ snprintf(mkp.peerhook, sizeof(mkp.peerhook), "%s", av[3]);
+ break;
+ default:
+ return(CMDRTN_USAGE);
+ }
+
+ /* Send message */
+ if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE,
+ NGM_MKPEER, &mkp, sizeof(mkp)) < 0) {
+ warn("send msg");
+ return(CMDRTN_ERROR);
+ }
+ return(CMDRTN_OK);
+}
+
diff --git a/usr.sbin/ngctl/msg.c b/usr.sbin/ngctl/msg.c
new file mode 100644
index 0000000..4ae5171
--- /dev/null
+++ b/usr.sbin/ngctl/msg.c
@@ -0,0 +1,146 @@
+
+/*
+ * msg.c
+ *
+ * Copyright (c) 1999 Whistle Communications, Inc.
+ * All rights reserved.
+ *
+ * Subject to the following obligations and disclaimer of warranty, use and
+ * redistribution of this software, in source or object code forms, with or
+ * without modifications are expressly permitted by Whistle Communications;
+ * provided, however, that:
+ * 1. Any and all reproductions of the source or object code must include the
+ * copyright notice above and the following disclaimer of warranties; and
+ * 2. No rights are granted, in any manner or form, to use Whistle
+ * Communications, Inc. trademarks, including the mark "WHISTLE
+ * COMMUNICATIONS" on advertising, endorsements, or otherwise except as
+ * such appears in the above copyright notice or in the software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
+ * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
+ * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+ * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
+ * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
+ * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
+ * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
+ * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
+ * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * $Whistle: msg.c,v 1.2 1999/11/29 23:38:35 archie Exp $
+ * $FreeBSD$
+ */
+
+#include "ngctl.h"
+
+#define BUF_SIZE 4096
+
+static int MsgCmd(int ac, char **av);
+
+const struct ngcmd msg_cmd = {
+ MsgCmd,
+ "msg path command [args ... ]",
+ "Send a netgraph control message to the node at \"path\"",
+ "The msg command constructs a netgraph control message from the"
+ " command name and ASCII arguments (if any) and sends that message"
+ " to the node. It does this by first asking the node to convert"
+ " the ASCII message into binary format, and resending the result.",
+ { "cmd" }
+};
+
+static int
+MsgCmd(int ac, char **av)
+{
+ char buf[BUF_SIZE];
+ char *path, *cmdstr;
+ int i;
+
+ /* Get arguments */
+ if (ac < 3)
+ return(CMDRTN_USAGE);
+ path = av[1];
+ cmdstr = av[2];
+
+ /* Put command and arguments back together as one string */
+ for (*buf = '\0', i = 3; i < ac; i++) {
+ snprintf(buf + strlen(buf),
+ sizeof(buf) - strlen(buf), " %s", av[i]);
+ }
+
+ /* Send it */
+ if (NgSendAsciiMsg(csock, path, "%s%s", cmdstr, buf) < 0) {
+ warn("send msg");
+ return(CMDRTN_ERROR);
+ }
+
+ /* See if a synchronous reply awaits */
+ {
+ struct timeval tv;
+ fd_set rfds;
+
+ FD_ZERO(&rfds);
+ FD_SET(csock, &rfds);
+ memset(&tv, 0, sizeof(tv));
+ switch (select(csock + 1, &rfds, NULL, NULL, &tv)) {
+ case -1:
+ err(EX_OSERR, "select");
+ case 0:
+ break;
+ default:
+ MsgRead();
+ break;
+ }
+ }
+
+ /* Done */
+ return(CMDRTN_OK);
+}
+
+/*
+ * Read and display the next incoming control message
+ */
+void
+MsgRead()
+{
+ u_char buf[2 * sizeof(struct ng_mesg) + BUF_SIZE];
+ struct ng_mesg *const m = (struct ng_mesg *)buf;
+ struct ng_mesg *const ascii = (struct ng_mesg *)m->data;
+ char path[NG_PATHLEN+1];
+
+ /* Get incoming message (in binary form) */
+ if (NgRecvMsg(csock, m, sizeof(buf), path) < 0) {
+ warn("recv incoming message");
+ return;
+ }
+
+ /* Ask originating node to convert message to ASCII */
+ if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE,
+ NGM_BINARY2ASCII, m, sizeof(*m) + m->header.arglen) < 0
+ || NgRecvMsg(csock, m, sizeof(buf), NULL) < 0) {
+ printf("Rec'd %s %d from \"%s\":\n",
+ (m->header.flags & NGF_RESP) != 0 ? "response" : "command",
+ m->header.cmd, path);
+ if (m->header.arglen == 0)
+ printf("No arguments\n");
+ else
+ DumpAscii(m->data, m->header.arglen);
+ return;
+ }
+
+ /* Display message in ASCII form */
+ printf("Rec'd %s \"%s\" (%d) from \"%s\":\n",
+ (ascii->header.flags & NGF_RESP) != 0 ? "response" : "command",
+ ascii->header.cmdstr, ascii->header.cmd, path);
+ if (*ascii->data != '\0')
+ printf("Args:\t%s\n", ascii->data);
+ else
+ printf("No arguments\n");
+}
+
diff --git a/usr.sbin/ngctl/name.c b/usr.sbin/ngctl/name.c
new file mode 100644
index 0000000..85c6eff
--- /dev/null
+++ b/usr.sbin/ngctl/name.c
@@ -0,0 +1,75 @@
+
+/*
+ * name.c
+ *
+ * Copyright (c) 1996-1999 Whistle Communications, Inc.
+ * All rights reserved.
+ *
+ * Subject to the following obligations and disclaimer of warranty, use and
+ * redistribution of this software, in source or object code forms, with or
+ * without modifications are expressly permitted by Whistle Communications;
+ * provided, however, that:
+ * 1. Any and all reproductions of the source or object code must include the
+ * copyright notice above and the following disclaimer of warranties; and
+ * 2. No rights are granted, in any manner or form, to use Whistle
+ * Communications, Inc. trademarks, including the mark "WHISTLE
+ * COMMUNICATIONS" on advertising, endorsements, or otherwise except as
+ * such appears in the above copyright notice or in the software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
+ * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
+ * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+ * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
+ * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
+ * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
+ * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
+ * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
+ * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "ngctl.h"
+
+static int NameCmd(int ac, char **av);
+
+const struct ngcmd name_cmd = {
+ NameCmd,
+ "name <path> <name>",
+ "Assign name <name> to the node at <path>",
+ NULL
+};
+
+static int
+NameCmd(int ac, char **av)
+{
+ struct ngm_name name;
+ char *path;
+
+ /* Get arguments */
+ switch (ac) {
+ case 3:
+ path = av[1];
+ snprintf(name.name, sizeof(name.name), "%s", av[2]);
+ break;
+ default:
+ return(CMDRTN_USAGE);
+ }
+
+ /* Send message */
+ if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE,
+ NGM_NAME, &name, sizeof(name)) < 0) {
+ warn("send msg");
+ return(CMDRTN_ERROR);
+ }
+ return(CMDRTN_OK);
+}
+
diff --git a/usr.sbin/ngctl/ngctl.8 b/usr.sbin/ngctl/ngctl.8
new file mode 100644
index 0000000..ca9e25b
--- /dev/null
+++ b/usr.sbin/ngctl/ngctl.8
@@ -0,0 +1,139 @@
+.\" Copyright (c) 1996-1999 Whistle Communications, Inc.
+.\" All rights reserved.
+.\"
+.\" Subject to the following obligations and disclaimer of warranty, use and
+.\" redistribution of this software, in source or object code forms, with or
+.\" without modifications are expressly permitted by Whistle Communications;
+.\" provided, however, that:
+.\" 1. Any and all reproductions of the source or object code must include the
+.\" copyright notice above and the following disclaimer of warranties; and
+.\" 2. No rights are granted, in any manner or form, to use Whistle
+.\" Communications, Inc. trademarks, including the mark "WHISTLE
+.\" COMMUNICATIONS" on advertising, endorsements, or otherwise except as
+.\" such appears in the above copyright notice or in the software.
+.\"
+.\" THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
+.\" TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
+.\" REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
+.\" INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+.\" WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
+.\" REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
+.\" SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
+.\" IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
+.\" RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
+.\" WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+.\" PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
+.\" SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
+.\" OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\" $Whistle: ngctl.8,v 1.6 1999/01/20 03:19:44 archie Exp $
+.\"
+.Dd January 19, 1999
+.Dt NGCTL 8
+.Os
+.Sh NAME
+.Nm ngctl
+.Nd netgraph control utility
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Op Fl f Ar filename
+.Op Fl n Ar nodename
+.Op Ar command ...
+.Sh DESCRIPTION
+The
+.Nm
+utility creates a new netgraph node of type
+.Em socket
+which can be used to issue netgraph commands.
+If no
+.Fl f
+flag is given, no
+command is supplied on the command line, and standard input is a tty,
+.Nm
+will enter interactive mode.
+Otherwise
+.Nm
+will execute the supplied command(s) and exit immediately.
+.Pp
+Nodes can be created, removed, joined together, etc.
+.Tn ASCII
+formatted control messages can be sent to any node if that node
+supports binary/ASCII control message conversion.
+.Pp
+In interactive mode,
+.Nm
+will display any control messages and data packets received by the socket node.
+In the case of control messages, the message arguments are displayed in
+.Tn ASCII
+form if the originating node supports conversion.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl f Ar nodeinfo
+Read commands from the named file.
+A single dash represents the standard input.
+Blank lines and lines starting with a
+.Dq #
+are ignored.
+.It Fl n Ar nodename
+Assign
+.Em nodename
+to the newly created netgraph node.
+The default name is
+.Em ngctlXXX
+where XXX is the process ID number.
+.It Fl d
+Increase the debugging verbosity level.
+.El
+.Sh COMMANDS
+The currently supported commands in
+.Nm
+are:
+.Pp
+.Bd -literal -offset indent -compact
+connect Connects two nodes
+debug Get/set debugging verbosity level
+help Show command summary or get help on a command
+list Show information about all nodes
+mkpeer Create and connect a new node to an existing node
+msg Send an ASCII formatted message to a node
+name Assign a name to a node
+read Read and execute commands from a file
+rmhook Disconnect a node's hook
+show Show information about a node
+shutdown Shutdown a node
+status Get human readable status from a node
+types Show all installed node types
+write Send a data packet down the hook named by "hook".
+quit Exit program
+.Ed
+.Pp
+Some commands have aliases, e.g.,
+.Dq ls
+is the same as
+.Dq list .
+The
+.Dq help
+command displays the available
+commands, their usage and aliases, and a brief description.
+.Sh DIAGNOSTICS
+.Ex -std
+.Sh SEE ALSO
+.Xr netgraph 3 ,
+.Xr netgraph 4 ,
+.Xr nghook 8
+.Sh HISTORY
+The
+.Nm netgraph
+system was designed and first implemented at Whistle Communications, Inc. in
+a version
+.Fx 2.2
+customized for the Whistle InterJet.
+.Sh AUTHORS
+.An Archie Cobbs Aq archie@whistle.com
diff --git a/usr.sbin/ngctl/ngctl.h b/usr.sbin/ngctl/ngctl.h
new file mode 100644
index 0000000..86411f7
--- /dev/null
+++ b/usr.sbin/ngctl/ngctl.h
@@ -0,0 +1,101 @@
+
+/*
+ * ngctl.h
+ *
+ * Copyright (c) 1996-1999 Whistle Communications, Inc.
+ * All rights reserved.
+ *
+ * Subject to the following obligations and disclaimer of warranty, use and
+ * redistribution of this software, in source or object code forms, with or
+ * without modifications are expressly permitted by Whistle Communications;
+ * provided, however, that:
+ * 1. Any and all reproductions of the source or object code must include the
+ * copyright notice above and the following disclaimer of warranties; and
+ * 2. No rights are granted, in any manner or form, to use Whistle
+ * Communications, Inc. trademarks, including the mark "WHISTLE
+ * COMMUNICATIONS" on advertising, endorsements, or otherwise except as
+ * such appears in the above copyright notice or in the software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
+ * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
+ * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+ * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
+ * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
+ * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
+ * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
+ * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
+ * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sysexits.h>
+#include <limits.h>
+#include <ctype.h>
+#include <errno.h>
+#include <err.h>
+
+#include <netgraph.h>
+#include <netgraph/ng_socket.h>
+#include <netgraph/ng_message.h>
+
+#define MAX_CMD_ALIAS 8
+
+/* Command descriptors */
+struct ngcmd {
+ int (*func)(int ac, char **av); /* command function */
+ const char *cmd; /* command usage */
+ const char *desc; /* description */
+ const char *help; /* help text */
+ const char *aliases[MAX_CMD_ALIAS]; /* command aliases */
+};
+
+/* Command return values */
+#define CMDRTN_OK 0
+#define CMDRTN_USAGE 1
+#define CMDRTN_ERROR 2
+#define CMDRTN_QUIT 3
+
+/* Available commands */
+extern const struct ngcmd config_cmd;
+extern const struct ngcmd connect_cmd;
+extern const struct ngcmd debug_cmd;
+extern const struct ngcmd help_cmd;
+extern const struct ngcmd list_cmd;
+extern const struct ngcmd mkpeer_cmd;
+extern const struct ngcmd msg_cmd;
+extern const struct ngcmd name_cmd;
+extern const struct ngcmd read_cmd;
+extern const struct ngcmd rmhook_cmd;
+extern const struct ngcmd show_cmd;
+extern const struct ngcmd shutdown_cmd;
+extern const struct ngcmd status_cmd;
+extern const struct ngcmd types_cmd;
+extern const struct ngcmd write_cmd;
+extern const struct ngcmd quit_cmd;
+
+/* Data and control sockets */
+extern int csock, dsock;
+
+/* Misc functions */
+extern void MsgRead(void);
+extern void DumpAscii(const u_char *buf, int len);
+
diff --git a/usr.sbin/ngctl/rmhook.c b/usr.sbin/ngctl/rmhook.c
new file mode 100644
index 0000000..4af398e
--- /dev/null
+++ b/usr.sbin/ngctl/rmhook.c
@@ -0,0 +1,82 @@
+
+/*
+ * rmhook.c
+ *
+ * Copyright (c) 1996-1999 Whistle Communications, Inc.
+ * All rights reserved.
+ *
+ * Subject to the following obligations and disclaimer of warranty, use and
+ * redistribution of this software, in source or object code forms, with or
+ * without modifications are expressly permitted by Whistle Communications;
+ * provided, however, that:
+ * 1. Any and all reproductions of the source or object code must include the
+ * copyright notice above and the following disclaimer of warranties; and
+ * 2. No rights are granted, in any manner or form, to use Whistle
+ * Communications, Inc. trademarks, including the mark "WHISTLE
+ * COMMUNICATIONS" on advertising, endorsements, or otherwise except as
+ * such appears in the above copyright notice or in the software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
+ * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
+ * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+ * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
+ * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
+ * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
+ * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
+ * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
+ * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "ngctl.h"
+
+static int RmHookCmd(int ac, char **av);
+
+const struct ngcmd rmhook_cmd = {
+ RmHookCmd,
+ "rmhook [path] <hook>",
+ "Disconnect hook \"hook\" of the node at \"path\"",
+ "The rmhook command forces the node at \"path\" to break the link"
+ " formed by its hook \"hook\", if connected."
+ " If \"path\" is omitted then \".\" is assumed.",
+ { "disconnect" }
+};
+
+static int
+RmHookCmd(int ac, char **av)
+{
+ struct ngm_rmhook rmh;
+ char *path = ".";
+
+ /* Get arguments */
+ switch (ac) {
+ case 3:
+ path = av[1];
+ ac--;
+ av++;
+ /* FALLTHROUGH */
+ case 2:
+ snprintf(rmh.ourhook, sizeof(rmh.ourhook), "%s", av[1]);
+ break;
+ default:
+ return(CMDRTN_USAGE);
+ }
+
+ /* Send message */
+ if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE,
+ NGM_RMHOOK, &rmh, sizeof(rmh)) < 0) {
+ warn("send msg");
+ return(CMDRTN_ERROR);
+ }
+ return(CMDRTN_OK);
+}
+
diff --git a/usr.sbin/ngctl/show.c b/usr.sbin/ngctl/show.c
new file mode 100644
index 0000000..da51361
--- /dev/null
+++ b/usr.sbin/ngctl/show.c
@@ -0,0 +1,131 @@
+
+/*
+ * show.c
+ *
+ * Copyright (c) 1996-1999 Whistle Communications, Inc.
+ * All rights reserved.
+ *
+ * Subject to the following obligations and disclaimer of warranty, use and
+ * redistribution of this software, in source or object code forms, with or
+ * without modifications are expressly permitted by Whistle Communications;
+ * provided, however, that:
+ * 1. Any and all reproductions of the source or object code must include the
+ * copyright notice above and the following disclaimer of warranties; and
+ * 2. No rights are granted, in any manner or form, to use Whistle
+ * Communications, Inc. trademarks, including the mark "WHISTLE
+ * COMMUNICATIONS" on advertising, endorsements, or otherwise except as
+ * such appears in the above copyright notice or in the software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
+ * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
+ * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+ * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
+ * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
+ * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
+ * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
+ * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
+ * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "ngctl.h"
+
+#define FMT " %-15s %-15s %-12s %-15s %-15s\n"
+#define UNNAMED "<unnamed>"
+#define NOSTATUS "<no status>"
+
+static int ShowCmd(int ac, char **av);
+
+const struct ngcmd show_cmd = {
+ ShowCmd,
+ "show [-n] <path>",
+ "Show information about the node at <path>",
+ "If the -n flag is given, hooks are not listed.",
+ { "inquire", "info" }
+};
+
+static int
+ShowCmd(int ac, char **av)
+{
+ char *path;
+ u_char rbuf[16 * 1024];
+ struct ng_mesg *const resp = (struct ng_mesg *) rbuf;
+ struct hooklist *const hlist = (struct hooklist *) resp->data;
+ struct nodeinfo *const ninfo = &hlist->nodeinfo;
+ int ch, no_hooks = 0;
+
+ /* Get options */
+ optind = 1;
+ while ((ch = getopt(ac, av, "n")) != EOF) {
+ switch (ch) {
+ case 'n':
+ no_hooks = 1;
+ break;
+ case '?':
+ default:
+ return(CMDRTN_USAGE);
+ break;
+ }
+ }
+ ac -= optind;
+ av += optind;
+
+ /* Get arguments */
+ switch (ac) {
+ case 1:
+ path = av[0];
+ break;
+ default:
+ return(CMDRTN_USAGE);
+ }
+
+ /* Get node info and hook list */
+ if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE,
+ NGM_LISTHOOKS, NULL, 0) < 0) {
+ warn("send msg");
+ return(CMDRTN_ERROR);
+ }
+ if (NgRecvMsg(csock, resp, sizeof(rbuf), NULL) < 0) {
+ warn("recv msg");
+ return(CMDRTN_ERROR);
+ }
+
+ /* Show node information */
+ if (!*ninfo->name)
+ snprintf(ninfo->name, sizeof(ninfo->name), "%s", UNNAMED);
+ printf(" Name: %-15s Type: %-15s ID: %08x Num hooks: %d\n",
+ ninfo->name, ninfo->type, ninfo->id, ninfo->hooks);
+ if (!no_hooks && ninfo->hooks > 0) {
+ int k;
+
+ printf(FMT, "Local hook", "Peer name",
+ "Peer type", "Peer ID", "Peer hook");
+ printf(FMT, "----------", "---------",
+ "---------", "-------", "---------");
+ for (k = 0; k < ninfo->hooks; k++) {
+ struct linkinfo *const link = &hlist->link[k];
+ struct nodeinfo *const peer = &hlist->link[k].nodeinfo;
+ char idbuf[20];
+
+ if (!*peer->name) {
+ snprintf(peer->name, sizeof(peer->name),
+ "%s", UNNAMED);
+ }
+ snprintf(idbuf, sizeof(idbuf), "%08x", peer->id);
+ printf(FMT, link->ourhook, peer->name,
+ peer->type, idbuf, link->peerhook);
+ }
+ }
+ return(CMDRTN_OK);
+}
+
+
diff --git a/usr.sbin/ngctl/shutdown.c b/usr.sbin/ngctl/shutdown.c
new file mode 100644
index 0000000..d391ba8
--- /dev/null
+++ b/usr.sbin/ngctl/shutdown.c
@@ -0,0 +1,75 @@
+
+/*
+ * shutdown.c
+ *
+ * Copyright (c) 1996-1999 Whistle Communications, Inc.
+ * All rights reserved.
+ *
+ * Subject to the following obligations and disclaimer of warranty, use and
+ * redistribution of this software, in source or object code forms, with or
+ * without modifications are expressly permitted by Whistle Communications;
+ * provided, however, that:
+ * 1. Any and all reproductions of the source or object code must include the
+ * copyright notice above and the following disclaimer of warranties; and
+ * 2. No rights are granted, in any manner or form, to use Whistle
+ * Communications, Inc. trademarks, including the mark "WHISTLE
+ * COMMUNICATIONS" on advertising, endorsements, or otherwise except as
+ * such appears in the above copyright notice or in the software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
+ * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
+ * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+ * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
+ * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
+ * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
+ * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
+ * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
+ * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "ngctl.h"
+
+static int ShutdownCmd(int ac, char **av);
+
+const struct ngcmd shutdown_cmd = {
+ ShutdownCmd,
+ "shutdown <path>",
+ "Shutdown the node at <path>",
+ NULL,
+ { "kill", "rmnode" }
+};
+
+static int
+ShutdownCmd(int ac, char **av)
+{
+ char *path;
+
+ /* Get arguments */
+ switch (ac) {
+ case 2:
+ path = av[1];
+ break;
+ default:
+ return(CMDRTN_USAGE);
+ }
+
+ /* Shutdown node */
+ if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE,
+ NGM_SHUTDOWN, NULL, 0) < 0) {
+ warn("shutdown");
+ return(CMDRTN_ERROR);
+ }
+ return(CMDRTN_OK);
+}
+
+
diff --git a/usr.sbin/ngctl/status.c b/usr.sbin/ngctl/status.c
new file mode 100644
index 0000000..75706b3
--- /dev/null
+++ b/usr.sbin/ngctl/status.c
@@ -0,0 +1,95 @@
+
+/*
+ * status.c
+ *
+ * Copyright (c) 1996-1999 Whistle Communications, Inc.
+ * All rights reserved.
+ *
+ * Subject to the following obligations and disclaimer of warranty, use and
+ * redistribution of this software, in source or object code forms, with or
+ * without modifications are expressly permitted by Whistle Communications;
+ * provided, however, that:
+ * 1. Any and all reproductions of the source or object code must include the
+ * copyright notice above and the following disclaimer of warranties; and
+ * 2. No rights are granted, in any manner or form, to use Whistle
+ * Communications, Inc. trademarks, including the mark "WHISTLE
+ * COMMUNICATIONS" on advertising, endorsements, or otherwise except as
+ * such appears in the above copyright notice or in the software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
+ * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
+ * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+ * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
+ * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
+ * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
+ * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
+ * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
+ * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "ngctl.h"
+
+#define NOSTATUS "<no status>"
+
+static int StatusCmd(int ac, char **av);
+
+const struct ngcmd status_cmd = {
+ StatusCmd,
+ "status <path>",
+ "Get human readable status information from the node at <path>",
+ NULL
+};
+
+static int
+StatusCmd(int ac, char **av)
+{
+ u_char sbuf[sizeof(struct ng_mesg) + NG_TEXTRESPONSE];
+ struct ng_mesg *const resp = (struct ng_mesg *) sbuf;
+ char *const status = (char *) resp->data;
+ char *path;
+ int nostat = 0;
+
+ /* Get arguments */
+ switch (ac) {
+ case 2:
+ path = av[1];
+ break;
+ default:
+ return(CMDRTN_USAGE);
+ }
+
+ /* Get node status summary */
+ if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE,
+ NGM_TEXT_STATUS, NULL, 0) < 0) {
+ switch (errno) {
+ case EINVAL:
+ nostat = 1;
+ break;
+ default:
+ warn("send msg");
+ return(CMDRTN_ERROR);
+ }
+ } else {
+ if (NgRecvMsg(csock, resp, sizeof(sbuf), NULL) < 0
+ || (resp->header.flags & NGF_RESP) == 0)
+ nostat = 1;
+ }
+
+ /* Show it */
+ if (nostat)
+ printf("No status available for \"%s\"\n", path);
+ else
+ printf("Status for \"%s\":\n%s\n", path, status);
+ return(CMDRTN_OK);
+}
+
diff --git a/usr.sbin/ngctl/types.c b/usr.sbin/ngctl/types.c
new file mode 100644
index 0000000..472933e
--- /dev/null
+++ b/usr.sbin/ngctl/types.c
@@ -0,0 +1,92 @@
+
+/*
+ * types.c
+ *
+ * Copyright (c) 1996-1999 Whistle Communications, Inc.
+ * All rights reserved.
+ *
+ * Subject to the following obligations and disclaimer of warranty, use and
+ * redistribution of this software, in source or object code forms, with or
+ * without modifications are expressly permitted by Whistle Communications;
+ * provided, however, that:
+ * 1. Any and all reproductions of the source or object code must include the
+ * copyright notice above and the following disclaimer of warranties; and
+ * 2. No rights are granted, in any manner or form, to use Whistle
+ * Communications, Inc. trademarks, including the mark "WHISTLE
+ * COMMUNICATIONS" on advertising, endorsements, or otherwise except as
+ * such appears in the above copyright notice or in the software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
+ * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
+ * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+ * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
+ * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
+ * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
+ * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
+ * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
+ * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "ngctl.h"
+
+static int TypesCmd(int ac, char **av);
+
+const struct ngcmd types_cmd = {
+ TypesCmd,
+ "types",
+ "Show information about all installed node types",
+ NULL
+};
+
+static int
+TypesCmd(int ac, char **av)
+{
+ u_char rbuf[16 * 1024];
+ struct ng_mesg *const resp = (struct ng_mesg *) rbuf;
+ struct typelist *const tlist = (struct typelist *) resp->data;
+ int k, rtn = CMDRTN_OK;
+
+ /* Get arguments */
+ switch (ac) {
+ case 1:
+ break;
+ default:
+ return(CMDRTN_USAGE);
+ }
+
+ /* Get list of types */
+ if (NgSendMsg(csock, ".", NGM_GENERIC_COOKIE,
+ NGM_LISTTYPES, NULL, 0) < 0) {
+ warn("send msg");
+ return(CMDRTN_ERROR);
+ }
+ if (NgRecvMsg(csock, resp, sizeof(rbuf), NULL) < 0) {
+ warn("recv msg");
+ return(CMDRTN_ERROR);
+ }
+
+ /* Show each type */
+ printf("There are %d total types:\n", tlist->numtypes);
+ if (tlist->numtypes > 0) {
+ printf("%15s Number of living nodes\n", "Type name");
+ printf("%15s ----------------------\n", "---------");
+ }
+ for (k = 0; k < tlist->numtypes; k++) {
+ struct typeinfo *const ti = &tlist->typeinfo[k];
+ printf("%15s %5d\n", ti->type_name, ti->numnodes);
+ }
+
+ /* Done */
+ return (rtn);
+}
+
diff --git a/usr.sbin/ngctl/write.c b/usr.sbin/ngctl/write.c
new file mode 100644
index 0000000..d1bdca0
--- /dev/null
+++ b/usr.sbin/ngctl/write.c
@@ -0,0 +1,111 @@
+
+/*
+ * write.c
+ *
+ * Copyright (c) 2002 Archie L. Cobbs
+ * All rights reserved.
+ *
+ * Subject to the following obligations and disclaimer of warranty, use and
+ * redistribution of this software, in source or object code forms, with or
+ * without modifications are expressly permitted by Archie L. Cobbs;
+ * provided, however, that:
+ * 1. Any and all reproductions of the source or object code must include the
+ * copyright notice above and the following disclaimer of warranties
+ *
+ * THIS SOFTWARE IS BEING PROVIDED BY ARCHIE L. COBBS AS IS", AND TO
+ * THE MAXIMUM EXTENT PERMITTED BY LAW, ARCHIE L. COBBS MAKES NO
+ * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
+ * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+ * ARCHIE L. COBBS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
+ * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
+ * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
+ * IN NO EVENT SHALL ARCHIE L. COBBS BE LIABLE FOR ANY DAMAGES
+ * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
+ * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ARCHIE L. COBBS IS ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "ngctl.h"
+
+#define BUF_SIZE 8192
+
+static int WriteCmd(int ac, char **av);
+
+const struct ngcmd write_cmd = {
+ WriteCmd,
+ "write hook < -f file | byte ... >",
+ "Send a data packet down the hook named by \"hook\".",
+ "The data may be contained in a file, or may be described directly"
+ " on the command line by supplying a sequence of bytes.",
+ { "w" }
+};
+
+static int
+WriteCmd(int ac, char **av)
+{
+ u_int32_t sagbuf[64];
+ struct sockaddr_ng *sag = (struct sockaddr_ng *)sagbuf;
+ u_char buf[BUF_SIZE];
+ const char *hook;
+ FILE *fp;
+ u_int len;
+ int byte;
+ int i;
+
+ /* Get arguments */
+ if (ac < 3)
+ return(CMDRTN_USAGE);
+ hook = av[1];
+
+ /* Get data */
+ if (strcmp(av[2], "-f") == 0) {
+ if (ac != 4)
+ return(CMDRTN_USAGE);
+ if ((fp = fopen(av[3], "r")) == NULL) {
+ warn("can't read file \"%s\"", av[3]);
+ return(CMDRTN_ERROR);
+ }
+ if ((len = fread(buf, 1, sizeof(buf), fp)) == 0) {
+ if (ferror(fp))
+ warn("can't read file \"%s\"", av[3]);
+ else
+ warnx("file \"%s\" is empty", av[3]);
+ fclose(fp);
+ return(CMDRTN_ERROR);
+ }
+ fclose(fp);
+ } else {
+ for (i = 2, len = 0; i < ac && len < sizeof(buf); i++, len++) {
+ if (sscanf(av[i], "%i", &byte) != 1
+ || (byte < -128 || byte > 255)) {
+ warnx("invalid byte \"%s\"", av[i]);
+ return(CMDRTN_ERROR);
+ }
+ buf[len] = (u_char)byte;
+ }
+ if (len == 0)
+ return(CMDRTN_USAGE);
+ }
+
+ /* Send data */
+ sag->sg_len = 3 + strlen(hook);
+ sag->sg_family = AF_NETGRAPH;
+ strlcpy(sag->sg_data, hook, sizeof(sagbuf) - 2);
+ if (sendto(dsock, buf, len,
+ 0, (struct sockaddr *)sag, sag->sg_len) == -1) {
+ warn("writing to hook \"%s\"", hook);
+ return(CMDRTN_ERROR);
+ }
+
+ /* Done */
+ return(CMDRTN_OK);
+}
+
diff --git a/usr.sbin/nghook/Makefile b/usr.sbin/nghook/Makefile
new file mode 100644
index 0000000..e427f26
--- /dev/null
+++ b/usr.sbin/nghook/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+# $Whistle: Makefile,v 1.4 1999/01/16 04:44:33 archie Exp $
+
+PROG= nghook
+MAN= nghook.8
+SRCS= main.c
+
+DPADD= ${LIBNETGRAPH}
+LDADD= -lnetgraph
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/nghook/main.c b/usr.sbin/nghook/main.c
new file mode 100644
index 0000000..c5fd205
--- /dev/null
+++ b/usr.sbin/nghook/main.c
@@ -0,0 +1,238 @@
+
+/*
+ * main.c
+ *
+ * Copyright (c) 1996-1999 Whistle Communications, Inc.
+ * All rights reserved.
+ *
+ * Subject to the following obligations and disclaimer of warranty, use and
+ * redistribution of this software, in source or object code forms, with or
+ * without modifications are expressly permitted by Whistle Communications;
+ * provided, however, that:
+ * 1. Any and all reproductions of the source or object code must include the
+ * copyright notice above and the following disclaimer of warranties; and
+ * 2. No rights are granted, in any manner or form, to use Whistle
+ * Communications, Inc. trademarks, including the mark "WHISTLE
+ * COMMUNICATIONS" on advertising, endorsements, or otherwise except as
+ * such appears in the above copyright notice or in the software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
+ * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
+ * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+ * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
+ * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
+ * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
+ * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
+ * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
+ * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ * $Whistle: main.c,v 1.9 1999/01/20 00:26:26 archie Exp $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sysexits.h>
+#include <errno.h>
+#include <err.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+
+#include <netgraph.h>
+
+#define DEFAULT_HOOKNAME "debug"
+#define NG_SOCK_HOOK_NAME "hook"
+
+#define BUF_SIZE (64 * 1024)
+
+static void WriteAscii(u_char * buf, int len);
+static void Usage(void);
+
+/*
+ * main()
+ */
+int
+main(int ac, char *av[])
+{
+ struct ngm_connect ngc;
+ char *path = NULL, *hook = DEFAULT_HOOKNAME;
+ int csock, dsock;
+ int asciiFlag = 0;
+ int loopFlag = 0;
+ int noInput = 0;
+ int ch;
+
+ /* Parse flags */
+ while ((ch = getopt(ac, av, "adln")) != EOF) {
+ switch (ch) {
+ case 'a':
+ asciiFlag = 1;
+ break;
+ case 'd':
+ NgSetDebug(NgSetDebug(-1) + 1);
+ break;
+ case 'l':
+ loopFlag = 1;
+ break;
+ case 'n':
+ noInput = 1;
+ break;
+ case '?':
+ default:
+ Usage();
+ }
+ }
+ ac -= optind;
+ av += optind;
+
+ /* Get params */
+ switch (ac) {
+ case 2:
+ hook = av[1];
+ /* FALLTHROUGH */
+ case 1:
+ path = av[0];
+ break;
+ default:
+ Usage();
+ }
+
+ /* Get sockets */
+ if (NgMkSockNode(NULL, &csock, &dsock) < 0)
+ errx(EX_OSERR, "can't get sockets");
+
+ /* Connect socket node to specified node */
+ snprintf(ngc.path, sizeof(ngc.path), "%s", path);
+ snprintf(ngc.ourhook, sizeof(ngc.ourhook), NG_SOCK_HOOK_NAME);
+ snprintf(ngc.peerhook, sizeof(ngc.peerhook), "%s", hook);
+
+ if (NgSendMsg(csock, ".",
+ NGM_GENERIC_COOKIE, NGM_CONNECT, &ngc, sizeof(ngc)) < 0)
+ errx(EX_OSERR, "can't connect to node");
+
+ /* Close standard input if not reading from it */
+ if (noInput)
+ fclose(stdin);
+
+ /* Relay data */
+ while (1) {
+ fd_set rfds;
+
+ /* Setup bits */
+ FD_ZERO(&rfds);
+ if (!noInput)
+ FD_SET(0, &rfds);
+ FD_SET(dsock, &rfds);
+
+ /* Wait for something to happen */
+ if (select(FD_SETSIZE, &rfds, NULL, NULL, NULL) < 0)
+ err(EX_OSERR, "select");
+
+ /* Check data from socket */
+ if (FD_ISSET(dsock, &rfds)) {
+ char buf[BUF_SIZE];
+ int rl, wl;
+
+ /* Read packet from socket */
+ if ((rl = NgRecvData(dsock,
+ buf, sizeof(buf), NULL)) < 0)
+ err(EX_OSERR, "read(hook)");
+ if (rl == 0)
+ errx(EX_OSERR, "read EOF from hook?!");
+
+ /* Write packet to stdout */
+ if (asciiFlag)
+ WriteAscii((u_char *) buf, rl);
+ else if ((wl = write(STDOUT_FILENO, buf, rl)) != rl) {
+ if (wl < 0) {
+ err(EX_OSERR, "write(stdout)");
+ } else {
+ errx(EX_OSERR,
+ "stdout: read %d, wrote %d",
+ rl, wl);
+ }
+ }
+ /* Loopback */
+ if (loopFlag) {
+ if (NgSendData(dsock, NG_SOCK_HOOK_NAME, buf, rl) < 0)
+ err(EX_OSERR, "write(hook)");
+ }
+ }
+
+ /* Check data from stdin */
+ if (FD_ISSET(0, &rfds)) {
+ char buf[BUF_SIZE];
+ int rl;
+
+ /* Read packet from stdin */
+ if ((rl = read(0, buf, sizeof(buf))) < 0)
+ err(EX_OSERR, "read(stdin)");
+ if (rl == 0)
+ errx(EX_OSERR, "EOF(stdin)");
+
+ /* Write packet to socket */
+ if (NgSendData(dsock, NG_SOCK_HOOK_NAME, buf, rl) < 0)
+ err(EX_OSERR, "write(hook)");
+ }
+ }
+}
+
+/*
+ * Dump data in hex and ASCII form
+ */
+static void
+WriteAscii(u_char *buf, int len)
+{
+ char ch, sbuf[100];
+ int k, count;
+
+ for (count = 0; count < len; count += 16) {
+ snprintf(sbuf, sizeof(sbuf), "%04x: ", count);
+ for (k = 0; k < 16; k++)
+ if (count + k < len)
+ snprintf(sbuf + strlen(sbuf),
+ sizeof(sbuf) - strlen(sbuf),
+ "%02x ", buf[count + k]);
+ else
+ snprintf(sbuf + strlen(sbuf),
+ sizeof(sbuf) - strlen(sbuf), " ");
+ snprintf(sbuf + strlen(sbuf), sizeof(sbuf) - strlen(sbuf), " ");
+ for (k = 0; k < 16; k++)
+ if (count + k < len) {
+ ch = isprint(buf[count + k]) ?
+ buf[count + k] : '.';
+ snprintf(sbuf + strlen(sbuf),
+ sizeof(sbuf) - strlen(sbuf), "%c", ch);
+ } else
+ snprintf(sbuf + strlen(sbuf),
+ sizeof(sbuf) - strlen(sbuf), " ");
+ snprintf(sbuf + strlen(sbuf),
+ sizeof(sbuf) - strlen(sbuf), "\n");
+ (void) write(STDOUT_FILENO, sbuf, strlen(sbuf));
+ }
+ ch = '\n';
+ write(1, &ch, 1);
+}
+
+/*
+ * Display usage and exit
+ */
+static void
+Usage(void)
+{
+ errx(EX_USAGE, "usage: nghook [-adln] path [hookname]");
+}
+
diff --git a/usr.sbin/nghook/nghook.8 b/usr.sbin/nghook/nghook.8
new file mode 100644
index 0000000..48a0e51
--- /dev/null
+++ b/usr.sbin/nghook/nghook.8
@@ -0,0 +1,106 @@
+.\" Copyright (c) 1996-1999 Whistle Communications, Inc.
+.\" All rights reserved.
+.\"
+.\" Subject to the following obligations and disclaimer of warranty, use and
+.\" redistribution of this software, in source or object code forms, with or
+.\" without modifications are expressly permitted by Whistle Communications;
+.\" provided, however, that:
+.\" 1. Any and all reproductions of the source or object code must include the
+.\" copyright notice above and the following disclaimer of warranties; and
+.\" 2. No rights are granted, in any manner or form, to use Whistle
+.\" Communications, Inc. trademarks, including the mark "WHISTLE
+.\" COMMUNICATIONS" on advertising, endorsements, or otherwise except as
+.\" such appears in the above copyright notice or in the software.
+.\"
+.\" THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
+.\" TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
+.\" REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
+.\" INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+.\" WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
+.\" REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
+.\" SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
+.\" IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
+.\" RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
+.\" WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+.\" PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
+.\" SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
+.\" OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\" $Whistle: nghook.8,v 1.4 1999/01/20 03:19:45 archie Exp $
+.\"
+.Dd January 19, 1999
+.Dt NGHOOK 8
+.Os
+.Sh NAME
+.Nm nghook
+.Nd connect to a
+.Xr netgraph 4
+node
+.Sh SYNOPSIS
+.Nm
+.Op Fl a
+.Op Fl d
+.Op Fl l
+.Op Fl n
+.Ar path
+.Op Ar hookname
+.Sh DESCRIPTION
+.Nm
+creates a
+.Xr ng_socket 4
+socket type node and connects it to hook
+.Ar hookname
+of the node found at
+.Ar path .
+If
+.Ar hookname
+is omitted,
+.Dq debug
+is assumed.
+.Pp
+At this point all data written to standard input is sent
+to the node and all data received from the node is relayed
+to standard output.
+.Nm
+exits when
+.Dv EOF
+is detected on standard input.
+.Pp
+The options are as follows:
+.Pp
+.Bl -tag -width indent
+.It Fl a
+Output each packet read in human-readable decoded
+.Tn ASCII
+form instead of raw binary.
+.It Fl d
+Increase the debugging verbosity level.
+.It Fl l
+Loops all received data back to the hook in addition to writing it
+to standard output.
+.It Fl n
+Don't attempt to read any data from standard input.
+.Nm
+will continue reading from the node until stopped by a signal.
+.El
+.Sh BUGS
+Although all input is read in unbuffered mode,
+there's no way to control the packetization of the input.
+.Sh SEE ALSO
+.Xr netgraph 3 ,
+.Xr netgraph 4 ,
+.Xr ngctl 8
+.Sh HISTORY
+The
+.Em netgraph
+system was designed and first implemented at Whistle Communications, Inc.\&
+in a version of
+.Fx 2.2
+customized for the Whistle InterJet.
+.Sh AUTHORS
+.An Archie Cobbs Aq archie@whistle.com
diff --git a/usr.sbin/nologin/Makefile b/usr.sbin/nologin/Makefile
new file mode 100644
index 0000000..b1611c0
--- /dev/null
+++ b/usr.sbin/nologin/Makefile
@@ -0,0 +1,7 @@
+# @(#)Makefile 8.2 (Berkeley) 4/22/94
+# $FreeBSD$
+
+SCRIPTS=nologin.sh
+MAN= nologin.5 nologin.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/nologin/nologin.5 b/usr.sbin/nologin/nologin.5
new file mode 100644
index 0000000..9d63c88
--- /dev/null
+++ b/usr.sbin/nologin/nologin.5
@@ -0,0 +1,72 @@
+.\" Copyright (c) 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)nologin.8 8.1 (Berkeley) 6/19/93
+.\" $FreeBSD$
+.\"
+.Dd June 19, 1993
+.Dt NOLOGIN 5
+.Os
+.Sh NAME
+.Nm nologin
+.Nd disallow logins
+.Sh DESCRIPTION
+Programs such as
+.Xr login 1
+disallow logins if the file
+.Pa /var/run/nologin
+exists.
+Programs display the contents of
+.Pa /var/run/nologin
+to the user and exit.
+This makes it simple to temporarily prevent incoming logins systemwide.
+.Pp
+To disable logins on a per-account basis,
+investigate
+.Xr nologin 8 .
+.Sh SECURITY
+Ignored by
+.Xr login 1
+for user root.
+.Sh FILES
+.Bl -tag -width /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..7f8f9ff
--- /dev/null
+++ b/usr.sbin/nologin/nologin.8
@@ -0,0 +1,61 @@
+.\" Copyright (c) 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)nologin.8 8.1 (Berkeley) 6/19/93
+.\" $FreeBSD$
+.\"
+.Dd June 19, 1993
+.Dt NOLOGIN 8
+.Os
+.Sh NAME
+.Nm nologin
+.Nd politely refuse a login
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+The
+.Nm
+utility displays a message that an account is not available and
+exits non-zero.
+It is intended as a replacement shell field for accounts that
+have been disabled.
+.Pp
+To disable all logins,
+investigate
+.Xr nologin 5 .
+.Sh SEE ALSO
+.Xr login 1 ,
+.Xr nologin 5
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.4 .
diff --git a/usr.sbin/nologin/nologin.sh b/usr.sbin/nologin/nologin.sh
new file mode 100644
index 0000000..52279c1
--- /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
+# $FreeBSD$
+#
+
+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..0ed4fc2
--- /dev/null
+++ b/usr.sbin/nslookup/Makefile
@@ -0,0 +1,22 @@
+# $FreeBSD$
+
+USE_LIBBIND= yes
+.include "${.CURDIR}/../named/Makefile.inc"
+
+.PATH: ${BIND_DIR}/bin/nslookup
+.PATH: ${BIND_DIR}/doc/man
+
+PROG= nslookup
+MAN= nslookup.8
+SRCS= main.c getinfo.c debug.c send.c skip.c list.c subr.c commands.l
+
+CFLAGS+= -D_PATH_HELPFILE=\"${DESTHELP}/nslookup.help\"
+CFLAGS+= -I${.CURDIR}/../../contrib/bind/bin/nslookup
+
+LDADD+= -ll -ledit -ltermcap
+DPADD+= ${LIBL} ${LIBEDIT} ${LIBTERMCAP}
+
+FILES= nslookup.help
+FILESDIR= ${DESTHELP}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/nsupdate/Makefile b/usr.sbin/nsupdate/Makefile
new file mode 100644
index 0000000..16697cb
--- /dev/null
+++ b/usr.sbin/nsupdate/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+USE_LIBBIND= yes
+.include "${.CURDIR}/../named/Makefile.inc"
+
+.PATH: ${BIND_DIR}/bin/nsupdate
+
+PROG= nsupdate
+NOMAN= hmm..
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ntp/Makefile b/usr.sbin/ntp/Makefile
new file mode 100644
index 0000000..15fad6c
--- /dev/null
+++ b/usr.sbin/ntp/Makefile
@@ -0,0 +1,9 @@
+# Makefile for ntpd.
+# $FreeBSD$
+
+MAINTAINER= roberto
+
+SUBDIR= doc libntp libparse ntpd ntpdc ntpq ntpdate ntptrace \
+ ntptimeset ntptime ntp-genkeys
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/ntp/Makefile.inc b/usr.sbin/ntp/Makefile.inc
new file mode 100644
index 0000000..8b8a199
--- /dev/null
+++ b/usr.sbin/ntp/Makefile.inc
@@ -0,0 +1,25 @@
+# $FreeBSD$
+
+MAINTAINER= roberto
+
+DEFS_LOCAL= -DPARSE -DHAVE_CONFIG_H
+NTPDEFS= -DSYS_FREEBSD
+CLOCKDEFS=
+# -DLOCAL_CLOCK -DPST -DWWVB -DAS2201 -DGOES -DGPSTM -DOMEGA \
+# -DLEITCH -DTRAK -DACTS -DATOM -DDATUM -DHEATH -DMSFEES \
+# -DMX4200 -DNMEA -DBOEDER
+CFLAGS+= ${NTPDEFS} ${DEFS_LOCAL} ${CLOCKDEFS}
+
+.if exists(${.OBJDIR}/../libparse)
+LIBPARSE= ${.OBJDIR}/../libparse/libparse.a
+.else
+LIBPARSE= ${.CURDIR}/../libparse/libparse.a
+.endif
+
+.if exists(${.OBJDIR}/../libntp)
+LIBNTP= ${.OBJDIR}/../libntp/libntp.a
+.else
+LIBNTP= ${.CURDIR}/../libntp/libntp.a
+.endif
+
+.include "../Makefile.inc"
diff --git a/usr.sbin/ntp/config.h b/usr.sbin/ntp/config.h
new file mode 100644
index 0000000..fb50848
--- /dev/null
+++ b/usr.sbin/ntp/config.h
@@ -0,0 +1,994 @@
+/* config.h. Generated automatically by configure. */
+/* config.h.in. Generated automatically from configure.in by autoheader. */
+/* $FreeBSD$ */
+/* debugging code */
+#define DEBUG 1
+
+/* Minutes per DST adjustment */
+#define DSTMINUTES 60
+
+/* MD5 authentication */
+#define MD5 1
+
+/* DES authentication (COCOM only) */
+/* #undef DES */
+
+/* time_t */
+/* #undef time_t */
+
+/* reference clock interface */
+#define REFCLOCK 1
+
+/* Audio CHU? */
+/* #undef AUDIO_CHU */
+
+/* PARSE kernel PLL PPS support */
+#define PPS_SYNC 1
+
+/* ACTS modem service */
+/* #undef CLOCK_ACTS */
+
+/* Arbiter 1088A/B GPS receiver */
+/* #undef CLOCK_ARBITER */
+
+/* DHD19970505: ARCRON support. */
+/* #undef CLOCK_ARCRON_MSF */
+
+/* Austron 2200A/2201A GPS receiver */
+/* #undef CLOCK_AS2201 */
+
+/* PPS interface */
+#define CLOCK_ATOM 1
+
+/* PPS auxiliary interface for ATOM */
+#define PPS_SAMPLE 1
+
+/* Datum/Bancomm bc635/VME interface */
+/* #undef CLOCK_BANC */
+
+/* Diems Computime Radio Clock */
+/* #undef CLOCK_COMPUTIME */
+
+/* Chronolog K-series WWVB receiver */
+/* #undef CLOCK_CHRONOLOG */
+
+/* Datum Programmable Time System */
+/* #undef CLOCK_DATUM */
+
+/* ELV/DCF7000 clock */
+/* #undef CLOCK_DCF7000 */
+
+/* Dumb generic hh:mm:ss local clock */
+#define CLOCK_DUMBCLOCK 1
+
+/* Forum Graphic GPS datating station driver */
+/* #undef CLOCK_FG */
+
+/* TrueTime GPS receiver/VME interface */
+/* #undef CLOCK_GPSVME */
+
+/* Heath GC-1000 WWV/WWVH receiver */
+/* #undef CLOCK_HEATH */
+
+/* HOPF 6021 clock */
+/* #undef CLOCK_HOPF6021 */
+
+/* HOPF PCI clock device */
+/* #undef CLOCK_HOPF_PCI */
+
+/* HOPF serial clock device*/
+/* #undef CLOCK_HOPF_SERIAL */
+
+/* HP 58503A GPS receiver */
+/* #undef CLOCK_HPGPS */
+
+/* Sun IRIG audio decoder */
+/* #undef CLOCK_IRIG */
+
+/* Rockwell Jupiter GPS clock */
+/* #undef CLOCK_JUPITER */
+
+/* Leitch CSD 5300 Master Clock System Driver */
+/* #undef CLOCK_LEITCH */
+
+/* local clock reference */
+#define CLOCK_LOCAL 1
+
+/* Meinberg clocks */
+/* #undef CLOCK_MEINBERG */
+
+/* EES M201 MSF receiver */
+/* #undef CLOCK_MSFEES */
+
+/* Magnavox MX4200 GPS receiver */
+/* #undef CLOCK_MX4200 */
+
+/* NMEA GPS receiver */
+#define CLOCK_NMEA 1
+
+/* Motorola UT Oncore GPS */
+#define CLOCK_ONCORE 1
+
+/* Palisade clock */
+/* #undef CLOCK_PALISADE */
+
+/* PARSE driver interface */
+#define CLOCK_PARSE 1
+
+/* Conrad parallel port radio clock */
+/* #undef CLOCK_PCF */
+
+/* PCL 720 clock support */
+/* #undef CLOCK_PPS720 */
+
+/* PST/Traconex 1020 WWV/WWVH receiver */
+/* #undef CLOCK_PST */
+
+/* PTB modem service */
+/* #undef CLOCK_PTBACTS */
+
+/* DCF77 raw time code */
+#define CLOCK_RAWDCF 1
+
+/* RCC 8000 clock */
+/* #undef CLOCK_RCC8000 */
+
+/* Schmid DCF77 clock */
+/* #undef CLOCK_SCHMID */
+
+/* clock thru shared memory */
+/* #undef CLOCK_SHM */
+
+/* Spectracom 8170/Netclock/2 WWVB receiver */
+/* #undef CLOCK_SPECTRACOM */
+
+/* KSI/Odetics TPRO/S GPS receiver/IRIG interface */
+/* #undef CLOCK_TPRO */
+
+/* TRAK 8810 GPS receiver */
+/* #undef CLOCK_TRAK */
+
+/* Trimble GPS receiver/TAIP protocol */
+/* #undef CLOCK_TRIMTAIP */
+
+/* Trimble GPS receiver/TSIP protocol */
+/* #undef CLOCK_TRIMTSIP */
+
+/* Kinemetrics/TrueTime receivers */
+/* #undef CLOCK_TRUETIME */
+
+/* Ultralink M320 WWVB receiver */
+/* #undef CLOCK_ULINK */
+
+/* USNO modem service */
+/* #undef CLOCK_USNO */
+
+/* WHARTON 400A Series protocol */
+/* #undef CLOCK_WHARTON_400A */
+
+/* WWV audio driver */
+/* #undef CLOCK_WWV */
+
+/* VARITEXT protocol */
+/* #undef CLOCK_VARITEXT */
+
+/* define if we need to declare int errno; */
+/* #undef DECL_ERRNO */
+
+/* define if we may declare int h_errno; */
+#define DECL_H_ERRNO 1
+
+/* define if it's OK to declare char *sys_errlist[]; */
+/* #undef CHAR_SYS_ERRLIST */
+
+/* define if it's OK to declare int syscall P((int, struct timeval *, struct timeval *)); */
+#define DECL_SYSCALL 1
+
+/* define if we have syscall is buggy (Solaris 2.4) */
+/* #undef SYSCALL_BUG */
+
+/* Do we need extra room for SO_RCVBUF? (HPUX <8) */
+/* #undef NEED_RCVBUF_SLOP */
+
+/* Should we open the broadcast socket? */
+#define OPEN_BCAST_SOCKET 1
+
+/* Do we want the HPUX FindConfig()? */
+/* #undef NEED_HPUX_FINDCONFIG */
+
+/* canonical system (cpu-vendor-os) string */
+#ifdef __alpha__
+#define STR_SYSTEM "alpha-unknown-freebsd"
+#else
+#define STR_SYSTEM "i386-unknown-freebsd"
+#endif
+
+/* define if NetInfo support is available */
+/* #undef HAVE_NETINFO */
+
+/* define if [gs]ettimeofday() only takes 1 argument */
+/* #undef SYSV_TIMEOFDAY */
+
+/* define if struct sockaddr has sa_len */
+#define HAVE_SA_LEN_IN_STRUCT_SOCKADDR 1
+
+/* define if struct clockinfo has hz */
+#define HAVE_HZ_IN_STRUCT_CLOCKINFO 1
+
+/* define if struct sigaction has sa_sigaction */
+#define HAVE_SA_SIGACTION_IN_STRUCT_SIGACTION 1
+
+/* define if struct clockinfo has tickadj */
+#define HAVE_TICKADJ_IN_STRUCT_CLOCKINFO 1
+
+/* define if struct ntptimeval uses time.tv_nsec instead of time.tv_usec */
+/* #undef HAVE_TV_NSEC_IN_NTPTIMEVAL */
+
+/* Does a system header defind struct ppsclockev? */
+/* #undef HAVE_STRUCT_PPSCLOCKEV */
+
+/* define if function prototypes are OK */
+#define HAVE_PROTOTYPES 1
+
+/* define if setpgrp takes 0 arguments */
+/* #undef HAVE_SETPGRP_0 */
+
+/* hardwire a value for tick? */
+#define PRESET_TICK 1000000L/hz
+
+/* hardwire a value for tickadj? */
+#define PRESET_TICKADJ 500/hz
+
+/* is adjtime() accurate? */
+/* #undef ADJTIME_IS_ACCURATE */
+
+/* should we NOT read /dev/kmem? */
+/* #undef NOKMEM */
+
+/* use UDP Wildcard Delivery? */
+#define UDP_WILDCARD_DELIVERY 1
+
+/* always slew the clock? */
+/* #undef SLEWALWAYS */
+
+/* step, then slew the clock? */
+/* #undef STEP_SLEW */
+
+/* force ntpdate to step the clock if !defined(STEP_SLEW) ? */
+/* #undef FORCE_NTPDATE_STEP */
+
+/* synch TODR hourly? */
+/* #undef DOSYNCTODR */
+
+/* do we set process groups with -pid? */
+/* #undef UDP_BACKWARDS_SETOWN */
+
+/* must we have a CTTY for fsetown? */
+#define USE_FSETOWNCTTY 1
+
+/* can we use SIGIO for tcp and udp IO? */
+/* #undef HAVE_SIGNALED_IO */
+
+/* can we use SIGPOLL for UDP? */
+/* #undef USE_UDP_SIGPOLL */
+
+/* can we use SIGPOLL for tty IO? */
+/* #undef USE_TTY_SIGPOLL */
+
+/* should we use clock_settime()? */
+/* #undef USE_CLOCK_SETTIME */
+
+/* do we want the CHU driver? */
+/* #undef CLOCK_CHU */
+
+/* do we have the ppsclock streams module? */
+/* #undef PPS */
+
+/* do we have the tty_clk line discipline/streams module? */
+/* #undef TTYCLK */
+
+/* does the kernel support precision time discipline? */
+#define KERNEL_PLL 1
+
+/* does the kernel support multicasting IP? */
+#define MCAST 1
+
+/* do we have ntp_{adj,get}time in libc? */
+#define NTP_SYSCALLS_LIBC 1
+
+/* do we have ntp_{adj,get}time in the kernel? */
+/* #undef NTP_SYSCALLS_STD */
+
+/* do we have STREAMS/TLI? (Can we replace this with HAVE_SYS_STROPTS_H? */
+/* #undef STREAMS_TLI */
+
+/* do we need an s_char typedef? */
+#define NEED_S_CHAR_TYPEDEF 1
+
+/* include the GDT Surveying code? */
+/* #undef GDT_SURVEYING */
+
+/* does SIOCGIFCONF return size in the buffer? */
+/* #undef SIZE_RETURNED_IN_BUFFER */
+
+/* what is the name of TICK in the kernel? */
+#define K_TICK_NAME "_tick"
+
+/* Is K_TICK_NAME (nsec_per_tick, for example) in nanoseconds? */
+/* #undef TICK_NANO */
+
+/* what is the name of TICKADJ in the kernel? */
+#define K_TICKADJ_NAME "_tickadj"
+
+/* Is K_TICKADJ_NAME (hrestime_adj, for example) in nanoseconds? */
+/* #undef TICKADJ_NANO */
+
+/* what is (probably) the name of DOSYNCTODR in the kernel? */
+#define K_DOSYNCTODR_NAME "_dosynctodr"
+
+/* what is (probably) the name of NOPRINTF in the kernel? */
+#define K_NOPRINTF_NAME "_noprintf"
+
+/* do we need HPUX adjtime() library support? */
+/* #undef NEED_HPUX_ADJTIME */
+
+/* Might nlist() values require an extra level of indirection (AIX)? */
+/* #undef NLIST_EXTRA_INDIRECTION */
+
+/* Other needed NLIST stuff */
+#define NLIST_STRUCT 1
+/* #undef NLIST_NAME_UNION */
+
+/* Should we recommend a minimum value for tickadj? */
+/* #undef MIN_REC_TICKADJ */
+
+/* Is there a problem using PARENB and IGNPAR (IRIX)? */
+#define NO_PARENB_IGNPAR 1
+
+/* Should we not IGNPAR (Linux)? */
+/* #undef RAWDCF_NO_IGNPAR */
+
+/* Does the compiler like "volatile"? */
+/* #undef volatile */
+
+/* Does qsort expect to work on "void *" stuff? */
+#define QSORT_USES_VOID_P 1
+
+/* What is the fallback value for HZ? */
+#define DEFAULT_HZ 100
+
+/* Do we need to override the system's idea of HZ? */
+#define OVERRIDE_HZ 1
+
+/* Do we want the SCO clock hacks? */
+/* #undef SCO5_CLOCK */
+
+/* Do we want the ReliantUNIX clock hacks? */
+/* #undef RELIANTUNIX_CLOCK */
+
+/* Does the kernel have an FLL bug? */
+/* #undef KERNEL_FLL_BUG */
+
+/* Define if you have the TIOCGPPSEV ioctl (Solaris) */
+/* #undef HAVE_TIOCGPPSEV */
+
+/* Define if you have the TIOCSPPS ioctl (Solaris) */
+/* #undef HAVE_TIOCSPPS */
+
+/* Define if you have the CIOGETEV ioctl (SunOS, Linux) */
+/* #undef HAVE_CIOGETEV */
+
+/* Define if you have the TIOCGSERIAL, TIOCSSERIAL, ASYNC_PPS_CD_POS, and ASYNC_PPS_CD_NEG ioctls (linux) */
+/* #undef HAVE_TIO_SERIAL_STUFF */
+
+/* Define if you have the interface in the Draft RFC */
+#define HAVE_PPSAPI 1
+
+/* Do we need to #define _SVID3 when we #include <termios.h>? */
+/* #undef TERMIOS_NEEDS__SVID3 */
+
+/* Do we have support for SHMEM_STATUS? */
+#define ONCORE_SHMEM_STATUS 1
+
+/***/
+
+/* Which way should we declare... */
+
+/* adjtime()? */
+/* #undef DECL_ADJTIME_0 */
+
+/* bcopy()? */
+/* #undef DECL_BCOPY_0 */
+
+/* bzero()? */
+/* #undef DECL_BZERO_0 */
+
+/* cfset[io]speed()? */
+/* #undef DECL_CFSETISPEED_0 */
+
+/* hstrerror()? */
+/* #undef DECL_HSTRERROR_0 */
+
+/* ioctl()? */
+/* #undef DECL_IOCTL_0 */
+
+/* IPC? (bind, connect, recvfrom, sendto, setsockopt, socket) */
+/* #undef DECL_IPC_0 */
+
+/* memmove()? */
+/* #undef DECL_MEMMOVE_0 */
+
+/* memset()? */
+/* #undef DECL_MEMSET_0 */
+
+/* mkstemp()? */
+/* #undef DECL_MKSTEMP_0 */
+
+/* mktemp()? */
+/* #undef DECL_MKTEMP_0 */
+
+/* mrand48()? */
+/* #undef DECL_MRAND48_0 */
+
+/* nlist()? */
+/* #undef DECL_NLIST_0 */
+
+/* plock()? */
+/* #undef DECL_PLOCK_0 */
+
+/* rename()? */
+/* #undef DECL_RENAME_0 */
+
+/* select()? */
+/* #undef DECL_SELECT_0 */
+
+/* setitimer()? */
+/* #undef DECL_SETITIMER_0 */
+
+/* setpriority()? */
+/* #undef DECL_SETPRIORITY_0 */
+/* #undef DECL_SETPRIORITY_1 */
+
+/* sigvec()? */
+/* #undef DECL_SIGVEC_0 */
+
+/* srand48()? */
+/* #undef DECL_SRAND48_0 */
+
+/* stdio stuff? */
+/* #undef DECL_STDIO_0 */
+
+/* stime()? */
+/* #undef DECL_STIME_0 */
+/* #undef DECL_STIME_1 */
+
+/* strtol()? */
+/* #undef DECL_STRTOL_0 */
+
+/* syslog() stuff? */
+/* #undef DECL_SYSLOG_0 */
+
+/* time()? */
+/* #undef DECL_TIME_0 */
+
+/* [gs]ettimeofday()? */
+/* #undef DECL_TIMEOFDAY_0 */
+
+/* tolower()? */
+/* #undef DECL_TOLOWER_0 */
+
+/* toupper()? */
+/* #undef DECL_TOUPPER_0 */
+
+/* strerror()? */
+/* #undef DECL_STRERROR_0 */
+
+#define ULONG_CONST(a) a ## UL
+
+/* Autokey? */
+#define AUTOKEY
+
+/* Define if you have the <arpa/nameser.h> header file. */
+#define HAVE_ARPA_NAMESER_H 1
+
+/* Do we have audio support? */
+/* #undef HAVE_AUDIO */
+
+/* Define if you have the <bstring.h> header file. */
+/* #undef HAVE_BSTRING_H */
+
+/* Define if you have the `clock_settime' function. */
+#define HAVE_CLOCK_SETTIME 1
+
+/* Define if you have the `daemon' function. */
+#define HAVE_DAEMON 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 `finite' function. */
+/* #undef HAVE_FINITE */
+
+/* Define if you have the `getbootfile' function. */
+#define HAVE_GETBOOTFILE 1
+
+/* Define if you have the `getclock' function. */
+/* #undef HAVE_GETCLOCK */
+
+/* Define if you have the `getdtablesize' function. */
+#define HAVE_GETDTABLESIZE 1
+
+/* Define if you have the `getrusage' function. */
+#define HAVE_GETRUSAGE 1
+
+/* Define if you have the `gettimeofday' function. */
+#define HAVE_GETTIMEOFDAY 1
+
+/* Define if you have the `getuid' function. */
+#define HAVE_GETUID 1
+
+/* Define if you have the `hstrerror' function. */
+#define HAVE_HSTRERROR 1
+
+/* Define if you have the <ieeefp.h> header file. */
+#define HAVE_IEEEFP_H 1
+
+/* Define if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define if you have the `isfinite' function. */
+/* #undef HAVE_ISFINITE */
+
+/* Define if you have the `kvm_open' function. */
+#define HAVE_KVM_OPEN 1
+
+/* Define if you have the `K_open' function. */
+/* #undef HAVE_K_OPEN */
+
+/* Define if you have the `advapi32' library (-ladvapi32). */
+/* #undef HAVE_LIBADVAPI32 */
+
+/* Define if you have the `elf' library (-lelf). */
+/* #undef HAVE_LIBELF */
+
+/* Define if you have the `gen' library (-lgen). */
+/* #undef HAVE_LIBGEN */
+
+/* Define if you have the `kvm' library (-lkvm). */
+#define HAVE_LIBKVM 1
+
+/* Define if you have the `ld' library (-lld). */
+/* #undef HAVE_LIBLD */
+
+/* Define if you have the `mld' library (-lmld). */
+/* #undef HAVE_LIBMLD */
+
+/* Define if you have the `nsl' library (-lnsl). */
+/* #undef HAVE_LIBNSL */
+
+/* Define if you have the `posix4' library (-lposix4). */
+/* #undef HAVE_LIBPOSIX4 */
+
+/* Define if you have the `readline' library (-lreadline). */
+#define HAVE_LIBREADLINE 1
+
+/* Define if you have the `rt' library (-lrt). */
+/* #undef HAVE_LIBRT */
+
+/* Define if you have the `socket' library (-lsocket). */
+/* #undef HAVE_LIBSOCKET */
+
+/* Define if you have the <machine/inline.h> header file. */
+/* #undef HAVE_MACHINE_INLINE_H */
+
+/* Define if you have the <math.h> header file. */
+#define HAVE_MATH_H 1
+
+/* Define if you have the `memcpy' function. */
+#define HAVE_MEMCPY 1
+
+/* Define if you have the `memlk' function. */
+/* #undef HAVE_MEMLK */
+
+/* Define if you have the `memmove' function. */
+#define HAVE_MEMMOVE 1
+
+/* Define if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define if you have the `memset' function. */
+#define HAVE_MEMSET 1
+
+/* Define if you have the `mkstemp' function. */
+#define HAVE_MKSTEMP 1
+
+/* Define if you have the `mktime' function. */
+#define HAVE_MKTIME 1
+
+/* Define if you have the `mlockall' function. */
+/* #undef HAVE_MLOCKALL */
+
+/* Define if you have the `mrand48' function. */
+#define HAVE_MRAND48 1
+
+/* Define if you have the <netdb.h> header file. */
+#define HAVE_NETDB_H 1
+
+/* Define if you have the <netinet/in.h> header file. */
+#define HAVE_NETINET_IN_H 1
+
+/* Define if you have the <netinet/in_systm.h> header file. */
+#define HAVE_NETINET_IN_SYSTM_H 1
+
+/* Define if you have the <netinfo/ni.h> header file. */
+/* #undef HAVE_NETINFO_NI_H */
+
+/* Define if you have the <net/if.h> header file. */
+#define HAVE_NET_IF_H 1
+
+/* Define if you have the `nice' function. */
+#define HAVE_NICE 1
+
+/* Define if you have the `nlist' function. */
+#define HAVE_NLIST 1
+
+/* Define if you have the `ntp_adjtime' function. */
+#define HAVE_NTP_ADJTIME 1
+
+/* Define if you have the `ntp_gettime' function. */
+#define HAVE_NTP_GETTIME 1
+
+/* Define if you have the `plock' function. */
+/* #undef HAVE_PLOCK */
+
+/* Define if you have the <poll.h> header file. */
+#define HAVE_POLL_H 1
+
+/* Define if you have the `pututline' function. */
+/* #undef HAVE_PUTUTLINE */
+
+/* Define if you have the `pututxline' function. */
+/* #undef HAVE_PUTUTXLINE */
+
+/* Define if you have the `random' function. */
+/* #undef HAVE_RANDOM */
+
+/* Define if you have the `readlink' function. */
+#define HAVE_READLINK 1
+
+/* Define if you have the <resolv.h> header file. */
+#define HAVE_RESOLV_H 1
+
+/* Define if you have the `rtprio' function. */
+#define HAVE_RTPRIO 1
+
+/* Define if you have the <sched.h> header file. */
+/* #undef HAVE_SCHED_H */
+
+/* Define if you have the `sched_setscheduler' function. */
+/* #undef HAVE_SCHED_SETSCHEDULER */
+
+/* Define if you have the `setlinebuf' function. */
+#define HAVE_SETLINEBUF 1
+
+/* Define if you have the `setpgid' function. */
+#define HAVE_SETPGID 1
+
+/* Define if you have the `setpriority' function. */
+#define HAVE_SETPRIORITY 1
+
+/* Define if you have the `setsid' function. */
+#define HAVE_SETSID 1
+
+/* Define if you have the `settimeofday' function. */
+#define HAVE_SETTIMEOFDAY 1
+
+/* Define if you have the `setvbuf' function. */
+#define HAVE_SETVBUF 1
+
+/* Define if you have the <sgtty.h> header file. */
+#define HAVE_SGTTY_H 1
+
+/* Define if you have the `sigaction' function. */
+#define HAVE_SIGACTION 1
+
+/* Define if you have the `sigset' function. */
+/* #undef HAVE_SIGSET */
+
+/* Define if you have the `sigsuspend' function. */
+#define HAVE_SIGSUSPEND 1
+
+/* Define if you have the `sigvec' function. */
+#define HAVE_SIGVEC 1
+
+/* Define if you have the `snprintf' function. */
+#define HAVE_SNPRINTF 1
+
+/* Define if you have the `srand48' function. */
+#define HAVE_SRAND48 1
+
+/* Define if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define if you have the `stime' function. */
+/* #undef HAVE_STIME */
+
+/* Define if you have the `strchr' function. */
+#define HAVE_STRCHR 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 <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Do we have struct ntptimeval? */
+#define HAVE_STRUCT_NTPTIMEVAL 1
+
+/* Define if `time.tv_nsec' is member of `struct ntptimeval'. */
+#define HAVE_STRUCT_NTPTIMEVAL_TIME_TV_NSEC 1
+
+/* Do we have struct timespec? */
+#define HAVE_STRUCT_TIMESPEC 1
+
+/* Define if you have the <sun/audioio.h> header file. */
+/* #undef HAVE_SUN_AUDIOIO_H */
+
+/* Define if you have the `sysconf' function. */
+#define HAVE_SYSCONF 1
+
+/* Define if you have the `sysctl' function. */
+#define HAVE_SYSCTL 1
+
+/* Define if you have the <sys/audioio.h> header file. */
+/* #undef HAVE_SYS_AUDIOIO_H */
+
+/* Define if you have the <sys/clkdefs.h> header file. */
+/* #undef HAVE_SYS_CLKDEFS_H */
+
+/* Define if you have the <sys/file.h> header file. */
+#define HAVE_SYS_FILE_H 1
+
+/* Define if you have the <sys/i8253.h> header file. */
+/* #undef HAVE_SYS_I8253_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/mman.h> header file. */
+#define HAVE_SYS_MMAN_H 1
+
+/* Define if you have the <sys/modem.h> header file. */
+/* #undef HAVE_SYS_MODEM_H */
+
+/* Define if you have the <sys/param.h> header file. */
+#define HAVE_SYS_PARAM_H 1
+
+/* Define if you have the <sys/pcl720.h> header file. */
+/* #undef HAVE_SYS_PCL720_H */
+
+/* Define if you have the <sys/ppsclock.h> header file. */
+/* #undef HAVE_SYS_PPSCLOCK_H */
+
+/* Define if you have the <sys/ppstime.h> header file. */
+/* #undef HAVE_SYS_PPSTIME_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/sched.h> header file. */
+/* #undef HAVE_SYS_SCHED_H */
+
+/* Define if you have the <sys/select.h> header file. */
+#define HAVE_SYS_SELECT_H 1
+
+/* Define if you have the <sys/sio.h> header file. */
+/* #undef HAVE_SYS_SIO_H */
+
+/* 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/stream.h> header file. */
+/* #undef HAVE_SYS_STREAM_H */
+
+/* Define if you have the <sys/stropts.h> header file. */
+/* #undef HAVE_SYS_STROPTS_H */
+
+/* Define if you have the <sys/sysctl.h> header file. */
+#define HAVE_SYS_SYSCTL_H 1
+
+/* Define if you have the <sys/syssgi.h> header file. */
+/* #undef HAVE_SYS_SYSSGI_H */
+
+/* Define if you have the <sys/termios.h> header file. */
+#define HAVE_SYS_TERMIOS_H 1
+
+/* Define if you have the <sys/timepps.h> header file. */
+#define HAVE_SYS_TIMEPPS_H 1
+
+/* Define if you have the <sys/timers.h> header file. */
+#define HAVE_SYS_TIMERS_H 1
+
+/* Define if you have the <sys/timex.h> header file. */
+#define HAVE_SYS_TIMEX_H 1
+
+/* Define if you have the <sys/time.h> header file. */
+#define HAVE_SYS_TIME_H 1
+
+/* Define if you have the <sys/tpro.h> header file. */
+/* #undef HAVE_SYS_TPRO_H */
+
+/* Define if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define if you have the <sys/wait.h> header file. */
+#define HAVE_SYS_WAIT_H 1
+
+/* Define if the system has the type `s_char'. */
+/* #undef HAVE_S_CHAR */
+
+/* Define if you have the <termios.h> header file. */
+#define HAVE_TERMIOS_H 1
+
+/* Define if you have the <termio.h> header file. */
+/* #undef HAVE_TERMIO_H */
+
+/* Define if you have the <timepps.h> header file. */
+/* #undef HAVE_TIMEPPS_H */
+
+/* Define if you have the `timer_create' function. */
+/* #undef HAVE_TIMER_CREATE */
+
+/* Define if you have the `timer_settime' function. */
+/* #undef HAVE_TIMER_SETTIME */
+
+/* Define if you have the <timex.h> header file. */
+/* #undef HAVE_TIMEX_H */
+
+/* Define if you have the `umask' function. */
+#define HAVE_UMASK 1
+
+/* Define if you have the `uname' function. */
+#define HAVE_UNAME 1
+
+/* Define if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define if you have the `updwtmp' function. */
+/* #undef HAVE_UPDWTMP */
+
+/* Define if you have the `updwtmpx' function. */
+/* #undef HAVE_UPDWTMPX */
+
+/* Define if you have the <utmpx.h> header file. */
+/* #undef HAVE_UTMPX_H */
+
+/* Define if you have the <utmp.h> header file. */
+#define HAVE_UTMP_H 1
+
+/* Define if you have the `vsprintf' function. */
+#define HAVE_VSPRINTF 1
+
+/* Define if you have the </sys/sync/queue.h> header file. */
+/* #undef HAVE__SYS_SYNC_QUEUE_H */
+
+/* Define if you have the </sys/sync/sema.h> header file. */
+/* #undef HAVE__SYS_SYNC_SEMA_H */
+
+/* Define if you have the `__adjtimex' function. */
+/* #undef HAVE___ADJTIMEX */
+
+/* Define if you have the `__ntp_gettime' function. */
+/* #undef HAVE___NTP_GETTIME */
+
+/* Default location of crypto key info */
+#define NTP_KEYSDIR "/usr/local/etc"
+
+/* Use OpenSSL? */
+/* #undef OPENSSL */
+
+/* Name of package */
+#define PACKAGE "ntp"
+
+/* Define if compiler has function prototypes */
+#define PROTOTYPES 1
+
+/* Public key? */
+/* #undef PUBKEY */
+
+/* Define as the return type of signal handlers (`int' or `void'). */
+#define RETSIGTYPE void
+
+/* Use RSAREF? */
+/* #undef RSAREF */
+
+/* The size of a `int', as computed by sizeof. */
+#define SIZEOF_INT 4
+
+/* The size of a `long', as computed by sizeof. */
+#ifdef __alpha__
+#define SIZEOF_LONG 8
+#else
+#define SIZEOF_LONG 4
+#endif
+
+/* The size of a `signed char', as computed by sizeof. */
+#define SIZEOF_SIGNED_CHAR 1
+
+/* 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 */
+
+/* Version number of package */
+#define VERSION "4.1.0"
+
+/* Define if your processor stores words with the most significant byte first
+ (like Motorola and SPARC, unlike Intel and VAX). */
+/* #undef WORDS_BIGENDIAN */
+
+/* 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 if on MINIX. */
+/* #undef _MINIX */
+
+/* Define if the system does not provide POSIX.1 features except with this
+ defined. */
+/* #undef _POSIX_1_SOURCE */
+
+/* Define if you need to in order for stat and other things to work. */
+/* #undef _POSIX_SOURCE */
+
+/* Define if type `char' is unsigned and you are not using gcc. */
+#ifndef __CHAR_UNSIGNED__
+/* # undef __CHAR_UNSIGNED__ */
+#endif
+
+/* Define to empty if `const' does not conform to ANSI C. */
+/* #undef const */
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+/* #undef gid_t */
+
+/* Define as `__inline' if that's what the C compiler calls it, or to nothing
+ if it is not supported. */
+/* #undef inline */
+
+/* Define to `long' if <sys/types.h> does not define. */
+/* #undef off_t */
+
+/* Define to `unsigned' if <sys/types.h> does not define. */
+/* #undef size_t */
+
+/* Define to `long' if <sys/types.h> does not define. */
+/* #undef time_t */
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+/* #undef uid_t */
diff --git a/usr.sbin/ntp/doc/Makefile b/usr.sbin/ntp/doc/Makefile
new file mode 100644
index 0000000..8c79e51
--- /dev/null
+++ b/usr.sbin/ntp/doc/Makefile
@@ -0,0 +1,29 @@
+# $FreeBSD$
+
+MAINTAINER= sheldonh
+
+FILESDIR= ${SHAREDIR}/doc/ntp
+
+FILES= accopt.htm assoc.htm audio.htm authopt.htm biblio.htm build.htm \
+ clockopt.htm \
+ config.htm confopt.htm copyright.htm debug.htm driver1.htm \
+ driver10.htm driver11.htm driver12.htm driver16.htm driver18.htm \
+ driver19.htm driver2.htm driver20.htm driver22.htm driver23.htm \
+ driver24.htm driver26.htm driver27.htm driver28.htm driver29.htm \
+ driver3.htm driver30.htm driver32.htm driver33.htm driver34.htm \
+ driver35.htm driver36.htm driver37.htm \
+ driver4.htm driver5.htm driver6.htm driver7.htm driver8.htm \
+ driver9.htm exec.htm extern.htm gadget.htm hints.htm \
+ howto.htm htmlprimer.htm index.htm kern.htm kernpps.htm \
+ ldisc.htm measure.htm miscopt.htm monopt.htm mx4200data.htm \
+ notes.htm ntpd.htm ntpdate.htm ntpdc.htm ntpq.htm ntptime.htm \
+ ntptrace.htm parsedata.htm parsenew.htm patches.htm porting.htm \
+ pps.htm prefer.htm qth.htm quick.htm rdebug.htm refclock.htm \
+ release.htm tickadj.htm vxworks.htm y2k.htm
+
+MAN= ntp.conf.5 ntp.keys.5
+MAN+= ntp-genkeys.8 ntpd.8 ntpdate.8 ntpdc.8 ntpq.8 ntptime.8 ntptrace.8
+
+.PATH: ${.CURDIR}/../../../contrib/ntp/html
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ntp/doc/ntp-genkeys.8 b/usr.sbin/ntp/doc/ntp-genkeys.8
new file mode 100644
index 0000000..3af0f47
--- /dev/null
+++ b/usr.sbin/ntp/doc/ntp-genkeys.8
@@ -0,0 +1,206 @@
+.\"
+.\" $FreeBSD$
+.\"
+.Dd August 2, 2001
+.Dt NTP_GENKEYS 8
+.Os
+.Sh NAME
+.Nm ntp-genkeys
+.Nd generate public and private keys
+.Sh SYNOPSIS
+.Nm
+.Op Fl dfhlnt
+.Op Fl c Ar conffile
+.Op Fl g Ar target
+.Op Fl k Ar keyfile
+.Sh DESCRIPTION
+This program generates random keys used by either or both the
+NTPv3/NTPv4 symmetric key or the NTPv4 public key (Autokey)
+cryptographic authentication schemes.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl c Ar conffile
+Location of
+.Xr ntp.conf 8
+file.
+.It Fl d
+enable debug messages (can be used multiple times)
+.It Fl f
+force installation of generated keys.
+.It Fl g target
+Generate file or files indicated by the characters in the
+.Ar target
+string:
+.Bl -tag -width X
+.It Li d
+Generate D-H parameter file.
+.It Li m
+Generate MD5 key file.
+.It Li r
+Generate RSA keys.
+.El
+.It Fl h
+Build keys here (current directory).
+Implies
+.Fl l .
+.It Fl k Ar keyfile
+Location of key file.
+.It Fl l
+Do not make the symlinks.
+.It Fl n
+Do not actually do anything, just say what would be done.
+.It Fl t
+Trash the (old) files at the end of symlink.
+.El
+.Pp
+By default the program
+generates the
+.Xr ntp.keys 5
+file containing 16 random symmetric
+keys.
+In addition, if the
+rsaref20
+package is configured
+for the software build, the program generates cryptographic values
+used by the Autokey scheme.
+These values are incorporated as a set
+of three files,
+.Pa ntpkey
+containing the RSA private key,
+.Pa ntpkey_ Ns Ar host
+containing the RSA public key, where
+.Ar host
+is the DNS name of the generating machine, and
+.Pa ntpkey_dh
+containing the parameters for the Diffie-Hellman
+key-agreement algorithm.
+All files and are in printable ASCII
+format.
+A timestamp in NTP seconds is appended to each.
+Since the
+algorithms are seeded by the system clock, each run of this program
+produces a different file and file name.
+.Pp
+The
+.Xr ntp.keys 5
+file contains 16 MD5 keys.
+Each key
+consists of 16 characters randomized over the ASCII 95-character
+printing subset.
+The file is read by the daemon at the location
+specified by the
+.Ic keys
+configuration file command and made
+visible only to root.
+An additional key consisting of a easily
+remembered password should be added by hand for use with the
+.Xr ntpq 8
+and
+.Xr ntpdc 8
+programs.
+The file must be
+distributed by secure means to other servers and clients sharing
+the same security compartment.
+While the key identifiers for MD5
+and DES keys must be in the range 1-65534, inclusive, the
+.Nm
+program uses only the identifiers from 1 to
+16.
+The key identifier for each association is specified as the key
+argument in the
+.Ic server
+or
+.Ic peer
+configuration file command.
+.Pp
+The
+.Pa ntpkey
+file contains the RSA private key.
+It is
+read by the daemon at the location specified by the
+.Ar privatekey
+argument of the
+.Ic crypto
+configuration
+file command and made visible only to root.
+This file is useful
+only to the machine that generated it and never shared with any
+other daemon or application program.
+.Pp
+The
+.Pa ntpkey_ Ns Ar host
+file contains the RSA public
+key, where
+.Ar host
+is the DNS name of the host that
+generated it.
+The file is read by the daemon at the location
+specified by the
+.Ar publickey
+argument to the
+.Ic server
+or
+.Ic peer
+configuration file command.
+This file can be
+widely distributed and stored without using secure means, since the
+data are public values.
+.Pp
+The
+.Pa ntp_dh
+file contains two Diffie-Hellman parameters:
+the prime modulus and the generator.
+The file is read by the daemon
+at the location specified by the
+.Ar dhparams
+argument of the
+.Ic crypto
+configuration file command.
+The file can be
+distributed by insecure means to other servers and clients sharing
+the same key agreement compartment, since the data are public
+values.
+.Pp
+The file formats begin with two lines, the first containing the
+generating system DNS name and the second the datestamp.
+Lines
+beginning with
+.Ql #
+are considered comments and ignored by
+the daemon.
+In the
+.Xr ntp.keys 5
+file, the next 16 lines
+contain the MD5 keys in order.
+If necessary, this file can be
+further customized by an ordinary text editor.
+The format is
+described in the following section.
+In the
+.Pa ntpkey
+and
+.Pa ntpkey_ Ns Ar host
+files, the next line contains the
+modulus length in bits followed by the key as a PEM encoded string.
+In the
+.Pa ntpkey_dh
+file, the next line contains the prime
+length in bytes followed by the prime as a PEM encoded string, and
+the next and final line contains the generator length in bytes
+followed by the generator as a PEM encoded string.
+.Pp
+Note: See the file
+.Pa ./source/rsaref.h
+in the
+rsaref20
+package for explanation of return values, if
+necessary.
+.Sh SEE ALSO
+.Xr ntp.keys 5 ,
+.Xr ntpdc 8 ,
+.Xr ntpq 8
+.Sh BUGS
+It can take quite a while to generate the RSA public/private key
+pair and Diffie-Hellman parameters, from a few seconds on a modern
+workstation to several minutes on older machines.
diff --git a/usr.sbin/ntp/doc/ntp.conf.5 b/usr.sbin/ntp/doc/ntp.conf.5
new file mode 100644
index 0000000..928cb8f
--- /dev/null
+++ b/usr.sbin/ntp/doc/ntp.conf.5
@@ -0,0 +1,2093 @@
+.\"
+.\" $FreeBSD$
+.\"
+.Dd January 13, 2000
+.Dt NTP.CONF 5
+.Os
+.Sh NAME
+.Nm ntp.conf
+.Nd Network Time Protocol (NTP) daemon configuration file
+.Sh SYNOPSIS
+.Nm /etc/ntp.conf
+.Sh DESCRIPTION
+The
+.Nm
+configuration file is read at initial startup by the
+.Xr ntpd 8
+daemon in order to specify the synchronization sources,
+modes and other related information.
+Usually, it is installed in the
+.Pa /etc
+directory,
+but could be installed elsewhere
+(see the daemon's
+.Fl c
+command line option).
+.Pp
+The file format is similar to other
+.Ux
+configuration files.
+Comments begin with a
+.Ql #
+character and extend to the end of the line;
+blank lines are ignored.
+Configuration commands consist of an initial keyword
+followed by a list of arguments,
+some of which may be optional, separated by whitespace.
+Commands may not be continued over multiple lines.
+Arguments may be host names,
+host addresses written in numeric, dotted-quad form,
+integers, floating point numbers (when specifying times in seconds)
+and text strings.
+.Pp
+The rest of this page describes the configuration and control options.
+The
+.Qq "Notes on Configuring NTP and Setting up a NTP Subnet"
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp )
+contains an extended discussion of these options.
+In addition to the discussion of general
+.Sx Configuration Options ,
+there are sections describing the following supported functionality
+and the options used to control it:
+.Bl -bullet -offset indent
+.It
+.Sx Authentication Support
+.It
+.Sx Monitoring Support
+.It
+.Sx Access Control Support
+.It
+.Sx Reference Clock Support
+.El
+.Pp
+Following these is a section describing
+.Sx Miscellaneous Options .
+While there is a rich set of options available,
+the only required option is one or more
+.Ic server ,
+.Ic peer ,
+.Ic broadcast
+or
+.Ic manycastclient
+commands.
+.Sh Configuration Support
+Following is a description of the configuration commands in
+NTPv4.
+These commands have the same basic functions as in NTPv3 and
+in some cases new functions and new arguments.
+There are two
+classes of commands, configuration commands that configure a
+persistent association with a remote server or peer or reference
+clock, and auxilliary commands that specify environmental variables
+that control various related operations.
+.Ss Configuration Commands
+The various modes are determined by the command keyword and the
+type of the required IP address.
+Addresses are classed by type as
+(s) a remote server or peer (IP class A, B and C), (b) the
+broadcast address of a local interface, (m) a multicast address (IP
+class D), or (r) a reference clock address (127.127.x.x).
+Note that
+only those options applicable to each command are listed below.
+Use
+of options not listed may not be caught as an error, but may result
+in some weird and even destructive behavior.
+.Bl -tag -width indent
+.It Xo Ic server Ar address
+.Op Cm key Ar key \&| Cm autokey
+.Op Cm burst
+.Op Cm iburst
+.Op Cm version Ar version
+.Op Cm prefer
+.Op Cm minpoll Ar minpoll
+.Op Cm maxpoll Ar maxpoll
+.Xc
+.It Xo Ic peer Ar address
+.Op Cm key Ar key \&| Cm autokey
+.Op Cm version Ar version
+.Op Cm prefer
+.Op Cm minpoll Ar minpoll
+.Op Cm maxpoll Ar maxpoll
+.Xc
+.It Xo Ic broadcast Ar address
+.Op Cm key Ar key \&| Cm autokey
+.Op Cm version Ar version
+.Op Cm prefer
+.Op Cm minpoll Ar minpoll
+.Op Cm ttl Ar ttl
+.Xc
+.It Xo Ic manycastclient Ar address
+.Op Cm key Ar key \&| Cm autokey
+.Op Cm version Ar version
+.Op Cm prefer
+.Op Cm minpoll Ar minpoll
+.Op Cm maxpoll Ar maxpoll
+.Op Cm ttl Ar ttl
+.Xc
+.El
+.Pp
+These four commands specify the time server name or address to
+be used and the mode in which to operate.
+The
+.Ar address
+can be
+either a DNS name or a IP address in dotted-quad notation.
+Additional information on association behavior can be found in the
+.Qq "Association Management"
+page.
+.Bl -tag -width indent
+.It Ic server
+For type s and r addresses, this command mobilizes a persistent
+client mode association with the specified remote server or local
+radio clock.
+In this mode the local clock can synchronized to the
+remote server, but the remote server can never be synchronized to
+the local clock.
+This command should
+.Em not
+be used for type
+b or m addresses.
+.It Ic peer
+For type s addresses (only), this command mobilizes a
+persistent symmetric-active mode association with the specified
+remote peer.
+In this mode the local clock can be synchronized to
+the remote peer or the remote peer can be synchronized to the local
+clock.
+This is useful in a network of servers where, depending on
+various failure scenarios, either the local or remote peer may be
+the better source of time.
+This command should NOT be used for type
+b, m or r addresses.
+.It Ic broadcast
+For type b and m addresses (only), this
+command mobilizes a persistent broadcast mode association.
+Multiple
+commands can be used to specify multiple local broadcast interfaces
+(subnets) and/or multiple multicast groups.
+Note that local
+broadcast messages go only to the interface associated with the
+subnet specified, but multicast messages go to all interfaces.
+In broadcast mode the local server sends periodic broadcast
+messages to a client population at the
+.Ar address
+specified, which is usually the broadcast address on (one of) the
+local network(s) or a multicast address assigned to NTP.
+The IANA
+has assigned the multicast group address 224.0.1.1 exclusively to
+NTP, but other nonconflicting addresses can be used to contain the
+messages within administrative boundaries.
+Ordinarily, this
+specification applies only to the local server operating as a
+sender; for operation as a broadcast client, see the
+.Ic broadcastclient
+or
+.Ic multicastclient
+commands
+below.
+.It Ic manycastclient
+For type m addresses (only), this command mobilizes a
+manycast client mode association for the multicast address
+specified.
+In this case a specific address must be supplied which
+matches the address used on the
+.Ic manycastserver
+command for
+the designated manycast servers.
+The NTP multicast address
+224.0.1.1 assigned by the IANA should NOT be used, unless specific
+means are taken to avoid spraying large areas of the Internet with
+these messages and causing a possibly massive implosion of replies
+at the sender.
+The
+.Ic manycastserver
+command specifies that the local server
+is to operate in client mode with the remote servers that are
+discovered as the result of broadcast/multicast messages.
+The
+client broadcasts a request message to the group address associated
+with the specified
+.Ar address
+and specifically enabled
+servers respond to these messages.
+The client selects the servers
+providing the best time and continues as with the
+.Ic server
+command.
+The remaining servers are discarded as if never
+heard.
+.El
+.Pp
+Options:
+.Bl -tag -width indent
+.It Cm autokey
+All packets sent to and received from the server or peer are to
+include authentication fields encrypted using the autokey scheme
+described in
+.Sx Authentication Options .
+.It Cm burst
+when the server is reachable and at each poll interval, send a
+burst of eight packets instead of the usual one packet.
+The spacing
+between the first and the second packets is about 16s to allow a
+modem call to complete, while the spacing between the remaining
+packets is about 2s.
+This is designed to improve timekeeping
+quality with the
+.Ic server
+command and s
+addresses.
+.It Cm iburst
+When the server is unreachable and at each poll interval, send
+a burst of eight packets instead of the usual one.
+As long as the
+server is unreachable, the spacing between packets is about 16s to
+allow a modem call to complete.
+Once the server is reachable, the
+spacing between packets is about 2s.
+This is designed to speed the
+initial synchronization acquisition with the
+.Ic server
+command and s addresses and when
+.Xr ntpd 8
+is started
+with the
+.Fl q
+option.
+.It Cm key Ar key
+All packets sent to and received from the server or peer are to
+include authentication fields encrypted using the specified
+.Ar key
+identifier with values from 1 to 65534, inclusive.
+The
+default is to include no encryption field.
+.It Cm minpoll Ar minpoll
+.It Cm maxpoll Ar maxpoll
+These options specify the minimum and maximum poll intervals
+for NTP messages, in seconds to the power of two.
+The maximum poll
+interval defaults to 10 (1,024 s), but can be increased by the
+.Cm maxpoll
+option to an upper limit of 17 (36.4 h).
+The
+minimum poll interval defaults to 6 (64 s), but can be decreased by
+the
+.Cm minpoll
+option to a lower limit of 4 (16 s).
+.It Cm prefer
+Marks the server as preferred.
+All other things being equal,
+this host will be chosen for synchronization among a set of
+correctly operating hosts.
+See the
+.Qq "Mitigation Rules and the prefer Keyword"
+page for further
+information.
+.It Cm ttl Ar ttl
+This option is used only with broadcast server and manycast
+client modes.
+It specifies the time-to-live
+.Cm ttl
+to
+use on broadcast server and multicast server and the maximum
+.Cm ttl
+for the expanding ring search with manycast
+client packets.
+Selection of the proper value, which defaults to
+127, is something of a black art and should be coordinated with the
+network administrator.
+.It Cm version Ar version
+Specifies the version number to be used for outgoing NTP
+packets.
+Versions 1-4 are the choices, with version 4 the
+default.
+.El
+.Ss Auxilliary Commands
+.Bl -tag -width indent
+.It Ic broadcastclient
+This command enables reception of broadcast server messages to
+any local interface (type b) address.
+Upon receiving a message for
+the first time, the broadcast client measures the nominal server
+propagation delay using a brief client/server exchange with the
+server, then enters the broadcast client mode, in which it
+synchronizes to succeeding broadcast messages.
+Note that, in order
+to avoid accidental or malicious disruption in this mode, both the
+server and client should operate using symmetric-key or public-key
+authentication as described in
+.Sx Authentication Options .
+.It Ic manycastserver Ar address ...
+This command enables reception of manycast client messages to
+the multicast group address(es) (type m) specified.
+At least one
+address is required, but the NTP multicast address 224.0.1.1
+assigned by the IANA should NOT be used, unless specific means are
+taken to limit the span of the reply and avoid a possibly massive
+implosion at the original sender.
+Note that, in order to avoid
+accidental or malicious disruption in this mode, both the server
+and client should operate using symmetric-key or public-key
+authentication as described in
+.Sx Authentication Options .
+.It Ic multicastclient Ar address ...
+This command enables reception of multicast server messages to
+the multicast group address(es) (type m) specified.
+Upon receiving
+a message for the first time, the multicast client measures the
+nominal server propagation delay using a brief client/server
+exchange with the server, then enters the broadcast client mode, in
+which it synchronizes to succeeding multicast messages.
+Note that,
+in order to avoid accidental or malicious disruption in this mode,
+both the server and client should operate using symmetric-key or
+public-key authentication as described in
+.Sx Authentication Options .
+.El
+.Sh Authentication Support
+Authentication support allows the NTP client to verify that the
+server is in fact known and trusted and not an intruder intending
+accidentally or on purpose to masquerade as that server.
+The NTPv3
+specification RFC-1305 defines an scheme which provides
+cryptographic authentication of received NTP packets.
+Originally,
+this was done using the Data Encryption Standard (DES) algorithm
+operating in Cipher Block Chaining (CBC) mode, commonly called
+DES-CBC.
+Subsequently, this was augmented by the RSA Message Digest
+5 (MD5) algorithm using a private key, commonly called keyed-MD5.
+Either algorithm computes a message digest, or one-way hash, which
+can be used to verify the server has the correct private key and
+key identifier.
+.Pp
+NTPv4 retains the NTPv3 schemes, properly described as
+symmetric-key cryptography and, in addition, provides a new Autokey
+scheme based on public-key cryptography.
+Public-key cryptography is
+generally considered more secure than symmetric-key cryptography,
+since the security is based on a private value which is generated
+by each server and never revealed.
+With Autokey all key
+distribution and management functions involve only public values,
+which considerably simplifies key distribution and storage.
+.Pp
+Authentication is configured separately for each association
+using the
+.Cm key
+or
+.Cm autokey
+subcommands on the
+.Ic peer ,
+.Ic server ,
+.Ic broadcast
+and
+.Ic manycastclient
+commands as described in
+.Sx Configuration Options .
+The authentication
+options described below specify the suite of keys, select the key
+for each configured association and manage the configuration
+operations.
+.Pp
+The
+.Cm auth
+flag controls whether new associations or
+remote configuration commands require cryptographic authentication.
+This flag can be set or reset by the
+.Ic enable
+and
+.Ic disable
+configuration commands and also by remote
+configuration commands sent by a
+.Xr ntpdc 8
+program running in
+another machine.
+If this flag is enabled, which is the default
+case, new broadcast client and symmetric passive associations and
+remote configuration commands must be cryptographically
+authenticated using either symmetric-key or public-key schemes.
+If
+this flag is disabled, these operations are effective even if not
+cryptographic authenticated.
+It should be understood that operating
+in the latter mode invites a significant vulnerability where a
+rogue hacker can seriously disrupt client timekeeping.
+.Pp
+In networks with firewalls and large numbers of broadcast
+clients it may be acceptable to disable authentication, since that
+avoids key distribution and simplifies network maintenance.
+However, when the configuration file contains host names, or when a
+server or client is configured remotely, host names are resolved
+using the DNS and a separate name resolution process.
+In order to
+protect against bogus name server messages, name resolution
+messages are authenticated using an internally generated key which
+is normally invisible to the user.
+However, if cryptographic
+support is disabled, the name resolution process will fail.
+This
+can be avoided either by specifying IP addresses instead of host
+names, which is generally inadvisable, or by enabling the flag for
+name resolution and disabled it once the name resolution process is
+complete.
+.Pp
+An attractive alternative where multicast support is available
+is manycast mode, in which clients periodically troll for servers.
+Cryptographic authentication in this mode uses public-key schemes
+as described below.
+The principle advantage of this manycast mode
+is that potential servers need not be configured in advance, since
+the client finds them during regular operation, and the
+configuration files for all clients can be identical.
+.Pp
+In addition to the default symmetric-key cryptographic support,
+support for public-key cryptography is available if the requisite
+.Sy rsaref20
+software distribution has been installed before
+building the distribution.
+Public-key cryptography provides secure
+authentication of servers without compromising accuracy and
+stability.
+The security model and protocol schemes for both
+symmetric-key and public-key cryptography are described below.
+.Ss Symmetric-Key Scheme
+The original RFC-1305 specification allows any one of possibly
+65,534 keys, each distinguished by a 32-bit key identifier, to
+authenticate an association.
+The servers and clients involved must
+agree on the key and key identifier to authenticate their messages.
+Keys and related information are specified in a key file, usually
+called
+.Pa ntp.keys ,
+which should be exchanged and stored
+using secure procedures beyond the scope of the NTP protocol
+itself.
+Besides the keys used for ordinary NTP associations,
+additional keys can be used as passwords for the
+.Xr ntpq 8
+and
+.Xr ntpdc 8
+utility programs.
+.Pp
+When
+.Xr ntpd 8
+is first started, it reads the key file
+specified in the
+.Ic keys
+command and installs the keys in the
+key cache.
+However, the keys must be activated with the
+.Ic trusted
+command before use.
+This allows, for instance, the
+installation of possibly several batches of keys and then
+activating or deactivating each batch remotely using
+.Xr ntpdc 8 .
+This also provides a revocation capability that can
+be used if a key becomes compromised.
+The
+.Ic requestkey
+command selects the key used as the password for the
+.Xr ntpdc 8
+utility, while the
+.Ic controlkey
+command selects the key used
+as the password for the
+.Xr ntpq 8
+utility.
+.Ss Public-Key Scheme
+The original NTPv3 authentication scheme described in RFC-1305
+continues to be supported; however, in NTPv4 an additional
+authentication scheme called Autokey is available.
+It uses MD5
+message digest, RSA public-key signature and Diffie-Hellman key
+agreement algorithms available from several sources, but not
+included in the NTPv4 software distribution.
+In order to be
+effective, the
+.Sy rsaref20
+package must be installed as
+described in the
+.Pa README.rsa
+file.
+Once installed, the
+configure and build process automatically detects it and compiles
+the routines required.
+The Autokey scheme has several modes of
+operation corresponding to the various NTP modes supported.
+RSA
+signatures with timestamps are used in all modes to verify the
+source of cryptographic values.
+All modes use a special cookie
+which can be computed independently by the client and server.
+In
+symmetric modes the cookie is constructed using the Diffie-Hellman
+key agreement algorithm.
+In other modes the cookie is constructed
+from the IP addresses and a private value known only to the server.
+All modes use in addition a variant of the S-KEY scheme, in which a
+pseudo-random key list is generated and used in reverse order.
+These schemes are described along with an executive summary,
+current status, briefing slides and reading list, in the
+.Qq "Autonomous Authentication"
+page.
+.Pp
+The cryptographic values used by the Autokey scheme are
+incorporated as a set of files generated by the
+.Xr ntp-genkeys 8
+program, including the
+symmetric private keys, public/private key pair, and the agreement
+parameters.
+See the
+.Xr ntp.keys 5
+page for a description of
+the formats of these files.
+They contain cryptographic values
+generated by the algorithms of the
+.Sy rsaref20
+package and
+are in printable ASCII format.
+All file names include the
+timestamp, in NTP seconds, following the default names given below.
+Since the file data are derived from random values seeded by the
+system clock and the file name includes the timestamp, every
+generation produces a different file and different file name.
+.Pp
+The
+.Pa ntp.keys
+file contains the DES/MD5 private keys.
+It
+must be distributed by secure means to other servers and clients
+sharing the same security compartment and made visible only to
+root.
+While this file is not used with the Autokey scheme, it is
+needed to authenticate some remote configuration commands used by
+the
+.Xr ntpdc 8 ,
+.Xr ntpq 8
+utilities.
+The
+.Pa ntpkey
+file
+contains the RSA private key.
+It is useful only to the machine that
+generated it and never shared with any other daemon or application
+program, so must be made visible only to root.
+.Pp
+The
+.Pa ntp_dh
+file contains the agreement parameters,
+which are used only in symmetric (active and passive) modes.
+It is
+necessary that both peers beginning a symmetric-mode association
+share the same parameters, but it does not matter which
+.Pa ntp_dh
+file generates them.
+If one of the peers contains
+the parameters, the other peer obtains them using the Autokey
+protocol.
+If both peers contain the parameters, the most recent
+copy is used by both peers.
+If a peer does not have the parameters,
+they will be requested by all associations, either configured or
+not; but, none of the associations can proceed until one of them
+has received the parameters.
+Once loaded, the parameters can be
+provided on request to other clients and servers.
+The
+.Pa ntp_dh
+file can be also be distributed using insecure
+means, since the data are public values.
+.Pp
+The
+.Pa ntpkey_ Ns Ar host
+file contains the RSA public
+key, where
+.Ar host
+is the name of the host.
+Each host
+must have its own
+.Pa ntpkey_ Ns Ar host
+file, which is
+normally provided to other hosts using the Autokey protocol.
+Each
+.Ic server
+or
+.Ic peer
+association requires the public
+key associated with the particular server or peer to be loaded
+either directly from a local file or indirectly from the server
+using the Autokey protocol.
+These files can be widely distributed
+and stored using insecure means, since the data are public
+values.
+.Pp
+The optional
+.Pa ntpkey_certif_ Ns Ar host
+file contains
+the PKI certificate for the host.
+This provides a binding between
+the host hame and RSA public key.
+In the current implementation the
+certificate is obtained by a client, if present, but the contents
+are ignored.
+.Pp
+Due to the widespread use of interface-specific naming, the host
+names used in configured and mobilized associations are determined
+by the
+.Ux
+.Xr gethostname 3
+library routine.
+Both the
+.Xr ntp-genkeys 8
+program and the Autokey protocol derive the
+name of the public key file using the name returned by this
+routine.
+While every server and client is required to load their
+own public and private keys, the public keys for each client or
+peer association can be obtained from the server or peer using the
+Autokey protocol.
+Note however, that at the current stage of
+development the authenticity of the server or peer and the
+cryptographic binding of the server name, address and public key is
+not yet established by a certificate authority or web of trust.
+.Ss Leapseconds Table
+The NIST provides a table showing the epoch for all historic
+occasions of leap second insertion since 1972.
+The leapsecond table
+shows each epoch of insertion along with the offset of
+International Atomic Time (TAI) with respect to Coordinated
+Universtal Time (UTC), as disseminated by NTP.
+The table can be
+obtained directly from NIST national time servers using
+FTP as the ASCII file
+.Pa pub/leap-seconds .
+.Pp
+While not strictly a security function, the Autokey scheme
+provides means to securely retrieve the leapsecond table from a
+server or peer.
+Servers load the leapsecond table directly from the
+file specified in the
+.Ic crypto
+command, while clients can
+load the table indirectly from the servers using the Autokey
+protocol.
+Once loaded, the table can be provided on request to
+other clients and servers.
+.Ss Key Management
+All key files are installed by default in
+.Pa /usr/local/etc ,
+which is normally in a shared filesystem
+in NFS-mounted networks and avoids installing them in each machine
+separately.
+The default can be overridden by the
+.Ic keysdir
+configuration command.
+However, this is not a good place to install
+the private key file, since each machine needs its own file.
+A
+suitable place to install it is in
+.Pa /etc ,
+which is normally
+not in a shared filesystem.
+.Pp
+The recommended practice is to keep the timestamp extensions
+when installing a file and to install a link from the default name
+(without the timestamp extension) to the actual file.
+This allows
+new file generations to be activated simply by changing the link.
+However,
+.Xr ntpd 8
+parses the link name when present to extract
+the extension value and sends it along with the public key and host
+name when requested.
+This allows clients to verify that the file
+and generation time are always current.
+However, the actual
+location of each file can be overridden by the
+.Ic crypto
+configuration command.
+.Pp
+All cryptographic keys and related parameters should be
+regenerated on a periodic and automatic basis, like once per month.
+The
+.Xr ntp-genkeys 8
+program uses the same timestamp extension
+for all files generated at one time, so each generation is distinct
+and can be readily recognized in monitoring data.
+While a
+public/private key pair must be generated by every server and
+client, the public keys and agreement parameters do not need to be
+explicitly copied to all machines in the same security compartment,
+since they can be obtained automatically using the Autokey
+protocol.
+However, it is necessary that all primary servers have
+the same agreement parameter file.
+The recommended way to do this
+is for one of the primary servers to generate that file and then
+copy it to the other primary servers in the same compartment using
+the
+.Ux
+.Xr rdist 1
+command.
+Future versions of the Autokey
+protocol are to contain provisions for an agreement protocol to do
+this automatically.
+.Pp
+Servers and clients can make a new generation in the following
+way.
+All machines have loaded the old generation at startup and are
+operating normally.
+At designated intervals, each machine generates
+a new public/private key pair and makes links from the default file
+names to the new file names.
+The
+.Xr ntpd 8
+is then restarted
+and loads the new generation, with result clients no longer can
+authenticate correctly.
+The Autokey protocol is designed so that
+after a few minutes the clients time out and restart the protocol
+from the beginning, with result the new generation is loaded and
+operation continues as before.
+A similar procedure can be used for
+the agreement parameter file, but in this case precautions must be
+take to be sure that all machines with this file have the same
+copy.
+.Ss Authentication Commands
+.Bl -tag -width indent
+.It Ic autokey Op Ar logsec
+Specifies the interval between regenerations of the session key
+list used with the Autokey protocol.
+Note that the size of the key
+list for each association depends on this interval and the current
+poll interval.
+The default value is 12 (4096 s or about 1.1 hours).
+For poll intervals above the specified interval, a session key list
+with a single entry will be regenerated for every message
+sent.
+.It Ic controlkey Ar key
+Specifies the key identifier to use with the
+.Xr ntpq 8
+utility, which uses the standard
+protocol defined in RFC-1305.
+The
+.Ar key
+argument is
+the key identifier for a trusted key, where the value can be in the
+range 1 to 65534, inclusive.
+.It Xo Ic crypto
+.Op Cm flags Ar flags
+.Op Cm privatekey Ar file
+.Op Cm publickey Ar file
+.Op Cm dhparms Ar file
+.Op Cm leap Ar file
+.Xc
+This command requires the NTP daemon build process be
+configured with the RSA library.
+This command activates public-key
+cryptography and loads the required RSA private and public key
+files and the optional Diffie-Hellman agreement parameter file, if
+present.
+If one or more files are left unspecified, the default
+names are used as described below.
+Following are the
+subcommands:
+.Bl -tag -width indent
+.It Cm privatekey Ar file
+Specifies the location of the RSA private key file, which
+otherwise defaults to
+.Pa /usr/local/etc/ntpkey .
+.It Cm publickey Ar file
+Specifies the location of the RSA public key file, which
+otherwise defaults to
+.Pa /usr/local/etc/ntpkey_ Ns Ar host ,
+where
+.Ar host
+is the name of the generating machine.
+.It Cm dhparms Ar file
+Specifies the location of the Diffie-Hellman parameters file,
+which otherwise defaults to
+.Pa /usr/local/etc/ntpkey_dh .
+.It Cm leap Ar file
+Specifies the location of the leapsecond table file, which
+otherwise defaults to
+.Pa /usr/local/etc/ntpkey_leap .
+.El
+.It Ic keys Ar keyfile
+Specifies the location of the DES/MD5 private key file
+containing the keys and key identifiers used by
+.Xr ntpd 8 ,
+.Xr ntpq 8
+and
+.Xr ntpdc 8
+when operating in symmetric-key
+mode.
+.It Ic keysdir Ar path
+This command requires the NTP daemon build process be
+configured with the RSA library.
+It specifies the default directory
+path for the private key file, agreement parameters file and one or
+more public key files.
+The default when this command does not
+appear in the configuration file is
+.Pa /usr/local/etc .
+.It Ic requestkey Ar key
+Specifies the key identifier to use with the
+.Xr ntpdc 8
+utility program, which uses a
+proprietary protocol specific to this implementation of
+.Xr ntpd 8 .
+The
+.Ar key
+argument is a key identifier
+for the trusted key, where the value can be in the range 1 to
+65534, inclusive.
+.It Ic revoke Ar logsec
+Specifies the interval between re-randomization of certain
+cryptographic values used by the Autokey scheme, as a power of 2 in
+seconds.
+These values need to be updated frequently in order to
+deflect brute-force attacks on the algorithms of the scheme;
+however, updating some values is a relatively expensive operation.
+The default interval is 16 (65,536 s or about 18 hours).
+For poll
+intervals above the specified interval, the values will be updated
+for every message sent.
+.It Ic trustedkey Ar key ...
+Specifies the key identifiers which are trusted for the
+purposes of authenticating peers with symmetric-key cryptography,
+as well as keys used by the
+.Xr ntpq 8
+and
+.Xr ntpdc 8
+programs.
+The authentication procedures require that both the local
+and remote servers share the same key and key identifier for this
+purpose, although different keys can be used with different
+servers.
+The
+.Ar key
+arguments are 32-bit unsigned
+integers with values from 1 to 65,534.
+.El
+.Sh Monitoring Support
+.Xr ntpd 8
+includes a comprehensive monitoring facility suitable
+for continuous, long term recording of server and client
+timekeeping performance.
+See the
+.Ic statistics
+command below
+for a listing and example of each type of statistics currently
+supported.
+Statistic files are managed using file generation sets
+and scripts in the
+.Pa ./scripts
+directory of this distribution.
+Using
+these facilities and
+.Ux
+.Xr cron 8
+jobs, the data can be
+automatically summarized and archived for retrospective analysis.
+.Ss Monitoring Commands
+.Bl -tag -width indent
+.It Ic statistics Ar name ...
+Enables writing of statistics records.
+Currently, four kinds of
+.Ar name
+statistics are supported.
+.Bl -tag -width indent
+.It Cm loopstats
+Enables recording of loop filter statistics information.
+Each
+update of the local clock outputs a line of the following form to
+the file generation set named loopstats:
+.Bd -literal
+50935 75440.031 0.000006019 13.778190 0.000351733 0.013380 6
+.Ed
+.Pp
+The first two fields show the date (Modified Julian Day) and
+time (seconds and fraction past UTC midnight).
+The next five fields
+show time offset (seconds), frequency offset (parts per million -
+PPM), RMS jitter (seconds), Allan deviation (PPM) and clock
+discipline time constant.
+.It Cm peerstats
+Enables recording of peer statistics information.
+This includes
+statistics records of all peers of a NTP server and of special
+signals, where present and configured.
+Each valid update appends a
+line of the following form to the current element of a file
+generation set named peerstats:
+.Bd -literal
+48773 10847.650 127.127.4.1 9714 -0.001605 0.00000 0.00142
+.Ed
+.Pp
+The first two fields show the date (Modified Julian Day) and
+time (seconds and fraction past UTC midnight).
+The next two fields
+show the peer address in dotted-quad notation and status,
+respectively.
+The status field is encoded in hex in the format
+described in Appendix A of the NTP specification RFC 1305.
+The
+final three fields show the offset, delay and RMS jitter, all in
+seconds.
+.It Cm clockstats
+Enables recording of clock driver statistics information.
+Each
+update received from a clock driver appends a line of the following
+form to the file generation set named clockstats:
+.Bd -literal
+49213 525.624 127.127.4.1 93 226 00:08:29.606 D
+.Ed
+.Pp
+The first two fields show the date (Modified Julian Day) and
+time (seconds and fraction past UTC midnight).
+The next field shows
+the clock address in dotted-quad notation.
+The final field shows
+the last timecode received from the clock in decoded ASCII format,
+where meaningful.
+In some clock drivers a good deal of additional
+information can be gathered and displayed as well.
+See information
+specific to each clock for further details.
+.It Cm rawstats
+Enables recording of raw-timestamp statistics information.
+This
+includes statistics records of all peers of a NTP server and of
+special signals, where present and configured.
+Each NTP message
+received from a peer or clock driver appends a line of the
+following form to the file generation set named rawstats:
+.Bd -literal
+50928 2132.543 128.4.1.1 128.4.1.20 3102453281.584327000 3102453281.58622800031 02453332.540806000 3102453332.541458000
+.Ed
+The first two fields show the date (Modified Julian Day) and
+time (seconds and fraction past UTC midnight).
+The next two fields
+show the remote peer or clock address followed by the local address
+in dotted-quad notation.
+The final four fields show the originate,
+receive, transmit and final NTP timestamps in order.
+The timestamp
+values are as received and before processing by the various data
+smoothing and mitigation algorithms.
+.El
+.It Ic statsdir Ar directory_path
+Indicates the full path of a directory where statistics files
+should be created (see below).
+This keyword allows the (otherwise
+constant)
+.Ic filegen
+filename prefix to be modified for file
+generation sets, which is useful for handling statistics logs.
+.It Xo Ic filegen Ar name
+.Op Cm file Ar filename
+.Op Cm type Ar typename
+.Op Cm link \&| Cm nolink
+.Op Cm enable \&| Cm disable
+.Xc
+Configures setting of generation file set
+.Ar name .
+Generation file sets provide a means for handling files that are
+continuously growing during the lifetime of a server.
+Server
+statistics are a typical example for such files.
+Generation file
+sets provide access to a set of files used to store the actual
+data.
+At any time at most one element of the set is being written
+to.
+The type given specifies when and how data will be directed to
+a new element of the set.
+This way, information stored in elements
+of a file set that are currently unused are available for
+administrational operations without the risk of disturbing the
+operation of
+.Xr ntpd 8 .
+(Most important: they can be removed to
+free space for new data produced.)
+Note that this command can be sent from the
+.Xr ntpdc 8
+program running at a remote location.
+.Bl -tag -width indent
+.It Ar name
+This is the type of the statistics records, as shown in the
+.Ic statistics
+command.
+.It Cm file Ar filename
+This is the file name for the statistics records.
+Filenames of
+set members are built from three concatenated elements
+prefix, filename and
+suffix:
+.Bl -tag -width indent
+.It prefix
+This is a constant filename path.
+It is not subject to
+modifications via the
+.Ic filegen
+option.
+It is defined by the
+server, usually specified as a compile-time constant.
+It may,
+however, be configurable for individual file generation sets via
+other commands.
+For example, the prefix used with
+.Cm loopstats
+and
+.Cm peerstats
+generation can be
+configured using the
+.Ic statsdir
+option explained above.
+.It filename
+This string is directly concatenated to the prefix mentioned
+above (no intervening
+.Ql /
+(slash)).
+This can be modified
+using the
+.Ar file
+argument to the
+.Ic filegen
+statement.
+No
+.Ql \&..
+elements are allowed in this component to prevent
+filenames referring to parts outside the filesystem hierarchy
+denoted by prefix.
+.It suffix
+This part is reflects individual elements of a file set.
+It is
+generated according to the type of a file set.
+.El
+.It Cm type Ar typename
+A file generation set is characterized by its type.
+The
+following types are supported:
+.Bl -tag -width indent
+.It none
+The file set is actually a single plain file.
+.It pid
+One element of file set is used per incarnation of a
+.Xr ntpd 8
+server.
+This type does not perform any changes to
+file set members during runtime, however it provides an easy way of
+separating files belonging to different
+.Xr ntpd 8
+server
+incarnations.
+The set member filename is built by appending a
+.Ql \&.
+(dot) to concatenated prefix and filename
+strings, and appending the decimal representation of the process ID
+of the
+.Xr ntpd 8
+server process.
+.It day
+One file generation set element is created per day.
+A day is
+defined as the period between 00:00 and 24:00 UTC.
+The file set
+member suffix consists of a
+.Ql \&.
+(dot) and a day
+specification in the form
+.Ar YYYYMMdd .
+.Ar YYYY
+is a 4-digit year
+number (e.g., 1992).
+.Ar MM
+is a two digit month number.
+.Ar dd
+is a two digit day number.
+Thus, all information
+written at 10 December 1992 would end up in a file named
+.Sm off
+.Pa Ar prefix / Ar filename / 19921210 .
+.Sm on
+.It week
+Any file set member contains data related to a certain week of
+a year.
+The term week is defined by computing day-of-year modulo 7.
+Elements of such a file generation set are distinguished by
+appending the following suffix to the file set filename base: A
+dot, a 4-digit year number, the letter
+Ql W ,
+and a 2-digit
+week number.
+For example, information from January, 10th 1992 would
+end up in a file with suffix
+.Pa .1992W1 .
+.It month
+One generation file set element is generated per month.
+The
+file name suffix consists of a dot, a 4-digit year number, and a
+2-digit month.
+.It year
+One generation file element is generated per year.
+The filename
+suffix consists of a dot and a 4 digit year number.
+.It age
+This type of file generation sets changes to a new element of
+the file set every 24 hours of server operation.
+The filename
+suffix consists of a dot, the letter
+.Ql a ,
+and an 8-digit
+number.
+This number is taken to be the number of seconds the server
+is running at the start of the corresponding 24-hour period.
+Information is only written to a file generation by specifying
+.Ic enable ;
+output is prevented by specifying
+.Ic disable .
+.El
+.It Cm link \&| Cm nolink
+It is convenient to be able to access the current element of a
+file generation set by a fixed name.
+This feature is enabled by
+specifying
+.Cm link
+and disabled using
+.Cm nolink .
+If
+.Cm link
+is specified, a hard link from the current file set
+element to a file without suffix is created.
+When there is already
+a file with this name and the number of links of this file is one,
+it is renamed appending a dot, the letter
+.Ql C ,
+and the pid
+of the
+.Xr ntpd 8
+server process.
+When the number of links is
+greater than one, the file is unlinked.
+This allows the current
+file to be accessed by a constant name.
+.It Cm enable \&| Cm disable
+Enables or disables the recording function.
+.El
+.El
+.Sh Access Control Support
+.Xr ntpd 8
+implements a general purpose address-and-mask based
+restriction list.
+The list is sorted by address and by mask, and
+the list is searched in this order for matches, with the last match
+found defining the restriction flags associated with the incoming
+packets.
+The source address of incoming packets is used for the
+match, with the 32- bit address being and'ed with the mask
+associated with the restriction entry and then compared with the
+entry's address (which has also been and'ed with the mask) to look
+for a match.
+Additional information and examples can be found in the
+.Qq "Notes on Configuring NTP and Setting up a NTP Subnet"
+page.
+.Pp
+The restriction facility was implemented in conformance with the
+access policies for the original NSFnet backbone time servers.
+While this facility may be otherwise useful for keeping unwanted or
+broken remote time servers from affecting your own, it should not
+be considered an alternative to the standard NTP authentication
+facility.
+Source address based restrictions are easily circumvented
+by a determined cracker.
+.Ss The Kiss-of-Death Packet
+Ordinarily, packets denied service are simply dropped with no
+further action except incrementing statistics counters.
+Sometimes a
+more proactive response is needed, such as a server message that
+explicitly requests the client to stop sending and leave a message
+for the system operator.
+A special packet format has been created
+for this purpose called the kiss-of-death packet.
+If the
+.Cm kod
+flag is set and either service is denied or the client
+limit is exceeded, the server returns the packet and sets the
+leap bits unsynchronized, stratum zero and the ASCII string "DENY"
+in the reference source identifier field.
+If the
+.Cm kod
+flag
+is not set, the server simply drops the packet.
+.Pp
+A client or peer receiving a kiss-of-death packet performs a set
+of sanity checks to minimize security exposure.
+If this is the
+first packet received from the server, the client assumes an access
+denied condition at the server.
+It updates the stratum and
+reference identifier peer variables and sets the access denied
+(test 4) bit in the peer flash variable.
+If this bit is set, the
+client sends no packets to the server.
+If this is not the first
+packet, the client assumes a client limit condition at the server,
+but does not update the peer variables.
+In either case, a message
+is sent to the system log.
+.Ss Access Control Commands
+.Bl -tag -width indent
+.It Xo Ic restrict numeric_address
+.Op Cm mask Ar numeric_mask
+.Op Ar flag ...
+.Xc
+The
+.Ar numeric_address
+argument, expressed in
+dotted- quad form, is the address of an host or network.
+The
+.Cm mask ,
+also expressed in dotted-quad form,
+defaults to 255.255.255.255, meaning that the
+.Ar numeric_address
+is treated as the address of an
+individual host.
+A default entry (address 0.0.0.0, mask
+0.0.0.0) is always included and, given the sort algorithm,
+is always the first entry in the list.
+Note that, while
+.Ar numeric_address
+is normally given in dotted-quad
+format, the text string
+.Ql default ,
+with no mask option, may
+be used to indicate the default entry.
+In the current implementation,
+.Cm flag
+always
+restricts access, i.e., an entry with no flags indicates that free
+access to the server is to be given.
+The flags are not orthogonal,
+in that more restrictive flags will often make less restrictive
+ones redundant.
+The flags can generally be classed into two
+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 Cm kod
+If access is denied, send a kiss-of-death packet.
+.It Cm ignore
+Ignore all packets from hosts which match this entry.
+If this
+flag is specified neither queries nor time server polls will be
+responded to.
+.It Cm noquery
+Ignore all NTP mode 6 and 7 packets (i.e. information queries
+and configuration requests) from the source.
+Time service is not
+affected.
+.It Cm nomodify
+Ignore all NTP mode 6 and 7 packets which attempt to modify the
+state of the server (i.e. run time reconfiguration).
+Queries which
+return information are permitted.
+.It Cm notrap
+Decline to provide mode 6 control message trap service to
+matching hosts.
+The trap service is a subsystem of the mode 6
+control message protocol which is intended for use by remote event
+logging programs.
+.It Cm lowpriotrap
+Declare traps set by matching hosts to be low priority.
+The
+number of traps a server can maintain is limited (the current limit
+is 3).
+Traps are usually assigned on a first come, first served
+basis, with later trap requestors being denied service.
+This flag
+modifies the assignment algorithm by allowing low priority traps to
+be overridden by later requests for normal priority traps.
+.It Cm noserve
+Ignore NTP packets whose mode is other than 6 or 7.
+In effect,
+time service is denied, though queries may still be permitted.
+.It Cm nopeer
+Provide stateless time service to polling hosts, but do not
+allocate peer memory resources to these hosts even if they
+otherwise might be considered useful as future synchronization
+partners.
+.It Cm notrust
+Treat these hosts normally in other respects, but never use
+them as synchronization sources.
+.It Cm limited
+These hosts are subject to limitation of number of clients from
+the same net.
+Net in this context refers to the IP notion of net
+(class A, class B, class C, etc.).
+Only the first
+.Va client_limit
+hosts that have shown up at the server and
+that have been active during the last
+.Va client_limit_period
+seconds are accepted.
+Requests from other clients from the same net
+are rejected.
+Only time request packets are taken into account.
+Query packets sent by the
+.Xr ntpq 8
+and
+.Xr ntpdc 8
+programs
+are not subject to these limits.
+A history of clients is kept using
+the monitoring capability of
+.Xr ntpd 8 .
+Thus, monitoring is
+always active as long as there is a restriction entry with the
+.Cm limited
+flag.
+.It Cm ntpport
+This is actually a match algorithm modifier, rather than a
+restriction flag.
+Its presence causes the restriction entry to be
+matched only if the source port in the packet is the standard NTP
+UDP port (123).
+Both
+.Cm ntpport
+and
+.Cm non-ntpport
+may
+be specified.
+The
+.Cm ntpport
+is considered more specific and
+is sorted later in the list.
+.It Cm version
+Ignore these hosts if not the current NTP version.
+.El
+.Pp
+Default restriction list entries, with the flags
+.Cm ignore ,
+.Cm interface ,
+.Cm ntpport ,
+for each of the local host's interface
+addresses are inserted into the table at startup to prevent the
+server from attempting to synchronize to its own time.
+A default
+entry is also always present, though if it is otherwise
+unconfigured; no flags are associated with the default entry (i.e.,
+everything besides your own NTP server is unrestricted).
+.It Ic clientlimit Ar limit
+Set the
+.Va client_limit
+variable, which limits the number
+of simultaneous access-controlled clients.
+The default value for
+this variable is 3.
+.It Ic clientperiod Ar period
+Set the
+.Va client_limit_period
+variable, which specifies
+the number of seconds after which a client is considered inactive
+and thus no longer is counted for client limit restriction.
+The
+default value for this variable is 3600 seconds.
+.El
+.Sh Reference Clock Support
+The NTP Version 4 daemon supports some three dozen different radio,
+satellite and modem reference clocks plus a special pseudo-clock
+used for backup or when no other clock source is available.
+Detailed descriptions of individual device drivers and options can
+be found in the
+.Qq "Reference Clock Drivers"
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp ) .
+Additional information can be found in the pages linked
+there, including the
+.Qq "Debugging Hints for Reference Clock Drivers"
+and
+.Qq "How To Write a Reference Clock Driver"
+pages.
+In addition, support for a PPS
+signal is available as described in the
+.Qq "Pulse-per-second (PPS) Signal Interfacing"
+page.
+Many
+drivers support special line discipline/streams modules which can
+significantly improve the accuracy using the driver.
+These are
+described in the
+.Qq "Line Disciplines and Streams Drivers"
+page.
+.Pp
+A reference clock will generally (though not always) be a radio
+timecode receiver which is synchronized to a source of standard
+time such as the services offered by the NRC in Canada and NIST and
+USNO in the US.
+The interface between the computer and the timecode
+receiver is device dependent, but is usually a serial port.
+A
+device driver specific to each reference clock must be selected and
+compiled in the distribution; however, most common radio, satellite
+and modem clocks are included by default.
+Note that an attempt to
+configure a reference clock when the driver has not been compiled
+or the hardware port has not been appropriately configured results
+in a scalding remark to the system log file, but is otherwise non
+hazardous.
+.Pp
+For the purposes of configuration,
+.Xr ntpd 8
+treats
+reference clocks in a manner analogous to normal NTP peers as much
+as possible.
+Reference clocks are identified by a syntactically
+correct but invalid IP address, in order to distinguish them from
+normal NTP peers.
+Reference clock addresses are of the form
+.Sm off
+.Li 127.127. Ar t . Ar u ,
+.Sm on
+where
+.Ar t
+is an integer
+denoting the clock type and
+.Ar u
+indicates the unit
+number in the range 0-3.
+While it may seem overkill, it is in fact
+sometimes useful to configure multiple reference clocks of the same
+type, in which case the unit numbers must be unique.
+.Pp
+The
+.Ic server
+command is used to configure a reference
+clock, where the
+.Ar address
+argument in that command
+is the clock address.
+The
+.Cm key ,
+.Cm version
+and
+.Cm ttl
+options are not used for reference clock support.
+The
+.Cm mode
+option is added for reference clock support, as
+described below.
+The
+.Cm prefer
+option can be useful to
+persuade the server to cherish a reference clock with somewhat more
+enthusiasm than other reference clocks or peers.
+Further
+information on this option can be found in the
+.Qq "Mitigation Rules and the prefer Keyword"
+page.
+The
+.Cm minpoll
+and
+.Cm maxpoll
+options have
+meaning only for selected clock drivers.
+See the individual clock
+driver document pages for additional information.
+.Pp
+The
+.Ic fudge
+command is used to provide additional
+information for individual clock drivers and normally follows
+immediately after the
+.Ic server
+command.
+The
+.Ar address
+argument specifies the clock address.
+The
+.Cm refid
+and
+.Cm stratum
+options can be used to
+override the defaults for the device.
+There are two optional
+device-dependent time offsets and four flags that can be included
+in the
+.Ic fudge
+command as well.
+.Pp
+The stratum number of a reference clock is by default zero.
+Since the
+.Xr ntpd 8
+daemon adds one to the stratum of each
+peer, a primary server ordinarily displays an external stratum of
+one.
+In order to provide engineered backups, it is often useful to
+specify the reference clock stratum as greater than zero.
+The
+.Cm stratum
+option is used for this purpose.
+Also, in cases
+involving both a reference clock and a pulse-per-second (PPS)
+discipline signal, it is useful to specify the reference clock
+identifier as other than the default, depending on the driver.
+The
+.Cm refid
+option is used for this purpose.
+Except where noted,
+these options apply to all clock drivers.
+.Ss Reference Clock Commands
+.Bl -tag -width indent
+.It Xo Ic server
+.Sm off
+.Li 127.127. Ar t . Ar u
+.Sm on
+.Op Cm prefer
+.Op Cm mode Ar int
+.Op Cm minpoll Ar int
+.Op Cm maxpoll Ar int
+.Xc
+This command can be used to configure reference clocks in
+special ways.
+The options are interpreted as follows:
+.Bl -tag -width indent
+.It Cm prefer
+Marks the reference clock as preferred.
+All other things being
+equal, this host will be chosen for synchronization among a set of
+correctly operating hosts.
+See the
+.Qq "Mitigation Rules and the prefer Keyword"
+page for further
+information.
+.It Cm mode Ar int
+Specifies a mode number which is interpreted in a
+device-specific fashion.
+For instance, it selects a dialing
+protocol in the ACTS driver and a device subtype in the
+parse
+drivers.
+.It Cm minpoll Ar int
+.It Cm maxpoll Ar int
+These options specify the minimum and maximum polling interval
+for reference clock messages, in seconds to the power of two.
+For
+most directly connected reference clocks, both
+.Cm minpoll
+and
+.Cm maxpoll
+default to 6 (64 s).
+For modem reference clocks,
+.Cm minpoll
+defaults to 10 (17.1 m) and
+.Cm maxpoll
+defaults to 14 (4.5 h).
+The allowable range is 4 (16 s) to 17 (36.4 h) inclusive.
+.El
+.It Xo Ic fudge
+.Sm off
+.Li 127.127. Ar t . Ar u
+.Sm on
+.Op Cm time1 Ar sec
+.Op Cm time2 Ar sec
+.Op Cm stratum Ar int
+.Op Cm refid Ar string
+.Op Cm mode Ar int
+.Op Cm flag1 Cm 0 \&| Cm 1
+.Op Cm flag2 Cm 0 \&| Cm 1
+.Op Cm flag3 Cm 0 \&| Cm 1
+.Op Cm flag4 Cm 0 \&| Cm 1
+.Xc
+This command can be used to configure reference clocks in
+special ways.
+It must immediately follow the
+.Ic server
+command which configures the driver.
+Note that the same capability
+is possible at run time using the
+.Xr ntpdc 8
+program.
+The options are interpreted as
+follows:
+.Bl -tag -width indent
+.It Cm time1 Ar sec
+Specifies a constant to be added to the time offset produced by
+the driver, a fixed-point decimal number in seconds.
+This is used
+as a calibration constant to adjust the nominal time offset of a
+particular clock to agree with an external standard, such as a
+precision PPS signal.
+It also provides a way to correct a
+systematic error or bias due to serial port or operating system
+latencies, different cable lengths or receiver internal delay.
+The
+specified offset is in addition to the propagation delay provided
+by other means, such as internal DIPswitches.
+Where a calibration
+for an individual system and driver is available, an approximate
+correction is noted in the driver documentation pages.
+Note: in order to facilitate calibration when more than one
+radio clock or PPS signal is supported, a special calibration
+feature is available.
+It takes the form of an argument to the
+.Ic enable
+command described in
+.Sx Miscellaneous Options
+page and operates as described in the
+.Qq "Reference Clock Drivers"
+page.
+.It Cm time2 Ar secs
+Specifies a fixed-point decimal number in seconds, which is
+interpreted in a driver-dependent way.
+See the descriptions of
+specific drivers in the
+.Qq "reference clock drivers"
+page.
+.It Cm stratum Ar int
+Specifies the stratum number assigned to the driver, an integer
+between 0 and 15.
+This number overrides the default stratum number
+ordinarily assigned by the driver itself, usually zero.
+.It Cm refid Ar string
+Specifies an ASCII string of from one to four characters which
+defines the reference identifier used by the driver.
+This string
+overrides the default identifier ordinarily assigned by the driver
+itself.
+.It Cm mode Ar int
+Specifies a mode number which is interpreted in a
+device-specific fashion.
+For instance, it selects a dialing
+protocol in the ACTS driver and a device subtype in the
+parse
+drivers.
+.It Cm flag1 Cm 0 \&| Cm 1
+.It Cm flag2 Cm 0 \&| Cm 1
+.It Cm flag3 Cm 0 \&| Cm 1
+.It Cm flag4 Cm 0 \&| Cm 1
+These four flags are used for customizing the clock driver.
+The
+interpretation of these values, and whether they are used at all,
+is a function of the particular clock driver.
+However, by
+convention
+.Cm flag4
+is used to enable recording monitoring
+data to the
+.Cm clockstats
+file configured with the
+.Ic filegen
+command.
+Further information on the
+.Ic filegen
+command can be found in
+.Sx Monitoring Options .
+.El
+.El
+.Sh Miscellaneous Options
+.Bl -tag -width indent
+.It Ic broadcastdelay Ar seconds
+The broadcast and multicast modes require a special calibration
+to determine the network delay between the local and remote
+servers.
+Ordinarily, this is done automatically by the initial
+protocol exchanges between the client and server.
+In some cases,
+the calibration procedure may fail due to network or server access
+controls, for example.
+This command specifies the default delay to
+be used under these circumstances.
+Typically (for Ethernet), a
+number between 0.003 and 0.007 seconds is appropriate.
+The default
+when this command is not used is 0.004 seconds.
+.It Ic driftfile Ar driftfile
+This command specifies the name of the file used to record the
+frequency offset of the local clock oscillator.
+If the file exists,
+it is read at startup in order to set the initial frequency offset
+and then updated once per hour with the current frequency offset
+computed by the daemon.
+If the file does not exist or this command
+is not given, the initial frequency offset is assumed zero.
+In this
+case, it may take some hours for the frequency to stabilize and the
+residual timing errors to subside.
+.Pp
+The file format consists of a single line containing a single
+floating point number, which records the frequency offset measured
+in parts-per-million (PPM).
+The file is updated by first writing
+the current drift value into a temporary file and then renaming
+this file to replace the old version.
+This implies that
+.Xr ntpd 8
+must have write permission for the directory the
+drift file is located in, and that filesystem links, symbolic or
+otherwise, should be avoided.
+.It Xo Ic enable
+.Oo
+.Cm auth | Cm bclient |
+.Cm calibrate | Cm kernel |
+.Cm monitor | Cm ntp |
+.Cm stats
+.Oc
+.Xc
+.It Xo Ic disable
+.Oo
+.Cm auth | Cm bclient |
+.Cm calibrate | Cm kernel |
+.Cm monitor | Cm ntp |
+.Cm stats
+.Oc
+.Xc
+Provides a way to enable or disable various server options.
+Flags not mentioned are unaffected.
+Note that all of these flags
+can be controlled remotely using the
+.Xr ntpdc 8
+utility program.
+.Bl -tag -width indent
+.It Cm bclient
+When enabled, this is identical to the
+.Ic broadcastclient
+command.
+The default for this flag is
+.Ic disable .
+.It Cm calibrate
+Enables the calibration facility, which automatically adjusts
+the
+.Ic time1
+values for each clock driver to display the same
+offset as the currently selected source or kernel discipline
+signal.
+See the
+.Qq "Reference Clock Drivers"
+page
+for further information.
+The default for this flag is
+.Ic disable .
+.It Cm kernel
+Enables the precision-time kernel support for the
+.Xr adjtime 2
+system call, if implemented.
+Ordinarily,
+support for this routine is detected automatically when the NTP
+daemon is compiled, so it is not necessary for the user to worry
+about this flag.
+It is provided primarily so that this support
+can be disabled during kernel development.
+The default for this
+flag is
+.Ic enable .
+.It Cm monitor
+Enables the monitoring facility.
+See the
+.Xr ntpdc 8
+program
+and the
+.Ic monlist
+command or further information.
+The
+default for this flag is
+.Ic enable .
+.It Cm ntp
+Enables the server to adjust its local clock by means of NTP.
+If disabled, the local clock free-runs at its intrinsic time and
+frequency offset.
+This flag is useful in case the local clock is
+controlled by some other device or protocol and NTP is used only to
+provide synchronization to other clients.
+In this case, the local
+clock driver can be used to provide this function and also certain
+time variables for error estimates and leap-indicators.
+See the
+.Qq "Reference Clock Drivers"
+page for further
+information.
+The default for this flag is
+.Ic enable .
+.It Cm stats
+Enables the statistics facility.
+See the
+.Qq "Monitoring Options"
+page for further information.
+The default for this flag is
+.Ic enable .
+.El
+.It Ic logconfig Ar configkeyword
+This command controls the amount and type of output written to
+the system
+.Xr syslog 3
+facility or the alternate
+.Ic logfile
+log file.
+By default, all output is turned on.
+All
+.Ar configkeyword
+keywords can be prefixed with
+.Ql = ,
+.Ql +
+and
+.Ql - ,
+where
+.Ql =
+sets the
+.Xr syslog 3
+priority mask,
+.Ql +
+adds and
+.Ql -
+removes
+messages.
+.Xr syslog 3
+messages can be controlled in four
+classes
+.Po
+.Cm clock ,
+.Cm peer ,
+.Cm sys
+and
+.Cm sync
+.Pc .
+Within these classes four types of messages can be
+controlled.
+Informational messages
+.Pq Cm info
+control configuration
+information.
+Event messages
+.Pq Cm events
+control logging of
+events (reachability, synchronization, alarm conditions).
+Statistical output is controlled with the
+.Cm statistics
+keyword.
+The final message group is the status messages.
+This
+describes mainly the synchronizations status.
+Configuration
+keywords are formed by concatenating the message class with the
+event class.
+The
+.Cm all
+prefix can be used instead of a
+message class.
+A message class may also be followed by the
+.Cm all
+keyword to enable/disable all messages of the
+respective message class.
+Thus, a minimal log configuration could look like this:
+.Bd -literal
+logconfig=syncstatus +sysevents
+.Ed
+.Pp
+This would just list the synchronizations state of
+.Xr ntpd 8
+and the major system events.
+For a simple reference server, the
+following minimum message configuration could be useful:
+.Bd -literal
+logconfig=syncall +clockall
+.Ed
+.Pp
+This configuration will list all clock information and
+synchronization information.
+All other events and messages about
+peers, system events and so on is suppressed.
+.It Ic logfile Ar logfile
+This command specifies the location of an alternate log file to
+be used instead of the default system
+.Xr syslog 3
+facility.
+.It Ic setvar Ar variable Op Cm default
+This command adds an additional system variable.
+These
+variables can be used to distribute additional information such as
+the access policy.
+If the variable of the form
+.Sm off
+.Va name = Ar value
+.Sm on
+is followed by the
+.Cm default
+keyword, the
+variable will be listed as part of the default system variables
+.Po
+.Xr ntpq 8
+.Ic rv
+command
+.Pc ) .
+These additional variables serve
+informational purposes only.
+They are not related to the protocol
+other that they can be listed.
+The known protocol variables will
+always override any variables defined via the
+.Ic setvar
+mechanism.
+There are three special variables that contain the names
+of all variable of the same group.
+The
+.Va sys_var_list
+holds
+the names of all system variables.
+The
+.Va peer_var_list
+holds
+the names of all peer variables and the
+.Va clock_var_list
+holds the names of the reference clock variables.
+.It Xo Ic tinker
+.Oo
+.Cm step Ar step |
+.Cm panic Ar panic |
+.Cm dispersion Ar dispersion |
+.Cm stepout Ar stepout |
+.Cm minpoll Ar minpoll |
+.Cm allan Ar allan |
+.Cm huffpuff Ar huffpuff
+.Oc
+.Xc
+This command can be used to alter several system variables in
+very exceptional circumstances.
+It should occur in the
+configuration file before any other configuration options.
+The
+default values of these variables have been carefully optimized for
+a wide range of network speeds and reliability expectations.
+In
+general, they interact in intricate ways that are hard to predict
+and some combinations can result in some very nasty behavior.
+Very
+rarely is it necessary to change the default values; but, some
+folks can't resist twisting the knobs anyway and this command is
+for them.
+Emphasis added: twisters are on their own and can expect
+no help from the support group.
+.Pp
+All arguments are in floating point seconds or seconds per
+second.
+The
+.Ar minpoll
+argument is an integer in seconds to
+the power of two.
+The variables operate as follows:
+.Bl -tag -width indent
+.It Cm step Ar step
+The argument becomes the new value for the step threshold,
+normally 0.128 s.
+If set to zero, step adjustments will never
+occur.
+In general, if the intent is only to avoid step adjustments,
+the step threshold should be left alone and the
+.Fl x
+command
+line option be used instead.
+.It Cm panic Ar panic
+The argument becomes the new value for the panic threshold,
+normally 1000 s.
+If set to zero, the panic sanity check is disabled
+and a clock offset of any value will be accepted.
+.It Cm dispersion Ar dispersion
+The argument becomes the new value for the dispersion increase
+rate, normally .000015.
+.It Cm stepout Ar stepout
+The argument becomes the new value for the watchdog timeout,
+normally 900 s.
+.It Cm minpoll Ar minpoll
+The argument becomes the new value for the minimum poll
+interval used when configuring multicast client, manycast client
+and , symmetric passive mode association.
+The value defaults to 6
+(64 s) and has a lower limit of 4 (16 s).
+.It Cm allan Ar allan
+The argument becomes the new value for the minimum Allan
+intercept, which is a parameter of the PLL/FLL clock discipline
+algorithm.
+The value defaults to 1024 s, which is also the lower
+limit.
+.It Cm huffpuff Ar huffpuff
+The argument becomes the new value for the experimental
+huff-n'-puff filter span, which determines the most recent interval
+the algorithm will search for a minimum delay.
+The lower limit is
+900 s (15 m), but a more reasonable value is 7200 (2 hours).
+There
+is no default, since the filter is not enabled unless this command
+is given.
+.El
+.It Xo Ic trap Ar host_address
+.Op Cm port Ar port_number
+.Op Cm interface Ar interface_address
+.Xc
+This command configures a trap receiver at the given host
+address and port number for sending messages with the specified
+local interface address.
+If the port number is unspecified, a value
+of 18447 is used.
+If the interface address is not specified, the
+message is sent with a source address of the local interface the
+message is sent through.
+Note that on a multihomed host the
+interface used may vary from time to time with routing changes.
+.Pp
+The trap receiver will generally log event messages and other
+information from the server in a log file.
+While such monitor
+programs may also request their own trap dynamically, configuring a
+trap receiver will ensure that no messages are lost when the server
+is started.
+.El
+.Sh FILES
+.Bl -tag -width /etc/ntp.drift -compact
+.It Pa /etc/ntp.conf
+the default name of the configuration file
+.It Pa ntp.keys
+private MD5 keys
+.It Pa ntpkey
+RSA private key
+.It Pa ntpkey_ Ns Ar host
+RSA public key
+.It Pa ntp_dh
+Diffie-Hellman agreement parameters
+.El
+.Sh SEE ALSO
+.Xr ntpd 8 ,
+.Xr ntpdc 8 ,
+.Xr ntpq 8
+.Pp
+In addition to the manual pages provided,
+comprehensive documentation is available on the world wide web
+at
+.Li http://www.ntp.org/ .
+A snapshot of this documentation is available in HTML format in
+.Pa /usr/share/doc/ntp .
+.Rs
+.%A David L. Mills
+.%T Network Time Protocol (Version 3)
+.%O RFC1305
+.Re
+.Sh BUGS
+The syntax checking is not picky; some combinations of
+ridiculous and even hilarious options and modes may not be
+detected.
+.Pp
+The
+.Pa ntpkey_ Ns Ar host
+files are really digital
+certificates.
+These should be obtained via secure directory
+services when they become universally available.
diff --git a/usr.sbin/ntp/doc/ntp.keys.5 b/usr.sbin/ntp/doc/ntp.keys.5
new file mode 100644
index 0000000..a59205b
--- /dev/null
+++ b/usr.sbin/ntp/doc/ntp.keys.5
@@ -0,0 +1,120 @@
+.\"
+.\" $FreeBSD$
+.\"
+.Dd January 13, 2000
+.Dt NTP.KEYS 5
+.Os
+.Sh NAME
+.Nm ntp.keys
+.Nd NTP daemon key file format
+.Sh SYNOPSIS
+.Nm /etc/ntp.keys
+.Sh DESCRIPTION
+Following is a description of the format of NTP key files.
+For a description of the use of these files, see the
+.Qq Authentication Support
+section of the
+.Xr ntp.conf 5
+page.
+.Pp
+In the case of DES, the keys are 56 bits long with,
+depending on type, a parity check on each byte.
+In the case of MD5, the keys are 64 bits (8 bytes).
+.Xr ntpd 8
+reads its keys from a file specified using the
+.Fl k
+command line option or the
+.Ic keys
+statement in the configuration file.
+While key number 0 is fixed by the NTP standard
+(as 56 zero bits)
+and may not be changed,
+one or more of the keys numbered 1 through 15
+may be arbitrarily set in the keys file.
+.Pp
+The key file uses the same comment conventions
+as the configuration file.
+Key entries use a fixed format of the form
+.Pp
+.D1 Ar keyno type key
+.Pp
+where
+.Ar keyno
+is a positive integer,
+.Ar type
+is a single character which defines the key format,
+and
+.Ar key
+is the key itself.
+.Pp
+The
+.Ar key
+may be given in one of four different formats,
+controlled by the
+.Ar type
+character.
+The four key types, and corresponding formats,
+are listed following.
+.Bl -tag -width X
+.It Li S
+The key is a 64-bit hexadecimal number in the format
+specified in the DES specification;
+that is, the high order seven bits of each octet are used
+to form the 56-bit key
+while the low order bit of each octet is given a value
+such that odd parity is maintained for the octet.
+Leading zeroes must be specified
+(i.e., the key must be exactly 16 hex digits long)
+and odd parity must be maintained.
+Hence a zero key, in standard format, would be given as
+.Ql 0101010101010101 .
+.It Li N
+The key is a 64-bit hexadecimal number in the format
+specified in the NTP standard.
+This is the same as the DES format,
+except the bits in each octet have been rotated one bit right
+so that the parity bit is now the high order bit of the octet.
+Leading zeroes must be specified and odd parity must be maintained.
+A zero key in NTP format would be specified as
+.Ql 8080808080808080 .
+.It Li A
+The key is a 1-to-8 character ASCII string.
+A key is formed from this by using the low order 7 bits
+of each ASCII character in the string,
+with zeroes added on the right
+when necessary to form a full width 56-bit key,
+in the same way that encryption keys are formed from
+.Ux
+passwords.
+.It Li M
+The key is a 1-to-8 character ASCII string,
+using the MD5 authentication scheme.
+Note that both the keys and the authentication schemes (DES or MD5)
+must be identical between a set of peers sharing the same key number.
+.El
+.Pp
+Note that the keys used by the
+.Xr ntpq 8
+and
+.Xr ntpdc 8
+programs are checked against passwords
+requested by the programs and entered by hand,
+so it is generally appropriate to specify these keys in ASCII format.
+.Sh FILES
+.Bl -tag -width /etc/ntp.drift -compact
+.It Pa /etc/ntp.keys
+the default name of the configuration file
+.El
+.Sh SEE ALSO
+.Xr ntp.conf 5 ,
+.Xr ntpd 8 ,
+.Xr ntpdate 8 ,
+.Xr ntpdc 8
+.Sh BUGS
+.Xr ntpd 8
+has gotten rather fat.
+While not huge, it has gotten larger than might
+be 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/ntp/doc/ntpd.8 b/usr.sbin/ntp/doc/ntpd.8
new file mode 100644
index 0000000..f4107aa
--- /dev/null
+++ b/usr.sbin/ntp/doc/ntpd.8
@@ -0,0 +1,585 @@
+.\"
+.\" $FreeBSD$
+.\"
+.Dd August 2, 2001
+.Dt NTPD 8
+.Os
+.Sh NAME
+.Nm ntpd
+.Nd Network Time Protocol (NTP) daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl aAbdgLmnPqx
+.Op Fl c Ar conffile
+.Op Fl D Ar level
+.Op Fl f Ar driftfile
+.Op Fl k Ar keyfile
+.Op Fl l Ar logfile
+.Op Fl N Cm high
+.Op Fl p Ar pidfile
+.Op Fl r Ar broadcastdelay
+.Op Fl s Ar statsdir
+.Op Fl t Ar key
+.Op Fl v Ar variable
+.Op Fl V Ar variable
+.Sh DESCRIPTION
+The
+.Nm
+program is an operating system daemon which sets
+and maintains the system time of day in synchronism with Internet
+standard time servers.
+It is a complete implementation of the
+Network Time Protocol (NTP) version 4, but also retains
+compatibility with version 3, as defined by RFC-1305, and version 1
+and 2, as defined by RFC-1059 and RFC-1119, respectively.
+.Pp
+.Nm
+does most computations in 64-bit floating point
+arithmetic and does relatively clumsy 64-bit fixed point operations
+only when necessary to preserve the ultimate precision, about 232
+picoseconds.
+While the ultimate precision, is not achievable with
+ordinary workstations and networks of today, it may be required
+with future gigahertz CPU clocks and gigabit LANs.
+.Pp
+Ordinarily,
+.Nm
+reads the
+.Xr ntp.conf 5
+configuration file at startup time in order to determine the
+synchronization sources and operating modes.
+It is also possible to
+specify a working, although limited, configuration entirely on the
+command line, obviating the need for a configuration file.
+This may
+be particularly useful when the local host is to be configured as a
+broadcast/multicast client, with all peers being determined by
+listening to broadcasts at run time.
+.Pp
+If NetInfo support is built into
+.Nm ,
+then
+.Nm
+will attempt to read its configuration from the
+NetInfo if the default
+.Xr ntp.conf 5
+file cannot be read and no file is
+specified by the
+.Fl c
+option.
+.Pp
+Various internal
+.Nm
+variables can be displayed and
+configuration options altered while the
+.Nm
+is running
+using the
+.Xr ntpq 8
+and
+.Xr ntpdc 8
+utility programs.
+.Pp
+When
+.Nm
+starts it looks at the value of
+.Xr umask 2 ,
+and if zero
+.Nm
+will set the
+.Xr umask 2
+to 022.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl a
+Enable authentication mode (default).
+.It Fl A
+Disable authentication mode.
+.It Fl b
+Synchronize using NTP broadcast messages.
+.It Fl c Ar conffile
+Specify the name and path of the configuration file.
+(Disable
+netinfo?)
+.It Fl d
+Specify debugging mode.
+This flag may occur multiple times,
+with each occurrence indicating greater detail of display.
+.It Fl D Ar level
+Specify debugging level directly.
+.It Fl f Ar driftfile
+Specify the name and path of the drift file.
+.It Fl g
+Normally,
+.Nm
+exits if the offset exceeds the sanity
+limit, which is 1000 s by default.
+If the sanity limit is set to
+zero, no sanity checking is performed and any offset is acceptable.
+This option overrides the limit and allows the time to be set to
+any value without restriction; however, this can happen only once.
+After that,
+.Nm
+will exit if the limit is exceeded.
+This
+option can be used with the
+.Fl q
+option.
+.It Fl k Ar keyfile
+Specify the name and path of the file containing the NTP
+authentication keys.
+.It Fl l Ar logfile
+Specify the name and path of the log file.
+The default is the
+system log facility.
+.It Fl L
+Listen to virtual IPs.
+.It Fl m
+Synchronize using NTP multicast messages on the IP multicast
+group address 224.0.1.1 (requires multicast kernel).
+.It Fl n
+Don't fork.
+.It Fl N Ar priority
+To the extent permitted by the operating system, run the
+.Nm
+at a high priority.
+.It Fl p Ar pidfile
+Specify the name and path to record the
+.Nm Ns 's
+process
+ID.
+.It Fl P
+Override the priority limit set by the operating system.
+Not
+recommended for sissies.
+.It Fl q
+Exit the
+.Nm
+just after the first time the clock is
+set.
+This behavior mimics that of the
+.Xr ntpdate 8
+program,
+which is to be retired.
+The
+.Fl g
+and
+.Fl x
+options can
+be used with this option.
+.It Fl r Ar broadcastdelay
+Specify the default propagation delay from the
+broadcast/multicast server and this computer.
+This is necessary
+only if the delay cannot be computed automatically by the
+protocol.
+.It Fl s Ar statsdir
+Specify the directory path for files created by the statistics
+facility.
+.It Fl t Ar key
+Add a key number to the trusted key list.
+.It Fl v Ar variable
+.It Fl V Ar variable
+Add a system variable listed by default.
+.It Fl x
+Normally, the time is slewed if the offset is less than the
+step threshold, which is 128 ms by default, and stepped if above
+the threshold.
+This option forces the time to be slewed in all
+cases.
+If the step threshold is set to zero, all offsets are
+stepped, regardless of value and regardless of the
+.Fl x
+option.
+In general, this is not a good idea, as it bypasses the
+clock state machine which is designed to cope with large time and
+frequency errors Note: Since the slew rate is limited to 0.5 ms/s,
+each second of adjustment requires an amortization interval of 2000
+s.
+Thus, an adjustment of many seconds can take hours or days to
+amortize.
+This option can be used with the
+.Fl q
+option.
+.El
+.Ss "How NTP Operates"
+The
+.Nm
+program operates by exchanging messages with
+one or more configured servers at designated poll intervals.
+When
+started, whether for the first or subsequent times, the program
+requires several exchanges from the majority of these servers so
+the signal processing and mitigation algorithms can accumulate and
+groom the data and set the clock.
+In order to protect the network
+from bursts, the initial poll interval for each server is delayed
+an interval randomized over 0-16s.
+At the default initial poll
+interval of 64s, several minutes can elapse before the clock is
+set.
+The initial delay to set the clock can be reduced using the
+.Cm iburst
+keyword with the
+.Ic server
+configuration
+command, as described in
+.Xr ntp.conf 5 .
+.Pp
+Most operating systems and hardware of today incorporate a
+time-of-year (TOY) chip to maintain the time during periods when
+the power is off.
+When the machine is booted, the chip is used to
+initialize the operating system time.
+After the machine has
+synchronized to a NTP server, the operating system corrects the
+chip from time to time.
+In case there is no TOY chip or for some
+reason its time is more than 1000s from the server time,
+.Nm
+assumes something must be terribly wrong and the only
+reliable action is for the operator to intervene and set the clock
+by hand.
+This causes
+.Nm
+to exit with a panic message to
+the system log.
+The
+.Fl g
+option overrides this check and the
+clock will be set to the server time regardless of the chip time.
+However, and to protect against broken hardware, such as when the
+CMOS battery fails or the clock counter becomes defective, once the
+clock has been set, an error greater than 1000s will cause
+.Nm
+to exit anyway.
+.Pp
+Under ordinary conditions,
+.Nm
+adjusts the clock in
+small steps so that the timescale is effectively continuous and
+without discontinuities.
+Under conditions of extreme network
+congestion, the roundtrip delay jitter can exceed three seconds and
+the synchronization distance, which is equal to one-half the
+roundtrip delay plus error budget terms, can become very large.
+The
+.Nm
+algorithms discard sample offsets exceeding 128 ms,
+unless the interval during which no sample offset is less than 128
+ms exceeds 900s.
+The first sample after that, no matter what the
+offset, steps the clock to the indicated time.
+In practice this
+reduces the false alarm rate where the clock is stepped in error to
+a vanishingly low incidence.
+.Pp
+As the result of this behavior, once the clock has been set, it
+very rarely strays more than 128 ms, even under extreme cases of
+network path congestion and jitter.
+Sometimes, in particular when
+.Nm
+is first started, the error might exceed 128 ms.
+This
+may on occasion cause the clock to be set backwards if the local
+clock time is more than 128 s in the future relative to the server.
+In some applications, this behavior may be unacceptable.
+If the
+.Fl x
+option is included on the command line, the clock will
+never be stepped and only slew corrections will be used.
+.Pp
+The issues should be carefully explored before deciding to use
+the
+.Fl x
+option.
+The maximum slew rate possible is limited
+to 500 parts-per-million (PPM) as a consequence of the correctness
+principles on which the NTP protocol and algorithm design are
+based.
+As a result, the local clock can take a long time to
+converge to an acceptable offset, about 2,000 s for each second the
+clock is outside the acceptable range.
+During this interval the
+local clock will not be consistent with any other network clock and
+the system cannot be used for distributed applications that require
+correctly synchronized network time.
+.Pp
+In spite of the above precautions, sometimes when large
+frequency errors are present the resulting time offsets stray
+outside the 128-ms range and an eventual step or slew time
+correction is required.
+If following such a correction the
+frequency error is so large that the first sample is outside the
+acceptable range,
+.Nm
+enters the same state as when the
+.Pa ntp.drift
+file is not present.
+The intent of this behavior
+is to quickly correct the frequency and restore operation to the
+normal tracking mode.
+In the most extreme cases
+(time.ien.it comes to mind), there may be occasional
+step/slew corrections and subsequent frequency corrections.
+It
+helps in these cases to use the
+.Cm burst
+keyword when
+configuring the server.
+.Ss "Frequency Discipline"
+The
+.Nm
+behavior at startup depends on whether the
+frequency file, usually
+.Pa ntp.drift ,
+exists.
+This file
+contains the latest estimate of clock frequency error.
+When the
+.Nm
+is started and the file does not exist, the
+.Nm
+enters a special mode designed to quickly adapt to
+the particular system clock oscillator time and frequency error.
+This takes approximately 15 minutes, after which the time and
+frequency are set to nominal values and the
+.Nm
+enters
+normal mode, where the time and frequency are continuously tracked
+relative to the server.
+After one hour the frequency file is
+created and the current frequency offset written to it.
+When the
+.Nm
+is started and the file does exist, the
+.Nm
+frequency is initialized from the file and enters normal mode
+immediately.
+After that the current frequency offset is written to
+the file at hourly intervals.
+.Ss "Operating Modes"
+.Nm
+can operate in any of several modes, including
+symmetric active/passive, client/server broadcast/multicast and
+manycast, as described in the
+.Qq Association Management
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp ) .
+It normally operates continuously while
+monitoring for small changes in frequency and trimming the clock
+for the ultimate precision.
+However, it can operate in a one-time
+mode where the time is set from an external server and frequency is
+set from a previously recorded frequency file.
+A
+broadcast/multicast or manycast client can discover remote servers,
+compute server-client propagation delay correction factors and
+configure itself automatically.
+This makes it possible to deploy a
+fleet of workstations without specifying configuration details
+specific to the local environment.
+.Pp
+By default,
+.Nm
+runs in continuous mode where each of
+possibly several external servers is polled at intervals determined
+by an intricate state machine.
+The state machine measures the
+incidental roundtrip delay jitter and oscillator frequency wander
+and determines the best poll interval using a heuristic algorithm.
+Ordinarily, and in most operating environments, the state machine
+will start with 64s intervals and eventually increase in steps to
+1024s.
+A small amount of random variation is introduced in order to
+avoid bunching at the servers.
+In addition, should a server become
+unreachable for some time, the poll interval is increased in steps
+to 1024s in order to reduce network overhead.
+.Pp
+In some cases it may not be practical for
+.Nm
+to run
+continuously.
+A common workaround has been to run the
+.Xr ntpdate 8
+program from a
+.Xr cron 8
+job at designated
+times.
+However, this program does not have the crafted signal
+processing, error checking and mitigation algorithms of
+.Nm .
+The
+.Fl q
+option is intended for this purpose.
+Setting this option will cause
+.Nm
+to exit just after
+setting the clock for the first time.
+The procedure for initially
+setting the clock is the same as in continuous mode; most
+applications will probably want to specify the
+.Cm iburst
+keyword with the
+.Ic server
+configuration command.
+With this
+keyword a volley of messages are exchanged to groom the data and
+the clock is set in about a minute.
+If nothing is heard after a
+couple of minutes, the daemon times out and exits.
+After a suitable
+period of mourning, the
+.Xr ntpdate 8
+program may be
+retired.
+.Pp
+When kernel support is available to discipline the clock
+frequency, which is the case for stock Solaris, Tru64, Linux and
+.Fx ,
+a useful feature is available to discipline the clock
+frequency.
+First,
+.Nm
+is run in continuous mode with
+selected servers in order to measure and record the intrinsic clock
+frequency offset in the frequency file.
+It may take some hours for
+the frequency and offset to settle down.
+Then the
+.Nm
+is
+stopped and run in one-time mode as required.
+At each startup, the
+frequency is read from the file and initializes the kernel
+frequency.
+.Ss "Poll Interval Control"
+This version of NTP includes an intricate state machine to
+reduce the network load while maintaining a quality of
+synchronization consistent with the observed jitter and wander.
+There are a number of ways to tailor the operation in order enhance
+accuracy by reducing the interval or to reduce network overhead by
+increasing it.
+However, the user is advised to carefully consider
+the consequences of changing the poll adjustment range from the
+default minimum of 64 s to the default maximum of 1,024 s.
+The
+default minimum can be changed with the
+.Ic tinker
+.Cm minpoll
+command to a value not less than 16 s.
+This value is used for all
+configured associations, unless overridden by the
+.Cm minpoll
+option on the configuration command.
+Note that most device drivers
+will not operate properly if the poll interval is less than 64 s
+and that the broadcast server and manycast client associations will
+also use the default, unless overridden.
+.Pp
+In some cases involving dial up or toll services, it may be
+useful to increase the minimum interval to a few tens of minutes
+and maximum interval to a day or so.
+Under normal operation
+conditions, once the clock discipline loop has stabilized the
+interval will be increased in steps from the minimum to the
+maximum.
+However, this assumes the intrinsic clock frequency error
+is small enough for the discipline loop correct it.
+The capture
+range of the loop is 500 PPM at an interval of 64s decreasing by a
+factor of two for each doubling of interval.
+At a minimum of 1,024
+s, for example, the capture range is only 31 PPM.
+If the intrinsic
+error is greater than this, the drift file
+.Pa ntp.drift
+will
+have to be specially tailored to reduce the residual error below
+this limit.
+Once this is done, the drift file is automatically
+updated once per hour and is available to initialize the frequency
+on subsequent daemon restarts.
+.Ss "The huff-n'-puff filter"
+In scenarios where a considerable amount of data are to be
+downloaded or uploaded over telephone modems, timekeeping quality
+can be seriously degraded.
+This occurs because the differential
+delays on the two directions of transmission can be quite large.
+In
+many cases the apparent time errors are so large as to exceed the
+step threshold and a step correction can occur during and after the
+data transfer is in progress.
+.Pp
+The huff-n'-puff filter is designed to correct the apparent time
+offset in these cases.
+It depends on knowledge of the propagation
+delay when no other traffic is present.
+In common scenarios this
+occurs during other than work hours.
+The filter maintains a shift
+register that remembers the minimum delay over the most recent
+interval measured usually in hours.
+Under conditions of severe
+delay, the filter corrects the apparent offset using the sign of
+the offset and the difference between the apparent delay and
+minimum delay.
+The name of the filter reflects the negative (huff)
+and positive (puff) correction, which depends on the sign of the
+offset.
+.Pp
+The filter is activated by the
+.Ic tinker command and
+.Cm huffpuff
+keyword, as described in
+.Xr ntp.conf 5 .
+.Sh FILES
+.Bl -tag -width /etc/ntp.drift -compact
+.It Pa /etc/ntp.conf
+the default name of the configuration file
+.It Pa /etc/ntp.drift
+the default name of the drift file
+.It Pa /etc/ntp.keys
+the default name of the key file
+.El
+.Sh SEE ALSO
+.Xr ntp.conf 5 ,
+.Xr ntpdate 8 ,
+.Xr ntpdc 8 ,
+.Xr ntpq 8
+.Pp
+In addition to the manual pages provided,
+comprehensive documentation is available on the world wide web
+at
+.Li http://www.ntp.org/ .
+A snapshot of this documentation is available in HTML format in
+.Pa /usr/share/doc/ntp .
+.Rs
+.%A David L. Mills
+.%T Network Time Protocol (Version 1)
+.%O RFC1059
+.Re
+.Rs
+.%A David L. Mills
+.%T Network Time Protocol (Version 2)
+.%O RFC1119
+.Re
+.Rs
+.%A David L. Mills
+.%T Network Time Protocol (Version 3)
+.%O RFC1305
+.Re
+.Sh BUGS
+.Nm
+has gotten rather fat.
+While not huge, it has gotten
+larger than might be desirable for an elevated-priority
+.Nm
+running on a workstation, particularly since many of
+the fancy features which consume the space were designed more with
+a busy primary server, rather than a high stratum workstation in
+mind.
diff --git a/usr.sbin/ntp/doc/ntpdate.8 b/usr.sbin/ntp/doc/ntpdate.8
new file mode 100644
index 0000000..7ff96da
--- /dev/null
+++ b/usr.sbin/ntp/doc/ntpdate.8
@@ -0,0 +1,264 @@
+.\"
+.\" $FreeBSD$
+.\"
+.Dd January 6, 2000
+.Dt NTPDATE 8
+.Os
+.Sh NAME
+.Nm ntpdate
+.Nd set the date and time via NTP
+.Sh SYNOPSIS
+.Nm
+.Op Fl bBdoqsuv
+.Op Fl a Ar key
+.Op Fl e Ar authdelay
+.Op Fl k Ar keyfile
+.Op Fl o Ar version
+.Op Fl p Ar samples
+.Op Fl t Ar timeout
+.Ar server ...
+.Sh DESCRIPTION
+.Pp
+.Em Note :
+The functionality of this program is now available
+in the
+.Xr ntpd 8
+program.
+See the
+.Fl q
+command line
+option in the
+.Xr ntpd 8
+page.
+After a suitable period of
+mourning, the
+.Nm
+program is to be retired from this
+distribution.
+.Pp
+.Nm
+sets the local date and time by polling the
+Network Time Protocol (NTP) server(s) given as the
+.Ar server
+arguments to determine the correct time.
+It must be run as root on
+the local host.
+A number of samples are obtained from each of the
+servers specified and a subset of the NTP clock filter and
+selection algorithms are applied to select the best of these.
+Note
+that the accuracy and reliability of
+.Nm
+depends on
+the number of servers, the number of polls each time it is run and
+the interval between runs.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl a Ar key
+Enable the authentication function and specify the key
+identifier to be used for authentication as the argument
+.Ar key .
+The keys and key identifiers must match
+in both the client and server key files.
+The default is to disable
+the authentication function.
+.It Fl B
+Force the time to always be slewed using the
+.Xr adjtime 2
+system
+call, even if the measured offset is greater than +-128 ms.
+The
+default is to step the time using
+.Xr settimeofday 2
+if the offset is
+greater than +-128 ms.
+Note that, if the offset is much greater
+than +-128 ms in this case, it can take a long time (hours) to
+slew the clock to the correct value.
+During this time, the host
+should not be used to synchronize clients.
+.It Fl b
+Force the time to be stepped using the
+.Xr settimeofday 2
+system
+call, rather than slewed (default) using the
+.Xr adjtime 2
+system call.
+This option should be used when called from a startup file at boot
+time.
+.It Fl d
+Enable the debugging mode, in which
+.Nm
+will go
+through all the steps, but not adjust the local clock.
+Information
+useful for general debugging will also be printed.
+.It Fl e Ar authdelay
+Specify the processing delay to perform an authentication
+function as the value
+.Ar authdelay ,
+in seconds and fraction
+(see
+.Xr ntpd 8
+for details).
+This number is usually small
+enough to be negligible for most purposes, though specifying a
+value may improve timekeeping on very slow CPU's.
+.It Fl k Ar keyfile
+Specify the path for the authentication key file as the string
+.Ar keyfile .
+The default is
+.Pa /etc/ntp.keys .
+This file
+should be in the format described in
+.Xr ntpd 8 .
+.It Fl o Ar version
+Specify the NTP version for outgoint packets as the integer
+.Ar version ,
+which can be 1 or 2.
+The default is 3.
+This allows
+.Nm
+to be used with older NTP versions.
+.It Fl p Ar samples
+Specify the number of samples to be acquired from each server
+as the integer
+.Ar samples ,
+with values from 1 to 8 inclusive.
+The default is 4.
+.It Fl q
+Query only - don't set the clock.
+.It Fl s
+Divert logging output from the standard output (default) to the
+system
+.Xr syslog 3
+facility.
+This is designed primarily for
+convenience of
+.Xr cron 8
+scripts.
+.It Fl t Ar timeout
+Specify the maximum time waiting for a server response as the
+value
+.Ar timeout ,
+in seconds and fraction.
+The value is
+rounded to a multiple of 0.2 seconds.
+The default is 1 second, a
+value suitable for polling across a LAN.
+.It Fl u
+Direct
+.Nm
+to use an unprivileged port for outgoing
+packets.
+This is most useful when behind a firewall that blocks
+incoming traffic to privileged ports, and you want to synchronise
+with hosts beyond the firewall.
+Note that the
+.Fl d
+option
+always uses unprivileged ports.
+.It Fl v
+Be verbose.
+This option will cause
+.Nm Ns 's
+version
+identification string to be logged.
+.El
+.Pp
+.Nm
+can be run manually as necessary to set the
+host clock, or it can be run from the host startup script to set
+the clock at boot time.
+This is useful in some cases to set the
+clock initially before starting the NTP daemon
+.Xr ntpd 8 .
+It is
+also possible to run
+.Nm
+from a
+.Xr cron 8
+script.
+However, it is important to note that
+.Nm
+with
+contrived
+.Xr cron 8
+scripts is no substitute for the NTP
+daemon, which uses sophisticated algorithms to maximize accuracy
+and reliability while minimizing resource use.
+Finally, since
+.Nm
+does not discipline the host clock frequency as
+does
+.Xr ntpd 8 ,
+the accuracy using
+.Nm
+is
+limited.
+.Pp
+Time adjustments are made by
+.Nm
+in one of two
+ways.
+If
+.Nm
+determines the clock is in error more
+than 0.5 second it will simply step the time by calling the system
+.Xr settimeofday 2
+routine.
+If the error is less than 0.5
+seconds, it will slew the time by calling the system
+.Xr adjtime 2
+routine.
+The latter technique is less disruptive
+and more accurate when the error is small, and works quite well
+when
+.Nm
+is run by
+.Xr cron 8
+every hour or
+two.
+.Pp
+.Nm
+will decline to set the date if an NTP server
+daemon (e.g.,
+.Xr ntpd 8 )
+is running on the same host.
+When
+running
+.Nm
+on a regular basis from
+.Xr cron 8
+as
+an alternative to running a daemon, doing so once every hour or two
+will result in precise enough timekeeping to avoid stepping the
+clock.
+.Pp
+If NetInfo support is compiled into
+.Nm ,
+then the
+.Ic server
+argument is optional if
+.Nm
+can find a
+time server in the NetInfo configuration for
+.Xr ntpd 8 .
+.Sh FILES
+.Bl -tag -width /etc/ntp.keys -compact
+.It Pa /etc/ntp.keys
+contains the encryption keys used by
+.Nm .
+.El
+.Sh SEE ALSO
+.Xr ntpd 8
+.Sh BUGS
+The slew adjustment is actually 50% larger than the measured
+offset, since this (it is argued) will tend to keep a badly
+drifting clock more accurate.
+This is probably not a good idea and
+may cause a troubling hunt for some values of the kernel variables
+.Va kern.clockrate.tick
+and
+.Va kern.clockrate.tickadj .
diff --git a/usr.sbin/ntp/doc/ntpdc.8 b/usr.sbin/ntp/doc/ntpdc.8
new file mode 100644
index 0000000..bca7479
--- /dev/null
+++ b/usr.sbin/ntp/doc/ntpdc.8
@@ -0,0 +1,720 @@
+.\"
+.\" $FreeBSD$
+.\"
+.Dd January 7, 2000
+.Dt NTPDC 8
+.Os
+.Sh NAME
+.Nm ntpdc
+.Nd special NTP query program
+.Sh SYNOPSIS
+.Nm
+.Op Fl ilnps
+.Op Fl c Ar command
+.Op Ar host ...
+.Sh DESCRIPTION
+.Nm
+is used to query the
+.Xr ntpd 8
+daemon about its
+current state and to request changes in that state.
+The program may
+be run either in interactive mode or controlled using command line
+arguments.
+Extensive state and statistics information is available
+through the
+.Nm
+interface.
+In addition, nearly all the
+configuration options which can be specified at startup using
+ntpd's configuration file may also be specified at run time using
+.Nm .
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl c Ar command
+The following argument is interpreted as an interactive format
+command and is added to the list of commands to be executed on the
+specified host(s).
+Multiple
+.Fl c
+options may be given.
+.It Fl i
+Force
+.Nm
+to operate in interactive mode.
+Prompts
+will be written to the standard output and commands read from the
+standard input.
+.It Fl l
+Obtain a list of peers which are known to the server(s).
+This
+switch is equivalent to
+.Ql -c listpeers .
+.It Fl n
+Output all host addresses in dotted-quad numeric format rather
+than converting to the canonical host names.
+.It Fl p
+Print a list of the peers known to the server as well as a
+summary of their state.
+This is equivalent to
+.Ql -c peers .
+.It Fl s
+Print a list of the peers known to the server as well as a
+summary of their state, but in a slightly different format than the
+.Fl p
+switch.
+This is equivalent to
+.Ql -c dmpeers .
+.El
+.Pp
+If one or more request options are included on the command line
+when
+.Nm
+is executed, each of the requests will be sent
+to the NTP servers running on each of the hosts given as command
+line arguments, or on localhost by default.
+If no request options
+are given,
+.Nm
+will attempt to read commands from the
+standard input and execute these on the NTP server running on the
+first host given on the command line, again defaulting to localhost
+when no other host is specified.
+.Nm
+will prompt for
+commands if the standard input is a terminal device.
+.Pp
+.Nm
+uses NTP mode 7 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
+makes
+no attempt to retransmit requests, and will time requests out if
+the remote host is not heard from within a suitable timeout
+time.
+.Pp
+The operation of
+.Nm
+are specific to the particular
+implementation of the
+.Xr ntpd 8
+daemon and can be expected to
+work only with this and maybe some previous versions of the daemon.
+Requests from a remote
+.Nm
+program which affect the
+state of the local server must be authenticated, which requires
+both the remote program and local server share a common key and key
+identifier.
+Specifying a command line option other than
+.Fl i
+or
+.Fl n
+will cause the specified query (queries) to be sent to
+the indicated host(s) immediately.
+Otherwise,
+.Nm
+will
+attempt to read interactive format commands from the standard
+input.
+.Ss "Interactive Commands"
+Interactive format commands consist of a keyword followed by zero
+to four arguments.
+Only enough characters of the full keyword to
+uniquely identify the command need be typed.
+The output of a
+command is normally sent to the standard output, but optionally the
+output of individual commands may be sent to a file by appending a
+.Ql \&> ,
+followed by a file name, to the command line.
+.Pp
+A number of interactive format commands are executed entirely
+within the
+.Nm
+program itself and do not result in NTP
+mode 7 requests being sent to a server.
+These are described
+following.
+.Bl -tag -width indent
+.It Ic \&? Ar command_keyword
+.It Ic help Ar command_keyword
+A
+.Ic \&?
+will print a list of all the command
+keywords known to this incarnation of
+.Nm .
+A
+.Ic \&?
+followed by a command keyword will print function and usage
+information about the command.
+This command is probably a better
+source of information about
+.Xr ntpq 8
+than this manual
+page.
+.It Ic delay Ar milliseconds
+Specify a time interval to be added to timestamps included in
+requests which require authentication.
+This is used to enable
+(unreliable) server reconfiguration over long delay network paths
+or between machines whose clocks are unsynchronized.
+Actually the
+server does not now require timestamps in authenticated requests,
+so this command may be obsolete.
+.It Ic host Ar hostname
+Set the host to which future queries will be sent.
+Hostname may
+be either a host name or a numeric address.
+.It Ic hostnames Op Cm yes | Cm no
+If
+.Cm yes
+is specified, host names are printed in
+information displays.
+If
+.Cm no
+is specified, numeric
+addresses are printed instead.
+The default is
+.Cm yes ,
+unless
+modified using the command line
+.Fl n
+switch.
+.It Ic keyid Ar keyid
+This command allows the specification of a key number to be
+used to authenticate configuration requests.
+This must correspond
+to a key number the server has been configured to use for this
+purpose.
+.It Ic quit
+Exit
+.Nm .
+.It Ic passwd
+This command prompts you to type in a password (which will not
+be echoed) which will be used to authenticate configuration
+requests.
+The password must correspond to the key configured for
+use by the NTP server for this purpose if such requests are to be
+successful.
+.It Ic timeout Ar milliseconds
+Specify a timeout period for responses to server queries.
+The
+default is about 8000 milliseconds.
+Note that since
+.Nm
+retries each query once after a timeout, the total waiting time for
+a timeout will be twice the timeout value set.
+.El
+.Ss "Control Message Commands"
+Query commands result in NTP mode 7 packets containing requests for
+information being sent to the server.
+These are read-only commands
+in that they make no modification of the server configuration
+state.
+.Bl -tag -width indent
+.It Ic listpeers
+Obtains and prints a brief list of the peers for which the
+server is maintaining state.
+These should include all configured
+peer associations as well as those peers whose stratum is such that
+they are considered by the server to be possible future
+synchonization candidates.
+.It Ic peers
+Obtains a list of peers for which the server is maintaining
+state, along with a summary of that state.
+Summary information
+includes the address of the remote peer, the local interface
+address (0.0.0.0 if a local address has yet to be determined), the
+stratum of the remote peer (a stratum of 16 indicates the remote
+peer is unsynchronized), the polling interval, in seconds, the
+reachability register, in octal, and the current estimated delay,
+offset and dispersion of the peer, all in seconds.
+.Pp
+The character in the left margin indicates the mode this peer
+entry is operating in.
+A
+.Ql \&+
+denotes symmetric active, a
+.Ql \&-
+indicates symmetric passive, a
+.Ql \&=
+means the
+remote server is being polled in client mode, a
+.Ql \&^
+indicates that the server is broadcasting to this address, a
+.Ql \&~
+denotes that the remote peer is sending broadcasts and a
+.Ql \&*
+marks the peer the server is currently synchonizing
+to.
+.Pp
+The contents of the host field may be one of four forms.
+It may
+be a host name, an IP address, a reference clock implementation
+name with its parameter or
+.Fn REFCLK "implementation_number" "parameter" .
+On
+.Ic hostnames
+.Cm no
+only IP-addresses
+will be displayed.
+.It Ic dmpeers
+A slightly different peer summary list.
+Identical to the output
+of the
+.Ic peers
+command, except for the character in the
+leftmost column.
+Characters only appear beside peers which were
+included in the final stage of the clock selection algorithm.
+A
+.Ql \&.
+indicates that this peer was cast off in the falseticker
+detection, while a
+.Ql \&+
+indicates that the peer made it
+through.
+A
+.Ql \&*
+denotes the peer the server is currently
+synchronizing with.
+.It Ic showpeer Ar peer_address ...
+Shows a detailed display of the current peer variables for one
+or more peers.
+Most of these values are described in the NTP
+Version 2 specification.
+.It Ic pstats Ar peer_address ...
+Show per-peer statistic counters associated with the specified
+peer(s).
+.It Ic clockinfo Ar clock_peer_address ...
+Obtain and print information concerning a peer clock.
+The
+values obtained provide information on the setting of fudge factors
+and other clock performance information.
+.It Ic kerninfo
+Obtain and print kernel phase-lock loop operating parameters.
+This information is available only if the kernel has been specially
+modified for a precision timekeeping function.
+.It Ic loopinfo Op Cm oneline | Cm multiline
+Print the values of selected loop filter variables.
+The loop
+filter is the part of NTP which deals with adjusting the local
+system clock.
+The
+.Sq offset
+is the last offset given to the
+loop filter by the packet processing code.
+The
+.Sq frequency
+is the frequency error of the local clock in parts-per-million
+(ppm).
+The
+.Sq time_const
+controls the stiffness of the
+phase-lock loop and thus the speed at which it can adapt to
+oscillator drift.
+The
+.Sq watchdog timer
+value is the number
+of seconds which have elapsed since the last sample offset was
+given to the loop filter.
+The
+.Cm oneline
+and
+.Cm multiline
+options specify the format in which this
+information is to be printed, with
+.Cm multiline
+as the
+default.
+.It Ic sysinfo
+Print a variety of system state variables, i.e., state related
+to the local server.
+All except the last four lines are described
+in the NTP Version 3 specification, RFC-1305.
+.Pp
+The
+.Sq system flags
+show various system flags, some of
+which can be set and cleared by the
+.Ic enable
+and
+.Ic disable
+configuration commands, respectively.
+These are
+the
+.Cm auth ,
+.Cm bclient ,
+.Cm monitor ,
+.Cm pll ,
+.Cm pps
+and
+.Cm stats
+flags.
+See the
+.Xr ntpd 8
+documentation for the meaning of these flags.
+There
+are two additional flags which are read only, the
+.Cm kernel_pll
+and
+.Cm kernel_pps .
+These flags indicate
+the synchronization status when the precision time kernel
+modifications are in use.
+The
+.Sq kernel_pll
+indicates that
+the local clock is being disciplined by the kernel, while the
+.Sq kernel_pps
+indicates the kernel discipline is provided by the PPS
+signal.
+.Pp
+The
+.Sq stability
+is the residual frequency error remaining
+after the system frequency correction is applied and is intended for
+maintenance and debugging.
+In most architectures, this value will
+initially decrease from as high as 500 ppm to a nominal value in
+the range .01 to 0.1 ppm.
+If it remains high for some time after
+starting the daemon, something may be wrong with the local clock,
+or the value of the kernel variable
+.Va kern.clockrate.tick
+may be
+incorrect.
+.Pp
+The
+.Sq broadcastdelay
+shows the default broadcast delay,
+as set by the
+.Ic broadcastdelay
+configuration command.
+.Pp
+The
+.Sq authdelay
+shows the default authentication delay,
+as set by the
+.Ic authdelay
+configuration command.
+.It Ic sysstats
+Print statistics counters maintained in the protocol
+module.
+.It Ic memstats
+Print statistics counters related to memory allocation
+code.
+.It Ic iostats
+Print statistics counters maintained in the input-output
+module.
+.It Ic timerstats
+Print statistics counters maintained in the timer/event queue
+support code.
+.It Ic reslist
+Obtain and print the server's restriction list.
+This list is
+(usually) printed in sorted order and may help to understand how
+the restrictions are applied.
+.It Ic monlist Op Ar version
+Obtain and print traffic counts collected and maintained by the
+monitor facility.
+The version number should not normally need to be
+specified.
+.It Ic clkbug Ar clock_peer_address ...
+Obtain debugging information for a reference clock driver.
+This
+information is provided only by some clock drivers and is mostly
+undecodable without a copy of the driver source in hand.
+.El
+.Ss "Runtime Configuration Requests"
+All requests which cause state changes in the server are
+authenticated by the server using a configured NTP key (the
+facility can also be disabled by the server by not configuring a
+key).
+The key number and the corresponding key must also be made
+known to
+.Nm .
+This can be done using the
+.Ic keyid
+and
+.Ic passwd
+commands, the latter of which will prompt at the terminal for a
+password to use as the encryption key.
+You will also be prompted
+automatically for both the key number and password the first time a
+command which would result in an authenticated request to the
+server is given.
+Authentication not only provides verification that
+the requester has permission to make such changes, but also gives
+an extra degree of protection again transmission errors.
+.Pp
+Authenticated requests always include a timestamp in the packet
+data, which is included in the computation of the authentication
+code.
+This timestamp is compared by the server to its receive time
+stamp.
+If they differ by more than a small amount the request is
+rejected.
+This is done for two reasons.
+First, it makes simple
+replay attacks on the server, by someone who might be able to
+overhear traffic on your LAN, much more difficult.
+Second, it makes
+it more difficult to request configuration changes to your server
+from topologically remote hosts.
+While the reconfiguration facility
+will work well with a server on the local host, and may work
+adequately between time-synchronized hosts on the same LAN, it will
+work very poorly for more distant hosts.
+As such, if reasonable
+passwords are chosen, care is taken in the distribution and
+protection of keys and appropriate source address restrictions are
+applied, the run time reconfiguration facility should provide an
+adequate level of security.
+.Pp
+The following commands all make authenticated requests.
+.Bl -tag -width indent
+.It Xo Ic addpeer Ar peer_address
+.Op Ar keyid
+.Op Ar version
+.Op Cm prefer
+.Xc
+Add a configured peer association at the given address and
+operating in symmetric active mode.
+Note that an existing
+association with the same peer may be deleted when this command is
+executed, or may simply be converted to conform to the new
+configuration, as appropriate.
+If the optional
+.Ar keyid
+is a
+nonzero integer, all outgoing packets to the remote server will
+have an authentication field attached encrypted with this key.
+If
+the value is 0 (or not given) no authentication will be done.
+The
+.Ar version
+can be 1, 2 or 3 and defaults to 3.
+The
+.Cm prefer
+keyword indicates a preferred peer (and thus will
+be used primarily for clock synchronisation if possible).
+The
+preferred peer also determines the validity of the PPS signal - if
+the preferred peer is suitable for synchronisation so is the PPS
+signal.
+.It Xo Ic addserver Ar peer_address
+.Op Ar keyid
+.Op Ar version
+.Op Cm prefer
+.Xc
+Identical to the addpeer command, except that the operating
+mode is client.
+.It Xo Ic broadcast Ar peer_address
+.Op Ar keyid
+.Op Ar version
+.Op Cm prefer
+.Xc
+Identical to the addpeer command, except that the operating
+mode is broadcast.
+In this case a valid key identifier and key are
+required.
+The
+.Ar peer_address
+parameter can be the broadcast
+address of the local network or a multicast group address assigned
+to NTP.
+If a multicast address, a multicast-capable kernel is
+required.
+.It Ic unconfig Ar peer_address ...
+This command causes the configured bit to be removed from the
+specified peer(s).
+In many cases this will cause the peer
+association to be deleted.
+When appropriate, however, the
+association may persist in an unconfigured mode if the remote peer
+is willing to continue on in this fashion.
+.It Xo Ic fudge Ar peer_address
+.Op Cm time1
+.Op Cm time2
+.Op Ar stratum
+.Op Ar refid
+.Xc
+This command provides a way to set certain data for a reference
+clock.
+See the source listing for further information.
+.It Ic enable Ar flag ...
+.It Ic disable Ar flag ...
+These commands operate in the same way as the
+.Ic enable
+and
+.Ic disable
+configuration file commands of
+.Xr ntpd 8 .
+Following is a description of the flags.
+Note that only the
+.Cm auth ,
+.Cm bclient ,
+.Cm monitor ,
+.Cm pll ,
+.Cm pps
+and
+.Cm stats
+flags can be set by
+.Nm ;
+the
+.Cm pll_kernel
+and
+.Cm pps_kernel
+flags are
+read-only.
+.Bl -tag -width indent
+.It Cm auth
+Enables the server to synchronize with unconfigured peers only
+if the peer has been correctly authenticated using a trusted key
+and key identifier.
+The default for this flag is enable.
+.It Cm bclient
+Enables the server to listen for a message from a broadcast or
+multicast server, as in the
+.Ic multicastclient
+command with
+default address.
+The default for this flag is disable.
+.It Cm monitor
+Enables the monitoring facility.
+See the
+.Ic monlist
+command for further information.
+The
+default for this flag is enable.
+.It Cm pll
+Enables the server to adjust its local clock by means of NTP.
+If disabled, the local clock free-runs at its intrinsic time and
+frequency offset.
+This flag is useful in case the local clock is
+controlled by some other device or protocol and NTP is used only to
+provide synchronization to other clients.
+In this case, the local
+clock driver is used.
+See the
+.Qq "Reference Clock Drivers"
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp )
+page for further information.
+The default for
+this flag is enable.
+.It Cm pps
+Enables the pulse-per-second (PPS) signal when frequency and
+time is disciplined by the precision time kernel modifications.
+See
+the
+.Qq "A Kernel Model for Precision Timekeeping"
+page for further information.
+The default for this flag is
+disable.
+.It Cm stats
+Enables the statistics facility.
+See the
+.Sx Monitoring Options
+section
+of the
+.Xr ntp.conf 5
+page for further information.
+The default for this flag is enable.
+.It Cm pll_kernel
+When the precision time kernel modifications are installed,
+this indicates the kernel controls the clock discipline; otherwise,
+the daemon controls the clock discipline.
+.It Cm pps_kernel
+When the precision time kernel modifications are installed and
+a pulse-per-second (PPS) signal is available, this indicates the
+PPS signal controls the clock discipline; otherwise, the daemon or
+kernel controls the clock discipline, as indicated by the
+.Cm pll_kernel
+flag.
+.El
+.It Xo Ic restrict Ar address Ar mask
+.Ar flag ...
+.Xc
+This command operates in the same way as the
+.Ic restrict
+configuration file commands of
+.Xr ntpd 8 .
+.It Xo Ic unrestrict Ar address Ar mask
+.Ar flag ...
+.Xc
+Unrestrict the matching entry from the restrict list.
+.It Xo Ic delrestrict Ar address Ar mask
+.Op Cm ntpport
+.Xc
+Delete the matching entry from the restrict list.
+.It Ic readkeys
+Causes the current set of authentication keys to be purged and
+a new set to be obtained by rereading the keys file (which must
+have been specified in the
+.Xr ntpd 8
+configuration file).
+This
+allows encryption keys to be changed without restarting the
+server.
+.It Ic trustedkey Ar keyid ...
+.It Ic untrustedkey Ar keyid ...
+These commands operate in the same way as the
+.Ic trustedkey
+and
+.Ic untrustedkey
+configuration file
+commands of
+.Xr ntpd 8 .
+.It Ic authinfo
+Returns information concerning the authentication module,
+including known keys and counts of encryptions and decryptions
+which have been done.
+.It Ic traps
+Display the traps set in the server.
+See the source listing for
+further information.
+.It Xo Ic addtrap Ar address
+.Op Ar port
+.Op Ar interface
+.Xc
+Set a trap for asynchronous messages.
+See the source listing
+for further information.
+.It Xo Ic clrtrap Ar address
+.Op Ar port
+.Op Ar interface
+.Xc
+Clear a trap for asynchronous messages.
+See the source listing
+for further information.
+.It Ic reset
+Clear the statistics counters in various modules of the server.
+See the source listing for further information.
+.El
+.Sh SEE ALSO
+.Xr ntp.conf 5 ,
+.Xr ntpd 8
+.Rs
+.%A David L. Mills
+.%T Network Time Protocol (Version 3)
+.%O RFC1305
+.Re
+.Sh BUGS
+.Nm
+is a crude hack.
+Much of the information it shows is
+deadly boring and could only be loved by its implementer.
+The
+program was designed so that new (and temporary) features were easy
+to hack in, at great expense to the program's ease of use.
+Despite
+this, the program is occasionally useful.
diff --git a/usr.sbin/ntp/doc/ntpq.8 b/usr.sbin/ntp/doc/ntpq.8
new file mode 100644
index 0000000..d47afbd
--- /dev/null
+++ b/usr.sbin/ntp/doc/ntpq.8
@@ -0,0 +1,744 @@
+.\"
+.\" $FreeBSD$
+.\"
+.Dd January 7, 2000
+.Dt NTPQ 8
+.Os
+.Sh NAME
+.Nm ntpq
+.Nd standard NTP query program
+.Sh SYNOPSIS
+.Nm
+.Op Fl inp
+.Op Fl c Ar command
+.Op Ar host ...
+.Sh DESCRIPTION
+The
+.Nm
+utility program 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
+can also obtain and print a
+list of peers in a common format by sending multiple queries to the
+server.
+.Pp
+If one or more request options is included on the command line
+when
+.Nm
+is executed, each of the requests will be sent
+to the NTP servers running on each of the hosts given as command
+line arguments, or on localhost by default.
+If no request options
+are given,
+.Nm
+will attempt to read commands from the
+standard input and execute these on the NTP server running on the
+first host given on the command line, again defaulting to localhost
+when no other host is specified.
+.Nm
+will prompt for
+commands if the standard input is a terminal device.
+.Pp
+.Nm
+uses NTP mode 6 packets to communicate with the
+NTP server, and hence can be used to query any compatible server on
+the network which permits it.
+Note that since NTP is a UDP protocol
+this communication will be somewhat unreliable, especially over
+large distances in terms of network topology.
+.Nm
+makes
+one attempt to retransmit requests, and will time requests out if
+the remote host is not heard from within a suitable timeout
+time.
+.Pp
+For examples and usage, see the
+.Qq "NTP Debugging Techniques"
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp ) .
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl c
+The following argument is interpreted as an interactive format
+command and is added to the list of commands to be executed on the
+specified host(s).
+Multiple
+.Fl c
+options may be given.
+.It Fl i
+Force
+.Nm
+to operate in interactive mode.
+Prompts
+will be written to the standard output and commands read from the
+standard input.
+.It Fl n
+Output all host addresses in dotted-quad numeric format rather
+than converting to the canonical host names.
+.It Fl p
+Print a list of the peers known to the server as well as a
+summary of their state.
+This is equivalent to the
+.Ic peers
+interactive command.
+.El
+.Pp
+Specifying a
+command line option other than
+.Fl i
+or
+.Fl n
+will
+cause the specified query (queries) to be sent to the indicated
+host(s) immediately.
+Otherwise,
+.Nm
+will attempt to read
+interactive format commands from the standard input.
+.Ss "Internal Commands"
+Interactive format commands consist of a keyword followed by zero
+to four arguments.
+Only enough characters of the full keyword to
+uniquely identify the command need be typed.
+The output of a
+command is normally sent to the standard output, but optionally the
+output of individual commands may be sent to a file by appending a
+.Ql \&> ,
+followed by a file name, to the command line.
+A
+number of interactive format commands are executed entirely within
+the
+.Nm
+program itself and do not result in NTP mode 6
+requests being sent to a server.
+These are described following.
+.Bl -tag -width indent
+.It Ic \&? Op Ar command_keyword
+.It Ic help Op Ar command_keyword
+A
+.Ql \&?
+by itself will print a list of all the command
+keywords known to this incarnation of
+.Nm .
+A
+.Ql \&?
+followed by a command keyword will print function and usage
+information about the command.
+This command is probably a better
+source of information about
+.Nm
+than this manual
+page.
+.It Xo Ic addvars
+.Ar variable_name Ns Op = Ns Ar value ...
+.Xc
+.It Ic rmvars Ar variable_name ...
+.It Ic clearvars
+The data carried by NTP mode 6 messages consists of a list of
+items of the form
+.Ql variable_name=value ,
+where the
+.Ql =value
+is ignored, and can be omitted,
+in requests to the server to read variables.
+.Nm
+maintains an internal list in which data to be included in control
+messages can be assembled, and sent using the
+.Ic readlist
+and
+.Ic writelist
+commands described below.
+The
+.Ic addvars
+command allows variables and their optional values to be added to
+the list.
+If more than one variable is to be added, the list should
+be comma-separated and not contain white space.
+The
+.Ic rmvars
+command can be used to remove individual variables from the list,
+while the
+.Ic clearlist
+command removes all variables from the
+list.
+.It Ic authenticate Cm yes | Cm no
+Normally
+.Nm
+does not authenticate requests unless
+they are write requests.
+The command
+.Ql authenticate yes
+causes
+.Nm
+to send authentication with all requests it
+makes.
+Authenticated requests causes some servers to handle
+requests slightly differently, and can occasionally melt the CPU in
+fuzzballs if you turn authentication on before doing a
+.Ic peer
+display.
+.It Ic cooked
+Causes output from query commands to be "cooked", so that
+variables which are recognized by
+.Nm
+will have their
+values reformatted for human consumption.
+Variables which
+.Nm
+thinks should have a decodable value but didn't are
+marked with a trailing
+.Ql \&? .
+.It Xo Ic debug
+.Cm more |
+.Cm less |
+.Cm off
+.Xc
+Turns internal query program debugging on and off.
+.It Ic delay Ar milliseconds
+Specify a time interval to be added to timestamps included in
+requests which require authentication.
+This is used to enable
+(unreliable) server reconfiguration over long delay network paths
+or between machines whose clocks are unsynchronized.
+Actually the
+server does not now require timestamps in authenticated requests,
+so this command may be obsolete.
+.It Ic host Ar hostname
+Set the host to which future queries will be sent.
+Hostname may
+be either a host name or a numeric address.
+.It Ic hostnames Cm yes | Cm no
+If
+.Cm yes
+is specified, host names are printed in
+information displays.
+If
+.Cm no
+is specified, numeric
+addresses are printed instead.
+The default is
+.Cm yes ,
+unless
+modified using the command line
+.Fl n
+switch.
+.It Ic keyid Ar keyid
+This command allows the specification of a key number to be
+used to authenticate configuration requests.
+This must correspond
+to a key number the server has been configured to use for this
+purpose.
+.It Xo Ic ntpversion
+.Cm 1 |
+.Cm 2 |
+.Cm 3 |
+.Cm 4
+.Xc
+Sets the NTP version number which
+.Nm
+claims in
+packets.
+Defaults to 3, Note that mode 6 control messages (and
+modes, for that matter) didn't exist in NTP version 1.
+There appear
+to be no servers left which demand version 1.
+.It Ic quit
+Exit
+.Nm .
+.It Ic passwd
+This command prompts you to type in a password (which will not
+be echoed) which will be used to authenticate configuration
+requests.
+The password must correspond to the key configured for
+use by the NTP server for this purpose if such requests are to be
+successful.
+.It Ic raw
+Causes all output from query commands is printed as received
+from the remote server.
+The only formating/interpretation done on
+the data is to transform nonascii data into a printable (but barely
+understandable) form.
+.It Ic timeout Ar milliseconds
+Specify a timeout period for responses to server queries.
+The
+default is about 5000 milliseconds.
+Note that since
+.Nm
+retries each query once after a timeout, the total waiting time for
+a timeout will be twice the timeout value set.
+.El
+.Ss Control Message Commands
+Each peer known to an NTP server has a 16 bit integer association
+identifier assigned to it.
+NTP control messages which carry peer
+variables must identify the peer the values correspond to by
+including its association ID.
+An association ID of 0 is special,
+and indicates the variables are system variables, whose names are
+drawn from a separate name space.
+.Pp
+Control message commands result in one or more NTP mode 6
+messages being sent to the server, and cause the data returned to
+be printed in some format.
+Most commands currently implemented send
+a single message and expect a single response.
+The current
+exceptions are the peers command, which will send a preprogrammed
+series of messages to obtain the data it needs, and the mreadlist
+and mreadvar commands, which will iterate over a range of
+associations.
+.Bl -tag -width indent
+.It Ic associations
+Obtains and prints a list of association identifiers and peer
+statuses for in-spec peers of the server being queried.
+The list is
+printed in columns.
+The first of these is an index numbering the
+associations from 1 for internal use, the second the actual
+association identifier returned by the server and the third the
+status word for the peer.
+This is followed by a number of columns
+containing data decoded from the status word.
+See the peers command
+for a decode of the
+.Sq condition
+field.
+Note that the data
+returned by the
+.Ic associations
+command is cached internally
+in
+.Xr ntpq 8 .
+The index is then of use when dealing with stupid
+servers which use association identifiers which are hard for humans
+to type, in that for any subsequent commands which require an
+association identifier as an argument, the form of index may be
+used as an alternative.
+.It Xo Ic clockvar Op Ar assocID
+.Oo
+.Ar variable_name Ns Op = Ns Ar value ...
+.Oc
+.Ar ...
+.Xc
+.It Xo Ic cv Op Ar assocID
+.Oo
+.Ar variable_name Ns Op = Ns Ar value ...
+.Oc
+.Ar ...
+.Xc
+Requests that a list of the server's clock variables be sent.
+Servers which have a radio clock or other external synchronization
+will respond positively to this.
+If the association identifier is
+omitted or zero the request is for the variables of the
+.Sq system clock
+and will generally get a positive response from all
+servers with a clock.
+If the server treats clocks as pseudo-peers,
+and hence can possibly have more than one clock connected at once,
+referencing the appropriate peer association ID will show the
+variables of a particular clock.
+Omitting the variable list will
+cause the server to return a default variable display.
+.It Ic lassociations
+Obtains and prints a list of association identifiers and peer
+statuses for all associations for which the server is maintaining
+state.
+This command differs from the
+.Ic associations
+command
+only for servers which retain state for out-of-spec client
+associations (i.e., fuzzballs).
+Such associations are normally
+omitted from the display when the
+.Ic associations
+command is
+used, but are included in the output of
+.Ic lassociations .
+.It Ic lpassociations
+Print data for all associations, including out-of-spec client
+associations, from the internally cached list of associations.
+This
+command differs from
+.Ic passociations
+only when dealing with
+fuzzballs.
+.It Ic lpeers
+Like R peers, except a summary of all associations for which
+the server is maintaining state is printed.
+This can produce a much
+longer list of peers from fuzzball servers.
+.It Ic mreadlist Ar assocID Ar assocID
+.It Ic mrl Ar assocID Ar assocID
+Like the
+.Ic readlist
+command, except the query is done
+for each of a range of (nonzero) association IDs.
+This range is
+determined from the association list cached by the most recent
+.Ic associations
+command.
+.It Xo Ic mreadvar Ar assocID Ar assocID
+.Oo
+.Ar variable_name Ns Op = Ns Ar value ...
+.Oc
+.Xc
+.It Xo Ic mrv Ar assocID Ar assocID
+.Oo
+.Ar variable_name Ns Op = Ns Ar value ...
+.Oc
+.Xc
+Like the
+.Ic readvar
+command, except the query is done for
+each of a range of (nonzero) association IDs.
+This range is
+determined from the association list cached by the most recent
+.Ic associations
+command.
+.It Ic opeers
+An old form of the
+.Ic peers
+command with the reference ID
+replaced by the local interface address.
+.It Ic passociations
+Displays association data concerning in-spec peers from the
+internally cached list of associations.
+This command performs
+identically to the
+.Ic associations
+except that it displays
+the internally stored data rather than making a new query.
+.It Ic peers
+Obtains a current list peers of the server, along with a
+summary of each peer's state.
+Summary information includes the
+address of the remote peer, the reference ID (0.0.0.0 if this is
+unknown), the stratum of the remote peer, the type of the peer
+(local, unicast, multicast or broadcast), when the last packet was
+received, the polling interval, in seconds, the reachability
+register, in octal, and the current estimated delay, offset and
+dispersion of the peer, all in milliseconds.
+The character in the left margin indicates the fate of this
+peer in the clock selection process.
+Following is a list of these
+characters, the pigeon used in the
+.Ic rv
+command, and a short
+explanation of the condition revealed.
+.Bl -tag -width indent
+.It space
+.Pq reject
+The peer is discarded as unreachable, synchronized to this
+server (synch loop) or outrageous synchronization distance.
+.It x
+.Pq falsetick
+The peer is discarded by the intersection algorithm as a
+falseticker.
+.It \&.
+.Pq excess
+The peer is discarded as not among the first ten peers sorted
+by synchronization distance and so is probably a poor candidate for
+further consideration.
+.It \&-
+.Pq outlyer
+The peer is discarded by the clustering algorithm as an
+outlyer.
+.It \&+
+.Pq candidate
+The peer is a survivor and a candidate for the combining
+algorithm.
+.It \&#
+.Pq selected
+The peer is a survivor, but not among the first six peers
+sorted by synchronization distance.
+If the association is ephemeral,
+it may be demobilized to conserve resources.
+.It \&*
+.Pq peer
+The peer has been declared the system peer and lends its
+variables to the system variables.
+.It o
+.Pq (pps.peer)
+The peer has been declared the system peer and lends its
+variables to the system variables.
+However, the actual system
+synchronization is derived from a pulse-per-second (PPS) signal,
+either indirectly via the PPS reference clock driver or directly
+via kernel interface.
+.El
+.El
+.Pp
+The
+.Va flash
+variable is a valuable debugging aid.
+It
+displays the results of the original sanity checks defined in the
+NTP specification RFC-1305 and additional ones added in NTP Version
+4.
+There are eleven tests called
+.Sy TEST1
+through
+.Sy TEST11 .
+The tests are performed in a certain order
+designed to gain maximum diagnostic information while protecting
+against accidental or malicious errors.
+The
+.Va flash
+variable
+is first initialized to zero.
+If after each set of tests one or
+more bits are set, the packet is discarded.
+.Pp
+Tests
+.Sy TEST4
+and
+.Sy TEST5
+check the access
+permissions and cryptographic message digest.
+If any bits are set
+after that, the packet is discarded.
+Tests
+.Sy TEST10
+and
+.Sy TEST11
+check the authentication state using Autokey
+public-key cryptography, as described in the
+.Sx Authentication Options
+section of
+.Xr ntp.conf 5 .
+If any bits are set
+and the association has previously been marked reachable, the
+packet is discarded; otherwise, the originate and receive
+timestamps are saved, as required by the NTP protocol, and
+processing continues.
+.Pp
+Tests
+.Sy TEST1
+through
+.Sy TEST3
+check the packet
+timestamps from which the offset and delay are calculated.
+If any
+bits are set, the packet is discarded; otherwise, the packet header
+variables are saved.
+Tests
+.Sy TEST6
+through
+.Sy TEST8
+check the health of the server.
+If any bits are set, the packet is
+discarded; otherwise, the offset and delay relative to the server
+are calculated and saved.
+Test
+.Sy TEST9
+checks the health of
+the association itself.
+If any bits are set, the packet is
+discarded; otherwise, the saved variables are passed to the clock
+filter and mitigation algorithms.
+.Pp
+The
+.Va flash
+bits for each test read in increasing order
+from the least significant bit are defined as follows.
+.Bl -tag -width indent
+.It Sy TEST1
+Duplicate packet.
+The packet is at best a casual retransmission
+and at worst a malicious replay.
+.It Sy TEST2
+Bogus packet.
+The packet is not a reply to a message previously
+sent.
+This can happen when the NTP daemon is restarted and before
+somebody else notices.
+.It Sy TEST3
+Unsynchronized.
+One or more timestamp fields are invalid.
+This
+normally happens when the first packet from a peer is
+received.
+.It Sy TEST4
+Access is denied.
+See the
+.Qq "Access Control"
+page.
+.It Sy TEST5
+Cryptographic authentication fails.
+See the
+.Sx Authentication Options
+section of
+.Xr ntp.conf 5 .
+.It Sy TEST6
+The server is unsynchronized.
+Wind up its clock first.
+.It Sy TEST7
+The server stratum is at the maximum than 15.
+It is probably
+unsynchronized and its clock needs to be wound up.
+.It Sy TEST8
+Either the root delay or dispersion is greater than one second,
+which is highly unlikely unless the peer is synchronized to
+Mars.
+.It Sy TEST9
+Either the peer delay or dispersion is greater than one second,
+which is higly unlikely unless the peer is on Mars.
+.It Sy TEST10
+The autokey protocol has detected an authentication failure.
+See the
+.Sx Authentication Options
+section of
+.Xr ntp.conf 5 .
+.It Sy TEST11
+The autokey protocol has not verified the server or peer is
+authentic and has valid public key credentials.
+See the
+.Sx Authentication Options
+section of
+.Xr ntp.conf 5 .
+.El
+.Pp
+Additional system variables used by the NTP Version 4 Autokey
+support include the following:
+.Bl -tag -width indent
+.It Ic certificate Ar filestamp
+Shows the NTP seconds when the certificate file was
+created.
+.It Ic hostname Ar host
+Shows the name of the host as returned by the Unix
+.Xr gethostname 3
+library function.
+.It Ic flags Ar hex
+Shows the current flag bits, where the
+.Ar hex
+bits
+are interpreted as follows:
+.Bl -tag -width indent
+.It 0x01
+autokey enabled
+.It 0x02
+RSA public/private key files present
+.It 0x04
+PKI certificate file present
+.It 0x08
+Diffie-Hellman parameters file present
+.It 0x10
+NIST leapseconds table file present
+.El
+.It Ic leapseconds Ar filestamp
+Shows the NTP seconds when the NIST leapseconds table file was
+created.
+.It Ic params Ar filestamp
+Shows the NTP seconds when the Diffie-Hellman agreement
+parameter file was created.
+.It Ic publickey Ar filestamp
+Shows the NTP seconds when the RSA public/private key files
+were created.
+.It Ic refresh Ar filestamp
+Shows the NTP seconds when the public cryptographic values were
+refreshed and signed.
+.It Ic tai Ar offset
+Shows the TAI-UTC offset in seconds obtained from the NIST
+leapseconds table.
+.El
+.Pp
+Additional peer variables used by the NTP Version 4 Autokey
+support include the following:
+.Bl -tag -width indent
+.It Ic certificate Ar filestamp
+Shows the NTP seconds when the certificate file was
+created.
+.It Ic flags Ar hex
+Shows the current flag bits, where the
+.Ar hex
+bits are
+interpreted as in the system variable of the same name.
+The bits
+are set in the first autokey message received from the server and
+then reset as the associated data are obtained from the server and
+stored.
+.It Ic hcookie Ar hex
+Shows the host cookie used in the key agreement algorithm.
+.It Ic initkey Ar key
+Shows the initial key used by the key list generator in the
+autokey protocol.
+.It Ic initsequence Ar index
+Shows the initial index used by the key list generator in the
+autokey protocol.
+.It Ic pcookie Ar hex
+Specifies the peer cookie used in the key agreement
+algorithm.
+.It Ic timestamp Ar time
+Shows the NTP seconds when the last autokey key list was
+generated and signed.
+.It Ic pstatus Ar assocID
+Sends a read status request to the server for the given
+association.
+The names and values of the peer variables returned
+will be printed.
+Note that the status word from the header is
+displayed preceding the variables, both in hexadecimal and in
+pidgeon English.
+.It Ic readlist Ar assocID
+.It Ic rl Ar assocID
+Requests that the values of the variables in the internal
+variable list be returned by the server.
+If the association ID is
+omitted or is 0 the variables are assumed to be system variables.
+Otherwise they are treated as peer variables.
+If the internal
+variable list is empty a request is sent without data, which should
+induce the remote server to return a default display.
+.It Xo Ic readvar Ar assocID
+.Ar variable_name Ns Op = Ns Ar value
+.Ar ...
+.Xc
+.It Xo Ic rv Ar assocID
+.Ar variable_name Ns Op = Ns Ar value
+.Ar ...
+.Xc
+Requests that the values of the specified variables be returned
+by the server by sending a read variables request.
+If the
+association ID is omitted or is given as zero the variables are
+system variables, otherwise they are peer variables and the values
+returned will be those of the corresponding peer.
+Omitting the
+variable list will send a request with no data which should induce
+the server to return a default display.
+.It Xo Ic writevar Ar assocID
+.Ar variable_name Ns Op = Ns Ar value
+.Ar ...
+.Xc
+Like the readvar request, except the specified variables are
+written instead of read.
+.It Ic writelist Op Ar assocID
+Like the readlist request, except the internal list variables
+are written instead of read.
+.El
+.Sh SEE ALSO
+.Xr ntp.conf 5 ,
+.Xr ntpd 8 ,
+.Xr ntpdc 8
+.Sh BUGS
+The
+.Ic peers
+command is non-atomic and may occasionally result in
+spurious error messages about invalid associations occurring and
+terminating the command.
+The timeout time is a fixed constant,
+which means you wait a long time for timeouts since it assumes sort
+of a worst case.
+The program should improve the timeout estimate as
+it sends queries to a particular host, but doesn't.
diff --git a/usr.sbin/ntp/doc/ntptime.8 b/usr.sbin/ntp/doc/ntptime.8
new file mode 100644
index 0000000..2572cdf
--- /dev/null
+++ b/usr.sbin/ntp/doc/ntptime.8
@@ -0,0 +1,67 @@
+.\"
+.\" $FreeBSD$
+.\"
+.Dd January 7, 2000
+.Dt NTPTIME 8
+.Os
+.Sh NAME
+.Nm ntptime
+.Nd read kernel time variables
+.Sh SYNOPSIS
+.Nm
+.Op Fl chr
+.Op Fl e Ar est_error
+.Op Fl f Ar frequency
+.Op Fl m Ar max_error
+.Op Fl o Ar offset
+.Op Fl s Ar status
+.Op Fl t Ar time_constant
+.Sh DESCRIPTION
+This program is useful only with special kernels
+described in the
+.Qo
+A Kernel Model for Precision Timekeeping
+.Qc
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp ) .
+It reads and displays time-related kernel variables
+using the
+.Fn gettime
+and
+.Xr adjtime 2
+system calls if available.
+A similar display can be obtained using the
+.Xr ntpdc 8
+program's
+.Ic kerninfo
+command.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl c
+Display the execution time of
+.Nm
+itself.
+.It Fl e Ar est_error
+Specify estimated error, in microseconds.
+.It Fl f Ar frequency
+Specify frequency offset, in parts per million.
+.It Fl h
+Display help information.
+.It Fl l
+Specify the leap bits as a code from 0 to 3.
+.It Fl m Ar max_error
+Specify max possible errors, in microseconds.
+.It Fl o Ar offset
+Specify clock offset, in microseconds.
+.It Fl r
+Display Unix and NTP times in raw format.
+.It Fl s Ar status
+.It Fl t Ar time_constant
+Specify time constant, an integer in the range 0-4.
+.El
+.Sh SEE ALSO
+.Xr adjtime 2 ,
+.Xr ntpdc 8
diff --git a/usr.sbin/ntp/doc/ntptrace.8 b/usr.sbin/ntp/doc/ntptrace.8
new file mode 100644
index 0000000..c76288f
--- /dev/null
+++ b/usr.sbin/ntp/doc/ntptrace.8
@@ -0,0 +1,73 @@
+.\"
+.\" $FreeBSD$
+.\"
+.Dd January 6, 2000
+.Dt NTPTRACE 8
+.Os
+.Sh NAME
+.Nm ntptrace
+.Nd "trace a chain of NTP servers back to the primary source"
+.Sh SYNOPSIS
+.Nm
+.Op Fl vdn
+.Op Fl r Ar retries
+.Op Fl t Ar timeout
+.Op Ar server
+.Sh DESCRIPTION
+.Nm
+determines where a given Network Time Protocol (NTP) server gets
+its time from, and follows the chain of NTP servers back to their
+master time source.
+If given no arguments, it starts with
+.Dq localhost .
+.Pp
+Here is an example of the output from
+.Nm :
+.Bd -literal
+% ntptrace
+localhost: stratum 4, offset 0.0019529, synch distance 0.144135
+server2ozo.com: stratum 2, offset 0.0124263, synch distance 0.115784
+usndh.edu: stratum 1, offset 0.0019298, synch distance 0.011993, refid 'WWVB'
+.Ed
+.Pp
+On each line, the fields are (left to right): the host name, the
+host stratum,
+the time offset between that host and the local host
+(as measured by
+.Nm ;
+this is why it is not always zero for
+.Dq localhost ) ,
+the host
+synchronization distance,
+and (only for stratum-1 servers) the reference clock ID. All times
+are given in seconds.
+Note that the stratum is the server hop count to the primary source,
+while the synchronization distance is the estimated error
+relative to the primary source.
+These terms are precisely defined in RFC 1305.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl d
+Turn on some debugging output.
+.It Fl n
+Turn off the printing of host names; instead, host IP addresses
+are given. This may be necessary if a nameserver is down.
+.It Fl r Ar retries
+Set the number of retransmission attempts for each host; the default is 5.
+.It Fl t Ar timeout
+Set the retransmission timeout (in seconds); the default is 2.
+.It Fl v
+Print verbose information about the NTP servers.
+.El
+.Sh SEE ALSO
+.Xr ntpd 8 ,
+.Xr ntpdc 8
+.Rs
+.%A D L Mills
+.%T Network Time Protocol (Version 3)
+.%O RFC1305
+.Re
+.Sh BUGS
+This program makes no attempt to improve accuracy by doing multiple
+samples.
diff --git a/usr.sbin/ntp/libntp/Makefile b/usr.sbin/ntp/libntp/Makefile
new file mode 100644
index 0000000..d4101a9
--- /dev/null
+++ b/usr.sbin/ntp/libntp/Makefile
@@ -0,0 +1,28 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../contrib/ntp/libntp
+
+LIB= ntp
+INTERNALLIB= true
+
+SRCS= a_md5encrypt.c adjtime.c atoint.c atolfp.c \
+ atouint.c audio.c authencrypt.c authkeys.c \
+ authparity.c authreadkeys.c authusekey.c binio.c \
+ buftvtots.c caljulian.c calleapwhen.c caltontp.c \
+ calyearstart.c clocktime.c clocktypes.c decodenetnum.c \
+ dofptoa.c dolfptoa.c emalloc.c findconfig.c \
+ fptoa.c fptoms.c getopt.c gpstolfp.c \
+ hextoint.c hextolfp.c humandate.c icom.c \
+ ieee754io.c inttoa.c iosignal.c lib_strbuf.c \
+ machines.c md5c.c memmove.c mfp_mul.c \
+ mfptoa.c mfptoms.c modetoa.c mstolfp.c \
+ msutotsf.c msyslog.c netof.c numtoa.c \
+ numtohost.c octtoint.c prettydate.c ranny.c \
+ recvbuff.c refnumtoa.c statestr.c syssignal.c \
+ systime.c tsftomsu.c tstotv.c tvtoa.c \
+ tvtots.c uglydate.c uinttoa.c utvtoa.c \
+ ymd2yd.c
+
+CFLAGS+= -I${.CURDIR}/../../../contrib/ntp/include -I${.CURDIR}/../
+
+.include <bsd.lib.mk>
diff --git a/usr.sbin/ntp/libparse/Makefile b/usr.sbin/ntp/libparse/Makefile
new file mode 100644
index 0000000..6176726
--- /dev/null
+++ b/usr.sbin/ntp/libparse/Makefile
@@ -0,0 +1,15 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../contrib/ntp/libparse
+
+LIB= parse
+INTERNALLIB= true
+
+SRCS= clk_computime.c clk_dcf7000.c clk_hopf6021.c clk_meinberg.c \
+ clk_rawdcf.c clk_rcc8000.c clk_schmid.c clk_trimtaip.c \
+ clk_trimtsip.c clk_varitext.c clk_wharton.c data_mbg.c \
+ info_trimble.c parse.c parse_conf.c trim_info.c
+
+CFLAGS+= -I${.CURDIR}/../../../contrib/ntp/include -I${.CURDIR}/../
+
+.include <bsd.lib.mk>
diff --git a/usr.sbin/ntp/ntp-genkeys/Makefile b/usr.sbin/ntp/ntp-genkeys/Makefile
new file mode 100644
index 0000000..7a60029
--- /dev/null
+++ b/usr.sbin/ntp/ntp-genkeys/Makefile
@@ -0,0 +1,15 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../contrib/ntp/util \
+ ${.CURDIR}/../../../contrib/ntp/ntpd
+
+PROG= ntp-genkeys
+NOMAN= yes
+SRCS= ntp-genkeys.c ntp_config.c
+
+CFLAGS+= -I${.CURDIR}/../../../contrib/ntp/include -I${.CURDIR}/../
+
+DPADD= ${LIBNTP}
+LDADD= ${LIBNTP}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ntp/ntpd/Makefile b/usr.sbin/ntp/ntpd/Makefile
new file mode 100644
index 0000000..84fd658
--- /dev/null
+++ b/usr.sbin/ntp/ntpd/Makefile
@@ -0,0 +1,38 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../contrib/ntp/ntpd
+
+PROG= ntpd
+NOMAN= yes
+SRCS= cmd_args.c map_vme.c ntp_config.c \
+ ntp_control.c ntp_crypto.c ntp_filegen.c \
+ ntp_intres.c ntp_io.c ntp_loopfilter.c \
+ ntp_monitor.c ntp_peer.c ntp_proto.c \
+ ntp_refclock.c ntp_request.c ntp_resolver.c \
+ ntp_restrict.c ntp_timer.c ntp_util.c \
+ ntpd.c refclock_acts.c refclock_arbiter.c \
+ refclock_arc.c refclock_as2201.c refclock_atom.c \
+ refclock_bancomm.c refclock_chronolog.c refclock_chu.c \
+ refclock_conf.c refclock_datum.c refclock_dumbclock.c \
+ refclock_fg.c refclock_gpsvme.c refclock_heath.c \
+ refclock_hopfpci.c refclock_hopfser.c refclock_hpgps.c \
+ refclock_irig.c refclock_jupiter.c refclock_leitch.c \
+ refclock_local.c refclock_msfees.c refclock_mx4200.c \
+ refclock_nmea.c refclock_oncore.c refclock_palisade.c \
+ refclock_parse.c refclock_pcf.c refclock_pst.c \
+ refclock_ptbacts.c refclock_shm.c refclock_tpro.c \
+ refclock_trak.c refclock_true.c refclock_ulink.c \
+ refclock_usno.c refclock_wwv.c refclock_wwvb.c \
+ version.c
+
+CFLAGS+= -I${.CURDIR}/../../../contrib/ntp/include -I${.CURDIR}/../
+
+DPADD= ${LIBPARSE} ${LIBNTP} -lm
+LDADD= ${LIBPARSE} ${LIBNTP} -lm
+
+CLEANFILES+= .version version.c
+
+version.c:
+ sh -e ${.CURDIR}/../scripts/mkver ntpd
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ntp/ntpdate/Makefile b/usr.sbin/ntp/ntpdate/Makefile
new file mode 100644
index 0000000..af0db44
--- /dev/null
+++ b/usr.sbin/ntp/ntpdate/Makefile
@@ -0,0 +1,19 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../contrib/ntp/ntpdate
+
+PROG= ntpdate
+NOMAN= yes
+SRCS= ntpdate.c version.c
+
+CFLAGS+= -I${.CURDIR}/../../../contrib/ntp/include -I${.CURDIR}/../
+
+DPADD= ${LIBNTP}
+LDADD= ${LIBNTP}
+
+CLEANFILES+= .version version.c
+
+version.c:
+ sh -e ${.CURDIR}/../scripts/mkver ntpdate
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ntp/ntpdc/Makefile b/usr.sbin/ntp/ntpdc/Makefile
new file mode 100644
index 0000000..d17f0ae
--- /dev/null
+++ b/usr.sbin/ntp/ntpdc/Makefile
@@ -0,0 +1,22 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../contrib/ntp/ntpdc
+
+PROG= ntpdc
+NOMAN= yes
+SRCS= ntpdc.c ntpdc_ops.c version.c
+
+CFLAGS+= -I${.CURDIR}/../../../contrib/ntp/include -I${.CURDIR}/../
+
+DPADD= ${LIBNTP} ${LIBREADLINE} ${LIBTERMCAP}
+LDADD= ${LIBNTP} -lreadline -ltermcap
+
+CLEANFILES+= .version version.c
+
+version.c:
+ sh -e ${.CURDIR}/../scripts/mkver ntpdc
+
+afterinstall:
+ rm -f ${DESTDIR}/usr/sbin/xntpdc
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ntp/ntpq/Makefile b/usr.sbin/ntp/ntpq/Makefile
new file mode 100644
index 0000000..17d5545
--- /dev/null
+++ b/usr.sbin/ntp/ntpq/Makefile
@@ -0,0 +1,21 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../contrib/ntp/ntpq
+
+BINDIR= /usr/bin
+
+PROG= ntpq
+NOMAN= yes
+SRCS= ntpq.c ntpq_ops.c version.c
+
+CFLAGS+= -I${.CURDIR}/../../../contrib/ntp/include -I${.CURDIR}/../
+
+DPADD= ${LIBNTP}
+LDADD= ${LIBNTP}
+
+CLEANFILES+= .version version.c
+
+version.c:
+ sh -e ${.CURDIR}/../scripts/mkver ntpq
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ntp/ntptime/Makefile b/usr.sbin/ntp/ntptime/Makefile
new file mode 100644
index 0000000..9e14c38
--- /dev/null
+++ b/usr.sbin/ntp/ntptime/Makefile
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../contrib/ntp/util
+
+PROG= ntptime
+NOMAN= yes
+SRCS= ntptime.c
+
+CFLAGS+= -I${.CURDIR}/../../../contrib/ntp/include -I${.CURDIR}/../
+
+DPADD= ${LIBNTP}
+LDADD= ${LIBNTP}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ntp/ntptimeset/Makefile b/usr.sbin/ntp/ntptimeset/Makefile
new file mode 100644
index 0000000..240e516
--- /dev/null
+++ b/usr.sbin/ntp/ntptimeset/Makefile
@@ -0,0 +1,19 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../contrib/ntp/ntpdate
+
+PROG= ntptimeset
+NOMAN= yes
+SRCS= ntptimeset.c ntptime_config.c version.c
+
+CFLAGS+= -I${.CURDIR}/../../../contrib/ntp/include -I${.CURDIR}/../
+
+DPADD= ${LIBNTP}
+LDADD= ${LIBNTP}
+
+CLEANFILES+= .version version.c
+
+version.c:
+ sh -e ${.CURDIR}/../scripts/mkver ntptimeset
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ntp/ntptrace/Makefile b/usr.sbin/ntp/ntptrace/Makefile
new file mode 100644
index 0000000..297339f
--- /dev/null
+++ b/usr.sbin/ntp/ntptrace/Makefile
@@ -0,0 +1,19 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../contrib/ntp/ntptrace
+
+PROG= ntptrace
+NOMAN= yes
+SRCS= ntptrace.c version.c
+
+CFLAGS+= -I${.CURDIR}/../../../contrib/ntp/include -I${.CURDIR}/../
+
+DPADD= ${LIBNTP}
+LDADD= ${LIBNTP}
+
+CLEANFILES+= .version version.c
+
+version.c:
+ sh -e ${.CURDIR}/../scripts/mkver ntptrace
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ntp/scripts/mkver b/usr.sbin/ntp/scripts/mkver
new file mode 100755
index 0000000..c4f8920
--- /dev/null
+++ b/usr.sbin/ntp/scripts/mkver
@@ -0,0 +1,41 @@
+#!/bin/sh
+#
+# $FreeBSD$
+#
+PROG=${1-UNKNOWN}
+
+ConfStr="$PROG"
+
+ConfStr="$ConfStr 4.1.0"
+
+case "" in
+ '')
+ case "1" in
+ '') ;;
+ *) ConfStr="${ConfStr}-a" ;;
+ esac
+ ;;
+ *) ConfStr="${ConfStr}-r" ;;
+esac
+
+ConfStr="$ConfStr `LC_ALL=C date`"
+
+if [ ! -f .version ]; then
+ echo 0 > .version
+fi
+RUN="`cat .version`"
+RUN="`expr $RUN + 1`"
+echo $RUN > .version
+
+ConfStr="$ConfStr (${RUN})"
+
+echo "Version <${ConfStr}>";
+
+rm -f version.c
+cat > version.c << -EoF-
+/*
+ * version file for $PROG
+ */
+#include <config.h>
+const char * Version = "${ConfStr}";
+-EoF-
diff --git a/usr.sbin/ntp/scripts/ntpver b/usr.sbin/ntp/scripts/ntpver
new file mode 100755
index 0000000..6dbc510
--- /dev/null
+++ b/usr.sbin/ntp/scripts/ntpver
@@ -0,0 +1,8 @@
+#!/bin/sh
+# $FreeBSD$
+# print version string of NTP daemon
+# Copyright (c) 1997 by Ulrich Windl
+# Modified 970318: Harlan Stenn: rewritten...
+# usage: ntpver hostname
+
+ntpq -c "rv 0 daemon_version" $* | awk '/daemon_version/ { print $2 }'
diff --git a/usr.sbin/pccard/Makefile b/usr.sbin/pccard/Makefile
new file mode 100644
index 0000000..10b8a4c
--- /dev/null
+++ b/usr.sbin/pccard/Makefile
@@ -0,0 +1,6 @@
+# Makefile for pccardc/pccardd.
+# $FreeBSD$
+
+SUBDIR= pccardc pccardd
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/pccard/Makefile.inc b/usr.sbin/pccard/Makefile.inc
new file mode 100644
index 0000000..88fdf2c
--- /dev/null
+++ b/usr.sbin/pccard/Makefile.inc
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+NOSHARED?= YES
+
+.include "../Makefile.inc"
diff --git a/usr.sbin/pccard/pccardc/Makefile b/usr.sbin/pccard/pccardc/Makefile
new file mode 100644
index 0000000..0d09f92
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/Makefile
@@ -0,0 +1,13 @@
+# pccardc Makefile
+#
+# $FreeBSD$
+
+PROG= pccardc
+MAN= pccardc.8
+SRCS= beep.c dumpcis.c enabler.c pccardc.c pccardmem.c power.c printcis.c \
+ rdattr.c rdmap.c rdreg.c readcis.c wrattr.c wrreg.c
+
+CFLAGS+= -I${.CURDIR}/../pccardd
+
+.include <bsd.prog.mk>
+.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..be62c56
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/beep.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 1995 Andrew McRae. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Code cleanup, bug-fix and extension
+ * by Tatsumi Hosokawa <hosokawa@mt.cs.keio.ac.jp>
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <ctype.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <pccard/cardinfo.h>
+
+int
+beep_main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int fd, newstat, valid = 1;
+ char name[64], *p;
+
+ if (argc != 2)
+ valid = 0;
+ if (valid) {
+ for (p = argv[1]; *p; p++) {
+ if (!isdigit(*p)) {
+ valid = 0;
+ break;
+ }
+ }
+ }
+ if (!valid)
+ errx(1, "usage: %s beep newstat", argv[0]);
+
+ sscanf(argv[1], "%d", &newstat);
+ sprintf(name, CARD_DEVICE, 0);
+ fd = open(name, O_RDWR);
+ if (fd < 0)
+ err(1, "%s", name);
+ if (ioctl(fd, PIOCSBEEP, &newstat) < 0)
+ err(1, "ioctl (PIOCSBEEP)");
+ return 0;
+}
diff --git a/usr.sbin/pccard/pccardc/dumpcis.c b/usr.sbin/pccard/pccardc/dumpcis.c
new file mode 100644
index 0000000..d5fc757
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/dumpcis.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 1995 Andrew McRae. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#include <pccard/cardinfo.h>
+#include <pccard/cis.h>
+#include "readcis.h"
+
+int nocards;
+
+static void
+scan(slot)
+ int slot;
+{
+ int fd;
+ char name[64];
+ struct cis *cp;
+ struct slotstate st;
+
+ sprintf(name, CARD_DEVICE, slot);
+ fd = open(name, O_RDONLY);
+ if (fd < 0)
+ return;
+ nocards++;
+ if (ioctl(fd, PIOCGSTATE, &st))
+ err(1, "ioctl (PIOCGSTATE)");
+ if (st.state == filled) {
+ cp = readcis(fd);
+ if (cp) {
+ printf("Configuration data for card in slot %d\n",
+ slot);
+ dumpcis(cp);
+ freecis(cp);
+ }
+ }
+}
+
+void
+dump(p, sz)
+ unsigned char *p;
+ int sz;
+{
+ int ad = 0, i;
+
+ while (sz > 0) {
+ printf("%03x: ", ad);
+ for (i = 0; i < ((sz < 16) ? sz : 16); i++)
+ printf(" %02x", p[i]);
+ printf("\n");
+ sz -= 16;
+ p += 16;
+ ad += 16;
+ }
+}
+
+void *
+xmalloc(int sz)
+{
+ void *p;
+
+ sz = (sz + 7) & ~7;
+ p = malloc(sz);
+ if (p)
+ bzero(p, sz);
+ else
+ errx(1, "malloc");
+ return (p);
+}
+
+int
+dumpcis_main(int argc, char **argv)
+{
+ int node;
+
+ for (node = 0; node < 8; node++)
+ scan(node);
+ printf("%d slots found\n", nocards);
+ return 0;
+}
diff --git a/usr.sbin/pccard/pccardc/enabler.c b/usr.sbin/pccard/pccardc/enabler.c
new file mode 100644
index 0000000..ae9fc18
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/enabler.c
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 1995 Andrew McRae. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#include <pccard/cardinfo.h>
+#include <pccard/cis.h>
+
+static void usage __P((char *));
+
+int
+enabler_main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct dev_desc drv;
+ struct mem_desc mem;
+ struct io_desc io;
+ int fd, slot, i, card_addr;
+ char name[32];
+ char *p;
+
+ bzero(&drv, sizeof(drv));
+ if (argc < 3)
+ usage("arg count");
+ slot = atoi(argv[1]);
+ if (slot < 0 || slot >= MAXSLOT)
+ usage("illegal slot number");
+ p = argv[2];
+ while (*p && (*p < '0' || *p > '9'))
+ p++;
+ if (*p == 0)
+ usage("no unit on device name");
+ drv.unit = atoi(p);
+ *p = 0;
+ strcpy(drv.name, argv[2]);
+ argv += 3;
+ argc -= 3;
+ while (argc > 1) {
+ if (strcmp(argv[0], "-m") == 0) {
+ if (argc < 4)
+ usage("memory argument error");
+ if (sscanf(argv[1], "%x", &card_addr) != 1)
+ usage("bad card address");
+ if (sscanf(argv[2], "%lx", &drv.mem) != 1)
+ usage("bad memory address");
+ if (sscanf(argv[3], "%d", &i) != 1)
+ usage("bad memory size");
+ drv.memsize = i * 1024;
+ argc -= 2;
+ argv += 2;
+ } else if (strcmp(argv[0], "-f") == 0) {
+ if (sscanf(argv[1], "%x", &drv.flags) != 1)
+ usage("bad driver flags");
+ } else if (strcmp(argv[0], "-a") == 0) {
+ if (sscanf(argv[1], "%x", &drv.iobase) != 1)
+ usage("bad I/O address");
+ } else if (strcmp(argv[0], "-i") == 0) {
+ if (sscanf(argv[1], "%d", &i) != 1 || i < 1 || i > 15)
+ usage("illegal IRQ");
+ drv.irqmask = 1 << i;
+ }
+ argc -= 2;
+ argv += 2;
+ }
+ if (argc)
+ usage("no parameter for argument");
+ printf("drv %s%d, mem 0x%lx, size %d, io %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.8 b/usr.sbin/pccard/pccardc/pccardc.8
new file mode 100644
index 0000000..147e731
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/pccardc.8
@@ -0,0 +1,271 @@
+.\"
+.\" Copyright (c) 1998 Toshihiko ARAI <toshi@jp.FreeBSD.org>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" Translated to English by Hiroki Sato <hrs@geocities.co.jp>
+.\"
+.\" $FreeBSD$
+.\"
+.Dd November 14, 1998
+.Dt PCCARDC 8
+.Os
+.Sh NAME
+.Nm pccardc
+.Nd PC-CARD (PCMCIA) management and monitoring tool
+.Sh SYNOPSIS
+.Nm
+.Ar subcommand
+.Op Ar arg ...
+.Sh DESCRIPTION
+.Nm
+controls PC-CARD slots and configures and displays information about PCMCIA cards.
+.Nm
+understands the following subcommands:
+.Pp
+.Bl -tag -width pccardmem -compact
+.It Ic beep
+Set beep type
+.It Ic dumpcis
+Print card CIS(s)
+.It Ic enabler
+Device driver enabler
+.It Ic help
+Print command summary
+.It Ic pccardmem
+Allocate memory for pccard driver
+.It Ic power
+Power on/off slots and leaves them disabled
+.It Ic rdattr
+Read attribute memory
+.It Ic rdmap
+Read pcic mappings
+.It Ic rdreg
+Read pcic register
+.It Ic wrattr
+Write byte to attribute memory
+.It Ic wrreg
+Write pcic register
+.El
+.Bl -enum
+.It
+.Ic beep Ar beep_mode
+.Pp
+Specifies the sound made upon card insertion or removal.
+This subcommand corresponds to
+.Va pccard_beep
+in
+.Xr rc.conf 5 .
+Allowed values for
+.Ar beep_mode
+are:
+.Pp
+.Bl -tag -width Ds -compact
+.It Li 0
+silent mode
+.It Li 1
+simple beep mode
+.It Li 2
+melody mode
+.El
+.It
+.Ic dumpcis
+.Op Ar slot
+.Pp
+Displays
+.Em CIS
+(Card Information Structure) tuple in EEPROM of a PC-CARD card.
+.Ar Slot
+specifies which slot to read.
+When no option is supplied, it displays
+the CIS of all of the available cards.
+.It
+.Ic enabler Ar slot driver
+.Op Fl m Ar card addr size
+.Op Fl a Ar iobase
+.Op Fl i Ar irq
+.Pp
+This is a "point enabler" which can be used to set
+parameters manually to enable a card when
+.Xr pccardd 8
+cannot allocate drivers to a PC-CARD card properly.
+Specify a PC-CARD slot in
+.Ar slot
+and a device name (such as "ed0" or "sio2") in
+.Ar driver ,
+along with some or all of the following options:
+.Bl -tag -width Ds
+.It Fl m Ar card addr size
+maps the shared-memory window of the card to host address.
+.Ar card
+is the starting address of shared-memory (hex) in the card's address space,
+.Ar addr
+is the address (hex) to map the memory to in the computer's address space, and
+.Ar size
+is the size of memory window (kb).
+.It Fl a Ar iobase
+.Ar iobase
+specifies the port a number to be mapped to the I/O window (hex)
+.It Fl i Ar irq
+.Ar irq
+specifies the IRQ (decimal from 1 to 15) the card will use for interrupts.
+.El
+.Pp
+For example,
+.Bd -literal
+ enabler 0 ed0 -m 2000 d4000 16 -a 300 -i 3
+.Ed
+.Pp
+assigns the card in slot zero to the first NE2000 ethernet card driver at
+port 0x300 and IRQ 3,
+mapping the 16KB memory region at 0x2000 in the card to 0xd4000.
+.Pp
+This allows use of some unrecognized cards with broken CIS tuples,
+and is also useful for testing a card that has not yet reported.
+However, beware that it frequently cannot recognize new cards properly.
+.It
+.Ic help
+.Pp
+Prints help for
+.Nm .
+.It
+.Ic pccardmem Ar addr
+.Pp
+Specifies the host address using PC-CARD controller(PCIC)
+will use to map cards to.
+Because the PCIC needs a contiguous 16KB memory, and most machines
+have other ROMs or devices at other places in the ISA I/O memory
+address range, generally you can only use the following four addresses:
+.Pp
+.Bl -tag -width 0xd0000 -compact
+.It Ar 0xd0000
+0xd0000-0xd3fff (default)
+.It Ar 0xd4000
+0xd4000-0xd7fff
+.It Ar 0xd8000
+0xd8000-0xdbfff
+.It Ar 0xdc000
+0xdc000-0xdffff
+.El
+.Pp
+You can use "DEFAULT" instead of 0xd0000.
+This subcommand corresponds to
+.Va pccard_mem
+in
+.Xr rc.conf 5 .
+.It
+.Ic power Ar slot power_mode
+.Pp
+Changes the state of the power supply of the card in the specified
+.Ar slot :
+.Pp
+.Bl -tag -width Ds
+.It Li 0
+Turn off a power supply.
+If a card becomes unstable when it is removed at
+activate state,
+this can force it into inactive state first and remove it safely.
+The slot will remain disabled until you eject the card with the system
+running or reenable the card with another
+.Ic pccardc power
+power command.
+The slot will even remain disabled when you suspend and resume the
+computer.
+.It Li 1
+Turn on a power supply and set it into active state
+(the system treats the card as if it was just inserted).
+.El
+.Pp
+.It
+.Ic rdattr Ar slot offs length
+.Pp
+Prints a hex dump
+.Ar length
+bytes long of the EEPROM of the card in slot
+.Ar slot
+starting at
+.Ar offs .
+All parameters are in hex.
+.It
+.Ic rdmap
+.Op Ar slot
+.Pp
+Displays where the four memory windows and two I/O windows of a PC-CARD slot
+are mapped to on the host.
+If
+.Ar slot
+is not supplied, it displays the information for all of slots in the system.
+.It
+.Ic rdreg
+.Op Ar slot
+.Pp
+Displays the 64 registers of the card in
+.Ar slot
+(all slots by default).
+.It
+.Ic wrattr Ar slot offs value
+.Pp
+Writes a single byte to the card's EEPROM at
+an offset address from the top specified in
+.Ar offs
+(hex),
+with a value specified in
+.Ar value
+(hex).
+This is preserved after the card is removed.
+.It
+.Ic wrreg Ar slot reg value
+.Pp
+Writes a register of a PC-CARD.
+Specify a PC-CARD slot number in
+.Ar slot ,
+a register number in
+.Ar reg
+(hex) and
+a value in
+.Ar value
+(hex).
+.El
+.Sh FILES
+.Bl -tag -width /etc/rc.conf -compact
+.It Pa /etc/rc.conf
+configuration file
+.El
+.Sh SEE ALSO
+.Xr rc.conf 5 ,
+.Xr pccardd 8
+.Sh AUTHORS
+.An -nosplit
+The original version was written by
+.An Andrew McRae Aq andrew@mega.com.au .
+.An Tatsumi Hosokawa Aq hosokawa@mt.cs.keio.ac.jp
+fixed bugs and added some features.
+This man page was written by
+.An Toshihiko ARAI Aq toshi@jp.FreeBSD.org .
+.Sh BUGS
+Be careful when using
+.Ic enabler
+and
+.Ic wrattr .
+Misuse can make the system unstable or damage the card.
diff --git a/usr.sbin/pccard/pccardc/pccardc.c b/usr.sbin/pccard/pccardc/pccardc.c
new file mode 100644
index 0000000..743493b
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/pccardc.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 1995 Andrew McRae. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+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(power_main);
+DECL(rdattr_main);
+DECL(rdmap_main);
+DECL(rdreg_main);
+DECL(wrattr_main);
+DECL(wrreg_main);
+
+struct {
+ char *name;
+ main_t func;
+ char *help;
+} subcommands[] = {
+ { "beep", beep_main, "Beep type" },
+ { "dumpcis", dumpcis_main, "Prints CIS for all cards" },
+ { "enabler", enabler_main, "Device driver enabler" },
+ { "help", help_main, "Prints command summary" },
+ { "pccardmem", pccardmem_main, "Allocate memory for pccard driver" },
+ { "power", power_main, "Power on/off slots" },
+ { "rdattr", rdattr_main, "Read attribute memory" },
+ { "rdmap", rdmap_main, "Read pcic mappings" },
+ { "rdreg", rdreg_main, "Read pcic register" },
+ { "wrattr", wrattr_main, "Write byte to attribute memory" },
+ { "wrreg", wrreg_main, "Write pcic register" },
+ { 0, 0 }
+};
+
+int
+main(int argc, char **argv)
+{
+ int i;
+
+ for (i = 0; argc > 1 && subcommands[i].name; i++) {
+ if (!strcmp(argv[1], subcommands[i].name)) {
+ argv[1] = argv[0];
+ return (*subcommands[i].func) (argc - 1, argv + 1);
+ }
+ }
+ if (argc > 1)
+ warnx("unknown subcommand");
+ return help_main(argc, argv);
+}
+
+int
+help_main(int argc, char **argv)
+{
+ int i;
+
+ fprintf(stderr, "usage: pccardc <subcommand> <arg> ...\n");
+ fprintf(stderr, "subcommands:\n");
+ for (i = 0; subcommands[i].name; i++)
+ fprintf(stderr, "\t%s\n\t\t%s\n",
+ subcommands[i].name, subcommands[i].help);
+ return 1;
+}
diff --git a/usr.sbin/pccard/pccardc/pccardmem.c b/usr.sbin/pccard/pccardc/pccardmem.c
new file mode 100644
index 0000000..2b79e58
--- /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[] =
+ "$FreeBSD$";
+#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/power.c b/usr.sbin/pccard/pccardc/power.c
new file mode 100644
index 0000000..59d43a5
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/power.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 1995 Andrew McRae. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* $FreeBSD$ */
+
+/*
+ * Code cleanup, bug-fix and extension
+ * by Tatsumi Hosokawa <hosokawa@mt.cs.keio.ac.jp>
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "PAO: power.c,v 1.3 1999/02/11 05:00:54 kuriyama Exp";
+#endif /* not lint */
+
+#include <ctype.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <pccard/cardinfo.h>
+
+int
+power_main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int fd, i, newstat, valid = 1;
+ char name[64], *p;
+
+ if (argc != 3)
+ valid = 0;
+ for (i = 1; i <= 2; i++) {
+ if (valid) {
+ for (p = argv[i]; *p; p++) {
+ if (!isdigit(*p)) {
+ valid = 0;
+ break;
+ }
+ }
+ }
+ }
+ if (!valid)
+ errx(1, "usage: %s power slot newstat", argv[0]);
+
+ sscanf(argv[2], "%d", &newstat);
+ sprintf(name, CARD_DEVICE, atoi(argv[1]));
+ fd = open(name, O_RDWR);
+ if (fd < 0)
+ err(1, "%s", name);
+ newstat = newstat ? 1 : 0;
+ if (ioctl(fd, PIOCSVIR, &newstat) < 0)
+ err(1, "ioctl (PIOCSVIR)");
+ return 0;
+}
diff --git a/usr.sbin/pccard/pccardc/printcis.c b/usr.sbin/pccard/pccardc/printcis.c
new file mode 100644
index 0000000..f8a53b3
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/printcis.c
@@ -0,0 +1,1107 @@
+/*
+ * Copyright (c) 1995 Andrew McRae. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/*
+ * Code cleanup, bug-fix and extension
+ * by Tatsumi Hosokawa <hosokawa@mt.cs.keio.ac.jp>
+ */
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#include <pccard/cardinfo.h>
+#include <pccard/cis.h>
+
+#include "readcis.h"
+
+static void dump_config_map(struct tuple *tp);
+static void dump_cis_config(struct tuple *tp);
+static void dump_other_cond(u_char *p, int len);
+static void dump_device_desc(u_char *p, int len, char *type);
+static void dump_info_v1(u_char *p, int len);
+static void dump_longlink_mfc(u_char *p, int len);
+static void dump_bar(u_char *p, int len);
+static void dump_device_geo(u_char *p, int len);
+static void dump_func_id(u_char *p);
+static void dump_serial_ext(u_char *p, int len);
+static void dump_disk_ext(u_char *p, int len);
+static void dump_network_ext(u_char *p, int len);
+static void dump_info_v2(u_char *p, int len);
+static void dump_org(u_char *p, int len);
+
+void
+dumpcis(struct cis *cp)
+{
+ struct tuple *tp;
+ struct tuple_list *tl;
+ int count = 0, sz, ad, i;
+ u_char *p;
+ int func = 0;
+
+ for (tl = cp->tlist; tl; tl = tl->next)
+ for (tp = tl->tuples; tp; tp = tp->next) {
+ printf("Tuple #%d, code = 0x%x (%s), length = %d\n",
+ ++count, tp->code, tuple_name(tp->code), tp->length);
+ p = tp->data;
+ sz = tp->length;
+ ad = 0;
+ while (sz > 0) {
+ printf(" %03x: ", ad);
+ for (i = 0; i < ((sz < 16) ? sz : 16); i++)
+ printf(" %02x", p[i]);
+ printf("\n");
+ sz -= 16;
+ p += 16;
+ ad += 16;
+ }
+ switch (tp->code) {
+ default:
+ break;
+ case CIS_MEM_COMMON: /* 0x01 */
+ dump_device_desc(tp->data, tp->length, "Common");
+ break;
+ case CIS_CONF_MAP_CB: /* 0x04 */
+ dump_config_map(tp);
+ break;
+ case CIS_CONFIG_CB: /* 0x05 */
+ dump_cis_config(tp);
+ break;
+ case CIS_LONGLINK_MFC: /* 0x06 */
+ dump_longlink_mfc(tp->data, tp->length);
+ break;
+ case CIS_BAR: /* 0x07 */
+ dump_bar(tp->data, tp->length);
+ break;
+ case CIS_CHECKSUM: /* 0x10 */
+ printf("\tChecksum from offset %d, length %d, value is 0x%x\n",
+ tpl16(tp->data),
+ tpl16(tp->data + 2),
+ tp->data[4]);
+ break;
+ case CIS_LONGLINK_A: /* 0x11 */
+ printf("\tLong link to attribute memory, address 0x%x\n",
+ tpl32(tp->data));
+ break;
+ case CIS_LONGLINK_C: /* 0x12 */
+ printf("\tLong link to common memory, address 0x%x\n",
+ tpl32(tp->data));
+ break;
+ case CIS_INFO_V1: /* 0x15 */
+ dump_info_v1(tp->data, tp->length);
+ break;
+ case CIS_ALTSTR: /* 0x16 */
+ break;
+ case CIS_MEM_ATTR: /* 0x17 */
+ dump_device_desc(tp->data, tp->length, "Attribute");
+ break;
+ case CIS_JEDEC_C: /* 0x18 */
+ case CIS_JEDEC_A: /* 0x19 */
+ break;
+ case CIS_CONF_MAP: /* 0x1A */
+ dump_config_map(tp);
+ break;
+ case CIS_CONFIG: /* 0x1B */
+ dump_cis_config(tp);
+ break;
+ case CIS_DEVICE_OC: /* 0x1C */
+ case CIS_DEVICE_OA: /* 0x1D */
+ dump_other_cond(tp->data, tp->length);
+ break;
+ case CIS_DEVICEGEO: /* 0x1E */
+ case CIS_DEVICEGEO_A: /* 0x1F */
+ dump_device_geo(tp->data, tp->length);
+ break;
+ case CIS_MANUF_ID: /* 0x20 */
+ printf("\tPCMCIA ID = 0x%x, OEM ID = 0x%x\n",
+ tpl16(tp->data),
+ tpl16(tp->data + 2));
+ break;
+ case CIS_FUNC_ID: /* 0x21 */
+ func = tp->data[0];
+ dump_func_id(tp->data);
+ break;
+ case CIS_FUNC_EXT: /* 0x22 */
+ switch (func) {
+ case 2:
+ dump_serial_ext(tp->data, tp->length);
+ break;
+ case 4:
+ dump_disk_ext(tp->data, tp->length);
+ break;
+ case 6:
+ dump_network_ext(tp->data, tp->length);
+ break;
+ }
+ break;
+ case CIS_VERS_2: /* 0x40 */
+ dump_info_v2(tp->data, tp->length);
+ break;
+ case CIS_ORG: /* 0x46 */
+ dump_org(tp->data, tp->length);
+ break;
+ }
+ }
+}
+
+/*
+ * CIS_CONF_MAP : Dump configuration map tuple.
+ * CIS_CONF_MAP_CB: Dump configuration map for CardBus
+ */
+static void
+dump_config_map(struct tuple *tp)
+{
+ u_char *p = tp->data, x;
+ int rlen, mlen = 0;
+ int i;
+
+ rlen = (p[0] & 3) + 1;
+ if (tp->code == CIS_CONF_MAP)
+ mlen = ((p[0] >> 2) & 3) + 1;
+ if (tp->length < rlen + mlen + 2) {
+ printf("\tWrong length for configuration map tuple\n");
+ return;
+ }
+ printf("\tReg len = %d, config register addr = 0x%x, last config = 0x%x\n",
+ rlen, parse_num(rlen | 0x10, p + 2, &p, 0), p[1]);
+ if (mlen) {
+ printf("\tRegisters: ");
+ for (i = 0; i < mlen; i++, p++) {
+ for (x = 0x1; x; x <<= 1)
+ printf("%c", x & *p ? 'X' : '-');
+ putchar(' ');
+ }
+ }
+ i = tp->length - (rlen + mlen + 2);
+ if (i) {
+ if (!mlen)
+ putchar('\t');
+ printf("%d bytes in subtuples", i);
+ }
+ if (mlen || i)
+ putchar('\n');
+}
+
+/*
+ * Dump power descriptor.
+ * call from dump_cis_config()
+ */
+static int
+print_pwr_desc(u_char *p)
+{
+ int len = 1, i;
+ u_char mask;
+ char **expp;
+ static char *pname[] =
+ {"Nominal operating supply voltage",
+ "Minimum operating supply voltage",
+ "Maximum operating supply voltage",
+ "Continuous supply current",
+ "Max current average over 1 second",
+ "Max current average over 10 ms",
+ "Power down supply current",
+ "Reserved"
+ };
+ static char *vexp[] =
+ {"10uV", "100uV", "1mV", "10mV", "100mV", "1V", "10V", "100V"};
+ static char *cexp[] =
+ {"10nA", "1uA", "10uA", "100uA", "1mA", "10mA", "100mA", "1A"};
+ static char *mant[] =
+ {"1", "1.2", "1.3", "1.5", "2", "2.5", "3", "3.5", "4", "4.5",
+ "5", "5.5", "6", "7", "8", "9"};
+
+ mask = *p++;
+ expp = vexp;
+ for (i = 0; i < 8; i++)
+ if (mask & (1 << i)) {
+ len++;
+ if (i >= 3)
+ expp = cexp;
+ printf("\t\t%s: ", pname[i]);
+ printf("%s x %s",
+ mant[(*p >> 3) & 0xF],
+ expp[*p & 7]);
+ while (*p & 0x80) {
+ len++;
+ p++;
+ printf(", ext = 0x%x", *p);
+ }
+ printf("\n");
+ p++;
+ }
+ return (len);
+}
+
+/*
+ * print_ext_speed - Print extended speed.
+ * call from dump_cis_config(), dump_device_desc()
+ */
+static void
+print_ext_speed(u_char x, int scale)
+{
+ static char *mant[] =
+ {"Reserved", "1.0", "1.2", "1.3", "1.5", "2.0", "2.5", "3.0",
+ "3.5", "4.0", "4.5", "5.0", "5.5", "6.0", "7.0", "8.0"};
+ static char *exp[] =
+ {"1 ns", "10 ns", "100 ns", "1 us", "10 us", "100 us",
+ "1 ms", "10 ms"};
+ static char *scale_name[] =
+ {"None", "10", "100", "1,000", "10,000", "100,000",
+ "1,000,000", "10,000,000"};
+
+ printf("Speed = %s x %s", mant[(x >> 3) & 0xF], exp[x & 7]);
+ if (scale)
+ printf(", scaled by %s", scale_name[scale & 7]);
+}
+
+/*
+ * Print variable length value.
+ * call from print_io_map(), print_mem_map()
+ */
+static int
+print_num(int sz, char *fmt, u_char *p, int ofs)
+{
+ switch (sz) {
+ case 0:
+ case 0x10:
+ return 0;
+ case 1:
+ case 0x11:
+ printf(fmt, *p + ofs);
+ return 1;
+ case 2:
+ case 0x12:
+ printf(fmt, tpl16(p) + ofs);
+ return 2;
+ case 0x13:
+ printf(fmt, tpl24(p) + ofs);
+ return 3;
+ case 3:
+ case 0x14:
+ printf(fmt, tpl32(p) + ofs);
+ return 4;
+ }
+ errx(1, "print_num(0x%x): Illegal arguments", sz);
+/*NOTREACHED*/
+}
+
+/*
+ * Print I/O mapping sub-tuple.
+ * call from dump_cis_config()
+ */
+static u_char *
+print_io_map(u_char *p, u_char *q)
+{
+ int i, j;
+ u_char c;
+
+ if (q <= p)
+ goto err;
+ if (CIS_IO_ADDR(*p)) /* I/O address line */
+ printf("\tCard decodes %d address lines",
+ CIS_IO_ADDR(*p));
+ else
+ printf("\tCard provides address decode");
+
+ /* 8/16 bit I/O */
+ switch (*p & (CIS_IO_8BIT | CIS_IO_16BIT)) {
+ case CIS_IO_8BIT:
+ printf(", 8 Bit I/O only");
+ break;
+ case CIS_IO_16BIT:
+ printf(", limited 8/16 Bit I/O");
+ break;
+ case (CIS_IO_8BIT | CIS_IO_16BIT):
+ printf(", full 8/16 Bit I/O");
+ break;
+ }
+ putchar('\n');
+
+ /* I/O block sub-tuple exist */
+ if (*p++ & CIS_IO_RANGE) {
+ if (q <= p)
+ goto err;
+ c = *p++;
+ /* calculate byte length */
+ j = CIS_IO_ADSZ(c) + CIS_IO_BLKSZ(c);
+ if (CIS_IO_ADSZ(c) == 3)
+ j++;
+ if (CIS_IO_BLKSZ(c) == 3)
+ j++;
+ /* number of I/O block sub-tuples */
+ for (i = 0; i <= CIS_IO_BLKS(c); i++) {
+ if (q - p < j)
+ goto err;
+ printf("\t\tI/O address # %d: ", i + 1);
+ /* start block address */
+ p += print_num(CIS_IO_ADSZ(c),
+ "block start = 0x%x", p, 0);
+ /* block size */
+ p += print_num(CIS_IO_BLKSZ(c),
+ " block length = 0x%x", p, 1);
+ putchar('\n');
+ }
+ }
+ return p;
+
+ err: /* warning */
+ printf("\tWrong length for I/O mapping sub-tuple\n");
+ return p;
+}
+
+/*
+ * Print IRQ sub-tuple.
+ * call from dump_cis_config()
+ */
+static u_char *
+print_irq_map(u_char *p, u_char *q)
+{
+ int i, j;
+ u_char c;
+
+ if (q <= p)
+ goto err;
+ printf("\t\tIRQ modes:");
+ c = ' ';
+ if (*p & CIS_IRQ_LEVEL) { /* Level triggered interrupts */
+ printf(" Level");
+ c = ',';
+ }
+ if (*p & CIS_IRQ_PULSE) { /* Pulse triggered requests */
+ printf("%c Pulse", c);
+ c = ',';
+ }
+ if (*p & CIS_IRQ_SHARING) /* Interrupt sharing */
+ printf("%c Shared", c);
+ putchar('\n');
+
+ /* IRQ mask values exist */
+ if (*p & CIS_IRQ_MASK) {
+ if (q - p < 3)
+ goto err;
+ i = tpl16(p + 1); /* IRQ mask */
+ printf("\t\tIRQs: ");
+ if (*p & 1)
+ printf(" NMI");
+ if (*p & 0x2)
+ printf(" IOCK");
+ if (*p & 0x4)
+ printf(" BERR");
+ if (*p & 0x8)
+ printf(" VEND");
+ for (j = 0; j < 16; j++)
+ if (i & (1 << j))
+ printf(" %d", j);
+ putchar('\n');
+ p += 3;
+ } else {
+ printf("\t\tIRQ level = %d\n", CIS_IRQ_IRQN(*p));
+ p++;
+ }
+ return p;
+
+ err: /* warning */
+ printf("\tWrong length for IRQ sub-tuple\n");
+ return p;
+}
+
+/*
+ * Print memory map sub-tuple.
+ * call from dump_cis_config()
+ */
+static u_char *
+print_mem_map(u_char feat, u_char *p, u_char *q)
+{
+ int i, j;
+ u_char c;
+
+ switch (CIS_FEAT_MEMORY(feat)) {
+
+ case CIS_FEAT_MEM_NONE: /* No memory block */
+ break;
+ case CIS_FEAT_MEM_LEN: /* Specify memory length */
+ if (q - p < 2)
+ goto err;
+ printf("\tMemory space length = 0x%x\n", tpl16(p));
+ p += 2;
+ break;
+ case CIS_FEAT_MEM_ADDR: /* Memory address and length */
+ if (q - p < 4)
+ goto err;
+ printf("\tMemory space address = 0x%x, length = 0x%x\n",
+ tpl16(p + 2), tpl16(p));
+ p += 4;
+ break;
+ case CIS_FEAT_MEM_WIN: /* Memory descriptors. */
+ if (q <= p)
+ goto err;
+ c = *p++;
+ /* calculate byte length */
+ j = CIS_MEM_LENSZ(c) + CIS_MEM_ADDRSZ(c);
+ if (c & CIS_MEM_HOST)
+ j += CIS_MEM_ADDRSZ(c);
+ /* number of memory block */
+ for (i = 0; i < CIS_MEM_WINS(c); i++) {
+ if (q - p < j)
+ goto err;
+ printf("\tMemory descriptor %d\n\t\t", i + 1);
+ /* memory length */
+ p += print_num(CIS_MEM_LENSZ(c) | 0x10,
+ " blk length = 0x%x00", p, 0);
+ /* card address */
+ p += print_num(CIS_MEM_ADDRSZ(c) | 0x10,
+ " card addr = 0x%x00", p, 0);
+ if (c & CIS_MEM_HOST) /* Host address value exist */
+ p += print_num(CIS_MEM_ADDRSZ(c) | 0x10,
+ " host addr = 0x%x00", p, 0);
+ putchar('\n');
+ }
+ break;
+ }
+ return p;
+
+ err: /* warning */
+ printf("\tWrong length for memory mapping sub-tuple\n");
+ return p;
+}
+
+/*
+ * CIS_CONFIG : Dump a config entry.
+ * CIS_CONFIG_CB: Dump a configuration entry for CardBus
+ */
+static void
+dump_cis_config(struct tuple *tp)
+{
+ u_char *p, *q, feat;
+ int i, j;
+ char c;
+
+ p = tp->data;
+ q = p + tp->length;
+ printf("\tConfig index = 0x%x%s\n", *p & 0x3F,
+ *p & 0x40 ? "(default)" : "");
+
+ /* Interface byte exists */
+ if (tp->code == CIS_CONFIG && (*p & 0x80)) {
+ p++;
+ printf("\tInterface byte = 0x%x ", *p);
+ switch (*p & 0xF) { /* Interface type */
+ default:
+ printf("(reserved)");
+ break;
+ case 0:
+ printf("(memory)");
+ break;
+ case 1:
+ printf("(I/O)");
+ break;
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ printf("(custom)");
+ break;
+ }
+ c = ' ';
+ if (*p & 0x10) { /* Battery voltage detect */
+ printf(" BVD1/2 active");
+ c = ',';
+ }
+ if (*p & 0x20) { /* Write protect active */
+ printf("%c card WP active", c); /* Write protect */
+ c = ',';
+ }
+ if (*p & 0x40) { /* RdyBsy active bit */
+ printf("%c +RDY/-BSY active", c);
+ c = ',';
+ }
+ if (*p & 0x80) /* Wait signal required */
+ printf("%c wait signal supported", c);
+ printf("\n");
+ }
+
+ /* features byte */
+ p++;
+ feat = *p++;
+
+ /* Power structure sub-tuple */
+ switch (CIS_FEAT_POWER(feat)) { /* Power sub-tuple(s) exists */
+ case 0:
+ break;
+ case 1:
+ printf("\tVcc pwr:\n");
+ p += print_pwr_desc(p);
+ break;
+ case 2:
+ printf("\tVcc pwr:\n");
+ p += print_pwr_desc(p);
+ printf("\tVpp pwr:\n");
+ p += print_pwr_desc(p);
+ break;
+ case 3:
+ printf("\tVcc pwr:\n");
+ p += print_pwr_desc(p);
+ printf("\tVpp1 pwr:\n");
+ p += print_pwr_desc(p);
+ printf("\tVpp2 pwr:\n");
+ p += print_pwr_desc(p);
+ break;
+ }
+
+ /* Timing sub-tuple */
+ if (tp->code == CIS_CONFIG &&
+ (feat & CIS_FEAT_TIMING)) { /* Timing sub-tuple exists */
+ i = *p++;
+ j = CIS_WAIT_SCALE(i);
+ if (j != 3) {
+ printf("\tWait scale ");
+ print_ext_speed(*p++, j);
+ printf("\n");
+ }
+ j = CIS_READY_SCALE(i);
+ if (j != 7) {
+ printf("\tRDY/BSY scale ");
+ print_ext_speed(*p++, j);
+ printf("\n");
+ }
+ j = CIS_RESERVED_SCALE(i);
+ if (j != 7) {
+ printf("\tExternal scale ");
+ print_ext_speed(*p++, j);
+ printf("\n");
+ }
+ }
+
+ /* I/O mapping sub-tuple */
+ if (feat & CIS_FEAT_I_O) { /* I/O space sub-tuple exists */
+ if (tp->code == CIS_CONFIG)
+ p = print_io_map(p, q);
+ else { /* CIS_CONFIG_CB */
+ printf("\tI/O base:");
+ for (i = 0; i < 8; i++)
+ if (*p & (1 << i))
+ printf(" %d", i);
+ putchar('\n');
+ p++;
+ }
+ }
+
+ /* IRQ descriptor sub-tuple */
+ if (feat & CIS_FEAT_IRQ) /* IRQ sub-tuple exists */
+ p = print_irq_map(p, q);
+
+ /* Memory map sub-tuple */
+ if (CIS_FEAT_MEMORY(feat)) { /* Memory space sub-tuple(s) exists */
+ if (tp->code == CIS_CONFIG)
+ p = print_mem_map(feat, p, q);
+ else { /* CIS_CONFIG_CB */
+ printf("\tMemory base:");
+ for (i = 0; i < 8; i++)
+ if (*p & (1 << i))
+ printf(" %d", i);
+ putchar('\n');
+ p++;
+ }
+ }
+
+ /* Misc sub-tuple */
+ if (feat & CIS_FEAT_MISC) { /* Miscellaneous sub-tuple exists */
+ if (tp->code == CIS_CONFIG) {
+ printf("\tMax twin cards = %d\n", *p & 7);
+ printf("\tMisc attr:%s%s%s",
+ (*p & 8) ? " (Audio-BVD2)" : "",
+ (*p & 0x10) ? " (Read-only)" : "",
+ (*p & 0x20) ? " (Power down supported)" : "");
+ if (*p++ & 0x80) {
+ printf(" (Ext byte = 0x%x)", *p);
+ p++;
+ }
+ putchar('\n');
+ }
+ else { /* CIS_CONFIG_CB */
+ printf("\tMisc attr:");
+ printf("%s%s%s%s%s%s%s",
+ (*p & 1) ? " (Master)" : "",
+ (*p & 2) ? " (Invalidate)" : "",
+ (*p & 4) ? " (VGA palette)" : "",
+ (*p & 8) ? " (Parity)" : "",
+ (*p & 0x10) ? " (Wait)" : "",
+ (*p & 0x20) ? " (Serr)" : "",
+ (*p & 0x40) ? " (Fast back)" : "");
+ if (*p++ & 0x80) {
+ printf("%s%s",
+ (*p & 1) ? " (Binary audio)" : "",
+ (*p & 2) ? " (pwm audio)" : "");
+ p++;
+ }
+ putchar('\n');
+ }
+ }
+}
+
+/*
+ * CIS_DEVICE_OC, CIS_DEVICE_OA:
+ * Dump other conditions for common/attribute memory
+ */
+static void
+dump_other_cond(u_char *p, int len)
+{
+ if (p[0] && len > 0) {
+ printf("\t");
+ if (p[0] & 1)
+ printf("(MWAIT)");
+ if (p[0] & 2)
+ printf(" (3V card)");
+ if (p[0] & 0x80)
+ printf(" (Extension bytes follow)");
+ printf("\n");
+ }
+}
+
+/*
+ * CIS_MEM_COMMON, CIS_MEM_ATTR:
+ * Common / Attribute memory descripter
+ */
+static void
+dump_device_desc(u_char *p, int len, char *type)
+{
+ static char *un_name[] =
+ {"512b", "2Kb", "8Kb", "32Kb", "128Kb", "512Kb", "2Mb", "reserved"};
+ static char *speed[] =
+ {"No speed", "250nS", "200nS", "150nS",
+ "100nS", "Reserved", "Reserved"};
+ static char *dev[] =
+ {"No device", "Mask ROM", "OTPROM", "UV EPROM",
+ "EEPROM", "FLASH EEPROM", "SRAM", "DRAM",
+ "Reserved", "Reserved", "Reserved", "Reserved",
+ "Reserved", "Function specific", "Extended",
+ "Reserved"};
+ int count = 0;
+
+ while (*p != 0xFF && len > 0) {
+ u_char x;
+
+ x = *p++;
+ len -= 2;
+ if (count++ == 0)
+ printf("\t%s memory device information:\n", type);
+ printf("\t\tDevice number %d, type %s, WPS = %s\n",
+ count, dev[x >> 4], (x & 0x8) ? "ON" : "OFF");
+ if ((x & 7) == 7) {
+ len--;
+ if (*p) {
+ printf("\t\t");
+ print_ext_speed(*p, 0);
+ while (*p & 0x80) {
+ p++;
+ len--;
+ }
+ }
+ p++;
+ } else
+ printf("\t\tSpeed = %s", speed[x & 7]);
+ printf(", Memory block size = %s, %d units\n",
+ un_name[*p & 7], (*p >> 3) + 1);
+ p++;
+ }
+}
+
+/*
+ * CIS_INFO_V1: Print version-1 info
+ */
+static void
+dump_info_v1(u_char *p, int len)
+{
+ if (len < 2) {
+ printf("\tWrong length for version-1 info tuple\n");
+ return;
+ }
+ printf("\tVersion = %d.%d", p[0], p[1]);
+ p += 2;
+ len -= 2;
+ if (len > 1 && *p != 0xff) {
+ printf(", Manuf = [%s]", p);
+ while (*p++ && --len > 0);
+ }
+ if (len > 1 && *p != 0xff) {
+ printf(", card vers = [%s]", p);
+ while (*p++ && --len > 0);
+ } else {
+ printf("\n\tWrong length for version-1 info tuple\n");
+ return;
+ }
+ putchar('\n');
+ if (len > 1 && *p != 0xff) {
+ printf("\tAddit. info = [%.*s]", len, p);
+ while (*p++ && --len > 0);
+ if (len > 1 && *p != 0xff)
+ printf(",[%.*s]", len, p);
+ putchar('\n');
+ }
+}
+
+/*
+ * CIS_FUNC_ID: Functional ID
+ */
+static void
+dump_func_id(u_char *p)
+{
+ static char *id[] = {
+ "Multifunction card",
+ "Memory card",
+ "Serial port/modem",
+ "Parallel port",
+ "Fixed disk card",
+ "Video adapter",
+ "Network/LAN adapter",
+ "AIMS",
+ "SCSI card",
+ "Security"
+ };
+
+ printf("\t%s%s%s\n",
+ (*p <= 9) ? id[*p] : "Unknown function",
+ (p[1] & 1) ? " - POST initialize" : "",
+ (p[1] & 2) ? " - Card has ROM" : "");
+}
+
+/*
+ * CIS_FUNC_EXT: Dump functional extension tuple.
+ * (Serial port/modem)
+ */
+static void
+dump_serial_ext(u_char *p, int len)
+{
+ static char *type[] = {
+ "", "Modem", "Data", "Fax", "Voice", "Data modem",
+ "Fax/modem", "Voice", " (Data)", " (Fax)", " (Voice)"
+ };
+
+ if (len < 1)
+ return;
+ switch (p[0]) {
+ case 0: /* Serial */
+ case 8: /* Data */
+ case 9: /* Fax */
+ case 10: /* Voice */
+ printf("\tSerial interface extension:%s\n", type[*p]);
+ if (len < 4)
+ goto err;
+ switch (p[1] & 0x1F) {
+ default:
+ printf("\t\tUnkn device");
+ break;
+ case 0:
+ printf("\t\t8250 UART");
+ break;
+ case 1:
+ printf("\t\t16450 UART");
+ break;
+ case 2:
+ printf("\t\t16550 UART");
+ break;
+ }
+ printf(", Parity - %s%s%s%s\n",
+ (p[2] & 1) ? "Space," : "",
+ (p[2] & 2) ? "Mark," : "",
+ (p[2] & 4) ? "Odd," : "",
+ (p[2] & 8) ? "Even" : "");
+ printf("\t\tData bit - %s%s%s%s Stop bit - %s%s%s\n",
+ (p[3] & 1) ? "5bit," : "",
+ (p[3] & 2) ? "6bit," : "",
+ (p[3] & 4) ? "7bit," : "",
+ (p[3] & 8) ? "8bit," : "",
+ (p[3] & 0x10) ? "1bit," : "",
+ (p[3] & 0x20) ? "1.5bit," : "",
+ (p[3] & 0x40) ? "2bit" : "");
+ break;
+ case 1: /* Serial */
+ case 5: /* Data */
+ case 6: /* Fax */
+ case 7: /* Voice */
+ printf("\t%s interface capabilities:\n", type[*p]);
+ if (len < 9)
+ goto err;
+ break;
+ case 2: /* Data */
+ printf("\tData modem services available:\n");
+ break;
+ case 0x13: /* Fax1 */
+ case 0x23: /* Fax2 */
+ case 0x33: /* Fax3 */
+ printf("\tFax%d/modem services available:\n", *p >> 4);
+ break;
+ case 0x84: /* Voice */
+ printf("\tVoice services available:\n");
+ break;
+ err: /* warning */
+ printf("\tWrong length for serial extension tuple\n");
+ return;
+ }
+}
+
+/*
+ * CIS_FUNC_EXT: Dump functional extension tuple.
+ * (Fixed disk card)
+ */
+static void
+dump_disk_ext(u_char *p, int len)
+{
+ if (len < 1)
+ return;
+ switch (p[0]) {
+ case 1: /* IDE interface */
+ if (len < 2)
+ goto err;
+ printf("\tDisk interface: %s\n",
+ (p[1] & 1) ? "IDE" : "Undefined");
+ break;
+ case 2: /* Master */
+ case 3: /* Slave */
+ if (len < 3)
+ goto err;
+ printf("\tDisk features: %s, %s%s\n",
+ (p[1] & 0x04) ? "Silicon" : "Rotating",
+ (p[1] & 0x08) ? "Unique, " : "",
+ (p[1] & 0x10) ? "Dual" : "Single");
+ if (p[2] & 0x7f)
+ printf("\t\t%s%s%s%s%s%s%s\n",
+ (p[2] & 0x01) ? "Sleep, " : "",
+ (p[2] & 0x02) ? "Standby, " : "",
+ (p[2] & 0x04) ? "Idle, " : "",
+ (p[2] & 0x08) ? "Low power, " : "",
+ (p[2] & 0x10) ? "Reg inhibit, " : "",
+ (p[2] & 0x20) ? "Index, " : "",
+ (p[2] & 0x40) ? "Iois16" : "");
+ break;
+ err: /* warning */
+ printf("\tWrong length for fixed disk extension tuple\n");
+ return;
+ }
+}
+
+static void
+print_speed(u_int i)
+{
+ if (i < 1000)
+ printf("%u bits/sec", i);
+ else if (i < 1000000)
+ printf("%u kb/sec", i / 1000);
+ else
+ printf("%u Mb/sec", i / 1000000);
+}
+
+/*
+ * CIS_FUNC_EXT: Dump functional extension tuple.
+ * (Network/LAN adapter)
+ */
+static void
+dump_network_ext(u_char *p, int len)
+{
+ static const char *tech[] = {
+ "Undefined", "ARCnet", "Ethernet", "Token ring",
+ "Localtalk", "FDDI/CDDI", "ATM", "Wireless"
+ };
+ static const char *media[] = {
+ "Undefined", "UTP", "STP", "Thin coax",
+ "THICK coax", "Fiber", "900 MHz", "2.4 GHz",
+ "5.4 GHz", "Diffuse Infrared", "Point to point Infrared"
+ };
+ u_int i = 0;
+
+ if (len < 1)
+ return;
+ switch (p[0]) {
+ case 1: /* Network technology */
+ if (len < 2)
+ goto err;
+ printf("\tNetwork technology: %s\n", tech[p[1] & 7]);
+ break;
+ case 2: /* Network speed */
+ if (len < 5)
+ goto err;
+ printf("\tNetwork speed: ");
+ print_speed(tpl32(p + 1));
+ putchar('\n');
+ break;
+ case 3: /* Network media */
+ if (len < 2)
+ goto err;
+ if (p[1] <= 10)
+ i = p[1];
+ printf("\tNetwork media: %s\n", media[i]);
+ break;
+ case 4: /* Node ID */
+ if (len <= 2 || len < p[1] + 2)
+ goto err;
+ printf("\tNetwork node ID:");
+ for (i = 0; i < p[1]; i++)
+ printf(" %02x", p[i + 2]);
+ putchar('\n');
+ break;
+ case 5: /* Connecter type */
+ if (len < 2)
+ goto err;
+ printf("\tNetwork connector: %s connector standard\n",
+ (p[1] == 0) ? "open" : "closed");
+ break;
+ err: /* warning */
+ printf("\tWrong length for network extension tuple\n");
+ return;
+ }
+}
+
+/*
+ * CIS_LONGLINK_MFC: Long link to next chain for Multi function card
+ */
+static void
+dump_longlink_mfc(u_char *p, int len)
+{
+ u_int i, n = *p++;
+
+ --len;
+ for (i = 0; i < n; i++) {
+ if (len < 5) {
+ printf("\tWrong length for long link MFC tuple\n");
+ return;
+ }
+ printf("\tFunction %d: %s memory, address 0x%x\n",
+ i, (*p ? "common" : "attribute"), tpl32(p + 1));
+ p += 5;
+ len -= 5;
+ }
+}
+
+/*
+ * CIS_DEVICEGEO, CIS_DEVICEGEO_A:
+ * Geometry info for common/attribute memory
+ */
+static void
+dump_device_geo(u_char *p, int len)
+{
+ while (len >= 6) {
+ printf("\twidth = %d, erase = 0x%x, read = 0x%x, write = 0x%x\n"
+ "\t\tpartition = 0x%x, interleave = 0x%x\n",
+ p[0], 1 << (p[1] - 1),
+ 1 << (p[2] - 1), 1 << (p[3] - 1),
+ 1 << (p[4] - 1), 1 << (p[5] - 1));
+ len -= 6;
+ }
+}
+
+/*
+ * CIS_INFO_V2: Print version-2 info
+ */
+static void
+dump_info_v2(u_char *p, int len)
+{
+ if (len < 9) {
+ printf("\tWrong length for version-2 info tuple\n");
+ return;
+ }
+ printf("\tVersion = 0x%x, compliance = 0x%x, dindex = 0x%x\n",
+ p[0], p[1], tpl16(p + 2));
+ printf("\tVspec8 = 0x%x, vspec9 = 0x%x, nhdr = %d\n",
+ p[6], p[7], p[8]);
+ p += 9;
+ len -= 9;
+ if (len <= 1 || *p == 0xff)
+ return;
+ printf("\tVendor = [%.*s]", len, p);
+ while (*p++ && --len > 0);
+ if (len > 1 && *p != 0xff)
+ printf(", info = [%.*s]", len, p);
+ putchar('\n');
+}
+
+/*
+ * CIS_ORG: Organization
+ */
+static void
+dump_org(u_char *p, int len)
+{
+ if (len < 1) {
+ printf("\tWrong length for organization tuple\n");
+ return;
+ }
+ switch (*p) {
+ case 0:
+ printf("\tFilesystem");
+ break;
+ case 1:
+ printf("\tApp specific");
+ break;
+ case 2:
+ printf("\tCode");
+ break;
+ default:
+ if (*p < 0x80)
+ printf("\tReserved");
+ else
+ printf("\tVendor specific");
+ break;
+ }
+ printf(" [%.*s]\n", len - 1, p + 1);
+}
+
+static void
+print_size(u_int i)
+{
+ if (i < 1024)
+ printf("%ubits", i);
+ else if (i < 1024*1024)
+ printf("%ukb", i / 1024);
+ else
+ printf("%uMb", i / (1024*1024));
+}
+
+/*
+ * CIS_BAR: Base address register for CardBus
+ */
+static void
+dump_bar(u_char *p, int len)
+{
+ if (len < 6) {
+ printf("\tWrong length for BAR tuple\n");
+ return;
+ }
+ printf("\tBAR %d: size = ", *p & 7);
+ print_size(tpl32(p + 2));
+ printf(", %s%s%s%s\n",
+ (*p & 0x10) ? "I/O" : "Memory",
+ (*p & 0x20) ? ", Prefetch" : "",
+ (*p & 0x40) ? ", Cacheable" : "",
+ (*p & 0x80) ? ", <1Mb" : "");
+}
diff --git a/usr.sbin/pccard/pccardc/rdattr.c b/usr.sbin/pccard/pccardc/rdattr.c
new file mode 100644
index 0000000..1f90c77
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/rdattr.c
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 1995 Andrew McRae. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+#include <pccard/cardinfo.h>
+
+int
+rdattr_main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int i, reg, length;
+ char name[64];
+ u_char *buf;
+ int fd;
+ off_t offs;
+
+ if (argc != 4)
+ errx(1, "Usage: %s rdattr slot offs length", argv[0]);
+
+ sprintf(name, CARD_DEVICE, atoi(argv[1]));
+ fd = open(name, O_RDONLY);
+ if (fd < 0)
+ err(1, "%s", name);
+
+ reg = MDF_ATTR;
+ if (ioctl(fd, PIOCRWFLAG, &reg))
+ err(1, "ioctl (PIOCRWFLAG)");
+
+ if (sscanf(argv[2], "%x", &reg) != 1 ||
+ sscanf(argv[3], "%x", &length) != 1)
+ errx(1, "arg error");
+
+ offs = reg;
+ if ((buf = malloc(length)) == 0)
+ errx(1, "malloc failed");
+
+ lseek(fd, offs, SEEK_SET);
+ if (read(fd, buf, length) != length)
+ err(1, "%s", name);
+
+ for (i = 0; i < length; i++) {
+ if (i % 16 == 0) {
+ printf("%04x: ", (int) offs + i);
+ }
+ printf("%02x ", buf[i]);
+ if (i % 16 == 15) {
+ printf("\n");
+ }
+ }
+ if (i % 16 != 0) {
+ printf("\n");
+ }
+ return 0;
+}
diff --git a/usr.sbin/pccard/pccardc/rdmap.c b/usr.sbin/pccard/pccardc/rdmap.c
new file mode 100644
index 0000000..3ac81f9
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/rdmap.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 1995 Andrew McRae. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#include <pccard/cardinfo.h>
+#include <pccard/cis.h>
+
+static void
+dump_io(fd, nio)
+ int fd, nio;
+{
+ struct io_desc io;
+ int i;
+
+ for (i = 0; i < nio; i++) {
+ io.window = i;
+ if (ioctl(fd, PIOCGIO, &io))
+ err(1, "ioctl (PIOCGIO)");
+ printf("I/O %d: flags 0x%03x port 0x%3x size %d bytes\n",
+ io.window, io.flags, io.start, io.size);
+ }
+}
+
+static void
+dump_mem(fd, nmem)
+ int fd, nmem;
+{
+ struct mem_desc mem;
+ int i;
+
+ for (i = 0; i < nmem; i++) {
+ mem.window = i;
+ if (ioctl(fd, PIOCGMEM, &mem))
+ err(1, "ioctl (PIOCGMEM)");
+ printf("Mem %d: flags 0x%03x host %p card %04lx size %d bytes\n",
+ mem.window, mem.flags, mem.start, mem.card, mem.size);
+ }
+}
+
+static void
+scan(slot)
+ int slot;
+{
+ int fd;
+ char name[64];
+ struct slotstate st;
+
+ sprintf(name, CARD_DEVICE, slot);
+ fd = open(name, O_RDONLY);
+ if (fd < 0)
+ return;
+ if (ioctl(fd, PIOCGSTATE, &st))
+ err(1, "ioctl (PIOCGSTATE)");
+/*
+ if (st.state == filled)
+ */
+ {
+ dump_mem(fd, st.maxmem);
+ dump_io(fd, st.maxio);
+ }
+ close(fd);
+}
+
+int
+rdmap_main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int node;
+
+ for (node = 0; node < 8; node++)
+ scan(node);
+ exit(0);
+}
diff --git a/usr.sbin/pccard/pccardc/rdreg.c b/usr.sbin/pccard/pccardc/rdreg.c
new file mode 100644
index 0000000..a317148
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/rdreg.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 1995 Andrew McRae. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <pccard/cardinfo.h>
+
+void
+dumpslot(sl)
+ int sl;
+{
+ char name[64];
+ int fd;
+ struct pcic_reg r;
+
+ sprintf(name, CARD_DEVICE, sl);
+ fd = open(name, O_RDONLY);
+ if (fd < 0) {
+ warn("%s", name);
+ return;
+ }
+ printf("Registers for slot %d\n", sl);
+ for (r.reg = 0; r.reg < 0x40; r.reg++) {
+ if (ioctl(fd, PIOCGREG, &r)) {
+ err(1, "ioctl (PIOCGREG)");
+ break;
+ }
+ if ((r.reg % 16) == 0)
+ printf("%02x:", r.reg);
+ printf(" %02x", r.value);
+ if ((r.reg % 16) == 15)
+ printf("\n");
+ }
+ close(fd);
+}
+
+int
+rdreg_main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ if (argc != 2) {
+ dumpslot(0);
+ dumpslot(1);
+ } else
+ dumpslot(atoi(argv[1]));
+ return 0;
+}
diff --git a/usr.sbin/pccard/pccardc/wrattr.c b/usr.sbin/pccard/pccardc/wrattr.c
new file mode 100644
index 0000000..cc3df8e
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/wrattr.c
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 1995 Andrew McRae. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <pccard/cardinfo.h>
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: pccardc wrattr slot offs value\n");
+ exit(1);
+}
+
+int
+wrattr_main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int reg, value;
+ char name[64], c;
+ int fd;
+ off_t offs;
+
+ if (argc != 4)
+ usage();
+ sprintf(name, CARD_DEVICE, atoi(argv[1]));
+ fd = open(name, O_RDWR);
+ if (fd < 0)
+ err(1, "%s", name);
+
+ reg = MDF_ATTR;
+ if (ioctl(fd, PIOCRWFLAG, &reg))
+ err(1, "ioctl (PIOCRWFLAG)");
+
+ if (sscanf(argv[2], "%x", &reg) != 1 ||
+ sscanf(argv[3], "%x", &value) != 1)
+ errx(1, "arg error");
+
+ offs = reg;
+ c = value;
+ lseek(fd, offs, SEEK_SET);
+ if (write(fd, &c, 1) != 1)
+ err(1, "%s", name);
+ return 0;
+}
diff --git a/usr.sbin/pccard/pccardc/wrreg.c b/usr.sbin/pccard/pccardc/wrreg.c
new file mode 100644
index 0000000..5fda2a7
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/wrreg.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 1995 Andrew McRae. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <pccard/cardinfo.h>
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: pccardc wrreg slot reg value\n");
+ exit(1);
+}
+
+int
+wrreg_main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int reg, value;
+ char name[64];
+ int fd;
+ struct pcic_reg r;
+
+ if (argc != 4)
+ usage();
+ sprintf(name, CARD_DEVICE, atoi(argv[1]));
+ fd = open(name, O_RDWR);
+ if (fd < 0)
+ err(1, "%s", name);
+ if (sscanf(argv[2], "%x", &reg) != 1 ||
+ sscanf(argv[3], "%x", &value) != 1)
+ errx(1, "arg error");
+ r.reg = reg;
+ r.value = value;
+ if (ioctl(fd, PIOCSREG, &r))
+ err(1, "ioctl (PIOCSREG)");
+ return 0;
+}
diff --git a/usr.sbin/pccard/pccardd/Makefile b/usr.sbin/pccard/pccardd/Makefile
new file mode 100644
index 0000000..0fb8c6e
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/Makefile
@@ -0,0 +1,14 @@
+# Makefile for pccardd
+# $FreeBSD$
+
+PROG= pccardd
+MAN= pccard.conf.5 pccardd.8
+SRCS= pccardd.c cardd.c file.c util.c readcis.c printcis.c server.c
+
+CFLAGS+= -I${.CURDIR} -I${.CURDIR}/../pccardc
+
+DPADD= ${LIBUTIL}
+LDADD= -lutil
+
+.include <bsd.prog.mk>
+.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..c2431bf
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/cardd.c
@@ -0,0 +1,1017 @@
+/*
+ * Copyright (c) 1995 Andrew McRae. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <regex.h>
+#include <machine/resource.h>
+#include <sys/ioctl.h>
+#include "cardd.h"
+
+static struct card_config *assign_driver(struct slot *, struct card *);
+static int assign_io(struct slot *);
+static int setup_slot(struct slot *);
+static void card_inserted(struct slot *);
+static void card_removed(struct slot *);
+static void pr_cmd(struct cmd *);
+static void read_ether(struct slot *);
+static void read_ether_attr2(struct slot *sp);
+
+struct slot *slots;
+
+/*
+ * Dump configuration file data.
+ */
+void
+dump_config_file(void)
+{
+ struct card *cp;
+ struct card_config *confp;
+
+ for (cp = cards; cp; cp = cp->next) {
+ printf("Card manuf %s, vers %s\n", cp->manuf, cp->version);
+ printf("Configuration entries:\n");
+ for (confp = cp->config; confp; confp = confp->next) {
+ printf("\tIndex code = ");
+ switch (confp->index_type) {
+ case DEFAULT_INDEX:
+ printf("default");
+ break;
+ case AUTO_INDEX:
+ printf("auto");
+ break;
+ default:
+ printf("0x%x", confp->index);
+ break;
+ }
+ printf(", driver name = %s\n", confp->driver->name);
+ }
+ if (cp->insert) {
+ printf("Insert commands are:\n");
+ pr_cmd(cp->insert);
+ }
+ if (cp->remove) {
+ printf("Remove commands are:\n");
+ pr_cmd(cp->remove);
+ }
+ }
+ fflush(stdout);
+}
+
+static void
+pr_cmd(struct cmd *cp)
+{
+ while (cp) {
+ printf("\t%s\n", cp->line);
+ cp = cp->next;
+ }
+}
+
+/*
+ * readslots - read all the PCMCIA slots, and build
+ * a list of the slots.
+ */
+struct slot *
+readslots(void)
+{
+ char name[128];
+ int i, fd;
+ struct slot *sp;
+
+ slots = NULL;
+ for (i = 0; i < MAXSLOT; i++) {
+ sprintf(name, CARD_DEVICE, i);
+ fd = open(name, O_RDWR);
+ if (fd < 0)
+ continue;
+ sp = xmalloc(sizeof(*sp));
+ sp->fd = fd;
+ sp->name = newstr(name);
+ sp->slot = i;
+
+ /* Check to see if the controller memory has been set up. */
+ if (slots == 0) {
+ unsigned long mem = 0;
+
+ if (ioctl(fd, PIOCRWMEM, &mem))
+ logerr("ioctl (PIOCRWMEM)");
+#ifdef DEBUG
+ logmsg("mem=0x%x\n", mem);
+#endif
+ if (mem == 0) {
+ mem = alloc_memory(4 * 1024);
+ if (mem == 0)
+ die("can't allocate memory for "
+ "controller access");
+ if (ioctl(fd, PIOCRWMEM, &mem))
+ logerr("ioctl (PIOCRWMEM)");
+ }
+ }
+ sp->next = slots;
+ slots = sp;
+ slot_change(sp);
+ }
+ return (slots);
+}
+
+/*
+ * slot_change - Card status has changed.
+ * read new state and process.
+ */
+void
+slot_change(struct slot *sp)
+{
+ struct slotstate state;
+
+ if (ioctl(sp->fd, PIOCGSTATE, &state)) {
+ logerr("ioctl (PIOCGSTATE)");
+ return;
+ }
+ switch (state.state) {
+ case empty:
+ case inactive:
+ case noslot:
+ /* Debounce potentially incorrectly reported removals */
+ if (state.laststate == filled || state.laststate == suspend)
+ card_removed(sp);
+ break;
+ case filled:
+ /* KLUDGE: if we were suspended, remove card */
+ if (state.laststate == suspend)
+ card_removed(sp);
+ card_inserted(sp);
+ break;
+ case suspend:
+ /* ignored */
+ break;
+ }
+ sp->state = state.state;
+ stat_changed(sp);
+}
+
+/*
+ * card_removed - card has been removed from slot.
+ * Execute the remove commands, and clear the slot's state.
+ * Execute the device commands, then the driver commands
+ * and then the card commands. This is the reverse
+ * order to the insertion commands
+ */
+void
+card_removed(struct slot *sp)
+{
+ struct card *cp;
+ struct allocblk *sio;
+ int in_use = 0;
+
+ if (sp->config && sp->config->driver && sp->card)
+ logmsg("%s%d: %s removed.", sp->config->driver->kernel,
+ sp->config->driver->unit, sp->card->logstr);
+ if (sp->cis)
+ freecis(sp->cis);
+ if (sp->config) {
+ if (sp->config->inuse && sp->config->driver->inuse)
+ in_use = 1;
+ sp->config->inuse = 0;
+ sp->config->driver->inuse = 0;
+ }
+ if ((cp = sp->card) != 0 && in_use)
+ execute(cp->remove, sp);
+ sp->cis = 0;
+ sp->config = 0;
+ /* release io */
+ if (sp->flags & IO_ASSIGNED)
+ for (sio = &sp->io; sio; sio = sio->next)
+ if (sio->addr && sio->size)
+ bit_nset(io_avail, sio->addr, sio->addr + sio->size - 1);
+ /* release irq */
+ if (sp->flags & IRQ_ASSIGNED)
+ if (sp->irq >= 1 && sp->irq <= 15)
+ pool_irq[sp->irq] = 1;
+}
+
+/* CIS string comparison */
+
+#define REGCOMP_FLAGS (REG_EXTENDED | REG_NOSUB)
+#define REGEXEC_FLAGS (0)
+
+static int
+cis_strcmp(char *db, char *cis)
+{
+ int res, err;
+ char buf[256];
+ regex_t rx;
+ char * p;
+ size_t n;
+
+ if (!db || !cis) {
+ return -1;
+ }
+ n = strlen(db);
+ if (n > 2 && db[0] == '/' && db[n-1] == '/') {
+ /* matching by regex */
+ db++;
+ } else {
+ /* otherwise, matching by strncmp() */
+ if (n != strlen(cis)) {
+ return -1;
+ }
+
+ return strncmp(db, cis, n);
+ }
+ p = xmalloc(n);
+ strncpy(p + 1, db, n-2);
+ *p = '^';
+ db = p;
+ if ((err = regcomp(&rx, p, REGCOMP_FLAGS))) {
+ regerror(err, &rx, buf, sizeof buf);
+ logmsg("Warning: REGEX error for\"%s\" -- %s\n", p, buf);
+ regfree(&rx);
+ free(p);
+ return -1;
+ }
+ res = regexec(&rx, cis, 0, NULL, REGEXEC_FLAGS);
+ regfree(&rx);
+ free(p);
+ return res;
+}
+
+/*
+ * card_inserted - Card has been inserted;
+ * - Read the CIS
+ * - match the card type.
+ * - Match the driver and allocate a driver instance.
+ * - Allocate I/O ports, memory and IRQ.
+ * - Set up the slot.
+ * - assign the driver (if failed, then terminate).
+ * - Run the card commands.
+ * - Run the driver commands
+ * - Run the device commands
+ */
+void
+card_inserted(struct slot *sp)
+{
+ struct card *cp;
+ int err;
+
+ usleep(pccard_init_sleep);
+ sp->cis = readcis(sp->fd);
+ if (sp->cis == 0) {
+ logmsg("Error reading CIS on %s\n", sp->name);
+ return;
+ }
+#if 0
+ dumpcis(sp->cis);
+#endif
+ for (cp = cards; cp; cp = cp->next) {
+ switch (cp->deftype) {
+ case DT_VERS:
+ if (cis_strcmp(cp->manuf, sp->cis->manuf) == 0 &&
+ cis_strcmp(cp->version, sp->cis->vers) == 0) {
+ if (cp->add_info1 != NULL &&
+ cis_strcmp(cp->add_info1, sp->cis->add_info1) != 0) {
+ break;
+ }
+ if (cp->add_info2 != NULL &&
+ cis_strcmp(cp->add_info2, sp->cis->add_info2) != 0) {
+ break;
+ }
+
+ logmsg("Card \"%s\"(\"%s\") "
+ "[%s] [%s] "
+ "matched \"%s\" (\"%s\") "
+ "[%s] [%s] ",
+ sp->cis->manuf, sp->cis->vers,
+ sp->cis->add_info1, sp->cis->add_info2,
+ cp->manuf, cp->version,
+ cp->add_info1, cp->add_info2);
+ goto escape;
+ }
+ break;
+ case DT_FUNC:
+ if (cp->func_id == sp->cis->func_id1) {
+ logmsg("Card \"%s\"(\"%s\") "
+ "[%s] [%s] "
+ "has function ID %d\n",
+ sp->cis->manuf, sp->cis->vers,
+ sp->cis->add_info1, sp->cis->add_info2,
+ cp->func_id);
+ goto escape;
+ }
+ break;
+ default:
+ logmsg("Unknown deftype %d\n", cp->deftype);
+ die("cardd.c:card_inserted()");
+ }
+ }
+escape:
+ sp->card = cp;
+#if 0
+ reset_slot(sp);
+#endif
+ if (cp == 0) {
+ logmsg("No card in database for \"%s\"(\"%s\")",
+ sp->cis->manuf, sp->cis->vers);
+ return;
+ }
+ /*
+ * Copy CIS_MANUF_ID and CIS_FUNC_EXT from "struct cis"
+ * to "struct slot"
+ */
+ if (sp->cis->lan_nid && sp->cis->lan_nid[0] == sizeof(sp->eaddr)) {
+ bcopy(sp->cis->lan_nid + 1, sp->eaddr, sizeof(sp->eaddr));
+ sp->flags |= EADDR_CONFIGED;
+ } else {
+ bzero(sp->eaddr, sizeof(sp->eaddr));
+ }
+ if (sp->cis->manufacturer && sp->cis->product) {
+ sp->manufacturer=sp->cis->manufacturer;
+ sp->product=sp->cis->product;
+ if(sp->cis->prodext) {
+ sp->prodext=sp->cis->prodext; /* For xe driver */
+ }
+ } else {
+ sp->manufacturer=0;
+ sp->product=0;
+ sp->prodext=0;
+ }
+
+ if (cp->ether) {
+ struct ether *e = 0;
+ e = cp->ether;
+ switch (e->type) {
+ case ETHTYPE_ATTR2:
+ read_ether_attr2(sp);
+ break;
+ default:
+ read_ether(sp);
+ break;
+ }
+ }
+ if ((sp->config = assign_driver(sp, cp)) == NULL)
+ return;
+ if ((err = assign_io(sp))) {
+ char *reason;
+
+ switch (err) {
+ case -1:
+ reason = "specified CIS was not found";
+ break;
+ case -2:
+ reason = "memory block allocation failed";
+ break;
+ case -3:
+ reason = "I/O block allocation failed";
+ break;
+ case -4:
+ reason = "requires more than one memory window";
+ break;
+ default:
+ reason = "Unknown";
+ break;
+ }
+ logmsg("Resource allocation failure for \"%s\"(\"%s\") "
+ "[%s] [%s]; Reason %s\n",
+ sp->cis->manuf, sp->cis->vers,
+ sp->cis->add_info1, sp->cis->add_info2, reason);
+ return;
+ }
+
+ /*
+ *
+ * Once assigned, set up the I/O & mem contexts, set up the
+ * windows, and then attach the driver.
+ */
+ if (setup_slot(sp))
+ execute(cp->insert, sp);
+#if 0
+ else
+ reset_slot(sp);
+#endif
+}
+
+/*
+ * read_ether - read ethernet address from card. Offset is
+ * the offset into the attribute memory of the card.
+ */
+static void
+read_ether(struct slot *sp)
+{
+ unsigned char net_addr[12];
+ int flags = MDF_ATTR; /* attribute memory */
+
+ ioctl(sp->fd, PIOCRWFLAG, &flags);
+ lseek(sp->fd, (off_t)sp->card->ether->value, SEEK_SET);
+ if (read(sp->fd, net_addr, sizeof(net_addr)) != sizeof(net_addr)) {
+ logerr("read err on net addr");
+ return;
+ }
+ sp->eaddr[0] = net_addr[0];
+ sp->eaddr[1] = net_addr[2];
+ sp->eaddr[2] = net_addr[4];
+ sp->eaddr[3] = net_addr[6];
+ sp->eaddr[4] = net_addr[8];
+ sp->eaddr[5] = net_addr[10];
+ logmsg("Ether=%02x:%02x:%02x:%02x:%02x:%02x\n",
+ sp->eaddr[0], sp->eaddr[1], sp->eaddr[2],
+ sp->eaddr[3], sp->eaddr[4], sp->eaddr[5]);
+ sp->flags |= EADDR_CONFIGED;
+}
+
+/*
+ * Megahertz X-Jack Ethernet uses unique way to get/set MAC
+ * address of the card.
+ */
+static void
+read_ether_attr2(struct slot *sp)
+{
+ int i;
+ char *hexaddr;
+
+ hexaddr = sp->cis->add_info2;
+ for (i = 0; i < 6; i++)
+ sp->eaddr[i] = 0;
+ if (!hexaddr)
+ return;
+ if (strlen(hexaddr) != 12)
+ return;
+ for (i = 0; i < 12; i++)
+ if (!isxdigit(hexaddr[i]))
+ return;
+ for (i = 0; i < 6; i++) {
+ u_int d;
+ char s[3];
+ s[0] = hexaddr[i * 2];
+ s[1] = hexaddr[i * 2 + 1];
+ s[2] = '\0';
+ if (!sscanf(s, "%x", &d)) {
+ int j;
+ for (j = 0; j < 6; j++)
+ sp->eaddr[j] = 0;
+ return;
+ }
+ sp->eaddr[i] = (u_char)d;
+ }
+ sp->flags |= EADDR_CONFIGED;
+}
+
+
+/*
+ * assign_driver - Assign driver to card.
+ * First, see if an existing driver is already setup.
+ */
+static struct card_config *
+assign_driver(struct slot *sp, struct card *cp)
+{
+ struct driver *drvp;
+ struct card_config *conf;
+ struct pccard_resource res;
+ int i;
+ int irqmin, irqmax;
+
+ for (conf = cp->config; conf; conf = conf->next)
+ if (conf->inuse == 0 && conf->driver->card == cp &&
+ conf->driver->config == conf &&
+ conf->driver->inuse == 0) {
+ if (debug_level > 0) {
+ logmsg("Found existing driver (%s) for %s\n",
+ conf->driver->name, cp->manuf);
+ }
+ conf->driver->inuse = 1;
+ conf->inuse = 1;
+ return (conf);
+ }
+ /*
+ * New driver must be allocated. Find the first configuration
+ * not in use.
+ */
+ for (conf = cp->config; conf; conf = conf->next)
+ if (conf->inuse == 0 && conf->driver->inuse/*card*/ == 0)
+ break;
+ if (conf == 0) {
+ logmsg("No free configuration for card %s", cp->manuf);
+ return (NULL);
+ }
+ /*
+ * Now we have a free driver and a matching configuration.
+ * Before assigning and allocating everything, check to
+ * see if a device class can be allocated to this.
+ */
+ drvp = conf->driver;
+
+ /* If none available, then we can't use this card. */
+ if (drvp->inuse) {
+ logmsg("Driver already being used for %s", cp->manuf);
+ return (NULL);
+ }
+
+ /*
+ * Allocate a free IRQ if none has been specified. When we're
+ * sharing interrupts (cardbus bridge case), then we'll use what
+ * the kernel tells us to use, reguardless of what the user
+ * configured. Asking the kernel for IRQ 0 is our way of asking
+ * if we should use a shared interrupt.
+ */
+ res.type = SYS_RES_IRQ;
+ res.size = 1;
+ if (conf->irq == 0) {
+ irqmin = 1;
+ irqmax = 15;
+ } else {
+ irqmin = irqmax = conf->irq;
+ conf->irq = 0; /* Make sure we get it. */
+ }
+ res.min = 0;
+ res.max = 0;
+ if (ioctl(sp->fd, PIOCSRESOURCE, &res) < 0)
+ err(1, "ioctl (PIOCSRESOURCE)");
+ if (res.resource_addr != ~0ul) {
+ conf->irq = res.resource_addr;
+ pool_irq[conf->irq] = 0;
+ } else {
+ for (i = irqmin; i <= irqmax; i++) {
+ /*
+ * Skip irqs not in the pool.
+ */
+ if (pool_irq[i] == 0)
+ continue;
+ /*
+ * -I forces us to use the interrupt, so use it.
+ */
+ if (!use_kern_irq) {
+ conf->irq = i;
+ pool_irq[i] = 0;
+ break;
+ }
+ /*
+ * Ask the kernel if we have an free irq.
+ */
+ res.min = i;
+ res.max = i;
+ if (ioctl(sp->fd, PIOCSRESOURCE, &res) < 0)
+ err(1, "ioctl (PIOCSRESOURCE)");
+ if (res.resource_addr == ~0ul)
+ continue;
+ /*
+ * res.resource_addr might be the kernel's
+ * better idea than i, so we have to check to
+ * see if that's in use too. If not, mark it
+ * in use and break out of the loop. I'm not
+ * sure this can happen when IRQ 0 above fails,
+ * but the test is cheap enough.
+ */
+ if (pool_irq[res.resource_addr] == 0)
+ continue;
+ conf->irq = res.resource_addr;
+ pool_irq[conf->irq] = 0;
+ break;
+ }
+ }
+ if (conf->irq == 0) {
+ logmsg("Failed to allocate IRQ for %s\n", cp->manuf);
+ return (NULL);
+ }
+ drvp->card = cp;
+ drvp->config = conf;
+ drvp->inuse = 1;
+ conf->inuse = 1;
+ return (conf);
+}
+
+/*
+ * Auto select config index
+ */
+static struct cis_config *
+assign_card_index(struct slot *sp, struct cis * cis)
+{
+ struct cis_config *cp;
+ struct cis_ioblk *cio;
+ struct pccard_resource res;
+ int i;
+
+ res.type = SYS_RES_IOPORT;
+ for (cp = cis->conf; cp; cp = cp->next) {
+ if (!cp->iospace || !cp->io)
+ continue;
+ for (cio = cp->io; cio; cio = cio->next) {
+ res.size = cio->size;
+ res.min = cio->addr;
+ res.max = res.min + cio->size - 1;
+ if (ioctl(sp->fd, PIOCSRESOURCE, &res) < 0)
+ err(1, "ioctl (PIOCSRESOURCE)");
+ if (res.resource_addr != cio->addr)
+ goto next;
+ for (i = cio->addr; i < cio->addr + cio->size - 1; i++)
+ if (!bit_test(io_avail, i))
+ goto next;
+ }
+ return cp; /* found */
+ next:
+ }
+ return cis->def_config;
+}
+
+/*
+ * assign_io - Allocate resources to slot matching the
+ * configuration index selected.
+ */
+static int
+assign_io(struct slot *sp)
+{
+ struct cis *cis;
+ struct cis_config *cisconf, *defconf;
+
+ cis = sp->cis;
+ defconf = cis->def_config;
+ switch (sp->config->index_type) {
+ case DEFAULT_INDEX: /* default */
+ cisconf = defconf;
+ sp->config->index = cisconf->id;
+ break;
+ case AUTO_INDEX: /* auto */
+ cisconf = assign_card_index(sp, cis);
+ if (cisconf)
+ sp->config->index = cisconf->id;
+ break;
+ default: /* normal, use index value */
+ for (cisconf = cis->conf; cisconf; cisconf = cisconf->next)
+ if (cisconf->id == sp->config->index)
+ break;
+ }
+
+ if (cisconf == 0) {
+ logmsg("Config id %d not present in this card",
+ sp->config->index);
+ return (-1);
+ }
+ sp->card_config = cisconf;
+
+ /*
+ * Found a matching configuration. Now look at the I/O, memory and IRQ
+ * to create the desired parameters. Look at memory first.
+ */
+
+ /* Skip ed cards in PIO mode */
+ if ((strncmp(sp->config->driver->name, "ed", 2) == 0) &&
+ (sp->config->flags & 0x10))
+ goto memskip;
+
+ if (cisconf->memspace || (defconf && defconf->memspace)) {
+ struct cis_memblk *mp;
+
+ mp = cisconf->mem;
+ /*
+ * Currently we do not handle the presence of multiple windows.
+ * Then again neither does the interface to the kernel!
+ * See setup_slot() and readcis.c:cis_conf()
+ */
+ if (cisconf->memwins > 1) {
+ logmsg("Card requires %d memory windows.",
+ cisconf->memwins);
+ return (-4);
+ }
+
+ if (!cisconf->memspace)
+ mp = defconf->mem;
+ sp->mem.size = mp->length;
+ sp->mem.cardaddr = mp->address;
+
+ /* For now, we allocate our own memory from the pool. */
+ sp->mem.addr = sp->config->driver->mem;
+ /*
+ * Host memory address is required. Allocate one
+ * from our pool.
+ */
+ if (sp->mem.size && sp->mem.addr == 0) {
+ sp->mem.addr = alloc_memory(mp->length);
+ if (sp->mem.addr == 0)
+ return (-2);
+ sp->config->driver->mem = sp->mem.addr;
+ }
+ /* Driver specific set up */
+ if (strncmp(sp->config->driver->name, "ed", 2) == 0) {
+ sp->mem.cardaddr = 0x4000;
+ sp->mem.flags = MDF_ACTIVE | MDF_16BITS;
+ } else {
+ sp->mem.flags = MDF_ACTIVE | MDF_16BITS;
+ }
+
+ if (sp->mem.flags & MDF_ACTIVE)
+ sp->flags |= MEM_ASSIGNED;
+ if (debug_level > 0) {
+ logmsg("Using mem addr 0x%x, size %d, card addr 0x%x, flags 0x%x\n",
+ sp->mem.addr, sp->mem.size, sp->mem.cardaddr,
+ sp->mem.flags);
+ }
+ }
+memskip:
+
+ /* Now look at I/O. */
+ bzero(&sp->io, sizeof(sp->io));
+ if (cisconf->iospace || (defconf && defconf->iospace)
+ || sp->card->iosize) {
+ struct cis_config *cp;
+ struct cis_ioblk *cio;
+ struct allocblk *sio;
+ int x, xmax;
+ int iosize;
+
+ cp = cisconf;
+ if (!cisconf->iospace)
+ cp = defconf;
+ iosize = sp->card->iosize;
+
+ /* iosize auto */
+ if (iosize < 0) {
+ if (cp->io)
+ iosize = cp->io->size;
+ else
+ iosize = 1 << cp->io_addr;
+ }
+
+ /*
+ * If # of I/O lines decoded == 10, then card does its
+ * own decoding.
+ *
+ * If an I/O block exists, then use it.
+ * If no address (but a length) is available, allocate
+ * from the pool.
+ */
+ cio = cp->io;
+ sio = &(sp->io);
+ xmax = 1;
+ if (iosize == 0 && cio)
+ xmax = cisconf->io_blks;
+ for (x = 0; x < xmax; x++) {
+ if (iosize) {
+ sio->addr = 0;
+ sio->size = iosize;
+ } else if (cio) {
+ sio->addr = cio->addr;
+ sio->size = cio->size;
+ } else {
+ /*
+ * No I/O block, assume the address lines
+ * decode gives the size.
+ */
+ sio->size = 1 << cp->io_addr;
+ }
+ if (sio->addr == 0) {
+ struct pccard_resource res;
+ int i, j;
+
+ res.type = SYS_RES_IOPORT;
+ res.size = sio->size;
+
+ for (i = 0; i < IOPORTS; i++) {
+ j = bit_fns(io_avail, IOPORTS, i,
+ sio->size, sio->size);
+ if ((j & (sio->size - 1)) != 0)
+ continue;
+ res.min = j;
+ res.max = j + sio->size - 1;
+ if (ioctl(sp->fd, PIOCSRESOURCE, &res) < 0)
+ err(1, "ioctl (PIOCSRESOURCE)");
+ if (res.resource_addr == j)
+ break;
+ }
+ if (j < 0) {
+ return (-3);
+ } else {
+ sio->addr = j;
+ }
+ }
+ bit_nclear(io_avail, sio->addr,
+ sio->addr + sio->size - 1);
+ sp->flags |= IO_ASSIGNED;
+
+ /* Set up the size to take into account the decode lines. */
+ sio->cardaddr = cp->io_addr;
+ switch (cp->io_bus) {
+ case 0:
+ break;
+ case 1:
+ sio->flags = IODF_WS;
+ break;
+ case 2:
+ sio->flags = IODF_WS | IODF_CS16;
+ break;
+ case 3:
+ sio->flags = IODF_WS | IODF_CS16 | IODF_16BIT;
+ break;
+ }
+ if (debug_level > 0) {
+ logmsg("Using I/O addr 0x%x, size %d\n",
+ sio->addr, sio->size);
+ }
+ if (cio && cio->next) {
+ sio->next = xmalloc(sizeof(*sio));
+ sio = sio->next;
+ cio = cio->next;
+ }
+ }
+ }
+ sp->irq = sp->config->irq;
+ sp->flags |= IRQ_ASSIGNED;
+ return (0);
+}
+
+/*
+ * setup_slot - Allocate the I/O and memory contexts
+ * return true if completed OK.
+ */
+static int
+setup_slot(struct slot *sp)
+{
+ struct mem_desc mem;
+ struct io_desc io;
+ struct dev_desc drv;
+ struct driver *drvp = sp->config->driver;
+ struct allocblk *sio;
+ char *p;
+ char c;
+ off_t offs;
+ int rw_flags;
+ int iowin;
+
+ memset(&io, 0, sizeof io);
+ memset(&drv, 0, sizeof drv);
+ offs = sp->cis->reg_addr;
+ rw_flags = MDF_ATTR;
+ ioctl(sp->fd, PIOCRWFLAG, &rw_flags);
+#if RESET_MAY_BE_HARMFUL
+ lseek(sp->fd, offs, SEEK_SET);
+ c = 0x80;
+ write(sp->fd, &c, sizeof(c));
+ usleep(sp->card->reset_time * 1000);
+ lseek(sp->fd, offs, SEEK_SET);
+ c = 0x00;
+ write(sp->fd, &c, sizeof(c));
+ usleep(sp->card->reset_time * 1000);
+#endif
+ lseek(sp->fd, offs, SEEK_SET);
+ c = sp->config->index;
+ c |= 0x40;
+ write(sp->fd, &c, sizeof(c));
+ if (debug_level > 0) {
+ logmsg("Setting config reg at offs 0x%lx to 0x%x, "
+ "Reset time = %d ms\n", (unsigned long)offs, c,
+ sp->card->reset_time);
+ }
+ usleep(pccard_init_sleep);
+ usleep(sp->card->reset_time * 1000);
+
+ /* If other config registers exist, set them up. */
+ if (sp->cis->ccrs & 2) {
+ /* CCSR */
+ c = 0;
+ if (sp->cis->def_config && sp->cis->def_config->misc_valid &&
+ (sp->cis->def_config->misc & 0x8))
+ c |= 0x08;
+ if (sp->card_config->io_bus == 1)
+ c |= 0x20;
+ lseek(sp->fd, offs + 2, SEEK_SET);
+ write(sp->fd, &c, sizeof(c));
+ }
+ if (sp->flags & MEM_ASSIGNED) {
+ mem.window = 0;
+ mem.flags = sp->mem.flags;
+ mem.start = (caddr_t) sp->mem.addr;
+ mem.card = sp->mem.cardaddr;
+ mem.size = sp->mem.size;
+ if (ioctl(sp->fd, PIOCSMEM, &mem)) {
+ logerr("ioctl (PIOCSMEM)");
+ return (0);
+ }
+ }
+ if (sp->flags & IO_ASSIGNED) {
+ for (iowin = 0, sio = &(sp->io); iowin <= 1; iowin++) {
+ io.window = iowin;
+ if (sio->size) {
+ io.flags = sio->flags;
+ io.start = sio->addr;
+ io.size = sio->size;
+ }
+#if 0
+ io.start = sp->io.addr & ~((1 << sp->io.cardaddr) - 1);
+ io.size = 1 << sp->io.cardaddr;
+ if (io.start < 0x100) {
+ io.start = 0x100;
+ io.size = 0x300;
+ }
+#endif
+ if (debug_level > 0) {
+ logmsg("Assigning I/O window %d, start 0x%x, "
+ "size 0x%x flags 0x%x\n", io.window, io.start,
+ io.size, io.flags);
+ }
+ io.flags |= IODF_ACTIVE;
+ if (ioctl(sp->fd, PIOCSIO, &io)) {
+ logerr("ioctl (PIOCSIO)");
+ return (0);
+ }
+ if (ioctl(sp->fd, PIOCGIO, &io))
+ {
+ logerr("ioctl (PIOCGIO)");
+ return(0);
+ }
+ if (io.start != sio->addr){
+ logmsg("I/O base address changed from 0x%x to 0x%x\n",
+ sio->addr, io.start);
+ sio->addr = io.start;
+ }
+ if (sio->next)
+ sio = sio->next;
+ else
+ break;
+ }
+ }
+ strcpy(drv.name, drvp->kernel);
+ drv.unit = drvp->unit;
+ drv.irqmask = 1 << sp->irq;
+ drv.flags = sp->config->flags;
+ if (sp->flags & MEM_ASSIGNED) {
+ drv.mem = sp->mem.addr;
+ drv.memsize = sp->mem.size;
+ } else {
+ drv.mem = 0;
+ drv.memsize = 0;
+ }
+ if (sp->flags & IO_ASSIGNED)
+ drv.iobase = sp->io.addr;
+ else
+ drv.iobase = 0;
+#ifdef DEV_DESC_HAS_SIZE
+ drv.iosize = sp->io.size;
+#endif
+ if (debug_level > 0) {
+ logmsg("Assign %s%d, io 0x%x-0x%x, mem 0x%lx, %d bytes, "
+ "irq %d, flags %x\n", drv.name, drv.unit, drv.iobase,
+ drv.iobase + sp->io.size - 1, drv.mem, drv.memsize,
+ sp->irq, drv.flags);
+ }
+ /*
+ * Copy CIS_MANUF_ID from "struct slot" to "struct dev_desc"
+ * This means moving CIS_MANUF_ID to kernel driver area.
+ */
+ drv.manufacturer = sp->manufacturer;
+ drv.product = sp->product;
+ drv.prodext = sp->prodext;
+ /*
+ * If the driver fails to be connected to the device,
+ * then it may mean that the driver did not recognise it.
+ */
+ memcpy(drv.misc, sp->eaddr, 6);
+ if (ioctl(sp->fd, PIOCSDRV, &drv)) {
+ logmsg("driver allocation failed for %s(%s): %s",
+ sp->card->manuf, sp->card->version, strerror(errno));
+ return (0);
+ }
+ drv.name[sizeof(drv.name) - 1] = '\0';
+ if (strncmp(drv.name, drvp->kernel, sizeof(drv.name))) {
+ drvp->kernel = newstr(drv.name);
+ p = drvp->kernel;
+ while (*p++)
+ if (*p >= '0' && *p <= '9') {
+ drvp->unit = atoi(p);
+ *p = '\0';
+ break;
+ }
+ }
+ logmsg("%s%d: %s inserted.", sp->config->driver->kernel,
+ sp->config->driver->unit, sp->card->logstr);
+ return (1);
+}
diff --git a/usr.sbin/pccard/pccardd/cardd.h b/usr.sbin/pccard/pccardd/cardd.h
new file mode 100644
index 0000000..afd8732
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/cardd.h
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 1995 Andrew McRae. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ * Common include file for PCMCIA daemon
+ */
+#include <bitstring.h>
+
+#include <pccard/cardinfo.h>
+#include <pccard/cis.h>
+
+#include "readcis.h"
+
+#ifndef EXTERN
+#define EXTERN extern
+#endif
+
+struct cmd {
+ struct cmd *next;
+ char *line; /* Command line */
+ int macro; /* Contains macros */
+};
+
+struct card_config {
+ struct card_config *next;
+ unsigned char index_type;
+ unsigned char index;
+ struct driver *driver;
+ int irq;
+ int flags;
+ char inuse;
+};
+
+struct ether {
+ struct ether *next;
+ int type;
+ int value;
+};
+
+#define ETHTYPE_GENERIC 0
+#define ETHTYPE_ATTR2 1
+
+struct card {
+ struct card *next;
+ char *manuf;
+ char *version;
+ char *add_info1;
+ char *add_info2;
+ u_char func_id;
+ int deftype;
+ struct ether *ether; /* For net cards, ether at offset */
+ int reset_time; /* Reset time */
+ int iosize; /* I/O window size (ignore location) */
+ struct card_config *config; /* List of configs */
+ struct cmd *insert; /* Insert commands */
+ struct cmd *remove; /* Remove commands */
+ char *logstr; /* String for logger */
+};
+
+struct driver {
+ struct driver *next;
+ char *name;
+ char *kernel; /* Kernel driver base name */
+ int unit; /* Unit of driver */
+ /*
+ * The rest of the structure is allocated dynamically.
+ * Once allocated, it stays allocated.
+ */
+ struct card *card; /* Current card, if any */
+ struct card_config *config; /* Config back ptr */
+ unsigned int mem; /* Allocated host address (if any) */
+ int inuse;
+};
+
+/*
+ * Defines one allocation block i.e a starting address
+ * and size. Used for either memory or I/O ports
+ */
+struct allocblk {
+ struct allocblk *next;
+ int addr; /* Address */
+ int size; /* Size */
+ int flags; /* Flags for block */
+ int cardaddr; /* Card address */
+};
+/*
+ * Slot structure - data held for each slot.
+ */
+struct slot {
+ struct slot *next;
+ int fd;
+ int mask;
+ int slot;
+ char *name;
+ enum cardstate state;
+ struct cis *cis;
+ struct card *card; /* Current card */
+ struct card_config *config; /* Current configuration */
+ struct cis_config *card_config;
+ char devname[16];
+ u_int manufacturer;
+ u_int product;
+ u_int prodext;
+ unsigned char eaddr[6]; /* If any */
+ struct allocblk io; /* I/O block spec */
+ struct allocblk mem; /* Memory block spec */
+ int irq; /* Irq value */
+ int flags; /* Resource assignment flags */
+};
+
+/*
+ * Slot resource assignment/configuration flags
+ */
+#define IO_ASSIGNED 0x1
+#define MEM_ASSIGNED 0x2
+#define IRQ_ASSIGNED 0x4
+#define EADDR_CONFIGED 0x8
+#define WL_CONFIGED 0x10
+#define AFLAGS (IO_ASSIGNED | MEM_ASSIGNED | IRQ_ASSIGNED)
+#define CFLAGS (EADDR_CONFIGED | WL_CONFIGED)
+
+EXTERN struct slot *slots, *current_slot;
+EXTERN int slen;
+
+EXTERN struct allocblk *pool_ioblks; /* I/O blocks in the pool */
+EXTERN struct allocblk *pool_mem; /* Memory in the pool */
+EXTERN int pool_irq[16]; /* IRQ allocations */
+EXTERN int irq_init[16]; /* initial IRQ allocations */
+EXTERN struct driver *drivers; /* List of drivers */
+EXTERN struct card *cards;
+EXTERN struct card *last_card;
+EXTERN bitstr_t *mem_avail;
+EXTERN bitstr_t *mem_init;
+EXTERN bitstr_t *io_avail;
+EXTERN bitstr_t *io_init;
+EXTERN int pccard_init_sleep; /* Time to sleep on init */
+EXTERN int use_kern_irq;
+EXTERN int debug_level;
+
+/* cardd.c functions */
+void dump_config_file(void);
+struct slot *readslots(void);
+void slot_change(struct slot *);
+
+/* util.c functions */
+unsigned long alloc_memory(int);
+int bit_fns(bitstr_t *, int, int, int, int);
+void die(char *);
+void execute(struct cmd *, struct slot *);
+void logmsg(const char *, ...);
+void log_setup(void);
+void logerr(char *);
+char *newstr();
+void reset_slot(struct slot *);
+void *xmalloc(int);
+
+/* file.c functions */
+void readfile(char *);
+
+/* server.c functions */
+void set_socket(int);
+void stat_changed(struct slot *);
+void process_client(void);
+
+#define IOPORTS 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)
+
+#define MAXINCLUDES 10
+#define MAXERRORS 10
+
+/*
+ * Config index types
+ */
+#define NORMAL_INDEX 0
+#define DEFAULT_INDEX 1
+#define AUTO_INDEX 2
+
+#define DT_VERS 0
+#define DT_FUNC 1
diff --git a/usr.sbin/pccard/pccardd/file.c b/usr.sbin/pccard/pccardd/file.c
new file mode 100644
index 0000000..4643812
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/file.c
@@ -0,0 +1,1093 @@
+/*
+ * Copyright (c) 1995 Andrew McRae. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include "cardd.h"
+
+static FILE *in;
+static int includes = 0;
+static struct {
+ FILE *filep;
+ char *filename;
+ int lineno;
+} configfiles[MAXINCLUDES] = {{NULL, NULL, 0}, };
+
+static int pushc, pusht;
+static int lineno;
+static char *filename;
+
+static char *keys[] = {
+ "__EOF__", /* 1 */
+ "io", /* 2 */
+ "irq", /* 3 */
+ "memory", /* 4 */
+ "card", /* 5 */
+ "device", /* 6 */
+ "config", /* 7 */
+ "reset", /* 8 */
+ "ether", /* 9 */
+ "insert", /* 10 */
+ "remove", /* 11 */
+ "iosize", /* 12 */
+ "debuglevel", /* 13 */
+ "include", /* 14 */
+ "function", /* 15 */
+ "logstr", /* 16 */
+ 0
+};
+
+#define KWD_EOF 1
+#define KWD_IO 2
+#define KWD_IRQ 3
+#define KWD_MEMORY 4
+#define KWD_CARD 5
+#define KWD_DEVICE 6
+#define KWD_CONFIG 7
+#define KWD_RESET 8
+#define KWD_ETHER 9
+#define KWD_INSERT 10
+#define KWD_REMOVE 11
+#define KWD_IOSIZE 12
+#define KWD_DEBUGLEVEL 13
+#define KWD_INCLUDE 14
+#define KWD_FUNCTION 15
+#define KWD_LOGSTR 16
+
+/* for keyword compatibility with PAO/plain FreeBSD */
+static struct {
+ char *alias;
+ u_int key;
+} key_aliases[] = {
+ {"generic", KWD_FUNCTION},
+ {0, 0}
+};
+
+struct flags {
+ char *name;
+ int mask;
+};
+
+extern int doverbose;
+
+static void parsefile(void);
+static char *getline(void);
+static char *next_tok(void);
+static int num_tok(void);
+static void error(char *);
+static int keyword(char *);
+static int irq_tok(int);
+static int config_tok(unsigned char *);
+static int func_tok(void);
+static int debuglevel_tok(int);
+static struct allocblk *ioblk_tok(int);
+static struct allocblk *memblk_tok(int);
+static struct driver *new_driver(char *);
+static int iosize_tok(void);
+static void file_include(char *);
+
+static void addcmd(struct cmd **);
+static void parse_card(int);
+
+static void
+delete_card(struct card *cp)
+{
+ struct ether *etherp, *ether_next;
+ struct card_config *configp, *config_next;
+ struct cmd *cmdp, *cmd_next;
+
+ /* free strings */
+ free(cp->manuf);
+ free(cp->version);
+ free(cp->add_info1);
+ free(cp->add_info2);
+ free(cp->logstr);
+
+ /* free structures */
+ for (etherp = cp->ether; etherp; etherp = ether_next) {
+ ether_next = etherp->next;
+ free(etherp);
+ }
+ for (configp = cp->config; configp; configp = config_next) {
+ config_next = configp->next;
+ free(configp);
+ }
+ for (cmdp = cp->insert; cmdp; cmdp = cmd_next) {
+ cmd_next = cmdp->next;
+ free(cmdp->line);
+ free(cmdp);
+ }
+ for (cmdp = cp->remove; cmdp; cmdp = cmd_next) {
+ cmd_next = cmdp->next;
+ free(cmdp->line);
+ free(cmdp);
+ }
+ free(cp);
+}
+
+/*
+ * Read a file and parse the pcmcia configuration data.
+ * After parsing, verify the links.
+ */
+void
+readfile(char *name)
+{
+ int i, inuse;
+ struct card *cp, *card_next;
+ struct card *genericp, *tail_gp;
+ struct card_config *configp;
+
+ /* delete all card configuration data before we proceed */
+ genericp = 0;
+ cp = cards;
+ cards = last_card = 0;
+ while (cp) {
+ card_next = cp->next;
+
+ /* check whether this card is in use */
+ inuse = 0;
+ for (configp = cp->config; configp; configp = configp->next) {
+ if (configp->inuse) {
+ inuse = 1;
+ break;
+ }
+ }
+
+ /*
+ * don't delete entry in use for consistency.
+ * leave normal entry in the cards list,
+ * insert generic entry into the list after re-loading config files.
+ */
+ if (inuse == 1) {
+ cp->next = 0; /* unchain from the cards list */
+ switch (cp->deftype) {
+ case DT_VERS:
+ /* don't delete this entry for consistency */
+ if (debug_level >= 1) {
+ logmsg("Card \"%s\"(\"%s\") is in use, "
+ "can't change configuration\n",
+ cp->manuf, cp->version);
+ }
+ /* add this to the card list */
+ if (!last_card) {
+ cards = last_card = cp;
+ } else {
+ last_card->next = cp;
+ last_card = cp;
+ }
+ break;
+
+ case DT_FUNC:
+ /* generic entry must be inserted to the list later */
+ if (debug_level >= 1) {
+ logmsg("Generic entry is in use, "
+ "can't change configuration\n");
+ }
+ cp->next = genericp;
+ genericp = cp;
+ break;
+ }
+ } else {
+ delete_card(cp);
+ }
+
+ cp = card_next;
+ }
+
+ for (i = 0; i < MAXINCLUDES; i++) {
+ if (configfiles[i].filep) {
+ fclose(configfiles[i].filep);
+ configfiles[i].filep = NULL;
+ if (i > 0) {
+ free(configfiles[i].filename);
+ }
+ }
+ }
+ in = fopen(name, "r");
+ if (in == 0) {
+ logerr(name);
+ die("readfile");
+ }
+ includes = 0;
+ configfiles[includes].filep = in;
+ filename = configfiles[includes].filename = name;
+
+ parsefile();
+ for (cp = cards; cp; cp = cp->next) {
+ if (cp->config == 0)
+ logmsg("warning: card %s(%s) has no valid configuration\n",
+ cp->manuf, cp->version);
+ }
+
+ /* insert generic entries in use into the top of generic entries */
+ if (genericp) {
+ /* search tail of generic entries in use */
+ for (tail_gp = genericp; tail_gp->next; tail_gp = tail_gp->next)
+ ;
+
+ /*
+ * if the top of cards list is generic entry,
+ * insert generic entries in use before it.
+ */
+ if (cards && cards->deftype == DT_FUNC) {
+ tail_gp->next = cards;
+ cards = genericp;
+ goto generic_done;
+ }
+
+ /* search top of generic entries */
+ for (cp = cards; cp; cp = cp->next) {
+ if (cp->next && cp->next->deftype == DT_FUNC) {
+ break;
+ }
+ }
+
+ /*
+ * if we have generic entry in the cards list,
+ * insert generic entries in use into there.
+ */
+ if (cp) {
+ tail_gp->next = cp->next;
+ cp->next = genericp;
+ goto generic_done;
+ }
+
+ /*
+ * otherwise we don't have generic entries in
+ * cards list, just add them to the list.
+ */
+ if (!last_card) {
+ cards = genericp;
+ } else {
+ last_card->next = genericp;
+ last_card = tail_gp;
+ }
+generic_done:
+ }
+
+ /* save the initial state of resource pool */
+ bcopy(io_avail, io_init, bitstr_size(IOPORTS));
+ bcopy(mem_avail, mem_init, bitstr_size(MEMBLKS));
+ bcopy(pool_irq, irq_init, sizeof(pool_irq));
+}
+
+static void
+parsefile(void)
+{
+ int i;
+ int errors = 0;
+ struct allocblk *bp, *next;
+ char *incl;
+
+ pushc = 0;
+ lineno = 1;
+ for (;;)
+ switch (keyword(next_tok())) {
+ case KWD_EOF:
+ /* EOF */
+ return;
+ case KWD_IO:
+ /* override reserved I/O blocks */
+ bit_nclear(io_avail, 0, IOPORTS-1);
+ for (bp = pool_ioblks; bp; bp = next) {
+ next = bp->next;
+ free(bp);
+ }
+ pool_ioblks = NULL;
+
+ while ((bp = ioblk_tok(0)) != 0) {
+ if (bp->size == 0 || bp->addr == 0) {
+ free(bp);
+ continue;
+ }
+ bit_nset(io_avail, bp->addr,
+ bp->addr + bp->size - 1);
+ bp->next = pool_ioblks;
+ pool_ioblks = bp;
+ }
+ pusht = 1;
+ break;
+ case KWD_IRQ:
+ /* override reserved irqs */
+ bzero(pool_irq, sizeof(pool_irq));
+ while ((i = irq_tok(0)) > 0)
+ pool_irq[i] = 1;
+ pusht = 1;
+ break;
+ case KWD_MEMORY:
+ /* override reserved memory blocks. */
+ bit_nclear(mem_avail, 0, MEMBLKS-1);
+ for (bp = pool_mem; bp; bp = next) {
+ next = bp->next;
+ free(bp);
+ }
+ pool_mem = NULL;
+
+ while ((bp = memblk_tok(0)) != 0) {
+ if (bp->size == 0 || bp->addr == 0) {
+ free(bp);
+ continue;
+ }
+ bit_nset(mem_avail, MEM2BIT(bp->addr),
+ MEM2BIT(bp->addr + bp->size) - 1);
+ bp->next = pool_mem;
+ pool_mem = bp;
+ }
+ pusht = 1;
+ break;
+ case KWD_CARD:
+ /* Card definition. */
+ parse_card(DT_VERS);
+ break;
+ case KWD_FUNCTION:
+ /* Function definition. */
+ parse_card(DT_FUNC);
+ break;
+ case KWD_DEBUGLEVEL:
+ i = debuglevel_tok(0);
+ if (i > 0)
+ debug_level = i;
+ break;
+ case KWD_INCLUDE:
+ incl = newstr(next_tok());
+ file_include(incl);
+ break;
+ default:
+ error("syntax error");
+ pusht = 0;
+ if (errors++ >= MAXERRORS) {
+ error("too many errors, giving up");
+ return;
+ }
+ break;
+ }
+}
+
+/*
+ * Parse a card definition.
+ */
+static void
+parse_card(int deftype)
+{
+ char *man, *vers, *tmp;
+ char *add_info;
+ unsigned char index_type;
+ struct card *cp;
+ int i, iosize;
+ struct card_config *confp, *lastp;
+ struct ether *ether;
+
+ confp = 0;
+ cp = xmalloc(sizeof(*cp));
+ cp->deftype = deftype;
+ switch (deftype) {
+ case DT_VERS:
+ man = newstr(next_tok());
+ vers = newstr(next_tok());
+ add_info = newstr(next_tok());
+ if (keyword(add_info)) {
+ pusht = 1;
+ free(add_info);
+ cp->add_info1 = NULL;
+ cp->add_info2 = NULL;
+ } else {
+ cp->add_info1 = add_info;
+ add_info = newstr(next_tok());
+ if (keyword(add_info)) {
+ pusht = 1;
+ free(add_info);
+ cp->add_info2 = NULL;
+ } else {
+ cp->add_info2 = add_info;
+ }
+ }
+ cp->manuf = man;
+ cp->version = vers;
+ cp->logstr = NULL;
+ asprintf(&cp->logstr, "%s (%s)", man, vers);
+ cp->func_id = 0;
+ break;
+ case DT_FUNC:
+ cp->manuf = NULL;
+ cp->version = NULL;
+ cp->logstr = NULL;
+ cp->func_id = (u_char) func_tok();
+ break;
+ default:
+ fprintf(stderr, "parse_card: unknown deftype %d\n", deftype);
+ exit(1);
+ }
+ cp->reset_time = 50;
+ cp->next = 0;
+ if (!last_card) {
+ cards = last_card = cp;
+ } else {
+ last_card->next = cp;
+ last_card = cp;
+ }
+ for (;;) {
+ switch (keyword(next_tok())) {
+ case KWD_CONFIG:
+ /* config */
+ i = config_tok(&index_type);
+ if (i == -1) {
+ error("illegal card config index");
+ break;
+ }
+ confp = xmalloc(sizeof(*confp));
+ man = next_tok();
+ confp->driver = new_driver(man);
+ confp->irq = irq_tok(1);
+ confp->flags = num_tok();
+ if (confp->flags == -1) {
+ pusht = 1;
+ confp->flags = 0;
+ }
+ if (confp->irq < 0 || confp->irq > 15) {
+ error("illegal card IRQ value");
+ break;
+ }
+ confp->index = i & 0x3F;
+ confp->index_type = index_type;
+
+ /*
+ * If no valid driver for this config, then do not save
+ * this configuration entry.
+ */
+ if (confp->driver) {
+ if (cp->config == 0)
+ cp->config = confp;
+ else {
+ for (lastp = cp->config; lastp->next;
+ lastp = lastp->next);
+ lastp->next = confp;
+ }
+ } else
+ free(confp);
+ break;
+ case KWD_RESET:
+ /* reset */
+ i = num_tok();
+ if (i == -1) {
+ error("illegal card reset time");
+ break;
+ }
+ cp->reset_time = i;
+ break;
+ case KWD_ETHER:
+ /* ether */
+ ether = xmalloc(sizeof(*ether));
+ ether->type = ETHTYPE_GENERIC;
+ tmp = next_tok();
+ if (strcmp("attr2", tmp) == 0)
+ ether->type = ETHTYPE_ATTR2;
+ else {
+ pusht = 1;
+ ether->value = num_tok();
+ if (ether->value == -1) {
+ error("illegal ether address offset");
+ free(ether);
+ break;
+ }
+ }
+ ether->next = cp->ether;
+ cp->ether = ether;
+ break;
+ case KWD_INSERT:
+ /* insert */
+ addcmd(&cp->insert);
+ break;
+ case KWD_REMOVE:
+ /* remove */
+ addcmd(&cp->remove);
+ break;
+ case KWD_IOSIZE:
+ /* iosize */
+ iosize = iosize_tok();
+ if (!iosize) {
+ error("Illegal cardio arguments");
+ break;
+ }
+ if (!confp) {
+ error("iosize should be placed after config");
+ break;
+ }
+ cp->iosize = iosize;
+ break;
+ case KWD_LOGSTR:
+ free(cp->logstr);
+ cp->logstr = newstr(next_tok());
+ break;
+ default:
+ pusht = 1;
+ return;
+ }
+ }
+}
+
+/*
+ * Generate a new driver structure. If one exists, use
+ * that one after confirming the correct class.
+ */
+static struct driver *
+new_driver(char *name)
+{
+ struct driver *drvp;
+ char *p;
+
+ for (drvp = drivers; drvp; drvp = drvp->next)
+ if (strcmp(drvp->name, name) == 0)
+ return (drvp);
+ drvp = xmalloc(sizeof(*drvp));
+ drvp->next = drivers;
+ drivers = drvp;
+ drvp->name = newstr(name);
+ drvp->kernel = newstr(name);
+ drvp->unit = -1;
+ p = drvp->kernel;
+ while (*p++)
+ if (*p >= '0' && *p <= '9') {
+ drvp->unit = atoi(p);
+ *p = 0;
+ break;
+ }
+#ifdef DEBUG
+ printf("Drv %s%d created\n", drvp->kernel, drvp->unit);
+#endif
+ return (drvp);
+}
+
+
+/*
+ * Parse one I/O block.
+ */
+static struct allocblk *
+ioblk_tok(int force)
+{
+ struct allocblk *io;
+ int i, j;
+
+ /* ignore the keyword to allow separete blocks in multiple lines */
+ if (keyword(next_tok()) != KWD_IO) {
+ pusht = 1;
+ }
+
+ if ((i = num_tok()) >= 0) {
+ if (strcmp("-", next_tok()) || (j = num_tok()) < 0 || j < i) {
+ error("I/O block format error");
+ return (0);
+ }
+ io = xmalloc(sizeof(*io));
+ io->addr = i;
+ io->size = j - i + 1;
+ if (j > IOPORTS) {
+ error("I/O port out of range");
+ if (force) {
+ free(io);
+ io = 0;
+ } else
+ io->addr = io->size = 0;
+ }
+ return (io);
+ }
+ if (force)
+ error("illegal or missing I/O block spec");
+ return (0);
+}
+
+/*
+ * Parse a memory block.
+ */
+static struct allocblk *
+memblk_tok(int force)
+{
+ struct allocblk *mem;
+ int i, j;
+
+ /* ignore the keyword to allow separete blocks in multiple lines */
+ if (keyword(next_tok()) != KWD_MEMORY) {
+ pusht = 1;
+ }
+
+ if ((i = num_tok()) >= 0) {
+ if ((j = num_tok()) < 0)
+ error("illegal memory block");
+ else {
+ mem = xmalloc(sizeof(*mem));
+ mem->addr = i & ~(MEMUNIT - 1);
+ mem->size = (j + MEMUNIT - 1) & ~(MEMUNIT - 1);
+ if (i < MEMSTART || (i + j) > MEMEND) {
+ error("memory address out of range");
+ if (force) {
+ free(mem);
+ mem = 0;
+ } else
+ mem->addr = mem->size = 0;
+ }
+ return (mem);
+ }
+ }
+ if (force)
+ error("illegal or missing memory block spec");
+ return (0);
+}
+
+/*
+ * IRQ token. Must be number > 0 && < 16.
+ * If force is set, IRQ must exist, and can also be '?'.
+ */
+static int
+irq_tok(int force)
+{
+ int i;
+
+ /* ignore the keyword to allow separete blocks in multiple lines */
+ if (keyword(next_tok()) != KWD_IRQ) {
+ pusht = 1;
+ }
+
+ if (strcmp("?", next_tok()) == 0 && force)
+ return (0);
+ pusht = 1;
+ i = num_tok();
+ if (i > 0 && i < 16)
+ return (i);
+ if (force)
+ error("illegal IRQ value");
+ return (-1);
+}
+
+/*
+ * Config index token
+ */
+static int
+config_tok(unsigned char *index_type)
+{
+ if (strcmp("default", next_tok()) == 0) {
+ *index_type = DEFAULT_INDEX;
+ return 0;
+ }
+ pusht = 1;
+ if (strcmp("auto", next_tok()) == 0) {
+ *index_type = AUTO_INDEX;
+ return 0;
+ }
+ pusht = 1;
+ *index_type = NORMAL_INDEX;
+ return num_tok();
+}
+/*
+ * Function ID token
+ */
+static int
+func_tok(void)
+{
+ if (strcmp("serial", next_tok()) == 0)
+ return 2;
+ pusht = 1;
+ if (strcmp("fixed_disk", next_tok()) == 0)
+ return 4;
+ pusht = 1;
+ return num_tok();
+}
+
+
+/*
+ * debuglevel token. Must be between 0 and 9.
+ */
+static int
+debuglevel_tok(int force)
+{
+ int i;
+
+ i = num_tok();
+ if (i >= 0 && i <= 9)
+ return (i);
+ return (-1);
+}
+
+/*
+ * iosize token
+ * iosize {<size>|auto}
+ */
+static int
+iosize_tok(void)
+{
+ int iosize = 0;
+ if (strcmp("auto", next_tok()) == 0)
+ iosize = -1; /* wildcard */
+ else {
+ pusht = 1;
+ iosize = num_tok();
+ if (iosize == -1)
+ return 0;
+ }
+#ifdef DEBUG
+ if (doverbose)
+ printf("iosize: size=%x\n", iosize);
+#endif
+ return iosize;
+}
+
+
+/*
+ * search the table for a match.
+ */
+static int
+keyword(char *str)
+{
+ char **s;
+ int i = 1;
+
+ for (s = keys; *s; s++, i++)
+ if (strcmp(*s, str) == 0)
+ return (i);
+
+ /* search keyword aliases too */
+ for (i = 0; key_aliases[i].key ; i++)
+ if (strcmp(key_aliases[i].alias, str) == 0)
+ return (key_aliases[i].key);
+
+ return (0);
+}
+
+/*
+ * addcmd - Append the command line to the list of
+ * commands.
+ */
+static void
+addcmd(struct cmd **cp)
+{
+ struct cmd *ncp;
+ char *s = getline();
+
+ if (*s) {
+ ncp = xmalloc(sizeof(*ncp));
+ ncp->line = s;
+ while (*cp)
+ cp = &(*cp)->next;
+ *cp = ncp;
+ }
+
+}
+
+static void
+error(char *msg)
+{
+ pusht = 1;
+ logmsg("%s: %s at line %d, near %s\n",
+ filename, msg, lineno, next_tok());
+ pusht = 1;
+}
+
+static int last_char;
+
+static int
+get(void)
+{
+ int c;
+
+ if (pushc)
+ c = pushc;
+ else
+ c = getc(in);
+ pushc = 0;
+ while (c == '\\') {
+ c = getc(in);
+ switch (c) {
+ case '#':
+ return (last_char = c);
+ case '\n':
+ lineno++;
+ c = getc(in);
+ continue;
+ }
+ pushc = c;
+ return ('\\');
+ }
+ if (c == '\n')
+ lineno++;
+ if (c == '#')
+ while (((c = get()) != '\n') && (c != EOF));
+ return (last_char = c);
+}
+
+/*
+ * num_tok - expecting a number token. If not a number,
+ * return -1.
+ * Handles octal (who uses octal anymore?)
+ * hex
+ * decimal
+ * Looks for a 'k' at the end of decimal numbers
+ * and multiplies by 1024.
+ */
+static int
+num_tok(void)
+{
+ char *s = next_tok(), c;
+ int val = 0, base;
+
+ base = 10;
+ c = *s++;
+ if (c == '0') {
+ base = 8;
+ c = *s++;
+ if (c == '\0') return 0;
+ else if (c == 'x' || c == 'X') {
+ c = *s++;
+ base = 16;
+ }
+ }
+ do {
+ switch (c) {
+ case 'k':
+ case 'K':
+ if (val && base == 10 && *s == 0)
+ return (val * 1024);
+ return (-1);
+ default:
+ return (-1);
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ val = val * base + c - '0';
+ break;
+
+ case '8':
+ case '9':
+ if (base == 8)
+ return (-1);
+ else
+ val = val * base + c - '0';
+ break;
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ if (base == 16)
+ val = val * base + c - 'a' + 10;
+ else
+ return (-1);
+ break;
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ if (base == 16)
+ val = val * base + c - 'A' + 10;
+ else
+ return (-1);
+ break;
+ }
+ } while ((c = *s++) != 0);
+ return (val);
+}
+
+static char *_next_tok(void);
+
+static char *
+next_tok(void)
+{
+ char *s = _next_tok();
+#if 0
+ printf("Tok = %s\n", s);
+#endif
+ return (s);
+}
+
+/*
+ * get one token. Handles string quoting etc.
+ */
+static char *
+_next_tok(void)
+{
+ static char buf[1024];
+ char *p = buf, instr = 0;
+ int c;
+
+ if (pusht) {
+ pusht = 0;
+ return (buf);
+ }
+ for (;;) {
+ c = get();
+ switch (c) {
+ default:
+ *p++ = c;
+ break;
+ case '"':
+ if (instr) {
+ *p++ = 0;
+ return (buf);
+ }
+ instr = 1;
+ break;
+ case '\n':
+ if (instr) {
+ error("unterminated string");
+ break;
+ }
+ case ' ':
+ case '\t':
+ /* Eat whitespace unless in a string. */
+ if (!instr) {
+ if (p != buf) {
+ *p++ = 0;
+ return (buf);
+ }
+ } else
+ *p++ = c;
+ break;
+ case '-':
+ case '?':
+ case '*':
+ /* Special characters that are tokens on their own. */
+ if (instr)
+ *p++ = c;
+ else {
+ if (p != buf)
+ pushc = c;
+ else
+ *p++ = c;
+ *p++ = 0;
+ return (buf);
+ }
+ break;
+ case EOF:
+ if (includes) {
+ fclose(in);
+ /* go back to previous config file */
+ includes--;
+ in = configfiles[includes].filep;
+ filename = configfiles[includes].filename;
+ lineno = configfiles[includes].lineno;
+ return _next_tok(); /* recursive */
+ }
+ if (p != buf) {
+ *p++ = 0;
+ return (buf);
+ }
+ strcpy(buf, "__EOF__");
+ return (buf);
+ }
+ }
+}
+
+/*
+ * get the rest of the line. If the
+ * last character scanned was a newline, then
+ * return an empty line. If this isn't checked, then
+ * a getline may incorrectly return the next line.
+ */
+static char *
+getline(void)
+{
+ char buf[1024], *p = buf;
+ int c, i = 0;
+
+ if (last_char == '\n')
+ return (newstr(""));
+ do {
+ c = get();
+ } while (c == ' ' || c == '\t');
+ for (; c != '\n' && c != EOF; c = get())
+ if (i++ < sizeof(buf) - 10)
+ *p++ = c;
+ *p = 0;
+ return (newstr(buf));
+}
+
+/*
+ * Include configuration file
+ */
+static void
+file_include(char *incl)
+{
+ int i, included;
+ FILE *fp;
+
+ /* check nesting overflow */
+ if (includes >= MAXINCLUDES) {
+ if (debug_level >= 1) {
+ logmsg("%s: include nesting overflow "
+ "at line %d, near %s\n", filename, lineno, incl);
+ }
+ free(incl);
+ goto out;
+ }
+
+ /* check recursive inclusion */
+ for (i = 0, included = 0; i <= includes; i++) {
+ if (strcmp(incl, configfiles[i].filename) == 0) {
+ included = 1;
+ break;
+ }
+ }
+ if (included == 1) {
+ if (debug_level >= 1) {
+ logmsg("%s: can't include the same file twice "
+ "at line %d, near %s\n", filename, lineno, incl);
+ }
+ free(incl);
+ goto out;
+ }
+
+ if (!(fp = fopen(incl, "r"))) {
+ if (debug_level >= 1) {
+ logmsg("%s: can't open include file "
+ "at line %d, near %s\n", filename, lineno, incl);
+ }
+ free(incl);
+ goto out;
+ }
+
+ /* save line number of the current config file */
+ configfiles[includes].lineno = lineno;
+ lineno = 1;
+
+ /* now we start parsing new config file */
+ includes++;
+ in = configfiles[includes].filep = fp;
+ filename = configfiles[includes].filename = incl;
+out:
+ return;
+}
diff --git a/usr.sbin/pccard/pccardd/pccard.conf.5 b/usr.sbin/pccard/pccardd/pccard.conf.5
new file mode 100644
index 0000000..51adca2
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/pccard.conf.5
@@ -0,0 +1,298 @@
+.\"
+.\" Copyright (c) 1994 Andrew McRae. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd November 2, 1994
+.Dt PCCARD.CONF 5
+.Os
+.Sh NAME
+.Nm pccard.conf
+.Nd
+.Xr pccardd 8
+configuration file
+.Sh DESCRIPTION
+The
+.Nm
+file is the configuration file for the
+.Xr pccardd 8
+PC-CARD slot management daemon.
+It provides information to allow card
+identification, and the matching of drivers (along
+with driver resources) to the PC-CARD cards.
+.Pp
+There are four basic elements within the configuration file;
+An optional
+.Em "resource pool"
+preceding the other sections,
+and one or more
+.Em "card identifiers" ,
+and
+.Em "device instances" .
+The latter two may appear in any order, and may be
+interspersed as desired.
+.Pp
+The
+.Pa /etc/pccard.conf
+file is included from the file
+.Pa /etc/defaults/pccard.conf ,
+which contains the default resource pool settings and
+pccard identifiers database.
+The user specific configuration can be specified in
+.Pa /etc/pccard.conf
+when the user wishes to override these defaults and/or
+add additional entries.
+.Pp
+Each PC-CARD card contains configuration tuples that provide
+the manufacturer and card version; these are used
+to identify the card specification in the configuration
+file, and from this find a driver that can be used to
+interface to the particular card.
+There is a many-to-one mapping
+between cards to drivers i.e a single driver may interface to
+multiple types of cards.
+To aid this, card parameters may be
+specified separately from the driver to initialize the card or
+extract (in the case of a network card) an Ethernet address.
+.Pp
+Once a driver is allocated to a card, it stays
+allocated to that particular card.
+However, multiple instances of the same type of driver can be
+configured, so that if two cards are plugged in that map to a
+similar type of driver, other driver instances of the same name
+can be configured.
+.Pp
+The
+.Em insert
+and
+.Em remove
+commands allow a shell command line to be executed.
+The command to be executed is the rest of the line after
+the keyword.
+The line can be continued using a backslash.
+A simple
+macro substitution allows the current kernel device name
+.Em ( $device )
+and
+network card Ethernet address
+.Em ( $ether )
+to be inserted into the command line.
+.Xr pccardd 8
+uses the
+.Xr system 3
+subroutine to execute the command line.
+.Pp
+.Xr pccardd 8
+will use syslog to announce the insertion and removal of cards.
+It uses either the string set by the
+.Em logstr
+command, or the manufacturer and card version strings if none has
+been set.
+.Pp
+Numeric values may be expressed as octal, hex or decimal.
+If a decimal number has
+.Em k
+or
+.Em K
+appended to it, the value is multiplied by 1024. Names may be
+quoted using double quotes if spaces are required.
+A hash character comments out the rest of the line.
+.Ss "Resource pool"
+The (optional) section specifies a pool of system resources
+such as ISA bus memory address space, Input/Output ports and
+interrupt request numbers.
+This resource pool is used
+to allocate address space and interrupt numbers dynamically
+according to the requirements specified in each driver
+description.
+.Pp
+The syntax of the resources is as follows:
+.Pp
+.Dl io Ar start - end ...
+.Dl memory Ar address size ...
+.Dl irq Ar irq-number ...
+.Pp
+Each of the statements define I/O, memory or IRQ
+blocks that can be used to allocate to drivers when
+they are initialized.
+.Pp
+The syntax of the debuglevel parameter:
+.Pp
+.Dl debuglevel Ar level
+.Pp
+Multiple lines of any of the above statements may be
+present to allow separate blocks of each resource to be
+defined.
+.Ss "Card Identifiers"
+The syntax for card identifiers is:
+.Pp
+.Dl card Ar manufacturer version [ add_info1 [ add_info2 ]]
+.Dl config Ar index driver interrupt [ flags ]
+.Dl ether Ar offset
+.Dl reset Ar time
+.Dl iosize Ar size
+.Dl memsize Ar size
+.Dl insert Ar command
+.Dl remove Ar command
+.Dl logstr Ar string
+.Pp
+The first line is mandatory;
+the latter statements are optional and can appear in
+any order.
+There may be multiple
+.Em config
+lines.
+The
+.Em card
+parameters are the Manufacturer name, card version and
+additional information add_info1, add_info2 that
+is used to match the values from the card's CIS memory.
+These parameter can be described in extended regular expression
+.Xr regex 3
+if the string is enclosed by '/' like "/.*/".
+Each of the expressions is evaluated with a character '^' at top.
+.Pp
+The
+.Em config
+parameters select the particular card's configuration index
+from the range available in the card's CIS, the driver that
+is to be associated with this configuration, and the interrupt
+level (if any) to be assigned.
+An optional set of flags may
+be assigned.
+In
+.Ar index ,
+specify either ``auto'' or ``default'' or the range available in the card's CIS.
+``auto'' allows to allocate resources automatically with information
+from the CIS and status of using I/O resources.
+.Pp
+The optional
+.Em ether
+keyword is used when network cards have their physical Ethernet address
+located within the attribute memory of the card.
+The parameter of this
+statement indicates the offset within the attribute memory of the
+Ethernet address.
+This value can be used within insert/remove
+commands using the
+.Em $ether
+macro.
+.Pp
+The optional
+.Em reset
+keyword specifies reset duration at a card insertion in
+.Ar time
+milliseconds.
+Default is 100msec.
+.Pp
+.Em iosize
+and
+.Em memsize
+keywords are used with cards whose resources such as I/O ports and
+shared memory block are not specified in the CIS tuple.
+.Pp
+The
+.Em insert
+and
+.Em remove
+sections allow shell commands to be specified that are executed
+when the card is inserted or removed.
+Multiple
+.Em insert
+and
+.Em remove
+commands are allowed, and they are executed in the order they
+are listed.
+.Pp
+The
+.Em logstr
+command allows the user to set the string to be logged when this card is
+inserted or removed.
+If
+.Em logstr
+isn't specified, then the manufacturer and
+card version strings from the CIS are used to synthesize the string issued.
+.Ss "Wildcard entries"
+Following two wildcard entries of card identifiers are available
+for generic type of the cards:
+.Pp
+.Dl generic serial
+.Dl generic fixed_disk
+.Pp
+The keyword
+.Em serial
+matches ``Functional ID: Serial port/modem'' and
+.Em fixed_disk
+matches ``Fixed disk card''.
+The syntax is the same of
+.Em "card identifiers"
+but used ``generic'' instead of ``card'' in the first line.
+These are in the last of
+.Nm
+because unmatched cards with the other
+.Em card
+entries can match these entries secondly.
+The alias ``function'' can be used instead of ``generic'' because of
+the historical reason.
+.Sh EXAMPLES
+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/defaults/pccard.conf -compact
+.It Pa /etc/defaults/pccard.conf
+The
+.Xr pccardd 8
+default configuration file.
+.It Pa /etc/pccard.conf
+The
+user configuration file.
+.El
+.Sh SEE ALSO
+.Xr pccardd 8
diff --git a/usr.sbin/pccard/pccardd/pccardd.8 b/usr.sbin/pccard/pccardd/pccardd.8
new file mode 100644
index 0000000..266c9ae
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/pccardd.8
@@ -0,0 +1,180 @@
+.\"
+.\" Copyright (c) 1994 Andrew McRae. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd November 1, 1994
+.Dt PCCARDD 8
+.Os
+.Sh NAME
+.Nm pccardd
+.Nd PC-CARD (PCMCIA) management daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Op Fl v
+.Op Fl z
+.Op Fl i Ar IRQ
+.Op Fl I
+.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/defaults/pccard.conf
+which includes
+.Pa /etc/pccard.conf
+as the user configuration file)
+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
+SIGHUP causes
+.Nm
+to reload the configuration files.
+.Pp
+The start options understood by
+.Nm
+are:
+.Bl -tag -width Ds
+.It Fl d
+Do not run as a daemon, but run in the foreground and
+display error messages.
+.It Fl v
+After reading the configuration file, print out a summary
+of it.
+.It Fl z
+Delays running as a daemon until after the cards have been probed and attached.
+.It Fl I
+Don't get a list of free IRQs from kernel.
+.It Fl i Ar IRQ
+Configures an available IRQ. It overrides the "irq" line in
+.Pa /etc/defaults/pccard.conf
+and
+.Pa /etc/pccard.conf .
+.It Fl f Ar configfile
+Specifies a different configuration file to be used
+in placed of the default file
+.Pa /etc/defaults/pccard.conf .
+The file format is detailed in
+.Xr pccard.conf 5 ,
+and lists the PC-CARD cards recognized by
+.Nm ,
+and the kernel drivers and devices that are used to
+interface to the card.
+.El
+.Sh FILES
+.Bl -tag -width /etc/defaults/pccard.conf -compact
+.It Pa /etc/defaults/pccard.conf
+default configuration file
+.It Pa /etc/pccard.conf
+user configuration file
+.It Pa /var/run/pccardd.pid
+process id of the currently running
+.Nm
+.El
+.Sh SEE ALSO
+.Xr pccard.conf 5 ,
+.Xr ifconfig 8
+.Sh AUTHORS
+Developed by
+.An Andrew McRae Aq andrew@mega.com.au .
+.Sh BUGS
+.Nm Pccardd
+can set up card parameters, but cannot guarantee that
+particular drivers can work with the card.
+.Pp
+Removing cards may cause problems if system resources
+have been associated with the card, such as network
+mounted filesystems.
diff --git a/usr.sbin/pccard/pccardd/pccardd.c b/usr.sbin/pccard/pccardd/pccardd.c
new file mode 100644
index 0000000..ba70336
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/pccardd.c
@@ -0,0 +1,279 @@
+/*
+ * Copyright (c) 1995 Andrew McRae. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+#define EXTERN
+#include "cardd.h"
+
+char *config_file = "/etc/defaults/pccard.conf";
+static char *pid_file = "/var/run/pccardd.pid";
+
+/*
+ * pathname of UNIX-domain socket
+ */
+static char *socket_name = "/var/tmp/.pccardd";
+static char *sock = 0;
+static int server_sock;
+
+/* SIGHUP signal handler */
+static void
+restart(void)
+{
+ bitstr_t bit_decl(io_inuse, IOPORTS);
+ bitstr_t bit_decl(mem_inuse, MEMBLKS);
+ int irq_inuse[16];
+ int i;
+ struct sockaddr_un sun;
+
+ bit_nclear(io_inuse, 0, IOPORTS-1);
+ bit_nclear(mem_inuse, 0, MEMBLKS-1);
+ bzero(irq_inuse, sizeof(irq_inuse));
+
+ /* compare the initial and current state of resource pool */
+ for (i = 0; i < IOPORTS; i++) {
+ if (bit_test(io_init, i) == 1 && bit_test(io_avail, i) == 0) {
+ if (debug_level >= 1) {
+ logmsg("io 0x%x seems to be in use\n", i);
+ }
+ bit_set(io_inuse, i);
+ }
+ }
+ for (i = 0; i < MEMBLKS; i++) {
+ if (bit_test(mem_init, i) == 1 && bit_test(mem_avail, i) == 0) {
+ if (debug_level >= 1) {
+ logmsg("mem 0x%x seems to be in use\n", i);
+ }
+ bit_set(mem_inuse, i);
+ }
+ }
+ for (i = 0; i < 16; i++) {
+ if (irq_init[i] == 1 && pool_irq[i] == 0) {
+ if (debug_level >= 1) {
+ logmsg("irq %d seems to be in use\n", i);
+ }
+ irq_inuse[i] = 1;
+ }
+ }
+
+ readfile(config_file);
+
+ /* reflect used resources to managed resource pool */
+ for (i = 0; i < IOPORTS; i++) {
+ if (bit_test(io_inuse, i) == 1) {
+ bit_clear(io_avail, i);
+ }
+ }
+ for (i = 0; i < MEMBLKS; i++) {
+ if (bit_test(mem_inuse, i) == 1) {
+ bit_clear(mem_avail, i);
+ }
+ }
+ for (i = 0; i < 16; i++) {
+ if (irq_inuse[i] == 1) {
+ pool_irq[i] = 0;
+ }
+ }
+ close(server_sock);
+ if ((server_sock = socket(PF_LOCAL, SOCK_DGRAM, 0)) < 0)
+ die("socket failed");
+ bzero(&sun, sizeof(sun));
+ sun.sun_family = AF_UNIX;
+ if (sock) {
+ socket_name = sock;
+ }
+ strcpy(sun.sun_path, socket_name);
+ slen = SUN_LEN(&sun);
+ (void)unlink(socket_name);
+ if (bind(server_sock, (struct sockaddr *) & sun, slen) < 0)
+ die("bind failed");
+ chown(socket_name, 0, 5); /* XXX - root.operator */
+ chmod(socket_name, 0660);
+ set_socket(server_sock);
+}
+
+/* SIGTERM/SIGINT signal handler */
+static void
+term(int sig)
+{
+ logmsg("pccardd terminated: signal %d received", sig);
+ (void)unlink(pid_file);
+ exit(0);
+}
+
+static void
+write_pid()
+{
+ FILE *fp = fopen(pid_file, "w");
+
+ if (fp) {
+ fprintf(fp, "%d\n", getpid());
+ fclose(fp);
+ }
+}
+
+int doverbose = 0;
+/*
+ * mainline code for cardd
+ */
+int
+main(int argc, char *argv[])
+{
+ struct slot *sp;
+ int count, dodebug = 0;
+ int delay = 0;
+ int irq_arg[16];
+ int irq_specified = 0;
+ int i;
+ struct sockaddr_un sun;
+#define COM_OPTS ":Idvf:s:i:z"
+
+ bzero(irq_arg, sizeof(irq_arg));
+ use_kern_irq = 1;
+ debug_level = 0;
+ pccard_init_sleep = 5000000;
+ cards = last_card = 0;
+ while ((count = getopt(argc, argv, COM_OPTS)) != -1) {
+ switch (count) {
+ case 'I':
+ use_kern_irq = 0;
+ break;
+ case 'd':
+ setbuf(stdout, 0);
+ setbuf(stderr, 0);
+ dodebug = 1;
+ break;
+ case 'v':
+ doverbose = 1;
+ break;
+ case 'f':
+ config_file = optarg;
+ break;
+ case 'i':
+ /* configure available irq */
+ if (sscanf(optarg, "%d", &i) != 1) {
+ fprintf(stderr, "%s: -i number\n", argv[0]);
+ exit(1);
+ }
+ irq_arg[i] = 1;
+ irq_specified = 1;
+ break;
+ case 's':
+ sock = optarg;
+ break;
+ case 'z':
+ delay = 1;
+ break;
+ case ':':
+ die("no config file argument");
+ break;
+ case '?':
+ die("illegal option");
+ break;
+ }
+ }
+#ifdef DEBUG
+ dodebug = 1;
+#endif
+ io_avail = bit_alloc(IOPORTS); /* Only supports ISA ports */
+ io_init = bit_alloc(IOPORTS);
+
+ /* Mem allocation done in MEMUNIT units. */
+ mem_avail = bit_alloc(MEMBLKS);
+ mem_init = bit_alloc(MEMBLKS);
+ readfile(config_file);
+ if (irq_specified) {
+ bcopy(irq_arg, pool_irq, sizeof(irq_arg));
+ bcopy(irq_arg, irq_init, sizeof(irq_arg));
+ }
+ if (doverbose)
+ dump_config_file();
+ log_setup();
+ if (!dodebug && !delay)
+ if (daemon(0, 0))
+ die("fork failed");
+ slots = readslots();
+ if (slots == 0)
+ die("no PC-CARD slots");
+ if (delay)
+ if (daemon(0, 0))
+ die("fork failed");
+ logmsg("pccardd started", NULL);
+ write_pid();
+
+ if ((server_sock = socket(PF_LOCAL, SOCK_DGRAM, 0)) < 0)
+ die("socket failed");
+ bzero(&sun, sizeof(sun));
+ sun.sun_family = AF_UNIX;
+ if (sock) {
+ socket_name = sock;
+ }
+ strcpy(sun.sun_path, socket_name);
+ slen = SUN_LEN(&sun);
+ (void)unlink(socket_name);
+ if (bind(server_sock, (struct sockaddr *) & sun, slen) < 0)
+ die("bind failed");
+ chown(socket_name, 0, 5); /* XXX - root.operator */
+ chmod(socket_name, 0660);
+ set_socket(server_sock);
+
+ (void)signal(SIGINT, dodebug ? term : SIG_IGN);
+ (void)signal(SIGTERM, term);
+ (void)signal(SIGHUP, (void (*)(int))restart);
+
+ for (;;) {
+ fd_set rmask, emask;
+ FD_ZERO(&emask);
+ FD_ZERO(&rmask);
+ for (sp = slots; sp; sp = sp->next)
+ FD_SET(sp->fd, &emask);
+ FD_SET(server_sock, &rmask);
+ count = select(32, &rmask, 0, &emask, 0);
+ if (count == -1) {
+ logerr("select");
+ continue;
+ }
+ if (count) {
+ for (sp = slots; sp; sp = sp->next)
+ if (FD_ISSET(sp->fd, &emask))
+ slot_change(sp);
+ if (FD_ISSET(server_sock, &rmask))
+ process_client();
+ }
+ }
+}
diff --git a/usr.sbin/pccard/pccardd/readcis.c b/usr.sbin/pccard/pccardd/readcis.c
new file mode 100644
index 0000000..6953337
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/readcis.c
@@ -0,0 +1,788 @@
+/*
+ * Copyright (c) 1995 Andrew McRae. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/*
+ * Code cleanup, bug-fix and extension
+ * by Tatsumi Hosokawa <hosokawa@mt.cs.keio.ac.jp>
+ */
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#include <pccard/cardinfo.h>
+#include <pccard/cis.h>
+
+#include "readcis.h"
+
+#ifdef RATOCLAN
+static int rex5588 = 0;
+#endif
+
+static int read_attr(int, char *, int);
+static int ck_linktarget(int, off_t, int);
+static void cis_info(struct cis *, unsigned char *, int);
+static void device_desc(unsigned char *, int, struct dev_mem *);
+static void config_map(struct cis *, unsigned char *, int);
+static void cis_config(struct cis *, unsigned char *, int);
+static void cis_manuf_id(struct cis *, unsigned char *, int);
+static void cis_func_id(struct cis *, unsigned char *, int);
+static void cis_network_ext(struct cis *, unsigned char *, int);
+static struct tuple_list *read_one_tuplelist(int, int, off_t);
+static struct tuple_list *read_tuples(int);
+static struct tuple *find_tuple_in_list(struct tuple_list *, unsigned char);
+static struct tuple_info *get_tuple_info(unsigned char);
+
+static struct tuple_info tuple_info[] = {
+ {"Null tuple", 0x00, 0},
+ {"Common memory descriptor", 0x01, 255},
+ {"Long link to next chain for CardBus", 0x02, 255},
+ {"Indirect access", 0x03, 255},
+ {"Configuration map for CardBus", 0x04, 255},
+ {"Configuration entry for CardBus", 0x05, 255},
+ {"Long link to next chain for MFC", 0x06, 255},
+ {"Base address register for CardBus", 0x07, 6},
+ {"Checksum", 0x10, 5},
+ {"Long link to attribute memory", 0x11, 4},
+ {"Long link to common memory", 0x12, 4},
+ {"Link target", 0x13, 3},
+ {"No link", 0x14, 0},
+ {"Version 1 info", 0x15, 255},
+ {"Alternate language string", 0x16, 255},
+ {"Attribute memory descriptor", 0x17, 255},
+ {"JEDEC descr for common memory", 0x18, 255},
+ {"JEDEC descr for attribute memory", 0x19, 255},
+ {"Configuration map", 0x1A, 255},
+ {"Configuration entry", 0x1B, 255},
+ {"Other conditions for common memory", 0x1C, 255},
+ {"Other conditions for attribute memory", 0x1D, 255},
+ {"Geometry info for common memory", 0x1E, 255},
+ {"Geometry info for attribute memory", 0x1F, 255},
+ {"Manufacturer ID", 0x20, 4},
+ {"Functional ID", 0x21, 2},
+ {"Functional EXT", 0x22, 255},
+ {"Software interleave", 0x23, 2},
+ {"Version 2 Info", 0x40, 255},
+ {"Data format", 0x41, 255},
+ {"Geometry", 0x42, 4},
+ {"Byte order", 0x43, 2},
+ {"Card init date", 0x44, 4},
+ {"Battery replacement", 0x45, 4},
+ {"Organization", 0x46, 255},
+ {"Terminator", 0xFF, 0},
+ {0, 0, 0}
+};
+
+/*
+ * After reading the tuples, decode the relevant ones.
+ */
+struct cis *
+readcis(int fd)
+{
+ struct tuple_list *tl;
+ struct tuple *tp;
+ struct cis *cp;
+
+ cp = xmalloc(sizeof(*cp));
+ cp->tlist = read_tuples(fd);
+ if (cp->tlist == 0)
+ return (NULL);
+
+ for (tl = cp->tlist; tl; tl = tl->next)
+ for (tp = tl->tuples; tp; tp = tp->next) {
+#if 0
+ printf("tuple code = 0x%02x, data is\n", tp->code);
+ dump(tp->data, tp->length);
+#endif
+ switch (tp->code) {
+ case CIS_MEM_COMMON: /* 0x01 */
+ device_desc(tp->data, tp->length, &cp->common_mem);
+ break;
+ case CIS_INFO_V1: /* 0x15 */
+ cis_info(cp, tp->data, tp->length);
+ break;
+ case CIS_MEM_ATTR: /* 0x17 */
+ device_desc(tp->data, tp->length, &cp->attr_mem);
+ break;
+ case CIS_CONF_MAP: /* 0x1A */
+ config_map(cp, tp->data, tp->length);
+ break;
+ case CIS_CONFIG: /* 0x1B */
+ cis_config(cp, tp->data, tp->length);
+ break;
+ case CIS_MANUF_ID: /* 0x20 */
+ cis_manuf_id(cp, tp->data, tp->length);
+ break;
+ case CIS_FUNC_ID: /* 0x21 */
+ cis_func_id(cp, tp->data, tp->length);
+ break;
+ case CIS_FUNC_EXT: /* 0x22 */
+ if (cp->func_id1 == 6) /* LAN adaptor */
+ cis_network_ext(cp, tp->data, tp->length);
+ break;
+ }
+ }
+ return (cp);
+}
+
+/*
+ * free_cis - delete cis entry.
+ */
+void
+freecis(struct cis *cp)
+{
+ struct cis_ioblk *io;
+ struct cis_memblk *mem;
+ struct cis_config *conf;
+ struct tuple *tp;
+ struct tuple_list *tl;
+
+ while ((tl = cp->tlist) != 0) {
+ cp->tlist = tl->next;
+ while ((tp = tl->tuples) != 0) {
+ tl->tuples = tp->next;
+ if (tp->data)
+ free(tp->data);
+ }
+ }
+
+ while ((conf = cp->conf) != 0) {
+ cp->conf = conf->next;
+ while ((io = conf->io) != 0) {
+ conf->io = io->next;
+ free(io);
+ }
+ while ((mem = conf->mem) != 0) {
+ conf->mem = mem->next;
+ free(mem);
+ }
+ free(conf);
+ }
+ free(cp);
+}
+
+/*
+ * Fills in CIS version data.
+ */
+static void
+cis_info(struct cis *cp, unsigned char *p, int len)
+{
+ cp->maj_v = *p++;
+ cp->min_v = *p++;
+ len -= 2;
+ if (cp->manuf) {
+ free(cp->manuf);
+ cp->manuf = NULL;
+ }
+ if (len > 1 && *p != 0xff) {
+ cp->manuf = strdup(p);
+ len -= strlen(p) + 1;
+ p += strlen(p) + 1;
+ }
+ if (cp->vers) {
+ free(cp->vers);
+ cp->vers = NULL;
+ }
+ if (len > 1 && *p != 0xff) {
+ cp->vers = strdup(p);
+ len -= strlen(p) + 1;
+ p += strlen(p) + 1;
+ } else {
+ cp->vers = strdup("[none]");
+ }
+ if (cp->add_info1) {
+ free(cp->add_info1);
+ cp->add_info1 = NULL;
+ }
+ if (len > 1 && *p != 0xff) {
+ cp->add_info1 = strdup(p);
+ len -= strlen(p) + 1;
+ p += strlen(p) + 1;
+ } else {
+ cp->add_info1 = strdup("[none]");
+ }
+ if (cp->add_info2) {
+ free(cp->add_info2);
+ cp->add_info2 = NULL;
+ }
+ if (len > 1 && *p != 0xff)
+ cp->add_info2 = strdup(p);
+ else
+ cp->add_info2 = strdup("[none]");
+}
+
+static void
+cis_manuf_id(struct cis *cp, unsigned char *p, int len)
+{
+ if (len > 4) {
+ cp->manufacturer = tpl16(p);
+ cp->product = tpl16(p+2);
+ if (len == 5)
+ cp->prodext = *(p+4); /* For xe driver */
+ } else {
+ cp->manufacturer=0;
+ cp->product=0;
+ cp->prodext=0;
+ }
+}
+/*
+ * Fills in CIS function ID.
+ */
+static void
+cis_func_id(struct cis *cp, unsigned char *p, int len)
+{
+ cp->func_id1 = *p++;
+ cp->func_id2 = *p++;
+}
+
+static void
+cis_network_ext(struct cis *cp, unsigned char *p, int len)
+{
+ int i;
+
+ switch (p[0]) {
+ case 4: /* Node ID */
+ if (len <= 2 || len < p[1] + 2)
+ return;
+
+ if (cp->lan_nid)
+ free(cp->lan_nid);
+ cp->lan_nid = xmalloc(p[1]);
+
+ for (i = 0; i <= p[1]; i++)
+ cp->lan_nid[i] = p[i + 1];
+ break;
+ }
+}
+
+/*
+ * "FUJITSU LAN Card (FMV-J182)" has broken CIS
+ */
+static int
+fmvj182_check(unsigned char *p)
+{
+ char manuf[BUFSIZ], vers[BUFSIZ];
+
+ p++; /* major version */
+ p++; /* minor version */
+ strncpy(manuf, p, sizeof(manuf) - 1);
+ while (*p++);
+ strncpy(vers, p, sizeof(vers) - 1);
+ if (!strcmp(manuf, "FUJITSU") && !strcmp(vers, "LAN Card(FMV-J182)"))
+ return 1;
+ else
+ return 0;
+}
+
+#ifdef RATOCLAN
+/*
+ * "RATOC LAN Card (REX-5588)" has broken CIS
+ */
+static int
+rex5588_check(unsigned char *p)
+{
+ char manuf[BUFSIZ], vers[BUFSIZ];
+
+ p++; /* major version */
+ p++; /* minor version */
+ strncpy(manuf, p, sizeof(manuf) - 1);
+ while (*p++);
+ strncpy(vers, p, sizeof(manuf) - 1);
+ if (!strcmp(manuf, "PCMCIA LAN MBH10304 ES"))
+ return 1;
+ else
+ return 0;
+}
+#endif
+
+#ifdef HSSYNTH
+/*
+ * Broken CIS for "HITACHI MICROCOMPUTER SYSTEM LTD." "MSSHVPC02"
+ */
+static int
+hss_check(unsigned char *p)
+{
+ char manuf[BUFSIZ], vers[BUFSIZ];
+
+ p++; /* major version */
+ p++; /* minor version */
+ strncpy(manuf, p, sizeof(manuf) - 1);
+ while (*p++);
+ strncpy(vers, p, sizeof(vers) - 1);
+ if (!strcmp(manuf, "HITACHI MICROCOMPUTER SYSTEMS LTD.")
+ && !strcmp(vers, "MSSHVPC02"))
+ return 1;
+ else
+ return 0;
+}
+#endif /* HSSYNTH */
+
+/*
+ * device_desc - decode device descriptor.
+ */
+static void
+device_desc(unsigned char *p, int len, struct dev_mem *dp)
+{
+ while (len > 0 && *p != 0xFF) {
+ dp->valid = 1;
+ dp->type = (*p & 0xF0) >> 4;
+ dp->wps = !!(*p & 0x8);
+ dp->speed = *p & 7;
+ p++;
+ if (*p != 0xFF) {
+ dp->addr = (*p >> 3) & 0xF;
+ dp->units = *p & 7;
+ }
+ p++;
+ len -= 2;
+ }
+}
+
+/*
+ * configuration map of card control register.
+ */
+static void
+config_map(struct cis *cp, unsigned char *p, int len)
+{
+ unsigned char *p1;
+ int rlen = (*p & 3) + 1;
+
+ p1 = p + 1;
+ cp->last_config = *p1++ & 0x3F;
+ cp->reg_addr = parse_num(rlen | 0x10, p1, &p1, 0);
+ cp->ccrs = *p1;
+}
+
+/*
+ * Parse variable length value.
+ */
+u_int
+parse_num(int sz, u_char *p, u_char **q, int ofs)
+{
+ u_int num = 0;
+
+ switch (sz) {
+ case 0:
+ case 0x10:
+ break;
+ case 1:
+ case 0x11:
+ num = (*p++) + ofs;
+ break;
+ case 2:
+ case 0x12:
+ num = tpl16(p) + ofs;
+ p += 2;
+ break;
+ case 0x13:
+ num = tpl24(p) + ofs;
+ p += 3;
+ break;
+ case 3:
+ case 0x14:
+ num = tpl32(p) + ofs;
+ p += 4;
+ break;
+ }
+ if (q)
+ *q = p;
+ return num;
+}
+
+/*
+ * CIS config entry - Decode and build configuration entry.
+ */
+static void
+cis_config(struct cis *cp, unsigned char *p, int len)
+{
+ int x;
+ int i, j;
+ struct cis_config *conf, *last;
+ unsigned char feat;
+
+ conf = xmalloc(sizeof(*conf));
+ if ((last = cp->conf) != 0) {
+ while (last->next)
+ last = last->next;
+ last->next = conf;
+ } else
+ cp->conf = conf;
+ conf->id = *p & 0x3F; /* Config index */
+#ifdef RATOCLAN
+ if (rex5588 && conf->id >= 0x08 && conf->id <= 0x1d)
+ conf->id |= 0x20;
+#endif
+ if (*p & 0x40) /* Default flag */
+ cp->def_config = conf;
+ if (*p++ & 0x80)
+ p++; /* Interface byte skip */
+ feat = *p++; /* Features byte */
+ for (i = 0; i < CIS_FEAT_POWER(feat); i++) {
+ unsigned char parms = *p++;
+
+ conf->pwr = 1;
+ for (j = 0; j < 8; j++)
+ if (parms & (1 << j))
+ while (*p++ & 0x80);
+ }
+ if (feat & CIS_FEAT_TIMING) {
+ conf->timing = 1;
+ i = *p++;
+ if (CIS_WAIT_SCALE(i) != 3)
+ p++;
+ if (CIS_READY_SCALE(i) != 7)
+ p++;
+ if (CIS_RESERVED_SCALE(i) != 7)
+ p++;
+ }
+ if (feat & CIS_FEAT_I_O) {
+ conf->iospace = 1;
+ if (CIS_IO_RANGE & *p)
+ conf->io_blks = CIS_IO_BLKS(p[1]) + 1;
+ conf->io_addr = CIS_IO_ADDR(*p);
+ conf->io_bus = (*p >> 5) & 3; /* CIS_IO_8BIT | CIS_IO_16BIT */
+ if (*p++ & CIS_IO_RANGE) {
+ struct cis_ioblk *io;
+ struct cis_ioblk *last_io = NULL;
+
+ i = CIS_IO_ADSZ(*p);
+ j = CIS_IO_BLKSZ(*p++);
+ for (x = 0; x < conf->io_blks; x++) {
+ io = xmalloc(sizeof(*io));
+ if (last_io)
+ last_io->next = io;
+ else
+ conf->io = io;
+ last_io = io;
+ io->addr = parse_num(i, p, &p, 0);
+ io->size = parse_num(j, p, &p, 1);
+ }
+ }
+ }
+ if (feat & CIS_FEAT_IRQ) {
+ conf->irq = 1;
+ conf->irqlevel = *p & 0xF;
+ conf->irq_flags = *p & 0xF0;
+ if (*p++ & CIS_IRQ_MASK) {
+ conf->irq_mask = tpl16(p);
+ p += 2;
+ }
+ }
+ switch (CIS_FEAT_MEMORY(feat)) {
+ case CIS_FEAT_MEM_NONE:
+ break;
+ case CIS_FEAT_MEM_LEN:
+ conf->memspace = 1;
+ conf->mem = xmalloc(sizeof(*conf->mem));
+ conf->mem->length = tpl16(p) << 8;
+ break;
+ case CIS_FEAT_MEM_ADDR:
+ conf->memspace = 1;
+ conf->mem = xmalloc(sizeof(*conf->mem));
+ conf->mem->length = tpl16(p) << 8;
+ conf->mem->address = tpl16(p + 2) << 8;
+ break;
+ case CIS_FEAT_MEM_WIN: {
+ struct cis_memblk *mem;
+ struct cis_memblk *last_mem = NULL;
+
+ conf->memspace = 1;
+ x = *p++;
+ conf->memwins = CIS_MEM_WINS(x);
+ for (i = 0; i < conf->memwins; i++) {
+ mem = xmalloc(sizeof(*mem));
+ if (last_mem)
+ last_mem->next = mem;
+ else
+ conf->mem = mem;
+ last_mem = mem;
+ mem->length = parse_num(CIS_MEM_LENSZ(x) | 0x10, p, &p, 0) << 8;
+ mem->address = parse_num(CIS_MEM_ADDRSZ(x) | 0x10, p, &p, 0) << 8;
+ if (x & CIS_MEM_HOST) {
+ mem->host_address = parse_num(CIS_MEM_ADDRSZ(x) | 0x10,
+ p, &p, 0) << 8;
+ }
+ }
+ break;
+ }
+ }
+ if (feat & CIS_FEAT_MISC) {
+ conf->misc_valid = 1;
+ conf->misc = *p++;
+ }
+}
+
+/*
+ * Read the tuples from the card.
+ * The processing of tuples is as follows:
+ * - Read tuples at attribute memory, offset 0.
+ * - If a CIS_END is the first tuple, look for
+ * a tuple list at common memory offset 0; this list
+ * must start with a LINKTARGET.
+ * - If a long link tuple was encountered, execute the long
+ * link.
+ * - If a no-link tuple was seen, terminate processing.
+ * - If no no-link tuple exists, and no long link tuple
+ * exists while processing the primary tuple list,
+ * then look for a LINKTARGET tuple in common memory.
+ * - If a long link tuple is found in any list, then process
+ * it. Only one link is allowed per list.
+ */
+static struct tuple_list *tlist;
+
+static struct tuple_list *
+read_tuples(int fd)
+{
+ struct tuple_list *tl = 0, *last_tl;
+ struct tuple *tp;
+ int flag;
+ off_t offs;
+
+ tlist = 0;
+ last_tl = tlist = read_one_tuplelist(fd, MDF_ATTR, (off_t) 0);
+
+ /* Now start processing the links (if any). */
+ do {
+ flag = MDF_ATTR;
+ tp = find_tuple_in_list(last_tl, CIS_LONGLINK_A);
+ if (tp == 0) {
+ flag = 0;
+ tp = find_tuple_in_list(last_tl, CIS_LONGLINK_C);
+ }
+ if (tp && tp->length == 4) {
+ offs = tpl32(tp->data);
+#ifdef DEBUG
+ printf("Checking long link at %qd (%s memory)\n",
+ offs, flag ? "Attribute" : "Common");
+#endif
+ /* If a link was found, read the tuple list from it. */
+ if (ck_linktarget(fd, offs, flag)) {
+ tl = read_one_tuplelist(fd, flag, offs);
+ last_tl->next = tl;
+ last_tl = tl;
+ }
+ } else
+ tl = 0;
+ } while (tl);
+
+ /*
+ * If the primary list had no NOLINK tuple, and no LINKTARGET,
+ * then try to read a tuple list at common memory (offset 0).
+ */
+ if (find_tuple_in_list(tlist, CIS_NOLINK) == 0 && tlist->next == 0 &&
+ ck_linktarget(fd, (off_t) 0, 0)) {
+#ifdef DEBUG
+ printf("Reading long link at %qd (%s memory)\n",
+ offs, flag ? "Attribute" : "Common");
+#endif
+ tlist->next = read_one_tuplelist(fd, 0, (off_t) 0);
+ }
+ return (tlist);
+}
+
+/*
+ * Read one tuple list from the card.
+ */
+static struct tuple_list *
+read_one_tuplelist(int fd, int flags, off_t offs)
+{
+ struct tuple *tp, *last_tp = 0;
+ struct tuple_list *tl;
+ struct tuple_info *tinfo;
+ int total = 0;
+ unsigned char code, length;
+ int fmvj182 = 0;
+#ifdef HSSYNTH
+ int hss = 0;
+#endif /* HSSYNTH */
+
+ /* Check to see if this memory has already been scanned. */
+ for (tl = tlist; tl; tl = tl->next)
+ if (tl->offs == offs && tl->flags == (flags & MDF_ATTR))
+ return (0);
+ tl = xmalloc(sizeof(*tl));
+ tl->offs = offs;
+ tl->flags = flags & MDF_ATTR;
+ ioctl(fd, PIOCRWFLAG, &flags);
+ lseek(fd, offs, SEEK_SET);
+ do {
+ if (read_attr(fd, &code, 1) != 1) {
+ warn("CIS code read");
+ break;
+ }
+ total++;
+ if (code == CIS_NULL)
+ continue;
+ tp = xmalloc(sizeof(*tp));
+ tp->code = code;
+ if (code == CIS_END)
+ length = 0;
+ else {
+ if (read_attr(fd, &length, 1) != 1) {
+ warn("CIS len read");
+ break;
+ }
+ total++;
+ if (fmvj182 && (code == 0x1b) && (length == 25))
+ length = 31;
+ }
+ tp->length = length;
+#ifdef DEBUG
+ printf("Tuple code = 0x%x, len = %d\n", code, length);
+#endif
+ if (length == 0xFF) {
+ length = tp->length = 0;
+ code = CIS_END;
+ }
+ if (length != 0) {
+ total += length;
+ tp->data = xmalloc(length);
+ if (read_attr(fd, tp->data, length) != length) {
+ warn("CIS read");
+ break;
+ }
+ }
+
+ /*
+ * Check the tuple, and ignore it if it isn't in the table
+ * or the length is illegal.
+ */
+ tinfo = get_tuple_info(code);
+ if (code == CIS_INFO_V1) {
+ /* Hack for broken CIS of FMV-J182 Ethernet card */
+ fmvj182 = fmvj182_check(tp->data);
+#ifdef RATOCLAN
+ /* Hack for RATOC LAN card */
+ rex5588 = rex5588_check(tp->data);
+#endif /* RATOCLAN */
+#ifdef HSSYNTH
+ /* Hack for Hitachi Speech Synthesis card */
+ hss = hss_check(tp->data);
+#endif /* HSSYNTH */
+ }
+ if (tinfo == NULL || (tinfo->length != 255 && tinfo->length > length)) {
+ printf("code %s ignored\n", tuple_name(code));
+ tp->code = CIS_NULL;
+ }
+ if (tl->tuples == NULL)
+ tl->tuples = tp;
+ else
+ last_tp->next = tp;
+ last_tp = tp;
+ } while (code != CIS_END && total < 1024);
+ return (tl);
+}
+
+/*
+ * return true if the offset points to a LINKTARGET tuple.
+ */
+static int
+ck_linktarget(int fd, off_t offs, int flag)
+{
+ char blk[5];
+
+ ioctl(fd, PIOCRWFLAG, &flag);
+ lseek(fd, offs, SEEK_SET);
+ if (read_attr(fd, blk, 5) != 5)
+ return (0);
+ if (blk[0] == 0x13 &&
+ blk[1] == 0x3 &&
+ blk[2] == 'C' &&
+ blk[3] == 'I' &&
+ blk[4] == 'S')
+ return (1);
+ return (0);
+}
+
+/*
+ * find_tuple_in_list - find a tuple within a
+ * single tuple list.
+ */
+static struct tuple *
+find_tuple_in_list(struct tuple_list *tl, unsigned char code)
+{
+ struct tuple *tp;
+
+ for (tp = tl->tuples; tp; tp = tp->next)
+ if (tp->code == code)
+ break;
+ return (tp);
+}
+
+static int
+read_attr(int fd, char *bp, int len)
+{
+ char blk[1024], *p = blk;
+ int i, l;
+
+ if (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..8c718e2
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/readcis.h
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 1995 Andrew McRae. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct tuple {
+ struct tuple *next;
+ unsigned char code;
+ int length;
+ unsigned char *data;
+};
+
+struct tuple_list {
+ struct tuple_list *next;
+ struct tuple *tuples;
+ off_t offs;
+ int flags;
+};
+
+struct tuple_info {
+ char *name;
+ unsigned char code;
+ unsigned char length; /* 255 means variable length */
+};
+
+/*
+ * Memory device descriptor.
+ */
+struct dev_mem {
+ unsigned char valid;
+ unsigned char type;
+ unsigned char speed;
+ unsigned char wps;
+ unsigned char addr;
+ unsigned char units;
+};
+
+/*
+ * One I/O structure describing a possible I/O map
+ * of the card.
+ */
+struct cis_ioblk {
+ struct cis_ioblk *next;
+ unsigned int addr;
+ unsigned int size;
+};
+
+/*
+ * A structure storing a memory map for the card.
+ */
+struct cis_memblk {
+ struct cis_memblk *next;
+ unsigned int address;
+ unsigned int length;
+ unsigned int host_address;
+};
+
+/*
+ * One configuration entry for the card.
+ */
+struct cis_config {
+ struct cis_config *next;
+ unsigned int pwr:1; /* Which values are defined. */
+ unsigned int timing:1;
+ unsigned int iospace:1;
+ unsigned int irq:1;
+ unsigned int memspace:1;
+ unsigned int misc_valid:1;
+ unsigned char id;
+ unsigned char io_blks;
+ unsigned char io_addr;
+ unsigned char io_bus;
+ struct cis_ioblk *io;
+ unsigned char irqlevel;
+ unsigned char irq_flags;
+ unsigned irq_mask;
+ unsigned char memwins;
+ struct cis_memblk *mem;
+ unsigned char misc;
+};
+
+/*
+ * Structure holding all data retrieved from the
+ * CIS block on the card.
+ * The default configuration contains interface defaults
+ * not listed in each separate configuration.
+ */
+struct cis {
+ struct tuple_list *tlist;
+ char *manuf;
+ char *vers;
+ char *add_info1;
+ char *add_info2;
+ unsigned char maj_v, min_v;
+ unsigned char last_config;
+ unsigned char ccrs;
+ unsigned long reg_addr;
+ u_int manufacturer;
+ u_int product;
+ u_int prodext;
+ unsigned char func_id1, func_id2;
+ struct dev_mem attr_mem;
+ struct dev_mem common_mem;
+ struct cis_config *def_config;
+ struct cis_config *conf;
+ unsigned char *lan_nid;
+};
+
+#define tpl32(tp) ((*((tp) + 3) << 24) | \
+ (*((tp) + 2) << 16) | \
+ (*((tp) + 1) << 8) | *(tp))
+#define tpl24(tp) ((*((tp) + 2) << 16) | \
+ (*((tp) + 1) << 8) | *(tp))
+#define tpl16(tp) ((*((tp) + 1) << 8) | *(tp))
+
+void *xmalloc(int);
+void dump(unsigned char *, int);
+void dumpcis(struct cis *);
+void freecis(struct cis *);
+struct cis *readcis(int);
+
+char *tuple_name(unsigned char);
+u_int parse_num(int, u_char *, u_char **, int);
diff --git a/usr.sbin/pccard/pccardd/server.c b/usr.sbin/pccard/pccardd/server.c
new file mode 100644
index 0000000..d5a271f
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/server.c
@@ -0,0 +1,187 @@
+/*
+ * pccardd UNIX-domain socket interface
+ * Copyright (C) 1996 by Tatsumi Hosokawa <hosokawa@mt.cs.keio.ac.jp>
+ *
+ * $Id: server.c,v 1.3 1999/02/07 08:02:44 kuriyama Exp $
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <signal.h>
+#include <setjmp.h>
+
+#include "cardd.h"
+
+static void
+cardnum(char *buf)
+{
+ int i = 0;
+ struct slot *sp;
+
+ for (sp = slots; sp; sp = sp->next)
+ i++;
+ if (i > MAXSLOT)
+ i = MAXSLOT;
+ sprintf(buf, "%2d", i);
+}
+
+static struct slot *
+find_slot(int slot)
+{
+ struct slot *sp;
+
+ /* Search the list until we find the slot or get to the end */
+ for (sp = slots; sp && sp->slot != slot; sp = sp->next)
+ continue;
+
+ return ( sp );
+}
+
+static void
+cardname(char *buf, int slot)
+{
+ struct slot *sp;
+ char *manuf, *vers, *drv, *stat;
+
+ /* Look for the slot */
+ if ( (sp = find_slot(slot)) == NULL)
+ return;
+
+ /* Fill in the information in the buff */
+ if (sp->cis) {
+
+ manuf = sp->cis->manuf;
+ vers = sp->cis->vers;
+ if (sp->config && sp->config->driver &&
+ sp->config->driver->name)
+ drv = sp->config->driver->name;
+ else
+ drv = "";
+ } else
+ manuf = vers = drv = "";
+
+ switch (sp->state) {
+ case empty:
+ stat = "0";
+ break;
+ case filled:
+ stat = "1";
+ break;
+ case inactive:
+ stat = "2";
+ break;
+ default:
+ stat = "9";
+ }
+ sprintf(buf, "%d~%s~%s~%s~%s", slot, manuf, vers, drv, stat);
+}
+
+static void
+cardpwr(int slot, int pwon)
+{
+ struct slot *sp;
+
+ /* Look for the slot */
+ if ( (sp = find_slot(slot)) == NULL)
+ return;
+
+ if (ioctl(sp->fd, PIOCSVIR, &pwon) < 0)
+ logerr("invaild arguments for cardpwr");
+}
+
+static int sock = 0;
+static int slen = 0;
+static struct sockaddr_un sun;
+
+void
+set_socket(int s)
+{
+ sock = s;
+}
+
+void
+stat_changed(struct slot *sp)
+{
+ int len;
+ char buf[512];
+
+ if (!slen)
+ return;
+
+ cardname(buf, sp->slot);
+ len = strlen(buf);
+ if (sendto(sock, buf, len, 0, (struct sockaddr *) & sun, slen) != len) {
+ logerr("sendto failed");
+ slen = 0;
+ }
+}
+
+void
+process_client(void)
+{
+ char buf[512], obuf[512];
+ int len;
+ int snum;
+
+ if (!sock)
+ return;
+ slen = sizeof(sun);
+ len = recvfrom(sock, buf, sizeof(buf),
+ 0, (struct sockaddr *)&sun, &slen);
+ if (len < 0)
+ logerr("recvfrom failed");
+ buf[len] = '\0';
+ obuf[0] = '\0';
+ switch (buf[0]) { /* Protocol implementation */
+ case 'S': /* How many slots? */
+ cardnum(obuf);
+ break;
+ case 'N': /* Card name request */
+ sscanf(buf + 1, "%d", &snum);
+ if (snum >= 0 && snum <= MAXSLOT)
+ cardname(obuf, snum);
+ else
+ logerr("Illegal slot requests for N command");
+ break;
+ case 'P': /* Virtual insertion request */
+ sscanf(buf + 1, "%d", &snum);
+ if (snum >= 0 && snum <= MAXSLOT) {
+ logmsg("slot %d: spring has come", snum);
+ cardpwr(snum, 1);
+ } else
+ logerr("Illegal slot requests for P command");
+ break;
+ case 'Q': /* Virtual removal request */
+ sscanf(buf + 1, "%d", &snum);
+ if (snum >= 0 && snum <= MAXSLOT) {
+ logmsg("slot %d: hibernation", snum);
+ cardpwr(snum, 0);
+ } else
+ logerr("Illegal slot requests for Q command");
+ break;
+ default:
+ logerr("Unknown control message from socket");
+ break;
+ }
+ len = strlen(obuf);
+ if (len) {
+ if (sendto(sock, obuf, len, 0, (struct sockaddr *)&sun, slen)
+ != len) {
+ logerr("sendto failed");
+ slen = 0;
+ }
+ } else if (sendto(sock, 0, 0, 0, (struct sockaddr *)&sun, slen)
+ != len) {
+ logerr("sendto failed");
+ slen = 0;
+ }
+}
diff --git a/usr.sbin/pccard/pccardd/util.c b/usr.sbin/pccard/pccardd/util.c
new file mode 100644
index 0000000..8f9a48d
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/util.c
@@ -0,0 +1,273 @@
+/*
+ * Copyright (c) 1995 Andrew McRae. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Code cleanup, bug-fix and extension
+ * by:
+ * Tatsumi Hosokawa <hosokawa@jp.FreeBSD.org>
+ * Nate Williams <nate@FreeBSD.org>
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <syslog.h>
+#ifdef SYSINSTALL
+#include <dialog.h>
+#endif
+#include "cardd.h"
+
+static int do_log = 0;
+
+void
+log_setup(void)
+{
+#ifndef SYSINSTALL
+ do_log = 1;
+ openlog("pccardd", LOG_PID, LOG_DAEMON);
+#endif
+}
+
+void
+logmsg(const char *fmt, ...)
+{
+ va_list ap;
+ char s[256];
+
+ va_start(ap, fmt);
+ vsnprintf(s, 256, fmt, ap);
+
+ if (do_log)
+ syslog(LOG_ERR, "%s", s);
+ else {
+#ifdef SYSINSTALL
+ dialog_clear();
+ msgConfirm(s);
+#else
+ warnx("%s", s);
+#endif
+ }
+}
+
+void
+logerr(char *msg)
+{
+ if (do_log)
+ syslog(LOG_ERR, "%s: %m", msg);
+ else {
+#ifdef SYSINSTALL
+ dialog_clear();
+ msgConfirm(msg);
+#else
+ warn("%s", msg);
+#endif
+ }
+}
+
+/*
+ * Deliver last will and testament, and die.
+ */
+void
+die(char *msg)
+{
+ if (do_log)
+ syslog(LOG_CRIT, "fatal error: %s", msg);
+ else {
+#ifdef SYSINSTALL
+ char s[256];
+
+ sprintf(s, "cardd fatal error: %s\n", msg);
+ dialog_clear();
+ msgConfirm(s);
+#else
+ warnx("fatal error: %s", msg);
+#endif
+ }
+ closelog();
+ exit(1);
+}
+
+void *
+xmalloc(int sz)
+{
+ void *p;
+
+ p = malloc(sz);
+ if (p)
+ bzero(p, sz);
+ else
+ die("malloc failed");
+ return (p);
+}
+
+char *
+newstr(char *p)
+{
+ char *s;
+
+ s = strdup(p);
+ if (s == 0)
+ die("strdup failed");
+ return (s);
+}
+
+/*
+ * Find contiguous bit string (all set) of at
+ * least count number.
+ */
+int
+bit_fns(bitstr_t *nm, int nbits, int min, int count, int step)
+{
+ int i, j;
+ int found = 0;
+
+ for (i = min; i < nbits; i += step)
+ for (j = i, found = 0; j < nbits; j++)
+ if (bit_test(nm, j)) {
+ if (++found == count)
+ return i;
+ } else
+ break;
+ return (-1);
+}
+
+/*
+ * Allocate a block of memory and return the address.
+ */
+unsigned long
+alloc_memory(int size)
+{
+ int i;
+
+ i = bit_fns(mem_avail, MEMBLKS, 0, size / MEMUNIT + (size % MEMUNIT != 0), 1);
+ if (i < 0)
+ return (0);
+ bit_nclear(mem_avail, i, i + size / MEMUNIT + (size % MEMUNIT != 0) - 1);
+ return (BIT2MEM(i));
+}
+
+/*
+ * reset_slot - Power has been applied to the card.
+ * Now reset the card.
+ */
+void
+reset_slot(struct slot *sp)
+{
+ char c;
+ off_t offs;
+ struct mem_desc mem;
+ struct io_desc io;
+ int rw_flags;
+
+ rw_flags = MDF_ATTR;
+ ioctl(sp->fd, PIOCRWFLAG, &rw_flags);
+#ifdef DEBUG
+ printf("Resetting card, writing 0x80 to offs 0x%x\n",
+ sp->cis->reg_addr);
+#endif
+ offs = sp->cis->reg_addr;
+ lseek(sp->fd, offs, SEEK_SET);
+ c = 0x80;
+ write(sp->fd, &c, sizeof(c));
+ usleep(10 * 1000);
+ c = 0;
+ lseek(sp->fd, offs, SEEK_SET);
+ write(sp->fd, &c, sizeof(c));
+
+ /* Reset all the memory and I/O windows. */
+ bzero((caddr_t) & mem, sizeof(mem));
+ bzero((caddr_t) & io, sizeof(io));
+ for (mem.window = 0; mem.window < NUM_MEM_WINDOWS; mem.window++)
+ ioctl(sp->fd, PIOCSMEM, &mem);
+ for (io.window = 0; io.window < NUM_IO_WINDOWS; io.window++)
+ ioctl(sp->fd, PIOCSIO, &io);
+}
+
+/*
+ * execute - Execute the command strings.
+ * For the current slot (if any) perform macro
+ * substitutions.
+ */
+void
+execute(struct cmd *cmdp, struct slot *sp)
+{
+ char cmd[1024];
+ char *p, *cp, *lp;
+
+ for (; cmdp; cmdp = cmdp->next) {
+ cp = cmd;
+ lp = cmdp->line;
+ if (*lp == 0)
+ continue;
+ while ((p = strchr(lp, '$')) != 0) {
+ /* copy over preceding string. */
+ while (lp != p)
+ *cp++ = *lp++;
+ /* stringify ethernet address and place here. */
+ if (strncmp(p, "$ether", 6) == 0) {
+ sprintf(cp, "%x:%x:%x:%x:%x:%x",
+ sp->eaddr[0],
+ sp->eaddr[1],
+ sp->eaddr[2],
+ sp->eaddr[3],
+ sp->eaddr[4],
+ sp->eaddr[5]);
+ while (*++cp)
+ continue;
+ lp += 6;
+ } else
+ /* replace device name */
+ if (strncmp(p, "$device", 7) == 0) {
+ sprintf(cp, "%s%d",
+ sp->config->driver->kernel,
+ sp->config->driver->unit);
+ while (*cp)
+ cp++;
+ lp += 7;
+ } else
+ /* Copy the `$' and rescan. */
+ *cp++ = *lp++;
+ }
+ /* No more replacements. Copy rest of string. */
+ while ((*cp++ = *lp++) != 0)
+ continue;
+#ifdef DEBUG
+ fprintf(stderr, "Executing [%s]\n", cmd);
+#endif
+ system(cmd);
+ }
+}
diff --git a/usr.sbin/pciconf/Makefile b/usr.sbin/pciconf/Makefile
new file mode 100644
index 0000000..d892a12
--- /dev/null
+++ b/usr.sbin/pciconf/Makefile
@@ -0,0 +1,9 @@
+# $ANA: Makefile,v 1.1.1.1 1996/09/25 21:12:57 wollman Exp $
+# $FreeBSD$
+
+PROG= pciconf
+MAN= pciconf.8
+
+CFLAGS+= -I${.CURDIR}/../../sys
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pciconf/pathnames.h b/usr.sbin/pciconf/pathnames.h
new file mode 100644
index 0000000..719615a
--- /dev/null
+++ b/usr.sbin/pciconf/pathnames.h
@@ -0,0 +1,3 @@
+/* $FreeBSD$ */
+#define _PATH_DEVPCI "/dev/pci"
+#define _PATH_PCIVDB "/usr/share/misc/pci_vendors"
diff --git a/usr.sbin/pciconf/pciconf.8 b/usr.sbin/pciconf/pciconf.8
new file mode 100644
index 0000000..a632f7c
--- /dev/null
+++ b/usr.sbin/pciconf/pciconf.8
@@ -0,0 +1,214 @@
+.\" $FreeBSD$
+.\" 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
+.Sh NAME
+.Nm pciconf
+.Nd diagnostic utility for the PCI bus
+.Sh SYNOPSIS
+.Nm
+.Fl l Op Fl v
+.Nm
+.Fl a Ar sel
+.Nm
+.Fl r Oo Fl b | h Oc Ar sel addr Ns Op : Ns Ar addr
+.Nm
+.Fl w Oo Fl b | h Oc Ar sel addr Op Ar 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
+If the
+.Fl v
+option is supplied,
+.Nm
+will attempt to load the vendor/device information database, and print
+vendor, device, class and subclass identification strings for each device.
+.Pp
+The first column gives the
+device name, unit number, and
+.Ar selector .
+If there is no device configured in the kernel for the
+.Tn PCI
+device in question, the device name will be
+.Dq none .
+Unit numbers for unconfigured devices start at zero and are incremented for
+each unconfigured device that is encountered. The
+.Ar selector
+is in a form which may directly be used for the other forms of the command.
+The second column is the class code, with the class byte printed as two
+hex digits, followed by the sub-class and the interface bytes.
+The third column gives the contents of the subvendorid register, introduced
+in revision 2.1 of the
+.Tn PCI
+standard.
+It is 0 for most current (2.0)
+.Tn PCI
+cards, but is supposed to be loaded with a unique card identification code
+in newly developed
+.Tn PCI
+cards.
+The field consists of the card ID in the upper
+half and the card vendor ID in the lower half of the value.
+.Pp
+The fourth column contains the chip device ID, which identifies the chip
+this card is based on.
+It consists of two fields, identifying the chip and
+its vendor, as above.
+The fifth column prints the chip's revision.
+The sixth column describes the header type.
+Currently assigned header types are 0 for all devices except
+.Tn PCI
+to
+.Tn PCI
+bridges, and 1 for such bridge chips.
+If the most significant bit
+of the header type register is set for
+function 0 of a
+.Tn PCI
+device, it is a
+.Em multi-function
+device, which contains several (similar or independent) functions on
+one chip.
+.Pp
+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 optional second
+.Ar reg2
+specifies a range to read.
+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 ENVIRONMENT
+The PCI vendor/device information database is normally read from
+.Pa /usr/share/misc/pci_vendors .
+This path can be overridden by setting the environment variable
+.Ev PCICONF_VENDOR_DATABASE .
+.Sh SEE ALSO
+.Xr ioctl 2 ,
+.\" .Xr pci 4 ,
+.Xr kldload 8
+.Sh HISTORY
+The
+.Nm
+command appeared first in
+.Fx 2.2 .
+The
+.Fl a
+option was added for
+.Tn PCI
+KLD support in
+.Fx 3.0 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+facility was written by
+.An Stefan Esser
+and
+.An Garrett Wollman .
+.Sh BUGS
+The
+.Fl b
+and
+.Fl h
+options are implemented in
+.Nm ,
+but not in the underlying
+.Xr ioctl 2 .
+.Pp
+It might be useful to give non-root users access to the
+.Fl a
+and
+.Fl r
+options.
+But only root will be able to execute a
+.Nm kldload
+to provide the device with a driver KLD, and reading of configuration space
+registers may cause a failure in badly designed
+.Tn PCI
+chips.
diff --git a/usr.sbin/pciconf/pciconf.c b/usr.sbin/pciconf/pciconf.c
new file mode 100644
index 0000000..6a18b6d
--- /dev/null
+++ b/usr.sbin/pciconf/pciconf.c
@@ -0,0 +1,536 @@
+/*
+ * Copyright 1996 Massachusetts Institute of Technology
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that both the above copyright notice and this
+ * permission notice appear in all copies, that both the above
+ * copyright notice and this permission notice appear in all
+ * supporting documentation, and that the name of M.I.T. not be used
+ * in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission. M.I.T. makes
+ * no representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
+ * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/fcntl.h>
+
+#include <err.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/pciio.h>
+#include <sys/queue.h>
+
+#include <dev/pci/pcireg.h>
+
+#include "pathnames.h"
+
+struct pci_device_info
+{
+ TAILQ_ENTRY(pci_device_info) link;
+ int id;
+ char *desc;
+};
+
+struct pci_vendor_info
+{
+ TAILQ_ENTRY(pci_vendor_info) link;
+ TAILQ_HEAD(,pci_device_info) devs;
+ int id;
+ char *desc;
+};
+
+TAILQ_HEAD(,pci_vendor_info) pci_vendors;
+
+static void list_devs(int vendors);
+static void list_verbose(struct pci_conf *p);
+static char *guess_class(struct pci_conf *p);
+static char *guess_subclass(struct pci_conf *p);
+static int load_vendors(void);
+static void readit(const char *, const char *, int);
+static void writeit(const char *, const char *, const char *, int);
+static void chkattached(const char *, int);
+
+static int exitstatus = 0;
+
+static void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n%s\n%s\n",
+ "usage: pciconf -l [-v]",
+ " pciconf -a sel",
+ " pciconf -r [-b | -h] sel addr[:addr]",
+ " pciconf -w [-b | -h] sel addr [value]");
+ exit (1);
+}
+
+int
+main(int argc, char **argv)
+{
+ int c;
+ int listmode, readmode, writemode, attachedmode, verbose;
+ int byte, isshort;
+
+ listmode = readmode = writemode = attachedmode = verbose = byte = isshort = 0;
+
+ while ((c = getopt(argc, argv, "alrwbhv")) != -1) {
+ switch(c) {
+ case 'a':
+ attachedmode = 1;
+ break;
+
+ case 'l':
+ listmode = 1;
+ break;
+
+ case 'r':
+ readmode = 1;
+ break;
+
+ case 'w':
+ writemode = 1;
+ break;
+
+ case 'b':
+ byte = 1;
+ break;
+
+ case 'h':
+ isshort = 1;
+ break;
+
+ case 'v':
+ verbose = 1;
+ break;
+
+ default:
+ usage();
+ }
+ }
+
+ if ((listmode && optind != argc)
+ || (writemode && optind + 3 != argc)
+ || (readmode && optind + 2 != argc)
+ || (attachedmode && optind + 1 != argc))
+ usage();
+
+ if (listmode) {
+ list_devs(verbose);
+ } else if (attachedmode) {
+ chkattached(argv[optind],
+ byte ? 1 : isshort ? 2 : 4);
+ } else if (readmode) {
+ readit(argv[optind], argv[optind + 1],
+ byte ? 1 : isshort ? 2 : 4);
+ } else if (writemode) {
+ writeit(argv[optind], argv[optind + 1], argv[optind + 2],
+ byte ? 1 : isshort ? 2 : 4);
+ } else {
+ usage();
+ }
+
+ return exitstatus;
+}
+
+static void
+list_devs(int verbose)
+{
+ int fd;
+ struct pci_conf_io pc;
+ struct pci_conf conf[255], *p;
+ int none_count = 0;
+
+ if (verbose)
+ load_vendors();
+
+ fd = open(_PATH_DEVPCI, O_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%08x "
+ "chip=0x%08x rev=0x%02x hdr=0x%02x\n",
+ (p->pd_name && *p->pd_name) ? p->pd_name :
+ "none",
+ (p->pd_name && *p->pd_name) ? (int)p->pd_unit :
+ none_count++,
+ p->pc_sel.pc_bus, p->pc_sel.pc_dev,
+ p->pc_sel.pc_func, (p->pc_class << 16) |
+ (p->pc_subclass << 8) | p->pc_progif,
+ (p->pc_subdevice << 16) | p->pc_subvendor,
+ (p->pc_device << 16) | p->pc_vendor,
+ p->pc_revid, p->pc_hdr);
+ if (verbose)
+ list_verbose(p);
+ }
+ } while (pc.status == PCI_GETCONF_MORE_DEVS);
+
+ close(fd);
+}
+
+static void
+list_verbose(struct pci_conf *p)
+{
+ struct pci_vendor_info *vi;
+ struct pci_device_info *di;
+ char *dp;
+
+ TAILQ_FOREACH(vi, &pci_vendors, link) {
+ if (vi->id == p->pc_vendor) {
+ printf(" vendor = '%s'\n", vi->desc);
+ break;
+ }
+ }
+ if (vi == NULL) {
+ di = NULL;
+ } else {
+ TAILQ_FOREACH(di, &vi->devs, link) {
+ if (di->id == p->pc_device) {
+ printf(" device = '%s'\n", di->desc);
+ break;
+ }
+ }
+ }
+ if ((dp = guess_class(p)) != NULL)
+ printf(" class = %s\n", dp);
+ if ((dp = guess_subclass(p)) != NULL)
+ printf(" subclass = %s\n", dp);
+}
+
+/*
+ * This is a direct cut-and-paste from the table in sys/dev/pci/pci.c.
+ */
+static struct
+{
+ int class;
+ int subclass;
+ char *desc;
+} pci_nomatch_tab[] = {
+ {PCIC_OLD, -1, "old"},
+ {PCIC_OLD, PCIS_OLD_NONVGA, "non-VGA display device"},
+ {PCIC_OLD, PCIS_OLD_VGA, "VGA-compatible display device"},
+ {PCIC_STORAGE, -1, "mass storage"},
+ {PCIC_STORAGE, PCIS_STORAGE_SCSI, "SCSI"},
+ {PCIC_STORAGE, PCIS_STORAGE_IDE, "ATA"},
+ {PCIC_STORAGE, PCIS_STORAGE_FLOPPY, "floppy disk"},
+ {PCIC_STORAGE, PCIS_STORAGE_IPI, "IPI"},
+ {PCIC_STORAGE, PCIS_STORAGE_RAID, "RAID"},
+ {PCIC_NETWORK, -1, "network"},
+ {PCIC_NETWORK, PCIS_NETWORK_ETHERNET, "ethernet"},
+ {PCIC_NETWORK, PCIS_NETWORK_TOKENRING, "token ring"},
+ {PCIC_NETWORK, PCIS_NETWORK_FDDI, "fddi"},
+ {PCIC_NETWORK, PCIS_NETWORK_ATM, "ATM"},
+ {PCIC_DISPLAY, -1, "display"},
+ {PCIC_DISPLAY, PCIS_DISPLAY_VGA, "VGA"},
+ {PCIC_DISPLAY, PCIS_DISPLAY_XGA, "XGA"},
+ {PCIC_MULTIMEDIA, -1, "multimedia"},
+ {PCIC_MULTIMEDIA, PCIS_MULTIMEDIA_VIDEO, "video"},
+ {PCIC_MULTIMEDIA, PCIS_MULTIMEDIA_AUDIO, "audio"},
+ {PCIC_MEMORY, -1, "memory"},
+ {PCIC_MEMORY, PCIS_MEMORY_RAM, "RAM"},
+ {PCIC_MEMORY, PCIS_MEMORY_FLASH, "flash"},
+ {PCIC_BRIDGE, -1, "bridge"},
+ {PCIC_BRIDGE, PCIS_BRIDGE_HOST, "HOST-PCI"},
+ {PCIC_BRIDGE, PCIS_BRIDGE_ISA, "PCI-ISA"},
+ {PCIC_BRIDGE, PCIS_BRIDGE_EISA, "PCI-EISA"},
+ {PCIC_BRIDGE, PCIS_BRIDGE_MCA, "PCI-MCA"},
+ {PCIC_BRIDGE, PCIS_BRIDGE_PCI, "PCI-PCI"},
+ {PCIC_BRIDGE, PCIS_BRIDGE_PCMCIA, "PCI-PCMCIA"},
+ {PCIC_BRIDGE, PCIS_BRIDGE_NUBUS, "PCI-NuBus"},
+ {PCIC_BRIDGE, PCIS_BRIDGE_CARDBUS, "PCI-CardBus"},
+ {PCIC_BRIDGE, PCIS_BRIDGE_OTHER, "PCI-unknown"},
+ {PCIC_SIMPLECOMM, -1, "simple comms"},
+ {PCIC_SIMPLECOMM, PCIS_SIMPLECOMM_UART, "UART"}, /* could detect 16550 */
+ {PCIC_SIMPLECOMM, PCIS_SIMPLECOMM_PAR, "parallel port"},
+ {PCIC_BASEPERIPH, -1, "base peripheral"},
+ {PCIC_BASEPERIPH, PCIS_BASEPERIPH_PIC, "interrupt controller"},
+ {PCIC_BASEPERIPH, PCIS_BASEPERIPH_DMA, "DMA controller"},
+ {PCIC_BASEPERIPH, PCIS_BASEPERIPH_TIMER, "timer"},
+ {PCIC_BASEPERIPH, PCIS_BASEPERIPH_RTC, "realtime clock"},
+ {PCIC_INPUTDEV, -1, "input device"},
+ {PCIC_INPUTDEV, PCIS_INPUTDEV_KEYBOARD, "keyboard"},
+ {PCIC_INPUTDEV, PCIS_INPUTDEV_DIGITIZER,"digitizer"},
+ {PCIC_INPUTDEV, PCIS_INPUTDEV_MOUSE, "mouse"},
+ {PCIC_DOCKING, -1, "docking station"},
+ {PCIC_PROCESSOR, -1, "processor"},
+ {PCIC_SERIALBUS, -1, "serial bus"},
+ {PCIC_SERIALBUS, PCIS_SERIALBUS_FW, "FireWire"},
+ {PCIC_SERIALBUS, PCIS_SERIALBUS_ACCESS, "AccessBus"},
+ {PCIC_SERIALBUS, PCIS_SERIALBUS_SSA, "SSA"},
+ {PCIC_SERIALBUS, PCIS_SERIALBUS_USB, "USB"},
+ {PCIC_SERIALBUS, PCIS_SERIALBUS_FC, "Fibre Channel"},
+ {PCIC_SERIALBUS, PCIS_SERIALBUS_SMBUS, "SMBus"},
+ {0, 0, NULL}
+};
+
+static char *
+guess_class(struct pci_conf *p)
+{
+ int i;
+
+ for (i = 0; pci_nomatch_tab[i].desc != NULL; i++) {
+ if (pci_nomatch_tab[i].class == p->pc_class)
+ return(pci_nomatch_tab[i].desc);
+ }
+ return(NULL);
+}
+
+static char *
+guess_subclass(struct pci_conf *p)
+{
+ int i;
+
+ for (i = 0; pci_nomatch_tab[i].desc != NULL; i++) {
+ if ((pci_nomatch_tab[i].class == p->pc_class) &&
+ (pci_nomatch_tab[i].subclass == p->pc_subclass))
+ return(pci_nomatch_tab[i].desc);
+ }
+ return(NULL);
+}
+
+static int
+load_vendors(void)
+{
+ char *dbf;
+ FILE *db;
+ struct pci_vendor_info *cv;
+ struct pci_device_info *cd;
+ char buf[100], str[100];
+ int id, error;
+
+ /*
+ * Locate the database and initialise.
+ */
+ TAILQ_INIT(&pci_vendors);
+ if ((dbf = getenv("PCICONF_VENDOR_DATABASE")) == NULL)
+ dbf = _PATH_PCIVDB;
+ if ((db = fopen(dbf, "r")) == NULL)
+ return(1);
+ cv = NULL;
+ cd = NULL;
+ error = 0;
+
+ /*
+ * Scan input lines from the database
+ */
+ for (;;) {
+ if (fgets(buf, sizeof(buf), db) == NULL)
+ break;
+
+ /* Check for vendor entry */
+ if ((buf[0] != '\t') && (sscanf(buf, "%04x\t%[^\n]", &id, str) == 2)) {
+ if ((id == 0) || (strlen(str) < 1))
+ continue;
+ if ((cv = malloc(sizeof(struct pci_vendor_info))) == NULL) {
+ warn("allocating vendor entry");
+ error = 1;
+ break;
+ }
+ if ((cv->desc = strdup(str)) == NULL) {
+ free(cv);
+ warn("allocating vendor description");
+ error = 1;
+ break;
+ }
+ cv->id = id;
+ TAILQ_INIT(&cv->devs);
+ TAILQ_INSERT_TAIL(&pci_vendors, cv, link);
+ continue;
+ }
+
+ /* Check for device entry */
+ if ((buf[0] == '\t') && (sscanf(buf + 1, "%04x\t%[^\n]", &id, str) == 2)) {
+ if ((id == 0) || (strlen(str) < 1))
+ continue;
+ if (cv == NULL) {
+ warnx("device entry with no vendor!");
+ continue;
+ }
+ if ((cd = malloc(sizeof(struct pci_device_info))) == NULL) {
+ warn("allocating device entry");
+ error = 1;
+ break;
+ }
+ if ((cd->desc = strdup(str)) == NULL) {
+ free(cd);
+ warn("allocating device description");
+ error = 1;
+ break;
+ }
+ cd->id = id;
+ TAILQ_INSERT_TAIL(&cv->devs, cd, link);
+ continue;
+ }
+
+ /* It's a comment or junk, ignore it */
+ }
+ if (ferror(db))
+ error = 1;
+ fclose(db);
+
+ return(error);
+}
+
+
+static struct pcisel
+getsel(const char *str)
+{
+ char *ep = (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
+readone(int fd, struct pcisel *sel, long reg, int width)
+{
+ struct pci_io pi;
+
+ pi.pi_sel = *sel;
+ pi.pi_reg = reg;
+ pi.pi_width = width;
+
+ if (ioctl(fd, PCIOCREAD, &pi) < 0)
+ err(1, "ioctl(PCIOCREAD)");
+
+ printf("%0*x", width*2, pi.pi_data);
+}
+
+static void
+readit(const char *name, const char *reg, int width)
+{
+ long rstart;
+ long rend;
+ long r;
+ char *end;
+ int i;
+ int fd;
+ struct pcisel sel;
+
+ fd = open(_PATH_DEVPCI, O_RDWR, 0);
+ if (fd < 0)
+ err(1, "%s", _PATH_DEVPCI);
+
+ rend = rstart = strtol(reg, &end, 0);
+ if (end && *end == ':') {
+ end++;
+ rend = strtol(end, (char **) 0, 0);
+ }
+ sel = getsel(name);
+ for (i = 1, r = rstart; r <= rend; i++, r += width) {
+ readone(fd, &sel, r, width);
+ if (i && !(i % 8)) putchar(' ');
+ putchar(i % (16/width) ? ' ' : '\n');
+ }
+ if (i % (16/width) != 1)
+ putchar('\n');
+ close(fd);
+}
+
+static void
+writeit(const char *name, const char *reg, const char *data, int width)
+{
+ int fd;
+ struct pci_io pi;
+
+ pi.pi_sel = getsel(name);
+ pi.pi_reg = strtoul(reg, (char **)0, 0); /* XXX error check */
+ pi.pi_width = width;
+ pi.pi_data = strtoul(data, (char **)0, 0); /* XXX error check */
+
+ fd = open(_PATH_DEVPCI, O_RDWR, 0);
+ if (fd < 0)
+ err(1, "%s", _PATH_DEVPCI);
+
+ if (ioctl(fd, PCIOCWRITE, &pi) < 0)
+ err(1, "ioctl(PCIOCWRITE)");
+}
+
+static void
+chkattached (const char *name, int width)
+{
+ int fd;
+ struct pci_io pi;
+
+ pi.pi_sel = getsel(name);
+ pi.pi_reg = 0;
+ pi.pi_width = width;
+ pi.pi_data = 0;
+
+ fd = open(_PATH_DEVPCI, O_RDWR, 0);
+ if (fd < 0)
+ err(1, "%s", _PATH_DEVPCI);
+
+ if (ioctl(fd, PCIOCATTACHED, &pi) < 0)
+ err(1, "ioctl(PCIOCATTACHED)");
+
+ exitstatus = pi.pi_data ? 0 : 2; /* exit(2), if NOT attached */
+ printf("%s: %s%s\n", name, pi.pi_data == 0 ? "not " : "", "attached");
+}
diff --git a/usr.sbin/pcvt/Makefile b/usr.sbin/pcvt/Makefile
new file mode 100644
index 0000000..0dbdf05
--- /dev/null
+++ b/usr.sbin/pcvt/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+SUBDIR= keycap cursor fontedit fonts kcon loadfont scon \
+ userkeys vttest ispcvt
+SUBDIR+= vgaio kbdio
+SUBDIR+= demo
+SUBDIR+= Misc
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/pcvt/Makefile.inc b/usr.sbin/pcvt/Makefile.inc
new file mode 100644
index 0000000..7899d9c
--- /dev/null
+++ b/usr.sbin/pcvt/Makefile.inc
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+FONTDIR = /usr/share/misc/pcvtfonts
+
+.include "../Makefile.inc"
diff --git a/usr.sbin/pcvt/Misc/Doc/Acknowledgements b/usr.sbin/pcvt/Misc/Doc/Acknowledgements
new file mode 100644
index 0000000..327f23d
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Doc/Acknowledgements
@@ -0,0 +1,111 @@
+Thank You, (in the order of appearance)
+
+
+ Lynne and Bill Jolitz
+
+ for your work on 386BSD and making all this possible !
+
+
+ Holger Veit (veit@du9ds3.uni-duisburg.de)
+
+ for the permission to use a modified version of his keyboard
+ driver and utilities for keyboard remapping / multinational
+ keyboard support.
+
+
+ Per Lindberg
+
+ for the extremely helpful vt100 terminal testprogram found
+ in the directory vttest.
+
+
+ John Birchfield
+
+ for the ncsa telnet package, which contains a vt100 emulation
+ and which was very helpful in studying some concepts.
+
+
+ Ralf Friedl (friedl@informatik.uni-kl.de)
+
+ for making his implementation of multi-sceens for the net-2
+ distribution available. i looked at this and took some ideas
+ and lines from his code.
+
+
+ Bruce Evans (bde@runx.oz.au)
+
+ for contributing some bugfixes and a complete termcap entry
+
+
+ Brian H. Dunford-Shore (brian@morpheus.wustl.edu)
+
+ for contributing most of the EGA/VGA screen switching code
+ and being a fast, reliable and responsive co-author. This
+ driver would not be what it is without Brian, Thank You !
+
+
+ Frank da Cruz (fdc@columbia.edu)
+
+ for my famous datacomm program and for giving the permission
+ to redistribute files from the msdos kermit distribution
+ located in the directory support/demo.
+
+
+ Joerg Wunsch (joerg_wunsch@uriah.sax.de)
+
+ for contributing precise bugreports and -fixes, the 8x10
+ EGA/VGA fonts and the color palette ioctls and for being
+ a very responsive contributor of various ideas.
+ Joerg wrote pcvt's interface to XFree86 1.2 and 1.3 and
+ the SYSV/syscons - like interface to XFree86 2.0.
+ There would be no support for X without Joerg's work!
+ Thank you very much Joerg, i enjoy it !!! :-)
+
+
+ Scott Turner (scotty@gagetalker.com)
+
+ for contributing code to change the winsize structure, many
+ discussions on the keyboard code and fine-tuning the driver
+
+
+ Gordon L. Burditt (gordon@sneaky.lonestar.org)
+
+ for the nicest and most complete bugreports i ever got
+
+
+ Theo Deraadt (deraadt@fsa.ca)
+
+ for pushing me forward. There would be no 3.00 release
+ if Theo didn't asked for features ... ;-)
+
+
+ Onno van der Linden (c/o frank@fwi.uva.nl)
+
+ for writing the 132 column support for the Cirrus
+ chipsets although he had no time to do it ... :-)
+
+
+ Wolfram Solfrank, Ingo Koenig
+
+ for putting some data onto tape (and handling and shipping
+ in the case of Wolfgang) to provide me with some latest
+ sources because i still have no ip-connectivity ....
+
+
+ Michael Havemester (tik@abqhh.hanse.de)
+
+ for giving me a chance to stay up to date with NetBSD-
+ current, for programming the initial version of the fast
+ scrolling code and for the keyboard fifo code!
+
+
+ Charles Hannum (mycroft@gnu.ai.mit.edu)
+
+ for getting bored by a slow-scrolling video driver and
+ for leaving me some bugs to fix ;-)
+
+
+ The NetBSD and FreeBSD teams
+
+ for giving me something to play, work and learn with !
+ There would be nothing to write a driver for without you !!!
diff --git a/usr.sbin/pcvt/Misc/Doc/Bibliography b/usr.sbin/pcvt/Misc/Doc/Bibliography
new file mode 100644
index 0000000..2cce039
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Doc/Bibliography
@@ -0,0 +1,189 @@
+
+VGA BOOKS
+---------
+
+ Richard Wilton, "Programmers Guide to PC & PS/2 Video Systems",
+ Microsoft Press 1987
+
+
+ Richard F. Ferraro, "Programmers Guide to the EGA and VGA Cards",
+ Second Edition, Addison-Wesley 1990
+
+
+ Richard F. Ferraro, "Programmers Guide to the EGA and VGA Cards",
+ Third Edition, Addison-Wesley 1994
+
+
+ Matthias Uphoff, "Die Programmierung der EGA/VGA Grafikkarte",
+ Addison Wesley 1992
+
+
+ Bradley Dyck Kliewer, "EGA/VGA A Programmers Reference Guide",
+ McGraw Hill, 2nd Edition 1990
+
+
+UNIX AND UNIX DEVICE DRIVERS
+----------------------------
+
+ Bell Telephone Laboratories, Inc. "UNIX Programmer's Manual, Seventh
+ Edition, Volume 2". Revised and Expanded Version.
+ Holt, Rinehart and Winston 1983
+
+
+ George Pajari, "Writing Unix Device Drivers"
+ Addison Wesley 1992
+
+
+ Janet I. Egan and Thomas J. Teixeira, "Writing a UNIX Device Driver"
+ John Wiley & Sons 1988
+
+
+ Janet I. Egan and Thomas J. Teixeira, "Writing a UNIX Device Driver"
+ Second Edition. John Wiley & Sons 1992
+
+
+ Leffler, McKusick, Karels, Quarterman, "The Design and Implementation
+ of the 4.3BSD UNIX Operating System"
+ Addison Wesley 1988, corrected Reprint 1989
+
+
+ Leffler, McKusick, "The Design and Implementation of the 4.3BSD UNIX
+ Operating System, Answer Book"
+ Addison Wesley 1991
+
+
+ Maurice J. Bach, "The Design of the UNIX Operating System"
+ Prentice-Hall 1986
+
+
+ Sun Microsystems Inc., "Writing Device Drivers"
+ Part No. 800-3851-10, Revision A of 27 March 1990
+
+
+ Hewlett-Packard Company, "HP-UX Driver Development Guide",
+ Part No. 98577-90013, First Edition 07/91
+
+
+ W. Richard Stevens, "Advanced Programming in the UNIX Environment",
+ Addison Wesley 1992
+
+
+ Phillip M. Adams, Clovis L. Tondo, "Writing Unix Device Drivers in C",
+ Prentice Hall 1993
+
+
+ Berny Goodheart, James Cox, "The Magic Garden Explained",
+ Prentice Hall 1994
+
+
+ Peter Kettle, Steve Statler, "Writing Device Drivers for SCO Unix"
+ Addison Wesley 1993
+
+
+TERMINAL MANUALS
+----------------
+
+ Digital Equipment, VT100 Users Manual, 2nd ed. 1979
+ Digital Equipment, VT132 Users Manual
+ Digital Equipment, VT220 Programmers Reference Manual, 2nd ed. 1984
+ Digital Equipment Corporation
+
+
+ Hewlett Packard, 700/92 Technical Reference Manual (ANSI Operation)
+ Hewlett Packard, 2392a Users Manual (ANSI Operation)
+ Hewlett-Packard Company
+
+
+ Walker Richer & Quinn, Inc., "Reflection 2 and Reflection 4 Technical
+ Reference Manual", Version 4.2
+ Walker, Richer & Quinn, Seattle, August 1992
+
+
+IBM PC HARDWARE/FIRMWARE
+------------------------
+
+ Frank van Gilluwe, "The Undocumented PC",
+ Addison Wesley, First Edition May 1994
+
+
+ IBM Corporation, "Technical Reference Personal Computer AT",
+ Part No. 6280070, Form No. S229-9611-00, First Edition 1985
+
+
+ Phoenix Technologies Ltd., "System BIOS for IBM PC/XT/AT Computers
+ and Compatibles", Addison Wesley, Fourth Printing June 1990
+
+
+ Phoenix Technologies Ltd., "System BIOS for IBM PCs, Compatibles,
+ and EISA Computers", Second Edition
+ Addison Wesley, First Printing May 1991
+
+
+ American Megatrends, Inc., "Hi-Flex ISA and EISA AMIBIOS Technical
+ Reference", American Megatrends, Inc. 1992 (9/25/92)
+
+
+ Thom Hogan, "The Programmers PC Sourcebook", 2nd Edition
+ Microsoft Press, 1991
+
+
+TERMCAP/TERMINFO
+----------------
+
+ John Strang, Linda Mui and Tim O'Reilly, "Termcap and Terminfo",
+ O'Reilly & Associates, Inc. , April 1991
+
+
+ Richard M. Stallman, "Termcap - The Termcap Library and Data Base",
+ Free Software Foundation, Second Edition November 1988
+
+
+
+DATABOOKS/DATASHEETS
+--------------------
+
+ Intel Corporation, "Microsystem Components Handbook Volume II",
+ Intel Corporation, 1984
+
+
+ Intel Corporation, "Peripheral Design Handbook",
+ Intel Corporation, 1980
+
+
+ Tseng Labs, Inc. "ET4000 Graphics Controller Data Book", 1990
+
+
+ Western Digital Corporation, "WD90C11, WD90C11A (PVGA1C) Enhanced
+ VGA Controller", Western Digital 9/18/91
+
+
+ Western Digital Corporation, "WD90C00 Enhanced VGA Controller",
+ Western Digital 1/14/91
+
+
+ Western Digital Corporation, "WD90C00 Interface Guide",
+ Western Digital 1/10/91
+
+
+ Western Digital Corporation, "VGA Register Based Programmers Manual",
+ Western Digital 1/30/91
+
+
+ Western Digital Corporation, "VGA BIOS Programmers Manual",
+ Western Digital 1/10/91
+
+
+ Cirrus Logic, "True Color VGA Family - CL-GD542X", Technical Ref.
+ Manual, Cirrus Logic, Inc. April 1993
+
+
+ S3 Inc, "86C801/86C805 GUI Accelerators"
+ S3 Incorporated, September 1992
+
+
+ S3 Inc, "86C928 GUI Accelerator"
+ S3 Incorporated, July 1993
+
+
+ Trident Microsystems Inc., "TVGA9000i Technical Reference Manual",
+ Trident Microsystems, Inc. (c) 1992, March 1993, Rev 1
diff --git a/usr.sbin/pcvt/Misc/Doc/CharGen b/usr.sbin/pcvt/Misc/Doc/CharGen
new file mode 100644
index 0000000..c047bfa
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Doc/CharGen
@@ -0,0 +1,149 @@
+Character Generator description (before it gets lost ..)
+--------------------------------------------------------------------------------
+
+The lower character generator is the default IBM character set II.
+
+The description of the higher character generator follows below. The
+character names are taken from the "Postscript Language Reference
+Manual", 2nd Edition, Fourth printing July 1991, pp 596.
+
+NOTE: The hex values here are NOT the values the character is identified
+ by in the emulator. The "physical" (vt220 character) to "logical"
+ (this table, character generator) conversion is done in the character
+ output routine by using the tables from file pcvt_tbl.h.
+
+The order of the characters is not regular and was largely influenced by
+the status of my brain while pixel-placing characters ....
+
+HEX What
+--- -------------------------------------------------
+00 Control code display for
+. control characters in the
+1f range 0x00 - 0x1f
+
+20 Control code display for
+. control characters in the
+3f range 0x80 - 0x9f
+
+HEX What
+--- -------------------------------------------------
+40 rho
+41 psi
+42 partialdiff
+43 lambda
+44 iota
+45 eta
+46 epsilon
+47 chi
+48 logicaland
+49 logicalor
+4a union
+4b propersuperset
+4c propersubset
+4d gamma
+4e Xi
+4f Psi
+
+HEX What
+--- -------------------------------------------------
+50 Pi
+51 arrowdblright
+52 arrowdblboth
+53 Lambda
+54 Theta
+55 congruent
+56 gradient
+57 Delta
+58 proportional
+59 therefore
+5a integral
+5b fraction
+5c (inverted fraction ????)
+5d angle
+5e (inverted angle ????)
+5f braceleftmid
+
+HEX What
+--- -------------------------------------------------
+60 bracerightmid
+61 bracelefttp
+62 braceleftbt
+63 bracerighttp
+64 bracerightbt
+65 radical
+66 omega
+67 (Yen ??)
+68 xi
+69 yacute
+6a thorn
+6b eth
+6c Thorn
+6d Yacute
+6e multiply
+6f Eth
+
+HEX What
+--- -------------------------------------------------
+70 threequarters
+71 Cedillasmall
+72 Acutesmall
+73 emdash
+74 registered
+75 endash
+76 logicalnot
+77 dieresis
+78 notequal
+79 scan 9
+7a scan 7
+7b scan 5
+7c scan 3
+7d scan 1
+7e upsilon
+7f emptyset
+
+HEX What
+--- -------------------------------------------------
+80 oe
+81 Otilde
+82 atilde
+83 Ydieresis
+84 Ucircumflex
+85 Uacute
+86 Ugrave
+87 Oslash
+88 OE
+89 Otilde
+8a Ocircumflex
+8b Oacute
+8c Ograve
+8d Idieresis
+8e Icircumflex
+8f Iacute
+
+HEX What
+--- -------------------------------------------------
+90 Igrave
+91 Edieresis
+92 Ecircumflex
+93 Egrave
+94 Atilde
+95 Acircumflex
+96 Aacute
+97 Agrave
+98 onesuperior
+99 (small black rectangle)
+9a zeta
+9b threesuperior
+9c copyright
+9d currency
+9e kappa
+9f (inverted question mark)
+
+HEX What
+--- -------------------------------------------------
+a0 the remaining positions
+. are used for the vt220
+ff downloadable characterset
+
+-------------------------------------------------------------------------------
+ (phoooo ..)
diff --git a/usr.sbin/pcvt/Misc/Doc/Charsets b/usr.sbin/pcvt/Misc/Doc/Charsets
new file mode 100644
index 0000000..b30dd8a
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Doc/Charsets
@@ -0,0 +1,99 @@
+
+I. Character Set Selection on VT220 Terminals
+==================================================
+
+
+name C0 GL C1 GR
+ +---+ +-------------------+ +---+ +-------------------+
+ |00h| | | |80h| | |
+range | | | | 20h .. 7fh | | | | | a0h .. ffh |
+ |1fh| | | |9fh| | |
+ +---+ +-------------------+ +---+ +-------------------+
+length 32 96 32 96
+
+ /\
+SECOND /||\ "lock"- or "single"-shift one set of G0, G1, G2 or
+STEP || G3 into one of the "displayable" charactersets GL
+ || or GR. (escape) sequences are: SI, SO, ESC ~, ESC n,
+ || ESC }, ESC o, ESC |, ESC N and ESC O.
+
+ +----+ +----+ +----+ +----+
+name | G0 | | G1 | | G2 | | G3 |
+length |(96)| |(96)| |(96)| |(96)|
+ +----+ +----+ +----+ +----+
+
+ /\
+ /||\ designate a hard or a soft character set as
+ || one of G0, G1, G2 or G3, used escape sequences
+FIRST || are, ESC ( X, ESC ) X, ESC * X and ESC + X - where X is
+STEP || B for ascii, < for supplemental, 0 for special, A for
+ || british, 4 for dutch, C and 5 for finnish etc. etc. ...
+ ||
+
+ +-----+ +------------+ +--------+ +-----------+ +------------+
+name |ascii| |supplemental| |special | | national | |downloadable|
+ | | | graphics | |graphics| |replacement| |characterset|
+length | (96)| | (96)| | (96)| | (96)| | (96)|
+ +-----+ +------------+ +--------+ +-----------+ +------------+
+
+ \-------\ /----------/ norway/danish
+ | dutch
+ together, this is also finnish
+ referred to as the french
+ multinational character french canada
+ set (power on default german
+ on a dec vt220) italian
+ spanish
+ swedish
+ swiss
+
+
+II. Emulating Character Set Selection
+=========================================
+
+MDA/HCG/CGA:
+
+ just a partial emulation is done, because these boards don't allow
+ downloadable charactersets. some characters simply don't display
+ because they are not in the characterset roms on the video board.
+
+ if you want to change the mapping, have a look at the default tables
+ in pcvt_tbl.h.
+
+EGA/VGA:
+
+ these cards have provisions for downloadable charactersets and so
+ many vt220/vt320 charactersets are fully supported:
+
+ - US Ascii
+ - DEC Supplemental
+ - DEC Special Graphic
+ - DEC Technical
+ - ISO Latin-1
+ - Downloadable
+
+ when the emulator is started, it behaves initially similar to
+ the MDA/CGA/HCG method described above.
+
+ when a second (special) characterset for a given screen resolution
+ is loaded via the "loadfont" utility, the emulator detects this fact
+ and uses from then on extended tables to access the second charset
+ as an upper half 512 characterset in terms of ega/vga speak.
+
+ from this time on, vt220 downloadable fonts are usable.
+
+ the organization of this extended characterset is as follows:
+
+ 0x00 ... 0x1f C0 display function fonts
+ 0x20 ... 0x3f C1 display function fonts
+ 0x40 ... 0x9f fonts for DEC Supplemental, DEC Special
+ Graphic, DEC Technical and ISO Latin-1
+ 0xa0 ... 0xff VT220 Downloadable Font
+
+ the mapping between vt220 charactersets and the charactersets inside
+ the ega/vga is done by tables found in the file pcvt_tbl.h.
+
+ there is a MSDOS fonteditor called "EVAFONT" available on the
+ SIMTEL-20 archive, which i used to edit the character sets in the
+ distribution.
+
diff --git a/usr.sbin/pcvt/Misc/Doc/EscapeSequences b/usr.sbin/pcvt/Misc/Doc/EscapeSequences
new file mode 100644
index 0000000..8e0e2c7
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Doc/EscapeSequences
@@ -0,0 +1,268 @@
+ Control Codes and Escape Sequences supported by pcvt
+===============================================================================
+
+CONTROL CODES
+-------------------------------------------------------------------------------
+
+ NUL (0x00) ignored
+ SOH (0x01) ignored
+ STX (0x02) ignored
+ ETX (0x03) ignored
+ EOT (0x04) ignored
+ ENQ (0x05) ignored
+ ACK (0x06) ignored
+
+ BEL (0x07) beep
+
+ BS (0x08) move one character position to the left
+ until at left margin
+
+ HT (0x09) move to next tab stop
+
+ LF (0x0a) move to next line, same column
+ VT (0x0b) move to next line, same column
+ FF (0x0c) move to next line, same column
+
+ CR (0x0d) move to left margin on current column
+
+ SO (0x0e) invoke character set G1 into GL
+ SI (0x0f) invoke character set G0 into GL
+
+ DLE (0x10) ignored
+ DC1 (0x11) ignored
+ DC2 (0x12) ignored
+ DC3 (0x13) ignored
+ DC4 (0x14) ignored
+ NAK (0x15) ignored
+ SYN (0x16) ignored
+ ETB (0x17) ignored
+
+ CAN (0x18) abort current escape sequence
+
+ EM (0x19) ignored
+
+ SUB (0x1a) abort current escape sequence
+
+ ESC (0x1b) start of escape sequence
+
+ FS (0x1c) ignored
+ GS (0x1d) ignored
+ RS (0x1e) ignored
+ US (0x1f) ignored
+
+ VT220 control codes in the range 0x80 .. 0x9f are completely ignored,
+ but displayed as C1 display controls.
+
+
+ESCAPE SEQUENCES DIGITAL EQUIPMENT
+-------------------------------------------------------------------------------
+
+(ni) = not implemented yet, all hooks available inside emulator!
+<p> = numeric parameter
+
+ ESC space F select 7-bit c1 control transmission (ni)
+ ESC space G select 8-bit c1 control transmission (ni)
+
+ ESC # 3 double height top half (ni)
+ ESC # 4 double height bottom half (ni)
+ ESC # 5 single width single height (ni)
+ ESC # 6 double width single height (ni)
+
+ ESC # 8 fill screen with 'E's
+
+ ESC 7 save cursor
+
+ ESC 8 restore cursor
+
+ ESC = keypad application mode
+
+ ESC > keypad numeric mode
+
+ ESC D index
+
+ ESC E next line
+
+ ESC H set tab at cur col
+
+ ESC M reverse index
+
+ ESC N single shift G2
+
+ ESC O single shift G3
+
+ ESC Z who are you
+
+ ESC d Only available if PCVT_SETCOLOR was defined when
+ compiling the kernel, allows to set custom color table
+ for more info, see pcvt_out.c ...
+
+ ESC c power up reset
+
+ ESC n Lock Shift G2 -> GL
+
+ ESC o Lock Shift G3 -> GL
+
+ ESC } Lock Shift G2 -> GR
+
+ ESC | Lock Shift G3 -> GR
+
+ ESC ~ Lock Shift G1 -> GR
+
+ ESC [ ? <p> h set dec private modes
+ ESC [ ? <p> l reset dec private modes
+ 1 CKM - cursor key mode
+ 6 OM - origin mode
+ 7 AWM - auto wrap mode
+
+ ESC [ <p> ' z DECELR - Enable Locator Report (ni)
+
+ ESC [ <p> ' { DECSLE - Select type of locator events (ni)
+
+ ESC [ ? n Terminal Reports
+
+ ESC [ ? K selective erase in line
+
+ ESC [ ? J selective erase in display
+
+ ESC [ <p> @ insert char(s)
+
+ ESC [ <p> A cursor up
+
+ ESC [ <p> B cursor down
+
+ ESC [ <p> C cursor forward
+
+ ESC [ <p> D cursor backward
+
+ ESC [ <p> H cursor direct cursor addressing
+
+ ESC [ <p> J erase screen
+
+ ESC [ <p> K erase line
+
+ ESC [ <p> L insert line
+
+ ESC [ <p> M delete line
+
+ ESC [ <p> P delete char
+
+ ESC [ <p> S scroll up
+
+ ESC [ <p> T scroll down
+
+ ESC [ <p> X erase character
+
+ ESC [ <p> c device attributes
+
+ ESC [ <p> f direct cursor addressing
+
+ ESC [ <p> g clear tabs
+
+ ESC [ <p> h set mode
+ ESC [ <p> l reset mode
+ 4 IRM - insert replacement mode
+ 20 LNM - line feed / newline mode
+
+ ESC [ <p> i media copy (ni)
+
+ ESC [ <p> m select graphic rendition
+ 0 reset to normal attributes
+ 1 bold
+ 4 underline
+ 5 blinking
+ 7 reverse
+ 22 bold off
+ 24 underline off
+ 25 blinking off
+ 27 reverse off
+ 30-37 foreground colors (on color display)
+ 40-47 background colors (on color display)
+
+ ESC [ <p> n device status report
+
+ ESC [ <p> r set scrolling region
+
+ ESC [ <p> x request / report terminal parameters
+
+ ESC [ <p> y invoke selftests (ni)
+
+
+ ESC [ x request/report parameters
+ ESC [ y invoke seftest(s)
+
+ ESC [ " q SCA
+ ESC [ ! p SCA
+
+ ESC ( <p> designate G0
+ ESC ) <p> designate G1
+ ESC * <p> designate G2
+ ESC + <p> designate G3
+ ESC - <p> designate G1 (96)
+ ESC . <p> designate G2 (96)
+ ESC / <p> designate G3 (96)
+ A British or ISO-Latin-1
+ B USASCII
+ C Finnish
+ 5 Finnish
+ E Norwegian/Danish
+ 6 Norwegian/Danish
+ H Swedish
+ 7 Swedish
+ K German
+ Q French Canadien
+ R French
+ Y Italian
+ Z Spanish
+ 0 special graphics
+ 1 alternate ROM
+ 2 alt ROM, spec graphics
+ 3 HP Roman 8, upper 128 chars*/
+ 4 Dutch
+ < DEC Supplemental
+ = Swiss
+ > DEC Technical
+
+
+ESCAPE SEQUENCES HEWLETT-PACKARD
+-------------------------------------------------------------------------------
+
+ESC&f<attr>a<key>k<llen>d<slen>L<label><string> set function key label
+
+ attr = attribute, 0 - normal (not implemented)
+ 1 - local only (not implemented)
+ 2 - transmit only (not implemented)
+
+ key = function key number, range 1 .. 8
+
+ llen = label string length
+
+ slen = string string length
+
+ label = label data (up to 16 chars per label)
+
+ string = string to send data (up to 80 chars per label)
+ (not implemented)
+
+EXAMPLE:
+========
+
+ ESC&f0a1k16d1LFUNCTION KEY 0
+
+ sets function key label 1 to "FUNCTION KEY ".
+ should program fkey 1 to emit "0" on keypress
+
+
+ESC&j<parm> function key operations
+
+ parm = '@' remove the function key labels from screen
+
+ parm = 'A' display the modes set of function key labels
+
+ parm = 'B' enable & display user function key labels
+
+ parm = 'C' clear message & restore the current key labels
+
+ parm = 'R' enable usr/sys and menu and label modes
+
+ parm = 'S' disable usr/sys and menu and label modes
+
diff --git a/usr.sbin/pcvt/Misc/Doc/Keyboard.HP b/usr.sbin/pcvt/Misc/Doc/Keyboard.HP
new file mode 100644
index 0000000..06ad5b7
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Doc/Keyboard.HP
@@ -0,0 +1,286 @@
+================================================================================
+# #
+# Function key mapping for the "more HP" - like layout #
+# #
+================================================================================
+
+
+I. Function Key Map
+========================
+
+
+PC | F1 | F2 | F3 | F4 | F5 | F6 | F7 | F8 | F9 | F10 | F11 | F12 |
+Key
+ _________________________________________________________________________
+UNSHF| 132 | Soft| | | | 7/8 | Dspl| Auto| SCRN| SCRN| SCRN| SCRN|
+SYS | Cols|Reset| | | Beep| Bit | Func| Wrap| 0 | 1 | 2 | 3 |
+LABEL|_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|
+UNSHF| | | | | | | | | SCRN| SCRN| SCRN| SCRN|
+USER | F6 | F7 | F8 | F9 | F10 | F11 | F12 | F13 | 0 | 1 | 2 | 3 |
+LABEL|_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|
+ | | | | | | | | | Fkey| U/S |VT/HP| Next|
+ALT | F14 | HELP| DO | F17 | F18 | F19 | F20 | F21 |Label|Label| Mode| SCRN|
+ |_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|
+ | User| User| User| User| User| User| User| User| | | | |
+SHIFT| F6 | F7 | F8 | F9 | F10 | F11 | F12 | F13 | | | | |
+ |_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|
+ALT- | User| User| User| User| User| User| User| User| | | | |
+SHIFT| F14 | F15 | F16 | F17 | F18 | F19 | F20 | F21 | | | | |
+ |_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|
+CTRL-| SCRN| SCRN| SCRN| SCRN| SCRN| SCRN| SCRN| SCRN| SCRN| SCRN| SCRN| SCRN|
+ALT | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
+ |_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|
+
+PC | F1 | F2 | F3 | F4 | F5 | F6 | F7 | F8 | F9 | F10 | F11 | F12 |
+Key
+
+
+
+II. Special Keys Used To Change The General Runtime Functionality
+=====================================================================
+
+
+ To be consistent with XFfree86 2.0 Virtual Screen switching, it is
+ now also possible to switch screens by using:
+
+ CTRL - ALT - Fx , where x can be 1 ... No of screens compiled,
+ see the definition of PCVT_NSCREENS !
+
+
+ Virtual Screen/Terminal switching
+ ---------------------------------
+
+ F9 or CTRL-ALT-F1 switches to screen 0
+ F10 or CTRL-ALT-F2 switches to screen 1
+ F11 or CTRL-ALT-F3 switches to screen 2
+ F12 or CTRL-ALT-F4 switches to screen 3
+ CTRL-ALT-F5 switches to screen 4
+ CTRL-ALT-F6 switches to screen 5
+ CTRL-ALT-F7 switches to screen 6
+ CTRL-ALT-F8 switches to screen 7
+ CTRL-ALT-F9 switches to screen 8
+ CTRL-ALT-F10 switches to screen 9
+ CTRL-ALT-F11 switches to screen 10
+ CTRL-ALT-F12 switches to screen 11
+
+ (see also ALT-F12 below)
+
+
+ ALT-F9 Function key labels ON / OFF
+ ------------------------------------
+
+ this key is only operational, when in HP/VT mode, see F11
+
+ ALT-F9 toggles between function key labels displayed or not.
+
+ ON: screen has either 25-3 = 22, 28-3 = 25 or 50-3 = 47 lines
+ 2 lines fkey labels displayed
+ row/col display enabled
+ 1 line status/load avg line
+
+ OFF: screen has either 25-3 = 22, 28-3 = 25 or 50-3 = 47 lines
+ no function key labels displayed
+ no row/col display
+ no status/load avg line
+
+ applicable escape sequences:
+
+ switch OFF:
+ ESC & j @ remove labels from screen
+
+ switch ON:
+ ESC & j A display system fkey labels
+ ESC & j B display user fkey labels
+ ESC & j C display current fkey labels
+
+
+ ALT-F10 User / System Function key labels
+ -----------------------------------------
+
+ this key is only operational, when in HP/VT mode, see ALT-F11
+
+ ALT-F10 toggles between the display of user or system
+ function key labels
+
+ this key is only active if labels are toggled on via
+ the ALT-F9 function key
+
+ screen has either 25-3 = 22, 28-3 = 25 or 50-3 = 47 lines
+ 2 lines fkey labels displayed
+ row/col display enabled
+ 1 line status/load avg line
+
+
+ applicable escape sequences: (see above)
+
+ ESC & j A display system fkey labels
+ ESC & j B display user fkey labels
+ ESC & j C display current fkey labels
+
+
+ ALT-F11 Toggle between pure VT and HP-VT Emulation
+ --------------------------------------------------
+
+ This key switches between pure vt100/vt220 mode and
+ vt100/vt220 with hp-fkey-labels mode of operation.
+ this is not dependent of any screen resolution the
+ virtual consoles are in.
+
+ initially, after power on, all virtual screens are in
+ the pure vt-mode:
+
+ - the emulator does not execute any hp escape sequences
+ - the function key label lines are switched off
+ - no load average or status line is displayed
+ - no row / column counter is displayed
+ - no window number is displayed
+ - the full screen 25x80, 28x80 or 50x80 is usable
+ - ALT-F9 and ALT-F10 have no function
+
+ when toggled to the vt/hp mix of operation, the following
+ changes take place:
+
+ - the emulator executes the supported hp-esc sequences
+ - the function key labels are displayable depending
+ on the state of ALT-F9/F10
+ - the load average is displayed
+ - the row / column counters are displayed
+ - the window number is displayed in the lower right corner
+ - in any screen resolution, the last 3 lines are lost,
+ so one has a 22x80, 25x80 and 47x80 resolution
+ - F9 and F10 are operational.
+
+ when switching modes by means of ALT-F11, the following
+ changes to the current environment, the emulator may
+ be in, apply:
+
+ - the screen is cleared
+ - the cursor moves to the home position
+ - the scrolling region is reset to default
+
+ (this functionality is available via an ioctl)
+
+ ALT-F12 Cycle current screen
+ ----------------------------
+
+ This key cycles through the display of the video
+ screens. on startup, screen 0 is displayed; with every
+ keypress of F12, the next screen is displayed, wrapping
+ from the maximum screen number back to screen 0.
+
+ (this functionality is available via an ioctl)
+
+
+III. Special Keys used to change the Runtime Functionality of a Page
+=======================================================================
+
+ when in mixed HP/VT mode, one has two types of function key labels
+ on screen, user function keys and system function keys.
+
+ this functionality is NOT available in pure VT220 mode !
+
+ the user function keys emit the below mentioned VT220 function-
+ key sequences. the labels can be reprogrammed by use of escape
+ sequences.
+
+ in system function key mode, several pre-programmed functions inside
+ the emulator can be toggled, currently implemented are:
+
+ - F1, if a chipset is detected for which 132 operation is supported,
+ F1 toggles between 80 columns and 132 columns.
+ - F2, does a soft reset of the emulator code
+ - F3, -/-
+ - F4, -/-
+ - F5, toggle the audible beep generation
+ - F6, toggle 7/8 bit char width
+ - F7, toggle display functions. this means that control codes
+ in the range 0x00 to 0x1f are not EXECUTED by the emulator
+ any longer, but displayed on the screen
+ - F8, toggle automatic cursor wraparound at end of line
+
+ these functions operate just for the selected screen
+
+
+IV. Keyboard VT220 compatibility
+====================================
+
+ The following keys behave different as probably expected.
+
+ They were mapped to provide more VT220 compatibility.
+
+ To have F1-F8 emit something in HP-mode, you have to switch
+ to user function key labels by using ALT-F10 (see ALT-F10 above) !
+
+ PC Keyboard VT220 Keyboard
+ ------------- --------------------------------------------------
+ F1 F6 (ESC [ 17 ~)
+ F2 F7 (ESC [ 18 ~)
+ F3 F8 (ESC [ 19 ~)
+ F4 F9 (ESC [ 20 ~)
+ F5 F10 (ESC [ 21 ~)
+ F6 F11 (ESC [ 23 ~)
+ F7 F12 (ESC [ 24 ~)
+ F8 F13 (ESC [ 25 ~)
+ ALT-F1 F14 (ESC [ 26 ~)
+ ALT-F2 HELP (ESC [ 28 ~)
+ ALT-F3 DO (ESC [ 29 ~)
+ ALT-F4 F17 (ESC [ 31 ~)
+ ALT-F5 F18 (ESC [ 32 ~)
+ ALT-F6 F19 (ESC [ 33 ~)
+ ALT-F7 F20 (ESC [ 34 ~)
+ ALT-F8 F21 (ESC [ 35 ~) (i know !!!!)
+
+ SHIFT-F1 ... SHIFT-F8
+ User Defined Keys for F6 - F13
+
+ ALT-SHIFT-F1 ... ALT-SHIFT-F8
+ User Defined Keys for F14 - F20
+
+ Insert Insert Here (ESC [ 2 ~)
+
+ Delete Remove (ESC [ 3 ~)
+
+ Home Find (ESC [ 1 ~)
+
+ End Select (ESC [ 4 ~)
+
+ PgUp Prev Screen (ESC [ 5 ~)
+
+ PgDn Next Screen (ESC [ 6 ~)
+
+ PrtSc (ignored)
+
+ Pause (ignored)
+
+ Break (ignored)
+
+ Cursor Keys dependent upon state of cursor key mode either
+ ESC [ A ... ESC [ D or ESC O A ... ESC O D
+
+ NumLock toggles Numeric Keypad for Keypad Numeric Mode
+ and Keypad Application Mode
+
+ ALT-Keypad-0 can be used to generate any keycode in the
+ ALT-Keypad-1 rage 0-255.
+ ALT-Keypad-2 this was modeled with the behaviour of a
+ ALT-Keypad-3 popular boot loader in mind
+ ALT-Keypad-4
+ ALT-Keypad-5
+ ALT-Keypad-6
+ ALT-Keypad-7
+ ALT-Keypad-8
+ ALT-Keypad-9
+
+ Numeric Keypad dependent upon state of keypad numeric/application
+ mode either 0 ... 9 or ESC O p ... ESC O y
+
+ ALT-NumLock emits PF1 sequence (ESC O P)
+
+ ALT-Keypad-/ emits PF2 sequence (ESC O Q)
+
+ ALT-Keypad-* emits PF3 sequence (ESC O R)
+
+ ALT-Keypad-- emits PF4 sequence (ESC O S)
+
+
+/* EOF */
diff --git a/usr.sbin/pcvt/Misc/Doc/Keyboard.VT b/usr.sbin/pcvt/Misc/Doc/Keyboard.VT
new file mode 100644
index 0000000..4353ec7
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Doc/Keyboard.VT
@@ -0,0 +1,231 @@
+================================================================================
+# #
+# Function key mapping for the "more VT220" - like layout #
+# #
+================================================================================
+
+
+I. Special Keys Used To Change The General Runtime Functionality
+=====================================================================
+
+
+ CTRL-F9 Function key labels ON / OFF
+ ------------------------------------
+
+ this key is only operational, when in HP/VT mode, see CTRL-F11
+
+ CTRL-F9 toggles between function key labels displayed or not.
+
+ ON: screen has either 25-3 = 22, 28-3 = 25 or 50-3 = 47 lines
+ 2 lines fkey labels displayed
+ row/col display enabled
+ 1 line status/load avg line
+
+ OFF: screen has either 25-3 = 22, 28-3 = 25 or 50-3 = 47 lines
+ no function key labels displayed
+ no row/col display
+ no status/load avg line
+
+ applicable escape sequences:
+
+ switch OFF:
+ ESC & j @ remove labels from screen
+
+ switch ON:
+ ESC & j A display system fkey labels
+ ESC & j B display user fkey labels
+ ESC & j C display current fkey labels
+
+
+ CTRL-F10 User / System Function key labels
+ -------------------------------------------------
+
+ this key is only operational, when in HP/VT mode, see CTRL-F11
+
+ CTRL-F10 toggles between the display of user or system
+ function key labels
+
+ this key is only active if labels are toggled on via
+ the CTRL-F9 function key
+
+ screen has either 25-3 = 22, 28-3 = 25 or 50-3 = 47 lines
+ 2 lines fkey labels displayed
+ row/col display enabled
+ 1 line status/load avg line
+
+
+ applicable escape sequences: (see above)
+
+ ESC & j A display system fkey labels
+ ESC & j B display user fkey labels
+ ESC & j C display current fkey labels
+
+
+ CTRL-F11 Toggle between pure VT and HP-VT Emulation
+ ----------------------------------------------------------
+
+ This key switches between pure vt100/vt220 mode and
+ vt100/vt220 with hp-fkey-labels mode of operation.
+ this is not dependent of any screen resolution the
+ virtual consoles are in.
+
+ initially, after power on, all virtual screens are in
+ the pure vt-mode:
+
+ - the emulator does not execute any hp escape sequences
+ - the function key label lines are switched off
+ - no load average or status line is displayed
+ - no row / column counter is displayed
+ - the full screen 25x80, 28x80 or 50x80 is usable
+ - CTRL-F9 and CTRL-F10 have no function
+
+ when toggled to the vt/hp mix of operation, the following
+ changes take place:
+
+ - the emulator executes the supported hp-esc sequences
+ - the function key labels are displayable depending
+ on the state of CTRL-F9/CTRL-F10
+ - the load average is displayed
+ - the row / column counters are displayed
+ - in any screen resolution, the last 3 lines are lost,
+ so one has a 22x80, 25x80 and 47x80 resolution
+ - CTRL-F9 and CTRL-F10 are operational.
+
+ when switching modes by means of CTRL-F11, the following
+ changes to the current environment, the emulator may
+ be in, apply:
+
+ - the screen is cleared
+ - the cursor moves to the home position
+ - the scrolling region is reset to default
+
+ (this functionality is available via an ioctl)
+
+ F5 Cycle current page
+ --------------------------
+
+ This key cycles through the display of the video
+ pages. on startup, page 0 is displayed; with every
+ keypress of F5, the next page is displayed, wrapping
+ from the maximum page number back to page 0.
+
+ F1 switches to page 0
+ F2 switches to page 1
+ F3 switches to page 2
+ F4 switches to page 3
+
+ Shift-F1 -- select screen 4
+ Shift-F2 -- select screen 5
+ Shift-F3 -- select screen 6
+ Shift-F4 -- select screen 7
+ Shift-F5 -- select (current screen - 1)
+
+ (this functionality is available via an ioctl)
+
+
+II. Special Keys used to change the Runtime Functionality of a Page
+=======================================================================
+
+ when in mixed HP/VT mode, one has two types of function key labels
+ on screen, user function keys and system function keys. they are
+ accessed by using the CTRL key while pressing a function key.
+
+ the function key labels are NOT available in pure VT220 mode--although
+ the function keys are still active
+
+ the user function keys and their labels can be reprogrammed by use of
+ escape sequences.
+
+ in system function key mode, several pre-programmed functions inside
+ the emulator can be toggled, currently implemented are:
+
+ - CTRL-F1, toggle 80/132 columns
+ - CTRL-F2, soft reset emulator
+ - CTRL-F3, toggle force 24 lines mode
+ - CTRL-F4, toggle keyboard debugging (if compiled in)
+ - CTRL-F5, toggle the audible beep generation
+ - CTRL-F6, toggle 7/8 bit char width
+ - CTRL-F7, toggle display functions. this means that control codes
+ in the range 0x00 to 0x1f are not EXECUTED by the emulator
+ any longer, but displayed on the screen
+ - CTRL-F8, toggle automatic cursor wraparound at end of line
+
+ these functions operate just for the selected page
+
+
+III. Keyboard VT220 compatibility
+====================================
+
+ The following keys behave different as probably expected.
+
+ They were mapped to provide more VT220 compatibility.
+
+
+ PC Keyboard VT220 Keyboard
+ ------------- --------------------------------------------------
+ F6 - F12 emit the sequences for VT220 F6 - F12 keys
+ (ESC [ 17 ~ .... ESC [ 24 ~)
+
+ ALT F1 - F10 emit the sequences for VT220 F11 - F14, HELP, DO,
+ F17 - F20 keys
+ (ESC [ 23 ~ .... ESC [ 34 ~)
+
+ SHIFT F6 - F12 emit the User Definable Key sequences for VT220
+ F6 - F12 keys or
+ (ESC [ 17 ~ .... ESC [ 24 ~) for cleared sequences
+
+ ALTSHIFT F1-F10 emit the User Definable Key sequences for VT220
+ F11 - F14, HELP, DO, F17 - F20 keys or
+ (ESC [ 23 ~ .... ESC [ 34 ~) for cleared sequences
+
+ Insert Insert Here (ESC [ 2 ~)
+
+ Delete Remove (ESC [ 3 ~)
+
+ Home Find (ESC [ 1 ~)
+
+ End Select (ESC [ 4 ~)
+
+ PgUp Prev Screen (ESC [ 5 ~)
+
+ PgDn Next Screen (ESC [ 6 ~)
+
+ PrtSc (ignored)
+
+ Pause (ignored)
+
+ Break (ignored)
+
+ Cursor Keys dependent upon state of cursor key mode either
+ ESC [ A ... ESC [ D or ESC O A ... ESC O D
+
+ NumLock toggles Numeric Keypad for Keypad Numeric Mode
+ and Keypad Application Mode
+
+ ALT-Keypad-0 can be used to generate any keycode in the
+ ALT-Keypad-1 rage 0-255.
+ ALT-Keypad-2 this was modeled with the behaviour of a
+ ALT-Keypad-3 popular boot loader in mind
+ ALT-Keypad-4
+ ALT-Keypad-5
+ ALT-Keypad-6
+ ALT-Keypad-7
+ ALT-Keypad-8
+ ALT-Keypad-9
+
+ Numeric Keypad dependent upon state of keypad numeric/application
+ mode either 0 ... 9 or ESC O p ... ESC O y
+
+ ALT-NumLock emits PF1 sequence (ESC O P)
+
+ ALT-Keypad-/ emits PF2 sequence (ESC O Q)
+
+ ALT-Keypad-* emits PF3 sequence (ESC O R)
+
+ ALT-Keypad-- emits PF4 sequence (ESC O S)
+
+ ALT-F11 emits PF1 sequence (ESC O P)
+
+ ALT-F12 emits PF2 sequence (ESC O Q)
+
+/* EOF */
diff --git a/usr.sbin/pcvt/Misc/Doc/Makefile b/usr.sbin/pcvt/Misc/Doc/Makefile
new file mode 100644
index 0000000..a6d9660
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Doc/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+NOOBJ= noobj
+FILES= Acknowledgements Bibliography CharGen \
+ Charsets EscapeSequences Keyboard.HP Keyboard.VT \
+ NotesAndHints
+FILESDIR= ${BINDIR}/Doc
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pcvt/Misc/Doc/NotesAndHints b/usr.sbin/pcvt/Misc/Doc/NotesAndHints
new file mode 100644
index 0000000..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/Etc/Makefile b/usr.sbin/pcvt/Misc/Etc/Makefile
new file mode 100644
index 0000000..5e1aac5
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Etc/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+NOOBJ= noobj
+FILES= Termcap Terminfo pcvt.el xmodmap-german pcvt.sh
+FILESDIR= ${BINDIR}/Etc
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pcvt/Misc/Etc/Termcap b/usr.sbin/pcvt/Misc/Etc/Termcap
new file mode 100644
index 0000000..a4eb030
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Etc/Termcap
@@ -0,0 +1,284 @@
+#---------------------------------------------------------------------------
+#
+# pcvt termcap database entry (release 3.20)
+#
+# last edit-date: [Sun Apr 2 18:31:04 1995]
+#
+# -hm new entries for SR and SF
+# -hm removing duplicate entries
+#
+#---------------------------------------------------------------------------
+
+#---------------------------------------------------------------------------
+# NetBSD/FreeBSD vt220 terminal emulator console (pc keyboard & monitor)
+# termcap entries for pure VT220-Emulation and 25, 28, 35, 40, 43 and
+# 50 lines entries
+# 80 columns
+#---------------------------------------------------------------------------
+pcvt25|dec vt220 with 25 lines:\
+ :li#25:\
+ :co#80:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;25r\E[25;1H:\
+ :tc=pcvtXX:
+
+pcvt28|dec vt220 with 28 lines:\
+ :li#28:\
+ :co#80:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;28r\E[28;1H:\
+ :tc=pcvtXX:
+
+pcvt35|dec vt220 with 35 lines:\
+ :li#35:\
+ :co#80:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;35r\E[35;1H:\
+ :tc=pcvtXX:
+
+pcvt40|dec vt220 with 40 lines:\
+ :li#40:\
+ :co#80:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;40r\E[40;1H:\
+ :tc=pcvtXX:
+
+pcvt43|dec vt220 with 43 lines:\
+ :li#43:\
+ :co#80:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;43r\E[43;1H:\
+ :tc=pcvtXX:
+
+pcvt50|dec vt220 with 50 lines:\
+ :li#50:\
+ :co#80:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;50r\E[50;1H:\
+ :tc=pcvtXX:
+
+#---------------------------------------------------------------------------
+# NetBSD/FreeBSD vt220 terminal emulator console (pc keyboard & monitor)
+# termcap entries for pure VT220-Emulation and 25, 28, 35, 40, 43 and
+# 50 lines entries
+# 132 columns
+#---------------------------------------------------------------------------
+pcvt25w|dec vt220 with 25 lines and 132 cols:\
+ :li#25:\
+ :co#132:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;25r\E[25;1H:\
+ :tc=pcvtXX:
+
+pcvt28w|dec vt220 with 28 lines and 132 cols:\
+ :li#28:\
+ :co#132:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;28r\E[28;1H:\
+ :tc=pcvtXX:
+
+pcvt35w|dec vt220 with 35 lines and 132 cols:\
+ :li#35:\
+ :co#132:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;35r\E[35;1H:\
+ :tc=pcvtXX:
+
+pcvt40w|dec vt220 with 40 lines and 132 cols:\
+ :li#40:\
+ :co#132:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;40r\E[40;1H:\
+ :tc=pcvtXX:
+
+pcvt43w|dec vt220 with 43 lines and 132 cols:\
+ :li#43:\
+ :co#132:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;43r\E[43;1H:\
+ :tc=pcvtXX:
+
+pcvt50w|dec vt220 with 50 lines and 132 cols:\
+ :li#50:\
+ :co#132:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;50r\E[50;1H:\
+ :tc=pcvtXX:
+
+#---------------------------------------------------------------------------
+# NetBSD/FreeBSD vt220 terminal emulator console (pc keyboard & monitor)
+# termcap entries for HP-Emulation and 25, 28, 35, 40, 43 and 50
+# lines entries. note that the HP-Emulation uses the bottom 3 lines
+# for status and function key labels, so we get always 3 lines less.
+# "Xs" is a nonstandard, private flag indicating HP-like fkey labels
+# 80 column entries
+#---------------------------------------------------------------------------
+pcvt22h|dec vt220 with HP-fkey labels and 22 lines:\
+ :li#22:\
+ :co#80:\
+ :Xs:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;22r\E[22;1H:\
+ :tc=pcvtXX:
+
+pcvt25h|dec vt220 with HP-fkey labels and 25 lines:\
+ :li#25:\
+ :co#80:\
+ :Xs:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;25r\E[25;1H:\
+ :tc=pcvtXX:
+
+pcvt32h|dec vt220 with HP-fkey labels and 32 lines:\
+ :li#32:\
+ :co#80:\
+ :Xs:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;32r\E[32;1H:\
+ :tc=pcvtXX:
+
+pcvt37h|dec vt220 with HP-fkey labels and 37 lines:\
+ :li#37:\
+ :co#80:\
+ :Xs:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;37r\E[37;1H:\
+ :tc=pcvtXX:
+
+pcvt40h|dec vt220 with HP-fkey labels and 40 lines:\
+ :li#40:\
+ :co#80:\
+ :Xs:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;40r\E[40;1H:\
+ :tc=pcvtXX:
+
+pcvt47h|dec vt220 with HP-fkey labels and 47 lines:\
+ :li#47:\
+ :co#80:\
+ :Xs:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;47r\E[47;1H:\
+ :tc=pcvtXX:
+
+#---------------------------------------------------------------------------
+# NetBSD/FreeBSD vt220 terminal emulator console (pc keyboard & monitor)
+# termcap entries for HP-Emulation and 25, 28, 35, 40, 43 and 50
+# lines entries. note that the HP-Emulation uses the bottom 3 lines
+# for status and function key labels, so we get always 3 lines less.
+# "Xs" is a nonstandard, private flag indicating HP-like fkey labels
+# 132 column entries
+#---------------------------------------------------------------------------
+pcvt22hw|dec vt220 with HP-fkey labels, 22 lines and 132 cols:\
+ :li#22:\
+ :co#132:\
+ :Xs:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;22r\E[22;1H:\
+ :tc=pcvtXX:
+
+pcvt25hw|dec vt220 with HP-fkey labels, 25 lines and 132 cols:\
+ :li#25:\
+ :co#132:\
+ :Xs:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;25r\E[25;1H:\
+ :tc=pcvtXX:
+
+pcvt32hw|dec vt220 with HP-fkey labels, 32 lines and 132 cols:\
+ :li#32:\
+ :co#132:\
+ :Xs:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;32r\E[32;1H:\
+ :tc=pcvtXX:
+
+pcvt37hw|dec vt220 with HP-fkey labels, 37 lines and 132 cols:\
+ :li#37:\
+ :co#132:\
+ :Xs:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;37r\E[37;1H:\
+ :tc=pcvtXX:
+
+pcvt40hw|dec vt220 with HP-fkey labels, 40 lines and 132 cols:\
+ :li#40:\
+ :co#132:\
+ :Xs:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;40r\E[40;1H:\
+ :tc=pcvtXX:
+
+pcvt47hw|dec vt220 with HP-fkey labels, 47 lines and 132 cols:\
+ :li#47:\
+ :co#132:\
+ :Xs:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;47r\E[47;1H:\
+ :tc=pcvtXX:
+
+#---------------------------------------------------------------------------
+# main entry, without "is" and "li" capabilities
+#
+# NOTE: because the 386BSD "vi"/"elvis" seems to have a bug if
+# both "ic" and "im" are specified (an original VT220
+# shows the same buggy behaviour!), "ic" has been taken
+# out of this entry. for reference, it should be "ic=\E[@".
+#
+#---------------------------------------------------------------------------
+pcvtXX|pcvt vt200 emulator (DEC VT220):\
+ :AL=\E[%dL:\
+ :DC=\E[%dP:\
+ :DL=\E[%dM:\
+ :DO=\E[%dB:\
+ :IC=\E[%d@:\
+ :LE=\E[%dD:\
+ :RI=\E[%dC:\
+ :SF=\E[%dS:\
+ :SR=\E[%dT:\
+ :UP=\E[%dA:\
+ :ae=^O:\
+ :al=\E[L:\
+ :am:\
+ :as=^N:\
+ :bl=^G:\
+ :bs:\
+ :cb=\E[1K:\
+ :cd=\E[J:\
+ :ce=\E[K:\
+ :cl=\E[H\E[J:\
+ :cm=\E[%i%d;%dH:\
+ :cr=^M:\
+ :cs=\E[%i%d;%dr:\
+ :ct=\E[3g:\
+ :dc=\E[P:\
+ :dl=\E[M:\
+ :do=^J:\
+ :ei=\E[4l:\
+ :ho=\E[H:\
+ :im=\E[4h:\
+ :it#8:\
+ :k1=\E[17~:\
+ :k2=\E[18~:\
+ :k3=\E[19~:\
+ :k4=\E[20~:\
+ :k5=\E[21~:\
+ :k6=\E[23~:\
+ :k7=\E[24~:\
+ :k8=\E[25~:\
+ :kD=\E[3~:\
+ :kH=\E[4~:\
+ :kI=\E[2~:\
+ :kN=\E[6~:\
+ :kP=\E[5~:\
+ :kb=\177:\
+ :kd=\EOB:\
+ :ke=\E[?1l\E>:\
+ :kh=\E[1~:\
+ :kl=\EOD:\
+ :km:\
+ :kr=\EOC:\
+ :ks=\E[?1h\E=:\
+ :ku=\EOA:\
+ :le=^H:\
+ :mb=\E[5m:\
+ :md=\E[1m:\
+ :me=\E[m:\
+ :mi:\
+ :mr=\E[7m:\
+ :ms:\
+ :nd=\E[C:\
+ :pb#16000000:\
+ :pt:\
+ :rc=\E8:\
+ :rf=/usr/share/tabset/vt100:\
+ :rs=\Ec\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h:\
+ :sc=\E7:\
+ :se=\E[27m:\
+ :sf=\ED:\
+ :so=\E[7m:\
+ :sr=\EM:\
+ :st=\EH:\
+ :ue=\E[24m:\
+ :up=\E[A:\
+ :us=\E[4m:\
+ :vt#3:\
+ :xn:
+
+#---------------------------- E O F -------------------------------------------
diff --git a/usr.sbin/pcvt/Misc/Etc/Terminfo b/usr.sbin/pcvt/Misc/Etc/Terminfo
new file mode 100644
index 0000000..072f885
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Etc/Terminfo
@@ -0,0 +1,41 @@
+pcvt25h|pcvt vt220 emulator video driver 80 cols,
+ lines#25, cols#80,
+ am, bel=^G, blink=^[[5m, bold=^[[1m, clear=^[[H^[[J,
+ cr=^M, csr=^[[%i%p1%d;%p2%dr, cub1=^H, cub=^[[%p1%dD, cud1=^J,
+ cud=^[[%p1%dB, cuf1=^[[C, cuf=^[[%p1%dC, cup=^[[%i%p1%d;%p2%dH,
+ cuu1=^[[A, cuu=^[[%p1%dA, dch1=^[[P, dch=^[[%p1%dP, dl1=^[[M,
+ dl=^[[%p1%dM, ed=^[[J, el=^[[K, home=^[[H, ht=^I, hts=^[H,
+ ich1=^[[@, ich=^[[%p1%d@, il1=^[[L, il=^[[%p1%dL, ind=^[D,
+ indn=^[%p1%dD, is1=^[>^[[?3l^[[?4l^[[?5l^[[?7h^[[?8h^[[1;25r^[[25;1H,
+ it#8, kbs=^?, kcub1=^[OD, kcud1=^[OB, kcuf1=^[OC, kcuu1=^[OA,
+ kdch1=^[[3~, kf1=^[[17~, kf2=^[[18~, kf3=^[[19~, kf4=^[[20~,
+ kf5=^[[21~, kf6=^[[23~, kf7=^[[24~, kf8=^[[25~, khome=^[[1~,
+ kich1=^[[2~, kll=^[[4~, km, knp=^[[6~, kpp=^[[5~,
+ mir, msgr, nel=^M^J, pb#16000000, rc=^[8, rev=^[[7m,
+ rf=/usr/share/tabset/vt100, ri=^[M, rin=^[%p1%dM, rmacs=^O,
+ rmir=^[[4l, rmkx=^[[?1l^[>, rmso=^[[27m, rmul=^[[24m,
+ rs1=^[c^[>^[[?3l^[[?4l^[[?5l^[[?7h^[[?8h, sc=^[7, sgr0=^[[m,
+ smacs=^N, smir=^[[4h, smkx=^[[?1h^[=, smso=^[[7m, smul=^[[4m,
+ tbc=^[[3g, vt#3, xenl, colors#8, pairs#64, setf=\E[%p1%{30}%+%dm,
+ setb=\E[%p1%{40}%+%dm,
+
+pcvt25hw|pcvt vt220 emulator video driver 132 cols,
+ lines#25, cols#132,
+ am, bel=^G, blink=^[[5m, bold=^[[1m, clear=^[[H^[[J,
+ cr=^M, csr=^[[%i%p1%d;%p2%dr, cub1=^H, cub=^[[%p1%dD, cud1=^J,
+ cud=^[[%p1%dB, cuf1=^[[C, cuf=^[[%p1%dC, cup=^[[%i%p1%d;%p2%dH,
+ cuu1=^[[A, cuu=^[[%p1%dA, dch1=^[[P, dch=^[[%p1%dP, dl1=^[[M,
+ dl=^[[%p1%dM, ed=^[[J, el=^[[K, home=^[[H, ht=^I, hts=^[H,
+ ich1=^[[@, ich=^[[%p1%d@, il1=^[[L, il=^[[%p1%dL, ind=^[D,
+ indn=^[%p1%dD, is1=^[>^[[?3l^[[?4l^[[?5l^[[?7h^[[?8h^[[1;25r^[[25;1H,
+ it#8, kbs=^?, kcub1=^[OD, kcud1=^[OB, kcuf1=^[OC, kcuu1=^[OA,
+ kdch1=^[[3~, kf1=^[[17~, kf2=^[[18~, kf3=^[[19~, kf4=^[[20~,
+ kf5=^[[21~, kf6=^[[23~, kf7=^[[24~, kf8=^[[25~, khome=^[[1~,
+ kich1=^[[2~, kll=^[[4~, km, knp=^[[6~, kpp=^[[5~,
+ mir, msgr, nel=^M^J, pb#16000000, rc=^[8, rev=^[[7m,
+ rf=/usr/share/tabset/vt100, ri=^[M, rin=^[%p1%dM, rmacs=^O,
+ rmir=^[[4l, rmkx=^[[?1l^[>, rmso=^[[27m, rmul=^[[24m,
+ rs1=^[c^[>^[[?3l^[[?4l^[[?5l^[[?7h^[[?8h, sc=^[7, sgr0=^[[m,
+ smacs=^N, smir=^[[4h, smkx=^[[?1h^[=, smso=^[[7m, smul=^[[4m,
+ tbc=^[[3g, vt#3, xenl, colors#8, pairs#64, setf=\E[%p1%{30}%+%dm,
+ setb=\E[%p1%{40}%+%dm,
diff --git a/usr.sbin/pcvt/Misc/Etc/pcvt.el b/usr.sbin/pcvt/Misc/Etc/pcvt.el
new file mode 100644
index 0000000..bd6484f
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Etc/pcvt.el
@@ -0,0 +1,19 @@
+;; pcvt25.el, by J"org Wunsch <joerg_wunsch@uriah.sax.de>
+;;
+;; pcvt is a good VT emulator
+
+(load "term/vt220")
+
+;; ...but i don't like `find' and `select' on `home' and `end' keys
+(global-set-key [find] 'beginning-of-line)
+(global-set-key [select] 'end-of-line)
+
+;; and the f1 thru f8 keys are designated as f6 thru f13
+(define-key function-key-map "\e[17~" [f1])
+(define-key function-key-map "\e[18~" [f2])
+(define-key function-key-map "\e[19~" [f3])
+(define-key function-key-map "\e[20~" [f4])
+(define-key function-key-map "\e[21~" [f5])
+(define-key function-key-map "\e[23~" [f6])
+(define-key function-key-map "\e[24~" [f7])
+(define-key function-key-map "\e[25~" [f8])
diff --git a/usr.sbin/pcvt/Misc/Etc/pcvt.sh b/usr.sbin/pcvt/Misc/Etc/pcvt.sh
new file mode 100644
index 0000000..cc3b34b
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Etc/pcvt.sh
@@ -0,0 +1,163 @@
+#---------------------------------------------------------------------------
+#
+# configure pcvt on system startup example
+# ----------------------------------------
+#
+# This script can be moved to /usr/local/etc/rc.d to
+# configure the pcvt driver at system startup time.
+#
+# Please adjust the values in the configuration
+# section below to suit your needs!
+#
+#---------------------------------------------------------------------------
+#
+# last edit-date: [Fri Mar 31 10:40:18 2000]
+#
+# $FreeBSD$
+#
+#---------------------------------------------------------------------------
+
+############################################################################
+# configuration section
+############################################################################
+
+# path for pcvt's EGA/VGA download fonts
+FONTP=/usr/share/misc/pcvtfonts
+
+pcvt_keymap="de" # keyboard map in /usr/share/misc/keycap.pcvt (or NO).
+pcvt_keydel="0" # key repeat delay, 0-3 (250,500,750,1000 msec) (or NO).
+pcvt_keyrate="5" # keyboard repetition rate 31-0 (2-30 char/sec) (or NO).
+pcvt_keyrepeat="ON" # keyboard repeat ON or OFF (or NO).
+pcvt_force24="NO" # force a 24 line display (when 25 possible) (or NO).
+pcvt_hpext="YES" # use HP extensions (function keys labels) (or NO).
+pcvt_lines="28" # lines (25, 28, 40, 50 or NO).
+pcvt_blanktime="60" # blank time (in seconds) (or NO).
+pcvt_cursorh="0" # cursor top scanline (topmost line is 0) (or NO).
+pcvt_cursorl="16" # cursor low scanline (bottom line is 16) (or NO).
+pcvt_monohigh="YES" # set intensity to high on monochrome monitors (or NO).
+
+############################################################################
+# nothing to configure from here
+############################################################################
+
+# check for correct driver and driver version matching
+
+if ispcvt -d /dev/ttyv0 ; then
+ echo ""
+ echo "configuring pcvt console driver"
+
+# get video adaptor type
+
+ adaptor=`scon -d /dev/ttyv0 -a`
+ echo -n " video adaptor type is $adaptor, "
+
+# get monitor type (mono/color)
+
+ monitor=`scon -d /dev/ttyv0 -m`
+ echo "monitor type is $monitor"
+
+# load fonts into vga
+
+ if [ $adaptor = VGA ] ; then
+ echo -n ' loading fonts: 8x16:0,'
+ loadfont -d /dev/ttyv0 -c0 -f $FONTP/vt220l.816
+ echo -n '1 '
+ loadfont -d /dev/ttyv0 -c1 -f $FONTP/vt220h.816
+ echo -n ' 8x14:0,'
+ loadfont -d /dev/ttyv0 -c2 -f $FONTP/vt220l.814
+ echo -n '1 '
+ loadfont -d /dev/ttyv0 -c3 -f $FONTP/vt220h.814
+ echo -n ' 8x10:0,'
+ loadfont -d /dev/ttyv0 -c4 -f $FONTP/vt220l.810
+ echo -n '1 '
+ loadfont -d /dev/ttyv0 -c5 -f $FONTP/vt220h.810
+ echo -n ' 8x8:0,'
+ loadfont -d /dev/ttyv0 -c6 -f $FONTP/vt220l.808
+ echo '1 '
+ loadfont -d /dev/ttyv0 -c7 -f $FONTP/vt220h.808
+
+# setting screen sizes
+
+ if [ "X${pcvt_lines}" = X"28" ]; then
+ size=-s28
+ echo ' switching to 28 lines'
+ elif [ "X${pcvt_lines}" = X"40" ]; then
+ size=-s40
+ echo ' switching to 40 lines'
+ elif [ "X${pcvt_lines}" = X"50" ]; then
+ size=-s50
+ echo ' switching to 50 lines'
+ else
+ size=-s25
+ echo ' switching to 25 lines'
+ fi
+ fi
+
+# use HP extensions to VT220 emulation ?
+
+ if [ "X${pcvt_hpext}" != X"NO" ] ; then
+ emulation=-H
+ echo " setting emulation to VT220 with HP extensions"
+ else
+ emulation=-V
+ echo " setting emulation to VT220"
+ fi
+
+# for all screens do
+
+ for device in /dev/ttyv*
+ do
+ scon -d$device $size $emulation >/dev/null 2>&1
+ if [ $? != 0 ]
+ then
+ break 1
+ fi
+
+ if [ X${pcvt_cursorh} != X"NO" -a X${pcvt_cursorl} != X"NO" ] ; then
+ cursor -d$device -s$pcvt_cursorh -e$pcvt_cursorl
+ fi
+
+# if monochrome monitor, set color palette to use a higher intensity
+
+ if [ X${pcvt_monohigh} != X"NO" -a $monitor = MONO -a $adaptor = VGA ] ; then
+ scon -d$device -p8,60,60,60
+ fi
+ done
+
+# switch to screen 0
+
+ echo " switching to screen 0"
+ scon -d /dev/ttyv0
+
+# set screensaver timeout
+
+ if [ "X${pcvt_blanktime}" != X"NO" ]; then
+ echo " setting screensaver timeout to $pcvt_blanktime seconds"
+ scon -d /dev/ttyv0 -t$pcvt_blanktime
+ fi
+
+# setup keyboard for national keyboard layout
+
+ if [ "X${pcvt_keymap}" != X"NO" ]; then
+ echo " switching national keyboard layout to $pcvt_keymap"
+ kcon -m $pcvt_keymap
+ fi
+
+# setup keyboard repeat delay value
+
+ if [ "X${pcvt_keydel}" != X"NO" ]; then
+ echo " setting keyboard delay to $pcvt_keydel"
+ kcon -d$pcvt_keydel
+ fi
+
+# setup keyboard repeat rate value
+
+ if [ "X${pcvt_keyrate}" != X"NO" ]; then
+ echo " setting keyboard repeat rate to $pcvt_keyrate"
+ kcon -r$pcvt_keyrate
+ fi
+
+ echo "finished configuring pcvt console driver"
+fi
+
+# EOF
diff --git a/usr.sbin/pcvt/Misc/Etc/xmodmap-german b/usr.sbin/pcvt/Misc/Etc/xmodmap-german
new file mode 100644
index 0000000..61bd100
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Etc/xmodmap-german
@@ -0,0 +1,117 @@
+! German keyboard with a programmer-like mapping,
+! J"org Wunsch <joerg_wunsch@uriah.sax.de>
+!
+! (``programmer-like'' stands for having brackets and braces on diaeresis
+! keys, whereas the ModeShifted keys result in umlauts)
+!
+! Note: the modifier keys are commented out, since they are remapped
+! within Xconfig to match internationalization requirements
+!
+keycode 8 =
+keycode 9 = Escape
+keycode 10 = 1 exclam
+keycode 11 = 2 quotedbl twosuperior
+keycode 12 = 3 paragraph threesuperior
+keycode 13 = 4 dollar
+keycode 14 = 5 percent
+keycode 15 = 6 ampersand
+keycode 16 = 7 slash braceleft
+keycode 17 = 8 parenleft bracketleft
+keycode 18 = 9 parenright bracketright
+keycode 19 = 0 equal braceright
+keycode 20 = ssharp question backslash
+keycode 21 = apostrophe grave
+keycode 22 = Delete
+keycode 23 = Tab
+keycode 24 = q Q at
+keycode 25 = W
+keycode 26 = E
+keycode 27 = R
+keycode 28 = T
+keycode 29 = Z
+keycode 30 = U
+keycode 31 = I
+keycode 32 = O
+keycode 33 = P
+keycode 34 = bracketright braceright udiaeresis Udiaeresis
+keycode 35 = plus asterisk asciitilde
+keycode 36 = Return
+! keycode 37 = Control_L
+keycode 38 = A
+keycode 39 = S
+keycode 40 = D
+keycode 41 = F
+keycode 42 = G
+keycode 43 = H
+keycode 44 = J
+keycode 45 = K
+keycode 46 = L
+keycode 47 = backslash bar odiaeresis Odiaeresis
+keycode 48 = bracketleft braceleft adiaeresis Adiaeresis
+keycode 49 = asciicircum degree
+! keycode 50 = Shift_L
+keycode 51 = numbersign apostrophe
+keycode 52 = Y
+keycode 53 = X
+keycode 54 = C
+keycode 55 = V
+keycode 56 = B
+keycode 57 = N
+keycode 58 = m M mu
+keycode 59 = comma semicolon
+keycode 60 = period colon
+keycode 61 = minus underscore
+! keycode 62 = Shift_R
+keycode 63 = KP_Multiply
+! keycode 64 = Alt_L Meta_L
+keycode 65 = space
+! keycode 66 = Caps_Lock
+keycode 67 = F1
+keycode 68 = F2
+keycode 69 = F3
+keycode 70 = F4
+keycode 71 = F5
+keycode 72 = F6
+keycode 73 = F7
+keycode 74 = F8
+keycode 75 = F9
+keycode 76 = F10
+! keycode 77 = Num_Lock
+keycode 78 = Multi_key
+keycode 79 = KP_7
+keycode 80 = KP_8
+keycode 81 = KP_9
+keycode 82 = KP_Subtract
+keycode 83 = KP_4
+keycode 84 = KP_5
+keycode 85 = KP_6
+keycode 86 = KP_Add
+keycode 87 = KP_1
+keycode 88 = KP_2
+keycode 89 = KP_3
+keycode 90 = KP_0
+keycode 91 = KP_Decimal
+keycode 92 = X386Sys_Req
+keycode 93 =
+keycode 94 = less greater bar
+keycode 95 = F11
+keycode 96 = F12
+keycode 97 = Home
+keycode 98 = Up
+keycode 99 = Prior
+keycode 100 = Left
+keycode 101 = Begin
+keycode 102 = Right
+keycode 103 = End
+keycode 104 = Down
+keycode 105 = Next
+keycode 106 = Insert
+keycode 107 = Delete
+keycode 108 = KP_Enter
+! keycode 109 = Control_R
+keycode 110 = Pause
+keycode 111 = Print
+keycode 112 = KP_Divide
+! keycode 113 = Alt_R Meta_R
+keycode 114 = Break
+
diff --git a/usr.sbin/pcvt/Misc/Makefile b/usr.sbin/pcvt/Misc/Makefile
new file mode 100644
index 0000000..6eff974
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+NOOBJ= noobj
+FILES= README.FIRST
+SUBDIR= Doc Etc
+
+.include "Makefile.inc"
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pcvt/Misc/Makefile.inc b/usr.sbin/pcvt/Misc/Makefile.inc
new file mode 100644
index 0000000..c7a2ebc
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Makefile.inc
@@ -0,0 +1,4 @@
+# $FreeBSD$
+
+DISTRIBUTION= doc
+BINDIR= ${SHAREDIR}/pcvt
diff --git a/usr.sbin/pcvt/Misc/README.FIRST b/usr.sbin/pcvt/Misc/README.FIRST
new file mode 100644
index 0000000..a2701c7
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/README.FIRST
@@ -0,0 +1,184 @@
+================================================================================
+| |
+| P C V T - VT220 Terminal Emulation Driver |
+| ------------------------------------------- |
+| |
+| (c) Copyright 1992, 2000 by |
+| |
+| Hellmuth Michaelis |
+| Hallstr.20 |
+| Rellingen |
+| Europe |
+| |
+| For the contributors copyrights which apply to parts of the source |
+| see the header sections of the respective source files. |
+| |
+================================================================================
+
+Written by: Hellmuth Michaelis (hm@freebsd.org)
+
+The major contributors to pcvt are Brian and Joerg, pcvt would not be what it
+is without the help, the support and the code from Joerg:
+
+ Brian Dunford-Shore (brian@athe.wustl.edu)
+
+ wrote parts of the EGA/VGA hardware handling and
+ some of the more ugly aspects of the VT220.
+
+ Joerg Wunsch (joerg_wunsch@uriah.heep.sax.de)
+
+ added ALL support for XFree86, the screensaver sub-
+ system and support for FreeBSD (and much more ...).
+
+
+I have to thank the following people for their help, for beta-testing, bugfixes,
+code, keymaps, suggestions, hints, patience and too much more to mention:
+
+ Scott Turner (scotty@gagetalker.com)
+ Peter Galbavy (peter@wonderland.org)
+ Michael Havemester (tik@abqhh.hanse.de)
+ Gordon L. Burditt (gordon@sneaky.lonestar.org)
+ Bruce Evans (bde@runx.oz.au)
+ Heiko W. Rupp (hwr@pilhuhn.ka.sub.org)
+ Carsten Lutz (clu@malihh.hanse.de)
+ Christian Rohrmueller (internal@doitcr.doit.sub.org)
+ Andy Duplain (duplain@rtf.bt.co.uk)
+ Marko Karppinen (dreamer@purkki.apu.fi)
+ Onno van der Linden (c/o frank@fwi.uva.nl)
+ Dave Nelson (dcn@ignatz.acs.depaul.edu)
+ Mark Weaver (Mark_Weaver@brown.edu)
+ John Brezak (brezak@apollo.hp.com)
+ Jan-Oliver Neumann (jan@encap.hanse.de)
+ Kim Andersen (kim@dde.dk)
+ Michael Graff (explorer@iastate.edu)
+ Randy Terbush (randyt@cse.unl.edu)
+ Benjamin Lewis (blewis@vet.vet.purdue.edu)
+ Daniel Harris (daniel@reubio.apana.org.au)
+ Alistair G. Crooks (agc@uts.amdahl.com)
+ Szabolcs Szigeti (pink@bagira.fsz.bme.hu)
+ Charles Hannum (mycroft@gnu.ai.mit.edu)
+ Thomas Gellekum (thomas@ghpc8.ihf.rwth-aachen.de)
+ Matthieu Herrb (matthieu@laas.fr)
+ John A. Perry (perry@jpunix.com)
+ John Kohl (jtk@kolvir.blrc.ma.us)
+ Brian Moore (ziff@eecs.umich.edu)
+ Martin Husemann (martin@euterpe.owl.de)
+ Lon Willett (willett@math.utah.edu)
+ Mark Willey (mwilley@mipos2.intel.com)
+ Bill Sommerfeld (sommerfeld@orchard.medford.ma.us)
+ Rafal Boni (r-boni@uiuc.edu)
+ Thomas Eberhardt (thomas@mathematik.uni-bremen.de)
+
+
+History (see also Doc/ChangeLog)
+--------------------------------------------------------------------------------
+
+Release Changes/Features
+------------- ----------------------------------------------------------------
+1.00 (08/92) Released as "pccons_vga" to alt.sources, VT100 emulation
+
+2.00 (01/93) VT220 emulation, many bugfixes and enhancements
+
+2.10 (03/93) Fixed bugs, monochrome environments, configuration changes
+
+2.20 (10/93) never released (experimental internal release)
+
+3.00 (03/94) Support for XFree86 >= 1.2, support for XFree86 2.0's
+ syscons/USL model for multiple X servers and/or terminal
+ sessions from Joerg Wunsch (Thank You, Joerg !!!), fixed
+ bugs, (n)curses-based EGA/VGA font editor, memory mapped
+ screens, full MDA/CGA virtual screen support, 132 columns
+ on some super VGA chipsets, support for NetBSD >= 0.9,
+ support for FreeBSD >= 1.0 and much more ....
+ (posted to comp.sources.misc, Volume 41, Issue 140-152)
+
+3.10 (08/94) never released (experimental internal release)
+
+3.20 Fast forward/backward scrolling from Michael Havemester,
+ further optimization by Charles Hannum. Keyboard queueing
+ for silo overflow minimization also from Michael.
+ Many bugfixes, cleanups and enhancements.
+ Support for NetBSD 1.0 and FreeBSD 2.0.
+
+
+Features
+--------------------------------------------------------------------------------
+
+The 'pcvt' VT220 emulator driver has:
+
+ - Almost full DEC VT220 (VT100/VT102) Terminal functionality
+ - support for XFree86 >= 1.2 using the pccons model
+ - full multiple virtual screen / multiple X-server support
+ for XFree86 >= 2.0 using the USL-VT/syscons model
+ - Full Support for MDA, CGA, EGA and VGA display adaptors
+ - configurable number of virtual screens on any video board
+ - completely independent virtual terminals for any video board
+ - (24), 25, 28, 40, or 50 lines for each virtual screen on VGA's
+ - (24), 25, 35, or 43 lines for each virtual screen on EGA's
+ - Fully remappable keyboard to support national keyboards
+ - All VT220 character sets plus ISO Latin-1 and DEC Technical supported
+ - VT220 downloadable character set supported when run on EGA/VGA
+ - VT220 user defined keys for each virtual terminal
+ - Optional function key label support a 'la Hewlett-Packard
+ - Display function codes (0x00-0x1f/0x90-0xaf) functionality
+ - Optional screen-saving feature
+ - 132 column operation on several VGA chipsets:
+ o Tseng Labs ET3000 and ET4000
+ o Western Digital WD90C11
+ o Trident TVGA9000, TVGA8900B, TVGA8900C, TVGA8900CL
+ o Video 7 1024i
+ o S3 80C928 (board dependent)
+ o Cirrus Logic GD542x (board dependent)
+
+What it cannot:
+
+ - No double wide/high characters
+ - No softscroll
+ - No inverse background
+ - No VT220 printer output support
+ - No VT52 support at all
+ - No 8-bit controls
+ - Only limited AT-keyboard (84 keys) support
+
+
+The entire pcvt package consists of:
+
+ - the VT220 emulating driver itself
+ - complete documentation for installation and operation
+ - termcap/terminfo, pcvt.el, rc.local, /etc/ttys, xmodmap examples
+ - cursor: utility to set the cursor size and shape
+ - fed: curses-based EGA/VGA character set editor
+ - fontedit: utility to edit the vt220 downloadable character set
+ - ispcvt: utility to display the drivers compile time configuration
+ - kcon: utility to setup national keyboard layouts and remap keys
+ - keycap: keyboard mapping database library similar to termcap
+ - loadfont: utility to load up to 4/8 fonts into an EGA/VGA board
+ - mcon: utility to control/configure a keyboard based mouse emulator
+ - scon: utility to runtime configure the video part of pcvt
+ - userkeys: utility to set the VT220 user programmable function keys
+ - vttest: VT100 compatibility torture test program
+ - some color- characterset- and attribute demos
+ - vga and keyboard register-level debugging utilities
+
+
+WYSIWYG - What You Share Is What You Get
+--------------------------------------------------------------------------------
+
+PLEASE, if you fix bugs, add features, hack this driver to work on your
+hardware or simply don't get it up and running, get in contact with me!
+
+ Help us to avoid reinventing the wheel over and over again!
+ -----------------------------------------------------------
+
+The code is far from being perfect, YOU are very welcome to enhance it !
+Please mail bug reports/fixes, suggestions, enhancements & diffs to
+
+ hm@freebsd.org
+
+I will support this driver as my time permits it, feel free to contact me!
+
+Have fun!
+
+Hellmuth
+
+$FreeBSD$
diff --git a/usr.sbin/pcvt/cursor/Makefile b/usr.sbin/pcvt/cursor/Makefile
new file mode 100644
index 0000000..fb06878
--- /dev/null
+++ b/usr.sbin/pcvt/cursor/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+PROG= cursor
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pcvt/cursor/cursor.1 b/usr.sbin/pcvt/cursor/cursor.1
new file mode 100644
index 0000000..6e31d4b
--- /dev/null
+++ b/usr.sbin/pcvt/cursor/cursor.1
@@ -0,0 +1,75 @@
+.\" Copyright (c) 1992, 2000 Hellmuth Michaelis
+.\"
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" Last Edit-Date: [Mon Mar 27 16:33:23 2000]
+.\"
+.\" $FreeBSD$
+.\"
+.Dd March 27, 2000
+.Dt CURSOR 1
+.Os
+.Sh NAME
+.Nm cursor
+.Nd set cursor shape for the pcvt VT220 video driver
+.Sh SYNOPSIS
+.Nm
+.Op Fl d Ar device
+.Op Fl n Ar screenno
+.Op Fl s Ar lineno
+.Op Fl e Ar lineno
+.Sh DESCRIPTION
+The
+.Nm
+utility allows the user to set the cursor shape in a given virtual screen
+of the above mentioned driver.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl d
+Specifies a device for which the cursor shape is set.
+.It Fl n
+Sets the virtual screen number to apply the following parameters to.
+Not
+specifying this parameter implies the current virtual screen or the screen
+referenced by the -d parameter.
+.It Fl s
+Specifies the starting (top) scanline the cursor should have.
+.It Fl e
+Specifies the last (bottom) scanline the cursor should have.
+.El
+.Pp
+Be aware of the fact that the parameters need to be adjusted for the current
+size of the characterfont in use, on EGA and VGA boards sizes of 8, 14 and
+16 scanlines are currently supported.
+.Sh EXAMPLES
+The command
+.Dq Li cursor -s3 -e10
+sets the cursor on the current virtual screen to a rectangular shape on a
+14 line VGA screen.
+.Sh BUGS
+No known bugs
+.Sh SEE ALSO
+.Xr loadfont 1 ,
+.Xr scon 1 ,
+.Xr pcvt 4
diff --git a/usr.sbin/pcvt/cursor/cursor.c b/usr.sbin/pcvt/cursor/cursor.c
new file mode 100644
index 0000000..01bdd10
--- /dev/null
+++ b/usr.sbin/pcvt/cursor/cursor.c
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 1992, 1995 Hellmuth Michaelis
+ *
+ * Copyright (c) 1992, 1994 Brian Dunford-Shore
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by
+ * Hellmuth Michaelis and Brian Dunford-Shore
+ * 4. The name authors may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+static char *id =
+ "@(#)cursor.c, 3.20, Last Edit-Date: [Tue Apr 4 12:27:54 1995]\n$FreeBSD$";
+
+/*---------------------------------------------------------------------------*
+ *
+ * history:
+ *
+ * -hm adding option -d <device>
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <err.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <machine/pcvt_ioctl.h>
+#include <unistd.h>
+#include <paths.h>
+
+#define DEFAULTFD 0
+
+main(argc,argv)
+int argc;
+char *argv[];
+{
+ struct cursorshape cursorshape;
+ int fd;
+ int c;
+ int screen = -1;
+ int start = -1;
+ int end = -1;
+ int dflag = -1;
+ char *device;
+
+ while( (c = getopt(argc, argv, "d:n:s:e:")) != -1)
+ {
+ switch(c)
+ {
+ case 'd':
+ device = optarg;
+ dflag = 1;
+ break;
+
+ case 'n':
+ screen = atoi(optarg);
+ break;
+
+ case 's':
+ start = atoi(optarg);
+ break;
+
+ case 'e':
+ end = atoi(optarg);
+ break;
+
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if(start == -1 || end == -1)
+ usage();
+
+ if(dflag == -1)
+ {
+ fd = DEFAULTFD;
+ }
+ else if((fd = open(device, O_RDWR)) == -1)
+ err(1, "ERROR opening %s", device);
+
+ if(screen == -1)
+ {
+ struct stat stat;
+
+ if((fstat(fd, &stat)) == -1)
+ err(1, "ERROR opening %s", device);
+ screen = minor(stat.st_rdev);
+ }
+
+ cursorshape.start = start;
+ cursorshape.end = end;
+ cursorshape.screen_no = screen;
+
+ if(ioctl(fd, VGACURSOR, &cursorshape) == -1)
+ err(1, "cursor - ioctl VGACURSOR failed, error");
+ else
+ exit(0);
+}
+
+usage()
+{
+ fprintf(stderr,"\ncursor - set cursor shape for pcvt video driver\n");
+ fprintf(stderr,"usage: cursor -d [device] -n [no] -s [line] -e [line]\n");
+ fprintf(stderr," -d <device> device to use (%svX), default current\n", _PATH_TTY);
+ fprintf(stderr," -n <no> screen no if specified, else current screen\n");
+ fprintf(stderr," -s <line> start scan line (topmost scan line)\n");
+ fprintf(stderr," -e <line> ending scan line (bottom scan line)\n\n");
+ exit(1);
+}
+
diff --git a/usr.sbin/pcvt/demo/Makefile b/usr.sbin/pcvt/demo/Makefile
new file mode 100644
index 0000000..ee2d1ef
--- /dev/null
+++ b/usr.sbin/pcvt/demo/Makefile
@@ -0,0 +1,56 @@
+# $FreeBSD$
+
+PROG= playvt
+NOMAN= #true
+SRCS= playvt.c
+
+DEMOS= chardemo.vt colors.vt sgr.vt
+DEMOS+= outerlimit.vt twzone.vt cowscene.vt xmas.vt
+CLEANFILES+= ${DEMOS}
+
+all: $(DEMOS) $(PROG)
+
+install:
+ @${ECHO} "to look at the demos, execute:"
+ @${ECHO} " \"cat <filename>.vt\""
+ @${ECHO} "if it is an animation and it runs too fast, try out:"
+ @${ECHO} " \"playvt -f <filename>.vt -d<some-delay-val>\""
+
+.include <bsd.prog.mk>
+
+# this seems to be the lowest common denominator
+
+chardemo.vt: ${.CURDIR}/${.TARGET}.gz.uu
+ uudecode ${.CURDIR}/${.TARGET}.gz.uu
+ gunzip -f ${.TARGET}.gz
+ rm -f ${.TARGET}.gz
+
+colors.vt: ${.CURDIR}/${.TARGET}.gz.uu
+ uudecode ${.CURDIR}/${.TARGET}.gz.uu
+ gunzip -f ${.TARGET}.gz
+ rm -f ${.TARGET}.gz
+
+sgr.vt: ${.CURDIR}/${.TARGET}.gz.uu
+ uudecode ${.CURDIR}/${.TARGET}.gz.uu
+ gunzip -f ${.TARGET}.gz
+ rm -f ${.TARGET}.gz
+
+cowscene.vt: ${.CURDIR}/${.TARGET}.gz.uu
+ uudecode ${.CURDIR}/${.TARGET}.gz.uu
+ gunzip -f ${.TARGET}.gz
+ rm -f ${.TARGET}.gz
+
+xmas.vt: ${.CURDIR}/${.TARGET}.gz.uu
+ uudecode ${.CURDIR}/${.TARGET}.gz.uu
+ gunzip -f ${.TARGET}.gz
+ rm -f ${.TARGET}.gz
+
+outerlimit.vt: ${.CURDIR}/${.TARGET}.gz.uu
+ uudecode ${.CURDIR}/${.TARGET}.gz.uu
+ gunzip -f ${.TARGET}.gz
+ rm -f ${.TARGET}.gz
+
+twzone.vt: ${.CURDIR}/${.TARGET}.gz.uu
+ uudecode ${.CURDIR}/${.TARGET}.gz.uu
+ gunzip -f ${.TARGET}.gz
+ rm -f ${.TARGET}.gz
diff --git a/usr.sbin/pcvt/demo/README b/usr.sbin/pcvt/demo/README
new file mode 100644
index 0000000..5e33f54
--- /dev/null
+++ b/usr.sbin/pcvt/demo/README
@@ -0,0 +1,20 @@
+- The files "chardemo.vt" and "colors.vt" are taken from the MSDOS Kermit
+ distribution and are redistributed with permission from Frank da Cruz.
+
+- cat chardemo.vt - displays all available character sets
+
+- cat colors.vt - displays all available back/foreground color combinations
+
+- cat sgr.vt - displays all possible graphic renditions
+
+- all other files are some VT100 animations collected over the time from
+ unkown sources, play them with playvt.
+
+- Playvt is a program to play an animation file on pcvt: because pcvt is
+ _much_ faster than an original VT100/VT220, it adds a programmable delay
+ for each character so one can enjoy smooth-running animations! :-)
+ There is no manpage available for playvt, just try to run playvt -? which
+ should give sufficient information. You have to empirically find out which
+ delay value fit's your needs!
+
+have fun!
diff --git a/usr.sbin/pcvt/demo/chardemo.vt.gz.uu b/usr.sbin/pcvt/demo/chardemo.vt.gz.uu
new file mode 100644
index 0000000..f26c6fc
--- /dev/null
+++ b/usr.sbin/pcvt/demo/chardemo.vt.gz.uu
@@ -0,0 +1,54 @@
+# $FreeBSD$
+begin 644 chardemo.vt.gz
+M'XL("):V^BX"`V-H87)D96UO+G9T`*7829,=5Q5%X3D1X!]0DZ(1($`F[VLR
+M\UI"IF3`1V",<=DT%IT0!MF6)6%D^N:OLW*OO!',J<E>\09U[N2;Y,6CBP>'
+MVZT^^YE+_MY\^.*#9T\?/KF\O+I^[?[][:<W^.GIK79Y>?WI\\O7/^&7Z^>/
+MC,MWW[J^WO;RG?<?/=[VZLF+6V\_^]A_]K]_]T9<C;AQWF,:O]P9<7=$V__1
+MX7@ZS\O_'Q</YMN'NIQ>H9:MVE;K5H>M^E;'K=JTY2G9MCPG#UO.R>.62_*T
+MY9H\;]F3VZF64VV[U7*K;<=:CK7M6LNUPW:MY=IAN]9R[=73X?'%\KGMT7/E
+MQ7-]?GON7%_8WCK7%_/0N;Z45\YU(T^<Z\MYWUQ?R>/F^FI>-M?-/&NNK^5-
+M<WT]#YKK&WG-7+?RE+E>SCOF^N9V>ZEIN[U4VVXO==AN+W7,[:5.N;W4.;>7
+MFG-[J26WEUIS>ZF>VTN]DMM+W<[MI>[D]E+?RNVE[N;V4J]NM]?Z]G9[K:OM
+M]EKWMMMKO9;;:WTGM]?Z;FZO];W<7NOUW%ZK<GNM^[F]UO=S>ZT?Y/9:;^3V
+M6C_,[;7>S.VU?K3=[O76=KO7C[?;O=[>;O>ZSNU>[^1VKW=SN]=/<KO73W.[
+MU\]RN]?/<[O7>[G=ZT%N]_I%;O?Z96[W^E5N]_KU=KM-]9OM./MPN\[^=CO/
+M/LI]XG=Y`/%^7D#\/D\@_I`W$(_S".*#O(+X,,\@/LH[B"=Y"/%Q7D(\S5.(
+M9WE+J^=Y2ZL_YBVM/LE;6OW)M[1ZX5M:?>I;6OW9M[3ZBV]I]5??TNIOOJ75
+MWWU+JW_XEE;_]"VM_N5;6OW;M[3ZSTL7:TP\N;AY;V?1SKI@`X.-#%8:A#8(
+M<1#J(.1!Z(,0"*$00B*$1@B1$"HAPJ3-.F$#A8T45BJ$5@BQ$&HAY$+HA1`,
+MH1A",H1F"-$0JB'"IBVZ80.'C1Q6.H1V"/$0ZB'D0^B'$!"A($)"A(8($1$J
+M(L*HK3IB`XF-)%9*A)8(,1%J(N1$Z(D0%*$H0E*$I@A1$:HB9-5W5GUGU7=6
+M?;#J@U4?K/I@U0>K/ECUP:H/5GVPZH-5'ZSZ8-5E=9ADQ885&U:LK`A9$;(B
+M9$7(BI`5(2M"5H2L"%D1LB)D1835K:L=U.$D*#:@V(!B!44(BA`4(2A"4(2@
+M"$$1@B($10B*$!0A*"*@#F=!L0'%!A0K*$)0A*`(01&"(@1%"(H0%"$H0E"$
+MH`A!$0%UF`7%!A0;4*R@"$$1@B($10B*$!0A*$)0A*`(01&"(@1%!-1A$10;
+M4&Q`L8(B!$4(BA`4(2A"4(2@"$$1@B($10B*$!014(=54&Q`L0'%"HH0%"$H
+M0E"$H`A!$8(B!$4(BA`4(2A"4(2@^@ZJ[Z#Z#JH/4'V`Z@-4'Z#Z`-4'J#Y`
+M]0&J#U!]@.H#5!^@NJ!NWCCOHHY'1;$1Q484JRA"482B"$41BB(412B*4!2A
+M*$)1A*((11$1=3PIBHTH-J)811&*(A1%*(I0%*$H0E&$H@A%$8HB%$4HBHBH
+MXUE1;$2Q$<4JBE`4H2A"482B"$41BB(412B*4!2A*$)11$0=9T6Q$<5&%*LH
+M0E&$H@A%$8HB%$4HBE`4H2A"482B"$41$75<%,5&%!M1K*((11&*(A1%*(I0
+M%*$H0E&$H@A%$8HB%$5$U'%5%!M1;$2QBB(412B*4!2A*$)1A*((11&*(A1%
+M*(I0%*&H:0=U.@B*#2@VH%A!$8(B!$4(BA`4(2A"4(2@"$$1@B($10B*"*C3
+M45!L0+$!Q0J*$!0A*$)0A*`(01&"(@1%"(H0%"$H0E!$0)U.@F(#B@TH5E"$
+MH`A!$8(B!$4(BA`4(2A"4(2@"$$1@B("ZG06%!M0;$"Q@B($10B*$!0A*$)0
+MA*`(01&"(@1%"(H0%!%0IUE0;$"Q`<4*BA`4(2A"4(2@"$$1@B($10B*$!0A
+M*$)01$"=%D&Q`<4&%"LH0E"$H`A!$8(B!$4(BA`4(2A"4(2@"$$1@KJS@SHW
+M0;$!Q084*RA"4(2@"$$1@B($10B*$!0A*$)0A*`(01$!=3X(B@TH-J!801&"
+M(@1%"(H0%"$H0E"$H`A!$8(B!$4(B@BH\U%0;$"Q`<4*BA`4(2A"4(2@"$$1
+M@B($10B*$!0A*$)01$"=3X)B`XH-*%90A*`(01&"(@1%"(H0%"$H0E"$H`A!
+M$8(B`NI\%A0;4&Q`L8(B!$4(BA`4(2A"4(2@"$$1@B($10B*$!014.=94&Q`
+ML0'%"HH0%"$H0E"$H`A!$8(B!$4(BA`4(2A"4(2@[HZ/==/^M6[:/]=-^_>Z
+M:7RPF\87NVE\LIO&-[MI?+2;QE>[:7RVF\9WNVE\N)O&E[MI?+J;QK>[25!S
+M$Q0;4&Q`L8(B!$4(BA`4(2A"4(2@"$$1@B($10B*$!014/-!4&Q`L0'%"HH0
+M%"$H0E"$H`A!$8(B!$4(BA`4(2A"4$1`S4=!L0'%!A0K*$)0A*`(01&"(@1%
+M"(H0%"$H0E"$H`A!$0$UGP3%!A0;4*R@"$$1@B($10B*$!0A*$)0A*`(01&"
+M(@1%!-1\%A0;4&Q`L8(B!$4(BA`4(2A"4(2@"$$1@B($10B*$!0AJ#9`]1U4
+MWT'U'50?H/H`U0>H/D#U`:H/4'V`Z@-4'Z#Z`-4'J#Y`]?UK^+1_#I_V[^'3
+M_D%\&E_$I_%)?!K?Q*?Q47P:7\6G\5E\&M_%I_%A?!I?QJ?Q:7P:W\:G\7%\
+M$M32!,4&%!M0K*`(01&"(@1%"(H0%"$H0E"$H`A!$8(B!$4$U'(0%!M0;$"Q
+M@B($10B*$!0A*$)0A*`(01&"(@1%"(H0%!%0RU%0;$"Q`<4*BA`4(2A"4(2@
+M"$$1@B($10B*$!0A*$)01$`M)T&Q`<4&%"LH0E"$H`A!$8(B!$4(BA`4(2A"
+64(2@"$$1?CT_'&^W^B\8)0@*\AL``,4&
+`
+end
diff --git a/usr.sbin/pcvt/demo/colors.vt.gz.uu b/usr.sbin/pcvt/demo/colors.vt.gz.uu
new file mode 100644
index 0000000..456e6dc
--- /dev/null
+++ b/usr.sbin/pcvt/demo/colors.vt.gz.uu
@@ -0,0 +1,16 @@
+# $FreeBSD$
+begin 644 colors.vt.gz
+M'XL("):V^BX"`V-O;&]R<RYV=`#MU+UNG$`4!>!^I7T"FGD`Q^+^S)TB2N-8
+M;B(GA=UMM;*1C;(8":\E/W[,G(.SX0E2+,WE(#%\@N$T._.O7H;MYO;NR_6O
+MN_2CFX;^F.RR3??=^S'=CH]=^CX>QNGU(ME[^I9NQJE[FL:WE\>+Y/.%J_W#
+M;US8;K:;A*/963LO_'&+MVF^TP5#,0S#,3)&8)24MIMEA63MLEP[I#EYBRB,
+M@JB,BFB,ANB,CI@9,V(PQN=3:RRG#*D,(4/`$#($#"%#P!`R!`PA0\`0,@0,
+M(4/`$#*DOH_T<YR&_>'4H]6C]"@\2H_"H_0H/$J/PJ/T*#Q*C\*C]"@\2H_"
+M\W<;G)JLFHPF@\EH,IB,)H/):#*8C":#R6@RF(PF@\EHLM6G\LIP,AP,)\/!
+M<#(<#"?#P7`R'`PGP\%P,AP,)\-7C%P9F8P,1B8C@Y')R&!D,C(8F8P,1B8C
+M@Y')R&!D,O**$9419`0804:`$60$&$%&@!%D!!A!1H`19`0804:L&*4R"AD%
+MC$)&`:.04<`H9!0P"AD%C$)&`:.04<`H9)1_&/?/_6M:_BZ<UHV]3\MV&MX.
+MQW[YJ`]S\Z7EW1[ZEVY9Z++YK,YF)\.YKU9]=37U3\_'<U^=^^K<5_]57S6[
+-]J.M_@#AP6KE^0D``&Y9
+`
+end
diff --git a/usr.sbin/pcvt/demo/cowscene.vt.gz.uu b/usr.sbin/pcvt/demo/cowscene.vt.gz.uu
new file mode 100644
index 0000000..7a5a4dbb
--- /dev/null
+++ b/usr.sbin/pcvt/demo/cowscene.vt.gz.uu
@@ -0,0 +1,90 @@
+begin 644 cowscene.vt.gz
+M'XL(".BA?2H"`V-O=W-C96YE+G9T`.U=P8[CQA$]&MA?F`N!R6%6B"QU=9.4
+ML`?;"1((1GP*XLL(&!C916QDXPT<(-Z#H&\/V5U-=C<?BV4@<)RD%XA":JA^
+M[W635:\DLOSP3%\^/']&[?N'Y_X-N1]>/3R;XQMS>3UNF+A!PT8S;MAQPV^Y
+M::N-?^R&C=WVOX>GXU]_?/_P])OM0R.;EXG.AXG/`1&*?*8/)I^</WJ=/WN;
+M/WR;Y=R;.,!3,L)3,L0^'2,9Y'!-1IF':=)QFFR@=*0F'6I_2Q@UZ6#Y:,5P
+MV7CY@/N479,/68Q9#IJ/6@R;,RT'+D=>#%V,G0U^W>>L[\OA%^,O`?;YBN<0
+MMUNAX(Y`EB@9S&&!TRR`FD.IIL%0`"L#:Y9H!P#7+)0U:X`(,8<$F`T`W35`
+M90[;)Q#*?R5;2+?@BP@CQLT.3M3,F=$)H1-")X1.")T\>C%AA-`M0K<(W2)T
+MB]`MTFX1ND/H#J$[A.X0ND/:'4)O$7J+T%N$WB+T%FEO$7J'T#N$WB'T#J%W
+M2'N'T'N$WB/T'J'W"+U'VGN$?D+H)X1^0N@GA'Y"VD\(_8S0SPC]C-#/"/V,
+MM)\1NCG"<'.$\>8(`\X11IPCF@`>M^2`0QZ.>3CHX:B'PQZ,>P8&/@,CGX&A
+MS\#89V#P,S#Z&1C^#(Q_!@9``R.@@2'0P!AH8!`T,`H:&`8-C(,&!D(#(Z&!
+MH=#`6&A@,#0P&AH8#@V,AP8&1`,CHH$AT<"8:&!0-#`J&A@6#8R+!@9&`R.C
+M@:'1P-AH8'`T,#H:&!X-C(\&!D@#(Z2!(=+`&&E@D#0P2AH8)@V,DP3C),$X
+M23!.$HR3=$0<:!DG3T/=>OFB^>V''Q^>SV^HN]P"K^[R]?B9P<V-K^1?K7]U
+M_K7UKYU_[?WKR;^>_>L0D/W_A4\/82F,%08P8003AC!A#!,&,6$4$X:A,`PQ
+MB<""[*4RJ\Q^)F;^^O`7(E\?3;P^FCJCE5F]/FK^J,PJLYH_*K/*K.:/RJPR
+MJ_FC,JO,*K/*K#+[.9@-=-SE$'[H;L>-\QMK+U]]^/#IIW5**[/*S%\@3;Q`
+M&KY`IGMCZI169O4"J1FD,JO,:@:IS"JSFD$JL\JL9I#*K#*KS"JSRNS_FYE[
+M8_O+U^%9J-[_0D]C]OJB>?_-]V__]MWW[ZK)J\PJLW"=-/$Z:>)U4CR+66>V
+M,JO72<TGE5EE5O-)95:9U7Q2F55FOY1\<AP_C)Y*AIT3"'9.(-@Y@0QLV8([
+MQN"6,;AG#&X:@[O&$'PR&G9.(-@Y@6#G!(*=$PAV3B`+YP%V3B#8.8%@YP2"
+MG1,(=DX@!^<!=DX@V#F!8.<$@IT3"'9.H!;.`^R<0+!S`L'."00[)Q#LG$`=
+MG`?8.8%@YP2"G1,(=DX@V#F!>C@/R\X)OKW&PZ/]W<>_O__P]KOO_S(^.#.\
+MWX7W7?G^R!EV6B#8:8%@IP6"G1;H!.<M[[3PZI/0%JF[7&^'\6F&(1K]X>'Y
+MJX?GS]IO.2*UE^LP]H%;+;G+-<WC]HTSEP,W5QJ.W.UVAU<Q;EW#1LSXCL*1
+MSH^R&]B%8X<@3/Y8$X\=`K*]E'S>O_(_RIWF=G-'?WSS]/GGL674J([GA<:]
+M89:8]\!F%[:ZR^X6-MWQ,K$]7@XA\-(Y;/D9W(4>#,6LG%(63.GCQT@IYT`!
+MEV9<&J=L4F["W\W\=S-.5/R[&SM"Q+6_-4U<\%M"D6?_Z%N[#8R'_]WO/-'#
+MXAR:G3]?=E<PH7TJY91*.6=*CBF3?F;23TQZ9F)#QHISW?#F\/$KFLN!P#DE
+M<$X)G-.I')\]B[#=!-L%V%+7R?.=ELCO3$ODI:1+=-C')1KF*2[1_N#IN="3
+M(\;.B4([46B7%+X-J"8]5_T><S!^CSF<PD[$<#.&FS#<O,Y39ID6V7(?P/T5
+M+?$(36F/1K_'1/Q5&8F<PTXD,IR:_&!32-97WO'Y,=#Q)[!_T\R+'1!B:S=_
+M6K^*%_5$@\(>T[!^+SG9;,9CHM#$YBG#Z3P##.%I6LJ\M5XX"X=K^Y!,4,1F
+M)C9CXC(F)C*9@'G;]XM)2/27^3)\?`Q@CX_\L8Q`$Z^2XR4CRJN6,&)^+N/7
+M9OQHXA=:E(ZS[1N+YHGHW=L&)J+Y_6K7*[-:?-4O\RJSRNS?<9UP_FG+=-S!
+M][WW2/M:S]UOK].5QXUOKR\O+[]NLI;*8]T3&]Z^A'^WJ=?M4_,ZCO^K"'";
+M$9X!Q`K&`B1!26'N,TX"U$"D-:@E5@J6HC4SW$N&MP*XB@@@,\P4].7>)).9
+MPMX`[@T"WP#R;04ZPWY))#<Y>H/@&XS?(`(OJPQR"HG^_Q2%;!H*$F\QBS4:
+MF(=`I&"234?)Y<,:F54V*W0D/B6A?'(6E%8XO9-(K;$2:2UX%5-5,OO]&K4_
+MR]Q6R<GLEO3*B2L)-JL,O]NBN,YQ@R1@N9C&DN<_UHG^<YNI0!5R?2V374[J
+M@J[`]P<-88DQGMZ"<TD9S'!)^AN)]3L=;9$W)/ZTR1Q-=\G]*Y'\'[7L9?KX
+M7%D(X.\1"A%P`4H9>UE'HQ>RH63E'%IJV0$E>$%*+7_:$O-3U&S)63NU%H*@
+MGI6U603+;4D_2=.F*+Q*I:P]"E&+A;H)PFZ;RFX*:;=-;;=M<:6Z?:JN>5E?
+M-DE?LRVPT2ALMB4V&HV%R"%@3")I(7):0Y)$TK9(THBD;9&T+9(6(L>@.(FT
+MZRMI)9%V6Z35B+3;(NVV2%N*'#7.(MWZ2CI)I-L6Z30BW;9(MRW2%2*]QEED
+MN[Z2K22RW1;9:D2VVR+;;9%M+C)HG+Q(M[Z0G:2QV];8:31VVQJ[;8U=II$E
+M3AK[=8V]I+'?UMAK-/;;&OMMC7VJ,4H<WTA_P[F//[>LB)Y.WO]2T9-F+WJ/
+M1)_65_HDB3YMBSYI1)^V19^V19]FT;/F8+Z!YO/Z0I\ES>=MS6>-YO.VYO.V
+MYG.B>=<L-.>2Q_]&SZH9/(IVZ:CP2T>583HJ'--189F.L_)9^&-47@@W@G#9
+M)VJ,HLXI:JRBQBL"L]@\1N&%;A+LL6@=C<([&I5Y-`KW:!3VT2S]XR!['W07
+MLJVPW**9-`HW:51VTBC\I%$82K-PE*/J(+M0[83%%MVE4=A+H_*71F$PC<)A
+MFM)B>M%>=2&Z%99:=)M&83>-RF\:A>$T"L?I_W-5^T1T<"C>JF2:.V&A1?=I
+M%/;3J/RG41A0HW"@XS'[1#.;LE%S)EGPHD;T949AS(S*F1F%-3,*;S8<$XK[
+M(#G:4+^7*#X)BRR:,J-P949ERXS"EQF%,1N."96^UQ@%>\6)X+.PQ*(C,PI+
+M9E2>S"A,F5&XLN&8W12^)KUCCK[/-^+1<7V%271CI'!CI')CI'!CI'!CE+BQ
+M6>^C7^^HUW:7>_-+ONW!4VSNOWB*=1;K+-99_)^8Q4]\(A"J4Q*K4U)4IZ2J
+M3DE1G9*B.B54G8;OAJ>\)Q2E)/^>H?E!0_>+AN8G#<UO&K0F=U(KU*(DUJ*D
+MJ$5)58N2HA8E12U*%JF]#WO!PI%0@9)8@9*B`B55!4J*"I04%2@YH/4^?3=.
+M0MU)8MU)BKJ35'4G*>I.4M2=U"ZEWN>?`4@H-TDL-TE1;I*JW"1%N4F*<I.Z
+MA=31D4]2A3*3Q#*3%&4FJ<I,4I29I"@SJ2^E^MICDBK4ER36EZ2H+TE57Y*B
+MOB1%?4FG0FJHLB:I0F5)8F5)BLJ25)4E*2I+4E26=,ZE!J635"O4E%:L*:VB
+MIK2JFM(J:DJKJ"GM,9/*2F>I@FNRHFNR"M=D5:[)*ER35;@FF[FFJ'26*C@F
+M*SHFJW!,5N68K,(Q685CLJECFI3.4@6[9.7[0#0W@NCN!-'<"J*Y%R2Q2[/2
+M6:K@EJSHEJS"+5F56[(*MV05;LEF;BDJG:4*;LF*;LDJW))5N26K<$M6X99L
+MZI8FI;-4P2U9T2U9A5NR*K=D%6[)*MR23=S2K'26*K@E*[HEJW!+5N66K,(M
+M685;LHE;.BQO/;:"6[*B6[(*MV15;LDJW))5N"5[6JM9IR?R5U=5=$M6X9:L
+MRBU9A5NR"K=DSZ)4)[@E)[HEIW!+3N66G,(M.85;\CT,[NM2!;?D1+?D%&[)
+MJ=R24[@EIW!+XS%W854%M^1$M^04;LFIW))3N"6G<$O#,7?I!!;<DA/=DE.X
+M):=R2T[AEIS"+86>!^M2!;?DY'MG-3?/ZNZ>=9>G3:GC,5M2!S1?B:](%=R2
+M$]V24[@EIW)+PU$OFU+;R\=-J>TE5.(K4@6WY$2WY!1NR:G<TG#4MM1.(;6[
+M'*1K57!+3G1+3N&6G,HM#4>]WI0Z'K,EM9?SJN"6G.B6G,(M.<DM-9-4R2VQ
+M4LDLL5#9*SG!*SG1*SF%5W*"5V*1@D]BC8)+8HFR1VH%C]2*'JE5>*1VW2--
+M1UF$4ZYENVZ1^';^HRQ3\$>MZ(]:A3]J5_T1"S1;Z]BN^B*69V1Y@B=J14_4
+M*CQ1N^:)$@D6X2Q6<<T2L<BU7]N"1L$,M:(9:E?,4)-J7#-#+,YNKN"*!V)I
+M%DL+R@3OTV+OP\*<0IA3+9[;7CS\/5%L2]@ZJ#`(%!Q/"QT/ZX-^AW5!G\-J
+MX'=!+`-^!\0:6J@AJA#,3`O,S$M4@:P,B^A4B]-M+P[ZNH=%=<+"(-/"BH!G
+M83W]NIY>OHKZS:L(?)?#,GI!!C(D+&/I$UC%THJPAJ7_8/9X*!?^D)-FSB>!
+M\]);,.-%TF?""TL1)WWA(^)T+T:*L[QP#?$B7IB%\#3<PB3PHW!EXN:'X$IG
+MP`^^E6:`'W5#@[CP=@G9^K?7IK0KDCR?`EV1?9EDGM?CPWE%)F>*8`07WEU,
+M85=DZL"LR,],C/#T92F9>64IF%D1GK@LR3(I$F8MRZO,+$UWS"I-HDPJS9G,
+MJ?R@"V\5=/)L&&BD29!9.#P_2=9C(DF.8QX.STV2PYB*D+:Z)&TQHSFQ,)<Y
+M0S&5.2TQD_PC+KR149BS3H"=\PRCQOC/F%,^8<@IB3!B>K@+NRG<E`\"VI0"
+M&*S/P6*P9ZP8X!FJSZ%B]&:D&+(#4`S2C'/*<3@<,PS_D5'"'F-PL&4(CK`!
+M@4,J`YQS@!`^>?SS-*+-#W1A=QH_!,3PZ&B(@ORPZ'$:P?`.CQUV^&G/:=N%
+M;1ZV]U$LC.J#%H>J<8?SD]^,3Y;&M^V\Z>;-UF_.P\5NI\=I;QJQF7JRIGLV
+>.?"2C#BW34UWQQ.;K+_C\C-JOWWU+THZ&OFDJ0``
+`
+end
diff --git a/usr.sbin/pcvt/demo/outerlimit.vt.gz.uu b/usr.sbin/pcvt/demo/outerlimit.vt.gz.uu
new file mode 100644
index 0000000..a2b6d1a
--- /dev/null
+++ b/usr.sbin/pcvt/demo/outerlimit.vt.gz.uu
@@ -0,0 +1,193 @@
+begin 644 outerlimit.vt.gz
+M'XL(",2<V"H"`V]U=&5R;&EM:70N=G0`[9U=C]S(=88O#0R"Y`?H9@#?V#<!
+MZYM<76FU8_>LM1IG-+*\WJL-D@M[K37LK&(;0?Y[2-;[G"*[1^.6+4ZV%RT(
+MF&)WL5AUZCWO^:ABUS]=//G)IT]^VET\^:I[^^0K__F3K\KXU_FGL=M=UH*;
+M"V%1F+_JCJPW%MSN8I-V_4;MAHW:C1NUFS9J-V_4;MFHW7ZC=H>-VG7=5@UO
+MI7%N*Y5S6^F<VTKIW%9:Y[92.[>5WKFM%,]MI7E^UCP_:>#=[NKVZFSVMC9[
+M<=+$ZU=G06\MZ#SI^<N;N]WURY^?I7WVYM;>7/_4Y]V;VYLS-L[8V,>&BV/[
+M;Z[O=F=H;`T-EY\&O_ORYO7M6=:;R[J??/6[JQ=7O[I^=7WS\BSQ,_&MB<]W
+MTP>OKN[.T-@:&G/(-=[V"T4$M3C[K+4X.RBU.-LC%6>^5+F?;OR?IM#_RPQ.
+MWU^<Y_`Q")69\T_]."\_#I_=//DJU'*<RN<YV#J<#K/<QT#OR5>I7L3YXBSY
+M;27?SPSVX_!LE/50R_'9"<K=O9W',J/H[NJ+7];AS#C2Y=3B&4T;<^ELX\89
+MN+F9KM)\%>O5R2&*L;Q9C>7-28_E1DY%FYH3]3*<$==8-N*:7:=*`I^-92<&
+M^*R9^1^'S^L-4SE^/OMB+D^?OQ[+I9;CZRHSUT]?O)K%-9?CJ_D&/S_Y;KYA
+M+L>[LZ/V",[V+.J+4?2SR["\RJNK?G7ENO7E^DZWOM6M[_7K>[V7'NVNQHL@
+M'=J=Z(++.!H_#^>7U\_O7M_6(<V?1/OD;#7/"8:#!,-Y7\T9$_O9]NYIZ'9O
+MSDO/9V3L(\-//M*S4]R5,+L*/]??EQ3FE>C:S/3HN5TJO[BV:B\6Y1LNYF<>
+MW'5[LZQ\M[Y\N;@.]]Y^\W+OEN?MDPN&M7?7U(S[Q?.]6_T]GU9YU;O"O7?%
+M^^]RW)7NO2O?\VE8W%7NO:N__R[KX7#O77.6=O7QV5\_$]4^41$AWMT^>_GJ
+MB^M7=7G-@L7EQV?X;)]+&J=EM!LG:#7\T^QV_M0Z_J-SA[>'QHAIO_OB-#L>
+M=L]/L^-Q=X)+>.<@^^RAW!-*.7\.LA_#^9A3HV^N7[PXR_JLA0=:&'IB[.5C
+M7G+[W<./>;C>(3P^6KM^HW;#1NVBBLGO]A993D/44R3@=[N;V^O?C&AY]H,%
+MBY1BF/RLJ9+?W=;O/``9K%(_>9%3I3#7#E-!E7JK5%#%N'M1*T4J%:N4J93H
+M"H7I*U5*5,I4HC!]I4J12H5*%*:O5"E0J:<2A>DK5?)4&JA$8?I*E9PJ):,K
+M4R1GE6"CA*"M$$SB?J"2U[0D).Y-XKZ?93A6"KM=K83$O4G<%RK%2=&F2DC<
+MF\1]WEW72@C:"MXD/IG-6@E!6\&;Q#T23PC:"MXD[I%X0M!6\"9QC\03@K:"
+M-XE[))Z-R`WE)G&/Q#."MH(WB3LD/NDU\9R]W4>EGDI@/(?V;B&5P/@8GMP2
+MI]B;C50"XSG-H@]3P=ZKI!(8SWGWFUHIM[<ZJ83$<Z%/I;U32J4P3_Y8J9_U
+M/4P%>Z/ULGF@M=(@T.6AO4]+)21>YA3.]);TXFU>*B'QXJ3FQ;5WB54)@1?D
+M;`63-^(N2-D*)FV$79!Q*U`%41<D;`63-((NR-<*)F?$7)"N%1J34`796J&A
+MFBI(U@I-PJK2V^OBS2!<7AC3_ZUJQSWLB"X?,?`CQ'?$)!PQE4<`X@A8+>%Y
+M^3YX+H%^^5Z@(V+3E/M4!B&;SMVG?`LUOGRO&B\(X?*]A+"@ELOW4LN"I"[?
+M2U(+NKM\+]TMB//R?<1Y%`4?1^9'F86C#,Q1I@J)/VCTD/B#YA.)F_V]SQ`C
+M<;/D]YGTA7-P^3[GX"@WXSB'Y2C7YR@GZBAW["C'[B@7<>%L7K[7V43BYJW>
+MY[8B<?-[[W&`_T:8>5Y8.N<;]O(-<;H^)_T>XUV(44W/.;^S#IYS?O_?.;\\
+MJ>))YOQ\-[D-O[JZ/<FNCQ_<79]FS_WN^0\[P^JXT_%=7!14J:-2QW=I4:B5
+M!M49^"9/A:M:Z';5%(Z%Z_EOS<T4ZA9S4]6*?=,O"K5*5I7,-\.B4*LD5:&7
+M?N[WM8'1_)_Y[]Q+S_B]HQ6$BM2\7Q1J%<C>O@F+0F<I$VWK=+]85)W?#`Y[
+M%ZK%PW7+JI;?NVBWQ,4M<5G+[5VT6]+BEK2LU>U=M%ORXI:\J&73P$6[I2QN
+M*<M:_=Y%NZ5?W+*J5?8NVBW#XI957_+>1;O%=8M[W'+,+NU=+&YRRYN6LG5Q
+M[V)QTQ(";CF'+NQ=+.1V]A?/_N(J9NM/,F3+4X;P^;,3_%69?DKL/=\]>_GS
+M<Z1\5L=[WDYRNY-\4;-&GC^[>?YZ]6N3S[GY]<,/>;C>8><_6KM^HW;#1NW&
+MC=I-&[7+;]S%?G=W<WK(&+V[E->[^T^DX_VTO/7JYF=W9W7<6AW'<*:XW:<O
+M7M]>G(5]YK[W_9+_1VNW;-1NOU&[PT;M[O^2_\=K>"N-FW[)_Y_GQ(S;??V1
+M_XT/J$SA_0:M;\ALT^_?G%1_N]/J[_1FZ$GUMS^Q_I83ZV\^L?ZF$^MO/+'^
+MAL?O[]?TX^N'^[NL9_W=W+Y]33^^?KB_#]>S_KH3ZV]W6OT=3JN[_6EUMYQ6
+M=_-I=3>=5G?C:74WG%9W3\VNG1B1G4Z[?J-VPT;MQHW:31NUFTF3C'[P^*^F
+M(,;RS6W=VJ//SZ#9'#13;J*?A7U99V(*_L</7NV>W?[RZF7=R;6L<9Z3[><D
+MSR_5U.E(<_GZKF[?"V?%.+/IO6PZ!HT)T`QS\>YFWFF73A<R\WMVC9KR?/W\
+M]LM7=\]>S-L;TYF8'I68WDX12%Y-2GW]]/F+9[?7=U_.FV'S>5+.1/5^HMJ@
+MW;)1N_U&[0Y;A4/=5@UO%L#9H3SS#P'\\8]__-U$(7VW^\L4WXY_OYD#73]]
+M]1'_U>#Y]S7D_XNMW(;555Q=I=557EV5U56_NAI65W-N=W'IUI?K[KAU?]RZ
+M0V[=([?NDEOWR:T[Y=:]\NM>^76O_+I7\^+YVWH(Q$>>EM_5A\R/G#!0^T*I
+MHS3VGE)OI6*E;*5DI6BE8"5[AK-G.'N&/<*>8`^P]JUY:WV2U'<?5R:7JW_O
+MA-+-'Q(?XR'I,1Z2'^,AY3$>TC_&0X;'>,A,@UL^Y1WDNOE#_&,\)#S&0^)C
+M/"0]QD/R8SRD/,9#^L=XR/`(#_&/H?'^,33>/X;&5R?KOS_JO]^M/;KOOOVH
+M_]ZMW<?M6N^V;+WJPF:M]YNV7C9M/6_:>MJT];AIZV'3UC?55;>IKKI-=753
+M5=U44S=5U$WU=%,UW51+-U72;75T2E7]^:/^^V8A\Y!VWS8GX=OY8/6T>W?9
+MN4_&_]Y??C=A:JK5[O^V_M)$V/E.!3=0Z"D4"IE"HA`I!!IRGH*C0-,=37<T
+MW=%T1]-=HJ&.MKM`@:8[FNXZ_5!&UWV2!NY,-)]*!6,GP<PR&:_>7?[LYK:*
+M8_IN+8E$5Q*C3/0DT9-$3Q(]20PR6C<BW8B,,M)TI.E(TY&F(TU'5SN?=ZWK
+M>7JG61W/NU6W(QT(2#GP_,#S`\\//#_P_&`3&.A`8&R!ICU->YKV-.UIVJ>Y
+MV[[;-9&/%R^O?GU7.SY>[$&//GADX.F"=\?`\^(8?!X#SXMC\'D,/*L(TE($
+M:;>[>2W0C1=K$50<AU)Q?#^4CX'GQ3'X/`:>%\?@\QCD71P#O6.0=W$,](Y!
+MU(74_MT<H_U^-N+3U:OK&:#]/93PO2+'6?4),6OW\^[?7E]?W;WX4@.HU+!&
+MV(>P:>K?RZ;?/XKL5Y,YU*MG+S^;9.&Z>V;S>\64M8?OU'OGZM6;F>3K;U)_
+M^_TERZGS:7?Y5BPW]7Z\G'Y1D^Z+Z!8X_`C4N@?6\@_QY3@&[W8+KVD<Q?B!
+M?OI1XQ@_.-"H,\<^R+$5O*:7=55[5$QA(W[?:?;O8,W\7M;4[Z\O9#%Y<L_N
+M$$7>?8]]4/T&N_5]O/CRYK6Z+LWXGI)K7&-0SO^KJTJO]WG_/TQG=`^HZ1]C
+MS(7[]%W-5BY-[GT.U)DK'^;*A0_S]MN:O)ZN=U?/QHCA1VRE__:'%;.G>$)>
+MYO>/W#X:7<V)Z?HCE^7MR.S:^/7V`#AA'R4-'.6`D<(>_XSS'9;S70XF-^Q/
+M9:.&LB2"^?"9^7C[9[=7>YUML^<.IJH<3$S8GX59K>-T:D25QY[\TX&P35^&
+MNFY3CZ,_4,*'1%CWQ97E$Q\4)]+TDS3M`)PJCT]O7M_M2:1I43C0$!-KO."T
+MF$7WFPC[`\GYI<!\G`XM64HL'PBJ.Y!)U(]GYY7(W`'=I#63C"-WBY$[06'Z
+M3;2_;]QI;[J/'_9\G,JB\T</NW^:ET\\:M#=<M"E#OKJU[^\NKV^>OE\7PV:
+M4KF#,1OHA[ILE%<3T,;J#N`?EF"?CVI:#B(=C*:LP=N-"FV#2-/!,XOG'M_C
+M^3B<.N>[_7$?W?O1PGKWP=WOZ7Z9SB#ZNWK?34=!+9Y[?(_G(W>JFK_9'[?9
+M],;(D4ZOPN-8$TC]2L^'?1O8.#.N`.^F7Q]<ZGD\L"SW=*0ZXCFT9XZ]6L5*
+M\8&>V,D^=>@O/]N?\GA@.._IU;RTXU<D=8^HTOV=JAY:*BNA/20KZTC=`%+\
+M"FG#$9,6%]H^_^ST./8OOGQU=W7[Y;ZJAP/$N7ULS;,W3)NTEVP5#KC)K>EG
+M[$>P?N3IQ*;%,(Y^;B6[#WOLTK9%:?N;W?7SW=\[>/BF^Z!N-$,CNG`?/'KY
+M!!_VW"77)QFXVZMGSW=7K_8%X/<=V6:UW!*&HNM[?)-NG^;"R-+Y8H5^J6^S
+MD?Y@L/V!2Q07L'&'K@UA0!A9U1QC<\Q1_"S.^]GMS1?[FN\.9)K63QD;+R9*
+M:7`#L#M@Z+1/P6[)V?<X*2:RD<Y:C%J68\!+:(Q_3+>3==M-)Z/4A\\_0__V
+MT.HUQ\8=."IA.1MIS]LRQ(6XG/*R#Z4E?S0D=P?BP%D)83FE2Q8\]#/,:3)$
+MR@L:FUG20.A74KA^^7(Z<F7M_`_[G&Q4;*0[^]NR@!W6*+@%\3_8RL(*NCU#
+M,C;3_8UF%E[VH3FP9OQP?S,7'#^WE,,7UP<6T1\D`12ICTWW2YS&A>_>)N8@
+MD'0'D;LOG\0Z,3CP#=T'4:O+^VCSH[;$.J\U\&E6]2#UXO<R$N/=2]V(W5(:
+M\XF2=OWC<'?#69+MPSA]N(?S@[C.$67YN.?R-0H\YFXSGXHIW0?=[1<#S?F#
+M'V[62Y[/ASW<;-#1M[@5;N^YX6)_*MT(QW*0VC."=/T^<MQ(Y_YBJ1'KP+SU
+M)2];#@>*G/;#:9<^B:6-U_#7=`AR<W&J>42]L&[1K9\]5O!+>84#O;&NN872
+M+@;M#Z33?1+[P^?A5';#4B;=P;18.UV_&&&3&?WIRH+=;7H=MJO+:^1T^YIL
+M$NI&F8>#3%;KQBAH,TK>.F1&L@O+;I:#K_W#7[O5]/@#ENI&:<:+?;1/G?>?
+M+PWS@W]/JN*=3@.O)N;!OQ>G5/'F]=W5[<)X/OCWXESQJ(HOKK^XOGO%^;E_
+MZ^_%:534F]&Y'3!>7S[526B#CCNK)X5/BS*<2U9/_/:<ZNT]QW-[CN?V','M
+M.69[NJVG(9K6D=B%8Z]3.]J:,ZH[G5'=ZQCJK).FHPZ3MO.B.1*:4Y\YV)D#
+MFCF?.>H(YJQ3EH=VD')`!`$9U$.//>?.Z02S.M*(%.IAR=-WD>/?$J?)Y=4A
+MSX-.A,XZ/KJ>-3V[ACJ96L=8<_AUT@'9P^)8[2SA)>1:S_7V]3CPZK-G!)O=
+MZF3RS.GE''6N<]$3AZC;V>O#_(L8=:0ZSWW@]/=.9\5SP/SR+/JHONAH^_2T
+MU(:G%^4UH87^%LU8WZDN`%-?U54[HURWU<.MI^,3=&!@7V6IUIQ.Y7-N<0I]
+MQ1T3I3/DLQT&WPFED?/>P:@@&H30`D"S8#FVZNF7SCGWG',^#:27Y`3.U,XD
+MK_#L=;1X$CKM@'`[Z!MX"IV<UIV$S7)P).)\,/==/6(N<&1A1&:1WD;TLT+3
+M<VRYYY#S"5FH?N+LP\3PD_33CG!W@N@@A!8!-`F?'%)O1]N#S1F:\TBS1%"Q
+M&`3%-"-QQNY015:08>$$<N$OS?BKB2@!L`>`PXS`>5@"H!,`ZVO[(BOF4G^#
+M:3LB03MH7?+H=50EIZ0[9L5IGIW7J9%.-.1JDX!1A.F$1*-+(1!J[(T9@Y#G
+MH,2)4`N@9"X]0PP,,:!E.M1^L-/I,^`+G$#?"7U9Z`M0(\?!@SZ!KQX*7X]:
+M#!SG&3F&$]D)>IV`-J$):56@)7!F,!/*(CSHA+*)]9C-BJ_I%UNZ6C=+].*\
+M828]$6325Q*Z6*\(:U-!`RSH0.$X4:%N!MT\HY7T)C"@7,)<%.:B(`?B(I0G
+MP$T$0<^BD"QEDBH($VI%C3@==.HX)U48FXRV41TTY4"J&,Z+XH8&,)&Z*,Z`
+M)JISPEL0W)+0UL-R9H(C+%<PPH-980?1P713`;%5L'GCN@ZX]>`M+\YU<;MK
+MY46\C&N0<156HOHKO(GIG(C.`3^,L!?J@HQO%.BR0-=C>SM1F\/V.LAMG+?<
+M,0<9NY.9C,R(,^#-D&\VHXBG43@RMMBLBN8*B$NP7`!Q3H@;A+@BQ"4A;GJ?
+M2%D2Z;%$(#60.:]596AD9YQ!5OU)0MFT)PK/1^Z?6[A_#,QA5>3U#7A]>4:<
+M*"T(DY*\*$Y0PZ`&`2P+7P-<%HW+>N#E%O#"M1&JL*$<&6PG#@M1;D94C8HQ
+M<A&S$L%JI+$*)\@LPF%)</+S\H:(-@F:2:HD0'7"41:'3<#0"#.><0-1,J3)
+MXHK%!*8`AT%BDPW$P,ES<\)4Y:ZZ=`((-/WRTCHX*S\%F%ZB,+*K/>U%BW)Q
+MA1M82NZ8$WB"L),%G0$[6`,'X0P6</CC'O%XL]B8;(^T/;0L\YAG%-45GHJC
+M03B*PD\0.3DAQ`L@<>\<YX*9FV@(1S?R%-%,#S(&'"HO\^9A&N\7V$BX&@ED
+M)5@PT9!8IN!#99RHN,!'T'S)VO4R=EDPB8))@'.:J=,,F5<?97R2M*`(9Y5C
+MG'GP9L^*8<.&!=[T>0]FX<M&*P*ZX2/)/Q(.G;P_SPW>X=2V*8=^/(%7G>@>
+MT^1P<7I<G*RYKB;FHAV,[3I"O$Z6Q0([YAJ3$C$E0<H?Y@F>999P/I+%,5(P
+MZ;QFUR:W1_<=<YKF.:U3J2G)$+;:JE.:<5]L3@OVQ&*TP7QDI_D,YIW(A\.<
+M`C'U!B('S*ZSH!\K[_##Y=22!ACD?A3-9D"!L0&8@(PST>%#H)MU<57\G4`9
+M5B;0N3#LG8SN%^>I!Z8S<K`\XHJ07))/E:3H%H\3CLD':+,GY=&D22&#YBY)
+M'8MF;C#69NX<^NAL]GK-WF3[;?:P9@4O6A:_6&1M7B9N9B^U)+!^*OV7VT9E
+MHGMG^N1PLIU@12SB9)T\U"^.+3+0`X''K&]S'"`/L&@:/7D88HQ`@-MCGWOL
+M,W99YYG+V4/7(X^.2F%(*:63F<G-"_I%L9(S0ZRH2CR,EC:7KZ#8(*SE5DAZ
+MB&ACBR9D^HJ\E")J*R"YX"\5"*N`9,VFQP![,[S]4_.7[,A$\93Z*L35D?2R
+MYTX&WD&)#H?%P1&>!W@S[;@'C5I[''@/.`->1<`(!3C.<FM2^R"UERZ:_606
+MR4MHTI*FJD@/!YPI#*;#.7?5*Z^X30`H&W?A'V=<]ZP>93THR\+7F2CS1%2O
+MHZ"-!<^U!S^65[&S(*MZJ-..=(GR2\&,F`?07N/R6!+S;%&M0-0@H7:DA`**
+M4E/3M0Y3&=5NE$,3I6Q1>(Z03BR6$C+O@BRLA-U)V%FR#M(*8NXD\NLMZ('^
+M)LF37<BP;@9W+7_CI!^*MT1R.!RN)0\EG%[NFNZ3[EJF`6L),>ZTG0E%8AZ=
+MI7LL-4B2R)$(<+"'P\?QG:6D92&]N+VR8)+[$36IP:*6H&1N9;K*;SJXFWS1
+MR'3*V3'_+:V,M4LP3R+:D3)89)%EGB*^(]-`QL,92?4MTUMP,PJ2E(M`%BTT
+M1\\2@32+I0#83F-UBC_=(-=%-@-IQ1;;H9J>#GF+^LD+!5G54#N&"^"0Z<1&
+MYG9W@KJ7;R9JCQ)-E/V+/9K>[`*TE["R"3")6R+..)8_"?*1A+DS1SH@V4$0
+MS4]M$M"`"B2UY-228\G"(2HY$Q8>(_[FY[)VX4E@>>QZZ-#T@((O#VB_FA59
+MJ;($7!*>7*-9@Z'EN2U`R79XK62N(%6H&W!'>RP??DR$;P<@UC=SYV4[\(91
+M2%LN@*:<](]T?D:-![27K`/$2Z;!68K!MX4CUH=L<2"0.8Y8O`CS1\`;95>4
+M11>FTB)GU$-'&91EC&O&N\M05?/?F;,BTBF*YG`4D-=3B5[1C".(<5A.A^5T
+M6$=;K!.U><OU&<0*$"/=-^FI6%\I98DGRLAG`JD)9[9&9BMB$$F2;Y2TV)-P
+M@A,SEC6K\IA[#`@+`=-2$@`L2N08=F11!$/SLHC:43B28I*M`Q0.FZN,@^"2
+MD$^PU1Y;:`P"3C2G)^(-1408L2[FFR86#K1XY\S![%K^FE"">%Q]*;+IA58M
+M7$5!L>P.[FT!&N-P9/J=C).MA+$"YN1U>'E(#0<D/SW)V6#B-X\0MIRV7%0=
+M,G0D'(*LK'_+F-HRBZ4UE9@I8+<(;(6H7<*R8)&F4)\Z*I*96+J6N<R6@Q0B
+MM<)GZ6Z-"7/N6$%V0TM<1RQSS*R#R+],6`GYB"U?@R7+MCYEJ(;+^\XZAT&4
+M#9/RJ&VYH6(")^_-*3YP%GHZO$!G"T:6+*,['M%[P;E.=\3_QZ[8JI%;R`-K
+M%.6I15NUEG%&',V#Z;%29D-8#B1:;ZB0V<SDN$%;4;;!$Q$;Y;$H3'*)Q3<"
+M7-N2@+6QQ;7`BE8`!W*MHJ0>];GY%?ATB6XEU"^99C:H`\#,`S.>1B854B0T
+M\Y<EH<(*C<)(T6"Q`-$6?S#K3GUV1!`M@(=E&YWA$P<8>31_NVKLR,1$S4#B
+M20FJE9?@\!)"H[I,%K1%T**6K-7&MNA&I+78/J`Q]E(DV,ARQ+8:18@E/\*S
+MPNX4K@<I43#C!'U%XN!H,T=7;95;6B'<LF!HFR0BSE]IF;?>HC\41CZ"N$U,
+MAK@=7.9HP.$GV/PY9MP65>3E-NVD&P%E4/I,$UP$:!QG-K1,)"9ZB19)69J$
+M-7Z8WW++(H="K%*P005_-+%$CQ-,<HEH"I[U&"O;8A'PG&S/SCA!MS-LK:\L
+M^H$Z>1%)$$NXD9G)SN0'L[D'!6Q9P@9QPT6VBFKHPK7W>K#',`6VLV"2;)5]
+M%/2K*FB3C44B<F`RLY<MRY'I3,%T%S&0V41];D$;0;FSM`[I:4FT+7"0_+4-
+M#[8@S:J!=#3S%.(>UIL\5IHM%TZYY/)4%L39?@49QQ;$#2`M$I1:^(8A[0A1
+MB)2342A?95D^DJV^^4B%[!5Y2<<6J>;S"2J.'6;R'90"2^IVD;MC^2_?ME1$
+M7(26:B9DB_*SI%1AL79`'Q-10%,ON99%3VR;$30;\A!*HWQB(@L,"0B\K;(3
+MM042*=)`9J%;,"+(T7:AME!)LQEMS9B`3-ZU6&:YH"4%(]G;1AD!4+,LL&D0
+MBGD6^[5PH#T9"0]@`IY54+0<A+Z`P99"1MOW$C%UT=*]NC<!BBS)9Z17>$PA
+M#6OKXMA41UW'/'KY(1X;[XGS`B8OL,(<+!,96&^;]O?.(Y+P;3-$,@Y#'!G'
+M*D,.G<Q>401<+-;'"N-M2ABF*9"2;:E;I,((IW&6;>.0):G)0-H2.D-.+&(F
+MV[65\6\M7=4,B4!:B+(TW3*[CM1@@HT<VU.TC<RS<<[V(5F^DW$ED7FRI[/P
+M)H';4S1LGM%2Z3!`<R0);#V9L`!S1GR6MOQD_@8>D2WT$K%8PMJKN:+5!G:_
+M8&_QAPAG:*!9*EA7/7-Y@;1A]WIF*1QXQ:"P,0DC=@O(^EHR!PVPU5TM<EH\
+MC8X$G-S(3E?A)3LPEH$2?K_E*?G"X2;:_A./[VWI'07L62H7Q-I1RD12C!2Z
+M6ZXP(<M$0$:T'J1PC:$SKKCXS-BAX-\76L-<]YI(BT4]:NT):E'-%FH1G;4U
+M8@U#N`@\QRTV;K#$+D)T6C'RA(-MNQ;A8#0+&FVBE/ZQ#`[1.Y$M:I61GX4V
+M!"RL9V*_7,(GR(B?C%`O2T/4YWQ;)A6E#=@.F$:Z:1;>=HW0,TO("Y7D)-CR
+MZN)BRRMN&UNQ;4&CL*9M&3;(S].QB`/4<AVLN;.%VHP^$&;:`L3!QH1NL91M
+M"62Y5*SXLKO4MB<'`A=VQ9&'9%ERDB.D56QH%FB@`F@<^9=`YCE89!8L><FD
+MLA8@:F+75E+@9<;5^#85]H?8AKJ"@U'DT13QN4)/BY9LT<W4R&-6/!&-)R7G
+M!7U3J!:L$X-B%@4NCV?J+=UFN2U+IV.4(N)O`"#(3$Q=PAX8A8AC+<?5-L9;
+M,I=-4[9LB&_#BHUMKDU,*DM!;/12X[;L3V*<ARD^=I:U[5J*P_8?6:[*(G5[
+MCP0D@H^A[M,D)>Y8S/6L+[K>=JRUC8UTT[;9VYJG;04(BE78.F\>AGJG/D53
+MU$+"LCF='9DB-H_"-J8!MFAHJ5/EC#RZD0A82>^((D)CP$+$X[2OQ#9!8'S8
+MBT62))KU4W1,:LNB8TOR=+LOJF,U+^:$MG<%TX-_Y4%A:NM:&:%9=I.=W#A>
+MT1A$HN&E%5O6B0LE50A1A$MP"G",*PFJ-)UX8^C?9%E)<UNL@ZPLE"(D!=K-
+MO+"9D%#.V`I;&*"+EE,0E49U/>$P:RF][8IA_8-5<=*1%GZ:$\XV3W+4MK_"
+M]!<)1M2/I0Q+?M@"',99GE%N(2ST:3E\<:,7ER%;L^&-\<WYL->\;`,6H2N)
+MSY9X,`\)@&FFQ$%M<0&4V>YS3U@2L!$M*<9NXL#>FX0.)$C-MA<49%U$FI:9
+ML<0!6W@*`U**TZ6VI-[V#6M.6^!NV4H;#%O#>.$@6X!F6HO<21`(.K8^"A-%
+M/**D&<,JL]8;;$,(KXP5,D[!7&=10M1?>]E+?I%+S4&V5P3(_;%!1SUN&V5L
+MW+9DZ^B:Y9NDXZ2EB9J88UO3(M)R]D*=[>EM\K7E+MPTND+(8*Z3GF.AK=:.
+MM0.Y;QE?>0@!D`1;;K,7#7E["J>RA;21%PUY]<LPPKA--?%GQ!QMZ;*'B"PW
+MP,J]O5%HKP9JP99=N;9BG4@[V99VSP*K;C$B;/ABUP54Q?.#+7@9T!Q!9Y;X
+M;,81N&TWMGTA;%7%-$3;>FCK<K8U`>_(-L@'WN0D@K=F9,BSV2F+HG6+A3OD
+M38HHQS$Y60(V2QL-:H$0T';D>U;&;)N`O?_UY">?/OGII_\R_<Q(_2F7\:8W
+M?_KM=]_]Y[>7__[7R]MW__'7R[M_O?ST#W_ZY@]__J]O?JO;+B[_#PQUTQ:@
+#^0``
+`
+end
diff --git a/usr.sbin/pcvt/demo/playvt.c b/usr.sbin/pcvt/demo/playvt.c
new file mode 100644
index 0000000..17eeca9
--- /dev/null
+++ b/usr.sbin/pcvt/demo/playvt.c
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 1995 Hellmuth Michaelis
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Hellmuth Michaelis
+ * 4. The name authors may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef lint
+#if 0
+static char *id =
+ "@(#)playvt.c, 1.00, Last Edit-Date: [Sun Jan 1 18:32:22 1995]";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/*---------------------------------------------------------------------------*
+ *
+ * history:
+ *
+ * -hm want to see my xmas greeting ... :-)
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <unistd.h>
+
+main(argc,argv)
+int argc;
+char *argv[];
+{
+ int c;
+ FILE *fp = stdin;
+ volatile int i;
+ int delay = 0;
+ int fflag = -1;
+ char *filename;
+
+ while( (c = getopt(argc, argv, "d:f:")) != -1)
+ {
+ switch(c)
+ {
+ case 'd':
+ delay = atoi(optarg);
+ break;
+
+ case 'f':
+ filename = optarg;
+ fflag = 1;
+ break;
+
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if(fflag == 1)
+ {
+ if((fp = fopen(filename, "r")) == NULL)
+ {
+ char buffer[80];
+ strcpy(buffer,"ERROR opening file ");
+ strcat(buffer,filename);
+ perror(buffer);
+ exit(1);
+ }
+ }
+
+ while((c = getc(fp)) != EOF)
+ {
+ putchar(c);
+ for(i = delay; i > 0; i--)
+ ;
+ }
+}
+
+
+usage()
+{
+ fprintf(stderr,"\nplayvt - play a VT animation with programmable delay\n");
+ fprintf(stderr,"usage: playvt -f [filename] -d [delay]\n");
+ fprintf(stderr," -f <filename> file containing the animation\n");
+ fprintf(stderr," -d <delay> delay between characters\n");
+ exit(1);
+}
+
diff --git a/usr.sbin/pcvt/demo/sgr.vt.gz.uu b/usr.sbin/pcvt/demo/sgr.vt.gz.uu
new file mode 100644
index 0000000..b2f0eef
--- /dev/null
+++ b/usr.sbin/pcvt/demo/sgr.vt.gz.uu
@@ -0,0 +1,12 @@
+# $FreeBSD$
+begin 644 sgr.vt.gz
+M'XL("):V^BX"`W-G<BYV=`"%44U/@T`0/2]_H9<Y:AH;MMT5#?&`L*D8^Q&H
+MIYYJNU0BA0:HO]]AV18L&"=D=LE[,^\]&*Q-VXP&ZU=CL#8/)!1OPEW!-'"6
+M+[X+@9A[_LI?S,$3L\4\7`6.>KOQA!M.`W@"$;JP+&Q\1B/L<+@U#"**[>8H
+M"0ED<4K*.-W#/M\</^,MY#+=Q66<I0:Y4T7TV5NU)Y3`0UV)DR3@E&4>?YQ*
+M64`610V'(H<BAP(\9\FN`1@"#($QP'NZDWD2I[)!.:(<T0F.(?*%=AO00M!"
+MD`$$\EOF16N0VM7BJA->2PY[UE.;*Q9*W&M65X;:EB*AE*5)'3FF%E6=/+2"
+M]&QC:EO5R6.;V5G)%;'JA)J7],.^I#J%DJ?T*FUO(J8S*1]T?#W2(\+U1&UH
+F\OMC=0<L[<K2KMCY%_UKC%^LU5+\KSQGT6K:P/H!L`MLDRX#``#2
+`
+end
diff --git a/usr.sbin/pcvt/demo/twzone.vt.gz.uu b/usr.sbin/pcvt/demo/twzone.vt.gz.uu
new file mode 100644
index 0000000..df05d50
--- /dev/null
+++ b/usr.sbin/pcvt/demo/twzone.vt.gz.uu
@@ -0,0 +1,350 @@
+begin 644 twzone.vt.gz
+M'XL("`PIFB8"`W1W>F]N92YV=`#M?5MS'<>1YN-&G-@`?H!>VK$7CV-WI*Y[
+MMV%Z!Y8@'U@2Y1$XJ_!X7R2*MD00D"%1XO'^^NW.S"_KTET@'V;?K`@%#[JK
+MJ[*R\EY96?_Y\-X__>Z]7XV']_YLS(5/Q^&]/UMS8?WZ(UP8M_YKPH4-]&.Z
+MX`?V(DS<@AJZBS`>AZ6/Z<+2^Z4':FCMA9_IB;\(^-30-V:^,./Z8[YPHW0>
+M(_6R_+)1&GL>>)2.XT6DQOXB"62.81[I8^[64B_+0(#52I,`Z!B69:*&H$L"
+M0I)I+="B$WLQR:N8Y)6=I3N>FKN(CO^=91K>\[PL?3G>K?W\84'RV?+_?QK^
+M8_\[_$?W]P\`_P'@/P#\!X#_GP%<1?A\7,3EW=4J(DE.Q_GX0#\6D?M`+29J
+M\86V"%%:+`*<6P1J\4Q;I"`M'/J(U.*8^\`H(4F+U,!AC;2PZ&/1!FN3&VWB
+M+9J@$V.HR77N9<(X$YIX:G*I39P#L&B1FDX6;2?CS&C"./E8FQ@T"=)B@;J>
+ML0?6G'82FTX<@%T4KS296\RB24*31<?7T"ZZF)MH"Y[Q9QDI7EHL.E*:\#B?
+MY_7!"EJ=4&JPGS!E9]%D:D`Q6&5GT"0TRYP`K=%>>)D_RK``W(A>'"/W:4:N
+M$BTZB4T+"UHQC)9U:(-?7G\EI\]&_`KY[:S/K+;3E_@1)_PR07]ILZ#]6ATK
+MZ5NOWT9]ZW(O>0R=A<_M\K/<+O_2,4R&+V-`Q]`I)IVB_HCZR^11M=_(?:P(
+M7QE^78`5BR=A[RNV;O%@7A_,^>]P?+;^HW]/QR_(.-0'"X$LQ&=2\<0<%UJS
+MKGCBCY=DJ.8GT_%CM@?Q9.',(QO%VB:M_9@"F(5(/F;K4I_,*WRF`'"AJ(]`
+M*?>@_Q6>HLE\_'RU2?7!0K@+2<:B15P?%'-:&&1%5/'-PJV?D9&=G[AUG/)!
+M6E&C<S0RP#DOQ+>$HP66#P31KPDF_)V.+]BI\/)@I@=FY8H/9"E^H.&2_+VL
+MQ(_K/!=2_@`+\1U-*^*!/W[%ZV#Q)*U-R"S_``OS%UH&I[W$]<D"O--NYA76
+M!>F+9/P`B_<M$<'"%!\`I?>T5`Y#+2A\02ATF.*"GQ\)R_AHP>!W1&W:KS_>
+M$3DZ/(AKMPM-.'UBC]^L)!V`EF6YOX/W\P'6^WLB6H<VL11WQ.CO$[*G]5^W
+M,M3[3&?\@B!ZGY"P"'AN:B=YM7Y<:KE!A)M;L?P^+3)_M/Q8]-'[!#Y_;4D!
+MOD^PQ4G&M%&&6K#[?F$+0&C&=1G7I@+FBG4&,\BW*Q.M_R9,+/'?;I5/[Q?&
+M`Q3+:CMPCX`/'7I`)6-%0#ZNB.>VBT4B"!@K%>%H!M26$>"HZ3J>C&,RYGGZ
+MAEQ5QI4#7EDW_4YZ32M!O@\?64#A[A9N`E8C'HR"WH257+CD_<*R^9/0@%MI
+MC7`@*^+0";G&[Q/58*V<E1\&O4;%0:R((`BT"Y#XF!'HI=?5^<<BR;J"<%*!
+MV=J4`RCK#RP8?H!0A29`<RNPDR`]`VNK;I-,;`9('@0["4'(,!X+""2MI`V\
+MUI;<2MG49`(/34!L7(4$@Z9<X"(H&908M%]7FFY!\+>.3!^'5972O[+41@:,
+MJ\H4GE6&1*=BU6"YIE5X,@#<VR3_6@L,3\")EU<K3XP\<@#'BIGY(7<;\XQY
+M95>R`.\JR^GJ6^T?C;V":RKS=5P-3UHP:2H@1="'DQ5=&4QH2E?!`U@QK+^$
+M,$B"1_XBK!:E\($NEX=4P')EAH:$$5O\J&3@519YACD!!>!:D#UWMF`"7*QX
+M%</[6KDK"L%&95HAAJ3<-?(Z!L@B@P6UNEJ^M*'76)A0.__K!<M61DE@W2@T
+MD;*HT.G7]G]`BP1=,%Z`%@4O8S'M")V`M4.GH:36E8Z%PH-RRRB"7G#L%<=X
+M(-(G0KK:6A5XD%^"%K0RRJITA2<F0&JQ8A"(#J*E\054\GL0)JNJ&4SDA5(C
+M6D:9_:JKI,M*OUH+]6+DQX*H!,),8.,LR70<89&@&&"1_8E2JJI3[0W:.N)-
+M%+X+&!FKJ4I;'+.G*EF4T:U@406*8EQLBB0#3D+%+O.J>(3@U5F$WZK@+,A(
+M=+VL&D1!U,5*@%75B_,59D>H"B\P6BL(!62K72<\DK0%-!"HU=7:9<*B./EV
+MU87@"LA'-3,"NE4BQ'(Y5Q%LPA*K%@'1*Q\[\/P(VR$*I<1L$;EZO4*6QTY5
+M'*3=)%RF3*UH\I`8BMI8:4,+V6DOP.1J1X%5$\P:KV]&,1D4V%#%#@)W%K`&
+M7E"APD7E(6Q1`WLG0KBR?QB.OV/@H#:<=)0@I=0\<BIS8C:`!S*G,;4$ZR&I
+M)H-N`7\ZJ+9(#I9JFQG*.G<BO!9T<J$P/#]BF:J$I30!RQ=<$"#,5*E,JS=H
+M0I;,(D7`H!Z]`2LF*^1Y]1-#EMAL&\R0D!8T,T+PI3QN.OZ)[0FP5`#2)[4`
+M0&#:QL=L9EW2\@&U`4,ZR!7U53PZSE\[\K9=]C:BF.B*;B5ES"T;#'9UNJ,T
+MQ:).JGR41!+X+]IL'M-VD*(VP:8`>B90?\`395%#P8C53(4245$6P>!J^4,,
+M9F,W';_D-Q!N3F1:4.,%+)C='I\M#PHB)&BZ*.A1?C;Z1EEMS.;@ASQ)C*3V
+M61+<J1@5T4N"%B8J[^Y9]4?$H(:!IK:_*KM06&%']AE!_T;L(2%93!7&C;49
+MWXNUR1]#WGE11VH?`9/JR[C"_KEF)QB.':\T)"^LVTDT"JM46#FR/:H\JT:L
+MR*RDLP'/JONQ\.H-P2:<*&P!=P$^[DK/8K3[;+(\)32JO:.R**G]+IU@^=0[
+M<08;G["50)K*EU8-3[5P?%;K7[*X55,@\DP]L*2^(=2\S>;+)\Q*F.&$3HR@
+M7`,%22A&)>=BI3S%QK"(7=AE(%6U+=5SR(M,HE/%U`0CRZF-+^QH@%-_8;/-
+M07O5F<T5Z[`!G,Q=G:9L73J2V7,.T!C1@)/:&&J.H#N5!$[DEX?_%J`V`H2/
+M4UM<M%`H[(2GO`#B#'C(:+62H=NLNDF%-7#)8@IM#)2:A2<G'K:'U9'I,U#$
+M,5O=RNX60D]M9XE8J-$T4]@,S+8J4UCN7K71WYGP5,>IW+>J*+]G`:I8PNJJ
+MR(AKC,^!85UV<[63A2:_886H/A-B0,K#X?@UJW.1A=F*81DQ9P\L@=9=H1B_
+M8AH$_$J#8]9^WS(EJQ,&8[G0-:^)=<"T"7C)NLQ2&#0@(*`AB1&JTL1,S`$Z
+M/;N(X_$Y<Y@&/<#L8Q;?WY)F!0*<<&<JM-H;0AD(5LT,'@8ZT@"XPN\Q@@4#
+M9:P&9UZM>0VFIAP#4#M2NC$N,X"&X91/%P[XD<#58*G&4:>L*+[CP2';H0E=
+M(>%N264)BZA9D<78O&)A-6>$*354E[&=B`5"X7:I<66S//N>T0Q=:!"T*!U;
+M[B>II(>&$W]J74C8V-I/(7M>LUC2P*_&YVR6;@Q&1'@!-@'W8JUX4$":8=\$
+M(N8K'@"KH1'5[)0P+K)=&,$BV24,1_97+*)`2EACX2]0WD\A6CEMR>;EG427
+MYS">.`DZ4D3>4>9^RBGR678-+(H+]J4<HE2P/"="Z3<F(/-IJBS/U34HHI]L
+M6!6!,'Y@BGC;P&9:8XVYBX*+>:0<K)6\J<Q$D\S`^,P7`U'TF+E`)EE;068N
+MC(0D;5QC=!2XX]RP.1OZ=I(YJ?4N&66^B'\!P[YPW0F^K/4\4LH*-WP@FK29
+MJ#@7K*!$SODJ-1@GQO$WJ]G!C=7H5#C7A8(,M1Z_!#L.2V\X^LOM%5A9B96E
+M(@82]6!I%W_]E;2/"0.L9B1_.@&C)H*F[(BI&B<48[$V9LZSED96;4-KP3(F
+M02&;I`,8=!)TZ@Y3#YC3JFLG/(,.%,",SZ0GB+7J/ABB6/DEJ#)&/U7]OW(*
+MFNF<!%B+YHA1$\-]1FLM?\YL!RY4?+.V=V+]B825KB%Q$4C!_I"!`H+8U;]E
+M]P9\4[A`#BW$B'?0SA;V,38[)-XY'6\(QT;\+2@*B1\;D;16#"=+OH#':T%^
+M]IZ<>$+XVXJWX<2L<F*.PL447DN*`NPVJ0GEQ*DS,B.!>^G@DCJ.F>]NF&RX
+M'<(XB"KB7P=$.-&:0N2ZE0,4&MDK-!)_-9@"5+X\=R!ZB_`:(JE&HI56D`+4
+M6VRG"DX7X_=`(Z)G(^%K^#KH%XOI,)+@1$2W,;`K1XFBR=\D]Z]H7<3[%WIU
+MR'.`0!C587"(%,DT$7:R<+)%T?+>OHU95]P0"0-HH`_8PAZMS-U2!@3H(`KV
+MX>.0@/TC+9NPA)`G-N2L.,G"S`B!K`KXFG8*9?I$+5;6#AD(I/R>$N8A/P5_
+M%UA!D+]$=+`PX!;=.C20548`$S@E/@434)UK,8!TFN'XC-$/AA%)881T8&TX
+M`0`36=#U(7]7Z+X;`D36"98QUD^(3#D8`ZD,=`#58O)@22%G`R2&X_\6-7Y-
+M'AKWD#TE!/V,]BBSD<@Q8`!E81$P,,2A@2&)D1%\=;(LZ+:P5Y]1!\+"L`H-
+MY\!D@8K`.`">P`4J-<!ZD&."/6S*6,@]B2A,6:-S9@]<-0>!+/0-&8`.,%=!
+M0D!F`E`$B:^H``6*:P0Z\U@$R$\KDM;(O^!+Z""5$!R8,K!DC&)7!`*9`*Q-
+MD6>_;CR-GV1K=365:#/J$WUN):Y9-(4UV38-;%L5+9V8K_(H&\ZQZG`6ZZEX
+MQ/;VI,]*PS9M1F;;VTS5B]+0M2548NJ6C\22';<]2URW_'R4<Q+%YVSEUC.8
+MQ?XOGWFQ:S?#B(U9=<"F?H4\*Z<,RG9L4/OZ&<YP;!$R\4AEXQE>1OE0/*#R
+M$=N]53,GQSE*(!U.7%0-#8*?S=3%WG8MJ,8W3<V\]^W-'R\_O$(+LNA^A3S%
+M_XET.0=J?PVP?LL_'#4FL_%#I`O^^P$[(;]`UN^O]=$K]/1_\-V?T>@%AOT.
+M@WR$03Y`WQG(OP&VVP->6O3T=[Q[C6%_C;[O\/TK#/('`/(;S2W]5XP;-;7T
+M&=[]6K,BT?=/&/82/?T[!GF.0?YR0)>_`"2_48-[1`=O\.X%AOT"P_Y"(3GB
+M,X//;M'HO^'[;S35]"]H]#<T^A=\;]'COVKKO^*=:T`:*:I*W_]WA=OCT7]%
+MH]_CL[^BQV^U=4"CJW*=ZPGDOB,:O<2[7Z'U2=?IG_'H%3Y+^.SV@'?_%^\F
+M?/8_T.B5]C2@T=P0BCU^I]BYWRR8(O67VM,3M/Z^1"$OV*%$3[D&[O@W??=-
+MLP8YP=D>?\:C'W0-%+H?&T3;X]?Z[C4>_:3X/6S83M\E_>[G#<"3?O=R@[(W
+M6XJ^P[N3]OGW%ATN+Y'31U]M6WV];?6\:L48/53-FF=-NVE-#K6LBQ/U1C+\
+MN1B:5A*>GW.+>7U`Z=U6;#A+ALHB1*]8W(:U*05)K7@03Q#!Y-UXSTUIR^0)
+M8J3V@)V1Y^(@7(EL?B(&_7-DC4IWB8`FK_Z*80O')^P]A+4->1&4P.J<S"92
+M)_/Z[179:XDZHZB=%4/\3D7I$S:$9H'$RA:\C\`%NB=V#CDH&+@7LO]X-H0U
+MSBJ[$QOU"5*^N0]B&G)T.'-E0?3=05P>SCSRA`J.EUR)37^%_(HG2(BW\&JO
+M:.!E00^0OW?86[G#+NYSR>*VW"_!X%9TW5'_LZ!KF8^E`,?R1++59@R=CG>(
+M`UTAQ^HYSY4^YVVC)SRT86C""N@=#D(*"3C!#!&`I7,ZSY$4\80&IT6SE,M\
+M=T#^K14'UF)GX0IFP!713>",?<>DMG;/+9?N[4%L0\M3X23V:9W3$^BUYP03
+M.J/)4OCB"0*+S]E3<#,>>5F"94(66057*C">77]V]>G5S8V:SI<W*G:OGWY\
+M_?3ZV9_`V)=HM;BM3S\:#B#']0^L.[7AM:.?1(7%((P3^8(8G'LEA!?MB+XQ
+MRHJE#`RO('U&_%Z`2>M?3HE6BYJ2S."QB%-*F&B+$7]K#)"!)/(K&I,@R4,2
+MN>8_B<?KKD/QFL223)Y6!*^4"DO@22YP:Y(90*C,B)BS:$]"BIN3H"R!=*F>
+M(!$Z8X,$5EZ0P(^)J'@QB;L4:*>K1Q*TPHQ+6#+F%ZR1E:&(Q]%IJ`B,!$(Y
+M^3$3(Y$L#TI"!F1!+,+/O>*'>4VP1KQ9+P?6E06GHB"5P)"T*_'%BD!INV8;
+MDJ+U.@2=NGQ%HEWHT$V9R8@D$2&ZQ,3'E@RF3+F^@-\5\Q0<D^3663GIGM4@
+M8`)Z6"9>"LPICS$7R""Q>RFL&US)AR2[2S218JD;9#$0L_PH68+EFR!I6;GB
+M#<G3_#>)U8)A2/S7"Q%,#7FU+JS7*VD1QKI_P1%K#?UI*S8=%2$LD<$N($6V
+M"03OE*S`Q_%ASX91_$TO'F*`ESR)QZN!C)FBX"N<CN,&R\@WLO4P@$Y5J`KQ
+M!(E$8`-MK0,`BVB0\TW,,;.X]Q:;?D%"("%*BX0M(<?5#(),9]V),-@"0RI-
+ME,U$=I2C],]"_D:K%Y`\9R#731>G$0*!`:4)!$U6P)VP/S6B%V(992/BK(#(
+MC'P\"BZ=S)$3*08DC3_EL%%"+P[3EWTO&1&(LK*&O+<YX%3C@'VP0:)3`_(E
+M!N1VLMJ097:Z,"CWL*#[V4$X$P*S+,O`A,UKEX"5>/R4%V26]4Y'/HHLTP"2
+MC>"6M,6E''@;D/\`A<)KNDZ<`)%/';RP!+Q93%W)-.C$O$9GA%IL\3EH@2<]
+MR]K-F0B#QIK`3M`C*(;!RV*5@Q*P,0D-&BR5+K-1DHOX*@*INF8!A(]B&GP6
+M9<!IGP%;>])11!!/&=C+VLN_'@5#@NQ\>P`LY#*AB7#8)/0Y`6$C(/$";<Q2
+M`;RHJX0E#[)ABU"5P[A@E!&L/M)Z"$,GP.9!:]BXEE<.2S`*PB>,S$+L('IU
+M$!&*HYB#G$0=1!1?2I[4`"'*\PK"02)L1LB"<1W_3\0.4IQ$"`S,["%3$VJV
+M<-K^(-GZ`CL60^045LD5-()(H2S/++!X&8GV,SY"YJ3J!XT&2G=&)B2P)*`M
+MRJL$R@N%<#82)$Y"_BJ#K(`2-&D`BF0$"%K`1G,R%',2#G4S9/^,MLA_"!Q8
+MCP6O(]RKA(!B-EZH92Q*X(0CC&$U(AEEVAW4V00$!:4Y!$>#%*2!TG*HS9.9
+M7J.X3E.1Q2>P`BY6(JA2">!951`)V1DF)[B(J$,RBG(Z`VP1PK69OR'@=-4B
+MECKC!0(ZRQ19-X^]B`"QXU$]"+(%:EQ+`:$XD0<+8[:0!U'(>5W0Q:HZR![U
+M()X(]B0&G)`44E))/6N&AV;UB%\G#)2*'Y")7M$Z@_\@B+'HS@EZG===D`%'
+M5`9D]LOLE?%'S<09X'->ZXG^&S8RH"(C$*2EBG2-Q1I0^2S+Q#DG:M.IJ0;S
+M7'V=`0;_(/N+/(,9O40H8VSZP(A)N3C3QSS'62@'8MU-8KMDX$2XN4GW.X0%
+M\$((WREYS^``9%X+4Q>4JFN&M"2U0B:99QQ%P'CL)7G*$IFQ0E:C4@&2$WL_
+MJJUF2'\(0:_:1"U4H`^@I5P.BZ)4L!:TK!9D'OOIES1W_8AR@5V!ZI%W>0.F
+M$V#I94*#%H:)@:0KV`HC$.MAS,$PFK`62OI)9H.$M074I_"O!N01#0=X[X,<
+M]QOD6/P`/U_ZU>51LWK,65@@6H';04A.NGDYB&\_X!C!@+,E3)BR/F/I2`UB
+M6J;-D[AY$HHG!QRQ+QN)1WJCK]WFM2M?V\UK6[XVF]>F?#UN7I.7IGZ".GOR
+M0>&-R@?LN@TH(;/Y8-I\,!4?3-L/TN:#5'Q01%T&I&"W[6/1/E;M)[R4L<+F
+MVU!\&]IO0_GM9MDX,C&@^DCS;;FF;K.F'&L94/BG^;9<<+=9<`X&#:AYTWR+
+MEQK^0S\;RN`PQX#".$T_)O=CJGXV).0*$G)CV\^8^QG+?NR&LFQ!679N^M&7
+MM"U=]K,A.%L0G)W:?J;<SU3ULZ%#6]"A36T_*?>3JGXV]&D+^K0M?>I+3A<K
+M^MG0JBUHU;:TJB]IW[_L9T.WMJ!;V]*MOJ2<@K*?#0W;@H9M2\/ZDG(.RGXV
+M]&P+>K8M/=M,S[:B9[NA9UO0LVWIV69ZMA4]VPT]VX*>;4O/-M.SK>C9;.C9
+M%/1L6GHVF9Y-1<]F0\^FH&?3TK/)]&PJ>C8;>C8%/9N6GDVF9U/1L]G0LRGH
+MV;3T;#(]FXJ>S8:>34'/IJ5GD^G95/1L-O1L"GHV+3V;3,^FHF>SH6=3T+-I
+MZ=ED>C85/9L-/9N"GDU+SR;3LZGHV6SHV13T;%IZ-IF>347/9D//IJ!GT]*S
+MR?1L*GK>D'-!S2TQ9UJN2'E#R04AMW2<R;BBX@T1%S3<DG"FX(J`-_1;D&]+
+MO9EX*]K=D&Y!N2WA9KJMR'9K]!5V5]-%)MF*8K=&1*%W6[6;M6XE[#:RKA!U
+MK:3+@JZBKPUY%=35$E>FK;J+MH?<0?.]?EY^W7RLW]:?XLL*\NH[^:S^BC\J
+M0-W[9.\+'61OB'[SG=;]QCMM>TTW+3<-SY`[>8]<R0=)=#Y)QO0IQ^,]2NU9
+M>4'R]$'RYT\B/Q[P`1(5'U@H/$A":M$AY=^?#B(33Z(P'LH1)RW)^2`ITS5`
+M28MZ/B`CO6X@!0:QI6#R2#-#EBL:R@L[\@O?3$6J@&ZQPD@AI2C%]^HY2+U'
+MC]E2\O.)ODAERRC8HOSNTZ'J@@N#A@P]QJ0BG%/NT3/PAFO&%=#S=&(%_4$.
+MIO"G<P.,$YP+,&DS<2G(R0<+3I(R7?1A<]],82:7]E9L1YF)H#=_PG_C@0)L
+MYOQMJI9(>R_&'9D^;5$7]`$ZN^PU7E2S-QDM*(V).64*DA*L47"[ZK8'G,L^
+M5<AW-5HL*(%+I=$P20`-#=4Y4WWJ]-.8(8S\;:P^)3OUOJ(BJ8ZJD\R$&&1T
+M9J=):&&21:$4]9.<E6AFYG:7(%X(2J3H[00JFI1BA2M&Z9-HIR995W,@GYP[
+ME8,X&60&V$GZ=B72`L"FA/F:L:2@J2U(N.8"*1L\"4=-LMB^`A=^0M,W2-LP
+MF+R4=DO1KL*KG!PXB4OW(*;P0]UWJJDRD[PL\B2B@(M;%9U/0B\*L$>QW\P+
+MSK5$5PND282#0=\A0YQ:2.?J04255A(!]^*/EZP7,V!^[%`7ZM"F/*Y0K#$-
+M`WG3ZR,*',*=TP919H1\6];A'H<-&U1U9-"LD/&R3ZAF:];.3CB(\H#S&@4&
+M9FJ!;>@.\!.`K^AUNH!\`S6.(B6)ENX1.)#Q]WOW*,9,D;ZRA34Z*^FTX?^$
+MSETQBC+ZU"Y-Z`D/1CR7#:F6A&7")`6ES2@2*F)V.76`"<+X`IFQ6;N&-*:+
+M4./2T=]<G5"0*;029'KXMQRWE;H^-0S@:S$\7<B<4#0Y]\6DXS&YF)%JYH;-
+MI%IS;$EFJBB4_0)!'9..:Z>0BE&F=A2H/.]WEVX263*A4//8R!(SBI!6,ISR
+M`H6Q7:#0&458"K)E+D".#1U`B\@H>2ZHI9W7!0*A%3NC&&8<DJN7.U?$7B;*
+M,B+8'EFG=N$9!%O(!!=;=/FRO#67FG`5$`8/BOKGH:N7(3C\/I2VD`]".:.6
+MV,[*3LP27PP=I+'@H4"T;Q$]MWC(-#EE0_Z>>2_4RC(40XYLG33XT/+C'K7C
+M,T6%L4-1(O(XIT)`:,6#<\*&&8**MW)9_F+R,`*"Z8V<K>BQ67L4?D\R8MJ9
+MJA;J;WDUM'(GP#)H!"%GB_"<:WEOICQ5VTZ5\10+NI\Z-">7,]#N\JZR'$&-
+MHSA\!HM:K+;%K04=J\#330[UP#`U_+29L9,!]T'B'9N3F+CW6IH?"K2F\#BV
+M`P<,/&\&AM*//>>%S0(+MG89!1[L'7M&38`,ZQ`ZYS8R``%*MB8J6XR'^RC@
+M!)$YU<S=@L!3JTW-9NZM<,N7#>!B!0NQ5,/$2#@@G:[D`37*U%ZD1(E:V$?7
+MKM#4R*"0-9;@QW?=2Z]74I3W-MS70#E(R%D\;P&*ZM!V5@^.3$=\4V&"!^2X
+M8.38RB"Q0`PPE;8H<_!5O6_$1@P-IB(\H.![0$%Z>+VQ0:_;:)'H,_T)RB9`
+MYUMCFHJLU?([]J1H"*WQ%,96:7G(RC8J!-MP!BAS`5-L$92$X*-M"7XC5UL6
+ML)5`*"_2X(W[AJX32$C<0&$^*H7>X,7U\))`UAVS1.X!0=CD_H""P0\XC5Q%
+M[!0O'J&Y.'>H.4+Z=A0#)V@6]Y/H%2<-P<Q-]"2O#8GFN!5*:<0:A7:-YBXL
+MOKPAI490;;&'`@(K+H(^";@C*)EVC6)GC:+ZF2%T(UJNONF($S>K.*/PD\<5
+M-YRN>X\*/0TPK;2.&VG=DK,5HRS?@Y.#B!DAH]!L2VZQ)7U*PCPAU>:$',^:
+MV1*"JW'J(4_E=J.`\M5!!18-D*?N(8M:6Q'Z`=FC#TCAU+6]KR(5OC6Z$CSG
+MABNF#8>.&[:PK0%HH<X+F1`TJB-?"]P1.F,&N#F4J>CU;7`FM<(^C8U:C&H7
+MMYQC@7#;6HUJ=V]#M%$D+2>=G7"XXP%RJ(X`IM@1+7)?TM3R-U>4;\`L[]VJ
+MP123TF](':XGY\F?JC67N?&;&INMQ9U:3S]JW")N-`.0&;;8K$UOKP;H")3%
+M:@;(*'M`7J/<>]2SSY-KP4RM,LW7+;76!H27;C$4M-JH600GYF+MD\C/+3(1
+M:DB]4$.<-E"J.@G%/5U[`E6W5'QA=`&7WC:XU!O;II*XSE%>Y/KU<,:@]N(5
+M<=Z`ZEOK).):,`^$1LA2:YLH6^9HTTBDC0^48K.X2;5-[&J;6%]$9W3WJ^MZ
+M\)&.!_&Q[Y%8^'#`:8C[&BKL-"33T\?Y7K'8ZF.K4(VB\N)&VT13[P$HRA`T
+MHH3K$S)3'ZK8;7X#][UAGJG5J"7@>M&9:7DE`'`#]=.HA1T=(U/3F"]GO#8$
+MFL249N'!QSY.#/C<H<F&Z[FZ;W&G6RV#(O2.F7KKS]9[H6Z$'?F45BE$0X%@
+M+THVPF1K>8RV18?O?B0>FWKB8`5_WK!44GS#E&YI/F)FL0F]J>`TZH[YC.>:
+MNOC('>]OI8U&2AN-U#%FC.[;>J5^-6$C(FR51CK@\..IVET-,$L5SQ'!GXY.
+M7>%49[9CTAL/L]'KCE1MUB(IMEF'C#8'DPH*/'5#0HWRE&L>#[@*Z@$WG#3+
+MJ4&ZQL(UIB"Z$?#XEJLW@:+D>D(RJ3%I0!\MXB+V9[8&[)BQ(H;QM/564M=;
+MR?<TMH+(5P2'Q(/[`Q)^!9RZYWE+0QY;M['5('0=Z[GL$P^OOWU!K%D"%S9+
+MIXK$0)&DKB(1'T*MT0FH*H!+`$[=YVAA%*6-X](3&IJQXI7<(@"+\!&*:"@;
+MKX7F:%>52J^?JC@BT[R%R<>7)<AV4MJX+!T3PB"%1ETBKK/]@'3G)LRMI%5K
+M"K5$^)J'6H0)Y"0K[G$864R%M/%5TD9%)*B(KH807M)$`[YQ@U)>-N27-/;-
+M<%G8*[9V15"Q_M20X$89--:YD:0BL_&"#%(J^,H<`52H8JHVV"3.?E]'C@,V
+M82.ZB4W^RZ@\Q-O`P]UWWWSSJN$COF*W`1D>BZJ'!/50[H`><`,-*^0-:PNM
+MAXQ;IA@[;L2FW0@A7Y$MZ\4-`J%4&_/39M;1\)\0M!T;)X8K63_`OKL_Z%W0
+MI7G`NU[#7W_X_J?[;QB!5>X62K`W`,H4<GY/O>V'F[58X2:$GC?,'4"2K?'A
+M@3;DAYG:AD3:NXSMH`(Z_&_'?,FQLF=03\$V\Y4+LTUM_B%%'F-.[7PC4A24
+M+)CJ^:1K0P2F60<^IOSUB]=O7KRXYX6HC3GD=MP+=4*$-U%?+CK]`+?S'H<!
+M&\5J-L(Y06&YV.BV/!W7$%@3<.)4>^"GE9V-3+%POPN)"7E1Y_8=<*OM`ZXZ
+MZJRQA_G.9)!4L,4VH\_H=F01T[.EEN0#:P^XB[2=9M!IUJ@-6R+GE>+C;IA=
+M1Z%."#Y:[/;X+5A;;C3*A&YLB:%68784DG8E;FR-FPR-[,?:3`(2%QDWJQ#;
+M#=^R&T"<NW$])6?4Z[4;PNYLNE-J/3Z*FX_,_NSBA9)J(_OYF%]/"=?(YXPV
+M/>K7.D==C*C0;K+'3+P(+>5;V]UD39L0D<[)M(APL6LQ;N8TMDK9;754^Y%1
+MV\CY[NJ*VM8*&'>?7O_^2/?]U=I[:G>9["8UPDQZC7K19@L6[\K8L4H&9229
+MKKIP2H8=7N4#>.U8NJR-36FF'I?QP;FV'Y4"MDM"9FSGCKCF=#%W05;ZF%KR
+M:-/9IO[>?E:\L=M$]607Q5F7=3%L-AAV[:3;S4CD3MEQ1X>T2I>+/'P%RZ?H
+M-;8,T.206DTMXI-0O0E8$)_9\*/9R.KNLFU8(;MMO4\T,]=OY,!FY3>0U6C2
+MD@?I[N9X^='G7PJ[GNOYO[,WYP@(##`&!UBG`QR[`?:*:H4!V6=P$.%FN:I4
+M$N<NJTI2\W*`#Z1%8[XZ@`]59``V=)EP\%TALEGW#LC4AY&`(@:V$(V#,)`6
+MQJY*E5`,Y>SG<U@'@RHF30]4Q3$(MPY0WAJS1`=5>:$#/!6\-0!"KK,AUY5?
+MI@Q8U#Z*:A<'"!KU:33O5LW9`4*R*HBB90UR498)55,2"I10W&.`H37`H1J@
+MF]0D'&#393-4U8F*-5[#.5>#26.NE`YDA)"5J_+[`%-B@*.@!RA4A2BK*AF'
+MHJ:(6I]Z=I,&9*>/OF!YP1^3-06(DHX?LM`&J4]%D058]+F2$+(H<NTSG-U0
+M1V`9Y8"H]3?PK0?X)FIA`W=Y8<:JH!<;&"A=-.=J[<"&5HK02FRH$V%S:8I[
+MZ%HI1J/\&B=-1%A@>3A'N:Y[Y$.=W9\CE69Y?<#MY2^0FZ3?T$5Q7-KKM>;"
+MZ%M#98`IL4*?A>.+`_)3]*&GNLN4'L+/*-E(AHX`+6*\B*YI3_#-`1MQKP'H
+MV:!SXI^T`_;B@-'TO<L_EUF?%,;EJ6Z:OL!4Y6$"/*D``SB)@@!IR#!%F0WN
+M37^#6>49FBPB>(X)0\=B%6+&6M(930`C%4*`OR59=7\H2[Y,&&`"="R5#F7E
+MM)0%N(";M!Y+RD7S"@CX/C12,E\#IA>UM%R+3J/+%X#D_M!(KX`PGT,C*Y\M
+M<D*[UL%D_$4G?0C^>0KVO`)3?PA`;J`4KB`JKP]@^-_BQ]G#;W5+ZK?GBR%R
+M@*0]J_Y.S=]3\_=<_QW'YF_3_&V;OUWSMV_^#LW?#7RQ@2\V\,4&OM3`EQKX
+M4@-?:N!+#7RI@2_%S=^_7?[XC;8^IS_/'FX.6BU0#NMIE8E!A]+&_W8HBI^-
+MHH38D%$X<^,_ZDU;@YQ6U`S^'&$LFE]IA&[(-2$I0CDHELKF7QQD-P'E!W(A
+M/L5RU?Y&0R$/R$=".9`L_NLOGAUR%4V/$H<6]3QYI9LOKC6=H:S@Q^I-J:7]
+MAFM/:KW-(N5_4(K;?'-]D)SN(5?U"V4=OK#SS>=%'5+<3L+[%X/2_LY73P\(
+MI*IB?9"L\$%Y:.\[Y`65)>NBUJ$C5MS][""!L2'7Q\K*E#BZ]QDEPPPXO*3E
+M-U4R]+Y#M3:M#QL+2RK,_?&\5D2S`8=D!A54CWVF[":'JJ.R74B/SB_E*'%5
+M-I%EYR-#>B'E9/1L(PRR161_>'WU],.KAT=&CE7UQ*EP%98G;_O>*\MYEZMD
+M'J",WOXY^T5:MXZ">8/N!+W]>RVJ&W)MR0-4X-L^YZVWHL!B*CTW^R[8$P\K
+MP'1D!E3#XFT]!`AH+Q3`MQ#I_L-;(1AQIZ<3/X?VG`?=1'A;!U$Y*R#/*KN*
+MT]O'!X?,N1"RPN_2.RR@\_F,CB;39U_U[?!'HX<]<*`I>W!O^UP=-BL_4"^!
+M':1W0']$$4(SYHI_.2#_=@"T)NTD6B4#\`X4*)=N<16"LGP@>_]OQW\2!A)_
+MVQ<<X,9WH%\M0:DU1:W.W\[OQ$)R:Y;5TV'X?'H']!E9]C1IR81!H^UOIQZU
+M9>3SY(OPQCN,;JJRTY0\-&@(_JWRQ^F%7*A$&G-XY=W(KRX<[4OLOYWZ4&/4
+M*O5D_6C?2?X9+=$*^1?S`IAWF8&5PN8V)T+B^_$=%C#G=AD]*H]8R?P."(@8
+MU]BZTC#%HMY!@=E\TF^0<Z7X/KT+_R:G9]-R85PE@?-AN+S_9L#VZ2+<5O?U
+M;J!-AF$-6Y[AK#!J&YGCP_DFM*FE<0\:UA#8D)B<A<[06'NSV@1K()6"LHMQ
+M<J5"XOR[,ZTIKR&'/3#YB/F@Z3F[<-HRF!=*,#D'+,O6%DZJT'I=PLEUJ54:
+MGK\^PT7W5QH0V`5TG<'3@Z;K[.+3%X[,*CT+0$.IQ/P&T$F)7`"U!=\O"W!.
+M_8^*"`J+[*][T!#WFKZS!V<H*=*58'+IDB+<N0'35%#&PA-9T'_^ZDPJS`QZ
+M2?0N,@T*%TO*SAZ4OK`;5J`*9%JM",UDOO%%_(8ZRR`G4V>VBRF<U*'.E+DH
+M[L,92_EB*CA+VW?=$FC@S)H5<-(62D;_^8LS.14$..T^G!Y4+=DY>W!.A4.V
+M6B893E=::-.6B6)9QN=XT)1C78#S'\_DR-F@X;<.G-)&$G=VN:@4V'/#[<87
+MENP.$]7DR6E?>0'`106@8X?;%[K^]*!I._OTF0/Z4\5$Y.1_H1;[%DPW5V!2
+MSFS&__G79UIN_H!\]#TH>=]`ML#6W)]=X4DEKS_';F<!)U\RJH[)9ME'O0)!
+MX2S]"").2^7LGQZ0?=[AHJ"KGDP/F]G.B@TVUQO8U`/;`;-A=EMTM:ZOB/BU
+MZ.P!6>A[<'HPGZ07[3-1%IVA1J9-A9^Y@9)2DVMDIK'`_OF;,YSLQJ+'+@\E
+M!=-UP2RV&RO)F?6E]WO(K)=\@J!FY.N:F^-G!V2<[T+)5VXH,K=`/B`A%!15
+MPCD5/IO?JB&3K6)51"86Z!?!B0KNE'#>0:9)!6GN@AD+G6@;>13F(C2RM3X:
+M*ZFT#U;5>'^F5_!H9OH>F)24/2B?[R/3%G:V:3G(%1&@K1J*H0$SFX6KF<%:
+MSILB87W?]/#9])B[2Q[+G4O\QQM_%=1EV&JKE-+&`O7EQF-DMJ>TL4'SVCN4
+M6EA,TS[87KV@N2:!4`;'=C52*YNR!EM-XF_/Y"6`'+MT&@L%W\.MUQQSURI.
+M)=0P[K+]U`+J"^P3/_&I#VS0SQU*%5IB@ZE'J4;AW(BG'%8U.Y1:0\F%TC+Z
+MB5)YIUJ2$&W/.XIC87_N@SFJR3Y?M%Z'(B?8/7[RC?FY%JA7[)__3132?/SX
+M@%SW?8649>@:L=\73DZ!K/1FB9K@]HR0C00M,`\'SJ@-8F,'1MD=8+]H%\8)
+M\UA@3"TB,Y!^Q\T,XT:W3P7JB<F-ORCS1CKK'0HOL[O>2I93`Z;+6G]''7FX
+M"84A[U*!_7-6R@4V?0=.:PJOO2.+,IASXVX4R(F[EM*&R[-]O.#__/LSLGWM
+M\:,#,M\[7)[-^+&[Z),691P;)S-[C2'MH=.UV"S$PK+"?SF3XI7`INV`&=3+
+M#'-WU46WKG"V[EL.'89=ORB7SB!`RS2O=8MF.$-=)`#:]8N<(C1T=!!7ULOU
+M36MSR1>;6#L+'UNYF2V"=1?J[@S'>P#HV(LNJ)\9>FHH*)W/37@A%CL1<=P1
+MG,FU_MNB)#_.VVQ?G96W`7**_RY"U?'C(R-=\S,OO:\]XHR?:/;"(&%C?-AR
+M&_'^#`>4`>C4M>95$X6.)K)T#=,S`;1513DX&'>\HU5[?]Y(T&JC])?$\HLZ
+M/^JAA(YSE.'LZ")#%VEH'=9JX3.!QJTRLFI<%H*I7``),`#K9.UW/&*CJ;'!
+M]2WE3*"I8:1BBWP#)EVS]<=ZW;/;%ZV:R@IFZ"#39NJT/<7N,Y!3#60.%\2P
+M$_URKC4_/+MKO('_ES-4%1KTI$:'-KUJH]#51D9UNYD;7+HBLV$+9J.+7)G5
+MX-GD3(60-_NZR&ITF#,6NB:GFDEV;"*S66BO<%;_L5T5]W24S:6M4-NNR,GX
+MZ@QGX@<]@-*)T^<#&7-7E6;@31UU4.3O4D*(C3>?MZ)7Z_R',Q2ZR<=;]L-A
+M=BQNN.H`:;*+U&JG;*+NQAS2)@Q:B(L$[G=9ZH\]J9\)UJ<>P3KE*^L:?>\5
+M3K>WY$C5R8QEUQ2X6$9MQ^,RU"<XDIR?DJGJLVB8>]ZH5TO`]Q37RN'_)E-H
+MMQE"*HX9[\QAWD2<0[D?(@ZI"MJN_^2*PU<=@L@"S(::(%)&M-D#TC0.5"CT
+ML9,PE-P%)V<8.P9@#M][WQ4--G-7;`E"I[!CKT1-*BYD6"KW0U9U,!<R+'87
+M7468=WWW)#-8:@W563W5><>N"JF1`SD):8V,_7BFEX[B>%8'G:DXIM8#<U(K
+MU39ZR^J5?7OFM-4@2[8"\I;%&FC\Z0REMA&^[XBKQ;7^7$_&]<15!K/16\I!
+M82]89ESC-V<+8`W9WIW)<5)$=+H[2\4YO)[9MQ#M+R6>,S:&=$;E[@;8O`GG
+MS.5&R-T9+M04>[]'F#DD[CK:B4_'H"2K:?U[M;##GH*RF_B8*Y.-$(9(V?#K
+MFBHJ-EU70WE=66=K=-H,IM]U2,>-AK+E7@C%(4SIX!\?-H!R_?!\HV'/*W'*
+MS*Y64.LB?GG02XFW8(8-.N-8;H4,9RC?K-2Y!Z;1E#$^4MD3G%G7.-]N?&8F
+MVE5#QK0.5/9C5IY=(Q&NP*??!306^YZN%\<S!1&Z=M-&H32[`JFQ44V=#<BF
+M="@"^&$?GV.QW^U\5W(&U3:N<9^BBO[]\&VPC8"OLA[/92/0V.+0[CZ<(1-H
+MWWW*BLC5BBC[S?->.)QN1/Y];9#Z<D-$K(^8LE[?9Z.43_KU%-%*4%<"Y51C
+M,\.XKX8V08CY>)FW0[X[DTJN:B+MXM+":N=3N;TUSY+'S74((G/7SJ:--6WR
+M@*^+T+!2S\FC='Y\G]<+.,=NE#D#,S8B/JJXVMD#\^UF-P>7'O(6"/;D0V'=
+M[P(:"NO>SCU`8]Y:,(V,5Z/3[\9PW48DY3-CJ]=S>R:WB!2U`'9E4LA@=O>3
+M4L:GK;>]"@[:"4'D;;%"M9=;4!QZLD6.@]G71:;TZFQ'&]D*'E<+SU"_V4#J
+MVV2A(CHZ2K#99E?#N`X^D\I.^T@HK\):*3IK3&]WOAI\ZFWHO!'RYDP*8^9@
+M24<D:4C']G>4YHH$\ZJ/&DJ>]W(QO";!9RA'#8Q:3KRRY7:-Z6HBHX:2]3U#
+M*4P52U<[(7,E"#9;7XT>*G8D9%O>F$(/F:XBRHK=]K>5?"4A2_LC[]?N)`K%
+MUB%B=?>0-T*^.9,R)T,NU;(#IX<!RP4#.B+)SI6^J?Q+-4!V$J_61")SO*ED
+M?+D/=?[7,RG7EX/,'1&O$MZ:OC57J^[2[+0<'9[WLMBF-GPSE;LU$;D89B[+
+M\NSB,D?";4</I6)K(8R-<YDJXVFCU5V;V1)P-&PL$KS7X`V*/LOYI+G8%-E%
+M;]1;6FB?O2>A@JF,S<I]=Y6!NH5];B1I(0Y<L;FT+YFLLI*9^JL_5P9[H9#"
+M6%GY&U:J0,L9=SDBRE6L=D!33X0+6?0TN@)6YZI:E2/S;NZO;6-=0<,LI6=A
+M]]5.*JP-$WMJ/.,FU!Y0Q$8$G,VMWMFDK8FFFHK]+>M[')T%CPE]DDN5]UVR
+M=#Q^4KCL&\&SL/QE;;4YO8''IV*[?0^\N0C)FW[BPE2%,"J3,B_LKKN3TB90
+MI-@LMH5C1]#D6('I.3ETUJR(`E4J,%:1HXW=TR0M1-TZ,T42E4T=:\=FX+H:
+MQ=6!M&)=%_GQ61%\V[#$M(ECZ)-LX]BI`YO/[&KZX0M7Q2*K9'U313`;Z#)7
+M9O`FO7LS)QYUI%PLBLYT#(:IBN96\8I8Q8!;%;<)IT'LYC1.+DJZ3V]SKG[3
+MW9^J(N*E92C*!6'TC=^R#:%-^>K-G/S6DR-*;E/7:LW1A]CL[.3<";N[7[J>
+M^*R5[HPBUF4ZX:YR<"LQ7^.&[]YF3K5C4T9VM/*.VPOBQG8G9^1@P5@X>,YU
+M`3/'(VH3]8).Q49,F^26X^#6[C%INQL^:IS4%CN*SO<"#WG7HY=14-A1<7.B
+MQE>;C0UTH3U<$96K0V'8N]#AA1Q@ZF_(F,RGL47=6&WCUL"%UJZ7DMU[B[AF
+M.N^//Y,B+D:5G>V-%M(A4F\(VQ]B:H>8'AUBZ@UA^D/,[1#IT2'F_BSV#VK,
+M)/OJ(>)C0_BQOQ;=(4P[1'AT"-,;PC\Z1+'A/^V,Z1\=T_;&[!QMDB&*,=/.
+MF.[1,5UOS,XQI76(S6K91X?PO2%2=X@MS9E'APB](:;^$!O.&1\=HLO_<W^(
+MEO\?97_?97\S]H>(S1"/LK_OL[_I#Q&:(1YE?]]E?V/[0_AFB#[[KUKOAT(T
+M_R__K61P3\?W_HM[=KR^T0NI>H"X/B"N`:0O)*SE(3V&#%3B=T^3SASMVQLR
+M-J:0G+3I#SFOL^0!^>:KO0&Y6F?GK&YM&*YEZ-XZH.<!'=5FWALP<01N_S!K
+M8R33@>RWS?#944K\!-\+EIC4/?C7.`SAPKQ]BC*BH?I2^_L&IB=-;.L_^?)0
+M"H2S>PL8<9WX1]>?73V]D2(Z7*=\W^`V/;EC6D_3[@%CW@$87P)##NPN+(XC
+M6GNPC%N?_!T6__./]3*_?</4=J15&Y^8WX6V:;1$E?X[VQO=0WQUL.9MK!N(
+M=3^[_/WUTTLID\17&>U'KFU'3*4F@)7>(J0",7`>ELYTL+=B_0_\USK*T/]O
+MX8UEA,\^NWXV?/[QL,B[X9.GGW_YZ=5'O[_J?[3"JS=I=J;HNU.T]10?40CN
+M[?#S#/[(X']V^?27-\/'5Y=?W#SZ"2L;]X,.0=,)7%Y@+UAE0^]D=VBV$-.C
+MLV$1_PQ5C+IABLZA_#J@'1ZQ"#"8EYIBT7="F#9US];/M;L\O\/,;K@N0>@'
+MKG8'<\VVAWO,8LM3NZ%%LW.O)D2]Y6,?LS1U"I=/L5'>$WLKO2@,U-QQ\*37
+M7+HG(^;RBZM+PI&S[_:%ER\V/7UYO/[PN//"[[V@J7UYM?/4;YY2[Q]>?OKI
+M7N?T//\GDF;]=\7*'PY<0'I9DEOD:_^T\L],EP&XU5CB'];+]?"+-GK%_>,*
+M$J_W[VEERD5>OI23QJ?BY@"N7_4`1^6$5$VZ\GLA[#N2_EQLW^3KR>7B)LID
+M.;$@]<<[K?2/W*L3+J9Z*3=&C^ND+*YT7IPEFF18R?TD1QLTD?PGGE,2Z*Q<
+M_D'1B9^*BPU691U6#/!1D),4Q[@M[^H*%TEN8);JV[QV>I[S):43,D96?/(=
+M[Q/UNH9%7\I9OI-4NCEIXN2)JNH!$,&N0P5_S\NFMYCK/99)=RZL+*2ES,P3
+M=C5><9`[\#V-O.AZ%3G5M+A%PB#K$<=E!'GMDGPQ"4:XKMTMS8HF15D,KQ@C
+M"?>/Y[M8PSICN6\!9M]KGN%$ZTQ+)41AU^7@@YP_'9!N<XL#KR?^?EKKH7*B
+MFUXQ^U*2WM_01A"7"E]6Y<VAN$PI275S!^R8U1:YA4<AV`[Y,M8[*;URTLRE
+M$XJ9,$,)X$3$KYDL9]!16F%:W25/<\JWD-".QZOR5DH*;_XL%3].3%&XZE/N
+MYG/YJA&Y%GOMVO&T&/VT;W5+($_R\=+_'6S8GY$$Q^2PM/F92U5:`(,KNOAV
+MCK!B_)X7BJB6<Y-^+F]JH=2)6RWY\AJY)8Q*G@R?USX5]Z9;6O%;;.;=ZAV$
+M;`C,PG>0)1[B*GC!ML$:);G9FP>8]4X4XDB2._ROP<WULUQ\,HF86%TD%GA\
+MGP4?ZOCI@`UH)DL;92(B6SS(960(9ESD,^86"R?^K.F[MT2=5E;2$/EP>M^=
+M5`N]1:[&2>H7/.@MR@=D$3V4MRN2)W1'RAER(8":A%%GN<>#C/63;J,^2/&+
+M4WD/J0.C.=$.>ML-;AZ<44(FR>T)#I.E+(/7+&2`9:[GGP"VP^UN$9=8S/D2
+M-2$8BG>^DC(G+,46#K^CIB,4#*F>I3N]ODDN$)EED=(ZUULR$F4:*HGB*%0<
+M2+_POM_/ZH4QVT8H(;GHQ:S<P/UZXA,C!)JO0*'S."\/<H)6`"?NE[M69J&D
+M&1>.X?9WV1-`4OEM>7D/KERA^,K/I!EFJ@,]BTB08_-,44'X7/9\D][L8`FE
+M?(+NA)1GZBZ!JT#*WHOFFW&5W3+B'<OU&4(H02P%H-VP1INQAA"`"93K82[(
+MTGDH`DM"4R^BLD;T/&UQ_,3R.4*A\#TIEJH@WS$IXW)L-T-#X\8M9BC5@E[O
+MKXSX"I=61[21NVE(3]V3CBCNH[RK+I:B*E%W.%,F^,"EQU8X@(65H7UIOA%=
+M5H#*XYP.2+K6T\3,\'.^EOR$79,''/9YPQNK<@L;*>N5"[&R_(.S[DZH8RD+
+MZ(1X(2E%EQD`,\NRT*FP.R28W)<7K5H(.Q^@,;`F2R>B3&@IK!'='//5D%;M
+M`#(Q9]R*1OD,),>$BV9H>BO(2&)-6@-)84&T!F@&BUG:JWNI1UY.4@KD-5)6
+M7B*Q\*1Y+BAT^!KY][=ETFMD:X(+K8D`()*WT"T)HC/*_341RTE!-;Y"5G57
+M)DDPKW,01U[DK%"B@Q@T5GJQO"0.`Y!C_XJY*HJ&#V(4Q"AFG:$F:RF<UT(P
+M(RZ\@FWA27[@NF]KY<JN5?4YXL2$&X<H$_0-U]U$$Q@0,J.4S2OQ*SPT!04,
+M3QQF$%8*)&-B5M2>E2$1XLOR_K`$P6=%R'"NZTL]RO.*6!ZN@EZ+RIIGEG_#
+MBM9[*5=S0E#MC@@O0=$E^":PE=D&F>7R0LZ_/<EQUY>2DW;2&VD?)%'^)Y(K
+M4.YV$L*'`K=@=A&F2>N@C.*SD>2]8X8"R3IPHYJCANR_`'>)$@-AAN-^TM7_
+M*B^O!5/C4OE9J,TX`<M2BHY`(T,Y<0F66<Y8X8!;RT&1#@3.=S=-4-54:>:N
+M*/8>A$XLY<8R"[#&(8/NEK%IP*E&-%LDZ1/Y(BB5Y["3B<EO4;CGE:8%H)SL
+MF[QY@M3#DY0$Y`6.JQP@@7E+0HDM&X?^R7!F@1DAB8W8RT)0Y$2+67%\[Y]^
+0]]ZO?G=^^'_[0&'EA?,``'E+
+`
+end
diff --git a/usr.sbin/pcvt/demo/xmas.vt.gz.uu b/usr.sbin/pcvt/demo/xmas.vt.gz.uu
new file mode 100644
index 0000000..ca2ba3a
--- /dev/null
+++ b/usr.sbin/pcvt/demo/xmas.vt.gz.uu
@@ -0,0 +1,110 @@
+begin 644 xmas.vt.gz
+M'XL(".RV^BX"`WAM87,N=G0`[5U;;R.YL7X<0`A:/T`O#/9EL]A8S6NWH#B7
+M'2!HY"#8Q_-@XV`-'"N>&6EDR7/Q`H/][>&UR>IFR3.>5LXFA[W><9MB?:PB
+M/Q:+U90T^\WB#XNK;G'UM\6W?UG\KEY<_4G<Z7_D=G&UF\T65\V:L>Y_%E?M
+MFM%NN;B2N_WBJMY=+ZY6:U9W2T*(OJ?UFJXTBM@MS:NZ8/?++^9.OVQ*KWTI
+M7=-6R^B&*@.UL<7?_C`GJ83!8VO:6'`+T+]VK36B?$U5;,M6T6AT[?'FJ<C:
+MMVWDQ)I*8T*B6[BN*Z_H]G#X\^'P1E>2:\Z[=X?#V\/AO?Y3F3^-U.YP^'`X
+MO)[;*E18)7.8UU:(\JXZN.OC86/_-__IUQK3?8^ZXJ/^H^V[3\,?7H?NTIVN
+M.JV256AE_K`OSVSGT^[/KJ-67?63[18FNMK:R9INOJ^<AK(SVFT\HEPSWGTW
+MGUD%:%=]NKP<_CC=9)=YZ7-^%E=,D^&YTMC/W'62[DU-2=W+C[;3&U-BARAT
+MMG]%L\!UDA70@V-[2C/Y%\>?UM[4IL=,Y0O7,\*6"M.U04B7MMU%P"*NF^ON
+MPHY'8\;,4"R07A>UH:BZ<86Z-W2]C6&YM<$*T;58:S.B6!/%$KFVVWR*@BT4
+MG`6;2-`^F*JM=Z@J04U@FVYSF>`V$)=46R<M4^E$7&GQR\0@-9)W=&W$"(`X
+M!*GMNMSX0;7M#.3)+(P3L5W/0H]S#%(8FS:7'E)D(!T`PP"XM2H`<!2`8@`,
+M&,40HWK.$3=\JJM^\:-58\@4V$8QU=0*`ZB!;34*T"(`&CFQ3:T0VRP)W1RA
+MEE2[*I)1-1AXFYJG6E0[A0$TJ7FJ00$D!J"`>0HQCQJOZ'BH,&KK-E)C)*H+
+M1F0E@#$HD15&9(V<&L,18ZR7<YY#CY6W"N.V;BRUBJ%*8116%%B%4EAB%-;(
+MJ54USD#K,DR@L1KY0]EV+_+PNMW$0+E"]<-(+-O40(F26&(DULB)@;(YY3Z"
+MQY`2M4<!>Q2J#L9C*8$]*(\EQF.-G-HC4!^O5U/B@K.5IZ'$J"TYL`KUT1+C
+ML63`*I3'$N.Q1DZMHHA5UJ^3L(0YJP1&;5D#JU#O+##O+%:I50(EK\#(*]K4
+M*M&>\G]NK/2"/)Q<`F.V:%+[!.J>!>:>A0+VH606&)D%B#?$B7BC\?&&CI&3
+MD$]#8S07(.X0J+L6&*<%B#L$RFF!<5J`N$,PU#,ZX\PHTD!)C.8"1!P"==<<
+MX[0`$8=`.<TQ3G,0<?#5*7?HX]\827&,Z!R$&ASUTASC,@>A!D>YS#$N<Q!J
+M<(6O8\Z!U'8+$^S"",Y!U,%1;\TQ&G,0=7"4QARC,0=1!\>C#NWE_7A9IIJM
+ME]\#<8SA'$0?'/7:'*,S!]$'1^G,,#IS$'WP^HGX-]EWSGOS&$9U!F(/-J!Z
+M]:;7#F,U`[$'&["Z.O0`&*L9B#U8DP%(]V/&SK!6LS'17SM,$($P-<8,6HU)
+M?7`((`1A`U)7V^V;D!;A&`*(01ADM:Y?V3R+QIGY328)@;$WCF'`(!!A?`3\
+MV@,[F#&QW4Z:@7"$L1',P<&X_3C#Z,U`4,(H!M,GXGS\'P-)BA&?@=B$U:>@
+M7_=4IQC5*8A4Z&H$MXMPAPB'$9^"N(6V([CE`&X6R#M(Y%!L9E`0N]#AS"`C
+M53&_3T$,0^%TT+\J$E7=13C,X5,0T5`YA"-5A%LF0]XGB^HX]MB:0$%H0\5(
+MY4?71LQB46QUH"#(H1RW/DV*46Q!H"#DH0RW'J;*W(8W67(H-J4H"(`HS8U[
+MLWOL@;#Y0T$<1.L\3C09FS@@&EKE\FV)I318*D)TM%IC4P@$1.U8/;^OQQ)+
+M(*^$2:-I%["EQR,\'O,4-J>4+*KH]A5LJ3#%T*`>!*^8-!H*@5CAJ?2+,"M;
+M/Q<Q-PR\,*90D!YR$%#P<X13V62>=6F@4W<C.2`6&ZS&34#E^HI]/1)>K#Y=
+MAK8)F3BC?S)YKJRV\<&5LMUN=+_NGZ3042+?/E+9I/*Z6Z[GB03)BGP:RD`A
+MUS%1;!Z?WFSFGVN)906$W6*XAC!!EN2%R4@Z&G,9[&%Y:89)LZZZW`2C1NE_
+MM>9Y/([A<8/7:R/RT@*3%L`6F9>6F+1,;<&R_FJM\K`*@U7`I"8OW6#2#3"I
+M3:3]RADQ6@RC30T[D?)7ZU7$7UR]U#\1?>70W4.\E;/)53"DJWO!N1.T/X%[
+M=>>6O*2T,:65=SK*+-@>(!%/6J>6O^$%(TV-"KXQD.M7)KX@*8R3D9ER`\2Z
+MR[1$N6@G(STL=@\S!V7*Q5VQK.KCS?X!]!AF(.)&:ISP5RYP!,@IM,Q`IP*]
+MCU#=`.)E@J(R*+V`?;#K#37T`1`I2I-!<0*;3\FPG<K_*^-/S(;%=N#+41-M
+MIHDVH46OZ,JB`%X9^14B_VD^'M0\A-U?#2`&98-G`,JM;`-NC<N:3)ER.TZ$
+M6.EK/0;+8/`!QB;!R'"\+ZN"EQ\^`%#.5V)Z90COROI5E,D3*F5([<IZ.IMX
+M[#":LE")#*F-3[?K9OX!@'(5+)`[`1+6/-5=QO66-9ZBIHXY0J)=KUOC[.0-
+M,DV7KK)M!$["$YZ--G3EZ+U/9?25V2<'76X@;G;M7J5V\#KH=#.0=9O2=\ZT
+M9*FN4Y-T/=?TS5`\;KD'\K0#H4,^DZ],^K`B&62&(S-@F(XI=AEYWGFSPK;[
+M[0B'`PM%WD+1]5S#]!%=NOX.T_K*Y'^ST/)I:`E,U83]D,%QAZ_>1ISW(QP%
+M3&WR^C2I/KGHK>D&$=0@TZ],"C\+W3X%W0)#-=%?9U#<D:SW.,HJ-5/4:3`6
+MP\NZ(_GPL@;C.$[X*_/D)0M),4B:FB587IIAT@R8P_/2')/FB3EXGE^9AV19
+M8($!"V"4S$M+3%H"HU1>6F'2JAO'NIGTOC+/-;/(#8;<`*O:O'2+2;?`JE5>
+M>H5)K[KQ-BMF]95Y")T#E!B599T:(_.LE1AK)4V-D7G62HRUDD5CQDE\98X)
+M9/$P'DL.;,F356)DU2^DMN3)*C&R2@GF4!+FR3QQ)49<":(*F2>GQ,@I07PA
+M\^24&#EE"R?-(-VNS/F:+"#&5PE""Y4GI\+(J4!DH?+D5!@Y%85#,LJ@*W,>
+M*@N),5:!@$+E^:DP?BH01J@\/Q7&3Y4&#^.4N#)'UK*`&&45"!E4GJ8*HZD"
+M@8+*TU1A-%4-C&4'66]E#A-F`3'F*A`6J#Q-%493!<*!)D_3!J-I4\-H!\UZ
+M*W,$-`N-<;@!84&3IVN#T;4!84&3IVN#T;6!8<$XZZW,:=TL),;A!@0$C819
+M+"_<\[4Q]\8`7UVE2:E06[GHWU2V*_X<9+F5.2,=I**0H:63,3ST\&W,/(5Z
+M;GEMS(W/3S4A./(WC;FI/I4SZ^7,>CFS7LZLES/KY<QZ.;->SJR7,^OES'HY
+MLU[.K/];G%F?<N-BHW:^UD.@`_8K=U3V;F:N/WSWQ]GL]'O6PV7?%[X]A.OC
+MQX_]_1LRN!Y.71;IT5B_^_OM\?@S>7EWU(::XW_4_+-SK^UN'LAW1->PKPU;
+MN";XM?0M..A7#^^J!-IAZI>J$2:Y/R*(Q_M$:8>IU<LH#0W*(.T/QX=A9QT/
+M>P_O<+6*J<:)&8AZ^_W]O7E#O!Z1^_N]`WO7C\[K`+7KB]Z34]<C>7001K"7
+M.?P4;S789T)$C@0MME^HQ3/&,0_SU6.7`WWFB.5T@X2'ND&]44!-I<-KHL?L
+MZ&99'+T/'SY$0N1$39:%'`_W^Y17]X>C?L'D)]:T/FI/8E,L6_/Q%?JW+K>Y
+MET?SL1;FMZO0=MN#K=%VYI&A3>(\$EO'W+A*C:[TT=9J=*T/MEJC7W58]LY5
+M-!_8\-'AF8]K^.`0E:G@,-VMJRQC91DKRUA9)I5%K"QB91$KBZ0RCY5YK,QC
+M99Y49K$RBY59K,R2RC16IK$RC95I4KF.E>M8N8Z5ZU#9[M&7ILS\M@N**[#[
+MZZ5?.EV1W_93D\0RJY"M0H(P,6UX$!TZA*V_K>Q`[&>0.!028"!*A%^ZX-BA
+M!`S:[_EW2Y+`DJ77>X`Z!/6QF[/&H#N-`JYF=E0V!0.&!U!35Z7U/#CH(M=(
+M8JWOA]";H:/)4-.T,WEH+BJ:Z$7ZL?(8RZ`8Z85[<X>*D.&HDMAUH6;C*YXV
+MMN_0H.9#:".Q:17T:U/&^"R#WJ6ER_](=W*"`<O``!([%.B_S!D@S,;NP=XH
+MT<4F29X=2]A[J[[WR'(X!DGS<1RMB<=X8819A4Y<Q4XD"!M'TS"T)+K>D8.N
+M3`9KU6'D3V?J<!+%CCOV+1P?^IF5)7WL0@+)!Z;K<,A\2U2W%,8H:>A<4]BV
+M>#S:#[<1)H.ES3N>;6X+DS&Y=]=7S_6@NM.<=ZGBYW$!KD4=3H;.TA'F<5K?
+MX)NI;5]H(NQT:SIZM9>^.X3B?Y'#J,,9IUVOA=,D%)_5B\QF#EZ3QOO),!NU
+M#GV8OW>%37=!YON*;&;G\32N%=5=;$RS03/CQ5USK`OJF4^(<K5BQ:G\46S!
+M7L%8T=H>,G[6N2A]Q?ZJN]'V:G8.%Q:FR(5IXL*U=!$T;+I4P43)(&5[;.,$
+M-U&_Z!2F=("VQ=`;&]-FU-/K>!\NY_-[[GGKQESSX=\4SC*T9?H$$$ZKYU5+
+M)N-]JAY)R'<1]7MXAI<-'GM#`-V866_[T*57RZF4[K^]6MS4OS#]Y90*L)&.
+M:3?"T9[,?P<]@BT;G*S,KO</?>22F)@:Z/>"P42NH4]2N85;A&>L$:$9S3W?
+M?H53F9F,L!NBX]&U9^(Q.&CALP^Y/<WC_U*Q.9.CMJ5SL`6N^E',3`<6[,D[
+MD2?6HJ3E?EA<^X$_&V1R,)/!]J-V`)>QVL8)W#X3"-9XP(O0"<CT8:D9SJBG
+M5C5=0,Q&U.>%_(F<P\=08/>DQ&[L5;QUZGE]Y@-S,[-0F^L-O0=7V#?W*O@M
+M=:^!WT'W"M"H@'GH8BP/XWW1<VPT7]G`.Y]:6/UHF_,_7BF?$P@Z^0Q`KQ(#
+M*OGIY!E.3DU=W2F^._;@\ED*W[Q/7_C6?:ZB;YPGC?MI;1?XZ+QR4YP%^_.+
+M$_%)%:>!3[8X!7QBI6]?)(1HK)5V=J7S/OJO[,S74;8S>Y#.LCD@XG(D_@"3
+M39W8+%#??K@U2(;*?KHD/N=YGH&Z!T$Z@CUU!?<H>L:YSA4=L$-9.WPZC-C$
+MF4U\]5:HQ`KS6:4D,<+=YQS-:3_CDW-D=_`).[(]^.2<33:I_LZU&AQ)<&&P
+M"[_,%_GT(=GYA*(Y&&;3A]Y>=^,Z[L(/3*!-11)-+K[$U?BT)O%I3N+3FL2G
+M.4EH<4-27]V3P;!R\R6>91[>C^3>1_4-)R_O;F^/#[\UG>_>=_6-Z,O\!P@'
+MMEP`KEZ$?MWX->YS/4D8O4T8N,J^N.E[:O,EOB%R(6EX$[G\C%D^[=2<>HJ<
+M@_K3TWI:VA82%A(6$A82%A(6$A82%A(6$A82%A(6$OY'DM!]+-E_+;Z1=D^>
+M_/'KIV>][@N7,$.XOYZ>OK&UI3L!&DZ"FM.:T_,[-O=(R+<FT_;PX\/B:O<[
+M,OSS\5S3`:C@KI_ZHWWGF2_)D%[KG_[)U-*<>IUZ/L'V!D=NEV=Q^6E[B7G:
+MP.EG'&C+GZ&U#XW./QVS[9UE+F*FG6$>XKUXIEF7;_!\,PYI[DS3#31VAKG6
+M+?[^E/.:;K*EC7VVLYYP\O7MXVO3=-.O;VR\[)+))Z!N[$PS+2)/.Z52W.GF
+M3HIZKA4IOP*>:TG*+W]E32IKTI/+Q*33ZBG'>9Y5Z93OG'!5.MMR4R90F4#D
+M\P@]]00J\^2I1?0_<*(LKH[_'BDZ8?#F+U[\[=7;:NH)X.#;;OZ/[6TUZ328
+MGO^3$G]29MM>U`7S%S_<;K</WU=G(//D7)V8JF=@9B'C\\FX&GB,B:EHW^&4
+M.HW"RV?R4IC/SLHYCO\/1"U.\M=%1CD.-::EHAP&&X67S^6E=!''S79;E:7\
+MJVDIC1-^=U=X.0$O5:W[\N/-S]\78I:E^_^2BLHM9S_>_;8P\6N9:+^48?[B
+MO^]NWA4?^?7$E(:8F_=O"S$G(*;=[+Z:GI?VJQ7FKQZJ<MIPDM2^_78''6;M
+MJW+\]:O3]O;;)>8OCJ_^]_;[0M")SA,53GX5)QN?/_CQ[?2,M%\Z-;^I"CFG
+M(F?C0KH7^[>WOZ\F)ZO]+JKYW?[X<%N5MQ,4BOX*_&?C4A,O]O=G8J3];JWY
+M[=NJL+.P\U>PNC<NX'[8WK[ZQ]U%865A97D;8.%@X6#A8.%@X6"&@P1\9&*9
+M<V7.E3E7.%@X6#A8.%@X6#A8.%@X6#A8./BOW8.!\7V2A&1(PLP0)R0D@(1)
+M7ZZ@+"$G*(-0,*)YRIQ$F\\&G^T-5`_OA#.?)W5E)?OO'W3?]!?.K^L*X>R?
+LK:O,4Y_?D[\>]SORP^WQS>WV]N?PK-K59<Q^3M6?Q'9Q/?LG-?=D49*R``"?
+`
+end
diff --git a/usr.sbin/pcvt/fed/Makefile b/usr.sbin/pcvt/fed/Makefile
new file mode 100644
index 0000000..ac1baa4
--- /dev/null
+++ b/usr.sbin/pcvt/fed/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PROG= fed
+SRCS= fed.c select.c edit.c misc.c
+
+DPADD= ${LIBCURSES}
+LDADD= -lncurses
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pcvt/fed/edit.c b/usr.sbin/pcvt/fed/edit.c
new file mode 100644
index 0000000..1d7e4e6
--- /dev/null
+++ b/usr.sbin/pcvt/fed/edit.c
@@ -0,0 +1,331 @@
+/*
+ * Copyright (c) 1992, 2000 Hellmuth Michaelis
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*---------------------------------------------------------------------------
+ *
+ * edit.c font editor edit character
+ * ------------------------------------------
+ *
+ * edit.c, 3.00, last edit-date: [Mon Mar 27 16:35:47 2000]
+ *
+ * $FreeBSD$
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "fed.h"
+
+#define UP 0
+#define DOWN 1
+
+static int pen;
+
+/*---------------------------------------------------------------------------*
+ * fill edit mode command window
+ *---------------------------------------------------------------------------*/
+void edit_mode(void)
+{
+ mvwprintw(cmd_win,1,1,"(W)hite ");
+ mvwprintw(cmd_win,2,1,"(Black ");
+ mvwprintw(cmd_win,3,1,"(I)nvert ");
+ mvwprintw(cmd_win,4,1,"(R)ow BLACK ");
+ mvwprintw(cmd_win,5,1,"(r)ow WHITE ");
+ mvwprintw(cmd_win,6,1,"(C)ol BLACK ");
+ mvwprintw(cmd_win,7,1,"(c)ol WHITE ");
+ mvwprintw(cmd_win,8,1,"(Q)uit/Save ");
+
+ mvwprintw(cmd_win,9 ,1,"e(X)it/undo ");
+ mvwprintw(cmd_win,10,1,"Pen (U)p ");
+ mvwprintw(cmd_win,11,1,"Pen (D)own ");
+ mvwprintw(cmd_win,12,1," ");
+ mvwprintw(cmd_win,13,1,"(^P)rev Line");
+ mvwprintw(cmd_win,14,1,"(^N)ext Line");
+ mvwprintw(cmd_win,15,1,"(^F)orwd Col");
+ mvwprintw(cmd_win,16,1,"(^B)ack Col");
+ wrefresh(cmd_win);
+}
+
+/*---------------------------------------------------------------------------*
+ * edit mode command loop
+ *---------------------------------------------------------------------------*/
+int edit(void)
+{
+ int c, r;
+ char l;
+ unsigned int k_ch;
+
+ c = r = 0;
+
+ pen = UP;
+
+ for(;;)
+ {
+ if(pen == DOWN)
+ dis_cmd(" Edit Mode, the Pen is DOWN");
+ else
+ dis_cmd(" Edit Mode, the Pen is UP");
+
+ l = ((mvwinch(ch_win,(r+1),(c+1))) & A_CHARTEXT);
+ wattron(ch_win,A_REVERSE);
+ mvwprintw(ch_win,(r+1),(c+1),"%c",l);
+ wattroff(ch_win,A_REVERSE);
+ wmove(ch_win,(r+1),(c+1));
+ wrefresh(ch_win);
+
+ k_ch = wgetch(ch_win);
+
+ switch(k_ch)
+ {
+ case K_LEFT:
+ case KEY_LEFT:
+ if(c > 0)
+ {
+ normal_ch(r,c);
+ c--;
+ }
+ break;
+
+ case K_DOWN:
+ case KEY_DOWN:
+ if(r < (ch_height-1))
+ {
+ normal_ch(r,c);
+ r++;
+ }
+ break;
+
+ case K_UP:
+ case KEY_UP:
+ if(r > 0)
+ {
+ normal_ch(r,c);
+ r--;
+ }
+ break;
+
+ case K_RIGHT:
+ case KEY_RIGHT:
+ if(c < (ch_width-1))
+ {
+ normal_ch(r,c);
+ c++;
+ }
+ break;
+
+ case KEY_HOME:
+ normal_ch(r,c);
+ c = r = 0;
+ break;
+
+ case KEY_LL:
+ normal_ch(r,c);
+ c = ch_width-1;
+ r = ch_height-1;
+ break;
+
+ case 0x0c:
+ wrefresh(curscr);
+ break;
+
+ case '\n':
+ case '\r':
+ case ' ' :
+ chg_pt(r,c);
+ break;
+
+ case 'q':
+ pen = UP;
+ normal_ch(r,c);
+ wrefresh(ch_win);
+ return(1);
+ break;
+
+ case 'x':
+ pen = UP;
+ normal_ch(r,c);
+ wrefresh(ch_win);
+ return(0);
+ break;
+
+ case 'w':
+ case 'W':
+ setchr(WHITE);
+ break;
+
+ case 'b':
+ case 'B':
+ setchr(BLACK);
+ break;
+
+ case 'i':
+ case 'I':
+ invert();
+ break;
+
+ case 'r':
+ setrow(WHITE);
+ break;
+
+ case 'R':
+ setrow(BLACK);
+ break;
+
+ case 'c':
+ setcol(WHITE);
+ break;
+
+ case 'C':
+ setcol(BLACK);
+ break;
+
+ case 'u':
+ case 'U':
+ pen = UP;
+ break;
+
+ case 'd':
+ case 'D':
+ pen = DOWN;
+ break;
+
+ default:
+ beep();
+ break;
+
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+void normal_ch(int r, int c)
+{
+ char l = ((mvwinch(ch_win,(r+1),(c+1))) & A_CHARTEXT);
+ wattroff(ch_win,A_REVERSE);
+ if(pen == DOWN)
+ mvwprintw(ch_win,(r+1),(c+1),"*");
+ else
+ mvwprintw(ch_win,(r+1),(c+1),"%c",l);
+ wmove(ch_win,(r+1),(c+1));
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+void chg_pt(int r, int c)
+{
+ char l;
+ l = ((mvwinch(ch_win,(r+1),(c+1))) & A_CHARTEXT);
+ if(l == WHITE)
+ l = BLACK;
+ else
+ l = WHITE;
+ mvwprintw(ch_win,(r+1),(c+1),"%c",l);
+ wmove(ch_win,(r+1),(c+1));
+}
+
+/*---------------------------------------------------------------------------*
+ * invert current character
+ *---------------------------------------------------------------------------*/
+void invert(void)
+{
+ int r,c;
+
+ r = 1;
+
+ while(r <= ch_height)
+ {
+ c = 1;
+ while(c <= ch_width)
+ {
+ if(WHITE == mvwinch(ch_win, r, c))
+ mvwaddch(ch_win, r, c, BLACK);
+ else
+ mvwaddch(ch_win, r, c, WHITE);
+ c++;
+ }
+ r++;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * fill current character black/white
+ *---------------------------------------------------------------------------*/
+void setchr(char type)
+{
+ int r,c;
+
+ r = 1;
+
+ while(r <= ch_height)
+ {
+ c = 1;
+ while(c <= ch_width)
+ {
+ mvwaddch(ch_win, r, c, type);
+ c++;
+ }
+ r++;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * set current row to black/white
+ *---------------------------------------------------------------------------*/
+void setrow(char type)
+{
+ int r,c;
+
+ getyx(ch_win,r,c);
+
+ c = 1;
+
+ while(c <= ch_width)
+ {
+ mvwaddch(ch_win, r, c, type);
+ c++;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * set current column to black/white
+ *---------------------------------------------------------------------------*/
+void setcol(char type)
+{
+ int r,c;
+
+ getyx(ch_win,r,c);
+
+ r = 1;
+
+ while(r <= ch_height)
+ {
+ mvwaddch(ch_win, r, c, type);
+ r++;
+ }
+}
+
+/*---------------------------------- E O F ----------------------------------*/
diff --git a/usr.sbin/pcvt/fed/fed.1 b/usr.sbin/pcvt/fed/fed.1
new file mode 100644
index 0000000..e5c4df3
--- /dev/null
+++ b/usr.sbin/pcvt/fed/fed.1
@@ -0,0 +1,59 @@
+.\" Copyright (c) 2000 Hellmuth Michaelis
+.\"
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" Last Edit-Date: [Mon Mar 27 16:57:41 2000]
+.\"
+.\" $FreeBSD$
+.\"
+.Dd March 27, 2000
+.Dt FED 8
+.Os
+.Sh NAME
+.Nm fed
+.Nd fonteditor for pcvt EGA/VGA font files
+.Sh SYNOPSIS
+.Nm
+filename
+.Sh DESCRIPTION
+The
+.Nm
+utility is a curses based fullscreen application which allows to edit
+.Xr pcvt 4
+fontfiles interactively.
+.Pp
+The
+.Nm
+utility displays a command window, a character display window and a
+character select window.
+.Pp
+In character select mode, it allows to move a character font to
+another position, exchange two character fonts or switch to edit
+character mode.
+.Pp
+In character edit mode, the user is able to edit the selected
+character font or apply several operations to it.
+.Sh BUGS
+No known bugs
+.Sh SEE ALSO
+.Xr pcvt 4
diff --git a/usr.sbin/pcvt/fed/fed.c b/usr.sbin/pcvt/fed/fed.c
new file mode 100644
index 0000000..8c226b5
--- /dev/null
+++ b/usr.sbin/pcvt/fed/fed.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 1992, 2000 Hellmuth Michaelis
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*---------------------------------------------------------------------------
+ *
+ * fed.c font editor main file
+ * -------------------------------------
+ *
+ * last edit-date: [Mon Mar 27 16:36:45 2000]
+ *
+ * $FreeBSD$
+ *
+ *---------------------------------------------------------------------------*/
+
+#define FED
+
+#include "fed.h"
+
+void main(int argc, char *argv[])
+{
+ int i;
+ int row, col;
+ int ret;
+
+ if(argc != 2)
+ {
+ fprintf(stderr,"EGA/VGA Fonteditor, Rel 1.00\n");
+ fprintf(stderr,"usage: %s <fontfilename>\n",argv[0]);
+ exit(1);
+ }
+
+ readfont(argv[1]); /* read fontfile into memory */
+
+ initscr();
+ cbreak();
+ noecho();
+ nonl();
+ keypad(stdscr,TRUE);
+ idlok(stdscr, TRUE);
+
+ move(0,0);
+ standout();
+ addstr(" Interactive EGA/VGA Fonteditor - (c) 1993, 2000 Hellmuth Michaelis ");
+ standend();
+
+/* character horizontal ruler */
+
+ move(WINROW-1, CHCOL + ((WIDTH16 - ch_width)/2) + 1);
+ if(ch_width == WIDTH16)
+ addstr("1234567890123456");
+ else
+ addstr("12345678");
+
+/* charcater vertical ruler */
+
+ for(i=1; i < ch_height+1; i++)
+ mvprintw((WINROW+i), (CHCOL + ((WIDTH16 - ch_width)/2) - 2), "%2d", i);
+
+
+/* select horizontal ruler */
+
+ move(WINROW-1,SETCOL+2);
+ addstr("0 1 2 3 4 5 6 7 8 9 A B C D E F ");
+
+/* select vertical ruler */
+
+ for(i=0; i<10; i++)
+ mvaddch((WINROW+i+1),(SETCOL-1),(i+'0'));
+ for(i=0; i<6; i++)
+ mvaddch((WINROW+10+i+1),(SETCOL-1),(i+'A'));
+
+/* label available commands window */
+
+ move(WINROW-1,CMDCOL+1);
+ addstr("Commands");
+
+ refresh();
+
+/* command window */
+
+ cmd_win = newwin(((WSIZE)+(2*WBORDER)),(CMDSIZE+(2*WBORDER)),
+ WINROW,CMDCOL);
+ keypad(cmd_win,TRUE);
+ idlok(cmd_win, TRUE);
+ box(cmd_win,'|','-');
+
+ sel_mode();
+
+/* character font window */
+
+ ch_win = newwin((ch_height+(2*WBORDER)),(ch_width+(2*WBORDER)),
+ WINROW, CHCOL+((WIDTH16 - ch_width)/2));
+ keypad(ch_win,TRUE);
+ idlok(ch_win, TRUE);
+
+ box(ch_win,'|','-');
+ wrefresh(ch_win);
+
+/* character select window */
+
+ set_win = newwin((WSIZE+(2*WBORDER)),((WSIZE*2)+(2*WBORDER)),
+ WINROW,SETCOL); /* whole character set */
+ keypad(set_win,TRUE);
+ idlok(set_win, TRUE);
+
+ box(set_win,'|','-');
+
+ row = 0;
+ col = 0;
+
+ for(i=0; i<256; i++)
+ {
+ mvwprintw(set_win,row+1,col+1,"%02.2X",i);
+ if(++row > 15)
+ {
+ row = 0;
+ col += 2;
+ }
+ }
+ wmove(set_win,1,1);
+ wrefresh(set_win);
+
+/* start */
+
+ clr_cmd();
+
+ curchar = 0;
+
+ if((ret = selectc()) == 1)
+ {
+ writefont();
+ }
+ endwin();
+}
+
+/*---------------------------------- E O F ----------------------------------*/
diff --git a/usr.sbin/pcvt/fed/fed.h b/usr.sbin/pcvt/fed/fed.h
new file mode 100644
index 0000000..bf488ab
--- /dev/null
+++ b/usr.sbin/pcvt/fed/fed.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 1992, 2000 Hellmuth Michaelis
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * last edit-date: [Mon Mar 27 16:37:27 2000]
+ *
+ * $FreeBSD$
+ */
+
+#include <ncurses.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef FED
+
+int ch_height;
+int ch_width;
+
+int curchar;
+
+WINDOW *ch_win;
+WINDOW *set_win;
+WINDOW *cmd_win;
+
+#else
+
+extern int ch_height; /* current fontfile character dimensions */
+extern int ch_width;
+
+extern int curchar; /* character being edited */
+
+extern WINDOW *ch_win; /* windows */
+extern WINDOW *set_win;
+extern WINDOW *cmd_win;
+
+#endif
+
+#define FONTCHARS 256 /* no of chars in a fontfile */
+
+#define WHITE ('.')
+#define BLACK ('*')
+
+#define K_UP 0x10 /* ^P */
+#define K_DOWN 0x0e /* ^N */
+#define K_RIGHT 0x06 /* ^F */
+#define K_LEFT 0x02 /* ^B */
+
+#define WINROW 3
+#define CMDCOL 3
+#define CHCOL 20
+#define SETCOL 41
+#define WSIZE 16
+#define CMDSIZE 12
+#define WBORDER 1
+
+/* fonts */
+
+#define WIDTH8 8 /* 8 bits wide font */
+#define WIDTH16 16 /* 16 bits wide font */
+
+#define FONT8X8 2048 /* filesize for 8x8 font */
+#define HEIGHT8X8 8 /* 8 scan lines char cell height */
+
+#define FONT8X10 2560 /* filesize for 8x10 font */
+#define HEIGHT8X10 10 /* 10 scan lines char cell height */
+
+#define FONT8X14 3584 /* filesize for 8x14 font */
+#define HEIGHT8X14 14 /* 14 scan lines char cell height */
+#define WIDTH8X14 8 /* 8 bits wide font */
+
+#define FONT8X16 4096 /* filesize for 8x16 font */
+#define HEIGHT8X16 16 /* 16 scan lines char cell height */
+
+#define FONT16X16 8192 /* filesize for 16x16 font */
+#define HEIGHT16X16 16 /* 16 scan lines char cell height */
+
+
+void edit_mode ( void );
+int edit ( void );
+void normal_ch ( int r, int c );
+void chg_pt ( int r, int c );
+void invert ( void );
+void setchr ( char type );
+void setrow ( char type );
+void setcol ( char type );
+void main ( int argc, char *argv[] );
+void readfont ( char *filename );
+void dis_cmd ( char *strg );
+void clr_cmd ( void );
+void save_ch ( void );
+void move_ch ( int src, int dest );
+void xchg_ch ( int src, int dest );
+void display ( int no );
+void sel_mode ( void );
+int selectc ( void );
+void normal_set ( int r, int c );
+int sel_dest ( void );
+void normal_uset ( int r, int c );
+void writefont( void );
+
+/* ------------------------------ EOF ----------------------------------- */
diff --git a/usr.sbin/pcvt/fed/misc.c b/usr.sbin/pcvt/fed/misc.c
new file mode 100644
index 0000000..5a0cbc9
--- /dev/null
+++ b/usr.sbin/pcvt/fed/misc.c
@@ -0,0 +1,344 @@
+/*
+ * Copyright (c) 1992, 2000 Hellmuth Michaelis
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*---------------------------------------------------------------------------
+ *
+ * misc.c font editor misc routines
+ * -----------------------------------------
+ *
+ * last edit-date: [Mon Mar 27 16:38:12 2000]
+ *
+ * $FreeBSD$
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "fed.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+static unsigned char *fonttab; /* ptr to font in core memory */
+
+static char *bitmask[] = {
+ "....", /* 0 */
+ "...*", /* 1 */
+ "..*.", /* 2 */
+ "..**", /* 3 */
+ ".*..", /* 4 */
+ ".*.*", /* 5 */
+ ".**.", /* 6 */
+ ".***", /* 7 */
+ "*...", /* 8 */
+ "*..*", /* 9 */
+ "*.*.", /* A */
+ "*.**", /* B */
+ "**..", /* C */
+ "**.*", /* D */
+ "***.", /* E */
+ "****", /* F */
+ NULL };
+
+static char lfilename[1024]; /* current filename */
+static unsigned int lfilesize; /* current filename's size */
+
+/*---------------------------------------------------------------------------*
+ * read fontfile into memory
+ *---------------------------------------------------------------------------*/
+void readfont(char *filename)
+{
+ FILE *in;
+ struct stat sbuf, *sbp;
+ int ret;
+ char buffer[1024];
+
+ sbp = &sbuf;
+
+ if((in = fopen(filename, "r")) == NULL)
+ {
+ sprintf(buffer, "cannot open file %s for reading", filename);
+ perror(buffer);
+ exit(1);
+ }
+
+ if((fstat(fileno(in), sbp)) != 0)
+ {
+ sprintf(buffer, "cannot fstat file %s", filename);
+ perror(buffer);
+ exit(1);
+ }
+
+ switch(sbp->st_size)
+ {
+ case FONT8X8:
+ ch_height = HEIGHT8X8;
+ ch_width = WIDTH8;
+ break;
+
+ case FONT8X10:
+ ch_height = HEIGHT8X10;
+ ch_width = WIDTH8;
+ break;
+
+ case FONT8X14:
+ ch_height = HEIGHT8X14;
+ ch_width = WIDTH8;
+ break;
+
+ case FONT8X16:
+ ch_height = HEIGHT8X16;
+ ch_width = WIDTH8;
+ break;
+
+ case FONT16X16:
+ ch_height = HEIGHT16X16;
+ ch_width = WIDTH16;
+ break;
+
+ default:
+ fprintf(stderr,"error, file %s is no valid font file, size=%d\n",filename,sbp->st_size);
+ exit(1);
+ }
+
+ if((fonttab = (unsigned char *)malloc((size_t)sbp->st_size)) == NULL)
+ {
+ fprintf(stderr,"error, malloc failed\n");
+ exit(1);
+ }
+
+ strcpy(lfilename, filename); /* save for write */
+ lfilesize = sbp->st_size; /* save for write */
+
+ if((ret = fread(fonttab, sizeof(*fonttab), sbp->st_size, in)) != sbp->st_size)
+ {
+ sprintf(buffer,"error reading file %s, size = %d, ret = %d\n",filename,sbp->st_size, ret);
+ perror(buffer);
+ exit(1);
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * write fontfile to disk
+ *---------------------------------------------------------------------------*/
+void writefont()
+{
+ FILE *in, *out;
+ int ret;
+ char buffer[1024];
+
+ if((in = fopen(lfilename, "r")) != NULL)
+ {
+ int c;
+ char wfn[1024];
+
+ strcpy(wfn, lfilename);
+ strcat(wfn, ".BAK");
+ if((out = fopen(wfn, "w")) == NULL)
+ {
+ sprintf(buffer, "cannot open file %s for writing", wfn);
+ perror(buffer);
+ exit(1);
+ }
+
+ while(( c = fgetc(in) ) != EOF )
+ fputc(c, out);
+
+ fclose(out);
+ fclose(in);
+ }
+
+ if((out = fopen(lfilename, "w")) == NULL)
+ {
+ sprintf(buffer, "cannot open file %s for writing", lfilename);
+ perror(buffer);
+ exit(1);
+ }
+
+ if((ret = fwrite(fonttab, sizeof(*fonttab), lfilesize, out)) != lfilesize)
+ {
+ sprintf(buffer,"error writing file %s, size=%d, ret=%d\n",lfilename,lfilesize, ret);
+ perror(buffer);
+ exit(1);
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * display a string
+ *---------------------------------------------------------------------------*/
+void dis_cmd(char *strg)
+{
+ move(22,0);
+ clrtoeol();
+ mvaddstr(22,0,strg);
+ refresh();
+}
+
+/*---------------------------------------------------------------------------*
+ * clear a command string
+ *---------------------------------------------------------------------------*/
+void clr_cmd(void)
+{
+ move(22,0);
+ clrtoeol();
+ refresh();
+}
+
+/*---------------------------------------------------------------------------*
+ * move char from src to dest
+ *---------------------------------------------------------------------------*/
+void move_ch(int src, int dst)
+{
+ unsigned char *s, *d;
+ int offset = 0;
+
+ if(ch_width == WIDTH16)
+ offset = 2;
+ else
+ offset = 1;
+
+ s = &(fonttab[ch_height * offset * src]);
+ d = &(fonttab[ch_height * offset * dst]);
+
+ bcopy(s, d, (ch_height*offset)); /* src -> dst */
+}
+
+/*---------------------------------------------------------------------------*
+ * exchange char's src and dest
+ *---------------------------------------------------------------------------*/
+void xchg_ch(int src, int dst)
+{
+ unsigned char *s, *d;
+ unsigned char buf[32];
+ int offset = 0;
+
+ if(ch_width == WIDTH16)
+ offset = 2;
+ else
+ offset = 1;
+
+ s = &(fonttab[ch_height * offset * src]);
+ d = &(fonttab[ch_height * offset * dst]);
+
+ bcopy(s, buf, (ch_height*offset)); /* src -> tmp */
+ bcopy(d, s, (ch_height*offset)); /* dst -> src */
+ bcopy(buf, d, (ch_height*offset)); /* tmp -> dst */
+}
+
+/*---------------------------------------------------------------------------*
+ * display the current selected character
+ *---------------------------------------------------------------------------*/
+void display(int no)
+{
+ unsigned char *fontchar;
+ char line[32];
+ int ln_no;
+ unsigned char hibyte;
+ unsigned char lobyte;
+ int offset;
+ int r;
+
+ offset = 0;
+ r = 1;
+ lobyte = 0;
+
+ if(ch_width == WIDTH16)
+ fontchar = &(fonttab[ch_height * 2 * no]);
+ else
+ fontchar = &(fonttab[ch_height * no]);
+
+ for (ln_no = 0; ln_no < ch_height; ln_no++)
+ {
+ hibyte = *(fontchar + (offset++));
+
+ if(ch_width == WIDTH16)
+ {
+ lobyte = *(fontchar + offset++);
+ }
+
+ strcpy(line,bitmask[(int)((hibyte >> 4) & 0x0f)]);
+ strcat(line,bitmask[(int)(hibyte & 0x0f)]);
+
+ if(ch_width == WIDTH16)
+ {
+ strcat(line,bitmask[(int)((lobyte >> 4) & 0x0f)]);
+ strcat(line,bitmask[(int)(lobyte & 0x0f)]);
+ mvwprintw(ch_win, r, 1, "%16.16s", line);
+ }
+ else
+ {
+ mvwprintw(ch_win, r, 1, "%8.8s", line);
+ }
+ r++;
+ }
+ wmove(ch_win, 1, 1);
+ wrefresh(ch_win);
+}
+
+/*---------------------------------------------------------------------------*
+ * save character
+ *---------------------------------------------------------------------------*/
+void save_ch(void)
+{
+ unsigned char *s;
+ int offset = 0;
+ int r, c;
+ unsigned short byte;
+ unsigned short shift;
+
+ if(ch_width == WIDTH16)
+ offset = 2;
+ else
+ offset = 1;
+
+ s = &(fonttab[ch_height * offset * curchar]);
+
+ r = 1;
+
+ while(r <= ch_height)
+ {
+ c = 1;
+ byte = 0;
+ if(offset == 2)
+ shift = 0x8000;
+ else
+ shift = 0x80;
+
+ while(c <= ch_width)
+ {
+ if(mvwinch(ch_win, r, c) == BLACK)
+ byte |= shift;
+ shift = (shift >> 1);
+ c++;
+ }
+ *s++ = byte;
+ r++;
+ }
+}
+
+/*---------------------------------- E O F ----------------------------------*/
+
+
diff --git a/usr.sbin/pcvt/fed/select.c b/usr.sbin/pcvt/fed/select.c
new file mode 100644
index 0000000..e4e44ca
--- /dev/null
+++ b/usr.sbin/pcvt/fed/select.c
@@ -0,0 +1,325 @@
+/*
+ * Copyright (c) 1992, 2000 Hellmuth Michaelis
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*---------------------------------------------------------------------------
+ *
+ * select.c font editor select character
+ * ----------------------------------------------------
+ *
+ * last edit-date: [Mon Mar 27 16:38:50 2000]
+ *
+ * $FreeBSD$
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "fed.h"
+
+int sc, sr, scurchar;
+
+int edit();
+
+void sel_mode(void)
+{
+ mvwprintw(cmd_win,1,1,"(E)dit ");
+ mvwprintw(cmd_win,2,1,"(M)ove ");
+ mvwprintw(cmd_win,3,1,"exchan(G)e ");
+ mvwprintw(cmd_win,4,1,"(Q)uit/Save ");
+ mvwprintw(cmd_win,5,1,"e(X)it/Undo ");
+ mvwprintw(cmd_win,6,1," ");
+ mvwprintw(cmd_win,7,1," ");
+ mvwprintw(cmd_win,8,1," ");
+
+ mvwprintw(cmd_win,9 ,1," ");
+ mvwprintw(cmd_win,10,1," ");
+ mvwprintw(cmd_win,11,1," ");
+ mvwprintw(cmd_win,12,1," ");
+ mvwprintw(cmd_win,13,1,"(^P)rev Line");
+ mvwprintw(cmd_win,14,1,"(^N)ext Line");
+ mvwprintw(cmd_win,15,1,"(^F)orwd Col");
+ mvwprintw(cmd_win,16,1,"(^B)ack Col");
+ wrefresh(cmd_win);
+}
+
+int selectc()
+{
+ int c, r;
+ int ret;
+ char h, l;
+ unsigned int k_ch;
+
+ c = (curchar / 16);
+ r = (curchar % 16);
+
+ for(;;)
+ {
+ dis_cmd(" Select Character");
+
+ sel_mode();
+
+ curchar = r + (c*16);
+
+ display(curchar);
+
+ h = ((mvwinch(set_win,(r+1),((c*2)+1))) & A_CHARTEXT);
+ l = ((mvwinch(set_win,(r+1),((c*2)+2))) & A_CHARTEXT);
+ wattron(set_win,A_REVERSE);
+ mvwprintw(set_win,(r+1),((c*2)+1),"%c%c",h,l);
+ wattroff(set_win,A_REVERSE);
+ wmove(set_win,(r+1),((c*2)+1));
+ wrefresh(set_win);
+
+ k_ch = wgetch(set_win);
+
+ switch(k_ch)
+ {
+ case K_LEFT:
+ case KEY_LEFT:
+ if(c > 0)
+ {
+ normal_set(r,c);
+ c--;
+ }
+ break;
+
+ case K_DOWN:
+ case KEY_DOWN:
+ if(r < 15)
+ {
+ normal_set(r,c);
+ r++;
+ }
+ break;
+
+ case K_UP:
+ case KEY_UP:
+ if(r > 0)
+ {
+ normal_set(r,c);
+ r--;
+ }
+ break;
+
+ case K_RIGHT:
+ case KEY_RIGHT:
+ if(c < 15)
+ {
+ normal_set(r,c);
+ c++;
+ }
+ break;
+
+ case 'e':
+ case 'E':
+ edit_mode();
+ dis_cmd(" Edit Character");
+ display(curchar);
+ ret = edit();
+ if(ret == 1)
+ save_ch();
+ break;
+
+ case 'g':
+ case 'G':
+ dis_cmd(" Exchange: select Destination, then press RETURN or any other Key to ABORT");
+ sr = r;
+ sc = c;
+ scurchar = curchar;
+ if((curchar = sel_dest()) == -1)
+ { /* failsafe */
+ r = sr;
+ c = sc;
+ curchar = scurchar;
+ }
+ else
+ { /* valid return */
+ normal_set(r,c);
+ c = (curchar / 16);
+ r = (curchar % 16);
+ xchg_ch(scurchar,curchar);
+ }
+ break;
+
+ case 'm':
+ case 'M':
+ dis_cmd(" Move: select Destination, then press RETURN or any other Key to ABORT");
+ sr = r;
+ sc = c;
+ scurchar = curchar;
+ if((curchar = sel_dest()) == -1)
+ { /* failsafe */
+ r = sr;
+ c = sc;
+ curchar = scurchar;
+ }
+ else
+ { /* valid return */
+ normal_set(r,c);
+ c = (curchar / 16);
+ r = (curchar % 16);
+ move_ch(scurchar,curchar);
+ }
+ break;
+
+ case 'q':
+ case 'Q':
+ normal_set(r,c);
+ wrefresh(set_win);
+ return(1);
+ break;
+
+ case 'x':
+ case 'X':
+ normal_set(r,c);
+ wrefresh(set_win);
+ return(0);
+ break;
+
+ case 0x0c:
+ wrefresh(curscr);
+ break;
+
+ default:
+ beep();
+ break;
+
+ }
+ }
+}
+
+void normal_set(int r, int c)
+{
+ char h, l;
+
+ h = ((mvwinch(set_win,(r+1),((c*2)+1))) & A_CHARTEXT);
+ l = ((mvwinch(set_win,(r+1),((c*2)+2))) & A_CHARTEXT);
+ wattroff(set_win,A_REVERSE);
+ mvwprintw(set_win,(r+1),((c*2)+1),"%c%c",h,l);
+ wmove(set_win,(r+1),((c*2)+1));
+}
+
+int sel_dest(void)
+{
+ int c, r;
+ char h, l;
+ unsigned int k_ch;
+
+ c = (curchar / 16);
+ r = (curchar % 16);
+
+ for(;;)
+ {
+
+ curchar = r + (c*16);
+
+ display(curchar);
+
+ h = ((mvwinch(set_win,(r+1),((c*2)+1))) & A_CHARTEXT);
+ l = ((mvwinch(set_win,(r+1),((c*2)+2))) & A_CHARTEXT);
+ wattron(set_win,A_UNDERLINE);
+ mvwprintw(set_win,(r+1),((c*2)+1),"%c%c",h,l);
+ wattroff(set_win,A_UNDERLINE);
+ wmove(set_win,(r+1),((c*2)+1));
+ wrefresh(set_win);
+
+ k_ch = wgetch(set_win);
+
+ switch(k_ch)
+ {
+ case K_LEFT:
+ case KEY_LEFT:
+ if(c > 0)
+ {
+ normal_uset(r,c);
+ c--;
+ }
+ break;
+
+ case K_DOWN:
+ case KEY_DOWN:
+ if(r < 15)
+ {
+ normal_uset(r,c);
+ r++;
+ }
+ break;
+
+ case K_UP:
+ case KEY_UP:
+ if(r > 0)
+ {
+ normal_uset(r,c);
+ r--;
+ }
+ break;
+
+ case K_RIGHT:
+ case KEY_RIGHT:
+ if(c < 15)
+ {
+ normal_uset(r,c);
+ c++;
+ }
+ break;
+
+ case '\r':
+ case '\n':
+ normal_uset(r,c);
+ return(r + (c*16));
+
+ case 0x0c:
+ wrefresh(curscr);
+ break;
+
+ default:
+ normal_uset(r,c);
+ return(-1);
+ }
+ }
+}
+
+void normal_uset(int r, int c)
+{
+ char h, l;
+
+ h = ((mvwinch(set_win,(r+1),((c*2)+1))) & A_CHARTEXT);
+ l = ((mvwinch(set_win,(r+1),((c*2)+2))) & A_CHARTEXT);
+
+ wattroff(set_win,A_UNDERLINE);
+ mvwprintw(set_win,(r+1),((c*2)+1),"%c%c",h,l);
+ wmove(set_win,(r+1),((c*2)+1));
+
+ if((r==sr) && (c==sc))
+ {
+ wattron(set_win,A_REVERSE);
+ mvwprintw(set_win,(r+1),((c*2)+1),"%c%c",h,l);
+ wattroff(set_win,A_REVERSE);
+ wmove(set_win,(r+1),((c*2)+1));
+ }
+}
+
+
+
+/*---------------------------------- E O F ----------------------------------*/
diff --git a/usr.sbin/pcvt/fontedit/Makefile b/usr.sbin/pcvt/fontedit/Makefile
new file mode 100644
index 0000000..4851169
--- /dev/null
+++ b/usr.sbin/pcvt/fontedit/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+PROG= fontedit
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pcvt/fontedit/README b/usr.sbin/pcvt/fontedit/README
new file mode 100644
index 0000000..1854129
--- /dev/null
+++ b/usr.sbin/pcvt/fontedit/README
@@ -0,0 +1,36 @@
+When I first saw this posted to rn, I tried to compile this on a machine
+running BSD UNIX. Much to my dissapointment, It said "unable to find
+/usr/include/termio.h" and thus it sat for a couple months. I was able to
+compile it on a 3b5, but I didn't have a vt220 hooked up to it. I was doing
+some unrelated work with ioctl calls and finally realized that it would not be
+too hard to convert it from System V to BSD. It also looked kind of strange
+with the cursor on, so I turned this off. To implement this, compile with
+the "-DCURFIX" flag in the Makefile.
+I am working on a new version that uses curses and that would enable you to
+change the file that your are working on without leaving the program.
+I thought I'd post it as it is now, since it has a lot of uses right away.
+Imagine changing your favorite game to have objects that look like what they
+are instead of the regular characters. Also, I think people should post their
+own character sets, if they come up with some neat stuff.
+ Please send any comments or suggestions to:
+
+ UUCP : ..!harvard!bu-cs!bucsb!eap
+ ARPANET: eap@bucsb.bu.edu
+ CSNET : eap%bucsb@bu-cs
+
+ Have fun,
+
+ - Eric Pearce
+ Boston University
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/usr.sbin/pcvt/fontedit/fontedit.1 b/usr.sbin/pcvt/fontedit/fontedit.1
new file mode 100644
index 0000000..d0b7515
--- /dev/null
+++ b/usr.sbin/pcvt/fontedit/fontedit.1
@@ -0,0 +1,92 @@
+.\" $FreeBSD$
+.\"
+.Dd January 16, 2001
+.Dt FONTEDIT 1
+.Os
+.Sh NAME
+.Nm fontedit
+.Nd "edit fonts"
+.Sh SYNOPSIS
+.Nm
+.Ar file
+.Sh DESCRIPTION
+The
+.Nm
+utility is used to edit the down line reloadable character set (DRCS) of a
+.Tn VT220
+terminal.
+The editor has two display areas, one for displaying the
+entry currently being manipulated, and one for displaying the complete
+DRCS.
+Commands to the editor take the form of function keys.
+.Pp
+The
+.Nm
+utility
+takes one command line parameter, a
+.Ar file
+name.
+This file is used to save the character set.
+If the file exists when
+.Nm
+is invoked, it is read in to initialize the DRCS.
+The file is written to when
+.Nm
+exits.
+.Pp
+Commands to
+.Nm
+take the form of function keys.
+The current definitions are:
+.Bl -tag -width "Cursors"
+.It Ic HELP
+Display a help screen.
+.It Ic F6
+Turn the pixel under the cursor on.
+.It Ic F7
+Turn the pixel under the cursor off.
+.It Ic F13
+Clear the display area.
+.It Ic Find
+Save the current font in the font table.
+Update the DRCS display.
+.It Ic Select
+Extract the entry selected by the cursor in the DRCS display.
+.It Ic Prev
+Move the cursor to the previous entry in the DRCS display.
+.It Ic Next
+Move the cursor to the next entry in the DRCS display.
+.It Ic Insert
+Insert a blank line at the current cursor position.
+The bottom row is lost.
+.It Ic Remove
+Remove the row at the current cursor position.
+All rows below the
+current one are shifted up.
+.It Ic Cursors
+Move the cursor in the main display area.
+.El
+.Pp
+If the screen gets garbled, press
+.Aq control\-L .
+.Pp
+To exit
+.Nm ,
+press
+.Aq control\-D .
+The DRCS will be saved in
+.Ar file .
+To exit without saving the DRCS, hit interrupt (usually
+DEL).
+.Sh DIAGNOSTICS
+The
+.Nm
+utility
+will issue a warning when the entry being worked on is not saved, and
+some potentially destructive command, like
+.Ic Select
+is used.
+To
+override the warning message, immediately reissue the command.
+.Sh AUTHORS
+.An Greg Franks
diff --git a/usr.sbin/pcvt/fontedit/fontedit.c b/usr.sbin/pcvt/fontedit/fontedit.c
new file mode 100644
index 0000000..b1444c6
--- /dev/null
+++ b/usr.sbin/pcvt/fontedit/fontedit.c
@@ -0,0 +1,926 @@
+/*
+ * fontedit
+ * Fonteditor for VT220
+ *
+ * BUGS:
+ * o Cursor motion is less than optimal (but who cares at 9600),
+ *
+ * COMPILE:
+ * cc -O fontedit.c -o fontedit
+ * (use Makefile)
+ *
+ * Copyright (c) 1987 by Greg Franks.
+ *
+ * Permission is granted to do anything you want with this program
+ * except claim that you wrote it.
+ *
+ *
+ * REVISION HISTORY:
+ *
+ * Nov 21, 1987 - Fixed man page to say "Fontedit" instead of "Top"
+ * Nov 22, 1987 - Added BSD Compatible ioctl, turned cursor on/off
+ * - eap@bucsf.bu.edu
+ * $FreeBSD$
+ */
+
+void clear_screen();
+#include <stdio.h>
+#ifdef SYSV
+#include <sys/termio.h>
+#endif /* SYSV */
+#ifdef BSD
+#include <sys/ioctl.h>
+#endif /* BSD */
+#if defined (__NetBSD__) || defined (__FreeBSD__)
+#include <sys/termios.h>
+#include <sys/ioctl.h>
+#endif /* __NetBSD__ || __FreeBSD__ */
+#include <signal.h>
+
+#ifdef CURFIX
+#define CURSORON "\033[?25h"
+#define CURSOROFF "\033[?25l"
+#endif /* CURFIX */
+
+#define MAX_ROWS 10
+#define MAX_COLS 8
+
+typedef enum { false, true } bool;
+
+#define KEY_FIND 0x0100
+#define KEY_INSERT 0x0101
+#define KEY_REMOVE 0x0102
+#define KEY_SELECT 0x0103
+#define KEY_PREV 0x0104
+#define KEY_NEXT 0x0105
+#define KEY_F6 0X0106
+#define KEY_F7 0x0107
+#define KEY_F8 0x0108
+#define KEY_F9 0x0109
+#define KEY_F10 0x010a
+#define KEY_F11 0x010b
+#define KEY_F12 0x010c
+#define KEY_F13 0x010d
+#define KEY_F14 0x010e
+#define KEY_HELP 0x010f
+#define KEY_DO 0x0110
+#define KEY_F17 0x0111
+#define KEY_F18 0x0112
+#define KEY_F19 0x0113
+#define KEY_F20 0x0114
+#define KEY_UP 0x0115
+#define KEY_DOWN 0x0116
+#define KEY_RIGHT 0x0117
+#define KEY_LEFT 0x0118
+
+/*
+ * Position of main drawing screen.
+ */
+
+#define ROW_OFFSET 3
+#define COL_OFFSET 10
+
+/*
+ * Position of the DRCS table.
+ */
+
+#define TABLE_ROW 4
+#define TABLE_COL 50
+
+/*
+ *
+ */
+
+#define ERROR_ROW 20
+#define ERROR_COL 40
+
+bool display_table[MAX_ROWS][MAX_COLS];
+
+#define TOTAL_ENTRIES (128 - 32)
+#define SIXELS_PER_CHAR 16
+
+char font_table[TOTAL_ENTRIES][SIXELS_PER_CHAR];
+unsigned int current_entry;
+
+#ifdef SYSV
+struct termio old_stty, new_stty;
+#endif /* SYSV */
+#ifdef BSD
+struct sgttyb old_stty, new_stty;
+#endif /* BSD */
+#if defined (__NetBSD__) || defined (__FreeBSD__)
+struct termios old_stty, new_stty;
+#endif /* __NetBSD__ || __FreeBSD__ */
+FILE * font_file = (FILE *)0;
+
+
+/*
+ * Interrupt
+ * Exit gracefully.
+ */
+
+interrupt()
+{
+ void clear_screen();
+#ifdef CURFIX
+ printf("%s\n",CURSORON);
+#endif /* CURFIX */
+#ifdef SYSV
+ ioctl( 0, TCSETA, &old_stty );
+#endif /* SYSV */
+#ifdef BSD
+ ioctl( 0, TIOCSETP, &old_stty );
+#endif /* BSD */
+#if defined (__NetBSD__) || defined (__FreeBSD__)
+ ioctl( 0, TIOCSETA, &old_stty );
+#endif /* __NetBSD__ || __FreeBSD__ */
+ clear_screen();
+ exit( 0 );
+}
+
+
+/*
+ * Main
+ * Grab input/output file and call main command processor.
+ */
+
+main( argc, argv )
+int argc;
+char *argv[];
+{
+ void command(), init_restore(), clear_screen();
+ void save_table(), get_table(), extract_entry();
+
+ if ( argc != 2 ) {
+ fprintf( stderr, "usage: fontedit filename\n" );
+ exit( 1 );
+ }
+
+ printf( "Press HELP for help\n" );
+ printf( "\033P1;1;2{ @\033\\" ); /* Clear font buffer */
+ fflush( stdout );
+ sleep( 1 ); /* Let terminal catch up */
+ /* otherwise we get frogs */
+
+ if ( ( font_file = fopen( argv[1], "r" ) ) == (FILE *)0 ) {
+ if ( ( font_file = fopen( argv[1], "w" ) ) == (FILE *)0 ) {
+ fprintf( stderr, "Cannot create file %s \n", argv[1] );
+ exit( 1 );
+ }
+ }
+ fclose( font_file );
+
+ if ( ( font_file = fopen( argv[1], "r" ) ) != (FILE *)0 ) {
+ get_table( font_file );
+ fclose( font_file );
+ }
+
+ if ( ( font_file = fopen( argv[1], "r+" ) ) == (FILE *)0 ) {
+ fprintf( stderr, "Cannot open %s for writing\n", argv[1] );
+ exit( 1 );
+ }
+#ifdef CURFIX
+ printf("%s\n",CURSOROFF);
+#endif /* CURFIX */
+#ifdef SYSV
+ ioctl( 0, TCGETA, &old_stty );
+#endif /* SYSV */
+#ifdef BSD
+ ioctl( 0, TIOCGETP, &old_stty );
+#endif /* BSD */
+#if defined (__NetBSD__) || defined (__FreeBSD__)
+ ioctl( 0, TIOCGETA, &old_stty );
+#endif /* __NetBSD__ || __FreeBSD__ */
+ signal( SIGINT, (void *) interrupt );
+ new_stty = old_stty;
+#ifdef SYSV
+ new_stty.c_lflag &= ~ICANON;
+ new_stty.c_cc[VMIN] = 1;
+ ioctl( 0, TCSETA, &new_stty );
+#endif /* SYSV */
+#if defined (__NetBSD__) || defined (__FreeBSD__)
+ new_stty.c_lflag &= ~ICANON;
+ new_stty.c_lflag &= ~ECHO;
+ new_stty.c_cc[VMIN] = 1;
+ ioctl( 0, TIOCSETA, &new_stty );
+#endif /* __NetBSD__ || __FreeBSD__ */
+#ifdef BSD
+ new_stty.sg_flags |= CBREAK;
+ new_stty.sg_flags &= ~ECHO;
+ ioctl( 0, TIOCSETP, &new_stty );
+#endif /* BSD */
+ current_entry = 1;
+ extract_entry( current_entry );
+ init_restore();
+ command();
+#ifdef SYSV
+ ioctl( 0, TCSETA, &old_stty );
+#endif /* SYSV */
+#ifdef BSD
+ ioctl( 0, TIOCSETP, &old_stty );
+#endif /* BSD */
+#if defined (__NetBSD__) || defined (__FreeBSD__)
+ ioctl( 0, TIOCSETA, &old_stty );
+#endif /* __NetBSD__ || __FreeBSD__ */
+ clear_screen();
+
+ /* Overwrite the old file. */
+
+ fseek( font_file, 0L, 0 );
+ save_table( font_file );
+ fclose( font_file );
+#ifdef CURFIX
+ printf("%s\n",CURSORON);
+#endif /* CURFIX */
+}
+
+
+
+/*
+ * Command
+ * Process a function key.
+ *
+ * The user cannot fill in slots 0 or 95 (space and del respecitively).
+ */
+
+void
+command()
+{
+ register int c;
+ register int row, col;
+ register int i, j;
+ bool change, error, override;
+
+ void build_entry(), extract_entry(), send_entry(), print_entry();
+ void highlight(), draw_current(), init_restore(), help();
+ void warning();
+
+ change = false;
+ error = false;
+ override = false;
+ row = 0; col = 0;
+ highlight( row, col, true );
+
+ for ( ;; ) {
+ c = get_key();
+ highlight( row, col, false ); /* turn cursor off */
+
+ if ( error ) {
+ move ( ERROR_ROW, ERROR_COL );
+ printf( "\033[K" ); /* Clear error message */
+ move ( ERROR_ROW+1, ERROR_COL );
+ printf( "\033[K" ); /* Clear error message */
+ error = false;
+ } else {
+ override = false;
+ }
+
+ switch ( c ) {
+
+ case KEY_FIND: /* update DRCS */
+ if ( !change && !override ) {
+ warning( "No changes to save" );
+ override = true;
+ error = true;
+ } else {
+ build_entry( current_entry );
+ send_entry( current_entry );
+ print_entry( current_entry, true );
+ change = false;
+ }
+ break;
+
+ case KEY_F6: /* Turn on pixel */
+ change = true;
+ display_table[row][col] = true;
+ highlight( row, col, false );
+ col = ( col + 1 ) % MAX_COLS;
+ if ( col == 0 )
+ row = ( row + 1 ) % MAX_ROWS;
+ break;
+
+ case KEY_F7: /* Turn off pixel */
+ change = true;
+ display_table[row][col] = false;
+ highlight( row, col, false );
+ col = ( col + 1 ) % MAX_COLS;
+ if ( col == 0 )
+ row = ( row + 1 ) % MAX_ROWS;
+ break;
+
+ case KEY_INSERT: /* Insert a blank row */
+ change = true;
+ for ( j = 0; j < MAX_COLS; ++j ) {
+ for ( i = MAX_ROWS - 1; i > row; --i ) {
+ display_table[i][j] = display_table[i-1][j];
+ }
+ display_table[row][j] = false;
+ }
+ draw_current();
+ break;
+
+ case KEY_REMOVE: /* Remove a row */
+ change = true;
+ for ( j = 0; j < MAX_COLS; ++j ) {
+ for ( i = row; i < MAX_ROWS - 1; ++i ) {
+ display_table[i][j] = display_table[i+1][j];
+ }
+ display_table[MAX_ROWS-1][j] = false;
+ }
+ draw_current();
+ break;
+
+ case KEY_F13: /* Clear buffer */
+ if ( change && !override ) {
+ warning( "Changes not saved" );
+ error = true;
+ override = true;
+ } else {
+ for ( j = 0; j < MAX_COLS; ++j ) {
+ for ( i = 0; i < MAX_ROWS; ++i ) {
+ display_table[i][j] = false;
+ }
+ }
+ draw_current();
+ }
+ break;
+
+ case KEY_SELECT: /* Select font from DRCS */
+ if ( change && !override ) {
+ warning( "Changes not saved" );
+ error = true;
+ override = true;
+ } else {
+ extract_entry( current_entry );
+ draw_current();
+ }
+ break;
+
+ case KEY_PREV: /* Move to prev entry in DRCS */
+ if ( change && !override ) {
+ warning( "Changes not saved" );
+ override = true;
+ error = true;
+ } else {
+ print_entry( current_entry, false );
+ current_entry = current_entry - 1;
+ if ( current_entry == 0 )
+ current_entry = TOTAL_ENTRIES - 2;
+ print_entry( current_entry, true );
+ }
+ break;
+
+ case KEY_NEXT: /* Move to next entry in DRCS */
+ if ( change && !override ) {
+ warning( "Changes not saved" );
+ override = true;
+ error = true;
+ } else {
+ print_entry( current_entry, false );
+ current_entry = current_entry + 1;
+ if ( current_entry == TOTAL_ENTRIES - 1 )
+ current_entry = 1;
+ print_entry( current_entry, true );
+ }
+ break;
+
+ case KEY_UP: /* UP one row. */
+ if ( row == 0 )
+ row = MAX_ROWS;
+ row = row - 1;
+ break;
+
+ case KEY_DOWN: /* Guess. */
+ row = ( row + 1 ) % MAX_ROWS;
+ break;
+
+ case KEY_RIGHT:
+ col = ( col + 1 ) % MAX_COLS;
+ break;
+
+ case KEY_LEFT:
+ if ( col == 0 )
+ col = MAX_COLS;
+ col = col - 1;
+ break;
+
+ case KEY_HELP: /* Display helpful info */
+ clear_screen();
+ help();
+ c = getchar();
+ init_restore();
+ break;
+
+ case '\004': /* All done! */
+ return;
+
+ case '\f': /* Redraw display */
+ init_restore();
+ break;
+
+ default: /* user is a klutzy typist */
+ move ( ERROR_ROW, ERROR_COL );
+ printf( "Unknown key: " );
+ if ( c < 0x20 ) {
+ printf( "^%c", c );
+ } else if ( c < 0x0100 ) {
+ printf( "%c", c );
+ } else {
+ printf( "0x%04x", c );
+ }
+ fflush( stdout );
+ error = true;
+ }
+
+ highlight( row, col, true ); /* turn cursor on */
+ }
+}
+
+
+
+char *key_table[] = {
+ "\033[1~", /* Find */
+ "\033[2~", /* Insert */
+ "\033[3~", /* Remove */
+ "\033[4~", /* Select */
+ "\033[5~", /* Prev */
+ "\033[6~", /* Next */
+ "\033[17~",
+ "\033[18~",
+ "\033[19~",
+ "\033[20~",
+ "\033[21~",
+ "\033[23~",
+ "\033[24~",
+ "\033[25~",
+ "\033[26~",
+ "\033[28~",
+ "\033[29~",
+ "\033[31~",
+ "\033[32~",
+ "\033[33~",
+ "\033[34~",
+ "\033[A",
+ "\033[B",
+ "\033[C",
+ "\033[D",
+ (char *)0 };
+
+/*
+ * get_key
+ * Convert VT220 escape sequence into something more reasonable.
+ */
+
+int
+get_key()
+{
+ register char *p;
+ char s[10];
+ register int i, j;
+
+ p = s;
+ for ( i = 0; i < 10; ++i ) {
+ *p = getchar();
+ if ( i == 0 && *p != '\033' )
+ return( (int)*p ); /* Not an escape sequence */
+ if ( *p != '\033' && *p < 0x0020 )
+ return( (int)*p ); /* Control character */
+ *++p = '\0'; /* Null terminate */
+ for ( j = 0; key_table[j]; ++j ) {
+ if ( strcmp( s, key_table[j] ) == 0 ) {
+ return( j | 0x0100 );
+ }
+ }
+ }
+ return( -1 );
+}
+
+
+
+/*
+ * pad
+ * Emit nulls so that the terminal can catch up.
+ */
+
+pad()
+{
+ int i;
+
+ for ( i = 0; i < 20; ++i )
+ putchar( '\000' );
+ fflush( stdout );
+}
+
+
+
+/*
+ * init_restore
+ * refresh the main display table.
+ */
+
+void
+init_restore()
+{
+ register int row, col;
+ register int i;
+
+ void draw_current(), clear_screen(), print_entry();
+
+ clear_screen();
+
+ for ( col = 0; col < MAX_COLS; ++col ) {
+ move( ROW_OFFSET - 2, col * 3 + COL_OFFSET + 1 );
+ printf( "%d", col );
+ }
+ move( ROW_OFFSET - 1, COL_OFFSET );
+ printf( "+--+--+--+--+--+--+--+--+" );
+ move( ROW_OFFSET + MAX_ROWS * 2, COL_OFFSET );
+ printf( "+--+--+--+--+--+--+--+--+" );
+
+ for ( row = 0; row < MAX_ROWS; ++row ) {
+ if ( row != 0 && row != 7 ) {
+ move( row * 2 + ROW_OFFSET, COL_OFFSET - 2 );
+ printf( "%d|", row );
+ move( row * 2 + ROW_OFFSET + 1, COL_OFFSET - 1 );
+ printf( "|" );
+ move( row * 2 + ROW_OFFSET, COL_OFFSET + MAX_COLS * 3 );
+ printf( "|" );
+ move( row * 2 + ROW_OFFSET + 1, COL_OFFSET + MAX_COLS * 3 );
+ printf( "|" );
+ } else {
+ move( row * 2 + ROW_OFFSET, COL_OFFSET - 2 );
+ printf( "%d*", row );
+ move( row * 2 + ROW_OFFSET + 1, COL_OFFSET - 1 );
+ printf( "*" );
+ move( row * 2 + ROW_OFFSET, COL_OFFSET + MAX_COLS * 3 );
+ printf( "*" );
+ move( row * 2 + ROW_OFFSET + 1, COL_OFFSET + MAX_COLS * 3 );
+ printf( "*" );
+ }
+ }
+ draw_current();
+
+ move( TABLE_ROW - 1, TABLE_COL - 1 );
+ printf( "+-+-+-+-+-+-+-+-+-+-+-+-+" );
+ move( TABLE_ROW + 8 * 2 - 1, TABLE_COL - 1 );
+ printf( "+-+-+-+-+-+-+-+-+-+-+-+-+" );
+ for ( i = 0; i < 8; ++i ) {
+ move ( TABLE_ROW + i * 2, TABLE_COL - 1 );
+ printf( "|" );
+ move ( TABLE_ROW + i * 2 + 1, TABLE_COL - 1 );
+ printf( "+" );
+ move ( TABLE_ROW + i * 2, TABLE_COL + 12 * 2 - 1);
+ printf( "|" );
+ move ( TABLE_ROW + i * 2 + 1, TABLE_COL +12 * 2 - 1);
+ printf( "+" );
+ }
+ for ( i = 0; i < TOTAL_ENTRIES; ++i )
+ print_entry( i, (i == current_entry) ? true : false );
+}
+
+
+
+/*
+ * draw_current
+ * Draw the complete current entry.
+ */
+
+void
+draw_current()
+{
+ register int row, col;
+
+ printf( "\033)0" ); /* Special graphics in G1 */
+ printf( "\016" ); /* Lock in G1 (SO) */
+
+ for ( row = 0; row < MAX_ROWS; ++row ) {
+ for ( col = 0; col < MAX_COLS; ++col ) {
+ if ( display_table[row][col] ) {
+ move( row * 2 + ROW_OFFSET, col * 3 + COL_OFFSET );
+ printf( "\141\141\141" );
+ move( row * 2 + ROW_OFFSET + 1, col * 3 + COL_OFFSET );
+ printf( "\141\141\141" );
+ } else {
+ move( row * 2 + ROW_OFFSET, col * 3 + COL_OFFSET );
+ printf( " " ); /* erase splat */
+ move( row * 2 + ROW_OFFSET + 1, col * 3 + COL_OFFSET );
+ printf( " " ); /* erase splat */
+ }
+ }
+ pad();
+ }
+ printf( "\017" ); /* Lock in G0 (SI) */
+ fflush( stdout );
+}
+
+
+
+/*
+ * highlight
+ * Draw the cursor in the main display area.
+ */
+
+void
+highlight( row, col, on )
+unsigned int row, col;
+bool on;
+{
+
+ printf( "\033)0" ); /* Special graphics in G1 */
+ printf( "\016" ); /* Lock in G1 (SO) */
+ if ( on ) {
+ printf( "\033[7m" ); /* Reverse video cursor */
+ }
+
+ if ( display_table[row][col] ) {
+ move( row * 2 + ROW_OFFSET, col * 3 + COL_OFFSET );
+ printf( "\141\141\141" );
+ move( row * 2 + ROW_OFFSET + 1, col * 3 + COL_OFFSET );
+ printf( "\141\141\141" );
+ } else {
+ move( row * 2 + ROW_OFFSET, col * 3 + COL_OFFSET );
+ printf( " " ); /* erase splat */
+ move( row * 2 + ROW_OFFSET + 1, col * 3 + COL_OFFSET );
+ printf( " " ); /* erase splat */
+ }
+ pad();
+ printf( "\017" ); /* Lock in G0 (SI) */
+ printf( "\033[0m" ); /* normal video */
+ printf( "\b" ); /* Back up one spot */
+ fflush( stdout );
+}
+
+
+
+/*
+ * Clear_screen
+ */
+
+void
+clear_screen()
+{
+ printf( "\033[H\033[J" ); /* Clear screen. */
+ fflush( stdout );
+}
+
+
+
+/*
+ * move
+ */
+
+move( y, x )
+int y, x;
+{
+ printf( "\033[%d;%df", y, x );
+}
+
+
+
+/*
+ * Build_entry
+ * Convert the bit pattern used in the main display area into something
+ * that the vt220 can digest - namely sixels...
+ */
+
+void
+build_entry( entry_no )
+unsigned int entry_no;
+{
+ register int row, col;
+ register unsigned int mask;
+
+ for ( col = 0; col < 8; ++col ) {
+
+ /* Top set of sixels */
+
+ mask = 0;
+ for ( row = 5; row >= 0; --row ) {
+ mask = mask << 1;
+ if ( display_table[row][col] )
+ mask |= 1;
+ }
+ font_table[entry_no][col] = mask + 077;
+
+ /* Bottom set of sixels */
+
+ mask = 0;
+ for ( row = 9; row >= 6; --row ) {
+ mask = mask << 1;
+ if ( display_table[row][col] )
+ mask |= 1;
+ }
+ font_table[entry_no][col+8] = mask + 077;
+ }
+
+}
+
+
+
+/*
+ * Extract_engry
+ * convert sixel representation into an array of bits.
+ */
+
+void
+extract_entry( entry_no )
+unsigned int entry_no;
+{
+ register int row, col;
+ register unsigned int mask;
+
+ for ( col = 0; col < 8; ++col ) {
+
+ /* Top set of sixels */
+
+ mask = font_table[entry_no][col];
+ if ( mask >= 077 )
+ mask -= 077;
+ else
+ mask = 0; /* Bogus entry */
+
+ for ( row = 0; row <= 5; ++row ) {
+ display_table[row][col] = (bool)(mask & 0x0001);
+ mask = mask >> 1;
+ }
+
+ /* Bottom set of sixels */
+
+ mask = font_table[entry_no][col+8];
+ if ( mask >= 077 )
+ mask -= 077;
+ else
+ mask = 0;
+
+ for ( row = 6; row <= 9; ++row ) {
+ display_table[row][col] = (bool)(mask & 0x0001);
+ mask = mask >> 1;
+ }
+ }
+
+}
+
+
+
+/*
+ * Send_entry
+ * Emit the stuff used by the VT220 to load a character into the
+ * DRCS. We could, of course, send more than one entry at a time...
+ */
+
+void
+send_entry( entry_no )
+int entry_no;
+{
+ register char *fp = font_table[entry_no];
+
+ printf( "\033P1;%d;1;0;0;0{ @%c%c%c%c%c%c%c%c/%c%c%c%c%c%c%c%c\033\\",
+ entry_no,
+ fp[ 0], fp[ 1], fp[ 2], fp[ 3], fp[ 4], fp[ 5], fp[ 6], fp[ 7],
+ fp[ 8], fp[ 9], fp[10], fp[11], fp[12], fp[13], fp[14], fp[15] );
+}
+
+
+
+/*
+ * Print_entry
+ * The terminal normally has G0 in GL. We don't want to change
+ * this, nor do we want to use GR. Sooooo send out the necessary
+ * magic for shifting in G2 temporarily for the character that we
+ * want to display.
+ */
+
+void
+print_entry( entry_no, highlight )
+register unsigned int entry_no;
+bool highlight;
+{
+
+ register int y, x;
+
+ y = entry_no & 0x07;
+ x = entry_no >> 3 & 0x1f;
+ entry_no += 32; /* Map up to G set */
+
+ move( y * 2 + TABLE_ROW, x * 2 + TABLE_COL );
+
+ if ( highlight )
+ printf( "\033[7m" );
+
+ printf( "\033* @" ); /* select DRCS into G2 */
+ printf( "\033N" ); /* select single shift */
+ printf( "%c", entry_no ); /* Draw the character */
+
+ if ( highlight )
+ printf( "\033[0m" );
+}
+
+
+
+/*
+ * Save_table
+ * Save a font table
+ */
+
+void
+save_table( font_file )
+FILE *font_file;
+{
+ register char *fp;
+ register int i;
+
+ for ( i = 0; i < TOTAL_ENTRIES; ++i ) {
+ fp = font_table[i];
+ fprintf( font_file, "\033P1;%d;1;0;0;0{ @%c%c%c%c%c%c%c%c/%c%c%c%c%c%c%c%c\033\\\n",
+ i,
+ fp[ 0], fp[ 1], fp[ 2], fp[ 3], fp[ 4], fp[ 5], fp[ 6], fp[ 7],
+ fp[ 8], fp[ 9], fp[10], fp[11], fp[12], fp[13], fp[14], fp[15] );
+ }
+}
+
+
+
+/*
+ * Get_table
+ * Extract font table entries from a file
+ */
+
+void
+get_table( font_file )
+FILE *font_file;
+{
+ char s[256];
+ register char *p;
+ char *fp;
+ int i;
+ register int j;
+
+ while( fgets( s, 255, font_file ) ) {
+ if ( strncmp( s, "\033P1;", 4 ) != 0 )
+ continue; /* Bogus line */
+ p = &s[4];
+ if ( sscanf( p, "%d", &i ) != 1 )
+ continue; /* Illegal entry number */
+
+ if ( i <= 0 || TOTAL_ENTRIES <= i )
+ continue; /* Bogues entry */
+
+ fp = font_table[i];
+
+ while ( *p && *p != '@' )
+ ++p; /* Skip to font definition */
+ if ( ! *p++ )
+ continue; /* Skip @ */
+
+ for ( j = 0; *p && *p != '\033' && j < 16; ++j, ++p ) {
+ if ( *p == '/' ) {
+ j = 8;
+ ++p;
+ }
+ fp[j] = *p;
+ }
+ send_entry( i );
+ }
+}
+
+
+
+/*
+ * Help
+ * Print out help information.
+ */
+
+void
+help()
+{
+ printf( "Font editor\n\n" );
+ printf( "F6 - Pixel on\n" );
+ printf( "F7 - Pixel off\n" );
+ printf( "F13 - Clear display area\n" );
+ printf( "HELP - This screen\n" );
+ printf( "FIND - Update font table\n" );
+ printf( "INSERT - Insert a blank row\n" );
+ printf( "REMOVE - Remove a row\n" );
+ printf( "SELECT - Select current font table entry\n" );
+ printf( "PREV - Move to previous font table entry\n" );
+ printf( "NEXT - Move to next font table entry\n" );
+ printf( "^D - Exit\n" );
+ printf( "\n\n\n\nPress any key to continue\n" );
+}
+
+
+
+/*
+ * Warning
+ * Issue a warning to the regarding the current status.
+ */
+
+void
+warning( s )
+char *s;
+{
+ move( ERROR_ROW, ERROR_COL );
+ printf( "Warning: %s!\n", s );
+ move( ERROR_ROW+1, ERROR_COL );
+ printf( " Reissue command to override\n" );
+}
diff --git a/usr.sbin/pcvt/fonts/COPYRIGHT b/usr.sbin/pcvt/fonts/COPYRIGHT
new file mode 100644
index 0000000..175f7b8
--- /dev/null
+++ b/usr.sbin/pcvt/fonts/COPYRIGHT
@@ -0,0 +1,38 @@
+The font files:
+
+ vt100pc.814.uu, vt100sg.814.uu, vt220h.808.uu, vt220h.810.uu,
+ vt220h.814.uu, vt220h.816.uu, vt220l.808.uu, vt220l.810.uu,
+ vt220l.814.uu and vt220l.816.uu
+
+in this directory are
+
+ Copyright (c) 1992, 1993, 1994 Hellmuth Michaelis and Joerg Wunsch
+
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. All advertising materials mentioning features or use of this software
+ must display the following acknowledgement:
+ This product includes software developed by
+ Hellmuth Michaelis and Joerg Wunsch
+ 4. The name authors may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/usr.sbin/pcvt/fonts/Makefile b/usr.sbin/pcvt/fonts/Makefile
new file mode 100644
index 0000000..8af01cc
--- /dev/null
+++ b/usr.sbin/pcvt/fonts/Makefile
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+.SUFFIXES: .uu
+.uu:
+ rm -f ${.TARGET}
+ uudecode ${.IMPSRC}
+
+FILES= vt220h.808 vt220h.810 vt220h.814 vt220h.816 \
+ vt220l.808 vt220l.810 vt220l.814 vt220l.816
+#FILES+= vt100pc.814 vt100sg.814
+FILESDIR= ${FONTDIR}
+CLEANFILES= ${FILES}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pcvt/fonts/vt100pc.814.uu b/usr.sbin/pcvt/fonts/vt100pc.814.uu
new file mode 100644
index 0000000..470f9c9
--- /dev/null
+++ b/usr.sbin/pcvt/fonts/vt100pc.814.uu
@@ -0,0 +1,83 @@
+begin 644 vt100pc.814
+M`$1D5$Q$`"`@("`^````/$`X!'@`(B(^(B(````\0#@$>``B%`@4(@```'Q`
+M>$!\`"(4"!0B````?$!X0'P`/@@("`@```!\0'A`?``<(B(J'`(``#A$?$1$
+M`"0H,"@D````>$1X1'@`("`@(#X```!X1'A$>``>(!P"/````$1$?$1$`#X(
+M"`@(````0$!`0'P`/B`\("````!$1$0H$``^"`@("````'Q`>$!``#X@/"`@
+M````/$!`0#P`/"(\)"(````\0#@$>``<(B(B'````#Q`.`1X`!P("`@<````
+M>$1$1'@`("`@(#X```!X1$1$>``$#`0$#@```'A$1$1X`!PB#!`^````>$1$
+M1'@`'`(,`AP```!X1$1$>``$"!(^`@```$1D5$Q$`"(D."0B````/$`X!'@`
+M(A0("`@```!\0'A`?``\(CPB/````#Q`0$`\`"(R*B8B````?$!X0'P`(C8J
+M(B(````\0#@$>``B(B(B'````'Q`>$!\`!X@("`>````?$!X0$``'B`<`CP`
+M```\0$Q$/``>(!P"/````'A$>$A$`!X@'`(\````1$1$1#@`'B`<`CP`````
+M```````````````````8/#P\&!@`&!@`````8V-C(@``````````````-C9_
+M-C8V?S8V````#`P^8V%@/@-#8SX,#```````86,&#!@S8P``````'#8V'#MN
+M9F8[`````#`P,&````````````````P8,#`P,#`8#```````&`P&!@8&!@P8
+M`````````&8\_SQF```````````8&!C_&!@8````````````````&!@8,```
+M````````_P`````````````````````8&````````0,&#!@P8$`````````^
+M8V=O>W-C8SX```````P</`P,#`P,/P``````/F,#!@P8,&-_```````^8P,#
+M'@,#8SX```````8.'C9F?P8&#P``````?V!@8'X#`V,^```````<,&!@?F-C
+M8SX``````']C`P8,&!@8&```````/F-C8SYC8V,^```````^8V-C/P,#!CP`
+M```````8&````!@8`````````!@8````&!@P```````&#!@P8#`8#`8`````
+M`````'X``'X`````````8#`8#`8,&#!@```````^8V,&#`P`#`P``````#YC
+M8V]O;VY@/@``````"!PV8V-_8V-C``````!^,S,S/C,S,WX``````!XS86!@
+M8&$S'@``````?#8S,S,S,S9\``````!_,3$T/#0Q,7\``````'\Q,30\-#`P
+M>```````'C-A8&!O8S,=``````!C8V-C?V-C8V,``````#P8&!@8&!@8/```
+M````#P8&!@8&9F8\``````!S,S8V/#8V,W,``````'@P,#`P,#$Q?P``````
+MP^?_V\/#P\/#``````!C<WM_;V=C8V,``````!PV8V-C8V,V'```````?C,S
+M,SXP,#!X```````^8V-C8VMO/@8'`````'XS,S,^-C,S<P``````/F-C,!P&
+M8V,^``````#_F9D8&!@8&#P``````&-C8V-C8V-C/@``````P\/#P\/#9CP8
+M``````##P\/#V]O_9F8``````,/#9CP8/&;#PP``````P\/#9CP8&!@\````
+M``#_@X8,&#!AP?\``````#PP,#`P,#`P/```````0&!P.!P.!P,!```````\
+M#`P,#`P,##P````('#9C`````````````````````````````/\`&!@,````
+M`````````````````#P&/F9F.P``````<#`P/#8S,S-N```````````^8V!@
+M8SX```````X&!AXV9F9F.P``````````/F-_8&,^```````<-C(P?#`P,'@`
+M`````````#MF9F8^!F8\````<#`P-CLS,S-S```````,#``<#`P,#!X`````
+M``8&``X&!@8&9F8\````<#`P,S8\-C-S```````<#`P,#`P,#!X`````````
+M`.;_V]O;VP``````````;C,S,S,S```````````^8V-C8SX``````````&XS
+M,S,^,#!X````````.V9F9CX&!@\```````!N.S,P,'@``````````#YC.`YC
+M/@``````"!@8?A@8&!L.``````````!F9F9F9CL``````````,/#PV8\&```
+M````````P\/;V_]F``````````!C-AP<-F,``````````&-C8V,_`P8\````
+M````?V8,&#-_```````.&!@8<!@8&`X``````!@8&!@`&!@8&```````<!@8
+M&`X8&!AP```````[;@``````````````J@"J`*H`J@"J`*H`J@``/&;"P,#"
+M9CP,!GP```#,S`#,S,S,S'8`````#!@P`'S&_L#&?``````0.&P`>`Q\S,QV
+M``````#,S`!X#'S,S'8`````8#`8`'@,?,S,=@`````X;#@`>`Q\S,QV````
+M`````'C,P,QX&`QX````$#AL`'S&_L#&?```````S,P`?,;^P,9\`````&`P
+M&`!\QO[`QGP``````,S,`'`P,#`P>``````P>,P`<#`P,#!X`````,!@,`!P
+M,#`P,'@`````QL80.&S&QO[&Q@```#AL.``X;,;&_L;&````&#!@`/YB8'Q@
+M8OX`````````;+H2?I"8?@``````/FS,S/[,S,S.`````!`X;`!\QL;&QGP`
+M`````,;&`'S&QL;&?`````!@,!@`?,;&QL9\`````#!XS`#,S,S,S'8`````
+M8#`8`,S,S,S,=@``````QL8`QL;&QGX&#'@``,;&.&S&QL;&;#@`````QL8`
+MQL;&QL;&?``````("'[(R,C(?@@(`````#AL9&#P8&!@YOP``````,9L.!#^
+M$/X0$`````#XS,SXQ,S>S,SF`````!PT,#`P_#`P,#"PX```&#!@`'@,?,S,
+M=@`````,&#``.!@8&!@\`````!@P8`!\QL;&QGP`````&#!@`,S,S,S,=@``
+M````=MP`W&9F9F9F````=MP`QN;V_M[.QL8`````/&QL/@!^```````````X
+M;&PX`'P````````````P,``P,&#&QGP```````````!_8&!@````````````
+M`'\!`0$``````,#`QLS8,&#<A@P8/@``P,#&S-@P9LZ:/@8&````&!@`&!@\
+M/#P8`````````#9LV&PV````````````V&PV;-@``````"*((H@BB"*((H@B
+MB"*(JE2J5*I4JE2J5*I4JE2Z[KKNNNZZ[KKNNNZZ[A@8&!@8&!@8&!@8&!@8
+M&!@8&!@8&/@8&!@8&!@8&!@8&/@8^!@8&!@8&#8V-C8V-C;V-C8V-C8V````
+M`````/XV-C8V-C8``````/@8^!@8&!@8&#8V-C8V]@;V-C8V-C8V-C8V-C8V
+M-C8V-C8V-C8``````/X&]C8V-C8V-C8V-C8V]@;^````````-C8V-C8V-OX`
+M```````8&!@8&/@8^`````````````````#X&!@8&!@8&!@8&!@8&!\`````
+M```8&!@8&!@8_P````````````````#_&!@8&!@8&!@8&!@8&!\8&!@8&!@`
+M````````_P```````!@8&!@8&!C_&!@8&!@8&!@8&!@?&!\8&!@8&!@V-C8V
+M-C8V-S8V-C8V-C8V-C8V-S`_```````````````_,#<V-C8V-C8V-C8V-O<`
+M_P``````````````_P#W-C8V-C8V-C8V-C8W,#<V-C8V-C8``````/\`_P``
+M`````#8V-C8V]P#W-C8V-C8V&!@8&!C_`/\````````V-C8V-C8V_P``````
+M````````_P#_&!@8&!@8`````````/\V-C8V-C8V-C8V-C8V/P```````!@8
+M&!@8'Q@?```````````````?&!\8&!@8&!@`````````/S8V-C8V-C8V-C8V
+M-C;_-C8V-C8V&!@8&!C_&/\8&!@8&!@8&!@8&!@8^``````````````````?
+M&!@8&!@8__________________\`````````__________#P\/#P\/#P\/#P
+M\/#P#P\/#P\/#P\/#P\/#P__________````````````````=MS8V-QV````
+M`````'S&_,;&_,#`0````/[&QL#`P,#`P`````````#^;&QL;&QL``````#^
+MQF`P&#!@QOX``````````'[8V-C8<`````````!F9F9F?&!@P````````';<
+M&!@8&!@``````/PP>,S,S'@P_```````.&S&QO[&QFPX```````X;,;&QFQL
+M;.X``````#Q@,!A\S,S,>```````````?-;6?``````````&#'S.UN9\8,``
+M`````#A@P,#XP,!@.````````'S&QL;&QL;&````````_@``_@``_@``````
+M`#`P,/PP,#``_```````8#`8#!@P8`#\```````8,&#`8#`8`/P```````X:
+M&A@8&!@8&!@8&!@8&!@8&!@86%AP```````P,```_```,#``````````=MP`
+M=MP```````!PV-AP````````````````````&!@`````````````````&```
+M```````>&!@8&!C8V'@X`````-AL;&QL;```````````<-@P8,CX````````
+=```````^/CX^/CX`````````````````````````
+`
+end
diff --git a/usr.sbin/pcvt/fonts/vt100sg.814.uu b/usr.sbin/pcvt/fonts/vt100sg.814.uu
new file mode 100644
index 0000000..24ea4c7
--- /dev/null
+++ b/usr.sbin/pcvt/fonts/vt100sg.814.uu
@@ -0,0 +1,83 @@
+begin 644 vt100sg.814
+M`$1D5$Q$`"`@("`^````/$`X!'@`(B(^(B(````\0#@$>``B%`@4(@```'Q`
+M>$!\`"(4"!0B````?$!X0'P`/@@("`@```!\0'A`?``<(B(J'`(``#A$?$1$
+M`"0H,"@D````>$1X1'@`("`@(#X```!X1'A$>``>(!P"/````$1$?$1$`#X(
+M"`@(````0$!`0'P`/B`\("````!$1$0H$``^"`@("````'Q`>$!``#X@/"`@
+M````/$!`0#P`/"(\)"(````\0#@$>``<(B(B'````#Q`.`1X`!P("`@<````
+M>$1$1'@`("`@(#X```!X1$1$>``$#`0$#@```'A$1$1X`!PB#!`^````>$1$
+M1'@`'`(,`AP```!X1$1$>``$"!(^`@```$1D5$Q$`"(D."0B````/$`X!'@`
+M(A0("`@```!\0'A`?``\(CPB/````#Q`0$`\`"(R*B8B````?$!X0'P`(C8J
+M(B(````\0#@$>``B(B(B'````'Q`>$!\`!X@("`>````?$!X0$``'B`<`CP`
+M```\0$Q$/``>(!P"/````'A$>$A$`!X@'`(\````1$1$1#@`'B`<`CP`````
+M```````````````````8/#P\&!@`&!@`````8V-C(@``````````````-C9_
+M-C8V?S8V````#`P^8V%@/@-#8SX,#```````86,&#!@S8P``````'#8V'#MN
+M9F8[`````#`P,&````````````````P8,#`P,#`8#```````&`P&!@8&!@P8
+M`````````&8\_SQF```````````8&!C_&!@8````````````````&!@8,```
+M````````_P`````````````````````8&````````0,&#!@P8$`````````^
+M8V=O>W-C8SX```````P</`P,#`P,/P``````/F,#!@P8,&-_```````^8P,#
+M'@,#8SX```````8.'C9F?P8&#P``````?V!@8'X#`V,^```````<,&!@?F-C
+M8SX``````']C`P8,&!@8&```````/F-C8SYC8V,^```````^8V-C/P,#!CP`
+M```````8&````!@8`````````!@8````&!@P```````&#!@P8#`8#`8`````
+M`````'X``'X`````````8#`8#`8,&#!@```````^8V,&#`P`#`P``````#YC
+M8V]O;VY@/@``````"!PV8V-_8V-C``````!^,S,S/C,S,WX``````!XS86!@
+M8&$S'@``````?#8S,S,S,S9\``````!_,3$T/#0Q,7\``````'\Q,30\-#`P
+M>```````'C-A8&!O8S,=``````!C8V-C?V-C8V,``````#P8&!@8&!@8/```
+M````#P8&!@8&9F8\``````!S,S8V/#8V,W,``````'@P,#`P,#$Q?P``````
+MP^?_V\/#P\/#``````!C<WM_;V=C8V,``````!PV8V-C8V,V'```````?C,S
+M,SXP,#!X```````^8V-C8VMO/@8'`````'XS,S,^-C,S<P``````/F-C,!P&
+M8V,^``````#_F9D8&!@8&#P``````&-C8V-C8V-C/@``````P\/#P\/#9CP8
+M``````##P\/#V]O_9F8``````,/#9CP8/&;#PP``````P\/#9CP8&!@\````
+M``#_@X8,&#!AP?\``````#PP,#`P,#`P/```````0&!P.!P.!P,!```````\
+M#`P,#`P,##P````('#9C```````````````````````````````````0.'S^
+M_GPX$```````_H*"_H*"_H*"_@````!$1'Q$1``^"`@("````'Q`>$!\`#X@
+M."`@````/$!`0#P`/"(\)"(```!`0$!`?``^(#P@(````'#8V'``````````
+M`````#`P,/PP,#``_`````!$9%1,1``@("`@/@```$1$1"@0`#X("`@(```8
+M&!@8&!@8^`````````````````#X&!@8&!@8`````````!\8&!@8&!@8&!@8
+M&!@8'P```````!@8&!@8&!C_&!@8&!@8``#_````````````````````_P``
+M`````````````````/\```````````````````#_````````````````````
+M_P```!@8&!@8&!@?&!@8&!@8&!@8&!@8&/@8&!@8&!@8&!@8&!@8_P``````
+M``````````#_&!@8&!@8&!@8&!@8&!@8&!@8&!@``!@P8,!@,!@`_```````
+M8#`8#!@P8`#\`````````/YL;&QL;&P````````"!'X($'X@0``````X;&1@
+M\&!@8.;\`````````````!@`````````J@"J`*H`J@"J`*H`J@``````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````&!@`&!@\/#P8``````@(?LC(R,A^"`@`````.&QD8/!@8&#F_```
+M``````````````````````#&;#@0$/X0$!````````````````````````!\
+MQF`X;,;&;#@,QGP```"!F3QF9F8\F8$``````'R"FJ*BHIJ"?``````\;&P^
+M`'X``````````````#9LV&PV````````````````````````````````````
+M```````````````````````````````````````````````````\)"0\````
+M````````&!A^&!@`?@```````'S&!@0,&##&_@``````?,8&!CP&!L9\````
+M````````````````````````9F9F9GQ@8,``````?O3T]'04%!04````````
+M```8&```````````````````````````````"!@X&!@8&!A^`````#AL;#@`
+M?```````````````V&PV;-@```````#`P,;,V#!FSIH^!@8``,#`QLS8,&#<
+MA@P8/@``````-FS8;#8`````````,#``,#!@QL9\````,!@`$#ALQL;^QL8`
+M```,&``0.&S&QO[&Q@```!!LQA`X;,;&_L;&````=MP`$#ALQL;^QL8`````
+MQL80.&S&QO[&Q@```#AL.``X;,;&_L;&```````^;,S,_LS,S,X``````#QF
+MPL#`PF8\#`9\`#`8#`#^8F!\8&+^````&#!@`/YB8'Q@8OX````0;,8`_F)@
+M?&!B_@````#&Q@#^8F!\8&+^````,!@`/!@8&!@8&#P````,&``\&!@8&!@8
+M/````#QF`#P8&!@8&!@\````9F8`/!@8&!@8&#P`````````````````````
+M`';<`,;F]O[>SL;&````8#``.&S&QL;&;#@````,&``X;,;&QL9L.````!!L
+M@CALQL;&QFPX````=MP`.&S&QL;&;#@`````QL8X;,;&QL9L.```````?LS,
+MS,[,S,Q^```````".FS.UM;F;+B```!@,!@`QL;&QL;&?`````8,&`#&QL;&
+MQL9\`````';<`,;&QL;&QGP`````QL8`QL;&QL;&?````,;&`,;&;#@0$!`X
+M`````````````````````````'S&QMS&QOS`P,````!@,!@`>`Q\S,QV````
+M`!@P8`!X#'S,S'8`````$#AL`'@,?,S,=@``````=MP`>`Q\S,QV``````#,
+MS`!X#'S,S'8`````.&PX`'@,?,S,=@````````!LNA)^D)A^`````````'C,
+MP,QX&`QX````8#`8`'S&_L#&?``````,&#``?,;^P,9\`````!`X;`!\QO[`
+MQGP``````,S,`'S&_L#&?`````#`8#``<#`P,#!X``````P8,``X&!@8&#P`
+M````,'C,`'`P,#`P>```````S,P`<#`P,#!X````````````````````````
+M`';<`-QF9F9F9@````!@,!@`?,;&QL9\`````!@P8`!\QL;&QGP`````.&S&
+M`'S&QL;&?```````=MP`?,;&QL9\``````#&Q@!\QL;&QGP``````````'R2
+MDIR0?@````````)\SM;6YGR``````&`P&`#,S,S,S'8`````&#!@`,S,S,S,
+M=@`````P>,P`S,S,S,QV``````#,S`#,S,S,S'8``````,;&`,;&QL9^!@QX
+=````````````````````````````````````````
+`
+end
diff --git a/usr.sbin/pcvt/fonts/vt220h.808.uu b/usr.sbin/pcvt/fonts/vt220h.808.uu
new file mode 100644
index 0000000..c9585dc
--- /dev/null
+++ b/usr.sbin/pcvt/fonts/vt220h.808.uu
@@ -0,0 +1,49 @@
+begin 644 vt220h.808
+M9%1,1"`@(#X\0#@$>"(^(CQ`.`1X-@@V?$!X0'PV"#9\0'A`?#X("'Q`>'P<
+M(BH<.$1\1"PP*"1X1'A$>"`@/GA$>$1^$`P\1$1\1#X("`A`0$!\/B`\($1$
+M*!`^"`@(?$!X0#X@/"`\0$`\/"(\)CQ`.`1X'"(</$`X!'@<"!QX1$1X("`@
+M/GA$1'@$#`0.>$1X'"(,$#YX1'@<`@P"''A$1'@,$CX"9%1,(B0X)"(\0#@$
+M>"(<"'Q`>'P\(CP^/$`\(C(J)B)\0'A`?"(V*CQ`.`1X(B(<?$!X0'P>(!Y\
+M0'A`'B`>/#Q`3#P>(!X\>$1X3!X@'CQ$1#@>(!P"/'"(<(AP#!(,<(APB'`,
+M!`YPB'"(?"0(/'"(<(QB#@(<<(APC'@2/@)PB'"><!P"''"(<(YP/"(<<(AP
+MCG($"`APB'"<<APB''"(<)QR#@(<<(APB'P2'A)PB'"<<AP2''"(<(AP#A`.
+M<(APB'`<$AQPB'"><!P0'G"(<)YP'!`0<(AX"'`,$@QPB'@(<`P$#G"(>`A\
+M)`@\<(AX#&(.`AQPB'@,>!(^`G"(>!YP'`(<<(AX#G`\(AQPB'@><@0("'"(
+M>!QR'"(<<(AX''(.`AQPB'@(?!(>$G"(>!QR'!(<<(AX"'`.$`YPB'@(<!P2
+M''"(>!YP'!`><(AX'G`<$!```'S&QOS`P````,945'P8`'S&!G[&?```8+`8
+M+$V&`````!@X&!H,````_+8V-@0``'SD<.1\````PJ08*L8``$1$*"@0$```
+M$!`H*$1$``#&QL;&QGP```#\!@8&_````'[`P,!^````PD(D&"08`/Z"*#BJ
+M_@``QE14?!@\`````/YL;.X````$_@'^!````"1^@7XD````$!`H1.X`.$2"
+MJKJJ1#@``-QV`/X``/Z"Q,1H:#``&"PL1$:&_@`````VS,PV```8&`!F9@``
+M`!PT,#`PL.`!`@0($"!`@(!`(!`(!`(!`0($"!`@0/\``#\0"`0"`1@8,&`P
+M&!@8&!@,!@P8&!@```X8&!@8&!@8&!@8&`X```!P&!@8&!@8&!@8&!AP```"
+M`@(B$@H&``#NQM;6?```9F8\&'X8`%@D&"0X8`QX`!@`QL9^#'@`P,#XS/C`
+MP%`@4`P\9CP`\&!X;'A@\``.`&9F/!@\````1"@0*$0`_&9F]F9F_`#P$'(6
+M_CINP@``````&`QX&#!@````````````_@```'R"NJJRJH)\`````'@`````
+M``!X"`@``,;&``````````($?AA^($````````#_````````_P```````/\`
+M``````#_````````_P````````#N9F8L&````GS.UN9\@```?-;<T'P``/X`
+M?,;&?`#^`'@,?,QV`,P`S$AX,'@`.&P`QL;&?``<`,;&QL9\`'``QL;&QGP`
+M`CILUM9LN(``?LS.S,Q^`/XX;,;&;#@`.,8X;,9L.``.`#ALQFPX`'``.&S&
+M;#@`9@`\&!@8/``8)``\&!@\``X`/!@8&#P`<``\&!@8/`#&`/YB>&+^`#A$
+M_F)X8OX`<`#^8GAB_@#^`#ALQO[&`#AL$&S&_L8`#@`X;,;^Q@!P`#ALQO[&
+M```<#!X`````````&!@````('CIB>#P$&'@,.`QX````?(*:HJ*:@GP`P]L\
+M9CS;PP``S%1@4$P``'S&8#``,```?,9@,``P``!\QF`P`#```'S&8#``,```
+M?,9@,``P``!\QF`P`#```'S&8#``,```?,9@,``P``!\QF`P`#```'S&8#``
+M,```?,9@,``P``!\QF`P`#```'S&8#``,```?,9@,``P``!\QF`P`#```'S&
+M8#``,```?,9@,``P``!\QF`P`#```'S&8#``,```?,9@,``P``!\QF`P`#``
+M`'S&8#``,```?,9@,``P``!\QF`P`#```'S&8#``,```?,9@,``P``!\QF`P
+M`#```'S&8#``,```?,9@,``P``!\QF`P`#```'S&8#``,```?,9@,``P``!\
+MQF`P`#```'S&8#``,```?,9@,``P``!\QF`P`#```'S&8#``,```?,9@,``P
+M``!\QF`P`#```'S&8#``,```?,9@,``P``!\QF`P`#```'S&8#``,```?,9@
+M,``P``!\QF`P`#```'S&8#``,```?,9@,``P``!\QF`P`#```'S&8#``,```
+M?,9@,``P``!\QF`P`#```'S&8#``,```?,9@,``P``!\QF`P`#```'S&8#``
+M,```?,9@,``P``!\QF`P`#```'S&8#``,```?,9@,``P``!\QF`P`#```'S&
+M8#``,```?,9@,``P``!\QF`P`#```'S&8#``,```?,9@,``P``!\QF`P`#``
+M`'S&8#``,```?,9@,``P``!\QF`P`#```'S&8#``,```?,9@,``P``!\QF`P
+M`#```'S&8#``,```?,9@,``P``!\QF`P`#```'S&8#``,```?,9@,``P``!\
+MQF`P`#```'S&8#``,```?,9@,``P``!\QF`P`#```'S&8#``,```?,9@,``P
+M``!\QF`P`#```'S&8#``,```?,9@,``P``!\QF`P`#```'S&8#``,```?,9@
+M,``P``!\QF`P`#```'S&8#``,```?,9@,``P``!\QF`P`#```'S&8#``,```
+7?,9@,``P``!\QF`P`#```'S&8#``,`!@
+`
+end
diff --git a/usr.sbin/pcvt/fonts/vt220h.810.uu b/usr.sbin/pcvt/fonts/vt220h.810.uu
new file mode 100644
index 0000000..04689b1
--- /dev/null
+++ b/usr.sbin/pcvt/fonts/vt220h.810.uu
@@ -0,0 +1,60 @@
+begin 644 vt220h.810
+M1&143$0@("`@/CQ`.`1X(B(^(B(\0#@$>"(4"!0B?$!X0'PB%`@4(GQ`>$!\
+M/@@("`A\0'A`?!PB(BH<.$1\1$0D*#`H)'A$>$1X("`@(#YX1'A$>!X@'`(\
+M1$1\1$0^"`@("$!`0$!\/B`\("!$1$0H$#X("`@(?$!X0$`^(#P@(#Q`0$`\
+M/"(\)"(\0#@$>!PB(B(</$`X!'@<"`@(''A$1$1X("`@(#YX1$1$>`0,!`0.
+M>$1$1'@<(@P0/GA$1$1X'`(,`AQX1$1$>`0($CX"1&143$0B)#@D(CQ`.`1X
+M(A0("`A\0'A`?#PB/"(\/$!`0#PB,BHF(GQ`>$!\(C8J(B(\0#@$>"(B(B(<
+M?$!X0'P>("`@'GQ`>$!`'B`<`CP\0$Q$/!X@'`(\>$1X2$0>(!P"/$1$1$0X
+M'B`<`CQPB'"(<`P2$A(,<(APB'`$#`0$#G"(<(AP'"(,$#YPB'"(<!P"#`(<
+M<(APB'`$"!(^`G"(<(AP'A`<`AQPB'"(<!P@/"(<<(APB'`^`@0("'"(<(AP
+M'"(<(AQPB'"(<!PB'@(<<(APB'`,$AX2$G"(<(AP'!(<$AQPB'"(<`X0$!`.
+M<(APB'`<$A(2''"(<(AP'A`<$!YPB'"(<!X0'!`0<(AX"'`,$A(2#'"(>`AP
+M!`P$!`YPB'@(<!PB#!`^<(AX"'`<`@P"''"(>`AP!`@2/@)PB'@(<!X0'`(<
+M<(AX"'`<(!PB''"(>`AP/@($"`APB'@(<!PB'"(<<(AX"'`<(AX"''"(>`AP
+M#!(>$A)PB'@(<!P2'!(<<(AX"'`.$!`0#G"(>`AP'!(2$AQPB'@(<!X0'!`>
+M<(AX"'`>$!P0$```?,;&QOS`P,````#&5%14?!@8`'S&!@9^QL9\``!@L#`8
+M&"Q-A@`````8.!@8&!H,````_+8V-C8&!`````!\Y'#D?`````#"I!@8*L8`
+M``""@D1$*"@0$```$!`H*$1$@H(``,;&QL;&QGP`````_`8&!@;\`````'[`
+MP,#`?@```,)")!@8)!@```#^@B@X*(+^``#&5%14?#@0.````/YL;&QL;.X`
+M````!/X!_@0``````"1^@7XD`````!`0*"A$1.X``#A$@JJZJH)$.````-QV
+M`/X``````/Z"Q,1H:#```!@8+"Q$1H;^```````V2$@V`````!@8``!F9@``
+M`!PT,#`P,+#@```!`@0($"!`@```@$`@$`@$`@$```$"!`@0($#_`````#\0
+M"`0"`0`8&!@P8#`8&!@8&!@8#`8,&!@8&```#A@8&!@8&!@8&!@8&!@8&`X`
+M``!P&!@8&!@8&!@8&!@8&!@8<`````("`B(2"@8``````.[&UM9\``!F9CP8
+M&'X8&`!8)!@8)#A@>`1X`!@P`,;&QGX,>`#`P/C,S/C`P```4"!0&`P\9F8\
+M`/!@>&QX8&#P``8,`&9F/!@8/`````!$*!`H1````/QF9O9F9F;\`/`0<!;N
+M&C9JSH(`````````&`QX&#!@````````````````_@``````?(*ZJKJRJH)\
+M``````!X``````````!X"`@```#&Q@````````````($?@@0?B!`````````
+M`````/\`````````_P````````#_`````````/\`````````_P``````````
+M````[FYF9BP8```"?,[6UN9\@`````!\UM;<T'P``';<`'S&QL9\`';<`'@,
+M?,S,=@#,`,S,2'@P,'@`.&P`QL;&QL9\``P8`,;&QL;&?``P&`#&QL;&QGP`
+M`CILSM;F;+B```!^S,S.S,S,?@!VS#ALQL;&;#@`.,8X;,;&QFPX``X`.&S&
+MQL9L.`!P`#ALQL;&;#@`9@`\&!@8&!@\`!@D`#P8&!@8/``,&``\&!@8&#P`
+M,!@`/!@8&!@\`,8`_F9B>&!B_@`X;`#^8GA@8OX`,!@`_F)X8&+^`';<`!`X
+M;,;^Q@`X;``0.&S&_L8`#!@`$#ALQO[&`&`P`!`X;,;^Q@``#!P,#!X`````
+M`````!@8```````('CID8'@\!!@`>`PX#'@``````'R"FJ*BHIJ"?`##VSQF
+M9CS;PP``````S%1@4$P``'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#``
+M`'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@
+M,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#``
+M`'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@
+M,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#``
+M`'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@
+M,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#``
+M`'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@
+M,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#``
+M`'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@
+M,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#``
+M`'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@
+M,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#``
+M`'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@
+M,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#``
+M`'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@
+M,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#``
+M`'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@
+M,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#``
+M`'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@
+M,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#``
+H`'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#```'S&QF`P,``P`#`P
+`
+end
diff --git a/usr.sbin/pcvt/fonts/vt220h.814.uu b/usr.sbin/pcvt/fonts/vt220h.814.uu
new file mode 100644
index 0000000..9d49552
--- /dev/null
+++ b/usr.sbin/pcvt/fonts/vt220h.814.uu
@@ -0,0 +1,83 @@
+begin 644 vt220h.814
+M`$1D5$Q$`"`@("`^````/$`X!'@`(B(^(B(````\0#@$>``B%`@4(@```'Q`
+M>$!\`"(4"!0B````?$!X0'P`/@@("`@```!\0'A`?``<(B(J'`(``#A$?$1$
+M`"0H,"@D````>$1X1'@`("`@(#X```!X1'A$>``>(!P"/````$1$?$1$`#X(
+M"`@(````0$!`0'P`/B`\("````!$1$0H$``^"`@("````'Q`>$!``#X@/"`@
+M````/$!`0#P`/"(\)"(````\0#@$>``<(B(B'````#Q`.`1X`!P("`@<````
+M>$1$1'@`("`@(#X```!X1$1$>``$#`0$#@```'A$1$1X`!PB#!`^````>$1$
+M1'@`'`(,`AP```!X1$1$>``$"!(^`@```$1D5$Q$`"(D."0B````/$`X!'@`
+M(A0("`@```!\0'A`?``\(CPB/````#Q`0$`\`"(R*B8B````?$!X0'P`(C8J
+M(B(````\0#@$>``B(B(B'````'Q`>$!\`!X@("`>````?$!X0$``'B`<`CP`
+M```\0$Q$/``>(!P"/````'A$>$A$`!X@'`(\````1$1$1#@`'B`<`CP```!P
+MB'"(<``,$A(2#````'"(<(AP``0,!`0.````<(APB'``'"(,$#X```!PB'"(
+M<``<`@P"'````'"(<(AP``0($CX"````<(APB'``'A`<`AP```!PB'"(<``<
+M(#PB'````'"(<(AP`#X"!`@(````<(APB'``'"(<(AP```!PB'"(<``<(AX"
+M'````'"(<(AP``P2'A(2````<(APB'``'!(<$AP```!PB'"(<``.$!`0#@``
+M`'"(<(AP`!P2$A(<````<(APB'``'A`<$!X```!PB'"(<``>$!P0$````'"(
+M>`AP``P2$A(,````<(AX"'``!`P$!`X```!PB'@(<``<(@P0/@```'"(>`AP
+M`!P"#`(<````<(AX"'``!`@2/@(```!PB'@(<``>$!P"'````'"(>`AP`!P@
+M'"(<````<(AX"'``/@($"`@```!PB'@(<``<(APB'````'"(>`AP`!PB'@(<
+M````<(AX"'``#!(>$A(```!PB'@(<``<$AP2'````'"(>`AP``X0$!`.````
+M<(AX"'``'!(2$AP```!PB'@(<``>$!P0'@```'"(>`AP`!X0'!`0````````
+M`'S&QL;&_,#`P``````8V]O;V]M^&!@8````?,8&!G[&QGP``````,#`8#`8
+M'#9C00`````````P<#`P,#08``````````#<9F9F9F8&!@8``````'S&\,#&
+M?`````````#BMAP8.&S&````````@L;&;&PX.!`````````0.#AL;,;&@@``
+M`````,;&QL;&QL9\`````````/P&!@8&_```````````?L#`P,!^````````
+M``#&9CP8/&8\``````#^Q@!L?&P`QOX``````#P8V]O;VWX8/```````_FQL
+M;&QL;&SN````````"`S^`_X,"``````````D9G[#?F8D```````````0$"@H
+M1$3N```````\9L/G_^?#9CP``````````-QV`/X``````````/[&QLQL:#@P
+M````````&#@L;$3&QOX``````````&[8V&X````````````8&```9F8`````
+M```<-#`P,#`PL.`````````!`@0($"!`@````````(!`(!`(!`(!````````
+M``$"!`@0('\````````````_$`@$`@$``!@8&!@8,&`P&!@8&!@8&!@8&!@,
+M!@P8&!@8&!@```X8&!@8&!@8&!@8&!@8&!@8&!@8&!@.``````!P&!@8&!@8
+M&!@8&!@8&!@8&!@8&!@8<````````@("`B(2"@8"``````````!LQM;6UFP`
+M`````,S,>#`P_#`P,```````X'S@P'C`P&`X#'@```P8,`#&QL;&?@8,>```
+M`,#`^,S,^,#`P`````#8<.`P&`P^9F8\``````#P8'AL>&!@8/`````,&`#,
+MS,QX,#`P>`````````!$*!`H1`````````#\9F9F]F9F9OP```#P$'`6_!@P
+M9LZ:/@8&```````````````8#'@`&#!@``````````````````````#^````
+M````````?(*ZJKJRJH)\````````````>````````````````'@("```````
+M`,;&`````````````````````@9^"!!^8$```````````````````/\`````
+M`````````/\``````````````/\``````````````/\``````````````/\`
+M``````````````````#N;F9F;#@````````&?,[>]N9\P```````````?-;6
+MW-!^``````!VW`!\QL;&QGP``````';<`'@,?,S,=@```,S,`,S,2'@P,#!X
+M````.&S&`,;&QL;&QGP````&#!@`QL;&QL;&?````&`P&`#&QL;&QL9\````
+M```&/FS.UM;F;/C``````'[,S,S.S,S,?@```';<`#ALQL;&QFPX````.&S&
+M.&S&QL;&;#@````,&``X;,;&QL9L.````&`P`#ALQL;&QFPX````9F8`/!@8
+M&!@8&#P````\9@`\&!@8&!@8/`````P8`#P8&!@8&!@\````,!@`/!@8&!@8
+M&#P```#&Q@#^9F)X8&)F_@```#ALQ@#^9F!X8&;^````,!@,`/YF8'A@9OX`
+M``!VW``0.&S&QO[&Q@```#ALQA`X;,;&_L;&````#!@`$#ALQL;^QL8````P
+M&``0.&S&QO[&Q@`````,'`P,'@`````````````````8&````````````.`\
+M8,#`P,!@.`QX```\!AP&/`````````````!\@IJBHJ*:@GP```````##VSQF
+M9CS;PP``````````YFQX;&9F``````!\QL9@,#``,#```````'S&QF`P,``P
+M,```````?,;&8#`P`#`P``````!\QL9@,#``,#```````'S&QF`P,``P,```
+M````?,;&8#`P`#`P``````!\QL9@,#``,#```````'S&QF`P,``P,```````
+M?,;&8#`P`#`P``````!\QL9@,#``,#```````'S&QF`P,``P,```````?,;&
+M8#`P`#`P``````!\QL9@,#``,#```````'S&QF`P,``P,```````?,;&8#`P
+M`#`P``````!\QL9@,#``,#```````'S&QF`P,``P,```````?,;&8#`P`#`P
+M``````!\QL9@,#``,#```````'S&QF`P,``P,```````?,;&8#`P`#`P````
+M``!\QL9@,#``,#```````'S&QF`P,``P,```````?,;&8#`P`#`P``````!\
+MQL9@,#``,#```````'S&QF`P,``P,```````?,;&8#`P`#`P``````!\QL9@
+M,#``,#```````'S&QF`P,``P,```````?,;&8#`P`#`P``````!\QL9@,#``
+M,#```````'S&QF`P,``P,```````?,;&8#`P`#`P``````!\QL9@,#``,#``
+M`````'S&QF`P,``P,```````?,;&8#`P`#`P``````!\QL9@,#``,#``````
+M`'S&QF`P,``P,```````?,;&8#`P`#`P``````!\QL9@,#``,#```````'S&
+MQF`P,``P,```````?,;&8#`P`#`P``````!\QL9@,#``,#```````'S&QF`P
+M,``P,```````?,;&8#`P`#`P``````!\QL9@,#``,#```````'S&QF`P,``P
+M,```````?,;&8#`P`#`P``````!\QL9@,#``,#```````'S&QF`P,``P,```
+M````?,;&8#`P`#`P``````!\QL9@,#``,#```````'S&QF`P,``P,```````
+M?,;&8#`P`#`P``````!\QL9@,#``,#```````'S&QF`P,``P,```````?,;&
+M8#`P`#`P``````!\QL9@,#``,#```````'S&QF`P,``P,```````?,;&8#`P
+M`#`P``````!\QL9@,#``,#```````'S&QF`P,``P,```````?,;&8#`P`#`P
+M``````!\QL9@,#``,#```````'S&QF`P,``P,```````?,;&8#`P`#`P````
+M``!\QL9@,#``,#```````'S&QF`P,``P,```````?,;&8#`P`#`P``````!\
+MQL9@,#``,#```````'S&QF`P,``P,```````?,;&8#`P`#`P``````!\QL9@
+M,#``,#```````'S&QF`P,``P,```````?,;&8#`P`#`P``````!\QL9@,#``
+M,#```````'S&QF`P,``P,```````?,;&8#`P`#`P``````!\QL9@,#``,#``
+M`````'S&QF`P,``P,```````?,;&8#`P`#`P``````!\QL9@,#``,#``````
+M`'S&QF`P,``P,```````?,;&8#`P`#`P``````!\QL9@,#``,#```````'S&
+MQF`P,``P,```````?,;&8#`P`#`P``````!\QL9@,#``,#```````'S&QF`P
+M,``P,```````?,;&8#`P`#`P``````!\QL9@,#``,#```````'S&QF`P,``P
+M,```````?,;&8#`P`#`P``````!\QL9@,#``,#```````'S&QF`P,``P,```
+=````?,;&8#`P`#`P``````!\QL9@,#``,#``````
+`
+end
diff --git a/usr.sbin/pcvt/fonts/vt220h.816.uu b/usr.sbin/pcvt/fonts/vt220h.816.uu
new file mode 100644
index 0000000..401db37
--- /dev/null
+++ b/usr.sbin/pcvt/fonts/vt220h.816.uu
@@ -0,0 +1,95 @@
+begin 644 vt220h.816
+M``!$9%1,1``@("`@/@``````/$`X!'@`(B(^(B(``````#Q`.`1X`"(4"!0B
+M``````!\0'A`?``B%`@4(@``````?$!X0'P`/@@("`@``````'Q`>$!\`!PB
+M(BH<`@`````X1'Q$1``D*#`H)```````>$1X1'@`("`@(#X``````'A$>$1X
+M`!X@'`(\``````!$1'Q$1``^"`@("```````0$!`0'P`/B`\("```````$1$
+M1"@0`#X("`@(``````!\0'A`0``^(#P@(```````/$!`0#P`/"(\)"(`````
+M`#Q`.`1X`!PB(B(<```````\0#@$>``<"`@('```````>$1$1'@`("`@(#X`
+M`````'A$1$1X``0,!`0.``````!X1$1$>``<(@P0/@``````>$1$1'@`'`(,
+M`AP``````'A$1$1X``0($CX"``````!$9%1,1``B)#@D(@``````/$`X!'@`
+M(A0("`@``````'Q`>$!\`#PB/"(\```````\0$!`/``B,BHF(@``````?$!X
+M0'P`(C8J(B(``````#Q`.`1X`"(B(B(<``````!\0'A`?``>("`@'@``````
+M?$!X0$``'B`<`CP``````#Q`3$0\`!X@'`(\``````!X1'A(1``>(!P"/```
+M````1$1$1#@`'B`<`CP`````<(APB'``#!(2$@P``````'"(<(AP``0,!`0.
+M``````!PB'"(<``<(@P0/@``````<(APB'``'`(,`AP``````'"(<(AP``0(
+M$CX"``````!PB'"(<``>$!P"'```````<(APB'``'"`\(AP``````'"(<(AP
+M`#X"!`@(``````!PB'"(<``<(APB'```````<(APB'``'"(>`AP``````'"(
+M<(AP``P2'A(2``````!PB'"(<``<$AP2'```````<(APB'``#A`0$`X`````
+M`'"(<(AP`!P2$A(<``````!PB'"(<``>$!P0'@``````<(APB'``'A`<$!``
+M`````'"(>`AP``P2$A(,``````!PB'@(<``$#`0$#@``````<(AX"'``'"(,
+M$#X``````'"(>`AP`!P"#`(<``````!PB'@(<``$"!(^`@``````<(AX"'``
+M'A`<`AP``````'"(>`AP`!P@/"(<``````!PB'@(<``^`@0("```````<(AX
+M"'``'"(<(AP``````'"(>`AP`!PB'@(<``````!PB'@(<``,$AX2$@``````
+M<(AX"'``'!(<$AP``````'"(>`AP``X0$!`.``````!PB'@(<``<$A(2'```
+M````<(AX"'``'A`<$!X``````'"(>`AP`!X0'!`0`````````````#YC8V-C
+M?F!@8&```````!C;V]O;VWX8&!@8`````'S&!@9^QL9\`````````,#`8#`8
+M'#9C00```````````#!P,#`P-!@`````````````W&9F9F9F!@8&!@``````
+M``!\Y'#D?````````````.:V'!@X;,8```````````""QD1L*#@0````````
+M````$#@H;$3&@@````````#&QL;&QL;&QGP``````````/P&!@8&_```````
+M``````!^P,#`P'X``````````````,9F/!@\9CP```````#^Q@!L?'QL`,;^
+M```````\&-O;V]O;?A@8/````````/YL;&QL;&QL;.X```````````@,_@/^
+M#`@````````````D9G[#?F8D````````````$!`X*&Q$1.X````````X1(*J
+MNKJJ@D0X````````````W'8`_@``````````````_H+$Q&AH,#``````````
+M`!@8+"Q$1H;^````````````=HB(=@``````````````&!@``&9F````````
+M```,'A@8&!@8&'@P```````````!`@0($"!`@```````````@$`@$`@$`@$`
+M```````````!`@0($"!_```````````````_$`@$`@$````8&!@8&!@P8#`8
+M&!@8&!@8&!@8&!@8#`8,&!@8&!@8&```#A@8&!@8&!@8&!@8&!@8&!@8&!@8
+M&!@8&`X```````!P&!@8&!@8&!@8&!@8&!@8&!@8&!@8&!@8<``````````"
+M`@("(A(*!@(``````````````&S&UM9L````````9F9F/!@8?A@8&```````
+M<#YP8#Q@8#`<!CP`````````#!@P`,;&QL9^!@QX````P,#XS,S,^,#`P```
+M`````+!@X+`8##YF9CP```````#P8'QF9GQ@8&#P``````P8`,S,S'@P,#`P
+M>```````````1"@0*$0```````````#\9F9F]F9F9F;\`````.`0<!#F#!@P
+M9LJ2/@(``````````````````!@,>```&#!@````````````````````````
+M``#^`````````````'R"NJJJLJJJ@GP``````````````'@`````````````
+M`````'@("`````````#&Q@```````````````````````@9^"!!^8$``````
+M````````````````_P````````````````#_`````````````````/\`````
+M````````````_P````````````````#_````````````````````````YF9B
+M8C08```````````&?,[>]N9\P`````````````!\UM;<T'X`````````=MP`
+M?,;&QL9\`````````';<`'@,?,S,=@````#,S`#,S,QX,#`P,'@`````.&S&
+M`,;&QL;&QL9\``````8,&`#&QL;&QL;&?`````!@,!@`QL;&QL;&QGP`````
+M```&/FS.WO;F;/C`````````?MC8V-[>V-C8?@````!VW``X;,;&QL;&;#@`
+M````.&S&.&S&QL;&QFPX``````P8`#ALQL;&QL9L.`````!@,``X;,;&QL;&
+M;#@`````9F8`/!@8&!@8&!@\`````!@\9@`\&!@8&!@8/``````&#!@`/!@8
+M&!@8&#P`````8#`8`#P8&!@8&!@\`````,;&`/YB8&!\8&!B_@`````X;,8`
+M_F)@?&!@8OX`````8#`8`/YB8'Q@8&+^`````';<`!`X;,;&_L;&Q@`````X
+M;,80.&S&QO[&QL8`````&#!@$#ALQL;^QL;&`````#`8#!`X;,;&_L;&Q@``
+M````"!@("!P`````````````````````&!@`````````````X#Q@P,#`8#@,
+M>```````.`08!#@```````````````!\@IJBHJ*BFH)\``````````##VSQF
+M9CS;PP``````````````YFYX;&8```````!\QL9@,#`P`#`P````````````
+M`````````````````'S&QF`P,#``,#````````!\QL9@,#`P`#`P````````
+M?,;&8#`P,``P,````````'S&QF`P,#``,#````````!\QL9@,#`P`#`P````
+M````?,;&8#`P,``P,````````'S&QF`P,#``,#````````!\QL9@,#`P`#`P
+M````````?,;&8#`P,``P,````````'S&QF`P,#``,#````````!\QL9@,#`P
+M`#`P````````?,;&8#`P,``P,````````'S&QF`P,#``,#````````!\QL9@
+M,#`P`#`P````````?,;&8#`P,``P,````````'S&QF`P,#``,#````````!\
+MQL9@,#`P`#`P````````?,;&8#`P,``P,````````'S&QF`P,#``,#``````
+M``!\QL9@,#`P`#`P````````?,;&8#`P,``P,````````'S&QF`P,#``,#``
+M``````!\QL9@,#`P`#`P````````?,;&8#`P,``P,````````'S&QF`P,#``
+M,#````````!\QL9@,#`P`#`P````````?,;&8#`P,``P,````````'S&QF`P
+M,#``,#````````!\QL9@,#`P`#`P````````?,;&8#`P,``P,````````'S&
+MQF`P,#``,#````````!\QL9@,#`P`#`P````````?,;&8#`P,``P,```````
+M`'S&QF`P,#``,#````````!\QL9@,#`P`#`P````````?,;&8#`P,``P,```
+M`````'S&QF`P,#``,#````````!\QL9@,#`P`#`P````````?,;&8#`P,``P
+M,````````'S&QF`P,#``,#````````!\QL9@,#`P`#`P````````?,;&8#`P
+M,``P,````````'S&QF`P,#``,#````````!\QL9@,#`P`#`P````````?,;&
+M8#`P,``P,````````'S&QF`P,#``,#````````!\QL9@,#`P`#`P````````
+M?,;&8#`P,``P,````````'S&QF`P,#``,#````````!\QL9@,#`P`#`P````
+M````?,;&8#`P,``P,````````'S&QF`P,#``,#````````!\QL9@,#`P`#`P
+M````````?,;&8#`P,``P,````````'S&QF`P,#``,#````````!\QL9@,#`P
+M`#`P````````?,;&8#`P,``P,````````'S&QF`P,#``,#````````!\QL9@
+M,#`P`#`P````````?,;&8#`P,``P,````````'S&QF`P,#``,#````````!\
+MQL9@,#`P`#`P````````?,;&8#`P,``P,````````'S&QF`P,#``,#``````
+M``!\QL9@,#`P`#`P````````?,;&8#`P,``P,````````'S&QF`P,#``,#``
+M``````!\QL9@,#`P`#`P````````?,;&8#`P,``P,````````'S&QF`P,#``
+M,#````````!\QL9@,#`P`#`P````````?,;&8#`P,``P,````````'S&QF`P
+M,#``,#````````!\QL9@,#`P`#`P````````?,;&8#`P,``P,````````'S&
+MQF`P,#``,#````````!\QL9@,#`P`#`P````````?,;&8#`P,``P,```````
+M`'S&QF`P,#``,#````````!\QL9@,#`P`#`P````````?,;&8#`P,``P,```
+M`````'S&QF`P,#``,#````````!\QL9@,#`P`#`P````````?,;&8#`P,``P
+M,````````'S&QF`P,#``,#````````!\QL9@,#`P`#`P````````?,;&8#`P
+M,``P,````````'S&QF`P,#``,#````````!\QL9@,#`P`#`P````````?,;&
+M8#`P,``P,````````'S&QF`P,#``,#````````!\QL9@,#`P`#`P````````
+M?,;&8#`P,``P,````````'S&QF`P,#``,#``````````````````````````
+!`,;&
+`
+end
diff --git a/usr.sbin/pcvt/fonts/vt220l.808.uu b/usr.sbin/pcvt/fonts/vt220l.808.uu
new file mode 100644
index 0000000..02ad944
--- /dev/null
+++ b/usr.sbin/pcvt/fonts/vt220l.808.uu
@@ -0,0 +1,49 @@
+begin 644 vt220l.808
+M``````````!^@:6!O9F!?G[_V__#Y_]^;/[^_GPX$``0.'S^?#@0`#A\./[^
+MUA`X$#A\_OY\$#@``!@\/!@``/__Y\/#Y___/&;#P\/#9CS_PYF]O9G#_Q\'
+M#7G-S,QX/&9F9CP8?A@X/#8R-'#PX']C?V-C9^;`&-L\Y^<\VQB`X/C^^."`
+M``(./OX^#@(`&#Q^&!A^/!AF9F9F9@!F`'_;VWL;&QL`?L/\9F8_PWX``'Y^
+M?@```!@\?AA^/!C_&#Q^&!@8&``8&!@8?CP8```8#/X,&````#!@_F`P````
+M`&!@8'X````D9O]F)````!`X?/[^````_OY\.!``````````````&#P\&!@`
+M&`!L;&P``````&QL_FS^;&P`,'S`?`;\,```QLP8,&;&`#AL.';<SGL`8&#`
+M```````8,&!@8#`8`#`8#`P,&#```&PX_CAL````,##\,#``````````,#!@
+M````_````````````!@8``8,&#!@P(``?,[>]N;&?``P<+`P,##\`'C,##A@
+MP/P`>,P,.`S,>``</&S,_@P>`/S`^`P,S'@`>,S`^,S,>`#\S`P8,#`P`'C,
+MS'C,S'@`>,S,?`S,>```,#```#`P```P,```,#!@&#!@P&`P&````'X``'X`
+M`#`8#`8,&#``/&8&#!@`&`!\QM[>WL!^`#!XS,S\S,P`_&9F?&9F_``\9L#`
+MP&8\`/AL9F9F;/@`_F)H>&AB_@#^8FAX:&#P`#QFP,#.9CX`S,S,_,S,S`!X
+M,#`P,#!X`!X,#`S,S'@`YFQX<'ALY@#P8&!@8F;^`,;N_M;&QL8`QN;VWL[&
+MQ@`X;,;&QFPX`/QF9GQ@8/``>,S,S-QX'`#\9F9\;&;F`'C,P'@,S'@`_+0P
+M,#`P>`#,S,S,S,QX`,S,S,S,>#``QL;&UO[^Q@#&QFPX.&S&`,S,S'@P,'@`
+M_,R8,&3,_`!X8&!@8&!X`,!@,!@,!@(`>!@8&!@8>``0.&S&````````````
+M``#_,#`8`````````'@,?,QV`.!@8'QF9OP```!XS,#,>``<#`Q\S,Q^````
+M>,S\P'@`.&Q@^&!@\````';,S'P,^.!@;'9F9N8`,`!P,#`P>``,`!P,#,S,
+M>.!@9FQX;.8`<#`P,#`P>````,S^_M;&````^,S,S,P```!XS,S,>````-QF
+M9GQ@\```=LS,?`P>``#<=F!@\````'S`?`;\`!`P?#`P-AP```#,S,S,=@``
+M`,;&QGPX````QM;^_FP```#&;#ALQ@```,S,S'P,^```_)@P9/P`'#`PX#`P
+M'``8&!@`&!@8`.`P,!PP,.``=MP````````0.&QLQL;^`'C,P,QX&`QX`,P`
+MS,S,?@`<`'C,_,!X`'[#/`8^9C\`S`!X#'S,?@#@`'@,?,Q^`#`P>`Q\S'X`
+M``!XP,!X##A^PSQF?F`\`,P`>,S\P'@`X`!XS/S`>`#,`'`P,#!X`'S&.!@8
+M&#P`X`!P,#`P>`#&.&S&_L;&`#`P`'C,_,P`'`#\8'A@_````'\,?\Q_`#YL
+MS/[,S,X`>,P`>,S,>```S`!XS,QX``#@`'C,S'@`>,P`S,S,?@``X`#,S,Q^
+M``#,`,S,?`SXPQ@\9F8\&`#,`,S,S,QX`!@8?L#`?A@8.&QD\&#F_`#,S'C\
+M,/PP,/C,S/K&S\;'#AL8/!@8V'`<`'@,?,Q^`#@`<#`P,'@``!P`>,S,>```
+M'`#,S,Q^``#X`/C,S,P`_`#,[/S<S``\;&P^`'X``#AL;#@`?```,``P8,#,
+M>`````#\P,```````/P,#```P\;,WC-FS`_#QLS;-V_/`Q@8`!@8&!@``#-F
+MS&8S````S&8S9LP``"*((H@BB"*(5:I5JE6J5:K;=]ONVW?;[A@8&!@8&!@8
+M&!@8&/@8&!@8&/@8^!@8&#8V-C;V-C8V`````/XV-C8``/@8^!@8&#8V]@;V
+M-C8V-C8V-C8V-C8``/X&]C8V-C8V]@;^````-C8V-OX````8&/@8^```````
+M``#X&!@8&!@8&!\````8&!@8_P````````#_&!@8&!@8&!\8&!@`````_P``
+M`!@8&!C_&!@8&!@?&!\8&!@V-C8V-S8V-C8V-S`_```````_,#<V-C8V-O<`
+M_P``````_P#W-C8V-C8W,#<V-C8``/\`_P```#8V]P#W-C8V&!C_`/\````V
+M-C8V_P``````_P#_&!@8`````/\V-C8V-C8V/P```!@8'Q@?```````?&!\8
+M&!@`````/S8V-C8V-C;_-C8V&!C_&/\8&!@8&!@8^``````````?&!@8____
+M______\`````______#P\/#P\/#P#P\/#P\/#P______````````=MS(W'8`
+M`'C,^,SXP,``_,S`P,#```#^;&QL;&P`_,Q@,&#,_````'[8V-AP``!F9F9F
+M?&#``';<&!@8&`#\,'C,S'@P_#ALQO[&;#@`.&S&QFQL[@`<,!A\S,QX````
+M?MO;?@``!@Q^V]M^8,`\8,#\P&`\`'C,S,S,S,P``/P`_`#\```P,/PP,`#\
+M`&`P&#!@`/P`&#!@,!@`_``.&QL8&!@8&!@8&!@8V-AP,#``_``P,```=MP`
+M=MP``#AL;#@`````````&!@`````````&`````\,#`SL;#P<>&QL;&P```!P
+7&#!@>```````/#P\/```````````````
+`
+end
diff --git a/usr.sbin/pcvt/fonts/vt220l.810.uu b/usr.sbin/pcvt/fonts/vt220l.810.uu
new file mode 100644
index 0000000..7cba2cb
--- /dev/null
+++ b/usr.sbin/pcvt/fonts/vt220l.810.uu
@@ -0,0 +1,60 @@
+begin 644 vt220l.810
+M`````````````'Z!I:6!@;V9@7Y^_]O;___#Y_]^;/[^?'PX.!`0````$#A\
+M_GPX$``X?'PX?/[^?#A\$!`X.'S^_GPX?````!@\/!@```#____GP\/G____
+M```\9D)"9CP``/__PYF]O9G#__\`#P</?<S,S'@``#QF9F8\&'X8`#\S/S`P
+M,'#PX`!_8W]C8V-GYL```-M^/.?G/'[;````@.#X_OC@@`````(./OX^#@(`
+M&!@\?A@8?CP8&&9F9F9F9@!F9@!_V]O;>QL;&QL`/&8P;,9L&,QX````````
+M?GY^?@`8&#Q^&!A^/!C_&!@\/'X8&!@8&!@8&!@8?CP\&!@`````&`S^#!@`
+M`````#!@_F`P``````#`P,#`_@``````)&;_9B0``!@8/#Q^?O__``#__WY^
+M/#P8&```````````````,'AX>#`P`#`P`&QL;"@```````!L;/YL;&S^;&P`
+M&'[8V'PV-OPP`,;,#!@X,&!FQ@`X;&PX,';<S'8`X.!@8,````````P8,#`P
+M,#`8#`!@,!@8&!@8,&````#&;#C^.&S&`````#`P_#`P````````<'`P,&``
+M``````#^```````````````P,```#`P8&#`P8&``?,;.WM;VYL9\`#!P,#`P
+M,#`P_`!\QL8&##A@QOX`?,;&!CP&QL9\``P,'#QLS/X,'@#^P,#\!@8&QGP`
+M'#!@P/S&QL9\`/[&!@P,&!@8&`!\QL;&?,;&QGP`?,;&QGX&#!AP`````#`P
+M```P,````#`P```P,&`````8,&#`8#`8``````#\``#\`````&`P&`P8,&``
+M?,;&!@P8&``8`'S&SMK6WL#&?``X?,;&QO[&QL8`_&9F9GQF9F;\`#QFP,#`
+MP,!F/`#X;&9F9F9F;/@`_F)@:'AH8&+^`/YB8&AX:&!@\``\9L#`P,[&9CP`
+MQL;&QO[&QL;&`#P8&!@8&!@8/``>#`P,#`S,S'@`YF9L;'AL;&;F`/!@8&!@
+M8&)F_@#&[O[^UL;&QL8`QN;F]M[.SL;&`#ALQL;&QL9L.`#\9F9F?&!@8/``
+M?,;&QL;&SGP.`/QF9F9X;&QFY@!\QL!@.`P&QGP`?EH8&!@8&!@\`,;&QL;&
+MQL;&?`!F9F9F9F9F/!@`QL;&QL;6_N[&`,;&;#@0.&S&Q@#,S,S,>#`P,'@`
+M_L:,&#!@PL;^`!X8&!@8&!@8'@``8&`P,!@8#`P`\#`P,#`P,##P`!`X;,8`
+M``````````````````#_,#`8`````````````'@,?,S,=@#@8&!\9F9F9MP`
+M````?,;`P,9\`!P,#'S,S,S,=@````!\QO[`P'P`.&Q@\&!@8&#P`````';,
+MS'P,S'C@8&!L=F9F9N8`,#``<#`P,#!X``P,``P,#`S,S'C@8&!F;'AX;.8`
+M<#`P,#`P,#!X`````,S^_M;6U@````#<YL;&QL8`````?,;&QL9\`````-QF
+M9F9\8/````!VS,S,?`P>````W'9F8&#P`````'S&<!S&?``@8&#\8&!@;#@`
+M````S,S,S,QV`````,;&QL9L.`````#&UM;^_FP`````QFPX.&S&`````,;&
+MQGX&!OP```#^C!@P8OX`#A@8&'`8&!@.`!@8&!@`&!@8&`#@,#`P'#`P,.``
+M=MP``````````````!`X;,;&_@!\QL#`P,9\#`8\`,P`S,S,S,QV```.`'S&
+M_L#`?``\9@`\!CYF9C\``,P`>`Q\S,QV``!P`'@,?,S,=@`P,`!X#'S,S'8`
+M````?L#`P'X&/#QF`#QF?F!@/`#,``!\QO[`P'P``'``?,;^P,!\``#,`'`P
+M,#`P>``X;``X&!@8&#P``.``<#`P,#!X`,8`.&S&_L;&Q@`P,`!XS/S,S,P`
+M'`#\8&!X8&#\`````'X;?MC8?P`^;,S,_LS,S,X`.&P`?,;&QL9\``#&`'S&
+MQL;&?```<`!\QL;&QGP`>,P`S,S,S,QV``!P`,S,S,S,=@``Q@#&QL9^!@;\
+MQCALQL;&QFPX`,8`QL;&QL;&?``8&'[`P,#`?A@8.&QD8/A@8.;\`,S,>##\
+M,/PP,`#XS,S,^L;/QL<`#AL8&#P8&!C8<``<`'@,?,S,=@``.`!P,#`P,'@`
+M`!P`?,;&QL9\```<`,S,S,S,=@!P_AP`W.;&QL8`_@#&YO;^WL[&`#QL;&P^
+M`'X````X;&QL.`!\````,``P,&#`QL9\``````#^P,``````````_@8&````
+MP\;,V#Y[PP8,#\/&S-DS9\T/`P,8&``8&#P\/!@`````,V;,9C,``````,QF
+M,V;,```BB"*((H@BB"*(5:I5JE6J5:I5JMMWV^[;=]ONVW<8&!@8&!@8&!@8
+M&!@8&!CX&!@8&!@8&/@8^!@8&!@V-C8V-O8V-C8V``````#^-C8V-@```/@8
+M^!@8&!@V-C;V!O8V-C8V-C8V-C8V-C8V-@```/X&]C8V-C8V-C;V!OX`````
+M-C8V-C;^`````!@8&/@8^````````````/@8&!@8&!@8&!@?`````!@8&!@8
+M_P```````````/\8&!@8&!@8&!@?&!@8&```````_P`````8&!@8&/\8&!@8
+M&!@8'Q@?&!@8&#8V-C8V-S8V-C8V-C8W,#\`````````/S`W-C8V-C8V-O<`
+M_P````````#_`/<V-C8V-C8V-S`W-C8V-@```/\`_P`````V-C;W`/<V-C8V
+M&!@8_P#_`````#8V-C8V_P````````#_`/\8&!@8``````#_-C8V-C8V-C8V
+M/P`````8&!@?&!\`````````'Q@?&!@8&```````/S8V-C8V-C8V-O\V-C8V
+M&!@8_QC_&!@8&!@8&!@8^````````````!\8&!@8_____________P``````
+M_______P\/#P\/#P\/#P#P\/#P\/#P\/#_______``````````!VW-C,W'8`
+M`'S&QMS&QMS`P/YF9&!@8&!@\`#^;&QL;&QL;.X`_L9@,!@P8,;^`````'_,
+MS,S,>`````!F9F9V;&#`````=MP8&!@8`#P8?MO;VWX8/``X;,;&_L;&;#@`
+M.&S&QL9L;&SN`!PR,!A\S,S,>`````!VV]O;;@```P9\S]O;\SY@P```'#!@
+M?&`P'````'S&QL;&QL8`````_@#^`/X`````,##\,#``_`!@,!@,&#!@`/X`
+M#!@P8#`8#`#^``X;&Q@8&!@8&!@8&!@8&!@8V-AP```P,`#\`#`P`````';<
+M`';<```X;&QL.````````````!@8````````````&``````/#`P,[&QL-#P<
+H>&QL;&QL`````'#8&'#`^``````````\/#P\`````````````````&QL
+`
+end
diff --git a/usr.sbin/pcvt/fonts/vt220l.814.uu b/usr.sbin/pcvt/fonts/vt220l.814.uu
new file mode 100644
index 0000000..7d918bb
--- /dev/null
+++ b/usr.sbin/pcvt/fonts/vt220l.814.uu
@@ -0,0 +1,83 @@
+begin 644 vt220l.814
+M`````````````````````'Z!I8&!O9F!?@``````?O_;___#Y_]^````````
+M;/[^_OY\.!`````````0.'S^?#@0````````&#P\Y^?G&!@\```````8/'[_
+M_WX8&#P``````````!@\/!@``````/______Y\/#Y_______`````#QF0D)F
+M/`````#_____PYF]O9G#_____P``'@X:,GC,S,QX```````\9F9F/!A^&!@`
+M`````#\S/S`P,'#PX```````?V-_8V-C9^?FP``````8&-L\YSS;&!@`````
+M`(#`X/C^^.#`@````````@8./OX^#@8"```````8/'X8&!A^/!@``````&9F
+M9F9F9@!F9@``````?]O;VWL;&QL;`````'S&8#ALQL9L.`S&?```````````
+M`/[^_@``````&#Q^&!@8?CP8?@`````8/'X8&!@8&!@``````!@8&!@8&'X\
+M&``````````8#/X,&````````````#!@_F`P`````````````,#`P/X`````
+M```````H;/YL*```````````$#@X?'S^_@````````#^_GQ\.#@0````````
+M```````````````````8/#P\&!@`&!@`````9F9F)```````````````;&S^
+M;&QL_FQL````&!A\QL+`?`:&QGP8&```````PL8,&#!FQ@``````.&QL.';<
+MS,QV`````#`P,&````````````````P8,#`P,#`8#```````,!@,#`P,#!@P
+M`````````&8\_SQF````````````&!A^&!@`````````````````&!@8,```
+M````````_@`````````````````````8&````````@8,&#!@P(````````!\
+MQL[>]N;&QGP``````!@X>!@8&!@8?@``````?,8&#!@P8,;^``````!\Q@8&
+M/`8&QGP```````P</&S,_@P,'@``````_L#`P/P&!L9\```````X8,#`_,;&
+MQGP``````/[&!@P8,#`P,```````?,;&QGS&QL9\``````!\QL;&?@8&#'@`
+M```````8&````!@8`````````!@8````&!@P```````&#!@P8#`8#`8`````
+M`````'X``'X`````````8#`8#`8,&#!@``````!\QL8,&!@`&!@``````'S&
+MQM[>WMS`?```````$#ALQL;^QL;&``````#\9F9F?&9F9OP``````#QFPL#`
+MP,)F/```````^&QF9F9F9FSX``````#^9F)H>&AB9OX``````/YF8FAX:&!@
+M\```````/&;"P,#>QF8Z``````#&QL;&_L;&QL8``````#P8&!@8&!@8/```
+M````'@P,#`P,S,QX``````#F9FQL>&QL9N8``````/!@8&!@8&)F_@``````
+MQN[^_M;&QL;&``````#&YO;^WL[&QL8``````#ALQL;&QL9L.```````_&9F
+M9GQ@8&#P``````!\QL;&QM;>?`P.`````/QF9F9\;&9FY@``````?,;&8#@,
+MQL9\``````!^?EH8&!@8&#P``````,;&QL;&QL;&?```````QL;&QL;&;#@0
+M``````#&QL;&UM;^?&P``````,;&;#@X.&S&Q@``````9F9F9CP8&!@\````
+M``#^QHP8,&#"QOX``````#PP,#`P,#`P/```````@,#@<#@<#@8"```````\
+M#`P,#`P,##P````0.&S&`````````````````````````````/\`,#`8````
+M`````````````````'@,?,S,=@``````X&!@>&QF9F9\``````````!\QL#`
+MQGP``````!P,##QLS,S,=@``````````?,;^P,9\```````X;&1@\&!@8/``
+M`````````';,S,Q\#,QX````X&!@;'9F9F;F```````8&``X&!@8&#P`````
+M``8&``X&!@8&9F8\````X&!@9FQX;&;F```````X&!@8&!@8&#P`````````
+M`.S^UM;6Q@``````````W&9F9F9F``````````!\QL;&QGP``````````-QF
+M9F9\8&#P````````=LS,S'P,#!X```````#<=F9@8/```````````'S&<!S&
+M?```````$#`P_#`P,#8<``````````#,S,S,S'8``````````,;&QFPX$```
+M````````QL;6UOYL``````````#&;#@X;,8``````````,;&QL9^!@SX````
+M````_LP8,&;^```````.&!@8<!@8&`X``````!@8&!@`&!@8&```````<!@8
+M&`X8&!AP``````!VW```````````````````$#ALQL;^````````/&;"P,#"
+M9CP,!GP```#,S`#,S,S,S'8`````#!@P`'S&_L#&?``````0.&P`>`Q\S,QV
+M``````#,S`!X#'S,S'8`````8#`8`'@,?,S,=@`````X;#@`>`Q\S,QV````
+M`````#QF8&8\#`8\````$#AL`'S&_L#&?```````S,P`?,;^P,9\`````&`P
+M&`!\QO[`QGP``````&9F`#@8&!@8/``````8/&8`.!@8&!@\`````&`P&``X
+M&!@8&#P`````QL80.&S&QO[&Q@```#AL.``X;,;&_L;&````&#!@`/YF8'Q@
+M9OX`````````S'8V?MC8;@``````/FS,S/[,S,S.`````!`X;`!\QL;&QGP`
+M`````,;&`'S&QL;&?`````!@,!@`?,;&QL9\`````#!XS`#,S,S,S'8`````
+M8#`8`,S,S,S,=@``````QL8`QL;&QGX&#'@`QL8`.&S&QL;&;#@```#&Q@#&
+MQL;&QL;&?``````8&#QF8&!F/!@8`````#AL9&#P8&!@YOP``````&9F/!A^
+M&'X8&`````#XS,SXQ,S>S,S&``````X;&!@8?A@8&!C8<```&#!@`'@,?,S,
+M=@`````,&#``.!@8&!@\`````!@P8`!\QL;&QGP`````&#!@`,S,S,S,=@``
+M````=MP`W&9F9F9F````=MP`QN;V_M[.QL8`````/&QL/@!^```````````X
+M;&PX`'P````````````P,``P,&#&QGP```````````#^P,#`````````````
+M`/X&!@8``````,#`QLS8,&#<A@P8/@``P,#&S-@P9LZ>/@8&````&!@`&!@\
+M/#P8`````````#9LV&PV````````````V&PV;-@``````!%$$4011!%$$401
+M1!%$5:I5JE6J5:I5JE6J5:K==]UWW7?==]UWW7?==Q@8&!@8&!@8&!@8&!@8
+M&!@8&!@8&/@8&!@8&!@8&!@8&/@8^!@8&!@8&#8V-C8V-C;V-C8V-C8V````
+M`````/XV-C8V-C8``````/@8^!@8&!@8&#8V-C8V]@;V-C8V-C8V-C8V-C8V
+M-C8V-C8V-C8``````/X&]C8V-C8V-C8V-C8V]@;^````````-C8V-C8V-OX`
+M```````8&!@8&/@8^`````````````````#X&!@8&!@8&!@8&!@8&!\`````
+M```8&!@8&!@8_P````````````````#_&!@8&!@8&!@8&!@8&!\8&!@8&!@`
+M````````_P```````!@8&!@8&!C_&!@8&!@8&!@8&!@?&!\8&!@8&!@V-C8V
+M-C8V-S8V-C8V-C8V-C8V-S`_```````````````_,#<V-C8V-C8V-C8V-O<`
+M_P``````````````_P#W-C8V-C8V-C8V-C8W,#<V-C8V-C8``````/\`_P``
+M`````#8V-C8V]P#W-C8V-C8V&!@8&!C_`/\````````V-C8V-C8V_P``````
+M````````_P#_&!@8&!@8`````````/\V-C8V-C8V-C8V-C8V/P```````!@8
+M&!@8'Q@?```````````````?&!\8&!@8&!@`````````/S8V-C8V-C8V-C8V
+M-C;_-C8V-C8V&!@8&!C_&/\8&!@8&!@8&!@8&!@8^``````````````````?
+M&!@8&!@8__________________\`````````__________#P\/#P\/#P\/#P
+M\/#P#P\/#P\/#P\/#P\/#P__________````````````````=MS8V-QV````
+M`````'S&_,;&_,#`0````/[&QL#`P,#`P`````````#^;&QL;&QL``````#^
+MQF`P&#!@QOX``````````'[8V-C8<`````````!F9F9F?&!@P````````';<
+M&!@8&!@``````'X8/&9F9CP8?@``````.&S&QO[&QFPX```````X;,;&QFQL
+M;.X``````!XP&`P^9F9F/```````````?MO;?@`````````#!G[;V_-^8,``
+M`````!XP8&!^8&`P'@```````'S&QL;&QL;&````````_@``_@``_@``````
+M```8&'X8&```_P``````,!@,!@P8,`!^```````,&#!@,!@,`'X```````X;
+M&Q@8&!@8&!@8&!@8&!@8&!@8V-AP````````&!@`?@`8&```````````=MP`
+M=MP````````X;&PX````````````````````&!@`````````````````&```
+M```````/#`P,#`SL;#P<`````-AL;&QL;```````````<-@P8,CX````````
+=``````!\?'Q\?'P`````````````````````````
+`
+end
diff --git a/usr.sbin/pcvt/fonts/vt220l.816.uu b/usr.sbin/pcvt/fonts/vt220l.816.uu
new file mode 100644
index 0000000..62393e1
--- /dev/null
+++ b/usr.sbin/pcvt/fonts/vt220l.816.uu
@@ -0,0 +1,95 @@
+begin 644 vt220l.816
+M````````````````````````?H&E@8&]F8&!?@```````'[_V___P^?__WX`
+M`````````&S^_O[^?#@0```````````0.'S^?#@0```````````8/#SGY^<8
+M&#P`````````&#Q^__]^&!@\`````````````!@\/!@```````#________G
+MP\/G________```````\9D)"9CP``````/______PYF]O9G#______\``!X.
+M&C)XS,S,S'@````````\9F9F9CP8?A@8````````/S,_,#`P,'#PX```````
+M`']C?V-C8V-GY^;`````````&!C;/.<\VQ@8``````"`P.#P^/[X\.#`@```
+M`````@8.'C[^/AX.!@(````````8/'X8&!A^/!@`````````9F9F9F9F9@!F
+M9@```````'_;V]M[&QL;&QL``````'S&8#ALQL9L.`S&?```````````````
+M_O[^_@```````!@\?A@8&'X\&'X````````8/'X8&!@8&!@8````````&!@8
+M&!@8&'X\&````````````!@,_@P8```````````````P8/Y@,```````````
+M`````,#`P/X``````````````"AL_FPH`````````````!`X.'Q\_OX`````
+M``````#^_GQ\.#@0```````````````````````````````8/#P\&!@8`!@8
+M``````!F9F8D``````````````````!L;/YL;&S^;&P``````!@8?,;"P'P&
+MAL9\&!@```````#"Q@P8,&#&A@```````#AL;#AVW,S,S'8``````#`P,&``
+M````````````````#!@P,#`P,#`8#````````#`8#`P,#`P,&#``````````
+M``!F//\\9@``````````````&!A^&!@````````````````````8&!@P````
+M`````````'X````````````````````````8&````````````@8,&#!@P(``
+M``````!\QL;.WO;FQL9\````````&#AX&!@8&!@8?@```````'S&!@P8,&#`
+MQOX```````!\Q@8&/`8&!L9\````````#!P\;,S^#`P,'@```````/[`P,#\
+M!@8&QGP````````X8,#`_,;&QL9\````````_L8&!@P8,#`P,````````'S&
+MQL9\QL;&QGP```````!\QL;&?@8&!@QX```````````8&````!@8````````
+M````&!@````8&#``````````!@P8,&`P&`P&````````````?@``?@``````
+M``````!@,!@,!@P8,&````````!\QL8,&!@8`!@8````````?,;&QM[>WMS`
+M?````````!`X;,;&_L;&QL8```````#\9F9F?&9F9F;\````````/&;"P,#`
+MP,)F/````````/AL9F9F9F9F;/@```````#^9F)H>&A@8F;^````````_F9B
+M:'AH8&!@\````````#QFPL#`WL;&9CH```````#&QL;&_L;&QL;&````````
+M/!@8&!@8&!@8/````````!X,#`P,#,S,S'@```````#F9F9L>'AL9F;F````
+M````\&!@8&!@8&)F_@```````,;N_O[6QL;&QL8```````#&YO;^WL[&QL;&
+M````````?,;&QL;&QL;&?````````/QF9F9\8&!@8/````````!\QL;&QL;&
+MUMY\#`X`````_&9F9GQL9F9FY@```````'S&QF`X#`;&QGP```````!^?EH8
+M&!@8&!@\````````QL;&QL;&QL;&?````````,;&QL;&QL9L.!````````#&
+MQL;&UM;6_NYL````````QL9L?#@X?&S&Q@```````&9F9F8\&!@8&#P`````
+M``#^QH8,&#!@PL;^````````/#`P,#`P,#`P/`````````"`P.!P.!P.!@(`
+M```````\#`P,#`P,#`P\`````!`X;,8`````````````````````````````
+M````_P``,#`8````````````````````````>`Q\S,S,=@```````.!@8'AL
+M9F9F9GP```````````!\QL#`P,9\````````'`P,/&S,S,S,=@``````````
+M`'S&_L#`QGP````````X;&1@\&!@8&#P````````````=LS,S,S,?`S,>```
+M`.!@8&QV9F9F9N8````````8&``X&!@8&!@\````````!@8`#@8&!@8&!F9F
+M/````.!@8&9L>'AL9N8````````X&!@8&!@8&!@\````````````[/[6UM;6
+MQ@```````````-QF9F9F9F8```````````!\QL;&QL9\````````````W&9F
+M9F9F?&!@\````````';,S,S,S'P,#!X```````#<=F9@8&#P````````````
+M?,9@.`S&?````````!`P,/PP,#`P-AP```````````#,S,S,S,QV````````
+M````QL;&QFPX$````````````,;&UM;6_FP```````````#&;#@X.&S&````
+M````````QL;&QL;&?@8,^````````/[,&#!@QOX````````.&!@8<!@8&!@.
+M````````&!@8&``8&!@8&````````'`8&!@.&!@8&'````````!VW```````
+M```````````````0.&S&QL;^`````````#QFPL#`P,)F/`P&?`````#,``#,
+MS,S,S,QV```````,&#``?,;^P,#&?```````$#AL`'@,?,S,S'8```````#,
+M``!X#'S,S,QV``````!@,!@`>`Q\S,S,=@``````.&PX`'@,?,S,S'8`````
+M`````#QF8&!F/`P&/``````0.&P`?,;^P,#&?````````,8``'S&_L#`QGP`
+M`````&`P&`!\QO[`P,9\````````9@``.!@8&!@8/```````&#QF`#@8&!@8
+M&#P``````&`P&``X&!@8&!@\``````#&`!`X;,;&_L;&Q@`````X;#@`.&S&
+MQO[&QL8`````&#!@`/YF8'Q@8&;^``````````!L_K(R?MC8;@```````#YL
+MS,S^S,S,S,X``````!`X;`!\QL;&QL9\````````Q@``?,;&QL;&?```````
+M8#`8`'S&QL;&QGP``````#!XS`#,S,S,S,QV``````!@,!@`S,S,S,S,=@``
+M````QL8``,;&QL;&QGX&#'@`QL8`?,;&QL;&QL9\`````,;&`,;&QL;&QL;&
+M?```````&!@\9F!@8&8\&!@``````#AL9&#P8&!@8.;\````````9F8\&'X8
+M?A@8&```````^,S,^,3,WLS,S,8```````X;&!@8?A@8&!@8V'`````8,&``
+M>`Q\S,S,=@``````#!@P`#@8&!@8&#P``````!@P8`!\QL;&QL9\```````8
+M,&``S,S,S,S,=@```````';<`-QF9F9F9F8`````=MP`QN;V_M[.QL;&````
+M```\;&P^`'X`````````````.&QL.`!\```````````````P,``P,&#`QL9\
+M`````````````/[`P,#```````````````#^!@8&!@```````,#`PL;,&#!@
+MW(8,&#X```#`P,+&S!@P9LZ>/@8&`````!@8`!@8&#P\/!@````````````V
+M;-AL-@``````````````V&PV;-@````````11!%$$4011!%$$4011!%$5:I5
+MJE6J5:I5JE6J5:I5JMUWW7?==]UWW7?==]UWW7<8&!@8&!@8&!@8&!@8&!@8
+M&!@8&!@8&/@8&!@8&!@8&!@8&!@8^!CX&!@8&!@8&!@V-C8V-C8V]C8V-C8V
+M-C8V`````````/XV-C8V-C8V-@``````^!CX&!@8&!@8&!@V-C8V-O8&]C8V
+M-C8V-C8V-C8V-C8V-C8V-C8V-C8V-@``````_@;V-C8V-C8V-C8V-C8V-O8&
+M_@``````````-C8V-C8V-OX``````````!@8&!@8^!CX````````````````
+M````^!@8&!@8&!@8&!@8&!@8&!\``````````!@8&!@8&!C_````````````
+M````````_Q@8&!@8&!@8&!@8&!@8&!\8&!@8&!@8&`````````#_````````
+M```8&!@8&!@8_Q@8&!@8&!@8&!@8&!@?&!\8&!@8&!@8&#8V-C8V-C8W-C8V
+M-C8V-C8V-C8V-C<P/P`````````````````_,#<V-C8V-C8V-C8V-C8V]P#_
+M`````````````````/\`]S8V-C8V-C8V-C8V-C8W,#<V-C8V-C8V-@``````
+M_P#_```````````V-C8V-O<`]S8V-C8V-C8V&!@8&!C_`/\``````````#8V
+M-C8V-C;_`````````````````/\`_Q@8&!@8&!@8`````````/\V-C8V-C8V
+M-C8V-C8V-C8_```````````8&!@8&!\8'P`````````````````?&!\8&!@8
+M&!@8&``````````_-C8V-C8V-C8V-C8V-C8V_S8V-C8V-C8V&!@8&!C_&/\8
+M&!@8&!@8&!@8&!@8&!CX````````````````````'Q@8&!@8&!@8________
+M_____________P````````#____________P\/#P\/#P\/#P\/#P\/#P#P\/
+M#P\/#P\/#P\/#P\/#_________\``````````````````';<V-C8W'8`````
+M``!XS,S,V,S&QL;,````````_L;&P,#`P,#`P```````````_FQL;&QL;&P`
+M````````_L9@,!@P8,;^````````````?MC8V-C8<```````````9F9F9F9\
+M8&#``````````';<&!@8&!@8`````````'X8/&9F9CP8?@`````````X;,;&
+M_L;&;#@````````X;,;&QFQL;&SN````````'C`8##YF9F9F/```````````
+M`'[;V]M^`````````````P9^V]OS?F#`````````'C!@8'Y@8&`P'@``````
+M``!\QL;&QL;&QL8``````````/X``/X``/X````````````8&'X8&```_P``
+M```````P&`P&#!@P`'X`````````#!@P8#`8#`!^````````#AL;&Q@8&!@8
+M&!@8&!@8&!@8&!@8&-C8V'```````````!@8`'X`&!@`````````````=MP`
+M=MP`````````.&QL.````````````````````````!@8````````````````
+M````&```````````#PP,#`P,[&QL/!P``````-AL;&QL;`````````````!P
+MV#!@R/@`````````````````?'Q\?'Q\?```````````````````````````
+!`#!@
+`
+end
diff --git a/usr.sbin/pcvt/ispcvt/Makefile b/usr.sbin/pcvt/ispcvt/Makefile
new file mode 100644
index 0000000..994b406
--- /dev/null
+++ b/usr.sbin/pcvt/ispcvt/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+PROG= ispcvt
+MAN= ispcvt.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pcvt/ispcvt/ispcvt.8 b/usr.sbin/pcvt/ispcvt/ispcvt.8
new file mode 100644
index 0000000..b9eee0a
--- /dev/null
+++ b/usr.sbin/pcvt/ispcvt/ispcvt.8
@@ -0,0 +1,99 @@
+.\" Copyright (c) 1992, 2000 Hellmuth Michaelis
+.\"
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" Last Edit-Date: [Mon Mar 27 16:31:54 2000]
+.\"
+.\" $FreeBSD$
+.\"
+.Dd March 27, 2000
+.Dt ISPCVT 8
+.Os
+.Sh NAME
+.Nm ispcvt
+.Nd verify if current video driver is pcvt driver
+.Sh SYNOPSIS
+.Nm
+.Op Fl c
+.Op Fl d Ar device
+.Op Fl n
+.Op Fl v
+.Sh DESCRIPTION
+The
+.Nm
+utility allows the user to check whether the current video driver compiled
+into the kernel is a pcvt driver.
+The major and minor release numbers of
+the driver are also checked.
+Furthermore
+.Nm
+is also able to print out the values of all the
+.Dq Ar PCVT_XXXXXX
+compile time options, the driver in the current running kernel was
+compiled with.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl d
+Specifies a device for which the check is done.
+.It Fl v
+Specifies being verbose.
+On success the name and revision is reported, on
+failure which comparison failed.
+.It Fl n
+print the number of compiled-in virtual terminals.
+.It Fl c
+This options prints out the values of all
+.Dq Ar PCVT_XXXXXX
+#defines which were given to the compiler at the time the currently running
+kernel was compiled.
+Specifying
+.Fl v
+with the
+.Fl c
+option gives a verbose listing of the compile-time options.
+.El
+.Sh DIAGNOSTICS
+The
+.Nm
+utility exits with one of the following values:
+.Pp
+.Bl -tag -width indent -compact
+.It 0
+driver is pcvt and major and minor numbers match
+.It 1
+.Xr open 2
+or
+.Xr ioctl 2
+system call failed
+.It 2
+driver name mismatch
+.It 3
+name matched, release major number mismatch
+.It 4
+name and major number matched, release minor number mismatch
+.It 5
+usage error
+.El
+.Sh SEE ALSO
+.Xr pcvt 4
diff --git a/usr.sbin/pcvt/ispcvt/ispcvt.c b/usr.sbin/pcvt/ispcvt/ispcvt.c
new file mode 100644
index 0000000..40979ca
--- /dev/null
+++ b/usr.sbin/pcvt/ispcvt/ispcvt.c
@@ -0,0 +1,250 @@
+/*
+ * Copyright (c) 1992, 2000 Hellmuth Michaelis
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/*---------------------------------------------------------------------------*
+ *
+ * ispcvt - check for pcvt driver running and its options
+ * ------------------------------------------------------
+ *
+ * Last Edit-Date: [Fri Mar 31 10:24:43 2000]
+ *
+ * $FreeBSD$
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <err.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <machine/pcvt_ioctl.h>
+
+#define DEFAULTFD 0
+
+main(argc,argv)
+int argc;
+char *argv[];
+{
+ struct pcvtid pcvtid;
+ struct pcvtinfo pcvtinfo;
+ int c;
+ char *p;
+ int verbose = 0;
+ int config = 0;
+ int dflag = 0;
+ int n_screens = 0;
+ int fd;
+ char *device;
+
+ while( (c = getopt(argc, argv, "cd:nv")) != -1)
+ {
+ switch(c)
+ {
+ case 'c':
+ config = 1;
+ break;
+
+ case 'd':
+ device = optarg;
+ dflag = 1;
+ break;
+
+ case 'n':
+ n_screens = 1;
+ break;
+
+ case 'v':
+ verbose = 1;
+ break;
+
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if(dflag)
+ {
+ if((fd = open(device, O_RDWR)) == -1)
+ {
+ if(verbose)
+ warn("ERROR opening %s", device);
+ exit(1);
+ }
+ }
+ else
+ {
+ fd = DEFAULTFD;
+ }
+
+ if(ioctl(fd, VGAPCVTID, &pcvtid) == -1)
+ {
+ if(verbose)
+ warn("ioctl VGAPCVTID failed, error");
+ exit(1);
+ }
+
+ if(!strcmp(pcvtid.name, PCVTIDNAME))
+ {
+ if(pcvtid.rmajor == PCVTIDMAJOR)
+ {
+ if(pcvtid.rminor != PCVTIDMINOR)
+ {
+ if(verbose)
+ warnx("minor revision: expected %d, got %d", PCVTIDMINOR, pcvtid.rminor);
+ exit(4); /* minor revision mismatch */
+ }
+ }
+ else
+ {
+ if(verbose)
+ warnx("major revision: expected %d, got %d", PCVTIDMAJOR, pcvtid.rmajor);
+ exit(3); /* major revision mismatch */
+ }
+ }
+ else
+ {
+ if(verbose)
+ warnx("name check: expected %s, got %s", PCVTIDNAME, pcvtid.name);
+ exit(2); /* name mismatch */
+ }
+
+ if(verbose)
+ {
+ warnx("\nkernel and utils match, driver name [%s], release [%1.1d.%02.2d]\n",
+ pcvtid.name, pcvtid.rmajor, pcvtid.rminor);
+ }
+
+ if(config == 0 && n_screens == 0)
+ exit(0);
+
+ if(ioctl(fd, VGAPCVTINFO, &pcvtinfo) == -1)
+ {
+ if(verbose)
+ warn("ioctl VGAPCVTINFO failed, error");
+ exit(1);
+ }
+
+ if(n_screens)
+ {
+ printf("%d", pcvtinfo.nscreens);
+ exit(0);
+ }
+
+ if(verbose)
+ {
+ fprintf(stderr,"PCVT_NSCREENS = %u\t\t", pcvtinfo.nscreens);
+ fprintf(stderr,"PCVT_UPDATEFAST = %u\n", pcvtinfo.updatefast);
+ fprintf(stderr,"PCVT_UPDATESLOW = %u\t\t", pcvtinfo.updateslow);
+ fprintf(stderr,"PCVT_SYSBEEPF = %u\n", pcvtinfo.sysbeepf);
+ fprintf(stderr,"PCVT_PCBURST = %u\t\t", pcvtinfo.pcburst);
+ fprintf(stderr,"PCVT_KBD_FIFO_SZ = %u\n\n", pcvtinfo.kbd_fifo_sz);
+
+ /* config booleans */
+
+ fprintf(stderr,"PCVT_132GENERIC = %s",
+ (pcvtinfo.compile_opts & CONF_132GENERIC) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_24LINESDEF = %s",
+ (pcvtinfo.compile_opts & CONF_24LINESDEF) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_CTRL_ALT_DEL = %s",
+ (pcvtinfo.compile_opts & CONF_CTRL_ALT_DEL) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_INHIBIT_NUMLOCK = %s",
+ (pcvtinfo.compile_opts & CONF_INHIBIT_NUMLOCK) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_META_ESC = %s",
+ (pcvtinfo.compile_opts & CONF_META_ESC) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_NO_LED_UPDATE = %s",
+ (pcvtinfo.compile_opts & CONF_NO_LED_UPDATE) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_NULLCHARS = %s",
+ (pcvtinfo.compile_opts & CONF_NULLCHARS) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_PRETTYSCRNS = %s",
+ (pcvtinfo.compile_opts & CONF_PRETTYSCRNS) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_SCREENSAVER = %s",
+ (pcvtinfo.compile_opts & CONF_SCREENSAVER) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_SETCOLOR = %s",
+ (pcvtinfo.compile_opts & CONF_SETCOLOR) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_SHOWKEYS = %s",
+ (pcvtinfo.compile_opts & CONF_SHOWKEYS) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_SLOW_INTERRUPT = %s",
+ (pcvtinfo.compile_opts & CONF_SLOW_INTERRUPT) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_USEKBDSEC = %s",
+ (pcvtinfo.compile_opts & CONF_USEKBDSEC) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_VT220KEYB = %s",
+ ((u_int)pcvtinfo.compile_opts & (u_int)CONF_VT220KEYB) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"XSERVER = %s",
+ (pcvtinfo.compile_opts & CONF_XSERVER) ? "ON" : "OFF");
+
+ next();
+ fprintf(stderr,"PCVT_GREENSAVER = %s",
+ (pcvtinfo.compile_opts & CONF_GREENSAVER) ? "ON" : "OFF");
+
+ fprintf(stderr,"\n\n");
+ }
+ else /* !verbose */
+ {
+ fprintf(stderr,"PCVT_NSCREENS = %u\n", pcvtinfo.nscreens);
+ fprintf(stderr,"PCVT_UPDATEFAST = %u\n", pcvtinfo.updatefast);
+ fprintf(stderr,"PCVT_UPDATESLOW = %u\n", pcvtinfo.updateslow);
+ fprintf(stderr,"PCVT_SYSBEEPF = %u\n", pcvtinfo.sysbeepf);
+ fprintf(stderr,"Compile options = 0x%08X\n", pcvtinfo.compile_opts);
+ }
+}
+
+usage()
+{
+ fprintf(stderr,"\nispcvt - verify current video driver is the pcvt-driver\n");
+ fprintf(stderr," usage: ispcvt [-c] [-d device] [-n] [-v]\n");
+ fprintf(stderr,"options: -c print compile time configuration\n");
+ fprintf(stderr," -d <name> use devicefile <name>\n");
+ fprintf(stderr," -n print number of virtual screens (to stdout)\n");
+ fprintf(stderr," -v be verbose\n\n");
+ exit(5);
+}
+
+next()
+{
+ static int i = 0;
+
+ fprintf(stderr, "%s", (i == 0) ? "\t\t" : "\n");
+
+ i = ~i;
+}
+
+/* EOF */
diff --git a/usr.sbin/pcvt/kbdio/Makefile b/usr.sbin/pcvt/kbdio/Makefile
new file mode 100644
index 0000000..1bbbdda
--- /dev/null
+++ b/usr.sbin/pcvt/kbdio/Makefile
@@ -0,0 +1,23 @@
+# $FreeBSD$
+
+PROG= kbdio
+NOMAN= #true
+SRCS= kbdio.y lex.l y.tab.h
+
+CFLAGS+= -I${.OBJDIR} -I${.CURDIR} #-g
+
+#YACC= bison
+#YFLAGS+= -yd # Bison only
+
+YFLAGS+= -v # verbose
+LFLAGS+= -I
+
+DPADD= ${LIBM} ${LIBY} ${LIBL}
+LDADD= -lm -ly -ll
+
+CLEANFILES+= y.output # comment file from bison
+
+install:
+ @${ECHO} "kbdio is not installed automatically ...."
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pcvt/kbdio/kbdio.y b/usr.sbin/pcvt/kbdio/kbdio.y
new file mode 100644
index 0000000..55bf231
--- /dev/null
+++ b/usr.sbin/pcvt/kbdio/kbdio.y
@@ -0,0 +1,329 @@
+/* Hello emacs, this should be edited in -*- Fundamental -*- mode */
+%{
+/*
+ * Copyright (c) 1994 Joerg Wunsch
+ *
+ * All rights reserved.
+ *
+ * This program is free software.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Joerg Wunsch
+ * 4. The name of the developer may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ident "$FreeBSD$"
+
+/*
+ * $Log: kbdio.y,v $
+ * Revision 1.2 1994/09/18 19:49:22 j
+ * Refined expr handling; can now set/clear bits.
+ *
+ * Revision 1.1 1994/09/18 12:57:13 j
+ * Initial revision
+ *
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <sys/fcntl.h>
+#include <machine/cpufunc.h>
+#include <machine/pcvt_ioctl.h>
+
+#ifdef __NetBSD__
+#include <machine/pio.h>
+#endif
+
+#define KBD_DELAY \
+ { u_char x = inb(0x84); } \
+ { u_char x = inb(0x84); } \
+ { u_char x = inb(0x84); } \
+ { u_char x = inb(0x84); } \
+ { u_char x = inb(0x84); } \
+ { u_char x = inb(0x84); }
+
+#define YYDEBUG 1
+
+void yyerror(const char *msg);
+
+static void help(int), status(void), data(int), kbd(int), cmdbyte(int),
+ kbc(int), whatMCA(void);
+static int kbget(void);
+%}
+
+%union {
+ int num;
+}
+
+%token NEWLINE
+%token ALL CMD DATA DEFAULTS ECHOC ENABLE EXPR HELP ID LED
+%token MAKE ONLY RELEASE RESEND RESET SCAN STATUS TYPEMATIC
+%token WHAT
+%token <num> NUM
+
+%type <num> expr opr
+
+%%
+
+interpret: lines ;
+
+lines: line
+ | lines line
+ ;
+
+line: statements NEWLINE
+ | NEWLINE
+ | error NEWLINE { fprintf(stderr, "bing!\n"); }
+ ;
+
+statements: statement
+ | statements ';' statement
+ ;
+
+statement: '?' { help(0); }
+ | HELP { help(0); }
+ | HELP EXPR { help(1); }
+ | STATUS '?' { status(); }
+ | WHAT '?' { whatMCA(); }
+ | DATA '?' { data(kbget()); }
+ | LED '=' NUM { kbd(0xed); kbd($3); }
+ | ECHOC { kbd(0xee); kbget(); }
+ | SCAN '=' NUM { kbd(0xf0); kbd($3);
+ if($3 == 0) data(kbget()); }
+ | SCAN '?' { kbd(0xf0); kbd(0); data(kbget()); }
+ | ID '?' { kbd(0xf2); data(kbget());
+ data(kbget()); }
+ | TYPEMATIC '=' NUM ',' NUM
+ { kbd(0xf3);
+ if($3 > 1000) $3 = 1000;
+ if($5 > 30) $5 = 30;
+ if($5 < 2) $5 = 2;
+ kbd(
+ (int)
+ (8.0 * log(30.0 / (double)$5)
+ / log(2))
+ | ((($3 / 250) - 1) * 32)
+ );
+ }
+ | ENABLE { kbd(0xf4); }
+ | DEFAULTS { kbd(0xf6); }
+ | ALL TYPEMATIC { kbd(0xf7); }
+ | ALL MAKE RELEASE { kbd(0xf8); }
+ | ALL MAKE ONLY { kbd(0xf9); }
+ | ALL TYPEMATIC MAKE RELEASE
+ { kbd(0xfa); }
+ | NUM TYPEMATIC { kbd(0xfb); kbd($1); }
+ | NUM MAKE RELEASE { kbd(0xfc); kbd($1); }
+ | NUM MAKE ONLY { kbd(0xfd); kbd($1); }
+ | RESEND { kbd(0xfe); }
+ | RESET { kbd(0xff); }
+ | CMD '?' { kbc(0x20); cmdbyte(kbget()); }
+ | CMD '=' expr { kbc(0x60); kbd($3); }
+ | /* lambda */
+ ;
+
+expr: opr { $$ = $1; }
+ | expr '+' opr { $$ = $1 | $3; }
+ | expr '-' opr { $$ = $1 & ~($3); }
+ ;
+
+opr: NUM { $$ = $1; }
+ | CMD { kbc(0x20); $$ = kbget(); }
+ ;
+
+%%
+
+static void
+help(int topic) {
+ switch(topic) {
+ case 0:
+ printf(
+ "Input consists of lines, containing one or more semicolon-separated\n"
+ "statements. Numbers are implicitly hexadecimal, append a dot for\n"
+ "decimal numbers. Valid statements include:\n"
+ "help [expr]; give help [to expression syntax]\n"
+ "status ? interpret kbd ctrl status byte\n"
+ "what ? check for MCA type 1 or 2 motherboard controller\n"
+ "data ? get one byte of data\n"
+ "led = NUM set kbd LEDs\n"
+ "echo = NUM echo byte to kbd\n"
+ "scan = NUM; scan ? set scan code set; return current set\n"
+ "id ? get two id bytes\n"
+ "typematic=delay,rate set typematic delay(ms)&rate(1/s)\n"
+ "enable; defaults enable kbd; back to defaults\n"
+ "all typematic make all keys typematic\n"
+ "all make release make all keys sending make/release\n"
+ "all make only make all keys sending make only\n"
+ "all typematic make release make all keys typematic & make/release\n"
+ "NUM typematic make specific key typematic\n"
+ "NUM make release make specific key sending make/release\n"
+ "NUM make only make specific key sending make only\n"
+ "resend; reset resend last byte from kbd; reset kbd\n"
+ "cmd ? fetch kbd ctrl command byte\n"
+ "cmd = expr set kbd ctrl command byte\n"
+ "\n");
+ break;
+
+ case 1:
+ printf(
+ "Expressions can either consist of a number, possibly followed\n"
+ "by a + or - sign and bit values in numeric or symbolic form.\n"
+ "Symbolic bit values are:\n"
+ "SCCONV IGNPAR CLKLOW OVRINH TEST IRQ\n"
+ "\n");
+ break;
+ }
+}
+
+static void
+status(void) {
+ int c = inb(0x64);
+ if(c&0x80) printf("parity error | ");
+ if(c&0x40) printf("rx timeout | ");
+ if(c&0x20) printf("tx timeout | ");
+ if(c&0x10) printf("kbd released ");
+ else printf("kbd locked ");
+ if(c&0x08) printf("| cmd last sent ");
+ else printf("| data last sent ");
+ if(c&0x04) printf("| power-on ");
+ else printf("| test ok ");
+ if(c&0x02) printf("| ctrl write busy ");
+ else printf("| ctrl write ok ");
+ if(c&0x01) printf("| ctrl read ok\n");
+ else printf("| ctrl read empty\n");
+}
+
+/* see: Frank van Gilluwe, "The Undocumented PC", Addison Wesley 1994, pp 273 */
+
+static void
+whatMCA(void) {
+ int new, sav;
+ kbc(0x20); /* get command byte */
+ sav = kbget(); /* sav = command byte */
+ kbc(0x60); /* set command byte */
+ kbd(sav | 0x40); /* set keyboard xlate bit */
+ kbc(0x20); /* get keyboard command */
+ new = kbget(); /* new = command byte */
+ kbc(0x60); /* set command byte */
+ kbd(sav); /* restore command byte */
+ if(new & 0xbf)
+ printf("Hmm - looks like MCA type 1 motherboard controller\n");
+ else
+ printf("Hmm - looks like MCA type 2 motherboard controller\n");
+}
+
+static void
+kbd(int d) {
+ int i = 100000;
+ while(i && (inb(0x64) & 2)) i--;
+ if(i == 0) { printf("kbd write: timed out\n"); return; }
+ outb(0x60, d);
+}
+
+static void
+kbc(int d) {
+ int i = 100000;
+ while(i && (inb(0x64) & 2)) i--;
+ if(i == 0) { printf("ctrl write: timed out\n"); return; }
+ outb(0x64, d);
+}
+
+static int
+kbget(void) {
+ int i, c;
+ for(;;) {
+ i = 10000;
+ while(i && (inb(0x64) & 1) == 0) i--;
+ if(i == 0) { printf("data read: timed out\n"); return -1; }
+ KBD_DELAY
+ c = (unsigned char)inb(0x60);
+ switch(c) {
+ case 0: case 0xff:
+ printf("got kbd overrun\n"); break;
+ case 0xaa:
+ printf("got self-test OK\n"); break;
+ case 0xee:
+ printf("got ECHO byte\n"); break;
+ case 0xfa:
+ printf("got ACK\n"); break;
+ case 0xfc:
+ printf("got self-test FAIL\n"); break;
+ case 0xfd:
+ printf("got internal failure\n"); break;
+ case 0xfe:
+ printf("got RESEND request\n"); break;
+ default:
+ goto done;
+ }
+ }
+done:
+ return c;
+}
+
+static void
+cmdbyte(int d) {
+ if(d&0x40) printf("scan conv ");
+ else printf("pass thru ");
+ if(d&0x20) printf("| ign parity ");
+ else printf("| check parity ");
+ if(d&0x10) printf("| kbd clk low ");
+ else printf("| enable kbd ");
+ if(d&0x08) printf("| override kbd inh ");
+ if(d&0x04) printf("| test ok ");
+ else printf("| power-on ");
+ if(d&0x01) printf("| irq 1 enable\n");
+ else printf("| no irq\n");
+}
+
+static void
+data(int d) {
+ if(d < 0) return;
+ printf("data: 0x%02x\n", d);
+}
+
+void yyerror(const char *msg) {
+ fprintf(stderr, "yyerror: %s\n", msg);
+}
+
+int main(int argc, char **argv) {
+ int fd;
+
+ if(argc > 1) yydebug = 1;
+
+ if((fd = open("/dev/console", O_RDONLY)) < 0)
+ fd = 0;
+
+ if(ioctl(fd, KDENABIO, 0) < 0) {
+ perror("ioctl(KDENABIO)");
+ return 1;
+ }
+ yyparse();
+
+ (void)ioctl(fd, KDDISABIO, 0);
+ return 0;
+}
+
diff --git a/usr.sbin/pcvt/kbdio/lex.l b/usr.sbin/pcvt/kbdio/lex.l
new file mode 100644
index 0000000..de1a252
--- /dev/null
+++ b/usr.sbin/pcvt/kbdio/lex.l
@@ -0,0 +1,98 @@
+%{
+/*
+ * Copyright (c) 1994 Joerg Wunsch
+ *
+ * All rights reserved.
+ *
+ * This program is free software.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Joerg Wunsch
+ * 4. The name of the developer may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ident "$FreeBSD$"
+
+/*
+ * $Log: lex.l,v $
+ * Revision 1.2 1994/09/18 19:48:45 j
+ * Added the symbolic values for kbd cmd byte.
+ *
+ * Revision 1.1 1994/09/18 12:57:13 j
+ * Initial revision
+ *
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "y.tab.h"
+
+extern YYSTYPE yylval;
+
+%}
+
+D [0-9a-fA-F]
+
+%%
+
+all { return ALL; }
+cmd { return CMD; }
+data { return DATA; }
+defaults { return DEFAULTS; }
+echo { return ECHOC; }
+enable { return ENABLE; }
+expr { return EXPR; }
+help { return HELP; }
+id { return ID; }
+led { return LED; }
+make { return MAKE; }
+only { return ONLY; }
+release { return RELEASE; }
+resend { return RESEND; }
+reset { return RESET; }
+scan { return SCAN; }
+status { return STATUS; }
+typematic { return TYPEMATIC; }
+what { return WHAT; }
+
+ /* numeric values */
+clklow { yylval.num = 0x10; return NUM; }
+ignpar { yylval.num = 0x20; return NUM; }
+irq { yylval.num = 0x01; return NUM; }
+ovrinh { yylval.num = 0x08; return NUM; }
+scconv { yylval.num = 0x40; return NUM; }
+test { yylval.num = 0x04; return NUM; }
+
+{D}({D}*)\. { sscanf(yytext, "%d", &yylval.num); return NUM; }
+
+{D}({D}*) { sscanf(yytext, "%x", &yylval.num); return NUM; }
+
+[ \t] { /* ignore */ }
+
+\n { return NEWLINE; }
+
+. { return yytext[0]; }
diff --git a/usr.sbin/pcvt/kcon/Makefile b/usr.sbin/pcvt/kcon/Makefile
new file mode 100644
index 0000000..61c273f
--- /dev/null
+++ b/usr.sbin/pcvt/kcon/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PROG= kcon
+DEVICE= /dev/ttyv0
+CFLAGS+= -I${.CURDIR}/../keycap -DKEYB_DEVICE=\"${DEVICE}\"
+DPADD= ${LIBKEYCAP}
+LDADD= -lkeycap
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pcvt/kcon/kcon.1 b/usr.sbin/pcvt/kcon/kcon.1
new file mode 100644
index 0000000..075937b
--- /dev/null
+++ b/usr.sbin/pcvt/kcon/kcon.1
@@ -0,0 +1,129 @@
+.\" Copyright (c) 1992, 2000 Hellmuth Michaelis
+.\"
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" Last Edit-Date: [Mon Mar 27 17:04:14 2000]
+.\"
+.\" $FreeBSD$
+.\"
+.Dd March 27, 2000
+.Dt KCON 1
+.Os
+.Sh NAME
+.Nm kcon
+.Nd pcvt keyboard control and remapping
+.Sh SYNOPSIS
+.Nm
+.Op Fl d Ar delay
+.Op Fl l
+.Op Fl m Ar map
+.Op Fl o
+.Op Fl p
+.Op Fl R
+.Op Fl r Ar rate
+.Op Fl s
+.Op Fl t Ns Ar +/-
+.Op Fl x
+.Sh DESCRIPTION
+The
+.Nm
+utility
+is used for controlling all aspects of keyboard configuration for the 'pcvt'
+video driver.
+.Pp
+The available options are:
+.Bl -tag -width flag
+.It Fl d Ar delay
+Specifies the delay after which the last key entered will be repeated by the
+Keyboard.
+Valid values are 0..3 corresponding to delays of 250, 500, 750 and
+1000 milliseconds.
+.It Fl l
+Displays the current keyboard map in use by the driver.
+.It Fl m Ar map
+Specifies the map entry to be searched in the keyboard capabilities database
+.Nm keycap .
+The database is searched for the entry and if found, the mapping
+is loaded and is used in the driver from then on.
+.It Fl o
+Switches display of control codes to octal in the listing of the current map.
+To be used in conjunction with the
+.Fl l
+option.
+.It Fl p
+Uses 'pure' output when listing - the Escape character is displayed in either
+octal or hexadecimal and not as 'ESC'. To be used in conjunction with the
+.Fl l
+option.
+.It Fl r Ar rate
+Specifies the character repetition rate.
+Valid argument values are 0...31
+corresponding to rates of 30 characters/second ... 2 characters/second.
+.It Fl R
+Reset the Keyboard.
+.It Fl s
+Displays the current settings of the rate and delay values.
+.It Fl t Ar +/-
+Specify this option to enable
+.Pq Ar +
+or disable
+.Pq Ar -
+the repetition of keys.
+.It Fl x
+Switches display of control codes to hexadecimal in the listing of the current map.
+To be used in conjunction with the
+.Fl l
+option.
+This is the default behaviour.
+.El
+.Sh FILES
+.Bl -tag -width /usr/share/misc/keycap.pcvt -compact
+.It Pa /usr/share/misc/keycap.pcvt
+Keyboard capabilities data base file if nothing else was chosen during installation.
+.It Pa /dev/console
+Keyboard raw device.
+.El
+.Sh SEE ALSO
+.Xr keycap 3 ,
+.Xr keycap 5
+.Sh BUGS
+The
+.Nm
+utility detects several inconsistencies in the keycap database.
+In case of errors
+.Nm
+exits with an error message.
+If this happens, the keyboard may remain in
+an undefined state.
+To recover from such situation, execute
+.Dq Li kcon -m default .
+.Sh EXAMPLES
+The command
+.Dq Li kcon -m gb
+loads the entry 'gb' from the keycap file into the keyboard to switch to
+a british keyboard behaviour.
+.Pp
+The command
+.Dq Li kcon -r 0 -d 0
+switches the keyboard to emit characters at a rate of 30 characters per second
+after a key has been held down for 250 milliseconds.
diff --git a/usr.sbin/pcvt/kcon/kcon.c b/usr.sbin/pcvt/kcon/kcon.c
new file mode 100644
index 0000000..4318bf2
--- /dev/null
+++ b/usr.sbin/pcvt/kcon/kcon.c
@@ -0,0 +1,743 @@
+/*
+ * Copyright (c) 1992, 2000 Hellmuth Michaelis
+ *
+ * Copyright (c) 1992, 1993 Holger Veit.
+ *
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to 386BSD by
+ * Holger Veit
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by
+ * Hellmuth Michaelis and Holger Veit
+ * 4. The name authors may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*---------------------------------------------------------------------------*
+ *
+ * kcon.c Keyboard control and remapping
+ * ----------------------------------------------
+ *
+ * based on "keymap" which was written by
+ * Holger Veit (veit@du9ds3.uni-duisburg.de)
+ *
+ * Last Edit-Date: [Mon Mar 27 17:03:50 2000]";
+ *
+ * $FreeBSD$
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <machine/pcvt_ioctl.h>
+
+#include "keycap.h"
+
+int Rf = 0;
+int df = 0;
+int lf = 0;
+int mf = 0;
+int of = 0;
+int pf = 0;
+int rf = 0;
+int tf = 0;
+int xf = 0;
+int sf = 0;
+
+/*---------------------------------------------------------------------------*
+ * main entry
+ *---------------------------------------------------------------------------*/
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ int c = 0;
+
+ int errf = 0;
+
+ int rate = -1;
+ int delay = -1;
+ char *map;
+ int kbfd;
+
+ while((c = getopt(argc, argv, "Rd:lm:opr:st:x")) != -1)
+ {
+ switch(c)
+ {
+ case 'R':
+ Rf = 1;
+ break;
+
+ case 'd':
+ df = 1;
+ delay = atoi(optarg);
+ break;
+
+ case 'l':
+ lf = 1;
+ break;
+
+ case 'm':
+ mf = 1;
+ map = optarg;
+ break;
+
+ case 'o':
+ if(xf)
+ errf = 1;
+ else
+ of = 1;
+ break;
+
+ case 'p':
+ pf = 1;
+ break;
+
+ case 'r':
+ rf = 1;
+ rate = atoi(optarg);
+ break;
+
+ case 's':
+ sf = 1;
+ break;
+
+ case 't':
+ if(*optarg == '+')
+ tf = 1;
+ else if(*optarg == '-')
+ tf = -1;
+ else
+ errf = 1;
+ break;
+
+ case 'x':
+ if(of)
+ errf = 1;
+ else
+ xf = 1;
+ break;
+
+ default:
+ usage();
+ }
+ }
+
+ if((Rf == 0 && df == 0 && lf == 0 && tf == 0 && sf == 0 &&
+ rf == 0 && mf == 0 ) || errf)
+ {
+ usage();
+ }
+
+ if((kbfd = open(KEYB_DEVICE, 0)) < 0)
+ {
+ perror("kcon: keyboard open failiure");
+ exit(1);
+ }
+
+ if(sf)
+ {
+ showtypeamatic(kbfd);
+ exit(0);
+ }
+
+ if(lf)
+ {
+ listcurrent(kbfd);
+ exit(0);
+ }
+
+ if (Rf)
+ {
+ if (ioctl(kbfd, KBDRESET, 0) < 0) {
+ perror ("kcon: ioctl KBDRESET failed");
+ exit (1);
+ }
+ }
+
+ if(tf)
+ {
+ setrepeat(kbfd, tf);
+ }
+
+ if(df || rf)
+ {
+ if(delay > 3)
+ {
+ fprintf(stderr,"Delay value (%d) out of range, possible values are 0..3!\n",delay);
+ exit(1);
+ }
+ if(rate > 31)
+ {
+ fprintf(stderr,"Rate value (%d) out of range, possible values are 0..31!\n",rate);
+ exit(1);
+ }
+ settypeam(kbfd, delay, rate);
+ }
+
+ if(mf)
+ {
+ remapkeys(kbfd, map);
+ }
+
+ close(kbfd);
+ exit(0);
+}
+
+/*---------------------------------------------------------------------------*
+ * display usage info & exit
+ *---------------------------------------------------------------------------*/
+usage()
+{
+ fprintf(stderr, "\nkcon: keyboard control and remapping utility for pcvt video driver\n");
+ fprintf(stderr, "usage: [-R] [-d delay] [-l] [-m map] [-o] [-p] [-r rate] [-t +/-] [-x]\n");
+ fprintf(stderr, " -R full reset of keyboard\n");
+ fprintf(stderr, " -d delay until a key is repeated (range: 0...3 => 250...1000ms)\n");
+ fprintf(stderr, " -l produce listing of current keyboard mapping\n");
+ fprintf(stderr, " -m set keyboard remapping from a keycap entry\n");
+ fprintf(stderr, " -o set octal output for listing\n");
+ fprintf(stderr, " -p pure, don't display escape as 'ESC' for listing\n");
+ fprintf(stderr, " -r chars/second repeat value (range: 0...31 => 30...2 chars/sec)\n");
+ fprintf(stderr, " -s show, display the current keyboard typematic values\n");
+ fprintf(stderr, " -t switch repeat on(+) or off(-)\n");
+ fprintf(stderr, " -x set hexadecimal output for listing\n\n");
+ exit(1);
+}
+
+/*---------------------------------------------------------------------------*
+ * convert control char in string to printable values
+ *---------------------------------------------------------------------------*/
+char *showcntrl(s)
+u_char *s;
+{
+ static char res_str[80];
+ static char conv_buf[80];
+ int i;
+
+ res_str[0] = '\0';
+
+ for(i = 0; s[i]; i++)
+ {
+ if(((s[i] > 0x20) && (s[i] <= 0x7e)) || ((s[i] >= 0xa0) && (s[i] <= 0xff)))
+ {
+ conv_buf[0] = s[i];
+ conv_buf[1] = '\0';
+ }
+ else if((s[i] == 0x1b) && (pf == 0))
+ {
+ strcpy(conv_buf,"ESC ");
+ }
+ else if(of)
+ {
+ sprintf(conv_buf,"\\%03.3o ", s[i]);
+ }
+ else
+ {
+ sprintf(conv_buf,"0x%02.2X ", s[i]);
+ }
+ strcat(res_str, conv_buf);
+ }
+ return(res_str);
+}
+
+/*---------------------------------------------------------------------------*
+ * list the current keyboard mapping
+ *---------------------------------------------------------------------------*/
+listcurrent(kbfd)
+int kbfd;
+{
+ static char *keytypetab[] = {
+ "NONE ",
+ "SHIFT ",
+ "ALT/META ",
+ "NUMLOCK ",
+ "CONTROL ",
+ "CAPSLOCK ",
+ "ASCII ",
+ "SCROLL ",
+ "FUNCTION ",
+ "KEYPAD ",
+ "BREAK ",
+ "ALTGR ",
+ "SHIFTLOCK",
+ "CURSOR ",
+ "RETURN "
+ };
+
+ struct kbd_ovlkey keyboardmap[KBDMAXKEYS];
+ struct kbd_ovlkey *kbmapp;
+ int keytype;
+ int altgr_defined;
+ int i;
+
+ altgr_defined = 0;
+ kbmapp = keyboardmap;
+
+ for (i = 0; i < KBDMAXKEYS; i++)
+ {
+ kbmapp->keynum = i;
+
+ if(ioctl(kbfd, KBDGCKEY, kbmapp) < 0)
+ {
+ perror("kcon: ioctl KBDGCKEY failed");
+ exit(1);
+ }
+
+ if((kbmapp->type & KBD_MASK) == KBD_ALTGR)
+ altgr_defined = i;
+
+ kbmapp++;
+ }
+
+ if(altgr_defined)
+ {
+ printf("S Key KeyType Normal Shift Control Altgr \n");
+ printf("- --- --------- --------------- --------------- --------------- ---------------\n");
+ }
+ else
+ {
+ printf("S Key KeyType Normal Shift Control \n");
+ printf("- --- --------- --------------- --------------- ---------------\n");
+ }
+
+ kbmapp = &keyboardmap[1];
+
+ for(i = 1; i < KBDMAXKEYS; i++)
+ {
+ keytype = kbmapp->type;
+
+ if(keytype)
+ {
+ if(keytype & KBD_OVERLOAD)
+ printf("! %3.3d %9.9s ", i, keytypetab[keytype & KBD_MASK]);
+ else
+ printf("- %3.3d %9.9s ", i, keytypetab[keytype & KBD_MASK]);
+
+ switch(keytype & KBD_MASK)
+ {
+
+ case KBD_NUM:
+ case KBD_ASCII:
+ case KBD_FUNC:
+ case KBD_KP:
+ case KBD_CURSOR:
+ case KBD_RETURN: /* ??? */
+
+ if(kbmapp->subu == KBD_SUBT_STR)
+ printf("%-15s ",showcntrl(kbmapp->unshift));
+ else
+ printf("Function() ");
+
+ if(kbmapp->subs == KBD_SUBT_STR)
+ printf("%-15s ",showcntrl(kbmapp->shift));
+ else
+ printf("Function() ");
+
+ if(kbmapp->subc == KBD_SUBT_STR)
+ printf("%-15s ",showcntrl(kbmapp->ctrl));
+ else
+ printf("Function() ");
+
+ if(altgr_defined)
+ {
+ if(kbmapp->suba == KBD_SUBT_STR)
+ printf("%-15s ",showcntrl(kbmapp->altgr));
+ else
+ printf("Function() ");
+ }
+ break;
+ }
+ putchar('\n');
+ }
+ kbmapp++;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * show delay and rate values for keyboard
+ *---------------------------------------------------------------------------*/
+showtypeamatic(kbfd)
+int kbfd;
+{
+ static char *delaytab[] = {
+ "250",
+ "500",
+ "750",
+ "1000"
+ };
+
+ static char *ratetab[] = {
+ "30.0",
+ "26.7",
+ "24.0",
+ "21.8",
+ "20.0",
+ "18.5",
+ "17.1",
+ "16.0",
+ "15.0",
+ "13.3",
+ "12.0",
+ "10.9",
+ "10.0",
+ "9.2",
+ "8.6",
+ "8.0",
+ "7.5",
+ "6.7",
+ "6.0",
+ "5.5",
+ "5.0",
+ "4.6",
+ "4.3",
+ "4.0",
+ "3.7",
+ "3.3",
+ "3.0",
+ "2.7",
+ "2.5",
+ "2.3",
+ "2.1",
+ "2.0"
+ };
+
+ int cur_typemat_val;
+ int delay, rate;
+
+ if((ioctl(kbfd, KBDGTPMAT, &cur_typemat_val)) < 0)
+ {
+ perror("kcon: ioctl KBDGTPMAT failed");
+ exit(1);
+ }
+
+ delay = ((cur_typemat_val & 0x60) >> 5);
+ rate = cur_typemat_val & 0x1f;
+
+ printf("\nDisplaying the current keyboard typematic values:\n\n");
+ printf("The delay-until-repeat time is [ %s ] milliseconds\n",delaytab[delay]);
+ printf("The repeat-rate is [ %s ] characters per second\n\n",ratetab[rate]);
+}
+
+/*---------------------------------------------------------------------------*
+ * set repeat feature on/off
+ *---------------------------------------------------------------------------*/
+setrepeat(kbfd, tf)
+int kbfd;
+int tf;
+{
+ int srepsw_val;
+
+ if(tf == 1)
+ srepsw_val = KBD_REPEATON;
+ else
+ srepsw_val = KBD_REPEATOFF;
+
+ if(ioctl(kbfd, KBDSREPSW, &srepsw_val) < 0)
+ {
+ perror("kcon: ioctl KBDREPSW failed");
+ exit(1);
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * set delay and rate values for keyboard
+ *---------------------------------------------------------------------------*/
+settypeam(kbfd, delay, rate)
+int kbfd;
+int delay;
+int rate;
+{
+ int cur_typemat_val;
+ int new_typemat_val;
+
+ if((ioctl(kbfd, KBDGTPMAT, &cur_typemat_val)) < 0)
+ {
+ perror("kcon: ioctl KBDGTPMAT failed");
+ exit(1);
+ }
+
+ if(delay == -1)
+ delay = (cur_typemat_val & 0x60);
+ else
+ delay = ((delay << 5) & 0x60);
+
+ if(rate == -1)
+ rate = (cur_typemat_val & 0x1f);
+ else
+ rate &= 0x1f;
+
+ new_typemat_val = delay | rate;
+
+ if((ioctl(kbfd, KBDSTPMAT, &new_typemat_val)) < 0)
+ {
+ perror("kcon: ioctl KBDSTPMAT failed");
+ exit(1);
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * remap keyboard from keycap entry
+ *---------------------------------------------------------------------------*/
+remapkeys(kbfd, map)
+int kbfd;
+char *map;
+{
+ char cap_entry[1024];
+ int ret;
+ char keyflag[128];
+ int i;
+
+ /* try to find the entry */
+
+ ret = kgetent(cap_entry, map);
+
+ if(ret == -1)
+ {
+ fprintf(stderr, "kcon: keycap database not found or not accessible!\n");
+ exit(1);
+ }
+ else if(ret == 0)
+ {
+ fprintf(stderr, "kcon: keycap entry [%s] not found in database!\n", map);
+ exit(1);
+ }
+
+ /* set default mapping */
+
+ if((ioctl(kbfd, KBDDEFAULT)) < 0)
+ {
+ perror("kcon: ioctl KBDDEFAULT failed");
+ exit(1);
+ }
+
+ /* DE flag present? */
+
+ if(kgetflag("de"))
+ return;
+
+ for(i = 0; i < KBDMAXKEYS; i++)
+ keyflag[i] = 0;
+
+ set_lock(keyflag, kbfd);
+
+ set_shift(keyflag, kbfd);
+
+ set_char(keyflag, kbfd);
+}
+
+/*---------------------------------------------------------------------------*
+ * care for lock keys
+ *---------------------------------------------------------------------------*/
+set_lock(keyflag, kbfd)
+char keyflag[];
+int kbfd;
+{
+ int i, j;
+ char cap[16];
+ struct kbd_ovlkey entry;
+
+ struct {
+ char *ch;
+ u_short typ;
+ } lock[] =
+ {
+ "ca", KBD_CAPS,
+ "sh", KBD_SHFTLOCK,
+ "nl", KBD_NUMLOCK,
+ "sc", KBD_SCROLL
+ };
+
+
+ for(i = 0; i < 4; i++)
+ {
+ int n;
+
+ sprintf(cap, "%s", lock[i].ch);
+
+ n = kgetnum(cap);
+
+ if(n > 0)
+ {
+ if (keyflag[n])
+ {
+ fprintf(stderr,"kcon: duplicate key definition for key [%d]!\n",n);
+ exit(1);
+ }
+ keyflag[n] = 1;
+
+ entry.keynum = n;
+ entry.type = lock[i].typ;
+
+ if((ioctl(kbfd, KBDSCKEY, &entry)) < 0)
+ {
+ perror("kcon: ioctl KBDSCKEY failed");
+ exit(1);
+ }
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * care for shifting keys
+ *---------------------------------------------------------------------------*/
+set_shift(keyflag, kbfd)
+char keyflag[];
+int kbfd;
+{
+ int i, j;
+ char cap[16];
+ struct kbd_ovlkey entry;
+
+ struct {
+ char ch;
+ u_short typ;
+ } shift[] =
+ {
+ 'm', KBD_META,
+ 'l', KBD_ALTGR,
+ 'h', KBD_SHIFT,
+ 't', KBD_CTL
+ };
+
+ for(i = 0; i < 4; i++)
+ {
+ for(j = 1; j < 10; j++)
+ {
+ int n;
+
+ sprintf(cap, "%c%d", shift[i].ch,j);
+
+ n = kgetnum(cap);
+
+ if (n >= 0)
+ {
+ if (keyflag[n])
+ {
+ fprintf(stderr,"kcon: duplicate key definition for key [%d]!\n",n);
+ exit(1);
+ }
+ keyflag[n] = 1;
+
+ entry.keynum = n;
+ entry.type = shift[i].typ;
+ if((ioctl(kbfd, KBDSCKEY, &entry)) < 0)
+ {
+ perror("kcon: ioctl KBDSCKEY failed");
+ exit(1);
+ }
+ }
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * care for normal keys
+ *---------------------------------------------------------------------------*/
+set_char(keyflag, kbfd)
+char keyflag[];
+int kbfd;
+{
+ int i, j;
+ char cap[16];
+ int setflag;
+ char *addr_str;
+ char *new_str;
+ struct kbd_ovlkey entry;
+
+ struct {
+ char *addr;
+ char ch;
+ } standard[] = {
+ 0, 'D',
+ &entry.unshift[0], 'K',
+ &entry.shift[0], 'S',
+ &entry.ctrl[0], 'C',
+ &entry.altgr[0], 'A'
+ };
+
+ for(i = 1; i < KBDMAXKEYS; i++)
+ {
+ setflag = 0;
+
+ entry.keynum = i;
+
+ if((ioctl(kbfd, KBDGOKEY, &entry)) < 0)
+ {
+ perror("kcon: ioctl KBDGOKEY failed");
+ exit(1);
+ }
+
+ entry.type = KBD_ASCII;
+
+ for(j = 0; j < 5; j++)
+ {
+ sprintf(cap, "%c%d", standard[j].ch,i);
+
+ if((j == 0) && (kgetflag(cap)))
+ {
+ /* delete a key */
+
+ entry.type = KBD_NONE;
+ setflag = 1;
+ goto setit;
+
+ }
+ else
+ {
+ addr_str = standard[j].addr;
+ if(new_str = kgetstr(cap, &addr_str))
+ {
+ if(strlen(new_str) > KBDMAXOVLKEYSIZE)
+ {
+ fprintf(stderr, "kcon: database entry string [%s] longer than max [%d]!\n",new_str,KBDMAXOVLKEYSIZE);
+ exit(1);
+ }
+ setflag = 1;
+ }
+ }
+ }
+
+setit: if (setflag)
+ {
+ if (keyflag[i])
+ {
+ fprintf(stderr,"kcon: duplicate key definition for key [%d]!\n",i);
+ exit(1);
+ }
+ keyflag[i] = 1;
+
+ if((ioctl(kbfd, KBDSCKEY, &entry)) < 0)
+ {
+ perror("kcon: ioctl KBDSCKEY failed");
+ exit(1);
+ }
+ }
+ }
+}
+
+/*------------------- EOF ------------------------------------------------*/
diff --git a/usr.sbin/pcvt/keycap/Makefile b/usr.sbin/pcvt/keycap/Makefile
new file mode 100644
index 0000000..c85dba4
--- /dev/null
+++ b/usr.sbin/pcvt/keycap/Makefile
@@ -0,0 +1,24 @@
+# $FreeBSD$
+
+LIB= keycap
+MAN= keycap.3
+MLINKS+= keycap.3 kgetent.3 \
+ keycap.3 kgetnum.3 \
+ keycap.3 kgetflag.3 \
+ keycap.3 kgetstr.3
+MAN+= man5/keycap.5
+SRCS= keycap.c
+
+KEYCAPSRC= keycap.src
+CAPDIR= /usr/share/misc
+CAPPATH= $(CAPDIR)/keycap.pcvt
+
+CFLAGS+= -DKEYCAP_PATH=\"$(CAPPATH)\"
+
+#CLEANFILES+= keycap.0 man5/keycap.0
+
+beforeinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${NOBINMODE} \
+ ${.CURDIR}/${KEYCAPSRC} ${DESTDIR}${CAPPATH}
+
+.include <bsd.lib.mk>
diff --git a/usr.sbin/pcvt/keycap/keycap.3 b/usr.sbin/pcvt/keycap/keycap.3
new file mode 100644
index 0000000..19348d2
--- /dev/null
+++ b/usr.sbin/pcvt/keycap/keycap.3
@@ -0,0 +1,125 @@
+.\"
+.\" Copyright (c) 1992,1993,1994 Hellmuth Michaelis
+.\"
+.\" Copyright (c) 1990 The Regents of the University of California.
+.\"
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)keycap.3, 3.00, Last Edit-Date: [Sun Jan 2 13:46:43 1994]
+.\" $FreeBSD$
+.\"
+.Dd January 3, 1993
+.Dt KEYCAP 3
+.Os
+.Sh NAME
+.Nm kgetent ,
+.Nm kgetnum ,
+.Nm kgetflag ,
+.Nm kgetstr
+.Nd routines for accessing the keycap database
+.Sh SYNOPSIS
+.Ft int
+.Fn kgetent "char *bp" "char *name"
+.Ft int
+.Fn kgetnum "char *id"
+.Ft int
+.Fn kgetflag "char *id"
+.Ft char *
+.Fn kgetstr "char *id" "char **area"
+.Sh DESCRIPTION
+These functions extract and use capabilities from a keyboard capability data
+base, usually
+.Pa /usr/share/misc/keycap.pcvt ,
+the format of which is described in
+.Xr keycap 5 .
+.Pp
+The
+.Fn kgetent
+function
+extracts the entry for keyboard mapping
+.Fa name
+into the buffer at
+.Fa bp .
+The
+.Fa bp
+argument
+should be a character buffer of size
+1024 and must be retained through all subsequent calls to
+.Fn kgetnum ,
+.Fn kgetflag ,
+and
+.Fn kgetstr .
+The
+.Fn kgetent
+function
+returns \-1 if none of the
+.Nm keycap
+data base files could be opened,
+0 if the map name given does not have an entry,
+and 1 if all goes well.
+.Pp
+The
+.Fn kgetnum
+function
+gets the numeric value of capability
+.Fa id ,
+returning \-1 if is not given for the map.
+.Pp
+The
+.Fn kgetflag
+function
+returns 1 if the specified capability is present in
+the map's entry, 0 if it is not.
+.Pp
+The
+.Fn kgetstr
+function
+returns the string value of the capability
+.Fa id ,
+places it in the buffer at
+.Fa area ,
+and advances the
+.Fa area
+pointer.
+The
+.Fn kgetstr
+function
+returns
+.Dv NULL
+if the capability was not found.
+.Sh FILES
+.Bl -tag -width /usr/share/misc/keycap.pcvt -compact
+.It Pa /usr/share/misc/keycap.pcvt
+Keyboard capabilities database (if nothing else chosen during installation).
+.El
+.Sh SEE ALSO
+.Xr kcon 1 ,
+.Xr keycap 5
diff --git a/usr.sbin/pcvt/keycap/keycap.c b/usr.sbin/pcvt/keycap/keycap.c
new file mode 100644
index 0000000..9c276a3
--- /dev/null
+++ b/usr.sbin/pcvt/keycap/keycap.c
@@ -0,0 +1,383 @@
+/*-
+ * Copyright (c) 1992, 1993 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Holger Veit
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif
+
+static char *id =
+ "@(#)keycap.c, 3.20, Last Edit-Date: [Tue Dec 20 14:51:50 1994]";
+
+/*---------------------------------------------------------------------------*
+ *
+ * keycap.c Keyboard capabilities database handling
+ * -------------------------------------------------------
+ *
+ * converted from printcap by Holger Veit (veit@du9ds3.uni-duisburg.de)
+ *
+ * BUG: Should use a "last" pointer in tbuf, so that searching
+ * for capabilities alphabetically would not be a n**2/2
+ * process when large numbers of capabilities are given.
+ *
+ * Note: If we add a last pointer now we will screw up the
+ * tc capability. We really should compile termcap.
+ *
+ * modified by Hellmuth Michaelis (hm@hcshh.hcs.de) to fit into the
+ * vt220 driver pcvt 2.0 distribution
+ *
+ * -hm header conversion & cosmetic changes for pcvt 2.0 distribution
+ * -hm debugging remapping
+ * -hm cleaning up from termcap ....
+ * -hm split off header file keycap.h
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <ctype.h>
+#include <unistd.h>
+
+#include "keycap.h"
+
+#define KEYCAP_BUFSIZ 1024
+
+#define MAXHOP 32 /* max number of tc= indirections */
+
+char *getenv();
+
+static FILE *pfp = NULL; /* keycap data base file pointer */
+static char *tbuf;
+static int hopcount; /* detect infinite loops in keycap, init 0 */
+
+static int knchktc();
+static int knamatch();
+static char *kdecode();
+
+/*---------------------------------------------------------------------------*
+ * match a name
+ *---------------------------------------------------------------------------*/
+static char *nmatch(id,cstr)
+char *id,*cstr;
+{
+ register n = strlen(id);
+ register char *c = cstr+n;
+
+ if (strncmp(id,cstr,n)==0 &&
+ (*c==':' || *c=='|' || *c=='=' || *c=='#') || *c=='@')
+ return c;
+ return 0;
+}
+
+/*---------------------------------------------------------------------------*
+ * Get an entry for keyboard name in buffer bp from the keycap file.
+ * Parse is very rudimentary, we just notice escaped newlines.
+ *---------------------------------------------------------------------------*/
+kgetent(bp, name)
+char *bp, *name;
+{
+ register char *cp;
+ register int c;
+ register int i = 0, cnt = 0;
+ char ibuf[KEYCAP_BUFSIZ];
+ char *cp2;
+ int tf;
+
+ tbuf = bp;
+ tf = 0;
+
+ tf = open(KEYCAP_PATH, 0);
+
+ if (tf < 0)
+ return (-1);
+ for (;;) {
+ cp = bp;
+ for (;;) {
+ if (i == cnt) {
+ cnt = read(tf, ibuf, KEYCAP_BUFSIZ);
+ if (cnt <= 0) {
+ close(tf);
+ return (0);
+ }
+ i = 0;
+ }
+ c = ibuf[i++];
+ if (c == '\n') {
+ if (cp > bp && cp[-1] == '\\'){
+ cp--;
+ continue;
+ }
+ break;
+ }
+ if (cp >= bp+KEYCAP_BUFSIZ) {
+ write(STDERR_FILENO, "Keycap entry too long\n", 23);
+ break;
+ } else
+ *cp++ = c;
+ }
+ *cp = 0;
+
+ /*
+ * The real work for the match.
+ */
+ if (knamatch(name)) {
+ close(tf);
+ return(knchktc());
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * knchktc: check the last entry, see if it's tc=xxx. If so, recursively
+ * find xxx and append that entry (minus the names) to take the place of
+ * the tc=xxx entry. Note that this works because of the left to right scan.
+ *---------------------------------------------------------------------------*/
+static int knchktc()
+{
+ register char *p, *q;
+ char tcname[16]; /* name of similar keyboard */
+ char tcbuf[KEYCAP_BUFSIZ];
+ char *holdtbuf = tbuf;
+ int l;
+
+ p = tbuf + strlen(tbuf) - 2; /* before the last colon */
+ while (*--p != ':')
+ if (p<tbuf) {
+ write(STDERR_FILENO, "Bad keycap entry\n", 18);
+ return (0);
+ }
+ p++;
+ /* p now points to beginning of last field */
+ if (p[0] != 't' || p[1] != 'c')
+ return(1);
+ strcpy(tcname,p+3);
+ q = tcname;
+ while (q && *q != ':')
+ q++;
+ *q = 0;
+ if (++hopcount > MAXHOP) {
+ write(STDERR_FILENO, "Infinite tc= loop\n", 18);
+ return (0);
+ }
+ if (kgetent(tcbuf, tcname) != 1)
+ return(0);
+ for (q=tcbuf; *q != ':'; q++)
+ ;
+ l = p - holdtbuf + strlen(q);
+ if (l > KEYCAP_BUFSIZ) {
+ write(STDERR_FILENO, "Keycap entry too long\n", 23);
+ q[KEYCAP_BUFSIZ - (p-tbuf)] = 0;
+ }
+ strcpy(p, q+1);
+ tbuf = holdtbuf;
+ return(1);
+}
+
+/*---------------------------------------------------------------------------*
+ * knamatch deals with name matching. The first field of the keycap entry
+ * is a sequence of names separated by |'s, so we compare against each such
+ * name. The normal : terminator after the last name (before the first field)
+ * stops us.
+ *---------------------------------------------------------------------------*/
+static int knamatch(np)
+char *np;
+{
+ register char *Np, *Bp;
+
+ Bp = tbuf;
+ if (*Bp == '#' || *Bp == 0)
+ return(0);
+ for (;;) {
+ for (Np = np; *Np && *Bp == *Np; Bp++, Np++)
+ continue;
+ if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0))
+ return (1);
+ while (*Bp && *Bp != ':' && *Bp != '|')
+ Bp++;
+ if (*Bp == 0 || *Bp == ':')
+ return (0);
+ Bp++;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * Skip to the next field. Notice that this is very dumb, not knowing about
+ * \: escapes or any such. If necessary, :'s can be put into the keycap file
+ * in octal.
+ *---------------------------------------------------------------------------*/
+static char *kskip(bp)
+char *bp;
+{
+ while (*bp && *bp != ':')
+ bp++;
+ if (*bp == ':')
+ bp++;
+ return (bp);
+}
+
+/*---------------------------------------------------------------------------*
+ * Return the (numeric) option id. Numeric options look like 'li#80' i.e.
+ * the option string is separated from the numeric value by a # character.
+ * If the option is not found we return -1. Note that we handle octal
+ * numbers beginning with 0.
+ *---------------------------------------------------------------------------*/
+int kgetnum(id)
+char *id;
+{
+ register int i, base;
+ register char *bp = tbuf,*xp;
+
+ for (;;) {
+ bp = kskip(bp);
+ if (*bp == 0)
+ return (-1);
+ if ((xp=nmatch(id,bp)) == 0)
+ continue;
+ bp = xp; /* we have an entry */
+ if (*bp == '@')
+ return(-1);
+ if (*bp != '#')
+ continue;
+ bp++;
+ base = 10;
+ if (*bp == '0')
+ base = 8;
+ i = 0;
+ while (isdigit(*bp))
+ i *= base, i += *bp++ - '0';
+ return (i);
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * Handle a flag option. Flag options are given "naked", i.e. followed by
+ * a : or the end of the buffer. Return 1 if we find the option, or 0 if
+ * it is not given.
+ *---------------------------------------------------------------------------*/
+int kgetflag(id)
+char *id;
+{
+ register char *bp = tbuf,*xp;
+
+ for (;;) {
+ bp = kskip(bp);
+ if (!*bp)
+ return (0);
+ if ((xp=nmatch(id,bp)) != 0) {
+ bp = xp;
+ if (!*bp || *bp == ':')
+ return (1);
+ else if (*bp == '@')
+ return(0);
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * Get a string valued option. These are given as 'cl=^Z'. Much decoding
+ * is done on the strings, and the strings are placed in area, which is a
+ * ref parameter which is updated. No checking on area overflow.
+ *---------------------------------------------------------------------------*/
+char *kgetstr(id, area)
+char *id;
+char **area;
+{
+ register char *bp = tbuf,*xp;
+
+ for (;;) {
+ bp = kskip(bp);
+ if (!*bp)
+ return (0);
+ if ((xp = nmatch(id,bp)) == 0)
+ continue;
+ bp = xp;
+ if (*bp == '@')
+ return(0);
+ if (*bp != '=')
+ continue;
+ bp++;
+ return (kdecode(bp, area));
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * kdecode does the grung work to decode the string capability escapes.
+ *---------------------------------------------------------------------------*/
+static char *kdecode(str, area)
+char *str;
+char **area;
+{
+ register char *cp;
+ register int c;
+ register char *dp;
+ int i;
+
+ cp = *area;
+ while ((c = *str++) && c != ':') {
+ switch (c) {
+
+ case '^':
+ c = *str++ & 037;
+ break;
+
+ case '\\':
+ dp = "E\033^^\\\\::n\nr\rt\tb\bf\f";
+ c = *str++;
+nextc:
+ if (*dp++ == c) {
+ c = *dp++;
+ break;
+ }
+ dp++;
+ if (*dp)
+ goto nextc;
+ if (isdigit(c)) {
+ c -= '0', i = 2;
+ do
+ c <<= 3, c |= *str++ - '0';
+ while (--i && isdigit(*str));
+ }
+ break;
+ }
+ *cp++ = c;
+ }
+ *cp++ = 0;
+ str = *area;
+ *area = cp;
+ return (str);
+}
+
+/*-------------------------------- EOF --------------------------------------*/
diff --git a/usr.sbin/pcvt/keycap/keycap.h b/usr.sbin/pcvt/keycap/keycap.h
new file mode 100644
index 0000000..1dc4c3e
--- /dev/null
+++ b/usr.sbin/pcvt/keycap/keycap.h
@@ -0,0 +1,49 @@
+/*-
+ * Copyright (c) 1992, 1993 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Holger Veit
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)keycap.h, 3.20, Last Edit-Date: [Tue Dec 20 14:52:11 1994]
+ */
+
+#ifndef _KEYCAP_H_
+#define _KEYCAP_H_
+
+int kgetent( char*, char* );
+int kgetnum( char* );
+int kgetflag( char* );
+char *kgetstr( char*, char** );
+
+#endif /* _KEYCAP_H_ */
+
+/*-------------------------------- EOF -------------------------------------*/
diff --git a/usr.sbin/pcvt/keycap/keycap.src b/usr.sbin/pcvt/keycap/keycap.src
new file mode 100644
index 0000000..a991264e
--- /dev/null
+++ b/usr.sbin/pcvt/keycap/keycap.src
@@ -0,0 +1,627 @@
+#
+# Copyright (c) 1992, 2002 Hellmuth Michaelis
+#
+# Copyright (c) 1992, 1994 Joerg Wunsch and Holger Veit
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by Hellmuth Michaelis,
+# Joerg Wunsch and Holger Veit.
+# 4. The names of the Authors may not be used to endorse or promote
+# products derived from this software without specific prior written
+# permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#---------------------------------------------------------------------------
+#
+# keyboard mappings for vt220 emulator pcvt 3.00
+# ----------------------------------------------
+#
+# Last Edit-Date: [Wed Apr 24 16:27:35 2002]
+#
+# $FreeBSD$
+#
+#---------------------------------------------------------------------------
+
+df|default|default entry:\
+ :de:
+
+tt|test|Test entry which swaps y and z:\
+ :K22=z:S22=Z:C22=\032:\
+ :K46=y:S46=Y:C46=\031:
+
+# from codrv, untested
+be|belgium|Belgian mapping:\
+ :K1=\262:S1=\263:\
+ :K2=&:S2=1:A2=|:\
+ :K3=\351:S3=2:A3=@:\
+ :K4=":S4=3:A4=#:\
+ :K5=':S5=4:\
+ :K6=(:S6=5:\
+ :K7=\247:S7=6:A7=\136:\
+ :K8=\350:S8=7:\
+ :K9=!:S9=8:\
+ :K10=\347:S10=9:A10={:\
+ :K11=\340:S11=0:A11=}:\
+ :K12=):S12=\260:\
+ :K13=-:S13=_:\
+ :K17=a:S17=A:C17=^A:\
+ :K18=z:S18=Z:C18=^z:\
+ :K27=\136:S27=\250:A27=[:p1#27:\
+ :K28=$:S28=*:A28=]:\
+ :K31=q:S31=Q:C31=^q:\
+ :K40=m:S40=M:C40=^m:\
+ :K41=\371:S41=%:A41=':p2#41:\
+ :K42=\265:S42=\243:A42=\264:p3#42:\
+ :K45=<:S45=>:A45=\\:\
+ :K46=w:S46=W:C46=^w:\
+ :K52=,:S52=\077:\
+ :K53=;:S53=.:\
+ :K54=\072:S54=/:\
+ :K55==:S55=+:A55=~:\
+ :l1#62:a0:
+
+# from codrv, untested
+ca|canadafr|Canadian French mapping:\
+ :K1=#:S1=|:A1=\\:\
+ :A2=\261:S3=":A3=@:S4=/:A4=\243:A5=\242:\
+ :A6=\244:S7=\077:A7=\254:S8=&:A8=\246:S9=*:A9=\262:\
+ :S10=(:A10=\263:S11=):A11=\274:\
+ :K12=-:S12=_:A12=\275:\
+ :K13==:S13=+:A13=\276:\
+ :A25=\247:A26=\266:\
+ :K27=\136:S27=\136:A27=[:\
+ :K28=\270:S28=\250:A28=]:p1#28:\
+ :A40=~:K41=`:S41=`:A41={:\
+ :K42=<:S42=>:A42=}:\
+ :K45=\253:S45=\273:A45=\260:\
+ :A49=\253:A50=\273:A51=\260:\
+ :A52=\265:K53=,:S53=,:A53=-:\
+ :K54=\351:S54=\311:A54=':\
+ :l1#62:a0:
+
+# from codrv, untested
+c1|swissde|Swiss German mapping:\
+ :K1=\247:S1=\260:p1#1:\
+ :S2=+:A2=|:S3=":A3=@:S4=*:A4=#:S5=\347:S7=&:A7=\254:\
+ :S8=/:A8=\246:S9=(:A9=\242:S10=):S11==:\
+ :K12=`:S12=\077:A12=':p2#12:\
+ :K13=^:S13=\\:A13=~:p3#13:\
+ :K22=z:S22=Z:C22=\032:\
+ :S27=\350:K27=\374:A27=[:\
+ :K28=\250:S28=!:A28=]:\
+ :S40=\351:K40=\366:\
+ :S41=\340:K41=\344:A41={:\
+ :K42=$:S42=\243:A42=}:\
+ :K45=<:S45=>:A45=\\:\
+ :K46=y:S46=Y:C46=\031:
+ :S53=;:S54=\072:K55=-:S55=_:\
+ :l1#62:a0:
+
+# from codrv, untested
+c2|swissfr|Swiss French mapping:\
+ :K27=\350:S27=\374:A27=[:\
+ :K40=\351:S40=\366:\
+ :K41=\340:S41=\344:A41={:\
+ :tc=swissde:
+
+# more programmer-like than an original German kbd, you needn't
+# have gum-fingers to get `{}' and the like:-)
+# maps: ae -> [, oe -> \, ue -> ], Ae -> {, Oe -> |, Ue -> }
+# umlaute are available as AltGr- and Control-Mappings
+# also maps Pause -> ^Z
+#
+# (from Joerg Wunsch)
+#
+# l1/m1 bindings: left Alt is AltGr
+# Emacs functions:
+# C79/C89: ctrl-{leftarrow,rightarrow} {backward,forward} word
+# A79/A89: {backward,forward} sexp
+# C83/C84: ctrl-{uparrow,downarrow} {backward,forward} window
+#
+de-prog|germany-prog|programmer's mapping for german keyboard:\
+ :K27=]:S27=}:A27=\374:C27=\334:\
+ :K40=\\:S40=|:A40=\366:C40=\326:\
+ :K41=[:S41={:A41=\344:C41=\304:\
+ :K126=\032:C126=\003:\
+ :tc=de:
+# :l1#60:l2#62:\
+# :C79=^[B:K79=^[[D:S79=^[OD:A79=^[^B:\
+# :C89=^[F:K89=^[[C:S89=^[OC:A89=^[^F:\
+# :C83=^U-1^XO:K83=^[[A:S84=^[OA:\
+# :C84=^XO:K84=^[[B:S84=^[OB:\
+
+de|germany|German mapping for MF II-Keyboard:\
+ :K1=\136:S1=\260:\
+ :S3=\042:S4=\247:S7=&:S8=/:S9=(:S10=):S11=\075:\
+ :A3=\262:A4=\263:A8={:A9=[:A10=]:A11=}:A12=\134:\
+ :K12=\337:S12=?:C12=\034:\
+ :K13=':S13=`:\
+ :A17=\100:\
+ :K22=z:S22=Z:C22=\032:\
+ :K27=\374:S27=\334:\
+ :K28=+:S28=*:A28=\176:\
+ :K29=\043:S29=':A29=\174:\
+ :K40=\366:S40=\326:\
+ :K41=\344:S41=\304:\
+ :A45=\174:\
+ :K46=y:S46=Y:C46=\031:\
+ :A52=\265:\
+ :S53=;:S54=\072:\
+ :K55=-:S55=_:\
+ :l1#62:
+
+de-at|germany-at|German mapping for AT-Keyboard:\
+ :K1=<:S1=>:\
+ :S3=\042:\
+ :S4=\247:\
+ :S7=&:\
+ :S8=/:\
+ :S9=(:\
+ :S10=):\
+ :S11=\075:\
+ :K12=\337:S12=?:C12=\034:\
+ :K13=':S13=`:\
+ :K14=#:S14=\136:\
+ :K22=z:S22=Z:C22=\032:\
+ :K27=\374:S27=\334:\
+ :K28=+:S28=*:\
+ :K29=\043:S29=':\
+ :K40=\366:S40=\326:\
+ :K41=\344:S41=\304:\
+ :K46=y:S46=Y:C46=\031:\
+ :S53=;:S54=\072:\
+ :K55=-:S55=_:\
+
+# from codrv, untested
+# Includes improvements by Thomas Hiller (hiller@fzi.de)
+# and Andreas Israel (ai@hrz.tu-chemnitz.de)
+de-hi|germany-hiller|yet another German mapping:\
+ :K1=\136:S1=\260:C1=|:\
+ :S3=\042:S4=#:S7=&:S8=/:S9=(:S10=):S11=\075:\
+ :A8={:A9=[:A10=]:A11=}:A17=@:A28=~:\
+ :K12=\337:S12=\077:C12=\036:A12=\\:\
+ :K13=':S13=`:C13=\134:p1#13:\
+ :K22=z:S22=Z:C22=\032:\
+ :K27=\374:S27=\334:C27=\035:\
+ :K28=+:S28=*:C28=\000:\
+ :K29=<:S29=>:C29=\134:\
+ :K40=\366:S40=\326:C40=\034:\
+ :K41=\344:S41=\304:C41=\033:\
+ :K46=y:S46=Y:C46=\031:\
+ :S53=;:S54=\072:\
+ :K55=-:S55=_:\
+ :l1#62:a0:
+
+# from codrv, untested
+# Contribution by Thomas Hiller (hiller@fzi.de)
+# "K42 may not work on 102 keys kbds, K29 seems to work"
+d1|de-102|german with mf2:\
+ :K29=#:\
+ :K42=#:\
+ :K45=<:S45=>:A45=|:\
+ :tc=germany:
+
+# from codrv, untested
+hv|holgi|Holgi's special MF1 keyboard mapping:\
+ :K1=<:S1=>:C1=|:\
+ :K29=#:S29=\136:A29=\\:C29=~:\
+ :tc=germany:
+
+# from codrv, untested
+# Contributed by Andreas Israel (ai@hrz.tu-chemnitz.de)
+de-ai|nixmf2|ct22|nix|nix7|German Nixdorf MF2:\
+ :A28=~:\
+ :K29=#:S29=':\
+ :K45=<:S45=>:A45=|:\
+ :tc=germany:
+
+# from codrv, untested
+dk|denmark|Danish mapping:\
+ :K1=\275:S1=\247:\
+ :S3=":A3=@:\
+ :A4=\243:\
+ :S5=\244:A5=$:\
+ :S7=&:S8=/:A8={:S9=(:A9=[:S10=):A10=]:S11==:A11=}:\
+ :K12=+:S12=\077:\
+ :K13=':S13=`:A13=|:p1#13:\
+ :K27=\345:S27=\305:\
+ :K28=\250:S28=\136:A28=~:p2#28:\
+ :K40=\346:S40=\306:\
+ :K41=\370:S41=\330:\
+ :K42=:S42=*:\
+ :K45=<:S45=>:A45=\\:\
+ :S53=;:S54=\072:\
+ :K55=-:S55=_:\
+ :l1#62:a0:
+
+# from codrv, untested
+# Finnish keyboard map with 7-bit versions of the national
+# chars. The Latin1 chars are available with Alt-7, Alt-8, etc
+# (where normally you would have the 7-bit ones).
+# Makes C/C++ programming more comfortable, since the 7-bit chars
+# (|\{[}]) are needed much more often than the Latin1 chars.
+# -- Petri.Wessman@hut.fi
+fi|finland|finland7b|finland-ascii|Finnish ASCII mapping:\
+ :l1#60:l2#62:\
+ :A8=\344:A9=\304:A10=\305:A11=\345:A12=\326:A13=\366:\
+ :K40=|:S40=\\:K41={:S41=[:K27=}:S27=]:\
+ :S1=\275:K1=\247:\
+ :S3=":A3=@:\
+ :A4=\243:\
+ :S5=$:A5=$:\
+ :S7=&:S8=/:S9=(:S10=):S11==:\
+ :K12=+:S12=\077:\
+ :K13=':S13=`:\
+ :K28=~:S28=\136:A28=~:\
+ :K29=':S29=*:\
+ :K45=<:S45=>:A45=|:\
+ :S53=;:S54=\072:\
+ :K55=-:S55=_:\
+ :a0:
+
+# from codrv, untested
+# Finnish keyboard map with the Latin1 national chars in
+# their "right" place. --Petri.Wessman@hut.fi
+f1|finland8b|finland-latin1|Finnish Latin1 mapping:\
+ :A8={:A9=[:A10=]:A11=}:A12=\\:\
+ :K40=\366:S40=\326:K41=\344:S41=\304:K27=\345:S27=\305:\
+ :tc=finland:
+
+
+# French keyboard mapping
+# From Matthieu Herrb <matthieu@laas.fr>
+# For 102 keys keyboards, produces 8 bits characters
+# with ISO Latin-1 encoding
+f8|france-iso-8859-1|French ISO 8859-1 102 keys keyboard:\
+ :l1#62:\
+ :K1=\262:S1=:\
+ :K2=&:S2=1:\
+ :K3=\351:S3=2:C3=\211:A3=~:\
+ :K4=":S4=3:A4=#:\
+ :K5=':S5=4:A5={:\
+ :K6=(:S6=5:A6=[:\
+ :K7=-:S7=6:C7=\036:A7=|:\
+ :K8=\350:S8=7:C8=\210:A8=`:\
+ :K9=_:S9=8:C9=\037:A9=\\:\
+ :K10=\347:S10=9:C10=\207:A10=\136:\
+ :K11=\340:S11=0:C11=\340:A11=@:\
+ :K12=):S12=\260:A12=]:\
+ :A13=}:\
+ :K17=a:S17=A:C17=\001:\
+ :K18=z:S18=Z:C18=\032:\
+ :D27:\
+ :K28=$:S28=\243:\
+ :K29=*:S29=\265:\
+ :K31=q:S31=Q:C31=\021:\
+ :K40=m:S40=M:C40=\015;\
+ :K41=\371:C41=\231:S41=%:\
+ :K42=*:S42=\265:\
+ :K46=w:S46=W:C46=\027:\
+ :K52=,:S52=?:\
+ :K53=;:S53=.:\
+ :K54=\072:S54=/:C54=\037\
+ :K55=!:S55=\266:
+
+# fr|france|French mapping:\
+# :de:
+#
+# from codrv, untested
+# f1|france120|French 120 mapping:\
+# :tc=belgium:
+#
+#
+# from codrv, untested
+#f2|france189|French 189 mapping:\
+# :S1=:A1=':p3#1:\
+# :A2=:A3=~:A5={:A6=[:A7=|:A8=`:\
+# :S9=_:A9=\\:A10=\136:A11=@:A12=]:A13=}:\
+# :A27=:A28=\244:A41=:A42=:\
+# :A45=:!:S45=\247:\
+# :tc=belgium:
+
+# From: Andy Duplain, duplain@rtf.bt.co.uk
+gb|greatbritain|British mapping for MF-2 keyboard:\
+ :S1=|:S3=":C3=2:C12=-:S41=@:K42=#:S42=~:C42=#:K45=\\:S45=|:C45=\\:
+
+# from codrv, untested
+# This entry has been corrected by Mike Trim (mtrim@crucible.demon.co.uk)
+# (hv's comment: For the keys # and ~ you might also check the following
+# line
+# :K42=#:S42=~:\
+# Also I think I was wrong with the ALTGR key. If you need one, add this:
+# :l1#62:
+g1|greatbritain166|British 166 mapping:\
+ :K1=`:S1=\254:A1=|:\
+ :S3=":S4=\243:\
+ :K41=':S41=@:\
+ :K29=#:S29=~:\
+ :K45=\\:S45=|:
+
+# from codrv, untested
+g2|greatbritain168|British 168 mapping:\
+ :K1=\\:S1=|:\
+ :S3=":S4=\243:S7=&:S8=:S9=(:S10=):A10=\261:\
+ :S11=#:A11=\260:S12=:K13=\254:S13=-:\
+ :K27=@:S27=`:\
+ :K28=[:S28={:A28=~:\
+ :K40=;:S40=+:\
+ :K41=\072:S41=*:A41=\136:\
+ :K42=]:S42=}:\
+ :K45=|:S45=_:\
+ :A52=\265:\
+ :l1#62:a0:
+
+# from codrv, untested
+is:iceland:Island mapping:\
+ :K1=\260:S1=\250:p1#1:\
+ :S2=":S7=&:S8=/:A8={:S9=(:A9=[:S10=):A10=]:S11==:A11=}:\
+ :K12=\\:S12=\326:\
+ :K13=':S13=`:A13=|:\
+ :A17=@:K27=\360:S27=\320:\
+ :K28=':S28=\077:A28=~:\
+ :K40=\346:S40=\306:\
+ :K41=':S41=':A41=\136:p2#41:\
+ :K42=+:S42=*:A42=`:p3#42:\
+ :K45=<:S45=>:A45=|:\
+ :S53=;:S54=\072:\
+ :K54=\376:S54=\336:\
+ :K104=,:104=,:\
+ :l1#62:a0:
+
+# from codrv, untested
+i1|italy141|Italian 141 mapping:\
+ :K1=\\:S1=|:\
+ :S3=":S4=\243:S7=&:S8=/:A8={:S9=(:A9=[:S10=):A10=]:S11==:A11=}:\
+ :K12=':A12=`:S12=\077:\
+ :K13=\354:S13=\136:A13=~:\
+ :K27=\350:S27=\351:\A27=[:\
+ :K28=+:S28=*:A28=]:\
+ :K40=\362:S40=\347:A40=@:\
+ :K41=\340:S41=\260:A41=#:\
+ :K29=\371:S29=\247:\
+ :K45=<:S45=>:\
+ :S53=;:S54=\072:\
+ :K55=-:S55=_:\
+ :l1#62:a0:
+
+# from codrv, untested
+i2|italy142|Italian 142 mapping:\
+ :A8={:A9=[:A10=]:A11=}:\
+ :A17=@:A27=:A28=~:A40=:A41=:\
+ :tc=italy141:a0:
+
+# from codrv, untested
+nl|netherlands|Dutch mapping:\
+ :K1=@:S1=\247:A1=\254:\
+ :A2=\271:S3=":A3=\262:S4=#:A4=\263:A5=\274:A6=\275:S7=&:\
+ :A7=\276:S8=_:A8=\243:S9=(:A9={:S10=):A10=}:S11=':\
+ :K12=/:S12=\077:A12=\\:\
+ :K13=\260:S13=~:A13=\270:p1#13:\
+ :K20=\266:K27=\250:S27=^:p2#27:\
+ :K28=*:S28=|:\
+ :K40=+:S40=\261:\
+ :K41=':S41=`:\
+ :K42=<:S42=>:\
+ :K45=[:S45=]:A45=|:\
+ :S53=;:S54=\072:K55=-:S55=_:\
+ :K104=,:S104=,:\
+ :l1#62:a0:
+
+# Norwegian keyboard map with national characters.
+# Anders Nordby, anders@fix.no
+no|norway|Norwegian 8bit mapping ISO 8859-1:\
+ :K1=\174:S1=\247:\
+ :K2=\61:S2=\41:\
+ :K3=\62:S3=\42:A3=\100:\
+ :K4=\63:S4=\43:A4=\243:\
+ :K5=\64:S5=\244:A5=\44:\
+ :K6=\65:S6=\45:\
+ :K7=\66:S7=\46:\
+ :K8=\67:S8=\57:A8=\173:\
+ :K9=\70:S9=\50:A9=\133:\
+ :K10=\71:S10=\51:A10=\135:\
+ :K11=\60:S11=\75:A11=\175:\
+ :K12=\53:S12=\77:\
+ :K13=\134:S13=\140:A13=\264:\
+ :K28=\250:S28=\136:A28=\176:\
+ :K29=\47:S29=\52:\
+ :K54=\56:S54=\72:\
+ :K53=\54:S53=\73:\
+ :K55=\55:S55=\137:\
+ :K41=\346:S41=\306:\
+ :K40=\370:S40=\330:\
+ :K27=\345:S27=\305:\
+ :l1#62:a0:
+
+# from codrv, untested
+pt|portugal|Portugesian mapping:\
+ :K1=\\:S1=|:\
+ :S3=":A3=@:A4=\243:A5=\247:S7=&:S8=/:A8={:S9=(:A9=[:\
+ :S10=):A10=]:S11=}:A11==:\
+ :K12=':S12=\077:\
+ :K13=\253:S13=\273:\
+ :K40=\347:S40=\307:\
+ :K41=\272:S41=\252:\
+ :K42=~:S42=^:p1#42:\
+ :K45=<:S45=>:\
+ :S53=;:S54=\072:K55=-:S55=_:\
+ :l1#62:a0:
+
+# from codrv, untested
+es|spain|Spainish mapping:\
+ :K1=\272:S1=\252:A1=\\:\
+ :A2=|:S3=":A3=@:S4=:A4=#:S7=&:A7=\254:S8=/:S9=(:S10=):S11==:\
+ :K12=':S12=\077:p1#12:\
+ :K13=\277:S13=\241:\
+ :K27=`:S27=^:A27=[:p2#27:\
+ :K28=+:S28=*:A28=]:\
+ :K40=\361:S41=\321:\
+ :K41=/:S41=\250:A41={:p3#41:\
+ :K42=\347:S42=\307:A42=}:\
+ :K45=<:S45=>:\
+ :S53=;:S54=\072:\
+ :K55=-:S55=_:\
+ :K104=,:S104=,:\
+ :l1#62:a0:
+
+# from codrv, untested
+# Contributed by Mats O Jansson, moj@stacken.kth.se
+# "Here is my map, rather large, but i want ALTGR normaly to be dead.
+# Only seven bit national characters have been placed where national characters
+# is on the keyboard."
+# We can help this man, just use the 'a0' capability -hv-
+s1|sweden1|Swedish mapping:\
+ :l1#62:a0:\
+ :D1:\
+ :K12=+:K13=':K29=':\
+ :S2=!:S3=":S4=#:S5=$:S6=%:S7=&:S8=/:\
+ :S9=(:S10=):S11=\075:S12=\077:S13=`:S29=*:\
+ :C2=:C3=\000:C4=\033:C5=\034:C6=\035:C7=:C8=:C9=:\
+ :C10=:C11=:C12=:C13=:C29=:\
+ :A3=\100:A5=$:A8={:\
+ :A9=[:A10=]:A11=}:A12=\134:\
+ :K27=]:K28=:\
+ :S27=}:S28=\136:\
+ :C27=\035:C28=:\
+ :A28=\176:\
+ :K40=\134:K41=[:\
+ :S40=|:S41={:\
+ :C40=\034:C41=\033:\
+ :K45=<:K53=,:K54=.:K55=-:\
+ :S45=>:S53=;:S54=\072:S55=_:\
+ :C45=\034:C53=:C54=:C55=:\
+ :A45=|:\
+ :C61=\000:
+
+# from codrv, untested
+# Contributed by Mats O Jansson, moj@stacken.kth.se
+# "Here is my map, rather large, but i want ALTGR normaly to be dead.
+# Only seven bit national characters have been placed where national
+# characters is on the keyboard."
+# We can help this man, just use the 'a0' capability -hv-
+# Corrected by Paul Pries, 5322@msg.abc.se (Some national shifts were
+# wrong).
+sa|sweden1a|Swedish 7bit mapping ISO 646:\
+ :l1#62:a0:\
+ :D1:\
+ :K12=+:K13=':K29=':\
+ :S2=!:S3=":S4=#:S5=$:S6=%:S7=&:S8=/:\
+ :S9=(:S10=):S11=\075:S12=\077:S13=`:S29=*:\
+ :C2=:C3=\000:C4=\033:C5=\034:C6=\035:C7=:C8=:C9=:\
+ :C10=:C11=:C12=:C13=:C29=:\
+ :A3=\100:A5=$:A8={:\
+ :A9=[:A10=]:A11=}:A12=\134:\
+ :K27=}:K28=:\
+ :S27=]:S28=\136:\
+ :C27=\035:C28=:\
+ :A28=\176:\
+ :S40=\134:S41=[:\
+ :K40=|:K41={:\
+ :C40=\034:C41=\033:\
+ :K45=<:K53=,:K54=.:K55=-:\
+ :S45=>:S53=;:S54=\072:S55=_:\
+ :C45=\034:C53=:C54=:C55=:\
+ :A45=|:\
+ :C61=\000:
+
+# from codrv, untested
+# Swedish keyboard map with national characters.
+# Paul Pries, 5322@msg.abc.se
+s2|sweden2|Swedish 8bit mapping ISO 8859-1:\
+ :l1#62:\
+ :K1=\247:S1=:\
+ :K12=+:K13=':K29=':\
+ :S2=!:S3=":S4=#:S5=$:S6=%:S7=&:S8=/:\
+ :S9=(:S10=):S11=\075:S12=\077:S13=`:S29=*:\
+ :C2=:C3=\000:C4=\033:C5=\034:C6=\035:C7=:C8=:C9=:\
+ :C10=:C11=:C12=:C13=:C29=:\
+ :A3=\100:A4=\234:A5=$:A8={:\
+ :A9=[:A10=]:A11=}:A12=\134:\
+ :K27=\206:K28=:\
+ :S27=\217:S28=\136:\
+ :C27=\035:C28=:\
+ :A28=\176:\
+ :S40=\231:S41=\216:\
+ :K40=\224:K41=\204:\
+ :C40=\034:C41=\033:\
+ :K45=<:K53=,:K54=.:K55=-:\
+ :S45=>:S53=;:S54=\072:S55=_:\
+ :C45=\034:C53=:C54=:C55=:\
+ :A45=|:\
+ :C61=\000:
+
+# Swedish keyboard map with national characters.
+# Paul Pries, 5322@msg.abc.se
+# Corrected by Mats O Jansson, moj@stacken.kth.se (Some national was CP850
+# and not ISO 8859-1 as said).
+s8|sweden8|Swedish 8bit mapping ISO 8859-1 and not CP850 national characters:\
+ :A8={:A9=[:A10=]:A11=}:A12=\\:\
+ :K40=\366:S40=\326:K41=\344:S41=\304:K27=\345:S27=\305:\
+ :tc=sweden2:
+
+#
+# tg: my idiosyncratic mappings (thomas@ghpc8.ihf.rwth-aachen.de)
+#
+# the six function keys above the cursor keys are arranged
+# identical to a real VT220:
+#
+# find insert remove
+# select up down
+#
+# since i don't have a use for the numbers on the keypad,
+# i map NumLock, /, *, - to PF1-PF4;
+# + is mapped to SS3 l, shifted + is mapped to SS3 m
+#
+# they convinced me finally to add some support for german umlauts.
+# so, i stole the mapping from jörg wunsch's de-prog entry.
+#
+# tg
+#
+
+tg:\
+ :l1#62:\
+ :A12=\337:\
+ :A27=\374:C27=\334:\
+ :A40=\366:C40=\326:\
+ :A41=\344:C41=\304:\
+ :K126=\032:C126=\003:\
+ :K75=[1~:S75=[1~:C75=[1~:\
+ :K76=[4~:S76=[4~:C76=[4~:\
+ :K80=[2~:S80=[2~:C80=[2~:\
+ :K81=[5~:S81=[5~:C81=[5~:\
+ :K85=[3~:S85=[3~:C85=[3~:\
+ :K86=[6~:S86=[6~:C86=[6~:\
+ :K90=OP:S90=OP:C90=OP:\
+ :K95=OQ:S95=OQ:C95=OQ:\
+ :K100=OR:S100=OR:C100=OR:\
+ :K104=On:S104=On:C104=On:\
+ :K105=OS:S105=OS:C105=OS:\
+ :K106=Ol:S106=Om:\
+ :K108=OM:S108=OM:C108=OM:
+
+us|usa|United States mapping:\
+ :de:
+
+# EOF
diff --git a/usr.sbin/pcvt/keycap/man5/keycap.5 b/usr.sbin/pcvt/keycap/man5/keycap.5
new file mode 100644
index 0000000..b5208661
--- /dev/null
+++ b/usr.sbin/pcvt/keycap/man5/keycap.5
@@ -0,0 +1,129 @@
+.\"
+.\" Copyright (c) 1992,1993,1994 Hellmuth Michaelis
+.\"
+.\" Copyright (c) 1990 The Regents of the University of California.
+.\"
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)keycap.5, 3.00, Last Edit-Date: [Sun Jan 2 13:45:59 1994]
+.\" $FreeBSD$
+.\"
+.Dd January 3, 1993
+.Dt KEYCAP 5
+.Os
+.Sh NAME
+.Nm keycap
+.Nd keyboard mapping data base
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+The
+.Nm
+file
+is a data base describing keyboard mappings, used by
+.Xr kcon 1 .
+.Pp
+Entries in
+.Nm
+consist of a number of `:'-separated fields.
+The first entry for each mapping gives the names that are known for the
+mapping, separated by `|' characters.
+All names but the first and last
+should be in lower case and contain no blanks;
+the last name may well contain
+upper case and blanks for readability.
+.Sh CAPABILITIES
+.Bl -column indent indent
+.Sy "Name Type Description
+.It "de bool Resets Keyboard mapping to compiled-in default"
+.It "D<n> bool Disables key <n> completely"
+.It "
+.It "m<n> num specify key numbers for ALT keys
+.It "l<n> num specify key numbers for ALTGR keys
+.It "h<n> num specify key numbers for SHIFT keys
+.It "t<n> num specify key numbers for CONTROL keys
+.It "ca<n> num specify key number for the CAPS LOCK key
+.It "sh<n> num specify key number for the SHIFT LOCK key
+.It "nl<n> num specify key number for the NUM LOCK key
+.It "sc<n> num specify key number for the SCROLL LOCK key
+.It "
+.It "K<n> str bind a string to 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 "
+.It "tc str Entry of similar map \- must be last."
+.El
+.Pp
+Parameter <n> describing the key number can have values from 1 to 128.
+.Pp
+A string parameter may have up to 15 characters.
+.Pp
+.Ss A Sample Entry
+The following entry, which describes a test entry, is among the very
+easy entries in the
+.Nm
+file as of this writing.
+.Pp
+.Bd -literal
+tt\||test\||Test entry which swaps y and z:\e
+ :K22=z:S22=Z:C22=\e032:\e
+ :K46=y:S46=Y:C46=\e031:
+.Ed
+.Pp
+Entries may continue onto multiple lines by giving a \e as the last
+character of a line.
+Comments may be included on lines beginning with
+.Dq # .
+.Sh FILES
+.Bl -tag -width /usr/share/misc/keycap.pcvt -compact
+.It Pa /usr/share/misc/keycap.pcvt
+File containing keyboard mapping descriptions.
+.El
+.Sh SEE ALSO
+.Xr kcon 1 ,
+.Xr keycap 3
+.Sh EXAMPLES
+The entry
+.Dq Li l1#60
+sets the keynumber for the ALTGR key to 60.
+.Pp
+The entry
+.Dq Li K100=hugo
+binds the string 'hugo' to the key number 100.
+.Pp
+The entry
+.Dq Li K100=^D
+binds the control character EOT (0x04) to the key number 100.
+.Pp
+The entry
+.Dq Li K100=\e000
+binds the control character NUL (0x00) to the key number 100.
diff --git a/usr.sbin/pcvt/loadfont/Makefile b/usr.sbin/pcvt/loadfont/Makefile
new file mode 100644
index 0000000..50646a7
--- /dev/null
+++ b/usr.sbin/pcvt/loadfont/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+PROG= loadfont
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pcvt/loadfont/loadfont.1 b/usr.sbin/pcvt/loadfont/loadfont.1
new file mode 100644
index 0000000..a66a530
--- /dev/null
+++ b/usr.sbin/pcvt/loadfont/loadfont.1
@@ -0,0 +1,87 @@
+.\" Copyright (c) 1992, 2000 Hellmuth Michaelis
+.\"
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" Last Edit-Date: [Mon Mar 27 17:07:57 2000]
+.\"
+.\" $FreeBSD$
+.\"
+.Dd March 27, 2000
+.Dt LOADFONT 1
+.Os
+.Sh NAME
+.Nm loadfont
+.Nd pcvt utility for loading fonts into VGA/EGA boards
+.Sh SYNOPSIS
+.Nm
+.Op Fl c Ar charsetno
+.Op Fl d Ar devicefile
+.Op Fl f Ar fontfilename
+.Op Fl i
+.Sh DESCRIPTION
+The
+.Nm
+utility is used to load fonts needed for proper operation of the pcvt
+VT220 driver on EGA and VGA boards into the font ram of this boards.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl c
+Specifies the slot, the font is to load into.
+EGA boards have four
+slots and VGA boards have eight slots available for downloading fonts.
+.It Fl d
+Specifies the devicefile to use.
+.It Fl f
+Specifies the file which contains the font to be downloaded.
+.It Fl i
+Gives information what type(s) of font do currently reside in which slot.
+This is also the default behaviour if no options are specified on the commandline.
+.El
+.Pp
+This utility is used only on EGA and VGA boards, as MDA, HCG and CGA boards
+do not have downloadable charactersets available.
+.Sh FILES
+The following fontfiles are available in the pcvt distribution:
+.Bd -literal
+/usr/share/misc/pcvtfonts/vt220l.808: 8x8 IBM II font
+/usr/share/misc/pcvtfonts/vt220h.808: 8x8 Extension font
+/usr/share/misc/pcvtfonts/vt220l.810: 8x10 IBM II font
+/usr/share/misc/pcvtfonts/vt220h.810: 8x10 Extension font
+/usr/share/misc/pcvtfonts/vt220l.814: 8x14 IBM II font
+/usr/share/misc/pcvtfonts/vt220h.814: 8x14 Extension font
+/usr/share/misc/pcvtfonts/vt220l.816: 8x16 IBM II font
+/usr/share/misc/pcvtfonts/vt220h.816: 8x16 Extension font
+.Ed
+.Sh EXAMPLES
+The command
+.Dq Li loadfont -c0 -f/usr/share/misc/pcvtfonts/vt220l.816
+loads a 8x16 font containing the standard IBM characterset II into font slot
+0 on a VGA or EGA board.
+.Sh BUGS
+No known bugs
+.Sh SEE ALSO
+.Xr cursor 1 ,
+.Xr scon 1 ,
+.Xr pcvt 4 ,
+.Xr ispcvt 8
diff --git a/usr.sbin/pcvt/loadfont/loadfont.c b/usr.sbin/pcvt/loadfont/loadfont.c
new file mode 100644
index 0000000..acdd013
--- /dev/null
+++ b/usr.sbin/pcvt/loadfont/loadfont.c
@@ -0,0 +1,306 @@
+/*
+ * Copyright (c) 1992, 1995 Hellmuth Michaelis
+ *
+ * Copyright (c) 1992, 1994 Brian Dunford-Shore
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by
+ * Hellmuth Michaelis and Brian Dunford-Shore
+ * 4. The name authors may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*---------------------------------------------------------------------------*
+ *
+ * load a font into VGA character font memory
+ * ------------------------------------------
+ *
+ * Last Edit-Date: [Mon Mar 27 17:09:25 2000];
+ *
+ * $FreeBSD$
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <err.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <machine/pcvt_ioctl.h>
+
+#define FONT8X8 2048 /* filesize for 8x8 font */
+#define HEIGHT8X8 8 /* 8 scan lines char cell height */
+#define SSCAN8X8 143 /* 400 scan lines on screen - 256 - 1 */
+
+#define FONT8X10 2560 /* filesize for 8x10 font */
+#define HEIGHT8X10 10 /* 10 scan lines char cell height */
+#define SSCAN8X10 143 /* 400 scan lines on screen - 256 - 1 */
+
+#define FONT8X14 3584 /* filesize for 8x14 font */
+#define HEIGHT8X14 14 /* 14 scan lines char cell height */
+#define SSCAN8X14 135 /* 392 scan lines on screen - 256 - 1 */
+
+#define FONT8X16 4096 /* filesize for 8x16 font */
+#define HEIGHT8X16 16 /* 16 scan lines char cell height */
+#define SSCAN8X16 143 /* 400 scan lines on screen - 256 - 1 */
+
+struct screeninfo screeninfo;
+
+#define DEFAULTFD 0
+int fd;
+
+
+main(argc,argv)
+int argc;
+char *argv[];
+{
+ FILE *in;
+ struct stat sbuf, *sbp;
+ unsigned char *fonttab;
+ int ret;
+ int chr_height;
+ int scr_scan;
+ int scr_rows;
+ int c;
+ int chr_set = -1;
+ char *filename;
+ int fflag = -1;
+ int info = -1;
+ int dflag = 0;
+ char *device;
+
+ while( (c = getopt(argc, argv, "c:d:f:i")) != -1)
+ {
+ switch(c)
+ {
+ case 'c':
+ chr_set = atoi(optarg);
+ break;
+
+ case 'd':
+ device = optarg;
+ dflag = 1;
+ break;
+
+ case 'f':
+ filename = optarg;
+ fflag = 1;
+ break;
+
+ case 'i':
+ info = 1;
+ break;
+
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if(chr_set == -1 || fflag == -1)
+ info = 1;
+
+ if(dflag)
+ {
+ if((fd = open(device, O_RDWR)) == -1)
+ err(1, "ERROR opening %s", device);
+ }
+ else
+ {
+ fd = DEFAULTFD;
+ }
+
+ if(info == 1)
+ {
+ int i;
+
+ if(ioctl(fd, VGAGETSCREEN, &screeninfo) == -1)
+ err(1, "ioctl VGAGETSCREEN failed");
+
+ switch(screeninfo.adaptor_type)
+ {
+ case UNKNOWN_ADAPTOR:
+ case MDA_ADAPTOR:
+ case CGA_ADAPTOR:
+ printf("Adaptor does not support Downloadable Fonts!\n");
+ break;
+ case EGA_ADAPTOR:
+ printheader();
+ for(i = 0;i < 4;i++)
+ {
+ printvgafontattr(i);
+ }
+ break;
+ case VGA_ADAPTOR:
+ printheader();
+ for(i = 0;i < 8;i++)
+ {
+ printvgafontattr(i);
+ }
+ }
+ printf("\n");
+ exit(0);
+ }
+
+ if(chr_set < 0 || chr_set > 7)
+ usage();
+
+ sbp = &sbuf;
+
+ if((in = fopen(filename, "r")) == NULL)
+ err(1, "cannot open file %s for reading", filename);
+
+ if((fstat(fileno(in), sbp)) != 0)
+ err(1, "cannot fstat file %s", filename);
+
+ switch(sbp->st_size)
+ {
+ case FONT8X8:
+ chr_height = HEIGHT8X8;
+ scr_scan = SSCAN8X8;
+ scr_rows = SIZ_50ROWS;
+ break;
+
+ case FONT8X10:
+ chr_height = HEIGHT8X10;
+ scr_scan = SSCAN8X10;
+ scr_rows = SIZ_40ROWS;
+ break;
+
+ case FONT8X14:
+ chr_height = HEIGHT8X14;
+ scr_scan = SSCAN8X14;
+ scr_rows = SIZ_28ROWS;
+ break;
+
+ case FONT8X16:
+ chr_height = HEIGHT8X16;
+ scr_scan = SSCAN8X16;
+ scr_rows = SIZ_25ROWS;
+ break;
+
+ default:
+ errx(1, "error, file %s is no valid font file, size=%d", argv[1], sbp->st_size);
+ }
+
+ if((fonttab = (unsigned char *)malloc((size_t)sbp->st_size)) == NULL)
+ errx(1, "error, malloc failed");
+
+ if((ret = fread(fonttab, sizeof(*fonttab), sbp->st_size, in)) != sbp->st_size)
+ errx(1, "error reading file %s, size = %d, read = is no valid font file, size=%d",
+ argv[1], sbp->st_size, ret);
+
+ loadfont(chr_set, chr_height, fonttab);
+ setfont(chr_set, 1, chr_height - 1, scr_scan, scr_rows);
+
+ exit(0);
+}
+
+setfont(charset, fontloaded, charscan, scrscan, scrrow)
+int charset, fontloaded, charscan, scrscan, scrrow;
+{
+ struct vgafontattr vfattr;
+
+ vfattr.character_set = charset;
+ vfattr.font_loaded = fontloaded;
+ vfattr.character_scanlines = charscan;
+ vfattr.screen_scanlines = scrscan;
+ vfattr.screen_size = scrrow;
+
+ if(ioctl(fd, VGASETFONTATTR, &vfattr) == -1)
+ err(1, "ioctl VGASETFONTATTR failed, error");
+}
+
+loadfont(fontset,charscanlines,font_table)
+int fontset;
+int charscanlines;
+unsigned char *font_table;
+{
+ int i, j;
+ struct vgaloadchar vlc;
+
+ vlc.character_set = fontset;
+ vlc.character_scanlines = charscanlines;
+
+ for(i = 0; i < 256; i++)
+ {
+ vlc.character = i;
+ for (j = 0; j < charscanlines; j++)
+ {
+ vlc.char_table[j] = font_table[j];
+ }
+ font_table += charscanlines;
+ if(ioctl(fd, VGALOADCHAR, &vlc) == -1)
+ err(1, "ioctl VGALOADCHAR failed, error");
+ }
+}
+
+printvgafontattr(charset)
+int charset;
+{
+ struct vgafontattr vfattr;
+ static int sizetab[] = { 25, 28, 35, 40, 43, 50 };
+
+ vfattr.character_set = charset;
+
+ if(ioctl(fd, VGAGETFONTATTR, &vfattr) == -1)
+ err(1, "ioctl VGAGETFONTATTR failed, error");
+ printf(" %d ",charset);
+ if(vfattr.font_loaded)
+ {
+
+ printf("Loaded ");
+ printf(" %2.2d ", sizetab[vfattr.screen_size]);
+ printf(" %2.2d ",
+ (((int)vfattr.character_scanlines) & 0x1f) + 1);
+ printf(" %3.3d",
+ ((int)vfattr.screen_scanlines+0x101));
+ }
+ else
+ {
+ printf("Empty");
+ }
+ printf("\n");
+}
+
+printheader()
+{
+ printf("\nEGA/VGA Charactersets Status Info:\n\n");
+ printf("Set Status Lines CharScanLines ScreenScanLines\n");
+ printf("--- ------ ----- ------------- ---------------\n");
+}
+
+usage()
+{
+ fprintf(stderr,"\nloadfont - load font into ega/vga font ram for pcvt video driver\n");
+ fprintf(stderr,"usage: loadfont -c <cset> -d <dev> -f <name> -i\n");
+ fprintf(stderr," -c <cset> characterset to load (ega 0..3, vga 0..7)\n");
+ fprintf(stderr," -d <dev> specify device\n");
+ fprintf(stderr," -f <name> filename containing binary font data\n");
+ fprintf(stderr," -i print status and types of loaded fonts (default)\n");
+ exit(1);
+}
diff --git a/usr.sbin/pcvt/scon/Makefile b/usr.sbin/pcvt/scon/Makefile
new file mode 100644
index 0000000..dc7d02a
--- /dev/null
+++ b/usr.sbin/pcvt/scon/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+PROG= scon
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pcvt/scon/scon.1 b/usr.sbin/pcvt/scon/scon.1
new file mode 100644
index 0000000..76a4c0f
--- /dev/null
+++ b/usr.sbin/pcvt/scon/scon.1
@@ -0,0 +1,220 @@
+.\" Copyright (c) 1992, 2000 Hellmuth Michaelis
+.\" Copyright (c) 1992, 1994 Joerg Wunsch
+.\"
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" Last Edit-Date: [Mon Mar 27 17:17:50 2000]
+.\"
+.\" $FreeBSD$
+.\"
+.Dd March 27, 2000
+.Dt SCON 1
+.Os
+.Sh NAME
+.Nm scon
+.Nd controls screen modes for pcvt video driver
+.Sh SYNOPSIS
+.Nm
+.Op Fl a
+.Op Fl c Ar screenno
+.Op Fl d Ar device
+.Op Fl f Ar on|off
+.Op Fl h
+.Op Fl H
+.Op Fl l
+.Op Fl m
+.Op Fl v
+.Op Fl V
+.Op Fl s Ar lines
+.Nm
+.Op Fl v
+.Op Fl d Ar device
+.Fl p Ar entry,red,green,blue
+.Nm
+.Op Fl v
+.Op Fl d Ar device
+.Fl p Ar default
+.Nm
+.Op Fl v
+.Op Fl d Ar device
+.Fl p Ar list
+.Nm
+.Op Fl v
+.Fl t Ar timeout
+.Nm
+.Op Fl v
+.Fl 1 | Fl 8
+.Sh DESCRIPTION
+The
+.Nm
+utility controls several aspects of the runtime behaviour of the pcvt vt220
+driver.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl a
+Returns a string describing the video adaptor found by pcvt, the string
+returned could be MDA, HGC, CGA, EGA, VGA or UNKNOWN.
+.It Fl c
+Specify the screen number the current (displayed) screen should be switched
+to.
+.It Fl d
+Specify the device filename (i.e. /dev/ttyv2) further operations specified on
+the command line should be applied to.
+.It Fl f
+Some programs which silently assume 24 lines when they run on a VT220 show
+incorrect behaviour when the terminal has really 25 lines.
+To support full
+VT220 behaviour, it is possible to force pcvt to select only 24 lines when
+it is running in 25-lines pure VT mode and/or in 28-lines HP-mode.
+The
+.Fl f
+option requires one additional parameter, the string 'on' or 'off' to switch
+this mode for a virtual screen on or off respectively.
+This mode has no effect
+if any other vertical resolutions are selected than the two above mentioned.
+.It Fl h
+Prints a usage/help text.
+.It Fl l
+Lists the current configuration of runtime changeable options and fixed
+parameters (such as the type of the adaptor, and in case of a VGA adaptor,
+the Manufacturer, Chipset and 132 column support) of the output portion
+of the pcvt driver.
+.It Fl m
+Returns a string describing the connected display monitor type found by pcvt,
+the string returned can be MONO, COLOR or UNKNOWN.
+.It Fl v
+Specify verbose operation of the program.
+.It Fl V
+Switch the specified/current screen into a pure VT220 mode without recognizing
+any HP escape sequences and without displaying function key labels.
+.It Fl H
+Switch the specified/current screen into a mixed HP/VT220 mode.
+That is, that
+in addition to the full VT220 emulation, the HP function key labels and the
+escape sequences for handling the labels are available to the user.
+.It Fl s
+Specify the number of character lines on the screen.
+Possible parameters are
+25, 28, 35, 40, 43 or 50. To use all this screen sizes, the fonts required
+for proper operation of a desired size have to be downloaded to the EGA/VGA
+font ram.
+This option is available only for EGA and VGA boards.
+.It Fl p
+Modify VGA palette
+(DAC).
+The
+.Fl p
+is mutually exclusive with
+.Fl s ,
+.Fl H ,
+and
+.Fl V .
+Naturally, option
+.Fl p
+is available only for VGA boards.
+Three flavors are available.
+.Pp
+If used with argument
+.Dq Ar default ,
+this flag will restore the default palette
+(as installed by VGA ROM BIOS after hardware reset).
+.Pp
+If used with argument
+.Dq Ar list ,
+the current VGA DAC palette entries are listed.
+Each entry contains
+the table index, values for red, green, and blue, and if there's a
+known name for this entry, the color name.
+Trailing empty table
+slots (RGB values all zero) are omitted.
+.Pp
+Otherwise, four comma-separated arguments are expected.
+The first
+denotes the number of palette entry to be modified.
+This may be either
+a number between 0 and 255, or the usual name of an associated color
+(case-insensitive).
+The following values for red, green and blue are restricted to 0 through 63
+due to VGA DAC conventions.
+Note that the first delimiter within such an argument may be a colon
+.Dq \&:
+instead of a comma
+.Dq \&,
+for better readability, but this violates common command argument
+conventions.
+Multiple
+.Fl p
+options may be specified if unambiguous.
+.It Fl t
+Specifying
+.Fl t
+will activate the screen saver.
+The behaviour depends on
+.Ar timeout :
+if
+.Ar timeout
+is given as 0, the screen saver is turned off.
+Otherwise,
+.Ar timeout
+is taken as a number of seconds to wait until activating the
+screen saver.
+NOTE: the
+.Fl t
+option is only available if screen saver support has been compiled into
+the driver!
+.It Fl 1
+Sets 132 columns mode
+(only available on VGA adaptors).
+.It Fl 8
+Sets 80 columns mode.
+.El
+.Pp
+When switching between HP and VT mode, when switching the force 24 lines
+mode on and off, or when switching between 80 and 132 columns operation,
+the screen is cleared, the scrolling
+region is reset and the cursor is placed in the home position.
+.Sh EXAMPLES
+The command
+.Dq Li scon Fl H s Ar 28
+places the current screen into HP mode and sets the screen size to 28x80.
+.Pp
+Invoking
+.Do
+.Li scon Fl p
+.Ar lightgray,0,15,0
+.Fl p
+.Ar 0:45,45,45
+.Dc
+will result in green on gray output for normal text.
+Note that normal text color is light gray, and not white as one might expect.
+.Sh BUGS
+the
+.Fl c
+and
+.Fl d
+options collide somehow, this will change in a future release.
+.Sh SEE ALSO
+.Xr cursor 1 ,
+.Xr loadfont 1 ,
+.Xr pcvt 4
diff --git a/usr.sbin/pcvt/scon/scon.c b/usr.sbin/pcvt/scon/scon.c
new file mode 100644
index 0000000..0d77f11
--- /dev/null
+++ b/usr.sbin/pcvt/scon/scon.c
@@ -0,0 +1,782 @@
+/*
+ * Copyright (c) 1992, 2000 Hellmuth Michaelis
+ *
+ * Copyright (c) 1992, 1994 Joerg Wunsch
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*---------------------------------------------------------------------------*
+ *
+ * scon - screen control utility for pcvt
+ * --------------------------------------
+ *
+ * Last Edit-Date: [Mon Mar 27 17:19:34 2000]
+ *
+ * $FreeBSD$
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <err.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <machine/pcvt_ioctl.h>
+
+#define DEFAULTFD 0
+
+int aflag = -1;
+int lflag = -1;
+int mflag = -1;
+int current = -1;
+int pflag = -1;
+int hflag = -1;
+int res = -1;
+char *device;
+int dflag = -1;
+int vflag = 0;
+int Pflag = 0;
+int tflag = 0;
+int fflag = -1;
+int colms = 0;
+char *onoff;
+
+unsigned timeout;
+struct screeninfo screeninfo;
+
+#define NVGAPEL 256
+
+struct rgb {
+ unsigned r, g, b;
+ int dothis;
+};
+
+static struct rgb palette[NVGAPEL] = {
+ { 0x00, 0x00, 0x00, 0}, /* 0 - black */
+ { 0x00, 0x00, 0x2a, 0}, /* 1 - blue */
+ { 0x00, 0x2a, 0x00, 0}, /* 2 - green */
+ { 0x00, 0x2a, 0x2a, 0}, /* 3 - cyan */
+ { 0x2a, 0x00, 0x00, 0}, /* 4 - red */
+ { 0x2a, 0x00, 0x2a, 0}, /* 5 - magenta */
+ { 0x2a, 0x2a, 0x00, 0}, /* 6 */
+ { 0x2a, 0x2a, 0x2a, 0}, /* 7 - lightgray */
+ { 0x00, 0x00, 0x15, 0}, /* 8 */
+ { 0x00, 0x00, 0x3f, 0}, /* 9 */
+ { 0x00, 0x2a, 0x15, 0}, /* 10 */
+ { 0x00, 0x2a, 0x3f, 0}, /* 11 */
+ { 0x2a, 0x00, 0x15, 0}, /* 12 */
+ { 0x2a, 0x00, 0x3f, 0}, /* 13 */
+ { 0x2a, 0x2a, 0x15, 0}, /* 14 */
+ { 0x2a, 0x2a, 0x3f, 0}, /* 15 */
+ { 0x00, 0x15, 0x00, 0}, /* 16 */
+ { 0x00, 0x15, 0x2a, 0}, /* 17 */
+ { 0x00, 0x3f, 0x00, 0}, /* 18 */
+ { 0x00, 0x3f, 0x2a, 0}, /* 19 */
+ { 0x2a, 0x15, 0x00, 0}, /* 20 - brown */
+ { 0x2a, 0x15, 0x2a, 0}, /* 21 */
+ { 0x2a, 0x3f, 0x00, 0}, /* 22 */
+ { 0x2a, 0x3f, 0x2a, 0}, /* 23 */
+ { 0x00, 0x15, 0x15, 0}, /* 24 */
+ { 0x00, 0x15, 0x3f, 0}, /* 25 */
+ { 0x00, 0x3f, 0x15, 0}, /* 26 */
+ { 0x00, 0x3f, 0x3f, 0}, /* 27 */
+ { 0x2a, 0x15, 0x15, 0}, /* 28 */
+ { 0x2a, 0x15, 0x3f, 0}, /* 29 */
+ { 0x2a, 0x3f, 0x15, 0}, /* 30 */
+ { 0x2a, 0x3f, 0x3f, 0}, /* 31 */
+ { 0x15, 0x00, 0x00, 0}, /* 32 */
+ { 0x15, 0x00, 0x2a, 0}, /* 33 */
+ { 0x15, 0x2a, 0x00, 0}, /* 34 */
+ { 0x15, 0x2a, 0x2a, 0}, /* 35 */
+ { 0x3f, 0x00, 0x00, 0}, /* 36 */
+ { 0x3f, 0x00, 0x2a, 0}, /* 37 */
+ { 0x3f, 0x2a, 0x00, 0}, /* 38 */
+ { 0x3f, 0x2a, 0x2a, 0}, /* 39 */
+ { 0x15, 0x00, 0x15, 0}, /* 40 */
+ { 0x15, 0x00, 0x3f, 0}, /* 41 */
+ { 0x15, 0x2a, 0x15, 0}, /* 42 */
+ { 0x15, 0x2a, 0x3f, 0}, /* 43 */
+ { 0x3f, 0x00, 0x15, 0}, /* 44 */
+ { 0x3f, 0x00, 0x3f, 0}, /* 45 */
+ { 0x3f, 0x2a, 0x15, 0}, /* 46 */
+ { 0x3f, 0x2a, 0x3f, 0}, /* 47 */
+ { 0x15, 0x15, 0x00, 0}, /* 48 */
+ { 0x15, 0x15, 0x2a, 0}, /* 49 */
+ { 0x15, 0x3f, 0x00, 0}, /* 50 */
+ { 0x15, 0x3f, 0x2a, 0}, /* 51 */
+ { 0x3f, 0x15, 0x00, 0}, /* 52 */
+ { 0x3f, 0x15, 0x2a, 0}, /* 53 */
+ { 0x3f, 0x3f, 0x00, 0}, /* 54 */
+ { 0x3f, 0x3f, 0x2a, 0}, /* 55 */
+ { 0x15, 0x15, 0x15, 0}, /* 56 - darkgray */
+ { 0x15, 0x15, 0x3f, 0}, /* 57 - lightblue */
+ { 0x15, 0x3f, 0x15, 0}, /* 58 - lightgreen */
+ { 0x15, 0x3f, 0x3f, 0}, /* 59 - lightcyan */
+ { 0x3f, 0x15, 0x15, 0}, /* 60 - lightred */
+ { 0x3f, 0x15, 0x3f, 0}, /* 61 - lightmagenta */
+ { 0x3f, 0x3f, 0x15, 0}, /* 62 - yellow */
+ { 0x3f, 0x3f, 0x3f, 0}, /* 63 - white */
+ { 0x00, 0x00, 0x00, 0} /* 64 ... - empty */
+};
+
+static struct colname {
+ const char *name;
+ unsigned idx;
+} colnames[] = {
+ {"black", 0},
+ {"blue", 1},
+ {"green", 2},
+ {"cyan", 3},
+ {"red", 4},
+ {"magenta", 5},
+ {"brown", 20},
+ {"lightgray", 7},
+ {"lightgrey", 7},
+ {"darkgray", 56},
+ {"darkgrey", 56},
+ {"lightblue", 57},
+ {"lightgreen", 58},
+ {"lightcyan", 59},
+ {"lightred", 60},
+ {"lightmagenta", 61},
+ {"yellow", 62},
+ {"white", 63},
+ /* must be terminator: */ {(const char *)NULL, 0}
+};
+
+
+static void parsepopt(char *arg, unsigned *idx,
+ unsigned *r, unsigned *g, unsigned *b);
+static void printpalette(int fd);
+
+main(argc,argv)
+int argc;
+char *argv[];
+{
+ int c;
+ int fd;
+
+ while( (c = getopt(argc, argv, "ac:d:f:HVlms:t:vp:18")) != -1)
+ {
+ switch(c)
+ {
+ case 'a':
+ aflag = 1;
+ break;
+
+ case 'l':
+ lflag = 1;
+ break;
+
+ case 'm':
+ mflag = 1;
+ break;
+
+ case 'c':
+ current = atoi(optarg);
+ break;
+
+ case 'd':
+ device = optarg;
+ dflag = 1;
+ break;
+
+ case 'f':
+ onoff = optarg;
+ fflag = 1;
+ break;
+
+ case 'V':
+ pflag = 1;
+ break;
+
+ case 'H':
+ hflag = 1;
+ break;
+
+ case 's':
+ if (!strncmp(optarg, "25", 2))
+ res = SIZ_25ROWS;
+ else if(!strncmp(optarg, "28", 2))
+ res = SIZ_28ROWS;
+ else if(!strncmp(optarg, "35", 2))
+ res = SIZ_35ROWS;
+ else if(!strncmp(optarg, "40", 2))
+ res = SIZ_40ROWS;
+ else if(!strncmp(optarg, "43", 2))
+ res = SIZ_43ROWS;
+ else if(!strncmp(optarg, "50", 2))
+ res = SIZ_50ROWS;
+ break;
+
+ case 'v':
+ vflag++;
+ break;
+
+ case 'p':
+ if(!strcmp(optarg, "list"))
+ {
+ if(Pflag)
+ errx(2, "-p list is mutual exclusive with other -p options");
+ Pflag = 3;
+ }
+ else if(!strcmp(optarg, "default"))
+ {
+ if(Pflag)
+ errx(2, "multiple -p default not allowed");
+ Pflag = 2;
+ } else {
+ unsigned idx, r, g, b;
+
+ if(Pflag > 1)
+ errx(2, "-p default and -p i,r,g,b ambiguous");
+ Pflag = 1;
+ parsepopt(optarg, &idx, &r, &g, &b);
+ if(idx >= NVGAPEL)
+ errx(2, "index %u in -p option out of range", idx);
+ palette[idx].r = r;
+ palette[idx].g = g;
+ palette[idx].b = b;
+ palette[idx].dothis = 1;
+ }
+ break;
+
+ case 't':
+ tflag++;
+ timeout = atoi(optarg);
+ break;
+
+ case '1':
+ colms = 132;
+ break;
+
+ case '8':
+ colms = 80;
+ break;
+
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if((pflag == 1) && (hflag == 1))
+ usage();
+
+ if(dflag == -1 && lflag == -1 && current == -1 && pflag == -1 &&
+ hflag == -1 && res == -1 && Pflag == 0 && tflag == 0 && fflag == -1
+ && colms == 0 && mflag == -1)
+ {
+ lflag = 1;
+ }
+
+ if(dflag == -1)
+ {
+ if(vflag)
+ printf("using current device\n");
+ fd = DEFAULTFD; /* -hm, Feb 12 1993 */
+ }
+ else
+ {
+ if((fd = open(device, O_RDWR)) == -1)
+ err(1, "ERROR opening %s", device);
+ if(vflag)
+ printf("using device %s\n",device);
+ }
+
+ if(aflag == 1) /* return adaptor type */
+ {
+ printadaptor(fd);
+ exit(0);
+ }
+
+ if(mflag == 1) /* return monitor type */
+ {
+ printmonitor(fd);
+ exit(0);
+ }
+
+ if(lflag == 1) /* list information */
+ {
+ if(vflag)
+ printf("processing option -l, listing screen info\n");
+ printinfo(fd);
+ exit(0);
+ }
+
+ if(tflag) /* set screen saver timeout */
+ {
+ if(vflag)
+ {
+ printf(
+ "processing option -t, setting screen saver timeout: "
+ );
+ if(timeout)
+ printf("new timeout = %d s\n", timeout);
+ else
+ printf("turned off\n");
+ }
+
+ if(ioctl(fd, VGASCREENSAVER, &timeout) < 0)
+ {
+ warn("ioctl(VGASCREENSAVER)");
+ fprintf(stderr, "Check the driver, the screensaver is probably not compiled in!\n");
+ exit(2);
+ }
+ goto success;
+ }
+
+ if(colms)
+ {
+ if(vflag)
+ printf("Setting number of columns to %d\n", colms);
+ if(ioctl(fd, VGASETCOLMS, &colms) < 0)
+ err(2, "ioctl(VGASETCOLMS)");
+ goto success;
+ }
+
+ if(Pflag == 3)
+ {
+ /* listing VGA palette */
+ if(vflag)
+ printf("processing option -p list, "
+ "listing VGA palette\n");
+
+ printpalette(fd);
+ goto success;
+ }
+
+ if(Pflag)
+ {
+ unsigned int idx;
+
+ /* setting VGA palette */
+ if(vflag)
+ printf("processing option -p, setting VGA palette%s\n",
+ Pflag == 2? " to default": "");
+
+ for(idx = 0; idx < NVGAPEL; idx++)
+ if(Pflag == 2 || palette[idx].dothis)
+ {
+ struct vgapel p;
+ p.idx = idx;
+ p.r = palette[idx].r;
+ p.g = palette[idx].g;
+ p.b = palette[idx].b;
+ if(ioctl(fd, VGAWRITEPEL, (caddr_t)&p) < 0)
+ err(2, "ioctl(fd, VGAWRITEPEL)");
+ }
+ goto success;
+ }
+
+ screeninfo.screen_no = -1; /* We are using fd */
+ screeninfo.current_screen = current;
+ screeninfo.pure_vt_mode = -1;
+ screeninfo.screen_size = res;
+ screeninfo.force_24lines = -1;
+
+ if(current != -1) /* set current screen */
+ {
+ if(vflag)
+ printf("processing option -c, setting current screen to %d\n",current);
+
+ if(ioctl(1, VGASETSCREEN, &screeninfo) == -1)
+ err(1, "ioctl VGASETSCREEN failed");
+ exit(0);
+ }
+
+ if(pflag == 1)
+ {
+ if(vflag)
+ printf("processing option -V, setting emulation to pure VT220\n");
+ screeninfo.pure_vt_mode = M_PUREVT;
+ }
+ else if(hflag == 1)
+ {
+ if(vflag)
+ printf("processing option -H, setting emulation to VT220 + HP Labels\n");
+ screeninfo.pure_vt_mode = M_HPVT;
+ }
+ else
+ {
+ if(vflag)
+ printf("no change in terminal emulation\n");
+ }
+
+ if(vflag)
+ {
+ if(res == -1)
+ printf("no change in screen resolution\n");
+ else if(res == SIZ_25ROWS)
+ printf("change screen resolution to 25 lines\n");
+ else if(res == SIZ_28ROWS)
+ printf("change screen resolution to 28 lines\n");
+ else if(res == SIZ_35ROWS)
+ printf("change screen resolution to 35 lines\n");
+ else if(res == SIZ_40ROWS)
+ printf("change screen resolution to 40 lines\n");
+ else if(res == SIZ_43ROWS)
+ printf("change screen resolution to 43 lines\n");
+ else if(res == SIZ_50ROWS)
+ printf("change screen resolution to 50 lines\n");
+ }
+
+ if(fflag == 1) /* force 24 lines on/off */
+ {
+ if(!strcmp(onoff, "on"))
+ {
+ fflag = 1;
+ }
+ else if(!strcmp(onoff, "off"))
+ {
+ fflag = 0;
+ }
+ else
+ {
+ fprintf(stderr,"you must specify 'on' or 'off' with -f option!\n");
+ exit(1);
+ }
+ }
+ screeninfo.force_24lines = fflag;
+
+ if(ioctl(fd, VGASETSCREEN, &screeninfo) == -1)
+ err(1, "ioctl VGASETSCREEN failed");
+success:
+ if(vflag)
+ printf("successful execution of ioctl VGASETSCREEN!\n");
+ exit(0);
+}
+
+usage()
+{
+ fprintf(stderr,"\nscon - screen control utility for the pcvt video driver\n");
+ fprintf(stderr,"usage: scon -a -l -m -v -c [n] -d [dev] -f [on|off] -V -H -s [n]\n");
+ fprintf(stderr,"usage: scon -p [default | list | i,r,g,b] | -t [sec] | -1 | -8\n");
+ fprintf(stderr," -a list video adaptor type (MDA,CGA,EGA or VGA)\n");
+ fprintf(stderr," -c <screen no> switch current virtual screen to <screen no>\n");
+ fprintf(stderr," -d <device> set parameters(-V|-H|-s) for virtual device\n");
+ fprintf(stderr," -f <on|off> force 24 lines in VT 25 lines and HP 28 lines mode\n");
+ fprintf(stderr," -H set VT220/HP emulation mode for a virtual screen\n");
+ fprintf(stderr," -l list current parameters for a virtual screen\n");
+ fprintf(stderr," -m report monitor type (MONO/COLOR)\n");
+ fprintf(stderr," -p default set default VGA palette\n");
+ fprintf(stderr," -p list list current VGA palette\n");
+ fprintf(stderr," -p <i,r,g,b> set VGA palette entry i to r/g/b\n");
+ fprintf(stderr," -p <name,r,g,b> set VGA palette entry for color name to r/g/b\n");
+ fprintf(stderr," -s <lines> set 25, 28, 35, 40, 43 or 50 lines for a virtual screen\n");
+ fprintf(stderr," -t <timeout> set screen saver timeout [seconds]\n");
+ fprintf(stderr," -1 set 132 columns mode\n");
+ fprintf(stderr," -8 set 80 columns mode\n");
+ fprintf(stderr," -v verbose mode\n");
+ fprintf(stderr," -V set pure VT220 emulation for a virtual screen\n");
+ fprintf(stderr," -? display help (this message)\n\n");
+ exit(1);
+}
+
+printadaptor(fd)
+int fd;
+{
+ if(ioctl(fd, VGAGETSCREEN, &screeninfo) == -1)
+ err(1, "ioctl VGAGETSCREEN failed");
+ switch(screeninfo.adaptor_type)
+ {
+ default:
+ case UNKNOWN_ADAPTOR:
+ printf("UNKNOWN\n");
+ break;
+
+ case MDA_ADAPTOR:
+ printf("MDA\n");
+ break;
+
+ case CGA_ADAPTOR:
+ printf("CGA\n");
+ break;
+
+ case EGA_ADAPTOR:
+ printf("EGA\n");
+ break;
+
+ case VGA_ADAPTOR:
+ printf("VGA\n");
+ break;
+ }
+}
+
+printmonitor(fd)
+int fd;
+{
+ if(ioctl(fd, VGAGETSCREEN, &screeninfo) == -1)
+ err(1, "ioctl VGAGETSCREEN failed");
+ switch(screeninfo.monitor_type)
+ {
+ default:
+ printf("UNKNOWN\n");
+ break;
+
+ case MONITOR_MONO:
+ printf("MONO\n");
+ break;
+
+ case MONITOR_COLOR:
+ printf("COLOR\n");
+ break;
+ }
+}
+
+char *vga_type(int number)
+{
+ static char *vga_tab[] = {
+ "Generic VGA",
+ "ET4000",
+ "ET3000",
+ "PVGA1A",
+ "WD90C00",
+ "WD90C10",
+ "WD90C11",
+ "VIDEO 7 VEGA",
+ "VIDEO 7 FAST",
+ "VIDEO 7 VER5",
+ "VIDEO 7 1024I",
+ "Unknown VIDEO 7",
+ "TVGA 8800BR",
+ "TVGA 8800CS",
+ "TVGA 8900B",
+ "TVGA 8900C",
+ "TVGA 8900CL",
+ "TVGA 9000",
+ "TVGA 9100",
+ "TVGA 9200",
+ "Unknown TRIDENT",
+ "S3 80C911",
+ "S3 80C924",
+ "S3 80C801/80C805",
+ "S3 80C928",
+ "Unknown S3",
+ "CL-GD5402",
+ "CL-GD5402r1",
+ "CL-GD5420",
+ "CL-GD5420r1",
+ "CL-GD5422",
+ "CL-GD5424",
+ "CL-GD5426",
+ "CL-GD5428",
+
+ };
+ return(vga_tab[number]);
+}
+
+char *vga_family(int number)
+{
+ static char *vga_tab[] = {
+ "Generic VGA",
+ "Tseng Labs",
+ "Western Digital",
+ "Video Seven",
+ "Trident",
+ "S3 Incorporated",
+ "Cirrus Logic",
+ };
+ return(vga_tab[number]);
+}
+
+printinfo(fd)
+int fd;
+{
+ if(ioctl(fd, VGAGETSCREEN, &screeninfo) == -1)
+ err(1, "ioctl VGAGETSCREEN failed");
+
+ printf( "\nVideo Adaptor Type = ");
+
+ switch(screeninfo.adaptor_type)
+ {
+ default:
+ case UNKNOWN_ADAPTOR:
+ printf("UNKNOWN Video Adaptor\n");
+ break;
+
+ case MDA_ADAPTOR:
+ printf("MDA - Monochrome Display Adaptor\n");
+ break;
+
+ case CGA_ADAPTOR:
+ printf("CGA - Color Graphics Adaptor\n");
+ break;
+
+ case EGA_ADAPTOR:
+ printf("EGA - Enhanced Graphics Adaptor\n");
+ break;
+
+ case VGA_ADAPTOR:
+ printf("VGA - Video Graphics Adaptor/Array\n");
+ printf(" VGA Chipset Manufacturer = %s\n",
+ vga_family(screeninfo.vga_family));
+ printf(" VGA Chipset Type = %s\n",
+ vga_type(screeninfo.vga_type));
+ printf(" Support for 132 Column Mode = %s\n",
+ screeninfo.vga_132 ? "Yes" : "No");
+ break;
+ }
+
+ printf( "Display Monitor Type = ");
+
+ switch(screeninfo.monitor_type)
+ {
+ default:
+ printf("UNKNOWN Monitor Type\n");
+ break;
+
+ case MONITOR_MONO:
+ printf("Monochrome Monitor\n");
+ break;
+
+ case MONITOR_COLOR:
+ printf("Color Monitor\n");
+ break;
+ }
+
+ printf( "Number of Downloadable Fonts = %d\n",screeninfo.totalfonts);
+ printf( "Number of Virtual Screens = %d\n",screeninfo.totalscreens);
+ printf( "Info Request Screen Number = %d\n",screeninfo.screen_no);
+ printf( "Current Displayed Screen = %d\n",screeninfo.current_screen);
+
+ if(screeninfo.pure_vt_mode == M_PUREVT)
+ printf( "Terminal Emulation Mode = VT220\n");
+ else
+ printf( "Terminal Emulation Mode = VT220 with HP Features\n");
+
+ printf( "Lines = ");
+
+ switch(screeninfo.screen_size)
+ {
+ case SIZ_25ROWS:
+ printf( "25\n");
+ break;
+
+ case SIZ_28ROWS:
+ printf( "28\n");
+ break;
+
+ case SIZ_35ROWS:
+ printf( "35\n");
+ break;
+
+ case SIZ_40ROWS:
+ printf( "40\n");
+ break;
+
+ case SIZ_43ROWS:
+ printf( "43\n");
+ break;
+
+ case SIZ_50ROWS:
+ printf( "50\n");
+ break;
+
+ default:
+ printf( "UNKNOWN\n");
+ break;
+ }
+ printf( "Force 24 Lines = %s",
+ screeninfo.force_24lines ? "Yes" : "No");
+
+ printf("\n\n");
+}
+
+static const char *findname(unsigned idx)
+{
+ /* try to find a name for palette entry idx */
+ /* if multiple names exist, returns first matching */
+ register struct colname *cnp;
+
+ for(cnp = colnames; cnp->name; cnp++)
+ if(cnp->idx == idx)
+ return cnp->name;
+
+ /* not found */
+ return (const char *)NULL;
+}
+
+static void printpalette(int fd)
+{
+ register unsigned idx, last;
+
+ for(idx = 0; idx < NVGAPEL; idx++)
+ {
+ struct vgapel p;
+ p.idx = idx;
+ if(ioctl(fd, VGAREADPEL, &p) < 0)
+ err(2, "ioctl(VGAREADPEL)");
+ palette[idx].r = p.r;
+ palette[idx].g = p.g;
+ palette[idx].b = p.b;
+ }
+
+ /* find last non-empty entry */
+ for(last = NVGAPEL - 1; last; last--)
+ if(palette[last].r || palette[last].g || palette[last].b)
+ break;
+
+ if(last != NVGAPEL - 1)
+ last++;
+
+ /* now, everything's collected. print out table */
+ printf("VGA palette status\n");
+ printf("index red green blue name\n");
+ for(idx = 0; idx < last; idx++)
+ {
+ const char *cp;
+ printf("%5d %5d %5d %5d",
+ idx, palette[idx].r, palette[idx].g, palette[idx].b);
+ if(cp = findname(idx))
+ printf(" %s\n", cp);
+ else
+ putchar('\n');
+ }
+ putchar('\n');
+}
+
+
+static void parsepopt(char *arg, unsigned *idx,
+ unsigned *r, unsigned *g, unsigned *b)
+{
+ char firstarg[21];
+ register unsigned i;
+
+ if(sscanf(arg, "%20[a-zA-Z0-9]%*[,:]%u,%u,%u", firstarg, r, g, b) < 4
+ || strlen(firstarg) == 0)
+ errx(2, "too few args in -p i,r,g,b");
+
+ if(firstarg[0] >= '0' && firstarg[0] <= '9') {
+ *idx = strtoul(firstarg, NULL, 10);
+ return;
+ }
+
+ for(i = 0; colnames[i].name; i++)
+ if(strcasecmp(colnames[i].name, firstarg) == 0) {
+ *idx = colnames[i].idx;
+ return;
+ }
+ errx(2, "arg ``%s'' in -p option not recognized", firstarg);
+}
diff --git a/usr.sbin/pcvt/userkeys/Makefile b/usr.sbin/pcvt/userkeys/Makefile
new file mode 100644
index 0000000..66fe022
--- /dev/null
+++ b/usr.sbin/pcvt/userkeys/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+PROG= vt220keys
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pcvt/userkeys/vt220keys.1 b/usr.sbin/pcvt/userkeys/vt220keys.1
new file mode 100644
index 0000000..1da8544
--- /dev/null
+++ b/usr.sbin/pcvt/userkeys/vt220keys.1
@@ -0,0 +1,175 @@
+.\" $FreeBSD$
+.\"
+.Dd January 16, 2001
+.Dt VT220KEYS 1
+.Os
+.Sh NAME
+.Nm vt220keys
+.Nd "define SHIFTED function keys on VT220 terminal"
+.Sh SYNOPSIS
+.Nm
+.Op Fl cil
+.Op Ar keyname keystring ...
+.Sh DESCRIPTION
+The
+.Nm
+utility sets up a
+.Dq "vt220 terminal"
+in vt200 mode to allow user
+definition of the SHIFTED function keys.
+Each
+.Ar keyname
+specified on the command line will be loaded with
+the corresponding
+.Ar keystring .
+A
+.Ar keyname
+is one of the following words:
+.Cm F6 F7 F8 F9 F10 F11 ESC
+.Cm F12 BS F13 LF F14 HELP
+.Cm DO F17 F18 F19 F20 .
+.Ar Keystrings
+must be quoted if spaces, tabs, or shell metacharacters are included.
+.Pp
+The
+.Nm
+utility expects to receive some combination of option flags and/or
+argument pair(s), otherwise a usage message
+is printed.
+.Pp
+The options are:
+.Bl -tag -width indent
+.It Fl c
+Clears all SHIFTED function key definitions before setting them to user
+defined strings.
+.It Fl i
+Read the initialization file
+.Pa $HOME/.vt220rc
+for SHIFTED function key definitions.
+This is done before any
+argument pair specified on the command line is processed.
+Each line in the file must consist of two fields (separated by spaces
+or tabs) where the first field is the
+.Ar keyname
+and the second field is the
+.Ar keystring .
+The second field extends to the end of the line, thus a
+.Ar keystring
+may include spaces or tabs.
+A newline (return) may be specified
+within the string by using the C Language notation for newline (\\n).
+.It Fl l
+Locks the function keys from further definition.
+Locking occurs after processing the initialization file (if the
+.Fl i
+option is specified) and any argument
+pairs.
+The only way
+to unlock is by turning the power off.
+.El
+.Sh EXAMPLES
+.Bd -literal
+vt220keys -ci
+vt220keys F6 'nroff -ms '
+vt220keys -i F20 'cc -O -c '
+vt220keys -l HELP man
+.Ed
+.Sh "OTHER FEATURES"
+Pressing the function keys without using the shift key, generates
+a string of characters.
+With
+.Xr csh 1
+this string can be aliased to some command.
+For example:
+.Pp
+.Dl alias\ ^[[17~\ "ls\ -CR\ |\ more"
+.Pp
+where
+.Ql "^[[17~"
+is what is generated by pressing the F6 key.
+Therefore
+F6 can perform two commands, depending if pressed with/without the SHIFT
+key.
+.Pp
+The
+.Nm
+utility can be called from your
+.Pa .login
+or
+.Pa .profile
+file.
+Typically an user
+will create a initialization file and include a line like
+.Pp
+.Dl "vt220keys -ci"
+OR
+.Dl "vt220keys -cil"
+.Pp
+in the above mentioned files.
+This way the SHIFTED function keys
+will be set to your favorite commands when logging in.
+.Sh CAVEATS
+If the SHIFTED function keys are unlocked, redefinition of a SHIFTED
+function key will rewrite the old string.
+.Pp
+There are 256 bytes available for the SHIFTED function keys.
+Space is
+supplied on a first\-come/first\-serve basis.
+After the 256 bytes are
+used, you can't define any more keys unless space is cleared.
+This
+can be done by redefining a key to contain a string of fewer bytes.
+.Pp
+All key definitions are stored in volatile RAM, and are lost when
+terminal power is lost.
+.Pp
+The ESC key (unshifted) no longer generates the proper escape character.
+This
+is of particular importance since many editors require use of the
+ESC key.
+Here are some available alternatives:
+.Bl -bullet
+.It
+The escape character can be generated by typing
+.Ql ^[
+(control\-[).
+.It
+Use
+.Nm
+as follows (note
+.Ql ^[
+is control\-[)
+.Pp
+.Dl "vt220keys ESC '^['"
+.Pp
+This will require you
+to press the SHIFT key and ESC to generate the escape sequence.
+.It
+Some editors, allow other character(s) to be substituted for the
+escape character.
+For example with
+.Xr emacs 1
+include this line in your
+.Pa .emacs_pro :
+.Pp
+.Dl (bind-to-key\ "ESC-prefix"\ "\\033[23~")
+.Pp
+Thus when the ESC key is pressed,
+.Nm emacs
+will allow the characters generated
+.Pq Li ^[[23~
+to perform the same function as the escape
+character.
+.El
+.Sh FILES
+.Bl -tag -width $HOME/.vt220rc
+.It Pa $HOME/.vt220rc
+initialization file
+.El
+.Sh SEE ALSO
+.Rs
+.%B "VT220 Programmer Reference Manual"
+.Re
+.Rs
+.%B "VT220 Programmer Pocket Guide"
+.Re
diff --git a/usr.sbin/pcvt/userkeys/vt220keys.c b/usr.sbin/pcvt/userkeys/vt220keys.c
new file mode 100644
index 0000000..e3fe90c
--- /dev/null
+++ b/usr.sbin/pcvt/userkeys/vt220keys.c
@@ -0,0 +1,287 @@
+/*
+ * Trivial program to load VT220 Function keys with strings,
+ * note that the values only get sent when the key is shifted
+ * (shoulda been an option to flip the shift set like the Z19!)
+ *
+ * Typing no args gives help, basically pairs of keyname/value
+ * strings.
+ *
+ * Author, Author: Barry Shein, Boston University
+ *
+ * HISTORY
+ {1} 30-Oct-85 Kenneth J. Lester (ken) at ektools
+
+ Added the necessary code to read an initialization file. This
+ should make it easier to used this program. Also added code
+ that will set-up the terminal in vt200 (this saves the user the
+ trouble of checking if the set-up is in vt200).
+
+ Restructed the main function to use getopt, for argument
+ processing.
+
+ Alterated usage function to include new "i" option (init file)
+
+
+ -hm minor modifications for pcvt 2.0 release
+
+$FreeBSD$
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+
+/*
+ * The default toupper() macro is stupid, will toupper anything
+ */
+
+#ifdef toupper
+#undef toupper
+#endif
+#define toupper(c) (islower(c) ? ((c)-' ') : c)
+
+#define VT200_7BIT 1
+#define ESC 033
+#define INITFILE ".vt220rc"
+
+struct keynames {
+ char *name ;
+ char *string ;
+} keys[] = {
+ "F6", "17",
+ "F7", "18",
+ "F8", "19",
+ "F9", "20",
+ "F10", "21",
+ "F11", "23",
+ "ESC", "23",
+ "F12", "24",
+ "BS", "24",
+ "F13", "25",
+ "LF", "25",
+ "F14", "26",
+ "HELP", "28",
+ "DO", "29",
+ "F17", "31",
+ "F18", "32",
+ "F19", "33",
+ "F20", "34",
+ NULL, NULL
+};
+
+char prog[BUFSIZ];
+
+main(argc,argv)
+ int argc;
+ char *argv[];
+{
+ int option; /* option character returned by getopt */
+ int initf = 0; /* read initialization file */
+ int lockf = 0; /* lock keys after loading strings */
+ int clearf = 0; /* clear all keys before loading strings */
+
+ strlcpy(prog, *argv, sizeof(prog)); /* store program name */
+
+ if(argc == 1) usage(); /* program requires options */
+
+ /* get options */
+ while ((option = getopt(argc, argv, "cli")) != -1)
+ switch(option)
+ {
+ case 'c' :
+ clearf++;
+ break;
+ case 'l' :
+ lockf++;
+ break;
+ case 'i' :
+ initf++;
+ break;
+ case '?' :
+ usage();
+ }
+
+ if (VT200_7BIT)
+ printf("\033[62;1\"p"); /* vt200 7 bits */
+ else
+ printf("\033[62;2\"p"); /* vt200 8 bits */
+
+ if(clearf) clearkeys();
+
+ if (initf) getinit();
+
+ /* process {key, key string} pairs. Note optind is index to argv
+ for first pair. By adding 1 to optind insures that a pair exists
+ i.e. the last key has a key string. */
+
+ while(optind + 1 < argc)
+ {
+ dokey(argv[optind], argv[optind+1]);
+ optind += 2;
+ }
+
+ if(lockf) lockkeys();
+
+ exit(0);
+}
+
+/****************************************************************************/
+
+/*
+ * Load the VT220 SHIFT-FNKEY value, the basic pattern is
+ * "\EP1;1|"+KEYNAME+"/"+VAL_AS_HEX+"\E\\"
+ * that is, literally what is in quotes (w/o quotes) then the
+ * name of the key from the keytable above (a numeric string)
+ * then a slash, then the string value as hex pairs then ESC-BACKSLASH
+ *
+ * Note: you can gang together key defns with semicolons but that
+ * would complicate things, especially error handling, so do it all
+ * for each pair, who cares, really.
+ */
+
+dokey(nm,val) char *nm, *val;
+{
+ register char *scr;
+ register struct keynames *kp;
+
+ for(scr = nm; *scr = toupper(*scr); scr++)
+ ;
+ for(kp = keys; kp->name != NULL; kp++)
+ if(strcmp(nm,kp->name) == 0) {
+ printf("%cP1;1|%s/",ESC,kp->string);
+ while(*val) printf("%02x",*val++);
+ printf("%c\\",ESC);
+ fflush(stdout);
+ return;
+ }
+ fprintf(stderr,"Bad key name: %s\n",nm);
+ usage(); /* bad key name, give up */
+}
+
+/****************************************************************************/
+
+clearkeys()
+{
+ printf("%cP0;1|%c\\",ESC,ESC);
+ fflush(stdout);
+}
+
+/****************************************************************************/
+
+lockkeys()
+{
+ printf("%cP1;0|%c\\",ESC,ESC);
+ fflush(stdout);
+}
+
+/****************************************************************************/
+
+usage()
+{
+ int i;
+
+ fprintf(stderr,"usage: %s [-cil] [keyname string keyname string...]\n\n",prog);
+ fprintf(stderr,"The following options are available\n");
+ fprintf(stderr,"\t-c\tclears keys first\n");
+ fprintf(stderr,"\t-l\t[sets then] locks further setting\n");
+ fprintf(stderr,"\t-i\tfirst read initialization file $HOME/%s\n",INITFILE);
+ fprintf(stderr,"(note that the only way to unlock is via Set-Up)\n\n");
+ fprintf(stderr,"Keyname is one of:\n\t");
+ for(i=0; keys[i].name != NULL; i++)
+ fprintf(stderr,"%s ",keys[i].name);
+ fprintf(stderr,"\nKeyname is SHIFTED function key that sends the string\n\n");
+ fprintf(stderr,"Strings may need quoting to protect from shell\n");
+ fprintf(stderr,"You must specify an option or key,string pairs\n\n");
+ exit(1);
+}
+
+/****************************************************************************/
+
+/* This routine process the INITFILE. This file expects lines in the format
+
+ <ws> keyname ws string
+
+ Where ws is white space (spaces or tabs) and <ws> is optional white space.
+ The string may include spaces or tabs and need not be quoted. If the
+ string has the sequence of "\n" then a newline character is included in
+ the string.
+
+ examples:
+
+ F6 ls -lg\n
+ F7 uulog -s
+
+*/
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+getinit()
+{
+ char *home; /* user's home directory */
+ char path[BUFSIZ]; /* full path name of init file */
+ char buf[BUFSIZ]; /* buffer to hold 1 line from init file */
+ char key[BUFSIZ]; /* buffer, to hold specified fcn key */
+ char keystr[BUFSIZ]; /* string associated with fcn key */
+ char *ptr; /* pointer to transverse buf */
+ int i, j; /* array indices */
+ int statflag; /* whether init file is regular & readable */
+ struct stat statbuf; /* stat of the init file */
+ FILE *fp; /* file pointer to init file */
+
+ /* construct full path name for init file */
+ home = getenv("HOME");
+ snprintf(path, sizeof(path), "%s/%s", home, INITFILE);
+
+ /* check status if init file */
+ if (stat(path, &statbuf) != -1)
+ {
+ statflag = statbuf.st_mode & S_IFREG && statbuf.st_mode & S_IREAD;
+ if (!statflag || (fp = fopen(path, "r")) == NULL)
+ {
+ fprintf(stderr, "couldn't open initalization file: %s\n", path);
+ exit(1);
+ }
+
+ /* process lines from init file */
+ while (fgets(buf, BUFSIZ, fp) != NULL)
+ {
+ /* variable initializations */
+ i = 0; j = 0;
+ key[0] = '\0'; keystr[0] = '\0';
+ ptr = buf;
+
+ while (*ptr == ' ' || *ptr == '\t') ptr++; /*skip whitespace*/
+
+ if (*ptr == '\n') break; /* we hit an emtpy line */
+
+ while (!isspace(*ptr) && *ptr != '\0') /* get keyname */
+ key[i++] = *ptr++;
+ key[i] = '\0'; /* place EOS in buffer */
+
+ while (*ptr == ' ' || *ptr == '\t') ptr++; /*skip whitespace*/
+
+ while (*ptr != '\n' && *ptr != '\0') /* get string */
+ {
+ /* check if string is to include newline i.e. \n */
+ if (*ptr == '\\' && *(ptr+1) == 'n')
+ {
+ keystr[j] = '\012';
+ ptr++;
+ }
+ else
+ keystr[j] = *ptr;
+ j++; ptr++;
+ }
+ keystr[j] = '\0'; /* place EOS in buffer */
+ dokey(key, keystr); /* load key with string */
+ }
+ }
+ else
+ {
+ fprintf(stderr, "init file %s not found\n\n", path);
+ usage();
+ }
+}
diff --git a/usr.sbin/pcvt/vgaio/CAUTION b/usr.sbin/pcvt/vgaio/CAUTION
new file mode 100644
index 0000000..e1eba06
--- /dev/null
+++ b/usr.sbin/pcvt/vgaio/CAUTION
@@ -0,0 +1,28 @@
+CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION
+CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION
+CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION
+
+ THE USE OF THIS PROGRAM MAY DESTROY YOUR MONITOR !!!
+ ====================================================
+
+ IF YOU DON'T KNOW WHAT YOU ARE DOING, STAY AWAY FROM IT !!!
+ ===========================================================
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION
+CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION
+CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION
+
diff --git a/usr.sbin/pcvt/vgaio/Makefile b/usr.sbin/pcvt/vgaio/Makefile
new file mode 100644
index 0000000..0ac0388
--- /dev/null
+++ b/usr.sbin/pcvt/vgaio/Makefile
@@ -0,0 +1,24 @@
+# $FreeBSD$
+
+PROG= vgaio
+MAN= vgaio.8
+SRCS= vgaio.y lex.l y.tab.h
+
+CFLAGS+= -I${.OBJDIR} -I${.CURDIR}
+
+YACC= yacc
+
+#YFLAGS+= -yd # Bison
+#YFLAGS+= -v # verbose
+
+LFLAGS+= -I
+
+DPADD= ${LIBM} ${LIBY} ${LIBL}
+LDADD= -lm -ly -ll
+
+CLEANFILES+= y.output # comment file from bison
+
+install:
+ @${ECHO} "vgaio is not installed automatically ...."
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pcvt/vgaio/lex.l b/usr.sbin/pcvt/vgaio/lex.l
new file mode 100644
index 0000000..e729850
--- /dev/null
+++ b/usr.sbin/pcvt/vgaio/lex.l
@@ -0,0 +1,82 @@
+%{
+/*
+ * Copyright (c) 1994 Joerg Wunsch
+ *
+ * All rights reserved.
+ *
+ * This program is free software.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Joerg Wunsch
+ * 4. The name of the developer may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ident "$FreeBSD$"
+
+/*
+ * $Log: lex.l,v $
+ * Revision 1.1 1994/03/29 02:47:20 mycroft
+ * pcvt 3.0, with some performance enhancements by Joerg Wunsch and me.
+ *
+ * Revision 1.2 1994/01/08 17:42:58 j
+ * cleanup
+ * made multiple commands per line work
+ * wrote man page
+ *
+ * Revision 1.3 21.12.1994 -hm
+ * Added mi command for accessing the misc out register
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "vgaio.h"
+#include "y.tab.h"
+
+extern YYSTYPE yylval;
+
+%}
+
+D [0-9a-fA-F]
+
+%%
+
+cr|CR { return CR; }
+
+ar|AR { return AR; }
+
+gr|GR { return GR; }
+
+sr|SR { return SR; }
+
+mi|MI { return MI; }
+
+{D}({D}*) { sscanf(yytext, "%x", &yylval.num); return NUM; }
+
+[ \t] { /* ignore */ }
+
+\n { return NEWLINE; }
+
+. { return yytext[0]; }
diff --git a/usr.sbin/pcvt/vgaio/vgaio.8 b/usr.sbin/pcvt/vgaio/vgaio.8
new file mode 100644
index 0000000..657eba2
--- /dev/null
+++ b/usr.sbin/pcvt/vgaio/vgaio.8
@@ -0,0 +1,139 @@
+.\"
+.\" Copyright (c) 1994 Joerg Wunsch
+.\"
+.\" All rights reserved.
+.\"
+.\" This program is free software.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Joerg Wunsch
+.\" 4. The name of the developer may not be used to endorse or promote
+.\" products derived from this software without specific prior written
+.\" permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\" -hm updated 31.12.94
+.\"
+.Dd December 31, 1994
+.Dt VGAIO 8
+.Os
+.Sh NAME
+.Nm vgaio
+.Nd perform input/output on a Video Graphics Array
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Sh DESCRIPTION
+.Ss Purpose
+.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
+cares of all the things necessary and allows the user to access the
+registers of several register groups with their symbolic names.
+.Ss Options
+.Bl -tag -width 10n -offset indent
+.It Fl d
+Turn on the grammar parser debugger.
+.El
+.Ss Command language
+The command language of
+.Nm
+constitutes of some very simple tokens and rules.
+Commands are executed
+line by line as they are entered.
+Each line may contain any number of
+semicolon-separated input/output commands.
+.Pp
+Symbolic register names look like:
+.Pp
+.D1 Ao Em reggroup Ac Ns Aq Em regnumber
+.Pp
+with
+.Aq Em regnumber
+being any hexadecimal number
+(without a leading
+.Em 0x ) ,
+and
+.Aq Em reggroup
+one of the strings
+.Dq Em ar ,
+.Dq Em cr ,
+.Dq Em gr ,
+.Dq Em mi ,
+or
+.Dq Em sr ,
+standing for the
+.Em Attribute controller ,
+.Em CRT controller ,
+.Em Graphics controller ,
+.Em Miscellaneous Output Register ,
+or
+.Em Timing sequencer ,
+respectively.
+.Pp
+An input instruction has the form
+.Pp
+.D1 Ao Em regname Ac \&?
+.Pp
+and will cause
+.Nm
+to output a line like
+.Bd -ragged -offset indent
+.Aq Em regname
+.No = 0x Ns Aq Em number
+.Ed
+.Pp
+An output instruction looks like
+.Bd -ragged -offset indent
+.Ao Em regname Ac =
+.Aq Em number
+.Ed
+.Pp
+Spaces or Tabs between the
+.Aq Em reggroup ,
+the
+.Aq Em regnumber ,
+or any of the other tokens are ignored.
+They are not required anyway.
+.Pp
+The
+.Dq Em mi
+needs a single unused argument to satisfy the syntax :-) (-hm).
+.Ss Access control
+The caller must have uid 0 in order to gain the required access to
+the IO registers.
+.Sh HISTORY
+This program is considered
+.Dq hackware .
+It has been developed in order to simplify the process of developing other
+software that needs to program the Video Graphics Array.
+.Pp
+Remember, to use this program, your kernel has to be compiled with XSERVER
+being defined!
+.Sh AUTHORS
+The program has been contributed by
+.An J\(:org Wunsch ,
+Dresden
+.Aq joerg_wunsch@uriah.sax.de .
diff --git a/usr.sbin/pcvt/vgaio/vgaio.h b/usr.sbin/pcvt/vgaio/vgaio.h
new file mode 100644
index 0000000..e313f86
--- /dev/null
+++ b/usr.sbin/pcvt/vgaio/vgaio.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 1994 Joerg Wunsch
+ *
+ * All rights reserved.
+ *
+ * This program is free software.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Joerg Wunsch
+ * 4. The name of the developer may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * $Header
+ * The author may be reached unter <joerg_wunsch@uriah.sax.de>
+ *
+ * $Log: vgaio.h,v $
+ * Revision 1.1 1994/03/29 02:47:25 mycroft
+ * pcvt 3.0, with some performance enhancements by Joerg Wunsch and me.
+ *
+ * Revision 1.2 1994/01/08 17:42:58 j
+ * cleanup
+ * made multiple commands per line work
+ * wrote man page
+ *
+ *
+ */
+
+/* common structure to hold the definition for a VGA register */
+
+#ifndef VGAIO_H
+#define VGAIO_H
+
+struct reg {
+ int group, num;
+};
+
+#endif /* VGAIO_H */
diff --git a/usr.sbin/pcvt/vgaio/vgaio.y b/usr.sbin/pcvt/vgaio/vgaio.y
new file mode 100644
index 0000000..7d5c05a
--- /dev/null
+++ b/usr.sbin/pcvt/vgaio/vgaio.y
@@ -0,0 +1,251 @@
+%{
+/*
+ * Copyright (c) 1994 Joerg Wunsch
+ *
+ * All rights reserved.
+ *
+ * This program is free software.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Joerg Wunsch
+ * 4. The name of the developer may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ident "$FreeBSD$"
+
+/*
+ * $Log: vgaio.y,v $
+ * Revision 1.1 1994/03/29 02:47:27 mycroft
+ * pcvt 3.0, with some performance enhancements by Joerg Wunsch and me.
+ *
+ * Revision 1.2 1994/01/08 17:42:58 j
+ * cleanup
+ * made multiple commands per line work
+ * wrote man page
+ *
+ * Revision 1.3 21.12.1994 -hm
+ * Added mi command for accessing the misc out register
+ * hex values shown as 2 fixed chars, added binary output
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/fcntl.h>
+#include <machine/cpufunc.h>
+#include <machine/pcvt_ioctl.h>
+
+#ifdef __NetBSD__
+#include <machine/pio.h>
+#endif
+
+#include "vgaio.h"
+
+void setreg(struct reg r, int val);
+void getreg(struct reg r);
+void yyerror(const char *msg);
+
+#define YYDEBUG 1
+
+unsigned short vgabase;
+
+%}
+
+%union {
+ int num;
+ struct reg reg;
+}
+
+%token MI GR CR SR AR NEWLINE
+%token <num> NUM
+
+%type <num> reggroup
+%type <reg> register
+
+%%
+
+interpret: lines ;
+
+lines: line
+ | lines line
+ ;
+
+line: statements NEWLINE
+ | NEWLINE
+ | error NEWLINE { fprintf(stderr, "bing!\n"); }
+ ;
+
+statements: statement
+ | statements ';' statement
+ ;
+
+statement: register '?' { getreg($1); }
+ | register '=' NUM { setreg($1, $3); }
+ | /* lambda */
+ ;
+
+register: reggroup NUM { $$.num = $2; $$.group = $1; }
+ ;
+
+reggroup: GR { $$ = GR; }
+ | CR { $$ = CR; }
+ | SR { $$ = SR; }
+ | AR { $$ = AR; }
+ | MI { $$ = MI; }
+ ;
+
+%%
+
+static struct {
+ int id;
+ const char *name;
+} regnames[] = {
+ {GR, "gr"}, {CR, "cr"}, {SR, "sr"}, {AR, "ar"}, {MI, "mi"},
+ {0, 0}
+};
+
+const char *getname(struct reg r) {
+ int idx;
+ for(idx = 0; regnames[idx].id; idx++)
+ if(regnames[idx].id == r.group)
+ return regnames[idx].name;
+ return "??";
+}
+
+/*---------------------------------------------------------------------------*
+ * return ptr to string of 1's and 0's for value
+ *---------------------------------------------------------------------------*/
+char *
+bin_str(unsigned long val, int length)
+{
+ static char buffer[80];
+ int i = 0;
+
+ if (length > 32)
+ length = 32;
+
+ val = val << (32 - length);
+
+ while (length--)
+ {
+ if (val & 0x80000000)
+ buffer[i++] = '1';
+ else
+ buffer[i++] = '0';
+ if ((length % 4) == 0 && length)
+ buffer[i++] = '.';
+ val = val << 1;
+ }
+ return (buffer);
+}
+
+void getreg(struct reg r) {
+ int val; /* FreeBSD gcc ONLY accepts an int !! */
+
+ switch(r.group) {
+ case GR:
+ outb(0x3ce, r.num);
+ val = inb(0x3cf);
+ break;
+
+ case AR:
+ r.num &= 0x1f;
+ (void)inb(vgabase + 0x0a);
+ outb(0x3c0, r.num + 0x20);
+ val = inb(0x3c1);
+ break;
+
+ case CR:
+ outb(vgabase + 4, r.num);
+ val = inb(vgabase + 5);
+ break;
+
+ case SR:
+ outb(0x3c4, r.num);
+ val = inb(0x3c5);
+ break;
+
+ case MI:
+ val = inb(0x3cc);
+ break;
+ }
+
+ printf("%s%02x = 0x%02x = %s (bin)\n", getname(r), r.num, val, bin_str(val,8));
+}
+
+void setreg(struct reg r, int val) {
+ switch(r.group) {
+ case GR:
+ outb(0x3ce, r.num);
+ outb(0x3cf, val);
+ break;
+
+ case AR:
+ r.num &= 0x1f;
+ (void)inb(vgabase + 0x0a);
+ outb(0x3c0, r.num);
+ outb(0x3c0, val);
+ outb(0x3c0, r.num + 0x20);
+ break;
+
+ case CR:
+ outb(vgabase + 4, r.num);
+ outb(vgabase + 5, val);
+ break;
+
+ case SR:
+ outb(0x3c4, r.num);
+ outb(0x3c5, val);
+ break;
+
+ case MI:
+ outb(0x3c2, val);
+ break;
+ }
+
+ printf("%s%02x set to 0x%02x = %s (bin) now\n", getname(r), r.num, val, bin_str(val,8));
+}
+
+void yyerror(const char *msg) {
+ fprintf(stderr, "yyerror: %s\n", msg);
+}
+
+int main(int argc, char **argv) {
+ int fd;
+
+ if(argc > 1) yydebug = 1;
+
+ if((fd = open("/dev/console", O_RDONLY)) < 0)
+ fd = 0;
+
+ if(ioctl(fd, KDENABIO, 0) < 0) {
+ perror("ioctl(KDENABIO)");
+ return 1;
+ }
+ vgabase = (inb(0x3cc) & 1)? 0x3d0: 0x3b0;
+ yyparse();
+
+ (void)ioctl(fd, KDDISABIO, 0);
+ return 0;
+}
diff --git a/usr.sbin/pcvt/vttest/Makefile b/usr.sbin/pcvt/vttest/Makefile
new file mode 100644
index 0000000..f5d6e7b
--- /dev/null
+++ b/usr.sbin/pcvt/vttest/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= vttest
+SRCS= main.c esc.c
+
+CFLAGS+= -DUSEMYSTTY
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pcvt/vttest/README b/usr.sbin/pcvt/vttest/README
new file mode 100644
index 0000000..ffc2e8b
--- /dev/null
+++ b/usr.sbin/pcvt/vttest/README
@@ -0,0 +1,59 @@
+$FreeBSD$
+
+NOTES FROM THE MOD.SOURCES MODERATOR:
+I split the source up into the three separate pieces it now is.
+In doing this, I put lines like "int reading;" in a header file
+that both C modules include. If your Unix requires one of these
+to be "extern int reading;" then you will have some editing to do.
+Also note that this program uses FIONREAD, which must be implemented
+differently in SystemV, etc., and check out the setjmp() call...
+ /Rich $alz
+Oh, yeah: I also wrote the Makefile and manpage, such as they are.
+-------------------------------------------------------------------
+
+This is a program to test the compatibility (or to demonstrate the
+non-compatibility) of so-called "VT100-compatible" terminals. In
+conformance of the good old hacker traditions, the only documentation
+of this program is the source code itself. To understand it, you also
+need a copy of the original VT100 manual from DEC.
+
+Comments and bug reports: Since this is a release (via USENET) to the
+whole world, a lot of people are going to have opinions and fresh
+ideas about it. (What -- bugs in MY program? Aww...) I can't deal
+with everyone sending me a hacked version, but if you have found a
+serious bug, or ported it to VMS, do tell me. I can't promise any new
+version release, though. From this version on (1.7b) VTTEST will have
+to live its own life without its father holding its hand.
+
+My address is:
+
+Network-mail address: (mcvax,seismo)!enea!suadb!lindberg
+
+Real-world-mail address: Per Lindberg
+ QZ, Stockholm University Computing Center
+ Box 27322
+ S - 102 54 Stockholm
+ SWEDEN
+
+The original version of this program is written for the Sargasso C
+compiler for the DECsystem-10. Many thanks to all sales persons with
+quote VT100-compatible unquote terminals, who prompted me to write
+this program, and also to:
+
+-- Bo Kleve, LIDAC, Linkoping University, Sweden
+ for the portation to DECSYSTEM-20 with the Sargasso C compiler
+
+-- Johan Widen, TTDS, Royal Institute of Technology, Stockholm, Sweden
+ for the portation to various UNIX systems (incl. System III and Xenix)
+
+-- Russ Herman, AES Data Inc., Missisauga, Ont. Canada
+ for fixes and code for the VT102 test
+
+Thanx also to JMR "Gremlin" at KTH, and Goran Wallberg at QZ
+for suggestions, bug fixes, etc.
+
+This program does not have support for all the different variations
+of VT100, like VT125, VT131 nor the new VT200 series. Feel free to
+add that yourself. Happy Hacking!
+
+ /TMP
diff --git a/usr.sbin/pcvt/vttest/esc.c b/usr.sbin/pcvt/vttest/esc.c
new file mode 100644
index 0000000..21baa97
--- /dev/null
+++ b/usr.sbin/pcvt/vttest/esc.c
@@ -0,0 +1,403 @@
+#include "header.h"
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif
+
+println(s) char *s; {
+ printf("%s\n", s);
+}
+
+esc(s) char *s; {
+ printf("%c%s", 27, s);
+}
+
+esc2(s1, s2) char s1, s2; {
+ printf("%c%s%s", 27, s1, s2);
+}
+
+brcstr(ps, c) char *ps, c; {
+ printf("%c[%s%c", 27, ps, c);
+}
+
+brc(pn,c) int pn; char c; {
+ printf("%c[%d%c", 27, pn, c);
+}
+
+brc2(pn1, pn2 ,c) int pn1, pn2; char c; {
+ printf("%c[%d;%d%c", 27, pn1, pn2, c);
+}
+
+cub(pn) int pn; { /* Cursor Backward */
+ brc(pn,'D');
+}
+cud(pn) int pn; { /* Cursor Down */
+ brc(pn,'B');
+}
+cuf(pn) int pn; { /* Cursor Forward */
+ brc(pn,'C');
+}
+cup(pn1, pn2) int pn1, pn2; { /* Cursor Position */
+ brc2(pn1, pn2, 'H');
+}
+cuu(pn) int pn; { /* Cursor Up */
+ brc(pn,'A');
+}
+da() { /* Device Attributes */
+ brc(0,'c');
+}
+decaln() { /* Screen Alignment Display */
+ esc("#8");
+}
+decdhl(lower) int lower; { /* Double Height Line (also double width) */
+ if (lower) esc("#4");
+ else esc("#3");
+}
+decdwl() { /* Double Wide Line */
+ esc("#6");
+}
+deckpam() { /* Keypad Application Mode */
+ esc("=");
+}
+deckpnm() { /* Keypad Numeric Mode */
+ esc(">");
+}
+decll(ps) char *ps; { /* Load LEDs */
+ brcstr(ps, 'q');
+}
+decrc() { /* Restore Cursor */
+ esc("8");
+}
+decreqtparm(pn) int pn; { /* Request Terminal Parameters */
+ brc(pn,'x');
+}
+decsc() { /* Save Cursor */
+ esc("7");
+}
+decstbm(pn1, pn2) int pn1, pn2; { /* Set Top and Bottom Margins */
+ if (pn1 || pn2) brc2(pn1, pn2, 'r');
+ else esc("[r");
+ /* Good for >24-line terminals */
+}
+decswl() { /* Single With Line */
+ esc("#5");
+}
+dectst(pn) int pn; { /* Invoke Confidence Test */
+ brc2(2, pn, 'y');
+}
+dsr(pn) int pn; { /* Device Status Report */
+ brc(pn, 'n');
+}
+ed(pn) int pn; { /* Erase in Display */
+ brc(pn, 'J');
+}
+el(pn) int pn; { /* Erase in Line */
+ brc(pn,'K');
+}
+hts() { /* Horizontal Tabulation Set */
+ esc("H");
+}
+hvp(pn1, pn2) int pn1, pn2; { /* Horizontal and Vertical Position */
+ brc2(pn1, pn2, 'f');
+}
+ind() { /* Index */
+ esc("D");
+}
+nel() { /* Next Line */
+ esc("E");
+}
+ri() { /* Reverse Index */
+ esc("M");
+}
+ris() { /* Reset to Initial State */
+ esc("c");
+}
+rm(ps) char *ps; { /* Reset Mode */
+ brcstr(ps, 'l');
+}
+scs(g,c) int g; char c; { /* Select character Set */
+ printf("%c%c%c%c%c%c%c", 27, g ? ')' : '(', c,
+ 27, g ? '(' : ')', 'B',
+ g ? 14 : 15);
+}
+sgr(ps) char *ps; { /* Select Graphic Rendition */
+ brcstr(ps, 'm');
+}
+sm(ps) char *ps; { /* Set Mode */
+ brcstr(ps, 'h');
+}
+tbc(pn) int pn; { /* Tabulation Clear */
+ brc(pn, 'g');
+}
+
+vt52cup(l,c) int l,c; {
+ printf("%cY%c%c", 27, l + 31, c + 31);
+}
+
+char inchar() {
+
+ /*
+ * Wait until a character is typed on the terminal
+ * then read it, without waiting for CR.
+ */
+
+#ifdef UNIX
+ int lval, waittime, getpid(); static int val; char ch;
+
+ fflush(stdout);
+ lval = val;
+ brkrd = 0;
+ reading = 1;
+ read(STDIN_FILENO,&ch,1);
+ reading = 0;
+ if (brkrd)
+ val = 0177;
+ else
+ val = ch;
+ if ((val==0177) && (val==lval))
+ kill(getpid(), (int) SIGTERM);
+#endif
+#ifdef SARG10
+ int val, waittime;
+
+ waittime = 0;
+ while(!uuo(051,2,&val)) { /* TTCALL 2, (INCHRS) */
+ zleep(100); /* Wait 0.1 seconds */
+ if ((waittime += ttymode) > 600) /* Time-out, in case */
+ return('\177'); /* of hung in ttybin(1) */
+ }
+#endif
+#ifdef SARG20 /* try to fix a time-out function */
+ int val, waittime;
+
+ waittime = 0;
+ while(jsys(SIBE,2,_PRIIN) == 0) { /* Is input empty? */
+ zleep(100);
+ if ((waittime += ttymode) > 600)
+ return('\177');
+ }
+ ejsys(BIN,_PRIIN);
+ val = jsac[2];
+#endif
+ return(val);
+}
+
+char *instr() {
+
+ /*
+ * Get an unfinished string from the terminal:
+ * wait until a character is typed on the terminal,
+ * then read it, and all other available characters.
+ * Return a pointer to that string.
+ */
+
+
+ int i, val, crflag; long l1; char ch;
+ static char result[80];
+
+ i = 0;
+ result[i++] = inchar();
+/* Wait 0.1 seconds (1 second in vanilla UNIX) */
+#ifdef SARG10
+ if (trmop(01031,0) < 5) zleep(500); /* wait longer if low speed */
+ else zleep(100);
+#else
+ zleep(100);
+#endif
+#ifdef UNIX
+ fflush(stdout);
+#ifdef XENIX
+ while(rdchk(0)) {
+ read(STDIN_FILENO,result+i,1);
+ if (i++ == 78) break;
+ }
+#else
+#ifdef SIII
+ while(read(STDERR_FILENO,result+i,1) == 1)
+ if (i++ == 78) break;
+#else
+ while(ioctl(0,FIONREAD,&l1), l1 > 0L) {
+ while(l1-- > 0L) {
+ read(STDIN_FILENO,result+i,1);
+ if (i++ == 78) goto out1;
+ }
+ }
+out1:
+#endif
+#endif
+#endif
+#ifdef SARG10
+ while(uuo(051,2,&val)) { /* TTCALL 2, (INCHRS) */
+ if (!(val == '\012' && crflag)) /* TOPS-10 adds LF to CR */
+ result[i++] = val;
+ crflag = val == '\015';
+ if (i == 79) break;
+ zleep(50); /* Wait 0.05 seconds */
+ }
+#endif
+#ifdef SARG20
+ while(jsys(SIBE,2,_PRIIN) != 0) { /* read input until buffer is empty */
+ ejsys(BIN,_PRIIN);
+ result[i++] = jsac[2];
+ if (i == 79) break;
+ zleep(50); /* Wait 0.05 seconds */
+ }
+#endif
+ result[i] = '\0';
+ return(result);
+}
+
+ttybin(bin) int bin; {
+#ifdef SARG10
+ #define OPEN 050
+ #define IO_MOD 0000017
+ #define _IOPIM 2
+ #define _IOASC 0
+ #define _TOPAG 01021
+ #define _TOSET 01000
+
+ int v;
+ static int arglst[] = {
+ _IOPIM,
+ `TTY`,
+ 0
+ };
+ arglst[0] = bin ? _IOPIM : _IOASC;
+ v = uuo(OPEN, 1, &arglst[0]);
+ if (!v) { printf("OPEN failed"); exit(); }
+ trmop(_TOPAG + _TOSET, bin ? 0 : 1);
+ ttymode = bin;
+#endif
+#ifdef SARG20
+ /* TTYBIN will set the line in BINARY/ASCII mode
+ * BINARY mode is needed to send control characters
+ * Bit 28 must be 0 (we don't flip it).
+ * Bit 29 is used for the mode change.
+ */
+
+ #define _TTASC 0000100
+ #define _MOXOF 0000043
+
+ int v;
+
+ ejsys(RFMOD,_CTTRM);
+ v = ejsys(SFMOD,_CTTRM, bin ? (~_TTASC & jsac[2]) : (_TTASC | jsac[2]));
+ if (v) { printf("SFMOD failed"); exit(); }
+ v = ejsys(MTOPR,_CTTRM,_MOXOF,0);
+ if (v) { printf("MTOPR failed"); exit(); }
+#endif
+}
+
+#ifdef SARG20
+/*
+ * SUPERBIN turns off/on all input character interrupts
+ * This affects ^C, ^O, ^T
+ * Beware where and how you use it !!!!!!!
+ */
+
+superbin(bin) int bin; {
+ int v;
+
+ v = ejsys(STIW,(0//-5), bin ? 0 : -1);
+ if (v) { printf("STIW superbinary setting failed"); exit(); }
+ ttymode = bin;
+}
+
+/*
+ * PAGE affects the ^S/^Q handshake.
+ * Set bit 34 to turn it on. Clear it for off.
+ */
+
+page(bin) int bin; {
+ int v;
+
+ #define TT_PGM 0000002
+
+ ejsys(RFMOD,_CTTRM); /* Get the current terminal status */
+ v = ejsys(STPAR,_CTTRM, bin ? (TT_PGM | jsac[2]) : (~TT_PGM & jsac[2]));
+ if (v) { printf("STPAR failed"); exit(); }
+}
+#endif
+
+trmop(fc,arg) int fc, arg; {
+#ifdef SARG10
+ int retvalp;
+ int arglst[3];
+
+ /* TRMOP is a TOPS-10 monitor call that does things to the terminal. */
+
+ /* Find out TTY nbr (PA1050 barfs if TRMOP get -1 instead of udx) */
+ /* A TRMNO monitor call returns the udx (Universal Device Index) */
+
+ arglst[0] = fc; /* function code */
+ arglst[1] = calli(0115, -1); /* udx, TRMNO. UUO */
+ arglst[2] = arg; /* Optional argument */
+
+ if (calli(0116, 3 // &arglst[0], &retvalp)) /* TRMOP. UUO */
+ return (retvalp);
+ else {
+ printf("?Error return in TRMOP.");
+ exit();
+ }
+#endif
+}
+
+inputline(s) char *s; {
+ scanf("%s",s);
+#ifdef SARG10
+ readnl();
+#endif
+#ifdef SARG20
+ readnl();
+#endif
+}
+
+inflush() {
+
+ /*
+ * Flush input buffer, make sure no pending input character
+ */
+
+ int val;
+
+#ifdef UNIX
+#ifdef XENIX
+ while(rdchk(0)) read(STDIN_FILENO,&val,1);
+#else
+#ifdef SIII
+ while(read(STDERR_FILENO,&val,1));
+#else
+ long l1;
+ ioctl (0, FIONREAD, &l1);
+ while(l1-- > 0L) read(STDIN_FILENO,&val,1);
+#endif
+#endif
+#endif
+#ifdef SARG10
+ while(uuo(051,2,&val)) /* TTCALL 2, (INCHRS) */
+ ;
+#endif
+#ifdef SARG20
+ ejsys(CFIBF,_PRIIN); /* Clear input buffer */
+#endif
+}
+
+zleep(t) int t; {
+
+/*
+ * Sleep and do nothing (don't waste CPU) for t milliseconds
+ */
+
+#ifdef SARG10
+ calli(072,t); /* (HIBER) t milliseconds */
+#endif
+#ifdef SARG20
+ ejsys(DISMS,t); /* DISMISS for t milliseconds */
+#endif
+#ifdef UNIX
+ t = t / 1000;
+ if (t == 0) t = 1;
+ sleep(t); /* UNIX can only sleep whole seconds */
+#endif
+}
diff --git a/usr.sbin/pcvt/vttest/header.h b/usr.sbin/pcvt/vttest/header.h
new file mode 100644
index 0000000..ae1ccaf
--- /dev/null
+++ b/usr.sbin/pcvt/vttest/header.h
@@ -0,0 +1,45 @@
+/* $FreeBSD$ */
+#define VERSION "1.7b 1985-04-19"
+
+/* Choose one of these */
+
+/* #define XENIX */ /* XENIX implies UNIX */
+/* #define SIII */ /* SIII implies UNIX, (NDELAY a la System III) */
+#define UNIX /* UNIX */
+/* #define VMS */ /* VMS not done yet -- send me your version!!!! */
+/* #define SARG20 */ /* Sargasso C for TOPS-20 */
+/* #define SARG10 */ /* Sargasso C for TOPS-10 */
+
+/* These #ifdef:s are implementation dependent stuff for the Sargasso C */
+/* Unix C barfs on directives like "#strings", so we keep them */
+/* indented. Then unix c can't find them, but Sargasso C *can*. */
+/* Admittedly kludgey, but it works...) */
+#ifdef SARG10
+ #define _UNIXCON /* Make UNIX-flavored I/O on TOPS */
+ #strings low /* put strings in lowseg mem so we can modify them. */
+#endif
+#ifdef SARG20
+ #define _UNIXCON /* Make UNIX-flavored I/O on TOPS */
+ #strings low /* put strings in lowseg mem so we can modify them. */
+ #include <TOPS20.HDR>
+#endif
+
+#include <stdio.h>
+
+
+#ifdef UNIX
+#include <ctype.h>
+#include <sgtty.h>
+#include <signal.h>
+#include <setjmp.h>
+#include <unistd.h>
+jmp_buf intrenv;
+struct sgttyb sgttyOrg, sgttyNew;
+char stdioBuf[BUFSIZ];
+int brkrd, reading;
+extern onterm(), onbrk();
+#ifdef SIII
+#include <fcntl.h>
+#endif
+#endif
+int ttymode;
diff --git a/usr.sbin/pcvt/vttest/main.c b/usr.sbin/pcvt/vttest/main.c
new file mode 100644
index 0000000..4093c57
--- /dev/null
+++ b/usr.sbin/pcvt/vttest/main.c
@@ -0,0 +1,2018 @@
+/*
+ VTTEST.C
+
+ Written Novemeber 1983 - July 1984 by Per Lindberg,
+ Stockholm University Computer Center (QZ), Sweden.
+
+ THE MAD PROGRAMMER STRIKES AGAIN!
+
+ This software is (c) 1984 by QZ
+ Non-commercial use and copying allowed.
+
+If you are developing a commercial product, and use this program to do
+it, and that product is successful, please send a sum of money of your
+choice to the address below.
+
+*/
+
+/* $FreeBSD$ */
+
+#include "header.h"
+
+char inchar(), *instr(), *lookup();
+
+struct table {
+ int key;
+ char *msg;
+} paritytable[] = {
+ { 1, "NONE" },
+ { 4, "ODD" },
+ { 5, "EVEN" },
+ { -1, "" }
+},nbitstable[] = {
+ { 1, "8" },
+ { 2, "7" },
+ { -1,"" }
+},speedtable[] = {
+ { 0, "50" },
+ { 8, "75" },
+ { 16, "110" },
+ { 24, "132.5"},
+ { 32, "150" },
+ { 40, "200" },
+ { 48, "300" },
+ { 56, "600" },
+ { 64, "1200" },
+ { 72, "1800" },
+ { 80, "2000" },
+ { 88, "2400" },
+ { 96, "3600" },
+ { 104, "4800" },
+ { 112, "9600" },
+ { 120, "19200" },
+ { -1, "" }
+};
+
+#ifdef USEMYSTTY
+#ifndef stty
+int stty(fd,ptr)
+int fd;
+struct sgttyb *ptr;
+{
+ return(ioctl(fd, TIOCSETP, ptr));
+}
+#endif
+#ifndef gtty
+int gtty(fd,ptr)
+int fd;
+struct sgttyb *ptr;
+{
+ return(ioctl(fd, TIOCGETP, ptr));
+}
+#endif
+#endif
+
+main() {
+
+ int menuchoice;
+
+ static char *mainmenu[] = {
+ "Exit",
+ "Test of cursor movements",
+ "Test of screen features",
+ "Test of character sets",
+ "Test of double-sized characters",
+ "Test of keyboard",
+ "Test of terminal reports",
+ "Test of VT52 mode",
+ "Test of VT102 features (Insert/Delete Char/Line)",
+ "Test of known bugs",
+ "Test of reset and self-test",
+ ""
+ };
+
+#ifdef UNIX
+ initterminal(setjmp(intrenv));
+ signal(SIGINT, (void *)onbrk);
+ signal(SIGTERM, (void *)onterm);
+ reading = 0;
+#else
+ initterminal(0);
+#endif
+ do {
+#ifdef SARG20
+ ttybin(1); /* set line to binary mode again. It's reset somehow!! */
+#endif
+ ed(2);
+ cup(5,10); printf("VT100 test program, version %s", VERSION);
+ cup(7,10); println("Choose test type:");
+ menuchoice = menu(mainmenu);
+ switch (menuchoice) {
+ case 1: tst_movements(); break;
+ case 2: tst_screen(); break;
+ case 3: tst_characters(); break;
+ case 4: tst_doublesize(); break;
+ case 5: tst_keyboard(); break;
+ case 6: tst_reports(); break;
+ case 7: tst_vt52(); break;
+ case 8: tst_insdel(); break;
+ case 9: tst_bugs(); break;
+ case 10: tst_rst(); break;
+ }
+ } while (menuchoice);
+ bye();
+}
+
+tst_movements() {
+
+ /* Test of:
+ CUF (Cursor Forward)
+ CUB (Cursor Backward)
+ CUD (Cursor Down) IND (Index) NEL (Next Line)
+ CUU (Cursor Up) RI (Reverse Index)
+ CUP (Cursor Position) HVP (Horizontal and Vertical Position)
+ ED (Erase in Display)
+ EL (Erase in Line)
+ DECALN (Screen Alignment Display)
+ <CR> <BS>
+ Cursor control characters inside CSI sequences
+ */
+
+ int i, row, col, pass, width, hlfxtra;
+ char c, *ctext = "This is a correct sentence";
+
+ for (pass = 0; pass <= 1; pass++) {
+ if (pass == 0) { rm("?3"); width = 80; hlfxtra = 0; }
+ else { sm("?3"); width = 132; hlfxtra = 26; }
+
+ decaln();
+ cup( 9,10+hlfxtra); ed(1);
+ cup(18,60+hlfxtra); ed(0); el(1);
+ cup( 9,71+hlfxtra); el(0);
+ for (row = 10; row <= 16; row++) {
+ cup(row, 10+hlfxtra); el(1);
+ cup(row, 71+hlfxtra); el(0);
+ }
+ cup(17,30); el(2);
+ for (col = 1; col <= width; col++) {
+ hvp(24, col); printf("*");
+ hvp( 1, col); printf("*");
+ }
+ cup(2,2);
+ for (row = 2; row <= 23; row++) {
+ printf("+");
+ cub(1);
+ ind();
+ }
+ cup(23,width-1);
+ for (row = 23; row >=2; row--) {
+ printf("+");
+ cub(1); ri();
+ }
+ cup(2,1);
+ for (row = 2; row <= 23; row++) {
+ printf("*");
+ cup(row, width);
+ printf("*");
+ cub(10);
+ if(row < 10)
+ nel();
+ else
+ printf("\n");
+ }
+ cup(2,10);
+ cub(42+hlfxtra); cuf(2);
+ for (col = 3; col <= width-2; col++) {
+ printf("+");
+ cuf(0); cub(2); cuf(1);
+ }
+ cup(23,70+hlfxtra);
+ cuf(42+hlfxtra); cub(2);
+ for (col = width-2; col >= 3; col--) {
+ printf("+");
+ cub(1); cuf(1); cub(0); printf("%c", 8);
+ }
+ cup( 1, 1); cuu(10); cuu(1); cuu(0);
+ cup(24,width); cud(10); cud(1); cud(0);
+
+ cup(10,12+hlfxtra);
+ for (row = 10; row <= 15; row++) {
+ for (col = 12+hlfxtra; col <= 69+hlfxtra; col++) printf(" ");
+ cud(1); cub(58);
+ }
+ cuu(5); cuf(1);
+ printf("The screen should be cleared, and have an unbroken bor-");
+ cup(12,13+hlfxtra);
+ printf("der of *'s and +'s around the edge, and exactly in the");
+ cup(13,13+hlfxtra);
+ printf("middle there should be a frame of E's around this text");
+ cup(14,13+hlfxtra);
+ printf("with one (1) free position around it. ");
+ holdit();
+ }
+ rm("?3");
+
+ ed(2);
+ cup(1,1);
+ println("Test of cursor-control characters inside ESC sequences.");
+ println("Below should be two identical lines:");
+ println("");
+ println("A B C D E F G H I J K L M N O P Q R S");
+ for (i = 1; i < 20; i++) {
+ printf("%c", 64 + i);
+ brcstr("2\010", 'C'); /* Two forward, one backspace */
+ }
+ println("");
+ println("");
+ holdit();
+
+ ed(2);
+ cup(1,1);
+ println("Test of leading zeros in ESC sequences.");
+ printf("Two lines below you should see the sentence \"%s\".",ctext);
+ for (col = 1; *ctext; col++)
+ printf("\033[00000000004;00000000%dH%c",col,*ctext++);
+ cup(20,1);
+ holdit();
+}
+
+tst_screen() {
+
+ /* Test of:
+ - DECSTBM (Set Top and Bottom Margins)
+ - TBC (Tabulation Clear)
+ - HTS (Horizontal Tabulation Set)
+ - SM RM (Set/Reset mode): - 80/132 chars
+ - Origin: Realtive/absolute
+ - Scroll: Smooth/jump
+ - Wraparound
+ - SGR (Select Graphic Rendition)
+ - SM RM (Set/Reset Mode) - Inverse
+ - DECSC (Save Cursor)
+ - DECRC (Restore Cursor)
+ */
+
+ int i, j, cset, row, col, down, soft, background;
+
+ static char *tststr = "*qx`";
+ static char *attr[5] = { ";0", ";1", ";4", ";5", ";7" };
+
+ cup(1,1);
+ sm("?7"); /* Wrap Around ON */
+ for (col = 1; col <= 160; col++) printf("*");
+ rm("?7"); /* Wrap Around OFF */
+ cup(3,1);
+ for (col = 1; col <= 160; col++) printf("*");
+ sm("?7"); /* Wrap Around ON */
+ cup(5,1);
+ println("This should be three identical lines of *'s completely filling");
+ println("the top of the screen without any empty lines between.");
+ println("(Test of WRAP AROUND mode setting.)");
+ holdit();
+
+ ed(2);
+ tbc(3);
+ cup(1,1);
+ for (col = 1; col <= 78; col += 3) {
+ cuf(3); hts();
+ }
+ cup(1,4);
+ for (col = 4; col <= 78; col += 6) {
+ tbc(0); cuf(6);
+ }
+ cup(1,7); tbc(1); tbc(2); /* no-op */
+ cup(1,1); for (col = 1; col <= 78; col += 6) printf("\t*");
+ cup(2,2); for (col = 2; col <= 78; col += 6) printf(" *");
+ cup(4,1);
+ println("Test of TAB setting/resetting. These two lines");
+ printf("should look the same. ");
+ holdit();
+ for (background = 0; background <= 1; background++) {
+ if (background) rm("?5");
+ else sm("?5");
+ sm("?3"); /* 132 cols */
+ ed(2); /* VT100 clears screen on SM3/RM3, but not obviously, so... */
+ cup(1,1); tbc(3);
+ for (col = 1; col <= 132; col += 8) {
+ cuf(8); hts();
+ }
+ cup(1,1); for (col = 1; col <= 130; col += 10) printf("1234567890");
+ printf("12");
+ for (row = 3; row <= 20; row++) {
+ cup(row,row);
+ printf("This is 132 column mode, %s background.",
+ background ? "dark" : "light");
+ }
+ holdit();
+ rm("?3"); /* 80 cols */
+ ed(2); /* VT100 clears screen on SM3/RM3, but not obviously, so... */
+ cup(1,1); for (col = 1; col <= 80; col += 10) printf("1234567890");
+ for (row = 3; row <= 20; row++) {
+ cup(row,row);
+ printf("This is 80 column mode, %s background.",
+ background ? "dark" : "light");
+ }
+ holdit();
+ }
+
+ ed(2);
+ sm("?6"); /* Origin mode (relative) */
+ for (soft = -1; soft <= 0; soft++) {
+ if (soft) sm("?4");
+ else rm("?4");
+ for (row = 12; row >= 1; row -= 11) {
+ decstbm(row, 24-row+1);
+ ed(2);
+ for (down = 0; down >= -1; down--) {
+ if (down) cuu(24);
+ else cud(24);
+ for (i = 1; i <= 30; i++) {
+ printf("%s scroll %s region %d Line %d\n",
+ soft ? "Soft" : "Jump",
+ down ? "down" : "up",
+ 2*(13-row), i);
+ if (down) { ri(); ri(); }
+ }
+ }
+ holdit();
+ }
+ }
+ ed(2);
+ decstbm(23,24);
+ printf(
+ "\nOrigin mode test. This line should be at the bottom of the screen.");
+ cup(1,1);
+ printf("%s",
+ "This line should be the one above the bottom of the screeen. ");
+ holdit();
+ ed(2);
+ rm("?6"); /* Origin mode (absolute) */
+ cup(24,1);
+ printf(
+ "Origin mode test. This line should be at the bottom of the screen.");
+ cup(1,1);
+ printf("%s", "This line should be at the top if the screen. ");
+ holdit();
+ decstbm(1,24);
+
+ ed(2);
+ cup( 1,20); printf("Graphic rendition test pattern:");
+ cup( 4, 1); sgr("0"); printf("vanilla");
+ cup( 4,40); sgr("0;1"); printf("bold");
+ cup( 6, 6); sgr(";4"); printf("underline");
+ cup( 6,45);sgr(";1");sgr("4");printf("bold underline");
+ cup( 8, 1); sgr("0;5"); printf("blink");
+ cup( 8,40); sgr("0;5;1"); printf("bold blink");
+ cup(10, 6); sgr("0;4;5"); printf("underline blink");
+ cup(10,45); sgr("0;1;4;5"); printf("bold underline blink");
+ cup(12, 1); sgr("1;4;5;0;7"); printf("negative");
+ cup(12,40); sgr("0;1;7"); printf("bold negative");
+ cup(14, 6); sgr("0;4;7"); printf("underline negative");
+ cup(14,45); sgr("0;1;4;7"); printf("bold underline negative");
+ cup(16, 1); sgr("1;4;;5;7"); printf("blink negative");
+ cup(16,40); sgr("0;1;5;7"); printf("bold blink negative");
+ cup(18, 6); sgr("0;4;5;7"); printf("underline blink negative");
+ cup(18,45); sgr("0;1;4;5;7"); printf("bold underline blink negative");
+ sgr("");
+
+ rm("?5"); /* Inverse video off */
+ cup(23,1); el(0); printf("Dark background. "); holdit();
+ sm("?5"); /* Inverse video */
+ cup(23,1); el(0); printf("Light background. "); holdit();
+ rm("?5");
+ ed(2);
+ cup(8,12); printf("normal");
+ cup(8,24); printf("bold");
+ cup(8,36); printf("underscored");
+ cup(8,48); printf("blinking");
+ cup(8,60); printf("reversed");
+ cup(10,1); printf("stars:");
+ cup(12,1); printf("line:");
+ cup(14,1); printf("x'es:");
+ cup(16,1); printf("diamonds:");
+ for (cset = 0; cset <= 3; cset++) {
+ for (i = 0; i <= 4; i++) {
+ cup(10 + 2 * cset, 12 + 12 * i);
+ sgr(attr[i]);
+ if (cset == 0 || cset == 2) scs(0,'B');
+ else scs(0,'0');
+ for (j = 0; j <= 4; j++) {
+ printf("%c", tststr[cset]);
+ }
+ decsc();
+ cup(cset + 1, i + 1); sgr(""); scs(0,'B'); printf("A");
+ decrc();
+ for (j = 0; j <= 4; j++) {
+ printf("%c", tststr[cset]);
+ }
+ }
+ }
+ sgr("0"); scs(0,'B'); cup(21,1);
+ println("Test of the SAVE/RESTORE CURSOR feature. There should");
+ println("be ten characters of each flavour, and a rectangle");
+ println("of 5 x 4 A's filling the top left of the screen.");
+ holdit();
+}
+
+tst_characters() {
+ /* Test of:
+ SCS (Select character Set)
+ */
+
+ int i, j, g, cset;
+ char chcode[5], *setmsg[5];
+
+ chcode[0] = 'A';
+ chcode[1] = 'B';
+ chcode[2] = '0';
+ chcode[3] = '1';
+ chcode[4] = '2';
+ setmsg[0] = "UK / national";
+ setmsg[1] = "US ASCII";
+ setmsg[2] = "Special graphics and line drawing";
+ setmsg[3] = "Alternate character ROM standard characters";
+ setmsg[4] = "Alternate character ROM special graphics";
+
+ cup(1,10); printf("Selected as G0 (with SI)");
+ cup(1,48); printf("Selected as G1 (with SO)");
+ for (cset = 0; cset <= 4; cset++) {
+ scs(1,'B');
+ cup(3 + 4 * cset, 1);
+ sgr("1");
+ printf("Character set %c (%s)",chcode[cset], setmsg[cset]);
+ sgr("0");
+ for (g = 0; g <= 1; g++) {
+ scs(g,chcode[cset]);
+ for (i = 1; i <= 3; i++) {
+ cup(3 + 4 * cset + i, 10 + 38 * g);
+ for (j = 0; j <= 31; j++) {
+ printf("%c", i * 32 + j);
+ }
+ }
+ }
+ }
+ scs(1,'B');
+ cup(24,1); printf("These are the installed character sets. ");
+ holdit();
+}
+
+tst_doublesize() {
+ /* Test of:
+ DECSWL (Single Width Line)
+ DECDWL (Double Width Line)
+ DECDHL (Double Height Line) (also implicit double width)
+ */
+
+ int col, i, w, w1;
+
+ /* Print the test pattern in both 80 and 132 character width */
+
+ for(w = 0; w <= 1; w++) {
+ w1 = 13 * w;
+
+ ed(2);
+ cup(1, 1);
+ if (w) { sm("?3"); printf("132 column mode"); }
+ else { rm("?3"); printf(" 80 column mode"); }
+
+ cup( 5, 3 + 2 * w1);
+ printf("v------- left margin");
+
+ cup( 7, 3 + 2 * w1);
+ printf("This is a normal-sized line");
+ decdhl(0); decdhl(1); decdwl(); decswl();
+
+ cup( 9, 2 + w1);
+ printf("This is a Double-width line");
+ decswl(); decdhl(0); decdhl(1); decdwl();
+
+ cup(11, 2 + w1);
+ decdwl(); decswl(); decdhl(1); decdhl(0);
+ printf("This is a Double-width-and-height line");
+ cup(12, 2 + w1);
+ decdwl(); decswl(); decdhl(0); decdhl(1);
+ printf("This is a Double-width-and-height line");
+
+ cup(14, 2 + w1);
+ decdwl(); decswl(); decdhl(1); decdhl(0); el(2);
+ printf("This is another such line");
+ cup(15, 2 + w1);
+ decdwl(); decswl(); decdhl(0); decdhl(1);
+ printf("This is another such line");
+
+ cup(17, 3 + 2 * w1);
+ printf("^------- left margin");
+
+ cup(21, 1);
+ printf("This is not a double-width line");
+ for (i = 0; i <= 1; i++) {
+ cup(21,6);
+ if (i) { printf("**is**"); decdwl(); }
+ else { printf("is not"); decswl(); }
+ cup(23,1); holdit();
+ }
+ }
+ /* Set vanilla tabs for next test */
+ cup(1,1); tbc(3); for (col = 1; col <= 132; col += 8) { cuf(8); hts(); }
+ rm("?3");
+ ed(2);
+ scs(0,'0');
+
+ cup( 8,1); decdhl(0); printf("lqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqk");
+ cup( 9,1); decdhl(1); printf("lqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqk");
+ cup(10,1); decdhl(0); printf("x%c%c%c%c%cx",9,9,9,9,9);
+ cup(11,1); decdhl(1); printf("x%c%c%c%c%cx",9,9,9,9,9);
+ cup(12,1); decdhl(0); printf("x%c%c%c%c%cx",9,9,9,9,9);
+ cup(13,1); decdhl(1); printf("x%c%c%c%c%cx",9,9,9,9,9);
+ cup(14,1); decdhl(0); printf("x x");
+ cup(15,1); decdhl(1); printf("x x");
+ cup(16,1); decdhl(0); printf("mqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqj");
+ cup(17,1); decdhl(1); printf("mqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqj");
+ scs(0,'B'); sgr("1;5");
+ cup(12,3);
+ printf("* The mad programmer strikes again * ");
+ cup(13,3); printf("%c",9); cub(6);
+ printf("* The mad programmer strikes again *");
+ sgr("0");
+ cup(22,1);
+ println("Another test pattern... a frame with blinking bold text,");
+ printf("all in double-height double-width size. ");
+ holdit();
+
+ decstbm(8,24); /* Absolute origin mode, so cursor is set at (1,1) */
+ cup(8,1);
+ for (i = 1; i <= 12; i++)
+ ri();
+ decstbm(0,0); /* No scroll region */
+ cup(1,1);
+ printf("%s", "Exactly half of the box should remain. ");
+ holdit();
+}
+
+tst_keyboard() {
+
+/* Test of:
+ - DECLL (Load LEDs)
+ - Keyboard return messages
+ - SM RM (Set/Reset Mode) - Cursor Keys
+ - Auto repeat
+ - DECKPAM (Keypad Application Mode)
+ - DECKPNM (Keypad Numeric Mode)
+
+The standard VT100 keayboard layout:
+
+ UP DN LE RI
+
+ESC 1! 2@ 3# 4$ 5% 6^ 7& 8* 9( 0) -_ =+ `~ BS
+
+TAB* qQ wW eE rR tT yY uU iI oO pP [{ ]} DEL
+
+** ** aA sS dD fF gG hH jJ kK lL ;: ," RETN \|
+
+** **** zZ xX cC vV bB nN mM ,< .> /? **** LF
+
+ ****************SPACE BAR****************
+
+ PF1 PF2 PF3 PF4
+
+ *7* *8* *9* *-*
+
+ *4* *5* *6* *,*
+
+ *1* *2* *3*
+
+ ***0*** *.* ENT
+*/
+
+ char *ledmsg[6], *ledseq[6];
+
+ int i, j, okflag;
+ int kblayout;
+ int ckeymode;
+ int fkeymode;
+ char kbdc;
+ char *kbds = " ";
+ char *curkeystr, *fnkeystr, *abmstr;
+ char arptstring[500];
+
+ static struct key {
+ char c;
+ int row;
+ int col;
+ char *symbol;
+ } keytab [] = {
+ { 27, 1, 0, "ESC" },
+ { '1', 1, 6, "1" }, { '!', 1, 7, "!" },
+ { '2', 1, 11, "2" }, { '@', 1, 12, "@" },
+ { '3', 1, 16, "3" }, { '#', 1, 17, "#" },
+ { '4', 1, 21, "4" }, { '$', 1, 22, "$" },
+ { '5', 1, 26, "5" }, { '%', 1, 27, "%" },
+ { '6', 1, 31, "6" }, { '^', 1, 32, "^" },
+ { '7', 1, 36, "7" }, { '&', 1, 37, "&" },
+ { '8', 1, 41, "8" }, { '*', 1, 42, "*" },
+ { '9', 1, 46, "9" }, { '(', 1, 47, "(" },
+ { '0', 1, 51, "0" }, { ')', 1, 52, ")" },
+ { '-', 1, 56, "-" }, { '_', 1, 57, "_" },
+ { '=', 1, 61, "=" }, { '+', 1, 62, "+" },
+ { '`', 1, 66, "`" }, { '~', 1, 67, "~" },
+ { 8, 1, 70, "BS" },
+ { 9, 2, 0, " TAB " },
+ { 'q', 2, 8, "q" }, { 'Q', 2, 9, "Q" },
+ { 'w', 2, 13, "w" }, { 'W', 2, 14, "W" },
+ { 'e', 2, 18, "e" }, { 'E', 2, 19, "E" },
+ { 'r', 2, 23, "r" }, { 'R', 2, 24, "R" },
+ { 't', 2, 28, "t" }, { 'T', 2, 29, "T" },
+ { 'y', 2, 33, "y" }, { 'Y', 2, 34, "Y" },
+ { 'u', 2, 38, "u" }, { 'U', 2, 39, "U" },
+ { 'i', 2, 43, "i" }, { 'I', 2, 44, "I" },
+ { 'o', 2, 48, "o" }, { 'O', 2, 49, "O" },
+ { 'p', 2, 53, "p" }, { 'P', 2, 54, "P" },
+ { '[', 2, 58, "[" }, { '{', 2, 59, "{" },
+ { ']', 2, 63, "]" }, { '}', 2, 64, "}" },
+ { 127, 2, 71, "DEL" },
+ { 'a', 3, 10, "a" }, { 'A', 3, 11, "A" },
+ { 's', 3, 15, "s" }, { 'S', 3, 16, "S" },
+ { 'd', 3, 20, "d" }, { 'D', 3, 21, "D" },
+ { 'f', 3, 25, "f" }, { 'F', 3, 26, "F" },
+ { 'g', 3, 30, "g" }, { 'G', 3, 31, "G" },
+ { 'h', 3, 35, "h" }, { 'H', 3, 36, "H" },
+ { 'j', 3, 40, "j" }, { 'J', 3, 41, "J" },
+ { 'k', 3, 45, "k" }, { 'K', 3, 46, "K" },
+ { 'l', 3, 50, "l" }, { 'L', 3, 51, "L" },
+ { ';', 3, 55, ";" }, { ':', 3, 56, ":" },
+ {'\'', 3, 60, "'" }, { '"', 3, 61,"\"" },
+ { 13, 3, 65, "RETN"},
+ {'\\', 3, 71,"\\" }, { '|', 3, 72, "|" },
+ { 'z', 4, 12, "z" }, { 'Z', 4, 13, "Z" },
+ { 'x', 4, 17, "x" }, { 'X', 4, 18, "X" },
+ { 'c', 4, 22, "c" }, { 'C', 4, 23, "C" },
+ { 'v', 4, 27, "v" }, { 'V', 4, 28, "V" },
+ { 'b', 4, 32, "b" }, { 'B', 4, 33, "B" },
+ { 'n', 4, 37, "n" }, { 'N', 4, 38, "N" },
+ { 'm', 4, 42, "m" }, { 'M', 4, 43, "M" },
+ { ',', 4, 47, "," }, { '<', 4, 48, "<" },
+ { '.', 4, 52, "." }, { '>', 4, 53, ">" },
+ { '/', 4, 57, "/" }, { '?', 4, 58, "?" },
+ { 10, 4, 69, "LF" },
+ { ' ', 5, 13, " SPACE BAR "},
+ {'\0', 0, 0, "" }
+ };
+
+ static struct natkey {
+ char natc;
+ int natrow;
+ int natcol;
+ char *natsymbol;
+ } natkeytab [][29] = {
+ {
+ { '"', 1, 12, "\""},
+ { '&', 1, 32, "&" },
+ { '/', 1, 37, "/" },
+ { '(', 1, 42, "(" },
+ { ')', 1, 47, ")" },
+ { '=', 1, 52, "=" },
+ { '+', 1, 56, "+" }, { '?', 1, 57, "?" },
+ { '`', 1, 61, "`" }, { '@', 1, 62, "@" },
+ { '<', 1, 66, "<" }, { '>', 1, 67, ">" },
+ { '}', 2, 58, "}" }, { ']', 2, 59, "]" },
+ { '^', 2, 63, "^" }, { '~', 2, 64, "~" },
+ { '|', 3, 55, "|" }, {'\\', 3, 56,"\\" },
+ { '{', 3, 60, "{" }, { '[', 3, 61, "[" },
+ {'\'', 3, 71, "'" }, { '*', 3, 72, "*" },
+ { ',', 4, 47, "," }, { ';', 4, 48, ";" },
+ { '.', 4, 52, "." }, { ':', 4, 53, ":" },
+ { '-', 4, 57, "-" }, { '_', 4, 58, "_" },
+ {'\0', 0, 0, "" }
+ },
+ {
+ { '"', 1, 12, "\""},
+ { '&', 1, 32, "&" },
+ { '/', 1, 37, "/" },
+ { '(', 1, 42, "(" },
+ { ')', 1, 47, ")" },
+ { '=', 1, 52, "=" },
+ { '+', 1, 56, "+" }, { '?', 1, 57, "?" },
+ { '`', 1, 61, "`" }, { '@', 1, 62, "@" },
+ { '<', 1, 66, "<" }, { '>', 1, 67, ">" },
+ { '}', 2, 58, "}" }, { ']', 2, 59, "]" },
+ { '~', 2, 63, "~" }, { '^', 2, 64, "^" },
+ { '|', 3, 55, "|" }, {'\\', 3, 56,"\\" },
+ { '{', 3, 60, "{" }, { '[', 3, 61, "[" },
+ {'\'', 3, 71, "'" }, { '*', 3, 72, "*" },
+ { ',', 4, 47, "," }, { ';', 4, 48, ";" },
+ { '.', 4, 52, "." }, { ':', 4, 53, ":" },
+ { '-', 4, 57, "-" }, { '_', 4, 58, "_" },
+ {'\0', 0, 0, "" }
+ }
+ };
+
+ static struct curkey {
+ char *curkeymsg[3];
+ int curkeyrow;
+ int curkeycol;
+ char *curkeysymbol;
+ char *curkeyname;
+ } curkeytab [] = {
+
+ /* A Reset, A Set, VT52 */
+
+ {{"\033[A","\033OA","\033A"}, 0, 56, "UP", "Up arrow" },
+ {{"\033[B","\033OB","\033B"}, 0, 61, "DN", "Down arrow" },
+ {{"\033[D","\033OD","\033D"}, 0, 66, "LT", "Left arrow" },
+ {{"\033[C","\033OC","\033C"}, 0, 71, "RT", "Right arrow"},
+ {{"", "", "" }, 0, 0, "", "" }
+ };
+
+ static struct fnkey {
+ char *fnkeymsg[4];
+ int fnkeyrow;
+ int fnkeycol;
+ char *fnkeysymbol;
+ char *fnkeyname;
+ } fnkeytab [] = {
+
+ /* ANSI-num,ANSI-app,VT52-nu,VT52-ap, r, c, symb name */
+
+ {{"\033OP","\033OP","\033P","\033P" }, 6, 59, "PF1", "PF1" },
+ {{"\033OQ","\033OQ","\033Q","\033Q" }, 6, 63, "PF2", "PF2" },
+ {{"\033OR","\033OR","\033R","\033R" }, 6, 67, "PF3", "PF3" },
+ {{"\033OS","\033OS","\033S","\033S" }, 6, 71, "PF4", "PF4" },
+ {{"7", "\033Ow","7", "\033?w"}, 7, 59, " 7 ", "Numeric 7" },
+ {{"8", "\033Ox","8", "\033?x"}, 7, 63, " 8 ", "Numeric 8" },
+ {{"9", "\033Oy","9", "\033?y"}, 7, 67, " 9 ", "Numeric 9" },
+ {{"-", "\033Om","-", "\033?m"}, 7, 71, " - ", "Minus" },
+ {{"4", "\033Ot","4", "\033?t"}, 8, 59, " 4 ", "Numeric 4" },
+ {{"5", "\033Ou","5", "\033?u"}, 8, 63, " 5 ", "Numeric 5" },
+ {{"6", "\033Ov","6", "\033?v"}, 8, 67, " 6 ", "Numeric 6" },
+ {{",", "\033Ol",",", "\033?l"}, 8, 71, " , ", "Comma" },
+ {{"1", "\033Oq","1", "\033?q"}, 9, 59, " 1 ", "Numeric 1" },
+ {{"2", "\033Or","2", "\033?r"}, 9, 63, " 2 ", "Numeric 2" },
+ {{"3", "\033Os","3", "\033?s"}, 9, 67, " 3 ", "Numeric 3" },
+ {{"0", "\033Op","0", "\033?p"},10, 59," O ","Numeric 0"},
+ {{".", "\033On",".", "\033?n"},10, 67, " . ", "Point" },
+ {{"\015", "\033OM","\015", "\033?M"},10, 71, "ENT", "ENTER" },
+ {{"","","",""}, 0, 0, "", "" }
+ };
+
+ static struct ckey {
+ int ccount;
+ char *csymbol;
+ } ckeytab [] = {
+ { 0, "NUL (CTRL-@ or CTRL-Space)" },
+ { 0, "SOH (CTRL-A)" },
+ { 0, "STX (CTRL-B)" },
+ { 0, "ETX (CTRL-C)" },
+ { 0, "EOT (CTRL-D)" },
+ { 0, "ENQ (CTRL-E)" },
+ { 0, "ACK (CTRL-F)" },
+ { 0, "BEL (CTRL-G)" },
+ { 0, "BS (CTRL-H) (BACK SPACE)" },
+ { 0, "HT (CTRL-I) (TAB)" },
+ { 0, "LF (CTRL-J) (LINE FEED)" },
+ { 0, "VT (CTRL-K)" },
+ { 0, "FF (CTRL-L)" },
+ { 0, "CR (CTRL-M) (RETURN)" },
+ { 0, "SO (CTRL-N)" },
+ { 0, "SI (CTRL-O)" },
+ { 0, "DLE (CTRL-P)" },
+ { 0, "DC1 (CTRL-Q) (X-On)" },
+ { 0, "DC2 (CTRL-R)" },
+ { 0, "DC3 (CTRL-S) (X-Off)" },
+ { 0, "DC4 (CTRL-T)" },
+ { 0, "NAK (CTRL-U)" },
+ { 0, "SYN (CTRL-V)" },
+ { 0, "ETB (CTRL-W)" },
+ { 0, "CAN (CTRL-X)" },
+ { 0, "EM (CTRL-Y)" },
+ { 0, "SUB (CTRL-Z)" },
+ { 0, "ESC (CTRL-[) (ESCAPE)" },
+ { 0, "FS (CTRL-\\ or CTRL-? or CTRL-_)" },
+ { 0, "GS (CTRL-])" },
+ { 0, "RS (CTRL-^ or CTRL-~ or CTRL-`)" },
+ { 0, "US (CTRL-_ or CTRL-?)" }
+ };
+
+ static char *keyboardmenu[] = {
+ "Standard American ASCII layout",
+ "Swedish national layout D47",
+ "Swedish national layout E47",
+ /* add new keyboard layouts here */
+ ""
+ };
+
+ static char *curkeymodes[3] = {
+ "ANSI / Cursor key mode RESET",
+ "ANSI / Cursor key mode SET",
+ "VT52 Mode"
+ };
+
+ static char *fnkeymodes[4] = {
+ "ANSI Numeric mode",
+ "ANSI Application mode",
+ "VT52 Numeric mode",
+ "VT52 Application mode"
+ };
+
+ ledmsg[0] = "L1 L2 L3 L4"; ledseq[0] = "1;2;3;4";
+ ledmsg[1] = " L2 L3 L4"; ledseq[1] = "1;0;4;3;2";
+ ledmsg[2] = " L2 L3"; ledseq[2] = "1;4;;2;3";
+ ledmsg[3] = "L1 L2"; ledseq[3] = ";;2;1";
+ ledmsg[4] = "L1"; ledseq[4] = "1";
+ ledmsg[5] = ""; ledseq[5] = "";
+
+#ifdef UNIX
+ fflush(stdout);
+#endif
+ ed(2);
+ cup(10,1);
+ println("These LEDs (\"lamps\") on the keyboard should be on:");
+ for (i = 0; i <= 5; i++) {
+ cup(10,52); el(0); printf("%s", ledmsg[i]);
+ decll("0");
+ decll(ledseq[i]);
+ cup(12,1); holdit();
+ }
+
+ ed(2);
+ cup(10,1);
+ println("Test of the AUTO REPEAT feature");
+ println("");
+ println("Hold down an alphanumeric key for a while, then push RETURN.");
+ printf("%s", "Auto Repeat OFF: ");
+ rm("?8");
+ inputline(arptstring);
+ if (strlen(arptstring) == 0) println("No characters read!??");
+ else if (strlen(arptstring) == 1) println("OK.");
+ else println("Too many characters read.");
+ println("");
+ println("Hold down an alphanumeric key for a while, then push RETURN.");
+ printf("%s", "Auto Repeat ON: ");
+ sm("?8");
+ inputline(arptstring);
+ if (strlen(arptstring) == 0) println("No characters read!??");
+ else if (strlen(arptstring) == 1) println("Not enough characters read.");
+ else println("OK.");
+ println("");
+ holdit();
+
+ ed(2);
+ cup(5,10);
+ println("Choose keyboard layout:");
+ kblayout = menu(keyboardmenu);
+ if (kblayout) {
+ kblayout--;
+ for (j = 0; natkeytab[kblayout][j].natc != '\0'; j++) {
+ for (i = 0; keytab[i].c != '\0'; i++) {
+ if (keytab[i].row == natkeytab[kblayout][j].natrow &&
+ keytab[i].col == natkeytab[kblayout][j].natcol) {
+ keytab[i].c = natkeytab[kblayout][j].natc;
+ keytab[i].symbol = natkeytab[kblayout][j].natsymbol;
+ break;
+ }
+ }
+ }
+ }
+
+ ed(2);
+ for (i = 0; keytab[i].c != '\0'; i++) {
+ cup(1 + 2 * keytab[i].row, 1 + keytab[i].col);
+ sgr("7");
+ printf("%s", keytab[i].symbol);
+ sgr("");
+ }
+ cup(22,1);
+#ifdef UNIX
+ sgttyNew.sg_flags &= ~CRMOD;
+ sgttyNew.sg_flags &= ~ECHO;
+ stty(0, &sgttyNew);
+#endif
+ inflush();
+ printf("Press each key, both shifted and unshifted. Finish with RETURN:");
+ do { /* while (kbdc != 13) */
+ cup(23,1); kbdc = inchar();
+ cup(23,1); el(0);
+ sprintf(kbds, "%c", kbdc);
+ chrprint(kbds);
+ for (i = 0; keytab[i].c != '\0'; i++) {
+ if (keytab[i].c == kbdc) {
+ cup(1 + 2 * keytab[i].row, 1 + keytab[i].col);
+ printf("%s", keytab[i].symbol);
+ break;
+ }
+ }
+ } while (kbdc != 13);
+#ifdef SARG10
+ inchar(); /* Local hack: Read LF that TOPS-10 adds to CR */
+#endif
+ cup(23,1); el(0);
+
+ for (ckeymode = 0; ckeymode <= 2; ckeymode++) {
+ if (ckeymode) sm("?1");
+ else rm("?1");
+ for (i = 0; curkeytab[i].curkeysymbol[0] != '\0'; i++) {
+ cup(1 + 2 * curkeytab[i].curkeyrow, 1 + curkeytab[i].curkeycol);
+ sgr("7");
+ printf("%s", curkeytab[i].curkeysymbol);
+ sgr("");
+ }
+ cup(20,1); printf("<%s>%20s", curkeymodes[ckeymode], "");
+ cup(22,1); el(0);
+ cup(22,1); printf("%s", "Press each cursor key. Finish with TAB.");
+ for(;;) {
+ cup(23,1);
+ if (ckeymode == 2) rm("?2"); /* VT52 mode */
+ curkeystr = instr();
+ esc("<"); /* ANSI mode */
+ cup(23,1); el(0);
+ cup(23,1); chrprint(curkeystr);
+ if (!strcmp(curkeystr,"\t")) break;
+ for (i = 0; curkeytab[i].curkeysymbol[0] != '\0'; i++) {
+ if (!strcmp(curkeystr,curkeytab[i].curkeymsg[ckeymode])) {
+ sgr("7");
+ printf(" (%s key) ", curkeytab[i].curkeyname);
+ sgr("");
+ cup(1 + 2 * curkeytab[i].curkeyrow,
+ 1 + curkeytab[i].curkeycol);
+ printf("%s", curkeytab[i].curkeysymbol);
+ break;
+ }
+ }
+ if (i == sizeof(curkeytab) / sizeof(struct curkey) - 1) {
+ sgr("7");
+ printf("%s", " (Unknown cursor key) ");
+ sgr("");
+ }
+ }
+ }
+
+ for (fkeymode = 0; fkeymode <= 3; fkeymode++) {
+ for (i = 0; fnkeytab[i].fnkeysymbol[0] != '\0'; i++) {
+ cup(1 + 2 * fnkeytab[i].fnkeyrow, 1 + fnkeytab[i].fnkeycol);
+ sgr("7");
+ printf("%s", fnkeytab[i].fnkeysymbol);
+ sgr("");
+ }
+ cup(20,1); printf("<%s>%20s", fnkeymodes[fkeymode], "");
+ cup(22,1); el(0);
+ cup(22,1); printf("%s", "Press each function key. Finish with TAB.");
+ for(;;) {
+ cup(23,1);
+ if (fkeymode >= 2) rm("?2"); /* VT52 mode */
+ if (fkeymode % 2) deckpam(); /* Application mode */
+ else deckpnm(); /* Numeric mode */
+ fnkeystr = instr();
+ esc("<"); /* ANSI mode */
+ cup(23,1); el(0);
+ cup(23,1); chrprint(fnkeystr);
+ if (!strcmp(fnkeystr,"\t")) break;
+ for (i = 0; fnkeytab[i].fnkeysymbol[0] != '\0'; i++) {
+ if (!strcmp(fnkeystr,fnkeytab[i].fnkeymsg[fkeymode])) {
+ sgr("7");
+ printf(" (%s key) ", fnkeytab[i].fnkeyname);
+ sgr("");
+ cup(1 + 2 * fnkeytab[i].fnkeyrow, 1 + fnkeytab[i].fnkeycol);
+ printf("%s", fnkeytab[i].fnkeysymbol);
+ break;
+ }
+ }
+ if (i == sizeof(fnkeytab) / sizeof(struct fnkey) - 1) {
+ sgr("7");
+ printf("%s", " (Unknown function key) ");
+ sgr("");
+ }
+ }
+ }
+
+#ifdef UNIX
+ sgttyNew.sg_flags |= CRMOD;
+ stty(0, &sgttyNew);
+#endif
+ ed(2);
+ cup(5,1);
+ println("Finally, a check of the ANSWERBACK MESSAGE, which can be sent");
+ println("by pressing CTRL-BREAK. The answerback message can be loaded");
+ println("in SET-UP B by pressing SHIFT-A and typing e.g.");
+ println("");
+ println(" \" H e l l o , w o r l d Return \"");
+ println("");
+ println("(the double-quote characters included). Do that, and then try");
+ println("to send an answerback message with CTRL-BREAK. If it works,");
+ println("the answerback message should be displayed in reverse mode.");
+ println("Finish with a single RETURN.");
+
+#ifdef UNIX
+ sgttyNew.sg_flags &= ~CRMOD;
+ stty(0, &sgttyNew);
+#endif
+ do {
+ cup(17,1);
+ inflush();
+ abmstr = instr();
+ cup(17,1);
+ el(0);
+ chrprint(abmstr);
+ } while (strcmp(abmstr,"\r"));
+
+ ed(2);
+ for (i = 0; i < 32; i++) {
+ cup(1 + (i % 16), 1 + 40 * (i / 16));
+ sgr("7");
+ printf("%s", ckeytab[i].csymbol);
+ sgr("0");
+ }
+ cup(19,1);
+#ifdef UNIX
+ sgttyNew.sg_flags |= CRMOD;
+ stty(0, &sgttyNew);
+#endif
+ println(
+ "Push each CTRL-key TWICE. Note that you should be able to send *all*");
+ println(
+ "CTRL-codes twice, including CTRL-S (X-Off) and CTRL-Q (X-Off)!");
+ println(
+ "Finish with DEL (also called DELETE or RUB OUT), or wait 1 minute.");
+#ifdef UNIX
+#ifdef SIII
+ sgttyNew.sg_flags &= ~CBREAK;
+ stty(0, &sgttyNew);
+#endif
+ sgttyNew.sg_flags |= RAW;
+ stty(0, &sgttyNew);
+#endif
+ ttybin(1);
+#ifdef SARG20
+ page(0); /* Turn off all character processing at input */
+ superbin(1); /* Turn off ^C (among others). Keep your fingers crossed!! */
+#endif
+ do {
+ cup(23,1); kbdc = inchar();
+ cup(23,1); el(0);
+ if (kbdc < 32) printf(" %s", ckeytab[kbdc].csymbol);
+ else {
+ sprintf(kbds, "%c", kbdc);
+ chrprint(kbds);
+ printf("%s", " -- not a CTRL key");
+ }
+ if (kbdc < 32) ckeytab[kbdc].ccount++;
+ if (ckeytab[kbdc].ccount == 2) {
+ cup(1 + (kbdc % 16), 1 + 40 * (kbdc / 16));
+ printf("%s", ckeytab[kbdc].csymbol);
+ }
+ } while (kbdc != '\177');
+#ifdef UNIX
+ sgttyNew.sg_flags &= ~RAW;
+ sgttyNew.sg_flags |= ECHO;
+ stty(0, &sgttyNew);
+#ifdef SIII
+ sgttyNew.sg_flags |= CBREAK;
+ stty(0, &sgttyNew);
+#endif
+#endif
+ ttybin(0);
+#ifdef SARG20
+ superbin(0); /* Puuuh! We made it!? */
+ page(1); /* Back to normal input processing */
+ ttybin(1); /* This must be the mode for DEC20 */
+#endif
+ cup(24,1);
+ okflag = 1;
+ for (i = 0; i < 32; i++) if (ckeytab[i].ccount < 2) okflag = 0;
+ if (okflag) printf("%s", "OK. ");
+ else printf("%s", "You have not been able to send all CTRL keys! ");
+ holdit();
+}
+
+tst_reports() {
+ /* Test of:
+ <ENQ> (AnswerBack Message)
+ SM RM (Set/Reset Mode) - LineFeed / Newline
+ DSR (Device Status Report)
+ DA (Device Attributes)
+ DECREQTPARM (Request Terminal Parameters)
+ */
+
+ int parity, nbits, xspeed, rspeed, clkmul, flags;
+ int i, reportpos;
+ char *report, *report2;
+ static char *attributes[][2] = {
+ { "\033[?1;0c", "No options (vanilla VT100)" },
+ { "\033[?1;1c", "VT100 with STP" },
+ { "\033[?1;2c", "VT100 with AVO (could be a VT102)" },
+ { "\033[?1;3c", "VT100 with STP and AVO" },
+ { "\033[?1;4c", "VT100 with GPO" },
+ { "\033[?1;5c", "VT100 with STP and GPO" },
+ { "\033[?1;6c", "VT100 with AVO and GPO" },
+ { "\033[?1;7c", "VT100 with STP, AVO and GPO" },
+ { "\033[?1;11c", "VT100 with PP and AVO" },
+ { "\033[?1;15c", "VT100 with PP, GPO and AVO" },
+ { "\033[?4;2c", "VT132 with AVO" },
+ { "\033[?4;3c", "VT132 with AVO and STP" },
+ { "\033[?4;6c", "VT132 with GPO and AVO" },
+ { "\033[?4;7c", "VT132 with GPO, AVO, and STP" },
+ { "\033[?4;11c", "VT132 with PP and AVO" },
+ { "\033[?4;15c", "VT132 with PP, GPO and AVO" },
+ { "\033[?7c", "VT131" },
+ { "\033[?12;5c", "VT125" }, /* VT125 also has ROM version */
+ { "\033[?12;7c", "VT125 with AVO" }, /* number, so this won't work */
+ { "\033[?5;0c", "VK100 (GIGI)" },
+ { "\033[?5c", "VK100 (GIGI)" },
+ { "", "" }
+ };
+
+#ifdef UNIX
+ sgttyNew.sg_flags &= ~ECHO;
+ stty(0, &sgttyNew);
+#endif
+ cup(5,1);
+ println("This is a test of the ANSWERBACK MESSAGE. (To load the A.B.M.");
+ println("see the TEST KEYBOARD part of this program). Below here, the");
+ println("current answerback message in your terminal should be");
+ println("displayed. Finish this test with RETURN.");
+ cup(10,1);
+ inflush();
+ printf("%c", 5); /* ENQ */
+ report = instr();
+ cup(10,1);
+ chrprint(report);
+ cup(12,1);
+ holdit();
+
+ ed(2);
+ cup(1,1);
+ println("Test of LineFeed/NewLine mode.");
+ cup(3,1);
+ sm("20");
+#ifdef UNIX
+ sgttyNew.sg_flags &= ~CRMOD;
+ stty(0, &sgttyNew);
+#endif
+ printf("NewLine mode set. Push the RETURN key: ");
+ report = instr();
+ cup(4,1);
+ el(0);
+ chrprint(report);
+ if (!strcmp(report, "\015\012")) printf(" -- OK");
+ else printf(" -- Not expected");
+ cup(6,1);
+ rm("20");
+ printf("NewLine mode reset. Push the RETURN key: ");
+ report = instr();
+ cup(7,1);
+ el(0);
+ chrprint(report);
+ if (!strcmp(report, "\015")) printf(" -- OK");
+ else printf(" -- Not expected");
+ cup(9,1);
+#ifdef UNIX
+ sgttyNew.sg_flags |= CRMOD;
+ stty(0, &sgttyNew);
+#endif
+ holdit();
+
+ ed(2);
+ cup(1,1);
+ printf("Test of Device Status Report 5 (report terminal status).");
+ cup(2,1);
+ dsr(5);
+ report = instr();
+ cup(2,1);
+ el(0);
+ printf("Report is: ");
+ chrprint(report);
+ if (!strcmp(report,"\033[0n")) printf(" -- means \"TERMINAL OK\"");
+ else if (!strcmp(report,"\033[3n")) printf(" -- means \"TERMINAL OK\"");
+ else printf(" -- Unknown response!");
+
+ cup(4,1);
+ println("Test of Device Status Report 6 (report cursor position).");
+ cup(5,1);
+ dsr(6);
+ report = instr();
+ cup(5,1);
+ el(0);
+ printf("Report is: ");
+ chrprint(report);
+ if (!strcmp(report,"\033[5;1R")) printf(" -- OK");
+ else printf(" -- Unknown response!");
+
+ cup(7,1);
+ println("Test of Device Attributes report (what are you)");
+ cup(8,1);
+ da(0);
+ report = instr();
+ cup(8,1);
+ el(0);
+ printf("Report is: ");
+ chrprint(report);
+ for (i = 0; *attributes[i][0] != '\0'; i++) {
+ if (!strcmp(report,attributes[i][0])) break;
+ }
+ if (*attributes[i][0] == '\0')
+ printf(" -- Unknown response, refer to the manual");
+ else {
+ printf(" -- means %s", attributes[i][1]);
+ if (i) {
+ cup(9,1);
+ println("Legend: STP = Processor Option");
+ println(" AVO = Advanced Video Option");
+ println(" GPO = Graphics Processor Option");
+ println(" PP = Printer Port");
+ }
+ }
+
+ cup(14,1);
+ println("Test of the \"Request Terminal Parameters\" feature, argument 0.");
+ cup(15,1);
+ decreqtparm(0);
+ report = instr();
+ cup(15,1);
+ el(0);
+ printf("Report is: ");
+ chrprint(report);
+ if (strlen(report) < 16
+ || report[0] != '\033'
+ || report[1] != '['
+ || report[2] != '2'
+ || report[3] != ';')
+ println(" -- Bad format");
+ else {
+ reportpos = 4;
+ parity = scanto(report, &reportpos, ';');
+ nbits = scanto(report, &reportpos, ';');
+ xspeed = scanto(report, &reportpos, ';');
+ rspeed = scanto(report, &reportpos, ';');
+ clkmul = scanto(report, &reportpos, ';');
+ flags = scanto(report, &reportpos, 'x');
+ if (parity == 0 || nbits == 0 || clkmul == 0) println(" -- Bad format");
+ else println(" -- OK");
+ printf(
+ "This means: Parity %s, %s bits, xmitspeed %s, recvspeed %s.\n",
+ lookup(paritytable, parity),
+ lookup(nbitstable, nbits),
+ lookup(speedtable, xspeed),
+ lookup(speedtable, rspeed));
+ printf("(CLoCk MULtiplier = %d, STP option flags = %d)\n", clkmul, flags);
+ }
+
+ cup(19,1);
+ println("Test of the \"Request Terminal Parameters\" feature, argument 1.");
+ cup(20,1);
+ decreqtparm(1); /* Does the same as decreqtparm(0), reports "3" */
+ report2 = instr();
+ cup(20,1);
+ el(0);
+ printf("Report is: ");
+ chrprint(report2);
+ if (strlen(report2) < 3
+ || report2[2] != '3')
+ println(" -- Bad format");
+ else {
+ report2[2] = '2';
+ if (!strcmp(report,report2)) println(" -- OK");
+ else println(" -- Bad format");
+ }
+ cup(24,1);
+ holdit();
+#ifdef UNIX
+ sgttyNew.sg_flags |= ECHO;
+ stty(0, &sgttyNew);
+#endif
+}
+
+tst_vt52() {
+
+ static struct rtabl {
+ char *rcode;
+ char *rmsg;
+ } resptable[] = {
+ { "\033/K", " -- OK (means Standard VT52)" },
+ { "\033/Z", " -- OK (means VT100 emulating VT52)" },
+ { "", " -- Unknown response"}
+ };
+
+ int i,j;
+ char *response;
+
+ rm("?2"); /* Reset ANSI (VT100) mode, Set VT52 mode */
+ esc("H"); /* Cursor home */
+ esc("J"); /* Erase to end of screen */
+ esc("H"); /* Cursor home */
+ for (i = 0; i <= 23; i++) {
+ for (j = 0; j <= 9; j++)
+ printf("%s", "FooBar ");
+ println("Bletch");
+ }
+ esc("H"); /* Cursor home */
+ esc("J"); /* Erase to end of screen */
+
+ vt52cup(7,47);
+ printf("nothing more.");
+ for (i = 1; i <= 10; i++) printf("THIS SHOULD GO AWAY! ");
+ for (i = 1; i <= 5; i++) {
+ vt52cup(1,1);
+ printf("%s", "Back scroll (this should go away)");
+ esc("I"); /* Reverse LineFeed (with backscroll!) */
+ }
+ vt52cup(12,60);
+ esc("J"); /* Erase to end of screen */
+ for (i = 2; i <= 6; i++) {
+ vt52cup(i,1);
+ esc("K"); /* Erase to end of line */
+ }
+
+ for (i = 2; i <= 23; i++) {
+ vt52cup(i,70); printf("%s", "**Foobar");
+ }
+ vt52cup(23,10);
+ for (i = 23; i >= 2; i--) {
+ printf("%s", "*");
+ printf("%c", 8); /* BS */
+ esc("I"); /* Reverse LineFeed (LineStarve) */
+ }
+ vt52cup(1,70);
+ for (i = 70; i >= 10; i--) {
+ printf("%s", "*");
+ esc("D"); esc("D"); /* Cursor Left */
+ }
+ vt52cup(24,10);
+ for (i = 10; i <= 70; i++) {
+ printf("%s", "*");
+ printf("%c", 8); /* BS */
+ esc("C"); /* Cursor Right */
+ }
+ vt52cup(2,11);
+ for (i = 2; i <= 23; i++) {
+ printf("%s", "!");
+ printf("%c", 8); /* BS */
+ esc("B"); /* Cursor Down */
+ }
+ vt52cup(23,69);
+ for (i = 23; i >= 2; i--) {
+ printf("%s", "!");
+ printf("%c", 8); /* BS */
+ esc("A"); /* Cursor Up */
+ }
+ for (i = 2; i <= 23; i++) {
+ vt52cup(i,71);
+ esc("K"); /* Erase to end of line */
+ }
+
+ vt52cup(10,16);
+ printf("%s", "The screen should be cleared, and have a centered");
+ vt52cup(11,16);
+ printf("%s", "rectangle of \"*\"s with \"!\"s on the inside to the");
+ vt52cup(12,16);
+ printf("%s", "left and right. Only this, and");
+ vt52cup(13,16);
+ holdit();
+
+ esc("H"); /* Cursor home */
+ esc("J"); /* Erase to end of screen */
+ printf("%s", "This is the normal character set:");
+ for (j = 0; j <= 1; j++) {
+ vt52cup(3 + j, 16);
+ for (i = 0; i <= 47; i++)
+ printf("%c", 32 + i + 48 * j);
+ }
+ vt52cup(6,1);
+ printf("%s", "This is the special graphics character set:");
+ esc("F"); /* Select Special Graphics character set */
+ for (j = 0; j <= 1; j++) {
+ vt52cup(8 + j, 16);
+ for (i = 0; i <= 47; i++)
+ printf("%c", 32 + i + 48 * j);
+ }
+ esc("G"); /* Select ASCII character set */
+ vt52cup(12,1);
+ holdit();
+
+ esc("H"); /* Cursor home */
+ esc("J"); /* Erase to end of screen */
+ println("Test of terminal response to IDENTIFY command");
+ esc("Z"); /* Identify */
+ response = instr();
+ println("");
+ printf("Response was");
+ esc("<"); /* Enter ANSI mode (VT100 mode) */
+ chrprint(response);
+ for(i = 0; resptable[i].rcode[0] != '\0'; i++)
+ if (!strcmp(response, resptable[i].rcode))
+ break;
+ printf("%s", resptable[i].rmsg);
+ println("");
+ println("");
+ holdit();
+}
+
+tst_insdel() {
+
+ /* Test of:
+ SM/RM(4) (= IRM (Insertion/replacement mode))
+ ICH (Insert Character)
+ DCH (Delete character)
+ IL (Insert line)
+ DL (Delete line)
+ */
+
+ int i, row, col, sw, dblchr, scr132;
+
+ for(scr132 = 0; scr132 <= 1; scr132++) {
+ if (scr132) { sm("?3"); sw = 132; }
+ else { rm("?3"); sw = 80; }
+ ed(2);
+ cup(1,1);
+ for (row=1; row<=24; row++) {
+ cup(row,1);
+ for (col=1; col<=sw; col++)
+ printf("%c", 'A'-1+row);
+ }
+ cup(4,1);
+ printf("Screen accordion test (Insert & Delete Line). "); holdit();
+ ri(); el(2);
+ decstbm( 2,23);
+ sm("?6");
+ cup(1,1);
+ for (row=1; row<=24; row++) {
+ il(row);
+ dl(row);
+ }
+ rm("?6");
+ decstbm( 0, 0);
+ cup(2,1);
+ printf(
+ "Top line: A's, bottom line: X's, this line, nothing more. ");
+ holdit();
+ cup(2,1); ed(0);
+ cup(1,2);
+ printf("B");
+ cub(1);
+ sm("4");
+ for (col=2; col<=sw-1; col++)
+ printf("*");
+ rm("4");
+ cup(4,1);
+ printf("Test of 'Insert Mode'. The top line should be 'A*** ... ***B'. ");
+ holdit(); ri(); el(2);
+ cup(1,2);
+ dch(sw-2);
+ cup(4,1);
+ printf("Test of 'Delete Character'. The top line should be 'AB'. ");
+ holdit();
+
+ for(dblchr = 1; dblchr <= 2; dblchr++) {
+ ed(2);
+ for (row=1; row<=24; row++) {
+ cup(row,1);
+ if (dblchr == 2) decdwl();
+ for (col=1; col<=sw/dblchr; col++)
+ printf("%c", 'A'-1+row);
+ cup(row,sw/dblchr-row);
+ dch(row);
+ }
+ cup(4,1);
+ println("The right column should be staggered ");
+ printf("by one. ");
+ holdit();
+ }
+ ed(2);
+ cup(1,1);
+ println("If your terminal has the ANSI 'Insert Character' function");
+ println("(the VT102 does not), then you should see a line like this");
+ println(" A B C D E F G H I J K L M N O P Q R S T U V W X Y Z");
+ println("below:");
+ println("");
+ for (i = 'Z'; i >= 'A'; i--) {
+ printf("%c\010",i);
+ ich(2);
+ }
+ cup(10,1);
+ holdit();
+
+ if (sw == 132) rm("?3");
+ }
+}
+
+dch(pn) int pn; { brc(pn, 'P'); } /* Delete character */
+ich(pn) int pn; { brc(pn, '@'); } /* Insert character -- not in VT102 */
+dl(pn) int pn; { brc(pn, 'M'); } /* Delete line */
+il(pn) int pn; { brc(pn, 'L'); } /* Insert line */
+
+/* Test of some known VT100 bugs and misfeatures */
+
+tst_bugs() {
+
+ int i, menuchoice;
+
+ static char *menutable[] = {
+ "Exit to main menu",
+ "Bug A: Smooth scroll to jump scroll",
+ "Bug B: Scrolling region",
+ "Bug C: Wide to narrow screen",
+ "Bug D: Narrow to wide screen",
+ "Bug E: Cursor move from double- to single-wide line",
+ "Bug F: Column mode escape sequence",
+ "Wrap around with cursor addressing",
+ "Erase right half of double width lines",
+ "Funny scroll regions",
+ /* Add more here */
+ ""
+ };
+
+ static char *hmsg[] = {
+ "Test of known bugs in the DEC VT100 series. The numbering of some of",
+ "the bugs (A-F) refers to the article 'VT100 MAGIC' by Sami Tabih in",
+ "the 'Proceedings of the DEC Users Society' at St. Louis, Missouri, May",
+ "1983. To understand some of the tests, you have to look at the source",
+ "code or the article. Of course, a good VT100-compatible terminal",
+ "should not have these bugs (or have some means of disabling them)! If",
+ "a bug appears, you might want to RESET the terminal before continuing",
+ "the test. There is a test of the RESET function in the main menu.",
+ "" };
+
+ do {
+ ed(2); cup(1,1);
+ for (i = 0; *hmsg[i]; i++) println(hmsg[i]);
+ println("");
+ println(" Choose bug test number:");
+ menuchoice = menu(menutable);
+ switch (menuchoice) {
+ case 1: bug_a(); break;
+ case 2: bug_b(); break;
+ case 3: bug_c(); break;
+ case 4: bug_d(); break;
+ case 5: bug_e(); break;
+ case 6: bug_f(); break;
+ case 7: bug_w(); break;
+ case 8: bug_l(); break;
+ case 9: bug_s(); break;
+ }
+ } while (menuchoice);
+}
+
+/* Bug A: Smooth scroll to jump scroll */
+
+bug_a() {
+ int i;
+
+ cup (10, 1);
+ println("This is a test of the VT100 'Scroll while toggle softscroll'");
+ println("bug. The cursor may disappear, or move UP the screen, or");
+ println("multiple copies of some lines may appear.");
+ holdit();
+
+ /* Invoke the bug */
+
+ esc ("[24H"); /* Simplified cursor movement */
+ rm("?4"); for (i = 1; i <= 20; i++) printf("\n");
+ sm("?4"); for (i = 1; i <= 10; i++) printf("\n");
+ rm("?4"); for (i = 1; i <= 5; i++) printf("\n");
+
+ /* That should be enough to show the bug. But we'll try another way: */
+ sm ("?4"); /* Set soft scroll */
+ nel (); /* "NextLine", move down */
+ rm ("?4"); /* Reset soft scroll */
+ nel (); /* "NextLine", move down */
+ for (i = 1; i <= 10; i++) { /* Show the bug */
+ printf ("Softscroll bug test, line %d. ", i);
+ holdit();
+ }
+ println("That should have been enough to show the bug, if present.");
+ holdit();
+}
+
+/* Bug B: Scrolling region */
+
+bug_b() {
+ char c;
+
+ decaln();
+ cup( 1,1); el(0);
+ printf("Line 11 should be double-wide, line 12 should be cleared.");
+ cup( 2,1); el(0);
+ printf("Then, the letters A-P should be written at the beginning");
+ cup( 3,1); el(0);
+ printf("of lines 12-24, and the empty line and A-E are scrolled away.");
+ cup( 4,1); el(0);
+ printf("If the bug is present, some lines are confused, look at K-P.");
+ cup(11,1); decdwl();
+ decstbm(12,24);
+ cup(12,1); el(0); printf("Here we go... "); holdit();
+ cup(12,1); ri(); /* Bug comes here */
+ for (c = 'A'; c <= 'P'; c++) printf("%c\n",c); /* Bug shows here */
+ holdit();
+ decstbm(0,0); /* No scr. region */
+}
+
+/* Bug C: Wide to narrow screen */
+
+bug_c() {
+ sm("?3"); /* 132 column mode */
+ cup(1,81);
+ rm("?3"); /* 80 column mode */
+ cup(12,5);
+ printf("Except for this line, the screen should be blank. ");
+ holdit();
+}
+
+/* Bug D: Narrow to wide screen */
+
+bug_d() {
+ int i;
+ char result;
+ /* Make the bug appear */
+ do {
+ cup(14,1);
+
+ /* The original code in the article says
+ * PRINT ESC$; "[13;1H"; CHR$(10%);
+ * but I guess a cup(14,1); would do.
+ * (To output a pure LF might be tricky).
+ */
+
+ sm("?3"); /* Make the bug visible */
+ cup(1,9); decdwl();
+ println("You should see blinking text at the bottom line.");
+ cup(3,9); decdwl();
+ println("Enter 0 to exit, 1 to try to invoke the bug again.");
+ cup(24,9); decdwl(); sgr("1;5;7");
+ printf("If you can see this then the bug did not appear."); sgr("");
+ cup(4,9); decdwl();
+ result = inchar(); readnl();
+ rm("?3");
+ } while (result == '1');
+ sm("?4"); /* Syrup scroll */
+ cup(23,1);
+ for (i = 1; i <= 5; i++)
+ println("If the bug is present, this should make things much worse!");
+ holdit();
+ rm("?4"); /* Jump scroll */
+}
+
+/* Bug E: Cursor move from double- to single-wide line */
+
+bug_e() {
+ int i;
+ static char *rend[2] = { "\033[m", "\033[7m" };
+ sm("?3");
+ cup(1,1); decdwl();
+ println("This test should put an 'X' at line 3 column 100.");
+ for (i = 1; i <= 12; i++) printf("1234567890%s",rend[i & 1]);
+ cup(1,1); /* The bug appears when we jump from a dobule-wide line */
+ cup(3,100); /* to a single-wide line, column > 66. */
+ printf("X");
+ cup(4, 66); printf("! !");
+ cup(5,1);
+ printf("--------------------------- The 'X' should NOT be above here -");
+ printf("---+------------ but above here -----+");
+ cup(10,1); decdwl(); holdit();
+ rm("?3");
+}
+
+/* Bug F: Column mode escape sequence */
+
+bug_f() {
+ int i, row, col;
+
+ /*
+ * VT100 "toggle origin mode, forget rest" bug. If you try to set
+ * (or clear) parameters and one of them is the "origin mode"
+ * ("?6") parameter, parameters that appear after the "?6"
+ * remain unaffected. This is also true on CIT-101 terminals.
+ */
+ sm ("?5"); /* Set reverse mode */
+ sm ("?3"); /* Set 132 column mode */
+ println("Test VT100 'Toggle origin mode, forget rest' bug, part 1.");
+ println("The screen should be in reverse, 132 column mode.");
+ holdit();
+ ed (2);
+ rm ("?6;5;3"); /* Reset (origin, reverse, 132 col) */
+ println("Test VT100 'Toggle origin mode, forget rest' bug, part 2.\n");
+ println("The screen should be in non-reverse, 80 column mode.");
+ holdit();
+}
+
+ /* Bug W:
+ * The dreaded "wraparound" bug! You CUP to col 80, write a char,
+ * CUP to another line in col 80, write a char. And the brain-damaged
+ * terminal thinks that "Hokay, so he's written a char in col 80, so
+ * I stay in col 80 and wait for next character. Let's see now, here
+ * comes another character, and I'm still in col 80, so I must make
+ * a NewLine first." -- It doesn't clear that "still in col 80" flag
+ * on a CUP. Argh!
+ */
+
+bug_w() {
+ int row, col;
+
+ cup (16,1);
+ println(" This illustrates the \"wrap around bug\" which exists on a");
+ println(" standard VT100. At the top of the screen there should be");
+ println(" a row of +'s, and the rightmost column should be filled");
+ println(" with *'s. But if the bug is present, some of the *'s may");
+ println(" be placed in other places, e.g. in the leftmost column,");
+ println(" and the top line of +'s may be scrolled away.");
+
+ cup(1,1);
+ for (col = 1; col <= 79; col++)
+ printf ("+");
+ for (row = 1; row <= 24; row++) {
+ hvp (row, 80);
+ printf ("*");
+ }
+ cup(24,1);
+ holdit();
+}
+
+ /* Bug L:
+ * Check if the right half of double-width lines comes back
+ * when a line is first set to single-width, filled with stuff,
+ * set to double-width, and finally reset to single-width.
+ *
+ * A VT100 has this misfeature, and many others. Foo!
+ */
+
+bug_l() {
+ cup(15, 1);
+ printf("This-is-a-long-line-This-is-a-long-line-");
+ printf("This-is-a-long-line-This-is-a-long-line-");
+ cup(1, 1);
+ printf("This is a test of what happens to the right half of double-width");
+ println(" lines.");
+ printf("A common misfeature is that the right half does not come back");
+ println(" when a long");
+ printf("single-width line is set to double-width and then reset to");
+ println(" single-width.");
+
+ cup(5, 1);
+ println("Now the line below should contain 80 characters in single width.");
+ holdit();
+ cup(15, 1); decdwl();
+ cup(8, 1);
+ println("Now the line below should contain 40 characters in double width.");
+ holdit();
+ cup(15, 1); decswl();
+ cup(11, 1);
+ println("Now the line below should contain 80 characters in single width.");
+ holdit();
+
+ /* ...and in 132 column mode */
+
+ sm("?3");
+ ed(2);
+ cup(15, 1);
+ printf("This-is-a-long-line-This-is-a-long-line-");
+ printf("This-is-a-long-line-This-is-a-long-line-");
+ printf("This-is-a-long-line-This-is-a-long-line-");
+ printf("ending-here-");
+
+ cup(1, 1);
+ printf("This is the same test in 132 column mode.");
+
+ cup(5, 1);
+ println("Now the line below should contain 132 characters in single width.");
+ holdit();
+ cup(15, 1); decdwl();
+ cup(8, 1);
+ println("Now the line below should contain 66 characters in double width.");
+ holdit();
+ cup(15, 1); decswl();
+ cup(11, 1);
+ println("Now the line below should contain 132 characters in single width.");
+ holdit();
+ rm("?3");
+}
+
+bug_s() {
+ int i;
+ decstbm(20,10); /* 20-10=-10, < 2, so no scroll region. */
+ cup(1,1);
+ for (i=1; i<=20; i++)
+ printf("This is 20 lines of text (line %d), no scroll region.\n", i);
+ holdit();
+ ed(2);
+ decstbm(0,1); /* Should be interpreted as decstbm(1,1) = none */
+ cup(1,1);
+ for (i=1; i<=20; i++)
+ printf("This is 20 lines of text (line %d), no scroll region.\n", i);
+ holdit();
+ decstbm(0,0); /* No scroll region (just in case...) */
+}
+
+tst_rst() {
+
+ /*
+ * Test of
+ * - RIS (Reset to Initial State)
+ * - DECTST (invoke terminal test)
+ */
+
+ cup(10,1);
+ printf ("The terminal will now be RESET. ");
+ holdit();
+ ris();
+#ifdef UNIX
+ fflush(stdout);
+#endif
+ zleep(5000); /* Wait 5.0 seconds */
+ cup(10,1);
+ println("The terminal is now RESET. Next, the built-in confidence test");
+ printf("%s", "will be invoked. ");
+ holdit();
+ ed(2);
+ dectst(1);
+#ifdef UNIX
+ fflush(stdout);
+#endif
+ zleep(5000); /* Wait 5.0 seconds */
+ cup(10,1);
+ println("If the built-in confidence test found any errors, a code");
+ printf("%s", "is visible above. ");
+ holdit();
+}
+
+initterminal(pn) int pn; {
+
+#ifdef UNIX
+ if (pn==0) {
+ fflush(stdout);
+ gtty(0,&sgttyOrg);
+ gtty(0,&sgttyNew);
+ sgttyNew.sg_flags |= CBREAK;
+ }
+ else {
+ fflush(stdout);
+ inflush();
+ sleep(2);
+ sgttyNew.sg_flags = sgttyOrg.sg_flags | CBREAK;
+ }
+ stty(0,&sgttyNew);
+#ifdef SIII
+ close(2);
+ open(_PATH_TTY,O_RDWR|O_NDELAY);
+#endif
+#endif
+#ifdef SARG10
+ /* Set up neccesary TOPS-10 terminal parameters */
+
+ trmop(02041, `VT100`); /* tty type vt100 */
+ trmop(02002, 0); /* tty no tape */
+ trmop(02003, 0); /* tty lc */
+ trmop(02005, 1); /* tty tab */
+ trmop(02010, 1); /* tty no crlf */
+ trmop(02020, 0); /* tty no tape */
+ trmop(02021, 1); /* tty page */
+ trmop(02025, 0); /* tty blanks */
+ trmop(02026, 1); /* tty no alt */
+ trmop(02040, 1); /* tty defer */
+#endif
+#ifdef SARG20
+ ttybin(1); /* set line to binary mode */
+#endif
+ /* Set up my personal prejudices */
+
+ esc("<"); /* Enter ANSI mode (if in VT52 mode) */
+ rm("?1"); /* cursor keys normal */
+ rm("?3"); /* 80 col mode */
+ rm("?4"); /* Jump scroll */
+ rm("?5"); /* Normal screen */
+ rm("?6"); /* Absolute origin mode */
+ sm("?7"); /* Wrap around on */
+ rm("?8"); /* Auto repeat off */
+ decstbm(0,0); /* No scroll region */
+ sgr("0"); /* Normal character attributes */
+
+}
+
+bye () {
+ /* Force my personal prejudices upon the poor luser */
+
+ esc("<"); /* Enter ANSI mode (if in VT52 mode) */
+ rm("?1"); /* cursor keys normal */
+ rm("?3"); /* 80 col mode */
+ rm("?5"); /* Normal screen */
+ rm("?6"); /* Absolute origin mode */
+ sm("?7"); /* Wrap around on */
+ sm("?8"); /* Auto repeat on */
+ decstbm(0,0); /* No scroll region */
+ sgr("0"); /* Normal character attributes */
+
+ /* Say goodbye */
+
+ ed(2);
+ cup(12,30);
+ printf("That's all, folks!\n");
+ printf("\n\n\n");
+ inflush();
+#ifdef SARG20
+ ttybin(0); /* reset line to normal mode */
+#endif
+#ifdef UNIX
+ stty(0,&sgttyOrg);
+#endif
+ exit();
+}
+
+#ifdef UNIX
+onbrk() {
+ signal(SIGINT, (void *)onbrk);
+ if (reading)
+ brkrd = 1;
+ else
+ longjmp(intrenv, 1);
+}
+
+onterm() {
+ signal(SIGTERM, (void *)onterm);
+ longjmp(intrenv, 1);
+}
+#endif
+
+holdit() {
+ inflush();
+ printf("Push <RETURN>");
+ readnl();
+}
+
+readnl() {
+#ifdef UNIX
+ char ch;
+ fflush(stdout);
+ brkrd = 0;
+ reading = 1;
+ do { read(STDIN_FILENO,&ch,1); } while(ch != '\n' && !brkrd);
+ if (brkrd)
+ kill(getpid(), SIGTERM);
+ reading = 0;
+#endif
+#ifdef SARG10
+ while (getchar() != '\n')
+ ;
+#endif
+#ifdef SARG20
+ while (getchar() != '\n')
+ ;
+#endif
+}
+
+scanto(str, pos, toc) char *str; int *pos; char toc; {
+ char c;
+ int result = 0;
+
+ while (toc != (c = str[(*pos)++])) {
+ if (isdigit(c)) result = result * 10 + c - '0';
+ else break;
+ }
+ if (c == toc) return(result);
+ else return(0);
+}
+
+char *lookup(t, k) struct table t[]; int k; {
+
+ int i;
+ for (i = 0; t[i].key != -1; i++) {
+ if (t[i].key == k) return(t[i].msg);
+ }
+ return("BAD VALUE");
+}
+
+menu(table) char *table[]; {
+
+ int i, tablesize, choice;
+ char c;
+ char storage[80];
+ char *s = storage;
+ println("");
+ tablesize = 0;
+ for (i = 0; *table[i] != '\0'; i++) {
+ printf(" %d. %s\n", i, table[i]);
+ tablesize++;
+ }
+ tablesize--;
+
+ printf("\n Enter choice number (0 - %d): ", tablesize);
+ for(;;) {
+ inputline(s);
+ choice = 0;
+ while (c = *s++) choice = 10 * choice + c - '0';
+ if (choice >= 0 && choice <= tablesize) {
+ ed(2);
+ return (choice);
+ }
+ printf(" Bad choice, try again: ");
+ }
+}
+
+chrprint (s) char *s; {
+
+ int i;
+
+ printf(" ");
+ sgr("7");
+ printf(" ");
+ for (i = 0; s[i] != '\0'; i++) {
+ if (s[i] <= ' ' || s[i] == '\177')
+ printf("<%d> ", s[i]);
+ else printf("%c ", s[i]);
+ }
+ sgr("");
+}
diff --git a/usr.sbin/pcvt/vttest/vttest.1 b/usr.sbin/pcvt/vttest/vttest.1
new file mode 100644
index 0000000..31f73f5
--- /dev/null
+++ b/usr.sbin/pcvt/vttest/vttest.1
@@ -0,0 +1,21 @@
+.\" $FreeBSD$
+.\"
+.Dd January 16, 2001
+.Dt VTTEST 1
+.Os
+.Sh NAME
+.Nm vttest
+.Nd "test VT100-type terminal"
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+The
+.Nm
+utility is a program designed to test the functionality of a
+.Tn VT100
+terminal
+(or emulator thereof).
+It tests both display (escape sequence handling) and keyboard.
+.Pp
+The program is menu\-driven and contains full on\-line operating
+instructions.
diff --git a/usr.sbin/periodic/Makefile b/usr.sbin/periodic/Makefile
new file mode 100644
index 0000000..875d078
--- /dev/null
+++ b/usr.sbin/periodic/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+SCRIPTS=periodic.sh
+MAN= periodic.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/periodic/periodic.8 b/usr.sbin/periodic/periodic.8
new file mode 100644
index 0000000..b350ee0
--- /dev/null
+++ b/usr.sbin/periodic/periodic.8
@@ -0,0 +1,252 @@
+.\" Copyright (c) 1997 FreeBSD, Inc.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd November 28, 2001
+.Os
+.Dt PERIODIC 8
+.Sh NAME
+.Nm periodic
+.Nd run periodic system functions
+.Sh SYNOPSIS
+.Nm
+.Ar directory ...
+.Sh DESCRIPTION
+The
+.Nm
+program is intended to be called by
+.Xr cron 8
+to execute shell scripts
+located in the specified directory.
+.Pp
+One or more of the following arguments must be specified:
+.Bl -tag -width ".Pa monthly"
+.It Pa daily
+Perform the standard daily periodic executable run.
+This usually occurs early in the morning (local time).
+.It Pa weekly
+Perform the standard weekly periodic executable run.
+This usually occurs on Sunday mornings.
+.It Pa monthly
+Perform the standard monthly periodic executable run.
+This usually occurs on the first day of the month.
+.It Pa security
+Perform the standard daily security checks.
+This is usually spawned by the
+.Pa daily
+run.
+.It Ar path
+An arbitrary directory containing a set of executables to be run.
+.El
+.Pp
+If an argument is an absolute directory name it is used as is, otherwise
+it is searched for under
+.Pa /etc/periodic
+and any other directories specified by the
+.Va local_periodic
+setting in
+.Xr periodic.conf 5
+(see below).
+.Pp
+The
+.Nm
+program will run each executable file in the directory or directories
+specified.
+If a file does not have the executable bit set, it is silently ignored.
+.Pp
+Each script is required to exit with one of the following values:
+.Bl -tag -width 4n
+.It 0
+The script has produced nothing notable in its output.
+The
+.Ao Ar basedir Ac Ns Va _show_success
+variable controls the masking of this output.
+.It 1
+The script has produced some notable information in its output.
+The
+.Ao Ar basedir Ac Ns Va _show_info
+variable controls the masking of this output.
+.It 2
+The script has produced some warnings due to invalid configuration settings.
+The
+.Ao Ar basedir Ac Ns Va _show_badconfig
+variable controls the masking of this output.
+.It >2
+The script has produced output that must not be masked.
+.El
+.Pp
+If the relevant variable (where
+.Aq Ar basedir
+is the base directory in which the script resides) is set to
+.Dq Li NO
+in
+.Pa periodic.conf ,
+.Nm
+will mask the script output.
+If the variable is not set to either
+.Dq Li YES
+or
+.Dq Li NO ,
+it will be given a default value as described in
+.Xr periodic.conf 5 .
+.Pp
+All remaining script output is delivered based on the value of the
+.Ao Ar basedir Ac Ns Va _output
+setting.
+.Pp
+If this is set to a path name (beginning with a
+.Ql /
+character), output is simply logged to that file.
+.Xr newsyslog 8
+knows about the files
+.Pa /var/log/daily.log , /var/log/weekly.log
+and
+.Pa /var/log/monthly.log ,
+and if they exist, it will rotate them at the appropriate times.
+These are therefore good values if you wish to log
+.Nm
+output.
+.Pp
+If the
+.Ao Ar basedir Ac Ns Va _output
+value does not begin with a
+.Ql /
+and is not empty, it is assumed to contain a list of email addresses, and
+the output is mailed to them.
+.Pp
+If
+.Ao Ar basedir Ac Ns Va _output
+is not set or is empty, output is sent to standard output.
+.Sh ENVIRONMENT
+The
+.Nm
+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 ".Pa /etc/periodic.conf"
+.It Pa /etc/crontab
+The
+.Nm
+program is typically called via entries in the system default
+.Xr cron 8
+table.
+.It Pa /etc/periodic
+The top level directory containing
+.Pa daily ,
+.Pa weekly ,
+and
+.Pa monthly
+subdirectories which contain standard system periodic executables.
+.It Pa /etc/defaults/periodic.conf
+The
+.Pa periodic.conf
+system registry contains variables that control the behaviour of
+.Nm
+and the standard
+.Pa daily , weekly ,
+and
+.Pa monthly
+scripts.
+.It Pa /etc/periodic.conf
+This file contains local overrides for the default
+.Nm
+configuration.
+.El
+.Sh EXAMPLES
+The system crontab should have entries for
+.Nm
+similar to the following example:
+.Bd -literal -offset indent
+# do daily/weekly/monthly maintenance
+0 2 * * * root periodic daily
+0 3 * * 6 root periodic weekly
+0 5 1 * * root periodic monthly
+.Ed
+.Pp
+The
+.Pa /etc/defaults/periodic.conf
+system registry will typically have a
+.Va local_periodic
+variable reading:
+.Pp
+.Dl local_periodic="/usr/local/etc/periodic /usr/X11R6/etc/periodic"
+.Pp
+To log
+.Nm
+output instead of receiving it as email, add the following lines to
+.Pa /etc/periodic.conf :
+.Bd -literal -offset indent
+daily_output=/var/log/daily.log
+weekly_output=/var/log/weekly.log
+monthly_output=/var/log/monthly.log
+.Ed
+.Pp
+To only see important information from daily periodic jobs, add the
+following lines to
+.Pa /etc/periodic.conf :
+.Bd -literal -offset indent
+daily_show_success=NO
+daily_show_info=NO
+daily_show_badconfig=NO
+.Ed
+.Sh SEE ALSO
+.Xr sh 1 ,
+.Xr crontab 5 ,
+.Xr periodic.conf 5 ,
+.Xr cron 8 ,
+.Xr newsyslog 8
+.Sh DIAGNOSTICS
+Exit status is 0 on success and 1 if the command
+fails for one of the following reasons:
+.Bl -diag
+.It usage: periodic <directory of files to execute>
+No directory path argument was passed to
+.Nm
+to specify where the script fragments reside.
+.It <directory> not found
+Self explanatory.
+.El
+.Sh HISTORY
+The
+.Nm
+program first appeared in
+.Fx 3.0 .
+.Sh BUGS
+Since one specifies information about a directory using shell
+variables containing the string,
+.Aq Ar basedir ,
+.Aq Ar basedir
+must only contain characters that are valid within a
+.Xr sh 1
+variable name, alphanumerics and underscores, and the first character
+may not be numeric.
+.Sh AUTHORS
+.An Paul Traina Aq pst@FreeBSD.org
+.An Brian Somers Aq brian@Awfulhak.org
diff --git a/usr.sbin/periodic/periodic.sh b/usr.sbin/periodic/periodic.sh
new file mode 100644
index 0000000..73e8576
--- /dev/null
+++ b/usr.sbin/periodic/periodic.sh
@@ -0,0 +1,106 @@
+#!/bin/sh -
+#
+# $FreeBSD$
+#
+# Run nightly periodic scripts
+#
+# usage: periodic { daily | weekly | monthly } - run standard periodic scripts
+# periodic /absolute/path/to/directory - run periodic scripts in dir
+#
+
+usage () {
+ echo "usage: $0 <directory of files to execute>" 1>&2
+ echo "or $0 { daily | weekly | monthly }" 1>&2
+ exit 1
+}
+
+if [ $# -lt 1 ] ; then
+ usage
+fi
+
+# If possible, check the global system configuration file,
+# to see if there are additional dirs to check
+if [ -r /etc/defaults/periodic.conf ]; then
+ . /etc/defaults/periodic.conf
+ source_periodic_confs
+fi
+
+host=`hostname`
+export host
+tmp_output=`mktemp ${TMPDIR:-/tmp}/periodic.XXXXXXXXXX`
+
+# Execute each executable file in the directory list. If the x bit is not
+# set, assume the user didn't really want us to muck with it (it's a
+# README file or has been disabled).
+
+for arg
+do
+ # Where's our output going ?
+ eval output=\$${arg##*/}_output
+ case "$output" in
+ /*) pipe="cat >>$output";;
+ "") pipe=cat;;
+ *) pipe="mail -s '$host ${arg##*/} run output' $output";;
+ esac
+
+ success=YES info=YES badconfig=NO # Defaults when ${run}_* aren't YES/NO
+ for var in success info badconfig
+ do
+ case $(eval echo "\$${arg##*/}_show_$var") in
+ [Yy][Ee][Ss]) eval $var=YES;;
+ [Nn][Oo]) eval $var=NO;;
+ esac
+ done
+
+ case $arg in
+ /*) if [ -d "$arg" ]
+ then
+ dirlist="$arg"
+ else
+ echo "$0: $arg not found" >&2
+ continue
+ fi;;
+ *) dirlist=
+ for top in /etc/periodic ${local_periodic}
+ do
+ [ -d $top/$arg ] && dirlist="$dirlist $top/$arg"
+ done;;
+ esac
+
+ {
+ empty=TRUE
+ processed=0
+ for dir in $dirlist
+ do
+ for file in $dir/*
+ do
+ if [ -x $file -a ! -d $file ]
+ then
+ output=TRUE
+ processed=$(($processed + 1))
+ $file </dev/null >$tmp_output 2>&1
+ rc=$?
+ if [ -s $tmp_output ]
+ then
+ case $rc in
+ 0) [ $success = NO ] && output=FALSE;;
+ 1) [ $info = NO ] && output=FALSE;;
+ 2) [ $badconfig = NO ] && output=FALSE;;
+ esac
+ [ $output = TRUE ] && { cat $tmp_output; empty=FALSE; }
+ fi
+ cp /dev/null $tmp_output
+ fi
+ done
+ done
+ if [ $empty = TRUE ]
+ then
+ [ $processed = 1 ] && plural= || plural=s
+ echo "No output from the $processed file$plural processed"
+ else
+ echo ""
+ echo "-- End of $arg output --"
+ fi
+ } | eval $pipe
+done
+rm -f $tmp_output
diff --git a/usr.sbin/pkg_install/Makefile b/usr.sbin/pkg_install/Makefile
new file mode 100644
index 0000000..16b8de9
--- /dev/null
+++ b/usr.sbin/pkg_install/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+SUBDIR= lib add create delete info version
+
+.if exists(${.CURDIR}/../../crypto) && !defined(NOCRYPT) && !defined(NO_OPENSSL)
+DISTRIBUTION= crypto
+SUBDIR+= sign
+.endif
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/pkg_install/Makefile.inc b/usr.sbin/pkg_install/Makefile.inc
new file mode 100644
index 0000000..31b1204
--- /dev/null
+++ b/usr.sbin/pkg_install/Makefile.inc
@@ -0,0 +1,15 @@
+# $FreeBSD$
+
+.if exists(${.OBJDIR}/../lib)
+LIBINSTALL= ${.OBJDIR}/../lib/libinstall.a
+.else
+LIBINSTALL= ${.CURDIR}/../lib/libinstall.a
+.endif
+
+# Inherit BINDIR from one level up.
+.if !target(__foobar__)
+__foobar__:
+.if exists(../Makefile.inc)
+.include "../Makefile.inc"
+.endif
+.endif
diff --git a/usr.sbin/pkg_install/README b/usr.sbin/pkg_install/README
new file mode 100644
index 0000000..a5a517d
--- /dev/null
+++ b/usr.sbin/pkg_install/README
@@ -0,0 +1,8 @@
+This is the pkg_install suite of tools for doing maintainance of
+software "packages". More documentation is available in the man pages
+for each individual command.
+
+This code was written by Jordan Hubbard for FreeBSD, snatched and
+mildly reshaped by John Kohl in NetBSD and the changes taken back into
+FreeBSD again by Jordan, who then proceeded to add another couple
+of dozen features on top. Whee! :-)
diff --git a/usr.sbin/pkg_install/add/Makefile b/usr.sbin/pkg_install/add/Makefile
new file mode 100644
index 0000000..6788852
--- /dev/null
+++ b/usr.sbin/pkg_install/add/Makefile
@@ -0,0 +1,18 @@
+# $FreeBSD$
+
+PROG= pkg_add
+SRCS= main.c perform.c futil.c extract.c
+
+CFLAGS+= ${DEBUG} -I${.CURDIR}/../lib
+
+WARNS?= 2
+
+DPADD= ${LIBINSTALL} ${LIBFETCH} ${LIBMD}
+LDADD= ${LIBINSTALL} -lfetch -lmd
+
+.if !defined(NOCRYPT) && !defined(NOSECURE) && !defined(NO_OPENSSL)
+DPADD+= ${LIBSSL} ${LIBCRYPTO}
+LDADD+= -lssl -lcrypto
+.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pkg_install/add/add.h b/usr.sbin/pkg_install/add/add.h
new file mode 100644
index 0000000..b64cef7
--- /dev/null
+++ b/usr.sbin/pkg_install/add/add.h
@@ -0,0 +1,44 @@
+/* $FreeBSD$ */
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * Include and define various things wanted by the add command.
+ *
+ */
+
+#ifndef _INST_ADD_H_INCLUDE
+#define _INST_ADD_H_INCLUDE
+
+typedef enum { NORMAL, MASTER, SLAVE } add_mode_t;
+
+extern char *Prefix;
+extern Boolean NoInstall;
+extern Boolean NoRecord;
+extern Boolean Force;
+extern char *Mode;
+extern char *Owner;
+extern char *Group;
+extern char *Directory;
+extern char *PkgName;
+extern char FirstPen[];
+extern add_mode_t AddMode;
+
+int make_hierarchy(char *);
+void extract_plist(const char *, Package *);
+void apply_perms(const char *, const char *);
+
+#endif /* _INST_ADD_H_INCLUDE */
diff --git a/usr.sbin/pkg_install/add/extract.c b/usr.sbin/pkg_install/add/extract.c
new file mode 100644
index 0000000..f74a90b
--- /dev/null
+++ b/usr.sbin/pkg_install/add/extract.c
@@ -0,0 +1,262 @@
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * This is the package extraction code for the add module.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <err.h>
+#include "lib.h"
+#include "add.h"
+
+
+#define STARTSTRING "tar cf - "
+#define TOOBIG(str) (((int)strlen(str) + FILENAME_MAX + where_count > maxargs) \
+ || ((int)strlen(str) + FILENAME_MAX + perm_count > maxargs))
+
+#define PUSHOUT(todir) /* push out string */ \
+ if (where_count > (int)sizeof(STARTSTRING)-1) { \
+ strcat(where_args, "|tar --unlink -xf - -C "); \
+ strcat(where_args, todir); \
+ if (system(where_args)) { \
+ cleanup(0); \
+ errx(2, \
+ "%s: can not invoke %ld byte tar pipeline: %s", \
+ __func__, \
+ (long)strlen(where_args), where_args); \
+ } \
+ strcpy(where_args, STARTSTRING); \
+ where_count = sizeof(STARTSTRING)-1; \
+ } \
+ if (perm_count) { \
+ apply_perms(todir, perm_args); \
+ perm_args[0] = 0;\
+ perm_count = 0; \
+ }
+
+static void
+rollback(const char *name, const char *home, PackingList start, PackingList stop)
+{
+ PackingList q;
+ char try[FILENAME_MAX], bup[FILENAME_MAX];
+ const char *dir;
+
+ dir = home;
+ for (q = start; q != stop; q = q->next) {
+ if (q->type == PLIST_FILE) {
+ snprintf(try, FILENAME_MAX, "%s/%s", dir, q->name);
+ if (make_preserve_name(bup, FILENAME_MAX, name, try) && fexists(bup)) {
+ (void)chflags(try, 0);
+ (void)unlink(try);
+ if (rename(bup, try))
+ warnx("rollback: unable to rename %s back to %s", bup, try);
+ }
+ }
+ else if (q->type == PLIST_CWD) {
+ if (strcmp(q->name, "."))
+ dir = q->name;
+ else
+ dir = home;
+ }
+ }
+}
+
+void
+extract_plist(const char *home, Package *pkg)
+{
+ PackingList p = pkg->head;
+ char *last_file;
+ char *where_args, *perm_args, *last_chdir;
+ int maxargs, where_count = 0, perm_count = 0, add_count;
+ Boolean preserve;
+
+ maxargs = sysconf(_SC_ARG_MAX) / 2; /* Just use half the argument space */
+ where_args = alloca(maxargs);
+ if (!where_args) {
+ cleanup(0);
+ errx(2, "%s: can't get argument list space", __func__);
+ }
+ perm_args = alloca(maxargs);
+ if (!perm_args) {
+ cleanup(0);
+ errx(2, "%s: can't get argument list space", __func__);
+ }
+
+ strcpy(where_args, STARTSTRING);
+ where_count = sizeof(STARTSTRING)-1;
+ perm_args[0] = 0;
+
+ last_chdir = 0;
+ preserve = find_plist_option(pkg, "preserve") ? TRUE : FALSE;
+
+ /* Reset the world */
+ Owner = NULL;
+ Group = NULL;
+ Mode = NULL;
+ last_file = NULL;
+ (const char *)Directory = home;
+
+ /* Do it */
+ while (p) {
+ char cmd[FILENAME_MAX];
+
+ switch(p->type) {
+ case PLIST_NAME:
+ PkgName = p->name;
+ if (Verbose)
+ printf("extract: Package name is %s\n", p->name);
+ break;
+
+ case PLIST_FILE:
+ last_file = p->name;
+ if (Verbose)
+ printf("extract: %s/%s\n", Directory, p->name);
+ if (!Fake) {
+ char try[FILENAME_MAX];
+
+ if (strrchr(p->name,'\'')) {
+ cleanup(0);
+ errx(2, "%s: Bogus filename \"%s\"", __func__, 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 < 0 || add_count > maxargs - perm_count) {
+ cleanup(0);
+ errx(2, "%s: oops, miscounted strings!", __func__);
+ }
+ perm_count += add_count;
+ }
+ else {
+ /* rename failed, try copying with a big tar command */
+ if (last_chdir != Directory) {
+ if (last_chdir == NULL) {
+ PUSHOUT(Directory);
+ } else {
+ PUSHOUT(last_chdir);
+ }
+ last_chdir = Directory;
+ }
+ else if (p->name[0] == '/' || TOOBIG(p->name)) {
+ PUSHOUT(Directory);
+ }
+ add_count = snprintf(&where_args[where_count], maxargs - where_count, " '%s'", p->name);
+ if (add_count < 0 || add_count > maxargs - where_count) {
+ cleanup(0);
+ errx(2, "%s: oops, miscounted strings!", __func__);
+ }
+ where_count += add_count;
+ add_count = snprintf(&perm_args[perm_count],
+ maxargs - perm_count,
+ "'%s' ", p->name);
+ if (add_count < 0 || add_count > maxargs - perm_count) {
+ cleanup(0);
+ errx(2, "%s: oops, miscounted strings!", __func__);
+ }
+ perm_count += add_count;
+ }
+ }
+ break;
+
+ case PLIST_CWD:
+ if (Verbose)
+ printf("extract: CWD to %s\n", p->name);
+ PUSHOUT(Directory);
+ if (strcmp(p->name, ".")) {
+ if (!Fake && make_hierarchy(p->name) == FAIL) {
+ cleanup(0);
+ errx(2, "%s: unable to cwd to '%s'", __func__, p->name);
+ }
+ Directory = p->name;
+ }
+ else
+ (const char *)Directory = home;
+ break;
+
+ case PLIST_CMD:
+ if ((strstr(p->name, "%B") || strstr(p->name, "%F") ||
+ strstr(p->name, "%f")) && last_file == NULL) {
+ cleanup(0);
+ errx(2, "%s: no last file specified for '%s' command",
+ __func__, p->name);
+ }
+ if (strstr(p->name, "%D") && Directory == NULL) {
+ cleanup(0);
+ errx(2, "%s: no directory specified for '%s' command",
+ __func__, p->name);
+ }
+ format_cmd(cmd, p->name, Directory, last_file);
+ PUSHOUT(Directory);
+ if (Verbose)
+ printf("extract: execute '%s'\n", cmd);
+ if (!Fake && system(cmd))
+ warnx("command '%s' failed", cmd);
+ break;
+
+ case PLIST_CHMOD:
+ PUSHOUT(Directory);
+ Mode = p->name;
+ break;
+
+ case PLIST_CHOWN:
+ PUSHOUT(Directory);
+ Owner = p->name;
+ break;
+
+ case PLIST_CHGRP:
+ PUSHOUT(Directory);
+ Group = p->name;
+ break;
+
+ case PLIST_COMMENT:
+ break;
+
+ case PLIST_IGNORE:
+ p = p->next;
+ break;
+
+ default:
+ break;
+ }
+ p = p->next;
+ }
+ PUSHOUT(Directory);
+}
diff --git a/usr.sbin/pkg_install/add/futil.c b/usr.sbin/pkg_install/add/futil.c
new file mode 100644
index 0000000..66ff738
--- /dev/null
+++ b/usr.sbin/pkg_install/add/futil.c
@@ -0,0 +1,97 @@
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * Miscellaneous file access utilities.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <err.h>
+#include "lib.h"
+#include "add.h"
+
+/*
+ * Assuming dir is a desired directory name, make it and all intervening
+ * directories necessary.
+ */
+
+int
+make_hierarchy(char *dir)
+{
+ char *cp1, *cp2;
+
+ if (dir[0] == '/')
+ cp1 = cp2 = dir + 1;
+ else
+ cp1 = cp2 = dir;
+ while (cp2) {
+ if ((cp2 = strchr(cp1, '/')) !=NULL )
+ *cp2 = '\0';
+ if (fexists(dir)) {
+ if (!isdir(dir)) {
+ if (cp2)
+ *cp2 = '/';
+ return FAIL;
+ }
+ }
+ else {
+ if (vsystem("mkdir %s", dir)) {
+ if (cp2)
+ *cp2 = '/';
+ return FAIL;
+ }
+ apply_perms(NULL, dir);
+ }
+ /* Put it back */
+ if (cp2) {
+ *cp2 = '/';
+ cp1 = cp2 + 1;
+ }
+ }
+ return SUCCESS;
+}
+
+/* Using permission defaults, apply them as necessary */
+void
+apply_perms(const char *dir, const char *arg)
+{
+ const char *cd_to;
+
+ if (!dir || *arg == '/') /* absolute path? */
+ cd_to = "/";
+ else
+ cd_to = dir;
+
+ if (Mode)
+ if (vsystem("cd %s && chmod -R %s %s", cd_to, Mode, arg))
+ warnx("couldn't change modes of '%s' to '%s'", arg, Mode);
+ if (Owner && Group) {
+ if (vsystem("cd %s && chown -R %s:%s %s", cd_to, Owner, Group, arg))
+ warnx("couldn't change owner/group of '%s' to '%s:%s'",
+ arg, Owner, Group);
+ return;
+ }
+ if (Owner) {
+ if (vsystem("cd %s && chown -R %s %s", cd_to, Owner, arg))
+ warnx("couldn't change owner of '%s' to '%s'", arg, Owner);
+ return;
+ } else if (Group)
+ if (vsystem("cd %s && chgrp -R %s %s", cd_to, Group, arg))
+ warnx("couldn't change group of '%s' to '%s'", arg, Group);
+}
+
diff --git a/usr.sbin/pkg_install/add/main.c b/usr.sbin/pkg_install/add/main.c
new file mode 100644
index 0000000..2ebf912
--- /dev/null
+++ b/usr.sbin/pkg_install/add/main.c
@@ -0,0 +1,276 @@
+/*
+ *
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * This is the add module.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <err.h>
+#include <sys/param.h>
+#include <sys/utsname.h>
+#include "lib.h"
+#include "add.h"
+
+static char Options[] = "hvIRfnrp:SMt:";
+
+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 200
+char pkgnames[MAX_PKGS][MAXPATHLEN];
+char *pkgs[MAX_PKGS];
+
+struct {
+ int lowver; /* Lowest version number to match */
+ int hiver; /* Highest version number to match */
+ const char *directory; /* Directory it lives in */
+} releases[] = {
+ { 410000, 410000, "/packages-4.1-release" },
+ { 420000, 420000, "/packages-4.2-release" },
+ { 430000, 430000, "/packages-4.3-release" },
+ { 440000, 440000, "/packages-4.4-release" },
+ { 450000, 450000, "/packages-4.5-release" },
+ { 300000, 399000, "/packages-3-stable" },
+ { 400000, 499000, "/packages-4-stable" },
+ { 510000, 599000, "/packages-5-stable" },
+ { 0, 9999999, "/packages-current" },
+ { 0, 0, NULL }
+};
+
+static char *getpackagesite(void);
+int getosreldate(void);
+
+static void usage __P((void));
+
+int
+main(int argc, char **argv)
+{
+ int ch, error;
+ char **start;
+ char *cp, *packagesite = NULL, *remotepkg = NULL, *ptr;
+ static char temppackageroot[MAXPATHLEN];
+
+ start = argv;
+ while ((ch = getopt(argc, argv, Options)) != -1) {
+ switch(ch) {
+ case 'v':
+ Verbose = TRUE;
+ break;
+
+ case 'p':
+ Prefix = optarg;
+ break;
+
+ case 'I':
+ NoInstall = TRUE;
+ break;
+
+ case 'R':
+ NoRecord = TRUE;
+ break;
+
+ case 'f':
+ Force = TRUE;
+ break;
+
+ case 'n':
+ Fake = TRUE;
+ Verbose = TRUE;
+ break;
+
+ case 'r':
+ Remote = TRUE;
+ break;
+
+ case 't':
+ if (strlcpy(FirstPen, optarg, sizeof(FirstPen)) >= sizeof(FirstPen))
+ errx(1, "-t Argument too long.");
+ break;
+
+ case 'S':
+ AddMode = SLAVE;
+ break;
+
+ case 'M':
+ AddMode = MASTER;
+ break;
+
+ case 'h':
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc > MAX_PKGS) {
+ errx(1, "too many packages (max %d)", MAX_PKGS);
+ }
+
+ if (AddMode != SLAVE) {
+ for (ch = 0; ch < MAX_PKGS; pkgs[ch++] = NULL) ;
+
+ /* Get all the remaining package names, if any */
+ for (ch = 0; *argv; ch++, argv++) {
+ if (Remote) {
+ if ((packagesite = getpackagesite()) == NULL)
+ errx(1, "package name too long");
+ if (strlcpy(temppackageroot, packagesite,
+ sizeof(temppackageroot)) >= sizeof(temppackageroot))
+ errx(1, "package name too long");
+ if (strlcat(temppackageroot, *argv, sizeof(temppackageroot))
+ >= sizeof(temppackageroot))
+ errx(1, "package name too long");
+ remotepkg = temppackageroot;
+ if (!((ptr = strrchr(remotepkg, '.')) && ptr[1] == 't' &&
+ ptr[2] == 'g' && ptr[3] == 'z' && !ptr[4]))
+ if (strlcat(remotepkg, ".tgz", sizeof(temppackageroot))
+ >= sizeof(temppackageroot))
+ errx(1, "package name too long");
+ }
+ if (!strcmp(*argv, "-")) /* stdin? */
+ (const char *)pkgs[ch] = "-";
+ else if (isURL(*argv)) { /* preserve URLs */
+ if (strlcpy(pkgnames[ch], *argv, sizeof(pkgnames[ch]))
+ >= sizeof(pkgnames[ch]))
+ errx(1, "package name too long");
+ pkgs[ch] = pkgnames[ch];
+ }
+ else if ((Remote) && isURL(remotepkg)) {
+ if (strlcpy(pkgnames[ch], remotepkg, sizeof(pkgnames[ch]))
+ >= sizeof(pkgnames[ch]))
+ errx(1, "package name too long");
+ pkgs[ch] = pkgnames[ch];
+ } else { /* expand all pathnames to fullnames */
+ if (fexists(*argv)) /* refers to a file directly */
+ pkgs[ch] = realpath(*argv, pkgnames[ch]);
+ else { /* look for the file in the expected places */
+ if (!(cp = fileFindByPath(NULL, *argv))) {
+ /* let pkg_do() fail later, so that error is reported */
+ if (strlcpy(pkgnames[ch], *argv, sizeof(pkgnames[ch]))
+ >= sizeof(pkgnames[ch]))
+ errx(1, "package name too long");
+ pkgs[ch] = pkgnames[ch];
+ } else {
+ if (strlcpy(pkgnames[ch], cp, sizeof(pkgnames[ch]))
+ >= sizeof(pkgnames[ch]))
+ errx(1, "package name too long");
+ pkgs[ch] = pkgnames[ch];
+ }
+ }
+ }
+ if (packagesite != NULL)
+ packagesite[0] = '\0';
+ }
+ }
+ /* If no packages, yelp */
+ else if (!ch) {
+ warnx("missing package name(s)");
+ usage();
+ }
+ else if (ch > 1 && AddMode == MASTER) {
+ warnx("only one package name may be specified with master mode");
+ usage();
+ }
+ /* Make sure the sub-execs we invoke get found */
+ setenv("PATH",
+ "/sbin:/usr/sbin:/bin:/usr/bin:/usr/X11R6/bin",
+ 1);
+
+ /* Set a reasonable umask */
+ umask(022);
+
+ if ((error = pkg_perform(pkgs)) != 0) {
+ if (Verbose)
+ warnx("%d package addition(s) failed", error);
+ return error;
+ }
+ else
+ return 0;
+}
+
+static char *
+getpackagesite(void)
+{
+ int reldate, i;
+ static char sitepath[MAXPATHLEN];
+ struct utsname u;
+
+ if (getenv("PACKAGESITE")) {
+ if (strlcpy(sitepath, getenv("PACKAGESITE"), sizeof(sitepath))
+ >= sizeof(sitepath))
+ return NULL;
+ return sitepath;
+ }
+
+ if (getenv("PACKAGEROOT")) {
+ if (strlcpy(sitepath, getenv("PACKAGEROOT"), sizeof(sitepath))
+ >= sizeof(sitepath))
+ return NULL;
+ } else {
+ if (strlcat(sitepath, "ftp://ftp.freebsd.org", sizeof(sitepath))
+ >= sizeof(sitepath))
+ return NULL;
+ }
+
+ if (strlcat(sitepath, "/pub/FreeBSD/ports/", sizeof(sitepath))
+ >= sizeof(sitepath))
+ return NULL;
+
+ uname(&u);
+ if (strlcat(sitepath, u.machine, sizeof(sitepath)) >= sizeof(sitepath))
+ return NULL;
+
+ reldate = getosreldate();
+ for(i = 0; releases[i].directory != NULL; i++) {
+ if (reldate >= releases[i].lowver && reldate <= releases[i].hiver) {
+ if (strlcat(sitepath, releases[i].directory, sizeof(sitepath))
+ >= sizeof(sitepath))
+ return NULL;
+ break;
+ }
+ }
+
+ if (strlcat(sitepath, "/Latest/", sizeof(sitepath)) >= sizeof(sitepath))
+ return NULL;
+
+ return sitepath;
+
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n",
+ "usage: pkg_add [-vInrfRMS] [-t template] [-p prefix]",
+ " 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..d707ec6
--- /dev/null
+++ b/usr.sbin/pkg_install/add/perform.c
@@ -0,0 +1,562 @@
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * This is the main body of the add module.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <err.h>
+#include <paths.h>
+#include "lib.h"
+#include "add.h"
+
+#include <libgen.h>
+#include <signal.h>
+#include <sys/wait.h>
+
+static int pkg_do(char *);
+static int sanity_check(char *);
+static char LogDir[FILENAME_MAX];
+static int zapLogDir; /* Should we delete LogDir? */
+
+int
+pkg_perform(char **pkgs)
+{
+ int i, err_cnt = 0;
+
+ signal(SIGINT, cleanup);
+ signal(SIGHUP, cleanup);
+
+ if (AddMode == SLAVE)
+ err_cnt = pkg_do(NULL);
+ else {
+ for (i = 0; pkgs[i]; i++)
+ err_cnt += pkg_do(pkgs[i]);
+ }
+ return err_cnt;
+}
+
+static Package Plist;
+static char *Home;
+
+/*
+ * This is seriously ugly code following. Written very fast!
+ * [And subsequently made even worse.. Sigh! This code was just born
+ * to be hacked, I guess.. :) -jkh]
+ */
+static int
+pkg_do(char *pkg)
+{
+ char pkg_fullname[FILENAME_MAX];
+ char playpen[FILENAME_MAX];
+ char extract_contents[FILENAME_MAX];
+ char *where_to, *extract;
+ FILE *cfile;
+ int code;
+ PackingList p;
+ struct stat sb;
+ int inPlace;
+ /* 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 %qd bytes", (long long)sb.st_size * 4);
+ where_to = Home;
+ /* Since we can call ourselves recursively, keep notes on where we came from */
+ if (!getenv("_TOP"))
+ setenv("_TOP", Home, 1);
+ if (unpack(pkg_fullname, extract)) {
+ warnx(
+ "unable to extract table of contents file from '%s' - not a package?",
+ pkg_fullname);
+ goto bomb;
+ }
+ cfile = fopen(CONTENTS_FNAME, "r");
+ if (!cfile) {
+ warnx(
+ "unable to open table of contents file '%s' - not a package?",
+ CONTENTS_FNAME);
+ goto bomb;
+ }
+ read_plist(&Plist, cfile);
+ fclose(cfile);
+
+ /* Extract directly rather than moving? Oh goodie! */
+ if (find_plist_option(&Plist, "extract-in-place")) {
+ if (Verbose)
+ printf("Doing in-place extraction for %s\n", pkg_fullname);
+ p = find_plist(&Plist, PLIST_CWD);
+ if (p) {
+ if (!isdir(p->name) && !Fake) {
+ if (Verbose)
+ printf("Desired prefix of %s does not exist, creating..\n", p->name);
+ vsystem("mkdir -p %s", p->name);
+ if (chdir(p->name) == -1) {
+ warn("unable to change directory to '%s'", p->name);
+ goto bomb;
+ }
+ }
+ where_to = p->name;
+ inPlace = 1;
+ }
+ else {
+ warnx(
+ "no prefix specified in '%s' - this is a bad package!",
+ pkg_fullname);
+ goto bomb;
+ }
+ }
+
+ /*
+ * Apply a crude heuristic to see how much space the package will
+ * take up once it's unpacked. I've noticed that most packages
+ * compress an average of 75%, so multiply by 4 for good measure.
+ */
+
+ if (!extract && !inPlace && min_free(playpen) < sb.st_size * 4) {
+ warnx("projected size of %qd exceeds available free space.\n"
+"Please set your PKG_TMPDIR variable to point to a location with more\n"
+ "free space and try again", (long long)sb.st_size * 4);
+ warnx("not extracting %s\ninto %s, sorry!",
+ pkg_fullname, where_to);
+ goto bomb;
+ }
+
+ /* If this is a direct extract and we didn't want it, stop now */
+ if (inPlace && Fake)
+ goto success;
+
+ /* Finally unpack the whole mess. If extract is null we
+ already + did so so don't bother doing it again. */
+ if (extract && unpack(pkg_fullname, NULL)) {
+ warnx("unable to extract '%s'!", pkg_fullname);
+ goto bomb;
+ }
+ }
+
+ /* Check for sanity and dependencies */
+ if (sanity_check(pkg))
+ goto bomb;
+
+ /* If we're running in MASTER mode, just output the plist and return */
+ if (AddMode == MASTER) {
+ printf("%s\n", where_playpen());
+ write_plist(&Plist, stdout);
+ return 0;
+ }
+ }
+
+ /*
+ * If we have a prefix, delete the first one we see and add this
+ * one in place of it.
+ */
+ if (Prefix) {
+ delete_plist(&Plist, FALSE, PLIST_CWD, NULL);
+ add_plist_top(&Plist, PLIST_CWD, Prefix);
+ }
+
+ setenv(PKG_PREFIX_VNAME, (p = find_plist(&Plist, PLIST_CWD)) ? p->name : ".", 1);
+ /* Protect against old packages with bogus @name and origin fields */
+ if (Plist.name == NULL)
+ Plist.name = "anonymous";
+ if (Plist.origin == NULL)
+ Plist.origin = "anonymous/anonymous";
+
+ /*
+ * See if we're already registered either with the same name (the same
+ * version) or some other version with the same origin.
+ */
+ if ((isinstalledpkg(Plist.name) ||
+ matchbyorigin(Plist.origin, NULL) != NULL) && !Force) {
+ warnx("package '%s' or its older version already installed",
+ Plist.name);
+ code = 1;
+ goto success; /* close enough for government work */
+ }
+
+ /* Now check the packing list for dependencies */
+ for (p = Plist.head; p ; p = p->next) {
+ char *deporigin;
+
+ if (p->type != PLIST_PKGDEP)
+ continue;
+ deporigin = (p->next->type == PLIST_DEPORIGIN) ? p->next->name : NULL;
+ if (Verbose) {
+ printf("Package '%s' depends on '%s'", Plist.name, p->name);
+ if (deporigin != NULL)
+ printf(" with '%s' origin", deporigin);
+ printf(".\n");
+ }
+ if (!isinstalledpkg(p->name) &&
+ !(deporigin != NULL && matchbyorigin(deporigin, NULL) != NULL)) {
+ char path[FILENAME_MAX], *cp = NULL;
+
+ if (!Fake) {
+ if (!isURL(pkg) && !getenv("PKG_ADD_BASE")) {
+ 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", Plist.name);
+ if (!Fake && vsystem("./%s %s INSTALL", REQUIRE_FNAME, Plist.name)) {
+ warnx("package %s fails requirements %s", pkg_fullname,
+ Force ? "installing anyway" : "- not installed");
+ if (!Force) {
+ code = 1;
+ goto success; /* close enough for government work */
+ }
+ }
+ }
+
+ /*
+ * Test whether to use the old method of passing tokens to installation
+ * scripts, and set appropriate variables..
+ */
+
+ if (fexists(POST_INSTALL_FNAME)) {
+ new_m = 1;
+ sprintf(post_script, "%s", POST_INSTALL_FNAME);
+ pre_arg[0] = '\0';
+ post_arg[0] = '\0';
+ } else {
+ if (fexists(INSTALL_FNAME)) {
+ sprintf(post_script, "%s", INSTALL_FNAME);
+ sprintf(pre_arg, "PRE-INSTALL");
+ sprintf(post_arg, "POST-INSTALL");
+ }
+ }
+
+ /* If we're really installing, and have an installation file, run it */
+ if (!NoInstall && fexists(pre_script)) {
+ vsystem("chmod +x %s", pre_script); /* make sure */
+ if (Verbose)
+ printf("Running pre-install for %s..\n", Plist.name);
+ if (!Fake && vsystem("./%s %s %s", pre_script, Plist.name, pre_arg)) {
+ warnx("install script returned error status");
+ unlink(pre_script);
+ code = 1;
+ goto success; /* nothing to uninstall yet */
+ }
+ }
+
+ /* Now finally extract the entire show if we're not going direct */
+ if (!inPlace && !Fake)
+ extract_plist(".", &Plist);
+
+ if (!Fake && fexists(MTREE_FNAME)) {
+ if (Verbose)
+ printf("Running mtree for %s..\n", Plist.name);
+ p = find_plist(&Plist, PLIST_CWD);
+ if (Verbose)
+ printf("mtree -U -f %s -d -e -p %s >%s\n", MTREE_FNAME, p ? p->name : "/", _PATH_DEVNULL);
+ if (!Fake) {
+ if (vsystem("/usr/sbin/mtree -U -f %s -d -e -p %s >%s", MTREE_FNAME, p ? p->name : "/", _PATH_DEVNULL))
+ warnx("mtree returned a non-zero status - continuing");
+ }
+ }
+
+ /* Run the installation script one last time? */
+ if (!NoInstall && fexists(post_script)) {
+ vsystem("chmod +x %s", post_script); /* make sure */
+ if (Verbose)
+ printf("Running post-install for %s..\n", Plist.name);
+ if (!Fake && vsystem("./%s %s %s", post_script, Plist.name, post_arg)) {
+ warnx("install script returned error status");
+ unlink(post_script);
+ code = 1;
+ goto fail;
+ }
+ }
+
+ /* Time to record the deed? */
+ if (!NoRecord && !Fake) {
+ char contents[FILENAME_MAX];
+ FILE *contfile;
+
+ if (getuid() != 0)
+ warnx("not running as root - trying to record install anyway");
+ sprintf(LogDir, "%s/%s", LOG_DIR, Plist.name);
+ zapLogDir = 1;
+ if (Verbose)
+ printf("Attempting to record package into %s..\n", LogDir);
+ if (make_hierarchy(LogDir)) {
+ warnx("can't record package into '%s', you're on your own!",
+ LogDir);
+ bzero(LogDir, FILENAME_MAX);
+ code = 1;
+ goto success; /* close enough for government work */
+ }
+ /* Make sure pkg_info can read the entry */
+ vsystem("chmod a+rx %s", LogDir);
+ move_file(".", DESC_FNAME, LogDir);
+ move_file(".", COMMENT_FNAME, LogDir);
+ if (fexists(INSTALL_FNAME))
+ move_file(".", INSTALL_FNAME, LogDir);
+ if (fexists(POST_INSTALL_FNAME))
+ move_file(".", POST_INSTALL_FNAME, LogDir);
+ if (fexists(DEINSTALL_FNAME))
+ move_file(".", DEINSTALL_FNAME, LogDir);
+ if (fexists(POST_DEINSTALL_FNAME))
+ move_file(".", POST_DEINSTALL_FNAME, LogDir);
+ if (fexists(REQUIRE_FNAME))
+ move_file(".", REQUIRE_FNAME, LogDir);
+ if (fexists(DISPLAY_FNAME))
+ move_file(".", DISPLAY_FNAME, LogDir);
+ if (fexists(MTREE_FNAME))
+ move_file(".", MTREE_FNAME, LogDir);
+ sprintf(contents, "%s/%s", LogDir, CONTENTS_FNAME);
+ contfile = fopen(contents, "w");
+ if (!contfile) {
+ warnx("can't open new contents file '%s'! can't register pkg",
+ contents);
+ goto success; /* can't log, but still keep pkg */
+ }
+ write_plist(&Plist, contfile);
+ fclose(contfile);
+ for (p = Plist.head; p ; p = p->next) {
+ char *deporigin, **depnames;
+ int i;
+
+ if (p->type != PLIST_PKGDEP)
+ continue;
+ deporigin = (p->next->type == PLIST_DEPORIGIN) ? p->next->name :
+ NULL;
+ if (Verbose) {
+ printf("Trying to record dependency on package '%s'", p->name);
+ if (deporigin != NULL)
+ printf(" with '%s' origin", deporigin);
+ printf(".\n");
+ }
+
+ depnames = (deporigin != NULL) ? matchbyorigin(deporigin, NULL) :
+ NULL;
+ if (depnames == NULL) {
+ depnames = alloca(sizeof(*depnames) * 2);
+ depnames[0] = p->name;
+ depnames[1] = NULL;
+ }
+ for (i = 0; depnames[i] != NULL; i++) {
+ sprintf(contents, "%s/%s/%s", LOG_DIR, depnames[i],
+ REQUIRED_BY_FNAME);
+ if (strcmp(p->name, depnames[i]) != 0)
+ warnx("warning: package '%s' requires '%s', but '%s' "
+ "is installed", Plist.name, p->name, depnames[i]);
+ contfile = fopen(contents, "a");
+ if (!contfile)
+ warnx("can't open dependency file '%s'!\n"
+ "dependency registration is incomplete", contents);
+ else {
+ fprintf(contfile, "%s\n", Plist.name);
+ if (fclose(contfile) == EOF)
+ warnx("cannot properly close file %s", contents);
+ }
+ }
+ }
+ if (Verbose)
+ printf("Package %s registered in %s\n", Plist.name, LogDir);
+ }
+
+ if ((p = find_plist(&Plist, PLIST_DISPLAY)) != NULL) {
+ FILE *fp;
+ char buf[BUFSIZ];
+
+ snprintf(buf, sizeof buf, "%s/%s", LogDir, p->name);
+ fp = fopen(buf, "r");
+ if (fp) {
+ putc('\n', stdout);
+ while (fgets(buf, sizeof(buf), fp))
+ fputs(buf, stdout);
+ putc('\n', stdout);
+ (void) fclose(fp);
+ } else
+ warnx("cannot open %s as display file", buf);
+ }
+
+ goto success;
+
+ bomb:
+ code = 1;
+ goto success;
+
+ fail:
+ /* Nuke the whole (installed) show, XXX but don't clean directories */
+ if (!Fake)
+ delete_package(FALSE, FALSE, &Plist);
+
+ success:
+ /* delete the packing list contents */
+ free_plist(&Plist);
+ leave_playpen();
+ return code;
+}
+
+static int
+sanity_check(char *pkg)
+{
+ int code = 0;
+
+ if (!fexists(CONTENTS_FNAME)) {
+ warnx("package %s has no CONTENTS file!", pkg);
+ code = 1;
+ }
+ else if (!fexists(COMMENT_FNAME)) {
+ warnx("package %s has no COMMENT file!", pkg);
+ code = 1;
+ }
+ else if (!fexists(DESC_FNAME)) {
+ warnx("package %s has no DESC file!", pkg);
+ code = 1;
+ }
+ return code;
+}
+
+void
+cleanup(int sig)
+{
+ static int in_cleanup = 0;
+
+ if (!in_cleanup) {
+ in_cleanup = 1;
+ if (sig)
+ printf("Signal %d received, cleaning up..\n", sig);
+ if (!Fake && zapLogDir && LogDir[0])
+ vsystem("%s -rf %s", REMOVE_CMD, LogDir);
+ leave_playpen();
+ }
+ if (sig)
+ exit(1);
+}
diff --git a/usr.sbin/pkg_install/add/pkg_add.1 b/usr.sbin/pkg_install/add/pkg_add.1
new file mode 100644
index 0000000..4622d44
--- /dev/null
+++ b/usr.sbin/pkg_install/add/pkg_add.1
@@ -0,0 +1,483 @@
+.\"
+.\" 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
+.\" $FreeBSD$
+.\"
+.Dd November 25, 1994
+.Dt PKG_ADD 1
+.Os
+.Sh NAME
+.Nm pkg_add
+.Nd a utility for installing software package distributions
+.Sh SYNOPSIS
+.Nm
+.Op Fl vInfrRMS
+.Op Fl t Ar template
+.Op Fl p Ar prefix
+.Ar pkg-name Op Ar pkg-name ...
+.Sh DESCRIPTION
+The
+.Nm
+command is used to extract packages that have been previously created
+with the
+.Xr pkg_create 1
+command.
+.Sh WARNING
+.Bf -emphasis
+Since the
+.Nm
+command may execute scripts or programs contained within a package file,
+your system may be susceptible to
+.Dq Em trojan horses
+or other subtle
+attacks from miscreants who create dangerous package files.
+.Pp
+You are advised to verify the competence and identity of those who
+provide installable package files. For extra protection, use the
+.Fl M
+flag to extract the package file, and inspect its contents and scripts to
+ensure it poses no danger to your system's integrity. Pay particular
+attention to any +INSTALL, +POST-INSTALL, +DEINSTALL, +POST-DEINSTALL,
++REQUIRE or +MTREE_DIRS files, and inspect the +CONTENTS file for
+.Cm @cwd ,
+.Cm @mode
+(check for setuid),
+.Cm @dirrm ,
+.Cm @exec ,
+and
+.Cm @unexec
+directives, and/or use the
+.Xr pkg_info 1
+command to examine the package file.
+.Ef
+.Sh OPTIONS
+The following command line arguments are supported:
+.Bl -tag -width indent
+.It Ar pkg-name Op Ar pkg-name ...
+The named packages are installed. A package name of - 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
+.Dq staging area .
+By default, this is the string
+.Pa /var/tmp/instmp.XXXXXX ,
+but it may be necessary to override it in the situation where
+space in your
+.Pa /var/tmp
+directory is limited. Be sure to leave some number of `X' characters
+for
+.Xr mktemp 3
+to fill in with a unique ID.
+.Pp
+You can get a performance boost by setting the staging area
+.Ar template
+to reside on the same disk partition as target directories for package
+file installation; often this is
+.Pa /usr .
+.It Fl M
+Run in
+.Cm MASTER
+mode. This is a very specialized mode for running
+.Nm
+and is meant to be run in conjunction with
+.Cm SLAVE
+mode. When run in this mode,
+.Nm
+does no work beyond extracting the package into a temporary staging
+area (see the
+.Fl t
+option), reading in the packing list, and then dumping it (prefaced by
+the current staging area) to stdout where it may be filtered by a
+program such as
+.Xr sed 1 .
+When used in conjunction with
+.Cm SLAVE
+mode, it allows you to make radical changes to the package structure
+before acting on its contents.
+.It Fl S
+Run in
+.Cm SLAVE
+mode. This is a very specialized mode for running
+.Nm
+and is meant to be run in conjunction with
+.Cm MASTER
+mode. When run in this mode,
+.Nm
+expects the release contents to be already extracted and waiting
+in the staging area, the location of which is read as a string
+from stdin. The complete packing list is also read from stdin,
+and the contents then acted on as normal.
+.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
+.Dq .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
+.Li 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
+The
+.Nm
+utility 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
+.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 -ragged -offset indent -compact
+.Ar pkg-name
+.Ar INSTALL
+.Ed
+where
+.Ar pkg-name
+is the name of the package in question and the
+.Ar INSTALL
+keyword denotes this as an installation requirements check (useful if
+you want to have one script serving multiple functions).
+.It
+If a
+.Ar pre-install
+script exists for the package, it is then executed with the following
+arguments:
+.Bd -ragged -offset indent -compact
+.Cm script
+.Ar pkg-name
+.Ar PRE-INSTALL
+.Ed
+.Pp
+where
+.Ar pkg-name
+is the name of the package in question and
+.Ar PRE-INSTALL
+is a keyword denoting this as the preinstallation phase.
+.Pp
+.Sy Note :
+The
+.Ar PRE-INSTALL
+keyword will not appear if separate scripts for pre-install and post-install
+are given during package creation time (using the
+.Fl i
+and
+.Fl I
+flags to
+.Xr pkg_create 1 ) .
+.It
+If
+.Cm @option extract-in-place
+is not used, then the packing list (this is the
+.Pa +CONTENTS
+file) is now used as a guide for moving (or copying, as necessary) files from
+the staging area into their final locations.
+.It
+If the package contains an
+.Ar mtreefile
+file (see
+.Xr pkg_create 1 ) ,
+then mtree is invoked as:
+.Bd -ragged -offset indent -compact
+.Cm mtree
+.Fl u
+.Fl f
+.Ar mtreefile
+.Fl d
+.Fl e
+.Fl p
+.Pa prefix
+.Ed
+where
+.Pa prefix
+is either the prefix specified with the
+.Fl p
+flag or, if no
+.Fl p
+flag was specified, the name of the first directory named by a
+.Cm @cwd
+directive within this package.
+.It
+If a
+.Ar post-install
+script exists for the package, it is then executed as
+.Bd -ragged -offset indent -compact
+.Cm script
+.Ar pkg-name
+.Ar POST-INSTALL
+.Ed
+where
+.Ar pkg-name
+is the name of the package in question and
+.Ar POST-INSTALL
+is a keyword denoting this as the post-installation phase.
+.Pp
+.Sy Note :
+The
+.Ar POST-INSTALL
+keyword will not appear if separate scripts for pre-install and post-install
+are given during package creation time (using the
+.Fl i
+and
+.Fl I
+flags to
+.Xr pkg_create 1 ) .
+.Pp
+Reasoning behind passing keywords such as
+.Ar POST-INSTALL
+and
+.Ar PRE-INSTALL
+is that this allows you to write a single
+.Ar install
+script that does both
+.Dq before and after
+actions.
+But, separating the
+functionality is more advantageous and easier from a 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 separated by colons. Each entry
+consists of a directory name.
+The current directory may be indicated
+implicitly by an empty directory name, or explicitly by a single
+period.
+.Pp
+The environment variable
+.Ev PKG_DBDIR
+specifies an alternative location for the installed package database.
+.Pp
+The environment variables
+.Ev PKG_TMPDIR
+and
+.Ev TMPDIR ,
+in that order, are taken to name temporary directories where
+.Nm
+will attempt to create its staging area in.
+If these variables are not present or if the directories named lack
+sufficient space, then
+.Nm
+will use the first of
+.Pa /var/tmp ,
+.Pa /tmp
+or
+.Pa /usr/tmp
+with sufficient space.
+.Pp
+The environment variable
+.Ev PACKAGEROOT
+specifies an alternate location for
+.Nm
+to fetch from.
+The fetch URL is built using this environment variable and the automatic
+directory logic that
+.Nm
+uses when the
+.Fl r
+option is invoked.
+An example setting would be
+.Qq Li ftp://ftp3.FreeBSD.org .
+.Pp
+The environment variable
+.Ev PACKAGESITE
+specifies an alternate location for
+.Nm
+to fetch from.
+This variable subverts the automatic directory logic
+that
+.Nm
+uses when the
+.Fl r
+option is invoked.
+Thus it should be a complete URL to the remote package file(s).
+.Sh FILES
+.Bl -tag -width /var/db/pkg -compact
+.It Pa /var/tmp
+Temporary directory for creating the staging area, if environmental variables
+.Ev PKG_TMPDIR
+or
+.Ev TMPDIR
+do not point to a suitable directory.
+.It Pa /tmp
+Next choice if
+.Pa /var/tmp
+does not exist or has insufficient space.
+.It Pa /usr/tmp
+Last choice if
+.Pa /var/tmp
+and
+.Pa /tmp
+are not suitable for creating the staging area.
+.It Pa /var/db/pkg
+Default location of the installed package database.
+.El
+.Sh SEE ALSO
+.Xr pkg_create 1 ,
+.Xr pkg_delete 1 ,
+.Xr pkg_info 1 ,
+.Xr pkg_version 1 ,
+.Xr mktemp 3 ,
+.Xr sysconf 3 ,
+.Xr mtree 8
+.Sh AUTHORS
+.An Jordan Hubbard
+.Sh CONTRIBUTORS
+.An John Kohl Aq jtk@rational.com
+.Sh BUGS
+Hard links between files in a distribution are only preserved if either
+(1) the staging area is on the same filesystem 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..4128517
--- /dev/null
+++ b/usr.sbin/pkg_install/create/Makefile
@@ -0,0 +1,18 @@
+# $FreeBSD$
+
+PROG= pkg_create
+SRCS= main.c perform.c pl.c
+
+CFLAGS+= ${DEBUG} -I${.CURDIR}/../lib
+
+WARNS?= 2
+
+DPADD= ${LIBINSTALL} ${LIBFETCH} ${LIBMD}
+LDADD= ${LIBINSTALL} -lfetch -lmd
+
+.if !defined(NOCRYPT) && !defined(NOSECURE) && !defined(NO_OPENSSL)
+DPADD+= ${LIBSSL} ${LIBCRYPTO}
+LDADD+= -lssl -lcrypto
+.endif
+
+.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..e7a18b9
--- /dev/null
+++ b/usr.sbin/pkg_install/create/create.h
@@ -0,0 +1,53 @@
+/* $FreeBSD$ */
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * Include and define various things wanted by the create command.
+ *
+ */
+
+#ifndef _INST_CREATE_H_INCLUDE
+#define _INST_CREATE_H_INCLUDE
+
+extern char *Prefix;
+extern char *Comment;
+extern char *Desc;
+extern char *Display;
+extern char *Install;
+extern char *PostInstall;
+extern char *DeInstall;
+extern char *PostDeInstall;
+extern char *Contents;
+extern char *Require;
+extern char *SrcDir;
+extern char *ExcludeFrom;
+extern char *Mtree;
+extern char *Pkgdeps;
+extern char *Origin;
+extern char *InstalledPkg;
+extern char PlayPen[];
+extern int Dereference;
+extern int PlistOnly;
+
+enum zipper {NONE, GZIP, BZIP, BZIP2 };
+extern enum zipper Zipper;
+
+void check_list(const char *, Package *);
+int pkg_perform(char **);
+void copy_plist(const char *, Package *);
+
+#endif /* _INST_CREATE_H_INCLUDE */
diff --git a/usr.sbin/pkg_install/create/main.c b/usr.sbin/pkg_install/create/main.c
new file mode 100644
index 0000000..f309853
--- /dev/null
+++ b/usr.sbin/pkg_install/create/main.c
@@ -0,0 +1,205 @@
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * This is the create module.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <err.h>
+#include "lib.h"
+#include "create.h"
+
+static char Options[] = "YNOhjvyzf:p:P:c:d:i:I:k:K:r:t:X:D:m:s:o:b:";
+
+char *Prefix = NULL;
+char *Comment = NULL;
+char *Desc = NULL;
+char *SrcDir = NULL;
+char *Display = NULL;
+char *Install = NULL;
+char *PostInstall = NULL;
+char *DeInstall = NULL;
+char *PostDeInstall = NULL;
+char *Contents = NULL;
+char *Require = NULL;
+char *ExcludeFrom = NULL;
+char *Mtree = NULL;
+char *Pkgdeps = NULL;
+char *Origin = NULL;
+char *InstalledPkg = NULL;
+char PlayPen[FILENAME_MAX];
+int Dereference = FALSE;
+int PlistOnly = FALSE;
+enum zipper Zipper = GZIP;
+
+static void usage __P((void));
+
+int
+main(int argc, char **argv)
+{
+ int ch;
+ char **pkgs, **start, *tmp;
+
+ pkgs = start = argv;
+ while ((ch = getopt(argc, argv, Options)) != -1)
+ switch(ch) {
+ case 'v':
+ Verbose = TRUE;
+ break;
+
+ case 'N':
+ AutoAnswer = NO;
+ break;
+
+ case 'Y':
+ AutoAnswer = YES;
+ break;
+
+ case 'O':
+ PlistOnly = TRUE;
+ break;
+
+ case 'p':
+ Prefix = optarg;
+ break;
+
+ case 's':
+ SrcDir = optarg;
+ break;
+
+ case 'f':
+ Contents = optarg;
+ break;
+
+ case 'c':
+ Comment = optarg;
+ break;
+
+ case 'd':
+ Desc = optarg;
+ break;
+
+ case 'i':
+ Install = optarg;
+ break;
+
+ case 'I':
+ PostInstall = optarg;
+ break;
+
+ case 'k':
+ DeInstall = optarg;
+ break;
+
+ case 'K':
+ PostDeInstall = optarg;
+ break;
+
+ case 'r':
+ Require = optarg;
+ break;
+
+ case 't':
+ strlcpy(PlayPen, optarg, sizeof(PlayPen));
+ break;
+
+ case 'X':
+ ExcludeFrom = optarg;
+ break;
+
+ case 'h':
+ Dereference = TRUE;
+ break;
+
+ case 'D':
+ Display = optarg;
+ break;
+
+ case 'm':
+ Mtree = optarg;
+ break;
+
+ case 'P':
+ Pkgdeps = optarg;
+ break;
+
+ case 'o':
+ Origin = optarg;
+ break;
+
+ case 'y':
+ case 'j':
+ Zipper = BZIP2;
+ break;
+
+ case 'z':
+ Zipper = GZIP;
+ break;
+
+ case 'b':
+ InstalledPkg = optarg;
+ while ((tmp = strrchr(optarg, (int)'/')) != NULL) {
+ *tmp++ = '\0';
+ /*
+ * If character after the '/' is alphanumeric, then we've
+ * found the package name. Otherwise we've come across
+ * a trailing '/' and need to continue our quest.
+ */
+ if (isalpha(*tmp)) {
+ InstalledPkg = tmp;
+ break;
+ }
+ }
+ break;
+
+ case '?':
+ default:
+ usage();
+ break;
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ /* Get all the remaining package names, if any */
+ while (*argv)
+ *pkgs++ = *argv++;
+
+ /* If no packages, yelp */
+ if ((pkgs == start) && (InstalledPkg == NULL))
+ warnx("missing package name"), usage();
+ *pkgs = NULL;
+ if ((start[0] != NULL) && (start[1] != NULL)) {
+ warnx("only one package name allowed ('%s' extraneous)", start[1]);
+ usage();
+ }
+ if (start[0] == NULL)
+ start[0] = InstalledPkg;
+ if (!pkg_perform(start)) {
+ if (Verbose)
+ warnx("package creation failed");
+ return 1;
+ }
+ else
+ return 0;
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n",
+"usage: pkg_create [-YNOhvy] [-P pkgs] [-p prefix] [-f contents] [-i iscript]",
+" [-I piscript] [-k dscript] [-K pdscript] [-r rscript] ",
+" [-t template] [-X excludefile] [-D displayfile] ",
+" [-m mtreefile] [-o origin] -c comment -d description ",
+" -f packlist pkg-filename",
+" pkg_create [-YNhvy] -b pkg-name [pkg-filename]");
+ exit(1);
+}
diff --git a/usr.sbin/pkg_install/create/perform.c b/usr.sbin/pkg_install/create/perform.c
new file mode 100644
index 0000000..0be0c56
--- /dev/null
+++ b/usr.sbin/pkg_install/create/perform.c
@@ -0,0 +1,464 @@
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * This is the main body of the create module.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "lib.h"
+#include "create.h"
+
+#include <err.h>
+#include <libgen.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <sys/syslimits.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+static void sanity_check(void);
+static void make_dist(const char *, const char *, const char *, Package *);
+static int create_from_installed(const char *, const char *);
+
+static char *home;
+
+int
+pkg_perform(char **pkgs)
+{
+ char *pkg = *pkgs; /* Only one arg to create */
+ char *cp;
+ FILE *pkg_in, *fp;
+ Package plist;
+ int len;
+ const char *suf;
+
+ /* Preliminary setup */
+ if (InstalledPkg == NULL)
+ sanity_check();
+ if (Verbose && !PlistOnly)
+ printf("Creating package %s\n", pkg);
+
+ /* chop suffix off if already specified, remembering if we want to compress */
+ len = strlen(pkg);
+ if (len > 4) {
+ if (!strcmp(&pkg[len - 4], ".tgz")) {
+ Zipper = GZIP;
+ pkg[len - 4] = '\0';
+ }
+ else if (!strcmp(&pkg[len - 4], ".tar")) {
+ Zipper = NONE;
+ pkg[len - 4] = '\0';
+ }
+ else if (!strcmp(&pkg[len - 4], ".tbz")) {
+ Zipper = BZIP2;
+ pkg[len - 4] = '\0';
+ }
+ else if ((len > 5) && (!strcmp(&pkg[len - 5], ".tbz2"))) {
+ Zipper = BZIP2;
+ pkg[len - 5] = '\0';
+ }
+ }
+ if (Zipper == BZIP2) {
+ suf = "tbz2";
+ setenv("BZIP2", "-9", 0);
+ } else if (Zipper == GZIP) {
+ suf = "tgz";
+ setenv("GZIP", "-9", 0);
+ } else
+ suf = "tar";
+
+ if (InstalledPkg != NULL)
+ return (create_from_installed(pkg, suf));
+
+ get_dash_string(&Comment);
+ get_dash_string(&Desc);
+ if (!strcmp(Contents, "-"))
+ pkg_in = stdin;
+ else {
+ pkg_in = fopen(Contents, "r");
+ if (!pkg_in) {
+ cleanup(0);
+ errx(2, "%s: unable to open contents file '%s' for input",
+ __func__, Contents);
+ }
+ }
+ plist.head = plist.tail = NULL;
+
+ /* Stick the dependencies, if any, at the top */
+ if (Pkgdeps) {
+ char **deps, *deporigin;
+ int i;
+ int ndeps = 0;
+
+ if (Verbose && !PlistOnly)
+ printf("Registering depends:");
+
+ /* Count number of dependencies */
+ for (cp = Pkgdeps; cp != NULL && *cp != '\0';
+ cp = strpbrk(++cp, " \t\n")) {
+ ndeps++;
+ }
+
+ if (ndeps != 0) {
+ /* Create easy to use NULL-terminated list */
+ deps = alloca(sizeof(*deps) * ndeps + 1);
+ if (deps == NULL) {
+ errx(2, "%s: alloca() failed", __func__);
+ /* Not reached */
+ }
+ for (i = 0; Pkgdeps;) {
+ cp = strsep(&Pkgdeps, " \t\n");
+ if (*cp) {
+ deps[i] = cp;
+ i++;
+ }
+ }
+ ndeps = i;
+ deps[ndeps] = NULL;
+
+ sortdeps(deps);
+ for (i = 0; i < ndeps; i++) {
+ deporigin = strchr(deps[i], ':');
+ if (deporigin != NULL) {
+ *deporigin = '\0';
+ add_plist_top(&plist, PLIST_DEPORIGIN, ++deporigin);
+ }
+ add_plist_top(&plist, PLIST_PKGDEP, deps[i]);
+ if (Verbose && !PlistOnly)
+ printf(" %s", deps[i]);
+ }
+ }
+
+ if (Verbose && !PlistOnly)
+ printf(".\n");
+ }
+
+ /* If a SrcDir override is set, add it now */
+ if (SrcDir) {
+ if (Verbose && !PlistOnly)
+ printf("Using SrcDir value of %s\n", SrcDir);
+ add_plist(&plist, PLIST_SRC, SrcDir);
+ }
+
+ /* Slurp in the packing list */
+ read_plist(&plist, pkg_in);
+
+ /* Prefix should add an @cwd to the packing list */
+ if (Prefix)
+ add_plist_top(&plist, PLIST_CWD, Prefix);
+
+ /* Add the origin if asked, at the top */
+ if (Origin)
+ add_plist_top(&plist, PLIST_ORIGIN, Origin);
+
+ /*
+ * Run down the list and see if we've named it, if not stick in a name
+ * at the top.
+ */
+ if (find_plist(&plist, PLIST_NAME) == NULL)
+ add_plist_top(&plist, PLIST_NAME, basename(pkg));
+
+ if (asprintf(&cp, "PKG_FORMAT_REVISION:%d.%d", PLIST_FMT_VER_MAJOR,
+ PLIST_FMT_VER_MINOR) == -1) {
+ errx(2, "%s: asprintf() failed", __func__);
+ }
+ add_plist_top(&plist, PLIST_COMMENT, cp);
+ free(cp);
+
+ /*
+ * We're just here for to dump out a revised plist for the FreeBSD ports
+ * hack. It's not a real create in progress.
+ */
+ if (PlistOnly) {
+ check_list(home, &plist);
+ write_plist(&plist, stdout);
+ exit(0);
+ }
+
+ /* Make a directory to stomp around in */
+ home = make_playpen(PlayPen, 0);
+ signal(SIGINT, cleanup);
+ signal(SIGHUP, cleanup);
+
+ /* Make first "real contents" pass over it */
+ check_list(home, &plist);
+ (void) umask(022); /*
+ * Make sure gen'ed directories, files don't have
+ * group or other write bits.
+ */
+ /* copy_plist(home, &plist); */
+ /* mark_plist(&plist); */
+
+ /* Now put the release specific items in */
+ add_plist(&plist, PLIST_CWD, ".");
+ write_file(COMMENT_FNAME, Comment);
+ add_plist(&plist, PLIST_IGNORE, NULL);
+ add_plist(&plist, PLIST_FILE, COMMENT_FNAME);
+ write_file(DESC_FNAME, Desc);
+ add_plist(&plist, PLIST_IGNORE, NULL);
+ add_plist(&plist, PLIST_FILE, DESC_FNAME);
+
+ if (Install) {
+ copy_file(home, Install, INSTALL_FNAME);
+ add_plist(&plist, PLIST_IGNORE, NULL);
+ add_plist(&plist, PLIST_FILE, INSTALL_FNAME);
+ }
+ if (PostInstall) {
+ copy_file(home, PostInstall, POST_INSTALL_FNAME);
+ add_plist(&plist, PLIST_IGNORE, NULL);
+ add_plist(&plist, PLIST_FILE, POST_INSTALL_FNAME);
+ }
+ if (DeInstall) {
+ copy_file(home, DeInstall, DEINSTALL_FNAME);
+ add_plist(&plist, PLIST_IGNORE, NULL);
+ add_plist(&plist, PLIST_FILE, DEINSTALL_FNAME);
+ }
+ if (PostDeInstall) {
+ copy_file(home, PostDeInstall, POST_DEINSTALL_FNAME);
+ add_plist(&plist, PLIST_IGNORE, NULL);
+ add_plist(&plist, PLIST_FILE, POST_DEINSTALL_FNAME);
+ }
+ if (Require) {
+ copy_file(home, Require, REQUIRE_FNAME);
+ add_plist(&plist, PLIST_IGNORE, NULL);
+ add_plist(&plist, PLIST_FILE, REQUIRE_FNAME);
+ }
+ if (Display) {
+ copy_file(home, Display, DISPLAY_FNAME);
+ add_plist(&plist, PLIST_IGNORE, NULL);
+ add_plist(&plist, PLIST_FILE, DISPLAY_FNAME);
+ add_plist(&plist, PLIST_DISPLAY, DISPLAY_FNAME);
+ }
+ if (Mtree) {
+ copy_file(home, Mtree, MTREE_FNAME);
+ add_plist(&plist, PLIST_IGNORE, NULL);
+ add_plist(&plist, PLIST_FILE, MTREE_FNAME);
+ add_plist(&plist, PLIST_MTREE, MTREE_FNAME);
+ }
+
+ /* Finally, write out the packing list */
+ fp = fopen(CONTENTS_FNAME, "w");
+ if (!fp) {
+ cleanup(0);
+ errx(2, "%s: can't open file %s for writing",
+ __func__, CONTENTS_FNAME);
+ }
+ write_plist(&plist, fp);
+ if (fclose(fp)) {
+ cleanup(0);
+ errx(2, "%s: error while closing %s",
+ __func__, CONTENTS_FNAME);
+ }
+
+ /* And stick it into a tar ball */
+ make_dist(home, pkg, suf, &plist);
+
+ /* Cleanup */
+ free(Comment);
+ free(Desc);
+ free_plist(&plist);
+ leave_playpen();
+ return TRUE; /* Success */
+}
+
+static void
+make_dist(const char *homedir, const char *pkg, const char *suff, Package *plist)
+{
+ char tball[FILENAME_MAX];
+ PackingList p;
+ int ret;
+ const char *args[50]; /* Much more than enough. */
+ int nargs = 0;
+ int pipefds[2];
+ FILE *totar;
+ pid_t pid;
+ const char *cname;
+
+ args[nargs++] = "tar"; /* argv[0] */
+
+ if (*pkg == '/')
+ snprintf(tball, FILENAME_MAX, "%s.%s", pkg, suff);
+ else
+ snprintf(tball, FILENAME_MAX, "%s/%s.%s", homedir, pkg, suff);
+
+ args[nargs++] = "-c";
+ args[nargs++] = "-f";
+ args[nargs++] = tball;
+ if (strchr(suff, 'z')) { /* Compress/gzip/bzip2? */
+ if (Zipper == BZIP2) {
+ args[nargs++] = "-j";
+ cname = "bzip'd ";
+ }
+ else {
+ args[nargs++] = "-z";
+ cname = "gzip'd ";
+ }
+ } else {
+ cname = "";
+ }
+ if (Dereference)
+ args[nargs++] = "-h";
+ if (ExcludeFrom) {
+ args[nargs++] = "-X";
+ args[nargs++] = ExcludeFrom;
+ }
+ args[nargs++] = "-T"; /* Take filenames from file instead of args. */
+ args[nargs++] = "-"; /* Use stdin for the file. */
+ args[nargs] = NULL;
+
+ if (Verbose)
+ printf("Creating %star ball in '%s'\n", cname, tball);
+
+ /* Set up a pipe for passing the filenames, and fork off a tar process. */
+ if (pipe(pipefds) == -1) {
+ cleanup(0);
+ errx(2, "%s: cannot create pipe", __func__);
+ }
+ if ((pid = fork()) == -1) {
+ cleanup(0);
+ errx(2, "%s: cannot fork process for tar", __func__);
+ }
+ if (pid == 0) { /* The child */
+ dup2(pipefds[0], 0);
+ close(pipefds[0]);
+ close(pipefds[1]);
+ execv("/usr/bin/tar", (char * const *)(uintptr_t)args);
+ cleanup(0);
+ errx(2, "%s: failed to execute tar command", __func__);
+ }
+
+ /* Meanwhile, back in the parent process ... */
+ close(pipefds[0]);
+ if ((totar = fdopen(pipefds[1], "w")) == NULL) {
+ cleanup(0);
+ errx(2, "%s: fdopen failed", __func__);
+ }
+
+ fprintf(totar, "%s\n", CONTENTS_FNAME);
+ fprintf(totar, "%s\n", COMMENT_FNAME);
+ fprintf(totar, "%s\n", DESC_FNAME);
+
+ if (Install)
+ fprintf(totar, "%s\n", INSTALL_FNAME);
+ if (PostInstall)
+ fprintf(totar, "%s\n", POST_INSTALL_FNAME);
+ if (DeInstall)
+ fprintf(totar, "%s\n", DEINSTALL_FNAME);
+ if (PostDeInstall)
+ fprintf(totar, "%s\n", POST_DEINSTALL_FNAME);
+ if (Require)
+ fprintf(totar, "%s\n", REQUIRE_FNAME);
+ if (Display)
+ fprintf(totar, "%s\n", DISPLAY_FNAME);
+ if (Mtree)
+ fprintf(totar, "%s\n", MTREE_FNAME);
+
+ for (p = plist->head; p; p = p->next) {
+ if (p->type == PLIST_FILE)
+ fprintf(totar, "%s\n", p->name);
+ else if (p->type == PLIST_CWD || p->type == PLIST_SRC)
+ fprintf(totar, "-C\n%s\n", p->name);
+ else if (p->type == PLIST_IGNORE)
+ p = p->next;
+ }
+
+ fclose(totar);
+ wait(&ret);
+ /* assume either signal or bad exit is enough for us */
+ if (ret) {
+ cleanup(0);
+ errx(2, "%s: tar command failed with code %d", __func__, ret);
+ }
+}
+
+static void
+sanity_check()
+{
+ if (!Comment) {
+ cleanup(0);
+ errx(2, "%s: required package comment string is missing (-c comment)",
+ __func__);
+ }
+ if (!Desc) {
+ cleanup(0);
+ errx(2, "%s: required package description string is missing (-d desc)",
+ __func__);
+ }
+ if (!Contents) {
+ cleanup(0);
+ errx(2, "%s: required package contents list is missing (-f [-]file)",
+ __func__);
+ }
+}
+
+
+/* Clean up those things that would otherwise hang around */
+void
+cleanup(int sig)
+{
+ int in_cleanup = 0;
+
+ if (!in_cleanup) {
+ in_cleanup = 1;
+ leave_playpen();
+ }
+ if (sig)
+ exit(1);
+}
+
+static int
+create_from_installed(const char *pkg, const char *suf)
+{
+ FILE *fp;
+ Package plist;
+ char homedir[MAXPATHLEN], log_dir[FILENAME_MAX];
+
+ snprintf(log_dir, sizeof(log_dir), "%s/%s", LOG_DIR, InstalledPkg);
+ if (!fexists(log_dir)) {
+ warnx("can't find package '%s' installed!", InstalledPkg);
+ return FALSE;
+ }
+ getcwd(homedir, sizeof(homedir));
+ if (chdir(log_dir) == FAIL) {
+ warnx("can't change directory to '%s'!", log_dir);
+ return FALSE;
+ }
+ /* Suck in the contents list */
+ plist.head = plist.tail = NULL;
+ fp = fopen(CONTENTS_FNAME, "r");
+ if (!fp) {
+ warnx("unable to open %s file", CONTENTS_FNAME);
+ return FALSE;
+ }
+ read_plist(&plist, fp);
+ fclose(fp);
+
+ (const char *)Install = isfile(INSTALL_FNAME) ? INSTALL_FNAME : NULL;
+ (const char *)PostInstall = isfile(POST_INSTALL_FNAME) ? POST_INSTALL_FNAME : NULL;
+ (const char *)DeInstall = isfile(DEINSTALL_FNAME) ? DEINSTALL_FNAME : NULL;
+ (const char *)PostDeInstall = isfile(POST_DEINSTALL_FNAME) ? POST_DEINSTALL_FNAME : NULL;
+ (const char *)Require = isfile(REQUIRE_FNAME) ? REQUIRE_FNAME : NULL;
+ (const char *)Display = isfile(DISPLAY_FNAME) ? DISPLAY_FNAME : NULL;
+ (const char *)Mtree = isfile(MTREE_FNAME) ? MTREE_FNAME : NULL;
+
+ make_dist(homedir, pkg, suf, &plist);
+
+ free_plist(&plist);
+ return TRUE;
+}
diff --git a/usr.sbin/pkg_install/create/pkg_create.1 b/usr.sbin/pkg_install/create/pkg_create.1
new file mode 100644
index 0000000..5ae414f
--- /dev/null
+++ b/usr.sbin/pkg_install/create/pkg_create.1
@@ -0,0 +1,545 @@
+.\"
+.\" FreeBSD install - a package for the installation and maintainance
+.\" of non-core utilities.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" Jordan K. Hubbard
+.\"
+.\"
+.\" @(#)pkg_create.1
+.\" $FreeBSD$
+.\"
+.\" hacked up by John Kohl for NetBSD--fixed a few bugs, extended keywords,
+.\" added dependency tracking, etc.
+.\"
+.\" [jkh] Took John's changes back and made some additional extensions for
+.\" better integration with FreeBSD's new ports collection.
+.\"
+.Dd April 21, 1995
+.Dt PKG_CREATE 1
+.Os
+.Sh NAME
+.Nm pkg_create
+.Nd a utility for creating software package distributions
+.Sh SYNOPSIS
+.Nm
+.Op Fl YNOhjvyz
+.Op Fl P Ar pkgs
+.Op Fl p Ar prefix
+.Op Fl f Ar contents
+.Op Fl i Ar iscript
+.Op Fl I Ar piscript
+.Op Fl k Ar dscript
+.Op Fl K Ar pdscript
+.Op Fl r Ar rscript
+.Op Fl s Ar srcdir
+.Op Fl t Ar template
+.Op Fl X Ar excludefile
+.Op Fl D Ar displayfile
+.Op Fl m Ar mtreefile
+.Op Fl o Ar originpath
+.Fl c Ar comment
+.Fl d Ar description
+.Fl f Ar packlist
+.Ar pkg-filename
+.Nm
+.Op Fl YNhvy
+.Fl b Ar pkg-name
+.Op Ar pkg-filename
+.Sh DESCRIPTION
+The
+.Nm
+command is used to create packages that will subsequently be fed to
+one of the package extraction/info utilities. The input description
+and command line arguments for the creation of a package are not
+really meant to be human-generated, though it is easy enough to
+do so. It is more expected that you will use a front-end tool for
+the job rather than muddling through it yourself. Nonetheless, a short
+description of the input syntax is included in this document.
+.Sh OPTIONS
+The following command line options are supported:
+.Bl -tag -width indent
+.It Fl f Ar packinglist
+Fetch
+.Dq packing list
+for package from the file
+.Ar packinglist
+or
+.Cm stdin
+if
+.Ar packinglist
+is a
+.Cm -
+(dash).
+.It Fl c Xo
+.Oo Fl Oc Ns Ar desc
+.Xc
+Fetch package
+.Dq one line description
+from file
+.Ar desc
+or, if preceded by
+.Cm - ,
+the argument itself. This string should also
+give some idea of which version of the product (if any) the package
+represents.
+.It Fl d Xo
+.Oo Fl Oc Ns Ar desc
+.Xc
+Fetch long description for package from file
+.Ar desc
+or, if preceded by
+.Cm - ,
+the argument itself.
+.It Fl Y
+Assume a default answer of `Yes' for any questions asked.
+.It Fl N
+Assume a default answer of `No' for any questions asked.
+.It Fl O
+Go into a `packing list Only' mode. This is a custom hack for the
+.Fx
+.Em "Ports Collection"
+and is used to do `fake pkg_add' operations when a port is installed.
+In such cases, it is necessary to know what the final, adjusted packing
+list will look like.
+.It Fl v
+Turn on verbose output.
+.It Fl h
+Force tar to follow symbolic links, so that the files they point to
+are dumped, rather than the links themselves.
+.It Fl i Ar iscript
+Set
+.Ar iscript
+to be the pre-install procedure for the package. This can be any executable
+program (or shell script). It will be invoked automatically when the
+package is later installed.
+It will be passed the package's name as the
+first argument.
+.Pp
+.Sy Note :
+if the
+.Fl I
+option is not given, this script will serve as both the pre-install and the
+post-install script for the package, differentiating between the
+functionality by passing the keywords
+.Ar PRE-INSTALL
+and
+.Ar POST-INSTALL
+respectively, 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
+.Sx "PACKING LIST DETAILS"
+section below).
+Each argiment from the
+.Ar pkgs
+list could be in the form
+.Ar pkgname Ns Op : Ns Ar pkgorigin ,
+where optional
+.Ar pkgorigin
+element denotes origin of each dependency from the list and it is
+recorded into the packing list along with the
+.Ar pkgname
+using
+.Cm @comment
+directive.
+.It Fl p Ar prefix
+Set
+.Ar prefix
+as the initial directory
+.Dq base
+to start from in selecting files for
+the package.
+.It Fl k Ar dscript
+Set
+.Ar dscript
+to be the de-install procedure for the package. This can be any executable
+program (or shell script). It will be invoked automatically when the
+package is later (if ever) de-installed.
+It will be passed the package's
+name as the first argument.
+.Pp
+.Sy Note :
+if the
+.Fl K
+option is not given, this script will serve as both the de-install and the
+post-deinstall script for the package, differentiating between the
+functionality by passing the keywords
+.Ar DEINSTALL
+and
+.Ar POST-DEINSTALL
+respectively, along with the package's name.
+.It Fl K Ar pdscript
+Set
+.Ar pdscript
+to be the post-deinstall procedure for the package. This can be any
+executable program (or shell script). It will be invoked automatically when
+the package is later de-installed.
+It will be passed the package's name as
+the first argument.
+.It Fl r Ar rscript
+Set
+.Ar rscript
+to be the
+.Dq requirements
+procedure for the package. This can be any
+executable program (or shell script). It will be invoked automatically
+at installation/deinstallation time to determine whether or not
+installation/deinstallation should proceed.
+To differentiate between installation and deinstallation, the keywords
+.Ar INSTALL
+and
+.Ar DEINSTALL
+are passed respectively, along with the package's name.
+.It Fl s Ar srcdir
+.Ar srcdir
+will override the value of
+.Cm @cwd
+during package creation.
+.It Fl t Ar template
+Use
+.Ar template
+as the input to
+.Xr mktemp 3 .
+By default, this is the string
+.Pa /tmp/instmp.XXXXXX ,
+but it may be necessary to override it in the situation where
+space in your
+.Pa /tmp
+directory is limited. Be sure to leave some number of `X' characters
+for
+.Xr mktemp 3
+to fill in with a unique ID.
+.It Fl X Ar excludefile
+Pass
+.Ar excludefile
+as a
+.Fl exclude-from
+argument to
+.Cm tar
+when creating final package. See
+.Cm tar
+man page (or run
+.Cm tar
+with
+.Fl -help
+flag) for further information on using this flag.
+.It Fl D Ar displayfile
+Display the file (by concatenating it to stdout)
+after installing the package. Useful for things like
+legal notices on almost-free software, etc.
+.It Fl m Ar mtreefile
+Run
+.Xr mtree 8
+with input from mtreefile before the package is installed.
+Mtree is invoked as
+.Cm mtree
+.Fl u
+.Fl f
+.Ar mtreefile
+.Fl d
+.Fl e
+.Fl p
+.Pa prefix ,
+where
+.Pa prefix
+is the name of the first directory named by a
+.Cm @cwd
+directive.
+.It Fl o Ar originpath
+Record an
+.Ar originpath ,
+as location of the port from which package has been created in the
+.Fx
+.Em "Ports Collection" .
+It should be in the form
+.Pa MASTERCATEGORY/PORTDIR .
+.It Fl j
+Use
+.Xr bzip2 1
+utility to compress package tarball instead of
+.Xr gzip 1 .
+Please note that this option is a NO-OP if the format of the resulting
+archive is explicitly specified by the recognizeable suffix of
+.Ar pkg-filename .
+Currently
+.Nm
+recognizes the following suffixes:
+.Pa .tgz , .tar , .tbz
+and
+.Pa .tbz2 .
+.It Fl y
+Compatibility synonym for
+.Fl j .
+.It Fl z
+Use
+.Xr gzip 1
+utility to compress package tarball.
+.It Fl b Ar pkg-name
+Create package file from a locally installed package named
+.Ar pkg-name .
+If the
+.Ar pkg-filename
+is not specified, then resulting archive will be created in the
+current directory and named
+.Ar pkg-name
+with an appropriate extraction suffix applied.
+.El
+.Sh PACKING LIST DETAILS
+The
+.Dq packing list
+format (see
+.Fl f )
+is fairly simple, being
+nothing more than a single column of filenames to include in the
+package. However, since absolute pathnames are generally a bad idea
+for a package that could be installed potentially anywhere, there is
+another method of specifying where things are supposed to go
+and, optionally, what ownership and mode information they should be
+installed with. This is done by 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
+.Dq basename
+of the fully qualified filename, that
+is the current directory prefix, plus the last filespec, minus
+the trailing filename. In the example case, that would be
+.Pa /usr/local/bin .
+.It Cm "%f"
+Expand to the
+filename
+part of the fully qualified name, or
+the converse of
+.Cm \&%B ,
+being in the example case,
+.Pa emacs .
+.El
+.It Cm @unexec Ar command
+Execute
+.Ar command
+as part of the deinstallation process. Expansion of special
+.Cm %
+sequences is the same as for
+.Cm @exec .
+This command is not executed during the package add, as
+.Cm @exec
+is, but rather when the package is deleted. This is useful
+for deleting links and other ancillary files that were created
+as a result of adding the package, but not directly known to
+the package's table of contents (and hence not automatically
+removable). The advantage of using
+.Cm @unexec
+over a deinstallation script is that you can use the
+.Dq special sequence expansion
+to get at files regardless of where they've
+been potentially redirected (see
+.Fl p ) .
+.It Cm @mode Ar mode
+Set default permission for all subsequently extracted files to
+.Ar mode .
+Format is the same as that used by the
+.Cm chmod
+command (well, considering that it's later handed off to it, that's
+no surprise). Use without an arg to set back to default (extraction)
+permissions.
+.It Cm @option Ar option
+Set internal package options, the only two currently supported ones
+being
+.Ar extract-in-place ,
+which tells the pkg_add command not to extract the package's tarball
+into a staging area but rather directly into the target
+hierarchy (this is typically meant to be used only by distributions
+or other special package types), and
+.Ar preserve ,
+which tells pkg_add to move any existing files out of the way,
+preserving the previous contents (which are also resurrected on
+pkg_delete, so caveat emptor).
+.It Cm @owner Ar user
+Set default ownership for all subsequently extracted files to
+.Ar user .
+Use without an arg to set back to default (extraction)
+ownership.
+.It Cm @group Ar group
+Set default group ownership for all subsequently extracted files to
+.Ar group .
+Use without an arg to set back to default (extraction)
+group ownership.
+.It Cm @comment Ar string
+Imbed a comment in the packing list. Useful in
+trying to document some particularly hairy sequence that
+may trip someone up later.
+.It Cm @ignore
+Used internally to tell extraction to ignore the next file (don't
+copy it anywhere), as it's used for some special purpose.
+.It Cm @ignore_inst
+Similar to
+.Cm @ignore ,
+but the ignoring of the next file is delayed one evaluation cycle. This
+makes it possible to use this directive in the
+.Ar packinglist
+file, so you can pack a
+specialized datafile in with a distribution for your install script (or
+something) yet have the installer ignore it.
+.It Cm @name Ar name
+Set the name of the package. This is mandatory and is usually
+put at the top. This name is potentially different from the name of
+the file it came in, and is used when keeping track of the package
+for later deinstallation. Note that
+.Nm
+will derive this field from the package name and add it automatically
+if none is given.
+.It Cm @dirrm Ar name
+Declare directory
+.Pa name
+to be deleted at deinstall time. By default, directories created by a
+package installation are not deleted when the package is deinstalled;
+this provides an explicit directory cleanup method. This directive
+should appear at the end of the package list. If more than one
+.Cm @dirrm
+directives are used, the directories are removed in the order specified.
+The
+.Pa name
+directory will not be removed unless it is empty.
+.It Cm @mtree Ar name
+Declare
+.Pa name
+as an
+.Xr mtree 8
+input file to be used at install time (see
+.Fl m
+above). Only the first
+.Cm @mtree
+directive is honored.
+.It Cm @display Ar name
+Declare
+.Pa name
+as the file to be displayed at install time (see
+.Fl D
+above).
+.It Cm @pkgdep Ar pkgname
+Declare a dependency on the
+.Ar pkgname
+package. The
+.Ar pkgname
+package must be installed before this package may be
+installed, and this package must be deinstalled before the
+.Ar pkgname
+package is deinstalled. Multiple
+.Cm @pkgdep
+directives may be used if the package depends on multiple other packages.
+.El
+.Sh ENVIRONMENT
+The environment variable
+.Ev PKG_TMPDIR
+names the directory where
+.Nm
+will attempt to create its temporary files.
+If
+.Ev PKG_TMPDIR
+is not set,
+the directory named by the contents of
+.Ev TMPDIR
+will be used.
+If neither of
+.Ev PKG_TMPDIR
+and
+.Ev TMPDIR
+are set, the builtin defaults are used.
+.Sh FILES
+.Bl -tag -width /usr/tmp -compact
+.It Pa /var/tmp
+Temporary directory if environmental variables
+.Ev PKG_TMPDIR
+and
+.Ev TMPDIR
+are not set.
+.It Pa /tmp
+The next choice if
+.Pa /var/tmp
+does not exist.
+.It Pa /usr/tmp
+The last choice if
+.Pa /tmp
+is unsuitable.
+.El
+.Sh SEE ALSO
+.Xr pkg_add 1 ,
+.Xr pkg_delete 1 ,
+.Xr pkg_info 1 ,
+.Xr pkg_version 1 ,
+.Xr sysconf 3
+.Sh HISTORY
+The
+.Nm
+command first appeared in
+.Fx .
+.Sh AUTHORS
+.An Jordan Hubbard
+.Sh CONTRIBUTORS
+.An John Kohl Aq jtk@rational.com
+.Sh BUGS
+Hard links between files in a distribution must be bracketed by
+.Cm @cwd
+directives in order to be preserved as hard links when the package is
+extracted. They additionally must not end up being split between
+.Cm tar
+invocations due to exec argument-space limitations (this depends on the
+value returned by
+.Fn sysconf _SC_ARG_MAX ) .
+.Pp
+Sure to be others.
diff --git a/usr.sbin/pkg_install/create/pl.c b/usr.sbin/pkg_install/create/pl.c
new file mode 100644
index 0000000..d8b3d7e
--- /dev/null
+++ b/usr.sbin/pkg_install/create/pl.c
@@ -0,0 +1,257 @@
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * Routines for dealing with the packing list.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "lib.h"
+#include "create.h"
+#include <errno.h>
+#include <err.h>
+#include <md5.h>
+
+/* Check a list for files that require preconversion */
+void
+check_list(const char *home, Package *pkg)
+{
+ const char *where = home;
+ const char *there = NULL;
+ char *cp, name[FILENAME_MAX], buf[33];
+ PackingList p;
+
+ for (p = pkg->head; p != NULL; p = p->next)
+ switch (p->type) {
+ case PLIST_CWD:
+ where = p->name;
+ break;
+
+ case PLIST_IGNORE:
+ p = p->next;
+ break;
+
+ case PLIST_SRC:
+ there = p->name;
+ break;
+
+ case PLIST_FILE:
+ cp = NULL;
+ sprintf(name, "%s/%s", there ? there : where, p->name);
+ if (issymlink(name)) {
+ int len;
+ char lnk[FILENAME_MAX];
+
+ if ((len = readlink(name, lnk, FILENAME_MAX)) > 0)
+ cp = MD5Data((unsigned char *)lnk, len, buf);
+ } else if (isfile(name)) {
+ /* Don't record MD5 checksum for device nodes and such */
+ cp = MD5File(name, buf);
+ }
+
+ if (cp != NULL) {
+ PackingList tmp = new_plist_entry();
+
+ tmp->name = copy_string(strconcat("MD5:", cp));
+ tmp->type = PLIST_COMMENT;
+ tmp->next = p->next;
+ tmp->prev = p;
+ p->next = tmp;
+ if (pkg->tail == p)
+ pkg->tail = tmp;
+ p = tmp;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static int
+trylink(const char *from, const char *to)
+{
+ if (link(from, to) == 0)
+ return 0;
+ if (errno == ENOENT) {
+ /* try making the container directory */
+ char *cp = strrchr(to, '/');
+ if (cp)
+ vsystem("mkdir -p %.*s", cp - to,
+ to);
+ return link(from, to);
+ }
+ return -1;
+}
+
+#define STARTSTRING "tar cf -"
+#define TOOBIG(str) (int)strlen(str) + 6 + (int)strlen(home) + where_count > maxargs
+#define PUSHOUT() /* push out string */ \
+ if (where_count > (int)sizeof(STARTSTRING)-1) { \
+ strcat(where_args, "|tar xpf -"); \
+ if (system(where_args)) { \
+ cleanup(0); \
+ errx(2, "%s: can't invoke tar pipeline", __func__); \
+ } \
+ memset(where_args, 0, maxargs); \
+ last_chdir = NULL; \
+ strcpy(where_args, STARTSTRING); \
+ where_count = sizeof(STARTSTRING)-1; \
+ }
+
+/*
+ * Copy unmarked files in packing list to playpen - marked files
+ * have already been copied in an earlier pass through the list.
+ */
+void
+copy_plist(const char *home, Package *plist)
+{
+ PackingList p = plist->head;
+ const char *where = home;
+ const char *there = NULL, *mythere;
+ char *where_args;
+ const char *last_chdir, *root = "/";
+ int maxargs, where_count = 0, add_count;
+ struct stat stb;
+ dev_t curdir;
+
+ maxargs = sysconf(_SC_ARG_MAX);
+ maxargs -= 64; /*
+ * Some slop for the tar cmd text,
+ * and sh -c
+ */
+ where_args = malloc(maxargs);
+ if (!where_args) {
+ cleanup(0);
+ errx(2, "%s: can't get argument list space", __func__);
+ }
+
+ memset(where_args, 0, maxargs);
+ strcpy(where_args, STARTSTRING);
+ where_count = sizeof(STARTSTRING)-1;
+ last_chdir = 0;
+
+ if (stat(".", &stb) == 0)
+ curdir = stb.st_dev;
+ else
+ curdir = (dev_t) -1; /*
+ * It's ok if this is a valid dev_t;
+ * this is just a hint for an
+ * optimization.
+ */
+
+ while (p) {
+ if (p->type == PLIST_CWD)
+ where = p->name;
+ else if (p->type == PLIST_SRC)
+ there = p->name;
+ else if (p->type == PLIST_IGNORE)
+ p = p->next;
+ else if (p->type == PLIST_FILE && !p->marked) {
+ char fn[FILENAME_MAX];
+
+
+ /* First, look for it in the "home" dir */
+ sprintf(fn, "%s/%s", home, p->name);
+ if (fexists(fn)) {
+ if (lstat(fn, &stb) == 0 && stb.st_dev == curdir &&
+ S_ISREG(stb.st_mode)) {
+ /*
+ * If we can link it to the playpen, that avoids a copy
+ * and saves time.
+ */
+ if (p->name[0] != '/') {
+ /*
+ * Don't link abspn stuff--it doesn't come from
+ * local dir!
+ */
+ if (trylink(fn, p->name) == 0) {
+ p = p->next;
+ continue;
+ }
+ }
+ }
+ if (TOOBIG(fn)) {
+ PUSHOUT();
+ }
+ if (p->name[0] == '/') {
+ add_count = snprintf(&where_args[where_count],
+ maxargs - where_count,
+ " %s %s",
+ last_chdir == root ? "" : "-C /",
+ p->name);
+ last_chdir = root;
+ } else {
+ add_count = snprintf(&where_args[where_count],
+ maxargs - where_count,
+ " %s%s %s",
+ last_chdir == home ? "" : "-C ",
+ last_chdir == home ? "" : home,
+ p->name);
+ last_chdir = home;
+ }
+ if (add_count < 0 || add_count > maxargs - where_count) {
+ cleanup(0);
+ errx(2, "%s: oops, miscounted strings!", __func__);
+ }
+ where_count += add_count;
+ }
+ /*
+ * Otherwise, try along the actual extraction path..
+ */
+ else {
+ if (p->name[0] == '/')
+ mythere = root;
+ else mythere = there;
+ sprintf(fn, "%s/%s", mythere ? mythere : where, p->name);
+ if (lstat(fn, &stb) == 0 && stb.st_dev == curdir &&
+ S_ISREG(stb.st_mode)) {
+ /*
+ * If we can link it to the playpen, that avoids a copy
+ * and saves time.
+ */
+ if (trylink(fn, p->name) == 0) {
+ p = p->next;
+ continue;
+ }
+ }
+ if (TOOBIG(p->name)) {
+ PUSHOUT();
+ }
+ if (last_chdir == (mythere ? mythere : where))
+ add_count = snprintf(&where_args[where_count],
+ maxargs - where_count,
+ " %s", p->name);
+ else
+ add_count = snprintf(&where_args[where_count],
+ maxargs - where_count,
+ " -C %s %s",
+ mythere ? mythere : where,
+ p->name);
+ if (add_count < 0 || add_count > maxargs - where_count) {
+ cleanup(0);
+ errx(2, "%s: oops, miscounted strings!", __func__);
+ }
+ where_count += add_count;
+ last_chdir = (mythere ? mythere : where);
+ }
+ }
+ p = p->next;
+ }
+ PUSHOUT();
+ free(where_args);
+}
diff --git a/usr.sbin/pkg_install/delete/Makefile b/usr.sbin/pkg_install/delete/Makefile
new file mode 100644
index 0000000..47642cc
--- /dev/null
+++ b/usr.sbin/pkg_install/delete/Makefile
@@ -0,0 +1,18 @@
+# $FreeBSD$
+
+PROG= pkg_delete
+SRCS= main.c perform.c
+
+CFLAGS+= ${DEBUG} -I${.CURDIR}/../lib
+
+WARNS?= 2
+
+DPADD= ${LIBINSTALL} ${LIBFETCH} ${LIBMD}
+LDADD= ${LIBINSTALL} -lfetch -lmd
+
+.if !defined(NOCRYPT) && !defined(NOSECURE) && !defined(NO_OPENSSL)
+DPADD+= ${LIBSSL} ${LIBCRYPTO}
+LDADD+= -lssl -lcrypto
+.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pkg_install/delete/delete.h b/usr.sbin/pkg_install/delete/delete.h
new file mode 100644
index 0000000..a973642
--- /dev/null
+++ b/usr.sbin/pkg_install/delete/delete.h
@@ -0,0 +1,36 @@
+/* $FreeBSD$ */
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * Include and define various things wanted by the delete command.
+ *
+ */
+
+#ifndef _INST_DELETE_H_INCLUDE
+#define _INST_DELETE_H_INCLUDE
+
+extern char *Prefix;
+extern Boolean CleanDirs;
+extern Boolean Interactive;
+extern Boolean NoDeInstall;
+extern Boolean Force;
+extern Boolean Recursive;
+extern char *Directory;
+extern char *PkgName;
+extern match_t MatchType;
+
+#endif /* _INST_DELETE_H_INCLUDE */
diff --git a/usr.sbin/pkg_install/delete/main.c b/usr.sbin/pkg_install/delete/main.c
new file mode 100644
index 0000000..dcac1d6
--- /dev/null
+++ b/usr.sbin/pkg_install/delete/main.c
@@ -0,0 +1,157 @@
+/*
+ *
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * This is the delete module.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <err.h>
+#include "lib.h"
+#include "delete.h"
+
+static char Options[] = "adDfGhinp:rvx";
+
+char *Prefix = NULL;
+Boolean CleanDirs = FALSE;
+Boolean Interactive = FALSE;
+Boolean NoDeInstall = FALSE;
+Boolean Recursive = FALSE;
+match_t MatchType = MATCH_GLOB;
+
+static void usage __P((void));
+
+int
+main(int argc, char **argv)
+{
+ int ch, error;
+ char **pkgs, **start;
+ char *pkgs_split;
+ const char *tmp;
+ struct stat stat_s;
+
+ pkgs = start = argv;
+ while ((ch = getopt(argc, argv, Options)) != -1)
+ switch(ch) {
+ case 'v':
+ Verbose = TRUE;
+ break;
+
+ case 'f':
+ Force = TRUE;
+ break;
+
+ case 'p':
+ Prefix = optarg;
+ break;
+
+ case 'D':
+ NoDeInstall = TRUE;
+ break;
+
+ case 'd':
+ CleanDirs = TRUE;
+ break;
+
+ case 'n':
+ Fake = TRUE;
+ Verbose = TRUE;
+ break;
+
+ case 'a':
+ MatchType = MATCH_ALL;
+ break;
+
+ case 'G':
+ MatchType = MATCH_EXACT;
+ break;
+
+ case 'x':
+ MatchType = MATCH_REGEX;
+ break;
+
+ case 'i':
+ Interactive = TRUE;
+ break;
+
+ case 'r':
+ Recursive = TRUE;
+ break;
+
+ case 'h':
+ case '?':
+ default:
+ usage();
+ break;
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ /* Get all the remaining package names, if any */
+ while (*argv) {
+ /* Don't try to apply heuristics if arguments are regexs */
+ if (MatchType != MATCH_REGEX)
+ while ((pkgs_split = strrchr(*argv, (int)'/')) != NULL) {
+ *pkgs_split++ = '\0';
+ /*
+ * If character after the '/' is alphanumeric, then we've found the
+ * package name. Otherwise we've come across a trailing '/' and
+ * need to continue our quest.
+ */
+ if (isalpha(*pkgs_split) || ((MatchType == MATCH_GLOB) && \
+ strpbrk(pkgs_split, "*?[]") != NULL)) {
+ *argv = pkgs_split;
+ break;
+ }
+ }
+ *pkgs++ = *argv++;
+ }
+
+ /* If no packages, yelp */
+ if (pkgs == start && MatchType != MATCH_ALL)
+ warnx("missing package name(s)"), usage();
+ *pkgs = NULL;
+ tmp = LOG_DIR;
+ (void) stat(tmp, &stat_s);
+ if (!Fake && getuid() && geteuid() != stat_s.st_uid) {
+ if (!Force)
+ errx(1, "you do not own %s, use -f to force", tmp);
+ else
+ warnx("you do not own %s (proceeding anyways)", tmp);
+ }
+ if ((error = pkg_perform(start)) != 0) {
+ if (Verbose)
+ warnx("%d package deletion(s) failed", error);
+ return error;
+ }
+ else
+ return 0;
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n",
+ "usage: pkg_delete [-dDfGinrvx] [-p prefix] pkg-name ...",
+ " pkg_delete -a [flags]");
+ exit(1);
+}
diff --git a/usr.sbin/pkg_install/delete/perform.c b/usr.sbin/pkg_install/delete/perform.c
new file mode 100644
index 0000000..1102165
--- /dev/null
+++ b/usr.sbin/pkg_install/delete/perform.c
@@ -0,0 +1,383 @@
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * This is the main body of the delete module.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <err.h>
+#include "lib.h"
+#include "delete.h"
+
+static int pkg_do(char *);
+static void sanity_check(char *);
+static void undepend(char *, char *);
+static char LogDir[FILENAME_MAX];
+
+
+int
+pkg_perform(char **pkgs)
+{
+ char **matched, **rb, **rbtmp;
+ int errcode, i, j;
+ int err_cnt = 0;
+ struct reqr_by_entry *rb_entry;
+ struct reqr_by_head *rb_list;
+
+ if (MatchType != MATCH_EXACT) {
+ matched = matchinstalled(MatchType, pkgs, &errcode);
+ if (errcode != 0)
+ return 1;
+ /* Not reached */
+
+ /*
+ * Copy matched[] into pkgs[], because we'll need to use
+ * matchinstalled() later on.
+ */
+ if (matched != NULL) {
+ pkgs = NULL;
+ for (i = 0; matched[i] != NULL; i++) {
+ pkgs = realloc(pkgs, sizeof(*pkgs) * (i + 2));
+ pkgs[i] = strdup(matched[i]);
+ }
+ pkgs[i] = NULL;
+ }
+ else switch (MatchType) {
+ case MATCH_GLOB:
+ break;
+ case MATCH_ALL:
+ warnx("no packages installed");
+ return 0;
+ case MATCH_REGEX:
+ warnx("no packages match pattern(s)");
+ return 1;
+ default:
+ break;
+ }
+ }
+
+ err_cnt += sortdeps(pkgs);
+ for (i = 0; pkgs[i]; i++) {
+ if (Recursive == TRUE) {
+ errcode = requiredby(pkgs[i], &rb_list, FALSE, TRUE);
+ if (errcode < 0) {
+ err_cnt++;
+ } else if (errcode > 0) {
+ /*
+ * Copy values from the rb_list queue into argv-like NULL
+ * terminated list because requiredby() uses some static
+ * storage, while pkg_do() below will call this function,
+ * thus blowing our rb_list away.
+ */
+ rbtmp = rb = alloca((errcode + 1) * sizeof(*rb));
+ if (rb == NULL) {
+ warnx("%s(): alloca() failed", __func__);
+ err_cnt++;
+ continue;
+ }
+ STAILQ_FOREACH(rb_entry, rb_list, link) {
+ *rbtmp = alloca(strlen(rb_entry->pkgname) + 1);
+ if (*rbtmp == NULL) {
+ warnx("%s(): alloca() failed", __func__);
+ err_cnt++;
+ continue;
+ }
+ strcpy(*rbtmp, rb_entry->pkgname);
+ rbtmp++;
+ }
+ *rbtmp = NULL;
+
+ err_cnt += sortdeps(rb);
+ for (j = 0; rb[j]; j++)
+ err_cnt += pkg_do(rb[j]);
+ }
+ }
+ err_cnt += pkg_do(pkgs[i]);
+ }
+
+ return err_cnt;
+}
+
+static Package Plist;
+
+/* This is seriously ugly code following. Written very fast! */
+static int
+pkg_do(char *pkg)
+{
+ FILE *cfile;
+ char *deporigin, **depnames, home[FILENAME_MAX];
+ PackingList p;
+ int i, len;
+ /* support for separate pre/post install scripts */
+ int new_m = 0;
+ char pre_script[FILENAME_MAX] = DEINSTALL_FNAME;
+ char post_script[FILENAME_MAX];
+ char pre_arg[FILENAME_MAX], post_arg[FILENAME_MAX];
+ struct reqr_by_entry *rb_entry;
+ struct reqr_by_head *rb_list;
+
+ if (!pkg || !(len = strlen(pkg)))
+ return 1;
+ if (pkg[len - 1] == '/')
+ pkg[len - 1] = '\0';
+
+ /* Reset some state */
+ if (Plist.head)
+ free_plist(&Plist);
+
+ if (!isinstalledpkg(pkg)) {
+ warnx("no such package '%s' installed", pkg);
+ return 1;
+ }
+
+ if (!getcwd(home, FILENAME_MAX)) {
+ cleanup(0);
+ errx(2, "%s: unable to get current working directory!", __func__);
+ }
+
+ sprintf(LogDir, "%s/%s", LOG_DIR, pkg);
+
+ if (chdir(LogDir) == FAIL) {
+ warnx("unable to change directory to %s! deinstall failed", LogDir);
+ return 1;
+ }
+
+ if (Interactive == TRUE) {
+ int first, ch;
+
+ (void)fprintf(stderr, "delete %s? ", pkg);
+ (void)fflush(stderr);
+ first = ch = getchar();
+ while (ch != '\n' && ch != EOF)
+ ch = getchar();
+ if (first != 'y' && first != 'Y')
+ return 0;
+ /* Not reached */
+ }
+
+ if (requiredby(pkg, &rb_list, FALSE, TRUE) < 0)
+ return 1;
+ if (!STAILQ_EMPTY(rb_list)) {
+ warnx("package '%s' is required by these other packages\n"
+ "and may not be deinstalled%s:",
+ pkg, Force ? " (but I'll delete it anyway)" : "");
+ STAILQ_FOREACH(rb_entry, rb_list, link)
+ fprintf(stderr, "%s\n", rb_entry->pkgname);
+ if (!Force)
+ return 1;
+ }
+
+ sanity_check(LogDir);
+ cfile = fopen(CONTENTS_FNAME, "r");
+
+ if (!cfile) {
+ warnx("unable to open '%s' file", CONTENTS_FNAME);
+ return 1;
+ }
+
+ /* If we have a prefix, add it now */
+ if (Prefix)
+ add_plist(&Plist, PLIST_CWD, Prefix);
+ read_plist(&Plist, cfile);
+ fclose(cfile);
+ p = find_plist(&Plist, PLIST_CWD);
+
+ if (!p) {
+ warnx("package '%s' doesn't have a prefix", pkg);
+ return 1;
+ }
+
+ setenv(PKG_PREFIX_VNAME, p->name, 1);
+
+ if (fexists(REQUIRE_FNAME)) {
+ if (Verbose)
+ printf("Executing 'require' script.\n");
+ vsystem("chmod +x %s", REQUIRE_FNAME); /* be sure */
+ if (vsystem("./%s %s DEINSTALL", REQUIRE_FNAME, pkg)) {
+ warnx("package %s fails requirements %s", pkg,
+ Force ? "" : "- not deleted");
+ if (!Force)
+ return 1;
+ }
+ }
+
+ /*
+ * Test whether to use the old method of passing tokens to deinstallation
+ * scripts, and set appropriate variables..
+ */
+
+ if (fexists(POST_DEINSTALL_FNAME)) {
+ new_m = 1;
+ sprintf(post_script, "%s", POST_DEINSTALL_FNAME);
+ pre_arg[0] = '\0';
+ post_arg[0] = '\0';
+ } 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, "%s: unable to return to working directory %s!", __func__,
+ home);
+ }
+
+ /*
+ * Some packages aren't packed right, so we need to just ignore
+ * delete_package()'s status. Ugh! :-(
+ */
+ if (delete_package(FALSE, CleanDirs, &Plist) == FAIL)
+ warnx(
+ "couldn't entirely delete package (perhaps the packing list is\n"
+ "incorrectly specified?)");
+
+ if (chdir(LogDir) == FAIL) {
+ warnx("unable to change directory to %s! deinstall failed", LogDir);
+ return 1;
+ }
+
+ if (!NoDeInstall && fexists(post_script)) {
+ if (Fake)
+ printf("Would execute post-deinstall script at this point.\n");
+ else {
+ vsystem("chmod +x %s", post_script); /* make sure */
+ if (vsystem("./%s %s %s", post_script, pkg, post_arg)) {
+ warnx("post-deinstall script returned error status");
+ if (!Force)
+ return 1;
+ }
+ }
+ }
+
+ if (chdir(home) == FAIL) {
+ cleanup(0);
+ errx(2, "%s: unable to return to working directory %s!", __func__,
+ home);
+ }
+
+ if (!Fake) {
+ if (vsystem("%s -r%c %s", REMOVE_CMD, Force ? 'f' : ' ', LogDir)) {
+ warnx("couldn't remove log entry in %s, deinstall failed", LogDir);
+ if (!Force)
+ return 1;
+ }
+ }
+
+ for (p = Plist.head; p ; p = p->next) {
+ if (p->type != PLIST_PKGDEP)
+ continue;
+ deporigin = (p->next->type == PLIST_DEPORIGIN) ? p->next->name :
+ NULL;
+ if (Verbose) {
+ printf("Trying to remove dependency on package '%s'", p->name);
+ if (deporigin != NULL)
+ printf(" with '%s' origin", deporigin);
+ printf(".\n");
+ }
+ if (!Fake) {
+ depnames = (deporigin != NULL) ? matchbyorigin(deporigin, NULL) :
+ NULL;
+ if (depnames == NULL) {
+ depnames = alloca(sizeof(*depnames) * 2);
+ depnames[0] = p->name;
+ depnames[1] = NULL;
+ }
+ for (i = 0; depnames[i] != NULL; i++)
+ undepend(depnames[i], pkg);
+ }
+ }
+ return 0;
+}
+
+static void
+sanity_check(char *pkg)
+{
+ if (!fexists(CONTENTS_FNAME)) {
+ cleanup(0);
+ errx(2, "%s: installed package %s has no %s file!", __func__,
+ pkg, CONTENTS_FNAME);
+ }
+}
+
+void
+cleanup(int sig)
+{
+ if (sig)
+ exit(1);
+}
+
+static void
+undepend(char *p, char *pkgname)
+{
+ char fname[FILENAME_MAX], ftmp[FILENAME_MAX];
+ FILE *fpwr;
+ int s;
+ struct reqr_by_entry *rb_entry;
+ struct reqr_by_head *rb_list;
+
+
+ if (requiredby(p, &rb_list, Verbose, FALSE) <= 0)
+ return;
+ snprintf(fname, sizeof(fname), "%s/%s/%s", LOG_DIR, p, REQUIRED_BY_FNAME);
+ snprintf(ftmp, sizeof(ftmp), "%s.XXXXXX", fname);
+ s = mkstemp(ftmp);
+ if (s == -1) {
+ warnx("couldn't open temp file '%s'", ftmp);
+ return;
+ }
+ fpwr = fdopen(s, "w");
+ if (fpwr == NULL) {
+ close(s);
+ warnx("couldn't fdopen temp file '%s'", ftmp);
+ goto cleanexit;
+ }
+ STAILQ_FOREACH(rb_entry, rb_list, link)
+ if (strcmp(rb_entry->pkgname, pkgname)) /* no match */
+ fputs(rb_entry->pkgname, fpwr), putc('\n', fpwr);
+ if (fchmod(s, 0644) == FAIL) {
+ warnx("error changing permission of temp file '%s'", ftmp);
+ fclose(fpwr);
+ goto cleanexit;
+ }
+ if (fclose(fpwr) == EOF) {
+ warnx("error closing temp file '%s'", ftmp);
+ goto cleanexit;
+ }
+ if (rename(ftmp, fname) == -1)
+ warnx("error renaming '%s' to '%s'", ftmp, fname);
+cleanexit:
+ remove(ftmp);
+ return;
+}
diff --git a/usr.sbin/pkg_install/delete/pkg_delete.1 b/usr.sbin/pkg_install/delete/pkg_delete.1
new file mode 100644
index 0000000..fbcf752
--- /dev/null
+++ b/usr.sbin/pkg_install/delete/pkg_delete.1
@@ -0,0 +1,276 @@
+.\"
+.\" FreeBSD install - a package for the installation and maintainance
+.\" of non-core utilities.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" Jordan K. Hubbard
+.\"
+.\"
+.\" @(#)pkg_delete.1
+.\" $FreeBSD$
+.\"
+.Dd November 25, 1994
+.Dt PKG_DELETE 1
+.Os
+.Sh NAME
+.Nm pkg_delete
+.Nd a utility for deleting previously installed software package distributions
+.Sh SYNOPSIS
+.Nm
+.Op Fl dDfGinrvx
+.Op Fl p Ar prefix
+.Ar pkg-name ...
+.Nm
+.Fl a
+.Op Ar flags
+.Sh DESCRIPTION
+The
+.Nm
+command is used to delete packages that have been previously installed
+with the
+.Xr pkg_add 1
+command.
+.Sh WARNING
+.Bf -emphasis
+Since the
+.Nm
+command may execute scripts or programs provided by a package file,
+your system may be susceptible to
+.Dq Em trojan horses
+or other subtle
+attacks from miscreants who create dangerous package files.
+.Pp
+You are advised to verify the competence and identity of those who
+provide installable package files. For extra protection, examine all
+the package control files in the package record directory
+.Pa ( /var/db/pkg/<pkg-name>/ ) .
+Pay particular attention to any +INSTALL, +POST-INSTALL, +DEINSTALL,
++POST-DEINSTALL, +REQUIRE or +MTREE_DIRS files, and inspect the +CONTENTS
+file for
+.Cm @cwd ,
+.Cm @mode
+(check for setuid),
+.Cm @dirrm ,
+.Cm @exec ,
+and
+.Cm @unexec
+directives, and/or use the
+.Xr pkg_info 1
+command to examine the installed package control files.
+.Ef
+.Sh OPTIONS
+The following command line options are supported:
+.Bl -tag -width indent
+.It Ar pkg-name ...
+The named packages are deinstalled.
+.It Fl a
+Unconditionally delete all currently installed packages.
+.It Fl i
+Request confirmation before attempting to delete each package,
+regardless whether or not the standard input device is a
+terminal.
+.It Fl v
+Turn on verbose output.
+.It Fl D
+If a deinstallation script exists for a given package, do not execute it.
+.It Fl n
+Don't actually deinstall a package, just report the steps that
+would be taken if it were.
+.It Fl p Ar prefix
+Set
+.Ar prefix
+as the directory in which to delete files from any installed packages
+which do not explicitly set theirs. For most packages, the prefix will
+be set automatically to the installed location by
+.Xr pkg_add 1 .
+.It Fl d
+Remove empty directories created by file cleanup. By default, only
+files/directories explicitly listed in a package's contents (either as
+normal files/directories or with the
+.Cm @dirrm
+directive) will be removed at deinstallation time. This option tells
+.Nm
+to also remove any directories that were emptied as a result of removing
+the package.
+.It Fl f
+Force removal of the package, even if a dependency is recorded or the
+deinstall or require script fails.
+.It Fl G
+Do not try to expand shell glob patterns in the
+.Ar pkg-name
+when selecting packages to be deleted (by default
+.Nm
+automatically expands shell glob patterns in the
+.Ar pkg-name ) .
+.It Fl x
+Treat the
+.Ar pkg-name
+as a regular expression and delete all packages whose names match
+that regular expression. Multiple regular expressions could be
+provided, in that case
+.Nm
+deletes all packages that match at least one
+regular expression from the list.
+.It Fl r
+Recursive removal. In addition to specified packages, delete all
+packages that depend on those packages as well.
+.El
+.Sh TECHNICAL DETAILS
+The
+.Nm
+utility
+does pretty much what it says. It examines installed package records in
+.Pa /var/db/pkg/<pkg-name> ,
+deletes the package contents, and finally removes the package records.
+If the environment variable
+.Ev PKG_DBDIR
+is set, this overrides the
+.Pa /var/db/pkg/
+path shown above.
+.Pp
+If a package is required by other installed packages,
+.Nm
+will list those dependent packages and refuse to delete the package
+(unless the
+.Fl f
+option is given).
+.Pp
+If the package contains a
+.Ar require
+file (see
+.Xr pkg_create 1 ) ,
+then this is executed first as
+.Bd -ragged -offset indent -compact
+.Cm require
+.Ar <pkg-name>
+.Ar DEINSTALL
+.Ed
+(where
+.Ar pkg-name
+is the name of the package in question and
+.Ar DEINSTALL
+is a keyword denoting that this is a deinstallation)
+to see whether or not deinstallation should continue. A non-zero exit
+status means no, unless the
+.Fl f
+option is specified.
+.Pp
+If a
+.Cm deinstall
+script exists for the package, it is executed before any files are removed.
+It is this script's responsibility to clean up any additional messy details
+around the package's installation, since all
+.Nm
+knows how to do is delete the files created in the original distribution.
+The
+.Nm deinstall
+script is called as:
+.Bd -ragged -offset indent -compact
+.Cm script
+.Ar <pkg-name>
+.Ar DEINSTALL
+.Ed
+where
+.Ar pkg-name
+is the name of the package in question and
+.Ar DEINSTALL
+is a keyword denoting this as the pre-deinstallation phase.
+.Pp
+.Sy Note :
+The
+.Ar DEINSTALL
+keyword will not appear if separate scripts for deinstall and post-deinstall
+are given during package creation time (using the
+.Fl k
+and
+.Fl K
+flags to
+.Xr pkg_create 1 ) .
+.Pp
+If a
+.Cm post-deinstall
+script exists for the package, it is executed
+.Cm after
+all files are removed. It is this script's responsibility to clean up any
+additional messy details around the package's installation, and leave the
+system (hopefully) in the same state that it was prior to the installation
+of the package.
+.Pp
+The
+.Nm post-deinstall
+script is called as:
+.Bd -ragged -offset indent -compact
+.Cm script
+.Ar <pkg-name>
+.Ar POST-DEINSTALL
+.Ed
+where
+.Ar pkg-name
+is the name of the package in question and
+.Ar POST-DEINSTALL
+is a keyword denoting this as the post-deinstallation phase.
+.Pp
+.Sy Note :
+The
+.Ar POST-DEINSTALL
+keyword will not appear if separate scripts for deinstall and post-deinstall
+are given during package creation time (using the
+.Fl k
+and
+.Fl K
+flags to
+.Xr pkg_create 1 ) .
+.Pp
+Reasoning behind passing keywords such as
+.Ar DEINSTALL
+and
+.Ar POST-DEINSTALL
+is that it lets you potentially write only one program/script that handles
+all aspects of installation and deletion.
+.Pp
+But experience has proved that this is a lot more difficult to maintain and
+is not as advantageous as having separate scripts that handle each aspect of
+installation and deinstallation.
+.Pp
+All scripts are called with the environment variable
+.Ev PKG_PREFIX
+set to the installation prefix (see the
+.Fl p
+option above). This allows a package author to write a script
+that reliably performs some action on the directory where the package
+is installed, even if the user might have changed it by specifying the
+.Fl p
+option when running
+.Nm
+or
+.Cm pkg_add .
+.Sh ENVIRONMENT
+The environment variable
+.Ev PKG_DBDIR
+specifies an alternative location for the installed package database.
+.Sh FILES
+.Bl -tag -width /var/db/pkg -compact
+.It Pa /var/db/pkg
+Default location of the installed package database.
+.El
+.Sh SEE ALSO
+.Xr pkg_add 1 ,
+.Xr pkg_create 1 ,
+.Xr pkg_info 1 ,
+.Xr pkg_version 1 ,
+.Xr mktemp 3 ,
+.Xr mtree 8
+.Sh AUTHORS
+.An Jordan Hubbard
+.Sh CONTRIBUTORS
+.An John Kohl Aq jtk@rational.com
+.Sh BUGS
+Sure to be some.
diff --git a/usr.sbin/pkg_install/info/Makefile b/usr.sbin/pkg_install/info/Makefile
new file mode 100644
index 0000000..d3b16b8
--- /dev/null
+++ b/usr.sbin/pkg_install/info/Makefile
@@ -0,0 +1,18 @@
+# $FreeBSD$
+
+PROG= pkg_info
+SRCS= main.c perform.c show.c
+
+CFLAGS+= ${DEBUG} -I${.CURDIR}/../lib
+
+WARNS?= 2
+
+DPADD= ${LIBINSTALL} ${LIBFETCH} ${LIBMD}
+LDADD= ${LIBINSTALL} -lfetch -lmd
+
+.if !defined(NOCRYPT) && !defined(NOSECURE) && !defined(NO_OPENSSL)
+DPADD+= ${LIBSSL} ${LIBCRYPTO}
+LDADD+= -lssl -lcrypto
+.endif
+
+.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..9f4b52f
--- /dev/null
+++ b/usr.sbin/pkg_install/info/info.h
@@ -0,0 +1,79 @@
+/* $FreeBSD$ */
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 23 August 1993
+ *
+ * Include and define various things wanted by the info command.
+ *
+ */
+
+#ifndef _INST_INFO_H_INCLUDE
+#define _INST_INFO_H_INCLUDE
+
+#include <sys/queue.h>
+
+#ifndef MAXINDEXSIZE
+#define MAXINDEXSIZE 59
+#endif
+
+#ifndef MAXNAMESIZE
+#define MAXNAMESIZE 20
+#endif
+
+#define SHOW_COMMENT 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
+#define SHOW_SIZE 0x1000
+#define SHOW_ORIGIN 0x2000
+#define SHOW_CKSUM 0x4000
+#define SHOW_FMTREV 0x8000
+
+struct which_entry {
+ TAILQ_ENTRY(which_entry) next;
+ char file[PATH_MAX];
+ char package[PATH_MAX];
+ Boolean skip;
+};
+TAILQ_HEAD(which_head, which_entry);
+
+extern int Flags;
+extern Boolean Quiet;
+extern char *InfoPrefix;
+extern char PlayPen[];
+extern char *CheckPkg;
+extern char *LookUpOrigin;
+extern match_t MatchType;
+extern struct which_head *whead;
+
+extern void show_file(const char *, const char *);
+extern void show_plist(const char *, Package *, plist_t, Boolean);
+extern void show_files(const char *, Package *);
+extern void show_index(const char *, const char *);
+extern void show_size(const char *, Package *);
+extern void show_cksum(const char *, Package *);
+extern void show_origin(const char *, Package *);
+extern void show_fmtrev(const char *, Package *);
+
+#endif /* _INST_INFO_H_INCLUDE */
diff --git a/usr.sbin/pkg_install/info/main.c b/usr.sbin/pkg_install/info/main.c
new file mode 100644
index 0000000..c1c8816
--- /dev/null
+++ b/usr.sbin/pkg_install/info/main.c
@@ -0,0 +1,237 @@
+/*
+ *
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * This is the info module.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "lib.h"
+#include "info.h"
+#include <err.h>
+
+static char Options[] = "acdDe:fgGhiIkl:LmoO:pqrRst:vVW:x";
+
+int Flags = 0;
+match_t MatchType = MATCH_GLOB;
+Boolean Quiet = FALSE;
+char *InfoPrefix = (char *)(uintptr_t)"";
+char PlayPen[FILENAME_MAX];
+char *CheckPkg = NULL;
+char *LookUpOrigin = NULL;
+struct which_head *whead;
+
+static void usage __P((void));
+
+int
+main(int argc, char **argv)
+{
+ int ch;
+ char **pkgs, **start;
+ char *pkgs_split;
+
+ whead = malloc(sizeof(struct which_head));
+ if (whead == NULL)
+ err(2, NULL);
+ TAILQ_INIT(whead);
+
+ pkgs = start = argv;
+ if (argc == 1) {
+ MatchType = MATCH_ALL;
+ Flags = SHOW_INDEX;
+ }
+ else while ((ch = getopt(argc, argv, Options)) != -1) {
+ switch(ch) {
+ case 'a':
+ MatchType = MATCH_ALL;
+ break;
+
+ case 'v':
+ Verbose = TRUE;
+ /* Reasonable definition of 'everything' */
+ Flags = SHOW_COMMENT | SHOW_DESC | SHOW_PLIST | SHOW_INSTALL |
+ SHOW_DEINSTALL | SHOW_REQUIRE | SHOW_DISPLAY | SHOW_MTREE;
+ break;
+
+ case 'I':
+ Flags |= SHOW_INDEX;
+ break;
+
+ case 'p':
+ Flags |= SHOW_PREFIX;
+ break;
+
+ case 'c':
+ Flags |= SHOW_COMMENT;
+ break;
+
+ case 'd':
+ Flags |= SHOW_DESC;
+ break;
+
+ case 'D':
+ Flags |= SHOW_DISPLAY;
+ break;
+
+ case 'f':
+ Flags |= SHOW_PLIST;
+ break;
+
+ case 'g':
+ Flags |= SHOW_CKSUM;
+ break;
+
+ case 'G':
+ MatchType = MATCH_EXACT;
+ break;
+
+ case 'i':
+ Flags |= SHOW_INSTALL;
+ break;
+
+ case 'k':
+ Flags |= SHOW_DEINSTALL;
+ break;
+
+ case 'r':
+ Flags |= SHOW_REQUIRE;
+ break;
+
+ case 'R':
+ Flags |= SHOW_REQBY;
+ break;
+
+ case 'L':
+ Flags |= SHOW_FILES;
+ break;
+
+ case 'm':
+ Flags |= SHOW_MTREE;
+ break;
+
+ case 's':
+ Flags |= SHOW_SIZE;
+ break;
+
+ case 'o':
+ Flags |= SHOW_ORIGIN;
+ break;
+
+ case 'O':
+ LookUpOrigin = strdup(optarg);
+ if (LookUpOrigin == NULL)
+ err(2, NULL);
+ break;
+
+ case 'V':
+ Flags |= SHOW_FMTREV;
+ break;
+
+ case 'l':
+ InfoPrefix = optarg;
+ break;
+
+ case 'q':
+ Quiet = TRUE;
+ break;
+
+ case 't':
+ strlcpy(PlayPen, optarg, sizeof(PlayPen));
+ break;
+
+ case 'x':
+ MatchType = MATCH_REGEX;
+ break;
+
+ case 'e':
+ CheckPkg = optarg;
+ break;
+
+ case 'W':
+ {
+ struct which_entry *entp;
+
+ entp = calloc(1, sizeof(struct which_entry));
+ if (entp == NULL)
+ err(2, NULL);
+
+ strlcpy(entp->file, optarg, PATH_MAX);
+ entp->skip = FALSE;
+ TAILQ_INSERT_TAIL(whead, entp, next);
+ break;
+ }
+
+ case '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) {
+ /*
+ * Don't try to apply heuristics if arguments are regexs or if
+ * the argument refers to an existing file.
+ */
+ if (MatchType != MATCH_REGEX && !isfile(*argv))
+ while ((pkgs_split = strrchr(*argv, (int)'/')) != NULL) {
+ *pkgs_split++ = '\0';
+ /*
+ * If character after the '/' is alphanumeric or shell
+ * metachar, then we've found the package name. Otherwise
+ * we've come across a trailing '/' and need to continue our
+ * quest.
+ */
+ if (isalpha(*pkgs_split) || ((MatchType == MATCH_GLOB) && \
+ strpbrk(pkgs_split, "*?[]") != NULL)) {
+ *argv = pkgs_split;
+ break;
+ }
+ }
+ *pkgs++ = *argv++;
+ }
+
+ /* If no packages, yelp */
+ if (pkgs == start && MatchType != MATCH_ALL && !CheckPkg &&
+ TAILQ_EMPTY(whead) && LookUpOrigin == NULL)
+ warnx("missing package name(s)"), usage();
+ *pkgs = NULL;
+ return pkg_perform(start);
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n",
+ "usage: pkg_info [-cdDfGiIkLmopqrRsvVx] [-e package] [-l prefix]",
+ " [-t template] [pkg-name ...]",
+ " pkg_info [-q] -W filename",
+ " pkg_info [-q] -O origin",
+ " 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..e9581f1
--- /dev/null
+++ b/usr.sbin/pkg_install/info/perform.c
@@ -0,0 +1,437 @@
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 23 Aug 1993
+ *
+ * This is the main body of the info module.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "lib.h"
+#include "info.h"
+#include <err.h>
+#include <signal.h>
+
+static int pkg_do(char *);
+static int find_pkg(struct which_head *);
+static int cmp_path(const char *, const char *, const char *);
+static char *abspath(const char *);
+static int find_pkgs_by_origin(const char *);
+
+int
+pkg_perform(char **pkgs)
+{
+ char **matched;
+ int err_cnt = 0;
+ int i, errcode;
+
+ signal(SIGINT, cleanup);
+
+ /* Overriding action? */
+ if (CheckPkg) {
+ return isinstalledpkg(CheckPkg) == TRUE ? 0 : 1;
+ /* Not reached */
+ } else if (!TAILQ_EMPTY(whead)) {
+ return find_pkg(whead);
+ } else if (LookUpOrigin != NULL) {
+ return find_pkgs_by_origin(LookUpOrigin);
+ }
+
+ if (MatchType != MATCH_EXACT) {
+ matched = matchinstalled(MatchType, pkgs, &errcode);
+ if (errcode != 0)
+ return 1;
+ /* Not reached */
+
+ if (matched != NULL)
+ pkgs = matched;
+ else switch (MatchType) {
+ case MATCH_GLOB:
+ break;
+ case MATCH_ALL:
+ warnx("no packages installed");
+ return 0;
+ /* Not reached */
+ case MATCH_REGEX:
+ warnx("no packages match pattern(s)");
+ return 1;
+ /* Not reached */
+ default:
+ break;
+ }
+ }
+
+ for (i = 0; pkgs[i]; i++)
+ err_cnt += pkg_do(pkgs[i]);
+
+ return err_cnt;
+}
+
+static char *Home;
+
+static int
+pkg_do(char *pkg)
+{
+ Boolean installed = FALSE, isTMP = FALSE;
+ char log_dir[FILENAME_MAX];
+ char fname[FILENAME_MAX];
+ 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 {
+ if (!isinstalledpkg(pkg)) {
+ warnx("can't find package '%s' installed or in a file!", pkg);
+ return 1;
+ }
+ sprintf(log_dir, "%s/%s", LOG_DIR, pkg);
+ if (chdir(log_dir) == FAIL) {
+ warnx("can't change directory to '%s'!", log_dir);
+ return 1;
+ }
+ installed = TRUE;
+ }
+
+ /* Suck in the contents list */
+ plist.head = plist.tail = NULL;
+ fp = fopen(CONTENTS_FNAME, "r");
+ if (!fp) {
+ warnx("unable to open %s file", CONTENTS_FNAME);
+ code = 1;
+ goto bail;
+ }
+ /* If we have a prefix, add it now */
+ read_plist(&plist, fp);
+ fclose(fp);
+
+ /*
+ * Index is special info type that has to override all others to make
+ * any sense.
+ */
+ if (Flags & SHOW_INDEX) {
+ char tmp[FILENAME_MAX];
+
+ snprintf(tmp, FILENAME_MAX, "%-19s ", pkg);
+ show_index(tmp, COMMENT_FNAME);
+ }
+ else {
+ /* Start showing the package contents */
+ if (!Quiet)
+ printf("%sInformation for %s:\n\n", InfoPrefix, pkg);
+ if (Flags & SHOW_COMMENT)
+ show_file("Comment:\n", COMMENT_FNAME);
+ if (Flags & SHOW_REQUIRE)
+ show_plist("Depends on:\n", &plist, PLIST_PKGDEP, FALSE);
+ if ((Flags & SHOW_REQBY) && !isemptyfile(REQUIRED_BY_FNAME))
+ show_file("Required by:\n", REQUIRED_BY_FNAME);
+ if (Flags & SHOW_DESC)
+ show_file("Description:\n", DESC_FNAME);
+ if ((Flags & SHOW_DISPLAY) && fexists(DISPLAY_FNAME))
+ show_file("Install notice:\n", DISPLAY_FNAME);
+ if (Flags & SHOW_PLIST)
+ show_plist("Packing list:\n", &plist, (plist_t)0, TRUE);
+ if ((Flags & SHOW_INSTALL) && fexists(INSTALL_FNAME))
+ show_file("Install script:\n", INSTALL_FNAME);
+ if ((Flags & SHOW_INSTALL) && fexists(POST_INSTALL_FNAME))
+ show_file("Post-Install script:\n", POST_INSTALL_FNAME);
+ if ((Flags & SHOW_DEINSTALL) && fexists(DEINSTALL_FNAME))
+ show_file("De-Install script:\n", DEINSTALL_FNAME);
+ if ((Flags & SHOW_DEINSTALL) && fexists(POST_DEINSTALL_FNAME))
+ show_file("Post-DeInstall script:\n", POST_DEINSTALL_FNAME);
+ if ((Flags & SHOW_MTREE) && fexists(MTREE_FNAME))
+ show_file("mtree file:\n", MTREE_FNAME);
+ if (Flags & SHOW_PREFIX)
+ show_plist("Prefix(s):\n", &plist, PLIST_CWD, FALSE);
+ if (Flags & SHOW_FILES)
+ show_files("Files:\n", &plist);
+ if ((Flags & SHOW_SIZE) && installed)
+ show_size("Package Size:\n", &plist);
+ if ((Flags & SHOW_CKSUM) && installed)
+ show_cksum("Mismatched Checksums:\n", &plist);
+ if (Flags & SHOW_ORIGIN)
+ show_origin("Origin:\n", &plist);
+ if (Flags & SHOW_FMTREV)
+ show_fmtrev("Packing list format revision:\n", &plist);
+ if (!Quiet)
+ puts(InfoPrefix);
+ }
+ free_plist(&plist);
+ bail:
+ leave_playpen();
+ if (isTMP)
+ unlink(fname);
+ return code;
+}
+
+void
+cleanup(int sig)
+{
+ static int in_cleanup = 0;
+
+ if (!in_cleanup) {
+ in_cleanup = 1;
+ leave_playpen();
+ }
+ if (sig)
+ exit(1);
+}
+
+/*
+ * Return an absolute path, additionally removing all .'s, ..'s, and extraneous
+ * /'s, as realpath() would, but without resolving symlinks, because that can
+ * potentially screw up our comparisons later.
+ */
+static char *
+abspath(const char *pathname)
+{
+ char *tmp, *tmp1, *resolved_path;
+ char *cwd = NULL;
+ int len;
+
+ if (pathname[0] != '/') {
+ cwd = getcwd(NULL, MAXPATHLEN);
+ asprintf(&resolved_path, "%s/%s/", cwd, pathname);
+ } else
+ asprintf(&resolved_path, "%s/", pathname);
+
+ if (resolved_path == NULL)
+ errx(2, NULL);
+
+ if (cwd != NULL)
+ free(cwd);
+
+ while ((tmp = strstr(resolved_path, "//")) != NULL)
+ strcpy(tmp, tmp + 1);
+
+ while ((tmp = strstr(resolved_path, "/./")) != NULL)
+ strcpy(tmp, tmp + 2);
+
+ while ((tmp = strstr(resolved_path, "/../")) != NULL) {
+ *tmp = '\0';
+ if ((tmp1 = strrchr(resolved_path, '/')) == NULL)
+ tmp1 = resolved_path;
+ strcpy(tmp1, tmp + 3);
+ }
+
+ len = strlen(resolved_path);
+ if (len > 1 && resolved_path[len - 1] == '/')
+ resolved_path[len - 1] = '\0';
+
+ return resolved_path;
+}
+
+/*
+ * Comparison to see if the path we're on matches the
+ * one we are looking for.
+ */
+static int
+cmp_path(const char *target, const char *current, const char *cwd)
+{
+ char *resolved, *temp;
+ int rval;
+
+ asprintf(&temp, "%s/%s", cwd, current);
+ if (temp == NULL)
+ errx(2, NULL);
+
+ /*
+ * Make sure there's no multiple /'s or other weird things in the PLIST,
+ * since some plists seem to have them and it could screw up our strncmp.
+ */
+ resolved = abspath(temp);
+
+ if (strcmp(target, resolved) == 0)
+ rval = 1;
+ else
+ rval = 0;
+
+ free(temp);
+ free(resolved);
+ return rval;
+}
+
+/*
+ * Look through package dbs in LOG_DIR and find which
+ * packages installed the files in which_list.
+ */
+static int
+find_pkg(struct which_head *which_list)
+{
+ char **installed;
+ int errcode, i;
+ struct which_entry *wp;
+
+ TAILQ_FOREACH(wp, which_list, next) {
+ const char *msg = "file cannot be found";
+ char *tmp;
+
+ wp->skip = TRUE;
+ /* If it's not a file, we'll see if it's an executable. */
+ if (isfile(wp->file) == FALSE) {
+ if (strchr(wp->file, '/') == NULL) {
+ tmp = vpipe("/usr/bin/which %s", wp->file);
+ if (tmp != NULL) {
+ strlcpy(wp->file, tmp, PATH_MAX);
+ wp->skip = FALSE;
+ free(tmp);
+ } else
+ msg = "file is not in PATH";
+ }
+ } else {
+ tmp = abspath(wp->file);
+ if (isfile(tmp)) {
+ strlcpy(wp->file, tmp, PATH_MAX);
+ wp->skip = FALSE;
+ }
+ free(tmp);
+ }
+ if (wp->skip == TRUE)
+ warnx("%s: %s", wp->file, msg);
+ }
+
+ installed = matchinstalled(MATCH_ALL, NULL, &errcode);
+ if (installed == NULL)
+ return errcode;
+
+ for (i = 0; installed[i] != NULL; i++) {
+ FILE *fp;
+ Package pkg;
+ PackingList itr;
+ char *cwd = NULL;
+ char tmp[PATH_MAX];
+
+ snprintf(tmp, PATH_MAX, "%s/%s/%s", LOG_DIR, installed[i],
+ CONTENTS_FNAME);
+ fp = fopen(tmp, "r");
+ if (fp == NULL) {
+ warn("%s", tmp);
+ return 1;
+ }
+
+ pkg.head = pkg.tail = NULL;
+ read_plist(&pkg, fp);
+ fclose(fp);
+ for (itr = pkg.head; itr != pkg.tail; itr = itr->next) {
+ if (itr->type == PLIST_CWD) {
+ cwd = itr->name;
+ } else if (itr->type == PLIST_FILE) {
+ TAILQ_FOREACH(wp, which_list, next) {
+ if (wp->skip == TRUE)
+ continue;
+ if (!cmp_path(wp->file, itr->name, cwd))
+ continue;
+ if (wp->package[0] != '\0') {
+ warnx("both %s and %s claim to have installed %s\n",
+ wp->package, installed[i], wp->file);
+ } else {
+ strlcpy(wp->package, installed[i], PATH_MAX);
+ }
+ }
+ }
+ }
+ free_plist(&pkg);
+ }
+
+ TAILQ_FOREACH(wp, which_list, next) {
+ if (wp->package[0] != '\0') {
+ if (Quiet)
+ puts(wp->package);
+ else
+ printf("%s was installed by package %s\n", \
+ wp->file, wp->package);
+ }
+ }
+ while (!TAILQ_EMPTY(which_list)) {
+ wp = TAILQ_FIRST(which_list);
+ TAILQ_REMOVE(which_list, wp, next);
+ free(wp);
+ }
+
+ free(which_list);
+ return 0;
+}
+
+/*
+ * Look through package dbs in LOG_DIR and find which
+ * packages have the given origin. Don't use read_plist()
+ * because this increases time necessary for lookup by 40
+ * times, as we don't really have to parse all plist to
+ * get origin.
+ */
+static int
+find_pkgs_by_origin(const char *origin)
+{
+ char **matched;
+ int errcode, i;
+
+ if (!Quiet)
+ printf("The following installed package(s) has %s origin:\n", origin);
+
+ matched = matchbyorigin(origin, &errcode);
+ if (matched == NULL)
+ return errcode;
+
+ for (i = 0; matched[i] != NULL; i++)
+ puts(matched[i]);
+
+ return 0;
+}
diff --git a/usr.sbin/pkg_install/info/pkg_info.1 b/usr.sbin/pkg_install/info/pkg_info.1
new file mode 100644
index 0000000..c4a97ba
--- /dev/null
+++ b/usr.sbin/pkg_install/info/pkg_info.1
@@ -0,0 +1,225 @@
+.\"
+.\" FreeBSD install - a package for the installation and maintenance
+.\" of non-core utilities.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" Jordan K. Hubbard
+.\"
+.\"
+.\" @(#)pkg_info.1
+.\" $FreeBSD$
+.\"
+.Dd February 8, 2001
+.Dt PKG_INFO 1
+.Os
+.Sh NAME
+.Nm pkg_info
+.Nd a utility for displaying information on software packages
+.Sh SYNOPSIS
+.Nm
+.Op Fl cdDfgGiIkLmopqrRsvVx
+.Op Fl e Ar package
+.Op Fl l Ar prefix
+.Op Fl t Ar template
+.Op Ar pkg-name ...
+.Nm
+.Op Fl q
+.Fl W Ar filename
+.Nm
+.Op Fl q
+.Fl O Ar origin
+.Nm
+.Fl a
+.Op Ar flags
+.Sh DESCRIPTION
+The
+.Nm
+command is used to dump out information for packages, either packed up in
+files with the
+.Xr pkg_create 1
+command or already installed on the system
+with the
+.Xr pkg_add 1
+command.
+.Sh OPTIONS
+The following command line options are supported:
+.Bl -tag -width indent
+.It Ar pkg-name ...
+The named packages are described. A package name may either be the name of
+an installed package, the pathname to a package distribution file or a
+URL to an FTP available package.
+.It Fl a
+Show all currently installed packages.
+.It Fl v
+Turn on verbose output.
+.It Fl p
+Show the installation prefix for each package.
+.It Fl q
+Be
+.Dq quiet
+in emitting report headers and such, just dump the
+raw info (basically, assume a non-human reading).
+.It Fl c
+Show the (one line) comment field for each package.
+.It Fl d
+Show the long description field for each package.
+.It Fl D
+Show the install-message file for each package.
+.It Fl f
+Show the packing list instructions for each package.
+.It Fl g
+Show files that don't match the recorded checksum.
+.It Fl i
+Show the install script (if any) for each package.
+.It Fl I
+Show an index line for each package. This option takes
+precedence over all other package formatting options.
+.It Fl k
+Show the de-install script (if any) for each package.
+.It Fl r
+Show the requirements script (if any) for each package.
+.It Fl R
+Show the list of installed packages which require each package.
+.It Fl m
+Show the mtree file (if any) for each package.
+.It Fl L
+Show the files within each package. This is different from just
+viewing the packing list, since full pathnames for everything
+are generated.
+.It Fl s
+Show the total size occupied by files installed within each package.
+.It Fl o
+Show the
+.Dq origin
+path recorded on package generation. This path
+intended to give an idea as to where the underlying port, from which
+package was generated, is located in the
+.Fx
+.Em "Ports Collection" .
+.It Fl G
+Do not try to expand shell glob patterns in the
+.Ar pkg-name
+when selecting packages to be displayed (by default
+.Nm
+automatically expands shell glob patterns in the
+.Ar pkg-name ) .
+.It Fl W
+For the specified
+.Ar filename
+argument show which package it belongs to. If the file is not in the
+current directory, and does not have an absolute path, then the
+.Ev PATH
+is searched using
+.Xr which 1 .
+.It Fl O
+For the specified
+.Ar origin
+argument list all packages having this origin.
+.It Fl x
+Treat the
+.Ar pkg-name
+as a regular expression and display information only for packages
+whose names match that regular expression. Multiple regular
+expressions could be provided, in that case
+.Nm
+displays information about all packages that match at least one
+regular expression from the list.
+.It Fl e Ar pkg-name
+If the package identified by
+.Ar pkg-name
+is currently installed, return 0, otherwise return 1. This option
+allows you to easily test for the presence of another (perhaps
+prerequisite) package from a script.
+.It Fl l Ar str
+Prefix each information category header (see
+.Fl q )
+shown with
+.Ar str .
+This is primarily of use to front-end programs who want to request a
+lot of different information fields at once for a package, but don't
+necessary want the output intermingled in such a way that they can't
+organize it. This lets you add a special token to the start of
+each field.
+.It Fl t Ar template
+Use
+.Ar template
+as the input to
+.Xr mktemp 3
+when creating a
+.Dq staging area .
+By default, this is the string
+.Pa /tmp/instmp.XXXXXX ,
+but it may be necessary to override it in the situation where
+space in your
+.Pa /tmp
+directory is limited. Be sure to leave some number of `X' characters
+for
+.Xr mktemp 3
+to fill in with a unique ID.
+.Bd -ragged -offset indent -compact
+Note: This should really not be necessary with
+.Nm ,
+since very little information is extracted from each package
+and one would have to have a very small
+.Pa /tmp
+indeed to overflow it.
+.Ed
+.It Fl V
+Show revision number of the packing list format.
+.El
+.Sh TECHNICAL DETAILS
+Package info is either extracted from package files named on the
+command line, or from already installed package information
+in
+.Pa /var/db/pkg/ Ns Aq Ar pkg-name .
+.Sh ENVIRONMENT
+.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 pkg_version 1 ,
+.Xr mktemp 3 ,
+.Xr mtree 8
+.Sh AUTHORS
+.An Jordan Hubbard
+.Sh CONTRIBUTORS
+.An John Kohl Aq jtk@rational.com
+.Sh BUGS
+Sure to be some.
diff --git a/usr.sbin/pkg_install/info/show.c b/usr.sbin/pkg_install/info/show.c
new file mode 100644
index 0000000..e26888d
--- /dev/null
+++ b/usr.sbin/pkg_install/info/show.c
@@ -0,0 +1,340 @@
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 23 Aug 1993
+ *
+ * Various display routines for the info module.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "lib.h"
+#include "info.h"
+#include <err.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <md5.h>
+
+void
+show_file(const char *title, const char *fname)
+{
+ FILE *fp;
+ char line[1024];
+ int n;
+
+ if (!Quiet)
+ printf("%s%s", InfoPrefix, title);
+ fp = fopen(fname, "r");
+ if (!fp)
+ 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(const char *title, const 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 showall is TRUE, show all */
+void
+show_plist(const char *title, Package *plist, plist_t type, Boolean showall)
+{
+ PackingList p;
+ Boolean ign = FALSE;
+
+ if (!Quiet)
+ printf("%s%s", InfoPrefix, title);
+ p = plist->head;
+ while (p) {
+ if (p->type != type && showall != TRUE) {
+ p = p->next;
+ continue;
+ }
+ switch(p->type) {
+ case PLIST_FILE:
+ if (ign) {
+ printf(Quiet ? "%s\n" : "File: %s (ignored)\n", p->name);
+ ign = FALSE;
+ }
+ else
+ printf(Quiet ? "%s\n" : "File: %s\n", p->name);
+ break;
+
+ case PLIST_CWD:
+ printf(Quiet ? "@cwd %s\n" : "\tCWD to %s\n", p->name);
+ break;
+
+ case PLIST_SRC:
+ printf(Quiet ? "@srcdir %s\n" : "\tSRCDIR to %s\n", p->name);
+ break;
+
+ case PLIST_CMD:
+ printf(Quiet ? "@exec %s\n" : "\tEXEC '%s'\n", p->name);
+ break;
+
+ case PLIST_UNEXEC:
+ printf(Quiet ? "@unexec %s\n" : "\tUNEXEC '%s'\n", p->name);
+ break;
+
+ case PLIST_CHMOD:
+ printf(Quiet ? "@chmod %s\n" : "\tCHMOD to %s\n",
+ p->name ? p->name : "(clear default)");
+ break;
+
+ case PLIST_CHOWN:
+ printf(Quiet ? "@chown %s\n" : "\tCHOWN to %s\n",
+ p->name ? p->name : "(clear default)");
+ break;
+
+ case PLIST_CHGRP:
+ printf(Quiet ? "@chgrp %s\n" : "\tCHGRP to %s\n",
+ p->name ? p->name : "(clear default)");
+ break;
+
+ case PLIST_COMMENT:
+ printf(Quiet ? "@comment %s\n" : "\tComment: %s\n", p->name);
+ break;
+
+ case PLIST_IGNORE:
+ ign = TRUE;
+ break;
+
+ case PLIST_IGNORE_INST:
+ printf(Quiet ? "@ignore_inst ??? doesn't belong here.\n" :
+ "\tIgnore next file installation directive (doesn't belong)\n");
+ ign = TRUE;
+ break;
+
+ case PLIST_NAME:
+ printf(Quiet ? "@name %s\n" : "\tPackage name: %s\n", p->name);
+ break;
+
+ case PLIST_DISPLAY:
+ printf(Quiet ? "@display %s\n" : "\tInstall message file: %s\n", p->name);
+ break;
+
+ case PLIST_PKGDEP:
+ printf(Quiet ? "@pkgdep %s\n" : "Dependency: %s\n", p->name);
+ break;
+
+ case PLIST_DEPORIGIN:
+ printf(Quiet ? "@comment DEPORIGIN:%s\n" :
+ "\tdependency origin: %s\n", p->name);
+ break;
+
+ case PLIST_MTREE:
+ printf(Quiet ? "@mtree %s\n" : "\tPackage mtree file: %s\n", p->name);
+ break;
+
+ case PLIST_DIR_RM:
+ printf(Quiet ? "@dirrm %s\n" : "\tDeinstall directory remove: %s\n", p->name);
+ break;
+
+ case PLIST_OPTION:
+ printf(Quiet ? "@option %s\n" :
+ "\tOption \"%s\" controlling package installation behaviour\n",
+ p->name);
+ break;
+
+ case PLIST_ORIGIN:
+ printf(Quiet ? "@comment ORIGIN:%s\n" :
+ "\tPackage origin: %s\n", p->name);
+ break;
+
+ default:
+ cleanup(0);
+ errx(2, "%s: unknown command type %d (%s)",
+ __func__, p->type, p->name);
+ break;
+ }
+ p = p->next;
+ }
+}
+
+/* Show all files in the packing list (except ignored ones) */
+void
+show_files(const char *title, Package *plist)
+{
+ PackingList p;
+ Boolean ign = FALSE;
+ const char *dir = ".";
+
+ if (!Quiet)
+ printf("%s%s", InfoPrefix, title);
+ p = plist->head;
+ while (p) {
+ switch(p->type) {
+ case PLIST_FILE:
+ if (!ign)
+ printf("%s/%s\n", dir, p->name);
+ ign = FALSE;
+ break;
+
+ case PLIST_CWD:
+ dir = p->name;
+ break;
+
+ case PLIST_IGNORE:
+ ign = TRUE;
+ break;
+
+ /* Silence GCC in the -Wall mode */
+ default:
+ break;
+ }
+ p = p->next;
+ }
+}
+
+/* Calculate and show size of all installed package files (except ignored ones) */
+void
+show_size(const char *title, Package *plist)
+{
+ PackingList p;
+ Boolean ign = FALSE;
+ const char *dir = ".";
+ struct stat sb;
+ char tmp[FILENAME_MAX];
+ unsigned long size = 0;
+ long blksize;
+ int headerlen;
+ char *descr;
+
+ descr = getbsize(&headerlen, &blksize);
+ if (!Quiet)
+ printf("%s%s", InfoPrefix, title);
+ for (p = plist->head; p != NULL; p = p->next) {
+ switch (p->type) {
+ case PLIST_FILE:
+ if (!ign) {
+ snprintf(tmp, FILENAME_MAX, "%s/%s", dir, p->name);
+ if (!lstat(tmp, &sb)) {
+ size += sb.st_size;
+ if (Verbose)
+ printf("%lu\t%s\n", (unsigned long) howmany(sb.st_size, blksize), tmp);
+ }
+ }
+ ign = FALSE;
+ break;
+
+ case PLIST_CWD:
+ dir = p->name;
+ break;
+
+ case PLIST_IGNORE:
+ ign = TRUE;
+ break;
+
+ /* Silence GCC in the -Wall mode */
+ default:
+ break;
+ }
+ }
+ if (!Quiet)
+ printf("%lu\t(%s)\n", howmany(size, blksize), descr);
+ else
+ printf("%lu\n", size);
+}
+
+/* Show files that don't match the recorded checksum */
+void
+show_cksum(const char *title, Package *plist)
+{
+ PackingList p;
+ const char *dir = ".";
+ char tmp[FILENAME_MAX];
+
+ if (!Quiet)
+ printf("%s%s", InfoPrefix, title);
+
+ for (p = plist->head; p != NULL; p = p->next)
+ if (p->type == PLIST_CWD)
+ dir = p->name;
+ else if (p->type == PLIST_FILE) {
+ snprintf(tmp, FILENAME_MAX, "%s/%s", dir, p->name);
+ if (!fexists(tmp))
+ warnx("%s doesn't exist\n", tmp);
+ else if (p->next && p->next->type == PLIST_COMMENT &&
+ (strncmp(p->next->name, "MD5:", 4) == 0)) {
+ char *cp = NULL, buf[33];
+
+ /*
+ * For packing lists whose version is 1.1 or greater, the md5
+ * hash for a symlink is calculated on the string returned
+ * by readlink().
+ */
+ if (issymlink(tmp) && verscmp(plist, 1, 0) > 0) {
+ int len;
+ char linkbuf[FILENAME_MAX];
+
+ if ((len = readlink(tmp, linkbuf, FILENAME_MAX)) > 0)
+ cp = MD5Data((unsigned char *)linkbuf, len, buf);
+ } else if (isfile(tmp) || verscmp(plist, 1, 1) < 0)
+ cp = MD5File(tmp, buf);
+
+ if (cp != NULL) {
+ /* Mismatch? */
+ if (strcmp(cp, p->next->name + 4))
+ printf("%s fails the original MD5 checksum\n", tmp);
+ else if (Verbose)
+ printf("%s matched the original MD5 checksum\n", tmp);
+ }
+ }
+ }
+}
+
+/* Show an "origin" path (usually category/portname) */
+void
+show_origin(const char *title, Package *plist)
+{
+
+ if (!Quiet)
+ printf("%s%s", InfoPrefix, title);
+ printf("%s\n", plist->origin != NULL ? plist->origin : "");
+}
+
+/* Show revision number of the packing list */
+void
+show_fmtrev(const char *title, Package *plist)
+{
+
+ if (!Quiet)
+ printf("%s%s", InfoPrefix, title);
+ printf("%d.%d\n", plist->fmtver_maj, plist->fmtver_mnr);
+}
diff --git a/usr.sbin/pkg_install/lib/Makefile b/usr.sbin/pkg_install/lib/Makefile
new file mode 100644
index 0000000..acb837e
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/Makefile
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+LIB= install
+INTERNALLIB= YES
+SRCS= file.c msg.c plist.c str.c exec.c global.c pen.c match.c \
+ deps.c version.c
+
+CFLAGS+= ${DEBUG}
+
+WARNS?= 2
+
+.include <bsd.lib.mk>
diff --git a/usr.sbin/pkg_install/lib/deps.c b/usr.sbin/pkg_install/lib/deps.c
new file mode 100644
index 0000000..4ebb2cf
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/deps.c
@@ -0,0 +1,195 @@
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Maxim Sobolev
+ * 14 March 2001
+ *
+ * Routines used to do various operations with dependencies
+ * among installed packages.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "lib.h"
+#include <err.h>
+#include <stdio.h>
+
+/*
+ * Sort given NULL-terminated list of installed packages (pkgs) in
+ * such a way that if package A depends on package B then after
+ * sorting A will be listed before B no matter how they were
+ * originally positioned in the list.
+ */
+int
+sortdeps(char **pkgs)
+{
+ char *tmp;
+ int i, j, loop_cnt;
+ int err_cnt = 0;
+
+ if (pkgs[0] == NULL || pkgs[1] == NULL)
+ return (0);
+
+ for (i = 0; pkgs[i + 1]; i++) {
+ /*
+ * Check to see if any other package in pkgs[i+1:] depends
+ * on pkgs[i] and swap those two packages if so.
+ */
+ loop_cnt = 0;
+ for (j = i + 1; pkgs[j]; j++) {
+ if (chkifdepends(pkgs[j], pkgs[i]) == 1) {
+ /*
+ * Try to avoid deadlock if package A depends on B which in
+ * turn depends on C and C due to an error depends on A.
+ * Use ugly but simple method, becase it Should Never
+ * Happen[tm] in the real life anyway.
+ */
+ if (loop_cnt > 4096) {
+ warnx("dependency loop detected for package %s", pkgs[j]);
+ err_cnt++;
+ break;
+ }
+ loop_cnt++;
+ tmp = pkgs[i];
+ pkgs[i] = pkgs[j];
+ pkgs[j] = tmp;
+ /*
+ * Another iteration requred to check if new pkgs[i]
+ * itself has any packages that depend on it
+ */
+ j = i + 1;
+ }
+ }
+ }
+ return err_cnt;
+}
+
+/*
+ * Check to see if pkgname1 depends on pkgname2.
+ * Returns 1 if depends, 0 if not, and -1 if error occured.
+ */
+int
+chkifdepends(const char *pkgname1, const char *pkgname2)
+{
+ char *cp1, *cp2;
+ int errcode;
+ struct reqr_by_entry *rb_entry;
+ struct reqr_by_head *rb_list;
+
+ cp2 = strchr(pkgname2, ':');
+ if (cp2 != NULL)
+ *cp2 = '\0';
+ cp1 = strchr(pkgname1, ':');
+ if (cp1 != NULL)
+ *cp1 = '\0';
+
+ errcode = 0;
+ /* Check that pkgname2 is actually installed */
+ if (!isinstalledpkg(pkgname2))
+ goto exit;
+
+ errcode = requiredby(pkgname2, &rb_list, FALSE, TRUE);
+ if (errcode < 0)
+ goto exit;
+
+ errcode = 0;
+ STAILQ_FOREACH(rb_entry, rb_list, link) {
+ if (strcmp(rb_entry->pkgname, pkgname1) == 0) { /* match */
+ errcode = 1;
+ break;
+ }
+ }
+
+exit:
+ if (cp1 != NULL)
+ *cp1 = ':';
+ if (cp2 != NULL)
+ *cp2 = ':';
+ return errcode;
+}
+
+/*
+ * Load +REQUIRED_BY file and return a list with names of
+ * packages that require package reffered to by `pkgname'.
+ *
+ * Optionally check that packages listed there are actually
+ * installed and filter out those that don't (filter == TRUE).
+ *
+ * strict argument controls whether the caller want warnings
+ * to be emitted when there are some non-fatal conditions,
+ * i.e. package doesn't have +REQUIRED_BY file or some packages
+ * listed in +REQUIRED_BY don't exist.
+ *
+ * Result returned in the **list, while return value is equal
+ * to the number of entries in the resulting list. Print error
+ * message and return -1 on error.
+ */
+int
+requiredby(const char *pkgname, struct reqr_by_head **list, Boolean strict, Boolean filter)
+{
+ FILE *fp;
+ char fbuf[FILENAME_MAX], fname[FILENAME_MAX];
+ int retval;
+ struct reqr_by_entry *rb_entry;
+ static struct reqr_by_head rb_list = STAILQ_HEAD_INITIALIZER(rb_list);
+
+ *list = &rb_list;
+ /* Deallocate any previously allocated space */
+ while (!STAILQ_EMPTY(&rb_list)) {
+ rb_entry = STAILQ_FIRST(&rb_list);
+ STAILQ_REMOVE_HEAD(&rb_list, link);
+ free(rb_entry);
+ }
+
+ if (!isinstalledpkg(pkgname)) {
+ if (strict == TRUE)
+ warnx("no such package '%s' installed", pkgname);
+ return -1;
+ }
+
+ snprintf(fname, sizeof(fname), "%s/%s/%s", LOG_DIR, pkgname,
+ REQUIRED_BY_FNAME);
+ fp = fopen(fname, "r");
+ if (fp == NULL) {
+ /* Probably pkgname doesn't have any packages that depend on it */
+ if (strict == TRUE)
+ warnx("couldn't open dependency file '%s'", fname);
+ return 0;
+ }
+
+ retval = 0;
+ while (fgets(fbuf, sizeof(fbuf), fp) != NULL) {
+ if (fbuf[strlen(fbuf) - 1] == '\n')
+ fbuf[strlen(fbuf) - 1] = '\0';
+ if (filter == TRUE && !isinstalledpkg(fbuf)) {
+ if (strict == TRUE)
+ warnx("package '%s' is recorded in the '%s' but isn't "
+ "actually installed", fbuf, fname);
+ continue;
+ }
+ retval++;
+ rb_entry = malloc(sizeof(*rb_entry));
+ if (rb_entry == NULL) {
+ warnx("%s(): malloc() failed", __func__);
+ retval = -1;
+ break;
+ }
+ strlcpy(rb_entry->pkgname, fbuf, sizeof(rb_entry->pkgname));
+ STAILQ_INSERT_TAIL(&rb_list, rb_entry, link);
+ }
+ fclose(fp);
+
+ return retval;
+}
diff --git a/usr.sbin/pkg_install/lib/exec.c b/usr.sbin/pkg_install/lib/exec.c
new file mode 100644
index 0000000..86285fb
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/exec.c
@@ -0,0 +1,106 @@
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * Miscellaneous system routines.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "lib.h"
+#include <err.h>
+
+/*
+ * Unusual system() substitute. Accepts format string and args,
+ * builds and executes command. Returns exit code.
+ */
+
+int
+vsystem(const char *fmt, ...)
+{
+ va_list args;
+ char *cmd;
+ int ret, maxargs;
+
+ maxargs = sysconf(_SC_ARG_MAX);
+ maxargs -= 32; /* some slop for the sh -c */
+ cmd = malloc(maxargs);
+ if (!cmd) {
+ warnx("vsystem can't alloc arg space");
+ return 1;
+ }
+
+ va_start(args, fmt);
+ if (vsnprintf(cmd, maxargs, fmt, args) > maxargs) {
+ warnx("vsystem args are too long");
+ return 1;
+ }
+#ifdef DEBUG
+printf("Executing %s\n", cmd);
+#endif
+ ret = system(cmd);
+ va_end(args);
+ free(cmd);
+ return ret;
+}
+
+char *
+vpipe(const char *fmt, ...)
+{
+ FILE *fp;
+ char *cmd, *rp;
+ int maxargs;
+ va_list args;
+
+ rp = malloc(MAXPATHLEN);
+ if (!rp) {
+ warnx("vpipe can't alloc buffer space");
+ return NULL;
+ }
+ maxargs = sysconf(_SC_ARG_MAX);
+ maxargs -= 32; /* some slop for the sh -c */
+ cmd = alloca(maxargs);
+ if (!cmd) {
+ warnx("vpipe can't alloc arg space");
+ return NULL;
+ }
+
+ va_start(args, fmt);
+ if (vsnprintf(cmd, maxargs, fmt, args) > maxargs) {
+ warnx("vsystem args are too long");
+ return NULL;
+ }
+#ifdef DEBUG
+ fprintf(stderr, "Executing %s\n", cmd);
+#endif
+ fflush(NULL);
+ fp = popen(cmd, "r");
+ if (fp == NULL) {
+ warnx("popen() failed");
+ return NULL;
+ }
+ get_string(rp, MAXPATHLEN, fp);
+#ifdef DEBUG
+ fprintf(stderr, "Returned %s\n", rp);
+#endif
+ va_end(args);
+ if (pclose(fp) || (strlen(rp) == 0)) {
+ free(rp);
+ return NULL;
+ }
+ return rp;
+}
diff --git a/usr.sbin/pkg_install/lib/file.c b/usr.sbin/pkg_install/lib/file.c
new file mode 100644
index 0000000..163517a
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/file.c
@@ -0,0 +1,534 @@
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * Miscellaneous file access utilities.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "lib.h"
+#include <err.h>
+#include <fetch.h>
+#include <pwd.h>
+#include <time.h>
+#include <sys/wait.h>
+
+/* Quick check to see if a file exists */
+Boolean
+fexists(const char *fname)
+{
+ struct stat dummy;
+ if (!lstat(fname, &dummy))
+ return TRUE;
+ return FALSE;
+}
+
+/* Quick check to see if something is a directory or symlink to a directory */
+Boolean
+isdir(const char *fname)
+{
+ struct stat sb;
+
+ if (lstat(fname, &sb) != FAIL && S_ISDIR(sb.st_mode))
+ return TRUE;
+ else if (lstat(strconcat(fname, "/."), &sb) != FAIL && S_ISDIR(sb.st_mode))
+ return TRUE;
+ else
+ return FALSE;
+}
+
+/* Check to see if file is a dir or symlink to a dir, and is empty */
+Boolean
+isemptydir(const char *fname)
+{
+ if (isdir(fname)) {
+ DIR *dirp;
+ struct dirent *dp;
+
+ dirp = opendir(fname);
+ if (!dirp)
+ return FALSE; /* no perms, leave it alone */
+ for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
+ if (strcmp(dp->d_name, ".") && strcmp(dp->d_name, "..")) {
+ closedir(dirp);
+ return FALSE;
+ }
+ }
+ (void)closedir(dirp);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ * Returns TRUE if file is a regular file or symlink pointing to a regular
+ * file
+ */
+Boolean
+isfile(const char *fname)
+{
+ struct stat sb;
+ if (stat(fname, &sb) != FAIL && S_ISREG(sb.st_mode))
+ return TRUE;
+ return FALSE;
+}
+
+/*
+ * Check to see if file is a file or symlink pointing to a file and is empty.
+ * If nonexistent or not a file, say "it's empty", otherwise return TRUE if
+ * zero sized.
+ */
+Boolean
+isemptyfile(const char *fname)
+{
+ struct stat sb;
+ if (stat(fname, &sb) != FAIL && S_ISREG(sb.st_mode)) {
+ if (sb.st_size != 0)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/* Returns TRUE if file is a symbolic link. */
+Boolean
+issymlink(const char *fname)
+{
+ struct stat sb;
+ if (lstat(fname, &sb) != FAIL && S_ISLNK(sb.st_mode))
+ return TRUE;
+ return FALSE;
+}
+
+/* Returns TRUE if file is a URL specification */
+Boolean
+isURL(const char *fname)
+{
+ /*
+ * I'm sure there are other types of URL specifications that I could
+ * also be looking for here, but for now I'll just be happy to get ftp
+ * and http working.
+ */
+ if (!fname)
+ return FALSE;
+ while (isspace(*fname))
+ ++fname;
+ if (!strncmp(fname, "ftp://", 6) || !strncmp(fname, "http://", 7))
+ return TRUE;
+ return FALSE;
+}
+
+#define HOSTNAME_MAX 64
+/*
+ * Try and fetch a file by URL, returning the directory name for where
+ * it's unpacked, if successful.
+ */
+char *
+fileGetURL(const char *base, const char *spec)
+{
+ char *cp, *rp;
+ char fname[FILENAME_MAX];
+ char pen[FILENAME_MAX];
+ char buf[8192];
+ FILE *ftp;
+ pid_t tpid;
+ int pfd[2], pstat, r, w;
+ char *hint;
+ int fd;
+
+ rp = NULL;
+ /* Special tip that sysinstall left for us */
+ hint = getenv("PKG_ADD_BASE");
+ if (!isURL(spec)) {
+ if (!base && !hint)
+ return NULL;
+ /*
+ * We've been given an existing URL (that's known-good) and now we need
+ * to construct a composite one out of that and the basename we were
+ * handed as a dependency.
+ */
+ if (base) {
+ strcpy(fname, base);
+ /*
+ * Advance back two slashes to get to the root of the package
+ * hierarchy
+ */
+ cp = strrchr(fname, '/');
+ if (cp) {
+ *cp = '\0'; /* chop name */
+ cp = strrchr(fname, '/');
+ }
+ if (cp) {
+ *(cp + 1) = '\0';
+ strcat(cp, "All/");
+ strcat(cp, spec);
+ 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);
+ strcat(fname, ".tgz");
+ }
+ }
+ else
+ strcpy(fname, spec);
+
+ if ((ftp = fetchGetURL(fname, Verbose ? "v" : NULL)) == NULL) {
+ printf("Error: FTP Unable to get %s: %s\n",
+ fname, fetchLastErrString);
+ return NULL;
+ }
+
+ if (isatty(0) || Verbose)
+ printf("Fetching %s...", fname), fflush(stdout);
+ pen[0] = '\0';
+ if ((rp = make_playpen(pen, 0)) == NULL) {
+ printf("Error: Unable to construct a new playpen for FTP!\n");
+ fclose(ftp);
+ return NULL;
+ }
+ if (pipe(pfd) == -1) {
+ warn("pipe()");
+ cleanup(0);
+ exit(2);
+ }
+ if ((tpid = fork()) == -1) {
+ warn("pipe()");
+ cleanup(0);
+ exit(2);
+ }
+ if (!tpid) {
+ dup2(pfd[0], 0);
+ for (fd = getdtablesize() - 1; fd >= 3; --fd)
+ close(fd);
+ execl("/usr/bin/tar", "tar", Verbose ? "-xzvf" : "-xzf", "-",
+ (char *)0);
+ _exit(2);
+ }
+ close(pfd[0]);
+ for (;;) {
+ if ((r = fread(buf, 1, sizeof buf, ftp)) < 1)
+ break;
+ if ((w = write(pfd[1], buf, r)) != r)
+ break;
+ }
+ if (ferror(ftp))
+ warn("warning: error reading from server");
+ fclose(ftp);
+ close(pfd[1]);
+ if (w == -1)
+ warn("warning: error writing to tar");
+ tpid = waitpid(tpid, &pstat, 0);
+ if (Verbose)
+ printf("tar command returns %d status\n", WEXITSTATUS(pstat));
+ if (rp && (isatty(0) || Verbose))
+ printf(" Done.\n");
+ return rp;
+}
+
+char *
+fileFindByPath(const char *base, const char *fname)
+{
+ static char tmp[FILENAME_MAX];
+ char *cp;
+ const char *suffixes[] = {".tgz", ".tar", ".tbz2", NULL};
+ int i;
+
+ if (fexists(fname) && isfile(fname)) {
+ strcpy(tmp, fname);
+ return tmp;
+ }
+ if (base) {
+ strcpy(tmp, base);
+
+ cp = strrchr(tmp, '/');
+ if (cp) {
+ *cp = '\0'; /* chop name */
+ cp = strrchr(tmp, '/');
+ }
+ if (cp)
+ for (i = 0; suffixes[i] != NULL; i++) {
+ *(cp + 1) = '\0';
+ strcat(cp, "All/");
+ strcat(cp, fname);
+ strcat(cp, suffixes[i]);
+ if (fexists(tmp))
+ return tmp;
+ }
+ }
+
+ cp = getenv("PKG_PATH");
+ while (cp) {
+ char *cp2 = strsep(&cp, ":");
+
+ for (i = 0; suffixes[i] != NULL; i++) {
+ snprintf(tmp, FILENAME_MAX, "%s/%s%s", cp2 ? cp2 : cp, fname, suffixes[i]);
+ if (fexists(tmp) && isfile(tmp))
+ return tmp;
+ }
+ }
+ return NULL;
+}
+
+char *
+fileGetContents(const char *fname)
+{
+ char *contents;
+ struct stat sb;
+ int fd;
+
+ if (stat(fname, &sb) == FAIL) {
+ cleanup(0);
+ errx(2, "%s: can't stat '%s'", __func__, fname);
+ }
+
+ contents = (char *)malloc(sb.st_size + 1);
+ fd = open(fname, O_RDONLY, 0);
+ if (fd == FAIL) {
+ cleanup(0);
+ errx(2, "%s: unable to open '%s' for reading", __func__, fname);
+ }
+ if (read(fd, contents, sb.st_size) != sb.st_size) {
+ cleanup(0);
+ errx(2, "%s: short read on '%s' - did not get %qd bytes", __func__,
+ fname, (long long)sb.st_size);
+ }
+ close(fd);
+ contents[sb.st_size] = '\0';
+ return contents;
+}
+
+/*
+ * Takes a filename and package name, returning (in "try") the
+ * canonical "preserve" name for it.
+ */
+Boolean
+make_preserve_name(char *try, int max, const char *name, const char *file)
+{
+ int len, i;
+
+ if ((len = strlen(file)) == 0)
+ return FALSE;
+ else
+ i = len - 1;
+ strncpy(try, file, max);
+ if (try[i] == '/') /* Catch trailing slash early and save checking in the loop */
+ --i;
+ for (; i; i--) {
+ if (try[i] == '/') {
+ try[i + 1]= '.';
+ strncpy(&try[i + 2], &file[i + 1], max - i - 2);
+ break;
+ }
+ }
+ if (!i) {
+ try[0] = '.';
+ strncpy(try + 1, file, max - 1);
+ }
+ /* I should probably be called rude names for these inline assignments */
+ strncat(try, ".", max -= strlen(try));
+ strncat(try, name, max -= strlen(name));
+ strncat(try, ".", max--);
+ strncat(try, "backup", max -= 6);
+ return TRUE;
+}
+
+/* Write the contents of "str" to a file */
+void
+write_file(const char *name, const char *str)
+{
+ FILE *fp;
+ size_t len;
+
+ fp = fopen(name, "w");
+ if (!fp) {
+ cleanup(0);
+ errx(2, "%s: cannot fopen '%s' for writing", __func__, name);
+ }
+ len = strlen(str);
+ if (fwrite(str, 1, len, fp) != len) {
+ cleanup(0);
+ errx(2, "%s: short fwrite on '%s', tried to write %ld bytes",
+ __func__, name, (long)len);
+ }
+ if (fclose(fp)) {
+ cleanup(0);
+ errx(2, "%s: failure to fclose '%s'", __func__, name);
+ }
+}
+
+void
+copy_file(const char *dir, const char *fname, const char *to)
+{
+ char cmd[FILENAME_MAX];
+
+ if (fname[0] == '/')
+ snprintf(cmd, FILENAME_MAX, "cp -r %s %s", fname, to);
+ else
+ snprintf(cmd, FILENAME_MAX, "cp -r %s/%s %s", dir, fname, to);
+ if (vsystem(cmd)) {
+ cleanup(0);
+ errx(2, "%s: could not perform '%s'", __func__, cmd);
+ }
+}
+
+void
+move_file(const char *dir, const char *fname, const char *to)
+{
+ char cmd[FILENAME_MAX];
+
+ if (fname[0] == '/')
+ snprintf(cmd, FILENAME_MAX, "mv %s %s", fname, to);
+ else
+ snprintf(cmd, FILENAME_MAX, "mv %s/%s %s", dir, fname, to);
+ if (vsystem(cmd)) {
+ cleanup(0);
+ errx(2, "%s: could not perform '%s'", __func__, cmd);
+ }
+}
+
+/*
+ * Copy a hierarchy (possibly from dir) to the current directory, or
+ * if "to" is TRUE, from the current directory to a location someplace
+ * else.
+ *
+ * Though slower, using tar to copy preserves symlinks and everything
+ * without me having to write some big hairy routine to do it.
+ */
+void
+copy_hierarchy(const char *dir, const char *fname, Boolean to)
+{
+ char cmd[FILENAME_MAX * 3];
+
+ if (!to) {
+ /* If absolute path, use it */
+ if (*fname == '/')
+ dir = "/";
+ snprintf(cmd, FILENAME_MAX * 3, "tar cf - -C %s %s | tar xpf -",
+ dir, fname);
+ }
+ else
+ snprintf(cmd, FILENAME_MAX * 3, "tar cf - %s | tar xpf - -C %s",
+ fname, dir);
+#ifdef DEBUG
+ printf("Using '%s' to copy trees.\n", cmd);
+#endif
+ if (system(cmd)) {
+ cleanup(0);
+ errx(2, "%s: could not perform '%s'", __func__, cmd);
+ }
+}
+
+/* Unpack a tar file */
+int
+unpack(const char *pkg, const char *flist)
+{
+ char args[10], suff[80], *cp;
+
+ args[0] = '\0';
+ /*
+ * Figure out by a crude heuristic whether this or not this is probably
+ * compressed and whichever compression utility was used (gzip or bzip2).
+ */
+ if (strcmp(pkg, "-")) {
+ cp = strrchr(pkg, '.');
+ if (cp) {
+ strcpy(suff, cp + 1);
+ if (strchr(suff, 'z') || strchr(suff, 'Z')) {
+ if (strchr(suff, 'b'))
+ strcpy(args, "-y");
+ else
+ 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, const char *fmt, const char *dir, const 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..291f48b
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/global.c
@@ -0,0 +1,31 @@
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+
+ * 18 July 1993
+ *
+ * Semi-convenient place to stick some needed globals.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "lib.h"
+
+/* These are global for all utils */
+Boolean Verbose = FALSE;
+Boolean Fake = FALSE;
+Boolean Force = FALSE;
+int AutoAnswer = FALSE;
diff --git a/usr.sbin/pkg_install/lib/lib.h b/usr.sbin/pkg_install/lib/lib.h
new file mode 100644
index 0000000..6a01fac
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/lib.h
@@ -0,0 +1,215 @@
+/* $FreeBSD$ */
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * Include and define various things wanted by the library routines.
+ *
+ */
+
+#ifndef _INST_LIB_LIB_H_
+#define _INST_LIB_LIB_H_
+
+/* Includes */
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/queue.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/* Macros */
+#define SUCCESS (0)
+#define FAIL (-1)
+
+#ifndef TRUE
+#define TRUE (1)
+#endif
+
+#ifndef FALSE
+#define FALSE (0)
+#endif
+
+#define YES 2
+#define NO 1
+
+/* Usually "rm", but often "echo" during debugging! */
+#define REMOVE_CMD "rm"
+
+/* Usually "rm", but often "echo" during debugging! */
+#define RMDIR_CMD "rmdir"
+
+/* Where we put logging information by default, else ${PKG_DBDIR} if set */
+#define DEF_LOG_DIR "/var/db/pkg"
+/* just in case we change the environment variable name */
+#define PKG_DBDIR "PKG_DBDIR"
+/* macro to get name of directory where we put logging information */
+#define LOG_DIR (getenv(PKG_DBDIR) ? getenv(PKG_DBDIR) : DEF_LOG_DIR)
+
+/* The names of our "special" files */
+#define CONTENTS_FNAME "+CONTENTS"
+#define COMMENT_FNAME "+COMMENT"
+#define DESC_FNAME "+DESC"
+#define INSTALL_FNAME "+INSTALL"
+#define POST_INSTALL_FNAME "+POST-INSTALL"
+#define DEINSTALL_FNAME "+DEINSTALL"
+#define POST_DEINSTALL_FNAME "+POST-DEINSTALL"
+#define REQUIRE_FNAME "+REQUIRE"
+#define REQUIRED_BY_FNAME "+REQUIRED_BY"
+#define DISPLAY_FNAME "+DISPLAY"
+#define MTREE_FNAME "+MTREE_DIRS"
+
+#define CMD_CHAR '@' /* prefix for extended PLIST cmd */
+
+/* The name of the "prefix" environment variable given to scripts */
+#define PKG_PREFIX_VNAME "PKG_PREFIX"
+
+/* Version numbers to assist with changes in package file format */
+#define PLIST_FMT_VER_MAJOR 1
+#define PLIST_FMT_VER_MINOR 1
+
+enum _plist_t {
+ PLIST_FILE, PLIST_CWD, PLIST_CMD, PLIST_CHMOD,
+ PLIST_CHOWN, PLIST_CHGRP, PLIST_COMMENT, PLIST_IGNORE,
+ PLIST_NAME, PLIST_UNEXEC, PLIST_SRC, PLIST_DISPLAY,
+ PLIST_PKGDEP, PLIST_MTREE, PLIST_DIR_RM, PLIST_IGNORE_INST,
+ PLIST_OPTION, PLIST_ORIGIN, PLIST_DEPORIGIN
+};
+typedef enum _plist_t plist_t;
+
+enum _match_t {
+ MATCH_ALL, MATCH_EXACT, MATCH_GLOB, MATCH_REGEX
+};
+typedef enum _match_t match_t;
+
+/* Types */
+typedef unsigned int Boolean;
+
+struct _plist {
+ struct _plist *prev, *next;
+ char *name;
+ Boolean marked;
+ plist_t type;
+};
+typedef struct _plist *PackingList;
+
+struct _pack {
+ struct _plist *head, *tail;
+ char *name;
+ char *origin;
+ int fmtver_maj, fmtver_mnr;
+};
+typedef struct _pack Package;
+
+struct reqr_by_entry {
+ STAILQ_ENTRY(reqr_by_entry) link;
+ char pkgname[PATH_MAX];
+};
+STAILQ_HEAD(reqr_by_head, reqr_by_entry);
+
+/* Prototypes */
+/* Misc */
+int vsystem(const char *, ...);
+char *vpipe(const char *, ...);
+void cleanup(int);
+char *make_playpen(char *, off_t);
+char *where_playpen(void);
+void leave_playpen(void);
+off_t min_free(const char *);
+
+/* String */
+char *get_dash_string(char **);
+char *copy_string(const char *);
+Boolean suffix(const char *, const char *);
+void nuke_suffix(char *);
+void str_lowercase(char *);
+char *strconcat(const char *, const char *);
+char *get_string(char *, int, FILE *);
+
+/* File */
+Boolean fexists(const char *);
+Boolean isdir(const char *);
+Boolean isemptydir(const char *fname);
+Boolean isemptyfile(const char *fname);
+Boolean isfile(const char *);
+Boolean isempty(const char *);
+Boolean issymlink(const char *);
+Boolean isURL(const char *);
+char *fileGetURL(const char *, const char *);
+char *fileFindByPath(const char *, const char *);
+char *fileGetContents(const char *);
+void write_file(const char *, const char *);
+void copy_file(const char *, const char *, const char *);
+void move_file(const char *, const char *, const char *);
+void copy_hierarchy(const char *, const char *, Boolean);
+int delete_hierarchy(const char *, Boolean, Boolean);
+int unpack(const char *, const char *);
+void format_cmd(char *, const char *, const char *, const char *);
+
+/* Msg */
+void upchuck(const char *);
+void barf(const char *, ...);
+void whinge(const char *, ...);
+Boolean y_or_n(Boolean, const char *, ...);
+
+/* Packing list */
+PackingList new_plist_entry(void);
+PackingList last_plist(Package *);
+PackingList find_plist(Package *, plist_t);
+char *find_plist_option(Package *, const char *name);
+void plist_delete(Package *, Boolean, plist_t, const char *);
+void free_plist(Package *);
+void mark_plist(Package *);
+void csum_plist_entry(char *, PackingList);
+void add_plist(Package *, plist_t, const char *);
+void add_plist_top(Package *, plist_t, const char *);
+void delete_plist(Package *pkg, Boolean all, plist_t type, const char *name);
+void write_plist(Package *, FILE *);
+void read_plist(Package *, FILE *);
+int plist_cmd(const char *, char **);
+int delete_package(Boolean, Boolean, Package *);
+Boolean make_preserve_name(char *, int, const char *, const char *);
+
+/* For all */
+int pkg_perform(char **);
+
+/* Query installed packages */
+char **matchinstalled(match_t, char **, int *);
+char **matchbyorigin(const char *, int *);
+int isinstalledpkg(const char *name);
+
+/* Dependencies */
+int sortdeps(char **);
+int chkifdepends(const char *, const char *);
+int requiredby(const char *, struct reqr_by_head **, Boolean, Boolean);
+
+/* Version */
+int verscmp(Package *, int, int);
+const char *version_of(const char *, int *, int *);
+int version_cmp(const char *, const char *);
+
+/* Externs */
+extern Boolean Verbose;
+extern Boolean Fake;
+extern Boolean Force;
+extern int AutoAnswer;
+
+#endif /* _INST_LIB_LIB_H_ */
diff --git a/usr.sbin/pkg_install/lib/match.c b/usr.sbin/pkg_install/lib/match.c
new file mode 100644
index 0000000..cf745c3
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/match.c
@@ -0,0 +1,343 @@
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Maxim Sobolev
+ * 24 February 2001
+ *
+ * Routines used to query installed packages.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "lib.h"
+#include <err.h>
+#include <fnmatch.h>
+#include <fts.h>
+#include <regex.h>
+
+/*
+ * Simple structure representing argv-like
+ * NULL-terminated list.
+ */
+struct store {
+ int currlen;
+ int used;
+ char **store;
+};
+
+static int rex_match(const char *, const char *);
+struct store *storecreate(struct store *);
+static int storeappend(struct store *, const char *);
+static int fname_cmp(const FTSENT **, const FTSENT **);
+
+/*
+ * Function to query names of installed packages.
+ * MatchType - one of MATCH_ALL, MATCH_REGEX, MATCH_GLOB;
+ * patterns - NULL-terminated list of glob or regex patterns
+ * (could be NULL for MATCH_ALL);
+ * retval - return value (could be NULL if you don't want/need
+ * return value).
+ * Returns NULL-terminated list with matching names.
+ * Names in list returned are dynamically allocated and should
+ * not be altered by the caller.
+ */
+char **
+matchinstalled(match_t MatchType, char **patterns, int *retval)
+{
+ int i, errcode, len;
+ char *matched;
+ const char *paths[2] = {LOG_DIR, NULL};
+ static struct store *store = NULL;
+ FTS *ftsp;
+ FTSENT *f;
+ Boolean *lmatched;
+
+ store = storecreate(store);
+ if (store == NULL) {
+ if (retval != NULL)
+ *retval = 1;
+ return NULL;
+ }
+
+ if (retval != NULL)
+ *retval = 0;
+
+ if (!isdir(paths[0])) {
+ if (retval != NULL)
+ *retval = 1;
+ return NULL;
+ /* Not reached */
+ }
+
+ /* Count number of patterns */
+ if (patterns != NULL) {
+ for (len = 0; patterns[len]; len++) {}
+ lmatched = alloca(sizeof(*lmatched) * len);
+ if (lmatched == NULL) {
+ warnx("%s(): alloca() failed", __func__);
+ if (retval != NULL)
+ *retval = 1;
+ return NULL;
+ }
+ } else
+ len = 0;
+
+ for (i = 0; i < len; i++)
+ lmatched[i] = FALSE;
+
+ ftsp = fts_open((char * const *)(uintptr_t)paths, FTS_LOGICAL | FTS_NOCHDIR | FTS_NOSTAT, fname_cmp);
+ if (ftsp != NULL) {
+ while ((f = fts_read(ftsp)) != NULL) {
+ if (f->fts_info == FTS_D && f->fts_level == 1) {
+ fts_set(ftsp, f, FTS_SKIP);
+ matched = NULL;
+ errcode = 0;
+ if (MatchType == MATCH_ALL)
+ matched = f->fts_name;
+ else
+ for (i = 0; patterns[i]; i++) {
+ switch (MatchType) {
+ case MATCH_REGEX:
+ errcode = rex_match(patterns[i], f->fts_name);
+ if (errcode == 1) {
+ matched = f->fts_name;
+ errcode = 0;
+ }
+ break;
+ case MATCH_GLOB:
+ if (fnmatch(patterns[i], f->fts_name, 0) == 0) {
+ matched = f->fts_name;
+ lmatched[i] = TRUE;
+ }
+ break;
+ default:
+ break;
+ }
+ if (matched != NULL || errcode != 0)
+ break;
+ }
+ if (errcode == 0 && matched != NULL)
+ errcode = storeappend(store, matched);
+ if (errcode != 0) {
+ if (retval != NULL)
+ *retval = 1;
+ return NULL;
+ /* Not reached */
+ }
+ }
+ }
+ fts_close(ftsp);
+ }
+
+ if (MatchType == MATCH_GLOB) {
+ for (i = 0; i < len; i++)
+ if (lmatched[i] == FALSE)
+ storeappend(store, patterns[i]);
+ }
+
+ if (store->used == 0)
+ return NULL;
+ else
+ return store->store;
+}
+
+/*
+ * Synopsis is similar to matchinstalled(), but use origin
+ * as a key for matching packages.
+ */
+char **
+matchbyorigin(const char *origin, int *retval)
+{
+ char **installed;
+ int i;
+ static struct store *store = NULL;
+
+ store = storecreate(store);
+ if (store == NULL) {
+ if (retval != NULL)
+ *retval = 1;
+ return NULL;
+ }
+
+ if (retval != NULL)
+ *retval = 0;
+
+ installed = matchinstalled(MATCH_ALL, NULL, retval);
+ if (installed == NULL)
+ return NULL;
+
+ for (i = 0; installed[i] != NULL; i++) {
+ FILE *fp;
+ char *cp, tmp[PATH_MAX];
+ int cmd;
+
+ snprintf(tmp, PATH_MAX, "%s/%s", LOG_DIR, installed[i]);
+ /*
+ * SPECIAL CASE: ignore empty dirs, since we can can see them
+ * during port installation.
+ */
+ if (isemptydir(tmp))
+ continue;
+ snprintf(tmp, PATH_MAX, "%s/%s", tmp, CONTENTS_FNAME);
+ fp = fopen(tmp, "r");
+ if (fp == NULL) {
+ warn("%s", tmp);
+ if (retval != NULL)
+ *retval = 1;
+ return NULL;
+ }
+
+ cmd = -1;
+ while (fgets(tmp, sizeof(tmp), fp)) {
+ int len = strlen(tmp);
+
+ while (len && isspace(tmp[len - 1]))
+ tmp[--len] = '\0';
+ if (!len)
+ continue;
+ cp = tmp;
+ if (tmp[0] != CMD_CHAR)
+ continue;
+ cmd = plist_cmd(tmp + 1, &cp);
+ if (cmd == PLIST_ORIGIN) {
+ if (strcmp(origin, cp) == 0)
+ storeappend(store, installed[i]);
+ break;
+ }
+ }
+ if (cmd != PLIST_ORIGIN)
+ warnx("package %s has no origin recorded", installed[i]);
+ fclose(fp);
+ }
+
+ if (store->used == 0)
+ return NULL;
+ else
+ return store->store;
+}
+
+/*
+ * Return TRUE if the specified package is installed,
+ * or FALSE otherwise.
+ */
+int
+isinstalledpkg(const char *name)
+{
+ char buf[FILENAME_MAX];
+
+ snprintf(buf, sizeof(buf), "%s/%s", LOG_DIR, name);
+ if (!isdir(buf) || access(buf, R_OK) == FAIL)
+ return FALSE;
+
+ snprintf(buf, sizeof(buf), "%s/%s", buf, CONTENTS_FNAME);
+ if (!isfile(buf) || access(buf, R_OK) == FAIL)
+ return FALSE;
+
+ return TRUE;
+}
+
+/*
+ * Returns 1 if specified pkgname matches RE pattern.
+ * Otherwise returns 0 if doesn't match or -1 if RE
+ * engine reported an error (usually invalid syntax).
+ */
+static int
+rex_match(const char *pattern, const char *pkgname)
+{
+ char errbuf[128];
+ int errcode;
+ int retval;
+ regex_t rex;
+
+ retval = 0;
+
+ errcode = regcomp(&rex, pattern, REG_BASIC | REG_NOSUB);
+ if (errcode == 0)
+ errcode = regexec(&rex, pkgname, 0, NULL, 0);
+
+ if (errcode == 0) {
+ retval = 1;
+ } else if (errcode != REG_NOMATCH) {
+ regerror(errcode, &rex, errbuf, sizeof(errbuf));
+ warnx("%s: %s", pattern, errbuf);
+ retval = -1;
+ }
+
+ regfree(&rex);
+
+ return retval;
+}
+
+/*
+ * Create an empty store, optionally deallocating
+ * any previously allocated space if store != NULL.
+ */
+struct store *
+storecreate(struct store *store)
+{
+ int i;
+
+ if (store == NULL) {
+ store = malloc(sizeof *store);
+ if (store == NULL) {
+ warnx("%s(): malloc() failed", __func__);
+ return NULL;
+ }
+ store->currlen = 0;
+ store->store = NULL;
+ } else if (store->store != NULL) {
+ /* Free previously allocated memory */
+ for (i = 0; store->store[i] != NULL; i++)
+ free(store->store[i]);
+ store->store[0] = NULL;
+ }
+ store->used = 0;
+
+ return store;
+}
+
+/*
+ * Append specified element to the provided store.
+ */
+static int
+storeappend(struct store *store, const char *item)
+{
+ if (store->used + 2 > store->currlen) {
+ store->currlen += 16;
+ store->store = reallocf(store->store,
+ store->currlen * sizeof(*(store->store)));
+ if (store->store == NULL) {
+ store->currlen = 0;
+ warnx("%s(): reallocf() failed", __func__);
+ return 1;
+ }
+ }
+
+ asprintf(&(store->store[store->used]), "%s", item);
+ if (store->store[store->used] == NULL) {
+ warnx("%s(): malloc() failed", __func__);
+ return 1;
+ }
+ store->used++;
+ store->store[store->used] = NULL;
+
+ return 0;
+}
+
+static int
+fname_cmp(const FTSENT **a, const FTSENT **b)
+{
+ return strcmp((*a)->fts_name, (*b)->fts_name);
+}
diff --git a/usr.sbin/pkg_install/lib/msg.c b/usr.sbin/pkg_install/lib/msg.c
new file mode 100644
index 0000000..5b17624
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/msg.c
@@ -0,0 +1,75 @@
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * Miscellaneous message routines.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "lib.h"
+#include <err.h>
+#include <paths.h>
+
+/* Die a relatively simple death */
+void
+upchuck(const char *message)
+{
+ cleanup(0);
+ errx(1, "fatal error during execution: %s", message);
+}
+
+/*
+ * As a yes/no question, prompting from the varargs string and using
+ * default if user just hits return.
+ */
+Boolean
+y_or_n(Boolean def, const char *msg, ...)
+{
+ va_list args;
+ int ch = 0;
+ FILE *tty;
+
+ va_start(args, msg);
+ /*
+ * Need to open /dev/tty because file collection may have been
+ * collected on stdin
+ */
+ tty = fopen(_PATH_TTY, "r");
+ if (!tty) {
+ cleanup(0);
+ errx(2, "can't open %s!", _PATH_TTY);
+ }
+ while (ch != 'Y' && ch != 'N') {
+ vfprintf(stderr, msg, args);
+ if (def)
+ fprintf(stderr, " [yes]? ");
+ else
+ fprintf(stderr, " [no]? ");
+ fflush(stderr);
+ if (AutoAnswer) {
+ ch = (AutoAnswer == YES) ? 'Y' : 'N';
+ fprintf(stderr, "%c\n", ch);
+ }
+ else
+ ch = toupper(fgetc(tty));
+ if (ch == '\n')
+ ch = (def) ? 'Y' : 'N';
+ }
+ fclose(tty) ;
+ return (ch == 'Y') ? TRUE : FALSE;
+}
diff --git a/usr.sbin/pkg_install/lib/pen.c b/usr.sbin/pkg_install/lib/pen.c
new file mode 100644
index 0000000..eedda00
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/pen.c
@@ -0,0 +1,176 @@
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * Routines for managing the "play pen".
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "lib.h"
+#include <err.h>
+#include <libgen.h>
+#include <sys/signal.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+
+/* For keeping track of where we are */
+static char PenLocation[FILENAME_MAX];
+static char Previous[FILENAME_MAX];
+
+char *
+where_playpen(void)
+{
+ return PenLocation;
+}
+
+/* Find a good place to play. */
+static char *
+find_play_pen(char *pen, off_t sz)
+{
+ char *cp;
+ struct stat sb;
+
+ if (pen[0] && isdir(dirname(pen)) == TRUE && (min_free(dirname(pen)) >= sz))
+ return pen;
+ else if ((cp = getenv("PKG_TMPDIR")) != NULL && stat(cp, &sb) != FAIL && (min_free(cp) >= sz))
+ sprintf(pen, "%s/instmp.XXXXXX", cp);
+ else if ((cp = getenv("TMPDIR")) != NULL && stat(cp, &sb) != FAIL && (min_free(cp) >= sz))
+ sprintf(pen, "%s/instmp.XXXXXX", cp);
+ else if (stat("/var/tmp", &sb) != FAIL && min_free("/var/tmp") >= sz)
+ strcpy(pen, "/var/tmp/instmp.XXXXXX");
+ else if (stat("/tmp", &sb) != FAIL && min_free("/tmp") >= sz)
+ strcpy(pen, "/tmp/instmp.XXXXXX");
+ else if ((stat("/usr/tmp", &sb) == SUCCESS || mkdir("/usr/tmp", 01777) == SUCCESS) && min_free("/usr/tmp") >= sz)
+ strcpy(pen, "/usr/tmp/instmp.XXXXXX");
+ else {
+ cleanup(0);
+ errx(2,
+"%s: can't find enough temporary space to extract the files, please set your\n"
+"PKG_TMPDIR environment variable to a location with at least %ld bytes\n"
+"free", __func__, (long)sz);
+ return NULL;
+ }
+ return pen;
+}
+
+#define MAX_STACK 20
+static char *pstack[MAX_STACK];
+static int pdepth = -1;
+
+static void
+pushPen(const char *pen)
+{
+ if (++pdepth == MAX_STACK)
+ errx(2, "%s: stack overflow.\n", __func__);
+ pstack[pdepth] = strdup(pen);
+}
+
+static void
+popPen(char *pen)
+{
+ if (pdepth == -1) {
+ pen[0] = '\0';
+ return;
+ }
+ strcpy(pen, pstack[pdepth]);
+ free(pstack[pdepth--]);
+}
+
+/*
+ * Make a temporary directory to play in and chdir() to it, returning
+ * pathname of previous working directory.
+ */
+char *
+make_playpen(char *pen, off_t sz)
+{
+ if (!find_play_pen(pen, sz))
+ return NULL;
+
+ if (!mkdtemp(pen)) {
+ cleanup(0);
+ errx(2, "%s: can't mktemp '%s'", __func__, pen);
+ }
+ if (chmod(pen, 0700) == FAIL) {
+ cleanup(0);
+ errx(2, "%s: can't mkdir '%s'", __func__, pen);
+ }
+
+ if (Verbose) {
+ if (sz)
+ fprintf(stderr, "Requested space: %d bytes, free space: %qd bytes in %s\n", (int)sz, (long long)min_free(pen), pen);
+ }
+
+ if (min_free(pen) < sz) {
+ rmdir(pen);
+ cleanup(0);
+ errx(2, "%s: not enough free space to create '%s'.\n"
+ "Please set your PKG_TMPDIR environment variable to a location\n"
+ "with more space and\ntry the command again", __func__, pen);
+ }
+
+ if (!getcwd(Previous, FILENAME_MAX)) {
+ upchuck("getcwd");
+ return NULL;
+ }
+
+ if (chdir(pen) == FAIL) {
+ cleanup(0);
+ errx(2, "%s: can't chdir to '%s'", __func__, pen);
+ }
+
+ if (PenLocation[0])
+ pushPen(PenLocation);
+
+ strcpy(PenLocation, pen);
+ return Previous;
+}
+
+/* Convenience routine for getting out of playpen */
+void
+leave_playpen()
+{
+ void (*oldsig)(int);
+
+ /* Don't interrupt while we're cleaning up */
+ oldsig = signal(SIGINT, SIG_IGN);
+ if (Previous[0]) {
+ if (chdir(Previous) == FAIL) {
+ cleanup(0);
+ errx(2, "%s: can't chdir back to '%s'", __func__, Previous);
+ }
+ Previous[0] = '\0';
+ }
+ if (PenLocation[0]) {
+ if (PenLocation[0] == '/' && vsystem("rm -rf %s", PenLocation))
+ warnx("couldn't remove temporary dir '%s'", PenLocation);
+ popPen(PenLocation);
+ }
+ signal(SIGINT, oldsig);
+}
+
+off_t
+min_free(const char *tmpdir)
+{
+ struct statfs buf;
+
+ if (statfs(tmpdir, &buf) != 0) {
+ warn("statfs");
+ return -1;
+ }
+ return (off_t)buf.f_bavail * (off_t)buf.f_bsize;
+}
diff --git a/usr.sbin/pkg_install/lib/plist.c b/usr.sbin/pkg_install/lib/plist.c
new file mode 100644
index 0000000..603588e
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/plist.c
@@ -0,0 +1,571 @@
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * General packing list routines.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "lib.h"
+#include <err.h>
+#include <md5.h>
+
+/* Add an item to a packing list */
+void
+add_plist(Package *p, plist_t type, const char *arg)
+{
+ PackingList tmp;
+
+ tmp = new_plist_entry();
+ tmp->name = copy_string(arg);
+ tmp->type = type;
+
+ if (!p->head)
+ p->head = p->tail = tmp;
+ else {
+ tmp->prev = p->tail;
+ p->tail->next = tmp;
+ p->tail = tmp;
+ }
+ switch (type) {
+ case PLIST_NAME:
+ p->name = tmp->name;
+ break;
+
+ case PLIST_ORIGIN:
+ p->origin = tmp->name;
+ break;
+
+ default:
+ break;
+ }
+}
+
+void
+add_plist_top(Package *p, plist_t type, const char *arg)
+{
+ PackingList tmp;
+
+ tmp = new_plist_entry();
+ tmp->name = copy_string(arg);
+ tmp->type = type;
+
+ if (!p->head)
+ p->head = p->tail = tmp;
+ else {
+ tmp->next = p->head;
+ p->head->prev = tmp;
+ p->head = tmp;
+ }
+}
+
+/* Return the last (most recent) entry in a packing list */
+PackingList
+last_plist(Package *p)
+{
+ return p->tail;
+}
+
+/* Mark all items in a packing list to prevent iteration over them */
+void
+mark_plist(Package *pkg)
+{
+ PackingList p = pkg->head;
+
+ while (p) {
+ p->marked = TRUE;
+ p = p->next;
+ }
+}
+
+/* Find a given item in a packing list and, if so, return it (else NULL) */
+PackingList
+find_plist(Package *pkg, plist_t type)
+{
+ PackingList p = pkg->head;
+
+ while (p) {
+ if (p->type == type)
+ return p;
+ p = p->next;
+ }
+ return NULL;
+}
+
+/* Look for a specific boolean option argument in the list */
+char *
+find_plist_option(Package *pkg, const char *name)
+{
+ PackingList p = pkg->head;
+
+ while (p) {
+ if (p->type == PLIST_OPTION && !strcmp(p->name, name))
+ return p->name;
+ p = p->next;
+ }
+ return NULL;
+}
+
+/*
+ * Delete plist item 'type' in the list (if 'name' is non-null, match it
+ * too.) If 'all' is set, delete all items, not just the first occurance.
+ */
+void
+delete_plist(Package *pkg, Boolean all, plist_t type, const char *name)
+{
+ PackingList p = pkg->head;
+
+ while (p) {
+ PackingList pnext = p->next;
+
+ if (p->type == type && (!name || !strcmp(name, p->name))) {
+ free(p->name);
+ if (p->prev)
+ p->prev->next = pnext;
+ else
+ pkg->head = pnext;
+ if (pnext)
+ pnext->prev = p->prev;
+ else
+ pkg->tail = p->prev;
+ free(p);
+ if (!all)
+ return;
+ p = pnext;
+ }
+ else
+ p = p->next;
+ }
+}
+
+/* Allocate a new packing list entry */
+PackingList
+new_plist_entry(void)
+{
+ PackingList ret;
+
+ ret = (PackingList)malloc(sizeof(struct _plist));
+ bzero(ret, sizeof(struct _plist));
+ return ret;
+}
+
+/* Free an entire packing list */
+void
+free_plist(Package *pkg)
+{
+ PackingList p = pkg->head;
+
+ while (p) {
+ PackingList p1 = p->next;
+
+ free(p->name);
+ free(p);
+ p = p1;
+ }
+ pkg->head = pkg->tail = NULL;
+}
+
+/*
+ * For an ascii string denoting a plist command, return its code and
+ * optionally its argument(s)
+ */
+int
+plist_cmd(const char *s, char **arg)
+{
+ char cmd[FILENAME_MAX + 20]; /* 20 == fudge for max cmd len */
+ char *cp;
+ const char *sp;
+
+ strcpy(cmd, s);
+ str_lowercase(cmd);
+ cp = cmd;
+ sp = s;
+ while (*cp) {
+ if (isspace(*cp)) {
+ *cp = '\0';
+ while (isspace(*sp)) /* Never sure if macro, increment later */
+ ++sp;
+ break;
+ }
+ ++cp, ++sp;
+ }
+ if (arg)
+ (const char *)*arg = sp;
+ if (!strcmp(cmd, "cwd"))
+ return PLIST_CWD;
+ else if (!strcmp(cmd, "srcdir"))
+ return PLIST_SRC;
+ else if (!strcmp(cmd, "cd"))
+ return PLIST_CWD;
+ else if (!strcmp(cmd, "exec"))
+ return PLIST_CMD;
+ else if (!strcmp(cmd, "unexec"))
+ return PLIST_UNEXEC;
+ else if (!strcmp(cmd, "mode"))
+ return PLIST_CHMOD;
+ else if (!strcmp(cmd, "owner"))
+ return PLIST_CHOWN;
+ else if (!strcmp(cmd, "group"))
+ return PLIST_CHGRP;
+ else if (!strcmp(cmd, "comment")) {
+ if (!strncmp(*arg, "ORIGIN:", 7)) {
+ *arg += 7;
+ return PLIST_ORIGIN;
+ } else if (!strncmp(*arg, "DEPORIGIN:", 10)) {
+ *arg += 10;
+ return PLIST_DEPORIGIN;
+ }
+ return PLIST_COMMENT;
+ } else if (!strcmp(cmd, "ignore"))
+ return PLIST_IGNORE;
+ else if (!strcmp(cmd, "ignore_inst"))
+ return PLIST_IGNORE_INST;
+ else if (!strcmp(cmd, "name"))
+ return PLIST_NAME;
+ else if (!strcmp(cmd, "display"))
+ return PLIST_DISPLAY;
+ else if (!strcmp(cmd, "pkgdep"))
+ return PLIST_PKGDEP;
+ else if (!strcmp(cmd, "mtree"))
+ return PLIST_MTREE;
+ else if (!strcmp(cmd, "dirrm"))
+ return PLIST_DIR_RM;
+ else if (!strcmp(cmd, "option"))
+ return PLIST_OPTION;
+ else
+ return FAIL;
+}
+
+/* Read a packing list from a file */
+void
+read_plist(Package *pkg, FILE *fp)
+{
+ char *cp, pline[FILENAME_MAX];
+ int cmd, major, minor;
+
+ pkg->fmtver_maj = 1;
+ pkg->fmtver_mnr = 0;
+ while (fgets(pline, FILENAME_MAX, fp)) {
+ int len = strlen(pline);
+
+ while (len && isspace(pline[len - 1]))
+ pline[--len] = '\0';
+ if (!len)
+ continue;
+ cp = pline;
+ if (pline[0] != CMD_CHAR) {
+ cmd = PLIST_FILE;
+ goto bottom;
+ }
+ cmd = plist_cmd(pline + 1, &cp);
+ if (cmd == FAIL) {
+ cleanup(0);
+ errx(2, "%s: bad command '%s'", __func__, pline);
+ }
+ if (*cp == '\0') {
+ cp = NULL;
+ goto bottom;
+ }
+ if (cmd == PLIST_COMMENT && sscanf(cp, "PKG_FORMAT_REVISION:%d.%d\n",
+ &major, &minor) == 2) {
+ pkg->fmtver_maj = major;
+ pkg->fmtver_mnr = minor;
+ if (verscmp(pkg, PLIST_FMT_VER_MAJOR, PLIST_FMT_VER_MINOR) <= 0)
+ goto bottom;
+
+ warnx("plist format revision (%d.%d) is higher than supported"
+ "(%d.%d)", pkg->fmtver_maj, pkg->fmtver_mnr,
+ PLIST_FMT_VER_MAJOR, PLIST_FMT_VER_MINOR);
+ if (pkg->fmtver_maj > PLIST_FMT_VER_MAJOR) {
+ cleanup(0);
+ exit(2);
+ }
+ }
+bottom:
+ add_plist(pkg, cmd, cp);
+ }
+}
+
+/* Write a packing list to a file, converting commands to ascii equivs */
+void
+write_plist(Package *pkg, FILE *fp)
+{
+ PackingList plist = pkg->head;
+
+ while (plist) {
+ switch(plist->type) {
+ case PLIST_FILE:
+ fprintf(fp, "%s\n", plist->name);
+ break;
+
+ case PLIST_CWD:
+ fprintf(fp, "%ccwd %s\n", CMD_CHAR, plist->name);
+ break;
+
+ case PLIST_SRC:
+ fprintf(fp, "%csrcdir %s\n", CMD_CHAR, plist->name);
+ break;
+
+ case PLIST_CMD:
+ fprintf(fp, "%cexec %s\n", CMD_CHAR, plist->name);
+ break;
+
+ case PLIST_UNEXEC:
+ fprintf(fp, "%cunexec %s\n", CMD_CHAR, plist->name);
+ break;
+
+ case PLIST_CHMOD:
+ fprintf(fp, "%cmode %s\n", CMD_CHAR, plist->name ? plist->name : "");
+ break;
+
+ case PLIST_CHOWN:
+ fprintf(fp, "%cowner %s\n", CMD_CHAR, plist->name ? plist->name : "");
+ break;
+
+ case PLIST_CHGRP:
+ fprintf(fp, "%cgroup %s\n", CMD_CHAR, plist->name ? plist->name : "");
+ break;
+
+ case PLIST_COMMENT:
+ fprintf(fp, "%ccomment %s\n", CMD_CHAR, plist->name);
+ break;
+
+ case PLIST_IGNORE:
+ case PLIST_IGNORE_INST: /* a one-time non-ignored file */
+ fprintf(fp, "%cignore\n", CMD_CHAR);
+ break;
+
+ case PLIST_NAME:
+ fprintf(fp, "%cname %s\n", CMD_CHAR, plist->name);
+ break;
+
+ case PLIST_DISPLAY:
+ fprintf(fp, "%cdisplay %s\n", CMD_CHAR, plist->name);
+ break;
+
+ case PLIST_PKGDEP:
+ fprintf(fp, "%cpkgdep %s\n", CMD_CHAR, plist->name);
+ break;
+
+ case PLIST_MTREE:
+ fprintf(fp, "%cmtree %s\n", CMD_CHAR, plist->name);
+ break;
+
+ case PLIST_DIR_RM:
+ fprintf(fp, "%cdirrm %s\n", CMD_CHAR, plist->name);
+ break;
+
+ case PLIST_OPTION:
+ fprintf(fp, "%coption %s\n", CMD_CHAR, plist->name);
+ break;
+
+ case PLIST_ORIGIN:
+ fprintf(fp, "%ccomment ORIGIN:%s\n", CMD_CHAR, plist->name);
+ break;
+
+ case PLIST_DEPORIGIN:
+ fprintf(fp, "%ccomment DEPORIGIN:%s\n", CMD_CHAR, plist->name);
+ break;
+
+ default:
+ cleanup(0);
+ errx(2, "%s: unknown command type %d (%s)", __func__,
+ plist->type, plist->name);
+ break;
+ }
+ plist = plist->next;
+ }
+}
+
+/*
+ * Delete the results of a package installation.
+ *
+ * This is here rather than in the pkg_delete code because pkg_add needs to
+ * run it too in cases of failure.
+ */
+int
+delete_package(Boolean ign_err, Boolean nukedirs, Package *pkg)
+{
+ PackingList p;
+ const char *Where = ".", *last_file = "";
+ Boolean fail = SUCCESS;
+ Boolean preserve;
+ char tmp[FILENAME_MAX], *name = NULL;
+
+ preserve = find_plist_option(pkg, "preserve") ? TRUE : FALSE;
+ for (p = pkg->head; p; p = p->next) {
+ switch (p->type) {
+ case PLIST_NAME:
+ name = p->name;
+ break;
+
+ case PLIST_IGNORE:
+ p = p->next;
+ break;
+
+ case PLIST_CWD:
+ Where = p->name;
+ if (Verbose)
+ printf("Change working directory to %s\n", Where);
+ break;
+
+ case PLIST_UNEXEC:
+ format_cmd(tmp, p->name, Where, last_file);
+ if (Verbose)
+ printf("Execute '%s'\n", tmp);
+ if (!Fake && system(tmp)) {
+ warnx("unexec command for '%s' failed", tmp);
+ fail = FAIL;
+ }
+ break;
+
+ case PLIST_FILE:
+ last_file = p->name;
+ sprintf(tmp, "%s/%s", Where, p->name);
+ if (isdir(tmp) && fexists(tmp) && !issymlink(tmp)) {
+ warnx("cannot delete specified file '%s' - it is a directory!\n"
+ "this packing list is incorrect - ignoring delete request", tmp);
+ }
+ else {
+ if (p->next && p->next->type == PLIST_COMMENT && !strncmp(p->next->name, "MD5:", 4)) {
+ char *cp = NULL, buf[33];
+
+ /*
+ * For packing lists whose version is 1.1 or greater, the md5
+ * hash for a symlink is calculated on the string returned
+ * by readlink().
+ */
+ if (issymlink(tmp) && verscmp(pkg, 1, 0) > 0) {
+ int len;
+ char linkbuf[FILENAME_MAX];
+
+ if ((len = readlink(tmp, linkbuf, FILENAME_MAX)) > 0)
+ cp = MD5Data((unsigned char *)linkbuf, len, buf);
+ } else if (isfile(tmp) || verscmp(pkg, 1, 1) < 0)
+ cp = MD5File(tmp, buf);
+
+ if (cp != NULL) {
+ /* Mismatch? */
+ if (strcmp(cp, p->next->name + 4)) {
+ warnx("'%s' fails original MD5 checksum - %s",
+ tmp, Force ? "deleted anyway." : "not deleted.");
+ if (!Force) {
+ fail = FAIL;
+ continue;
+ }
+ }
+ }
+ }
+ if (Verbose)
+ printf("Delete file %s\n", tmp);
+ if (!Fake) {
+ if (delete_hierarchy(tmp, ign_err, nukedirs))
+ fail = FAIL;
+ if (preserve && name) {
+ char tmp2[FILENAME_MAX];
+
+ if (make_preserve_name(tmp2, FILENAME_MAX, name, tmp)) {
+ if (fexists(tmp2)) {
+ if (rename(tmp2, tmp))
+ warn("preserve: unable to restore %s as %s",
+ tmp2, tmp);
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ case PLIST_DIR_RM:
+ sprintf(tmp, "%s/%s", Where, p->name);
+ if (!isdir(tmp) && fexists(tmp)) {
+ warnx("cannot delete specified directory '%s' - it is a file!\n"
+ "this packing list is incorrect - ignoring delete request", tmp);
+ }
+ else {
+ if (Verbose)
+ printf("Delete directory %s\n", tmp);
+ if (!Fake && delete_hierarchy(tmp, ign_err, FALSE)) {
+ warnx("unable to completely remove directory '%s'", tmp);
+ fail = FAIL;
+ }
+ }
+ last_file = p->name;
+ break;
+
+ default:
+ break;
+ }
+ }
+ return fail;
+}
+
+#ifdef DEBUG
+#define RMDIR(dir) vsystem("%s %s", RMDIR_CMD, dir)
+#define REMOVE(dir,ie) vsystem("%s %s%s", REMOVE_CMD, (ie ? "-f " : ""), dir)
+#else
+#define RMDIR rmdir
+#define REMOVE(file,ie) (remove(file) && !(ie))
+#endif
+
+/* Selectively delete a hierarchy */
+int
+delete_hierarchy(const char *dir, Boolean ign_err, Boolean nukedirs)
+{
+ char *cp1, *cp2;
+
+ cp1 = cp2 = strdup(dir);
+ if (!fexists(dir)) {
+ if (!ign_err)
+ warnx("%s '%s' doesn't really exist",
+ isdir(dir) ? "directory" : "file", dir);
+ return !ign_err;
+ }
+ else if (nukedirs) {
+ if (vsystem("%s -r%s %s", REMOVE_CMD, (ign_err ? "f" : ""), dir))
+ return 1;
+ }
+ else if (isdir(dir) && !issymlink(dir)) {
+ if (RMDIR(dir) && !ign_err)
+ return 1;
+ }
+ else {
+ if (REMOVE(dir, ign_err))
+ return 1;
+ }
+
+ if (!nukedirs)
+ return 0;
+ while (cp2) {
+ if ((cp2 = strrchr(cp1, '/')) != NULL)
+ *cp2 = '\0';
+ if (!isemptydir(dir))
+ return 0;
+ if (RMDIR(dir) && !ign_err) {
+ if (!fexists(dir))
+ warnx("directory '%s' doesn't really exist", dir);
+ else
+ return 1;
+ }
+ /* back up the pathname one component */
+ if (cp2) {
+ cp1 = strdup(dir);
+ }
+ }
+ return 0;
+}
diff --git a/usr.sbin/pkg_install/lib/str.c b/usr.sbin/pkg_install/lib/str.c
new file mode 100644
index 0000000..8f6aec3
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/str.c
@@ -0,0 +1,108 @@
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * Miscellaneous string utilities.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "lib.h"
+
+char *
+strconcat(const char *s1, const char *s2)
+{
+ static char tmp[FILENAME_MAX];
+
+ tmp[0] = '\0';
+ strncpy(tmp, s1 ? s1 : s2, FILENAME_MAX); /* XXX: what if both are NULL? */
+ if (s1 && s2)
+ strncat(tmp, s2, FILENAME_MAX - strlen(tmp));
+ return tmp;
+}
+
+/* Get a string parameter as a file spec or as a "contents follow -" spec */
+char *
+get_dash_string(char **str)
+{
+ char *s = *str;
+
+ if (*s == '-')
+ *str = copy_string(s + 1);
+ else
+ *str = fileGetContents(s);
+ return *str;
+}
+
+/* Rather Obvious */
+char *
+copy_string(const char *str)
+{
+ return (str ? strdup(str) : NULL);
+}
+
+/* Return TRUE if 'str' ends in suffix 'suff' */
+Boolean
+suffix(const char *str, const char *suff)
+{
+ char *idx;
+ Boolean ret = FALSE;
+
+ idx = strrchr(str, '.');
+ if (idx && !strcmp(idx + 1, suff))
+ ret = TRUE;
+ return ret;
+}
+
+/* Assuming str has a suffix, brutally murder it! */
+void
+nuke_suffix(char *str)
+{
+ char *idx;
+
+ idx = strrchr(str, '.');
+ if (idx)
+ *idx = '\0'; /* Yow! Don't try this on a const! */
+}
+
+/* Lowercase a whole string */
+void
+str_lowercase(char *str)
+{
+ while (*str) {
+ *str = tolower(*str);
+ ++str;
+ }
+}
+
+char *
+get_string(char *str, int max, FILE *fp)
+{
+ int len;
+
+ if (!str)
+ return NULL;
+ str[0] = '\0';
+ while (fgets(str, max, fp)) {
+ len = strlen(str);
+ while (len && isspace(str[len - 1]))
+ str[--len] = '\0';
+ if (len)
+ return str;
+ }
+ return NULL;
+}
diff --git a/usr.sbin/pkg_install/lib/version.c b/usr.sbin/pkg_install/lib/version.c
new file mode 100644
index 0000000..8241961
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/version.c
@@ -0,0 +1,171 @@
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Maxim Sobolev
+ * 31 July 2001
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "lib.h"
+#include <err.h>
+
+/*
+ * Routines to assist with PLIST_FMT_VER numbers in the packing
+ * lists.
+ *
+ * Following is the PLIST_FMT_VER history:
+ * 1.0 - Initial revision;
+ * 1.1 - When recording/checking checksum of symlink use hash of readlink()
+ * value insted of the hash of an object this links points to.
+ *
+ */
+int
+verscmp(Package *pkg, int major, int minor)
+{
+ int rval = 0;
+
+ if ((pkg->fmtver_maj < major) || (pkg->fmtver_maj == major &&
+ pkg->fmtver_mnr < minor))
+ rval = -1;
+ else if ((pkg->fmtver_maj > major) || (pkg->fmtver_maj == major &&
+ pkg->fmtver_mnr > minor))
+ rval = 1;
+
+ return rval;
+}
+
+/*
+ * version_of(pkgname, epoch, revision) returns a pointer to the version
+ * portion of a package name and the two special components.
+ *
+ * Jeremy D. Lea.
+ */
+const char *
+version_of(const char *pkgname, int *epoch, int *revision)
+{
+ char *ch;
+
+ if (pkgname == NULL)
+ errx(2, "%s: Passed NULL pkgname.", __func__);
+ if (epoch != NULL) {
+ if ((ch = strrchr(pkgname, ',')) == NULL)
+ *epoch = 0;
+ else
+ *epoch = atoi(&ch[1]);
+ }
+ if (revision != NULL) {
+ if ((ch = strrchr(pkgname, '_')) == NULL)
+ *revision = 0;
+ else
+ *revision = atoi(&ch[1]);
+ }
+ /* Cheat if we are just passed a version, not a valid package name */
+ if ((ch = strrchr(pkgname, '-')) == NULL)
+ return pkgname;
+ else
+ return &ch[1];
+}
+
+/*
+ * version_cmp(pkg1, pkg2) returns -1, 0 or 1 depending on if the version
+ * components of pkg1 is less than, equal to or greater than pkg2. No
+ * comparision of the basenames is done.
+ *
+ * The port verison is defined by:
+ * ${PORTVERSION}[_${PORTREVISION}][,${PORTEPOCH}]
+ * ${PORTEPOCH} supercedes ${PORTVERSION} supercedes ${PORTREVISION}.
+ * See the commit log for revision 1.349 of ports/Mk/bsd.port.mk
+ * for more information.
+ *
+ * The epoch and revision are defined to be a single number, while the rest
+ * of the version should conform to the porting guidelines. It can contain
+ * multiple components, seperated by a period, including letters.
+ *
+ * The tests below allow for significantly more latitude in the version
+ * numbers than is allowed in the guidelines. No point in wasting user's
+ * time enforcing them here. That's what flamewars are for.
+ *
+ * Jeremy D. Lea.
+ */
+int
+version_cmp(const char *pkg1, const char *pkg2)
+{
+ const char *c1, *c2, *v1, *v2;
+ char *t1, *t2;
+ int e1, e2, r1, r2, n1, n2;
+
+ v1 = version_of(pkg1, &e1, &r1);
+ v2 = version_of(pkg2, &e2, &r2);
+ /* Minor optimisation. */
+ if (strcmp(v1, v2) == 0)
+ return 0;
+ /* First compare epoch. */
+ if (e1 != e2)
+ return (e1 < e2 ? -1 : 1);
+ else {
+ /*
+ * We walk down the versions, trying to convert to numbers.
+ * We terminate when we reach an underscore, a comma or the
+ * string terminator, thanks to a nasty trick with strchr().
+ * strtol() conveniently gobbles up the chars it converts.
+ */
+ c1 = strchr("_,", v1[0]);
+ c2 = strchr("_,", v2[0]);
+ while (c1 == NULL && c2 == NULL) {
+ n1 = strtol(v1, &t1, 10);
+ n2 = strtol(v2, &t2, 10);
+ if (n1 != n2)
+ return (n1 < n2 ? -1 : 1);
+ /*
+ * The numbers are equal, check for letters. Assume they're
+ * letters purely because strtol() didn't chomp them.
+ */
+ c1 = strchr("_,.", t1[0]);
+ c2 = strchr("_,.", t2[0]);
+ if (c1 == NULL && c2 == NULL) {
+ /* Both have letters. Compare them. */
+ if (t1[0] != t2[0])
+ return (t1[0] < t2[0] ? -1 : 1);
+ /* Boring. The letters are equal. Carry on. */
+ v1 = &t1[1], v2 = &t2[1];
+ } else if (c1 == NULL) {
+ /*
+ * Letters are strange. After a number, a letter counts
+ * as greater, but after a period it's less.
+ */
+ return (isdigit(v1[0]) ? 1 : -1);
+ } else if (c2 == NULL) {
+ return (isdigit(v2[0]) ? -1 : 1);
+ } else {
+ /* Neither were letters. Advance over the period. */
+ v1 = (t1[0] == '.' ? &t1[1] : t1);
+ v2 = (t2[0] == '.' ? &t2[1] : t2);
+ }
+ c1 = strchr("_,", v1[0]);
+ c2 = strchr("_,", v2[0]);
+ }
+ /* If we got here, check if one version has something left. */
+ if (c1 == NULL)
+ return (isdigit(v1[0]) ? 1 : -1);
+ if (c2 == NULL)
+ return (isdigit(v2[0]) ? -1 : 1);
+ /* We've run out of version. Try the revision... */
+ if (r1 != r2)
+ return (r1 < r2 ? -1 : 1);
+ else
+ return 0;
+ }
+}
diff --git a/usr.sbin/pkg_install/sign/Makefile b/usr.sbin/pkg_install/sign/Makefile
new file mode 100644
index 0000000..a7b354c
--- /dev/null
+++ b/usr.sbin/pkg_install/sign/Makefile
@@ -0,0 +1,14 @@
+# $FreeBSD$
+# $OpenBSD: Makefile.bsd-wrapper,v 1.2 1999/10/07 16:30:32 espie Exp $
+
+PROG= pkg_sign
+LINKS= ${BINDIR}/pkg_sign ${BINDIR}/pkg_check
+MLINKS= pkg_sign.1 pkg_check.1
+SRCS= main.c check.c common.c gzip.c pgp_check.c pgp_sign.c \
+ sha1.c sign.c stand.c x509.c
+
+DPADD= ${LIBINSTALL} ${LIBCRYPTO}
+LDADD= ${LIBINSTALL} -lcrypto
+
+.include <bsd.prog.mk>
+
diff --git a/usr.sbin/pkg_install/sign/README b/usr.sbin/pkg_install/sign/README
new file mode 100644
index 0000000..8e81fcd
--- /dev/null
+++ b/usr.sbin/pkg_install/sign/README
@@ -0,0 +1,55 @@
+To sign packages in a transparent way:
+gzip files can handle an extra field at the beginning that
+stores anything we wish.
+
+So it's just a question to choose a format for the signature, and to
+embed it there.
+
+We use the extra field to store signatures. Each signature consists
+of a 6 bytes type marker, a 2 bytes length, followed by the signature
+itself. We can potentially stack signatures: resign a signed archive
+by just prepending the new signature to the extra field.
+
+To check the first signature, the checker just needs to extract it, pass it
+off to the checking protocol (e.g. PGP), followed by the unsigned archive
+(e.g., regenerate the gzip header without the first signature, then put
+the gzip data).
+
+* Signed archives just look like normal .tar.gz files, except for programs
+that use the extra field for their own purpose,
+* Possibility to grab the files off the net and extract stuff/verify
+signatures on the fly (just need to wedge the checker as an intermediate
+pipe)
+* Pretty simple, small portable code to be able to check signatures
+everywhere (the signer itself needs getpass and corresponding functionality)
+
+The scheme should be extensible to any compressed format which allows for
+extended headers.
+
+
+Thanks to Angelos D. Keromytis for pointing out I did not need to
+uncompress the archive to sign it, and to other members of the OpenBSD
+project for various reasons.
+
+--
+ Marc Espie, 1999
+ $OpenBSD: README,v 1.2 1999/10/04 21:46:27 espie Exp $
+
+--
+
+X.509 notes:
+
+I added the ability to sign a package with an X.509 key, and to check
+against a stack of X.509 certificates. This allows a "vendor" to
+distribute a system with one or more certificates pre-installed, and
+to add certificates in a signed package by appending them to the
+default certficiate stack.
+
+The X.509 signatures are stored in the gzip header in the same manner
+as other signatures. This is known to compile against OpenSSL
+libraries on OpenBSD 2.7 and FreeBSD 5.0, your mileage may vary.
+
+--
+
+ Wes Peters, Dec 2000
+ $FreeBSD$
diff --git a/usr.sbin/pkg_install/sign/check.c b/usr.sbin/pkg_install/sign/check.c
new file mode 100644
index 0000000..cfc3bfa
--- /dev/null
+++ b/usr.sbin/pkg_install/sign/check.c
@@ -0,0 +1,119 @@
+/* $OpenBSD: check.c,v 1.2 1999/10/04 21:46:27 espie Exp $ */
+/*-
+ * Copyright (c) 1999 Marc Espie.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Marc Espie for the OpenBSD
+ * Project.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENBSD
+ * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/* Simple code for a stand-alone package checker */
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "stand.h"
+#include "pgp.h"
+#include "gzip.h"
+#include "extern.h"
+
+struct checker {
+ void *context;
+ void (*add)(void *, const char *, size_t);
+ int (*get)(void *);
+ int status;
+};
+
+#define MAX_CHECKERS 20
+
+int
+check_signature(file, userid, envp, filename)
+ /*@dependent@*/FILE *file;
+ const char *userid;
+ char *envp[];
+ /*@observer@*/const char *filename;
+{
+ struct signature *sign;
+ struct mygzip_header h;
+ int status;
+ char buffer[1024];
+ size_t length;
+ struct checker checker[MAX_CHECKERS];
+ struct signature *sweep;
+ int i, j;
+
+ status = read_header_and_diagnose(file, &h, &sign, filename);
+ if (status != 1)
+ return PKG_UNSIGNED;
+
+ for (sweep = sign, i = 0;
+ sweep != NULL && i < MAX_CHECKERS;
+ sweep=sweep->next, i++) {
+ switch(sweep->type) {
+ case TAG_OLD:
+ fprintf(stderr, "File %s uses old signatures, no longer supported\n",
+ filename);
+ checker[i].context = NULL;
+ break;
+ case TAG_X509:
+ checker[i].context = new_x509_checker(&h, sweep, userid, envp, filename);
+ checker[i].add = x509_add;
+ checker[i].get = x509_sign_ok;
+ break;
+ case TAG_SHA1:
+ checker[i].context = new_sha1_checker(&h, sweep, userid, envp, filename);
+ checker[i].add = sha1_add;
+ checker[i].get = sha1_sign_ok;
+ break;
+ case TAG_PGP:
+ checker[i].context = new_pgp_checker(&h, sweep, userid, envp, filename);
+ checker[i].add = pgp_add;
+ checker[i].get = pgp_sign_ok;
+ break;
+ default:
+ abort();
+ }
+ }
+ while ((length = fread(buffer, 1, sizeof buffer, file)) > 0) {
+ for (j = 0; j < i; j++) {
+ if (checker[j].context) {
+ (*checker[j].add)(checker[j].context, buffer, length);
+ }
+ }
+ }
+// for (j = i-1; j >= 0; j--)
+ for (j = 0; j < i; j++) {
+ if (checker[j].context) {
+ checker[j].status = (*checker[j].get)(checker[j].context);
+ } else {
+ checker[j].status = PKG_SIGERROR;
+ }
+ }
+ free_signature(sign);
+ return checker[0].status;
+}
+
diff --git a/usr.sbin/pkg_install/sign/common.c b/usr.sbin/pkg_install/sign/common.c
new file mode 100644
index 0000000..ec32357
--- /dev/null
+++ b/usr.sbin/pkg_install/sign/common.c
@@ -0,0 +1,90 @@
+/* $OpenBSD: common.c,v 1.3 1999/10/07 16:30:32 espie Exp $ */
+/*-
+ * Copyright (c) 1999 Marc Espie.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Marc Espie for the OpenBSD
+ * Project.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENBSD
+ * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include "stand.h"
+#include "gzip.h"
+#include "pgp.h"
+#include "extern.h"
+
+/* Ensure consistent diagnostics */
+int
+read_header_and_diagnose(file, h, sign, filename)
+ FILE *file;
+ struct mygzip_header *h;
+ struct signature **sign;
+ const char *filename;
+{
+ switch(gzip_read_header(file, h, sign)) {
+ case GZIP_SIGNED:
+ if (sign == NULL) {
+ fprintf(stderr, "File %s is already signed\n", filename);
+ return 0;
+ } else
+ return 1;
+ case GZIP_UNSIGNED:
+ if (sign != NULL) {
+ fprintf(stderr, "File %s is not a signed gzip file\n", filename);
+ return 0;
+ } else
+ return 1;
+ case GZIP_NOT_GZIP:
+ fprintf(stderr, "File %s is not a gzip file\n", filename);
+ return 0;
+ case GZIP_NOT_PGPSIGNED:
+ fprintf(stderr, "File %s contains an unknown extension\n", filename);
+ return 0;
+ default:
+ /* this should not happen */
+ abort();
+ }
+}
+
+int
+reap(pid)
+ pid_t pid;
+{
+ int pstat;
+ pid_t result;
+
+ do {
+ result = waitpid(pid, &pstat, 0);
+ } while (result == -1 && errno == EINTR);
+ return result == -1 ? -1 : pstat;
+}
+
diff --git a/usr.sbin/pkg_install/sign/extern.h b/usr.sbin/pkg_install/sign/extern.h
new file mode 100644
index 0000000..e7c0076
--- /dev/null
+++ b/usr.sbin/pkg_install/sign/extern.h
@@ -0,0 +1,100 @@
+/* $FreeBSD$ */
+/* $OpenBSD: extern.h,v 1.3 1999/10/07 16:30:32 espie Exp $ */
+/*-
+ * Copyright (c) 1999 Marc Espie.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Marc Espie for the OpenBSD
+ * Project.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENBSD
+ * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Convention: all functions that operate on a FILE * also take a filename
+ * for diagnostic purposes. The file can be connected to a pipe, so
+ * - don't rewind
+ * - don't reopen from filename.
+ */
+
+struct mygzip_header;
+struct signature;
+
+/* main.c */
+extern int verbose;
+extern int quiet;
+extern char *userkey;
+
+/* common.c */
+extern int read_header_and_diagnose __P((FILE *file, \
+ /*@out@*/struct mygzip_header *h, /*@null@*/struct signature **sign, \
+ const char *filename));
+extern int reap __P((pid_t pid));
+
+/* sign.c */
+extern int sign __P((/*@observer@*/const char *filename, int type, \
+ /*@null@*/const char *userid, char *envp[]));
+
+/* check.c */
+extern int check_signature __P((/*@dependent@*/FILE *file, \
+ /*@null@*/const char *userid, char *envp[], \
+ /*@observer@*/const char *filename));
+
+#define PKG_BADSIG 0
+#define PKG_GOODSIG 1
+#define PKG_UNSIGNED 2
+#define PKG_SIGNED 4
+#define PKG_SIGERROR 8
+#define PKG_SIGUNKNOWN 16
+
+typedef /*@observer@*/char *pchar;
+
+#define MAXID 512
+/* sha1.c */
+#define SHA1_DB_NAME "/var/db/pkg/SHA1"
+
+extern void *new_sha1_checker __P((struct mygzip_header *h, \
+ struct signature *sign, const char *userid, char *envp[], \
+ const char *filename));
+
+extern void sha1_add __P((void *arg, const char *buffer, \
+ size_t length));
+
+extern int sha1_sign_ok __P((void *arg));
+
+extern int retrieve_sha1_marker __P((const char *filename, \
+ struct signature **sign, const char *userid));
+
+/* x509.c */
+#define X509_DB_NAME "/var/db/pkg/X509"
+
+extern void *new_x509_checker __P((struct mygzip_header *h, \
+ struct signature *sign, const char *userid, char *envp[], \
+ const char *filename));
+
+extern void x509_add __P((void *arg, const char *buffer, \
+ size_t length));
+
+extern int x509_sign_ok __P((void *arg));
+
+extern int retrieve_x509_marker __P((const char *filename, \
+ struct signature **sign, const char *userid));
diff --git a/usr.sbin/pkg_install/sign/gzip.c b/usr.sbin/pkg_install/sign/gzip.c
new file mode 100644
index 0000000..33d9bae
--- /dev/null
+++ b/usr.sbin/pkg_install/sign/gzip.c
@@ -0,0 +1,319 @@
+/* $OpenBSD: gzip.c,v 1.3 1999/10/04 21:46:28 espie Exp $ */
+/*-
+ * Copyright (c) 1999 Marc Espie.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Marc Espie for the OpenBSD
+ * Project.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENBSD
+ * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include "stand.h"
+#include "gzip.h"
+#include "pgp.h"
+
+/*
+ * Signatures follow a simple format
+ * (endianess was chosen to conform to gzip header format)
+ */
+
+SIGNTAG known_tags[KNOWN_TAGS] = {
+ {'S', 'I', 'G', 'P', 'G', 'P', 0, 0 },
+ {'C', 'K', 'S', 'H', 'A', '1', 0, 0 },
+ {'C', 'R', 'X', '5', '0', '9', 0, 0 },
+ {'S', 'i', 'g', 'P', 'G', 'P', 0, 0 } /* old format */
+};
+
+void
+sign_fill_tag(sign)
+ struct signature *sign;
+{
+ sign->tag[6] = sign->length % 256;
+ sign->tag[7] = sign->length / 256;
+}
+
+void
+sign_fill_length(sign)
+ struct signature *sign;
+{
+ sign->length = sign->tag[6] + 256 * sign->tag[7];
+}
+
+static size_t
+stack_sign(match, t, f, sign)
+ SIGNTAG match;
+ int t;
+ FILE *f;
+ struct signature **sign;
+{
+ struct signature *new_sign;
+ size_t length;
+
+ new_sign = malloc(sizeof *new_sign);
+ if (new_sign == NULL)
+ return 0;
+ new_sign->type = t;
+ new_sign->next = NULL;
+ memcpy(new_sign->tag, match, sizeof(SIGNTAG));
+ sign_fill_length(new_sign);
+ new_sign->data = malloc(new_sign->length);
+ if (new_sign->data == NULL ||
+ fread(new_sign->data, 1, new_sign->length, f) != new_sign->length) {
+ free_signature(new_sign);
+ return 0;
+ }
+ length = new_sign->length;
+ if (sign != NULL) {
+ if (!*sign)
+ *sign = new_sign;
+ else {
+ while ((*sign)->next != NULL)
+ sign = &((*sign)->next);
+ (*sign)->next = new_sign;
+ }
+ } else
+ free_signature(new_sign);
+ return length;
+}
+
+
+static int
+add_sign(f, sign)
+ FILE *f;
+ struct signature **sign;
+{
+ SIGNTAG match;
+ int i;
+
+ if (fread(match, 1, sizeof(SIGNTAG), f) != sizeof(SIGNTAG))
+ return -1;
+ for (i = 0; i < KNOWN_TAGS; i++) {
+ if (memcmp(match, known_tags[i], TAGCHECK) == 0) {
+ unsigned int sign_length = stack_sign(match, i, f, sign);
+ if (sign_length > 0)
+ return sign_length + sizeof(SIGNTAG);
+ else
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int
+gzip_magic(f)
+ FILE *f;
+{
+ int c, d;
+
+ c = fgetc(f);
+ d = fgetc(f);
+ if ((unsigned char)c != (unsigned char)GZIP_MAGIC0
+ || (unsigned char)d != (unsigned char)GZIP_MAGIC1)
+ return 0;
+ else
+ return 1;
+}
+
+static int
+fill_gzip_fields(f, h)
+ FILE *f;
+ struct mygzip_header *h;
+{
+ int method, flags;
+
+ method = fgetc(f);
+ flags = fgetc(f);
+
+ if (method == EOF || flags == EOF || fread(h->stamp, 1, 6, f) != 6)
+ return 0;
+ h->method = (char)method;
+ h->flags = (char)flags;
+ if ((h->flags & CONTINUATION) != 0)
+ if (fread(h->part, 1, 2, f) != 2)
+ return 0;
+ return 1;
+}
+
+/* retrieve a gzip header, including signatures */
+int
+gzip_read_header(f, h, sign)
+ FILE *f;
+ struct mygzip_header *h;
+ struct signature **sign;
+{
+ if (sign != NULL)
+ *sign = NULL;
+ if (!gzip_magic(f) || !fill_gzip_fields(f, h))
+ return GZIP_NOT_GZIP;
+
+ if ((h->flags & EXTRA_FIELD) == 0) {
+ h->remaining = 0;
+ return GZIP_UNSIGNED;
+ }
+ else {
+ int c;
+
+ c = fgetc(f);
+ if (c == EOF)
+ return GZIP_NOT_GZIP;
+ h->remaining = (unsigned)c;
+ c = fgetc(f);
+ if (c == EOF)
+ return GZIP_NOT_PGPSIGNED;
+ h->remaining += ((unsigned) c) << 8;
+ while (h->remaining >= sizeof(SIGNTAG)) {
+ int sign_length = add_sign(f, sign);
+ if (sign_length > 0)
+ h->remaining -= sign_length;
+ if (sign_length < 0)
+ return GZIP_NOT_GZIP;
+ if (sign_length == 0)
+ return GZIP_SIGNED;
+ }
+ return GZIP_SIGNED;
+ }
+}
+
+static unsigned
+sign_length(sign)
+ struct signature *sign;
+{
+ unsigned total = 0;
+
+ while (sign != NULL) {
+ total += sizeof(SIGNTAG) + sign->length;
+ sign = sign->next;
+ }
+ return total;
+}
+
+struct mydata {
+ FILE *file;
+ int ok;
+};
+
+static void myadd(arg, buffer, size)
+ void *arg;
+ const char *buffer;
+ size_t size;
+{
+ struct mydata *d = arg;
+
+ if (fwrite(buffer, 1, size, d->file) == size)
+ d->ok = 1;
+ else
+ d->ok = 0;
+}
+
+/* write a gzip header, including signatures */
+int
+gzip_write_header(f, h, sign)
+ FILE *f;
+ const struct mygzip_header *h;
+ struct signature *sign;
+{
+ struct mydata d;
+ d.file = f;
+ if (gzip_copy_header(h, sign, myadd, &d) == 0)
+ return 0;
+ return d.ok;
+}
+
+int
+gzip_copy_header(h, sign, add, data)
+ const struct mygzip_header *h;
+ struct signature *sign;
+ void (*add)(void *, const char *, size_t);
+ void *data;
+{
+ char flags;
+ size_t length;
+ size_t buflength;
+ size_t i;
+ char *buffer;
+
+ length = h->remaining + sign_length(sign);
+ if (length) {
+ buflength = length + 2;
+ flags = h->flags | EXTRA_FIELD;
+ } else {
+ flags = h->flags & ~EXTRA_FIELD;
+ buflength = 0;
+ }
+ buflength += 10;
+ if ((h->flags & CONTINUATION) != 0)
+ buflength += 2;
+
+ buffer = malloc(buflength);
+ if (buffer == NULL)
+ return 0;
+
+ i = 0;
+ buffer[i++] = GZIP_MAGIC0;
+ buffer[i++] = GZIP_MAGIC1;
+ buffer[i++] = h->method;
+ buffer[i++] = flags;
+ memcpy(buffer+i, h->stamp, 6);
+ i += 6;
+ if ((flags & CONTINUATION) != 0) {
+ memcpy(buffer+i, h->part, 2);
+ i += 2;
+ }
+ if (length) {
+ buffer[i++] = (char)(length % 256);
+ buffer[i++] = (char)(length / 256);
+ while (sign != NULL) {
+ memcpy(buffer+i, sign->tag, sizeof(SIGNTAG));
+ i += sizeof(SIGNTAG);
+ memcpy(buffer+i, sign->data, sign->length);
+ i += sign->length;
+ sign = sign->next;
+ }
+ }
+ (*add)(data, buffer, buflength);
+ free(buffer);
+ return 1;
+}
+
+void
+free_signature(sign)
+ struct signature *sign;
+{
+ struct signature *next;
+
+ while (sign != NULL) {
+ next = sign->next;
+ free(sign->data);
+ free(sign);
+ sign = next;
+ }
+}
diff --git a/usr.sbin/pkg_install/sign/gzip.h b/usr.sbin/pkg_install/sign/gzip.h
new file mode 100644
index 0000000..238010f
--- /dev/null
+++ b/usr.sbin/pkg_install/sign/gzip.h
@@ -0,0 +1,95 @@
+/* $FreeBSD$ */
+/* $OpenBSD: gzip.h,v 1.2 1999/10/04 21:46:28 espie Exp $ */
+/*-
+ * Copyright (c) 1999 Marc Espie.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Marc Espie for the OpenBSD
+ * Project.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENBSD
+ * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define GZIP_MAGIC0 '\037'
+#define GZIP_MAGIC1 '\213'
+/* flags values */
+#define CONTINUATION 0x02
+#define EXTRA_FIELD 0x04
+
+/*
+ * Meaningful fields in a gzip header, see gzip proper for details.
+ * This structure should not be fiddled with outside of gzip_read_header
+ * and gzip_write_header
+ */
+struct mygzip_header {
+ char method;
+ char flags;
+ char stamp[6];
+ char part[2];
+ /* remaining extra, after know signs have been read */
+ unsigned int remaining;
+};
+
+#define TAGSIZE 8
+#define TAGCHECK 6
+
+typedef unsigned char SIGNTAG[8];
+
+/* stack of signatures */
+struct signature {
+ SIGNTAG tag;
+ int type;
+ int length;
+ char *data;
+ struct signature *next;
+};
+
+/* returns from gzip_read_header */
+#define GZIP_UNSIGNED 0 /* gzip file, no signature */
+#define GZIP_SIGNED 1 /* gzip file, signature parsed ok */
+#define GZIP_NOT_GZIP 2 /* not a proper gzip file */
+#define GZIP_NOT_PGPSIGNED 3 /* gzip file, unknown extension */
+extern int gzip_read_header __P((FILE *f, /*@out@*/struct mygzip_header *h, \
+ /*@null@*/struct signature **sign));
+/* gzip_write_header returns 1 for success */
+extern int gzip_write_header __P((FILE *f, const struct mygzip_header *h, \
+ /*@null@*/struct signature *sign));
+/*
+ * Writing header to memory. Returns size needed, or 0 if buffer too small
+ * buffer must be at least 14 characters
+ */
+extern int gzip_copy_header __P((const struct mygzip_header *h, \
+ /*@null@*/struct signature *sign, \
+ void (*add)(void *, const char *, size_t), void *data));
+
+extern void free_signature __P((/*@null@*/struct signature *sign));
+extern void sign_fill_tag __P((struct signature *sign));
+#define KNOWN_TAGS 4
+#define TAG_PGP 0
+#define TAG_SHA1 1
+#define TAG_X509 2
+#define TAG_OLD 3
+#define TAG_ANY -1
+#define pgptag (known_tags[TAG_PGP])
+#define sha1tag (known_tags[TAG_SHA1])
+#define x509tag (known_tags[TAG_X509])
+extern SIGNTAG known_tags[KNOWN_TAGS];
diff --git a/usr.sbin/pkg_install/sign/main.c b/usr.sbin/pkg_install/sign/main.c
new file mode 100644
index 0000000..c5068b7
--- /dev/null
+++ b/usr.sbin/pkg_install/sign/main.c
@@ -0,0 +1,185 @@
+/* $OpenBSD: main.c,v 1.2 1999/10/04 21:46:28 espie Exp $ */
+/*-
+ * Copyright (c) 1999 Marc Espie.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Marc Espie for the OpenBSD
+ * Project.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENBSD
+ * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include "stand.h"
+#include "gzip.h"
+#include "pgp.h"
+#include "extern.h"
+
+#ifdef __OpenBSD__
+extern char *__progname;
+#define argv0 __progname
+#else
+static char *argv0;
+#endif
+
+#define NM_SIGN "pkg_sign"
+
+int verbose = 0;
+int quiet = 0;
+char *userkey = NULL;
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: %s [-sc] [-t type] [-u userid] [-k keyfile] pkg1 ...\n", argv0);
+ exit(EXIT_FAILURE);
+}
+
+#define SIGN 0
+#define CHECK 1
+
+/* wrapper for the check_signature function (open file if needed) */
+static int
+check(filename, type, userid, envp)
+ /*@observer@*/const char *filename;
+ int type;
+ /*@null@*/const char *userid;
+ char *envp[];
+{
+ int result;
+ FILE *file;
+
+ if (strcmp(filename, "-") == 0)
+ return check_signature(stdin, userid, envp, "stdin");
+ file = fopen(filename, "r");
+ if (file == NULL) {
+ fprintf(stderr, "Can't open %s\n", filename);
+ return 0;
+ }
+ result = check_signature(file, userid, envp, filename);
+ if (fclose(file) == 0) {
+ if (result == PKG_BADSIG || result == PKG_SIGERROR)
+ return 0;
+ else
+ return 1;
+ } else
+ return 0;
+}
+
+int
+main(argc, argv, envp)
+ int argc;
+ char *argv[];
+ char *envp[];
+{
+ int success = 1;
+ int ch;
+ char *userid = NULL;
+ int mode;
+ int i;
+ int type = TAG_ANY;
+
+/* #ifndef BSD4_4 */
+ set_program_name(argv[0]);
+/* #endif */
+#ifdef CHECKER_ONLY
+ mode = CHECK;
+#else
+#ifndef __OpenBSD__
+ if ((argv0 = strrchr(argv[0], '/')) != NULL)
+ argv0++;
+ else
+ argv0 = argv[0];
+#endif
+ if (strcmp(argv0, NM_SIGN) == 0)
+ mode = SIGN;
+ else
+ mode = CHECK;
+#endif
+
+ while ((ch = getopt(argc, argv, "t:u:k:qscv")) != -1) {
+ switch(ch) {
+ case 't':
+ if (strcmp(optarg, "pgp") == 0)
+ type = TAG_PGP;
+ else if (strcmp(optarg, "sha1") == 0)
+ type = TAG_SHA1;
+ else if (strcmp(optarg, "x509") == 0)
+ type = TAG_X509;
+ else
+ usage();
+ break;
+ case 'u':
+ userid = strdup(optarg);
+ break;
+
+ case 'k':
+ userkey = optarg;
+ break;
+
+ case 'q':
+ quiet = 1;
+ break;
+
+#ifndef CHECKER_ONLY
+ case 's':
+ mode = SIGN;
+ break;
+#endif
+ case 'c':
+ mode = CHECK;
+ break;
+
+ case 'v':
+ verbose = 1;
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc == 0) {
+ if (mode == CHECK)
+ success &= check("-", 0, userid, envp);
+ else
+ usage();
+ }
+
+#ifndef CHECKER_ONLY
+ if (mode == SIGN && type == TAG_ANY)
+ type = TAG_PGP;
+ if (mode == SIGN && type == TAG_PGP)
+ handle_pgp_passphrase();
+#endif
+ for (i = 0; i < argc; i++)
+ success &= (mode == SIGN ? sign : check)(argv[i], type, userid, envp);
+ exit(success == 1 ? EXIT_SUCCESS : EXIT_FAILURE);
+}
diff --git a/usr.sbin/pkg_install/sign/pgp.h b/usr.sbin/pkg_install/sign/pgp.h
new file mode 100644
index 0000000..99476f2
--- /dev/null
+++ b/usr.sbin/pkg_install/sign/pgp.h
@@ -0,0 +1,25 @@
+/* $FreeBSD$ */
+/* $OpenBSD: pgp.h,v 1.2 1999/10/04 21:46:28 espie Exp $ */
+/* Estimate size of pgp signature */
+#define MAXPGPSIGNSIZE 1024
+
+#ifndef PGP
+#define PGP "/usr/local/bin/pgp"
+#endif
+
+struct mygzip_header;
+struct signature;
+
+extern void *new_pgp_checker __P((struct mygzip_header *h, \
+ struct signature *sign, const char *userid, char *envp[], \
+ const char *filename));
+
+extern void pgp_add __P((void *arg, const char *buffer, \
+ size_t length));
+
+extern int pgp_sign_ok __P((void *arg));
+
+extern void handle_pgp_passphrase __P((void));
+
+extern int retrieve_pgp_signature __P((const char *filename, \
+struct signature **sign, const char *userid, char *envp[]));
diff --git a/usr.sbin/pkg_install/sign/pgp_check.c b/usr.sbin/pkg_install/sign/pgp_check.c
new file mode 100644
index 0000000..52b0345
--- /dev/null
+++ b/usr.sbin/pkg_install/sign/pgp_check.c
@@ -0,0 +1,196 @@
+/* $OpenBSD: pgp_check.c,v 1.2 1999/10/07 16:30:32 espie Exp $ */
+/*-
+ * Copyright (c) 1999 Marc Espie.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Marc Espie for the OpenBSD
+ * Project.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENBSD
+ * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stdio.h>
+#include <errno.h>
+#include <assert.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include "stand.h"
+#include "pgp.h"
+#include "gzip.h"
+#include "extern.h"
+
+#ifndef _PATH_DEVNULL
+#define _PATH_DEVNULL "/dev/null"
+#endif
+
+/* transform current process into pgp signature checker -u userid <fd */
+static void
+pgpcheck(fd, userid, envp)
+ int fd;
+ const char *userid;
+ char *envp[];
+{
+ int fdnull;
+ pchar argv[6];
+ int argc = 0;
+
+ argv[argc++] = PGP;
+ argv[argc++] = "+batchmode";
+ argv[argc++] = "-f";
+
+ if (userid) {
+ argv[argc++] = "-u";
+ argv[argc++] = (char *)userid;
+ }
+ argv[argc++] = NULL;
+
+ assert(argc <= sizeof argv / sizeof(pchar));
+
+ fdnull = open(_PATH_DEVNULL, O_RDWR);
+ if (fdnull == -1 ||
+ dup2(fd, fileno(stdin)) == -1 ||
+ dup2(fdnull, fileno(stdout)) == -1 ||
+ close(fdnull) == -1 || close(fd) == -1 ||
+ execve(PGP, argv, envp) == -1)
+ perror("launching pgp");
+ exit(errno);
+}
+
+struct pgp_checker {
+ pid_t id;
+ int fdout;
+ int status;
+#ifdef DEBUG_DUMP
+ FILE *out;
+#endif
+};
+
+void *
+new_pgp_checker(h, sign, userid, envp, filename)
+ struct mygzip_header *h;
+ struct signature *sign;
+ const char *userid;
+ char *envp[];
+ /*@observer@*/const char *filename;
+{
+ struct pgp_checker *n;
+ int topgpcheck[2];
+
+ assert(sign->type == TAG_PGP);
+ n = malloc(sizeof *n);
+
+ {
+ struct stat sbuf;
+
+ if (stat(PGP, &sbuf) == -1) {
+ warnx("%s does not exist", PGP);
+ return NULL;
+ }
+ }
+ if (n == NULL) {
+ warnx("Can't allocate pgp_checker");
+ return NULL;
+ }
+
+ if (pipe(topgpcheck) == -1) {
+ warn("Pgp checker pipe");
+ free(n);
+ return NULL;
+ }
+ switch(n->id = fork()) {
+ case -1:
+ warn("Pgp checker process");
+ free(n);
+ return NULL;
+ case 0:
+ if (close(topgpcheck[1]) == -1)
+ exit(errno);
+ pgpcheck(topgpcheck[0], userid, envp);
+ /*@notreached@*/
+ break;
+ default:
+ (void)close(topgpcheck[0]);
+ break;
+ }
+ n->fdout = topgpcheck[1];
+ /* so that subsequent fork() won't duplicate it inadvertently */
+ (void)fcntl(n->fdout, F_SETFD, FD_CLOEXEC);
+#ifdef DEBUG_DUMP
+ n->out = fopen("compare", "w");
+#endif
+ n->status = PKG_GOODSIG;
+
+ pgp_add(n, sign->data, sign->length);
+ if (gzip_copy_header(h, sign->next, pgp_add, n) == 0) {
+ warnx("Unexpected header in %s", filename);
+ n->status = PKG_SIGERROR;
+ }
+ return n;
+}
+
+void
+pgp_add(arg, buffer, length)
+ void *arg;
+ const char *buffer;
+ size_t length;
+{
+ struct pgp_checker *n = arg;
+
+ if (n->status == PKG_GOODSIG) {
+#ifdef DEBUG_DUMP
+ fwrite(buffer, 1, length, n->out);
+#endif
+ while (length > 0) {
+ ssize_t l = write(n->fdout, buffer, length);
+ if (l == -1) {
+ n->status = PKG_SIGERROR;
+ break;
+ }
+ length -= l;
+ buffer += l;
+ }
+ }
+}
+
+int
+pgp_sign_ok(arg)
+ void *arg;
+{
+ struct pgp_checker *n = arg;
+ int status = n->status;
+
+#ifdef DEBUG_DUMP
+ fclose(n->out);
+#endif
+ if (close(n->fdout) != 0)
+ status = PKG_SIGERROR;
+ if (reap(n->id) != 0)
+ status = PKG_BADSIG;
+ free(n);
+ return status;
+}
diff --git a/usr.sbin/pkg_install/sign/pgp_sign.c b/usr.sbin/pkg_install/sign/pgp_sign.c
new file mode 100644
index 0000000..cb7d186
--- /dev/null
+++ b/usr.sbin/pkg_install/sign/pgp_sign.c
@@ -0,0 +1,280 @@
+/* $OpenBSD: pgp_sign.c,v 1.1 1999/10/04 21:46:29 espie Exp $ */
+/*-
+ * Copyright (c) 1999 Marc Espie.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Marc Espie for the OpenBSD
+ * Project.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENBSD
+ * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <signal.h>
+#include <pwd.h>
+#include <assert.h>
+#include "stand.h"
+#include "pgp.h"
+#include "gzip.h"
+#include "extern.h"
+
+static void
+pgpsign(fdin, fdout, userid, envp)
+ int fdin, fdout;
+ const char *userid;
+ char *envp[];
+{
+ pchar argv[10];
+ int argc = 0;
+
+ argv[argc++] = PGP;
+ argv[argc++] = "+batchmode";
+ argv[argc++] = "+compress=off";
+ argv[argc++] = "-f";
+ argv[argc++] = "-s";
+ argv[argc++] = "-zAthlon";
+
+ if (userid) {
+ argv[argc++] = "-u";
+ argv[argc++] = (char *)userid;
+ }
+ argv[argc++] = NULL;
+ assert(argc <= sizeof argv / sizeof(pchar));
+
+ if (dup2(fdin, fileno(stdin)) == -1 ||
+ dup2(fdout, fileno(stdout)) == -1 ||
+ execve(PGP, argv, envp) == -1)
+ exit(errno);
+}
+
+static struct signature *
+new_pgpsignature(old)
+ struct signature *old;
+{
+ struct signature *n;
+
+ n = malloc(sizeof(*n));
+ if (n != NULL) {
+ n->data = malloc(MAXPGPSIGNSIZE);
+ if (n->data == NULL) {
+ free(n);
+ return NULL;
+ }
+ n->length = 0;
+ n->next = old;
+ n->type = TAG_PGP;
+ memcpy(n->tag, pgptag, sizeof pgptag);
+ }
+ return n;
+}
+
+int
+retrieve_pgp_signature(filename, sign, userid, envp)
+ const char *filename;
+ struct signature **sign;
+ const char *userid;
+ char *envp[];
+{
+ int topgp[2], frompgp[2];
+ pid_t pgpid;
+ struct mygzip_header h;
+ int success;
+
+ FILE *orig, *dest, *signin;
+ struct signature *old;
+
+ orig = fopen(filename, "r");
+ if (orig == NULL)
+ return 0;
+ if (gzip_read_header(orig, &h, &old) == GZIP_NOT_GZIP) {
+ warnx("File %s is not a gzip file\n", filename);
+ fclose(orig);
+ return 0;
+ }
+
+ if (pipe(topgp) == -1) {
+ fclose(orig);
+ return 0;
+ }
+ if (pipe(frompgp) == -1) {
+ fclose(orig);
+ (void)close(topgp[0]);
+ (void)close(topgp[1]);
+ return 0;
+ }
+ switch(pgpid = fork()) {
+ case 0:
+ (void)close(topgp[1]);
+ (void)close(frompgp[0]);
+ pgpsign(topgp[0], frompgp[1], userid, envp);
+ /*NOT REACHED */
+ case -1:
+ (void)close(topgp[0]);
+ (void)close(topgp[1]);
+ (void)close(frompgp[0]);
+ (void)close(frompgp[1]);
+ fclose(orig);
+ return 0;
+ default:
+ (void)close(topgp[0]);
+ (void)close(frompgp[1]);
+ }
+
+ dest = fdopen(topgp[1], "w");
+ if (dest == NULL) {
+ (void)close(topgp[1]);
+ (void)close(frompgp[0]);
+ (void)reap(pgpid);
+ return 0;
+ }
+
+ success = 1;
+ if (gzip_write_header(dest, &h, old) == 0)
+ success = 0;
+ else {
+ int c;
+
+ while ((c = fgetc(orig)) != EOF && fputc(c, dest) != EOF)
+ ;
+ if (ferror(dest))
+ success = 0;
+ }
+ if (fclose(dest) != 0)
+ success = 0;
+
+ if (fclose(orig) != 0)
+ success = 0;
+
+ signin = fdopen(frompgp[0], "r");
+ if (signin == NULL) {
+ (void)close(frompgp[0]);
+ } else {
+ enum { NONE, FIRST, DONE, COPY} magic = NONE;
+ int c;
+#ifdef DEBUG_DUMP
+ FILE *out = fopen("dump", "w");
+#endif
+
+ if ((*sign = new_pgpsignature(old)) == NULL)
+ success = 0;
+ else {
+ while ((c = fgetc(signin)) != EOF && magic != DONE &&
+ (*sign)->length < MAXPGPSIGNSIZE) {
+ switch(magic) {
+ case NONE:
+ (*sign)->data[(*sign)->length++] = c;
+ if ((unsigned char)c == (unsigned char)GZIP_MAGIC0)
+ magic = FIRST;
+ break;
+ case FIRST:
+ (*sign)->data[(*sign)->length++] = c;
+ if ((unsigned char)c == (unsigned char)GZIP_MAGIC1)
+#ifdef DEBUG_DUMP
+ magic = COPY;
+#else
+ magic = DONE;
+#endif
+ else if ((unsigned char)c != (unsigned char)GZIP_MAGIC0)
+ magic = NONE;
+ break;
+ case DONE:
+ case COPY:
+ break;
+ }
+#ifdef DEBUG_DUMP
+ fputc(c, out);
+#endif
+ }
+ if ((*sign)->length == MAXPGPSIGNSIZE)
+ success = 0;
+ (*sign)->length -= 2;
+ sign_fill_tag(*sign);
+ }
+ fclose(signin);
+#ifdef DEBUG_DUMP
+ fclose(out);
+#endif
+ reap(pgpid);
+ }
+ return success;
+}
+
+void
+handle_pgp_passphrase()
+{
+ pid_t pid;
+ int fd[2];
+ char *p;
+
+printf("Short-circuiting %s\n", __func__);
+return;
+
+ /* Retrieve the pgp passphrase */
+ p = getpass("Enter passphrase:");
+
+ /*
+ * Somewhat kludgy code to get the passphrase to pgp, see
+ * pgp documentation for the gore
+ */
+ if (pipe(fd) != 0) {
+ perror("pkg_sign");
+ exit(EXIT_FAILURE);
+ }
+ switch(pid = fork()) {
+ case -1:
+ perror("pkg_sign");
+ exit(EXIT_FAILURE);
+ case 0:
+ {
+ (void)close(fd[0]);
+ /*
+ * The child fills the pipe with copies of the passphrase.
+ * Expect violent death when father exits.
+ */
+ printf("Child process %d stuffing passphrase in pipe:\n", getpid());
+ for(;;) {
+ char c = '\n';
+ (void)write(fd[1], p, strlen(p));
+ (void)write(fd[1], &c, 1);
+ putchar('.'); fflush(stdout);
+ }
+ }
+ default:
+ {
+ char buf[10];
+
+ sleep(1);
+ (void)close(fd[1]);
+ (void)sprintf(buf, "%d", fd[0]);
+ (void)setenv("PGPPASSFD", buf, 1);
+ printf("Parent process PGPPASSFD=%d.\n", fd[0]);
+ }
+ }
+}
+
diff --git a/usr.sbin/pkg_install/sign/pkg_sign.1 b/usr.sbin/pkg_install/sign/pkg_sign.1
new file mode 100644
index 0000000..9a11794
--- /dev/null
+++ b/usr.sbin/pkg_install/sign/pkg_sign.1
@@ -0,0 +1,208 @@
+.\" $FreeBSD$
+.\" $OpenBSD: pkg_sign.1,v 1.6 2000/04/15 02:15:20 aaron Exp $
+.\"
+.\" Copyright (c) 1999 Marc Espie.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Marc Espie for the OpenBSD
+.\" Project.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+.\" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+.\" A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENBSD
+.\" PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+.\" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+.\" OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.Dd September 24, 1999
+.Dt PKG_SIGN 1
+.Os
+.Sh NAME
+.Nm pkg_sign ,
+.Nm pkg_check
+.Nd handle package signatures
+.Sh SYNOPSIS
+.Nm
+.Op Fl sc
+.Op Fl t Ar type
+.Op Fl u Ar id
+.Op Fl k Ar key
+.Op Ar
+.Nm pkg_check
+.Op Fl sc
+.Op Fl u Ar id
+.Op Fl k Ar cert
+.Op Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility embeds a cryptographic signature within a gzip file
+.Ar file .
+.Ar type
+can be
+.Cm pgp
+(default),
+.Cm sha1 ,
+or
+.Cm x509 .
+If
+.Ar type
+is
+.Cm pgp ,
+it will always prompt you for a passphrase to unlock your private
+pgp key, even if you don't use a passphrase (which is a bad idea, anyway).
+If
+.Ar type
+is
+.Cm sha1 ,
+you must supply an
+.Ar id ,
+which will be recorded as the name of the package, and printed as the
+SHA1 checksum.
+.Pp
+The
+.Nm pkg_check
+utility checks that cryptographic signature.
+It currently disregards
+.Ar type
+and checks only the topmost signature.
+For
+.Cm sha1 ,
+it checksums the file
+and verifies that the result matches the list of checksums recorded in
+.Pa /var/db/pkg/SHA1 .
+.Pp
+Options
+.Fl s
+and
+.Fl c
+can be used to force package signing or signature checking mode.
+.Pp
+For
+.Cm pgp ,
+the
+.Ar id
+to use to sign the package or verify the signature can be forced with
+.Fl u .
+.Pp
+For
+.Cm x509 ,
+the signing key or verification certificate may be
+specified with the
+.Fl k
+option. If not specified, packages are signed or verified with the
+default keys and certificates documented below.
+.Pp
+If
+.Ar file
+is a single dash
+.Pq Sq Fl
+or absent,
+.Nm
+reads from the standard input.
+.Pp
+Package signing uses a feature of the gzip format, namely that one can
+set a flag
+.Dv EXTRA_FIELD
+in the gzip header and store extra data between the gzip header and the
+compressed file proper.
+The
+.Ox
+signing scheme uses eight bytes markers such
+.Sq Li SIGPGP
++ length or
+.Sq CKSHA1
++ length for its signatures (those markers are conveniently
+eight bytes long).
+.Sh DIAGNOSTICS
+The
+.Nm
+and
+.Nm pkg_check
+utilities return with an exit code >0 if anything went wrong for any
+.Ar file .
+For
+.Nm pkg_check ,
+this usually indicates that the package is not signed, or that the
+signature is forged.
+.Bl -diag
+.It "File %s is already signed"
+There is a signature embedded within the gzip file already.
+The
+.Nm
+utility currently does not handle multiple signatures.
+.It "File %s is not a signed gzip file"
+This is an unsigned package.
+.It "File %s is not a gzip file"
+The program couldn't find a proper gzip header.
+.It "File %s contains an unknown extension"
+The extended area of the gzip file has been used for an unknown purpose.
+.It "File %s uses old signatures, no longer supported"
+The gzip file uses a very early version of package signing that was
+substantially slower.
+.El
+.Sh BUGS
+.Xr pgp 1
+is an ill-designed program, which is hard to interface with.
+For instance, the `separate signing scheme' it pretends to offer is
+useless, as it can't be used with pipes, so that
+.Nm pgp_sign
+needs to kludge it by knowing the length of a pgp signature, and invoking
+pgp in `seamless' signature mode, without compression of the main file,
+and just retrieving the signature.
+.Pp
+The checking scheme is little less convoluted, namely we rebuild the file
+that pgp expects on the fly.
+.Pp
+Paths to
+.Nm pgp
+and
+the checksum file are hard-coded to avoid tampering and hinder flexibility.
+.Sh FILES
+.Bl -tag -width "/usr/local/bin/pgp" -compact
+.It Pa file.sign
+Temporary file built by
+.Nm
+from
+.Ar file .
+.It Pa /usr/local/bin/pgp
+Default path to
+.Xr pgp 1 .
+.It Pa /var/db/pkgs/SHA1
+Recorded checksums.
+.It Pa /etc/ssl/pkg.key
+Default package signing key.
+.It Pa /etc/ssl/pkg.crt
+Default package verification certificate(s).
+.El
+.Sh SEE ALSO
+.Xr gzip 1 ,
+.Xr pgp 1 ,
+.Xr pkg_add 1 ,
+.Xr sha1 1
+.Sh AUTHORS
+.An -nosplit
+A
+.Nm
+utility was created by
+.An Marc Espie
+for the
+.Ox
+Project.
+X.509 signatures and
+.Fx
+support added by
+.An Wes Peters Aq wes@softweyr.com .
diff --git a/usr.sbin/pkg_install/sign/sha1.c b/usr.sbin/pkg_install/sign/sha1.c
new file mode 100644
index 0000000..24ca997
--- /dev/null
+++ b/usr.sbin/pkg_install/sign/sha1.c
@@ -0,0 +1,224 @@
+/* $OpenBSD: sha1.c,v 1.1 1999/10/04 21:46:29 espie Exp $ */
+/*-
+ * Copyright (c) 1999 Marc Espie.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Marc Espie for the OpenBSD
+ * Project.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENBSD
+ * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <openssl/sha.h>
+#include "stand.h"
+#include "gzip.h"
+#include "extern.h"
+
+/* private context for sha1 signature checker */
+struct sha1_checker {
+ SHA_CTX context;
+ const char *id;
+ const char *filename;
+};
+
+
+#define SHA1_TEMPLATE "SHA1 (%s) = "
+#define BUFSIZE (MAXID+sizeof(SHA1_TEMPLATE)+2*SHA_DIGEST_LENGTH+1)
+
+/*
+ * Finalize SHA1 checksum for our sha1_context into result
+ * (size at least BUFSIZE). Returns the length of the checksum
+ * marker, e.g., SHA1 (id) = xxxxxxxxx
+ * ^here
+ * Return 0 for errors.
+ */
+size_t
+sha1_build_checksum(result, n)
+ char *result;
+ struct sha1_checker *n;
+{
+ size_t length;
+
+ sprintf(result, "SHA1 (%s) = ", n->id);
+ length = strlen(result);
+ SHA1_Final(result + length, &n->context);
+ strcat(result, "\n");
+ free(n);
+ return length;
+}
+
+void *
+new_sha1_checker(h, sign, userid, envp, filename)
+ struct mygzip_header *h;
+ struct signature *sign;
+ const char *userid;
+ char *envp[];
+ /*@observer@*/const char *filename;
+{
+ struct sha1_checker *n;
+
+ assert(sign->type == TAG_SHA1);
+ /* make sure data conforms to what we can handle */
+ if (sign->length > MAXID || sign->data[sign->length-1] != '\0') {
+ warnx("Corrupted SHA1 header in %s", filename);
+ return 0;
+ }
+
+ n = malloc(sizeof *n);
+ if (n == NULL) {
+ warnx("Can't allocate sha1_checker");
+ return NULL;
+ }
+ SHA1_Init(&n->context);
+ n->id = sign->data;
+ n->filename = filename;
+
+ /* copy header, as this is a checksum, we don't strip our own marker */
+ if (gzip_copy_header(h, sign, sha1_add, n) == 0) {
+ warnx("Unexpected header in %s", filename);
+ free(n);
+ return 0;
+ }
+ return n;
+}
+
+void
+sha1_add(arg, buffer, length)
+ void *arg;
+ const char *buffer;
+ size_t length;
+{
+ struct sha1_checker *n = arg;
+ SHA1_Update(&n->context, buffer, length);
+}
+
+int
+sha1_sign_ok(arg)
+ void *arg;
+{
+ struct sha1_checker *n = arg;
+ char buffer[BUFSIZE];
+ char scan[BUFSIZE];
+ size_t length;
+ FILE *f;
+ int tag_found;
+
+ length = sha1_build_checksum(buffer, n);
+ f= fopen(SHA1_DB_NAME, "r");
+ tag_found = 0;
+
+ if (f == NULL) {
+ warn("Can't access checksum file %s", SHA1_DB_NAME);
+ return PKG_BADSIG;
+ }
+ while (fgets(scan, sizeof(scan), f) != NULL) {
+ if (strcmp(scan, buffer) == 0) {
+ fprintf(stderr, "Checksum ok\n");
+ return PKG_GOODSIG;
+ }
+ if (strncmp(scan, buffer, length) == 0)
+ tag_found = 1;
+ }
+
+ if (tag_found) {
+ warnx("Checksum incorrect for %s (%s)", n->filename, n->id);
+ return PKG_BADSIG;
+ } else {
+ warnx("No checksum found for %s (%s)", n->filename, n->id);
+ return PKG_SIGUNKNOWN;
+ }
+}
+
+int
+retrieve_sha1_marker(filename, sign, userid)
+ const char *filename;
+ struct signature **sign;
+ const char *userid;
+{
+ struct signature *n;
+ struct mygzip_header h;
+ FILE *f;
+ char buffer[1024];
+ char result[BUFSIZE];
+ ssize_t length;
+ struct sha1_checker *checker;
+ struct signature *old;
+
+ *sign = NULL;
+ if (userid == NULL)
+ return 0;
+
+ /*
+ * Create a blank signature and fill it with the userid.
+ */
+ n = malloc(sizeof *n);
+ if (n == NULL)
+ return 0;
+ n->data = (char *)userid;
+ n->length = strlen(n->data)+1;
+ n->type = TAG_SHA1;
+ memcpy(n->tag, sha1tag, sizeof sha1tag);
+ sign_fill_tag(n);
+
+ /*
+ * Read the gzip header and add our "userid" signature to it.
+ */
+ f = fopen(filename, "r");
+ if (f == NULL) {
+ free(n);
+ return 0;
+ }
+ if (gzip_read_header(f, &h, sign) == GZIP_NOT_GZIP) {
+ warnx("File %s is not a gzip file\n", filename);
+ fclose(f);
+ free(n);
+ return 0;
+ }
+ n->next = *sign;
+ *sign = n;
+
+ /*
+ * Calculate the SHA1 of the remaining data and write it to stderr.
+ */
+ checker = new_sha1_checker(&h, *sign, NULL, NULL, filename);
+ while ((length = fread(buffer, 1, sizeof buffer, f)) > 0)
+ sha1_add(checker, buffer, length);
+ if (fclose(f) != 0 || length == -1) {
+ warn("Problem checksumming %s", filename);
+ *sign = n->next;
+ free(n);
+ return 0;
+ }
+
+ (void)sha1_build_checksum(result, checker);
+ fputs(result, stderr);
+ return 1;
+}
+
diff --git a/usr.sbin/pkg_install/sign/sign.c b/usr.sbin/pkg_install/sign/sign.c
new file mode 100644
index 0000000..56572d8
--- /dev/null
+++ b/usr.sbin/pkg_install/sign/sign.c
@@ -0,0 +1,145 @@
+/* $OpenBSD: sign.c,v 1.3 1999/10/04 21:46:29 espie Exp $ */
+/*-
+ * Copyright (c) 1999 Marc Espie.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Marc Espie for the OpenBSD
+ * Project.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENBSD
+ * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <signal.h>
+#include <pwd.h>
+#include <assert.h>
+#include "stand.h"
+#include "pgp.h"
+#include "gzip.h"
+#include "extern.h"
+
+#define COPY_TEMPLATE "%s.sign"
+
+static int
+embed_signature_FILE(orig, dest, sign, filename)
+ /*@temp@*/FILE *orig;
+ /*@temp@*/FILE *dest;
+ struct signature *sign;
+ const char *filename;
+{
+ struct mygzip_header h;
+ int c;
+
+ if (gzip_read_header(orig, &h, NULL) == GZIP_NOT_GZIP)
+ return 0;
+
+ if (gzip_write_header(dest, &h, sign) == 0)
+ return 0;
+ while ((c = fgetc(orig)) != EOF && fputc(c, dest) != EOF)
+ ;
+ if (ferror(dest) != 0)
+ return 0;
+ return 1;
+}
+
+static int
+embed_signature(filename, copy, sign)
+ const char *filename;
+ const char *copy;
+ struct signature *sign;
+{
+ FILE *orig, *dest;
+ int success;
+
+ success = 0;
+ orig= fopen(filename, "r");
+ if (orig) {
+ dest = fopen(copy, "w");
+ if (dest) {
+ success = embed_signature_FILE(orig, dest, sign, filename);
+ if (fclose(dest) != 0)
+ success = 0;
+ }
+ if (fclose(orig) != 0)
+ success = 0;
+ }
+ return success;
+}
+
+int
+sign(filename, type, userid, envp)
+ const char *filename;
+ const char *userid;
+ int type;
+ char *envp[];
+{
+ char *copy;
+ int result;
+ struct signature *sign;
+ int success;
+
+ switch(type) {
+ case TAG_PGP:
+ success = retrieve_pgp_signature(filename, &sign, userid, envp);
+ break;
+ case TAG_SHA1:
+ success = retrieve_sha1_marker(filename, &sign, userid);
+ break;
+ case TAG_X509:
+ success = retrieve_x509_marker(filename, &sign, userid);
+ break;
+ }
+
+ if (!success) {
+ fprintf(stderr, "Problem signing %s\n", filename);
+ free_signature(sign);
+ return 0;
+ }
+ copy = malloc(strlen(filename)+sizeof(COPY_TEMPLATE));
+ if (copy == NULL) {
+ fprintf(stderr, "Can't allocate memory\n");
+ free_signature(sign);
+ return 0;
+ }
+ sprintf(copy, COPY_TEMPLATE, filename);
+ result = embed_signature(filename, copy, sign);
+ if (result == 0) {
+ fprintf(stderr, "Can't embed signature in %s\n", filename);
+ } else if (unlink(filename) != 0) {
+ fprintf(stderr, "Can't unlink original %s\n", filename);
+ result = 0;
+ } else if (rename(copy, filename) != 0) {
+ fprintf(stderr, "Can't rename new file %s\n", copy);
+ result = 0;
+ }
+ free(copy);
+ free_signature(sign);
+ return result;
+}
+
diff --git a/usr.sbin/pkg_install/sign/stand.c b/usr.sbin/pkg_install/sign/stand.c
new file mode 100644
index 0000000..703c58d
--- /dev/null
+++ b/usr.sbin/pkg_install/sign/stand.c
@@ -0,0 +1,57 @@
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "stand.h"
+
+#ifdef BSD4_4
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdarg.h>
+
+/* shortened version of warn */
+static const char *program_name;
+
+void
+set_program_name(n)
+ const char *n;
+{
+ if ((program_name = strrchr(n, '/')) != NULL)
+ program_name++;
+ else
+ program_name = n;
+}
+
+void
+warn(const char *fmt, ...)
+{
+ va_list ap;
+ int interrno;
+
+ va_start(ap, fmt);
+
+ interrno = errno;
+ (void)fprintf(stderr, "%s: ", program_name);
+ if (fmt != NULL) {
+ (void)vfprintf(stderr, fmt, ap);
+ (void)fprintf(stderr, ": ");
+ }
+ (void)fprintf(stderr, "%s\n", strerror(interrno));
+
+ va_end(ap);
+}
+
+void
+warnx(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ (void)fprintf(stderr, "%s: ", program_name);
+ if (fmt != NULL)
+ (void)vfprintf(stderr, fmt, ap);
+ (void)fprintf(stderr, "\n");
+ va_end(ap);
+}
+
+#endif
diff --git a/usr.sbin/pkg_install/sign/stand.h b/usr.sbin/pkg_install/sign/stand.h
new file mode 100644
index 0000000..bc8de2f
--- /dev/null
+++ b/usr.sbin/pkg_install/sign/stand.h
@@ -0,0 +1,28 @@
+/* $FreeBSD$ */
+/* $OpenBSD: stand.h,v 1.2 1999/10/04 21:46:30 espie Exp $ */
+
+/* provided to cater for BSD idiosyncrasies */
+
+#if (defined(__unix__) || defined(unix)) && !defined(USG)
+#include <sys/param.h>
+#endif
+
+#ifndef __P
+#ifdef __STDC__
+#define __P(x) x
+#else
+#define __P(x) ()
+#endif
+#endif
+
+#if defined(BSD4_4)
+#include <err.h>
+#else
+extern void set_program_name __P((const char * name));
+extern void warn __P((const char *fmt, ...));
+extern void warnx __P((const char *fmt, ...));
+#endif
+
+#ifndef __GNUC__
+#define __attribute__(x)
+#endif
diff --git a/usr.sbin/pkg_install/sign/x509.c b/usr.sbin/pkg_install/sign/x509.c
new file mode 100644
index 0000000..5917734
--- /dev/null
+++ b/usr.sbin/pkg_install/sign/x509.c
@@ -0,0 +1,426 @@
+/*-
+ * Copyright (c) 2000 Softweyr LLC, South Jordan, Utah, USA.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Softweyr LLC ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL Softweyr LLC BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <assert.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libgen.h>
+
+#include <openssl/rsa.h>
+#include <openssl/evp.h>
+#include <openssl/objects.h>
+#include <openssl/x509.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
+#include <openssl/ssl.h>
+
+#include "stand.h"
+#include "gzip.h"
+#include "extern.h"
+
+
+/*
+ * Default names for the signing key and certificate(s) for verification.
+ */
+#define CERTFILE "/etc/ssl/pkg.crt"
+#define KEYFILE "/etc/ssl/pkg.key"
+
+
+/*
+ * Private context for X.509 signature checker
+ */
+struct x509_checker
+{
+ const char * id;
+ const char * filename;
+
+ struct signature * signature;
+
+ STACK_OF(X509) * certs;
+ EVP_MD_CTX rsa_ctx, dsa_ctx;
+ int has_rsa, has_dsa;
+};
+
+
+static void key_from_name(char *, const char *);
+
+/*
+ * Initialize an X.509 "checker" context.
+ */
+void *
+new_x509_checker(h, sign, userid, envp, filename)
+ struct mygzip_header *h;
+ struct signature *sign;
+ const char *userid;
+ char *envp[];
+ /*@observer@*/const char *filename;
+{
+ FILE * fp;
+ struct x509_checker * me;
+ char certfile[PATH_MAX + 1] = CERTFILE;
+ char * cp;
+ X509 * x509;
+
+ assert(sign->type == TAG_X509);
+
+ /*
+ * Make sure data conforms to what we can handle. We do not write a
+ * trailing null onto the signature like some other types, because
+ * the X.509 signature is binary data.
+ */
+ if (sign->length > MAXID) {
+ warnx("Corrupted X.509 header in %s", filename);
+ return 0;
+ }
+
+ me = malloc(sizeof *me);
+ if (me == NULL) {
+ warn("Cannot allocate x509_checker");
+ return 0;
+ }
+ me->id = sign->data;
+ me->filename = filename;
+ me->signature = sign;
+ me->has_rsa = 0;
+ me->has_dsa = 0;
+
+ key_from_name(certfile, userkey);
+
+ /*
+ * Load just the crypto library error strings.
+ */
+ ERR_load_crypto_strings();
+
+ /*
+ * Load the stack of X.509 certs we will compare against.
+ *
+ * KLUDGE: this needs to be fleshed out a bit. We can do better
+ * than hard-coding the location of the cert key file.
+ */
+ me->certs = sk_X509_new_null();
+
+ fp = fopen(certfile, "r");
+ if (fp == NULL) {
+ warnx("Cannot open public key %s", certfile);
+ return 0;
+ }
+
+ if (verbose)
+ printf("Loading certificates from %s:\n", certfile);
+
+ while (x509 = PEM_read_X509(fp, NULL, NULL, 0)) {
+ sk_X509_push(me->certs, x509);
+
+ switch (EVP_PKEY_type(X509_get_pubkey(x509)->type))
+ {
+ case EVP_PKEY_RSA:
+ me->has_rsa = 1;
+ break;
+
+ case EVP_PKEY_DSA:
+ me->has_dsa = 1;
+ break;
+
+ default:
+ warnx("Uknown certificate type");
+ return 0;
+ }
+
+ /*
+ * By default, print the contents of the cert we matched so the
+ * user can decide if she is willing to accept a package from
+ * whoever signed this.
+ */
+ if (!quiet)
+ X509_print_fp(stdout, x509);
+ }
+ fclose(fp);
+
+ /*
+ * Initialize the verification contexts for both RSA and DSA.
+ */
+ if (me->has_rsa) EVP_VerifyInit(&me->rsa_ctx, EVP_sha1());
+ if (me->has_dsa) EVP_VerifyInit(&me->dsa_ctx, EVP_dss1());
+
+ return me;
+}
+
+
+/*
+ * "Add" another data block to an existing checker.
+ */
+void
+x509_add(arg, buffer, length)
+ void *arg;
+ const char *buffer;
+ size_t length;
+{
+ struct x509_checker * me = arg;
+
+ if (me->has_rsa) EVP_VerifyUpdate(&me->rsa_ctx, buffer, length);
+ if (me->has_dsa) EVP_VerifyUpdate(&me->dsa_ctx, buffer, length);
+}
+
+
+/*
+ * Finalize an existing checker and verify the signature matches one of the
+ * certs in our stack.
+ */
+int
+x509_sign_ok(arg)
+ void *arg;
+{
+ struct x509_checker * n = arg;
+ X509 * x509;
+ EVP_PKEY * pkey;
+ EVP_MD_CTX * md_ctx;
+ int status;
+
+ if (verbose)
+ printf("\n\n-------\n\nChecking package signature:\n");
+
+ while ((x509 = sk_X509_pop(n->certs)) != NULL) {
+ /*
+ * Get public key from cert.
+ */
+ pkey = X509_get_pubkey(x509);
+ if (pkey == NULL) {
+ warnx("Getting public key:");
+ ERR_print_errors_fp(stderr);
+ continue;
+ }
+
+ if (verbose)
+ X509_print_fp(stdout, x509);
+
+ switch (EVP_PKEY_type(pkey->type))
+ {
+ case EVP_PKEY_RSA:
+ md_ctx = &n->rsa_ctx;
+ break;
+
+ case EVP_PKEY_DSA:
+ md_ctx = &n->dsa_ctx;
+ break;
+
+ default:
+ }
+
+ status = EVP_VerifyFinal(md_ctx,
+ n->signature->data,
+ n->signature->length,
+ pkey);
+
+ EVP_PKEY_free(pkey);
+ X509_free(x509);
+
+ if (status == 1) {
+ fprintf(stderr, "X.509 signature matched\n");
+
+ /*
+ * KLUDGE: Does this free the rest of the certs, or just the
+ * stack itself? Enquiring minds want to know.
+ */
+ sk_X509_free(n->certs);
+ return PKG_GOODSIG;
+ }
+ }
+
+ warnx("Verifying signature:");
+ ERR_print_errors_fp(stderr);
+ sk_X509_free(n->certs);
+ return PKG_BADSIG;
+}
+
+
+/*
+ * Sign the specified filename into sign.
+ */
+int
+retrieve_x509_marker(filename, sign, userid)
+ const char * filename;
+ struct signature ** sign;
+ const char * userid;
+{
+ struct signature * n;
+ struct mygzip_header h;
+ FILE * f, * keyf;
+ char buffer[1024];
+ ssize_t length;
+ int err;
+ int sig_len = 4096;
+ unsigned char * sig_buf;
+ EVP_MD_CTX md_ctx;
+ EVP_MD * md_type;
+ EVP_PKEY * pkey;
+
+ char keyfile[PATH_MAX + 1] = KEYFILE;
+ char * kp;
+
+ key_from_name(keyfile, userkey);
+
+ f = fopen(filename, "r");
+ if (f == NULL) {
+ free(n);
+ return 0;
+ }
+ if (gzip_read_header(f, &h, sign) == GZIP_NOT_GZIP) {
+ warnx("File %s is not a gzip file\n", filename);
+ fclose(f);
+ free(n);
+ return 0;
+ }
+
+ /*
+ * Sign the remaining data:
+ * Load just the crypto library error strings.
+ */
+ ERR_load_crypto_strings();
+
+ /*
+ * Read private key.
+ */
+ keyf = fopen(keyfile, "r");
+ if (keyf == NULL)
+ {
+ warnx("Cannot open private key %s.", keyfile);
+ return 0;
+ }
+
+ pkey = PEM_read_PrivateKey(keyf, NULL, NULL, 0);
+ fclose(keyf);
+
+ if (pkey == NULL)
+ {
+ warnx("Reading private key %s:", keyfile);
+ ERR_print_errors_fp(stderr);
+ return 0;
+ }
+
+ /*
+ * Do the signature. The remaining bytes of the GZIP file are the
+ * compressed tar image, which is what we are signing.
+ */
+ switch (EVP_PKEY_type(pkey->type))
+ {
+ case EVP_PKEY_RSA:
+ md_type = EVP_sha1();
+printf("*** It's an RSA key.\n");
+ break;
+
+ case EVP_PKEY_DSA:
+ md_type = EVP_dss1();
+printf("@@@ It's a DSA key, yippee!\n");
+ break;
+
+ default:
+ warnx("Uknown key type");
+ return 0;
+ }
+
+ EVP_SignInit(&md_ctx, md_type);
+
+ while ((length = fread(buffer, 1, sizeof buffer, f)) > 0)
+ EVP_SignUpdate(&md_ctx, buffer, length);
+
+ sig_buf = malloc(sig_len);
+ if (sig_buf == NULL) {
+ warnx("Cannot allocated %u bytes for signature buffer", sig_len);
+ return 0;
+ }
+
+ err = EVP_SignFinal(&md_ctx, sig_buf, &sig_len, pkey);
+
+ if (err != 1)
+ {
+ warnx("Creating signature:");
+ ERR_print_errors_fp(stderr);
+ return 0;
+ }
+
+ EVP_PKEY_free(pkey);
+
+ /*
+ * Stuff the signature onto the head of the chain of signatures in
+ * the package.
+ */
+ n = malloc(sizeof *n);
+ if (n == NULL) {
+ warnx("Cannot allocate %u bytes for new signature", sizeof *n);
+ return 0;
+ }
+ n->data = sig_buf;
+ n->length = sig_len;
+ n->type = TAG_X509;
+ memcpy(n->tag, x509tag, sizeof x509tag);
+ sign_fill_tag(n);
+ n->next = *sign;
+ *sign = n;
+
+ /*
+ * Report our success.
+ */
+ return 1;
+}
+
+
+static void
+key_from_name(char * filename, const char * ident)
+{
+ char * cp;
+
+ /*
+ * If an alternate keyfile was specified, treat it as the name of an
+ * alternate private key with which to sign or verify the package.
+ */
+ if (ident) {
+ printf("Using alternate key/cert \"%s\".\n", ident);
+ if (strchr(ident, '/')) {
+ /*
+ * The user specified a path, take it verbatim.
+ */
+ strncpy(filename, ident, PATH_MAX);
+ } else {
+ cp = dirname(filename);
+ if (cp == NULL) {
+ warnx("Key directory not correctly specified.");
+ return;
+ }
+ snprintf(filename, PATH_MAX, "%s/%s", cp, ident);
+ }
+ }
+
+ if (verbose)
+ printf("Key is \"%s\".\n", filename);
+}
diff --git a/usr.sbin/pkg_install/tkpkg b/usr.sbin/pkg_install/tkpkg
new file mode 100755
index 0000000..9071155
--- /dev/null
+++ b/usr.sbin/pkg_install/tkpkg
@@ -0,0 +1,177 @@
+#!/usr/local/bin/wish -f
+#$FreeBSD$
+#
+#$Log: tkpkg,v $
+#Revision 1.2 1994/12/06 00:51:21 jkh
+#Many of John T. Kohl's patches from NetBSD. Thanks, John!
+#Submitted by: jkohl
+#
+# Revision 1.1 1994/01/06 08:16:20 jkh
+# Cleaning house.
+#
+# Revision 1.1 1993/09/04 17:06:09 jkh
+# Added Rich's wish front-end.
+#
+# Revision 1.6 1993/09/03 23:37:22 rich
+# warn user if no tar archives are found in the current directory.
+# removed the revision string from the lower text frame.
+#
+# Revision 1.5 1993/09/03 15:48:04 rich
+# glob for .tar.gz, .tar.z and .tar.Z looking for archives
+#
+# Revision 1.4 1993/08/28 15:53:59 rich
+# added version and date info to lower text window.
+#
+# Revision 1.3 1993/08/28 15:47:12 rich
+# filtered out ^Ls in pkg_* output.
+#
+#
+set pkgname ""
+wm title . "Package Installation"
+#--------------------------------------------------------------
+# The top level main window, consisting of a bar of buttons and a list
+# of packages and a description of the current package.
+#--------------------------------------------------------------
+frame .menu -relief raised -borderwidth 1
+frame .frame -borderwidth 4
+
+scrollbar .frame.scroll -relief sunken -command ".frame.list yview"
+listbox .frame.list -yscroll ".frame.scroll set" -relief sunken -setgrid 1
+pack append .frame .frame.scroll {right filly} \
+ .frame.list {left expand fill}
+
+# build the lower window shoing the complete description of a pacage
+frame .f -borderwidth 4
+text .f.t -width 80 -height 20 -yscrollcommand ".f.s set" -relief sunken
+
+# Initially display instructions in this window. Erase the
+# instructions and show the package description when the user clicks
+# on a package.
+#
+.f.t insert end "Double click on a package above to see its
+complete description here."
+scrollbar .f.s -relief sunken -command ".f.t yview"
+pack append .f .f.s {right filly} .f.t {left expand fill}
+
+bind .frame.list <Double-Button-1> \
+ {foreach i [selection get] {do_description $i}}
+pack append . .menu {top fill} \
+ .f {bottom expand fill} \
+ .frame {bottom expand fill}
+
+#----------------------------------------------------------------
+# Make menu bar:
+#----------------------------------------------------------------
+button .menu.inst -text "Install" \
+ -command "apply_to_pkg \"pkg_add -v\""
+button .menu.dein -text "Deinstall" \
+ -command "apply_to_pkg \"pkg_delete -v\""
+button .menu.installed -text "What is Installed?" \
+ -command "list_pkgs \"pkg_info -I -a |tr ' ' ' '\""
+button .menu.available -text "What can I install?" \
+ -command "list_pkgs \"pkg_info -I -c [glob -nocomplain *.{tgz,tar.z,tar.gz,tar.Z}] |tr ' ' ' '\""
+button .menu.cont -text "Contents?" \
+ -command "apply_to_pkg \"pkg_info -d -v\""
+button .menu.quit -text "Quit" -command "destroy ."
+button .menu.help -text "Help" -command "do_help"
+
+pack append .menu \
+ .menu.inst left \
+ .menu.dein left \
+ .menu.installed left \
+ .menu.available left \
+ .menu.cont left \
+ .menu.quit left \
+ .menu.help right
+#-------------------------------------------------------
+# Display the package description.
+#-------------------------------------------------------
+proc list_pkgs {s} {
+ set line ""
+ set f [eval "open {| sh -c \"$s\" } r"]
+ .frame.list delete 0 end
+ while {[gets $f line] > 0} {
+ .frame.list insert end $line
+ }
+ close $f
+}
+
+# display the list of available packages
+set archives [glob -nocomplain *.{tgz,tar.z,tar.gz,tar.Z}]
+if {$archives == ""} {
+ .frame.list delete 0 end
+ .frame.list insert end "Warning: no compressed tar archives files found."
+} else {
+ list_pkgs "pkg_info -I -c $archives |tr ' ' ' '"
+}
+
+#-------------------------------------------------------
+# Display the package description.
+#-------------------------------------------------------
+proc do_description {s} {
+ global pkgname
+ regexp {[^ ]*} $s filename
+ set pkgname $filename
+ .f.t delete 0.0 end
+ set cmd "pkg_info -d $filename |tr -d ' '"
+ set f [eval "open {| csh -c \"$cmd\" } r"]
+ while {![eof $f]} {
+ .f.t insert end [read $f]
+ }
+}
+#-------------------------------------------------------
+# package install window.
+#-------------------------------------------------------
+proc do_help {{w .help}} {
+ catch {destroy $w}
+ toplevel $w
+ wm title $w "Help"
+ wm iconname $w "Help"
+ button $w.ok -text OK -command "destroy $w"
+ message $w.t -relief raised -bd 2 \
+ -text "You can install, deinstall and list info on the available packages. To select a package and see its complete description, press mouse button 1 over the package name. To install a selected package, press the Install button. To exit, press the \"Quit\" button."
+ pack append $w $w.ok {bottom fillx} $w.t {expand fill}
+}
+#-------------------------------------------------------
+# Apply a command to a package.
+#-------------------------------------------------------
+proc apply_to_pkg {s} {
+ apply_to_pkg_err $s ""
+}
+#-------------------------------------------------------
+# Apply a command to a package, with error stream redirection instructions.
+#-------------------------------------------------------
+proc apply_to_pkg_err {s errredir} {
+ global pkgname
+ .f.t delete 0.0 end
+ if {$pkgname == ""} {
+ .f.t insert end "You must double click on a package name first!"
+ } else {
+ apply_to_pkg_int "$s $pkgname" "2>&1"
+ }
+}
+proc apply_to_pkg_int {s errredir} {
+ .f.t delete 0.0 end
+ .f.t insert end "Running: $s\n"
+ set f [eval "open {| sh -c \"$s $errredir\" } r"]
+ while {![eof $f]} {
+ .f.t insert end [read $f 64]
+ }
+}
+#-------------------------------------------------------
+# Invoke an arbitrary command.
+#-------------------------------------------------------
+proc do_command {s} {
+ .f.t delete 0.0 end
+ .f.t insert end "Running: $s\n"
+ set f [eval "open {| $s} r"]
+ while {![eof $f]} {
+ .f.t insert end [read $f 64]
+ }
+}
+# local variables:
+# mode: csh
+# compile-command: ""
+# comment-start: "# "
+# comment-start-skip: "# "
+# end:
diff --git a/usr.sbin/pkg_install/version/Makefile b/usr.sbin/pkg_install/version/Makefile
new file mode 100644
index 0000000..f18bedf
--- /dev/null
+++ b/usr.sbin/pkg_install/version/Makefile
@@ -0,0 +1,21 @@
+# $FreeBSD$
+
+PROG= pkg_version
+SRCS= main.c perform.c
+
+CFLAGS+= ${DEBUG} -I${.CURDIR}/../lib
+
+WARNS?= 2
+
+DPADD= ${LIBINSTALL} ${LIBFETCH} ${LIBMD}
+LDADD= ${LIBINSTALL} -lfetch -lmd
+
+.if !defined(NOCRYPT) && !defined(NOSECURE) && !defined(NO_OPENSSL)
+DPADD+= ${LIBSSL} ${LIBCRYPTO}
+LDADD+= -lssl -lcrypto
+.endif
+
+test:
+ ./test-pkg_version.sh
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pkg_install/version/main.c b/usr.sbin/pkg_install/version/main.c
new file mode 100644
index 0000000..cbc6a9e
--- /dev/null
+++ b/usr.sbin/pkg_install/version/main.c
@@ -0,0 +1,89 @@
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jeremy D. Lea.
+ * 11 May 2002
+ *
+ * This is the version module. Based on pkg_version.pl by Bruce A. Mah.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "lib.h"
+#include "version.h"
+#include <err.h>
+
+static char Options[] = "dhl:L:s:tv";
+
+char *LimitChars = NULL;
+char *PreventChars = NULL;
+char *MatchName = NULL;
+
+static void usage __P((void));
+
+int
+main(int argc, char **argv)
+{
+ int ch, cmp = 0;
+
+ if (argc == 4 && !strcmp(argv[1], "-t")) {
+ cmp = version_cmp(argv[2], argv[3]);
+ printf(cmp > 0 ? ">\n" : (cmp < 0 ? "<\n" : "=\n"));
+ exit(0);
+ }
+ else while ((ch = getopt(argc, argv, Options)) != -1) {
+ switch(ch) {
+ case 'v':
+ Verbose = TRUE;
+ break;
+
+ case 'l':
+ LimitChars = optarg;
+ break;
+
+ case 'L':
+ PreventChars = optarg;
+ break;
+
+ case 's':
+ MatchName = optarg;
+ break;
+
+ case 't':
+ errx(2, "Invalid -t usage.");
+ break;
+
+ case 'h':
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ return pkg_perform(argv);
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n",
+ "usage: pkg_version [-hv] [-l limchar] [-L limchar] [-s string] index",
+ " pkg_version -t v1 v2");
+ exit(1);
+}
diff --git a/usr.sbin/pkg_install/version/perform.c b/usr.sbin/pkg_install/version/perform.c
new file mode 100644
index 0000000..fd340ff
--- /dev/null
+++ b/usr.sbin/pkg_install/version/perform.c
@@ -0,0 +1,281 @@
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jeremy D. Lea.
+ * 11 May 2002
+ *
+ * This is the version module. Based on pkg_version.pl by Bruce A. Mah.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "lib.h"
+#include "version.h"
+#include <err.h>
+#include <fetch.h>
+#include <signal.h>
+
+FILE *IndexFile;
+struct index_head Index = SLIST_HEAD_INITIALIZER(Index);
+
+static int pkg_do(char *);
+static void show_version(const char *, const char *, const char *);
+
+/*
+ * This is the traditional pkg_perform, except that the argument is _not_
+ * a list of packages. It is the index file from the command line.
+ *
+ * We loop over the installed packages, matching them with the -s flag
+ * if needed and calling pkg_do(). Before hand we set up a few things,
+ * and after we tear them down...
+ */
+int
+pkg_perform(char **indexarg)
+{
+ char tmp[PATH_MAX], **pkgs;
+ struct index_entry *ie;
+ int i, err_cnt = 0;
+
+ /*
+ * Try to find and open the INDEX. We only check IndexFile != NULL
+ * later, if we actually need the INDEX.
+ */
+ if (*indexarg == NULL)
+ snprintf(tmp, PATH_MAX, "%s/INDEX", PORTS_DIR);
+ else
+ strlcpy(tmp, *indexarg, PATH_MAX);
+ if (isURL(tmp))
+ IndexFile = fetchGetURL(tmp, "");
+ else
+ IndexFile = fopen(tmp, "r");
+
+ /* Get a list of all the installed packages */
+ pkgs = matchinstalled(MATCH_ALL, NULL, &err_cnt);
+ if (err_cnt != 0)
+ errx(2, "Unable to find package database directory!");
+ i = -1;
+ while (pkgs[++i] != NULL) {
+ if (MatchName == NULL || strstr(pkgs[i], MatchName))
+ err_cnt += pkg_do(pkgs[i]);
+ }
+
+ /* If we opened the INDEX in pkg_do(), clean up. */
+ while (!SLIST_EMPTY(&Index)) {
+ ie = SLIST_FIRST(&Index);
+ SLIST_REMOVE_HEAD(&Index, next);
+ free(ie);
+ }
+ if (IndexFile != NULL)
+ fclose(IndexFile);
+
+ return err_cnt;
+}
+
+/*
+ * Traditional pkg_do(). We take the package name we are passed and
+ * first slurp in the CONTENTS file, getting name and origin, then
+ * we look for it's corresponding Makefile. If that fails we pull in
+ * the INDEX, and check there.
+ */
+static int
+pkg_do(char *pkg)
+{
+ char *ch, tmp[PATH_MAX], tmp2[PATH_MAX], *latest = NULL;
+ Package plist;
+ struct index_entry *ie;
+ FILE *fp;
+ size_t len;
+
+ /* Suck in the contents list. */
+ plist.head = plist.tail = NULL;
+ plist.name = plist.origin = NULL;
+ snprintf(tmp, PATH_MAX, "%s/%s/%s", LOG_DIR, pkg, CONTENTS_FNAME);
+ fp = fopen(tmp, "r");
+ if (!fp) {
+ warnx("unable to open %s file", CONTENTS_FNAME);
+ return 1;
+ }
+ read_plist(&plist, fp);
+ fclose(fp);
+
+ /*
+ * First we check if the installed package has an origin, and try
+ * looking for it's Makefile. If we find the Makefile we get the
+ * latest version from there. If we fail, we start looking in the
+ * INDEX, first matching the origin and then the package name.
+ */
+ if (plist.origin != NULL) {
+ snprintf(tmp, PATH_MAX, "%s/%s", PORTS_DIR, plist.origin);
+ if (isdir(tmp) && chdir(tmp) != FAIL && isfile("Makefile")) {
+ if ((latest = vpipe("make -V PKGNAME", tmp)) == NULL)
+ warnx("Failed to get PKGNAME from %s/Makefile!", tmp);
+ else
+ show_version(plist.name, latest, "port");
+ }
+ }
+ if (latest == NULL) {
+ /* We only pull in the INDEX once, if needed. */
+ if (SLIST_EMPTY(&Index)) {
+ if (!IndexFile)
+ errx(2, "Unable to open INDEX in %s.", __func__);
+ while ((ch = fgetln(IndexFile, &len)) != NULL) {
+ /*
+ * Don't use strlcpy() because fgetln() doesn't
+ * return a valid C string.
+ */
+ strncpy(tmp, ch, MIN(len, PATH_MAX));
+ tmp[PATH_MAX-1] = '\0';
+ /* The INDEX has pkgname|portdir|... */
+ if ((ch = strchr(tmp, '|')) != NULL)
+ ch[0] = '\0';
+ if (ch != NULL && (ch = strchr(&ch[1], '|')) != NULL)
+ ch[0] = '\0';
+ /* Look backwards for the last two dirs = origin */
+ while (ch != NULL && *--ch != '/')
+ if (ch[0] == '\0')
+ ch = NULL;
+ while (ch != NULL && *--ch != '/')
+ if (ch[0] == '\0')
+ ch = NULL;
+ if (ch == NULL)
+ errx(2, "The INDEX does not appear to be valid!");
+ if ((ie = malloc(sizeof(struct index_entry))) == NULL)
+ errx(2, "Unable to allocate memory in %s.", __func__);
+ strlcpy(ie->name, tmp, PATH_MAX);
+ strlcpy(ie->origin, &ch[1], PATH_MAX);
+ /* Who really cares if we reverse the index... */
+ SLIST_INSERT_HEAD(&Index, ie, next);
+ }
+ }
+ /* Now that we've slurped in the INDEX... */
+ SLIST_FOREACH(ie, &Index, next) {
+ if (plist.origin != NULL) {
+ if (strcmp(plist.origin, ie->origin) == 0)
+ latest = strdup(ie->name);
+ } else {
+ strlcpy(tmp, ie->name, PATH_MAX);
+ strlcpy(tmp2, plist.name, PATH_MAX);
+ /* Chop off the versions and compare. */
+ if ((ch = strrchr(tmp, '-')) == NULL)
+ errx(2, "The INDEX does not appear to be valid!");
+ ch[0] = '\0';
+ if ((ch = strrchr(tmp2, '-')) == NULL)
+ warnx("%s is not a valid package!", plist.name);
+ else
+ ch[0] = '\0';
+ if (strcmp(tmp2, tmp) == 0) {
+ if (latest != NULL) {
+ /* Multiple matches */
+ snprintf(tmp, PATH_MAX, "%s|%s", latest, ie->name);
+ free(latest);
+ latest = strdup(tmp);
+ } else
+ latest = strdup(ie->name);
+ }
+ }
+ }
+ if (latest == NULL)
+ show_version(plist.name, NULL, plist.origin);
+ else
+ show_version(plist.name, latest, "index");
+ }
+ if (latest != NULL)
+ free(latest);
+ free_plist(&plist);
+ return 0;
+}
+
+#define OUTPUT(c) ((PreventChars != NULL && !strchr(PreventChars, (c))) || \
+ (LimitChars != NULL && strchr(LimitChars, (c))) || \
+ (PreventChars == NULL && LimitChars == NULL))
+
+/*
+ * Do the work of comparing and outputing. Ugly, but well that's what
+ * You get when you try to match perl output in C ;-).
+ */
+void
+show_version(const char *installed, const char *latest, const char *source)
+{
+ char *ch, tmp[PATH_MAX];
+ const char *ver;
+ int cmp = 0;
+
+ if (!installed || strlen(installed) == 0)
+ return;
+ strlcpy(tmp, installed, PATH_MAX);
+ if (!Verbose) {
+ if ((ch = strrchr(tmp, '-')) != NULL)
+ ch[0] = '\0';
+ }
+ if (latest == NULL) {
+ if (source == NULL && OUTPUT('!')) {
+ printf("%-34s !", tmp);
+ if (Verbose)
+ printf(" Comparison failed");
+ printf("\n");
+ } else if (source != NULL && OUTPUT('?')) {
+ printf("%-34s ?", tmp);
+ if (Verbose)
+ printf(" orphaned: %s", source);
+ printf("\n");
+ }
+ } else if (strchr(latest,'|') != NULL) {
+ if (OUTPUT('*')) {
+ printf("%-34s *", tmp);
+ if (Verbose) {
+ strlcpy(tmp, latest, PATH_MAX);
+ ch = strchr(tmp, '|');
+ ch[0] = '\0';
+
+ ver = version_of(tmp, NULL, NULL);
+ printf(" multiple versions (index has %s", ver);
+ do {
+ ver = version_of(&ch[1], NULL, NULL);
+ if ((ch = strchr(&ch[1], '|')) != NULL)
+ ch[0] = '\0';
+ printf(", %s", ver);
+ } while (ch != NULL);
+ printf(")");
+ }
+ printf("\n");
+ }
+ } else {
+ cmp = version_cmp(installed, latest);
+ ver = version_of(latest, NULL, NULL);
+ if (cmp < 0 && OUTPUT('<')) {
+ printf("%-34s <", tmp);
+ if (Verbose)
+ printf(" needs updating (%s has %s)", source, ver);
+ printf("\n");
+ } else if (cmp == 0 && OUTPUT('=')) {
+ printf("%-34s =", tmp);
+ if (Verbose)
+ printf(" up-to-date with %s", source);
+ printf("\n");
+ } else if (cmp > 0 && OUTPUT('>')) {
+ printf("%-34s >", tmp);
+ if (Verbose)
+ printf(" succeeds %s (%s has %s)", source, source, ver);
+ printf("\n");
+ }
+ }
+}
+
+void
+cleanup(int sig)
+{
+ if (sig)
+ exit(1);
+}
diff --git a/usr.sbin/pkg_install/version/pkg_version.1 b/usr.sbin/pkg_install/version/pkg_version.1
new file mode 100644
index 0000000..e252210
--- /dev/null
+++ b/usr.sbin/pkg_install/version/pkg_version.1
@@ -0,0 +1,202 @@
+.\"
+.\" Copyright 1998 Bruce A. Mah
+.\"
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.Dd July 17, 1998
+.Dt PKG_VERSION 1
+.Os
+.Sh NAME
+.Nm pkg_version
+.Nd summarize installed versions of packages
+.Sh SYNOPSIS
+.Nm
+.Op Fl hv
+.Op Fl l Ar limchar
+.Op Fl L Ar limchar
+.Op Fl s Ar string
+.Op Ar index
+.Nm
+.Op Fl t Ar version1 version2
+.Sh DESCRIPTION
+The
+.Nm
+command is used to produce a report of non-base software packages
+installed using the
+.Xr pkg_add 1
+command.
+.Pp
+Each package's version number is checked against one of two sources to
+see if that package may require updating. If the package contains
+information about its origin in the
+.Fx
+ports tree, and a version number can be determined from the port's
+.Pa Makefile ,
+then the version number from the
+.Pa Makefile
+will be used to determine whether the installed package is up-to-date
+or requires updating.
+.Pp
+If no origin for a package can be found, or if the port's
+.Pa Makefile
+cannot be located,
+.Nm
+will search for the package in the ports collection index file
+(typically
+.Pa /usr/ports/INDEX ) .
+Any matching version number(s) there will be used to determine whether
+the installed package is up-to-date or requires updating.
+.Pp
+Generally, using the version number from a port's
+.Pa Makefile
+will provide a more accurate result, since, unlike the index file, it
+provides an unambiguous current version number, even when multiple
+versions of a port exist in the ports collection.
+Moreover, the ports collection index file is only updated at
+intervals, meaning that it may not completely reflect the version
+numbers of the software contained in the ports collection.
+.Pp
+Each package name is printed, along with a one-character status flag:
+.Bl -tag -width indent
+.It Li =
+The installed version of the package is current.
+.It Li \&<
+The installed version of the package is older than the current version.
+.It Li \&>
+The installed version of the package is newer than the current version.
+This situation can arise with an out-of-date index file, or when
+testing new ports.
+.It Li \&?
+The installed package does not appear in the index.
+This could be due to an out of date index or a package taken from a PR
+that has not yet been committed.
+.It Li *
+There are multiple versions of a particular software package
+listed in the index file.
+Examples from the
+.Fx
+ports collection are the Tcl toolkit or the
+.Tn EMACS
+editor.
+.It Li \&!
+The installed package exists in the index but for some reason,
+.Nm
+was unable to compare the version number of the installed package
+with the corresponding entry in the index.
+.El
+.Sh OPTIONS
+The
+.Nm
+utility supports several command-line arguments:
+.Bl -tag -width indent
+.It Fl h
+Print help message.
+.It Fl l
+Limit the output to those packages whose status flag matches the
+character(s) in
+.Ar limchar .
+More than one character can be specified in
+.Ar limchar .
+Note that because some of the status flag characters are also special
+to the shell, it is best to quote
+.Ar limchar
+with single quotes.
+.It Fl L
+Limit the output to those packages whose status flag doesn't match
+.Ar limchar .
+You may specify more than one character to match in
+.Ar limchar .
+Note that because some of the status flag characters are also special
+to the shell, it is best to quote
+.Ar limchar
+with single quotes.
+.It Fl s
+Limit the output to those packages whose names match a given
+.Ar string .
+.It Fl t
+Test a pair of version number strings and exit.
+The output consists of one of the single characters
+.Li =
+(equal),
+.Li \&<
+(right-hand number greater), or
+.Li \&>
+(left-hand number greater) on standard output.
+This flag is mostly useful for scripts or for testing.
+.It Fl v
+Enable verbose output. Verbose output includes some English-text
+interpretations of the version number comparisons, as well as the
+version numbers compared for each package. Non-verbose output is
+probably easier for programs or scripts to parse.
+.It Ar index
+Specify the index to be used as a basis of comparison. This index can
+be specified as a filename (in the local filesystem) or a URL. Any
+URL understandable by
+.Xr fetch 1
+can be used here. If no
+.Ar index
+file is specified on the command line,
+.Pa /usr/ports/INDEX
+is used.
+.El
+.Sh SEE ALSO
+.Xr fetch 1 ,
+.Xr pkg_add 1 ,
+.Xr pkg_create 1 ,
+.Xr pkg_delete 1 ,
+.Xr pkg_info 1
+.Sh FILES
+.Bl -tag -width /usr/ports/INDEX -compact
+.It Pa /usr/ports/INDEX
+Default index file.
+.El
+.Sh EXAMPLES
+The following is a typical invocation of the
+.Nm
+command, which checks the installed packages against the local ports
+index file:
+.Pp
+.Dl % pkg_version -v
+.Pp
+The command below generates a report against
+the version numbers in the on-line ports collection:
+.Pp
+.Dl % pkg_version ftp://ftp.FreeBSD.org/pub/FreeBSD/branches/-current/ports/INDEX
+.Pp
+The following command compares two package version strings:
+.Pp
+.Dl % pkg_version -t 1.5 1.5.1
+.Sh AUTHORS
+The
+.Nm
+utility was written by
+.An Jeremy D. Lea Aq reg@FreeBSD.org ,
+partially based on a Perl script written by
+.An Bruce A. Mah Aq bmah@FreeBSD.org .
+.Sh CONTRIBUTORS
+.An Nik Clayton Aq nik@FreeBSD.org ,
+.An Dominic Mitchell Aq dom@palmerharvey.co.uk ,
+.An Mark Ovens Aq marko@FreeBSD.org ,
+.An Doug Barton Aq DougB@gorean.org ,
+.An Akinori MUSHA Aq knu@FreeBSD.org
diff --git a/usr.sbin/pkg_install/version/test-pkg_version.sh b/usr.sbin/pkg_install/version/test-pkg_version.sh
new file mode 100755
index 0000000..0651047
--- /dev/null
+++ b/usr.sbin/pkg_install/version/test-pkg_version.sh
@@ -0,0 +1,75 @@
+#!/bin/sh
+#
+# Copyright 2001 Bruce A. Mah
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# test-pkg_version.sh
+#
+# Regression testing for pkg_version
+# Originally from an idea by "Akinori MUSHA" <knu@iDaemons.org>
+#
+# $FreeBSD$
+#
+
+ECHO=echo
+PKG_VERSION=./pkg_version
+
+test-pv ( ) { \
+ setvar v1 $1
+ setvar answer $2
+ setvar v2 $3
+ setvar type $4
+ res=`${PKG_VERSION} -t ${v1} ${v2}`
+ if [ ${res} != ${answer} ]; then \
+ ${ECHO} "${type} test failed (${v1} ${res} ${v2}, should have been ${answer})"; \
+ fi
+}
+
+# Test coercion of default PORTREVISION and PORTEPOCH
+test-pv 0.10 "=" 0.10_0 coercion
+test-pv 0.10 "=" 0.10,0 coercion
+test-pv 0.10 "=" 0.10_0,0 coercion
+
+# Test various comparisons
+test-pv 1.0 "=" 1.0 equality
+test-pv 2.15a "=" 2.15a equality
+
+test-pv 0.10 ">" 0.9 inequality
+test-pv 0.9 "<" 0.10 inequality
+
+test-pv 2.3p10 ">" 2.3p9 number/letter
+test-pv 1.6.0 ">" 1.6.0.p3 number/letter
+test-pv 1.0.b ">" 1.0.a3 number/letter
+test-pv 1.0a ">" 1.0 number/letter
+test-pv 1.0a "<" 1.0b number/letter
+test-pv 5.0a ">" 5.0.b number/letter
+
+test-pv 1.5_1 ">" 1.5 portrevision
+test-pv 1.5_2 ">" 1.5_1 portrevision
+test-pv 1.5_1 "<" 1.5.0.1 portrevision
+test-pv 1.5 "<" 1.5.0.1 portrevision
+
+test-pv 00.01.01,1 ">" 99.12.31 portepoch
+test-pv 0.0.1_1,2 ">" 0.0.1,2 portrevision/portepoch
+test-pv 0.0.1_1,3 ">" 0.0.1_2,2 portrevision/portepoch
diff --git a/usr.sbin/pkg_install/version/version.h b/usr.sbin/pkg_install/version/version.h
new file mode 100644
index 0000000..6ed6511
--- /dev/null
+++ b/usr.sbin/pkg_install/version/version.h
@@ -0,0 +1,44 @@
+/* $FreeBSD$ */
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jeremy D. Lea.
+ * 11 May 2002
+ *
+ * This is the version module. Based on pkg_version.pl by Bruce A. Mah.
+ *
+ */
+
+#ifndef _INST_VERSION_H_INCLUDE
+#define _INST_VERSION_H_INCLUDE
+
+/* Where the ports lives by default */
+#define DEF_PORTS_DIR "/usr/ports"
+/* just in case we change the environment variable name */
+#define PORTSDIR "PORTSDIR"
+/* macro to get name of directory where we put logging information */
+#define PORTS_DIR (getenv(PORTSDIR) ? getenv(PORTSDIR) : DEF_PORTS_DIR)
+
+struct index_entry {
+ SLIST_ENTRY(index_entry) next;
+ char name[PATH_MAX];
+ char origin[PATH_MAX];
+};
+SLIST_HEAD(index_head, index_entry);
+
+extern char *LimitChars;
+extern char *PreventChars;
+extern char *MatchName;
+
+#endif /* _INST_VERSION_H_INCLUDE */
diff --git a/usr.sbin/pnpinfo/Makefile b/usr.sbin/pnpinfo/Makefile
new file mode 100644
index 0000000..24f87a6
--- /dev/null
+++ b/usr.sbin/pnpinfo/Makefile
@@ -0,0 +1,19 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../contrib/pnpinfo
+
+PROG= pnpinfo
+MAN= pnpinfo.8
+SRCS= pnpinfo.c
+
+CFLAGS+= -I${.CURDIR}/../../sys
+
+.if ${MACHINE} == "pc98"
+CFLAGS+= -DPC98
+.endif
+
+.if ${MACHINE_ARCH} == "alpha"
+LDADD+= -lio
+.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ppp/Makefile b/usr.sbin/ppp/Makefile
new file mode 100644
index 0000000..86a22ab
--- /dev/null
+++ b/usr.sbin/ppp/Makefile
@@ -0,0 +1,108 @@
+# $FreeBSD$
+
+PROG= ppp
+MAN= ppp.8
+SRCS= acf.c arp.c async.c auth.c bundle.c cbcp.c ccp.c chap.c chat.c \
+ command.c datalink.c deflate.c defs.c exec.c filter.c fsm.c hdlc.c \
+ iface.c ip.c ipcp.c ipv6cp.c iplist.c lcp.c link.c log.c lqr.c main.c \
+ mbuf.c mp.c ncp.c ncpaddr.c pap.c physical.c pred.c probe.c prompt.c \
+ proto.c route.c server.c sig.c slcompress.c sync.c systems.c tcp.c \
+ tcpmss.c throughput.c timer.c tty.c tun.c udp.c vjcomp.c
+.if defined(RELEASE_CRUNCH)
+CFLAGS+=-DRELEASE_CRUNCH
+NOATM= true
+NOI4B= true
+NONAT= true
+NOKLDLOAD= true
+NORADIUS= true
+NOSECURE= true
+NOSUID= true
+.endif
+
+.if defined(NOSUID) || defined(PPP_NOSUID)
+BINMODE=554
+.else
+BINMODE=4554
+BINOWN= root
+.endif
+BINGRP= network
+M4FLAGS=
+
+LDADD= -lcrypt -lmd -lutil -lz
+DPADD= ${LIBCRYPT} ${LIBMD} ${LIBUTIL} ${LIBZ}
+
+.SUFFIXES: .8 .8.m4
+
+.8.m4.8:
+ m4 ${M4FLAGS} ${.IMPSRC} >${.TARGET}
+
+CLEANFILES= ppp.8
+
+.if defined(PPP_CONFDIR) && !empty(PPP_CONFDIR)
+CFLAGS+=-DPPP_CONFDIR=\"${PPP_CONFDIR}\"
+.endif
+
+.if defined(NOKLDLOAD)
+CFLAGS+=-DNOKLDLOAD
+.endif
+
+.if defined(NOINET6)
+CFLAGS+=-DNOINET6
+.endif
+
+.if defined(NOALIAS) || defined(NONAT)
+CFLAGS+=-DNONAT
+.else
+SRCS+= nat_cmd.c
+LDADD+= -lalias
+DPADD+= ${LIBALIAS}
+.endif
+
+.if defined(NOATM)
+CFLAGS+=-DNOATM
+.else
+SRCS+= atm.c
+.endif
+
+.if defined(NOSUID) || defined(PPP_NOSUID)
+CFLAGS+=-DNOSUID
+.else
+SRCS+= id.c
+.endif
+
+.if !exists(${.CURDIR}/../../secure) || defined(NOCRYPT) || defined(NOSECURE) || defined(NO_OPENSSL) || defined(NODES)
+CFLAGS+=-DNODES
+.else
+DISTRIBUTION=crypto
+SRCS+= chap_ms.c mppe.c
+LDADD+= -lcrypto
+DPADD+= ${LIBCRYPTO}
+.endif
+
+.if defined(NORADIUS)
+CFLAGS+=-DNORADIUS
+.else
+SRCS+= radius.c
+LDADD+= -lradius
+DPADD+= ${LIBRADIUS}
+.endif
+
+.if defined(NOI4B) || ${MACHINE_ARCH} != "i386"
+CFLAGS+=-DNOI4B
+.else
+SRCS+= i4b.c
+.endif
+
+.if defined(NONETGRAPH)
+CFLAGS+=-DNONETGRAPH
+.else
+SRCS+= ether.c
+LDADD+= -lnetgraph
+DPADD+= ${LIBNETGRAPH}
+.if defined(EXPERIMENTAL_NETGRAPH)
+CFLAGS+=-DEXPERIMENTAL_NETGRAPH
+SRCS+= netgraph.c
+.endif
+.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ppp/README.changes b/usr.sbin/ppp/README.changes
new file mode 100644
index 0000000..5b8a7cd
--- /dev/null
+++ b/usr.sbin/ppp/README.changes
@@ -0,0 +1,140 @@
+Copyright (c) 2001 Brian Somers <brian@Awfulhak.org>
+ based on work by Eivind Eklund <perhaps@yes.no>,
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+$FreeBSD$
+
+This file summarises changes made to ppp that effect
+its configuration.
+
+It does not describe new features, rather it attempts
+to answer any `this used to work, why doesn't it now?'
+questions.
+
+o The `set debug' command was replaced with `set log'.
+o The `set log LCP' command was split into LCP, IPCP and CCP logs.
+o Syslogd is used for logging. /etc/syslog.conf must be updated.
+o LQR is disabled by default.
+o Openmode is active by default.
+o Users must be a member of group `network' for ppp access. Furthermore,
+ they must be `allow'ed to run ppp via the `allow' command in the
+ configuration file.
+ For a brief period, ppp could only be run as root.
+o No diagnostic socket is created by default. The `set server' command
+ must be used.
+o The diagnostic socket password must be specified *only* on the `set
+ server' command line.
+o When `set server' is used to re-select a diagnostic port, all existing
+ diagnostic connections are dropped.
+o pppd-deflate is now called deflate24.
+o Filter IPs of 0.0.0.0 have a default width of 0, not 32.
+o Errors in `add' and `delete' are logged as warnings rather than being
+ written to the TCP/IP log.
+o Any number of diagnostic prompts are allowed, and they are allowed in
+ interactive mode.
+o The default `device' is cuaa1, then cuaa0
+o A password of "*" in ppp.secret causes a passwd database lookup in
+ pap mode.
+o The value of the CONNECT environment variable is logged in the
+ utmp host field in -direct mode.
+o Out-of-sequence FSM packets (IPCP/LCP/CCP) are dropped by default.
+o Reconnect values are used after an LQR timeout.
+o ^C works on the parent in -background mode.
+o The dial/call/open command works asynchronously. As a result, prompts
+ do not lose control while dialing.
+o The `display' command has been removed. All information is available
+ with the appropriate `show' command.
+o Msext does not need to be enabled/disabled. Setting the NBNS (set nbns)
+ will auto enable it. The DNS side may be enabled/disabled, and if
+ enabled without a `set dns' (was `set ns') will use values from
+ /etc/resolv.conf.
+o Filters are now called `allow', `dial', `in' and `out'. `set
+ ifilter ...' becomes `set filter in ...' etc.
+o Authname and Authkey may only be `set' in phase DEAD.
+o Set encrypt is no longer necessary. Ppp will respond to M$CHAP
+ servers correctly if it's built with DES.
+o Throughput statistics are enabled by default.
+o `Set stopped' only has two parameters. It's no longer possible to
+ have an IPCP stopped timer.
+o `Set timeout' only has one or two parameters. Use `set lqrperiod' and
+ `set {lcp,ccp,ipcp,chap,pap}retry' for the other timers. These timeout
+ values can be seen using the relevant show commands.
+o `set loopback' is now `enable/disable loopback'.
+o `show auto', `show loopback' and `show mtu' are all part of `show bundle'.
+o `show mru' is part of `show lcp'
+o `show msext' and `show vj' are part of `show ipcp'
+o `show reconnect' and `show redial' are part of `show link'
+o A signal 15 (TERM) will now shut down the link gracefully.
+o A signal 2 (HUP) will drop all links immediately.
+o Signal 30 (USR1) is now ignored.
+o Add & delete commands are not necessary in ppp.linkup if they are
+ `sticky routes' (ie, contain MYADDR or HISADDR).
+o LINK and CARRIER logging are no longer available.
+o Timer based DEBUG messages are now logged in the new TIMER log.
+o Ppp can use tun devices > tun255.
+o Protocol-compressed packets are accepted even if they were denied
+ at LCP negotiation time.
+o Passwords aren't logged when logging the ``set server'' line.
+o Command line options only need enough characters to uniquely identify
+ them. -a == -auto, -dd == -ddial etc. -interactive is also allowed.
+o If you don't like seeing additional interface aliases when running in
+ -auto -alias mode, add ``iface clear'' to your ppp.linkdown file -
+ check the sample file.
+o Ppp waits for 1 second before checking whether the device supports
+ carrier. This is controllable with ``set cd''.
+o Random dial timeouts are now between 1 and 30 seconds inclusive rather
+ than between 0 and 29.
+o Ppp now accepts M$CHAP (as well as normal CHAP) by default. If this
+ is not required, you must ``deny chap05 chap80''.
+o The ``set device'' command now expects each device to be specified as an
+ argument rather than concatentating all arguments and splitting based
+ on commas and spaces.
+o The ``show modem'' command is deprecated and has been changed to
+ ``show physical''.
+o The words ``host'' and ``port'' are no longer accepted by the ``set filter''
+ command. Removing them should yield the same results as before.
+o The ``set weight'' command has been deprecated. The ``set bandwidth''
+ command should now be used instead.
+o The ``set autoload'' command syntax and implementation have changed as the
+ old implementation was mis-designed and dysfunctional.
+o Ppp now waits either the full ``set cd'' time or until carrier is detected
+ before running the login script (whichever comes first).
+o The -alias flag has been deprecated. The -nat flag should be used instead.
+o Unbalanced quotes in commands are now warned about and the entire command
+ is ignored.
+o It is now only necessary to escape the `-' character in chat scripts twice.
+ See the example files for details.
+o Environment variables and ~ are expanded on in commands
+o ``nat pptp'' is no longer necessary as this is now done transparently
+o The ``!'' at the start of chat scripts and authkey can be made literal
+ (rather than meaning execute) by doubling it to ``!!''.
+o MP autoload throughput measurements are now based on the maximum of input
+ and output averages rather than on the total.
+o When only one link is open in MP mode, MP link level compression is not
+ open and the peer MRU >= the peer MRRU, ppp sends outbound traffic as
+ PROTO_IP traffic rather than PROTO_MP.
+o MSCHAPv2 is now accepted by default. If you don't wish to negotiate
+ this, you must explicitly deny it.
+o MPPE is enabled and accepted by default (although deflate and predictor1
+ are preferred.
diff --git a/usr.sbin/ppp/README.devel b/usr.sbin/ppp/README.devel
new file mode 100644
index 0000000..69230e1
--- /dev/null
+++ b/usr.sbin/ppp/README.devel
@@ -0,0 +1,47 @@
+Copyright (c) 2001 Brian Somers <brian@Awfulhak.org>
+ based on work by Eivind Eklund <perhaps@yes.no>,
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+$FreeBSD$
+
+This file summarises changes made to ppp that effect
+This program was originally written by Toshiharu OHNO <tony-o@iij.ad.jp>,
+and was submitted to FreeBSD-2.0.5 by Atsushi Murai <amurai@spec.co.jp>.
+The original version was usually referred to as iij-ppp.
+
+Ppp is currently maintained under FreeBSD and OpenBSD by Brian Somers
+<brian@Awfulhak.org>. The sources for both operating systems are the
+same except that the libalias code is built directly into ppp under
+OpenBSD, and the Makefiles vary per OS.
+
+The latest sources are available in FreeBSD-current and OpenBSD-current.
+An archive hacked so that it will build on just about any version of
+FreeBSD or OpenBSD is frequently generated and made available via
+http://www.Awfulhak.org/ppp.html.
+
+A FAQ is available at http://www.FreeBSD.org/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..9ff240a9
--- /dev/null
+++ b/usr.sbin/ppp/README.nat
@@ -0,0 +1,378 @@
+Copyright (c) 2001 Charles Mott <cm@linktel.net>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+$FreeBSD$
+
+User PPP NAT (Packet Aliasing)
+
+
+
+0. Contents
+ 1. Background
+ 2. Setup
+ 3. New commands in ppp
+ 4. Future Work
+ 5. Authors / Acknowledgements
+ 6. Revision History for Aliasing Code
+
+
+
+1. Background
+
+User mode ppp has embedded NAT (Network Address Translation) code.
+Enabling this, either by the "-nat" command line option or the
+"nat enable yes" command in a ppp.conf file, makes the ppp host
+automatically NAT IP packets forwarded from a local network, making
+them appear to come from the ppp host machine. Incoming packets
+from the outside world are then appropriately de-NAT'd.
+
+The process of NAT'ing involves both the IP address and the TCP or UDP
+port numbers. ICMP echo and timestamp packets are natted by their id
+numbers. ICMP error messages can be properly directed by examining the
+fragment of the offending packet which is contained in the body of the
+message.
+
+This software was specifically meant to support users who have
+unregistered, private address IP networks (e.g. 192.168.0.x or 10.0.0.x
+addresses). The ppp host can act as a gateway for these networks, and
+computers on the local area net will have some degree of Internet access
+without the need for a registered IP address. Additionally, there will
+be no need for an Internet service provider to maintain routing tables
+for the local area network.
+
+A disadvantage of NAT is that machines on the local network,
+behind the ppp host, are not visible from the outside world. They can
+establish TCP connections and make UDP inquiries (such as domain name
+service requests) but the connections seem to come from the ppp host
+itself. There is, in effect, a partial firewall. Of course, if this is
+what you want, the disadvantage becomes an advantage.
+
+A second disadvantage is that "IP encoding" protocols, which send IP
+address or port information within the data stream, are not supported
+for the cases where exception code exists. This implementation has
+workarounds for FTP and IRC DCC, the most well known of the IP encoding
+protocols. This frees users from depending on using the ftp passive
+mode and avoiding IRC DCC sends, as is sometimes the case with other
+masquerading solutions.
+
+The implementation supports all standard, non-encoding TCP and UDP protocols.
+Examples of these protocols are http, gopher and telnet. The standard UDP
+mode of Real-Audio is not presently supported, but the TCP mode does work
+correctly.
+
+The NAT code also handles many ICMP messages. In particular,
+ping and traceroute are supported.
+
+
+
+2. Packet Aliasing Setup
+
+It is recommended that users first verify correct ppp operation without
+NAT enabled. This will confirm that the ppp.conf file is
+properly set up and that there are no ppp problems. Then start ppp with
+the "-nat" option on the command line. The user should verify that
+the ppp host can correctly connect to the Internet in NAT
+mode. Finally, check that machines on the private network can access
+the Internet.
+
+The NAT software handles all packets, whether they come from
+the host or another computer on the local area network. Thus, a correctly
+operating ppp host indicates that the software should work properly for
+other computers on the private network.
+
+If the ppp host can access the Internet, but other computers on the local
+network cannot, check that IP forwarding is enabled on the ppp host. Also,
+verify that the other computers use this machine as a gateway. Of course,
+you should also verify that machines within the local area network
+communicate properly. A common error is inconsistent subnet addresses
+and masks.
+
+
+
+3. New commands in ppp
+
+In order to control NAT behaviour in a simple manner (no need for
+recompilation), a new command has been added to ppp: nat. This
+is in addition to the -nat command line option. System managers and
+more experienced users may prefer to use the ppp command syntax
+within the ppp.conf file. The nat command also allows NAT
+behaviour to be more precisely specified.
+
+The decision to add a command instead of extending 'set' or 'option' was
+to make obvious that these options only work when NAT is enabled.
+
+The syntax for 'nat' is
+
+ ppp> nat option [yes|no]
+
+where option is given by one of the following templates.
+
+
+ - nat enable [yes|no] (default no)
+
+Enable NAT functionality. If disabled, no other NAT
+options will have any effect. You should usually enable NAT
+before routing any packets over the link; good points are in the
+initial script or right before adding a route. If you do not always
+want NAT, consider using the -nat option to ppp instead of this
+command.
+
+
+ - nat deny_incoming [yes|no] (default yes)
+
+Set to "yes" to disable all incoming connections. This just drops
+connections to, for example, ftp, telnet or web servers. The NAT
+mechanism prevents these connections. Technically, this option denies
+all incoming TCP and UDP requests, making the NAT software a
+fairly efficient one-way firewall. The default is no, which will allow
+all incoming connections to telnetd, ftpd, etc.
+
+
+ - nat log [yes|no]
+
+Controls logging of NAT link creation to "/var/log/alias.log" - this
+is usually only useful if debugging a setup, to see if the bug is in
+the PPP NATing. The debugging information is fairly limited, listing
+the number of NAT links open for different protocols.
+
+
+ - nat same_ports [yes|no] (default yes)
+
+When a connection is being established going through the NAT
+routines, it will normally have its port number changed to allow the
+NAT code to track it. If same_ports is enabled, the NAT
+software attempts to keep the connection's source port unchanged.
+This will allow rsh, RPC and other specialised protocols to work
+_most of the time_, at least on the host machine. Please, do not
+report this being unstable as a bug - it is a result of the way
+NAT has to work. TCP/IP was intended to have one IP address
+per machine.
+
+
+ - nat use_sockets [yes|no] (default yes)
+
+This is a fairly obscure option. For the most part, the NAT
+software does not have to allocate system sockets when it chooses a
+NAT port number. Under very specific circumstances, FTP data
+connections (which don't know the remote port number, though it is
+usually 20) and IRC DCC send (which doesn't know either the address or
+the port from which the connection will come), there can potentially be
+some interference with an open server socket having the same port number
+on the ppp host machine. This possibility for interference only exists
+until the TCP connection has been acknowledged on both sides. The safe
+option is yes, though fewer system resources are consumed by specifying
+no.
+
+
+ - nat unregistered_only [yes|no] (default no)
+
+NAT normally remaps all packets coming from the local area
+network to the ppp host machine address. Set this option to only map
+addresses from the following standard ranges for private, unregistered
+addresses:
+
+ 10.0.0.0 -> 10.255.255.255
+ 172.16.0.0 -> 172.31.255.255
+ 192.168.0.0 -> 192.168.255.255 */
+
+In the instance that there is a subnet of public addresses and another
+subnet of private addresses being routed by the ppp host, then only the
+packets on the private subnet will be NAT'd.
+
+
+- nat port <proto> <local addr>:<port> <nat port>
+
+This command allows incoming traffic to <nat port> on the host
+machine to be redirected to a specific machine and port on the
+local area network. One example of this would be:
+
+ nat port tcp 192.168.0.4:telnet 8066
+
+All traffic to port 8066 of the ppp host would then be sent to
+the telnet port (23) of machine 192.168.0.4. Port numbers
+can either be designated numerically or by symbolic names
+listed in /etc/services. Similarly, addresses can be either
+in dotted quad notation or in /etc/hosts.
+
+
+- nat addr <local addr> <public addr>
+
+This command allows traffic for a public IP address to be
+redirected to a machine on the local network. This function
+is known as "static NAT". An address assignment of 0 refers
+to the default address of the ppp host. Normally static
+NAT is useful if your ISP has allocated a small block of
+IP addresses to the user, but it can even be used in the
+case of a single, dynamically allocated IP address:
+
+ nat addr 10.0.0.8 0
+
+The above command would redirect all incoming traffic to
+machine 10.0.0.8.
+
+If several address NATs specify the same public address
+as follows
+
+ nat addr 192.168.0.2 public_addr
+ nat addr 192.168.0.3 public_addr
+ nat addr 192.168.0.4 public_addr
+
+then incoming traffic will be directed to the last
+translated local address (192.168.0.4), but outgoing
+traffic to the first two addresses will still be NAT'd
+to the specified public address.
+
+
+
+4. Future Work
+
+What is called NAT here has been variously called masquerading, packet
+aliasing and transparent proxying by others. It is an extremely useful
+function to many users, but it is also necessarily imperfect. The
+occasional IP-encoding protocols always need workarounds (hacks).
+Users who are interested in supporting new IP-encoding protocols
+can follow the examples of alias_ftp.c and alias_irc.c.
+
+ICMP error messages are currently handled only in the incoming direction.
+A handler needs to be added to correctly NAT outgoing error messages.
+
+IRC and FTP exception handling make reasonable, though not strictly correct
+assumptions, about how IP encoded messages will appear in the control
+stream. Programmers may wish to consider how to make this process more
+robust.
+
+The NAT engine (alias.c, alias_db.c, alias_ftp.c, alias_irc.c
+and alias_util.c) runs in user space, and is intended to be both portable
+and reusable for interfaces other than ppp. To access the basic engine
+only requires four simple function calls (initialisation, communication of
+host address, outgoing NAT and incoming de-NATing).
+
+
+
+5. Authors / Acknowledgements
+
+Charles Mott (cm@linktel.net) <versions 1.0 - 1.8, 2.0, 2.1>
+Eivind Eklund (perhaps@yes.no) <versions 1.8b - 1.9, new ppp commands>
+
+Listed below, in chronological order, are individuals who have provided
+valuable comments and/or debugging assistance.
+
+ Gary Roberts
+ Tom Torrance
+ Reto Burkhalter
+ Martin Renters
+ Brian Somers
+ Paul Traina
+ Ari Suutari
+ J. Fortes
+ Andrzej Bialeki
+
+
+
+6. Revision History for Aliasing Code
+
+Version 1.0: August 11, 1996 (cjm)
+
+Version 1.1: August 20, 1996 (cjm)
+ PPP host accepts incoming connections for ports 0 to 1023.
+
+Version 1.2: September 7, 1996 (cjm)
+ Fragment handling error in alias_db.c corrected.
+
+Version 1.3: September 15, 1996 (cjm)
+ - Generalised mechanism for handling incoming connections
+ (no more 0 to 1023 restriction).
+ - Increased ICMP support (will handle traceroute now).
+ - Improved TCP close connection logic.
+
+Version 1.4: September 16, 1996
+ Can't remember (this version only lasted a day -- cjm).
+
+Version 1.5: September 17, 1996 (cjm)
+ Corrected error in handling incoming UDP packets
+ with zero checksum.
+
+Version 1.6: September 18, 1996
+ Simplified ICMP data storage. Will now handle
+ tracert from Win95 as well as FreeBSD traceroute.
+
+Version 1.7: January 9, 1997 (cjm)
+ - Reduced malloc() activity for ICMP echo and
+ timestamp requests.
+ - Added handling for out-of-order IP fragments.
+ - Switched to differential checksum computation
+ for IP headers (TCP, UDP and ICMP checksums
+ were already differential).
+ - Accepts FTP data connections from other than
+ port 20. This allows one ftp connections
+ from two hosts which are both running packet
+ aliasing.
+
+Version 1.8: January 14, 1997 (cjm)
+ - Fixed data type error in function StartPoint()
+ in alias_db.c (this bug did not exist before v1.7)
+
+Version 1.8b: January 16, 1997 (Eivind Eklund <perhaps@yes.no>)
+ - Upgraded base PPP version to be the source code from
+ FreeBSD 2.1.6, with additional security patches. This
+ version should still be possible to run on 2.1.5, though -
+ I've run it with a 2.1.5 kernel without problems.
+ (Update done with the permission of cjm)
+
+Version 1.9: February 1, 1997 (Eivind Eklund <perhaps@yes.no>)
+ - Added support for IRC DCC (ee)
+ - Changed the aliasing routines to use ANSI style throughout -
+ minor API changes for integration with other programs than PPP (ee)
+ - Changed the build process, making all options switchable
+ from the Makefile (ee)
+ - Fixed minor security hole in alias_ftp.c for other applications
+ of the aliasing software. Hole could _not_ manifest in
+ PPP+pktAlias, but could potentially manifest in other
+ applications of the aliasing. (ee)
+ - Connections initiated from packet aliasing host machine will
+ not have their port number aliased unless it conflicts with
+ an aliasing port already being used. (There is an option to
+ disable this for debugging) (cjm)
+ - Sockets will be allocated in cases where there might be
+ port interference with the host machine. This can be disabled
+ in cases where the ppp host will be acting purely as a
+ masquerading router and not generate any traffic of its own.
+ (cjm)
+
+Version 2.0: March, 1997 (cjm)
+ - Incoming packets which are not recognised by the packet
+ aliasing engine are now completely dropped in ip.c.
+ - Aliasing links are cleared when a host interface address
+ changes (due to re-dial and dynamic address allocation).
+ - PacketAliasPermanentLink() API added.
+ - Option for only aliasing private, unregistered IP addresses
+ added.
+ - Substantial rework to the aliasing lookup engine.
+
+Version 2.1: May, 1997 (cjm)
+ - Continuing rework to the aliasing lookup engine to support
+ multiple incoming addresses and static NAT.
+ - Now supports outgoing as well as incoming ICMP error messages/
+ - PPP commands to support address and port redirection.
+
diff --git a/usr.sbin/ppp/acf.c b/usr.sbin/ppp/acf.c
new file mode 100644
index 0000000..4db0b6b
--- /dev/null
+++ b/usr.sbin/ppp/acf.c
@@ -0,0 +1,115 @@
+/*-
+ * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <termios.h>
+
+#include "defs.h"
+#include "layer.h"
+#include "timer.h"
+#include "fsm.h"
+#include "log.h"
+#include "mbuf.h"
+#include "acf.h"
+#include "proto.h"
+#include "throughput.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "descriptor.h"
+#include "async.h"
+#include "physical.h"
+
+int
+acf_WrapperOctets(struct lcp *lcp, u_short proto)
+{
+ return (proto == PROTO_LCP || lcp->his_acfcomp == 0) ? 2 : 0;
+}
+
+static struct mbuf *
+acf_LayerPush(struct bundle *b, struct link *l, struct mbuf *bp,
+ int pri, u_short *proto)
+{
+ const u_char cp[2] = { HDLC_ADDR, HDLC_UI };
+
+ if (*proto == PROTO_LCP || l->lcp.his_acfcomp == 0) {
+ bp = m_prepend(bp, cp, 2, 0);
+ m_settype(bp, MB_ACFOUT);
+ }
+
+ return bp;
+}
+
+static struct mbuf *
+acf_LayerPull(struct bundle *b, struct link *l, struct mbuf *bp, u_short *proto)
+{
+ struct physical *p = link2physical(l);
+ u_char cp[2];
+
+ if (!p) {
+ log_Printf(LogERROR, "Can't Pull an acf packet from a logical link\n");
+ return bp;
+ }
+
+ if (mbuf_View(bp, cp, 2) == 2) {
+ if (!p->link.lcp.want_acfcomp) {
+ /* We expect the packet not to be compressed */
+ bp = mbuf_Read(bp, cp, 2);
+ if (cp[0] != HDLC_ADDR) {
+ p->hdlc.lqm.SaveInErrors++;
+ p->hdlc.stats.badaddr++;
+ log_Printf(LogDEBUG, "acf_LayerPull: addr 0x%02x\n", cp[0]);
+ m_freem(bp);
+ return NULL;
+ }
+ if (cp[1] != HDLC_UI) {
+ p->hdlc.lqm.SaveInErrors++;
+ p->hdlc.stats.badcommand++;
+ log_Printf(LogDEBUG, "acf_LayerPull: control 0x%02x\n", cp[1]);
+ m_freem(bp);
+ return NULL;
+ }
+ m_settype(bp, MB_ACFIN);
+ } else if (cp[0] == HDLC_ADDR && cp[1] == HDLC_UI) {
+ /*
+ * We can receive compressed packets, but the peer still sends
+ * uncompressed packets (or maybe this is a PROTO_LCP packet) !
+ */
+ bp = mbuf_Read(bp, cp, 2);
+ m_settype(bp, MB_ACFIN);
+ }
+ }
+
+ return bp;
+}
+
+struct layer acflayer = { LAYER_ACF, "acf", acf_LayerPush, acf_LayerPull };
diff --git a/usr.sbin/ppp/acf.h b/usr.sbin/ppp/acf.h
new file mode 100644
index 0000000..e32adbc
--- /dev/null
+++ b/usr.sbin/ppp/acf.h
@@ -0,0 +1,33 @@
+/*-
+ * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct lcp;
+
+extern int acf_WrapperOctets(struct lcp *, u_short);
+
+extern struct layer acflayer;
diff --git a/usr.sbin/ppp/arp.c b/usr.sbin/ppp/arp.c
new file mode 100644
index 0000000..47b7869
--- /dev/null
+++ b/usr.sbin/ppp/arp.c
@@ -0,0 +1,317 @@
+/*
+ * sys-bsd.c - System-dependent procedures for setting up
+ * PPP interfaces on bsd-4.4-ish systems (including 386BSD, NetBSD, etc.)
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $FreeBSD$
+ *
+ */
+
+/*
+ * TODO:
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <arpa/inet.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/un.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/sysctl.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "layer.h"
+#include "mbuf.h"
+#include "log.h"
+#include "id.h"
+#include "timer.h"
+#include "fsm.h"
+#include "defs.h"
+#include "iplist.h"
+#include "throughput.h"
+#include "slcompress.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "ncpaddr.h"
+#include "ipcp.h"
+#include "ipv6cp.h"
+#include "descriptor.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "mp.h"
+#include "ncp.h"
+#include "filter.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "bundle.h"
+#include "iface.h"
+#include "arp.h"
+
+/*
+ * SET_SA_FAMILY - set the sa_family field of a struct sockaddr,
+ * if it exists.
+ */
+#define SET_SA_FAMILY(addr, family) \
+ memset((char *) &(addr), '\0', sizeof(addr)); \
+ addr.sa_family = (family); \
+ addr.sa_len = sizeof(addr);
+
+
+#if RTM_VERSION >= 3
+
+/*
+ * arp_SetProxy - Make a proxy ARP entry for the peer.
+ */
+static struct {
+ struct rt_msghdr hdr;
+ struct sockaddr_inarp dst;
+ struct sockaddr_dl hwa;
+ char extra[128];
+} arpmsg;
+
+static int
+arp_ProxySub(struct bundle *bundle, struct in_addr addr, int add, int s)
+{
+ int routes;
+
+ /*
+ * Get the hardware address of an interface on the same subnet as our local
+ * address.
+ */
+
+ memset(&arpmsg, 0, sizeof arpmsg);
+ if (!arp_EtherAddr(s, addr, &arpmsg.hwa, 0)) {
+ log_Printf(LogWARN, "%s: Cannot determine ethernet address for proxy ARP\n",
+ inet_ntoa(addr));
+ return 0;
+ }
+ routes = ID0socket(PF_ROUTE, SOCK_RAW, AF_INET);
+ if (routes < 0) {
+ log_Printf(LogERROR, "arp_SetProxy: opening routing socket: %s\n",
+ strerror(errno));
+ return 0;
+ }
+ arpmsg.hdr.rtm_type = add ? RTM_ADD : RTM_DELETE;
+ arpmsg.hdr.rtm_flags = RTF_ANNOUNCE | RTF_HOST | RTF_STATIC;
+ arpmsg.hdr.rtm_version = RTM_VERSION;
+ arpmsg.hdr.rtm_seq = ++bundle->routing_seq;
+ arpmsg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY;
+ arpmsg.hdr.rtm_inits = RTV_EXPIRE;
+ arpmsg.dst.sin_len = sizeof(struct sockaddr_inarp);
+ arpmsg.dst.sin_family = AF_INET;
+ arpmsg.dst.sin_addr.s_addr = addr.s_addr;
+ arpmsg.dst.sin_other = SIN_PROXY;
+
+ arpmsg.hdr.rtm_msglen = (char *) &arpmsg.hwa - (char *) &arpmsg
+ + arpmsg.hwa.sdl_len;
+
+
+ if (ID0write(routes, &arpmsg, arpmsg.hdr.rtm_msglen) < 0 &&
+ !(!add && errno == ESRCH)) {
+ log_Printf(LogERROR, "%s proxy arp entry %s: %s\n",
+ add ? "Add" : "Delete", inet_ntoa(addr), strerror(errno));
+ close(routes);
+ return 0;
+ }
+ close(routes);
+ return 1;
+}
+
+int
+arp_SetProxy(struct bundle *bundle, struct in_addr addr, int s)
+{
+ return (arp_ProxySub(bundle, addr, 1, s));
+}
+
+/*
+ * arp_ClearProxy - Delete the proxy ARP entry for the peer.
+ */
+int
+arp_ClearProxy(struct bundle *bundle, struct in_addr addr, int s)
+{
+ return (arp_ProxySub(bundle, addr, 0, s));
+}
+
+#else /* RTM_VERSION */
+
+/*
+ * arp_SetProxy - Make a proxy ARP entry for the peer.
+ */
+int
+arp_SetProxy(struct bundle *bundle, struct in_addr addr, int s)
+{
+ struct arpreq arpreq;
+ struct {
+ struct sockaddr_dl sdl;
+ char space[128];
+ } dls;
+
+ memset(&arpreq, '\0', sizeof arpreq);
+
+ /*
+ * Get the hardware address of an interface on the same subnet as our local
+ * address.
+ */
+ if (!arp_EtherAddr(s, addr, &dls.sdl, 1)) {
+ log_Printf(LOG_PHASE_BIT, "Cannot determine ethernet address for "
+ "proxy ARP\n");
+ return 0;
+ }
+ arpreq.arp_ha.sa_len = sizeof(struct sockaddr);
+ arpreq.arp_ha.sa_family = AF_UNSPEC;
+ memcpy(arpreq.arp_ha.sa_data, LLADDR(&dls.sdl), dls.sdl.sdl_alen);
+ SET_SA_FAMILY(arpreq.arp_pa, AF_INET);
+ ((struct sockaddr_in *)&arpreq.arp_pa)->sin_addr.s_addr = addr.s_addr;
+ arpreq.arp_flags = ATF_PERM | ATF_PUBL;
+ if (ID0ioctl(s, SIOCSARP, (caddr_t) & arpreq) < 0) {
+ log_Printf(LogERROR, "arp_SetProxy: ioctl(SIOCSARP): %s\n",
+ strerror(errno));
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * arp_ClearProxy - Delete the proxy ARP entry for the peer.
+ */
+int
+arp_ClearProxy(struct bundle *bundle, struct in_addr addr, int s)
+{
+ struct arpreq arpreq;
+
+ memset(&arpreq, '\0', sizeof arpreq);
+ SET_SA_FAMILY(arpreq.arp_pa, AF_INET);
+ ((struct sockaddr_in *)&arpreq.arp_pa)->sin_addr.s_addr = addr.s_addr;
+ if (ID0ioctl(s, SIOCDARP, (caddr_t) & arpreq) < 0) {
+ log_Printf(LogERROR, "arp_ClearProxy: ioctl(SIOCDARP): %s\n",
+ strerror(errno));
+ return 0;
+ }
+ return 1;
+}
+
+#endif /* RTM_VERSION */
+
+
+/*
+ * arp_EtherAddr - get the hardware address of an interface on the
+ * the same subnet as ipaddr.
+ */
+
+int
+arp_EtherAddr(int s, struct in_addr ipaddr, struct sockaddr_dl *hwaddr,
+ int verbose)
+{
+ int mib[6], skip;
+ size_t needed;
+ char *buf, *ptr, *end;
+ struct if_msghdr *ifm;
+ struct ifa_msghdr *ifam;
+ struct sockaddr_dl *dl;
+ struct sockaddr *sa[RTAX_MAX];
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = 0;
+ mib[4] = NET_RT_IFLIST;
+ mib[5] = 0;
+
+ if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
+ log_Printf(LogERROR, "arp_EtherAddr: sysctl: estimate: %s\n",
+ strerror(errno));
+ return 0;
+ }
+
+ if ((buf = malloc(needed)) == NULL)
+ return 0;
+
+ if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
+ free(buf);
+ return 0;
+ }
+ end = buf + needed;
+
+ ptr = buf;
+ while (ptr < end) {
+ ifm = (struct if_msghdr *)ptr; /* On if_msghdr */
+ if (ifm->ifm_type != RTM_IFINFO)
+ break;
+ dl = (struct sockaddr_dl *)(ifm + 1); /* Single _dl at end */
+ skip = (ifm->ifm_flags & (IFF_UP | IFF_BROADCAST | IFF_POINTOPOINT |
+ IFF_NOARP | IFF_LOOPBACK)) != (IFF_UP | IFF_BROADCAST);
+ ptr += ifm->ifm_msglen; /* First ifa_msghdr */
+ while (ptr < end) {
+ ifam = (struct ifa_msghdr *)ptr; /* Next ifa_msghdr (alias) */
+ if (ifam->ifam_type != RTM_NEWADDR) /* finished ? */
+ break;
+ ptr += ifam->ifam_msglen;
+ if (skip || (ifam->ifam_addrs & (RTA_NETMASK|RTA_IFA)) !=
+ (RTA_NETMASK|RTA_IFA))
+ continue;
+ /* Found a candidate. Do the addresses match ? */
+ if (log_IsKept(LogDEBUG) &&
+ ptr == (char *)ifm + ifm->ifm_msglen + ifam->ifam_msglen)
+ log_Printf(LogDEBUG, "%.*s interface is a candidate for proxy\n",
+ dl->sdl_nlen, dl->sdl_data);
+
+ iface_ParseHdr(ifam, sa);
+
+ if (sa[RTAX_IFA]->sa_family == AF_INET) {
+ struct sockaddr_in *ifa, *netmask;
+
+ ifa = (struct sockaddr_in *)sa[RTAX_IFA];
+ netmask = (struct sockaddr_in *)sa[RTAX_NETMASK];
+
+ if (log_IsKept(LogDEBUG)) {
+ char a[16];
+
+ strncpy(a, inet_ntoa(netmask->sin_addr), sizeof a - 1);
+ a[sizeof a - 1] = '\0';
+ log_Printf(LogDEBUG, "Check addr %s, mask %s\n",
+ inet_ntoa(ifa->sin_addr), a);
+ }
+
+ if ((ifa->sin_addr.s_addr & netmask->sin_addr.s_addr) ==
+ (ipaddr.s_addr & netmask->sin_addr.s_addr)) {
+ log_Printf(verbose ? LogPHASE : LogDEBUG,
+ "Found interface %.*s for %s\n", dl->sdl_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..ac0a1ca
--- /dev/null
+++ b/usr.sbin/ppp/arp.h
@@ -0,0 +1,36 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct sockaddr_dl;
+struct bundle;
+
+extern int arp_ClearProxy(struct bundle *, struct in_addr, int);
+extern int arp_SetProxy(struct bundle *, struct in_addr, int);
+extern int arp_EtherAddr(int, struct in_addr, struct sockaddr_dl *, int);
diff --git a/usr.sbin/ppp/async.c b/usr.sbin/ppp/async.c
new file mode 100644
index 0000000..f28e2f6
--- /dev/null
+++ b/usr.sbin/ppp/async.c
@@ -0,0 +1,214 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+
+#include <string.h>
+#include <termios.h>
+
+#include "layer.h"
+#include "mbuf.h"
+#include "log.h"
+#include "defs.h"
+#include "timer.h"
+#include "fsm.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "lcp.h"
+#include "proto.h"
+#include "async.h"
+#include "throughput.h"
+#include "ccp.h"
+#include "link.h"
+#include "descriptor.h"
+#include "physical.h"
+
+#define MODE_HUNT 0x01
+#define MODE_ESC 0x02
+
+void
+async_Init(struct async *async)
+{
+ async_Setup(async);
+ memset(async->cfg.EscMap, '\0', sizeof async->cfg.EscMap);
+}
+
+void
+async_Setup(struct async *async)
+{
+ async->mode = MODE_HUNT;
+ async->length = 0;
+ async->my_accmap = async->his_accmap = 0xffffffff;
+}
+
+void
+async_SetLinkParams(struct async *async, u_int32_t mymap, u_int32_t hismap)
+{
+ async->my_accmap = mymap;
+ async->his_accmap = hismap | mymap;
+}
+
+/*
+ * Encode into async HDLC byte code
+ */
+static void
+async_Encode(struct async *async, u_char **cp, u_char c, int proto)
+{
+ u_char *wp;
+
+ wp = *cp;
+ if ((c < 0x20 && (proto == PROTO_LCP || (async->his_accmap & (1 << c))))
+ || (c == HDLC_ESC) || (c == HDLC_SYN)) {
+ *wp++ = HDLC_ESC;
+ c ^= HDLC_XOR;
+ }
+ if (async->cfg.EscMap[32] && async->cfg.EscMap[c >> 3] & (1 << (c & 7))) {
+ *wp++ = HDLC_ESC;
+ c ^= HDLC_XOR;
+ }
+ *wp++ = c;
+ *cp = wp;
+}
+
+static struct mbuf *
+async_LayerPush(struct bundle *bundle, struct link *l, struct mbuf *bp,
+ int pri, u_short *proto)
+{
+ struct physical *p = link2physical(l);
+ u_char *cp, *sp, *ep;
+ struct mbuf *wp;
+ int cnt;
+
+ if (!p || m_length(bp) > HDLCSIZE) {
+ m_freem(bp);
+ return NULL;
+ }
+
+ cp = p->async.xbuff;
+ ep = cp + HDLCSIZE - 10;
+ wp = bp;
+ *cp++ = HDLC_SYN;
+ while (wp) {
+ sp = MBUF_CTOP(wp);
+ for (cnt = wp->m_len; cnt > 0; cnt--) {
+ async_Encode(&p->async, &cp, *sp++, *proto);
+ if (cp >= ep) {
+ m_freem(bp);
+ return NULL;
+ }
+ }
+ wp = wp->m_next;
+ }
+ *cp++ = HDLC_SYN;
+
+ cnt = cp - p->async.xbuff;
+ m_freem(bp);
+ bp = m_get(cnt, MB_ASYNCOUT);
+ memcpy(MBUF_CTOP(bp), p->async.xbuff, cnt);
+ log_DumpBp(LogASYNC, "Write", bp);
+
+ return bp;
+}
+
+static struct mbuf *
+async_Decode(struct async *async, u_char c)
+{
+ struct mbuf *bp;
+
+ if ((async->mode & MODE_HUNT) && c != HDLC_SYN)
+ return NULL;
+
+ switch (c) {
+ case HDLC_SYN:
+ async->mode &= ~MODE_HUNT;
+ if (async->length) { /* packet is ready. */
+ bp = m_get(async->length, MB_ASYNCIN);
+ mbuf_Write(bp, async->hbuff, async->length);
+ async->length = 0;
+ return bp;
+ }
+ break;
+ case HDLC_ESC:
+ if (!(async->mode & MODE_ESC)) {
+ async->mode |= MODE_ESC;
+ break;
+ }
+ /* 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;
+}
+
+static struct mbuf *
+async_LayerPull(struct bundle *b, struct link *l, struct mbuf *bp,
+ u_short *proto)
+{
+ struct mbuf *nbp, **last;
+ struct physical *p = link2physical(l);
+ u_char *ch;
+ size_t cnt;
+
+ if (!p) {
+ log_Printf(LogERROR, "Can't Pull an async packet from a logical link\n");
+ return bp;
+ }
+
+ last = &nbp;
+
+ log_DumpBp(LogASYNC, "Read", bp);
+ while (bp) {
+ ch = MBUF_CTOP(bp);
+ for (cnt = bp->m_len; cnt; cnt--) {
+ *last = async_Decode(&p->async, *ch++);
+ if (*last != NULL)
+ last = &(*last)->m_nextpkt;
+ }
+ bp = m_free(bp);
+ }
+
+ return nbp;
+}
+
+struct layer asynclayer =
+ { LAYER_ASYNC, "async", async_LayerPush, async_LayerPull };
diff --git a/usr.sbin/ppp/async.h b/usr.sbin/ppp/async.h
new file mode 100644
index 0000000..309f597
--- /dev/null
+++ b/usr.sbin/ppp/async.h
@@ -0,0 +1,53 @@
+/*-
+ * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define HDLCSIZE (MAX_MRU*2+6)
+
+struct async {
+ int mode;
+ int length;
+ u_char hbuff[HDLCSIZE]; /* recv buffer */
+ u_char xbuff[HDLCSIZE]; /* xmit buffer */
+ u_int32_t my_accmap;
+ u_int32_t his_accmap;
+
+ struct {
+ u_char EscMap[33];
+ } cfg;
+};
+
+struct lcp;
+struct mbuf;
+struct physical;
+struct bundle;
+
+extern void async_Init(struct async *);
+extern void async_Setup(struct async *);
+extern void async_SetLinkParams(struct async *, u_int32_t, u_int32_t);
+
+extern struct layer asynclayer;
diff --git a/usr.sbin/ppp/atm.c b/usr.sbin/ppp/atm.c
new file mode 100644
index 0000000..0d51564
--- /dev/null
+++ b/usr.sbin/ppp/atm.c
@@ -0,0 +1,237 @@
+/*-
+ * Copyright (c) 2000 Jakob Stoklund Olesen <stoklund@taxidriver.dk>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netnatm/natm.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <sys/uio.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "layer.h"
+#include "defs.h"
+#include "mbuf.h"
+#include "log.h"
+#include "timer.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "throughput.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "async.h"
+#include "descriptor.h"
+#include "physical.h"
+#include "main.h"
+#include "atm.h"
+
+/* String identifying PPPoA */
+#define PPPOA "PPPoA"
+#define PPPOA_LEN (sizeof(PPPOA) - 1)
+
+struct atmdevice {
+ struct device dev; /* What struct physical knows about */
+};
+
+#define device2atm(d) ((d)->type == ATM_DEVICE ? (struct atmdevice *)d : NULL)
+
+int
+atm_DeviceSize(void)
+{
+ return sizeof(struct atmdevice);
+}
+
+static ssize_t
+atm_Sendto(struct physical *p, const void *v, size_t n)
+{
+ ssize_t ret = write(p->fd, v, n);
+ if (ret < 0) {
+ log_Printf(LogDEBUG, "atm_Sendto(%ld): %s\n", (long)n, strerror(errno));
+ return ret;
+ }
+ return ret;
+}
+
+static ssize_t
+atm_Recvfrom(struct physical *p, void *v, size_t n)
+{
+ ssize_t ret = read(p->fd, (char*)v, n);
+ if (ret < 0) {
+ log_Printf(LogDEBUG, "atm_Recvfrom(%ld): %s\n", (long)n, strerror(errno));
+ return ret;
+ }
+ return ret;
+}
+
+static void
+atm_Free(struct physical *p)
+{
+ struct atmdevice *dev = device2atm(p->handler);
+
+ free(dev);
+}
+
+static void
+atm_device2iov(struct device *d, struct iovec *iov, int *niov,
+ int maxiov, int *auxfd, int *nauxfd)
+{
+ int sz = physical_MaxDeviceSize();
+
+ iov[*niov].iov_base = realloc(d, sz);
+ if (iov[*niov].iov_base == NULL) {
+ log_Printf(LogALERT, "Failed to allocate memory: %d\n", sz);
+ AbortProgram(EX_OSERR);
+ }
+ iov[*niov].iov_len = sz;
+ (*niov)++;
+}
+
+static const struct device baseatmdevice = {
+ ATM_DEVICE,
+ "atm",
+ 0,
+ { CD_NOTREQUIRED, 0 },
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ atm_Free,
+ atm_Recvfrom,
+ atm_Sendto,
+ atm_device2iov,
+ NULL,
+ NULL,
+ NULL
+};
+
+struct device *
+atm_iov2device(int type, struct physical *p, struct iovec *iov, int *niov,
+ int maxiov, int *auxfd, int *nauxfd)
+{
+ if (type == ATM_DEVICE) {
+ struct atmdevice *dev = (struct atmdevice *)iov[(*niov)++].iov_base;
+
+ dev = realloc(dev, sizeof *dev); /* Reduce to the correct size */
+ if (dev == NULL) {
+ log_Printf(LogALERT, "Failed to allocate memory: %d\n",
+ (int)(sizeof *dev));
+ AbortProgram(EX_OSERR);
+ }
+
+ /* Refresh function pointers etc */
+ memcpy(&dev->dev, &baseatmdevice, sizeof dev->dev);
+
+ physical_SetupStack(p, dev->dev.name, PHYSICAL_FORCE_SYNCNOACF);
+ return &dev->dev;
+ }
+
+ return NULL;
+}
+
+static struct atmdevice *
+atm_CreateDevice(struct physical *p, const char *iface, unsigned vpi,
+ unsigned vci)
+{
+ struct atmdevice *dev;
+ struct sockaddr_natm sock;
+
+ if ((dev = calloc(1, sizeof *dev)) == NULL) {
+ log_Printf(LogWARN, "%s: Cannot allocate an atm device: %s\n",
+ p->link.name, strerror(errno));
+ return NULL;
+ }
+
+ sock.snatm_len = sizeof sock;
+ sock.snatm_family = AF_NATM;
+ strncpy(sock.snatm_if, iface, IFNAMSIZ);
+ sock.snatm_vpi = vpi;
+ sock.snatm_vci = vci;
+
+ log_Printf(LogPHASE, "%s: Connecting to %s:%u.%u\n", p->link.name,
+ iface, vpi, vci);
+
+ p->fd = socket(PF_NATM, SOCK_DGRAM, PROTO_NATMAAL5);
+ if (p->fd >= 0) {
+ log_Printf(LogDEBUG, "%s: Opened atm socket %s\n", p->link.name,
+ p->name.full);
+ if (connect(p->fd, (struct sockaddr *)&sock, sizeof sock) == 0)
+ return dev;
+ else
+ log_Printf(LogWARN, "%s: connect: %s\n", p->name.full, strerror(errno));
+ } else
+ log_Printf(LogWARN, "%s: socket: %s\n", p->name.full, strerror(errno));
+
+ close(p->fd);
+ p->fd = -1;
+ free(dev);
+
+ return NULL;
+}
+
+struct device *
+atm_Create(struct physical *p)
+{
+ struct atmdevice *dev;
+
+ dev = NULL;
+ if (p->fd < 0 && !strncasecmp(p->name.full, PPPOA, PPPOA_LEN)
+ && p->name.full[PPPOA_LEN] == ':') {
+ char iface[25];
+ unsigned vci, vpi;
+
+ if (sscanf(p->name.full + PPPOA_LEN + 1, "%25[A-Za-z0-9]:%u.%u", iface,
+ &vpi, &vci) != 3) {
+ log_Printf(LogWARN, "Malformed ATM device name \'%s\', "
+ "PPPoA:if:vpi.vci expected\n", p->name.full);
+ return NULL;
+ }
+
+ dev = atm_CreateDevice(p, iface, vpi, vci);
+ }
+
+ if (dev) {
+ memcpy(&dev->dev, &baseatmdevice, sizeof dev->dev);
+ physical_SetupStack(p, dev->dev.name, PHYSICAL_FORCE_SYNCNOACF);
+ if (p->cfg.cd.necessity != CD_DEFAULT)
+ log_Printf(LogWARN, "Carrier settings ignored\n");
+ return &dev->dev;
+ }
+
+ return NULL;
+}
diff --git a/usr.sbin/ppp/atm.h b/usr.sbin/ppp/atm.h
new file mode 100644
index 0000000..3fbc0c5
--- /dev/null
+++ b/usr.sbin/ppp/atm.h
@@ -0,0 +1,35 @@
+/*-
+ * Copyright (c) 2000 Jakob Stoklund Olesen <stoklund@taxidriver.dk>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct physical;
+struct device;
+
+extern struct device *atm_Create(struct physical *);
+extern struct device *atm_iov2device(int, struct physical *,
+ struct iovec *, int *, int, int *, int *);
+extern int atm_DeviceSize(void);
diff --git a/usr.sbin/ppp/auth.c b/usr.sbin/ppp/auth.c
new file mode 100644
index 0000000..3cdb9e8
--- /dev/null
+++ b/usr.sbin/ppp/auth.c
@@ -0,0 +1,433 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <pwd.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "layer.h"
+#include "mbuf.h"
+#include "defs.h"
+#include "log.h"
+#include "timer.h"
+#include "fsm.h"
+#include "iplist.h"
+#include "throughput.h"
+#include "slcompress.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "ncpaddr.h"
+#include "ipcp.h"
+#include "auth.h"
+#include "systems.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "descriptor.h"
+#include "chat.h"
+#include "proto.h"
+#include "filter.h"
+#include "mp.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "cbcp.h"
+#include "chap.h"
+#include "async.h"
+#include "physical.h"
+#include "datalink.h"
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+
+const char *
+Auth2Nam(u_short auth, u_char type)
+{
+ static char chap[10];
+
+ switch (auth) {
+ case PROTO_PAP:
+ return "PAP";
+ case PROTO_CHAP:
+ snprintf(chap, sizeof chap, "CHAP 0x%02x", type);
+ return chap;
+ case 0:
+ return "none";
+ }
+ return "unknown";
+}
+
+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, lineno;
+ char *vector[6], buff[LINE_LEN];
+ const char *slash;
+
+ fp = OpenSecret(SECRETFILE);
+ if (fp != NULL) {
+again:
+ lineno = 0;
+ while (fgets(buff, sizeof buff, fp)) {
+ lineno++;
+ if (buff[0] == '#')
+ continue;
+ buff[strlen(buff) - 1] = '\0';
+ memset(vector, '\0', sizeof vector);
+ if ((n = MakeArgs(buff, vector, VECSIZE(vector), PARSE_REDUCE)) < 0)
+ log_Printf(LogWARN, "%s: %d: Invalid line\n", SECRETFILE, lineno);
+ if (n < 5)
+ continue;
+ if (strcmp(vector[0], name) == 0) {
+ CloseSecret(fp);
+ if (*vector[4] == '\0')
+ return 0;
+ strncpy(phone, vector[4], phonelen - 1);
+ phone[phonelen - 1] = '\0';
+ return 1; /* Valid */
+ }
+ }
+
+ if ((slash = strrchr(name, '\\')) != NULL && slash[1]) {
+ /* Look for the name without the leading domain */
+ name = slash + 1;
+ rewind(fp);
+ goto again;
+ }
+
+ CloseSecret(fp);
+ }
+ *phone = '\0';
+ return 0;
+}
+
+int
+auth_Select(struct bundle *bundle, const char *name)
+{
+ FILE *fp;
+ int n, lineno;
+ char *vector[5], buff[LINE_LEN];
+ const char *slash;
+
+ if (*name == '\0') {
+ ipcp_Setup(&bundle->ncp.ipcp, INADDR_NONE);
+ return 1;
+ }
+
+#ifndef NORADIUS
+ if (bundle->radius.valid && bundle->radius.ip.s_addr != INADDR_NONE &&
+ bundle->radius.ip.s_addr != RADIUS_INADDR_POOL) {
+ /* We've got a radius IP - it overrides everything */
+ if (!ipcp_UseHisIPaddr(bundle, bundle->radius.ip))
+ return 0;
+ ipcp_Setup(&bundle->ncp.ipcp, bundle->radius.mask.s_addr);
+ /* Continue with ppp.secret in case we've got a new label */
+ }
+#endif
+
+ fp = OpenSecret(SECRETFILE);
+ if (fp != NULL) {
+again:
+ lineno = 0;
+ while (fgets(buff, sizeof buff, fp)) {
+ lineno++;
+ if (buff[0] == '#')
+ continue;
+ buff[strlen(buff) - 1] = '\0';
+ memset(vector, '\0', sizeof vector);
+ if ((n = MakeArgs(buff, vector, VECSIZE(vector), PARSE_REDUCE)) < 0)
+ log_Printf(LogWARN, "%s: %d: Invalid line\n", SECRETFILE, lineno);
+ if (n < 2)
+ continue;
+ if (strcmp(vector[0], name) == 0) {
+ CloseSecret(fp);
+#ifndef NORADIUS
+ if (!bundle->radius.valid || bundle->radius.ip.s_addr == INADDR_NONE) {
+#endif
+ if (n > 2 && *vector[2] && strcmp(vector[2], "*") &&
+ !ipcp_UseHisaddr(bundle, vector[2], 1))
+ return 0;
+ ipcp_Setup(&bundle->ncp.ipcp, INADDR_NONE);
+#ifndef NORADIUS
+ }
+#endif
+ if (n > 3 && *vector[3] && strcmp(vector[3], "*"))
+ bundle_SetLabel(bundle, vector[3]);
+ return 1; /* Valid */
+ }
+ }
+
+ if ((slash = strrchr(name, '\\')) != NULL && slash[1]) {
+ /* Look for the name without the leading domain */
+ name = slash + 1;
+ rewind(fp);
+ goto again;
+ }
+
+ CloseSecret(fp);
+ }
+
+#ifndef NOPASSWDAUTH
+ /* Let 'em in anyway - they must have been in the passwd file */
+ ipcp_Setup(&bundle->ncp.ipcp, INADDR_NONE);
+ return 1;
+#else
+#ifndef NORADIUS
+ if (bundle->radius.valid)
+ return 1;
+#endif
+
+ /* Disappeared from ppp.secret ??? */
+ return 0;
+#endif
+}
+
+int
+auth_Validate(struct bundle *bundle, const char *name,
+ const char *key, struct physical *physical)
+{
+ /* Used by PAP routines */
+
+ FILE *fp;
+ int n, lineno;
+ char *vector[5], buff[LINE_LEN];
+ const char *slash;
+
+ fp = OpenSecret(SECRETFILE);
+again:
+ lineno = 0;
+ if (fp != NULL) {
+ while (fgets(buff, sizeof buff, fp)) {
+ lineno++;
+ if (buff[0] == '#')
+ continue;
+ buff[strlen(buff) - 1] = 0;
+ memset(vector, '\0', sizeof vector);
+ if ((n = MakeArgs(buff, vector, VECSIZE(vector), PARSE_REDUCE)) < 0)
+ log_Printf(LogWARN, "%s: %d: Invalid line\n", SECRETFILE, lineno);
+ if (n < 2)
+ continue;
+ if (strcmp(vector[0], name) == 0) {
+ CloseSecret(fp);
+ return auth_CheckPasswd(name, vector[1], key);
+ }
+ }
+ }
+
+ if ((slash = strrchr(name, '\\')) != NULL && slash[1]) {
+ /* Look for the name without the leading domain */
+ name = slash + 1;
+ if (fp != NULL) {
+ rewind(fp);
+ goto again;
+ }
+ }
+
+ if (fp != NULL)
+ CloseSecret(fp);
+
+#ifndef NOPASSWDAUTH
+ if (Enabled(bundle, OPT_PASSWDAUTH))
+ return auth_CheckPasswd(name, "*", key);
+#endif
+
+ return 0; /* Invalid */
+}
+
+char *
+auth_GetSecret(struct bundle *bundle, const char *name, int len,
+ struct physical *physical)
+{
+ /* Used by CHAP routines */
+
+ FILE *fp;
+ int n, lineno;
+ char *vector[5];
+ const char *slash;
+ static char buff[LINE_LEN]; /* vector[] will point here when returned */
+
+ fp = OpenSecret(SECRETFILE);
+ if (fp == NULL)
+ return (NULL);
+
+again:
+ lineno = 0;
+ while (fgets(buff, sizeof buff, fp)) {
+ lineno++;
+ if (buff[0] == '#')
+ continue;
+ n = strlen(buff) - 1;
+ if (buff[n] == '\n')
+ buff[n] = '\0'; /* Trim the '\n' */
+ memset(vector, '\0', sizeof vector);
+ if ((n = MakeArgs(buff, vector, VECSIZE(vector), PARSE_REDUCE)) < 0)
+ log_Printf(LogWARN, "%s: %d: Invalid line\n", SECRETFILE, lineno);
+ if (n < 2)
+ continue;
+ if (strlen(vector[0]) == len && strncmp(vector[0], name, len) == 0) {
+ CloseSecret(fp);
+ return vector[1];
+ }
+ }
+
+ if ((slash = strrchr(name, '\\')) != NULL && slash[1]) {
+ /* Go back and look for the name without the leading domain */
+ len -= slash - name + 1;
+ name = slash + 1;
+ rewind(fp);
+ goto again;
+ }
+
+ CloseSecret(fp);
+ return (NULL); /* Invalid */
+}
+
+static void
+AuthTimeout(void *vauthp)
+{
+ struct authinfo *authp = (struct authinfo *)vauthp;
+
+ timer_Stop(&authp->authtimer);
+ if (--authp->retry > 0) {
+ authp->id++;
+ (*authp->fn.req)(authp);
+ timer_Start(&authp->authtimer);
+ } else {
+ log_Printf(LogPHASE, "Auth: No response from server\n");
+ datalink_AuthNotOk(authp->physical->dl);
+ }
+}
+
+void
+auth_Init(struct authinfo *authp, struct physical *p, auth_func req,
+ auth_func success, auth_func failure)
+{
+ memset(authp, '\0', sizeof(struct authinfo));
+ authp->cfg.fsm.timeout = DEF_FSMRETRY;
+ authp->cfg.fsm.maxreq = DEF_FSMAUTHTRIES;
+ authp->cfg.fsm.maxtrm = 0; /* not used */
+ authp->fn.req = req;
+ authp->fn.success = success;
+ authp->fn.failure = failure;
+ authp->physical = p;
+}
+
+void
+auth_StartReq(struct authinfo *authp)
+{
+ timer_Stop(&authp->authtimer);
+ authp->authtimer.func = AuthTimeout;
+ authp->authtimer.name = "auth";
+ authp->authtimer.load = authp->cfg.fsm.timeout * SECTICKS;
+ authp->authtimer.arg = (void *)authp;
+ authp->retry = authp->cfg.fsm.maxreq;
+ authp->id = 1;
+ (*authp->fn.req)(authp);
+ timer_Start(&authp->authtimer);
+}
+
+void
+auth_StopTimer(struct authinfo *authp)
+{
+ timer_Stop(&authp->authtimer);
+}
+
+struct mbuf *
+auth_ReadHeader(struct authinfo *authp, struct mbuf *bp)
+{
+ int len;
+
+ len = m_length(bp);
+ if (len >= sizeof authp->in.hdr) {
+ bp = mbuf_Read(bp, (u_char *)&authp->in.hdr, sizeof authp->in.hdr);
+ if (len >= ntohs(authp->in.hdr.length))
+ return bp;
+ authp->in.hdr.length = htons(0);
+ log_Printf(LogWARN, "auth_ReadHeader: Short packet (%d > %d) !\n",
+ ntohs(authp->in.hdr.length), len);
+ } else {
+ authp->in.hdr.length = htons(0);
+ log_Printf(LogWARN, "auth_ReadHeader: Short packet header (%d > %d) !\n",
+ (int)(sizeof authp->in.hdr), len);
+ }
+
+ m_freem(bp);
+ return NULL;
+}
+
+struct mbuf *
+auth_ReadName(struct authinfo *authp, struct mbuf *bp, int len)
+{
+ if (len > sizeof authp->in.name - 1)
+ log_Printf(LogWARN, "auth_ReadName: Name too long (%d) !\n", len);
+ else {
+ int mlen = m_length(bp);
+
+ if (len > mlen)
+ log_Printf(LogWARN, "auth_ReadName: Short packet (%d > %d) !\n",
+ len, mlen);
+ else {
+ bp = mbuf_Read(bp, (u_char *)authp->in.name, len);
+ authp->in.name[len] = '\0';
+ return bp;
+ }
+ }
+
+ *authp->in.name = '\0';
+ m_freem(bp);
+ return NULL;
+}
diff --git a/usr.sbin/ppp/auth.h b/usr.sbin/ppp/auth.h
new file mode 100644
index 0000000..bbc257c
--- /dev/null
+++ b/usr.sbin/ppp/auth.h
@@ -0,0 +1,70 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct physical;
+struct bundle;
+struct authinfo;
+typedef void (*auth_func)(struct authinfo *);
+
+struct authinfo {
+ struct {
+ auth_func req;
+ auth_func success;
+ auth_func failure;
+ } fn;
+ struct {
+ struct fsmheader hdr;
+ char name[AUTHLEN];
+ } in;
+ struct pppTimer authtimer;
+ int retry;
+ int id;
+ struct physical *physical;
+ struct {
+ struct fsm_retry fsm; /* How often/frequently to resend requests */
+ } cfg;
+};
+
+#define auth_Failure(a) (*(a)->fn.failure)(a)
+#define auth_Success(a) (*(a)->fn.success)(a)
+
+extern const char *Auth2Nam(u_short, u_char);
+extern void auth_Init(struct authinfo *, struct physical *,
+ auth_func, auth_func, auth_func);
+extern void auth_StopTimer(struct authinfo *);
+extern void auth_StartReq(struct authinfo *);
+extern int auth_Validate(struct bundle *, const char *, const char *,
+ struct physical *);
+extern char *auth_GetSecret(struct bundle *, const char *, int,
+ struct physical *);
+extern int auth_SetPhoneList(const char *, char *, int);
+extern int auth_Select(struct bundle *, const char *);
+extern struct mbuf *auth_ReadHeader(struct authinfo *, struct mbuf *);
+extern struct mbuf *auth_ReadName(struct authinfo *, struct mbuf *, int);
diff --git a/usr.sbin/ppp/bundle.c b/usr.sbin/ppp/bundle.c
new file mode 100644
index 0000000..f0c8f94
--- /dev/null
+++ b/usr.sbin/ppp/bundle.c
@@ -0,0 +1,2008 @@
+/*-
+ * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <net/if_tun.h> /* For TUNS* ioctls */
+#include <net/route.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/un.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#ifdef __OpenBSD__
+#include <util.h>
+#else
+#include <libutil.h>
+#endif
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "layer.h"
+#include "defs.h"
+#include "command.h"
+#include "mbuf.h"
+#include "log.h"
+#include "id.h"
+#include "timer.h"
+#include "fsm.h"
+#include "iplist.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "throughput.h"
+#include "slcompress.h"
+#include "ncpaddr.h"
+#include "ip.h"
+#include "ipcp.h"
+#include "filter.h"
+#include "descriptor.h"
+#include "route.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "mp.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+#include "async.h"
+#include "physical.h"
+#include "auth.h"
+#include "proto.h"
+#include "chap.h"
+#include "tun.h"
+#include "prompt.h"
+#include "chat.h"
+#include "cbcp.h"
+#include "datalink.h"
+#include "iface.h"
+#include "server.h"
+#include "probe.h"
+#ifndef NODES
+#include "mppe.h"
+#endif
+
+#define SCATTER_SEGMENTS 7 /* version, datalink, name, physical,
+ throughput, throughput, device */
+
+#define SEND_MAXFD 3 /* Max file descriptors passed through
+ the local domain socket */
+
+static int bundle_RemainingIdleTime(struct bundle *);
+
+static const char * const PhaseNames[] = {
+ "Dead", "Establish", "Authenticate", "Network", "Terminate"
+};
+
+const char *
+bundle_PhaseName(struct bundle *bundle)
+{
+ return bundle->phase <= PHASE_TERMINATE ?
+ PhaseNames[bundle->phase] : "unknown";
+}
+
+void
+bundle_NewPhase(struct bundle *bundle, u_int new)
+{
+ if (new == bundle->phase)
+ return;
+
+ if (new <= PHASE_TERMINATE)
+ log_Printf(LogPHASE, "bundle: %s\n", PhaseNames[new]);
+
+ switch (new) {
+ case PHASE_DEAD:
+ bundle->phase = new;
+#ifndef NODES
+ MPPE_MasterKeyValid = 0;
+#endif
+ log_DisplayPrompts();
+ break;
+
+ case PHASE_ESTABLISH:
+ bundle->phase = new;
+ break;
+
+ case PHASE_AUTHENTICATE:
+ bundle->phase = new;
+ log_DisplayPrompts();
+ break;
+
+ case PHASE_NETWORK:
+ if (ncp_fsmStart(&bundle->ncp, bundle)) {
+ bundle->phase = new;
+ log_DisplayPrompts();
+ } else {
+ log_Printf(LogPHASE, "bundle: All NCPs are disabled\n");
+ bundle_Close(bundle, NULL, CLOSE_STAYDOWN);
+ }
+ break;
+
+ case PHASE_TERMINATE:
+ bundle->phase = new;
+ mp_Down(&bundle->ncp.mp);
+ log_DisplayPrompts();
+ break;
+ }
+}
+
+static void
+bundle_LayerStart(void *v, struct fsm *fp)
+{
+ /* The given FSM is about to start up ! */
+}
+
+
+void
+bundle_Notify(struct bundle *bundle, char c)
+{
+ if (bundle->notify.fd != -1) {
+ int ret;
+
+ ret = write(bundle->notify.fd, &c, 1);
+ if (c != EX_REDIAL && c != EX_RECONNECT) {
+ if (ret == 1)
+ log_Printf(LogCHAT, "Parent notified of %s\n",
+ c == EX_NORMAL ? "success" : "failure");
+ else
+ log_Printf(LogERROR, "Failed to notify parent of success\n");
+ close(bundle->notify.fd);
+ bundle->notify.fd = -1;
+ } else if (ret == 1)
+ log_Printf(LogCHAT, "Parent notified of %s\n", ex_desc(c));
+ else
+ log_Printf(LogERROR, "Failed to notify parent of %s\n", ex_desc(c));
+ }
+}
+
+static void
+bundle_ClearQueues(void *v)
+{
+ struct bundle *bundle = (struct bundle *)v;
+ struct datalink *dl;
+
+ log_Printf(LogPHASE, "Clearing choked output queue\n");
+ timer_Stop(&bundle->choked.timer);
+
+ /*
+ * Emergency time:
+ *
+ * We've had a full queue for PACKET_DEL_SECS seconds without being
+ * able to get rid of any of the packets. We've probably given up
+ * on the redials at this point, and the queued data has almost
+ * definitely been timed out by the layer above. As this is preventing
+ * us from reading the TUN_NAME device (we don't want to buffer stuff
+ * indefinitely), we may as well nuke this data and start with a clean
+ * slate !
+ *
+ * Unfortunately, this has the side effect of shafting any compression
+ * dictionaries in use (causing the relevant RESET_REQ/RESET_ACK).
+ */
+
+ ncp_DeleteQueues(&bundle->ncp);
+ for (dl = bundle->links; dl; dl = dl->next)
+ physical_DeleteQueue(dl->physical);
+}
+
+static void
+bundle_LinkAdded(struct bundle *bundle, struct datalink *dl)
+{
+ bundle->phys_type.all |= dl->physical->type;
+ if (dl->state == DATALINK_OPEN)
+ bundle->phys_type.open |= dl->physical->type;
+
+#ifndef NORADIUS
+ if ((bundle->phys_type.open & (PHYS_DEDICATED|PHYS_DDIAL))
+ != bundle->phys_type.open && bundle->session.timer.state == TIMER_STOPPED)
+ if (bundle->radius.sessiontime)
+ bundle_StartSessionTimer(bundle, 0);
+#endif
+
+ if ((bundle->phys_type.open & (PHYS_DEDICATED|PHYS_DDIAL))
+ != bundle->phys_type.open && bundle->idle.timer.state == TIMER_STOPPED)
+ /* We may need to start our idle timer */
+ bundle_StartIdleTimer(bundle, 0);
+}
+
+void
+bundle_LinksRemoved(struct bundle *bundle)
+{
+ struct datalink *dl;
+
+ bundle->phys_type.all = bundle->phys_type.open = 0;
+ for (dl = bundle->links; dl; dl = dl->next)
+ bundle_LinkAdded(bundle, dl);
+
+ bundle_CalculateBandwidth(bundle);
+ mp_CheckAutoloadTimer(&bundle->ncp.mp);
+
+ if ((bundle->phys_type.open & (PHYS_DEDICATED|PHYS_DDIAL))
+ == bundle->phys_type.open) {
+#ifndef NORADIUS
+ if (bundle->radius.sessiontime)
+ bundle_StopSessionTimer(bundle);
+#endif
+ bundle_StopIdleTimer(bundle);
+ }
+}
+
+static void
+bundle_LayerUp(void *v, struct fsm *fp)
+{
+ /*
+ * The given fsm is now up
+ * If it's an LCP, adjust our phys_mode.open value and check the
+ * autoload timer.
+ * If it's the first NCP, calculate our bandwidth
+ * If it's the first NCP, set our ``upat'' time
+ * If it's the first NCP, start the idle timer.
+ * If it's an NCP, tell our -background parent to go away.
+ * If it's the first NCP, start the autoload timer
+ */
+ struct bundle *bundle = (struct bundle *)v;
+
+ if (fp->proto == PROTO_LCP) {
+ struct physical *p = link2physical(fp->link);
+
+ bundle_LinkAdded(bundle, p->dl);
+ mp_CheckAutoloadTimer(&bundle->ncp.mp);
+ } else if (isncp(fp->proto)) {
+ if (ncp_LayersOpen(&fp->bundle->ncp) == 1) {
+ bundle_CalculateBandwidth(fp->bundle);
+ time(&bundle->upat);
+#ifndef NORADIUS
+ if (bundle->radius.sessiontime)
+ bundle_StartSessionTimer(bundle, 0);
+#endif
+ bundle_StartIdleTimer(bundle, 0);
+ mp_CheckAutoloadTimer(&fp->bundle->ncp.mp);
+ }
+ bundle_Notify(bundle, EX_NORMAL);
+ } else if (fp->proto == PROTO_CCP)
+ bundle_CalculateBandwidth(fp->bundle); /* Against ccp_MTUOverhead */
+}
+
+static void
+bundle_LayerDown(void *v, struct fsm *fp)
+{
+ /*
+ * The given FSM has been told to come down.
+ * If it's our last NCP, stop the idle timer.
+ * If it's our last NCP, clear our ``upat'' value.
+ * If it's our last NCP, stop the autoload timer
+ * If it's an LCP, adjust our phys_type.open value and any timers.
+ * If it's an LCP and we're in multilink mode, adjust our tun
+ * If it's the last LCP, down all NCPs
+ * speed and make sure our minimum sequence number is adjusted.
+ */
+
+ struct bundle *bundle = (struct bundle *)v;
+
+ if (isncp(fp->proto)) {
+ if (ncp_LayersOpen(&fp->bundle->ncp) == 0) {
+#ifndef NORADIUS
+ if (bundle->radius.sessiontime)
+ bundle_StopSessionTimer(bundle);
+#endif
+ bundle_StopIdleTimer(bundle);
+ bundle->upat = 0;
+ mp_StopAutoloadTimer(&bundle->ncp.mp);
+ }
+ } else if (fp->proto == PROTO_LCP) {
+ struct datalink *dl;
+ struct datalink *lost;
+ int others_active;
+
+ bundle_LinksRemoved(bundle); /* adjust timers & phys_type values */
+
+ lost = NULL;
+ others_active = 0;
+ for (dl = bundle->links; dl; dl = dl->next) {
+ if (fp == &dl->physical->link.lcp.fsm)
+ lost = dl;
+ else if (dl->state != DATALINK_CLOSED && dl->state != DATALINK_HANGUP)
+ others_active++;
+ }
+
+ if (bundle->ncp.mp.active) {
+ bundle_CalculateBandwidth(bundle);
+
+ if (lost)
+ mp_LinkLost(&bundle->ncp.mp, lost);
+ else
+ log_Printf(LogALERT, "Oops, lost an unrecognised datalink (%s) !\n",
+ fp->link->name);
+ }
+
+ if (!others_active) {
+ /* Down the NCPs. We don't expect to get fsm_Close()d ourself ! */
+ ncp2initial(&bundle->ncp);
+ mp_Down(&bundle->ncp.mp);
+ }
+ }
+}
+
+static void
+bundle_LayerFinish(void *v, struct fsm *fp)
+{
+ /* The given fsm is now down (fp cannot be NULL)
+ *
+ * If it's the last NCP, fsm_Close all LCPs
+ * If it's the last NCP, bring any MP layer down
+ */
+
+ struct bundle *bundle = (struct bundle *)v;
+ struct datalink *dl;
+
+ if (isncp(fp->proto) && !ncp_LayersUnfinished(&bundle->ncp)) {
+ if (bundle_Phase(bundle) != PHASE_DEAD)
+ bundle_NewPhase(bundle, PHASE_TERMINATE);
+ for (dl = bundle->links; dl; dl = dl->next)
+ if (dl->state == DATALINK_OPEN)
+ datalink_Close(dl, CLOSE_STAYDOWN);
+ fsm2initial(fp);
+ mp_Down(&bundle->ncp.mp);
+ }
+}
+
+void
+bundle_Close(struct bundle *bundle, const char *name, int how)
+{
+ /*
+ * Please close the given datalink.
+ * If name == NULL or name is the last datalink, fsm_Close all NCPs
+ * (except our MP)
+ * If it isn't the last datalink, just Close that datalink.
+ */
+
+ struct datalink *dl, *this_dl;
+ int others_active;
+
+ others_active = 0;
+ this_dl = NULL;
+
+ for (dl = bundle->links; dl; dl = dl->next) {
+ if (name && !strcasecmp(name, dl->name))
+ this_dl = dl;
+ if (name == NULL || this_dl == dl) {
+ switch (how) {
+ case CLOSE_LCP:
+ datalink_DontHangup(dl);
+ break;
+ case CLOSE_STAYDOWN:
+ datalink_StayDown(dl);
+ break;
+ }
+ } else if (dl->state != DATALINK_CLOSED && dl->state != DATALINK_HANGUP)
+ others_active++;
+ }
+
+ if (name && this_dl == NULL) {
+ log_Printf(LogWARN, "%s: Invalid datalink name\n", name);
+ return;
+ }
+
+ if (!others_active) {
+#ifndef NORADIUS
+ if (bundle->radius.sessiontime)
+ bundle_StopSessionTimer(bundle);
+#endif
+ bundle_StopIdleTimer(bundle);
+ if (ncp_LayersUnfinished(&bundle->ncp))
+ ncp_Close(&bundle->ncp);
+ else {
+ ncp2initial(&bundle->ncp);
+ mp_Down(&bundle->ncp.mp);
+ for (dl = bundle->links; dl; dl = dl->next)
+ datalink_Close(dl, how);
+ }
+ } else if (this_dl && this_dl->state != DATALINK_CLOSED &&
+ this_dl->state != DATALINK_HANGUP)
+ datalink_Close(this_dl, how);
+}
+
+void
+bundle_Down(struct bundle *bundle, int how)
+{
+ struct datalink *dl;
+
+ for (dl = bundle->links; dl; dl = dl->next)
+ datalink_Down(dl, how);
+}
+
+static int
+bundle_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n)
+{
+ struct bundle *bundle = descriptor2bundle(d);
+ struct datalink *dl;
+ int result, nlinks;
+ u_short ifqueue;
+ size_t queued;
+
+ result = 0;
+
+ /* If there are aren't many packets queued, look for some more. */
+ for (nlinks = 0, dl = bundle->links; dl; dl = dl->next)
+ nlinks++;
+
+ if (nlinks) {
+ queued = r ? ncp_FillPhysicalQueues(&bundle->ncp, bundle) :
+ ncp_QueueLen(&bundle->ncp);
+
+ if (r && (bundle->phase == PHASE_NETWORK ||
+ bundle->phys_type.all & PHYS_AUTO)) {
+ /* enough surplus so that we can tell if we're getting swamped */
+ ifqueue = nlinks > bundle->cfg.ifqueue ? nlinks : bundle->cfg.ifqueue;
+ if (queued < ifqueue) {
+ /* Not enough - select() for more */
+ if (bundle->choked.timer.state == TIMER_RUNNING)
+ timer_Stop(&bundle->choked.timer); /* Not needed any more */
+ FD_SET(bundle->dev.fd, r);
+ if (*n < bundle->dev.fd + 1)
+ *n = bundle->dev.fd + 1;
+ log_Printf(LogTIMER, "%s: fdset(r) %d\n", TUN_NAME, bundle->dev.fd);
+ result++;
+ } else if (bundle->choked.timer.state == TIMER_STOPPED) {
+ bundle->choked.timer.func = bundle_ClearQueues;
+ bundle->choked.timer.name = "output choke";
+ bundle->choked.timer.load = bundle->cfg.choked.timeout * SECTICKS;
+ bundle->choked.timer.arg = bundle;
+ timer_Start(&bundle->choked.timer);
+ }
+ }
+ }
+
+#ifndef NORADIUS
+ result += descriptor_UpdateSet(&bundle->radius.desc, r, w, e, n);
+#endif
+
+ /* Which links need a select() ? */
+ for (dl = bundle->links; dl; dl = dl->next)
+ result += descriptor_UpdateSet(&dl->desc, r, w, e, n);
+
+ /*
+ * This *MUST* be called after the datalink UpdateSet()s as it
+ * might be ``holding'' one of the datalinks (death-row) and
+ * wants to be able to de-select() it from the descriptor set.
+ */
+ result += descriptor_UpdateSet(&bundle->ncp.mp.server.desc, r, w, e, n);
+
+ return result;
+}
+
+static int
+bundle_IsSet(struct fdescriptor *d, const fd_set *fdset)
+{
+ struct bundle *bundle = descriptor2bundle(d);
+ struct datalink *dl;
+
+ for (dl = bundle->links; dl; dl = dl->next)
+ if (descriptor_IsSet(&dl->desc, fdset))
+ return 1;
+
+#ifndef NORADIUS
+ if (descriptor_IsSet(&bundle->radius.desc, fdset))
+ return 1;
+#endif
+
+ if (descriptor_IsSet(&bundle->ncp.mp.server.desc, fdset))
+ return 1;
+
+ return FD_ISSET(bundle->dev.fd, fdset);
+}
+
+static void
+bundle_DescriptorRead(struct fdescriptor *d, struct bundle *bundle,
+ const fd_set *fdset)
+{
+ struct datalink *dl;
+ unsigned secs;
+ u_int32_t af;
+
+ if (descriptor_IsSet(&bundle->ncp.mp.server.desc, fdset))
+ descriptor_Read(&bundle->ncp.mp.server.desc, bundle, fdset);
+
+ for (dl = bundle->links; dl; dl = dl->next)
+ if (descriptor_IsSet(&dl->desc, fdset))
+ descriptor_Read(&dl->desc, bundle, fdset);
+
+#ifndef NORADIUS
+ if (descriptor_IsSet(&bundle->radius.desc, fdset))
+ descriptor_Read(&bundle->radius.desc, bundle, fdset);
+#endif
+
+ if (FD_ISSET(bundle->dev.fd, fdset)) {
+ struct tun_data tun;
+ int n, pri;
+ u_char *data;
+ size_t sz;
+
+ if (bundle->dev.header) {
+ data = (u_char *)&tun;
+ sz = sizeof tun;
+ } else {
+ data = tun.data;
+ sz = sizeof tun.data;
+ }
+
+ /* something to read from tun */
+
+ n = read(bundle->dev.fd, data, sz);
+ if (n < 0) {
+ log_Printf(LogWARN, "%s: read: %s\n", bundle->dev.Name, strerror(errno));
+ return;
+ }
+
+ if (bundle->dev.header) {
+ n -= sz - sizeof tun.data;
+ if (n <= 0) {
+ log_Printf(LogERROR, "%s: read: Got only %d bytes of data !\n",
+ bundle->dev.Name, n);
+ return;
+ }
+ af = ntohl(tun.header.family);
+#ifndef NOINET6
+ if (af != AF_INET && af != AF_INET6)
+#else
+ if (af != AF_INET)
+#endif
+ /* XXX: Should be maintaining drop/family counts ! */
+ return;
+ } else
+ af = AF_INET;
+
+ if (af == AF_INET && ((struct ip *)tun.data)->ip_dst.s_addr ==
+ bundle->ncp.ipcp.my_ip.s_addr) {
+ /* we've been asked to send something addressed *to* us :( */
+ if (Enabled(bundle, OPT_LOOPBACK)) {
+ pri = PacketCheck(bundle, af, tun.data, n, &bundle->filter.in,
+ NULL, NULL);
+ if (pri >= 0) {
+ n += sz - sizeof tun.data;
+ write(bundle->dev.fd, data, n);
+ log_Printf(LogDEBUG, "Looped back packet addressed to myself\n");
+ }
+ return;
+ } else
+ log_Printf(LogDEBUG, "Oops - forwarding packet addressed to myself\n");
+ }
+
+ /*
+ * Process on-demand dialup. Output packets are queued within the tunnel
+ * device until the appropriate NCP is opened.
+ */
+
+ if (bundle_Phase(bundle) == PHASE_DEAD) {
+ /*
+ * Note, we must be in AUTO mode :-/ otherwise our interface should
+ * *not* be UP and we can't receive data
+ */
+ pri = PacketCheck(bundle, af, tun.data, n, &bundle->filter.dial,
+ NULL, NULL);
+ if (pri >= 0)
+ bundle_Open(bundle, NULL, PHYS_AUTO, 0);
+ else
+ /*
+ * Drop the packet. If we were to queue it, we'd just end up with
+ * a pile of timed-out data in our output queue by the time we get
+ * around to actually dialing. We'd also prematurely reach the
+ * threshold at which we stop select()ing to read() the tun
+ * device - breaking auto-dial.
+ */
+ return;
+ }
+
+ secs = 0;
+ pri = PacketCheck(bundle, af, tun.data, n, &bundle->filter.out,
+ NULL, &secs);
+ if (pri >= 0) {
+ /* Prepend the number of seconds timeout given in the filter */
+ tun.header.timeout = secs;
+ ncp_Enqueue(&bundle->ncp, af, pri, (char *)&tun, n + sizeof tun.header);
+ }
+ }
+}
+
+static int
+bundle_DescriptorWrite(struct fdescriptor *d, struct bundle *bundle,
+ const fd_set *fdset)
+{
+ struct datalink *dl;
+ int result = 0;
+
+ /* This is not actually necessary as struct mpserver doesn't Write() */
+ if (descriptor_IsSet(&bundle->ncp.mp.server.desc, fdset))
+ if (descriptor_Write(&bundle->ncp.mp.server.desc, bundle, fdset) == 1)
+ result++;
+
+ for (dl = bundle->links; dl; dl = dl->next)
+ if (descriptor_IsSet(&dl->desc, fdset))
+ switch (descriptor_Write(&dl->desc, bundle, fdset)) {
+ case -1:
+ datalink_ComeDown(dl, CLOSE_NORMAL);
+ break;
+ case 1:
+ result++;
+ }
+
+ return result;
+}
+
+void
+bundle_LockTun(struct bundle *bundle)
+{
+ FILE *lockfile;
+ char pidfile[PATH_MAX];
+
+ snprintf(pidfile, sizeof pidfile, "%stun%d.pid", _PATH_VARRUN, bundle->unit);
+ lockfile = ID0fopen(pidfile, "w");
+ if (lockfile != NULL) {
+ fprintf(lockfile, "%d\n", (int)getpid());
+ fclose(lockfile);
+ }
+#ifndef RELEASE_CRUNCH
+ else
+ log_Printf(LogERROR, "Warning: Can't create %s: %s\n",
+ pidfile, strerror(errno));
+#endif
+}
+
+static void
+bundle_UnlockTun(struct bundle *bundle)
+{
+ char pidfile[PATH_MAX];
+
+ snprintf(pidfile, sizeof pidfile, "%stun%d.pid", _PATH_VARRUN, bundle->unit);
+ ID0unlink(pidfile);
+}
+
+struct bundle *
+bundle_Create(const char *prefix, int type, int unit)
+{
+ static struct bundle bundle; /* there can be only one */
+ int enoentcount, err, minunit, maxunit;
+ const char *ifname;
+#if defined(__FreeBSD__) && !defined(NOKLDLOAD)
+ int kldtried;
+#endif
+#if defined(TUNSIFMODE) || defined(TUNSLMODE) || defined(TUNSIFHEAD)
+ int iff;
+#endif
+
+ if (bundle.iface != NULL) { /* Already allocated ! */
+ log_Printf(LogALERT, "bundle_Create: There's only one BUNDLE !\n");
+ return NULL;
+ }
+
+ if (unit == -1) {
+ minunit = 0;
+ maxunit = -1;
+ } else {
+ minunit = unit;
+ maxunit = unit + 1;
+ }
+ err = ENOENT;
+ enoentcount = 0;
+#if defined(__FreeBSD__) && !defined(NOKLDLOAD)
+ kldtried = 0;
+#endif
+ for (bundle.unit = minunit; bundle.unit != maxunit; bundle.unit++) {
+ snprintf(bundle.dev.Name, sizeof bundle.dev.Name, "%s%d",
+ prefix, bundle.unit);
+ bundle.dev.fd = ID0open(bundle.dev.Name, O_RDWR);
+ if (bundle.dev.fd >= 0)
+ break;
+ else if (errno == ENXIO || errno == ENOENT) {
+#if defined(__FreeBSD__) && !defined(NOKLDLOAD)
+ if (bundle.unit == minunit && !kldtried++) {
+ /*
+ * Attempt to load the tunnel interface KLD if it isn't loaded
+ * already.
+ */
+ if (loadmodules(LOAD_VERBOSLY, "if_tun", NULL))
+ bundle.unit--;
+ continue;
+ }
+#endif
+ if (errno != ENOENT || ++enoentcount > 2) {
+ err = errno;
+ break;
+ }
+ } else
+ err = errno;
+ }
+
+ if (bundle.dev.fd < 0) {
+ if (unit == -1)
+ log_Printf(LogWARN, "No available tunnel devices found (%s)\n",
+ strerror(err));
+ else
+ log_Printf(LogWARN, "%s%d: %s\n", prefix, unit, strerror(err));
+ return NULL;
+ }
+
+ log_SetTun(bundle.unit);
+
+ ifname = strrchr(bundle.dev.Name, '/');
+ if (ifname == NULL)
+ ifname = bundle.dev.Name;
+ else
+ ifname++;
+
+ bundle.iface = iface_Create(ifname);
+ if (bundle.iface == NULL) {
+ close(bundle.dev.fd);
+ return NULL;
+ }
+
+#ifdef TUNSIFMODE
+ /* Make sure we're POINTOPOINT & IFF_MULTICAST */
+ iff = IFF_POINTOPOINT | IFF_MULTICAST;
+ if (ID0ioctl(bundle.dev.fd, TUNSIFMODE, &iff) < 0)
+ log_Printf(LogERROR, "bundle_Create: ioctl(TUNSIFMODE): %s\n",
+ strerror(errno));
+#endif
+
+#ifdef TUNSLMODE
+ /* Make sure we're not prepending sockaddrs */
+ iff = 0;
+ if (ID0ioctl(bundle.dev.fd, TUNSLMODE, &iff) < 0)
+ log_Printf(LogERROR, "bundle_Create: ioctl(TUNSLMODE): %s\n",
+ strerror(errno));
+#endif
+
+#ifdef TUNSIFHEAD
+ /* We want the address family please ! */
+ iff = 1;
+ if (ID0ioctl(bundle.dev.fd, TUNSIFHEAD, &iff) < 0) {
+ log_Printf(LogERROR, "bundle_Create: ioctl(TUNSIFHEAD): %s\n",
+ strerror(errno));
+ bundle.dev.header = 0;
+ } else
+ bundle.dev.header = 1;
+#else
+#ifdef __OpenBSD__
+ /* Always present for OpenBSD */
+ bundle.dev.header = 1;
+#else
+ /*
+ * If TUNSIFHEAD isn't available and we're not OpenBSD, assume
+ * everything's AF_INET (hopefully the tun device won't pass us
+ * anything else !).
+ */
+ bundle.dev.header = 0;
+#endif
+#endif
+
+ log_Printf(LogPHASE, "Using interface: %s\n", ifname);
+
+ bundle.bandwidth = 0;
+ bundle.routing_seq = 0;
+ bundle.phase = PHASE_DEAD;
+ bundle.CleaningUp = 0;
+ bundle.NatEnabled = 0;
+
+ bundle.fsm.LayerStart = bundle_LayerStart;
+ bundle.fsm.LayerUp = bundle_LayerUp;
+ bundle.fsm.LayerDown = bundle_LayerDown;
+ bundle.fsm.LayerFinish = bundle_LayerFinish;
+ bundle.fsm.object = &bundle;
+
+ bundle.cfg.idle.timeout = NCP_IDLE_TIMEOUT;
+ bundle.cfg.idle.min_timeout = 0;
+ *bundle.cfg.auth.name = '\0';
+ *bundle.cfg.auth.key = '\0';
+ bundle.cfg.opt = OPT_IDCHECK | OPT_LOOPBACK | OPT_SROUTES | OPT_TCPMSSFIXUP |
+ OPT_THROUGHPUT | OPT_UTMP;
+#ifndef NOINET6
+ bundle.cfg.opt |= OPT_IPCP;
+ if (probe.ipv6_available)
+ bundle.cfg.opt |= OPT_IPV6CP;
+#endif
+ *bundle.cfg.label = '\0';
+ bundle.cfg.ifqueue = DEF_IFQUEUE;
+ bundle.cfg.choked.timeout = CHOKED_TIMEOUT;
+ bundle.phys_type.all = type;
+ bundle.phys_type.open = 0;
+ bundle.upat = 0;
+
+ bundle.links = datalink_Create("deflink", &bundle, type);
+ if (bundle.links == NULL) {
+ log_Printf(LogALERT, "Cannot create data link: %s\n", strerror(errno));
+ iface_Destroy(bundle.iface);
+ bundle.iface = NULL;
+ close(bundle.dev.fd);
+ return NULL;
+ }
+
+ bundle.desc.type = BUNDLE_DESCRIPTOR;
+ bundle.desc.UpdateSet = bundle_UpdateSet;
+ bundle.desc.IsSet = bundle_IsSet;
+ bundle.desc.Read = bundle_DescriptorRead;
+ bundle.desc.Write = bundle_DescriptorWrite;
+
+ ncp_Init(&bundle.ncp, &bundle);
+
+ memset(&bundle.filter, '\0', sizeof bundle.filter);
+ bundle.filter.in.fragok = bundle.filter.in.logok = 1;
+ bundle.filter.in.name = "IN";
+ bundle.filter.out.fragok = bundle.filter.out.logok = 1;
+ bundle.filter.out.name = "OUT";
+ bundle.filter.dial.name = "DIAL";
+ bundle.filter.dial.logok = 1;
+ bundle.filter.alive.name = "ALIVE";
+ bundle.filter.alive.logok = 1;
+ {
+ int i;
+ for (i = 0; i < MAXFILTERS; i++) {
+ bundle.filter.in.rule[i].f_action = A_NONE;
+ bundle.filter.out.rule[i].f_action = A_NONE;
+ bundle.filter.dial.rule[i].f_action = A_NONE;
+ bundle.filter.alive.rule[i].f_action = A_NONE;
+ }
+ }
+ memset(&bundle.idle.timer, '\0', sizeof bundle.idle.timer);
+ bundle.idle.done = 0;
+ bundle.notify.fd = -1;
+ memset(&bundle.choked.timer, '\0', sizeof bundle.choked.timer);
+#ifndef NORADIUS
+ radius_Init(&bundle.radius);
+#endif
+
+ /* Clean out any leftover crud */
+ iface_Clear(bundle.iface, &bundle.ncp, 0, IFACE_CLEAR_ALL);
+
+ bundle_LockTun(&bundle);
+
+ return &bundle;
+}
+
+static void
+bundle_DownInterface(struct bundle *bundle)
+{
+ route_IfDelete(bundle, 1);
+ iface_ClearFlags(bundle->iface->name, IFF_UP);
+}
+
+void
+bundle_Destroy(struct bundle *bundle)
+{
+ struct datalink *dl;
+
+ /*
+ * Clean up the interface. We don't really need to do the timer_Stop()s,
+ * mp_Down(), iface_Clear() and bundle_DownInterface() unless we're getting
+ * out under exceptional conditions such as a descriptor exception.
+ */
+ timer_Stop(&bundle->idle.timer);
+ timer_Stop(&bundle->choked.timer);
+ mp_Down(&bundle->ncp.mp);
+ iface_Clear(bundle->iface, &bundle->ncp, 0, IFACE_CLEAR_ALL);
+ bundle_DownInterface(bundle);
+
+#ifndef NORADIUS
+ /* Tell the radius server the bad news */
+ radius_Destroy(&bundle->radius);
+#endif
+
+ /* Again, these are all DATALINK_CLOSED unless we're abending */
+ dl = bundle->links;
+ while (dl)
+ dl = datalink_Destroy(dl);
+
+ ncp_Destroy(&bundle->ncp);
+
+ close(bundle->dev.fd);
+ bundle_UnlockTun(bundle);
+
+ /* In case we never made PHASE_NETWORK */
+ bundle_Notify(bundle, EX_ERRDEAD);
+
+ iface_Destroy(bundle->iface);
+ bundle->iface = NULL;
+}
+
+void
+bundle_LinkClosed(struct bundle *bundle, struct datalink *dl)
+{
+ /*
+ * Our datalink has closed.
+ * CleanDatalinks() (called from DoLoop()) will remove closed
+ * BACKGROUND, FOREGROUND and DIRECT links.
+ * If it's the last data link, enter phase DEAD.
+ *
+ * NOTE: dl may not be in our list (bundle_SendDatalink()) !
+ */
+
+ struct datalink *odl;
+ int other_links;
+
+ log_SetTtyCommandMode(dl);
+
+ other_links = 0;
+ for (odl = bundle->links; odl; odl = odl->next)
+ if (odl != dl && odl->state != DATALINK_CLOSED)
+ other_links++;
+
+ if (!other_links) {
+ if (dl->physical->type != PHYS_AUTO) /* Not in -auto mode */
+ bundle_DownInterface(bundle);
+ ncp2initial(&bundle->ncp);
+ mp_Down(&bundle->ncp.mp);
+ bundle_NewPhase(bundle, PHASE_DEAD);
+#ifndef NORADIUS
+ if (bundle->radius.sessiontime)
+ bundle_StopSessionTimer(bundle);
+#endif
+ bundle_StopIdleTimer(bundle);
+ }
+}
+
+void
+bundle_Open(struct bundle *bundle, const char *name, int mask, int force)
+{
+ /*
+ * Please open the given datalink, or all if name == NULL
+ */
+ struct datalink *dl;
+
+ for (dl = bundle->links; dl; dl = dl->next)
+ if (name == NULL || !strcasecmp(dl->name, name)) {
+ if ((mask & dl->physical->type) &&
+ (dl->state == DATALINK_CLOSED ||
+ (force && dl->state == DATALINK_OPENING &&
+ dl->dial.timer.state == TIMER_RUNNING) ||
+ dl->state == DATALINK_READY)) {
+ timer_Stop(&dl->dial.timer); /* We're finished with this */
+ datalink_Up(dl, 1, 1);
+ if (mask & PHYS_AUTO)
+ break; /* Only one AUTO link at a time */
+ }
+ if (name != NULL)
+ break;
+ }
+}
+
+struct datalink *
+bundle2datalink(struct bundle *bundle, const char *name)
+{
+ struct datalink *dl;
+
+ if (name != NULL) {
+ for (dl = bundle->links; dl; dl = dl->next)
+ if (!strcasecmp(dl->name, name))
+ return dl;
+ } else if (bundle->links && !bundle->links->next)
+ return bundle->links;
+
+ return NULL;
+}
+
+int
+bundle_ShowLinks(struct cmdargs const *arg)
+{
+ struct datalink *dl;
+ struct pppThroughput *t;
+ unsigned long long octets;
+ int secs;
+
+ for (dl = arg->bundle->links; dl; dl = dl->next) {
+ octets = MAX(dl->physical->link.stats.total.in.OctetsPerSecond,
+ dl->physical->link.stats.total.out.OctetsPerSecond);
+
+ prompt_Printf(arg->prompt, "Name: %s [%s, %s]",
+ dl->name, mode2Nam(dl->physical->type), datalink_State(dl));
+ if (dl->physical->link.stats.total.rolling && dl->state == DATALINK_OPEN)
+ prompt_Printf(arg->prompt, " bandwidth %d, %llu bps (%llu bytes/sec)",
+ dl->mp.bandwidth ? dl->mp.bandwidth :
+ physical_GetSpeed(dl->physical),
+ octets * 8, octets);
+ prompt_Printf(arg->prompt, "\n");
+ }
+
+ t = &arg->bundle->ncp.mp.link.stats.total;
+ octets = MAX(t->in.OctetsPerSecond, t->out.OctetsPerSecond);
+ secs = t->downtime ? 0 : throughput_uptime(t);
+ if (secs > t->SamplePeriod)
+ secs = t->SamplePeriod;
+ if (secs)
+ prompt_Printf(arg->prompt, "Currently averaging %llu bps (%llu bytes/sec)"
+ " over the last %d secs\n", octets * 8, octets, secs);
+
+ return 0;
+}
+
+static const char *
+optval(struct bundle *bundle, int bit)
+{
+ return (bundle->cfg.opt & bit) ? "enabled" : "disabled";
+}
+
+int
+bundle_ShowStatus(struct cmdargs const *arg)
+{
+ int remaining;
+
+ prompt_Printf(arg->prompt, "Phase %s\n", bundle_PhaseName(arg->bundle));
+ prompt_Printf(arg->prompt, " Device: %s\n", arg->bundle->dev.Name);
+ prompt_Printf(arg->prompt, " Interface: %s @ %lubps",
+ arg->bundle->iface->name, arg->bundle->bandwidth);
+
+ if (arg->bundle->upat) {
+ int secs = bundle_Uptime(arg->bundle);
+
+ prompt_Printf(arg->prompt, ", up time %d:%02d:%02d", secs / 3600,
+ (secs / 60) % 60, secs % 60);
+ }
+ prompt_Printf(arg->prompt, "\n Queued: %lu of %u\n",
+ (unsigned long)ncp_QueueLen(&arg->bundle->ncp),
+ arg->bundle->cfg.ifqueue);
+
+ prompt_Printf(arg->prompt, "\nDefaults:\n");
+ prompt_Printf(arg->prompt, " Label: %s\n",
+ arg->bundle->cfg.label);
+ prompt_Printf(arg->prompt, " Auth name: %s\n",
+ arg->bundle->cfg.auth.name);
+ prompt_Printf(arg->prompt, " Diagnostic socket: ");
+ if (*server.cfg.sockname != '\0') {
+ prompt_Printf(arg->prompt, "%s", server.cfg.sockname);
+ if (server.cfg.mask != (mode_t)-1)
+ prompt_Printf(arg->prompt, ", mask 0%03o", (int)server.cfg.mask);
+ prompt_Printf(arg->prompt, "%s\n", server.fd == -1 ? " (not open)" : "");
+ } else if (server.cfg.port != 0)
+ prompt_Printf(arg->prompt, "TCP port %d%s\n", server.cfg.port,
+ server.fd == -1 ? " (not open)" : "");
+ else
+ prompt_Printf(arg->prompt, "none\n");
+
+ prompt_Printf(arg->prompt, " Choked Timer: %ds\n",
+ arg->bundle->cfg.choked.timeout);
+
+#ifndef NORADIUS
+ radius_Show(&arg->bundle->radius, arg->prompt);
+#endif
+
+ prompt_Printf(arg->prompt, " Idle Timer: ");
+ if (arg->bundle->cfg.idle.timeout) {
+ prompt_Printf(arg->prompt, "%ds", arg->bundle->cfg.idle.timeout);
+ if (arg->bundle->cfg.idle.min_timeout)
+ prompt_Printf(arg->prompt, ", min %ds",
+ arg->bundle->cfg.idle.min_timeout);
+ remaining = bundle_RemainingIdleTime(arg->bundle);
+ if (remaining != -1)
+ prompt_Printf(arg->prompt, " (%ds remaining)", remaining);
+ prompt_Printf(arg->prompt, "\n");
+ } else
+ prompt_Printf(arg->prompt, "disabled\n");
+
+ prompt_Printf(arg->prompt, " Filter Decap: %-20.20s",
+ optval(arg->bundle, OPT_FILTERDECAP));
+ prompt_Printf(arg->prompt, " ID check: %s\n",
+ optval(arg->bundle, OPT_IDCHECK));
+ prompt_Printf(arg->prompt, " Iface-Alias: %-20.20s",
+ optval(arg->bundle, OPT_IFACEALIAS));
+#ifndef NOINET6
+ prompt_Printf(arg->prompt, " IPCP: %s\n",
+ optval(arg->bundle, OPT_IPCP));
+ prompt_Printf(arg->prompt, " IPV6CP: %-20.20s",
+ optval(arg->bundle, OPT_IPV6CP));
+#endif
+ prompt_Printf(arg->prompt, " Keep-Session: %s\n",
+ optval(arg->bundle, OPT_KEEPSESSION));
+ prompt_Printf(arg->prompt, " Loopback: %-20.20s",
+ optval(arg->bundle, OPT_LOOPBACK));
+ prompt_Printf(arg->prompt, " PasswdAuth: %s\n",
+ optval(arg->bundle, OPT_PASSWDAUTH));
+ prompt_Printf(arg->prompt, " Proxy: %-20.20s",
+ optval(arg->bundle, OPT_PROXY));
+ prompt_Printf(arg->prompt, " Proxyall: %s\n",
+ optval(arg->bundle, OPT_PROXYALL));
+ prompt_Printf(arg->prompt, " Sticky Routes: %-20.20s",
+ optval(arg->bundle, OPT_SROUTES));
+ prompt_Printf(arg->prompt, " TCPMSS Fixup: %s\n",
+ optval(arg->bundle, OPT_TCPMSSFIXUP));
+ prompt_Printf(arg->prompt, " Throughput: %-20.20s",
+ optval(arg->bundle, OPT_THROUGHPUT));
+ prompt_Printf(arg->prompt, " Utmp Logging: %s\n",
+ optval(arg->bundle, OPT_UTMP));
+
+ return 0;
+}
+
+static void
+bundle_IdleTimeout(void *v)
+{
+ struct bundle *bundle = (struct bundle *)v;
+
+ log_Printf(LogPHASE, "Idle timer expired\n");
+ bundle_StopIdleTimer(bundle);
+ bundle_Close(bundle, NULL, CLOSE_STAYDOWN);
+}
+
+/*
+ * Start Idle timer. If timeout is reached, we call bundle_Close() to
+ * close LCP and link.
+ */
+void
+bundle_StartIdleTimer(struct bundle *bundle, unsigned secs)
+{
+ timer_Stop(&bundle->idle.timer);
+ if ((bundle->phys_type.open & (PHYS_DEDICATED|PHYS_DDIAL)) !=
+ bundle->phys_type.open && bundle->cfg.idle.timeout) {
+ time_t now = time(NULL);
+
+ if (secs == 0)
+ secs = bundle->cfg.idle.timeout;
+
+ /* We want at least `secs' */
+ if (bundle->cfg.idle.min_timeout > secs && bundle->upat) {
+ int up = now - bundle->upat;
+
+ if ((long long)bundle->cfg.idle.min_timeout - up > (long long)secs)
+ /* Only increase from the current `remaining' value */
+ secs = bundle->cfg.idle.min_timeout - up;
+ }
+ bundle->idle.timer.func = bundle_IdleTimeout;
+ bundle->idle.timer.name = "idle";
+ bundle->idle.timer.load = secs * SECTICKS;
+ bundle->idle.timer.arg = bundle;
+ timer_Start(&bundle->idle.timer);
+ bundle->idle.done = now + secs;
+ }
+}
+
+void
+bundle_SetIdleTimer(struct bundle *bundle, int timeout, int min_timeout)
+{
+ bundle->cfg.idle.timeout = timeout;
+ if (min_timeout >= 0)
+ bundle->cfg.idle.min_timeout = min_timeout;
+ if (ncp_LayersOpen(&bundle->ncp))
+ bundle_StartIdleTimer(bundle, 0);
+}
+
+void
+bundle_StopIdleTimer(struct bundle *bundle)
+{
+ timer_Stop(&bundle->idle.timer);
+ bundle->idle.done = 0;
+}
+
+static int
+bundle_RemainingIdleTime(struct bundle *bundle)
+{
+ if (bundle->idle.done)
+ return bundle->idle.done - time(NULL);
+ return -1;
+}
+
+#ifndef NORADIUS
+
+static void
+bundle_SessionTimeout(void *v)
+{
+ struct bundle *bundle = (struct bundle *)v;
+
+ log_Printf(LogPHASE, "Session-Timeout timer expired\n");
+ bundle_StopSessionTimer(bundle);
+ bundle_Close(bundle, NULL, CLOSE_STAYDOWN);
+}
+
+void
+bundle_StartSessionTimer(struct bundle *bundle, unsigned secs)
+{
+ timer_Stop(&bundle->session.timer);
+ if ((bundle->phys_type.open & (PHYS_DEDICATED|PHYS_DDIAL)) !=
+ bundle->phys_type.open && bundle->radius.sessiontime) {
+ time_t now = time(NULL);
+
+ if (secs == 0)
+ secs = bundle->radius.sessiontime;
+
+ bundle->session.timer.func = bundle_SessionTimeout;
+ bundle->session.timer.name = "session";
+ bundle->session.timer.load = secs * SECTICKS;
+ bundle->session.timer.arg = bundle;
+ timer_Start(&bundle->session.timer);
+ bundle->session.done = now + secs;
+ }
+}
+
+void
+bundle_StopSessionTimer(struct bundle *bundle)
+{
+ timer_Stop(&bundle->session.timer);
+ bundle->session.done = 0;
+}
+
+#endif
+
+int
+bundle_IsDead(struct bundle *bundle)
+{
+ return !bundle->links || (bundle->phase == PHASE_DEAD && bundle->CleaningUp);
+}
+
+static struct datalink *
+bundle_DatalinkLinkout(struct bundle *bundle, struct datalink *dl)
+{
+ struct datalink **dlp;
+
+ for (dlp = &bundle->links; *dlp; dlp = &(*dlp)->next)
+ if (*dlp == dl) {
+ *dlp = dl->next;
+ dl->next = NULL;
+ bundle_LinksRemoved(bundle);
+ return dl;
+ }
+
+ return NULL;
+}
+
+static void
+bundle_DatalinkLinkin(struct bundle *bundle, struct datalink *dl)
+{
+ struct datalink **dlp = &bundle->links;
+
+ while (*dlp)
+ dlp = &(*dlp)->next;
+
+ *dlp = dl;
+ dl->next = NULL;
+
+ bundle_LinkAdded(bundle, dl);
+ mp_CheckAutoloadTimer(&bundle->ncp.mp);
+}
+
+void
+bundle_CleanDatalinks(struct bundle *bundle)
+{
+ struct datalink **dlp = &bundle->links;
+ int found = 0;
+
+ while (*dlp)
+ if ((*dlp)->state == DATALINK_CLOSED &&
+ (*dlp)->physical->type &
+ (PHYS_DIRECT|PHYS_BACKGROUND|PHYS_FOREGROUND)) {
+ *dlp = datalink_Destroy(*dlp);
+ found++;
+ } else
+ dlp = &(*dlp)->next;
+
+ if (found)
+ bundle_LinksRemoved(bundle);
+}
+
+int
+bundle_DatalinkClone(struct bundle *bundle, struct datalink *dl,
+ const char *name)
+{
+ if (bundle2datalink(bundle, name)) {
+ log_Printf(LogWARN, "Clone: %s: name already exists\n", name);
+ return 0;
+ }
+
+ bundle_DatalinkLinkin(bundle, datalink_Clone(dl, name));
+ return 1;
+}
+
+void
+bundle_DatalinkRemove(struct bundle *bundle, struct datalink *dl)
+{
+ dl = bundle_DatalinkLinkout(bundle, dl);
+ if (dl)
+ datalink_Destroy(dl);
+}
+
+void
+bundle_SetLabel(struct bundle *bundle, const char *label)
+{
+ if (label)
+ strncpy(bundle->cfg.label, label, sizeof bundle->cfg.label - 1);
+ else
+ *bundle->cfg.label = '\0';
+}
+
+const char *
+bundle_GetLabel(struct bundle *bundle)
+{
+ return *bundle->cfg.label ? bundle->cfg.label : NULL;
+}
+
+int
+bundle_LinkSize()
+{
+ struct iovec iov[SCATTER_SEGMENTS];
+ int niov, expect, f;
+
+ iov[0].iov_len = strlen(Version) + 1;
+ iov[0].iov_base = NULL;
+ niov = 1;
+ if (datalink2iov(NULL, iov, &niov, SCATTER_SEGMENTS, NULL, NULL) == -1) {
+ log_Printf(LogERROR, "Cannot determine space required for link\n");
+ return 0;
+ }
+
+ for (f = expect = 0; f < niov; f++)
+ expect += iov[f].iov_len;
+
+ return expect;
+}
+
+void
+bundle_ReceiveDatalink(struct bundle *bundle, int s)
+{
+ char cmsgbuf[sizeof(struct cmsghdr) + sizeof(int) * SEND_MAXFD];
+ int niov, expect, f, *fd, nfd, onfd, got;
+ struct iovec iov[SCATTER_SEGMENTS];
+ struct cmsghdr *cmsg;
+ struct msghdr msg;
+ struct datalink *dl;
+ pid_t pid;
+
+ log_Printf(LogPHASE, "Receiving datalink\n");
+
+ /*
+ * Create our scatter/gather array - passing NULL gets the space
+ * allocation requirement rather than actually flattening the
+ * structures.
+ */
+ iov[0].iov_len = strlen(Version) + 1;
+ iov[0].iov_base = NULL;
+ niov = 1;
+ if (datalink2iov(NULL, iov, &niov, SCATTER_SEGMENTS, NULL, NULL) == -1) {
+ log_Printf(LogERROR, "Cannot determine space required for link\n");
+ return;
+ }
+
+ /* Allocate the scatter/gather array for recvmsg() */
+ for (f = expect = 0; f < niov; f++) {
+ if ((iov[f].iov_base = malloc(iov[f].iov_len)) == NULL) {
+ log_Printf(LogERROR, "Cannot allocate space to receive link\n");
+ return;
+ }
+ if (f)
+ expect += iov[f].iov_len;
+ }
+
+ /* Set up our message */
+ cmsg = (struct cmsghdr *)cmsgbuf;
+ cmsg->cmsg_len = sizeof cmsgbuf;
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = 0;
+
+ memset(&msg, '\0', sizeof msg);
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 1; /* Only send the version at the first pass */
+ msg.msg_control = cmsgbuf;
+ msg.msg_controllen = sizeof cmsgbuf;
+
+ log_Printf(LogDEBUG, "Expecting %u scatter/gather bytes\n",
+ (unsigned)iov[0].iov_len);
+
+ if ((got = recvmsg(s, &msg, MSG_WAITALL)) != iov[0].iov_len) {
+ if (got == -1)
+ log_Printf(LogERROR, "Failed recvmsg: %s\n", strerror(errno));
+ else
+ log_Printf(LogERROR, "Failed recvmsg: Got %d, not %u\n",
+ got, (unsigned)iov[0].iov_len);
+ while (niov--)
+ free(iov[niov].iov_base);
+ return;
+ }
+
+ if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) {
+ log_Printf(LogERROR, "Recvmsg: no descriptors received !\n");
+ while (niov--)
+ free(iov[niov].iov_base);
+ return;
+ }
+
+ fd = (int *)CMSG_DATA(cmsg);
+ nfd = ((caddr_t)cmsg + cmsg->cmsg_len - (caddr_t)fd) / sizeof(int);
+
+ if (nfd < 2) {
+ log_Printf(LogERROR, "Recvmsg: %d descriptor%s received (too few) !\n",
+ nfd, nfd == 1 ? "" : "s");
+ while (nfd--)
+ close(fd[nfd]);
+ while (niov--)
+ free(iov[niov].iov_base);
+ return;
+ }
+
+ /*
+ * We've successfully received two or more open file descriptors
+ * through our socket, plus a version string. Make sure it's the
+ * correct version, and drop the connection if it's not.
+ */
+ if (strncmp(Version, iov[0].iov_base, iov[0].iov_len)) {
+ log_Printf(LogWARN, "Cannot receive datalink, incorrect version"
+ " (\"%.*s\", not \"%s\")\n", (int)iov[0].iov_len,
+ (char *)iov[0].iov_base, Version);
+ while (nfd--)
+ close(fd[nfd]);
+ while (niov--)
+ free(iov[niov].iov_base);
+ return;
+ }
+
+ /*
+ * Everything looks good. Send the other side our process id so that
+ * they can transfer lock ownership, and wait for them to send the
+ * actual link data.
+ */
+ pid = getpid();
+ if ((got = write(fd[1], &pid, sizeof pid)) != sizeof pid) {
+ if (got == -1)
+ log_Printf(LogERROR, "Failed write: %s\n", strerror(errno));
+ else
+ log_Printf(LogERROR, "Failed write: Got %d, not %d\n", got,
+ (int)(sizeof pid));
+ while (nfd--)
+ close(fd[nfd]);
+ while (niov--)
+ free(iov[niov].iov_base);
+ return;
+ }
+
+ if ((got = readv(fd[1], iov + 1, niov - 1)) != expect) {
+ if (got == -1)
+ log_Printf(LogERROR, "Failed write: %s\n", strerror(errno));
+ else
+ log_Printf(LogERROR, "Failed write: Got %d, not %d\n", got, expect);
+ while (nfd--)
+ close(fd[nfd]);
+ while (niov--)
+ free(iov[niov].iov_base);
+ return;
+ }
+ close(fd[1]);
+
+ onfd = nfd; /* We've got this many in our array */
+ nfd -= 2; /* Don't include p->fd and our reply descriptor */
+ niov = 1; /* Skip the version id */
+ dl = iov2datalink(bundle, iov, &niov, sizeof iov / sizeof *iov, fd[0],
+ fd + 2, &nfd);
+ if (dl) {
+
+ if (nfd) {
+ log_Printf(LogERROR, "bundle_ReceiveDatalink: Failed to handle %d "
+ "auxiliary file descriptors (%d remain)\n", onfd, nfd);
+ datalink_Destroy(dl);
+ while (nfd--)
+ close(fd[onfd--]);
+ close(fd[0]);
+ } else {
+ bundle_DatalinkLinkin(bundle, dl);
+ datalink_AuthOk(dl);
+ bundle_CalculateBandwidth(dl->bundle);
+ }
+ } else {
+ while (nfd--)
+ close(fd[onfd--]);
+ close(fd[0]);
+ close(fd[1]);
+ }
+
+ free(iov[0].iov_base);
+}
+
+void
+bundle_SendDatalink(struct datalink *dl, int s, struct sockaddr_un *sun)
+{
+ char cmsgbuf[CMSG_SPACE(sizeof(int) * SEND_MAXFD)];
+ const char *constlock;
+ char *lock;
+ struct cmsghdr *cmsg;
+ struct msghdr msg;
+ struct iovec iov[SCATTER_SEGMENTS];
+ int niov, f, expect, newsid, fd[SEND_MAXFD], nfd, reply[2], got;
+ pid_t newpid;
+
+ log_Printf(LogPHASE, "Transmitting datalink %s\n", dl->name);
+
+ /* Record the base device name for a lock transfer later */
+ constlock = physical_LockedDevice(dl->physical);
+ if (constlock) {
+ lock = alloca(strlen(constlock) + 1);
+ strcpy(lock, constlock);
+ } else
+ lock = NULL;
+
+ bundle_LinkClosed(dl->bundle, dl);
+ bundle_DatalinkLinkout(dl->bundle, dl);
+
+ /* Build our scatter/gather array */
+ iov[0].iov_len = strlen(Version) + 1;
+ iov[0].iov_base = strdup(Version);
+ niov = 1;
+ nfd = 0;
+
+ fd[0] = datalink2iov(dl, iov, &niov, SCATTER_SEGMENTS, fd + 2, &nfd);
+
+ if (fd[0] != -1 && socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, reply) != -1) {
+ /*
+ * fd[1] is used to get the peer process id back, then to confirm that
+ * we've transferred any device locks to that process id.
+ */
+ fd[1] = reply[1];
+
+ nfd += 2; /* Include fd[0] and fd[1] */
+ memset(&msg, '\0', sizeof msg);
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ /*
+ * Only send the version to start... We used to send the whole lot, but
+ * this caused problems with our RECVBUF size as a single link is about
+ * 22k ! This way, we should bump into no limits.
+ */
+ msg.msg_iovlen = 1;
+ msg.msg_iov = iov;
+ msg.msg_control = cmsgbuf;
+ msg.msg_controllen = CMSG_SPACE(sizeof(int) * nfd);
+ msg.msg_flags = 0;
+
+ cmsg = (struct cmsghdr *)cmsgbuf;
+ cmsg->cmsg_len = msg.msg_controllen;
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+
+ for (f = 0; f < nfd; f++)
+ *((int *)CMSG_DATA(cmsg) + f) = fd[f];
+
+ for (f = 1, expect = 0; f < niov; f++)
+ expect += iov[f].iov_len;
+
+ if (setsockopt(reply[0], SOL_SOCKET, SO_SNDBUF, &expect, sizeof(int)) == -1)
+ log_Printf(LogERROR, "setsockopt(SO_RCVBUF, %d): %s\n", expect,
+ strerror(errno));
+ if (setsockopt(reply[1], SOL_SOCKET, SO_RCVBUF, &expect, sizeof(int)) == -1)
+ log_Printf(LogERROR, "setsockopt(SO_RCVBUF, %d): %s\n", expect,
+ strerror(errno));
+
+ log_Printf(LogDEBUG, "Sending %d descriptor%s and %u bytes in scatter"
+ "/gather array\n", nfd, nfd == 1 ? "" : "s",
+ (unsigned)iov[0].iov_len);
+
+ if ((got = sendmsg(s, &msg, 0)) == -1)
+ log_Printf(LogERROR, "Failed sendmsg: %s: %s\n",
+ sun->sun_path, strerror(errno));
+ else if (got != iov[0].iov_len)
+ log_Printf(LogERROR, "%s: Failed initial sendmsg: Only sent %d of %u\n",
+ sun->sun_path, got, (unsigned)iov[0].iov_len);
+ else {
+ /* We must get the ACK before closing the descriptor ! */
+ int res;
+
+ if ((got = read(reply[0], &newpid, sizeof newpid)) == sizeof newpid) {
+ log_Printf(LogDEBUG, "Received confirmation from pid %ld\n",
+ (long)newpid);
+ if (lock && (res = ID0uu_lock_txfr(lock, newpid)) != UU_LOCK_OK)
+ log_Printf(LogERROR, "uu_lock_txfr: %s\n", uu_lockerr(res));
+
+ log_Printf(LogDEBUG, "Transmitting link (%d bytes)\n", expect);
+ if ((got = writev(reply[0], iov + 1, niov - 1)) != expect) {
+ if (got == -1)
+ log_Printf(LogERROR, "%s: Failed writev: %s\n",
+ sun->sun_path, strerror(errno));
+ else
+ log_Printf(LogERROR, "%s: Failed writev: Wrote %d of %d\n",
+ sun->sun_path, got, expect);
+ }
+ } else if (got == -1)
+ log_Printf(LogERROR, "%s: Failed socketpair read: %s\n",
+ sun->sun_path, strerror(errno));
+ else
+ log_Printf(LogERROR, "%s: Failed socketpair read: Got %d of %d\n",
+ sun->sun_path, got, (int)(sizeof newpid));
+ }
+
+ close(reply[0]);
+ close(reply[1]);
+
+ newsid = Enabled(dl->bundle, OPT_KEEPSESSION) ||
+ tcgetpgrp(fd[0]) == getpgrp();
+ while (nfd)
+ close(fd[--nfd]);
+ if (newsid)
+ bundle_setsid(dl->bundle, got != -1);
+ }
+ close(s);
+
+ while (niov--)
+ free(iov[niov].iov_base);
+}
+
+int
+bundle_RenameDatalink(struct bundle *bundle, struct datalink *ndl,
+ const char *name)
+{
+ struct datalink *dl;
+
+ if (!strcasecmp(ndl->name, name))
+ return 1;
+
+ for (dl = bundle->links; dl; dl = dl->next)
+ if (!strcasecmp(dl->name, name))
+ return 0;
+
+ datalink_Rename(ndl, name);
+ return 1;
+}
+
+int
+bundle_SetMode(struct bundle *bundle, struct datalink *dl, int mode)
+{
+ int omode;
+
+ omode = dl->physical->type;
+ if (omode == mode)
+ return 1;
+
+ if (mode == PHYS_AUTO && !(bundle->phys_type.all & PHYS_AUTO))
+ /* First auto link */
+ if (bundle->ncp.ipcp.peer_ip.s_addr == INADDR_ANY) {
+ log_Printf(LogWARN, "You must `set ifaddr' or `open' before"
+ " changing mode to %s\n", mode2Nam(mode));
+ return 0;
+ }
+
+ if (!datalink_SetMode(dl, mode))
+ return 0;
+
+ if (mode == PHYS_AUTO && !(bundle->phys_type.all & PHYS_AUTO) &&
+ bundle->phase != PHASE_NETWORK)
+ /* First auto link, we need an interface */
+ ipcp_InterfaceUp(&bundle->ncp.ipcp);
+
+ /* Regenerate phys_type and adjust idle timer */
+ bundle_LinksRemoved(bundle);
+
+ return 1;
+}
+
+void
+bundle_setsid(struct bundle *bundle, int holdsession)
+{
+ /*
+ * Lose the current session. This means getting rid of our pid
+ * too so that the tty device will really go away, and any getty
+ * etc will be allowed to restart.
+ */
+ pid_t pid, orig;
+ int fds[2];
+ char done;
+ struct datalink *dl;
+
+ if (!holdsession && bundle_IsDead(bundle)) {
+ /*
+ * No need to lose our session after all... we're going away anyway
+ *
+ * We should really stop the timer and pause if holdsession is set and
+ * the bundle's dead, but that leaves other resources lying about :-(
+ */
+ return;
+ }
+
+ orig = getpid();
+ if (pipe(fds) == -1) {
+ log_Printf(LogERROR, "pipe: %s\n", strerror(errno));
+ return;
+ }
+ switch ((pid = fork())) {
+ case -1:
+ log_Printf(LogERROR, "fork: %s\n", strerror(errno));
+ close(fds[0]);
+ close(fds[1]);
+ return;
+ case 0:
+ close(fds[1]);
+ read(fds[0], &done, 1); /* uu_locks are mine ! */
+ close(fds[0]);
+ if (pipe(fds) == -1) {
+ log_Printf(LogERROR, "pipe(2): %s\n", strerror(errno));
+ return;
+ }
+ switch ((pid = fork())) {
+ case -1:
+ log_Printf(LogERROR, "fork(2): %s\n", strerror(errno));
+ close(fds[0]);
+ close(fds[1]);
+ return;
+ case 0:
+ close(fds[1]);
+ bundle_LockTun(bundle); /* update pid */
+ read(fds[0], &done, 1); /* uu_locks are mine ! */
+ close(fds[0]);
+ setsid();
+ bundle_ChangedPID(bundle);
+ log_Printf(LogDEBUG, "%ld -> %ld: %s session control\n",
+ (long)orig, (long)getpid(),
+ holdsession ? "Passed" : "Dropped");
+ timer_InitService(0); /* Start the Timer Service */
+ break;
+ default:
+ close(fds[0]);
+ /* Give away all our physical locks (to the final process) */
+ for (dl = bundle->links; dl; dl = dl->next)
+ if (dl->state != DATALINK_CLOSED)
+ physical_ChangedPid(dl->physical, pid);
+ write(fds[1], "!", 1); /* done */
+ close(fds[1]);
+ _exit(0);
+ break;
+ }
+ break;
+ default:
+ close(fds[0]);
+ /* Give away all our physical locks (to the intermediate process) */
+ for (dl = bundle->links; dl; dl = dl->next)
+ if (dl->state != DATALINK_CLOSED)
+ physical_ChangedPid(dl->physical, pid);
+ write(fds[1], "!", 1); /* done */
+ close(fds[1]);
+ if (holdsession) {
+ int fd, status;
+
+ timer_TermService();
+ signal(SIGPIPE, SIG_DFL);
+ signal(SIGALRM, SIG_DFL);
+ signal(SIGHUP, SIG_DFL);
+ signal(SIGTERM, SIG_DFL);
+ signal(SIGINT, SIG_DFL);
+ signal(SIGQUIT, SIG_DFL);
+ for (fd = getdtablesize(); fd >= 0; fd--)
+ close(fd);
+ /*
+ * Reap the intermediate process. As we're not exiting but the
+ * intermediate is, we don't want it to become defunct.
+ */
+ waitpid(pid, &status, 0);
+ /* Tweak our process arguments.... */
+ SetTitle("session owner");
+#ifndef NOSUID
+ setuid(ID0realuid());
+#endif
+ /*
+ * Hang around for a HUP. This should happen as soon as the
+ * ppp that we passed our ctty descriptor to closes it.
+ * NOTE: If this process dies, the passed descriptor becomes
+ * invalid and will give a select() error by setting one
+ * of the error fds, aborting the other ppp. We don't
+ * want that to happen !
+ */
+ pause();
+ }
+ _exit(0);
+ break;
+ }
+}
+
+int
+bundle_HighestState(struct bundle *bundle)
+{
+ struct datalink *dl;
+ int result = DATALINK_CLOSED;
+
+ for (dl = bundle->links; dl; dl = dl->next)
+ if (result < dl->state)
+ result = dl->state;
+
+ return result;
+}
+
+int
+bundle_Exception(struct bundle *bundle, int fd)
+{
+ struct datalink *dl;
+
+ for (dl = bundle->links; dl; dl = dl->next)
+ if (dl->physical->fd == fd) {
+ datalink_Down(dl, CLOSE_NORMAL);
+ return 1;
+ }
+
+ return 0;
+}
+
+void
+bundle_AdjustFilters(struct bundle *bundle, struct ncpaddr *local,
+ struct ncpaddr *remote)
+{
+ filter_AdjustAddr(&bundle->filter.in, local, remote, NULL);
+ filter_AdjustAddr(&bundle->filter.out, local, remote, NULL);
+ filter_AdjustAddr(&bundle->filter.dial, local, remote, NULL);
+ filter_AdjustAddr(&bundle->filter.alive, local, remote, NULL);
+}
+
+void
+bundle_AdjustDNS(struct bundle *bundle)
+{
+ struct in_addr *dns = bundle->ncp.ipcp.ns.dns;
+
+ filter_AdjustAddr(&bundle->filter.in, NULL, NULL, dns);
+ filter_AdjustAddr(&bundle->filter.out, NULL, NULL, dns);
+ filter_AdjustAddr(&bundle->filter.dial, NULL, NULL, dns);
+ filter_AdjustAddr(&bundle->filter.alive, NULL, NULL, dns);
+}
+
+void
+bundle_CalculateBandwidth(struct bundle *bundle)
+{
+ struct datalink *dl;
+ int sp, overhead, maxoverhead;
+
+ bundle->bandwidth = 0;
+ bundle->iface->mtu = 0;
+ maxoverhead = 0;
+
+ for (dl = bundle->links; dl; dl = dl->next) {
+ overhead = ccp_MTUOverhead(&dl->physical->link.ccp);
+ if (maxoverhead < overhead)
+ maxoverhead = overhead;
+ if (dl->state == DATALINK_OPEN) {
+ if ((sp = dl->mp.bandwidth) == 0 &&
+ (sp = physical_GetSpeed(dl->physical)) == 0)
+ log_Printf(LogDEBUG, "%s: %s: Cannot determine bandwidth\n",
+ dl->name, dl->physical->name.full);
+ else
+ bundle->bandwidth += sp;
+ if (!bundle->ncp.mp.active) {
+ bundle->iface->mtu = dl->physical->link.lcp.his_mru;
+ break;
+ }
+ }
+ }
+
+ if (bundle->bandwidth == 0)
+ bundle->bandwidth = 115200; /* Shrug */
+
+ if (bundle->ncp.mp.active) {
+ bundle->iface->mtu = bundle->ncp.mp.peer_mrru;
+ overhead = ccp_MTUOverhead(&bundle->ncp.mp.link.ccp);
+ if (maxoverhead < overhead)
+ maxoverhead = overhead;
+ } else if (!bundle->iface->mtu)
+ bundle->iface->mtu = DEF_MRU;
+
+#ifndef NORADIUS
+ if (bundle->radius.valid && bundle->radius.mtu &&
+ bundle->radius.mtu < bundle->iface->mtu) {
+ log_Printf(LogLCP, "Reducing MTU to radius value %lu\n",
+ bundle->radius.mtu);
+ bundle->iface->mtu = bundle->radius.mtu;
+ }
+#endif
+
+ if (maxoverhead) {
+ log_Printf(LogLCP, "Reducing MTU from %d to %d (CCP requirement)\n",
+ bundle->iface->mtu, bundle->iface->mtu - maxoverhead);
+ bundle->iface->mtu -= maxoverhead;
+ }
+
+ tun_configure(bundle);
+
+ route_UpdateMTU(bundle);
+}
+
+void
+bundle_AutoAdjust(struct bundle *bundle, int percent, int what)
+{
+ struct datalink *dl, *choice, *otherlinkup;
+
+ choice = otherlinkup = NULL;
+ for (dl = bundle->links; dl; dl = dl->next)
+ if (dl->physical->type == PHYS_AUTO) {
+ if (dl->state == DATALINK_OPEN) {
+ if (what == AUTO_DOWN) {
+ if (choice)
+ otherlinkup = choice;
+ choice = dl;
+ }
+ } else if (dl->state == DATALINK_CLOSED) {
+ if (what == AUTO_UP) {
+ choice = dl;
+ break;
+ }
+ } else {
+ /* An auto link in an intermediate state - forget it for the moment */
+ choice = NULL;
+ break;
+ }
+ } else if (dl->state == DATALINK_OPEN && what == AUTO_DOWN)
+ otherlinkup = dl;
+
+ if (choice) {
+ if (what == AUTO_UP) {
+ log_Printf(LogPHASE, "%d%% saturation -> Opening link ``%s''\n",
+ percent, choice->name);
+ datalink_Up(choice, 1, 1);
+ mp_CheckAutoloadTimer(&bundle->ncp.mp);
+ } else if (otherlinkup) { /* Only bring the second-last link down */
+ log_Printf(LogPHASE, "%d%% saturation -> Closing link ``%s''\n",
+ percent, choice->name);
+ datalink_Close(choice, CLOSE_STAYDOWN);
+ mp_CheckAutoloadTimer(&bundle->ncp.mp);
+ }
+ }
+}
+
+int
+bundle_WantAutoloadTimer(struct bundle *bundle)
+{
+ struct datalink *dl;
+ int autolink, opened;
+
+ if (bundle->phase == PHASE_NETWORK) {
+ for (autolink = opened = 0, dl = bundle->links; dl; dl = dl->next)
+ if (dl->physical->type == PHYS_AUTO) {
+ if (++autolink == 2 || (autolink == 1 && opened))
+ /* Two auto links or one auto and one open in NETWORK phase */
+ return 1;
+ } else if (dl->state == DATALINK_OPEN) {
+ opened++;
+ if (autolink)
+ /* One auto and one open link in NETWORK phase */
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+void
+bundle_ChangedPID(struct bundle *bundle)
+{
+#ifdef TUNSIFPID
+ ioctl(bundle->dev.fd, TUNSIFPID, 0);
+#endif
+}
+
+int
+bundle_Uptime(struct bundle *bundle)
+{
+ if (bundle->upat)
+ return time(NULL) - bundle->upat;
+
+ return 0;
+}
diff --git a/usr.sbin/ppp/bundle.h b/usr.sbin/ppp/bundle.h
new file mode 100644
index 0000000..b2cb6f0
--- /dev/null
+++ b/usr.sbin/ppp/bundle.h
@@ -0,0 +1,207 @@
+/*-
+ * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define PHASE_DEAD 0 /* Link is dead */
+#define PHASE_ESTABLISH 1 /* Establishing link */
+#define PHASE_AUTHENTICATE 2 /* Being authenticated */
+#define PHASE_NETWORK 3 /* We're alive ! */
+#define PHASE_TERMINATE 4 /* Terminating link */
+
+/* cfg.opt bit settings */
+#define OPT_FILTERDECAP 0x0001
+#define OPT_IDCHECK 0x0002
+#define OPT_IFACEALIAS 0x0004
+#ifndef NOINET6
+#define OPT_IPCP 0x0008
+#define OPT_IPV6CP 0x0010
+#endif
+#define OPT_KEEPSESSION 0x0020
+#define OPT_LOOPBACK 0x0040
+#define OPT_PASSWDAUTH 0x0080
+#define OPT_PROXY 0x0100
+#define OPT_PROXYALL 0x0200
+#define OPT_SROUTES 0x0400
+#define OPT_TCPMSSFIXUP 0x0800
+#define OPT_THROUGHPUT 0x1000
+#define OPT_UTMP 0x2000
+
+#define MAX_ENDDISC_CLASS 5
+
+#define Enabled(b, o) ((b)->cfg.opt & (o))
+
+/* AutoAdjust() values */
+#define AUTO_UP 1
+#define AUTO_DOWN 2
+
+struct sockaddr_un;
+struct datalink;
+struct physical;
+struct link;
+struct server;
+struct prompt;
+struct iface;
+
+struct bundle {
+ struct fdescriptor desc; /* really all our datalinks */
+ int unit; /* The device/interface unit number */
+
+ struct {
+ char Name[20]; /* The /dev/XXXX name */
+ int fd; /* The /dev/XXXX descriptor */
+ unsigned header : 1; /* Family header sent & received ? */
+ } dev;
+
+ u_long bandwidth; /* struct tuninfo speed */
+ struct iface *iface; /* Interface information */
+
+ int routing_seq; /* The current routing sequence number */
+ u_int phase; /* Curent phase */
+
+ struct {
+ int all; /* Union of all physical::type's */
+ int open; /* Union of all open physical::type's */
+ } phys_type;
+
+ unsigned CleaningUp : 1; /* Going to exit.... */
+ unsigned NatEnabled : 1; /* Are we using libalias ? */
+
+ struct fsm_parent fsm; /* Our callback functions */
+ struct datalink *links; /* Our data links */
+
+ time_t upat; /* When the link came up */
+
+ struct {
+ struct {
+ int timeout; /* NCP Idle timeout value */
+ int min_timeout; /* Don't idle out before this */
+ } idle;
+ struct {
+ char name[AUTHLEN]; /* PAP/CHAP system name */
+ char key[AUTHLEN]; /* PAP/CHAP key */
+ } auth;
+ unsigned opt; /* Uses OPT_ bits from above */
+ char label[50]; /* last thing `load'ed */
+ u_short ifqueue; /* Interface queue size */
+
+ struct {
+ int timeout; /* How long to leave the output queue choked */
+ } choked;
+ } cfg;
+
+ struct ncp ncp;
+
+ struct {
+ struct filter in; /* incoming packet filter */
+ struct filter out; /* outgoing packet filter */
+ struct filter dial; /* dial-out packet filter */
+ struct filter alive; /* keep-alive packet filter */
+ } filter;
+
+ struct {
+ struct pppTimer timer; /* timeout after cfg.idle_timeout */
+ time_t done;
+ } idle;
+
+#ifndef NORADIUS
+ struct {
+ struct pppTimer timer;
+ time_t done;
+ } session;
+#endif
+
+ struct {
+ int fd; /* write status here */
+ } notify;
+
+ struct {
+ struct pppTimer timer; /* choked output queue timer */
+ } choked;
+
+#ifndef NORADIUS
+ struct radius radius; /* Info retrieved from radius server */
+ struct radacct radacct;
+#endif
+};
+
+#define descriptor2bundle(d) \
+ ((d)->type == BUNDLE_DESCRIPTOR ? (struct bundle *)(d) : NULL)
+
+extern struct bundle *bundle_Create(const char *, int, int);
+extern void bundle_Destroy(struct bundle *);
+extern const char *bundle_PhaseName(struct bundle *);
+#define bundle_Phase(b) ((b)->phase)
+extern void bundle_NewPhase(struct bundle *, u_int);
+extern void bundle_LinksRemoved(struct bundle *);
+extern void bundle_Close(struct bundle *, const char *, int);
+extern void bundle_Down(struct bundle *, int);
+extern void bundle_Open(struct bundle *, const char *, int, int);
+extern void bundle_LinkClosed(struct bundle *, struct datalink *);
+
+extern int bundle_ShowLinks(struct cmdargs const *);
+extern int bundle_ShowStatus(struct cmdargs const *);
+extern void bundle_StartIdleTimer(struct bundle *, unsigned secs);
+extern void bundle_SetIdleTimer(struct bundle *, int, int);
+extern void bundle_StopIdleTimer(struct bundle *);
+extern int bundle_IsDead(struct bundle *);
+extern struct datalink *bundle2datalink(struct bundle *, const char *);
+
+#ifndef NORADIUS
+extern void bundle_StartSessionTimer(struct bundle *, unsigned secs);
+extern void bundle_StopSessionTimer(struct bundle *);
+#endif
+
+extern void bundle_RegisterDescriptor(struct bundle *, struct fdescriptor *);
+extern void bundle_UnRegisterDescriptor(struct bundle *, struct fdescriptor *);
+
+extern void bundle_SetTtyCommandMode(struct bundle *, struct datalink *);
+
+extern int bundle_DatalinkClone(struct bundle *, struct datalink *,
+ const char *);
+extern void bundle_DatalinkRemove(struct bundle *, struct datalink *);
+extern void bundle_CleanDatalinks(struct bundle *);
+extern void bundle_SetLabel(struct bundle *, const char *);
+extern const char *bundle_GetLabel(struct bundle *);
+extern void bundle_SendDatalink(struct datalink *, int, struct sockaddr_un *);
+extern int bundle_LinkSize(void);
+extern void bundle_ReceiveDatalink(struct bundle *, int);
+extern int bundle_SetMode(struct bundle *, struct datalink *, int);
+extern int bundle_RenameDatalink(struct bundle *, struct datalink *,
+ const char *);
+extern void bundle_setsid(struct bundle *, int);
+extern void bundle_LockTun(struct bundle *);
+extern int bundle_HighestState(struct bundle *);
+extern int bundle_Exception(struct bundle *, int);
+extern void bundle_AdjustFilters(struct bundle *, struct ncpaddr *,
+ struct ncpaddr *);
+extern void bundle_AdjustDNS(struct bundle *);
+extern void bundle_CalculateBandwidth(struct bundle *);
+extern void bundle_AutoAdjust(struct bundle *, int, int);
+extern int bundle_WantAutoloadTimer(struct bundle *);
+extern void bundle_ChangedPID(struct bundle *);
+extern void bundle_Notify(struct bundle *, char);
+extern int bundle_Uptime(struct bundle *);
diff --git a/usr.sbin/ppp/cbcp.c b/usr.sbin/ppp/cbcp.c
new file mode 100644
index 0000000..674ea70
--- /dev/null
+++ b/usr.sbin/ppp/cbcp.c
@@ -0,0 +1,759 @@
+/*-
+ * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+
+#ifdef __FreeBSD__
+#include <netinet/in.h>
+#endif
+#include <sys/un.h>
+
+#include <string.h>
+#include <termios.h>
+
+#include "layer.h"
+#include "defs.h"
+#include "log.h"
+#include "timer.h"
+#include "descriptor.h"
+#include "lqr.h"
+#include "mbuf.h"
+#include "fsm.h"
+#include "throughput.h"
+#include "hdlc.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "async.h"
+#include "physical.h"
+#include "proto.h"
+#include "cbcp.h"
+#include "mp.h"
+#include "chat.h"
+#include "auth.h"
+#include "chap.h"
+#include "datalink.h"
+
+void
+cbcp_Init(struct cbcp *cbcp, struct physical *p)
+{
+ cbcp->required = 0;
+ cbcp->fsm.state = CBCP_CLOSED;
+ cbcp->fsm.id = 0;
+ cbcp->fsm.delay = 0;
+ *cbcp->fsm.phone = '\0';
+ memset(&cbcp->fsm.timer, '\0', sizeof cbcp->fsm.timer);
+ cbcp->p = p;
+}
+
+static void cbcp_SendReq(struct cbcp *);
+static void cbcp_SendResponse(struct cbcp *);
+static void cbcp_SendAck(struct cbcp *);
+
+static void
+cbcp_Timeout(void *v)
+{
+ struct cbcp *cbcp = (struct cbcp *)v;
+
+ timer_Stop(&cbcp->fsm.timer);
+ if (cbcp->fsm.restart) {
+ switch (cbcp->fsm.state) {
+ case CBCP_CLOSED:
+ case CBCP_STOPPED:
+ log_Printf(LogCBCP, "%s: Urk - unexpected CBCP timeout !\n",
+ cbcp->p->dl->name);
+ break;
+
+ case CBCP_REQSENT:
+ cbcp_SendReq(cbcp);
+ break;
+ case CBCP_RESPSENT:
+ cbcp_SendResponse(cbcp);
+ break;
+ case CBCP_ACKSENT:
+ cbcp_SendAck(cbcp);
+ break;
+ }
+ } else {
+ const char *missed;
+
+ switch (cbcp->fsm.state) {
+ case CBCP_STOPPED:
+ missed = "REQ";
+ break;
+ case CBCP_REQSENT:
+ missed = "RESPONSE";
+ break;
+ case CBCP_RESPSENT:
+ missed = "ACK";
+ break;
+ case CBCP_ACKSENT:
+ missed = "Terminate REQ";
+ break;
+ default:
+ log_Printf(LogCBCP, "%s: Urk - unexpected CBCP timeout !\n",
+ cbcp->p->dl->name);
+ missed = NULL;
+ break;
+ }
+ if (missed)
+ log_Printf(LogCBCP, "%s: Timeout waiting for peer %s\n",
+ cbcp->p->dl->name, missed);
+ datalink_CBCPFailed(cbcp->p->dl);
+ }
+}
+
+static void
+cbcp_StartTimer(struct cbcp *cbcp, int timeout)
+{
+ timer_Stop(&cbcp->fsm.timer);
+ cbcp->fsm.timer.func = cbcp_Timeout;
+ cbcp->fsm.timer.name = "cbcp";
+ cbcp->fsm.timer.load = timeout * SECTICKS;
+ cbcp->fsm.timer.arg = cbcp;
+ timer_Start(&cbcp->fsm.timer);
+}
+
+#define CBCP_CLOSED (0) /* Not in use */
+#define CBCP_STOPPED (1) /* Waiting for a REQ */
+#define CBCP_REQSENT (2) /* Waiting for a RESP */
+#define CBCP_RESPSENT (3) /* Waiting for an ACK */
+#define CBCP_ACKSENT (4) /* Waiting for an LCP Term REQ */
+
+static const char * const cbcpname[] = {
+ "closed", "stopped", "req-sent", "resp-sent", "ack-sent"
+};
+
+static const char *
+cbcpstate(int s)
+{
+ if (s < sizeof cbcpname / sizeof cbcpname[0])
+ return cbcpname[s];
+ return HexStr(s, NULL, 0);
+}
+
+static void
+cbcp_NewPhase(struct cbcp *cbcp, int new)
+{
+ if (cbcp->fsm.state != new) {
+ log_Printf(LogCBCP, "%s: State change %s --> %s\n", cbcp->p->dl->name,
+ cbcpstate(cbcp->fsm.state), cbcpstate(new));
+ cbcp->fsm.state = new;
+ }
+}
+
+struct cbcp_header {
+ u_char code;
+ u_char id;
+ u_int16_t length; /* Network byte order */
+};
+
+
+/* cbcp_header::code values */
+#define CBCP_REQ (1)
+#define CBCP_RESPONSE (2)
+#define CBCP_ACK (3)
+
+struct cbcp_data {
+ u_char type;
+ u_char length;
+ u_char delay;
+ char addr_start[253]; /* max cbcp_data length 255 + 1 for NULL */
+};
+
+/* cbcp_data::type values */
+#define CBCP_NONUM (1)
+#define CBCP_CLIENTNUM (2)
+#define CBCP_SERVERNUM (3)
+#define CBCP_LISTNUM (4)
+
+static void
+cbcp_Output(struct cbcp *cbcp, u_char code, struct cbcp_data *data)
+{
+ struct cbcp_header *head;
+ struct mbuf *bp;
+
+ bp = m_get(sizeof *head + data->length, MB_CBCPOUT);
+ head = (struct cbcp_header *)MBUF_CTOP(bp);
+ head->code = code;
+ head->id = cbcp->fsm.id;
+ head->length = htons(sizeof *head + data->length);
+ memcpy(MBUF_CTOP(bp) + sizeof *head, data, data->length);
+ log_DumpBp(LogDEBUG, "cbcp_Output", bp);
+ link_PushPacket(&cbcp->p->link, bp, cbcp->p->dl->bundle,
+ LINK_QUEUES(&cbcp->p->link) - 1, PROTO_CBCP);
+}
+
+static const char *
+cbcp_data_Type(int type)
+{
+ static const char * const types[] = {
+ "No callback", "User-spec", "Server-spec", "list"
+ };
+
+ if (type < 1 || type > sizeof types / sizeof types[0])
+ return HexStr(type, NULL, 0);
+ return types[type-1];
+}
+
+struct cbcp_addr {
+ u_char type;
+ char addr[1]; /* Really ASCIIZ */
+};
+
+/* cbcp_data::type values */
+#define CBCP_ADDR_PSTN (1)
+
+static void
+cbcp_data_Show(struct cbcp_data *data)
+{
+ struct cbcp_addr *addr;
+ char *end;
+
+ addr = (struct cbcp_addr *)data->addr_start;
+ end = (char *)data + data->length;
+ *end = '\0';
+
+ log_Printf(LogCBCP, " TYPE %s\n", cbcp_data_Type(data->type));
+ if ((char *)&data->delay < end) {
+ log_Printf(LogCBCP, " DELAY %d\n", data->delay);
+ while (addr->addr < end) {
+ if (addr->type == CBCP_ADDR_PSTN)
+ log_Printf(LogCBCP, " ADDR %s\n", addr->addr);
+ else
+ log_Printf(LogCBCP, " ADDR type %d ??\n", (int)addr->type);
+ addr = (struct cbcp_addr *)(addr->addr + strlen(addr->addr) + 1);
+ }
+ }
+}
+
+static void
+cbcp_SendReq(struct cbcp *cbcp)
+{
+ struct cbcp_data data;
+ struct cbcp_addr *addr;
+ char list[sizeof cbcp->fsm.phone], *next;
+ int len, max;
+
+ /* Only callees send REQs */
+
+ log_Printf(LogCBCP, "%s: SendReq(%d) state = %s\n", cbcp->p->dl->name,
+ cbcp->fsm.id, cbcpstate(cbcp->fsm.state));
+ data.type = cbcp->fsm.type;
+ data.delay = 0;
+ strncpy(list, cbcp->fsm.phone, sizeof list - 1);
+ list[sizeof list - 1] = '\0';
+
+ switch (data.type) {
+ case CBCP_CLIENTNUM:
+ addr = (struct cbcp_addr *)data.addr_start;
+ addr->type = CBCP_ADDR_PSTN;
+ *addr->addr = '\0';
+ data.length = addr->addr - (char *)&data;
+ break;
+
+ case CBCP_LISTNUM:
+ addr = (struct cbcp_addr *)data.addr_start;
+ for (next = strtok(list, ","); next; next = strtok(NULL, ",")) {
+ len = strlen(next);
+ max = data.addr_start + sizeof data.addr_start - addr->addr - 1;
+ if (len <= max) {
+ addr->type = CBCP_ADDR_PSTN;
+ strcpy(addr->addr, next);
+ addr = (struct cbcp_addr *)((char *)addr + len + 2);
+ } else
+ log_Printf(LogWARN, "CBCP ADDR \"%s\" skipped - packet too large\n",
+ next);
+ }
+ data.length = (char *)addr - (char *)&data;
+ break;
+
+ case CBCP_SERVERNUM:
+ data.length = data.addr_start - (char *)&data;
+ break;
+
+ default:
+ data.length = (char *)&data.delay - (char *)&data;
+ break;
+ }
+
+ cbcp_data_Show(&data);
+ cbcp_Output(cbcp, CBCP_REQ, &data);
+ cbcp->fsm.restart--;
+ cbcp_StartTimer(cbcp, cbcp->fsm.delay);
+ cbcp_NewPhase(cbcp, CBCP_REQSENT); /* Wait for a RESPONSE */
+}
+
+void
+cbcp_Up(struct cbcp *cbcp)
+{
+ struct lcp *lcp = &cbcp->p->link.lcp;
+
+ cbcp->fsm.delay = cbcp->p->dl->cfg.cbcp.delay;
+ if (*cbcp->p->dl->peer.authname == '\0' ||
+ !auth_SetPhoneList(cbcp->p->dl->peer.authname, cbcp->fsm.phone,
+ sizeof cbcp->fsm.phone)) {
+ strncpy(cbcp->fsm.phone, cbcp->p->dl->cfg.cbcp.phone,
+ sizeof cbcp->fsm.phone - 1);
+ cbcp->fsm.phone[sizeof cbcp->fsm.phone - 1] = '\0';
+ }
+
+ if (lcp->want_callback.opmask) {
+ if (*cbcp->fsm.phone == '\0')
+ cbcp->fsm.type = CBCP_NONUM;
+ else if (!strcmp(cbcp->fsm.phone, "*")) {
+ cbcp->fsm.type = CBCP_SERVERNUM;
+ *cbcp->fsm.phone = '\0';
+ } else
+ cbcp->fsm.type = CBCP_CLIENTNUM;
+ cbcp_NewPhase(cbcp, CBCP_STOPPED); /* Wait for a REQ */
+ cbcp_StartTimer(cbcp, cbcp->fsm.delay * DEF_FSMTRIES);
+ } else {
+ if (*cbcp->fsm.phone == '\0')
+ cbcp->fsm.type = CBCP_NONUM;
+ else if (!strcmp(cbcp->fsm.phone, "*")) {
+ cbcp->fsm.type = CBCP_CLIENTNUM;
+ *cbcp->fsm.phone = '\0';
+ } else if (strchr(cbcp->fsm.phone, ','))
+ cbcp->fsm.type = CBCP_LISTNUM;
+ else
+ cbcp->fsm.type = CBCP_SERVERNUM;
+ cbcp->fsm.restart = DEF_FSMTRIES;
+ cbcp_SendReq(cbcp);
+ }
+}
+
+static int
+cbcp_AdjustResponse(struct cbcp *cbcp, struct cbcp_data *data)
+{
+ /*
+ * We've received a REQ (data). Adjust our reponse (cbcp->fsm.*)
+ * so that we (hopefully) agree with the peer
+ */
+ struct cbcp_addr *addr;
+
+ switch (data->type) {
+ case CBCP_NONUM:
+ if (cbcp->p->dl->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_NONE))
+ /*
+ * if ``none'' is a configured callback possibility
+ * (ie, ``set callback cbcp none''), go along with the callees
+ * request
+ */
+ cbcp->fsm.type = CBCP_NONUM;
+
+ /*
+ * Otherwise, we send our desired response anyway. This seems to be
+ * what Win95 does - although I can't find this behaviour documented
+ * in the CBCP spec....
+ */
+
+ return 1;
+
+ case CBCP_CLIENTNUM:
+ if (cbcp->fsm.type == CBCP_CLIENTNUM) {
+ char *ptr;
+
+ if (data->length > data->addr_start - (char *)data) {
+ /*
+ * The peer has given us an address type spec - make sure we
+ * understand !
+ */
+ addr = (struct cbcp_addr *)data->addr_start;
+ if (addr->type != CBCP_ADDR_PSTN) {
+ log_Printf(LogPHASE, "CBCP: Unrecognised address type %d !\n",
+ (int)addr->type);
+ return 0;
+ }
+ }
+ /* we accept the REQ even if the peer didn't specify an addr->type */
+ ptr = strchr(cbcp->fsm.phone, ',');
+ if (ptr)
+ *ptr = '\0'; /* Just use the first number in our list */
+ return 1;
+ }
+ log_Printf(LogPHASE, "CBCP: no number to pass to the peer !\n");
+ return 0;
+
+ case CBCP_SERVERNUM:
+ if (cbcp->fsm.type == CBCP_SERVERNUM) {
+ *cbcp->fsm.phone = '\0';
+ return 1;
+ }
+ if (data->length > data->addr_start - (char *)data) {
+ /*
+ * This violates the spec, but if the peer has told us the
+ * number it wants to call back, take advantage of this fact
+ * and allow things to proceed if we've specified the same
+ * number
+ */
+ addr = (struct cbcp_addr *)data->addr_start;
+ if (addr->type != CBCP_ADDR_PSTN) {
+ log_Printf(LogPHASE, "CBCP: Unrecognised address type %d !\n",
+ (int)addr->type);
+ return 0;
+ } else if (cbcp->fsm.type == CBCP_CLIENTNUM) {
+ /*
+ * If the peer's insisting on deciding the number, make sure
+ * it's one of the ones in our list. If it is, let the peer
+ * think it's in control :-)
+ */
+ char list[sizeof cbcp->fsm.phone], *next;
+
+ strncpy(list, cbcp->fsm.phone, sizeof list - 1);
+ list[sizeof list - 1] = '\0';
+ for (next = strtok(list, ","); next; next = strtok(NULL, ","))
+ if (!strcmp(next, addr->addr)) {
+ cbcp->fsm.type = CBCP_SERVERNUM;
+ strcpy(cbcp->fsm.phone, next);
+ return 1;
+ }
+ }
+ }
+ log_Printf(LogPHASE, "CBCP: Peer won't allow local decision !\n");
+ return 0;
+
+ case CBCP_LISTNUM:
+ if (cbcp->fsm.type == CBCP_CLIENTNUM || cbcp->fsm.type == CBCP_LISTNUM) {
+ /*
+ * Search through ``data''s addresses and see if cbcp->fsm.phone
+ * contains any of them
+ */
+ char list[sizeof cbcp->fsm.phone], *next, *end;
+
+ addr = (struct cbcp_addr *)data->addr_start;
+ end = (char *)data + data->length;
+
+ while (addr->addr < end) {
+ if (addr->type == CBCP_ADDR_PSTN) {
+ strncpy(list, cbcp->fsm.phone, sizeof list - 1);
+ list[sizeof list - 1] = '\0';
+ for (next = strtok(list, ","); next; next = strtok(NULL, ","))
+ if (!strcmp(next, addr->addr)) {
+ cbcp->fsm.type = CBCP_LISTNUM;
+ strcpy(cbcp->fsm.phone, next);
+ return 1;
+ }
+ } else
+ log_Printf(LogCBCP, "Warning: Unrecognised address type %d !\n",
+ (int)addr->type);
+ addr = (struct cbcp_addr *)(addr->addr + strlen(addr->addr) + 1);
+ }
+ }
+ log_Printf(LogPHASE, "CBCP: no good number to pass to the peer !\n");
+ return 0;
+ }
+
+ log_Printf(LogCBCP, "Unrecognised REQ type %d !\n", (int)data->type);
+ return 0;
+}
+
+static void
+cbcp_SendResponse(struct cbcp *cbcp)
+{
+ struct cbcp_data data;
+ struct cbcp_addr *addr;
+
+ /* Only callers send RESPONSEs */
+
+ log_Printf(LogCBCP, "%s: SendResponse(%d) state = %s\n", cbcp->p->dl->name,
+ cbcp->fsm.id, cbcpstate(cbcp->fsm.state));
+
+ data.type = cbcp->fsm.type;
+ data.delay = cbcp->fsm.delay;
+ addr = (struct cbcp_addr *)data.addr_start;
+ if (data.type == CBCP_NONUM)
+ data.length = (char *)&data.delay - (char *)&data;
+ else if (*cbcp->fsm.phone) {
+ addr->type = CBCP_ADDR_PSTN;
+ strcpy(addr->addr, cbcp->fsm.phone);
+ data.length = (addr->addr + strlen(addr->addr) + 1) - (char *)&data;
+ } else
+ data.length = data.addr_start - (char *)&data;
+
+ cbcp_data_Show(&data);
+ cbcp_Output(cbcp, CBCP_RESPONSE, &data);
+ cbcp->fsm.restart--;
+ cbcp_StartTimer(cbcp, cbcp->fsm.delay);
+ cbcp_NewPhase(cbcp, CBCP_RESPSENT); /* Wait for an ACK */
+}
+
+/* What to do after checking an incoming response */
+#define CBCP_ACTION_DOWN (0)
+#define CBCP_ACTION_REQ (1)
+#define CBCP_ACTION_ACK (2)
+
+static int
+cbcp_CheckResponse(struct cbcp *cbcp, struct cbcp_data *data)
+{
+ /*
+ * We've received a RESPONSE (data). Check if it agrees with
+ * our REQ (cbcp->fsm)
+ */
+ struct cbcp_addr *addr;
+
+ addr = (struct cbcp_addr *)data->addr_start;
+
+ if (data->type == cbcp->fsm.type) {
+ switch (cbcp->fsm.type) {
+ case CBCP_NONUM:
+ return CBCP_ACTION_ACK;
+
+ case CBCP_CLIENTNUM:
+ if ((char *)data + data->length <= addr->addr)
+ log_Printf(LogPHASE, "CBCP: peer didn't respond with a number !\n");
+ else if (addr->type != CBCP_ADDR_PSTN)
+ log_Printf(LogPHASE, "CBCP: Unrecognised address type %d !\n",
+ addr->type);
+ else {
+ strcpy(cbcp->fsm.phone, addr->addr);
+ cbcp->fsm.delay = data->delay;
+ return CBCP_ACTION_ACK;
+ }
+ return CBCP_ACTION_DOWN;
+
+ case CBCP_SERVERNUM:
+ cbcp->fsm.delay = data->delay;
+ return CBCP_ACTION_ACK;
+
+ case CBCP_LISTNUM:
+ if ((char *)data + data->length <= addr->addr)
+ log_Printf(LogPHASE, "CBCP: peer didn't respond with a number !\n");
+ else if (addr->type != CBCP_ADDR_PSTN)
+ log_Printf(LogPHASE, "CBCP: Unrecognised address type %d !\n",
+ addr->type);
+ else {
+ char list[sizeof cbcp->fsm.phone], *next;
+
+ strncpy(list, cbcp->fsm.phone, sizeof list - 1);
+ list[sizeof list - 1] = '\0';
+ for (next = strtok(list, ","); next; next = strtok(NULL, ","))
+ if (!strcmp(addr->addr, next)) {
+ strcpy(cbcp->fsm.phone, next);
+ cbcp->fsm.delay = data->delay;
+ return CBCP_ACTION_ACK;
+ }
+ log_Printf(LogPHASE, "CBCP: peer didn't respond with a "
+ "valid number !\n");
+ }
+ return CBCP_ACTION_DOWN;
+ }
+ log_Printf(LogPHASE, "Internal CBCP error - agreed on %d !\n",
+ (int)cbcp->fsm.type);
+ return CBCP_ACTION_DOWN;
+ } else if (data->type == CBCP_NONUM && cbcp->fsm.type == CBCP_CLIENTNUM) {
+ /*
+ * Client doesn't want CBCP after all....
+ * We only allow this when ``set cbcp *'' has been specified.
+ */
+ cbcp->fsm.type = CBCP_NONUM;
+ return CBCP_ACTION_ACK;
+ }
+ log_Printf(LogCBCP, "Invalid peer RESPONSE\n");
+ return CBCP_ACTION_REQ;
+}
+
+static void
+cbcp_SendAck(struct cbcp *cbcp)
+{
+ struct cbcp_data data;
+ struct cbcp_addr *addr;
+
+ /* Only callees send ACKs */
+
+ log_Printf(LogCBCP, "%s: SendAck(%d) state = %s\n", cbcp->p->dl->name,
+ cbcp->fsm.id, cbcpstate(cbcp->fsm.state));
+
+ data.type = cbcp->fsm.type;
+ switch (data.type) {
+ case CBCP_NONUM:
+ data.length = (char *)&data.delay - (char *)&data;
+ break;
+ case CBCP_CLIENTNUM:
+ addr = (struct cbcp_addr *)data.addr_start;
+ addr->type = CBCP_ADDR_PSTN;
+ strcpy(addr->addr, cbcp->fsm.phone);
+ data.delay = cbcp->fsm.delay;
+ data.length = addr->addr + strlen(addr->addr) + 1 - (char *)&data;
+ break;
+ default:
+ data.delay = cbcp->fsm.delay;
+ data.length = data.addr_start - (char *)&data;
+ break;
+ }
+
+ cbcp_data_Show(&data);
+ cbcp_Output(cbcp, CBCP_ACK, &data);
+ cbcp->fsm.restart--;
+ cbcp_StartTimer(cbcp, cbcp->fsm.delay);
+ cbcp_NewPhase(cbcp, CBCP_ACKSENT); /* Wait for an ACK */
+}
+
+extern struct mbuf *
+cbcp_Input(struct bundle *bundle, struct link *l, struct mbuf *bp)
+{
+ struct physical *p = link2physical(l);
+ struct cbcp_header *head;
+ struct cbcp_data *data;
+ struct cbcp *cbcp = &p->dl->cbcp;
+ int len;
+
+ if (p == NULL) {
+ log_Printf(LogERROR, "cbcp_Input: Not a physical link - dropped\n");
+ m_freem(bp);
+ return NULL;
+ }
+
+ bp = m_pullup(bp);
+ len = m_length(bp);
+ if (len < sizeof(struct cbcp_header)) {
+ m_freem(bp);
+ return NULL;
+ }
+ head = (struct cbcp_header *)MBUF_CTOP(bp);
+ if (ntohs(head->length) != len) {
+ log_Printf(LogWARN, "Corrupt CBCP packet (code %d, length %d not %d)"
+ " - ignored\n", head->code, ntohs(head->length), len);
+ m_freem(bp);
+ return NULL;
+ }
+ m_settype(bp, MB_CBCPIN);
+
+ /* XXX check the id */
+
+ bp->m_offset += sizeof(struct cbcp_header);
+ bp->m_len -= sizeof(struct cbcp_header);
+ data = (struct cbcp_data *)MBUF_CTOP(bp);
+
+ switch (head->code) {
+ case CBCP_REQ:
+ log_Printf(LogCBCP, "%s: RecvReq(%d) state = %s\n",
+ p->dl->name, head->id, cbcpstate(cbcp->fsm.state));
+ cbcp_data_Show(data);
+ if (cbcp->fsm.state == CBCP_STOPPED || cbcp->fsm.state == CBCP_RESPSENT) {
+ timer_Stop(&cbcp->fsm.timer);
+ if (cbcp_AdjustResponse(cbcp, data)) {
+ cbcp->fsm.restart = DEF_FSMTRIES;
+ cbcp->fsm.id = head->id;
+ cbcp_SendResponse(cbcp);
+ } else
+ datalink_CBCPFailed(cbcp->p->dl);
+ } else
+ log_Printf(LogCBCP, "%s: unexpected REQ dropped\n", p->dl->name);
+ break;
+
+ case CBCP_RESPONSE:
+ log_Printf(LogCBCP, "%s: RecvResponse(%d) state = %s\n",
+ p->dl->name, head->id, cbcpstate(cbcp->fsm.state));
+ cbcp_data_Show(data);
+ if (cbcp->fsm.id != head->id) {
+ log_Printf(LogCBCP, "Warning: Expected id was %d, not %d\n",
+ cbcp->fsm.id, head->id);
+ cbcp->fsm.id = head->id;
+ }
+ if (cbcp->fsm.state == CBCP_REQSENT || cbcp->fsm.state == CBCP_ACKSENT) {
+ timer_Stop(&cbcp->fsm.timer);
+ switch (cbcp_CheckResponse(cbcp, data)) {
+ case CBCP_ACTION_REQ:
+ cbcp_SendReq(cbcp);
+ break;
+
+ case CBCP_ACTION_ACK:
+ cbcp->fsm.restart = DEF_FSMTRIES;
+ cbcp_SendAck(cbcp);
+ if (cbcp->fsm.type == CBCP_NONUM) {
+ /*
+ * Don't change state in case the peer doesn't get our ACK,
+ * just bring the layer up.
+ */
+ timer_Stop(&cbcp->fsm.timer);
+ datalink_NCPUp(cbcp->p->dl);
+ }
+ break;
+
+ default:
+ datalink_CBCPFailed(cbcp->p->dl);
+ break;
+ }
+ } else
+ log_Printf(LogCBCP, "%s: unexpected RESPONSE dropped\n", p->dl->name);
+ break;
+
+ case CBCP_ACK:
+ log_Printf(LogCBCP, "%s: RecvAck(%d) state = %s\n",
+ p->dl->name, head->id, cbcpstate(cbcp->fsm.state));
+ cbcp_data_Show(data);
+ if (cbcp->fsm.id != head->id) {
+ log_Printf(LogCBCP, "Warning: Expected id was %d, not %d\n",
+ cbcp->fsm.id, head->id);
+ cbcp->fsm.id = head->id;
+ }
+ if (cbcp->fsm.type == CBCP_NONUM) {
+ /*
+ * Don't change state in case the peer doesn't get our ACK,
+ * just bring the layer up.
+ */
+ timer_Stop(&cbcp->fsm.timer);
+ datalink_NCPUp(cbcp->p->dl);
+ } else if (cbcp->fsm.state == CBCP_RESPSENT) {
+ timer_Stop(&cbcp->fsm.timer);
+ datalink_CBCPComplete(cbcp->p->dl);
+ log_Printf(LogPHASE, "%s: CBCP: Peer will dial back\n", p->dl->name);
+ } else
+ log_Printf(LogCBCP, "%s: unexpected ACK dropped\n", p->dl->name);
+ break;
+
+ default:
+ log_Printf(LogWARN, "Unrecognised CBCP packet (code %d, length %d)\n",
+ head->code, len);
+ break;
+ }
+
+ m_freem(bp);
+ return NULL;
+}
+
+void
+cbcp_Down(struct cbcp *cbcp)
+{
+ timer_Stop(&cbcp->fsm.timer);
+ cbcp_NewPhase(cbcp, CBCP_CLOSED);
+ cbcp->required = 0;
+}
+
+void
+cbcp_ReceiveTerminateReq(struct physical *p)
+{
+ if (p->dl->cbcp.fsm.state == CBCP_ACKSENT) {
+ /* Don't change our state in case the peer doesn't get the ACK */
+ p->dl->cbcp.required = 1;
+ log_Printf(LogPHASE, "%s: CBCP: Will dial back on %s\n", p->dl->name,
+ p->dl->cbcp.fsm.phone);
+ } else
+ cbcp_NewPhase(&p->dl->cbcp, CBCP_CLOSED);
+}
diff --git a/usr.sbin/ppp/cbcp.h b/usr.sbin/ppp/cbcp.h
new file mode 100644
index 0000000..46bf274
--- /dev/null
+++ b/usr.sbin/ppp/cbcp.h
@@ -0,0 +1,65 @@
+/*-
+ * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct mbuf;
+struct physical;
+struct datalink;
+
+/* fsm states */
+#define CBCP_CLOSED (0) /* Not in use */
+#define CBCP_STOPPED (1) /* Waiting for a REQ */
+#define CBCP_REQSENT (2) /* Waiting for a RESP */
+#define CBCP_RESPSENT (3) /* Waiting for an ACK */
+#define CBCP_ACKSENT (4) /* Waiting for an LCP Term REQ */
+
+struct cbcpcfg {
+ u_char delay;
+ char phone[SCRIPT_LEN];
+ long fsmretry;
+};
+
+struct cbcp {
+ unsigned required : 1; /* Are we gonna call back ? */
+ struct physical *p; /* On this physical link */
+ struct {
+ u_char type; /* cbcp_data::type (none/me/him/list) */
+ u_char delay; /* How long to delay */
+ char phone[SCRIPT_LEN]; /* What to dial */
+
+ int state; /* Our FSM state */
+ u_char id; /* Our FSM ID */
+ u_char restart; /* FSM Send again ? */
+ struct pppTimer timer; /* Resend last option */
+ } fsm;
+};
+
+extern void cbcp_Init(struct cbcp *, struct physical *);
+extern void cbcp_Up(struct cbcp *);
+extern struct mbuf *cbcp_Input(struct bundle *, struct link *, struct mbuf *);
+extern void cbcp_Down(struct cbcp *);
+extern void cbcp_ReceiveTerminateReq(struct physical *);
diff --git a/usr.sbin/ppp/ccp.c b/usr.sbin/ppp/ccp.c
new file mode 100644
index 0000000..03c56b4
--- /dev/null
+++ b/usr.sbin/ppp/ccp.c
@@ -0,0 +1,818 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h> /* memcpy() on some archs */
+#include <termios.h>
+
+#include "layer.h"
+#include "defs.h"
+#include "command.h"
+#include "mbuf.h"
+#include "log.h"
+#include "timer.h"
+#include "fsm.h"
+#include "proto.h"
+#include "pred.h"
+#include "deflate.h"
+#include "throughput.h"
+#include "iplist.h"
+#include "slcompress.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "ncpaddr.h"
+#include "ipcp.h"
+#include "filter.h"
+#include "descriptor.h"
+#include "prompt.h"
+#include "link.h"
+#include "mp.h"
+#include "async.h"
+#include "physical.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#ifndef NODES
+#include "mppe.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+
+static void CcpSendConfigReq(struct fsm *);
+static void CcpSentTerminateReq(struct fsm *);
+static void CcpSendTerminateAck(struct fsm *, u_char);
+static void CcpDecodeConfig(struct fsm *, u_char *, u_char *, int,
+ struct fsm_decode *);
+static void CcpLayerStart(struct fsm *);
+static void CcpLayerFinish(struct fsm *);
+static int CcpLayerUp(struct fsm *);
+static void CcpLayerDown(struct fsm *);
+static void CcpInitRestartCounter(struct fsm *, int);
+static int CcpRecvResetReq(struct fsm *);
+static void CcpRecvResetAck(struct fsm *, u_char);
+
+static struct fsm_callbacks ccp_Callbacks = {
+ CcpLayerUp,
+ CcpLayerDown,
+ CcpLayerStart,
+ CcpLayerFinish,
+ CcpInitRestartCounter,
+ CcpSendConfigReq,
+ CcpSentTerminateReq,
+ CcpSendTerminateAck,
+ CcpDecodeConfig,
+ CcpRecvResetReq,
+ CcpRecvResetAck
+};
+
+static const char * const ccp_TimerNames[] =
+ {"CCP restart", "CCP openmode", "CCP stopped"};
+
+static const char *
+protoname(int proto)
+{
+ static char const * const cftypes[] = {
+ /* Check out the latest ``Compression Control Protocol'' rfc (1962) */
+ "OUI", /* 0: OUI */
+ "PRED1", /* 1: Predictor type 1 */
+ "PRED2", /* 2: Predictor type 2 */
+ "PUDDLE", /* 3: Puddle Jumber */
+ NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL,
+ "HWPPC", /* 16: Hewlett-Packard PPC */
+ "STAC", /* 17: Stac Electronics LZS (rfc1974) */
+ "MPPE", /* 18: Microsoft PPC (rfc2118) and */
+ /* Microsoft PPE (draft-ietf-pppext-mppe) */
+ "GAND", /* 19: Gandalf FZA (rfc1993) */
+ "V42BIS", /* 20: ARG->DATA.42bis compression */
+ "BSD", /* 21: BSD LZW Compress */
+ NULL,
+ "LZS-DCP", /* 23: LZS-DCP Compression Protocol (rfc1967) */
+ "MAGNALINK/DEFLATE",/* 24: Magnalink Variable Resource (rfc1975) */
+ /* 24: Deflate (according to pppd-2.3.*) */
+ "DCE", /* 25: Data Circuit-Terminating Equip (rfc1976) */
+ "DEFLATE", /* 26: Deflate (rfc1979) */
+ };
+
+ if (proto < 0 || proto > sizeof cftypes / sizeof *cftypes ||
+ cftypes[proto] == NULL) {
+ if (proto == -1)
+ return "none";
+ return HexStr(proto, NULL, 0);
+ }
+
+ return cftypes[proto];
+}
+
+/* We support these algorithms, and Req them in the given order */
+static const struct ccp_algorithm * const algorithm[] = {
+ &DeflateAlgorithm,
+ &Pred1Algorithm,
+ &PppdDeflateAlgorithm
+#ifndef NODES
+ , &MPPEAlgorithm
+#endif
+};
+
+#define NALGORITHMS (sizeof algorithm/sizeof algorithm[0])
+
+int
+ccp_ReportStatus(struct cmdargs const *arg)
+{
+ struct ccp_opt **o;
+ struct link *l;
+ struct ccp *ccp;
+ int f;
+
+ l = command_ChooseLink(arg);
+ ccp = &l->ccp;
+
+ prompt_Printf(arg->prompt, "%s: %s [%s]\n", l->name, ccp->fsm.name,
+ State2Nam(ccp->fsm.state));
+ if (ccp->fsm.state == ST_OPENED) {
+ prompt_Printf(arg->prompt, " My protocol = %s, His protocol = %s\n",
+ protoname(ccp->my_proto), protoname(ccp->his_proto));
+ prompt_Printf(arg->prompt, " Output: %ld --> %ld, Input: %ld --> %ld\n",
+ ccp->uncompout, ccp->compout,
+ ccp->compin, ccp->uncompin);
+ }
+
+ if (ccp->in.algorithm != -1)
+ prompt_Printf(arg->prompt, "\n Input Options: %s\n",
+ (*algorithm[ccp->in.algorithm]->Disp)(&ccp->in.opt));
+
+ if (ccp->out.algorithm != -1) {
+ o = &ccp->out.opt;
+ for (f = 0; f < ccp->out.algorithm; f++)
+ if (IsEnabled(ccp->cfg.neg[algorithm[f]->Neg]))
+ o = &(*o)->next;
+ prompt_Printf(arg->prompt, " Output Options: %s\n",
+ (*algorithm[ccp->out.algorithm]->Disp)(&(*o)->val));
+ }
+
+ prompt_Printf(arg->prompt, "\n Defaults: ");
+ prompt_Printf(arg->prompt, "FSM retry = %us, max %u Config"
+ " REQ%s, %u Term REQ%s\n", ccp->cfg.fsm.timeout,
+ ccp->cfg.fsm.maxreq, ccp->cfg.fsm.maxreq == 1 ? "" : "s",
+ ccp->cfg.fsm.maxtrm, ccp->cfg.fsm.maxtrm == 1 ? "" : "s");
+ prompt_Printf(arg->prompt, " deflate windows: ");
+ prompt_Printf(arg->prompt, "incoming = %d, ", ccp->cfg.deflate.in.winsize);
+ prompt_Printf(arg->prompt, "outgoing = %d\n", ccp->cfg.deflate.out.winsize);
+#ifndef NODES
+ prompt_Printf(arg->prompt, " MPPE: ");
+ if (ccp->cfg.mppe.keybits)
+ prompt_Printf(arg->prompt, "%d bits, ", ccp->cfg.mppe.keybits);
+ else
+ prompt_Printf(arg->prompt, "any bits, ");
+ switch (ccp->cfg.mppe.state) {
+ case MPPE_STATEFUL:
+ prompt_Printf(arg->prompt, "stateful");
+ break;
+ case MPPE_STATELESS:
+ prompt_Printf(arg->prompt, "stateless");
+ break;
+ case MPPE_ANYSTATE:
+ prompt_Printf(arg->prompt, "any state");
+ break;
+ }
+ prompt_Printf(arg->prompt, "%s\n",
+ ccp->cfg.mppe.required ? ", required" : "");
+#endif
+
+ prompt_Printf(arg->prompt, "\n DEFLATE: %s\n",
+ command_ShowNegval(ccp->cfg.neg[CCP_NEG_DEFLATE]));
+ prompt_Printf(arg->prompt, " PREDICTOR1: %s\n",
+ command_ShowNegval(ccp->cfg.neg[CCP_NEG_PRED1]));
+ prompt_Printf(arg->prompt, " DEFLATE24: %s\n",
+ command_ShowNegval(ccp->cfg.neg[CCP_NEG_DEFLATE24]));
+#ifndef NODES
+ prompt_Printf(arg->prompt, " MPPE: %s\n",
+ command_ShowNegval(ccp->cfg.neg[CCP_NEG_MPPE]));
+#endif
+ return 0;
+}
+
+void
+ccp_SetupCallbacks(struct ccp *ccp)
+{
+ ccp->fsm.fn = &ccp_Callbacks;
+ ccp->fsm.FsmTimer.name = ccp_TimerNames[0];
+ ccp->fsm.OpenTimer.name = ccp_TimerNames[1];
+ ccp->fsm.StoppedTimer.name = ccp_TimerNames[2];
+}
+
+void
+ccp_Init(struct ccp *ccp, struct bundle *bundle, struct link *l,
+ const struct fsm_parent *parent)
+{
+ /* Initialise ourselves */
+
+ fsm_Init(&ccp->fsm, "CCP", PROTO_CCP, 1, CCP_MAXCODE, LogCCP,
+ bundle, l, parent, &ccp_Callbacks, ccp_TimerNames);
+
+ ccp->cfg.deflate.in.winsize = 0;
+ ccp->cfg.deflate.out.winsize = 15;
+ ccp->cfg.fsm.timeout = DEF_FSMRETRY;
+ ccp->cfg.fsm.maxreq = DEF_FSMTRIES;
+ ccp->cfg.fsm.maxtrm = DEF_FSMTRIES;
+ ccp->cfg.neg[CCP_NEG_DEFLATE] = NEG_ENABLED|NEG_ACCEPTED;
+ ccp->cfg.neg[CCP_NEG_PRED1] = NEG_ENABLED|NEG_ACCEPTED;
+ ccp->cfg.neg[CCP_NEG_DEFLATE24] = 0;
+#ifndef NODES
+ ccp->cfg.mppe.keybits = 0;
+ ccp->cfg.mppe.state = MPPE_ANYSTATE;
+ ccp->cfg.mppe.required = 0;
+ ccp->cfg.neg[CCP_NEG_MPPE] = NEG_ENABLED|NEG_ACCEPTED;
+#endif
+
+ ccp_Setup(ccp);
+}
+
+void
+ccp_Setup(struct ccp *ccp)
+{
+ /* Set ourselves up for a startup */
+ ccp->fsm.open_mode = 0;
+ ccp->his_proto = ccp->my_proto = -1;
+ ccp->reset_sent = ccp->last_reset = -1;
+ ccp->in.algorithm = ccp->out.algorithm = -1;
+ ccp->in.state = ccp->out.state = NULL;
+ ccp->in.opt.hdr.id = -1;
+ ccp->out.opt = NULL;
+ ccp->his_reject = ccp->my_reject = 0;
+ ccp->uncompout = ccp->compout = 0;
+ ccp->uncompin = ccp->compin = 0;
+}
+
+/*
+ * Is ccp *REQUIRED* ?
+ * We ask each of the configured ccp protocols if they're required and
+ * return TRUE if they are.
+ *
+ * It's not possible for the peer to reject a required ccp protocol
+ * without our state machine bringing the supporting lcp layer down.
+ *
+ * If ccp is required but not open, the NCP layer should not push
+ * any data into the link.
+ */
+int
+ccp_Required(struct ccp *ccp)
+{
+ int f;
+
+ for (f = 0; f < NALGORITHMS; f++)
+ if (IsEnabled(ccp->cfg.neg[algorithm[f]->Neg]) &&
+ (*algorithm[f]->Required)(&ccp->fsm))
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Report whether it's possible to increase a packet's size after
+ * compression (and by how much).
+ */
+int
+ccp_MTUOverhead(struct ccp *ccp)
+{
+ if (ccp->fsm.state == ST_OPENED && ccp->out.algorithm >= 0)
+ return algorithm[ccp->out.algorithm]->o.MTUOverhead;
+
+ return 0;
+}
+
+static void
+CcpInitRestartCounter(struct fsm *fp, int what)
+{
+ /* Set fsm timer load */
+ struct ccp *ccp = fsm2ccp(fp);
+
+ fp->FsmTimer.load = ccp->cfg.fsm.timeout * SECTICKS;
+ switch (what) {
+ case FSM_REQ_TIMER:
+ fp->restart = ccp->cfg.fsm.maxreq;
+ break;
+ case FSM_TRM_TIMER:
+ fp->restart = ccp->cfg.fsm.maxtrm;
+ break;
+ default:
+ fp->restart = 1;
+ break;
+ }
+}
+
+static void
+CcpSendConfigReq(struct fsm *fp)
+{
+ /* Send config REQ please */
+ struct ccp *ccp = fsm2ccp(fp);
+ struct ccp_opt **o;
+ u_char *cp, buff[100];
+ int f, alloc;
+
+ cp = buff;
+ o = &ccp->out.opt;
+ alloc = ccp->his_reject == 0 && ccp->out.opt == NULL;
+ ccp->my_proto = -1;
+ ccp->out.algorithm = -1;
+ for (f = 0; f < NALGORITHMS; f++)
+ if (IsEnabled(ccp->cfg.neg[algorithm[f]->Neg]) &&
+ !REJECTED(ccp, algorithm[f]->id) &&
+ (*algorithm[f]->Usable)(fp)) {
+
+ if (!alloc)
+ for (o = &ccp->out.opt; *o != NULL; o = &(*o)->next)
+ if ((*o)->val.hdr.id == algorithm[f]->id && (*o)->algorithm == f)
+ break;
+
+ if (alloc || *o == NULL) {
+ *o = (struct ccp_opt *)malloc(sizeof(struct ccp_opt));
+ (*o)->val.hdr.id = algorithm[f]->id;
+ (*o)->val.hdr.len = 2;
+ (*o)->next = NULL;
+ (*o)->algorithm = f;
+ (*algorithm[f]->o.OptInit)(fp->bundle, &(*o)->val, &ccp->cfg);
+ }
+
+ if (cp + (*o)->val.hdr.len > buff + sizeof buff) {
+ log_Printf(LogERROR, "%s: CCP REQ buffer overrun !\n", fp->link->name);
+ break;
+ }
+ memcpy(cp, &(*o)->val, (*o)->val.hdr.len);
+ cp += (*o)->val.hdr.len;
+
+ ccp->my_proto = (*o)->val.hdr.id;
+ ccp->out.algorithm = f;
+
+ if (alloc)
+ o = &(*o)->next;
+ }
+
+ fsm_Output(fp, CODE_CONFIGREQ, fp->reqid, buff, cp - buff, MB_CCPOUT);
+}
+
+void
+ccp_SendResetReq(struct fsm *fp)
+{
+ /* We can't read our input - ask peer to reset */
+ struct ccp *ccp = fsm2ccp(fp);
+
+ ccp->reset_sent = fp->reqid;
+ ccp->last_reset = -1;
+ fsm_Output(fp, CODE_RESETREQ, fp->reqid, NULL, 0, MB_CCPOUT);
+}
+
+static void
+CcpSentTerminateReq(struct fsm *fp)
+{
+ /* Term REQ just sent by FSM */
+}
+
+static void
+CcpSendTerminateAck(struct fsm *fp, u_char id)
+{
+ /* Send Term ACK please */
+ fsm_Output(fp, CODE_TERMACK, id, NULL, 0, MB_CCPOUT);
+}
+
+static int
+CcpRecvResetReq(struct fsm *fp)
+{
+ /* Got a reset REQ, reset outgoing dictionary */
+ struct ccp *ccp = fsm2ccp(fp);
+ if (ccp->out.state == NULL)
+ return 1;
+ return (*algorithm[ccp->out.algorithm]->o.Reset)(ccp->out.state);
+}
+
+static void
+CcpLayerStart(struct fsm *fp)
+{
+ /* We're about to start up ! */
+ struct ccp *ccp = fsm2ccp(fp);
+
+ log_Printf(LogCCP, "%s: LayerStart.\n", fp->link->name);
+ fp->more.reqs = fp->more.naks = fp->more.rejs = ccp->cfg.fsm.maxreq * 3;
+}
+
+static void
+CcpLayerDown(struct fsm *fp)
+{
+ /* About to come down */
+ struct ccp *ccp = fsm2ccp(fp);
+ struct ccp_opt *next;
+
+ log_Printf(LogCCP, "%s: LayerDown.\n", fp->link->name);
+ if (ccp->in.state != NULL) {
+ (*algorithm[ccp->in.algorithm]->i.Term)(ccp->in.state);
+ ccp->in.state = NULL;
+ ccp->in.algorithm = -1;
+ }
+ if (ccp->out.state != NULL) {
+ (*algorithm[ccp->out.algorithm]->o.Term)(ccp->out.state);
+ ccp->out.state = NULL;
+ ccp->out.algorithm = -1;
+ }
+ ccp->his_reject = ccp->my_reject = 0;
+
+ while (ccp->out.opt) {
+ next = ccp->out.opt->next;
+ free(ccp->out.opt);
+ ccp->out.opt = next;
+ }
+ ccp_Setup(ccp);
+}
+
+static void
+CcpLayerFinish(struct fsm *fp)
+{
+ /* We're now down */
+ struct ccp *ccp = fsm2ccp(fp);
+ struct ccp_opt *next;
+
+ log_Printf(LogCCP, "%s: LayerFinish.\n", fp->link->name);
+
+ /*
+ * Nuke options that may be left over from sending a REQ but never
+ * coming up.
+ */
+ while (ccp->out.opt) {
+ next = ccp->out.opt->next;
+ free(ccp->out.opt);
+ ccp->out.opt = next;
+ }
+
+ if (ccp_Required(ccp)) {
+ if (fp->link->lcp.fsm.state == ST_OPENED)
+ log_Printf(LogLCP, "%s: Closing due to CCP completion\n", fp->link->name);
+ fsm_Close(&fp->link->lcp.fsm);
+ }
+}
+
+/* Called when CCP has reached the OPEN state */
+static int
+CcpLayerUp(struct fsm *fp)
+{
+ /* We're now up */
+ struct ccp *ccp = fsm2ccp(fp);
+ struct ccp_opt **o;
+ int f, fail;
+
+ for (f = fail = 0; f < NALGORITHMS; f++)
+ if (IsEnabled(ccp->cfg.neg[algorithm[f]->Neg]) &&
+ (*algorithm[f]->Required)(&ccp->fsm) &&
+ (ccp->in.algorithm != f || ccp->out.algorithm != f)) {
+ /* Blow it all away - we haven't negotiated a required algorithm */
+ log_Printf(LogWARN, "%s: Failed to negotiate (required) %s\n",
+ fp->link->name, protoname(algorithm[f]->id));
+ fail = 1;
+ }
+
+ if (fail) {
+ ccp->his_proto = ccp->my_proto = -1;
+ fsm_Close(fp);
+ fsm_Close(&fp->link->lcp.fsm);
+ return 0;
+ }
+
+ log_Printf(LogCCP, "%s: LayerUp.\n", fp->link->name);
+
+ if (ccp->in.state == NULL && ccp->in.algorithm >= 0 &&
+ ccp->in.algorithm < NALGORITHMS) {
+ ccp->in.state = (*algorithm[ccp->in.algorithm]->i.Init)
+ (fp->bundle, &ccp->in.opt);
+ if (ccp->in.state == NULL) {
+ log_Printf(LogERROR, "%s: %s (in) initialisation failure\n",
+ fp->link->name, protoname(ccp->his_proto));
+ ccp->his_proto = ccp->my_proto = -1;
+ fsm_Close(fp);
+ return 0;
+ }
+ }
+
+ o = &ccp->out.opt;
+ for (f = 0; f < ccp->out.algorithm; f++)
+ if (IsEnabled(ccp->cfg.neg[algorithm[f]->Neg]))
+ o = &(*o)->next;
+
+ if (ccp->out.state == NULL && ccp->out.algorithm >= 0 &&
+ ccp->out.algorithm < NALGORITHMS) {
+ ccp->out.state = (*algorithm[ccp->out.algorithm]->o.Init)
+ (fp->bundle, &(*o)->val);
+ if (ccp->out.state == NULL) {
+ log_Printf(LogERROR, "%s: %s (out) initialisation failure\n",
+ fp->link->name, protoname(ccp->my_proto));
+ ccp->his_proto = ccp->my_proto = -1;
+ fsm_Close(fp);
+ return 0;
+ }
+ }
+
+ fp->more.reqs = fp->more.naks = fp->more.rejs = ccp->cfg.fsm.maxreq * 3;
+
+ log_Printf(LogCCP, "%s: Out = %s[%d], In = %s[%d]\n",
+ fp->link->name, protoname(ccp->my_proto), ccp->my_proto,
+ protoname(ccp->his_proto), ccp->his_proto);
+
+ return 1;
+}
+
+static void
+CcpDecodeConfig(struct fsm *fp, u_char *cp, u_char *end, int mode_type,
+ struct fsm_decode *dec)
+{
+ /* Deal with incoming data */
+ struct ccp *ccp = fsm2ccp(fp);
+ int f;
+ const char *disp;
+ struct fsm_opt *opt;
+
+ if (mode_type == MODE_REQ)
+ ccp->in.algorithm = -1; /* In case we've received two REQs in a row */
+
+ while (end - cp >= sizeof(opt->hdr)) {
+ if ((opt = fsm_readopt(&cp)) == NULL)
+ break;
+
+ for (f = NALGORITHMS-1; f > -1; f--)
+ if (algorithm[f]->id == opt->hdr.id)
+ break;
+
+ disp = f == -1 ? "" : (*algorithm[f]->Disp)(opt);
+ if (disp == NULL)
+ disp = "";
+
+ log_Printf(LogCCP, " %s[%d] %s\n", protoname(opt->hdr.id),
+ opt->hdr.len, disp);
+
+ if (f == -1) {
+ /* Don't understand that :-( */
+ if (mode_type == MODE_REQ) {
+ ccp->my_reject |= (1 << opt->hdr.id);
+ fsm_rej(dec, opt);
+ }
+ } else {
+ struct ccp_opt *o;
+
+ switch (mode_type) {
+ case MODE_REQ:
+ if (IsAccepted(ccp->cfg.neg[algorithm[f]->Neg]) &&
+ (*algorithm[f]->Usable)(fp) &&
+ ccp->in.algorithm == -1) {
+ memcpy(&ccp->in.opt, opt, opt->hdr.len);
+ switch ((*algorithm[f]->i.Set)(fp->bundle, &ccp->in.opt, &ccp->cfg)) {
+ case MODE_REJ:
+ fsm_rej(dec, &ccp->in.opt);
+ break;
+ case MODE_NAK:
+ fsm_nak(dec, &ccp->in.opt);
+ break;
+ case MODE_ACK:
+ fsm_ack(dec, &ccp->in.opt);
+ ccp->his_proto = opt->hdr.id;
+ ccp->in.algorithm = f; /* This one'll do :-) */
+ break;
+ }
+ } else {
+ fsm_rej(dec, opt);
+ }
+ break;
+ case MODE_NAK:
+ for (o = ccp->out.opt; o != NULL; o = o->next)
+ if (o->val.hdr.id == opt->hdr.id)
+ break;
+ if (o == NULL)
+ log_Printf(LogCCP, "%s: Warning: Ignoring peer NAK of unsent"
+ " option\n", fp->link->name);
+ else {
+ memcpy(&o->val, opt, opt->hdr.len);
+ if ((*algorithm[f]->o.Set)(fp->bundle, &o->val, &ccp->cfg) ==
+ MODE_ACK)
+ ccp->my_proto = algorithm[f]->id;
+ else {
+ ccp->his_reject |= (1 << opt->hdr.id);
+ ccp->my_proto = -1;
+ if (algorithm[f]->Required(fp)) {
+ log_Printf(LogWARN, "%s: Cannot understand peers (required)"
+ " %s negotiation\n", fp->link->name,
+ protoname(algorithm[f]->id));
+ fsm_Close(&fp->link->lcp.fsm);
+ }
+ }
+ }
+ break;
+ case MODE_REJ:
+ ccp->his_reject |= (1 << opt->hdr.id);
+ ccp->my_proto = -1;
+ if (algorithm[f]->Required(fp)) {
+ log_Printf(LogWARN, "%s: Peer rejected (required) %s negotiation\n",
+ fp->link->name, protoname(algorithm[f]->id));
+ fsm_Close(&fp->link->lcp.fsm);
+ }
+ break;
+ }
+ }
+ }
+
+ if (mode_type != MODE_NOP) {
+ fsm_opt_normalise(dec);
+ if (dec->rejend != dec->rej || dec->nakend != dec->nak) {
+ if (ccp->in.state == NULL) {
+ ccp->his_proto = -1;
+ ccp->in.algorithm = -1;
+ }
+ }
+ }
+}
+
+extern struct mbuf *
+ccp_Input(struct bundle *bundle, struct link *l, struct mbuf *bp)
+{
+ /* Got PROTO_CCP from link */
+ m_settype(bp, MB_CCPIN);
+ if (bundle_Phase(bundle) == PHASE_NETWORK)
+ fsm_Input(&l->ccp.fsm, bp);
+ else {
+ if (bundle_Phase(bundle) < PHASE_NETWORK)
+ log_Printf(LogCCP, "%s: Error: Unexpected CCP in phase %s (ignored)\n",
+ l->ccp.fsm.link->name, bundle_PhaseName(bundle));
+ m_freem(bp);
+ }
+ return NULL;
+}
+
+static void
+CcpRecvResetAck(struct fsm *fp, u_char id)
+{
+ /* Got a reset ACK, reset incoming dictionary */
+ struct ccp *ccp = fsm2ccp(fp);
+
+ if (ccp->reset_sent != -1) {
+ if (id != ccp->reset_sent) {
+ log_Printf(LogCCP, "%s: Incorrect ResetAck (id %d, not %d)"
+ " ignored\n", fp->link->name, id, ccp->reset_sent);
+ return;
+ }
+ /* Whaddaya know - a correct reset ack */
+ } else if (id == ccp->last_reset)
+ log_Printf(LogCCP, "%s: Duplicate ResetAck (resetting again)\n",
+ fp->link->name);
+ else {
+ log_Printf(LogCCP, "%s: Unexpected ResetAck (id %d) ignored\n",
+ fp->link->name, id);
+ return;
+ }
+
+ ccp->last_reset = ccp->reset_sent;
+ ccp->reset_sent = -1;
+ if (ccp->in.state != NULL)
+ (*algorithm[ccp->in.algorithm]->i.Reset)(ccp->in.state);
+}
+
+static struct mbuf *
+ccp_LayerPush(struct bundle *b, struct link *l, struct mbuf *bp,
+ int pri, u_short *proto)
+{
+ if (PROTO_COMPRESSIBLE(*proto)) {
+ if (l->ccp.fsm.state != ST_OPENED) {
+ if (ccp_Required(&l->ccp)) {
+ /* The NCP layer shouldn't have let this happen ! */
+ log_Printf(LogERROR, "%s: Unexpected attempt to use an unopened and"
+ " required CCP layer\n", l->name);
+ m_freem(bp);
+ bp = NULL;
+ }
+ } else if (l->ccp.out.state != NULL) {
+ bp = (*algorithm[l->ccp.out.algorithm]->o.Write)
+ (l->ccp.out.state, &l->ccp, l, pri, proto, bp);
+ switch (*proto) {
+ case PROTO_ICOMPD:
+ m_settype(bp, MB_ICOMPDOUT);
+ break;
+ case PROTO_COMPD:
+ m_settype(bp, MB_COMPDOUT);
+ break;
+ }
+ }
+ }
+
+ return bp;
+}
+
+static struct mbuf *
+ccp_LayerPull(struct bundle *b, struct link *l, struct mbuf *bp, u_short *proto)
+{
+ /*
+ * If proto isn't PROTO_[I]COMPD, we still want to pass it to the
+ * decompression routines so that the dictionary's updated
+ */
+ if (l->ccp.fsm.state == ST_OPENED) {
+ if (*proto == PROTO_COMPD || *proto == PROTO_ICOMPD) {
+ /* Decompress incoming data */
+ if (l->ccp.reset_sent != -1)
+ /* Send another REQ and put the packet in the bit bucket */
+ fsm_Output(&l->ccp.fsm, CODE_RESETREQ, l->ccp.reset_sent, NULL, 0,
+ MB_CCPOUT);
+ else if (l->ccp.in.state != NULL) {
+ bp = (*algorithm[l->ccp.in.algorithm]->i.Read)
+ (l->ccp.in.state, &l->ccp, proto, bp);
+ switch (*proto) {
+ case PROTO_ICOMPD:
+ m_settype(bp, MB_ICOMPDIN);
+ break;
+ case PROTO_COMPD:
+ m_settype(bp, MB_COMPDIN);
+ break;
+ }
+ return bp;
+ }
+ m_freem(bp);
+ bp = NULL;
+ } else if (PROTO_COMPRESSIBLE(*proto) && l->ccp.in.state != NULL) {
+ /* Add incoming Network Layer traffic to our dictionary */
+ (*algorithm[l->ccp.in.algorithm]->i.DictSetup)
+ (l->ccp.in.state, &l->ccp, *proto, bp);
+ }
+ }
+
+ return bp;
+}
+
+u_short
+ccp_Proto(struct ccp *ccp)
+{
+ return !link2physical(ccp->fsm.link) || !ccp->fsm.bundle->ncp.mp.active ?
+ PROTO_COMPD : PROTO_ICOMPD;
+}
+
+int
+ccp_SetOpenMode(struct ccp *ccp)
+{
+ int f;
+
+ for (f = 0; f < CCP_NEG_TOTAL; f++)
+ if (IsEnabled(ccp->cfg.neg[f])) {
+ ccp->fsm.open_mode = 0;
+ return 1;
+ }
+
+ ccp->fsm.open_mode = OPEN_PASSIVE; /* Go straight to ST_STOPPED ? */
+
+ for (f = 0; f < CCP_NEG_TOTAL; f++)
+ if (IsAccepted(ccp->cfg.neg[f]))
+ return 1;
+
+ return 0; /* No CCP at all */
+}
+
+int
+ccp_DefaultUsable(struct fsm *fp)
+{
+ return 1;
+}
+
+int
+ccp_DefaultRequired(struct fsm *fp)
+{
+ return 0;
+}
+
+struct layer ccplayer = { LAYER_CCP, "ccp", ccp_LayerPush, ccp_LayerPull };
diff --git a/usr.sbin/ppp/ccp.h b/usr.sbin/ppp/ccp.h
new file mode 100644
index 0000000..bc867b6
--- /dev/null
+++ b/usr.sbin/ppp/ccp.h
@@ -0,0 +1,165 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define CCP_MAXCODE CODE_RESETACK
+
+#define TY_OUI 0 /* OUI */
+#define TY_PRED1 1 /* Predictor type 1 */
+#define TY_PRED2 2 /* Predictor type 2 */
+#define TY_PUDDLE 3 /* Puddle Jumper */
+#define TY_HWPPC 16 /* Hewlett-Packard PPC */
+#define TY_STAC 17 /* Stac Electronics LZS */
+#define TY_MSPPC 18 /* Microsoft PPC */
+#define TY_MPPE 18 /* Microsoft PPE */
+#define TY_GAND 19 /* Gandalf FZA */
+#define TY_V42BIS 20 /* V.42bis compression */
+#define TY_BSD 21 /* BSD LZW Compress */
+#define TY_PPPD_DEFLATE 24 /* Deflate (gzip) - (mis) numbered by pppd */
+#define TY_DEFLATE 26 /* Deflate (gzip) - rfc 1979 */
+
+#define CCP_NEG_DEFLATE 0
+#define CCP_NEG_PRED1 1
+#define CCP_NEG_DEFLATE24 2
+#ifndef NODES
+#define CCP_NEG_MPPE 3
+#define CCP_NEG_TOTAL 4
+#else
+#define CCP_NEG_TOTAL 3
+#endif
+
+#ifndef NODES
+enum mppe_negstate {
+ MPPE_ANYSTATE,
+ MPPE_STATELESS,
+ MPPE_STATEFUL
+};
+#endif
+
+struct mbuf;
+struct link;
+
+struct ccp_config {
+ struct {
+ struct {
+ int winsize;
+ } in, out;
+ } deflate;
+#ifndef NODES
+ struct {
+ int keybits;
+ enum mppe_negstate state;
+ unsigned required : 1;
+ } mppe;
+#endif
+ struct fsm_retry fsm; /* How often/frequently to resend requests */
+ unsigned neg[CCP_NEG_TOTAL];
+};
+
+struct ccp_opt {
+ struct ccp_opt *next;
+ int algorithm;
+ struct fsm_opt val;
+};
+
+struct ccp {
+ struct fsm fsm; /* The finite state machine */
+
+ int his_proto; /* peer's compression protocol */
+ int my_proto; /* our compression protocol */
+
+ int reset_sent; /* If != -1, ignore compressed 'till ack */
+ int last_reset; /* We can receive more (dups) w/ this id */
+
+ struct {
+ int algorithm; /* Algorithm in use */
+ void *state; /* Returned by implementations Init() */
+ struct fsm_opt opt; /* Set by implementation's OptInit() */
+ } in;
+
+ struct {
+ int algorithm; /* Algorithm in use */
+ void *state; /* Returned by implementations Init() */
+ struct ccp_opt *opt; /* Set by implementation's OptInit() */
+ } out;
+
+ u_int32_t his_reject; /* Request codes rejected by peer */
+ u_int32_t my_reject; /* Request codes I have rejected */
+
+ u_long uncompout, compout; /* Outgoing bytes before/after compression */
+ u_long uncompin, compin; /* Incoming bytes after/before decompression */
+
+ struct ccp_config cfg;
+};
+
+#define fsm2ccp(fp) (fp->proto == PROTO_CCP ? (struct ccp *)fp : NULL)
+
+struct ccp_algorithm {
+ int id;
+ int Neg; /* ccp_config neg array item */
+ const char *(*Disp)(struct fsm_opt *); /* Use result immediately ! */
+ int (*Usable)(struct fsm *); /* Ok to negotiate ? */
+ int (*Required)(struct fsm *); /* Must negotiate ? */
+ struct {
+ int (*Set)(struct bundle *, struct fsm_opt *, const struct ccp_config *);
+ void *(*Init)(struct bundle *, struct fsm_opt *);
+ void (*Term)(void *);
+ void (*Reset)(void *);
+ struct mbuf *(*Read)(void *, struct ccp *, u_short *, struct mbuf *);
+ void (*DictSetup)(void *, struct ccp *, u_short, struct mbuf *);
+ } i;
+ struct {
+ int MTUOverhead;
+ void (*OptInit)(struct bundle *, struct fsm_opt *,
+ const struct ccp_config *);
+ int (*Set)(struct bundle *, struct fsm_opt *, const struct ccp_config *);
+ void *(*Init)(struct bundle *, struct fsm_opt *);
+ void (*Term)(void *);
+ int (*Reset)(void *);
+ struct mbuf *(*Write)(void *, struct ccp *, struct link *, int, u_short *,
+ struct mbuf *);
+ } o;
+};
+
+extern void ccp_Init(struct ccp *, struct bundle *, struct link *,
+ const struct fsm_parent *);
+extern void ccp_Setup(struct ccp *);
+extern int ccp_Required(struct ccp *);
+extern int ccp_MTUOverhead(struct ccp *);
+
+extern void ccp_SendResetReq(struct fsm *);
+extern struct mbuf *ccp_Input(struct bundle *, struct link *, struct mbuf *);
+extern int ccp_ReportStatus(struct cmdargs const *);
+extern u_short ccp_Proto(struct ccp *);
+extern void ccp_SetupCallbacks(struct ccp *);
+extern int ccp_SetOpenMode(struct ccp *);
+extern int ccp_DefaultUsable(struct fsm *);
+extern int ccp_DefaultRequired(struct fsm *);
+
+extern struct layer ccplayer;
diff --git a/usr.sbin/ppp/chap.c b/usr.sbin/ppp/chap.c
new file mode 100644
index 0000000..20ad528
--- /dev/null
+++ b/usr.sbin/ppp/chap.c
@@ -0,0 +1,962 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#ifndef NODES
+#include <md4.h>
+#endif
+#include <md5.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "layer.h"
+#include "mbuf.h"
+#include "log.h"
+#include "defs.h"
+#include "timer.h"
+#include "fsm.h"
+#include "proto.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "lcp.h"
+#include "auth.h"
+#include "async.h"
+#include "throughput.h"
+#include "descriptor.h"
+#include "chap.h"
+#include "iplist.h"
+#include "slcompress.h"
+#include "ncpaddr.h"
+#include "ipcp.h"
+#include "filter.h"
+#include "ccp.h"
+#include "link.h"
+#include "physical.h"
+#include "mp.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+#include "chat.h"
+#include "cbcp.h"
+#include "command.h"
+#include "datalink.h"
+#ifndef NODES
+#include "chap_ms.h"
+#include "mppe.h"
+#endif
+#include "id.h"
+
+static const char * const chapcodes[] = {
+ "???", "CHALLENGE", "RESPONSE", "SUCCESS", "FAILURE"
+};
+#define MAXCHAPCODE (sizeof chapcodes / sizeof chapcodes[0] - 1)
+
+static void
+ChapOutput(struct physical *physical, u_int code, u_int id,
+ const u_char *ptr, int count, const char *text)
+{
+ int plen;
+ struct fsmheader lh;
+ struct mbuf *bp;
+
+ plen = sizeof(struct fsmheader) + count;
+ lh.code = code;
+ lh.id = id;
+ lh.length = htons(plen);
+ bp = m_get(plen, MB_CHAPOUT);
+ memcpy(MBUF_CTOP(bp), &lh, sizeof(struct fsmheader));
+ if (count)
+ memcpy(MBUF_CTOP(bp) + sizeof(struct fsmheader), ptr, count);
+ log_DumpBp(LogDEBUG, "ChapOutput", bp);
+ if (text == NULL)
+ log_Printf(LogPHASE, "Chap Output: %s\n", chapcodes[code]);
+ else
+ log_Printf(LogPHASE, "Chap Output: %s (%s)\n", chapcodes[code], text);
+ link_PushPacket(&physical->link, bp, physical->dl->bundle,
+ LINK_QUEUES(&physical->link) - 1, PROTO_CHAP);
+}
+
+static char *
+chap_BuildAnswer(char *name, char *key, u_char id, char *challenge, u_char type
+#ifndef NODES
+ , char *peerchallenge, char *authresponse, int lanman
+#endif
+ )
+{
+ char *result, *digest;
+ size_t nlen, klen;
+
+ nlen = strlen(name);
+ klen = strlen(key);
+
+#ifndef NODES
+ if (type == 0x80) {
+ char expkey[AUTHLEN << 2];
+ MD4_CTX MD4context;
+ int f;
+
+ if ((result = malloc(1 + nlen + MS_CHAP_RESPONSE_LEN)) == NULL)
+ return result;
+
+ digest = result; /* the response */
+ *digest++ = MS_CHAP_RESPONSE_LEN; /* 49 */
+ memcpy(digest + MS_CHAP_RESPONSE_LEN, name, nlen);
+ if (lanman) {
+ memset(digest + 24, '\0', 25);
+ mschap_LANMan(digest, challenge + 1, key); /* LANMan response */
+ } else {
+ memset(digest, '\0', 25);
+ digest += 24;
+
+ for (f = 0; f < klen; f++) {
+ expkey[2*f] = key[f];
+ expkey[2*f+1] = '\0';
+ }
+ /*
+ * -----------
+ * expkey = | k\0e\0y\0 |
+ * -----------
+ */
+ MD4Init(&MD4context);
+ MD4Update(&MD4context, expkey, klen << 1);
+ MD4Final(digest, &MD4context);
+
+ /*
+ * ---- -------- ---------------- ------- ------
+ * result = | 49 | LANMan | 16 byte digest | 9 * ? | name |
+ * ---- -------- ---------------- ------- ------
+ */
+ mschap_NT(digest, challenge + 1);
+ }
+ /*
+ * ---- -------- ------------- ----- ------
+ * | | struct MS_ChapResponse24 | |
+ * result = | 49 | LANMan | NT digest | 0/1 | name |
+ * ---- -------- ------------- ----- ------
+ * where only one of LANMan & NT digest are set.
+ */
+ } else if (type == 0x81) {
+ char expkey[AUTHLEN << 2];
+ char pwdhash[CHAP81_HASH_LEN];
+ char pwdhashhash[CHAP81_HASH_LEN];
+ char *ntresponse;
+ int f;
+
+ if ((result = malloc(1 + nlen + CHAP81_RESPONSE_LEN)) == NULL)
+ return result;
+
+ memset(result, 0, 1 + nlen + CHAP81_RESPONSE_LEN);
+
+ digest = result;
+ *digest++ = CHAP81_RESPONSE_LEN; /* value size */
+
+ /* Copy our challenge */
+ memcpy(digest, peerchallenge + 1, CHAP81_CHALLENGE_LEN);
+
+ /* Expand password to Unicode XXX */
+ for (f = 0; f < klen; f++) {
+ expkey[2*f] = key[f];
+ expkey[2*f+1] = '\0';
+ }
+
+ ntresponse = digest + CHAP81_NTRESPONSE_OFF;
+
+ /* Get some needed hashes */
+ NtPasswordHash(expkey, klen * 2, pwdhash);
+ HashNtPasswordHash(pwdhash, pwdhashhash);
+
+ /* Generate NTRESPONSE to respond on challenge call */
+ GenerateNTResponse(challenge + 1, peerchallenge + 1, name, nlen,
+ expkey, klen * 2, ntresponse);
+
+ /* Generate MPPE MASTERKEY */
+ GetMasterKey(pwdhashhash, ntresponse, MPPE_MasterKey); /* XXX Global ! */
+
+ /* Generate AUTHRESPONSE to verify on auth success */
+ GenerateAuthenticatorResponse(expkey, klen * 2, ntresponse,
+ peerchallenge + 1, challenge + 1, name, nlen,
+ authresponse);
+
+ authresponse[CHAP81_AUTHRESPONSE_LEN] = 0;
+
+ memcpy(digest + CHAP81_RESPONSE_LEN, name, nlen);
+ } else
+#endif
+ if ((result = malloc(nlen + 17)) != NULL) {
+ /* Normal MD5 stuff */
+ MD5_CTX MD5context;
+
+ digest = result;
+ *digest++ = 16; /* value size */
+
+ MD5Init(&MD5context);
+ MD5Update(&MD5context, &id, 1);
+ MD5Update(&MD5context, key, klen);
+ MD5Update(&MD5context, challenge + 1, *challenge);
+ MD5Final(digest, &MD5context);
+
+ memcpy(digest + 16, name, nlen);
+ /*
+ * ---- -------- ------
+ * result = | 16 | digest | name |
+ * ---- -------- ------
+ */
+ }
+
+ return result;
+}
+
+static void
+chap_StartChild(struct chap *chap, char *prog, const char *name)
+{
+ char *argv[MAXARGS], *nargv[MAXARGS];
+ int argc, fd;
+ int in[2], out[2];
+ pid_t pid;
+
+ if (chap->child.fd != -1) {
+ log_Printf(LogWARN, "Chap: %s: Program already running\n", prog);
+ return;
+ }
+
+ if (pipe(in) == -1) {
+ log_Printf(LogERROR, "Chap: pipe: %s\n", strerror(errno));
+ return;
+ }
+
+ if (pipe(out) == -1) {
+ log_Printf(LogERROR, "Chap: pipe: %s\n", strerror(errno));
+ close(in[0]);
+ close(in[1]);
+ return;
+ }
+
+ pid = getpid();
+ switch ((chap->child.pid = fork())) {
+ case -1:
+ log_Printf(LogERROR, "Chap: fork: %s\n", strerror(errno));
+ close(in[0]);
+ close(in[1]);
+ close(out[0]);
+ close(out[1]);
+ chap->child.pid = 0;
+ return;
+
+ case 0:
+ timer_TermService();
+
+ if ((argc = command_Interpret(prog, strlen(prog), argv)) <= 0) {
+ if (argc < 0) {
+ log_Printf(LogWARN, "CHAP: Invalid command syntax\n");
+ _exit(255);
+ }
+ _exit(0);
+ }
+
+ close(in[1]);
+ close(out[0]);
+ if (out[1] == STDIN_FILENO)
+ out[1] = dup(out[1]);
+ dup2(in[0], STDIN_FILENO);
+ dup2(out[1], STDOUT_FILENO);
+ close(STDERR_FILENO);
+ if (open(_PATH_DEVNULL, O_RDWR) != STDERR_FILENO) {
+ log_Printf(LogALERT, "Chap: Failed to open %s: %s\n",
+ _PATH_DEVNULL, strerror(errno));
+ exit(1);
+ }
+ for (fd = getdtablesize(); fd > STDERR_FILENO; fd--)
+ fcntl(fd, F_SETFD, 1);
+#ifndef NOSUID
+ setuid(ID0realuid());
+#endif
+ command_Expand(nargv, argc, (char const *const *)argv,
+ chap->auth.physical->dl->bundle, 0, pid);
+ execvp(nargv[0], nargv);
+ printf("exec() of %s failed: %s\n", nargv[0], strerror(errno));
+ _exit(255);
+
+ default:
+ close(in[0]);
+ close(out[1]);
+ chap->child.fd = out[0];
+ chap->child.buf.len = 0;
+ write(in[1], chap->auth.in.name, strlen(chap->auth.in.name));
+ write(in[1], "\n", 1);
+ write(in[1], chap->challenge.peer + 1, *chap->challenge.peer);
+ write(in[1], "\n", 1);
+ write(in[1], name, strlen(name));
+ write(in[1], "\n", 1);
+ close(in[1]);
+ break;
+ }
+}
+
+static void
+chap_Cleanup(struct chap *chap, int sig)
+{
+ if (chap->child.pid) {
+ int status;
+
+ close(chap->child.fd);
+ chap->child.fd = -1;
+ if (sig)
+ kill(chap->child.pid, SIGTERM);
+ chap->child.pid = 0;
+ chap->child.buf.len = 0;
+
+ if (wait(&status) == -1)
+ log_Printf(LogERROR, "Chap: wait: %s\n", strerror(errno));
+ else if (WIFSIGNALED(status))
+ log_Printf(LogWARN, "Chap: Child received signal %d\n", WTERMSIG(status));
+ else if (WIFEXITED(status) && WEXITSTATUS(status))
+ log_Printf(LogERROR, "Chap: Child exited %d\n", WEXITSTATUS(status));
+ }
+ *chap->challenge.local = *chap->challenge.peer = '\0';
+#ifndef NODES
+ chap->peertries = 0;
+#endif
+}
+
+static void
+chap_Respond(struct chap *chap, char *name, char *key, u_char type
+#ifndef NODES
+ , int lm
+#endif
+ )
+{
+ u_char *ans;
+
+ ans = chap_BuildAnswer(name, key, chap->auth.id, chap->challenge.peer, type
+#ifndef NODES
+ , chap->challenge.local, chap->authresponse, lm
+#endif
+ );
+
+ if (ans) {
+ ChapOutput(chap->auth.physical, CHAP_RESPONSE, chap->auth.id,
+ ans, *ans + 1 + strlen(name), name);
+#ifndef NODES
+ chap->NTRespSent = !lm;
+ MPPE_IsServer = 0; /* XXX Global ! */
+#endif
+ free(ans);
+ } else
+ ChapOutput(chap->auth.physical, CHAP_FAILURE, chap->auth.id,
+ "Out of memory!", 14, NULL);
+}
+
+static int
+chap_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n)
+{
+ struct chap *chap = descriptor2chap(d);
+
+ if (r && chap && chap->child.fd != -1) {
+ FD_SET(chap->child.fd, r);
+ if (*n < chap->child.fd + 1)
+ *n = chap->child.fd + 1;
+ log_Printf(LogTIMER, "Chap: fdset(r) %d\n", chap->child.fd);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+chap_IsSet(struct fdescriptor *d, const fd_set *fdset)
+{
+ struct chap *chap = descriptor2chap(d);
+
+ return chap && chap->child.fd != -1 && FD_ISSET(chap->child.fd, fdset);
+}
+
+static void
+chap_Read(struct fdescriptor *d, struct bundle *bundle, const fd_set *fdset)
+{
+ struct chap *chap = descriptor2chap(d);
+ int got;
+
+ got = read(chap->child.fd, chap->child.buf.ptr + chap->child.buf.len,
+ sizeof chap->child.buf.ptr - chap->child.buf.len - 1);
+ if (got == -1) {
+ log_Printf(LogERROR, "Chap: Read: %s\n", strerror(errno));
+ chap_Cleanup(chap, SIGTERM);
+ } else if (got == 0) {
+ log_Printf(LogWARN, "Chap: Read: Child terminated connection\n");
+ chap_Cleanup(chap, SIGTERM);
+ } else {
+ char *name, *key, *end;
+
+ chap->child.buf.len += got;
+ chap->child.buf.ptr[chap->child.buf.len] = '\0';
+ name = chap->child.buf.ptr;
+ name += strspn(name, " \t");
+ if ((key = strchr(name, '\n')) == NULL)
+ end = NULL;
+ else
+ end = strchr(++key, '\n');
+
+ if (end == NULL) {
+ if (chap->child.buf.len == sizeof chap->child.buf.ptr - 1) {
+ log_Printf(LogWARN, "Chap: Read: Input buffer overflow\n");
+ chap_Cleanup(chap, SIGTERM);
+ }
+ } else {
+#ifndef NODES
+ int lanman = chap->auth.physical->link.lcp.his_authtype == 0x80 &&
+ ((chap->NTRespSent &&
+ IsAccepted(chap->auth.physical->link.lcp.cfg.chap80lm)) ||
+ !IsAccepted(chap->auth.physical->link.lcp.cfg.chap80nt));
+#endif
+
+ while (end >= name && strchr(" \t\r\n", *end))
+ *end-- = '\0';
+ end = key - 1;
+ while (end >= name && strchr(" \t\r\n", *end))
+ *end-- = '\0';
+ key += strspn(key, " \t");
+
+ chap_Respond(chap, name, key, chap->auth.physical->link.lcp.his_authtype
+#ifndef NODES
+ , lanman
+#endif
+ );
+ chap_Cleanup(chap, 0);
+ }
+ }
+}
+
+static int
+chap_Write(struct fdescriptor *d, struct bundle *bundle, const fd_set *fdset)
+{
+ /* We never want to write here ! */
+ log_Printf(LogALERT, "chap_Write: Internal error: Bad call !\n");
+ return 0;
+}
+
+static void
+chap_ChallengeInit(struct authinfo *authp)
+{
+ struct chap *chap = auth2chap(authp);
+ int len, i;
+ char *cp;
+
+ len = strlen(authp->physical->dl->bundle->cfg.auth.name);
+
+ if (!*chap->challenge.local) {
+ randinit();
+ cp = chap->challenge.local;
+
+#ifndef NORADIUS
+ if (*authp->physical->dl->bundle->radius.cfg.file) {
+ /* For radius, our challenge is 16 readable NUL terminated bytes :*/
+ *cp++ = 16;
+ for (i = 0; i < 16; i++)
+ *cp++ = (random() % 10) + '0';
+ } else
+#endif
+ {
+#ifndef NODES
+ if (authp->physical->link.lcp.want_authtype == 0x80)
+ *cp++ = 8; /* MS does 8 byte callenges :-/ */
+ else if (authp->physical->link.lcp.want_authtype == 0x81)
+ *cp++ = 16; /* MS-CHAP-V2 does 16 bytes challenges */
+ else
+#endif
+ *cp++ = random() % (CHAPCHALLENGELEN-16) + 16;
+ for (i = 0; i < *chap->challenge.local; i++)
+ *cp++ = random() & 0xff;
+ }
+ memcpy(cp, authp->physical->dl->bundle->cfg.auth.name, len);
+ }
+}
+
+static void
+chap_Challenge(struct authinfo *authp)
+{
+ struct chap *chap = auth2chap(authp);
+ int len;
+
+ log_Printf(LogDEBUG, "CHAP%02X: Challenge\n",
+ authp->physical->link.lcp.want_authtype);
+
+ len = strlen(authp->physical->dl->bundle->cfg.auth.name);
+
+ /* Generate new local challenge value */
+ if (!*chap->challenge.local)
+ chap_ChallengeInit(authp);
+
+#ifndef NODES
+ if (authp->physical->link.lcp.want_authtype == 0x81)
+ ChapOutput(authp->physical, CHAP_CHALLENGE, authp->id,
+ chap->challenge.local, 1 + *chap->challenge.local, NULL);
+ else
+#endif
+ ChapOutput(authp->physical, CHAP_CHALLENGE, authp->id,
+ chap->challenge.local, 1 + *chap->challenge.local + len, NULL);
+}
+
+static void
+chap_Success(struct authinfo *authp)
+{
+ struct bundle *bundle = authp->physical->dl->bundle;
+ const char *msg;
+
+ datalink_GotAuthname(authp->physical->dl, authp->in.name);
+#ifndef NODES
+ if (authp->physical->link.lcp.want_authtype == 0x81) {
+#ifndef NORADIUS
+ if (*bundle->radius.cfg.file && bundle->radius.msrepstr)
+ msg = bundle->radius.msrepstr;
+ else
+#endif
+ msg = auth2chap(authp)->authresponse;
+ MPPE_MasterKeyValid = 1; /* XXX Global ! */
+ } else
+#endif
+#ifndef NORADIUS
+ if (*bundle->radius.cfg.file && bundle->radius.repstr)
+ msg = bundle->radius.repstr;
+ else
+#endif
+ msg = "Welcome!!";
+
+ ChapOutput(authp->physical, CHAP_SUCCESS, authp->id, msg, strlen(msg),
+ NULL);
+
+ authp->physical->link.lcp.auth_ineed = 0;
+ if (Enabled(bundle, OPT_UTMP))
+ physical_Login(authp->physical, authp->in.name);
+
+ if (authp->physical->link.lcp.auth_iwait == 0)
+ /*
+ * Either I didn't need to authenticate, or I've already been
+ * told that I got the answer right.
+ */
+ datalink_AuthOk(authp->physical->dl);
+}
+
+static void
+chap_Failure(struct authinfo *authp)
+{
+#ifndef NODES
+ char buf[1024], *ptr;
+#endif
+ const char *msg;
+
+#ifndef NORADIUS
+ struct bundle *bundle = authp->physical->link.lcp.fsm.bundle;
+ if (*bundle->radius.cfg.file && bundle->radius.errstr)
+ msg = bundle->radius.errstr;
+ else
+#endif
+#ifndef NODES
+ if (authp->physical->link.lcp.want_authtype == 0x80) {
+ sprintf(buf, "E=691 R=1 M=Invalid!");
+ msg = buf;
+ } else if (authp->physical->link.lcp.want_authtype == 0x81) {
+ int i;
+
+ ptr = buf;
+ ptr += sprintf(buf, "E=691 R=0 C=");
+ for (i=0; i<16; i++)
+ ptr += sprintf(ptr, "%02X", *(auth2chap(authp)->challenge.local+1+i));
+
+ sprintf(ptr, " V=3 M=Invalid!");
+ msg = buf;
+ } else
+#endif
+ msg = "Invalid!!";
+
+ ChapOutput(authp->physical, CHAP_FAILURE, authp->id, msg, strlen(msg) + 1,
+ NULL);
+ datalink_AuthNotOk(authp->physical->dl);
+}
+
+static int
+chap_Cmp(u_char type, char *myans, int mylen, char *hisans, int hislen
+#ifndef NODES
+ , int lm
+#endif
+ )
+{
+ if (mylen != hislen)
+ return 0;
+#ifndef NODES
+ 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;
+}
+
+#ifndef NODES
+static int
+chap_HaveAnotherGo(struct chap *chap)
+{
+ if (++chap->peertries < 3) {
+ /* Give the peer another shot */
+ *chap->challenge.local = '\0';
+ chap_Challenge(&chap->auth);
+ return 1;
+ }
+
+ return 0;
+}
+#endif
+
+void
+chap_Init(struct chap *chap, struct physical *p)
+{
+ chap->desc.type = CHAP_DESCRIPTOR;
+ chap->desc.UpdateSet = chap_UpdateSet;
+ chap->desc.IsSet = chap_IsSet;
+ chap->desc.Read = chap_Read;
+ chap->desc.Write = chap_Write;
+ chap->child.pid = 0;
+ chap->child.fd = -1;
+ auth_Init(&chap->auth, p, chap_Challenge, chap_Success, chap_Failure);
+ *chap->challenge.local = *chap->challenge.peer = '\0';
+#ifndef NODES
+ chap->NTRespSent = 0;
+ chap->peertries = 0;
+#endif
+}
+
+void
+chap_ReInit(struct chap *chap)
+{
+ chap_Cleanup(chap, SIGTERM);
+}
+
+struct mbuf *
+chap_Input(struct bundle *bundle, struct link *l, struct mbuf *bp)
+{
+ struct physical *p = link2physical(l);
+ struct chap *chap = &p->dl->chap;
+ char *name, *key, *ans;
+ int len, nlen;
+ u_char alen;
+#ifndef NODES
+ int lanman;
+#endif
+
+ if (p == NULL) {
+ log_Printf(LogERROR, "chap_Input: Not a physical link - dropped\n");
+ m_freem(bp);
+ return NULL;
+ }
+
+ if (bundle_Phase(bundle) != PHASE_NETWORK &&
+ bundle_Phase(bundle) != PHASE_AUTHENTICATE) {
+ log_Printf(LogPHASE, "Unexpected chap input - dropped !\n");
+ m_freem(bp);
+ return NULL;
+ }
+
+ m_settype(bp, MB_CHAPIN);
+ if ((bp = auth_ReadHeader(&chap->auth, bp)) == NULL &&
+ ntohs(chap->auth.in.hdr.length) == 0)
+ log_Printf(LogWARN, "Chap Input: Truncated header !\n");
+ else if (chap->auth.in.hdr.code == 0 || chap->auth.in.hdr.code > MAXCHAPCODE)
+ log_Printf(LogPHASE, "Chap Input: %d: Bad CHAP code !\n",
+ chap->auth.in.hdr.code);
+ else {
+ len = m_length(bp);
+ ans = NULL;
+
+ if (chap->auth.in.hdr.code != CHAP_CHALLENGE &&
+ chap->auth.id != chap->auth.in.hdr.id &&
+ Enabled(bundle, OPT_IDCHECK)) {
+ /* Wrong conversation dude ! */
+ log_Printf(LogPHASE, "Chap Input: %s dropped (got id %d, not %d)\n",
+ chapcodes[chap->auth.in.hdr.code], chap->auth.in.hdr.id,
+ chap->auth.id);
+ m_freem(bp);
+ return NULL;
+ }
+ chap->auth.id = chap->auth.in.hdr.id; /* We respond with this id */
+
+#ifndef NODES
+ lanman = 0;
+#endif
+ switch (chap->auth.in.hdr.code) {
+ case CHAP_CHALLENGE:
+ bp = mbuf_Read(bp, &alen, 1);
+ len -= alen + 1;
+ if (len < 0) {
+ log_Printf(LogERROR, "Chap Input: Truncated challenge !\n");
+ m_freem(bp);
+ return NULL;
+ }
+ *chap->challenge.peer = alen;
+ bp = mbuf_Read(bp, chap->challenge.peer + 1, alen);
+ bp = auth_ReadName(&chap->auth, bp, len);
+#ifndef NODES
+ lanman = p->link.lcp.his_authtype == 0x80 &&
+ ((chap->NTRespSent && IsAccepted(p->link.lcp.cfg.chap80lm)) ||
+ !IsAccepted(p->link.lcp.cfg.chap80nt));
+
+ /* Generate local challenge value */
+ chap_ChallengeInit(&chap->auth);
+#endif
+ break;
+
+ case CHAP_RESPONSE:
+ auth_StopTimer(&chap->auth);
+ bp = mbuf_Read(bp, &alen, 1);
+ len -= alen + 1;
+ if (len < 0) {
+ log_Printf(LogERROR, "Chap Input: Truncated response !\n");
+ m_freem(bp);
+ return NULL;
+ }
+ if ((ans = malloc(alen + 1)) == NULL) {
+ log_Printf(LogERROR, "Chap Input: Out of memory !\n");
+ m_freem(bp);
+ return NULL;
+ }
+ *ans = chap->auth.id;
+ bp = mbuf_Read(bp, ans + 1, alen);
+ bp = auth_ReadName(&chap->auth, bp, len);
+#ifndef NODES
+ lanman = p->link.lcp.want_authtype == 0x80 &&
+ alen == 49 && ans[alen] == 0;
+#endif
+ break;
+
+ case CHAP_SUCCESS:
+ case CHAP_FAILURE:
+ /* chap->auth.in.name is already set up at CHALLENGE time */
+ if ((ans = malloc(len + 1)) == NULL) {
+ log_Printf(LogERROR, "Chap Input: Out of memory !\n");
+ m_freem(bp);
+ return NULL;
+ }
+ bp = mbuf_Read(bp, ans, len);
+ ans[len] = '\0';
+ break;
+ }
+
+ switch (chap->auth.in.hdr.code) {
+ case CHAP_CHALLENGE:
+ case CHAP_RESPONSE:
+ if (*chap->auth.in.name)
+ log_Printf(LogPHASE, "Chap Input: %s (%d bytes from %s%s)\n",
+ chapcodes[chap->auth.in.hdr.code], alen,
+ chap->auth.in.name,
+#ifndef NODES
+ lanman && chap->auth.in.hdr.code == CHAP_RESPONSE ?
+ " - lanman" :
+#endif
+ "");
+ else
+ log_Printf(LogPHASE, "Chap Input: %s (%d bytes%s)\n",
+ chapcodes[chap->auth.in.hdr.code], alen,
+#ifndef NODES
+ lanman && chap->auth.in.hdr.code == CHAP_RESPONSE ?
+ " - lanman" :
+#endif
+ "");
+ break;
+
+ case CHAP_SUCCESS:
+ case CHAP_FAILURE:
+ if (*ans)
+ log_Printf(LogPHASE, "Chap Input: %s (%s)\n",
+ chapcodes[chap->auth.in.hdr.code], ans);
+ else
+ log_Printf(LogPHASE, "Chap Input: %s\n",
+ chapcodes[chap->auth.in.hdr.code]);
+ break;
+ }
+
+ switch (chap->auth.in.hdr.code) {
+ case CHAP_CHALLENGE:
+ if (*bundle->cfg.auth.key == '!' && bundle->cfg.auth.key[1] != '!')
+ chap_StartChild(chap, bundle->cfg.auth.key + 1,
+ bundle->cfg.auth.name);
+ else
+ chap_Respond(chap, bundle->cfg.auth.name, bundle->cfg.auth.key +
+ (*bundle->cfg.auth.key == '!' ? 1 : 0),
+ p->link.lcp.his_authtype
+#ifndef NODES
+ , lanman
+#endif
+ );
+ break;
+
+ case CHAP_RESPONSE:
+ name = chap->auth.in.name;
+ nlen = strlen(name);
+#ifndef NODES
+ if (p->link.lcp.want_authtype == 0x81) {
+ struct MSCHAPv2_resp *resp = (struct MSCHAPv2_resp *)(ans + 1);
+
+ chap->challenge.peer[0] = sizeof resp->PeerChallenge;
+ memcpy(chap->challenge.peer + 1, resp->PeerChallenge,
+ sizeof resp->PeerChallenge);
+ }
+#endif
+
+#ifndef NORADIUS
+ if (*bundle->radius.cfg.file) {
+ if (!radius_Authenticate(&bundle->radius, &chap->auth,
+ chap->auth.in.name, ans, alen + 1,
+ chap->challenge.local + 1,
+ *chap->challenge.local))
+ chap_Failure(&chap->auth);
+ } else
+#endif
+ {
+ if (p->link.lcp.want_authtype == 0x81 && ans[alen] != '\0' &&
+ alen == sizeof(struct MSCHAPv2_resp)) {
+ struct MSCHAPv2_resp *resp = (struct MSCHAPv2_resp *)(ans + 1);
+
+ log_Printf(LogWARN, "%s: Compensating for corrupt (Win98/WinME?) "
+ "CHAP81 RESPONSE\n", l->name);
+ resp->Flags = '\0'; /* rfc2759 says it *MUST* be zero */
+ }
+ key = auth_GetSecret(bundle, name, nlen, p);
+ if (key) {
+#ifndef NODES
+ if (p->link.lcp.want_authtype == 0x80 &&
+ lanman && !IsEnabled(p->link.lcp.cfg.chap80lm)) {
+ log_Printf(LogPHASE, "Auth failure: LANMan not enabled\n");
+ if (chap_HaveAnotherGo(chap))
+ break;
+ key = NULL;
+ } else if (p->link.lcp.want_authtype == 0x80 &&
+ !lanman && !IsEnabled(p->link.lcp.cfg.chap80nt)) {
+ log_Printf(LogPHASE, "Auth failure: mschap not enabled\n");
+ if (chap_HaveAnotherGo(chap))
+ break;
+ key = NULL;
+ } else if (p->link.lcp.want_authtype == 0x81 &&
+ !IsEnabled(p->link.lcp.cfg.chap81)) {
+ log_Printf(LogPHASE, "Auth failure: CHAP81 not enabled\n");
+ key = NULL;
+ } else
+#endif
+ {
+ char *myans = chap_BuildAnswer(name, key, chap->auth.id,
+ chap->challenge.local,
+ p->link.lcp.want_authtype
+#ifndef NODES
+ , chap->challenge.peer,
+ chap->authresponse, lanman);
+ MPPE_IsServer = 1; /* XXX Global ! */
+#else
+ );
+#endif
+ if (myans == NULL)
+ key = NULL;
+ else {
+ if (!chap_Cmp(p->link.lcp.want_authtype, myans + 1, *myans,
+ ans + 1, alen
+#ifndef NODES
+ , lanman
+#endif
+ ))
+ key = NULL;
+ free(myans);
+ }
+ }
+ }
+
+ if (key)
+ chap_Success(&chap->auth);
+ else
+ chap_Failure(&chap->auth);
+ }
+
+ break;
+
+ case CHAP_SUCCESS:
+ if (p->link.lcp.auth_iwait == PROTO_CHAP) {
+ p->link.lcp.auth_iwait = 0;
+ if (p->link.lcp.auth_ineed == 0) {
+#ifndef NODES
+ if (p->link.lcp.his_authtype == 0x81) {
+ if (strncmp(ans, chap->authresponse, 42)) {
+ datalink_AuthNotOk(p->dl);
+ log_Printf(LogWARN, "CHAP81: AuthenticatorResponse: (%.42s)"
+ " != ans: (%.42s)\n", chap->authresponse, ans);
+
+ } else {
+ /* Successful login */
+ MPPE_MasterKeyValid = 1; /* XXX Global ! */
+ datalink_AuthOk(p->dl);
+ }
+ } else
+#endif
+ /*
+ * We've succeeded in our ``login''
+ * If we're not expecting the peer to authenticate (or he already
+ * has), proceed to network phase.
+ */
+ datalink_AuthOk(p->dl);
+ }
+ }
+ break;
+
+ case CHAP_FAILURE:
+ datalink_AuthNotOk(p->dl);
+ break;
+ }
+ free(ans);
+ }
+
+ m_freem(bp);
+ return NULL;
+}
diff --git a/usr.sbin/ppp/chap.h b/usr.sbin/ppp/chap.h
new file mode 100644
index 0000000..617555d
--- /dev/null
+++ b/usr.sbin/ppp/chap.h
@@ -0,0 +1,75 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct mbuf;
+struct physical;
+
+#define CHAP_CHALLENGE 1
+#define CHAP_RESPONSE 2
+#define CHAP_SUCCESS 3
+#define CHAP_FAILURE 4
+
+struct chap {
+ struct fdescriptor desc;
+ struct {
+ pid_t pid;
+ int fd;
+ struct {
+ char ptr[AUTHLEN * 2 + 3]; /* Allow for \r\n at the end (- NUL) */
+ int len;
+ } buf;
+ } child;
+ struct authinfo auth;
+ struct {
+ u_char local[CHAPCHALLENGELEN + AUTHLEN]; /* I invented this one */
+ u_char peer[CHAPCHALLENGELEN + AUTHLEN]; /* Peer gave us this one */
+ } challenge;
+#ifndef NODES
+ unsigned NTRespSent : 1; /* Our last response */
+ int peertries;
+ u_char authresponse[CHAPAUTHRESPONSELEN]; /* CHAP 81 response */
+#endif
+};
+
+#define descriptor2chap(d) \
+ ((d)->type == CHAP_DESCRIPTOR ? (struct chap *)(d) : NULL)
+#define auth2chap(a) \
+ ((struct chap *)((char *)a - (int)&((struct chap *)0)->auth))
+
+struct MSCHAPv2_resp { /* rfc2759 */
+ char PeerChallenge[16];
+ char Reserved[8];
+ char NTResponse[24];
+ char Flags;
+};
+
+extern void chap_Init(struct chap *, struct physical *);
+extern void chap_ReInit(struct chap *);
+extern struct mbuf *chap_Input(struct bundle *, struct link *, struct mbuf *);
diff --git a/usr.sbin/ppp/chap_ms.c b/usr.sbin/ppp/chap_ms.c
new file mode 100644
index 0000000..74ac199
--- /dev/null
+++ b/usr.sbin/ppp/chap_ms.c
@@ -0,0 +1,417 @@
+/*-
+ * Copyright (c) 1997 Gabor Kincses <gabor@acm.org>
+ * 1997 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Eric Rosenquist
+ * Strata Software Limited.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <ctype.h>
+#ifdef __FreeBSD__
+#include <openssl/des.h>
+#include <sha.h>
+#else
+#include <sys/types.h>
+#include <stdlib.h>
+#ifdef __NetBSD__
+#include <openssl/des.h>
+#else
+#include <des.h>
+#endif
+#include <openssl/sha.h>
+#endif
+#include <md4.h>
+#include <string.h>
+
+#include "chap_ms.h"
+
+/*
+ * Documentation & specifications:
+ *
+ * MS-CHAP (CHAP80) rfc2433
+ * MS-CHAP-V2 (CHAP81) rfc2759
+ * MPPE key management draft-ietf-pppext-mppe-keys-02.txt
+ */
+
+static char SHA1_Pad1[40] =
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+static char SHA1_Pad2[40] =
+ {0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
+ 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
+ 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
+ 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2};
+
+/* unused, for documentation only */
+/* only NTResp is filled in for FreeBSD */
+struct MS_ChapResponse {
+ u_char LANManResp[24];
+ u_char NTResp[24];
+ u_char UseNT; /* If 1, ignore the LANMan response field */
+};
+
+static u_char
+Get7Bits(u_char *input, int startBit)
+{
+ register unsigned int word;
+
+ word = (unsigned)input[startBit / 8] << 8;
+ word |= (unsigned)input[startBit / 8 + 1];
+
+ word >>= 15 - (startBit % 8 + 7);
+
+ return word & 0xFE;
+}
+
+/* IN 56 bit DES key missing parity bits
+ OUT 64 bit DES key with parity bits added */
+static void
+MakeKey(u_char *key, u_char *des_key)
+{
+ des_key[0] = Get7Bits(key, 0);
+ des_key[1] = Get7Bits(key, 7);
+ des_key[2] = Get7Bits(key, 14);
+ des_key[3] = Get7Bits(key, 21);
+ des_key[4] = Get7Bits(key, 28);
+ des_key[5] = Get7Bits(key, 35);
+ des_key[6] = Get7Bits(key, 42);
+ des_key[7] = Get7Bits(key, 49);
+
+ des_set_odd_parity((des_cblock *)des_key);
+}
+
+static void /* IN 8 octets IN 7 octest OUT 8 octets */
+DesEncrypt(u_char *clear, u_char *key, u_char *cipher)
+{
+ des_cblock des_key;
+ des_key_schedule key_schedule;
+
+ MakeKey(key, des_key);
+ des_set_key(&des_key, key_schedule);
+ des_ecb_encrypt((des_cblock *)clear, (des_cblock *)cipher, key_schedule, 1);
+}
+
+static void /* IN 8 octets IN 16 octets OUT 24 octets */
+ChallengeResponse(u_char *challenge, u_char *pwHash, u_char *response)
+{
+ char ZPasswordHash[21];
+
+ memset(ZPasswordHash, '\0', sizeof ZPasswordHash);
+ memcpy(ZPasswordHash, pwHash, 16);
+
+ DesEncrypt(challenge, ZPasswordHash + 0, response + 0);
+ DesEncrypt(challenge, ZPasswordHash + 7, response + 8);
+ DesEncrypt(challenge, ZPasswordHash + 14, response + 16);
+}
+
+void
+NtPasswordHash(char *key, int keylen, char *hash)
+{
+ MD4_CTX MD4context;
+
+ MD4Init(&MD4context);
+ MD4Update(&MD4context, key, keylen);
+ MD4Final(hash, &MD4context);
+}
+
+void
+HashNtPasswordHash(char *hash, char *hashhash)
+{
+ MD4_CTX MD4context;
+
+ MD4Init(&MD4context);
+ MD4Update(&MD4context, hash, 16);
+ MD4Final(hashhash, &MD4context);
+}
+
+void
+ChallengeHash(char *PeerChallenge, char *AuthenticatorChallenge,
+ char *UserName, int UserNameLen, char *Challenge)
+{
+ SHA_CTX Context;
+ char Digest[SHA_DIGEST_LENGTH];
+ char *Name;
+
+ Name = strrchr(UserName, '\\');
+ if(NULL == Name)
+ Name = UserName;
+ else
+ Name++;
+
+ SHA1_Init(&Context);
+
+ SHA1_Update(&Context, PeerChallenge, 16);
+ SHA1_Update(&Context, AuthenticatorChallenge, 16);
+ SHA1_Update(&Context, Name, strlen(Name));
+
+ SHA1_Final(Digest, &Context);
+ memcpy(Challenge, Digest, 8);
+}
+
+void
+GenerateNTResponse(char *AuthenticatorChallenge, char *PeerChallenge,
+ char *UserName, int UserNameLen, char *Password,
+ int PasswordLen, char *Response)
+{
+ char Challenge[8];
+ char PasswordHash[16];
+
+ ChallengeHash(PeerChallenge, AuthenticatorChallenge, UserName, UserNameLen,
+ Challenge);
+ NtPasswordHash(Password, PasswordLen, PasswordHash);
+ ChallengeResponse(Challenge, PasswordHash, Response);
+}
+
+#ifndef __FreeBSD__
+#define LENGTH 20
+static char *
+SHA1_End(SHA_CTX *ctx, char *buf)
+{
+ int i;
+ unsigned char digest[LENGTH];
+ static const char hex[]="0123456789abcdef";
+
+ if (!buf)
+ buf = malloc(2*LENGTH + 1);
+ if (!buf)
+ return 0;
+ SHA1_Final(digest, ctx);
+ for (i = 0; i < LENGTH; i++) {
+ buf[i+i] = hex[digest[i] >> 4];
+ buf[i+i+1] = hex[digest[i] & 0x0f];
+ }
+ buf[i+i] = '\0';
+ return buf;
+}
+#endif
+
+void
+GenerateAuthenticatorResponse(char *Password, int PasswordLen,
+ char *NTResponse, char *PeerChallenge,
+ char *AuthenticatorChallenge, char *UserName,
+ int UserNameLen, char *AuthenticatorResponse)
+{
+ SHA_CTX Context;
+ char PasswordHash[16];
+ char PasswordHashHash[16];
+ char Challenge[8];
+ u_char Digest[SHA_DIGEST_LENGTH];
+ int i;
+
+ /*
+ * "Magic" constants used in response generation
+ */
+ char Magic1[39] =
+ {0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
+ 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
+ 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
+ 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74};
+
+
+ char Magic2[41] =
+ {0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
+ 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
+ 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
+ 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
+ 0x6E};
+ /*
+ * Hash the password with MD4
+ */
+ NtPasswordHash(Password, PasswordLen, PasswordHash);
+ /*
+ * Now hash the hash
+ */
+ HashNtPasswordHash(PasswordHash, PasswordHashHash);
+
+ SHA1_Init(&Context);
+ SHA1_Update(&Context, PasswordHashHash, 16);
+ SHA1_Update(&Context, NTResponse, 24);
+ SHA1_Update(&Context, Magic1, 39);
+ SHA1_Final(Digest, &Context);
+ ChallengeHash(PeerChallenge, AuthenticatorChallenge, UserName, UserNameLen,
+ Challenge);
+ SHA1_Init(&Context);
+ SHA1_Update(&Context, Digest, 20);
+ SHA1_Update(&Context, Challenge, 8);
+ SHA1_Update(&Context, Magic2, 41);
+
+ /*
+ * Encode the value of 'Digest' as "S=" followed by
+ * 40 ASCII hexadecimal digits and return it in
+ * AuthenticatorResponse.
+ * For example,
+ * "S=0123456789ABCDEF0123456789ABCDEF01234567"
+ */
+ AuthenticatorResponse[0] = 'S';
+ AuthenticatorResponse[1] = '=';
+ SHA1_End(&Context, AuthenticatorResponse + 2);
+ for (i=2; i<42; i++)
+ AuthenticatorResponse[i] = toupper(AuthenticatorResponse[i]);
+
+}
+
+void
+GetMasterKey(char *PasswordHashHash, char *NTResponse, char *MasterKey)
+{
+ char Digest[SHA_DIGEST_LENGTH];
+ SHA_CTX Context;
+ static char Magic1[27] =
+ {0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
+ 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79};
+
+ SHA1_Init(&Context);
+ SHA1_Update(&Context, PasswordHashHash, 16);
+ SHA1_Update(&Context, NTResponse, 24);
+ SHA1_Update(&Context, Magic1, 27);
+ SHA1_Final(Digest, &Context);
+ memcpy(MasterKey, Digest, 16);
+}
+
+void
+GetAsymetricStartKey(char *MasterKey, char *SessionKey, int SessionKeyLength,
+ int IsSend, int IsServer)
+{
+ char Digest[SHA_DIGEST_LENGTH];
+ SHA_CTX Context;
+ char *s;
+
+ static char Magic2[84] =
+ {0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
+ 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
+ 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
+ 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
+ 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
+ 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
+ 0x6b, 0x65, 0x79, 0x2e};
+
+ static char Magic3[84] =
+ {0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
+ 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
+ 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
+ 0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
+ 0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
+ 0x6b, 0x65, 0x79, 0x2e};
+
+ if (IsSend) {
+ if (IsServer) {
+ s = Magic3;
+ } else {
+ s = Magic2;
+ }
+ } else {
+ if (IsServer) {
+ s = Magic2;
+ } else {
+ s = Magic3;
+ }
+ }
+
+ SHA1_Init(&Context);
+ SHA1_Update(&Context, MasterKey, 16);
+ SHA1_Update(&Context, SHA1_Pad1, 40);
+ SHA1_Update(&Context, s, 84);
+ SHA1_Update(&Context, SHA1_Pad2, 40);
+ SHA1_Final(Digest, &Context);
+
+ memcpy(SessionKey, Digest, SessionKeyLength);
+}
+
+void
+GetNewKeyFromSHA(char *StartKey, char *SessionKey, long SessionKeyLength,
+ char *InterimKey)
+{
+ SHA_CTX Context;
+ char Digest[SHA_DIGEST_LENGTH];
+
+ SHA1_Init(&Context);
+ SHA1_Update(&Context, StartKey, SessionKeyLength);
+ SHA1_Update(&Context, SHA1_Pad1, 40);
+ SHA1_Update(&Context, SessionKey, SessionKeyLength);
+ SHA1_Update(&Context, SHA1_Pad2, 40);
+ SHA1_Final(Digest, &Context);
+
+ memcpy(InterimKey, Digest, SessionKeyLength);
+}
+
+#if 0
+static void
+Get_Key(char *InitialSessionKey, char *CurrentSessionKey,
+ int LengthOfDesiredKey)
+{
+ SHA_CTX Context;
+ char Digest[SHA_DIGEST_LENGTH];
+
+ SHA1_Init(&Context);
+ SHA1_Update(&Context, InitialSessionKey, LengthOfDesiredKey);
+ SHA1_Update(&Context, SHA1_Pad1, 40);
+ SHA1_Update(&Context, CurrentSessionKey, LengthOfDesiredKey);
+ SHA1_Update(&Context, SHA1_Pad2, 40);
+ SHA1_Final(Digest, &Context);
+
+ memcpy(CurrentSessionKey, Digest, LengthOfDesiredKey);
+}
+#endif
+
+/* passwordHash 16-bytes MD4 hashed password
+ challenge 8-bytes peer CHAP challenge
+ since passwordHash is in a 24-byte buffer, response is written in there */
+void
+mschap_NT(char *passwordHash, char *challenge)
+{
+ u_char response[24];
+
+ ChallengeResponse(challenge, passwordHash, response);
+ memcpy(passwordHash, response, 24);
+ passwordHash[24] = 1; /* NT-style response */
+}
+
+void
+mschap_LANMan(char *digest, char *challenge, char *secret)
+{
+ static u_char salt[] = "KGS!@#$%"; /* RASAPI32.dll */
+ char SECRET[14], *ptr, *end;
+ u_char hash[16];
+
+ end = SECRET + sizeof SECRET;
+ for (ptr = SECRET; *secret && ptr < end; ptr++, secret++)
+ *ptr = toupper(*secret);
+ if (ptr < end)
+ memset(ptr, '\0', end - ptr);
+
+ DesEncrypt(salt, SECRET, hash);
+ DesEncrypt(salt, SECRET + 7, hash + 8);
+
+ ChallengeResponse(challenge, hash, digest);
+}
diff --git a/usr.sbin/ppp/chap_ms.h b/usr.sbin/ppp/chap_ms.h
new file mode 100644
index 0000000..35ee978
--- /dev/null
+++ b/usr.sbin/ppp/chap_ms.h
@@ -0,0 +1,53 @@
+/*-
+ * Copyright (c) 1997 Gabor Kincses <gabor@acm.org>
+ * 1997 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Eric Rosenquist
+ * Strata Software Limited.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/* Max # of (Unicode) chars in an NT password */
+#define MAX_NT_PASSWORD 256
+
+/* Don't rely on sizeof(MS_ChapResponse) in case of struct padding */
+#define MS_CHAP_RESPONSE_LEN 49
+#define CHAP81_RESPONSE_LEN 49
+#define CHAP81_NTRESPONSE_LEN 24
+#define CHAP81_NTRESPONSE_OFF 24
+#define CHAP81_HASH_LEN 16
+#define CHAP81_AUTHRESPONSE_LEN 42
+#define CHAP81_CHALLENGE_LEN 16
+
+extern void mschap_NT(char *, char *);
+extern void mschap_LANMan(char *, char *, char *);
+extern void GenerateNTResponse(char *, char *, char *, int, char *, int , char *);
+extern void HashNtPasswordHash(char *, char *);
+extern void NtPasswordHash(char *, int, char *);
+extern void ChallengeHash(char *, char *, char *UserName, int, char *);
+extern void GenerateAuthenticatorResponse(char *, int, char *, char *, char *, char *, int, char *);
+extern void GetAsymetricStartKey(char *, char *, int, int, int);
+extern void GetMasterKey(char *, char *, char *);
+extern void GetNewKeyFromSHA(char *, char *, long, char *);
diff --git a/usr.sbin/ppp/chat.c b/usr.sbin/ppp/chat.c
new file mode 100644
index 0000000..79e41ea
--- /dev/null
+++ b/usr.sbin/ppp/chat.c
@@ -0,0 +1,794 @@
+/*-
+ * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "layer.h"
+#include "mbuf.h"
+#include "log.h"
+#include "defs.h"
+#include "timer.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "throughput.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "async.h"
+#include "descriptor.h"
+#include "physical.h"
+#include "chat.h"
+#include "mp.h"
+#include "auth.h"
+#include "chap.h"
+#include "slcompress.h"
+#include "iplist.h"
+#include "ncpaddr.h"
+#include "ipcp.h"
+#include "filter.h"
+#include "cbcp.h"
+#include "command.h"
+#include "datalink.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+#include "id.h"
+
+#define BUFLEFT(c) (sizeof (c)->buf - ((c)->bufend - (c)->buf))
+
+static void ExecStr(struct physical *, char *, char *, int);
+static char *ExpandString(struct chat *, const char *, char *, int, int);
+
+static void
+chat_PauseTimer(void *v)
+{
+ struct chat *c = (struct chat *)v;
+ timer_Stop(&c->pause);
+ c->pause.load = 0;
+}
+
+static void
+chat_Pause(struct chat *c, u_long load)
+{
+ timer_Stop(&c->pause);
+ c->pause.load += load;
+ c->pause.func = chat_PauseTimer;
+ c->pause.name = "chat pause";
+ c->pause.arg = c;
+ timer_Start(&c->pause);
+}
+
+static void
+chat_TimeoutTimer(void *v)
+{
+ struct chat *c = (struct chat *)v;
+ timer_Stop(&c->timeout);
+ c->TimedOut = 1;
+}
+
+static void
+chat_SetTimeout(struct chat *c)
+{
+ timer_Stop(&c->timeout);
+ if (c->TimeoutSec > 0) {
+ c->timeout.load = SECTICKS * c->TimeoutSec;
+ c->timeout.func = chat_TimeoutTimer;
+ c->timeout.name = "chat timeout";
+ c->timeout.arg = c;
+ timer_Start(&c->timeout);
+ }
+}
+
+static char *
+chat_NextChar(char *ptr, char ch)
+{
+ for (; *ptr; ptr++)
+ if (*ptr == ch)
+ return ptr;
+ else if (*ptr == '\\')
+ if (*++ptr == '\0')
+ return NULL;
+
+ return NULL;
+}
+
+static int
+chat_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n)
+{
+ struct chat *c = descriptor2chat(d);
+ int special, gotabort, gottimeout, needcr;
+ int TimedOut = c->TimedOut;
+ static char arg_term; /* An empty string */
+
+ if (c->pause.state == TIMER_RUNNING)
+ return 0;
+
+ if (TimedOut) {
+ log_Printf(LogCHAT, "Expect timeout\n");
+ if (c->nargptr == NULL)
+ c->state = CHAT_FAILED;
+ else {
+ /* c->state = CHAT_EXPECT; */
+ c->argptr = &arg_term;
+ }
+ c->TimedOut = 0;
+ }
+
+ if (c->state != CHAT_EXPECT && c->state != CHAT_SEND)
+ return 0;
+
+ gottimeout = gotabort = 0;
+
+ if (c->arg < c->argc && (c->arg < 0 || *c->argptr == '\0')) {
+ /* Go get the next string */
+ if (c->arg < 0 || c->state == CHAT_SEND)
+ c->state = CHAT_EXPECT;
+ else
+ c->state = CHAT_SEND;
+
+ special = 1;
+ while (special && (c->nargptr || c->arg < c->argc - 1)) {
+ if (c->arg < 0 || (!TimedOut && c->state == CHAT_SEND))
+ c->nargptr = NULL;
+
+ if (c->nargptr != NULL) {
+ /* We're doing expect-send-expect.... */
+ c->argptr = c->nargptr;
+ /* Put the '-' back in case we ever want to rerun our script */
+ c->nargptr[-1] = '-';
+ c->nargptr = chat_NextChar(c->nargptr, '-');
+ if (c->nargptr != NULL)
+ *c->nargptr++ = '\0';
+ } else {
+ int minus;
+
+ if ((c->argptr = c->argv[++c->arg]) == NULL) {
+ /* End of script - all ok */
+ c->state = CHAT_DONE;
+ return 0;
+ }
+
+ if (c->state == CHAT_EXPECT) {
+ /* Look for expect-send-expect sequence */
+ c->nargptr = c->argptr;
+ minus = 0;
+ while ((c->nargptr = chat_NextChar(c->nargptr, '-'))) {
+ c->nargptr++;
+ minus++;
+ }
+
+ if (minus % 2)
+ log_Printf(LogWARN, "chat_UpdateSet: \"%s\": Uneven number of"
+ " '-' chars, all ignored\n", c->argptr);
+ else if (minus) {
+ c->nargptr = chat_NextChar(c->argptr, '-');
+ *c->nargptr++ = '\0';
+ }
+ }
+ }
+
+ /*
+ * c->argptr now temporarily points into c->script (via c->argv)
+ * If it's an expect-send-expect sequence, we've just got the correct
+ * portion of that sequence.
+ */
+
+ needcr = c->state == CHAT_SEND &&
+ (*c->argptr != '!' || c->argptr[1] == '!');
+
+ /* We leave room for a potential HDLC header in the target string */
+ ExpandString(c, c->argptr, c->exp + 2, sizeof c->exp - 2, needcr);
+
+ /*
+ * Now read our string. If it's not a special string, we unset
+ * ``special'' to break out of the loop.
+ */
+ if (gotabort) {
+ if (c->abort.num < MAXABORTS) {
+ int len, i;
+
+ len = strlen(c->exp+2);
+ for (i = 0; i < c->abort.num; i++)
+ if (len > c->abort.string[i].len) {
+ int last;
+
+ for (last = c->abort.num; last > i; last--) {
+ c->abort.string[last].data = c->abort.string[last-1].data;
+ c->abort.string[last].len = c->abort.string[last-1].len;
+ }
+ break;
+ }
+ c->abort.string[i].len = len;
+ c->abort.string[i].data = (char *)malloc(len+1);
+ memcpy(c->abort.string[i].data, c->exp+2, len+1);
+ c->abort.num++;
+ } else
+ log_Printf(LogERROR, "chat_UpdateSet: too many abort strings\n");
+ gotabort = 0;
+ } else if (gottimeout) {
+ c->TimeoutSec = atoi(c->exp + 2);
+ if (c->TimeoutSec <= 0)
+ c->TimeoutSec = 30;
+ gottimeout = 0;
+ } else if (c->nargptr == NULL && !strcmp(c->exp+2, "ABORT"))
+ gotabort = 1;
+ else if (c->nargptr == NULL && !strcmp(c->exp+2, "TIMEOUT"))
+ gottimeout = 1;
+ else {
+ if (c->exp[2] == '!' && c->exp[3] != '!')
+ ExecStr(c->physical, c->exp + 3, c->exp + 3, sizeof c->exp - 3);
+
+ if (c->exp[2] == '\0') {
+ /* Empty string, reparse (this may be better as a `goto start') */
+ c->argptr = &arg_term;
+ return chat_UpdateSet(d, r, w, e, n);
+ }
+
+ special = 0;
+ }
+ }
+
+ if (special) {
+ if (gottimeout)
+ log_Printf(LogWARN, "chat_UpdateSet: TIMEOUT: Argument expected\n");
+ else if (gotabort)
+ log_Printf(LogWARN, "chat_UpdateSet: ABORT: Argument expected\n");
+
+ /* End of script - all ok */
+ c->state = CHAT_DONE;
+ return 0;
+ }
+
+ /* set c->argptr to point in the right place */
+ c->argptr = c->exp + (c->exp[2] == '!' ? 3 : 2);
+ c->arglen = strlen(c->argptr);
+
+ if (c->state == CHAT_EXPECT) {
+ /* We must check to see if the string's already been found ! */
+ char *begin, *end;
+
+ end = c->bufend - c->arglen + 1;
+ if (end < c->bufstart)
+ end = c->bufstart;
+ for (begin = c->bufstart; begin < end; begin++)
+ if (!strncmp(begin, c->argptr, c->arglen)) {
+ c->bufstart = begin + c->arglen;
+ c->argptr += c->arglen;
+ c->arglen = 0;
+ /* Continue - we've already read our expect string */
+ return chat_UpdateSet(d, r, w, e, n);
+ }
+
+ log_Printf(LogCHAT, "Expect(%d): %s\n", c->TimeoutSec, c->argptr);
+ chat_SetTimeout(c);
+ }
+ }
+
+ /*
+ * We now have c->argptr pointing at what we want to expect/send and
+ * c->state saying what we want to do... we now know what to put in
+ * the fd_set :-)
+ */
+
+ if (c->state == CHAT_EXPECT)
+ return physical_doUpdateSet(&c->physical->desc, r, NULL, e, n, 1);
+ else
+ return physical_doUpdateSet(&c->physical->desc, NULL, w, e, n, 1);
+}
+
+static int
+chat_IsSet(struct fdescriptor *d, const fd_set *fdset)
+{
+ struct chat *c = descriptor2chat(d);
+ return c->argptr && physical_IsSet(&c->physical->desc, fdset);
+}
+
+static void
+chat_UpdateLog(struct chat *c, int in)
+{
+ if (log_IsKept(LogCHAT) || log_IsKept(LogCONNECT)) {
+ /*
+ * If a linefeed appears in the last `in' characters of `c's input
+ * buffer, output from there, all the way back to the last linefeed.
+ * This is called for every read of `in' bytes.
+ */
+ char *ptr, *end, *stop, ch;
+ int level;
+
+ level = log_IsKept(LogCHAT) ? LogCHAT : LogCONNECT;
+ if (in == -1)
+ end = ptr = c->bufend;
+ else {
+ ptr = c->bufend - in;
+ for (end = c->bufend - 1; end >= ptr; end--)
+ if (*end == '\n')
+ break;
+ }
+
+ if (end >= ptr) {
+ for (ptr = c->bufend - (in == -1 ? 1 : in + 1); ptr >= c->bufstart; ptr--)
+ if (*ptr == '\n')
+ break;
+ ptr++;
+ stop = NULL;
+ while (stop < end) {
+ if ((stop = memchr(ptr, '\n', end - ptr)) == NULL)
+ stop = end;
+ ch = *stop;
+ *stop = '\0';
+ if (level == LogCHAT || strstr(ptr, "CONNECT"))
+ log_Printf(level, "Received: %s\n", ptr);
+ *stop = ch;
+ ptr = stop + 1;
+ }
+ }
+ }
+}
+
+static void
+chat_Read(struct fdescriptor *d, struct bundle *bundle, const fd_set *fdset)
+{
+ struct chat *c = descriptor2chat(d);
+
+ if (c->state == CHAT_EXPECT) {
+ ssize_t in;
+ char *abegin, *ebegin, *begin, *aend, *eend, *end;
+ int n;
+
+ /*
+ * XXX - should this read only 1 byte to guarantee that we don't
+ * swallow any ppp talk from the peer ?
+ */
+ in = BUFLEFT(c);
+ if (in > sizeof c->buf / 2)
+ in = sizeof c->buf / 2;
+
+ in = physical_Read(c->physical, c->bufend, in);
+ if (in <= 0)
+ return;
+
+ /* `begin' and `end' delimit where we're going to strncmp() from */
+ ebegin = c->bufend - c->arglen + 1;
+ eend = ebegin + in;
+ if (ebegin < c->bufstart)
+ ebegin = c->bufstart;
+
+ if (c->abort.num) {
+ abegin = c->bufend - c->abort.string[0].len + 1;
+ aend = c->bufend - c->abort.string[c->abort.num-1].len + in + 1;
+ if (abegin < c->bufstart)
+ abegin = c->bufstart;
+ } else {
+ abegin = ebegin;
+ aend = eend;
+ }
+ begin = abegin < ebegin ? abegin : ebegin;
+ end = aend < eend ? eend : aend;
+
+ c->bufend += in;
+
+ chat_UpdateLog(c, in);
+
+ if (c->bufend > c->buf + sizeof c->buf / 2) {
+ /* Shuffle our receive buffer back a bit */
+ int chop;
+
+ for (chop = begin - c->buf; chop; chop--)
+ if (c->buf[chop] == '\n')
+ /* found some already-logged garbage to remove :-) */
+ break;
+
+ if (!chop)
+ chop = begin - c->buf;
+
+ if (chop) {
+ char *from, *to;
+
+ to = c->buf;
+ from = to + chop;
+ while (from < c->bufend)
+ *to++ = *from++;
+ c->bufstart -= chop;
+ c->bufend -= chop;
+ begin -= chop;
+ end -= chop;
+ abegin -= chop;
+ aend -= chop;
+ ebegin -= chop;
+ eend -= chop;
+ }
+ }
+
+ for (; begin < end; begin++)
+ if (begin >= ebegin && begin < eend &&
+ !strncmp(begin, c->argptr, c->arglen)) {
+ /* Got it ! */
+ timer_Stop(&c->timeout);
+ if (memchr(begin + c->arglen - 1, '\n',
+ c->bufend - begin - c->arglen + 1) == NULL) {
+ /* force it into the log */
+ end = c->bufend;
+ c->bufend = begin + c->arglen;
+ chat_UpdateLog(c, -1);
+ c->bufend = end;
+ }
+ c->bufstart = begin + c->arglen;
+ c->argptr += c->arglen;
+ c->arglen = 0;
+ break;
+ } else if (begin >= abegin && begin < aend) {
+ for (n = c->abort.num - 1; n >= 0; n--) {
+ if (begin + c->abort.string[n].len > c->bufend)
+ break;
+ if (!strncmp(begin, c->abort.string[n].data,
+ c->abort.string[n].len)) {
+ if (memchr(begin + c->abort.string[n].len - 1, '\n',
+ c->bufend - begin - c->abort.string[n].len + 1) == NULL) {
+ /* force it into the log */
+ end = c->bufend;
+ c->bufend = begin + c->abort.string[n].len;
+ chat_UpdateLog(c, -1);
+ c->bufend = end;
+ }
+ c->bufstart = begin + c->abort.string[n].len;
+ c->state = CHAT_FAILED;
+ return;
+ }
+ }
+ }
+ }
+}
+
+static int
+chat_Write(struct fdescriptor *d, struct bundle *bundle, const fd_set *fdset)
+{
+ struct chat *c = descriptor2chat(d);
+ int result = 0;
+
+ if (c->state == CHAT_SEND) {
+ int wrote;
+
+ if (strstr(c->argv[c->arg], "\\P")) /* Don't log the password */
+ log_Printf(LogCHAT, "Send: %s\n", c->argv[c->arg]);
+ else {
+ int sz;
+
+ sz = c->arglen - 1;
+ while (sz >= 0 && c->argptr[sz] == '\n')
+ sz--;
+ log_Printf(LogCHAT, "Send: %.*s\n", sz + 1, c->argptr);
+ }
+
+ if (physical_IsSync(c->physical)) {
+ /*
+ * XXX: Fix me
+ * This data should be stuffed down through the link layers
+ */
+ /* There's always room for the HDLC header */
+ c->argptr -= 2;
+ c->arglen += 2;
+ memcpy(c->argptr, "\377\003", 2); /* Prepend HDLC header */
+ }
+
+ wrote = physical_Write(c->physical, c->argptr, c->arglen);
+ result = wrote > 0 ? 1 : 0;
+ if (wrote == -1) {
+ if (errno != EINTR) {
+ log_Printf(LogWARN, "chat_Write: %s\n", strerror(errno));
+ result = -1;
+ }
+ if (physical_IsSync(c->physical)) {
+ c->argptr += 2;
+ c->arglen -= 2;
+ }
+ } else if (wrote < 2 && physical_IsSync(c->physical)) {
+ /* Oops - didn't even write our HDLC header ! */
+ c->argptr += 2;
+ c->arglen -= 2;
+ } else {
+ c->argptr += wrote;
+ c->arglen -= wrote;
+ }
+ }
+
+ return result;
+}
+
+void
+chat_Init(struct chat *c, struct physical *p)
+{
+ c->desc.type = CHAT_DESCRIPTOR;
+ c->desc.UpdateSet = chat_UpdateSet;
+ c->desc.IsSet = chat_IsSet;
+ c->desc.Read = chat_Read;
+ c->desc.Write = chat_Write;
+ c->physical = p;
+ *c->script = '\0';
+ c->argc = 0;
+ c->arg = -1;
+ c->argptr = NULL;
+ c->nargptr = NULL;
+ c->bufstart = c->bufend = c->buf;
+
+ memset(&c->pause, '\0', sizeof c->pause);
+ memset(&c->timeout, '\0', sizeof c->timeout);
+}
+
+int
+chat_Setup(struct chat *c, const char *data, const char *phone)
+{
+ c->state = CHAT_EXPECT;
+
+ if (data == NULL) {
+ *c->script = '\0';
+ c->argc = 0;
+ } else {
+ strncpy(c->script, data, sizeof c->script - 1);
+ c->script[sizeof c->script - 1] = '\0';
+ c->argc = MakeArgs(c->script, c->argv, VECSIZE(c->argv), PARSE_NOHASH);
+ }
+
+ c->arg = -1;
+ c->argptr = NULL;
+ c->nargptr = NULL;
+
+ c->TimeoutSec = 30;
+ c->TimedOut = 0;
+ c->phone = phone;
+ c->abort.num = 0;
+
+ timer_Stop(&c->pause);
+ timer_Stop(&c->timeout);
+
+ return c->argc >= 0;
+}
+
+void
+chat_Finish(struct chat *c)
+{
+ timer_Stop(&c->pause);
+ timer_Stop(&c->timeout);
+ while (c->abort.num)
+ free(c->abort.string[--c->abort.num].data);
+ c->abort.num = 0;
+}
+
+void
+chat_Destroy(struct chat *c)
+{
+ chat_Finish(c);
+}
+
+/*
+ * \c don't add a cr
+ * \d Sleep a little (delay 2 seconds
+ * \n Line feed character
+ * \P Auth Key password
+ * \p pause 0.25 sec
+ * \r Carrige return character
+ * \s Space character
+ * \T Telephone number(s) (defined via `set phone')
+ * \t Tab character
+ * \U Auth User
+ */
+static char *
+ExpandString(struct chat *c, const char *str, char *result, int reslen, int cr)
+{
+ int len;
+
+ result[--reslen] = '\0';
+ while (*str && reslen > 0) {
+ switch (*str) {
+ case '\\':
+ str++;
+ switch (*str) {
+ case 'c':
+ cr = 0;
+ break;
+ case 'd': /* Delay 2 seconds */
+ chat_Pause(c, 2 * SECTICKS);
+ break;
+ case 'p':
+ chat_Pause(c, SECTICKS / 4);
+ break; /* Delay 0.25 seconds */
+ case 'n':
+ *result++ = '\n';
+ reslen--;
+ break;
+ case 'r':
+ *result++ = '\r';
+ reslen--;
+ break;
+ case 's':
+ *result++ = ' ';
+ reslen--;
+ break;
+ case 't':
+ *result++ = '\t';
+ reslen--;
+ break;
+ case 'P':
+ strncpy(result, c->physical->dl->bundle->cfg.auth.key, reslen);
+ len = strlen(result);
+ reslen -= len;
+ result += len;
+ break;
+ case 'T':
+ if (c->phone) {
+ strncpy(result, c->phone, reslen);
+ len = strlen(result);
+ reslen -= len;
+ result += len;
+ }
+ break;
+ case 'U':
+ strncpy(result, c->physical->dl->bundle->cfg.auth.name, reslen);
+ len = strlen(result);
+ reslen -= len;
+ result += len;
+ break;
+ default:
+ reslen--;
+ *result++ = *str;
+ break;
+ }
+ if (*str)
+ str++;
+ break;
+ case '^':
+ str++;
+ if (*str) {
+ *result++ = *str++ & 0x1f;
+ reslen--;
+ }
+ break;
+ default:
+ *result++ = *str++;
+ reslen--;
+ break;
+ }
+ }
+ if (--reslen > 0) {
+ if (cr)
+ *result++ = '\r';
+ }
+ if (--reslen > 0)
+ *result++ = '\0';
+ return (result);
+}
+
+static void
+ExecStr(struct physical *physical, char *command, char *out, int olen)
+{
+ pid_t pid;
+ int fids[2];
+ char *argv[MAXARGS], *vector[MAXARGS], *startout, *endout;
+ int stat, nb, argc, i;
+
+ log_Printf(LogCHAT, "Exec: %s\n", command);
+ if ((argc = MakeArgs(command, vector, VECSIZE(vector),
+ PARSE_REDUCE|PARSE_NOHASH)) <= 0) {
+ if (argc < 0)
+ log_Printf(LogWARN, "Syntax error in exec command\n");
+ *out = '\0';
+ return;
+ }
+
+ if (pipe(fids) < 0) {
+ log_Printf(LogCHAT, "Unable to create pipe in ExecStr: %s\n",
+ strerror(errno));
+ *out = '\0';
+ return;
+ }
+ if ((pid = fork()) == 0) {
+ command_Expand(argv, argc, (char const *const *)vector,
+ physical->dl->bundle, 0, getpid());
+ close(fids[0]);
+ timer_TermService();
+ if (fids[1] == STDIN_FILENO)
+ fids[1] = dup(fids[1]);
+ dup2(physical->fd, STDIN_FILENO);
+ dup2(fids[1], STDERR_FILENO);
+ dup2(STDIN_FILENO, STDOUT_FILENO);
+ close(3);
+ if (open(_PATH_TTY, O_RDWR) != 3)
+ open(_PATH_DEVNULL, O_RDWR); /* Leave it closed if it fails... */
+ for (i = getdtablesize(); i > 3; i--)
+ fcntl(i, F_SETFD, 1);
+#ifndef NOSUID
+ setuid(ID0realuid());
+#endif
+ execvp(argv[0], argv);
+ fprintf(stderr, "execvp: %s: %s\n", argv[0], strerror(errno));
+ _exit(127);
+ } else {
+ char *name = strdup(vector[0]);
+
+ close(fids[1]);
+ endout = out + olen - 1;
+ startout = out;
+ while (out < endout) {
+ nb = read(fids[0], out, 1);
+ if (nb <= 0)
+ break;
+ out++;
+ }
+ *out = '\0';
+ close(fids[0]);
+ close(fids[1]);
+ waitpid(pid, &stat, WNOHANG);
+ if (WIFSIGNALED(stat)) {
+ log_Printf(LogWARN, "%s: signal %d\n", name, WTERMSIG(stat));
+ free(name);
+ *out = '\0';
+ return;
+ } else if (WIFEXITED(stat)) {
+ switch (WEXITSTATUS(stat)) {
+ case 0:
+ free(name);
+ break;
+ case 127:
+ log_Printf(LogWARN, "%s: %s\n", name, startout);
+ free(name);
+ *out = '\0';
+ return;
+ break;
+ default:
+ log_Printf(LogWARN, "%s: exit %d\n", name, WEXITSTATUS(stat));
+ free(name);
+ *out = '\0';
+ return;
+ break;
+ }
+ } else {
+ log_Printf(LogWARN, "%s: Unexpected exit result\n", name);
+ free(name);
+ *out = '\0';
+ return;
+ }
+ }
+}
diff --git a/usr.sbin/ppp/chat.h b/usr.sbin/ppp/chat.h
new file mode 100644
index 0000000..b8162bf
--- /dev/null
+++ b/usr.sbin/ppp/chat.h
@@ -0,0 +1,82 @@
+/*-
+ * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define CHAT_EXPECT 0
+#define CHAT_SEND 1
+#define CHAT_DONE 2
+#define CHAT_FAILED 3
+
+#define MAXABORTS 50
+
+struct physical;
+
+struct chat {
+ struct fdescriptor desc;
+ struct physical *physical;
+
+ int state; /* Our CHAT_* status */
+
+ char script[LINE_LEN]; /* Our arg buffer */
+ char *argv[MAXARGS]; /* Our arguments (pointing to script) */
+ int argc; /* Number of argv's */
+
+ int arg; /* Our current arg number */
+ char exp[LINE_LEN]; /* Our translated current argument */
+ char *argptr; /* Our current arg pointer */
+ int arglen; /* The length of argptr */
+ char *nargptr; /* Our next for expect-send-expect */
+
+ char buf[LINE_LEN*2]; /* Our input */
+ char *bufstart; /* start of relevent data */
+ char *bufend; /* end of relevent data */
+
+ int TimeoutSec; /* Expect timeout value */
+ int TimedOut; /* We timed out */
+
+ const char *phone; /* Our phone number */
+
+ struct {
+ struct {
+ char *data; /* Abort the dial if we get one */
+ int len;
+ } string[MAXABORTS];
+ int num; /* How many AbortStrings */
+ } abort;
+
+ struct pppTimer pause; /* Inactivity timer */
+ struct pppTimer timeout; /* TimeoutSec timer */
+};
+
+#define descriptor2chat(d) \
+ ((d)->type == CHAT_DESCRIPTOR ? (struct chat *)(d) : NULL)
+#define VECSIZE(v) (sizeof(v) / sizeof(v[0]))
+
+extern void chat_Init(struct chat *, struct physical *);
+extern int chat_Setup(struct chat *, const char *, const char *);
+extern void chat_Finish(struct chat *);
+extern void chat_Destroy(struct chat *);
diff --git a/usr.sbin/ppp/command.c b/usr.sbin/ppp/command.c
new file mode 100644
index 0000000..cf12305
--- /dev/null
+++ b/usr.sbin/ppp/command.c
@@ -0,0 +1,3160 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <net/route.h>
+#include <netdb.h>
+#include <sys/un.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <termios.h>
+#include <unistd.h>
+
+#ifndef NONAT
+#ifdef LOCALNAT
+#include "alias.h"
+#else
+#include <alias.h>
+#endif
+#endif
+
+#include "layer.h"
+#include "defs.h"
+#include "command.h"
+#include "mbuf.h"
+#include "log.h"
+#include "timer.h"
+#include "fsm.h"
+#include "iplist.h"
+#include "throughput.h"
+#include "slcompress.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "lcp.h"
+#include "ncpaddr.h"
+#include "ipcp.h"
+#ifndef NONAT
+#include "nat_cmd.h"
+#endif
+#include "systems.h"
+#include "filter.h"
+#include "descriptor.h"
+#include "main.h"
+#include "route.h"
+#include "ccp.h"
+#include "auth.h"
+#include "async.h"
+#include "link.h"
+#include "physical.h"
+#include "mp.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+#include "server.h"
+#include "prompt.h"
+#include "chat.h"
+#include "chap.h"
+#include "cbcp.h"
+#include "datalink.h"
+#include "iface.h"
+#include "id.h"
+#include "probe.h"
+
+/* ``set'' values */
+#define VAR_AUTHKEY 0
+#define VAR_DIAL 1
+#define VAR_LOGIN 2
+#define VAR_AUTHNAME 3
+#define VAR_AUTOLOAD 4
+#define VAR_WINSIZE 5
+#define VAR_DEVICE 6
+#define VAR_ACCMAP 7
+#define VAR_MRRU 8
+#define VAR_MRU 9
+#define VAR_MTU 10
+#define VAR_OPENMODE 11
+#define VAR_PHONE 12
+#define VAR_HANGUP 13
+#define VAR_IDLETIMEOUT 14
+#define VAR_LQRPERIOD 15
+#define VAR_LCPRETRY 16
+#define VAR_CHAPRETRY 17
+#define VAR_PAPRETRY 18
+#define VAR_CCPRETRY 19
+#define VAR_IPCPRETRY 20
+#define VAR_DNS 21
+#define VAR_NBNS 22
+#define VAR_MODE 23
+#define VAR_CALLBACK 24
+#define VAR_CBCP 25
+#define VAR_CHOKED 26
+#define VAR_SENDPIPE 27
+#define VAR_RECVPIPE 28
+#define VAR_RADIUS 29
+#define VAR_CD 30
+#define VAR_PARITY 31
+#define VAR_CRTSCTS 32
+#define VAR_URGENTPORTS 33
+#define VAR_LOGOUT 34
+#define VAR_IFQUEUE 35
+#define VAR_MPPE 36
+
+/* ``accept|deny|disable|enable'' masks */
+#define NEG_HISMASK (1)
+#define NEG_MYMASK (2)
+
+/* ``accept|deny|disable|enable'' values */
+#define NEG_ACFCOMP 40
+#define NEG_CHAP05 41
+#define NEG_CHAP80 42
+#define NEG_CHAP80LM 43
+#define NEG_DEFLATE 44
+#define NEG_DNS 45
+#define NEG_ENDDISC 46
+#define NEG_LQR 47
+#define NEG_PAP 48
+#define NEG_PPPDDEFLATE 49
+#define NEG_PRED1 50
+#define NEG_PROTOCOMP 51
+#define NEG_SHORTSEQ 52
+#define NEG_VJCOMP 53
+#define NEG_MPPE 54
+#define NEG_CHAP81 55
+
+const char Version[] = "3.1";
+
+static int ShowCommand(struct cmdargs const *);
+static int TerminalCommand(struct cmdargs const *);
+static int QuitCommand(struct cmdargs const *);
+static int OpenCommand(struct cmdargs const *);
+static int CloseCommand(struct cmdargs const *);
+static int DownCommand(struct cmdargs const *);
+static int SetCommand(struct cmdargs const *);
+static int LinkCommand(struct cmdargs const *);
+static int AddCommand(struct cmdargs const *);
+static int DeleteCommand(struct cmdargs const *);
+static int NegotiateCommand(struct cmdargs const *);
+static int ClearCommand(struct cmdargs const *);
+static int RunListCommand(struct cmdargs const *);
+static int IfaceAddCommand(struct cmdargs const *);
+static int IfaceDeleteCommand(struct cmdargs const *);
+static int IfaceClearCommand(struct cmdargs const *);
+static int SetProcTitle(struct cmdargs const *);
+#ifndef NONAT
+static int NatEnable(struct cmdargs const *);
+static int NatOption(struct cmdargs const *);
+#endif
+
+static const char *
+showcx(struct cmdtab const *cmd)
+{
+ if (cmd->lauth & LOCAL_CX)
+ return "(c)";
+ else if (cmd->lauth & LOCAL_CX_OPT)
+ return "(o)";
+
+ return "";
+}
+
+static int
+HelpCommand(struct cmdargs const *arg)
+{
+ struct cmdtab const *cmd;
+ int n, cmax, dmax, cols, cxlen;
+ const char *cx;
+
+ if (!arg->prompt) {
+ log_Printf(LogWARN, "help: Cannot help without a prompt\n");
+ return 0;
+ }
+
+ if (arg->argc > arg->argn) {
+ for (cmd = arg->cmdtab; cmd->name || cmd->alias; cmd++)
+ if ((cmd->lauth & arg->prompt->auth) &&
+ ((cmd->name && !strcasecmp(cmd->name, arg->argv[arg->argn])) ||
+ (cmd->alias && !strcasecmp(cmd->alias, arg->argv[arg->argn])))) {
+ prompt_Printf(arg->prompt, "%s %s\n", cmd->syntax, showcx(cmd));
+ return 0;
+ }
+ return -1;
+ }
+
+ cmax = dmax = 0;
+ for (cmd = arg->cmdtab; cmd->func; cmd++)
+ if (cmd->name && (cmd->lauth & arg->prompt->auth)) {
+ if ((n = strlen(cmd->name) + strlen(showcx(cmd))) > cmax)
+ cmax = n;
+ if ((n = strlen(cmd->helpmes)) > dmax)
+ dmax = n;
+ }
+
+ cols = 80 / (dmax + cmax + 3);
+ n = 0;
+ prompt_Printf(arg->prompt, "(o) = Optional context,"
+ " (c) = Context required\n");
+ for (cmd = arg->cmdtab; cmd->func; cmd++)
+ if (cmd->name && (cmd->lauth & arg->prompt->auth)) {
+ cx = showcx(cmd);
+ cxlen = cmax - strlen(cmd->name);
+ if (n % cols != 0)
+ prompt_Printf(arg->prompt, " ");
+ prompt_Printf(arg->prompt, "%s%-*.*s: %-*.*s",
+ cmd->name, cxlen, cxlen, cx, dmax, dmax, cmd->helpmes);
+ if (++n % cols == 0)
+ prompt_Printf(arg->prompt, "\n");
+ }
+ if (n % cols != 0)
+ prompt_Printf(arg->prompt, "\n");
+
+ return 0;
+}
+
+static int
+IdentCommand(struct cmdargs const *arg)
+{
+ Concatinate(arg->cx->physical->link.lcp.cfg.ident,
+ sizeof arg->cx->physical->link.lcp.cfg.ident,
+ arg->argc - arg->argn, arg->argv + arg->argn);
+ return 0;
+}
+
+static int
+SendIdentification(struct cmdargs const *arg)
+{
+ if (arg->cx->state < DATALINK_LCP) {
+ log_Printf(LogWARN, "sendident: link has not reached LCP\n");
+ return 2;
+ }
+ return lcp_SendIdentification(&arg->cx->physical->link.lcp) ? 0 : 1;
+}
+
+static int
+CloneCommand(struct cmdargs const *arg)
+{
+ char namelist[LINE_LEN];
+ char *name;
+ int f;
+
+ if (arg->argc == arg->argn)
+ return -1;
+
+ namelist[sizeof namelist - 1] = '\0';
+ for (f = arg->argn; f < arg->argc; f++) {
+ strncpy(namelist, arg->argv[f], sizeof namelist - 1);
+ for(name = strtok(namelist, ", "); name; name = strtok(NULL,", "))
+ bundle_DatalinkClone(arg->bundle, arg->cx, name);
+ }
+
+ return 0;
+}
+
+static int
+RemoveCommand(struct cmdargs const *arg)
+{
+ if (arg->argc != arg->argn)
+ return -1;
+
+ if (arg->cx->state != DATALINK_CLOSED) {
+ log_Printf(LogWARN, "remove: Cannot delete links that aren't closed\n");
+ return 2;
+ }
+
+ bundle_DatalinkRemove(arg->bundle, arg->cx);
+ return 0;
+}
+
+static int
+RenameCommand(struct cmdargs const *arg)
+{
+ if (arg->argc != arg->argn + 1)
+ return -1;
+
+ if (bundle_RenameDatalink(arg->bundle, arg->cx, arg->argv[arg->argn]))
+ return 0;
+
+ log_Printf(LogWARN, "%s -> %s: target name already exists\n",
+ arg->cx->name, arg->argv[arg->argn]);
+ return 1;
+}
+
+static int
+LoadCommand(struct cmdargs const *arg)
+{
+ const char *err;
+ int n, mode;
+
+ mode = arg->bundle->phys_type.all;
+
+ if (arg->argn < arg->argc) {
+ for (n = arg->argn; n < arg->argc; n++)
+ if ((err = system_IsValid(arg->argv[n], arg->prompt, mode)) != NULL) {
+ log_Printf(LogWARN, "%s: %s\n", arg->argv[n], err);
+ return 1;
+ }
+
+ for (n = arg->argn; n < arg->argc; n++) {
+ bundle_SetLabel(arg->bundle, arg->argv[arg->argc - 1]);
+ system_Select(arg->bundle, arg->argv[n], CONFFILE, arg->prompt, arg->cx);
+ }
+ bundle_SetLabel(arg->bundle, arg->argv[arg->argc - 1]);
+ } else if ((err = system_IsValid("default", arg->prompt, mode)) != NULL) {
+ log_Printf(LogWARN, "default: %s\n", err);
+ return 1;
+ } else {
+ bundle_SetLabel(arg->bundle, "default");
+ system_Select(arg->bundle, "default", CONFFILE, arg->prompt, arg->cx);
+ bundle_SetLabel(arg->bundle, "default");
+ }
+
+ return 0;
+}
+
+static int
+LogCommand(struct cmdargs const *arg)
+{
+ char buf[LINE_LEN];
+
+ if (arg->argn < arg->argc) {
+ char *argv[MAXARGS];
+ int argc = arg->argc - arg->argn;
+
+ if (argc >= sizeof argv / sizeof argv[0]) {
+ argc = sizeof argv / sizeof argv[0] - 1;
+ log_Printf(LogWARN, "Truncating log command to %d args\n", argc);
+ }
+ command_Expand(argv, argc, arg->argv + arg->argn, arg->bundle, 1, getpid());
+ Concatinate(buf, sizeof buf, argc, (const char *const *)argv);
+ log_Printf(LogLOG, "%s\n", buf);
+ command_Free(argc, argv);
+ return 0;
+ }
+
+ return -1;
+}
+
+static int
+SaveCommand(struct cmdargs const *arg)
+{
+ log_Printf(LogWARN, "save command is not yet implemented.\n");
+ return 1;
+}
+
+static int
+DialCommand(struct cmdargs const *arg)
+{
+ int res;
+
+ if ((arg->cx && !(arg->cx->physical->type & (PHYS_INTERACTIVE|PHYS_AUTO)))
+ || (!arg->cx &&
+ (arg->bundle->phys_type.all & ~(PHYS_INTERACTIVE|PHYS_AUTO)))) {
+ log_Printf(LogWARN, "Manual dial is only available for auto and"
+ " interactive links\n");
+ return 1;
+ }
+
+ if (arg->argc > arg->argn && (res = LoadCommand(arg)) != 0)
+ return res;
+
+ bundle_Open(arg->bundle, arg->cx ? arg->cx->name : NULL, PHYS_ALL, 1);
+
+ return 0;
+}
+
+#define isinword(ch) (isalnum(ch) || (ch) == '_')
+
+static char *
+strstrword(char *big, const char *little)
+{
+ /* Get the first occurance of the word ``little'' in ``big'' */
+ char *pos;
+ int len;
+
+ pos = big;
+ len = strlen(little);
+
+ while ((pos = strstr(pos, little)) != NULL)
+ if ((pos != big && isinword(pos[-1])) || isinword(pos[len]))
+ pos++;
+ else if (pos != big && pos[-1] == '\\')
+ memmove(pos - 1, pos, strlen(pos) + 1);
+ else
+ break;
+
+ return pos;
+}
+
+static char *
+subst(char *tgt, const char *oldstr, const char *newstr)
+{
+ /* tgt is a malloc()d area... realloc() as necessary */
+ char *word, *ntgt;
+ int ltgt, loldstr, lnewstr, pos;
+
+ if ((word = strstrword(tgt, oldstr)) == NULL)
+ return tgt;
+
+ ltgt = strlen(tgt) + 1;
+ loldstr = strlen(oldstr);
+ lnewstr = strlen(newstr);
+ do {
+ pos = word - tgt;
+ if (loldstr > lnewstr)
+ bcopy(word + loldstr, word + lnewstr, ltgt - pos - loldstr);
+ if (loldstr != lnewstr) {
+ ntgt = realloc(tgt, ltgt += lnewstr - loldstr);
+ if (ntgt == NULL)
+ break; /* Oh wonderful ! */
+ word = ntgt + pos;
+ tgt = ntgt;
+ }
+ if (lnewstr > loldstr)
+ bcopy(word + loldstr, word + lnewstr, ltgt - pos - loldstr);
+ bcopy(newstr, word, lnewstr);
+ } while ((word = strstrword(word, oldstr)));
+
+ return tgt;
+}
+
+static char *
+substip(char *tgt, const char *oldstr, struct in_addr ip)
+{
+ return subst(tgt, oldstr, inet_ntoa(ip));
+}
+
+static char *
+substlong(char *tgt, const char *oldstr, long l)
+{
+ char buf[23];
+
+ snprintf(buf, sizeof buf, "%ld", l);
+
+ return subst(tgt, oldstr, buf);
+}
+
+static char *
+substull(char *tgt, const char *oldstr, unsigned long long ull)
+{
+ char buf[21];
+
+ snprintf(buf, sizeof buf, "%llu", ull);
+
+ return subst(tgt, oldstr, buf);
+}
+
+
+#ifndef NOINET6
+static char *
+substipv6(char *tgt, const char *oldstr, const struct ncpaddr *ip)
+{
+ return subst(tgt, oldstr, ncpaddr_ntoa(ip));
+}
+#endif
+
+void
+command_Expand(char **nargv, int argc, char const *const *oargv,
+ struct bundle *bundle, int inc0, pid_t pid)
+{
+ int arg, secs;
+ char uptime[20];
+ unsigned long long oin, oout, pin, pout;
+
+ if (inc0)
+ arg = 0; /* Start at arg 0 */
+ else {
+ nargv[0] = strdup(oargv[0]);
+ arg = 1;
+ }
+
+ secs = bundle_Uptime(bundle);
+ snprintf(uptime, sizeof uptime, "%d:%02d:%02d",
+ secs / 3600, (secs / 60) % 60, secs % 60);
+ oin = bundle->ncp.ipcp.throughput.OctetsIn;
+ oout = bundle->ncp.ipcp.throughput.OctetsOut;
+ pin = bundle->ncp.ipcp.throughput.PacketsIn;
+ pout = bundle->ncp.ipcp.throughput.PacketsOut;
+#ifndef NOINET6
+ oin += bundle->ncp.ipv6cp.throughput.OctetsIn;
+ oout += bundle->ncp.ipv6cp.throughput.OctetsOut;
+ pin += bundle->ncp.ipv6cp.throughput.PacketsIn;
+ pout += bundle->ncp.ipv6cp.throughput.PacketsOut;
+#endif
+
+ for (; arg < argc; arg++) {
+ nargv[arg] = strdup(oargv[arg]);
+ nargv[arg] = subst(nargv[arg], "AUTHNAME", bundle->cfg.auth.name);
+ nargv[arg] = subst(nargv[arg], "COMPILATIONDATE", __DATE__);
+ nargv[arg] = substip(nargv[arg], "DNS0", bundle->ncp.ipcp.ns.dns[0]);
+ nargv[arg] = substip(nargv[arg], "DNS1", bundle->ncp.ipcp.ns.dns[1]);
+ nargv[arg] = subst(nargv[arg], "ENDDISC",
+ mp_Enddisc(bundle->ncp.mp.cfg.enddisc.class,
+ bundle->ncp.mp.cfg.enddisc.address,
+ bundle->ncp.mp.cfg.enddisc.len));
+ nargv[arg] = substip(nargv[arg], "HISADDR", bundle->ncp.ipcp.peer_ip);
+#ifndef NOINET6
+ nargv[arg] = substipv6(nargv[arg], "HISADDR6", &bundle->ncp.ipv6cp.hisaddr);
+#endif
+ nargv[arg] = subst(nargv[arg], "INTERFACE", bundle->iface->name);
+ nargv[arg] = substull(nargv[arg], "IPOCTETSIN",
+ bundle->ncp.ipcp.throughput.OctetsIn);
+ nargv[arg] = substull(nargv[arg], "IPOCTETSOUT",
+ bundle->ncp.ipcp.throughput.OctetsOut);
+ nargv[arg] = substull(nargv[arg], "IPPACKETSIN",
+ bundle->ncp.ipcp.throughput.PacketsIn);
+ nargv[arg] = substull(nargv[arg], "IPPACKETSOUT",
+ bundle->ncp.ipcp.throughput.PacketsOut);
+#ifndef NOINET6
+ nargv[arg] = substull(nargv[arg], "IPV6OCTETSIN",
+ bundle->ncp.ipv6cp.throughput.OctetsIn);
+ nargv[arg] = substull(nargv[arg], "IPV6OCTETSOUT",
+ bundle->ncp.ipv6cp.throughput.OctetsOut);
+ nargv[arg] = substull(nargv[arg], "IPV6PACKETSIN",
+ bundle->ncp.ipv6cp.throughput.PacketsIn);
+ nargv[arg] = substull(nargv[arg], "IPV6PACKETSOUT",
+ bundle->ncp.ipv6cp.throughput.PacketsOut);
+#endif
+ nargv[arg] = subst(nargv[arg], "LABEL", bundle_GetLabel(bundle));
+ nargv[arg] = substip(nargv[arg], "MYADDR", bundle->ncp.ipcp.my_ip);
+#ifndef NOINET6
+ nargv[arg] = substipv6(nargv[arg], "MYADDR6", &bundle->ncp.ipv6cp.myaddr);
+#endif
+ nargv[arg] = substull(nargv[arg], "OCTETSIN", oin);
+ nargv[arg] = substull(nargv[arg], "OCTETSOUT", oout);
+ nargv[arg] = substull(nargv[arg], "PACKETSIN", pin);
+ nargv[arg] = substull(nargv[arg], "PACKETSOUT", pout);
+ nargv[arg] = subst(nargv[arg], "PEER_ENDDISC",
+ mp_Enddisc(bundle->ncp.mp.peer.enddisc.class,
+ bundle->ncp.mp.peer.enddisc.address,
+ bundle->ncp.mp.peer.enddisc.len));
+ nargv[arg] = substlong(nargv[arg], "PROCESSID", pid);
+ if (server.cfg.port)
+ nargv[arg] = substlong(nargv[arg], "SOCKNAME", server.cfg.port);
+ else
+ nargv[arg] = subst(nargv[arg], "SOCKNAME", server.cfg.sockname);
+ nargv[arg] = subst(nargv[arg], "UPTIME", uptime);
+ nargv[arg] = subst(nargv[arg], "USER", bundle->ncp.mp.peer.authname);
+ nargv[arg] = subst(nargv[arg], "VERSION", Version);
+ }
+ nargv[arg] = NULL;
+}
+
+void
+command_Free(int argc, char **argv)
+{
+ while (argc) {
+ free(*argv);
+ argc--;
+ argv++;
+ }
+}
+
+static int
+ShellCommand(struct cmdargs const *arg, int bg)
+{
+ const char *shell;
+ pid_t shpid, pid;
+
+#ifdef SHELL_ONLY_INTERACTIVELY
+ /* we're only allowed to shell when we run ppp interactively */
+ if (arg->prompt && arg->prompt->owner) {
+ log_Printf(LogWARN, "Can't start a shell from a network connection\n");
+ return 1;
+ }
+#endif
+
+ if (arg->argc == arg->argn) {
+ if (!arg->prompt) {
+ log_Printf(LogWARN, "Can't start an interactive shell from"
+ " a config file\n");
+ return 1;
+ } else if (arg->prompt->owner) {
+ log_Printf(LogWARN, "Can't start an interactive shell from"
+ " a socket connection\n");
+ return 1;
+ } else if (bg) {
+ log_Printf(LogWARN, "Can only start an interactive shell in"
+ " the foreground mode\n");
+ return 1;
+ }
+ }
+
+ pid = getpid();
+ if ((shpid = fork()) == 0) {
+ int i, fd;
+
+ if ((shell = getenv("SHELL")) == 0)
+ shell = _PATH_BSHELL;
+
+ timer_TermService();
+
+ if (arg->prompt)
+ fd = arg->prompt->fd_out;
+ else if ((fd = open(_PATH_DEVNULL, O_RDWR)) == -1) {
+ log_Printf(LogALERT, "Failed to open %s: %s\n",
+ _PATH_DEVNULL, strerror(errno));
+ exit(1);
+ }
+ dup2(fd, STDIN_FILENO);
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDERR_FILENO);
+ for (i = getdtablesize(); i > STDERR_FILENO; i--)
+ fcntl(i, F_SETFD, 1);
+
+#ifndef NOSUID
+ setuid(ID0realuid());
+#endif
+ if (arg->argc > arg->argn) {
+ /* substitute pseudo args */
+ char *argv[MAXARGS];
+ int argc = arg->argc - arg->argn;
+
+ if (argc >= sizeof argv / sizeof argv[0]) {
+ argc = sizeof argv / sizeof argv[0] - 1;
+ log_Printf(LogWARN, "Truncating shell command to %d args\n", argc);
+ }
+ command_Expand(argv, argc, arg->argv + arg->argn, arg->bundle, 0, pid);
+ if (bg) {
+ pid_t p;
+
+ p = getpid();
+ if (daemon(1, 1) == -1) {
+ log_Printf(LogERROR, "%ld: daemon: %s\n", (long)p, strerror(errno));
+ exit(1);
+ }
+ } else if (arg->prompt)
+ printf("ppp: Pausing until %s finishes\n", arg->argv[arg->argn]);
+ execvp(argv[0], argv);
+ } else {
+ if (arg->prompt)
+ printf("ppp: Pausing until %s finishes\n", shell);
+ prompt_TtyOldMode(arg->prompt);
+ execl(shell, shell, (char *)NULL);
+ }
+
+ log_Printf(LogWARN, "exec() of %s failed: %s\n",
+ arg->argc > arg->argn ? arg->argv[arg->argn] : shell,
+ strerror(errno));
+ _exit(255);
+ }
+
+ if (shpid == (pid_t)-1)
+ log_Printf(LogERROR, "Fork failed: %s\n", strerror(errno));
+ else {
+ int status;
+ waitpid(shpid, &status, 0);
+ }
+
+ if (arg->prompt && !arg->prompt->owner)
+ prompt_TtyCommandMode(arg->prompt);
+
+ return 0;
+}
+
+static int
+BgShellCommand(struct cmdargs const *arg)
+{
+ if (arg->argc == arg->argn)
+ return -1;
+ return ShellCommand(arg, 1);
+}
+
+static int
+FgShellCommand(struct cmdargs const *arg)
+{
+ return ShellCommand(arg, 0);
+}
+
+static int
+ResolvCommand(struct cmdargs const *arg)
+{
+ if (arg->argc == arg->argn + 1) {
+ if (!strcasecmp(arg->argv[arg->argn], "reload"))
+ ipcp_LoadDNS(&arg->bundle->ncp.ipcp);
+ else if (!strcasecmp(arg->argv[arg->argn], "restore"))
+ ipcp_RestoreDNS(&arg->bundle->ncp.ipcp);
+ else if (!strcasecmp(arg->argv[arg->argn], "rewrite"))
+ ipcp_WriteDNS(&arg->bundle->ncp.ipcp);
+ else if (!strcasecmp(arg->argv[arg->argn], "readonly"))
+ arg->bundle->ncp.ipcp.ns.writable = 0;
+ else if (!strcasecmp(arg->argv[arg->argn], "writable"))
+ arg->bundle->ncp.ipcp.ns.writable = 1;
+ else
+ return -1;
+
+ return 0;
+ }
+
+ return -1;
+}
+
+#ifndef NONAT
+static struct cmdtab const NatCommands[] =
+{
+ {"addr", NULL, nat_RedirectAddr, LOCAL_AUTH,
+ "static address translation", "nat addr [addr_local addr_alias]"},
+ {"deny_incoming", NULL, NatOption, LOCAL_AUTH,
+ "stop incoming connections", "nat deny_incoming yes|no",
+ (const void *) PKT_ALIAS_DENY_INCOMING},
+ {"enable", NULL, NatEnable, LOCAL_AUTH,
+ "enable NAT", "nat enable yes|no"},
+ {"log", NULL, NatOption, LOCAL_AUTH,
+ "log NAT link creation", "nat log yes|no",
+ (const void *) PKT_ALIAS_LOG},
+ {"port", NULL, nat_RedirectPort, LOCAL_AUTH, "port redirection",
+ "nat port proto localaddr:port[-port] aliasport[-aliasport]"},
+ {"proto", NULL, nat_RedirectProto, LOCAL_AUTH, "protocol redirection",
+ "nat proto proto localIP [publicIP [remoteIP]]"},
+ {"proxy", NULL, nat_ProxyRule, LOCAL_AUTH,
+ "proxy control", "nat proxy server host[:port] ..."},
+#ifndef NO_FW_PUNCH
+ {"punch_fw", NULL, nat_PunchFW, LOCAL_AUTH,
+ "firewall control", "nat punch_fw [base count]"},
+#endif
+ {"same_ports", NULL, NatOption, LOCAL_AUTH,
+ "try to leave port numbers unchanged", "nat same_ports yes|no",
+ (const void *) PKT_ALIAS_SAME_PORTS},
+ {"target", NULL, nat_SetTarget, LOCAL_AUTH,
+ "Default address for incoming connections", "nat target addr" },
+ {"unregistered_only", NULL, NatOption, LOCAL_AUTH,
+ "translate unregistered (private) IP address space only",
+ "nat unregistered_only yes|no",
+ (const void *) PKT_ALIAS_UNREGISTERED_ONLY},
+ {"use_sockets", NULL, NatOption, LOCAL_AUTH,
+ "allocate host sockets", "nat use_sockets yes|no",
+ (const void *) PKT_ALIAS_USE_SOCKETS},
+ {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
+ "Display this message", "nat help|? [command]", NatCommands},
+ {NULL, NULL, NULL},
+};
+#endif
+
+static struct cmdtab const AllowCommands[] = {
+ {"modes", "mode", AllowModes, LOCAL_AUTH,
+ "Only allow certain ppp modes", "allow modes mode..."},
+ {"users", "user", AllowUsers, LOCAL_AUTH,
+ "Only allow ppp access to certain users", "allow users logname..."},
+ {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
+ "Display this message", "allow help|? [command]", AllowCommands},
+ {NULL, NULL, NULL},
+};
+
+static struct cmdtab const IfaceCommands[] =
+{
+ {"add", NULL, IfaceAddCommand, LOCAL_AUTH,
+ "Add iface address", "iface add addr[/bits| mask] peer", NULL},
+ {NULL, "add!", IfaceAddCommand, LOCAL_AUTH,
+ "Add or change an iface address", "iface add! addr[/bits| mask] peer",
+ (void *)1},
+ {"clear", NULL, IfaceClearCommand, LOCAL_AUTH,
+ "Clear iface address(es)", "iface clear [INET | INET6]"},
+ {"delete", "rm", IfaceDeleteCommand, LOCAL_AUTH,
+ "Delete iface address", "iface delete addr", NULL},
+ {NULL, "rm!", IfaceDeleteCommand, LOCAL_AUTH,
+ "Delete iface address", "iface delete addr", (void *)1},
+ {NULL, "delete!", IfaceDeleteCommand, LOCAL_AUTH,
+ "Delete iface address", "iface delete addr", (void *)1},
+ {"show", NULL, iface_Show, LOCAL_AUTH,
+ "Show iface address(es)", "iface show"},
+ {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
+ "Display this message", "nat help|? [command]", IfaceCommands},
+ {NULL, NULL, NULL},
+};
+
+static struct cmdtab const Commands[] = {
+ {"accept", NULL, NegotiateCommand, LOCAL_AUTH | LOCAL_CX_OPT,
+ "accept option request", "accept option .."},
+ {"add", NULL, AddCommand, LOCAL_AUTH,
+ "add route", "add dest mask gateway", NULL},
+ {NULL, "add!", AddCommand, LOCAL_AUTH,
+ "add or change route", "add! dest mask gateway", (void *)1},
+ {"allow", "auth", RunListCommand, LOCAL_AUTH,
+ "Allow ppp access", "allow users|modes ....", AllowCommands},
+ {"bg", "!bg", BgShellCommand, LOCAL_AUTH,
+ "Run a background command", "[!]bg command"},
+ {"clear", NULL, ClearCommand, LOCAL_AUTH | LOCAL_CX_OPT,
+ "Clear throughput statistics",
+ "clear ipcp|ipv6cp|physical [current|overall|peak]..."},
+ {"clone", NULL, CloneCommand, LOCAL_AUTH | LOCAL_CX,
+ "Clone a link", "clone newname..."},
+ {"close", NULL, CloseCommand, LOCAL_AUTH | LOCAL_CX_OPT,
+ "Close an FSM", "close [lcp|ccp]"},
+ {"delete", NULL, DeleteCommand, LOCAL_AUTH,
+ "delete route", "delete dest", NULL},
+ {NULL, "delete!", DeleteCommand, LOCAL_AUTH,
+ "delete a route if it exists", "delete! dest", (void *)1},
+ {"deny", NULL, NegotiateCommand, LOCAL_AUTH | LOCAL_CX_OPT,
+ "Deny option request", "deny option .."},
+ {"dial", "call", DialCommand, LOCAL_AUTH | LOCAL_CX_OPT,
+ "Dial and login", "dial|call [system ...]", NULL},
+ {"disable", NULL, NegotiateCommand, LOCAL_AUTH | LOCAL_CX_OPT,
+ "Disable option", "disable option .."},
+ {"down", NULL, DownCommand, LOCAL_AUTH | LOCAL_CX_OPT,
+ "Generate a down event", "down [ccp|lcp]"},
+ {"enable", NULL, NegotiateCommand, LOCAL_AUTH | LOCAL_CX_OPT,
+ "Enable option", "enable option .."},
+ {"ident", NULL, IdentCommand, LOCAL_AUTH | LOCAL_CX,
+ "Set the link identity", "ident text..."},
+ {"iface", "interface", RunListCommand, LOCAL_AUTH,
+ "interface control", "iface option ...", IfaceCommands},
+ {"link", "datalink", LinkCommand, LOCAL_AUTH,
+ "Link specific commands", "link name command ..."},
+ {"load", NULL, LoadCommand, LOCAL_AUTH | LOCAL_CX_OPT,
+ "Load settings", "load [system ...]"},
+ {"log", NULL, LogCommand, LOCAL_AUTH | LOCAL_CX_OPT,
+ "log information", "log word ..."},
+#ifndef NONAT
+ {"nat", "alias", RunListCommand, LOCAL_AUTH,
+ "NAT control", "nat option yes|no", NatCommands},
+#endif
+ {"open", NULL, OpenCommand, LOCAL_AUTH | LOCAL_CX_OPT,
+ "Open an FSM", "open! [lcp|ccp|ipcp]", (void *)1},
+ {"passwd", NULL, PasswdCommand, LOCAL_NO_AUTH,
+ "Password for manipulation", "passwd LocalPassword"},
+ {"quit", "bye", QuitCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
+ "Quit PPP program", "quit|bye [all]"},
+ {"remove", "rm", RemoveCommand, LOCAL_AUTH | LOCAL_CX,
+ "Remove a link", "remove"},
+ {"rename", "mv", RenameCommand, LOCAL_AUTH | LOCAL_CX,
+ "Rename a link", "rename name"},
+ {"resolv", NULL, ResolvCommand, LOCAL_AUTH,
+ "Manipulate resolv.conf", "resolv readonly|reload|restore|rewrite|writable"},
+ {"save", NULL, SaveCommand, LOCAL_AUTH,
+ "Save settings", "save"},
+ {"sendident", NULL, SendIdentification, LOCAL_AUTH | LOCAL_CX,
+ "Transmit the link identity", "sendident"},
+ {"set", "setup", SetCommand, LOCAL_AUTH | LOCAL_CX_OPT,
+ "Set parameters", "set[up] var value"},
+ {"shell", "!", FgShellCommand, LOCAL_AUTH,
+ "Run a subshell", "shell|! [sh command]"},
+ {"show", NULL, ShowCommand, LOCAL_AUTH | LOCAL_CX_OPT,
+ "Show status and stats", "show var"},
+ {"term", NULL, TerminalCommand, LOCAL_AUTH | LOCAL_CX,
+ "Enter terminal mode", "term"},
+ {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
+ "Display this message", "help|? [command]", Commands},
+ {NULL, NULL, NULL},
+};
+
+static int
+ShowEscape(struct cmdargs const *arg)
+{
+ if (arg->cx->physical->async.cfg.EscMap[32]) {
+ int code, bit;
+ const char *sep = "";
+
+ for (code = 0; code < 32; code++)
+ if (arg->cx->physical->async.cfg.EscMap[code])
+ for (bit = 0; bit < 8; bit++)
+ if (arg->cx->physical->async.cfg.EscMap[code] & (1 << bit)) {
+ prompt_Printf(arg->prompt, "%s0x%02x", sep, (code << 3) + bit);
+ sep = ", ";
+ }
+ prompt_Printf(arg->prompt, "\n");
+ }
+ return 0;
+}
+
+static int
+ShowTimerList(struct cmdargs const *arg)
+{
+ timer_Show(0, arg->prompt);
+ return 0;
+}
+
+static int
+ShowStopped(struct cmdargs const *arg)
+{
+ prompt_Printf(arg->prompt, " Stopped Timer: LCP: ");
+ if (!arg->cx->physical->link.lcp.fsm.StoppedTimer.load)
+ prompt_Printf(arg->prompt, "Disabled");
+ else
+ prompt_Printf(arg->prompt, "%ld secs",
+ arg->cx->physical->link.lcp.fsm.StoppedTimer.load / SECTICKS);
+
+ prompt_Printf(arg->prompt, ", CCP: ");
+ if (!arg->cx->physical->link.ccp.fsm.StoppedTimer.load)
+ prompt_Printf(arg->prompt, "Disabled");
+ else
+ prompt_Printf(arg->prompt, "%ld secs",
+ arg->cx->physical->link.ccp.fsm.StoppedTimer.load / SECTICKS);
+
+ prompt_Printf(arg->prompt, "\n");
+
+ return 0;
+}
+
+static int
+ShowVersion(struct cmdargs const *arg)
+{
+ prompt_Printf(arg->prompt, "PPP Version %s - %s\n", Version, __DATE__);
+ return 0;
+}
+
+static int
+ShowProtocolStats(struct cmdargs const *arg)
+{
+ struct link *l = command_ChooseLink(arg);
+
+ prompt_Printf(arg->prompt, "%s:\n", l->name);
+ link_ReportProtocolStatus(l, arg->prompt);
+ return 0;
+}
+
+static struct cmdtab const ShowCommands[] = {
+ {"bundle", NULL, bundle_ShowStatus, LOCAL_AUTH,
+ "bundle details", "show bundle"},
+ {"ccp", NULL, ccp_ReportStatus, LOCAL_AUTH | LOCAL_CX_OPT,
+ "CCP status", "show cpp"},
+ {"compress", NULL, sl_Show, LOCAL_AUTH,
+ "VJ compression stats", "show compress"},
+ {"escape", NULL, ShowEscape, LOCAL_AUTH | LOCAL_CX,
+ "escape characters", "show escape"},
+ {"filter", NULL, filter_Show, LOCAL_AUTH,
+ "packet filters", "show filter [in|out|dial|alive]"},
+ {"hdlc", NULL, hdlc_ReportStatus, LOCAL_AUTH | LOCAL_CX,
+ "HDLC errors", "show hdlc"},
+ {"iface", "interface", iface_Show, LOCAL_AUTH,
+ "Interface status", "show iface"},
+ {"ipcp", NULL, ipcp_Show, LOCAL_AUTH,
+ "IPCP status", "show ipcp"},
+#ifndef NOINET6
+ {"ipv6cp", NULL, ipv6cp_Show, LOCAL_AUTH,
+ "IPV6CP status", "show ipv6cp"},
+#endif
+ {"layers", NULL, link_ShowLayers, LOCAL_AUTH | LOCAL_CX_OPT,
+ "Protocol layers", "show layers"},
+ {"lcp", NULL, lcp_ReportStatus, LOCAL_AUTH | LOCAL_CX,
+ "LCP status", "show lcp"},
+ {"link", "datalink", datalink_Show, LOCAL_AUTH | LOCAL_CX,
+ "(high-level) link info", "show link"},
+ {"links", NULL, bundle_ShowLinks, LOCAL_AUTH,
+ "available link names", "show links"},
+ {"log", NULL, log_ShowLevel, LOCAL_AUTH,
+ "log levels", "show log"},
+ {"mem", NULL, mbuf_Show, LOCAL_AUTH,
+ "mbuf allocations", "show mem"},
+ {"ncp", NULL, ncp_Show, LOCAL_AUTH,
+ "NCP status", "show ncp"},
+ {"physical", NULL, physical_ShowStatus, LOCAL_AUTH | LOCAL_CX,
+ "(low-level) link info", "show physical"},
+ {"mp", "multilink", mp_ShowStatus, LOCAL_AUTH,
+ "multilink setup", "show mp"},
+ {"proto", NULL, ShowProtocolStats, LOCAL_AUTH | LOCAL_CX_OPT,
+ "protocol summary", "show proto"},
+ {"route", NULL, route_Show, LOCAL_AUTH,
+ "routing table", "show route"},
+ {"stopped", NULL, ShowStopped, LOCAL_AUTH | LOCAL_CX,
+ "STOPPED timeout", "show stopped"},
+ {"timers", NULL, ShowTimerList, LOCAL_AUTH,
+ "alarm timers", "show timers"},
+ {"version", NULL, ShowVersion, LOCAL_NO_AUTH | LOCAL_AUTH,
+ "version string", "show version"},
+ {"who", NULL, log_ShowWho, LOCAL_AUTH,
+ "client list", "show who"},
+ {"help", "?", HelpCommand, LOCAL_NO_AUTH | LOCAL_AUTH,
+ "Display this message", "show help|? [command]", ShowCommands},
+ {NULL, NULL, NULL},
+};
+
+static struct cmdtab const *
+FindCommand(struct cmdtab const *cmds, const char *str, int *pmatch)
+{
+ int nmatch;
+ int len;
+ struct cmdtab const *found;
+
+ found = NULL;
+ len = strlen(str);
+ nmatch = 0;
+ while (cmds->func) {
+ if (cmds->name && strncasecmp(str, cmds->name, len) == 0) {
+ if (cmds->name[len] == '\0') {
+ *pmatch = 1;
+ return cmds;
+ }
+ nmatch++;
+ found = cmds;
+ } else if (cmds->alias && strncasecmp(str, cmds->alias, len) == 0) {
+ if (cmds->alias[len] == '\0') {
+ *pmatch = 1;
+ return cmds;
+ }
+ nmatch++;
+ found = cmds;
+ }
+ cmds++;
+ }
+ *pmatch = nmatch;
+ return found;
+}
+
+static const char *
+mkPrefix(int argc, char const *const *argv, char *tgt, int sz)
+{
+ int f, tlen, len;
+
+ tlen = 0;
+ for (f = 0; f < argc && tlen < sz - 2; f++) {
+ if (f)
+ tgt[tlen++] = ' ';
+ len = strlen(argv[f]);
+ if (len > sz - tlen - 1)
+ len = sz - tlen - 1;
+ strncpy(tgt+tlen, argv[f], len);
+ tlen += len;
+ }
+ tgt[tlen] = '\0';
+ return tgt;
+}
+
+static int
+FindExec(struct bundle *bundle, struct cmdtab const *cmds, int argc, int argn,
+ char const *const *argv, struct prompt *prompt, struct datalink *cx)
+{
+ struct cmdtab const *cmd;
+ int val = 1;
+ int nmatch;
+ struct cmdargs arg;
+ char prefix[100];
+
+ cmd = FindCommand(cmds, argv[argn], &nmatch);
+ if (nmatch > 1)
+ log_Printf(LogWARN, "%s: Ambiguous command\n",
+ mkPrefix(argn+1, argv, prefix, sizeof prefix));
+ else if (cmd && (!prompt || (cmd->lauth & prompt->auth))) {
+ if ((cmd->lauth & LOCAL_CX) && !cx)
+ /* We've got no context, but we require it */
+ cx = bundle2datalink(bundle, NULL);
+
+ if ((cmd->lauth & LOCAL_CX) && !cx)
+ log_Printf(LogWARN, "%s: No context (use the `link' command)\n",
+ mkPrefix(argn+1, argv, prefix, sizeof prefix));
+ else {
+ if (cx && !(cmd->lauth & (LOCAL_CX|LOCAL_CX_OPT))) {
+ log_Printf(LogWARN, "%s: Redundant context (%s) ignored\n",
+ mkPrefix(argn+1, argv, prefix, sizeof prefix), cx->name);
+ cx = NULL;
+ }
+ arg.cmdtab = cmds;
+ arg.cmd = cmd;
+ arg.argc = argc;
+ arg.argn = argn+1;
+ arg.argv = argv;
+ arg.bundle = bundle;
+ arg.cx = cx;
+ arg.prompt = prompt;
+ val = (*cmd->func) (&arg);
+ }
+ } else
+ log_Printf(LogWARN, "%s: Invalid command\n",
+ mkPrefix(argn+1, argv, prefix, sizeof prefix));
+
+ if (val == -1)
+ log_Printf(LogWARN, "usage: %s\n", cmd->syntax);
+ else if (val)
+ log_Printf(LogWARN, "%s: Failed %d\n",
+ mkPrefix(argn+1, argv, prefix, sizeof prefix), val);
+
+ return val;
+}
+
+int
+command_Expand_Interpret(char *buff, int nb, char *argv[MAXARGS], int offset)
+{
+ char buff2[LINE_LEN-offset];
+
+ InterpretArg(buff, buff2);
+ strncpy(buff, buff2, LINE_LEN - offset - 1);
+ buff[LINE_LEN - offset - 1] = '\0';
+
+ return command_Interpret(buff, nb, argv);
+}
+
+int
+command_Interpret(char *buff, int nb, char *argv[MAXARGS])
+{
+ char *cp;
+
+ if (nb > 0) {
+ cp = buff + strcspn(buff, "\r\n");
+ if (cp)
+ *cp = '\0';
+ return MakeArgs(buff, argv, MAXARGS, PARSE_REDUCE);
+ }
+ return 0;
+}
+
+static int
+arghidden(int argc, char const *const *argv, int n)
+{
+ /* Is arg n of the given command to be hidden from the log ? */
+
+ /* set authkey xxxxx */
+ /* set key xxxxx */
+ if (n == 2 && !strncasecmp(argv[0], "se", 2) &&
+ (!strncasecmp(argv[1], "authk", 5) || !strncasecmp(argv[1], "ke", 2)))
+ return 1;
+
+ /* passwd xxxxx */
+ if (n == 1 && !strncasecmp(argv[0], "p", 1))
+ return 1;
+
+ /* set server port xxxxx .... */
+ if (n == 3 && !strncasecmp(argv[0], "se", 2) &&
+ !strncasecmp(argv[1], "se", 2))
+ return 1;
+
+ return 0;
+}
+
+void
+command_Run(struct bundle *bundle, int argc, char const *const *argv,
+ struct prompt *prompt, const char *label, struct datalink *cx)
+{
+ if (argc > 0) {
+ if (log_IsKept(LogCOMMAND)) {
+ char buf[LINE_LEN];
+ int f, n;
+
+ if (label) {
+ strncpy(buf, label, sizeof buf - 3);
+ buf[sizeof buf - 3] = '\0';
+ strcat(buf, ": ");
+ n = strlen(buf);
+ } else {
+ *buf = '\0';
+ n = 0;
+ }
+ buf[sizeof buf - 1] = '\0'; /* In case we run out of room in buf */
+
+ for (f = 0; f < argc; f++) {
+ if (n < sizeof buf - 1 && f)
+ buf[n++] = ' ';
+ if (arghidden(argc, argv, f))
+ strncpy(buf+n, "********", sizeof buf - n - 1);
+ else
+ strncpy(buf+n, argv[f], sizeof buf - n - 1);
+ n += strlen(buf+n);
+ }
+ log_Printf(LogCOMMAND, "%s\n", buf);
+ }
+ FindExec(bundle, Commands, argc, 0, argv, prompt, cx);
+ }
+}
+
+int
+command_Decode(struct bundle *bundle, char *buff, int nb, struct prompt *prompt,
+ const char *label)
+{
+ int argc;
+ char *argv[MAXARGS];
+
+ if ((argc = command_Expand_Interpret(buff, nb, argv, 0)) < 0)
+ return 0;
+
+ command_Run(bundle, argc, (char const *const *)argv, prompt, label, NULL);
+ return 1;
+}
+
+static int
+ShowCommand(struct cmdargs const *arg)
+{
+ if (!arg->prompt)
+ log_Printf(LogWARN, "show: Cannot show without a prompt\n");
+ else if (arg->argc > arg->argn)
+ FindExec(arg->bundle, ShowCommands, arg->argc, arg->argn, arg->argv,
+ arg->prompt, arg->cx);
+ else
+ prompt_Printf(arg->prompt, "Use ``show ?'' to get a list.\n");
+
+ return 0;
+}
+
+static int
+TerminalCommand(struct cmdargs const *arg)
+{
+ if (!arg->prompt) {
+ log_Printf(LogWARN, "term: Need a prompt\n");
+ return 1;
+ }
+
+ if (arg->cx->physical->link.lcp.fsm.state > ST_CLOSED) {
+ prompt_Printf(arg->prompt, "LCP state is [%s]\n",
+ State2Nam(arg->cx->physical->link.lcp.fsm.state));
+ return 1;
+ }
+
+ datalink_Up(arg->cx, 0, 0);
+ prompt_TtyTermMode(arg->prompt, arg->cx);
+ return 0;
+}
+
+static int
+QuitCommand(struct cmdargs const *arg)
+{
+ if (!arg->prompt || prompt_IsController(arg->prompt) ||
+ (arg->argc > arg->argn && !strcasecmp(arg->argv[arg->argn], "all") &&
+ (arg->prompt->auth & LOCAL_AUTH)))
+ Cleanup(EX_NORMAL);
+ if (arg->prompt)
+ prompt_Destroy(arg->prompt, 1);
+
+ return 0;
+}
+
+static int
+OpenCommand(struct cmdargs const *arg)
+{
+ if (arg->argc == arg->argn)
+ bundle_Open(arg->bundle, arg->cx ? arg->cx->name : NULL, PHYS_ALL, 1);
+ else if (arg->argc == arg->argn + 1) {
+ if (!strcasecmp(arg->argv[arg->argn], "lcp")) {
+ struct datalink *cx = arg->cx ?
+ arg->cx : bundle2datalink(arg->bundle, NULL);
+ if (cx) {
+ if (cx->physical->link.lcp.fsm.state == ST_OPENED)
+ fsm_Reopen(&cx->physical->link.lcp.fsm);
+ else
+ bundle_Open(arg->bundle, cx->name, PHYS_ALL, 1);
+ } else
+ log_Printf(LogWARN, "open lcp: You must specify a link\n");
+ } else if (!strcasecmp(arg->argv[arg->argn], "ccp")) {
+ struct fsm *fp;
+
+ fp = &command_ChooseLink(arg)->ccp.fsm;
+ if (fp->link->lcp.fsm.state != ST_OPENED)
+ log_Printf(LogWARN, "open: LCP must be open before opening CCP\n");
+ else if (fp->state == ST_OPENED)
+ fsm_Reopen(fp);
+ else {
+ fp->open_mode = 0; /* Not passive any more */
+ if (fp->state == ST_STOPPED) {
+ fsm_Down(fp);
+ fsm_Up(fp);
+ } else {
+ fsm_Up(fp);
+ fsm_Open(fp);
+ }
+ }
+ } else if (!strcasecmp(arg->argv[arg->argn], "ipcp")) {
+ if (arg->cx)
+ log_Printf(LogWARN, "open ipcp: You need not specify a link\n");
+ if (arg->bundle->ncp.ipcp.fsm.state == ST_OPENED)
+ fsm_Reopen(&arg->bundle->ncp.ipcp.fsm);
+ else
+ bundle_Open(arg->bundle, NULL, PHYS_ALL, 1);
+ } else
+ return -1;
+ } else
+ return -1;
+
+ return 0;
+}
+
+static int
+CloseCommand(struct cmdargs const *arg)
+{
+ if (arg->argc == arg->argn)
+ bundle_Close(arg->bundle, arg->cx ? arg->cx->name : NULL, CLOSE_STAYDOWN);
+ else if (arg->argc == arg->argn + 1) {
+ if (!strcasecmp(arg->argv[arg->argn], "lcp"))
+ bundle_Close(arg->bundle, arg->cx ? arg->cx->name : NULL, CLOSE_LCP);
+ else if (!strcasecmp(arg->argv[arg->argn], "ccp") ||
+ !strcasecmp(arg->argv[arg->argn], "ccp!")) {
+ struct fsm *fp;
+
+ fp = &command_ChooseLink(arg)->ccp.fsm;
+ if (fp->state == ST_OPENED) {
+ fsm_Close(fp);
+ if (arg->argv[arg->argn][3] == '!')
+ fp->open_mode = 0; /* Stay ST_CLOSED */
+ else
+ fp->open_mode = OPEN_PASSIVE; /* Wait for the peer to start */
+ }
+ } else
+ return -1;
+ } else
+ return -1;
+
+ return 0;
+}
+
+static int
+DownCommand(struct cmdargs const *arg)
+{
+ if (arg->argc == arg->argn) {
+ if (arg->cx)
+ datalink_Down(arg->cx, CLOSE_STAYDOWN);
+ else
+ bundle_Down(arg->bundle, CLOSE_STAYDOWN);
+ } else if (arg->argc == arg->argn + 1) {
+ if (!strcasecmp(arg->argv[arg->argn], "lcp")) {
+ if (arg->cx)
+ datalink_Down(arg->cx, CLOSE_LCP);
+ else
+ bundle_Down(arg->bundle, CLOSE_LCP);
+ } else if (!strcasecmp(arg->argv[arg->argn], "ccp")) {
+ struct fsm *fp = arg->cx ? &arg->cx->physical->link.ccp.fsm :
+ &arg->bundle->ncp.mp.link.ccp.fsm;
+ fsm2initial(fp);
+ } else
+ return -1;
+ } else
+ return -1;
+
+ return 0;
+}
+
+static int
+SetModemSpeed(struct cmdargs const *arg)
+{
+ long speed;
+ char *end;
+
+ if (arg->argc > arg->argn && *arg->argv[arg->argn]) {
+ if (arg->argc > arg->argn+1) {
+ log_Printf(LogWARN, "SetModemSpeed: Too many arguments\n");
+ return -1;
+ }
+ if (strcasecmp(arg->argv[arg->argn], "sync") == 0) {
+ physical_SetSync(arg->cx->physical);
+ return 0;
+ }
+ end = NULL;
+ speed = strtol(arg->argv[arg->argn], &end, 10);
+ if (*end) {
+ log_Printf(LogWARN, "SetModemSpeed: Bad argument \"%s\"",
+ arg->argv[arg->argn]);
+ return -1;
+ }
+ if (physical_SetSpeed(arg->cx->physical, speed))
+ return 0;
+ log_Printf(LogWARN, "%s: Invalid speed\n", arg->argv[arg->argn]);
+ } else
+ log_Printf(LogWARN, "SetModemSpeed: No speed specified\n");
+
+ return -1;
+}
+
+static int
+SetStoppedTimeout(struct cmdargs const *arg)
+{
+ struct link *l = &arg->cx->physical->link;
+
+ l->lcp.fsm.StoppedTimer.load = 0;
+ l->ccp.fsm.StoppedTimer.load = 0;
+ if (arg->argc <= arg->argn+2) {
+ if (arg->argc > arg->argn) {
+ l->lcp.fsm.StoppedTimer.load = atoi(arg->argv[arg->argn]) * SECTICKS;
+ if (arg->argc > arg->argn+1)
+ l->ccp.fsm.StoppedTimer.load = atoi(arg->argv[arg->argn+1]) * SECTICKS;
+ }
+ return 0;
+ }
+ return -1;
+}
+
+static int
+SetServer(struct cmdargs const *arg)
+{
+ int res = -1;
+
+ if (arg->argc > arg->argn && arg->argc < arg->argn+4) {
+ const char *port, *passwd, *mask;
+ int mlen;
+
+ /* What's what ? */
+ port = arg->argv[arg->argn];
+ if (arg->argc == arg->argn + 2) {
+ passwd = arg->argv[arg->argn+1];
+ mask = NULL;
+ } else if (arg->argc == arg->argn + 3) {
+ passwd = arg->argv[arg->argn+1];
+ mask = arg->argv[arg->argn+2];
+ mlen = strlen(mask);
+ if (mlen == 0 || mlen > 4 || strspn(mask, "01234567") != mlen ||
+ (mlen == 4 && *mask != '0')) {
+ log_Printf(LogWARN, "%s %s: %s: Invalid mask\n",
+ arg->argv[arg->argn - 2], arg->argv[arg->argn - 1], mask);
+ return -1;
+ }
+ } else if (arg->argc != arg->argn + 1)
+ return -1;
+ else if (strcasecmp(port, "none") == 0) {
+ if (server_Clear(arg->bundle))
+ log_Printf(LogPHASE, "Disabled server socket\n");
+ return 0;
+ } else if (strcasecmp(port, "open") == 0) {
+ switch (server_Reopen(arg->bundle)) {
+ case SERVER_OK:
+ return 0;
+ case SERVER_FAILED:
+ log_Printf(LogWARN, "Failed to reopen server port\n");
+ return 1;
+ case SERVER_UNSET:
+ log_Printf(LogWARN, "Cannot reopen unset server socket\n");
+ return 1;
+ default:
+ break;
+ }
+ return -1;
+ } else if (strcasecmp(port, "closed") == 0) {
+ if (server_Close(arg->bundle))
+ log_Printf(LogPHASE, "Closed server socket\n");
+ else
+ log_Printf(LogWARN, "Server socket not open\n");
+
+ return 0;
+ } else
+ return -1;
+
+ strncpy(server.cfg.passwd, passwd, sizeof server.cfg.passwd - 1);
+ server.cfg.passwd[sizeof server.cfg.passwd - 1] = '\0';
+
+ if (*port == '/') {
+ mode_t imask;
+ char *ptr, name[LINE_LEN + 12];
+
+ if (mask == NULL)
+ imask = (mode_t)-1;
+ else for (imask = mlen = 0; mask[mlen]; mlen++)
+ imask = (imask * 8) + mask[mlen] - '0';
+
+ ptr = strstr(port, "%d");
+ if (ptr) {
+ snprintf(name, sizeof name, "%.*s%d%s",
+ (int)(ptr - port), port, arg->bundle->unit, ptr + 2);
+ port = name;
+ }
+ res = server_LocalOpen(arg->bundle, port, imask);
+ } else {
+ int iport, add = 0;
+
+ if (mask != NULL)
+ return -1;
+
+ if (*port == '+') {
+ port++;
+ add = 1;
+ }
+ if (strspn(port, "0123456789") != strlen(port)) {
+ struct servent *s;
+
+ if ((s = getservbyname(port, "tcp")) == NULL) {
+ iport = 0;
+ log_Printf(LogWARN, "%s: Invalid port or service\n", port);
+ } else
+ iport = ntohs(s->s_port);
+ } else
+ iport = atoi(port);
+
+ if (iport) {
+ if (add)
+ iport += arg->bundle->unit;
+ res = server_TcpOpen(arg->bundle, iport);
+ } else
+ res = -1;
+ }
+ }
+
+ return res;
+}
+
+static int
+SetEscape(struct cmdargs const *arg)
+{
+ int code;
+ int argc = arg->argc - arg->argn;
+ char const *const *argv = arg->argv + arg->argn;
+
+ for (code = 0; code < 33; code++)
+ arg->cx->physical->async.cfg.EscMap[code] = 0;
+
+ while (argc-- > 0) {
+ sscanf(*argv++, "%x", &code);
+ code &= 0xff;
+ arg->cx->physical->async.cfg.EscMap[code >> 3] |= (1 << (code & 7));
+ arg->cx->physical->async.cfg.EscMap[32] = 1;
+ }
+ return 0;
+}
+
+static int
+SetInterfaceAddr(struct cmdargs const *arg)
+{
+ struct ncp *ncp = &arg->bundle->ncp;
+ struct ncpaddr ncpaddr;
+ const char *hisaddr;
+
+ if (arg->argc > arg->argn + 4)
+ return -1;
+
+ hisaddr = NULL;
+ memset(&ncp->ipcp.cfg.my_range, '\0', sizeof ncp->ipcp.cfg.my_range);
+ memset(&ncp->ipcp.cfg.peer_range, '\0', sizeof ncp->ipcp.cfg.peer_range);
+ ncp->ipcp.cfg.HaveTriggerAddress = 0;
+ ncp->ipcp.cfg.netmask.s_addr = INADDR_ANY;
+ iplist_reset(&ncp->ipcp.cfg.peer_list);
+
+ if (arg->argc > arg->argn) {
+ if (!ncprange_aton(&ncp->ipcp.cfg.my_range, ncp, arg->argv[arg->argn]))
+ return 1;
+ if (arg->argc > arg->argn+1) {
+ hisaddr = arg->argv[arg->argn+1];
+ if (arg->argc > arg->argn+2) {
+ ncp->ipcp.ifmask = ncp->ipcp.cfg.netmask =
+ GetIpAddr(arg->argv[arg->argn+2]);
+ if (arg->argc > arg->argn+3) {
+ ncp->ipcp.cfg.TriggerAddress = GetIpAddr(arg->argv[arg->argn+3]);
+ ncp->ipcp.cfg.HaveTriggerAddress = 1;
+ }
+ }
+ }
+ }
+
+ /* 0.0.0.0 means any address (0 bits) */
+ ncpaddr_getip4(&ncpaddr, &ncp->ipcp.my_ip);
+ ncprange_getaddr(&ncp->ipcp.cfg.my_range, &ncpaddr);
+ if (ncp->ipcp.my_ip.s_addr == INADDR_ANY)
+ ncprange_setwidth(&ncp->ipcp.cfg.my_range, 0);
+ bundle_AdjustFilters(arg->bundle, &ncpaddr, NULL);
+
+ if (hisaddr && !ipcp_UseHisaddr(arg->bundle, hisaddr,
+ arg->bundle->phys_type.all & PHYS_AUTO))
+ return 4;
+
+ return 0;
+}
+
+static int
+SetRetry(int argc, char const *const *argv, u_int *timeout, u_int *maxreq,
+ u_int *maxtrm, int def)
+{
+ if (argc == 0) {
+ *timeout = DEF_FSMRETRY;
+ *maxreq = def;
+ if (maxtrm != NULL)
+ *maxtrm = def;
+ } else {
+ long l = atol(argv[0]);
+
+ if (l < MIN_FSMRETRY) {
+ log_Printf(LogWARN, "%ld: Invalid FSM retry period - min %d\n",
+ l, MIN_FSMRETRY);
+ return 1;
+ } else
+ *timeout = l;
+
+ if (argc > 1) {
+ l = atol(argv[1]);
+ if (l < 1) {
+ log_Printf(LogWARN, "%ld: Invalid FSM REQ tries - changed to 1\n", l);
+ l = 1;
+ }
+ *maxreq = l;
+
+ if (argc > 2 && maxtrm != NULL) {
+ l = atol(argv[2]);
+ if (l < 1) {
+ log_Printf(LogWARN, "%ld: Invalid FSM TRM tries - changed to 1\n", l);
+ l = 1;
+ }
+ *maxtrm = l;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int
+SetVariable(struct cmdargs const *arg)
+{
+ long long_val, param = (long)arg->cmd->args;
+ int mode, dummyint, f, first, res;
+ u_short *change;
+ const char *argp;
+ struct datalink *cx = arg->cx; /* LOCAL_CX uses this */
+ struct link *l = command_ChooseLink(arg); /* LOCAL_CX_OPT uses this */
+ struct in_addr *ipaddr;
+ struct ncpaddr ncpaddr[2];
+
+ if (arg->argc > arg->argn)
+ argp = arg->argv[arg->argn];
+ else
+ argp = "";
+
+ res = 0;
+
+ if ((arg->cmd->lauth & LOCAL_CX) && !cx) {
+ log_Printf(LogWARN, "set %s: No context (use the `link' command)\n",
+ arg->cmd->name);
+ return 1;
+ } else if (cx && !(arg->cmd->lauth & (LOCAL_CX|LOCAL_CX_OPT))) {
+ log_Printf(LogWARN, "set %s: Redundant context (%s) ignored\n",
+ arg->cmd->name, cx->name);
+ cx = NULL;
+ }
+
+ switch (param) {
+ case VAR_AUTHKEY:
+ strncpy(arg->bundle->cfg.auth.key, argp,
+ sizeof arg->bundle->cfg.auth.key - 1);
+ arg->bundle->cfg.auth.key[sizeof arg->bundle->cfg.auth.key - 1] = '\0';
+ break;
+
+ case VAR_AUTHNAME:
+ switch (bundle_Phase(arg->bundle)) {
+ default:
+ log_Printf(LogWARN, "Altering authname while at phase %s\n",
+ bundle_PhaseName(arg->bundle));
+ /* drop through */
+ case PHASE_DEAD:
+ case PHASE_ESTABLISH:
+ strncpy(arg->bundle->cfg.auth.name, argp,
+ sizeof arg->bundle->cfg.auth.name - 1);
+ arg->bundle->cfg.auth.name[sizeof arg->bundle->cfg.auth.name-1] = '\0';
+ break;
+ }
+ break;
+
+ case VAR_AUTOLOAD:
+ if (arg->argc == arg->argn + 3) {
+ int v1, v2, v3;
+ char *end;
+
+ v1 = strtol(arg->argv[arg->argn], &end, 0);
+ if (v1 < 0 || *end) {
+ log_Printf(LogWARN, "autoload: %s: Invalid min percentage\n",
+ arg->argv[arg->argn]);
+ res = 1;
+ break;
+ }
+
+ v2 = strtol(arg->argv[arg->argn + 1], &end, 0);
+ if (v2 < 0 || *end) {
+ log_Printf(LogWARN, "autoload: %s: Invalid max percentage\n",
+ arg->argv[arg->argn + 1]);
+ res = 1;
+ break;
+ }
+ if (v2 < v1) {
+ v3 = v1;
+ v1 = v2;
+ v2 = v3;
+ }
+
+ v3 = strtol(arg->argv[arg->argn + 2], &end, 0);
+ if (v3 <= 0 || *end) {
+ log_Printf(LogWARN, "autoload: %s: Invalid throughput period\n",
+ arg->argv[arg->argn + 2]);
+ res = 1;
+ break;
+ }
+
+ arg->bundle->ncp.mp.cfg.autoload.min = v1;
+ arg->bundle->ncp.mp.cfg.autoload.max = v2;
+ arg->bundle->ncp.mp.cfg.autoload.period = v3;
+ mp_RestartAutoloadTimer(&arg->bundle->ncp.mp);
+ } else {
+ log_Printf(LogWARN, "Set autoload requires three arguments\n");
+ res = 1;
+ }
+ break;
+
+ case VAR_DIAL:
+ strncpy(cx->cfg.script.dial, argp, sizeof cx->cfg.script.dial - 1);
+ cx->cfg.script.dial[sizeof cx->cfg.script.dial - 1] = '\0';
+ break;
+
+ case VAR_LOGIN:
+ strncpy(cx->cfg.script.login, argp, sizeof cx->cfg.script.login - 1);
+ cx->cfg.script.login[sizeof cx->cfg.script.login - 1] = '\0';
+ break;
+
+ case VAR_WINSIZE:
+ if (arg->argc > arg->argn) {
+ l->ccp.cfg.deflate.out.winsize = atoi(arg->argv[arg->argn]);
+ if (l->ccp.cfg.deflate.out.winsize < 8 ||
+ l->ccp.cfg.deflate.out.winsize > 15) {
+ log_Printf(LogWARN, "%d: Invalid outgoing window size\n",
+ l->ccp.cfg.deflate.out.winsize);
+ l->ccp.cfg.deflate.out.winsize = 15;
+ }
+ if (arg->argc > arg->argn+1) {
+ l->ccp.cfg.deflate.in.winsize = atoi(arg->argv[arg->argn+1]);
+ if (l->ccp.cfg.deflate.in.winsize < 8 ||
+ l->ccp.cfg.deflate.in.winsize > 15) {
+ log_Printf(LogWARN, "%d: Invalid incoming window size\n",
+ l->ccp.cfg.deflate.in.winsize);
+ l->ccp.cfg.deflate.in.winsize = 15;
+ }
+ } else
+ l->ccp.cfg.deflate.in.winsize = 0;
+ } else {
+ log_Printf(LogWARN, "No window size specified\n");
+ res = 1;
+ }
+ break;
+
+#ifndef NODES
+ case VAR_MPPE:
+ if (arg->argc > arg->argn + 2) {
+ res = -1;
+ break;
+ }
+
+ if (arg->argc == arg->argn) {
+ l->ccp.cfg.mppe.keybits = 0;
+ l->ccp.cfg.mppe.state = MPPE_ANYSTATE;
+ l->ccp.cfg.mppe.required = 0;
+ break;
+ }
+
+ if (!strcmp(argp, "*"))
+ long_val = 0;
+ else {
+ long_val = atol(argp);
+ if (long_val != 40 && long_val != 56 && long_val != 128) {
+ log_Printf(LogWARN, "%s: Invalid bits value\n", argp);
+ res = -1;
+ break;
+ }
+ }
+
+ if (arg->argc == arg->argn + 2) {
+ if (!strcmp(arg->argv[arg->argn + 1], "*"))
+ l->ccp.cfg.mppe.state = MPPE_ANYSTATE;
+ else if (!strcasecmp(arg->argv[arg->argn + 1], "stateless"))
+ l->ccp.cfg.mppe.state = MPPE_STATELESS;
+ else if (!strcasecmp(arg->argv[arg->argn + 1], "stateful"))
+ l->ccp.cfg.mppe.state = MPPE_STATEFUL;
+ else {
+ log_Printf(LogWARN, "%s: Invalid state value\n",
+ arg->argv[arg->argn + 1]);
+ res = -1;
+ break;
+ }
+ } else
+ l->ccp.cfg.mppe.state = MPPE_ANYSTATE;
+ l->ccp.cfg.mppe.keybits = long_val;
+ l->ccp.cfg.mppe.required = 1;
+ break;
+#endif
+
+ case VAR_DEVICE:
+ physical_SetDeviceList(cx->physical, arg->argc - arg->argn,
+ arg->argv + arg->argn);
+ break;
+
+ case VAR_ACCMAP:
+ if (arg->argc > arg->argn) {
+ u_long ulong_val;
+ sscanf(argp, "%lx", &ulong_val);
+ cx->physical->link.lcp.cfg.accmap = (u_int32_t)ulong_val;
+ } else {
+ log_Printf(LogWARN, "No accmap specified\n");
+ res = 1;
+ }
+ break;
+
+ case VAR_MODE:
+ mode = Nam2mode(argp);
+ if (mode == PHYS_NONE || mode == PHYS_ALL) {
+ log_Printf(LogWARN, "%s: Invalid mode\n", argp);
+ res = -1;
+ break;
+ }
+ bundle_SetMode(arg->bundle, cx, mode);
+ break;
+
+ case VAR_MRRU:
+ switch (bundle_Phase(arg->bundle)) {
+ case PHASE_DEAD:
+ break;
+ case PHASE_ESTABLISH:
+ /* Make sure none of our links are DATALINK_LCP or greater */
+ if (bundle_HighestState(arg->bundle) >= DATALINK_LCP) {
+ log_Printf(LogWARN, "mrru: Only changable before LCP negotiations\n");
+ res = 1;
+ break;
+ }
+ break;
+ default:
+ log_Printf(LogWARN, "mrru: Only changable at phase DEAD/ESTABLISH\n");
+ res = 1;
+ break;
+ }
+ if (res != 0)
+ break;
+ long_val = atol(argp);
+ if (long_val && long_val < MIN_MRU) {
+ log_Printf(LogWARN, "MRRU %ld: too small - min %d\n", long_val, MIN_MRU);
+ res = 1;
+ break;
+ } else if (long_val > MAX_MRU) {
+ log_Printf(LogWARN, "MRRU %ld: too big - max %d\n", long_val, MAX_MRU);
+ res = 1;
+ break;
+ } else
+ arg->bundle->ncp.mp.cfg.mrru = long_val;
+ break;
+
+ case VAR_MRU:
+ long_val = 0; /* silence gcc */
+ change = NULL; /* silence gcc */
+ switch(arg->argc - arg->argn) {
+ case 1:
+ if (argp[strspn(argp, "0123456789")] != '\0') {
+ res = -1;
+ break;
+ }
+ /*FALLTHRU*/
+ case 0:
+ long_val = atol(argp);
+ change = &l->lcp.cfg.mru;
+ if (long_val > l->lcp.cfg.max_mru) {
+ log_Printf(LogWARN, "MRU %ld: too large - max set to %d\n", long_val,
+ l->lcp.cfg.max_mru);
+ res = 1;
+ break;
+ }
+ break;
+ case 2:
+ if (strcasecmp(argp, "max") && strcasecmp(argp, "maximum")) {
+ res = -1;
+ break;
+ }
+ long_val = atol(arg->argv[arg->argn + 1]);
+ change = &l->lcp.cfg.max_mru;
+ if (long_val > MAX_MRU) {
+ log_Printf(LogWARN, "MRU %ld: too large - maximum is %d\n", long_val,
+ MAX_MRU);
+ res = 1;
+ break;
+ }
+ break;
+ default:
+ res = -1;
+ break;
+ }
+ if (res != 0)
+ break;
+
+ if (long_val == 0)
+ *change = 0;
+ else if (long_val < MIN_MRU) {
+ log_Printf(LogWARN, "MRU %ld: too small - min %d\n", long_val, MIN_MRU);
+ res = 1;
+ break;
+ } else if (long_val > MAX_MRU) {
+ log_Printf(LogWARN, "MRU %ld: too big - max %d\n", long_val, MAX_MRU);
+ res = 1;
+ break;
+ } else
+ *change = long_val;
+ if (l->lcp.cfg.mru > *change)
+ l->lcp.cfg.mru = *change;
+ break;
+
+ case VAR_MTU:
+ long_val = 0; /* silence gcc */
+ change = NULL; /* silence gcc */
+ switch(arg->argc - arg->argn) {
+ case 1:
+ if (argp[strspn(argp, "0123456789")] != '\0') {
+ res = -1;
+ break;
+ }
+ /*FALLTHRU*/
+ case 0:
+ long_val = atol(argp);
+ change = &l->lcp.cfg.mtu;
+ if (long_val > l->lcp.cfg.max_mtu) {
+ log_Printf(LogWARN, "MTU %ld: too large - max set to %d\n", long_val,
+ l->lcp.cfg.max_mtu);
+ res = 1;
+ break;
+ }
+ break;
+ case 2:
+ if (strcasecmp(argp, "max") && strcasecmp(argp, "maximum")) {
+ res = -1;
+ break;
+ }
+ long_val = atol(arg->argv[arg->argn + 1]);
+ change = &l->lcp.cfg.max_mtu;
+ if (long_val > MAX_MTU) {
+ log_Printf(LogWARN, "MTU %ld: too large - maximum is %d\n", long_val,
+ MAX_MTU);
+ res = 1;
+ break;
+ }
+ break;
+ default:
+ res = -1;
+ break;
+ }
+
+ if (res != 0)
+ break;
+
+ if (long_val && long_val < MIN_MTU) {
+ log_Printf(LogWARN, "MTU %ld: too small - min %d\n", long_val, MIN_MTU);
+ res = 1;
+ break;
+ } else if (long_val > MAX_MTU) {
+ log_Printf(LogWARN, "MTU %ld: too big - max %d\n", long_val, MAX_MTU);
+ res = 1;
+ break;
+ } else
+ *change = long_val;
+ if (l->lcp.cfg.mtu > *change)
+ l->lcp.cfg.mtu = *change;
+ break;
+
+ case VAR_OPENMODE:
+ if (strcasecmp(argp, "active") == 0)
+ cx->physical->link.lcp.cfg.openmode = arg->argc > arg->argn+1 ?
+ atoi(arg->argv[arg->argn+1]) : 1;
+ else if (strcasecmp(argp, "passive") == 0)
+ cx->physical->link.lcp.cfg.openmode = OPEN_PASSIVE;
+ else {
+ log_Printf(LogWARN, "%s: Invalid openmode\n", argp);
+ res = 1;
+ }
+ break;
+
+ case VAR_PHONE:
+ strncpy(cx->cfg.phone.list, argp, sizeof cx->cfg.phone.list - 1);
+ cx->cfg.phone.list[sizeof cx->cfg.phone.list - 1] = '\0';
+ cx->phone.alt = cx->phone.next = NULL;
+ break;
+
+ case VAR_HANGUP:
+ strncpy(cx->cfg.script.hangup, argp, sizeof cx->cfg.script.hangup - 1);
+ cx->cfg.script.hangup[sizeof cx->cfg.script.hangup - 1] = '\0';
+ break;
+
+ case VAR_IFQUEUE:
+ long_val = atol(argp);
+ arg->bundle->cfg.ifqueue = long_val < 0 ? 0 : long_val;
+ break;
+
+ case VAR_LOGOUT:
+ strncpy(cx->cfg.script.logout, argp, sizeof cx->cfg.script.logout - 1);
+ cx->cfg.script.logout[sizeof cx->cfg.script.logout - 1] = '\0';
+ break;
+
+ case VAR_IDLETIMEOUT:
+ if (arg->argc > arg->argn+2) {
+ log_Printf(LogWARN, "Too many idle timeout values\n");
+ res = 1;
+ } else if (arg->argc == arg->argn) {
+ log_Printf(LogWARN, "Too few idle timeout values\n");
+ res = 1;
+ } else {
+ int timeout, min;
+
+ timeout = atoi(argp);
+ min = arg->argc == arg->argn + 2 ? atoi(arg->argv[arg->argn + 1]) : -1;
+ bundle_SetIdleTimer(arg->bundle, timeout, min);
+ }
+ break;
+
+ case VAR_LQRPERIOD:
+ long_val = atol(argp);
+ if (long_val < MIN_LQRPERIOD) {
+ log_Printf(LogWARN, "%ld: Invalid lqr period - min %d\n",
+ long_val, MIN_LQRPERIOD);
+ res = 1;
+ } else
+ l->lcp.cfg.lqrperiod = long_val;
+ break;
+
+ case VAR_LCPRETRY:
+ res = SetRetry(arg->argc - arg->argn, arg->argv + arg->argn,
+ &cx->physical->link.lcp.cfg.fsm.timeout,
+ &cx->physical->link.lcp.cfg.fsm.maxreq,
+ &cx->physical->link.lcp.cfg.fsm.maxtrm, DEF_FSMTRIES);
+ break;
+
+ case VAR_CHAPRETRY:
+ res = SetRetry(arg->argc - arg->argn, arg->argv + arg->argn,
+ &cx->chap.auth.cfg.fsm.timeout,
+ &cx->chap.auth.cfg.fsm.maxreq, NULL, DEF_FSMAUTHTRIES);
+ break;
+
+ case VAR_PAPRETRY:
+ res = SetRetry(arg->argc - arg->argn, arg->argv + arg->argn,
+ &cx->pap.cfg.fsm.timeout, &cx->pap.cfg.fsm.maxreq,
+ NULL, DEF_FSMAUTHTRIES);
+ break;
+
+ case VAR_CCPRETRY:
+ res = SetRetry(arg->argc - arg->argn, arg->argv + arg->argn,
+ &l->ccp.cfg.fsm.timeout, &l->ccp.cfg.fsm.maxreq,
+ &l->ccp.cfg.fsm.maxtrm, DEF_FSMTRIES);
+ break;
+
+ case VAR_IPCPRETRY:
+ res = SetRetry(arg->argc - arg->argn, arg->argv + arg->argn,
+ &arg->bundle->ncp.ipcp.cfg.fsm.timeout,
+ &arg->bundle->ncp.ipcp.cfg.fsm.maxreq,
+ &arg->bundle->ncp.ipcp.cfg.fsm.maxtrm, DEF_FSMTRIES);
+ break;
+
+ case VAR_NBNS:
+ case VAR_DNS:
+ if (param == VAR_DNS) {
+ ipaddr = arg->bundle->ncp.ipcp.cfg.ns.dns;
+ ipaddr[0].s_addr = ipaddr[1].s_addr = INADDR_NONE;
+ } else {
+ ipaddr = arg->bundle->ncp.ipcp.cfg.ns.nbns;
+ ipaddr[0].s_addr = ipaddr[1].s_addr = INADDR_ANY;
+ }
+
+ if (arg->argc > arg->argn) {
+ ncpaddr_aton(ncpaddr, &arg->bundle->ncp, arg->argv[arg->argn]);
+ if (!ncpaddr_getip4(ncpaddr, ipaddr))
+ return -1;
+ if (arg->argc > arg->argn+1) {
+ ncpaddr_aton(ncpaddr + 1, &arg->bundle->ncp, arg->argv[arg->argn + 1]);
+ if (!ncpaddr_getip4(ncpaddr + 1, ipaddr + 1))
+ return -1;
+ }
+
+ if (ipaddr[0].s_addr == INADDR_ANY) {
+ ipaddr[0] = ipaddr[1];
+ ipaddr[1].s_addr = INADDR_ANY;
+ }
+ if (ipaddr[0].s_addr == INADDR_NONE) {
+ ipaddr[0] = ipaddr[1];
+ ipaddr[1].s_addr = INADDR_NONE;
+ }
+ }
+ break;
+
+ case VAR_CALLBACK:
+ cx->cfg.callback.opmask = 0;
+ for (dummyint = arg->argn; dummyint < arg->argc; dummyint++) {
+ if (!strcasecmp(arg->argv[dummyint], "auth"))
+ cx->cfg.callback.opmask |= CALLBACK_BIT(CALLBACK_AUTH);
+ else if (!strcasecmp(arg->argv[dummyint], "cbcp"))
+ cx->cfg.callback.opmask |= CALLBACK_BIT(CALLBACK_CBCP);
+ else if (!strcasecmp(arg->argv[dummyint], "e.164")) {
+ if (dummyint == arg->argc - 1)
+ log_Printf(LogWARN, "No E.164 arg (E.164 ignored) !\n");
+ else {
+ cx->cfg.callback.opmask |= CALLBACK_BIT(CALLBACK_E164);
+ strncpy(cx->cfg.callback.msg, arg->argv[++dummyint],
+ sizeof cx->cfg.callback.msg - 1);
+ cx->cfg.callback.msg[sizeof cx->cfg.callback.msg - 1] = '\0';
+ }
+ } else if (!strcasecmp(arg->argv[dummyint], "none"))
+ cx->cfg.callback.opmask |= CALLBACK_BIT(CALLBACK_NONE);
+ else {
+ res = -1;
+ break;
+ }
+ }
+ if (cx->cfg.callback.opmask == CALLBACK_BIT(CALLBACK_NONE))
+ cx->cfg.callback.opmask = 0;
+ break;
+
+ case VAR_CBCP:
+ cx->cfg.cbcp.delay = 0;
+ *cx->cfg.cbcp.phone = '\0';
+ cx->cfg.cbcp.fsmretry = DEF_FSMRETRY;
+ if (arg->argc > arg->argn) {
+ strncpy(cx->cfg.cbcp.phone, arg->argv[arg->argn],
+ sizeof cx->cfg.cbcp.phone - 1);
+ cx->cfg.cbcp.phone[sizeof cx->cfg.cbcp.phone - 1] = '\0';
+ if (arg->argc > arg->argn + 1) {
+ cx->cfg.cbcp.delay = atoi(arg->argv[arg->argn + 1]);
+ if (arg->argc > arg->argn + 2) {
+ long_val = atol(arg->argv[arg->argn + 2]);
+ if (long_val < MIN_FSMRETRY)
+ log_Printf(LogWARN, "%ld: Invalid CBCP FSM retry period - min %d\n",
+ long_val, MIN_FSMRETRY);
+ else
+ cx->cfg.cbcp.fsmretry = long_val;
+ }
+ }
+ }
+ break;
+
+ case VAR_CHOKED:
+ arg->bundle->cfg.choked.timeout = atoi(argp);
+ if (arg->bundle->cfg.choked.timeout <= 0)
+ arg->bundle->cfg.choked.timeout = CHOKED_TIMEOUT;
+ break;
+
+ case VAR_SENDPIPE:
+ long_val = atol(argp);
+ arg->bundle->ncp.cfg.sendpipe = long_val;
+ break;
+
+ case VAR_RECVPIPE:
+ long_val = atol(argp);
+ arg->bundle->ncp.cfg.recvpipe = long_val;
+ break;
+
+#ifndef NORADIUS
+ case VAR_RADIUS:
+ if (!*argp)
+ *arg->bundle->radius.cfg.file = '\0';
+ else if (access(argp, R_OK)) {
+ log_Printf(LogWARN, "%s: %s\n", argp, strerror(errno));
+ res = 1;
+ break;
+ } else {
+ strncpy(arg->bundle->radius.cfg.file, argp,
+ sizeof arg->bundle->radius.cfg.file - 1);
+ arg->bundle->radius.cfg.file
+ [sizeof arg->bundle->radius.cfg.file - 1] = '\0';
+ }
+ break;
+#endif
+
+ case VAR_CD:
+ if (*argp) {
+ if (strcasecmp(argp, "off")) {
+ long_val = atol(argp);
+ if (long_val < 0)
+ long_val = 0;
+ cx->physical->cfg.cd.delay = long_val;
+ cx->physical->cfg.cd.necessity = argp[strlen(argp)-1] == '!' ?
+ CD_REQUIRED : CD_VARIABLE;
+ } else
+ cx->physical->cfg.cd.necessity = CD_NOTREQUIRED;
+ } else {
+ cx->physical->cfg.cd.delay = 0;
+ cx->physical->cfg.cd.necessity = CD_DEFAULT;
+ }
+ break;
+
+ case VAR_PARITY:
+ if (arg->argc == arg->argn + 1)
+ res = physical_SetParity(arg->cx->physical, argp);
+ else {
+ log_Printf(LogWARN, "Parity value must be odd, even or none\n");
+ res = 1;
+ }
+ break;
+
+ case VAR_CRTSCTS:
+ if (strcasecmp(argp, "on") == 0)
+ physical_SetRtsCts(arg->cx->physical, 1);
+ else if (strcasecmp(argp, "off") == 0)
+ physical_SetRtsCts(arg->cx->physical, 0);
+ else {
+ log_Printf(LogWARN, "RTS/CTS value must be on or off\n");
+ res = 1;
+ }
+ break;
+
+ case VAR_URGENTPORTS:
+ if (arg->argn == arg->argc) {
+ ncp_SetUrgentTOS(&arg->bundle->ncp);
+ ncp_ClearUrgentTcpPorts(&arg->bundle->ncp);
+ ncp_ClearUrgentUdpPorts(&arg->bundle->ncp);
+ } else if (!strcasecmp(arg->argv[arg->argn], "udp")) {
+ ncp_SetUrgentTOS(&arg->bundle->ncp);
+ if (arg->argn == arg->argc - 1)
+ ncp_ClearUrgentUdpPorts(&arg->bundle->ncp);
+ else for (f = arg->argn + 1; f < arg->argc; f++)
+ if (*arg->argv[f] == '+')
+ ncp_AddUrgentUdpPort(&arg->bundle->ncp, atoi(arg->argv[f] + 1));
+ else if (*arg->argv[f] == '-')
+ ncp_RemoveUrgentUdpPort(&arg->bundle->ncp, atoi(arg->argv[f] + 1));
+ else {
+ if (f == arg->argn)
+ ncp_ClearUrgentUdpPorts(&arg->bundle->ncp);
+ ncp_AddUrgentUdpPort(&arg->bundle->ncp, atoi(arg->argv[f]));
+ }
+ } else if (arg->argn == arg->argc - 1 &&
+ !strcasecmp(arg->argv[arg->argn], "none")) {
+ ncp_ClearUrgentTcpPorts(&arg->bundle->ncp);
+ ncp_ClearUrgentUdpPorts(&arg->bundle->ncp);
+ ncp_ClearUrgentTOS(&arg->bundle->ncp);
+ } else {
+ ncp_SetUrgentTOS(&arg->bundle->ncp);
+ first = arg->argn;
+ if (!strcasecmp(arg->argv[first], "tcp") && ++first == arg->argc)
+ ncp_ClearUrgentTcpPorts(&arg->bundle->ncp);
+
+ for (f = first; f < arg->argc; f++)
+ if (*arg->argv[f] == '+')
+ ncp_AddUrgentTcpPort(&arg->bundle->ncp, atoi(arg->argv[f] + 1));
+ else if (*arg->argv[f] == '-')
+ ncp_RemoveUrgentTcpPort(&arg->bundle->ncp, atoi(arg->argv[f] + 1));
+ else {
+ if (f == first)
+ ncp_ClearUrgentTcpPorts(&arg->bundle->ncp);
+ ncp_AddUrgentTcpPort(&arg->bundle->ncp, atoi(arg->argv[f]));
+ }
+ }
+ break;
+ }
+
+ return res;
+}
+
+static struct cmdtab const SetCommands[] = {
+ {"accmap", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
+ "accmap value", "set accmap hex-value", (const void *)VAR_ACCMAP},
+ {"authkey", "key", SetVariable, LOCAL_AUTH,
+ "authentication key", "set authkey|key key", (const void *)VAR_AUTHKEY},
+ {"authname", NULL, SetVariable, LOCAL_AUTH,
+ "authentication name", "set authname name", (const void *)VAR_AUTHNAME},
+ {"autoload", NULL, SetVariable, LOCAL_AUTH,
+ "auto link [de]activation", "set autoload maxtime maxload mintime minload",
+ (const void *)VAR_AUTOLOAD},
+ {"bandwidth", NULL, mp_SetDatalinkBandwidth, LOCAL_AUTH | LOCAL_CX,
+ "datalink bandwidth", "set bandwidth value"},
+ {"callback", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
+ "callback control", "set callback [none|auth|cbcp|"
+ "E.164 *|number[,number]...]...", (const void *)VAR_CALLBACK},
+ {"cbcp", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
+ "CBCP control", "set cbcp [*|phone[,phone...] [delay [timeout]]]",
+ (const void *)VAR_CBCP},
+ {"ccpretry", "ccpretries", SetVariable, LOCAL_AUTH | LOCAL_CX_OPT,
+ "CCP retries", "set ccpretry value [attempts]", (const void *)VAR_CCPRETRY},
+ {"cd", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "Carrier delay requirement",
+ "set cd value[!]", (const void *)VAR_CD},
+ {"chapretry", "chapretries", SetVariable, LOCAL_AUTH | LOCAL_CX,
+ "CHAP retries", "set chapretry value [attempts]",
+ (const void *)VAR_CHAPRETRY},
+ {"choked", NULL, SetVariable, LOCAL_AUTH,
+ "choked timeout", "set choked [secs]", (const void *)VAR_CHOKED},
+ {"ctsrts", "crtscts", SetVariable, LOCAL_AUTH | LOCAL_CX,
+ "Use hardware flow control", "set ctsrts [on|off]",
+ (const char *)VAR_CRTSCTS},
+ {"deflate", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX_OPT,
+ "deflate window sizes", "set deflate out-winsize in-winsize",
+ (const void *) VAR_WINSIZE},
+#ifndef NODES
+ {"mppe", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX_OPT,
+ "MPPE key size and state", "set mppe [40|56|128|* [stateful|stateless|*]]",
+ (const void *) VAR_MPPE},
+#endif
+ {"device", "line", SetVariable, LOCAL_AUTH | LOCAL_CX,
+ "physical device name", "set device|line device-name[,device-name]",
+ (const void *) VAR_DEVICE},
+ {"dial", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
+ "dialing script", "set dial chat-script", (const void *) VAR_DIAL},
+ {"dns", NULL, SetVariable, LOCAL_AUTH, "Domain Name Server",
+ "set dns pri-addr [sec-addr]", (const void *)VAR_DNS},
+ {"enddisc", NULL, mp_SetEnddisc, LOCAL_AUTH,
+ "Endpoint Discriminator", "set enddisc [IP|magic|label|psn value]"},
+ {"escape", NULL, SetEscape, LOCAL_AUTH | LOCAL_CX,
+ "escape characters", "set escape hex-digit ..."},
+ {"filter", NULL, filter_Set, LOCAL_AUTH,
+ "packet filters", "set filter alive|dial|in|out rule-no permit|deny "
+ "[src_addr[/width]] [dst_addr[/width]] [proto "
+ "[src [lt|eq|gt port]] [dst [lt|eq|gt port]] [estab] [syn] [finrst]]"},
+ {"hangup", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
+ "hangup script", "set hangup chat-script", (const void *) VAR_HANGUP},
+ {"ifaddr", NULL, SetInterfaceAddr, LOCAL_AUTH, "destination address",
+ "set ifaddr [src-addr [dst-addr [netmask [trg-addr]]]]"},
+ {"ifqueue", NULL, SetVariable, LOCAL_AUTH, "interface queue",
+ "set ifqueue packets", (const void *)VAR_IFQUEUE},
+ {"ipcpretry", "ipcpretries", SetVariable, LOCAL_AUTH, "IPCP retries",
+ "set ipcpretry value [attempts]", (const void *)VAR_IPCPRETRY},
+ {"lcpretry", "lcpretries", SetVariable, LOCAL_AUTH | LOCAL_CX, "LCP retries",
+ "set lcpretry value [attempts]", (const void *)VAR_LCPRETRY},
+ {"log", NULL, log_SetLevel, LOCAL_AUTH, "log level",
+ "set log [local] [+|-]all|async|cbcp|ccp|chat|command|connect|debug|dns|hdlc|"
+ "id0|ipcp|lcp|lqm|phase|physical|sync|tcp/ip|timer|tun..."},
+ {"login", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
+ "login script", "set login chat-script", (const void *) VAR_LOGIN},
+ {"logout", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
+ "logout script", "set logout chat-script", (const void *) VAR_LOGOUT},
+ {"lqrperiod", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX_OPT,
+ "LQR period", "set lqrperiod value", (const void *)VAR_LQRPERIOD},
+ {"mode", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "mode value",
+ "set mode interactive|auto|ddial|background", (const void *)VAR_MODE},
+ {"mrru", NULL, SetVariable, LOCAL_AUTH, "MRRU value",
+ "set mrru value", (const void *)VAR_MRRU},
+ {"mru", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
+ "MRU value", "set mru [max[imum]] [value]", (const void *)VAR_MRU},
+ {"mtu", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
+ "interface MTU value", "set mtu [max[imum]] [value]", (const void *)VAR_MTU},
+ {"nbns", NULL, SetVariable, LOCAL_AUTH, "NetBIOS Name Server",
+ "set nbns pri-addr [sec-addr]", (const void *)VAR_NBNS},
+ {"openmode", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "open mode",
+ "set openmode active|passive [secs]", (const void *)VAR_OPENMODE},
+ {"papretry", "papretries", SetVariable, LOCAL_AUTH | LOCAL_CX, "PAP retries",
+ "set papretry value [attempts]", (const void *)VAR_PAPRETRY},
+ {"parity", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "serial parity",
+ "set parity [odd|even|none]", (const void *)VAR_PARITY},
+ {"phone", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "telephone number(s)",
+ "set phone phone1[:phone2[...]]", (const void *)VAR_PHONE},
+ {"proctitle", "title", SetProcTitle, LOCAL_AUTH,
+ "Process title", "set proctitle [value]"},
+#ifndef NORADIUS
+ {"radius", NULL, SetVariable, LOCAL_AUTH,
+ "RADIUS Config", "set radius cfgfile", (const void *)VAR_RADIUS},
+#endif
+ {"reconnect", NULL, datalink_SetReconnect, LOCAL_AUTH | LOCAL_CX,
+ "Reconnect timeout", "set reconnect value ntries"},
+ {"recvpipe", NULL, SetVariable, LOCAL_AUTH,
+ "RECVPIPE value", "set recvpipe value", (const void *)VAR_RECVPIPE},
+ {"redial", NULL, datalink_SetRedial, LOCAL_AUTH | LOCAL_CX,
+ "Redial timeout", "set redial secs[+inc[-incmax]][.next] [attempts]"},
+ {"sendpipe", NULL, SetVariable, LOCAL_AUTH,
+ "SENDPIPE value", "set sendpipe value", (const void *)VAR_SENDPIPE},
+ {"server", "socket", SetServer, LOCAL_AUTH, "diagnostic port",
+ "set server|socket TcpPort|LocalName|none|open|closed [password [mask]]"},
+ {"speed", NULL, SetModemSpeed, LOCAL_AUTH | LOCAL_CX,
+ "physical speed", "set speed value|sync"},
+ {"stopped", NULL, SetStoppedTimeout, LOCAL_AUTH | LOCAL_CX,
+ "STOPPED timeouts", "set stopped [LCPseconds [CCPseconds]]"},
+ {"timeout", NULL, SetVariable, LOCAL_AUTH, "Idle timeout",
+ "set timeout idletime", (const void *)VAR_IDLETIMEOUT},
+ {"urgent", NULL, SetVariable, LOCAL_AUTH, "urgent ports",
+ "set urgent [tcp|udp] [+|-]port...", (const void *)VAR_URGENTPORTS},
+ {"vj", NULL, ipcp_vjset, LOCAL_AUTH,
+ "vj values", "set vj slots|slotcomp [value]"},
+ {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
+ "Display this message", "set help|? [command]", SetCommands},
+ {NULL, NULL, NULL},
+};
+
+static int
+SetCommand(struct cmdargs const *arg)
+{
+ if (arg->argc > arg->argn)
+ FindExec(arg->bundle, SetCommands, arg->argc, arg->argn, arg->argv,
+ arg->prompt, arg->cx);
+ else if (arg->prompt)
+ prompt_Printf(arg->prompt, "Use `set ?' to get a list or `set ? <var>' for"
+ " syntax help.\n");
+ else
+ log_Printf(LogWARN, "set command must have arguments\n");
+
+ return 0;
+}
+
+static int
+AddCommand(struct cmdargs const *arg)
+{
+ struct ncpaddr gw;
+ struct ncprange dest;
+ struct in_addr host;
+ int dest_default, gw_arg, addrs;
+
+ if (arg->argc != arg->argn+3 && arg->argc != arg->argn+2)
+ return -1;
+
+ addrs = 0;
+ dest_default = 0;
+ if (arg->argc == arg->argn + 2) {
+ if (!strcasecmp(arg->argv[arg->argn], "default"))
+ dest_default = 1;
+ else {
+ if (!ncprange_aton(&dest, &arg->bundle->ncp, arg->argv[arg->argn]))
+ return -1;
+ if (!strncasecmp(arg->argv[arg->argn], "MYADDR", 6))
+ addrs = ROUTE_DSTMYADDR;
+ else if (!strncasecmp(arg->argv[arg->argn], "MYADDR6", 7))
+ addrs = ROUTE_DSTMYADDR6;
+ else if (!strncasecmp(arg->argv[arg->argn], "HISADDR", 7))
+ addrs = ROUTE_DSTHISADDR;
+ else if (!strncasecmp(arg->argv[arg->argn], "HISADDR6", 8))
+ addrs = ROUTE_DSTHISADDR6;
+ else if (!strncasecmp(arg->argv[arg->argn], "DNS0", 4))
+ addrs = ROUTE_DSTDNS0;
+ else if (!strncasecmp(arg->argv[arg->argn], "DNS1", 4))
+ addrs = ROUTE_DSTDNS1;
+ }
+ gw_arg = 1;
+ } else {
+ if (strcasecmp(arg->argv[arg->argn], "MYADDR") == 0) {
+ addrs = ROUTE_DSTMYADDR;
+ host = arg->bundle->ncp.ipcp.my_ip;
+ } else if (strcasecmp(arg->argv[arg->argn], "HISADDR") == 0) {
+ addrs = ROUTE_DSTHISADDR;
+ host = arg->bundle->ncp.ipcp.peer_ip;
+ } else if (strcasecmp(arg->argv[arg->argn], "DNS0") == 0) {
+ addrs = ROUTE_DSTDNS0;
+ host = arg->bundle->ncp.ipcp.ns.dns[0];
+ } else if (strcasecmp(arg->argv[arg->argn], "DNS1") == 0) {
+ addrs = ROUTE_DSTDNS1;
+ host = arg->bundle->ncp.ipcp.ns.dns[1];
+ } else {
+ host = GetIpAddr(arg->argv[arg->argn]);
+ if (host.s_addr == INADDR_NONE) {
+ log_Printf(LogWARN, "%s: Invalid destination address\n",
+ arg->argv[arg->argn]);
+ return -1;
+ }
+ }
+ ncprange_setip4(&dest, host, GetIpAddr(arg->argv[arg->argn + 1]));
+ gw_arg = 2;
+ }
+
+ if (strcasecmp(arg->argv[arg->argn + gw_arg], "HISADDR") == 0) {
+ ncpaddr_setip4(&gw, arg->bundle->ncp.ipcp.peer_ip);
+ addrs |= ROUTE_GWHISADDR;
+#ifndef NOINET6
+ } else if (strcasecmp(arg->argv[arg->argn + gw_arg], "HISADDR6") == 0) {
+ ncpaddr_copy(&gw, &arg->bundle->ncp.ipv6cp.hisaddr);
+ addrs |= ROUTE_GWHISADDR6;
+#endif
+ } else {
+ if (!ncpaddr_aton(&gw, &arg->bundle->ncp, arg->argv[arg->argn + gw_arg])) {
+ log_Printf(LogWARN, "%s: Invalid gateway address\n",
+ arg->argv[arg->argn + gw_arg]);
+ return -1;
+ }
+ }
+
+ if (dest_default)
+ ncprange_setdefault(&dest, ncpaddr_family(&gw));
+
+ if (rt_Set(arg->bundle, RTM_ADD, &dest, &gw, arg->cmd->args ? 1 : 0,
+ ((addrs & ROUTE_GWHISADDR) || (addrs & ROUTE_GWHISADDR6)) ? 1 : 0)
+ && addrs != ROUTE_STATIC)
+ route_Add(&arg->bundle->ncp.route, addrs, &dest, &gw);
+
+ return 0;
+}
+
+static int
+DeleteCommand(struct cmdargs const *arg)
+{
+ struct ncprange dest;
+ int addrs;
+
+ if (arg->argc == arg->argn+1) {
+ if(strcasecmp(arg->argv[arg->argn], "all") == 0) {
+ route_IfDelete(arg->bundle, 0);
+ route_DeleteAll(&arg->bundle->ncp.route);
+ } else {
+ addrs = 0;
+ if (strcasecmp(arg->argv[arg->argn], "MYADDR") == 0) {
+ ncprange_setip4host(&dest, arg->bundle->ncp.ipcp.my_ip);
+ addrs = ROUTE_DSTMYADDR;
+#ifndef NOINET6
+ } else if (strcasecmp(arg->argv[arg->argn], "MYADDR6") == 0) {
+ ncprange_sethost(&dest, &arg->bundle->ncp.ipv6cp.myaddr);
+ addrs = ROUTE_DSTMYADDR6;
+#endif
+ } else if (strcasecmp(arg->argv[arg->argn], "HISADDR") == 0) {
+ ncprange_setip4host(&dest, arg->bundle->ncp.ipcp.peer_ip);
+ addrs = ROUTE_DSTHISADDR;
+#ifndef NOINET6
+ } else if (strcasecmp(arg->argv[arg->argn], "HISADDR6") == 0) {
+ ncprange_sethost(&dest, &arg->bundle->ncp.ipv6cp.hisaddr);
+ addrs = ROUTE_DSTHISADDR6;
+#endif
+ } else if (strcasecmp(arg->argv[arg->argn], "DNS0") == 0) {
+ ncprange_setip4host(&dest, arg->bundle->ncp.ipcp.ns.dns[0]);
+ addrs = ROUTE_DSTDNS0;
+ } else if (strcasecmp(arg->argv[arg->argn], "DNS1") == 0) {
+ ncprange_setip4host(&dest, arg->bundle->ncp.ipcp.ns.dns[1]);
+ addrs = ROUTE_DSTDNS1;
+ } else {
+ ncprange_aton(&dest, &arg->bundle->ncp, arg->argv[arg->argn]);
+ addrs = ROUTE_STATIC;
+ }
+ rt_Set(arg->bundle, RTM_DELETE, &dest, NULL, arg->cmd->args ? 1 : 0, 0);
+ route_Delete(&arg->bundle->ncp.route, addrs, &dest);
+ }
+ } else
+ return -1;
+
+ return 0;
+}
+
+#ifndef NONAT
+static int
+NatEnable(struct cmdargs const *arg)
+{
+ if (arg->argc == arg->argn+1) {
+ if (strcasecmp(arg->argv[arg->argn], "yes") == 0) {
+ if (!arg->bundle->NatEnabled) {
+ if (arg->bundle->ncp.ipcp.fsm.state == ST_OPENED)
+ PacketAliasSetAddress(arg->bundle->ncp.ipcp.my_ip);
+ arg->bundle->NatEnabled = 1;
+ }
+ return 0;
+ } else if (strcasecmp(arg->argv[arg->argn], "no") == 0) {
+ arg->bundle->NatEnabled = 0;
+ arg->bundle->cfg.opt &= ~OPT_IFACEALIAS;
+ /* Don't iface_Clear() - there may be manually configured addresses */
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+
+static int
+NatOption(struct cmdargs const *arg)
+{
+ long param = (long)arg->cmd->args;
+
+ if (arg->argc == arg->argn+1) {
+ if (strcasecmp(arg->argv[arg->argn], "yes") == 0) {
+ if (arg->bundle->NatEnabled) {
+ PacketAliasSetMode(param, param);
+ return 0;
+ }
+ log_Printf(LogWARN, "nat not enabled\n");
+ } else if (strcmp(arg->argv[arg->argn], "no") == 0) {
+ if (arg->bundle->NatEnabled) {
+ PacketAliasSetMode(0, param);
+ return 0;
+ }
+ log_Printf(LogWARN, "nat not enabled\n");
+ }
+ }
+ return -1;
+}
+#endif /* #ifndef NONAT */
+
+static int
+LinkCommand(struct cmdargs const *arg)
+{
+ if (arg->argc > arg->argn+1) {
+ char namelist[LINE_LEN];
+ struct datalink *cx;
+ char *name;
+ int result = 0;
+
+ if (!strcmp(arg->argv[arg->argn], "*")) {
+ struct datalink *dl;
+
+ cx = arg->bundle->links;
+ while (cx) {
+ /* Watch it, the command could be a ``remove'' */
+ dl = cx->next;
+ FindExec(arg->bundle, Commands, arg->argc, arg->argn+1, arg->argv,
+ arg->prompt, cx);
+ for (cx = arg->bundle->links; cx; cx = cx->next)
+ if (cx == dl)
+ break; /* Pointer's still valid ! */
+ }
+ } else {
+ strncpy(namelist, arg->argv[arg->argn], sizeof namelist - 1);
+ namelist[sizeof namelist - 1] = '\0';
+ for(name = strtok(namelist, ", "); name; name = strtok(NULL,", "))
+ if (!bundle2datalink(arg->bundle, name)) {
+ log_Printf(LogWARN, "link: %s: Invalid link name\n", name);
+ return 1;
+ }
+
+ strncpy(namelist, arg->argv[arg->argn], sizeof namelist - 1);
+ namelist[sizeof namelist - 1] = '\0';
+ for(name = strtok(namelist, ", "); name; name = strtok(NULL,", ")) {
+ cx = bundle2datalink(arg->bundle, name);
+ if (cx)
+ FindExec(arg->bundle, Commands, arg->argc, arg->argn+1, arg->argv,
+ arg->prompt, cx);
+ else {
+ log_Printf(LogWARN, "link: %s: Invalidated link name !\n", name);
+ result++;
+ }
+ }
+ }
+ return result;
+ }
+
+ log_Printf(LogWARN, "usage: %s\n", arg->cmd->syntax);
+ return 2;
+}
+
+struct link *
+command_ChooseLink(struct cmdargs const *arg)
+{
+ if (arg->cx)
+ return &arg->cx->physical->link;
+ else if (!arg->bundle->ncp.mp.cfg.mrru) {
+ struct datalink *dl = bundle2datalink(arg->bundle, NULL);
+ if (dl)
+ return &dl->physical->link;
+ }
+ return &arg->bundle->ncp.mp.link;
+}
+
+static const char *
+ident_cmd(const char *cmd, unsigned *keep, unsigned *add)
+{
+ const char *result;
+
+ switch (*cmd) {
+ case 'A':
+ case 'a':
+ result = "accept";
+ *keep = NEG_MYMASK;
+ *add = NEG_ACCEPTED;
+ break;
+ case 'D':
+ case 'd':
+ switch (cmd[1]) {
+ case 'E':
+ case 'e':
+ result = "deny";
+ *keep = NEG_MYMASK;
+ *add = 0;
+ break;
+ case 'I':
+ case 'i':
+ result = "disable";
+ *keep = NEG_HISMASK;
+ *add = 0;
+ break;
+ default:
+ return NULL;
+ }
+ break;
+ case 'E':
+ case 'e':
+ result = "enable";
+ *keep = NEG_HISMASK;
+ *add = NEG_ENABLED;
+ break;
+ default:
+ return NULL;
+ }
+
+ return result;
+}
+
+static int
+OptSet(struct cmdargs const *arg)
+{
+ int bit = (int)(long)arg->cmd->args;
+ unsigned keep; /* Keep these bits */
+ unsigned add; /* Add these bits */
+
+ if (ident_cmd(arg->argv[arg->argn - 2], &keep, &add) == NULL)
+ return 1;
+
+#ifndef NOINET6
+ if (add == NEG_ENABLED && bit == OPT_IPV6CP && !probe.ipv6_available) {
+ log_Printf(LogWARN, "IPv6 is not available on this machine\n");
+ return 1;
+ }
+#endif
+
+ if (add)
+ arg->bundle->cfg.opt |= bit;
+ else
+ arg->bundle->cfg.opt &= ~bit;
+
+ return 0;
+}
+
+static int
+IfaceAliasOptSet(struct cmdargs const *arg)
+{
+ unsigned save = arg->bundle->cfg.opt;
+ int result = OptSet(arg);
+
+ if (result == 0)
+ if (Enabled(arg->bundle, OPT_IFACEALIAS) && !arg->bundle->NatEnabled) {
+ arg->bundle->cfg.opt = save;
+ log_Printf(LogWARN, "Cannot enable iface-alias without NAT\n");
+ result = 2;
+ }
+
+ return result;
+}
+
+static int
+NegotiateSet(struct cmdargs const *arg)
+{
+ long param = (long)arg->cmd->args;
+ struct link *l = command_ChooseLink(arg); /* LOCAL_CX_OPT uses this */
+ struct datalink *cx = arg->cx; /* LOCAL_CX uses this */
+ const char *cmd;
+ unsigned keep; /* Keep these bits */
+ unsigned add; /* Add these bits */
+
+ if ((cmd = ident_cmd(arg->argv[arg->argn-2], &keep, &add)) == NULL)
+ return 1;
+
+ if ((arg->cmd->lauth & LOCAL_CX) && !cx) {
+ log_Printf(LogWARN, "%s %s: No context (use the `link' command)\n",
+ cmd, arg->cmd->name);
+ return 2;
+ } else if (cx && !(arg->cmd->lauth & (LOCAL_CX|LOCAL_CX_OPT))) {
+ log_Printf(LogWARN, "%s %s: Redundant context (%s) ignored\n",
+ cmd, arg->cmd->name, cx->name);
+ cx = NULL;
+ }
+
+ switch (param) {
+ case NEG_ACFCOMP:
+ cx->physical->link.lcp.cfg.acfcomp &= keep;
+ cx->physical->link.lcp.cfg.acfcomp |= add;
+ break;
+ case NEG_CHAP05:
+ cx->physical->link.lcp.cfg.chap05 &= keep;
+ cx->physical->link.lcp.cfg.chap05 |= add;
+ break;
+#ifndef NODES
+ case NEG_CHAP80:
+ cx->physical->link.lcp.cfg.chap80nt &= keep;
+ cx->physical->link.lcp.cfg.chap80nt |= add;
+ break;
+ case NEG_CHAP80LM:
+ cx->physical->link.lcp.cfg.chap80lm &= keep;
+ cx->physical->link.lcp.cfg.chap80lm |= add;
+ break;
+ case NEG_CHAP81:
+ cx->physical->link.lcp.cfg.chap81 &= keep;
+ cx->physical->link.lcp.cfg.chap81 |= add;
+ break;
+ case NEG_MPPE:
+ l->ccp.cfg.neg[CCP_NEG_MPPE] &= keep;
+ l->ccp.cfg.neg[CCP_NEG_MPPE] |= add;
+ break;
+#endif
+ case NEG_DEFLATE:
+ l->ccp.cfg.neg[CCP_NEG_DEFLATE] &= keep;
+ l->ccp.cfg.neg[CCP_NEG_DEFLATE] |= add;
+ break;
+ case NEG_DNS:
+ arg->bundle->ncp.ipcp.cfg.ns.dns_neg &= keep;
+ arg->bundle->ncp.ipcp.cfg.ns.dns_neg |= add;
+ break;
+ case NEG_ENDDISC:
+ arg->bundle->ncp.mp.cfg.negenddisc &= keep;
+ arg->bundle->ncp.mp.cfg.negenddisc |= add;
+ break;
+ case NEG_LQR:
+ cx->physical->link.lcp.cfg.lqr &= keep;
+ cx->physical->link.lcp.cfg.lqr |= add;
+ break;
+ case NEG_PAP:
+ cx->physical->link.lcp.cfg.pap &= keep;
+ cx->physical->link.lcp.cfg.pap |= add;
+ break;
+ case NEG_PPPDDEFLATE:
+ l->ccp.cfg.neg[CCP_NEG_DEFLATE24] &= keep;
+ l->ccp.cfg.neg[CCP_NEG_DEFLATE24] |= add;
+ break;
+ case NEG_PRED1:
+ l->ccp.cfg.neg[CCP_NEG_PRED1] &= keep;
+ l->ccp.cfg.neg[CCP_NEG_PRED1] |= add;
+ break;
+ case NEG_PROTOCOMP:
+ cx->physical->link.lcp.cfg.protocomp &= keep;
+ cx->physical->link.lcp.cfg.protocomp |= add;
+ break;
+ case NEG_SHORTSEQ:
+ switch (bundle_Phase(arg->bundle)) {
+ case PHASE_DEAD:
+ break;
+ case PHASE_ESTABLISH:
+ /* Make sure none of our links are DATALINK_LCP or greater */
+ if (bundle_HighestState(arg->bundle) >= DATALINK_LCP) {
+ log_Printf(LogWARN, "shortseq: Only changable before"
+ " LCP negotiations\n");
+ return 1;
+ }
+ break;
+ default:
+ log_Printf(LogWARN, "shortseq: Only changable at phase"
+ " DEAD/ESTABLISH\n");
+ return 1;
+ }
+ arg->bundle->ncp.mp.cfg.shortseq &= keep;
+ arg->bundle->ncp.mp.cfg.shortseq |= add;
+ break;
+ case NEG_VJCOMP:
+ arg->bundle->ncp.ipcp.cfg.vj.neg &= keep;
+ arg->bundle->ncp.ipcp.cfg.vj.neg |= add;
+ break;
+ }
+
+ return 0;
+}
+
+static struct cmdtab const NegotiateCommands[] = {
+ {"filter-decapsulation", NULL, OptSet, LOCAL_AUTH,
+ "filter on PPPoUDP payloads", "disable|enable",
+ (const void *)OPT_FILTERDECAP},
+ {"idcheck", NULL, OptSet, LOCAL_AUTH, "Check FSM reply ids",
+ "disable|enable", (const void *)OPT_IDCHECK},
+ {"iface-alias", NULL, IfaceAliasOptSet, LOCAL_AUTH,
+ "retain interface addresses", "disable|enable",
+ (const void *)OPT_IFACEALIAS},
+#ifndef NOINET6
+ {"ipcp", NULL, OptSet, LOCAL_AUTH, "IP Network Control Protocol",
+ "disable|enable", (const void *)OPT_IPCP},
+ {"ipv6cp", NULL, OptSet, LOCAL_AUTH, "IPv6 Network Control Protocol",
+ "disable|enable", (const void *)OPT_IPV6CP},
+#endif
+ {"keep-session", NULL, OptSet, LOCAL_AUTH, "Retain device session leader",
+ "disable|enable", (const void *)OPT_KEEPSESSION},
+ {"loopback", NULL, OptSet, LOCAL_AUTH, "Loop packets for local iface",
+ "disable|enable", (const void *)OPT_LOOPBACK},
+ {"passwdauth", NULL, OptSet, LOCAL_AUTH, "Use passwd file",
+ "disable|enable", (const void *)OPT_PASSWDAUTH},
+ {"proxy", NULL, OptSet, LOCAL_AUTH, "Create a proxy ARP entry",
+ "disable|enable", (const void *)OPT_PROXY},
+ {"proxyall", NULL, OptSet, LOCAL_AUTH, "Proxy ARP for all remote hosts",
+ "disable|enable", (const void *)OPT_PROXYALL},
+ {"sroutes", NULL, OptSet, LOCAL_AUTH, "Use sticky routes",
+ "disable|enable", (const void *)OPT_SROUTES},
+ {"tcpmssfixup", "mssfixup", OptSet, LOCAL_AUTH, "Modify MSS options",
+ "disable|enable", (const void *)OPT_TCPMSSFIXUP},
+ {"throughput", NULL, OptSet, LOCAL_AUTH, "Rolling throughput",
+ "disable|enable", (const void *)OPT_THROUGHPUT},
+ {"utmp", NULL, OptSet, LOCAL_AUTH, "Log connections in utmp",
+ "disable|enable", (const void *)OPT_UTMP},
+
+#ifndef NOINET6
+#define OPT_MAX 13 /* accept/deny allowed below and not above */
+#else
+#define OPT_MAX 11
+#endif
+
+ {"acfcomp", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX,
+ "Address & Control field compression", "accept|deny|disable|enable",
+ (const void *)NEG_ACFCOMP},
+ {"chap", "chap05", NegotiateSet, LOCAL_AUTH | LOCAL_CX,
+ "Challenge Handshake Authentication Protocol", "accept|deny|disable|enable",
+ (const void *)NEG_CHAP05},
+#ifndef NODES
+ {"mschap", "chap80nt", NegotiateSet, LOCAL_AUTH | LOCAL_CX,
+ "Microsoft (NT) CHAP", "accept|deny|disable|enable",
+ (const void *)NEG_CHAP80},
+ {"LANMan", "chap80lm", NegotiateSet, LOCAL_AUTH | LOCAL_CX,
+ "Microsoft (NT) CHAP", "accept|deny|disable|enable",
+ (const void *)NEG_CHAP80LM},
+ {"mschapv2", "chap81", NegotiateSet, LOCAL_AUTH | LOCAL_CX,
+ "Microsoft CHAP v2", "accept|deny|disable|enable",
+ (const void *)NEG_CHAP81},
+ {"mppe", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX_OPT,
+ "MPPE encryption", "accept|deny|disable|enable",
+ (const void *)NEG_MPPE},
+#endif
+ {"deflate", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX_OPT,
+ "Deflate compression", "accept|deny|disable|enable",
+ (const void *)NEG_DEFLATE},
+ {"deflate24", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX_OPT,
+ "Deflate (type 24) compression", "accept|deny|disable|enable",
+ (const void *)NEG_PPPDDEFLATE},
+ {"dns", NULL, NegotiateSet, LOCAL_AUTH,
+ "DNS specification", "accept|deny|disable|enable", (const void *)NEG_DNS},
+ {"enddisc", NULL, NegotiateSet, LOCAL_AUTH, "ENDDISC negotiation",
+ "accept|deny|disable|enable", (const void *)NEG_ENDDISC},
+ {"lqr", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX,
+ "Link Quality Reports", "accept|deny|disable|enable",
+ (const void *)NEG_LQR},
+ {"pap", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX,
+ "Password Authentication protocol", "accept|deny|disable|enable",
+ (const void *)NEG_PAP},
+ {"pred1", "predictor1", NegotiateSet, LOCAL_AUTH | LOCAL_CX_OPT,
+ "Predictor 1 compression", "accept|deny|disable|enable",
+ (const void *)NEG_PRED1},
+ {"protocomp", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX,
+ "Protocol field compression", "accept|deny|disable|enable",
+ (const void *)NEG_PROTOCOMP},
+ {"shortseq", NULL, NegotiateSet, LOCAL_AUTH,
+ "MP Short Sequence Numbers", "accept|deny|disable|enable",
+ (const void *)NEG_SHORTSEQ},
+ {"vjcomp", NULL, NegotiateSet, LOCAL_AUTH,
+ "Van Jacobson header compression", "accept|deny|disable|enable",
+ (const void *)NEG_VJCOMP},
+ {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
+ "Display this message", "accept|deny|disable|enable help|? [value]",
+ NegotiateCommands},
+ {NULL, NULL, NULL},
+};
+
+static int
+NegotiateCommand(struct cmdargs const *arg)
+{
+ if (arg->argc > arg->argn) {
+ char const *argv[3];
+ unsigned keep, add;
+ int n;
+
+ if ((argv[0] = ident_cmd(arg->argv[arg->argn-1], &keep, &add)) == NULL)
+ return -1;
+ argv[2] = NULL;
+
+ for (n = arg->argn; n < arg->argc; n++) {
+ argv[1] = arg->argv[n];
+ FindExec(arg->bundle, NegotiateCommands + (keep == NEG_HISMASK ?
+ 0 : OPT_MAX), 2, 1, argv, arg->prompt, arg->cx);
+ }
+ } else if (arg->prompt)
+ prompt_Printf(arg->prompt, "Use `%s ?' to get a list.\n",
+ arg->argv[arg->argn-1]);
+ else
+ log_Printf(LogWARN, "%s command must have arguments\n",
+ arg->argv[arg->argn] );
+
+ return 0;
+}
+
+const char *
+command_ShowNegval(unsigned val)
+{
+ switch (val&3) {
+ case 1: return "disabled & accepted";
+ case 2: return "enabled & denied";
+ case 3: return "enabled & accepted";
+ }
+ return "disabled & denied";
+}
+
+static int
+ClearCommand(struct cmdargs const *arg)
+{
+ struct pppThroughput *t;
+ struct datalink *cx;
+ int i, clear_type;
+
+ if (arg->argc < arg->argn + 1)
+ return -1;
+
+ if (strcasecmp(arg->argv[arg->argn], "physical") == 0) {
+ cx = arg->cx;
+ if (!cx)
+ cx = bundle2datalink(arg->bundle, NULL);
+ if (!cx) {
+ log_Printf(LogWARN, "A link must be specified for ``clear physical''\n");
+ return 1;
+ }
+ t = &cx->physical->link.stats.total;
+ } else if (strcasecmp(arg->argv[arg->argn], "ipcp") == 0)
+ t = &arg->bundle->ncp.ipcp.throughput;
+#ifndef NOINET6
+ else if (strcasecmp(arg->argv[arg->argn], "ipv6cp") == 0)
+ t = &arg->bundle->ncp.ipv6cp.throughput;
+#endif
+ else
+ return -1;
+
+ if (arg->argc > arg->argn + 1) {
+ clear_type = 0;
+ for (i = arg->argn + 1; i < arg->argc; i++)
+ if (strcasecmp(arg->argv[i], "overall") == 0)
+ clear_type |= THROUGHPUT_OVERALL;
+ else if (strcasecmp(arg->argv[i], "current") == 0)
+ clear_type |= THROUGHPUT_CURRENT;
+ else if (strcasecmp(arg->argv[i], "peak") == 0)
+ clear_type |= THROUGHPUT_PEAK;
+ else
+ return -1;
+ } else
+ clear_type = THROUGHPUT_ALL;
+
+ throughput_clear(t, clear_type, arg->prompt);
+ return 0;
+}
+
+static int
+RunListCommand(struct cmdargs const *arg)
+{
+ const char *cmd = arg->argc ? arg->argv[arg->argc - 1] : "???";
+
+#ifndef NONAT
+ if (arg->cmd->args == NatCommands &&
+ tolower(*arg->argv[arg->argn - 1]) == 'a') {
+ if (arg->prompt)
+ prompt_Printf(arg->prompt, "The alias command is deprecated\n");
+ else
+ log_Printf(LogWARN, "The alias command is deprecated\n");
+ }
+#endif
+
+ if (arg->argc > arg->argn)
+ FindExec(arg->bundle, arg->cmd->args, arg->argc, arg->argn, arg->argv,
+ arg->prompt, arg->cx);
+ else if (arg->prompt)
+ prompt_Printf(arg->prompt, "Use `%s help' to get a list or `%s help"
+ " <option>' for syntax help.\n", cmd, cmd);
+ else
+ log_Printf(LogWARN, "%s command must have arguments\n", cmd);
+
+ return 0;
+}
+
+static int
+IfaceAddCommand(struct cmdargs const *arg)
+{
+ struct ncpaddr peer, addr;
+ struct ncprange ifa;
+ struct in_addr mask;
+ int n, how;
+
+ if (arg->argc == arg->argn + 1) {
+ if (!ncprange_aton(&ifa, NULL, arg->argv[arg->argn]))
+ return -1;
+ ncpaddr_init(&peer);
+ } else {
+ if (arg->argc == arg->argn + 2) {
+ if (!ncprange_aton(&ifa, NULL, arg->argv[arg->argn]))
+ return -1;
+ n = 1;
+ } else if (arg->argc == arg->argn + 3) {
+ if (!ncpaddr_aton(&addr, NULL, arg->argv[arg->argn]))
+ return -1;
+ if (ncpaddr_family(&addr) != AF_INET)
+ return -1;
+ ncprange_sethost(&ifa, &addr);
+ if (!ncpaddr_aton(&addr, NULL, arg->argv[arg->argn + 1]))
+ return -1;
+ if (!ncpaddr_getip4(&addr, &mask))
+ return -1;
+ if (!ncprange_setip4mask(&ifa, mask))
+ return -1;
+ n = 2;
+ } else
+ return -1;
+
+ if (!ncpaddr_aton(&peer, NULL, arg->argv[arg->argn + n]))
+ return -1;
+
+ if (ncprange_family(&ifa) != ncpaddr_family(&peer)) {
+ log_Printf(LogWARN, "IfaceAddCommand: src and dst address families"
+ " differ\n");
+ return -1;
+ }
+ }
+
+ how = IFACE_ADD_LAST;
+ if (arg->cmd->args)
+ how |= IFACE_FORCE_ADD;
+
+ return !iface_Add(arg->bundle->iface, &arg->bundle->ncp, &ifa, &peer, how);
+}
+
+static int
+IfaceDeleteCommand(struct cmdargs const *arg)
+{
+ struct ncpaddr ifa;
+ struct in_addr ifa4;
+ int ok;
+
+ if (arg->argc != arg->argn + 1)
+ return -1;
+
+ if (!ncpaddr_aton(&ifa, NULL, arg->argv[arg->argn]))
+ return -1;
+
+ if (arg->bundle->ncp.ipcp.fsm.state == ST_OPENED &&
+ ncpaddr_getip4(&ifa, &ifa4) &&
+ arg->bundle->ncp.ipcp.my_ip.s_addr == ifa4.s_addr) {
+ log_Printf(LogWARN, "%s: Cannot remove active interface address\n",
+ ncpaddr_ntoa(&ifa));
+ return 1;
+ }
+
+ ok = iface_Delete(arg->bundle->iface, &arg->bundle->ncp, &ifa);
+ if (!ok) {
+ if (arg->cmd->args)
+ ok = 1;
+ else if (arg->prompt)
+ prompt_Printf(arg->prompt, "%s: No such interface address\n",
+ ncpaddr_ntoa(&ifa));
+ else
+ log_Printf(LogWARN, "%s: No such interface address\n",
+ ncpaddr_ntoa(&ifa));
+ }
+
+ return !ok;
+}
+
+static int
+IfaceClearCommand(struct cmdargs const *arg)
+{
+ int family, how;
+
+ family = 0;
+ if (arg->argc == arg->argn + 1) {
+ if (strcasecmp(arg->argv[arg->argn], "inet") == 0)
+ family = AF_INET;
+#ifndef NOINET6
+ else if (strcasecmp(arg->argv[arg->argn], "inet6") == 0)
+ family = AF_INET6;
+#endif
+ else
+ return -1;
+ } else if (arg->argc != arg->argn)
+ return -1;
+
+ how = arg->bundle->ncp.ipcp.fsm.state == ST_OPENED ||
+ arg->bundle->phys_type.all & PHYS_AUTO ?
+ IFACE_CLEAR_ALIASES : IFACE_CLEAR_ALL;
+ iface_Clear(arg->bundle->iface, &arg->bundle->ncp, family, how);
+
+ return 0;
+}
+
+static int
+SetProcTitle(struct cmdargs const *arg)
+{
+ static char title[LINE_LEN];
+ char *argv[MAXARGS];
+ int argc = arg->argc - arg->argn;
+
+ if (arg->argc == arg->argn) {
+ SetTitle(NULL);
+ return 0;
+ }
+
+ if (argc >= sizeof argv / sizeof argv[0]) {
+ argc = sizeof argv / sizeof argv[0] - 1;
+ log_Printf(LogWARN, "Truncating proc title to %d args\n", argc);
+ }
+ command_Expand(argv, argc, arg->argv + arg->argn, arg->bundle, 1, getpid());
+ Concatinate(title, sizeof title, argc, (const char *const *)argv);
+ SetTitle(title);
+ command_Free(argc, argv);
+
+ return 0;
+}
diff --git a/usr.sbin/ppp/command.h b/usr.sbin/ppp/command.h
new file mode 100644
index 0000000..8eb1e38
--- /dev/null
+++ b/usr.sbin/ppp/command.h
@@ -0,0 +1,75 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct cmdtab;
+struct bundle;
+struct datalink;
+struct prompt;
+
+struct cmdargs {
+ struct cmdtab const *cmdtab; /* The entire command table */
+ struct cmdtab const *cmd; /* This command entry */
+ int argc; /* Number of arguments (excluding cmd */
+ int argn; /* Argument to start processing from */
+ char const *const *argv; /* Arguments */
+ struct bundle *bundle; /* Our bundle */
+ struct datalink *cx; /* Our context */
+ struct prompt *prompt; /* Who executed us */
+};
+
+struct cmdtab {
+ const char *name;
+ const char *alias;
+ int (*func) (struct cmdargs const *);
+ u_char lauth;
+ const char *helpmes;
+ const char *syntax;
+ const void *args;
+};
+
+#define NEG_ACCEPTED (1)
+#define NEG_ENABLED (2)
+#define IsAccepted(x) ((x) & NEG_ACCEPTED)
+#define IsEnabled(x) ((x) & NEG_ENABLED)
+
+extern const char Version[];
+
+extern void command_Expand(char **, int, char const *const *, struct bundle *,
+ int, pid_t);
+extern void command_Free(int, char **);
+extern int command_Expand_Interpret(char *, int, char *vector[MAXARGS], int);
+extern int command_Interpret(char *, int, char *vector[MAXARGS]);
+extern void command_Run(struct bundle *, int, char const *const *,
+ struct prompt *, const char *, struct datalink *);
+extern int command_Decode(struct bundle *, char *, int, struct prompt *,
+ const char *);
+extern struct link *command_ChooseLink(struct cmdargs const *);
+extern const char *command_ShowNegval(unsigned);
+
diff --git a/usr.sbin/ppp/datalink.c b/usr.sbin/ppp/datalink.c
new file mode 100644
index 0000000..048a21b
--- /dev/null
+++ b/usr.sbin/ppp/datalink.c
@@ -0,0 +1,1468 @@
+/*-
+ * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/uio.h>
+#include <termios.h>
+
+#include "layer.h"
+#include "mbuf.h"
+#include "log.h"
+#include "defs.h"
+#include "timer.h"
+#include "fsm.h"
+#include "descriptor.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "lcp.h"
+#include "async.h"
+#include "throughput.h"
+#include "ccp.h"
+#include "link.h"
+#include "physical.h"
+#include "iplist.h"
+#include "slcompress.h"
+#include "ncpaddr.h"
+#include "ipcp.h"
+#include "filter.h"
+#include "mp.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+#include "chat.h"
+#include "auth.h"
+#include "prompt.h"
+#include "proto.h"
+#include "pap.h"
+#include "chap.h"
+#include "command.h"
+#include "cbcp.h"
+#include "datalink.h"
+
+static void datalink_LoginDone(struct datalink *);
+static void datalink_NewState(struct datalink *, int);
+
+static void
+datalink_OpenTimeout(void *v)
+{
+ struct datalink *dl = (struct datalink *)v;
+
+ timer_Stop(&dl->dial.timer);
+ if (dl->state == DATALINK_OPENING)
+ log_Printf(LogCHAT, "%s: Redial timer expired.\n", dl->name);
+}
+
+static int
+datalink_StartDialTimer(struct datalink *dl, int Timeout)
+{
+ int result = Timeout;
+
+ timer_Stop(&dl->dial.timer);
+ if (Timeout < 0)
+ result = (random() % DIAL_TIMEOUT) + 1;
+ dl->dial.timer.load = result ? result * SECTICKS : 1;
+ dl->dial.timer.func = datalink_OpenTimeout;
+ dl->dial.timer.name = "dial";
+ dl->dial.timer.arg = dl;
+ timer_Start(&dl->dial.timer);
+ if (dl->state == DATALINK_OPENING)
+ log_Printf(LogPHASE, "%s: Enter pause (%d) for redialing.\n",
+ dl->name, result);
+ return result;
+}
+
+static void
+datalink_HangupDone(struct datalink *dl)
+{
+ if (dl->physical->type == PHYS_DEDICATED && !dl->bundle->CleaningUp &&
+ dl->physical->fd != -1) {
+ /* Don't close our device if the link is dedicated */
+ datalink_LoginDone(dl);
+ return;
+ }
+
+ chat_Finish(&dl->chat);
+ physical_Close(dl->physical);
+ dl->phone.chosen = "N/A";
+
+ if (dl->cbcp.required) {
+ log_Printf(LogPHASE, "Call peer back on %s\n", dl->cbcp.fsm.phone);
+ dl->cfg.callback.opmask = 0;
+ strncpy(dl->cfg.phone.list, dl->cbcp.fsm.phone,
+ sizeof dl->cfg.phone.list - 1);
+ dl->cfg.phone.list[sizeof dl->cfg.phone.list - 1] = '\0';
+ dl->phone.alt = dl->phone.next = NULL;
+ dl->reconnect_tries = dl->cfg.reconnect.max;
+ dl->dial.tries = dl->cfg.dial.max;
+ dl->dial.incs = 0;
+ dl->script.run = 1;
+ dl->script.packetmode = 1;
+ if (!physical_SetMode(dl->physical, PHYS_BACKGROUND))
+ log_Printf(LogERROR, "Oops - can't change mode to BACKGROUND (gulp) !\n");
+ bundle_LinksRemoved(dl->bundle);
+ /* if dial.timeout is < 0 (random), we don't override fsm.delay */
+ if (dl->cbcp.fsm.delay < dl->cfg.dial.timeout)
+ dl->cbcp.fsm.delay = dl->cfg.dial.timeout;
+ datalink_StartDialTimer(dl, dl->cbcp.fsm.delay);
+ cbcp_Down(&dl->cbcp);
+ datalink_NewState(dl, DATALINK_OPENING);
+ if (bundle_Phase(dl->bundle) == PHASE_DEAD ||
+ bundle_Phase(dl->bundle) == PHASE_TERMINATE)
+ bundle_NewPhase(dl->bundle, PHASE_ESTABLISH);
+ } else if (dl->bundle->CleaningUp ||
+ (dl->physical->type == PHYS_DIRECT) ||
+ ((!dl->dial.tries || (dl->dial.tries < 0 && !dl->reconnect_tries)) &&
+ !(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)))) {
+ datalink_NewState(dl, DATALINK_CLOSED);
+ dl->dial.tries = -1;
+ dl->dial.incs = 0;
+ dl->reconnect_tries = 0;
+ bundle_LinkClosed(dl->bundle, dl);
+ if (!dl->bundle->CleaningUp &&
+ !(dl->physical->type & (PHYS_DIRECT|PHYS_BACKGROUND|PHYS_FOREGROUND)))
+ datalink_StartDialTimer(dl, datalink_GetDialTimeout(dl));
+ } else {
+ datalink_NewState(dl, DATALINK_OPENING);
+ if (bundle_Phase(dl->bundle) == PHASE_DEAD ||
+ bundle_Phase(dl->bundle) == PHASE_TERMINATE)
+ bundle_NewPhase(dl->bundle, PHASE_ESTABLISH);
+ if (dl->dial.tries < 0) {
+ datalink_StartDialTimer(dl, dl->cfg.reconnect.timeout);
+ dl->dial.tries = dl->cfg.dial.max;
+ dl->dial.incs = 0;
+ dl->reconnect_tries--;
+ log_Printf(LogCHAT, "%s: Reconnect try %d of %d\n",
+ dl->name, dl->cfg.reconnect.max - dl->reconnect_tries,
+ dl->cfg.reconnect.max);
+ bundle_Notify(dl->bundle, EX_RECONNECT);
+ } else {
+ if (dl->phone.next == NULL)
+ datalink_StartDialTimer(dl, datalink_GetDialTimeout(dl));
+ else
+ datalink_StartDialTimer(dl, dl->cfg.dial.next_timeout);
+ bundle_Notify(dl->bundle, EX_REDIAL);
+ }
+ }
+}
+
+const char *
+datalink_ChoosePhoneNumber(struct datalink *dl)
+{
+ char *phone;
+
+ if (dl->phone.alt == NULL) {
+ if (dl->phone.next == NULL) {
+ strncpy(dl->phone.list, dl->cfg.phone.list, sizeof dl->phone.list - 1);
+ dl->phone.list[sizeof dl->phone.list - 1] = '\0';
+ if (*dl->phone.list == '\0')
+ return "";
+ dl->phone.next = dl->phone.list;
+ }
+ dl->phone.alt = strsep(&dl->phone.next, ":");
+ }
+ phone = strsep(&dl->phone.alt, "|");
+ dl->phone.chosen = *phone ? phone : "[NONE]";
+ if (*phone)
+ log_Printf(LogCHAT, "Phone: %s\n", phone);
+ return phone;
+}
+
+static void
+datalink_LoginDone(struct datalink *dl)
+{
+ chat_Finish(&dl->chat);
+
+ if (!dl->script.packetmode) {
+ dl->dial.tries = -1;
+ dl->dial.incs = 0;
+ datalink_NewState(dl, DATALINK_READY);
+ } else if (!physical_Raw(dl->physical)) {
+ dl->dial.tries = 0;
+ log_Printf(LogWARN, "datalink_LoginDone: Not connected.\n");
+ if (dl->script.run) {
+ datalink_NewState(dl, DATALINK_LOGOUT);
+ if (!chat_Setup(&dl->chat, dl->cfg.script.logout, NULL))
+ log_Printf(LogWARN, "Invalid logout script\n");
+ } else {
+ physical_StopDeviceTimer(dl->physical);
+ if (dl->physical->type == PHYS_DEDICATED)
+ /* force a redial timeout */
+ physical_Close(dl->physical);
+ datalink_HangupDone(dl);
+ }
+ } else {
+ dl->dial.tries = -1;
+ dl->dial.incs = 0;
+
+ hdlc_Init(&dl->physical->hdlc, &dl->physical->link.lcp);
+ async_Setup(&dl->physical->async);
+
+ lcp_Setup(&dl->physical->link.lcp, dl->state == DATALINK_READY ?
+ 0 : dl->physical->link.lcp.cfg.openmode);
+ ccp_Setup(&dl->physical->link.ccp);
+
+ datalink_NewState(dl, DATALINK_LCP);
+ fsm_Up(&dl->physical->link.lcp.fsm);
+ fsm_Open(&dl->physical->link.lcp.fsm);
+ }
+}
+
+static int
+datalink_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e,
+ int *n)
+{
+ struct datalink *dl = descriptor2datalink(d);
+ int result;
+
+ result = 0;
+ switch (dl->state) {
+ case DATALINK_CLOSED:
+ if ((dl->physical->type & (PHYS_DIRECT|PHYS_DEDICATED|PHYS_BACKGROUND|
+ PHYS_FOREGROUND|PHYS_DDIAL)) &&
+ !dl->bundle->CleaningUp)
+ /*
+ * Our first time in - DEDICATED & DDIAL never come down, and
+ * DIRECT, FOREGROUND & BACKGROUND get deleted when they enter
+ * DATALINK_CLOSED. Go to DATALINK_OPENING via datalink_Up()
+ * and fall through.
+ */
+ datalink_Up(dl, 1, 1);
+ else
+ break;
+ /* fall through */
+
+ case DATALINK_OPENING:
+ if (dl->dial.timer.state != TIMER_RUNNING) {
+ if (--dl->dial.tries < 0)
+ dl->dial.tries = 0;
+ if (physical_Open(dl->physical, dl->bundle) >= 0) {
+ log_WritePrompts(dl, "%s: Entering terminal mode on %s\r\n"
+ "Type `~?' for help\r\n", dl->name,
+ dl->physical->name.full);
+ if (dl->script.run) {
+ datalink_NewState(dl, DATALINK_DIAL);
+ if (!chat_Setup(&dl->chat, dl->cfg.script.dial,
+ *dl->cfg.script.dial ?
+ datalink_ChoosePhoneNumber(dl) : ""))
+ log_Printf(LogWARN, "Invalid dial script\n");
+ if (!(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)) &&
+ dl->cfg.dial.max)
+ log_Printf(LogCHAT, "%s: Dial attempt %u of %d\n",
+ dl->name, dl->cfg.dial.max - dl->dial.tries,
+ dl->cfg.dial.max);
+ } else
+ datalink_NewState(dl, DATALINK_CARRIER);
+ return datalink_UpdateSet(d, r, w, e, n);
+ } else {
+ if (!(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)) &&
+ dl->cfg.dial.max)
+ log_Printf(LogCHAT, "Failed to open device (attempt %u of %d)\n",
+ dl->cfg.dial.max - dl->dial.tries, dl->cfg.dial.max);
+ else
+ log_Printf(LogCHAT, "Failed to open device\n");
+
+ if (dl->bundle->CleaningUp ||
+ (!(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)) &&
+ dl->cfg.dial.max && dl->dial.tries == 0)) {
+ datalink_NewState(dl, DATALINK_CLOSED);
+ dl->reconnect_tries = 0;
+ dl->dial.tries = -1;
+ log_WritePrompts(dl, "Failed to open %s\n",
+ dl->physical->name.full);
+ bundle_LinkClosed(dl->bundle, dl);
+ }
+ if (!dl->bundle->CleaningUp) {
+ int timeout;
+
+ timeout = datalink_StartDialTimer(dl, datalink_GetDialTimeout(dl));
+ bundle_Notify(dl->bundle, EX_REDIAL);
+ log_WritePrompts(dl, "Failed to open %s, pause %d seconds\n",
+ dl->physical->name.full, timeout);
+ }
+ }
+ }
+ break;
+
+ case DATALINK_CARRIER:
+ /* Wait for carrier on the device */
+ switch (physical_AwaitCarrier(dl->physical)) {
+ case CARRIER_PENDING:
+ log_Printf(LogDEBUG, "Waiting for carrier\n");
+ return 0; /* A device timer is running to wake us up again */
+
+ case CARRIER_OK:
+ if (dl->script.run) {
+ datalink_NewState(dl, DATALINK_LOGIN);
+ if (!chat_Setup(&dl->chat, dl->cfg.script.login, NULL))
+ log_Printf(LogWARN, "Invalid login script\n");
+ } else
+ datalink_LoginDone(dl);
+ return datalink_UpdateSet(d, r, w, e, n);
+
+ case CARRIER_LOST:
+ physical_Offline(dl->physical); /* Is this required ? */
+ if (dl->script.run) {
+ datalink_NewState(dl, DATALINK_HANGUP);
+ if (!chat_Setup(&dl->chat, dl->cfg.script.hangup, NULL))
+ log_Printf(LogWARN, "Invalid hangup script\n");
+ return datalink_UpdateSet(d, r, w, e, n);
+ } else {
+ datalink_HangupDone(dl);
+ return 0; /* Maybe bundle_CleanDatalinks() has something to do */
+ }
+ }
+
+ case DATALINK_HANGUP:
+ case DATALINK_DIAL:
+ case DATALINK_LOGOUT:
+ case DATALINK_LOGIN:
+ result = descriptor_UpdateSet(&dl->chat.desc, r, w, e, n);
+ switch (dl->chat.state) {
+ case CHAT_DONE:
+ /* script succeeded */
+ switch(dl->state) {
+ case DATALINK_HANGUP:
+ datalink_HangupDone(dl);
+ break;
+ case DATALINK_DIAL:
+ datalink_NewState(dl, DATALINK_CARRIER);
+ return datalink_UpdateSet(d, r, w, e, n);
+ case DATALINK_LOGOUT:
+ datalink_NewState(dl, DATALINK_HANGUP);
+ physical_Offline(dl->physical);
+ if (!chat_Setup(&dl->chat, dl->cfg.script.hangup, NULL))
+ log_Printf(LogWARN, "Invalid hangup script\n");
+ return datalink_UpdateSet(d, r, w, e, n);
+ case DATALINK_LOGIN:
+ dl->phone.alt = NULL;
+ datalink_LoginDone(dl);
+ return datalink_UpdateSet(d, r, w, e, n);
+ }
+ break;
+ case CHAT_FAILED:
+ /* Going down - script failed */
+ log_Printf(LogWARN, "Chat script failed\n");
+ switch(dl->state) {
+ case DATALINK_HANGUP:
+ datalink_HangupDone(dl);
+ break;
+ case DATALINK_DIAL:
+ case DATALINK_LOGOUT:
+ case DATALINK_LOGIN:
+ datalink_NewState(dl, DATALINK_HANGUP);
+ physical_Offline(dl->physical);
+ if (!chat_Setup(&dl->chat, dl->cfg.script.hangup, NULL))
+ log_Printf(LogWARN, "Invalid hangup script\n");
+ return datalink_UpdateSet(d, r, w, e, n);
+ }
+ break;
+ }
+ break;
+
+ case DATALINK_READY:
+ case DATALINK_LCP:
+ case DATALINK_AUTH:
+ case DATALINK_CBCP:
+ case DATALINK_OPEN:
+ result = descriptor_UpdateSet(&dl->chap.desc, r, w, e, n) +
+ descriptor_UpdateSet(&dl->physical->desc, r, w, e, n);
+ break;
+ }
+ return result;
+}
+
+int
+datalink_RemoveFromSet(struct datalink *dl, fd_set *r, fd_set *w, fd_set *e)
+{
+ return physical_RemoveFromSet(dl->physical, r, w, e);
+}
+
+static int
+datalink_IsSet(struct fdescriptor *d, const fd_set *fdset)
+{
+ struct datalink *dl = descriptor2datalink(d);
+
+ switch (dl->state) {
+ case DATALINK_CLOSED:
+ case DATALINK_OPENING:
+ break;
+
+ case DATALINK_HANGUP:
+ case DATALINK_DIAL:
+ case DATALINK_LOGOUT:
+ case DATALINK_LOGIN:
+ return descriptor_IsSet(&dl->chat.desc, fdset);
+
+ case DATALINK_READY:
+ case DATALINK_LCP:
+ case DATALINK_AUTH:
+ case DATALINK_CBCP:
+ case DATALINK_OPEN:
+ return descriptor_IsSet(&dl->chap.desc, fdset) ? 1 :
+ descriptor_IsSet(&dl->physical->desc, fdset);
+ }
+ return 0;
+}
+
+static void
+datalink_Read(struct fdescriptor *d, struct bundle *bundle, const fd_set *fdset)
+{
+ struct datalink *dl = descriptor2datalink(d);
+
+ switch (dl->state) {
+ case DATALINK_CLOSED:
+ case DATALINK_OPENING:
+ break;
+
+ case DATALINK_HANGUP:
+ case DATALINK_DIAL:
+ case DATALINK_LOGOUT:
+ case DATALINK_LOGIN:
+ descriptor_Read(&dl->chat.desc, bundle, fdset);
+ break;
+
+ case DATALINK_READY:
+ case DATALINK_LCP:
+ case DATALINK_AUTH:
+ case DATALINK_CBCP:
+ case DATALINK_OPEN:
+ if (descriptor_IsSet(&dl->chap.desc, fdset))
+ descriptor_Read(&dl->chap.desc, bundle, fdset);
+ if (descriptor_IsSet(&dl->physical->desc, fdset))
+ descriptor_Read(&dl->physical->desc, bundle, fdset);
+ break;
+ }
+}
+
+static int
+datalink_Write(struct fdescriptor *d, struct bundle *bundle,
+ const fd_set *fdset)
+{
+ struct datalink *dl = descriptor2datalink(d);
+ int result = 0;
+
+ switch (dl->state) {
+ case DATALINK_CLOSED:
+ case DATALINK_OPENING:
+ break;
+
+ case DATALINK_HANGUP:
+ case DATALINK_DIAL:
+ case DATALINK_LOGOUT:
+ case DATALINK_LOGIN:
+ if ((result = descriptor_Write(&dl->chat.desc, bundle, fdset)) == -1) {
+ datalink_ComeDown(dl, CLOSE_NORMAL);
+ result = 0;
+ }
+ break;
+
+ case DATALINK_READY:
+ case DATALINK_LCP:
+ case DATALINK_AUTH:
+ case DATALINK_CBCP:
+ case DATALINK_OPEN:
+ if (descriptor_IsSet(&dl->chap.desc, fdset))
+ switch (descriptor_Write(&dl->chap.desc, bundle, fdset)) {
+ case -1:
+ datalink_ComeDown(dl, CLOSE_NORMAL);
+ break;
+ case 1:
+ result++;
+ }
+ if (descriptor_IsSet(&dl->physical->desc, fdset))
+ switch (descriptor_Write(&dl->physical->desc, bundle, fdset)) {
+ case -1:
+ datalink_ComeDown(dl, CLOSE_NORMAL);
+ break;
+ case 1:
+ result++;
+ }
+ break;
+ }
+
+ return result;
+}
+
+void
+datalink_ComeDown(struct datalink *dl, int how)
+{
+ int stayonline;
+
+ if (how == CLOSE_LCP)
+ datalink_DontHangup(dl);
+ else if (how == CLOSE_STAYDOWN)
+ datalink_StayDown(dl);
+
+ stayonline = dl->stayonline;
+ dl->stayonline = 0;
+
+ if (dl->state >= DATALINK_READY && stayonline) {
+ physical_StopDeviceTimer(dl->physical);
+ datalink_NewState(dl, DATALINK_READY);
+ } else if (dl->state != DATALINK_CLOSED && dl->state != DATALINK_HANGUP) {
+ physical_Offline(dl->physical);
+ if (dl->script.run && dl->state != DATALINK_OPENING) {
+ if (dl->state == DATALINK_LOGOUT) {
+ datalink_NewState(dl, DATALINK_HANGUP);
+ if (!chat_Setup(&dl->chat, dl->cfg.script.hangup, NULL))
+ log_Printf(LogWARN, "Invalid hangup script\n");
+ } else {
+ datalink_NewState(dl, DATALINK_LOGOUT);
+ if (!chat_Setup(&dl->chat, dl->cfg.script.logout, NULL))
+ log_Printf(LogWARN, "Invalid logout script\n");
+ }
+ } else
+ datalink_HangupDone(dl);
+ }
+}
+
+static void
+datalink_LayerStart(void *v, struct fsm *fp)
+{
+ /* The given FSM is about to start up ! */
+ struct datalink *dl = (struct datalink *)v;
+
+ if (fp->proto == PROTO_LCP)
+ (*dl->parent->LayerStart)(dl->parent->object, fp);
+}
+
+static void
+datalink_LayerUp(void *v, struct fsm *fp)
+{
+ /* The given fsm is now up */
+ struct datalink *dl = (struct datalink *)v;
+ struct lcp *lcp = &dl->physical->link.lcp;
+
+ if (fp->proto == PROTO_LCP) {
+ datalink_GotAuthname(dl, "");
+ lcp->auth_ineed = lcp->want_auth;
+ lcp->auth_iwait = lcp->his_auth;
+ if (lcp->his_auth || lcp->want_auth) {
+ if (bundle_Phase(dl->bundle) != PHASE_NETWORK)
+ bundle_NewPhase(dl->bundle, PHASE_AUTHENTICATE);
+ log_Printf(LogPHASE, "%s: his = %s, mine = %s\n", dl->name,
+ Auth2Nam(lcp->his_auth, lcp->his_authtype),
+ Auth2Nam(lcp->want_auth, lcp->want_authtype));
+ if (lcp->his_auth == PROTO_PAP)
+ auth_StartReq(&dl->pap);
+ if (lcp->want_auth == PROTO_CHAP)
+ auth_StartReq(&dl->chap.auth);
+ } else
+ datalink_AuthOk(dl);
+ } else if (fp->proto == PROTO_CCP)
+ (*dl->parent->LayerUp)(dl->parent->object, &dl->physical->link.ccp.fsm);
+}
+
+static void
+datalink_AuthReInit(struct datalink *dl)
+{
+ auth_StopTimer(&dl->pap);
+ auth_StopTimer(&dl->chap.auth);
+ chap_ReInit(&dl->chap);
+}
+
+void
+datalink_GotAuthname(struct datalink *dl, const char *name)
+{
+ strncpy(dl->peer.authname, name, sizeof dl->peer.authname - 1);
+ dl->peer.authname[sizeof dl->peer.authname - 1] = '\0';
+}
+
+void
+datalink_NCPUp(struct datalink *dl)
+{
+ int ccpok = ccp_SetOpenMode(&dl->physical->link.ccp);
+
+ if (dl->physical->link.lcp.want_mrru && dl->physical->link.lcp.his_mrru) {
+ /* we've authenticated in multilink mode ! */
+ switch (mp_Up(&dl->bundle->ncp.mp, dl)) {
+ case MP_LINKSENT:
+ /* We've handed the link off to another ppp (well, we will soon) ! */
+ return;
+ case MP_UP:
+ /* First link in the bundle */
+ auth_Select(dl->bundle, dl->peer.authname);
+ bundle_CalculateBandwidth(dl->bundle);
+ /* fall through */
+ case MP_ADDED:
+ /* We're in multilink mode ! */
+ dl->physical->link.ccp.fsm.open_mode = OPEN_PASSIVE; /* override */
+ bundle_CalculateBandwidth(dl->bundle);
+ break;
+ case MP_FAILED:
+ datalink_AuthNotOk(dl);
+ return;
+ }
+ } else if (bundle_Phase(dl->bundle) == PHASE_NETWORK) {
+ log_Printf(LogPHASE, "%s: Already in NETWORK phase\n", dl->name);
+ datalink_NewState(dl, DATALINK_OPEN);
+ bundle_CalculateBandwidth(dl->bundle);
+ (*dl->parent->LayerUp)(dl->parent->object, &dl->physical->link.lcp.fsm);
+ return;
+ } else {
+ dl->bundle->ncp.mp.peer = dl->peer;
+ ncp_SetLink(&dl->bundle->ncp, &dl->physical->link);
+ auth_Select(dl->bundle, dl->peer.authname);
+ }
+
+ if (ccpok) {
+ fsm_Up(&dl->physical->link.ccp.fsm);
+ fsm_Open(&dl->physical->link.ccp.fsm);
+ }
+ datalink_NewState(dl, DATALINK_OPEN);
+ bundle_NewPhase(dl->bundle, PHASE_NETWORK);
+ (*dl->parent->LayerUp)(dl->parent->object, &dl->physical->link.lcp.fsm);
+}
+
+void
+datalink_CBCPComplete(struct datalink *dl)
+{
+ datalink_NewState(dl, DATALINK_LCP);
+ datalink_AuthReInit(dl);
+ fsm_Close(&dl->physical->link.lcp.fsm);
+}
+
+void
+datalink_CBCPFailed(struct datalink *dl)
+{
+ cbcp_Down(&dl->cbcp);
+ datalink_CBCPComplete(dl);
+}
+
+void
+datalink_AuthOk(struct datalink *dl)
+{
+ if ((dl->physical->link.lcp.his_callback.opmask &
+ CALLBACK_BIT(CALLBACK_CBCP) ||
+ dl->physical->link.lcp.want_callback.opmask &
+ CALLBACK_BIT(CALLBACK_CBCP)) &&
+ !(dl->physical->link.lcp.want_callback.opmask &
+ CALLBACK_BIT(CALLBACK_AUTH))) {
+ /* We must have agreed CBCP if AUTH isn't there any more */
+ datalink_NewState(dl, DATALINK_CBCP);
+ cbcp_Up(&dl->cbcp);
+ } else if (dl->physical->link.lcp.want_callback.opmask) {
+ /* It's not CBCP */
+ log_Printf(LogPHASE, "%s: Shutdown and await peer callback\n", dl->name);
+ datalink_NewState(dl, DATALINK_LCP);
+ datalink_AuthReInit(dl);
+ fsm_Close(&dl->physical->link.lcp.fsm);
+ } else
+ switch (dl->physical->link.lcp.his_callback.opmask) {
+ case 0:
+ datalink_NCPUp(dl);
+ break;
+
+ case CALLBACK_BIT(CALLBACK_AUTH):
+ auth_SetPhoneList(dl->peer.authname, dl->cbcp.fsm.phone,
+ sizeof dl->cbcp.fsm.phone);
+ if (*dl->cbcp.fsm.phone == '\0' || !strcmp(dl->cbcp.fsm.phone, "*")) {
+ log_Printf(LogPHASE, "%s: %s cannot be called back\n", dl->name,
+ dl->peer.authname);
+ *dl->cbcp.fsm.phone = '\0';
+ } else {
+ char *ptr = strchr(dl->cbcp.fsm.phone, ',');
+ if (ptr)
+ *ptr = '\0'; /* Call back on the first number */
+ log_Printf(LogPHASE, "%s: Calling peer back on %s\n", dl->name,
+ dl->cbcp.fsm.phone);
+ dl->cbcp.required = 1;
+ }
+ dl->cbcp.fsm.delay = 0;
+ datalink_NewState(dl, DATALINK_LCP);
+ datalink_AuthReInit(dl);
+ fsm_Close(&dl->physical->link.lcp.fsm);
+ break;
+
+ case CALLBACK_BIT(CALLBACK_E164):
+ strncpy(dl->cbcp.fsm.phone, dl->physical->link.lcp.his_callback.msg,
+ sizeof dl->cbcp.fsm.phone - 1);
+ dl->cbcp.fsm.phone[sizeof dl->cbcp.fsm.phone - 1] = '\0';
+ log_Printf(LogPHASE, "%s: Calling peer back on %s\n", dl->name,
+ dl->cbcp.fsm.phone);
+ dl->cbcp.required = 1;
+ dl->cbcp.fsm.delay = 0;
+ datalink_NewState(dl, DATALINK_LCP);
+ datalink_AuthReInit(dl);
+ fsm_Close(&dl->physical->link.lcp.fsm);
+ break;
+
+ default:
+ log_Printf(LogPHASE, "%s: Oops - Should have NAK'd peer callback !\n",
+ dl->name);
+ datalink_NewState(dl, DATALINK_LCP);
+ datalink_AuthReInit(dl);
+ fsm_Close(&dl->physical->link.lcp.fsm);
+ break;
+ }
+}
+
+void
+datalink_AuthNotOk(struct datalink *dl)
+{
+ datalink_NewState(dl, DATALINK_LCP);
+ datalink_AuthReInit(dl);
+ fsm_Close(&dl->physical->link.lcp.fsm);
+}
+
+static void
+datalink_LayerDown(void *v, struct fsm *fp)
+{
+ /* The given FSM has been told to come down */
+ struct datalink *dl = (struct datalink *)v;
+
+ if (fp->proto == PROTO_LCP) {
+ switch (dl->state) {
+ case DATALINK_OPEN:
+ peerid_Init(&dl->peer);
+ fsm2initial(&dl->physical->link.ccp.fsm);
+ datalink_NewState(dl, DATALINK_LCP); /* before parent TLD */
+ (*dl->parent->LayerDown)(dl->parent->object, fp);
+ /* 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.logout = '\0';
+ *dl->cfg.script.hangup = '\0';
+ *dl->cfg.phone.list = '\0';
+ *dl->phone.list = '\0';
+ dl->phone.next = NULL;
+ dl->phone.alt = NULL;
+ dl->phone.chosen = "N/A";
+ dl->stayonline = 0;
+ dl->script.run = 1;
+ dl->script.packetmode = 1;
+ mp_linkInit(&dl->mp);
+
+ dl->bundle = bundle;
+ dl->next = NULL;
+
+ memset(&dl->dial.timer, '\0', sizeof dl->dial.timer);
+
+ dl->dial.tries = 0;
+ dl->cfg.dial.max = 1;
+ dl->cfg.dial.next_timeout = DIAL_NEXT_TIMEOUT;
+ dl->cfg.dial.timeout = DIAL_TIMEOUT;
+ dl->cfg.dial.inc = 0;
+ dl->cfg.dial.maxinc = 10;
+
+ dl->reconnect_tries = 0;
+ dl->cfg.reconnect.max = 0;
+ dl->cfg.reconnect.timeout = RECONNECT_TIMEOUT;
+
+ dl->cfg.callback.opmask = 0;
+ dl->cfg.cbcp.delay = 0;
+ *dl->cfg.cbcp.phone = '\0';
+ dl->cfg.cbcp.fsmretry = DEF_FSMRETRY;
+
+ dl->name = strdup(name);
+ peerid_Init(&dl->peer);
+ dl->parent = &bundle->fsm;
+ dl->fsmp.LayerStart = datalink_LayerStart;
+ dl->fsmp.LayerUp = datalink_LayerUp;
+ dl->fsmp.LayerDown = datalink_LayerDown;
+ dl->fsmp.LayerFinish = datalink_LayerFinish;
+ dl->fsmp.object = dl;
+
+ if ((dl->physical = physical_Create(dl, type)) == NULL) {
+ free(dl->name);
+ free(dl);
+ return NULL;
+ }
+
+ pap_Init(&dl->pap, dl->physical);
+ chap_Init(&dl->chap, dl->physical);
+ cbcp_Init(&dl->cbcp, dl->physical);
+
+ memset(&dl->chat, '\0', sizeof dl->chat); /* Force buf{start,end} reset */
+ chat_Init(&dl->chat, dl->physical);
+
+ log_Printf(LogPHASE, "%s: Created in %s state\n",
+ dl->name, datalink_State(dl));
+
+ return dl;
+}
+
+struct datalink *
+datalink_Clone(struct datalink *odl, const char *name)
+{
+ struct datalink *dl;
+
+ dl = (struct datalink *)malloc(sizeof(struct datalink));
+ if (dl == NULL)
+ return dl;
+
+ dl->desc.type = DATALINK_DESCRIPTOR;
+ dl->desc.UpdateSet = datalink_UpdateSet;
+ dl->desc.IsSet = datalink_IsSet;
+ dl->desc.Read = datalink_Read;
+ dl->desc.Write = datalink_Write;
+
+ dl->state = DATALINK_CLOSED;
+
+ memcpy(&dl->cfg, &odl->cfg, sizeof dl->cfg);
+ mp_linkInit(&dl->mp);
+ *dl->phone.list = '\0';
+ dl->phone.next = NULL;
+ dl->phone.alt = NULL;
+ dl->phone.chosen = "N/A";
+ dl->bundle = odl->bundle;
+ dl->next = NULL;
+ memset(&dl->dial.timer, '\0', sizeof dl->dial.timer);
+ dl->dial.tries = 0;
+ dl->reconnect_tries = 0;
+ dl->name = strdup(name);
+ peerid_Init(&dl->peer);
+ dl->parent = odl->parent;
+ memcpy(&dl->fsmp, &odl->fsmp, sizeof dl->fsmp);
+ dl->fsmp.object = dl;
+
+ if ((dl->physical = physical_Create(dl, PHYS_INTERACTIVE)) == NULL) {
+ free(dl->name);
+ free(dl);
+ return NULL;
+ }
+ pap_Init(&dl->pap, dl->physical);
+ dl->pap.cfg = odl->pap.cfg;
+
+ chap_Init(&dl->chap, dl->physical);
+ dl->chap.auth.cfg = odl->chap.auth.cfg;
+
+ memcpy(&dl->physical->cfg, &odl->physical->cfg, sizeof dl->physical->cfg);
+ memcpy(&dl->physical->link.lcp.cfg, &odl->physical->link.lcp.cfg,
+ sizeof dl->physical->link.lcp.cfg);
+ memcpy(&dl->physical->link.ccp.cfg, &odl->physical->link.ccp.cfg,
+ sizeof dl->physical->link.ccp.cfg);
+ memcpy(&dl->physical->async.cfg, &odl->physical->async.cfg,
+ sizeof dl->physical->async.cfg);
+
+ cbcp_Init(&dl->cbcp, dl->physical);
+
+ memset(&dl->chat, '\0', sizeof dl->chat); /* Force buf{start,end} reset */
+ chat_Init(&dl->chat, dl->physical);
+
+ log_Printf(LogPHASE, "%s: Cloned in %s state\n",
+ dl->name, datalink_State(dl));
+
+ return dl;
+}
+
+struct datalink *
+datalink_Destroy(struct datalink *dl)
+{
+ struct datalink *result;
+
+ if (dl->state != DATALINK_CLOSED) {
+ log_Printf(LogERROR, "Oops, destroying a datalink in state %s\n",
+ datalink_State(dl));
+ switch (dl->state) {
+ case DATALINK_HANGUP:
+ case DATALINK_DIAL:
+ case DATALINK_LOGIN:
+ chat_Finish(&dl->chat); /* Gotta blat the timers ! */
+ break;
+ }
+ }
+
+ chat_Destroy(&dl->chat);
+ timer_Stop(&dl->dial.timer);
+ result = dl->next;
+ physical_Destroy(dl->physical);
+ free(dl->name);
+ free(dl);
+
+ return result;
+}
+
+void
+datalink_Up(struct datalink *dl, int runscripts, int packetmode)
+{
+ if (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) {
+ dl->script.run = 0;
+ datalink_NewState(dl, DATALINK_CARRIER);
+ }
+ }
+ break;
+ }
+}
+
+void
+datalink_Close(struct datalink *dl, int how)
+{
+ /* Please close */
+ switch (dl->state) {
+ case DATALINK_OPEN:
+ peerid_Init(&dl->peer);
+ fsm2initial(&dl->physical->link.ccp.fsm);
+ /* fall through */
+
+ case DATALINK_CBCP:
+ case DATALINK_AUTH:
+ case DATALINK_LCP:
+ datalink_AuthReInit(dl);
+ if (how == CLOSE_LCP)
+ datalink_DontHangup(dl);
+ else if (how == CLOSE_STAYDOWN)
+ datalink_StayDown(dl);
+ fsm_Close(&dl->physical->link.lcp.fsm);
+ break;
+
+ default:
+ datalink_ComeDown(dl, how);
+ }
+}
+
+void
+datalink_Down(struct datalink *dl, int how)
+{
+ /* Carrier is lost */
+ switch (dl->state) {
+ case DATALINK_OPEN:
+ peerid_Init(&dl->peer);
+ fsm2initial(&dl->physical->link.ccp.fsm);
+ /* fall through */
+
+ case DATALINK_CBCP:
+ case DATALINK_AUTH:
+ case DATALINK_LCP:
+ fsm2initial(&dl->physical->link.lcp.fsm);
+ if (dl->state == DATALINK_OPENING)
+ return; /* we're doing a callback... */
+ /* fall through */
+
+ default:
+ datalink_ComeDown(dl, how);
+ }
+}
+
+void
+datalink_StayDown(struct datalink *dl)
+{
+ dl->dial.tries = -1;
+ dl->reconnect_tries = 0;
+ dl->stayonline = 0;
+}
+
+void
+datalink_DontHangup(struct datalink *dl)
+{
+ dl->dial.tries = -1;
+ dl->reconnect_tries = 0;
+ dl->stayonline = dl->state >= DATALINK_LCP ? 1 : 0;
+}
+
+int
+datalink_Show(struct cmdargs const *arg)
+{
+ prompt_Printf(arg->prompt, "Name: %s\n", arg->cx->name);
+ prompt_Printf(arg->prompt, " State: %s\n",
+ datalink_State(arg->cx));
+ prompt_Printf(arg->prompt, " Peer name: ");
+ if (*arg->cx->peer.authname)
+ prompt_Printf(arg->prompt, "%s\n", arg->cx->peer.authname);
+ else if (arg->cx->state == DATALINK_OPEN)
+ prompt_Printf(arg->prompt, "None requested\n");
+ else
+ prompt_Printf(arg->prompt, "N/A\n");
+ prompt_Printf(arg->prompt, " Discriminator: %s\n",
+ mp_Enddisc(arg->cx->peer.enddisc.class,
+ arg->cx->peer.enddisc.address,
+ arg->cx->peer.enddisc.len));
+
+ prompt_Printf(arg->prompt, "\nDefaults:\n");
+ prompt_Printf(arg->prompt, " Phone List: %s\n",
+ arg->cx->cfg.phone.list);
+ if (arg->cx->cfg.dial.max)
+ prompt_Printf(arg->prompt, " Dial tries: %d, delay ",
+ arg->cx->cfg.dial.max);
+ else
+ prompt_Printf(arg->prompt, " Dial tries: infinite, delay ");
+ if (arg->cx->cfg.dial.next_timeout >= 0)
+ prompt_Printf(arg->prompt, "%ds/", arg->cx->cfg.dial.next_timeout);
+ else
+ prompt_Printf(arg->prompt, "random/");
+ if (arg->cx->cfg.dial.timeout >= 0)
+ prompt_Printf(arg->prompt, "%ds\n", arg->cx->cfg.dial.timeout);
+ else
+ prompt_Printf(arg->prompt, "random\n");
+ prompt_Printf(arg->prompt, " Reconnect tries: %d, delay ",
+ arg->cx->cfg.reconnect.max);
+ if (arg->cx->cfg.reconnect.timeout > 0)
+ prompt_Printf(arg->prompt, "%ds\n", arg->cx->cfg.reconnect.timeout);
+ else
+ prompt_Printf(arg->prompt, "random\n");
+ prompt_Printf(arg->prompt, " Callback %s ", arg->cx->physical->type ==
+ PHYS_DIRECT ? "accepted: " : "requested:");
+ if (!arg->cx->cfg.callback.opmask)
+ prompt_Printf(arg->prompt, "none\n");
+ else {
+ int comma = 0;
+
+ if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_NONE)) {
+ prompt_Printf(arg->prompt, "none");
+ comma = 1;
+ }
+ if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_AUTH)) {
+ prompt_Printf(arg->prompt, "%sauth", comma ? ", " : "");
+ comma = 1;
+ }
+ if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_E164)) {
+ prompt_Printf(arg->prompt, "%sE.164", comma ? ", " : "");
+ if (arg->cx->physical->type != PHYS_DIRECT)
+ prompt_Printf(arg->prompt, " (%s)", arg->cx->cfg.callback.msg);
+ comma = 1;
+ }
+ if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_CBCP)) {
+ prompt_Printf(arg->prompt, "%scbcp\n", comma ? ", " : "");
+ prompt_Printf(arg->prompt, " CBCP: delay: %ds\n",
+ arg->cx->cfg.cbcp.delay);
+ prompt_Printf(arg->prompt, " phone: ");
+ if (!strcmp(arg->cx->cfg.cbcp.phone, "*")) {
+ if (arg->cx->physical->type & PHYS_DIRECT)
+ prompt_Printf(arg->prompt, "Caller decides\n");
+ else
+ prompt_Printf(arg->prompt, "Dialback server decides\n");
+ } else
+ prompt_Printf(arg->prompt, "%s\n", arg->cx->cfg.cbcp.phone);
+ prompt_Printf(arg->prompt, " timeout: %lds\n",
+ arg->cx->cfg.cbcp.fsmretry);
+ } else
+ prompt_Printf(arg->prompt, "\n");
+ }
+
+ prompt_Printf(arg->prompt, " Dial Script: %s\n",
+ arg->cx->cfg.script.dial);
+ prompt_Printf(arg->prompt, " Login Script: %s\n",
+ arg->cx->cfg.script.login);
+ prompt_Printf(arg->prompt, " Logout Script: %s\n",
+ arg->cx->cfg.script.logout);
+ prompt_Printf(arg->prompt, " Hangup Script: %s\n",
+ arg->cx->cfg.script.hangup);
+ return 0;
+}
+
+int
+datalink_SetReconnect(struct cmdargs const *arg)
+{
+ if (arg->argc == arg->argn+2) {
+ arg->cx->cfg.reconnect.timeout = atoi(arg->argv[arg->argn]);
+ arg->cx->cfg.reconnect.max = atoi(arg->argv[arg->argn+1]);
+ return 0;
+ }
+ return -1;
+}
+
+int
+datalink_SetRedial(struct cmdargs const *arg)
+{
+ const char *sep, *osep;
+ int timeout, inc, maxinc, tries;
+
+ if (arg->argc == arg->argn+1 || arg->argc == arg->argn+2) {
+ if (strncasecmp(arg->argv[arg->argn], "random", 6) == 0 &&
+ (arg->argv[arg->argn][6] == '\0' || arg->argv[arg->argn][6] == '.')) {
+ arg->cx->cfg.dial.timeout = -1;
+ randinit();
+ } else {
+ timeout = atoi(arg->argv[arg->argn]);
+
+ if (timeout >= 0)
+ arg->cx->cfg.dial.timeout = timeout;
+ else {
+ log_Printf(LogWARN, "Invalid redial timeout\n");
+ return -1;
+ }
+ }
+
+ sep = strchr(arg->argv[arg->argn], '+');
+ if (sep) {
+ inc = atoi(++sep);
+ osep = sep;
+ if (inc >= 0)
+ arg->cx->cfg.dial.inc = inc;
+ else {
+ log_Printf(LogWARN, "Invalid timeout increment\n");
+ return -1;
+ }
+ sep = strchr(sep, '-');
+ if (sep) {
+ maxinc = atoi(++sep);
+ if (maxinc >= 0)
+ arg->cx->cfg.dial.maxinc = maxinc;
+ else {
+ log_Printf(LogWARN, "Invalid maximum timeout increments\n");
+ return -1;
+ }
+ } else {
+ /* Default timeout increment */
+ arg->cx->cfg.dial.maxinc = 10;
+ sep = osep;
+ }
+ } else {
+ /* Default timeout increment & max increment */
+ arg->cx->cfg.dial.inc = 0;
+ arg->cx->cfg.dial.maxinc = 10;
+ sep = arg->argv[arg->argn];
+ }
+
+ sep = strchr(sep, '.');
+ if (sep) {
+ if (strcasecmp(++sep, "random") == 0) {
+ arg->cx->cfg.dial.next_timeout = -1;
+ randinit();
+ } else {
+ timeout = atoi(sep);
+ if (timeout >= 0)
+ arg->cx->cfg.dial.next_timeout = timeout;
+ else {
+ log_Printf(LogWARN, "Invalid next redial timeout\n");
+ return -1;
+ }
+ }
+ } else
+ /* Default next timeout */
+ arg->cx->cfg.dial.next_timeout = DIAL_NEXT_TIMEOUT;
+
+ if (arg->argc == arg->argn+2) {
+ tries = atoi(arg->argv[arg->argn+1]);
+
+ if (tries >= 0) {
+ arg->cx->cfg.dial.max = tries;
+ } else {
+ log_Printf(LogWARN, "Invalid retry value\n");
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ return -1;
+}
+
+static const char * const states[] = {
+ "closed",
+ "opening",
+ "hangup",
+ "dial",
+ "carrier",
+ "logout",
+ "login",
+ "ready",
+ "lcp",
+ "auth",
+ "cbcp",
+ "open"
+};
+
+const char *
+datalink_State(struct datalink *dl)
+{
+ if (dl->state < 0 || dl->state >= sizeof states / sizeof states[0])
+ return "unknown";
+ return states[dl->state];
+}
+
+static void
+datalink_NewState(struct datalink *dl, int state)
+{
+ if (state != dl->state) {
+ if (state >= 0 && state < sizeof states / sizeof states[0]) {
+ log_Printf(LogPHASE, "%s: %s -> %s\n", dl->name, datalink_State(dl),
+ states[state]);
+ dl->state = state;
+ } else
+ log_Printf(LogERROR, "%s: Can't enter state %d !\n", dl->name, state);
+ }
+}
+
+struct datalink *
+iov2datalink(struct bundle *bundle, struct iovec *iov, int *niov, int maxiov,
+ int fd, int *auxfd, int *nauxfd)
+{
+ struct datalink *dl, *cdl;
+ struct fsm_retry copy;
+ char *oname;
+
+ dl = (struct datalink *)iov[(*niov)++].iov_base;
+ dl->name = iov[*niov].iov_base;
+
+ if (dl->name[DATALINK_MAXNAME-1]) {
+ dl->name[DATALINK_MAXNAME-1] = '\0';
+ if (strlen(dl->name) == DATALINK_MAXNAME - 1)
+ log_Printf(LogWARN, "Datalink name truncated to \"%s\"\n", dl->name);
+ }
+
+ /* Make sure the name is unique ! */
+ oname = NULL;
+ do {
+ for (cdl = bundle->links; cdl; cdl = cdl->next)
+ if (!strcasecmp(dl->name, cdl->name)) {
+ if (oname)
+ free(datalink_NextName(dl));
+ else
+ oname = datalink_NextName(dl);
+ break; /* Keep renaming 'till we have no conflicts */
+ }
+ } while (cdl);
+
+ if (oname) {
+ log_Printf(LogPHASE, "Rename link %s to %s\n", oname, dl->name);
+ free(oname);
+ } else {
+ dl->name = strdup(dl->name);
+ free(iov[*niov].iov_base);
+ }
+ (*niov)++;
+
+ dl->desc.type = DATALINK_DESCRIPTOR;
+ dl->desc.UpdateSet = datalink_UpdateSet;
+ dl->desc.IsSet = datalink_IsSet;
+ dl->desc.Read = datalink_Read;
+ dl->desc.Write = datalink_Write;
+
+ mp_linkInit(&dl->mp);
+ *dl->phone.list = '\0';
+ dl->phone.next = NULL;
+ dl->phone.alt = NULL;
+ dl->phone.chosen = "N/A";
+
+ dl->bundle = bundle;
+ dl->next = NULL;
+ memset(&dl->dial.timer, '\0', sizeof dl->dial.timer);
+ dl->dial.tries = 0;
+ dl->reconnect_tries = 0;
+ dl->parent = &bundle->fsm;
+ dl->fsmp.LayerStart = datalink_LayerStart;
+ dl->fsmp.LayerUp = datalink_LayerUp;
+ dl->fsmp.LayerDown = datalink_LayerDown;
+ dl->fsmp.LayerFinish = datalink_LayerFinish;
+ dl->fsmp.object = dl;
+
+ dl->physical = iov2physical(dl, iov, niov, maxiov, fd, auxfd, nauxfd);
+
+ if (!dl->physical) {
+ free(dl->name);
+ free(dl);
+ dl = NULL;
+ } else {
+ copy = dl->pap.cfg.fsm;
+ pap_Init(&dl->pap, dl->physical);
+ dl->pap.cfg.fsm = copy;
+
+ copy = dl->chap.auth.cfg.fsm;
+ chap_Init(&dl->chap, dl->physical);
+ dl->chap.auth.cfg.fsm = copy;
+
+ cbcp_Init(&dl->cbcp, dl->physical);
+
+ memset(&dl->chat, '\0', sizeof dl->chat); /* Force buf{start,end} reset */
+ chat_Init(&dl->chat, dl->physical);
+
+ log_Printf(LogPHASE, "%s: Transferred in %s state\n",
+ dl->name, datalink_State(dl));
+ }
+
+ return dl;
+}
+
+int
+datalink2iov(struct datalink *dl, struct iovec *iov, int *niov, int maxiov,
+ int *auxfd, int *nauxfd)
+{
+ /* If `dl' is NULL, we're allocating before a Fromiov() */
+ int link_fd;
+
+ if (dl) {
+ timer_Stop(&dl->dial.timer);
+ /* The following is purely for the sake of paranoia */
+ cbcp_Down(&dl->cbcp);
+ timer_Stop(&dl->pap.authtimer);
+ timer_Stop(&dl->chap.auth.authtimer);
+ }
+
+ if (*niov >= maxiov - 1) {
+ log_Printf(LogERROR, "Toiov: No room for datalink !\n");
+ if (dl) {
+ free(dl->name);
+ free(dl);
+ }
+ return -1;
+ }
+
+ iov[*niov].iov_base = (void *)dl;
+ iov[(*niov)++].iov_len = sizeof *dl;
+ iov[*niov].iov_base = dl ? realloc(dl->name, DATALINK_MAXNAME) : NULL;
+ iov[(*niov)++].iov_len = DATALINK_MAXNAME;
+
+ link_fd = physical2iov(dl ? dl->physical : NULL, iov, niov, maxiov, auxfd,
+ nauxfd);
+
+ if (link_fd == -1 && dl) {
+ free(dl->name);
+ free(dl);
+ }
+
+ return link_fd;
+}
+
+void
+datalink_Rename(struct datalink *dl, const char *name)
+{
+ free(dl->name);
+ dl->physical->link.name = dl->name = strdup(name);
+}
+
+char *
+datalink_NextName(struct datalink *dl)
+{
+ int f, n;
+ char *name, *oname;
+
+ n = strlen(dl->name);
+ name = (char *)malloc(n+3);
+ for (f = n - 1; f >= 0; f--)
+ if (!isdigit(dl->name[f]))
+ break;
+ n = sprintf(name, "%.*s-", dl->name[f] == '-' ? f : f + 1, dl->name);
+ sprintf(name + n, "%d", atoi(dl->name + f + 1) + 1);
+ oname = dl->name;
+ dl->name = name;
+ /* our physical link name isn't updated (it probably isn't created yet) */
+ return oname;
+}
+
+int
+datalink_SetMode(struct datalink *dl, int mode)
+{
+ if (!physical_SetMode(dl->physical, mode))
+ return 0;
+ if (dl->physical->type & (PHYS_DIRECT|PHYS_DEDICATED))
+ dl->script.run = 0;
+ if (dl->physical->type == PHYS_DIRECT)
+ dl->reconnect_tries = 0;
+ if (mode & (PHYS_DDIAL|PHYS_BACKGROUND|PHYS_FOREGROUND) &&
+ dl->state <= DATALINK_READY)
+ datalink_Up(dl, 1, 1);
+ return 1;
+}
+
+int
+datalink_GetDialTimeout(struct datalink *dl)
+{
+ int result = dl->cfg.dial.timeout + dl->dial.incs * dl->cfg.dial.inc;
+
+ if (dl->dial.incs < dl->cfg.dial.maxinc)
+ dl->dial.incs++;
+
+ return result;
+}
diff --git a/usr.sbin/ppp/datalink.h b/usr.sbin/ppp/datalink.h
new file mode 100644
index 0000000..64b3c16
--- /dev/null
+++ b/usr.sbin/ppp/datalink.h
@@ -0,0 +1,157 @@
+/*-
+ * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define DATALINK_CLOSED (0)
+#define DATALINK_OPENING (1)
+#define DATALINK_HANGUP (2)
+#define DATALINK_DIAL (3)
+#define DATALINK_CARRIER (4)
+#define DATALINK_LOGOUT (5)
+#define DATALINK_LOGIN (6)
+#define DATALINK_READY (7)
+#define DATALINK_LCP (8)
+#define DATALINK_AUTH (9)
+#define DATALINK_CBCP (10)
+#define DATALINK_OPEN (11)
+
+#define DATALINK_MAXNAME (20) /* Maximum datalink::name length */
+
+/* How to close the link */
+#define CLOSE_NORMAL 0
+#define CLOSE_STAYDOWN 1
+#define CLOSE_LCP 2
+
+struct iovec;
+struct prompt;
+struct physical;
+struct bundle;
+
+struct datalink {
+ struct fdescriptor desc; /* We play either a physical or a chat */
+ int state; /* Our DATALINK_* state */
+ struct physical *physical; /* Our link */
+
+ struct chat chat; /* For bringing the link up & down */
+
+ unsigned stayonline : 1; /* stay online when LCP is closed ? */
+ struct {
+ unsigned run : 1; /* run scripts ? */
+ unsigned packetmode : 1; /* Go into packet mode after login ? */
+ } script;
+
+ struct {
+ struct {
+ char dial[SCRIPT_LEN];
+ char login[SCRIPT_LEN];
+ char logout[SCRIPT_LEN];
+ char hangup[SCRIPT_LEN];
+ } script; /* various chat scripts */
+ struct {
+ char list[SCRIPT_LEN]; /* Telephone Numbers */
+ } phone;
+ struct {
+ int max; /* initially try again this number of times */
+ int next_timeout; /* Redial next timeout value */
+ int inc; /* Increment timeout by `inc' each time read */
+ int maxinc; /* Maximum number of increments */
+ int timeout; /* Redial timeout value (end of phone list) */
+ } dial;
+ struct {
+ int max; /* initially try again this number of times */
+ int timeout; /* Timeout before reconnect on carrier loss */
+ } reconnect;
+ struct callback callback; /* Direction depends on physical type */
+ struct cbcpcfg cbcp; /* Direction depends on phys type & callback */
+ } cfg; /* All our config data is in here */
+
+ struct {
+ char list[SCRIPT_LEN]; /* copy of cfg.list for strsep() */
+ char *next; /* Next phone from the list */
+ char *alt; /* Alternate (after fail) phone from the list */
+ const char *chosen; /* Chosen phone number after DIAL */
+ } phone;
+
+ struct cbcp cbcp;
+
+ struct {
+ struct pppTimer timer; /* For timing between close & open */
+ int tries; /* currently try again this number of times */
+ int incs; /* # times our timeout has been incremented */
+ } dial;
+
+ unsigned reconnect_tries; /* currently try again this number of times */
+
+ char *name; /* Our name */
+
+ struct peerid peer; /* Peer identification */
+
+ struct fsm_parent fsmp; /* Our callback functions */
+ const struct fsm_parent *parent; /* Our parent */
+
+ struct authinfo pap; /* Authentication using pap */
+ struct chap chap; /* Authentication using chap */
+
+ struct mp_link mp; /* multilink data */
+
+ struct bundle *bundle; /* for the moment */
+ struct datalink *next; /* Next in the list */
+};
+
+#define descriptor2datalink(d) \
+ ((d)->type == DATALINK_DESCRIPTOR ? (struct datalink *)(d) : NULL)
+
+extern struct datalink *datalink_Create(const char *name, struct bundle *, int);
+extern struct datalink *datalink_Clone(struct datalink *, const char *);
+extern struct datalink *iov2datalink(struct bundle *, struct iovec *, int *,
+ int, int, int *, int *);
+extern int datalink2iov(struct datalink *, struct iovec *, int *, int, int *,
+ int *);
+extern struct datalink *datalink_Destroy(struct datalink *);
+extern void datalink_GotAuthname(struct datalink *, const char *);
+extern void datalink_Up(struct datalink *, int, int);
+extern void datalink_Close(struct datalink *, int);
+extern void datalink_Down(struct datalink *, int);
+extern void datalink_StayDown(struct datalink *);
+extern void datalink_DontHangup(struct datalink *);
+extern void datalink_AuthOk(struct datalink *);
+extern void datalink_AuthNotOk(struct datalink *);
+extern void datalink_NCPUp(struct datalink *);
+extern void datalink_CBCPComplete(struct datalink *);
+extern void datalink_CBCPFailed(struct datalink *);
+extern int datalink_Show(struct cmdargs const *);
+extern int datalink_SetRedial(struct cmdargs const *);
+extern int datalink_SetReconnect(struct cmdargs const *);
+extern const char *datalink_State(struct datalink *);
+extern void datalink_Rename(struct datalink *, const char *);
+extern char *datalink_NextName(struct datalink *);
+extern int datalink_RemoveFromSet(struct datalink *, fd_set *, fd_set *,
+ fd_set *);
+extern int datalink_SetMode(struct datalink *, int);
+extern int datalink_GetDialTimeout(struct datalink *);
+extern const char *datalink_ChoosePhoneNumber(struct datalink *);
+extern void datalink_ComeDown(struct datalink *, int);
diff --git a/usr.sbin/ppp/deflate.c b/usr.sbin/ppp/deflate.c
new file mode 100644
index 0000000..3e6198d
--- /dev/null
+++ b/usr.sbin/ppp/deflate.c
@@ -0,0 +1,601 @@
+/*-
+ * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <zlib.h>
+
+#include "mbuf.h"
+#include "log.h"
+#include "timer.h"
+#include "fsm.h"
+#include "ccp.h"
+#include "deflate.h"
+
+/* Our state */
+struct deflate_state {
+ u_short seqno;
+ int uncomp_rec;
+ int winsize;
+ z_stream cx;
+};
+
+static char garbage[10];
+static u_char EMPTY_BLOCK[4] = { 0x00, 0x00, 0xff, 0xff };
+
+#define DEFLATE_CHUNK_LEN (1536 - sizeof(struct mbuf))
+
+static int
+DeflateResetOutput(void *v)
+{
+ struct deflate_state *state = (struct deflate_state *)v;
+
+ state->seqno = 0;
+ state->uncomp_rec = 0;
+ deflateReset(&state->cx);
+ log_Printf(LogCCP, "Deflate: Output channel reset\n");
+
+ return 1; /* Ask FSM to ACK */
+}
+
+static struct mbuf *
+DeflateOutput(void *v, struct ccp *ccp, struct link *l, int pri, u_short *proto,
+ struct mbuf *mp)
+{
+ struct deflate_state *state = (struct deflate_state *)v;
+ u_char *wp, *rp;
+ int olen, ilen, len, res, flush;
+ struct mbuf *mo_head, *mo, *mi_head, *mi;
+
+ ilen = m_length(mp);
+ log_Printf(LogDEBUG, "DeflateOutput: Proto %02x (%d bytes)\n", *proto, ilen);
+ log_DumpBp(LogDEBUG, "DeflateOutput: Compress packet:", mp);
+
+ /* Stuff the protocol in front of the input */
+ mi_head = mi = m_get(2, MB_CCPOUT);
+ mi->m_next = mp;
+ rp = MBUF_CTOP(mi);
+ if (*proto < 0x100) { /* Compress the protocol */
+ rp[0] = *proto & 0377;
+ mi->m_len = 1;
+ } else { /* Don't compress the protocol */
+ rp[0] = *proto >> 8;
+ rp[1] = *proto & 0377;
+ mi->m_len = 2;
+ }
+
+ /* Allocate the initial output mbuf */
+ mo_head = mo = m_get(DEFLATE_CHUNK_LEN, MB_CCPOUT);
+ mo->m_len = 2;
+ wp = MBUF_CTOP(mo);
+ *wp++ = state->seqno >> 8;
+ *wp++ = state->seqno & 0377;
+ log_Printf(LogDEBUG, "DeflateOutput: Seq %d\n", state->seqno);
+ state->seqno++;
+
+ /* Set up the deflation context */
+ state->cx.next_out = wp;
+ state->cx.avail_out = DEFLATE_CHUNK_LEN - 2;
+ state->cx.next_in = MBUF_CTOP(mi);
+ state->cx.avail_in = mi->m_len;
+ flush = Z_NO_FLUSH;
+
+ olen = 0;
+ while (1) {
+ if ((res = deflate(&state->cx, flush)) != Z_OK) {
+ if (res == Z_STREAM_END)
+ break; /* Done */
+ log_Printf(LogWARN, "DeflateOutput: deflate returned %d (%s)\n",
+ res, state->cx.msg ? state->cx.msg : "");
+ m_freem(mo_head);
+ m_free(mi_head);
+ state->seqno--;
+ return mp; /* Our dictionary's probably dead now :-( */
+ }
+
+ if (flush == Z_SYNC_FLUSH && state->cx.avail_out != 0)
+ break;
+
+ if (state->cx.avail_in == 0 && mi->m_next != NULL) {
+ mi = mi->m_next;
+ state->cx.next_in = MBUF_CTOP(mi);
+ state->cx.avail_in = mi->m_len;
+ if (mi->m_next == NULL)
+ flush = Z_SYNC_FLUSH;
+ }
+
+ if (state->cx.avail_out == 0) {
+ mo->m_next = m_get(DEFLATE_CHUNK_LEN, MB_CCPOUT);
+ olen += (mo->m_len = DEFLATE_CHUNK_LEN);
+ mo = mo->m_next;
+ mo->m_len = 0;
+ state->cx.next_out = MBUF_CTOP(mo);
+ state->cx.avail_out = DEFLATE_CHUNK_LEN;
+ }
+ }
+
+ olen += (mo->m_len = DEFLATE_CHUNK_LEN - state->cx.avail_out);
+ olen -= 4; /* exclude the trailing EMPTY_BLOCK */
+
+ /*
+ * If the output packet (including seqno and excluding the EMPTY_BLOCK)
+ * got bigger, send the original.
+ */
+ if (olen >= ilen) {
+ m_freem(mo_head);
+ m_free(mi_head);
+ log_Printf(LogDEBUG, "DeflateOutput: %d => %d: Uncompressible (0x%04x)\n",
+ ilen, olen, *proto);
+ ccp->uncompout += ilen;
+ ccp->compout += ilen; /* We measure this stuff too */
+ return mp;
+ }
+
+ m_freem(mi_head);
+
+ /*
+ * Lose the last four bytes of our output.
+ * XXX: We should probably assert that these are the same as the
+ * contents of EMPTY_BLOCK.
+ */
+ mo = mo_head;
+ for (len = mo->m_len; len < olen; mo = mo->m_next, len += mo->m_len)
+ ;
+ mo->m_len -= len - olen;
+ if (mo->m_next != NULL) {
+ m_freem(mo->m_next);
+ mo->m_next = NULL;
+ }
+
+ ccp->uncompout += ilen;
+ ccp->compout += olen;
+
+ log_Printf(LogDEBUG, "DeflateOutput: %d => %d bytes, proto 0x%04x\n",
+ ilen, olen, *proto);
+
+ *proto = ccp_Proto(ccp);
+ return mo_head;
+}
+
+static void
+DeflateResetInput(void *v)
+{
+ struct deflate_state *state = (struct deflate_state *)v;
+
+ state->seqno = 0;
+ state->uncomp_rec = 0;
+ inflateReset(&state->cx);
+ log_Printf(LogCCP, "Deflate: Input channel reset\n");
+}
+
+static struct mbuf *
+DeflateInput(void *v, struct ccp *ccp, u_short *proto, struct mbuf *mi)
+{
+ struct deflate_state *state = (struct deflate_state *)v;
+ struct mbuf *mo, *mo_head, *mi_head;
+ u_char *wp;
+ int ilen, olen;
+ int seq, flush, res, first;
+ u_char hdr[2];
+
+ log_DumpBp(LogDEBUG, "DeflateInput: Decompress packet:", mi);
+ mi_head = mi = mbuf_Read(mi, hdr, 2);
+ ilen = 2;
+
+ /* Check the sequence number. */
+ seq = (hdr[0] << 8) + hdr[1];
+ log_Printf(LogDEBUG, "DeflateInput: Seq %d\n", seq);
+ if (seq != state->seqno) {
+ if (seq <= state->uncomp_rec)
+ /*
+ * So the peer's started at zero again - fine ! If we're wrong,
+ * inflate() will fail. This is better than getting into a loop
+ * trying to get a ResetReq to a busy sender.
+ */
+ state->seqno = seq;
+ else {
+ log_Printf(LogCCP, "DeflateInput: Seq error: Got %d, expected %d\n",
+ seq, state->seqno);
+ m_freem(mi_head);
+ ccp_SendResetReq(&ccp->fsm);
+ return NULL;
+ }
+ }
+ state->seqno++;
+ state->uncomp_rec = 0;
+
+ /* Allocate an output mbuf */
+ mo_head = mo = m_get(DEFLATE_CHUNK_LEN, MB_CCPIN);
+
+ /* Our proto starts with 0 if it's compressed */
+ wp = MBUF_CTOP(mo);
+ wp[0] = '\0';
+
+ /*
+ * We set avail_out to 1 initially so we can look at the first
+ * byte of the output and decide whether we have a compressed
+ * proto field.
+ */
+ state->cx.next_in = MBUF_CTOP(mi);
+ state->cx.avail_in = mi->m_len;
+ state->cx.next_out = wp + 1;
+ state->cx.avail_out = 1;
+ ilen += mi->m_len;
+
+ flush = mi->m_next ? Z_NO_FLUSH : Z_SYNC_FLUSH;
+ first = 1;
+ olen = 0;
+
+ while (1) {
+ if ((res = inflate(&state->cx, flush)) != Z_OK) {
+ if (res == Z_STREAM_END)
+ break; /* Done */
+ log_Printf(LogCCP, "DeflateInput: inflate returned %d (%s)\n",
+ res, state->cx.msg ? state->cx.msg : "");
+ m_freem(mo_head);
+ m_freem(mi);
+ ccp_SendResetReq(&ccp->fsm);
+ return NULL;
+ }
+
+ if (flush == Z_SYNC_FLUSH && state->cx.avail_out != 0)
+ break;
+
+ if (state->cx.avail_in == 0 && mi && (mi = m_free(mi)) != NULL) {
+ /* underflow */
+ state->cx.next_in = MBUF_CTOP(mi);
+ ilen += (state->cx.avail_in = mi->m_len);
+ if (mi->m_next == NULL)
+ flush = Z_SYNC_FLUSH;
+ }
+
+ if (state->cx.avail_out == 0) {
+ /* overflow */
+ if (first) {
+ if (!(wp[1] & 1)) {
+ /* 2 byte proto, shuffle it back in output */
+ wp[0] = wp[1];
+ state->cx.next_out--;
+ state->cx.avail_out = DEFLATE_CHUNK_LEN-1;
+ } else
+ state->cx.avail_out = DEFLATE_CHUNK_LEN-2;
+ first = 0;
+ } else {
+ olen += (mo->m_len = DEFLATE_CHUNK_LEN);
+ mo->m_next = m_get(DEFLATE_CHUNK_LEN, MB_CCPIN);
+ mo = mo->m_next;
+ state->cx.next_out = MBUF_CTOP(mo);
+ state->cx.avail_out = DEFLATE_CHUNK_LEN;
+ }
+ }
+ }
+
+ if (mi != NULL)
+ m_freem(mi);
+
+ if (first) {
+ log_Printf(LogCCP, "DeflateInput: Length error\n");
+ m_freem(mo_head);
+ ccp_SendResetReq(&ccp->fsm);
+ return NULL;
+ }
+
+ olen += (mo->m_len = DEFLATE_CHUNK_LEN - state->cx.avail_out);
+
+ *proto = ((u_short)wp[0] << 8) | wp[1];
+ mo_head->m_offset += 2;
+ mo_head->m_len -= 2;
+ olen -= 2;
+
+ ccp->compin += ilen;
+ ccp->uncompin += olen;
+
+ log_Printf(LogDEBUG, "DeflateInput: %d => %d bytes, proto 0x%04x\n",
+ ilen, olen, *proto);
+
+ /*
+ * Simulate an EMPTY_BLOCK so that our dictionary stays in sync.
+ * The peer will have silently removed this!
+ */
+ state->cx.next_out = garbage;
+ state->cx.avail_out = sizeof garbage;
+ state->cx.next_in = EMPTY_BLOCK;
+ state->cx.avail_in = sizeof EMPTY_BLOCK;
+ inflate(&state->cx, Z_SYNC_FLUSH);
+
+ return mo_head;
+}
+
+static void
+DeflateDictSetup(void *v, struct ccp *ccp, u_short proto, struct mbuf *mi)
+{
+ struct deflate_state *state = (struct deflate_state *)v;
+ int res, flush, expect_error;
+ u_char *rp;
+ struct mbuf *mi_head;
+ short len;
+
+ log_Printf(LogDEBUG, "DeflateDictSetup: Got seq %d\n", state->seqno);
+
+ /*
+ * Stuff an ``uncompressed data'' block header followed by the
+ * protocol in front of the input
+ */
+ mi_head = m_get(7, MB_CCPOUT);
+ mi_head->m_next = mi;
+ len = m_length(mi);
+ mi = mi_head;
+ rp = MBUF_CTOP(mi);
+ if (proto < 0x100) { /* Compress the protocol */
+ rp[5] = proto & 0377;
+ mi->m_len = 6;
+ len++;
+ } else { /* Don't compress the protocol */
+ rp[5] = proto >> 8;
+ rp[6] = proto & 0377;
+ mi->m_len = 7;
+ len += 2;
+ }
+ rp[0] = 0x80; /* BITS: 100xxxxx */
+ rp[1] = len & 0377; /* The length */
+ rp[2] = len >> 8;
+ rp[3] = (~len) & 0377; /* One's compliment of the length */
+ rp[4] = (~len) >> 8;
+
+ state->cx.next_in = rp;
+ state->cx.avail_in = mi->m_len;
+ state->cx.next_out = garbage;
+ state->cx.avail_out = sizeof garbage;
+ flush = Z_NO_FLUSH;
+ expect_error = 0;
+
+ while (1) {
+ if ((res = inflate(&state->cx, flush)) != Z_OK) {
+ if (res == Z_STREAM_END)
+ break; /* Done */
+ if (expect_error && res == Z_BUF_ERROR)
+ break;
+ log_Printf(LogCCP, "DeflateDictSetup: inflate returned %d (%s)\n",
+ res, state->cx.msg ? state->cx.msg : "");
+ log_Printf(LogCCP, "DeflateDictSetup: avail_in %d, avail_out %d\n",
+ state->cx.avail_in, state->cx.avail_out);
+ ccp_SendResetReq(&ccp->fsm);
+ m_free(mi_head); /* lose our allocated ``head'' buf */
+ return;
+ }
+
+ if (flush == Z_SYNC_FLUSH && state->cx.avail_out != 0)
+ break;
+
+ if (state->cx.avail_in == 0 && mi && (mi = mi->m_next) != NULL) {
+ /* underflow */
+ state->cx.next_in = MBUF_CTOP(mi);
+ state->cx.avail_in = mi->m_len;
+ if (mi->m_next == NULL)
+ flush = Z_SYNC_FLUSH;
+ }
+
+ if (state->cx.avail_out == 0) {
+ if (state->cx.avail_in == 0)
+ /*
+ * This seems to be a bug in libz ! If inflate() finished
+ * with 0 avail_in and 0 avail_out *and* this is the end of
+ * our input *and* inflate() *has* actually written all the
+ * output it's going to, it *doesn't* return Z_STREAM_END !
+ * When we subsequently call it with no more input, it gives
+ * us Z_BUF_ERROR :-( It seems pretty safe to ignore this
+ * error (the dictionary seems to stay in sync). In the worst
+ * case, we'll drop the next compressed packet and do a
+ * CcpReset() then.
+ */
+ expect_error = 1;
+ /* overflow */
+ state->cx.next_out = garbage;
+ state->cx.avail_out = sizeof garbage;
+ }
+ }
+
+ ccp->compin += len;
+ ccp->uncompin += len;
+
+ state->seqno++;
+ state->uncomp_rec++;
+ m_free(mi_head); /* lose our allocated ``head'' buf */
+}
+
+static const char *
+DeflateDispOpts(struct fsm_opt *o)
+{
+ static char disp[7]; /* Must be used immediately */
+
+ sprintf(disp, "win %d", (o->data[0]>>4) + 8);
+ return disp;
+}
+
+static void
+DeflateInitOptsOutput(struct bundle *bundle, struct fsm_opt *o,
+ const struct ccp_config *cfg)
+{
+ o->hdr.len = 4;
+ o->data[0] = ((cfg->deflate.out.winsize - 8) << 4) + 8;
+ o->data[1] = '\0';
+}
+
+static int
+DeflateSetOptsOutput(struct bundle *bundle, struct fsm_opt *o,
+ const struct ccp_config *cfg)
+{
+ if (o->hdr.len != 4 || (o->data[0] & 15) != 8 || o->data[1] != '\0')
+ return MODE_REJ;
+
+ if ((o->data[0] >> 4) + 8 > 15) {
+ o->data[0] = ((15 - 8) << 4) + 8;
+ return MODE_NAK;
+ }
+
+ return MODE_ACK;
+}
+
+static int
+DeflateSetOptsInput(struct bundle *bundle, struct fsm_opt *o,
+ const struct ccp_config *cfg)
+{
+ int want;
+
+ if (o->hdr.len != 4 || (o->data[0] & 15) != 8 || o->data[1] != '\0')
+ return MODE_REJ;
+
+ want = (o->data[0] >> 4) + 8;
+ if (cfg->deflate.in.winsize == 0) {
+ if (want < 8 || want > 15) {
+ o->data[0] = ((15 - 8) << 4) + 8;
+ }
+ } else if (want != cfg->deflate.in.winsize) {
+ o->data[0] = ((cfg->deflate.in.winsize - 8) << 4) + 8;
+ return MODE_NAK;
+ }
+
+ return MODE_ACK;
+}
+
+static void *
+DeflateInitInput(struct bundle *bundle, struct fsm_opt *o)
+{
+ struct deflate_state *state;
+
+ state = (struct deflate_state *)malloc(sizeof(struct deflate_state));
+ if (state != NULL) {
+ state->winsize = (o->data[0] >> 4) + 8;
+ state->cx.zalloc = NULL;
+ state->cx.opaque = NULL;
+ state->cx.zfree = NULL;
+ state->cx.next_out = NULL;
+ if (inflateInit2(&state->cx, -state->winsize) == Z_OK)
+ DeflateResetInput(state);
+ else {
+ free(state);
+ state = NULL;
+ }
+ }
+
+ return state;
+}
+
+static void *
+DeflateInitOutput(struct bundle *bundle, struct fsm_opt *o)
+{
+ struct deflate_state *state;
+
+ state = (struct deflate_state *)malloc(sizeof(struct deflate_state));
+ if (state != NULL) {
+ state->winsize = (o->data[0] >> 4) + 8;
+ state->cx.zalloc = NULL;
+ state->cx.opaque = NULL;
+ state->cx.zfree = NULL;
+ state->cx.next_in = NULL;
+ if (deflateInit2(&state->cx, Z_DEFAULT_COMPRESSION, 8,
+ -state->winsize, 8, Z_DEFAULT_STRATEGY) == Z_OK)
+ DeflateResetOutput(state);
+ else {
+ free(state);
+ state = NULL;
+ }
+ }
+
+ return state;
+}
+
+static void
+DeflateTermInput(void *v)
+{
+ struct deflate_state *state = (struct deflate_state *)v;
+
+ inflateEnd(&state->cx);
+ free(state);
+}
+
+static void
+DeflateTermOutput(void *v)
+{
+ struct deflate_state *state = (struct deflate_state *)v;
+
+ deflateEnd(&state->cx);
+ free(state);
+}
+
+const struct ccp_algorithm PppdDeflateAlgorithm = {
+ TY_PPPD_DEFLATE, /* Older versions of pppd expected this ``type'' */
+ CCP_NEG_DEFLATE24,
+ DeflateDispOpts,
+ ccp_DefaultUsable,
+ ccp_DefaultRequired,
+ {
+ DeflateSetOptsInput,
+ DeflateInitInput,
+ DeflateTermInput,
+ DeflateResetInput,
+ DeflateInput,
+ DeflateDictSetup
+ },
+ {
+ 0,
+ DeflateInitOptsOutput,
+ DeflateSetOptsOutput,
+ DeflateInitOutput,
+ DeflateTermOutput,
+ DeflateResetOutput,
+ DeflateOutput
+ },
+};
+
+const struct ccp_algorithm DeflateAlgorithm = {
+ TY_DEFLATE, /* rfc 1979 */
+ CCP_NEG_DEFLATE,
+ DeflateDispOpts,
+ ccp_DefaultUsable,
+ ccp_DefaultRequired,
+ {
+ DeflateSetOptsInput,
+ DeflateInitInput,
+ DeflateTermInput,
+ DeflateResetInput,
+ DeflateInput,
+ DeflateDictSetup
+ },
+ {
+ 0,
+ DeflateInitOptsOutput,
+ DeflateSetOptsOutput,
+ DeflateInitOutput,
+ DeflateTermOutput,
+ DeflateResetOutput,
+ DeflateOutput
+ },
+};
diff --git a/usr.sbin/ppp/deflate.h b/usr.sbin/ppp/deflate.h
new file mode 100644
index 0000000..a478b24
--- /dev/null
+++ b/usr.sbin/ppp/deflate.h
@@ -0,0 +1,30 @@
+/*-
+ * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+extern const struct ccp_algorithm PppdDeflateAlgorithm;
+extern const struct ccp_algorithm DeflateAlgorithm;
diff --git a/usr.sbin/ppp/defs.c b/usr.sbin/ppp/defs.c
new file mode 100644
index 0000000..f29eff6
--- /dev/null
+++ b/usr.sbin/ppp/defs.c
@@ -0,0 +1,443 @@
+/*-
+ * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+
+#include <sys/param.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#if defined(__FreeBSD__) && !defined(NOKLDLOAD)
+#include <sys/module.h>
+#endif
+#include <termios.h>
+#if !defined(__FreeBSD__) || __FreeBSD__ < 3
+#include <time.h>
+#endif
+#include <unistd.h>
+
+#if defined(__FreeBSD__) && !defined(NOKLDLOAD)
+#include "id.h"
+#include "log.h"
+#endif
+#include "defs.h"
+
+#define issep(c) ((c) == '\t' || (c) == ' ')
+
+#if defined(__NetBSD__) || __FreeBSD__ < 3
+void
+randinit()
+{
+#if defined(__FreeBSD__)
+ static int initdone; /* srandomdev() call is only required once */
+
+ if (!initdone) {
+ initdone = 1;
+ srandomdev();
+ }
+#else
+ srandom((time(NULL)^getpid())+random());
+#endif
+}
+#endif
+
+ssize_t
+fullread(int fd, void *v, size_t n)
+{
+ size_t got, total;
+
+ for (total = 0; total < n; total += got)
+ switch ((got = read(fd, (char *)v + total, n - total))) {
+ case 0:
+ return total;
+ case -1:
+ if (errno == EINTR)
+ got = 0;
+ else
+ return -1;
+ }
+ return total;
+}
+
+static struct {
+ int mode;
+ const char *name;
+} modes[] = {
+ { PHYS_INTERACTIVE, "interactive" },
+ { PHYS_AUTO, "auto" },
+ { PHYS_DIRECT, "direct" },
+ { PHYS_DEDICATED, "dedicated" },
+ { PHYS_DDIAL, "ddial" },
+ { PHYS_BACKGROUND, "background" },
+ { PHYS_FOREGROUND, "foreground" },
+ { PHYS_ALL, "*" },
+ { 0, 0 }
+};
+
+const char *
+mode2Nam(int mode)
+{
+ int m;
+
+ for (m = 0; modes[m].mode; m++)
+ if (modes[m].mode == mode)
+ return modes[m].name;
+
+ return "unknown";
+}
+
+int
+Nam2mode(const char *name)
+{
+ int m, got, len;
+
+ len = strlen(name);
+ got = -1;
+ for (m = 0; modes[m].mode; m++)
+ if (!strncasecmp(name, modes[m].name, len)) {
+ if (modes[m].name[len] == '\0')
+ return modes[m].mode;
+ if (got != -1)
+ return 0;
+ got = m;
+ }
+
+ return got == -1 ? 0 : modes[got].mode;
+}
+
+struct in_addr
+GetIpAddr(const char *cp)
+{
+ struct in_addr ipaddr;
+
+ if (!strcasecmp(cp, "default"))
+ ipaddr.s_addr = INADDR_ANY;
+ else if (inet_aton(cp, &ipaddr) == 0) {
+ const char *ptr;
+
+ /* Any illegal characters ? */
+ for (ptr = cp; *ptr != '\0'; ptr++)
+ if (!isalnum(*ptr) && strchr("-.", *ptr) == NULL)
+ break;
+
+ if (*ptr == '\0') {
+ struct hostent *hp;
+
+ hp = gethostbyname(cp);
+ if (hp && hp->h_addrtype == AF_INET)
+ memcpy(&ipaddr, hp->h_addr, hp->h_length);
+ else
+ ipaddr.s_addr = INADDR_NONE;
+ } else
+ ipaddr.s_addr = INADDR_NONE;
+ }
+
+ return ipaddr;
+}
+
+static const struct speeds {
+ int nspeed;
+ speed_t speed;
+} speeds[] = {
+#ifdef B50
+ { 50, B50, },
+#endif
+#ifdef B75
+ { 75, B75, },
+#endif
+#ifdef B110
+ { 110, B110, },
+#endif
+#ifdef B134
+ { 134, B134, },
+#endif
+#ifdef B150
+ { 150, B150, },
+#endif
+#ifdef B200
+ { 200, B200, },
+#endif
+#ifdef B300
+ { 300, B300, },
+#endif
+#ifdef B600
+ { 600, B600, },
+#endif
+#ifdef B1200
+ { 1200, B1200, },
+#endif
+#ifdef B1800
+ { 1800, B1800, },
+#endif
+#ifdef B2400
+ { 2400, B2400, },
+#endif
+#ifdef B4800
+ { 4800, B4800, },
+#endif
+#ifdef B9600
+ { 9600, B9600, },
+#endif
+#ifdef B19200
+ { 19200, B19200, },
+#endif
+#ifdef B38400
+ { 38400, B38400, },
+#endif
+#ifndef _POSIX_SOURCE
+#ifdef B7200
+ { 7200, B7200, },
+#endif
+#ifdef B14400
+ { 14400, B14400, },
+#endif
+#ifdef B28800
+ { 28800, B28800, },
+#endif
+#ifdef B57600
+ { 57600, B57600, },
+#endif
+#ifdef B76800
+ { 76800, B76800, },
+#endif
+#ifdef B115200
+ { 115200, B115200, },
+#endif
+#ifdef B230400
+ { 230400, B230400, },
+#endif
+#ifdef B460800
+ { 460800, B460800, },
+#endif
+#ifdef B921600
+ { 921600, B921600, },
+#endif
+#ifdef EXTA
+ { 19200, EXTA, },
+#endif
+#ifdef EXTB
+ { 38400, EXTB, },
+#endif
+#endif /* _POSIX_SOURCE */
+ { 0, 0 }
+};
+
+int
+SpeedToInt(speed_t speed)
+{
+ const struct speeds *sp;
+
+ for (sp = speeds; sp->nspeed; sp++) {
+ if (sp->speed == speed) {
+ return sp->nspeed;
+ }
+ }
+ return 0;
+}
+
+speed_t
+IntToSpeed(int nspeed)
+{
+ const struct speeds *sp;
+
+ for (sp = speeds; sp->nspeed; sp++) {
+ if (sp->nspeed == nspeed) {
+ return sp->speed;
+ }
+ }
+ return B0;
+}
+
+char *
+findblank(char *p, int flags)
+{
+ int instring;
+
+ instring = 0;
+ while (*p) {
+ if (*p == '\\') {
+ if (flags & PARSE_REDUCE) {
+ memmove(p, p + 1, strlen(p));
+ if (!*p)
+ break;
+ } else
+ p++;
+ } else if (*p == '"') {
+ memmove(p, p + 1, strlen(p));
+ instring = !instring;
+ continue;
+ } else if (!instring && (issep(*p) ||
+ (*p == '#' && !(flags & PARSE_NOHASH))))
+ return p;
+ p++;
+ }
+
+ return instring ? NULL : p;
+}
+
+int
+MakeArgs(char *script, char **pvect, int maxargs, int flags)
+{
+ int nargs;
+
+ nargs = 0;
+ while (*script) {
+ script += strspn(script, " \t");
+ if (*script == '#' && !(flags & PARSE_NOHASH)) {
+ *script = '\0';
+ break;
+ }
+ if (*script) {
+ if (nargs >= maxargs - 1)
+ break;
+ *pvect++ = script;
+ nargs++;
+ script = findblank(script, flags);
+ if (script == NULL)
+ return -1;
+ else if (!(flags & PARSE_NOHASH) && *script == '#')
+ *script = '\0';
+ else if (*script)
+ *script++ = '\0';
+ }
+ }
+ *pvect = NULL;
+ return nargs;
+}
+
+const char *
+NumStr(long val, char *buf, size_t sz)
+{
+ static char result[23]; /* handles 64 bit numbers */
+
+ if (buf == NULL || sz == 0) {
+ buf = result;
+ sz = sizeof result;
+ }
+ snprintf(buf, sz, "<%ld>", val);
+ return buf;
+}
+
+const char *
+HexStr(long val, char *buf, size_t sz)
+{
+ static char result[21]; /* handles 64 bit numbers */
+
+ if (buf == NULL || sz == 0) {
+ buf = result;
+ sz = sizeof result;
+ }
+ snprintf(buf, sz, "<0x%lx>", val);
+ return buf;
+}
+
+const char *
+ex_desc(int ex)
+{
+ static char num[12]; /* Used immediately if returned */
+ static const char * const desc[] = {
+ "normal", "start", "sock", "modem", "dial", "dead", "done",
+ "reboot", "errdead", "hangup", "term", "nodial", "nologin",
+ "redial", "reconnect"
+ };
+
+ if (ex >= 0 && ex < sizeof desc / sizeof *desc)
+ return desc[ex];
+ snprintf(num, sizeof num, "%d", ex);
+ return num;
+}
+
+void
+SetTitle(const char *title)
+{
+ if (title == NULL)
+ setproctitle(NULL);
+ else if (title[0] == '-' && title[1] != '\0')
+ setproctitle("-%s", title + 1);
+ else
+ setproctitle("%s", title);
+}
+
+fd_set *
+mkfdset()
+{
+ return (fd_set *)malloc(howmany(getdtablesize(), NFDBITS) * sizeof (fd_mask));
+}
+
+void
+zerofdset(fd_set *s)
+{
+ memset(s, '\0', howmany(getdtablesize(), NFDBITS) * sizeof (fd_mask));
+}
+
+void
+Concatinate(char *buf, size_t sz, int argc, const char *const *argv)
+{
+ int i, n, pos;
+
+ *buf = '\0';
+ for (pos = i = 0; i < argc; i++) {
+ n = snprintf(buf + pos, sz - pos, "%s%s", i ? " " : "", argv[i]);
+ if (n < 0) {
+ buf[pos] = '\0';
+ break;
+ }
+ if ((pos += n) >= sz)
+ break;
+ }
+}
+
+int
+loadmodules(int how, const char *module, ...)
+{
+ int loaded = 0;
+#if defined(__FreeBSD__) && !defined(NOKLDLOAD)
+ va_list ap;
+
+ va_start(ap, module);
+ while (module != NULL) {
+ if (modfind(module) == -1) {
+ if (ID0kldload(module) == -1) {
+ if (how == LOAD_VERBOSLY)
+ log_Printf(LogWARN, "%s: Cannot load module\n", module);
+ } else
+ loaded++;
+ }
+ module = va_arg(ap, const char *);
+ }
+ va_end(ap);
+#endif
+ return loaded;
+}
diff --git a/usr.sbin/ppp/defs.h b/usr.sbin/ppp/defs.h
new file mode 100644
index 0000000..2be7686
--- /dev/null
+++ b/usr.sbin/ppp/defs.h
@@ -0,0 +1,142 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/* Check the following definitions for your machine environment */
+#ifdef __FreeBSD__
+# define MODEM_LIST "/dev/cuaa1\0/dev/cuaa0" /* name of tty device */
+#else
+# ifdef __OpenBSD__
+# define MODEM_LIST "/dev/cua01\0/dev/cua00" /* name of tty device */
+# else
+# define MODEM_LIST "/dev/tty01\0/dev/tty00" /* name of tty device */
+# endif
+#endif
+#define NMODEMS 2
+
+#ifndef PPP_CONFDIR
+#define PPP_CONFDIR "/etc/ppp"
+#endif
+
+#define TUN_NAME "tun"
+#define TUN_PREFIX (_PATH_DEV TUN_NAME) /* /dev/tun */
+
+#define MODEM_SPEED B38400 /* tty speed */
+#define SERVER_PORT 3000 /* Base server port no. */
+#define MODEM_CTSRTS 1 /* Default (true): use CTS/RTS signals */
+#define RECONNECT_TIMEOUT 3 /* Default timer for carrier loss */
+#define DIAL_TIMEOUT 30 /* Default and Max random time to redial */
+#define DIAL_NEXT_TIMEOUT 3 /* Default Hold time to next number redial */
+#define SCRIPT_LEN 512 /* Size of login/dial/hangup scripts */
+#define LINE_LEN SCRIPT_LEN /* Size of lines */
+#define DEVICE_LEN SCRIPT_LEN /* Size of individual devices */
+#define AUTHLEN 100 /* Size of authname/authkey */
+#define CHAPDIGESTLEN 100 /* Maximum chap digest */
+#define CHAPCHALLENGELEN 48 /* Maximum chap challenge */
+#define CHAPAUTHRESPONSELEN 48 /* Maximum chap authresponse (chap81) */
+#define MAXARGS 40 /* How many args per config line */
+#define NCP_IDLE_TIMEOUT 180 /* Drop all links */
+#define CHOKED_TIMEOUT 120 /* Delete queued packets w/ blocked tun */
+
+#define MIN_LQRPERIOD 1 /* Minimum LQR frequency */
+#define DEF_LQRPERIOD 30 /* Default LQR frequency */
+#define MIN_FSMRETRY 1 /* Minimum FSM retry frequency */
+#define DEF_FSMRETRY 3 /* FSM retry frequency */
+#define DEF_FSMTRIES 5 /* Default max retries */
+#define DEF_FSMAUTHTRIES 3 /* Default max auth retries */
+#define DEF_IFQUEUE 30 /* Default interface queue size */
+
+#define CONFFILE "ppp.conf"
+#define LINKUPFILE "ppp.linkup"
+#define LINKDOWNFILE "ppp.linkdown"
+#define SECRETFILE "ppp.secret"
+
+#define EX_SIG -1
+#define EX_NORMAL 0
+#define EX_START 1
+#define EX_SOCK 2
+#define EX_MODEM 3
+#define EX_DIAL 4
+#define EX_DEAD 5
+#define EX_DONE 6
+#define EX_REBOOT 7
+#define EX_ERRDEAD 8
+#define EX_HANGUP 9
+#define EX_TERM 10
+#define EX_NODIAL 11
+#define EX_NOLOGIN 12
+/* return values for -background mode, not really exits */
+#define EX_REDIAL 13
+#define EX_RECONNECT 14
+
+/* physical::type values (OR'd in bundle::phys_type) */
+#define PHYS_NONE 0
+#define PHYS_INTERACTIVE 1 /* Manual link */
+#define PHYS_AUTO 2 /* Dial-on-demand link */
+#define PHYS_DIRECT 4 /* Incoming link, deleted when closed */
+#define PHYS_DEDICATED 8 /* Dedicated link */
+#define PHYS_DDIAL 16 /* Dial immediately, stay connected */
+#define PHYS_BACKGROUND 32 /* Dial immediately, deleted when closed */
+#define PHYS_FOREGROUND 64 /* Pseudo mode, same as background */
+#define PHYS_ALL 127
+
+/* flags passed to findblank() and MakeArgs() */
+#define PARSE_NORMAL 0
+#define PARSE_REDUCE 1
+#define PARSE_NOHASH 2
+
+/* flags passed to loadmodules */
+#define LOAD_QUIETLY 1
+#define LOAD_VERBOSLY 2
+
+#define ROUNDUP(x) ((x) ? (1 + (((x) - 1) | (sizeof(long) - 1))) : sizeof(long))
+
+#if defined(__NetBSD__) || __FreeBSD__ < 3
+extern void randinit(void);
+#else
+#define random arc4random
+#define randinit()
+#endif
+
+extern ssize_t fullread(int, void *, size_t);
+extern const char *mode2Nam(int);
+extern int Nam2mode(const char *);
+extern struct in_addr GetIpAddr(const char *);
+extern int SpeedToInt(speed_t);
+extern speed_t IntToSpeed(int);
+extern char *findblank(char *, int);
+extern int MakeArgs(char *, char **, int, int);
+extern const char *NumStr(long, char *, size_t);
+extern const char *HexStr(long, char *, size_t);
+extern const char *ex_desc(int);
+extern void SetTitle(const char *);
+extern fd_set *mkfdset(void);
+extern void zerofdset(fd_set *);
+extern void Concatinate(char *, size_t, int, const char *const *);
+extern int loadmodules(int, const char *, ...);
diff --git a/usr.sbin/ppp/descriptor.h b/usr.sbin/ppp/descriptor.h
new file mode 100644
index 0000000..a3c1b10
--- /dev/null
+++ b/usr.sbin/ppp/descriptor.h
@@ -0,0 +1,53 @@
+/*-
+ * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define PHYSICAL_DESCRIPTOR (1)
+#define SERVER_DESCRIPTOR (2)
+#define PROMPT_DESCRIPTOR (3)
+#define CHAT_DESCRIPTOR (4)
+#define DATALINK_DESCRIPTOR (5)
+#define BUNDLE_DESCRIPTOR (6)
+#define MPSERVER_DESCRIPTOR (7)
+#define RADIUS_DESCRIPTOR (8)
+#define CHAP_DESCRIPTOR (9)
+
+struct bundle;
+
+struct fdescriptor {
+ int type;
+
+ int (*UpdateSet)(struct fdescriptor *, fd_set *, fd_set *, fd_set *, int *);
+ int (*IsSet)(struct fdescriptor *, const fd_set *);
+ void (*Read)(struct fdescriptor *, struct bundle *, const fd_set *);
+ int (*Write)(struct fdescriptor *, struct bundle *, const fd_set *);
+};
+
+#define descriptor_UpdateSet(d, r, w, e, n) ((*(d)->UpdateSet)(d, r, w, e, n))
+#define descriptor_IsSet(d, s) ((*(d)->IsSet)(d, s))
+#define descriptor_Read(d, b, f) ((*(d)->Read)(d, b, f))
+#define descriptor_Write(d, b, f) ((*(d)->Write)(d, b, f))
diff --git a/usr.sbin/ppp/ether.c b/usr.sbin/ppp/ether.c
new file mode 100644
index 0000000..77ad7e5
--- /dev/null
+++ b/usr.sbin/ppp/ether.c
@@ -0,0 +1,722 @@
+/*-
+ * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <netgraph.h>
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/route.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netgraph/ng_ether.h>
+#include <netgraph/ng_message.h>
+#include <netgraph/ng_pppoe.h>
+#include <netgraph/ng_socket.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <termios.h>
+#include <sys/time.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "layer.h"
+#include "defs.h"
+#include "mbuf.h"
+#include "log.h"
+#include "timer.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "throughput.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "async.h"
+#include "descriptor.h"
+#include "physical.h"
+#include "main.h"
+#include "mp.h"
+#include "chat.h"
+#include "auth.h"
+#include "chap.h"
+#include "cbcp.h"
+#include "datalink.h"
+#include "slcompress.h"
+#include "iplist.h"
+#include "ncpaddr.h"
+#include "ip.h"
+#include "ipcp.h"
+#include "filter.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+#include "id.h"
+#include "iface.h"
+#include "route.h"
+#include "ether.h"
+
+
+#define PPPOE_NODE_TYPE_LEN (sizeof NG_PPPOE_NODE_TYPE - 1) /* "PPPoE" */
+
+struct etherdevice {
+ struct device dev; /* What struct physical knows about */
+ int cs; /* Control socket */
+ int connected; /* Are we connected yet ? */
+ int timeout; /* Seconds attempting to connect */
+ char hook[sizeof TUN_NAME + 11]; /* Our socket node hook */
+ u_int32_t slot; /* ifindex << 24 | unit */
+};
+
+#define device2ether(d) \
+ ((d)->type == ETHER_DEVICE ? (struct etherdevice *)d : NULL)
+
+int
+ether_DeviceSize(void)
+{
+ return sizeof(struct etherdevice);
+}
+
+static ssize_t
+ether_Write(struct physical *p, const void *v, size_t n)
+{
+ struct etherdevice *dev = device2ether(p->handler);
+
+ return NgSendData(p->fd, dev->hook, v, n) == -1 ? -1 : n;
+}
+
+static ssize_t
+ether_Read(struct physical *p, void *v, size_t n)
+{
+ char hook[sizeof TUN_NAME + 11];
+
+ return NgRecvData(p->fd, v, n, hook);
+}
+
+static int
+ether_RemoveFromSet(struct physical *p, fd_set *r, fd_set *w, fd_set *e)
+{
+ struct etherdevice *dev = device2ether(p->handler);
+ int result;
+
+ if (r && dev->cs >= 0 && FD_ISSET(dev->cs, r)) {
+ FD_CLR(dev->cs, r);
+ log_Printf(LogTIMER, "%s: fdunset(ctrl) %d\n", p->link.name, dev->cs);
+ result = 1;
+ } else
+ result = 0;
+
+ /* Careful... physical_RemoveFromSet() called us ! */
+
+ p->handler->removefromset = NULL;
+ result += physical_RemoveFromSet(p, r, w, e);
+ p->handler->removefromset = ether_RemoveFromSet;
+
+ return result;
+}
+
+static void
+ether_Free(struct physical *p)
+{
+ struct etherdevice *dev = device2ether(p->handler);
+
+ physical_SetDescriptor(p);
+ if (dev->cs != -1)
+ close(dev->cs);
+ free(dev);
+}
+
+static const char *
+ether_OpenInfo(struct physical *p)
+{
+ struct etherdevice *dev = device2ether(p->handler);
+
+ switch (dev->connected) {
+ case CARRIER_PENDING:
+ return "negotiating";
+ case CARRIER_OK:
+ return "established";
+ }
+
+ return "disconnected";
+}
+
+static int
+ether_Slot(struct physical *p)
+{
+ struct etherdevice *dev = device2ether(p->handler);
+
+ return dev->slot;
+}
+
+
+static void
+ether_device2iov(struct device *d, struct iovec *iov, int *niov,
+ int maxiov, int *auxfd, int *nauxfd)
+{
+ struct etherdevice *dev = device2ether(d);
+ int sz = physical_MaxDeviceSize();
+
+ iov[*niov].iov_base = realloc(d, sz);
+ if (iov[*niov].iov_base == NULL) {
+ log_Printf(LogALERT, "Failed to allocate memory: %d\n", sz);
+ AbortProgram(EX_OSERR);
+ }
+ iov[*niov].iov_len = sz;
+ (*niov)++;
+
+ if (dev->cs >= 0) {
+ *auxfd = dev->cs;
+ (*nauxfd)++;
+ }
+}
+
+static void
+ether_MessageIn(struct etherdevice *dev)
+{
+ char msgbuf[sizeof(struct ng_mesg) + sizeof(struct ngpppoe_sts)];
+ struct ng_mesg *rep = (struct ng_mesg *)msgbuf;
+ struct ngpppoe_sts *sts = (struct ngpppoe_sts *)(msgbuf + sizeof *rep);
+ char *end, unknown[14], sessionid[5];
+ const char *msg;
+ struct timeval t;
+ fd_set *r;
+ u_long slot;
+ int asciilen, ret;
+
+ if (dev->cs < 0)
+ return;
+
+ if ((r = mkfdset()) == NULL) {
+ log_Printf(LogERROR, "DoLoop: Cannot create fd_set\n");
+ return;
+ }
+
+ while (1) {
+ zerofdset(r);
+ FD_SET(dev->cs, r);
+ t.tv_sec = t.tv_usec = 0;
+ ret = select(dev->cs + 1, r, NULL, NULL, &t);
+
+ if (ret <= 0)
+ break;
+
+ if (NgRecvMsg(dev->cs, rep, sizeof msgbuf, NULL) <= 0)
+ break;
+
+ if (rep->header.version != NG_VERSION) {
+ log_Printf(LogWARN, "%ld: Unexpected netgraph version, expected %ld\n",
+ (long)rep->header.version, (long)NG_VERSION);
+ break;
+ }
+
+ if (rep->header.typecookie != NGM_PPPOE_COOKIE) {
+ log_Printf(LogWARN, "%ld: Unexpected netgraph cookie, expected %ld\n",
+ (long)rep->header.typecookie, (long)NGM_PPPOE_COOKIE);
+ break;
+ }
+
+ asciilen = 0;
+ switch (rep->header.cmd) {
+ case NGM_PPPOE_SET_FLAG: msg = "SET_FLAG"; break;
+ case NGM_PPPOE_CONNECT: msg = "CONNECT"; break;
+ case NGM_PPPOE_LISTEN: msg = "LISTEN"; break;
+ case NGM_PPPOE_OFFER: msg = "OFFER"; break;
+ case NGM_PPPOE_SUCCESS: msg = "SUCCESS"; break;
+ case NGM_PPPOE_FAIL: msg = "FAIL"; break;
+ case NGM_PPPOE_CLOSE: msg = "CLOSE"; break;
+ case NGM_PPPOE_GET_STATUS: msg = "GET_STATUS"; break;
+ case NGM_PPPOE_ACNAME:
+ msg = "ACNAME";
+ if (setenv("ACNAME", sts->hook, 1) != 0)
+ log_Printf(LogWARN, "setenv: cannot set ACNAME=%s: %m", sts->hook);
+ asciilen = rep->header.arglen;
+ break;
+ case NGM_PPPOE_SESSIONID:
+ msg = "SESSIONID";
+ snprintf(sessionid, sizeof sessionid, "%04x", *(u_int16_t *)sts);
+ if (setenv("SESSIONID", sessionid, 1) != 0)
+ syslog(LOG_WARNING, "setenv: cannot set SESSIONID=%s: %m",
+ sessionid);
+ /* Use this in preference to our interface index */
+ slot = strtoul(sessionid, &end, 16);
+ if (end != sessionid && *end == '\0')
+ dev->slot = slot;
+ break;
+ default:
+ snprintf(unknown, sizeof unknown, "<%d>", (int)rep->header.cmd);
+ msg = unknown;
+ break;
+ }
+
+ if (asciilen)
+ log_Printf(LogPHASE, "Received NGM_PPPOE_%s (hook \"%.*s\")\n",
+ msg, asciilen, sts->hook);
+ else
+ log_Printf(LogPHASE, "Received NGM_PPPOE_%s\n", msg);
+
+ switch (rep->header.cmd) {
+ case NGM_PPPOE_SUCCESS:
+ dev->connected = CARRIER_OK;
+ break;
+ case NGM_PPPOE_FAIL:
+ case NGM_PPPOE_CLOSE:
+ dev->connected = CARRIER_LOST;
+ break;
+ }
+ }
+ free(r);
+}
+
+static int
+ether_AwaitCarrier(struct physical *p)
+{
+ struct etherdevice *dev = device2ether(p->handler);
+
+ if (dev->connected != CARRIER_OK && !dev->timeout--)
+ dev->connected = CARRIER_LOST;
+ else if (dev->connected == CARRIER_PENDING)
+ ether_MessageIn(dev);
+
+ return dev->connected;
+}
+
+static const struct device baseetherdevice = {
+ ETHER_DEVICE,
+ "ether",
+ 1492,
+ { CD_REQUIRED, DEF_ETHERCDDELAY },
+ ether_AwaitCarrier,
+ ether_RemoveFromSet,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ ether_Free,
+ ether_Read,
+ ether_Write,
+ ether_device2iov,
+ NULL,
+ ether_OpenInfo,
+ ether_Slot
+};
+
+struct device *
+ether_iov2device(int type, struct physical *p, struct iovec *iov, int *niov,
+ int maxiov, int *auxfd, int *nauxfd)
+{
+ if (type == ETHER_DEVICE) {
+ struct etherdevice *dev = (struct etherdevice *)iov[(*niov)++].iov_base;
+
+ dev = realloc(dev, sizeof *dev); /* Reduce to the correct size */
+ if (dev == NULL) {
+ log_Printf(LogALERT, "Failed to allocate memory: %d\n",
+ (int)(sizeof *dev));
+ AbortProgram(EX_OSERR);
+ }
+
+ if (*nauxfd) {
+ dev->cs = *auxfd;
+ (*nauxfd)--;
+ } else
+ dev->cs = -1;
+
+ /* Refresh function pointers etc */
+ memcpy(&dev->dev, &baseetherdevice, sizeof dev->dev);
+
+ physical_SetupStack(p, dev->dev.name, PHYSICAL_FORCE_SYNCNOACF);
+ return &dev->dev;
+ }
+
+ return NULL;
+}
+
+static int
+ether_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n)
+{
+ struct physical *p = descriptor2physical(d);
+ struct etherdevice *dev = device2ether(p->handler);
+ int result;
+
+ if (r && dev->cs >= 0) {
+ FD_SET(dev->cs, r);
+ log_Printf(LogTIMER, "%s(ctrl): fdset(r) %d\n", p->link.name, dev->cs);
+ result = 1;
+ } else
+ result = 0;
+
+ result += physical_doUpdateSet(d, r, w, e, n, 0);
+
+ return result;
+}
+
+static int
+ether_IsSet(struct fdescriptor *d, const fd_set *fdset)
+{
+ struct physical *p = descriptor2physical(d);
+ struct etherdevice *dev = device2ether(p->handler);
+ int result;
+
+ result = dev->cs >= 0 && FD_ISSET(dev->cs, fdset);
+ result += physical_IsSet(d, fdset);
+
+ return result;
+}
+
+static void
+ether_DescriptorRead(struct fdescriptor *d, struct bundle *bundle,
+ const fd_set *fdset)
+{
+ struct physical *p = descriptor2physical(d);
+ struct etherdevice *dev = device2ether(p->handler);
+
+ if (dev->cs >= 0 && FD_ISSET(dev->cs, fdset)) {
+ ether_MessageIn(dev);
+ if (dev->connected == CARRIER_LOST) {
+ log_Printf(LogPHASE, "%s: Device disconnected\n", p->link.name);
+ datalink_Down(p->dl, CLOSE_NORMAL);
+ return;
+ }
+ }
+
+ if (physical_IsSet(d, fdset))
+ physical_DescriptorRead(d, bundle, fdset);
+}
+
+static struct device *
+ether_Abandon(struct etherdevice *dev, struct physical *p)
+{
+ /* Abandon our node construction */
+ close(dev->cs);
+ close(p->fd);
+ p->fd = -2; /* Nobody else need try.. */
+ free(dev);
+
+ return NULL;
+}
+
+struct device *
+ether_Create(struct physical *p)
+{
+ u_char rbuf[2048];
+ struct etherdevice *dev;
+ struct ng_mesg *resp;
+ const struct hooklist *hlist;
+ const struct nodeinfo *ninfo;
+ char *path, *sessionid;
+ int ifacelen, f;
+
+ dev = NULL;
+ path = NULL;
+ ifacelen = 0;
+ if (p->fd < 0 && !strncasecmp(p->name.full, NG_PPPOE_NODE_TYPE,
+ PPPOE_NODE_TYPE_LEN) &&
+ p->name.full[PPPOE_NODE_TYPE_LEN] == ':') {
+ const struct linkinfo *nlink;
+ struct ngpppoe_init_data *data;
+ struct ngm_mkpeer mkp;
+ struct ngm_connect ngc;
+ const char *iface, *provider;
+ char etherid[12];
+ int providerlen;
+ char connectpath[sizeof dev->hook + 2]; /* .:<hook> */
+
+ p->fd--; /* We own the device - change fd */
+
+ loadmodules(LOAD_VERBOSLY, "netgraph", "ng_ether", "ng_pppoe", "ng_socket",
+ NULL);
+
+ if ((dev = malloc(sizeof *dev)) == NULL)
+ return NULL;
+
+ iface = p->name.full + PPPOE_NODE_TYPE_LEN + 1;
+
+ provider = strchr(iface, ':');
+ if (provider) {
+ ifacelen = provider - iface;
+ provider++;
+ providerlen = strlen(provider);
+ } else {
+ ifacelen = strlen(iface);
+ provider = "";
+ providerlen = 0;
+ }
+
+ /*
+ * We're going to do this (where tunN is our tunnel device):
+ *
+ * .---------.
+ * | ether |
+ * | <iface> | dev->cs
+ * `---------' |
+ * (orphan) p->fd |
+ * | | |
+ * | | |
+ * (ethernet) | |
+ * .---------. .-----------.
+ * | pppoe | | socket |
+ * | <iface> |(tunN)<---->(tunN)| <unnamed> |
+ * `--------- `-----------'
+ * (tunX)
+ * ^
+ * |
+ * `--->(tunX)
+ */
+
+ /* Create a socket node */
+ if (ID0NgMkSockNode(NULL, &dev->cs, &p->fd) == -1) {
+ log_Printf(LogWARN, "Cannot create netgraph socket node: %s\n",
+ strerror(errno));
+ free(dev);
+ p->fd = -2;
+ return NULL;
+ }
+
+ /*
+ * Ask for a list of hooks attached to the "ether" node. This node should
+ * magically exist as a way of hooking stuff onto an ethernet device
+ */
+ path = (char *)alloca(ifacelen + 2);
+ sprintf(path, "%.*s:", ifacelen, iface);
+ if (NgSendMsg(dev->cs, path, NGM_GENERIC_COOKIE, NGM_LISTHOOKS,
+ NULL, 0) < 0) {
+ log_Printf(LogWARN, "%s Cannot send a netgraph message: %s\n",
+ path, strerror(errno));
+ return ether_Abandon(dev, p);
+ }
+
+ /* Get our list back */
+ resp = (struct ng_mesg *)rbuf;
+ if (NgRecvMsg(dev->cs, resp, sizeof rbuf, NULL) <= 0) {
+ log_Printf(LogWARN, "Cannot get netgraph response: %s\n",
+ strerror(errno));
+ return ether_Abandon(dev, p);
+ }
+
+ hlist = (const struct hooklist *)resp->data;
+ ninfo = &hlist->nodeinfo;
+
+ /* Make sure we've got the right type of node */
+ if (strncmp(ninfo->type, NG_ETHER_NODE_TYPE,
+ sizeof NG_ETHER_NODE_TYPE - 1)) {
+ log_Printf(LogWARN, "%s Unexpected node type ``%s'' (wanted ``"
+ NG_ETHER_NODE_TYPE "'')\n", path, ninfo->type);
+ return ether_Abandon(dev, p);
+ }
+
+ log_Printf(LogDEBUG, "List of netgraph node ``%s'' (id %x) hooks:\n",
+ path, ninfo->id);
+
+ /* look for a hook already attached. */
+ for (f = 0; f < ninfo->hooks; f++) {
+ nlink = &hlist->link[f];
+
+ log_Printf(LogDEBUG, " Found %s -> %s\n", nlink->ourhook,
+ nlink->peerhook);
+
+ if (!strcmp(nlink->ourhook, NG_ETHER_HOOK_ORPHAN) ||
+ !strcmp(nlink->ourhook, NG_ETHER_HOOK_DIVERT)) {
+ /*
+ * Something is using the data coming out of this ``ether'' node.
+ * If it's a PPPoE node, we use that node, otherwise we complain that
+ * someone else is using the node.
+ */
+ if (!strcmp(nlink->nodeinfo.type, NG_PPPOE_NODE_TYPE))
+ /* Use this PPPoE node ! */
+ snprintf(ngc.path, sizeof ngc.path, "[%x]:", nlink->nodeinfo.id);
+ else {
+ log_Printf(LogWARN, "%s Node type ``%s'' is currently active\n",
+ path, nlink->nodeinfo.type);
+ return ether_Abandon(dev, p);
+ }
+ break;
+ }
+ }
+
+ if (f == ninfo->hooks) {
+ /*
+ * Create a new ``PPPoE'' node connected to the ``ether'' node using
+ * the ``orphan'' and ``ethernet'' hooks
+ */
+ snprintf(mkp.type, sizeof mkp.type, "%s", NG_PPPOE_NODE_TYPE);
+ snprintf(mkp.ourhook, sizeof mkp.ourhook, "%s", NG_ETHER_HOOK_ORPHAN);
+ snprintf(mkp.peerhook, sizeof mkp.peerhook, "%s", NG_PPPOE_HOOK_ETHERNET);
+ snprintf(etherid, sizeof etherid, "[%x]:", ninfo->id);
+
+ log_Printf(LogDEBUG, "Creating PPPoE netgraph node %s%s -> %s\n",
+ etherid, mkp.ourhook, mkp.peerhook);
+
+ if (NgSendMsg(dev->cs, etherid, NGM_GENERIC_COOKIE,
+ NGM_MKPEER, &mkp, sizeof mkp) < 0) {
+ log_Printf(LogWARN, "%s Cannot create PPPoE netgraph node: %s\n",
+ etherid, strerror(errno));
+ return ether_Abandon(dev, p);
+ }
+
+ snprintf(ngc.path, sizeof ngc.path, "%s%s", path, NG_ETHER_HOOK_ORPHAN);
+ }
+
+ snprintf(dev->hook, sizeof dev->hook, "%s%d",
+ TUN_NAME, p->dl->bundle->unit);
+
+ /*
+ * Connect the PPPoE node to our socket node.
+ * ngc.path has already been set up
+ */
+ snprintf(ngc.ourhook, sizeof ngc.ourhook, "%s", dev->hook);
+ memcpy(ngc.peerhook, ngc.ourhook, sizeof ngc.peerhook);
+
+ log_Printf(LogDEBUG, "Connecting netgraph socket .:%s -> %s:%s\n",
+ ngc.ourhook, ngc.path, ngc.peerhook);
+ if (NgSendMsg(dev->cs, ".:", NGM_GENERIC_COOKIE,
+ NGM_CONNECT, &ngc, sizeof ngc) < 0) {
+ log_Printf(LogWARN, "Cannot connect PPPoE and socket netgraph "
+ "nodes: %s\n", strerror(errno));
+ return ether_Abandon(dev, p);
+ }
+
+ /* Bring the Ethernet interface up */
+ path[ifacelen] = '\0'; /* Remove the trailing ':' */
+ if (!iface_SetFlags(path, IFF_UP))
+ log_Printf(LogWARN, "%s: Failed to set the IFF_UP flag on %s\n",
+ p->link.name, path);
+
+ /* And finally, request a connection to the given provider */
+
+ data = (struct ngpppoe_init_data *)alloca(sizeof *data + providerlen);
+ snprintf(data->hook, sizeof data->hook, "%s", dev->hook);
+ memcpy(data->data, provider, providerlen);
+ data->data_len = providerlen;
+
+ snprintf(connectpath, sizeof connectpath, ".:%s", dev->hook);
+ log_Printf(LogDEBUG, "Sending PPPOE_CONNECT to %s\n", connectpath);
+ if (NgSendMsg(dev->cs, connectpath, NGM_PPPOE_COOKIE,
+ NGM_PPPOE_CONNECT, data, sizeof *data + providerlen) == -1) {
+ log_Printf(LogWARN, "``%s'': Cannot start netgraph node: %s\n",
+ connectpath, strerror(errno));
+ return ether_Abandon(dev, p);
+ }
+
+ /* Hook things up so that we monitor dev->cs */
+ p->desc.UpdateSet = ether_UpdateSet;
+ p->desc.IsSet = ether_IsSet;
+ p->desc.Read = ether_DescriptorRead;
+
+ memcpy(&dev->dev, &baseetherdevice, sizeof dev->dev);
+ switch (p->cfg.cd.necessity) {
+ case CD_VARIABLE:
+ dev->dev.cd.delay = p->cfg.cd.delay;
+ break;
+ case CD_REQUIRED:
+ dev->dev.cd = p->cfg.cd;
+ break;
+ case CD_NOTREQUIRED:
+ log_Printf(LogWARN, "%s: Carrier must be set, using ``set cd %d!''\n",
+ p->link.name, dev->dev.cd.delay);
+ case CD_DEFAULT:
+ break;
+ }
+
+ dev->timeout = dev->dev.cd.delay;
+ dev->connected = CARRIER_PENDING;
+ /* This will be overridden by our session id - if provided by netgraph */
+ dev->slot = GetIfIndex(path);
+ } else {
+ /* See if we're a netgraph socket */
+ struct stat st;
+
+ if (fstat(p->fd, &st) != -1 && (st.st_mode & S_IFSOCK)) {
+ struct sockaddr_storage ssock;
+ struct sockaddr *sock = (struct sockaddr *)&ssock;
+ int sz;
+
+ sz = sizeof ssock;
+ if (getsockname(p->fd, sock, &sz) == -1) {
+ log_Printf(LogPHASE, "%s: Link is a closed socket !\n", p->link.name);
+ close(p->fd);
+ p->fd = -1;
+ return NULL;
+ }
+
+ if (sock->sa_family == AF_NETGRAPH) {
+ /*
+ * It's a netgraph node... We can't determine hook names etc, so we
+ * stay pretty impartial....
+ */
+ log_Printf(LogPHASE, "%s: Link is a netgraph node\n", p->link.name);
+
+ if ((dev = malloc(sizeof *dev)) == NULL) {
+ log_Printf(LogWARN, "%s: Cannot allocate an ether device: %s\n",
+ p->link.name, strerror(errno));
+ return NULL;
+ }
+
+ memcpy(&dev->dev, &baseetherdevice, sizeof dev->dev);
+ dev->cs = -1;
+ dev->timeout = 0;
+ dev->connected = CARRIER_OK;
+ *dev->hook = '\0';
+
+ /*
+ * If we're being envoked from pppoed(8), we may have a SESSIONID
+ * set in the environment. If so, use it as the slot
+ */
+ if ((sessionid = getenv("SESSIONID")) != NULL) {
+ char *end;
+ u_long slot;
+
+ slot = strtoul(sessionid, &end, 16);
+ dev->slot = end != sessionid && *end == '\0' ? slot : 0;
+ } else
+ dev->slot = 0;
+ }
+ }
+ }
+
+ if (dev) {
+ physical_SetupStack(p, dev->dev.name, PHYSICAL_FORCE_SYNCNOACF);
+ return &dev->dev;
+ }
+
+ return NULL;
+}
diff --git a/usr.sbin/ppp/ether.h b/usr.sbin/ppp/ether.h
new file mode 100644
index 0000000..16c9a9e
--- /dev/null
+++ b/usr.sbin/ppp/ether.h
@@ -0,0 +1,37 @@
+/*-
+ * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct physical;
+struct device;
+
+#define DEF_ETHERCDDELAY 5 /* Default ``set cd'' value */
+
+extern struct device *ether_Create(struct physical *);
+extern struct device *ether_iov2device(int, struct physical *, struct iovec *,
+ int *, int, int *, int *);
+extern int ether_DeviceSize(void);
diff --git a/usr.sbin/ppp/exec.c b/usr.sbin/ppp/exec.c
new file mode 100644
index 0000000..67d00ac
--- /dev/null
+++ b/usr.sbin/ppp/exec.c
@@ -0,0 +1,233 @@
+/*-
+ * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <sys/uio.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "layer.h"
+#include "defs.h"
+#include "mbuf.h"
+#include "log.h"
+#include "timer.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "throughput.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "async.h"
+#include "descriptor.h"
+#include "physical.h"
+#include "mp.h"
+#include "chat.h"
+#include "command.h"
+#include "auth.h"
+#include "chap.h"
+#include "cbcp.h"
+#include "datalink.h"
+#include "id.h"
+#include "exec.h"
+
+static struct device execdevice = {
+ EXEC_DEVICE,
+ "exec",
+ 0,
+ { CD_NOTREQUIRED, 0 },
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+struct device *
+exec_iov2device(int type, struct physical *p, struct iovec *iov,
+ int *niov, int maxiov, int *auxfd, int *nauxfd)
+{
+ if (type == EXEC_DEVICE) {
+ free(iov[(*niov)++].iov_base);
+ physical_SetupStack(p, execdevice.name, PHYSICAL_NOFORCE);
+ return &execdevice;
+ }
+
+ return NULL;
+}
+
+struct device *
+exec_Create(struct physical *p)
+{
+ if (p->fd < 0 && *p->name.full == '!') {
+ int fids[2], type;
+
+ p->fd--; /* We own the device but maybe can't use it - change fd */
+ type = physical_IsSync(p) ? SOCK_DGRAM : SOCK_STREAM;
+
+ if (socketpair(AF_UNIX, type, PF_UNSPEC, fids) < 0)
+ log_Printf(LogPHASE, "Unable to create pipe for line exec: %s\n",
+ strerror(errno));
+ else {
+ static int child_status; /* This variable is abused ! */
+ int stat, argc, i, ret, wret, pidpipe[2];
+ pid_t pid, realpid;
+ char *argv[MAXARGS];
+
+ stat = fcntl(fids[0], F_GETFL, 0);
+ if (stat > 0) {
+ stat |= O_NONBLOCK;
+ fcntl(fids[0], F_SETFL, stat);
+ }
+ realpid = getpid();
+ if (pipe(pidpipe) == -1) {
+ log_Printf(LogPHASE, "Unable to pipe for line exec: %s\n",
+ strerror(errno));
+ close(fids[1]);
+ } else switch ((pid = fork())) {
+ case -1:
+ log_Printf(LogPHASE, "Unable to fork for line exec: %s\n",
+ strerror(errno));
+ close(pidpipe[0]);
+ close(pidpipe[1]);
+ close(fids[1]);
+ break;
+
+ case 0:
+ close(pidpipe[0]);
+ close(fids[0]);
+ timer_TermService();
+#ifndef NOSUID
+ setuid(ID0realuid());
+#endif
+
+ child_status = 0;
+ switch ((pid = vfork())) {
+ case 0:
+ close(pidpipe[1]);
+ break;
+
+ case -1:
+ ret = errno;
+ log_Printf(LogPHASE, "Unable to vfork to drop parent: %s\n",
+ strerror(errno));
+ close(pidpipe[1]);
+ _exit(ret);
+
+ default:
+ write(pidpipe[1], &pid, sizeof pid);
+ close(pidpipe[1]);
+ _exit(child_status); /* The error from exec() ! */
+ }
+
+ log_Printf(LogDEBUG, "Exec'ing ``%s''\n", p->name.base);
+
+ if ((argc = MakeArgs(p->name.base, argv, VECSIZE(argv),
+ PARSE_REDUCE|PARSE_NOHASH)) < 0) {
+ log_Printf(LogWARN, "Syntax error in exec command\n");
+ _exit(ESRCH);
+ }
+
+ command_Expand(argv, argc, (char const *const *)argv,
+ p->dl->bundle, 0, realpid);
+
+ dup2(fids[1], STDIN_FILENO);
+ dup2(fids[1], STDOUT_FILENO);
+ dup2(fids[1], STDERR_FILENO);
+ for (i = getdtablesize(); i > STDERR_FILENO; i--)
+ fcntl(i, F_SETFD, 1);
+
+ execvp(*argv, argv);
+ child_status = errno; /* Only works for vfork() */
+ printf("execvp failed: %s: %s\r\n", *argv, strerror(child_status));
+ _exit(child_status);
+ break;
+
+ default:
+ close(pidpipe[1]);
+ close(fids[1]);
+ if (read(pidpipe[0], &p->session_owner, sizeof p->session_owner) !=
+ sizeof p->session_owner)
+ p->session_owner = (pid_t)-1;
+ close(pidpipe[0]);
+ while ((wret = waitpid(pid, &stat, 0)) == -1 && errno == EINTR)
+ ;
+ if (wret == -1) {
+ log_Printf(LogWARN, "Waiting for child process: %s\n",
+ strerror(errno));
+ close(fids[0]);
+ p->session_owner = (pid_t)-1;
+ break;
+ } else if (WIFSIGNALED(stat)) {
+ log_Printf(LogWARN, "Child process received sig %d !\n",
+ WTERMSIG(stat));
+ close(fids[0]);
+ p->session_owner = (pid_t)-1;
+ break;
+ } else if (WIFSTOPPED(stat)) {
+ log_Printf(LogWARN, "Child process received stop sig %d !\n",
+ WSTOPSIG(stat));
+ /* I guess that's ok.... */
+ } else if ((ret = WEXITSTATUS(stat))) {
+ log_Printf(LogWARN, "Cannot exec \"%s\": %s\n", p->name.base,
+ strerror(ret));
+ close(fids[0]);
+ p->session_owner = (pid_t)-1;
+ break;
+ }
+ p->fd = fids[0];
+ log_Printf(LogDEBUG, "Using descriptor %d for child\n", p->fd);
+ physical_SetupStack(p, execdevice.name, PHYSICAL_NOFORCE);
+ if (p->cfg.cd.necessity != CD_DEFAULT)
+ log_Printf(LogWARN, "Carrier settings ignored\n");
+ return &execdevice;
+ }
+ close(fids[0]);
+ }
+ }
+
+ return NULL;
+}
diff --git a/usr.sbin/ppp/exec.h b/usr.sbin/ppp/exec.h
new file mode 100644
index 0000000..d4b3387
--- /dev/null
+++ b/usr.sbin/ppp/exec.h
@@ -0,0 +1,35 @@
+/*-
+ * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct physical;
+struct device;
+
+extern struct device *exec_Create(struct physical *);
+extern struct device *exec_iov2device(int, struct physical *,
+ struct iovec *, int *, int, int *, int *);
+#define exec_DeviceSize physical_DeviceSize
diff --git a/usr.sbin/ppp/filter.c b/usr.sbin/ppp/filter.c
new file mode 100644
index 0000000..61f03ab
--- /dev/null
+++ b/usr.sbin/ppp/filter.c
@@ -0,0 +1,605 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "layer.h"
+#include "defs.h"
+#include "command.h"
+#include "mbuf.h"
+#include "log.h"
+#include "iplist.h"
+#include "timer.h"
+#include "throughput.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "slcompress.h"
+#include "ncpaddr.h"
+#include "ipcp.h"
+#include "filter.h"
+#include "descriptor.h"
+#include "prompt.h"
+#include "mp.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+
+static int filter_Nam2Op(const char *);
+
+static int
+ParsePort(const char *service, const char *proto)
+{
+ struct servent *servent;
+ char *cp;
+ int port;
+
+ servent = getservbyname(service, proto);
+ if (servent != 0)
+ return ntohs(servent->s_port);
+
+ port = strtol(service, &cp, 0);
+ if (cp == service) {
+ log_Printf(LogWARN, "ParsePort: %s is not a port name or number.\n",
+ service);
+ return 0;
+ }
+ return port;
+}
+
+/*
+ * ICMP Syntax: src eq icmp_message_type
+ */
+static int
+ParseIcmp(int argc, char const *const *argv, const struct protoent *pe,
+ struct filterent *tgt)
+{
+ int type;
+ char *cp;
+
+ switch (argc) {
+ case 0:
+ /* permit/deny all ICMP types */
+ tgt->f_srcop = tgt->f_dstop = OP_NONE;
+ break;
+
+ case 3:
+ if (!strcmp(*argv, "src") && !strcmp(argv[1], "eq")) {
+ type = strtol(argv[2], &cp, 0);
+ if (cp == argv[2]) {
+ log_Printf(LogWARN, "ParseIcmp: type is expected.\n");
+ return 0;
+ }
+ tgt->f_srcop = OP_EQ;
+ tgt->f_srcport = type;
+ tgt->f_dstop = OP_NONE;
+ }
+ break;
+
+ default:
+ log_Printf(LogWARN, "ParseIcmp: bad icmp syntax.\n");
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * UDP Syntax: [src op port] [dst op port]
+ */
+static int
+ParseUdpOrTcp(int argc, char const *const *argv, const struct protoent *pe,
+ struct filterent *tgt)
+{
+ tgt->f_srcop = tgt->f_dstop = OP_NONE;
+ tgt->f_estab = tgt->f_syn = tgt->f_finrst = 0;
+
+ if (argc >= 3 && !strcmp(*argv, "src")) {
+ tgt->f_srcop = filter_Nam2Op(argv[1]);
+ if (tgt->f_srcop == OP_NONE) {
+ log_Printf(LogWARN, "ParseUdpOrTcp: bad operator\n");
+ return 0;
+ }
+ if (pe == NULL)
+ return 0;
+ tgt->f_srcport = ParsePort(argv[2], pe->p_name);
+ if (tgt->f_srcport == 0)
+ return 0;
+ argc -= 3;
+ argv += 3;
+ }
+
+ if (argc >= 3 && !strcmp(argv[0], "dst")) {
+ tgt->f_dstop = filter_Nam2Op(argv[1]);
+ if (tgt->f_dstop == OP_NONE) {
+ log_Printf(LogWARN, "ParseUdpOrTcp: bad operator\n");
+ return 0;
+ }
+ if (pe == NULL)
+ return 0;
+ tgt->f_dstport = ParsePort(argv[2], pe->p_name);
+ if (tgt->f_dstport == 0)
+ return 0;
+ argc -= 3;
+ argv += 3;
+ }
+
+ if (pe && pe->p_proto == IPPROTO_TCP) {
+ for (; argc > 0; argc--, argv++)
+ if (!strcmp(*argv, "estab"))
+ tgt->f_estab = 1;
+ else if (!strcmp(*argv, "syn"))
+ tgt->f_syn = 1;
+ else if (!strcmp(*argv, "finrst"))
+ tgt->f_finrst = 1;
+ else
+ break;
+ }
+
+ if (argc > 0) {
+ log_Printf(LogWARN, "ParseUdpOrTcp: bad src/dst port syntax: %s\n", *argv);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+ParseGeneric(int argc, char const * const *argv, const struct protoent *pe,
+ struct filterent *tgt)
+{
+ /*
+ * Filter currently is a catch-all. Requests are either permitted or
+ * dropped.
+ */
+ if (argc != 0) {
+ log_Printf(LogWARN, "ParseGeneric: Too many parameters\n");
+ return 0;
+ } else
+ tgt->f_srcop = tgt->f_dstop = OP_NONE;
+
+ return 1;
+}
+
+static unsigned
+addrtype(const char *addr)
+{
+ if (!strncasecmp(addr, "MYADDR", 6) && (addr[6] == '\0' || addr[6] == '/'))
+ return T_MYADDR;
+ if (!strncasecmp(addr, "MYADDR6", 7) && (addr[7] == '\0' || addr[7] == '/'))
+ return T_MYADDR6;
+ if (!strncasecmp(addr, "HISADDR", 7) && (addr[7] == '\0' || addr[7] == '/'))
+ return T_HISADDR;
+ if (!strncasecmp(addr, "HISADDR6", 8) && (addr[8] == '\0' || addr[8] == '/'))
+ return T_HISADDR6;
+ if (!strncasecmp(addr, "DNS0", 4) && (addr[4] == '\0' || addr[4] == '/'))
+ return T_DNS0;
+ if (!strncasecmp(addr, "DNS1", 4) && (addr[4] == '\0' || addr[4] == '/'))
+ return T_DNS1;
+
+ return T_ADDR;
+}
+
+static const char *
+addrstr(struct ncprange *addr, unsigned type)
+{
+ switch (type) {
+ case T_MYADDR:
+ return "MYADDR";
+ case T_HISADDR:
+ return "HISADDR";
+ case T_DNS0:
+ return "DNS0";
+ case T_DNS1:
+ return "DNS1";
+ }
+ return ncprange_ntoa(addr);
+}
+
+static int
+filter_Parse(struct ncp *ncp, int argc, char const *const *argv,
+ struct filterent *ofp)
+{
+ struct filterent fe;
+ struct protoent *pe;
+ char *wp;
+ int action, family, ruleno, val, width;
+
+ ruleno = strtol(*argv, &wp, 0);
+ if (*argv == wp || ruleno >= MAXFILTERS) {
+ log_Printf(LogWARN, "Parse: invalid filter number.\n");
+ return 0;
+ }
+ if (ruleno < 0) {
+ for (ruleno = 0; ruleno < MAXFILTERS; ruleno++) {
+ ofp->f_action = A_NONE;
+ ofp++;
+ }
+ log_Printf(LogWARN, "Parse: filter cleared.\n");
+ return 1;
+ }
+ ofp += ruleno;
+
+ if (--argc == 0) {
+ log_Printf(LogWARN, "Parse: missing action.\n");
+ return 0;
+ }
+ argv++;
+
+ memset(&fe, '\0', sizeof fe);
+
+ val = strtol(*argv, &wp, 0);
+ if (!*wp && val >= 0 && val < MAXFILTERS) {
+ if (val <= ruleno) {
+ log_Printf(LogWARN, "Parse: Can only jump forward from rule %d\n",
+ ruleno);
+ return 0;
+ }
+ action = val;
+ } else if (!strcmp(*argv, "permit")) {
+ action = A_PERMIT;
+ } else if (!strcmp(*argv, "deny")) {
+ action = A_DENY;
+ } else if (!strcmp(*argv, "clear")) {
+ ofp->f_action = A_NONE;
+ return 1;
+ } else {
+ log_Printf(LogWARN, "Parse: %s: bad action\n", *argv);
+ return 0;
+ }
+ fe.f_action = action;
+
+ argc--;
+ argv++;
+
+ if (argc && argv[0][0] == '!' && !argv[0][1]) {
+ fe.f_invert = 1;
+ argc--;
+ argv++;
+ }
+
+ ncprange_init(&fe.f_src);
+ ncprange_init(&fe.f_dst);
+
+ if (argc == 0)
+ pe = NULL;
+ else if ((pe = getprotobyname(*argv)) == NULL && strcmp(*argv, "all") != 0) {
+ if (argc < 2) {
+ log_Printf(LogWARN, "Parse: Protocol or address pair expected\n");
+ return 0;
+ } else if (strcasecmp(*argv, "any") == 0 ||
+ ncprange_aton(&fe.f_src, ncp, *argv)) {
+ family = ncprange_family(&fe.f_src);
+ if (!ncprange_getwidth(&fe.f_src, &width))
+ width = 0;
+ if (width == 0)
+ ncprange_init(&fe.f_src);
+ fe.f_srctype = addrtype(*argv);
+ argc--;
+ argv++;
+
+ if (strcasecmp(*argv, "any") == 0 ||
+ ncprange_aton(&fe.f_dst, ncp, *argv)) {
+ if (ncprange_family(&fe.f_dst) != AF_UNSPEC &&
+ ncprange_family(&fe.f_src) != AF_UNSPEC &&
+ family != ncprange_family(&fe.f_dst)) {
+ log_Printf(LogWARN, "Parse: src and dst address families differ\n");
+ return 0;
+ }
+ if (!ncprange_getwidth(&fe.f_dst, &width))
+ width = 0;
+ if (width == 0)
+ ncprange_init(&fe.f_dst);
+ fe.f_dsttype = addrtype(*argv);
+ argc--;
+ argv++;
+ } else {
+ log_Printf(LogWARN, "Parse: Protocol or address pair expected\n");
+ return 0;
+ }
+
+ if (argc) {
+ if ((pe = getprotobyname(*argv)) == NULL && strcmp(*argv, "all") != 0) {
+ log_Printf(LogWARN, "Parse: %s: Protocol expected\n", *argv);
+ return 0;
+ } else {
+ argc--;
+ argv++;
+ }
+ }
+ } else {
+ log_Printf(LogWARN, "Parse: Protocol or address pair expected\n");
+ return 0;
+ }
+ } else {
+ argc--;
+ argv++;
+ }
+
+ if (argc >= 2 && strcmp(*argv, "timeout") == 0) {
+ fe.timeout = strtoul(argv[1], NULL, 10);
+ argc -= 2;
+ argv += 2;
+ }
+
+ val = 1;
+ fe.f_proto = (pe == NULL) ? 0 : pe->p_proto;
+
+ switch (fe.f_proto) {
+ case IPPROTO_TCP:
+ case IPPROTO_UDP:
+ case IPPROTO_IPIP:
+#ifndef NOINET6
+ case IPPROTO_IPV6:
+#endif
+ val = ParseUdpOrTcp(argc, argv, pe, &fe);
+ break;
+ case IPPROTO_ICMP:
+#ifndef NOINET6
+ case IPPROTO_ICMPV6:
+#endif
+ val = ParseIcmp(argc, argv, pe, &fe);
+ break;
+ default:
+ val = ParseGeneric(argc, argv, pe, &fe);
+ break;
+ }
+
+ log_Printf(LogDEBUG, "Parse: Src: %s\n", ncprange_ntoa(&fe.f_src));
+ log_Printf(LogDEBUG, "Parse: Dst: %s\n", ncprange_ntoa(&fe.f_dst));
+ log_Printf(LogDEBUG, "Parse: Proto: %d\n", fe.f_proto);
+
+ log_Printf(LogDEBUG, "Parse: src: %s (%d)\n",
+ filter_Op2Nam(fe.f_srcop), fe.f_srcport);
+ log_Printf(LogDEBUG, "Parse: dst: %s (%d)\n",
+ filter_Op2Nam(fe.f_dstop), fe.f_dstport);
+ log_Printf(LogDEBUG, "Parse: estab: %u\n", fe.f_estab);
+ log_Printf(LogDEBUG, "Parse: syn: %u\n", fe.f_syn);
+ log_Printf(LogDEBUG, "Parse: finrst: %u\n", fe.f_finrst);
+
+ if (val)
+ *ofp = fe;
+
+ return val;
+}
+
+int
+filter_Set(struct cmdargs const *arg)
+{
+ struct filter *filter;
+
+ if (arg->argc < arg->argn+2)
+ return -1;
+
+ if (!strcmp(arg->argv[arg->argn], "in"))
+ filter = &arg->bundle->filter.in;
+ else if (!strcmp(arg->argv[arg->argn], "out"))
+ filter = &arg->bundle->filter.out;
+ else if (!strcmp(arg->argv[arg->argn], "dial"))
+ filter = &arg->bundle->filter.dial;
+ else if (!strcmp(arg->argv[arg->argn], "alive"))
+ filter = &arg->bundle->filter.alive;
+ else {
+ log_Printf(LogWARN, "filter_Set: %s: Invalid filter name.\n",
+ arg->argv[arg->argn]);
+ return -1;
+ }
+
+ filter_Parse(&arg->bundle->ncp, arg->argc - arg->argn - 1,
+ arg->argv + arg->argn + 1, filter->rule);
+ return 0;
+}
+
+const char *
+filter_Action2Nam(int act)
+{
+ static const char * const actname[] = { " none ", "permit ", " deny " };
+ static char buf[8];
+
+ if (act >= 0 && act < MAXFILTERS) {
+ snprintf(buf, sizeof buf, "%6d ", act);
+ return buf;
+ } else if (act >= A_NONE && act < A_NONE + sizeof(actname)/sizeof(char *))
+ return actname[act - A_NONE];
+ else
+ return "?????? ";
+}
+
+static void
+doShowFilter(struct filterent *fp, struct prompt *prompt)
+{
+ struct protoent *pe;
+ int n;
+
+ for (n = 0; n < MAXFILTERS; n++, fp++) {
+ if (fp->f_action != A_NONE) {
+ prompt_Printf(prompt, " %2d %s", n, filter_Action2Nam(fp->f_action));
+ prompt_Printf(prompt, "%c ", fp->f_invert ? '!' : ' ');
+
+ if (ncprange_isset(&fp->f_src))
+ prompt_Printf(prompt, "%s ", addrstr(&fp->f_src, fp->f_srctype));
+ else
+ prompt_Printf(prompt, "any ");
+
+ if (ncprange_isset(&fp->f_dst))
+ prompt_Printf(prompt, "%s ", addrstr(&fp->f_dst, fp->f_dsttype));
+ else
+ prompt_Printf(prompt, "any ");
+
+ if (fp->f_proto) {
+ if ((pe = getprotobynumber(fp->f_proto)) == NULL)
+ prompt_Printf(prompt, "P:%d", fp->f_proto);
+ else
+ prompt_Printf(prompt, "%s", pe->p_name);
+
+ if (fp->f_srcop)
+ prompt_Printf(prompt, " src %s %d", filter_Op2Nam(fp->f_srcop),
+ fp->f_srcport);
+ if (fp->f_dstop)
+ prompt_Printf(prompt, " dst %s %d", filter_Op2Nam(fp->f_dstop),
+ fp->f_dstport);
+ if (fp->f_estab)
+ prompt_Printf(prompt, " estab");
+ if (fp->f_syn)
+ prompt_Printf(prompt, " syn");
+ if (fp->f_finrst)
+ prompt_Printf(prompt, " finrst");
+ } else
+ prompt_Printf(prompt, "all");
+ if (fp->timeout != 0)
+ prompt_Printf(prompt, " timeout %u", fp->timeout);
+ prompt_Printf(prompt, "\n");
+ }
+ }
+}
+
+int
+filter_Show(struct cmdargs const *arg)
+{
+ if (arg->argc > arg->argn+1)
+ return -1;
+
+ if (arg->argc == arg->argn+1) {
+ struct filter *filter;
+
+ if (!strcmp(arg->argv[arg->argn], "in"))
+ filter = &arg->bundle->filter.in;
+ else if (!strcmp(arg->argv[arg->argn], "out"))
+ filter = &arg->bundle->filter.out;
+ else if (!strcmp(arg->argv[arg->argn], "dial"))
+ filter = &arg->bundle->filter.dial;
+ else if (!strcmp(arg->argv[arg->argn], "alive"))
+ filter = &arg->bundle->filter.alive;
+ else
+ return -1;
+ doShowFilter(filter->rule, arg->prompt);
+ } else {
+ struct filter *filter[4];
+ int f;
+
+ filter[0] = &arg->bundle->filter.in;
+ filter[1] = &arg->bundle->filter.out;
+ filter[2] = &arg->bundle->filter.dial;
+ filter[3] = &arg->bundle->filter.alive;
+ for (f = 0; f < 4; f++) {
+ if (f)
+ prompt_Printf(arg->prompt, "\n");
+ prompt_Printf(arg->prompt, "%s:\n", filter[f]->name);
+ doShowFilter(filter[f]->rule, arg->prompt);
+ }
+ }
+
+ return 0;
+}
+
+static const char * const opname[] = {"none", "eq", "gt", "lt"};
+
+const char *
+filter_Op2Nam(int op)
+{
+ if (op >= sizeof opname / sizeof opname[0])
+ return "unknown";
+ return opname[op];
+
+}
+
+static int
+filter_Nam2Op(const char *cp)
+{
+ int op;
+
+ for (op = sizeof opname / sizeof opname[0] - 1; op; op--)
+ if (!strcasecmp(cp, opname[op]))
+ break;
+
+ return op;
+}
+
+void
+filter_AdjustAddr(struct filter *filter, struct ncpaddr *local,
+ struct ncpaddr *remote, struct in_addr *dns)
+{
+ struct filterent *fp;
+ int n;
+
+ for (fp = filter->rule, n = 0; n < MAXFILTERS; fp++, n++)
+ if (fp->f_action != A_NONE) {
+ if (local) {
+ if (fp->f_srctype == T_MYADDR && ncpaddr_family(local) == AF_INET)
+ ncprange_sethost(&fp->f_src, local);
+ if (fp->f_dsttype == T_MYADDR && ncpaddr_family(local) == AF_INET)
+ ncprange_sethost(&fp->f_dst, local);
+#ifndef NOINET6
+ if (fp->f_srctype == T_MYADDR6 && ncpaddr_family(local) == AF_INET6)
+ ncprange_sethost(&fp->f_src, local);
+ if (fp->f_dsttype == T_MYADDR6 && ncpaddr_family(local) == AF_INET6)
+ ncprange_sethost(&fp->f_dst, local);
+#endif
+ }
+ if (remote) {
+ if (fp->f_srctype == T_HISADDR && ncpaddr_family(remote) == AF_INET)
+ ncprange_sethost(&fp->f_src, remote);
+ if (fp->f_dsttype == T_HISADDR && ncpaddr_family(remote) == AF_INET)
+ ncprange_sethost(&fp->f_dst, remote);
+#ifndef NOINET6
+ if (fp->f_srctype == T_HISADDR6 && ncpaddr_family(remote) == AF_INET6)
+ ncprange_sethost(&fp->f_src, remote);
+ if (fp->f_dsttype == T_HISADDR6 && ncpaddr_family(remote) == AF_INET6)
+ ncprange_sethost(&fp->f_dst, remote);
+#endif
+ }
+ if (dns) {
+ if (fp->f_srctype == T_DNS0)
+ ncprange_setip4host(&fp->f_src, dns[0]);
+ if (fp->f_dsttype == T_DNS0)
+ ncprange_setip4host(&fp->f_dst, dns[0]);
+ if (fp->f_srctype == T_DNS1)
+ ncprange_setip4host(&fp->f_src, dns[1]);
+ if (fp->f_dsttype == T_DNS1)
+ ncprange_setip4host(&fp->f_dst, dns[1]);
+ }
+ }
+}
diff --git a/usr.sbin/ppp/filter.h b/usr.sbin/ppp/filter.h
new file mode 100644
index 0000000..f994f4b
--- /dev/null
+++ b/usr.sbin/ppp/filter.h
@@ -0,0 +1,101 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/* Operations - f_srcop, f_dstop */
+#define OP_NONE 0
+#define OP_EQ 1
+#define OP_GT 2
+#define OP_LT 3
+
+/* srctype or dsttype */
+#define T_ADDR 0
+#define T_MYADDR 1
+#define T_MYADDR6 2
+#define T_HISADDR 3
+#define T_HISADDR6 4
+#define T_DNS0 5
+#define T_DNS1 6
+
+/*
+ * There's a struct filterent for each possible filter rule. The
+ * layout is designed to minimise size (there are 4 * MAXFILTERS of
+ * them) - which is also conveniently a power of 2 (32 bytes) on
+ * architectures where sizeof(int)==4 (this makes indexing faster).
+ *
+ * Note that there are four free bits in the initial word for future
+ * extensions.
+ */
+struct filterent {
+ int f_proto; /* Protocol: getprotoby*() */
+ unsigned f_action : 8; /* Filtering action: goto or A_... */
+ unsigned f_srcop : 2; /* Source port operation: OP_... */
+ unsigned f_dstop : 2; /* Destination port operation: OP_... */
+ unsigned f_srctype : 3; /* T_ value of src */
+ unsigned f_dsttype : 3; /* T_ value of dst */
+ unsigned f_estab : 1; /* Check TCP ACK bit */
+ unsigned f_syn : 1; /* Check TCP SYN bit */
+ unsigned f_finrst : 1; /* Check TCP FIN/RST bits */
+ unsigned f_invert : 1; /* true to complement match */
+ struct ncprange f_src; /* Source address and mask */
+ struct ncprange f_dst; /* Destination address and mask */
+ u_short f_srcport; /* Source port, compared with f_srcop */
+ u_short f_dstport; /* Destination port, compared with f_dstop */
+ unsigned timeout; /* Keep alive value for passed packet */
+};
+
+#define MAXFILTERS 40 /* in each filter set */
+
+/* f_action values [0..MAXFILTERS) specify the next filter rule, others are: */
+#define A_NONE (MAXFILTERS)
+#define A_PERMIT (A_NONE+1)
+#define A_DENY (A_PERMIT+1)
+
+struct filter {
+ struct filterent rule[MAXFILTERS]; /* incoming packet filter */
+ const char *name;
+ unsigned fragok : 1;
+ unsigned logok : 1;
+};
+
+/* Which filter set */
+#define FL_IN 0
+#define FL_OUT 1
+#define FL_DIAL 2
+#define FL_KEEP 3
+
+struct ipcp;
+struct cmdargs;
+
+extern int filter_Show(struct cmdargs const *);
+extern int filter_Set(struct cmdargs const *);
+extern const char * filter_Action2Nam(int);
+extern const char *filter_Op2Nam(int);
+extern void filter_AdjustAddr(struct filter *, struct ncpaddr *,
+ struct ncpaddr *, struct in_addr *);
diff --git a/usr.sbin/ppp/fsm.c b/usr.sbin/ppp/fsm.c
new file mode 100644
index 0000000..eea7b7d
--- /dev/null
+++ b/usr.sbin/ppp/fsm.c
@@ -0,0 +1,1209 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <string.h>
+#include <termios.h>
+
+#include "layer.h"
+#include "ua.h"
+#include "mbuf.h"
+#include "log.h"
+#include "defs.h"
+#include "timer.h"
+#include "fsm.h"
+#include "iplist.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "throughput.h"
+#include "slcompress.h"
+#include "ncpaddr.h"
+#include "ipcp.h"
+#include "filter.h"
+#include "descriptor.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "mp.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+#include "async.h"
+#include "physical.h"
+#include "proto.h"
+
+static void FsmSendConfigReq(struct fsm *);
+static void FsmSendTerminateReq(struct fsm *);
+static void FsmInitRestartCounter(struct fsm *, int);
+
+typedef void (recvfn)(struct fsm *, struct fsmheader *, struct mbuf *);
+static recvfn FsmRecvConfigReq, FsmRecvConfigAck, FsmRecvConfigNak,
+ FsmRecvConfigRej, FsmRecvTermReq, FsmRecvTermAck,
+ FsmRecvCodeRej, FsmRecvProtoRej, FsmRecvEchoReq,
+ FsmRecvEchoRep, FsmRecvDiscReq, FsmRecvIdent,
+ FsmRecvTimeRemain, FsmRecvResetReq, FsmRecvResetAck;
+
+static const struct fsmcodedesc {
+ recvfn *recv;
+ unsigned check_reqid : 1;
+ unsigned inc_reqid : 1;
+ const char *name;
+} FsmCodes[] = {
+ { FsmRecvConfigReq, 0, 0, "ConfigReq" },
+ { FsmRecvConfigAck, 1, 1, "ConfigAck" },
+ { FsmRecvConfigNak, 1, 1, "ConfigNak" },
+ { FsmRecvConfigRej, 1, 1, "ConfigRej" },
+ { FsmRecvTermReq, 0, 0, "TerminateReq" },
+ { FsmRecvTermAck, 1, 1, "TerminateAck" },
+ { FsmRecvCodeRej, 0, 0, "CodeRej" },
+ { FsmRecvProtoRej, 0, 0, "ProtocolRej" },
+ { FsmRecvEchoReq, 0, 0, "EchoRequest" },
+ { FsmRecvEchoRep, 0, 0, "EchoReply" },
+ { FsmRecvDiscReq, 0, 0, "DiscardReq" },
+ { FsmRecvIdent, 0, 1, "Ident" },
+ { FsmRecvTimeRemain,0, 0, "TimeRemain" },
+ { FsmRecvResetReq, 0, 0, "ResetReq" },
+ { FsmRecvResetAck, 0, 1, "ResetAck" }
+};
+
+static const char *
+Code2Nam(u_int code)
+{
+ if (code == 0 || code > sizeof FsmCodes / sizeof FsmCodes[0])
+ return "Unknown";
+ return FsmCodes[code-1].name;
+}
+
+const char *
+State2Nam(u_int state)
+{
+ static const char * const StateNames[] = {
+ "Initial", "Starting", "Closed", "Stopped", "Closing", "Stopping",
+ "Req-Sent", "Ack-Rcvd", "Ack-Sent", "Opened",
+ };
+
+ if (state >= sizeof StateNames / sizeof StateNames[0])
+ return "unknown";
+ return StateNames[state];
+}
+
+static void
+StoppedTimeout(void *v)
+{
+ struct fsm *fp = (struct fsm *)v;
+
+ log_Printf(fp->LogLevel, "%s: Stopped timer expired\n", fp->link->name);
+ if (fp->OpenTimer.state == TIMER_RUNNING) {
+ log_Printf(LogWARN, "%s: %s: aborting open delay due to stopped timer\n",
+ fp->link->name, fp->name);
+ timer_Stop(&fp->OpenTimer);
+ }
+ if (fp->state == ST_STOPPED)
+ fsm2initial(fp);
+}
+
+void
+fsm_Init(struct fsm *fp, const char *name, u_short proto, int mincode,
+ int maxcode, int LogLevel, struct bundle *bundle,
+ struct link *l, const struct fsm_parent *parent,
+ struct fsm_callbacks *fn, const char * const timer_names[3])
+{
+ fp->name = name;
+ fp->proto = proto;
+ fp->min_code = mincode;
+ fp->max_code = maxcode;
+ fp->state = fp->min_code > CODE_TERMACK ? ST_OPENED : ST_INITIAL;
+ fp->reqid = 1;
+ fp->restart = 1;
+ fp->more.reqs = fp->more.naks = fp->more.rejs = 3;
+ memset(&fp->FsmTimer, '\0', sizeof fp->FsmTimer);
+ memset(&fp->OpenTimer, '\0', sizeof fp->OpenTimer);
+ memset(&fp->StoppedTimer, '\0', sizeof fp->StoppedTimer);
+ fp->LogLevel = LogLevel;
+ fp->link = l;
+ fp->bundle = bundle;
+ fp->parent = parent;
+ fp->fn = fn;
+ fp->FsmTimer.name = timer_names[0];
+ fp->OpenTimer.name = timer_names[1];
+ fp->StoppedTimer.name = timer_names[2];
+}
+
+static void
+NewState(struct fsm *fp, int new)
+{
+ log_Printf(fp->LogLevel, "%s: State change %s --> %s\n",
+ fp->link->name, State2Nam(fp->state), State2Nam(new));
+ if (fp->state == ST_STOPPED && fp->StoppedTimer.state == TIMER_RUNNING)
+ timer_Stop(&fp->StoppedTimer);
+ fp->state = new;
+ if ((new >= ST_INITIAL && new <= ST_STOPPED) || (new == ST_OPENED)) {
+ timer_Stop(&fp->FsmTimer);
+ if (new == ST_STOPPED && fp->StoppedTimer.load) {
+ timer_Stop(&fp->StoppedTimer);
+ fp->StoppedTimer.func = StoppedTimeout;
+ fp->StoppedTimer.arg = (void *) fp;
+ timer_Start(&fp->StoppedTimer);
+ }
+ }
+}
+
+void
+fsm_Output(struct fsm *fp, u_int code, u_int id, u_char *ptr, int count,
+ int mtype)
+{
+ int plen;
+ struct fsmheader lh;
+ struct mbuf *bp;
+
+ if (log_IsKept(fp->LogLevel)) {
+ log_Printf(fp->LogLevel, "%s: Send%s(%d) state = %s\n",
+ fp->link->name, Code2Nam(code), id, State2Nam(fp->state));
+ switch (code) {
+ case CODE_CONFIGREQ:
+ case CODE_CONFIGACK:
+ case CODE_CONFIGREJ:
+ case CODE_CONFIGNAK:
+ (*fp->fn->DecodeConfig)(fp, ptr, ptr + count, MODE_NOP, NULL);
+ if (count < sizeof(struct fsm_opt_hdr))
+ log_Printf(fp->LogLevel, " [EMPTY]\n");
+ break;
+ }
+ }
+
+ plen = sizeof(struct fsmheader) + count;
+ lh.code = code;
+ lh.id = id;
+ lh.length = htons(plen);
+ bp = m_get(plen, mtype);
+ memcpy(MBUF_CTOP(bp), &lh, sizeof(struct fsmheader));
+ if (count)
+ memcpy(MBUF_CTOP(bp) + sizeof(struct fsmheader), ptr, count);
+ log_DumpBp(LogDEBUG, "fsm_Output", bp);
+ link_PushPacket(fp->link, bp, fp->bundle, LINK_QUEUES(fp->link) - 1,
+ fp->proto);
+
+ if (code == CODE_CONFIGREJ)
+ lcp_SendIdentification(&fp->link->lcp);
+}
+
+static void
+FsmOpenNow(void *v)
+{
+ struct fsm *fp = (struct fsm *)v;
+
+ timer_Stop(&fp->OpenTimer);
+ if (fp->state <= ST_STOPPED) {
+ if (fp->state != ST_STARTING) {
+ /*
+ * In practice, we're only here in ST_STOPPED (when delaying the
+ * first config request) or ST_CLOSED (when openmode == 0).
+ *
+ * The ST_STOPPED bit is breaking the RFC already :-(
+ *
+ * According to the RFC (1661) state transition table, a TLS isn't
+ * required for an Open event when state == Closed, but the RFC
+ * must be wrong as TLS hasn't yet been called (since the last TLF)
+ * ie, Initial gets an `Up' event, Closing gets a RTA etc.
+ */
+ (*fp->fn->LayerStart)(fp);
+ (*fp->parent->LayerStart)(fp->parent->object, fp);
+ }
+ FsmInitRestartCounter(fp, FSM_REQ_TIMER);
+ FsmSendConfigReq(fp);
+ NewState(fp, ST_REQSENT);
+ }
+}
+
+void
+fsm_Open(struct fsm *fp)
+{
+ switch (fp->state) {
+ case ST_INITIAL:
+ NewState(fp, ST_STARTING);
+ (*fp->fn->LayerStart)(fp);
+ (*fp->parent->LayerStart)(fp->parent->object, fp);
+ break;
+ case ST_CLOSED:
+ if (fp->open_mode == OPEN_PASSIVE) {
+ NewState(fp, ST_STOPPED); /* XXX: This is a hack ! */
+ } else if (fp->open_mode > 0) {
+ if (fp->open_mode > 1)
+ log_Printf(LogPHASE, "%s: Entering STOPPED state for %d seconds\n",
+ fp->link->name, fp->open_mode);
+ NewState(fp, ST_STOPPED); /* XXX: This is a not-so-bad hack ! */
+ timer_Stop(&fp->OpenTimer);
+ fp->OpenTimer.load = fp->open_mode * SECTICKS;
+ fp->OpenTimer.func = FsmOpenNow;
+ fp->OpenTimer.arg = (void *)fp;
+ timer_Start(&fp->OpenTimer);
+ } else
+ FsmOpenNow(fp);
+ break;
+ case ST_STOPPED: /* XXX: restart option */
+ case ST_REQSENT:
+ case ST_ACKRCVD:
+ case ST_ACKSENT:
+ case ST_OPENED: /* XXX: restart option */
+ break;
+ case ST_CLOSING: /* XXX: restart option */
+ case ST_STOPPING: /* XXX: restart option */
+ NewState(fp, ST_STOPPING);
+ break;
+ }
+}
+
+void
+fsm_Up(struct fsm *fp)
+{
+ switch (fp->state) {
+ case ST_INITIAL:
+ log_Printf(fp->LogLevel, "FSM: Using \"%s\" as a transport\n",
+ fp->link->name);
+ NewState(fp, ST_CLOSED);
+ break;
+ case ST_STARTING:
+ FsmInitRestartCounter(fp, FSM_REQ_TIMER);
+ FsmSendConfigReq(fp);
+ NewState(fp, ST_REQSENT);
+ break;
+ default:
+ log_Printf(fp->LogLevel, "%s: Oops, Up at %s\n",
+ fp->link->name, State2Nam(fp->state));
+ break;
+ }
+}
+
+void
+fsm_Down(struct fsm *fp)
+{
+ switch (fp->state) {
+ case ST_CLOSED:
+ NewState(fp, ST_INITIAL);
+ break;
+ case ST_CLOSING:
+ /* This TLF contradicts the RFC (1661), which ``misses it out'' ! */
+ (*fp->fn->LayerFinish)(fp);
+ NewState(fp, ST_INITIAL);
+ (*fp->parent->LayerFinish)(fp->parent->object, fp);
+ break;
+ case ST_STOPPED:
+ NewState(fp, ST_STARTING);
+ (*fp->fn->LayerStart)(fp);
+ (*fp->parent->LayerStart)(fp->parent->object, fp);
+ break;
+ case ST_STOPPING:
+ case ST_REQSENT:
+ case ST_ACKRCVD:
+ case ST_ACKSENT:
+ NewState(fp, ST_STARTING);
+ break;
+ case ST_OPENED:
+ (*fp->fn->LayerDown)(fp);
+ NewState(fp, ST_STARTING);
+ (*fp->parent->LayerDown)(fp->parent->object, fp);
+ break;
+ }
+}
+
+void
+fsm_Close(struct fsm *fp)
+{
+ switch (fp->state) {
+ case ST_STARTING:
+ (*fp->fn->LayerFinish)(fp);
+ NewState(fp, ST_INITIAL);
+ (*fp->parent->LayerFinish)(fp->parent->object, fp);
+ break;
+ case ST_STOPPED:
+ NewState(fp, ST_CLOSED);
+ break;
+ case ST_STOPPING:
+ NewState(fp, ST_CLOSING);
+ break;
+ case ST_OPENED:
+ (*fp->fn->LayerDown)(fp);
+ if (fp->state == ST_OPENED) {
+ FsmInitRestartCounter(fp, FSM_TRM_TIMER);
+ FsmSendTerminateReq(fp);
+ NewState(fp, ST_CLOSING);
+ (*fp->parent->LayerDown)(fp->parent->object, fp);
+ }
+ break;
+ case ST_REQSENT:
+ case ST_ACKRCVD:
+ case ST_ACKSENT:
+ FsmInitRestartCounter(fp, FSM_TRM_TIMER);
+ FsmSendTerminateReq(fp);
+ NewState(fp, ST_CLOSING);
+ break;
+ }
+}
+
+/*
+ * Send functions
+ */
+static void
+FsmSendConfigReq(struct fsm *fp)
+{
+ if (fp->more.reqs-- > 0 && fp->restart-- > 0) {
+ (*fp->fn->SendConfigReq)(fp);
+ timer_Start(&fp->FsmTimer); /* Start restart timer */
+ } else {
+ if (fp->more.reqs < 0)
+ log_Printf(LogPHASE, "%s: Too many %s REQs sent - abandoning "
+ "negotiation\n", fp->link->name, fp->name);
+ lcp_SendIdentification(&fp->link->lcp);
+ fsm_Close(fp);
+ }
+}
+
+static void
+FsmSendTerminateReq(struct fsm *fp)
+{
+ fsm_Output(fp, CODE_TERMREQ, fp->reqid, NULL, 0, MB_UNKNOWN);
+ (*fp->fn->SentTerminateReq)(fp);
+ timer_Start(&fp->FsmTimer); /* Start restart timer */
+ fp->restart--; /* Decrement restart counter */
+}
+
+/*
+ * Timeout actions
+ */
+static void
+FsmTimeout(void *v)
+{
+ struct fsm *fp = (struct fsm *)v;
+
+ if (fp->restart) {
+ switch (fp->state) {
+ case ST_CLOSING:
+ case ST_STOPPING:
+ FsmSendTerminateReq(fp);
+ break;
+ case ST_REQSENT:
+ case ST_ACKSENT:
+ FsmSendConfigReq(fp);
+ break;
+ case ST_ACKRCVD:
+ FsmSendConfigReq(fp);
+ NewState(fp, ST_REQSENT);
+ break;
+ }
+ timer_Start(&fp->FsmTimer);
+ } else {
+ switch (fp->state) {
+ case ST_CLOSING:
+ (*fp->fn->LayerFinish)(fp);
+ NewState(fp, ST_CLOSED);
+ (*fp->parent->LayerFinish)(fp->parent->object, fp);
+ break;
+ case ST_STOPPING:
+ (*fp->fn->LayerFinish)(fp);
+ NewState(fp, ST_STOPPED);
+ (*fp->parent->LayerFinish)(fp->parent->object, fp);
+ break;
+ case ST_REQSENT: /* XXX: 3p */
+ case ST_ACKSENT:
+ case ST_ACKRCVD:
+ (*fp->fn->LayerFinish)(fp);
+ NewState(fp, ST_STOPPED);
+ (*fp->parent->LayerFinish)(fp->parent->object, fp);
+ break;
+ }
+ }
+}
+
+static void
+FsmInitRestartCounter(struct fsm *fp, int what)
+{
+ timer_Stop(&fp->FsmTimer);
+ fp->FsmTimer.func = FsmTimeout;
+ fp->FsmTimer.arg = (void *)fp;
+ (*fp->fn->InitRestartCounter)(fp, what);
+}
+
+/*
+ * Actions when receive packets
+ */
+static void
+FsmRecvConfigReq(struct fsm *fp, struct fsmheader *lhp, struct mbuf *bp)
+/* RCR */
+{
+ struct fsm_decode dec;
+ int plen, flen;
+ int ackaction = 0;
+ u_char *cp;
+
+ bp = m_pullup(bp);
+ plen = m_length(bp);
+ flen = ntohs(lhp->length) - sizeof *lhp;
+ if (plen < flen) {
+ log_Printf(LogWARN, "%s: FsmRecvConfigReq: plen (%d) < flen (%d)\n",
+ fp->link->name, plen, flen);
+ m_freem(bp);
+ return;
+ }
+
+ /* Some things must be done before we Decode the packet */
+ switch (fp->state) {
+ case ST_OPENED:
+ (*fp->fn->LayerDown)(fp);
+ }
+
+ dec.ackend = dec.ack;
+ dec.nakend = dec.nak;
+ dec.rejend = dec.rej;
+ cp = MBUF_CTOP(bp);
+ (*fp->fn->DecodeConfig)(fp, cp, cp + flen, MODE_REQ, &dec);
+ if (flen < sizeof(struct fsm_opt_hdr))
+ log_Printf(fp->LogLevel, " [EMPTY]\n");
+
+ if (dec.nakend == dec.nak && dec.rejend == dec.rej)
+ ackaction = 1;
+
+ /* Check and process easy case */
+ switch (fp->state) {
+ case ST_INITIAL:
+ if (fp->proto == PROTO_CCP && fp->link->lcp.fsm.state == ST_OPENED) {
+ /*
+ * ccp_SetOpenMode() leaves us in initial if we're disabling
+ * & denying everything.
+ */
+ bp = m_prepend(bp, lhp, sizeof *lhp, 2);
+ bp = proto_Prepend(bp, fp->proto, 0, 0);
+ bp = m_pullup(bp);
+ lcp_SendProtoRej(&fp->link->lcp, MBUF_CTOP(bp), bp->m_len);
+ m_freem(bp);
+ return;
+ }
+ /* Drop through */
+ case ST_STARTING:
+ log_Printf(fp->LogLevel, "%s: Oops, RCR in %s.\n",
+ fp->link->name, State2Nam(fp->state));
+ m_freem(bp);
+ return;
+ case ST_CLOSED:
+ (*fp->fn->SendTerminateAck)(fp, lhp->id);
+ m_freem(bp);
+ return;
+ case ST_CLOSING:
+ log_Printf(fp->LogLevel, "%s: Error: Got ConfigReq while state = %s\n",
+ fp->link->name, State2Nam(fp->state));
+ case ST_STOPPING:
+ m_freem(bp);
+ return;
+ case ST_STOPPED:
+ FsmInitRestartCounter(fp, FSM_REQ_TIMER);
+ /* Drop through */
+ case ST_OPENED:
+ FsmSendConfigReq(fp);
+ break;
+ }
+
+ if (dec.rejend != dec.rej)
+ fsm_Output(fp, CODE_CONFIGREJ, lhp->id, dec.rej, dec.rejend - dec.rej,
+ MB_UNKNOWN);
+ if (dec.nakend != dec.nak)
+ fsm_Output(fp, CODE_CONFIGNAK, lhp->id, dec.nak, dec.nakend - dec.nak,
+ MB_UNKNOWN);
+ if (ackaction)
+ fsm_Output(fp, CODE_CONFIGACK, lhp->id, dec.ack, dec.ackend - dec.ack,
+ MB_UNKNOWN);
+
+ switch (fp->state) {
+ case ST_STOPPED:
+ /*
+ * According to the RFC (1661) state transition table, a TLS isn't
+ * required for a RCR when state == ST_STOPPED, but the RFC
+ * must be wrong as TLS hasn't yet been called (since the last TLF)
+ */
+ (*fp->fn->LayerStart)(fp);
+ (*fp->parent->LayerStart)(fp->parent->object, fp);
+ /* Fall through */
+
+ case ST_OPENED:
+ if (ackaction)
+ NewState(fp, ST_ACKSENT);
+ else
+ NewState(fp, ST_REQSENT);
+ (*fp->parent->LayerDown)(fp->parent->object, fp);
+ break;
+ case ST_REQSENT:
+ if (ackaction)
+ NewState(fp, ST_ACKSENT);
+ break;
+ case ST_ACKRCVD:
+ if (ackaction) {
+ NewState(fp, ST_OPENED);
+ if ((*fp->fn->LayerUp)(fp))
+ (*fp->parent->LayerUp)(fp->parent->object, fp);
+ else {
+ (*fp->fn->LayerDown)(fp);
+ FsmInitRestartCounter(fp, FSM_TRM_TIMER);
+ FsmSendTerminateReq(fp);
+ NewState(fp, ST_CLOSING);
+ lcp_SendIdentification(&fp->link->lcp);
+ }
+ }
+ break;
+ case ST_ACKSENT:
+ if (!ackaction)
+ NewState(fp, ST_REQSENT);
+ break;
+ }
+ m_freem(bp);
+
+ if (dec.rejend != dec.rej && --fp->more.rejs <= 0) {
+ log_Printf(LogPHASE, "%s: Too many %s REJs sent - abandoning negotiation\n",
+ fp->link->name, fp->name);
+ lcp_SendIdentification(&fp->link->lcp);
+ fsm_Close(fp);
+ }
+
+ if (dec.nakend != dec.nak && --fp->more.naks <= 0) {
+ log_Printf(LogPHASE, "%s: Too many %s NAKs sent - abandoning negotiation\n",
+ fp->link->name, fp->name);
+ lcp_SendIdentification(&fp->link->lcp);
+ fsm_Close(fp);
+ }
+}
+
+static void
+FsmRecvConfigAck(struct fsm *fp, struct fsmheader *lhp, struct mbuf *bp)
+/* RCA */
+{
+ struct fsm_decode dec;
+ int plen, flen;
+ u_char *cp;
+
+ plen = m_length(bp);
+ flen = ntohs(lhp->length) - sizeof *lhp;
+ if (plen < flen) {
+ m_freem(bp);
+ return;
+ }
+
+ bp = m_pullup(bp);
+ dec.ackend = dec.ack;
+ dec.nakend = dec.nak;
+ dec.rejend = dec.rej;
+ cp = MBUF_CTOP(bp);
+ (*fp->fn->DecodeConfig)(fp, cp, cp + flen, MODE_ACK, &dec);
+ if (flen < sizeof(struct fsm_opt_hdr))
+ log_Printf(fp->LogLevel, " [EMPTY]\n");
+
+ switch (fp->state) {
+ case ST_CLOSED:
+ case ST_STOPPED:
+ (*fp->fn->SendTerminateAck)(fp, lhp->id);
+ break;
+ case ST_CLOSING:
+ case ST_STOPPING:
+ break;
+ case ST_REQSENT:
+ FsmInitRestartCounter(fp, FSM_REQ_TIMER);
+ NewState(fp, ST_ACKRCVD);
+ break;
+ case ST_ACKRCVD:
+ FsmSendConfigReq(fp);
+ NewState(fp, ST_REQSENT);
+ break;
+ case ST_ACKSENT:
+ FsmInitRestartCounter(fp, FSM_REQ_TIMER);
+ NewState(fp, ST_OPENED);
+ if ((*fp->fn->LayerUp)(fp))
+ (*fp->parent->LayerUp)(fp->parent->object, fp);
+ else {
+ (*fp->fn->LayerDown)(fp);
+ FsmInitRestartCounter(fp, FSM_TRM_TIMER);
+ FsmSendTerminateReq(fp);
+ NewState(fp, ST_CLOSING);
+ lcp_SendIdentification(&fp->link->lcp);
+ }
+ break;
+ case ST_OPENED:
+ (*fp->fn->LayerDown)(fp);
+ FsmSendConfigReq(fp);
+ NewState(fp, ST_REQSENT);
+ (*fp->parent->LayerDown)(fp->parent->object, fp);
+ break;
+ }
+ m_freem(bp);
+}
+
+static void
+FsmRecvConfigNak(struct fsm *fp, struct fsmheader *lhp, struct mbuf *bp)
+/* RCN */
+{
+ struct fsm_decode dec;
+ int plen, flen;
+ u_char *cp;
+
+ plen = m_length(bp);
+ flen = ntohs(lhp->length) - sizeof *lhp;
+ if (plen < flen) {
+ m_freem(bp);
+ return;
+ }
+
+ /*
+ * Check and process easy case
+ */
+ switch (fp->state) {
+ case ST_INITIAL:
+ case ST_STARTING:
+ log_Printf(fp->LogLevel, "%s: Oops, RCN in %s.\n",
+ fp->link->name, State2Nam(fp->state));
+ m_freem(bp);
+ return;
+ case ST_CLOSED:
+ case ST_STOPPED:
+ (*fp->fn->SendTerminateAck)(fp, lhp->id);
+ m_freem(bp);
+ return;
+ case ST_CLOSING:
+ case ST_STOPPING:
+ m_freem(bp);
+ return;
+ }
+
+ bp = m_pullup(bp);
+ dec.ackend = dec.ack;
+ dec.nakend = dec.nak;
+ dec.rejend = dec.rej;
+ cp = MBUF_CTOP(bp);
+ (*fp->fn->DecodeConfig)(fp, cp, cp + flen, MODE_NAK, &dec);
+ if (flen < sizeof(struct fsm_opt_hdr))
+ log_Printf(fp->LogLevel, " [EMPTY]\n");
+
+ switch (fp->state) {
+ case ST_REQSENT:
+ case ST_ACKSENT:
+ FsmInitRestartCounter(fp, FSM_REQ_TIMER);
+ FsmSendConfigReq(fp);
+ break;
+ case ST_OPENED:
+ (*fp->fn->LayerDown)(fp);
+ FsmSendConfigReq(fp);
+ NewState(fp, ST_REQSENT);
+ (*fp->parent->LayerDown)(fp->parent->object, fp);
+ break;
+ case ST_ACKRCVD:
+ FsmSendConfigReq(fp);
+ NewState(fp, ST_REQSENT);
+ break;
+ }
+
+ m_freem(bp);
+}
+
+static void
+FsmRecvTermReq(struct fsm *fp, struct fsmheader *lhp, struct mbuf *bp)
+/* RTR */
+{
+ switch (fp->state) {
+ case ST_INITIAL:
+ case ST_STARTING:
+ log_Printf(fp->LogLevel, "%s: Oops, RTR in %s\n",
+ fp->link->name, State2Nam(fp->state));
+ break;
+ case ST_CLOSED:
+ case ST_STOPPED:
+ case ST_CLOSING:
+ case ST_STOPPING:
+ case ST_REQSENT:
+ (*fp->fn->SendTerminateAck)(fp, lhp->id);
+ break;
+ case ST_ACKRCVD:
+ case ST_ACKSENT:
+ (*fp->fn->SendTerminateAck)(fp, lhp->id);
+ NewState(fp, ST_REQSENT);
+ break;
+ case ST_OPENED:
+ (*fp->fn->LayerDown)(fp);
+ (*fp->fn->SendTerminateAck)(fp, lhp->id);
+ FsmInitRestartCounter(fp, FSM_TRM_TIMER);
+ timer_Start(&fp->FsmTimer); /* Start restart timer */
+ fp->restart = 0;
+ NewState(fp, ST_STOPPING);
+ (*fp->parent->LayerDown)(fp->parent->object, fp);
+ /* A delayed ST_STOPPED is now scheduled */
+ break;
+ }
+ m_freem(bp);
+}
+
+static void
+FsmRecvTermAck(struct fsm *fp, struct fsmheader *lhp, struct mbuf *bp)
+/* RTA */
+{
+ switch (fp->state) {
+ case ST_CLOSING:
+ (*fp->fn->LayerFinish)(fp);
+ NewState(fp, ST_CLOSED);
+ (*fp->parent->LayerFinish)(fp->parent->object, fp);
+ break;
+ case ST_STOPPING:
+ (*fp->fn->LayerFinish)(fp);
+ NewState(fp, ST_STOPPED);
+ (*fp->parent->LayerFinish)(fp->parent->object, fp);
+ break;
+ case ST_ACKRCVD:
+ NewState(fp, ST_REQSENT);
+ break;
+ case ST_OPENED:
+ (*fp->fn->LayerDown)(fp);
+ FsmSendConfigReq(fp);
+ NewState(fp, ST_REQSENT);
+ (*fp->parent->LayerDown)(fp->parent->object, fp);
+ break;
+ }
+ m_freem(bp);
+}
+
+static void
+FsmRecvConfigRej(struct fsm *fp, struct fsmheader *lhp, struct mbuf *bp)
+/* RCJ */
+{
+ struct fsm_decode dec;
+ int plen, flen;
+ u_char *cp;
+
+ plen = m_length(bp);
+ flen = ntohs(lhp->length) - sizeof *lhp;
+ if (plen < flen) {
+ m_freem(bp);
+ return;
+ }
+
+ lcp_SendIdentification(&fp->link->lcp);
+
+ /*
+ * Check and process easy case
+ */
+ switch (fp->state) {
+ case ST_INITIAL:
+ case ST_STARTING:
+ log_Printf(fp->LogLevel, "%s: Oops, RCJ in %s.\n",
+ fp->link->name, State2Nam(fp->state));
+ m_freem(bp);
+ return;
+ case ST_CLOSED:
+ case ST_STOPPED:
+ (*fp->fn->SendTerminateAck)(fp, lhp->id);
+ m_freem(bp);
+ return;
+ case ST_CLOSING:
+ case ST_STOPPING:
+ m_freem(bp);
+ return;
+ }
+
+ bp = m_pullup(bp);
+ dec.ackend = dec.ack;
+ dec.nakend = dec.nak;
+ dec.rejend = dec.rej;
+ cp = MBUF_CTOP(bp);
+ (*fp->fn->DecodeConfig)(fp, cp, cp + flen, MODE_REJ, &dec);
+ if (flen < sizeof(struct fsm_opt_hdr))
+ log_Printf(fp->LogLevel, " [EMPTY]\n");
+
+ switch (fp->state) {
+ case ST_REQSENT:
+ case ST_ACKSENT:
+ FsmInitRestartCounter(fp, FSM_REQ_TIMER);
+ FsmSendConfigReq(fp);
+ break;
+ case ST_OPENED:
+ (*fp->fn->LayerDown)(fp);
+ FsmSendConfigReq(fp);
+ NewState(fp, ST_REQSENT);
+ (*fp->parent->LayerDown)(fp->parent->object, fp);
+ break;
+ case ST_ACKRCVD:
+ FsmSendConfigReq(fp);
+ NewState(fp, ST_REQSENT);
+ break;
+ }
+ m_freem(bp);
+}
+
+static void
+FsmRecvCodeRej(struct fsm *fp, struct fsmheader *lhp, struct mbuf *bp)
+{
+ m_freem(bp);
+}
+
+static void
+FsmRecvProtoRej(struct fsm *fp, struct fsmheader *lhp, struct mbuf *bp)
+{
+ struct physical *p = link2physical(fp->link);
+ u_short proto;
+
+ if (m_length(bp) < 2) {
+ m_freem(bp);
+ return;
+ }
+ bp = mbuf_Read(bp, &proto, 2);
+ proto = ntohs(proto);
+ log_Printf(fp->LogLevel, "%s: -- Protocol 0x%04x (%s) was rejected!\n",
+ fp->link->name, proto, hdlc_Protocol2Nam(proto));
+
+ switch (proto) {
+ case PROTO_LQR:
+ if (p)
+ lqr_Stop(p, LQM_LQR);
+ else
+ log_Printf(LogERROR, "%s: FsmRecvProtoRej: Not a physical link !\n",
+ fp->link->name);
+ break;
+ case PROTO_CCP:
+ if (fp->proto == PROTO_LCP) {
+ fp = &fp->link->ccp.fsm;
+ /* Despite the RFC (1661), don't do an out-of-place TLF */
+ /* (*fp->fn->LayerFinish)(fp); */
+ switch (fp->state) {
+ case ST_CLOSED:
+ case ST_CLOSING:
+ NewState(fp, ST_CLOSED);
+ break;
+ default:
+ NewState(fp, ST_STOPPED);
+ break;
+ }
+ /* See above */
+ /* (*fp->parent->LayerFinish)(fp->parent->object, fp); */
+ }
+ break;
+ case PROTO_IPCP:
+ if (fp->proto == PROTO_LCP) {
+ log_Printf(LogPHASE, "%s: IPCP protocol reject closes IPCP !\n",
+ fp->link->name);
+ fsm_Close(&fp->bundle->ncp.ipcp.fsm);
+ }
+ break;
+#ifndef NOINET6
+ case PROTO_IPV6CP:
+ if (fp->proto == PROTO_LCP) {
+ log_Printf(LogPHASE, "%s: IPV6CP protocol reject closes IPV6CP !\n",
+ fp->link->name);
+ fsm_Close(&fp->bundle->ncp.ipv6cp.fsm);
+ }
+ break;
+#endif
+ case PROTO_MP:
+ if (fp->proto == PROTO_LCP) {
+ struct lcp *lcp = fsm2lcp(fp);
+
+ if (lcp->want_mrru && lcp->his_mrru) {
+ log_Printf(LogPHASE, "%s: MP protocol reject is fatal !\n",
+ fp->link->name);
+ fsm_Close(fp);
+ }
+ }
+ break;
+ }
+ m_freem(bp);
+}
+
+static void
+FsmRecvEchoReq(struct fsm *fp, struct fsmheader *lhp, struct mbuf *bp)
+{
+ struct lcp *lcp = fsm2lcp(fp);
+ u_char *cp;
+ u_int32_t magic;
+
+ bp = m_pullup(bp);
+ m_settype(bp, MB_ECHOIN);
+
+ if (lcp && ntohs(lhp->length) - sizeof *lhp >= 4) {
+ cp = MBUF_CTOP(bp);
+ ua_ntohl(cp, &magic);
+ if (magic != lcp->his_magic) {
+ log_Printf(fp->LogLevel, "%s: RecvEchoReq: magic 0x%08lx is wrong,"
+ " expecting 0x%08lx\n", fp->link->name, (u_long)magic,
+ (u_long)lcp->his_magic);
+ /* XXX: We should send terminate request */
+ }
+ if (fp->state == ST_OPENED) {
+ ua_htonl(&lcp->want_magic, cp); /* local magic */
+ fsm_Output(fp, CODE_ECHOREP, lhp->id, cp,
+ ntohs(lhp->length) - sizeof *lhp, MB_ECHOOUT);
+ }
+ }
+ m_freem(bp);
+}
+
+static void
+FsmRecvEchoRep(struct fsm *fp, struct fsmheader *lhp, struct mbuf *bp)
+{
+ if (fsm2lcp(fp))
+ bp = lqr_RecvEcho(fp, bp);
+
+ m_freem(bp);
+}
+
+static void
+FsmRecvDiscReq(struct fsm *fp, struct fsmheader *lhp, struct mbuf *bp)
+{
+ m_freem(bp);
+}
+
+static void
+FsmRecvIdent(struct fsm *fp, struct fsmheader *lhp, struct mbuf *bp)
+{
+ u_int32_t magic;
+ u_short len;
+ u_char *cp;
+
+ len = ntohs(lhp->length) - sizeof *lhp;
+ if (len >= 4) {
+ bp = m_pullup(m_append(bp, "", 1));
+ cp = MBUF_CTOP(bp);
+ ua_ntohl(cp, &magic);
+ if (magic != fp->link->lcp.his_magic)
+ log_Printf(fp->LogLevel, "%s: RecvIdent: magic 0x%08lx is wrong,"
+ " expecting 0x%08lx\n", fp->link->name, (u_long)magic,
+ (u_long)fp->link->lcp.his_magic);
+ cp[len] = '\0';
+ lcp_RecvIdentification(&fp->link->lcp, cp + 4);
+ }
+ m_freem(bp);
+}
+
+static void
+FsmRecvTimeRemain(struct fsm *fp, struct fsmheader *lhp, struct mbuf *bp)
+{
+ m_freem(bp);
+}
+
+static void
+FsmRecvResetReq(struct fsm *fp, struct fsmheader *lhp, struct mbuf *bp)
+{
+ if ((*fp->fn->RecvResetReq)(fp)) {
+ /*
+ * All sendable compressed packets are queued in the first (lowest
+ * priority) modem output queue.... dump 'em to the priority queue
+ * so that they arrive at the peer before our ResetAck.
+ */
+ link_SequenceQueue(fp->link);
+ fsm_Output(fp, CODE_RESETACK, lhp->id, NULL, 0, MB_CCPOUT);
+ }
+ m_freem(bp);
+}
+
+static void
+FsmRecvResetAck(struct fsm *fp, struct fsmheader *lhp, struct mbuf *bp)
+{
+ (*fp->fn->RecvResetAck)(fp, lhp->id);
+ m_freem(bp);
+}
+
+void
+fsm_Input(struct fsm *fp, struct mbuf *bp)
+{
+ int len;
+ struct fsmheader lh;
+ const struct fsmcodedesc *codep;
+
+ len = m_length(bp);
+ if (len < sizeof(struct fsmheader)) {
+ m_freem(bp);
+ return;
+ }
+ bp = mbuf_Read(bp, &lh, sizeof lh);
+
+ if (ntohs(lh.length) > len) {
+ log_Printf(LogWARN, "%s: Oops: Got %d bytes but %d byte payload "
+ "- dropped\n", fp->link->name, len, (int)ntohs(lh.length));
+ m_freem(bp);
+ return;
+ }
+
+ if (lh.code < fp->min_code || lh.code > fp->max_code ||
+ lh.code > sizeof FsmCodes / sizeof *FsmCodes) {
+ /*
+ * Use a private id. This is really a response-type packet, but we
+ * MUST send a unique id for each REQ....
+ */
+ static u_char id;
+
+ bp = m_prepend(bp, &lh, sizeof lh, 0);
+ bp = m_pullup(bp);
+ fsm_Output(fp, CODE_CODEREJ, id++, MBUF_CTOP(bp), bp->m_len, MB_UNKNOWN);
+ m_freem(bp);
+ return;
+ }
+
+ codep = FsmCodes + lh.code - 1;
+ if (lh.id != fp->reqid && codep->check_reqid &&
+ Enabled(fp->bundle, OPT_IDCHECK)) {
+ log_Printf(fp->LogLevel, "%s: Recv%s(%d), dropped (expected %d)\n",
+ fp->link->name, codep->name, lh.id, fp->reqid);
+ return;
+ }
+
+ log_Printf(fp->LogLevel, "%s: Recv%s(%d) state = %s\n",
+ fp->link->name, codep->name, lh.id, State2Nam(fp->state));
+
+ if (codep->inc_reqid && (lh.id == fp->reqid ||
+ (!Enabled(fp->bundle, OPT_IDCHECK) && codep->check_reqid)))
+ fp->reqid++; /* That's the end of that ``exchange''.... */
+
+ (*codep->recv)(fp, &lh, bp);
+}
+
+int
+fsm_NullRecvResetReq(struct fsm *fp)
+{
+ log_Printf(fp->LogLevel, "%s: Oops - received unexpected reset req\n",
+ fp->link->name);
+ return 1;
+}
+
+void
+fsm_NullRecvResetAck(struct fsm *fp, u_char id)
+{
+ log_Printf(fp->LogLevel, "%s: Oops - received unexpected reset ack\n",
+ fp->link->name);
+}
+
+void
+fsm_Reopen(struct fsm *fp)
+{
+ if (fp->state == ST_OPENED) {
+ (*fp->fn->LayerDown)(fp);
+ FsmInitRestartCounter(fp, FSM_REQ_TIMER);
+ FsmSendConfigReq(fp);
+ NewState(fp, ST_REQSENT);
+ (*fp->parent->LayerDown)(fp->parent->object, fp);
+ }
+}
+
+void
+fsm2initial(struct fsm *fp)
+{
+ timer_Stop(&fp->FsmTimer);
+ timer_Stop(&fp->OpenTimer);
+ timer_Stop(&fp->StoppedTimer);
+ if (fp->state == ST_STOPPED)
+ fsm_Close(fp);
+ if (fp->state > ST_INITIAL)
+ fsm_Down(fp);
+ if (fp->state > ST_INITIAL)
+ fsm_Close(fp);
+}
+
+struct fsm_opt *
+fsm_readopt(u_char **cp)
+{
+ struct fsm_opt *o = (struct fsm_opt *)*cp;
+
+ if (o->hdr.len < sizeof(struct fsm_opt_hdr)) {
+ log_Printf(LogERROR, "Bad option length %d (out of phase?)\n", o->hdr.len);
+ return NULL;
+ }
+
+ *cp += o->hdr.len;
+
+ if (o->hdr.len > sizeof(struct fsm_opt)) {
+ log_Printf(LogERROR, "Warning: Truncating option length from %d to %d\n",
+ o->hdr.len, (int)sizeof(struct fsm_opt));
+ o->hdr.len = sizeof(struct fsm_opt);
+ }
+
+ return o;
+}
+
+static int
+fsm_opt(u_char *opt, int optlen, const struct fsm_opt *o)
+{
+ int cplen = o->hdr.len;
+
+ if (optlen < sizeof(struct fsm_opt_hdr))
+ optlen = 0;
+
+ if (cplen > optlen) {
+ log_Printf(LogERROR, "Can't REJ length %d - trunating to %d\n",
+ cplen, optlen);
+ cplen = optlen;
+ }
+ memcpy(opt, o, cplen);
+ if (cplen)
+ opt[1] = cplen;
+
+ return cplen;
+}
+
+void
+fsm_rej(struct fsm_decode *dec, const struct fsm_opt *o)
+{
+ if (!dec)
+ return;
+ dec->rejend += fsm_opt(dec->rejend, FSM_OPTLEN - (dec->rejend - dec->rej), o);
+}
+
+void
+fsm_ack(struct fsm_decode *dec, const struct fsm_opt *o)
+{
+ if (!dec)
+ return;
+ dec->ackend += fsm_opt(dec->ackend, FSM_OPTLEN - (dec->ackend - dec->ack), o);
+}
+
+void
+fsm_nak(struct fsm_decode *dec, const struct fsm_opt *o)
+{
+ if (!dec)
+ return;
+ dec->nakend += fsm_opt(dec->nakend, FSM_OPTLEN - (dec->nakend - dec->nak), o);
+}
+
+void
+fsm_opt_normalise(struct fsm_decode *dec)
+{
+ if (dec->rejend != dec->rej) {
+ /* rejects are preferred */
+ dec->ackend = dec->ack;
+ dec->nakend = dec->nak;
+ } else if (dec->nakend != dec->nak)
+ /* then NAKs */
+ dec->ackend = dec->ack;
+}
diff --git a/usr.sbin/ppp/fsm.h b/usr.sbin/ppp/fsm.h
new file mode 100644
index 0000000..3280ba0
--- /dev/null
+++ b/usr.sbin/ppp/fsm.h
@@ -0,0 +1,201 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * State of machine
+ */
+#define ST_INITIAL 0
+#define ST_STARTING 1
+#define ST_CLOSED 2
+#define ST_STOPPED 3
+#define ST_CLOSING 4
+#define ST_STOPPING 5
+#define ST_REQSENT 6
+#define ST_ACKRCVD 7
+#define ST_ACKSENT 8
+#define ST_OPENED 9
+
+#define ST_MAX 10
+#define ST_UNDEF -1
+
+#define MODE_REQ 0
+#define MODE_NAK 1
+#define MODE_REJ 2
+#define MODE_NOP 3
+#define MODE_ACK 4 /* pseudo mode for ccp negotiations */
+
+#define OPEN_PASSIVE -1
+
+#define FSM_REQ_TIMER 1
+#define FSM_TRM_TIMER 2
+
+#define FSM_OPTLEN 100
+
+struct fsm;
+
+struct fsm_retry {
+ u_int timeout; /* FSM retry frequency */
+ u_int maxreq; /* Max Config REQ retries */
+ u_int maxtrm; /* Max Term REQ retries */
+};
+
+struct fsm_decode {
+ u_char ack[FSM_OPTLEN], *ackend;
+ u_char nak[FSM_OPTLEN], *nakend;
+ u_char rej[FSM_OPTLEN], *rejend;
+};
+
+struct fsm_callbacks {
+ int (*LayerUp)(struct fsm *); /* Layer is now up (tlu) */
+ void (*LayerDown)(struct fsm *); /* About to come down (tld) */
+ void (*LayerStart)(struct fsm *); /* Layer about to start (tls) */
+ void (*LayerFinish)(struct fsm *); /* Layer now down (tlf) */
+ void (*InitRestartCounter)(struct fsm *, int);/* Set fsm timer load */
+ void (*SendConfigReq)(struct fsm *); /* Send REQ please */
+ void (*SentTerminateReq)(struct fsm *); /* Term REQ just sent */
+ void (*SendTerminateAck)(struct fsm *, u_char); /* Send Term ACK please */
+ void (*DecodeConfig)(struct fsm *, u_char *, u_char *, int,
+ struct fsm_decode *); /* Deal with incoming data */
+ int (*RecvResetReq)(struct fsm *fp); /* Reset output */
+ void (*RecvResetAck)(struct fsm *fp, u_char); /* Reset input */
+};
+
+struct fsm_parent {
+ void (*LayerStart) (void *, struct fsm *); /* tls */
+ void (*LayerUp) (void *, struct fsm *); /* tlu */
+ void (*LayerDown) (void *, struct fsm *); /* tld */
+ void (*LayerFinish) (void *, struct fsm *); /* tlf */
+ void *object;
+};
+
+struct link;
+struct bundle;
+
+struct fsm {
+ const char *name; /* Name of protocol */
+ u_short proto; /* Protocol number */
+ u_short min_code;
+ u_short max_code;
+ int open_mode; /* Delay before config REQ (-1 forever) */
+ int state; /* State of the machine */
+ u_char reqid; /* Next request id */
+ int restart; /* Restart counter value */
+
+ struct {
+ int reqs; /* Max config REQs before a close() */
+ int naks; /* Max config NAKs before a close() */
+ int rejs; /* Max config REJs before a close() */
+ } more;
+
+ struct pppTimer FsmTimer; /* Restart Timer */
+ struct pppTimer OpenTimer; /* Delay before opening */
+
+ /*
+ * This timer times the ST_STOPPED state out after the given value
+ * (specified via "set stopped ..."). Although this isn't specified in the
+ * rfc, the rfc *does* say that "the application may use higher level
+ * timers to avoid deadlock". The StoppedTimer takes effect when the other
+ * side ABENDs rather than going into ST_ACKSENT (and sending the ACK),
+ * causing ppp to time out and drop into ST_STOPPED. At this point,
+ * nothing will change this state :-(
+ */
+ struct pppTimer StoppedTimer;
+ int LogLevel;
+
+ /* The link layer active with this FSM (may be our bundle below) */
+ struct link *link;
+
+ /* Our high-level link */
+ struct bundle *bundle;
+
+ const struct fsm_parent *parent;
+ const struct fsm_callbacks *fn;
+};
+
+struct fsmheader {
+ u_char code; /* Request code */
+ u_char id; /* Identification */
+ u_short length; /* Length of packet */
+};
+
+#define CODE_CONFIGREQ 1
+#define CODE_CONFIGACK 2
+#define CODE_CONFIGNAK 3
+#define CODE_CONFIGREJ 4
+#define CODE_TERMREQ 5
+#define CODE_TERMACK 6
+#define CODE_CODEREJ 7
+#define CODE_PROTOREJ 8
+#define CODE_ECHOREQ 9 /* Used in LCP */
+#define CODE_ECHOREP 10 /* Used in LCP */
+#define CODE_DISCREQ 11
+#define CODE_IDENT 12 /* Used in LCP Extension */
+#define CODE_TIMEREM 13 /* Used in LCP Extension */
+#define CODE_RESETREQ 14 /* Used in CCP */
+#define CODE_RESETACK 15 /* Used in CCP */
+
+struct fsm_opt_hdr {
+ u_char id;
+ u_char len;
+};
+
+#define MAX_FSM_OPT_LEN 52
+struct fsm_opt {
+ struct fsm_opt_hdr hdr;
+ u_char data[MAX_FSM_OPT_LEN-2];
+};
+
+#define INC_FSM_OPT(ty, length, o) \
+ do { \
+ (o)->hdr.id = (ty); \
+ (o)->hdr.len = (length); \
+ (o) = (struct fsm_opt *)((u_char *)(o) + (length)); \
+ } while (0)
+
+
+extern void fsm_Init(struct fsm *, const char *, u_short, int, int, int,
+ struct bundle *, struct link *, const struct fsm_parent *,
+ struct fsm_callbacks *, const char * const [3]);
+extern void fsm_Output(struct fsm *, u_int, u_int, u_char *, int, int);
+extern void fsm_Open(struct fsm *);
+extern void fsm_Up(struct fsm *);
+extern void fsm_Down(struct fsm *);
+extern void fsm_Input(struct fsm *, struct mbuf *);
+extern void fsm_Close(struct fsm *);
+extern int fsm_NullRecvResetReq(struct fsm *);
+extern void fsm_NullRecvResetAck(struct fsm *, u_char);
+extern void fsm_Reopen(struct fsm *);
+extern void fsm2initial(struct fsm *);
+extern const char *State2Nam(u_int);
+extern struct fsm_opt *fsm_readopt(u_char **);
+extern void fsm_rej(struct fsm_decode *, const struct fsm_opt *);
+extern void fsm_ack(struct fsm_decode *, const struct fsm_opt *);
+extern void fsm_nak(struct fsm_decode *, const struct fsm_opt *);
+extern void fsm_opt_normalise(struct fsm_decode *);
diff --git a/usr.sbin/ppp/hdlc.c b/usr.sbin/ppp/hdlc.c
new file mode 100644
index 0000000..45a8951
--- /dev/null
+++ b/usr.sbin/ppp/hdlc.c
@@ -0,0 +1,439 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/un.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+
+#include "defs.h"
+#include "layer.h"
+#include "command.h"
+#include "mbuf.h"
+#include "log.h"
+#include "timer.h"
+#include "fsm.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "throughput.h"
+#include "auth.h"
+#include "lcp.h"
+#include "async.h"
+#include "ccp.h"
+#include "link.h"
+#include "descriptor.h"
+#include "chap.h"
+#include "physical.h"
+#include "prompt.h"
+#include "chat.h"
+#include "mp.h"
+#include "cbcp.h"
+#include "datalink.h"
+
+static u_int16_t const fcstab[256] = {
+ /* 00 */ 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+ /* 08 */ 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+ /* 10 */ 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+ /* 18 */ 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+ /* 20 */ 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+ /* 28 */ 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+ /* 30 */ 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+ /* 38 */ 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+ /* 40 */ 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+ /* 48 */ 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+ /* 50 */ 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+ /* 58 */ 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+ /* 60 */ 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+ /* 68 */ 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+ /* 70 */ 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+ /* 78 */ 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+ /* 80 */ 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+ /* 88 */ 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+ /* 90 */ 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+ /* 98 */ 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+ /* a0 */ 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+ /* a8 */ 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+ /* b0 */ 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+ /* b8 */ 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+ /* c0 */ 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+ /* c8 */ 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+ /* d0 */ 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+ /* d8 */ 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+ /* e0 */ 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+ /* e8 */ 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+ /* f0 */ 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+ /* f8 */ 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+};
+
+void
+hdlc_Init(struct hdlc *hdlc, struct lcp *lcp)
+{
+ memset(hdlc, '\0', sizeof(struct hdlc));
+ hdlc->lqm.owner = lcp;
+}
+
+/*
+ * HDLC FCS computation. Read RFC 1171 Appendix B and CCITT X.25 section
+ * 2.27 for further details.
+ */
+u_short
+hdlc_Fcs(u_char *cp, size_t len)
+{
+ u_short fcs = INITFCS;
+
+ while (len--)
+ fcs = (fcs >> 8) ^ fcstab[(fcs ^ *cp++) & 0xff];
+
+ return fcs;
+}
+
+static inline u_short
+HdlcFcsBuf(u_short fcs, struct mbuf *m)
+{
+ int len;
+ u_char *pos, *end;
+
+ len = m_length(m);
+ pos = MBUF_CTOP(m);
+ end = pos + m->m_len;
+ while (len--) {
+ fcs = (fcs >> 8) ^ fcstab[(fcs ^ *pos++) & 0xff];
+ if (pos == end && len) {
+ m = m->m_next;
+ pos = MBUF_CTOP(m);
+ end = pos + m->m_len;
+ }
+ }
+ return (fcs);
+}
+
+int
+hdlc_WrapperOctets(struct lcp *lcp, u_short proto)
+{
+ return 2;
+}
+
+static struct mbuf *
+hdlc_LayerPush(struct bundle *bundle, struct link *l, struct mbuf *bp,
+ int pri, u_short *proto)
+{
+ struct mbuf *last;
+ u_char *cp;
+ u_short fcs;
+
+ m_settype(bp, MB_HDLCOUT);
+ fcs = HdlcFcsBuf(INITFCS, bp);
+ fcs = ~fcs;
+
+ for (last = bp; last->m_next; last = last->m_next)
+ ;
+
+ if (last->m_size - last->m_offset - last->m_len >= 2) {
+ cp = MBUF_CTOP(last) + last->m_len;
+ last->m_len += 2;
+ } else {
+ struct mbuf *tail = m_get(2, MB_HDLCOUT);
+ last->m_next = tail;
+ cp = MBUF_CTOP(tail);
+ }
+
+ *cp++ = fcs & 0377; /* Low byte first (nothing like consistency) */
+ *cp++ = fcs >> 8;
+
+ log_DumpBp(LogHDLC, "hdlc_Output", bp);
+
+ return bp;
+}
+
+/* Check out the latest ``Assigned numbers'' rfc (rfc1700.txt) */
+static struct {
+ u_short from;
+ u_short to;
+ const char *name;
+} protocols[] = {
+ { 0x0001, 0x0001, "Padding Protocol" },
+ { 0x0003, 0x001f, "reserved (transparency inefficient)" },
+ { 0x0021, 0x0021, "Internet Protocol" },
+ { 0x0023, 0x0023, "OSI Network Layer" },
+ { 0x0025, 0x0025, "Xerox NS IDP" },
+ { 0x0027, 0x0027, "DECnet Phase IV" },
+ { 0x0029, 0x0029, "Appletalk" },
+ { 0x002b, 0x002b, "Novell IPX" },
+ { 0x002d, 0x002d, "Van Jacobson Compressed TCP/IP" },
+ { 0x002f, 0x002f, "Van Jacobson Uncompressed TCP/IP" },
+ { 0x0031, 0x0031, "Bridging PDU" },
+ { 0x0033, 0x0033, "Stream Protocol (ST-II)" },
+ { 0x0035, 0x0035, "Banyan Vines" },
+ { 0x0037, 0x0037, "reserved (until 1993)" },
+ { 0x0039, 0x0039, "AppleTalk EDDP" },
+ { 0x003b, 0x003b, "AppleTalk SmartBuffered" },
+ { 0x003d, 0x003d, "Multi-Link" },
+ { 0x003f, 0x003f, "NETBIOS Framing" },
+ { 0x0041, 0x0041, "Cisco Systems" },
+ { 0x0043, 0x0043, "Ascom Timeplex" },
+ { 0x0045, 0x0045, "Fujitsu Link Backup and Load Balancing (LBLB)" },
+ { 0x0047, 0x0047, "DCA Remote Lan" },
+ { 0x0049, 0x0049, "Serial Data Transport Protocol (PPP-SDTP)" },
+ { 0x004b, 0x004b, "SNA over 802.2" },
+ { 0x004d, 0x004d, "SNA" },
+ { 0x004f, 0x004f, "IP6 Header Compression" },
+ { 0x0051, 0x0051, "KNX Bridging Data" },
+ { 0x0053, 0x0053, "Encryption" },
+ { 0x0055, 0x0055, "Individual Link Encryption" },
+ { 0x0057, 0x0057, "Internet Protocol V6" },
+ { 0x006f, 0x006f, "Stampede Bridging" },
+ { 0x0071, 0x0071, "BAP Bandwidth Allocation Protocol" },
+ { 0x0073, 0x0073, "MP+ Protocol" },
+ { 0x007d, 0x007d, "reserved (Control Escape)" },
+ { 0x007f, 0x007f, "reserved (compression inefficient)" },
+ { 0x00cf, 0x00cf, "reserved (PPP NLPID)" },
+ { 0x00fb, 0x00fb, "compression on single link in multilink group" },
+ { 0x00fd, 0x00fd, "1st choice compression" },
+ { 0x00ff, 0x00ff, "reserved (compression inefficient)" },
+ { 0x0200, 0x02ff, "(compression inefficient)" },
+ { 0x0201, 0x0201, "802.1d Hello Packets" },
+ { 0x0203, 0x0203, "IBM Source Routing BPDU" },
+ { 0x0205, 0x0205, "DEC LANBridge100 Spanning Tree" },
+ { 0x0207, 0x0207, "Cisco Discovery Protocol" },
+ { 0x0209, 0x0209, "Netcs Twin Routing" },
+ { 0x0231, 0x0231, "Luxcom" },
+ { 0x0233, 0x0233, "Sigma Network Systems" },
+ { 0x0235, 0x0235, "Apple Client Server Protocol" },
+ { 0x1e00, 0x1eff, "(compression inefficient)" },
+ { 0x4001, 0x4001, "Cray Communications Control Protocol" },
+ { 0x4003, 0x4003, "CDPD Mobile Network Registration Protocol" },
+ { 0x4021, 0x4021, "Stacker LZS" },
+ { 0x8001, 0x801f, "Not Used - reserved" },
+ { 0x8021, 0x8021, "Internet Protocol Control Protocol" },
+ { 0x8023, 0x8023, "OSI Network Layer Control Protocol" },
+ { 0x8025, 0x8025, "Xerox NS IDP Control Protocol" },
+ { 0x8027, 0x8027, "DECnet Phase IV Control Protocol" },
+ { 0x8029, 0x8029, "Appletalk Control Protocol" },
+ { 0x802b, 0x802b, "Novell IPX Control Protocol" },
+ { 0x802d, 0x802d, "reserved" },
+ { 0x802f, 0x802f, "reserved" },
+ { 0x8031, 0x8031, "Bridging NCP" },
+ { 0x8033, 0x8033, "Stream Protocol Control Protocol" },
+ { 0x8035, 0x8035, "Banyan Vines Control Protocol" },
+ { 0x8037, 0x8037, "reserved till 1993" },
+ { 0x8039, 0x8039, "reserved" },
+ { 0x803b, 0x803b, "reserved" },
+ { 0x803d, 0x803d, "Multi-Link Control Protocol" },
+ { 0x803f, 0x803f, "NETBIOS Framing Control Protocol" },
+ { 0x8041, 0x8041, "Cisco Systems Control Protocol" },
+ { 0x8043, 0x8043, "Ascom Timeplex" },
+ { 0x8045, 0x8045, "Fujitsu LBLB Control Protocol" },
+ { 0x8047, 0x8047, "DCA Remote Lan Network Control Protocol (RLNCP)" },
+ { 0x8049, 0x8049, "Serial Data Control Protocol (PPP-SDCP)" },
+ { 0x804b, 0x804b, "SNA over 802.2 Control Protocol" },
+ { 0x804d, 0x804d, "SNA Control Protocol" },
+ { 0x804f, 0x804f, "IP6 Header Compression Control Protocol" },
+ { 0x8051, 0x8051, "KNX Bridging Control Protocol" },
+ { 0x8053, 0x8053, "Encryption Control Protocol" },
+ { 0x8055, 0x8055, "Individual Link Encryption Control Protocol" },
+ { 0x8057, 0x8057, "Internet Protocol V6 Control Protocol" },
+ { 0x806f, 0x806f, "Stampede Bridging Control Protocol" },
+ { 0x8073, 0x8073, "MP+ Control Protocol" },
+ { 0x8071, 0x8071, "BACP Bandwidth Allocation Control Protocol" },
+ { 0x807d, 0x807d, "Not Used - reserved" },
+ { 0x80cf, 0x80cf, "Not Used - reserved" },
+ { 0x80fb, 0x80fb, "compression on single link in multilink group control" },
+ { 0x80fd, 0x80fd, "Compression Control Protocol" },
+ { 0x80ff, 0x80ff, "Not Used - reserved" },
+ { 0x8207, 0x8207, "Cisco Discovery Protocol Control" },
+ { 0x8209, 0x8209, "Netcs Twin Routing" },
+ { 0x8235, 0x8235, "Apple Client Server Protocol Control" },
+ { 0xc021, 0xc021, "Link Control Protocol" },
+ { 0xc023, 0xc023, "Password Authentication Protocol" },
+ { 0xc025, 0xc025, "Link Quality Report" },
+ { 0xc027, 0xc027, "Shiva Password Authentication Protocol" },
+ { 0xc029, 0xc029, "CallBack Control Protocol (CBCP)" },
+ { 0xc081, 0xc081, "Container Control Protocol" },
+ { 0xc223, 0xc223, "Challenge Handshake Authentication Protocol" },
+ { 0xc225, 0xc225, "RSA Authentication Protocol" },
+ { 0xc227, 0xc227, "Extensible Authentication Protocol" },
+ { 0xc26f, 0xc26f, "Stampede Bridging Authorization Protocol" },
+ { 0xc281, 0xc281, "Proprietary Authentication Protocol" },
+ { 0xc283, 0xc283, "Proprietary Authentication Protocol" },
+ { 0xc481, 0xc481, "Proprietary Node ID Authentication Protocol" }
+};
+
+#define NPROTOCOLS (sizeof protocols/sizeof protocols[0])
+
+const char *
+hdlc_Protocol2Nam(u_short proto)
+{
+ int f;
+
+ for (f = 0; f < NPROTOCOLS; f++)
+ if (proto >= protocols[f].from && proto <= protocols[f].to)
+ return protocols[f].name;
+ else if (proto < protocols[f].from)
+ break;
+ return "unrecognised protocol";
+}
+
+static struct mbuf *
+hdlc_LayerPull(struct bundle *b, struct link *l, struct mbuf *bp,
+ u_short *proto)
+{
+ struct physical *p = link2physical(l);
+ u_short fcs;
+ int len;
+
+ if (!p) {
+ log_Printf(LogERROR, "Can't Pull a hdlc packet from a logical link\n");
+ return bp;
+ }
+
+ log_DumpBp(LogHDLC, "hdlc_LayerPull:", bp);
+
+ fcs = hdlc_Fcs(MBUF_CTOP(bp), bp->m_len);
+
+ log_Printf(LogDEBUG, "%s: hdlc_LayerPull: fcs = %04x (%s)\n",
+ p->link.name, fcs, (fcs == GOODFCS) ? "good" : "BAD!");
+
+ if (fcs != GOODFCS) {
+ p->hdlc.lqm.SaveInErrors++;
+ p->hdlc.stats.badfcs++;
+ m_freem(bp);
+ return NULL;
+ }
+
+ p->hdlc.lqm.SaveInOctets += bp->m_len + 1;
+ p->hdlc.lqm.SaveInPackets++;
+
+ len = m_length(bp);
+ if (len < 4) { /* rfc1662 section 4.3 */
+ m_freem(bp);
+ bp = NULL;
+ }
+
+ bp = m_adj(bp, -2); /* discard the FCS */
+ m_settype(bp, MB_HDLCIN);
+
+ return bp;
+}
+
+/* Detect a HDLC frame */
+
+static const struct frameheader {
+ const u_char *data;
+ int len;
+} FrameHeaders[] = {
+ { "\176\377\003\300\041", 5 },
+ { "\176\377\175\043\300\041", 6 },
+ { "\176\177\175\043\100\041", 6 },
+ { "\176\175\337\175\043\300\041", 7 },
+ { "\176\175\137\175\043\100\041", 7 },
+ { NULL, 0 }
+};
+
+int
+hdlc_Detect(u_char const **cp, int n, int issync)
+{
+ const struct frameheader *fh;
+ const u_char *h;
+ size_t len, cmp;
+
+ while (n) {
+ for (fh = FrameHeaders; fh->len; fh++) {
+ h = issync ? fh->data + 1 : fh->data;
+ len = issync ? fh->len - 1 : fh->len;
+ cmp = n >= len ? len : n;
+ if (memcmp(*cp, h, cmp) == 0)
+ return cmp == len;
+ }
+ n--;
+ (*cp)++;
+ }
+
+ return 0;
+}
+
+int
+hdlc_ReportStatus(struct cmdargs const *arg)
+{
+ struct hdlc *hdlc = &arg->cx->physical->hdlc;
+
+ prompt_Printf(arg->prompt, "%s HDLC level errors:\n", arg->cx->name);
+ prompt_Printf(arg->prompt, " Bad Frame Check Sequence fields: %u\n",
+ hdlc->stats.badfcs);
+ prompt_Printf(arg->prompt, " Bad address (!= 0x%02x) fields: %u\n",
+ HDLC_ADDR, hdlc->stats.badaddr);
+ prompt_Printf(arg->prompt, " Bad command (!= 0x%02x) fields: %u\n",
+ HDLC_UI, hdlc->stats.badcommand);
+ prompt_Printf(arg->prompt, " Unrecognised protocol fields: %u\n",
+ hdlc->stats.unknownproto);
+ return 0;
+}
+
+static void
+hdlc_ReportTime(void *v)
+{
+ /* Moan about HDLC errors */
+ struct hdlc *hdlc = (struct hdlc *)v;
+
+ timer_Stop(&hdlc->ReportTimer);
+
+ if (memcmp(&hdlc->laststats, &hdlc->stats, sizeof hdlc->stats)) {
+ log_Printf(LogPHASE,
+ "%s: HDLC errors -> FCS: %u, ADDR: %u, COMD: %u, PROTO: %u\n",
+ hdlc->lqm.owner->fsm.link->name,
+ hdlc->stats.badfcs - hdlc->laststats.badfcs,
+ hdlc->stats.badaddr - hdlc->laststats.badaddr,
+ hdlc->stats.badcommand - hdlc->laststats.badcommand,
+ hdlc->stats.unknownproto - hdlc->laststats.unknownproto);
+ hdlc->laststats = hdlc->stats;
+ }
+
+ timer_Start(&hdlc->ReportTimer);
+}
+
+void
+hdlc_StartTimer(struct hdlc *hdlc)
+{
+ timer_Stop(&hdlc->ReportTimer);
+ hdlc->ReportTimer.load = 60 * SECTICKS;
+ hdlc->ReportTimer.arg = hdlc;
+ hdlc->ReportTimer.func = hdlc_ReportTime;
+ hdlc->ReportTimer.name = "hdlc";
+ timer_Start(&hdlc->ReportTimer);
+}
+
+void
+hdlc_StopTimer(struct hdlc *hdlc)
+{
+ timer_Stop(&hdlc->ReportTimer);
+}
+
+struct layer hdlclayer = { LAYER_HDLC, "hdlc", hdlc_LayerPush, hdlc_LayerPull };
diff --git a/usr.sbin/ppp/hdlc.h b/usr.sbin/ppp/hdlc.h
new file mode 100644
index 0000000..71f0814
--- /dev/null
+++ b/usr.sbin/ppp/hdlc.h
@@ -0,0 +1,111 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Definition for Async HDLC
+ */
+#define HDLC_SYN 0x7e /* SYNC character */
+#define HDLC_ESC 0x7d /* Escape character */
+#define HDLC_XOR 0x20 /* Modifier value */
+
+#define HDLC_ADDR 0xff
+#define HDLC_UI 0x03
+/*
+ * Definition for HDLC Frame Check Sequence
+ */
+#define INITFCS 0xffff /* Initial value for FCS computation */
+#define GOODFCS 0xf0b8 /* Good FCS value */
+
+#define DEF_MRU 1500
+#define MAX_MRU 2048
+#define MIN_MRU 296
+
+#define DEF_MTU 0 /* whatever peer says */
+#define MAX_MTU 2048
+#define MIN_MTU 296
+
+struct physical;
+struct link;
+struct lcp;
+struct bundle;
+struct mbuf;
+struct cmdargs;
+
+struct hdlc {
+ struct pppTimer ReportTimer;
+
+ struct {
+ int badfcs;
+ int badaddr;
+ int badcommand;
+ int unknownproto;
+ } laststats, stats;
+
+ struct {
+ struct lcp *owner; /* parent LCP */
+ struct pppTimer timer; /* When to send */
+ int method; /* bit-mask for LQM_* from lqr.h */
+
+ u_int32_t OutPackets; /* Packets sent by me */
+ u_int32_t OutOctets; /* Octets sent by me */
+ u_int32_t SaveInPackets; /* Packets received from peer */
+ u_int32_t SaveInDiscards; /* Discards */
+ u_int32_t SaveInErrors; /* Errors */
+ u_int32_t SaveInOctets; /* Octets received from peer */
+
+ struct {
+ u_int32_t OutLQRs; /* LQRs sent by me */
+ u_int32_t SaveInLQRs; /* LQRs received from peer */
+ struct lqrdata peer; /* Last LQR from peer */
+ int peer_timeout; /* peers max lqr timeout */
+ int resent; /* Resent last packet `resent' times */
+ } lqr;
+
+ struct {
+ u_int32_t seq_sent; /* last echo sent */
+ u_int32_t seq_recv; /* last echo received */
+ } echo;
+ } lqm;
+};
+
+
+extern void hdlc_Init(struct hdlc *, struct lcp *);
+extern void hdlc_StartTimer(struct hdlc *);
+extern void hdlc_StopTimer(struct hdlc *);
+extern int hdlc_ReportStatus(struct cmdargs const *);
+extern const char *hdlc_Protocol2Nam(u_short);
+extern void hdlc_DecodePacket(struct bundle *, u_short, struct mbuf *,
+ struct link *);
+
+extern u_short hdlc_Fcs(u_char *, size_t);
+extern int hdlc_Detect(u_char const **, int, int);
+extern int hdlc_WrapperOctets(struct lcp *, u_short);
+
+extern struct layer hdlclayer;
diff --git a/usr.sbin/ppp/i4b.c b/usr.sbin/ppp/i4b.c
new file mode 100644
index 0000000..5744e89
--- /dev/null
+++ b/usr.sbin/ppp/i4b.c
@@ -0,0 +1,451 @@
+/*-
+ * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+
+#include <sys/un.h>
+#if defined(__OpenBSD__) || defined(__NetBSD__)
+#include <sys/ioctl.h>
+#endif
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#ifdef __FreeBSD__
+#include <machine/i4b_ioctl.h>
+#include <machine/i4b_rbch_ioctl.h>
+#else
+#ifdef __NetBSD__
+#include <netisdn/i4b_ioctl.h>
+#include <netisdn/i4b_rbch_ioctl.h>
+#else
+#include <i4b/i4b_ioctl.h>
+#include <i4b/i4b_rbch_ioctl.h>
+#endif
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <sys/uio.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "layer.h"
+#include "defs.h"
+#include "mbuf.h"
+#include "log.h"
+#include "timer.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "throughput.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "async.h"
+#include "descriptor.h"
+#include "physical.h"
+#include "mp.h"
+#include "chat.h"
+#include "auth.h"
+#include "chap.h"
+#include "cbcp.h"
+#include "datalink.h"
+#include "main.h"
+#include "i4b.h"
+
+#define Online(dev) ((dev)->mbits & TIOCM_CD)
+
+struct i4bdevice {
+ struct device dev; /* What struct physical knows about */
+ struct pppTimer Timer; /* CD checks */
+ int mbits; /* Current DCD status */
+ int carrier_seconds; /* seconds before CD is *required* */
+};
+
+#define device2i4b(d) ((d)->type == I4B_DEVICE ? (struct i4bdevice *)d : NULL)
+
+int
+i4b_DeviceSize(void)
+{
+ return sizeof(struct i4bdevice);
+}
+
+/*
+ * i4b_Timeout() watches the DCD signal and mentions it if it's status
+ * changes.
+ */
+static void
+i4b_Timeout(void *data)
+{
+ struct physical *p = data;
+ struct i4bdevice *dev = device2i4b(p->handler);
+ int ombits, change;
+
+ timer_Stop(&dev->Timer);
+ dev->Timer.load = SECTICKS; /* Once a second please */
+ timer_Start(&dev->Timer);
+ ombits = dev->mbits;
+
+ if (p->fd >= 0) {
+ if (ioctl(p->fd, TIOCMGET, &dev->mbits) < 0) {
+ log_Printf(LogPHASE, "%s: ioctl error (%s)!\n", p->link.name,
+ strerror(errno));
+ datalink_Down(p->dl, CLOSE_NORMAL);
+ timer_Stop(&dev->Timer);
+ return;
+ }
+ } else
+ dev->mbits = 0;
+
+ if (ombits == -1) {
+ /* First time looking for carrier */
+ if (Online(dev))
+ log_Printf(LogPHASE, "%s: %s: CD detected\n", p->link.name, p->name.full);
+ else if (++dev->carrier_seconds >= dev->dev.cd.delay) {
+ log_Printf(LogPHASE, "%s: %s: No carrier"
+ " (increase ``set cd'' from %d ?)\n",
+ p->link.name, p->name.full, dev->dev.cd.delay);
+ timer_Stop(&dev->Timer);
+ /* i4b_AwaitCarrier() will notice */
+ } else {
+ /* Keep waiting */
+ log_Printf(LogDEBUG, "%s: %s: Still no carrier (%d/%d)\n",
+ p->link.name, p->name.full, dev->carrier_seconds,
+ dev->dev.cd.delay);
+ dev->mbits = -1;
+ }
+ } else {
+ change = ombits ^ dev->mbits;
+ if (change & TIOCM_CD) {
+ if (dev->mbits & TIOCM_CD)
+ log_Printf(LogDEBUG, "%s: offline -> online\n", p->link.name);
+ else {
+ log_Printf(LogDEBUG, "%s: online -> offline\n", p->link.name);
+ log_Printf(LogPHASE, "%s: Carrier lost\n", p->link.name);
+ datalink_Down(p->dl, CLOSE_NORMAL);
+ timer_Stop(&dev->Timer);
+ }
+ } else
+ log_Printf(LogDEBUG, "%s: Still %sline\n", p->link.name,
+ Online(dev) ? "on" : "off");
+ }
+}
+
+static void
+i4b_StartTimer(struct physical *p)
+{
+ struct i4bdevice *dev = device2i4b(p->handler);
+
+ timer_Stop(&dev->Timer);
+ dev->Timer.load = SECTICKS;
+ dev->Timer.func = i4b_Timeout;
+ dev->Timer.name = "i4b CD";
+ dev->Timer.arg = p;
+ log_Printf(LogDEBUG, "%s: Using i4b_Timeout [%p]\n",
+ p->link.name, i4b_Timeout);
+ timer_Start(&dev->Timer);
+}
+
+static int
+i4b_AwaitCarrier(struct physical *p)
+{
+ struct i4bdevice *dev = device2i4b(p->handler);
+
+ if (dev->mbits == -1) {
+ if (dev->Timer.state == TIMER_STOPPED) {
+ dev->carrier_seconds = 0;
+ i4b_StartTimer(p);
+ }
+ return CARRIER_PENDING; /* Not yet ! */
+ }
+
+ return Online(dev) ? CARRIER_OK : CARRIER_LOST;
+}
+
+static int
+i4b_Raw(struct physical *p)
+{
+ int oldflag;
+
+ log_Printf(LogDEBUG, "%s: Entering i4b_Raw\n", p->link.name);
+
+ oldflag = fcntl(p->fd, F_GETFL, 0);
+ if (oldflag < 0)
+ return 0;
+ fcntl(p->fd, F_SETFL, oldflag | O_NONBLOCK);
+
+ return 1;
+}
+
+static void
+i4b_Offline(struct physical *p)
+{
+ struct i4bdevice *dev = device2i4b(p->handler);
+
+ if (p->fd >= 0) {
+ timer_Stop(&dev->Timer);
+ if (Online(dev)) {
+ int dummy;
+
+ dummy = 1;
+ ioctl(p->fd, TIOCCDTR, &dummy);
+ }
+ }
+}
+
+static void
+i4b_Cooked(struct physical *p)
+{
+ int oldflag;
+
+ i4b_Offline(p); /* In case of emergency close()s */
+
+ if ((oldflag = fcntl(p->fd, F_GETFL, 0)) != -1)
+ fcntl(p->fd, F_SETFL, oldflag & ~O_NONBLOCK);
+}
+
+static void
+i4b_StopTimer(struct physical *p)
+{
+ struct i4bdevice *dev = device2i4b(p->handler);
+
+ timer_Stop(&dev->Timer);
+}
+
+static void
+i4b_Free(struct physical *p)
+{
+ struct i4bdevice *dev = device2i4b(p->handler);
+
+ i4b_Offline(p); /* In case of emergency close()s */
+ free(dev);
+}
+
+static int
+i4b_Speed(struct physical *p)
+{
+ struct termios ios;
+ int ret;
+
+ if (tcgetattr(p->fd, &ios) == -1 ||
+ (ret = SpeedToInt(cfgetispeed(&ios))) == 0)
+ ret = 64000;
+
+ return ret;
+}
+
+static const char *
+i4b_OpenInfo(struct physical *p)
+{
+ struct i4bdevice *dev = device2i4b(p->handler);
+ static char buf[26];
+
+ if (Online(dev))
+ snprintf(buf, sizeof buf, "carrier took %ds", dev->carrier_seconds);
+ else
+ *buf = '\0';
+
+ return buf;
+}
+
+static int
+i4b_Slot(struct physical *p)
+{
+ struct stat st;
+
+ if (fstat(p->fd, &st) == 0)
+ return minor(st.st_rdev);
+
+ return -1;
+}
+
+static void
+i4b_device2iov(struct device *d, struct iovec *iov, int *niov,
+ int maxiov, int *auxfd, int *nauxfd)
+{
+ struct i4bdevice *dev = device2i4b(d);
+ int sz = physical_MaxDeviceSize();
+
+ iov[*niov].iov_base = realloc(d, sz);
+ if (iov[*niov].iov_base == NULL) {
+ log_Printf(LogALERT, "Failed to allocate memory: %d\n", sz);
+ AbortProgram(EX_OSERR);
+ }
+ iov[*niov].iov_len = sz;
+ (*niov)++;
+
+ if (dev->Timer.state != TIMER_STOPPED) {
+ timer_Stop(&dev->Timer);
+ dev->Timer.state = TIMER_RUNNING;
+ }
+}
+
+static struct device basei4bdevice = {
+ I4B_DEVICE,
+ "i4b",
+ 0,
+ { CD_REQUIRED, DEF_I4BCDDELAY },
+ i4b_AwaitCarrier,
+ NULL,
+ i4b_Raw,
+ i4b_Offline,
+ i4b_Cooked,
+ NULL,
+ i4b_StopTimer,
+ i4b_Free,
+ NULL,
+ NULL,
+ i4b_device2iov,
+ i4b_Speed,
+ i4b_OpenInfo,
+ i4b_Slot
+};
+
+struct device *
+i4b_iov2device(int type, struct physical *p, struct iovec *iov, int *niov,
+ int maxiov, int *auxfd, int *nauxfd)
+{
+ if (type == I4B_DEVICE) {
+ struct i4bdevice *dev = (struct i4bdevice *)iov[(*niov)++].iov_base;
+
+ dev = realloc(dev, sizeof *dev); /* Reduce to the correct size */
+ if (dev == NULL) {
+ log_Printf(LogALERT, "Failed to allocate memory: %d\n",
+ (int)(sizeof *dev));
+ AbortProgram(EX_OSERR);
+ }
+
+ /* Refresh function pointers etc */
+ memcpy(&dev->dev, &basei4bdevice, sizeof dev->dev);
+
+ physical_SetupStack(p, dev->dev.name, PHYSICAL_NOFORCE);
+ if (dev->Timer.state != TIMER_STOPPED) {
+ dev->Timer.state = TIMER_STOPPED;
+ p->handler = &dev->dev; /* For the benefit of StartTimer */
+ i4b_StartTimer(p);
+ }
+ return &dev->dev;
+ }
+
+ return NULL;
+}
+
+struct device *
+i4b_Create(struct physical *p)
+{
+ struct i4bdevice *dev;
+ int oldflag, dial;
+ msg_vr_req_t req;
+ telno_t number;
+
+ if (p->fd < 0 || ioctl(p->fd, I4B_RBCH_VR_REQ, &req))
+ /* Don't want this */
+ return NULL;
+
+ /*
+ * We don't bother validating the version.... all versions of i4b that
+ * support I4B_RBCH_VR_REQ are fair game :-)
+ */
+
+ if (*p->name.full == '\0') {
+ physical_SetDevice(p, ttyname(p->fd));
+ log_Printf(LogDEBUG, "%s: Input is an i4b version %d.%d.%d isdn "
+ "device (%s)\n", p->link.name, req.version, req.release,
+ req.step, p->name.full);
+ dial = 0;
+ } else {
+ log_Printf(LogDEBUG, "%s: Opened %s (i4b version %d.%d.%d)\n",
+ p->link.name, p->name.full, req.version, req.release, req.step);
+ dial = 1;
+ }
+
+ /* We're gonna return an i4bdevice (unless something goes horribly wrong) */
+
+ if ((dev = malloc(sizeof *dev)) == NULL) {
+ /* Complete failure - parent doesn't continue trying to ``create'' */
+ close(p->fd);
+ p->fd = -1;
+ return NULL;
+ }
+
+ memcpy(&dev->dev, &basei4bdevice, sizeof dev->dev);
+ memset(&dev->Timer, '\0', sizeof dev->Timer);
+ dev->mbits = -1;
+
+ switch (p->cfg.cd.necessity) {
+ case CD_VARIABLE:
+ dev->dev.cd.delay = p->cfg.cd.delay;
+ break;
+ case CD_REQUIRED:
+ dev->dev.cd = p->cfg.cd;
+ break;
+ case CD_NOTREQUIRED:
+ log_Printf(LogWARN, "%s: Carrier must be set, using ``set cd %d!''\n",
+ p->link.name, dev->dev.cd.delay);
+ case CD_DEFAULT:
+ break;
+ }
+
+ oldflag = fcntl(p->fd, F_GETFL, 0);
+ if (oldflag < 0) {
+ /* Complete failure - parent doesn't continue trying to ``create'' */
+
+ log_Printf(LogWARN, "%s: Open: Cannot get physical flags: %s\n",
+ p->link.name, strerror(errno));
+ i4b_Cooked(p);
+ close(p->fd);
+ p->fd = -1;
+ free(dev);
+ return NULL;
+ } else
+ fcntl(p->fd, F_SETFL, oldflag & ~O_NONBLOCK);
+
+ if (dial) {
+ strncpy(number, datalink_ChoosePhoneNumber(p->dl), sizeof number - 1);
+ number[sizeof number - 1] = '\0';
+ if (number[0] == '\0')
+ dial = 0;
+ }
+ if (dial && ioctl(p->fd, I4B_RBCH_DIALOUT, number) == -1) {
+ /* Complete failure - parent doesn't continue trying to ``create'' */
+
+ log_Printf(LogWARN, "%s: ioctl(I4B_RBCH_DIALOUT): %s\n",
+ p->link.name, strerror(errno));
+ i4b_Cooked(p);
+ close(p->fd);
+ p->fd = -1;
+ free(dev);
+ return NULL;
+ }
+
+ physical_SetupStack(p, dev->dev.name, PHYSICAL_FORCE_SYNC);
+
+ return &dev->dev;
+}
diff --git a/usr.sbin/ppp/i4b.h b/usr.sbin/ppp/i4b.h
new file mode 100644
index 0000000..984535c
--- /dev/null
+++ b/usr.sbin/ppp/i4b.h
@@ -0,0 +1,37 @@
+/*-
+ * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct physical;
+struct device;
+
+#define DEF_I4BCDDELAY 6 /* Default ``set cd'' value */
+
+extern struct device *i4b_Create(struct physical *);
+extern struct device *i4b_iov2device(int, struct physical *,
+ struct iovec *, int *, int, int *, int *);
+extern int i4b_DeviceSize(void);
diff --git a/usr.sbin/ppp/id.c b/usr.sbin/ppp/id.c
new file mode 100644
index 0000000..440d460
--- /dev/null
+++ b/usr.sbin/ppp/id.c
@@ -0,0 +1,303 @@
+/*-
+ * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#ifndef NONETGRAPH
+#include <netgraph.h>
+#endif
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <sysexits.h>
+#if defined(__FreeBSD__) && !defined(NOKLDLOAD)
+#include <sys/linker.h>
+#endif
+#include <unistd.h>
+#ifdef __OpenBSD__
+#include <util.h>
+#else
+#include <libutil.h>
+#endif
+#include <utmp.h>
+
+#include "log.h"
+#include "main.h"
+#include "id.h"
+
+static int uid;
+static int euid;
+
+void
+ID0init()
+{
+ uid = getuid();
+ euid = geteuid();
+}
+
+static void
+ID0setuser(void)
+{
+ if (seteuid(uid) == -1) {
+ log_Printf(LogERROR, "ID0setuser: Unable to seteuid!\n");
+ AbortProgram(EX_NOPERM);
+ }
+}
+
+uid_t
+ID0realuid()
+{
+ return uid;
+}
+
+static void
+ID0set0(void)
+{
+ if (seteuid(euid) == -1) {
+ log_Printf(LogERROR, "ID0set0: Unable to seteuid!\n");
+ AbortProgram(EX_NOPERM);
+ }
+}
+
+int
+ID0ioctl(int fd, unsigned long req, void *arg)
+{
+ int ret;
+
+ ID0set0();
+ ret = ioctl(fd, req, arg);
+ log_Printf(LogID0, "%d = ioctl(%d, %lu, %p)\n", ret, fd, req, arg);
+ ID0setuser();
+ return ret;
+}
+
+int
+ID0unlink(const char *name)
+{
+ int ret;
+
+ ID0set0();
+ ret = unlink(name);
+ log_Printf(LogID0, "%d = unlink(\"%s\")\n", ret, name);
+ ID0setuser();
+ return ret;
+}
+
+int
+ID0socket(int domain, int type, int protocol)
+{
+ int ret;
+
+ ID0set0();
+ ret = socket(domain, type, protocol);
+ log_Printf(LogID0, "%d = socket(%d, %d, %d)\n", ret, domain, type, protocol);
+ ID0setuser();
+ return ret;
+}
+
+FILE *
+ID0fopen(const char *path, const char *mode)
+{
+ FILE *ret;
+
+ ID0set0();
+ ret = fopen(path, mode);
+ log_Printf(LogID0, "%p = fopen(\"%s\", \"%s\")\n", ret, path, mode);
+ ID0setuser();
+ return ret;
+}
+
+int
+ID0open(const char *path, int flags, ...)
+{
+ int ret;
+ va_list ap;
+
+ va_start(ap, flags);
+ ID0set0();
+ ret = open(path, flags, va_arg(ap, int));
+ log_Printf(LogID0, "%d = open(\"%s\", %d)\n", ret, path, flags);
+ ID0setuser();
+ va_end(ap);
+ return ret;
+}
+
+int
+ID0write(int fd, const void *data, size_t len)
+{
+ int ret;
+
+ ID0set0();
+ ret = write(fd, data, len);
+ log_Printf(LogID0, "%d = write(%d, data, %ld)\n", ret, fd, (long)len);
+ ID0setuser();
+ return ret;
+}
+
+int
+ID0uu_lock(const char *basettyname)
+{
+ int ret;
+
+ ID0set0();
+ ret = uu_lock(basettyname);
+ log_Printf(LogID0, "%d = uu_lock(\"%s\")\n", ret, basettyname);
+ ID0setuser();
+ return ret;
+}
+
+int
+ID0uu_lock_txfr(const char *basettyname, pid_t newpid)
+{
+ int ret;
+
+ ID0set0();
+ ret = uu_lock_txfr(basettyname, newpid);
+ log_Printf(LogID0, "%d = uu_lock_txfr(\"%s\", %ld)\n", ret, basettyname,
+ (long)newpid);
+ ID0setuser();
+ return ret;
+}
+
+int
+ID0uu_unlock(const char *basettyname)
+{
+ int ret;
+
+ ID0set0();
+ ret = uu_unlock(basettyname);
+ log_Printf(LogID0, "%d = uu_unlock(\"%s\")\n", ret, basettyname);
+ ID0setuser();
+ return ret;
+}
+
+void
+ID0login(struct utmp *ut)
+{
+ ID0set0();
+ if (logout(ut->ut_line)) {
+ log_Printf(LogID0, "logout(\"%s\")\n", ut->ut_line);
+ logwtmp(ut->ut_line, "", "");
+ log_Printf(LogID0, "logwtmp(\"%s\", \"\", \"\")\n", ut->ut_line);
+ }
+ login(ut);
+ log_Printf(LogID0, "login(\"%s\", \"%.*s\")\n",
+ ut->ut_line, (int)(sizeof ut->ut_name), ut->ut_name);
+ ID0setuser();
+}
+
+void
+ID0logout(const char *device, int nologout)
+{
+ struct utmp ut;
+ char ut_line[sizeof ut.ut_line + 1];
+
+ strncpy(ut_line, device, sizeof ut_line - 1);
+ ut_line[sizeof ut_line - 1] = '\0';
+
+ ID0set0();
+ if (nologout || logout(ut_line)) {
+ log_Printf(LogID0, "logout(\"%s\")\n", ut_line);
+ logwtmp(ut_line, "", "");
+ log_Printf(LogID0, "logwtmp(\"%s\", \"\", \"\")\n", ut_line);
+ } else
+ log_Printf(LogERROR, "ID0logout: No longer logged in on %s\n", ut_line);
+ ID0setuser();
+}
+
+int
+ID0bind_un(int s, const struct sockaddr_un *name)
+{
+ int result;
+
+ ID0set0();
+ result = bind(s, (const struct sockaddr *)name, sizeof *name);
+ log_Printf(LogID0, "%d = bind(%d, \"%s\", %d)\n",
+ result, s, name->sun_path, (int)sizeof(*name));
+ ID0setuser();
+ return result;
+}
+
+int
+ID0connect_un(int s, const struct sockaddr_un *name)
+{
+ int result;
+
+ ID0set0();
+ result = connect(s, (const struct sockaddr *)name, sizeof *name);
+ log_Printf(LogID0, "%d = connect(%d, \"%s\", %d)\n",
+ result, s, name->sun_path, (int)sizeof(*name));
+ ID0setuser();
+ return result;
+}
+
+int
+ID0kill(pid_t pid, int sig)
+{
+ int result;
+
+ ID0set0();
+ result = kill(pid, sig);
+ log_Printf(LogID0, "%d = kill(%ld, %d)\n", result, (long)pid, sig);
+ ID0setuser();
+ return result;
+}
+
+#if defined(__FreeBSD__) && !defined(NOKLDLOAD)
+int
+ID0kldload(const char *dev)
+{
+ int result;
+
+ ID0set0();
+ result = kldload(dev);
+ log_Printf(LogID0, "%d = kldload(\"%s\")\n", result, dev);
+ ID0setuser();
+ return result;
+}
+#endif
+
+#ifndef NONETGRAPH
+int
+ID0NgMkSockNode(const char *name, int *cs, int *ds)
+{
+ int result;
+
+ ID0set0();
+ result = NgMkSockNode(name, cs, ds);
+ log_Printf(LogID0, "%d = NgMkSockNode(\"%s\", &cs, &ds)\n",
+ result, name ? name : "");
+ ID0setuser();
+ return result;
+}
+#endif
diff --git a/usr.sbin/ppp/id.h b/usr.sbin/ppp/id.h
new file mode 100644
index 0000000..409da49
--- /dev/null
+++ b/usr.sbin/ppp/id.h
@@ -0,0 +1,91 @@
+/*-
+ * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef NOSUID
+struct utmp;
+struct sockaddr_un;
+
+extern void ID0init(void);
+extern uid_t ID0realuid(void);
+extern int ID0ioctl(int, unsigned long, void *);
+extern int ID0unlink(const char *);
+extern int ID0socket(int, int, int);
+extern FILE *ID0fopen(const char *, const char *);
+extern int ID0open(const char *, int, ...);
+extern int ID0write(int, const void *, size_t);
+extern int ID0uu_lock(const char *);
+extern int ID0uu_lock_txfr(const char *, pid_t);
+extern int ID0uu_unlock(const char *);
+extern void ID0login(struct utmp *);
+extern void ID0logout(const char *, int);
+extern int ID0bind_un(int, const struct sockaddr_un *);
+extern int ID0connect_un(int, const struct sockaddr_un *);
+extern int ID0kill(pid_t, int);
+#if defined(__FreeBSD__) && !defined(NOKLDLOAD)
+extern int ID0kldload(const char *);
+#endif
+#ifndef NONETGRAPH
+extern int ID0NgMkSockNode(const char *, int *, int *);
+#endif
+#else /* NOSUID */
+#define ID0init()
+#define ID0realuid() (0)
+#define ID0ioctl ioctl
+#define ID0unlink unlink
+#define ID0socket socket
+#define ID0fopen fopen
+#define ID0open open
+#define ID0write write
+#define ID0uu_lock uu_lock
+#define ID0uu_lock_txfr uu_lock_txfr
+#define ID0uu_unlock uu_unlock
+#define ID0login(u) \
+ do { \
+ if (logout((u)->ut_line)) \
+ logwtmp((u)->ut_line, "", ""); \
+ login(u); \
+ } while (0)
+#define ID0logout(dev, no) \
+ do { \
+ struct utmp ut; \
+ strncpy(ut.ut_line, dev, sizeof ut.ut_line - 1); \
+ ut.ut_line[sizeof ut.ut_line - 1] = '\0'; \
+ if (no || logout(ut.ut_line)) \
+ logwtmp(ut.ut_line, "", ""); \
+ } while (0)
+#define ID0bind_un(s, n) bind(s, (const struct sockaddr *)(n), sizeof *(n))
+#define ID0connect_un(s, n) \
+ connect(s, (const struct sockaddr *)(n), sizeof *(n))
+#define ID0kill kill
+#if defined(__FreeBSD__) && !defined(NOKLDLOAD)
+#define ID0kldload kldload
+#endif
+#ifndef NONETGRAPH
+#define ID0NgMkSockNode NgMkSockNode
+#endif
+#endif
diff --git a/usr.sbin/ppp/iface.c b/usr.sbin/ppp/iface.c
new file mode 100644
index 0000000..922d1f0
--- /dev/null
+++ b/usr.sbin/ppp/iface.c
@@ -0,0 +1,715 @@
+/*-
+ * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#ifdef __FreeBSD__
+#include <net/if_var.h>
+#endif
+#include <net/route.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#ifndef NOINET6
+#include <netinet6/nd6.h>
+#endif
+#include <sys/un.h>
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <sys/sysctl.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "layer.h"
+#include "defs.h"
+#include "command.h"
+#include "mbuf.h"
+#include "log.h"
+#include "id.h"
+#include "timer.h"
+#include "fsm.h"
+#include "iplist.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "throughput.h"
+#include "slcompress.h"
+#include "descriptor.h"
+#include "ncpaddr.h"
+#include "ipcp.h"
+#include "filter.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "mp.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+#include "prompt.h"
+#include "iface.h"
+
+
+struct iface *
+iface_Create(const char *name)
+{
+ int mib[6], maxtries, err;
+ size_t needed, namelen;
+ char *buf, *ptr, *end;
+ struct if_msghdr *ifm;
+ struct ifa_msghdr *ifam;
+ struct sockaddr_dl *dl;
+ struct sockaddr *sa[RTAX_MAX];
+ struct iface *iface;
+ struct iface_addr *addr;
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = 0;
+ mib[4] = NET_RT_IFLIST;
+ mib[5] = 0;
+
+ maxtries = 20;
+ err = 0;
+ do {
+ if (maxtries-- == 0 || (err && err != ENOMEM)) {
+ fprintf(stderr, "iface_Create: sysctl: %s\n", strerror(err));
+ return NULL;
+ }
+
+ if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
+ fprintf(stderr, "iface_Create: sysctl: estimate: %s\n",
+ strerror(errno));
+ return NULL;
+ }
+
+ if ((buf = (char *)malloc(needed)) == NULL) {
+ fprintf(stderr, "iface_Create: malloc failed: %s\n", strerror(errno));
+ return NULL;
+ }
+
+ if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
+ err = errno;
+ free(buf);
+ buf = NULL;
+ }
+ } while (buf == NULL);
+
+ ptr = buf;
+ end = buf + needed;
+ iface = NULL;
+ namelen = strlen(name);
+
+ while (ptr < end && iface == NULL) {
+ ifm = (struct if_msghdr *)ptr; /* On if_msghdr */
+ if (ifm->ifm_type != RTM_IFINFO)
+ break;
+ dl = (struct sockaddr_dl *)(ifm + 1); /* Single _dl at end */
+ if (dl->sdl_nlen == namelen && !strncmp(name, dl->sdl_data, namelen)) {
+ iface = (struct iface *)malloc(sizeof *iface);
+ if (iface == NULL) {
+ fprintf(stderr, "iface_Create: malloc: %s\n", strerror(errno));
+ return NULL;
+ }
+ iface->name = strdup(name);
+ iface->index = ifm->ifm_index;
+ iface->flags = ifm->ifm_flags;
+ iface->mtu = 0;
+ iface->addrs = 0;
+ iface->addr = NULL;
+ }
+ ptr += ifm->ifm_msglen; /* First ifa_msghdr */
+ for (; ptr < end; ptr += ifam->ifam_msglen) {
+ ifam = (struct ifa_msghdr *)ptr; /* Next if address */
+
+ if (ifam->ifam_type != RTM_NEWADDR) /* finished this if */
+ break;
+
+ if (iface != NULL && ifam->ifam_addrs & RTA_IFA) {
+ /* Found a configured interface ! */
+ iface_ParseHdr(ifam, sa);
+
+ if (sa[RTAX_IFA] && (sa[RTAX_IFA]->sa_family == AF_INET
+#ifndef NOINET6
+ || sa[RTAX_IFA]->sa_family == AF_INET6
+#endif
+ )) {
+ /* Record the address */
+
+ addr = (struct iface_addr *)
+ realloc(iface->addr, (iface->addrs + 1) * sizeof iface->addr[0]);
+ if (addr == NULL)
+ break;
+ iface->addr = addr;
+
+ addr += iface->addrs;
+ iface->addrs++;
+
+ ncprange_setsa(&addr->ifa, sa[RTAX_IFA], sa[RTAX_NETMASK]);
+ if (sa[RTAX_BRD])
+ ncpaddr_setsa(&addr->peer, sa[RTAX_BRD]);
+ else
+ ncpaddr_init(&addr->peer);
+ }
+ }
+ }
+ }
+
+ free(buf);
+
+ return iface;
+}
+
+static int
+iface_addr_Zap(const char *name, struct iface_addr *addr, int s)
+{
+ struct ifaliasreq ifra;
+#ifndef NOINET6
+ struct in6_aliasreq ifra6;
+#endif
+ struct sockaddr_in *me4, *msk4, *peer4;
+ struct sockaddr_storage ssme, sspeer, ssmsk;
+ int res;
+
+ ncprange_getsa(&addr->ifa, &ssme, &ssmsk);
+ ncpaddr_getsa(&addr->peer, &sspeer);
+ res = 0;
+
+ switch (ncprange_family(&addr->ifa)) {
+ case AF_INET:
+ memset(&ifra, '\0', sizeof ifra);
+ strncpy(ifra.ifra_name, name, sizeof ifra.ifra_name - 1);
+
+ me4 = (struct sockaddr_in *)&ifra.ifra_addr;
+ memcpy(me4, &ssme, sizeof *me4);
+
+ msk4 = (struct sockaddr_in *)&ifra.ifra_mask;
+ memcpy(msk4, &ssmsk, sizeof *msk4);
+
+ peer4 = (struct sockaddr_in *)&ifra.ifra_broadaddr;
+ if (ncpaddr_family(&addr->peer) == AF_UNSPEC) {
+ peer4->sin_family = AF_INET;
+ peer4->sin_len = sizeof(*peer4);
+ peer4->sin_addr.s_addr = INADDR_NONE;
+ } else
+ memcpy(peer4, &sspeer, sizeof *peer4);
+
+ res = ID0ioctl(s, SIOCDIFADDR, &ifra);
+ if (log_IsKept(LogDEBUG)) {
+ char buf[100];
+
+ snprintf(buf, sizeof buf, "%s", ncprange_ntoa(&addr->ifa));
+ log_Printf(LogWARN, "%s: DIFADDR %s -> %s returns %d\n",
+ ifra.ifra_name, buf, ncpaddr_ntoa(&addr->peer), res);
+ }
+ break;
+
+#ifndef NOINET6
+ case AF_INET6:
+ memset(&ifra6, '\0', sizeof ifra6);
+ strncpy(ifra6.ifra_name, name, sizeof ifra6.ifra_name - 1);
+
+ memcpy(&ifra6.ifra_addr, &ssme, sizeof ifra6.ifra_addr);
+ memcpy(&ifra6.ifra_prefixmask, &ssmsk, sizeof ifra6.ifra_prefixmask);
+ ifra6.ifra_prefixmask.sin6_family = AF_UNSPEC;
+ if (ncpaddr_family(&addr->peer) == AF_UNSPEC)
+ ifra6.ifra_dstaddr.sin6_family = AF_UNSPEC;
+ else
+ memcpy(&ifra6.ifra_dstaddr, &sspeer, sizeof ifra6.ifra_dstaddr);
+ ifra6.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
+ ifra6.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
+
+ res = ID0ioctl(s, SIOCDIFADDR_IN6, &ifra6);
+ break;
+#endif
+ }
+
+ if (res == -1) {
+ char dst[40];
+ const char *end =
+#ifndef NOINET6
+ ncprange_family(&addr->ifa) == AF_INET6 ? "_IN6" :
+#endif
+ "";
+
+ if (ncpaddr_family(&addr->peer) == AF_UNSPEC)
+ log_Printf(LogWARN, "iface rm: ioctl(SIOCDIFADDR%s, %s): %s\n",
+ end, ncprange_ntoa(&addr->ifa), strerror(errno));
+ else {
+ snprintf(dst, sizeof dst, "%s", ncpaddr_ntoa(&addr->peer));
+ log_Printf(LogWARN, "iface rm: ioctl(SIOCDIFADDR%s, %s -> %s): %s\n",
+ end, ncprange_ntoa(&addr->ifa), dst, strerror(errno));
+ }
+ }
+
+ return res != -1;
+}
+
+static int
+iface_addr_Add(const char *name, struct iface_addr *addr, int s)
+{
+ struct ifaliasreq ifra;
+#ifndef NOINET6
+ struct in6_aliasreq ifra6;
+#endif
+ struct sockaddr_in *me4, *msk4, *peer4;
+ struct sockaddr_storage ssme, sspeer, ssmsk;
+ int res;
+
+ ncprange_getsa(&addr->ifa, &ssme, &ssmsk);
+ ncpaddr_getsa(&addr->peer, &sspeer);
+ res = 0;
+
+ switch (ncprange_family(&addr->ifa)) {
+ case AF_INET:
+ memset(&ifra, '\0', sizeof ifra);
+ strncpy(ifra.ifra_name, name, sizeof ifra.ifra_name - 1);
+
+ me4 = (struct sockaddr_in *)&ifra.ifra_addr;
+ memcpy(me4, &ssme, sizeof *me4);
+
+ msk4 = (struct sockaddr_in *)&ifra.ifra_mask;
+ memcpy(msk4, &ssmsk, sizeof *msk4);
+
+ peer4 = (struct sockaddr_in *)&ifra.ifra_broadaddr;
+ if (ncpaddr_family(&addr->peer) == AF_UNSPEC) {
+ peer4->sin_family = AF_INET;
+ peer4->sin_len = sizeof(*peer4);
+ peer4->sin_addr.s_addr = INADDR_NONE;
+ } else
+ memcpy(peer4, &sspeer, sizeof *peer4);
+
+ res = ID0ioctl(s, SIOCAIFADDR, &ifra);
+ if (log_IsKept(LogDEBUG)) {
+ char buf[100];
+
+ snprintf(buf, sizeof buf, "%s", ncprange_ntoa(&addr->ifa));
+ log_Printf(LogWARN, "%s: AIFADDR %s -> %s returns %d\n",
+ ifra.ifra_name, buf, ncpaddr_ntoa(&addr->peer), res);
+ }
+ break;
+
+#ifndef NOINET6
+ case AF_INET6:
+ memset(&ifra6, '\0', sizeof ifra6);
+ strncpy(ifra6.ifra_name, name, sizeof ifra6.ifra_name - 1);
+
+ memcpy(&ifra6.ifra_addr, &ssme, sizeof ifra6.ifra_addr);
+ memcpy(&ifra6.ifra_prefixmask, &ssmsk, sizeof ifra6.ifra_prefixmask);
+ if (ncpaddr_family(&addr->peer) == AF_UNSPEC)
+ ifra6.ifra_dstaddr.sin6_family = AF_UNSPEC;
+ else
+ memcpy(&ifra6.ifra_dstaddr, &sspeer, sizeof ifra6.ifra_dstaddr);
+ ifra6.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
+ ifra6.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
+
+ res = ID0ioctl(s, SIOCAIFADDR_IN6, &ifra6);
+ break;
+#endif
+ }
+
+ if (res == -1) {
+ char dst[40];
+ const char *end =
+#ifndef NOINET6
+ ncprange_family(&addr->ifa) == AF_INET6 ? "_IN6" :
+#endif
+ "";
+
+ if (ncpaddr_family(&addr->peer) == AF_UNSPEC)
+ log_Printf(LogWARN, "iface add: ioctl(SIOCAIFADDR%s, %s): %s\n",
+ end, ncprange_ntoa(&addr->ifa), strerror(errno));
+ else {
+ snprintf(dst, sizeof dst, "%s", ncpaddr_ntoa(&addr->peer));
+ log_Printf(LogWARN, "iface add: ioctl(SIOCAIFADDR%s, %s -> %s): %s\n",
+ end, ncprange_ntoa(&addr->ifa), dst, strerror(errno));
+ }
+ }
+
+ return res != -1;
+}
+
+
+void
+iface_Clear(struct iface *iface, struct ncp *ncp, int family, int how)
+{
+ int addrs, af, inskip, in6skip, n, s4 = -1, s6 = -1, *s;
+
+ if (iface->addrs) {
+ inskip = in6skip = how == IFACE_CLEAR_ALL ? 0 : 1;
+ addrs = 0;
+
+ for (n = 0; n < iface->addrs; n++) {
+ af = ncprange_family(&iface->addr[n].ifa);
+ if (family == 0 || family == af) {
+ if (!iface->addr[n].system && (how & IFACE_SYSTEM))
+ continue;
+ switch (af) {
+ case AF_INET:
+ if (inskip) {
+ inskip = 0;
+ continue;
+ }
+ s = &s4;
+ break;
+
+#ifndef NOINET6
+ case AF_INET6:
+ if (in6skip) {
+ in6skip = 0;
+ continue;
+ }
+ s = &s6;
+ break;
+#endif
+ default:
+ continue;
+ }
+
+ if (*s == -1 && (*s = ID0socket(af, SOCK_DGRAM, 0)) == -1)
+ log_Printf(LogERROR, "iface_Clear: socket(): %s\n", strerror(errno));
+ else if (iface_addr_Zap(iface->name, iface->addr + n, *s)) {
+ ncp_IfaceAddrDeleted(ncp, iface->addr + n);
+ bcopy(iface->addr + n + 1, iface->addr + n,
+ (iface->addrs - n - 1) * sizeof *iface->addr);
+ iface->addrs--;
+ n--;
+ }
+ }
+ }
+
+ /* Don't bother realloc()ing - we have little to gain */
+
+ if (s4)
+ close(s4);
+ if (s6)
+ close(s6);
+ }
+}
+
+int
+iface_Add(struct iface *iface, struct ncp *ncp, const struct ncprange *ifa,
+ const struct ncpaddr *peer, int how)
+{
+ int af, n, removed, s, width;
+ struct ncpaddr ncplocal;
+ struct iface_addr *addr, newaddr;
+
+ af = ncprange_family(ifa);
+ if ((s = ID0socket(af, SOCK_DGRAM, 0)) == -1) {
+ log_Printf(LogERROR, "iface_Add: socket(): %s\n", strerror(errno));
+ return 0;
+ }
+ ncprange_getaddr(ifa, &ncplocal);
+
+ for (n = 0; n < iface->addrs; n++) {
+ if (ncprange_contains(&iface->addr[n].ifa, &ncplocal) ||
+ ncpaddr_equal(&iface->addr[n].peer, peer)) {
+ /* Replace this sockaddr */
+ if (!(how & IFACE_FORCE_ADD)) {
+ close(s);
+ return 0; /* errno = EEXIST; */
+ }
+
+ if (ncprange_equal(&iface->addr[n].ifa, ifa) &&
+ ncpaddr_equal(&iface->addr[n].peer, peer)) {
+ close(s);
+ return 1; /* Already there */
+ }
+
+ width =
+#ifndef NOINET6
+ (af == AF_INET6) ? 128 :
+#endif
+ 32;
+ removed = iface_addr_Zap(iface->name, iface->addr + n, s);
+ if (removed)
+ ncp_IfaceAddrDeleted(ncp, iface->addr + n);
+ ncprange_copy(&iface->addr[n].ifa, ifa);
+ ncpaddr_copy(&iface->addr[n].peer, peer);
+ if (!iface_addr_Add(iface->name, iface->addr + n, s)) {
+ if (removed) {
+ bcopy(iface->addr + n + 1, iface->addr + n,
+ (iface->addrs - n - 1) * sizeof *iface->addr);
+ iface->addrs--;
+ n--;
+ }
+ close(s);
+ return 0;
+ }
+ close(s);
+ ncp_IfaceAddrAdded(ncp, iface->addr + n);
+ return 1;
+ }
+ }
+
+ addr = (struct iface_addr *)realloc
+ (iface->addr, (iface->addrs + 1) * sizeof iface->addr[0]);
+ if (addr == NULL) {
+ log_Printf(LogERROR, "iface_inAdd: realloc: %s\n", strerror(errno));
+ close(s);
+ return 0;
+ }
+ iface->addr = addr;
+
+ ncprange_copy(&newaddr.ifa, ifa);
+ ncpaddr_copy(&newaddr.peer, peer);
+ newaddr.system = !!(how & IFACE_SYSTEM);
+ if (!iface_addr_Add(iface->name, &newaddr, s)) {
+ close(s);
+ return 0;
+ }
+
+ if (how & IFACE_ADD_FIRST) {
+ /* Stuff it at the start of our list */
+ n = 0;
+ bcopy(iface->addr, iface->addr + 1, iface->addrs * sizeof *iface->addr);
+ } else
+ n = iface->addrs;
+
+ iface->addrs++;
+ memcpy(iface->addr + n, &newaddr, sizeof(*iface->addr));
+
+ close(s);
+ ncp_IfaceAddrAdded(ncp, iface->addr + n);
+
+ return 1;
+}
+
+int
+iface_Delete(struct iface *iface, struct ncp *ncp, const struct ncpaddr *del)
+{
+ struct ncpaddr found;
+ int n, res, s;
+
+ if ((s = ID0socket(ncpaddr_family(del), SOCK_DGRAM, 0)) == -1) {
+ log_Printf(LogERROR, "iface_Delete: socket(): %s\n", strerror(errno));
+ return 0;
+ }
+
+ for (n = res = 0; n < iface->addrs; n++) {
+ ncprange_getaddr(&iface->addr[n].ifa, &found);
+ if (ncpaddr_equal(&found, del)) {
+ if (iface_addr_Zap(iface->name, iface->addr + n, s)) {
+ ncp_IfaceAddrDeleted(ncp, iface->addr + n);
+ bcopy(iface->addr + n + 1, iface->addr + n,
+ (iface->addrs - n - 1) * sizeof *iface->addr);
+ iface->addrs--;
+ res = 1;
+ }
+ break;
+ }
+ }
+
+ close(s);
+
+ return res;
+}
+
+#define IFACE_ADDFLAGS 1
+#define IFACE_DELFLAGS 2
+
+static int
+iface_ChangeFlags(const char *ifname, int flags, int how)
+{
+ struct ifreq ifrq;
+ int s;
+
+ s = ID0socket(PF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ log_Printf(LogERROR, "iface_ChangeFlags: socket: %s\n", strerror(errno));
+ return 0;
+ }
+
+ memset(&ifrq, '\0', sizeof ifrq);
+ strncpy(ifrq.ifr_name, ifname, sizeof ifrq.ifr_name - 1);
+ ifrq.ifr_name[sizeof ifrq.ifr_name - 1] = '\0';
+ if (ID0ioctl(s, SIOCGIFFLAGS, &ifrq) < 0) {
+ log_Printf(LogERROR, "iface_ChangeFlags: ioctl(SIOCGIFFLAGS): %s\n",
+ strerror(errno));
+ close(s);
+ return 0;
+ }
+
+ if (how == IFACE_ADDFLAGS)
+ ifrq.ifr_flags |= flags;
+ else
+ ifrq.ifr_flags &= ~flags;
+
+ if (ID0ioctl(s, SIOCSIFFLAGS, &ifrq) < 0) {
+ log_Printf(LogERROR, "iface_ChangeFlags: ioctl(SIOCSIFFLAGS): %s\n",
+ strerror(errno));
+ close(s);
+ return 0;
+ }
+ close(s);
+
+ return 1; /* Success */
+}
+
+int
+iface_SetFlags(const char *ifname, int flags)
+{
+ return iface_ChangeFlags(ifname, flags, IFACE_ADDFLAGS);
+}
+
+int
+iface_ClearFlags(const char *ifname, int flags)
+{
+ return iface_ChangeFlags(ifname, flags, IFACE_DELFLAGS);
+}
+
+void
+iface_Destroy(struct iface *iface)
+{
+ /*
+ * iface_Clear(iface, IFACE_CLEAR_ALL) must be called manually
+ * if that's what the user wants. It's better to leave the interface
+ * allocated so that existing connections can continue to work.
+ */
+
+ if (iface != NULL) {
+ free(iface->name);
+ free(iface->addr);
+ free(iface);
+ }
+}
+
+#define if_entry(x) { IFF_##x, #x }
+
+struct {
+ int flag;
+ const char *value;
+} if_flags[] = {
+ if_entry(UP),
+ if_entry(BROADCAST),
+ if_entry(DEBUG),
+ if_entry(LOOPBACK),
+ if_entry(POINTOPOINT),
+ if_entry(RUNNING),
+ if_entry(NOARP),
+ if_entry(PROMISC),
+ if_entry(ALLMULTI),
+ if_entry(OACTIVE),
+ if_entry(SIMPLEX),
+ if_entry(LINK0),
+ if_entry(LINK1),
+ if_entry(LINK2),
+ if_entry(MULTICAST),
+ { 0, "???" }
+};
+
+int
+iface_Show(struct cmdargs const *arg)
+{
+ struct ncpaddr ncpaddr;
+ struct iface *iface = arg->bundle->iface, *current;
+ int f, flags;
+#ifndef NOINET6
+ int scopeid, width;
+#endif
+ struct in_addr mask;
+
+ current = iface_Create(iface->name);
+ flags = iface->flags = current->flags;
+ iface_Destroy(current);
+
+ prompt_Printf(arg->prompt, "%s (idx %d) <", iface->name, iface->index);
+ for (f = 0; f < sizeof if_flags / sizeof if_flags[0]; f++)
+ if ((if_flags[f].flag & flags)) {
+ prompt_Printf(arg->prompt, "%s%s", flags == iface->flags ? "" : ",",
+ if_flags[f].value);
+ flags &= ~if_flags[f].flag;
+ }
+
+#if 0
+ if (flags)
+ prompt_Printf(arg->prompt, "%s0x%x", flags == iface->flags ? "" : ",",
+ flags);
+#endif
+
+ prompt_Printf(arg->prompt, "> mtu %d has %d address%s:\n", iface->mtu,
+ iface->addrs, iface->addrs == 1 ? "" : "es");
+
+ for (f = 0; f < iface->addrs; f++) {
+ ncprange_getaddr(&iface->addr[f].ifa, &ncpaddr);
+ switch (ncprange_family(&iface->addr[f].ifa)) {
+ case AF_INET:
+ prompt_Printf(arg->prompt, " inet %s --> ", ncpaddr_ntoa(&ncpaddr));
+ if (ncpaddr_family(&iface->addr[f].peer) == AF_UNSPEC)
+ prompt_Printf(arg->prompt, "255.255.255.255");
+ else
+ prompt_Printf(arg->prompt, "%s", ncpaddr_ntoa(&iface->addr[f].peer));
+ ncprange_getip4mask(&iface->addr[f].ifa, &mask);
+ prompt_Printf(arg->prompt, " netmask 0x%08lx", (long)ntohl(mask.s_addr));
+ break;
+
+#ifndef NOINET6
+ case AF_INET6:
+ prompt_Printf(arg->prompt, " inet6 %s", ncpaddr_ntoa(&ncpaddr));
+ if (ncpaddr_family(&iface->addr[f].peer) != AF_UNSPEC)
+ prompt_Printf(arg->prompt, " --> %s",
+ ncpaddr_ntoa(&iface->addr[f].peer));
+ ncprange_getwidth(&iface->addr[f].ifa, &width);
+ if (ncpaddr_family(&iface->addr[f].peer) == AF_UNSPEC)
+ prompt_Printf(arg->prompt, " prefixlen %d", width);
+ if ((scopeid = ncprange_scopeid(&iface->addr[f].ifa)) != -1)
+ prompt_Printf(arg->prompt, " scopeid 0x%x", (unsigned)scopeid);
+ break;
+#endif
+ }
+ prompt_Printf(arg->prompt, "\n");
+ }
+
+ return 0;
+}
+
+void
+iface_ParseHdr(struct ifa_msghdr *ifam, struct sockaddr *sa[RTAX_MAX])
+{
+ char *wp;
+ int rtax;
+
+ wp = (char *)(ifam + 1);
+
+ for (rtax = 0; rtax < RTAX_MAX; rtax++)
+ if (ifam->ifam_addrs & (1 << rtax)) {
+ sa[rtax] = (struct sockaddr *)wp;
+ wp += ROUNDUP(sa[rtax]->sa_len);
+ } else
+ sa[rtax] = NULL;
+}
diff --git a/usr.sbin/ppp/iface.h b/usr.sbin/ppp/iface.h
new file mode 100644
index 0000000..28c3761
--- /dev/null
+++ b/usr.sbin/ppp/iface.h
@@ -0,0 +1,65 @@
+/*-
+ * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct ifa_msghdr;
+
+struct iface_addr {
+ unsigned system : 1; /* System alias ? */
+ struct ncprange ifa; /* local address/mask */
+ struct ncpaddr peer; /* peer address */
+};
+
+struct iface {
+ char *name; /* Interface name (malloc'd) */
+ int index; /* Interface index */
+ int flags; /* Interface flags (IFF_*) */
+ int mtu; /* struct tuninfo MTU */
+
+ int addrs; /* How many in_addr's */
+ struct iface_addr *addr; /* Array of addresses (malloc'd) */
+};
+
+#define IFACE_CLEAR_ALL 0 /* Nuke 'em all */
+#define IFACE_CLEAR_ALIASES 1 /* Leave the NCP address */
+
+#define IFACE_ADD_LAST 0 /* Just another alias */
+#define IFACE_ADD_FIRST 1 /* The IPCP address */
+#define IFACE_FORCE_ADD 2 /* OR'd with IFACE_ADD_{FIRST,LAST} */
+
+#define IFACE_SYSTEM 4 /* Set/clear SYSTEM entries */
+
+extern struct iface *iface_Create(const char *name);
+extern void iface_Clear(struct iface *, struct ncp *, int, int);
+extern int iface_Add(struct iface *, struct ncp *, const struct ncprange *,
+ const struct ncpaddr *, int);
+extern int iface_Delete(struct iface *, struct ncp *, const struct ncpaddr *);
+extern int iface_Show(struct cmdargs const *);
+extern int iface_SetFlags(const char *, int);
+extern int iface_ClearFlags(const char *, int);
+extern void iface_Destroy(struct iface *);
+extern void iface_ParseHdr(struct ifa_msghdr *, struct sockaddr *[RTAX_MAX]);
diff --git a/usr.sbin/ppp/ip.c b/usr.sbin/ppp/ip.c
new file mode 100644
index 0000000..3c0d54b
--- /dev/null
+++ b/usr.sbin/ppp/ip.c
@@ -0,0 +1,955 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#ifndef NOINET6
+#include <netinet/icmp6.h>
+#include <netinet/ip6.h>
+#endif
+#include <netinet/ip_icmp.h>
+#include <netinet/udp.h>
+#include <netinet/tcp.h>
+#include <sys/un.h>
+
+#include <errno.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "layer.h"
+#include "proto.h"
+#include "mbuf.h"
+#include "log.h"
+#include "defs.h"
+#include "timer.h"
+#include "fsm.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "throughput.h"
+#include "iplist.h"
+#include "slcompress.h"
+#include "ncpaddr.h"
+#include "ip.h"
+#include "ipcp.h"
+#include "filter.h"
+#include "descriptor.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "mp.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+#include "tun.h"
+
+
+#define OPCODE_QUERY 0
+#define OPCODE_IQUERY 1
+#define OPCODE_STATUS 2
+
+struct dns_header {
+ u_short id;
+ unsigned qr : 1;
+ unsigned opcode : 4;
+ unsigned aa : 1;
+ unsigned tc : 1;
+ unsigned rd : 1;
+ unsigned ra : 1;
+ unsigned z : 3;
+ unsigned rcode : 4;
+ u_short qdcount;
+ u_short ancount;
+ u_short nscount;
+ u_short arcount;
+};
+
+static const char *
+dns_Qclass2Txt(u_short qclass)
+{
+ static char failure[6];
+ struct {
+ u_short id;
+ const char *txt;
+ } qtxt[] = {
+ /* rfc1035 */
+ { 1, "IN" }, { 2, "CS" }, { 3, "CH" }, { 4, "HS" }, { 255, "*" }
+ };
+ int f;
+
+ for (f = 0; f < sizeof qtxt / sizeof *qtxt; f++)
+ if (qtxt[f].id == qclass)
+ return qtxt[f].txt;
+
+ return HexStr(qclass, failure, sizeof failure);
+}
+
+static const char *
+dns_Qtype2Txt(u_short qtype)
+{
+ static char failure[6];
+ struct {
+ u_short id;
+ const char *txt;
+ } qtxt[] = {
+ /* rfc1035/rfc1700 */
+ { 1, "A" }, { 2, "NS" }, { 3, "MD" }, { 4, "MF" }, { 5, "CNAME" },
+ { 6, "SOA" }, { 7, "MB" }, { 8, "MG" }, { 9, "MR" }, { 10, "NULL" },
+ { 11, "WKS" }, { 12, "PTR" }, { 13, "HINFO" }, { 14, "MINFO" },
+ { 15, "MX" }, { 16, "TXT" }, { 17, "RP" }, { 18, "AFSDB" },
+ { 19, "X25" }, { 20, "ISDN" }, { 21, "RT" }, { 22, "NSAP" },
+ { 23, "NSAP-PTR" }, { 24, "SIG" }, { 25, "KEY" }, { 26, "PX" },
+ { 27, "GPOS" }, { 28, "AAAA" }, { 252, "AXFR" }, { 253, "MAILB" },
+ { 254, "MAILA" }, { 255, "*" }
+ };
+ int f;
+
+ for (f = 0; f < sizeof qtxt / sizeof *qtxt; f++)
+ if (qtxt[f].id == qtype)
+ return qtxt[f].txt;
+
+ return HexStr(qtype, failure, sizeof failure);
+}
+
+static __inline int
+PortMatch(int op, u_short pport, u_short rport)
+{
+ switch (op) {
+ case OP_EQ:
+ return pport == rport;
+ case OP_GT:
+ return pport > rport;
+ case OP_LT:
+ return pport < rport;
+ default:
+ return 0;
+ }
+}
+
+/*
+ * Check a packet against the given filter
+ * Returns 0 to accept the packet, non-zero to drop the packet.
+ * If psecs is not NULL, populate it with the timeout associated
+ * with the filter rule matched.
+ *
+ * If filtering is enabled, the initial fragment of a datagram must
+ * contain the complete protocol header, and subsequent fragments
+ * must not attempt to over-write it.
+ *
+ * One (and only one) of pip or pip6 must be set.
+ */
+int
+FilterCheck(const unsigned char *packet, u_int32_t family,
+ const struct filter *filter, unsigned *psecs)
+{
+ int gotinfo; /* true if IP payload decoded */
+ int cproto; /* IPPROTO_* protocol number if (gotinfo) */
+ int estab, syn, finrst; /* TCP state flags if (gotinfo) */
+ u_short sport, dport; /* src, dest port from packet if (gotinfo) */
+ int n; /* filter rule to process */
+ int len; /* bytes used in dbuff */
+ int didname; /* true if filter header printed */
+ int match; /* true if condition matched */
+ int mindata; /* minimum data size or zero */
+ const struct filterent *fp = filter->rule;
+ char dbuff[100], dstip[16], prototxt[16];
+ struct protoent *pe;
+ struct ncpaddr srcaddr, dstaddr;
+ const char *payload; /* IP payload */
+ int datalen; /* IP datagram length */
+
+ if (fp->f_action == A_NONE)
+ return 0; /* No rule is given. Permit this packet */
+
+#ifndef NOINET6
+ if (family == AF_INET6) {
+ const struct ip6_hdr *pip6 = (const struct ip6_hdr *)packet;
+
+ ncpaddr_setip6(&srcaddr, &pip6->ip6_src);
+ ncpaddr_setip6(&dstaddr, &pip6->ip6_dst);
+ datalen = ntohs(pip6->ip6_plen);
+ payload = packet + sizeof *pip6;
+ cproto = pip6->ip6_nxt;
+ } else
+#endif
+ {
+ /*
+ * Deny any packet fragment that tries to over-write the header.
+ * Since we no longer have the real header available, punt on the
+ * largest normal header - 20 bytes for TCP without options, rounded
+ * up to the next possible fragment boundary. Since the smallest
+ * `legal' MTU is 576, and the smallest recommended MTU is 296, any
+ * fragmentation within this range is dubious at best
+ */
+ const struct ip *pip = (const struct ip *)packet;
+
+ len = ntohs(pip->ip_off) & IP_OFFMASK; /* fragment offset */
+ if (len > 0) { /* Not first fragment within datagram */
+ if (len < (24 >> 3)) { /* don't allow fragment to over-write header */
+ log_Printf(LogFILTER, " error: illegal header\n");
+ return 1;
+ }
+ /* permit fragments on in and out filter */
+ if (!filter->fragok) {
+ log_Printf(LogFILTER, " error: illegal fragmentation\n");
+ return 1;
+ } else
+ return 0;
+ }
+
+ ncpaddr_setip4(&srcaddr, pip->ip_src);
+ ncpaddr_setip4(&dstaddr, pip->ip_dst);
+ datalen = ntohs(pip->ip_len) - (pip->ip_hl << 2);
+ payload = packet + (pip->ip_hl << 2);
+ cproto = pip->ip_p;
+ }
+
+ if ((pe = getprotobynumber(cproto)) == NULL)
+ snprintf(prototxt, sizeof prototxt, "%d", cproto);
+ else
+ snprintf(prototxt, sizeof prototxt, "%s", pe->p_name);
+ gotinfo = estab = syn = finrst = didname = 0;
+ sport = dport = 0;
+
+ for (n = 0; n < MAXFILTERS; ) {
+ if (fp->f_action == A_NONE) {
+ n++;
+ fp++;
+ continue;
+ }
+
+ if (!didname) {
+ log_Printf(LogDEBUG, "%s filter:\n", filter->name);
+ didname = 1;
+ }
+
+ match = 0;
+
+ if ((ncprange_family(&fp->f_src) == AF_UNSPEC ||
+ ncprange_contains(&fp->f_src, &srcaddr)) &&
+ (ncprange_family(&fp->f_dst) == AF_UNSPEC ||
+ ncprange_contains(&fp->f_dst, &dstaddr))) {
+ if (fp->f_proto != 0) {
+ if (!gotinfo) {
+ const struct tcphdr *th;
+ const struct udphdr *uh;
+ const struct icmp *ih;
+#ifndef NOINET6
+ const struct icmp6_hdr *ih6;
+#endif
+ mindata = 0;
+ sport = dport = 0;
+ estab = syn = finrst = -1;
+
+ switch (cproto) {
+ case IPPROTO_ICMP:
+ mindata = 8; /* ICMP must be at least 8 octets */
+ ih = (const struct icmp *)payload;
+ sport = ntohs(ih->icmp_type);
+ if (log_IsKept(LogDEBUG))
+ snprintf(dbuff, sizeof dbuff, "sport = %d", sport);
+ break;
+
+#ifndef NOINET6
+ case IPPROTO_ICMPV6:
+ mindata = 8; /* ICMP must be at least 8 octets */
+ ih6 = (const struct icmp6_hdr *)payload;
+ sport = ntohs(ih6->icmp6_type);
+ if (log_IsKept(LogDEBUG))
+ snprintf(dbuff, sizeof dbuff, "sport = %d", sport);
+ break;
+#endif
+
+ case IPPROTO_IGMP:
+ mindata = 8; /* IGMP uses 8-octet messages */
+ break;
+
+#ifdef IPPROTO_GRE
+ case IPPROTO_GRE:
+ mindata = 2; /* GRE uses 2-octet+ messages */
+ break;
+#endif
+#ifdef IPPROTO_OSPFIGP
+ case IPPROTO_OSPFIGP:
+ mindata = 8; /* IGMP uses 8-octet messages */
+ break;
+#endif
+#ifndef NOINET6
+ case IPPROTO_IPV6:
+ mindata = 20; /* RFC2893 Section 3.5: 5 * 32bit words */
+ break;
+#endif
+
+ case IPPROTO_UDP:
+ mindata = 8; /* UDP header is 8 octets */
+ uh = (const struct udphdr *)payload;
+ sport = ntohs(uh->uh_sport);
+ dport = ntohs(uh->uh_dport);
+ if (log_IsKept(LogDEBUG))
+ snprintf(dbuff, sizeof dbuff, "sport = %d, dport = %d",
+ sport, dport);
+ break;
+
+ case IPPROTO_TCP:
+ th = (const struct tcphdr *)payload;
+ /*
+ * TCP headers are variable length. The following code
+ * ensures that the TCP header length isn't de-referenced if
+ * the datagram is too short
+ */
+ if (datalen < 20 || datalen < (th->th_off << 2)) {
+ log_Printf(LogFILTER, " error: TCP header incorrect\n");
+ return 1;
+ }
+ sport = ntohs(th->th_sport);
+ dport = ntohs(th->th_dport);
+ estab = (th->th_flags & TH_ACK);
+ syn = (th->th_flags & TH_SYN);
+ finrst = (th->th_flags & (TH_FIN|TH_RST));
+ if (log_IsKept(LogDEBUG)) {
+ if (!estab)
+ snprintf(dbuff, sizeof dbuff,
+ "flags = %02x, sport = %d, dport = %d",
+ th->th_flags, sport, dport);
+ else
+ *dbuff = '\0';
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (datalen < mindata) {
+ log_Printf(LogFILTER, " error: proto %s must be at least"
+ " %d octets\n", prototxt, mindata);
+ return 1;
+ }
+
+ if (log_IsKept(LogDEBUG)) {
+ if (estab != -1) {
+ len = strlen(dbuff);
+ snprintf(dbuff + len, sizeof dbuff - len,
+ ", estab = %d, syn = %d, finrst = %d",
+ estab, syn, finrst);
+ }
+ log_Printf(LogDEBUG, " Filter: proto = %s, %s\n", prototxt, dbuff);
+ }
+ gotinfo = 1;
+ }
+
+ if (log_IsKept(LogDEBUG)) {
+ if (fp->f_srcop != OP_NONE) {
+ snprintf(dbuff, sizeof dbuff, ", src %s %d",
+ filter_Op2Nam(fp->f_srcop), fp->f_srcport);
+ len = strlen(dbuff);
+ } else
+ len = 0;
+ if (fp->f_dstop != OP_NONE) {
+ snprintf(dbuff + len, sizeof dbuff - len,
+ ", dst %s %d", filter_Op2Nam(fp->f_dstop),
+ fp->f_dstport);
+ } else if (!len)
+ *dbuff = '\0';
+
+ log_Printf(LogDEBUG, " rule = %d: Address match, "
+ "check against proto %d%s, action = %s\n",
+ n, fp->f_proto, dbuff, filter_Action2Nam(fp->f_action));
+ }
+
+ if (cproto == fp->f_proto) {
+ if ((fp->f_srcop == OP_NONE ||
+ PortMatch(fp->f_srcop, sport, fp->f_srcport)) &&
+ (fp->f_dstop == OP_NONE ||
+ PortMatch(fp->f_dstop, dport, fp->f_dstport)) &&
+ (fp->f_estab == 0 || estab) &&
+ (fp->f_syn == 0 || syn) &&
+ (fp->f_finrst == 0 || finrst)) {
+ match = 1;
+ }
+ }
+ } else {
+ /* Address is matched and no protocol specified. Make a decision. */
+ log_Printf(LogDEBUG, " rule = %d: Address match, action = %s\n", n,
+ filter_Action2Nam(fp->f_action));
+ match = 1;
+ }
+ } else
+ log_Printf(LogDEBUG, " rule = %d: Address mismatch\n", n);
+
+ if (match != fp->f_invert) {
+ /* Take specified action */
+ if (fp->f_action < A_NONE)
+ fp = &filter->rule[n = fp->f_action];
+ else {
+ if (fp->f_action == A_PERMIT) {
+ if (psecs != NULL)
+ *psecs = fp->timeout;
+ if (strcmp(filter->name, "DIAL") == 0) {
+ /* If dial filter then even print out accept packets */
+ if (log_IsKept(LogFILTER)) {
+ snprintf(dstip, sizeof dstip, "%s", ncpaddr_ntoa(&dstaddr));
+ log_Printf(LogFILTER, "%sbound rule = %d accept %s "
+ "src = %s:%d dst = %s:%d\n", filter->name, n, prototxt,
+ ncpaddr_ntoa(&srcaddr), sport, dstip, dport);
+ }
+ }
+ return 0;
+ } else {
+ if (log_IsKept(LogFILTER)) {
+ snprintf(dstip, sizeof dstip, "%s", ncpaddr_ntoa(&dstaddr));
+ log_Printf(LogFILTER,
+ "%sbound rule = %d deny %s src = %s/%d dst = %s/%d\n",
+ filter->name, n, prototxt,
+ ncpaddr_ntoa(&srcaddr), sport, dstip, dport);
+ }
+ return 1;
+ } /* Explict match. Deny this packet */
+ }
+ } else {
+ n++;
+ fp++;
+ }
+ }
+
+ if (log_IsKept(LogFILTER)) {
+ snprintf(dstip, sizeof dstip, "%s", ncpaddr_ntoa(&dstaddr));
+ log_Printf(LogFILTER,
+ "%sbound rule = implicit deny %s src = %s/%d dst = %s/%d\n",
+ filter->name, prototxt, ncpaddr_ntoa(&srcaddr), sport,
+ dstip, dport);
+ }
+
+ return 1; /* No rule matched, deny this packet */
+}
+
+static void
+ip_LogDNS(const struct udphdr *uh, const char *direction)
+{
+ struct dns_header header;
+ const u_short *pktptr;
+ const u_char *ptr;
+ u_short *hptr, tmp;
+ int len;
+
+ ptr = (const char *)uh + sizeof *uh;
+ len = ntohs(uh->uh_ulen) - sizeof *uh;
+ if (len < sizeof header + 5) /* rfc1024 */
+ return;
+
+ pktptr = (const u_short *)ptr;
+ hptr = (u_short *)&header;
+ ptr += sizeof header;
+ len -= sizeof header;
+
+ while (pktptr < (const u_short *)ptr) {
+ *hptr++ = ntohs(*pktptr); /* Careful of macro side-effects ! */
+ pktptr++;
+ }
+
+ if (header.opcode == OPCODE_QUERY && header.qr == 0) {
+ /* rfc1035 */
+ char namewithdot[MAXHOSTNAMELEN + 1], *n;
+ const char *qtype, *qclass;
+ const u_char *end;
+
+ n = namewithdot;
+ end = ptr + len - 4;
+ if (end - ptr >= sizeof namewithdot)
+ end = ptr + sizeof namewithdot - 1;
+ while (ptr < end) {
+ len = *ptr++;
+ if (len > end - ptr)
+ len = end - ptr;
+ if (n != namewithdot)
+ *n++ = '.';
+ memcpy(n, ptr, len);
+ ptr += len;
+ n += len;
+ }
+ *n = '\0';
+
+ if (log_IsKept(LogDNS)) {
+ memcpy(&tmp, end, sizeof tmp);
+ qtype = dns_Qtype2Txt(ntohs(tmp));
+ memcpy(&tmp, end + 2, sizeof tmp);
+ qclass = dns_Qclass2Txt(ntohs(tmp));
+
+ log_Printf(LogDNS, "%sbound query %s %s %s\n",
+ direction, qclass, qtype, namewithdot);
+ }
+ }
+}
+
+/*
+ * Check if the given packet matches the given filter.
+ * One of pip or pip6 must be set.
+ */
+int
+PacketCheck(struct bundle *bundle, u_int32_t family,
+ const unsigned char *packet, int nb, struct filter *filter,
+ const char *prefix, unsigned *psecs)
+{
+ static const char *const TcpFlags[] = {
+ "FIN", "SYN", "RST", "PSH", "ACK", "URG"
+ };
+ const struct tcphdr *th;
+ const struct udphdr *uh;
+ const struct icmp *icmph;
+#ifndef NOINET6
+ const struct icmp6_hdr *icmp6h;
+#endif
+ const unsigned char *payload;
+ struct ncpaddr srcaddr, dstaddr;
+ int cproto, mask, len, n, pri, logit, loglen, result;
+ char logbuf[200];
+ int datalen, frag;
+ u_char tos;
+
+ logit = (log_IsKept(LogTCPIP) || log_IsKept(LogDNS)) &&
+ (!filter || filter->logok);
+ loglen = 0;
+ pri = 0;
+
+#ifndef NOINET6
+ if (family == AF_INET6) {
+ const struct ip6_hdr *pip6 = (const struct ip6_hdr *)packet;
+
+ ncpaddr_setip6(&srcaddr, &pip6->ip6_src);
+ ncpaddr_setip6(&dstaddr, &pip6->ip6_dst);
+ datalen = ntohs(pip6->ip6_plen);
+ payload = packet + sizeof *pip6;
+ cproto = pip6->ip6_nxt;
+ tos = 0; /* XXX: pip6->ip6_vfc >> 4 ? */
+ frag = 0; /* XXX: ??? */
+ } else
+#endif
+ {
+ const struct ip *pip = (const struct ip *)packet;
+
+ ncpaddr_setip4(&srcaddr, pip->ip_src);
+ ncpaddr_setip4(&dstaddr, pip->ip_dst);
+ datalen = ntohs(pip->ip_len) - (pip->ip_hl << 2);
+ payload = packet + (pip->ip_hl << 2);
+ cproto = pip->ip_p;
+ tos = pip->ip_tos;
+ frag = ntohs(pip->ip_off) & IP_OFFMASK;
+ }
+
+ uh = NULL;
+
+ if (logit && loglen < sizeof logbuf) {
+ if (prefix)
+ snprintf(logbuf + loglen, sizeof logbuf - loglen, "%s", prefix);
+ else if (filter)
+ snprintf(logbuf + loglen, sizeof logbuf - loglen, "%s ", filter->name);
+ else
+ snprintf(logbuf + loglen, sizeof logbuf - loglen, " ");
+ loglen += strlen(logbuf + loglen);
+ }
+
+ switch (cproto) {
+ case IPPROTO_ICMP:
+ if (logit && loglen < sizeof logbuf) {
+ len = datalen - sizeof *icmph;
+ icmph = (const struct icmp *)payload;
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "ICMP: %s:%d ---> ", ncpaddr_ntoa(&srcaddr), icmph->icmp_type);
+ loglen += strlen(logbuf + loglen);
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "%s (%d/%d)", ncpaddr_ntoa(&dstaddr), len, nb);
+ loglen += strlen(logbuf + loglen);
+ }
+ break;
+
+#ifndef NOINET6
+ case IPPROTO_ICMPV6:
+ if (logit && loglen < sizeof logbuf) {
+ len = datalen - sizeof *icmp6h;
+ icmp6h = (const struct icmp6_hdr *)payload;
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "ICMP: %s:%d ---> ", ncpaddr_ntoa(&srcaddr), icmp6h->icmp6_type);
+ loglen += strlen(logbuf + loglen);
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "%s (%d/%d)", ncpaddr_ntoa(&dstaddr), len, nb);
+ loglen += strlen(logbuf + loglen);
+ }
+ break;
+#endif
+
+ case IPPROTO_UDP:
+ uh = (const struct udphdr *)payload;
+ if (tos == IPTOS_LOWDELAY && bundle->ncp.cfg.urgent.tos)
+ pri++;
+
+ if (!frag && ncp_IsUrgentUdpPort(&bundle->ncp, ntohs(uh->uh_sport),
+ ntohs(uh->uh_dport)))
+ pri++;
+
+ if (logit && loglen < sizeof logbuf) {
+ len = datalen - sizeof *uh;
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "UDP: %s:%d ---> ", ncpaddr_ntoa(&srcaddr), ntohs(uh->uh_sport));
+ loglen += strlen(logbuf + loglen);
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "%s:%d (%d/%d)", ncpaddr_ntoa(&dstaddr), ntohs(uh->uh_dport),
+ len, nb);
+ loglen += strlen(logbuf + loglen);
+ }
+
+ if (Enabled(bundle, OPT_FILTERDECAP) &&
+ payload[sizeof *uh] == HDLC_ADDR &&
+ payload[sizeof *uh + 1] == HDLC_UI) {
+ u_short proto;
+ const char *type;
+
+ memcpy(&proto, payload + sizeof *uh + 2, sizeof proto);
+ type = NULL;
+
+ switch (ntohs(proto)) {
+ case PROTO_IP:
+ snprintf(logbuf + loglen, sizeof logbuf - loglen, " contains ");
+ result = PacketCheck(bundle, AF_INET, payload + sizeof *uh + 4,
+ nb - (payload - packet) - sizeof *uh - 4, filter,
+ logbuf, psecs);
+ if (result != -2)
+ return result;
+ type = "IP";
+ break;
+
+ case PROTO_VJUNCOMP: type = "compressed VJ"; break;
+ case PROTO_VJCOMP: type = "uncompressed VJ"; break;
+ case PROTO_MP: type = "Multi-link"; break;
+ case PROTO_ICOMPD: type = "Individual link CCP"; break;
+ case PROTO_COMPD: type = "CCP"; break;
+ case PROTO_IPCP: type = "IPCP"; break;
+ case PROTO_LCP: type = "LCP"; break;
+ case PROTO_PAP: type = "PAP"; break;
+ case PROTO_CBCP: type = "CBCP"; break;
+ case PROTO_LQR: type = "LQR"; break;
+ case PROTO_CHAP: type = "CHAP"; break;
+ }
+ if (type) {
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ " - %s data", type);
+ loglen += strlen(logbuf + loglen);
+ }
+ }
+
+ break;
+
+#ifdef IPPROTO_GRE
+ case IPPROTO_GRE:
+ if (logit && loglen < sizeof logbuf) {
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "GRE: %s ---> ", ncpaddr_ntoa(&srcaddr));
+ loglen += strlen(logbuf + loglen);
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "%s (%d/%d)", ncpaddr_ntoa(&dstaddr), datalen, nb);
+ loglen += strlen(logbuf + loglen);
+ }
+ break;
+#endif
+
+#ifdef IPPROTO_OSPFIGP
+ case IPPROTO_OSPFIGP:
+ if (logit && loglen < sizeof logbuf) {
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "OSPF: %s ---> ", ncpaddr_ntoa(&srcaddr));
+ loglen += strlen(logbuf + loglen);
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "%s (%d/%d)", ncpaddr_ntoa(&dstaddr), datalen, nb);
+ loglen += strlen(logbuf + loglen);
+ }
+ break;
+#endif
+
+#ifndef NOINET6
+ case IPPROTO_IPV6:
+ if (logit && loglen < sizeof logbuf) {
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "IPv6: %s ---> ", ncpaddr_ntoa(&srcaddr));
+ loglen += strlen(logbuf + loglen);
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "%s (%d/%d)", ncpaddr_ntoa(&dstaddr), datalen, nb);
+ loglen += strlen(logbuf + loglen);
+ }
+
+ if (Enabled(bundle, OPT_FILTERDECAP)) {
+ snprintf(logbuf + loglen, sizeof logbuf - loglen, " contains ");
+ result = PacketCheck(bundle, AF_INET6, payload, nb - (payload - packet),
+ filter, logbuf, psecs);
+ if (result != -2)
+ return result;
+ }
+ break;
+#endif
+
+ case IPPROTO_IPIP:
+ if (logit && loglen < sizeof logbuf) {
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "IPIP: %s ---> ", ncpaddr_ntoa(&srcaddr));
+ loglen += strlen(logbuf + loglen);
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "%s", ncpaddr_ntoa(&dstaddr));
+ loglen += strlen(logbuf + loglen);
+ }
+
+ if (Enabled(bundle, OPT_FILTERDECAP) &&
+ ((const struct ip *)payload)->ip_v == 4) {
+ snprintf(logbuf + loglen, sizeof logbuf - loglen, " contains ");
+ result = PacketCheck(bundle, AF_INET, payload, nb - (payload - packet),
+ filter, logbuf, psecs);
+ if (result != -2)
+ return result;
+ }
+ break;
+
+ case IPPROTO_ESP:
+ if (logit && loglen < sizeof logbuf) {
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "ESP: %s ---> ", ncpaddr_ntoa(&srcaddr));
+ loglen += strlen(logbuf + loglen);
+ snprintf(logbuf + loglen, sizeof logbuf - loglen, "%s, spi %p",
+ ncpaddr_ntoa(&dstaddr), payload);
+ loglen += strlen(logbuf + loglen);
+ }
+ break;
+
+ case IPPROTO_AH:
+ if (logit && loglen < sizeof logbuf) {
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "AH: %s ---> ", ncpaddr_ntoa(&srcaddr));
+ loglen += strlen(logbuf + loglen);
+ snprintf(logbuf + loglen, sizeof logbuf - loglen, "%s, spi %p",
+ ncpaddr_ntoa(&dstaddr), payload + sizeof(u_int32_t));
+ loglen += strlen(logbuf + loglen);
+ }
+ break;
+
+ case IPPROTO_IGMP:
+ if (logit && loglen < sizeof logbuf) {
+ uh = (const struct udphdr *)payload;
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "IGMP: %s:%d ---> ", ncpaddr_ntoa(&srcaddr),
+ ntohs(uh->uh_sport));
+ loglen += strlen(logbuf + loglen);
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "%s:%d", ncpaddr_ntoa(&dstaddr), ntohs(uh->uh_dport));
+ loglen += strlen(logbuf + loglen);
+ }
+ break;
+
+ case IPPROTO_TCP:
+ th = (const struct tcphdr *)payload;
+ if (tos == IPTOS_LOWDELAY && bundle->ncp.cfg.urgent.tos)
+ pri++;
+
+ if (!frag && ncp_IsUrgentTcpPort(&bundle->ncp, ntohs(th->th_sport),
+ ntohs(th->th_dport)))
+ pri++;
+
+ if (logit && loglen < sizeof logbuf) {
+ len = datalen - (th->th_off << 2);
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "TCP: %s:%d ---> ", ncpaddr_ntoa(&srcaddr), ntohs(th->th_sport));
+ loglen += strlen(logbuf + loglen);
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "%s:%d", ncpaddr_ntoa(&dstaddr), ntohs(th->th_dport));
+ loglen += strlen(logbuf + loglen);
+ n = 0;
+ for (mask = TH_FIN; mask != 0x40; mask <<= 1) {
+ if (th->th_flags & mask) {
+ snprintf(logbuf + loglen, sizeof logbuf - loglen, " %s", TcpFlags[n]);
+ loglen += strlen(logbuf + loglen);
+ }
+ n++;
+ }
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ " seq:%lx ack:%lx (%d/%d)",
+ (u_long)ntohl(th->th_seq), (u_long)ntohl(th->th_ack), len, nb);
+ loglen += strlen(logbuf + loglen);
+ if ((th->th_flags & TH_SYN) && nb > 40) {
+ const u_short *sp;
+
+ sp = (const u_short *)(payload + 20);
+ if (ntohs(sp[0]) == 0x0204) {
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ " MSS = %d", ntohs(sp[1]));
+ loglen += strlen(logbuf + loglen);
+ }
+ }
+ }
+ break;
+
+ default:
+ if (prefix)
+ return -2;
+
+ if (logit && loglen < sizeof logbuf) {
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "<%d>: %s ---> ", cproto, ncpaddr_ntoa(&srcaddr));
+ loglen += strlen(logbuf + loglen);
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "%s (%d)", ncpaddr_ntoa(&dstaddr), nb);
+ loglen += strlen(logbuf + loglen);
+ }
+ break;
+ }
+
+ if (filter && FilterCheck(packet, family, filter, psecs)) {
+ if (logit)
+ log_Printf(LogTCPIP, "%s - BLOCKED\n", logbuf);
+ result = -1;
+ } else {
+ /* Check Keep Alive filter */
+ if (logit && log_IsKept(LogTCPIP)) {
+ unsigned alivesecs;
+
+ alivesecs = 0;
+ if (filter &&
+ FilterCheck(packet, family, &bundle->filter.alive, &alivesecs))
+ log_Printf(LogTCPIP, "%s - NO KEEPALIVE\n", logbuf);
+ else if (psecs != NULL) {
+ if(*psecs == 0)
+ *psecs = alivesecs;
+ if (*psecs) {
+ if (*psecs != alivesecs)
+ log_Printf(LogTCPIP, "%s - (timeout = %d / ALIVE = %d secs)\n",
+ logbuf, *psecs, alivesecs);
+ else
+ log_Printf(LogTCPIP, "%s - (timeout = %d secs)\n", logbuf, *psecs);
+ } else
+ log_Printf(LogTCPIP, "%s\n", logbuf);
+ }
+ }
+ result = pri;
+ }
+
+ if (filter && uh && ntohs(uh->uh_dport) == 53 && log_IsKept(LogDNS))
+ ip_LogDNS(uh, filter->name);
+
+ return result;
+}
+
+static int
+ip_Input(struct bundle *bundle, struct link *l, struct mbuf *bp, u_int32_t af)
+{
+ int nb, nw;
+ struct tun_data tun;
+ char *data;
+ unsigned secs, alivesecs;
+
+ nb = m_length(bp);
+ if (nb > sizeof tun.data) {
+ log_Printf(LogWARN, "ip_Input: %s: Packet too large (got %d, max %d)\n",
+ l->name, nb, (int)(sizeof tun.data));
+ m_freem(bp);
+ return 0;
+ }
+ mbuf_Read(bp, tun.data, nb);
+
+ secs = 0;
+ if (PacketCheck(bundle, af, tun.data, nb, &bundle->filter.in,
+ NULL, &secs) < 0)
+ return 0;
+
+ alivesecs = 0;
+ if (!FilterCheck(tun.data, af, &bundle->filter.alive, &alivesecs)) {
+ if (secs == 0)
+ secs = alivesecs;
+ bundle_StartIdleTimer(bundle, secs);
+ }
+
+ if (bundle->dev.header) {
+ tun.header.family = htonl(af);
+ nb += sizeof tun - sizeof tun.data;
+ data = (char *)&tun;
+ } else
+ data = tun.data;
+
+ nw = write(bundle->dev.fd, data, nb);
+ if (nw != nb) {
+ if (nw == -1)
+ log_Printf(LogERROR, "ip_Input: %s: wrote %d, got %s\n",
+ l->name, nb, strerror(errno));
+ else
+ log_Printf(LogERROR, "ip_Input: %s: wrote %d, got %d\n", l->name, nb, nw);
+ }
+
+ return nb;
+}
+
+struct mbuf *
+ipv4_Input(struct bundle *bundle, struct link *l, struct mbuf *bp)
+{
+ int nb;
+
+ if (bundle->ncp.ipcp.fsm.state != ST_OPENED) {
+ log_Printf(LogWARN, "ipv4_Input: IPCP not open - packet dropped\n");
+ m_freem(bp);
+ return NULL;
+ }
+
+ m_settype(bp, MB_IPIN);
+
+ nb = ip_Input(bundle, l, bp, AF_INET);
+ ipcp_AddInOctets(&bundle->ncp.ipcp, nb);
+
+ return NULL;
+}
+
+#ifndef NOINET6
+struct mbuf *
+ipv6_Input(struct bundle *bundle, struct link *l, struct mbuf *bp)
+{
+ int nb;
+
+ if (bundle->ncp.ipv6cp.fsm.state != ST_OPENED) {
+ log_Printf(LogWARN, "ipv6_Input: IPV6CP not open - packet dropped\n");
+ m_freem(bp);
+ return NULL;
+ }
+
+ m_settype(bp, MB_IPV6IN);
+
+ nb = ip_Input(bundle, l, bp, AF_INET6);
+ ipv6cp_AddInOctets(&bundle->ncp.ipv6cp, nb);
+
+ return NULL;
+}
+#endif
diff --git a/usr.sbin/ppp/ip.h b/usr.sbin/ppp/ip.h
new file mode 100644
index 0000000..a4c4179
--- /dev/null
+++ b/usr.sbin/ppp/ip.h
@@ -0,0 +1,44 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct mbuf;
+struct filter;
+struct link;
+struct bundle;
+
+extern int ip_PushPacket(struct link *, struct bundle *);
+extern int PacketCheck(struct bundle *, u_int32_t, const unsigned char *, int,
+ struct filter *, const char *, unsigned *secs);
+extern int FilterCheck(const unsigned char *, u_int32_t, const struct filter *,
+ unsigned *);
+extern struct mbuf *ipv4_Input(struct bundle *, struct link *, struct mbuf *);
+#ifndef NOINET6
+extern struct mbuf *ipv6_Input(struct bundle *, struct link *, struct mbuf *);
+#endif
diff --git a/usr.sbin/ppp/ipcp.c b/usr.sbin/ppp/ipcp.c
new file mode 100644
index 0000000..0a06e8b
--- /dev/null
+++ b/usr.sbin/ppp/ipcp.c
@@ -0,0 +1,1474 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/route.h>
+#include <netdb.h>
+#include <sys/un.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <resolv.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <termios.h>
+#include <unistd.h>
+
+#ifndef NONAT
+#ifdef LOCALNAT
+#include "alias.h"
+#else
+#include <alias.h>
+#endif
+#endif
+
+#include "layer.h"
+#include "ua.h"
+#include "defs.h"
+#include "command.h"
+#include "mbuf.h"
+#include "log.h"
+#include "timer.h"
+#include "fsm.h"
+#include "proto.h"
+#include "iplist.h"
+#include "throughput.h"
+#include "slcompress.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "lcp.h"
+#include "ncpaddr.h"
+#include "ip.h"
+#include "ipcp.h"
+#include "filter.h"
+#include "descriptor.h"
+#include "vjcomp.h"
+#include "async.h"
+#include "ccp.h"
+#include "link.h"
+#include "physical.h"
+#include "mp.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+#include "id.h"
+#include "arp.h"
+#include "systems.h"
+#include "prompt.h"
+#include "route.h"
+#include "iface.h"
+
+#undef REJECTED
+#define REJECTED(p, x) ((p)->peer_reject & (1<<(x)))
+#define issep(ch) ((ch) == ' ' || (ch) == '\t')
+#define isip(ch) (((ch) >= '0' && (ch) <= '9') || (ch) == '.')
+
+struct compreq {
+ u_short proto;
+ u_char slots;
+ u_char compcid;
+};
+
+static int IpcpLayerUp(struct fsm *);
+static void IpcpLayerDown(struct fsm *);
+static void IpcpLayerStart(struct fsm *);
+static void IpcpLayerFinish(struct fsm *);
+static void IpcpInitRestartCounter(struct fsm *, int);
+static void IpcpSendConfigReq(struct fsm *);
+static void IpcpSentTerminateReq(struct fsm *);
+static void IpcpSendTerminateAck(struct fsm *, u_char);
+static void IpcpDecodeConfig(struct fsm *, u_char *, u_char *, int,
+ struct fsm_decode *);
+
+static struct fsm_callbacks ipcp_Callbacks = {
+ IpcpLayerUp,
+ IpcpLayerDown,
+ IpcpLayerStart,
+ IpcpLayerFinish,
+ IpcpInitRestartCounter,
+ IpcpSendConfigReq,
+ IpcpSentTerminateReq,
+ IpcpSendTerminateAck,
+ IpcpDecodeConfig,
+ fsm_NullRecvResetReq,
+ fsm_NullRecvResetAck
+};
+
+static const char *
+protoname(int proto)
+{
+ static struct {
+ int id;
+ const char *txt;
+ } cftypes[] = {
+ /* Check out the latest ``Assigned numbers'' rfc (rfc1700.txt) */
+ { 1, "IPADDRS" }, /* IP-Addresses */ /* deprecated */
+ { 2, "COMPPROTO" }, /* IP-Compression-Protocol */
+ { 3, "IPADDR" }, /* IP-Address */
+ { 129, "PRIDNS" }, /* 129: Primary DNS Server Address */
+ { 130, "PRINBNS" }, /* 130: Primary NBNS Server Address */
+ { 131, "SECDNS" }, /* 131: Secondary DNS Server Address */
+ { 132, "SECNBNS" } /* 132: Secondary NBNS Server Address */
+ };
+ int f;
+
+ for (f = 0; f < sizeof cftypes / sizeof *cftypes; f++)
+ if (cftypes[f].id == proto)
+ return cftypes[f].txt;
+
+ return NumStr(proto, NULL, 0);
+}
+
+void
+ipcp_AddInOctets(struct ipcp *ipcp, int n)
+{
+ throughput_addin(&ipcp->throughput, n);
+}
+
+void
+ipcp_AddOutOctets(struct ipcp *ipcp, int n)
+{
+ throughput_addout(&ipcp->throughput, n);
+}
+
+void
+ipcp_LoadDNS(struct ipcp *ipcp)
+{
+ int fd;
+
+ ipcp->ns.dns[0].s_addr = ipcp->ns.dns[1].s_addr = INADDR_NONE;
+
+ if (ipcp->ns.resolv != NULL) {
+ free(ipcp->ns.resolv);
+ ipcp->ns.resolv = NULL;
+ }
+ if (ipcp->ns.resolv_nons != NULL) {
+ free(ipcp->ns.resolv_nons);
+ ipcp->ns.resolv_nons = NULL;
+ }
+ ipcp->ns.resolver = 0;
+
+ if ((fd = open(_PATH_RESCONF, O_RDONLY)) != -1) {
+ struct stat st;
+
+ if (fstat(fd, &st) == 0) {
+ ssize_t got;
+
+ if ((ipcp->ns.resolv_nons = (char *)malloc(st.st_size + 1)) == NULL)
+ log_Printf(LogERROR, "Failed to malloc %lu for %s: %s\n",
+ (unsigned long)st.st_size, _PATH_RESCONF, strerror(errno));
+ else if ((ipcp->ns.resolv = (char *)malloc(st.st_size + 1)) == NULL) {
+ log_Printf(LogERROR, "Failed(2) to malloc %lu for %s: %s\n",
+ (unsigned long)st.st_size, _PATH_RESCONF, strerror(errno));
+ free(ipcp->ns.resolv_nons);
+ ipcp->ns.resolv_nons = NULL;
+ } else if ((got = read(fd, ipcp->ns.resolv, st.st_size)) != st.st_size) {
+ if (got == -1)
+ log_Printf(LogERROR, "Failed to read %s: %s\n",
+ _PATH_RESCONF, strerror(errno));
+ else
+ log_Printf(LogERROR, "Failed to read %s, got %lu not %lu\n",
+ _PATH_RESCONF, (unsigned long)got,
+ (unsigned long)st.st_size);
+ free(ipcp->ns.resolv_nons);
+ ipcp->ns.resolv_nons = NULL;
+ free(ipcp->ns.resolv);
+ ipcp->ns.resolv = NULL;
+ } else {
+ char *cp, *cp_nons, *ncp, ch;
+ int n;
+
+ ipcp->ns.resolv[st.st_size] = '\0';
+ ipcp->ns.resolver = 1;
+
+ cp_nons = ipcp->ns.resolv_nons;
+ cp = ipcp->ns.resolv;
+ n = 0;
+
+ while ((ncp = strstr(cp, "nameserver")) != NULL) {
+ if (ncp != cp) {
+ memcpy(cp_nons, cp, ncp - cp);
+ cp_nons += ncp - cp;
+ }
+ if ((ncp != cp && ncp[-1] != '\n') || !issep(ncp[10])) {
+ memcpy(cp_nons, ncp, 9);
+ cp_nons += 9;
+ cp = ncp + 9; /* Can't match "nameserver" at cp... */
+ continue;
+ }
+
+ for (cp = ncp + 11; issep(*cp); cp++) /* Skip whitespace */
+ ;
+
+ for (ncp = cp; isip(*ncp); ncp++) /* Jump over IP */
+ ;
+
+ ch = *ncp;
+ *ncp = '\0';
+ if (n < 2 && inet_aton(cp, ipcp->ns.dns))
+ n++;
+ *ncp = ch;
+
+ if ((cp = strchr(ncp, '\n')) == NULL) /* Point at next line */
+ cp = ncp + strlen(ncp);
+ else
+ cp++;
+ }
+ strcpy(cp_nons, cp); /* Copy the end - including the NUL */
+ cp_nons += strlen(cp_nons) - 1;
+ while (cp_nons >= ipcp->ns.resolv_nons && *cp_nons == '\n')
+ *cp_nons-- = '\0';
+ if (n == 2 && ipcp->ns.dns[0].s_addr == INADDR_ANY) {
+ ipcp->ns.dns[0].s_addr = ipcp->ns.dns[1].s_addr;
+ ipcp->ns.dns[1].s_addr = INADDR_ANY;
+ }
+ bundle_AdjustDNS(ipcp->fsm.bundle);
+ }
+ } else
+ log_Printf(LogERROR, "Failed to stat opened %s: %s\n",
+ _PATH_RESCONF, strerror(errno));
+
+ close(fd);
+ }
+}
+
+int
+ipcp_WriteDNS(struct ipcp *ipcp)
+{
+ const char *paddr;
+ mode_t mask;
+ FILE *fp;
+
+ if (ipcp->ns.dns[0].s_addr == INADDR_ANY &&
+ ipcp->ns.dns[1].s_addr == INADDR_ANY) {
+ log_Printf(LogIPCP, "%s not modified: All nameservers NAKd\n",
+ _PATH_RESCONF);
+ return 0;
+ }
+
+ if (ipcp->ns.dns[0].s_addr == INADDR_ANY) {
+ ipcp->ns.dns[0].s_addr = ipcp->ns.dns[1].s_addr;
+ ipcp->ns.dns[1].s_addr = INADDR_ANY;
+ }
+
+ mask = umask(022);
+ if ((fp = ID0fopen(_PATH_RESCONF, "w")) != NULL) {
+ umask(mask);
+ if (ipcp->ns.resolv_nons)
+ fputs(ipcp->ns.resolv_nons, fp);
+ paddr = inet_ntoa(ipcp->ns.dns[0]);
+ log_Printf(LogIPCP, "Primary nameserver set to %s\n", paddr);
+ fprintf(fp, "\nnameserver %s\n", paddr);
+ if (ipcp->ns.dns[1].s_addr != INADDR_ANY &&
+ ipcp->ns.dns[1].s_addr != INADDR_NONE &&
+ ipcp->ns.dns[1].s_addr != ipcp->ns.dns[0].s_addr) {
+ paddr = inet_ntoa(ipcp->ns.dns[1]);
+ log_Printf(LogIPCP, "Secondary nameserver set to %s\n", paddr);
+ fprintf(fp, "nameserver %s\n", paddr);
+ }
+ if (fclose(fp) == EOF) {
+ log_Printf(LogERROR, "write(): Failed updating %s: %s\n", _PATH_RESCONF,
+ strerror(errno));
+ return 0;
+ }
+ } else
+ umask(mask);
+
+ return 1;
+}
+
+void
+ipcp_RestoreDNS(struct ipcp *ipcp)
+{
+ if (ipcp->ns.resolver) {
+ ssize_t got;
+ size_t len;
+ int fd;
+
+ if ((fd = ID0open(_PATH_RESCONF, O_WRONLY|O_TRUNC, 0644)) != -1) {
+ len = strlen(ipcp->ns.resolv);
+ if ((got = write(fd, ipcp->ns.resolv, len)) != len) {
+ if (got == -1)
+ log_Printf(LogERROR, "Failed rewriting %s: write: %s\n",
+ _PATH_RESCONF, strerror(errno));
+ else
+ log_Printf(LogERROR, "Failed rewriting %s: wrote %lu of %lu\n",
+ _PATH_RESCONF, (unsigned long)got, (unsigned long)len);
+ }
+ close(fd);
+ } else
+ log_Printf(LogERROR, "Failed rewriting %s: open: %s\n", _PATH_RESCONF,
+ strerror(errno));
+ } else if (remove(_PATH_RESCONF) == -1)
+ log_Printf(LogERROR, "Failed removing %s: %s\n", _PATH_RESCONF,
+ strerror(errno));
+
+}
+
+int
+ipcp_Show(struct cmdargs const *arg)
+{
+ struct ipcp *ipcp = &arg->bundle->ncp.ipcp;
+
+ prompt_Printf(arg->prompt, "%s [%s]\n", ipcp->fsm.name,
+ State2Nam(ipcp->fsm.state));
+ if (ipcp->fsm.state == ST_OPENED) {
+ prompt_Printf(arg->prompt, " His side: %s, %s\n",
+ inet_ntoa(ipcp->peer_ip), vj2asc(ipcp->peer_compproto));
+ prompt_Printf(arg->prompt, " My side: %s, %s\n",
+ inet_ntoa(ipcp->my_ip), vj2asc(ipcp->my_compproto));
+ prompt_Printf(arg->prompt, " Queued packets: %lu\n",
+ (unsigned long)ipcp_QueueLen(ipcp));
+ }
+
+ prompt_Printf(arg->prompt, "\nDefaults:\n");
+ prompt_Printf(arg->prompt, " FSM retry = %us, max %u Config"
+ " REQ%s, %u Term REQ%s\n", ipcp->cfg.fsm.timeout,
+ ipcp->cfg.fsm.maxreq, ipcp->cfg.fsm.maxreq == 1 ? "" : "s",
+ ipcp->cfg.fsm.maxtrm, ipcp->cfg.fsm.maxtrm == 1 ? "" : "s");
+ prompt_Printf(arg->prompt, " My Address: %s\n",
+ ncprange_ntoa(&ipcp->cfg.my_range));
+ if (ipcp->cfg.HaveTriggerAddress)
+ prompt_Printf(arg->prompt, " Trigger address: %s\n",
+ inet_ntoa(ipcp->cfg.TriggerAddress));
+
+ prompt_Printf(arg->prompt, " VJ compression: %s (%d slots %s slot "
+ "compression)\n", command_ShowNegval(ipcp->cfg.vj.neg),
+ ipcp->cfg.vj.slots, ipcp->cfg.vj.slotcomp ? "with" : "without");
+
+ if (iplist_isvalid(&ipcp->cfg.peer_list))
+ prompt_Printf(arg->prompt, " His Address: %s\n",
+ ipcp->cfg.peer_list.src);
+ else
+ prompt_Printf(arg->prompt, " His Address: %s\n",
+ ncprange_ntoa(&ipcp->cfg.peer_range));
+
+ prompt_Printf(arg->prompt, " DNS: %s",
+ ipcp->cfg.ns.dns[0].s_addr == INADDR_NONE ?
+ "none" : inet_ntoa(ipcp->cfg.ns.dns[0]));
+ if (ipcp->cfg.ns.dns[1].s_addr != INADDR_NONE)
+ prompt_Printf(arg->prompt, ", %s",
+ inet_ntoa(ipcp->cfg.ns.dns[1]));
+ prompt_Printf(arg->prompt, ", %s\n",
+ command_ShowNegval(ipcp->cfg.ns.dns_neg));
+ prompt_Printf(arg->prompt, " Resolver DNS: %s",
+ ipcp->ns.dns[0].s_addr == INADDR_NONE ?
+ "none" : inet_ntoa(ipcp->ns.dns[0]));
+ if (ipcp->ns.dns[1].s_addr != INADDR_NONE &&
+ ipcp->ns.dns[1].s_addr != ipcp->ns.dns[0].s_addr)
+ prompt_Printf(arg->prompt, ", %s",
+ inet_ntoa(ipcp->ns.dns[1]));
+ prompt_Printf(arg->prompt, "\n NetBIOS NS: %s, ",
+ inet_ntoa(ipcp->cfg.ns.nbns[0]));
+ prompt_Printf(arg->prompt, "%s\n\n",
+ inet_ntoa(ipcp->cfg.ns.nbns[1]));
+
+ throughput_disp(&ipcp->throughput, arg->prompt);
+
+ return 0;
+}
+
+int
+ipcp_vjset(struct cmdargs const *arg)
+{
+ if (arg->argc != arg->argn+2)
+ return -1;
+ if (!strcasecmp(arg->argv[arg->argn], "slots")) {
+ int slots;
+
+ slots = atoi(arg->argv[arg->argn+1]);
+ if (slots < 4 || slots > 16)
+ return 1;
+ arg->bundle->ncp.ipcp.cfg.vj.slots = slots;
+ return 0;
+ } else if (!strcasecmp(arg->argv[arg->argn], "slotcomp")) {
+ if (!strcasecmp(arg->argv[arg->argn+1], "on"))
+ arg->bundle->ncp.ipcp.cfg.vj.slotcomp = 1;
+ else if (!strcasecmp(arg->argv[arg->argn+1], "off"))
+ arg->bundle->ncp.ipcp.cfg.vj.slotcomp = 0;
+ else
+ return 2;
+ return 0;
+ }
+ return -1;
+}
+
+void
+ipcp_Init(struct ipcp *ipcp, struct bundle *bundle, struct link *l,
+ const struct fsm_parent *parent)
+{
+ struct hostent *hp;
+ struct in_addr host;
+ char name[MAXHOSTNAMELEN];
+ static const char * const timer_names[] =
+ {"IPCP restart", "IPCP openmode", "IPCP stopped"};
+
+ fsm_Init(&ipcp->fsm, "IPCP", PROTO_IPCP, 1, IPCP_MAXCODE, LogIPCP,
+ bundle, l, parent, &ipcp_Callbacks, timer_names);
+
+ ipcp->cfg.vj.slots = DEF_VJ_STATES;
+ ipcp->cfg.vj.slotcomp = 1;
+ memset(&ipcp->cfg.my_range, '\0', sizeof ipcp->cfg.my_range);
+
+ host.s_addr = htonl(INADDR_LOOPBACK);
+ ipcp->cfg.netmask.s_addr = INADDR_ANY;
+ if (gethostname(name, sizeof name) == 0) {
+ hp = gethostbyname(name);
+ if (hp && hp->h_addrtype == AF_INET && hp->h_length == sizeof host.s_addr)
+ memcpy(&host.s_addr, hp->h_addr, sizeof host.s_addr);
+ }
+ ncprange_setip4(&ipcp->cfg.my_range, host, ipcp->cfg.netmask);
+ ncprange_setip4(&ipcp->cfg.peer_range, ipcp->cfg.netmask, ipcp->cfg.netmask);
+
+ iplist_setsrc(&ipcp->cfg.peer_list, "");
+ ipcp->cfg.HaveTriggerAddress = 0;
+
+ ipcp->cfg.ns.dns[0].s_addr = INADDR_NONE;
+ ipcp->cfg.ns.dns[1].s_addr = INADDR_NONE;
+ ipcp->cfg.ns.dns_neg = 0;
+ ipcp->cfg.ns.nbns[0].s_addr = INADDR_ANY;
+ ipcp->cfg.ns.nbns[1].s_addr = INADDR_ANY;
+
+ ipcp->cfg.fsm.timeout = DEF_FSMRETRY;
+ ipcp->cfg.fsm.maxreq = DEF_FSMTRIES;
+ ipcp->cfg.fsm.maxtrm = DEF_FSMTRIES;
+ ipcp->cfg.vj.neg = NEG_ENABLED|NEG_ACCEPTED;
+
+ memset(&ipcp->vj, '\0', sizeof ipcp->vj);
+
+ ipcp->ns.resolv = NULL;
+ ipcp->ns.resolv_nons = NULL;
+ ipcp->ns.writable = 1;
+ ipcp_LoadDNS(ipcp);
+
+ throughput_init(&ipcp->throughput, SAMPLE_PERIOD);
+ memset(ipcp->Queue, '\0', sizeof ipcp->Queue);
+ ipcp_Setup(ipcp, INADDR_NONE);
+}
+
+void
+ipcp_Destroy(struct ipcp *ipcp)
+{
+ throughput_destroy(&ipcp->throughput);
+
+ if (ipcp->ns.resolv != NULL) {
+ free(ipcp->ns.resolv);
+ ipcp->ns.resolv = NULL;
+ }
+ if (ipcp->ns.resolv_nons != NULL) {
+ free(ipcp->ns.resolv_nons);
+ ipcp->ns.resolv_nons = NULL;
+ }
+}
+
+void
+ipcp_SetLink(struct ipcp *ipcp, struct link *l)
+{
+ ipcp->fsm.link = l;
+}
+
+void
+ipcp_Setup(struct ipcp *ipcp, u_int32_t mask)
+{
+ struct iface *iface = ipcp->fsm.bundle->iface;
+ struct ncpaddr ipaddr;
+ struct in_addr peer;
+ int pos, n;
+
+ ipcp->fsm.open_mode = 0;
+ ipcp->ifmask.s_addr = mask == INADDR_NONE ? ipcp->cfg.netmask.s_addr : mask;
+
+ if (iplist_isvalid(&ipcp->cfg.peer_list)) {
+ /* Try to give the peer a previously configured IP address */
+ for (n = 0; n < iface->addrs; n++) {
+ if (!ncpaddr_getip4(&iface->addr[n].peer, &peer))
+ continue;
+ if ((pos = iplist_ip2pos(&ipcp->cfg.peer_list, peer)) != -1) {
+ ncpaddr_setip4(&ipaddr, iplist_setcurpos(&ipcp->cfg.peer_list, pos));
+ break;
+ }
+ }
+ if (n == iface->addrs)
+ /* Ok, so none of 'em fit.... pick a random one */
+ ncpaddr_setip4(&ipaddr, iplist_setrandpos(&ipcp->cfg.peer_list));
+
+ ncprange_sethost(&ipcp->cfg.peer_range, &ipaddr);
+ }
+
+ ipcp->heis1172 = 0;
+ ipcp->peer_req = 0;
+ ncprange_getip4addr(&ipcp->cfg.peer_range, &ipcp->peer_ip);
+ ipcp->peer_compproto = 0;
+
+ if (ipcp->cfg.HaveTriggerAddress) {
+ /*
+ * Some implementations of PPP require that we send a
+ * *special* value as our address, even though the rfc specifies
+ * full negotiation (e.g. "0.0.0.0" or Not "0.0.0.0").
+ */
+ ipcp->my_ip = ipcp->cfg.TriggerAddress;
+ log_Printf(LogIPCP, "Using trigger address %s\n",
+ inet_ntoa(ipcp->cfg.TriggerAddress));
+ } else {
+ /*
+ * Otherwise, if we've used an IP number before and it's still within
+ * the network specified on the ``set ifaddr'' line, we really
+ * want to keep that IP number so that we can keep any existing
+ * connections that are bound to that IP.
+ */
+ for (n = 0; n < iface->addrs; n++) {
+ ncprange_getaddr(&iface->addr[n].ifa, &ipaddr);
+ if (ncprange_contains(&ipcp->cfg.my_range, &ipaddr)) {
+ ncpaddr_getip4(&ipaddr, &ipcp->my_ip);
+ break;
+ }
+ }
+ if (n == iface->addrs)
+ ncprange_getip4addr(&ipcp->cfg.my_range, &ipcp->my_ip);
+ }
+
+ if (IsEnabled(ipcp->cfg.vj.neg)
+#ifndef NORADIUS
+ || (ipcp->fsm.bundle->radius.valid && ipcp->fsm.bundle->radius.vj)
+#endif
+ )
+ ipcp->my_compproto = (PROTO_VJCOMP << 16) +
+ ((ipcp->cfg.vj.slots - 1) << 8) +
+ ipcp->cfg.vj.slotcomp;
+ else
+ ipcp->my_compproto = 0;
+ sl_compress_init(&ipcp->vj.cslc, ipcp->cfg.vj.slots - 1);
+
+ ipcp->peer_reject = 0;
+ ipcp->my_reject = 0;
+
+ /* Copy startup values into ipcp->ns.dns */
+ if (ipcp->cfg.ns.dns[0].s_addr != INADDR_NONE)
+ memcpy(ipcp->ns.dns, ipcp->cfg.ns.dns, sizeof ipcp->ns.dns);
+}
+
+static int
+numaddresses(struct in_addr mask)
+{
+ u_int32_t bit, haddr;
+ int n;
+
+ haddr = ntohl(mask.s_addr);
+ bit = 1;
+ n = 1;
+
+ do {
+ if (!(haddr & bit))
+ n <<= 1;
+ } while (bit <<= 1);
+
+ return n;
+}
+
+static int
+ipcp_proxyarp(struct ipcp *ipcp,
+ int (*proxyfun)(struct bundle *, struct in_addr, int),
+ const struct iface_addr *addr)
+{
+ struct bundle *bundle = ipcp->fsm.bundle;
+ struct in_addr peer, mask, ip;
+ int n, ret, s;
+
+ if (!ncpaddr_getip4(&addr->peer, &peer)) {
+ log_Printf(LogERROR, "Oops, ipcp_proxyarp() called with unexpected addr\n");
+ return 0;
+ }
+
+ if ((s = ID0socket(PF_INET, SOCK_DGRAM, 0)) == -1) {
+ log_Printf(LogERROR, "ipcp_proxyarp: socket: %s\n",
+ strerror(errno));
+ return 0;
+ }
+
+ ret = 0;
+
+ if (Enabled(bundle, OPT_PROXYALL)) {
+ ncprange_getip4mask(&addr->ifa, &mask);
+ if ((n = numaddresses(mask)) > 256) {
+ log_Printf(LogWARN, "%s: Too many addresses for proxyall\n",
+ ncprange_ntoa(&addr->ifa));
+ return 0;
+ }
+ ip.s_addr = peer.s_addr & mask.s_addr;
+ if (n >= 4) {
+ ip.s_addr = htonl(ntohl(ip.s_addr) + 1);
+ n -= 2;
+ }
+ while (n) {
+ if (!((ip.s_addr ^ peer.s_addr) & mask.s_addr)) {
+ if (!(ret = (*proxyfun)(bundle, ip, s)))
+ break;
+ n--;
+ }
+ ip.s_addr = htonl(ntohl(ip.s_addr) + 1);
+ }
+ ret = !n;
+ } else if (Enabled(bundle, OPT_PROXY))
+ ret = (*proxyfun)(bundle, peer, s);
+
+ close(s);
+
+ return ret;
+}
+
+static int
+ipcp_SetIPaddress(struct ipcp *ipcp, struct in_addr myaddr,
+ struct in_addr hisaddr)
+{
+ struct bundle *bundle = ipcp->fsm.bundle;
+ struct ncpaddr myncpaddr, hisncpaddr;
+ struct ncprange myrange;
+ struct in_addr mask;
+ struct sockaddr_storage ssdst, ssgw, ssmask;
+ struct sockaddr *sadst, *sagw, *samask;
+
+ sadst = (struct sockaddr *)&ssdst;
+ sagw = (struct sockaddr *)&ssgw;
+ samask = (struct sockaddr *)&ssmask;
+
+ ncpaddr_setip4(&hisncpaddr, hisaddr);
+ ncpaddr_setip4(&myncpaddr, myaddr);
+ ncprange_sethost(&myrange, &myncpaddr);
+
+ mask = addr2mask(myaddr);
+
+ if (ipcp->ifmask.s_addr != INADDR_ANY &&
+ (ipcp->ifmask.s_addr & mask.s_addr) == mask.s_addr)
+ ncprange_setip4mask(&myrange, ipcp->ifmask);
+
+ if (!iface_Add(bundle->iface, &bundle->ncp, &myrange, &hisncpaddr,
+ IFACE_ADD_FIRST|IFACE_FORCE_ADD|IFACE_SYSTEM))
+ return 0;
+
+ if (!Enabled(bundle, OPT_IFACEALIAS))
+ iface_Clear(bundle->iface, &bundle->ncp, AF_INET,
+ IFACE_CLEAR_ALIASES|IFACE_SYSTEM);
+
+ if (bundle->ncp.cfg.sendpipe > 0 || bundle->ncp.cfg.recvpipe > 0) {
+ ncprange_getsa(&myrange, &ssgw, &ssmask);
+ ncpaddr_getsa(&hisncpaddr, &ssdst);
+ rt_Update(bundle, sadst, sagw, samask);
+ }
+
+ if (Enabled(bundle, OPT_SROUTES))
+ route_Change(bundle, bundle->ncp.route, &myncpaddr, &hisncpaddr);
+
+#ifndef NORADIUS
+ if (bundle->radius.valid)
+ route_Change(bundle, bundle->radius.routes, &myncpaddr, &hisncpaddr);
+#endif
+
+ return 1; /* Ok */
+}
+
+static struct in_addr
+ChooseHisAddr(struct bundle *bundle, struct in_addr gw)
+{
+ struct in_addr try;
+ u_long f;
+
+ for (f = 0; f < bundle->ncp.ipcp.cfg.peer_list.nItems; f++) {
+ try = iplist_next(&bundle->ncp.ipcp.cfg.peer_list);
+ log_Printf(LogDEBUG, "ChooseHisAddr: Check item %ld (%s)\n",
+ f, inet_ntoa(try));
+ if (ipcp_SetIPaddress(&bundle->ncp.ipcp, gw, try)) {
+ log_Printf(LogIPCP, "Selected IP address %s\n", inet_ntoa(try));
+ break;
+ }
+ }
+
+ if (f == bundle->ncp.ipcp.cfg.peer_list.nItems) {
+ log_Printf(LogDEBUG, "ChooseHisAddr: All addresses in use !\n");
+ try.s_addr = INADDR_ANY;
+ }
+
+ return try;
+}
+
+static void
+IpcpInitRestartCounter(struct fsm *fp, int what)
+{
+ /* Set fsm timer load */
+ struct ipcp *ipcp = fsm2ipcp(fp);
+
+ fp->FsmTimer.load = ipcp->cfg.fsm.timeout * SECTICKS;
+ switch (what) {
+ case FSM_REQ_TIMER:
+ fp->restart = ipcp->cfg.fsm.maxreq;
+ break;
+ case FSM_TRM_TIMER:
+ fp->restart = ipcp->cfg.fsm.maxtrm;
+ break;
+ default:
+ fp->restart = 1;
+ break;
+ }
+}
+
+static void
+IpcpSendConfigReq(struct fsm *fp)
+{
+ /* Send config REQ please */
+ struct physical *p = link2physical(fp->link);
+ struct ipcp *ipcp = fsm2ipcp(fp);
+ u_char buff[24];
+ struct fsm_opt *o;
+
+ o = (struct fsm_opt *)buff;
+
+ if ((p && !physical_IsSync(p)) || !REJECTED(ipcp, TY_IPADDR)) {
+ memcpy(o->data, &ipcp->my_ip.s_addr, 4);
+ INC_FSM_OPT(TY_IPADDR, 6, o);
+ }
+
+ if (ipcp->my_compproto && !REJECTED(ipcp, TY_COMPPROTO)) {
+ if (ipcp->heis1172) {
+ u_int16_t proto = PROTO_VJCOMP;
+
+ ua_htons(&proto, o->data);
+ INC_FSM_OPT(TY_COMPPROTO, 4, o);
+ } else {
+ struct compreq req;
+
+ req.proto = htons(ipcp->my_compproto >> 16);
+ req.slots = (ipcp->my_compproto >> 8) & 255;
+ req.compcid = ipcp->my_compproto & 1;
+ memcpy(o->data, &req, 4);
+ INC_FSM_OPT(TY_COMPPROTO, 6, o);
+ }
+ }
+
+ if (IsEnabled(ipcp->cfg.ns.dns_neg)) {
+ if (!REJECTED(ipcp, TY_PRIMARY_DNS - TY_ADJUST_NS)) {
+ memcpy(o->data, &ipcp->ns.dns[0].s_addr, 4);
+ INC_FSM_OPT(TY_PRIMARY_DNS, 6, o);
+ }
+
+ if (!REJECTED(ipcp, TY_SECONDARY_DNS - TY_ADJUST_NS)) {
+ memcpy(o->data, &ipcp->ns.dns[1].s_addr, 4);
+ INC_FSM_OPT(TY_SECONDARY_DNS, 6, o);
+ }
+ }
+
+ fsm_Output(fp, CODE_CONFIGREQ, fp->reqid, buff, (u_char *)o - buff,
+ MB_IPCPOUT);
+}
+
+static void
+IpcpSentTerminateReq(struct fsm *fp)
+{
+ /* Term REQ just sent by FSM */
+}
+
+static void
+IpcpSendTerminateAck(struct fsm *fp, u_char id)
+{
+ /* Send Term ACK please */
+ fsm_Output(fp, CODE_TERMACK, id, NULL, 0, MB_IPCPOUT);
+}
+
+static void
+IpcpLayerStart(struct fsm *fp)
+{
+ /* We're about to start up ! */
+ struct ipcp *ipcp = fsm2ipcp(fp);
+
+ log_Printf(LogIPCP, "%s: LayerStart.\n", fp->link->name);
+ throughput_start(&ipcp->throughput, "IPCP throughput",
+ Enabled(fp->bundle, OPT_THROUGHPUT));
+ fp->more.reqs = fp->more.naks = fp->more.rejs = ipcp->cfg.fsm.maxreq * 3;
+ ipcp->peer_req = 0;
+}
+
+static void
+IpcpLayerFinish(struct fsm *fp)
+{
+ /* We're now down */
+ struct ipcp *ipcp = fsm2ipcp(fp);
+
+ log_Printf(LogIPCP, "%s: LayerFinish.\n", fp->link->name);
+ throughput_stop(&ipcp->throughput);
+ throughput_log(&ipcp->throughput, LogIPCP, NULL);
+}
+
+/*
+ * Called from iface_Add() via ncp_IfaceAddrAdded()
+ */
+void
+ipcp_IfaceAddrAdded(struct ipcp *ipcp, const struct iface_addr *addr)
+{
+ struct bundle *bundle = ipcp->fsm.bundle;
+
+ if (Enabled(bundle, OPT_PROXY) || Enabled(bundle, OPT_PROXYALL))
+ ipcp_proxyarp(ipcp, arp_SetProxy, addr);
+}
+
+/*
+ * Called from iface_Clear() and iface_Delete() via ncp_IfaceAddrDeleted()
+ */
+void
+ipcp_IfaceAddrDeleted(struct ipcp *ipcp, const struct iface_addr *addr)
+{
+ struct bundle *bundle = ipcp->fsm.bundle;
+
+ if (Enabled(bundle, OPT_PROXY) || Enabled(bundle, OPT_PROXYALL))
+ ipcp_proxyarp(ipcp, arp_ClearProxy, addr);
+}
+
+static void
+IpcpLayerDown(struct fsm *fp)
+{
+ /* About to come down */
+ struct ipcp *ipcp = fsm2ipcp(fp);
+ static int recursing;
+ char addr[16];
+
+ if (!recursing++) {
+ snprintf(addr, sizeof addr, "%s", inet_ntoa(ipcp->my_ip));
+ log_Printf(LogIPCP, "%s: LayerDown: %s\n", fp->link->name, addr);
+
+#ifndef NORADIUS
+ radius_Account(&fp->bundle->radius, &fp->bundle->radacct,
+ fp->bundle->links, RAD_STOP, &ipcp->peer_ip, &ipcp->ifmask,
+ &ipcp->throughput);
+
+ if (fp->bundle->radius.cfg.file && fp->bundle->radius.filterid)
+ system_Select(fp->bundle, fp->bundle->radius.filterid, LINKDOWNFILE,
+ NULL, NULL);
+#endif
+
+ /*
+ * XXX this stuff should really live in the FSM. Our config should
+ * associate executable sections in files with events.
+ */
+ if (system_Select(fp->bundle, addr, LINKDOWNFILE, NULL, NULL) < 0) {
+ if (bundle_GetLabel(fp->bundle)) {
+ if (system_Select(fp->bundle, bundle_GetLabel(fp->bundle),
+ LINKDOWNFILE, NULL, NULL) < 0)
+ system_Select(fp->bundle, "MYADDR", LINKDOWNFILE, NULL, NULL);
+ } else
+ system_Select(fp->bundle, "MYADDR", LINKDOWNFILE, NULL, NULL);
+ }
+
+ ipcp_Setup(ipcp, INADDR_NONE);
+ }
+ recursing--;
+}
+
+int
+ipcp_InterfaceUp(struct ipcp *ipcp)
+{
+ if (!ipcp_SetIPaddress(ipcp, ipcp->my_ip, ipcp->peer_ip)) {
+ log_Printf(LogERROR, "ipcp_InterfaceUp: unable to set ip address\n");
+ return 0;
+ }
+
+ if (!iface_SetFlags(ipcp->fsm.bundle->iface->name, IFF_UP)) {
+ log_Printf(LogERROR, "ipcp_InterfaceUp: Can't set the IFF_UP flag on %s\n",
+ ipcp->fsm.bundle->iface->name);
+ return 0;
+ }
+
+#ifndef NONAT
+ if (ipcp->fsm.bundle->NatEnabled)
+ PacketAliasSetAddress(ipcp->my_ip);
+#endif
+
+ return 1;
+}
+
+static int
+IpcpLayerUp(struct fsm *fp)
+{
+ /* We're now up */
+ struct ipcp *ipcp = fsm2ipcp(fp);
+ char tbuff[16];
+
+ log_Printf(LogIPCP, "%s: LayerUp.\n", fp->link->name);
+ snprintf(tbuff, sizeof tbuff, "%s", inet_ntoa(ipcp->my_ip));
+ log_Printf(LogIPCP, "myaddr %s hisaddr = %s\n",
+ tbuff, inet_ntoa(ipcp->peer_ip));
+
+ if (ipcp->peer_compproto >> 16 == PROTO_VJCOMP)
+ sl_compress_init(&ipcp->vj.cslc, (ipcp->peer_compproto >> 8) & 255);
+
+ if (!ipcp_InterfaceUp(ipcp))
+ return 0;
+
+#ifndef NORADIUS
+ radius_Account(&fp->bundle->radius, &fp->bundle->radacct, fp->bundle->links,
+ RAD_START, &ipcp->peer_ip, &ipcp->ifmask, &ipcp->throughput);
+
+ if (fp->bundle->radius.cfg.file && fp->bundle->radius.filterid)
+ system_Select(fp->bundle, fp->bundle->radius.filterid, LINKUPFILE,
+ NULL, NULL);
+#endif
+
+ /*
+ * XXX this stuff should really live in the FSM. Our config should
+ * associate executable sections in files with events.
+ */
+ if (system_Select(fp->bundle, tbuff, LINKUPFILE, NULL, NULL) < 0) {
+ if (bundle_GetLabel(fp->bundle)) {
+ if (system_Select(fp->bundle, bundle_GetLabel(fp->bundle),
+ LINKUPFILE, NULL, NULL) < 0)
+ system_Select(fp->bundle, "MYADDR", LINKUPFILE, NULL, NULL);
+ } else
+ system_Select(fp->bundle, "MYADDR", LINKUPFILE, NULL, NULL);
+ }
+
+ fp->more.reqs = fp->more.naks = fp->more.rejs = ipcp->cfg.fsm.maxreq * 3;
+ log_DisplayPrompts();
+
+ return 1;
+}
+
+static void
+ipcp_ValidateReq(struct ipcp *ipcp, struct in_addr ip, struct fsm_decode *dec)
+{
+ struct bundle *bundle = ipcp->fsm.bundle;
+ struct iface *iface = bundle->iface;
+ struct in_addr myaddr, peer;
+ int n;
+
+ if (iplist_isvalid(&ipcp->cfg.peer_list)) {
+ ncprange_getip4addr(&ipcp->cfg.my_range, &myaddr);
+ if (ip.s_addr == INADDR_ANY ||
+ iplist_ip2pos(&ipcp->cfg.peer_list, ip) < 0 ||
+ !ipcp_SetIPaddress(ipcp, myaddr, ip)) {
+ log_Printf(LogIPCP, "%s: Address invalid or already in use\n",
+ inet_ntoa(ip));
+ /*
+ * If we've already had a valid address configured for the peer,
+ * try NAKing with that so that we don't have to upset things
+ * too much.
+ */
+ for (n = 0; n < iface->addrs; n++) {
+ if (!ncpaddr_getip4(&iface->addr[n].peer, &peer))
+ continue;
+ if (iplist_ip2pos(&ipcp->cfg.peer_list, peer) >= 0) {
+ ipcp->peer_ip = peer;
+ break;
+ }
+ }
+
+ if (n == iface->addrs) {
+ /* Just pick an IP number from our list */
+ ipcp->peer_ip = ChooseHisAddr(bundle, myaddr);
+ }
+
+ if (ipcp->peer_ip.s_addr == INADDR_ANY) {
+ *dec->rejend++ = TY_IPADDR;
+ *dec->rejend++ = 6;
+ memcpy(dec->rejend, &ip.s_addr, 4);
+ dec->rejend += 4;
+ } else {
+ *dec->nakend++ = TY_IPADDR;
+ *dec->nakend++ = 6;
+ memcpy(dec->nakend, &ipcp->peer_ip.s_addr, 4);
+ dec->nakend += 4;
+ }
+ return;
+ }
+ } else if (!ncprange_containsip4(&ipcp->cfg.peer_range, ip)) {
+ /*
+ * If the destination address is not acceptable, NAK with what we
+ * want to use.
+ */
+ *dec->nakend++ = TY_IPADDR;
+ *dec->nakend++ = 6;
+ for (n = 0; n < iface->addrs; n++)
+ if (ncprange_contains(&ipcp->cfg.peer_range, &iface->addr[n].peer)) {
+ /* We prefer the already-configured address */
+ ncpaddr_getip4addr(&iface->addr[n].peer, (u_int32_t *)dec->nakend);
+ break;
+ }
+
+ if (n == iface->addrs)
+ memcpy(dec->nakend, &ipcp->peer_ip.s_addr, 4);
+
+ dec->nakend += 4;
+ return;
+ }
+
+ ipcp->peer_ip = ip;
+ *dec->ackend++ = TY_IPADDR;
+ *dec->ackend++ = 6;
+ memcpy(dec->ackend, &ip.s_addr, 4);
+ dec->ackend += 4;
+}
+
+static void
+IpcpDecodeConfig(struct fsm *fp, u_char *cp, u_char *end, int mode_type,
+ struct fsm_decode *dec)
+{
+ /* Deal with incoming PROTO_IPCP */
+ struct ncpaddr ncpaddr;
+ struct ipcp *ipcp = fsm2ipcp(fp);
+ int gotdnsnak;
+ u_int32_t compproto;
+ struct compreq *pcomp;
+ struct in_addr ipaddr, dstipaddr, have_ip;
+ char tbuff[100], tbuff2[100];
+ struct fsm_opt *opt, nak;
+
+ gotdnsnak = 0;
+
+ while (end - cp >= sizeof(opt->hdr)) {
+ if ((opt = fsm_readopt(&cp)) == NULL)
+ break;
+
+ snprintf(tbuff, sizeof tbuff, " %s[%d]", protoname(opt->hdr.id),
+ opt->hdr.len);
+
+ switch (opt->hdr.id) {
+ case TY_IPADDR: /* RFC1332 */
+ memcpy(&ipaddr.s_addr, opt->data, 4);
+ log_Printf(LogIPCP, "%s %s\n", tbuff, inet_ntoa(ipaddr));
+
+ switch (mode_type) {
+ case MODE_REQ:
+ ipcp->peer_req = 1;
+ ipcp_ValidateReq(ipcp, ipaddr, dec);
+ break;
+
+ case MODE_NAK:
+ if (ncprange_containsip4(&ipcp->cfg.my_range, ipaddr)) {
+ /* Use address suggested by peer */
+ snprintf(tbuff2, sizeof tbuff2, "%s changing address: %s ", tbuff,
+ inet_ntoa(ipcp->my_ip));
+ log_Printf(LogIPCP, "%s --> %s\n", tbuff2, inet_ntoa(ipaddr));
+ ipcp->my_ip = ipaddr;
+ ncpaddr_setip4(&ncpaddr, ipcp->my_ip);
+ bundle_AdjustFilters(fp->bundle, &ncpaddr, NULL);
+ } else {
+ log_Printf(log_IsKept(LogIPCP) ? LogIPCP : LogPHASE,
+ "%s: Unacceptable address!\n", inet_ntoa(ipaddr));
+ fsm_Close(&ipcp->fsm);
+ }
+ break;
+
+ case MODE_REJ:
+ ipcp->peer_reject |= (1 << opt->hdr.id);
+ break;
+ }
+ break;
+
+ case TY_COMPPROTO:
+ pcomp = (struct compreq *)opt->data;
+ compproto = (ntohs(pcomp->proto) << 16) + (pcomp->slots << 8) +
+ pcomp->compcid;
+ log_Printf(LogIPCP, "%s %s\n", tbuff, vj2asc(compproto));
+
+ switch (mode_type) {
+ case MODE_REQ:
+ if (!IsAccepted(ipcp->cfg.vj.neg))
+ fsm_rej(dec, opt);
+ else {
+ switch (opt->hdr.len) {
+ case 4: /* RFC1172 */
+ if (ntohs(pcomp->proto) == PROTO_VJCOMP) {
+ log_Printf(LogWARN, "Peer is speaking RFC1172 compression "
+ "protocol !\n");
+ ipcp->heis1172 = 1;
+ ipcp->peer_compproto = compproto;
+ fsm_ack(dec, opt);
+ } else {
+ pcomp->proto = htons(PROTO_VJCOMP);
+ nak.hdr.id = TY_COMPPROTO;
+ nak.hdr.len = 4;
+ memcpy(nak.data, &pcomp, 2);
+ fsm_nak(dec, &nak);
+ }
+ break;
+ case 6: /* RFC1332 */
+ if (ntohs(pcomp->proto) == PROTO_VJCOMP) {
+ if (pcomp->slots <= MAX_VJ_STATES
+ && pcomp->slots >= MIN_VJ_STATES) {
+ /* Ok, we can do that */
+ ipcp->peer_compproto = compproto;
+ ipcp->heis1172 = 0;
+ fsm_ack(dec, opt);
+ } else {
+ /* Get as close as we can to what he wants */
+ ipcp->heis1172 = 0;
+ pcomp->slots = pcomp->slots < MIN_VJ_STATES ?
+ MIN_VJ_STATES : MAX_VJ_STATES;
+ nak.hdr.id = TY_COMPPROTO;
+ nak.hdr.len = 4;
+ memcpy(nak.data, &pcomp, 2);
+ fsm_nak(dec, &nak);
+ }
+ } else {
+ /* What we really want */
+ pcomp->proto = htons(PROTO_VJCOMP);
+ pcomp->slots = DEF_VJ_STATES;
+ pcomp->compcid = 1;
+ nak.hdr.id = TY_COMPPROTO;
+ nak.hdr.len = 6;
+ memcpy(nak.data, &pcomp, sizeof pcomp);
+ fsm_nak(dec, &nak);
+ }
+ break;
+ default:
+ fsm_rej(dec, opt);
+ break;
+ }
+ }
+ break;
+
+ case MODE_NAK:
+ if (ntohs(pcomp->proto) == PROTO_VJCOMP) {
+ if (pcomp->slots > MAX_VJ_STATES)
+ pcomp->slots = MAX_VJ_STATES;
+ else if (pcomp->slots < MIN_VJ_STATES)
+ pcomp->slots = MIN_VJ_STATES;
+ compproto = (ntohs(pcomp->proto) << 16) + (pcomp->slots << 8) +
+ pcomp->compcid;
+ } else
+ compproto = 0;
+ log_Printf(LogIPCP, "%s changing compproto: %08x --> %08x\n",
+ tbuff, ipcp->my_compproto, compproto);
+ ipcp->my_compproto = compproto;
+ break;
+
+ case MODE_REJ:
+ ipcp->peer_reject |= (1 << opt->hdr.id);
+ break;
+ }
+ break;
+
+ case TY_IPADDRS: /* RFC1172 */
+ memcpy(&ipaddr.s_addr, opt->data, 4);
+ memcpy(&dstipaddr.s_addr, opt->data + 4, 4);
+ snprintf(tbuff2, sizeof tbuff2, "%s %s,", tbuff, inet_ntoa(ipaddr));
+ log_Printf(LogIPCP, "%s %s\n", tbuff2, inet_ntoa(dstipaddr));
+
+ switch (mode_type) {
+ case MODE_REQ:
+ fsm_rej(dec, opt);
+ break;
+
+ case MODE_NAK:
+ case MODE_REJ:
+ break;
+ }
+ break;
+
+ case TY_PRIMARY_DNS: /* DNS negotiation (rfc1877) */
+ case TY_SECONDARY_DNS:
+ memcpy(&ipaddr.s_addr, opt->data, 4);
+ log_Printf(LogIPCP, "%s %s\n", tbuff, inet_ntoa(ipaddr));
+
+ switch (mode_type) {
+ case MODE_REQ:
+ if (!IsAccepted(ipcp->cfg.ns.dns_neg)) {
+ ipcp->my_reject |= (1 << (opt->hdr.id - TY_ADJUST_NS));
+ fsm_rej(dec, opt);
+ break;
+ }
+ have_ip = ipcp->ns.dns[opt->hdr.id == TY_PRIMARY_DNS ? 0 : 1];
+
+ if (opt->hdr.id == TY_PRIMARY_DNS && ipaddr.s_addr != have_ip.s_addr &&
+ ipaddr.s_addr == ipcp->ns.dns[1].s_addr) {
+ /* Swap 'em 'round */
+ ipcp->ns.dns[0] = ipcp->ns.dns[1];
+ ipcp->ns.dns[1] = have_ip;
+ have_ip = ipcp->ns.dns[0];
+ }
+
+ if (ipaddr.s_addr != have_ip.s_addr) {
+ /*
+ * The client has got the DNS stuff wrong (first request) so
+ * we'll tell 'em how it is
+ */
+ nak.hdr.id = opt->hdr.id;
+ nak.hdr.len = 6;
+ memcpy(nak.data, &have_ip.s_addr, 4);
+ fsm_nak(dec, &nak);
+ } else {
+ /*
+ * Otherwise they have it right (this time) so we send a ack packet
+ * back confirming it... end of story
+ */
+ fsm_ack(dec, opt);
+ }
+ break;
+
+ case MODE_NAK:
+ if (IsEnabled(ipcp->cfg.ns.dns_neg)) {
+ gotdnsnak = 1;
+ memcpy(&ipcp->ns.dns[opt->hdr.id == TY_PRIMARY_DNS ? 0 : 1].s_addr,
+ opt->data, 4);
+ }
+ break;
+
+ case MODE_REJ: /* Can't do much, stop asking */
+ ipcp->peer_reject |= (1 << (opt->hdr.id - TY_ADJUST_NS));
+ break;
+ }
+ break;
+
+ case TY_PRIMARY_NBNS: /* M$ NetBIOS nameserver hack (rfc1877) */
+ case TY_SECONDARY_NBNS:
+ memcpy(&ipaddr.s_addr, opt->data, 4);
+ log_Printf(LogIPCP, "%s %s\n", tbuff, inet_ntoa(ipaddr));
+
+ switch (mode_type) {
+ case MODE_REQ:
+ have_ip.s_addr =
+ ipcp->cfg.ns.nbns[opt->hdr.id == TY_PRIMARY_NBNS ? 0 : 1].s_addr;
+
+ if (have_ip.s_addr == INADDR_ANY) {
+ log_Printf(LogIPCP, "NBNS REQ - rejected - nbns not set\n");
+ ipcp->my_reject |= (1 << (opt->hdr.id - TY_ADJUST_NS));
+ fsm_rej(dec, opt);
+ break;
+ }
+
+ if (ipaddr.s_addr != have_ip.s_addr) {
+ nak.hdr.id = opt->hdr.id;
+ nak.hdr.len = 6;
+ memcpy(nak.data, &have_ip.s_addr, 4);
+ fsm_nak(dec, &nak);
+ } else
+ fsm_ack(dec, opt);
+ break;
+
+ case MODE_NAK:
+ log_Printf(LogIPCP, "MS NBNS req %d - NAK??\n", opt->hdr.id);
+ break;
+
+ case MODE_REJ:
+ log_Printf(LogIPCP, "MS NBNS req %d - REJ??\n", opt->hdr.id);
+ break;
+ }
+ break;
+
+ default:
+ if (mode_type != MODE_NOP) {
+ ipcp->my_reject |= (1 << opt->hdr.id);
+ fsm_rej(dec, opt);
+ }
+ break;
+ }
+ }
+
+ if (gotdnsnak) {
+ if (ipcp->ns.writable) {
+ log_Printf(LogDEBUG, "Updating resolver\n");
+ if (!ipcp_WriteDNS(ipcp)) {
+ ipcp->peer_reject |= (1 << (TY_PRIMARY_DNS - TY_ADJUST_NS));
+ ipcp->peer_reject |= (1 << (TY_SECONDARY_DNS - TY_ADJUST_NS));
+ } else
+ bundle_AdjustDNS(fp->bundle);
+ } else {
+ log_Printf(LogDEBUG, "Not updating resolver (readonly)\n");
+ bundle_AdjustDNS(fp->bundle);
+ }
+ }
+
+ if (mode_type != MODE_NOP) {
+ if (mode_type == MODE_REQ && !ipcp->peer_req) {
+ if (dec->rejend == dec->rej && dec->nakend == dec->nak) {
+ /*
+ * Pretend the peer has requested an IP.
+ * We do this to ensure that we only send one NAK if the only
+ * reason for the NAK is because the peer isn't sending a
+ * TY_IPADDR REQ. This stops us from repeatedly trying to tell
+ * the peer that we have to have an IP address on their end.
+ */
+ ipcp->peer_req = 1;
+ }
+ ipaddr.s_addr = INADDR_ANY;
+ ipcp_ValidateReq(ipcp, ipaddr, dec);
+ }
+ fsm_opt_normalise(dec);
+ }
+}
+
+extern struct mbuf *
+ipcp_Input(struct bundle *bundle, struct link *l, struct mbuf *bp)
+{
+ /* Got PROTO_IPCP from link */
+ m_settype(bp, MB_IPCPIN);
+ if (bundle_Phase(bundle) == PHASE_NETWORK)
+ fsm_Input(&bundle->ncp.ipcp.fsm, bp);
+ else {
+ if (bundle_Phase(bundle) < PHASE_NETWORK)
+ log_Printf(LogIPCP, "%s: Error: Unexpected IPCP in phase %s (ignored)\n",
+ l->name, bundle_PhaseName(bundle));
+ m_freem(bp);
+ }
+ return NULL;
+}
+
+int
+ipcp_UseHisIPaddr(struct bundle *bundle, struct in_addr hisaddr)
+{
+ struct ipcp *ipcp = &bundle->ncp.ipcp;
+ struct in_addr myaddr;
+
+ memset(&ipcp->cfg.peer_range, '\0', sizeof ipcp->cfg.peer_range);
+ iplist_reset(&ipcp->cfg.peer_list);
+ ipcp->peer_ip = hisaddr;
+ ncprange_setip4host(&ipcp->cfg.peer_range, hisaddr);
+ ncprange_getip4addr(&ipcp->cfg.my_range, &myaddr);
+
+ return ipcp_SetIPaddress(ipcp, myaddr, hisaddr);
+}
+
+int
+ipcp_UseHisaddr(struct bundle *bundle, const char *hisaddr, int setaddr)
+{
+ struct in_addr myaddr;
+ struct ncp *ncp = &bundle->ncp;
+ struct ipcp *ipcp = &ncp->ipcp;
+ struct ncpaddr ncpaddr;
+
+ /* Use `hisaddr' for the peers address (set iface if `setaddr') */
+ memset(&ipcp->cfg.peer_range, '\0', sizeof ipcp->cfg.peer_range);
+ iplist_reset(&ipcp->cfg.peer_list);
+ if (strpbrk(hisaddr, ",-")) {
+ iplist_setsrc(&ipcp->cfg.peer_list, hisaddr);
+ if (iplist_isvalid(&ipcp->cfg.peer_list)) {
+ iplist_setrandpos(&ipcp->cfg.peer_list);
+ ipcp->peer_ip = ChooseHisAddr(bundle, ipcp->my_ip);
+ if (ipcp->peer_ip.s_addr == INADDR_ANY) {
+ log_Printf(LogWARN, "%s: None available !\n", ipcp->cfg.peer_list.src);
+ return 0;
+ }
+ ncprange_setip4host(&ipcp->cfg.peer_range, ipcp->peer_ip);
+ } else {
+ log_Printf(LogWARN, "%s: Invalid range !\n", hisaddr);
+ return 0;
+ }
+ } else if (ncprange_aton(&ipcp->cfg.peer_range, ncp, hisaddr) != 0) {
+ if (ncprange_family(&ipcp->cfg.my_range) != AF_INET) {
+ log_Printf(LogWARN, "%s: Not an AF_INET address !\n", hisaddr);
+ return 0;
+ }
+ ncprange_getip4addr(&ipcp->cfg.my_range, &myaddr);
+ ncprange_getip4addr(&ipcp->cfg.peer_range, &ipcp->peer_ip);
+
+ if (setaddr && !ipcp_SetIPaddress(ipcp, myaddr, ipcp->peer_ip))
+ return 0;
+ } else
+ return 0;
+
+ ncpaddr_setip4(&ncpaddr, ipcp->peer_ip);
+ bundle_AdjustFilters(bundle, NULL, &ncpaddr);
+
+ return 1; /* Ok */
+}
+
+struct in_addr
+addr2mask(struct in_addr addr)
+{
+ u_int32_t haddr = ntohl(addr.s_addr);
+
+ haddr = IN_CLASSA(haddr) ? IN_CLASSA_NET :
+ IN_CLASSB(haddr) ? IN_CLASSB_NET :
+ IN_CLASSC_NET;
+ addr.s_addr = htonl(haddr);
+
+ return addr;
+}
+
+size_t
+ipcp_QueueLen(struct ipcp *ipcp)
+{
+ struct mqueue *q;
+ size_t result;
+
+ result = 0;
+ for (q = ipcp->Queue; q < ipcp->Queue + IPCP_QUEUES(ipcp); q++)
+ result += q->len;
+
+ return result;
+}
+
+int
+ipcp_PushPacket(struct ipcp *ipcp, struct link *l)
+{
+ struct bundle *bundle = ipcp->fsm.bundle;
+ struct mqueue *queue;
+ struct mbuf *bp;
+ int m_len;
+ u_int32_t secs = 0;
+ unsigned alivesecs = 0;
+
+ if (ipcp->fsm.state != ST_OPENED)
+ return 0;
+
+ /*
+ * If ccp is not open but is required, do nothing.
+ */
+ if (l->ccp.fsm.state != ST_OPENED && ccp_Required(&l->ccp)) {
+ log_Printf(LogPHASE, "%s: Not transmitting... waiting for CCP\n", l->name);
+ return 0;
+ }
+
+ queue = ipcp->Queue + IPCP_QUEUES(ipcp) - 1;
+ do {
+ if (queue->top) {
+ bp = m_dequeue(queue);
+ bp = mbuf_Read(bp, &secs, sizeof secs);
+ bp = m_pullup(bp);
+ m_len = m_length(bp);
+ if (!FilterCheck(MBUF_CTOP(bp), AF_INET, &bundle->filter.alive,
+ &alivesecs)) {
+ if (secs == 0)
+ secs = alivesecs;
+ bundle_StartIdleTimer(bundle, secs);
+ }
+ link_PushPacket(l, bp, bundle, 0, PROTO_IP);
+ ipcp_AddOutOctets(ipcp, m_len);
+ return 1;
+ }
+ } while (queue-- != ipcp->Queue);
+
+ return 0;
+}
diff --git a/usr.sbin/ppp/ipcp.h b/usr.sbin/ppp/ipcp.h
new file mode 100644
index 0000000..dc1ed9e
--- /dev/null
+++ b/usr.sbin/ppp/ipcp.h
@@ -0,0 +1,132 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define IPCP_MAXCODE CODE_CODEREJ
+
+#define TY_IPADDRS 1
+#define TY_COMPPROTO 2
+#define TY_IPADDR 3
+
+/* Domain NameServer and NetBIOS NameServer options */
+
+#define TY_PRIMARY_DNS 129
+#define TY_PRIMARY_NBNS 130
+#define TY_SECONDARY_DNS 131
+#define TY_SECONDARY_NBNS 132
+#define TY_ADJUST_NS 119 /* subtract from NS val for REJECT bit */
+
+struct ipcp {
+ struct fsm fsm; /* The finite state machine */
+
+ struct {
+ struct {
+ int slots; /* Maximum VJ slots */
+ unsigned slotcomp : 1; /* Slot compression */
+ unsigned neg : 2; /* VJ negotiation */
+ } vj;
+
+ struct ncprange my_range; /* MYADDR spec */
+ struct in_addr netmask; /* Iface netmask (unused by most OSs) */
+ struct ncprange peer_range; /* HISADDR spec */
+ struct iplist peer_list; /* Ranges of HISADDR values */
+
+ struct in_addr TriggerAddress; /* Address to suggest in REQ */
+ unsigned HaveTriggerAddress : 1; /* Trigger address specified */
+
+ struct {
+ struct in_addr dns[2]; /* DNS addresses offered */
+ unsigned dns_neg : 2; /* dns negotiation */
+ struct in_addr nbns[2]; /* NetBIOS NS addresses offered */
+ } ns;
+
+ struct fsm_retry fsm; /* frequency to resend requests */
+ } cfg;
+
+ struct {
+ struct slcompress cslc; /* VJ state */
+ struct slstat slstat; /* VJ statistics */
+ } vj;
+
+ struct {
+ unsigned resolver : 1; /* Found resolv.conf ? */
+ unsigned writable : 1; /* Can write resolv.conf ? */
+ struct in_addr dns[2]; /* Current DNS addresses */
+ char *resolv; /* Contents of resolv.conf */
+ char *resolv_nons; /* Contents of resolv.conf without ns */
+ } ns;
+
+ unsigned heis1172 : 1; /* True if he is speaking rfc1172 */
+
+ unsigned peer_req : 1; /* Any TY_IPADDR REQs from the peer ? */
+ struct in_addr peer_ip; /* IP address he's willing to use */
+ u_int32_t peer_compproto; /* VJ params he's willing to use */
+
+ struct in_addr ifmask; /* Interface netmask */
+
+ struct in_addr my_ip; /* IP address I'm willing to use */
+ u_int32_t my_compproto; /* VJ params I'm willing to use */
+
+ u_int32_t peer_reject; /* Request codes rejected by peer */
+ u_int32_t my_reject; /* Request codes I have rejected */
+
+ struct pppThroughput throughput; /* throughput statistics */
+ struct mqueue Queue[3]; /* Output packet queues */
+};
+
+#define fsm2ipcp(fp) (fp->proto == PROTO_IPCP ? (struct ipcp *)fp : NULL)
+#define IPCP_QUEUES(ipcp) (sizeof ipcp->Queue / sizeof ipcp->Queue[0])
+
+struct bundle;
+struct link;
+struct cmdargs;
+struct iface_addr;
+
+extern void ipcp_Init(struct ipcp *, struct bundle *, struct link *,
+ const struct fsm_parent *);
+extern void ipcp_Destroy(struct ipcp *);
+extern void ipcp_Setup(struct ipcp *, u_int32_t);
+extern void ipcp_SetLink(struct ipcp *, struct link *);
+
+extern int ipcp_Show(struct cmdargs const *);
+extern struct mbuf *ipcp_Input(struct bundle *, struct link *, struct mbuf *);
+extern void ipcp_AddInOctets(struct ipcp *, int);
+extern void ipcp_AddOutOctets(struct ipcp *, int);
+extern int ipcp_UseHisIPaddr(struct bundle *, struct in_addr);
+extern int ipcp_UseHisaddr(struct bundle *, const char *, int);
+extern int ipcp_vjset(struct cmdargs const *);
+extern void ipcp_IfaceAddrAdded(struct ipcp *, const struct iface_addr *);
+extern void ipcp_IfaceAddrDeleted(struct ipcp *, const struct iface_addr *);
+extern int ipcp_InterfaceUp(struct ipcp *);
+extern struct in_addr addr2mask(struct in_addr);
+extern int ipcp_WriteDNS(struct ipcp *);
+extern void ipcp_RestoreDNS(struct ipcp *);
+extern void ipcp_LoadDNS(struct ipcp *);
+extern size_t ipcp_QueueLen(struct ipcp *);
+extern int ipcp_PushPacket(struct ipcp *, struct link *);
diff --git a/usr.sbin/ppp/iplist.c b/usr.sbin/ppp/iplist.c
new file mode 100644
index 0000000..4ef00e4
--- /dev/null
+++ b/usr.sbin/ppp/iplist.c
@@ -0,0 +1,225 @@
+/*-
+ * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "log.h"
+#include "defs.h"
+#include "iplist.h"
+
+static int
+do_inet_aton(const char *start, const char *end, struct in_addr *ip)
+{
+ char ipstr[16];
+
+ if (end - start > 15) {
+ log_Printf(LogWARN, "%.*s: Invalid IP address\n", (int)(end-start), start);
+ return 0;
+ }
+ strncpy(ipstr, start, end-start);
+ ipstr[end-start] = '\0';
+ return inet_aton(ipstr, ip);
+}
+
+static void
+iplist_first(struct iplist *list)
+{
+ list->cur.pos = -1;
+}
+
+static int
+iplist_setrange(struct iplist *list, char *range)
+{
+ char *ptr, *to;
+
+ if ((ptr = strpbrk(range, ",-")) == NULL) {
+ if (!inet_aton(range, &list->cur.ip))
+ return 0;
+ list->cur.lstart = ntohl(list->cur.ip.s_addr);
+ list->cur.nItems = 1;
+ } else {
+ if (!do_inet_aton(range, ptr, &list->cur.ip))
+ return 0;
+ if (*ptr == ',') {
+ list->cur.lstart = ntohl(list->cur.ip.s_addr);
+ list->cur.nItems = 1;
+ } else {
+ struct in_addr endip;
+
+ to = ptr+1;
+ if ((ptr = strpbrk(to, ",-")) == NULL)
+ ptr = to + strlen(to);
+ if (*to == '-')
+ return 0;
+ if (!do_inet_aton(to, ptr, &endip))
+ return 0;
+ list->cur.lstart = ntohl(list->cur.ip.s_addr);
+ list->cur.nItems = ntohl(endip.s_addr) - list->cur.lstart + 1;
+ if (list->cur.nItems < 1)
+ return 0;
+ }
+ }
+ list->cur.srcitem = 0;
+ list->cur.srcptr = range;
+ return 1;
+}
+
+static int
+iplist_nextrange(struct iplist *list)
+{
+ char *ptr, *to, *end;
+
+ ptr = list->cur.srcptr;
+ if (ptr != NULL && (ptr = strchr(ptr, ',')) != NULL)
+ ptr++;
+ else
+ ptr = list->src;
+
+ while (*ptr != '\0' && !iplist_setrange(list, ptr)) {
+ if ((end = strchr(ptr, ',')) == NULL)
+ end = ptr + strlen(ptr);
+ if (end == ptr)
+ return 0;
+ log_Printf(LogWARN, "%.*s: Invalid IP range (skipping)\n",
+ (int)(end - ptr), ptr);
+ to = ptr;
+ do
+ *to = *end++;
+ while (*to++ != '\0');
+ if (*ptr == '\0')
+ ptr = list->src;
+ }
+
+ return 1;
+}
+
+struct in_addr
+iplist_next(struct iplist *list)
+{
+ if (list->cur.pos == -1) {
+ list->cur.srcptr = NULL;
+ if (!iplist_nextrange(list)) {
+ list->cur.ip.s_addr = INADDR_ANY;
+ return list->cur.ip;
+ }
+ } else if (++list->cur.srcitem == list->cur.nItems) {
+ if (!iplist_nextrange(list)) {
+ list->cur.ip.s_addr = INADDR_ANY;
+ list->cur.pos = -1;
+ return list->cur.ip;
+ }
+ } else
+ list->cur.ip.s_addr = htonl(list->cur.lstart + list->cur.srcitem);
+ list->cur.pos++;
+
+ return list->cur.ip;
+}
+
+int
+iplist_setsrc(struct iplist *list, const char *src)
+{
+ strncpy(list->src, src, sizeof list->src - 1);
+ list->src[sizeof list->src - 1] = '\0';
+ list->cur.srcptr = list->src;
+ do {
+ if (iplist_nextrange(list))
+ list->nItems += list->cur.nItems;
+ else
+ return 0;
+ } while (list->cur.srcptr != list->src);
+ return 1;
+}
+
+void
+iplist_reset(struct iplist *list)
+{
+ list->src[0] = '\0';
+ list->nItems = 0;
+ list->cur.pos = -1;
+}
+
+struct in_addr
+iplist_setcurpos(struct iplist *list, long pos)
+{
+ if (pos < 0 || pos >= list->nItems) {
+ list->cur.pos = -1;
+ list->cur.ip.s_addr = INADDR_ANY;
+ return list->cur.ip;
+ }
+
+ list->cur.srcptr = NULL;
+ list->cur.pos = 0;
+ while (1) {
+ iplist_nextrange(list);
+ if (pos < list->cur.nItems) {
+ if (pos) {
+ list->cur.srcitem = pos;
+ list->cur.pos += pos;
+ list->cur.ip.s_addr = htonl(list->cur.lstart + list->cur.srcitem);
+ }
+ break;
+ }
+ pos -= list->cur.nItems;
+ list->cur.pos += list->cur.nItems;
+ }
+
+ return list->cur.ip;
+}
+
+struct in_addr
+iplist_setrandpos(struct iplist *list)
+{
+ randinit();
+ return iplist_setcurpos(list, random() % list->nItems);
+}
+
+int
+iplist_ip2pos(struct iplist *list, struct in_addr ip)
+{
+ struct iplist_cur cur;
+ u_long f;
+ int result;
+
+ result = -1;
+ memcpy(&cur, &list->cur, sizeof cur);
+
+ for (iplist_first(list), f = 0; f < list->nItems; f++)
+ if (iplist_next(list).s_addr == ip.s_addr) {
+ result = list->cur.pos;
+ break;
+ }
+
+ memcpy(&list->cur, &cur, sizeof list->cur);
+ return result;
+}
diff --git a/usr.sbin/ppp/iplist.h b/usr.sbin/ppp/iplist.h
new file mode 100644
index 0000000..5805a2c
--- /dev/null
+++ b/usr.sbin/ppp/iplist.h
@@ -0,0 +1,51 @@
+/*-
+ * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct iplist_cur {
+ struct in_addr ip;
+ int pos;
+ char *srcptr;
+ u_long srcitem;
+ u_int32_t lstart;
+ u_long nItems;
+};
+
+struct iplist {
+ struct iplist_cur cur;
+ u_long nItems;
+ char src[LINE_LEN];
+};
+
+extern int iplist_setsrc(struct iplist *, const char *);
+extern void iplist_reset(struct iplist *);
+extern struct in_addr iplist_setcurpos(struct iplist *, long);
+extern struct in_addr iplist_setrandpos(struct iplist *);
+extern int iplist_ip2pos(struct iplist *, struct in_addr);
+extern struct in_addr iplist_next(struct iplist *);
+
+#define iplist_isvalid(x) ((x)->src[0] != '\0')
diff --git a/usr.sbin/ppp/ipv6cp.c b/usr.sbin/ppp/ipv6cp.c
new file mode 100644
index 0000000..5db92bb
--- /dev/null
+++ b/usr.sbin/ppp/ipv6cp.c
@@ -0,0 +1,604 @@
+/*-
+ * Copyright (c) 2001 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <sys/socket.h>
+#include <net/route.h>
+#include <net/if.h>
+#include <sys/un.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "layer.h"
+#include "defs.h"
+#include "mbuf.h"
+#include "timer.h"
+#include "fsm.h"
+#include "iplist.h"
+#include "throughput.h"
+#include "slcompress.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "lcp.h"
+#include "ncpaddr.h"
+#include "ip.h"
+#include "ipcp.h"
+#include "ipv6cp.h"
+#include "filter.h"
+#include "descriptor.h"
+#include "ccp.h"
+#include "link.h"
+#include "mp.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ncp.h"
+#include "bundle.h"
+#include "route.h"
+#include "iface.h"
+#include "log.h"
+#include "proto.h"
+#include "command.h"
+#include "prompt.h"
+#include "async.h"
+#include "physical.h"
+#include "probe.h"
+
+
+#ifndef NOINET6
+static int ipv6cp_LayerUp(struct fsm *);
+static void ipv6cp_LayerDown(struct fsm *);
+static void ipv6cp_LayerStart(struct fsm *);
+static void ipv6cp_LayerFinish(struct fsm *);
+static void ipv6cp_InitRestartCounter(struct fsm *, int);
+static void ipv6cp_SendConfigReq(struct fsm *);
+static void ipv6cp_SentTerminateReq(struct fsm *);
+static void ipv6cp_SendTerminateAck(struct fsm *, u_char);
+static void ipv6cp_DecodeConfig(struct fsm *, u_char *, u_char *, int,
+ struct fsm_decode *);
+
+static struct fsm_callbacks ipv6cp_Callbacks = {
+ ipv6cp_LayerUp,
+ ipv6cp_LayerDown,
+ ipv6cp_LayerStart,
+ ipv6cp_LayerFinish,
+ ipv6cp_InitRestartCounter,
+ ipv6cp_SendConfigReq,
+ ipv6cp_SentTerminateReq,
+ ipv6cp_SendTerminateAck,
+ ipv6cp_DecodeConfig,
+ fsm_NullRecvResetReq,
+ fsm_NullRecvResetAck
+};
+
+static u_int32_t
+GenerateToken(void)
+{
+ /* Generate random number which will be used as negotiation token */
+ randinit();
+
+ return random() + 1;
+}
+
+static int
+ipcp_SetIPv6address(struct ipv6cp *ipv6cp, u_int32_t mytok, u_int32_t histok)
+{
+ struct bundle *bundle = ipv6cp->fsm.bundle;
+ struct in6_addr myaddr, hisaddr;
+ struct ncprange myrange;
+ struct sockaddr_storage ssdst, ssgw, ssmask;
+ struct sockaddr *sadst, *sagw, *samask;
+
+ sadst = (struct sockaddr *)&ssdst;
+ sagw = (struct sockaddr *)&ssgw;
+ samask = (struct sockaddr *)&ssmask;
+
+ memset(&myaddr, '\0', sizeof myaddr);
+ memset(&hisaddr, '\0', sizeof hisaddr);
+
+ myaddr.s6_addr[0] = 0xfe;
+ myaddr.s6_addr[1] = 0x80;
+ *(u_int32_t *)(myaddr.s6_addr + 12) = htonl(mytok);
+
+ hisaddr.s6_addr[0] = 0xfe;
+ hisaddr.s6_addr[1] = 0x80;
+ *(u_int32_t *)(hisaddr.s6_addr + 12) = htonl(histok);
+
+ ncpaddr_setip6(&ipv6cp->myaddr, &myaddr);
+ ncpaddr_setip6(&ipv6cp->hisaddr, &hisaddr);
+ ncprange_sethost(&myrange, &ipv6cp->myaddr);
+
+ if (!iface_Add(bundle->iface, &bundle->ncp, &myrange, &ipv6cp->hisaddr,
+ IFACE_ADD_FIRST|IFACE_FORCE_ADD|IFACE_SYSTEM))
+ return 0;
+
+ if (!Enabled(bundle, OPT_IFACEALIAS))
+ iface_Clear(bundle->iface, &bundle->ncp, AF_INET6,
+ IFACE_CLEAR_ALIASES|IFACE_SYSTEM);
+
+ if (bundle->ncp.cfg.sendpipe > 0 || bundle->ncp.cfg.recvpipe > 0) {
+ ncprange_getsa(&myrange, &ssgw, &ssmask);
+ if (ncpaddr_isset(&ipv6cp->hisaddr))
+ ncpaddr_getsa(&ipv6cp->hisaddr, &ssdst);
+ else
+ sadst = NULL;
+ rt_Update(bundle, sadst, sagw, samask);
+ }
+
+ if (Enabled(bundle, OPT_SROUTES))
+ route_Change(bundle, bundle->ncp.route, &ipv6cp->myaddr, &ipv6cp->hisaddr);
+
+#ifndef NORADIUS
+ if (bundle->radius.valid)
+ route_Change(bundle, bundle->radius.routes, &ipv6cp->myaddr,
+ &ipv6cp->hisaddr);
+#endif
+
+ return 1; /* Ok */
+}
+
+void
+ipv6cp_Init(struct ipv6cp *ipv6cp, struct bundle *bundle, struct link *l,
+ const struct fsm_parent *parent)
+{
+ static const char * const timer_names[] =
+ {"IPV6CP restart", "IPV6CP openmode", "IPV6CP stopped"};
+ int n;
+
+ fsm_Init(&ipv6cp->fsm, "IPV6CP", PROTO_IPV6CP, 1, IPV6CP_MAXCODE, LogIPV6CP,
+ bundle, l, parent, &ipv6cp_Callbacks, timer_names);
+
+ ipv6cp->cfg.fsm.timeout = DEF_FSMRETRY;
+ ipv6cp->cfg.fsm.maxreq = DEF_FSMTRIES;
+ ipv6cp->cfg.fsm.maxtrm = DEF_FSMTRIES;
+
+ ipv6cp->my_token = GenerateToken();
+ while ((ipv6cp->peer_token = GenerateToken()) == ipv6cp->my_token)
+ ;
+
+ if (probe.ipv6_available) {
+ n = 100;
+ while (n &&
+ !ipcp_SetIPv6address(ipv6cp, ipv6cp->my_token, ipv6cp->peer_token)) {
+ n--;
+ while (n && (ipv6cp->my_token = GenerateToken()) == ipv6cp->peer_token)
+ n--;
+ }
+ }
+
+ throughput_init(&ipv6cp->throughput, SAMPLE_PERIOD);
+ memset(ipv6cp->Queue, '\0', sizeof ipv6cp->Queue);
+ ipv6cp_Setup(ipv6cp);
+}
+
+void
+ipv6cp_Destroy(struct ipv6cp *ipv6cp)
+{
+ throughput_destroy(&ipv6cp->throughput);
+}
+
+void
+ipv6cp_Setup(struct ipv6cp *ipv6cp)
+{
+ ncpaddr_init(&ipv6cp->myaddr);
+ ncpaddr_init(&ipv6cp->hisaddr);
+
+ ipv6cp->his_reject = 0;
+ ipv6cp->my_reject = 0;
+}
+
+void
+ipv6cp_SetLink(struct ipv6cp *ipv6cp, struct link *l)
+{
+ ipv6cp->fsm.link = l;
+}
+
+int
+ipv6cp_Show(struct cmdargs const *arg)
+{
+ struct ipv6cp *ipv6cp = &arg->bundle->ncp.ipv6cp;
+
+ prompt_Printf(arg->prompt, "%s [%s]\n", ipv6cp->fsm.name,
+ State2Nam(ipv6cp->fsm.state));
+ if (ipv6cp->fsm.state == ST_OPENED) {
+ prompt_Printf(arg->prompt, " His side: %s\n",
+ ncpaddr_ntoa(&ipv6cp->hisaddr));
+ prompt_Printf(arg->prompt, " My side: %s\n",
+ ncpaddr_ntoa(&ipv6cp->myaddr));
+ prompt_Printf(arg->prompt, " Queued packets: %lu\n",
+ (unsigned long)ipv6cp_QueueLen(ipv6cp));
+ }
+
+ prompt_Printf(arg->prompt, "\nDefaults:\n");
+ prompt_Printf(arg->prompt, " FSM retry = %us, max %u Config"
+ " REQ%s, %u Term REQ%s\n\n", ipv6cp->cfg.fsm.timeout,
+ ipv6cp->cfg.fsm.maxreq, ipv6cp->cfg.fsm.maxreq == 1 ? "" : "s",
+ ipv6cp->cfg.fsm.maxtrm, ipv6cp->cfg.fsm.maxtrm == 1 ? "" : "s");
+
+ throughput_disp(&ipv6cp->throughput, arg->prompt);
+
+ return 0;
+}
+
+struct mbuf *
+ipv6cp_Input(struct bundle *bundle, struct link *l, struct mbuf *bp)
+{
+ /* Got PROTO_IPV6CP from link */
+ m_settype(bp, MB_IPV6CPIN);
+ if (bundle_Phase(bundle) == PHASE_NETWORK)
+ fsm_Input(&bundle->ncp.ipv6cp.fsm, bp);
+ else {
+ if (bundle_Phase(bundle) < PHASE_NETWORK)
+ log_Printf(LogIPV6CP, "%s: Error: Unexpected IPV6CP in phase %s"
+ " (ignored)\n", l->name, bundle_PhaseName(bundle));
+ m_freem(bp);
+ }
+ return NULL;
+}
+
+void
+ipv6cp_AddInOctets(struct ipv6cp *ipv6cp, int n)
+{
+ throughput_addin(&ipv6cp->throughput, n);
+}
+
+void
+ipv6cp_AddOutOctets(struct ipv6cp *ipv6cp, int n)
+{
+ throughput_addout(&ipv6cp->throughput, n);
+}
+
+void
+ipv6cp_IfaceAddrAdded(struct ipv6cp *ipv6cp, const struct iface_addr *addr)
+{
+}
+
+void
+ipv6cp_IfaceAddrDeleted(struct ipv6cp *ipv6cp, const struct iface_addr *addr)
+{
+}
+
+int
+ipv6cp_InterfaceUp(struct ipv6cp *ipv6cp)
+{
+ if (!ipcp_SetIPv6address(ipv6cp, ipv6cp->my_token, ipv6cp->peer_token)) {
+ log_Printf(LogERROR, "ipv6cp_InterfaceUp: unable to set ipv6 address\n");
+ return 0;
+ }
+
+ if (!iface_SetFlags(ipv6cp->fsm.bundle->iface->name, IFF_UP)) {
+ log_Printf(LogERROR, "ipv6cp_InterfaceUp: Can't set the IFF_UP"
+ " flag on %s\n", ipv6cp->fsm.bundle->iface->name);
+ return 0;
+ }
+
+ return 1;
+}
+
+size_t
+ipv6cp_QueueLen(struct ipv6cp *ipv6cp)
+{
+ struct mqueue *q;
+ size_t result;
+
+ result = 0;
+ for (q = ipv6cp->Queue; q < ipv6cp->Queue + IPV6CP_QUEUES(ipv6cp); q++)
+ result += q->len;
+
+ return result;
+}
+
+int
+ipv6cp_PushPacket(struct ipv6cp *ipv6cp, struct link *l)
+{
+ struct bundle *bundle = ipv6cp->fsm.bundle;
+ struct mqueue *queue;
+ struct mbuf *bp;
+ int m_len;
+ u_int32_t secs = 0;
+ unsigned alivesecs = 0;
+
+ if (ipv6cp->fsm.state != ST_OPENED)
+ return 0;
+
+ /*
+ * If ccp is not open but is required, do nothing.
+ */
+ if (l->ccp.fsm.state != ST_OPENED && ccp_Required(&l->ccp)) {
+ log_Printf(LogPHASE, "%s: Not transmitting... waiting for CCP\n", l->name);
+ return 0;
+ }
+
+ queue = ipv6cp->Queue + IPV6CP_QUEUES(ipv6cp) - 1;
+ do {
+ if (queue->top) {
+ bp = m_dequeue(queue);
+ bp = mbuf_Read(bp, &secs, sizeof secs);
+ bp = m_pullup(bp);
+ m_len = m_length(bp);
+ if (!FilterCheck(MBUF_CTOP(bp), AF_INET6, &bundle->filter.alive,
+ &alivesecs)) {
+ if (secs == 0)
+ secs = alivesecs;
+ bundle_StartIdleTimer(bundle, secs);
+ }
+ link_PushPacket(l, bp, bundle, 0, PROTO_IPV6);
+ ipv6cp_AddOutOctets(ipv6cp, m_len);
+ return 1;
+ }
+ } while (queue-- != ipv6cp->Queue);
+
+ return 0;
+}
+
+static int
+ipv6cp_LayerUp(struct fsm *fp)
+{
+ /* We're now up */
+ struct ipv6cp *ipv6cp = fsm2ipv6cp(fp);
+ char tbuff[40];
+
+ log_Printf(LogIPV6CP, "%s: LayerUp.\n", fp->link->name);
+ if (!ipv6cp_InterfaceUp(ipv6cp))
+ return 0;
+
+ snprintf(tbuff, sizeof tbuff, "%s", ncpaddr_ntoa(&ipv6cp->myaddr));
+ log_Printf(LogIPV6CP, "myaddr %s hisaddr = %s\n",
+ tbuff, ncpaddr_ntoa(&ipv6cp->hisaddr));
+
+ /* XXX: Call radius_Account() and system_Select() */
+
+ fp->more.reqs = fp->more.naks = fp->more.rejs = ipv6cp->cfg.fsm.maxreq * 3;
+ log_DisplayPrompts();
+
+ return 1;
+}
+
+static void
+ipv6cp_LayerDown(struct fsm *fp)
+{
+ /* About to come down */
+ struct ipv6cp *ipv6cp = fsm2ipv6cp(fp);
+ static int recursing;
+ char addr[40];
+
+ if (!recursing++) {
+ snprintf(addr, sizeof addr, "%s", ncpaddr_ntoa(&ipv6cp->myaddr));
+ log_Printf(LogIPV6CP, "%s: LayerDown: %s\n", fp->link->name, addr);
+
+ /* XXX: Call radius_Account() and system_Select() */
+
+ ipv6cp_Setup(ipv6cp);
+ }
+ recursing--;
+}
+
+static void
+ipv6cp_LayerStart(struct fsm *fp)
+{
+ /* We're about to start up ! */
+ struct ipv6cp *ipv6cp = fsm2ipv6cp(fp);
+
+ log_Printf(LogIPV6CP, "%s: LayerStart.\n", fp->link->name);
+ throughput_start(&ipv6cp->throughput, "IPV6CP throughput",
+ Enabled(fp->bundle, OPT_THROUGHPUT));
+ fp->more.reqs = fp->more.naks = fp->more.rejs = ipv6cp->cfg.fsm.maxreq * 3;
+ ipv6cp->peer_tokenreq = 0;
+}
+
+static void
+ipv6cp_LayerFinish(struct fsm *fp)
+{
+ /* We're now down */
+ struct ipv6cp *ipv6cp = fsm2ipv6cp(fp);
+
+ log_Printf(LogIPV6CP, "%s: LayerFinish.\n", fp->link->name);
+ throughput_stop(&ipv6cp->throughput);
+ throughput_log(&ipv6cp->throughput, LogIPV6CP, NULL);
+}
+
+static void
+ipv6cp_InitRestartCounter(struct fsm *fp, int what)
+{
+ /* Set fsm timer load */
+ struct ipv6cp *ipv6cp = fsm2ipv6cp(fp);
+
+ fp->FsmTimer.load = ipv6cp->cfg.fsm.timeout * SECTICKS;
+ switch (what) {
+ case FSM_REQ_TIMER:
+ fp->restart = ipv6cp->cfg.fsm.maxreq;
+ break;
+ case FSM_TRM_TIMER:
+ fp->restart = ipv6cp->cfg.fsm.maxtrm;
+ break;
+ default:
+ fp->restart = 1;
+ break;
+ }
+}
+
+static void
+ipv6cp_SendConfigReq(struct fsm *fp)
+{
+ /* Send config REQ please */
+ struct physical *p = link2physical(fp->link);
+ struct ipv6cp *ipv6cp = fsm2ipv6cp(fp);
+ u_char buff[6];
+ struct fsm_opt *o;
+
+ o = (struct fsm_opt *)buff;
+
+ if ((p && !physical_IsSync(p)) || !REJECTED(ipv6cp, TY_TOKEN)) {
+ memcpy(o->data, &ipv6cp->my_token, 4);
+ INC_FSM_OPT(TY_TOKEN, 6, o);
+ }
+
+ fsm_Output(fp, CODE_CONFIGREQ, fp->reqid, buff, (u_char *)o - buff,
+ MB_IPV6CPOUT);
+}
+
+static void
+ipv6cp_SentTerminateReq(struct fsm *fp)
+{
+ /* Term REQ just sent by FSM */
+}
+
+static void
+ipv6cp_SendTerminateAck(struct fsm *fp, u_char id)
+{
+ /* Send Term ACK please */
+ fsm_Output(fp, CODE_TERMACK, id, NULL, 0, MB_IPV6CPOUT);
+}
+
+static const char *
+protoname(int proto)
+{
+ static const char *cftypes[] = { "TOKEN", "COMPPROTO" };
+
+ if (proto > 0 && proto <= sizeof cftypes / sizeof *cftypes)
+ return cftypes[proto - 1];
+
+ return NumStr(proto, NULL, 0);
+}
+
+static void
+ipv6cp_ValidateToken(struct ipv6cp *ipv6cp, u_int32_t token,
+ struct fsm_decode *dec)
+{
+ struct fsm_opt opt;
+
+ if (token != 0 && token != ipv6cp->my_token)
+ ipv6cp->peer_token = token;
+
+ opt.hdr.id = TY_TOKEN;
+ opt.hdr.len = 6;
+ memcpy(opt.data, &ipv6cp->peer_token, 4);
+ if (token == ipv6cp->peer_token)
+ fsm_ack(dec, &opt);
+ else
+ fsm_nak(dec, &opt);
+}
+
+static void
+ipv6cp_DecodeConfig(struct fsm *fp, u_char *cp, u_char *end, int mode_type,
+ struct fsm_decode *dec)
+{
+ /* Deal with incoming PROTO_IPV6CP */
+ struct ipv6cp *ipv6cp = fsm2ipv6cp(fp);
+ int n;
+ char tbuff[100];
+ u_int32_t token;
+ struct fsm_opt *opt;
+
+ while (end - cp >= sizeof(opt->hdr)) {
+ if ((opt = fsm_readopt(&cp)) == NULL)
+ break;
+
+ snprintf(tbuff, sizeof tbuff, " %s[%d]", protoname(opt->hdr.id),
+ opt->hdr.len);
+
+ switch (opt->hdr.id) {
+ case TY_TOKEN:
+ memcpy(&token, opt->data, 4);
+ log_Printf(LogIPV6CP, "%s 0x%08lx\n", tbuff, (unsigned long)token);
+
+ switch (mode_type) {
+ case MODE_REQ:
+ ipv6cp->peer_tokenreq = 1;
+ ipv6cp_ValidateToken(ipv6cp, token, dec);
+ break;
+
+ case MODE_NAK:
+ if (token == 0) {
+ log_Printf(log_IsKept(LogIPV6CP) ? LogIPV6CP : LogPHASE,
+ "0x00000000: Unacceptable token!\n");
+ fsm_Close(&ipv6cp->fsm);
+ } else if (token == ipv6cp->peer_token)
+ log_Printf(log_IsKept(LogIPV6CP) ? LogIPV6CP : LogPHASE,
+ "0x%08lx: Unacceptable token!\n", (unsigned long)token);
+ else if (token != ipv6cp->my_token) {
+ n = 100;
+ while (n && !ipcp_SetIPv6address(ipv6cp, token, ipv6cp->peer_token)) {
+ n--;
+ while (n && (token = GenerateToken()) == ipv6cp->peer_token)
+ n--;
+ }
+
+ if (n == 0) {
+ log_Printf(log_IsKept(LogIPV6CP) ? LogIPV6CP : LogPHASE,
+ "0x00000000: Unacceptable token!\n");
+ fsm_Close(&ipv6cp->fsm);
+ } else {
+ log_Printf(LogIPV6CP, "%s changing token: 0x%08lx --> 0x%08lx\n",
+ tbuff, (unsigned long)ipv6cp->my_token,
+ (unsigned long)token);
+ ipv6cp->my_token = token;
+ bundle_AdjustFilters(fp->bundle, &ipv6cp->myaddr, NULL);
+ }
+ }
+ break;
+
+ case MODE_REJ:
+ ipv6cp->his_reject |= (1 << opt->hdr.id);
+ break;
+ }
+ break;
+
+ default:
+ if (mode_type != MODE_NOP) {
+ ipv6cp->my_reject |= (1 << opt->hdr.id);
+ fsm_rej(dec, opt);
+ }
+ break;
+ }
+ }
+
+ if (mode_type != MODE_NOP) {
+ if (mode_type == MODE_REQ && !ipv6cp->peer_tokenreq) {
+ if (dec->rejend == dec->rej && dec->nakend == dec->nak) {
+ /*
+ * Pretend the peer has requested a TOKEN.
+ * We do this to ensure that we only send one NAK if the only
+ * reason for the NAK is because the peer isn't sending a
+ * TY_TOKEN REQ. This stops us from repeatedly trying to tell
+ * the peer that we have to have an IP address on their end.
+ */
+ ipv6cp->peer_tokenreq = 1;
+ }
+ ipv6cp_ValidateToken(ipv6cp, 0, dec);
+ }
+ fsm_opt_normalise(dec);
+ }
+}
+#endif
diff --git a/usr.sbin/ppp/ipv6cp.h b/usr.sbin/ppp/ipv6cp.h
new file mode 100644
index 0000000..a2c1c63
--- /dev/null
+++ b/usr.sbin/ppp/ipv6cp.h
@@ -0,0 +1,81 @@
+/*-
+ * Copyright (c) 2001 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef NOINET6
+#define IPV6CP_MAXCODE CODE_CODEREJ
+
+#define TY_TOKEN 1
+#define TY_COMPPROTO 2
+
+struct ipv6cp {
+ struct fsm fsm; /* The finite state machine */
+
+ struct {
+ struct fsm_retry fsm; /* frequency to resend requests */
+ } cfg;
+
+ unsigned peer_tokenreq : 1; /* Any TY_TOKEN REQs from the peer ? */
+
+ u_int32_t my_token; /* Token I'm willing to use */
+ u_int32_t peer_token; /* Token he's willing to use */
+
+ struct ncpaddr myaddr; /* Local address */
+ struct ncpaddr hisaddr; /* Peer address */
+
+ u_int32_t his_reject; /* Request codes rejected by peer */
+ u_int32_t my_reject; /* Request codes I have rejected */
+
+ struct pppThroughput throughput; /* throughput statistics */
+ struct mqueue Queue[2]; /* Output packet queues */
+};
+
+#define fsm2ipv6cp(fp) (fp->proto == PROTO_IPV6CP ? (struct ipv6cp *)fp : NULL)
+#define IPV6CP_QUEUES(ipv6cp) (sizeof ipv6cp->Queue / sizeof ipv6cp->Queue[0])
+
+struct bundle;
+struct link;
+struct cmdargs;
+struct iface_addr;
+
+extern void ipv6cp_Init(struct ipv6cp *, struct bundle *, struct link *,
+ const struct fsm_parent *);
+extern void ipv6cp_Destroy(struct ipv6cp *);
+extern void ipv6cp_Setup(struct ipv6cp *);
+extern void ipv6cp_SetLink(struct ipv6cp *, struct link *);
+
+extern int ipv6cp_Show(struct cmdargs const *);
+extern struct mbuf *ipv6cp_Input(struct bundle *, struct link *, struct mbuf *);
+extern void ipv6cp_AddInOctets(struct ipv6cp *, int);
+extern void ipv6cp_AddOutOctets(struct ipv6cp *, int);
+
+extern void ipv6cp_IfaceAddrAdded(struct ipv6cp *, const struct iface_addr *);
+extern void ipv6cp_IfaceAddrDeleted(struct ipv6cp *, const struct iface_addr *);
+extern int ipv6cp_InterfaceUp(struct ipv6cp *);
+extern size_t ipv6cp_QueueLen(struct ipv6cp *);
+extern int ipv6cp_PushPacket(struct ipv6cp *, struct link *);
+#endif
diff --git a/usr.sbin/ppp/layer.h b/usr.sbin/ppp/layer.h
new file mode 100644
index 0000000..4ee59a1
--- /dev/null
+++ b/usr.sbin/ppp/layer.h
@@ -0,0 +1,52 @@
+/*-
+ * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define LAYER_ASYNC 2
+#define LAYER_SYNC 3
+#define LAYER_HDLC 4
+#define LAYER_ACF 5
+#define LAYER_PROTO 6
+#define LAYER_LQR 7
+#define LAYER_CCP 8
+#define LAYER_VJ 9
+#define LAYER_NAT 10
+
+#define LAYER_MAX 10 /* How many layers we can handle on a link */
+
+struct mbuf;
+struct link;
+struct bundle;
+
+struct layer {
+ int type;
+ const char *name;
+ struct mbuf *(*push)(struct bundle *, struct link *, struct mbuf *,
+ int pri, u_short *proto);
+ struct mbuf *(*pull)(struct bundle *, struct link *, struct mbuf *,
+ u_short *);
+};
diff --git a/usr.sbin/ppp/lcp.c b/usr.sbin/ppp/lcp.c
new file mode 100644
index 0000000..e620a2f
--- /dev/null
+++ b/usr.sbin/ppp/lcp.c
@@ -0,0 +1,1291 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "layer.h"
+#include "ua.h"
+#include "defs.h"
+#include "command.h"
+#include "mbuf.h"
+#include "log.h"
+#include "timer.h"
+#include "fsm.h"
+#include "iplist.h"
+#include "throughput.h"
+#include "proto.h"
+#include "descriptor.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "async.h"
+#include "link.h"
+#include "physical.h"
+#include "prompt.h"
+#include "slcompress.h"
+#include "ncpaddr.h"
+#include "ipcp.h"
+#include "filter.h"
+#include "mp.h"
+#include "chat.h"
+#include "auth.h"
+#include "chap.h"
+#include "cbcp.h"
+#include "datalink.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+
+/* for received LQRs */
+struct lqrreq {
+ struct fsm_opt_hdr hdr;
+ u_short proto; /* Quality protocol */
+ u_int32_t period; /* Reporting interval */
+};
+
+static int LcpLayerUp(struct fsm *);
+static void LcpLayerDown(struct fsm *);
+static void LcpLayerStart(struct fsm *);
+static void LcpLayerFinish(struct fsm *);
+static void LcpInitRestartCounter(struct fsm *, int);
+static void LcpSendConfigReq(struct fsm *);
+static void LcpSentTerminateReq(struct fsm *);
+static void LcpSendTerminateAck(struct fsm *, u_char);
+static void LcpDecodeConfig(struct fsm *, u_char *, u_char *, int,
+ struct fsm_decode *);
+
+static struct fsm_callbacks lcp_Callbacks = {
+ LcpLayerUp,
+ LcpLayerDown,
+ LcpLayerStart,
+ LcpLayerFinish,
+ LcpInitRestartCounter,
+ LcpSendConfigReq,
+ LcpSentTerminateReq,
+ LcpSendTerminateAck,
+ LcpDecodeConfig,
+ fsm_NullRecvResetReq,
+ fsm_NullRecvResetAck
+};
+
+static const char * const lcp_TimerNames[] =
+ {"LCP restart", "LCP openmode", "LCP stopped"};
+
+static const char *
+protoname(int proto)
+{
+ static const char * const cftypes[] = {
+ /* Check out the latest ``Assigned numbers'' rfc (1700) */
+ NULL,
+ "MRU", /* 1: Maximum-Receive-Unit */
+ "ACCMAP", /* 2: Async-Control-Character-Map */
+ "AUTHPROTO", /* 3: Authentication-Protocol */
+ "QUALPROTO", /* 4: Quality-Protocol */
+ "MAGICNUM", /* 5: Magic-Number */
+ "RESERVED", /* 6: RESERVED */
+ "PROTOCOMP", /* 7: Protocol-Field-Compression */
+ "ACFCOMP", /* 8: Address-and-Control-Field-Compression */
+ "FCSALT", /* 9: FCS-Alternatives */
+ "SDP", /* 10: Self-Describing-Pad */
+ "NUMMODE", /* 11: Numbered-Mode */
+ "MULTIPROC", /* 12: Multi-Link-Procedure */
+ "CALLBACK", /* 13: Callback */
+ "CONTIME", /* 14: Connect-Time */
+ "COMPFRAME", /* 15: Compound-Frames */
+ "NDE", /* 16: Nominal-Data-Encapsulation */
+ "MRRU", /* 17: Multilink-MRRU */
+ "SHORTSEQ", /* 18: Multilink-Short-Sequence-Number-Header */
+ "ENDDISC", /* 19: Multilink-Endpoint-Discriminator */
+ "PROPRIETRY", /* 20: Proprietary */
+ "DCEID", /* 21: DCE-Identifier */
+ "MULTIPP", /* 22: Multi-Link-Plus-Procedure */
+ "LDBACP", /* 23: Link Discriminator for BACP */
+ };
+
+ if (proto < 0 || proto > sizeof cftypes / sizeof *cftypes ||
+ cftypes[proto] == NULL)
+ return HexStr(proto, NULL, 0);
+
+ return cftypes[proto];
+}
+
+int
+lcp_ReportStatus(struct cmdargs const *arg)
+{
+ struct link *l;
+ struct lcp *lcp;
+
+ l = command_ChooseLink(arg);
+ lcp = &l->lcp;
+
+ prompt_Printf(arg->prompt, "%s: %s [%s]\n", l->name, lcp->fsm.name,
+ State2Nam(lcp->fsm.state));
+ prompt_Printf(arg->prompt,
+ " his side: MRU %d, ACCMAP %08lx, PROTOCOMP %s, ACFCOMP %s,\n"
+ " MAGIC %08lx, MRRU %u, SHORTSEQ %s, REJECT %04x\n",
+ lcp->his_mru, (u_long)lcp->his_accmap,
+ lcp->his_protocomp ? "on" : "off",
+ lcp->his_acfcomp ? "on" : "off",
+ (u_long)lcp->his_magic, lcp->his_mrru,
+ lcp->his_shortseq ? "on" : "off", lcp->his_reject);
+ prompt_Printf(arg->prompt,
+ " my side: MRU %d, ACCMAP %08lx, PROTOCOMP %s, ACFCOMP %s,\n"
+ " MAGIC %08lx, MRRU %u, SHORTSEQ %s, REJECT %04x\n",
+ lcp->want_mru, (u_long)lcp->want_accmap,
+ lcp->want_protocomp ? "on" : "off",
+ lcp->want_acfcomp ? "on" : "off",
+ (u_long)lcp->want_magic, lcp->want_mrru,
+ lcp->want_shortseq ? "on" : "off", lcp->my_reject);
+
+ if (lcp->cfg.mru)
+ prompt_Printf(arg->prompt, "\n Defaults: MRU = %d (max %d), ",
+ lcp->cfg.mru, lcp->cfg.max_mru);
+ else
+ prompt_Printf(arg->prompt, "\n Defaults: MRU = any (max %d), ",
+ lcp->cfg.max_mru);
+ if (lcp->cfg.mtu)
+ prompt_Printf(arg->prompt, "MTU = %d (max %d), ",
+ lcp->cfg.mtu, lcp->cfg.max_mtu);
+ else
+ prompt_Printf(arg->prompt, "MTU = any (max %d), ", lcp->cfg.max_mtu);
+ prompt_Printf(arg->prompt, "ACCMAP = %08lx\n", (u_long)lcp->cfg.accmap);
+ prompt_Printf(arg->prompt, " LQR period = %us, ",
+ lcp->cfg.lqrperiod);
+ prompt_Printf(arg->prompt, "Open Mode = %s",
+ lcp->cfg.openmode == OPEN_PASSIVE ? "passive" : "active");
+ if (lcp->cfg.openmode > 0)
+ prompt_Printf(arg->prompt, " (delay %ds)", lcp->cfg.openmode);
+ prompt_Printf(arg->prompt, "\n FSM retry = %us, max %u Config"
+ " REQ%s, %u Term REQ%s\n", lcp->cfg.fsm.timeout,
+ lcp->cfg.fsm.maxreq, lcp->cfg.fsm.maxreq == 1 ? "" : "s",
+ lcp->cfg.fsm.maxtrm, lcp->cfg.fsm.maxtrm == 1 ? "" : "s");
+ prompt_Printf(arg->prompt, " Ident: %s\n", lcp->cfg.ident);
+ prompt_Printf(arg->prompt, "\n Negotiation:\n");
+ prompt_Printf(arg->prompt, " ACFCOMP = %s\n",
+ command_ShowNegval(lcp->cfg.acfcomp));
+ prompt_Printf(arg->prompt, " CHAP = %s\n",
+ command_ShowNegval(lcp->cfg.chap05));
+#ifndef NODES
+ prompt_Printf(arg->prompt, " CHAP80 = %s\n",
+ command_ShowNegval(lcp->cfg.chap80nt));
+ prompt_Printf(arg->prompt, " LANMan = %s\n",
+ command_ShowNegval(lcp->cfg.chap80lm));
+ prompt_Printf(arg->prompt, " CHAP81 = %s\n",
+ command_ShowNegval(lcp->cfg.chap81));
+#endif
+ prompt_Printf(arg->prompt, " LQR = %s\n",
+ command_ShowNegval(lcp->cfg.lqr));
+ prompt_Printf(arg->prompt, " PAP = %s\n",
+ command_ShowNegval(lcp->cfg.pap));
+ prompt_Printf(arg->prompt, " PROTOCOMP = %s\n",
+ command_ShowNegval(lcp->cfg.protocomp));
+
+ return 0;
+}
+
+static u_int32_t
+GenerateMagic(void)
+{
+ /* Generate random number which will be used as magic number */
+ randinit();
+ return random();
+}
+
+void
+lcp_SetupCallbacks(struct lcp *lcp)
+{
+ lcp->fsm.fn = &lcp_Callbacks;
+ lcp->fsm.FsmTimer.name = lcp_TimerNames[0];
+ lcp->fsm.OpenTimer.name = lcp_TimerNames[1];
+ lcp->fsm.StoppedTimer.name = lcp_TimerNames[2];
+}
+
+void
+lcp_Init(struct lcp *lcp, struct bundle *bundle, struct link *l,
+ const struct fsm_parent *parent)
+{
+ /* Initialise ourselves */
+ int mincode = parent ? 1 : LCP_MINMPCODE;
+
+ fsm_Init(&lcp->fsm, "LCP", PROTO_LCP, mincode, LCP_MAXCODE, LogLCP,
+ bundle, l, parent, &lcp_Callbacks, lcp_TimerNames);
+
+ lcp->cfg.mru = 0;
+ lcp->cfg.max_mru = MAX_MRU;
+ lcp->cfg.mtu = 0;
+ lcp->cfg.max_mtu = MAX_MTU;
+ lcp->cfg.accmap = 0;
+ lcp->cfg.openmode = 1;
+ lcp->cfg.lqrperiod = DEF_LQRPERIOD;
+ lcp->cfg.fsm.timeout = DEF_FSMRETRY;
+ lcp->cfg.fsm.maxreq = DEF_FSMTRIES;
+ lcp->cfg.fsm.maxtrm = DEF_FSMTRIES;
+
+ lcp->cfg.acfcomp = NEG_ENABLED|NEG_ACCEPTED;
+ lcp->cfg.chap05 = NEG_ACCEPTED;
+#ifndef NODES
+ lcp->cfg.chap80nt = NEG_ACCEPTED;
+ lcp->cfg.chap80lm = 0;
+ lcp->cfg.chap81 = NEG_ACCEPTED;
+#endif
+ lcp->cfg.lqr = NEG_ACCEPTED;
+ lcp->cfg.pap = NEG_ACCEPTED;
+ lcp->cfg.protocomp = NEG_ENABLED|NEG_ACCEPTED;
+ *lcp->cfg.ident = '\0';
+
+ lcp_Setup(lcp, lcp->cfg.openmode);
+}
+
+void
+lcp_Setup(struct lcp *lcp, int openmode)
+{
+ struct physical *p = link2physical(lcp->fsm.link);
+
+ lcp->fsm.open_mode = openmode;
+
+ lcp->his_mru = DEF_MRU;
+ lcp->his_mrru = 0;
+ lcp->his_magic = 0;
+ lcp->his_lqrperiod = 0;
+ lcp->his_acfcomp = 0;
+ lcp->his_auth = 0;
+ lcp->his_authtype = 0;
+ lcp->his_callback.opmask = 0;
+ lcp->his_shortseq = 0;
+ lcp->mru_req = 0;
+
+ if ((lcp->want_mru = lcp->cfg.mru) == 0)
+ lcp->want_mru = DEF_MRU;
+ lcp->want_mrru = lcp->fsm.bundle->ncp.mp.cfg.mrru;
+ lcp->want_shortseq = IsEnabled(lcp->fsm.bundle->ncp.mp.cfg.shortseq) ? 1 : 0;
+ lcp->want_acfcomp = IsEnabled(lcp->cfg.acfcomp) ? 1 : 0;
+
+ if (lcp->fsm.parent) {
+ lcp->his_accmap = 0xffffffff;
+ lcp->want_accmap = lcp->cfg.accmap;
+ lcp->his_protocomp = 0;
+ lcp->want_protocomp = IsEnabled(lcp->cfg.protocomp) ? 1 : 0;
+ lcp->want_magic = GenerateMagic();
+
+ if (IsEnabled(lcp->cfg.chap05)) {
+ lcp->want_auth = PROTO_CHAP;
+ lcp->want_authtype = 0x05;
+#ifndef NODES
+ } else if (IsEnabled(lcp->cfg.chap80nt) ||
+ IsEnabled(lcp->cfg.chap80lm)) {
+ lcp->want_auth = PROTO_CHAP;
+ lcp->want_authtype = 0x80;
+ } else if (IsEnabled(lcp->cfg.chap81)) {
+ lcp->want_auth = PROTO_CHAP;
+ lcp->want_authtype = 0x81;
+#endif
+ } else if (IsEnabled(lcp->cfg.pap)) {
+ lcp->want_auth = PROTO_PAP;
+ lcp->want_authtype = 0;
+ } else {
+ lcp->want_auth = 0;
+ lcp->want_authtype = 0;
+ }
+
+ if (p->type != PHYS_DIRECT)
+ memcpy(&lcp->want_callback, &p->dl->cfg.callback,
+ sizeof(struct callback));
+ else
+ lcp->want_callback.opmask = 0;
+ lcp->want_lqrperiod = IsEnabled(lcp->cfg.lqr) ?
+ lcp->cfg.lqrperiod * 100 : 0;
+ } else {
+ lcp->his_accmap = lcp->want_accmap = 0;
+ lcp->his_protocomp = lcp->want_protocomp = 1;
+ lcp->want_magic = 0;
+ lcp->want_auth = 0;
+ lcp->want_authtype = 0;
+ lcp->want_callback.opmask = 0;
+ lcp->want_lqrperiod = 0;
+ }
+
+ lcp->his_reject = lcp->my_reject = 0;
+ lcp->auth_iwait = lcp->auth_ineed = 0;
+ lcp->LcpFailedMagic = 0;
+}
+
+static void
+LcpInitRestartCounter(struct fsm *fp, int what)
+{
+ /* Set fsm timer load */
+ struct lcp *lcp = fsm2lcp(fp);
+
+ fp->FsmTimer.load = lcp->cfg.fsm.timeout * SECTICKS;
+ switch (what) {
+ case FSM_REQ_TIMER:
+ fp->restart = lcp->cfg.fsm.maxreq;
+ break;
+ case FSM_TRM_TIMER:
+ fp->restart = lcp->cfg.fsm.maxtrm;
+ break;
+ default:
+ fp->restart = 1;
+ break;
+ }
+}
+
+static void
+LcpSendConfigReq(struct fsm *fp)
+{
+ /* Send config REQ please */
+ struct physical *p = link2physical(fp->link);
+ struct lcp *lcp = fsm2lcp(fp);
+ u_char buff[200];
+ struct fsm_opt *o;
+ struct mp *mp;
+ u_int16_t proto;
+ u_short maxmru;
+
+ if (!p) {
+ log_Printf(LogERROR, "%s: LcpSendConfigReq: Not a physical link !\n",
+ fp->link->name);
+ return;
+ }
+
+ o = (struct fsm_opt *)buff;
+ if (!physical_IsSync(p)) {
+ if (lcp->want_acfcomp && !REJECTED(lcp, TY_ACFCOMP))
+ INC_FSM_OPT(TY_ACFCOMP, 2, o);
+
+ if (lcp->want_protocomp && !REJECTED(lcp, TY_PROTOCOMP))
+ INC_FSM_OPT(TY_PROTOCOMP, 2, o);
+
+ if (!REJECTED(lcp, TY_ACCMAP)) {
+ ua_htonl(&lcp->want_accmap, o->data);
+ INC_FSM_OPT(TY_ACCMAP, 6, o);
+ }
+ }
+
+ maxmru = p ? physical_DeviceMTU(p) : 0;
+ if (lcp->cfg.max_mru && (!maxmru || maxmru > lcp->cfg.max_mru))
+ maxmru = lcp->cfg.max_mru;
+ if (maxmru && lcp->want_mru > maxmru) {
+ log_Printf(LogWARN, "%s: Reducing configured MRU from %u to %u\n",
+ fp->link->name, lcp->want_mru, maxmru);
+ lcp->want_mru = maxmru;
+ }
+ if (!REJECTED(lcp, TY_MRU)) {
+ ua_htons(&lcp->want_mru, o->data);
+ INC_FSM_OPT(TY_MRU, 4, o);
+ }
+
+ if (lcp->want_magic && !REJECTED(lcp, TY_MAGICNUM)) {
+ ua_htonl(&lcp->want_magic, o->data);
+ INC_FSM_OPT(TY_MAGICNUM, 6, o);
+ }
+
+ if (lcp->want_lqrperiod && !REJECTED(lcp, TY_QUALPROTO)) {
+ proto = PROTO_LQR;
+ ua_htons(&proto, o->data);
+ ua_htonl(&lcp->want_lqrperiod, o->data + 2);
+ INC_FSM_OPT(TY_QUALPROTO, 8, o);
+ }
+
+ switch (lcp->want_auth) {
+ case PROTO_PAP:
+ proto = PROTO_PAP;
+ ua_htons(&proto, o->data);
+ INC_FSM_OPT(TY_AUTHPROTO, 4, o);
+ break;
+
+ case PROTO_CHAP:
+ proto = PROTO_CHAP;
+ ua_htons(&proto, o->data);
+ o->data[2] = lcp->want_authtype;
+ INC_FSM_OPT(TY_AUTHPROTO, 5, o);
+ break;
+ }
+
+ if (!REJECTED(lcp, TY_CALLBACK)) {
+ if (lcp->want_callback.opmask & CALLBACK_BIT(CALLBACK_AUTH)) {
+ *o->data = CALLBACK_AUTH;
+ INC_FSM_OPT(TY_CALLBACK, 3, o);
+ } else if (lcp->want_callback.opmask & CALLBACK_BIT(CALLBACK_CBCP)) {
+ *o->data = CALLBACK_CBCP;
+ INC_FSM_OPT(TY_CALLBACK, 3, o);
+ } else if (lcp->want_callback.opmask & CALLBACK_BIT(CALLBACK_E164)) {
+ int sz = strlen(lcp->want_callback.msg);
+
+ if (sz > sizeof o->data - 1) {
+ sz = sizeof o->data - 1;
+ log_Printf(LogWARN, "Truncating E164 data to %d octets (oops!)\n", sz);
+ }
+ *o->data = CALLBACK_E164;
+ memcpy(o->data + 1, lcp->want_callback.msg, sz);
+ INC_FSM_OPT(TY_CALLBACK, sz + 3, o);
+ }
+ }
+
+ if (lcp->want_mrru && !REJECTED(lcp, TY_MRRU)) {
+ ua_htons(&lcp->want_mrru, o->data);
+ INC_FSM_OPT(TY_MRRU, 4, o);
+
+ if (lcp->want_shortseq && !REJECTED(lcp, TY_SHORTSEQ))
+ INC_FSM_OPT(TY_SHORTSEQ, 2, o);
+ }
+
+ mp = &lcp->fsm.bundle->ncp.mp;
+ if (mp->cfg.enddisc.class != 0 && IsEnabled(mp->cfg.negenddisc) &&
+ !REJECTED(lcp, TY_ENDDISC)) {
+ *o->data = mp->cfg.enddisc.class;
+ memcpy(o->data+1, mp->cfg.enddisc.address, mp->cfg.enddisc.len);
+ INC_FSM_OPT(TY_ENDDISC, mp->cfg.enddisc.len + 3, o);
+ }
+
+ fsm_Output(fp, CODE_CONFIGREQ, fp->reqid, buff, (u_char *)o - buff,
+ MB_LCPOUT);
+}
+
+void
+lcp_SendProtoRej(struct lcp *lcp, u_char *option, int count)
+{
+ /* Don't understand `option' */
+ fsm_Output(&lcp->fsm, CODE_PROTOREJ, lcp->fsm.reqid, option, count,
+ MB_LCPOUT);
+}
+
+int
+lcp_SendIdentification(struct lcp *lcp)
+{
+ static u_char id; /* Use a private id */
+ u_char msg[DEF_MRU - 3];
+ const char *argv[2];
+ char *exp[2];
+
+ if (*lcp->cfg.ident == '\0')
+ return 0;
+
+ argv[0] = lcp->cfg.ident;
+ argv[1] = NULL;
+
+ command_Expand(exp, 1, argv, lcp->fsm.bundle, 1, getpid());
+
+ ua_htonl(&lcp->want_magic, msg);
+ strncpy(msg + 4, exp[0], sizeof msg - 5);
+ msg[sizeof msg - 1] = '\0';
+
+ fsm_Output(&lcp->fsm, CODE_IDENT, id++, msg, 4 + strlen(msg + 4), MB_LCPOUT);
+ log_Printf(LogLCP, " MAGICNUM %08x\n", lcp->want_magic);
+ log_Printf(LogLCP, " TEXT %s\n", msg + 4);
+
+ command_Free(1, exp);
+ return 1;
+}
+
+void
+lcp_RecvIdentification(struct lcp *lcp, char *data)
+{
+ log_Printf(LogLCP, " MAGICNUM %08x\n", lcp->his_magic);
+ log_Printf(LogLCP, " TEXT %s\n", data);
+}
+
+static void
+LcpSentTerminateReq(struct fsm *fp)
+{
+ /* Term REQ just sent by FSM */
+}
+
+static void
+LcpSendTerminateAck(struct fsm *fp, u_char id)
+{
+ /* Send Term ACK please */
+ struct physical *p = link2physical(fp->link);
+
+ if (p && p->dl->state == DATALINK_CBCP)
+ cbcp_ReceiveTerminateReq(p);
+
+ fsm_Output(fp, CODE_TERMACK, id, NULL, 0, MB_LCPOUT);
+}
+
+static void
+LcpLayerStart(struct fsm *fp)
+{
+ /* We're about to start up ! */
+ struct lcp *lcp = fsm2lcp(fp);
+
+ log_Printf(LogLCP, "%s: LayerStart\n", fp->link->name);
+ lcp->LcpFailedMagic = 0;
+ fp->more.reqs = fp->more.naks = fp->more.rejs = lcp->cfg.fsm.maxreq * 3;
+ lcp->mru_req = 0;
+}
+
+static void
+LcpLayerFinish(struct fsm *fp)
+{
+ /* We're now down */
+ log_Printf(LogLCP, "%s: LayerFinish\n", fp->link->name);
+}
+
+static int
+LcpLayerUp(struct fsm *fp)
+{
+ /* We're now up */
+ struct physical *p = link2physical(fp->link);
+ struct lcp *lcp = fsm2lcp(fp);
+
+ log_Printf(LogLCP, "%s: LayerUp\n", fp->link->name);
+ physical_SetAsyncParams(p, lcp->want_accmap, lcp->his_accmap);
+ lqr_Start(lcp);
+ hdlc_StartTimer(&p->hdlc);
+ fp->more.reqs = fp->more.naks = fp->more.rejs = lcp->cfg.fsm.maxreq * 3;
+
+ lcp_SendIdentification(lcp);
+
+ return 1;
+}
+
+static void
+LcpLayerDown(struct fsm *fp)
+{
+ /* About to come down */
+ struct physical *p = link2physical(fp->link);
+
+ log_Printf(LogLCP, "%s: LayerDown\n", fp->link->name);
+ hdlc_StopTimer(&p->hdlc);
+ lqr_StopTimer(p);
+ lcp_Setup(fsm2lcp(fp), 0);
+}
+
+static int
+E164ok(struct callback *cb, char *req, int sz)
+{
+ char list[sizeof cb->msg], *next;
+ int len;
+
+ if (!strcmp(cb->msg, "*"))
+ return 1;
+
+ strncpy(list, cb->msg, sizeof list - 1);
+ list[sizeof list - 1] = '\0';
+ for (next = strtok(list, ","); next; next = strtok(NULL, ",")) {
+ len = strlen(next);
+ if (sz == len && !memcmp(list, req, sz))
+ return 1;
+ }
+ return 0;
+}
+
+static int
+lcp_auth_nak(struct lcp *lcp, struct fsm_decode *dec)
+{
+ struct fsm_opt nak;
+
+ nak.hdr.id = TY_AUTHPROTO;
+
+ if (IsAccepted(lcp->cfg.pap)) {
+ nak.hdr.len = 4;
+ nak.data[0] = (unsigned char)(PROTO_PAP >> 8);
+ nak.data[1] = (unsigned char)PROTO_PAP;
+ fsm_nak(dec, &nak);
+ return 1;
+ }
+
+ nak.hdr.len = 5;
+ nak.data[0] = (unsigned char)(PROTO_CHAP >> 8);
+ nak.data[1] = (unsigned char)PROTO_CHAP;
+
+ if (IsAccepted(lcp->cfg.chap05)) {
+ nak.data[2] = 0x05;
+ fsm_nak(dec, &nak);
+#ifndef NODES
+ } else if (IsAccepted(lcp->cfg.chap80nt) ||
+ IsAccepted(lcp->cfg.chap80lm)) {
+ nak.data[2] = 0x80;
+ fsm_nak(dec, &nak);
+ } else if (IsAccepted(lcp->cfg.chap81)) {
+ nak.data[2] = 0x81;
+ fsm_nak(dec, &nak);
+#endif
+ } else {
+ return 0;
+ }
+
+ return 1;
+}
+
+static void
+LcpDecodeConfig(struct fsm *fp, u_char *cp, u_char *end, int mode_type,
+ struct fsm_decode *dec)
+{
+ /* Deal with incoming PROTO_LCP */
+ struct lcp *lcp = fsm2lcp(fp);
+ int sz, pos, op, callback_req, chap_type;
+ u_int32_t magic, accmap;
+ u_short mru, phmtu, maxmtu, maxmru, wantmtu, wantmru, proto;
+ struct lqrreq *req;
+ char request[20], desc[22];
+ struct mp *mp;
+ struct physical *p = link2physical(fp->link);
+ struct fsm_opt *opt, nak;
+
+ sz = op = callback_req = 0;
+
+ while (end - cp >= sizeof(opt->hdr)) {
+ if ((opt = fsm_readopt(&cp)) == NULL)
+ break;
+
+ snprintf(request, sizeof request, " %s[%d]", protoname(opt->hdr.id),
+ opt->hdr.len);
+
+ switch (opt->hdr.id) {
+ case TY_MRRU:
+ mp = &lcp->fsm.bundle->ncp.mp;
+ ua_ntohs(opt->data, &mru);
+ log_Printf(LogLCP, "%s %u\n", request, mru);
+
+ switch (mode_type) {
+ case MODE_REQ:
+ if (mp->cfg.mrru) {
+ if (REJECTED(lcp, TY_MRRU))
+ /* Ignore his previous reject so that we REQ next time */
+ lcp->his_reject &= ~(1 << opt->hdr.id);
+
+ if (mru > MAX_MRU) {
+ /* Push him down to MAX_MRU */
+ lcp->his_mrru = MAX_MRU;
+ nak.hdr.id = TY_MRRU;
+ nak.hdr.len = 4;
+ ua_htons(&lcp->his_mrru, nak.data);
+ fsm_nak(dec, &nak);
+ } else if (mru < MIN_MRU) {
+ /* Push him up to MIN_MRU */
+ lcp->his_mrru = MIN_MRU;
+ nak.hdr.id = TY_MRRU;
+ nak.hdr.len = 4;
+ ua_htons(&lcp->his_mrru, nak.data);
+ fsm_nak(dec, &nak);
+ } else {
+ lcp->his_mrru = mru;
+ fsm_ack(dec, opt);
+ }
+ break;
+ } else {
+ fsm_rej(dec, opt);
+ lcp->my_reject |= (1 << opt->hdr.id);
+ }
+ break;
+ case MODE_NAK:
+ if (mp->cfg.mrru) {
+ if (REJECTED(lcp, TY_MRRU))
+ /* Must have changed his mind ! */
+ lcp->his_reject &= ~(1 << opt->hdr.id);
+
+ if (mru > MAX_MRU)
+ lcp->want_mrru = MAX_MRU;
+ else if (mru < MIN_MRU)
+ lcp->want_mrru = MIN_MRU;
+ else
+ lcp->want_mrru = mru;
+ }
+ /* else we honour our config and don't send the suggested REQ */
+ break;
+ case MODE_REJ:
+ lcp->his_reject |= (1 << opt->hdr.id);
+ lcp->want_mrru = 0; /* Ah well, no multilink :-( */
+ break;
+ }
+ break;
+
+ case TY_MRU:
+ lcp->mru_req = 1;
+ ua_ntohs(opt->data, &mru);
+ log_Printf(LogLCP, "%s %d\n", request, mru);
+
+ switch (mode_type) {
+ case MODE_REQ:
+ maxmtu = p ? physical_DeviceMTU(p) : 0;
+ if (lcp->cfg.max_mtu && (!maxmtu || maxmtu > lcp->cfg.max_mtu))
+ maxmtu = lcp->cfg.max_mtu;
+ wantmtu = lcp->cfg.mtu;
+ if (maxmtu && wantmtu > maxmtu) {
+ log_Printf(LogWARN, "%s: Reducing configured MTU from %u to %u\n",
+ fp->link->name, wantmtu, maxmtu);
+ wantmtu = maxmtu;
+ }
+
+ if (maxmtu && mru > maxmtu) {
+ lcp->his_mru = maxmtu;
+ nak.hdr.id = TY_MRU;
+ nak.hdr.len = 4;
+ ua_htons(&lcp->his_mru, nak.data);
+ fsm_nak(dec, &nak);
+ } else if (wantmtu && mru < wantmtu) {
+ /* Push him up to MTU or MIN_MRU */
+ lcp->his_mru = wantmtu;
+ nak.hdr.id = TY_MRU;
+ nak.hdr.len = 4;
+ ua_htons(&lcp->his_mru, nak.data);
+ fsm_nak(dec, &nak);
+ } else {
+ lcp->his_mru = mru;
+ fsm_ack(dec, opt);
+ }
+ break;
+ case MODE_NAK:
+ maxmru = p ? physical_DeviceMTU(p) : 0;
+ if (lcp->cfg.max_mru && (!maxmru || maxmru > lcp->cfg.max_mru))
+ maxmru = lcp->cfg.max_mru;
+ wantmru = lcp->cfg.mru > maxmru ? maxmru : lcp->cfg.mru;
+
+ if (wantmru && mru > wantmru)
+ lcp->want_mru = wantmru;
+ else if (mru > maxmru)
+ lcp->want_mru = maxmru;
+ else if (mru < MIN_MRU)
+ lcp->want_mru = MIN_MRU;
+ else
+ lcp->want_mru = mru;
+ break;
+ case MODE_REJ:
+ lcp->his_reject |= (1 << opt->hdr.id);
+ break;
+ }
+ break;
+
+ case TY_ACCMAP:
+ ua_ntohl(opt->data, &accmap);
+ log_Printf(LogLCP, "%s 0x%08lx\n", request, (u_long)accmap);
+
+ switch (mode_type) {
+ case MODE_REQ:
+ lcp->his_accmap = accmap;
+ fsm_ack(dec, opt);
+ break;
+ case MODE_NAK:
+ lcp->want_accmap = accmap;
+ break;
+ case MODE_REJ:
+ lcp->his_reject |= (1 << opt->hdr.id);
+ break;
+ }
+ break;
+
+ case TY_AUTHPROTO:
+ ua_ntohs(opt->data, &proto);
+ chap_type = opt->hdr.len == 5 ? opt->data[2] : 0;
+
+ log_Printf(LogLCP, "%s 0x%04x (%s)\n", request, proto,
+ Auth2Nam(proto, chap_type));
+
+ switch (mode_type) {
+ case MODE_REQ:
+ switch (proto) {
+ case PROTO_PAP:
+ if (opt->hdr.len == 4 && IsAccepted(lcp->cfg.pap)) {
+ lcp->his_auth = proto;
+ lcp->his_authtype = 0;
+ fsm_ack(dec, opt);
+ } else if (!lcp_auth_nak(lcp, dec)) {
+ lcp->my_reject |= (1 << opt->hdr.id);
+ fsm_rej(dec, opt);
+ }
+ break;
+
+ case PROTO_CHAP:
+ if ((chap_type == 0x05 && IsAccepted(lcp->cfg.chap05))
+#ifndef NODES
+ || (chap_type == 0x80 && (IsAccepted(lcp->cfg.chap80nt) ||
+ (IsAccepted(lcp->cfg.chap80lm))))
+ || (chap_type == 0x81 && IsAccepted(lcp->cfg.chap81))
+#endif
+ ) {
+ lcp->his_auth = proto;
+ lcp->his_authtype = chap_type;
+ fsm_ack(dec, opt);
+ } else {
+#ifdef NODES
+ if (chap_type == 0x80) {
+ log_Printf(LogWARN, "CHAP 0x80 not available without DES\n");
+ } else if (chap_type == 0x81) {
+ log_Printf(LogWARN, "CHAP 0x81 not available without DES\n");
+ } else
+#endif
+ if (chap_type != 0x05)
+ log_Printf(LogWARN, "%s not supported\n",
+ Auth2Nam(PROTO_CHAP, chap_type));
+
+ if (!lcp_auth_nak(lcp, dec)) {
+ lcp->my_reject |= (1 << opt->hdr.id);
+ fsm_rej(dec, opt);
+ }
+ }
+ break;
+
+ default:
+ log_Printf(LogLCP, "%s 0x%04x - not recognised\n",
+ request, proto);
+ if (!lcp_auth_nak(lcp, dec)) {
+ lcp->my_reject |= (1 << opt->hdr.id);
+ fsm_rej(dec, opt);
+ }
+ break;
+ }
+ break;
+
+ case MODE_NAK:
+ switch (proto) {
+ case PROTO_PAP:
+ if (IsEnabled(lcp->cfg.pap)) {
+ lcp->want_auth = PROTO_PAP;
+ lcp->want_authtype = 0;
+ } else {
+ log_Printf(LogLCP, "Peer will only send PAP (not enabled)\n");
+ lcp->his_reject |= (1 << opt->hdr.id);
+ }
+ break;
+ case PROTO_CHAP:
+ if (chap_type == 0x05 && IsEnabled(lcp->cfg.chap05)) {
+ lcp->want_auth = PROTO_CHAP;
+ lcp->want_authtype = 0x05;
+#ifndef NODES
+ } else if (chap_type == 0x80 && (IsEnabled(lcp->cfg.chap80nt) ||
+ IsEnabled(lcp->cfg.chap80lm))) {
+ lcp->want_auth = PROTO_CHAP;
+ lcp->want_authtype = 0x80;
+ } else if (chap_type == 0x81 && IsEnabled(lcp->cfg.chap81)) {
+ lcp->want_auth = PROTO_CHAP;
+ lcp->want_authtype = 0x81;
+#endif
+ } else {
+#ifdef NODES
+ if (chap_type == 0x80) {
+ log_Printf(LogLCP, "Peer will only send MSCHAP (not available"
+ " without DES)\n");
+ } else if (chap_type == 0x81) {
+ log_Printf(LogLCP, "Peer will only send MSCHAPV2 (not available"
+ " without DES)\n");
+ } else
+#endif
+ log_Printf(LogLCP, "Peer will only send %s (not %s)\n",
+ Auth2Nam(PROTO_CHAP, chap_type),
+#ifndef NODES
+ (chap_type == 0x80 || chap_type == 0x81) ? "configured" :
+#endif
+ "supported");
+ lcp->his_reject |= (1 << opt->hdr.id);
+ }
+ break;
+ default:
+ /* We've been NAK'd with something we don't understand :-( */
+ lcp->his_reject |= (1 << opt->hdr.id);
+ break;
+ }
+ break;
+
+ case MODE_REJ:
+ lcp->his_reject |= (1 << opt->hdr.id);
+ break;
+ }
+ break;
+
+ case TY_QUALPROTO:
+ req = (struct lqrreq *)opt;
+ log_Printf(LogLCP, "%s proto %x, interval %lums\n",
+ request, ntohs(req->proto), (u_long)ntohl(req->period) * 10);
+ switch (mode_type) {
+ case MODE_REQ:
+ if (ntohs(req->proto) != PROTO_LQR || !IsAccepted(lcp->cfg.lqr)) {
+ fsm_rej(dec, opt);
+ lcp->my_reject |= (1 << opt->hdr.id);
+ } else {
+ lcp->his_lqrperiod = ntohl(req->period);
+ if (lcp->his_lqrperiod < MIN_LQRPERIOD * 100)
+ lcp->his_lqrperiod = MIN_LQRPERIOD * 100;
+ req->period = htonl(lcp->his_lqrperiod);
+ fsm_ack(dec, opt);
+ }
+ break;
+ case MODE_NAK:
+ lcp->want_lqrperiod = ntohl(req->period);
+ break;
+ case MODE_REJ:
+ lcp->his_reject |= (1 << opt->hdr.id);
+ break;
+ }
+ break;
+
+ case TY_MAGICNUM:
+ ua_ntohl(opt->data, &magic);
+ log_Printf(LogLCP, "%s 0x%08lx\n", request, (u_long)magic);
+
+ switch (mode_type) {
+ case MODE_REQ:
+ if (lcp->want_magic) {
+ /* Validate magic number */
+ if (magic == lcp->want_magic) {
+ sigset_t emptyset;
+
+ log_Printf(LogLCP, "Magic is same (%08lx) - %d times\n",
+ (u_long)magic, ++lcp->LcpFailedMagic);
+ lcp->want_magic = GenerateMagic();
+ fsm_nak(dec, opt);
+ ualarm(TICKUNIT * (4 + 4 * lcp->LcpFailedMagic), 0);
+ sigemptyset(&emptyset);
+ sigsuspend(&emptyset);
+ } else {
+ lcp->his_magic = magic;
+ lcp->LcpFailedMagic = 0;
+ fsm_ack(dec, opt);
+ }
+ } else {
+ lcp->my_reject |= (1 << opt->hdr.id);
+ fsm_rej(dec, opt);
+ }
+ break;
+ case MODE_NAK:
+ log_Printf(LogLCP, " Magic 0x%08lx is NAKed!\n", (u_long)magic);
+ lcp->want_magic = GenerateMagic();
+ break;
+ case MODE_REJ:
+ log_Printf(LogLCP, " Magic 0x%08x is REJected!\n", magic);
+ lcp->want_magic = 0;
+ lcp->his_reject |= (1 << opt->hdr.id);
+ break;
+ }
+ break;
+
+ case TY_PROTOCOMP:
+ log_Printf(LogLCP, "%s\n", request);
+
+ switch (mode_type) {
+ case MODE_REQ:
+ if (IsAccepted(lcp->cfg.protocomp)) {
+ lcp->his_protocomp = 1;
+ fsm_ack(dec, opt);
+ } else {
+#ifdef OLDMST
+ /* MorningStar before v1.3 needs NAK */
+ fsm_nak(dec, opt);
+#else
+ fsm_rej(dec, opt);
+ lcp->my_reject |= (1 << opt->hdr.id);
+#endif
+ }
+ break;
+ case MODE_NAK:
+ case MODE_REJ:
+ lcp->want_protocomp = 0;
+ lcp->his_reject |= (1 << opt->hdr.id);
+ break;
+ }
+ break;
+
+ case TY_ACFCOMP:
+ log_Printf(LogLCP, "%s\n", request);
+ switch (mode_type) {
+ case MODE_REQ:
+ if (IsAccepted(lcp->cfg.acfcomp)) {
+ lcp->his_acfcomp = 1;
+ fsm_ack(dec, opt);
+ } else {
+#ifdef OLDMST
+ /* MorningStar before v1.3 needs NAK */
+ fsm_nak(dec, opt);
+#else
+ fsm_rej(dec, opt);
+ lcp->my_reject |= (1 << opt->hdr.id);
+#endif
+ }
+ break;
+ case MODE_NAK:
+ case MODE_REJ:
+ lcp->want_acfcomp = 0;
+ lcp->his_reject |= (1 << opt->hdr.id);
+ break;
+ }
+ break;
+
+ case TY_SDP:
+ log_Printf(LogLCP, "%s\n", request);
+ switch (mode_type) {
+ case MODE_REQ:
+ case MODE_NAK:
+ case MODE_REJ:
+ break;
+ }
+ break;
+
+ case TY_CALLBACK:
+ if (opt->hdr.len == 2)
+ op = CALLBACK_NONE;
+ else
+ op = (int)opt->data[0];
+ sz = opt->hdr.len - 3;
+ switch (op) {
+ case CALLBACK_AUTH:
+ log_Printf(LogLCP, "%s Auth\n", request);
+ break;
+ case CALLBACK_DIALSTRING:
+ log_Printf(LogLCP, "%s Dialstring %.*s\n", request, sz,
+ opt->data + 1);
+ break;
+ case CALLBACK_LOCATION:
+ log_Printf(LogLCP, "%s Location %.*s\n", request, sz, opt->data + 1);
+ break;
+ case CALLBACK_E164:
+ log_Printf(LogLCP, "%s E.164 (%.*s)\n", request, sz, opt->data + 1);
+ break;
+ case CALLBACK_NAME:
+ log_Printf(LogLCP, "%s Name %.*s\n", request, sz, opt->data + 1);
+ break;
+ case CALLBACK_CBCP:
+ log_Printf(LogLCP, "%s CBCP\n", request);
+ break;
+ default:
+ log_Printf(LogLCP, "%s ???\n", request);
+ break;
+ }
+
+ switch (mode_type) {
+ case MODE_REQ:
+ callback_req = 1;
+ if (p->type != PHYS_DIRECT) {
+ fsm_rej(dec, opt);
+ lcp->my_reject |= (1 << opt->hdr.id);
+ }
+ nak.hdr.id = opt->hdr.id;
+ nak.hdr.len = 3;
+ if ((p->dl->cfg.callback.opmask & CALLBACK_BIT(op)) &&
+ (op != CALLBACK_AUTH || p->link.lcp.want_auth) &&
+ (op != CALLBACK_E164 ||
+ E164ok(&p->dl->cfg.callback, opt->data + 1, sz))) {
+ lcp->his_callback.opmask = CALLBACK_BIT(op);
+ if (sz > sizeof lcp->his_callback.msg - 1) {
+ sz = sizeof lcp->his_callback.msg - 1;
+ log_Printf(LogWARN, "Truncating option arg to %d octets\n", sz);
+ }
+ memcpy(lcp->his_callback.msg, opt->data + 1, sz);
+ lcp->his_callback.msg[sz] = '\0';
+ fsm_ack(dec, opt);
+ } else if ((p->dl->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_AUTH)) &&
+ p->link.lcp.auth_ineed) {
+ nak.data[0] = CALLBACK_AUTH;
+ fsm_nak(dec, &nak);
+ } else if (p->dl->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_CBCP)) {
+ nak.data[0] = CALLBACK_CBCP;
+ fsm_nak(dec, &nak);
+ } else if (p->dl->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_E164)) {
+ nak.data[0] = CALLBACK_E164;
+ fsm_nak(dec, &nak);
+ } else if (p->dl->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_AUTH)) {
+ log_Printf(LogWARN, "Cannot insist on auth callback without"
+ " PAP or CHAP enabled !\n");
+ nak.data[0] = 2;
+ fsm_nak(dec, &nak);
+ } else {
+ lcp->my_reject |= (1 << opt->hdr.id);
+ fsm_rej(dec, opt);
+ }
+ break;
+ case MODE_NAK:
+ /* We don't do what he NAKs with, we do things in our preferred order */
+ if (lcp->want_callback.opmask & CALLBACK_BIT(CALLBACK_AUTH))
+ lcp->want_callback.opmask &= ~CALLBACK_BIT(CALLBACK_AUTH);
+ else if (lcp->want_callback.opmask & CALLBACK_BIT(CALLBACK_CBCP))
+ lcp->want_callback.opmask &= ~CALLBACK_BIT(CALLBACK_CBCP);
+ else if (lcp->want_callback.opmask & CALLBACK_BIT(CALLBACK_E164))
+ lcp->want_callback.opmask &= ~CALLBACK_BIT(CALLBACK_E164);
+ if (lcp->want_callback.opmask == CALLBACK_BIT(CALLBACK_NONE)) {
+ log_Printf(LogPHASE, "Peer NAKd all callbacks, trying none\n");
+ lcp->want_callback.opmask = 0;
+ } else if (!lcp->want_callback.opmask) {
+ log_Printf(LogPHASE, "Peer NAKd last configured callback\n");
+ fsm_Close(&lcp->fsm);
+ }
+ break;
+ case MODE_REJ:
+ if (lcp->want_callback.opmask & CALLBACK_BIT(CALLBACK_NONE)) {
+ lcp->his_reject |= (1 << opt->hdr.id);
+ lcp->want_callback.opmask = 0;
+ } else {
+ log_Printf(LogPHASE, "Peer rejected *required* callback\n");
+ fsm_Close(&lcp->fsm);
+ }
+ break;
+ }
+ break;
+
+ case TY_SHORTSEQ:
+ mp = &lcp->fsm.bundle->ncp.mp;
+ log_Printf(LogLCP, "%s\n", request);
+
+ switch (mode_type) {
+ case MODE_REQ:
+ if (lcp->want_mrru && IsAccepted(mp->cfg.shortseq)) {
+ lcp->his_shortseq = 1;
+ fsm_ack(dec, opt);
+ } else {
+ fsm_rej(dec, opt);
+ lcp->my_reject |= (1 << opt->hdr.id);
+ }
+ break;
+ case MODE_NAK:
+ /*
+ * He's trying to get us to ask for short sequence numbers.
+ * We ignore the NAK and honour our configuration file instead.
+ */
+ break;
+ case MODE_REJ:
+ lcp->his_reject |= (1 << opt->hdr.id);
+ lcp->want_shortseq = 0; /* For when we hit MP */
+ break;
+ }
+ break;
+
+ case TY_ENDDISC:
+ mp = &lcp->fsm.bundle->ncp.mp;
+ log_Printf(LogLCP, "%s %s\n", request,
+ mp_Enddisc(opt->data[0], opt->data + 1, opt->hdr.len - 3));
+ switch (mode_type) {
+ case MODE_REQ:
+ if (!p) {
+ log_Printf(LogLCP, " ENDDISC rejected - not a physical link\n");
+ fsm_rej(dec, opt);
+ lcp->my_reject |= (1 << opt->hdr.id);
+ } else if (!IsAccepted(mp->cfg.negenddisc)) {
+ lcp->my_reject |= (1 << opt->hdr.id);
+ fsm_rej(dec, opt);
+ } else if (opt->hdr.len - 3 < sizeof p->dl->peer.enddisc.address &&
+ opt->data[0] <= MAX_ENDDISC_CLASS) {
+ p->dl->peer.enddisc.class = opt->data[0];
+ p->dl->peer.enddisc.len = opt->hdr.len - 3;
+ memcpy(p->dl->peer.enddisc.address, opt->data + 1, opt->hdr.len - 3);
+ p->dl->peer.enddisc.address[opt->hdr.len - 3] = '\0';
+ /* XXX: If mp->active, compare and NAK with mp->peer ? */
+ fsm_ack(dec, opt);
+ } else {
+ if (opt->data[0] > MAX_ENDDISC_CLASS)
+ log_Printf(LogLCP, " ENDDISC rejected - unrecognised class %d\n",
+ opt->data[0]);
+ else
+ log_Printf(LogLCP, " ENDDISC rejected - local max length is %ld\n",
+ (long)(sizeof p->dl->peer.enddisc.address - 1));
+ fsm_rej(dec, opt);
+ lcp->my_reject |= (1 << opt->hdr.id);
+ }
+ break;
+
+ case MODE_NAK: /* Treat this as a REJ, we don't vary our disc (yet) */
+ case MODE_REJ:
+ lcp->his_reject |= (1 << opt->hdr.id);
+ break;
+ }
+ break;
+
+ default:
+ sz = (sizeof desc - 2) / 2;
+ if (sz > opt->hdr.len - 2)
+ sz = opt->hdr.len - 2;
+ pos = 0;
+ desc[0] = sz ? ' ' : '\0';
+ for (pos = 0; sz--; pos++)
+ sprintf(desc+(pos<<1)+1, "%02x", opt->data[pos]);
+
+ log_Printf(LogLCP, "%s%s\n", request, desc);
+
+ if (mode_type == MODE_REQ) {
+ fsm_rej(dec, opt);
+ lcp->my_reject |= (1 << opt->hdr.id);
+ }
+ break;
+ }
+ }
+
+ if (mode_type != MODE_NOP) {
+ if (mode_type == MODE_REQ && p && p->type == PHYS_DIRECT &&
+ p->dl->cfg.callback.opmask && !callback_req &&
+ !(p->dl->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_NONE))) {
+ /* We *REQUIRE* that the peer requests callback */
+ nak.hdr.id = TY_CALLBACK;
+ nak.hdr.len = 3;
+ if ((p->dl->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_AUTH)) &&
+ p->link.lcp.want_auth)
+ nak.data[0] = CALLBACK_AUTH;
+ else if (p->dl->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_CBCP))
+ nak.data[0] = CALLBACK_CBCP;
+ else if (p->dl->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_E164))
+ nak.data[0] = CALLBACK_E164;
+ else {
+ log_Printf(LogWARN, "Cannot insist on auth callback without"
+ " PAP or CHAP enabled !\n");
+ nak.hdr.len = 2; /* XXX: Silly ! */
+ }
+ fsm_nak(dec, &nak);
+ }
+ if (mode_type == MODE_REQ && !lcp->mru_req) {
+ mru = DEF_MRU;
+ phmtu = p ? physical_DeviceMTU(p) : 0;
+ if (phmtu && mru > phmtu)
+ mru = phmtu;
+ if (mru > lcp->cfg.max_mtu)
+ mru = lcp->cfg.max_mtu;
+ if (mru < DEF_MRU) {
+ /* Don't let the peer use the default MRU */
+ lcp->his_mru = lcp->cfg.mtu && lcp->cfg.mtu < mru ? lcp->cfg.mtu : mru;
+ nak.hdr.id = TY_MRU;
+ nak.hdr.len = 4;
+ ua_htons(&lcp->his_mru, nak.data);
+ fsm_nak(dec, &nak);
+ lcp->mru_req = 1; /* Don't keep NAK'ing this */
+ }
+ }
+ fsm_opt_normalise(dec);
+ }
+}
+
+extern struct mbuf *
+lcp_Input(struct bundle *bundle, struct link *l, struct mbuf *bp)
+{
+ /* Got PROTO_LCP from link */
+ m_settype(bp, MB_LCPIN);
+ fsm_Input(&l->lcp.fsm, bp);
+ return NULL;
+}
diff --git a/usr.sbin/ppp/lcp.h b/usr.sbin/ppp/lcp.h
new file mode 100644
index 0000000..dca86b7
--- /dev/null
+++ b/usr.sbin/ppp/lcp.h
@@ -0,0 +1,142 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/* callback::opmask values */
+#define CALLBACK_AUTH (0)
+#define CALLBACK_DIALSTRING (1) /* Don't do this */
+#define CALLBACK_LOCATION (2) /* Don't do this */
+#define CALLBACK_E164 (3)
+#define CALLBACK_NAME (4) /* Don't do this */
+#define CALLBACK_CBCP (6)
+#define CALLBACK_NONE (14) /* No callback is ok */
+
+#define CALLBACK_BIT(n) ((n) < 0 ? 0 : 1 << (n))
+
+struct callback {
+ int opmask; /* want these types of callback */
+ char msg[SCRIPT_LEN]; /* with this data (E.164) */
+};
+
+#define REJECTED(p, x) ((p)->his_reject & (1<<(x)))
+
+struct lcp {
+ struct fsm fsm; /* The finite state machine */
+ u_int16_t his_mru; /* Peers maximum packet size */
+ u_int16_t his_mrru; /* Peers maximum reassembled packet size (MP) */
+ u_int32_t his_accmap; /* Peeers async char control map */
+ u_int32_t his_magic; /* Peers magic number */
+ u_int32_t his_lqrperiod; /* Peers LQR frequency (100ths of seconds) */
+ u_short his_auth; /* Peer wants this type of authentication */
+ u_char his_authtype; /* Fifth octet of REQ/NAK/REJ */
+ struct callback his_callback; /* Peer wants callback ? */
+ unsigned his_shortseq : 1; /* Peer would like only 12bit seqs (MP) */
+ unsigned his_protocomp : 1; /* Does peer do Protocol field compression */
+ unsigned his_acfcomp : 1; /* Does peer do addr & cntrl fld compression */
+ unsigned mru_req : 1; /* Has the peer requested an MRU */
+
+ u_short want_mru; /* Our maximum packet size */
+ u_short want_mrru; /* Our maximum reassembled packet size (MP) */
+ u_int32_t want_accmap; /* Our async char control map */
+ u_int32_t want_magic; /* Our magic number */
+ u_int32_t want_lqrperiod; /* Our LQR frequency (100ths of seconds) */
+ u_short want_auth; /* We want this type of authentication */
+ u_char want_authtype; /* Fifth octet of REQ/NAK/REJ */
+ struct callback want_callback;/* We want callback ? */
+ unsigned want_shortseq : 1; /* I'd like only 12bit seqs (MP) */
+ unsigned want_protocomp : 1; /* Do we do protocol field compression */
+ unsigned want_acfcomp : 1; /* Do we do addr & cntrl fld compression */
+
+ u_int32_t his_reject; /* Request codes rejected by peer */
+ u_int32_t my_reject; /* Request codes I have rejected */
+
+ u_short auth_iwait; /* I must authenticate to the peer */
+ u_short auth_ineed; /* I require that the peer authenticates */
+
+ int LcpFailedMagic; /* Number of `magic is same' errors */
+
+ struct {
+ u_short mru; /* Preferred MRU value */
+ u_short max_mru; /* Preferred MRU value */
+ u_short mtu; /* Preferred MTU */
+ u_short max_mtu; /* Preferred MTU */
+ u_int32_t accmap; /* Initial ACCMAP value */
+ int openmode; /* when to start CFG REQs */
+ u_int32_t lqrperiod; /* LQR frequency (seconds) */
+ struct fsm_retry fsm; /* How often/frequently to resend requests */
+ unsigned acfcomp : 2; /* Address & Control Field Compression neg */
+ unsigned chap05 : 2; /* Challenge Handshake Authentication proto */
+#ifndef NODES
+ unsigned chap80nt : 2; /* Microsoft (NT) CHAP */
+ unsigned chap80lm : 2; /* Microsoft (LANMan) CHAP */
+ unsigned chap81 : 2; /* Microsoft CHAP v2 */
+#endif
+ unsigned lqr : 2; /* Link Quality Report */
+ unsigned pap : 2; /* Password Authentication protocol */
+ unsigned protocomp : 2; /* Protocol field compression */
+ char ident[DEF_MRU - 7]; /* SendIdentification() data */
+ } cfg;
+};
+
+#define LCP_MAXCODE CODE_IDENT
+#define LCP_MINMPCODE CODE_CODEREJ
+
+#define TY_MRU 1 /* Maximum-Receive-Unit */
+#define TY_ACCMAP 2 /* Async-Control-Character-Map */
+#define TY_AUTHPROTO 3 /* Authentication-Protocol */
+#define TY_QUALPROTO 4 /* Quality-Protocol */
+#define TY_MAGICNUM 5 /* Magic-Number */
+#define TY_RESERVED 6 /* RESERVED */
+#define TY_PROTOCOMP 7 /* Protocol-Field-Compression */
+#define TY_ACFCOMP 8 /* Address-and-Control-Field-Compression */
+#define TY_FCSALT 9 /* FCS-Alternatives */
+#define TY_SDP 10 /* Self-Describing-Padding */
+#define TY_CALLBACK 13 /* Callback */
+#define TY_CFRAMES 15 /* Compound-frames */
+#define TY_MRRU 17 /* Max Reconstructed Receive Unit (MP) */
+#define TY_SHORTSEQ 18 /* Want short seqs (12bit) please (see mp.h) */
+#define TY_ENDDISC 19 /* Endpoint discriminator */
+
+struct mbuf;
+struct link;
+struct bundle;
+struct cmdargs;
+
+#define fsm2lcp(fp) (fp->proto == PROTO_LCP ? (struct lcp *)fp : NULL)
+
+extern void lcp_Init(struct lcp *, struct bundle *, struct link *,
+ const struct fsm_parent *);
+extern void lcp_Setup(struct lcp *, int);
+
+extern void lcp_SendProtoRej(struct lcp *, u_char *, int);
+extern int lcp_SendIdentification(struct lcp *);
+extern void lcp_RecvIdentification(struct lcp *, char *);
+extern int lcp_ReportStatus(struct cmdargs const *);
+extern struct mbuf *lcp_Input(struct bundle *, struct link *, struct mbuf *);
+extern void lcp_SetupCallbacks(struct lcp *);
diff --git a/usr.sbin/ppp/link.c b/usr.sbin/ppp/link.c
new file mode 100644
index 0000000..6a2481c
--- /dev/null
+++ b/usr.sbin/ppp/link.c
@@ -0,0 +1,382 @@
+/*-
+ * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ */
+
+#include <sys/types.h>
+#include <netinet/in_systm.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+
+#include "defs.h"
+#include "layer.h"
+#include "mbuf.h"
+#include "log.h"
+#include "timer.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "throughput.h"
+#include "proto.h"
+#include "fsm.h"
+#include "descriptor.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "prompt.h"
+#include "async.h"
+#include "physical.h"
+#include "mp.h"
+#include "iplist.h"
+#include "slcompress.h"
+#include "ncpaddr.h"
+#include "ip.h"
+#include "ipcp.h"
+#include "ipv6cp.h"
+#include "auth.h"
+#include "pap.h"
+#include "chap.h"
+#include "cbcp.h"
+#include "command.h"
+
+static void Despatch(struct bundle *, struct link *, struct mbuf *, u_short);
+
+static inline void
+link_AddInOctets(struct link *l, int n)
+{
+ if (l->stats.gather) {
+ throughput_addin(&l->stats.total, n);
+ if (l->stats.parent)
+ throughput_addin(l->stats.parent, n);
+ }
+}
+
+static inline void
+link_AddOutOctets(struct link *l, int n)
+{
+ if (l->stats.gather) {
+ throughput_addout(&l->stats.total, n);
+ if (l->stats.parent)
+ throughput_addout(l->stats.parent, n);
+ }
+}
+
+void
+link_SequenceQueue(struct link *l)
+{
+ struct mqueue *queue, *highest;
+
+ log_Printf(LogDEBUG, "link_SequenceQueue\n");
+
+ highest = LINK_HIGHQ(l);
+ for (queue = l->Queue; queue < highest; queue++)
+ while (queue->len)
+ m_enqueue(highest, m_dequeue(queue));
+}
+
+void
+link_DeleteQueue(struct link *l)
+{
+ struct mqueue *queue, *highest;
+
+ highest = LINK_HIGHQ(l);
+ for (queue = l->Queue; queue <= highest; queue++)
+ while (queue->top)
+ m_freem(m_dequeue(queue));
+}
+
+size_t
+link_QueueLen(struct link *l)
+{
+ int i;
+ size_t len;
+
+ for (i = 0, len = 0; i < LINK_QUEUES(l); i++)
+ len += l->Queue[i].len;
+
+ return len;
+}
+
+size_t
+link_QueueBytes(struct link *l)
+{
+ int i;
+ size_t len, bytes;
+ struct mbuf *m;
+
+ bytes = 0;
+ for (i = 0, len = 0; i < LINK_QUEUES(l); i++) {
+ len = l->Queue[i].len;
+ m = l->Queue[i].top;
+ while (len--) {
+ bytes += m_length(m);
+ m = m->m_nextpkt;
+ }
+ }
+
+ return bytes;
+}
+
+struct mbuf *
+link_Dequeue(struct link *l)
+{
+ int pri;
+ struct mbuf *bp;
+
+ for (bp = NULL, pri = LINK_QUEUES(l) - 1; pri >= 0; pri--)
+ if (l->Queue[pri].len) {
+ bp = m_dequeue(l->Queue + pri);
+ log_Printf(LogDEBUG, "link_Dequeue: Dequeued from queue %d,"
+ " containing %lu more packets\n", pri,
+ (u_long)l->Queue[pri].len);
+ break;
+ }
+
+ return bp;
+}
+
+static struct protostatheader {
+ u_short number;
+ const char *name;
+} ProtocolStat[NPROTOSTAT] = {
+ { PROTO_IP, "IP" },
+ { PROTO_VJUNCOMP, "VJ_UNCOMP" },
+ { PROTO_VJCOMP, "VJ_COMP" },
+ { PROTO_COMPD, "COMPD" },
+ { PROTO_ICOMPD, "ICOMPD" },
+ { PROTO_LCP, "LCP" },
+ { PROTO_IPCP, "IPCP" },
+ { PROTO_CCP, "CCP" },
+ { PROTO_PAP, "PAP" },
+ { PROTO_LQR, "LQR" },
+ { PROTO_CHAP, "CHAP" },
+ { PROTO_MP, "MULTILINK" },
+ { 0, "Others" }
+};
+
+void
+link_ProtocolRecord(struct link *l, u_short proto, int type)
+{
+ int i;
+
+ for (i = 0; i < NPROTOSTAT; i++)
+ if (ProtocolStat[i].number == proto)
+ break;
+
+ if (type == PROTO_IN)
+ l->proto_in[i]++;
+ else
+ l->proto_out[i]++;
+}
+
+void
+link_ReportProtocolStatus(struct link *l, struct prompt *prompt)
+{
+ int i;
+
+ prompt_Printf(prompt, " Protocol in out "
+ "Protocol in out\n");
+ for (i = 0; i < NPROTOSTAT; i++) {
+ prompt_Printf(prompt, " %-9s: %8lu, %8lu",
+ ProtocolStat[i].name, l->proto_in[i], l->proto_out[i]);
+ if ((i % 2) == 0)
+ prompt_Printf(prompt, "\n");
+ }
+ if (!(i % 2))
+ prompt_Printf(prompt, "\n");
+}
+
+void
+link_PushPacket(struct link *l, struct mbuf *bp, struct bundle *b, int pri,
+ u_short proto)
+{
+ int layer;
+
+ /*
+ * When we ``push'' a packet into the link, it gets processed by the
+ * ``push'' function in each layer starting at the top.
+ * We never expect the result of a ``push'' to be more than one
+ * packet (as we do with ``pull''s).
+ */
+
+ if(pri < 0 || pri >= LINK_QUEUES(l))
+ pri = 0;
+
+ for (layer = l->nlayers; layer && bp; layer--)
+ if (l->layer[layer - 1]->push != NULL)
+ bp = (*l->layer[layer - 1]->push)(b, l, bp, pri, &proto);
+
+ if (bp) {
+ link_AddOutOctets(l, m_length(bp));
+ log_Printf(LogDEBUG, "link_PushPacket: Transmit proto 0x%04x\n", proto);
+ m_enqueue(l->Queue + pri, m_pullup(bp));
+ }
+}
+
+void
+link_PullPacket(struct link *l, char *buf, size_t len, struct bundle *b)
+{
+ struct mbuf *bp, *lbp[LAYER_MAX], *next;
+ u_short lproto[LAYER_MAX], proto;
+ int layer;
+
+ /*
+ * When we ``pull'' a packet from the link, it gets processed by the
+ * ``pull'' function in each layer starting at the bottom.
+ * Each ``pull'' may produce multiple packets, chained together using
+ * bp->m_nextpkt.
+ * Each packet that results from each pull has to be pulled through
+ * all of the higher layers before the next resulting packet is pulled
+ * through anything; this ensures that packets that depend on the
+ * fsm state resulting from the receipt of the previous packet aren't
+ * surprised.
+ */
+
+ link_AddInOctets(l, len);
+
+ memset(lbp, '\0', sizeof lbp);
+ lbp[0] = m_get(len, MB_UNKNOWN);
+ memcpy(MBUF_CTOP(lbp[0]), buf, len);
+ lproto[0] = 0;
+ layer = 0;
+
+ while (layer || lbp[layer]) {
+ if (lbp[layer] == NULL) {
+ layer--;
+ continue;
+ }
+ bp = lbp[layer];
+ lbp[layer] = bp->m_nextpkt;
+ bp->m_nextpkt = NULL;
+ proto = lproto[layer];
+
+ if (l->layer[layer]->pull != NULL)
+ bp = (*l->layer[layer]->pull)(b, l, bp, &proto);
+
+ if (layer == l->nlayers - 1) {
+ /* We've just done the top layer, despatch the packet(s) */
+ while (bp) {
+ next = bp->m_nextpkt;
+ bp->m_nextpkt = NULL;
+ log_Printf(LogDEBUG, "link_PullPacket: Despatch proto 0x%04x\n", proto);
+ Despatch(b, l, bp, proto);
+ bp = next;
+ }
+ } else {
+ lbp[++layer] = bp;
+ lproto[layer] = proto;
+ }
+ }
+}
+
+int
+link_Stack(struct link *l, struct layer *layer)
+{
+ if (l->nlayers == sizeof l->layer / sizeof l->layer[0]) {
+ log_Printf(LogERROR, "%s: Oops, cannot stack a %s layer...\n",
+ l->name, layer->name);
+ return 0;
+ }
+ l->layer[l->nlayers++] = layer;
+ return 1;
+}
+
+void
+link_EmptyStack(struct link *l)
+{
+ l->nlayers = 0;
+}
+
+static const struct {
+ u_short proto;
+ struct mbuf *(*fn)(struct bundle *, struct link *, struct mbuf *);
+} despatcher[] = {
+ { PROTO_IP, ipv4_Input },
+#ifndef NOINET6
+ { PROTO_IPV6, ipv6_Input },
+#endif
+ { PROTO_MP, mp_Input },
+ { PROTO_LCP, lcp_Input },
+ { PROTO_IPCP, ipcp_Input },
+#ifndef NOINET6
+ { PROTO_IPV6CP, ipv6cp_Input },
+#endif
+ { PROTO_PAP, pap_Input },
+ { PROTO_CHAP, chap_Input },
+ { PROTO_CCP, ccp_Input },
+ { PROTO_LQR, lqr_Input },
+ { PROTO_CBCP, cbcp_Input }
+};
+
+#define DSIZE (sizeof despatcher / sizeof despatcher[0])
+
+static void
+Despatch(struct bundle *bundle, struct link *l, struct mbuf *bp, u_short proto)
+{
+ int f;
+
+ for (f = 0; f < DSIZE; f++)
+ if (despatcher[f].proto == proto) {
+ bp = (*despatcher[f].fn)(bundle, l, bp);
+ break;
+ }
+
+ if (bp) {
+ struct physical *p = link2physical(l);
+
+ log_Printf(LogPHASE, "%s protocol 0x%04x (%s)\n",
+ f == DSIZE ? "Unknown" : "Unexpected", proto,
+ hdlc_Protocol2Nam(proto));
+ bp = m_pullup(proto_Prepend(bp, proto, 0, 0));
+ lcp_SendProtoRej(&l->lcp, MBUF_CTOP(bp), bp->m_len);
+ if (p) {
+ p->hdlc.lqm.SaveInDiscards++;
+ p->hdlc.stats.unknownproto++;
+ }
+ m_freem(bp);
+ }
+}
+
+int
+link_ShowLayers(struct cmdargs const *arg)
+{
+ struct link *l = command_ChooseLink(arg);
+ int layer;
+
+ for (layer = l->nlayers; layer; layer--)
+ prompt_Printf(arg->prompt, "%s%s", layer == l->nlayers ? "" : ", ",
+ l->layer[layer - 1]->name);
+ if (l->nlayers)
+ prompt_Printf(arg->prompt, "\n");
+
+ return 0;
+}
diff --git a/usr.sbin/ppp/link.h b/usr.sbin/ppp/link.h
new file mode 100644
index 0000000..89b6230
--- /dev/null
+++ b/usr.sbin/ppp/link.h
@@ -0,0 +1,80 @@
+/*-
+ * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ */
+
+
+#define PHYSICAL_LINK 1
+#define LOGICAL_LINK 2
+
+#define NPROTOSTAT 13
+
+struct bundle;
+struct prompt;
+struct cmdargs;
+
+struct link {
+ int type; /* _LINK type */
+ const char *name; /* Points to datalink::name */
+ int len; /* full size of parent struct */
+ struct {
+ unsigned gather : 1; /* Gather statistics ourself ? */
+ struct pppThroughput total; /* Link throughput statistics */
+ struct pppThroughput *parent; /* MP link throughput statistics */
+ } stats;
+ struct mqueue Queue[2]; /* Our output queue of mbufs */
+
+ u_long proto_in[NPROTOSTAT]; /* outgoing protocol stats */
+ u_long proto_out[NPROTOSTAT]; /* incoming protocol stats */
+
+ struct lcp lcp; /* Our line control FSM */
+ struct ccp ccp; /* Our compression FSM */
+
+ struct layer const *layer[LAYER_MAX]; /* i/o layers */
+ int nlayers;
+};
+
+#define LINK_QUEUES(link) (sizeof (link)->Queue / sizeof (link)->Queue[0])
+#define LINK_HIGHQ(link) ((link)->Queue + LINK_QUEUES(link) - 1)
+
+extern void link_SequenceQueue(struct link *);
+extern void link_DeleteQueue(struct link *);
+extern size_t link_QueueLen(struct link *);
+extern size_t link_QueueBytes(struct link *);
+extern struct mbuf *link_Dequeue(struct link *);
+
+extern void link_PushPacket(struct link *, struct mbuf *, struct bundle *,
+ int, u_short);
+extern void link_PullPacket(struct link *, char *, size_t, struct bundle *);
+extern int link_Stack(struct link *, struct layer *);
+extern void link_EmptyStack(struct link *);
+
+#define PROTO_IN 1 /* third arg to link_ProtocolRecord */
+#define PROTO_OUT 2
+extern void link_ProtocolRecord(struct link *, u_short, int);
+extern void link_ReportProtocolStatus(struct link *, struct prompt *);
+extern int link_ShowLayers(struct cmdargs const *);
diff --git a/usr.sbin/ppp/log.c b/usr.sbin/ppp/log.c
new file mode 100644
index 0000000..b2a6b99
--- /dev/null
+++ b/usr.sbin/ppp/log.c
@@ -0,0 +1,520 @@
+/*-
+ * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <termios.h>
+
+#include "defs.h"
+#include "command.h"
+#include "mbuf.h"
+#include "log.h"
+#include "descriptor.h"
+#include "prompt.h"
+
+static const char *const LogNames[] = {
+ "Async",
+ "CBCP",
+ "CCP",
+ "Chat",
+ "Command",
+ "Connect",
+ "Debug",
+ "DNS",
+ "Filter", /* Log discarded packets */
+ "HDLC",
+ "ID0",
+ "IPCP",
+ "IPV6CP",
+ "LCP",
+ "LQM",
+ "Phase",
+ "Physical",
+ "Sync",
+ "TCP/IP",
+ "Timer",
+ "Tun",
+ "Warning",
+ "Error",
+ "Alert"
+};
+
+#define MSK(n) (1<<((n)-1))
+
+static u_long LogMask = MSK(LogPHASE);
+static u_long LogMaskLocal = MSK(LogERROR) | MSK(LogALERT) | MSK(LogWARN);
+static int LogTunno = -1;
+static struct prompt *promptlist; /* Where to log local stuff */
+struct prompt *log_PromptContext;
+int log_PromptListChanged;
+
+struct prompt *
+log_PromptList()
+{
+ return promptlist;
+}
+
+void
+log_RegisterPrompt(struct prompt *prompt)
+{
+ prompt->next = promptlist;
+ promptlist = prompt;
+ prompt->active = 1;
+ log_DiscardAllLocal(&prompt->logmask);
+}
+
+void
+log_ActivatePrompt(struct prompt *prompt)
+{
+ prompt->active = 1;
+ LogMaskLocal |= prompt->logmask;
+}
+
+static void
+LogSetMaskLocal(void)
+{
+ struct prompt *p;
+
+ LogMaskLocal = MSK(LogERROR) | MSK(LogALERT) | MSK(LogWARN);
+ for (p = promptlist; p; p = p->next)
+ LogMaskLocal |= p->logmask;
+}
+
+void
+log_DeactivatePrompt(struct prompt *prompt)
+{
+ if (prompt->active) {
+ prompt->active = 0;
+ LogSetMaskLocal();
+ }
+}
+
+void
+log_UnRegisterPrompt(struct prompt *prompt)
+{
+ if (prompt) {
+ struct prompt **p;
+
+ for (p = &promptlist; *p; p = &(*p)->next)
+ if (*p == prompt) {
+ *p = prompt->next;
+ prompt->next = NULL;
+ break;
+ }
+ LogSetMaskLocal();
+ log_PromptListChanged++;
+ }
+}
+
+void
+log_DestroyPrompts(struct server *s)
+{
+ struct prompt *p, *pn, *pl;
+
+ p = promptlist;
+ pl = NULL;
+ while (p) {
+ pn = p->next;
+ if (s && p->owner == s) {
+ if (pl)
+ pl->next = p->next;
+ else
+ promptlist = p->next;
+ p->next = NULL;
+ prompt_Destroy(p, 1);
+ } else
+ pl = p;
+ p = pn;
+ }
+}
+
+void
+log_DisplayPrompts()
+{
+ struct prompt *p;
+
+ for (p = promptlist; p; p = p->next)
+ prompt_Required(p);
+}
+
+void
+log_WritePrompts(struct datalink *dl, const char *fmt,...)
+{
+ va_list ap;
+ struct prompt *p;
+
+ va_start(ap, fmt);
+ for (p = promptlist; p; p = p->next)
+ if (prompt_IsTermMode(p, dl))
+ prompt_vPrintf(p, fmt, ap);
+ va_end(ap);
+}
+
+void
+log_SetTtyCommandMode(struct datalink *dl)
+{
+ struct prompt *p;
+
+ for (p = promptlist; p; p = p->next)
+ if (prompt_IsTermMode(p, dl))
+ prompt_TtyCommandMode(p);
+}
+
+static int
+syslogLevel(int lev)
+{
+ switch (lev) {
+ case LogLOG:
+ return LOG_INFO;
+ case LogDEBUG:
+ case LogTIMER:
+ return LOG_DEBUG;
+ case LogWARN:
+ return LOG_WARNING;
+ case LogERROR:
+ return LOG_ERR;
+ case LogALERT:
+ return LOG_ALERT;
+ }
+ return lev >= LogMIN && lev <= LogMAX ? LOG_INFO : 0;
+}
+
+const char *
+log_Name(int id)
+{
+ if (id == LogLOG)
+ return "LOG";
+ return id < LogMIN || id > LogMAX ? "Unknown" : LogNames[id - 1];
+}
+
+void
+log_Keep(int id)
+{
+ if (id >= LogMIN && id <= LogMAXCONF)
+ LogMask |= MSK(id);
+}
+
+void
+log_KeepLocal(int id, u_long *mask)
+{
+ if (id >= LogMIN && id <= LogMAXCONF) {
+ LogMaskLocal |= MSK(id);
+ *mask |= MSK(id);
+ }
+}
+
+void
+log_Discard(int id)
+{
+ if (id >= LogMIN && id <= LogMAXCONF)
+ LogMask &= ~MSK(id);
+}
+
+void
+log_DiscardLocal(int id, u_long *mask)
+{
+ if (id >= LogMIN && id <= LogMAXCONF) {
+ *mask &= ~MSK(id);
+ LogSetMaskLocal();
+ }
+}
+
+void
+log_DiscardAll()
+{
+ LogMask = 0;
+}
+
+void
+log_DiscardAllLocal(u_long *mask)
+{
+ *mask = MSK(LogERROR) | MSK(LogALERT) | MSK(LogWARN);
+ LogSetMaskLocal();
+}
+
+int
+log_IsKept(int id)
+{
+ if (id == LogLOG)
+ return LOG_KEPT_SYSLOG;
+ if (id < LogMIN || id > LogMAX)
+ return 0;
+ if (id > LogMAXCONF)
+ return LOG_KEPT_LOCAL | LOG_KEPT_SYSLOG;
+
+ return ((LogMaskLocal & MSK(id)) ? LOG_KEPT_LOCAL : 0) |
+ ((LogMask & MSK(id)) ? LOG_KEPT_SYSLOG : 0);
+}
+
+int
+log_IsKeptLocal(int id, u_long mask)
+{
+ if (id < LogMIN || id > LogMAX)
+ return 0;
+ if (id > LogMAXCONF)
+ return LOG_KEPT_LOCAL | LOG_KEPT_SYSLOG;
+
+ return ((mask & MSK(id)) ? LOG_KEPT_LOCAL : 0) |
+ ((LogMask & MSK(id)) ? LOG_KEPT_SYSLOG : 0);
+}
+
+void
+log_Open(const char *Name)
+{
+ openlog(Name, LOG_PID, LOG_DAEMON);
+}
+
+void
+log_SetTun(int tunno)
+{
+ LogTunno = tunno;
+}
+
+void
+log_Close()
+{
+ closelog();
+ LogTunno = -1;
+}
+
+void
+log_Printf(int lev, const char *fmt,...)
+{
+ va_list ap;
+ struct prompt *prompt;
+
+ if (log_IsKept(lev)) {
+ char nfmt[200];
+
+ va_start(ap, fmt);
+ if (promptlist && (log_IsKept(lev) & LOG_KEPT_LOCAL)) {
+ if ((log_IsKept(LogTUN) & LOG_KEPT_LOCAL) && LogTunno != -1)
+ snprintf(nfmt, sizeof nfmt, "%s%d: %s: %s", TUN_NAME,
+ LogTunno, log_Name(lev), fmt);
+ else
+ snprintf(nfmt, sizeof nfmt, "%s: %s", log_Name(lev), fmt);
+
+ if (log_PromptContext && lev == LogWARN)
+ /* Warnings just go to the current prompt */
+ prompt_vPrintf(log_PromptContext, nfmt, ap);
+ else for (prompt = promptlist; prompt; prompt = prompt->next)
+ if (lev > LogMAXCONF || (prompt->logmask & MSK(lev)))
+ prompt_vPrintf(prompt, nfmt, ap);
+ }
+ va_end(ap);
+
+ va_start(ap, fmt);
+ if ((log_IsKept(lev) & LOG_KEPT_SYSLOG) &&
+ (lev != LogWARN || !log_PromptContext)) {
+ if ((log_IsKept(LogTUN) & LOG_KEPT_SYSLOG) && LogTunno != -1)
+ snprintf(nfmt, sizeof nfmt, "%s%d: %s: %s", TUN_NAME,
+ LogTunno, log_Name(lev), fmt);
+ else
+ snprintf(nfmt, sizeof nfmt, "%s: %s", log_Name(lev), fmt);
+ vsyslog(syslogLevel(lev), nfmt, ap);
+ }
+ va_end(ap);
+ }
+}
+
+void
+log_DumpBp(int lev, const char *hdr, const struct mbuf *bp)
+{
+ if (log_IsKept(lev)) {
+ char buf[68];
+ char *b, *c;
+ const u_char *ptr;
+ int f;
+
+ if (hdr && *hdr)
+ log_Printf(lev, "%s\n", hdr);
+
+ b = buf;
+ c = b + 50;
+ do {
+ f = bp->m_len;
+ ptr = CONST_MBUF_CTOP(bp);
+ while (f--) {
+ sprintf(b, " %02x", (int) *ptr);
+ *c++ = isprint(*ptr) ? *ptr : '.';
+ ptr++;
+ b += 3;
+ if (b == buf + 48) {
+ memset(b, ' ', 2);
+ *c = '\0';
+ log_Printf(lev, "%s\n", buf);
+ b = buf;
+ c = b + 50;
+ }
+ }
+ } while ((bp = bp->m_next) != NULL);
+
+ if (b > buf) {
+ memset(b, ' ', 50 - (b - buf));
+ *c = '\0';
+ log_Printf(lev, "%s\n", buf);
+ }
+ }
+}
+
+void
+log_DumpBuff(int lev, const char *hdr, const u_char *ptr, int n)
+{
+ if (log_IsKept(lev)) {
+ char buf[68];
+ char *b, *c;
+
+ if (hdr && *hdr)
+ log_Printf(lev, "%s\n", hdr);
+ while (n > 0) {
+ b = buf;
+ c = b + 50;
+ for (b = buf; b != buf + 48 && n--; b += 3, ptr++) {
+ sprintf(b, " %02x", (int) *ptr);
+ *c++ = isprint(*ptr) ? *ptr : '.';
+ }
+ memset(b, ' ', 50 - (b - buf));
+ *c = '\0';
+ log_Printf(lev, "%s\n", buf);
+ }
+ }
+}
+
+int
+log_ShowLevel(struct cmdargs const *arg)
+{
+ int i;
+
+ prompt_Printf(arg->prompt, "Log: ");
+ for (i = LogMIN; i <= LogMAX; i++)
+ if (log_IsKept(i) & LOG_KEPT_SYSLOG)
+ prompt_Printf(arg->prompt, " %s", log_Name(i));
+
+ prompt_Printf(arg->prompt, "\nLocal:");
+ for (i = LogMIN; i <= LogMAX; i++)
+ if (log_IsKeptLocal(i, arg->prompt->logmask) & LOG_KEPT_LOCAL)
+ prompt_Printf(arg->prompt, " %s", log_Name(i));
+
+ prompt_Printf(arg->prompt, "\n");
+
+ return 0;
+}
+
+int
+log_SetLevel(struct cmdargs const *arg)
+{
+ int i, res, argc, local;
+ char const *const *argv, *argp;
+
+ argc = arg->argc - arg->argn;
+ argv = arg->argv + arg->argn;
+ res = 0;
+
+ if (argc == 0 || strcasecmp(argv[0], "local"))
+ local = 0;
+ else {
+ if (arg->prompt == NULL) {
+ log_Printf(LogWARN, "set log local: Only available on the"
+ " command line\n");
+ return 1;
+ }
+ argc--;
+ argv++;
+ local = 1;
+ }
+
+ if (argc == 0 || (argv[0][0] != '+' && argv[0][0] != '-')) {
+ if (local)
+ log_DiscardAllLocal(&arg->prompt->logmask);
+ else
+ log_DiscardAll();
+ }
+
+ while (argc--) {
+ argp = **argv == '+' || **argv == '-' ? *argv + 1 : *argv;
+ /* Special case 'all' */
+ if (strcasecmp(argp, "all") == 0) {
+ if (**argv == '-') {
+ if (local)
+ for (i = LogMIN; i <= LogMAX; i++)
+ log_DiscardLocal(i, &arg->prompt->logmask);
+ else
+ for (i = LogMIN; i <= LogMAX; i++)
+ log_Discard(i);
+ } else if (local)
+ for (i = LogMIN; i <= LogMAX; i++)
+ log_KeepLocal(i, &arg->prompt->logmask);
+ else
+ for (i = LogMIN; i <= LogMAX; i++)
+ log_Keep(i);
+ argv++;
+ continue;
+ }
+ for (i = LogMIN; i <= LogMAX; i++)
+ if (strcasecmp(argp, log_Name(i)) == 0) {
+ if (**argv == '-') {
+ if (local)
+ log_DiscardLocal(i, &arg->prompt->logmask);
+ else
+ log_Discard(i);
+ } else if (local)
+ log_KeepLocal(i, &arg->prompt->logmask);
+ else
+ log_Keep(i);
+ break;
+ }
+ if (i > LogMAX) {
+ log_Printf(LogWARN, "%s: Invalid log value\n", argp);
+ res = -1;
+ }
+ argv++;
+ }
+ return res;
+}
+
+int
+log_ShowWho(struct cmdargs const *arg)
+{
+ struct prompt *p;
+
+ for (p = promptlist; p; p = p->next) {
+ prompt_Printf(arg->prompt, "%s (%s)", p->src.type, p->src.from);
+ if (p == arg->prompt)
+ prompt_Printf(arg->prompt, " *");
+ if (!p->active)
+ prompt_Printf(arg->prompt, " ^Z");
+ prompt_Printf(arg->prompt, "\n");
+ }
+
+ return 0;
+}
diff --git a/usr.sbin/ppp/log.h b/usr.sbin/ppp/log.h
new file mode 100644
index 0000000..f3fef65
--- /dev/null
+++ b/usr.sbin/ppp/log.h
@@ -0,0 +1,104 @@
+/*-
+ * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define LogLOG (0)
+#define LogMIN (1)
+#define LogASYNC (1) /* syslog(LOG_INFO, ....) */
+#define LogCBCP (2)
+#define LogCCP (3)
+#define LogCHAT (4)
+#define LogCOMMAND (5)
+#define LogCONNECT (6)
+#define LogDEBUG (7) /* syslog(LOG_DEBUG, ....) */
+#define LogDNS (8)
+#define LogFILTER (9)
+#define LogHDLC (10)
+#define LogID0 (11)
+#define LogIPCP (12)
+#define LogIPV6CP (13)
+#define LogLCP (14)
+#define LogLQM (15)
+#define LogPHASE (16)
+#define LogPHYSICAL (17) /* syslog(LOG_INFO, ....) */
+#define LogSYNC (18) /* syslog(LOG_INFO, ....) */
+#define LogTCPIP (19)
+#define LogTIMER (20) /* syslog(LOG_DEBUG, ....) */
+#define LogTUN (21) /* If set, tun%d is output with each message */
+#define LogWARN (22) /* Sent to VarTerm else syslog(LOG_WARNING, ) */
+#define LogERROR (23) /* syslog(LOG_ERR, ....), + sent to VarTerm */
+#define LogALERT (24) /* syslog(LOG_ALERT, ....) */
+
+#define LogMAXCONF (21)
+#define LogMAX (24)
+
+struct mbuf;
+struct cmdargs;
+struct prompt;
+struct server;
+struct datalink;
+
+/* The first int arg for all of the following is one of the above values */
+extern const char *log_Name(int);
+extern void log_Keep(int);
+extern void log_KeepLocal(int, u_long *);
+extern void log_Discard(int);
+extern void log_DiscardLocal(int, u_long *);
+extern void log_DiscardAll(void);
+extern void log_DiscardAllLocal(u_long *);
+#define LOG_KEPT_SYSLOG (1) /* Results of log_IsKept() */
+#define LOG_KEPT_LOCAL (2) /* Results of log_IsKept() */
+extern int log_IsKept(int);
+extern int log_IsKeptLocal(int, u_long);
+extern void log_Open(const char *);
+extern void log_SetTun(int);
+extern void log_Close(void);
+#ifdef __GNUC__
+extern void log_Printf(int, const char *,...)
+ __attribute__ ((format (printf, 2, 3)));
+extern void log_WritePrompts(struct datalink *, const char *, ...)
+ __attribute__ ((format (printf, 2, 3)));
+#else
+extern void log_Printf(int, const char *,...);
+extern void log_WritePrompts(struct datalink *, const char *, ...);
+#endif
+extern void log_DumpBp(int, const char *, const struct mbuf *);
+extern void log_DumpBuff(int, const char *, const u_char *, int);
+extern int log_ShowLevel(struct cmdargs const *);
+extern int log_SetLevel(struct cmdargs const *);
+extern int log_ShowWho(struct cmdargs const *);
+
+extern struct prompt *log_PromptContext;
+extern int log_PromptListChanged;
+extern void log_RegisterPrompt(struct prompt *);
+extern void log_UnRegisterPrompt(struct prompt *);
+extern void log_DestroyPrompts(struct server *);
+extern void log_DisplayPrompts(void);
+extern void log_ActivatePrompt(struct prompt *);
+extern void log_DeactivatePrompt(struct prompt *);
+extern void log_SetTtyCommandMode(struct datalink *);
+extern struct prompt *log_PromptList(void);
diff --git a/usr.sbin/ppp/lqr.c b/usr.sbin/ppp/lqr.c
new file mode 100644
index 0000000..f72c899
--- /dev/null
+++ b/usr.sbin/ppp/lqr.c
@@ -0,0 +1,447 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+
+#ifdef __FreeBSD__
+#include <netinet/in.h>
+#endif
+#include <sys/un.h>
+
+#include <string.h>
+#include <termios.h>
+
+#include "layer.h"
+#include "mbuf.h"
+#include "log.h"
+#include "defs.h"
+#include "timer.h"
+#include "fsm.h"
+#include "acf.h"
+#include "proto.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "lcp.h"
+#include "async.h"
+#include "throughput.h"
+#include "ccp.h"
+#include "link.h"
+#include "descriptor.h"
+#include "physical.h"
+#include "mp.h"
+#include "chat.h"
+#include "auth.h"
+#include "chap.h"
+#include "command.h"
+#include "cbcp.h"
+#include "datalink.h"
+
+struct echolqr {
+ u_int32_t magic;
+ u_int32_t signature;
+ u_int32_t sequence;
+};
+
+#define SIGNATURE 0x594e4f54
+
+static void
+SendEchoReq(struct lcp *lcp)
+{
+ struct hdlc *hdlc = &link2physical(lcp->fsm.link)->hdlc;
+ struct echolqr echo;
+
+ echo.magic = htonl(lcp->want_magic);
+ echo.signature = htonl(SIGNATURE);
+ echo.sequence = htonl(hdlc->lqm.echo.seq_sent);
+ fsm_Output(&lcp->fsm, CODE_ECHOREQ, hdlc->lqm.echo.seq_sent++,
+ (u_char *)&echo, sizeof echo, MB_ECHOOUT);
+}
+
+struct mbuf *
+lqr_RecvEcho(struct fsm *fp, struct mbuf *bp)
+{
+ struct hdlc *hdlc = &link2physical(fp->link)->hdlc;
+ struct lcp *lcp = fsm2lcp(fp);
+ struct echolqr lqr;
+
+ if (m_length(bp) >= sizeof lqr) {
+ m_freem(mbuf_Read(bp, &lqr, sizeof lqr));
+ bp = NULL;
+ lqr.magic = ntohl(lqr.magic);
+ lqr.signature = ntohl(lqr.signature);
+ lqr.sequence = ntohl(lqr.sequence);
+
+ /* Tolerate echo replies with either magic number */
+ if (lqr.magic != 0 && lqr.magic != lcp->his_magic &&
+ lqr.magic != lcp->want_magic) {
+ log_Printf(LogWARN, "%s: lqr_RecvEcho: Bad magic: expected 0x%08x,"
+ " got 0x%08x\n", fp->link->name, lcp->his_magic, lqr.magic);
+ /*
+ * XXX: We should send a terminate request. But poor implementations may
+ * die as a result.
+ */
+ }
+ if (lqr.signature == SIGNATURE) {
+ /* careful not to update lqm.echo.seq_recv with older values */
+ if ((hdlc->lqm.echo.seq_recv > (u_int32_t)0 - 5 && lqr.sequence < 5) ||
+ (hdlc->lqm.echo.seq_recv <= (u_int32_t)0 - 5 &&
+ lqr.sequence > hdlc->lqm.echo.seq_recv))
+ hdlc->lqm.echo.seq_recv = lqr.sequence;
+ } else
+ log_Printf(LogWARN, "lqr_RecvEcho: Got sig 0x%08lx, not 0x%08lx !\n",
+ (u_long)lqr.signature, (u_long)SIGNATURE);
+ } else
+ log_Printf(LogWARN, "lqr_RecvEcho: Got packet size %d, expecting %ld !\n",
+ m_length(bp), (long)sizeof(struct echolqr));
+ return bp;
+}
+
+void
+lqr_ChangeOrder(struct lqrdata *src, struct lqrdata *dst)
+{
+ u_int32_t *sp, *dp;
+ int n;
+
+ sp = (u_int32_t *) src;
+ dp = (u_int32_t *) dst;
+ for (n = 0; n < sizeof(struct lqrdata) / sizeof(u_int32_t); n++, sp++, dp++)
+ *dp = ntohl(*sp);
+}
+
+static void
+SendLqrData(struct lcp *lcp)
+{
+ struct mbuf *bp;
+ int extra;
+
+ extra = proto_WrapperOctets(lcp, PROTO_LQR) +
+ acf_WrapperOctets(lcp, PROTO_LQR);
+ bp = m_get(sizeof(struct lqrdata) + extra, MB_LQROUT);
+ bp->m_len -= extra;
+ bp->m_offset += extra;
+ link_PushPacket(lcp->fsm.link, bp, lcp->fsm.bundle,
+ LINK_QUEUES(lcp->fsm.link) - 1, PROTO_LQR);
+}
+
+static void
+SendLqrReport(void *v)
+{
+ struct lcp *lcp = (struct lcp *)v;
+ struct physical *p = link2physical(lcp->fsm.link);
+
+ timer_Stop(&p->hdlc.lqm.timer);
+
+ if (p->hdlc.lqm.method & LQM_LQR) {
+ if (p->hdlc.lqm.lqr.resent > 5) {
+ /* XXX: Should implement LQM strategy */
+ log_Printf(LogPHASE, "%s: ** Too many LQR packets lost **\n",
+ lcp->fsm.link->name);
+ log_Printf(LogLQM, "%s: Too many LQR packets lost\n",
+ lcp->fsm.link->name);
+ p->hdlc.lqm.method = 0;
+ datalink_Down(p->dl, CLOSE_NORMAL);
+ } else {
+ SendLqrData(lcp);
+ p->hdlc.lqm.lqr.resent++;
+ }
+ } else if (p->hdlc.lqm.method & LQM_ECHO) {
+ if ((p->hdlc.lqm.echo.seq_sent > 5 &&
+ p->hdlc.lqm.echo.seq_sent - 5 > p->hdlc.lqm.echo.seq_recv) ||
+ (p->hdlc.lqm.echo.seq_sent <= 5 &&
+ p->hdlc.lqm.echo.seq_sent > p->hdlc.lqm.echo.seq_recv + 5)) {
+ log_Printf(LogPHASE, "%s: ** Too many ECHO LQR packets lost **\n",
+ lcp->fsm.link->name);
+ log_Printf(LogLQM, "%s: Too many ECHO LQR packets lost\n",
+ lcp->fsm.link->name);
+ p->hdlc.lqm.method = 0;
+ datalink_Down(p->dl, CLOSE_NORMAL);
+ } else
+ SendEchoReq(lcp);
+ }
+ if (p->hdlc.lqm.method && p->hdlc.lqm.timer.load)
+ timer_Start(&p->hdlc.lqm.timer);
+}
+
+struct mbuf *
+lqr_Input(struct bundle *bundle, struct link *l, struct mbuf *bp)
+{
+ struct physical *p = link2physical(l);
+ struct lcp *lcp = p->hdlc.lqm.owner;
+ int len;
+
+ if (p == NULL) {
+ log_Printf(LogERROR, "lqr_Input: Not a physical link - dropped\n");
+ m_freem(bp);
+ return NULL;
+ }
+
+ p->hdlc.lqm.lqr.SaveInLQRs++;
+
+ len = m_length(bp);
+ if (len != sizeof(struct lqrdata))
+ log_Printf(LogWARN, "lqr_Input: Got packet size %d, expecting %ld !\n",
+ len, (long)sizeof(struct lqrdata));
+ else if (!IsAccepted(l->lcp.cfg.lqr) && !(p->hdlc.lqm.method & LQM_LQR)) {
+ bp = m_pullup(proto_Prepend(bp, PROTO_LQR, 0, 0));
+ lcp_SendProtoRej(lcp, MBUF_CTOP(bp), bp->m_len);
+ } else {
+ struct lqrdata *lqr;
+ u_int32_t lastLQR;
+
+ bp = m_pullup(bp);
+ lqr = (struct lqrdata *)MBUF_CTOP(bp);
+ if (ntohl(lqr->MagicNumber) != lcp->his_magic)
+ log_Printf(LogWARN, "lqr_Input: magic 0x%08lx is wrong,"
+ " expecting 0x%08lx\n",
+ (u_long)ntohl(lqr->MagicNumber), (u_long)lcp->his_magic);
+ else {
+ /*
+ * Remember our PeerInLQRs, then convert byte order and save
+ */
+ lastLQR = p->hdlc.lqm.lqr.peer.PeerInLQRs;
+
+ lqr_ChangeOrder(lqr, &p->hdlc.lqm.lqr.peer);
+ lqr_Dump(l->name, "Input", &p->hdlc.lqm.lqr.peer);
+ /* we have received an LQR from peer */
+ p->hdlc.lqm.lqr.resent = 0;
+
+ /*
+ * Generate an LQR response if we're not running an LQR timer OR
+ * two successive LQR's PeerInLQRs are the same OR we're not going to
+ * send our next one before the peers max timeout.
+ */
+ if (p->hdlc.lqm.timer.load == 0 ||
+ !(p->hdlc.lqm.method & LQM_LQR) ||
+ (lastLQR && lastLQR == p->hdlc.lqm.lqr.peer.PeerInLQRs) ||
+ (p->hdlc.lqm.lqr.peer_timeout &&
+ p->hdlc.lqm.timer.rest * 100 / SECTICKS >
+ p->hdlc.lqm.lqr.peer_timeout))
+ SendLqrData(lcp);
+ }
+ }
+ m_freem(bp);
+ return NULL;
+}
+
+/*
+ * When LCP is reached to opened state, We'll start LQM activity.
+ */
+
+static void
+lqr_Setup(struct lcp *lcp)
+{
+ struct physical *physical = link2physical(lcp->fsm.link);
+
+ physical->hdlc.lqm.lqr.resent = 0;
+ physical->hdlc.lqm.echo.seq_sent = 0;
+ physical->hdlc.lqm.echo.seq_recv = 0;
+ memset(&physical->hdlc.lqm.lqr.peer, '\0',
+ sizeof physical->hdlc.lqm.lqr.peer);
+
+ physical->hdlc.lqm.method = LQM_ECHO;
+ if (IsEnabled(lcp->cfg.lqr) && !REJECTED(lcp, TY_QUALPROTO))
+ physical->hdlc.lqm.method |= LQM_LQR;
+ timer_Stop(&physical->hdlc.lqm.timer);
+
+ physical->hdlc.lqm.lqr.peer_timeout = lcp->his_lqrperiod;
+ if (lcp->his_lqrperiod)
+ log_Printf(LogLQM, "%s: Expecting LQR every %d.%02d secs\n",
+ physical->link.name, lcp->his_lqrperiod / 100,
+ lcp->his_lqrperiod % 100);
+
+ if (lcp->want_lqrperiod) {
+ log_Printf(LogLQM, "%s: Will send %s every %d.%02d secs\n",
+ physical->link.name,
+ physical->hdlc.lqm.method & LQM_LQR ? "LQR" : "ECHO LQR",
+ lcp->want_lqrperiod / 100, lcp->want_lqrperiod % 100);
+ physical->hdlc.lqm.timer.load = lcp->want_lqrperiod * SECTICKS / 100;
+ physical->hdlc.lqm.timer.func = SendLqrReport;
+ physical->hdlc.lqm.timer.name = "lqm";
+ physical->hdlc.lqm.timer.arg = lcp;
+ } else {
+ physical->hdlc.lqm.timer.load = 0;
+ if (!lcp->his_lqrperiod)
+ log_Printf(LogLQM, "%s: LQR/ECHO LQR not negotiated\n",
+ physical->link.name);
+ }
+}
+
+void
+lqr_Start(struct lcp *lcp)
+{
+ struct physical *p = link2physical(lcp->fsm.link);
+
+ lqr_Setup(lcp);
+ if (p->hdlc.lqm.timer.load)
+ SendLqrReport(lcp);
+}
+
+void
+lqr_reStart(struct lcp *lcp)
+{
+ struct physical *p = link2physical(lcp->fsm.link);
+
+ lqr_Setup(lcp);
+ if (p->hdlc.lqm.timer.load)
+ timer_Start(&p->hdlc.lqm.timer);
+}
+
+void
+lqr_StopTimer(struct physical *physical)
+{
+ timer_Stop(&physical->hdlc.lqm.timer);
+}
+
+void
+lqr_Stop(struct physical *physical, int method)
+{
+ if (method == LQM_LQR)
+ log_Printf(LogLQM, "%s: Stop sending LQR, Use LCP ECHO instead.\n",
+ physical->link.name);
+ if (method == LQM_ECHO)
+ log_Printf(LogLQM, "%s: Stop sending LCP ECHO.\n",
+ physical->link.name);
+ physical->hdlc.lqm.method &= ~method;
+ if (physical->hdlc.lqm.method)
+ SendLqrReport(physical->hdlc.lqm.owner);
+ else
+ timer_Stop(&physical->hdlc.lqm.timer);
+}
+
+void
+lqr_Dump(const char *link, const char *message, const struct lqrdata *lqr)
+{
+ if (log_IsKept(LogLQM)) {
+ log_Printf(LogLQM, "%s: %s:\n", link, message);
+ log_Printf(LogLQM, " Magic: %08x LastOutLQRs: %08x\n",
+ lqr->MagicNumber, lqr->LastOutLQRs);
+ log_Printf(LogLQM, " LastOutPackets: %08x LastOutOctets: %08x\n",
+ lqr->LastOutPackets, lqr->LastOutOctets);
+ log_Printf(LogLQM, " PeerInLQRs: %08x PeerInPackets: %08x\n",
+ lqr->PeerInLQRs, lqr->PeerInPackets);
+ log_Printf(LogLQM, " PeerInDiscards: %08x PeerInErrors: %08x\n",
+ lqr->PeerInDiscards, lqr->PeerInErrors);
+ log_Printf(LogLQM, " PeerInOctets: %08x PeerOutLQRs: %08x\n",
+ lqr->PeerInOctets, lqr->PeerOutLQRs);
+ log_Printf(LogLQM, " PeerOutPackets: %08x PeerOutOctets: %08x\n",
+ lqr->PeerOutPackets, lqr->PeerOutOctets);
+ }
+}
+
+static struct mbuf *
+lqr_LayerPush(struct bundle *b, struct link *l, struct mbuf *bp,
+ int pri, u_short *proto)
+{
+ struct physical *p = link2physical(l);
+ int len;
+
+ if (!p) {
+ /* Oops - can't happen :-] */
+ m_freem(bp);
+ return NULL;
+ }
+
+ /*
+ * From rfc1989:
+ *
+ * All octets which are included in the FCS calculation MUST be counted,
+ * including the packet header, the information field, and any padding.
+ * The FCS octets MUST also be counted, and one flag octet per frame
+ * MUST be counted. All other octets (such as additional flag
+ * sequences, and escape bits or octets) MUST NOT be counted.
+ *
+ * As we're stacked before the HDLC layer (otherwise HDLC wouldn't be
+ * able to calculate the FCS), we must not forget about these additional
+ * bytes when we're asynchronous.
+ *
+ * We're also expecting to be stacked *before* the proto and acf layers.
+ * If we were after these, it makes alignment more of a pain, and we
+ * don't do LQR without these layers.
+ */
+
+ bp = m_pullup(bp);
+ len = m_length(bp);
+
+ if (!physical_IsSync(p))
+ p->hdlc.lqm.OutOctets += hdlc_WrapperOctets(&l->lcp, *proto);
+ p->hdlc.lqm.OutOctets += acf_WrapperOctets(&l->lcp, *proto) +
+ proto_WrapperOctets(&l->lcp, *proto) + len + 1;
+ p->hdlc.lqm.OutPackets++;
+
+ if (*proto == PROTO_LQR) {
+ /* Overwrite the entire packet (created in SendLqrData()) */
+ struct lqrdata lqr;
+
+ lqr.MagicNumber = p->link.lcp.want_magic;
+ lqr.LastOutLQRs = p->hdlc.lqm.lqr.peer.PeerOutLQRs;
+ lqr.LastOutPackets = p->hdlc.lqm.lqr.peer.PeerOutPackets;
+ lqr.LastOutOctets = p->hdlc.lqm.lqr.peer.PeerOutOctets;
+ lqr.PeerInLQRs = p->hdlc.lqm.lqr.SaveInLQRs;
+ lqr.PeerInPackets = p->hdlc.lqm.SaveInPackets;
+ lqr.PeerInDiscards = p->hdlc.lqm.SaveInDiscards;
+ lqr.PeerInErrors = p->hdlc.lqm.SaveInErrors;
+ lqr.PeerInOctets = p->hdlc.lqm.SaveInOctets;
+ lqr.PeerOutPackets = p->hdlc.lqm.OutPackets;
+ lqr.PeerOutOctets = p->hdlc.lqm.OutOctets;
+ if (p->hdlc.lqm.lqr.peer.LastOutLQRs == p->hdlc.lqm.lqr.OutLQRs) {
+ /*
+ * only increment if it's the first time or we've got a reply
+ * from the last one
+ */
+ lqr.PeerOutLQRs = ++p->hdlc.lqm.lqr.OutLQRs;
+ lqr_Dump(l->name, "Output", &lqr);
+ } else {
+ lqr.PeerOutLQRs = p->hdlc.lqm.lqr.OutLQRs;
+ lqr_Dump(l->name, "Output (again)", &lqr);
+ }
+ lqr_ChangeOrder(&lqr, (struct lqrdata *)MBUF_CTOP(bp));
+ }
+
+ return bp;
+}
+
+static struct mbuf *
+lqr_LayerPull(struct bundle *b, struct link *l, struct mbuf *bp, u_short *proto)
+{
+ /*
+ * We mark the packet as ours but don't do anything 'till it's dispatched
+ * to lqr_Input()
+ */
+ if (*proto == PROTO_LQR)
+ m_settype(bp, MB_LQRIN);
+ return bp;
+}
+
+/*
+ * Statistics for pulled packets are recorded either in hdlc_PullPacket()
+ * or sync_PullPacket()
+ */
+
+struct layer lqrlayer = { LAYER_LQR, "lqr", lqr_LayerPush, lqr_LayerPull };
diff --git a/usr.sbin/ppp/lqr.h b/usr.sbin/ppp/lqr.h
new file mode 100644
index 0000000..16bab39
--- /dev/null
+++ b/usr.sbin/ppp/lqr.h
@@ -0,0 +1,71 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Structure of LQR packet defined in RFC1989
+ */
+struct lqrdata {
+ u_int32_t MagicNumber;
+ u_int32_t LastOutLQRs; /* most recently received PeerOutLQRs */
+ u_int32_t LastOutPackets; /* most recently received PeerOutPackets */
+ u_int32_t LastOutOctets; /* most recently received PeerOutOctets */
+ u_int32_t PeerInLQRs; /* Peers SaveInLQRs */
+ u_int32_t PeerInPackets; /* Peers SaveInPackets */
+ u_int32_t PeerInDiscards; /* Peers SaveInDiscards */
+ u_int32_t PeerInErrors; /* Peers SaveInErrors */
+ u_int32_t PeerInOctets; /* Peers SaveInOctets */
+ u_int32_t PeerOutLQRs; /* Peers OutLQRs (hdlc.h) */
+ u_int32_t PeerOutPackets; /* Peers OutPackets (hdlc.h) */
+ u_int32_t PeerOutOctets; /* Peers OutOctets (hdlc.h) */
+};
+
+/*
+ * We support LQR and ECHO as LQM method
+ */
+#define LQM_LQR 1
+#define LQM_ECHO 2
+
+struct mbuf;
+struct physical;
+struct lcp;
+struct fsm;
+struct link;
+struct bundle;
+
+extern void lqr_Dump(const char *, const char *, const struct lqrdata *);
+extern void lqr_ChangeOrder(struct lqrdata *, struct lqrdata *);
+extern void lqr_Start(struct lcp *);
+extern void lqr_reStart(struct lcp *);
+extern void lqr_Stop(struct physical *, int);
+extern void lqr_StopTimer(struct physical *);
+extern struct mbuf *lqr_RecvEcho(struct fsm *, struct mbuf *);
+extern struct mbuf *lqr_Input(struct bundle *, struct link *, struct mbuf *);
+
+extern struct layer lqrlayer;
diff --git a/usr.sbin/ppp/main.c b/usr.sbin/ppp/main.c
new file mode 100644
index 0000000..fe5f2dc
--- /dev/null
+++ b/usr.sbin/ppp/main.c
@@ -0,0 +1,674 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/un.h>
+#include <sys/socket.h>
+#include <net/route.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <termios.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#ifndef NONAT
+#ifdef LOCALNAT
+#include "alias.h"
+#else
+#include <alias.h>
+#endif
+#endif
+
+#include "layer.h"
+#include "probe.h"
+#include "mbuf.h"
+#include "log.h"
+#include "defs.h"
+#include "id.h"
+#include "timer.h"
+#include "fsm.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "iplist.h"
+#include "throughput.h"
+#include "slcompress.h"
+#include "ncpaddr.h"
+#include "ipcp.h"
+#include "filter.h"
+#include "descriptor.h"
+#include "link.h"
+#include "mp.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+#include "auth.h"
+#include "systems.h"
+#include "sig.h"
+#include "main.h"
+#include "server.h"
+#include "prompt.h"
+#include "chat.h"
+#include "chap.h"
+#include "cbcp.h"
+#include "datalink.h"
+#include "iface.h"
+
+#ifndef O_NONBLOCK
+#ifdef O_NDELAY
+#define O_NONBLOCK O_NDELAY
+#endif
+#endif
+
+static void DoLoop(struct bundle *);
+static void TerminalStop(int);
+
+static struct bundle *SignalBundle;
+static struct prompt *SignalPrompt;
+
+void
+Cleanup(int excode)
+{
+ SignalBundle->CleaningUp = 1;
+ bundle_Close(SignalBundle, NULL, CLOSE_STAYDOWN);
+}
+
+void
+AbortProgram(int excode)
+{
+ if (SignalBundle)
+ server_Close(SignalBundle);
+ log_Printf(LogPHASE, "PPP Terminated (%s).\n", ex_desc(excode));
+ if (SignalBundle) {
+ bundle_Close(SignalBundle, NULL, CLOSE_STAYDOWN);
+ bundle_Destroy(SignalBundle);
+ }
+ log_Close();
+ exit(excode);
+}
+
+static void
+CloseConnection(int signo)
+{
+ /* NOTE, these are manual, we've done a setsid() */
+ sig_signal(SIGINT, SIG_IGN);
+ log_Printf(LogPHASE, "Caught signal %d, abort connection(s)\n", signo);
+ bundle_Down(SignalBundle, CLOSE_STAYDOWN);
+ sig_signal(SIGINT, CloseConnection);
+}
+
+static void
+CloseSession(int signo)
+{
+ log_Printf(LogPHASE, "Signal %d, terminate.\n", signo);
+ Cleanup(EX_TERM);
+}
+
+static pid_t BGPid = 0;
+
+static void
+KillChild(int signo)
+{
+ signal(signo, SIG_IGN);
+ log_Printf(LogPHASE, "Parent: Signal %d\n", signo);
+ kill(BGPid, SIGINT);
+}
+
+static void
+TerminalCont(int signo)
+{
+ signal(SIGCONT, SIG_DFL);
+ prompt_Continue(SignalPrompt);
+}
+
+static void
+TerminalStop(int signo)
+{
+ prompt_Suspend(SignalPrompt);
+ signal(SIGCONT, TerminalCont);
+ raise(SIGSTOP);
+}
+
+static void
+BringDownServer(int signo)
+{
+ /* Drops all child prompts too ! */
+ if (server_Close(SignalBundle))
+ log_Printf(LogPHASE, "Closed server socket\n");
+}
+
+static void
+RestartServer(int signo)
+{
+ /* Drops all child prompts and re-opens the socket */
+ server_Reopen(SignalBundle);
+}
+
+static void
+Usage(void)
+{
+ fprintf(stderr, "usage: ppp [-auto | -foreground | -background | -direct |"
+ " -dedicated | -ddial | -interactive]"
+#ifndef NOALIAS
+ " [-nat]"
+#endif
+ " [-quiet] [-unit N] [system ...]\n");
+ exit(EX_START);
+}
+
+struct switches {
+ unsigned nat : 1;
+ unsigned fg : 1;
+ unsigned quiet : 1;
+ int mode;
+ int unit;
+};
+
+static int
+ProcessArgs(int argc, char **argv, struct switches *sw)
+{
+ int optc, newmode, arg;
+ char *cp;
+
+ optc = 0;
+ memset(sw, '\0', sizeof *sw);
+ sw->mode = PHYS_INTERACTIVE;
+ sw->unit = -1;
+
+ for (arg = 1; arg < argc && *argv[arg] == '-'; arg++, optc++) {
+ cp = argv[arg] + 1;
+ newmode = Nam2mode(cp);
+ switch (newmode) {
+ case PHYS_NONE:
+ if (strcmp(cp, "nat") == 0) {
+#ifdef NONAT
+ log_Printf(LogWARN, "%s ignored: NAT is compiled out\n", argv[arg]);
+#else
+ sw->nat = 1;
+#endif
+ optc--; /* this option isn't exclusive */
+ } else if (strcmp(cp, "alias") == 0) {
+#ifdef NONAT
+ log_Printf(LogWARN, "%s ignored: NAT is compiled out\n", argv[arg]);
+ fprintf(stderr, "%s ignored: NAT is compiled out\n", argv[arg]);
+#else
+ log_Printf(LogWARN, "%s is deprecated\n", argv[arg]);
+ fprintf(stderr, "%s is deprecated\n", argv[arg]);
+ sw->nat = 1;
+#endif
+ optc--; /* this option isn't exclusive */
+ } else if (strncmp(cp, "unit", 4) == 0) {
+ optc--; /* this option isn't exclusive */
+ if (cp[4] == '\0') {
+ optc--; /* nor is the argument */
+ if (++arg == argc) {
+ fprintf(stderr, "-unit: Expected unit number\n");
+ Usage();
+ } else
+ sw->unit = atoi(argv[arg]);
+ } else
+ sw->unit = atoi(cp + 4);
+ } else if (strcmp(cp, "quiet") == 0) {
+ sw->quiet = 1;
+ optc--; /* this option isn't exclusive */
+ } else
+ Usage();
+ break;
+
+ case PHYS_ALL:
+ Usage();
+ break;
+
+ default:
+ sw->mode = newmode;
+ if (newmode == PHYS_FOREGROUND)
+ sw->fg = 1;
+ }
+ }
+
+ if (optc > 1) {
+ fprintf(stderr, "You may specify only one mode.\n");
+ exit(EX_START);
+ }
+
+ if (sw->mode == PHYS_AUTO && arg == argc) {
+ fprintf(stderr, "A system must be specified in auto mode.\n");
+ exit(EX_START);
+ }
+
+ return arg; /* Don't SetLabel yet ! */
+}
+
+static void
+CheckLabel(const char *label, struct prompt *prompt, int mode)
+{
+ const char *err;
+
+ if ((err = system_IsValid(label, prompt, mode)) != NULL) {
+ fprintf(stderr, "%s: %s\n", label, err);
+ if (mode == PHYS_DIRECT)
+ log_Printf(LogWARN, "Label %s rejected -direct connection: %s\n",
+ label, err);
+ log_Close();
+ exit(1);
+ }
+}
+
+
+int
+main(int argc, char **argv)
+{
+ char *name;
+ const char *lastlabel;
+ int arg, f, holdfd[3], label;
+ struct bundle *bundle;
+ struct prompt *prompt;
+ struct switches sw;
+
+ probe_Init();
+
+ /*
+ * We open 3 descriptors to ensure that STDIN_FILENO, STDOUT_FILENO and
+ * STDERR_FILENO are always open. These are closed before DoLoop(),
+ * but *after* we've avoided the possibility of erroneously closing
+ * an important descriptor with close(STD{IN,OUT,ERR}_FILENO).
+ */
+ if ((holdfd[0] = open(_PATH_DEVNULL, O_RDWR)) == -1) {
+ fprintf(stderr, "Cannot open %s !\n", _PATH_DEVNULL);
+ return 2;
+ }
+ for (f = 1; f < sizeof holdfd / sizeof *holdfd; f++)
+ holdfd[f] = dup(holdfd[0]);
+
+ name = strrchr(argv[0], '/');
+ log_Open(name ? name + 1 : argv[0]);
+
+#ifndef NONAT
+ PacketAliasInit();
+#endif
+ label = ProcessArgs(argc, argv, &sw);
+
+ /*
+ * A FreeBSD & OpenBSD hack to dodge a bug in the tty driver that drops
+ * output occasionally.... I must find the real reason some time. To
+ * display the dodgy behaviour, comment out this bit, make yourself a large
+ * routing table and then run ppp in interactive mode. The `show route'
+ * command will drop chunks of data !!!
+ */
+ if (sw.mode == PHYS_INTERACTIVE) {
+ close(STDIN_FILENO);
+ if (open(_PATH_TTY, O_RDONLY) != STDIN_FILENO) {
+ fprintf(stderr, "Cannot open %s for input !\n", _PATH_TTY);
+ return 2;
+ }
+ }
+
+ /* Allow output for the moment (except in direct mode) */
+ if (sw.mode == PHYS_DIRECT)
+ prompt = NULL;
+ else
+ SignalPrompt = prompt = prompt_Create(NULL, NULL, PROMPT_STD);
+
+ ID0init();
+ if (ID0realuid() != 0) {
+ char conf[200], *ptr;
+
+ snprintf(conf, sizeof conf, "%s/%s", PPP_CONFDIR, CONFFILE);
+ do {
+ struct stat sb;
+
+ if (stat(conf, &sb) == 0 && sb.st_mode & S_IWOTH) {
+ log_Printf(LogALERT, "ppp: Access violation: Please protect %s\n",
+ conf);
+ return -1;
+ }
+ ptr = conf + strlen(conf)-2;
+ while (ptr > conf && *ptr != '/')
+ *ptr-- = '\0';
+ } while (ptr >= conf);
+ }
+
+ if (label < argc)
+ for (arg = label; arg < argc; arg++)
+ CheckLabel(argv[arg], prompt, sw.mode);
+ else
+ CheckLabel("default", prompt, sw.mode);
+
+ if (!sw.quiet)
+ prompt_Printf(prompt, "Working in %s mode\n", mode2Nam(sw.mode));
+
+ if ((bundle = bundle_Create(TUN_PREFIX, sw.mode, sw.unit)) == NULL)
+ return EX_START;
+
+ /* NOTE: We may now have changed argv[1] via a ``set proctitle'' */
+
+ if (prompt) {
+ prompt->bundle = bundle; /* couldn't do it earlier */
+ if (!sw.quiet)
+ prompt_Printf(prompt, "Using interface: %s\n", bundle->iface->name);
+ }
+ SignalBundle = bundle;
+ bundle->NatEnabled = sw.nat;
+ if (sw.nat)
+ bundle->cfg.opt |= OPT_IFACEALIAS;
+
+ if (system_Select(bundle, "default", CONFFILE, prompt, NULL) < 0)
+ prompt_Printf(prompt, "Warning: No default entry found in config file.\n");
+
+ sig_signal(SIGHUP, CloseSession);
+ sig_signal(SIGTERM, CloseSession);
+ sig_signal(SIGINT, CloseConnection);
+ sig_signal(SIGQUIT, CloseSession);
+ sig_signal(SIGALRM, SIG_IGN);
+ signal(SIGPIPE, SIG_IGN);
+
+ if (sw.mode == PHYS_INTERACTIVE)
+ sig_signal(SIGTSTP, TerminalStop);
+
+ sig_signal(SIGUSR1, RestartServer);
+ sig_signal(SIGUSR2, BringDownServer);
+
+ lastlabel = argv[argc - 1];
+ for (arg = label; arg < argc; arg++) {
+ /* In case we use LABEL or ``set enddisc label'' */
+ bundle_SetLabel(bundle, lastlabel);
+ system_Select(bundle, argv[arg], CONFFILE, prompt, NULL);
+ }
+
+ if (label < argc)
+ /* In case the last label did a ``load'' */
+ bundle_SetLabel(bundle, lastlabel);
+
+ if (sw.mode == PHYS_AUTO &&
+ ncprange_family(&bundle->ncp.ipcp.cfg.peer_range) == AF_UNSPEC) {
+ prompt_Printf(prompt, "You must ``set ifaddr'' with a peer address "
+ "in auto mode.\n");
+ AbortProgram(EX_START);
+ }
+
+ if (sw.mode != PHYS_INTERACTIVE) {
+ if (sw.mode != PHYS_DIRECT) {
+ if (!sw.fg) {
+ int bgpipe[2];
+ pid_t bgpid;
+
+ if (sw.mode == PHYS_BACKGROUND && pipe(bgpipe)) {
+ log_Printf(LogERROR, "pipe: %s\n", strerror(errno));
+ AbortProgram(EX_SOCK);
+ }
+
+ bgpid = fork();
+ if (bgpid == -1) {
+ log_Printf(LogERROR, "fork: %s\n", strerror(errno));
+ AbortProgram(EX_SOCK);
+ }
+
+ if (bgpid) {
+ char c = EX_NORMAL;
+ int ret;
+
+ if (sw.mode == PHYS_BACKGROUND) {
+ close(bgpipe[1]);
+ BGPid = bgpid;
+ /* If we get a signal, kill the child */
+ signal(SIGHUP, KillChild);
+ signal(SIGTERM, KillChild);
+ signal(SIGINT, KillChild);
+ signal(SIGQUIT, KillChild);
+
+ /* Wait for our child to close its pipe before we exit */
+ while ((ret = read(bgpipe[0], &c, 1)) == 1) {
+ switch (c) {
+ case EX_NORMAL:
+ if (!sw.quiet) {
+ prompt_Printf(prompt, "PPP enabled\n");
+ log_Printf(LogPHASE, "Parent: PPP enabled\n");
+ }
+ break;
+ case EX_REDIAL:
+ if (!sw.quiet)
+ prompt_Printf(prompt, "Attempting redial\n");
+ continue;
+ case EX_RECONNECT:
+ if (!sw.quiet)
+ prompt_Printf(prompt, "Attempting reconnect\n");
+ continue;
+ default:
+ prompt_Printf(prompt, "Child failed (%s)\n",
+ ex_desc((int)c));
+ log_Printf(LogPHASE, "Parent: Child failed (%s)\n",
+ ex_desc((int) c));
+ }
+ break;
+ }
+ if (ret != 1) {
+ prompt_Printf(prompt, "Child exit, no status.\n");
+ log_Printf(LogPHASE, "Parent: Child exit, no status.\n");
+ }
+ close(bgpipe[0]);
+ }
+ return c;
+ } else if (sw.mode == PHYS_BACKGROUND) {
+ close(bgpipe[0]);
+ bundle->notify.fd = bgpipe[1];
+ }
+
+ bundle_ChangedPID(bundle);
+ bundle_LockTun(bundle); /* we have a new pid */
+ }
+
+ /* -auto, -dedicated, -ddial, -foreground & -background */
+ prompt_Destroy(prompt, 0);
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+ close(STDIN_FILENO);
+ if (!sw.fg)
+ setsid();
+ } else {
+ /* -direct - STDIN_FILENO gets used by physical_Open */
+ prompt_TtyInit(NULL);
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+ }
+ } else {
+ /* -interactive */
+ close(STDERR_FILENO);
+ prompt_TtyInit(prompt);
+ prompt_TtyCommandMode(prompt);
+ prompt_Required(prompt);
+ }
+
+ /* We can get rid of these now */
+ for (f = 0; f < sizeof holdfd / sizeof *holdfd; f++)
+ close(holdfd[f]);
+
+ log_Printf(LogPHASE, "PPP Started (%s mode).\n", mode2Nam(sw.mode));
+ DoLoop(bundle);
+ AbortProgram(EX_NORMAL);
+
+ return EX_NORMAL;
+}
+
+static void
+DoLoop(struct bundle *bundle)
+{
+ fd_set *rfds, *wfds, *efds;
+ int i, nfds, nothing_done;
+
+ if ((rfds = mkfdset()) == NULL) {
+ log_Printf(LogERROR, "DoLoop: Cannot create fd_set\n");
+ return;
+ }
+
+ if ((wfds = mkfdset()) == NULL) {
+ log_Printf(LogERROR, "DoLoop: Cannot create fd_set\n");
+ free(rfds);
+ return;
+ }
+
+ if ((efds = mkfdset()) == NULL) {
+ log_Printf(LogERROR, "DoLoop: Cannot create fd_set\n");
+ free(rfds);
+ free(wfds);
+ return;
+ }
+
+ for (; !bundle_IsDead(bundle); bundle_CleanDatalinks(bundle)) {
+ nfds = 0;
+ zerofdset(rfds);
+ zerofdset(wfds);
+ zerofdset(efds);
+
+ /* All our datalinks, the tun device and the MP socket */
+ descriptor_UpdateSet(&bundle->desc, rfds, wfds, efds, &nfds);
+
+ /* All our prompts and the diagnostic socket */
+ descriptor_UpdateSet(&server.desc, rfds, NULL, NULL, &nfds);
+
+ bundle_CleanDatalinks(bundle);
+ if (bundle_IsDead(bundle))
+ /* Don't select - we'll be here forever */
+ break;
+
+ /*
+ * It's possible that we've had a signal since we last checked. If
+ * we don't check again before calling select(), we may end up stuck
+ * after having missed the event.... sig_Handle() tries to be as
+ * quick as possible if nothing is likely to have happened.
+ * This is only really likely if we block in open(... O_NONBLOCK)
+ * which will happen with a misconfigured device.
+ */
+ if (sig_Handle())
+ continue;
+
+ i = select(nfds, rfds, wfds, efds, NULL);
+
+ if (i < 0 && errno != EINTR) {
+ log_Printf(LogERROR, "DoLoop: select(): %s\n", strerror(errno));
+ if (log_IsKept(LogTIMER)) {
+ struct timeval t;
+
+ for (i = 0; i <= nfds; i++) {
+ if (FD_ISSET(i, rfds)) {
+ log_Printf(LogTIMER, "Read set contains %d\n", i);
+ FD_CLR(i, rfds);
+ t.tv_sec = t.tv_usec = 0;
+ if (select(nfds, rfds, wfds, efds, &t) != -1) {
+ log_Printf(LogTIMER, "The culprit !\n");
+ break;
+ }
+ }
+ if (FD_ISSET(i, wfds)) {
+ log_Printf(LogTIMER, "Write set contains %d\n", i);
+ FD_CLR(i, wfds);
+ t.tv_sec = t.tv_usec = 0;
+ if (select(nfds, rfds, wfds, efds, &t) != -1) {
+ log_Printf(LogTIMER, "The culprit !\n");
+ break;
+ }
+ }
+ if (FD_ISSET(i, efds)) {
+ log_Printf(LogTIMER, "Error set contains %d\n", i);
+ FD_CLR(i, efds);
+ t.tv_sec = t.tv_usec = 0;
+ if (select(nfds, rfds, wfds, efds, &t) != -1) {
+ log_Printf(LogTIMER, "The culprit !\n");
+ break;
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ log_Printf(LogTIMER, "Select returns %d\n", i);
+
+ sig_Handle();
+
+ if (i <= 0)
+ continue;
+
+ for (i = 0; i <= nfds; i++)
+ if (FD_ISSET(i, efds)) {
+ log_Printf(LogPHASE, "Exception detected on descriptor %d\n", i);
+ /* We deal gracefully with link descriptor exceptions */
+ if (!bundle_Exception(bundle, i)) {
+ log_Printf(LogERROR, "Exception cannot be handled !\n");
+ break;
+ }
+ }
+
+ if (i <= nfds)
+ break;
+
+ nothing_done = 1;
+
+ if (descriptor_IsSet(&server.desc, rfds)) {
+ descriptor_Read(&server.desc, bundle, rfds);
+ nothing_done = 0;
+ }
+
+ if (descriptor_IsSet(&bundle->desc, rfds)) {
+ descriptor_Read(&bundle->desc, bundle, rfds);
+ nothing_done = 0;
+ }
+
+ if (descriptor_IsSet(&bundle->desc, wfds))
+ if (descriptor_Write(&bundle->desc, bundle, wfds) <= 0 && nothing_done) {
+ /*
+ * This is disastrous. The OS has told us that something is
+ * writable, and all our write()s have failed. Rather than
+ * going back immediately to do our UpdateSet()s and select(),
+ * we sleep for a bit to avoid gobbling up all cpu time.
+ */
+ struct timeval t;
+
+ t.tv_sec = 0;
+ t.tv_usec = 100000;
+ select(0, NULL, NULL, NULL, &t);
+ }
+ }
+
+ log_Printf(LogDEBUG, "DoLoop done.\n");
+}
diff --git a/usr.sbin/ppp/main.h b/usr.sbin/ppp/main.h
new file mode 100644
index 0000000..46e63b1
--- /dev/null
+++ b/usr.sbin/ppp/main.h
@@ -0,0 +1,32 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+extern void Cleanup(int);
+extern void AbortProgram(int);
diff --git a/usr.sbin/ppp/mbuf.c b/usr.sbin/ppp/mbuf.c
new file mode 100644
index 0000000..4ba547e
--- /dev/null
+++ b/usr.sbin/ppp/mbuf.c
@@ -0,0 +1,435 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <termios.h>
+
+#include "defs.h"
+#include "command.h"
+#include "mbuf.h"
+#include "log.h"
+#include "descriptor.h"
+#include "prompt.h"
+#include "main.h"
+
+#define BUCKET_CHUNK 20
+#define BUCKET_HASH 256
+
+struct mbucket;
+
+struct mfree {
+ struct mbucket *next;
+ size_t count;
+};
+
+static struct mbucket {
+ union {
+ struct mbuf m;
+ struct mfree f;
+ } u;
+} *bucket[(M_MAXLEN + sizeof(struct mbuf)) / BUCKET_HASH];
+
+#define M_BINDEX(sz) (((sz) + sizeof(struct mbuf) - 1) / BUCKET_HASH)
+#define M_BUCKET(sz) (bucket + M_BINDEX(sz))
+#define M_ROUNDUP(sz) ((M_BINDEX(sz) + 1) * BUCKET_HASH)
+
+static struct memmap {
+ struct mbuf *queue;
+ size_t fragments;
+ size_t octets;
+} MemMap[MB_MAX + 1];
+
+static unsigned long long mbuf_Mallocs, mbuf_Frees;
+
+int
+m_length(struct mbuf *bp)
+{
+ int len;
+
+ for (len = 0; bp; bp = bp->m_next)
+ len += bp->m_len;
+ return len;
+}
+
+static const char *
+mbuftype(int type)
+{
+ static const char * const mbufdesc[MB_MAX] = {
+ "ip in", "ip out", "ipv6 in", "ipv6 out", "nat in", "nat out",
+ "mp in", "mp out", "vj in", "vj out", "icompd in", "icompd out",
+ "compd in", "compd out", "lqr in", "lqr out", "echo in", "echo out",
+ "proto in", "proto out", "acf in", "acf out", "sync in", "sync out",
+ "hdlc in", "hdlc out", "async in", "async out", "cbcp in", "cbcp out",
+ "chap in", "chap out", "pap in", "pap out", "ccp in", "ccp out",
+ "ipcp in", "ipcp out", "ipv6cp in", "ipv6cp out", "lcp in", "lcp out"
+ };
+
+ return type < 0 || type >= MB_MAX ? "unknown" : mbufdesc[type];
+}
+
+struct mbuf *
+m_get(size_t m_len, int type)
+{
+ struct mbucket **mb;
+ struct mbuf *bp;
+ size_t size;
+
+ if (type > MB_MAX) {
+ log_Printf(LogERROR, "Bad mbuf type %d\n", type);
+ type = MB_UNKNOWN;
+ }
+
+ if (m_len > M_MAXLEN || m_len == 0) {
+ log_Printf(LogERROR, "Request for mbuf size %lu (\"%s\") denied !\n",
+ (u_long)m_len, mbuftype(type));
+ AbortProgram(EX_OSERR);
+ }
+
+ mb = M_BUCKET(m_len);
+ size = M_ROUNDUP(m_len);
+
+ if (*mb) {
+ /* We've got some free blocks of the right size */
+ bp = &(*mb)->u.m;
+ if (--(*mb)->u.f.count == 0)
+ *mb = (*mb)->u.f.next;
+ else {
+ ((struct mbucket *)((char *)*mb + size))->u.f.count = (*mb)->u.f.count;
+ *mb = (struct mbucket *)((char *)*mb + size);
+ (*mb)->u.f.next = NULL;
+ }
+ } else {
+ /*
+ * Allocate another chunk of mbufs, use the first and put the rest on
+ * the free list
+ */
+ *mb = (struct mbucket *)malloc(BUCKET_CHUNK * size);
+ if (*mb == NULL) {
+ log_Printf(LogALERT, "Failed to allocate memory (%lu)\n",
+ (unsigned long)BUCKET_CHUNK * size);
+ AbortProgram(EX_OSERR);
+ }
+ bp = &(*mb)->u.m;
+ *mb = (struct mbucket *)((char *)*mb + size);
+ (*mb)->u.f.count = BUCKET_CHUNK - 1;
+ (*mb)->u.f.next = NULL;
+ }
+
+ mbuf_Mallocs++;
+
+ memset(bp, '\0', sizeof(struct mbuf));
+ bp->m_size = size - sizeof *bp;
+ bp->m_len = m_len;
+ bp->m_type = type;
+
+ MemMap[type].fragments++;
+ MemMap[type].octets += bp->m_size;
+
+ return bp;
+}
+
+struct mbuf *
+m_free(struct mbuf *bp)
+{
+ struct mbucket **mb, *f;
+ struct mbuf *nbp;
+
+ if ((f = (struct mbucket *)bp) != NULL) {
+ MemMap[bp->m_type].fragments--;
+ MemMap[bp->m_type].octets -= bp->m_size;
+
+ nbp = bp->m_next;
+ mb = M_BUCKET(bp->m_size);
+ f->u.f.next = *mb;
+ f->u.f.count = 1;
+ *mb = f;
+
+ mbuf_Frees++;
+ bp = nbp;
+ }
+
+ return bp;
+}
+
+void
+m_freem(struct mbuf *bp)
+{
+ while (bp)
+ bp = m_free(bp);
+}
+
+struct mbuf *
+mbuf_Read(struct mbuf *bp, void *v, size_t len)
+{
+ int nb;
+ u_char *ptr = v;
+
+ while (bp && len > 0) {
+ if (len > bp->m_len)
+ nb = bp->m_len;
+ else
+ nb = len;
+ if (nb) {
+ memcpy(ptr, MBUF_CTOP(bp), nb);
+ ptr += nb;
+ bp->m_len -= nb;
+ len -= nb;
+ bp->m_offset += nb;
+ }
+ if (bp->m_len == 0)
+ bp = m_free(bp);
+ }
+
+ while (bp && bp->m_len == 0)
+ bp = m_free(bp);
+
+ return bp;
+}
+
+size_t
+mbuf_View(struct mbuf *bp, void *v, size_t len)
+{
+ size_t nb, l = len;
+ u_char *ptr = v;
+
+ while (bp && l > 0) {
+ if (l > bp->m_len)
+ nb = bp->m_len;
+ else
+ nb = l;
+ memcpy(ptr, MBUF_CTOP(bp), nb);
+ ptr += nb;
+ l -= nb;
+ bp = bp->m_next;
+ }
+
+ return len - l;
+}
+
+struct mbuf *
+m_prepend(struct mbuf *bp, const void *ptr, size_t len, size_t extra)
+{
+ struct mbuf *head;
+
+ if (bp && bp->m_offset) {
+ if (bp->m_offset >= len) {
+ bp->m_offset -= len;
+ bp->m_len += len;
+ memcpy(MBUF_CTOP(bp), ptr, len);
+ return bp;
+ }
+ len -= bp->m_offset;
+ memcpy(bp + 1, (const char *)ptr + len, bp->m_offset);
+ bp->m_len += bp->m_offset;
+ bp->m_offset = 0;
+ }
+
+ head = m_get(len + extra, bp ? bp->m_type : MB_UNKNOWN);
+ head->m_offset = extra;
+ head->m_len -= extra;
+ if (ptr)
+ memcpy(MBUF_CTOP(head), ptr, len);
+ head->m_next = bp;
+
+ return head;
+}
+
+struct mbuf *
+m_adj(struct mbuf *bp, ssize_t n)
+{
+ if (n > 0) {
+ while (bp) {
+ if (n < bp->m_len) {
+ bp->m_len = n;
+ bp->m_offset += n;
+ return bp;
+ }
+ n -= bp->m_len;
+ bp = m_free(bp);
+ }
+ } else {
+ if ((n = m_length(bp) + n) <= 0) {
+ m_freem(bp);
+ return NULL;
+ }
+ for (; bp; bp = bp->m_next, n -= bp->m_len)
+ if (n < bp->m_len) {
+ bp->m_len = n;
+ m_freem(bp->m_next);
+ bp->m_next = NULL;
+ break;
+ }
+ }
+
+ return bp;
+}
+
+void
+mbuf_Write(struct mbuf *bp, const void *ptr, size_t m_len)
+{
+ int plen;
+ int nb;
+
+ plen = m_length(bp);
+ if (plen < m_len)
+ m_len = plen;
+
+ while (m_len > 0) {
+ nb = (m_len < bp->m_len) ? m_len : bp->m_len;
+ memcpy(MBUF_CTOP(bp), ptr, nb);
+ m_len -= bp->m_len;
+ bp = bp->m_next;
+ }
+}
+
+int
+mbuf_Show(struct cmdargs const *arg)
+{
+ int i;
+
+ prompt_Printf(arg->prompt, "Fragments (octets) in use:\n");
+ for (i = 0; i < MB_MAX; i += 2)
+ prompt_Printf(arg->prompt, "%10.10s: %04lu (%06lu)\t"
+ "%10.10s: %04lu (%06lu)\n",
+ mbuftype(i), (u_long)MemMap[i].fragments,
+ (u_long)MemMap[i].octets, mbuftype(i+1),
+ (u_long)MemMap[i+1].fragments, (u_long)MemMap[i+1].octets);
+
+ if (i == MB_MAX)
+ prompt_Printf(arg->prompt, "%10.10s: %04lu (%06lu)\n",
+ mbuftype(i), (u_long)MemMap[i].fragments,
+ (u_long)MemMap[i].octets);
+
+ prompt_Printf(arg->prompt, "Mallocs: %llu, Frees: %llu\n",
+ mbuf_Mallocs, mbuf_Frees);
+
+ return 0;
+}
+
+struct mbuf *
+m_dequeue(struct mqueue *q)
+{
+ struct mbuf *bp;
+
+ log_Printf(LogDEBUG, "m_dequeue: queue len = %lu\n", (u_long)q->len);
+ bp = q->top;
+ if (bp) {
+ q->top = q->top->m_nextpkt;
+ q->len--;
+ if (q->top == NULL) {
+ q->last = q->top;
+ if (q->len)
+ log_Printf(LogERROR, "m_dequeue: Not zero (%lu)!!!\n",
+ (u_long)q->len);
+ }
+ bp->m_nextpkt = NULL;
+ }
+
+ return bp;
+}
+
+void
+m_enqueue(struct mqueue *queue, struct mbuf *bp)
+{
+ if (bp != NULL) {
+ if (queue->last) {
+ queue->last->m_nextpkt = bp;
+ queue->last = bp;
+ } else
+ queue->last = queue->top = bp;
+ queue->len++;
+ log_Printf(LogDEBUG, "m_enqueue: len = %lu\n", (unsigned long)queue->len);
+ }
+}
+
+struct mbuf *
+m_pullup(struct mbuf *bp)
+{
+ /* Put it all in one contigous (aligned) mbuf */
+
+ if (bp != NULL) {
+ if (bp->m_next != NULL) {
+ struct mbuf *nbp;
+ u_char *cp;
+
+ nbp = m_get(m_length(bp), bp->m_type);
+
+ for (cp = MBUF_CTOP(nbp); bp; bp = m_free(bp)) {
+ memcpy(cp, MBUF_CTOP(bp), bp->m_len);
+ cp += bp->m_len;
+ }
+ bp = nbp;
+ }
+#ifndef __i386__ /* Do any other archs not care about alignment ? */
+ else if ((bp->m_offset & (sizeof(long) - 1)) != 0) {
+ bcopy(MBUF_CTOP(bp), bp + 1, bp->m_len);
+ bp->m_offset = 0;
+ }
+#endif
+ }
+
+ return bp;
+}
+
+void
+m_settype(struct mbuf *bp, int type)
+{
+ for (; bp; bp = bp->m_next)
+ if (type != bp->m_type) {
+ MemMap[bp->m_type].fragments--;
+ MemMap[bp->m_type].octets -= bp->m_size;
+ bp->m_type = type;
+ MemMap[type].fragments++;
+ MemMap[type].octets += bp->m_size;
+ }
+}
+
+struct mbuf *
+m_append(struct mbuf *bp, const void *v, size_t sz)
+{
+ struct mbuf *m = bp;
+
+ if (m) {
+ while (m->m_next)
+ m = m->m_next;
+ if (m->m_size - m->m_len > sz)
+ m->m_len += sz;
+ else
+ m->m_next = m_prepend(NULL, v, sz, 0);
+ } else
+ bp = m_prepend(NULL, v, sz, 0);
+
+ return bp;
+}
diff --git a/usr.sbin/ppp/mbuf.h b/usr.sbin/ppp/mbuf.h
new file mode 100644
index 0000000..82a7428
--- /dev/null
+++ b/usr.sbin/ppp/mbuf.h
@@ -0,0 +1,118 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct mbuf {
+ size_t m_size; /* size allocated (excluding header) */
+ short m_offset; /* offset from header end to start position */
+ size_t m_len; /* available byte count in buffer */
+ short m_type; /* MB_* below */
+ struct mbuf *m_next; /* link to next mbuf */
+ struct mbuf *m_nextpkt; /* link to next packet */
+ /* buffer space is malloc()d directly after the header */
+};
+
+struct mqueue {
+ struct mbuf *top;
+ struct mbuf *last;
+ size_t len;
+};
+
+#define MBUF_CTOP(bp) \
+ ((bp) ? (u_char *)((bp)+1) + (bp)->m_offset : NULL)
+
+#define CONST_MBUF_CTOP(bp) \
+ ((bp) ? (const u_char *)((bp)+1) + (bp)->m_offset : NULL)
+
+#define MB_IPIN 0
+#define MB_IPOUT 1
+#define MB_IPV6IN 2
+#define MB_IPV6OUT 3
+#define MB_NATIN 4
+#define MB_NATOUT 5
+#define MB_MPIN 6
+#define MB_MPOUT 7
+#define MB_VJIN 8
+#define MB_VJOUT 9
+#define MB_ICOMPDIN 10
+#define MB_ICOMPDOUT 11
+#define MB_COMPDIN 12
+#define MB_COMPDOUT 13
+#define MB_LQRIN 14
+#define MB_LQROUT 15
+#define MB_ECHOIN 16
+#define MB_ECHOOUT 17
+#define MB_PROTOIN 18
+#define MB_PROTOOUT 19
+#define MB_ACFIN 20
+#define MB_ACFOUT 21
+#define MB_SYNCIN 22
+#define MB_SYNCOUT 23
+#define MB_HDLCIN 24
+#define MB_HDLCOUT 25
+#define MB_ASYNCIN 26
+#define MB_ASYNCOUT 27
+#define MB_CBCPIN 28
+#define MB_CBCPOUT 29
+#define MB_CHAPIN 30
+#define MB_CHAPOUT 31
+#define MB_PAPIN 32
+#define MB_PAPOUT 33
+#define MB_CCPIN 34
+#define MB_CCPOUT 35
+#define MB_IPCPIN 36
+#define MB_IPCPOUT 37
+#define MB_IPV6CPIN 38
+#define MB_IPV6CPOUT 39
+#define MB_LCPIN 40
+#define MB_LCPOUT 41
+#define MB_UNKNOWN 42
+#define MB_MAX MB_UNKNOWN
+
+#define M_MAXLEN (4352 - sizeof(struct mbuf)) /* > HDLCSIZE */
+
+struct cmdargs;
+
+extern int m_length(struct mbuf *);
+extern struct mbuf *m_get(size_t, int);
+extern struct mbuf *m_free(struct mbuf *);
+extern void m_freem(struct mbuf *);
+extern void mbuf_Write(struct mbuf *, const void *, size_t);
+extern struct mbuf *mbuf_Read(struct mbuf *, void *, size_t);
+extern size_t mbuf_View(struct mbuf *, void *, size_t);
+extern struct mbuf *m_prepend(struct mbuf *, const void *, size_t, size_t);
+extern struct mbuf *m_adj(struct mbuf *, ssize_t);
+extern struct mbuf *m_pullup(struct mbuf *);
+extern void m_settype(struct mbuf *, int);
+extern struct mbuf *m_append(struct mbuf *, const void *, size_t);
+
+extern int mbuf_Show(struct cmdargs const *);
+
+extern void m_enqueue(struct mqueue *, struct mbuf *);
+extern struct mbuf *m_dequeue(struct mqueue *);
diff --git a/usr.sbin/ppp/mp.c b/usr.sbin/ppp/mp.c
new file mode 100644
index 0000000..7e71dad
--- /dev/null
+++ b/usr.sbin/ppp/mp.c
@@ -0,0 +1,1212 @@
+/*-
+ * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+#include <net/if_dl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <errno.h>
+#include <paths.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "layer.h"
+#ifndef NONAT
+#include "nat_cmd.h"
+#endif
+#include "vjcomp.h"
+#include "ua.h"
+#include "defs.h"
+#include "command.h"
+#include "mbuf.h"
+#include "log.h"
+#include "timer.h"
+#include "fsm.h"
+#include "iplist.h"
+#include "throughput.h"
+#include "slcompress.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "ncpaddr.h"
+#include "ipcp.h"
+#include "auth.h"
+#include "lcp.h"
+#include "async.h"
+#include "ccp.h"
+#include "link.h"
+#include "descriptor.h"
+#include "physical.h"
+#include "chat.h"
+#include "proto.h"
+#include "filter.h"
+#include "mp.h"
+#include "chap.h"
+#include "cbcp.h"
+#include "datalink.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+#include "prompt.h"
+#include "id.h"
+#include "arp.h"
+
+void
+peerid_Init(struct peerid *peer)
+{
+ peer->enddisc.class = 0;
+ *peer->enddisc.address = '\0';
+ peer->enddisc.len = 0;
+ *peer->authname = '\0';
+}
+
+int
+peerid_Equal(const struct peerid *p1, const struct peerid *p2)
+{
+ return !strcmp(p1->authname, p2->authname) &&
+ p1->enddisc.class == p2->enddisc.class &&
+ p1->enddisc.len == p2->enddisc.len &&
+ !memcmp(p1->enddisc.address, p2->enddisc.address, p1->enddisc.len);
+}
+
+static u_int32_t
+inc_seq(unsigned is12bit, u_int32_t seq)
+{
+ seq++;
+ if (is12bit) {
+ if (seq & 0xfffff000)
+ seq = 0;
+ } else if (seq & 0xff000000)
+ seq = 0;
+ return seq;
+}
+
+static int
+isbefore(unsigned is12bit, u_int32_t seq1, u_int32_t seq2)
+{
+ u_int32_t max = (is12bit ? 0xfff : 0xffffff) - 0x200;
+
+ if (seq1 > max) {
+ if (seq2 < 0x200 || seq2 > seq1)
+ return 1;
+ } else if ((seq1 > 0x200 || seq2 <= max) && seq1 < seq2)
+ return 1;
+
+ return 0;
+}
+
+static int
+mp_ReadHeader(struct mp *mp, struct mbuf *m, struct mp_header *header)
+{
+ if (mp->local_is12bit) {
+ u_int16_t val;
+
+ ua_ntohs(MBUF_CTOP(m), &val);
+ if (val & 0x3000) {
+ log_Printf(LogWARN, "Oops - MP header without required zero bits\n");
+ return 0;
+ }
+ header->begin = val & 0x8000 ? 1 : 0;
+ header->end = val & 0x4000 ? 1 : 0;
+ header->seq = val & 0x0fff;
+ return 2;
+ } else {
+ ua_ntohl(MBUF_CTOP(m), &header->seq);
+ if (header->seq & 0x3f000000) {
+ log_Printf(LogWARN, "Oops - MP header without required zero bits\n");
+ return 0;
+ }
+ header->begin = header->seq & 0x80000000 ? 1 : 0;
+ header->end = header->seq & 0x40000000 ? 1 : 0;
+ header->seq &= 0x00ffffff;
+ return 4;
+ }
+}
+
+static void
+mp_LayerStart(void *v, struct fsm *fp)
+{
+ /* The given FSM (ccp) is about to start up ! */
+}
+
+static void
+mp_LayerUp(void *v, struct fsm *fp)
+{
+ /* The given fsm (ccp) is now up */
+
+ bundle_CalculateBandwidth(fp->bundle); /* Against ccp_MTUOverhead */
+}
+
+static void
+mp_LayerDown(void *v, struct fsm *fp)
+{
+ /* The given FSM (ccp) has been told to come down */
+}
+
+static void
+mp_LayerFinish(void *v, struct fsm *fp)
+{
+ /* The given fsm (ccp) is now down */
+ if (fp->state == ST_CLOSED && fp->open_mode == OPEN_PASSIVE)
+ fsm_Open(fp); /* CCP goes to ST_STOPPED */
+}
+
+static void
+mp_UpDown(void *v)
+{
+ struct mp *mp = (struct mp *)v;
+ int percent;
+
+ percent = MAX(mp->link.stats.total.in.OctetsPerSecond,
+ mp->link.stats.total.out.OctetsPerSecond) * 800 /
+ mp->bundle->bandwidth;
+ if (percent >= mp->cfg.autoload.max) {
+ log_Printf(LogDEBUG, "%d%% saturation - bring a link up ?\n", percent);
+ bundle_AutoAdjust(mp->bundle, percent, AUTO_UP);
+ } else if (percent <= mp->cfg.autoload.min) {
+ log_Printf(LogDEBUG, "%d%% saturation - bring a link down ?\n", percent);
+ bundle_AutoAdjust(mp->bundle, percent, AUTO_DOWN);
+ }
+}
+
+void
+mp_StopAutoloadTimer(struct mp *mp)
+{
+ throughput_stop(&mp->link.stats.total);
+}
+
+void
+mp_CheckAutoloadTimer(struct mp *mp)
+{
+ if (mp->link.stats.total.SamplePeriod != mp->cfg.autoload.period) {
+ throughput_destroy(&mp->link.stats.total);
+ throughput_init(&mp->link.stats.total, mp->cfg.autoload.period);
+ throughput_callback(&mp->link.stats.total, mp_UpDown, mp);
+ }
+
+ if (bundle_WantAutoloadTimer(mp->bundle))
+ throughput_start(&mp->link.stats.total, "MP throughput", 1);
+ else
+ mp_StopAutoloadTimer(mp);
+}
+
+void
+mp_RestartAutoloadTimer(struct mp *mp)
+{
+ if (mp->link.stats.total.SamplePeriod != mp->cfg.autoload.period)
+ mp_CheckAutoloadTimer(mp);
+ else
+ throughput_clear(&mp->link.stats.total, THROUGHPUT_OVERALL, NULL);
+}
+
+void
+mp_Init(struct mp *mp, struct bundle *bundle)
+{
+ mp->peer_is12bit = mp->local_is12bit = 0;
+ mp->peer_mrru = mp->local_mrru = 0;
+
+ peerid_Init(&mp->peer);
+
+ mp->out.seq = 0;
+ mp->out.link = 0;
+ mp->out.af = AF_INET;
+ mp->seq.min_in = 0;
+ mp->seq.next_in = 0;
+ mp->inbufs = NULL;
+ mp->bundle = bundle;
+
+ mp->link.type = LOGICAL_LINK;
+ mp->link.name = "mp";
+ mp->link.len = sizeof *mp;
+
+ mp->cfg.autoload.period = SAMPLE_PERIOD;
+ mp->cfg.autoload.min = mp->cfg.autoload.max = 0;
+ throughput_init(&mp->link.stats.total, mp->cfg.autoload.period);
+ throughput_callback(&mp->link.stats.total, mp_UpDown, mp);
+ mp->link.stats.parent = NULL;
+ mp->link.stats.gather = 0; /* Let the physical links gather stats */
+ memset(mp->link.Queue, '\0', sizeof mp->link.Queue);
+ memset(mp->link.proto_in, '\0', sizeof mp->link.proto_in);
+ memset(mp->link.proto_out, '\0', sizeof mp->link.proto_out);
+
+ mp->fsmp.LayerStart = mp_LayerStart;
+ mp->fsmp.LayerUp = mp_LayerUp;
+ mp->fsmp.LayerDown = mp_LayerDown;
+ mp->fsmp.LayerFinish = mp_LayerFinish;
+ mp->fsmp.object = mp;
+
+ mpserver_Init(&mp->server);
+
+ mp->cfg.mrru = 0;
+ mp->cfg.shortseq = NEG_ENABLED|NEG_ACCEPTED;
+ mp->cfg.negenddisc = NEG_ENABLED|NEG_ACCEPTED;
+ mp->cfg.enddisc.class = 0;
+ *mp->cfg.enddisc.address = '\0';
+ mp->cfg.enddisc.len = 0;
+
+ lcp_Init(&mp->link.lcp, mp->bundle, &mp->link, NULL);
+ ccp_Init(&mp->link.ccp, mp->bundle, &mp->link, &mp->fsmp);
+
+ link_EmptyStack(&mp->link);
+ link_Stack(&mp->link, &protolayer);
+ link_Stack(&mp->link, &ccplayer);
+ link_Stack(&mp->link, &vjlayer);
+#ifndef NONAT
+ link_Stack(&mp->link, &natlayer);
+#endif
+}
+
+int
+mp_Up(struct mp *mp, struct datalink *dl)
+{
+ struct lcp *lcp = &dl->physical->link.lcp;
+
+ if (mp->active) {
+ /* We're adding a link - do a last validation on our parameters */
+ if (!peerid_Equal(&dl->peer, &mp->peer)) {
+ log_Printf(LogPHASE, "%s: Inappropriate peer !\n", dl->name);
+ log_Printf(LogPHASE, " Attached to peer %s/%s\n", mp->peer.authname,
+ mp_Enddisc(mp->peer.enddisc.class, mp->peer.enddisc.address,
+ mp->peer.enddisc.len));
+ log_Printf(LogPHASE, " New link is peer %s/%s\n", dl->peer.authname,
+ mp_Enddisc(dl->peer.enddisc.class, dl->peer.enddisc.address,
+ dl->peer.enddisc.len));
+ return MP_FAILED;
+ }
+ if (mp->local_mrru != lcp->want_mrru ||
+ mp->peer_mrru != lcp->his_mrru ||
+ mp->local_is12bit != lcp->want_shortseq ||
+ mp->peer_is12bit != lcp->his_shortseq) {
+ log_Printf(LogPHASE, "%s: Invalid MRRU/SHORTSEQ MP parameters !\n",
+ dl->name);
+ return MP_FAILED;
+ }
+ return MP_ADDED;
+ } else {
+ /* First link in multilink mode */
+
+ mp->local_mrru = lcp->want_mrru;
+ mp->peer_mrru = lcp->his_mrru;
+ mp->local_is12bit = lcp->want_shortseq;
+ mp->peer_is12bit = lcp->his_shortseq;
+ mp->peer = dl->peer;
+
+ throughput_destroy(&mp->link.stats.total);
+ throughput_init(&mp->link.stats.total, mp->cfg.autoload.period);
+ throughput_callback(&mp->link.stats.total, mp_UpDown, mp);
+ memset(mp->link.Queue, '\0', sizeof mp->link.Queue);
+ memset(mp->link.proto_in, '\0', sizeof mp->link.proto_in);
+ memset(mp->link.proto_out, '\0', sizeof mp->link.proto_out);
+
+ /* Tell the link who it belongs to */
+ dl->physical->link.stats.parent = &mp->link.stats.total;
+
+ mp->out.seq = 0;
+ mp->out.link = 0;
+ mp->out.af = AF_INET;
+ mp->seq.min_in = 0;
+ mp->seq.next_in = 0;
+
+ /*
+ * Now we create our server socket.
+ * If it already exists, join it. Otherwise, create and own it
+ */
+ switch (mpserver_Open(&mp->server, &mp->peer)) {
+ case MPSERVER_CONNECTED:
+ log_Printf(LogPHASE, "mp: Transfer link on %s\n",
+ mp->server.socket.sun_path);
+ mp->server.send.dl = dl; /* Defer 'till it's safe to send */
+ return MP_LINKSENT;
+ case MPSERVER_FAILED:
+ return MP_FAILED;
+ case MPSERVER_LISTENING:
+ log_Printf(LogPHASE, "mp: Listening on %s\n", mp->server.socket.sun_path);
+ log_Printf(LogPHASE, " First link: %s\n", dl->name);
+
+ /* Re-point our NCP layers at our MP link */
+ ncp_SetLink(&mp->bundle->ncp, &mp->link);
+
+ /* Our lcp's already up 'cos of the NULL parent */
+ if (ccp_SetOpenMode(&mp->link.ccp)) {
+ fsm_Up(&mp->link.ccp.fsm);
+ fsm_Open(&mp->link.ccp.fsm);
+ }
+
+ mp->active = 1;
+ break;
+ }
+ }
+
+ return MP_UP;
+}
+
+void
+mp_Down(struct mp *mp)
+{
+ if (mp->active) {
+ struct mbuf *next;
+
+ /* Stop that ! */
+ mp_StopAutoloadTimer(mp);
+
+ /* Don't want any more of these */
+ mpserver_Close(&mp->server);
+
+ /* CCP goes down with a bang */
+ fsm2initial(&mp->link.ccp.fsm);
+
+ /* Received fragments go in the bit-bucket */
+ while (mp->inbufs) {
+ next = mp->inbufs->m_nextpkt;
+ m_freem(mp->inbufs);
+ mp->inbufs = next;
+ }
+
+ peerid_Init(&mp->peer);
+ mp->active = 0;
+ }
+}
+
+void
+mp_linkInit(struct mp_link *mplink)
+{
+ mplink->seq = 0;
+ mplink->bandwidth = 0;
+}
+
+static void
+mp_Assemble(struct mp *mp, struct mbuf *m, struct physical *p)
+{
+ struct mp_header mh, h;
+ struct mbuf *q, *last;
+ int32_t seq;
+
+ /*
+ * When `m' and `p' are NULL, it means our oldest link has gone down.
+ * We want to determine a new min, and process any intermediate stuff
+ * as normal
+ */
+
+ if (m && mp_ReadHeader(mp, m, &mh) == 0) {
+ m_freem(m);
+ return;
+ }
+
+ if (p) {
+ seq = p->dl->mp.seq;
+ p->dl->mp.seq = mh.seq;
+ } else
+ seq = mp->seq.min_in;
+
+ if (mp->seq.min_in == seq) {
+ /*
+ * We've received new data on the link that has our min (oldest) seq.
+ * Figure out which link now has the smallest (oldest) seq.
+ */
+ struct datalink *dl;
+
+ mp->seq.min_in = (u_int32_t)-1;
+ for (dl = mp->bundle->links; dl; dl = dl->next)
+ if (dl->state == DATALINK_OPEN &&
+ (mp->seq.min_in == -1 ||
+ isbefore(mp->local_is12bit, dl->mp.seq, mp->seq.min_in)))
+ mp->seq.min_in = dl->mp.seq;
+ }
+
+ /*
+ * Now process as many of our fragments as we can, adding our new
+ * fragment in as we go, and ordering with the oldest at the top of
+ * the queue.
+ */
+
+ last = NULL;
+ seq = mp->seq.next_in;
+ q = mp->inbufs;
+ while (q || m) {
+ if (!q) {
+ if (last)
+ last->m_nextpkt = m;
+ else
+ mp->inbufs = m;
+ q = m;
+ m = NULL;
+ h = mh;
+ } else {
+ mp_ReadHeader(mp, q, &h);
+
+ if (m && isbefore(mp->local_is12bit, mh.seq, h.seq)) {
+ /* Our received fragment fits in before this one, so link it in */
+ if (last)
+ last->m_nextpkt = m;
+ else
+ mp->inbufs = m;
+ m->m_nextpkt = q;
+ q = m;
+ h = mh;
+ m = NULL;
+ }
+ }
+
+ if (h.seq != seq) {
+ /* we're missing something :-( */
+ if (isbefore(mp->local_is12bit, seq, mp->seq.min_in)) {
+ /* we're never gonna get it */
+ struct mbuf *next;
+
+ /* Zap all older fragments */
+ while (mp->inbufs != q) {
+ log_Printf(LogDEBUG, "Drop frag\n");
+ next = mp->inbufs->m_nextpkt;
+ m_freem(mp->inbufs);
+ mp->inbufs = next;
+ }
+
+ /*
+ * Zap everything until the next `end' fragment OR just before
+ * the next `begin' fragment OR 'till seq.min_in - whichever
+ * comes first.
+ */
+ do {
+ mp_ReadHeader(mp, mp->inbufs, &h);
+ if (h.begin) {
+ /* We might be able to process this ! */
+ h.seq--; /* We're gonna look for fragment with h.seq+1 */
+ break;
+ }
+ next = mp->inbufs->m_nextpkt;
+ log_Printf(LogDEBUG, "Drop frag %u\n", h.seq);
+ m_freem(mp->inbufs);
+ mp->inbufs = next;
+ } while (mp->inbufs && (isbefore(mp->local_is12bit, mp->seq.min_in,
+ h.seq) || h.end));
+
+ /*
+ * Continue processing things from here.
+ * This deals with the possibility that we received a fragment
+ * on the slowest link that invalidates some of our data (because
+ * of the hole at `q'), but where there are subsequent `whole'
+ * packets that have already been received.
+ */
+
+ mp->seq.next_in = seq = inc_seq(mp->local_is12bit, h.seq);
+ last = NULL;
+ q = mp->inbufs;
+ } else
+ /* we may still receive the missing fragment */
+ break;
+ } else if (h.end) {
+ /* We've got something, reassemble */
+ struct mbuf **frag = &q;
+ int len;
+ u_long first = -1;
+
+ do {
+ *frag = mp->inbufs;
+ mp->inbufs = mp->inbufs->m_nextpkt;
+ len = mp_ReadHeader(mp, *frag, &h);
+ if (first == -1)
+ first = h.seq;
+ if (frag == &q && !h.begin) {
+ log_Printf(LogWARN, "Oops - MP frag %lu should have a begin flag\n",
+ (u_long)h.seq);
+ m_freem(q);
+ q = NULL;
+ } else if (frag != &q && h.begin) {
+ log_Printf(LogWARN, "Oops - MP frag %lu should have an end flag\n",
+ (u_long)h.seq - 1);
+ /*
+ * Stuff our fragment back at the front of the queue and zap
+ * our half-assembled packet.
+ */
+ (*frag)->m_nextpkt = mp->inbufs;
+ mp->inbufs = *frag;
+ *frag = NULL;
+ m_freem(q);
+ q = NULL;
+ frag = &q;
+ h.end = 0; /* just in case it's a whole packet */
+ } else {
+ (*frag)->m_offset += len;
+ (*frag)->m_len -= len;
+ (*frag)->m_nextpkt = NULL;
+ do
+ frag = &(*frag)->m_next;
+ while (*frag != NULL);
+ }
+ } while (!h.end);
+
+ if (q) {
+ q = m_pullup(q);
+ log_Printf(LogDEBUG, "MP: Reassembled frags %ld-%lu, length %d\n",
+ first, (u_long)h.seq, m_length(q));
+ link_PullPacket(&mp->link, MBUF_CTOP(q), q->m_len, mp->bundle);
+ m_freem(q);
+ }
+
+ mp->seq.next_in = seq = inc_seq(mp->local_is12bit, h.seq);
+ last = NULL;
+ q = mp->inbufs;
+ } else {
+ /* Look for the next fragment */
+ seq = inc_seq(mp->local_is12bit, seq);
+ last = q;
+ q = q->m_nextpkt;
+ }
+ }
+
+ if (m) {
+ /* We still have to find a home for our new fragment */
+ last = NULL;
+ for (q = mp->inbufs; q; last = q, q = q->m_nextpkt) {
+ mp_ReadHeader(mp, q, &h);
+ if (isbefore(mp->local_is12bit, mh.seq, h.seq))
+ break;
+ }
+ /* Our received fragment fits in here */
+ if (last)
+ last->m_nextpkt = m;
+ else
+ mp->inbufs = m;
+ m->m_nextpkt = q;
+ }
+}
+
+struct mbuf *
+mp_Input(struct bundle *bundle, struct link *l, struct mbuf *bp)
+{
+ struct physical *p = link2physical(l);
+
+ if (!bundle->ncp.mp.active)
+ /* Let someone else deal with it ! */
+ return bp;
+
+ if (p == NULL) {
+ log_Printf(LogWARN, "DecodePacket: Can't do MP inside MP !\n");
+ m_freem(bp);
+ } else {
+ m_settype(bp, MB_MPIN);
+ mp_Assemble(&bundle->ncp.mp, bp, p);
+ }
+
+ return NULL;
+}
+
+static void
+mp_Output(struct mp *mp, struct bundle *bundle, struct link *l,
+ struct mbuf *m, u_int32_t begin, u_int32_t end)
+{
+ char prepend[4];
+
+ /* Stuff an MP header on the front of our packet and send it */
+
+ if (mp->peer_is12bit) {
+ u_int16_t val;
+
+ val = (begin << 15) | (end << 14) | (u_int16_t)mp->out.seq;
+ ua_htons(&val, prepend);
+ m = m_prepend(m, prepend, 2, 0);
+ } else {
+ u_int32_t val;
+
+ val = (begin << 31) | (end << 30) | (u_int32_t)mp->out.seq;
+ ua_htonl(&val, prepend);
+ m = m_prepend(m, prepend, 4, 0);
+ }
+ if (log_IsKept(LogDEBUG))
+ log_Printf(LogDEBUG, "MP[frag %d]: Send %d bytes on link `%s'\n",
+ mp->out.seq, m_length(m), l->name);
+ mp->out.seq = inc_seq(mp->peer_is12bit, mp->out.seq);
+
+ if (l->ccp.fsm.state != ST_OPENED && ccp_Required(&l->ccp)) {
+ log_Printf(LogPHASE, "%s: Not transmitting... waiting for CCP\n", l->name);
+ return;
+ }
+
+ link_PushPacket(l, m, bundle, LINK_QUEUES(l) - 1, PROTO_MP);
+}
+
+int
+mp_FillPhysicalQueues(struct bundle *bundle)
+{
+ struct mp *mp = &bundle->ncp.mp;
+ struct datalink *dl, *fdl;
+ size_t total, add, len;
+ int thislink, nlinks, nopenlinks, sendasip;
+ u_int32_t begin, end;
+ struct mbuf *m, *mo;
+ struct link *bestlink;
+
+ thislink = nlinks = nopenlinks = 0;
+ for (fdl = NULL, dl = bundle->links; dl; dl = dl->next) {
+ /* Include non-open links here as mp->out.link will stay more correct */
+ if (!fdl) {
+ if (thislink == mp->out.link)
+ fdl = dl;
+ else
+ thislink++;
+ }
+ nlinks++;
+ if (dl->state == DATALINK_OPEN)
+ nopenlinks++;
+ }
+
+ if (!fdl) {
+ fdl = bundle->links;
+ if (!fdl)
+ return 0;
+ thislink = 0;
+ }
+
+ total = 0;
+ for (dl = fdl; nlinks > 0; dl = dl->next, nlinks--, thislink++) {
+ if (!dl) {
+ dl = bundle->links;
+ thislink = 0;
+ }
+
+ if (dl->state != DATALINK_OPEN)
+ continue;
+
+ if (dl->physical->out)
+ /* this link has suffered a short write. Let it continue */
+ continue;
+
+ add = link_QueueLen(&dl->physical->link);
+ if (add) {
+ /* this link has got stuff already queued. Let it continue */
+ total += add;
+ continue;
+ }
+
+ if (!mp_QueueLen(mp)) {
+ int mrutoosmall;
+
+ /*
+ * If there's only a single open link in our bundle and we haven't got
+ * MP level link compression, queue outbound traffic directly via that
+ * link's protocol stack rather than using the MP link. This results
+ * in the outbound traffic going out as PROTO_IP or PROTO_IPV6 rather
+ * than PROTO_MP.
+ */
+
+ mrutoosmall = 0;
+ sendasip = nopenlinks < 2;
+ if (sendasip) {
+ if (dl->physical->link.lcp.his_mru < mp->peer_mrru) {
+ /*
+ * Actually, forget it. This test is done against the MRRU rather
+ * than the packet size so that we don't end up sending some data
+ * in MP fragments and some data in PROTO_IP packets. That's just
+ * too likely to upset some ppp implementations.
+ */
+ mrutoosmall = 1;
+ sendasip = 0;
+ }
+ }
+
+ bestlink = sendasip ? &dl->physical->link : &mp->link;
+ if (!ncp_PushPacket(&bundle->ncp, &mp->out.af, bestlink))
+ break; /* Nothing else to send */
+
+ if (mrutoosmall)
+ log_Printf(LogDEBUG, "Don't send data as PROTO_IP, MRU < MRRU\n");
+ else if (sendasip)
+ log_Printf(LogDEBUG, "Sending data as PROTO_IP, not PROTO_MP\n");
+
+ if (sendasip) {
+ add = link_QueueLen(&dl->physical->link);
+ if (add) {
+ /* this link has got stuff already queued. Let it continue */
+ total += add;
+ continue;
+ }
+ }
+ }
+
+ m = link_Dequeue(&mp->link);
+ if (m) {
+ len = m_length(m);
+ begin = 1;
+ end = 0;
+
+ while (!end) {
+ if (dl->state == DATALINK_OPEN) {
+ /* Write at most his_mru bytes to the physical link */
+ if (len <= dl->physical->link.lcp.his_mru) {
+ mo = m;
+ end = 1;
+ m_settype(mo, MB_MPOUT);
+ } else {
+ /* It's > his_mru, chop the packet (`m') into bits */
+ mo = m_get(dl->physical->link.lcp.his_mru, MB_MPOUT);
+ len -= mo->m_len;
+ m = mbuf_Read(m, MBUF_CTOP(mo), mo->m_len);
+ }
+ mp_Output(mp, bundle, &dl->physical->link, mo, begin, end);
+ begin = 0;
+ }
+
+ if (!end) {
+ nlinks--;
+ dl = dl->next;
+ if (!dl) {
+ dl = bundle->links;
+ thislink = 0;
+ } else
+ thislink++;
+ }
+ }
+ }
+ }
+ mp->out.link = thislink; /* Start here next time */
+
+ return total;
+}
+
+int
+mp_SetDatalinkBandwidth(struct cmdargs const *arg)
+{
+ int val;
+
+ if (arg->argc != arg->argn+1)
+ return -1;
+
+ val = atoi(arg->argv[arg->argn]);
+ if (val <= 0) {
+ log_Printf(LogWARN, "The link bandwidth must be greater than zero\n");
+ return 1;
+ }
+ arg->cx->mp.bandwidth = val;
+
+ if (arg->cx->state == DATALINK_OPEN)
+ bundle_CalculateBandwidth(arg->bundle);
+
+ return 0;
+}
+
+int
+mp_ShowStatus(struct cmdargs const *arg)
+{
+ struct mp *mp = &arg->bundle->ncp.mp;
+
+ prompt_Printf(arg->prompt, "Multilink is %sactive\n", mp->active ? "" : "in");
+ if (mp->active) {
+ struct mbuf *m, *lm;
+ int bufs = 0;
+
+ lm = NULL;
+ prompt_Printf(arg->prompt, "Socket: %s\n",
+ mp->server.socket.sun_path);
+ for (m = mp->inbufs; m; m = m->m_nextpkt) {
+ bufs++;
+ lm = m;
+ }
+ prompt_Printf(arg->prompt, "Pending frags: %d", bufs);
+ if (bufs) {
+ struct mp_header mh;
+ unsigned long first, last;
+
+ first = mp_ReadHeader(mp, mp->inbufs, &mh) ? mh.seq : 0;
+ last = mp_ReadHeader(mp, lm, &mh) ? mh.seq : 0;
+ prompt_Printf(arg->prompt, " (Have %lu - %lu, want %lu, lowest %lu)\n",
+ first, last, (unsigned long)mp->seq.next_in,
+ (unsigned long)mp->seq.min_in);
+ prompt_Printf(arg->prompt, " First has %sbegin bit and "
+ "%send bit", mh.begin ? "" : "no ", mh.end ? "" : "no ");
+ }
+ prompt_Printf(arg->prompt, "\n");
+ }
+
+ prompt_Printf(arg->prompt, "\nMy Side:\n");
+ if (mp->active) {
+ prompt_Printf(arg->prompt, " Output SEQ: %u\n", mp->out.seq);
+ prompt_Printf(arg->prompt, " MRRU: %u\n", mp->local_mrru);
+ prompt_Printf(arg->prompt, " Short Seq: %s\n",
+ mp->local_is12bit ? "on" : "off");
+ }
+ prompt_Printf(arg->prompt, " Discriminator: %s\n",
+ mp_Enddisc(mp->cfg.enddisc.class, mp->cfg.enddisc.address,
+ mp->cfg.enddisc.len));
+
+ prompt_Printf(arg->prompt, "\nHis Side:\n");
+ if (mp->active) {
+ prompt_Printf(arg->prompt, " Auth Name: %s\n", mp->peer.authname);
+ prompt_Printf(arg->prompt, " Input SEQ: %u\n", mp->seq.next_in);
+ prompt_Printf(arg->prompt, " MRRU: %u\n", mp->peer_mrru);
+ prompt_Printf(arg->prompt, " Short Seq: %s\n",
+ mp->peer_is12bit ? "on" : "off");
+ }
+ prompt_Printf(arg->prompt, " Discriminator: %s\n",
+ mp_Enddisc(mp->peer.enddisc.class, mp->peer.enddisc.address,
+ mp->peer.enddisc.len));
+
+ prompt_Printf(arg->prompt, "\nDefaults:\n");
+
+ prompt_Printf(arg->prompt, " MRRU: ");
+ if (mp->cfg.mrru)
+ prompt_Printf(arg->prompt, "%d (multilink enabled)\n", mp->cfg.mrru);
+ else
+ prompt_Printf(arg->prompt, "disabled\n");
+ prompt_Printf(arg->prompt, " Short Seq: %s\n",
+ command_ShowNegval(mp->cfg.shortseq));
+ prompt_Printf(arg->prompt, " Discriminator: %s\n",
+ command_ShowNegval(mp->cfg.negenddisc));
+ prompt_Printf(arg->prompt, " AutoLoad: min %d%%, max %d%%,"
+ " period %d secs\n", mp->cfg.autoload.min,
+ mp->cfg.autoload.max, mp->cfg.autoload.period);
+
+ return 0;
+}
+
+const char *
+mp_Enddisc(u_char c, const char *address, int len)
+{
+ static char result[100]; /* Used immediately after it's returned */
+ int f, header;
+
+ switch (c) {
+ case ENDDISC_NULL:
+ sprintf(result, "Null Class");
+ break;
+
+ case ENDDISC_LOCAL:
+ snprintf(result, sizeof result, "Local Addr: %.*s", len, address);
+ break;
+
+ case ENDDISC_IP:
+ if (len == 4)
+ snprintf(result, sizeof result, "IP %s",
+ inet_ntoa(*(const struct in_addr *)address));
+ else
+ sprintf(result, "IP[%d] ???", len);
+ break;
+
+ case ENDDISC_MAC:
+ if (len == 6) {
+ const u_char *m = (const u_char *)address;
+ snprintf(result, sizeof result, "MAC %02x:%02x:%02x:%02x:%02x:%02x",
+ m[0], m[1], m[2], m[3], m[4], m[5]);
+ } else
+ sprintf(result, "MAC[%d] ???", len);
+ break;
+
+ case ENDDISC_MAGIC:
+ sprintf(result, "Magic: 0x");
+ header = strlen(result);
+ if (len > sizeof result - header - 1)
+ len = sizeof result - header - 1;
+ for (f = 0; f < len; f++)
+ sprintf(result + header + 2 * f, "%02x", address[f]);
+ break;
+
+ case ENDDISC_PSN:
+ snprintf(result, sizeof result, "PSN: %.*s", len, address);
+ break;
+
+ default:
+ sprintf(result, "%d: ", (int)c);
+ header = strlen(result);
+ if (len > sizeof result - header - 1)
+ len = sizeof result - header - 1;
+ for (f = 0; f < len; f++)
+ sprintf(result + header + 2 * f, "%02x", address[f]);
+ break;
+ }
+ return result;
+}
+
+int
+mp_SetEnddisc(struct cmdargs const *arg)
+{
+ struct mp *mp = &arg->bundle->ncp.mp;
+ struct in_addr addr;
+
+ switch (bundle_Phase(arg->bundle)) {
+ case PHASE_DEAD:
+ break;
+ case PHASE_ESTABLISH:
+ /* Make sure none of our links are DATALINK_LCP or greater */
+ if (bundle_HighestState(arg->bundle) >= DATALINK_LCP) {
+ log_Printf(LogWARN, "enddisc: Only changable before"
+ " LCP negotiations\n");
+ return 1;
+ }
+ break;
+ default:
+ log_Printf(LogWARN, "enddisc: Only changable at phase DEAD/ESTABLISH\n");
+ return 1;
+ }
+
+ if (arg->argc == arg->argn) {
+ mp->cfg.enddisc.class = 0;
+ *mp->cfg.enddisc.address = '\0';
+ mp->cfg.enddisc.len = 0;
+ } else if (arg->argc > arg->argn) {
+ if (!strcasecmp(arg->argv[arg->argn], "label")) {
+ mp->cfg.enddisc.class = ENDDISC_LOCAL;
+ strcpy(mp->cfg.enddisc.address, arg->bundle->cfg.label);
+ mp->cfg.enddisc.len = strlen(mp->cfg.enddisc.address);
+ } else if (!strcasecmp(arg->argv[arg->argn], "ip")) {
+ if (arg->bundle->ncp.ipcp.my_ip.s_addr == INADDR_ANY)
+ ncprange_getip4addr(&arg->bundle->ncp.ipcp.cfg.my_range, &addr);
+ else
+ addr = arg->bundle->ncp.ipcp.my_ip;
+ memcpy(mp->cfg.enddisc.address, &addr.s_addr, sizeof addr.s_addr);
+ mp->cfg.enddisc.class = ENDDISC_IP;
+ mp->cfg.enddisc.len = sizeof arg->bundle->ncp.ipcp.my_ip.s_addr;
+ } else if (!strcasecmp(arg->argv[arg->argn], "mac")) {
+ struct sockaddr_dl hwaddr;
+ int s;
+
+ if (arg->bundle->ncp.ipcp.my_ip.s_addr == INADDR_ANY)
+ ncprange_getip4addr(&arg->bundle->ncp.ipcp.cfg.my_range, &addr);
+ else
+ addr = arg->bundle->ncp.ipcp.my_ip;
+
+ s = ID0socket(PF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ log_Printf(LogERROR, "set enddisc: socket(): %s\n", strerror(errno));
+ return 2;
+ }
+ if (arp_EtherAddr(s, addr, &hwaddr, 1)) {
+ mp->cfg.enddisc.class = ENDDISC_MAC;
+ memcpy(mp->cfg.enddisc.address, hwaddr.sdl_data + hwaddr.sdl_nlen,
+ hwaddr.sdl_alen);
+ mp->cfg.enddisc.len = hwaddr.sdl_alen;
+ } else {
+ log_Printf(LogWARN, "set enddisc: Can't locate MAC address for %s\n",
+ inet_ntoa(addr));
+ close(s);
+ return 4;
+ }
+ close(s);
+ } else if (!strcasecmp(arg->argv[arg->argn], "magic")) {
+ int f;
+
+ randinit();
+ for (f = 0; f < 20; f += sizeof(long))
+ *(long *)(mp->cfg.enddisc.address + f) = random();
+ mp->cfg.enddisc.class = ENDDISC_MAGIC;
+ mp->cfg.enddisc.len = 20;
+ } else if (!strcasecmp(arg->argv[arg->argn], "psn")) {
+ if (arg->argc > arg->argn+1) {
+ mp->cfg.enddisc.class = ENDDISC_PSN;
+ strcpy(mp->cfg.enddisc.address, arg->argv[arg->argn+1]);
+ mp->cfg.enddisc.len = strlen(mp->cfg.enddisc.address);
+ } else {
+ log_Printf(LogWARN, "PSN endpoint requires additional data\n");
+ return 5;
+ }
+ } else {
+ log_Printf(LogWARN, "%s: Unrecognised endpoint type\n",
+ arg->argv[arg->argn]);
+ return 6;
+ }
+ }
+
+ return 0;
+}
+
+static int
+mpserver_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e,
+ int *n)
+{
+ struct mpserver *s = descriptor2mpserver(d);
+ int result;
+
+ result = 0;
+ if (s->send.dl != NULL) {
+ /* We've connect()ed */
+ if (!link_QueueLen(&s->send.dl->physical->link) &&
+ !s->send.dl->physical->out) {
+ /* Only send if we've transmitted all our data (i.e. the ConfigAck) */
+ result -= datalink_RemoveFromSet(s->send.dl, r, w, e);
+ bundle_SendDatalink(s->send.dl, s->fd, &s->socket);
+ s->send.dl = NULL;
+ s->fd = -1;
+ } else
+ /* Never read from a datalink that's on death row ! */
+ result -= datalink_RemoveFromSet(s->send.dl, r, NULL, NULL);
+ } else if (r && s->fd >= 0) {
+ if (*n < s->fd + 1)
+ *n = s->fd + 1;
+ FD_SET(s->fd, r);
+ log_Printf(LogTIMER, "mp: fdset(r) %d\n", s->fd);
+ result++;
+ }
+ return result;
+}
+
+static int
+mpserver_IsSet(struct fdescriptor *d, const fd_set *fdset)
+{
+ struct mpserver *s = descriptor2mpserver(d);
+ return s->fd >= 0 && FD_ISSET(s->fd, fdset);
+}
+
+static void
+mpserver_Read(struct fdescriptor *d, struct bundle *bundle, const fd_set *fdset)
+{
+ struct mpserver *s = descriptor2mpserver(d);
+
+ bundle_ReceiveDatalink(bundle, s->fd);
+}
+
+static int
+mpserver_Write(struct fdescriptor *d, struct bundle *bundle,
+ const fd_set *fdset)
+{
+ /* We never want to write here ! */
+ log_Printf(LogALERT, "mpserver_Write: Internal error: Bad call !\n");
+ return 0;
+}
+
+void
+mpserver_Init(struct mpserver *s)
+{
+ s->desc.type = MPSERVER_DESCRIPTOR;
+ s->desc.UpdateSet = mpserver_UpdateSet;
+ s->desc.IsSet = mpserver_IsSet;
+ s->desc.Read = mpserver_Read;
+ s->desc.Write = mpserver_Write;
+ s->send.dl = NULL;
+ s->fd = -1;
+ memset(&s->socket, '\0', sizeof s->socket);
+}
+
+int
+mpserver_Open(struct mpserver *s, struct peerid *peer)
+{
+ int f, l;
+ mode_t mask;
+
+ if (s->fd != -1) {
+ log_Printf(LogALERT, "Internal error ! mpserver already open\n");
+ mpserver_Close(s);
+ }
+
+ l = snprintf(s->socket.sun_path, sizeof s->socket.sun_path, "%sppp-%s-%02x-",
+ _PATH_VARRUN, peer->authname, peer->enddisc.class);
+ if (l < 0) {
+ log_Printf(LogERROR, "mpserver: snprintf(): %s\n", strerror(errno));
+ return MPSERVER_FAILED;
+ }
+
+ for (f = 0; f < peer->enddisc.len && l < sizeof s->socket.sun_path - 2; f++) {
+ snprintf(s->socket.sun_path + l, sizeof s->socket.sun_path - l,
+ "%02x", *(u_char *)(peer->enddisc.address+f));
+ l += 2;
+ }
+
+ s->socket.sun_family = AF_LOCAL;
+ s->socket.sun_len = sizeof s->socket;
+ s->fd = ID0socket(PF_LOCAL, SOCK_DGRAM, 0);
+ if (s->fd < 0) {
+ log_Printf(LogERROR, "mpserver: socket(): %s\n", strerror(errno));
+ return MPSERVER_FAILED;
+ }
+
+ setsockopt(s->fd, SOL_SOCKET, SO_REUSEADDR, (struct sockaddr *)&s->socket,
+ sizeof s->socket);
+ mask = umask(0177);
+
+ /*
+ * Try to bind the socket. If we succeed we play server, if we fail
+ * we connect() and hand the link off.
+ */
+
+ if (ID0bind_un(s->fd, &s->socket) < 0) {
+ if (errno != EADDRINUSE) {
+ log_Printf(LogPHASE, "mpserver: can't create bundle socket %s (%s)\n",
+ s->socket.sun_path, strerror(errno));
+ umask(mask);
+ close(s->fd);
+ s->fd = -1;
+ return MPSERVER_FAILED;
+ }
+
+ /* So we're the sender */
+ umask(mask);
+ if (ID0connect_un(s->fd, &s->socket) < 0) {
+ log_Printf(LogPHASE, "mpserver: can't connect to bundle socket %s (%s)\n",
+ s->socket.sun_path, strerror(errno));
+ if (errno == ECONNREFUSED)
+ log_Printf(LogPHASE, " The previous server died badly !\n");
+ close(s->fd);
+ s->fd = -1;
+ return MPSERVER_FAILED;
+ }
+
+ /* Donate our link to the other guy */
+ return MPSERVER_CONNECTED;
+ }
+
+ return MPSERVER_LISTENING;
+}
+
+void
+mpserver_Close(struct mpserver *s)
+{
+ if (s->send.dl != NULL) {
+ bundle_SendDatalink(s->send.dl, s->fd, &s->socket);
+ s->send.dl = NULL;
+ s->fd = -1;
+ } else if (s->fd >= 0) {
+ close(s->fd);
+ if (ID0unlink(s->socket.sun_path) == -1)
+ log_Printf(LogERROR, "%s: Failed to remove: %s\n", s->socket.sun_path,
+ strerror(errno));
+ memset(&s->socket, '\0', sizeof s->socket);
+ s->fd = -1;
+ }
+}
+
+void
+mp_LinkLost(struct mp *mp, struct datalink *dl)
+{
+ if (mp->seq.min_in == dl->mp.seq)
+ /* We've lost the link that's holding everything up ! */
+ mp_Assemble(mp, NULL, NULL);
+}
+
+size_t
+mp_QueueLen(struct mp *mp)
+{
+ return link_QueueLen(&mp->link);
+}
diff --git a/usr.sbin/ppp/mp.h b/usr.sbin/ppp/mp.h
new file mode 100644
index 0000000..525ee29
--- /dev/null
+++ b/usr.sbin/ppp/mp.h
@@ -0,0 +1,146 @@
+/*-
+ * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct mbuf;
+struct physical;
+struct bundle;
+struct cmdargs;
+struct datalink;
+
+#define ENDDISC_NULL 0
+#define ENDDISC_LOCAL 1
+#define ENDDISC_IP 2
+#define ENDDISC_MAC 3
+#define ENDDISC_MAGIC 4
+#define ENDDISC_PSN 5
+
+#define MP_LINKSENT 0 /* We attached the link to another ppp */
+#define MP_UP 1 /* We've started MP */
+#define MP_ADDED 2 /* We've added the link to our MP */
+#define MP_FAILED 3 /* No go */
+
+#define MPSERVER_CONNECTED 0
+#define MPSERVER_LISTENING 1
+#define MPSERVER_FAILED 2
+
+struct enddisc {
+ u_char class;
+ char address[50];
+ int len;
+};
+
+struct peerid {
+ struct enddisc enddisc; /* Peers endpoint discriminator */
+ char authname[AUTHLEN]; /* Peers name (authenticated) */
+};
+
+struct mpserver {
+ struct fdescriptor desc;
+ int fd; /* listen()ing or connect()ing here */
+ struct sockaddr_un socket; /* On this socket */
+
+ struct {
+ struct datalink *dl; /* Send this datalink */
+ } send; /* (in UpdateSet()) */
+};
+
+struct mp {
+ struct link link;
+
+ unsigned active : 1;
+ unsigned peer_is12bit : 1; /* 12/24bit seq nos */
+ unsigned local_is12bit : 1;
+ u_short peer_mrru;
+ u_short local_mrru;
+
+ struct peerid peer; /* Who are we talking to */
+ struct mpserver server; /* Our ``sharing'' socket */
+
+ struct {
+ u_int32_t seq; /* next outgoing seq */
+ int link; /* Next link to send on */
+ int af; /* Next address family to send */
+ } out;
+
+ struct {
+ u_int32_t min_in; /* minimum received incoming seq */
+ u_int32_t next_in; /* next incoming seq to process */
+ } seq;
+
+ struct {
+ u_short mrru; /* Max Reconstructed Receive Unit */
+ unsigned shortseq : 2; /* I want short Sequence Numbers */
+ unsigned negenddisc : 2; /* I want an endpoint discriminator */
+ struct enddisc enddisc; /* endpoint discriminator */
+ struct {
+ int min; /* Lowest percent of bundle->bandwidth */
+ int max; /* Highest percent of bundle->bandwidth out */
+ int period; /* link->throughput sample period */
+ } autoload;
+ } cfg;
+
+ struct mbuf *inbufs; /* Received fragments */
+ struct fsm_parent fsmp; /* Our callback functions */
+ struct bundle *bundle; /* Parent */
+};
+
+struct mp_link {
+ u_int32_t seq; /* 12 or 24 bit incoming seq */
+ unsigned bandwidth; /* Our link bandwidth (or zero) */
+};
+
+struct mp_header {
+ unsigned begin : 1;
+ unsigned end : 1;
+ u_int32_t seq;
+};
+
+#define descriptor2mpserver(d) \
+ ((d)->type == MPSERVER_DESCRIPTOR ? (struct mpserver *)(d) : NULL)
+#define mpserver_IsOpen(s) ((s)->fd != -1)
+
+extern void peerid_Init(struct peerid *);
+extern int peerid_Equal(const struct peerid *, const struct peerid *);
+extern void mpserver_Init(struct mpserver *);
+extern int mpserver_Open(struct mpserver *, struct peerid *);
+extern void mpserver_Close(struct mpserver *);
+extern void mp_Init(struct mp *, struct bundle *);
+extern void mp_linkInit(struct mp_link *);
+extern int mp_Up(struct mp *, struct datalink *);
+extern void mp_Down(struct mp *);
+extern struct mbuf *mp_Input(struct bundle *, struct link *, struct mbuf *);
+extern int mp_FillPhysicalQueues(struct bundle *);
+extern int mp_SetDatalinkBandwidth(struct cmdargs const *);
+extern int mp_ShowStatus(struct cmdargs const *);
+extern const char *mp_Enddisc(u_char, const char *, int);
+extern int mp_SetEnddisc(struct cmdargs const *);
+extern void mp_LinkLost(struct mp *, struct datalink *);
+extern void mp_RestartAutoloadTimer(struct mp *);
+extern void mp_CheckAutoloadTimer(struct mp *);
+extern void mp_StopAutoloadTimer(struct mp *);
+extern size_t mp_QueueLen(struct mp *);
diff --git a/usr.sbin/ppp/mppe.c b/usr.sbin/ppp/mppe.c
new file mode 100644
index 0000000..606ad0f
--- /dev/null
+++ b/usr.sbin/ppp/mppe.c
@@ -0,0 +1,814 @@
+/*-
+ * Copyright (c) 2000 Semen Ustimenko <semenu@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+
+#include <sys/socket.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <sys/un.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <openssl/rc4.h>
+
+#include "defs.h"
+#include "mbuf.h"
+#include "log.h"
+#include "timer.h"
+#include "fsm.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "throughput.h"
+#include "layer.h"
+#include "link.h"
+#include "chap_ms.h"
+#include "proto.h"
+#include "mppe.h"
+#include "ua.h"
+#include "descriptor.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ncpaddr.h"
+#include "iplist.h"
+#include "slcompress.h"
+#include "ipcp.h"
+#include "ipv6cp.h"
+#include "filter.h"
+#include "mp.h"
+#include "ncp.h"
+#include "bundle.h"
+
+/*
+ * Documentation:
+ *
+ * draft-ietf-pppext-mppe-04.txt
+ * draft-ietf-pppext-mppe-keys-02.txt
+ */
+
+#define MPPE_OPT_STATELESS 0x1000000
+#define MPPE_OPT_COMPRESSED 0x01
+#define MPPE_OPT_40BIT 0x20
+#define MPPE_OPT_56BIT 0x80
+#define MPPE_OPT_128BIT 0x40
+#define MPPE_OPT_BITMASK 0xe0
+#define MPPE_OPT_MASK (MPPE_OPT_STATELESS | MPPE_OPT_BITMASK)
+
+#define MPPE_FLUSHED 0x8000
+#define MPPE_ENCRYPTED 0x1000
+#define MPPE_HEADER_BITMASK 0xf000
+#define MPPE_HEADER_FLAG 0x00ff
+#define MPPE_HEADER_FLAGMASK 0x00ff
+#define MPPE_HEADER_FLAGSHIFT 8
+#define MPPE_HEADER_STATEFUL_KEYCHANGES 16
+
+struct mppe_state {
+ unsigned stateless : 1;
+ unsigned flushnext : 1;
+ unsigned flushrequired : 1;
+ int cohnum;
+ int keylen; /* 8 or 16 bytes */
+ int keybits; /* 40, 56 or 128 bits */
+ char sesskey[MPPE_KEY_LEN];
+ char mastkey[MPPE_KEY_LEN];
+ RC4_KEY rc4key;
+};
+
+int MPPE_MasterKeyValid = 0;
+int MPPE_IsServer = 0;
+char MPPE_MasterKey[MPPE_KEY_LEN];
+
+/*
+ * The peer has missed a packet. Mark the next output frame to be FLUSHED
+ */
+static int
+MPPEResetOutput(void *v)
+{
+ struct mppe_state *mop = (struct mppe_state *)v;
+
+ if (mop->stateless)
+ log_Printf(LogCCP, "MPPE: Unexpected output channel reset\n");
+ else {
+ log_Printf(LogCCP, "MPPE: Output channel reset\n");
+ mop->flushnext = 1;
+ }
+
+ return 0; /* Ask FSM not to ACK */
+}
+
+static void
+MPPEReduceSessionKey(struct mppe_state *mp)
+{
+ switch(mp->keybits) {
+ case 40:
+ mp->sesskey[2] = 0x9e;
+ mp->sesskey[1] = 0x26;
+ case 56:
+ mp->sesskey[0] = 0xd1;
+ case 128:
+ break;
+ }
+}
+
+static void
+MPPEKeyChange(struct mppe_state *mp)
+{
+ char InterimKey[MPPE_KEY_LEN];
+ RC4_KEY RC4Key;
+
+ GetNewKeyFromSHA(mp->mastkey, mp->sesskey, mp->keylen, InterimKey);
+ RC4_set_key(&RC4Key, mp->keylen, InterimKey);
+ RC4(&RC4Key, mp->keylen, InterimKey, mp->sesskey);
+
+ MPPEReduceSessionKey(mp);
+}
+
+static struct mbuf *
+MPPEOutput(void *v, struct ccp *ccp, struct link *l, int pri, u_short *proto,
+ struct mbuf *mp)
+{
+ struct mppe_state *mop = (struct mppe_state *)v;
+ struct mbuf *mo;
+ u_short nproto, prefix;
+ int dictinit, ilen, len;
+ char *rp;
+
+ ilen = m_length(mp);
+ dictinit = 0;
+
+ log_Printf(LogDEBUG, "MPPE: Output: Proto %02x (%d bytes)\n", *proto, ilen);
+ if (*proto < 0x21 && *proto > 0xFA) {
+ log_Printf(LogDEBUG, "MPPE: Output: Not encrypting\n");
+ ccp->compout += ilen;
+ ccp->uncompout += ilen;
+ return mp;
+ }
+
+ log_DumpBp(LogDEBUG, "MPPE: Output: Encrypt packet:", mp);
+
+ /* Get mbuf for prefixes */
+ mo = m_get(4, MB_CCPOUT);
+ mo->m_next = mp;
+
+ rp = MBUF_CTOP(mo);
+ prefix = MPPE_ENCRYPTED | mop->cohnum;
+
+ if (mop->stateless ||
+ (mop->cohnum & MPPE_HEADER_FLAGMASK) == MPPE_HEADER_FLAG) {
+ /* Change our key */
+ log_Printf(LogDEBUG, "MPPEOutput: Key changed [%d]\n", mop->cohnum);
+ MPPEKeyChange(mop);
+ dictinit = 1;
+ }
+
+ if (mop->stateless || mop->flushnext) {
+ prefix |= MPPE_FLUSHED;
+ dictinit = 1;
+ mop->flushnext = 0;
+ }
+
+ if (dictinit) {
+ /* Initialise our dictionary */
+ log_Printf(LogDEBUG, "MPPEOutput: Dictionary initialised [%d]\n",
+ mop->cohnum);
+ RC4_set_key(&mop->rc4key, mop->keylen, mop->sesskey);
+ }
+
+ /* Set MPPE packet prefix */
+ ua_htons(&prefix, rp);
+
+ /* Save encrypted protocol number */
+ nproto = htons(*proto);
+ RC4(&mop->rc4key, 2, (char *)&nproto, rp + 2);
+
+ /* Encrypt main packet */
+ rp = MBUF_CTOP(mp);
+ RC4(&mop->rc4key, ilen, rp, rp);
+
+ mop->cohnum++;
+ mop->cohnum &= ~MPPE_HEADER_BITMASK;
+
+ /* Set the protocol number */
+ *proto = ccp_Proto(ccp);
+ len = m_length(mo);
+ ccp->uncompout += ilen;
+ ccp->compout += len;
+
+ log_Printf(LogDEBUG, "MPPE: Output: Encrypted: Proto %02x (%d bytes)\n",
+ *proto, len);
+
+ return mo;
+}
+
+static void
+MPPEResetInput(void *v)
+{
+ log_Printf(LogCCP, "MPPE: Unexpected input channel ack\n");
+}
+
+static struct mbuf *
+MPPEInput(void *v, struct ccp *ccp, u_short *proto, struct mbuf *mp)
+{
+ struct mppe_state *mip = (struct mppe_state *)v;
+ u_short prefix;
+ char *rp;
+ int dictinit, flushed, ilen, len, n;
+
+ ilen = m_length(mp);
+ dictinit = 0;
+ ccp->compin += ilen;
+
+ log_Printf(LogDEBUG, "MPPE: Input: Proto %02x (%d bytes)\n", *proto, ilen);
+ log_DumpBp(LogDEBUG, "MPPE: Input: Packet:", mp);
+
+ mp = mbuf_Read(mp, &prefix, 2);
+ prefix = ntohs(prefix);
+ flushed = prefix & MPPE_FLUSHED;
+ prefix &= ~flushed;
+ if ((prefix & MPPE_HEADER_BITMASK) != MPPE_ENCRYPTED) {
+ log_Printf(LogERROR, "MPPE: Input: Invalid packet (flags = 0x%x)\n",
+ (prefix & MPPE_HEADER_BITMASK) | flushed);
+ m_freem(mp);
+ return NULL;
+ }
+
+ prefix &= ~MPPE_HEADER_BITMASK;
+
+ if (!flushed && mip->stateless) {
+ log_Printf(LogCCP, "MPPEInput: Packet without MPPE_FLUSHED set"
+ " in stateless mode\n");
+ flushed = MPPE_FLUSHED;
+ /* Should we really continue ? */
+ }
+
+ if (mip->stateless) {
+ /* Change our key for each missed packet in stateless mode */
+ while (prefix != mip->cohnum) {
+ log_Printf(LogDEBUG, "MPPEInput: Key changed [%u]\n", prefix);
+ MPPEKeyChange(mip);
+ /*
+ * mip->cohnum contains what we received last time in stateless
+ * mode.
+ */
+ mip->cohnum++;
+ mip->cohnum &= ~MPPE_HEADER_BITMASK;
+ }
+ dictinit = 1;
+ } else {
+ if (flushed) {
+ /*
+ * We can always process a flushed packet.
+ * Catch up on any outstanding key changes.
+ */
+ n = (prefix >> MPPE_HEADER_FLAGSHIFT) -
+ (mip->cohnum >> MPPE_HEADER_FLAGSHIFT);
+ if (n < 0)
+ n += MPPE_HEADER_STATEFUL_KEYCHANGES;
+ while (n--) {
+ log_Printf(LogDEBUG, "MPPEInput: Key changed during catchup [%u]\n",
+ prefix);
+ MPPEKeyChange(mip);
+ }
+ mip->flushrequired = 0;
+ mip->cohnum = prefix;
+ dictinit = 1;
+ }
+
+ if (mip->flushrequired) {
+ /*
+ * Perhaps we should be lenient if
+ * (prefix & MPPE_HEADER_FLAGMASK) == MPPE_HEADER_FLAG
+ * The spec says that we shouldn't be though....
+ */
+ log_Printf(LogDEBUG, "MPPE: Not flushed - discarded\n");
+ fsm_Output(&ccp->fsm, CODE_RESETREQ, ccp->fsm.reqid++, NULL, 0,
+ MB_CCPOUT);
+ m_freem(mp);
+ return NULL;
+ }
+
+ if (prefix != mip->cohnum) {
+ /*
+ * We're in stateful mode and didn't receive the expected
+ * packet. Send a reset request, but don't tell the CCP layer
+ * about it as we don't expect to receive a Reset ACK !
+ * Guess what... M$ invented this !
+ */
+ log_Printf(LogCCP, "MPPE: Input: Got seq %u, not %u\n",
+ prefix, mip->cohnum);
+ fsm_Output(&ccp->fsm, CODE_RESETREQ, ccp->fsm.reqid++, NULL, 0,
+ MB_CCPOUT);
+ mip->flushrequired = 1;
+ m_freem(mp);
+ return NULL;
+ }
+
+ if ((prefix & MPPE_HEADER_FLAGMASK) == MPPE_HEADER_FLAG) {
+ log_Printf(LogDEBUG, "MPPEInput: Key changed [%u]\n", prefix);
+ MPPEKeyChange(mip);
+ dictinit = 1;
+ } else if (flushed)
+ dictinit = 1;
+
+ /*
+ * mip->cohnum contains what we expect to receive next time in stateful
+ * mode.
+ */
+ mip->cohnum++;
+ mip->cohnum &= ~MPPE_HEADER_BITMASK;
+ }
+
+ if (dictinit) {
+ log_Printf(LogDEBUG, "MPPEInput: Dictionary initialised [%u]\n", prefix);
+ RC4_set_key(&mip->rc4key, mip->keylen, mip->sesskey);
+ }
+
+ mp = mbuf_Read(mp, proto, 2);
+ RC4(&mip->rc4key, 2, (char *)proto, (char *)proto);
+ *proto = ntohs(*proto);
+
+ rp = MBUF_CTOP(mp);
+ len = m_length(mp);
+ RC4(&mip->rc4key, len, rp, rp);
+
+ log_Printf(LogDEBUG, "MPPEInput: Decrypted: Proto %02x (%d bytes)\n",
+ *proto, len);
+ log_DumpBp(LogDEBUG, "MPPEInput: Decrypted: Packet:", mp);
+
+ ccp->uncompin += len;
+
+ return mp;
+}
+
+static void
+MPPEDictSetup(void *v, struct ccp *ccp, u_short proto, struct mbuf *mi)
+{
+}
+
+static const char *
+MPPEDispOpts(struct fsm_opt *o)
+{
+ static char buf[70];
+ u_int32_t val;
+ char ch;
+ int len, n;
+
+ ua_ntohl(o->data, &val);
+ len = 0;
+ if ((n = snprintf(buf, sizeof buf, "value 0x%08x ", (unsigned)val)) > 0)
+ len += n;
+ if (!(val & MPPE_OPT_BITMASK)) {
+ if ((n = snprintf(buf + len, sizeof buf - len, "(0")) > 0)
+ len += n;
+ } else {
+ ch = '(';
+ if (val & MPPE_OPT_128BIT) {
+ if ((n = snprintf(buf + len, sizeof buf - len, "%c128", ch)) > 0)
+ len += n;
+ ch = '/';
+ }
+ if (val & MPPE_OPT_56BIT) {
+ if ((n = snprintf(buf + len, sizeof buf - len, "%c56", ch)) > 0)
+ len += n;
+ ch = '/';
+ }
+ if (val & MPPE_OPT_40BIT) {
+ if ((n = snprintf(buf + len, sizeof buf - len, "%c40", ch)) > 0)
+ len += n;
+ ch = '/';
+ }
+ }
+
+ if ((n = snprintf(buf + len, sizeof buf - len, " bits, state%s",
+ (val & MPPE_OPT_STATELESS) ? "less" : "ful")) > 0)
+ len += n;
+
+ if (val & MPPE_OPT_COMPRESSED) {
+ if ((n = snprintf(buf + len, sizeof buf - len, ", compressed")) > 0)
+ len += n;
+ }
+
+ snprintf(buf + len, sizeof buf - len, ")");
+
+ return buf;
+}
+
+static int
+MPPEUsable(struct fsm *fp)
+{
+ int ok;
+#ifndef NORADIUS
+ struct radius *r = &fp->bundle->radius;
+
+ /*
+ * If the radius server gave us RAD_MICROSOFT_MS_MPPE_ENCRYPTION_TYPES,
+ * use that instead of our configuration value.
+ */
+ if (*r->cfg.file) {
+ ok = r->mppe.sendkeylen && r->mppe.recvkeylen;
+ if (!ok)
+ log_Printf(LogCCP, "MPPE: Not permitted by RADIUS server\n");
+ } else
+#endif
+ {
+ struct lcp *lcp = &fp->link->lcp;
+ ok = (lcp->want_auth == PROTO_CHAP && lcp->want_authtype == 0x81) ||
+ (lcp->his_auth == PROTO_CHAP && lcp->his_authtype == 0x81);
+ if (!ok)
+ log_Printf(LogCCP, "MPPE: Not usable without CHAP81\n");
+ }
+
+ return ok;
+}
+
+static int
+MPPERequired(struct fsm *fp)
+{
+#ifndef NORADIUS
+ /*
+ * If the radius server gave us RAD_MICROSOFT_MS_MPPE_ENCRYPTION_POLICY,
+ * use that instead of our configuration value.
+ */
+ if (*fp->bundle->radius.cfg.file && fp->bundle->radius.mppe.policy)
+ return fp->bundle->radius.mppe.policy == MPPE_POLICY_REQUIRED ? 1 : 0;
+#endif
+
+ return fp->link->ccp.cfg.mppe.required;
+}
+
+static u_int32_t
+MPPE_ConfigVal(struct bundle *bundle, const struct ccp_config *cfg)
+{
+ u_int32_t val;
+
+ val = cfg->mppe.state == MPPE_STATELESS ? MPPE_OPT_STATELESS : 0;
+#ifndef NORADIUS
+ /*
+ * If the radius server gave us RAD_MICROSOFT_MS_MPPE_ENCRYPTION_TYPES,
+ * use that instead of our configuration value.
+ */
+ if (*bundle->radius.cfg.file && bundle->radius.mppe.types) {
+ if (bundle->radius.mppe.types & MPPE_TYPE_40BIT)
+ val |= MPPE_OPT_40BIT;
+ if (bundle->radius.mppe.types & MPPE_TYPE_128BIT)
+ val |= MPPE_OPT_128BIT;
+ } else
+#endif
+ switch(cfg->mppe.keybits) {
+ case 128:
+ val |= MPPE_OPT_128BIT;
+ break;
+ case 56:
+ val |= MPPE_OPT_56BIT;
+ break;
+ case 40:
+ val |= MPPE_OPT_40BIT;
+ break;
+ case 0:
+ val |= MPPE_OPT_128BIT | MPPE_OPT_56BIT | MPPE_OPT_40BIT;
+ break;
+ }
+
+ return val;
+}
+
+/*
+ * What options should we use for our first configure request
+ */
+static void
+MPPEInitOptsOutput(struct bundle *bundle, struct fsm_opt *o,
+ const struct ccp_config *cfg)
+{
+ u_int32_t mval;
+
+ o->hdr.len = 6;
+
+ if (!MPPE_MasterKeyValid) {
+ log_Printf(LogCCP, "MPPE: MasterKey is invalid,"
+ " MPPE is available only with CHAP81 authentication\n");
+ ua_htonl(0x0, o->data);
+ return;
+ }
+
+
+ mval = MPPE_ConfigVal(bundle, cfg);
+ ua_htonl(&mval, o->data);
+}
+
+/*
+ * Our CCP request was NAK'd with the given options
+ */
+static int
+MPPESetOptsOutput(struct bundle *bundle, struct fsm_opt *o,
+ const struct ccp_config *cfg)
+{
+ u_int32_t mval, peer;
+
+ ua_ntohl(o->data, &peer);
+
+ if (!MPPE_MasterKeyValid)
+ /* Treat their NAK as a REJ */
+ return MODE_NAK;
+
+ mval = MPPE_ConfigVal(bundle, cfg);
+
+ /*
+ * If we haven't been configured with a specific number of keybits, allow
+ * whatever the peer asks for.
+ */
+ if (!cfg->mppe.keybits) {
+ mval &= ~MPPE_OPT_BITMASK;
+ mval |= (peer & MPPE_OPT_BITMASK);
+ if (!(mval & MPPE_OPT_BITMASK))
+ mval |= MPPE_OPT_128BIT;
+ }
+
+ /* Adjust our statelessness */
+ if (cfg->mppe.state == MPPE_ANYSTATE) {
+ mval &= ~MPPE_OPT_STATELESS;
+ mval |= (peer & MPPE_OPT_STATELESS);
+ }
+
+ ua_htonl(&mval, o->data);
+
+ return MODE_ACK;
+}
+
+/*
+ * The peer has requested the given options
+ */
+static int
+MPPESetOptsInput(struct bundle *bundle, struct fsm_opt *o,
+ const struct ccp_config *cfg)
+{
+ u_int32_t mval, peer;
+ int res = MODE_ACK;
+
+ ua_ntohl(o->data, &peer);
+ if (!MPPE_MasterKeyValid) {
+ if (peer != 0) {
+ peer = 0;
+ ua_htonl(&peer, o->data);
+ return MODE_NAK;
+ } else
+ return MODE_ACK;
+ }
+
+ mval = MPPE_ConfigVal(bundle, cfg);
+
+ if (peer & ~MPPE_OPT_MASK)
+ /* He's asking for bits we don't know about */
+ res = MODE_NAK;
+
+ if (peer & MPPE_OPT_STATELESS) {
+ if (cfg->mppe.state == MPPE_STATEFUL)
+ /* Peer can't have stateless */
+ res = MODE_NAK;
+ else
+ /* Peer wants stateless, that's ok */
+ mval |= MPPE_OPT_STATELESS;
+ } else {
+ if (cfg->mppe.state == MPPE_STATELESS)
+ /* Peer must have stateless */
+ res = MODE_NAK;
+ else
+ /* Peer doesn't want stateless, that's ok */
+ mval &= ~MPPE_OPT_STATELESS;
+ }
+
+ /* If we've got a configured number of keybits - the peer must use that */
+ if (cfg->mppe.keybits) {
+ ua_htonl(&mval, o->data);
+ return peer == mval ? res : MODE_NAK;
+ }
+
+ /* If a specific number of bits hasn't been requested, we'll need to NAK */
+ switch (peer & MPPE_OPT_BITMASK) {
+ case MPPE_OPT_128BIT:
+ case MPPE_OPT_56BIT:
+ case MPPE_OPT_40BIT:
+ break;
+ default:
+ res = MODE_NAK;
+ }
+
+ /* Suggest the best number of bits */
+ mval &= ~MPPE_OPT_BITMASK;
+ if (peer & MPPE_OPT_128BIT)
+ mval |= MPPE_OPT_128BIT;
+ else if (peer & MPPE_OPT_56BIT)
+ mval |= MPPE_OPT_56BIT;
+ else if (peer & MPPE_OPT_40BIT)
+ mval |= MPPE_OPT_40BIT;
+ else
+ mval |= MPPE_OPT_128BIT;
+ ua_htonl(&mval, o->data);
+
+ return res;
+}
+
+static struct mppe_state *
+MPPE_InitState(struct fsm_opt *o)
+{
+ struct mppe_state *mp;
+ u_int32_t val;
+
+ if ((mp = calloc(1, sizeof *mp)) != NULL) {
+ ua_ntohl(o->data, &val);
+
+ switch (val & MPPE_OPT_BITMASK) {
+ case MPPE_OPT_128BIT:
+ mp->keylen = 16;
+ mp->keybits = 128;
+ break;
+ case MPPE_OPT_56BIT:
+ mp->keylen = 8;
+ mp->keybits = 56;
+ break;
+ case MPPE_OPT_40BIT:
+ mp->keylen = 8;
+ mp->keybits = 40;
+ break;
+ default:
+ log_Printf(LogWARN, "Unexpected MPPE options 0x%08x\n", val);
+ free(mp);
+ return NULL;
+ }
+
+ mp->stateless = !!(val & MPPE_OPT_STATELESS);
+ }
+
+ return mp;
+}
+
+static void *
+MPPEInitInput(struct bundle *bundle, struct fsm_opt *o)
+{
+ struct mppe_state *mip;
+
+ if (!MPPE_MasterKeyValid) {
+ log_Printf(LogWARN, "MPPE: Cannot initialise without CHAP81\n");
+ return NULL;
+ }
+
+ if ((mip = MPPE_InitState(o)) == NULL) {
+ log_Printf(LogWARN, "MPPEInput: Cannot initialise - unexpected options\n");
+ return NULL;
+ }
+
+ log_Printf(LogDEBUG, "MPPE: InitInput: %d-bits\n", mip->keybits);
+
+#ifndef NORADIUS
+ if (*bundle->radius.cfg.file && bundle->radius.mppe.recvkey) {
+ if (mip->keylen > bundle->radius.mppe.recvkeylen)
+ mip->keylen = bundle->radius.mppe.recvkeylen;
+ if (mip->keylen > sizeof mip->mastkey)
+ mip->keylen = sizeof mip->mastkey;
+ memcpy(mip->mastkey, bundle->radius.mppe.recvkey, mip->keylen);
+ } else
+#endif
+ GetAsymetricStartKey(MPPE_MasterKey, mip->mastkey, mip->keylen, 0,
+ MPPE_IsServer);
+
+ GetNewKeyFromSHA(mip->mastkey, mip->mastkey, mip->keylen, mip->sesskey);
+
+ MPPEReduceSessionKey(mip);
+
+ log_Printf(LogCCP, "MPPE: Input channel initiated\n");
+
+ if (!mip->stateless) {
+ /*
+ * We need to initialise our dictionary here as the first packet we
+ * receive is unlikely to have the FLUSHED bit set.
+ */
+ log_Printf(LogDEBUG, "MPPEInitInput: Dictionary initialised [%d]\n",
+ mip->cohnum);
+ RC4_set_key(&mip->rc4key, mip->keylen, mip->sesskey);
+ } else {
+ /*
+ * We do the first key change here as the first packet is expected
+ * to have a sequence number of 0 and we'll therefore not expect
+ * to have to change the key at that point.
+ */
+ log_Printf(LogDEBUG, "MPPEInitInput: Key changed [%d]\n", mip->cohnum);
+ MPPEKeyChange(mip);
+ }
+
+ return mip;
+}
+
+static void *
+MPPEInitOutput(struct bundle *bundle, struct fsm_opt *o)
+{
+ struct mppe_state *mop;
+
+ if (!MPPE_MasterKeyValid) {
+ log_Printf(LogWARN, "MPPE: Cannot initialise without CHAP81\n");
+ return NULL;
+ }
+
+ if ((mop = MPPE_InitState(o)) == NULL) {
+ log_Printf(LogWARN, "MPPEOutput: Cannot initialise - unexpected options\n");
+ return NULL;
+ }
+
+ log_Printf(LogDEBUG, "MPPE: InitOutput: %d-bits\n", mop->keybits);
+
+#ifndef NORADIUS
+ if (*bundle->radius.cfg.file && bundle->radius.mppe.sendkey) {
+ if (mop->keylen > bundle->radius.mppe.sendkeylen)
+ mop->keylen = bundle->radius.mppe.sendkeylen;
+ if (mop->keylen > sizeof mop->mastkey)
+ mop->keylen = sizeof mop->mastkey;
+ memcpy(mop->mastkey, bundle->radius.mppe.sendkey, mop->keylen);
+ } else
+#endif
+ GetAsymetricStartKey(MPPE_MasterKey, mop->mastkey, mop->keylen, 1,
+ MPPE_IsServer);
+
+ GetNewKeyFromSHA(mop->mastkey, mop->mastkey, mop->keylen, mop->sesskey);
+
+ MPPEReduceSessionKey(mop);
+
+ log_Printf(LogCCP, "MPPE: Output channel initiated\n");
+
+ if (!mop->stateless) {
+ /*
+ * We need to initialise our dictionary now as the first packet we
+ * send won't have the FLUSHED bit set.
+ */
+ log_Printf(LogDEBUG, "MPPEInitOutput: Dictionary initialised [%d]\n",
+ mop->cohnum);
+ RC4_set_key(&mop->rc4key, mop->keylen, mop->sesskey);
+ }
+
+ return mop;
+}
+
+static void
+MPPETermInput(void *v)
+{
+ free(v);
+}
+
+static void
+MPPETermOutput(void *v)
+{
+ free(v);
+}
+
+const struct ccp_algorithm MPPEAlgorithm = {
+ TY_MPPE,
+ CCP_NEG_MPPE,
+ MPPEDispOpts,
+ MPPEUsable,
+ MPPERequired,
+ {
+ MPPESetOptsInput,
+ MPPEInitInput,
+ MPPETermInput,
+ MPPEResetInput,
+ MPPEInput,
+ MPPEDictSetup
+ },
+ {
+ 2,
+ MPPEInitOptsOutput,
+ MPPESetOptsOutput,
+ MPPEInitOutput,
+ MPPETermOutput,
+ MPPEResetOutput,
+ MPPEOutput
+ },
+};
diff --git a/usr.sbin/ppp/mppe.h b/usr.sbin/ppp/mppe.h
new file mode 100644
index 0000000..c70a609
--- /dev/null
+++ b/usr.sbin/ppp/mppe.h
@@ -0,0 +1,33 @@
+/*-
+ * Copyright (c) 2000 Semen Ustimenko <semenu@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define MPPE_KEY_LEN 16
+extern const struct ccp_algorithm MPPEAlgorithm;
+extern int MPPE_MasterKeyValid;
+extern int MPPE_IsServer;
+extern char MPPE_MasterKey[];
diff --git a/usr.sbin/ppp/nat_cmd.c b/usr.sbin/ppp/nat_cmd.c
new file mode 100644
index 0000000..6bdb3cf
--- /dev/null
+++ b/usr.sbin/ppp/nat_cmd.c
@@ -0,0 +1,570 @@
+/*-
+ * Copyright (c) 2001 Charles Mott <cm@linktel.net>
+ * Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#ifdef LOCALNAT
+#include "alias.h"
+#else
+#include <alias.h>
+#endif
+
+#include "layer.h"
+#include "proto.h"
+#include "defs.h"
+#include "command.h"
+#include "log.h"
+#include "nat_cmd.h"
+#include "descriptor.h"
+#include "prompt.h"
+#include "timer.h"
+#include "fsm.h"
+#include "slcompress.h"
+#include "throughput.h"
+#include "iplist.h"
+#include "mbuf.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "ncpaddr.h"
+#include "ip.h"
+#include "ipcp.h"
+#include "ipv6cp.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "mp.h"
+#include "filter.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ncp.h"
+#include "bundle.h"
+
+
+#define NAT_EXTRABUF (13)
+
+static int StrToAddr(const char *, struct in_addr *);
+static int StrToPortRange(const char *, u_short *, u_short *, const char *);
+static int StrToAddrAndPort(const char *, struct in_addr *, u_short *,
+ u_short *, const char *);
+
+static void
+lowhigh(u_short *a, u_short *b)
+{
+ if (a > b) {
+ u_short c;
+
+ c = *b;
+ *b = *a;
+ *a = c;
+ }
+}
+
+int
+nat_RedirectPort(struct cmdargs const *arg)
+{
+ if (!arg->bundle->NatEnabled) {
+ prompt_Printf(arg->prompt, "Alias not enabled\n");
+ return 1;
+ } else if (arg->argc == arg->argn + 3 || arg->argc == arg->argn + 4) {
+ char proto_constant;
+ const char *proto;
+ struct in_addr localaddr;
+ u_short hlocalport, llocalport;
+ struct in_addr aliasaddr;
+ u_short haliasport, laliasport;
+ struct in_addr remoteaddr;
+ u_short hremoteport, lremoteport;
+ struct alias_link *link;
+ int error;
+
+ proto = arg->argv[arg->argn];
+ if (strcmp(proto, "tcp") == 0) {
+ proto_constant = IPPROTO_TCP;
+ } else if (strcmp(proto, "udp") == 0) {
+ proto_constant = IPPROTO_UDP;
+ } else {
+ prompt_Printf(arg->prompt, "port redirect: protocol must be"
+ " tcp or udp\n");
+ return -1;
+ }
+
+ error = StrToAddrAndPort(arg->argv[arg->argn+1], &localaddr, &llocalport,
+ &hlocalport, proto);
+ if (error) {
+ prompt_Printf(arg->prompt, "nat port: error reading localaddr:port\n");
+ return -1;
+ }
+
+ error = StrToPortRange(arg->argv[arg->argn+2], &laliasport, &haliasport,
+ proto);
+ if (error) {
+ prompt_Printf(arg->prompt, "nat port: error reading alias port\n");
+ return -1;
+ }
+ aliasaddr.s_addr = INADDR_ANY;
+
+ if (arg->argc == arg->argn + 4) {
+ error = StrToAddrAndPort(arg->argv[arg->argn+3], &remoteaddr,
+ &lremoteport, &hremoteport, proto);
+ if (error) {
+ prompt_Printf(arg->prompt, "nat port: error reading "
+ "remoteaddr:port\n");
+ return -1;
+ }
+ } else {
+ remoteaddr.s_addr = INADDR_ANY;
+ lremoteport = hremoteport = 0;
+ }
+
+ lowhigh(&llocalport, &hlocalport);
+ lowhigh(&laliasport, &haliasport);
+ lowhigh(&lremoteport, &hremoteport);
+
+ if (haliasport - laliasport != hlocalport - llocalport) {
+ prompt_Printf(arg->prompt, "nat port: local & alias port ranges "
+ "are not equal\n");
+ return -1;
+ }
+
+ if (hremoteport && hremoteport - lremoteport != hlocalport - llocalport) {
+ prompt_Printf(arg->prompt, "nat port: local & remote port ranges "
+ "are not equal\n");
+ return -1;
+ }
+
+ while (laliasport <= haliasport) {
+ link = PacketAliasRedirectPort(localaddr, htons(llocalport),
+ remoteaddr, htons(lremoteport),
+ aliasaddr, htons(laliasport),
+ proto_constant);
+
+ if (link == NULL) {
+ prompt_Printf(arg->prompt, "nat port: %d: error %d\n", laliasport,
+ error);
+ return 1;
+ }
+ llocalport++;
+ laliasport++;
+ if (hremoteport)
+ lremoteport++;
+ }
+
+ return 0;
+ }
+
+ return -1;
+}
+
+
+int
+nat_RedirectAddr(struct cmdargs const *arg)
+{
+ if (!arg->bundle->NatEnabled) {
+ prompt_Printf(arg->prompt, "nat not enabled\n");
+ return 1;
+ } else if (arg->argc == arg->argn+2) {
+ int error;
+ struct in_addr localaddr, aliasaddr;
+ struct alias_link *link;
+
+ error = StrToAddr(arg->argv[arg->argn], &localaddr);
+ if (error) {
+ prompt_Printf(arg->prompt, "address redirect: invalid local address\n");
+ return 1;
+ }
+ error = StrToAddr(arg->argv[arg->argn+1], &aliasaddr);
+ if (error) {
+ prompt_Printf(arg->prompt, "address redirect: invalid alias address\n");
+ prompt_Printf(arg->prompt, "usage: nat %s %s\n", arg->cmd->name,
+ arg->cmd->syntax);
+ return 1;
+ }
+ link = PacketAliasRedirectAddr(localaddr, aliasaddr);
+ if (link == NULL) {
+ prompt_Printf(arg->prompt, "address redirect: packet aliasing"
+ " engine error\n");
+ prompt_Printf(arg->prompt, "usage: nat %s %s\n", arg->cmd->name,
+ arg->cmd->syntax);
+ }
+ } else
+ return -1;
+
+ return 0;
+}
+
+
+int
+nat_RedirectProto(struct cmdargs const *arg)
+{
+ if (!arg->bundle->NatEnabled) {
+ prompt_Printf(arg->prompt, "nat not enabled\n");
+ return 1;
+ } else if (arg->argc >= arg->argn + 2 && arg->argc <= arg->argn + 4) {
+ struct in_addr localIP, publicIP, remoteIP;
+ struct alias_link *link;
+ struct protoent *pe;
+ int error, len;
+
+ len = strlen(arg->argv[arg->argn]);
+ if (len == 0) {
+ prompt_Printf(arg->prompt, "proto redirect: invalid protocol\n");
+ return 1;
+ }
+ if (strspn(arg->argv[arg->argn], "01234567") == len)
+ pe = getprotobynumber(atoi(arg->argv[arg->argn]));
+ else
+ pe = getprotobyname(arg->argv[arg->argn]);
+ if (pe == NULL) {
+ prompt_Printf(arg->prompt, "proto redirect: invalid protocol\n");
+ return 1;
+ }
+
+ error = StrToAddr(arg->argv[arg->argn + 1], &localIP);
+ if (error) {
+ prompt_Printf(arg->prompt, "proto redirect: invalid src address\n");
+ return 1;
+ }
+
+ if (arg->argc >= arg->argn + 3) {
+ error = StrToAddr(arg->argv[arg->argn + 2], &publicIP);
+ if (error) {
+ prompt_Printf(arg->prompt, "proto redirect: invalid alias address\n");
+ prompt_Printf(arg->prompt, "usage: nat %s %s\n", arg->cmd->name,
+ arg->cmd->syntax);
+ return 1;
+ }
+ } else
+ publicIP.s_addr = INADDR_ANY;
+
+ if (arg->argc == arg->argn + 4) {
+ error = StrToAddr(arg->argv[arg->argn + 2], &remoteIP);
+ if (error) {
+ prompt_Printf(arg->prompt, "proto redirect: invalid dst address\n");
+ prompt_Printf(arg->prompt, "usage: nat %s %s\n", arg->cmd->name,
+ arg->cmd->syntax);
+ return 1;
+ }
+ } else
+ remoteIP.s_addr = INADDR_ANY;
+
+ link = PacketAliasRedirectProto(localIP, remoteIP, publicIP, pe->p_proto);
+ if (link == NULL) {
+ prompt_Printf(arg->prompt, "proto redirect: packet aliasing"
+ " engine error\n");
+ prompt_Printf(arg->prompt, "usage: nat %s %s\n", arg->cmd->name,
+ arg->cmd->syntax);
+ }
+ } else
+ return -1;
+
+ return 0;
+}
+
+
+static int
+StrToAddr(const char *str, struct in_addr *addr)
+{
+ struct hostent *hp;
+
+ if (inet_aton(str, addr))
+ return 0;
+
+ hp = gethostbyname(str);
+ if (!hp) {
+ log_Printf(LogWARN, "StrToAddr: Unknown host %s.\n", str);
+ return -1;
+ }
+ *addr = *((struct in_addr *) hp->h_addr);
+ return 0;
+}
+
+
+static int
+StrToPort(const char *str, u_short *port, const char *proto)
+{
+ struct servent *sp;
+ char *end;
+
+ *port = strtol(str, &end, 10);
+ if (*end != '\0') {
+ sp = getservbyname(str, proto);
+ if (sp == NULL) {
+ log_Printf(LogWARN, "StrToAddr: Unknown port or service %s/%s.\n",
+ str, proto);
+ return -1;
+ }
+ *port = ntohs(sp->s_port);
+ }
+
+ return 0;
+}
+
+static int
+StrToPortRange(const char *str, u_short *low, u_short *high, const char *proto)
+{
+ char *minus;
+ int res;
+
+ minus = strchr(str, '-');
+ if (minus)
+ *minus = '\0'; /* Cheat the const-ness ! */
+
+ res = StrToPort(str, low, proto);
+
+ if (minus)
+ *minus = '-'; /* Cheat the const-ness ! */
+
+ if (res == 0) {
+ if (minus)
+ res = StrToPort(minus + 1, high, proto);
+ else
+ *high = *low;
+ }
+
+ return res;
+}
+
+static int
+StrToAddrAndPort(const char *str, struct in_addr *addr, u_short *low,
+ u_short *high, const char *proto)
+{
+ char *colon;
+ int res;
+
+ colon = strchr(str, ':');
+ if (!colon) {
+ log_Printf(LogWARN, "StrToAddrAndPort: %s is missing port number.\n", str);
+ return -1;
+ }
+
+ *colon = '\0'; /* Cheat the const-ness ! */
+ res = StrToAddr(str, addr);
+ *colon = ':'; /* Cheat the const-ness ! */
+ if (res != 0)
+ return -1;
+
+ return StrToPortRange(colon + 1, low, high, proto);
+}
+
+int
+nat_ProxyRule(struct cmdargs const *arg)
+{
+ char cmd[LINE_LEN];
+ int f, pos;
+ size_t len;
+
+ if (arg->argn >= arg->argc)
+ return -1;
+
+ for (f = arg->argn, pos = 0; f < arg->argc; f++) {
+ len = strlen(arg->argv[f]);
+ if (sizeof cmd - pos < len + (len ? 1 : 0))
+ break;
+ if (len)
+ cmd[pos++] = ' ';
+ strcpy(cmd + pos, arg->argv[f]);
+ pos += len;
+ }
+
+ return PacketAliasProxyRule(cmd);
+}
+
+int
+nat_SetTarget(struct cmdargs const *arg)
+{
+ struct in_addr addr;
+
+ if (arg->argc == arg->argn) {
+ addr.s_addr = INADDR_ANY;
+ PacketAliasSetTarget(addr);
+ return 0;
+ }
+
+ if (arg->argc != arg->argn + 1)
+ return -1;
+
+ if (!strcasecmp(arg->argv[arg->argn], "MYADDR")) {
+ addr.s_addr = INADDR_ANY;
+ PacketAliasSetTarget(addr);
+ return 0;
+ }
+
+ addr = GetIpAddr(arg->argv[arg->argn]);
+ if (addr.s_addr == INADDR_NONE) {
+ log_Printf(LogWARN, "%s: invalid address\n", arg->argv[arg->argn]);
+ return 1;
+ }
+
+ PacketAliasSetTarget(addr);
+ return 0;
+}
+
+#ifndef NO_FW_PUNCH
+int
+nat_PunchFW(struct cmdargs const *arg)
+{
+ char *end;
+ long base, count;
+
+ if (arg->argc == arg->argn) {
+ PacketAliasSetMode(0, PKT_ALIAS_PUNCH_FW);
+ return 0;
+ }
+
+ if (arg->argc != arg->argn + 2)
+ return -1;
+
+ base = strtol(arg->argv[arg->argn], &end, 10);
+ if (*end != '\0' || base < 0)
+ return -1;
+
+ count = strtol(arg->argv[arg->argn + 1], &end, 10);
+ if (*end != '\0' || count < 0)
+ return -1;
+
+ PacketAliasSetFWBase(base, count);
+ PacketAliasSetMode(PKT_ALIAS_PUNCH_FW, PKT_ALIAS_PUNCH_FW);
+
+ return 0;
+}
+#endif
+
+static struct mbuf *
+nat_LayerPush(struct bundle *bundle, struct link *l, struct mbuf *bp,
+ int pri, u_short *proto)
+{
+ if (!bundle->NatEnabled || *proto != PROTO_IP)
+ return bp;
+
+ log_Printf(LogDEBUG, "nat_LayerPush: PROTO_IP -> PROTO_IP\n");
+ m_settype(bp, MB_NATOUT);
+ /* Ensure there's a bit of extra buffer for the NAT code... */
+ bp = m_pullup(m_append(bp, NULL, NAT_EXTRABUF));
+ PacketAliasOut(MBUF_CTOP(bp), bp->m_len);
+ bp->m_len = ntohs(((struct ip *)MBUF_CTOP(bp))->ip_len);
+
+ return bp;
+}
+
+static struct mbuf *
+nat_LayerPull(struct bundle *bundle, struct link *l, struct mbuf *bp,
+ u_short *proto)
+{
+ static int gfrags;
+ int ret, len, nfrags;
+ struct mbuf **last;
+ char *fptr;
+
+ if (!bundle->NatEnabled || *proto != PROTO_IP)
+ return bp;
+
+ log_Printf(LogDEBUG, "nat_LayerPull: PROTO_IP -> PROTO_IP\n");
+ m_settype(bp, MB_NATIN);
+ /* Ensure there's a bit of extra buffer for the NAT code... */
+ bp = m_pullup(m_append(bp, NULL, NAT_EXTRABUF));
+ ret = PacketAliasIn(MBUF_CTOP(bp), bp->m_len);
+
+ bp->m_len = ntohs(((struct ip *)MBUF_CTOP(bp))->ip_len);
+ if (bp->m_len > MAX_MRU) {
+ log_Printf(LogWARN, "nat_LayerPull: Problem with IP header length (%lu)\n",
+ (unsigned long)bp->m_len);
+ m_freem(bp);
+ return NULL;
+ }
+
+ switch (ret) {
+ case PKT_ALIAS_OK:
+ break;
+
+ case PKT_ALIAS_UNRESOLVED_FRAGMENT:
+ /* Save the data for later */
+ fptr = malloc(bp->m_len);
+ bp = mbuf_Read(bp, fptr, bp->m_len);
+ PacketAliasSaveFragment(fptr);
+ log_Printf(LogDEBUG, "Store another frag (%lu) - now %d\n",
+ (unsigned long)((struct ip *)fptr)->ip_id, ++gfrags);
+ break;
+
+ case PKT_ALIAS_FOUND_HEADER_FRAGMENT:
+ /* Fetch all the saved fragments and chain them on the end of `bp' */
+ last = &bp->m_nextpkt;
+ nfrags = 0;
+ while ((fptr = PacketAliasGetFragment(MBUF_CTOP(bp))) != NULL) {
+ nfrags++;
+ PacketAliasFragmentIn(MBUF_CTOP(bp), fptr);
+ len = ntohs(((struct ip *)fptr)->ip_len);
+ *last = m_get(len, MB_NATIN);
+ memcpy(MBUF_CTOP(*last), fptr, len);
+ free(fptr);
+ last = &(*last)->m_nextpkt;
+ }
+ gfrags -= nfrags;
+ log_Printf(LogDEBUG, "Found a frag header (%lu) - plus %d more frags (no"
+ "w %d)\n", (unsigned long)((struct ip *)MBUF_CTOP(bp))->ip_id,
+ nfrags, gfrags);
+ break;
+
+ case PKT_ALIAS_IGNORED:
+ if (PacketAliasSetMode(0, 0) & PKT_ALIAS_DENY_INCOMING) {
+ log_Printf(LogTCPIP, "NAT engine denied data:\n");
+ m_freem(bp);
+ bp = NULL;
+ } else if (log_IsKept(LogTCPIP)) {
+ log_Printf(LogTCPIP, "NAT engine ignored data:\n");
+ PacketCheck(bundle, AF_INET, MBUF_CTOP(bp), bp->m_len, NULL,
+ NULL, NULL);
+ }
+ break;
+
+ default:
+ log_Printf(LogWARN, "nat_LayerPull: Dropped a packet (%d)....\n", ret);
+ m_freem(bp);
+ bp = NULL;
+ break;
+ }
+
+ return bp;
+}
+
+struct layer natlayer =
+ { LAYER_NAT, "nat", nat_LayerPush, nat_LayerPull };
diff --git a/usr.sbin/ppp/nat_cmd.h b/usr.sbin/ppp/nat_cmd.h
new file mode 100644
index 0000000..c70afb0
--- /dev/null
+++ b/usr.sbin/ppp/nat_cmd.h
@@ -0,0 +1,41 @@
+/*-
+ * Copyright (c) 2001 Charles Mott <cm@linktel.net>
+ * Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct cmdargs;
+
+extern int nat_RedirectPort(struct cmdargs const *);
+extern int nat_RedirectAddr(struct cmdargs const *);
+extern int nat_RedirectProto(struct cmdargs const *);
+extern int nat_ProxyRule(struct cmdargs const *);
+extern int nat_SetTarget(struct cmdargs const *);
+#ifndef NO_FW_PUNCH
+extern int nat_PunchFW(struct cmdargs const *);
+#endif
+
+extern struct layer natlayer;
diff --git a/usr.sbin/ppp/ncp.c b/usr.sbin/ppp/ncp.c
new file mode 100644
index 0000000..ea03144
--- /dev/null
+++ b/usr.sbin/ppp/ncp.c
@@ -0,0 +1,536 @@
+/*-
+ * Copyright (c) 2001 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <sys/socket.h>
+#include <net/route.h>
+#include <sys/un.h>
+
+#include <errno.h>
+#include <resolv.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "layer.h"
+#include "defs.h"
+#include "command.h"
+#include "mbuf.h"
+#include "log.h"
+#include "timer.h"
+#include "fsm.h"
+#include "iplist.h"
+#include "throughput.h"
+#include "slcompress.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "lcp.h"
+#include "ncpaddr.h"
+#include "ipcp.h"
+#include "filter.h"
+#include "descriptor.h"
+#include "async.h"
+#include "ccp.h"
+#include "link.h"
+#include "physical.h"
+#include "mp.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+#include "prompt.h"
+#include "route.h"
+#include "iface.h"
+#include "chat.h"
+#include "auth.h"
+#include "chap.h"
+#include "cbcp.h"
+#include "datalink.h"
+
+
+static u_short default_urgent_tcp_ports[] = {
+ 21, /* ftp */
+ 22, /* ssh */
+ 23, /* telnet */
+ 513, /* login */
+ 514, /* shell */
+ 543, /* klogin */
+ 544 /* kshell */
+};
+
+static u_short default_urgent_udp_ports[] = { };
+
+#define NDEFTCPPORTS \
+ (sizeof default_urgent_tcp_ports / sizeof default_urgent_tcp_ports[0])
+#define NDEFUDPPORTS \
+ (sizeof default_urgent_udp_ports / sizeof default_urgent_udp_ports[0])
+
+void
+ncp_Init(struct ncp *ncp, struct bundle *bundle)
+{
+ ncp->afq = AF_INET;
+ ncp->route = NULL;
+
+ ncp->cfg.urgent.tcp.nports = ncp->cfg.urgent.tcp.maxports = NDEFTCPPORTS;
+ ncp->cfg.urgent.tcp.port = (u_short *)malloc(NDEFTCPPORTS * sizeof(u_short));
+ memcpy(ncp->cfg.urgent.tcp.port, default_urgent_tcp_ports,
+ NDEFTCPPORTS * sizeof(u_short));
+ ncp->cfg.urgent.tos = 1;
+
+ ncp->cfg.urgent.udp.nports = ncp->cfg.urgent.udp.maxports = NDEFUDPPORTS;
+ ncp->cfg.urgent.udp.port = (u_short *)malloc(NDEFUDPPORTS * sizeof(u_short));
+ memcpy(ncp->cfg.urgent.udp.port, default_urgent_udp_ports,
+ NDEFUDPPORTS * sizeof(u_short));
+
+
+ mp_Init(&ncp->mp, bundle);
+
+ /* Send over the first physical link by default */
+ ipcp_Init(&ncp->ipcp, bundle, &bundle->links->physical->link,
+ &bundle->fsm);
+#ifndef NOINET6
+ ipv6cp_Init(&ncp->ipv6cp, bundle, &bundle->links->physical->link,
+ &bundle->fsm);
+#endif
+}
+
+void
+ncp_Destroy(struct ncp *ncp)
+{
+ ipcp_Destroy(&ncp->ipcp);
+#ifndef NOINET6
+ ipv6cp_Destroy(&ncp->ipv6cp);
+#endif
+
+ if (ncp->cfg.urgent.tcp.maxports) {
+ ncp->cfg.urgent.tcp.nports = ncp->cfg.urgent.tcp.maxports = 0;
+ free(ncp->cfg.urgent.tcp.port);
+ ncp->cfg.urgent.tcp.port = NULL;
+ }
+ if (ncp->cfg.urgent.udp.maxports) {
+ ncp->cfg.urgent.udp.nports = ncp->cfg.urgent.udp.maxports = 0;
+ free(ncp->cfg.urgent.udp.port);
+ ncp->cfg.urgent.udp.port = NULL;
+ }
+}
+
+int
+ncp_fsmStart(struct ncp *ncp, struct bundle *bundle)
+{
+ int res = 0;
+
+#ifndef NOINET6
+ if (Enabled(bundle, OPT_IPCP)) {
+#endif
+ fsm_Up(&ncp->ipcp.fsm);
+ fsm_Open(&ncp->ipcp.fsm);
+ res++;
+#ifndef NOINET6
+ }
+
+ if (Enabled(bundle, OPT_IPV6CP)) {
+ fsm_Up(&ncp->ipv6cp.fsm);
+ fsm_Open(&ncp->ipv6cp.fsm);
+ res++;
+ }
+#endif
+
+ return res;
+}
+
+void
+ncp_IfaceAddrAdded(struct ncp *ncp, const struct iface_addr *addr)
+{
+ switch (ncprange_family(&addr->ifa)) {
+ case AF_INET:
+ ipcp_IfaceAddrAdded(&ncp->ipcp, addr);
+ break;
+#ifndef NOINET6
+ case AF_INET6:
+ ipv6cp_IfaceAddrAdded(&ncp->ipv6cp, addr);
+ break;
+#endif
+ }
+}
+
+void
+ncp_IfaceAddrDeleted(struct ncp *ncp, const struct iface_addr *addr)
+{
+ if (ncprange_family(&addr->ifa) == AF_INET)
+ ipcp_IfaceAddrDeleted(&ncp->ipcp, addr);
+}
+
+void
+ncp_SetLink(struct ncp *ncp, struct link *l)
+{
+ ipcp_SetLink(&ncp->ipcp, l);
+#ifndef NOINET6
+ ipv6cp_SetLink(&ncp->ipv6cp, l);
+#endif
+}
+
+/*
+ * Enqueue a packet of the given address family. Nothing will make it
+ * down to the physical link level 'till ncp_FillPhysicalQueues() is used.
+ */
+void
+ncp_Enqueue(struct ncp *ncp, int af, int pri, char *ptr, int count)
+{
+#ifndef NOINET6
+ struct ipv6cp *ipv6cp = &ncp->ipv6cp;
+#endif
+ struct ipcp *ipcp = &ncp->ipcp;
+ struct mbuf *bp;
+
+ /*
+ * We allocate an extra 6 bytes, four at the front and two at the end.
+ * This is an optimisation so that we need to do less work in
+ * m_prepend() in acf_LayerPush() and proto_LayerPush() and
+ * appending in hdlc_LayerPush().
+ */
+
+ switch (af) {
+ case AF_INET:
+ if (pri < 0 || pri >= IPCP_QUEUES(ipcp)) {
+ log_Printf(LogERROR, "Can't store in ip queue %d\n", pri);
+ break;
+ }
+
+ bp = m_get(count + 6, MB_IPOUT);
+ bp->m_offset += 4;
+ bp->m_len -= 6;
+ memcpy(MBUF_CTOP(bp), ptr, count);
+ m_enqueue(ipcp->Queue + pri, bp);
+ break;
+
+#ifndef NOINET6
+ case AF_INET6:
+ if (pri < 0 || pri >= IPV6CP_QUEUES(ipcp)) {
+ log_Printf(LogERROR, "Can't store in ipv6 queue %d\n", pri);
+ break;
+ }
+
+ bp = m_get(count + 6, MB_IPOUT);
+ bp->m_offset += 4;
+ bp->m_len -= 6;
+ memcpy(MBUF_CTOP(bp), ptr, count);
+ m_enqueue(ipv6cp->Queue + pri, bp);
+ break;
+#endif
+
+ default:
+ log_Printf(LogERROR, "Can't enqueue protocol family %d\n", af);
+ }
+}
+
+/*
+ * How many packets are queued to go out ?
+ */
+size_t
+ncp_QueueLen(struct ncp *ncp)
+{
+ size_t result;
+
+ result = ipcp_QueueLen(&ncp->ipcp);
+#ifndef NOINET6
+ result += ipv6cp_QueueLen(&ncp->ipv6cp);
+#endif
+ result += mp_QueueLen(&ncp->mp); /* Usually empty */
+
+ return result;
+}
+
+/*
+ * Ditch all queued packets. This is usually done after our choked timer
+ * has fired - which happens because we couldn't send any traffic over
+ * any links for some time.
+ */
+void
+ncp_DeleteQueues(struct ncp *ncp)
+{
+#ifndef NOINET6
+ struct ipv6cp *ipv6cp = &ncp->ipv6cp;
+#endif
+ struct ipcp *ipcp = &ncp->ipcp;
+ struct mp *mp = &ncp->mp;
+ struct mqueue *q;
+
+ for (q = ipcp->Queue; q < ipcp->Queue + IPCP_QUEUES(ipcp); q++)
+ while (q->top)
+ m_freem(m_dequeue(q));
+
+#ifndef NOINET6
+ for (q = ipv6cp->Queue; q < ipv6cp->Queue + IPV6CP_QUEUES(ipv6cp); q++)
+ while (q->top)
+ m_freem(m_dequeue(q));
+#endif
+
+ link_DeleteQueue(&mp->link); /* Usually empty anyway */
+}
+
+/*
+ * Arrange that each of our links has at least one packet. We keep the
+ * number of packets queued at the link level to a minimum so that the
+ * loss of a link in multi-link mode results in the minimum number of
+ * dropped packets.
+ */
+size_t
+ncp_FillPhysicalQueues(struct ncp *ncp, struct bundle *bundle)
+{
+ size_t total;
+
+ if (bundle->ncp.mp.active)
+ total = mp_FillPhysicalQueues(bundle);
+ else {
+ struct datalink *dl;
+ size_t add;
+
+ for (total = 0, dl = bundle->links; dl; dl = dl->next)
+ if (dl->state == DATALINK_OPEN) {
+ add = link_QueueLen(&dl->physical->link);
+ if (add == 0 && dl->physical->out == NULL)
+ add = ncp_PushPacket(ncp, &ncp->afq, &dl->physical->link);
+ total += add;
+ }
+ }
+
+ return total + ncp_QueueLen(&bundle->ncp);
+}
+
+/*
+ * Push a packet into the given link. ``af'' is used as a persistent record
+ * of what is to be pushed next, coming either from mp->out or ncp->afq.
+ */
+int
+ncp_PushPacket(struct ncp *ncp, int *af, struct link *l)
+{
+ struct bundle *bundle = l->lcp.fsm.bundle;
+ int res;
+
+#ifndef NOINET6
+ if (*af == AF_INET) {
+ if ((res = ipcp_PushPacket(&bundle->ncp.ipcp, l)))
+ *af = AF_INET6;
+ else
+ res = ipv6cp_PushPacket(&bundle->ncp.ipv6cp, l);
+ } else {
+ if ((res = ipv6cp_PushPacket(&bundle->ncp.ipv6cp, l)))
+ *af = AF_INET;
+ else
+ res = ipcp_PushPacket(&bundle->ncp.ipcp, l);
+ }
+#else
+ res = ipcp_PushPacket(&bundle->ncp.ipcp, l);
+#endif
+
+ return res;
+}
+
+int
+ncp_IsUrgentPort(struct port_range *range, u_short src, u_short dst)
+{
+ int f;
+
+ for (f = 0; f < range->nports; f++)
+ if (range->port[f] == src || range->port[f] == dst)
+ return 1;
+
+ return 0;
+}
+
+void
+ncp_AddUrgentPort(struct port_range *range, u_short port)
+{
+ u_short *newport;
+ int p;
+
+ if (range->nports == range->maxports) {
+ range->maxports += 10;
+ newport = (u_short *)realloc(range->port,
+ range->maxports * sizeof(u_short));
+ if (newport == NULL) {
+ log_Printf(LogERROR, "ncp_AddUrgentPort: realloc: %s\n",
+ strerror(errno));
+ range->maxports -= 10;
+ return;
+ }
+ range->port = newport;
+ }
+
+ for (p = 0; p < range->nports; p++)
+ if (range->port[p] == port) {
+ log_Printf(LogWARN, "%u: Port already set to urgent\n", port);
+ break;
+ } else if (range->port[p] > port) {
+ memmove(range->port + p + 1, range->port + p,
+ (range->nports - p) * sizeof(u_short));
+ range->port[p] = port;
+ range->nports++;
+ break;
+ }
+
+ if (p == range->nports)
+ range->port[range->nports++] = port;
+}
+
+void
+ncp_RemoveUrgentPort(struct port_range *range, u_short port)
+{
+ int p;
+
+ for (p = 0; p < range->nports; p++)
+ if (range->port[p] == port) {
+ if (p != range->nports - 1)
+ memmove(range->port + p, range->port + p + 1,
+ (range->nports - p - 1) * sizeof(u_short));
+ range->nports--;
+ return;
+ }
+
+ if (p == range->nports)
+ log_Printf(LogWARN, "%u: Port not set to urgent\n", port);
+}
+
+void
+ncp_ClearUrgentPorts(struct port_range *range)
+{
+ range->nports = 0;
+}
+
+int
+ncp_Show(struct cmdargs const *arg)
+{
+ struct ncp *ncp = &arg->bundle->ncp;
+ int p;
+
+#ifndef NOINET6
+ prompt_Printf(arg->prompt, "Next queued AF: %s\n",
+ ncp->afq == AF_INET6 ? "inet6" : "inet");
+#endif
+
+ if (ncp->route) {
+ prompt_Printf(arg->prompt, "\n");
+ route_ShowSticky(arg->prompt, ncp->route, "Sticky routes", 1);
+ }
+
+ prompt_Printf(arg->prompt, "\nDefaults:\n");
+ prompt_Printf(arg->prompt, " sendpipe: ");
+ if (ncp->cfg.sendpipe > 0)
+ prompt_Printf(arg->prompt, "%-20ld\n", ncp->cfg.sendpipe);
+ else
+ prompt_Printf(arg->prompt, "unspecified\n");
+ prompt_Printf(arg->prompt, " recvpipe: ");
+ if (ncp->cfg.recvpipe > 0)
+ prompt_Printf(arg->prompt, "%ld\n", ncp->cfg.recvpipe);
+ else
+ prompt_Printf(arg->prompt, "unspecified\n");
+
+ prompt_Printf(arg->prompt, "\n Urgent ports\n");
+ prompt_Printf(arg->prompt, " TCP: ");
+ if (ncp->cfg.urgent.tcp.nports == 0)
+ prompt_Printf(arg->prompt, "none");
+ else
+ for (p = 0; p < ncp->cfg.urgent.tcp.nports; p++) {
+ if (p)
+ prompt_Printf(arg->prompt, ", ");
+ prompt_Printf(arg->prompt, "%u", ncp->cfg.urgent.tcp.port[p]);
+ }
+
+ prompt_Printf(arg->prompt, "\n UDP: ");
+ if (ncp->cfg.urgent.udp.nports == 0)
+ prompt_Printf(arg->prompt, "none");
+ else
+ for (p = 0; p < ncp->cfg.urgent.udp.nports; p++) {
+ if (p)
+ prompt_Printf(arg->prompt, ", ");
+ prompt_Printf(arg->prompt, "%u", ncp->cfg.urgent.udp.port[p]);
+ }
+ prompt_Printf(arg->prompt, "\n TOS: %s\n\n",
+ ncp->cfg.urgent.tos ? "yes" : "no");
+
+ return 0;
+}
+
+int
+ncp_LayersOpen(struct ncp *ncp)
+{
+ int n;
+
+ n = !!(ncp->ipcp.fsm.state == ST_OPENED);
+#ifndef NOINET6
+ n += !!(ncp->ipv6cp.fsm.state == ST_OPENED);
+#endif
+
+ return n;
+}
+
+int
+ncp_LayersUnfinished(struct ncp *ncp)
+{
+ int n = 0;
+
+ if (ncp->ipcp.fsm.state > ST_CLOSED ||
+ ncp->ipcp.fsm.state == ST_STARTING)
+ n++;
+
+#ifndef NOINET6
+ if (ncp->ipv6cp.fsm.state > ST_CLOSED ||
+ ncp->ipv6cp.fsm.state == ST_STARTING)
+ n++;
+#endif
+
+ return n;
+}
+
+void
+ncp_Close(struct ncp *ncp)
+{
+ if (ncp->ipcp.fsm.state > ST_CLOSED ||
+ ncp->ipcp.fsm.state == ST_STARTING)
+ fsm_Close(&ncp->ipcp.fsm);
+
+#ifndef NOINET6
+ if (ncp->ipv6cp.fsm.state > ST_CLOSED ||
+ ncp->ipv6cp.fsm.state == ST_STARTING)
+ fsm_Close(&ncp->ipv6cp.fsm);
+#endif
+}
+
+void
+ncp2initial(struct ncp *ncp)
+{
+ fsm2initial(&ncp->ipcp.fsm);
+#ifndef NOINET6
+ fsm2initial(&ncp->ipv6cp.fsm);
+#endif
+}
diff --git a/usr.sbin/ppp/ncp.h b/usr.sbin/ppp/ncp.h
new file mode 100644
index 0000000..c9c3be5
--- /dev/null
+++ b/usr.sbin/ppp/ncp.h
@@ -0,0 +1,101 @@
+/*-
+ * Copyright (c) 2001 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct port_range {
+ unsigned nports; /* How many ports */
+ unsigned maxports; /* How many allocated (malloc) ports */
+ u_short *port; /* The actual ports */
+};
+
+struct ncp {
+ struct {
+ u_long sendpipe; /* route sendpipe size */
+ u_long recvpipe; /* route recvpipe size */
+
+ struct {
+ struct port_range tcp, udp; /* The range of urgent ports */
+ unsigned tos : 1; /* Urgent IPTOS_LOWDELAY packets ? */
+ } urgent;
+ } cfg;
+
+ int afq; /* Next address family to queue */
+
+ struct sticky_route *route; /* List of dynamic routes */
+
+ struct ipcp ipcp; /* Our IPCP FSM */
+#ifndef NOINET6
+ struct ipv6cp ipv6cp; /* Our IPV6CP FSM */
+#endif
+ struct mp mp; /* Our MP */
+};
+
+extern void ncp_Init(struct ncp *, struct bundle *);
+extern void ncp_Destroy(struct ncp *);
+extern int ncp_fsmStart(struct ncp *, struct bundle *);
+extern void ncp_IfaceAddrAdded(struct ncp *, const struct iface_addr *);
+extern void ncp_IfaceAddrDeleted(struct ncp *, const struct iface_addr *);
+extern void ncp_SetLink(struct ncp *, struct link *);
+extern void ncp_Enqueue(struct ncp *, int, int, char *, int);
+extern void ncp_DeleteQueues(struct ncp *);
+extern size_t ncp_QueueLen(struct ncp *);
+extern size_t ncp_FillPhysicalQueues(struct ncp *, struct bundle *);
+extern int ncp_PushPacket(struct ncp *, int *, struct link *);
+extern int ncp_IsUrgentPort(struct port_range *, u_short, u_short);
+extern void ncp_AddUrgentPort(struct port_range *, u_short);
+extern void ncp_RemoveUrgentPort(struct port_range *, u_short);
+extern void ncp_ClearUrgentPorts(struct port_range *);
+extern int ncp_Show(struct cmdargs const *);
+extern int ncp_LayersOpen(struct ncp *);
+extern int ncp_LayersUnfinished(struct ncp *);
+extern void ncp_Close(struct ncp *);
+extern void ncp2initial(struct ncp *);
+
+#define ncp_IsUrgentTcpPort(ncp, p1, p2) \
+ ncp_IsUrgentPort(&(ncp)->cfg.urgent.tcp, p1, p2)
+#define ncp_IsUrgentUdpPort(ncp, p1, p2) \
+ ncp_IsUrgentPort(&(ncp)->cfg.urgent.udp, p1, p2)
+#define ncp_AddUrgentTcpPort(ncp, p) \
+ ncp_AddUrgentPort(&(ncp)->cfg.urgent.tcp, p)
+#define ncp_AddUrgentUdpPort(ncp, p) \
+ ncp_AddUrgentPort(&(ncp)->cfg.urgent.udp, p)
+#define ncp_RemoveUrgentTcpPort(ncp, p) \
+ ncp_RemoveUrgentPort(&(ncp)->cfg.urgent.tcp, p)
+#define ncp_RemoveUrgentUdpPort(ncp, p) \
+ ncp_RemoveUrgentPort(&(ncp)->cfg.urgent.udp, p)
+#define ncp_ClearUrgentTcpPorts(ncp) \
+ ncp_ClearUrgentPorts(&(ncp)->cfg.urgent.tcp)
+#define ncp_ClearUrgentUdpPorts(ncp) \
+ ncp_ClearUrgentPorts(&(ncp)->cfg.urgent.udp)
+#define ncp_ClearUrgentTOS(ncp) (ncp)->cfg.urgent.tos = 0;
+#define ncp_SetUrgentTOS(ncp) (ncp)->cfg.urgent.tos = 1;
+
+#ifndef NOINET6
+#define isncp(proto) ((proto) == PROTO_IPCP || (proto) == PROTO_IPV6CP)
+#else
+#define isncp(proto) ((proto) == PROTO_IPCP)
+#endif
diff --git a/usr.sbin/ppp/ncpaddr.c b/usr.sbin/ppp/ncpaddr.c
new file mode 100644
index 0000000..3d2d356
--- /dev/null
+++ b/usr.sbin/ppp/ncpaddr.c
@@ -0,0 +1,992 @@
+/*-
+ * Copyright (c) 2001 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#ifdef __OpenBSD__
+#include <net/if_types.h>
+#include <net/route.h>
+#endif
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "log.h"
+#include "ncpaddr.h"
+#include "timer.h"
+#include "fsm.h"
+#include "defs.h"
+#include "slcompress.h"
+#include "iplist.h"
+#include "throughput.h"
+#include "mbuf.h"
+#include "ipcp.h"
+#include "descriptor.h"
+#include "layer.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "mp.h"
+#include "ipv6cp.h"
+#include "ncp.h"
+
+
+#define ncprange_ip4addr u.ip4.ipaddr
+#define ncprange_ip4mask u.ip4.mask
+#define ncprange_ip4width u.ip4.width
+#define ncpaddr_ip4addr u.ip4addr
+#ifndef NOINET6
+#define ncprange_ip6addr u.ip6.ipaddr
+#define ncprange_ip6width u.ip6.width
+#define ncpaddr_ip6addr u.ip6addr
+#endif
+
+#define NCP_ASCIIBUFFERSIZE 52
+
+static struct in_addr
+bits2mask4(int bits)
+{
+ struct in_addr result;
+ u_int32_t bit = 0x80000000;
+
+ result.s_addr = 0;
+
+ while (bits) {
+ result.s_addr |= bit;
+ bit >>= 1;
+ bits--;
+ }
+
+ result.s_addr = htonl(result.s_addr);
+ return result;
+}
+
+static int
+mask42bits(struct in_addr mask)
+{
+ u_int32_t msk = ntohl(mask.s_addr);
+ u_int32_t tst;
+ int ret;
+
+ for (ret = 32, tst = 1; tst; ret--, tst <<= 1)
+ if (msk & tst)
+ break;
+
+ for (tst <<= 1; tst; tst <<= 1)
+ if (!(msk & tst))
+ break;
+
+ return tst ? -1 : ret;
+}
+
+#ifndef NOINET6
+static struct in6_addr
+bits2mask6(int bits)
+{
+ struct in6_addr result;
+ u_int32_t bit = 0x80;
+ u_char *c = result.s6_addr;
+
+ memset(&result, '\0', sizeof result);
+
+ while (bits) {
+ if (bit == 0) {
+ bit = 0x80;
+ c++;
+ }
+ *c |= bit;
+ bit >>= 1;
+ bits--;
+ }
+
+ return result;
+}
+
+static int
+mask62bits(const struct in6_addr *mask)
+{
+ const u_char masks[] = { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe };
+ const u_char *c, *p, *end;
+ int masklen;
+
+ p = (const u_char *)mask;
+ for (masklen = 0, end = p + 16; p < end && *p == 0xff; p++)
+ masklen += 8;
+
+ if (p < end) {
+ for (c = masks; c < masks + sizeof masks; c++)
+ if (*c == *p) {
+ masklen += c - masks;
+ break;
+ }
+ }
+
+ return masklen;
+}
+
+static void
+adjust_linklocal(struct sockaddr_in6 *sin6)
+{
+ /* XXX: ?????!?!?!!!!! This is horrible ! */
+ if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) ||
+ IN6_IS_ADDR_MC_LINKLOCAL(&sin6->sin6_addr)) {
+ sin6->sin6_scope_id =
+ ntohs(*(u_short *)&sin6->sin6_addr.s6_addr[2]);
+ *(u_short *)&sin6->sin6_addr.s6_addr[2] = 0;
+ }
+}
+#endif
+
+void
+ncpaddr_init(struct ncpaddr *addr)
+{
+ addr->ncpaddr_family = AF_UNSPEC;
+}
+
+int
+ncpaddr_isset(const struct ncpaddr *addr)
+{
+ return addr->ncpaddr_family != AF_UNSPEC;
+}
+
+int
+ncpaddr_isdefault(const struct ncpaddr *addr)
+{
+ switch (addr->ncpaddr_family) {
+ case AF_INET:
+ if (addr->ncpaddr_ip4addr.s_addr == INADDR_ANY)
+ return 1;
+ break;
+
+#ifndef NOINET6
+ case AF_INET6:
+ if (IN6_IS_ADDR_UNSPECIFIED(&addr->ncpaddr_ip6addr))
+ return 1;
+ break;
+#endif
+ }
+
+ return 0;
+}
+
+int
+ncpaddr_equal(const struct ncpaddr *addr, const struct ncpaddr *cmp)
+{
+ if (addr->ncpaddr_family != cmp->ncpaddr_family)
+ return 0;
+
+ switch (addr->ncpaddr_family) {
+ case AF_INET:
+ return addr->ncpaddr_ip4addr.s_addr == cmp->ncpaddr_ip4addr.s_addr;
+
+#ifndef NOINET6
+ case AF_INET6:
+ return !memcmp(&addr->ncpaddr_ip6addr, &cmp->ncpaddr_ip6addr,
+ sizeof addr->ncpaddr_ip6addr);
+#endif
+
+ case AF_UNSPEC:
+ return 1;
+ }
+
+ return 0;
+}
+
+void
+ncpaddr_copy(struct ncpaddr *addr, const struct ncpaddr *from)
+{
+ switch (from->ncpaddr_family) {
+ case AF_INET:
+ addr->ncpaddr_family = AF_INET;
+ addr->ncpaddr_ip4addr = from->ncpaddr_ip4addr;
+ break;
+#ifndef NOINET6
+ case AF_INET6:
+ addr->ncpaddr_family = AF_INET6;
+ addr->ncpaddr_ip6addr = from->ncpaddr_ip6addr;
+ break;
+#endif
+ default:
+ addr->ncpaddr_family = AF_UNSPEC;
+ }
+}
+
+void
+ncpaddr_setip4addr(struct ncpaddr *addr, u_int32_t ip)
+{
+ addr->ncpaddr_family = AF_INET;
+ addr->ncpaddr_ip4addr.s_addr = ip;
+}
+
+int
+ncpaddr_getip4addr(const struct ncpaddr *addr, u_int32_t *ip)
+{
+ if (addr->ncpaddr_family != AF_INET)
+ return 0;
+ *ip = addr->ncpaddr_ip4addr.s_addr;
+ return 1;
+}
+
+void
+ncpaddr_setip4(struct ncpaddr *addr, struct in_addr ip)
+{
+ addr->ncpaddr_family = AF_INET;
+ addr->ncpaddr_ip4addr = ip;
+}
+
+int
+ncpaddr_getip4(const struct ncpaddr *addr, struct in_addr *ip)
+{
+ if (addr->ncpaddr_family != AF_INET)
+ return 0;
+ *ip = addr->ncpaddr_ip4addr;
+ return 1;
+}
+
+#ifndef NOINET6
+void
+ncpaddr_setip6(struct ncpaddr *addr, const struct in6_addr *ip6)
+{
+ addr->ncpaddr_family = AF_INET6;
+ addr->ncpaddr_ip6addr = *ip6;
+}
+
+int
+ncpaddr_getip6(const struct ncpaddr *addr, struct in6_addr *ip6)
+{
+ if (addr->ncpaddr_family != AF_INET6)
+ return 0;
+ *ip6 = addr->ncpaddr_ip6addr;
+ return 1;
+}
+#endif
+
+void
+ncpaddr_getsa(const struct ncpaddr *addr, struct sockaddr_storage *host)
+{
+ struct sockaddr_in *host4 = (struct sockaddr_in *)host;
+#ifndef NOINET6
+ struct sockaddr_in6 *host6 = (struct sockaddr_in6 *)host;
+#endif
+
+ memset(host, '\0', sizeof(*host));
+
+ switch (addr->ncpaddr_family) {
+ case AF_INET:
+ host4->sin_family = AF_INET;
+ host4->sin_len = sizeof(*host4);
+ host4->sin_addr = addr->ncpaddr_ip4addr;
+ break;
+
+#ifndef NOINET6
+ case AF_INET6:
+ host6->sin6_family = AF_INET6;
+ host6->sin6_len = sizeof(*host6);
+ host6->sin6_addr = addr->ncpaddr_ip6addr;
+ break;
+#endif
+
+ default:
+ host->ss_family = AF_UNSPEC;
+ break;
+ }
+}
+
+void
+ncpaddr_setsa(struct ncpaddr *addr, const struct sockaddr *host)
+{
+ const struct sockaddr_in *host4 = (const struct sockaddr_in *)host;
+#ifndef NOINET6
+ const struct sockaddr_in6 *host6 = (const struct sockaddr_in6 *)host;
+#endif
+
+ switch (host->sa_family) {
+ case AF_INET:
+ addr->ncpaddr_family = AF_INET;
+ addr->ncpaddr_ip4addr = host4->sin_addr;
+ break;
+
+#ifndef NOINET6
+ case AF_INET6:
+ if (IN6_IS_ADDR_V4MAPPED(&host6->sin6_addr)) {
+ addr->ncpaddr_family = AF_INET;
+ addr->ncpaddr_ip4addr.s_addr =
+ *(const u_int32_t *)(host6->sin6_addr.s6_addr + 12);
+ } else {
+ addr->ncpaddr_family = AF_INET6;
+ addr->ncpaddr_ip6addr = host6->sin6_addr;
+ }
+ break;
+#endif
+
+ default:
+ addr->ncpaddr_family = AF_UNSPEC;
+ }
+}
+
+static char *
+ncpaddr_ntowa(const struct ncpaddr *addr)
+{
+ static char res[NCP_ASCIIBUFFERSIZE];
+#ifndef NOINET6
+ struct sockaddr_in6 sin6;
+#endif
+
+ switch (addr->ncpaddr_family) {
+ case AF_INET:
+ snprintf(res, sizeof res, "%s", inet_ntoa(addr->ncpaddr_ip4addr));
+ return res;
+
+#ifndef NOINET6
+ case AF_INET6:
+ memset(&sin6, '\0', sizeof(sin6));
+ sin6.sin6_len = sizeof(sin6);
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_addr = addr->ncpaddr_ip6addr;
+ adjust_linklocal(&sin6);
+ if (getnameinfo((struct sockaddr *)&sin6, sizeof sin6, res, sizeof(res),
+ NULL, 0, NI_WITHSCOPEID | NI_NUMERICHOST) != 0)
+ break;
+
+ return res;
+#endif
+ }
+
+ snprintf(res, sizeof res, "<AF_UNSPEC>");
+ return res;
+}
+
+const char *
+ncpaddr_ntoa(const struct ncpaddr *addr)
+{
+ return ncpaddr_ntowa(addr);
+}
+
+
+int
+ncpaddr_aton(struct ncpaddr *addr, struct ncp *ncp, const char *data)
+{
+ struct ncprange range;
+
+ if (!ncprange_aton(&range, ncp, data))
+ return 0;
+
+ if (range.ncprange_family == AF_INET && range.ncprange_ip4width != 32) {
+ log_Printf(LogWARN, "ncpaddr_aton: %s: Only 32 bits allowed\n", data);
+ return 0;
+ }
+
+#ifndef NOINET6
+ if (range.ncprange_family == AF_INET6 && range.ncprange_ip6width != 128) {
+ log_Printf(LogWARN, "ncpaddr_aton: %s: Only 128 bits allowed\n", data);
+ return 0;
+ }
+#endif
+
+ switch (range.ncprange_family) {
+ case AF_INET:
+ addr->ncpaddr_family = range.ncprange_family;
+ addr->ncpaddr_ip4addr = range.ncprange_ip4addr;
+ return 1;
+
+#ifndef NOINET6
+ case AF_INET6:
+ addr->ncpaddr_family = range.ncprange_family;
+ addr->ncpaddr_ip6addr = range.ncprange_ip6addr;
+ return 1;
+#endif
+ }
+
+ return 0;
+}
+
+void
+ncprange_init(struct ncprange *range)
+{
+ range->ncprange_family = AF_UNSPEC;
+}
+
+int
+ncprange_isset(const struct ncprange *range)
+{
+ return range->ncprange_family != AF_UNSPEC;
+}
+
+int
+ncprange_equal(const struct ncprange *range, const struct ncprange *cmp)
+{
+ if (range->ncprange_family != cmp->ncprange_family)
+ return 0;
+
+ switch (range->ncprange_family) {
+ case AF_INET:
+ if (range->ncprange_ip4addr.s_addr != cmp->ncprange_ip4addr.s_addr)
+ return 0;
+ return range->ncprange_ip4mask.s_addr == cmp->ncprange_ip4mask.s_addr;
+
+#ifndef NOINET6
+ case AF_INET6:
+ if (range->ncprange_ip6width != cmp->ncprange_ip6width)
+ return 0;
+ return !memcmp(&range->ncprange_ip6addr, &cmp->ncprange_ip6addr,
+ sizeof range->ncprange_ip6addr);
+#endif
+
+ case AF_UNSPEC:
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+ncprange_isdefault(const struct ncprange *range)
+{
+ switch (range->ncprange_family) {
+ case AF_INET:
+ if (range->ncprange_ip4addr.s_addr == INADDR_ANY)
+ return 1;
+ break;
+
+#ifndef NOINET6
+ case AF_INET6:
+ if (range->ncprange_ip6width == 0 &&
+ IN6_IS_ADDR_UNSPECIFIED(&range->ncprange_ip6addr))
+ return 1;
+ break;
+#endif
+ }
+
+ return 0;
+}
+
+void
+ncprange_setdefault(struct ncprange *range, int af)
+{
+ memset(range, '\0', sizeof *range);
+ range->ncprange_family = af;
+}
+
+int
+ncprange_contains(const struct ncprange *range, const struct ncpaddr *addr)
+{
+#ifndef NOINET6
+ const u_char masks[] = { 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
+ const u_char *addrp, *rangep;
+ int bits;
+#endif
+
+ if (range->ncprange_family != addr->ncpaddr_family)
+ return 0;
+
+ switch (range->ncprange_family) {
+ case AF_INET:
+ return !((addr->ncpaddr_ip4addr.s_addr ^ range->ncprange_ip4addr.s_addr) &
+ range->ncprange_ip4mask.s_addr);
+
+#ifndef NOINET6
+ case AF_INET6:
+ rangep = (const u_char *)range->ncprange_ip6addr.s6_addr;
+ addrp = (const u_char *)addr->ncpaddr_ip6addr.s6_addr;
+
+ for (bits = range->ncprange_ip6width; bits > 0; bits -= 8)
+ if ((*addrp++ ^ *rangep++) & masks[bits > 7 ? 7 : bits - 1])
+ return 0;
+
+ return 1;
+#endif
+ }
+
+ return 0;
+}
+
+int
+ncprange_containsip4(const struct ncprange *range, struct in_addr addr)
+{
+ switch (range->ncprange_family) {
+ case AF_INET:
+ return !((addr.s_addr ^ range->ncprange_ip4addr.s_addr) &
+ range->ncprange_ip4mask.s_addr);
+ }
+
+ return 0;
+}
+
+void
+ncprange_copy(struct ncprange *range, const struct ncprange *from)
+{
+ switch (from->ncprange_family) {
+ case AF_INET:
+ range->ncprange_family = AF_INET;
+ range->ncprange_ip4addr = from->ncprange_ip4addr;
+ range->ncprange_ip4mask = from->ncprange_ip4mask;
+ range->ncprange_ip4width = from->ncprange_ip4width;
+ break;
+
+#ifndef NOINET6
+ case AF_INET6:
+ range->ncprange_family = AF_INET6;
+ range->ncprange_ip6addr = from->ncprange_ip6addr;
+ range->ncprange_ip6width = from->ncprange_ip6width;
+ break;
+#endif
+
+ default:
+ range->ncprange_family = AF_UNSPEC;
+ }
+}
+
+void
+ncprange_set(struct ncprange *range, const struct ncpaddr *addr, int width)
+{
+ ncprange_sethost(range, addr);
+ ncprange_setwidth(range, width);
+}
+
+void
+ncprange_sethost(struct ncprange *range, const struct ncpaddr *from)
+{
+ switch (from->ncpaddr_family) {
+ case AF_INET:
+ range->ncprange_family = AF_INET;
+ range->ncprange_ip4addr = from->ncpaddr_ip4addr;
+ if (from->ncpaddr_ip4addr.s_addr == INADDR_ANY) {
+ range->ncprange_ip4mask.s_addr = INADDR_ANY;
+ range->ncprange_ip4width = 0;
+ } else {
+ range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
+ range->ncprange_ip4width = 32;
+ }
+ break;
+
+#ifndef NOINET6
+ case AF_INET6:
+ range->ncprange_family = AF_INET6;
+ range->ncprange_ip6addr = from->ncpaddr_ip6addr;
+ range->ncprange_ip6width = 128;
+ break;
+#endif
+
+ default:
+ range->ncprange_family = AF_UNSPEC;
+ }
+}
+
+int
+ncprange_ishost(const struct ncprange *range)
+{
+ switch (range->ncprange_family) {
+ case AF_INET:
+ return range->ncprange_ip4width == 32;
+#ifndef NOINET6
+ case AF_INET6:
+ return range->ncprange_ip6width == 128;
+#endif
+ }
+
+ return (0);
+}
+
+int
+ncprange_setwidth(struct ncprange *range, int width)
+{
+ switch (range->ncprange_family) {
+ case AF_INET:
+ if (width < 0 || width > 32)
+ break;
+ range->ncprange_ip4width = width;
+ range->ncprange_ip4mask = bits2mask4(width);
+ break;
+
+#ifndef NOINET6
+ case AF_INET6:
+ if (width < 0 || width > 128)
+ break;
+ range->ncprange_ip6width = width;
+ break;
+#endif
+
+ case AF_UNSPEC:
+ return 1;
+ }
+
+ return 0;
+}
+
+void
+ncprange_setip4host(struct ncprange *range, struct in_addr from)
+{
+ range->ncprange_family = AF_INET;
+ range->ncprange_ip4addr = from;
+ if (from.s_addr == INADDR_ANY) {
+ range->ncprange_ip4mask.s_addr = INADDR_ANY;
+ range->ncprange_ip4width = 0;
+ } else {
+ range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
+ range->ncprange_ip4width = 32;
+ }
+}
+
+void
+ncprange_setip4(struct ncprange *range, struct in_addr from, struct in_addr msk)
+{
+ range->ncprange_family = AF_INET;
+ range->ncprange_ip4addr = from;
+ range->ncprange_ip4mask = msk;
+ range->ncprange_ip4width = mask42bits(msk);
+}
+
+
+int
+ncprange_setip4mask(struct ncprange *range, struct in_addr mask)
+{
+ if (range->ncprange_family != AF_INET)
+ return 0;
+ range->ncprange_ip4mask = mask;
+ range->ncprange_ip4width = mask42bits(mask);
+ return 1;
+}
+
+void
+ncprange_setsa(struct ncprange *range, const struct sockaddr *host,
+ const struct sockaddr *mask)
+{
+ const struct sockaddr_in *host4 = (const struct sockaddr_in *)host;
+ const struct sockaddr_in *mask4 = (const struct sockaddr_in *)mask;
+#ifndef NOINET6
+ const struct sockaddr_in6 *host6 = (const struct sockaddr_in6 *)host;
+ const struct sockaddr_in6 *mask6 = (const struct sockaddr_in6 *)mask;
+#endif
+
+ switch (host->sa_family) {
+ case AF_INET:
+ range->ncprange_family = AF_INET;
+ range->ncprange_ip4addr = host4->sin_addr;
+ if (host4->sin_addr.s_addr == INADDR_ANY) {
+ range->ncprange_ip4mask.s_addr = INADDR_ANY;
+ range->ncprange_ip4width = 0;
+ } else if (mask4 && mask4->sin_family == AF_INET) {
+ range->ncprange_ip4mask.s_addr = mask4->sin_addr.s_addr;
+ range->ncprange_ip4width = mask42bits(mask4->sin_addr);
+ } else {
+ range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
+ range->ncprange_ip4width = 32;
+ }
+ break;
+
+#ifndef NOINET6
+ case AF_INET6:
+ range->ncprange_family = AF_INET6;
+ range->ncprange_ip6addr = host6->sin6_addr;
+ range->ncprange_ip6width = mask6 ? mask62bits(&mask6->sin6_addr) : 128;
+ break;
+#endif
+
+ default:
+ range->ncprange_family = AF_UNSPEC;
+ }
+}
+
+void
+ncprange_getsa(const struct ncprange *range, struct sockaddr_storage *host,
+ struct sockaddr_storage *mask)
+{
+ struct sockaddr_in *host4 = (struct sockaddr_in *)host;
+ struct sockaddr_in *mask4 = (struct sockaddr_in *)mask;
+#ifndef NOINET6
+ struct sockaddr_in6 *host6 = (struct sockaddr_in6 *)host;
+ struct sockaddr_in6 *mask6 = (struct sockaddr_in6 *)mask;
+#endif
+
+ memset(host, '\0', sizeof(*host));
+ if (mask)
+ memset(mask, '\0', sizeof(*mask));
+
+ switch (range->ncprange_family) {
+ case AF_INET:
+ host4->sin_family = AF_INET;
+ host4->sin_len = sizeof(*host4);
+ host4->sin_addr = range->ncprange_ip4addr;
+ if (mask4) {
+ mask4->sin_family = AF_INET;
+ mask4->sin_len = sizeof(*host4);
+ mask4->sin_addr = range->ncprange_ip4mask;
+ }
+ break;
+
+#ifndef NOINET6
+ case AF_INET6:
+ host6->sin6_family = AF_INET6;
+ host6->sin6_len = sizeof(*host6);
+ host6->sin6_addr = range->ncprange_ip6addr;
+ if (mask6) {
+ mask6->sin6_family = AF_INET6;
+ mask6->sin6_len = sizeof(*host6);
+ mask6->sin6_addr = bits2mask6(range->ncprange_ip6width);
+ }
+ break;
+#endif
+
+ default:
+ host->ss_family = AF_UNSPEC;
+ if (mask)
+ mask->ss_family = AF_UNSPEC;
+ break;
+ }
+}
+
+int
+ncprange_getaddr(const struct ncprange *range, struct ncpaddr *addr)
+{
+ switch (range->ncprange_family) {
+ case AF_INET:
+ addr->ncpaddr_family = AF_INET;
+ addr->ncpaddr_ip4addr = range->ncprange_ip4addr;
+ return 1;
+#ifndef NOINET6
+ case AF_INET6:
+ addr->ncpaddr_family = AF_INET6;
+ addr->ncpaddr_ip6addr = range->ncprange_ip6addr;
+ return 1;
+#endif
+ }
+
+ return 0;
+}
+
+int
+ncprange_getip4addr(const struct ncprange *range, struct in_addr *addr)
+{
+ if (range->ncprange_family != AF_INET)
+ return 0;
+
+ *addr = range->ncprange_ip4addr;
+ return 1;
+}
+
+int
+ncprange_getip4mask(const struct ncprange *range, struct in_addr *mask)
+{
+ switch (range->ncprange_family) {
+ case AF_INET:
+ *mask = range->ncprange_ip4mask;
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+ncprange_getwidth(const struct ncprange *range, int *width)
+{
+ switch (range->ncprange_family) {
+ case AF_INET:
+ *width = range->ncprange_ip4width;
+ return 1;
+#ifndef NOINET6
+ case AF_INET6:
+ *width = range->ncprange_ip6width;
+ return 1;
+#endif
+ }
+
+ return 0;
+}
+
+const char *
+ncprange_ntoa(const struct ncprange *range)
+{
+ char *res;
+ struct ncpaddr addr;
+ int len;
+
+ if (!ncprange_getaddr(range, &addr))
+ return "<AF_UNSPEC>";
+
+ res = ncpaddr_ntowa(&addr);
+ len = strlen(res);
+ if (len >= NCP_ASCIIBUFFERSIZE - 1)
+ return res;
+
+ switch (range->ncprange_family) {
+ case AF_INET:
+ if (range->ncprange_ip4width == -1) {
+ /* A non-contiguous mask */
+ for (; len >= 3; res[len -= 2] = '\0')
+ if (strcmp(res + len - 2, ".0"))
+ break;
+ snprintf(res + len, sizeof res - len, "&0x%08lx",
+ (unsigned long)ntohl(range->ncprange_ip4mask.s_addr));
+ } else if (range->ncprange_ip4width < 32)
+ snprintf(res + len, sizeof res - len, "/%d", range->ncprange_ip4width);
+
+ return res;
+
+#ifndef NOINET6
+ case AF_INET6:
+ if (range->ncprange_ip6width != 128)
+ snprintf(res + len, sizeof res - len, "/%d", range->ncprange_ip6width);
+
+ return res;
+#endif
+ }
+
+ return "<AF_UNSPEC>";
+}
+
+#ifndef NOINET6
+int
+ncprange_scopeid(const struct ncprange *range)
+{
+ const struct in6_addr *sin6;
+ int scopeid = -1;
+
+ if (range->ncprange_family == AF_INET6) {
+ sin6 = &range->ncprange_ip6addr;
+ if (IN6_IS_ADDR_LINKLOCAL(sin6) || IN6_IS_ADDR_MC_LINKLOCAL(sin6))
+ if ((scopeid = ntohs(*(const u_short *)&sin6->s6_addr[2])) == 0)
+ scopeid = -1;
+ }
+
+ return scopeid;
+}
+#endif
+
+int
+ncprange_aton(struct ncprange *range, struct ncp *ncp, const char *data)
+{
+ int bits, len;
+ char *wp;
+ const char *cp;
+ char *s;
+
+ len = strcspn(data, "/");
+
+ if (ncp && strncasecmp(data, "HISADDR", len) == 0) {
+ range->ncprange_family = AF_INET;
+ range->ncprange_ip4addr = ncp->ipcp.peer_ip;
+ range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
+ range->ncprange_ip4width = 32;
+ return 1;
+#ifndef NOINET6
+ } else if (ncp && strncasecmp(data, "HISADDR6", len) == 0) {
+ ncprange_sethost(range, &ncp->ipv6cp.hisaddr);
+ return 1;
+#endif
+ } else if (ncp && strncasecmp(data, "MYADDR", len) == 0) {
+ range->ncprange_family = AF_INET;
+ range->ncprange_ip4addr = ncp->ipcp.my_ip;
+ range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
+ range->ncprange_ip4width = 32;
+ return 1;
+#ifndef NOINET6
+ } else if (ncp && strncasecmp(data, "MYADDR6", len) == 0) {
+ ncprange_sethost(range, &ncp->ipv6cp.myaddr);
+ return 1;
+#endif
+ } else if (ncp && strncasecmp(data, "DNS0", len) == 0) {
+ range->ncprange_family = AF_INET;
+ range->ncprange_ip4addr = ncp->ipcp.ns.dns[0];
+ range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
+ range->ncprange_ip4width = 32;
+ return 1;
+ } else if (ncp && strncasecmp(data, "DNS1", len) == 0) {
+ range->ncprange_family = AF_INET;
+ range->ncprange_ip4addr = ncp->ipcp.ns.dns[1];
+ range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
+ range->ncprange_ip4width = 32;
+ return 1;
+ }
+
+ s = (char *)alloca(len + 1);
+ strncpy(s, data, len);
+ s[len] = '\0';
+ bits = -1;
+
+ if (data[len] != '\0') {
+ bits = strtol(data + len + 1, &wp, 0);
+ if (*wp || wp == data + len + 1 || bits < 0 || bits > 128) {
+ log_Printf(LogWARN, "ncprange_aton: bad mask width.\n");
+ return 0;
+ }
+ }
+
+ if ((cp = strchr(data, ':')) == NULL) {
+ range->ncprange_family = AF_INET;
+
+ range->ncprange_ip4addr = GetIpAddr(s);
+
+ if (range->ncprange_ip4addr.s_addr == INADDR_NONE) {
+ log_Printf(LogWARN, "ncprange_aton: %s: Bad address\n", s);
+ return 0;
+ }
+
+ if (range->ncprange_ip4addr.s_addr == INADDR_ANY) {
+ range->ncprange_ip4mask.s_addr = INADDR_ANY;
+ range->ncprange_ip4width = 0;
+ } else if (bits == -1) {
+ range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
+ range->ncprange_ip4width = 32;
+ } else if (bits > 32) {
+ log_Printf(LogWARN, "ncprange_aton: bad mask width.\n");
+ return 0;
+ } else {
+ range->ncprange_ip4mask = bits2mask4(bits);
+ range->ncprange_ip4width = bits;
+ }
+
+ return 1;
+#ifndef NOINET6
+ } else if (strchr(cp + 1, ':') != NULL) {
+ range->ncprange_family = AF_INET6;
+
+ if (inet_pton(AF_INET6, s, &range->ncprange_ip6addr) != 1) {
+ log_Printf(LogWARN, "ncprange_aton: %s: Bad address\n", s);
+ return 0;
+ }
+
+ if (IN6_IS_ADDR_UNSPECIFIED(&range->ncprange_ip6addr))
+ range->ncprange_ip6width = 0;
+ else
+ range->ncprange_ip6width = (bits == -1) ? 128 : bits;
+ return 1;
+#endif
+ }
+
+ return 0;
+}
diff --git a/usr.sbin/ppp/ncpaddr.h b/usr.sbin/ppp/ncpaddr.h
new file mode 100644
index 0000000..8c6b886
--- /dev/null
+++ b/usr.sbin/ppp/ncpaddr.h
@@ -0,0 +1,109 @@
+/*-
+ * Copyright (c) 2001 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * These structures should be treated as opaque.
+ */
+struct ncprange {
+ sa_family_t ncprange_family;
+ union {
+ struct {
+ struct in_addr ipaddr;
+ struct in_addr mask;
+ int width;
+ } ip4;
+#ifndef NOINET6
+ struct {
+ struct in6_addr ipaddr;
+ int width;
+ } ip6;
+#endif
+ } u;
+};
+
+struct ncpaddr {
+ sa_family_t ncpaddr_family;
+ union {
+ struct in_addr ip4addr;
+#ifndef NOINET6
+ struct in6_addr ip6addr;
+#endif
+ } u;
+};
+
+struct ncp;
+
+extern void ncpaddr_init(struct ncpaddr *);
+extern int ncpaddr_isset(const struct ncpaddr *);
+extern int ncpaddr_isdefault(const struct ncpaddr *);
+extern int ncpaddr_equal(const struct ncpaddr *, const struct ncpaddr *);
+extern void ncpaddr_copy(struct ncpaddr *, const struct ncpaddr *);
+extern void ncpaddr_setip4addr(struct ncpaddr *, u_int32_t);
+extern int ncpaddr_getip4addr(const struct ncpaddr *, u_int32_t *);
+extern void ncpaddr_setip4(struct ncpaddr *, struct in_addr);
+extern int ncpaddr_getip4(const struct ncpaddr *, struct in_addr *);
+#ifndef NOINET6
+extern void ncpaddr_setip6(struct ncpaddr *, const struct in6_addr *);
+extern int ncpaddr_getip6(const struct ncpaddr *, struct in6_addr *);
+#endif
+extern void ncpaddr_getsa(const struct ncpaddr *, struct sockaddr_storage *);
+extern void ncpaddr_setsa(struct ncpaddr *, const struct sockaddr *);
+extern const char *ncpaddr_ntoa(const struct ncpaddr *);
+extern int ncpaddr_aton(struct ncpaddr *, struct ncp *, const char *);
+
+extern void ncprange_init(struct ncprange *);
+extern int ncprange_isset(const struct ncprange *);
+extern int ncprange_equal(const struct ncprange *, const struct ncprange *);
+extern int ncprange_isdefault(const struct ncprange *);
+extern void ncprange_setdefault(struct ncprange *, int);
+extern int ncprange_contains(const struct ncprange *, const struct ncpaddr *);
+extern int ncprange_containsip4(const struct ncprange *, struct in_addr);
+extern void ncprange_copy(struct ncprange *, const struct ncprange *);
+extern void ncprange_set(struct ncprange *, const struct ncpaddr *, int);
+extern void ncprange_sethost(struct ncprange *, const struct ncpaddr *);
+extern int ncprange_ishost(const struct ncprange *);
+extern int ncprange_setwidth(struct ncprange *, int);
+extern void ncprange_setip4(struct ncprange *, struct in_addr, struct in_addr);
+extern void ncprange_setip4host(struct ncprange *, struct in_addr);
+extern int ncprange_setip4mask(struct ncprange *, struct in_addr);
+extern void ncprange_setsa(struct ncprange *, const struct sockaddr *,
+ const struct sockaddr *);
+extern void ncprange_getsa(const struct ncprange *, struct sockaddr_storage *,
+ struct sockaddr_storage *);
+extern int ncprange_getaddr(const struct ncprange *, struct ncpaddr *);
+extern int ncprange_getip4addr(const struct ncprange *, struct in_addr *);
+extern int ncprange_getip4mask(const struct ncprange *, struct in_addr *);
+extern int ncprange_getwidth(const struct ncprange *, int *);
+extern const char *ncprange_ntoa(const struct ncprange *);
+#ifndef NOINET6
+extern int ncprange_scopeid(const struct ncprange *);
+#endif
+extern int ncprange_aton(struct ncprange *, struct ncp *, const char *);
+
+#define ncpaddr_family(a) ((a)->ncpaddr_family)
+#define ncprange_family(r) ((r)->ncprange_family)
diff --git a/usr.sbin/ppp/netgraph.c b/usr.sbin/ppp/netgraph.c
new file mode 100644
index 0000000..1ada596d
--- /dev/null
+++ b/usr.sbin/ppp/netgraph.c
@@ -0,0 +1,741 @@
+/*-
+ * Copyright (c) 2000 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <netgraph.h>
+#include <net/ethernet.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netgraph/ng_ether.h>
+#include <netgraph/ng_message.h>
+#include <netgraph/ng_socket.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <sys/fcntl.h>
+#include <sys/uio.h>
+#include <termios.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#include "layer.h"
+#include "defs.h"
+#include "mbuf.h"
+#include "log.h"
+#include "timer.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "throughput.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "async.h"
+#include "descriptor.h"
+#include "physical.h"
+#include "main.h"
+#include "mp.h"
+#include "chat.h"
+#include "auth.h"
+#include "chap.h"
+#include "cbcp.h"
+#include "datalink.h"
+#include "slcompress.h"
+#include "iplist.h"
+#include "ncpaddr.h"
+#include "ipcp.h"
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "filter.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "bundle.h"
+#include "id.h"
+#include "netgraph.h"
+
+
+struct ngdevice {
+ struct device dev; /* What struct physical knows about */
+ int cs; /* Control socket */
+ char hook[NG_HOOKLEN + 1]; /* Our socket node hook */
+};
+
+#define device2ng(d) ((d)->type == NG_DEVICE ? (struct ngdevice *)d : NULL)
+#define NG_MSGBUFSZ 4096
+#define NETGRAPH_PREFIX "netgraph:"
+
+int
+ng_DeviceSize(void)
+{
+ return sizeof(struct ngdevice);
+}
+
+static int
+ng_MessageOut(struct ngdevice *dev, struct physical *p, const char *data)
+{
+ char path[NG_PATHLEN + 1];
+ int len, pos, dpos;
+ char *fmt;
+
+ /*
+ * We expect a node path, one or more spaces, a command, one or more
+ * spaces and an ascii netgraph structure.
+ */
+ data += strspn(data, " \t");
+ len = strcspn(data, " \t");
+ if (len >= sizeof path) {
+ log_Printf(LogWARN, "%s: %.*s: Node path too long\n",
+ dev->dev.name, len, data);
+ return 0;
+ }
+ memcpy(path, data, len);
+ path[len] = '\0';
+ data += len;
+
+ data += strspn(data, " \t");
+ len = strcspn(data, " \t");
+ for (pos = len; pos >= 0; pos--)
+ if (data[pos] == '%')
+ len++;
+ if ((fmt = alloca(len + 4)) == NULL) {
+ log_Printf(LogWARN, "%s: alloca(%d) failure... %s\n",
+ dev->dev.name, len + 4, strerror(errno));
+ return 0;
+ }
+
+ /*
+ * This is probably a waste of time, but we really don't want to end
+ * up stuffing unexpected % escapes into the kernel....
+ */
+ for (pos = dpos = 0; pos < len;) {
+ if (data[dpos] == '%')
+ fmt[pos++] = '%';
+ fmt[pos++] = data[dpos++];
+ }
+ strcpy(fmt + pos, " %s");
+ data += dpos;
+
+ data += strspn(data, " \t");
+ if (NgSendAsciiMsg(dev->cs, path, fmt, data) < 0) {
+ log_Printf(LogDEBUG, "%s: NgSendAsciiMsg (to %s): \"%s\", \"%s\": %s\n",
+ dev->dev.name, path, fmt, data, strerror(errno));
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * Get a netgraph message
+ */
+static ssize_t
+ng_MessageIn(struct physical *p, char *buf, size_t sz)
+{
+ char msgbuf[sizeof(struct ng_mesg) * 2 + NG_MSGBUFSZ];
+ struct ngdevice *dev = device2ng(p->handler);
+ struct ng_mesg *rep = (struct ng_mesg *)msgbuf;
+ char path[NG_PATHLEN + 1];
+ int len;
+
+#ifdef BROKEN_SELECT
+ struct timeval t;
+ fd_set *r;
+ int ret;
+
+ if (dev->cs < 0)
+ return 0;
+
+ if ((r = mkfdset()) == NULL) {
+ log_Printf(LogERROR, "DoLoop: Cannot create fd_set\n");
+ return -1;
+ }
+ zerofdset(r);
+ FD_SET(dev->cs, r);
+ t.tv_sec = t.tv_usec = 0;
+ ret = select(dev->cs + 1, r, NULL, NULL, &t);
+ free(r);
+
+ if (ret <= 0)
+ return 0;
+#endif
+
+ if (NgRecvAsciiMsg(dev->cs, rep, sizeof msgbuf, path)) {
+ log_Printf(LogWARN, "%s: NgRecvAsciiMsg: %s\n",
+ dev->dev.name, strerror(errno));
+ return -1;
+ }
+
+ /* XXX: Should we check rep->header.version ? */
+
+ if (sz == 0)
+ log_Printf(LogWARN, "%s: Unexpected message: %s\n", dev->dev.name,
+ rep->header.cmdstr);
+ else {
+ log_Printf(LogDEBUG, "%s: Received message: %s\n", dev->dev.name,
+ rep->header.cmdstr);
+ len = strlen(rep->header.cmdstr);
+ if (sz > len)
+ sz = len;
+ memcpy(buf, rep->header.cmdstr, sz);
+ }
+
+ return sz;
+}
+
+static ssize_t
+ng_Write(struct physical *p, const void *v, size_t n)
+{
+ struct ngdevice *dev = device2ng(p->handler);
+
+ switch (p->dl->state) {
+ case DATALINK_DIAL:
+ case DATALINK_LOGIN:
+ return ng_MessageOut(dev, p, v) ? n : -1;
+ }
+ return NgSendData(p->fd, dev->hook, v, n) == -1 ? -1 : n;
+}
+
+static ssize_t
+ng_Read(struct physical *p, void *v, size_t n)
+{
+ char hook[NG_HOOKLEN + 1];
+
+log_Printf(LogDEBUG, "ng_Read\n");
+ switch (p->dl->state) {
+ case DATALINK_DIAL:
+ case DATALINK_LOGIN:
+ return ng_MessageIn(p, v, n);
+ }
+
+ return NgRecvData(p->fd, v, n, hook);
+}
+
+static int
+ng_RemoveFromSet(struct physical *p, fd_set *r, fd_set *w, fd_set *e)
+{
+ struct ngdevice *dev = device2ng(p->handler);
+ int result;
+
+ if (r && dev->cs >= 0 && FD_ISSET(dev->cs, r)) {
+ FD_CLR(dev->cs, r);
+ log_Printf(LogTIMER, "%s: fdunset(ctrl) %d\n", p->link.name, dev->cs);
+ result = 1;
+ } else
+ result = 0;
+
+ /* Careful... physical_RemoveFromSet() called us ! */
+
+ p->handler->removefromset = NULL;
+ result += physical_RemoveFromSet(p, r, w, e);
+ p->handler->removefromset = ng_RemoveFromSet;
+
+ return result;
+}
+
+static void
+ng_Free(struct physical *p)
+{
+ struct ngdevice *dev = device2ng(p->handler);
+
+ physical_SetDescriptor(p);
+ if (dev->cs != -1)
+ close(dev->cs);
+ free(dev);
+}
+
+static void
+ng_device2iov(struct device *d, struct iovec *iov, int *niov,
+ int maxiov, int *auxfd, int *nauxfd)
+{
+ struct ngdevice *dev = device2ng(d);
+ int sz = physical_MaxDeviceSize();
+
+ iov[*niov].iov_base = realloc(d, sz);
+ if (iov[*niov].iov_base == NULL) {
+ log_Printf(LogALERT, "Failed to allocate memory: %d\n", sz);
+ AbortProgram(EX_OSERR);
+ }
+ iov[*niov].iov_len = sz;
+ (*niov)++;
+
+ *auxfd = dev->cs;
+ (*nauxfd)++;
+}
+
+static const struct device basengdevice = {
+ NG_DEVICE,
+ "netgraph",
+ 0,
+ { CD_REQUIRED, DEF_NGCDDELAY },
+ NULL,
+ ng_RemoveFromSet,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ ng_Free,
+ ng_Read,
+ ng_Write,
+ ng_device2iov,
+ NULL,
+ NULL,
+ NULL
+};
+
+struct device *
+ng_iov2device(int type, struct physical *p, struct iovec *iov, int *niov,
+ int maxiov, int *auxfd, int *nauxfd)
+{
+ if (type == NG_DEVICE) {
+ struct ngdevice *dev = (struct ngdevice *)iov[(*niov)++].iov_base;
+
+ dev = realloc(dev, sizeof *dev); /* Reduce to the correct size */
+ if (dev == NULL) {
+ log_Printf(LogALERT, "Failed to allocate memory: %d\n",
+ (int)(sizeof *dev));
+ AbortProgram(EX_OSERR);
+ }
+
+ if (*nauxfd) {
+ dev->cs = *auxfd;
+ (*nauxfd)--;
+ } else
+ dev->cs = -1;
+
+ /* Refresh function pointers etc */
+ memcpy(&dev->dev, &basengdevice, sizeof dev->dev);
+
+ /* XXX: Are netgraph always synchronous ? */
+ physical_SetupStack(p, dev->dev.name, PHYSICAL_FORCE_SYNCNOACF);
+ return &dev->dev;
+ }
+
+ return NULL;
+}
+
+static int
+ng_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n)
+{
+ struct physical *p = descriptor2physical(d);
+ struct ngdevice *dev = device2ng(p->handler);
+ int result;
+
+ switch (p->dl->state) {
+ case DATALINK_DIAL:
+ case DATALINK_LOGIN:
+ if (r) {
+ FD_SET(dev->cs, r);
+ log_Printf(LogTIMER, "%s(ctrl): fdset(r) %d\n", p->link.name, dev->cs);
+ result = 1;
+ } else
+ result = 0;
+ break;
+
+ default:
+ result = physical_doUpdateSet(d, r, w, e, n, 0);
+ break;
+ }
+
+ return result;
+}
+
+static int
+ng_IsSet(struct fdescriptor *d, const fd_set *fdset)
+{
+ struct physical *p = descriptor2physical(d);
+ struct ngdevice *dev = device2ng(p->handler);
+ int result;
+
+ result = dev->cs >= 0 && FD_ISSET(dev->cs, fdset);
+ result += physical_IsSet(d, fdset);
+
+ return result;
+}
+
+static void
+ng_DescriptorRead(struct fdescriptor *d, struct bundle *bundle,
+ const fd_set *fdset)
+{
+ struct physical *p = descriptor2physical(d);
+ struct ngdevice *dev = device2ng(p->handler);
+
+ if (dev->cs >= 0 && FD_ISSET(dev->cs, fdset))
+ ng_MessageIn(p, NULL, 0);
+
+ if (physical_IsSet(d, fdset))
+ physical_DescriptorRead(d, bundle, fdset);
+}
+
+static struct device *
+ng_Abandon(struct ngdevice *dev, struct physical *p)
+{
+ /* Abandon our node construction */
+ close(dev->cs);
+ close(p->fd);
+ p->fd = -2; /* Nobody else need try.. */
+ free(dev);
+
+ return NULL;
+}
+
+
+/*
+ * Populate the ``word'' (of size ``sz'') named ``what'' from ``from''
+ * ending with any character from ``sep''. Point ``endp'' at the next
+ * word.
+ */
+
+#define GETSEGMENT(what, from, sep, endp) \
+ getsegment(#what, (what), sizeof(what), from, sep, endp)
+
+static int
+getsegment(const char *what, char *word, size_t sz, const char *from,
+ const char *sep, const char **endp)
+{
+ int len;
+
+ if ((len = strcspn(from, sep)) == 0) {
+ log_Printf(LogWARN, "%s name should not be empty !\n", what);
+ return 0;
+ }
+
+ if (len >= sz) {
+ log_Printf(LogWARN, "%s name too long, max %d !\n", what, sz - 1);
+ return 0;
+ }
+
+ strncpy(word, from, len);
+ word[len] = '\0';
+
+ *endp = from + len;
+ *endp += strspn(*endp, sep);
+
+ return 1;
+}
+
+struct device *
+ng_Create(struct physical *p)
+{
+ struct sockaddr_ng ngsock;
+ u_char rbuf[2048];
+ struct sockaddr *sock = (struct sockaddr *)&ngsock;
+ const struct hooklist *hlist;
+ const struct nodeinfo *ninfo;
+ const struct linkinfo *nlink;
+ struct ngdevice *dev;
+ struct ng_mesg *resp;
+ struct ngm_mkpeer mkp;
+ struct ngm_connect ngc;
+ const char *devp, *endp;
+ char lasthook[NG_HOOKLEN + 1];
+ char hook[NG_HOOKLEN + 1];
+ char nodetype[NG_TYPELEN + NG_NODELEN + 2];
+ char modname[NG_TYPELEN + 4];
+ char path[NG_PATHLEN + 1];
+ char *nodename;
+ int len, sz, done, f;
+
+ dev = NULL;
+ if (p->fd < 0 && !strncasecmp(p->name.full, NETGRAPH_PREFIX,
+ sizeof NETGRAPH_PREFIX - 1)) {
+ p->fd--; /* We own the device - change fd */
+
+ if ((dev = malloc(sizeof *dev)) == NULL)
+ return NULL;
+
+ loadmodules(LOAD_VERBOSLY, "netgraph", "ng_socket", NULL);
+
+ /* Create a socket node */
+ if (ID0NgMkSockNode(NULL, &dev->cs, &p->fd) == -1) {
+ log_Printf(LogWARN, "Cannot create netgraph socket node: %s\n",
+ strerror(errno));
+ free(dev);
+ p->fd = -2;
+ return NULL;
+ }
+
+ devp = p->name.full + sizeof NETGRAPH_PREFIX - 1;
+ *lasthook = *path = '\0';
+ log_Printf(LogDEBUG, "%s: Opening netgraph device \"%s\"\n",
+ p->link.name, devp);
+ done = 0;
+
+ while (*devp != '\0' && !done) {
+ if (*devp != '[') {
+ if (*lasthook == '\0') {
+ log_Printf(LogWARN, "%s: Netgraph devices must start with"
+ " [nodetype:nodename]\n", p->link.name);
+ return ng_Abandon(dev, p);
+ }
+
+ /* Get the hook name of the new node */
+ if (!GETSEGMENT(hook, devp, ".[", &endp))
+ return ng_Abandon(dev, p);
+ log_Printf(LogDEBUG, "%s: Got hook \"%s\"\n", p->link.name, hook);
+ devp = endp;
+ if (*devp == '\0') {
+ log_Printf(LogWARN, "%s: Netgraph device must not end with a second"
+ " hook\n", p->link.name);
+ return ng_Abandon(dev, p);
+ }
+ if (devp[-1] != '[') {
+ log_Printf(LogWARN, "%s: Expected a [nodetype:nodename] at device"
+ " pos %d\n", p->link.name, devp - p->link.name - 1);
+ return ng_Abandon(dev, p);
+ }
+ } else {
+ /* Use lasthook as the hook name */
+ strcpy(hook, lasthook);
+ devp++;
+ }
+
+ /* We've got ``lasthook'' and ``hook'', get the node type */
+ if (!GETSEGMENT(nodetype, devp, "]", &endp))
+ return ng_Abandon(dev, p);
+ log_Printf(LogDEBUG, "%s: Got node \"%s\"\n", p->link.name, nodetype);
+
+ if ((nodename = strchr(nodetype, ':')) != NULL) {
+ *nodename++ = '\0';
+ if (*nodename == '\0' && *nodetype == '\0') {
+ log_Printf(LogWARN, "%s: Empty [nodetype:nodename] at device"
+ " pos %d\n", p->link.name, devp - p->link.name - 1);
+ return ng_Abandon(dev, p);
+ }
+ }
+
+ /* Ignore optional colons after nodes */
+ devp = *endp == ':' ? endp + 1 : endp;
+ if (*devp == '.')
+ devp++;
+
+ if (*lasthook == '\0') {
+ /* This is the first node in the chain */
+ if (nodename == NULL || *nodename == '\0') {
+ log_Printf(LogWARN, "%s: %s: No initial device nodename\n",
+ p->link.name, devp);
+ return ng_Abandon(dev, p);
+ }
+
+ if (*nodetype != '\0') {
+ /* Attempt to load the module */
+ snprintf(modname, sizeof modname, "ng_%s", nodetype);
+ log_Printf(LogDEBUG, "%s: Attempting to load %s.ko\n",
+ p->link.name, modname);
+ loadmodules(LOAD_QUIETLY, modname, NULL);
+ }
+
+ snprintf(path, sizeof path, "%s:", nodename);
+ /* XXX: If we have a node type, ensure it's correct */
+ } else {
+ /*
+ * Ask for a list of hooks attached to the previous node. If we
+ * find the one we're interested in, and if it's connected to a
+ * node of the right type using the correct hook, use that.
+ * If we find the hook connected to something else, fail.
+ * If we find no match, mkpeer the new node.
+ */
+ if (*nodetype == '\0') {
+ log_Printf(LogWARN, "%s: Nodetype missing at device offset %d\n",
+ p->link.name,
+ devp - p->name.full + sizeof NETGRAPH_PREFIX - 1);
+ return ng_Abandon(dev, p);
+ }
+
+ /* Get a list of node hooks */
+ if (NgSendMsg(dev->cs, path, NGM_GENERIC_COOKIE, NGM_LISTHOOKS,
+ NULL, 0) < 0) {
+ log_Printf(LogWARN, "%s: %s Cannot send a LISTHOOOKS message: %s\n",
+ p->link.name, path, strerror(errno));
+ return ng_Abandon(dev, p);
+ }
+
+ /* Get our list back */
+ resp = (struct ng_mesg *)rbuf;
+ if (NgRecvMsg(dev->cs, resp, sizeof rbuf, NULL) <= 0) {
+ log_Printf(LogWARN, "%s: Cannot get netgraph response: %s\n",
+ p->link.name, strerror(errno));
+ return ng_Abandon(dev, p);
+ }
+
+ hlist = (const struct hooklist *)resp->data;
+ ninfo = &hlist->nodeinfo;
+
+ log_Printf(LogDEBUG, "List of netgraph node ``%s'' (id %x) hooks:\n",
+ path, ninfo->id);
+
+ /* look for a hook already attached. */
+ for (f = 0; f < ninfo->hooks; f++) {
+ nlink = &hlist->link[f];
+
+ log_Printf(LogDEBUG, " Found %s -> %s (type %s)\n", nlink->ourhook,
+ nlink->peerhook, nlink->nodeinfo.type);
+
+ if (!strcmp(nlink->ourhook, lasthook)) {
+ if (strcmp(nlink->peerhook, hook) ||
+ strcmp(nlink->nodeinfo.type, nodetype)) {
+ log_Printf(LogWARN, "%s: hook %s:%s is already in use\n",
+ p->link.name, nlink->ourhook, path);
+ return ng_Abandon(dev, p);
+ }
+ /* The node is already hooked up nicely.... reuse it */
+ break;
+ }
+ }
+
+ if (f == ninfo->hooks) {
+ /* Attempt to load the module */
+ snprintf(modname, sizeof modname, "ng_%s", nodetype);
+ log_Printf(LogDEBUG, "%s: Attempting to load %s.ko\n",
+ p->link.name, modname);
+ loadmodules(LOAD_QUIETLY, modname, NULL);
+
+ /* Create (mkpeer) the new node */
+
+ snprintf(mkp.type, sizeof mkp.type, "%s", nodetype);
+ snprintf(mkp.ourhook, sizeof mkp.ourhook, "%s", lasthook);
+ snprintf(mkp.peerhook, sizeof mkp.peerhook, "%s", hook);
+
+ log_Printf(LogDEBUG, "%s: Doing MKPEER %s%s -> %s (type %s)\n",
+ p->link.name, path, mkp.ourhook, mkp.peerhook, nodetype);
+
+ if (NgSendMsg(dev->cs, path, NGM_GENERIC_COOKIE,
+ NGM_MKPEER, &mkp, sizeof mkp) < 0) {
+ log_Printf(LogWARN, "%s Cannot create %s netgraph node: %s\n",
+ path, nodetype, strerror(errno));
+ return ng_Abandon(dev, p);
+ }
+ }
+ len = strlen(path);
+ snprintf(path + len, sizeof path - len, "%s%s",
+ path[len - 1] == ':' ? "" : ".", lasthook);
+ }
+
+ /* Get a list of node hooks */
+ if (NgSendMsg(dev->cs, path, NGM_GENERIC_COOKIE, NGM_LISTHOOKS,
+ NULL, 0) < 0) {
+ log_Printf(LogWARN, "%s: %s Cannot send a LISTHOOOKS message: %s\n",
+ p->link.name, path, strerror(errno));
+ return ng_Abandon(dev, p);
+ }
+
+ /* Get our list back */
+ resp = (struct ng_mesg *)rbuf;
+ if (NgRecvMsg(dev->cs, resp, sizeof rbuf, NULL) <= 0) {
+ log_Printf(LogWARN, "%s: Cannot get netgraph response: %s\n",
+ p->link.name, strerror(errno));
+ return ng_Abandon(dev, p);
+ }
+
+ hlist = (const struct hooklist *)resp->data;
+ ninfo = &hlist->nodeinfo;
+
+ if (*lasthook != '\0' && nodename != NULL && *nodename != '\0' &&
+ strcmp(ninfo->name, nodename) &&
+ NgNameNode(dev->cs, path, "%s", nodename) < 0) {
+ log_Printf(LogWARN, "%s: %s: Cannot name netgraph node: %s\n",
+ p->link.name, path, strerror(errno));
+ return ng_Abandon(dev, p);
+ }
+
+ if (!GETSEGMENT(lasthook, devp, " \t.[", &endp))
+ return ng_Abandon(dev, p);
+ log_Printf(LogDEBUG, "%s: Got hook \"%s\"\n", p->link.name, lasthook);
+
+ len = strlen(lasthook);
+ done = strchr(" \t", devp[len]) ? 1 : 0;
+ devp = endp;
+
+ if (*devp != '\0') {
+ if (devp[-1] == '[')
+ devp--;
+ } /* else should moan about devp[-1] being '[' ? */
+ }
+
+ snprintf(dev->hook, sizeof dev->hook, "%s", lasthook);
+
+ /* Connect the node to our socket node */
+ snprintf(ngc.path, sizeof ngc.path, "%s", path);
+ snprintf(ngc.ourhook, sizeof ngc.ourhook, "%s", dev->hook);
+ memcpy(ngc.peerhook, ngc.ourhook, sizeof ngc.peerhook);
+
+ log_Printf(LogDEBUG, "Connecting netgraph socket .:%s -> %s.%s\n",
+ ngc.ourhook, ngc.path, ngc.peerhook);
+ if (NgSendMsg(dev->cs, ".:", NGM_GENERIC_COOKIE,
+ NGM_CONNECT, &ngc, sizeof ngc) < 0) {
+ log_Printf(LogWARN, "Cannot connect %s and socket netgraph "
+ "nodes: %s\n", path, strerror(errno));
+ return ng_Abandon(dev, p);
+ }
+
+ /* Hook things up so that we monitor dev->cs */
+ p->desc.UpdateSet = ng_UpdateSet;
+ p->desc.IsSet = ng_IsSet;
+ p->desc.Read = ng_DescriptorRead;
+
+ memcpy(&dev->dev, &basengdevice, sizeof dev->dev);
+
+ } else {
+ /* See if we're a netgraph socket */
+
+ sz = sizeof ngsock;
+ if (getsockname(p->fd, sock, &sz) != -1 && sock->sa_family == AF_NETGRAPH) {
+ /*
+ * It's a netgraph node... We can't determine hook names etc, so we
+ * stay pretty impartial....
+ */
+ log_Printf(LogPHASE, "%s: Link is a netgraph node\n", p->link.name);
+
+ if ((dev = malloc(sizeof *dev)) == NULL) {
+ log_Printf(LogWARN, "%s: Cannot allocate an ether device: %s\n",
+ p->link.name, strerror(errno));
+ return NULL;
+ }
+
+ memcpy(&dev->dev, &basengdevice, sizeof dev->dev);
+ dev->cs = -1;
+ *dev->hook = '\0';
+ }
+ }
+
+ if (dev) {
+ physical_SetupStack(p, dev->dev.name, PHYSICAL_FORCE_SYNCNOACF);
+ return &dev->dev;
+ }
+
+ return NULL;
+}
diff --git a/usr.sbin/ppp/netgraph.h b/usr.sbin/ppp/netgraph.h
new file mode 100644
index 0000000..ffa012a
--- /dev/null
+++ b/usr.sbin/ppp/netgraph.h
@@ -0,0 +1,37 @@
+/*-
+ * Copyright (c) 2000 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct physical;
+struct device;
+
+#define DEF_NGCDDELAY 5 /* Default ``set cd'' value */
+
+extern struct device *ng_Create(struct physical *);
+extern struct device *ng_iov2device(int, struct physical *, struct iovec *,
+ int *, int, int *, int *);
+extern int ng_DeviceSize(void);
diff --git a/usr.sbin/ppp/pap.c b/usr.sbin/ppp/pap.c
new file mode 100644
index 0000000..8a1d112
--- /dev/null
+++ b/usr.sbin/ppp/pap.c
@@ -0,0 +1,303 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <stdlib.h>
+#include <string.h> /* strlen/memcpy */
+#include <termios.h>
+
+#include "layer.h"
+#include "mbuf.h"
+#include "log.h"
+#include "defs.h"
+#include "timer.h"
+#include "fsm.h"
+#include "auth.h"
+#include "pap.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "lcp.h"
+#include "proto.h"
+#include "async.h"
+#include "throughput.h"
+#include "ccp.h"
+#include "link.h"
+#include "descriptor.h"
+#include "physical.h"
+#include "iplist.h"
+#include "slcompress.h"
+#include "ncpaddr.h"
+#include "ipcp.h"
+#include "filter.h"
+#include "mp.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+#include "chat.h"
+#include "chap.h"
+#include "cbcp.h"
+#include "datalink.h"
+
+static const char * const papcodes[] = {
+ "???", "REQUEST", "SUCCESS", "FAILURE"
+};
+#define MAXPAPCODE (sizeof papcodes / sizeof papcodes[0] - 1)
+
+static void
+pap_Req(struct authinfo *authp)
+{
+ struct bundle *bundle = authp->physical->dl->bundle;
+ struct fsmheader lh;
+ struct mbuf *bp;
+ u_char *cp;
+ int namelen, keylen, plen;
+
+ namelen = strlen(bundle->cfg.auth.name);
+ keylen = strlen(bundle->cfg.auth.key);
+ plen = namelen + keylen + 2;
+ log_Printf(LogDEBUG, "pap_Req: namelen = %d, keylen = %d\n", namelen, keylen);
+ log_Printf(LogPHASE, "Pap Output: %s ********\n", bundle->cfg.auth.name);
+ if (*bundle->cfg.auth.name == '\0')
+ log_Printf(LogWARN, "Sending empty PAP authname!\n");
+ lh.code = PAP_REQUEST;
+ lh.id = authp->id;
+ lh.length = htons(plen + sizeof(struct fsmheader));
+ bp = m_get(plen + sizeof(struct fsmheader), MB_PAPOUT);
+ memcpy(MBUF_CTOP(bp), &lh, sizeof(struct fsmheader));
+ cp = MBUF_CTOP(bp) + sizeof(struct fsmheader);
+ *cp++ = namelen;
+ memcpy(cp, bundle->cfg.auth.name, namelen);
+ cp += namelen;
+ *cp++ = keylen;
+ memcpy(cp, bundle->cfg.auth.key, keylen);
+ link_PushPacket(&authp->physical->link, bp, bundle,
+ LINK_QUEUES(&authp->physical->link) - 1, PROTO_PAP);
+}
+
+static void
+SendPapCode(struct authinfo *authp, int code, const char *message)
+{
+ struct fsmheader lh;
+ struct mbuf *bp;
+ u_char *cp;
+ int plen, mlen;
+
+ lh.code = code;
+ lh.id = authp->id;
+ mlen = strlen(message);
+ plen = mlen + 1;
+ lh.length = htons(plen + sizeof(struct fsmheader));
+ bp = m_get(plen + sizeof(struct fsmheader), MB_PAPOUT);
+ memcpy(MBUF_CTOP(bp), &lh, sizeof(struct fsmheader));
+ cp = MBUF_CTOP(bp) + sizeof(struct fsmheader);
+ /*
+ * If our message is longer than 255 bytes, truncate the length to
+ * 255 and send the entire message anyway. Maybe the other end will
+ * display it... (see pap_Input() !)
+ */
+ *cp++ = mlen > 255 ? 255 : mlen;
+ memcpy(cp, message, mlen);
+ log_Printf(LogPHASE, "Pap Output: %s\n", papcodes[code]);
+
+ link_PushPacket(&authp->physical->link, bp, authp->physical->dl->bundle,
+ LINK_QUEUES(&authp->physical->link) - 1, PROTO_PAP);
+}
+
+static void
+pap_Success(struct authinfo *authp)
+{
+ struct bundle *bundle = authp->physical->dl->bundle;
+
+ datalink_GotAuthname(authp->physical->dl, authp->in.name);
+#ifndef NORADIUS
+ if (*bundle->radius.cfg.file && bundle->radius.repstr)
+ SendPapCode(authp, PAP_ACK, bundle->radius.repstr);
+ else
+#endif
+ SendPapCode(authp, PAP_ACK, "Greetings!!");
+ authp->physical->link.lcp.auth_ineed = 0;
+ if (Enabled(bundle, OPT_UTMP))
+ physical_Login(authp->physical, authp->in.name);
+
+ if (authp->physical->link.lcp.auth_iwait == 0)
+ /*
+ * Either I didn't need to authenticate, or I've already been
+ * told that I got the answer right.
+ */
+ datalink_AuthOk(authp->physical->dl);
+}
+
+static void
+pap_Failure(struct authinfo *authp)
+{
+ SendPapCode(authp, PAP_NAK, "Login incorrect");
+ datalink_AuthNotOk(authp->physical->dl);
+}
+
+void
+pap_Init(struct authinfo *pap, struct physical *p)
+{
+ auth_Init(pap, p, pap_Req, pap_Success, pap_Failure);
+}
+
+struct mbuf *
+pap_Input(struct bundle *bundle, struct link *l, struct mbuf *bp)
+{
+ struct physical *p = link2physical(l);
+ struct authinfo *authp = &p->dl->pap;
+ u_char nlen, klen, *key;
+ const char *txt;
+ int txtlen;
+
+ if (p == NULL) {
+ log_Printf(LogERROR, "pap_Input: Not a physical link - dropped\n");
+ m_freem(bp);
+ return NULL;
+ }
+
+ if (bundle_Phase(bundle) != PHASE_NETWORK &&
+ bundle_Phase(bundle) != PHASE_AUTHENTICATE) {
+ log_Printf(LogPHASE, "Unexpected pap input - dropped !\n");
+ m_freem(bp);
+ return NULL;
+ }
+
+ if ((bp = auth_ReadHeader(authp, bp)) == NULL &&
+ ntohs(authp->in.hdr.length) == 0) {
+ log_Printf(LogWARN, "Pap Input: Truncated header !\n");
+ return NULL;
+ }
+
+ if (authp->in.hdr.code == 0 || authp->in.hdr.code > MAXPAPCODE) {
+ log_Printf(LogPHASE, "Pap Input: %d: Bad PAP code !\n", authp->in.hdr.code);
+ m_freem(bp);
+ return NULL;
+ }
+
+ if (authp->in.hdr.code != PAP_REQUEST && authp->id != authp->in.hdr.id &&
+ Enabled(bundle, OPT_IDCHECK)) {
+ /* Wrong conversation dude ! */
+ log_Printf(LogPHASE, "Pap Input: %s dropped (got id %d, not %d)\n",
+ papcodes[authp->in.hdr.code], authp->in.hdr.id, authp->id);
+ m_freem(bp);
+ return NULL;
+ }
+ m_settype(bp, MB_PAPIN);
+ authp->id = authp->in.hdr.id; /* We respond with this id */
+
+ if (bp) {
+ bp = mbuf_Read(bp, &nlen, 1);
+ if (authp->in.hdr.code == PAP_ACK) {
+ /*
+ * Don't restrict the length of our acknowledgement freetext to
+ * nlen (a one-byte length). Show the rest of the ack packet
+ * instead. This isn't really part of the protocol.....
+ */
+ bp = m_pullup(bp);
+ txt = MBUF_CTOP(bp);
+ txtlen = m_length(bp);
+ } else {
+ bp = auth_ReadName(authp, bp, nlen);
+ txt = authp->in.name;
+ txtlen = strlen(authp->in.name);
+ }
+ } else {
+ txt = "";
+ txtlen = 0;
+ }
+
+ log_Printf(LogPHASE, "Pap Input: %s (%.*s)\n",
+ papcodes[authp->in.hdr.code], txtlen, txt);
+
+ switch (authp->in.hdr.code) {
+ case PAP_REQUEST:
+ if (bp == NULL) {
+ log_Printf(LogPHASE, "Pap Input: No key given !\n");
+ break;
+ }
+ bp = mbuf_Read(bp, &klen, 1);
+ if (m_length(bp) < klen) {
+ log_Printf(LogERROR, "Pap Input: Truncated key !\n");
+ break;
+ }
+ if ((key = malloc(klen+1)) == NULL) {
+ log_Printf(LogERROR, "Pap Input: Out of memory !\n");
+ break;
+ }
+ bp = mbuf_Read(bp, key, klen);
+ key[klen] = '\0';
+
+#ifndef NORADIUS
+ if (*bundle->radius.cfg.file) {
+ if (!radius_Authenticate(&bundle->radius, authp, authp->in.name,
+ key, strlen(key), NULL, 0))
+ pap_Failure(authp);
+ } else
+#endif
+ if (auth_Validate(bundle, authp->in.name, key, p))
+ pap_Success(authp);
+ else
+ pap_Failure(authp);
+
+ free(key);
+ break;
+
+ case PAP_ACK:
+ auth_StopTimer(authp);
+ if (p->link.lcp.auth_iwait == PROTO_PAP) {
+ p->link.lcp.auth_iwait = 0;
+ if (p->link.lcp.auth_ineed == 0)
+ /*
+ * We've succeeded in our ``login''
+ * If we're not expecting the peer to authenticate (or he already
+ * has), proceed to network phase.
+ */
+ datalink_AuthOk(p->dl);
+ }
+ break;
+
+ case PAP_NAK:
+ auth_StopTimer(authp);
+ datalink_AuthNotOk(p->dl);
+ break;
+ }
+
+ m_freem(bp);
+ return NULL;
+}
diff --git a/usr.sbin/ppp/pap.h b/usr.sbin/ppp/pap.h
new file mode 100644
index 0000000..8e8d457
--- /dev/null
+++ b/usr.sbin/ppp/pap.h
@@ -0,0 +1,40 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define PAP_REQUEST 1
+#define PAP_ACK 2
+#define PAP_NAK 3
+
+struct mbuf;
+struct physical;
+struct authinfo;
+
+extern void pap_Init(struct authinfo *, struct physical *);
+extern struct mbuf *pap_Input(struct bundle *, struct link *, struct mbuf *);
diff --git a/usr.sbin/ppp/physical.c b/usr.sbin/ppp/physical.c
new file mode 100644
index 0000000..2fab974
--- /dev/null
+++ b/usr.sbin/ppp/physical.c
@@ -0,0 +1,1130 @@
+/*
+ * Written by Eivind Eklund <eivind@yes.no>
+ * for Yes Interactive
+ *
+ * Copyright (C) 1998, Yes Interactive. All rights reserved.
+ *
+ * Redistribution and use in any form is permitted. Redistribution in
+ * source form should include the above copyright and this set of
+ * conditions, because large sections american law seems to have been
+ * created by a bunch of jerks on drugs that are now illegal, forcing
+ * me to include this copyright-stuff instead of placing this in the
+ * public domain. The name of of 'Yes Interactive' or 'Eivind Eklund'
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $FreeBSD$
+ *
+ */
+
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/tty.h> /* TIOCOUTQ */
+#include <sys/uio.h>
+#include <time.h>
+#include <unistd.h>
+#include <utmp.h>
+#if defined(__OpenBSD__) || defined(__NetBSD__)
+#include <sys/ioctl.h>
+#include <util.h>
+#else
+#include <libutil.h>
+#endif
+
+#include "layer.h"
+#ifndef NONAT
+#include "nat_cmd.h"
+#endif
+#include "proto.h"
+#include "acf.h"
+#include "vjcomp.h"
+#include "defs.h"
+#include "command.h"
+#include "mbuf.h"
+#include "log.h"
+#include "id.h"
+#include "timer.h"
+#include "fsm.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "lcp.h"
+#include "throughput.h"
+#include "sync.h"
+#include "async.h"
+#include "iplist.h"
+#include "slcompress.h"
+#include "ncpaddr.h"
+#include "ipcp.h"
+#include "filter.h"
+#include "descriptor.h"
+#include "ccp.h"
+#include "link.h"
+#include "physical.h"
+#include "mp.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+#include "prompt.h"
+#include "chat.h"
+#include "auth.h"
+#include "chap.h"
+#include "cbcp.h"
+#include "datalink.h"
+#include "tcp.h"
+#include "udp.h"
+#include "exec.h"
+#include "tty.h"
+#ifndef NOI4B
+#include "i4b.h"
+#endif
+#ifndef NONETGRAPH
+#include "ether.h"
+#include "netgraph.h"
+#endif
+#ifndef NOATM
+#include "atm.h"
+#endif
+#include "tcpmss.h"
+
+#define PPPOTCPLINE "ppp"
+
+static int physical_DescriptorWrite(struct fdescriptor *, struct bundle *,
+ const fd_set *);
+
+static int
+physical_DeviceSize(void)
+{
+ return sizeof(struct device);
+}
+
+struct {
+ struct device *(*create)(struct physical *);
+ struct device *(*iov2device)(int, struct physical *, struct iovec *,
+ int *, int, int *, int *);
+ int (*DeviceSize)(void);
+} devices[] = {
+#ifndef NOI4B
+ /*
+ * This must come before ``tty'' so that the probe routine is
+ * able to identify it as a more specific type of terminal device.
+ */
+ { i4b_Create, i4b_iov2device, i4b_DeviceSize },
+#endif
+ { tty_Create, tty_iov2device, tty_DeviceSize },
+#ifndef NONETGRAPH
+ /*
+ * This must come before ``udp'' so that the probe routine is
+ * able to identify it as a more specific type of SOCK_DGRAM.
+ */
+ { ether_Create, ether_iov2device, ether_DeviceSize },
+#ifdef EXPERIMENTAL_NETGRAPH
+ { ng_Create, ng_iov2device, ng_DeviceSize },
+#endif
+#endif
+#ifndef NOATM
+ /* Ditto for ATM devices */
+ { atm_Create, atm_iov2device, atm_DeviceSize },
+#endif
+ { tcp_Create, tcp_iov2device, tcp_DeviceSize },
+ { udp_Create, udp_iov2device, udp_DeviceSize },
+ { exec_Create, exec_iov2device, exec_DeviceSize }
+};
+
+#define NDEVICES (sizeof devices / sizeof devices[0])
+
+static int
+physical_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e,
+ int *n)
+{
+ return physical_doUpdateSet(d, r, w, e, n, 0);
+}
+
+void
+physical_SetDescriptor(struct physical *p)
+{
+ p->desc.type = PHYSICAL_DESCRIPTOR;
+ p->desc.UpdateSet = physical_UpdateSet;
+ p->desc.IsSet = physical_IsSet;
+ p->desc.Read = physical_DescriptorRead;
+ p->desc.Write = physical_DescriptorWrite;
+}
+
+struct physical *
+physical_Create(struct datalink *dl, int type)
+{
+ struct physical *p;
+
+ p = (struct physical *)malloc(sizeof(struct physical));
+ if (!p)
+ return NULL;
+
+ p->link.type = PHYSICAL_LINK;
+ p->link.name = dl->name;
+ p->link.len = sizeof *p;
+
+ /* The sample period is fixed - see physical2iov() & iov2physical() */
+ throughput_init(&p->link.stats.total, SAMPLE_PERIOD);
+ p->link.stats.parent = dl->bundle->ncp.mp.active ?
+ &dl->bundle->ncp.mp.link.stats.total : NULL;
+ p->link.stats.gather = 1;
+
+ memset(p->link.Queue, '\0', sizeof p->link.Queue);
+ memset(p->link.proto_in, '\0', sizeof p->link.proto_in);
+ memset(p->link.proto_out, '\0', sizeof p->link.proto_out);
+ link_EmptyStack(&p->link);
+
+ p->handler = NULL;
+ physical_SetDescriptor(p);
+ p->type = type;
+
+ hdlc_Init(&p->hdlc, &p->link.lcp);
+ async_Init(&p->async);
+
+ p->fd = -1;
+ p->out = NULL;
+ p->connect_count = 0;
+ p->dl = dl;
+ p->input.sz = 0;
+ *p->name.full = '\0';
+ p->name.base = p->name.full;
+
+ p->Utmp = 0;
+ p->session_owner = (pid_t)-1;
+
+ p->cfg.rts_cts = MODEM_CTSRTS;
+ p->cfg.speed = MODEM_SPEED;
+ p->cfg.parity = CS8;
+ memcpy(p->cfg.devlist, MODEM_LIST, sizeof MODEM_LIST);
+ p->cfg.ndev = NMODEMS;
+ p->cfg.cd.necessity = CD_DEFAULT;
+ p->cfg.cd.delay = 0; /* reconfigured or device specific default */
+
+ lcp_Init(&p->link.lcp, dl->bundle, &p->link, &dl->fsmp);
+ ccp_Init(&p->link.ccp, dl->bundle, &p->link, &dl->fsmp);
+
+ return p;
+}
+
+static const struct parity {
+ const char *name;
+ const char *name1;
+ int set;
+} validparity[] = {
+ { "even", "P_EVEN", CS7 | PARENB },
+ { "odd", "P_ODD", CS7 | PARENB | PARODD },
+ { "none", "P_ZERO", CS8 },
+ { NULL, 0 },
+};
+
+static int
+GetParityValue(const char *str)
+{
+ const struct parity *pp;
+
+ for (pp = validparity; pp->name; pp++) {
+ if (strcasecmp(pp->name, str) == 0 ||
+ strcasecmp(pp->name1, str) == 0) {
+ return pp->set;
+ }
+ }
+ return (-1);
+}
+
+int
+physical_SetParity(struct physical *p, const char *str)
+{
+ struct termios rstio;
+ int val;
+
+ val = GetParityValue(str);
+ if (val > 0) {
+ p->cfg.parity = val;
+ if (p->fd >= 0) {
+ tcgetattr(p->fd, &rstio);
+ rstio.c_cflag &= ~(CSIZE | PARODD | PARENB);
+ rstio.c_cflag |= val;
+ tcsetattr(p->fd, TCSADRAIN, &rstio);
+ }
+ return 0;
+ }
+ log_Printf(LogWARN, "%s: %s: Invalid parity\n", p->link.name, str);
+ return -1;
+}
+
+int
+physical_GetSpeed(struct physical *p)
+{
+ if (p->handler && p->handler->speed)
+ return (*p->handler->speed)(p);
+
+ return 0;
+}
+
+int
+physical_SetSpeed(struct physical *p, int speed)
+{
+ if (IntToSpeed(speed) != B0) {
+ p->cfg.speed = speed;
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+physical_Raw(struct physical *p)
+{
+ if (p->handler && p->handler->raw)
+ return (*p->handler->raw)(p);
+
+ return 1;
+}
+
+void
+physical_Offline(struct physical *p)
+{
+ if (p->handler && p->handler->offline)
+ (*p->handler->offline)(p);
+ log_Printf(LogPHASE, "%s: Disconnected!\n", p->link.name);
+}
+
+static int
+physical_Lock(struct physical *p)
+{
+ int res;
+
+ if (*p->name.full == '/' && p->type != PHYS_DIRECT &&
+ (res = ID0uu_lock(p->name.base)) != UU_LOCK_OK) {
+ if (res == UU_LOCK_INUSE)
+ log_Printf(LogPHASE, "%s: %s is in use\n", p->link.name, p->name.full);
+ else
+ log_Printf(LogPHASE, "%s: %s is in use: uu_lock: %s\n",
+ p->link.name, p->name.full, uu_lockerr(res));
+ return 0;
+ }
+
+ return 1;
+}
+
+static void
+physical_Unlock(struct physical *p)
+{
+ if (*p->name.full == '/' && p->type != PHYS_DIRECT &&
+ ID0uu_unlock(p->name.base) == -1)
+ log_Printf(LogALERT, "%s: Can't uu_unlock %s\n", p->link.name,
+ p->name.base);
+}
+
+void
+physical_Close(struct physical *p)
+{
+ int newsid;
+ char fn[PATH_MAX];
+
+ if (p->fd < 0)
+ return;
+
+ log_Printf(LogDEBUG, "%s: Close\n", p->link.name);
+
+ if (p->handler && p->handler->cooked)
+ (*p->handler->cooked)(p);
+
+ physical_StopDeviceTimer(p);
+ if (p->Utmp) {
+ if (p->handler && (p->handler->type == TCP_DEVICE ||
+ p->handler->type == UDP_DEVICE))
+ /* Careful - we logged in on line ``ppp'' with IP as our host */
+ ID0logout(PPPOTCPLINE, 1);
+ else
+ ID0logout(p->name.base, 0);
+ p->Utmp = 0;
+ }
+ newsid = tcgetpgrp(p->fd) == getpgrp();
+ close(p->fd);
+ p->fd = -1;
+ log_SetTtyCommandMode(p->dl);
+
+ throughput_stop(&p->link.stats.total);
+ throughput_log(&p->link.stats.total, LogPHASE, p->link.name);
+
+ if (p->session_owner != (pid_t)-1) {
+ log_Printf(LogPHASE, "%s: HUPing %ld\n", p->link.name,
+ (long)p->session_owner);
+ ID0kill(p->session_owner, SIGHUP);
+ p->session_owner = (pid_t)-1;
+ }
+
+ if (newsid)
+ bundle_setsid(p->dl->bundle, 0);
+
+ if (*p->name.full == '/') {
+ snprintf(fn, sizeof fn, "%s%s.if", _PATH_VARRUN, p->name.base);
+#ifndef RELEASE_CRUNCH
+ if (ID0unlink(fn) == -1)
+ log_Printf(LogALERT, "%s: Can't remove %s: %s\n",
+ p->link.name, fn, strerror(errno));
+#else
+ ID0unlink(fn);
+#endif
+ }
+ physical_Unlock(p);
+ if (p->handler && p->handler->destroy)
+ (*p->handler->destroy)(p);
+ p->handler = NULL;
+ p->name.base = p->name.full;
+ *p->name.full = '\0';
+}
+
+void
+physical_Destroy(struct physical *p)
+{
+ physical_Close(p);
+ throughput_destroy(&p->link.stats.total);
+ free(p);
+}
+
+static int
+physical_DescriptorWrite(struct fdescriptor *d, struct bundle *bundle,
+ const fd_set *fdset)
+{
+ struct physical *p = descriptor2physical(d);
+ int nw, result = 0;
+
+ if (p->out == NULL)
+ p->out = link_Dequeue(&p->link);
+
+ if (p->out) {
+ nw = physical_Write(p, MBUF_CTOP(p->out), p->out->m_len);
+ log_Printf(LogDEBUG, "%s: DescriptorWrite: wrote %d(%lu) to %d\n",
+ p->link.name, nw, (unsigned long)p->out->m_len, p->fd);
+ if (nw > 0) {
+ p->out->m_len -= nw;
+ p->out->m_offset += nw;
+ if (p->out->m_len == 0)
+ p->out = m_free(p->out);
+ result = 1;
+ } else if (nw < 0) {
+ if (errno == EAGAIN)
+ result = 1;
+ else if (errno != ENOBUFS) {
+ log_Printf(LogPHASE, "%s: write (%d): %s\n", p->link.name,
+ p->fd, strerror(errno));
+ datalink_Down(p->dl, CLOSE_NORMAL);
+ }
+ }
+ /* else we shouldn't really have been called ! select() is broken ! */
+ }
+
+ return result;
+}
+
+int
+physical_ShowStatus(struct cmdargs const *arg)
+{
+ struct physical *p = arg->cx->physical;
+ struct cd *cd;
+ const char *dev;
+ int n, slot;
+
+ prompt_Printf(arg->prompt, "Name: %s\n", p->link.name);
+ prompt_Printf(arg->prompt, " State: ");
+ if (p->fd < 0)
+ prompt_Printf(arg->prompt, "closed\n");
+ else {
+ slot = physical_Slot(p);
+ if (p->handler && p->handler->openinfo) {
+ if (slot == -1)
+ prompt_Printf(arg->prompt, "open (%s)\n", (*p->handler->openinfo)(p));
+ else
+ prompt_Printf(arg->prompt, "open (%s, port %d)\n",
+ (*p->handler->openinfo)(p), slot);
+ } else if (slot == -1)
+ prompt_Printf(arg->prompt, "open\n");
+ else
+ prompt_Printf(arg->prompt, "open (port %d)\n", slot);
+ }
+
+ prompt_Printf(arg->prompt, " Device: %s",
+ *p->name.full ? p->name.full :
+ p->type == PHYS_DIRECT ? "unknown" : "N/A");
+ if (p->session_owner != (pid_t)-1)
+ prompt_Printf(arg->prompt, " (session owner: %ld)", (long)p->session_owner);
+
+ prompt_Printf(arg->prompt, "\n Link Type: %s\n", mode2Nam(p->type));
+ prompt_Printf(arg->prompt, " Connect Count: %d\n", p->connect_count);
+#ifdef TIOCOUTQ
+ if (p->fd >= 0 && ioctl(p->fd, TIOCOUTQ, &n) >= 0)
+ prompt_Printf(arg->prompt, " Physical outq: %d\n", n);
+#endif
+
+ prompt_Printf(arg->prompt, " Queued Packets: %lu\n",
+ (u_long)link_QueueLen(&p->link));
+ prompt_Printf(arg->prompt, " Phone Number: %s\n", arg->cx->phone.chosen);
+
+ prompt_Printf(arg->prompt, "\nDefaults:\n");
+
+ prompt_Printf(arg->prompt, " Device List: ");
+ dev = p->cfg.devlist;
+ for (n = 0; n < p->cfg.ndev; n++) {
+ if (n)
+ prompt_Printf(arg->prompt, ", ");
+ prompt_Printf(arg->prompt, "\"%s\"", dev);
+ dev += strlen(dev) + 1;
+ }
+
+ prompt_Printf(arg->prompt, "\n Characteristics: ");
+ if (physical_IsSync(arg->cx->physical))
+ prompt_Printf(arg->prompt, "sync");
+ else
+ prompt_Printf(arg->prompt, "%dbps", p->cfg.speed);
+
+ switch (p->cfg.parity & CSIZE) {
+ case CS7:
+ prompt_Printf(arg->prompt, ", cs7");
+ break;
+ case CS8:
+ prompt_Printf(arg->prompt, ", cs8");
+ break;
+ }
+ if (p->cfg.parity & PARENB) {
+ if (p->cfg.parity & PARODD)
+ prompt_Printf(arg->prompt, ", odd parity");
+ else
+ prompt_Printf(arg->prompt, ", even parity");
+ } else
+ prompt_Printf(arg->prompt, ", no parity");
+
+ prompt_Printf(arg->prompt, ", CTS/RTS %s\n", (p->cfg.rts_cts ? "on" : "off"));
+
+ prompt_Printf(arg->prompt, " CD check delay: ");
+ cd = p->handler ? &p->handler->cd : &p->cfg.cd;
+ if (cd->necessity == CD_NOTREQUIRED)
+ prompt_Printf(arg->prompt, "no cd");
+ else if (p->cfg.cd.necessity == CD_DEFAULT) {
+ prompt_Printf(arg->prompt, "device specific");
+ } else {
+ prompt_Printf(arg->prompt, "%d second%s", p->cfg.cd.delay,
+ p->cfg.cd.delay == 1 ? "" : "s");
+ if (p->cfg.cd.necessity == CD_REQUIRED)
+ prompt_Printf(arg->prompt, " (required!)");
+ }
+ prompt_Printf(arg->prompt, "\n\n");
+
+ throughput_disp(&p->link.stats.total, arg->prompt);
+
+ return 0;
+}
+
+void
+physical_DescriptorRead(struct fdescriptor *d, struct bundle *bundle,
+ const fd_set *fdset)
+{
+ struct physical *p = descriptor2physical(d);
+ u_char *rbuff;
+ int n, found;
+
+ rbuff = p->input.buf + p->input.sz;
+
+ /* something to read */
+ n = physical_Read(p, rbuff, sizeof p->input.buf - p->input.sz);
+ log_Printf(LogDEBUG, "%s: DescriptorRead: read %d/%d from %d\n",
+ p->link.name, n, (int)(sizeof p->input.buf - p->input.sz), p->fd);
+ if (n <= 0) {
+ if (n < 0)
+ log_Printf(LogPHASE, "%s: read (%d): %s\n", p->link.name, p->fd,
+ strerror(errno));
+ else
+ log_Printf(LogPHASE, "%s: read (%d): Got zero bytes\n",
+ p->link.name, p->fd);
+ datalink_Down(p->dl, CLOSE_NORMAL);
+ return;
+ }
+
+ rbuff -= p->input.sz;
+ n += p->input.sz;
+
+ if (p->link.lcp.fsm.state <= ST_CLOSED) {
+ if (p->type != PHYS_DEDICATED) {
+ found = hdlc_Detect((u_char const **)&rbuff, n, physical_IsSync(p));
+ if (rbuff != p->input.buf)
+ log_WritePrompts(p->dl, "%.*s", (int)(rbuff - p->input.buf),
+ p->input.buf);
+ p->input.sz = n - (rbuff - p->input.buf);
+
+ if (found) {
+ /* LCP packet is detected. Turn ourselves into packet mode */
+ log_Printf(LogPHASE, "%s: PPP packet detected, coming up\n",
+ p->link.name);
+ log_SetTtyCommandMode(p->dl);
+ datalink_Up(p->dl, 0, 1);
+ link_PullPacket(&p->link, rbuff, p->input.sz, bundle);
+ p->input.sz = 0;
+ } else
+ bcopy(rbuff, p->input.buf, p->input.sz);
+ } else
+ /* In -dedicated mode, we just discard input until LCP is started */
+ p->input.sz = 0;
+ } else if (n > 0)
+ link_PullPacket(&p->link, rbuff, n, bundle);
+}
+
+struct physical *
+iov2physical(struct datalink *dl, struct iovec *iov, int *niov, int maxiov,
+ int fd, int *auxfd, int *nauxfd)
+{
+ struct physical *p;
+ int len, h, type;
+
+ p = (struct physical *)iov[(*niov)++].iov_base;
+ p->link.name = dl->name;
+ memset(p->link.Queue, '\0', sizeof p->link.Queue);
+
+ p->desc.UpdateSet = physical_UpdateSet;
+ p->desc.IsSet = physical_IsSet;
+ p->desc.Read = physical_DescriptorRead;
+ p->desc.Write = physical_DescriptorWrite;
+ p->type = PHYS_DIRECT;
+ p->dl = dl;
+ len = strlen(_PATH_DEV);
+ p->out = NULL;
+ p->connect_count = 1;
+
+ physical_SetDevice(p, p->name.full);
+
+ p->link.lcp.fsm.bundle = dl->bundle;
+ p->link.lcp.fsm.link = &p->link;
+ memset(&p->link.lcp.fsm.FsmTimer, '\0', sizeof p->link.lcp.fsm.FsmTimer);
+ memset(&p->link.lcp.fsm.OpenTimer, '\0', sizeof p->link.lcp.fsm.OpenTimer);
+ memset(&p->link.lcp.fsm.StoppedTimer, '\0',
+ sizeof p->link.lcp.fsm.StoppedTimer);
+ p->link.lcp.fsm.parent = &dl->fsmp;
+ lcp_SetupCallbacks(&p->link.lcp);
+
+ p->link.ccp.fsm.bundle = dl->bundle;
+ p->link.ccp.fsm.link = &p->link;
+ /* Our in.state & out.state are NULL (no link-level ccp yet) */
+ memset(&p->link.ccp.fsm.FsmTimer, '\0', sizeof p->link.ccp.fsm.FsmTimer);
+ memset(&p->link.ccp.fsm.OpenTimer, '\0', sizeof p->link.ccp.fsm.OpenTimer);
+ memset(&p->link.ccp.fsm.StoppedTimer, '\0',
+ sizeof p->link.ccp.fsm.StoppedTimer);
+ p->link.ccp.fsm.parent = &dl->fsmp;
+ ccp_SetupCallbacks(&p->link.ccp);
+
+ p->hdlc.lqm.owner = &p->link.lcp;
+ p->hdlc.ReportTimer.state = TIMER_STOPPED;
+ p->hdlc.lqm.timer.state = TIMER_STOPPED;
+
+ p->fd = fd;
+ p->link.stats.total.in.SampleOctets = (long long *)iov[(*niov)++].iov_base;
+ p->link.stats.total.out.SampleOctets = (long long *)iov[(*niov)++].iov_base;
+ p->link.stats.parent = dl->bundle->ncp.mp.active ?
+ &dl->bundle->ncp.mp.link.stats.total : NULL;
+ p->link.stats.gather = 1;
+
+ type = (long)p->handler;
+ p->handler = NULL;
+ for (h = 0; h < NDEVICES && p->handler == NULL; h++)
+ p->handler = (*devices[h].iov2device)(type, p, iov, niov, maxiov,
+ auxfd, nauxfd);
+ if (p->handler == NULL) {
+ log_Printf(LogPHASE, "%s: Unknown link type\n", p->link.name);
+ free(iov[(*niov)++].iov_base);
+ physical_SetupStack(p, "unknown", PHYSICAL_NOFORCE);
+ } else
+ log_Printf(LogPHASE, "%s: Device %s, link type is %s\n",
+ p->link.name, p->name.full, p->handler->name);
+
+ if (p->hdlc.lqm.method && p->hdlc.lqm.timer.load)
+ lqr_reStart(&p->link.lcp);
+ hdlc_StartTimer(&p->hdlc);
+
+ throughput_restart(&p->link.stats.total, "physical throughput",
+ Enabled(dl->bundle, OPT_THROUGHPUT));
+
+ return p;
+}
+
+int
+physical_MaxDeviceSize()
+{
+ int biggest, sz, n;
+
+ biggest = sizeof(struct device);
+ for (sz = n = 0; n < NDEVICES; n++)
+ if (devices[n].DeviceSize) {
+ sz = (*devices[n].DeviceSize)();
+ if (biggest < sz)
+ biggest = sz;
+ }
+
+ return biggest;
+}
+
+int
+physical2iov(struct physical *p, struct iovec *iov, int *niov, int maxiov,
+ int *auxfd, int *nauxfd)
+{
+ struct device *h;
+ int sz;
+
+ h = NULL;
+ if (p) {
+ hdlc_StopTimer(&p->hdlc);
+ lqr_StopTimer(p);
+ timer_Stop(&p->link.lcp.fsm.FsmTimer);
+ timer_Stop(&p->link.ccp.fsm.FsmTimer);
+ timer_Stop(&p->link.lcp.fsm.OpenTimer);
+ timer_Stop(&p->link.ccp.fsm.OpenTimer);
+ timer_Stop(&p->link.lcp.fsm.StoppedTimer);
+ timer_Stop(&p->link.ccp.fsm.StoppedTimer);
+ if (p->handler) {
+ h = p->handler;
+ p->handler = (struct device *)(long)p->handler->type;
+ }
+
+ if (Enabled(p->dl->bundle, OPT_KEEPSESSION) ||
+ tcgetpgrp(p->fd) == getpgrp())
+ p->session_owner = getpid(); /* So I'll eventually get HUP'd */
+ else
+ p->session_owner = (pid_t)-1;
+ timer_Stop(&p->link.stats.total.Timer);
+ }
+
+ if (*niov + 2 >= maxiov) {
+ log_Printf(LogERROR, "physical2iov: No room for physical + throughput"
+ " + device !\n");
+ if (p)
+ free(p);
+ return -1;
+ }
+
+ iov[*niov].iov_base = (void *)p;
+ iov[*niov].iov_len = sizeof *p;
+ (*niov)++;
+
+ iov[*niov].iov_base = p ? (void *)p->link.stats.total.in.SampleOctets : NULL;
+ iov[*niov].iov_len = SAMPLE_PERIOD * sizeof(long long);
+ (*niov)++;
+ iov[*niov].iov_base = p ? (void *)p->link.stats.total.out.SampleOctets : NULL;
+ iov[*niov].iov_len = SAMPLE_PERIOD * sizeof(long long);
+ (*niov)++;
+
+ sz = physical_MaxDeviceSize();
+ if (p) {
+ if (h && h->device2iov)
+ (*h->device2iov)(h, iov, niov, maxiov, auxfd, nauxfd);
+ else {
+ iov[*niov].iov_base = malloc(sz);
+ if (h)
+ memcpy(iov[*niov].iov_base, h, sizeof *h);
+ iov[*niov].iov_len = sz;
+ (*niov)++;
+ }
+ } else {
+ iov[*niov].iov_base = NULL;
+ iov[*niov].iov_len = sz;
+ (*niov)++;
+ }
+
+ return p ? p->fd : 0;
+}
+
+const char *
+physical_LockedDevice(struct physical *p)
+{
+ if (p->fd >= 0 && *p->name.full == '/' && p->type != PHYS_DIRECT)
+ return p->name.base;
+
+ return NULL;
+}
+
+void
+physical_ChangedPid(struct physical *p, pid_t newpid)
+{
+ if (physical_LockedDevice(p)) {
+ int res;
+
+ if ((res = ID0uu_lock_txfr(p->name.base, newpid)) != UU_LOCK_OK)
+ log_Printf(LogPHASE, "uu_lock_txfr: %s\n", uu_lockerr(res));
+ }
+}
+
+int
+physical_IsSync(struct physical *p)
+{
+ return p->cfg.speed == 0;
+}
+
+u_short
+physical_DeviceMTU(struct physical *p)
+{
+ return p->handler ? p->handler->mtu : 0;
+}
+
+const char *physical_GetDevice(struct physical *p)
+{
+ return p->name.full;
+}
+
+void
+physical_SetDeviceList(struct physical *p, int argc, const char *const *argv)
+{
+ int f, pos;
+
+ p->cfg.devlist[sizeof p->cfg.devlist - 1] = '\0';
+ for (f = 0, pos = 0; f < argc && pos < sizeof p->cfg.devlist - 1; f++) {
+ if (pos)
+ p->cfg.devlist[pos++] = '\0';
+ strncpy(p->cfg.devlist + pos, argv[f], sizeof p->cfg.devlist - pos - 1);
+ pos += strlen(p->cfg.devlist + pos);
+ }
+ p->cfg.ndev = f;
+}
+
+void
+physical_SetSync(struct physical *p)
+{
+ p->cfg.speed = 0;
+}
+
+int
+physical_SetRtsCts(struct physical *p, int enable)
+{
+ p->cfg.rts_cts = enable ? 1 : 0;
+ return 1;
+}
+
+ssize_t
+physical_Read(struct physical *p, void *buf, size_t nbytes)
+{
+ ssize_t ret;
+
+ if (p->handler && p->handler->read)
+ ret = (*p->handler->read)(p, buf, nbytes);
+ else
+ ret = read(p->fd, buf, nbytes);
+
+ log_DumpBuff(LogPHYSICAL, "read", buf, ret);
+
+ return ret;
+}
+
+ssize_t
+physical_Write(struct physical *p, const void *buf, size_t nbytes)
+{
+ log_DumpBuff(LogPHYSICAL, "write", buf, nbytes);
+
+ if (p->handler && p->handler->write)
+ return (*p->handler->write)(p, buf, nbytes);
+
+ return write(p->fd, buf, nbytes);
+}
+
+int
+physical_doUpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e,
+ int *n, int force)
+{
+ struct physical *p = descriptor2physical(d);
+ int sets;
+
+ sets = 0;
+ if (p->fd >= 0) {
+ if (r) {
+ FD_SET(p->fd, r);
+ log_Printf(LogTIMER, "%s: fdset(r) %d\n", p->link.name, p->fd);
+ sets++;
+ }
+ if (e) {
+ FD_SET(p->fd, e);
+ log_Printf(LogTIMER, "%s: fdset(e) %d\n", p->link.name, p->fd);
+ sets++;
+ }
+ if (w && (force || link_QueueLen(&p->link) || p->out)) {
+ FD_SET(p->fd, w);
+ log_Printf(LogTIMER, "%s: fdset(w) %d\n", p->link.name, p->fd);
+ sets++;
+ }
+ if (sets && *n < p->fd + 1)
+ *n = p->fd + 1;
+ }
+
+ return sets;
+}
+
+int
+physical_RemoveFromSet(struct physical *p, fd_set *r, fd_set *w, fd_set *e)
+{
+ if (p->handler && p->handler->removefromset)
+ return (*p->handler->removefromset)(p, r, w, e);
+ else {
+ int sets;
+
+ sets = 0;
+ if (p->fd >= 0) {
+ if (r && FD_ISSET(p->fd, r)) {
+ FD_CLR(p->fd, r);
+ log_Printf(LogTIMER, "%s: fdunset(r) %d\n", p->link.name, p->fd);
+ sets++;
+ }
+ if (e && FD_ISSET(p->fd, e)) {
+ FD_CLR(p->fd, e);
+ log_Printf(LogTIMER, "%s: fdunset(e) %d\n", p->link.name, p->fd);
+ sets++;
+ }
+ if (w && FD_ISSET(p->fd, w)) {
+ FD_CLR(p->fd, w);
+ log_Printf(LogTIMER, "%s: fdunset(w) %d\n", p->link.name, p->fd);
+ sets++;
+ }
+ }
+
+ return sets;
+ }
+}
+
+int
+physical_IsSet(struct fdescriptor *d, const fd_set *fdset)
+{
+ struct physical *p = descriptor2physical(d);
+ return p->fd >= 0 && FD_ISSET(p->fd, fdset);
+}
+
+void
+physical_Login(struct physical *p, const char *name)
+{
+ if (p->type == PHYS_DIRECT && *p->name.base && !p->Utmp) {
+ struct utmp ut;
+ const char *connstr;
+ char *colon;
+
+ memset(&ut, 0, sizeof ut);
+ time(&ut.ut_time);
+ strncpy(ut.ut_name, name, sizeof ut.ut_name);
+ if (p->handler && (p->handler->type == TCP_DEVICE ||
+ p->handler->type == UDP_DEVICE)) {
+ strncpy(ut.ut_line, PPPOTCPLINE, sizeof ut.ut_line);
+ strncpy(ut.ut_host, p->name.base, sizeof ut.ut_host);
+ colon = memchr(ut.ut_host, ':', sizeof ut.ut_host);
+ if (colon)
+ *colon = '\0';
+ } else
+ strncpy(ut.ut_line, p->name.base, sizeof ut.ut_line);
+ if ((connstr = getenv("CONNECT")))
+ /* mgetty sets this to the connection speed */
+ strncpy(ut.ut_host, connstr, sizeof ut.ut_host);
+ ID0login(&ut);
+ p->Utmp = ut.ut_time;
+ }
+}
+
+int
+physical_SetMode(struct physical *p, int mode)
+{
+ if ((p->type & (PHYS_DIRECT|PHYS_DEDICATED) ||
+ mode & (PHYS_DIRECT|PHYS_DEDICATED)) &&
+ (!(p->type & PHYS_DIRECT) || !(mode & PHYS_BACKGROUND))) {
+ /* Note: The -direct -> -background is for callback ! */
+ log_Printf(LogWARN, "%s: Cannot change mode %s to %s\n", p->link.name,
+ mode2Nam(p->type), mode2Nam(mode));
+ return 0;
+ }
+ p->type = mode;
+ return 1;
+}
+
+void
+physical_DeleteQueue(struct physical *p)
+{
+ if (p->out) {
+ m_freem(p->out);
+ p->out = NULL;
+ }
+ link_DeleteQueue(&p->link);
+}
+
+void
+physical_SetDevice(struct physical *p, const char *name)
+{
+ int len = strlen(_PATH_DEV);
+
+ if (name != p->name.full) {
+ strncpy(p->name.full, name, sizeof p->name.full - 1);
+ p->name.full[sizeof p->name.full - 1] = '\0';
+ }
+ p->name.base = *p->name.full == '!' ? p->name.full + 1 :
+ strncmp(p->name.full, _PATH_DEV, len) ?
+ p->name.full : p->name.full + len;
+}
+
+static void
+physical_Found(struct physical *p)
+{
+ FILE *lockfile;
+ char fn[PATH_MAX];
+
+ if (*p->name.full == '/') {
+ snprintf(fn, sizeof fn, "%s%s.if", _PATH_VARRUN, p->name.base);
+ lockfile = ID0fopen(fn, "w");
+ if (lockfile != NULL) {
+ fprintf(lockfile, "%s%d\n", TUN_NAME, p->dl->bundle->unit);
+ fclose(lockfile);
+ }
+#ifndef RELEASE_CRUNCH
+ else
+ log_Printf(LogALERT, "%s: Can't create %s: %s\n",
+ p->link.name, fn, strerror(errno));
+#endif
+ }
+
+ throughput_start(&p->link.stats.total, "physical throughput",
+ Enabled(p->dl->bundle, OPT_THROUGHPUT));
+ p->connect_count++;
+ p->input.sz = 0;
+
+ log_Printf(LogPHASE, "%s: Connected!\n", p->link.name);
+}
+
+int
+physical_Open(struct physical *p, struct bundle *bundle)
+{
+ int devno, h, wasfd, err;
+ char *dev;
+
+ if (p->fd >= 0)
+ log_Printf(LogDEBUG, "%s: Open: Modem is already open!\n", p->link.name);
+ /* We're going back into "term" mode */
+ else if (p->type == PHYS_DIRECT) {
+ physical_SetDevice(p, "");
+ p->fd = STDIN_FILENO;
+ for (h = 0; h < NDEVICES && p->handler == NULL && p->fd >= 0; h++)
+ p->handler = (*devices[h].create)(p);
+ if (p->fd >= 0) {
+ if (p->handler == NULL) {
+ physical_SetupStack(p, "unknown", PHYSICAL_NOFORCE);
+ log_Printf(LogDEBUG, "%s: stdin is unidentified\n", p->link.name);
+ }
+ physical_Found(p);
+ }
+ } else {
+ dev = p->cfg.devlist;
+ devno = 0;
+ while (devno < p->cfg.ndev && p->fd < 0) {
+ physical_SetDevice(p, dev);
+ if (physical_Lock(p)) {
+ err = 0;
+
+ if (*p->name.full == '/') {
+ p->fd = ID0open(p->name.full, O_RDWR | O_NONBLOCK);
+ if (p->fd < 0)
+ err = errno;
+ }
+
+ wasfd = p->fd;
+ for (h = 0; h < NDEVICES && p->handler == NULL; h++)
+ if ((p->handler = (*devices[h].create)(p)) == NULL && wasfd != p->fd)
+ break;
+
+ if (p->fd < 0) {
+ if (h == NDEVICES) {
+ if (err)
+ log_Printf(LogWARN, "%s: %s: %s\n", p->link.name, p->name.full,
+ strerror(errno));
+ else
+ log_Printf(LogWARN, "%s: Device (%s) must begin with a '/',"
+ " a '!' or contain at least one ':'\n", p->link.name,
+ p->name.full);
+ }
+ physical_Unlock(p);
+ } else
+ physical_Found(p);
+ }
+ dev += strlen(dev) + 1;
+ devno++;
+ }
+ }
+
+ return p->fd;
+}
+
+void
+physical_SetupStack(struct physical *p, const char *who, int how)
+{
+ link_EmptyStack(&p->link);
+ if (how == PHYSICAL_FORCE_SYNC || how == PHYSICAL_FORCE_SYNCNOACF ||
+ (how == PHYSICAL_NOFORCE && physical_IsSync(p)))
+ link_Stack(&p->link, &synclayer);
+ else {
+ link_Stack(&p->link, &asynclayer);
+ link_Stack(&p->link, &hdlclayer);
+ }
+ if (how != PHYSICAL_FORCE_SYNCNOACF)
+ link_Stack(&p->link, &acflayer);
+ link_Stack(&p->link, &protolayer);
+ link_Stack(&p->link, &lqrlayer);
+ link_Stack(&p->link, &ccplayer);
+ link_Stack(&p->link, &vjlayer);
+ link_Stack(&p->link, &tcpmsslayer);
+#ifndef NONAT
+ link_Stack(&p->link, &natlayer);
+#endif
+ if (how == PHYSICAL_FORCE_ASYNC && physical_IsSync(p)) {
+ log_Printf(LogWARN, "Sync device setting ignored for ``%s'' device\n", who);
+ p->cfg.speed = MODEM_SPEED;
+ } else if (how == PHYSICAL_FORCE_SYNC && !physical_IsSync(p)) {
+ log_Printf(LogWARN, "Async device setting ignored for ``%s'' device\n",
+ who);
+ physical_SetSync(p);
+ }
+}
+
+void
+physical_StopDeviceTimer(struct physical *p)
+{
+ if (p->handler && p->handler->stoptimer)
+ (*p->handler->stoptimer)(p);
+}
+
+int
+physical_AwaitCarrier(struct physical *p)
+{
+ if (p->handler && p->handler->awaitcarrier)
+ return (*p->handler->awaitcarrier)(p);
+
+ return CARRIER_OK;
+}
+
+
+void
+physical_SetAsyncParams(struct physical *p, u_int32_t mymap, u_int32_t hismap)
+{
+ if (p->handler && p->handler->setasyncparams)
+ return (*p->handler->setasyncparams)(p, mymap, hismap);
+
+ async_SetLinkParams(&p->async, mymap, hismap);
+}
+
+int
+physical_Slot(struct physical *p)
+{
+ if (p->handler && p->handler->slot)
+ return (*p->handler->slot)(p);
+
+ return -1;
+}
diff --git a/usr.sbin/ppp/physical.h b/usr.sbin/ppp/physical.h
new file mode 100644
index 0000000..f1e3ec5
--- /dev/null
+++ b/usr.sbin/ppp/physical.h
@@ -0,0 +1,173 @@
+/*
+ * Written by Eivind Eklund <eivind@yes.no>
+ * for Yes Interactive
+ *
+ * Copyright (C) 1998, Yes Interactive. All rights reserved.
+ *
+ * Redistribution and use in any form is permitted. Redistribution in
+ * source form should include the above copyright and this set of
+ * conditions, because large sections american law seems to have been
+ * created by a bunch of jerks on drugs that are now illegal, forcing
+ * me to include this copyright-stuff instead of placing this in the
+ * public domain. The name of of 'Yes Interactive' or 'Eivind Eklund'
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $FreeBSD$
+ *
+ */
+
+struct datalink;
+struct bundle;
+struct iovec;
+struct physical;
+struct bundle;
+struct ccp;
+struct cmdargs;
+
+/* Device types (don't use zero, it'll be confused with NULL in physical2iov */
+#define I4B_DEVICE 1
+#define TTY_DEVICE 2
+#define TCP_DEVICE 3
+#define UDP_DEVICE 4
+#define ETHER_DEVICE 5
+#define EXEC_DEVICE 6
+#define ATM_DEVICE 7
+#define NG_DEVICE 8
+
+/* Returns from awaitcarrier() */
+#define CARRIER_PENDING 1
+#define CARRIER_OK 2
+#define CARRIER_LOST 3
+
+/* A cd ``necessity'' value */
+#define CD_VARIABLE 0
+#define CD_REQUIRED 1
+#define CD_NOTREQUIRED 2
+#define CD_DEFAULT 3
+
+struct cd {
+ unsigned necessity : 2; /* A CD_ value */
+ int delay; /* Wait this many seconds after login script */
+};
+
+struct device {
+ int type;
+ const char *name;
+ u_short mtu;
+ struct cd cd;
+
+ int (*awaitcarrier)(struct physical *);
+ int (*removefromset)(struct physical *, fd_set *, fd_set *, fd_set *);
+ int (*raw)(struct physical *);
+ void (*offline)(struct physical *);
+ void (*cooked)(struct physical *);
+ void (*setasyncparams)(struct physical *, u_int32_t, u_int32_t);
+ void (*stoptimer)(struct physical *);
+ void (*destroy)(struct physical *);
+ ssize_t (*read)(struct physical *, void *, size_t);
+ ssize_t (*write)(struct physical *, const void *, size_t);
+ void (*device2iov)(struct device *, struct iovec *, int *, int, int *, int *);
+ int (*speed)(struct physical *);
+ const char *(*openinfo)(struct physical *);
+ int (*slot)(struct physical *);
+};
+
+struct physical {
+ struct link link;
+ struct fdescriptor desc;
+ int type; /* What sort of PHYS_* link are we ? */
+ struct async async; /* Our async state */
+ struct hdlc hdlc; /* Our hdlc state */
+ int fd; /* File descriptor for this device */
+ struct mbuf *out; /* mbuf that suffered a short write */
+ int connect_count;
+ struct datalink *dl; /* my owner */
+
+ struct {
+ u_char buf[MAX_MRU]; /* Our input data buffer */
+ size_t sz;
+ } input;
+
+ struct {
+ char full[DEVICE_LEN]; /* Our current device name */
+ char *base;
+ } name;
+
+ time_t Utmp; /* Are we in utmp ? */
+ pid_t session_owner; /* HUP this when closing the link */
+
+ struct device *handler; /* device specific handler */
+
+ struct {
+ unsigned rts_cts : 1; /* Is rts/cts enabled ? */
+ unsigned parity; /* What parity is enabled? (tty flags) */
+ unsigned speed; /* tty speed */
+
+ char devlist[LINE_LEN]; /* NUL separated list of devices */
+ int ndev; /* number of devices in list */
+ struct cd cd;
+ } cfg;
+};
+
+#define field2phys(fp, name) \
+ ((struct physical *)((char *)fp - (int)(&((struct physical *)0)->name)))
+
+#define link2physical(l) \
+ ((l)->type == PHYSICAL_LINK ? field2phys(l, link) : NULL)
+
+#define descriptor2physical(d) \
+ ((d)->type == PHYSICAL_DESCRIPTOR ? field2phys(d, desc) : NULL)
+
+#define PHYSICAL_NOFORCE 1
+#define PHYSICAL_FORCE_ASYNC 2
+#define PHYSICAL_FORCE_SYNC 3
+#define PHYSICAL_FORCE_SYNCNOACF 4
+
+extern struct physical *physical_Create(struct datalink *, int);
+extern int physical_Open(struct physical *, struct bundle *);
+extern int physical_Raw(struct physical *);
+extern int physical_GetSpeed(struct physical *);
+extern int physical_SetSpeed(struct physical *, int);
+extern int physical_SetParity(struct physical *, const char *);
+extern int physical_SetRtsCts(struct physical *, int);
+extern void physical_SetSync(struct physical *);
+extern int physical_ShowStatus(struct cmdargs const *);
+extern void physical_Offline(struct physical *);
+extern void physical_Close(struct physical *);
+extern void physical_Destroy(struct physical *);
+extern struct physical *iov2physical(struct datalink *, struct iovec *, int *,
+ int, int, int *, int *);
+extern int physical2iov(struct physical *, struct iovec *, int *, int, int *,
+ int *);
+extern const char *physical_LockedDevice(struct physical *);
+extern void physical_ChangedPid(struct physical *, pid_t);
+
+extern int physical_IsSync(struct physical *);
+extern u_short physical_DeviceMTU(struct physical *);
+extern const char *physical_GetDevice(struct physical *);
+extern void physical_SetDeviceList(struct physical *, int, const char *const *);
+extern void physical_SetDevice(struct physical *, const char *);
+
+extern ssize_t physical_Read(struct physical *, void *, size_t);
+extern ssize_t physical_Write(struct physical *, const void *, size_t);
+extern int physical_doUpdateSet(struct fdescriptor *, fd_set *, fd_set *,
+ fd_set *, int *, int);
+extern int physical_IsSet(struct fdescriptor *, const fd_set *);
+extern void physical_DescriptorRead(struct fdescriptor *, struct bundle *,
+ const fd_set *);
+extern void physical_Login(struct physical *, const char *);
+extern int physical_RemoveFromSet(struct physical *, fd_set *, fd_set *,
+ fd_set *);
+extern int physical_SetMode(struct physical *, int);
+extern void physical_DeleteQueue(struct physical *);
+extern void physical_SetupStack(struct physical *, const char *, int);
+extern void physical_StopDeviceTimer(struct physical *);
+extern int physical_MaxDeviceSize(void);
+extern int physical_AwaitCarrier(struct physical *);
+extern void physical_SetDescriptor(struct physical *);
+extern void physical_SetAsyncParams(struct physical *, u_int32_t, u_int32_t);
+extern int physical_Slot(struct physical *);
diff --git a/usr.sbin/ppp/ppp.8.m4 b/usr.sbin/ppp/ppp.8.m4
new file mode 100644
index 0000000..54eccd5
--- /dev/null
+++ b/usr.sbin/ppp/ppp.8.m4
@@ -0,0 +1,5906 @@
+changequote({,})dnl
+changecom(,)dnl
+.\"
+.\" Copyright (c) 2001 Brian Somers <brian@Awfulhak.org>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd September 20, 1995
+.Dt PPP 8
+.Os
+.Sh NAME
+.Nm ppp
+.Nd Point to Point Protocol (a.k.a. user-ppp)
+.Sh SYNOPSIS
+.Nm
+.Op Fl Va mode
+.Op Fl nat
+.Op Fl quiet
+.Op Fl unit Ns Ar N
+.Op Ar system ...
+.Sh DESCRIPTION
+This is a user process
+.Em PPP
+software package.
+Normally,
+.Em PPP
+is implemented as a part of the kernel (e.g., as managed by
+.Xr pppd 8 )
+and it's thus somewhat hard to debug and/or modify its behaviour.
+However, in this implementation
+.Em PPP
+is done as a user process with the help of the
+tunnel device driver (tun).
+.Pp
+The
+.Fl nat
+flag does the equivalent of a
+.Dq nat enable yes ,
+enabling
+.Nm Ns No 's
+network address translation features.
+This allows
+.Nm
+to act as a NAT or masquerading engine for all machines on an internal
+LAN.
+ifdef({LOCALNAT},{},{Refer to
+.Xr libalias 3
+for details on the technical side of the NAT engine.
+})dnl
+Refer to the
+.Sx NETWORK ADDRESS TRANSLATION (PACKET ALIASING)
+section of this manual page for details on how to configure NAT in
+.Nm .
+.Pp
+The
+.Fl quiet
+flag tells
+.Nm
+to be silent at startup rather than displaying the mode and interface
+to standard output.
+.Pp
+The
+.Fl unit
+flag tells
+.Nm
+to only attempt to open
+.Pa /dev/tun Ns Ar N .
+Normally,
+.Nm
+will start with a value of 0 for
+.Ar N ,
+and keep trying to open a tunnel device by incrementing the value of
+.Ar N
+by one each time until it succeeds.
+If it fails three times in a row
+because the device file is missing, it gives up.
+.Pp
+The following
+.Va mode Ns No s
+are understood by
+.Nm :
+.Bl -tag -width XXX -offset XXX
+.It Fl auto
+.Nm
+opens the tun interface, configures it then goes into the background.
+The link isn't brought up until outgoing data is detected on the tun
+interface at which point
+.Nm
+attempts to bring up the link.
+Packets received (including the first one) while
+.Nm
+is trying to bring the link up will remain queued for a default of
+2 minutes.
+See the
+.Dq set choked
+command below.
+.Pp
+In
+.Fl auto
+mode, at least one
+.Dq system
+must be given on the command line (see below) and a
+.Dq set ifaddr
+must be done in the system profile that specifies a peer IP address to
+use when configuring the interface.
+Something like
+.Dq 10.0.0.1/0
+is usually appropriate.
+See the
+.Dq pmdemand
+system in
+.Pa /usr/share/examples/ppp/ppp.conf.sample
+for an example.
+.It Fl background
+Here,
+.Nm
+attempts to establish a connection with the peer immediately.
+If it succeeds,
+.Nm
+goes into the background and the parent process returns an exit code
+of 0.
+If it fails,
+.Nm
+exits with a non-zero result.
+.It Fl foreground
+In foreground mode,
+.Nm
+attempts to establish a connection with the peer immediately, but never
+becomes a daemon.
+The link is created in background mode.
+This is useful if you wish to control
+.Nm Ns No 's
+invocation from another process.
+.It Fl direct
+This is used for 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
+modes have been specified.
+.Nm
+loads any sections specified on the command line then provides an
+interactive prompt.
+.El
+.Pp
+One or more configuration entries or systems
+(as specified in
+.Pa /etc/ppp/ppp.conf )
+may also be specified on the command line.
+.Nm
+will read the
+.Dq default
+system from
+.Pa /etc/ppp/ppp.conf
+at startup, followed by each of the systems specified on the command line.
+.Sh Major Features
+.Bl -diag
+.It Provides an interactive user interface.
+Using its command mode, the user can
+easily enter commands to establish the connection with the remote end, check
+the status of connection and close the connection.
+All functions can also be optionally password protected for security.
+.It Supports both manual and automatic dialing.
+Interactive mode has a
+.Dq term
+command which enables you to talk to the device directly.
+When you are connected to the remote peer and it starts to talk
+.Em PPP ,
+.Nm
+detects it and switches to packet mode automatically.
+Once you have
+determined the proper sequence for connecting with the remote host, you
+can write a chat script to {define} the necessary dialing and login
+procedure for later convenience.
+.It Supports on-demand dialup capability.
+By using
+.Fl auto
+mode,
+.Nm
+will act as a daemon and wait for a packet to be sent over the
+.Em PPP
+link.
+When this happens, the daemon automatically dials and establishes the
+connection.
+In almost the same manner
+.Fl ddial
+mode (direct-dial mode) also automatically dials and establishes the
+connection.
+However, it differs in that it will dial the remote site
+any time it detects the link is down, even if there are no packets to be
+sent.
+This mode is useful for full-time connections where we worry less
+about line charges and more about being connected full time.
+A third
+.Fl dedicated
+mode is also available.
+This mode is targeted at a dedicated link between two machines.
+.Nm
+will never voluntarily quit from dedicated mode - you must send it the
+.Dq quit all
+command via its diagnostic socket.
+A
+.Dv SIGHUP
+will force an LCP renegotiation, and a
+.Dv SIGTERM
+will force it to exit.
+.It Supports client callback.
+.Nm
+can use either the standard LCP callback protocol or the Microsoft
+CallBack Control Protocol (ftp://ftp.microsoft.com/developr/rfc/cbcp.txt).
+.It Supports NAT or packet aliasing.
+Packet aliasing (a.k.a. IP masquerading) allows computers on a
+private, unregistered network to access the Internet.
+The
+.Em PPP
+host acts as a masquerading gateway.
+IP addresses as well as TCP and
+UDP port numbers are NAT'd for outgoing packets and de-NAT'd for
+returning packets.
+.It Supports background PPP connections.
+In background mode, if
+.Nm
+successfully establishes the connection, it will become a daemon.
+Otherwise, it will exit with an error.
+This allows the setup of
+scripts that wish to execute certain commands only if the connection
+is successfully established.
+.It Supports server-side PPP connections.
+In direct mode,
+.Nm
+acts as server which accepts incoming
+.Em PPP
+connections on stdin/stdout.
+.It "Supports PAP and CHAP (rfc 1994, 2433 and 2759) authentication.
+With PAP or CHAP, it is possible to skip the Unix style
+.Xr login 1
+procedure, and use the
+.Em PPP
+protocol for authentication instead.
+If the peer requests Microsoft CHAP authentication and
+.Nm
+is compiled with DES support, an appropriate MD4/DES response will be
+made.
+.It Supports RADIUS (rfc 2138 & 2548) authentication.
+An extension to PAP and CHAP,
+.Em \&R Ns No emote
+.Em \&A Ns No ccess
+.Em \&D Ns No ial
+.Em \&I Ns No n
+.Em \&U Ns No ser
+.Em \&S Ns No ervice
+allows authentication information to be stored in a central or
+distributed database along with various per-user framed connection
+characteristics.
+ifdef({LOCALRAD},{},{If
+.Xr libradius 3
+is available at compile time,
+.Nm
+will use it to make
+.Em RADIUS
+requests when configured to do so.
+})dnl
+.It Supports Proxy Arp.
+.Nm
+can be configured to make one or more proxy arp entries on behalf of
+the peer.
+This allows routing from the peer to the LAN without
+configuring each machine on that LAN.
+.It Supports packet filtering.
+User can {define} four kinds of filters: the
+.Em in
+filter for incoming packets, the
+.Em out
+filter for outgoing packets, the
+.Em dial
+filter to {define} a dialing trigger packet and the
+.Em alive
+filter for keeping a connection alive with the trigger packet.
+.It Tunnel driver supports bpf.
+The user can use
+.Xr tcpdump 1
+to check the packet flow over the
+.Em PPP
+link.
+.It Supports PPP over TCP and PPP over UDP.
+If a device name is specified as
+.Em host Ns No : Ns Em port Ns
+.Xo
+.Op / Ns tcp|udp ,
+.Xc
+.Nm
+will open a TCP or UDP connection for transporting data rather than using a
+conventional serial device.
+UDP connections force
+.Nm
+into synchronous mode.
+.It Supports PPP over ISDN.
+If
+.Nm
+is given a raw B-channel i4b device to open as a link, it's able to talk
+to the
+.Xr isdnd 8
+daemon to establish an ISDN connection.
+.It Supports PPP over Ethernet (rfc 2516).
+If
+.Nm
+is given a device specification of the format
+.No PPPoE: Ns Ar iface Ns Xo
+.Op \&: Ns Ar provider Ns
+.Xc
+and if
+.Xr netgraph 4
+is available,
+.Nm
+will attempt talk
+.Em PPP
+over Ethernet to
+.Ar provider
+using the
+.Ar iface
+network interface.
+.Pp
+On systems that do not support
+.Xr netgraph 4 ,
+an external program such as
+.Xr pppoe 8
+may be used.
+.It "Supports IETF draft Predictor-1 (rfc 1978) and DEFLATE (rfc 1979) compression."
+.Nm
+supports not only VJ-compression but also Predictor-1 and DEFLATE compression.
+Normally, a modem has built-in compression (e.g., v42.bis) and the system
+may receive higher data rates from it as a result of such compression.
+While this is generally a good thing in most other situations, this
+higher speed data imposes a penalty on the system by increasing the
+number of serial interrupts the system has to process in talking to the
+modem and also increases latency.
+Unlike VJ-compression, Predictor-1 and DEFLATE compression pre-compresses
+.Em all
+network traffic flowing through the link, thus reducing overheads to a
+minimum.
+.It Supports Microsoft's IPCP extensions (rfc 1877).
+Name Server Addresses and NetBIOS Name Server Addresses can be negotiated
+with clients using the Microsoft
+.Em PPP
+stack (i.e., Win95, WinNT)
+.It Supports Multi-link PPP (rfc 1990)
+It is possible to configure
+.Nm
+to open more than one physical connection to the peer, combining the
+bandwidth of all links for better throughput.
+.It Supports MPPE (draft-ietf-pppext-mppe)
+MPPE is Microsoft Point to Point Encryption scheme.
+It is possible to configure
+.Nm
+to participate in Microsoft's Windows VPN.
+For now,
+.Nm
+can only get encryption keys from CHAP 81 authentication.
+.Nm
+must be compiled with DES for MPPE to operate.
+.It Supports IPV6CP (rfc 2023).
+An IPv6 connection can be made in addition to or instead of the normal
+IPv4 connection.
+.El
+.Sh PERMISSIONS
+.Nm
+is installed as user
+.Dv root
+and group
+.Dv network ,
+with permissions
+.Dv 04554 .
+By default,
+.Nm
+will not run if the invoking user id is not zero.
+This may be overridden by using the
+.Dq allow users
+command in
+.Pa /etc/ppp/ppp.conf .
+When running as a normal user,
+.Nm
+switches to user id 0 in order to alter the system routing table, set up
+system lock files and read the ppp configuration files.
+All external commands (executed via the "shell" or "!bg" commands) are executed
+as the user id that invoked
+.Nm .
+Refer to the
+.Sq ID0
+logging facility if you're interested in what exactly is done as user id
+zero.
+.Sh GETTING STARTED
+When you first run
+.Nm
+you may need to deal with some initial configuration details.
+.Bl -bullet
+.It
+Your kernel must {include} a tunnel device (the GENERIC kernel includes
+one by default).
+If it doesn't, or if you require more than one tun
+interface, you'll need to rebuild your kernel with the following line in
+your kernel configuration file:
+.Pp
+.Dl pseudo-device tun N
+.Pp
+where
+.Ar N
+is the maximum number of
+.Em PPP
+connections you wish to support.
+.It
+Check your
+.Pa /dev
+directory for the tunnel device entries
+.Pa /dev/tunN ,
+where
+.Sq N
+represents the number of the tun device, starting at zero.
+If they don't exist, you can create them by running "sh ./MAKEDEV tunN".
+This will create tun devices 0 through
+.Ar N .
+.It
+Make sure that your system has a group named
+.Dq network
+in the
+.Pa /etc/group
+file and that the group contains the names of all users expected to use
+.Nm .
+Refer to the
+.Xr group 5
+manual page for details.
+Each of these users must also be given access using the
+.Dq allow users
+command in
+.Pa /etc/ppp/ppp.conf .
+.It
+Create a log file.
+.Nm
+uses
+.Xr syslog 3
+to log information.
+A common log file name is
+.Pa /var/log/ppp.log .
+To make output go to this file, put the following lines in the
+.Pa /etc/syslog.conf
+file:
+.Bd -literal -offset indent
+!ppp
+*.*<TAB>/var/log/ppp.log
+.Ed
+.Pp
+It is possible to have more than one
+.Em PPP
+log file by creating a link to the
+.Nm
+executable:
+.Pp
+.Dl # cd /usr/sbin
+.Dl # ln ppp ppp0
+.Pp
+and using
+.Bd -literal -offset indent
+!ppp0
+*.*<TAB>/var/log/ppp0.log
+.Ed
+.Pp
+in
+.Pa /etc/syslog.conf .
+Don't forget to send a
+.Dv HUP
+signal to
+.Xr syslogd 8
+after altering
+.Pa /etc/syslog.conf .
+.It
+Although not strictly relevant to
+.Nm Ns No 's
+operation, you should configure your resolver so that it works correctly.
+This can be done by configuring a local DNS
+(using
+.Xr named 8 )
+or by adding the correct
+.Sq nameserver
+lines to the file
+.Pa /etc/resolv.conf .
+Refer to the
+.Xr resolv.conf 5
+manual page for details.
+.Pp
+Alternatively, if the peer supports it,
+.Nm
+can be configured to ask the peer for the nameserver address(es) and to
+update
+.Pa /etc/resolv.conf
+automatically.
+Refer to the
+.Dq enable dns
+and
+.Dq resolv
+commands below for details.
+.El
+.Sh MANUAL DIALING
+In the following examples, we assume that your machine name is
+.Dv awfulhak .
+when you invoke
+.Nm
+(see
+.Sx PERMISSIONS
+above) with no arguments, you are presented with a prompt:
+.Bd -literal -offset indent
+ppp ON awfulhak>
+.Ed
+.Pp
+The
+.Sq ON
+part of your prompt should always be in upper case.
+If it is in lower case, it means that you must supply a password using the
+.Dq passwd
+command.
+This only ever happens if you connect to a running version of
+.Nm
+and have not authenticated yourself using the correct password.
+.Pp
+You can start by specifying the device name and speed:
+.Bd -literal -offset indent
+ppp ON awfulhak> set device /dev/cuaa0
+ppp ON awfulhak> set speed 38400
+.Ed
+.Pp
+Normally, hardware flow control (CTS/RTS) is used.
+However, under
+certain circumstances (as may happen when you are connected directly
+to certain PPP-capable terminal servers), this may result in
+.Nm
+hanging as soon as it tries to write data to your communications link
+as it is waiting for the CTS (clear to send) signal - which will never
+come.
+Thus, if you have a direct line and can't seem to make a
+connection, try turning CTS/RTS off with
+.Dq set ctsrts off .
+If you need to do this, check the
+.Dq set accmap
+description below too - you'll probably need to
+.Dq set accmap 000a0000 .
+.Pp
+Usually, parity is set to
+.Dq none ,
+and this is
+.Nm Ns No 's
+default.
+Parity is a rather archaic error checking mechanism that is no
+longer used because modern modems do their own error checking, and most
+link-layer protocols (that's what
+.Nm
+is) use much more reliable checking mechanisms.
+Parity has a relatively
+huge overhead (a 12.5% increase in traffic) and as a result, it is always
+disabled
+(set to
+.Dq none )
+when
+.Dv PPP
+is opened.
+However, some ISPs (Internet Service Providers) may use
+specific parity settings at connection time (before
+.Dv PPP
+is opened).
+Notably, Compuserve insist on even parity when logging in:
+.Bd -literal -offset indent
+ppp ON awfulhak> set parity even
+.Ed
+.Pp
+You can now see what your current device settings look like:
+.Bd -literal -offset indent
+ppp ON awfulhak> show physical
+Name: deflink
+ State: closed
+ Device: N/A
+ Link Type: interactive
+ Connect Count: 0
+ Queued Packets: 0
+ Phone Number: N/A
+
+Defaults:
+ Device List: /dev/cuaa0
+ Characteristics: 38400bps, cs8, even parity, CTS/RTS on
+
+Connect time: 0 secs
+0 octets in, 0 octets out
+Overall 0 bytes/sec
+ppp ON awfulhak>
+.Ed
+.Pp
+The term command can now be used to talk directly to the device:
+.Bd -literal -offset indent
+ppp ON awfulhak> term
+at
+OK
+atdt123456
+CONNECT
+login: myispusername
+Password: myisppassword
+Protocol: ppp
+.Ed
+.Pp
+When the peer starts to talk in
+.Em PPP ,
+.Nm
+detects this automatically and returns to command mode.
+.Bd -literal -offset indent
+ppp ON awfulhak> # No link has been established
+Ppp ON awfulhak> # We've connected & finished LCP
+PPp ON awfulhak> # We've authenticated
+PPP ON awfulhak> # We've agreed IP numbers
+.Ed
+.Pp
+If it does not, it's probable that the peer is waiting for your end to
+start negotiating.
+To force
+.Nm
+to start sending
+.Em PPP
+configuration packets to the peer, use the
+.Dq ~p
+command to drop out of terminal mode and enter packet mode.
+.Pp
+If you never even receive a login prompt, it is quite likely that the
+peer wants to use PAP or CHAP authentication instead of using Unix-style
+login/password authentication.
+To set things up properly, drop back to
+the prompt and set your authentication name and key, then reconnect:
+.Bd -literal -offset indent
+~.
+ppp ON awfulhak> set authname myispusername
+ppp ON awfulhak> set authkey myisppassword
+ppp ON awfulhak> term
+at
+OK
+atdt123456
+CONNECT
+.Ed
+.Pp
+You may need to tell ppp to initiate negotiations with the peer here too:
+.Bd -literal -offset indent
+~p
+ppp ON awfulhak> # No link has been established
+Ppp ON awfulhak> # We've connected & finished LCP
+PPp ON awfulhak> # We've authenticated
+PPP ON awfulhak> # We've agreed IP numbers
+.Ed
+.Pp
+You are now connected!
+Note that
+.Sq PPP
+in the prompt has changed to capital letters to indicate that you have
+a peer connection.
+If only some of the three Ps go uppercase, wait until
+either everything is uppercase or lowercase.
+If they revert to lowercase, it means that
+.Nm
+couldn't successfully negotiate with the peer.
+A good first step for troubleshooting at this point would be to
+.Bd -literal -offset indent
+ppp ON awfulhak> set log local phase lcp ipcp
+.Ed
+.Pp
+and try again.
+Refer to the
+.Dq set log
+command description below for further details.
+If things fail at this point,
+it is quite important that you turn logging on and try again.
+It is also
+important that you note any prompt changes and report them to anyone trying
+to help you.
+.Pp
+When the link is established, the show command can be used to see how
+things are going:
+.Bd -literal -offset indent
+PPP ON awfulhak> show physical
+* Modem related information is shown here *
+PPP ON awfulhak> show ccp
+* CCP (compression) related information is shown here *
+PPP ON awfulhak> show lcp
+* LCP (line control) related information is shown here *
+PPP ON awfulhak> show ipcp
+* IPCP (IP) related information is shown here *
+PPP ON awfulhak> show ipv6cp
+* IPV6CP (IPv6) related information is shown here *
+PPP ON awfulhak> show link
+* Link (high level) related information is shown here *
+PPP ON awfulhak> show bundle
+* Logical (high level) connection related information is shown here *
+.Ed
+.Pp
+At this point, your machine has a host route to the peer.
+This means
+that you can only make a connection with the host on the other side
+of the link.
+If you want to add a default route entry (telling your
+machine to send all packets without another routing entry to the other
+side of the
+.Em PPP
+link), enter the following command:
+.Bd -literal -offset indent
+PPP ON awfulhak> add default HISADDR
+.Ed
+.Pp
+The string
+.Sq HISADDR
+represents the IP address of the connected peer.
+If the
+.Dq add
+command fails due to an existing route, you can overwrite the existing
+route using
+.Bd -literal -offset indent
+PPP ON awfulhak> add! default HISADDR
+.Ed
+.Pp
+This command can also be executed before actually making the connection.
+If a new IP address is negotiated at connection time,
+.Nm
+will update your default route accordingly.
+.Pp
+You can now use your network applications (ping, telnet, ftp etc.)
+in other windows or terminals on your machine.
+If you wish to reuse the current terminal, you can put
+.Nm
+into the background using your standard shell suspend and background
+commands (usually
+.Dq ^Z
+followed by
+.Dq bg ) .
+.Pp
+Refer to the
+.Sx PPP COMMAND LIST
+section for details on all available commands.
+.Sh AUTOMATIC DIALING
+To use automatic dialing, you must prepare some Dial and Login chat scripts.
+See the example definitions in
+.Pa /usr/share/examples/ppp/ppp.conf.sample
+(the format of
+.Pa /etc/ppp/ppp.conf
+is pretty simple).
+Each line contains one comment, inclusion, label or command:
+.Bl -bullet
+.It
+A line starting with a
+.Pq Dq #
+character is treated as a comment line.
+Leading whitespace are ignored when identifying comment lines.
+.It
+An inclusion is a line beginning with the word
+.Sq {!include} .
+It must have one argument - the file to {include}.
+You may wish to
+.Dq {!include} ~/.ppp.conf
+for compatibility with older versions of
+.Nm .
+.It
+A label name starts in the first column and is followed by
+a colon
+.Pq Dq \&: .
+.It
+A command line must contain a space or tab in the first column.
+.El
+.Pp
+The
+.Pa /etc/ppp/ppp.conf
+file should consist of at least a
+.Dq default
+section.
+This section is always executed.
+It should also contain
+one or more sections, named according to their purpose, for example,
+.Dq MyISP
+would represent your ISP, and
+.Dq ppp-in
+would represent an incoming
+.Nm
+configuration.
+You can now specify the destination label name when you invoke
+.Nm .
+Commands associated with the
+.Dq default
+label are executed, followed by those associated with the destination
+label provided.
+When
+.Nm
+is started with no arguments, the
+.Dq default
+section is still executed.
+The load command can be used to manually load a section from the
+.Pa /etc/ppp/ppp.conf
+file:
+.Bd -literal -offset indent
+ppp ON awfulhak> load MyISP
+.Ed
+.Pp
+Note, no action is taken by
+.Nm
+after a section is loaded, whether it's the result of passing a label on
+the command line or using the
+.Dq load
+command.
+Only the commands specified for that label in the configuration
+file are executed.
+However, when invoking
+.Nm
+with the
+.Fl background ,
+.Fl ddial ,
+or
+.Fl dedicated
+switches, the link mode tells
+.Nm
+to establish a connection.
+Refer to the
+.Dq set mode
+command below for further details.
+.Pp
+Once the connection is made, the
+.Sq ppp
+portion of the prompt will change to
+.Sq PPP :
+.Bd -literal -offset indent
+# ppp MyISP
+\&...
+ppp ON awfulhak> dial
+Ppp ON awfulhak>
+PPp ON awfulhak>
+PPP ON awfulhak>
+.Ed
+.Pp
+The Ppp prompt indicates that
+.Nm
+has entered the authentication phase.
+The PPp prompt indicates that
+.Nm
+has entered the network phase.
+The PPP prompt indicates that
+.Nm
+has successfully negotiated a network layer protocol and is in
+a usable state.
+.Pp
+If the
+.Pa /etc/ppp/ppp.linkup
+file is available, its contents are executed
+when the
+.Em PPP
+connection is established.
+See the provided
+.Dq pmdemand
+example in
+.Pa /usr/share/examples/ppp/ppp.conf.sample
+which runs a script in the background after the connection is established
+(refer to the
+.Dq shell
+and
+.Dq bg
+commands below for a description of possible substitution strings).
+Similarly, when a connection is closed, the contents of the
+.Pa /etc/ppp/ppp.linkdown
+file are executed.
+Both of these files have the same format as
+.Pa /etc/ppp/ppp.conf .
+.Pp
+In previous versions of
+.Nm ,
+it was necessary to re-add routes such as the default route in the
+.Pa ppp.linkup
+file.
+.Nm
+supports
+.Sq sticky routes ,
+where all routes that contain the
+.Dv HISADDR ,
+.Dv MYADDR ,
+.Dv HISADDR6
+or
+.Dv MYADDR6
+literals will automatically be updated when the values of these variables
+change.
+.Sh BACKGROUND DIALING
+If you want to establish a connection using
+.Nm
+non-interactively (such as from a
+.Xr crontab 5
+entry or an
+.Xr at 1
+job) you should use the
+.Fl background
+option.
+When
+.Fl background
+is specified,
+.Nm
+attempts to establish the connection immediately.
+If multiple phone
+numbers are specified, each phone number will be tried once.
+If the attempt fails,
+.Nm
+exits immediately with a non-zero exit code.
+If it succeeds, then
+.Nm
+becomes a daemon, and returns an exit status of zero to its caller.
+The daemon exits automatically if the connection is dropped by the
+remote system, or it receives a
+.Dv TERM
+signal.
+.Sh DIAL ON DEMAND
+Demand dialing is enabled with the
+.Fl auto
+or
+.Fl ddial
+options.
+You must also specify the destination label in
+.Pa /etc/ppp/ppp.conf
+to use.
+It must contain the
+.Dq set ifaddr
+command to {define} the remote peers IP address.
+(refer to
+.Pa /usr/share/examples/ppp/ppp.conf.sample )
+.Bd -literal -offset indent
+# ppp -auto pmdemand
+.Ed
+.Pp
+When
+.Fl auto
+or
+.Fl ddial
+is specified,
+.Nm
+runs as a daemon but you can still configure or examine its
+configuration by using the
+.Dq set server
+command in
+.Pa /etc/ppp/ppp.conf ,
+(for example,
+.Dq Li "set server +3000 mypasswd" )
+and connecting to the diagnostic port as follows:
+.Bd -literal -offset indent
+# pppctl 3000 (assuming tun0)
+Password:
+PPP ON awfulhak> show who
+tcp (127.0.0.1:1028) *
+.Ed
+.Pp
+The
+.Dq show who
+command lists users that are currently connected to
+.Nm
+itself.
+If the diagnostic socket is closed or changed to a different
+socket, all connections are immediately dropped.
+.Pp
+In
+.Fl auto
+mode, when an outgoing packet is detected,
+.Nm
+will perform the dialing action (chat script) and try to connect
+with the peer.
+In
+.Fl ddial
+mode, the dialing action is performed any time the line is found
+to be down.
+If the connect fails, the default behaviour is to wait 30 seconds
+and then attempt to connect when another outgoing packet is detected.
+This behaviour can be changed using the
+.Dq set redial
+command:
+.Pp
+.No set redial Ar secs Ns Xo
+.Oo + Ns Ar inc Ns
+.Op - Ns Ar max Ns
+.Oc Ns Op . Ns Ar next
+.Op Ar attempts
+.Xc
+.Pp
+.Bl -tag -width attempts -compact
+.It Ar secs
+is the number of seconds to wait before attempting
+to connect again.
+If the argument is the literal string
+.Sq Li random ,
+the delay period is a random value between 1 and 30 seconds inclusive.
+.It Ar inc
+is the number of seconds that
+.Ar secs
+should be incremented each time a new dial attempt is made.
+The timeout reverts to
+.Ar secs
+only after a successful connection is established.
+The default value for
+.Ar inc
+is zero.
+.It Ar max
+is the maximum number of times
+.Nm
+should increment
+.Ar secs .
+The default value for
+.Ar max
+is 10.
+.It Ar next
+is the number of seconds to wait before attempting
+to dial the next number in a list of numbers (see the
+.Dq set phone
+command).
+The default is 3 seconds.
+Again, if the argument is the literal string
+.Sq Li random ,
+the delay period is a random value between 1 and 30 seconds.
+.It Ar attempts
+is the maximum number of times to try to connect for each outgoing packet
+that triggers a dial.
+The previous value is unchanged if this parameter is omitted.
+If a value of zero is specified for
+.Ar attempts ,
+.Nm
+will keep trying until a connection is made.
+.El
+.Pp
+So, for example:
+.Bd -literal -offset indent
+set redial 10.3 4
+.Ed
+.Pp
+will attempt to connect 4 times for each outgoing packet that causes
+a dial attempt with a 3 second delay between each number and a 10 second
+delay after all numbers have been tried.
+If multiple phone numbers
+are specified, the total number of attempts is still 4 (it does not
+attempt each number 4 times).
+.Pp
+Alternatively,
+.Pp
+.Bd -literal -offset indent
+set redial 10+10-5.3 20
+.Ed
+.Pp
+tells
+.Nm
+to attempt to connect 20 times.
+After the first attempt,
+.Nm
+pauses for 10 seconds.
+After the next attempt it pauses for 20 seconds
+and so on until after the sixth attempt it pauses for 1 minute.
+The next 14 pauses will also have a duration of one minute.
+If
+.Nm
+connects, disconnects and fails to connect again, the timeout starts again
+at 10 seconds.
+.Pp
+Modifying the dial delay is very useful when running
+.Nm
+in
+.Fl auto
+mode on both ends of the link.
+If each end has the same timeout,
+both ends wind up calling each other at the same time if the link
+drops and both ends have packets queued.
+At some locations, the serial link may not be reliable, and carrier
+may be lost at inappropriate times.
+It is possible to have
+.Nm
+redial should carrier be unexpectedly lost during a session.
+.Bd -literal -offset indent
+set reconnect timeout ntries
+.Ed
+.Pp
+This command tells
+.Nm
+to re-establish the connection
+.Ar ntries
+times on loss of carrier with a pause of
+.Ar timeout
+seconds before each try.
+For example,
+.Bd -literal -offset indent
+set reconnect 3 5
+.Ed
+.Pp
+tells
+.Nm
+that on an unexpected loss of carrier, it should wait
+.Ar 3
+seconds before attempting to reconnect.
+This may happen up to
+.Ar 5
+times before
+.Nm
+gives up.
+The default value of ntries is zero (no reconnect).
+Care should be taken with this option.
+If the local timeout is slightly
+longer than the remote timeout, the reconnect feature will always be
+triggered (up to the given number of times) after the remote side
+times out and hangs up.
+NOTE: In this context, losing too many LQRs constitutes a loss of
+carrier and will trigger a reconnect.
+If the
+.Fl background
+flag is specified, all phone numbers are dialed at most once until
+a connection is made.
+The next number redial period specified with the
+.Dq set redial
+command is honoured, as is the reconnect tries value.
+If your redial
+value is less than the number of phone numbers specified, not all
+the specified numbers will be tried.
+To terminate the program, type
+.Bd -literal -offset indent
+PPP ON awfulhak> close
+ppp ON awfulhak> quit all
+.Ed
+.Pp
+A simple
+.Dq quit
+command will terminate the
+.Xr pppctl 8
+or
+.Xr telnet 1
+connection but not the
+.Nm
+program itself.
+You must use
+.Dq quit all
+to terminate
+.Nm
+as well.
+.Sh RECEIVING INCOMING PPP CONNECTIONS (Method 1)
+To handle an incoming
+.Em PPP
+connection request, follow these steps:
+.Bl -enum
+.It
+Make sure the modem and (optionally)
+.Pa /etc/rc.serial
+is configured correctly.
+.Bl -bullet -compact
+.It
+Use Hardware Handshake (CTS/RTS) for flow control.
+.It
+Modem should be set to NO echo back (ATE0) and NO results string (ATQ1).
+.El
+.Pp
+.It
+Edit
+.Pa /etc/ttys
+to enable a
+.Xr getty 8
+on the port where the modem is attached.
+For example:
+.Pp
+.Dl ttyd1 Qo /usr/libexec/getty std.38400 Qc dialup on secure
+.Pp
+Don't forget to send a
+.Dv HUP
+signal to the
+.Xr init 8
+process to start the
+.Xr getty 8 :
+.Pp
+.Dl # kill -HUP 1
+.Pp
+It is usually also necessary to train your modem to the same DTR speed
+as the getty:
+.Bd -literal -offset indent
+# ppp
+ppp ON awfulhak> set device /dev/cuaa1
+ppp ON awfulhak> set speed 38400
+ppp ON awfulhak> term
+deflink: Entering terminal mode on /dev/cuaa1
+Type `~?' for help
+at
+OK
+at
+OK
+atz
+OK
+at
+OK
+~.
+ppp ON awfulhak> quit
+.Ed
+.It
+Create a
+.Pa /usr/local/bin/ppplogin
+file with the following contents:
+.Bd -literal -offset indent
+#! /bin/sh
+exec /usr/sbin/ppp -direct incoming
+.Ed
+.Pp
+Direct mode
+.Pq Fl direct
+lets
+.Nm
+work with stdin and stdout.
+You can also use
+.Xr pppctl 8
+to connect to a configured diagnostic port, in the same manner as with
+client-side
+.Nm .
+.Pp
+Here, the
+.Ar incoming
+section must be set up in
+.Pa /etc/ppp/ppp.conf .
+.Pp
+Make sure that the
+.Ar incoming
+section contains the
+.Dq allow users
+command as appropriate.
+.It
+Prepare an account for the incoming user.
+.Bd -literal
+ppp:xxxx:66:66:PPP Login User:/home/ppp:/usr/local/bin/ppplogin
+.Ed
+.Pp
+Refer to the manual entries for
+.Xr adduser 8
+and
+.Xr vipw 8
+for details.
+.It
+Support for IPCP Domain Name Server and NetBIOS Name Server negotiation
+can be enabled using the
+.Dq accept dns
+and
+.Dq set nbns
+commands.
+Refer to their descriptions below.
+.El
+.Sh RECEIVING INCOMING PPP CONNECTIONS (Method 2)
+This method differs in that we use
+.Nm
+to authenticate the connection rather than
+.Xr login 1 :
+.Bl -enum
+.It
+Configure your default section in
+.Pa /etc/gettytab
+with automatic ppp recognition by specifying the
+.Dq pp
+capability:
+.Bd -literal
+default:\\
+ :pp=/usr/local/bin/ppplogin:\\
+ .....
+.Ed
+.It
+Configure your serial device(s), enable a
+.Xr getty 8
+and create
+.Pa /usr/local/bin/ppplogin
+as in the first three steps for method 1 above.
+.It
+Add either
+.Dq enable chap
+or
+.Dq enable pap
+(or both)
+to
+.Pa /etc/ppp/ppp.conf
+under the
+.Sq incoming
+label (or whatever label
+.Pa ppplogin
+uses).
+.It
+Create an entry in
+.Pa /etc/ppp/ppp.secret
+for each incoming user:
+.Bd -literal
+Pfred<TAB>xxxx
+Pgeorge<TAB>yyyy
+.Ed
+.El
+.Pp
+Now, as soon as
+.Xr getty 8
+detects a ppp connection (by recognising the HDLC frame headers), it runs
+.Dq /usr/local/bin/ppplogin .
+.Pp
+It is
+.Em VITAL
+that either PAP or CHAP are enabled as above.
+If they are not, you are
+allowing anybody to establish a ppp session with your machine
+.Em without
+a password, opening yourself up to all sorts of potential attacks.
+.Sh AUTHENTICATING INCOMING CONNECTIONS
+Normally, the receiver of a connection requires that the peer
+authenticates itself.
+This may be done using
+.Xr login 1 ,
+but alternatively, you can use PAP or CHAP.
+CHAP is the more secure of the two, but some clients may not support it.
+Once you decide which you wish to use, add the command
+.Sq enable chap
+or
+.Sq enable pap
+to the relevant section of
+.Pa ppp.conf .
+.Pp
+You must then configure the
+.Pa /etc/ppp/ppp.secret
+file.
+This file contains one line per possible client, each line
+containing up to five fields:
+.Pp
+.Ar name Ar key Oo
+.Ar hisaddr Op Ar label Op Ar callback-number
+.Oc
+.Pp
+The
+.Ar name
+and
+.Ar key
+specify the client username and password.
+If
+.Ar key
+is
+.Dq \&*
+and PAP is being used,
+.Nm
+will look up the password database
+.Pq Xr passwd 5
+when authenticating.
+If the client does not offer a suitable response based on any
+.Ar name Ns No / Ns Ar key
+combination in
+.Pa ppp.secret ,
+authentication fails.
+.Pp
+If authentication is successful,
+.Ar hisaddr
+(if specified)
+is used when negotiating IP numbers.
+See the
+.Dq set ifaddr
+command for details.
+.Pp
+If authentication is successful and
+.Ar label
+is specified, the current system label is changed to match the given
+.Ar label .
+This will change the subsequent parsing of the
+.Pa ppp.linkup
+and
+.Pa ppp.linkdown
+files.
+.Pp
+If authentication is successful and
+.Ar callback-number
+is specified and
+.Dq set callback
+has been used in
+.Pa ppp.conf ,
+the client will be called back on the given number.
+If CBCP is being used,
+.Ar callback-number
+may also contain a list of numbers or a
+.Dq \&* ,
+as if passed to the
+.Dq set cbcp
+command.
+The value will be used in
+.Nm Ns No 's
+subsequent CBCP phase.
+.Sh PPP OVER TCP and UDP (a.k.a Tunnelling)
+Instead of running
+.Nm
+over a serial link, it is possible to
+use a TCP connection instead by specifying the host, port and protocol as the
+device:
+.Pp
+.Dl set device ui-gate:6669/tcp
+.Pp
+Instead of opening a serial device,
+.Nm
+will open a TCP connection to the given machine on the given
+socket.
+It should be noted however that
+.Nm
+doesn't use the telnet protocol and will be unable to negotiate
+with a telnet server.
+You should set up a port for receiving this
+.Em PPP
+connection on the receiving machine (ui-gate).
+This is done by first updating
+.Pa /etc/services
+to name the service:
+.Pp
+.Dl ppp-in 6669/tcp # Incoming PPP connections over TCP
+.Pp
+and updating
+.Pa /etc/inetd.conf
+to tell
+.Xr inetd 8
+how to deal with incoming connections on that port:
+.Pp
+.Dl ppp-in stream tcp nowait root /usr/sbin/ppp ppp -direct ppp-in
+.Pp
+Don't forget to send a
+.Dv HUP
+signal to
+.Xr inetd 8
+after you've updated
+.Pa /etc/inetd.conf .
+Here, we use a label named
+.Dq ppp-in .
+The entry in
+.Pa /etc/ppp/ppp.conf
+on ui-gate (the receiver) should contain the following:
+.Bd -literal -offset indent
+ppp-in:
+ set timeout 0
+ set ifaddr 10.0.4.1 10.0.4.2
+.Ed
+.Pp
+and the entry in
+.Pa /etc/ppp/ppp.linkup
+should contain:
+.Bd -literal -offset indent
+ppp-in:
+ add 10.0.1.0/24 HISADDR
+.Ed
+.Pp
+It is necessary to put the
+.Dq add
+command in
+.Pa ppp.linkup
+to ensure that the route is only added after
+.Nm
+has negotiated and assigned addresses to its interface.
+.Pp
+You may also want to enable PAP or CHAP for security.
+To enable PAP, add the following line:
+.Bd -literal -offset indent
+ enable PAP
+.Ed
+.Pp
+You'll also need to create the following entry in
+.Pa /etc/ppp/ppp.secret :
+.Bd -literal -offset indent
+MyAuthName MyAuthPasswd
+.Ed
+.Pp
+If
+.Ar MyAuthPasswd
+is a
+.Dq * ,
+the password is looked up in the
+.Xr passwd 5
+database.
+.Pp
+The entry in
+.Pa /etc/ppp/ppp.conf
+on awfulhak (the initiator) should contain the following:
+.Bd -literal -offset indent
+ui-gate:
+ set escape 0xff
+ set device ui-gate:ppp-in/tcp
+ set dial
+ set timeout 30
+ set log Phase Chat Connect hdlc LCP IPCP IPV6CP CCP tun
+ set ifaddr 10.0.4.2 10.0.4.1
+.Ed
+.Pp
+with the route setup in
+.Pa /etc/ppp/ppp.linkup :
+.Bd -literal -offset indent
+ui-gate:
+ add 10.0.2.0/24 HISADDR
+.Ed
+.Pp
+Again, if you're enabling PAP, you'll also need this in the
+.Pa /etc/ppp/ppp.conf
+profile:
+.Bd -literal -offset indent
+ set authname MyAuthName
+ set authkey MyAuthKey
+.Ed
+.Pp
+We're assigning the address of 10.0.4.1 to ui-gate, and the address
+10.0.4.2 to awfulhak.
+To open the connection, just type
+.Pp
+.Dl awfulhak # ppp -background ui-gate
+.Pp
+The result will be an additional "route" on awfulhak to the
+10.0.2.0/24 network via the TCP connection, and an additional
+"route" on ui-gate to the 10.0.1.0/24 network.
+The networks are effectively bridged - the underlying TCP
+connection may be across a public network (such as the
+Internet), and the
+.Em PPP
+traffic is conceptually encapsulated
+(although not packet by packet) inside the TCP stream between
+the two gateways.
+.Pp
+The major disadvantage of this mechanism is that there are two
+"guaranteed delivery" mechanisms in place - the underlying TCP
+stream and whatever protocol is used over the
+.Em PPP
+link - probably TCP again.
+If packets are lost, both levels will
+get in each others way trying to negotiate sending of the missing
+packet.
+.Pp
+To avoid this overhead, it is also possible to do all this using
+UDP instead of TCP as the transport by simply changing the protocol
+from "tcp" to "udp".
+When using UDP as a transport,
+.Nm
+will operate in synchronous mode.
+This is another gain as the incoming
+data does not have to be rearranged into packets.
+.Pp
+Care should be taken when adding a default route through a tunneled
+setup like this.
+It is quite common for the default route
+(added in
+.Pa /etc/ppp/ppp.linkup )
+to end up routing the link's TCP connection through the tunnel,
+effectively garrotting the connection.
+To avoid this, make sure you add a static route for the benefit of
+the link:
+.Bd -literal -offset indent
+ui-gate:
+ set escape 0xff
+ set device ui-gate:ppp-in/tcp
+ add ui-gate x.x.x.x
+ .....
+.Ed
+.Pp
+where
+.Dq x.x.x.x
+is the IP number that your route to
+.Dq ui-gate
+would normally use.
+.Pp
+When routing your connection accross a public network such as the Internet,
+it is preferable to encrypt the data.
+This can be done with the help of the MPPE protocol, although currently this
+means that you will not be able to also compress the traffic as MPPE is
+implemented as a compression layer (thank Microsoft for this).
+To enable MPPE encryption, add the following lines to
+.Pa /etc/ppp/ppp.conf
+on the server:
+.Bd -literal -offset indent
+ enable MSCHAPv2
+ disable deflate pred1
+ deny deflate pred1
+.Ed
+.Pp
+ensuring that you've put the requisite entry in
+.Pa /etc/ppp/ppp.secret
+(MSCHAPv2 is challenge based, so
+.Xr passwd 5
+cannot be used)
+.Pp
+MSCHAPv2 and MPPE are accepted by default, so the client end should work
+without any additional changes (although ensure you have
+.Dq set authname
+and
+.Dq set authkey
+in your profile).
+.Sh NETWORK ADDRESS TRANSLATION (PACKET ALIASING)
+The
+.Fl nat
+command line option enables network address translation (a.k.a. packet
+aliasing).
+This allows the
+.Nm
+host to act as a masquerading gateway for other computers over
+a local area network.
+Outgoing IP packets are NAT'd so that they appear to come from the
+.Nm
+host, and incoming packets are de-NAT'd so that they are routed
+to the correct machine on the local area network.
+NAT allows computers on private, unregistered subnets to have Internet
+access, although they are invisible from the outside world.
+In general, correct
+.Nm
+operation should first be verified with network address translation disabled.
+Then, the
+.Fl nat
+option should be switched on, and network applications (web browser,
+.Xr telnet 1 ,
+.Xr ftp 1 ,
+.Xr ping 8 ,
+.Xr traceroute 8 )
+should be checked on the
+.Nm
+host.
+Finally, the same or similar applications should be checked on other
+computers in the LAN.
+If network applications work correctly on the
+.Nm
+host, but not on other machines in the LAN, then the masquerading
+software is working properly, but the host is either not forwarding
+or possibly receiving IP packets.
+Check that IP forwarding is enabled in
+.Pa /etc/rc.conf
+and that other machines have designated the
+.Nm
+host as the gateway for the LAN.
+.Sh PACKET FILTERING
+This implementation supports packet filtering.
+There are four kinds of
+filters: the
+.Em in
+filter, the
+.Em out
+filter, the
+.Em dial
+filter and the
+.Em alive
+filter.
+Here are the basics:
+.Bl -bullet
+.It
+A filter definition has the following syntax:
+.Pp
+set filter
+.Ar name
+.Ar rule-no
+.Ar action
+.Op !\&
+.Oo
+.Op host
+.Ar src_addr Ns Op / Ns Ar width
+.Op Ar dst_addr Ns Op / Ns Ar width
+.Oc
+.Ar [ proto Op src Ar cmp port
+.Op dst Ar cmp port
+.Op estab
+.Op syn
+.Op finrst
+.Op timeout Ar secs ]
+.Bl -enum
+.It
+.Ar Name
+should be one of
+.Sq in ,
+.Sq out ,
+.Sq dial
+or
+.Sq alive .
+.It
+.Ar Rule-no
+is a numeric value between
+.Sq 0
+and
+.Sq 39
+specifying the rule number.
+Rules are specified in numeric order according to
+.Ar rule-no ,
+but only if rule
+.Sq 0
+is defined.
+.It
+.Ar Action
+may be specified as
+.Sq permit
+or
+.Sq deny ,
+in which case, if a given packet matches the rule, the associated action
+is taken immediately.
+.Ar Action
+can also be specified as
+.Sq clear
+to clear the action associated with that particular rule, or as a new
+rule number greater than the current rule.
+In this case, if a given
+packet matches the current rule, the packet will next be matched against
+the new rule number (rather than the next rule number).
+.Pp
+The
+.Ar action
+may optionally be followed with an exclamation mark
+.Pq Dq !\& ,
+telling
+.Nm
+to reverse the sense of the following match.
+.It
+.Op Ar src_addr Ns Op / Ns Ar width
+and
+.Op Ar dst_addr Ns Op / Ns Ar width
+are the source and destination IP number specifications.
+If
+.Op / Ns Ar width
+is specified, it gives the number of relevant netmask bits,
+allowing the specification of an address range.
+.Pp
+Either
+.Ar src_addr
+or
+.Ar dst_addr
+may be given the values
+.Dv MYADDR ,
+.Dv HISADDR ,
+.Dv MYADDR6
+or
+.Dv HISADDR6
+(refer to the description of the
+.Dq bg
+command for a description of these values).
+When these values are used,
+the filters will be updated any time the values change.
+This is similar to the behaviour of the
+.Dq add
+command below.
+.It
+.Ar Proto
+may be any protocol from
+.Xr protocols 5 .
+.It
+.Ar Cmp
+is one of
+.Sq \&lt ,
+.Sq \&eq
+or
+.Sq \&gt ,
+meaning less-than, equal and greater-than respectively.
+.Ar Port
+can be specified as a numeric port or by service name from
+.Pa /etc/services .
+.It
+The
+.Sq estab ,
+.Sq syn ,
+and
+.Sq finrst
+flags are only allowed when
+.Ar proto
+is set to
+.Sq tcp ,
+and represent the TH_ACK, TH_SYN and TH_FIN or TH_RST TCP flags respectively.
+.It
+The timeout value adjusts the current idle timeout to at least
+.Ar secs
+seconds.
+If a timeout is given in the alive filter as well as in the in/out
+filter, the in/out value is used.
+If no timeout is given, the default timeout (set using
+.Ic set timeout
+and defaulting to 180 seconds) is used.
+.El
+.Pp
+.It
+Each filter can hold up to 40 rules, starting from rule 0.
+The entire rule set is not effective until rule 0 is defined,
+i.e., the default is to allow everything through.
+.It
+If no rule in a defined set of rules matches a packet, that packet will
+be discarded (blocked).
+If there are no rules in a given filter, the packet will be permitted.
+.It
+It's possible to filter based on the payload of UDP frames where those
+frames contain a
+.Em PROTO_IP
+.Em PPP
+frame header.
+See the
+.Ar filter-decapsulation
+option below for further details.
+.It
+Use
+.Dq set filter Ar name No -1
+to flush all rules.
+.El
+.Pp
+See
+.Pa /usr/share/examples/ppp/ppp.conf.sample .
+.Sh SETTING THE IDLE TIMER
+To check/set the idle timer, use the
+.Dq show bundle
+and
+.Dq set timeout
+commands:
+.Bd -literal -offset indent
+ppp ON awfulhak> set timeout 600
+.Ed
+.Pp
+The timeout period is measured in seconds, the default value for which
+is 180 seconds
+(or 3 min).
+To disable the idle timer function, use the command
+.Bd -literal -offset indent
+ppp ON awfulhak> set timeout 0
+.Ed
+.Pp
+In
+.Fl ddial
+and
+.Fl dedicated
+modes, the idle timeout is ignored.
+In
+.Fl auto
+mode, when the idle timeout causes the
+.Em PPP
+session to be
+closed, the
+.Nm
+program itself remains running.
+Another trigger packet will cause it to attempt to re-establish the link.
+.Sh PREDICTOR-1 and DEFLATE COMPRESSION
+.Nm
+supports both Predictor type 1 and deflate compression.
+By default,
+.Nm
+will attempt to use (or be willing to accept) both compression protocols
+when the peer agrees
+(or requests them).
+The deflate protocol is preferred by
+.Nm .
+Refer to the
+.Dq disable
+and
+.Dq deny
+commands if you wish to disable this functionality.
+.Pp
+It is possible to use a different compression algorithm in each direction
+by using only one of
+.Dq disable deflate
+and
+.Dq deny deflate
+(assuming that the peer supports both algorithms).
+.Pp
+By default, when negotiating DEFLATE,
+.Nm
+will use a window size of 15.
+Refer to the
+.Dq set deflate
+command if you wish to change this behaviour.
+.Pp
+A special algorithm called DEFLATE24 is also available, and is disabled
+and denied by default.
+This is exactly the same as DEFLATE except that
+it uses CCP ID 24 to negotiate.
+This allows
+.Nm
+to successfully negotiate DEFLATE with
+.Nm pppd
+version 2.3.*.
+.Sh CONTROLLING IP ADDRESS
+For IPv4,
+.Nm
+uses IPCP to negotiate IP addresses.
+Each side of the connection
+specifies the IP address that it's willing to use, and if the requested
+IP address is acceptable then
+.Nm
+returns an ACK to the requester.
+Otherwise,
+.Nm
+returns NAK to suggest that the peer use a different IP address.
+When
+both sides of the connection agree to accept the received request (and
+send an ACK), IPCP is set to the open state and a network level connection
+is established.
+To control this IPCP behaviour, this implementation has the
+.Dq set ifaddr
+command for defining the local and remote IP address:
+.Bd -ragged -offset indent
+.No set ifaddr Oo Ar src_addr Ns
+.Op / Ns Ar \&nn
+.Oo Ar dst_addr Ns Op / Ns Ar \&nn
+.Oo Ar netmask
+.Op Ar trigger_addr
+.Oc
+.Oc
+.Oc
+.Ed
+.Pp
+where,
+.Sq src_addr
+is the IP address that the local side is willing to use,
+.Sq dst_addr
+is the IP address which the remote side should use and
+.Sq netmask
+is the netmask that should be used.
+.Sq Src_addr
+defaults to the current
+.Xr hostname 1 ,
+.Sq dst_addr
+defaults to 0.0.0.0, and
+.Sq netmask
+defaults to whatever mask is appropriate for
+.Sq src_addr .
+It is only possible to make
+.Sq netmask
+smaller than the default.
+The usual value is 255.255.255.255, as
+most kernels ignore the netmask of a POINTOPOINT interface.
+.Pp
+Some incorrect
+.Em PPP
+implementations require that the peer negotiates a specific IP
+address instead of
+.Sq src_addr .
+If this is the case,
+.Sq trigger_addr
+may be used to specify this IP number.
+This will not affect the
+routing table unless the other side agrees with this proposed number.
+.Bd -literal -offset indent
+set ifaddr 192.244.177.38 192.244.177.2 255.255.255.255 0.0.0.0
+.Ed
+.Pp
+The above specification means:
+.Pp
+.Bl -bullet -compact
+.It
+I will first suggest that my IP address should be 0.0.0.0, but I
+will only accept an address of 192.244.177.38.
+.It
+I strongly insist that the peer uses 192.244.177.2 as his own
+address and won't permit the use of any IP address but 192.244.177.2.
+When the peer requests another IP address, I will always suggest that
+it uses 192.244.177.2.
+.It
+The routing table entry will have a netmask of 0xffffffff.
+.El
+.Pp
+This is all fine when each side has a pre-determined IP address, however
+it is often the case that one side is acting as a server which controls
+all IP addresses and the other side should go along with it.
+In order to allow more flexible behaviour, the
+.Dq set ifaddr
+command allows the user to specify IP addresses more loosely:
+.Pp
+.Dl set ifaddr 192.244.177.38/24 192.244.177.2/20
+.Pp
+A number followed by a slash
+.Pq Dq /
+represents the number of bits significant in the IP address.
+The above example means:
+.Pp
+.Bl -bullet -compact
+.It
+I'd like to use 192.244.177.38 as my address if it is possible, but I'll
+also accept any IP address between 192.244.177.0 and 192.244.177.255.
+.It
+I'd like to make him use 192.244.177.2 as his own address, but I'll also
+permit him to use any IP address between 192.244.176.0 and
+192.244.191.255.
+.It
+As you may have already noticed, 192.244.177.2 is equivalent to saying
+192.244.177.2/32.
+.It
+As an exception, 0 is equivalent to 0.0.0.0/0, meaning that I have no
+preferred IP address and will obey the remote peers selection.
+When using zero, no routing table entries will be made until a connection
+is established.
+.It
+192.244.177.2/0 means that I'll accept/permit any IP address but I'll
+suggest that 192.244.177.2 be used first.
+.El
+.Pp
+When negotiating IPv6 addresses, no control is given to the user.
+IPV6CP negotiation is fully automatic.
+.Sh CONNECTING WITH YOUR INTERNET SERVICE PROVIDER
+The following steps should be taken when connecting to your ISP:
+.Bl -enum
+.It
+Describe your providers phone number(s) in the dial script using the
+.Dq set phone
+command.
+This command allows you to set multiple phone numbers for
+dialing and redialing separated by either a pipe
+.Pq Dq \&|
+or a colon
+.Pq Dq \&: :
+.Bd -ragged -offset indent
+.No set phone Ar telno Ns Xo
+.Oo \&| Ns Ar backupnumber
+.Oc Ns ... Ns Oo : Ns Ar nextnumber
+.Oc Ns ...
+.Xc
+.Ed
+.Pp
+Numbers after the first in a pipe-separated list are only used if the
+previous number was used in a failed dial or login script.
+Numbers
+separated by a colon are used sequentially, irrespective of what happened
+as a result of using the previous number.
+For example:
+.Bd -literal -offset indent
+set phone "1234567|2345678:3456789|4567890"
+.Ed
+.Pp
+Here, the 1234567 number is attempted.
+If the dial or login script fails,
+the 2345678 number is used next time, but *only* if the dial or login script
+fails.
+On the dial after this, the 3456789 number is used.
+The 4567890
+number is only used if the dial or login script using the 3456789 fails.
+If the login script of the 2345678 number fails, the next number is still the
+3456789 number.
+As many pipes and colons can be used as are necessary
+(although a given site would usually prefer to use either the pipe or the
+colon, but not both).
+The next number redial timeout is used between all numbers.
+When the end of the list is reached, the normal redial period is
+used before starting at the beginning again.
+The selected phone number is substituted for the \\\\T string in the
+.Dq set dial
+command (see below).
+.It
+Set up your redial requirements using
+.Dq set redial .
+For example, if you have a bad telephone line or your provider is
+usually engaged (not so common these days), you may want to specify
+the following:
+.Bd -literal -offset indent
+set redial 10 4
+.Ed
+.Pp
+This says that up to 4 phone calls should be attempted with a pause of 10
+seconds before dialing the first number again.
+.It
+Describe your login procedure using the
+.Dq set dial
+and
+.Dq set login
+commands.
+The
+.Dq set dial
+command is used to talk to your modem and establish a link with your
+ISP, for example:
+.Bd -literal -offset indent
+set dial "ABORT BUSY ABORT NO\\\\sCARRIER TIMEOUT 4 \\"\\" \e
+ ATZ OK-ATZ-OK ATDT\\\\T TIMEOUT 60 CONNECT"
+.Ed
+.Pp
+This modem "chat" string means:
+.Bl -bullet
+.It
+Abort if the string "BUSY" or "NO CARRIER" are received.
+.It
+Set the timeout to 4 seconds.
+.It
+Expect nothing.
+.It
+Send ATZ.
+.It
+Expect OK.
+If that's not received within the 4 second timeout, send ATZ
+and expect OK.
+.It
+Send ATDTxxxxxxx where xxxxxxx is the next number in the phone list from
+above.
+.It
+Set the timeout to 60.
+.It
+Wait for the CONNECT string.
+.El
+.Pp
+Once the connection is established, the login script is executed.
+This script is written in the same style as the dial script, but care should
+be taken to avoid having your password logged:
+.Bd -literal -offset indent
+set authkey MySecret
+set login "TIMEOUT 15 login:-\\\\r-login: awfulhak \e
+ word: \\\\P ocol: PPP HELLO"
+.Ed
+.Pp
+This login "chat" string means:
+.Bl -bullet
+.It
+Set the timeout to 15 seconds.
+.It
+Expect "login:".
+If it's not received, send a carriage return and expect
+"login:" again.
+.It
+Send "awfulhak"
+.It
+Expect "word:" (the tail end of a "Password:" prompt).
+.It
+Send whatever our current
+.Ar authkey
+value is set to.
+.It
+Expect "ocol:" (the tail end of a "Protocol:" prompt).
+.It
+Send "PPP".
+.It
+Expect "HELLO".
+.El
+.Pp
+The
+.Dq set authkey
+command is logged specially.
+When
+.Ar command
+or
+.Ar chat
+logging is enabled, the actual password is not logged;
+.Sq ********
+is logged instead.
+.Pp
+Login scripts vary greatly between ISPs.
+If you're setting one up for the first time,
+.Em ENABLE CHAT LOGGING
+so that you can see if your script is behaving as you expect.
+.It
+Use
+.Dq set device
+and
+.Dq set speed
+to specify your serial line and speed, for example:
+.Bd -literal -offset indent
+set device /dev/cuaa0
+set speed 115200
+.Ed
+.Pp
+Cuaa0 is the first serial port on
+.Fx .
+If you're running
+.Nm
+on
+.Ox ,
+cua00 is the first.
+A speed of 115200 should be specified
+if you have a modem capable of bit rates of 28800 or more.
+In general, the serial speed should be about four times the modem speed.
+.It
+Use the
+.Dq set ifaddr
+command to {define} the IP address.
+.Bl -bullet
+.It
+If you know what IP address your provider uses, then use it as the remote
+address (dst_addr), otherwise choose something like 10.0.0.2/0 (see below).
+.It
+If your provider has assigned a particular IP address to you, then use
+it as your address (src_addr).
+.It
+If your provider assigns your address dynamically, choose a suitably
+unobtrusive and unspecific IP number as your address.
+10.0.0.1/0 would be appropriate.
+The bit after the / specifies how many bits of the
+address you consider to be important, so if you wanted to insist on
+something in the class C network 1.2.3.0, you could specify 1.2.3.1/24.
+.It
+If you find that your ISP accepts the first IP number that you suggest,
+specify third and forth arguments of
+.Dq 0.0.0.0 .
+This will force your ISP to assign a number.
+(The third argument will
+be ignored as it is less restrictive than the default mask for your
+.Sq src_addr ) .
+.El
+.Pp
+An example for a connection where you don't know your IP number or your
+ISPs IP number would be:
+.Bd -literal -offset indent
+set ifaddr 10.0.0.1/0 10.0.0.2/0 0.0.0.0 0.0.0.0
+.Ed
+.Pp
+.It
+In most cases, your ISP will also be your default router.
+If this is the case, add the line
+.Bd -literal -offset indent
+add default HISADDR
+.Ed
+.Pp
+to
+.Pa /etc/ppp/ppp.conf
+(or to
+.Pa /etc/ppp/ppp.linkup
+for setups that don't use
+.Fl auto
+mode).
+.Pp
+This tells
+.Nm
+to add a default route to whatever the peer address is
+(10.0.0.2 in this example).
+This route is
+.Sq sticky ,
+meaning that should the value of
+.Dv HISADDR
+change, the route will be updated accordingly.
+.It
+If your provider requests that you use PAP/CHAP authentication methods, add
+the next lines to your
+.Pa /etc/ppp/ppp.conf
+file:
+.Bd -literal -offset indent
+set authname MyName
+set authkey MyPassword
+.Ed
+.Pp
+Both are accepted by default, so
+.Nm
+will provide whatever your ISP requires.
+.Pp
+It should be noted that a login script is rarely (if ever) required
+when PAP or CHAP are in use.
+.It
+Ask your ISP to authenticate your nameserver address(es) with the line
+.Bd -literal -offset indent
+enable dns
+.Ed
+.Pp
+Do
+.Em NOT
+do this if you are running a local DNS unless you also either use
+.Dq resolv readonly
+or have
+.Dq resolv restore
+in
+.Pa /etc/ppp/ppp.linkdown ,
+as
+.Nm
+will simply circumvent its use by entering some nameserver lines in
+.Pa /etc/resolv.conf .
+.El
+.Pp
+Please refer to
+.Pa /usr/share/examples/ppp/ppp.conf.sample
+and
+.Pa /usr/share/examples/ppp/ppp.linkup.sample
+for some real examples.
+The pmdemand label should be appropriate for most ISPs.
+.Sh LOGGING FACILITY
+.Nm
+is able to generate the following log info either via
+.Xr syslog 3
+or directly to the screen:
+.Pp
+.Bl -tag -width XXXXXXXXX -offset XXX -compact
+.It Li All
+Enable all logging facilities.
+This generates a lot of log.
+The most common use of 'all' is as a basis, where you remove some facilities
+after enabling 'all' ('debug' and 'timer' are usually best disabled.)
+.It Li Async
+Dump async level packet in hex.
+.It Li CBCP
+Generate CBCP (CallBack Control Protocol) logs.
+.It Li CCP
+Generate a CCP packet trace.
+.It Li Chat
+Generate
+.Sq dial ,
+.Sq login ,
+.Sq logout
+and
+.Sq hangup
+chat script trace logs.
+.It Li Command
+Log commands executed either from the command line or any of the configuration
+files.
+.It Li Connect
+Log Chat lines containing the string "CONNECT".
+.It Li Debug
+Log debug information.
+.It Li DNS
+Log DNS QUERY packets.
+.It Li Filter
+Log packets permitted by the dial filter and denied by any filter.
+.It Li HDLC
+Dump HDLC packet in hex.
+.It Li ID0
+Log all function calls specifically made as user id 0.
+.It Li IPCP
+Generate an IPCP packet trace.
+.It Li LCP
+Generate an LCP packet trace.
+.It Li LQM
+Generate LQR reports.
+.It Li Phase
+Phase transition log output.
+.It Li Physical
+Dump physical level packet in hex.
+.It Li Sync
+Dump sync level packet in hex.
+.It Li TCP/IP
+Dump all TCP/IP packets.
+.It Li Timer
+Log timer manipulation.
+.It Li TUN
+Include the tun device on each log line.
+.It Li Warning
+Output to the terminal device.
+If there is currently no terminal,
+output is sent to the log file using syslogs
+.Dv LOG_WARNING .
+.It Li Error
+Output to both the terminal device
+and the log file using syslogs
+.Dv LOG_ERROR .
+.It Li Alert
+Output to the log file using
+.Dv LOG_ALERT .
+.El
+.Pp
+The
+.Dq set log
+command allows you to set the logging output level.
+Multiple levels can be specified on a single command line.
+The default is equivalent to
+.Dq set log Phase .
+.Pp
+It is also possible to log directly to the screen.
+The syntax is the same except that the word
+.Dq local
+should immediately follow
+.Dq set log .
+The default is
+.Dq set log local
+(i.e., only the un-maskable warning, error and alert output).
+.Pp
+If The first argument to
+.Dq set log Op local
+begins with a
+.Sq +
+or a
+.Sq -
+character, the current log levels are
+not cleared, for example:
+.Bd -literal -offset indent
+PPP ON awfulhak> set log phase
+PPP ON awfulhak> show log
+Log: Phase Warning Error Alert
+Local: Warning Error Alert
+PPP ON awfulhak> set log +tcp/ip -warning
+PPP ON awfulhak> set log local +command
+PPP ON awfulhak> show log
+Log: Phase TCP/IP Warning Error Alert
+Local: Command Warning Error Alert
+.Ed
+.Pp
+Log messages of level Warning, Error and Alert are not controllable
+using
+.Dq set log Op local .
+.Pp
+The
+.Ar Warning
+level is special in that it will not be logged if it can be displayed
+locally.
+.Sh SIGNAL HANDLING
+.Nm
+deals with the following signals:
+.Bl -tag -width "USR2"
+.It INT
+Receipt of this signal causes the termination of the current connection
+(if any).
+This will cause
+.Nm
+to exit unless it is in
+.Fl auto
+or
+.Fl ddial
+mode.
+.It HUP, TERM & QUIT
+These signals tell
+.Nm
+to exit.
+.It USR1
+This signal, tells
+.Nm
+to re-open any existing server socket, dropping all existing diagnostic
+connections.
+Sockets that couldn't previously be opened will be retried.
+.It USR2
+This signal, tells
+.Nm
+to close any existing server socket, dropping all existing diagnostic
+connections.
+.Dv SIGUSR1
+can still be used to re-open the socket.
+.El
+.Sh MULTI-LINK PPP
+If you wish to use more than one physical link to connect to a
+.Em PPP
+peer, that peer must also understand the
+.Em MULTI-LINK PPP
+protocol.
+Refer to RFC 1990 for specification details.
+.Pp
+The peer is identified using a combination of his
+.Dq endpoint discriminator
+and his
+.Dq authentication id .
+Either or both of these may be specified.
+It is recommended that
+at least one is specified, otherwise there is no way of ensuring that
+all links are actually connected to the same peer program, and some
+confusing lock-ups may result.
+Locally, these identification variables are specified using the
+.Dq set enddisc
+and
+.Dq set authname
+commands.
+The
+.Sq authname
+(and
+.Sq authkey )
+must be agreed in advance with the peer.
+.Pp
+Multi-link capabilities are enabled using the
+.Dq set mrru
+command (set maximum reconstructed receive unit).
+Once multi-link is enabled,
+.Nm
+will attempt to negotiate a multi-link connection with the peer.
+.Pp
+By default, only one
+.Sq link
+is available
+(called
+.Sq deflink ) .
+To create more links, the
+.Dq clone
+command is used.
+This command will clone existing links, where all
+characteristics are the same except:
+.Bl -enum
+.It
+The new link has its own name as specified on the
+.Dq clone
+command line.
+.It
+The new link is an
+.Sq interactive
+link.
+Its mode may subsequently be changed using the
+.Dq set mode
+command.
+.It
+The new link is in a
+.Sq closed
+state.
+.El
+.Pp
+A summary of all available links can be seen using the
+.Dq show links
+command.
+.Pp
+Once a new link has been created, command usage varies.
+All link specific commands must be prefixed with the
+.Dq link Ar name
+command, specifying on which link the command is to be applied.
+When only a single link is available,
+.Nm
+is smart enough not to require the
+.Dq link Ar name
+prefix.
+.Pp
+Some commands can still be used without specifying a link - resulting
+in an operation at the
+.Sq bundle
+level.
+For example, once two or more links are available, the command
+.Dq show ccp
+will show CCP configuration and statistics at the multi-link level, and
+.Dq link deflink show ccp
+will show the same information at the
+.Dq deflink
+link level.
+.Pp
+Armed with this information, the following configuration might be used:
+.Pp
+.Bd -literal -offset indent
+mp:
+ set timeout 0
+ set log phase chat
+ set device /dev/cuaa0 /dev/cuaa1 /dev/cuaa2
+ set phone "123456789"
+ set dial "ABORT BUSY ABORT NO\\sCARRIER TIMEOUT 5 \\"\\" ATZ \e
+ OK-AT-OK \\\\dATDT\\\\T TIMEOUT 45 CONNECT"
+ set login
+ set ifaddr 10.0.0.1/0 10.0.0.2/0 0.0.0.0 0.0.0.0
+ set authname ppp
+ set authkey ppppassword
+
+ set mrru 1500
+ clone 1,2,3 # Create 3 new links - duplicates of the default
+ link deflink remove # Delete the default link (called ``deflink'')
+.Ed
+.Pp
+Note how all cloning is done at the end of the configuration.
+Usually, the link will be configured first, then cloned.
+If you wish all links
+to be up all the time, you can add the following line to the end of your
+configuration.
+.Pp
+.Bd -literal -offset indent
+ link 1,2,3 set mode ddial
+.Ed
+.Pp
+If you want the links to dial on demand, this command could be used:
+.Pp
+.Bd -literal -offset indent
+ link * set mode auto
+.Ed
+.Pp
+Links may be tied to specific names by removing the
+.Dq set device
+line above, and specifying the following after the
+.Dq clone
+command:
+.Pp
+.Bd -literal -offset indent
+ link 1 set device /dev/cuaa0
+ link 2 set device /dev/cuaa1
+ link 3 set device /dev/cuaa2
+.Ed
+.Pp
+Use the
+.Dq help
+command to see which commands require context (using the
+.Dq link
+command), which have optional
+context and which should not have any context.
+.Pp
+When
+.Nm
+has negotiated
+.Em MULTI-LINK
+mode with the peer, it creates a local domain socket in the
+.Pa /var/run
+directory.
+This socket is used to pass link information (including
+the actual link file descriptor) between different
+.Nm
+invocations.
+This facilitates
+.Nm Ns No 's
+ability to be run from a
+.Xr getty 8
+or directly from
+.Pa /etc/gettydefs
+(using the
+.Sq pp=
+capability), without needing to have initial control of the serial
+line.
+Once
+.Nm
+negotiates multi-link mode, it will pass its open link to any
+already running process.
+If there is no already running process,
+.Nm
+will act as the master, creating the socket and listening for new
+connections.
+.Sh PPP COMMAND LIST
+This section lists the available commands and their effect.
+They are usable either from an interactive
+.Nm
+session, from a configuration file or from a
+.Xr pppctl 8
+or
+.Xr telnet 1
+session.
+.Bl -tag -width 2n
+.It accept|deny|enable|disable Ar option....
+These directives tell
+.Nm
+how to negotiate the initial connection with the peer.
+Each
+.Dq option
+has a default of either accept or deny and enable or disable.
+.Dq Accept
+means that the option will be ACK'd if the peer asks for it.
+.Dq Deny
+means that the option will be NAK'd if the peer asks for it.
+.Dq Enable
+means that the option will be requested by us.
+.Dq Disable
+means that the option will not be requested by us.
+.Pp
+.Dq Option
+may be one of the following:
+.Bl -tag -width 2n
+.It acfcomp
+Default: Enabled and Accepted.
+ACFComp stands for Address and Control Field Compression.
+Non LCP packets will usually have an address
+field of 0xff (the All-Stations address) and a control field of
+0x03 (the Unnumbered Information command).
+If this option is
+negotiated, these two bytes are simply not sent, thus minimising
+traffic.
+.Pp
+See
+.Pa rfc1662
+for details.
+.It chap Ns Op \&05
+Default: Disabled and Accepted.
+CHAP stands for Challenge Handshake Authentication Protocol.
+Only one of CHAP and PAP (below) may be negotiated.
+With CHAP, the authenticator sends a "challenge" message to its peer.
+The peer uses a one-way hash function to encrypt the
+challenge and sends the result back.
+The authenticator does the same, and compares the results.
+The advantage of this mechanism is that no
+passwords are sent across the connection.
+A challenge is made when the connection is first made.
+Subsequent challenges may occur.
+If you want to have your peer authenticate itself, you must
+.Dq enable chap .
+in
+.Pa /etc/ppp/ppp.conf ,
+and have an entry in
+.Pa /etc/ppp/ppp.secret
+for the peer.
+.Pp
+When using CHAP as the client, you need only specify
+.Dq AuthName
+and
+.Dq AuthKey
+in
+.Pa /etc/ppp/ppp.conf .
+CHAP is accepted by default.
+Some
+.Em PPP
+implementations use "MS-CHAP" rather than MD5 when encrypting the
+challenge.
+MS-CHAP is a combination of MD4 and DES.
+If
+.Nm
+was built on a machine with DES libraries available, it will respond
+to MS-CHAP authentication requests, but will never request them.
+.It deflate
+Default: Enabled and Accepted.
+This option decides if deflate
+compression will be used by the Compression Control Protocol (CCP).
+This is the same algorithm as used by the
+.Xr gzip 1
+program.
+Note: There is a problem negotiating
+.Ar deflate
+capabilities with
+.Xr pppd 8
+- a
+.Em PPP
+implementation available under many operating systems.
+.Nm pppd
+(version 2.3.1) incorrectly attempts to negotiate
+.Ar deflate
+compression using type
+.Em 24
+as the CCP configuration type rather than type
+.Em 26
+as specified in
+.Pa rfc1979 .
+Type
+.Ar 24
+is actually specified as
+.Dq PPP Magna-link Variable Resource Compression
+in
+.Pa rfc1975 Ns !
+.Nm
+is capable of negotiating with
+.Nm pppd ,
+but only if
+.Dq deflate24
+is
+.Ar enable Ns No d
+and
+.Ar accept Ns No ed .
+.It deflate24
+Default: Disabled and Denied.
+This is a variance of the
+.Ar deflate
+option, allowing negotiation with the
+.Xr pppd 8
+program.
+Refer to the
+.Ar deflate
+section above for details.
+It is disabled by default as it violates
+.Pa rfc1975 .
+.It dns
+Default: Disabled and Denied.
+This option allows DNS negotiation.
+.Pp
+If
+.Dq enable Ns No d,
+.Nm
+will request that the peer confirms the entries in
+.Pa /etc/resolv.conf .
+If the peer NAKs our request (suggesting new IP numbers),
+.Pa /etc/resolv.conf
+is updated and another request is sent to confirm the new entries.
+.Pp
+If
+.Dq accept Ns No ed,
+.Nm
+will answer any DNS queries requested by the peer rather than rejecting
+them.
+The answer is taken from
+.Pa /etc/resolv.conf
+unless the
+.Dq set dns
+command is used as an override.
+.It enddisc
+Default: Enabled and Accepted.
+This option allows control over whether we
+negotiate an endpoint discriminator.
+We only send our discriminator if
+.Dq set enddisc
+is used and
+.Ar enddisc
+is enabled.
+We reject the peers discriminator if
+.Ar enddisc
+is denied.
+.It LANMan|chap80lm
+Default: Disabled and Accepted.
+The use of this authentication protocol
+is discouraged as it partially violates the authentication protocol by
+implementing two different mechanisms (LANMan & NT) under the guise of
+a single CHAP type (0x80).
+.Dq LANMan
+uses a simple DES encryption mechanism and is the least secure of the
+CHAP alternatives (although is still more secure than PAP).
+.Pp
+Refer to the
+.Dq MSChap
+description below for more details.
+.It lqr
+Default: Disabled and Accepted.
+This option decides if Link Quality Requests will be sent or accepted.
+LQR is a protocol that allows
+.Nm
+to determine that the link is down without relying on the modems
+carrier detect.
+When LQR is enabled,
+.Nm
+sends the
+.Em QUALPROTO
+option (see
+.Dq set lqrperiod
+below) as part of the LCP request.
+If the peer agrees, both sides will
+exchange LQR packets at the agreed frequency, allowing detailed link
+quality monitoring by enabling LQM logging.
+If the peer doesn't agree,
+.Nm
+will send ECHO LQR requests instead.
+These packets pass no information of interest, but they
+.Em MUST
+be replied to by the peer.
+.Pp
+Whether using LQR or ECHO LQR,
+.Nm
+will abruptly drop the connection if 5 unacknowledged packets have been
+sent rather than sending a 6th.
+A message is logged at the
+.Em PHASE
+level, and any appropriate
+.Dq reconnect
+values are honoured as if the peer were responsible for dropping the
+connection.
+.It mppe
+Default: Enabled and Accepted.
+This is Microsoft Point to Point Encryption scheme.
+MPPE key size can be
+40-, 56- and 128-bits.
+Refer to
+.Dq set mppe
+command.
+.It MSChapV2|chap81
+Default: Disabled and Accepted.
+It is very similar to standard CHAP (type 0x05)
+except that it issues challenges of a fixed 16 bytes in length and uses a
+combination of MD4, SHA-1 and DES to encrypt the challenge rather than using the
+standard MD5 mechanism.
+.It MSChap|chap80nt
+Default: Disabled and Accepted.
+The use of this authentication protocol
+is discouraged as it partially violates the authentication protocol by
+implementing two different mechanisms (LANMan & NT) under the guise of
+a single CHAP type (0x80).
+It is very similar to standard CHAP (type 0x05)
+except that it issues challenges of a fixed 8 bytes in length and uses a
+combination of MD4 and DES to encrypt the challenge rather than using the
+standard MD5 mechanism.
+CHAP type 0x80 for LANMan is also supported - see
+.Dq enable LANMan
+for details.
+.Pp
+Because both
+.Dq LANMan
+and
+.Dq NT
+use CHAP type 0x80, when acting as authenticator with both
+.Dq enable Ns No d ,
+.Nm
+will rechallenge the peer up to three times if it responds using the wrong
+one of the two protocols.
+This gives the peer a chance to attempt using both protocols.
+.Pp
+Conversely, when
+.Nm
+acts as the authenticatee with both protocols
+.Dq accept Ns No ed ,
+the protocols are used alternately in response to challenges.
+.Pp
+Note: If only LANMan is enabled,
+.Xr pppd 8
+(version 2.3.5) misbehaves when acting as authenticatee.
+It provides both
+the NT and the LANMan answers, but also suggests that only the NT answer
+should be used.
+.It pap
+Default: Disabled and Accepted.
+PAP stands for Password Authentication Protocol.
+Only one of PAP and CHAP (above) may be negotiated.
+With PAP, the ID and Password are sent repeatedly to the peer until
+authentication is acknowledged or the connection is terminated.
+This is a rather poor security mechanism.
+It is only performed when the connection is first established.
+If you want to have your peer authenticate itself, you must
+.Dq enable pap .
+in
+.Pa /etc/ppp/ppp.conf ,
+and have an entry in
+.Pa /etc/ppp/ppp.secret
+for the peer (although see the
+.Dq passwdauth
+and
+.Dq set radius
+options below).
+.Pp
+When using PAP as the client, you need only specify
+.Dq AuthName
+and
+.Dq AuthKey
+in
+.Pa /etc/ppp/ppp.conf .
+PAP is accepted by default.
+.It pred1
+Default: Enabled and Accepted.
+This option decides if Predictor 1
+compression will be used by the Compression Control Protocol (CCP).
+.It protocomp
+Default: Enabled and Accepted.
+This option is used to negotiate
+PFC (Protocol Field Compression), a mechanism where the protocol
+field number is reduced to one octet rather than two.
+.It shortseq
+Default: Enabled and Accepted.
+This option determines if
+.Nm
+will request and accept requests for short
+(12 bit)
+sequence numbers when negotiating multi-link mode.
+This is only applicable if our MRRU is set (thus enabling multi-link).
+.It vjcomp
+Default: Enabled and Accepted.
+This option determines if Van Jacobson header compression will be used.
+.El
+.Pp
+The following options are not actually negotiated with the peer.
+Therefore, accepting or denying them makes no sense.
+.Bl -tag -width 2n
+.It filter-decapsulation
+Default: Disabled.
+When this option is enabled,
+.Nm
+will examine UDP frames to see if they actually contain a
+.Em PPP
+frame as their payload.
+If this is the case, all filters will operate on the payload rather
+than the actual packet.
+.Pp
+This is useful if you want to send PPPoUDP traffic over a
+.Em PPP
+link, but want that link to do smart things with the real data rather than
+the UDP wrapper.
+.Pp
+The UDP frame payload must not be compressed in any way, otherwise
+.Nm
+will not be able to interpret it.
+It's therefore recommended that you
+.Ic disable vj pred1 deflate
+and
+.Ic deny vj pred1 deflate
+in the configuration for the
+.Nm
+invocation with the udp link.
+.It idcheck
+Default: Enabled.
+When
+.Nm
+exchanges low-level LCP, CCP and IPCP configuration traffic, the
+.Em Identifier
+field of any replies is expected to be the same as that of the request.
+By default,
+.Nm
+drops any reply packets that do not contain the expected identifier
+field, reporting the fact at the respective log level.
+If
+.Ar idcheck
+is disabled,
+.Nm
+will ignore the identifier field.
+.It iface-alias
+Default: Enabled if
+.Fl nat
+is specified.
+This option simply tells
+.Nm
+to add new interface addresses to the interface rather than replacing them.
+The option can only be enabled if network address translation is enabled
+.Pq Dq nat enable yes .
+.Pp
+With this option enabled,
+.Nm
+will pass traffic for old interface addresses through the NAT
+ifdef({LOCALNAT},{engine,},{engine
+(see
+.Xr libalias 3 ) ,})
+resulting in the ability (in
+.Fl auto
+mode) to properly connect the process that caused the PPP link to
+come up in the first place.
+.Pp
+Disabling NAT with
+.Dq nat enable no
+will also disable
+.Sq iface-alias .
+.It ipcp
+Default: Enabled.
+This option allows
+.Nm
+to attempt to negotiate IP control protocol capabilities and if
+successful to exchange IP datagrams with the peer.
+.It ipv6cp
+Default: Enabled.
+This option allows
+.Nm
+to attempt to negotiate IPv6 control protocol capabilities and if
+successful to exchange IPv6 datagrams with the peer.
+.It keep-session
+Default: Disabled.
+When
+.Nm
+runs as a Multi-link server, a different
+.Nm
+instance initially receives each connection.
+After determining that
+the link belongs to an already existing bundle (controlled by another
+.Nm
+invocation),
+.Nm
+will transfer the link to that process.
+.Pp
+If the link is a tty device or if this option is enabled,
+.Nm
+will not exit, but will change its process name to
+.Dq session owner
+and wait for the controlling
+.Nm
+to finish with the link and deliver a signal back to the idle process.
+This prevents the confusion that results from
+.Nm Ns No 's
+parent considering the link resource available again.
+.Pp
+For tty devices that have entries in
+.Pa /etc/ttys ,
+this is necessary to prevent another
+.Xr getty 8
+from being started, and for program links such as
+.Xr sshd 8 ,
+it prevents
+.Xr sshd 8
+from exiting due to the death of its child.
+As
+.Nm
+cannot determine its parents requirements (except for the tty case), this
+option must be enabled manually depending on the circumstances.
+.It loopback
+Default: Enabled.
+When
+.Ar loopback
+is enabled,
+.Nm
+will automatically loop back packets being sent
+out with a destination address equal to that of the
+.Em PPP
+interface.
+If disabled,
+.Nm
+will send the packet, probably resulting in an ICMP redirect from
+the other end.
+It is convenient to have this option enabled when
+the interface is also the default route as it avoids the necessity
+of a loopback route.
+.It passwdauth
+Default: Disabled.
+Enabling this option will tell the PAP authentication
+code to use the password database (see
+.Xr passwd 5 )
+to authenticate the caller if they cannot be found in the
+.Pa /etc/ppp/ppp.secret
+file.
+.Pa /etc/ppp/ppp.secret
+is always checked first.
+If you wish to use passwords from
+.Xr passwd 5 ,
+but also to specify an IP number or label for a given client, use
+.Dq \&*
+as the client password in
+.Pa /etc/ppp/ppp.secret .
+.It proxy
+Default: Disabled.
+Enabling this option will tell
+.Nm
+to proxy ARP for the peer.
+This means that
+.Nm
+will make an entry in the ARP table using
+.Dv HISADDR
+and the
+.Dv MAC
+address of the local network in which
+.Dv HISADDR
+appears.
+This allows other machines connecteed to the LAN to talk to
+the peer as if the peer itself was connected to the LAN.
+The proxy entry cannot be made unless
+.Dv HISADDR
+is an address from a LAN.
+.It proxyall
+Default: Disabled.
+Enabling this will tell
+.Nm
+to add proxy arp entries for every IP address in all class C or
+smaller subnets routed via the tun interface.
+.Pp
+Proxy arp entries are only made for sticky routes that are added
+using the
+.Dq add
+command.
+No proxy arp entries are made for the interface address itself
+(as created by the
+.Dq set ifaddr
+command).
+.It sroutes
+Default: Enabled.
+When the
+.Dq add
+command is used with the
+.Dv HISADDR ,
+.Dv MYADDR ,
+.Dv HISADDR6
+or
+.Dv MYADDR6
+values, entries are stored in the
+.Sq sticky route
+list.
+Each time these variables change, this list is re-applied to the routing table.
+.Pp
+Disabling this option will prevent the re-application of sticky routes,
+although the
+.Sq stick route
+list will still be maintained.
+.It Op tcp Ns Xo
+.No mssfixup
+.Xc
+Default: Enabled.
+This option tells
+.Nm
+to adjust TCP SYN packets so that the maximum receive segment
+size is not greater than the amount allowed by the interface MTU.
+.It throughput
+Default: Enabled.
+This option tells
+.Nm
+to gather throughput statistics.
+Input and output is sampled over
+a rolling 5 second window, and current, best and total figures are retained.
+This data is output when the relevant
+.Em PPP
+layer shuts down, and is also available using the
+.Dq show
+command.
+Throughput statistics are available at the
+.Dq IPCP
+and
+.Dq physical
+levels.
+.It utmp
+Default: Enabled.
+Normally, when a user is authenticated using PAP or CHAP, and when
+.Nm
+is running in
+.Fl direct
+mode, an entry is made in the utmp and wtmp files for that user.
+Disabling this option will tell
+.Nm
+not to make any utmp or wtmp entries.
+This is usually only necessary if
+you require the user to both login and authenticate themselves.
+.El
+.Pp
+.It add Ns Xo
+.Op !\&
+.Ar dest Ns Op / Ns Ar nn
+.Op Ar mask
+.Op Ar gateway
+.Xc
+.Ar Dest
+is the destination IP address.
+The netmask is specified either as a number of bits with
+.Ar /nn
+or as an IP number using
+.Ar mask .
+.Ar 0 0
+or simply
+.Ar 0
+with no mask refers to the default route.
+It is also possible to use the literal name
+.Sq default
+instead of
+.Ar 0 .
+.Ar Gateway
+is the next hop gateway to get to the given
+.Ar dest
+machine/network.
+Refer to the
+.Xr route 8
+command for further details.
+.Pp
+It is possible to use the symbolic names
+.Sq MYADDR ,
+.Sq HISADDR ,
+.Sq MYADDR6
+or
+.Sq HISADDR6
+as the destination, and
+.Sq HISADDR
+or
+.Sq HISADDR6
+as the
+.Ar gateway .
+.Sq MYADDR
+is replaced with the interface IP address,
+.Sq HISADDR
+is replaced with the interface IP destination (peer) address,
+.Sq MYADDR6
+is replaced with the interface IPv6 address, and
+.Sq HISADDR6
+is replaced with the interface IPv6 destination address,
+.Pp
+If the
+.Ar add!\&
+command is used
+(note the trailing
+.Dq !\& ) ,
+then if the route already exists, it will be updated as with the
+.Sq route change
+command (see
+.Xr route 8
+for further details).
+.Pp
+Routes that contain the
+.Dq HISADDR ,
+.Dq MYADDR ,
+.Dq HISADDR6 ,
+.Dq MYADDR6 ,
+.Dq DNS0 ,
+or
+.Dq DNS1
+constants are considered
+.Sq sticky .
+They are stored in a list (use
+.Dq show ncp
+to see the list), and each time the value of one of these variables
+changes, the appropriate routing table entries are updated.
+This facility may be disabled using
+.Dq disable sroutes .
+.It allow Ar command Op Ar args
+This command controls access to
+.Nm
+and its configuration files.
+It is possible to allow user-level access,
+depending on the configuration file label and on the mode that
+.Nm
+is being run in.
+For example, you may wish to configure
+.Nm
+so that only user
+.Sq fred
+may access label
+.Sq fredlabel
+in
+.Fl background
+mode.
+.Pp
+User id 0 is immune to these commands.
+.Bl -tag -width 2n
+.It allow user Ns Xo
+.Op s
+.Ar logname Ns No ...
+.Xc
+By default, only user id 0 is allowed access to
+.Nm .
+If this command is used, all of the listed users are allowed access to
+the section in which the
+.Dq allow users
+command is found.
+The
+.Sq default
+section is always checked first (even though it is only ever automatically
+loaded at startup).
+.Dq allow users
+commands are cumulative in a given section, but users allowed in any given
+section override users allowed in the default section, so it's possible to
+allow users access to everything except a given label by specifying default
+users in the
+.Sq default
+section, and then specifying a new user list for that label.
+.Pp
+If user
+.Sq *
+is specified, access is allowed to all users.
+.It allow mode Ns Xo
+.Op s
+.Ar mode Ns No ...
+.Xc
+By default, access using any
+.Nm
+mode is possible.
+If this command is used, it restricts the access
+.Ar modes
+allowed to load the label under which this command is specified.
+Again, as with the
+.Dq allow users
+command, each
+.Dq allow modes
+command overrides any previous settings, and the
+.Sq default
+section is always checked first.
+.Pp
+Possible modes are:
+.Sq interactive ,
+.Sq auto ,
+.Sq direct ,
+.Sq dedicated ,
+.Sq ddial ,
+.Sq background
+and
+.Sq * .
+.Pp
+When running in multi-link mode, a section can be loaded if it allows
+.Em any
+of the currently existing line modes.
+.El
+.Pp
+.It nat Ar command Op Ar args
+This command allows the control of the network address translation (also
+known as masquerading or IP aliasing) facilities that are built into
+.Nm .
+NAT is done on the external interface only, and is unlikely to make sense
+if used with the
+.Fl direct
+flag.
+.Pp
+If nat is enabled on your system (it may be omitted at compile time),
+the following commands are possible:
+.Bl -tag -width 2n
+.It nat enable yes|no
+This command either switches network address translation on or turns it off.
+The
+.Fl nat
+command line flag is synonymous with
+.Dq nat enable yes .
+.It nat addr Op Ar addr_local addr_alias
+This command allows data for
+.Ar addr_alias
+to be redirected to
+.Ar addr_local .
+It is useful if you own a small number of real IP numbers that
+you wish to map to specific machines behind your gateway.
+.It nat deny_incoming yes|no
+If set to yes, this command will refuse all incoming packets where an
+aliasing link doesn't already exist.
+ifdef({LOCALNAT},{},{Refer to the
+.Sx CONCEPTUAL BACKGROUND
+section of
+.Xr libalias 3
+for a description of what an
+.Dq aliasing link
+is.
+})dnl
+.Pp
+It should be noted under what circumstances an aliasing link is
+ifdef({LOCALNAT},{created.},{created by
+.Xr libalias 3 .})
+It may be necessary to further protect your network from outside
+connections using the
+.Dq set filter
+or
+.Dq nat target
+commands.
+.It nat help|?
+This command gives a summary of available nat commands.
+.It nat log yes|no
+This option causes various NAT statistics and information to
+be logged to the file
+.Pa /var/log/alias.log .
+.It nat port Ar proto Ar targetIP Ns Xo
+.No : Ns Ar targetPort Ns
+.Oo
+.No - Ns Ar targetPort
+.Oc Ar aliasPort Ns
+.Oo
+.No - Ns Ar aliasPort
+.Oc Oo Ar remoteIP : Ns
+.Ar remotePort Ns
+.Oo
+.No - Ns Ar remotePort
+.Oc Ns
+.Oc
+.Xc
+This command causes incoming
+.Ar proto
+connections to
+.Ar aliasPort
+to be redirected to
+.Ar targetPort
+on
+.Ar targetIP .
+.Ar proto
+is either
+.Dq tcp
+or
+.Dq udp .
+.Pp
+A range of port numbers may be specified as shown above.
+The ranges must be of the same size.
+.Pp
+If
+.Ar remoteIP
+is specified, only data coming from that IP number is redirected.
+.Ar remotePort
+must either be
+.Dq 0
+(indicating any source port)
+or a range of ports the same size as the other ranges.
+.Pp
+This option is useful if you wish to run things like Internet phone on
+machines behind your gateway, but is limited in that connections to only
+one interior machine per source machine and target port are possible.
+.It nat proto Ar proto localIP Oo
+.Ar publicIP Op Ar remoteIP
+.Oc
+This command tells
+.Nm
+to redirect packets of protocol type
+.Ar proto
+(see
+.Xr protocols 5 )
+to the internal address
+.Ar localIP .
+.Pp
+If
+.Ar publicIP
+is specified, only packets destined for that address are matched,
+otherwise the default alias address is used.
+.Pp
+If
+.Ar remoteIP
+is specified, only packets matching that source address are matched,
+.Pp
+This command is useful for redirecting tunnel endpoints to an internal machine,
+for example:
+.Pp
+.Dl nat proto ipencap 10.0.0.1
+.It "nat proxy cmd" Ar arg Ns No ...
+This command tells
+.Nm
+to proxy certain connections, redirecting them to a given server.
+ifdef({LOCALNAT},{},{Refer to the description of
+.Fn PacketAliasProxyRule
+in
+.Xr libalias 3
+for details of the available commands.
+})dnl
+.It nat punch_fw Op Ar base count
+This command tells
+.Nm
+to punch holes in the firewall for FTP or IRC DCC connections.
+This is done dynamically by installing termporary firewall rules which
+allow a particular connection (and only that connection) to go through
+the firewall.
+The rules are removed once the corresponding connection terminates.
+.Pp
+A maximum of
+.Ar count
+rules starting from rule number
+.Ar base
+will be used for punching firewall holes.
+The range will be cleared when the
+.Dq nat punch_fw
+command is run.
+.Pp
+If no arguments are given, firewall punching is disabled.
+.It nat same_ports yes|no
+When enabled, this command will tell the network address translation engine to
+attempt to avoid changing the port number on outgoing packets.
+This is useful
+if you want to support protocols such as RPC and LPD which require
+connections to come from a well known port.
+.It nat target Op Ar address
+Set the given target address or clear it if no address is given.
+The target address is used
+ifdef({LOCALNAT},{},{by libalias })dnl
+to specify how to NAT incoming packets by default.
+If a target address is not set or if
+.Dq default
+is given, packets are not altered and are allowed to route to the internal
+network.
+.Pp
+The target address may be set to
+.Dq MYADDR ,
+in which case
+ifdef({LOCALNAT},{all packets will be redirected},
+{libalias will redirect all packets})
+to the interface address.
+.It nat use_sockets yes|no
+When enabled, this option tells the network address translation engine to
+create a socket so that it can guarantee a correct incoming ftp data or
+IRC connection.
+.It nat unregistered_only yes|no
+Only alter outgoing packets with an unregistered source address.
+According to RFC 1918, unregistered source addresses
+are 10.0.0.0/8, 172.16.0.0/12 and 192.168.0.0/16.
+.El
+.Pp
+These commands are also discussed in the file
+.Pa README.nat
+which comes with the source distribution.
+.Pp
+.It Op !\& Ns Xo
+.No bg Ar command
+.Xc
+The given
+.Ar command
+is executed in the background with the following words replaced:
+.Bl -tag -width COMPILATIONDATE
+.It Li AUTHNAME
+This is replaced with the local
+.Ar authname
+value.
+See the
+.Dq set authname
+command below.
+.It Li COMPILATIONDATE
+This is replaced with the date on which
+.Nm
+was compiled.
+.It Li DNS0 & DNS1
+These are replaced with the primary and secondary nameserver IP numbers.
+If nameservers are negotiated by IPCP, the values of these macros will change.
+.It Li ENDDISC
+This is replaced with the local endpoint discriminator value.
+See the
+.Dq set enddisc
+command below.
+.It Li HISADDR
+This is replaced with the peers IP number.
+.It Li HISADDR6
+This is replaced with the peers IPv6 number.
+.It Li INTERFACE
+This is replaced with the name of the interface that's in use.
+.It Li IPOCTETSIN
+This is replaced with the number of IP bytes received since the connection
+was established.
+.It Li IPOCTETSOUT
+This is replaced with the number of IP bytes sent since the connection
+was established.
+.It Li IPPACKETSIN
+This is replaced with the number of IP packets received since the connection
+was established.
+.It Li IPPACKETSOUT
+This is replaced with the number of IP packets sent since the connection
+was established.
+.It Li IPV6OCTETSIN
+This is replaced with the number of IPv6 bytes received since the connection
+was established.
+.It Li IPV6OCTETSOUT
+This is replaced with the number of IPv6 bytes sent since the connection
+was established.
+.It Li IPV6PACKETSIN
+This is replaced with the number of IPv6 packets received since the connection
+was established.
+.It Li IPV6PACKETSOUT
+This is replaced with the number of IPv6 packets sent since the connection
+was established.
+.It Li LABEL
+This is replaced with the last label name used.
+A label may be specified on the
+.Nm
+command line, via the
+.Dq load
+or
+.Dq dial
+commands and in the
+.Pa ppp.secret
+file.
+.It Li MYADDR
+This is replaced with the IP number assigned to the local interface.
+.It Li MYADDR6
+This is replaced with the IPv6 number assigned to the local interface.
+.It Li OCTETSIN
+This is replaced with the number of bytes received since the connection
+was established.
+.It Li OCTETSOUT
+This is replaced with the number of bytes sent since the connection
+was established.
+.It Li PACKETSIN
+This is replaced with the number of packets received since the connection
+was established.
+.It Li PACKETSOUT
+This is replaced with the number of packets sent since the connection
+was established.
+.It Li PEER_ENDDISC
+This is replaced with the value of the peers endpoint discriminator.
+.It Li PROCESSID
+This is replaced with the current process id.
+.It Li SOCKNAME
+This is replaced with the name of the diagnostic socket.
+.It Li UPTIME
+This is replaced with the bundle uptime in HH:MM:SS format.
+.It Li USER
+This is replaced with the username that has been authenticated with PAP or
+CHAP.
+Normally, this variable is assigned only in -direct mode.
+This value is available irrespective of whether utmp logging is enabled.
+.It Li VERSION
+This is replaced with the current version number of
+.Nm .
+.El
+.Pp
+These substitutions are also done by the
+.Dq set proctitle ,
+.Dq ident
+and
+.Dq log
+commands.
+.Pp
+If you wish to pause
+.Nm
+while the command executes, use the
+.Dq shell
+command instead.
+.It clear physical|ipcp|ipv6 Op current|overall|peak...
+Clear the specified throughput values at either the
+.Dq physical ,
+.Dq ipcp
+or
+.Dq ipv6cp
+level.
+If
+.Dq physical
+is specified, context must be given (see the
+.Dq link
+command below).
+If no second argument is given, all values are cleared.
+.It clone Ar name Ns Xo
+.Op \&, Ns Ar name Ns
+.No ...
+.Xc
+Clone the specified link, creating one or more new links according to the
+.Ar name
+argument(s).
+This command must be used from the
+.Dq link
+command below unless you've only got a single link (in which case that
+link becomes the default).
+Links may be removed using the
+.Dq remove
+command below.
+.Pp
+The default link name is
+.Dq deflink .
+.It close Op lcp|ccp Ns Op !\&
+If no arguments are given, the relevant protocol layers will be brought
+down and the link will be closed.
+If
+.Dq lcp
+is specified, the LCP layer is brought down, but
+.Nm
+will not bring the link offline.
+It is subsequently possible to use
+.Dq term
+(see below)
+to talk to the peer machine if, for example, something like
+.Dq slirp
+is being used.
+If
+.Dq ccp
+is specified, only the relevant compression layer is closed.
+If the
+.Dq !\&
+is used, the compression layer will remain in the closed state, otherwise
+it will re-enter the STOPPED state, waiting for the peer to initiate
+further CCP negotiation.
+In any event, this command does not disconnect the user from
+.Nm
+or exit
+.Nm .
+See the
+.Dq quit
+command below.
+.It delete Ns Xo
+.Op !\&
+.Ar dest
+.Xc
+This command deletes the route with the given
+.Ar dest
+IP address.
+If
+.Ar dest
+is specified as
+.Sq ALL ,
+all non-direct entries in the routing table for the current interface,
+and all
+.Sq sticky route
+entries are deleted.
+If
+.Ar dest
+is specified as
+.Sq default ,
+the default route is deleted.
+.Pp
+If the
+.Ar delete!\&
+command is used
+(note the trailing
+.Dq !\& ) ,
+.Nm
+will not complain if the route does not already exist.
+.It dial|call Op Ar label Ns Xo
+.No ...
+.Xc
+This command is the equivalent of
+.Dq load label
+followed by
+.Dq open ,
+and is provided for backwards compatibility.
+.It down Op Ar lcp|ccp
+Bring the relevant layer down ungracefully, as if the underlying layer
+had become unavailable.
+It's not considered polite to use this command on
+a Finite State Machine that's in the OPEN state.
+If no arguments are
+supplied, the entire link is closed (or if no context is given, all links
+are terminated).
+If
+.Sq lcp
+is specified, the
+.Em LCP
+layer is terminated but the device is not brought offline and the link
+is not closed.
+If
+.Sq ccp
+is specified, only the relevant compression layer(s) are terminated.
+.It help|? Op Ar command
+Show a list of available commands.
+If
+.Ar command
+is specified, show the usage string for that command.
+.It ident Op Ar text Ns No ...
+Identify the link to the peer using
+.Ar text .
+If
+.Ar text
+is empty, link identification is disabled.
+It is possible to use any of the words described for the
+.Ic bg
+command above.
+Refer to the
+.Ic sendident
+command for details of when
+.Nm
+identifies itself to the peer.
+.It iface Ar command Op args
+This command is used to control the interface used by
+.Nm .
+.Ar Command
+may be one of the following:
+.Bl -tag -width 2n
+.It iface add Ns Xo
+.Op !\&
+.Ar addr Ns Op / Ns Ar bits
+.Op Ar peer
+.Xc
+.It iface add Ns Xo
+.Op !\&
+.Ar addr
+.Ar mask
+.Ar peer
+.Xc
+Add the given
+.Ar addr mask peer
+combination to the interface.
+Instead of specifying
+.Ar mask ,
+.Ar /bits
+can be used
+(with no space between it and
+.Ar addr ) .
+If the given address already exists, the command fails unless the
+.Dq !\&
+is used - in which case the previous interface address entry is overwritten
+with the new one, allowing a change of netmask or peer address.
+.Pp
+If only
+.Ar addr
+is specified,
+.Ar bits
+defaults to
+.Dq 32
+and
+.Ar peer
+defaults to
+.Dq 255.255.255.255 .
+This address (the broadcast address) is the only duplicate peer address that
+.Nm
+allows.
+.It iface clear Op INET | INET6
+If this command is used while
+.Nm
+is in the OPENED state or while in
+.Fl auto
+mode, all addresses except for the NCP negotiated address are deleted
+from the interface.
+If
+.Nm
+is not in the OPENED state and is not in
+.Fl auto
+mode, all interface addresses are deleted.
+.Pp
+If the INET or INET6 arguments are used, only addresses for that address
+family are cleared.
+.Pp
+.It iface delete Ns Xo
+.Op !\& Ns
+.No |rm Ns Op !\&
+.Ar addr
+.Xc
+This command deletes the given
+.Ar addr
+from the interface.
+If the
+.Dq !\&
+is used, no error is given if the address isn't currently assigned to
+the interface (and no deletion takes place).
+.It iface show
+Shows the current state and current addresses for the interface.
+It is much the same as running
+.Dq ifconfig INTERFACE .
+.It iface help Op Ar sub-command
+This command, when invoked without
+.Ar sub-command ,
+will show a list of possible
+.Dq iface
+sub-commands and a brief synopsis for each.
+When invoked with
+.Ar sub-command ,
+only the synopsis for the given sub-command is shown.
+.El
+.It Op data Ns Xo
+.No link
+.Ar name Ns Op , Ns Ar name Ns
+.No ... Ar command Op Ar args
+.Xc
+This command may prefix any other command if the user wishes to
+specify which link the command should affect.
+This is only applicable after multiple links have been created in Multi-link
+mode using the
+.Dq clone
+command.
+.Pp
+.Ar Name
+specifies the name of an existing link.
+If
+.Ar name
+is a comma separated list,
+.Ar command
+is executed on each link.
+If
+.Ar name
+is
+.Dq * ,
+.Ar command
+is executed on all links.
+.It load Op Ar label Ns Xo
+.No ...
+.Xc
+Load the given
+.Ar label Ns No (s)
+from the
+.Pa ppp.conf
+file.
+If
+.Ar label
+is not given, the
+.Ar default
+label is used.
+.Pp
+Unless the
+.Ar label
+section uses the
+.Dq set mode ,
+.Dq open
+or
+.Dq dial
+commands,
+.Nm
+will not attempt to make an immediate connection.
+.It log Ar word Ns No ...
+Send the given word(s) to the log file with the prefix
+.Dq LOG: .
+Word substitutions are done as explained under the
+.Dq !bg
+command above.
+.It open Op lcp|ccp|ipcp
+This is the opposite of the
+.Dq close
+command.
+All closed links are immediately brought up apart from second and subsequent
+.Ar demand-dial
+links - these will come up based on the
+.Dq set autoload
+command that has been used.
+.Pp
+If the
+.Dq lcp
+argument is used while the LCP layer is already open, LCP will be
+renegotiated.
+This allows various LCP options to be changed, after which
+.Dq open lcp
+can be used to put them into effect.
+After renegotiating LCP,
+any agreed authentication will also take place.
+.Pp
+If the
+.Dq ccp
+argument is used, the relevant compression layer is opened.
+Again, if it is already open, it will be renegotiated.
+.Pp
+If the
+.Dq ipcp
+argument is used, the link will be brought up as normal, but if
+IPCP is already open, it will be renegotiated and the network
+interface will be reconfigured.
+.Pp
+It is probably not good practice to re-open the PPP state machines
+like this as it's possible that the peer will not behave correctly.
+It
+.Em is
+however useful as a way of forcing the CCP or VJ dictionaries to be reset.
+.It passwd Ar pass
+Specify the password required for access to the full
+.Nm
+command set.
+This password is required when connecting to the diagnostic port (see the
+.Dq set server
+command).
+.Ar Pass
+is specified on the
+.Dq set server
+command line.
+The value of
+.Ar pass
+is not logged when
+.Ar command
+logging is active, instead, the literal string
+.Sq ********
+is logged.
+.It quit|bye Op all
+If
+.Dq quit
+is executed from the controlling connection or from a command file,
+ppp will exit after closing all connections.
+Otherwise, if the user
+is connected to a diagnostic socket, the connection is simply dropped.
+.Pp
+If the
+.Ar all
+argument is given,
+.Nm
+will exit despite the source of the command after closing all existing
+connections.
+.It remove|rm
+This command removes the given link.
+It is only really useful in multi-link mode.
+A link must be in the
+.Dv CLOSED
+state before it is removed.
+.It rename|mv Ar name
+This command renames the given link to
+.Ar name .
+It will fail if
+.Ar name
+is already used by another link.
+.Pp
+The default link name is
+.Sq deflink .
+Renaming it to
+.Sq modem ,
+.Sq cuaa0
+or
+.Sq USR
+may make the log file more readable.
+.It resolv Ar command
+This command controls
+.Nm Ns No 's
+manipulation of the
+.Xr resolv.conf 5
+file.
+When
+.Nm
+starts up, it loads the contents of this file into memory and retains this
+image for future use.
+.Ar command
+is one of the following:
+.Bl -tag -width readonly
+.It Em readonly
+Treat
+.Pa /etc/resolv.conf
+as read only.
+If
+.Dq dns
+is enabled,
+.Nm
+will still attempt to negotiate nameservers with the peer, making the results
+available via the
+.Dv DNS0
+and
+.Dv DNS1
+macros.
+This is the opposite of the
+.Dq resolv writable
+command.
+.It Em reload
+Reload
+.Pa /etc/resolv.conf
+into memory.
+This may be necessary if for example a DHCP client overwrote
+.Pa /etc/resolv.conf .
+.It Em restore
+Replace
+.Pa /etc/resolv.conf
+with the version originally read at startup or with the last
+.Dq resolv reload
+command.
+This is sometimes a useful command to put in the
+.Pa /etc/ppp/ppp.linkdown
+file.
+.It Em rewrite
+Rewrite the
+.Pa /etc/resolv.conf
+file.
+This command will work even if the
+.Dq resolv readonly
+command has been used.
+It may be useful as a command in the
+.Pa /etc/ppp/ppp.linkup
+file if you wish to defer updating
+.Pa /etc/resolv.conf
+until after other commands have finished.
+.It Em writable
+Allow
+.Nm
+to update
+.Pa /etc/resolv.conf
+if
+.Dq dns
+is enabled and
+.Nm
+successfully negotiates a DNS.
+This is the opposite of the
+.Dq resolv readonly
+command.
+.El
+.It save
+This option is not (yet) implemented.
+.It sendident
+This command tells
+.Nm
+to identify itself to the peer.
+The link must be in LCP state or higher.
+If no identity has been set (via the
+.Ic ident
+command),
+.Ic sendident
+will fail.
+.Pp
+When an identity has been set,
+.Nm
+will automatically identify itself when it sends or receives a configure
+reject, when negotiation fails or when LCP reaches the opened state.
+.Pp
+Received identification packets are logged to the LCP log (see
+.Ic set log
+for details) and are never responded to.
+.It set Ns Xo
+.Op up
+.Ar var value
+.Xc
+This option allows the setting of any of the following variables:
+.Bl -tag -width 2n
+.It set accmap Ar hex-value
+ACCMap stands for Asynchronous Control Character Map.
+This is always
+negotiated with the peer, and defaults to a value of 00000000 in hex.
+This protocol is required to defeat hardware that depends on passing
+certain characters from end to end (such as XON/XOFF etc).
+.Pp
+For the XON/XOFF scenario, use
+.Dq set accmap 000a0000 .
+.It set Op auth Ns Xo
+.No key Ar value
+.Xc
+This sets the authentication key (or password) used in client mode
+PAP or CHAP negotiation to the given value.
+It also specifies the
+password to be used in the dial or login scripts in place of the
+.Sq \eP
+sequence, preventing the actual password from being logged.
+If
+.Ar command
+or
+.Ar chat
+logging is in effect,
+.Ar value
+is logged as
+.Sq ********
+for security reasons.
+.Pp
+If the first character of
+.Ar value
+is an exclamation mark
+.Pq Dq !\& ,
+.Nm
+treats the remainder of the string as a program that must be executed
+to determine the
+.Dq authname
+and
+.Dq authkey
+values.
+.Pp
+If the
+.Dq !\&
+is doubled up
+(to
+.Dq !! ) ,
+it is treated as a single literal
+.Dq !\& ,
+otherwise, ignoring the
+.Dq !\& ,
+.Ar value
+is parsed as a program to execute in the same was as the
+.Dq !bg
+command above, substituting special names in the same manner.
+Once executed,
+.Nm
+will feed the program three lines of input, each terminated by a newline
+character:
+.Bl -bullet
+.It
+The host name as sent in the CHAP challenge.
+.It
+The challenge string as sent in the CHAP challenge.
+.It
+The locally defined
+.Dq authname .
+.El
+.Pp
+Two lines of output are expected:
+.Bl -bullet
+.It
+The
+.Dq authname
+to be sent with the CHAP response.
+.It
+The
+.Dq authkey ,
+which is encrypted with the challenge and request id, the answer being sent
+in the CHAP response packet.
+.El
+.Pp
+When configuring
+.Nm
+in this manner, it's expected that the host challenge is a series of ASCII
+digits or characters.
+An encryption device or Secure ID card is usually
+required to calculate the secret appropriate for the given challenge.
+.It set authname Ar id
+This sets the authentication id used in client mode PAP or CHAP negotiation.
+.Pp
+If used in
+.Fl direct
+mode with CHAP enabled,
+.Ar id
+is used in the initial authentication challenge and should normally be set to
+the local machine name.
+.It set autoload Xo
+.Ar min-percent max-percent period
+.Xc
+These settings apply only in multi-link mode and default to zero, zero and
+five respectively.
+When more than one
+.Ar demand-dial
+(also known as
+.Fl auto )
+mode link is available, only the first link is made active when
+.Nm
+first reads data from the tun device.
+The next
+.Ar demand-dial
+link will be opened only when the current bundle throughput is at least
+.Ar max-percent
+percent of the total bundle bandwidth for
+.Ar period
+seconds.
+When the current bundle throughput decreases to
+.Ar min-percent
+percent or less of the total bundle bandwidth for
+.Ar period
+seconds, a
+.Ar demand-dial
+link will be brought down as long as it's not the last active link.
+.Pp
+Bundle throughput is measured as the maximum of inbound and outbound
+traffic.
+.Pp
+The default values cause
+.Ar demand-dial
+links to simply come up one at a time.
+.Pp
+Certain devices cannot determine their physical bandwidth, so it
+is sometimes necessary to use the
+.Dq set bandwidth
+command (described below) to make
+.Dq set autoload
+work correctly.
+.It set bandwidth Ar value
+This command sets the connection bandwidth in bits per second.
+.Ar value
+must be greater than zero.
+It is currently only used by the
+.Dq set autoload
+command above.
+.It set callback Ar option Ns No ...
+If no arguments are given, callback is disabled, otherwise,
+.Nm
+will request (or in
+.Fl direct
+mode, will accept) one of the given
+.Ar option Ns No s .
+In client mode, if an
+.Ar option
+is NAK'd
+.Nm
+will request a different
+.Ar option ,
+until no options remain at which point
+.Nm
+will terminate negotiations (unless
+.Dq none
+is one of the specified
+.Ar option ) .
+In server mode,
+.Nm
+will accept any of the given protocols - but the client
+.Em must
+request one of them.
+If you wish callback to be optional, you must {include}
+.Ar none
+as an option.
+.Pp
+The
+.Ar option Ns No s
+are as follows (in this order of preference):
+.Pp
+.Bl -tag -width Ds
+.It auth
+The callee is expected to decide the callback number based on
+authentication.
+If
+.Nm
+is the callee, the number should be specified as the fifth field of
+the peers entry in
+.Pa /etc/ppp/ppp.secret .
+.It cbcp
+Microsoft's callback control protocol is used.
+See
+.Dq set cbcp
+below.
+.Pp
+If you wish to negotiate
+.Ar cbcp
+in client mode but also wish to allow the server to request no callback at
+CBCP negotiation time, you must specify both
+.Ar cbcp
+and
+.Ar none
+as callback options.
+.It E.164 *| Ns Xo
+.Ar number Ns Op , Ns Ar number Ns
+.No ...
+.Xc
+The caller specifies the
+.Ar number .
+If
+.Nm
+is the callee,
+.Ar number
+should be either a comma separated list of allowable numbers or a
+.Dq \&* ,
+meaning any number is permitted.
+If
+.Nm
+is the caller, only a single number should be specified.
+.Pp
+Note, this option is very unsafe when used with a
+.Dq \&*
+as a malicious caller can tell
+.Nm
+to call any (possibly international) number without first authenticating
+themselves.
+.It none
+If the peer does not wish to do callback at all,
+.Nm
+will accept the fact and continue without callback rather than terminating
+the connection.
+This is required (in addition to one or more other callback
+options) if you wish callback to be optional.
+.El
+.Pp
+.It set cbcp Oo
+.No *| Ns Ar number Ns Oo
+.No , Ns Ar number Ns ...\& Oc
+.Op Ar delay Op Ar retry
+.Oc
+If no arguments are given, CBCP (Microsoft's CallBack Control Protocol)
+is disabled - ie, configuring CBCP in the
+.Dq set callback
+command will result in
+.Nm
+requesting no callback in the CBCP phase.
+Otherwise,
+.Nm
+attempts to use the given phone
+.Ar number Ns No (s).
+.Pp
+In server mode
+.Pq Fl direct ,
+.Nm
+will insist that the client uses one of these numbers, unless
+.Dq \&*
+is used in which case the client is expected to specify the number.
+.Pp
+In client mode,
+.Nm
+will attempt to use one of the given numbers (whichever it finds to
+be agreeable with the peer), or if
+.Dq \&*
+is specified,
+.Nm
+will expect the peer to specify the number.
+.It set cd Oo
+.No off| Ns Ar seconds Ns Op !\&
+.Oc
+Normally,
+.Nm
+checks for the existence of carrier depending on the type of device
+that has been opened:
+.Bl -tag -width XXX -offset XXX
+.It Terminal Devices
+Carrier is checked one second after the login script is complete.
+If it's not set,
+.Nm
+assumes that this is because the device doesn't support carrier (which
+is true for most
+.Dq laplink
+NULL-modem cables), logs the fact and stops checking
+for carrier.
+.Pp
+As ptys don't support the TIOCMGET ioctl, the tty device will switch all
+carrier detection off when it detects that the device is a pty.
+.It ISDN (i4b) Devices
+Carrier is checked once per second for 6 seconds.
+If it's not set after
+the sixth second, the connection attempt is considered to have failed and
+the device is closed.
+Carrier is always required for i4b devices.
+.It PPPoE (netgraph) Devices
+Carrier is checked once per second for 5 seconds.
+If it's not set after
+the fifth second, the connection attempt is considered to have failed and
+the device is closed.
+Carrier is always required for PPPoE devices.
+.El
+.Pp
+All other device types don't support carrier.
+Setting a carrier value will
+result in a warning when the device is opened.
+.Pp
+Some modems take more than one second after connecting to assert the carrier
+signal.
+If this delay isn't increased, this will result in
+.Nm Ns No 's
+inability to detect when the link is dropped, as
+.Nm
+assumes that the device isn't asserting carrier.
+.Pp
+The
+.Dq set cd
+command overrides the default carrier behaviour.
+.Ar seconds
+specifies the maximum number of seconds that
+.Nm
+should wait after the dial script has finished before deciding if
+carrier is available or not.
+.Pp
+If
+.Dq off
+is specified,
+.Nm
+will not check for carrier on the device, otherwise
+.Nm
+will not proceed to the login script until either carrier is detected
+or until
+.Ar seconds
+has elapsed, at which point
+.Nm
+assumes that the device will not set carrier.
+.Pp
+If no arguments are given, carrier settings will go back to their default
+values.
+.Pp
+If
+.Ar seconds
+is followed immediately by an exclamation mark
+.Pq Dq !\& ,
+.Nm
+will
+.Em require
+carrier.
+If carrier is not detected after
+.Ar seconds
+seconds, the link will be disconnected.
+.It set choked Op Ar timeout
+This sets the number of seconds that
+.Nm
+will keep a choked output queue before dropping all pending output packets.
+If
+.Ar timeout
+is less than or equal to zero or if
+.Ar timeout
+isn't specified, it is set to the default value of
+.Em 120 seconds .
+.Pp
+A choked output queue occurs when
+.Nm
+has read a certain number of packets from the local network for transmission,
+but cannot send the data due to link failure (the peer is busy etc.).
+.Nm
+will not read packets indefinitely.
+Instead, it reads up to
+.Em 30
+packets (or
+.Em 30 No +
+.Em nlinks No *
+.Em 2
+packets in multi-link mode), then stops reading the network interface
+until either
+.Ar timeout
+seconds have passed or at least one packet has been sent.
+.Pp
+If
+.Ar timeout
+seconds pass, all pending output packets are dropped.
+.It set ctsrts|crtscts on|off
+This sets hardware flow control.
+Hardware flow control is
+.Ar on
+by default.
+.It set deflate Ar out-winsize Op Ar in-winsize
+This sets the DEFLATE algorithms default outgoing and incoming window
+sizes.
+Both
+.Ar out-winsize
+and
+.Ar in-winsize
+must be values between
+.Em 8
+and
+.Em 15 .
+If
+.Ar in-winsize
+is specified,
+.Nm
+will insist that this window size is used and will not accept any other
+values from the peer.
+.It set dns Op Ar primary Op Ar secondary
+This command specifies DNS overrides for the
+.Dq accept dns
+command.
+Refer to the
+.Dq accept
+command description above for details.
+This command does not affect the IP numbers requested using
+.Dq enable dns .
+.It set device|line Xo
+.Ar value Ns No ...
+.Xc
+This sets the device(s) to which
+.Nm
+will talk to the given
+.Dq value .
+.Pp
+All ISDN and serial device names are expected to begin with
+.Pa /dev/ .
+ISDN devices are usually called
+.Pa i4brbchX
+and serial devices are usually called
+.Pa cuaXX .
+.Pp
+If
+.Dq value
+does not begin with
+.Pa /dev/ ,
+it must either begin with an exclamation mark
+.Pq Dq !\& ,
+be of the format
+.No PPPoE: Ns Ar iface Ns Xo
+.Op \&: Ns Ar provider Ns
+.Xc
+(on
+.Xr netgraph 4
+enabled systems), or be of the format
+.Sm off
+.Ar host : port Op /tcp|udp .
+.Sm on
+.Pp
+If it begins with an exclamation mark, the rest of the device name is
+treated as a program name, and that program is executed when the device
+is opened.
+Standard input, output and error are fed back to
+.Nm
+and are read and written as if they were a regular device.
+.Pp
+If a
+.No PPPoE: Ns Ar iface Ns Xo
+.Op \&: Ns Ar provider Ns
+.Xc
+specification is given,
+.Nm
+will attempt to create a
+.Em PPP
+over Ethernet connection using the given
+.Ar iface
+interface by using
+.Xr netgraph 4 .
+If
+.Xr netgraph 4
+is not available,
+.Nm
+will attempt to load it using
+.Xr kldload 2 .
+If this fails, an external program must be used such as the
+.Xr pppoe 8
+program available under
+.Ox .
+The given
+.Ar provider
+is passed as the service name in the PPPoE Discovery Initiation (PADI)
+packet.
+If no provider is given, an empty value will be used.
+.Pp
+When a PPPoE connection is established,
+.Nm
+will place the name of the Access Concentrator in the environment variable
+.Ev ACNAME .
+.Pp
+Refer to
+.Xr netgraph 4
+and
+.Xr ng_pppoe 4
+for further details.
+.Pp
+If a
+.Ar host Ns No : Ns Ar port Ns Oo
+.No /tcp|udp
+.Oc
+specification is given,
+.Nm
+will attempt to connect to the given
+.Ar host
+on the given
+.Ar port .
+If a
+.Dq /tcp
+or
+.Dq /udp
+suffix is not provided, the default is
+.Dq /tcp .
+Refer to the section on
+.Em PPP OVER TCP and UDP
+above for further details.
+.Pp
+If multiple
+.Dq values
+are specified,
+.Nm
+will attempt to open each one in turn until it succeeds or runs out of
+devices.
+.It set dial Ar chat-script
+This specifies the chat script that will be used to dial the other
+side.
+See also the
+.Dq set login
+command below.
+Refer to
+.Xr chat 8
+and to the example configuration files for details of the chat script
+format.
+It is possible to specify some special
+.Sq values
+in your chat script as follows:
+.Bl -tag -width 2n
+.It Li \ec
+When used as the last character in a
+.Sq send
+string, this indicates that a newline should not be appended.
+.It Li \ed
+When the chat script encounters this sequence, it delays two seconds.
+.It Li \ep
+When the chat script encounters this sequence, it delays for one quarter of
+a second.
+.It Li \en
+This is replaced with a newline character.
+.It Li \er
+This is replaced with a carriage return character.
+.It Li \es
+This is replaced with a space character.
+.It Li \et
+This is replaced with a tab character.
+.It Li \eT
+This is replaced by the current phone number (see
+.Dq set phone
+below).
+.It Li \eP
+This is replaced by the current
+.Ar authkey
+value (see
+.Dq set authkey
+above).
+.It Li \eU
+This is replaced by the current
+.Ar authname
+value (see
+.Dq set authname
+above).
+.El
+.Pp
+Note that two parsers will examine these escape sequences, so in order to
+have the
+.Sq chat parser
+see the escape character, it is necessary to escape it from the
+.Sq command parser .
+This means that in practice you should use two escapes, for example:
+.Bd -literal -offset indent
+set dial "... ATDT\\\\T CONNECT"
+.Ed
+.Pp
+It is also possible to execute external commands from the chat script.
+To do this, the first character of the expect or send string is an
+exclamation mark
+.Pq Dq !\& .
+If a literal exclamation mark is required, double it up to
+.Dq !!\&
+and it will be treated as a single literal
+.Dq !\& .
+When the command is executed, standard input and standard output are
+directed to the open device (see the
+.Dq set device
+command), and standard error is read by
+.Nm
+and substituted as the expect or send string.
+If
+.Nm
+is running in interactive mode, file descriptor 3 is attached to
+.Pa /dev/tty .
+.Pp
+For example (wrapped for readability):
+.Bd -literal -offset indent
+set login "TIMEOUT 5 \\"\\" \\"\\" login:--login: ppp \e
+word: ppp \\"!sh \\\\-c \\\\\\"echo \\\\-n label: >&2\\\\\\"\\" \e
+\\"!/bin/echo in\\" HELLO"
+.Ed
+.Pp
+would result in the following chat sequence (output using the
+.Sq set log local chat
+command before dialing):
+.Bd -literal -offset indent
+Dial attempt 1 of 1
+dial OK!
+Chat: Expecting:
+Chat: Sending:
+Chat: Expecting: login:--login:
+Chat: Wait for (5): login:
+Chat: Sending: ppp
+Chat: Expecting: word:
+Chat: Wait for (5): word:
+Chat: Sending: ppp
+Chat: Expecting: !sh \\-c "echo \\-n label: >&2"
+Chat: Exec: sh -c "echo -n label: >&2"
+Chat: Wait for (5): !sh \\-c "echo \\-n label: >&2" --> label:
+Chat: Exec: /bin/echo in
+Chat: Sending:
+Chat: Expecting: HELLO
+Chat: Wait for (5): HELLO
+login OK!
+.Ed
+.Pp
+Note (again) the use of the escape character, allowing many levels of
+nesting.
+Here, there are four parsers at work.
+The first parses the original line, reading it as three arguments.
+The second parses the third argument, reading it as 11 arguments.
+At this point, it is
+important that the
+.Dq \&-
+signs are escaped, otherwise this parser will see them as constituting
+an expect-send-expect sequence.
+When the
+.Dq !\&
+character is seen, the execution parser reads the first command as three
+arguments, and then
+.Xr sh 1
+itself expands the argument after the
+.Fl c .
+As we wish to send the output back to the modem, in the first example
+we redirect our output to file descriptor 2 (stderr) so that
+.Nm
+itself sends and logs it, and in the second example, we just output to stdout,
+which is attached directly to the modem.
+.Pp
+This, of course means that it is possible to execute an entirely external
+.Dq chat
+command rather than using the internal one.
+See
+.Xr chat 8
+for a good alternative.
+.Pp
+The external command that is executed is subjected to the same special
+word expansions as the
+.Dq !bg
+command.
+.It set enddisc Op label|IP|MAC|magic|psn value
+This command sets our local endpoint discriminator.
+If set prior to LCP negotiation, and if no
+.Dq disable enddisc
+command has been used,
+.Nm
+will send the information to the peer using the LCP endpoint discriminator
+option.
+The following discriminators may be set:
+.Bl -tag -width indent
+.It Li label
+The current label is used.
+.It Li IP
+Our local IP number is used.
+As LCP is negotiated prior to IPCP, it is
+possible that the IPCP layer will subsequently change this value.
+If
+it does, the endpoint discriminator stays at the old value unless manually
+reset.
+.It Li MAC
+This is similar to the
+.Ar IP
+option above, except that the MAC address associated with the local IP
+number is used.
+If the local IP number is not resident on any Ethernet
+interface, the command will fail.
+.Pp
+As the local IP number defaults to whatever the machine host name is,
+.Dq set enddisc mac
+is usually done prior to any
+.Dq set ifaddr
+commands.
+.It Li magic
+A 20 digit random number is used.
+Care should be taken when using magic numbers as restarting
+.Nm
+or creating a link using a different
+.Nm
+invocation will also use a different magic number and will therefore not
+be recognised by the peer as belonging to the same bundle.
+This makes it unsuitable for
+.Fl direct
+connections.
+.It Li psn Ar value
+The given
+.Ar value
+is used.
+.Ar Value
+should be set to an absolute public switched network number with the
+country code first.
+.El
+.Pp
+If no arguments are given, the endpoint discriminator is reset.
+.It set escape Ar value...
+This option is similar to the
+.Dq set accmap
+option above.
+It allows the user to specify a set of characters that will be
+.Sq escaped
+as they travel across the link.
+.It set filter dial|alive|in|out Ar rule-no Xo
+.No permit|deny|clear| Ns Ar rule-no
+.Op !\&
+.Oo Op host
+.Ar src_addr Ns Op / Ns Ar width
+.Op Ar dst_addr Ns Op / Ns Ar width
+.Oc [ Ns Ar proto
+.Op src lt|eq|gt Ar port
+.Op dst lt|eq|gt Ar port
+.Op estab
+.Op syn
+.Op finrst
+.Op timeout Ar secs ]
+.Xc
+.Nm
+supports four filter sets.
+The
+.Em alive
+filter specifies packets that keep the connection alive - resetting the
+idle timer.
+The
+.Em dial
+filter specifies packets that cause
+.Nm
+to dial when in
+.Fl auto
+mode.
+The
+.Em in
+filter specifies packets that are allowed to travel
+into the machine and the
+.Em out
+filter specifies packets that are allowed out of the machine.
+.Pp
+Filtering is done prior to any IP alterations that might be done by the
+NAT engine on outgoing packets and after any IP alterations that might
+be done by the NAT engine on incoming packets.
+By default all empty filter sets allow all packets to pass.
+Rules are processed in order according to
+.Ar rule-no
+(unless skipped by specifying a rule number as the
+.Ar action ) .
+Up to 40 rules may be given for each set.
+If a packet doesn't match
+any of the rules in a given set, it is discarded.
+In the case of
+.Em in
+and
+.Em out
+filters, this means that the packet is dropped.
+In the case of
+.Em alive
+filters it means that the packet will not reset the idle timer (even if
+the
+.Ar in Ns No / Ns Ar out
+filter has a
+.Dq timeout
+value) and in the case of
+.Em dial
+filters it means that the packet will not trigger a dial.
+A packet failing to trigger a dial will be dropped rather than queued.
+Refer to the
+section on
+.Sx PACKET FILTERING
+above for further details.
+.It set hangup Ar chat-script
+This specifies the chat script that will be used to reset the device
+before it is closed.
+It should not normally be necessary, but can
+be used for devices that fail to reset themselves properly on close.
+.It set help|? Op Ar command
+This command gives a summary of available set commands, or if
+.Ar command
+is specified, the command usage is shown.
+.It set ifaddr Oo Ar myaddr Ns
+.Op / Ns Ar \&nn
+.Oo Ar hisaddr Ns Op / Ns Ar \&nn
+.Oo Ar netmask
+.Op Ar triggeraddr
+.Oc Oc
+.Oc
+This command specifies the IP addresses that will be used during
+IPCP negotiation.
+Addresses are specified using the format
+.Pp
+.Dl a.b.c.d/nn
+.Pp
+Where
+.Dq a.b.c.d
+is the preferred IP, but
+.Ar nn
+specifies how many bits of the address we will insist on.
+If
+.No / Ns Ar nn
+is omitted, it defaults to
+.Dq /32
+unless the IP address is 0.0.0.0 in which case it defaults to
+.Dq /0 .
+.Pp
+If you wish to assign a dynamic IP number to the peer,
+.Ar hisaddr
+may also be specified as a range of IP numbers in the format
+.Bd -ragged -offset indent
+.Ar \&IP Ns Oo \&- Ns Ar \&IP Ns Xo
+.Oc Ns Oo , Ns Ar \&IP Ns
+.Op \&- Ns Ar \&IP Ns
+.Oc Ns ...
+.Xc
+.Ed
+.Pp
+for example:
+.Pp
+.Dl set ifaddr 10.0.0.1 10.0.1.2-10.0.1.10,10.0.1.20
+.Pp
+will only negotiate
+.Dq 10.0.0.1
+as the local IP number, but may assign any of the given 10 IP
+numbers to the peer.
+If the peer requests one of these numbers,
+and that number is not already in use,
+.Nm
+will grant the peers request.
+This is useful if the peer wants
+to re-establish a link using the same IP number as was previously
+allocated (thus maintaining any existing tcp or udp connections).
+.Pp
+If the peer requests an IP number that's either outside
+of this range or is already in use,
+.Nm
+will suggest a random unused IP number from the range.
+.Pp
+If
+.Ar triggeraddr
+is specified, it is used in place of
+.Ar myaddr
+in the initial IPCP negotiation.
+However, only an address in the
+.Ar myaddr
+range will be accepted.
+This is useful when negotiating with some
+.Dv PPP
+implementations that will not assign an IP number unless their peer
+requests
+.Dq 0.0.0.0 .
+.Pp
+It should be noted that in
+.Fl auto
+mode,
+.Nm
+will configure the interface immediately upon reading the
+.Dq set ifaddr
+line in the config file.
+In any other mode, these values are just
+used for IPCP negotiations, and the interface isn't configured
+until the IPCP layer is up.
+.Pp
+Note that the
+.Ar HISADDR
+argument may be overridden by the third field in the
+.Pa ppp.secret
+file once the client has authenticated itself
+(if PAP or CHAP are
+.Dq enabled ) .
+Refer to the
+.Sx AUTHENTICATING INCOMING CONNECTIONS
+section for details.
+.Pp
+In all cases, if the interface is already configured,
+.Nm
+will try to maintain the interface IP numbers so that any existing
+bound sockets will remain valid.
+.It set ifqueue Ar packets
+Set the maximum number of packets that
+.Nm
+will read from the tunnel interface while data cannot be sent to any of
+the available links.
+This queue limit is necessary to flow control outgoing data as the tunnel
+interface is likely to be far faster than the combined links available to
+.Nm .
+.Pp
+If
+.Ar packets
+is set to a value less than the number of links,
+.Nm
+will read up to that value regardless.
+This prevents any possible latency problems.
+.Pp
+The default value for
+.Ar packets
+is
+.Dq 30 .
+.It set ccpretry|ccpretries Oo Ar timeout
+.Op Ar reqtries Op Ar trmtries
+.Oc
+.It set chapretry|chapretries Oo Ar timeout
+.Op Ar reqtries
+.Oc
+.It set ipcpretry|ipcpretries Oo Ar timeout
+.Op Ar reqtries Op Ar trmtries
+.Oc
+.It set lcpretry|lcpretries Oo Ar timeout
+.Op Ar reqtries Op Ar trmtries
+.Oc
+.It set papretry|papretries Oo Ar timeout
+.Op Ar reqtries
+.Oc
+These commands set the number of seconds that
+.Nm
+will wait before resending Finite State Machine (FSM) Request packets.
+The default
+.Ar timeout
+for all FSMs is 3 seconds (which should suffice in most cases).
+.Pp
+If
+.Ar reqtries
+is specified, it tells
+.Nm
+how many configuration request attempts it should make while receiving
+no reply from the peer before giving up.
+The default is 5 attempts for
+CCP, LCP and IPCP and 3 attempts for PAP and CHAP.
+.Pp
+If
+.Ar trmtries
+is specified, it tells
+.Nm
+how many terminate requests should be sent before giving up waiting for the
+peers response.
+The default is 3 attempts.
+Authentication protocols are
+not terminated and it is therefore invalid to specify
+.Ar trmtries
+for PAP or CHAP.
+.Pp
+In order to avoid negotiations with the peer that will never converge,
+.Nm
+will only send at most 3 times the configured number of
+.Ar reqtries
+in any given negotiation session before giving up and closing that layer.
+.It set log Xo
+.Op local
+.Op +|- Ns
+.Ar value Ns No ...
+.Xc
+This command allows the adjustment of the current log level.
+Refer to the Logging Facility section for further details.
+.It set login Ar chat-script
+This
+.Ar chat-script
+compliments the dial-script.
+If both are specified, the login
+script will be executed after the dial script.
+Escape sequences available in the dial script are also available here.
+.It set logout Ar chat-script
+This specifies the chat script that will be used to logout
+before the hangup script is called.
+It should not normally be necessary.
+.It set lqrperiod Ar frequency
+This command sets the
+.Ar frequency
+in seconds at which
+.Em LQR
+or
+.Em ECHO LQR
+packets are sent.
+The default is 30 seconds.
+You must also use the
+.Dq enable lqr
+command if you wish to send LQR requests to the peer.
+.It set mode Ar interactive|auto|ddial|background
+This command allows you to change the
+.Sq mode
+of the specified link.
+This is normally only useful in multi-link mode,
+but may also be used in uni-link mode.
+.Pp
+It is not possible to change a link that is
+.Sq direct
+or
+.Sq dedicated .
+.Pp
+Note: If you issue the command
+.Dq set mode auto ,
+and have network address translation enabled, it may be useful to
+.Dq enable iface-alias
+afterwards.
+This will allow
+.Nm
+to do the necessary address translations to enable the process that
+triggers the connection to connect once the link is up despite the
+peer assigning us a new (dynamic) IP address.
+.It set mppe Op 40|56|128|* Op stateless|stateful|*
+This option selects the encryption parameters used when negotiation
+MPPE.
+MPPE can be disabled entirely with the
+.Dq disable mppe
+command.
+If no arguments are given,
+.Nm
+will attempt to negotiate a stateful link with a 128 bit key, but
+will agree to whatever the peer requests (including no encryption
+at all).
+.Pp
+If any arguments are given,
+.Nm
+will
+.Em insist
+on using MPPE and will close the link if it's rejected by the peer (Note;
+this behaviour can be overridden by a configured RADIUS server).
+.Pp
+The first argument specifies the number of bits that
+.Nm
+should insist on during negotiations and the second specifies whether
+.Nm
+should insist on stateful or stateless mode.
+In stateless mode, the
+encryption dictionary is re-initialised with every packet according to
+an encryption key that is changed with every packet.
+In stateful mode,
+the encryption dictionary is re-initialised every 256 packets or after
+the loss of any data and the key is changed every 256 packets.
+Stateless mode is less efficient but is better for unreliable transport
+layers.
+.It set mrru Op Ar value
+Setting this option enables Multi-link PPP negotiations, also known as
+Multi-link Protocol or MP.
+There is no default MRRU (Maximum Reconstructed Receive Unit) value.
+If no argument is given, multi-link mode is disabled.
+.It set mru Xo
+.Op max Ns Op imum
+.Op Ar value
+.Xc
+The default MRU (Maximum Receive Unit) is 1500.
+If it is increased, the other side *may* increase its MTU.
+In theory there is no point in decreasing the MRU to below the default as the
+.Em PPP
+protocol says implementations *must* be able to accept packets of at
+least 1500 octets.
+.Pp
+If the
+.Dq maximum
+keyword is used,
+.Nm
+will refuse to negotiate a higher value.
+The maximum MRU can be set to 2048 at most.
+Setting a maximum of less than 1500 violates the
+.Em PPP
+rfc, but may sometimes be necessary.
+For example,
+.Em PPPoE
+imposes a maximum of 1492 due to hardware limitations.
+.Pp
+If no argument is given, 1500 is assumed.
+A value must be given when
+.Dq maximum
+is specified.
+.It set mtu Xo
+.Op max Ns Op imum
+.Op Ar value
+.Xc
+The default MTU is 1500.
+At negotiation time,
+.Nm
+will accept whatever MRU the peer requests (assuming it's
+not less than 296 bytes or greater than the assigned maximum).
+If the MTU is set,
+.Nm
+will not accept MRU values less than
+.Ar value .
+When negotiations are complete, the MTU is used when writing to the
+interface, even if the peer requested a higher value MRU.
+This can be useful for
+limiting your packet size (giving better bandwidth sharing at the expense
+of more header data).
+.Pp
+If the
+.Dq maximum
+keyword is used,
+.Nm
+will refuse to negotiate a higher value.
+The maximum MTU can be set to 2048 at most.
+.Pp
+If no
+.Ar value
+is given, 1500, or whatever the peer asks for is used.
+A value must be given when
+.Dq maximum
+is specified.
+.It set nbns Op Ar x.x.x.x Op Ar y.y.y.y
+This option allows the setting of the Microsoft NetBIOS name server
+values to be returned at the peers request.
+If no values are given,
+.Nm
+will reject any such requests.
+.It set openmode active|passive Op Ar delay
+By default,
+.Ar openmode
+is always
+.Ar active
+with a one second
+.Ar delay .
+That is,
+.Nm
+will always initiate LCP/IPCP/CCP negotiation one second after the line
+comes up.
+If you want to wait for the peer to initiate negotiations, you
+can use the value
+.Ar passive .
+If you want to initiate negotiations immediately or after more than one
+second, the appropriate
+.Ar delay
+may be specified here in seconds.
+.It set parity odd|even|none|mark
+This allows the line parity to be set.
+The default value is
+.Ar none .
+.It set phone Ar telno Ns Xo
+.Oo \&| Ns Ar backupnumber
+.Oc Ns ... Ns Oo : Ns Ar nextnumber
+.Oc Ns ...
+.Xc
+This allows the specification of the phone number to be used in
+place of the \\\\T string in the dial and login chat scripts.
+Multiple phone numbers may be given separated either by a pipe
+.Pq Dq \&|
+or a colon
+.Pq Dq \&: .
+.Pp
+Numbers after the pipe are only dialed if the dial or login
+script for the previous number failed.
+.Pp
+Numbers after the colon are tried sequentially, irrespective of
+the reason the line was dropped.
+.Pp
+If multiple numbers are given,
+.Nm
+will dial them according to these rules until a connection is made, retrying
+the maximum number of times specified by
+.Dq set redial
+below.
+In
+.Fl background
+mode, each number is attempted at most once.
+.It set Op proc Ns Xo
+.No title Op Ar value
+.Xc
+The current process title as displayed by
+.Xr ps 1
+is changed according to
+.Ar value .
+If
+.Ar value
+is not specified, the original process title is restored.
+All the
+word replacements done by the shell commands (see the
+.Dq bg
+command above) are done here too.
+.Pp
+Note, if USER is required in the process title, the
+.Dq set proctitle
+command must appear in
+.Pa ppp.linkup ,
+as it is not known when the commands in
+.Pa ppp.conf
+are executed.
+.It set radius Op Ar config-file
+This command enables RADIUS support (if it's compiled in).
+.Ar config-file
+refers to the radius client configuration file as described in
+.Xr radius.conf 5 .
+If PAP, CHAP, MSCHAP or MSCHAPv2 are
+.Dq enable Ns No d ,
+.Nm
+behaves as a
+.Em \&N Ns No etwork
+.Em \&A Ns No ccess
+.Em \&S Ns No erver
+and uses the configured RADIUS server to authenticate rather than
+authenticating from the
+.Pa ppp.secret
+file or from the passwd database.
+.Pp
+If none of PAP, CHAP, MSCHAP or MSCHAPv2 are enabled,
+.Dq set radius
+will do nothing.
+.Pp
+.Nm
+uses the following attributes from the RADIUS reply:
+.Bl -tag -width XXX -offset XXX
+.It RAD_FRAMED_IP_ADDRESS
+The peer IP address is set to the given value.
+.It RAD_FRAMED_IP_NETMASK
+The tun interface netmask is set to the given value.
+.It RAD_FRAMED_MTU
+If the given MTU is less than the peers MRU as agreed during LCP
+negotiation, *and* it is less that any configured MTU (see the
+.Dq set mru
+command), the tun interface MTU is set to the given value.
+.It RAD_FRAMED_COMPRESSION
+If the received compression type is
+.Dq 1 ,
+.Nm
+will request VJ compression during IPCP negotiations despite any
+.Dq disable vj
+configuration command.
+.It RAD_FILTER_ID
+If this attribute is supplied,
+.Nm
+will attempt to use it as an additional label to load from the
+.Pa ppp.linkup
+and
+.Pa ppp.linkdown
+files.
+The load will be attempted before (and in addition to) the normal
+label search.
+If the label doesn't exist, no action is taken and
+.Nm
+proceeds to the normal load using the current label.
+.It RAD_FRAMED_ROUTE
+The received string is expected to be in the format
+.Ar dest Ns Op / Ns Ar bits
+.Ar gw
+.Op Ar metrics .
+Any specified metrics are ignored.
+.Dv MYADDR
+and
+.Dv HISADDR
+are understood as valid values for
+.Ar dest
+and
+.Ar gw ,
+.Dq default
+can be used for
+.Ar dest
+to sepcify the default route, and
+.Dq 0.0.0.0
+is understood to be the same as
+.Dq default
+for
+.Ar dest
+and
+.Dv HISADDR
+for
+.Ar gw .
+.Pp
+For example, a returned value of
+.Dq 1.2.3.4/24 0.0.0.0 1 2 -1 3 400
+would result in a routing table entry to the 1.2.3.0/24 network via
+.Dv HISADDR
+and a returned value of
+.Dq 0.0.0.0 0.0.0.0
+or
+.Dq default HISADDR
+would result in a default route to
+.Dv HISADDR .
+.Pp
+All RADIUS routes are applied after any sticky routes are applied, making
+RADIUS routes override configured routes.
+This also applies for RADIUS routes that don't {include} the
+.Dv MYADDR
+or
+.Dv HISADDR
+keywords.
+.Pp
+.It RAD_SESSION_TIMEOUT
+If supplied, the client connection is closed after the given number of
+seconds.
+.It RAD_REPLY_MESSAGE
+If supplied, this message is passed back to the peer as the authentication
+SUCCESS text.
+.It RAD_MICROSOFT_MS_CHAP_ERROR
+If this
+.Dv RAD_VENDOR_MICROSOFT
+vendor specific attribute is supplied, it is passed back to the peer as the
+authentication FAILURE text.
+.It RAD_MICROSOFT_MS_CHAP2_SUCCESS
+If this
+.Dv RAD_VENDOR_MICROSOFT
+vendor specific attribute is supplied and if MS-CHAPv2 authentication is
+being used, it is passed back to the peer as the authentication SUCCESS text.
+.It RAD_MICROSOFT_MS_MPPE_ENCRYPTION_POLICY
+If this
+.Dv RAD_VENDOR_MICROSOFT
+vendor specific attribute is supplied and has a value of 2 (Required),
+.Nm
+will insist that MPPE encryption is used (even if no
+.Dq set mppe
+configuration command has been given with arguments).
+If it is supplied with a value of 1 (Allowed), encryption is made optional
+(despite any
+.Dq set mppe
+configuration commands with arguments).
+.It RAD_MICROSOFT_MS_MPPE_ENCRYPTION_TYPES
+If this
+.Dv RAD_VENDOR_MICROSOFT
+vendor specific attribute is supplied, bits 1 and 2 are examined.
+If either or both are set, 40 bit and/or 128 bit (respectively) encryption
+options are set, overriding any given first argument to the
+.Dq set mppe
+command.
+Note, it is not currently possible for the RADIUS server to specify 56 bit
+encryption.
+.It RAD_MICROSOFT_MS_MPPE_RECV_KEY
+If this
+.Dv RAD_VENDOR_MICROSOFT
+vendor specific attribute is supplied, it's value is used as the master
+key for decryption of incoming data. When clients are authenticated using
+MSCHAPv2, the RADIUS server MUST provide this attribute if inbound MPPE is
+to function.
+.It RAD_MICROSOFT_MS_MPPE_SEND_KEY
+If this
+.Dv RAD_VENDOR_MICROSOFT
+vendor specific attribute is supplied, it's value is used as the master
+key for encryption of outgoing data. When clients are authenticated using
+MSCHAPv2, the RADIUS server MUST provide this attribute if outbound MPPE is
+to function.
+.El
+.Pp
+Values received from the RADIUS server may be viewed using
+.Dq show bundle .
+.It set reconnect Ar timeout ntries
+Should the line drop unexpectedly (due to loss of CD or LQR
+failure), a connection will be re-established after the given
+.Ar timeout .
+The line will be re-connected at most
+.Ar ntries
+times.
+.Ar Ntries
+defaults to zero.
+A value of
+.Ar random
+for
+.Ar timeout
+will result in a variable pause, somewhere between 1 and 30 seconds.
+.It set recvpipe Op Ar value
+This sets the routing table RECVPIPE value.
+The optimum value is just over twice the MTU value.
+If
+.Ar value
+is unspecified or zero, the default kernel controlled value is used.
+.It set redial Ar secs Ns Xo
+.Oo + Ns Ar inc Ns
+.Op - Ns Ar max Ns
+.Oc Ns Op . Ns Ar next
+.Op Ar attempts
+.Xc
+.Nm
+can be instructed to attempt to redial
+.Ar attempts
+times.
+If more than one phone number is specified (see
+.Dq set phone
+above), a pause of
+.Ar next
+is taken before dialing each number.
+A pause of
+.Ar secs
+is taken before starting at the first number again.
+A literal value of
+.Dq Li random
+may be used here in place of
+.Ar secs
+and
+.Ar next ,
+causing a random delay of between 1 and 30 seconds.
+.Pp
+If
+.Ar inc
+is specified, its value is added onto
+.Ar secs
+each time
+.Nm
+tries a new number.
+.Ar secs
+will only be incremented at most
+.Ar max
+times.
+.Ar max
+defaults to 10.
+.Pp
+Note, the
+.Ar secs
+delay will be effective, even after
+.Ar attempts
+has been exceeded, so an immediate manual dial may appear to have
+done nothing.
+If an immediate dial is required, a
+.Dq !\&
+should immediately follow the
+.Dq open
+keyword.
+See the
+.Dq open
+description above for further details.
+.It set sendpipe Op Ar value
+This sets the routing table SENDPIPE value.
+The optimum value is just over twice the MTU value.
+If
+.Ar value
+is unspecified or zero, the default kernel controlled value is used.
+.It "set server|socket" Ar TcpPort Ns No \&| Ns Xo
+.Ar LocalName Ns No |none|open|closed
+.Op password Op Ar mask
+.Xc
+This command tells
+.Nm
+to listen on the given socket or
+.Sq diagnostic port
+for incoming command connections.
+.Pp
+The word
+.Dq none
+instructs
+.Nm
+to close any existing socket and clear the socket configuration.
+The word
+.Dq open
+instructs
+.Nm
+to attempt to re-open the port.
+The word
+.Dq closed
+instructs
+.Nm
+to close the open port.
+.Pp
+If you wish to specify a local domain socket,
+.Ar LocalName
+must be specified as an absolute file name, otherwise it is assumed
+to be the name or number of a TCP port.
+You may specify the octal umask to be used with a local domain socket.
+Refer to
+.Xr umask 2
+for umask details.
+Refer to
+.Xr services 5
+for details of how to translate TCP port names.
+.Pp
+You must also specify the password that must be entered by the client
+(using the
+.Dq passwd
+variable above) when connecting to this socket.
+If the password is
+specified as an empty string, no password is required for connecting clients.
+.Pp
+When specifying a local domain socket, the first
+.Dq %d
+sequence found in the socket name will be replaced with the current
+interface unit number.
+This is useful when you wish to use the same
+profile for more than one connection.
+.Pp
+In a similar manner TCP sockets may be prefixed with the
+.Dq +
+character, in which case the current interface unit number is added to
+the port number.
+.Pp
+When using
+.Nm
+with a server socket, the
+.Xr pppctl 8
+command is the preferred mechanism of communications.
+Currently,
+.Xr telnet 1
+can also be used, but link encryption may be implemented in the future, so
+.Xr telnet 1
+should be avoided.
+.Pp
+Note;
+.Dv SIGUSR1
+and
+.Dv SIGUSR2
+interact with the diagnostic socket.
+.It set speed Ar value
+This sets the speed of the serial device.
+If speed is specified as
+.Dq sync ,
+.Nm
+treats the device as a synchronous device.
+.Pp
+Certain device types will know whether they should be specified as
+synchronous or asynchronous.
+These devices will override incorrect
+settings and log a warning to this effect.
+.It set stopped Op Ar LCPseconds Op Ar CCPseconds
+If this option is set,
+.Nm
+will time out after the given FSM (Finite State Machine) has been in
+the stopped state for the given number of
+.Dq seconds .
+This option may be useful if the peer sends a terminate request,
+but never actually closes the connection despite our sending a terminate
+acknowledgement.
+This is also useful if you wish to
+.Dq set openmode passive
+and time out if the peer doesn't send a Configure Request within the
+given time.
+Use
+.Dq set log +lcp +ccp
+to make
+.Nm
+log the appropriate state transitions.
+.Pp
+The default value is zero, where
+.Nm
+doesn't time out in the stopped state.
+.Pp
+This value should not be set to less than the openmode delay (see
+.Dq set openmode
+above).
+.It set timeout Ar idleseconds Op Ar mintimeout
+This command allows the setting of the idle timer.
+Refer to the section titled
+.Sx SETTING THE IDLE TIMER
+for further details.
+.Pp
+If
+.Ar mintimeout
+is specified,
+.Nm
+will never idle out before the link has been up for at least that number
+of seconds.
+.It set urgent Xo
+.Op tcp|udp|none
+.Oo Op +|- Ns
+.Ar port
+.Oc No ...
+.Xc
+This command controls the ports that
+.Nm
+prioritizes when transmitting data.
+The default priority TCP ports
+are ports 21 (ftp control), 22 (ssh), 23 (telnet), 513 (login), 514 (shell),
+543 (klogin) and 544 (kshell).
+There are no priority UDP ports by default.
+See
+.Xr services 5
+for details.
+.Pp
+If neither
+.Dq tcp
+or
+.Dq udp
+are specified,
+.Dq tcp
+is assumed.
+.Pp
+If no
+.Ar port Ns No s
+are given, the priority port lists are cleared (although if
+.Dq tcp
+or
+.Dq udp
+is specified, only that list is cleared).
+If the first
+.Ar port
+argument is prefixed with a plus
+.Pq Dq \&+
+or a minus
+.Pq Dq \&- ,
+the current list is adjusted, otherwise the list is reassigned.
+.Ar port Ns No s
+prefixed with a plus or not prefixed at all are added to the list and
+.Ar port Ns No s
+prefixed with a minus are removed from the list.
+.Pp
+If
+.Dq none
+is specified, all priority port lists are disabled and even
+.Dv IPTOS_LOWDELAY
+packets are not prioritised.
+.It set vj slotcomp on|off
+This command tells
+.Nm
+whether it should attempt to negotiate VJ slot compression.
+By default, slot compression is turned
+.Ar on .
+.It set vj slots Ar nslots
+This command sets the initial number of slots that
+.Nm
+will try to negotiate with the peer when VJ compression is enabled (see the
+.Sq enable
+command above).
+It defaults to a value of 16.
+.Ar Nslots
+must be between
+.Ar 4
+and
+.Ar 16
+inclusive.
+.El
+.Pp
+.It shell|! Op Ar command
+If
+.Ar command
+is not specified a shell is invoked according to the
+.Dv SHELL
+environment variable.
+Otherwise, the given
+.Ar command
+is executed.
+Word replacement is done in the same way as for the
+.Dq !bg
+command as described above.
+.Pp
+Use of the ! character
+requires a following space as with any of the other commands.
+You should note that this command is executed in the foreground;
+.Nm
+will not continue running until this process has exited.
+Use the
+.Dv bg
+command if you wish processing to happen in the background.
+.It show Ar var
+This command allows the user to examine the following:
+.Bl -tag -width 2n
+.It show bundle
+Show the current bundle settings.
+.It show ccp
+Show the current CCP compression statistics.
+.It show compress
+Show the current VJ compression statistics.
+.It show escape
+Show the current escape characters.
+.It show filter Op Ar name
+List the current rules for the given filter.
+If
+.Ar name
+is not specified, all filters are shown.
+.It show hdlc
+Show the current HDLC statistics.
+.It show help|?
+Give a summary of available show commands.
+.It show iface
+Show the current interface information
+(the same as
+.Dq iface show ) .
+.It show ipcp
+Show the current IPCP statistics.
+.It show layers
+Show the protocol layers currently in use.
+.It show lcp
+Show the current LCP statistics.
+.It show Op data Ns Xo
+.No link
+.Xc
+Show high level link information.
+.It show links
+Show a list of available logical links.
+.It show log
+Show the current log values.
+.It show mem
+Show current memory statistics.
+.It show ncp
+Show the current NCP statistics.
+.It show physical
+Show low level link information.
+.It show mp
+Show Multi-link information.
+.It show proto
+Show current protocol totals.
+.It show route
+Show the current routing tables.
+.It show stopped
+Show the current stopped timeouts.
+.It show timer
+Show the active alarm timers.
+.It show version
+Show the current version number of
+.Nm .
+.El
+.Pp
+.It term
+Go into terminal mode.
+Characters typed at the keyboard are sent to the device.
+Characters read from the device are displayed on the screen.
+When a remote
+.Em PPP
+peer is detected,
+.Nm
+automatically enables Packet Mode and goes back into command mode.
+.El
+.Sh MORE DETAILS
+.Bl -bullet
+.It
+Read the example configuration files.
+They are a good source of information.
+.It
+Use
+.Dq help ,
+.Dq nat \&? ,
+.Dq enable \&? ,
+.Dq set ?\&
+and
+.Dq show ?\&
+to get online information about what's available.
+.It
+The following URLs contain useful information:
+.Bl -bullet -compact
+.It
+http://www.FreeBSD.org/FAQ/userppp.html
+.It
+http://www.FreeBSD.org/handbook/userppp.html
+.El
+.Pp
+.El
+.Sh FILES
+.Nm
+refers to four files:
+.Pa ppp.conf ,
+.Pa ppp.linkup ,
+.Pa ppp.linkdown
+and
+.Pa ppp.secret .
+These files are placed in the
+.Pa /etc/ppp
+directory.
+.Bl -tag -width 2n
+.It Pa /etc/ppp/ppp.conf
+System default configuration file.
+.It Pa /etc/ppp/ppp.secret
+An authorisation file for each system.
+.It Pa /etc/ppp/ppp.linkup
+A file to check when
+.Nm
+establishes a network level connection.
+.It Pa /etc/ppp/ppp.linkdown
+A file to check when
+.Nm
+closes a network level connection.
+.It Pa /var/log/ppp.log
+Logging and debugging information file.
+Note, this name is specified in
+.Pa /etc/syslog.conf .
+See
+.Xr syslog.conf 5
+for further details.
+.It Pa /var/spool/lock/LCK..*
+tty port locking file.
+Refer to
+.Xr uucplock 3
+for further details.
+.It Pa /var/run/tunN.pid
+The process id (pid) of the
+.Nm
+program connected to the tunN device, where
+.Sq N
+is the number of the device.
+.It Pa /var/run/ttyXX.if
+The tun interface used by this port.
+Again, this file is only created in
+.Fl background ,
+.Fl auto
+and
+.Fl ddial
+modes.
+.It Pa /etc/services
+Get port number if port number is using service name.
+.It Pa /var/run/ppp-authname-class-value
+In multi-link mode, local domain sockets are created using the peer
+authentication name
+.Pq Sq authname ,
+the peer endpoint discriminator class
+.Pq Sq class
+and the peer endpoint discriminator value
+.Pq Sq value .
+As the endpoint discriminator value may be a binary value, it is turned
+to HEX to determine the actual file name.
+.Pp
+This socket is used to pass links between different instances of
+.Nm .
+.El
+.Sh SEE ALSO
+.Xr at 1 ,
+.Xr ftp 1 ,
+.Xr gzip 1 ,
+.Xr hostname 1 ,
+.Xr login 1 ,
+.Xr tcpdump 1 ,
+.Xr telnet 1 ,
+.Xr kldload 2 ,
+ifdef({LOCALNAT},{},{.Xr libalias 3 ,
+})dnl
+ifdef({LOCALRAD},{},{.Xr libradius 3 ,
+})dnl
+.Xr syslog 3 ,
+.Xr uucplock 3 ,
+.Xr netgraph 4 ,
+.Xr ng_pppoe 4 ,
+.Xr crontab 5 ,
+.Xr group 5 ,
+.Xr passwd 5 ,
+.Xr protocols 5 ,
+.Xr radius.conf 5 ,
+.Xr resolv.conf 5 ,
+.Xr syslog.conf 5 ,
+.Xr adduser 8 ,
+.Xr chat 8 ,
+.Xr getty 8 ,
+.Xr inetd 8 ,
+.Xr init 8 ,
+.Xr isdn 8 ,
+.Xr named 8 ,
+.Xr ping 8 ,
+.Xr pppctl 8 ,
+.Xr pppd 8 ,
+.Xr pppoe 8 ,
+.Xr route 8 ,
+.Xr sshd 8 ,
+.Xr syslogd 8 ,
+.Xr traceroute 8 ,
+.Xr vipw 8
+.Sh HISTORY
+This program was originally written by
+.An Toshiharu OHNO Aq tony-o@iij.ad.jp ,
+and was submitted to
+.Fx 2.0.5
+by
+.An Atsushi Murai Aq amurai@spec.co.jp .
+.Pp
+It was substantially modified during 1997 by
+.An Brian Somers Aq brian@Awfulhak.org ,
+and was ported to
+.Ox
+in November that year
+(just after the 2.2 release).
+.Pp
+Most of the code was rewritten by
+.An Brian Somers
+in early 1998 when multi-link ppp support was added.
diff --git a/usr.sbin/ppp/pred.c b/usr.sbin/ppp/pred.c
new file mode 100644
index 0000000..2a81381
--- /dev/null
+++ b/usr.sbin/ppp/pred.c
@@ -0,0 +1,343 @@
+/*-
+ * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
+ * Ian Donaldson <iand@labtam.labtam.oz.au>
+ * Carsten Bormann <cabo@cs.tu-berlin.de>
+ * Dave Rand <dlr@bungi.com>/<dave_rand@novell.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "defs.h"
+#include "layer.h"
+#include "mbuf.h"
+#include "log.h"
+#include "timer.h"
+#include "fsm.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "throughput.h"
+#include "link.h"
+#include "pred.h"
+
+/* The following hash code is the heart of the algorithm:
+ * It builds a sliding hash sum of the previous 3-and-a-bit characters
+ * which will be used to index the guess table.
+ * A better hash function would result in additional compression,
+ * at the expense of time.
+ */
+#define HASH(state, x) state->hash = (state->hash << 4) ^ (x)
+#define GUESS_TABLE_SIZE 65536
+
+struct pred1_state {
+ u_short hash;
+ u_char dict[GUESS_TABLE_SIZE];
+};
+
+static int
+compress(struct pred1_state *state, u_char *source, u_char *dest, int len)
+{
+ int i, bitmask;
+ unsigned char *flagdest, flags, *orgdest;
+
+ orgdest = dest;
+ while (len) {
+ flagdest = dest++;
+ flags = 0; /* All guess wrong initially */
+ for (bitmask = 1, i = 0; i < 8 && len; i++, bitmask <<= 1) {
+ if (state->dict[state->hash] == *source) {
+ flags |= bitmask; /* Guess was right - don't output */
+ } else {
+ state->dict[state->hash] = *source;
+ *dest++ = *source; /* Guess wrong, output char */
+ }
+ HASH(state, *source++);
+ len--;
+ }
+ *flagdest = flags;
+ }
+ return (dest - orgdest);
+}
+
+static void
+SyncTable(struct pred1_state *state, u_char *source, u_char *dest, int len)
+{
+ while (len--) {
+ *dest++ = state->dict[state->hash] = *source;
+ HASH(state, *source++);
+ }
+}
+
+static int
+decompress(struct pred1_state *state, u_char *source, u_char *dest, int len)
+{
+ int i, bitmask;
+ unsigned char flags, *orgdest;
+
+ orgdest = dest;
+ while (len) {
+ flags = *source++;
+ len--;
+ for (i = 0, bitmask = 1; i < 8; i++, bitmask <<= 1) {
+ if (flags & bitmask) {
+ *dest = state->dict[state->hash]; /* Guess correct */
+ } else {
+ if (!len)
+ break; /* we seem to be really done -- cabo */
+ state->dict[state->hash] = *source; /* Guess wrong */
+ *dest = *source++; /* Read from source */
+ len--;
+ }
+ HASH(state, *dest++);
+ }
+ }
+ return (dest - orgdest);
+}
+
+static void
+Pred1Term(void *v)
+{
+ struct pred1_state *state = (struct pred1_state *)v;
+ free(state);
+}
+
+static void
+Pred1ResetInput(void *v)
+{
+ struct pred1_state *state = (struct pred1_state *)v;
+ state->hash = 0;
+ memset(state->dict, '\0', sizeof state->dict);
+ log_Printf(LogCCP, "Predictor1: Input channel reset\n");
+}
+
+static int
+Pred1ResetOutput(void *v)
+{
+ struct pred1_state *state = (struct pred1_state *)v;
+ state->hash = 0;
+ memset(state->dict, '\0', sizeof state->dict);
+ log_Printf(LogCCP, "Predictor1: Output channel reset\n");
+
+ return 1; /* Ask FSM to ACK */
+}
+
+static void *
+Pred1InitInput(struct bundle *bundle, struct fsm_opt *o)
+{
+ struct pred1_state *state;
+ state = (struct pred1_state *)malloc(sizeof(struct pred1_state));
+ if (state != NULL)
+ Pred1ResetInput(state);
+ return state;
+}
+
+static void *
+Pred1InitOutput(struct bundle *bundle, struct fsm_opt *o)
+{
+ struct pred1_state *state;
+ state = (struct pred1_state *)malloc(sizeof(struct pred1_state));
+ if (state != NULL)
+ Pred1ResetOutput(state);
+ return state;
+}
+
+static struct mbuf *
+Pred1Output(void *v, struct ccp *ccp, struct link *l, int pri, u_short *proto,
+ struct mbuf *bp)
+{
+ struct pred1_state *state = (struct pred1_state *)v;
+ struct mbuf *mwp;
+ u_char *cp, *wp, *hp;
+ int orglen, len;
+ u_char bufp[MAX_MTU + 2];
+ u_short fcs;
+
+ orglen = m_length(bp) + 2; /* add count of proto */
+ mwp = m_get((orglen + 2) / 8 * 9 + 12, MB_CCPOUT);
+ hp = wp = MBUF_CTOP(mwp);
+ cp = bufp;
+ *wp++ = *cp++ = orglen >> 8;
+ *wp++ = *cp++ = orglen & 0377;
+ *cp++ = *proto >> 8;
+ *cp++ = *proto & 0377;
+ mbuf_Read(bp, cp, orglen - 2);
+ fcs = hdlc_Fcs(bufp, 2 + orglen);
+ fcs = ~fcs;
+
+ len = compress(state, bufp + 2, wp, orglen);
+ log_Printf(LogDEBUG, "Pred1Output: orglen (%d) --> len (%d)\n", orglen, len);
+ ccp->uncompout += orglen;
+ if (len < orglen) {
+ *hp |= 0x80;
+ wp += len;
+ ccp->compout += len;
+ } else {
+ memcpy(wp, bufp + 2, orglen);
+ wp += orglen;
+ ccp->compout += orglen;
+ }
+
+ *wp++ = fcs & 0377;
+ *wp++ = fcs >> 8;
+ mwp->m_len = wp - MBUF_CTOP(mwp);
+ *proto = ccp_Proto(ccp);
+ return mwp;
+}
+
+static struct mbuf *
+Pred1Input(void *v, struct ccp *ccp, u_short *proto, struct mbuf *bp)
+{
+ struct pred1_state *state = (struct pred1_state *)v;
+ u_char *cp, *pp;
+ int len, olen, len1;
+ struct mbuf *wp;
+ u_char *bufp;
+ u_short fcs;
+
+ wp = m_get(MAX_MRU + 2, MB_CCPIN);
+ cp = MBUF_CTOP(bp);
+ olen = m_length(bp);
+ pp = bufp = MBUF_CTOP(wp);
+ *pp++ = *cp & 0177;
+ len = *cp++ << 8;
+ *pp++ = *cp;
+ len += *cp++;
+ ccp->uncompin += len & 0x7fff;
+ if (len & 0x8000) {
+ len1 = decompress(state, cp, pp, olen - 4);
+ ccp->compin += olen;
+ len &= 0x7fff;
+ if (len != len1) { /* Error is detected. Send reset request */
+ log_Printf(LogCCP, "Pred1: Length error (got %d, not %d)\n", len1, len);
+ fsm_Reopen(&ccp->fsm);
+ m_freem(bp);
+ m_freem(wp);
+ return NULL;
+ }
+ cp += olen - 4;
+ pp += len1;
+ } else if (len + 4 != olen) {
+ log_Printf(LogCCP, "Pred1: Length error (got %d, not %d)\n", len + 4, olen);
+ fsm_Reopen(&ccp->fsm);
+ m_freem(wp);
+ m_freem(bp);
+ return NULL;
+ } else {
+ ccp->compin += len;
+ SyncTable(state, cp, pp, len);
+ cp += len;
+ pp += len;
+ }
+ *pp++ = *cp++; /* CRC */
+ *pp++ = *cp++;
+ fcs = hdlc_Fcs(bufp, wp->m_len = pp - bufp);
+ if (fcs == GOODFCS) {
+ wp->m_offset += 2; /* skip length */
+ wp->m_len -= 4; /* skip length & CRC */
+ pp = MBUF_CTOP(wp);
+ *proto = *pp++;
+ if (*proto & 1) {
+ wp->m_offset++;
+ wp->m_len--;
+ } else {
+ wp->m_offset += 2;
+ wp->m_len -= 2;
+ *proto = (*proto << 8) | *pp++;
+ }
+ m_freem(bp);
+ return wp;
+ } else {
+ const char *pre = *MBUF_CTOP(bp) & 0x80 ? "" : "un";
+ log_Printf(LogDEBUG, "Pred1Input: fcs = 0x%04x (%scompressed), len = 0x%x,"
+ " olen = 0x%x\n", fcs, pre, len, olen);
+ log_Printf(LogCCP, "%s: Bad %scompressed CRC-16\n",
+ ccp->fsm.link->name, pre);
+ fsm_Reopen(&ccp->fsm);
+ m_freem(wp);
+ }
+ m_freem(bp);
+ return NULL;
+}
+
+static void
+Pred1DictSetup(void *v, struct ccp *ccp, u_short proto, struct mbuf *bp)
+{
+}
+
+static const char *
+Pred1DispOpts(struct fsm_opt *o)
+{
+ return NULL;
+}
+
+static void
+Pred1InitOptsOutput(struct bundle *bundle, struct fsm_opt *o,
+ const struct ccp_config *cfg)
+{
+ o->hdr.len = 2;
+}
+
+static int
+Pred1SetOpts(struct bundle *bundle, struct fsm_opt *o,
+ const struct ccp_config *cfg)
+{
+ if (o->hdr.len != 2) {
+ o->hdr.len = 2;
+ return MODE_NAK;
+ }
+ return MODE_ACK;
+}
+
+const struct ccp_algorithm Pred1Algorithm = {
+ TY_PRED1,
+ CCP_NEG_PRED1,
+ Pred1DispOpts,
+ ccp_DefaultUsable,
+ ccp_DefaultRequired,
+ {
+ Pred1SetOpts,
+ Pred1InitInput,
+ Pred1Term,
+ Pred1ResetInput,
+ Pred1Input,
+ Pred1DictSetup
+ },
+ {
+ 0,
+ Pred1InitOptsOutput,
+ Pred1SetOpts,
+ Pred1InitOutput,
+ Pred1Term,
+ Pred1ResetOutput,
+ Pred1Output
+ },
+};
diff --git a/usr.sbin/ppp/pred.h b/usr.sbin/ppp/pred.h
new file mode 100644
index 0000000..1afb77a
--- /dev/null
+++ b/usr.sbin/ppp/pred.h
@@ -0,0 +1,31 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+extern const struct ccp_algorithm Pred1Algorithm;
diff --git a/usr.sbin/ppp/probe.c b/usr.sbin/ppp/probe.c
new file mode 100644
index 0000000..a33734c
--- /dev/null
+++ b/usr.sbin/ppp/probe.c
@@ -0,0 +1,78 @@
+/*-
+ * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/time.h>
+#include <sys/socket.h>
+
+#include <stdio.h>
+#include <unistd.h>
+
+#include "probe.h"
+#include "log.h"
+#include "id.h"
+
+struct probe probe;
+
+/* Does select() alter the passed time value ? */
+static int
+select_changes_time(void)
+{
+ struct timeval t;
+
+ t.tv_sec = 0;
+ t.tv_usec = 100000;
+ select(0, NULL, NULL, NULL, &t);
+ return t.tv_usec != 100000;
+}
+
+#ifndef NOINET6
+static int
+ipv6_available(void)
+{
+ int s;
+
+ if ((s = ID0socket(PF_INET6, SOCK_DGRAM, 0)) == -1)
+ return 0;
+
+ close(s);
+ return 1;
+}
+#endif
+
+void
+probe_Init()
+{
+ probe.select_changes_time = select_changes_time() ? 1 : 0;
+ log_Printf(LogDEBUG, "Select changes time: %s\n",
+ probe.select_changes_time ? "yes" : "no");
+#ifndef NOINET6
+ probe.ipv6_available = ipv6_available() ? 1 : 0;
+ log_Printf(LogDEBUG, "IPv6 available: %s\n",
+ probe.ipv6_available ? "yes" : "no");
+#endif
+}
diff --git a/usr.sbin/ppp/probe.h b/usr.sbin/ppp/probe.h
new file mode 100644
index 0000000..5a7dce9
--- /dev/null
+++ b/usr.sbin/ppp/probe.h
@@ -0,0 +1,38 @@
+/*-
+ * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct probe {
+ unsigned select_changes_time : 1;
+#ifndef NOINET6
+ unsigned ipv6_available : 1;
+#endif
+};
+
+extern struct probe probe;
+
+extern void probe_Init(void);
diff --git a/usr.sbin/ppp/prompt.c b/usr.sbin/ppp/prompt.c
new file mode 100644
index 0000000..7f1f94e
--- /dev/null
+++ b/usr.sbin/ppp/prompt.c
@@ -0,0 +1,566 @@
+/*-
+ * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/fcntl.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "layer.h"
+#include "defs.h"
+#include "timer.h"
+#include "command.h"
+#include "log.h"
+#include "descriptor.h"
+#include "prompt.h"
+#include "fsm.h"
+#include "auth.h"
+#include "iplist.h"
+#include "throughput.h"
+#include "slcompress.h"
+#include "mbuf.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "lcp.h"
+#include "ncpaddr.h"
+#include "ipcp.h"
+#include "filter.h"
+#include "async.h"
+#include "ccp.h"
+#include "link.h"
+#include "physical.h"
+#include "mp.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+#include "chat.h"
+#include "chap.h"
+#include "cbcp.h"
+#include "datalink.h"
+#include "server.h"
+#include "main.h"
+
+static void
+prompt_Display(struct prompt *p)
+{
+ /* XXX: See Index2Nam() - should we only figure this out once ? */
+ static char shostname[MAXHOSTNAMELEN];
+ const char *pconnect, *pauth;
+
+ if (p->TermMode || !p->needprompt)
+ return;
+
+ p->needprompt = 0;
+
+ if (p->nonewline)
+ p->nonewline = 0;
+ else
+ fprintf(p->Term, "\n");
+
+ if (p->auth == LOCAL_AUTH)
+ pauth = " ON ";
+ else
+ pauth = " on ";
+
+ if (p->bundle->ncp.ipcp.fsm.state == ST_OPENED)
+ pconnect = "PPP";
+ else if (bundle_Phase(p->bundle) == PHASE_NETWORK)
+ pconnect = "PPp";
+ else if (bundle_Phase(p->bundle) == PHASE_AUTHENTICATE)
+ pconnect = "Ppp";
+ else
+ pconnect = "ppp";
+
+ if (*shostname == '\0') {
+ char *dot;
+
+ if (gethostname(shostname, sizeof shostname) || *shostname == '\0')
+ strcpy(shostname, "localhost");
+ else if ((dot = strchr(shostname, '.')))
+ *dot = '\0';
+ }
+
+ fprintf(p->Term, "%s%s%s> ", pconnect, pauth, shostname);
+ fflush(p->Term);
+}
+
+static int
+prompt_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n)
+{
+ struct prompt *p = descriptor2prompt(d);
+ int sets;
+
+ sets = 0;
+
+ if (!p->active)
+ return sets;
+
+ if (p->fd_in >= 0) {
+ if (r) {
+ FD_SET(p->fd_in, r);
+ log_Printf(LogTIMER, "prompt %s: fdset(r) %d\n", p->src.from, p->fd_in);
+ sets++;
+ }
+ if (e) {
+ FD_SET(p->fd_in, e);
+ log_Printf(LogTIMER, "prompt %s: fdset(e) %d\n", p->src.from, p->fd_in);
+ sets++;
+ }
+ if (sets && *n < p->fd_in + 1)
+ *n = p->fd_in + 1;
+ }
+
+ prompt_Display(p);
+
+ return sets;
+}
+
+static int
+prompt_IsSet(struct fdescriptor *d, const fd_set *fdset)
+{
+ struct prompt *p = descriptor2prompt(d);
+ return p->fd_in >= 0 && FD_ISSET(p->fd_in, fdset);
+}
+
+
+static void
+prompt_ShowHelp(struct prompt *p)
+{
+ prompt_Printf(p, "The following commands are available:\n");
+ prompt_Printf(p, " ~p\tEnter Packet mode\n");
+ prompt_Printf(p, " ~t\tShow timers\n");
+ prompt_Printf(p, " ~m\tShow memory map\n");
+ prompt_Printf(p, " ~.\tTerminate program\n");
+ prompt_Printf(p, " ~?\tThis help\n");
+}
+
+static void
+prompt_Read(struct fdescriptor *d, struct bundle *bundle, const fd_set *fdset)
+{
+ struct prompt *p = descriptor2prompt(d);
+ struct prompt *op;
+ int n;
+ char ch;
+ char linebuff[LINE_LEN];
+
+ if (p->TermMode == NULL) {
+ n = read(p->fd_in, linebuff, sizeof linebuff - 1);
+ if (n > 0) {
+ if (linebuff[n-1] == '\n')
+ linebuff[--n] = '\0';
+ else
+ linebuff[n] = '\0';
+ p->nonewline = 1; /* Maybe command_Decode does a prompt */
+ prompt_Required(p);
+ if (n) {
+ if ((op = log_PromptContext) == NULL)
+ log_PromptContext = p;
+ if (!command_Decode(bundle, linebuff, n, p, p->src.from))
+ prompt_Printf(p, "Syntax error\n");
+ log_PromptContext = op;
+ }
+ } else if (n <= 0) {
+ log_Printf(LogPHASE, "%s: Client connection closed.\n", p->src.from);
+ if (!p->owner)
+ Cleanup(EX_NORMAL);
+ prompt_Destroy(p, 0);
+ }
+ return;
+ }
+
+ switch (p->TermMode->state) {
+ case DATALINK_CLOSED:
+ prompt_Printf(p, "Link lost, terminal mode.\n");
+ prompt_TtyCommandMode(p);
+ p->nonewline = 0;
+ prompt_Required(p);
+ return;
+
+ case DATALINK_READY:
+ break;
+
+ case DATALINK_OPEN:
+ prompt_Printf(p, "\nPacket mode detected.\n");
+ prompt_TtyCommandMode(p);
+ p->nonewline = 0;
+ /* We'll get a prompt because of our status change */
+ /* 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 fdescriptor *d, struct bundle *bundle, const fd_set *fdset)
+{
+ /* We never want to write here ! */
+ log_Printf(LogALERT, "prompt_Write: Internal error: Bad call !\n");
+ return 0;
+}
+
+struct prompt *
+prompt_Create(struct server *s, struct bundle *bundle, int fd)
+{
+ struct prompt *p = (struct prompt *)malloc(sizeof(struct prompt));
+
+ if (p != NULL) {
+ p->desc.type = PROMPT_DESCRIPTOR;
+ p->desc.UpdateSet = prompt_UpdateSet;
+ p->desc.IsSet = prompt_IsSet;
+ p->desc.Read = prompt_Read;
+ p->desc.Write = prompt_Write;
+
+ if (fd == PROMPT_STD) {
+ char *tty = ttyname(STDIN_FILENO);
+
+ if (!tty) {
+ free(p);
+ return NULL;
+ }
+ p->fd_in = STDIN_FILENO;
+ p->fd_out = STDOUT_FILENO;
+ p->Term = stdout;
+ p->owner = NULL;
+ p->auth = LOCAL_AUTH;
+ p->src.type = "Controller";
+ strncpy(p->src.from, tty, sizeof p->src.from - 1);
+ p->src.from[sizeof p->src.from - 1] = '\0';
+ tcgetattr(p->fd_in, &p->oldtio); /* Save original tty mode */
+ } else {
+ p->fd_in = p->fd_out = fd;
+ p->Term = fdopen(fd, "a+");
+ p->owner = s;
+ p->auth = *s->cfg.passwd ? LOCAL_NO_AUTH : LOCAL_AUTH;
+ p->src.type = "unknown";
+ *p->src.from = '\0';
+ }
+ p->TermMode = NULL;
+ p->nonewline = 1;
+ p->needprompt = 1;
+ p->readtilde = 0;
+ p->bundle = bundle;
+ log_RegisterPrompt(p);
+ }
+
+ return p;
+}
+
+void
+prompt_Destroy(struct prompt *p, int verbose)
+{
+ if (p) {
+ if (p->Term != stdout) {
+ fclose(p->Term);
+ close(p->fd_in);
+ if (p->fd_out != p->fd_in)
+ close(p->fd_out);
+ if (verbose)
+ log_Printf(LogPHASE, "%s: Client connection dropped.\n", p->src.from);
+ } else
+ prompt_TtyOldMode(p);
+
+ log_UnRegisterPrompt(p);
+ free(p);
+ }
+}
+
+void
+prompt_Printf(struct prompt *p, const char *fmt,...)
+{
+ if (p && p->active) {
+ va_list ap;
+
+ va_start(ap, fmt);
+ prompt_vPrintf(p, fmt, ap);
+ va_end(ap);
+ }
+}
+
+void
+prompt_vPrintf(struct prompt *p, const char *fmt, va_list ap)
+{
+ if (p && p->active) {
+ char nfmt[LINE_LEN];
+ const char *pfmt;
+
+ if (p->TermMode) {
+ /* Stuff '\r' in front of '\n' 'cos we're in raw mode */
+ int len = strlen(fmt);
+
+ if (len && len < sizeof nfmt - 1 && fmt[len-1] == '\n' &&
+ (len == 1 || fmt[len-2] != '\r')) {
+ strcpy(nfmt, fmt);
+ strcpy(nfmt + len - 1, "\r\n");
+ pfmt = nfmt;
+ } else
+ pfmt = fmt;
+ } else
+ pfmt = fmt;
+ vfprintf(p->Term, pfmt, ap);
+ fflush(p->Term);
+ p->nonewline = 1;
+ }
+}
+
+void
+prompt_TtyInit(struct prompt *p)
+{
+ int stat, fd = p ? p->fd_in : STDIN_FILENO;
+ struct termios newtio;
+
+ stat = fcntl(fd, F_GETFL, 0);
+ if (stat > 0) {
+ stat |= O_NONBLOCK;
+ fcntl(fd, F_SETFL, stat);
+ }
+
+ if (p)
+ newtio = p->oldtio;
+ else
+ tcgetattr(fd, &newtio);
+
+ newtio.c_lflag &= ~(ECHO | ISIG | ICANON);
+ newtio.c_iflag = 0;
+ newtio.c_oflag &= ~OPOST;
+ if (!p)
+ newtio.c_cc[VINTR] = _POSIX_VDISABLE;
+ newtio.c_cc[VMIN] = 1;
+ newtio.c_cc[VTIME] = 0;
+ newtio.c_cflag |= CS8;
+ tcsetattr(fd, TCSANOW, &newtio);
+ if (p)
+ p->comtio = newtio;
+}
+
+/*
+ * Set tty into command mode. We allow canonical input and echo processing.
+ */
+void
+prompt_TtyCommandMode(struct prompt *p)
+{
+ struct termios newtio;
+ int stat;
+
+ tcgetattr(p->fd_in, &newtio);
+ newtio.c_lflag |= (ECHO | ISIG | ICANON);
+ newtio.c_iflag = p->oldtio.c_iflag;
+ newtio.c_oflag |= OPOST;
+ tcsetattr(p->fd_in, TCSADRAIN, &newtio);
+
+ stat = fcntl(p->fd_in, F_GETFL, 0);
+ if (stat > 0) {
+ stat |= O_NONBLOCK;
+ fcntl(p->fd_in, F_SETFL, stat);
+ }
+
+ p->TermMode = NULL;
+}
+
+/*
+ * Set tty into terminal mode which is used while we invoke term command.
+ */
+void
+prompt_TtyTermMode(struct prompt *p, struct datalink *dl)
+{
+ int stat;
+
+ if (p->Term == stdout)
+ tcsetattr(p->fd_in, TCSADRAIN, &p->comtio);
+
+ stat = fcntl(p->fd_in, F_GETFL, 0);
+ if (stat > 0) {
+ stat &= ~O_NONBLOCK;
+ fcntl(p->fd_in, F_SETFL, stat);
+ }
+ p->TermMode = dl;
+}
+
+void
+prompt_TtyOldMode(struct prompt *p)
+{
+ int stat;
+
+ stat = fcntl(p->fd_in, F_GETFL, 0);
+ if (stat > 0) {
+ stat &= ~O_NONBLOCK;
+ fcntl(p->fd_in, F_SETFL, stat);
+ }
+
+ if (p->Term == stdout)
+ tcsetattr(p->fd_in, TCSADRAIN, &p->oldtio);
+}
+
+pid_t
+prompt_pgrp(struct prompt *p)
+{
+ return tcgetpgrp(p->fd_in);
+}
+
+int
+PasswdCommand(struct cmdargs const *arg)
+{
+ const char *pass;
+
+ if (!arg->prompt) {
+ log_Printf(LogWARN, "passwd: Cannot specify without a prompt\n");
+ return 0;
+ }
+
+ if (arg->prompt->owner == NULL) {
+ log_Printf(LogWARN, "passwd: Not required\n");
+ return 0;
+ }
+
+ if (arg->argc == arg->argn)
+ pass = "";
+ else if (arg->argc > arg->argn+1)
+ return -1;
+ else
+ pass = arg->argv[arg->argn];
+
+ if (!strcmp(arg->prompt->owner->cfg.passwd, pass))
+ arg->prompt->auth = LOCAL_AUTH;
+ else
+ arg->prompt->auth = LOCAL_NO_AUTH;
+
+ return 0;
+}
+
+static struct pppTimer bgtimer;
+
+static void
+prompt_TimedContinue(void *v)
+{
+ prompt_Continue((struct prompt *)v);
+}
+
+void
+prompt_Continue(struct prompt *p)
+{
+ timer_Stop(&bgtimer);
+ if (getpgrp() == prompt_pgrp(p)) {
+ prompt_TtyCommandMode(p);
+ p->nonewline = 1;
+ prompt_Required(p);
+ log_ActivatePrompt(p);
+ } else if (!p->owner) {
+ bgtimer.func = prompt_TimedContinue;
+ bgtimer.name = "prompt bg";
+ bgtimer.load = SECTICKS;
+ bgtimer.arg = p;
+ timer_Start(&bgtimer);
+ }
+}
+
+void
+prompt_Suspend(struct prompt *p)
+{
+ if (getpgrp() == prompt_pgrp(p)) {
+ prompt_TtyOldMode(p);
+ log_DeactivatePrompt(p);
+ }
+}
diff --git a/usr.sbin/ppp/prompt.h b/usr.sbin/ppp/prompt.h
new file mode 100644
index 0000000..6569cd9
--- /dev/null
+++ b/usr.sbin/ppp/prompt.h
@@ -0,0 +1,96 @@
+/*-
+ * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define LOCAL_AUTH 0x01
+#define LOCAL_NO_AUTH 0x02
+#define LOCAL_DENY 0x03
+#define LOCAL_CX 0x04 /* OR'd value - require a context */
+#define LOCAL_CX_OPT 0x08 /* OR'd value - optional context */
+
+struct server;
+struct datalink;
+struct bundle;
+struct cmdargs;
+
+struct prompt {
+ struct fdescriptor desc;
+ int fd_in, fd_out;
+ struct datalink *TermMode; /* The modem we're talking directly to */
+ FILE *Term; /* sits on top of fd_out */
+ u_char auth; /* Local Authorized status */
+ struct server *owner; /* who created me */
+ struct bundle *bundle; /* who I'm controlling */
+ unsigned nonewline : 1; /* need a newline before our prompt ? */
+ unsigned needprompt : 1; /* Show a prompt at the next UpdateSet() */
+ unsigned active : 1; /* Is the prompt active (^Z) */
+ unsigned readtilde : 1; /* We've read a ``~'' from fd_in */
+
+ struct {
+ const char *type; /* Type of connection */
+ char from[40]; /* Source of connection */
+ } src;
+
+ struct prompt *next; /* Maintained in log.c */
+ u_long logmask; /* Maintained in log.c */
+
+ struct termios oldtio; /* Original tty mode */
+ struct termios comtio; /* Command level tty mode */
+};
+
+#define descriptor2prompt(d) \
+ ((d)->type == PROMPT_DESCRIPTOR ? (struct prompt *)(d) : NULL)
+
+#define PROMPT_STD (-1)
+extern struct prompt *prompt_Create(struct server *, struct bundle *, int);
+extern void prompt_Destroy(struct prompt *, int);
+extern void prompt_Required(struct prompt *);
+#ifdef __GNUC__
+extern void prompt_Printf(struct prompt *, const char *, ...)
+ __attribute__ ((format (printf, 2, 3)));
+#else
+extern void prompt_Printf(struct prompt *, const char *, ...);
+#endif
+#ifdef __GNUC__
+extern void prompt_vPrintf(struct prompt *, const char *, _BSD_VA_LIST_)
+ __attribute__ ((format (printf, 2, 0)));
+#else
+extern void prompt_vPrintf(struct prompt *, const char *, _BSD_VA_LIST_);
+#endif
+#define PROMPT_DONT_WANT_INT 1
+#define PROMPT_WANT_INT 0
+extern void prompt_TtyInit(struct prompt *);
+extern void prompt_TtyCommandMode(struct prompt *);
+extern void prompt_TtyTermMode(struct prompt *, struct datalink *);
+extern void prompt_TtyOldMode(struct prompt *);
+extern pid_t prompt_pgrp(struct prompt *);
+extern int PasswdCommand(struct cmdargs const *);
+extern void prompt_Suspend(struct prompt *);
+extern void prompt_Continue(struct prompt *);
+#define prompt_IsTermMode(p, dl) ((p)->TermMode == (dl) ? 1 : 0)
+#define prompt_IsController(p) (!(p) || (p)->owner ? 0 : 1)
+#define prompt_Required(p) ((p)->needprompt = 1)
diff --git a/usr.sbin/ppp/proto.c b/usr.sbin/ppp/proto.c
new file mode 100644
index 0000000..e471998
--- /dev/null
+++ b/usr.sbin/ppp/proto.c
@@ -0,0 +1,115 @@
+/*-
+ * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <termios.h>
+
+#include "layer.h"
+#include "acf.h"
+#include "defs.h"
+#include "log.h"
+#include "timer.h"
+#include "fsm.h"
+#include "mbuf.h"
+#include "proto.h"
+#include "throughput.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+
+int
+proto_WrapperOctets(struct lcp *lcp, u_short proto)
+{
+ return (lcp->his_protocomp && !(proto & 0xff00)) ? 1 : 2;
+}
+
+struct mbuf *
+proto_Prepend(struct mbuf *bp, u_short proto, unsigned comp, int extra)
+{
+ u_char cp[2];
+
+ cp[0] = proto >> 8;
+ cp[1] = proto & 0xff;
+
+ if (comp && cp[0] == 0)
+ bp = m_prepend(bp, cp + 1, 1, extra);
+ else
+ bp = m_prepend(bp, cp, 2, extra);
+
+ return bp;
+}
+
+static struct mbuf *
+proto_LayerPush(struct bundle *b, struct link *l, struct mbuf *bp,
+ int pri, u_short *proto)
+{
+ log_Printf(LogDEBUG, "proto_LayerPush: Using 0x%04x\n", *proto);
+ bp = proto_Prepend(bp, *proto, l->lcp.his_protocomp,
+ acf_WrapperOctets(&l->lcp, *proto));
+ m_settype(bp, MB_PROTOOUT);
+ link_ProtocolRecord(l, *proto, PROTO_OUT);
+
+ return bp;
+}
+
+static struct mbuf *
+proto_LayerPull(struct bundle *b, struct link *l, struct mbuf *bp,
+ u_short *proto)
+{
+ u_char cp[2];
+ size_t got;
+
+ if ((got = mbuf_View(bp, cp, 2)) == 0) {
+ m_freem(bp);
+ return NULL;
+ }
+
+ *proto = cp[0];
+ if (!(*proto & 1)) {
+ if (got == 1) {
+ m_freem(bp);
+ return NULL;
+ }
+ bp = mbuf_Read(bp, cp, 2);
+ *proto = (*proto << 8) | cp[1];
+ } else
+ bp = mbuf_Read(bp, cp, 1);
+
+ log_Printf(LogDEBUG, "proto_LayerPull: unknown -> 0x%04x\n", *proto);
+ m_settype(bp, MB_PROTOIN);
+ link_ProtocolRecord(l, *proto, PROTO_IN);
+
+ return bp;
+}
+
+struct layer protolayer =
+ { LAYER_PROTO, "proto", proto_LayerPush, proto_LayerPull };
diff --git a/usr.sbin/ppp/proto.h b/usr.sbin/ppp/proto.h
new file mode 100644
index 0000000..8c93327
--- /dev/null
+++ b/usr.sbin/ppp/proto.h
@@ -0,0 +1,64 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Definition of protocol numbers
+ */
+#define PROTO_IP 0x0021 /* IP */
+#define PROTO_VJUNCOMP 0x002f /* VJ Uncompressed */
+#define PROTO_VJCOMP 0x002d /* VJ Compressed */
+#define PROTO_MP 0x003d /* Multilink fragment */
+#ifndef NOINET6
+#define PROTO_IPV6 0x0057 /* IPv6 */
+#endif
+#define PROTO_ICOMPD 0x00fb /* Individual link compressed */
+#define PROTO_COMPD 0x00fd /* Compressed datagram */
+
+#define PROTO_COMPRESSIBLE(p) (((p) & 0xff81) == 0x01)
+
+#define PROTO_IPCP 0x8021
+#ifndef NOINET6
+#define PROTO_IPV6CP 0x8057
+#endif
+#define PROTO_ICCP 0x80fb
+#define PROTO_CCP 0x80fd
+
+#define PROTO_LCP 0xc021
+#define PROTO_PAP 0xc023
+#define PROTO_CBCP 0xc029
+#define PROTO_LQR 0xc025
+#define PROTO_CHAP 0xc223
+
+struct lcp;
+
+extern int proto_WrapperOctets(struct lcp *, u_short);
+struct mbuf *proto_Prepend(struct mbuf *, u_short, unsigned, int);
+
+extern struct layer protolayer;
diff --git a/usr.sbin/ppp/radius.c b/usr.sbin/ppp/radius.c
new file mode 100644
index 0000000..f9118df
--- /dev/null
+++ b/usr.sbin/ppp/radius.c
@@ -0,0 +1,1056 @@
+/*
+ * Copyright 1999 Internet Business Solutions Ltd., Switzerland
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ */
+
+#include <sys/param.h>
+
+#include <sys/socket.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+#include <net/route.h>
+
+#ifdef LOCALRAD
+#include "radlib.h"
+#include "radlib_vs.h"
+#else
+#include <radlib.h>
+#include <radlib_vs.h>
+#endif
+
+#include <errno.h>
+#ifndef NODES
+#include <md5.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <termios.h>
+#include <unistd.h>
+#include <netdb.h>
+
+#include "layer.h"
+#include "defs.h"
+#include "log.h"
+#include "descriptor.h"
+#include "prompt.h"
+#include "timer.h"
+#include "fsm.h"
+#include "iplist.h"
+#include "slcompress.h"
+#include "throughput.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "mbuf.h"
+#include "ncpaddr.h"
+#include "ip.h"
+#include "ipcp.h"
+#include "ipv6cp.h"
+#include "route.h"
+#include "command.h"
+#include "filter.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "mp.h"
+#include "radius.h"
+#include "auth.h"
+#include "async.h"
+#include "physical.h"
+#include "chat.h"
+#include "cbcp.h"
+#include "chap.h"
+#include "datalink.h"
+#include "ncp.h"
+#include "bundle.h"
+#include "proto.h"
+
+#ifndef NODES
+struct mschap_response {
+ u_char ident;
+ u_char flags;
+ u_char lm_response[24];
+ u_char nt_response[24];
+};
+
+struct mschap2_response {
+ u_char ident;
+ u_char flags;
+ u_char pchallenge[16];
+ u_char reserved[8];
+ u_char response[24];
+};
+
+#define AUTH_LEN 16
+#define SALT_LEN 2
+#endif
+
+static const char *
+radius_policyname(int policy)
+{
+ switch(policy) {
+ case MPPE_POLICY_ALLOWED:
+ return "Allowed";
+ case MPPE_POLICY_REQUIRED:
+ return "Required";
+ }
+ return NumStr(policy, NULL, 0);
+}
+
+static const char *
+radius_typesname(int types)
+{
+ switch(types) {
+ case MPPE_TYPE_40BIT:
+ return "40 bit";
+ case MPPE_TYPE_128BIT:
+ return "128 bit";
+ case MPPE_TYPE_40BIT|MPPE_TYPE_128BIT:
+ return "40 or 128 bit";
+ }
+ return NumStr(types, NULL, 0);
+}
+
+#ifndef NODES
+static void
+demangle(struct radius *r, const void *mangled, size_t mlen,
+ char **buf, size_t *len)
+{
+ char R[AUTH_LEN]; /* variable names as per rfc2548 */
+ const char *S;
+ u_char b[16];
+ const u_char *A, *C;
+ MD5_CTX Context;
+ int Slen, i, Clen, Ppos;
+ u_char *P;
+
+ if (mlen % 16 != SALT_LEN) {
+ log_Printf(LogWARN, "Cannot interpret mangled data of length %ld\n",
+ (u_long)mlen);
+ *buf = NULL;
+ *len = 0;
+ return;
+ }
+
+ /* We need the RADIUS Request-Authenticator */
+ if (rad_request_authenticator(r->cx.rad, R, sizeof R) != AUTH_LEN) {
+ log_Printf(LogWARN, "Cannot obtain the RADIUS request authenticator\n");
+ *buf = NULL;
+ *len = 0;
+ return;
+ }
+
+ A = (const u_char *)mangled; /* Salt comes first */
+ C = (const u_char *)mangled + SALT_LEN; /* Then the ciphertext */
+ Clen = mlen - SALT_LEN;
+ S = rad_server_secret(r->cx.rad); /* We need the RADIUS secret */
+ Slen = strlen(S);
+ P = alloca(Clen); /* We derive our plaintext */
+
+ MD5Init(&Context);
+ MD5Update(&Context, S, Slen);
+ MD5Update(&Context, R, AUTH_LEN);
+ MD5Update(&Context, A, SALT_LEN);
+ MD5Final(b, &Context);
+ Ppos = 0;
+
+ while (Clen) {
+ Clen -= 16;
+
+ for (i = 0; i < 16; i++)
+ P[Ppos++] = C[i] ^ b[i];
+
+ if (Clen) {
+ MD5Init(&Context);
+ MD5Update(&Context, S, Slen);
+ MD5Update(&Context, C, 16);
+ MD5Final(b, &Context);
+ }
+
+ C += 16;
+ }
+
+ /*
+ * The resulting plain text consists of a one-byte length, the text and
+ * maybe some padding.
+ */
+ *len = *P;
+ if (*len > mlen - 1) {
+ log_Printf(LogWARN, "Mangled data seems to be garbage\n");
+ *buf = NULL;
+ *len = 0;
+ return;
+ }
+
+ *buf = malloc(*len);
+ memcpy(*buf, P + 1, *len);
+}
+#endif
+
+/*
+ * rad_continue_send_request() has given us `got' (non-zero). Deal with it.
+ */
+static void
+radius_Process(struct radius *r, int got)
+{
+ char *argv[MAXARGS], *nuke;
+ struct bundle *bundle;
+ int argc, addrs, res, width;
+ size_t len;
+ struct ncprange dest;
+ struct ncpaddr gw;
+ const void *data;
+ const char *stype;
+ u_int32_t ipaddr, vendor;
+ struct in_addr ip;
+
+ r->cx.fd = -1; /* Stop select()ing */
+ stype = r->cx.auth ? "auth" : "acct";
+
+ switch (got) {
+ case RAD_ACCESS_ACCEPT:
+ log_Printf(LogPHASE, "Radius(%s): ACCEPT received\n", stype);
+ if (!r->cx.auth) {
+ rad_close(r->cx.rad);
+ return;
+ }
+ break;
+
+ case RAD_ACCESS_REJECT:
+ log_Printf(LogPHASE, "Radius(%s): REJECT received\n", stype);
+ if (!r->cx.auth) {
+ rad_close(r->cx.rad);
+ return;
+ }
+ break;
+
+ case RAD_ACCESS_CHALLENGE:
+ /* we can't deal with this (for now) ! */
+ log_Printf(LogPHASE, "Radius: CHALLENGE received (can't handle yet)\n");
+ if (r->cx.auth)
+ auth_Failure(r->cx.auth);
+ rad_close(r->cx.rad);
+ return;
+
+ case RAD_ACCOUNTING_RESPONSE:
+ log_Printf(LogPHASE, "Radius(%s): Accounting response received\n", stype);
+ if (r->cx.auth)
+ auth_Failure(r->cx.auth); /* unexpected !!! */
+
+ /* No further processing for accounting requests, please */
+ rad_close(r->cx.rad);
+ return;
+
+ case -1:
+ log_Printf(LogPHASE, "radius(%s): %s\n", stype, rad_strerror(r->cx.rad));
+ if (r->cx.auth)
+ auth_Failure(r->cx.auth);
+ rad_close(r->cx.rad);
+ return;
+
+ default:
+ log_Printf(LogERROR, "rad_send_request(%s): Failed %d: %s\n", stype,
+ got, rad_strerror(r->cx.rad));
+ if (r->cx.auth)
+ auth_Failure(r->cx.auth);
+ rad_close(r->cx.rad);
+ return;
+ }
+
+ /* Let's see what we've got in our reply */
+ r->ip.s_addr = r->mask.s_addr = INADDR_NONE;
+ r->mtu = 0;
+ r->vj = 0;
+ while ((res = rad_get_attr(r->cx.rad, &data, &len)) > 0) {
+ switch (res) {
+ case RAD_FRAMED_IP_ADDRESS:
+ r->ip = rad_cvt_addr(data);
+ log_Printf(LogPHASE, " IP %s\n", inet_ntoa(r->ip));
+ break;
+
+ case RAD_FILTER_ID:
+ free(r->filterid);
+ if ((r->filterid = rad_cvt_string(data, len)) == NULL) {
+ log_Printf(LogERROR, "rad_cvt_string: %s\n", rad_strerror(r->cx.rad));
+ auth_Failure(r->cx.auth);
+ rad_close(r->cx.rad);
+ return;
+ }
+ log_Printf(LogPHASE, " Filter \"%s\"\n", r->filterid);
+ break;
+
+ case RAD_SESSION_TIMEOUT:
+ r->sessiontime = rad_cvt_int(data);
+ log_Printf(LogPHASE, " Session-Timeout %lu\n", r->sessiontime);
+ break;
+
+ case RAD_FRAMED_IP_NETMASK:
+ r->mask = rad_cvt_addr(data);
+ log_Printf(LogPHASE, " Netmask %s\n", inet_ntoa(r->mask));
+ break;
+
+ case RAD_FRAMED_MTU:
+ r->mtu = rad_cvt_int(data);
+ log_Printf(LogPHASE, " MTU %lu\n", r->mtu);
+ break;
+
+ case RAD_FRAMED_ROUTING:
+ /* Disabled for now - should we automatically set up some filters ? */
+ /* rad_cvt_int(data); */
+ /* bit 1 = Send routing packets */
+ /* bit 2 = Receive routing packets */
+ break;
+
+ case RAD_FRAMED_COMPRESSION:
+ r->vj = rad_cvt_int(data) == 1 ? 1 : 0;
+ log_Printf(LogPHASE, " VJ %sabled\n", r->vj ? "en" : "dis");
+ break;
+
+ case RAD_FRAMED_ROUTE:
+ /*
+ * We expect a string of the format ``dest[/bits] gw [metrics]''
+ * Any specified metrics are ignored. MYADDR and HISADDR are
+ * understood for ``dest'' and ``gw'' and ``0.0.0.0'' is the same
+ * as ``HISADDR''.
+ */
+
+ if ((nuke = rad_cvt_string(data, len)) == NULL) {
+ log_Printf(LogERROR, "rad_cvt_string: %s\n", rad_strerror(r->cx.rad));
+ auth_Failure(r->cx.auth);
+ rad_close(r->cx.rad);
+ return;
+ }
+
+ log_Printf(LogPHASE, " Route: %s\n", nuke);
+ bundle = r->cx.auth->physical->dl->bundle;
+ ip.s_addr = INADDR_ANY;
+ ncprange_setip4host(&dest, ip);
+ argc = command_Interpret(nuke, strlen(nuke), argv);
+ if (argc < 0)
+ log_Printf(LogWARN, "radius: %s: Syntax error\n",
+ argc == 1 ? argv[0] : "\"\"");
+ else if (argc < 2)
+ log_Printf(LogWARN, "radius: %s: Invalid route\n",
+ argc == 1 ? argv[0] : "\"\"");
+ else if ((strcasecmp(argv[0], "default") != 0 &&
+ !ncprange_aton(&dest, &bundle->ncp, argv[0])) ||
+ !ncpaddr_aton(&gw, &bundle->ncp, argv[1]))
+ log_Printf(LogWARN, "radius: %s %s: Invalid route\n",
+ argv[0], argv[1]);
+ else {
+ ncprange_getwidth(&dest, &width);
+ if (width == 32 && strchr(argv[0], '/') == NULL) {
+ /* No mask specified - use the natural mask */
+ ncprange_getip4addr(&dest, &ip);
+ ncprange_setip4mask(&dest, addr2mask(ip));
+ }
+ addrs = 0;
+
+ if (!strncasecmp(argv[0], "HISADDR", 7))
+ addrs = ROUTE_DSTHISADDR;
+ else if (!strncasecmp(argv[0], "MYADDR", 6))
+ addrs = ROUTE_DSTMYADDR;
+
+ if (ncpaddr_getip4addr(&gw, &ipaddr) && ipaddr == INADDR_ANY) {
+ addrs |= ROUTE_GWHISADDR;
+ ncpaddr_setip4(&gw, bundle->ncp.ipcp.peer_ip);
+ } else if (strcasecmp(argv[1], "HISADDR") == 0)
+ addrs |= ROUTE_GWHISADDR;
+
+ route_Add(&r->routes, addrs, &dest, &gw);
+ }
+ free(nuke);
+ break;
+
+ case RAD_REPLY_MESSAGE:
+ free(r->repstr);
+ if ((r->repstr = rad_cvt_string(data, len)) == NULL) {
+ log_Printf(LogERROR, "rad_cvt_string: %s\n", rad_strerror(r->cx.rad));
+ auth_Failure(r->cx.auth);
+ rad_close(r->cx.rad);
+ return;
+ }
+ log_Printf(LogPHASE, " Reply-Message \"%s\"\n", r->repstr);
+ break;
+
+ case RAD_VENDOR_SPECIFIC:
+ if ((res = rad_get_vendor_attr(&vendor, &data, &len)) <= 0) {
+ log_Printf(LogERROR, "rad_get_vendor_attr: %s (failing!)\n",
+ rad_strerror(r->cx.rad));
+ auth_Failure(r->cx.auth);
+ rad_close(r->cx.rad);
+ return;
+ }
+
+ switch (vendor) {
+ case RAD_VENDOR_MICROSOFT:
+ switch (res) {
+#ifndef NODES
+ case RAD_MICROSOFT_MS_CHAP_ERROR:
+ free(r->errstr);
+ if (len == 0)
+ r->errstr = NULL;
+ else {
+ if (len < 3 || ((const char *)data)[1] != '=') {
+ /*
+ * Only point at the String field if we don't think the
+ * peer has misformatted the response.
+ */
+ ((const char *)data)++;
+ len--;
+ } else
+ log_Printf(LogWARN, "Warning: The MS-CHAP-Error "
+ "attribute is mis-formatted. Compensating\n");
+ if ((r->errstr = rad_cvt_string((const char *)data,
+ len)) == NULL) {
+ log_Printf(LogERROR, "rad_cvt_string: %s\n",
+ rad_strerror(r->cx.rad));
+ auth_Failure(r->cx.auth);
+ rad_close(r->cx.rad);
+ return;
+ }
+ log_Printf(LogPHASE, " MS-CHAP-Error \"%s\"\n", r->errstr);
+ }
+ break;
+
+ case RAD_MICROSOFT_MS_CHAP2_SUCCESS:
+ free(r->msrepstr);
+ if (len == 0)
+ r->msrepstr = NULL;
+ else {
+ if (len < 3 || ((const char *)data)[1] != '=') {
+ /*
+ * Only point at the String field if we don't think the
+ * peer has misformatted the response.
+ */
+ ((const char *)data)++;
+ len--;
+ } else
+ log_Printf(LogWARN, "Warning: The MS-CHAP2-Success "
+ "attribute is mis-formatted. Compensating\n");
+ if ((r->msrepstr = rad_cvt_string((const char *)data,
+ len)) == NULL) {
+ log_Printf(LogERROR, "rad_cvt_string: %s\n",
+ rad_strerror(r->cx.rad));
+ auth_Failure(r->cx.auth);
+ rad_close(r->cx.rad);
+ return;
+ }
+ log_Printf(LogPHASE, " MS-CHAP2-Success \"%s\"\n",
+ r->msrepstr);
+ }
+ break;
+
+ case RAD_MICROSOFT_MS_MPPE_ENCRYPTION_POLICY:
+ r->mppe.policy = rad_cvt_int(data);
+ log_Printf(LogPHASE, " MS-MPPE-Encryption-Policy %s\n",
+ radius_policyname(r->mppe.policy));
+ break;
+
+ case RAD_MICROSOFT_MS_MPPE_ENCRYPTION_TYPES:
+ r->mppe.types = rad_cvt_int(data);
+ log_Printf(LogPHASE, " MS-MPPE-Encryption-Types %s\n",
+ radius_typesname(r->mppe.types));
+ break;
+
+ case RAD_MICROSOFT_MS_MPPE_RECV_KEY:
+ free(r->mppe.recvkey);
+ demangle(r, data, len, &r->mppe.recvkey, &r->mppe.recvkeylen);
+ log_Printf(LogPHASE, " MS-MPPE-Recv-Key ********\n");
+ break;
+
+ case RAD_MICROSOFT_MS_MPPE_SEND_KEY:
+ demangle(r, data, len, &r->mppe.sendkey, &r->mppe.sendkeylen);
+ log_Printf(LogPHASE, " MS-MPPE-Send-Key ********\n");
+ break;
+#endif
+
+ default:
+ log_Printf(LogDEBUG, "Dropping MICROSOFT vendor specific "
+ "RADIUS attribute %d\n", res);
+ break;
+ }
+ break;
+
+ default:
+ log_Printf(LogDEBUG, "Dropping vendor %lu RADIUS attribute %d\n",
+ (unsigned long)vendor, res);
+ break;
+ }
+ break;
+
+ default:
+ log_Printf(LogDEBUG, "Dropping RADIUS attribute %d\n", res);
+ break;
+ }
+ }
+
+ if (res == -1) {
+ log_Printf(LogERROR, "rad_get_attr: %s (failing!)\n",
+ rad_strerror(r->cx.rad));
+ auth_Failure(r->cx.auth);
+ } else if (got == RAD_ACCESS_REJECT)
+ auth_Failure(r->cx.auth);
+ else {
+ r->valid = 1;
+ auth_Success(r->cx.auth);
+ }
+ rad_close(r->cx.rad);
+}
+
+/*
+ * We've either timed out or select()ed on the read descriptor
+ */
+static void
+radius_Continue(struct radius *r, int sel)
+{
+ struct timeval tv;
+ int got;
+
+ timer_Stop(&r->cx.timer);
+ if ((got = rad_continue_send_request(r->cx.rad, sel, &r->cx.fd, &tv)) == 0) {
+ log_Printf(LogPHASE, "Radius: Request re-sent\n");
+ r->cx.timer.load = tv.tv_usec / TICKUNIT + tv.tv_sec * SECTICKS;
+ timer_Start(&r->cx.timer);
+ return;
+ }
+
+ radius_Process(r, got);
+}
+
+/*
+ * Time to call rad_continue_send_request() - timed out.
+ */
+static void
+radius_Timeout(void *v)
+{
+ radius_Continue((struct radius *)v, 0);
+}
+
+/*
+ * Time to call rad_continue_send_request() - something to read.
+ */
+static void
+radius_Read(struct fdescriptor *d, struct bundle *bundle, const fd_set *fdset)
+{
+ radius_Continue(descriptor2radius(d), 1);
+}
+
+/*
+ * Behave as a struct fdescriptor (descriptor.h)
+ */
+static int
+radius_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n)
+{
+ struct radius *rad = descriptor2radius(d);
+
+ if (r && rad->cx.fd != -1) {
+ FD_SET(rad->cx.fd, r);
+ if (*n < rad->cx.fd + 1)
+ *n = rad->cx.fd + 1;
+ log_Printf(LogTIMER, "Radius: fdset(r) %d\n", rad->cx.fd);
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Behave as a struct fdescriptor (descriptor.h)
+ */
+static int
+radius_IsSet(struct fdescriptor *d, const fd_set *fdset)
+{
+ struct radius *r = descriptor2radius(d);
+
+ return r && r->cx.fd != -1 && FD_ISSET(r->cx.fd, fdset);
+}
+
+/*
+ * Behave as a struct fdescriptor (descriptor.h)
+ */
+static int
+radius_Write(struct fdescriptor *d, struct bundle *bundle, const fd_set *fdset)
+{
+ /* We never want to write here ! */
+ log_Printf(LogALERT, "radius_Write: Internal error: Bad call !\n");
+ return 0;
+}
+
+/*
+ * Initialise ourselves
+ */
+void
+radius_Init(struct radius *r)
+{
+ r->desc.type = RADIUS_DESCRIPTOR;
+ r->desc.UpdateSet = radius_UpdateSet;
+ r->desc.IsSet = radius_IsSet;
+ r->desc.Read = radius_Read;
+ r->desc.Write = radius_Write;
+ r->cx.fd = -1;
+ r->cx.rad = NULL;
+ memset(&r->cx.timer, '\0', sizeof r->cx.timer);
+ r->cx.auth = NULL;
+ r->valid = 0;
+ r->vj = 0;
+ r->ip.s_addr = INADDR_ANY;
+ r->mask.s_addr = INADDR_NONE;
+ r->routes = NULL;
+ r->mtu = DEF_MTU;
+ r->msrepstr = NULL;
+ r->repstr = NULL;
+ r->errstr = NULL;
+ r->mppe.policy = 0;
+ r->mppe.types = 0;
+ r->mppe.recvkey = NULL;
+ r->mppe.recvkeylen = 0;
+ r->mppe.sendkey = NULL;
+ r->mppe.sendkeylen = 0;
+ *r->cfg.file = '\0';;
+ log_Printf(LogDEBUG, "Radius: radius_Init\n");
+}
+
+/*
+ * Forget everything and go back to initialised state.
+ */
+void
+radius_Destroy(struct radius *r)
+{
+ r->valid = 0;
+ log_Printf(LogDEBUG, "Radius: radius_Destroy\n");
+ timer_Stop(&r->cx.timer);
+ route_DeleteAll(&r->routes);
+ free(r->filterid);
+ r->filterid = NULL;
+ free(r->msrepstr);
+ r->msrepstr = NULL;
+ free(r->repstr);
+ r->repstr = NULL;
+ free(r->errstr);
+ r->errstr = NULL;
+ free(r->mppe.recvkey);
+ r->mppe.recvkey = NULL;
+ r->mppe.recvkeylen = 0;
+ free(r->mppe.sendkey);
+ r->mppe.sendkey = NULL;
+ r->mppe.sendkeylen = 0;
+ if (r->cx.fd != -1) {
+ r->cx.fd = -1;
+ rad_close(r->cx.rad);
+ }
+}
+
+static int
+radius_put_physical_details(struct rad_handle *rad, struct physical *p)
+{
+ int slot, type;
+
+ type = RAD_VIRTUAL;
+ if (p->handler)
+ switch (p->handler->type) {
+ case I4B_DEVICE:
+ type = RAD_ISDN_SYNC;
+ break;
+
+ case TTY_DEVICE:
+ type = RAD_ASYNC;
+ break;
+
+ case ETHER_DEVICE:
+ type = RAD_ETHERNET;
+ break;
+
+ case TCP_DEVICE:
+ case UDP_DEVICE:
+ case EXEC_DEVICE:
+ case ATM_DEVICE:
+ case NG_DEVICE:
+ type = RAD_VIRTUAL;
+ break;
+ }
+
+ if (rad_put_int(rad, RAD_NAS_PORT_TYPE, type) != 0) {
+ log_Printf(LogERROR, "rad_put: rad_put_int: %s\n", rad_strerror(rad));
+ rad_close(rad);
+ return 0;
+ }
+
+ if ((slot = physical_Slot(p)) >= 0)
+ if (rad_put_int(rad, RAD_NAS_PORT, slot) != 0) {
+ log_Printf(LogERROR, "rad_put: rad_put_int: %s\n", rad_strerror(rad));
+ rad_close(rad);
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * Start an authentication request to the RADIUS server.
+ */
+int
+radius_Authenticate(struct radius *r, struct authinfo *authp, const char *name,
+ const char *key, int klen, const char *nchallenge,
+ int nclen)
+{
+ struct timeval tv;
+ int got;
+ char hostname[MAXHOSTNAMELEN];
+#if 0
+ struct hostent *hp;
+ struct in_addr hostaddr;
+#endif
+#ifndef NODES
+ struct mschap_response msresp;
+ struct mschap2_response msresp2;
+ const struct MSCHAPv2_resp *keyv2;
+#endif
+
+ if (!*r->cfg.file)
+ return 0;
+
+ if (r->cx.fd != -1)
+ /*
+ * We assume that our name/key/challenge is the same as last time,
+ * and just continue to wait for the RADIUS server(s).
+ */
+ return 1;
+
+ radius_Destroy(r);
+
+ if ((r->cx.rad = rad_auth_open()) == NULL) {
+ log_Printf(LogERROR, "rad_auth_open: %s\n", strerror(errno));
+ return 0;
+ }
+
+ if (rad_config(r->cx.rad, r->cfg.file) != 0) {
+ log_Printf(LogERROR, "rad_config: %s\n", rad_strerror(r->cx.rad));
+ rad_close(r->cx.rad);
+ return 0;
+ }
+
+ if (rad_create_request(r->cx.rad, RAD_ACCESS_REQUEST) != 0) {
+ log_Printf(LogERROR, "rad_create_request: %s\n", rad_strerror(r->cx.rad));
+ rad_close(r->cx.rad);
+ return 0;
+ }
+
+ if (rad_put_string(r->cx.rad, RAD_USER_NAME, name) != 0 ||
+ rad_put_int(r->cx.rad, RAD_SERVICE_TYPE, RAD_FRAMED) != 0 ||
+ rad_put_int(r->cx.rad, RAD_FRAMED_PROTOCOL, RAD_PPP) != 0) {
+ log_Printf(LogERROR, "rad_put: %s\n", rad_strerror(r->cx.rad));
+ rad_close(r->cx.rad);
+ return 0;
+ }
+
+ switch (authp->physical->link.lcp.want_auth) {
+ case PROTO_PAP:
+ /* We're talking PAP */
+ if (rad_put_attr(r->cx.rad, RAD_USER_PASSWORD, key, klen) != 0) {
+ log_Printf(LogERROR, "PAP: rad_put_string: %s\n",
+ rad_strerror(r->cx.rad));
+ rad_close(r->cx.rad);
+ return 0;
+ }
+ break;
+
+ case PROTO_CHAP:
+ switch (authp->physical->link.lcp.want_authtype) {
+ case 0x5:
+ if (rad_put_attr(r->cx.rad, RAD_CHAP_PASSWORD, key, klen) != 0 ||
+ rad_put_attr(r->cx.rad, RAD_CHAP_CHALLENGE, nchallenge, nclen) != 0) {
+ log_Printf(LogERROR, "CHAP: rad_put_string: %s\n",
+ rad_strerror(r->cx.rad));
+ rad_close(r->cx.rad);
+ return 0;
+ }
+ break;
+
+#ifndef NODES
+ case 0x80:
+ if (klen != 50) {
+ log_Printf(LogERROR, "CHAP80: Unrecognised key length %d\n", klen);
+ rad_close(r->cx.rad);
+ return 0;
+ }
+
+ rad_put_vendor_attr(r->cx.rad, RAD_VENDOR_MICROSOFT,
+ RAD_MICROSOFT_MS_CHAP_CHALLENGE, nchallenge, nclen);
+ msresp.ident = *key;
+ msresp.flags = 0x01;
+ memcpy(msresp.lm_response, key + 1, 24);
+ memcpy(msresp.nt_response, key + 25, 24);
+ rad_put_vendor_attr(r->cx.rad, RAD_VENDOR_MICROSOFT,
+ RAD_MICROSOFT_MS_CHAP_RESPONSE, &msresp,
+ sizeof msresp);
+ break;
+
+ case 0x81:
+ if (klen != sizeof(*keyv2) + 1) {
+ log_Printf(LogERROR, "CHAP81: Unrecognised key length %d\n", klen);
+ rad_close(r->cx.rad);
+ return 0;
+ }
+
+ keyv2 = (const struct MSCHAPv2_resp *)(key + 1);
+ rad_put_vendor_attr(r->cx.rad, RAD_VENDOR_MICROSOFT,
+ RAD_MICROSOFT_MS_CHAP_CHALLENGE, nchallenge, nclen);
+ msresp2.ident = *key;
+ msresp2.flags = keyv2->Flags;
+ memcpy(msresp2.response, keyv2->NTResponse, sizeof msresp2.response);
+ memset(msresp2.reserved, '\0', sizeof msresp2.reserved);
+ memcpy(msresp2.pchallenge, keyv2->PeerChallenge,
+ sizeof msresp2.pchallenge);
+ rad_put_vendor_attr(r->cx.rad, RAD_VENDOR_MICROSOFT,
+ RAD_MICROSOFT_MS_CHAP2_RESPONSE, &msresp2,
+ sizeof msresp2);
+ break;
+#endif
+ default:
+ log_Printf(LogERROR, "CHAP: Unrecognised type 0x%02x\n",
+ authp->physical->link.lcp.want_authtype);
+ rad_close(r->cx.rad);
+ return 0;
+ }
+ }
+
+ if (gethostname(hostname, sizeof hostname) != 0)
+ log_Printf(LogERROR, "rad_put: gethostname(): %s\n", strerror(errno));
+ else {
+#if 0
+ if ((hp = gethostbyname(hostname)) != NULL) {
+ hostaddr.s_addr = *(u_long *)hp->h_addr;
+ if (rad_put_addr(r->cx.rad, RAD_NAS_IP_ADDRESS, hostaddr) != 0) {
+ log_Printf(LogERROR, "rad_put: rad_put_string: %s\n",
+ rad_strerror(r->cx.rad));
+ rad_close(r->cx.rad);
+ return 0;
+ }
+ }
+#endif
+ if (rad_put_string(r->cx.rad, RAD_NAS_IDENTIFIER, hostname) != 0) {
+ log_Printf(LogERROR, "rad_put: rad_put_string: %s\n",
+ rad_strerror(r->cx.rad));
+ rad_close(r->cx.rad);
+ return 0;
+ }
+ }
+
+ radius_put_physical_details(r->cx.rad, authp->physical);
+
+ r->cx.auth = authp;
+ if ((got = rad_init_send_request(r->cx.rad, &r->cx.fd, &tv)))
+ radius_Process(r, got);
+ else {
+ log_Printf(LogPHASE, "Radius: Request sent\n");
+ log_Printf(LogDEBUG, "Using radius_Timeout [%p]\n", radius_Timeout);
+ r->cx.timer.load = tv.tv_usec / TICKUNIT + tv.tv_sec * SECTICKS;
+ r->cx.timer.func = radius_Timeout;
+ r->cx.timer.name = "radius auth";
+ r->cx.timer.arg = r;
+ timer_Start(&r->cx.timer);
+ }
+
+ return 1;
+}
+
+/*
+ * Send an accounting request to the RADIUS server
+ */
+void
+radius_Account(struct radius *r, struct radacct *ac, struct datalink *dl,
+ int acct_type, struct in_addr *peer_ip, struct in_addr *netmask,
+ struct pppThroughput *stats)
+{
+ struct timeval tv;
+ int got;
+ char hostname[MAXHOSTNAMELEN];
+#if 0
+ struct hostent *hp;
+ struct in_addr hostaddr;
+#endif
+
+ if (!*r->cfg.file)
+ return;
+
+ if (r->cx.fd != -1)
+ /*
+ * We assume that our name/key/challenge is the same as last time,
+ * and just continue to wait for the RADIUS server(s).
+ */
+ return;
+
+ timer_Stop(&r->cx.timer);
+
+ if ((r->cx.rad = rad_acct_open()) == NULL) {
+ log_Printf(LogERROR, "rad_auth_open: %s\n", strerror(errno));
+ return;
+ }
+
+ if (rad_config(r->cx.rad, r->cfg.file) != 0) {
+ log_Printf(LogERROR, "rad_config: %s\n", rad_strerror(r->cx.rad));
+ rad_close(r->cx.rad);
+ return;
+ }
+
+ if (rad_create_request(r->cx.rad, RAD_ACCOUNTING_REQUEST) != 0) {
+ log_Printf(LogERROR, "rad_create_request: %s\n", rad_strerror(r->cx.rad));
+ rad_close(r->cx.rad);
+ return;
+ }
+
+ /* Grab some accounting data and initialize structure */
+ if (acct_type == RAD_START) {
+ ac->rad_parent = r;
+ /* Fetch username from datalink */
+ strncpy(ac->user_name, dl->peer.authname, sizeof ac->user_name);
+ ac->user_name[AUTHLEN-1] = '\0';
+
+ ac->authentic = 2; /* Assume RADIUS verified auth data */
+
+ /* Generate a session ID */
+ snprintf(ac->session_id, sizeof ac->session_id, "%s%ld-%s%lu",
+ dl->bundle->cfg.auth.name, (long)getpid(),
+ dl->peer.authname, (unsigned long)stats->uptime);
+
+ /* And grab our MP socket name */
+ snprintf(ac->multi_session_id, sizeof ac->multi_session_id, "%s",
+ dl->bundle->ncp.mp.active ?
+ dl->bundle->ncp.mp.server.socket.sun_path : "");
+
+ /* Fetch IP, netmask from IPCP */
+ memcpy(&ac->ip, peer_ip, sizeof(ac->ip));
+ memcpy(&ac->mask, netmask, sizeof(ac->mask));
+ };
+
+ if (rad_put_string(r->cx.rad, RAD_USER_NAME, ac->user_name) != 0 ||
+ rad_put_int(r->cx.rad, RAD_SERVICE_TYPE, RAD_FRAMED) != 0 ||
+ rad_put_int(r->cx.rad, RAD_FRAMED_PROTOCOL, RAD_PPP) != 0 ||
+ rad_put_addr(r->cx.rad, RAD_FRAMED_IP_ADDRESS, ac->ip) != 0 ||
+ rad_put_addr(r->cx.rad, RAD_FRAMED_IP_NETMASK, ac->mask) != 0) {
+ log_Printf(LogERROR, "rad_put: %s\n", rad_strerror(r->cx.rad));
+ rad_close(r->cx.rad);
+ return;
+ }
+
+ if (gethostname(hostname, sizeof hostname) != 0)
+ log_Printf(LogERROR, "rad_put: gethostname(): %s\n", strerror(errno));
+ else {
+#if 0
+ if ((hp = gethostbyname(hostname)) != NULL) {
+ hostaddr.s_addr = *(u_long *)hp->h_addr;
+ if (rad_put_addr(r->cx.rad, RAD_NAS_IP_ADDRESS, hostaddr) != 0) {
+ log_Printf(LogERROR, "rad_put: rad_put_string: %s\n",
+ rad_strerror(r->cx.rad));
+ rad_close(r->cx.rad);
+ return;
+ }
+ }
+#endif
+ if (rad_put_string(r->cx.rad, RAD_NAS_IDENTIFIER, hostname) != 0) {
+ log_Printf(LogERROR, "rad_put: rad_put_string: %s\n",
+ rad_strerror(r->cx.rad));
+ rad_close(r->cx.rad);
+ return;
+ }
+ }
+
+ radius_put_physical_details(r->cx.rad, dl->physical);
+
+ if (rad_put_int(r->cx.rad, RAD_ACCT_STATUS_TYPE, acct_type) != 0 ||
+ rad_put_string(r->cx.rad, RAD_ACCT_SESSION_ID, ac->session_id) != 0 ||
+ rad_put_string(r->cx.rad, RAD_ACCT_MULTI_SESSION_ID,
+ ac->multi_session_id) != 0 ||
+ rad_put_int(r->cx.rad, RAD_ACCT_DELAY_TIME, 0) != 0) {
+/* XXX ACCT_DELAY_TIME should be increased each time a packet is waiting */
+ log_Printf(LogERROR, "rad_put: %s\n", rad_strerror(r->cx.rad));
+ rad_close(r->cx.rad);
+ return;
+ }
+
+ if (acct_type == RAD_STOP)
+ /* Show some statistics */
+ if (rad_put_int(r->cx.rad, RAD_ACCT_INPUT_OCTETS, stats->OctetsIn) != 0 ||
+ rad_put_int(r->cx.rad, RAD_ACCT_INPUT_PACKETS, stats->PacketsIn) != 0 ||
+ rad_put_int(r->cx.rad, RAD_ACCT_OUTPUT_OCTETS, stats->OctetsOut) != 0 ||
+ rad_put_int(r->cx.rad, RAD_ACCT_OUTPUT_PACKETS, stats->PacketsOut)
+ != 0 ||
+ rad_put_int(r->cx.rad, RAD_ACCT_SESSION_TIME, throughput_uptime(stats))
+ != 0) {
+ log_Printf(LogERROR, "rad_put: %s\n", rad_strerror(r->cx.rad));
+ rad_close(r->cx.rad);
+ return;
+ }
+
+ r->cx.auth = NULL; /* Not valid for accounting requests */
+ if ((got = rad_init_send_request(r->cx.rad, &r->cx.fd, &tv)))
+ radius_Process(r, got);
+ else {
+ log_Printf(LogDEBUG, "Using radius_Timeout [%p]\n", radius_Timeout);
+ r->cx.timer.load = tv.tv_usec / TICKUNIT + tv.tv_sec * SECTICKS;
+ r->cx.timer.func = radius_Timeout;
+ r->cx.timer.name = "radius acct";
+ r->cx.timer.arg = r;
+ timer_Start(&r->cx.timer);
+ }
+}
+
+/*
+ * How do things look at the moment ?
+ */
+void
+radius_Show(struct radius *r, struct prompt *p)
+{
+ prompt_Printf(p, " Radius config: %s",
+ *r->cfg.file ? r->cfg.file : "none");
+ if (r->valid) {
+ prompt_Printf(p, "\n IP: %s\n", inet_ntoa(r->ip));
+ prompt_Printf(p, " Netmask: %s\n", inet_ntoa(r->mask));
+ prompt_Printf(p, " MTU: %lu\n", r->mtu);
+ prompt_Printf(p, " VJ: %sabled\n", r->vj ? "en" : "dis");
+ prompt_Printf(p, " Message: %s\n", r->repstr ? r->repstr : "");
+ prompt_Printf(p, " MPPE Enc Policy: %s\n",
+ radius_policyname(r->mppe.policy));
+ prompt_Printf(p, " MPPE Enc Types: %s\n",
+ radius_typesname(r->mppe.types));
+ prompt_Printf(p, " MPPE Recv Key: %seceived\n",
+ r->mppe.recvkey ? "R" : "Not r");
+ prompt_Printf(p, " MPPE Send Key: %seceived\n",
+ r->mppe.sendkey ? "R" : "Not r");
+ prompt_Printf(p, " MS-CHAP2-Response: %s\n",
+ r->msrepstr ? r->msrepstr : "");
+ prompt_Printf(p, " Error Message: %s\n", r->errstr ? r->errstr : "");
+ if (r->routes)
+ route_ShowSticky(p, r->routes, " Routes", 16);
+ } 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..5e9586d
--- /dev/null
+++ b/usr.sbin/ppp/radius.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright 1999 Internet Business Solutions Ltd., Switzerland
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define MPPE_POLICY_ALLOWED 1
+#define MPPE_POLICY_REQUIRED 2
+
+#define MPPE_TYPE_40BIT 2
+#define MPPE_TYPE_128BIT 4
+
+struct radius {
+ struct fdescriptor desc; /* We're a sort of (selectable) fdescriptor */
+ struct {
+ int fd; /* We're selecting on this */
+ struct rad_handle *rad; /* Using this to talk to our lib */
+ struct pppTimer timer; /* for this long */
+ struct authinfo *auth; /* Tell this about success/failure */
+ } cx;
+ unsigned valid : 1; /* Is this structure valid ? */
+ unsigned vj : 1; /* FRAMED Compression */
+ struct in_addr ip; /* FRAMED IP */
+ struct in_addr mask; /* FRAMED Netmask */
+ unsigned long mtu; /* FRAMED MTU */
+ unsigned long sessiontime; /* Session-Timeout */
+ char *filterid; /* FRAMED Filter Id */
+ struct sticky_route *routes; /* FRAMED Routes */
+ char *msrepstr; /* MS-CHAP2-Response */
+ char *repstr; /* Reply-Message */
+ char *errstr; /* Error-Message */
+ struct {
+ int policy; /* MPPE_POLICY_* */
+ int types; /* MPPE_TYPE_*BIT bitmask */
+ char *recvkey;
+ size_t recvkeylen;
+ char *sendkey;
+ size_t sendkeylen;
+ } mppe;
+ struct {
+ char file[PATH_MAX]; /* Radius config file */
+ } cfg;
+};
+
+struct radacct {
+ struct radius *rad_parent; /* "Parent" struct radius stored in bundle */
+ char user_name[AUTHLEN]; /* Session User-Name */
+ char session_id[256]; /* Unique session ID */
+ char multi_session_id[51]; /* Unique MP session ID */
+ int authentic; /* How the session has been authenticated */
+ struct in_addr ip;
+ struct in_addr mask;
+};
+
+#define descriptor2radius(d) \
+ ((d)->type == RADIUS_DESCRIPTOR ? (struct radius *)(d) : NULL)
+
+struct bundle;
+
+extern void radius_Init(struct radius *);
+extern void radius_Destroy(struct radius *);
+
+extern void radius_Show(struct radius *, struct prompt *);
+extern int radius_Authenticate(struct radius *, struct authinfo *,
+ const char *, const char *, int,
+ const char *, int);
+extern void radius_Account(struct radius *, struct radacct *,
+ struct datalink *, int, struct in_addr *,
+ struct in_addr *, struct pppThroughput *);
+
+/* An (int) parameter to radius_Account, from radlib.h */
+#if !defined(RAD_START)
+#define RAD_START 1
+#define RAD_STOP 2
+#endif
+
+/* Get address from NAS pool */
+#define RADIUS_INADDR_POOL htonl(0xfffffffe) /* 255.255.255.254 */
diff --git a/usr.sbin/ppp/route.c b/usr.sbin/ppp/route.c
new file mode 100644
index 0000000..e1a99c1
--- /dev/null
+++ b/usr.sbin/ppp/route.c
@@ -0,0 +1,894 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if_types.h>
+#include <net/route.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <net/if_dl.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/un.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/sysctl.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "layer.h"
+#include "defs.h"
+#include "command.h"
+#include "mbuf.h"
+#include "log.h"
+#include "iplist.h"
+#include "timer.h"
+#include "throughput.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "slcompress.h"
+#include "ncpaddr.h"
+#include "ipcp.h"
+#include "filter.h"
+#include "descriptor.h"
+#include "mp.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+#include "route.h"
+#include "prompt.h"
+#include "iface.h"
+#include "id.h"
+
+
+static void
+p_sockaddr(struct prompt *prompt, struct sockaddr *phost,
+ struct sockaddr *pmask, int width)
+{
+ struct ncprange range;
+ char buf[29];
+ struct sockaddr_dl *dl = (struct sockaddr_dl *)phost;
+
+ if (log_IsKept(LogDEBUG)) {
+ char tmp[50];
+
+ log_Printf(LogDEBUG, "Found the following sockaddr:\n");
+ log_Printf(LogDEBUG, " Family %d, len %d\n",
+ (int)phost->sa_family, (int)phost->sa_len);
+ inet_ntop(phost->sa_family, phost->sa_data, tmp, sizeof tmp);
+ log_Printf(LogDEBUG, " Addr %s\n", tmp);
+ if (pmask) {
+ inet_ntop(pmask->sa_family, pmask->sa_data, tmp, sizeof tmp);
+ log_Printf(LogDEBUG, " Mask %s\n", tmp);
+ }
+ }
+
+ switch (phost->sa_family) {
+ case AF_INET:
+#ifndef NOINET6
+ case AF_INET6:
+#endif
+ ncprange_setsa(&range, phost, pmask);
+ if (ncprange_isdefault(&range))
+ prompt_Printf(prompt, "%-*s ", width - 1, "default");
+ else
+ prompt_Printf(prompt, "%-*s ", width - 1, ncprange_ntoa(&range));
+ return;
+
+ case AF_LINK:
+ if (dl->sdl_nlen)
+ snprintf(buf, sizeof buf, "%.*s", dl->sdl_nlen, dl->sdl_data);
+ else if (dl->sdl_alen) {
+ if (dl->sdl_type == IFT_ETHER) {
+ if (dl->sdl_alen < sizeof buf / 3) {
+ int f;
+ u_char *MAC;
+
+ MAC = (u_char *)dl->sdl_data + dl->sdl_nlen;
+ for (f = 0; f < dl->sdl_alen; f++)
+ sprintf(buf+f*3, "%02x:", MAC[f]);
+ buf[f*3-1] = '\0';
+ } else
+ strcpy(buf, "??:??:??:??:??:??");
+ } else
+ sprintf(buf, "<IFT type %d>", dl->sdl_type);
+ } else if (dl->sdl_slen)
+ sprintf(buf, "<slen %d?>", dl->sdl_slen);
+ else
+ sprintf(buf, "link#%d", dl->sdl_index);
+ break;
+
+ default:
+ sprintf(buf, "<AF type %d>", phost->sa_family);
+ break;
+ }
+
+ prompt_Printf(prompt, "%-*s ", width-1, buf);
+}
+
+static struct bits {
+ u_int32_t b_mask;
+ char b_val;
+} bits[] = {
+ { RTF_UP, 'U' },
+ { RTF_GATEWAY, 'G' },
+ { RTF_HOST, 'H' },
+ { RTF_REJECT, 'R' },
+ { RTF_DYNAMIC, 'D' },
+ { RTF_MODIFIED, 'M' },
+ { RTF_DONE, 'd' },
+ { RTF_CLONING, 'C' },
+ { RTF_XRESOLVE, 'X' },
+ { RTF_LLINFO, 'L' },
+ { RTF_STATIC, 'S' },
+ { RTF_PROTO1, '1' },
+ { RTF_PROTO2, '2' },
+ { RTF_BLACKHOLE, 'B' },
+#ifdef RTF_WASCLONED
+ { RTF_WASCLONED, 'W' },
+#endif
+#ifdef RTF_PRCLONING
+ { RTF_PRCLONING, 'c' },
+#endif
+#ifdef RTF_PROTO3
+ { RTF_PROTO3, '3' },
+#endif
+#ifdef RTF_BROADCAST
+ { RTF_BROADCAST, 'b' },
+#endif
+ { 0, '\0' }
+};
+
+#ifndef RTF_WASCLONED
+#define RTF_WASCLONED (0)
+#endif
+
+static void
+p_flags(struct prompt *prompt, u_int32_t f, int max)
+{
+ char name[33], *flags;
+ register struct bits *p = bits;
+
+ if (max > sizeof name - 1)
+ max = sizeof name - 1;
+
+ for (flags = name; p->b_mask && flags - name < max; p++)
+ if (p->b_mask & f)
+ *flags++ = p->b_val;
+ *flags = '\0';
+ prompt_Printf(prompt, "%-*.*s", max, max, name);
+}
+
+static int route_nifs = -1;
+
+const char *
+Index2Nam(int idx)
+{
+ /*
+ * XXX: Maybe we should select() on the routing socket so that we can
+ * notice interfaces that come & go (PCCARD support).
+ * Or we could even support a signal that resets these so that
+ * the PCCARD insert/remove events can signal ppp.
+ */
+ static char **ifs; /* Figure these out once */
+ static int debug_done; /* Debug once */
+
+ if (idx > route_nifs || (idx > 0 && ifs[idx-1] == NULL)) {
+ int mib[6], have, had;
+ size_t needed;
+ char *buf, *ptr, *end;
+ struct sockaddr_dl *dl;
+ struct if_msghdr *ifm;
+
+ if (ifs) {
+ free(ifs);
+ ifs = NULL;
+ route_nifs = 0;
+ }
+ debug_done = 0;
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = 0;
+ mib[4] = NET_RT_IFLIST;
+ mib[5] = 0;
+
+ if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
+ log_Printf(LogERROR, "Index2Nam: sysctl: estimate: %s\n",
+ strerror(errno));
+ return NumStr(idx, NULL, 0);
+ }
+ if ((buf = malloc(needed)) == NULL)
+ return NumStr(idx, NULL, 0);
+ if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
+ free(buf);
+ return NumStr(idx, NULL, 0);
+ }
+ end = buf + needed;
+
+ have = 0;
+ for (ptr = buf; ptr < end; ptr += ifm->ifm_msglen) {
+ ifm = (struct if_msghdr *)ptr;
+ if (ifm->ifm_type != RTM_IFINFO)
+ continue;
+ dl = (struct sockaddr_dl *)(ifm + 1);
+ if (ifm->ifm_index > 0) {
+ if (ifm->ifm_index > have) {
+ char **newifs;
+
+ had = have;
+ have = ifm->ifm_index + 5;
+ if (had)
+ newifs = (char **)realloc(ifs, sizeof(char *) * have);
+ else
+ newifs = (char **)malloc(sizeof(char *) * have);
+ if (!newifs) {
+ log_Printf(LogDEBUG, "Index2Nam: %s\n", strerror(errno));
+ route_nifs = 0;
+ if (ifs) {
+ free(ifs);
+ ifs = NULL;
+ }
+ free(buf);
+ return NumStr(idx, NULL, 0);
+ }
+ ifs = newifs;
+ memset(ifs + had, '\0', sizeof(char *) * (have - had));
+ }
+ if (ifs[ifm->ifm_index-1] == NULL) {
+ ifs[ifm->ifm_index-1] = (char *)malloc(dl->sdl_nlen+1);
+ memcpy(ifs[ifm->ifm_index-1], dl->sdl_data, dl->sdl_nlen);
+ ifs[ifm->ifm_index-1][dl->sdl_nlen] = '\0';
+ if (route_nifs < ifm->ifm_index)
+ route_nifs = ifm->ifm_index;
+ }
+ } else if (log_IsKept(LogDEBUG))
+ log_Printf(LogDEBUG, "Skipping out-of-range interface %d!\n",
+ ifm->ifm_index);
+ }
+ free(buf);
+ }
+
+ if (log_IsKept(LogDEBUG) && !debug_done) {
+ int f;
+
+ log_Printf(LogDEBUG, "Found the following interfaces:\n");
+ for (f = 0; f < route_nifs; f++)
+ if (ifs[f] != NULL)
+ log_Printf(LogDEBUG, " Index %d, name \"%s\"\n", f+1, ifs[f]);
+ debug_done = 1;
+ }
+
+ if (idx < 1 || idx > route_nifs || ifs[idx-1] == NULL)
+ return NumStr(idx, NULL, 0);
+
+ return ifs[idx-1];
+}
+
+void
+route_ParseHdr(struct rt_msghdr *rtm, struct sockaddr *sa[RTAX_MAX])
+{
+ char *wp;
+ int rtax;
+
+ wp = (char *)(rtm + 1);
+
+ for (rtax = 0; rtax < RTAX_MAX; rtax++)
+ if (rtm->rtm_addrs & (1 << rtax)) {
+ sa[rtax] = (struct sockaddr *)wp;
+ wp += ROUNDUP(sa[rtax]->sa_len);
+ if (sa[rtax]->sa_family == 0)
+ sa[rtax] = NULL; /* ??? */
+ } else
+ sa[rtax] = NULL;
+}
+
+int
+route_Show(struct cmdargs const *arg)
+{
+ struct rt_msghdr *rtm;
+ struct sockaddr *sa[RTAX_MAX];
+ char *sp, *ep, *cp;
+ size_t needed;
+ int mib[6];
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = 0;
+ mib[4] = NET_RT_DUMP;
+ mib[5] = 0;
+ if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
+ log_Printf(LogERROR, "route_Show: sysctl: estimate: %s\n", strerror(errno));
+ return (1);
+ }
+ sp = malloc(needed);
+ if (sp == NULL)
+ return (1);
+ if (sysctl(mib, 6, sp, &needed, NULL, 0) < 0) {
+ log_Printf(LogERROR, "route_Show: sysctl: getroute: %s\n", strerror(errno));
+ free(sp);
+ return (1);
+ }
+ ep = sp + needed;
+
+ prompt_Printf(arg->prompt, "%-20s%-20sFlags Netif\n",
+ "Destination", "Gateway");
+ for (cp = sp; cp < ep; cp += rtm->rtm_msglen) {
+ rtm = (struct rt_msghdr *)cp;
+
+ route_ParseHdr(rtm, sa);
+
+ if (sa[RTAX_DST] && sa[RTAX_GATEWAY]) {
+ p_sockaddr(arg->prompt, sa[RTAX_DST], sa[RTAX_NETMASK], 20);
+ p_sockaddr(arg->prompt, sa[RTAX_GATEWAY], NULL, 20);
+
+ p_flags(arg->prompt, rtm->rtm_flags, 6);
+ prompt_Printf(arg->prompt, " %s\n", Index2Nam(rtm->rtm_index));
+ } else
+ prompt_Printf(arg->prompt, "<can't parse routing entry>\n");
+ }
+ free(sp);
+ return 0;
+}
+
+/*
+ * Delete routes associated with our interface
+ */
+void
+route_IfDelete(struct bundle *bundle, int all)
+{
+ struct rt_msghdr *rtm;
+ struct sockaddr *sa[RTAX_MAX];
+ struct ncprange range;
+ int pass;
+ size_t needed;
+ char *sp, *cp, *ep;
+ int mib[6];
+
+ log_Printf(LogDEBUG, "route_IfDelete (%d)\n", bundle->iface->index);
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = 0;
+ mib[4] = NET_RT_DUMP;
+ mib[5] = 0;
+ if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
+ log_Printf(LogERROR, "route_IfDelete: sysctl: estimate: %s\n",
+ strerror(errno));
+ return;
+ }
+
+ sp = malloc(needed);
+ if (sp == NULL)
+ return;
+
+ if (sysctl(mib, 6, sp, &needed, NULL, 0) < 0) {
+ log_Printf(LogERROR, "route_IfDelete: sysctl: getroute: %s\n",
+ strerror(errno));
+ free(sp);
+ return;
+ }
+ ep = sp + needed;
+
+ for (pass = 0; pass < 2; pass++) {
+ /*
+ * We do 2 passes. The first deletes all cloned routes. The second
+ * deletes all non-cloned routes. This is done to avoid
+ * potential errors from trying to delete route X after route Y where
+ * route X was cloned from route Y (and is no longer there 'cos it
+ * may have gone with route Y).
+ */
+ if (RTF_WASCLONED == 0 && pass == 0)
+ /* So we can't tell ! */
+ continue;
+ for (cp = sp; cp < ep; cp += rtm->rtm_msglen) {
+ rtm = (struct rt_msghdr *)cp;
+ route_ParseHdr(rtm, sa);
+ if (rtm->rtm_index == bundle->iface->index &&
+ sa[RTAX_DST] && sa[RTAX_GATEWAY] &&
+ (sa[RTAX_DST]->sa_family == AF_INET
+#ifndef NOINET6
+ || sa[RTAX_DST]->sa_family == AF_INET6
+#endif
+ ) &&
+ (all || (rtm->rtm_flags & RTF_GATEWAY))) {
+ if (log_IsKept(LogDEBUG)) {
+ char gwstr[41];
+ struct ncpaddr gw;
+ ncprange_setsa(&range, sa[RTAX_DST], sa[RTAX_NETMASK]);
+ ncpaddr_setsa(&gw, sa[RTAX_GATEWAY]);
+ snprintf(gwstr, sizeof gwstr, "%s", ncpaddr_ntoa(&gw));
+ log_Printf(LogDEBUG, "Found %s %s\n", ncprange_ntoa(&range), gwstr);
+ }
+ if (sa[RTAX_GATEWAY]->sa_family == AF_INET ||
+#ifndef NOINET6
+ sa[RTAX_GATEWAY]->sa_family == AF_INET6 ||
+#endif
+ sa[RTAX_GATEWAY]->sa_family == AF_LINK) {
+ if ((pass == 0 && (rtm->rtm_flags & RTF_WASCLONED)) ||
+ (pass == 1 && !(rtm->rtm_flags & RTF_WASCLONED))) {
+ ncprange_setsa(&range, sa[RTAX_DST], sa[RTAX_NETMASK]);
+ rt_Set(bundle, RTM_DELETE, &range, NULL, 0, 0);
+ } else
+ log_Printf(LogDEBUG, "route_IfDelete: Skip it (pass %d)\n", pass);
+ } else
+ log_Printf(LogDEBUG,
+ "route_IfDelete: Can't remove routes for family %d\n",
+ sa[RTAX_GATEWAY]->sa_family);
+ }
+ }
+ }
+ free(sp);
+}
+
+
+/*
+ * Update the MTU on all routes for the given interface
+ */
+void
+route_UpdateMTU(struct bundle *bundle)
+{
+ struct rt_msghdr *rtm;
+ struct sockaddr *sa[RTAX_MAX];
+ struct ncprange dst;
+ size_t needed;
+ char *sp, *cp, *ep;
+ int mib[6];
+
+ log_Printf(LogDEBUG, "route_UpdateMTU (%d)\n", bundle->iface->index);
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = 0;
+ mib[4] = NET_RT_DUMP;
+ mib[5] = 0;
+ if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
+ log_Printf(LogERROR, "route_IfDelete: sysctl: estimate: %s\n",
+ strerror(errno));
+ return;
+ }
+
+ sp = malloc(needed);
+ if (sp == NULL)
+ return;
+
+ if (sysctl(mib, 6, sp, &needed, NULL, 0) < 0) {
+ log_Printf(LogERROR, "route_IfDelete: sysctl: getroute: %s\n",
+ strerror(errno));
+ free(sp);
+ return;
+ }
+ ep = sp + needed;
+
+ for (cp = sp; cp < ep; cp += rtm->rtm_msglen) {
+ rtm = (struct rt_msghdr *)cp;
+ route_ParseHdr(rtm, sa);
+ if (sa[RTAX_DST] && (sa[RTAX_DST]->sa_family == AF_INET
+#ifndef NOINET6
+ || sa[RTAX_DST]->sa_family == AF_INET6
+#endif
+ ) &&
+ sa[RTAX_GATEWAY] && rtm->rtm_index == bundle->iface->index) {
+ if (log_IsKept(LogTCPIP)) {
+ ncprange_setsa(&dst, sa[RTAX_DST], sa[RTAX_NETMASK]);
+ log_Printf(LogTCPIP, "route_UpdateMTU: Netif: %d (%s), dst %s,"
+ " mtu %d\n", rtm->rtm_index, Index2Nam(rtm->rtm_index),
+ ncprange_ntoa(&dst), bundle->iface->mtu);
+ }
+ rt_Update(bundle, sa[RTAX_DST], sa[RTAX_GATEWAY], sa[RTAX_NETMASK]);
+ }
+ }
+
+ free(sp);
+}
+
+int
+GetIfIndex(char *name)
+{
+ int idx;
+
+ idx = 1;
+ while (route_nifs == -1 || idx < route_nifs)
+ if (strcmp(Index2Nam(idx), name) == 0)
+ return idx;
+ else
+ idx++;
+ return -1;
+}
+
+void
+route_Change(struct bundle *bundle, struct sticky_route *r,
+ const struct ncpaddr *me, const struct ncpaddr *peer)
+{
+ struct ncpaddr dst;
+
+ for (; r; r = r->next) {
+ ncprange_getaddr(&r->dst, &dst);
+ if (ncpaddr_family(me) == AF_INET) {
+ if ((r->type & ROUTE_DSTMYADDR) && !ncpaddr_equal(&dst, me)) {
+ rt_Set(bundle, RTM_DELETE, &r->dst, NULL, 1, 0);
+ ncprange_sethost(&r->dst, me);
+ if (r->type & ROUTE_GWHISADDR)
+ ncpaddr_copy(&r->gw, peer);
+ } else if ((r->type & ROUTE_DSTHISADDR) && !ncpaddr_equal(&dst, peer)) {
+ rt_Set(bundle, RTM_DELETE, &r->dst, NULL, 1, 0);
+ ncprange_sethost(&r->dst, peer);
+ if (r->type & ROUTE_GWHISADDR)
+ ncpaddr_copy(&r->gw, peer);
+ } else if ((r->type & ROUTE_DSTDNS0) && !ncpaddr_equal(&dst, peer)) {
+ if (bundle->ncp.ipcp.ns.dns[0].s_addr == INADDR_NONE)
+ continue;
+ rt_Set(bundle, RTM_DELETE, &r->dst, NULL, 1, 0);
+ if (r->type & ROUTE_GWHISADDR)
+ ncpaddr_copy(&r->gw, peer);
+ } else if ((r->type & ROUTE_DSTDNS1) && !ncpaddr_equal(&dst, peer)) {
+ if (bundle->ncp.ipcp.ns.dns[1].s_addr == INADDR_NONE)
+ continue;
+ rt_Set(bundle, RTM_DELETE, &r->dst, NULL, 1, 0);
+ if (r->type & ROUTE_GWHISADDR)
+ ncpaddr_copy(&r->gw, peer);
+ } else if ((r->type & ROUTE_GWHISADDR) && !ncpaddr_equal(&r->gw, peer))
+ ncpaddr_copy(&r->gw, peer);
+#ifndef NOINET6
+ } else if (ncpaddr_family(me) == AF_INET6) {
+ if ((r->type & ROUTE_DSTMYADDR6) && !ncpaddr_equal(&dst, me)) {
+ rt_Set(bundle, RTM_DELETE, &r->dst, NULL, 1, 0);
+ ncprange_sethost(&r->dst, me);
+ if (r->type & ROUTE_GWHISADDR)
+ ncpaddr_copy(&r->gw, peer);
+ } else if ((r->type & ROUTE_DSTHISADDR6) && !ncpaddr_equal(&dst, peer)) {
+ rt_Set(bundle, RTM_DELETE, &r->dst, NULL, 1, 0);
+ ncprange_sethost(&r->dst, peer);
+ if (r->type & ROUTE_GWHISADDR)
+ ncpaddr_copy(&r->gw, peer);
+ } else if ((r->type & ROUTE_GWHISADDR6) && !ncpaddr_equal(&r->gw, peer))
+ ncpaddr_copy(&r->gw, peer);
+#endif
+ }
+ rt_Set(bundle, RTM_ADD, &r->dst, &r->gw, 1, 0);
+ }
+}
+
+void
+route_Add(struct sticky_route **rp, int type, const struct ncprange *dst,
+ const struct ncpaddr *gw)
+{
+ struct sticky_route *r;
+ int dsttype = type & ROUTE_DSTANY;
+
+ r = NULL;
+ while (*rp) {
+ if ((dsttype && dsttype == ((*rp)->type & ROUTE_DSTANY)) ||
+ (!dsttype && ncprange_equal(&(*rp)->dst, dst))) {
+ /* Oops, we already have this route - unlink it */
+ free(r); /* impossible really */
+ r = *rp;
+ *rp = r->next;
+ } else
+ rp = &(*rp)->next;
+ }
+
+ if (!r)
+ r = (struct sticky_route *)malloc(sizeof(struct sticky_route));
+ r->type = type;
+ r->next = NULL;
+ ncprange_copy(&r->dst, dst);
+ ncpaddr_copy(&r->gw, gw);
+ *rp = r;
+}
+
+void
+route_Delete(struct sticky_route **rp, int type, const struct ncprange *dst)
+{
+ struct sticky_route *r;
+ int dsttype = type & ROUTE_DSTANY;
+
+ for (; *rp; rp = &(*rp)->next) {
+ if ((dsttype && dsttype == ((*rp)->type & ROUTE_DSTANY)) ||
+ (!dsttype && ncprange_equal(dst, &(*rp)->dst))) {
+ r = *rp;
+ *rp = r->next;
+ free(r);
+ break;
+ }
+ }
+}
+
+void
+route_DeleteAll(struct sticky_route **rp)
+{
+ struct sticky_route *r, *rn;
+
+ for (r = *rp; r; r = rn) {
+ rn = r->next;
+ free(r);
+ }
+ *rp = NULL;
+}
+
+void
+route_ShowSticky(struct prompt *p, struct sticky_route *r, const char *tag,
+ int indent)
+{
+ int tlen = strlen(tag);
+
+ if (tlen + 2 > indent)
+ prompt_Printf(p, "%s:\n%*s", tag, indent, "");
+ else
+ prompt_Printf(p, "%s:%*s", tag, indent - tlen - 1, "");
+
+ for (; r; r = r->next) {
+ prompt_Printf(p, "%*sadd ", tlen ? 0 : indent, "");
+ tlen = 0;
+ if (r->type & ROUTE_DSTMYADDR)
+ prompt_Printf(p, "MYADDR");
+ else if (r->type & ROUTE_DSTMYADDR6)
+ prompt_Printf(p, "MYADDR6");
+ else if (r->type & ROUTE_DSTHISADDR)
+ prompt_Printf(p, "HISADDR");
+ else if (r->type & ROUTE_DSTHISADDR6)
+ prompt_Printf(p, "HISADDR6");
+ else if (r->type & ROUTE_DSTDNS0)
+ prompt_Printf(p, "DNS0");
+ else if (r->type & ROUTE_DSTDNS1)
+ prompt_Printf(p, "DNS1");
+ else if (ncprange_isdefault(&r->dst))
+ prompt_Printf(p, "default");
+ else
+ prompt_Printf(p, "%s", ncprange_ntoa(&r->dst));
+
+ if (r->type & ROUTE_GWHISADDR)
+ prompt_Printf(p, " HISADDR\n");
+ else if (r->type & ROUTE_GWHISADDR6)
+ prompt_Printf(p, " HISADDR6\n");
+ else
+ prompt_Printf(p, " %s\n", ncpaddr_ntoa(&r->gw));
+ }
+}
+
+struct rtmsg {
+ struct rt_msghdr m_rtm;
+ char m_space[256];
+};
+
+static size_t
+memcpy_roundup(char *cp, const void *data, size_t len)
+{
+ size_t padlen;
+
+ padlen = ROUNDUP(len);
+ memcpy(cp, data, len);
+ if (padlen > len)
+ memset(cp + len, '\0', padlen - len);
+
+ return padlen;
+}
+
+int
+rt_Set(struct bundle *bundle, int cmd, const struct ncprange *dst,
+ const struct ncpaddr *gw, int bang, int quiet)
+{
+ struct rtmsg rtmes;
+ int s, nb, wb;
+ char *cp;
+ const char *cmdstr;
+ struct sockaddr_storage sadst, samask, sagw;
+ int result = 1;
+
+ if (bang)
+ cmdstr = (cmd == RTM_ADD ? "Add!" : "Delete!");
+ else
+ cmdstr = (cmd == RTM_ADD ? "Add" : "Delete");
+ s = ID0socket(PF_ROUTE, SOCK_RAW, 0);
+ if (s < 0) {
+ log_Printf(LogERROR, "rt_Set: socket(): %s\n", strerror(errno));
+ return result;
+ }
+ memset(&rtmes, '\0', sizeof rtmes);
+ rtmes.m_rtm.rtm_version = RTM_VERSION;
+ rtmes.m_rtm.rtm_type = cmd;
+ rtmes.m_rtm.rtm_addrs = RTA_DST;
+ rtmes.m_rtm.rtm_seq = ++bundle->routing_seq;
+ rtmes.m_rtm.rtm_pid = getpid();
+ rtmes.m_rtm.rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC;
+
+ if (cmd == RTM_ADD) {
+ if (bundle->ncp.cfg.sendpipe > 0) {
+ rtmes.m_rtm.rtm_rmx.rmx_sendpipe = bundle->ncp.cfg.sendpipe;
+ rtmes.m_rtm.rtm_inits |= RTV_SPIPE;
+ }
+ if (bundle->ncp.cfg.recvpipe > 0) {
+ rtmes.m_rtm.rtm_rmx.rmx_recvpipe = bundle->ncp.cfg.recvpipe;
+ rtmes.m_rtm.rtm_inits |= RTV_RPIPE;
+ }
+ }
+
+ ncprange_getsa(dst, &sadst, &samask);
+
+ cp = rtmes.m_space;
+ cp += memcpy_roundup(cp, &sadst, sadst.ss_len);
+ if (cmd == RTM_ADD) {
+ if (gw == NULL) {
+ log_Printf(LogERROR, "rt_Set: Program error\n");
+ close(s);
+ return result;
+ }
+ ncpaddr_getsa(gw, &sagw);
+ if (ncpaddr_isdefault(gw)) {
+ if (!quiet)
+ log_Printf(LogERROR, "rt_Set: Cannot add a route with"
+ " gateway 0.0.0.0\n");
+ close(s);
+ return result;
+ } else {
+ cp += memcpy_roundup(cp, &sagw, sagw.ss_len);
+ rtmes.m_rtm.rtm_addrs |= RTA_GATEWAY;
+ }
+ }
+
+ if (!ncprange_ishost(dst)) {
+ cp += memcpy_roundup(cp, &samask, samask.ss_len);
+ rtmes.m_rtm.rtm_addrs |= RTA_NETMASK;
+ }
+
+ nb = cp - (char *)&rtmes;
+ rtmes.m_rtm.rtm_msglen = nb;
+ wb = ID0write(s, &rtmes, nb);
+ if (wb < 0) {
+ log_Printf(LogTCPIP, "rt_Set failure:\n");
+ log_Printf(LogTCPIP, "rt_Set: Cmd = %s\n", cmdstr);
+ log_Printf(LogTCPIP, "rt_Set: Dst = %s\n", ncprange_ntoa(dst));
+ if (gw != NULL)
+ log_Printf(LogTCPIP, "rt_Set: Gateway = %s\n", ncpaddr_ntoa(gw));
+failed:
+ if (cmd == RTM_ADD && (rtmes.m_rtm.rtm_errno == EEXIST ||
+ (rtmes.m_rtm.rtm_errno == 0 && errno == EEXIST))) {
+ if (!bang) {
+ log_Printf(LogWARN, "Add route failed: %s already exists\n",
+ ncprange_ntoa(dst));
+ result = 0; /* Don't add to our dynamic list */
+ } else {
+ rtmes.m_rtm.rtm_type = cmd = RTM_CHANGE;
+ if ((wb = ID0write(s, &rtmes, nb)) < 0)
+ goto failed;
+ }
+ } else if (cmd == RTM_DELETE &&
+ (rtmes.m_rtm.rtm_errno == ESRCH ||
+ (rtmes.m_rtm.rtm_errno == 0 && errno == ESRCH))) {
+ if (!bang)
+ log_Printf(LogWARN, "Del route failed: %s: Non-existent\n",
+ ncprange_ntoa(dst));
+ } else if (rtmes.m_rtm.rtm_errno == 0) {
+ if (!quiet || errno != ENETUNREACH)
+ log_Printf(LogWARN, "%s route failed: %s: errno: %s\n", cmdstr,
+ ncprange_ntoa(dst), strerror(errno));
+ } else
+ log_Printf(LogWARN, "%s route failed: %s: %s\n",
+ cmdstr, ncprange_ntoa(dst), strerror(rtmes.m_rtm.rtm_errno));
+ }
+
+ if (log_IsKept(LogDEBUG)) {
+ char gwstr[40];
+
+ if (gw)
+ snprintf(gwstr, sizeof gwstr, "%s", ncpaddr_ntoa(gw));
+ else
+ snprintf(gwstr, sizeof gwstr, "<none>");
+ log_Printf(LogDEBUG, "wrote %d: cmd = %s, dst = %s, gateway = %s\n",
+ wb, cmdstr, ncprange_ntoa(dst), gwstr);
+ }
+ close(s);
+
+ return result;
+}
+
+void
+rt_Update(struct bundle *bundle, const struct sockaddr *dst,
+ const struct sockaddr *gw, const struct sockaddr *mask)
+{
+ struct ncprange ncpdst;
+ struct rtmsg rtmes;
+ char *p;
+ int s, wb;
+
+ s = ID0socket(PF_ROUTE, SOCK_RAW, 0);
+ if (s < 0) {
+ log_Printf(LogERROR, "rt_Update: socket(): %s\n", strerror(errno));
+ return;
+ }
+
+ memset(&rtmes, '\0', sizeof rtmes);
+ rtmes.m_rtm.rtm_version = RTM_VERSION;
+ rtmes.m_rtm.rtm_type = RTM_CHANGE;
+ rtmes.m_rtm.rtm_addrs = 0;
+ rtmes.m_rtm.rtm_seq = ++bundle->routing_seq;
+ rtmes.m_rtm.rtm_pid = getpid();
+ rtmes.m_rtm.rtm_flags = RTF_UP | RTF_STATIC;
+
+ if (bundle->ncp.cfg.sendpipe > 0) {
+ rtmes.m_rtm.rtm_rmx.rmx_sendpipe = bundle->ncp.cfg.sendpipe;
+ rtmes.m_rtm.rtm_inits |= RTV_SPIPE;
+ }
+
+ if (bundle->ncp.cfg.recvpipe > 0) {
+ rtmes.m_rtm.rtm_rmx.rmx_recvpipe = bundle->ncp.cfg.recvpipe;
+ rtmes.m_rtm.rtm_inits |= RTV_RPIPE;
+ }
+
+ rtmes.m_rtm.rtm_rmx.rmx_mtu = bundle->iface->mtu;
+ rtmes.m_rtm.rtm_inits |= RTV_MTU;
+ p = rtmes.m_space;
+
+ if (dst) {
+ rtmes.m_rtm.rtm_addrs |= RTA_DST;
+ p += memcpy_roundup(p, dst, dst->sa_len);
+ }
+
+ rtmes.m_rtm.rtm_addrs |= RTA_GATEWAY;
+ p += memcpy_roundup(p, gw, gw->sa_len);
+ if (mask) {
+ rtmes.m_rtm.rtm_addrs |= RTA_NETMASK;
+ p += memcpy_roundup(p, mask, mask->sa_len);
+ }
+
+ rtmes.m_rtm.rtm_msglen = p - (char *)&rtmes;
+
+ wb = ID0write(s, &rtmes, rtmes.m_rtm.rtm_msglen);
+ if (wb < 0) {
+ ncprange_setsa(&ncpdst, dst, mask);
+
+ log_Printf(LogTCPIP, "rt_Update failure:\n");
+ log_Printf(LogTCPIP, "rt_Update: Dst = %s\n", ncprange_ntoa(&ncpdst));
+
+ if (rtmes.m_rtm.rtm_errno == 0)
+ log_Printf(LogWARN, "%s: Change route failed: errno: %s\n",
+ ncprange_ntoa(&ncpdst), strerror(errno));
+ else
+ log_Printf(LogWARN, "%s: Change route failed: %s\n",
+ ncprange_ntoa(&ncpdst), strerror(rtmes.m_rtm.rtm_errno));
+ }
+ close(s);
+}
diff --git a/usr.sbin/ppp/route.h b/usr.sbin/ppp/route.h
new file mode 100644
index 0000000..edf6604
--- /dev/null
+++ b/usr.sbin/ppp/route.h
@@ -0,0 +1,73 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct bundle;
+struct cmdargs;
+struct rt_msghdr;
+struct sockaddr;
+
+#define ROUTE_STATIC 0x0000
+#define ROUTE_DSTMYADDR 0x0001
+#define ROUTE_DSTMYADDR6 0x0002
+#define ROUTE_DSTHISADDR 0x0004
+#define ROUTE_DSTHISADDR6 0x0008
+#define ROUTE_DSTDNS0 0x0010
+#define ROUTE_DSTDNS1 0x0020
+#define ROUTE_DSTANY 0x0040
+#define ROUTE_GWHISADDR 0x0080 /* May be ORd with DST_* */
+#define ROUTE_GWHISADDR6 0x0100 /* May be ORd with DST_* */
+
+struct sticky_route {
+ int type; /* ROUTE_* value (not _STATIC) */
+ struct sticky_route *next; /* next in list */
+
+ struct ncprange dst;
+ struct ncpaddr gw;
+};
+
+extern int GetIfIndex(char *);
+extern int route_Show(struct cmdargs const *);
+extern void route_IfDelete(struct bundle *, int);
+extern void route_UpdateMTU(struct bundle *);
+extern const char *Index2Nam(int);
+extern void route_Change(struct bundle *, struct sticky_route *,
+ const struct ncpaddr *, const struct ncpaddr *);
+extern void route_Add(struct sticky_route **, int, const struct ncprange *,
+ const struct ncpaddr *);
+extern void route_Delete(struct sticky_route **, int, const struct ncprange *);
+extern void route_DeleteAll(struct sticky_route **);
+extern void route_Clean(struct bundle *, struct sticky_route *);
+extern void route_ShowSticky(struct prompt *, struct sticky_route *,
+ const char *, int);
+extern void route_ParseHdr(struct rt_msghdr *, struct sockaddr *[RTAX_MAX]);
+extern int rt_Set(struct bundle *, int, const struct ncprange *,
+ const struct ncpaddr *, int, int);
+extern void rt_Update(struct bundle *, const struct sockaddr *,
+ const struct sockaddr *, const struct sockaddr *);
diff --git a/usr.sbin/ppp/server.c b/usr.sbin/ppp/server.c
new file mode 100644
index 0000000..0c13bde
--- /dev/null
+++ b/usr.sbin/ppp/server.c
@@ -0,0 +1,412 @@
+/*-
+ * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/un.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "log.h"
+#include "descriptor.h"
+#include "server.h"
+#include "prompt.h"
+#include "ncpaddr.h"
+#include "probe.h"
+
+static int
+server_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n)
+{
+ struct server *s = descriptor2server(d);
+ struct prompt *p;
+ int sets;
+
+ sets = 0;
+ if (r && s->fd >= 0) {
+ if (*n < s->fd + 1)
+ *n = s->fd + 1;
+ FD_SET(s->fd, r);
+ log_Printf(LogTIMER, "server: fdset(r) %d\n", s->fd);
+ sets++;
+ }
+
+ for (p = log_PromptList(); p; p = p->next)
+ sets += descriptor_UpdateSet(&p->desc, r, w, e, n);
+
+ return sets;
+}
+
+static int
+server_IsSet(struct fdescriptor *d, const fd_set *fdset)
+{
+ struct server *s = descriptor2server(d);
+ struct prompt *p;
+
+ if (s->fd >= 0 && FD_ISSET(s->fd, fdset))
+ return 1;
+
+ for (p = log_PromptList(); p; p = p->next)
+ if (descriptor_IsSet(&p->desc, fdset))
+ return 1;
+
+ return 0;
+}
+
+static void
+server_Read(struct fdescriptor *d, struct bundle *bundle, const fd_set *fdset)
+{
+ struct server *s = descriptor2server(d);
+ struct sockaddr_storage ss;
+ struct sockaddr *sa = (struct sockaddr *)&ss;
+ struct sockaddr_in *sin = (struct sockaddr_in *)&ss;
+#ifndef NOINET6
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&ss;
+#endif
+ int ssize = sizeof ss, wfd;
+ struct prompt *p;
+ struct ncpaddr addr;
+
+ if (s->fd >= 0 && FD_ISSET(s->fd, fdset)) {
+ wfd = accept(s->fd, sa, &ssize);
+ if (wfd < 0)
+ log_Printf(LogERROR, "server_Read: accept(): %s\n", strerror(errno));
+ else if (sa->sa_len == 0) {
+ close(wfd);
+ wfd = -1;
+ }
+ } else
+ wfd = -1;
+
+ if (wfd >= 0)
+ switch (sa->sa_family) {
+ case AF_LOCAL:
+ log_Printf(LogPHASE, "Connected to local client.\n");
+ break;
+
+ case AF_INET:
+ ncpaddr_setsa(&addr, sa);
+ if (ntohs(sin->sin_port) < 1024) {
+ log_Printf(LogALERT, "Rejected client connection from %s:%u"
+ "(invalid port number) !\n",
+ ncpaddr_ntoa(&addr), ntohs(sin->sin_port));
+ close(wfd);
+ wfd = -1;
+ break;
+ }
+ log_Printf(LogPHASE, "Connected to client from %s:%u\n",
+ ncpaddr_ntoa(&addr), ntohs(sin->sin_port));
+ break;
+
+#ifndef NOINET6
+ case AF_INET6:
+ ncpaddr_setsa(&addr, sa);
+ if (ntohs(sin6->sin6_port) < 1024) {
+ log_Printf(LogALERT, "Rejected client connection from %s:%u"
+ "(invalid port number) !\n",
+ ncpaddr_ntoa(&addr), ntohs(sin6->sin6_port));
+ close(wfd);
+ wfd = -1;
+ break;
+ }
+ log_Printf(LogPHASE, "Connected to client from %s:%u\n",
+ ncpaddr_ntoa(&addr), ntohs(sin6->sin6_port));
+ break;
+#endif
+
+ default:
+ write(wfd, "Unrecognised access !\n", 22);
+ close(wfd);
+ wfd = -1;
+ break;
+ }
+
+ if (wfd >= 0) {
+ if ((p = prompt_Create(s, bundle, wfd)) == NULL) {
+ write(wfd, "Connection refused.\n", 20);
+ close(wfd);
+ } else {
+ switch (sa->sa_family) {
+ case AF_LOCAL:
+ p->src.type = "local";
+ strncpy(p->src.from, s->cfg.sockname, sizeof p->src.from - 1);
+ p->src.from[sizeof p->src.from - 1] = '\0';
+ break;
+ case AF_INET:
+ p->src.type = "ip";
+ snprintf(p->src.from, sizeof p->src.from, "%s:%u",
+ ncpaddr_ntoa(&addr), ntohs(sin->sin_port));
+ break;
+#ifndef NOINET6
+ case AF_INET6:
+ p->src.type = "ip6";
+ snprintf(p->src.from, sizeof p->src.from, "%s:%u",
+ ncpaddr_ntoa(&addr), ntohs(sin6->sin6_port));
+ break;
+#endif
+ }
+ prompt_TtyCommandMode(p);
+ prompt_Required(p);
+ }
+ }
+
+ log_PromptListChanged = 0;
+ for (p = log_PromptList(); p; p = p->next)
+ if (descriptor_IsSet(&p->desc, fdset)) {
+ descriptor_Read(&p->desc, bundle, fdset);
+ if (log_PromptListChanged)
+ break;
+ }
+}
+
+static int
+server_Write(struct fdescriptor *d, struct bundle *bundle, const fd_set *fdset)
+{
+ /* We never want to write here ! */
+ log_Printf(LogALERT, "server_Write: Internal error: Bad call !\n");
+ return 0;
+}
+
+struct server server = {
+ {
+ SERVER_DESCRIPTOR,
+ server_UpdateSet,
+ server_IsSet,
+ server_Read,
+ server_Write
+ },
+ -1
+};
+
+enum server_stat
+server_Reopen(struct bundle *bundle)
+{
+ char name[sizeof server.cfg.sockname];
+ struct stat st;
+ u_short port;
+ mode_t mask;
+ enum server_stat ret;
+
+ if (server.cfg.sockname[0] != '\0') {
+ strcpy(name, server.cfg.sockname);
+ mask = server.cfg.mask;
+ server_Close(bundle);
+ if (server.cfg.sockname[0] != '\0' && stat(server.cfg.sockname, &st) == 0)
+ if (!(st.st_mode & S_IFSOCK) || unlink(server.cfg.sockname) != 0)
+ return SERVER_FAILED;
+ ret = server_LocalOpen(bundle, name, mask);
+ } else if (server.cfg.port != 0) {
+ port = server.cfg.port;
+ server_Close(bundle);
+ ret = server_TcpOpen(bundle, port);
+ } else
+ ret = SERVER_UNSET;
+
+ return ret;
+}
+
+enum server_stat
+server_LocalOpen(struct bundle *bundle, const char *name, mode_t mask)
+{
+ struct sockaddr_un ifsun;
+ mode_t oldmask;
+ int s;
+
+ oldmask = (mode_t)-1; /* Silence compiler */
+
+ if (server.cfg.sockname && !strcmp(server.cfg.sockname, name))
+ server_Close(bundle);
+
+ memset(&ifsun, '\0', sizeof ifsun);
+ ifsun.sun_len = strlen(name);
+ if (ifsun.sun_len > sizeof ifsun.sun_path - 1) {
+ log_Printf(LogERROR, "Local: %s: Path too long\n", name);
+ return SERVER_INVALID;
+ }
+ ifsun.sun_family = AF_LOCAL;
+ strcpy(ifsun.sun_path, name);
+
+ s = socket(PF_LOCAL, SOCK_STREAM, 0);
+ if (s < 0) {
+ log_Printf(LogERROR, "Local: socket: %s\n", strerror(errno));
+ goto failed;
+ }
+ setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &s, sizeof s);
+ if (mask != (mode_t)-1)
+ oldmask = umask(mask);
+ if (bind(s, (struct sockaddr *)&ifsun, sizeof ifsun) < 0) {
+ if (mask != (mode_t)-1)
+ umask(oldmask);
+ log_Printf(LogWARN, "Local: bind: %s\n", strerror(errno));
+ close(s);
+ goto failed;
+ }
+ if (mask != (mode_t)-1)
+ umask(oldmask);
+ if (listen(s, 5) != 0) {
+ log_Printf(LogERROR, "Local: Unable to listen to socket -"
+ " BUNDLE overload?\n");
+ close(s);
+ unlink(name);
+ goto failed;
+ }
+ server_Close(bundle);
+ server.fd = s;
+ server.cfg.port = 0;
+ strncpy(server.cfg.sockname, ifsun.sun_path, sizeof server.cfg.sockname - 1);
+ server.cfg.sockname[sizeof server.cfg.sockname - 1] = '\0';
+ server.cfg.mask = mask;
+ log_Printf(LogPHASE, "Listening at local socket %s.\n", name);
+
+ return SERVER_OK;
+
+failed:
+ if (server.fd == -1) {
+ server.fd = -1;
+ server.cfg.port = 0;
+ strncpy(server.cfg.sockname, ifsun.sun_path,
+ sizeof server.cfg.sockname - 1);
+ server.cfg.sockname[sizeof server.cfg.sockname - 1] = '\0';
+ server.cfg.mask = mask;
+ }
+ return SERVER_FAILED;
+}
+
+enum server_stat
+server_TcpOpen(struct bundle *bundle, u_short port)
+{
+ struct sockaddr_storage ss;
+ struct sockaddr_in *sin = (struct sockaddr_in *)&ss;
+#ifndef NOINET6
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&ss;
+#endif
+ int s, sz;
+
+ if (server.cfg.port == port)
+ server_Close(bundle);
+
+ if (port == 0)
+ return SERVER_INVALID;
+
+ memset(&ss, '\0', sizeof ss);
+#ifndef NOINET6
+ if (probe.ipv6_available) {
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = htons(port);
+ sin6->sin6_len = (u_int8_t)sizeof ss;
+ sz = sizeof *sin6;
+ s = socket(PF_INET6, SOCK_STREAM, 0);
+ } else
+#endif
+ {
+ sin->sin_family = AF_INET;
+ sin->sin_port = htons(port);
+ sin->sin_len = (u_int8_t)sizeof ss;
+ sin->sin_addr.s_addr = INADDR_ANY;
+ sz = sizeof *sin;
+ s = socket(PF_INET, SOCK_STREAM, 0);
+ }
+
+ if (s < 0) {
+ log_Printf(LogERROR, "Tcp: socket: %s\n", strerror(errno));
+ goto failed;
+ }
+
+ setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &s, sizeof s);
+ if (bind(s, (struct sockaddr *)&ss, sz) < 0) {
+ log_Printf(LogWARN, "Tcp: bind: %s\n", strerror(errno));
+ close(s);
+ goto failed;
+ }
+ if (listen(s, 5) != 0) {
+ log_Printf(LogERROR, "Tcp: Unable to listen to socket: %s\n",
+ strerror(errno));
+ close(s);
+ goto failed;
+ }
+ server_Close(bundle);
+ server.fd = s;
+ server.cfg.port = port;
+ *server.cfg.sockname = '\0';
+ server.cfg.mask = 0;
+ log_Printf(LogPHASE, "Listening at port %d.\n", port);
+ return SERVER_OK;
+
+failed:
+ if (server.fd == -1) {
+ server.fd = -1;
+ server.cfg.port = port;
+ *server.cfg.sockname = '\0';
+ server.cfg.mask = 0;
+ }
+ return SERVER_FAILED;
+}
+
+int
+server_Close(struct bundle *bundle)
+{
+ if (server.fd >= 0) {
+ if (*server.cfg.sockname != '\0') {
+ struct sockaddr_un un;
+ int sz = sizeof un;
+
+ if (getsockname(server.fd, (struct sockaddr *)&un, &sz) == 0 &&
+ un.sun_family == AF_LOCAL && sz == sizeof un)
+ unlink(un.sun_path);
+ }
+ close(server.fd);
+ server.fd = -1;
+ /* Drop associated prompts */
+ log_DestroyPrompts(&server);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+server_Clear(struct bundle *bundle)
+{
+ int ret;
+
+ ret = server_Close(bundle);
+
+ server.fd = -1;
+ server.cfg.port = 0;
+ *server.cfg.sockname = '\0';
+ server.cfg.mask = 0;
+
+ return ret;
+}
diff --git a/usr.sbin/ppp/server.h b/usr.sbin/ppp/server.h
new file mode 100644
index 0000000..9fdc52c
--- /dev/null
+++ b/usr.sbin/ppp/server.h
@@ -0,0 +1,61 @@
+/*-
+ * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct bundle;
+
+struct server {
+ struct fdescriptor desc;
+ int fd;
+
+ struct {
+ char passwd[50];
+
+ char sockname[PATH_MAX]; /* Points to local socket path */
+ mode_t mask;
+
+ u_short port; /* tcp socket */
+ } cfg;
+};
+
+enum server_stat {
+ SERVER_OK, /* Diagnostic socket available */
+ SERVER_INVALID, /* Bad args, can't be set up */
+ SERVER_FAILED, /* Failed - lack of resources */
+ SERVER_UNSET /* Not already set up */
+};
+
+#define descriptor2server(d) \
+ ((d)->type == SERVER_DESCRIPTOR ? (struct server *)(d) : NULL)
+
+extern struct server server;
+
+extern enum server_stat server_LocalOpen(struct bundle *, const char *, mode_t);
+extern enum server_stat server_TcpOpen(struct bundle *, u_short);
+extern enum server_stat server_Reopen(struct bundle *);
+extern int server_Close(struct bundle *);
+extern int server_Clear(struct bundle *);
diff --git a/usr.sbin/ppp/sig.c b/usr.sbin/ppp/sig.c
new file mode 100644
index 0000000..a42194b
--- /dev/null
+++ b/usr.sbin/ppp/sig.c
@@ -0,0 +1,119 @@
+/*-
+ * Copyright (c) 1997 - 1999, 2001 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+
+#include <signal.h>
+
+#include "log.h"
+#include "sig.h"
+
+static int caused[NSIG]; /* An array of pending signals */
+static int necessary; /* Anything set ? */
+static sig_type handler[NSIG]; /* all start at SIG_DFL */
+
+
+/*
+ * Record a signal in the "caused" array
+ *
+ * This function is the only thing actually called in signal context. It
+ * records that a signal has been caused and that sig_Handle() should be
+ * called (in non-signal context) as soon as possible to process that
+ * signal.
+ */
+static void
+signal_recorder(int sig)
+{
+ caused[sig - 1]++;
+ necessary = 1;
+}
+
+
+/*
+ * Set up signal_recorder to handle the given sig and record ``fn'' as
+ * the function to ultimately call in sig_Handle(). ``fn'' will not be
+ * called in signal context (as sig_Handle() is not called in signal
+ * context).
+ */
+sig_type
+sig_signal(int sig, sig_type fn)
+{
+ sig_type Result;
+
+ if (sig <= 0 || sig > NSIG) {
+ /* Oops - we must be a bit out of date (too many sigs ?) */
+ log_Printf(LogALERT, "Eeek! %s:%d: I must be out of date!\n",
+ __FILE__, __LINE__);
+ return signal(sig, fn);
+ }
+ Result = handler[sig - 1];
+ if (fn == SIG_DFL || fn == SIG_IGN) {
+ signal(sig, fn);
+ handler[sig - 1] = (sig_type) 0;
+ } else {
+ handler[sig - 1] = fn;
+ signal(sig, signal_recorder);
+ }
+ caused[sig - 1] = 0;
+ return Result;
+}
+
+
+/*
+ * Call the handlers for any pending signals
+ *
+ * This function is called from a non-signal context - in fact, it's
+ * called every time select() in DoLoop() returns - just in case
+ * select() returned due to a signal being recorded by signal_recorder().
+ */
+int
+sig_Handle()
+{
+ int sig;
+ int got;
+ int result;
+
+ result = 0;
+ if (necessary) {
+ /* We've *probably* got something in `caused' set */
+ necessary = 0;
+ /* `necessary' might go back to 1 while we're in here.... */
+ do {
+ got = 0;
+ for (sig = 0; sig < NSIG; sig++)
+ if (caused[sig]) {
+ caused[sig]--;
+ got++;
+ result++;
+ (*handler[sig])(sig + 1);
+ }
+ } while (got);
+ }
+
+ return result;
+}
diff --git a/usr.sbin/ppp/sig.h b/usr.sbin/ppp/sig.h
new file mode 100644
index 0000000..27d264c
--- /dev/null
+++ b/usr.sbin/ppp/sig.h
@@ -0,0 +1,35 @@
+/*-
+ * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+typedef void (*sig_type)(int);
+
+/* Call this instead of signal() */
+extern sig_type sig_signal(int, sig_type);
+
+/* Call this when you want things to *actually* happen */
+extern int sig_Handle(void);
diff --git a/usr.sbin/ppp/slcompress.c b/usr.sbin/ppp/slcompress.c
new file mode 100644
index 0000000..2995301
--- /dev/null
+++ b/usr.sbin/ppp/slcompress.c
@@ -0,0 +1,588 @@
+/*
+ * Routines to compress and uncompess tcp packets (for transmission
+ * over low speed serial lines.
+ *
+ * Copyright (c) 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $FreeBSD$
+ *
+ * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
+ * - Initial distribution.
+ */
+
+#include <sys/param.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <netinet/ip.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+
+#include "layer.h"
+#include "defs.h"
+#include "command.h"
+#include "mbuf.h"
+#include "log.h"
+#include "slcompress.h"
+#include "descriptor.h"
+#include "prompt.h"
+#include "timer.h"
+#include "fsm.h"
+#include "throughput.h"
+#include "iplist.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "ncpaddr.h"
+#include "ipcp.h"
+#include "filter.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "mp.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+
+void
+sl_compress_init(struct slcompress *comp, int max_state)
+{
+ register u_int i;
+ register struct cstate *tstate = comp->tstate;
+
+ memset(comp, '\0', sizeof *comp);
+ for (i = max_state; i > 0; --i) {
+ tstate[i].cs_id = i;
+ tstate[i].cs_next = &tstate[i - 1];
+ }
+ tstate[0].cs_next = &tstate[max_state];
+ tstate[0].cs_id = 0;
+ comp->last_cs = &tstate[0];
+ comp->last_recv = 255;
+ comp->last_xmit = 255;
+ comp->flags = SLF_TOSS;
+}
+
+
+/* ENCODE encodes a number that is known to be non-zero. ENCODEZ
+ * checks for zero (since zero has to be encoded in the 32-bit, 3 byte
+ * form).
+ */
+#define ENCODE(n) { \
+ if ((u_short)(n) >= 256) { \
+ *cp++ = 0; \
+ cp[1] = (n); \
+ cp[0] = (n) >> 8; \
+ cp += 2; \
+ } else { \
+ *cp++ = (n); \
+ } \
+}
+#define ENCODEZ(n) { \
+ if ((u_short)(n) >= 256 || (u_short)(n) == 0) { \
+ *cp++ = 0; \
+ cp[1] = (n); \
+ cp[0] = (n) >> 8; \
+ cp += 2; \
+ } else { \
+ *cp++ = (n); \
+ } \
+}
+
+#define DECODEL(f) { \
+ if (*cp == 0) {\
+ (f) = htonl(ntohl(f) + ((cp[1] << 8) | cp[2])); \
+ cp += 3; \
+ } else { \
+ (f) = htonl(ntohl(f) + (u_int32_t)*cp++); \
+ } \
+}
+
+#define DECODES(f) { \
+ if (*cp == 0) {\
+ (f) = htons(ntohs(f) + ((cp[1] << 8) | cp[2])); \
+ cp += 3; \
+ } else { \
+ (f) = htons(ntohs(f) + (u_int32_t)*cp++); \
+ } \
+}
+
+#define DECODEU(f) { \
+ if (*cp == 0) {\
+ (f) = htons((cp[1] << 8) | cp[2]); \
+ cp += 3; \
+ } else { \
+ (f) = htons((u_int32_t)*cp++); \
+ } \
+}
+
+
+u_char
+sl_compress_tcp(struct mbuf * m,
+ struct ip * ip,
+ struct slcompress *comp,
+ struct slstat *slstat,
+ int compress_cid)
+{
+ register struct cstate *cs = comp->last_cs->cs_next;
+ register u_int hlen = ip->ip_hl;
+ register struct tcphdr *oth;
+ register struct tcphdr *th;
+ register u_int deltaS, deltaA;
+ register u_int changes = 0;
+ u_char new_seq[16];
+ register u_char *cp = new_seq;
+
+ /*
+ * Bail if this is an IP fragment or if the TCP packet isn't `compressible'
+ * (i.e., ACK isn't set or some other control bit is set). (We assume that
+ * the caller has already made sure the packet is IP proto TCP).
+ */
+ if ((ip->ip_off & htons(0x3fff)) || m->m_len < 40) {
+ log_Printf(LogDEBUG, "??? 1 ip_off = %x, m_len = %lu\n",
+ ip->ip_off, (unsigned long)m->m_len);
+ log_DumpBp(LogDEBUG, "", m);
+ return (TYPE_IP);
+ }
+ th = (struct tcphdr *) & ((int *) ip)[hlen];
+ if ((th->th_flags & (TH_SYN | TH_FIN | TH_RST | TH_ACK)) != TH_ACK) {
+ log_Printf(LogDEBUG, "??? 2 th_flags = %x\n", th->th_flags);
+ log_DumpBp(LogDEBUG, "", m);
+ return (TYPE_IP);
+ }
+
+ /*
+ * Packet is compressible -- we're going to send either a COMPRESSED_TCP or
+ * UNCOMPRESSED_TCP packet. Either way we need to locate (or create) the
+ * connection state. Special case the most recently used connection since
+ * it's most likely to be used again & we don't have to do any reordering
+ * if it's used.
+ */
+ slstat->sls_packets++;
+ if (ip->ip_src.s_addr != cs->cs_ip.ip_src.s_addr ||
+ ip->ip_dst.s_addr != cs->cs_ip.ip_dst.s_addr ||
+ *(int *) th != ((int *) &cs->cs_ip)[cs->cs_ip.ip_hl]) {
+
+ /*
+ * Wasn't the first -- search for it.
+ *
+ * States are kept in a circularly linked list with last_cs pointing to the
+ * end of the list. The list is kept in lru order by moving a state to
+ * the head of the list whenever it is referenced. Since the list is
+ * short and, empirically, the connection we want is almost always near
+ * the front, we locate states via linear search. If we don't find a
+ * state for the datagram, the oldest state is (re-)used.
+ */
+ register struct cstate *lcs;
+ register struct cstate *lastcs = comp->last_cs;
+
+ do {
+ lcs = cs;
+ cs = cs->cs_next;
+ slstat->sls_searches++;
+ if (ip->ip_src.s_addr == cs->cs_ip.ip_src.s_addr
+ && ip->ip_dst.s_addr == cs->cs_ip.ip_dst.s_addr
+ && *(int *) th == ((int *) &cs->cs_ip)[cs->cs_ip.ip_hl])
+ goto found;
+ } while (cs != lastcs);
+
+ /*
+ * Didn't find it -- re-use oldest cstate. Send an uncompressed packet
+ * that tells the other side what connection number we're using for this
+ * conversation. Note that since the state list is circular, the oldest
+ * state points to the newest and we only need to set last_cs to update
+ * the lru linkage.
+ */
+ slstat->sls_misses++;
+ comp->last_cs = lcs;
+#define THOFFSET(th) (th->th_off)
+ hlen += th->th_off;
+ hlen <<= 2;
+ if (hlen > m->m_len)
+ return (TYPE_IP);
+ goto uncompressed;
+
+found:
+
+ /*
+ * Found it -- move to the front on the connection list.
+ */
+ if (cs == lastcs)
+ comp->last_cs = lcs;
+ else {
+ lcs->cs_next = cs->cs_next;
+ cs->cs_next = lastcs->cs_next;
+ lastcs->cs_next = cs;
+ }
+ }
+
+ /*
+ * Make sure that only what we expect to change changed. The first line of
+ * the `if' checks the IP protocol version, header length & type of
+ * service. The 2nd line checks the "Don't fragment" bit. The 3rd line
+ * checks the time-to-live and protocol (the protocol check is unnecessary
+ * but costless). The 4th line checks the TCP header length. The 5th line
+ * checks IP options, if any. The 6th line checks TCP options, if any. If
+ * any of these things are different between the previous & current
+ * datagram, we send the current datagram `uncompressed'.
+ */
+ oth = (struct tcphdr *) & ((int *) &cs->cs_ip)[hlen];
+ deltaS = hlen;
+ hlen += th->th_off;
+ hlen <<= 2;
+ if (hlen > m->m_len)
+ return (TYPE_IP);
+
+ if (((u_short *) ip)[0] != ((u_short *) & cs->cs_ip)[0] ||
+ ((u_short *) ip)[3] != ((u_short *) & cs->cs_ip)[3] ||
+ ((u_short *) ip)[4] != ((u_short *) & cs->cs_ip)[4] ||
+ THOFFSET(th) != THOFFSET(oth) ||
+ (deltaS > 5 &&
+ memcmp(ip + 1, &cs->cs_ip + 1, (deltaS - 5) << 2)) ||
+ (THOFFSET(th) > 5 &&
+ memcmp(th + 1, oth + 1, (THOFFSET(th) - 5) << 2))) {
+ goto uncompressed;
+ }
+
+ /*
+ * Figure out which of the changing fields changed. The receiver expects
+ * changes in the order: urgent, window, ack, seq (the order minimizes the
+ * number of temporaries needed in this section of code).
+ */
+ if (th->th_flags & TH_URG) {
+ deltaS = ntohs(th->th_urp);
+ ENCODEZ(deltaS);
+ changes |= NEW_U;
+ } else if (th->th_urp != oth->th_urp) {
+
+ /*
+ * argh! URG not set but urp changed -- a sensible implementation should
+ * never do this but RFC793 doesn't prohibit the change so we have to
+ * deal with it.
+ */
+ goto uncompressed;
+ }
+ deltaS = (u_short) (ntohs(th->th_win) - ntohs(oth->th_win));
+ if (deltaS) {
+ ENCODE(deltaS);
+ changes |= NEW_W;
+ }
+ deltaA = ntohl(th->th_ack) - ntohl(oth->th_ack);
+ if (deltaA) {
+ if (deltaA > 0xffff) {
+ goto uncompressed;
+ }
+ ENCODE(deltaA);
+ changes |= NEW_A;
+ }
+ deltaS = ntohl(th->th_seq) - ntohl(oth->th_seq);
+ if (deltaS) {
+ if (deltaS > 0xffff) {
+ goto uncompressed;
+ }
+ ENCODE(deltaS);
+ changes |= NEW_S;
+ }
+ switch (changes) {
+
+ case 0:
+
+ /*
+ * Nothing changed. If this packet contains data and the last one didn't,
+ * this is probably a data packet following an ack (normal on an
+ * interactive connection) and we send it compressed. Otherwise it's
+ * probably a retransmit, retransmitted ack or window probe. Send it
+ * uncompressed in case the other side missed the compressed version.
+ */
+ if (ip->ip_len != cs->cs_ip.ip_len &&
+ ntohs(cs->cs_ip.ip_len) == hlen)
+ break;
+
+ /* (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->m_len -= hlen;
+ m->m_offset += hlen;
+ *cp++ = deltaA >> 8;
+ *cp++ = deltaA;
+ memcpy(cp, new_seq, deltaS);
+ slstat->sls_compressed++;
+ return (TYPE_COMPRESSED_TCP);
+
+ /*
+ * Update connection state cs & send uncompressed packet ('uncompressed'
+ * means a regular ip/tcp packet but with the 'conversation id' we hope to
+ * use on future compressed packets in the protocol field).
+ */
+uncompressed:
+ memcpy(&cs->cs_ip, ip, hlen);
+ ip->ip_p = cs->cs_id;
+ comp->last_xmit = cs->cs_id;
+ return (TYPE_UNCOMPRESSED_TCP);
+}
+
+
+int
+sl_uncompress_tcp(u_char ** bufp, int len, u_int type, struct slcompress *comp,
+ struct slstat *slstat, int max_state)
+{
+ register u_char *cp;
+ register u_int hlen, changes;
+ register struct tcphdr *th;
+ register struct cstate *cs;
+ register struct ip *ip;
+ u_short *bp;
+
+ switch (type) {
+
+ case TYPE_UNCOMPRESSED_TCP:
+ ip = (struct ip *) * bufp;
+ if (ip->ip_p > max_state)
+ goto bad;
+ cs = &comp->rstate[comp->last_recv = ip->ip_p];
+ comp->flags &= ~SLF_TOSS;
+ ip->ip_p = IPPROTO_TCP;
+
+ /*
+ * Calculate the size of the TCP/IP header and make sure that we don't
+ * overflow the space we have available for it.
+ */
+ hlen = ip->ip_hl << 2;
+ if (hlen + sizeof(struct tcphdr) > len)
+ goto bad;
+ th = (struct tcphdr *) & ((char *) ip)[hlen];
+ hlen += THOFFSET(th) << 2;
+ if (hlen > MAX_HDR)
+ goto bad;
+ memcpy(&cs->cs_ip, ip, hlen);
+ cs->cs_hlen = hlen;
+ slstat->sls_uncompressedin++;
+ return (len);
+
+ default:
+ goto bad;
+
+ case TYPE_COMPRESSED_TCP:
+ break;
+ }
+
+ /* We've got a compressed packet. */
+ slstat->sls_compressedin++;
+ cp = *bufp;
+ changes = *cp++;
+ log_Printf(LogDEBUG, "compressed: changes = %02x\n", changes);
+
+ if (changes & NEW_C) {
+ /*
+ * Make sure the state index is in range, then grab the state. If we have
+ * a good state index, clear the 'discard' flag.
+ */
+ if (*cp > max_state || comp->last_recv == 255)
+ goto bad;
+
+ comp->flags &= ~SLF_TOSS;
+ comp->last_recv = *cp++;
+ } else {
+ /*
+ * this packet has an implicit state index. If we've had a line error
+ * since the last time we got an explicit state index, we have to toss
+ * the packet.
+ */
+ if (comp->flags & SLF_TOSS) {
+ slstat->sls_tossed++;
+ return (0);
+ }
+ }
+ cs = &comp->rstate[comp->last_recv];
+ hlen = cs->cs_ip.ip_hl << 2;
+ th = (struct tcphdr *) & ((u_char *) & cs->cs_ip)[hlen];
+ th->th_sum = htons((*cp << 8) | cp[1]);
+ cp += 2;
+ if (changes & TCP_PUSH_BIT)
+ th->th_flags |= TH_PUSH;
+ else
+ th->th_flags &= ~TH_PUSH;
+
+ switch (changes & SPECIALS_MASK) {
+ case SPECIAL_I:
+ {
+ register u_int i = ntohs(cs->cs_ip.ip_len) - cs->cs_hlen;
+
+ th->th_ack = htonl(ntohl(th->th_ack) + i);
+ th->th_seq = htonl(ntohl(th->th_seq) + i);
+ }
+ break;
+
+ case SPECIAL_D:
+ th->th_seq = htonl(ntohl(th->th_seq) + ntohs(cs->cs_ip.ip_len)
+ - cs->cs_hlen);
+ break;
+
+ default:
+ if (changes & NEW_U) {
+ th->th_flags |= TH_URG;
+ DECODEU(th->th_urp)
+ } else
+ th->th_flags &= ~TH_URG;
+ if (changes & NEW_W)
+ DECODES(th->th_win)
+ if (changes & NEW_A)
+ DECODEL(th->th_ack)
+ if (changes & NEW_S) {
+ log_Printf(LogDEBUG, "NEW_S: %02x, %02x, %02x\n",
+ *cp, cp[1], cp[2]);
+ DECODEL(th->th_seq)
+ }
+ break;
+ }
+ if (changes & NEW_I) {
+ DECODES(cs->cs_ip.ip_id)
+ } else
+ cs->cs_ip.ip_id = htons(ntohs(cs->cs_ip.ip_id) + 1);
+
+ log_Printf(LogDEBUG, "Uncompress: id = %04x, seq = %08lx\n",
+ cs->cs_ip.ip_id, (u_long)ntohl(th->th_seq));
+
+ /*
+ * At this point, cp points to the first byte of data in the packet.
+ * Back up cp by the tcp/ip header length to make room for the
+ * reconstructed header (we assume the packet we were handed has enough
+ * space to prepend 128 bytes of header). Adjust the length to account
+ * for the new header & fill in the IP total length.
+ */
+ len -= (cp - *bufp);
+ if (len < 0)
+ /*
+ * we must have dropped some characters (crc should detect this but the
+ * old slip framing won't)
+ */
+ goto bad;
+
+ *bufp = cp - cs->cs_hlen;
+ len += cs->cs_hlen;
+ cs->cs_ip.ip_len = htons(len);
+
+ /* recompute the ip header checksum */
+ cs->cs_ip.ip_sum = 0;
+ bp = (u_short *)&cs->cs_ip;
+ for (changes = 0; hlen > 0; hlen -= 2)
+ changes += *bp++;
+ changes = (changes & 0xffff) + (changes >> 16);
+ changes = (changes & 0xffff) + (changes >> 16);
+ cs->cs_ip.ip_sum = ~changes;
+
+ /* And copy the result into our buffer */
+ memcpy(*bufp, &cs->cs_ip, cs->cs_hlen);
+
+ return (len);
+bad:
+ comp->flags |= SLF_TOSS;
+ slstat->sls_errorin++;
+ return (0);
+}
+
+int
+sl_Show(struct cmdargs const *arg)
+{
+ prompt_Printf(arg->prompt, "VJ compression statistics:\n");
+ prompt_Printf(arg->prompt, " Out: %d (compress) / %d (total)",
+ arg->bundle->ncp.ipcp.vj.slstat.sls_compressed,
+ arg->bundle->ncp.ipcp.vj.slstat.sls_packets);
+ prompt_Printf(arg->prompt, " %d (miss) / %d (search)\n",
+ arg->bundle->ncp.ipcp.vj.slstat.sls_misses,
+ arg->bundle->ncp.ipcp.vj.slstat.sls_searches);
+ prompt_Printf(arg->prompt, " In: %d (compress), %d (uncompress)",
+ arg->bundle->ncp.ipcp.vj.slstat.sls_compressedin,
+ arg->bundle->ncp.ipcp.vj.slstat.sls_uncompressedin);
+ prompt_Printf(arg->prompt, " %d (error), %d (tossed)\n",
+ arg->bundle->ncp.ipcp.vj.slstat.sls_errorin,
+ arg->bundle->ncp.ipcp.vj.slstat.sls_tossed);
+ return 0;
+}
diff --git a/usr.sbin/ppp/slcompress.h b/usr.sbin/ppp/slcompress.h
new file mode 100644
index 0000000..223f659
--- /dev/null
+++ b/usr.sbin/ppp/slcompress.h
@@ -0,0 +1,149 @@
+/*
+ * Definitions for tcp compression routines.
+ *
+ * Copyright (c) 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $FreeBSD$
+ *
+ * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
+ * - Initial distribution.
+ */
+
+#define MIN_VJ_STATES 3
+#define MAX_VJ_STATES 255
+#define DEF_VJ_STATES 16 /* must be > 2 and < 256 */
+#define MAX_HDR 128
+
+/*
+ * Compressed packet format:
+ *
+ * The first octet contains the packet type (top 3 bits), TCP
+ * 'push' bit, and flags that indicate which of the 4 TCP sequence
+ * numbers have changed (bottom 5 bits). The next octet is a
+ * conversation number that associates a saved IP/TCP header with
+ * the compressed packet. The next two octets are the TCP checksum
+ * from the original datagram. The next 0 to 15 octets are
+ * sequence number changes, one change per bit set in the header
+ * (there may be no changes and there are two special cases where
+ * the receiver implicitly knows what changed -- see below).
+ *
+ * There are 5 numbers which can change (they are always inserted
+ * in the following order): TCP urgent pointer, window,
+ * acknowlegement, sequence number and IP ID. (The urgent pointer
+ * is different from the others in that its value is sent, not the
+ * change in value.) Since typical use of SLIP links is biased
+ * toward small packets (see comments on MTU/MSS below), changes
+ * use a variable length coding with one octet for numbers in the
+ * range 1 - 255 and 3 octets (0, MSB, LSB) for numbers in the
+ * range 256 - 65535 or 0. (If the change in sequence number or
+ * ack is more than 65535, an uncompressed packet is sent.)
+ */
+
+/*
+ * Packet types (must not conflict with IP protocol version)
+ *
+ * The top nibble of the first octet is the packet type. There are
+ * three possible types: IP (not proto TCP or tcp with one of the
+ * control flags set); uncompressed TCP (a normal IP/TCP packet but
+ * with the 8-bit protocol field replaced by an 8-bit connection id --
+ * this type of packet syncs the sender & receiver); and compressed
+ * TCP (described above).
+ *
+ * LSB of 4-bit field is TCP "PUSH" bit (a worthless anachronism) and
+ * is logically part of the 4-bit "changes" field that follows. Top
+ * three bits are actual packet type. For backward compatibility
+ * and in the interest of conserving bits, numbers are chosen so the
+ * IP protocol version number (4) which normally appears in this nibble
+ * means "IP packet".
+ */
+
+/* packet types */
+#define TYPE_IP 0x40
+#define TYPE_UNCOMPRESSED_TCP 0x70
+#define TYPE_COMPRESSED_TCP 0x80
+#define TYPE_ERROR 0x00
+
+/* Bits in first octet of compressed packet */
+#define NEW_C 0x40 /* flag bits for what changed in a packet */
+#define NEW_I 0x20
+#define NEW_S 0x08
+#define NEW_A 0x04
+#define NEW_W 0x02
+#define NEW_U 0x01
+
+/* reserved, special-case values of above */
+#define SPECIAL_I (NEW_S|NEW_W|NEW_U) /* echoed interactive traffic */
+#define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U) /* unidirectional data */
+#define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U)
+
+#define TCP_PUSH_BIT 0x10
+
+/*
+ * "state" data for each active tcp conversation on the wire. This is
+ * basically a copy of the entire IP/TCP header from the last packet
+ * we saw from the conversation together with a small identifier
+ * the transmit & receive ends of the line use to locate saved header.
+ */
+struct cstate {
+ struct cstate *cs_next; /* next most recently used cstate (xmit only) */
+ u_short cs_hlen; /* size of hdr (receive only) */
+ u_char cs_id; /* connection # associated with this state */
+ u_char cs_filler;
+ union {
+ char csu_hdr[MAX_HDR];
+ struct ip csu_ip; /* ip/tcp hdr from most recent packet */
+ } slcs_u;
+};
+
+#define cs_ip slcs_u.csu_ip
+#define cs_hdr slcs_u.csu_hdr
+
+/*
+ * all the state data for one serial line (we need one of these
+ * per line).
+ */
+struct slcompress {
+ struct cstate *last_cs; /* most recently used tstate */
+ u_char last_recv; /* last rcvd conn. id */
+ u_char last_xmit; /* last sent conn. id */
+ u_short flags;
+ struct cstate tstate[MAX_VJ_STATES]; /* xmit connection states */
+ struct cstate rstate[MAX_VJ_STATES]; /* receive connection states */
+};
+
+struct slstat {
+ int sls_packets; /* outbound packets */
+ int sls_compressed; /* outbound compressed packets */
+ int sls_searches; /* searches for connection state */
+ int sls_misses; /* times couldn't find conn. state */
+ int sls_uncompressedin; /* inbound uncompressed packets */
+ int sls_compressedin; /* inbound compressed packets */
+ int sls_errorin; /* inbound unknown type packets */
+ int sls_tossed; /* inbound packets tossed because of error */
+};
+
+/* flag values */
+#define SLF_TOSS 1 /* tossing rcvd frames because of input err */
+
+struct mbuf;
+struct cmdargs;
+
+extern void sl_compress_init(struct slcompress *, int);
+extern u_char sl_compress_tcp(struct mbuf *, struct ip *, struct slcompress *,
+ struct slstat *, int);
+extern int sl_uncompress_tcp(u_char **, int, u_int, struct slcompress *,
+ struct slstat *, int);
+extern int sl_Show(struct cmdargs const *);
diff --git a/usr.sbin/ppp/sync.c b/usr.sbin/ppp/sync.c
new file mode 100644
index 0000000..9fd782a
--- /dev/null
+++ b/usr.sbin/ppp/sync.c
@@ -0,0 +1,80 @@
+/*-
+ * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <termios.h>
+
+#include "layer.h"
+#include "defs.h"
+#include "mbuf.h"
+#include "log.h"
+#include "sync.h"
+#include "timer.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "throughput.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "async.h"
+#include "descriptor.h"
+#include "physical.h"
+
+static struct mbuf *
+sync_LayerPush(struct bundle *bundle, struct link *l, struct mbuf *bp,
+ int pri, u_short *proto)
+{
+ log_DumpBp(LogSYNC, "Write", bp);
+ m_settype(bp, MB_SYNCOUT);
+ return bp;
+}
+
+static struct mbuf *
+sync_LayerPull(struct bundle *b, struct link *l, struct mbuf *bp,
+ u_short *proto)
+{
+ struct physical *p = link2physical(l);
+
+ if (!p)
+ log_Printf(LogERROR, "Can't Pull a sync packet from a logical link\n");
+ else {
+ log_DumpBp(LogSYNC, "Read", bp);
+
+ /* Either done here or by the HDLC layer */
+ p->hdlc.lqm.SaveInOctets += m_length(bp) + 1;
+ p->hdlc.lqm.SaveInPackets++;
+ m_settype(bp, MB_SYNCIN);
+ }
+
+ return bp;
+}
+
+struct layer synclayer = { LAYER_SYNC, "sync", sync_LayerPush, sync_LayerPull };
diff --git a/usr.sbin/ppp/sync.h b/usr.sbin/ppp/sync.h
new file mode 100644
index 0000000..4f5bfee
--- /dev/null
+++ b/usr.sbin/ppp/sync.h
@@ -0,0 +1,29 @@
+/*-
+ * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+extern struct layer synclayer;
diff --git a/usr.sbin/ppp/systems.c b/usr.sbin/ppp/systems.c
new file mode 100644
index 0000000..24f58a5
--- /dev/null
+++ b/usr.sbin/ppp/systems.c
@@ -0,0 +1,482 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+
+#include <ctype.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "defs.h"
+#include "command.h"
+#include "log.h"
+#include "id.h"
+#include "systems.h"
+
+#define issep(ch) ((ch) == ' ' || (ch) == '\t')
+
+FILE *
+OpenSecret(const char *file)
+{
+ FILE *fp;
+ char line[100];
+
+ snprintf(line, sizeof line, "%s/%s", PPP_CONFDIR, file);
+ fp = ID0fopen(line, "r");
+ if (fp == NULL)
+ log_Printf(LogWARN, "OpenSecret: Can't open %s.\n", line);
+ return (fp);
+}
+
+void
+CloseSecret(FILE *fp)
+{
+ fclose(fp);
+}
+
+/* Move string from ``from'' to ``to'', interpreting ``~'' and $.... */
+const char *
+InterpretArg(const char *from, char *to)
+{
+ char *ptr, *startto, *endto;
+ struct passwd *pwd;
+ int len, instring;
+ const char *env;
+
+ instring = 0;
+ startto = to;
+ endto = to + LINE_LEN - 1;
+
+ while(issep(*from))
+ from++;
+
+ while (*from != '\0') {
+ switch (*from) {
+ case '"':
+ instring = !instring;
+ *to++ = *from++;
+ break;
+ case '\\':
+ switch (*++from) {
+ case '$':
+ case '~':
+ break; /* Swallow the escapes */
+
+ default:
+ *to++ = '\\'; /* Pass the escapes on, maybe skipping \# */
+ break;
+ }
+ *to++ = *from++;
+ break;
+ case '$':
+ if (from[1] == '$') {
+ *to = '\0'; /* For an empty var name below */
+ from += 2;
+ } else if (from[1] == '{') {
+ ptr = strchr(from+2, '}');
+ if (ptr) {
+ len = ptr - from - 2;
+ if (endto - to < len )
+ len = endto - to;
+ if (len) {
+ strncpy(to, from+2, len);
+ to[len] = '\0';
+ from = ptr+1;
+ } else {
+ *to++ = *from++;
+ continue;
+ }
+ } else {
+ *to++ = *from++;
+ continue;
+ }
+ } else {
+ ptr = to;
+ for (from++; (isalnum(*from) || *from == '_') && ptr < endto; from++)
+ *ptr++ = *from;
+ *ptr = '\0';
+ }
+ if (*to == '\0')
+ *to++ = '$';
+ else if ((env = getenv(to)) != NULL) {
+ strncpy(to, env, endto - to);
+ *endto = '\0';
+ to += strlen(to);
+ }
+ break;
+
+ case '~':
+ ptr = strchr(++from, '/');
+ len = ptr ? ptr - from : strlen(from);
+ if (len == 0)
+ pwd = getpwuid(ID0realuid());
+ else {
+ strncpy(to, from, len);
+ to[len] = '\0';
+ pwd = getpwnam(to);
+ }
+ if (pwd == NULL)
+ *to++ = '~';
+ else {
+ strncpy(to, pwd->pw_dir, endto - to);
+ *endto = '\0';
+ to += strlen(to);
+ from += len;
+ }
+ endpwent();
+ break;
+
+ default:
+ *to++ = *from++;
+ break;
+ }
+ }
+
+ while (to > startto) {
+ to--;
+ if (!issep(*to)) {
+ to++;
+ break;
+ }
+ }
+ *to = '\0';
+
+ return from;
+}
+
+#define CTRL_UNKNOWN (0)
+#define CTRL_INCLUDE (1)
+
+static int
+DecodeCtrlCommand(char *line, char *arg)
+{
+ const char *end;
+
+ if (!strncasecmp(line, "include", 7) && issep(line[7])) {
+ end = InterpretArg(line+8, arg);
+ if (*end && *end != '#')
+ log_Printf(LogWARN, "usage: !include filename\n");
+ else
+ return CTRL_INCLUDE;
+ }
+ return CTRL_UNKNOWN;
+}
+
+/*
+ * Initialised in system_IsValid(), set in ReadSystem(),
+ * used by system_IsValid()
+ */
+static int modeok;
+static int userok;
+static int modereq;
+
+int
+AllowUsers(struct cmdargs const *arg)
+{
+ /* arg->bundle may be NULL (see system_IsValid()) ! */
+ int f;
+ struct passwd *pwd;
+
+ if (userok == -1)
+ userok = 0;
+
+ pwd = getpwuid(ID0realuid());
+ if (pwd != NULL)
+ for (f = arg->argn; f < arg->argc; f++)
+ if (!strcmp("*", arg->argv[f]) || !strcmp(pwd->pw_name, arg->argv[f])) {
+ userok = 1;
+ break;
+ }
+ endpwent();
+
+ return 0;
+}
+
+int
+AllowModes(struct cmdargs const *arg)
+{
+ /* arg->bundle may be NULL (see system_IsValid()) ! */
+ int f, mode, allowed;
+
+ allowed = 0;
+ for (f = arg->argn; f < arg->argc; f++) {
+ mode = Nam2mode(arg->argv[f]);
+ if (mode == PHYS_NONE || mode == PHYS_ALL)
+ log_Printf(LogWARN, "allow modes: %s: Invalid mode\n", arg->argv[f]);
+ else
+ allowed |= mode;
+ }
+
+ modeok = modereq & allowed ? 1 : 0;
+ return 0;
+}
+
+static char *
+strip(char *line)
+{
+ int len;
+
+ len = strlen(line);
+ while (len && (line[len-1] == '\n' || line[len-1] == '\r' ||
+ issep(line[len-1])))
+ line[--len] = '\0';
+
+ while (issep(*line))
+ line++;
+
+ if (*line == '#')
+ *line = '\0';
+
+ return line;
+}
+
+static int
+xgets(char *buf, int buflen, FILE *fp)
+{
+ int len, n;
+
+ n = 0;
+ while (fgets(buf, buflen-1, fp)) {
+ n++;
+ buf[buflen-1] = '\0';
+ len = strlen(buf);
+ while (len && (buf[len-1] == '\n' || buf[len-1] == '\r'))
+ buf[--len] = '\0';
+ if (len && buf[len-1] == '\\') {
+ buf += len - 1;
+ buflen -= len - 1;
+ if (!buflen) /* No buffer space */
+ break;
+ } else
+ break;
+ }
+ return n;
+}
+
+/* Values for ``how'' in ReadSystem */
+#define SYSTEM_EXISTS 1
+#define SYSTEM_VALIDATE 2
+#define SYSTEM_EXEC 3
+
+static char *
+GetLabel(char *line, const char *filename, int linenum)
+{
+ char *argv[MAXARGS];
+ int argc, len;
+
+ argc = MakeArgs(line, argv, MAXARGS, PARSE_REDUCE);
+
+ if (argc == 2 && !strcmp(argv[1], ":"))
+ return argv[0];
+
+ if (argc != 1 || (len = strlen(argv[0])) < 2 || argv[0][len-1] != ':') {
+ log_Printf(LogWARN, "Bad label in %s (line %d) - missing colon\n",
+ filename, linenum);
+ return NULL;
+ }
+ argv[0][len-1] = '\0'; /* Lose the ':' */
+
+ return argv[0];
+}
+
+/* Returns -2 for ``file not found'' and -1 for ``label not found'' */
+
+static int
+ReadSystem(struct bundle *bundle, const char *name, const char *file,
+ struct prompt *prompt, struct datalink *cx, int how)
+{
+ FILE *fp;
+ char *cp;
+ int n, len;
+ char line[LINE_LEN];
+ char filename[PATH_MAX];
+ int linenum;
+ int argc;
+ char *argv[MAXARGS];
+ int allowcmd;
+ int indent;
+ char arg[LINE_LEN];
+ struct prompt *op;
+
+ if (*file == '/')
+ snprintf(filename, sizeof filename, "%s", file);
+ else
+ snprintf(filename, sizeof filename, "%s/%s", PPP_CONFDIR, file);
+ fp = ID0fopen(filename, "r");
+ if (fp == NULL) {
+ log_Printf(LogDEBUG, "ReadSystem: Can't open %s.\n", filename);
+ return -2;
+ }
+ log_Printf(LogDEBUG, "ReadSystem: Checking %s (%s).\n", name, filename);
+
+ linenum = 0;
+ while ((n = xgets(line, sizeof line, fp))) {
+ linenum += n;
+ if (issep(*line))
+ continue;
+
+ cp = strip(line);
+
+ switch (*cp) {
+ case '\0': /* empty/comment */
+ break;
+
+ case '!':
+ switch (DecodeCtrlCommand(cp+1, arg)) {
+ case CTRL_INCLUDE:
+ log_Printf(LogCOMMAND, "%s: Including \"%s\"\n", filename, arg);
+ n = ReadSystem(bundle, name, arg, prompt, cx, how);
+ log_Printf(LogCOMMAND, "%s: Done include of \"%s\"\n", filename, arg);
+ if (!n) {
+ fclose(fp);
+ return 0; /* got it */
+ }
+ break;
+ default:
+ log_Printf(LogWARN, "%s: %s: Invalid command\n", filename, cp);
+ break;
+ }
+ break;
+
+ default:
+ if ((cp = GetLabel(cp, filename, linenum)) == NULL)
+ continue;
+
+ if (strcmp(cp, name) == 0) {
+ /* We're in business */
+ if (how == SYSTEM_EXISTS) {
+ fclose(fp);
+ return 0;
+ }
+ while ((n = xgets(line, sizeof line, fp))) {
+ linenum += n;
+ indent = issep(*line);
+ cp = strip(line);
+
+ if (*cp == '\0') /* empty / comment */
+ continue;
+
+ if (!indent) { /* start of next section */
+ if (*cp != '!' && how == SYSTEM_EXEC)
+ cp = GetLabel(cp, filename, linenum);
+ break;
+ }
+
+ len = strlen(cp);
+ if ((argc = command_Expand_Interpret(cp, len, argv, cp - line)) < 0)
+ log_Printf(LogWARN, "%s: %d: Syntax error\n", filename, linenum);
+ else {
+ allowcmd = argc > 0 && !strcasecmp(argv[0], "allow");
+ if ((how != SYSTEM_EXEC && allowcmd) ||
+ (how == SYSTEM_EXEC && !allowcmd)) {
+ /*
+ * Disable any context so that warnings are given to everyone,
+ * including syslog.
+ */
+ op = log_PromptContext;
+ log_PromptContext = NULL;
+ command_Run(bundle, argc, (char const *const *)argv, prompt,
+ name, cx);
+ log_PromptContext = op;
+ }
+ }
+ }
+
+ fclose(fp); /* everything read - get out */
+ return 0;
+ }
+ break;
+ }
+ }
+ fclose(fp);
+ return -1;
+}
+
+const char *
+system_IsValid(const char *name, struct prompt *prompt, int mode)
+{
+ /*
+ * Note: The ReadSystem() calls only result in calls to the Allow*
+ * functions. arg->bundle will be set to NULL for these commands !
+ */
+ int def, how, rs;
+ int defuserok;
+
+ def = !strcmp(name, "default");
+ how = ID0realuid() == 0 ? SYSTEM_EXISTS : SYSTEM_VALIDATE;
+ userok = -1;
+ modeok = 1;
+ modereq = mode;
+
+ rs = ReadSystem(NULL, "default", CONFFILE, prompt, NULL, how);
+
+ defuserok = userok;
+ userok = -1;
+
+ if (!def) {
+ if (rs == -1)
+ rs = 0; /* we don't care that ``default'' doesn't exist */
+
+ if (rs == 0)
+ rs = ReadSystem(NULL, name, CONFFILE, prompt, NULL, how);
+
+ if (rs == -1)
+ return "Configuration label not found";
+
+ if (rs == -2)
+ return PPP_CONFDIR "/" CONFFILE " : File not found";
+ }
+
+ if (userok == -1)
+ userok = defuserok;
+
+ if (how == SYSTEM_EXISTS)
+ userok = modeok = 1;
+
+ if (!userok)
+ return "User access denied";
+
+ if (!modeok)
+ return "Mode denied for this label";
+
+ return NULL;
+}
+
+int
+system_Select(struct bundle *bundle, const char *name, const char *file,
+ struct prompt *prompt, struct datalink *cx)
+{
+ userok = modeok = 1;
+ modereq = PHYS_ALL;
+ return ReadSystem(bundle, name, file, prompt, cx, SYSTEM_EXEC);
+}
diff --git a/usr.sbin/ppp/systems.h b/usr.sbin/ppp/systems.h
new file mode 100644
index 0000000..9091e73
--- /dev/null
+++ b/usr.sbin/ppp/systems.h
@@ -0,0 +1,43 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct prompt;
+struct datalink;
+struct bundle;
+struct cmdargs;
+
+extern int system_Select(struct bundle *bundle, const char *, const char *,
+ struct prompt *, struct datalink *);
+extern const char *system_IsValid(const char *, struct prompt *, int);
+extern FILE *OpenSecret(const char *);
+extern void CloseSecret(FILE *);
+extern int AllowUsers(struct cmdargs const *);
+extern int AllowModes(struct cmdargs const *);
+extern const char *InterpretArg(const char *, char *);
diff --git a/usr.sbin/ppp/tcp.c b/usr.sbin/ppp/tcp.c
new file mode 100644
index 0000000..9a45e98
--- /dev/null
+++ b/usr.sbin/ppp/tcp.c
@@ -0,0 +1,212 @@
+/*-
+ * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "layer.h"
+#include "defs.h"
+#include "mbuf.h"
+#include "log.h"
+#include "timer.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "throughput.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "async.h"
+#include "descriptor.h"
+#include "physical.h"
+#include "tcp.h"
+
+static int
+tcp_OpenConnection(const char *name, char *host, char *port)
+{
+ struct sockaddr_in dest;
+ int sock;
+ struct servent *sp;
+
+ dest.sin_family = AF_INET;
+ dest.sin_addr.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 -2;
+ }
+ dest.sin_port = htons(atoi(port));
+ if (dest.sin_port == 0) {
+ sp = getservbyname(port, "tcp");
+ if (sp)
+ dest.sin_port = sp->s_port;
+ else {
+ log_Printf(LogWARN, "%s: %s: unknown service\n", name, port);
+ return -2;
+ }
+ }
+ log_Printf(LogPHASE, "%s: Connecting to %s:%s/tcp\n", name, host, port);
+
+ sock = socket(PF_INET, SOCK_STREAM, 0);
+ if (sock < 0)
+ return -2;
+
+ if (connect(sock, (struct sockaddr *)&dest, sizeof dest) < 0) {
+ log_Printf(LogWARN, "%s: connect: %s\n", name, strerror(errno));
+ close(sock);
+ return -2;
+ }
+
+ return sock;
+}
+
+static struct device tcpdevice = {
+ TCP_DEVICE,
+ "tcp",
+ 0,
+ { CD_NOTREQUIRED, 0 },
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+struct device *
+tcp_iov2device(int type, struct physical *p, struct iovec *iov,
+ int *niov, int maxiov, int *auxfd, int *nauxfd)
+{
+ if (type == TCP_DEVICE) {
+ free(iov[(*niov)++].iov_base);
+ physical_SetupStack(p, tcpdevice.name, PHYSICAL_FORCE_ASYNC);
+ return &tcpdevice;
+ }
+
+ return NULL;
+}
+
+struct device *
+tcp_Create(struct physical *p)
+{
+ char *cp, *host, *port, *svc;
+
+ if (p->fd < 0) {
+ if ((cp = strchr(p->name.full, ':')) != NULL && !strchr(cp + 1, ':')) {
+ *cp = '\0';
+ host = p->name.full;
+ port = cp + 1;
+ svc = strchr(port, '/');
+ if (svc && strcasecmp(svc, "/tcp")) {
+ *cp = ':';
+ return 0;
+ }
+ if (svc) {
+ p->fd--; /* We own the device but maybe can't use it - change fd */
+ *svc = '\0';
+ }
+ if (*host && *port) {
+ p->fd = tcp_OpenConnection(p->link.name, host, port);
+ *cp = ':';
+ if (svc)
+ *svc = '/';
+ if (p->fd >= 0)
+ log_Printf(LogDEBUG, "%s: Opened tcp socket %s\n", p->link.name,
+ p->name.full);
+ } else {
+ if (svc)
+ *svc = '/';
+ *cp = ':';
+ }
+ }
+ }
+
+ if (p->fd >= 0) {
+ /* See if we're a tcp socket */
+ struct stat st;
+
+ if (fstat(p->fd, &st) != -1 && (st.st_mode & S_IFSOCK)) {
+ int type, sz;
+
+ sz = sizeof type;
+ if (getsockopt(p->fd, SOL_SOCKET, SO_TYPE, &type, &sz) == -1) {
+ log_Printf(LogPHASE, "%s: Link is a closed socket !\n", p->link.name);
+ close(p->fd);
+ p->fd = -1;
+ return NULL;
+ }
+
+ if (sz == sizeof type && type == SOCK_STREAM) {
+ struct sockaddr_in sock;
+ struct sockaddr *sockp = (struct sockaddr *)&sock;
+
+ if (*p->name.full == '\0') {
+ sz = sizeof sock;
+ if (getpeername(p->fd, sockp, &sz) != 0 ||
+ sz != sizeof(struct sockaddr_in) || sock.sin_family != AF_INET) {
+ log_Printf(LogDEBUG, "%s: Link is SOCK_STREAM, but not inet\n",
+ p->link.name);
+ return NULL;
+ }
+
+ log_Printf(LogPHASE, "%s: Link is a tcp socket\n", p->link.name);
+
+ snprintf(p->name.full, sizeof p->name.full, "%s:%d/tcp",
+ inet_ntoa(sock.sin_addr), ntohs(sock.sin_port));
+ p->name.base = p->name.full;
+ }
+ physical_SetupStack(p, tcpdevice.name, PHYSICAL_FORCE_ASYNC);
+ if (p->cfg.cd.necessity != CD_DEFAULT)
+ log_Printf(LogWARN, "Carrier settings ignored\n");
+ return &tcpdevice;
+ }
+ }
+ }
+
+ return NULL;
+}
diff --git a/usr.sbin/ppp/tcp.h b/usr.sbin/ppp/tcp.h
new file mode 100644
index 0000000..f6a8235
--- /dev/null
+++ b/usr.sbin/ppp/tcp.h
@@ -0,0 +1,34 @@
+/*-
+ * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct physical;
+
+extern struct device *tcp_Create(struct physical *);
+extern struct device *tcp_iov2device(int, struct physical *,
+ struct iovec *, int *, int, int *, int *);
+#define tcp_DeviceSize physical_DeviceSize
diff --git a/usr.sbin/ppp/tcpmss.c b/usr.sbin/ppp/tcpmss.c
new file mode 100644
index 0000000..1fdbf02
--- /dev/null
+++ b/usr.sbin/ppp/tcpmss.c
@@ -0,0 +1,185 @@
+/*-
+ * Copyright (c) 2000 Ruslan Ermilov and Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+
+#include <sys/socket.h>
+#include <net/route.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <sys/un.h>
+
+#include <termios.h>
+
+#include "layer.h"
+#include "defs.h"
+#include "log.h"
+#include "timer.h"
+#include "fsm.h"
+#include "mbuf.h"
+#include "throughput.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "iplist.h"
+#include "slcompress.h"
+#include "ncpaddr.h"
+#include "ipcp.h"
+#include "filter.h"
+#include "descriptor.h"
+#include "mp.h"
+#include "iface.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+
+
+/*-
+ * We are in a liberal position about MSS
+ * (RFC 879, section 7).
+ */
+#define MAXMSS(mtu) (mtu - sizeof(struct ip) - sizeof(struct tcphdr))
+
+
+/*-
+ * The following macro is used to update an
+ * internet checksum. "acc" is a 32-bit
+ * accumulation of all the changes to the
+ * checksum (adding in old 16-bit words and
+ * subtracting out new words), and "cksum"
+ * is the checksum value to be updated.
+ */
+#define ADJUST_CHECKSUM(acc, cksum) { \
+ acc += cksum; \
+ if (acc < 0) { \
+ acc = -acc; \
+ acc = (acc >> 16) + (acc & 0xffff); \
+ acc += acc >> 16; \
+ cksum = (u_short) ~acc; \
+ } else { \
+ acc = (acc >> 16) + (acc & 0xffff); \
+ acc += acc >> 16; \
+ cksum = (u_short) acc; \
+ } \
+}
+
+static void
+MSSFixup(struct tcphdr *tc, ssize_t pktlen, u_int16_t maxmss)
+{
+ int hlen, olen, optlen;
+ u_char *opt;
+ u_int16_t *mss;
+ int accumulate;
+
+ hlen = tc->th_off << 2;
+
+ /* Invalid header length or header without options. */
+ if (hlen <= sizeof(struct tcphdr) || hlen > pktlen)
+ return;
+
+ /* MSS option only allowed within SYN packets. */
+ if (!(tc->th_flags & TH_SYN))
+ return;
+
+ for (olen = hlen - sizeof(struct tcphdr), opt = (u_char *)(tc + 1);
+ olen > 0; olen -= optlen, opt += optlen) {
+ if (*opt == TCPOPT_EOL)
+ break;
+ else if (*opt == TCPOPT_NOP)
+ optlen = 1;
+ else {
+ optlen = *(opt + 1);
+ if (optlen <= 0 || optlen > olen)
+ break;
+ if (*opt == TCPOPT_MAXSEG) {
+ if (optlen != TCPOLEN_MAXSEG)
+ continue;
+ mss = (u_int16_t *)(opt + 2);
+ if (ntohs(*mss) > maxmss) {
+ log_Printf(LogDEBUG, "MSS: %u -> %u\n",
+ ntohs(*mss), maxmss);
+ accumulate = *mss;
+ *mss = htons(maxmss);
+ accumulate -= *mss;
+ ADJUST_CHECKSUM(accumulate, tc->th_sum);
+ }
+ }
+ }
+ }
+}
+
+static struct mbuf *
+tcpmss_Check(struct bundle *bundle, struct mbuf *bp)
+{
+ struct ip *pip;
+ int hlen, plen;
+
+ if (!Enabled(bundle, OPT_TCPMSSFIXUP))
+ return bp;
+
+ bp = m_pullup(bp);
+ plen = m_length(bp);
+ pip = (struct ip *)MBUF_CTOP(bp);
+ hlen = pip->ip_hl << 2;
+
+ /*
+ * Check for MSS option only for TCP packets with zero fragment offsets
+ * and correct total and header lengths.
+ */
+ if (pip->ip_p == IPPROTO_TCP && (ntohs(pip->ip_off) & IP_OFFMASK) == 0 &&
+ ntohs(pip->ip_len) == plen && hlen <= plen &&
+ plen - hlen >= sizeof(struct tcphdr))
+ MSSFixup((struct tcphdr *)(MBUF_CTOP(bp) + hlen), plen - hlen,
+ MAXMSS(bundle->iface->mtu));
+
+ return bp;
+}
+
+static struct mbuf *
+tcpmss_LayerPush(struct bundle *bundle, struct link *l, struct mbuf *bp,
+ int pri, u_short *proto)
+{
+ return tcpmss_Check(bundle, bp);
+}
+
+static struct mbuf *
+tcpmss_LayerPull(struct bundle *bundle, struct link *l, struct mbuf *bp,
+ u_short *proto)
+{
+ return tcpmss_Check(bundle, bp);
+}
+
+struct layer tcpmsslayer =
+ { LAYER_PROTO, "tcpmss", tcpmss_LayerPush, tcpmss_LayerPull };
diff --git a/usr.sbin/ppp/tcpmss.h b/usr.sbin/ppp/tcpmss.h
new file mode 100644
index 0000000..ffd7b82
--- /dev/null
+++ b/usr.sbin/ppp/tcpmss.h
@@ -0,0 +1,29 @@
+/*-
+ * Copyright (c) 2000 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+extern struct layer tcpmsslayer;
diff --git a/usr.sbin/ppp/throughput.c b/usr.sbin/ppp/throughput.c
new file mode 100644
index 0000000..b0bb241
--- /dev/null
+++ b/usr.sbin/ppp/throughput.c
@@ -0,0 +1,301 @@
+/*-
+ * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <time.h>
+
+#include "log.h"
+#include "timer.h"
+#include "throughput.h"
+#include "descriptor.h"
+#include "prompt.h"
+
+
+void
+throughput_init(struct pppThroughput *t, int period)
+{
+ t->OctetsIn = t->OctetsOut = t->PacketsIn = t->PacketsOut = 0;
+ t->SamplePeriod = period;
+ t->in.SampleOctets = (long long *)
+ calloc(period, sizeof *t->in.SampleOctets);
+ t->in.OctetsPerSecond = 0;
+ t->out.SampleOctets = (long long *)
+ calloc(period, sizeof *t->out.SampleOctets);
+ t->out.OctetsPerSecond = 0;
+ t->BestOctetsPerSecond = 0;
+ t->nSample = 0;
+ time(&t->BestOctetsPerSecondTime);
+ memset(&t->Timer, '\0', sizeof t->Timer);
+ t->Timer.name = "throughput";
+ t->uptime = 0;
+ t->downtime = 0;
+ t->rolling = 0;
+ t->callback.data = NULL;
+ t->callback.fn = NULL;
+ throughput_stop(t);
+}
+
+void
+throughput_destroy(struct pppThroughput *t)
+{
+ if (t && t->in.SampleOctets) {
+ throughput_stop(t);
+ free(t->in.SampleOctets);
+ free(t->out.SampleOctets);
+ t->in.SampleOctets = NULL;
+ t->out.SampleOctets = NULL;
+ }
+}
+
+int
+throughput_uptime(struct pppThroughput *t)
+{
+ time_t downat;
+
+ downat = t->downtime ? t->downtime : time(NULL);
+ if (t->uptime && downat < t->uptime) {
+ /* Euch ! The clock's gone back ! */
+ int i;
+
+ for (i = 0; i < t->SamplePeriod; i++)
+ t->in.SampleOctets[i] = t->out.SampleOctets[i] = 0;
+ t->nSample = 0;
+ t->uptime = downat;
+ }
+ return t->uptime ? downat - t->uptime : 0;
+}
+
+void
+throughput_disp(struct pppThroughput *t, struct prompt *prompt)
+{
+ int secs_up, divisor;
+
+ secs_up = throughput_uptime(t);
+ prompt_Printf(prompt, "Connect time: %d:%02d:%02d", secs_up / 3600,
+ (secs_up / 60) % 60, secs_up % 60);
+ if (t->downtime)
+ prompt_Printf(prompt, " - down at %s", ctime(&t->downtime));
+ else
+ prompt_Printf(prompt, "\n");
+
+ divisor = secs_up ? secs_up : 1;
+ prompt_Printf(prompt, "%llu octets in, %llu octets out\n",
+ t->OctetsIn, t->OctetsOut);
+ prompt_Printf(prompt, "%llu packets in, %llu packets out\n",
+ t->PacketsIn, t->PacketsOut);
+ if (t->rolling) {
+ prompt_Printf(prompt, " overall %6qu bytes/sec\n",
+ (t->OctetsIn + t->OctetsOut) / divisor);
+ prompt_Printf(prompt, " %s %6qu bytes/sec in, %6qu bytes/sec out "
+ "(over the last %d secs)\n",
+ t->downtime ? "average " : "currently",
+ t->in.OctetsPerSecond, t->out.OctetsPerSecond,
+ secs_up > t->SamplePeriod ? t->SamplePeriod : secs_up);
+ prompt_Printf(prompt, " peak %6qu bytes/sec on %s",
+ t->BestOctetsPerSecond, ctime(&t->BestOctetsPerSecondTime));
+ } else
+ prompt_Printf(prompt, "Overall %llu bytes/sec\n",
+ (t->OctetsIn + t->OctetsOut) / divisor);
+}
+
+
+void
+throughput_log(struct pppThroughput *t, int level, const char *title)
+{
+ if (t->uptime) {
+ int secs_up;
+
+ secs_up = throughput_uptime(t);
+ if (title == NULL)
+ title = "";
+ log_Printf(level, "%s%sConnect time: %d secs: %llu octets in, %llu octets"
+ " out\n", title, *title ? ": " : "", secs_up, t->OctetsIn,
+ t->OctetsOut);
+ log_Printf(level, "%s%s%llu packets in, %llu packets out\n",
+ title, *title ? ": " : "", t->PacketsIn, t->PacketsOut);
+ if (secs_up == 0)
+ secs_up = 1;
+ if (t->rolling)
+ log_Printf(level, " total %llu bytes/sec, peak %llu bytes/sec on %s",
+ (t->OctetsIn + t->OctetsOut) / secs_up, t->BestOctetsPerSecond,
+ ctime(&t->BestOctetsPerSecondTime));
+ else
+ log_Printf(level, " total %llu bytes/sec\n",
+ (t->OctetsIn + t->OctetsOut) / secs_up);
+ }
+}
+
+static void
+throughput_sampler(void *v)
+{
+ struct pppThroughput *t = (struct pppThroughput *)v;
+ unsigned long long old;
+ int uptime, divisor;
+ unsigned long long octets;
+
+ timer_Stop(&t->Timer);
+
+ uptime = throughput_uptime(t);
+ divisor = uptime < t->SamplePeriod ? uptime + 1 : t->SamplePeriod;
+
+ old = t->in.SampleOctets[t->nSample];
+ t->in.SampleOctets[t->nSample] = t->OctetsIn;
+ t->in.OctetsPerSecond = (t->in.SampleOctets[t->nSample] - old) / divisor;
+
+ old = t->out.SampleOctets[t->nSample];
+ t->out.SampleOctets[t->nSample] = t->OctetsOut;
+ t->out.OctetsPerSecond = (t->out.SampleOctets[t->nSample] - old) / divisor;
+
+ octets = t->in.OctetsPerSecond + t->out.OctetsPerSecond;
+ if (t->BestOctetsPerSecond < octets) {
+ t->BestOctetsPerSecond = octets;
+ time(&t->BestOctetsPerSecondTime);
+ }
+
+ if (++t->nSample == t->SamplePeriod)
+ t->nSample = 0;
+
+ if (t->callback.fn != NULL && uptime >= t->SamplePeriod)
+ (*t->callback.fn)(t->callback.data);
+
+ timer_Start(&t->Timer);
+}
+
+void
+throughput_start(struct pppThroughput *t, const char *name, int rolling)
+{
+ int i;
+ timer_Stop(&t->Timer);
+
+ for (i = 0; i < t->SamplePeriod; i++)
+ t->in.SampleOctets[i] = t->out.SampleOctets[i] = 0;
+ t->nSample = 0;
+ t->OctetsIn = t->OctetsOut = 0;
+ t->in.OctetsPerSecond = t->out.OctetsPerSecond = t->BestOctetsPerSecond = 0;
+ time(&t->BestOctetsPerSecondTime);
+ t->downtime = 0;
+ time(&t->uptime);
+ throughput_restart(t, name, rolling);
+}
+
+void
+throughput_restart(struct pppThroughput *t, const char *name, int rolling)
+{
+ timer_Stop(&t->Timer);
+ t->rolling = rolling ? 1 : 0;
+ if (t->rolling) {
+ t->Timer.load = SECTICKS;
+ t->Timer.func = throughput_sampler;
+ t->Timer.name = name;
+ t->Timer.arg = t;
+ timer_Start(&t->Timer);
+ } else {
+ t->Timer.load = 0;
+ t->Timer.func = NULL;
+ t->Timer.name = NULL;
+ t->Timer.arg = NULL;
+ }
+}
+
+void
+throughput_stop(struct pppThroughput *t)
+{
+ if (t->Timer.state != TIMER_STOPPED)
+ time(&t->downtime);
+ timer_Stop(&t->Timer);
+}
+
+void
+throughput_addin(struct pppThroughput *t, long long n)
+{
+ t->OctetsIn += n;
+ t->PacketsIn++;
+}
+
+void
+throughput_addout(struct pppThroughput *t, long long n)
+{
+ t->OctetsOut += n;
+ t->PacketsOut++;
+}
+
+void
+throughput_clear(struct pppThroughput *t, int clear_type, struct prompt *prompt)
+{
+ if (clear_type & (THROUGHPUT_OVERALL|THROUGHPUT_CURRENT)) {
+ int i;
+
+ for (i = 0; i < t->SamplePeriod; i++)
+ t->in.SampleOctets[i] = t->out.SampleOctets[i] = 0;
+ t->nSample = 0;
+ }
+
+ if (clear_type & THROUGHPUT_OVERALL) {
+ int divisor;
+
+ if ((divisor = throughput_uptime(t)) == 0)
+ divisor = 1;
+ prompt_Printf(prompt, "overall cleared (was %6qu bytes/sec)\n",
+ (t->OctetsIn + t->OctetsOut) / divisor);
+ t->OctetsIn = t->OctetsOut = 0;
+ t->downtime = 0;
+ time(&t->uptime);
+ }
+
+ if (clear_type & THROUGHPUT_CURRENT) {
+ prompt_Printf(prompt, "current cleared (was %6qu bytes/sec in,"
+ " %6qu bytes/sec out)\n",
+ t->in.OctetsPerSecond, t->out.OctetsPerSecond);
+ t->in.OctetsPerSecond = t->out.OctetsPerSecond = 0;
+ }
+
+ if (clear_type & THROUGHPUT_PEAK) {
+ char *time_buf, *last;
+
+ time_buf = ctime(&t->BestOctetsPerSecondTime);
+ last = time_buf + strlen(time_buf);
+ if (last > time_buf && *--last == '\n')
+ *last = '\0';
+ prompt_Printf(prompt, "peak cleared (was %6qu bytes/sec on %s)\n",
+ t->BestOctetsPerSecond, time_buf);
+ t->BestOctetsPerSecond = 0;
+ time(&t->BestOctetsPerSecondTime);
+ }
+}
+
+void
+throughput_callback(struct pppThroughput *t, void (*fn)(void *), void *data)
+{
+ t->callback.fn = fn;
+ t->callback.data = data;
+}
diff --git a/usr.sbin/ppp/throughput.h b/usr.sbin/ppp/throughput.h
new file mode 100644
index 0000000..21e5450
--- /dev/null
+++ b/usr.sbin/ppp/throughput.h
@@ -0,0 +1,70 @@
+/*-
+ * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define SAMPLE_PERIOD 5 /* Default sample period */
+
+#define THROUGHPUT_OVERALL 0x0001
+#define THROUGHPUT_CURRENT 0x0002
+#define THROUGHPUT_PEAK 0x0004
+#define THROUGHPUT_ALL 0x0007
+
+struct pppThroughput {
+ time_t uptime, downtime;
+ unsigned long long OctetsIn;
+ unsigned long long OctetsOut;
+ unsigned long long PacketsIn;
+ unsigned long long PacketsOut;
+ int SamplePeriod;
+ struct {
+ unsigned long long *SampleOctets;
+ unsigned long long OctetsPerSecond;
+ } in, out;
+ unsigned long long BestOctetsPerSecond;
+ time_t BestOctetsPerSecondTime;
+ int nSample;
+ unsigned rolling : 1;
+ struct pppTimer Timer;
+ struct {
+ void *data;
+ void (*fn)(void *v);
+ } callback;
+};
+
+extern void throughput_init(struct pppThroughput *, int);
+extern void throughput_destroy(struct pppThroughput *);
+extern void throughput_disp(struct pppThroughput *, struct prompt *);
+extern void throughput_log(struct pppThroughput *, int, const char *);
+extern void throughput_start(struct pppThroughput *, const char *, int);
+extern void throughput_restart(struct pppThroughput *, const char *, int);
+extern void throughput_stop(struct pppThroughput *);
+extern void throughput_addin(struct pppThroughput *, long long);
+extern void throughput_addout(struct pppThroughput *, long long);
+extern void throughput_clear(struct pppThroughput *, int, struct prompt *);
+extern void throughput_callback(struct pppThroughput *, void (*)(void *),
+ void *);
+extern int throughput_uptime(struct pppThroughput *);
diff --git a/usr.sbin/ppp/timer.c b/usr.sbin/ppp/timer.c
new file mode 100644
index 0000000..9c4fc9f
--- /dev/null
+++ b/usr.sbin/ppp/timer.c
@@ -0,0 +1,292 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/time.h>
+#include <termios.h>
+
+#include "log.h"
+#include "sig.h"
+#include "timer.h"
+#include "descriptor.h"
+#include "prompt.h"
+
+
+#define RESTVAL(t) \
+ ((t).it_value.tv_sec * SECTICKS + (t).it_value.tv_usec / TICKUNIT + \
+ ((((t).it_value.tv_usec % TICKUNIT) >= (TICKUNIT >> 1)) ? 1 : 0))
+
+static struct pppTimer *TimerList = NULL, *ExpiredList = NULL;
+
+static void StopTimerNoBlock(struct pppTimer *);
+
+static const char *
+tState2Nam(u_int state)
+{
+ static const char * const StateNames[] = { "stopped", "running", "expired" };
+
+ if (state >= sizeof StateNames / sizeof StateNames[0])
+ return "unknown";
+ return StateNames[state];
+}
+
+void
+timer_Stop(struct pppTimer *tp)
+{
+ sigset_t mask, omask;
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGALRM);
+ sigprocmask(SIG_BLOCK, &mask, &omask);
+ StopTimerNoBlock(tp);
+ sigprocmask(SIG_SETMASK, &omask, NULL);
+}
+
+void
+timer_Start(struct pppTimer *tp)
+{
+ struct itimerval itimer;
+ struct pppTimer *t, *pt;
+ u_long ticks = 0;
+ sigset_t mask, omask;
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGALRM);
+ sigprocmask(SIG_BLOCK, &mask, &omask);
+
+ if (tp->state != TIMER_STOPPED)
+ StopTimerNoBlock(tp);
+
+ if (tp->load == 0) {
+ log_Printf(LogTIMER, "%s timer[%p] has 0 load!\n", tp->name, tp);
+ sigprocmask(SIG_SETMASK, &omask, NULL);
+ return;
+ }
+
+ /* Adjust our first delta so that it reflects what's really happening */
+ if (TimerList && getitimer(ITIMER_REAL, &itimer) == 0)
+ TimerList->rest = RESTVAL(itimer);
+
+ pt = NULL;
+ for (t = TimerList; t; t = t->next) {
+ if (ticks + t->rest >= tp->load)
+ break;
+ ticks += t->rest;
+ pt = t;
+ }
+
+ tp->state = TIMER_RUNNING;
+ tp->rest = tp->load - ticks;
+
+ if (t)
+ log_Printf(LogTIMER, "timer_Start: Inserting %s timer[%p] before %s "
+ "timer[%p], delta = %ld\n", tp->name, tp, t->name, t, tp->rest);
+ else
+ log_Printf(LogTIMER, "timer_Start: Inserting %s timer[%p]\n", tp->name, tp);
+
+ /* Insert given *tp just before *t */
+ tp->next = t;
+ if (pt) {
+ pt->next = tp;
+ } else {
+ TimerList = tp;
+ timer_InitService(t != NULL); /* [re]Start the Timer Service */
+ }
+ if (t)
+ t->rest -= tp->rest;
+
+ sigprocmask(SIG_SETMASK, &omask, NULL);
+}
+
+static void
+StopTimerNoBlock(struct pppTimer *tp)
+{
+ struct pppTimer *t, *pt;
+
+ /*
+ * A RUNNING timer must be removed from TimerList (->next list).
+ * A STOPPED timer isn't in any list, but may have a bogus [e]next field.
+ * An EXPIRED timer is in the ->enext list.
+ */
+
+ if (tp->state == TIMER_STOPPED)
+ return;
+
+ pt = NULL;
+ for (t = TimerList; t != tp && t != NULL; t = t->next)
+ pt = t;
+
+ if (t) {
+ if (pt)
+ pt->next = t->next;
+ else {
+ TimerList = t->next;
+ if (TimerList == NULL) /* Last one ? */
+ timer_TermService(); /* Terminate Timer Service */
+ }
+ if (t->next) {
+ if (!pt) { /* t (tp) was the first in the list */
+ struct itimerval itimer;
+
+ if (getitimer(ITIMER_REAL, &itimer) == 0)
+ t->rest = RESTVAL(itimer);
+ }
+ t->next->rest += t->rest;
+ if (!pt) /* t->next is now the first in the list */
+ timer_InitService(1);
+ }
+ } else {
+ /* Search for any pending expired timers */
+ pt = NULL;
+ for (t = ExpiredList; t != tp && t != NULL; t = t->enext)
+ pt = t;
+
+ if (t) {
+ if (pt)
+ pt->enext = t->enext;
+ else
+ ExpiredList = t->enext;
+ } else if (tp->state == TIMER_RUNNING)
+ log_Printf(LogERROR, "Oops, %s timer not found!!\n", tp->name);
+ }
+
+ tp->next = tp->enext = NULL;
+ tp->state = TIMER_STOPPED;
+}
+
+static void
+TimerService(void)
+{
+ struct pppTimer *tp, *exp, *next;
+
+ if (log_IsKept(LogTIMER)) {
+ static time_t t; /* Only show timers globally every second */
+ time_t n = time(NULL);
+
+ if (n > t)
+ timer_Show(LogTIMER, NULL);
+ t = n;
+ }
+
+ tp = TimerList;
+ if (tp) {
+ tp->rest = 0;
+
+ /* Multiple timers might expire at once. Create a list of expired timers */
+ exp = NULL;
+ do {
+ tp->state = TIMER_EXPIRED;
+ next = tp->next;
+ tp->enext = exp;
+ exp = tp;
+ tp = next;
+ } while (tp && tp->rest == 0);
+
+ TimerList = tp;
+ if (TimerList != NULL) /* Any timers remaining ? */
+ timer_InitService(1); /* Restart the Timer Service */
+ else
+ timer_TermService(); /* Stop the Timer Service */
+
+ /* Process all expired timers */
+ while (exp) {
+ ExpiredList = exp->enext;
+ exp->enext = NULL;
+ if (exp->func)
+ (*exp->func)(exp->arg);
+ exp = ExpiredList;
+ }
+ }
+}
+
+void
+timer_Show(int LogLevel, struct prompt *prompt)
+{
+ struct itimerval itimer;
+ struct pppTimer *pt;
+ u_long rest = 0;
+
+ /* Adjust our first delta so that it reflects what's really happening */
+ if (TimerList && getitimer(ITIMER_REAL, &itimer) == 0)
+ TimerList->rest = RESTVAL(itimer);
+
+#define SECS(val) ((val) / SECTICKS)
+#define HSECS(val) (((val) % SECTICKS) * 100 / SECTICKS)
+#define DISP \
+ "%s timer[%p]: freq = %ld.%02lds, next = %lu.%02lus, state = %s\n", \
+ pt->name, pt, SECS(pt->load), HSECS(pt->load), SECS(rest), \
+ HSECS(rest), tState2Nam(pt->state)
+
+ if (!prompt)
+ log_Printf(LogLevel, "---- Begin of Timer Service List---\n");
+
+ for (pt = TimerList; pt; pt = pt->next) {
+ rest += pt->rest;
+ if (prompt)
+ prompt_Printf(prompt, DISP);
+ else
+ log_Printf(LogLevel, DISP);
+ }
+
+ if (!prompt)
+ log_Printf(LogLevel, "---- End of Timer Service List ---\n");
+}
+
+void
+timer_InitService(int restart)
+{
+ struct itimerval itimer;
+
+ if (TimerList) {
+ if (!restart)
+ sig_signal(SIGALRM, (void (*)(int))TimerService);
+ itimer.it_interval.tv_sec = 0;
+ itimer.it_interval.tv_usec = 0;
+ itimer.it_value.tv_sec = TimerList->rest / SECTICKS;
+ itimer.it_value.tv_usec = (TimerList->rest % SECTICKS) * TICKUNIT;
+ if (setitimer(ITIMER_REAL, &itimer, NULL) == -1)
+ log_Printf(LogERROR, "Unable to set itimer (%s)\n", strerror(errno));
+ }
+}
+
+void
+timer_TermService(void)
+{
+ struct itimerval itimer;
+
+ itimer.it_interval.tv_usec = itimer.it_interval.tv_sec = 0;
+ itimer.it_value.tv_usec = itimer.it_value.tv_sec = 0;
+ if (setitimer(ITIMER_REAL, &itimer, NULL) == -1)
+ log_Printf(LogERROR, "Unable to set itimer (%s)\n", strerror(errno));
+ sig_signal(SIGALRM, SIG_IGN);
+}
diff --git a/usr.sbin/ppp/timer.h b/usr.sbin/ppp/timer.h
new file mode 100644
index 0000000..02fcdd1
--- /dev/null
+++ b/usr.sbin/ppp/timer.h
@@ -0,0 +1,55 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define TICKUNIT 100000 /* usec's per Unit */
+#define SECTICKS (1000000/TICKUNIT) /* Units per second */
+
+struct pppTimer {
+ int state;
+ const char *name;
+ u_long rest; /* Ticks to expire */
+ u_long load; /* Initial load value */
+ void (*func)(void *); /* Function called when timer is expired */
+ void *arg; /* Argument passed to timeout function */
+ struct pppTimer *next; /* Link to next timer */
+ struct pppTimer *enext; /* Link to next expired timer */
+};
+
+#define TIMER_STOPPED 0
+#define TIMER_RUNNING 1
+#define TIMER_EXPIRED 2
+
+struct prompt;
+
+extern void timer_Start(struct pppTimer *);
+extern void timer_Stop(struct pppTimer *);
+extern void timer_InitService(int);
+extern void timer_TermService(void);
+extern void timer_Show(int LogLevel, struct prompt *);
diff --git a/usr.sbin/ppp/tty.c b/usr.sbin/ppp/tty.c
new file mode 100644
index 0000000..b26bfdb
--- /dev/null
+++ b/usr.sbin/ppp/tty.c
@@ -0,0 +1,756 @@
+/*-
+ * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/un.h>
+#if defined(__OpenBSD__) || defined(__NetBSD__)
+#include <sys/ioctl.h>
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <sys/uio.h>
+#include <termios.h>
+#include <ttyent.h>
+#include <unistd.h>
+#ifndef NONETGRAPH
+#include <netgraph.h>
+#include <netgraph/ng_async.h>
+#include <netgraph/ng_message.h>
+#include <netgraph/ng_ppp.h>
+#include <netgraph/ng_tty.h>
+#endif
+
+#include "layer.h"
+#include "defs.h"
+#include "mbuf.h"
+#include "log.h"
+#include "timer.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "throughput.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "async.h"
+#include "descriptor.h"
+#include "physical.h"
+#include "mp.h"
+#include "chat.h"
+#include "auth.h"
+#include "chap.h"
+#include "cbcp.h"
+#include "datalink.h"
+#include "main.h"
+#include "id.h"
+#include "tty.h"
+
+#if defined(__mac68k__) || defined(__macppc__)
+#undef CRTS_IFLOW
+#undef CCTS_OFLOW
+#define CRTS_IFLOW CDTRCTS
+#define CCTS_OFLOW CDTRCTS
+#endif
+
+#define Online(dev) ((dev)->mbits & TIOCM_CD)
+
+struct ttydevice {
+ struct device dev; /* What struct physical knows about */
+ struct pppTimer Timer; /* CD checks */
+ int mbits; /* Current DCD status */
+ int carrier_seconds; /* seconds before CD is *required* */
+#ifndef NONETGRAPH
+ struct {
+ int speed; /* Pre-line-discipline speed */
+ int fd; /* Pre-line-discipline fd */
+ int disc; /* Old line-discipline */
+ } real;
+ char hook[sizeof NG_ASYNC_HOOK_SYNC]; /* our ng_socket hook */
+ int cs; /* A netgraph control socket (maybe) */
+#endif
+ struct termios ios; /* To be able to reset from raw mode */
+};
+
+#define device2tty(d) ((d)->type == TTY_DEVICE ? (struct ttydevice *)d : NULL)
+
+int
+tty_DeviceSize(void)
+{
+ return sizeof(struct ttydevice);
+}
+
+/*
+ * tty_Timeout() watches the DCD signal and mentions it if it's status
+ * changes.
+ */
+static void
+tty_Timeout(void *data)
+{
+ struct physical *p = data;
+ struct ttydevice *dev = device2tty(p->handler);
+ int ombits, change;
+
+ timer_Stop(&dev->Timer);
+ dev->Timer.load = SECTICKS; /* Once a second please */
+ timer_Start(&dev->Timer);
+ ombits = dev->mbits;
+
+ if (p->fd >= 0) {
+ if (ioctl(p->fd, TIOCMGET, &dev->mbits) < 0) {
+ /* we must be a pty ? */
+ if (p->cfg.cd.necessity != CD_DEFAULT)
+ log_Printf(LogWARN, "%s: Carrier ioctl not supported, "
+ "using ``set cd off''\n", p->link.name);
+ timer_Stop(&dev->Timer);
+ dev->mbits = TIOCM_CD;
+ return;
+ }
+ } else
+ dev->mbits = 0;
+
+ if (ombits == -1) {
+ /* First time looking for carrier */
+ if (Online(dev))
+ log_Printf(LogPHASE, "%s: %s: CD detected\n", p->link.name, p->name.full);
+ else if (++dev->carrier_seconds >= dev->dev.cd.delay) {
+ if (dev->dev.cd.necessity == CD_REQUIRED)
+ log_Printf(LogPHASE, "%s: %s: Required CD not detected\n",
+ p->link.name, p->name.full);
+ else {
+ log_Printf(LogPHASE, "%s: %s doesn't support CD\n",
+ p->link.name, p->name.full);
+ dev->mbits = TIOCM_CD; /* Dodgy null-modem cable ? */
+ }
+ timer_Stop(&dev->Timer);
+ /* tty_AwaitCarrier() will notice */
+ } else {
+ /* Keep waiting */
+ log_Printf(LogDEBUG, "%s: %s: Still no carrier (%d/%d)\n",
+ p->link.name, p->name.full, dev->carrier_seconds,
+ dev->dev.cd.delay);
+ dev->mbits = -1;
+ }
+ } else {
+ change = ombits ^ dev->mbits;
+ if (change & TIOCM_CD) {
+ if (dev->mbits & TIOCM_CD)
+ log_Printf(LogDEBUG, "%s: offline -> online\n", p->link.name);
+ else {
+ log_Printf(LogDEBUG, "%s: online -> offline\n", p->link.name);
+ log_Printf(LogPHASE, "%s: Carrier lost\n", p->link.name);
+ datalink_Down(p->dl, CLOSE_NORMAL);
+ timer_Stop(&dev->Timer);
+ }
+ } else
+ log_Printf(LogDEBUG, "%s: Still %sline\n", p->link.name,
+ Online(dev) ? "on" : "off");
+ }
+}
+
+static void
+tty_StartTimer(struct physical *p)
+{
+ struct ttydevice *dev = device2tty(p->handler);
+
+ timer_Stop(&dev->Timer);
+ dev->Timer.load = SECTICKS;
+ dev->Timer.func = tty_Timeout;
+ dev->Timer.name = "tty CD";
+ dev->Timer.arg = p;
+ log_Printf(LogDEBUG, "%s: Using tty_Timeout [%p]\n",
+ p->link.name, tty_Timeout);
+ timer_Start(&dev->Timer);
+}
+
+static int
+tty_AwaitCarrier(struct physical *p)
+{
+ struct ttydevice *dev = device2tty(p->handler);
+
+ if (dev->dev.cd.necessity == CD_NOTREQUIRED || physical_IsSync(p))
+ return CARRIER_OK;
+
+ if (dev->mbits == -1) {
+ if (dev->Timer.state == TIMER_STOPPED) {
+ dev->carrier_seconds = 0;
+ tty_StartTimer(p);
+ }
+ return CARRIER_PENDING; /* Not yet ! */
+ }
+
+ return Online(dev) ? CARRIER_OK : CARRIER_LOST;
+}
+
+#ifdef NONETGRAPH
+#define tty_SetAsyncParams NULL
+#define tty_Write NULL
+#define tty_Read NULL
+#else
+
+static int
+isngtty(struct ttydevice *dev)
+{
+ return dev->real.fd != -1;
+}
+
+static void
+tty_SetAsyncParams(struct physical *p, u_int32_t mymap, u_int32_t hismap)
+{
+ struct ttydevice *dev = device2tty(p->handler);
+ char asyncpath[NG_PATHLEN + 1];
+ struct ng_async_cfg cfg;
+
+ if (isngtty(dev)) {
+ /* Configure the async converter node */
+
+ snprintf(asyncpath, sizeof asyncpath, ".:%s", dev->hook);
+ memset(&cfg, 0, sizeof cfg);
+ cfg.enabled = 1;
+ cfg.accm = mymap | hismap;
+ cfg.amru = MAX_MTU;
+ cfg.smru = MAX_MRU;
+ log_Printf(LogDEBUG, "Configure async node at %s\n", asyncpath);
+ if (NgSendMsg(dev->cs, asyncpath, NGM_ASYNC_COOKIE,
+ NGM_ASYNC_CMD_SET_CONFIG, &cfg, sizeof cfg) < 0)
+ log_Printf(LogWARN, "%s: Can't configure async node at %s\n",
+ p->link.name, asyncpath);
+ } else
+ /* No netgraph node, just config the async layer */
+ async_SetLinkParams(&p->async, mymap, hismap);
+}
+
+static int
+LoadLineDiscipline(struct physical *p)
+{
+ struct ttydevice *dev = device2tty(p->handler);
+ u_char rbuf[sizeof(struct ng_mesg) + sizeof(struct nodeinfo)];
+ struct ng_mesg *reply;
+ struct nodeinfo *info;
+ char ttypath[NG_NODELEN + 1];
+ struct ngm_mkpeer ngm;
+ struct ngm_connect ngc;
+ int ldisc, cs, ds, hot, speed;
+
+ /*
+ * Don't use the netgraph line discipline for now. Using it works, but
+ * carrier cannot be detected via TIOCMGET and the device doesn't become
+ * selectable with 0 bytes to read when carrier is lost :(
+ */
+ return 0;
+
+ reply = (struct ng_mesg *)rbuf;
+ info = (struct nodeinfo *)reply->data;
+
+ loadmodules(LOAD_VERBOSLY, "netgraph", "ng_tty", "ng_async", "ng_socket",
+ NULL);
+
+ /* Get the speed before loading the line discipline */
+ speed = physical_GetSpeed(p);
+
+ if (ioctl(p->fd, TIOCGETD, &dev->real.disc) < 0) {
+ log_Printf(LogDEBUG, "%s: Couldn't get tty line discipline\n",
+ p->link.name);
+ return 0;
+ }
+ ldisc = NETGRAPHDISC;
+ if (ID0ioctl(p->fd, TIOCSETD, &ldisc) < 0) {
+ log_Printf(LogDEBUG, "%s: Couldn't set NETGRAPHDISC line discipline\n",
+ p->link.name);
+ return 0;
+ }
+
+ /* Get the name of the tty node */
+ if (ioctl(p->fd, NGIOCGINFO, info) < 0) {
+ log_Printf(LogWARN, "%s: ioctl(NGIOCGINFO): %s\n", p->link.name,
+ strerror(errno));
+ ID0ioctl(p->fd, TIOCSETD, &dev->real.disc);
+ return 0;
+ }
+ snprintf(ttypath, sizeof ttypath, "%s:", info->name);
+
+ /* Create a socket node for our endpoint (and to send messages via) */
+ if (ID0NgMkSockNode(NULL, &cs, &ds) == -1) {
+ log_Printf(LogWARN, "%s: NgMkSockNode: %s\n", p->link.name,
+ strerror(errno));
+ ID0ioctl(p->fd, TIOCSETD, &dev->real.disc);
+ return 0;
+ }
+
+ /* Set the ``hot char'' on the TTY node */
+ hot = HDLC_SYN;
+ log_Printf(LogDEBUG, "%s: Set tty hotchar to 0x%02x\n", p->link.name, hot);
+ if (NgSendMsg(cs, ttypath, NGM_TTY_COOKIE,
+ NGM_TTY_SET_HOTCHAR, &hot, sizeof hot) < 0) {
+ log_Printf(LogWARN, "%s: Can't set hot char\n", p->link.name);
+ goto failed;
+ }
+
+ /* Attach an async converter node */
+ snprintf(ngm.type, sizeof ngm.type, "%s", NG_ASYNC_NODE_TYPE);
+ snprintf(ngm.ourhook, sizeof ngm.ourhook, "%s", NG_TTY_HOOK);
+ snprintf(ngm.peerhook, sizeof ngm.peerhook, "%s", NG_ASYNC_HOOK_ASYNC);
+ log_Printf(LogDEBUG, "%s: Send mkpeer async:%s to %s:%s\n", p->link.name,
+ ngm.peerhook, ttypath, ngm.ourhook);
+ if (NgSendMsg(cs, ttypath, NGM_GENERIC_COOKIE,
+ NGM_MKPEER, &ngm, sizeof ngm) < 0) {
+ log_Printf(LogWARN, "%s: Can't create %s node\n", p->link.name,
+ NG_ASYNC_NODE_TYPE);
+ goto failed;
+ }
+
+ /* Connect the async node to our socket */
+ snprintf(ngc.path, sizeof ngc.path, "%s%s", ttypath, NG_TTY_HOOK);
+ snprintf(ngc.peerhook, sizeof ngc.peerhook, "%s", NG_ASYNC_HOOK_SYNC);
+ memcpy(ngc.ourhook, ngc.peerhook, sizeof ngc.ourhook);
+ log_Printf(LogDEBUG, "%s: Send connect %s:%s to .:%s\n", p->link.name,
+ ngc.path, ngc.peerhook, ngc.ourhook);
+ if (NgSendMsg(cs, ".:", NGM_GENERIC_COOKIE, NGM_CONNECT,
+ &ngc, sizeof ngc) < 0) {
+ log_Printf(LogWARN, "%s: Can't connect .:%s -> %s.%s: %s\n",
+ p->link.name, ngc.ourhook, ngc.path, ngc.peerhook,
+ strerror(errno));
+ goto failed;
+ }
+
+ /* Get the async node id */
+ if (NgSendMsg(cs, ngc.path, NGM_GENERIC_COOKIE, NGM_NODEINFO, NULL, 0) < 0) {
+ log_Printf(LogWARN, "%s: Can't request async node info at %s: %s\n",
+ p->link.name, ngc.path, strerror(errno));
+ goto failed;
+ }
+ if (NgRecvMsg(cs, reply, sizeof rbuf, NULL) < 0) {
+ log_Printf(LogWARN, "%s: Can't obtain async node info at %s: %s\n",
+ p->link.name, ngc.path, strerror(errno));
+ goto failed;
+ }
+
+ /* All done, set up our device state */
+ snprintf(dev->hook, sizeof dev->hook, "%s", ngc.ourhook);
+ dev->cs = cs;
+ dev->real.fd = p->fd;
+ p->fd = ds;
+ dev->real.speed = speed;
+ physical_SetSync(p);
+
+ tty_SetAsyncParams(p, 0xffffffff, 0xffffffff);
+ physical_SetupStack(p, dev->dev.name, PHYSICAL_NOFORCE);
+ log_Printf(LogPHASE, "%s: Loaded netgraph tty line discipline\n",
+ p->link.name);
+
+ return 1;
+
+failed:
+ ID0ioctl(p->fd, TIOCSETD, &dev->real.disc);
+ close(ds);
+ close(cs);
+
+ return 0;
+}
+
+static void
+UnloadLineDiscipline(struct physical *p)
+{
+ struct ttydevice *dev = device2tty(p->handler);
+
+ if (isngtty(dev)) {
+log_Printf(LogPHASE, "back to speed %d\n", dev->real.speed);
+ if (!physical_SetSpeed(p, dev->real.speed))
+ log_Printf(LogWARN, "Couldn't reset tty speed to %d\n", dev->real.speed);
+ dev->real.speed = 0;
+ close(p->fd);
+ p->fd = dev->real.fd;
+ dev->real.fd = -1;
+ close(dev->cs);
+ dev->cs = -1;
+ *dev->hook = '\0';
+ if (ID0ioctl(p->fd, TIOCSETD, &dev->real.disc) == 0) {
+ physical_SetupStack(p, dev->dev.name, PHYSICAL_NOFORCE);
+ log_Printf(LogPHASE, "%s: Unloaded netgraph tty line discipline\n",
+ p->link.name);
+ } else
+ log_Printf(LogWARN, "%s: Failed to unload netgraph tty line discipline\n",
+ p->link.name);
+ }
+}
+
+static ssize_t
+tty_Write(struct physical *p, const void *v, size_t n)
+{
+ struct ttydevice *dev = device2tty(p->handler);
+
+ if (isngtty(dev))
+ return NgSendData(p->fd, dev->hook, v, n) == -1 ? -1 : n;
+ else
+ return write(p->fd, v, n);
+}
+
+static ssize_t
+tty_Read(struct physical *p, void *v, size_t n)
+{
+ struct ttydevice *dev = device2tty(p->handler);
+ char hook[sizeof NG_ASYNC_HOOK_SYNC];
+
+ if (isngtty(dev))
+ return NgRecvData(p->fd, v, n, hook);
+ else
+ return read(p->fd, v, n);
+}
+
+#endif /* NETGRAPH */
+
+static int
+tty_Raw(struct physical *p)
+{
+ struct ttydevice *dev = device2tty(p->handler);
+ struct termios ios;
+ int oldflag;
+
+ log_Printf(LogDEBUG, "%s: Entering tty_Raw\n", p->link.name);
+
+ if (p->type != PHYS_DIRECT && p->fd >= 0 && !Online(dev))
+ log_Printf(LogDEBUG, "%s: Raw: descriptor = %d, mbits = %x\n",
+ p->link.name, p->fd, dev->mbits);
+
+ if (!physical_IsSync(p)) {
+#ifndef NONETGRAPH
+ if (!LoadLineDiscipline(p))
+#endif
+ {
+ tcgetattr(p->fd, &ios);
+ cfmakeraw(&ios);
+ if (p->cfg.rts_cts)
+ ios.c_cflag |= CLOCAL | CCTS_OFLOW | CRTS_IFLOW;
+ else
+ ios.c_cflag |= CLOCAL;
+
+ if (p->type != PHYS_DEDICATED)
+ ios.c_cflag |= HUPCL;
+
+ if (tcsetattr(p->fd, TCSANOW, &ios) == -1)
+ log_Printf(LogWARN, "%s: tcsetattr: Failed configuring device\n",
+ p->link.name);
+ }
+ }
+
+ oldflag = fcntl(p->fd, F_GETFL, 0);
+ if (oldflag < 0)
+ return 0;
+ fcntl(p->fd, F_SETFL, oldflag | O_NONBLOCK);
+
+ return 1;
+}
+
+static void
+tty_Offline(struct physical *p)
+{
+ struct ttydevice *dev = device2tty(p->handler);
+
+ if (p->fd >= 0) {
+ timer_Stop(&dev->Timer);
+ dev->mbits &= ~TIOCM_DTR; /* XXX: Hmm, what's this supposed to do ? */
+ if (Online(dev)) {
+ struct termios tio;
+
+ tcgetattr(p->fd, &tio);
+ if (cfsetspeed(&tio, B0) == -1 || tcsetattr(p->fd, TCSANOW, &tio) == -1)
+ log_Printf(LogWARN, "%s: Unable to set physical to speed 0\n",
+ p->link.name);
+ }
+ }
+}
+
+static void
+tty_Cooked(struct physical *p)
+{
+ struct ttydevice *dev = device2tty(p->handler);
+ int oldflag;
+
+ tty_Offline(p); /* In case of emergency close()s */
+
+ tcflush(p->fd, TCIOFLUSH);
+
+ if (!physical_IsSync(p) && tcsetattr(p->fd, TCSAFLUSH, &dev->ios) == -1)
+ log_Printf(LogWARN, "%s: tcsetattr: Unable to restore device settings\n",
+ p->link.name);
+
+#ifndef NONETGRAPH
+ UnloadLineDiscipline(p);
+#endif
+
+ if ((oldflag = fcntl(p->fd, F_GETFL, 0)) != -1)
+ fcntl(p->fd, F_SETFL, oldflag & ~O_NONBLOCK);
+}
+
+static void
+tty_StopTimer(struct physical *p)
+{
+ struct ttydevice *dev = device2tty(p->handler);
+
+ timer_Stop(&dev->Timer);
+}
+
+static void
+tty_Free(struct physical *p)
+{
+ struct ttydevice *dev = device2tty(p->handler);
+
+ tty_Offline(p); /* In case of emergency close()s */
+ free(dev);
+}
+
+static int
+tty_Speed(struct physical *p)
+{
+ struct termios ios;
+
+ if (tcgetattr(p->fd, &ios) == -1)
+ return 0;
+
+ return SpeedToInt(cfgetispeed(&ios));
+}
+
+static const char *
+tty_OpenInfo(struct physical *p)
+{
+ struct ttydevice *dev = device2tty(p->handler);
+ static char buf[13];
+
+ if (Online(dev))
+ strcpy(buf, "with");
+ else
+ strcpy(buf, "no");
+ strcat(buf, " carrier");
+
+ return buf;
+}
+
+static int
+tty_Slot(struct physical *p)
+{
+ struct ttyent *ttyp;
+ int slot;
+
+ setttyent();
+ for (slot = 1; (ttyp = getttyent()); ++slot)
+ if (!strcmp(ttyp->ty_name, p->name.base)) {
+ endttyent();
+ return slot;
+ }
+
+ endttyent();
+ return -1;
+}
+
+static void
+tty_device2iov(struct device *d, struct iovec *iov, int *niov,
+ int maxiov, int *auxfd, int *nauxfd)
+{
+ struct ttydevice *dev = device2tty(d);
+ int sz = physical_MaxDeviceSize();
+
+ iov[*niov].iov_base = realloc(d, sz);
+ if (iov[*niov].iov_base == NULL) {
+ log_Printf(LogALERT, "Failed to allocate memory: %d\n", sz);
+ AbortProgram(EX_OSERR);
+ }
+ iov[*niov].iov_len = sz;
+ (*niov)++;
+
+#ifndef NONETGRAPH
+ if (dev->cs >= 0) {
+ *auxfd = dev->cs;
+ (*nauxfd)++;
+ }
+#endif
+
+ if (dev->Timer.state != TIMER_STOPPED) {
+ timer_Stop(&dev->Timer);
+ dev->Timer.state = TIMER_RUNNING;
+ }
+}
+
+static struct device basettydevice = {
+ TTY_DEVICE,
+ "tty",
+ 0,
+ { CD_VARIABLE, DEF_TTYCDDELAY },
+ tty_AwaitCarrier,
+ NULL,
+ tty_Raw,
+ tty_Offline,
+ tty_Cooked,
+ tty_SetAsyncParams,
+ tty_StopTimer,
+ tty_Free,
+ tty_Read,
+ tty_Write,
+ tty_device2iov,
+ tty_Speed,
+ tty_OpenInfo,
+ tty_Slot
+};
+
+struct device *
+tty_iov2device(int type, struct physical *p, struct iovec *iov, int *niov,
+ int maxiov, int *auxfd, int *nauxfd)
+{
+ if (type == TTY_DEVICE) {
+ struct ttydevice *dev = (struct ttydevice *)iov[(*niov)++].iov_base;
+
+ dev = realloc(dev, sizeof *dev); /* Reduce to the correct size */
+ if (dev == NULL) {
+ log_Printf(LogALERT, "Failed to allocate memory: %d\n",
+ (int)(sizeof *dev));
+ AbortProgram(EX_OSERR);
+ }
+
+#ifndef NONETGRAPH
+ if (*nauxfd) {
+ dev->cs = *auxfd;
+ (*nauxfd)--;
+ } else
+ dev->cs = -1;
+#endif
+
+ /* Refresh function pointers etc */
+ memcpy(&dev->dev, &basettydevice, sizeof dev->dev);
+
+ physical_SetupStack(p, dev->dev.name, PHYSICAL_NOFORCE);
+ if (dev->Timer.state != TIMER_STOPPED) {
+ dev->Timer.state = TIMER_STOPPED;
+ p->handler = &dev->dev; /* For the benefit of StartTimer */
+ tty_StartTimer(p);
+ }
+ return &dev->dev;
+ }
+
+ return NULL;
+}
+
+struct device *
+tty_Create(struct physical *p)
+{
+ struct ttydevice *dev;
+ struct termios ios;
+ int oldflag;
+
+ if (p->fd < 0 || !isatty(p->fd))
+ /* Don't want this */
+ return NULL;
+
+ if (*p->name.full == '\0') {
+ physical_SetDevice(p, ttyname(p->fd));
+ log_Printf(LogDEBUG, "%s: Input is a tty (%s)\n",
+ p->link.name, p->name.full);
+ } else
+ log_Printf(LogDEBUG, "%s: Opened %s\n", p->link.name, p->name.full);
+
+ /* We're gonna return a ttydevice (unless something goes horribly wrong) */
+
+ if ((dev = malloc(sizeof *dev)) == NULL) {
+ /* Complete failure - parent doesn't continue trying to ``create'' */
+ close(p->fd);
+ p->fd = -1;
+ return NULL;
+ }
+
+ memcpy(&dev->dev, &basettydevice, sizeof dev->dev);
+ memset(&dev->Timer, '\0', sizeof dev->Timer);
+ dev->mbits = -1;
+#ifndef NONETGRAPH
+ dev->real.speed = 0;
+ dev->real.fd = -1;
+ dev->real.disc = -1;
+ *dev->hook = '\0';
+#endif
+ tcgetattr(p->fd, &ios);
+ dev->ios = ios;
+
+ if (p->cfg.cd.necessity != CD_DEFAULT)
+ /* Any override is ok for the tty device */
+ dev->dev.cd = p->cfg.cd;
+
+ log_Printf(LogDEBUG, "%s: tty_Create: physical (get): fd = %d,"
+ " iflag = %lx, oflag = %lx, cflag = %lx\n", p->link.name, p->fd,
+ (u_long)ios.c_iflag, (u_long)ios.c_oflag, (u_long)ios.c_cflag);
+
+ cfmakeraw(&ios);
+ if (p->cfg.rts_cts)
+ ios.c_cflag |= CLOCAL | CCTS_OFLOW | CRTS_IFLOW;
+ else {
+ ios.c_cflag |= CLOCAL;
+ ios.c_iflag |= IXOFF;
+ }
+ ios.c_iflag |= IXON;
+ if (p->type != PHYS_DEDICATED)
+ ios.c_cflag |= HUPCL;
+
+ if (p->type != PHYS_DIRECT) {
+ /* Change tty speed when we're not in -direct mode */
+ ios.c_cflag &= ~(CSIZE | PARODD | PARENB);
+ ios.c_cflag |= p->cfg.parity;
+ if (cfsetspeed(&ios, IntToSpeed(p->cfg.speed)) == -1)
+ log_Printf(LogWARN, "%s: %s: Unable to set speed to %d\n",
+ p->link.name, p->name.full, p->cfg.speed);
+ }
+
+ if (tcsetattr(p->fd, TCSADRAIN, &ios) == -1) {
+ log_Printf(LogWARN, "%s: tcsetattr: Failed configuring device\n",
+ p->link.name);
+ if (p->type != PHYS_DIRECT && p->cfg.speed > 115200)
+ log_Printf(LogWARN, "%.*s Perhaps the speed is unsupported\n",
+ (int)strlen(p->link.name), "");
+ }
+
+ log_Printf(LogDEBUG, "%s: physical (put): iflag = %lx, oflag = %lx, "
+ "cflag = %lx\n", p->link.name, (u_long)ios.c_iflag,
+ (u_long)ios.c_oflag, (u_long)ios.c_cflag);
+
+ oldflag = fcntl(p->fd, F_GETFL, 0);
+ if (oldflag < 0) {
+ /* Complete failure - parent doesn't continue trying to ``create'' */
+
+ log_Printf(LogWARN, "%s: Open: Cannot get physical flags: %s\n",
+ p->link.name, strerror(errno));
+ tty_Cooked(p);
+ close(p->fd);
+ p->fd = -1;
+ free(dev);
+ return NULL;
+ } else
+ fcntl(p->fd, F_SETFL, oldflag & ~O_NONBLOCK);
+
+ physical_SetupStack(p, dev->dev.name, PHYSICAL_NOFORCE);
+
+ return &dev->dev;
+}
diff --git a/usr.sbin/ppp/tty.h b/usr.sbin/ppp/tty.h
new file mode 100644
index 0000000..f3f0d53
--- /dev/null
+++ b/usr.sbin/ppp/tty.h
@@ -0,0 +1,37 @@
+/*-
+ * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct physical;
+struct device;
+
+#define DEF_TTYCDDELAY 1 /* Default ``set cd'' value */
+
+extern struct device *tty_Create(struct physical *);
+extern struct device *tty_iov2device(int, struct physical *,
+ struct iovec *, int *, int, int *, int *);
+extern int tty_DeviceSize(void);
diff --git a/usr.sbin/ppp/tun.c b/usr.sbin/ppp/tun.c
new file mode 100644
index 0000000..883cd5c
--- /dev/null
+++ b/usr.sbin/ppp/tun.c
@@ -0,0 +1,119 @@
+/*-
+ * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+
+#include <sys/socket.h> /* For IFF_ defines */
+#ifndef __FreeBSD__
+#include <net/if.h> /* For IFF_ defines */
+#endif
+#include <net/route.h>
+#include <netinet/in.h>
+#include <net/if_types.h>
+#include <net/if_tun.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/un.h>
+
+#include <errno.h>
+#include <string.h>
+#if defined(__OpenBSD__) || defined(__NetBSD__)
+#include <sys/ioctl.h>
+#endif
+#include <stdio.h>
+#include <termios.h>
+#ifdef __NetBSD__
+#include <unistd.h>
+#endif
+
+#include "layer.h"
+#include "mbuf.h"
+#include "log.h"
+#include "id.h"
+#include "timer.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "defs.h"
+#include "fsm.h"
+#include "throughput.h"
+#include "iplist.h"
+#include "slcompress.h"
+#include "ncpaddr.h"
+#include "ipcp.h"
+#include "filter.h"
+#include "descriptor.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "mp.h"
+#include "iface.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+#include "tun.h"
+
+void
+tun_configure(struct bundle *bundle)
+{
+#ifdef __NetBSD__
+ struct ifreq ifr;
+ int s;
+
+ s = socket(PF_INET, SOCK_DGRAM, 0);
+
+ if (s < 0) {
+ log_Printf(LogERROR, "tun_configure: socket(): %s\n", strerror(errno));
+ return;
+ }
+
+ sprintf(ifr.ifr_name, "tun%d", bundle->unit);
+ ifr.ifr_mtu = bundle->iface->mtu;
+ if (ioctl(s, SIOCSIFMTU, &ifr) < 0)
+ log_Printf(LogERROR, "tun_configure: ioctl(SIOCSIFMTU): %s\n",
+ strerror(errno));
+
+ close(s);
+#else
+ struct tuninfo info;
+
+ memset(&info, '\0', sizeof info);
+ info.type = IFT_PPP;
+ info.mtu = bundle->iface->mtu;
+
+ info.baudrate = bundle->bandwidth;
+#ifdef __OpenBSD__
+ info.flags = IFF_UP|IFF_POINTOPOINT|IFF_MULTICAST;
+#endif
+ if (ID0ioctl(bundle->dev.fd, TUNSIFINFO, &info) < 0)
+ log_Printf(LogERROR, "tun_configure: ioctl(TUNSIFINFO): %s\n",
+ strerror(errno));
+#endif
+}
diff --git a/usr.sbin/ppp/tun.h b/usr.sbin/ppp/tun.h
new file mode 100644
index 0000000..1bb0712
--- /dev/null
+++ b/usr.sbin/ppp/tun.h
@@ -0,0 +1,39 @@
+/*-
+ * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct tun_data {
+ union {
+ u_int32_t family;
+ u_int32_t timeout;
+ } header;
+ u_char data[MAX_MRU];
+};
+
+struct bundle;
+
+extern void tun_configure(struct bundle *);
diff --git a/usr.sbin/ppp/ua.h b/usr.sbin/ppp/ua.h
new file mode 100644
index 0000000..dbd89e5
--- /dev/null
+++ b/usr.sbin/ppp/ua.h
@@ -0,0 +1,74 @@
+/*-
+ * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifdef __i386__ /* Do any other archs not care about alignment ? */
+
+# define ua_htonl(src, tgt) (*(u_int32_t *)(tgt) = htonl(*(u_int32_t *)(src)))
+# define ua_ntohl(src, tgt) (*(u_int32_t *)(tgt) = ntohl(*(u_int32_t *)(src)))
+# define ua_htons(src, tgt) (*(u_int16_t *)(tgt) = htons(*(u_int16_t *)(src)))
+# define ua_ntohs(src, tgt) (*(u_int16_t *)(tgt) = ntohs(*(u_int16_t *)(src)))
+
+#else /* We care about alignment (or else drop a core !) */
+
+# define ua_htonl(src, tgt) \
+ do { \
+ u_int32_t __oh; \
+ memcpy(&__oh, (src), sizeof __oh); \
+ *(u_char *)(tgt) = __oh >> 24; \
+ *((u_char *)(tgt) + 1) = (__oh >> 16) & 0xff; \
+ *((u_char *)(tgt) + 2) = (__oh >> 8) & 0xff; \
+ *((u_char *)(tgt) + 3) = __oh & 0xff; \
+ } while (0)
+
+# define ua_ntohl(src, tgt) \
+ do { \
+ u_int32_t __nh; \
+ __nh = ((u_int32_t)*(u_char *)(src) << 24) | \
+ ((u_int32_t)*((u_char *)(src) + 1) << 16) | \
+ ((u_int32_t)*((u_char *)(src) + 2) << 8) | \
+ (u_int32_t)*((u_char *)(src) + 3); \
+ memcpy((tgt), &__nh, sizeof __nh); \
+ } while (0)
+
+# define ua_htons(src, tgt) \
+ do { \
+ u_int16_t __oh; \
+ memcpy(&__oh, (src), sizeof __oh); \
+ *(u_char *)(tgt) = __oh >> 8; \
+ *((u_char *)(tgt) + 1) = __oh & 0xff; \
+ } while (0)
+
+# define ua_ntohs(src, tgt) \
+ do { \
+ u_int16_t __nh; \
+ __nh = ((u_int16_t)*(u_char *)(src) << 8) | \
+ (u_int16_t)*((u_char *)(src) + 1); \
+ memcpy((tgt), &__nh, sizeof __nh); \
+ } while (0)
+
+#endif
diff --git a/usr.sbin/ppp/udp.c b/usr.sbin/ppp/udp.c
new file mode 100644
index 0000000..47cab3f
--- /dev/null
+++ b/usr.sbin/ppp/udp.c
@@ -0,0 +1,336 @@
+/*-
+ * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "layer.h"
+#include "defs.h"
+#include "mbuf.h"
+#include "log.h"
+#include "timer.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "throughput.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "async.h"
+#include "descriptor.h"
+#include "physical.h"
+#include "main.h"
+#include "udp.h"
+
+
+#define UDP_CONNECTED 1
+#define UDP_UNCONNECTED 2
+#define UDP_MAYBEUNCONNECTED 3
+
+struct udpdevice {
+ struct device dev; /* What struct physical knows about */
+ struct sockaddr_in sock; /* peer address */
+ unsigned connected : 2; /* Have we connect()d ? */
+};
+
+#define device2udp(d) ((d)->type == UDP_DEVICE ? (struct udpdevice *)d : NULL)
+
+int
+udp_DeviceSize(void)
+{
+ return sizeof(struct udpdevice);
+}
+
+static ssize_t
+udp_Sendto(struct physical *p, const void *v, size_t n)
+{
+ struct udpdevice *dev = device2udp(p->handler);
+ int ret;
+
+ switch (dev->connected) {
+ case UDP_CONNECTED:
+ ret = write(p->fd, v, n);
+ break;
+
+ case UDP_UNCONNECTED:
+ default:
+ ret = sendto(p->fd, v, n, 0, (struct sockaddr *)&dev->sock,
+ sizeof dev->sock);
+ break;
+ }
+ if (dev->connected == UDP_MAYBEUNCONNECTED) {
+ if (ret == -1 && errno == EISCONN) {
+ dev->connected = UDP_CONNECTED;
+ ret = write(p->fd, v, n);
+ } else
+ dev->connected = UDP_UNCONNECTED;
+ }
+
+ return ret;
+}
+
+static ssize_t
+udp_Recvfrom(struct physical *p, void *v, size_t n)
+{
+ struct udpdevice *dev = device2udp(p->handler);
+ int sz, ret;
+
+ if (dev->connected == UDP_CONNECTED)
+ return read(p->fd, v, n);
+
+ sz = sizeof dev->sock;
+ ret = recvfrom(p->fd, v, n, 0, (struct sockaddr *)&dev->sock, &sz);
+
+ if (*p->name.full == '\0') {
+ snprintf(p->name.full, sizeof p->name.full, "%s:%d/udp",
+ inet_ntoa(dev->sock.sin_addr), ntohs(dev->sock.sin_port));
+ p->name.base = p->name.full;
+ }
+
+ return ret;
+}
+
+static void
+udp_Free(struct physical *p)
+{
+ struct udpdevice *dev = device2udp(p->handler);
+
+ free(dev);
+}
+
+static void
+udp_device2iov(struct device *d, struct iovec *iov, int *niov,
+ int maxiov, int *auxfd, int *nauxfd)
+{
+ int sz = physical_MaxDeviceSize();
+
+ iov[*niov].iov_base = realloc(d, sz);
+ if (iov[*niov].iov_base == NULL) {
+ log_Printf(LogALERT, "Failed to allocate memory: %d\n", sz);
+ AbortProgram(EX_OSERR);
+ }
+ iov[*niov].iov_len = sz;
+ (*niov)++;
+}
+
+static const struct device baseudpdevice = {
+ UDP_DEVICE,
+ "udp",
+ 0,
+ { CD_NOTREQUIRED, 0 },
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ udp_Free,
+ udp_Recvfrom,
+ udp_Sendto,
+ udp_device2iov,
+ NULL,
+ NULL,
+ NULL
+};
+
+struct device *
+udp_iov2device(int type, struct physical *p, struct iovec *iov, int *niov,
+ int maxiov, int *auxfd, int *nauxfd)
+{
+ if (type == UDP_DEVICE) {
+ struct udpdevice *dev = (struct udpdevice *)iov[(*niov)++].iov_base;
+
+ dev = realloc(dev, sizeof *dev); /* Reduce to the correct size */
+ if (dev == NULL) {
+ log_Printf(LogALERT, "Failed to allocate memory: %d\n",
+ (int)(sizeof *dev));
+ AbortProgram(EX_OSERR);
+ }
+
+ /* Refresh function pointers etc */
+ memcpy(&dev->dev, &baseudpdevice, sizeof dev->dev);
+
+ physical_SetupStack(p, dev->dev.name, PHYSICAL_FORCE_SYNC);
+ return &dev->dev;
+ }
+
+ return NULL;
+}
+
+static struct udpdevice *
+udp_CreateDevice(struct physical *p, char *host, char *port)
+{
+ struct udpdevice *dev;
+ struct servent *sp;
+
+ if ((dev = malloc(sizeof *dev)) == NULL) {
+ log_Printf(LogWARN, "%s: Cannot allocate a udp device: %s\n",
+ p->link.name, strerror(errno));
+ return NULL;
+ }
+
+ dev->sock.sin_family = AF_INET;
+ dev->sock.sin_addr.s_addr = inet_addr(host);
+ dev->sock.sin_addr = GetIpAddr(host);
+ if (dev->sock.sin_addr.s_addr == INADDR_NONE) {
+ log_Printf(LogWARN, "%s: %s: unknown host\n", p->link.name, host);
+ free(dev);
+ return NULL;
+ }
+ dev->sock.sin_port = htons(atoi(port));
+ if (dev->sock.sin_port == 0) {
+ sp = getservbyname(port, "udp");
+ if (sp)
+ dev->sock.sin_port = sp->s_port;
+ else {
+ log_Printf(LogWARN, "%s: %s: unknown service\n", p->link.name, port);
+ free(dev);
+ return NULL;
+ }
+ }
+
+ log_Printf(LogPHASE, "%s: Connecting to %s:%s/udp\n", p->link.name,
+ host, port);
+
+ p->fd = socket(PF_INET, SOCK_DGRAM, 0);
+ if (p->fd >= 0) {
+ log_Printf(LogDEBUG, "%s: Opened udp socket %s\n", p->link.name,
+ p->name.full);
+ if (connect(p->fd, (struct sockaddr *)&dev->sock, sizeof dev->sock) == 0) {
+ dev->connected = UDP_CONNECTED;
+ return dev;
+ } else
+ log_Printf(LogWARN, "%s: connect: %s\n", p->name.full, strerror(errno));
+ } else
+ log_Printf(LogWARN, "%s: socket: %s\n", p->name.full, strerror(errno));
+
+ close(p->fd);
+ p->fd = -1;
+ free(dev);
+
+ return NULL;
+}
+
+struct device *
+udp_Create(struct physical *p)
+{
+ char *cp, *host, *port, *svc;
+ struct udpdevice *dev;
+
+ dev = NULL;
+ if (p->fd < 0) {
+ if ((cp = strchr(p->name.full, ':')) != NULL && !strchr(cp + 1, ':')) {
+ *cp = '\0';
+ host = p->name.full;
+ port = cp + 1;
+ svc = strchr(port, '/');
+ if (svc && strcasecmp(svc, "/udp")) {
+ *cp = ':';
+ return NULL;
+ }
+ if (svc) {
+ p->fd--; /* We own the device but maybe can't use it - change fd */
+ *svc = '\0';
+ }
+
+ if (*host && *port)
+ dev = udp_CreateDevice(p, host, port);
+
+ *cp = ':';
+ if (svc)
+ *svc = '/';
+ }
+ } else {
+ /* See if we're a connected udp socket */
+ struct stat st;
+
+ if (fstat(p->fd, &st) != -1 && (st.st_mode & S_IFSOCK)) {
+ int type, sz;
+
+ sz = sizeof type;
+ if (getsockopt(p->fd, SOL_SOCKET, SO_TYPE, &type, &sz) == -1) {
+ log_Printf(LogPHASE, "%s: Link is a closed socket !\n", p->link.name);
+ close(p->fd);
+ p->fd = -1;
+ return NULL;
+ }
+
+ if (sz == sizeof type && type == SOCK_DGRAM) {
+ struct sockaddr_in sock;
+ struct sockaddr *sockp = (struct sockaddr *)&sock;
+
+ if ((dev = malloc(sizeof *dev)) == NULL) {
+ log_Printf(LogWARN, "%s: Cannot allocate a udp device: %s\n",
+ p->link.name, strerror(errno));
+ return NULL;
+ }
+
+ if (getpeername(p->fd, sockp, &sz) == 0) {
+ log_Printf(LogPHASE, "%s: Link is a connected udp socket\n",
+ p->link.name);
+ dev->connected = UDP_CONNECTED;
+ } else {
+ log_Printf(LogPHASE, "%s: Link is a disconnected udp socket\n",
+ p->link.name);
+
+ dev->connected = UDP_MAYBEUNCONNECTED;
+
+ if (p->link.lcp.cfg.openmode != OPEN_PASSIVE) {
+ log_Printf(LogPHASE, "%s: Changing openmode to PASSIVE\n",
+ p->link.name);
+ p->link.lcp.cfg.openmode = OPEN_PASSIVE;
+ }
+ }
+ }
+ }
+ }
+
+ if (dev) {
+ memcpy(&dev->dev, &baseudpdevice, sizeof dev->dev);
+ physical_SetupStack(p, dev->dev.name, PHYSICAL_FORCE_SYNC);
+ if (p->cfg.cd.necessity != CD_DEFAULT)
+ log_Printf(LogWARN, "Carrier settings ignored\n");
+ return &dev->dev;
+ }
+
+ return NULL;
+}
diff --git a/usr.sbin/ppp/udp.h b/usr.sbin/ppp/udp.h
new file mode 100644
index 0000000..46b8fe6
--- /dev/null
+++ b/usr.sbin/ppp/udp.h
@@ -0,0 +1,35 @@
+/*-
+ * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct physical;
+struct device;
+
+extern struct device *udp_Create(struct physical *);
+extern struct device *udp_iov2device(int, struct physical *,
+ struct iovec *, int *, int, int *, int *);
+extern int udp_DeviceSize(void);
diff --git a/usr.sbin/ppp/vjcomp.c b/usr.sbin/ppp/vjcomp.c
new file mode 100644
index 0000000..8bc03db
--- /dev/null
+++ b/usr.sbin/ppp/vjcomp.c
@@ -0,0 +1,200 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <stdio.h>
+#include <string.h> /* strlen/memcpy */
+#include <termios.h>
+
+#include "layer.h"
+#include "mbuf.h"
+#include "log.h"
+#include "timer.h"
+#include "fsm.h"
+#include "proto.h"
+#include "slcompress.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "defs.h"
+#include "iplist.h"
+#include "throughput.h"
+#include "ncpaddr.h"
+#include "ipcp.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "filter.h"
+#include "descriptor.h"
+#include "mp.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+#include "vjcomp.h"
+
+#define MAX_VJHEADER 16 /* Maximum size of compressed header */
+
+static struct mbuf *
+vj_LayerPush(struct bundle *bundle, struct link *l, struct mbuf *bp, int pri,
+ u_short *proto)
+{
+ int type;
+ struct ip *pip;
+ u_short cproto = bundle->ncp.ipcp.peer_compproto >> 16;
+
+ bp = m_pullup(bp);
+ pip = (struct ip *)MBUF_CTOP(bp);
+ if (*proto == PROTO_IP && pip->ip_p == IPPROTO_TCP &&
+ cproto == PROTO_VJCOMP) {
+ type = sl_compress_tcp(bp, pip, &bundle->ncp.ipcp.vj.cslc,
+ &bundle->ncp.ipcp.vj.slstat,
+ bundle->ncp.ipcp.peer_compproto & 0xff);
+ log_Printf(LogDEBUG, "vj_LayerWrite: type = %x\n", type);
+ switch (type) {
+ case TYPE_IP:
+ break;
+
+ case TYPE_UNCOMPRESSED_TCP:
+ *proto = PROTO_VJUNCOMP;
+ log_Printf(LogDEBUG, "vj_LayerPush: PROTO_IP -> PROTO_VJUNCOMP\n");
+ m_settype(bp, MB_VJOUT);
+ break;
+
+ case TYPE_COMPRESSED_TCP:
+ *proto = PROTO_VJCOMP;
+ log_Printf(LogDEBUG, "vj_LayerPush: PROTO_IP -> PROTO_VJUNCOMP\n");
+ m_settype(bp, MB_VJOUT);
+ break;
+
+ default:
+ log_Printf(LogERROR, "vj_LayerPush: Unknown frame type %x\n", type);
+ m_freem(bp);
+ return NULL;
+ }
+ }
+
+ return bp;
+}
+
+static struct mbuf *
+VjUncompressTcp(struct ipcp *ipcp, struct mbuf *bp, u_char type)
+{
+ u_char *bufp;
+ int len, olen, rlen;
+ u_char work[MAX_HDR + MAX_VJHEADER]; /* enough to hold TCP/IP header */
+
+ bp = m_pullup(bp);
+ olen = len = m_length(bp);
+ if (type == TYPE_UNCOMPRESSED_TCP) {
+ /*
+ * Uncompressed packet does NOT change its size, so that we can use mbuf
+ * space for uncompression job.
+ */
+ bufp = MBUF_CTOP(bp);
+ len = sl_uncompress_tcp(&bufp, len, type, &ipcp->vj.cslc, &ipcp->vj.slstat,
+ (ipcp->my_compproto >> 8) & 255);
+ if (len <= 0) {
+ m_freem(bp);
+ bp = NULL;
+ } else
+ m_settype(bp, MB_VJIN);
+ return bp;
+ }
+
+ /*
+ * Handle compressed packet. 1) Read upto MAX_VJHEADER bytes into work
+ * space. 2) Try to uncompress it. 3) Compute amount of necessary space. 4)
+ * Copy unread data info there.
+ */
+ if (len > MAX_VJHEADER)
+ len = MAX_VJHEADER;
+ rlen = len;
+ bufp = work + MAX_HDR;
+ bp = mbuf_Read(bp, bufp, rlen);
+ len = sl_uncompress_tcp(&bufp, olen, type, &ipcp->vj.cslc, &ipcp->vj.slstat,
+ (ipcp->my_compproto >> 8) & 255);
+ if (len <= 0) {
+ m_freem(bp);
+ return NULL;
+ }
+ len -= olen;
+ len += rlen;
+
+ bp = m_prepend(bp, bufp, len, 0);
+ m_settype(bp, MB_VJIN);
+
+ return bp;
+}
+
+static struct mbuf *
+vj_LayerPull(struct bundle *bundle, struct link *l, struct mbuf *bp,
+ u_short *proto)
+{
+ u_char type;
+
+ switch (*proto) {
+ case PROTO_VJCOMP:
+ type = TYPE_COMPRESSED_TCP;
+ log_Printf(LogDEBUG, "vj_LayerPull: PROTO_VJCOMP -> PROTO_IP\n");
+ break;
+ case PROTO_VJUNCOMP:
+ type = TYPE_UNCOMPRESSED_TCP;
+ log_Printf(LogDEBUG, "vj_LayerPull: PROTO_VJUNCOMP -> PROTO_IP\n");
+ break;
+ default:
+ return bp;
+ }
+
+ *proto = PROTO_IP;
+ return VjUncompressTcp(&bundle->ncp.ipcp, bp, type);
+}
+
+const char *
+vj2asc(u_int32_t val)
+{
+ static char asc[50]; /* The return value is used immediately */
+
+ if (val)
+ snprintf(asc, sizeof asc, "%d VJ slots with%s slot compression",
+ (int)((val>>8)&15)+1, val & 1 ? "" : "out");
+ else
+ strcpy(asc, "VJ disabled");
+ return asc;
+}
+
+struct layer vjlayer = { LAYER_VJ, "vj", vj_LayerPush, vj_LayerPull };
diff --git a/usr.sbin/ppp/vjcomp.h b/usr.sbin/ppp/vjcomp.h
new file mode 100644
index 0000000..2956122
--- /dev/null
+++ b/usr.sbin/ppp/vjcomp.h
@@ -0,0 +1,36 @@
+/*-
+ * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct mbuf;
+struct link;
+struct ipcp;
+struct bundle;
+
+extern const char *vj2asc(u_int32_t);
+
+extern struct layer vjlayer;
diff --git a/usr.sbin/pppctl/Makefile b/usr.sbin/pppctl/Makefile
new file mode 100644
index 0000000..8da3f50
--- /dev/null
+++ b/usr.sbin/pppctl/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PROG= pppctl
+MAN= pppctl.8
+
+LDADD= -lc_r -ledit -ltermcap
+DPADD= ${LIBC_R} ${LIBEDIT} ${LIBTERMCAP}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pppctl/pppctl.8 b/usr.sbin/pppctl/pppctl.8
new file mode 100644
index 0000000..1e90579
--- /dev/null
+++ b/usr.sbin/pppctl/pppctl.8
@@ -0,0 +1,212 @@
+.\" $FreeBSD$
+.Dd June 26, 1997
+.Os
+.Dt PPPCTL 8
+.Sh NAME
+.Nm pppctl
+.Nd PPP control program
+.Sh SYNOPSIS
+.Nm
+.Op Fl v
+.Op Fl t Ar n
+.Op Fl p Ar passwd
+.Xo Oo Ar host : Oc Ns
+.Ar Port | LocalSocket
+.Xc
+.Oo
+.Sm off
+.Ar command Oo ; Ar command Oc Ar ...
+.Sm on
+.Oc
+.Sh DESCRIPTION
+This 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 : Ns Ar port
+pair, otherwise it is treated as a TCP port specification on the
+local machine (127.0.0.1). Both the
+.Ar host
+and
+.Ar port
+may be specified numerically if you wish to avoid a DNS lookup
+or don't have an entry for the given port in
+.Pa /etc/services .
+.Pp
+All remaining arguments are concatenated to form the
+.Ar command Ns (s)
+that will be sent to the
+.Nm ppp
+daemon. If any semi-colon characters are found, they are treated as
+.Ar command
+delimiters, allowing more than one
+.Ar command
+in a given
+.Sq session .
+For example:
+.Bd -literal -offset indent
+pppctl 3000 set timeout 300\\; show timeout
+.Ed
+.Pp
+Don't forget to escape or quote the ';' as it is a special character
+for most shells.
+.Pp
+If no
+.Ar command
+arguments are given,
+.Nm
+enters interactive mode, where commands are read from standard input.
+When reading commands, the
+.Xr editline 3
+library is used, allowing command-line editing (with
+.Xr editrc 5
+defining editing behaviour). The history size
+defaults to
+.Em 20 lines .
+.Pp
+The following command line options are available:
+.Bl -tag -width Ds
+.It Fl v
+Display all data sent to and received from the
+.Nm ppp
+daemon. Normally,
+.Nm
+displays only non-prompt lines received. This option is ignored in
+interactive mode.
+.It Fl t Ar n
+Use a timeout of
+.Ar n
+instead of the default 2 seconds when connecting. This may be required
+if you wish to control a daemon over a slow (or even a dialup) link.
+.It Fl p Ar passwd
+Specify the password required by the
+.Nm ppp
+daemon. If this switch is not used,
+.Nm
+will prompt for a password once it has successfully connected to
+.Nm ppp .
+.El
+.Sh EXAMPLES
+If you run
+.Nm ppp
+in
+.Fl auto
+mode,
+.Nm
+can be used to automate many frequent tasks (you can actually control
+.Nm ppp
+in any mode except interactive mode). Use of the
+.Fl p
+option is discouraged (even in scripts that aren't readable by others)
+as a
+.Xr ps 1
+listing may reveal your secret.
+.Pp
+The best way to allow easy, secure
+.Nm
+access is to create a local server socket in
+.Pa /etc/ppp/ppp.conf
+(in the correct section) like this:
+.Bd -literal -offset indent
+set server /var/run/internet "" 0177
+.Ed
+.Pp
+This will instruct
+.Nm ppp
+to create a local domain socket, with srw------- permissions and no
+password, allowing access only to the user that invoked
+.Nm ppp .
+Refer to the
+.Xr ppp 8
+man page for further details.
+.Pp
+You can now create some easy-access scripts. To connect to the internet:
+.Bd -literal -offset indent
+#! /bin/sh
+test $# -eq 0 && time=300 || time=$1
+exec pppctl /var/run/internet set timeout $time\\; dial
+.Ed
+.Pp
+To disconnect:
+.Bd -literal -offset indent
+#! /bin/sh
+exec pppctl /var/run/internet set timeout 300\\; close
+.Ed
+.Pp
+To check if the line is up:
+.Bd -literal -offset indent
+#! /bin/sh
+pppctl -p '' -v /var/run/internet quit | grep ^PPP >/dev/null
+if [ $? -eq 0 ]; then
+ echo Link is up
+else
+ echo Link is down
+fi
+.Ed
+.Pp
+You can even make a generic script:
+.Bd -literal -offset indent
+#! /bin/sh
+exec pppctl /var/run/internet "$@"
+.Ed
+.Pp
+You could also use
+.Nm
+to control when dial-on-demand works. Suppose you want
+.Nm ppp
+to run all the time, but you want to prevent dial-out between 8pm and 8am
+each day. However, any connections active at 8pm should continue to remain
+active until they are closed or naturally time out.
+.Pp
+A
+.Xr cron 8
+entry for 8pm which runs
+.Bd -literal -offset indent
+pppctl /var/run/internet set filter dial 0 deny 0 0
+.Ed
+.Pp
+will block all further dial requests, and the corresponding 8am entry
+.Bd -literal -offset indent
+pppctl /var/run/internet set filter dial -1
+.Ed
+.Pp
+will allow them again.
+.Sh ENVIRONMENT
+The following environment variables are understood by
+.Nm
+when in interactive mode:
+.Bl -tag -width XXXXXXXXXX
+.It Dv EL_SIZE
+The number of history lines. The default is 20.
+.It Dv EL_EDITOR
+The edit mode. Only values of "emacs" and "vi" are accepted. Other values
+are silently ignored. This environment variable will override the
+.Ar bind -v
+and
+.Ar bind -e
+commands in
+.Pa ~/.editrc .
+.El
+.Sh SEE ALSO
+.Xr ps 1 ,
+.Xr editline 3 ,
+.Xr editrc 5 ,
+.Xr services 5 ,
+.Xr ppp 8
+.Sh HISTORY
+The
+.Nm
+command first appeared in
+.Fx 2.2.5 .
diff --git a/usr.sbin/pppctl/pppctl.c b/usr.sbin/pppctl/pppctl.c
new file mode 100644
index 0000000..a83354d
--- /dev/null
+++ b/usr.sbin/pppctl/pppctl.c
@@ -0,0 +1,649 @@
+/*-
+ * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+#include <netdb.h>
+
+#include <sys/time.h>
+#include <err.h>
+#include <errno.h>
+#include <histedit.h>
+#include <semaphore.h>
+#include <pthread.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#define LINELEN 2048
+
+/* Data passed to the threads we create */
+struct thread_data {
+ EditLine *edit; /* libedit stuff */
+ History *hist; /* libedit stuff */
+ pthread_t trm; /* Terminal thread (for pthread_kill()) */
+ int ppp; /* ppp descriptor */
+};
+
+/* Flags passed to Receive() */
+#define REC_PASSWD (1) /* Handle a password request from ppp */
+#define REC_SHOW (2) /* Show everything except prompts */
+#define REC_VERBOSE (4) /* Show everything */
+
+static char *passwd;
+static char *prompt; /* Tell libedit what the current prompt is */
+static int data = -1; /* setjmp() has been done when data != -1 */
+static jmp_buf pppdead; /* Jump the Terminal thread out of el_gets() */
+static int timetogo; /* Tell the Monitor thread to exit */
+static sem_t sem_select; /* select() co-ordination between threads */
+static int TimedOut; /* Set if our connect() timed out */
+static int want_sem_post; /* Need to let the Monitor thread in ? */
+
+/*
+ * How to use pppctl...
+ */
+static int
+usage()
+{
+ fprintf(stderr, "usage: pppctl [-v] [-t n] [-p passwd] "
+ "Port|LocalSock [command[;command]...]\n");
+ fprintf(stderr, " -v tells pppctl to output all"
+ " conversation\n");
+ fprintf(stderr, " -t n specifies a timeout of n"
+ " seconds when connecting (default 2)\n");
+ fprintf(stderr, " -p passwd specifies your password\n");
+ exit(1);
+}
+
+/*
+ * Handle the SIGALRM received due to a connect() timeout.
+ */
+static void
+Timeout(int Sig)
+{
+ TimedOut = 1;
+}
+
+/*
+ * A callback routine for libedit to find out what the current prompt is.
+ * All the work is done in Receive() below.
+ */
+static char *
+GetPrompt(EditLine *e)
+{
+ if (prompt == NULL)
+ prompt = "";
+ return prompt;
+}
+
+/*
+ * Receive data from the ppp descriptor.
+ * We also handle password prompts here (if asked via the `display' arg)
+ * and buffer what our prompt looks like (via the `prompt' global).
+ */
+static int
+Receive(int fd, int display)
+{
+ static char Buffer[LINELEN];
+ struct timeval t;
+ int Result;
+ char *last;
+ fd_set f;
+ int len;
+
+ FD_ZERO(&f);
+ FD_SET(fd, &f);
+ t.tv_sec = 0;
+ t.tv_usec = 100000;
+ prompt = Buffer;
+ len = 0;
+
+ while (Result = read(fd, Buffer+len, sizeof(Buffer)-len-1), Result != -1) {
+ if (Result == 0 && errno != EINTR) {
+ Result = -1;
+ break;
+ }
+ len += Result;
+ Buffer[len] = '\0';
+ if (len > 2 && !strcmp(Buffer+len-2, "> ")) {
+ prompt = strrchr(Buffer, '\n');
+ if (display & (REC_SHOW|REC_VERBOSE)) {
+ if (display & REC_VERBOSE)
+ last = Buffer+len-1;
+ else
+ last = prompt;
+ if (last) {
+ last++;
+ write(STDOUT_FILENO, Buffer, last-Buffer);
+ }
+ }
+ prompt = prompt == NULL ? Buffer : prompt+1;
+ for (last = Buffer+len-2; last > Buffer && *last != ' '; last--)
+ ;
+ if (last > Buffer+3 && !strncmp(last-3, " on", 3)) {
+ /* a password is required ! */
+ if (display & REC_PASSWD) {
+ /* password time */
+ if (!passwd)
+ passwd = getpass("Password: ");
+ sprintf(Buffer, "passwd %s\n", passwd);
+ memset(passwd, '\0', strlen(passwd));
+ if (display & REC_VERBOSE)
+ write(STDOUT_FILENO, Buffer, strlen(Buffer));
+ write(fd, Buffer, strlen(Buffer));
+ memset(Buffer, '\0', strlen(Buffer));
+ return Receive(fd, display & ~REC_PASSWD);
+ }
+ Result = 1;
+ } else
+ Result = 0;
+ break;
+ } else
+ prompt = "";
+ if (len == sizeof Buffer - 1) {
+ int flush;
+ if ((last = strrchr(Buffer, '\n')) == NULL)
+ /* Yeuch - this is one mother of a line ! */
+ flush = sizeof Buffer / 2;
+ else
+ flush = last - Buffer + 1;
+ write(STDOUT_FILENO, Buffer, flush);
+ strcpy(Buffer, Buffer + flush);
+ len -= flush;
+ }
+ if ((Result = select(fd + 1, &f, NULL, NULL, &t)) <= 0) {
+ if (len)
+ write(STDOUT_FILENO, Buffer, len);
+ break;
+ }
+ }
+
+ return Result;
+}
+
+/*
+ * Handle being told by the Monitor thread that there's data to be read
+ * on the ppp descriptor.
+ *
+ * Note, this is a signal handler - be careful of what we do !
+ */
+static void
+InputHandler(int sig)
+{
+ static char buf[LINELEN];
+ struct timeval t;
+ int len;
+ fd_set f;
+
+ if (data != -1) {
+ FD_ZERO(&f);
+ FD_SET(data, &f);
+ t.tv_sec = t.tv_usec = 0;
+
+ if (select(data + 1, &f, NULL, NULL, &t) > 0) {
+ len = read(data, buf, sizeof buf);
+
+ if (len > 0)
+ write(STDOUT_FILENO, buf, len);
+ else if (data != -1)
+ longjmp(pppdead, -1);
+ }
+
+ sem_post(&sem_select);
+ } else
+ /* Don't let the Monitor thread in 'till we've set ``data'' up again */
+ want_sem_post = 1;
+}
+
+/*
+ * This is a simple wrapper for el_gets(), allowing our SIGUSR1 signal
+ * handler (above) to take effect only after we've done a setjmp().
+ *
+ * We don't want it to do anything outside of here as we're going to
+ * service the ppp descriptor anyway.
+ */
+static const char *
+SmartGets(EditLine *e, int *count, int fd)
+{
+ const char *result;
+
+ if (setjmp(pppdead))
+ result = NULL;
+ else {
+ data = fd;
+ if (want_sem_post)
+ /* Let the Monitor thread in again */
+ sem_post(&sem_select);
+ result = el_gets(e, count);
+ }
+
+ data = -1;
+
+ return result;
+}
+
+/*
+ * The Terminal thread entry point.
+ *
+ * The bulk of the interactive work is done here. We read the terminal,
+ * write the results to our ppp descriptor and read the results back.
+ *
+ * While reading the terminal (using el_gets()), it's possible to take
+ * a SIGUSR1 from the Monitor thread, telling us that the ppp descriptor
+ * has some data. The data is read and displayed by the signal handler
+ * itself.
+ */
+static void *
+Terminal(void *v)
+{
+ struct sigaction act, oact;
+ struct thread_data *td;
+ const char *l;
+ int len;
+#ifndef __OpenBSD__
+ HistEvent hev = { 0, "" };
+#endif
+
+ act.sa_handler = InputHandler;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_RESTART;
+ sigaction(SIGUSR1, &act, &oact);
+
+ td = (struct thread_data *)v;
+ want_sem_post = 1;
+
+ while ((l = SmartGets(td->edit, &len, td->ppp))) {
+ if (len > 1)
+#ifdef __OpenBSD__
+ history(td->hist, H_ENTER, l);
+#else
+ history(td->hist, &hev, H_ENTER, l);
+#endif
+ write(td->ppp, l, len);
+ if (Receive(td->ppp, REC_SHOW) != 0)
+ break;
+ }
+
+ return NULL;
+}
+
+/*
+ * The Monitor thread entry point.
+ *
+ * This thread simply monitors our ppp descriptor. When there's something
+ * to read, a SIGUSR1 is sent to the Terminal thread.
+ *
+ * sem_select() is used by the Terminal thread to keep us from sending
+ * flurries of SIGUSR1s, and is used from the main thread to wake us up
+ * when it's time to exit.
+ */
+static void *
+Monitor(void *v)
+{
+ struct thread_data *td;
+ fd_set f;
+ int ret;
+
+ td = (struct thread_data *)v;
+ FD_ZERO(&f);
+ FD_SET(td->ppp, &f);
+
+ sem_wait(&sem_select);
+ while (!timetogo)
+ if ((ret = select(td->ppp + 1, &f, NULL, NULL, NULL)) > 0) {
+ pthread_kill(td->trm, SIGUSR1);
+ sem_wait(&sem_select);
+ }
+
+ return NULL;
+}
+
+/*
+ * Connect to ppp using either a local domain socket or a tcp socket.
+ *
+ * If we're given arguments, process them and quit, otherwise create two
+ * threads to handle interactive mode.
+ */
+int
+main(int argc, char **argv)
+{
+ struct servent *s;
+ struct hostent *h;
+ struct sockaddr *sock;
+ struct sockaddr_in ifsin;
+ struct sockaddr_un ifsun;
+ int n, socksz, arg, fd, len, verbose, save_errno, hide1, hide1off, hide2;
+ unsigned TimeoutVal;
+ char *DoneWord = "x", *next, *start;
+ struct sigaction act, oact;
+ void *thread_ret;
+ pthread_t mon;
+ char Command[LINELEN];
+ char Buffer[LINELEN];
+
+ verbose = 0;
+ TimeoutVal = 2;
+ hide1 = hide1off = hide2 = 0;
+
+ for (arg = 1; arg < argc; arg++)
+ if (*argv[arg] == '-') {
+ for (start = argv[arg] + 1; *start; start++)
+ switch (*start) {
+ case 't':
+ TimeoutVal = (unsigned)atoi
+ (start[1] ? start + 1 : argv[++arg]);
+ start = DoneWord;
+ break;
+
+ case 'v':
+ verbose = REC_VERBOSE;
+ break;
+
+ case 'p':
+ if (start[1]) {
+ hide1 = arg;
+ hide1off = start - argv[arg];
+ passwd = start + 1;
+ } else {
+ hide1 = arg;
+ hide1off = start - argv[arg];
+ passwd = argv[++arg];
+ hide2 = arg;
+ }
+ start = DoneWord;
+ break;
+
+ default:
+ usage();
+ }
+ }
+ else
+ break;
+
+
+ if (argc < arg + 1)
+ usage();
+
+ if (hide1) {
+ char title[1024];
+ int pos, harg;
+
+ for (harg = pos = 0; harg < argc; harg++)
+ if (harg == 0 || harg != hide2) {
+ if (harg == 0 || harg != hide1)
+ n = snprintf(title + pos, sizeof title - pos, "%s%s",
+ harg ? " " : "", argv[harg]);
+ else if (hide1off > 1)
+ n = snprintf(title + pos, sizeof title - pos, " %.*s",
+ hide1off, argv[harg]);
+ else
+ n = 0;
+ if (n < 0 || n >= sizeof title - pos)
+ break;
+ pos += n;
+ }
+#ifdef __FreeBSD__
+ setproctitle("-%s", title);
+#else
+ setproctitle("%s", title);
+#endif
+ }
+
+ if (*argv[arg] == '/') {
+ 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) {
+ warnx("%s: path too long", argv[arg]);
+ return 1;
+ }
+ ifsun.sun_family = AF_LOCAL;
+ strcpy(ifsun.sun_path, argv[arg]);
+
+ if (fd = socket(AF_LOCAL, SOCK_STREAM, 0), fd < 0) {
+ warnx("cannot create local domain socket");
+ return 2;
+ }
+ } 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)) {
+ warnx("cannot translate %s", host);
+ return 1;
+ }
+ } else if ((h = gethostbyname(host)) == 0) {
+ warnx("cannot resolve %s", 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) {
+ warnx("%s isn't a valid port or service!", port);
+ 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) {
+ warnx("cannot create internet socket");
+ 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) {
+ if (TimeoutVal) {
+ save_errno = errno;
+ alarm(0);
+ sigaction(SIGALRM, &oact, 0);
+ errno = save_errno;
+ }
+ if (TimedOut)
+ warnx("timeout: cannot connect to socket %s", argv[arg]);
+ else {
+ if (errno)
+ warn("cannot connect to socket %s", argv[arg]);
+ else
+ warnx("cannot connect to socket %s", argv[arg]);
+ }
+ 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:
+ passwd = NULL;
+ if (len == 0) {
+ struct thread_data td;
+ const char *env;
+ int size;
+#ifndef __OpenBSD__
+ HistEvent hev = { 0, "" };
+#endif
+
+ td.hist = history_init();
+ if ((env = getenv("EL_SIZE"))) {
+ size = atoi(env);
+ if (size < 0)
+ size = 20;
+ } else
+ size = 20;
+#ifdef __OpenBSD__
+ history(td.hist, H_EVENT, size);
+ td.edit = el_init("pppctl", stdin, stdout);
+#else
+ history(td.hist, &hev, H_SETSIZE, size);
+ td.edit = el_init("pppctl", stdin, stdout, stderr);
+#endif
+ el_source(td.edit, NULL);
+ el_set(td.edit, EL_PROMPT, GetPrompt);
+ if ((env = getenv("EL_EDITOR"))) {
+ if (!strcmp(env, "vi"))
+ el_set(td.edit, EL_EDITOR, "vi");
+ else if (!strcmp(env, "emacs"))
+ el_set(td.edit, EL_EDITOR, "emacs");
+ }
+ el_set(td.edit, EL_SIGNAL, 1);
+ el_set(td.edit, EL_HIST, history, (const char *)td.hist);
+
+ td.ppp = fd;
+ td.trm = NULL;
+
+ /*
+ * We create two threads. The Terminal thread does all the
+ * work while the Monitor thread simply tells the Terminal
+ * thread when ``fd'' becomes readable. The telling is done
+ * by sending a SIGUSR1 to the Terminal thread. The
+ * sem_select semaphore is used to prevent the monitor
+ * thread from firing excessive signals at the Terminal
+ * thread (it's abused for exit handling too - see below).
+ *
+ * The Terminal thread never uses td.trm !
+ */
+ sem_init(&sem_select, 0, 0);
+
+ pthread_create(&td.trm, NULL, Terminal, &td);
+ pthread_create(&mon, NULL, Monitor, &td);
+
+ /* Wait for the terminal thread to finish */
+ pthread_join(td.trm, &thread_ret);
+ fprintf(stderr, "Connection closed\n");
+
+ /* Get rid of the monitor thread by abusing sem_select */
+ timetogo = 1;
+ close(fd);
+ fd = -1;
+ sem_post(&sem_select);
+ pthread_join(mon, &thread_ret);
+
+ /* Restore our terminal and release resources */
+ el_end(td.edit);
+ history_end(td.hist);
+ sem_destroy(&sem_select);
+ } else {
+ start = Command;
+ do {
+ next = strchr(start, ';');
+ while (*start == ' ' || *start == '\t')
+ start++;
+ if (next)
+ *next = '\0';
+ strcpy(Buffer, start);
+ Buffer[sizeof(Buffer)-2] = '\0';
+ strcat(Buffer, "\n");
+ if (verbose)
+ write(STDOUT_FILENO, Buffer, strlen(Buffer));
+ write(fd, Buffer, strlen(Buffer));
+ if (Receive(fd, verbose | REC_SHOW) != 0) {
+ fprintf(stderr, "Connection closed\n");
+ break;
+ }
+ if (next)
+ start = ++next;
+ } while (next && *next);
+ if (verbose)
+ write(STDOUT_FILENO, "quit\n", 5);
+ write(fd, "quit\n", 5);
+ while (Receive(fd, verbose | REC_SHOW) == 0)
+ ;
+ if (verbose)
+ puts("");
+ }
+ break;
+
+ default:
+ warnx("ppp is not responding");
+ break;
+ }
+
+ if (fd != -1)
+ close(fd);
+
+ return 0;
+}
diff --git a/usr.sbin/pppd/Makefile b/usr.sbin/pppd/Makefile
new file mode 100644
index 0000000..a628fb8
--- /dev/null
+++ b/usr.sbin/pppd/Makefile
@@ -0,0 +1,50 @@
+# $FreeBSD$
+
+# as per handbook policies section
+MAINTAINER= peter@freebsd.org
+
+PROG= pppd
+MAN= pppd.8
+SRCS= main.c magic.c fsm.c lcp.c ipcp.c ipxcp.c upap.c chap.c ccp.c \
+ demand.c auth.c options.c sys-bsd.c
+BINMODE=4550
+BINOWN= root
+BINGRP= dialer
+
+CFLAGS+= -DHAVE_PATHS_H
+
+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.
+.if exists(${.CURDIR}/../../secure) && !defined(NOCRYPT) && !defined(NOSECURE) && !defined(NO_OPENSSL) && !defined(RELEASE_CRUNCH)
+DISTRIBUTION=crypto
+CFLAGS+= -DCHAPMS
+SRCS+= chap_ms.c
+LDADD+= -lcrypto
+DPADD+= ${LIBCRYPTO}
+.endif
+
+.if defined(RELEASE_CRUNCH)
+# We must create these objects because crunchgen will link them,
+# and we don't want any unused symbols to spoil the final link.
+SRCS+= chap_ms.c
+chap_ms.o:
+ >null_${.PREFIX}.c
+ cc -c -o ${.TARGET} null_${.PREFIX}.c
+.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pppd/RELNOTES b/usr.sbin/pppd/RELNOTES
new file mode 100644
index 0000000..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..b2d43f8
--- /dev/null
+++ b/usr.sbin/pppd/auth.c
@@ -0,0 +1,1637 @@
+/*
+ * auth.c - PPP authentication and phase control.
+ *
+ * Copyright (c) 1993 The Australian National University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Australian National University. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char rcsid[] = "$FreeBSD$";
+#endif
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <paths.h>
+#include <pwd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <utmp.h>
+#include <fcntl.h>
+#if defined(_PATH_LASTLOG) && defined(_linux_)
+#include <lastlog.h>
+#endif
+
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/time.h>
+#include <utmp.h>
+
+#ifdef USE_PAM
+#include <security/pam_appl.h>
+#endif
+
+#ifdef HAS_SHADOW
+#include <shadow.h>
+#ifndef PW_PPP
+#define PW_PPP PW_LOGIN
+#endif
+#endif
+
+#include "pppd.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ipcp.h"
+#include "upap.h"
+#include "chap.h"
+#ifdef CBCP_SUPPORT
+#include "cbcp.h"
+#endif
+#include "pathnames.h"
+
+/* Used for storing a sequence of words. Usually malloced. */
+struct wordlist {
+ struct wordlist *next;
+ char word[1];
+};
+
+/* Bits in scan_authfile return value */
+#define NONWILD_SERVER 1
+#define NONWILD_CLIENT 2
+
+#define ISWILD(word) (word[0] == '*' && word[1] == 0)
+
+#define FALSE 0
+#define TRUE 1
+
+/* The name by which the peer authenticated itself to us. */
+char peer_authname[MAXNAMELEN];
+
+/* Records which authentication operations haven't completed yet. */
+static int auth_pending[NUM_PPP];
+
+/* Set if we have successfully called plogin() */
+static int logged_in;
+
+/* Set if not wild or blank */
+static int non_wildclient;
+
+/* Set if we have run the /etc/ppp/auth-up script. */
+static int did_authup;
+
+/* List of addresses which the peer may use. */
+static struct wordlist *addresses[NUM_PPP];
+
+/* Number of network protocols which we have opened. */
+static int num_np_open;
+
+/* Number of network protocols which have come up. */
+static int num_np_up;
+
+/* Set if we got the contents of passwd[] from the pap-secrets file. */
+static int passwd_from_file;
+
+/* Bits in auth_pending[] */
+#define PAP_WITHPEER 1
+#define PAP_PEER 2
+#define CHAP_WITHPEER 4
+#define CHAP_PEER 8
+
+extern char *crypt __P((const char *, const char *));
+
+/* Prototypes for procedures local to this file. */
+
+static void network_phase __P((int));
+static void check_idle __P((void *));
+static void connect_time_expired __P((void *));
+static int plogin __P((char *, char *, char **, int *));
+static void plogout __P((void));
+static int null_login __P((int));
+static int get_pap_passwd __P((char *));
+static int have_pap_secret __P((void));
+static int have_chap_secret __P((char *, char *, u_int32_t));
+static int ip_addr_check __P((u_int32_t, struct wordlist *));
+static int scan_authfile __P((FILE *, char *, char *, u_int32_t, char *,
+ struct wordlist **, char *));
+static void free_wordlist __P((struct wordlist *));
+static void auth_set_ip_addr __P((int));
+static void auth_script __P((char *));
+static void set_allowed_addrs __P((int, struct wordlist *));
+
+/*
+ * An Open on LCP has requested a change from Dead to Establish phase.
+ * Do what's necessary to bring the physical layer up.
+ */
+void
+link_required(unit)
+ int unit;
+{
+}
+
+/*
+ * LCP has terminated the link; go to the Dead phase and take the
+ * physical layer down.
+ */
+void
+link_terminated(unit)
+ int unit;
+{
+ extern time_t etime, stime;
+ extern int minutes;
+
+ if (phase == PHASE_DEAD)
+ return;
+ if (logged_in)
+ plogout();
+ phase = PHASE_DEAD;
+ etime = time((time_t *) NULL);
+ minutes = (etime-stime)/60;
+ syslog(LOG_NOTICE, "Connection terminated, connected for %d minutes\n",
+ minutes > 1 ? minutes : 1);
+}
+
+/*
+ * LCP has gone down; it will either die or try to re-establish.
+ */
+void
+link_down(unit)
+ int unit;
+{
+ int i;
+ struct protent *protp;
+
+ if (did_authup) {
+ auth_script(_PATH_AUTHDOWN);
+ did_authup = 0;
+ }
+ for (i = 0; (protp = protocols[i]) != NULL; ++i) {
+ if (!protp->enabled_flag)
+ continue;
+ if (protp->protocol != PPP_LCP && protp->lowerdown != NULL)
+ (*protp->lowerdown)(unit);
+ if (protp->protocol < 0xC000 && protp->close != NULL)
+ (*protp->close)(unit, "LCP down");
+ }
+ num_np_open = 0;
+ num_np_up = 0;
+ if (phase != PHASE_DEAD)
+ phase = PHASE_TERMINATE;
+}
+
+/*
+ * The link is established.
+ * Proceed to the Dead, Authenticate or Network phase as appropriate.
+ */
+void
+link_established(unit)
+ int unit;
+{
+ int auth;
+ lcp_options *wo = &lcp_wantoptions[unit];
+ lcp_options *go = &lcp_gotoptions[unit];
+ lcp_options *ho = &lcp_hisoptions[unit];
+ int i;
+ struct protent *protp;
+
+ /*
+ * Tell higher-level protocols that LCP is up.
+ */
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (protp->protocol != PPP_LCP && protp->enabled_flag
+ && protp->lowerup != NULL)
+ (*protp->lowerup)(unit);
+
+ if (auth_required && !(go->neg_chap || go->neg_upap)) {
+ /*
+ * We wanted the peer to authenticate itself, and it refused:
+ * treat it as though it authenticated with PAP using a username
+ * of "" and a password of "". If that's not OK, boot it out.
+ */
+ if (!wo->neg_upap || !null_login(unit)) {
+ syslog(LOG_WARNING, "peer refused to authenticate");
+ lcp_close(unit, "peer refused to authenticate");
+ return;
+ }
+ }
+
+ phase = PHASE_AUTHENTICATE;
+ auth = 0;
+ if (go->neg_chap) {
+ ChapAuthPeer(unit, our_name, go->chap_mdtype);
+ auth |= CHAP_PEER;
+ } else if (go->neg_upap) {
+ upap_authpeer(unit);
+ auth |= PAP_PEER;
+ }
+ if (ho->neg_chap) {
+ ChapAuthWithPeer(unit, user, ho->chap_mdtype);
+ auth |= CHAP_WITHPEER;
+ } else if (ho->neg_upap) {
+ if (passwd[0] == 0) {
+ passwd_from_file = 1;
+ if (!get_pap_passwd(passwd))
+ syslog(LOG_ERR, "No secret found for PAP login");
+ }
+ upap_authwithpeer(unit, user, passwd);
+ auth |= PAP_WITHPEER;
+ }
+ auth_pending[unit] = auth;
+
+ if (!auth)
+ network_phase(unit);
+}
+
+/*
+ * Proceed to the network phase.
+ */
+static void
+network_phase(unit)
+ int unit;
+{
+ int i;
+ struct protent *protp;
+ lcp_options *go = &lcp_gotoptions[unit];
+
+ /*
+ * If the peer had to authenticate, run the auth-up script now.
+ */
+ if ((go->neg_chap || go->neg_upap) && !did_authup) {
+ auth_script(_PATH_AUTHUP);
+ did_authup = 1;
+ }
+
+#ifdef CBCP_SUPPORT
+ /*
+ * If we negotiated callback, do it now.
+ */
+ if (go->neg_cbcp) {
+ phase = PHASE_CALLBACK;
+ (*cbcp_protent.open)(unit);
+ return;
+ }
+#endif
+
+ phase = PHASE_NETWORK;
+#if 0
+ if (!demand)
+ set_filters(&pass_filter, &active_filter);
+#endif
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (protp->protocol < 0xC000 && protp->enabled_flag
+ && protp->open != NULL) {
+ (*protp->open)(unit);
+ if (protp->protocol != PPP_CCP)
+ ++num_np_open;
+ }
+
+ if (num_np_open == 0)
+ /* nothing to do */
+ lcp_close(0, "No network protocols running");
+}
+
+/*
+ * The peer has failed to authenticate himself using `protocol'.
+ */
+void
+auth_peer_fail(unit, protocol)
+ int unit, protocol;
+{
+ /*
+ * Authentication failure: take the link down
+ */
+ lcp_close(unit, "Authentication failed");
+}
+
+/*
+ * The peer has been successfully authenticated using `protocol'.
+ */
+void
+auth_peer_success(unit, protocol, name, namelen)
+ int unit, protocol;
+ char *name;
+ int namelen;
+{
+ int bit;
+
+ switch (protocol) {
+ case PPP_CHAP:
+ bit = CHAP_PEER;
+ break;
+ case PPP_PAP:
+ bit = PAP_PEER;
+ break;
+ default:
+ syslog(LOG_WARNING, "auth_peer_success: unknown protocol %x",
+ protocol);
+ return;
+ }
+
+ /*
+ * Save the authenticated name of the peer for later.
+ */
+ if (namelen > sizeof(peer_authname) - 1)
+ namelen = sizeof(peer_authname) - 1;
+ BCOPY(name, peer_authname, namelen);
+ peer_authname[namelen] = 0;
+
+ /*
+ * If we have overridden addresses based on auth info
+ * then set that information now before continuing.
+ */
+ auth_set_ip_addr(unit);
+
+ script_setenv("PEERNAME", peer_authname);
+
+ /*
+ * If there is no more authentication still to be done,
+ * proceed to the network (or callback) phase.
+ */
+ if ((auth_pending[unit] &= ~bit) == 0)
+ network_phase(unit);
+}
+
+/*
+ * We have failed to authenticate ourselves to the peer using `protocol'.
+ */
+void
+auth_withpeer_fail(unit, protocol)
+ int unit, protocol;
+{
+ if (passwd_from_file)
+ BZERO(passwd, MAXSECRETLEN);
+ /*
+ * We've failed to authenticate ourselves to our peer.
+ * He'll probably take the link down, and there's not much
+ * we can do except wait for that.
+ */
+}
+
+/*
+ * We have successfully authenticated ourselves with the peer using `protocol'.
+ */
+void
+auth_withpeer_success(unit, protocol)
+ int unit, protocol;
+{
+ int bit;
+
+ switch (protocol) {
+ case PPP_CHAP:
+ bit = CHAP_WITHPEER;
+ break;
+ case PPP_PAP:
+ if (passwd_from_file)
+ BZERO(passwd, MAXSECRETLEN);
+ bit = PAP_WITHPEER;
+ break;
+ default:
+ syslog(LOG_WARNING, "auth_peer_success: unknown protocol %x",
+ protocol);
+ bit = 0;
+ }
+
+ /*
+ * If we have overridden addresses based on auth info
+ * then set that information now before continuing.
+ */
+ auth_set_ip_addr(unit);
+
+ /*
+ * If there is no more authentication still being done,
+ * proceed to the network (or callback) phase.
+ */
+ if ((auth_pending[unit] &= ~bit) == 0)
+ network_phase(unit);
+}
+
+
+/*
+ * np_up - a network protocol has come up.
+ */
+void
+np_up(unit, proto)
+ int unit, proto;
+{
+ if (num_np_up == 0) {
+ /*
+ * At this point we consider that the link has come up successfully.
+ */
+ need_holdoff = 0;
+
+ if (idle_time_limit > 0)
+ TIMEOUT(check_idle, NULL, idle_time_limit);
+
+ /*
+ * Set a timeout to close the connection once the maximum
+ * connect time has expired.
+ */
+ if (maxconnect > 0)
+ TIMEOUT(connect_time_expired, 0, maxconnect);
+
+ /*
+ * Detach now, if the updetach option was given.
+ */
+ if (nodetach == -1)
+ detach();
+ }
+ ++num_np_up;
+}
+
+/*
+ * np_down - a network protocol has gone down.
+ */
+void
+np_down(unit, proto)
+ int unit, proto;
+{
+ if (--num_np_up == 0 && idle_time_limit > 0) {
+ UNTIMEOUT(check_idle, NULL);
+ }
+}
+
+/*
+ * np_finished - a network protocol has finished using the link.
+ */
+void
+np_finished(unit, proto)
+ int unit, proto;
+{
+ if (--num_np_open <= 0) {
+ /* no further use for the link: shut up shop. */
+ lcp_close(0, "No network protocols running");
+ }
+}
+
+/*
+ * check_idle - check whether the link has been idle for long
+ * enough that we can shut it down.
+ */
+static void
+check_idle(arg)
+ void *arg;
+{
+ struct ppp_idle idle;
+ time_t itime;
+
+ if (!get_idle_time(0, &idle))
+ return;
+ itime = MIN(idle.xmit_idle, idle.recv_idle);
+ if (itime >= idle_time_limit) {
+ /* link is idle: shut it down. */
+ syslog(LOG_INFO, "Terminating connection due to lack of activity.");
+ lcp_close(0, "Link inactive");
+ } else {
+ TIMEOUT(check_idle, NULL, idle_time_limit - itime);
+ }
+}
+
+/*
+ * connect_time_expired - log a message and close the connection.
+ */
+static void
+connect_time_expired(arg)
+ void *arg;
+{
+ syslog(LOG_INFO, "Connect time expired");
+ lcp_close(0, "Connect time expired"); /* Close connection */
+}
+
+/*
+ * auth_check_options - called to check authentication options.
+ */
+void
+auth_check_options()
+{
+ lcp_options *wo = &lcp_wantoptions[0];
+ int can_auth;
+ ipcp_options *ipwo = &ipcp_wantoptions[0];
+ u_int32_t remote;
+
+ /* Default our_name to hostname, and user to our_name */
+ if (our_name[0] == 0 || usehostname)
+ strcpy(our_name, hostname);
+ if (user[0] == 0)
+ strcpy(user, our_name);
+
+ /* If authentication is required, ask peer for CHAP or PAP. */
+ if (auth_required && !wo->neg_chap && !wo->neg_upap) {
+ wo->neg_chap = 1;
+ wo->neg_upap = 1;
+ }
+
+ /*
+ * Check whether we have appropriate secrets to use
+ * to authenticate the peer.
+ */
+ can_auth = wo->neg_upap && (uselogin || have_pap_secret());
+ if (!can_auth && wo->neg_chap) {
+ remote = ipwo->accept_remote? 0: ipwo->hisaddr;
+ can_auth = have_chap_secret(remote_name, our_name, remote);
+ }
+
+ if (auth_required && !can_auth) {
+ option_error("peer authentication required but no suitable secret(s) found\n");
+ if (remote_name[0] == 0)
+ option_error("for authenticating any peer to us (%s)\n", our_name);
+ else
+ option_error("for authenticating peer %s to us (%s)\n",
+ remote_name, our_name);
+ exit(1);
+ }
+
+ /*
+ * Check whether the user tried to override certain values
+ * set by root.
+ */
+ if (!auth_required && auth_req_info.priv > 0) {
+ if (!default_device && devnam_info.priv == 0) {
+ option_error("can't override device name when noauth option used");
+ exit(1);
+ }
+ if ((connector != NULL && connector_info.priv == 0)
+ || (disconnector != NULL && disconnector_info.priv == 0)
+ || (welcomer != NULL && welcomer_info.priv == 0)) {
+ option_error("can't override connect, disconnect or welcome");
+ option_error("option values when noauth option used");
+ exit(1);
+ }
+ }
+}
+
+/*
+ * auth_reset - called when LCP is starting negotiations to recheck
+ * authentication options, i.e. whether we have appropriate secrets
+ * to use for authenticating ourselves and/or the peer.
+ */
+void
+auth_reset(unit)
+ int unit;
+{
+ lcp_options *go = &lcp_gotoptions[unit];
+ lcp_options *ao = &lcp_allowoptions[0];
+ ipcp_options *ipwo = &ipcp_wantoptions[0];
+ u_int32_t remote;
+
+ ao->neg_upap = !refuse_pap && (passwd[0] != 0 || get_pap_passwd(NULL));
+ ao->neg_chap = !refuse_chap
+ && have_chap_secret(user, remote_name, (u_int32_t)0);
+
+ if (go->neg_upap && !uselogin && !have_pap_secret())
+ go->neg_upap = 0;
+ if (go->neg_chap) {
+ remote = ipwo->accept_remote? 0: ipwo->hisaddr;
+ if (!have_chap_secret(remote_name, our_name, remote))
+ go->neg_chap = 0;
+ }
+}
+
+
+/*
+ * check_passwd - Check the user name and passwd against the PAP secrets
+ * file. If requested, also check against the system password database,
+ * and login the user if OK.
+ *
+ * returns:
+ * UPAP_AUTHNAK: Authentication failed.
+ * UPAP_AUTHACK: Authentication succeeded.
+ * In either case, msg points to an appropriate message.
+ */
+int
+check_passwd(unit, auser, userlen, apasswd, passwdlen, msg, msglen)
+ int unit;
+ char *auser;
+ int userlen;
+ char *apasswd;
+ int passwdlen;
+ char **msg;
+ int *msglen;
+{
+ int ret;
+ char *filename;
+ FILE *f;
+ struct wordlist *addrs;
+ u_int32_t remote;
+ ipcp_options *ipwo = &ipcp_wantoptions[unit];
+ char passwd[256], user[256];
+ char secret[MAXWORDLEN];
+ static int attempts = 0;
+ int len;
+
+ /*
+ * Make copies of apasswd and auser, then null-terminate them.
+ */
+ len = MIN(passwdlen, sizeof(passwd) - 1);
+ BCOPY(apasswd, passwd, len);
+ passwd[len] = '\0';
+ len = MIN(userlen, sizeof(user) - 1);
+ BCOPY(auser, user, len);
+ user[len] = '\0';
+ *msg = (char *) 0;
+
+ /*
+ * Open the file of pap secrets and scan for a suitable secret
+ * for authenticating this user.
+ */
+ filename = _PATH_UPAPFILE;
+ addrs = NULL;
+ ret = UPAP_AUTHACK;
+ f = fopen(filename, "r");
+ if (f == NULL) {
+ syslog(LOG_ERR, "Can't open PAP password file %s: %m", filename);
+ ret = UPAP_AUTHNAK;
+
+ } else {
+ check_access(f, filename);
+ remote = ipwo->accept_remote? 0: ipwo->hisaddr;
+ if (scan_authfile(f, user, our_name, remote,
+ secret, &addrs, filename) < 0) {
+ warn("no PAP secret found for %s", user);
+ } else {
+ if (secret[0] != 0) {
+ /* password given in pap-secrets - must match */
+ if ((cryptpap || strcmp(passwd, secret) != 0)
+ && strcmp(crypt(passwd, secret), secret) != 0) {
+ ret = UPAP_AUTHNAK;
+ warn("PAP authentication failure for %s", user);
+ }
+ }
+ }
+ fclose(f);
+ }
+
+ if (uselogin && ret == UPAP_AUTHACK) {
+ ret = plogin(user, passwd, msg, msglen);
+ if (ret == UPAP_AUTHNAK) {
+ syslog(LOG_WARNING, "PAP login failure for %s", user);
+ }
+ }
+
+ if (ret == UPAP_AUTHNAK) {
+ if (*msg == (char *) 0)
+ *msg = "Login incorrect";
+ *msglen = strlen(*msg);
+ /*
+ * Frustrate passwd stealer programs.
+ * Allow 10 tries, but start backing off after 3 (stolen from login).
+ * On 10'th, drop the connection.
+ */
+ if (attempts++ >= 10) {
+ syslog(LOG_WARNING, "%d LOGIN FAILURES ON %s, %s",
+ attempts, devnam, user);
+ quit();
+ }
+ if (attempts > 3)
+ sleep((u_int) (attempts - 3) * 5);
+ if (addrs != NULL)
+ free_wordlist(addrs);
+
+ } else {
+ attempts = 0; /* Reset count */
+ if (*msg == (char *) 0)
+ *msg = "Login ok";
+ *msglen = strlen(*msg);
+ set_allowed_addrs(unit, addrs);
+ }
+
+ BZERO(passwd, sizeof(passwd));
+ BZERO(secret, sizeof(secret));
+
+ return ret;
+}
+
+/*
+ * Check if an "entry" is in the file "fname" - used by ppplogin.
+ * Taken from libexec/ftpd/ftpd.c
+ * Returns: 0 if not found, 1 if found, 2 if file can't be opened for reading.
+ */
+static int
+checkfile(fname, name)
+ char *fname;
+ char *name;
+{
+ FILE *fd;
+ int found = 0;
+ char *p, line[BUFSIZ];
+
+ if ((fd = fopen(fname, "r")) != NULL) {
+ while (fgets(line, sizeof(line), fd) != NULL)
+ if ((p = strchr(line, '\n')) != NULL) {
+ *p = '\0';
+ if (line[0] == '#')
+ continue;
+ if (strcmp(line, name) == 0) {
+ found = 1;
+ break;
+ }
+ }
+ (void) fclose(fd);
+ } else {
+ return(2);
+ }
+ return (found);
+}
+
+/*
+ * This function is needed for PAM.
+ */
+
+#ifdef USE_PAM
+static char *PAM_username = "";
+static char *PAM_password = "";
+
+#ifdef PAM_ESTABLISH_CRED /* new PAM defines :(^ */
+#define MY_PAM_STRERROR(err_code) (char *) pam_strerror(pamh,err_code)
+#else
+#define MY_PAM_STRERROR(err_code) (char *) pam_strerror(err_code)
+#endif
+
+static int pam_conv (int num_msg,
+ const struct pam_message **msg,
+ struct pam_response **resp,
+ void *appdata_ptr)
+{
+ int count = 0, replies = 0;
+ struct pam_response *reply = NULL;
+ int size = 0;
+
+ for (count = 0; count < num_msg; count++)
+ {
+ size += sizeof (struct pam_response);
+ reply = realloc (reply, size); /* ANSI: is malloc() if reply==NULL */
+ if (!reply)
+ return PAM_CONV_ERR;
+
+ switch (msg[count]->msg_style)
+ {
+ case PAM_PROMPT_ECHO_ON:
+ reply[replies].resp_retcode = PAM_SUCCESS;
+ reply[replies++].resp = strdup(PAM_username); /* never NULL */
+ break;
+
+ case PAM_PROMPT_ECHO_OFF:
+ reply[replies].resp_retcode = PAM_SUCCESS;
+ reply[replies++].resp = strdup(PAM_password); /* never NULL */
+ break;
+
+ case PAM_TEXT_INFO:
+ reply[replies].resp_retcode = PAM_SUCCESS;
+ reply[replies++].resp = NULL;
+ break;
+
+ case PAM_ERROR_MSG:
+ default:
+ free (reply);
+ return PAM_CONV_ERR;
+ }
+ }
+
+ if (resp)
+ *resp = reply;
+ else
+ free (reply);
+
+ return PAM_SUCCESS;
+}
+#endif
+
+/*
+ * plogin - Check the user name and password against the system
+ * password database, and login the user if OK.
+ *
+ * returns:
+ * UPAP_AUTHNAK: Login failed.
+ * UPAP_AUTHACK: Login succeeded.
+ * In either case, msg points to an appropriate message.
+ *
+ * UPAP_AUTHACK should only be returned *after* wtmp and utmp are updated.
+ */
+
+static int
+plogin(user, passwd, msg, msglen)
+ char *user;
+ char *passwd;
+ char **msg;
+ int *msglen;
+{
+
+#ifdef USE_PAM
+
+ struct pam_conv pam_conversation;
+ pam_handle_t *pamh;
+ int pam_error;
+/*
+ * Fill the pam_conversion structure
+ */
+ memset (&pam_conversation, '\0', sizeof (struct pam_conv));
+ pam_conversation.conv = &pam_conv;
+
+ pam_error = pam_start ("ppp", user, &pam_conversation, &pamh);
+
+ if (pam_error != PAM_SUCCESS) {
+ *msg = MY_PAM_STRERROR (pam_error);
+ return UPAP_AUTHNAK;
+ }
+/*
+ * Define the fields for the credintial validation
+ */
+ (void) pam_set_item (pamh, PAM_TTY, devnam);
+ PAM_username = user;
+ PAM_password = passwd;
+/*
+ * Validate the user
+ */
+ pam_error = pam_authenticate (pamh, PAM_SILENT);
+ if (pam_error == PAM_SUCCESS) {
+ pam_error = pam_acct_mgmt (pamh, PAM_SILENT);
+
+ /* start a session for this user. Session closed when link ends. */
+ if (pam_error == PAM_SUCCESS)
+ (void) pam_open_session (pamh, PAM_SILENT);
+ }
+
+ *msg = MY_PAM_STRERROR (pam_error);
+
+ PAM_username =
+ PAM_password = "";
+/*
+ * Clean up the mess
+ */
+ (void) pam_end (pamh, pam_error);
+
+ if (pam_error != PAM_SUCCESS)
+ return UPAP_AUTHNAK;
+/*
+ * Use the non-PAM methods directly
+ */
+#else /* #ifdef USE_PAM */
+
+ struct passwd *pw;
+ struct utmp utmp;
+ struct timeval tp;
+ char *tty;
+
+#ifdef HAS_SHADOW
+ struct spwd *spwd;
+ struct spwd *getspnam();
+#endif
+
+ pw = getpwnam(user);
+ endpwent();
+ if (pw == NULL) {
+ return (UPAP_AUTHNAK);
+ }
+/*
+ * Check that the user is not listed in /etc/ppp/ppp.deny
+ * and that the user's shell is listed in /etc/ppp/ppp.shells
+ * if /etc/ppp/ppp.shells exists.
+ */
+
+ if (checkfile(_PATH_PPPDENY, user) == 1) {
+ syslog(LOG_WARNING, "upap user %s: login denied in %s",
+ user, _PATH_PPPDENY);
+ return (UPAP_AUTHNAK);
+ }
+
+ if (checkfile(_PATH_PPPSHELLS, pw->pw_shell) == 0) {
+ syslog(LOG_WARNING, "upap user %s: shell %s not in %s",
+ user, pw->pw_shell, _PATH_PPPSHELLS);
+ return (UPAP_AUTHNAK);
+ }
+
+#ifdef HAS_SHADOW
+ spwd = getspnam(user);
+ endspent();
+ if (spwd) {
+ /* check the age of the password entry */
+ long now = time(NULL) / 86400L;
+
+ if ((spwd->sp_expire > 0 && now >= spwd->sp_expire)
+ || ((spwd->sp_max >= 0 && spwd->sp_max < 10000)
+ && spwd->sp_lstchg >= 0
+ && now >= spwd->sp_lstchg + spwd->sp_max)) {
+ syslog(LOG_WARNING, "Password for %s has expired", user);
+ return (UPAP_AUTHNAK);
+ }
+ pw->pw_passwd = spwd->sp_pwdp;
+ }
+#endif
+
+ /*
+ * If no passwd, don't let them login.
+ */
+ if (pw->pw_passwd == NULL || *pw->pw_passwd == '\0'
+ || strcmp(crypt(passwd, pw->pw_passwd), pw->pw_passwd) != 0)
+ return (UPAP_AUTHNAK);
+
+ if (pw->pw_expire) {
+ (void)gettimeofday(&tp, (struct timezone *)NULL);
+ if (tp.tv_sec >= pw->pw_expire) {
+ syslog(LOG_INFO, "pap user %s account expired", user);
+ return (UPAP_AUTHNAK);
+ }
+ }
+
+ /* These functions are not enabled for PAM. The reason for this is that */
+ /* there is not necessarily a "passwd" entry for this user. That is */
+ /* real purpose of 'PAM' -- to virtualize the account data from the */
+ /* application. If you want to do the same thing, write the entry in */
+ /* the 'session' hook. */
+
+ /* Log in wtmp and utmp using login() */
+
+ tty = devnam;
+ if (strncmp(tty, _PATH_DEV, sizeof _PATH_DEV - 1) == 0)
+ tty += 5;
+
+ if (logout(tty)) /* Already entered (by login?) */
+ logwtmp(tty, "", "");
+
+#if defined(_PATH_LASTLOG)
+ {
+ struct lastlog ll;
+ int fd;
+
+ if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) {
+ (void)lseek(fd, (off_t)(pw->pw_uid * sizeof(ll)), SEEK_SET);
+ memset((void *)&ll, 0, sizeof(ll));
+ (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, _PATH_DEV, sizeof _PATH_DEV - 1) == 0)
+ tty += 5;
+ logwtmp(tty, "", ""); /* Wipe out wtmp logout entry */
+ logout(tty); /* Wipe out utmp */
+#endif
+
+ logged_in = FALSE;
+}
+
+
+/*
+ * null_login - Check if a username of "" and a password of "" are
+ * acceptable, and iff so, set the list of acceptable IP addresses
+ * and return 1.
+ */
+static int
+null_login(unit)
+ int unit;
+{
+ char *filename;
+ FILE *f;
+ int i, ret;
+ struct wordlist *addrs;
+ char secret[MAXWORDLEN];
+
+ /*
+ * Open the file of pap secrets and scan for a suitable secret.
+ * We don't accept a wildcard client.
+ */
+ filename = _PATH_UPAPFILE;
+ addrs = NULL;
+ f = fopen(filename, "r");
+ if (f == NULL)
+ return 0;
+ check_access(f, filename);
+
+ i = scan_authfile(f, "", our_name, (u_int32_t)0, secret, &addrs, filename);
+ ret = i >= 0 && (i & NONWILD_CLIENT) != 0 && secret[0] == 0;
+ BZERO(secret, sizeof(secret));
+
+ if (ret)
+ set_allowed_addrs(unit, addrs);
+ else
+ free_wordlist(addrs);
+
+ fclose(f);
+ return ret;
+}
+
+
+/*
+ * get_pap_passwd - get a password for authenticating ourselves with
+ * our peer using PAP. Returns 1 on success, 0 if no suitable password
+ * could be found.
+ */
+static int
+get_pap_passwd(passwd)
+ char *passwd;
+{
+ char *filename;
+ FILE *f;
+ int ret;
+ struct wordlist *addrs;
+ char secret[MAXWORDLEN];
+
+ filename = _PATH_UPAPFILE;
+ addrs = NULL;
+ f = fopen(filename, "r");
+ if (f == NULL)
+ return 0;
+ check_access(f, filename);
+ ret = scan_authfile(f, user,
+ remote_name[0]? remote_name: NULL,
+ (u_int32_t)0, secret, NULL, filename);
+ fclose(f);
+ if (ret < 0)
+ return 0;
+ if (passwd != NULL) {
+ strncpy(passwd, secret, MAXSECRETLEN);
+ passwd[MAXSECRETLEN-1] = 0;
+ }
+ BZERO(secret, sizeof(secret));
+ return 1;
+}
+
+
+/*
+ * have_pap_secret - check whether we have a PAP file with any
+ * secrets that we could possibly use for authenticating the peer.
+ */
+static int
+have_pap_secret()
+{
+ FILE *f;
+ int ret;
+ char *filename;
+ ipcp_options *ipwo = &ipcp_wantoptions[0];
+ u_int32_t remote;
+
+ filename = _PATH_UPAPFILE;
+ f = fopen(filename, "r");
+ if (f == NULL)
+ return 0;
+
+ remote = ipwo->accept_remote? 0: ipwo->hisaddr;
+ ret = scan_authfile(f, NULL, our_name, remote, NULL, NULL, filename);
+ fclose(f);
+ if (ret < 0)
+ return 0;
+
+ return 1;
+}
+
+
+/*
+ * have_chap_secret - check whether we have a CHAP file with a
+ * secret that we could possibly use for authenticating `client'
+ * on `server'. Either can be the null string, meaning we don't
+ * know the identity yet.
+ */
+static int
+have_chap_secret(client, server, remote)
+ char *client;
+ char *server;
+ u_int32_t remote;
+{
+ FILE *f;
+ int ret;
+ char *filename;
+
+ filename = _PATH_CHAPFILE;
+ f = fopen(filename, "r");
+ if (f == NULL)
+ return 0;
+
+ if (client[0] == 0)
+ client = NULL;
+ else if (server[0] == 0)
+ server = NULL;
+
+ ret = scan_authfile(f, client, server, remote, NULL, NULL, filename);
+ fclose(f);
+ if (ret < 0)
+ return 0;
+
+ return 1;
+}
+
+
+/*
+ * get_secret - open the CHAP secret file and return the secret
+ * for authenticating the given client on the given server.
+ * (We could be either client or server).
+ */
+int
+get_secret(unit, client, server, secret, secret_len, save_addrs)
+ int unit;
+ char *client;
+ char *server;
+ char *secret;
+ int *secret_len;
+ int save_addrs;
+{
+ FILE *f;
+ int ret, len;
+ char *filename;
+ struct wordlist *addrs;
+ char secbuf[MAXWORDLEN];
+
+ filename = _PATH_CHAPFILE;
+ addrs = NULL;
+ secbuf[0] = 0;
+
+ f = fopen(filename, "r");
+ if (f == NULL) {
+ syslog(LOG_ERR, "Can't open chap secret file %s: %m", filename);
+ return 0;
+ }
+ check_access(f, filename);
+
+ ret = scan_authfile(f, client, server, (u_int32_t)0,
+ secbuf, &addrs, filename);
+ fclose(f);
+ if (ret < 0)
+ return 0;
+
+ if (save_addrs)
+ set_allowed_addrs(unit, addrs);
+
+ len = strlen(secbuf);
+ if (len > MAXSECRETLEN) {
+ syslog(LOG_ERR, "Secret for %s on %s is too long", client, server);
+ len = MAXSECRETLEN;
+ }
+ BCOPY(secbuf, secret, len);
+ BZERO(secbuf, sizeof(secbuf));
+ *secret_len = len;
+
+ return 1;
+}
+
+/*
+ * set_allowed_addrs() - set the list of allowed addresses.
+ */
+static void
+set_allowed_addrs(unit, addrs)
+ int unit;
+ struct wordlist *addrs;
+{
+ if (addresses[unit] != NULL)
+ free_wordlist(addresses[unit]);
+ addresses[unit] = addrs;
+
+ /*
+ * If there's only one authorized address we might as well
+ * ask our peer for that one right away
+ */
+ if (addrs != NULL && addrs->next == NULL) {
+ char *p = addrs->word;
+ struct ipcp_options *wo = &ipcp_wantoptions[unit];
+ u_int32_t a;
+ struct hostent *hp;
+
+ if (*p != '!' && *p != '-' && strchr(p, '/') == NULL) {
+ hp = gethostbyname(p);
+ if (hp != NULL && hp->h_addrtype == AF_INET)
+ a = *(u_int32_t *)hp->h_addr;
+ else
+ a = inet_addr(p);
+ if (a != (u_int32_t) -1)
+ wo->hisaddr = a;
+ }
+ }
+}
+
+static void
+auth_set_ip_addr(unit)
+ int unit;
+{
+ struct wordlist *addrs;
+
+ if (non_wildclient && (addrs = addresses[unit]) != NULL) {
+ for (; addrs != NULL; addrs = addrs->next) {
+ /* Look for address overrides, and set them if we have any */
+ if (strchr(addrs->word, ':') != NULL) {
+ if (setipaddr(addrs->word))
+ break;
+ }
+ }
+ }
+}
+
+/*
+ * auth_ip_addr - check whether the peer is authorized to use
+ * a given IP address. Returns 1 if authorized, 0 otherwise.
+ */
+int
+auth_ip_addr(unit, addr)
+ int unit;
+ u_int32_t addr;
+{
+ return ip_addr_check(addr, addresses[unit]);
+}
+
+static int
+ip_addr_check(addr, addrs)
+ u_int32_t addr;
+ struct wordlist *addrs;
+{
+ int x, y;
+ u_int32_t a, mask, ah;
+ int accept;
+ char *ptr_word, *ptr_mask;
+ struct hostent *hp;
+ struct netent *np;
+
+ /* don't allow loopback or multicast address */
+ if (bad_ip_adrs(addr))
+ return 0;
+
+ if (addrs == NULL)
+ return !auth_required; /* no addresses authorized */
+
+ x = y = 0;
+ for (; addrs != NULL; addrs = addrs->next) {
+ y++;
+ /* "-" means no addresses authorized, "*" means any address allowed */
+ ptr_word = addrs->word;
+ if (strcmp(ptr_word, "-") == 0)
+ break;
+ if (strcmp(ptr_word, "*") == 0)
+ return 1;
+
+ /*
+ * A colon in the string means that we wish to force a specific
+ * local:remote address, but we ignore these for now.
+ */
+ if (strchr(addrs->word, ':') != NULL)
+ x++;
+ else {
+
+ accept = 1;
+ if (*ptr_word == '!') {
+ accept = 0;
+ ++ptr_word;
+ }
+
+ mask = ~ (u_int32_t) 0;
+ ptr_mask = strchr (ptr_word, '/');
+ if (ptr_mask != NULL) {
+ int bit_count;
+
+ bit_count = (int) strtol (ptr_mask+1, (char **) 0, 10);
+ if (bit_count <= 0 || bit_count > 32) {
+ syslog (LOG_WARNING,
+ "invalid address length %s in auth. address list",
+ ptr_mask);
+ continue;
+ }
+ *ptr_mask = '\0';
+ mask <<= 32 - bit_count;
+ }
+
+ hp = gethostbyname(ptr_word);
+ if (hp != NULL && hp->h_addrtype == AF_INET) {
+ a = *(u_int32_t *)hp->h_addr;
+ } else {
+ np = getnetbyname (ptr_word);
+ if (np != NULL && np->n_addrtype == AF_INET) {
+ a = htonl (*(u_int32_t *)np->n_net);
+ if (ptr_mask == NULL) {
+ /* calculate appropriate mask for net */
+ ah = ntohl(a);
+ if (IN_CLASSA(ah))
+ mask = IN_CLASSA_NET;
+ else if (IN_CLASSB(ah))
+ mask = IN_CLASSB_NET;
+ else if (IN_CLASSC(ah))
+ mask = IN_CLASSC_NET;
+ }
+ } else {
+ a = inet_addr (ptr_word);
+ }
+ }
+
+ if (ptr_mask != NULL)
+ *ptr_mask = '/';
+
+ if (a == (u_int32_t)-1L)
+ syslog (LOG_WARNING,
+ "unknown host %s in auth. address list",
+ addrs->word);
+ else
+ /* Here a and addr are in network byte order,
+ and mask is in host order. */
+ if (((addr ^ a) & htonl(mask)) == 0)
+ return accept;
+ } /* else */
+ }
+ return x == y; /* not in list => can't have it */
+}
+
+/*
+ * bad_ip_adrs - return 1 if the IP address is one we don't want
+ * to use, such as an address in the loopback net or a multicast address.
+ * addr is in network byte order.
+ */
+int
+bad_ip_adrs(addr)
+ u_int32_t addr;
+{
+ addr = ntohl(addr);
+ return (addr >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET
+ || IN_MULTICAST(addr) || IN_BADCLASS(addr);
+}
+
+/*
+ * check_access - complain if a secret file has too-liberal permissions.
+ */
+void
+check_access(f, filename)
+ FILE *f;
+ char *filename;
+{
+ struct stat sbuf;
+
+ if (fstat(fileno(f), &sbuf) < 0) {
+ syslog(LOG_WARNING, "cannot stat secret file %s: %m", filename);
+ } else if ((sbuf.st_mode & (S_IRWXG | S_IRWXO)) != 0) {
+ syslog(LOG_WARNING, "Warning - secret file %s has world and/or group access", filename);
+ }
+}
+
+
+/*
+ * scan_authfile - Scan an authorization file for a secret suitable
+ * for authenticating `client' on `server'. The return value is -1
+ * if no secret is found, otherwise >= 0. The return value has
+ * NONWILD_CLIENT set if the secret didn't have "*" for the client, and
+ * NONWILD_SERVER set if the secret didn't have "*" for the server.
+ * Any following words on the line (i.e. address authorization
+ * info) are placed in a wordlist and returned in *addrs.
+ */
+static int
+scan_authfile(f, client, server, ipaddr, secret, addrs, filename)
+ FILE *f;
+ char *client;
+ char *server;
+ u_int32_t ipaddr;
+ char *secret;
+ struct wordlist **addrs;
+ char *filename;
+{
+ int newline, xxx;
+ int got_flag, best_flag;
+ FILE *sf;
+ struct wordlist *ap, *addr_list, *alist, *alast;
+ char word[MAXWORDLEN];
+ char atfile[MAXWORDLEN];
+ char lsecret[MAXWORDLEN];
+
+ if (addrs != NULL)
+ *addrs = NULL;
+ addr_list = NULL;
+ if (!getword(f, word, &newline, filename))
+ return -1; /* file is empty??? */
+ newline = 1;
+ best_flag = -1;
+ for (;;) {
+ /*
+ * Skip until we find a word at the start of a line.
+ */
+ while (!newline && getword(f, word, &newline, filename))
+ ;
+ if (!newline)
+ break; /* got to end of file */
+
+ /*
+ * Got a client - check if it's a match or a wildcard.
+ */
+ got_flag = 0;
+ if (client != NULL && strcmp(word, client) != 0 && !ISWILD(word)) {
+ newline = 0;
+ continue;
+ }
+ if (!ISWILD(word))
+ got_flag = NONWILD_CLIENT;
+
+ /*
+ * Now get a server and check if it matches.
+ */
+ if (!getword(f, word, &newline, filename))
+ break;
+ if (newline)
+ continue;
+ if (server != NULL && strcmp(word, server) != 0 && !ISWILD(word))
+ continue;
+ if (!ISWILD(word))
+ got_flag |= NONWILD_SERVER;
+
+ /*
+ * Got some sort of a match - see if it's better than what
+ * we have already.
+ */
+ if (got_flag <= best_flag)
+ continue;
+
+ /*
+ * Get the secret.
+ */
+ if (!getword(f, word, &newline, filename))
+ break;
+ if (newline)
+ continue;
+
+ /*
+ * Special syntax: @filename means read secret from file.
+ */
+ if (word[0] == '@') {
+ strcpy(atfile, word+1);
+ if ((sf = fopen(atfile, "r")) == NULL) {
+ syslog(LOG_WARNING, "can't open indirect secret file %s",
+ atfile);
+ continue;
+ }
+ check_access(sf, atfile);
+ if (!getword(sf, word, &xxx, atfile)) {
+ syslog(LOG_WARNING, "no secret in indirect secret file %s",
+ atfile);
+ fclose(sf);
+ continue;
+ }
+ fclose(sf);
+ }
+ if (secret != NULL)
+ strcpy(lsecret, word);
+
+ /*
+ * Now read address authorization info and make a wordlist.
+ */
+ alist = alast = NULL;
+ for (;;) {
+ if (!getword(f, word, &newline, filename) || newline)
+ break;
+ ap = (struct wordlist *) malloc(sizeof(struct wordlist)
+ + strlen(word));
+ if (ap == NULL)
+ novm("authorized addresses");
+ ap->next = NULL;
+ strcpy(ap->word, word);
+ if (alist == NULL)
+ alist = ap;
+ else
+ alast->next = ap;
+ alast = ap;
+ }
+
+ /*
+ * Check if the given IP address is allowed by the wordlist.
+ */
+ if (ipaddr != 0 && !ip_addr_check(ipaddr, alist)) {
+ free_wordlist(alist);
+ continue;
+ }
+
+ /*
+ * This is the best so far; remember it.
+ */
+ best_flag = got_flag;
+ if (addr_list)
+ free_wordlist(addr_list);
+ addr_list = alist;
+ if (secret != NULL)
+ strcpy(secret, lsecret);
+
+ if (!newline)
+ break;
+ }
+
+ if (addrs != NULL)
+ *addrs = addr_list;
+ else if (addr_list != NULL)
+ free_wordlist(addr_list);
+
+ non_wildclient = (best_flag & NONWILD_CLIENT) && client != NULL &&
+ *client != '\0';
+ return best_flag;
+}
+
+/*
+ * free_wordlist - release memory allocated for a wordlist.
+ */
+static void
+free_wordlist(wp)
+ struct wordlist *wp;
+{
+ struct wordlist *next;
+
+ while (wp != NULL) {
+ next = wp->next;
+ free(wp);
+ wp = next;
+ }
+}
+
+/*
+ * auth_script - execute a script with arguments
+ * interface-name peer-name real-user tty speed
+ */
+static void
+auth_script(script)
+ char *script;
+{
+ char strspeed[32];
+ struct passwd *pw;
+ char struid[32];
+ char *user_name;
+ char *argv[8];
+
+ if ((pw = getpwuid(getuid())) != NULL && pw->pw_name != NULL)
+ user_name = pw->pw_name;
+ else {
+ sprintf(struid, "%d", getuid());
+ user_name = struid;
+ }
+ sprintf(strspeed, "%d", baud_rate);
+
+ argv[0] = script;
+ argv[1] = ifname;
+ argv[2] = peer_authname;
+ argv[3] = user_name;
+ argv[4] = devnam;
+ argv[5] = strspeed;
+ argv[6] = NULL;
+
+ run_program(script, argv, 0);
+}
diff --git a/usr.sbin/pppd/cbcp.c b/usr.sbin/pppd/cbcp.c
new file mode 100644
index 0000000..fb265e6
--- /dev/null
+++ b/usr.sbin/pppd/cbcp.c
@@ -0,0 +1,430 @@
+/*
+ * cbcp - Call Back Configuration Protocol.
+ *
+ * Copyright (c) 1995 Pedro Roque Marques
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Pedro Roque Marques. The name of the author may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char rcsid[] = "$FreeBSD$";
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <syslog.h>
+
+#include "pppd.h"
+#include "cbcp.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ipcp.h"
+
+/*
+ * Protocol entry points.
+ */
+static void cbcp_init __P((int unit));
+static void cbcp_open __P((int unit));
+static void cbcp_lowerup __P((int unit));
+static void cbcp_input __P((int unit, u_char *pkt, int len));
+static void cbcp_protrej __P((int unit));
+static int cbcp_printpkt __P((u_char *pkt, int len,
+ void (*printer) __P((void *, char *, ...)),
+ void *arg));
+
+struct protent cbcp_protent = {
+ PPP_CBCP,
+ cbcp_init,
+ cbcp_input,
+ cbcp_protrej,
+ cbcp_lowerup,
+ NULL,
+ cbcp_open,
+ NULL,
+ cbcp_printpkt,
+ NULL,
+ 0,
+ "CBCP",
+ NULL,
+ NULL,
+ NULL
+};
+
+cbcp_state cbcp[NUM_PPP];
+
+/* internal prototypes */
+
+static void cbcp_recvreq __P((cbcp_state *us, char *pckt, int len));
+static void cbcp_resp __P((cbcp_state *us));
+static void cbcp_up __P((cbcp_state *us));
+static void cbcp_recvack __P((cbcp_state *us, char *pckt, int len));
+static void cbcp_send __P((cbcp_state *us, u_char code, u_char *buf, int len));
+
+/* init state */
+static void
+cbcp_init(iface)
+ int iface;
+{
+ cbcp_state *us;
+
+ us = &cbcp[iface];
+ memset(us, 0, sizeof(cbcp_state));
+ us->us_unit = iface;
+ us->us_type |= (1 << CB_CONF_NO);
+}
+
+/* lower layer is up */
+static void
+cbcp_lowerup(iface)
+ int iface;
+{
+ cbcp_state *us = &cbcp[iface];
+
+ syslog(LOG_DEBUG, "cbcp_lowerup");
+ syslog(LOG_DEBUG, "want: %d", us->us_type);
+
+ if (us->us_type == CB_CONF_USER)
+ syslog(LOG_DEBUG, "phone no: %s", us->us_number);
+}
+
+static void
+cbcp_open(unit)
+ int unit;
+{
+ syslog(LOG_DEBUG, "cbcp_open");
+}
+
+/* process an incomming packet */
+static void
+cbcp_input(unit, inpacket, pktlen)
+ int unit;
+ u_char *inpacket;
+ int pktlen;
+{
+ u_char *inp;
+ u_char code, id;
+ u_short len;
+
+ cbcp_state *us = &cbcp[unit];
+
+ inp = inpacket;
+
+ if (pktlen < CBCP_MINLEN) {
+ syslog(LOG_ERR, "CBCP packet is too small");
+ return;
+ }
+
+ GETCHAR(code, inp);
+ GETCHAR(id, inp);
+ GETSHORT(len, inp);
+
+#if 0
+ if (len > pktlen) {
+ syslog(LOG_ERR, "CBCP packet: invalid length");
+ return;
+ }
+#endif
+
+ len -= CBCP_MINLEN;
+
+ switch(code) {
+ case CBCP_REQ:
+ us->us_id = id;
+ cbcp_recvreq(us, inp, len);
+ break;
+
+ case CBCP_RESP:
+ syslog(LOG_DEBUG, "CBCP_RESP received");
+ break;
+
+ case CBCP_ACK:
+ if (id != us->us_id)
+ syslog(LOG_DEBUG, "id doesn't match: expected %d recv %d",
+ us->us_id, id);
+
+ cbcp_recvack(us, inp, len);
+ break;
+
+ default:
+ break;
+ }
+}
+
+/* protocol was rejected by foe */
+void cbcp_protrej(int iface)
+{
+}
+
+char *cbcp_codenames[] = {
+ "Request", "Response", "Ack"
+};
+
+char *cbcp_optionnames[] = {
+ "NoCallback",
+ "UserDefined",
+ "AdminDefined",
+ "List"
+};
+
+/* pretty print a packet */
+static int
+cbcp_printpkt(p, plen, printer, arg)
+ u_char *p;
+ int plen;
+ void (*printer) __P((void *, char *, ...));
+ void *arg;
+{
+ int code, opt, id, len, olen, delay;
+ u_char *pstart;
+
+ if (plen < HEADERLEN)
+ return 0;
+ pstart = p;
+ GETCHAR(code, p);
+ GETCHAR(id, p);
+ GETSHORT(len, p);
+ if (len < HEADERLEN || len > plen)
+ return 0;
+
+ if (code >= 1 && code <= sizeof(cbcp_codenames) / sizeof(char *))
+ printer(arg, " %s", cbcp_codenames[code-1]);
+ else
+ printer(arg, " code=0x%x", code);
+
+ printer(arg, " id=0x%x", id);
+ len -= HEADERLEN;
+
+ switch (code) {
+ case CBCP_REQ:
+ case CBCP_RESP:
+ case CBCP_ACK:
+ while(len >= 2) {
+ GETCHAR(opt, p);
+ GETCHAR(olen, p);
+
+ if (olen < 2 || olen > len) {
+ break;
+ }
+
+ printer(arg, " <");
+ len -= olen;
+
+ if (opt >= 1 && opt <= sizeof(cbcp_optionnames) / sizeof(char *))
+ printer(arg, " %s", cbcp_optionnames[opt-1]);
+ else
+ printer(arg, " option=0x%x", opt);
+
+ if (olen > 2) {
+ GETCHAR(delay, p);
+ printer(arg, " delay = %d", delay);
+ }
+
+ if (olen > 3) {
+ int addrt;
+ char str[256];
+
+ GETCHAR(addrt, p);
+ memcpy(str, p, olen - 4);
+ str[olen - 4] = 0;
+ printer(arg, " number = %s", str);
+ }
+ printer(arg, ">");
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ for (; len > 0; --len) {
+ GETCHAR(code, p);
+ printer(arg, " %.2x", code);
+ }
+
+ return p - pstart;
+}
+
+/* received CBCP request */
+static void
+cbcp_recvreq(us, pckt, pcktlen)
+ cbcp_state *us;
+ char *pckt;
+ int pcktlen;
+{
+ u_char type, opt_len, delay, addr_type;
+ char address[256];
+ int len = pcktlen;
+
+ address[0] = 0;
+
+ while (len) {
+ syslog(LOG_DEBUG, "length: %d", len);
+
+ GETCHAR(type, pckt);
+ GETCHAR(opt_len, pckt);
+
+ if (opt_len > 2)
+ GETCHAR(delay, pckt);
+
+ us->us_allowed |= (1 << type);
+
+ switch(type) {
+ case CB_CONF_NO:
+ syslog(LOG_DEBUG, "no callback allowed");
+ break;
+
+ case CB_CONF_USER:
+ syslog(LOG_DEBUG, "user callback allowed");
+ if (opt_len > 4) {
+ GETCHAR(addr_type, pckt);
+ memcpy(address, pckt, opt_len - 4);
+ address[opt_len - 4] = 0;
+ if (address[0])
+ syslog(LOG_DEBUG, "address: %s", address);
+ }
+ break;
+
+ case CB_CONF_ADMIN:
+ syslog(LOG_DEBUG, "user admin defined allowed");
+ break;
+
+ case CB_CONF_LIST:
+ break;
+ }
+ len -= opt_len;
+ }
+
+ cbcp_resp(us);
+}
+
+static void
+cbcp_resp(us)
+ cbcp_state *us;
+{
+ u_char cb_type;
+ u_char buf[256];
+ u_char *bufp = buf;
+ int len = 0;
+
+ cb_type = us->us_allowed & us->us_type;
+ syslog(LOG_DEBUG, "cbcp_resp cb_type=%d", cb_type);
+
+#if 0
+ if (!cb_type)
+ lcp_down(us->us_unit);
+#endif
+
+ if (cb_type & ( 1 << CB_CONF_USER ) ) {
+ syslog(LOG_DEBUG, "cbcp_resp CONF_USER");
+ PUTCHAR(CB_CONF_USER, bufp);
+ len = 3 + 1 + strlen(us->us_number) + 1;
+ PUTCHAR(len , bufp);
+ PUTCHAR(5, bufp); /* delay */
+ PUTCHAR(1, bufp);
+ BCOPY(us->us_number, bufp, strlen(us->us_number) + 1);
+ cbcp_send(us, CBCP_RESP, buf, len);
+ return;
+ }
+
+ if (cb_type & ( 1 << CB_CONF_ADMIN ) ) {
+ syslog(LOG_DEBUG, "cbcp_resp CONF_ADMIN");
+ PUTCHAR(CB_CONF_ADMIN, bufp);
+ len = 3 + 1;
+ PUTCHAR(len , bufp);
+ PUTCHAR(5, bufp); /* delay */
+ PUTCHAR(0, bufp);
+ cbcp_send(us, CBCP_RESP, buf, len);
+ return;
+ }
+
+ if (cb_type & ( 1 << CB_CONF_NO ) ) {
+ syslog(LOG_DEBUG, "cbcp_resp CONF_NO");
+ PUTCHAR(CB_CONF_NO, bufp);
+ len = 3;
+ PUTCHAR(len , bufp);
+ PUTCHAR(0, bufp);
+ cbcp_send(us, CBCP_RESP, buf, len);
+ (*ipcp_protent.open)(us->us_unit);
+ return;
+ }
+}
+
+static void
+cbcp_send(us, code, buf, len)
+ cbcp_state *us;
+ u_char code;
+ u_char *buf;
+ int len;
+{
+ u_char *outp;
+ int outlen;
+
+ outp = outpacket_buf;
+
+ outlen = 4 + len;
+
+ MAKEHEADER(outp, PPP_CBCP);
+
+ PUTCHAR(code, outp);
+ PUTCHAR(us->us_id, outp);
+ PUTSHORT(outlen, outp);
+
+ if (len)
+ BCOPY(buf, outp, len);
+
+ output(us->us_unit, outpacket_buf, outlen + PPP_HDRLEN);
+}
+
+static void
+cbcp_recvack(us, pckt, len)
+ cbcp_state *us;
+ char *pckt;
+ int len;
+{
+ u_char type, delay, addr_type;
+ int opt_len;
+ char address[256];
+
+ if (len) {
+ GETCHAR(type, pckt);
+ GETCHAR(opt_len, pckt);
+
+ if (opt_len > 2)
+ GETCHAR(delay, pckt);
+
+ if (opt_len > 4) {
+ GETCHAR(addr_type, pckt);
+ memcpy(address, pckt, opt_len - 4);
+ address[opt_len - 4] = 0;
+ if (address[0])
+ syslog(LOG_DEBUG, "peer will call: %s", address);
+ }
+ }
+
+ cbcp_up(us);
+}
+
+extern int persist;
+
+/* ok peer will do callback */
+static void
+cbcp_up(us)
+ cbcp_state *us;
+{
+ persist = 0;
+ lcp_close(0, "Call me back, please");
+}
diff --git a/usr.sbin/pppd/cbcp.h b/usr.sbin/pppd/cbcp.h
new file mode 100644
index 0000000..c2ab3f6
--- /dev/null
+++ b/usr.sbin/pppd/cbcp.h
@@ -0,0 +1,26 @@
+#ifndef CBCP_H
+#define CBCP_H
+
+typedef struct cbcp_state {
+ int us_unit; /* Interface unit number */
+ u_char us_id; /* Current id */
+ u_char us_allowed;
+ int us_type;
+ char *us_number; /* Telefone Number */
+} cbcp_state;
+
+extern cbcp_state cbcp[];
+
+extern struct protent cbcp_protent;
+
+#define CBCP_MINLEN 4
+
+#define CBCP_REQ 1
+#define CBCP_RESP 2
+#define CBCP_ACK 3
+
+#define CB_CONF_NO 1
+#define CB_CONF_USER 2
+#define CB_CONF_ADMIN 3
+#define CB_CONF_LIST 4
+#endif
diff --git a/usr.sbin/pppd/ccp.c b/usr.sbin/pppd/ccp.c
new file mode 100644
index 0000000..fb21cfd
--- /dev/null
+++ b/usr.sbin/pppd/ccp.c
@@ -0,0 +1,1113 @@
+/*
+ * ccp.c - PPP Compression Control Protocol.
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies. This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ */
+
+#ifndef lint
+static char rcsid[] = "$FreeBSD$";
+#endif
+
+#include <string.h>
+#include <syslog.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+
+#include "pppd.h"
+#include "fsm.h"
+#include "ccp.h"
+#include <net/ppp_comp.h>
+
+/*
+ * Protocol entry points from main code.
+ */
+static void ccp_init __P((int unit));
+static void ccp_open __P((int unit));
+static void ccp_close __P((int unit, char *));
+static void ccp_lowerup __P((int unit));
+static void ccp_lowerdown __P((int));
+static void ccp_input __P((int unit, u_char *pkt, int len));
+static void ccp_protrej __P((int unit));
+static int ccp_printpkt __P((u_char *pkt, int len,
+ void (*printer) __P((void *, char *, ...)),
+ void *arg));
+static void ccp_datainput __P((int unit, u_char *pkt, int len));
+
+struct protent ccp_protent = {
+ PPP_CCP,
+ ccp_init,
+ ccp_input,
+ ccp_protrej,
+ ccp_lowerup,
+ ccp_lowerdown,
+ ccp_open,
+ ccp_close,
+ ccp_printpkt,
+ ccp_datainput,
+ 1,
+ "CCP",
+ NULL,
+ NULL,
+ NULL
+};
+
+fsm ccp_fsm[NUM_PPP];
+ccp_options ccp_wantoptions[NUM_PPP]; /* what to request the peer to use */
+ccp_options ccp_gotoptions[NUM_PPP]; /* what the peer agreed to do */
+ccp_options ccp_allowoptions[NUM_PPP]; /* what we'll agree to do */
+ccp_options ccp_hisoptions[NUM_PPP]; /* what we agreed to do */
+
+/*
+ * Callbacks for fsm code.
+ */
+static void ccp_resetci __P((fsm *));
+static int ccp_cilen __P((fsm *));
+static void ccp_addci __P((fsm *, u_char *, int *));
+static int ccp_ackci __P((fsm *, u_char *, int));
+static int ccp_nakci __P((fsm *, u_char *, int));
+static int ccp_rejci __P((fsm *, u_char *, int));
+static int ccp_reqci __P((fsm *, u_char *, int *, int));
+static void ccp_up __P((fsm *));
+static void ccp_down __P((fsm *));
+static int ccp_extcode __P((fsm *, int, int, u_char *, int));
+static void ccp_rack_timeout __P((void *));
+static char *method_name __P((ccp_options *, ccp_options *));
+
+static fsm_callbacks ccp_callbacks = {
+ ccp_resetci,
+ ccp_cilen,
+ ccp_addci,
+ ccp_ackci,
+ ccp_nakci,
+ ccp_rejci,
+ ccp_reqci,
+ ccp_up,
+ ccp_down,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ ccp_extcode,
+ "CCP"
+};
+
+/*
+ * Do we want / did we get any compression?
+ */
+#define ANY_COMPRESS(opt) ((opt).deflate || (opt).bsd_compress \
+ || (opt).predictor_1 || (opt).predictor_2)
+
+/*
+ * Local state (mainly for handling reset-reqs and reset-acks).
+ */
+static int ccp_localstate[NUM_PPP];
+#define RACK_PENDING 1 /* waiting for reset-ack */
+#define RREQ_REPEAT 2 /* send another reset-req if no reset-ack */
+
+#define RACKTIMEOUT 1 /* second */
+
+static int all_rejected[NUM_PPP]; /* we rejected all peer's options */
+
+/*
+ * ccp_init - initialize CCP.
+ */
+static void
+ccp_init(unit)
+ int unit;
+{
+ fsm *f = &ccp_fsm[unit];
+
+ f->unit = unit;
+ f->protocol = PPP_CCP;
+ f->callbacks = &ccp_callbacks;
+ fsm_init(f);
+
+ memset(&ccp_wantoptions[unit], 0, sizeof(ccp_options));
+ memset(&ccp_gotoptions[unit], 0, sizeof(ccp_options));
+ memset(&ccp_allowoptions[unit], 0, sizeof(ccp_options));
+ memset(&ccp_hisoptions[unit], 0, sizeof(ccp_options));
+
+ ccp_wantoptions[0].deflate = 1;
+ ccp_wantoptions[0].deflate_size = DEFLATE_MAX_SIZE;
+ ccp_wantoptions[0].deflate_correct = 1;
+ ccp_wantoptions[0].deflate_draft = 1;
+ ccp_allowoptions[0].deflate = 1;
+ ccp_allowoptions[0].deflate_size = DEFLATE_MAX_SIZE;
+ ccp_allowoptions[0].deflate_correct = 1;
+ ccp_allowoptions[0].deflate_draft = 1;
+
+ ccp_wantoptions[0].bsd_compress = 1;
+ ccp_wantoptions[0].bsd_bits = BSD_MAX_BITS;
+ ccp_allowoptions[0].bsd_compress = 1;
+ ccp_allowoptions[0].bsd_bits = BSD_MAX_BITS;
+
+ ccp_allowoptions[0].predictor_1 = 1;
+}
+
+/*
+ * ccp_open - CCP is allowed to come up.
+ */
+static void
+ccp_open(unit)
+ int unit;
+{
+ fsm *f = &ccp_fsm[unit];
+
+ if (f->state != OPENED)
+ ccp_flags_set(unit, 1, 0);
+
+ /*
+ * Find out which compressors the kernel supports before
+ * deciding whether to open in silent mode.
+ */
+ ccp_resetci(f);
+ if (!ANY_COMPRESS(ccp_gotoptions[unit]))
+ f->flags |= OPT_SILENT;
+
+ fsm_open(f);
+}
+
+/*
+ * ccp_close - Terminate CCP.
+ */
+static void
+ccp_close(unit, reason)
+ int unit;
+ char *reason;
+{
+ ccp_flags_set(unit, 0, 0);
+ fsm_close(&ccp_fsm[unit], reason);
+}
+
+/*
+ * ccp_lowerup - we may now transmit CCP packets.
+ */
+static void
+ccp_lowerup(unit)
+ int unit;
+{
+ fsm_lowerup(&ccp_fsm[unit]);
+}
+
+/*
+ * ccp_lowerdown - we may not transmit CCP packets.
+ */
+static void
+ccp_lowerdown(unit)
+ int unit;
+{
+ fsm_lowerdown(&ccp_fsm[unit]);
+}
+
+/*
+ * ccp_input - process a received CCP packet.
+ */
+static void
+ccp_input(unit, p, len)
+ int unit;
+ u_char *p;
+ int len;
+{
+ fsm *f = &ccp_fsm[unit];
+ int oldstate;
+
+ /*
+ * Check for a terminate-request so we can print a message.
+ */
+ oldstate = f->state;
+ fsm_input(f, p, len);
+ if (oldstate == OPENED && p[0] == TERMREQ && f->state != OPENED)
+ syslog(LOG_NOTICE, "Compression disabled by peer.");
+
+ /*
+ * If we get a terminate-ack and we're not asking for compression,
+ * close CCP.
+ */
+ if (oldstate == REQSENT && p[0] == TERMACK
+ && !ANY_COMPRESS(ccp_gotoptions[unit]))
+ ccp_close(unit, "No compression negotiated");
+}
+
+/*
+ * Handle a CCP-specific code.
+ */
+static int
+ccp_extcode(f, code, id, p, len)
+ fsm *f;
+ int code, id;
+ u_char *p;
+ int len;
+{
+ switch (code) {
+ case CCP_RESETREQ:
+ if (f->state != OPENED)
+ break;
+ /* send a reset-ack, which the transmitter will see and
+ reset its compression state. */
+ fsm_sdata(f, CCP_RESETACK, id, NULL, 0);
+ break;
+
+ case CCP_RESETACK:
+ if (ccp_localstate[f->unit] & RACK_PENDING && id == f->reqid) {
+ ccp_localstate[f->unit] &= ~(RACK_PENDING | RREQ_REPEAT);
+ UNTIMEOUT(ccp_rack_timeout, f);
+ }
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * ccp_protrej - peer doesn't talk CCP.
+ */
+static void
+ccp_protrej(unit)
+ int unit;
+{
+ ccp_flags_set(unit, 0, 0);
+ fsm_lowerdown(&ccp_fsm[unit]);
+}
+
+/*
+ * ccp_resetci - initialize at start of negotiation.
+ */
+static void
+ccp_resetci(f)
+ fsm *f;
+{
+ ccp_options *go = &ccp_gotoptions[f->unit];
+ u_char opt_buf[16];
+
+ *go = ccp_wantoptions[f->unit];
+ all_rejected[f->unit] = 0;
+
+ /*
+ * Check whether the kernel knows about the various
+ * compression methods we might request.
+ */
+ if (go->bsd_compress) {
+ opt_buf[0] = CI_BSD_COMPRESS;
+ opt_buf[1] = CILEN_BSD_COMPRESS;
+ opt_buf[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, BSD_MIN_BITS);
+ if (ccp_test(f->unit, opt_buf, CILEN_BSD_COMPRESS, 0) <= 0)
+ go->bsd_compress = 0;
+ }
+ if (go->deflate) {
+ if (go->deflate_correct) {
+ opt_buf[0] = CI_DEFLATE;
+ opt_buf[1] = CILEN_DEFLATE;
+ opt_buf[2] = DEFLATE_MAKE_OPT(DEFLATE_MIN_SIZE);
+ opt_buf[3] = DEFLATE_CHK_SEQUENCE;
+ if (ccp_test(f->unit, opt_buf, CILEN_DEFLATE, 0) <= 0)
+ go->deflate_correct = 0;
+ }
+ if (go->deflate_draft) {
+ opt_buf[0] = CI_DEFLATE_DRAFT;
+ opt_buf[1] = CILEN_DEFLATE;
+ opt_buf[2] = DEFLATE_MAKE_OPT(DEFLATE_MIN_SIZE);
+ opt_buf[3] = DEFLATE_CHK_SEQUENCE;
+ if (ccp_test(f->unit, opt_buf, CILEN_DEFLATE, 0) <= 0)
+ go->deflate_draft = 0;
+ }
+ if (!go->deflate_correct && !go->deflate_draft)
+ go->deflate = 0;
+ }
+ if (go->predictor_1) {
+ opt_buf[0] = CI_PREDICTOR_1;
+ opt_buf[1] = CILEN_PREDICTOR_1;
+ if (ccp_test(f->unit, opt_buf, CILEN_PREDICTOR_1, 0) <= 0)
+ go->predictor_1 = 0;
+ }
+ if (go->predictor_2) {
+ opt_buf[0] = CI_PREDICTOR_2;
+ opt_buf[1] = CILEN_PREDICTOR_2;
+ if (ccp_test(f->unit, opt_buf, CILEN_PREDICTOR_2, 0) <= 0)
+ go->predictor_2 = 0;
+ }
+}
+
+/*
+ * ccp_cilen - Return total length of our configuration info.
+ */
+static int
+ccp_cilen(f)
+ fsm *f;
+{
+ ccp_options *go = &ccp_gotoptions[f->unit];
+
+ return (go->bsd_compress? CILEN_BSD_COMPRESS: 0)
+ + (go->deflate? CILEN_DEFLATE: 0)
+ + (go->predictor_1? CILEN_PREDICTOR_1: 0)
+ + (go->predictor_2? CILEN_PREDICTOR_2: 0);
+}
+
+/*
+ * ccp_addci - put our requests in a packet.
+ */
+static void
+ccp_addci(f, p, lenp)
+ fsm *f;
+ u_char *p;
+ int *lenp;
+{
+ int res;
+ ccp_options *go = &ccp_gotoptions[f->unit];
+ u_char *p0 = p;
+
+ /*
+ * Add the compression types that we can receive, in decreasing
+ * preference order. Get the kernel to allocate the first one
+ * in case it gets Acked.
+ */
+ if (go->deflate) {
+ p[0] = go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT;
+ p[1] = CILEN_DEFLATE;
+ p[2] = DEFLATE_MAKE_OPT(go->deflate_size);
+ p[3] = DEFLATE_CHK_SEQUENCE;
+ for (;;) {
+ res = ccp_test(f->unit, p, CILEN_DEFLATE, 0);
+ if (res > 0) {
+ p += CILEN_DEFLATE;
+ break;
+ }
+ if (res < 0 || go->deflate_size <= DEFLATE_MIN_SIZE) {
+ go->deflate = 0;
+ break;
+ }
+ --go->deflate_size;
+ p[2] = DEFLATE_MAKE_OPT(go->deflate_size);
+ }
+ if (p != p0 && go->deflate_correct && go->deflate_draft) {
+ p[0] = CI_DEFLATE_DRAFT;
+ p[1] = CILEN_DEFLATE;
+ p[2] = p[2 - CILEN_DEFLATE];
+ p[3] = DEFLATE_CHK_SEQUENCE;
+ p += CILEN_DEFLATE;
+ }
+ }
+ if (go->bsd_compress) {
+ p[0] = CI_BSD_COMPRESS;
+ p[1] = CILEN_BSD_COMPRESS;
+ p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits);
+ if (p != p0) {
+ p += CILEN_BSD_COMPRESS; /* not the first option */
+ } else {
+ for (;;) {
+ res = ccp_test(f->unit, p, CILEN_BSD_COMPRESS, 0);
+ if (res > 0) {
+ p += CILEN_BSD_COMPRESS;
+ break;
+ }
+ if (res < 0 || go->bsd_bits <= BSD_MIN_BITS) {
+ go->bsd_compress = 0;
+ break;
+ }
+ --go->bsd_bits;
+ p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits);
+ }
+ }
+ }
+ /* XXX Should Predictor 2 be preferable to Predictor 1? */
+ if (go->predictor_1) {
+ p[0] = CI_PREDICTOR_1;
+ p[1] = CILEN_PREDICTOR_1;
+ if (p == p0 && ccp_test(f->unit, p, CILEN_PREDICTOR_1, 0) <= 0) {
+ go->predictor_1 = 0;
+ } else {
+ p += CILEN_PREDICTOR_1;
+ }
+ }
+ if (go->predictor_2) {
+ p[0] = CI_PREDICTOR_2;
+ p[1] = CILEN_PREDICTOR_2;
+ if (p == p0 && ccp_test(f->unit, p, CILEN_PREDICTOR_2, 0) <= 0) {
+ go->predictor_2 = 0;
+ } else {
+ p += CILEN_PREDICTOR_2;
+ }
+ }
+
+ go->method = (p > p0)? p0[0]: -1;
+
+ *lenp = p - p0;
+}
+
+/*
+ * ccp_ackci - process a received configure-ack, and return
+ * 1 iff the packet was OK.
+ */
+static int
+ccp_ackci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ ccp_options *go = &ccp_gotoptions[f->unit];
+ u_char *p0 = p;
+
+ if (go->deflate) {
+ if (len < CILEN_DEFLATE
+ || p[0] != (go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT)
+ || p[1] != CILEN_DEFLATE
+ || p[2] != DEFLATE_MAKE_OPT(go->deflate_size)
+ || p[3] != DEFLATE_CHK_SEQUENCE)
+ return 0;
+ p += CILEN_DEFLATE;
+ len -= CILEN_DEFLATE;
+ /* XXX Cope with first/fast ack */
+ if (len == 0)
+ return 1;
+ if (go->deflate_correct && go->deflate_draft) {
+ if (len < CILEN_DEFLATE
+ || p[0] != CI_DEFLATE_DRAFT
+ || p[1] != CILEN_DEFLATE
+ || p[2] != DEFLATE_MAKE_OPT(go->deflate_size)
+ || p[3] != DEFLATE_CHK_SEQUENCE)
+ return 0;
+ p += CILEN_DEFLATE;
+ len -= CILEN_DEFLATE;
+ }
+ }
+ if (go->bsd_compress) {
+ if (len < CILEN_BSD_COMPRESS
+ || p[0] != CI_BSD_COMPRESS || p[1] != CILEN_BSD_COMPRESS
+ || p[2] != BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits))
+ return 0;
+ p += CILEN_BSD_COMPRESS;
+ len -= CILEN_BSD_COMPRESS;
+ /* XXX Cope with first/fast ack */
+ if (p == p0 && len == 0)
+ return 1;
+ }
+ if (go->predictor_1) {
+ if (len < CILEN_PREDICTOR_1
+ || p[0] != CI_PREDICTOR_1 || p[1] != CILEN_PREDICTOR_1)
+ return 0;
+ p += CILEN_PREDICTOR_1;
+ len -= CILEN_PREDICTOR_1;
+ /* XXX Cope with first/fast ack */
+ if (p == p0 && len == 0)
+ return 1;
+ }
+ if (go->predictor_2) {
+ if (len < CILEN_PREDICTOR_2
+ || p[0] != CI_PREDICTOR_2 || p[1] != CILEN_PREDICTOR_2)
+ return 0;
+ p += CILEN_PREDICTOR_2;
+ len -= CILEN_PREDICTOR_2;
+ /* XXX Cope with first/fast ack */
+ if (p == p0 && len == 0)
+ return 1;
+ }
+
+ if (len != 0)
+ return 0;
+ return 1;
+}
+
+/*
+ * ccp_nakci - process received configure-nak.
+ * Returns 1 iff the nak was OK.
+ */
+static int
+ccp_nakci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ ccp_options *go = &ccp_gotoptions[f->unit];
+ ccp_options no; /* options we've seen already */
+ ccp_options try; /* options to ask for next time */
+
+ memset(&no, 0, sizeof(no));
+ try = *go;
+
+ if (go->deflate && len >= CILEN_DEFLATE
+ && p[0] == (go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT)
+ && p[1] == CILEN_DEFLATE) {
+ no.deflate = 1;
+ /*
+ * Peer wants us to use a different code size or something.
+ * Stop asking for Deflate if we don't understand his suggestion.
+ */
+ if (DEFLATE_METHOD(p[2]) != DEFLATE_METHOD_VAL
+ || DEFLATE_SIZE(p[2]) < DEFLATE_MIN_SIZE
+ || p[3] != DEFLATE_CHK_SEQUENCE)
+ try.deflate = 0;
+ else if (DEFLATE_SIZE(p[2]) < go->deflate_size)
+ try.deflate_size = DEFLATE_SIZE(p[2]);
+ p += CILEN_DEFLATE;
+ len -= CILEN_DEFLATE;
+ if (go->deflate_correct && go->deflate_draft
+ && len >= CILEN_DEFLATE && p[0] == CI_DEFLATE_DRAFT
+ && p[1] == CILEN_DEFLATE) {
+ p += CILEN_DEFLATE;
+ len -= CILEN_DEFLATE;
+ }
+ }
+
+ if (go->bsd_compress && len >= CILEN_BSD_COMPRESS
+ && p[0] == CI_BSD_COMPRESS && p[1] == CILEN_BSD_COMPRESS) {
+ no.bsd_compress = 1;
+ /*
+ * Peer wants us to use a different number of bits
+ * or a different version.
+ */
+ if (BSD_VERSION(p[2]) != BSD_CURRENT_VERSION)
+ try.bsd_compress = 0;
+ else if (BSD_NBITS(p[2]) < go->bsd_bits)
+ try.bsd_bits = BSD_NBITS(p[2]);
+ p += CILEN_BSD_COMPRESS;
+ len -= CILEN_BSD_COMPRESS;
+ }
+
+ /*
+ * Predictor-1 and 2 have no options, so they can't be Naked.
+ *
+ * XXX What should we do with any remaining options?
+ */
+
+ if (len != 0)
+ return 0;
+
+ if (f->state != OPENED)
+ *go = try;
+ return 1;
+}
+
+/*
+ * ccp_rejci - reject some of our suggested compression methods.
+ */
+static int
+ccp_rejci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ ccp_options *go = &ccp_gotoptions[f->unit];
+ ccp_options try; /* options to request next time */
+
+ try = *go;
+
+ /*
+ * Cope with empty configure-rejects by ceasing to send
+ * configure-requests.
+ */
+ if (len == 0 && all_rejected[f->unit])
+ return -1;
+
+ if (go->deflate && len >= CILEN_DEFLATE
+ && p[0] == (go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT)
+ && p[1] == CILEN_DEFLATE) {
+ if (p[2] != DEFLATE_MAKE_OPT(go->deflate_size)
+ || p[3] != DEFLATE_CHK_SEQUENCE)
+ return 0; /* Rej is bad */
+ if (go->deflate_correct)
+ try.deflate_correct = 0;
+ else
+ try.deflate_draft = 0;
+ p += CILEN_DEFLATE;
+ len -= CILEN_DEFLATE;
+ if (go->deflate_correct && go->deflate_draft
+ && len >= CILEN_DEFLATE && p[0] == CI_DEFLATE_DRAFT
+ && p[1] == CILEN_DEFLATE) {
+ if (p[2] != DEFLATE_MAKE_OPT(go->deflate_size)
+ || p[3] != DEFLATE_CHK_SEQUENCE)
+ return 0; /* Rej is bad */
+ try.deflate_draft = 0;
+ p += CILEN_DEFLATE;
+ len -= CILEN_DEFLATE;
+ }
+ if (!try.deflate_correct && !try.deflate_draft)
+ try.deflate = 0;
+ }
+ if (go->bsd_compress && len >= CILEN_BSD_COMPRESS
+ && p[0] == CI_BSD_COMPRESS && p[1] == CILEN_BSD_COMPRESS) {
+ if (p[2] != BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits))
+ return 0;
+ try.bsd_compress = 0;
+ p += CILEN_BSD_COMPRESS;
+ len -= CILEN_BSD_COMPRESS;
+ }
+ if (go->predictor_1 && len >= CILEN_PREDICTOR_1
+ && p[0] == CI_PREDICTOR_1 && p[1] == CILEN_PREDICTOR_1) {
+ try.predictor_1 = 0;
+ p += CILEN_PREDICTOR_1;
+ len -= CILEN_PREDICTOR_1;
+ }
+ if (go->predictor_2 && len >= CILEN_PREDICTOR_2
+ && p[0] == CI_PREDICTOR_2 && p[1] == CILEN_PREDICTOR_2) {
+ try.predictor_2 = 0;
+ p += CILEN_PREDICTOR_2;
+ len -= CILEN_PREDICTOR_2;
+ }
+
+ if (len != 0)
+ return 0;
+
+ if (f->state != OPENED)
+ *go = try;
+
+ return 1;
+}
+
+/*
+ * ccp_reqci - processed a received configure-request.
+ * Returns CONFACK, CONFNAK or CONFREJ and the packet modified
+ * appropriately.
+ */
+static int
+ccp_reqci(f, p, lenp, dont_nak)
+ fsm *f;
+ u_char *p;
+ int *lenp;
+ int dont_nak;
+{
+ int ret, newret, res;
+ u_char *p0, *retp;
+ int len, clen, type, nb;
+ ccp_options *ho = &ccp_hisoptions[f->unit];
+ ccp_options *ao = &ccp_allowoptions[f->unit];
+
+ ret = CONFACK;
+ retp = p0 = p;
+ len = *lenp;
+
+ memset(ho, 0, sizeof(ccp_options));
+ ho->method = (len > 0)? p[0]: -1;
+
+ while (len > 0) {
+ newret = CONFACK;
+ if (len < 2 || p[1] < 2 || p[1] > len) {
+ /* length is bad */
+ clen = len;
+ newret = CONFREJ;
+
+ } else {
+ type = p[0];
+ clen = p[1];
+
+ switch (type) {
+ case CI_DEFLATE:
+ case CI_DEFLATE_DRAFT:
+ if (!ao->deflate || clen != CILEN_DEFLATE
+ || (!ao->deflate_correct && type == CI_DEFLATE)
+ || (!ao->deflate_draft && type == CI_DEFLATE_DRAFT)) {
+ newret = CONFREJ;
+ break;
+ }
+
+ ho->deflate = 1;
+ ho->deflate_size = nb = DEFLATE_SIZE(p[2]);
+ if (DEFLATE_METHOD(p[2]) != DEFLATE_METHOD_VAL
+ || p[3] != DEFLATE_CHK_SEQUENCE
+ || nb > ao->deflate_size || nb < DEFLATE_MIN_SIZE) {
+ newret = CONFNAK;
+ if (!dont_nak) {
+ p[2] = DEFLATE_MAKE_OPT(ao->deflate_size);
+ p[3] = DEFLATE_CHK_SEQUENCE;
+ /* fall through to test this #bits below */
+ } else
+ break;
+ }
+
+ /*
+ * Check whether we can do Deflate with the window
+ * size they want. If the window is too big, reduce
+ * it until the kernel can cope and nak with that.
+ * We only check this for the first option.
+ */
+ if (p == p0) {
+ for (;;) {
+ res = ccp_test(f->unit, p, CILEN_DEFLATE, 1);
+ if (res > 0)
+ break; /* it's OK now */
+ if (res < 0 || nb == DEFLATE_MIN_SIZE || dont_nak) {
+ newret = CONFREJ;
+ p[2] = DEFLATE_MAKE_OPT(ho->deflate_size);
+ break;
+ }
+ newret = CONFNAK;
+ --nb;
+ p[2] = DEFLATE_MAKE_OPT(nb);
+ }
+ }
+ break;
+
+ case CI_BSD_COMPRESS:
+ if (!ao->bsd_compress || clen != CILEN_BSD_COMPRESS) {
+ newret = CONFREJ;
+ break;
+ }
+
+ ho->bsd_compress = 1;
+ ho->bsd_bits = nb = BSD_NBITS(p[2]);
+ if (BSD_VERSION(p[2]) != BSD_CURRENT_VERSION
+ || nb > ao->bsd_bits || nb < BSD_MIN_BITS) {
+ newret = CONFNAK;
+ if (!dont_nak) {
+ p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, ao->bsd_bits);
+ /* fall through to test this #bits below */
+ } else
+ break;
+ }
+
+ /*
+ * Check whether we can do BSD-Compress with the code
+ * size they want. If the code size is too big, reduce
+ * it until the kernel can cope and nak with that.
+ * We only check this for the first option.
+ */
+ if (p == p0) {
+ for (;;) {
+ res = ccp_test(f->unit, p, CILEN_BSD_COMPRESS, 1);
+ if (res > 0)
+ break;
+ if (res < 0 || nb == BSD_MIN_BITS || dont_nak) {
+ newret = CONFREJ;
+ p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION,
+ ho->bsd_bits);
+ break;
+ }
+ newret = CONFNAK;
+ --nb;
+ p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, nb);
+ }
+ }
+ break;
+
+ case CI_PREDICTOR_1:
+ if (!ao->predictor_1 || clen != CILEN_PREDICTOR_1) {
+ newret = CONFREJ;
+ break;
+ }
+
+ ho->predictor_1 = 1;
+ if (p == p0
+ && ccp_test(f->unit, p, CILEN_PREDICTOR_1, 1) <= 0) {
+ newret = CONFREJ;
+ }
+ break;
+
+ case CI_PREDICTOR_2:
+ if (!ao->predictor_2 || clen != CILEN_PREDICTOR_2) {
+ newret = CONFREJ;
+ break;
+ }
+
+ ho->predictor_2 = 1;
+ if (p == p0
+ && ccp_test(f->unit, p, CILEN_PREDICTOR_2, 1) <= 0) {
+ newret = CONFREJ;
+ }
+ break;
+
+ default:
+ newret = CONFREJ;
+ }
+ }
+
+ if (newret == CONFNAK && dont_nak)
+ newret = CONFREJ;
+ if (!(newret == CONFACK || (newret == CONFNAK && ret == CONFREJ))) {
+ /* we're returning this option */
+ if (newret == CONFREJ && ret == CONFNAK)
+ retp = p0;
+ ret = newret;
+ if (p != retp)
+ BCOPY(p, retp, clen);
+ retp += clen;
+ }
+
+ p += clen;
+ len -= clen;
+ }
+
+ if (ret != CONFACK) {
+ if (ret == CONFREJ && *lenp == retp - p0)
+ all_rejected[f->unit] = 1;
+ else
+ *lenp = retp - p0;
+ }
+ return ret;
+}
+
+/*
+ * Make a string name for a compression method (or 2).
+ */
+static char *
+method_name(opt, opt2)
+ ccp_options *opt, *opt2;
+{
+ static char result[64];
+
+ if (!ANY_COMPRESS(*opt))
+ return "(none)";
+ switch (opt->method) {
+ case CI_DEFLATE:
+ case CI_DEFLATE_DRAFT:
+ if (opt2 != NULL && opt2->deflate_size != opt->deflate_size)
+ sprintf(result, "Deflate%s (%d/%d)",
+ (opt->method == CI_DEFLATE_DRAFT? "(old#)": ""),
+ opt->deflate_size, opt2->deflate_size);
+ else
+ sprintf(result, "Deflate%s (%d)",
+ (opt->method == CI_DEFLATE_DRAFT? "(old#)": ""),
+ opt->deflate_size);
+ break;
+ case CI_BSD_COMPRESS:
+ if (opt2 != NULL && opt2->bsd_bits != opt->bsd_bits)
+ sprintf(result, "BSD-Compress (%d/%d)", opt->bsd_bits,
+ opt2->bsd_bits);
+ else
+ sprintf(result, "BSD-Compress (%d)", opt->bsd_bits);
+ break;
+ case CI_PREDICTOR_1:
+ return "Predictor 1";
+ case CI_PREDICTOR_2:
+ return "Predictor 2";
+ default:
+ sprintf(result, "Method %d", opt->method);
+ }
+ return result;
+}
+
+/*
+ * CCP has come up - inform the kernel driver and log a message.
+ */
+static void
+ccp_up(f)
+ fsm *f;
+{
+ ccp_options *go = &ccp_gotoptions[f->unit];
+ ccp_options *ho = &ccp_hisoptions[f->unit];
+ char method1[64];
+
+ ccp_flags_set(f->unit, 1, 1);
+ if (ANY_COMPRESS(*go)) {
+ if (ANY_COMPRESS(*ho)) {
+ if (go->method == ho->method) {
+ syslog(LOG_NOTICE, "%s compression enabled",
+ method_name(go, ho));
+ } else {
+ strcpy(method1, method_name(go, NULL));
+ syslog(LOG_NOTICE, "%s / %s compression enabled",
+ method1, method_name(ho, NULL));
+ }
+ } else
+ syslog(LOG_NOTICE, "%s receive compression enabled",
+ method_name(go, NULL));
+ } else if (ANY_COMPRESS(*ho))
+ syslog(LOG_NOTICE, "%s transmit compression enabled",
+ method_name(ho, NULL));
+}
+
+/*
+ * CCP has gone down - inform the kernel driver.
+ */
+static void
+ccp_down(f)
+ fsm *f;
+{
+ if (ccp_localstate[f->unit] & RACK_PENDING)
+ UNTIMEOUT(ccp_rack_timeout, f);
+ ccp_localstate[f->unit] = 0;
+ ccp_flags_set(f->unit, 1, 0);
+}
+
+/*
+ * Print the contents of a CCP packet.
+ */
+static char *ccp_codenames[] = {
+ "ConfReq", "ConfAck", "ConfNak", "ConfRej",
+ "TermReq", "TermAck", "CodeRej",
+ NULL, NULL, NULL, NULL, NULL, NULL,
+ "ResetReq", "ResetAck",
+};
+
+static int
+ccp_printpkt(p, plen, printer, arg)
+ u_char *p;
+ int plen;
+ void (*printer) __P((void *, char *, ...));
+ void *arg;
+{
+ u_char *p0, *optend;
+ int code, id, len;
+ int optlen;
+
+ p0 = p;
+ if (plen < HEADERLEN)
+ return 0;
+ code = p[0];
+ id = p[1];
+ len = (p[2] << 8) + p[3];
+ if (len < HEADERLEN || len > plen)
+ return 0;
+
+ if (code >= 1 && code <= sizeof(ccp_codenames) / sizeof(char *)
+ && ccp_codenames[code-1] != NULL)
+ printer(arg, " %s", ccp_codenames[code-1]);
+ else
+ printer(arg, " code=0x%x", code);
+ printer(arg, " id=0x%x", id);
+ len -= HEADERLEN;
+ p += HEADERLEN;
+
+ switch (code) {
+ case CONFREQ:
+ case CONFACK:
+ case CONFNAK:
+ case CONFREJ:
+ /* print list of possible compression methods */
+ while (len >= 2) {
+ code = p[0];
+ optlen = p[1];
+ if (optlen < 2 || optlen > len)
+ break;
+ printer(arg, " <");
+ len -= optlen;
+ optend = p + optlen;
+ switch (code) {
+ case CI_DEFLATE:
+ case CI_DEFLATE_DRAFT:
+ if (optlen >= CILEN_DEFLATE) {
+ printer(arg, "deflate%s %d",
+ (code == CI_DEFLATE_DRAFT? "(old#)": ""),
+ DEFLATE_SIZE(p[2]));
+ if (DEFLATE_METHOD(p[2]) != DEFLATE_METHOD_VAL)
+ printer(arg, " method %d", DEFLATE_METHOD(p[2]));
+ if (p[3] != DEFLATE_CHK_SEQUENCE)
+ printer(arg, " check %d", p[3]);
+ p += CILEN_DEFLATE;
+ }
+ break;
+ case CI_BSD_COMPRESS:
+ if (optlen >= CILEN_BSD_COMPRESS) {
+ printer(arg, "bsd v%d %d", BSD_VERSION(p[2]),
+ BSD_NBITS(p[2]));
+ p += CILEN_BSD_COMPRESS;
+ }
+ break;
+ case CI_PREDICTOR_1:
+ if (optlen >= CILEN_PREDICTOR_1) {
+ printer(arg, "predictor 1");
+ p += CILEN_PREDICTOR_1;
+ }
+ break;
+ case CI_PREDICTOR_2:
+ if (optlen >= CILEN_PREDICTOR_2) {
+ printer(arg, "predictor 2");
+ p += CILEN_PREDICTOR_2;
+ }
+ break;
+ }
+ while (p < optend)
+ printer(arg, " %.2x", *p++);
+ printer(arg, ">");
+ }
+ break;
+
+ case TERMACK:
+ case TERMREQ:
+ if (len > 0 && *p >= ' ' && *p < 0x7f) {
+ print_string(p, len, printer, arg);
+ p += len;
+ len = 0;
+ }
+ break;
+ }
+
+ /* dump out the rest of the packet in hex */
+ while (--len >= 0)
+ printer(arg, " %.2x", *p++);
+
+ return p - p0;
+}
+
+/*
+ * We have received a packet that the decompressor failed to
+ * decompress. Here we would expect to issue a reset-request, but
+ * Motorola has a patent on resetting the compressor as a result of
+ * detecting an error in the decompressed data after decompression.
+ * (See US patent 5,130,993; international patent publication number
+ * WO 91/10289; Australian patent 73296/91.)
+ *
+ * So we ask the kernel whether the error was detected after
+ * decompression; if it was, we take CCP down, thus disabling
+ * compression :-(, otherwise we issue the reset-request.
+ */
+static void
+ccp_datainput(unit, pkt, len)
+ int unit;
+ u_char *pkt;
+ int len;
+{
+ fsm *f;
+
+ f = &ccp_fsm[unit];
+ if (f->state == OPENED) {
+ if (ccp_fatal_error(unit)) {
+ /*
+ * Disable compression by taking CCP down.
+ */
+ syslog(LOG_ERR, "Lost compression sync: disabling compression");
+ ccp_close(unit, "Lost compression sync");
+ } else {
+ /*
+ * Send a reset-request to reset the peer's compressor.
+ * We don't do that if we are still waiting for an
+ * acknowledgement to a previous reset-request.
+ */
+ if (!(ccp_localstate[f->unit] & RACK_PENDING)) {
+ fsm_sdata(f, CCP_RESETREQ, f->reqid = ++f->id, NULL, 0);
+ TIMEOUT(ccp_rack_timeout, f, RACKTIMEOUT);
+ ccp_localstate[f->unit] |= RACK_PENDING;
+ } else
+ ccp_localstate[f->unit] |= RREQ_REPEAT;
+ }
+ }
+}
+
+/*
+ * Timeout waiting for reset-ack.
+ */
+static void
+ccp_rack_timeout(arg)
+ void *arg;
+{
+ fsm *f = arg;
+
+ if (f->state == OPENED && ccp_localstate[f->unit] & RREQ_REPEAT) {
+ fsm_sdata(f, CCP_RESETREQ, f->reqid, NULL, 0);
+ TIMEOUT(ccp_rack_timeout, f, RACKTIMEOUT);
+ ccp_localstate[f->unit] &= ~RREQ_REPEAT;
+ } else
+ ccp_localstate[f->unit] &= ~RACK_PENDING;
+}
+
diff --git a/usr.sbin/pppd/ccp.h b/usr.sbin/pppd/ccp.h
new file mode 100644
index 0000000..a03ac4d
--- /dev/null
+++ b/usr.sbin/pppd/ccp.h
@@ -0,0 +1,50 @@
+/*
+ * ccp.h - Definitions for PPP Compression Control Protocol.
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies. This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ *
+ * $FreeBSD$
+ */
+
+typedef struct ccp_options {
+ u_int bsd_compress: 1; /* do BSD Compress? */
+ u_int deflate: 1; /* do Deflate? (RFC code) */
+ u_int baddeflate: 1; /* do Deflate? (Magnalink!) */
+ u_int predictor_1: 1; /* do Predictor-1? */
+ u_int predictor_2: 1; /* do Predictor-2? */
+ u_int deflate_correct: 1; /* use correct code for deflate? */
+ u_int deflate_draft: 1; /* use draft RFC code for deflate? */
+ u_short bsd_bits; /* # bits/code for BSD Compress */
+ u_short deflate_size; /* lg(window size) for Deflate */
+ u_short baddeflate_size; /* lg(window size) for Deflate */
+ short method; /* code for chosen compression method */
+} ccp_options;
+
+extern fsm ccp_fsm[];
+extern ccp_options ccp_wantoptions[];
+extern ccp_options ccp_gotoptions[];
+extern ccp_options ccp_allowoptions[];
+extern ccp_options ccp_hisoptions[];
+
+extern struct protent ccp_protent;
diff --git a/usr.sbin/pppd/chap.c b/usr.sbin/pppd/chap.c
new file mode 100644
index 0000000..c3acafb
--- /dev/null
+++ b/usr.sbin/pppd/chap.c
@@ -0,0 +1,870 @@
+/*
+ * chap.c - Challenge Handshake Authentication Protocol.
+ *
+ * Copyright (c) 1993 The Australian National University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Australian National University. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Copyright (c) 1991 Gregory M. Christy.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Gregory M. Christy. The name of the author may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char rcsid[] = "$FreeBSD$";
+#endif
+
+/*
+ * TODO:
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <syslog.h>
+#include <md5.h>
+
+#include "pppd.h"
+#include "chap.h"
+#include "md5.h"
+#ifdef CHAPMS
+#include "chap_ms.h"
+#endif
+
+/*
+ * Protocol entry points.
+ */
+static void ChapInit __P((int));
+static void ChapLowerUp __P((int));
+static void ChapLowerDown __P((int));
+static void ChapInput __P((int, u_char *, int));
+static void ChapProtocolReject __P((int));
+static int ChapPrintPkt __P((u_char *, int,
+ void (*) __P((void *, char *, ...)), void *));
+
+struct protent chap_protent = {
+ PPP_CHAP,
+ ChapInit,
+ ChapInput,
+ ChapProtocolReject,
+ ChapLowerUp,
+ ChapLowerDown,
+ NULL,
+ NULL,
+ ChapPrintPkt,
+ NULL,
+ 1,
+ "CHAP",
+ NULL,
+ NULL,
+ NULL
+};
+
+chap_state chap[NUM_PPP]; /* CHAP state; one for each unit */
+
+static void ChapChallengeTimeout __P((void *));
+static void ChapResponseTimeout __P((void *));
+static void ChapReceiveChallenge __P((chap_state *, u_char *, int, int));
+static void ChapRechallenge __P((void *));
+static void ChapReceiveResponse __P((chap_state *, u_char *, int, int));
+static void ChapReceiveSuccess __P((chap_state *, u_char *, int, int));
+static void ChapReceiveFailure __P((chap_state *, u_char *, int, int));
+static void ChapSendStatus __P((chap_state *, int));
+static void ChapSendChallenge __P((chap_state *));
+static void ChapSendResponse __P((chap_state *));
+static void ChapGenChallenge __P((chap_state *));
+
+extern double drand48 __P((void));
+extern void srand48 __P((long));
+
+/*
+ * ChapInit - Initialize a CHAP unit.
+ */
+static void
+ChapInit(unit)
+ int unit;
+{
+ chap_state *cstate = &chap[unit];
+
+ BZERO(cstate, sizeof(*cstate));
+ cstate->unit = unit;
+ cstate->clientstate = CHAPCS_INITIAL;
+ cstate->serverstate = CHAPSS_INITIAL;
+ cstate->timeouttime = CHAP_DEFTIMEOUT;
+ cstate->max_transmits = CHAP_DEFTRANSMITS;
+ /* random number generator is initialized in magic_init */
+}
+
+
+/*
+ * ChapAuthWithPeer - Authenticate us with our peer (start client).
+ *
+ */
+void
+ChapAuthWithPeer(unit, our_name, digest)
+ int unit;
+ char *our_name;
+ int digest;
+{
+ chap_state *cstate = &chap[unit];
+
+ cstate->resp_name = our_name;
+ cstate->resp_type = digest;
+
+ if (cstate->clientstate == CHAPCS_INITIAL ||
+ cstate->clientstate == CHAPCS_PENDING) {
+ /* lower layer isn't up - wait until later */
+ cstate->clientstate = CHAPCS_PENDING;
+ return;
+ }
+
+ /*
+ * We get here as a result of LCP coming up.
+ * So even if CHAP was open before, we will
+ * have to re-authenticate ourselves.
+ */
+ cstate->clientstate = CHAPCS_LISTEN;
+}
+
+
+/*
+ * ChapAuthPeer - Authenticate our peer (start server).
+ */
+void
+ChapAuthPeer(unit, our_name, digest)
+ int unit;
+ char *our_name;
+ int digest;
+{
+ chap_state *cstate = &chap[unit];
+
+ cstate->chal_name = our_name;
+ cstate->chal_type = digest;
+
+ if (cstate->serverstate == CHAPSS_INITIAL ||
+ cstate->serverstate == CHAPSS_PENDING) {
+ /* lower layer isn't up - wait until later */
+ cstate->serverstate = CHAPSS_PENDING;
+ return;
+ }
+
+ ChapGenChallenge(cstate);
+ ChapSendChallenge(cstate); /* crank it up dude! */
+ cstate->serverstate = CHAPSS_INITIAL_CHAL;
+}
+
+
+/*
+ * ChapChallengeTimeout - Timeout expired on sending challenge.
+ */
+static void
+ChapChallengeTimeout(arg)
+ void *arg;
+{
+ chap_state *cstate = (chap_state *) arg;
+
+ /* if we aren't sending challenges, don't worry. then again we */
+ /* probably shouldn't be here either */
+ if (cstate->serverstate != CHAPSS_INITIAL_CHAL &&
+ cstate->serverstate != CHAPSS_RECHALLENGE)
+ return;
+
+ if (cstate->chal_transmits >= cstate->max_transmits) {
+ /* give up on peer */
+ syslog(LOG_ERR, "Peer failed to respond to CHAP challenge");
+ cstate->serverstate = CHAPSS_BADAUTH;
+ auth_peer_fail(cstate->unit, PPP_CHAP);
+ return;
+ }
+
+ ChapSendChallenge(cstate); /* Re-send challenge */
+}
+
+
+/*
+ * ChapResponseTimeout - Timeout expired on sending response.
+ */
+static void
+ChapResponseTimeout(arg)
+ void *arg;
+{
+ chap_state *cstate = (chap_state *) arg;
+
+ /* if we aren't sending a response, don't worry. */
+ if (cstate->clientstate != CHAPCS_RESPONSE)
+ return;
+
+ ChapSendResponse(cstate); /* re-send response */
+}
+
+
+/*
+ * ChapRechallenge - Time to challenge the peer again.
+ */
+static void
+ChapRechallenge(arg)
+ void *arg;
+{
+ chap_state *cstate = (chap_state *) arg;
+
+ /* if we aren't sending a response, don't worry. */
+ if (cstate->serverstate != CHAPSS_OPEN)
+ return;
+
+ ChapGenChallenge(cstate);
+ ChapSendChallenge(cstate);
+ cstate->serverstate = CHAPSS_RECHALLENGE;
+}
+
+
+/*
+ * ChapLowerUp - The lower layer is up.
+ *
+ * Start up if we have pending requests.
+ */
+static void
+ChapLowerUp(unit)
+ int unit;
+{
+ chap_state *cstate = &chap[unit];
+
+ if (cstate->clientstate == CHAPCS_INITIAL)
+ cstate->clientstate = CHAPCS_CLOSED;
+ else if (cstate->clientstate == CHAPCS_PENDING)
+ cstate->clientstate = CHAPCS_LISTEN;
+
+ if (cstate->serverstate == CHAPSS_INITIAL)
+ cstate->serverstate = CHAPSS_CLOSED;
+ else if (cstate->serverstate == CHAPSS_PENDING) {
+ ChapGenChallenge(cstate);
+ ChapSendChallenge(cstate);
+ cstate->serverstate = CHAPSS_INITIAL_CHAL;
+ }
+}
+
+
+/*
+ * ChapLowerDown - The lower layer is down.
+ *
+ * Cancel all timeouts.
+ */
+static void
+ChapLowerDown(unit)
+ int unit;
+{
+ chap_state *cstate = &chap[unit];
+
+ /* Timeout(s) pending? Cancel if so. */
+ if (cstate->serverstate == CHAPSS_INITIAL_CHAL ||
+ cstate->serverstate == CHAPSS_RECHALLENGE)
+ UNTIMEOUT(ChapChallengeTimeout, cstate);
+ else if (cstate->serverstate == CHAPSS_OPEN
+ && cstate->chal_interval != 0)
+ UNTIMEOUT(ChapRechallenge, cstate);
+ if (cstate->clientstate == CHAPCS_RESPONSE)
+ UNTIMEOUT(ChapResponseTimeout, cstate);
+
+ cstate->clientstate = CHAPCS_INITIAL;
+ cstate->serverstate = CHAPSS_INITIAL;
+}
+
+
+/*
+ * ChapProtocolReject - Peer doesn't grok CHAP.
+ */
+static void
+ChapProtocolReject(unit)
+ int unit;
+{
+ chap_state *cstate = &chap[unit];
+
+ if (cstate->serverstate != CHAPSS_INITIAL &&
+ cstate->serverstate != CHAPSS_CLOSED)
+ auth_peer_fail(unit, PPP_CHAP);
+ if (cstate->clientstate != CHAPCS_INITIAL &&
+ cstate->clientstate != CHAPCS_CLOSED)
+ auth_withpeer_fail(unit, PPP_CHAP);
+ ChapLowerDown(unit); /* shutdown chap */
+}
+
+
+/*
+ * ChapInput - Input CHAP packet.
+ */
+static void
+ChapInput(unit, inpacket, packet_len)
+ int unit;
+ u_char *inpacket;
+ int packet_len;
+{
+ chap_state *cstate = &chap[unit];
+ u_char *inp;
+ u_char code, id;
+ int len;
+
+ /*
+ * Parse header (code, id and length).
+ * If packet too short, drop it.
+ */
+ inp = inpacket;
+ if (packet_len < CHAP_HEADERLEN) {
+ CHAPDEBUG((LOG_INFO, "ChapInput: rcvd short header."));
+ return;
+ }
+ GETCHAR(code, inp);
+ GETCHAR(id, inp);
+ GETSHORT(len, inp);
+ if (len < CHAP_HEADERLEN) {
+ CHAPDEBUG((LOG_INFO, "ChapInput: rcvd illegal length."));
+ return;
+ }
+ if (len > packet_len) {
+ CHAPDEBUG((LOG_INFO, "ChapInput: rcvd short packet."));
+ return;
+ }
+ len -= CHAP_HEADERLEN;
+
+ /*
+ * Action depends on code (as in fact it usually does :-).
+ */
+ switch (code) {
+ case CHAP_CHALLENGE:
+ ChapReceiveChallenge(cstate, inp, id, len);
+ break;
+
+ case CHAP_RESPONSE:
+ ChapReceiveResponse(cstate, inp, id, len);
+ break;
+
+ case CHAP_FAILURE:
+ ChapReceiveFailure(cstate, inp, id, len);
+ break;
+
+ case CHAP_SUCCESS:
+ ChapReceiveSuccess(cstate, inp, id, len);
+ break;
+
+ default: /* Need code reject? */
+ syslog(LOG_WARNING, "Unknown CHAP code (%d) received.", code);
+ break;
+ }
+}
+
+
+/*
+ * ChapReceiveChallenge - Receive Challenge and send Response.
+ */
+static void
+ChapReceiveChallenge(cstate, inp, id, len)
+ chap_state *cstate;
+ u_char *inp;
+ int id;
+ int len;
+{
+ int rchallenge_len;
+ u_char *rchallenge;
+ int secret_len;
+ char secret[MAXSECRETLEN];
+ char rhostname[256];
+ MD5_CTX mdContext;
+ u_char hash[MD5_SIGNATURE_SIZE];
+
+ CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: Rcvd id %d.", id));
+ if (cstate->clientstate == CHAPCS_CLOSED ||
+ cstate->clientstate == CHAPCS_PENDING) {
+ CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: in state %d",
+ cstate->clientstate));
+ return;
+ }
+
+ if (len < 2) {
+ CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: rcvd short packet."));
+ return;
+ }
+
+ GETCHAR(rchallenge_len, inp);
+ len -= sizeof (u_char) + rchallenge_len; /* now name field length */
+ if (len < 0) {
+ CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: rcvd short packet."));
+ return;
+ }
+ rchallenge = inp;
+ INCPTR(rchallenge_len, inp);
+
+ if (len >= sizeof(rhostname))
+ len = sizeof(rhostname) - 1;
+ BCOPY(inp, rhostname, len);
+ rhostname[len] = '\000';
+
+ CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: received name field '%s'",
+ rhostname));
+
+ /* Microsoft doesn't send their name back in the PPP packet */
+ if (remote_name[0] != 0 && (explicit_remote || rhostname[0] == 0)) {
+ strncpy(rhostname, remote_name, sizeof(rhostname));
+ rhostname[sizeof(rhostname) - 1] = 0;
+ CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: using '%s' as remote name",
+ rhostname));
+ }
+
+ /* get secret for authenticating ourselves with the specified host */
+ if (!get_secret(cstate->unit, cstate->resp_name, rhostname,
+ secret, &secret_len, 0)) {
+ secret_len = 0; /* assume null secret if can't find one */
+ syslog(LOG_WARNING, "No CHAP secret found for authenticating us to %s",
+ rhostname);
+ }
+
+ /* cancel response send timeout if necessary */
+ if (cstate->clientstate == CHAPCS_RESPONSE)
+ UNTIMEOUT(ChapResponseTimeout, cstate);
+
+ cstate->resp_id = id;
+ cstate->resp_transmits = 0;
+
+ /* generate MD based on negotiated type */
+ switch (cstate->resp_type) {
+
+ case CHAP_DIGEST_MD5:
+ MD5Init(&mdContext);
+ MD5Update(&mdContext, &cstate->resp_id, 1);
+ MD5Update(&mdContext, secret, secret_len);
+ MD5Update(&mdContext, rchallenge, rchallenge_len);
+ MD5Final(hash, &mdContext);
+ BCOPY(hash, cstate->response, MD5_SIGNATURE_SIZE);
+ cstate->resp_length = MD5_SIGNATURE_SIZE;
+ break;
+
+#ifdef CHAPMS
+ case CHAP_MICROSOFT:
+ ChapMS(cstate, rchallenge, rchallenge_len, secret, secret_len);
+ break;
+#endif
+
+ default:
+ CHAPDEBUG((LOG_INFO, "unknown digest type %d", cstate->resp_type));
+ return;
+ }
+
+ BZERO(secret, sizeof(secret));
+ ChapSendResponse(cstate);
+}
+
+
+/*
+ * ChapReceiveResponse - Receive and process response.
+ */
+static void
+ChapReceiveResponse(cstate, inp, id, len)
+ chap_state *cstate;
+ u_char *inp;
+ int id;
+ int len;
+{
+ u_char *remmd, remmd_len;
+ int secret_len, old_state;
+ int code;
+ char rhostname[256];
+ MD5_CTX mdContext;
+ char secret[MAXSECRETLEN];
+ u_char hash[MD5_SIGNATURE_SIZE];
+
+ CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: Rcvd id %d.", id));
+
+ if (cstate->serverstate == CHAPSS_CLOSED ||
+ cstate->serverstate == CHAPSS_PENDING) {
+ CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: in state %d",
+ cstate->serverstate));
+ return;
+ }
+
+ if (id != cstate->chal_id)
+ return; /* doesn't match ID of last challenge */
+
+ /*
+ * If we have received a duplicate or bogus Response,
+ * we have to send the same answer (Success/Failure)
+ * as we did for the first Response we saw.
+ */
+ if (cstate->serverstate == CHAPSS_OPEN) {
+ ChapSendStatus(cstate, CHAP_SUCCESS);
+ return;
+ }
+ if (cstate->serverstate == CHAPSS_BADAUTH) {
+ ChapSendStatus(cstate, CHAP_FAILURE);
+ return;
+ }
+
+ if (len < 2) {
+ CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: rcvd short packet."));
+ return;
+ }
+ GETCHAR(remmd_len, inp); /* get length of MD */
+ remmd = inp; /* get pointer to MD */
+ INCPTR(remmd_len, inp);
+
+ len -= sizeof (u_char) + remmd_len;
+ if (len < 0) {
+ CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: rcvd short packet."));
+ return;
+ }
+
+ UNTIMEOUT(ChapChallengeTimeout, cstate);
+
+ if (len >= sizeof(rhostname))
+ len = sizeof(rhostname) - 1;
+ BCOPY(inp, rhostname, len);
+ rhostname[len] = '\000';
+
+ CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: received name field: %s",
+ rhostname));
+
+ /*
+ * Get secret for authenticating them with us,
+ * do the hash ourselves, and compare the result.
+ */
+ code = CHAP_FAILURE;
+ if (!get_secret(cstate->unit, rhostname, cstate->chal_name,
+ secret, &secret_len, 1)) {
+ syslog(LOG_WARNING, "No CHAP secret found for authenticating %s",
+ rhostname);
+ } else {
+
+ /* generate MD based on negotiated type */
+ switch (cstate->chal_type) {
+
+ case CHAP_DIGEST_MD5: /* only MD5 is defined for now */
+ if (remmd_len != MD5_SIGNATURE_SIZE)
+ break; /* it's not even the right length */
+ MD5Init(&mdContext);
+ MD5Update(&mdContext, &cstate->chal_id, 1);
+ MD5Update(&mdContext, secret, secret_len);
+ MD5Update(&mdContext, cstate->challenge, cstate->chal_len);
+ MD5Final(hash, &mdContext);
+
+ /* compare local and remote MDs and send the appropriate status */
+ if (memcmp (hash, remmd, MD5_SIGNATURE_SIZE) == 0)
+ code = CHAP_SUCCESS; /* they are the same! */
+ break;
+
+ default:
+ CHAPDEBUG((LOG_INFO, "unknown digest type %d", cstate->chal_type));
+ }
+ }
+
+ BZERO(secret, sizeof(secret));
+ ChapSendStatus(cstate, code);
+
+ if (code == CHAP_SUCCESS) {
+ old_state = cstate->serverstate;
+ cstate->serverstate = CHAPSS_OPEN;
+ if (old_state == CHAPSS_INITIAL_CHAL) {
+ auth_peer_success(cstate->unit, PPP_CHAP, rhostname, len);
+ }
+ if (cstate->chal_interval != 0)
+ TIMEOUT(ChapRechallenge, cstate, cstate->chal_interval);
+ syslog(LOG_NOTICE, "CHAP peer authentication succeeded for %s",
+ rhostname);
+
+ } else {
+ syslog(LOG_ERR, "CHAP peer authentication failed for remote host %s",
+ rhostname);
+ cstate->serverstate = CHAPSS_BADAUTH;
+ auth_peer_fail(cstate->unit, PPP_CHAP);
+ }
+}
+
+/*
+ * ChapReceiveSuccess - Receive Success
+ */
+static void
+ChapReceiveSuccess(cstate, inp, id, len)
+ chap_state *cstate;
+ u_char *inp;
+ u_char id;
+ int len;
+{
+
+ CHAPDEBUG((LOG_INFO, "ChapReceiveSuccess: Rcvd id %d.", id));
+
+ if (cstate->clientstate == CHAPCS_OPEN)
+ /* presumably an answer to a duplicate response */
+ return;
+
+ if (cstate->clientstate != CHAPCS_RESPONSE) {
+ /* don't know what this is */
+ CHAPDEBUG((LOG_INFO, "ChapReceiveSuccess: in state %d\n",
+ cstate->clientstate));
+ return;
+ }
+
+ UNTIMEOUT(ChapResponseTimeout, cstate);
+
+ /*
+ * Print message.
+ */
+ if (len > 0)
+ PRINTMSG(inp, len);
+
+ cstate->clientstate = CHAPCS_OPEN;
+
+ auth_withpeer_success(cstate->unit, PPP_CHAP);
+}
+
+
+/*
+ * ChapReceiveFailure - Receive failure.
+ */
+static void
+ChapReceiveFailure(cstate, inp, id, len)
+ chap_state *cstate;
+ u_char *inp;
+ u_char id;
+ int len;
+{
+ CHAPDEBUG((LOG_INFO, "ChapReceiveFailure: Rcvd id %d.", id));
+
+ if (cstate->clientstate != CHAPCS_RESPONSE) {
+ /* don't know what this is */
+ CHAPDEBUG((LOG_INFO, "ChapReceiveFailure: in state %d\n",
+ cstate->clientstate));
+ return;
+ }
+
+ UNTIMEOUT(ChapResponseTimeout, cstate);
+
+ /*
+ * Print message.
+ */
+ if (len > 0)
+ PRINTMSG(inp, len);
+
+ syslog(LOG_ERR, "CHAP authentication failed");
+ auth_withpeer_fail(cstate->unit, PPP_CHAP);
+}
+
+
+/*
+ * ChapSendChallenge - Send an Authenticate challenge.
+ */
+static void
+ChapSendChallenge(cstate)
+ chap_state *cstate;
+{
+ u_char *outp;
+ int chal_len, name_len;
+ int outlen;
+
+ chal_len = cstate->chal_len;
+ name_len = strlen(cstate->chal_name);
+ outlen = CHAP_HEADERLEN + sizeof (u_char) + chal_len + name_len;
+ outp = outpacket_buf;
+
+ MAKEHEADER(outp, PPP_CHAP); /* paste in a CHAP header */
+
+ PUTCHAR(CHAP_CHALLENGE, outp);
+ PUTCHAR(cstate->chal_id, outp);
+ PUTSHORT(outlen, outp);
+
+ PUTCHAR(chal_len, outp); /* put length of challenge */
+ BCOPY(cstate->challenge, outp, chal_len);
+ INCPTR(chal_len, outp);
+
+ BCOPY(cstate->chal_name, outp, name_len); /* append hostname */
+
+ output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
+
+ CHAPDEBUG((LOG_INFO, "ChapSendChallenge: Sent id %d.", cstate->chal_id));
+
+ TIMEOUT(ChapChallengeTimeout, cstate, cstate->timeouttime);
+ ++cstate->chal_transmits;
+}
+
+
+/*
+ * ChapSendStatus - Send a status response (ack or nak).
+ */
+static void
+ChapSendStatus(cstate, code)
+ chap_state *cstate;
+ int code;
+{
+ u_char *outp;
+ int outlen, msglen;
+ char msg[256];
+
+ if (code == CHAP_SUCCESS)
+ sprintf(msg, "Welcome to %s.", hostname);
+ else
+ sprintf(msg, "I don't like you. Go 'way.");
+ msglen = strlen(msg);
+
+ outlen = CHAP_HEADERLEN + msglen;
+ outp = outpacket_buf;
+
+ MAKEHEADER(outp, PPP_CHAP); /* paste in a header */
+
+ PUTCHAR(code, outp);
+ PUTCHAR(cstate->chal_id, outp);
+ PUTSHORT(outlen, outp);
+ BCOPY(msg, outp, msglen);
+ output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
+
+ CHAPDEBUG((LOG_INFO, "ChapSendStatus: Sent code %d, id %d.", code,
+ cstate->chal_id));
+}
+
+/*
+ * ChapGenChallenge is used to generate a pseudo-random challenge string of
+ * a pseudo-random length between min_len and max_len. The challenge
+ * string and its length are stored in *cstate, and various other fields of
+ * *cstate are initialized.
+ */
+
+static void
+ChapGenChallenge(cstate)
+ chap_state *cstate;
+{
+ int chal_len;
+ u_char *ptr = cstate->challenge;
+ unsigned int i;
+
+ /* pick a random challenge length between MIN_CHALLENGE_LENGTH and
+ MAX_CHALLENGE_LENGTH */
+ chal_len = (unsigned) ((drand48() *
+ (MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH)) +
+ MIN_CHALLENGE_LENGTH);
+ cstate->chal_len = chal_len;
+ cstate->chal_id = ++cstate->id;
+ cstate->chal_transmits = 0;
+
+ /* generate a random string */
+ for (i = 0; i < chal_len; i++ )
+ *ptr++ = (char) (drand48() * 0xff);
+}
+
+/*
+ * ChapSendResponse - send a response packet with values as specified
+ * in *cstate.
+ */
+/* ARGSUSED */
+static void
+ChapSendResponse(cstate)
+ chap_state *cstate;
+{
+ u_char *outp;
+ int outlen, md_len, name_len;
+
+ md_len = cstate->resp_length;
+ name_len = strlen(cstate->resp_name);
+ outlen = CHAP_HEADERLEN + sizeof (u_char) + md_len + name_len;
+ outp = outpacket_buf;
+
+ MAKEHEADER(outp, PPP_CHAP);
+
+ PUTCHAR(CHAP_RESPONSE, outp); /* we are a response */
+ PUTCHAR(cstate->resp_id, outp); /* copy id from challenge packet */
+ PUTSHORT(outlen, outp); /* packet length */
+
+ PUTCHAR(md_len, outp); /* length of MD */
+ BCOPY(cstate->response, outp, md_len); /* copy MD to buffer */
+ INCPTR(md_len, outp);
+
+ BCOPY(cstate->resp_name, outp, name_len); /* append our name */
+
+ /* send the packet */
+ output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
+
+ cstate->clientstate = CHAPCS_RESPONSE;
+ TIMEOUT(ChapResponseTimeout, cstate, cstate->timeouttime);
+ ++cstate->resp_transmits;
+}
+
+/*
+ * ChapPrintPkt - print the contents of a CHAP packet.
+ */
+static char *ChapCodenames[] = {
+ "Challenge", "Response", "Success", "Failure"
+};
+
+static int
+ChapPrintPkt(p, plen, printer, arg)
+ u_char *p;
+ int plen;
+ void (*printer) __P((void *, char *, ...));
+ void *arg;
+{
+ int code, id, len;
+ int clen, nlen;
+ u_char x;
+
+ if (plen < CHAP_HEADERLEN)
+ return 0;
+ GETCHAR(code, p);
+ GETCHAR(id, p);
+ GETSHORT(len, p);
+ if (len < CHAP_HEADERLEN || len > plen)
+ return 0;
+
+ if (code >= 1 && code <= sizeof(ChapCodenames) / sizeof(char *))
+ printer(arg, " %s", ChapCodenames[code-1]);
+ else
+ printer(arg, " code=0x%x", code);
+ printer(arg, " id=0x%x", id);
+ len -= CHAP_HEADERLEN;
+ switch (code) {
+ case CHAP_CHALLENGE:
+ case CHAP_RESPONSE:
+ if (len < 1)
+ break;
+ clen = p[0];
+ if (len < clen + 1)
+ break;
+ ++p;
+ nlen = len - clen - 1;
+ printer(arg, " <");
+ for (; clen > 0; --clen) {
+ GETCHAR(x, p);
+ printer(arg, "%.2x", x);
+ }
+ printer(arg, ">, name = ");
+ print_string((char *)p, nlen, printer, arg);
+ break;
+ case CHAP_FAILURE:
+ case CHAP_SUCCESS:
+ printer(arg, " ");
+ print_string((char *)p, len, printer, arg);
+ break;
+ default:
+ for (clen = len; clen > 0; --clen) {
+ GETCHAR(x, p);
+ printer(arg, " %.2x", x);
+ }
+ }
+
+ return len + CHAP_HEADERLEN;
+}
diff --git a/usr.sbin/pppd/chap.h b/usr.sbin/pppd/chap.h
new file mode 100644
index 0000000..4a4f383
--- /dev/null
+++ b/usr.sbin/pppd/chap.h
@@ -0,0 +1,124 @@
+/*
+ * chap.h - Challenge Handshake Authentication Protocol definitions.
+ *
+ * Copyright (c) 1993 The Australian National University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Australian National University. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Copyright (c) 1991 Gregory M. Christy
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the author.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __CHAP_INCLUDE__
+
+/* Code + ID + length */
+#define CHAP_HEADERLEN 4
+
+/*
+ * CHAP codes.
+ */
+
+#define CHAP_DIGEST_MD5 5 /* use MD5 algorithm */
+#define MD5_SIGNATURE_SIZE 16 /* 16 bytes in a MD5 message digest */
+#define CHAP_MICROSOFT 0x80 /* use Microsoft-compatible alg. */
+#define MS_CHAP_RESPONSE_LEN 49 /* Response length for MS-CHAP */
+
+#define CHAP_CHALLENGE 1
+#define CHAP_RESPONSE 2
+#define CHAP_SUCCESS 3
+#define CHAP_FAILURE 4
+
+/*
+ * Challenge lengths (for challenges we send) and other limits.
+ */
+#define MIN_CHALLENGE_LENGTH 32
+#define MAX_CHALLENGE_LENGTH 64
+#define MAX_RESPONSE_LENGTH 64 /* sufficient for MD5 or MS-CHAP */
+
+/*
+ * Each interface is described by a chap structure.
+ */
+
+typedef struct chap_state {
+ int unit; /* Interface unit number */
+ int clientstate; /* Client state */
+ int serverstate; /* Server state */
+ u_char challenge[MAX_CHALLENGE_LENGTH]; /* last challenge string sent */
+ u_char chal_len; /* challenge length */
+ u_char chal_id; /* ID of last challenge */
+ u_char chal_type; /* hash algorithm for challenges */
+ u_char id; /* Current id */
+ char *chal_name; /* Our name to use with challenge */
+ int chal_interval; /* Time until we challenge peer again */
+ int timeouttime; /* Timeout time in seconds */
+ int max_transmits; /* Maximum # of challenge transmissions */
+ int chal_transmits; /* Number of transmissions of challenge */
+ int resp_transmits; /* Number of transmissions of response */
+ u_char response[MAX_RESPONSE_LENGTH]; /* Response to send */
+ u_char resp_length; /* length of response */
+ u_char resp_id; /* ID for response messages */
+ u_char resp_type; /* hash algorithm for responses */
+ char *resp_name; /* Our name to send with response */
+} chap_state;
+
+
+/*
+ * Client (peer) states.
+ */
+#define CHAPCS_INITIAL 0 /* Lower layer down, not opened */
+#define CHAPCS_CLOSED 1 /* Lower layer up, not opened */
+#define CHAPCS_PENDING 2 /* Auth us to peer when lower up */
+#define CHAPCS_LISTEN 3 /* Listening for a challenge */
+#define CHAPCS_RESPONSE 4 /* Sent response, waiting for status */
+#define CHAPCS_OPEN 5 /* We've received Success */
+
+/*
+ * Server (authenticator) states.
+ */
+#define CHAPSS_INITIAL 0 /* Lower layer down, not opened */
+#define CHAPSS_CLOSED 1 /* Lower layer up, not opened */
+#define CHAPSS_PENDING 2 /* Auth peer when lower up */
+#define CHAPSS_INITIAL_CHAL 3 /* We've sent the first challenge */
+#define CHAPSS_OPEN 4 /* We've sent a Success msg */
+#define CHAPSS_RECHALLENGE 5 /* We've sent another challenge */
+#define CHAPSS_BADAUTH 6 /* We've sent a Failure msg */
+
+/*
+ * Timeouts.
+ */
+#define CHAP_DEFTIMEOUT 3 /* Timeout time in seconds */
+#define CHAP_DEFTRANSMITS 10 /* max # times to send challenge */
+
+extern chap_state chap[];
+
+void ChapAuthWithPeer __P((int, char *, int));
+void ChapAuthPeer __P((int, char *, int));
+
+extern struct protent chap_protent;
+
+#define __CHAP_INCLUDE__
+#endif /* __CHAP_INCLUDE__ */
diff --git a/usr.sbin/pppd/chap_ms.c b/usr.sbin/pppd/chap_ms.c
new file mode 100644
index 0000000..c3e1f6e
--- /dev/null
+++ b/usr.sbin/pppd/chap_ms.c
@@ -0,0 +1,335 @@
+/*
+ * chap_ms.c - Microsoft MS-CHAP compatible implementation.
+ *
+ * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited.
+ * http://www.strataware.com/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Eric Rosenquist. The name of the author may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/*
+ * Modifications by Lauri Pesonen / lpesonen@clinet.fi, april 1997
+ *
+ * Implemented LANManager type password response to MS-CHAP challenges.
+ * Now pppd provides both NT style and LANMan style blocks, and the
+ * prefered is set by option "ms-lanman". Default is to use NT.
+ * The hash text (StdText) was taken from Win95 RASAPI32.DLL.
+ *
+ * You should also use DOMAIN\\USERNAME as described in README.MSCHAP80
+ */
+
+#ifndef lint
+static char rcsid[] = "$FreeBSD$";
+#endif
+
+#ifdef CHAPMS
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <syslog.h>
+#include <unistd.h>
+#ifdef HAVE_CRYPT_H
+#include <crypt.h>
+#endif
+
+#include "pppd.h"
+#include "chap.h"
+#include "chap_ms.h"
+#include "md4.h"
+
+#ifndef USE_CRYPT
+#include <openssl/des.h>
+#endif
+
+typedef struct {
+ u_char LANManResp[24];
+ u_char NTResp[24];
+ u_char UseNT; /* If 1, ignore the LANMan response field */
+} MS_ChapResponse;
+/* We use MS_CHAP_RESPONSE_LEN, rather than sizeof(MS_ChapResponse),
+ in case this struct gets padded. */
+
+
+static void ChallengeResponse __P((u_char *, u_char *, u_char *));
+static void DesEncrypt __P((u_char *, u_char *, u_char *));
+static void MakeKey __P((u_char *, u_char *));
+static u_char Get7Bits __P((u_char *, int));
+static void ChapMS_NT __P((char *, int, char *, int, MS_ChapResponse *));
+#ifdef MSLANMAN
+static void ChapMS_LANMan __P((char *, int, char *, int, MS_ChapResponse *));
+#endif
+
+#ifdef USE_CRYPT
+static void Expand __P((u_char *, u_char *));
+static void Collapse __P((u_char *, u_char *));
+#endif
+
+static void
+ChallengeResponse(challenge, pwHash, response)
+ u_char *challenge; /* IN 8 octets */
+ u_char *pwHash; /* IN 16 octets */
+ u_char *response; /* OUT 24 octets */
+{
+ char ZPasswordHash[21];
+
+ BZERO(ZPasswordHash, sizeof(ZPasswordHash));
+ BCOPY(pwHash, ZPasswordHash, MD4_SIGNATURE_SIZE);
+
+#if 0
+ log_packet(ZPasswordHash, sizeof(ZPasswordHash), "ChallengeResponse - ZPasswordHash", LOG_DEBUG);
+#endif
+
+ DesEncrypt(challenge, ZPasswordHash + 0, response + 0);
+ DesEncrypt(challenge, ZPasswordHash + 7, response + 8);
+ DesEncrypt(challenge, ZPasswordHash + 14, response + 16);
+
+#if 0
+ log_packet(response, 24, "ChallengeResponse - response", LOG_DEBUG);
+#endif
+}
+
+
+#ifdef USE_CRYPT
+static void
+DesEncrypt(clear, key, cipher)
+ u_char *clear; /* IN 8 octets */
+ u_char *key; /* IN 7 octets */
+ u_char *cipher; /* OUT 8 octets */
+{
+ u_char des_key[8];
+ u_char crypt_key[66];
+ u_char des_input[66];
+
+ MakeKey(key, des_key);
+
+ Expand(des_key, crypt_key);
+ setkey(crypt_key);
+
+#if 0
+ CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet input : %02X%02X%02X%02X%02X%02X%02X%02X",
+ clear[0], clear[1], clear[2], clear[3], clear[4], clear[5], clear[6], clear[7]));
+#endif
+
+ Expand(clear, des_input);
+ encrypt(des_input, 0);
+ Collapse(des_input, cipher);
+
+#if 0
+ CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet output: %02X%02X%02X%02X%02X%02X%02X%02X",
+ cipher[0], cipher[1], cipher[2], cipher[3], cipher[4], cipher[5], cipher[6], cipher[7]));
+#endif
+}
+
+#else /* USE_CRYPT */
+
+static void
+DesEncrypt(clear, key, cipher)
+ u_char *clear; /* IN 8 octets */
+ u_char *key; /* IN 7 octets */
+ u_char *cipher; /* OUT 8 octets */
+{
+ des_cblock des_key;
+ des_key_schedule key_schedule;
+
+ MakeKey(key, des_key);
+
+ des_set_key(&des_key, key_schedule);
+
+#if 0
+ CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet input : %02X%02X%02X%02X%02X%02X%02X%02X",
+ clear[0], clear[1], clear[2], clear[3], clear[4], clear[5], clear[6], clear[7]));
+#endif
+
+ des_ecb_encrypt((des_cblock *)clear, (des_cblock *)cipher, key_schedule, 1);
+
+#if 0
+ CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet output: %02X%02X%02X%02X%02X%02X%02X%02X",
+ cipher[0], cipher[1], cipher[2], cipher[3], cipher[4], cipher[5], cipher[6], cipher[7]));
+#endif
+}
+
+#endif /* USE_CRYPT */
+
+
+static u_char Get7Bits(input, startBit)
+ u_char *input;
+ int startBit;
+{
+ register unsigned int word;
+
+ word = (unsigned)input[startBit / 8] << 8;
+ word |= (unsigned)input[startBit / 8 + 1];
+
+ word >>= 15 - (startBit % 8 + 7);
+
+ return word & 0xFE;
+}
+
+#ifdef USE_CRYPT
+
+/* in == 8-byte string (expanded version of the 56-bit key)
+ * out == 64-byte string where each byte is either 1 or 0
+ * Note that the low-order "bit" is always ignored by by setkey()
+ */
+static void Expand(in, out)
+ u_char *in;
+ u_char *out;
+{
+ int j, c;
+ int i;
+
+ for(i = 0; i < 64; in++){
+ c = *in;
+ for(j = 7; j >= 0; j--)
+ *out++ = (c >> j) & 01;
+ i += 8;
+ }
+}
+
+/* The inverse of Expand
+ */
+static void Collapse(in, out)
+ u_char *in;
+ u_char *out;
+{
+ int j;
+ int i;
+ unsigned int c;
+
+ for (i = 0; i < 64; i += 8, out++) {
+ c = 0;
+ for (j = 7; j >= 0; j--, in++)
+ c |= *in << j;
+ *out = c & 0xff;
+ }
+}
+#endif
+
+static void MakeKey(key, des_key)
+ u_char *key; /* IN 56 bit DES key missing parity bits */
+ u_char *des_key; /* OUT 64 bit DES key with parity bits added */
+{
+ des_key[0] = Get7Bits(key, 0);
+ des_key[1] = Get7Bits(key, 7);
+ des_key[2] = Get7Bits(key, 14);
+ des_key[3] = Get7Bits(key, 21);
+ des_key[4] = Get7Bits(key, 28);
+ des_key[5] = Get7Bits(key, 35);
+ des_key[6] = Get7Bits(key, 42);
+ des_key[7] = Get7Bits(key, 49);
+
+#ifndef USE_CRYPT
+ des_set_odd_parity((des_cblock *)des_key);
+#endif
+
+#if 0
+ CHAPDEBUG((LOG_INFO, "MakeKey: 56-bit input : %02X%02X%02X%02X%02X%02X%02X",
+ key[0], key[1], key[2], key[3], key[4], key[5], key[6]));
+ CHAPDEBUG((LOG_INFO, "MakeKey: 64-bit output: %02X%02X%02X%02X%02X%02X%02X%02X",
+ des_key[0], des_key[1], des_key[2], des_key[3], des_key[4], des_key[5], des_key[6], des_key[7]));
+#endif
+}
+
+static void
+ChapMS_NT(rchallenge, rchallenge_len, secret, secret_len, response)
+ char *rchallenge;
+ int rchallenge_len;
+ char *secret;
+ int secret_len;
+ MS_ChapResponse *response;
+{
+ int i;
+ MD4_CTX md4Context;
+ u_char hash[MD4_SIGNATURE_SIZE];
+ u_char unicodePassword[MAX_NT_PASSWORD * 2];
+
+ /* Initialize the Unicode version of the secret (== password). */
+ /* This implicitly supports 8-bit ISO8859/1 characters. */
+ BZERO(unicodePassword, sizeof(unicodePassword));
+ for (i = 0; i < secret_len; i++)
+ unicodePassword[i * 2] = (u_char)secret[i];
+
+ MD4Init(&md4Context);
+ MD4Update(&md4Context, unicodePassword, secret_len * 2); /* Unicode is 2 bytes/char */
+
+ MD4Final(hash, &md4Context); /* Tell MD4 we're done */
+
+ ChallengeResponse(rchallenge, hash, response->NTResp);
+}
+
+#ifdef MSLANMAN
+static u_char *StdText = (u_char *)"KGS!@#$%"; /* key from rasapi32.dll */
+
+static void
+ChapMS_LANMan(rchallenge, rchallenge_len, secret, secret_len, response)
+ char *rchallenge;
+ int rchallenge_len;
+ char *secret;
+ int secret_len;
+ MS_ChapResponse *response;
+{
+ int i;
+ u_char UcasePassword[MAX_NT_PASSWORD]; /* max is actually 14 */
+ u_char PasswordHash[MD4_SIGNATURE_SIZE];
+
+ /* LANMan password is case insensitive */
+ BZERO(UcasePassword, sizeof(UcasePassword));
+ for (i = 0; i < secret_len; i++)
+ UcasePassword[i] = (u_char)toupper(secret[i]);
+ DesEncrypt( StdText, UcasePassword + 0, PasswordHash + 0 );
+ DesEncrypt( StdText, UcasePassword + 7, PasswordHash + 8 );
+ ChallengeResponse(rchallenge, PasswordHash, response->LANManResp);
+}
+#endif
+
+void
+ChapMS(cstate, rchallenge, rchallenge_len, secret, secret_len)
+ chap_state *cstate;
+ char *rchallenge;
+ int rchallenge_len;
+ char *secret;
+ int secret_len;
+{
+ MS_ChapResponse response;
+#ifdef MSLANMAN
+ extern int ms_lanman;
+#endif
+
+#if 0
+ CHAPDEBUG((LOG_INFO, "ChapMS: secret is '%.*s'", secret_len, secret));
+#endif
+ BZERO(&response, sizeof(response));
+
+ /* Calculate both always */
+ ChapMS_NT(rchallenge, rchallenge_len, secret, secret_len, &response);
+
+#ifdef MSLANMAN
+ ChapMS_LANMan(rchallenge, rchallenge_len, secret, secret_len, &response);
+
+ /* prefered method is set by option */
+ response.UseNT = !ms_lanman;
+#else
+ response.UseNT = 1;
+#endif
+
+ BCOPY(&response, cstate->response, MS_CHAP_RESPONSE_LEN);
+ cstate->resp_length = MS_CHAP_RESPONSE_LEN;
+}
+
+#endif /* CHAPMS */
diff --git a/usr.sbin/pppd/chap_ms.h b/usr.sbin/pppd/chap_ms.h
new file mode 100644
index 0000000..7c00883
--- /dev/null
+++ b/usr.sbin/pppd/chap_ms.h
@@ -0,0 +1,33 @@
+/*
+ * chap.h - Challenge Handshake Authentication Protocol definitions.
+ *
+ * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited.
+ * http://www.strataware.com/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Eric Rosenquist. The name of the author may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __CHAPMS_INCLUDE__
+
+#define MD4_SIGNATURE_SIZE 16 /* 16 bytes in a MD4 message digest */
+#define MAX_NT_PASSWORD 256 /* Maximum number of (Unicode) chars in an NT password */
+
+void ChapMS __P((chap_state *, char *, int, char *, int));
+
+#define __CHAPMS_INCLUDE__
+#endif /* __CHAPMS_INCLUDE__ */
diff --git a/usr.sbin/pppd/demand.c b/usr.sbin/pppd/demand.c
new file mode 100644
index 0000000..59c0281
--- /dev/null
+++ b/usr.sbin/pppd/demand.c
@@ -0,0 +1,348 @@
+/*
+ * demand.c - Support routines for demand-dialling.
+ *
+ * Copyright (c) 1993 The Australian National University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Australian National University. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char rcsid[] = "$FreeBSD$";
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <netdb.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#ifdef PPP_FILTER
+#include <net/if.h>
+#include <net/bpf.h>
+#include <pcap.h>
+#endif
+
+#include "pppd.h"
+#include "fsm.h"
+#include "ipcp.h"
+#include "lcp.h"
+
+char *frame;
+int framelen;
+int framemax;
+int escape_flag;
+int flush_flag;
+int fcs;
+
+struct packet {
+ int length;
+ struct packet *next;
+ unsigned char data[1];
+};
+
+struct packet *pend_q;
+struct packet *pend_qtail;
+
+static int active_packet __P((unsigned char *, int));
+
+/*
+ * demand_conf - configure the interface for doing dial-on-demand.
+ */
+void
+demand_conf()
+{
+ int i;
+ struct protent *protp;
+
+/* framemax = lcp_allowoptions[0].mru;
+ if (framemax < PPP_MRU) */
+ framemax = PPP_MRU;
+ framemax += PPP_HDRLEN + PPP_FCSLEN;
+ frame = malloc(framemax);
+ if (frame == NULL)
+ novm("demand frame");
+ framelen = 0;
+ pend_q = NULL;
+ escape_flag = 0;
+ flush_flag = 0;
+ fcs = PPP_INITFCS;
+
+ ppp_send_config(0, PPP_MRU, (u_int32_t) 0, 0, 0);
+ ppp_recv_config(0, PPP_MRU, (u_int32_t) 0, 0, 0);
+
+#ifdef PPP_FILTER
+ set_filters(&pass_filter, &active_filter);
+#endif
+
+ /*
+ * Call the demand_conf procedure for each protocol that's got one.
+ */
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (protp->enabled_flag && protp->demand_conf != NULL)
+ if (!((*protp->demand_conf)(0)))
+ die(1);
+}
+
+
+/*
+ * demand_block - set each network protocol to block further packets.
+ */
+void
+demand_block()
+{
+ int i;
+ struct protent *protp;
+
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (protp->enabled_flag && protp->demand_conf != NULL)
+ sifnpmode(0, protp->protocol & ~0x8000, NPMODE_QUEUE);
+ get_loop_output();
+}
+
+/*
+ * demand_discard - set each network protocol to discard packets
+ * with an error.
+ */
+void
+demand_discard()
+{
+ struct packet *pkt, *nextpkt;
+ int i;
+ struct protent *protp;
+
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (protp->enabled_flag && protp->demand_conf != NULL)
+ sifnpmode(0, protp->protocol & ~0x8000, NPMODE_ERROR);
+ get_loop_output();
+
+ /* discard all saved packets */
+ for (pkt = pend_q; pkt != NULL; pkt = nextpkt) {
+ nextpkt = pkt->next;
+ free(pkt);
+ }
+ pend_q = NULL;
+ framelen = 0;
+ flush_flag = 0;
+ escape_flag = 0;
+ fcs = PPP_INITFCS;
+}
+
+/*
+ * demand_unblock - set each enabled network protocol to pass packets.
+ */
+void
+demand_unblock()
+{
+ int i;
+ struct protent *protp;
+
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (protp->enabled_flag && protp->demand_conf != NULL)
+ sifnpmode(0, protp->protocol & ~0x8000, NPMODE_PASS);
+}
+
+/*
+ * FCS lookup table as calculated by genfcstab.
+ */
+static u_short fcstab[256] = {
+ 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+ 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+ 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+ 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+ 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+ 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+ 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+ 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+ 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+ 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+ 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+ 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+ 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+ 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+ 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+ 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+ 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+ 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+ 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+ 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+ 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+ 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+ 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+ 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+ 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+ 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+ 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+ 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+ 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+ 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+ 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+ 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+};
+
+/*
+ * loop_chars - process characters received from the loopback.
+ * Calls loop_frame when a complete frame has been accumulated.
+ * Return value is 1 if we need to bring up the link, 0 otherwise.
+ */
+int
+loop_chars(p, n)
+ unsigned char *p;
+ int n;
+{
+ int c, rv;
+
+ rv = 0;
+ for (; n > 0; --n) {
+ c = *p++;
+ if (c == PPP_FLAG) {
+ if (!escape_flag && !flush_flag
+ && framelen > 2 && fcs == PPP_GOODFCS) {
+ framelen -= 2;
+ if (loop_frame(frame, framelen))
+ rv = 1;
+ }
+ framelen = 0;
+ flush_flag = 0;
+ escape_flag = 0;
+ fcs = PPP_INITFCS;
+ continue;
+ }
+ if (flush_flag)
+ continue;
+ if (escape_flag) {
+ c ^= PPP_TRANS;
+ escape_flag = 0;
+ } else if (c == PPP_ESCAPE) {
+ escape_flag = 1;
+ continue;
+ }
+ if (framelen >= framemax) {
+ flush_flag = 1;
+ continue;
+ }
+ frame[framelen++] = c;
+ fcs = PPP_FCS(fcs, c);
+ }
+ return rv;
+}
+
+/*
+ * loop_frame - given a frame obtained from the loopback,
+ * decide whether to bring up the link or not, and, if we want
+ * to transmit this frame later, put it on the pending queue.
+ * Return value is 1 if we need to bring up the link, 0 otherwise.
+ * We assume that the kernel driver has already applied the
+ * pass_filter, so we won't get packets it rejected.
+ * We apply the active_filter to see if we want this packet to
+ * bring up the link.
+ */
+int
+loop_frame(frame, len)
+ unsigned char *frame;
+ int len;
+{
+ struct packet *pkt;
+
+ /* log_packet(frame, len, "from loop: ", LOG_DEBUG); */
+ if (len < PPP_HDRLEN)
+ return 0;
+ if ((PPP_PROTOCOL(frame) & 0x8000) != 0)
+ return 0; /* shouldn't get any of these anyway */
+ if (!active_packet(frame, len))
+ return 0;
+
+ pkt = (struct packet *) malloc(sizeof(struct packet) + len);
+ if (pkt != NULL) {
+ pkt->length = len;
+ pkt->next = NULL;
+ memcpy(pkt->data, frame, len);
+ if (pend_q == NULL)
+ pend_q = pkt;
+ else
+ pend_qtail->next = pkt;
+ pend_qtail = pkt;
+ }
+ return 1;
+}
+
+/*
+ * demand_rexmit - Resend all those frames which we got via the
+ * loopback, now that the real serial link is up.
+ */
+void
+demand_rexmit(proto)
+ int proto;
+{
+ struct packet *pkt, *prev, *nextpkt;
+
+ prev = NULL;
+ pkt = pend_q;
+ pend_q = NULL;
+ for (; pkt != NULL; pkt = nextpkt) {
+ nextpkt = pkt->next;
+ if (PPP_PROTOCOL(pkt->data) == proto) {
+ output(0, pkt->data, pkt->length);
+ free(pkt);
+ } else {
+ if (prev == NULL)
+ pend_q = pkt;
+ else
+ prev->next = pkt;
+ prev = pkt;
+ }
+ }
+ pend_qtail = prev;
+ if (prev != NULL)
+ prev->next = NULL;
+}
+
+/*
+ * Scan a packet to decide whether it is an "active" packet,
+ * that is, whether it is worth bringing up the link for.
+ */
+static int
+active_packet(p, len)
+ unsigned char *p;
+ int len;
+{
+ int proto, i;
+ struct protent *protp;
+
+ if (len < PPP_HDRLEN)
+ return 0;
+ proto = PPP_PROTOCOL(p);
+#ifdef PPP_FILTER
+ if (active_filter.bf_len != 0
+ && bpf_filter(active_filter.bf_insns, frame, len, len) == 0)
+ return 0;
+#endif
+ for (i = 0; (protp = protocols[i]) != NULL; ++i) {
+ if (protp->protocol < 0xC000 && (protp->protocol & ~0x8000) == proto) {
+ if (!protp->enabled_flag)
+ return 0;
+ if (protp->active_pkt == NULL)
+ return 1;
+ return (*protp->active_pkt)(p, len);
+ }
+ }
+ return 0; /* not a supported protocol !!?? */
+}
diff --git a/usr.sbin/pppd/fsm.c b/usr.sbin/pppd/fsm.c
new file mode 100644
index 0000000..a1fd948
--- /dev/null
+++ b/usr.sbin/pppd/fsm.c
@@ -0,0 +1,798 @@
+/*
+ * fsm.c - {Link, IP} Control Protocol Finite State Machine.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char rcsid[] = "$FreeBSD$";
+#endif
+
+/*
+ * TODO:
+ * Randomize fsm id on link/init.
+ * Deal with variable outgoing MTU.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <syslog.h>
+
+#include "pppd.h"
+#include "fsm.h"
+
+static void fsm_timeout __P((void *));
+static void fsm_rconfreq __P((fsm *, int, u_char *, int));
+static void fsm_rconfack __P((fsm *, int, u_char *, int));
+static void fsm_rconfnakrej __P((fsm *, int, int, u_char *, int));
+static void fsm_rtermreq __P((fsm *, int, u_char *, int));
+static void fsm_rtermack __P((fsm *));
+static void fsm_rcoderej __P((fsm *, u_char *, int));
+static void fsm_sconfreq __P((fsm *, int));
+
+#define PROTO_NAME(f) ((f)->callbacks->proto_name)
+
+int peer_mru[NUM_PPP];
+
+
+/*
+ * fsm_init - Initialize fsm.
+ *
+ * Initialize fsm state.
+ */
+void
+fsm_init(f)
+ fsm *f;
+{
+ f->state = INITIAL;
+ f->flags = 0;
+ f->id = 0; /* XXX Start with random id? */
+ f->timeouttime = DEFTIMEOUT;
+ f->maxconfreqtransmits = DEFMAXCONFREQS;
+ f->maxtermtransmits = DEFMAXTERMREQS;
+ f->maxnakloops = DEFMAXNAKLOOPS;
+ f->term_reason_len = 0;
+}
+
+
+/*
+ * fsm_lowerup - The lower layer is up.
+ */
+void
+fsm_lowerup(f)
+ fsm *f;
+{
+ switch( f->state ){
+ case INITIAL:
+ f->state = CLOSED;
+ break;
+
+ case STARTING:
+ if( f->flags & OPT_SILENT )
+ f->state = STOPPED;
+ else {
+ /* Send an initial configure-request */
+ fsm_sconfreq(f, 0);
+ f->state = REQSENT;
+ }
+ break;
+
+ default:
+ FSMDEBUG((LOG_INFO, "%s: Up event in state %d!",
+ PROTO_NAME(f), f->state));
+ }
+}
+
+
+/*
+ * fsm_lowerdown - The lower layer is down.
+ *
+ * Cancel all timeouts and inform upper layers.
+ */
+void
+fsm_lowerdown(f)
+ fsm *f;
+{
+ switch( f->state ){
+ case CLOSED:
+ f->state = INITIAL;
+ break;
+
+ case STOPPED:
+ f->state = STARTING;
+ if( f->callbacks->starting )
+ (*f->callbacks->starting)(f);
+ break;
+
+ case CLOSING:
+ f->state = INITIAL;
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ break;
+
+ case STOPPING:
+ case REQSENT:
+ case ACKRCVD:
+ case ACKSENT:
+ f->state = STARTING;
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ break;
+
+ case OPENED:
+ if( f->callbacks->down )
+ (*f->callbacks->down)(f);
+ f->state = STARTING;
+ break;
+
+ default:
+ FSMDEBUG((LOG_INFO, "%s: Down event in state %d!",
+ PROTO_NAME(f), f->state));
+ }
+}
+
+
+/*
+ * fsm_open - Link is allowed to come up.
+ */
+void
+fsm_open(f)
+ fsm *f;
+{
+ switch( f->state ){
+ case INITIAL:
+ f->state = STARTING;
+ if( f->callbacks->starting )
+ (*f->callbacks->starting)(f);
+ break;
+
+ case CLOSED:
+ if( f->flags & OPT_SILENT )
+ f->state = STOPPED;
+ else {
+ /* Send an initial configure-request */
+ fsm_sconfreq(f, 0);
+ f->state = REQSENT;
+ }
+ break;
+
+ case CLOSING:
+ f->state = STOPPING;
+ /* 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..7b43b7a
--- /dev/null
+++ b/usr.sbin/pppd/fsm.h
@@ -0,0 +1,144 @@
+/*
+ * fsm.h - {Link, IP} Control Protocol Finite State Machine definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Packet header = Code, id, length.
+ */
+#define HEADERLEN (sizeof (u_char) + sizeof (u_char) + sizeof (u_short))
+
+
+/*
+ * CP (LCP, IPCP, etc.) codes.
+ */
+#define CONFREQ 1 /* Configuration Request */
+#define CONFACK 2 /* Configuration Ack */
+#define CONFNAK 3 /* Configuration Nak */
+#define CONFREJ 4 /* Configuration Reject */
+#define TERMREQ 5 /* Termination Request */
+#define TERMACK 6 /* Termination Ack */
+#define CODEREJ 7 /* Code Reject */
+
+
+/*
+ * Each FSM is described by an fsm structure and fsm callbacks.
+ */
+typedef struct fsm {
+ int unit; /* Interface unit number */
+ int protocol; /* Data Link Layer Protocol field value */
+ int state; /* State */
+ int flags; /* Contains option bits */
+ u_char id; /* Current id */
+ u_char reqid; /* Current request id */
+ u_char seen_ack; /* Have received valid Ack/Nak/Rej to Req */
+ int timeouttime; /* Timeout time in milliseconds */
+ int maxconfreqtransmits; /* Maximum Configure-Request transmissions */
+ int retransmits; /* Number of retransmissions left */
+ int maxtermtransmits; /* Maximum Terminate-Request transmissions */
+ int nakloops; /* Number of nak loops since last ack */
+ int maxnakloops; /* Maximum number of nak loops tolerated */
+ struct fsm_callbacks *callbacks; /* Callback routines */
+ char *term_reason; /* Reason for closing protocol */
+ int term_reason_len; /* Length of term_reason */
+} fsm;
+
+
+typedef struct fsm_callbacks {
+ void (*resetci) /* Reset our Configuration Information */
+ __P((fsm *));
+ int (*cilen) /* Length of our Configuration Information */
+ __P((fsm *));
+ void (*addci) /* Add our Configuration Information */
+ __P((fsm *, u_char *, int *));
+ int (*ackci) /* ACK our Configuration Information */
+ __P((fsm *, u_char *, int));
+ int (*nakci) /* NAK our Configuration Information */
+ __P((fsm *, u_char *, int));
+ int (*rejci) /* Reject our Configuration Information */
+ __P((fsm *, u_char *, int));
+ int (*reqci) /* Request peer's Configuration Information */
+ __P((fsm *, u_char *, int *, int));
+ void (*up) /* Called when fsm reaches OPENED state */
+ __P((fsm *));
+ void (*down) /* Called when fsm leaves OPENED state */
+ __P((fsm *));
+ void (*starting) /* Called when we want the lower layer */
+ __P((fsm *));
+ void (*finished) /* Called when we don't want the lower layer */
+ __P((fsm *));
+ void (*protreject) /* Called when Protocol-Reject received */
+ __P((int));
+ void (*retransmit) /* Retransmission is necessary */
+ __P((fsm *));
+ int (*extcode) /* Called when unknown code received */
+ __P((fsm *, int, int, u_char *, int));
+ char *proto_name; /* String name for protocol (for messages) */
+} fsm_callbacks;
+
+
+/*
+ * Link states.
+ */
+#define INITIAL 0 /* Down, hasn't been opened */
+#define STARTING 1 /* Down, been opened */
+#define CLOSED 2 /* Up, hasn't been opened */
+#define STOPPED 3 /* Open, waiting for down event */
+#define CLOSING 4 /* Terminating the connection, not open */
+#define STOPPING 5 /* Terminating, but open */
+#define REQSENT 6 /* We've sent a Config Request */
+#define ACKRCVD 7 /* We've received a Config Ack */
+#define ACKSENT 8 /* We've sent a Config Ack */
+#define OPENED 9 /* Connection available */
+
+
+/*
+ * Flags - indicate options controlling FSM operation
+ */
+#define OPT_PASSIVE 1 /* Don't die if we don't get a response */
+#define OPT_RESTART 2 /* Treat 2nd OPEN as DOWN, UP */
+#define OPT_SILENT 4 /* Wait for peer to speak first */
+
+
+/*
+ * Timeouts.
+ */
+#define DEFTIMEOUT 3 /* Timeout time in seconds */
+#define DEFMAXTERMREQS 2 /* Maximum Terminate-Request transmissions */
+#define DEFMAXCONFREQS 10 /* Maximum Configure-Request transmissions */
+#define DEFMAXNAKLOOPS 5 /* Maximum number of nak loops */
+
+
+/*
+ * Prototypes
+ */
+void fsm_init __P((fsm *));
+void fsm_lowerup __P((fsm *));
+void fsm_lowerdown __P((fsm *));
+void fsm_open __P((fsm *));
+void fsm_close __P((fsm *, char *));
+void fsm_input __P((fsm *, u_char *, int));
+void fsm_protreject __P((fsm *));
+void fsm_sdata __P((fsm *, int, int, u_char *, int));
+
+
+/*
+ * Variables
+ */
+extern int peer_mru[]; /* currently negotiated peer MRU (per unit) */
diff --git a/usr.sbin/pppd/ipcp.c b/usr.sbin/pppd/ipcp.c
new file mode 100644
index 0000000..576b200
--- /dev/null
+++ b/usr.sbin/pppd/ipcp.c
@@ -0,0 +1,1530 @@
+/*
+ * ipcp.c - PPP IP Control Protocol.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char rcsid[] = "$FreeBSD$";
+#endif
+
+/*
+ * TODO:
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <netdb.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include "pppd.h"
+#include "fsm.h"
+#include "ipcp.h"
+#include "pathnames.h"
+
+/* global vars */
+ipcp_options ipcp_wantoptions[NUM_PPP]; /* Options that we want to request */
+ipcp_options ipcp_gotoptions[NUM_PPP]; /* Options that peer ack'd */
+ipcp_options ipcp_allowoptions[NUM_PPP]; /* Options we allow peer to request */
+ipcp_options ipcp_hisoptions[NUM_PPP]; /* Options that we ack'd */
+
+/* local vars */
+static int cis_received[NUM_PPP]; /* # Conf-Reqs received */
+static int default_route_set[NUM_PPP]; /* Have set up a default route */
+static int proxy_arp_set[NUM_PPP]; /* Have created proxy arp entry */
+
+/*
+ * Callbacks for fsm code. (CI = Configuration Information)
+ */
+static void ipcp_resetci __P((fsm *)); /* Reset our CI */
+static int ipcp_cilen __P((fsm *)); /* Return length of our CI */
+static void ipcp_addci __P((fsm *, u_char *, int *)); /* Add our CI */
+static int ipcp_ackci __P((fsm *, u_char *, int)); /* Peer ack'd our CI */
+static int ipcp_nakci __P((fsm *, u_char *, int)); /* Peer nak'd our CI */
+static int ipcp_rejci __P((fsm *, u_char *, int)); /* Peer rej'd our CI */
+static int ipcp_reqci __P((fsm *, u_char *, int *, int)); /* Rcv CI */
+static void ipcp_up __P((fsm *)); /* We're UP */
+static void ipcp_down __P((fsm *)); /* We're DOWN */
+static void ipcp_script __P((fsm *, char *)); /* Run an up/down script */
+static void ipcp_finished __P((fsm *)); /* Don't need lower layer */
+
+fsm ipcp_fsm[NUM_PPP]; /* IPCP fsm structure */
+
+static fsm_callbacks ipcp_callbacks = { /* IPCP callback routines */
+ ipcp_resetci, /* Reset our Configuration Information */
+ ipcp_cilen, /* Length of our Configuration Information */
+ ipcp_addci, /* Add our Configuration Information */
+ ipcp_ackci, /* ACK our Configuration Information */
+ ipcp_nakci, /* NAK our Configuration Information */
+ ipcp_rejci, /* Reject our Configuration Information */
+ ipcp_reqci, /* Request peer's Configuration Information */
+ ipcp_up, /* Called when fsm reaches OPENED state */
+ ipcp_down, /* Called when fsm leaves OPENED state */
+ NULL, /* Called when we want the lower layer up */
+ ipcp_finished, /* Called when we want the lower layer down */
+ NULL, /* Called when Protocol-Reject received */
+ NULL, /* Retransmission is necessary */
+ NULL, /* Called to handle protocol-specific codes */
+ "IPCP" /* String name of protocol */
+};
+
+/*
+ * Protocol entry points from main code.
+ */
+static void ipcp_init __P((int));
+static void ipcp_open __P((int));
+static void ipcp_close __P((int, char *));
+static void ipcp_lowerup __P((int));
+static void ipcp_lowerdown __P((int));
+static void ipcp_input __P((int, u_char *, int));
+static void ipcp_protrej __P((int));
+static int ipcp_printpkt __P((u_char *, int,
+ void (*) __P((void *, char *, ...)), void *));
+static void ip_check_options __P((void));
+static int ip_demand_conf __P((int));
+static int ip_active_pkt __P((u_char *, int));
+
+struct protent ipcp_protent = {
+ PPP_IPCP,
+ ipcp_init,
+ ipcp_input,
+ ipcp_protrej,
+ ipcp_lowerup,
+ ipcp_lowerdown,
+ ipcp_open,
+ ipcp_close,
+ ipcp_printpkt,
+ NULL,
+ 1,
+ "IPCP",
+ ip_check_options,
+ ip_demand_conf,
+ ip_active_pkt
+};
+
+static void ipcp_clear_addrs __P((int));
+
+/*
+ * Lengths of configuration options.
+ */
+#define CILEN_VOID 2
+#define CILEN_COMPRESS 4 /* min length for compression protocol opt. */
+#define CILEN_VJ 6 /* length for RFC1332 Van-Jacobson opt. */
+#define CILEN_ADDR 6 /* new-style single address option */
+#define CILEN_ADDRS 10 /* old-style dual address option */
+
+
+#define CODENAME(x) ((x) == CONFACK ? "ACK" : \
+ (x) == CONFNAK ? "NAK" : "REJ")
+
+
+/*
+ * Make a string representation of a network IP address.
+ */
+char *
+ip_ntoa(ipaddr)
+u_int32_t ipaddr;
+{
+ static char b[64];
+
+ ipaddr = ntohl(ipaddr);
+
+ sprintf(b, "%d.%d.%d.%d",
+ (u_char)(ipaddr >> 24),
+ (u_char)(ipaddr >> 16),
+ (u_char)(ipaddr >> 8),
+ (u_char)(ipaddr));
+ return b;
+}
+
+
+/*
+ * ipcp_init - Initialize IPCP.
+ */
+static void
+ipcp_init(unit)
+ int unit;
+{
+ fsm *f = &ipcp_fsm[unit];
+ ipcp_options *wo = &ipcp_wantoptions[unit];
+ ipcp_options *ao = &ipcp_allowoptions[unit];
+
+ f->unit = unit;
+ f->protocol = PPP_IPCP;
+ f->callbacks = &ipcp_callbacks;
+ fsm_init(&ipcp_fsm[unit]);
+
+ memset(wo, 0, sizeof(*wo));
+ memset(ao, 0, sizeof(*ao));
+
+ wo->neg_addr = 1;
+ wo->neg_vj = 1;
+ wo->vj_protocol = IPCP_VJ_COMP;
+ wo->maxslotindex = MAX_STATES - 1; /* really max index */
+ wo->cflag = 1;
+
+ /* max slots and slot-id compression are currently hardwired in */
+ /* ppp_if.c to 16 and 1, this needs to be changed (among other */
+ /* things) gmc */
+
+ ao->neg_addr = 1;
+ ao->neg_vj = 1;
+ ao->maxslotindex = MAX_STATES - 1;
+ ao->cflag = 1;
+
+ /*
+ * XXX These control whether the user may use the proxyarp
+ * and defaultroute options.
+ */
+ ao->proxy_arp = 1;
+ ao->default_route = 1;
+}
+
+
+/*
+ * ipcp_open - IPCP is allowed to come up.
+ */
+static void
+ipcp_open(unit)
+ int unit;
+{
+ fsm_open(&ipcp_fsm[unit]);
+}
+
+
+/*
+ * ipcp_close - Take IPCP down.
+ */
+static void
+ipcp_close(unit, reason)
+ int unit;
+ char *reason;
+{
+ fsm_close(&ipcp_fsm[unit], reason);
+}
+
+
+/*
+ * ipcp_lowerup - The lower layer is up.
+ */
+static void
+ipcp_lowerup(unit)
+ int unit;
+{
+ fsm_lowerup(&ipcp_fsm[unit]);
+}
+
+
+/*
+ * ipcp_lowerdown - The lower layer is down.
+ */
+static void
+ipcp_lowerdown(unit)
+ int unit;
+{
+ fsm_lowerdown(&ipcp_fsm[unit]);
+}
+
+
+/*
+ * ipcp_input - Input IPCP packet.
+ */
+static void
+ipcp_input(unit, p, len)
+ int unit;
+ u_char *p;
+ int len;
+{
+ fsm_input(&ipcp_fsm[unit], p, len);
+}
+
+
+/*
+ * ipcp_protrej - A Protocol-Reject was received for IPCP.
+ *
+ * Pretend the lower layer went down, so we shut up.
+ */
+static void
+ipcp_protrej(unit)
+ int unit;
+{
+ fsm_lowerdown(&ipcp_fsm[unit]);
+}
+
+
+/*
+ * ipcp_resetci - Reset our CI.
+ */
+static void
+ipcp_resetci(f)
+ fsm *f;
+{
+ ipcp_options *wo = &ipcp_wantoptions[f->unit];
+
+ wo->req_addr = wo->neg_addr && ipcp_allowoptions[f->unit].neg_addr;
+ if (wo->ouraddr == 0)
+ wo->accept_local = 1;
+ if (wo->hisaddr == 0)
+ wo->accept_remote = 1;
+ ipcp_gotoptions[f->unit] = *wo;
+ cis_received[f->unit] = 0;
+}
+
+
+/*
+ * ipcp_cilen - Return length of our CI.
+ */
+static int
+ipcp_cilen(f)
+ fsm *f;
+{
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ ipcp_options *wo = &ipcp_wantoptions[f->unit];
+ ipcp_options *ho = &ipcp_hisoptions[f->unit];
+
+#define LENCIVJ(neg, old) (neg ? (old? CILEN_COMPRESS : CILEN_VJ) : 0)
+#define LENCIADDR(neg, old) (neg ? (old? CILEN_ADDRS : CILEN_ADDR) : 0)
+
+ /*
+ * First see if we want to change our options to the old
+ * forms because we have received old forms from the peer.
+ */
+ if (wo->neg_addr && !go->neg_addr && !go->old_addrs) {
+ /* use the old style of address negotiation */
+ go->neg_addr = 1;
+ go->old_addrs = 1;
+ }
+ if (wo->neg_vj && !go->neg_vj && !go->old_vj) {
+ /* try an older style of VJ negotiation */
+ if (cis_received[f->unit] == 0) {
+ /* keep trying the new style until we see some CI from the peer */
+ go->neg_vj = 1;
+ } else {
+ /* use the old style only if the peer did */
+ if (ho->neg_vj && ho->old_vj) {
+ go->neg_vj = 1;
+ go->old_vj = 1;
+ go->vj_protocol = ho->vj_protocol;
+ }
+ }
+ }
+
+ return (LENCIADDR(go->neg_addr, go->old_addrs) +
+ LENCIVJ(go->neg_vj, go->old_vj));
+}
+
+
+/*
+ * ipcp_addci - Add our desired CIs to a packet.
+ */
+static void
+ipcp_addci(f, ucp, lenp)
+ fsm *f;
+ u_char *ucp;
+ int *lenp;
+{
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ int len = *lenp;
+
+#define ADDCIVJ(opt, neg, val, old, maxslotindex, cflag) \
+ if (neg) { \
+ int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \
+ if (len >= vjlen) { \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(vjlen, ucp); \
+ PUTSHORT(val, ucp); \
+ if (!old) { \
+ PUTCHAR(maxslotindex, ucp); \
+ PUTCHAR(cflag, ucp); \
+ } \
+ len -= vjlen; \
+ } else \
+ neg = 0; \
+ }
+
+#define ADDCIADDR(opt, neg, old, val1, val2) \
+ if (neg) { \
+ int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \
+ if (len >= addrlen) { \
+ u_int32_t l; \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(addrlen, ucp); \
+ l = ntohl(val1); \
+ PUTLONG(l, ucp); \
+ if (old) { \
+ l = ntohl(val2); \
+ PUTLONG(l, ucp); \
+ } \
+ len -= addrlen; \
+ } else \
+ neg = 0; \
+ }
+
+ ADDCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), go->neg_addr,
+ go->old_addrs, go->ouraddr, go->hisaddr);
+
+ ADDCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj,
+ go->maxslotindex, go->cflag);
+
+ *lenp -= len;
+}
+
+
+/*
+ * ipcp_ackci - Ack our CIs.
+ *
+ * Returns:
+ * 0 - Ack was bad.
+ * 1 - Ack was good.
+ */
+static int
+ipcp_ackci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ u_short cilen, citype, cishort;
+ u_int32_t cilong;
+ u_char cimaxslotindex, cicflag;
+
+ /*
+ * CIs must be in exactly the same order that we sent...
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+
+#define ACKCIVJ(opt, neg, val, old, maxslotindex, cflag) \
+ if (neg) { \
+ int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \
+ if ((len -= vjlen) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != vjlen || \
+ citype != opt) \
+ goto bad; \
+ GETSHORT(cishort, p); \
+ if (cishort != val) \
+ goto bad; \
+ if (!old) { \
+ GETCHAR(cimaxslotindex, p); \
+ if (cimaxslotindex != maxslotindex) \
+ goto bad; \
+ GETCHAR(cicflag, p); \
+ if (cicflag != cflag) \
+ goto bad; \
+ } \
+ }
+
+#define ACKCIADDR(opt, neg, old, val1, val2) \
+ if (neg) { \
+ int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \
+ u_int32_t l; \
+ if ((len -= addrlen) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != addrlen || \
+ citype != opt) \
+ goto bad; \
+ GETLONG(l, p); \
+ cilong = htonl(l); \
+ if (val1 != cilong) \
+ goto bad; \
+ if (old) { \
+ GETLONG(l, p); \
+ cilong = htonl(l); \
+ if (val2 != cilong) \
+ goto bad; \
+ } \
+ }
+
+ ACKCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), go->neg_addr,
+ go->old_addrs, go->ouraddr, go->hisaddr);
+
+ ACKCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj,
+ go->maxslotindex, go->cflag);
+
+ /*
+ * If there are any remaining CIs, then this packet is bad.
+ */
+ if (len != 0)
+ goto bad;
+ return (1);
+
+bad:
+ IPCPDEBUG((LOG_INFO, "ipcp_ackci: received bad Ack!"));
+ return (0);
+}
+
+/*
+ * ipcp_nakci - Peer has sent a NAK for some of our CIs.
+ * This should not modify any state if the Nak is bad
+ * or if IPCP is in the OPENED state.
+ *
+ * Returns:
+ * 0 - Nak was bad.
+ * 1 - Nak was good.
+ */
+static int
+ipcp_nakci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ u_char cimaxslotindex, cicflag;
+ u_char citype, cilen, *next;
+ u_short cishort;
+ u_int32_t ciaddr1, ciaddr2, l;
+ ipcp_options no; /* options we've seen Naks for */
+ ipcp_options try; /* options to request next time */
+
+ BZERO(&no, sizeof(no));
+ try = *go;
+
+ /*
+ * Any Nak'd CIs must be in exactly the same order that we sent.
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+#define NAKCIADDR(opt, neg, old, code) \
+ if (go->neg && \
+ len >= (cilen = (old? CILEN_ADDRS: CILEN_ADDR)) && \
+ p[1] == cilen && \
+ p[0] == opt) { \
+ len -= cilen; \
+ INCPTR(2, p); \
+ GETLONG(l, p); \
+ ciaddr1 = htonl(l); \
+ if (old) { \
+ GETLONG(l, p); \
+ ciaddr2 = htonl(l); \
+ no.old_addrs = 1; \
+ } else \
+ ciaddr2 = 0; \
+ no.neg = 1; \
+ code \
+ }
+
+#define NAKCIVJ(opt, neg, code) \
+ if (go->neg && \
+ ((cilen = p[1]) == CILEN_COMPRESS || cilen == CILEN_VJ) && \
+ len >= cilen && \
+ p[0] == opt) { \
+ len -= cilen; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ no.neg = 1; \
+ code \
+ }
+
+ /*
+ * Accept the peer's idea of {our,his} address, if different
+ * from our idea, only if the accept_{local,remote} flag is set.
+ */
+ NAKCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), neg_addr, go->old_addrs,
+ if (go->accept_local && ciaddr1) { /* Do we know our address? */
+ try.ouraddr = ciaddr1;
+ IPCPDEBUG((LOG_INFO, "local IP address %s",
+ ip_ntoa(ciaddr1)));
+ }
+ if (go->accept_remote && ciaddr2) { /* Does he know his? */
+ try.hisaddr = ciaddr2;
+ IPCPDEBUG((LOG_INFO, "remote IP address %s",
+ ip_ntoa(ciaddr2)));
+ }
+ );
+
+ /*
+ * Accept the peer's value of maxslotindex provided that it
+ * is less than what we asked for. Turn off slot-ID compression
+ * if the peer wants. Send old-style compress-type option if
+ * the peer wants.
+ */
+ NAKCIVJ(CI_COMPRESSTYPE, neg_vj,
+ if (cilen == CILEN_VJ) {
+ GETCHAR(cimaxslotindex, p);
+ GETCHAR(cicflag, p);
+ if (cishort == IPCP_VJ_COMP) {
+ try.old_vj = 0;
+ if (cimaxslotindex < go->maxslotindex)
+ try.maxslotindex = cimaxslotindex;
+ if (!cicflag)
+ try.cflag = 0;
+ } else {
+ try.neg_vj = 0;
+ }
+ } else {
+ if (cishort == IPCP_VJ_COMP || cishort == IPCP_VJ_COMP_OLD) {
+ try.old_vj = 1;
+ try.vj_protocol = cishort;
+ } else {
+ try.neg_vj = 0;
+ }
+ }
+ );
+
+ /*
+ * There may be remaining CIs, if the peer is requesting negotiation
+ * on an option that we didn't include in our request packet.
+ * If they want to negotiate about IP addresses, we comply.
+ * If they want us to ask for compression, we refuse.
+ */
+ while (len > CILEN_VOID) {
+ GETCHAR(citype, p);
+ GETCHAR(cilen, p);
+ if( (len -= cilen) < 0 )
+ goto bad;
+ next = p + cilen - 2;
+
+ switch (citype) {
+ case CI_COMPRESSTYPE:
+ if (go->neg_vj || no.neg_vj ||
+ (cilen != CILEN_VJ && cilen != CILEN_COMPRESS))
+ goto bad;
+ no.neg_vj = 1;
+ break;
+ case CI_ADDRS:
+ if ((go->neg_addr && go->old_addrs) || no.old_addrs
+ || cilen != CILEN_ADDRS)
+ goto bad;
+ try.neg_addr = 1;
+ try.old_addrs = 1;
+ GETLONG(l, p);
+ ciaddr1 = htonl(l);
+ if (ciaddr1 && go->accept_local)
+ try.ouraddr = ciaddr1;
+ GETLONG(l, p);
+ ciaddr2 = htonl(l);
+ if (ciaddr2 && go->accept_remote)
+ try.hisaddr = ciaddr2;
+ no.old_addrs = 1;
+ break;
+ case CI_ADDR:
+ if (go->neg_addr || no.neg_addr || cilen != CILEN_ADDR)
+ goto bad;
+ try.old_addrs = 0;
+ GETLONG(l, p);
+ ciaddr1 = htonl(l);
+ if (ciaddr1 && go->accept_local)
+ try.ouraddr = ciaddr1;
+ if (try.ouraddr != 0)
+ try.neg_addr = 1;
+ no.neg_addr = 1;
+ break;
+ }
+ p = next;
+ }
+
+ /* If there is still anything left, this packet is bad. */
+ if (len != 0)
+ goto bad;
+
+ /*
+ * OK, the Nak is good. Now we can update state.
+ */
+ if (f->state != OPENED)
+ *go = try;
+
+ return 1;
+
+bad:
+ IPCPDEBUG((LOG_INFO, "ipcp_nakci: received bad Nak!"));
+ return 0;
+}
+
+
+/*
+ * ipcp_rejci - Reject some of our CIs.
+ */
+static int
+ipcp_rejci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ u_char cimaxslotindex, ciflag, cilen;
+ u_short cishort;
+ u_int32_t cilong;
+ ipcp_options try; /* options to request next time */
+
+ try = *go;
+ /*
+ * Any Rejected CIs must be in exactly the same order that we sent.
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+#define REJCIADDR(opt, neg, old, val1, val2) \
+ if (go->neg && \
+ len >= (cilen = old? CILEN_ADDRS: CILEN_ADDR) && \
+ p[1] == cilen && \
+ p[0] == opt) { \
+ u_int32_t l; \
+ len -= cilen; \
+ INCPTR(2, p); \
+ GETLONG(l, p); \
+ cilong = htonl(l); \
+ /* Check rejected value. */ \
+ if (cilong != val1) \
+ goto bad; \
+ if (old) { \
+ GETLONG(l, p); \
+ cilong = htonl(l); \
+ /* Check rejected value. */ \
+ if (cilong != val2) \
+ goto bad; \
+ } \
+ try.neg = 0; \
+ }
+
+#define REJCIVJ(opt, neg, val, old, maxslot, cflag) \
+ if (go->neg && \
+ p[1] == (old? CILEN_COMPRESS : CILEN_VJ) && \
+ len >= p[1] && \
+ p[0] == opt) { \
+ len -= p[1]; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ /* Check rejected value. */ \
+ if (cishort != val) \
+ goto bad; \
+ if (!old) { \
+ GETCHAR(cimaxslotindex, p); \
+ if (cimaxslotindex != maxslot) \
+ goto bad; \
+ GETCHAR(ciflag, p); \
+ if (ciflag != cflag) \
+ goto bad; \
+ } \
+ try.neg = 0; \
+ }
+
+ REJCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), neg_addr,
+ go->old_addrs, go->ouraddr, go->hisaddr);
+
+ REJCIVJ(CI_COMPRESSTYPE, neg_vj, go->vj_protocol, go->old_vj,
+ go->maxslotindex, go->cflag);
+
+ /*
+ * If there are any remaining CIs, then this packet is bad.
+ */
+ if (len != 0)
+ goto bad;
+ /*
+ * Now we can update state.
+ */
+ if (f->state != OPENED)
+ *go = try;
+ return 1;
+
+bad:
+ IPCPDEBUG((LOG_INFO, "ipcp_rejci: received bad Reject!"));
+ return 0;
+}
+
+
+/*
+ * ipcp_reqci - Check the peer's requested CIs and send appropriate response.
+ *
+ * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified
+ * appropriately. If reject_if_disagree is non-zero, doesn't return
+ * CONFNAK; returns CONFREJ if it can't return CONFACK.
+ */
+static int
+ipcp_reqci(f, inp, len, reject_if_disagree)
+ fsm *f;
+ u_char *inp; /* Requested CIs */
+ int *len; /* Length of requested CIs */
+ int reject_if_disagree;
+{
+ ipcp_options *wo = &ipcp_wantoptions[f->unit];
+ ipcp_options *ho = &ipcp_hisoptions[f->unit];
+ ipcp_options *ao = &ipcp_allowoptions[f->unit];
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ u_char *cip, *next; /* Pointer to current and next CIs */
+ u_short cilen, citype; /* Parsed len, type */
+ u_short cishort; /* Parsed short value */
+ u_int32_t tl, ciaddr1, ciaddr2;/* Parsed address values */
+ int rc = CONFACK; /* Final packet return code */
+ int orc; /* Individual option return code */
+ u_char *p; /* Pointer to next char to parse */
+ u_char *ucp = inp; /* Pointer to current output char */
+ int l = *len; /* Length left */
+ u_char maxslotindex, cflag;
+ int d;
+
+ cis_received[f->unit] = 1;
+
+ /*
+ * Reset all his options.
+ */
+ BZERO(ho, sizeof(*ho));
+
+ /*
+ * Process all his options.
+ */
+ next = inp;
+ while (l) {
+ orc = CONFACK; /* Assume success */
+ cip = p = next; /* Remember begining of CI */
+ if (l < 2 || /* Not enough data for CI header or */
+ p[1] < 2 || /* CI length too small or */
+ p[1] > l) { /* CI length too big? */
+ IPCPDEBUG((LOG_INFO, "ipcp_reqci: bad CI length!"));
+ orc = CONFREJ; /* Reject bad CI */
+ cilen = l; /* Reject till end of packet */
+ l = 0; /* Don't loop again */
+ goto endswitch;
+ }
+ GETCHAR(citype, p); /* Parse CI type */
+ GETCHAR(cilen, p); /* Parse CI length */
+ l -= cilen; /* Adjust remaining length */
+ next += cilen; /* Step to next CI */
+
+ switch (citype) { /* Check CI type */
+ case CI_ADDRS:
+ IPCPDEBUG((LOG_INFO, "ipcp: received ADDRS "));
+ if (!ao->neg_addr ||
+ cilen != CILEN_ADDRS) { /* Check CI length */
+ orc = CONFREJ; /* Reject CI */
+ break;
+ }
+
+ /*
+ * If he has no address, or if we both have his address but
+ * disagree about it, then NAK it with our idea.
+ * In particular, if we don't know his address, but he does,
+ * then accept it.
+ */
+ GETLONG(tl, p); /* Parse source address (his) */
+ ciaddr1 = htonl(tl);
+ IPCPDEBUG((LOG_INFO, "(%s:", ip_ntoa(ciaddr1)));
+ if (ciaddr1 != wo->hisaddr
+ && (ciaddr1 == 0 || !wo->accept_remote)) {
+ orc = CONFNAK;
+ if (!reject_if_disagree) {
+ DECPTR(sizeof(u_int32_t), p);
+ tl = ntohl(wo->hisaddr);
+ PUTLONG(tl, p);
+ }
+ } else if (ciaddr1 == 0 && wo->hisaddr == 0) {
+ /*
+ * If neither we nor he knows his address, reject the option.
+ */
+ orc = CONFREJ;
+ wo->req_addr = 0; /* don't NAK with 0.0.0.0 later */
+ break;
+ }
+
+ /*
+ * If he doesn't know our address, or if we both have our address
+ * but disagree about it, then NAK it with our idea.
+ */
+ GETLONG(tl, p); /* Parse desination address (ours) */
+ ciaddr2 = htonl(tl);
+ IPCPDEBUG((LOG_INFO, "%s)", ip_ntoa(ciaddr2)));
+ if (ciaddr2 != wo->ouraddr) {
+ if (ciaddr2 == 0 || !wo->accept_local) {
+ orc = CONFNAK;
+ if (!reject_if_disagree) {
+ DECPTR(sizeof(u_int32_t), p);
+ tl = ntohl(wo->ouraddr);
+ PUTLONG(tl, p);
+ }
+ } else {
+ go->ouraddr = ciaddr2; /* accept peer's idea */
+ }
+ }
+
+ ho->neg_addr = 1;
+ ho->old_addrs = 1;
+ ho->hisaddr = ciaddr1;
+ ho->ouraddr = ciaddr2;
+ break;
+
+ case CI_ADDR:
+ IPCPDEBUG((LOG_INFO, "ipcp: received ADDR "));
+
+ if (!ao->neg_addr ||
+ cilen != CILEN_ADDR) { /* Check CI length */
+ orc = CONFREJ; /* Reject CI */
+ break;
+ }
+
+ /*
+ * If he has no address, or if we both have his address but
+ * disagree about it, then NAK it with our idea.
+ * In particular, if we don't know his address, but he does,
+ * then accept it.
+ */
+ GETLONG(tl, p); /* Parse source address (his) */
+ ciaddr1 = htonl(tl);
+ IPCPDEBUG((LOG_INFO, "(%s)", ip_ntoa(ciaddr1)));
+ if (ciaddr1 != wo->hisaddr
+ && (ciaddr1 == 0 || !wo->accept_remote)) {
+ orc = CONFNAK;
+ if (!reject_if_disagree) {
+ DECPTR(sizeof(u_int32_t), p);
+ tl = ntohl(wo->hisaddr);
+ PUTLONG(tl, p);
+ }
+ } else if (ciaddr1 == 0 && wo->hisaddr == 0) {
+ /*
+ * Don't ACK an address of 0.0.0.0 - reject it instead.
+ */
+ orc = CONFREJ;
+ wo->req_addr = 0; /* don't NAK with 0.0.0.0 later */
+ break;
+ }
+
+ ho->neg_addr = 1;
+ ho->hisaddr = ciaddr1;
+ break;
+
+ case CI_MS_DNS1:
+ case CI_MS_DNS2:
+ /* Microsoft primary or secondary DNS request */
+ d = citype == CI_MS_DNS2;
+ IPCPDEBUG((LOG_INFO, "ipcp: received DNS%d Request ", d+1));
+
+ /* If we do not have a DNS address then we cannot send it */
+ if (ao->dnsaddr[d] == 0 ||
+ cilen != CILEN_ADDR) { /* Check CI length */
+ orc = CONFREJ; /* Reject CI */
+ break;
+ }
+ GETLONG(tl, p);
+ if (htonl(tl) != ao->dnsaddr[d]) {
+ DECPTR(sizeof(u_int32_t), p);
+ tl = ntohl(ao->dnsaddr[d]);
+ PUTLONG(tl, p);
+ orc = CONFNAK;
+ }
+ break;
+
+ case CI_MS_WINS1:
+ case CI_MS_WINS2:
+ /* Microsoft primary or secondary WINS request */
+ d = citype == CI_MS_WINS2;
+ IPCPDEBUG((LOG_INFO, "ipcp: received WINS%d Request ", d+1));
+
+ /* If we do not have a DNS address then we cannot send it */
+ if (ao->winsaddr[d] == 0 ||
+ cilen != CILEN_ADDR) { /* Check CI length */
+ orc = CONFREJ; /* Reject CI */
+ break;
+ }
+ GETLONG(tl, p);
+ if (htonl(tl) != ao->winsaddr[d]) {
+ DECPTR(sizeof(u_int32_t), p);
+ tl = ntohl(ao->winsaddr[d]);
+ PUTLONG(tl, p);
+ orc = CONFNAK;
+ }
+ break;
+
+ case CI_COMPRESSTYPE:
+ IPCPDEBUG((LOG_INFO, "ipcp: received COMPRESSTYPE "));
+ if (!ao->neg_vj ||
+ (cilen != CILEN_VJ && cilen != CILEN_COMPRESS)) {
+ orc = CONFREJ;
+ break;
+ }
+ GETSHORT(cishort, p);
+ IPCPDEBUG((LOG_INFO, "(%d)", cishort));
+
+ if (!(cishort == IPCP_VJ_COMP ||
+ (cishort == IPCP_VJ_COMP_OLD && cilen == CILEN_COMPRESS))) {
+ orc = CONFREJ;
+ break;
+ }
+
+ ho->neg_vj = 1;
+ ho->vj_protocol = cishort;
+ if (cilen == CILEN_VJ) {
+ GETCHAR(maxslotindex, p);
+ if (maxslotindex > ao->maxslotindex) {
+ orc = CONFNAK;
+ if (!reject_if_disagree){
+ DECPTR(1, p);
+ PUTCHAR(ao->maxslotindex, p);
+ }
+ }
+ GETCHAR(cflag, p);
+ if (cflag && !ao->cflag) {
+ orc = CONFNAK;
+ if (!reject_if_disagree){
+ DECPTR(1, p);
+ PUTCHAR(wo->cflag, p);
+ }
+ }
+ ho->maxslotindex = maxslotindex;
+ ho->cflag = cflag;
+ } else {
+ ho->old_vj = 1;
+ ho->maxslotindex = MAX_STATES - 1;
+ ho->cflag = 1;
+ }
+ break;
+
+ default:
+ orc = CONFREJ;
+ break;
+ }
+
+endswitch:
+ IPCPDEBUG((LOG_INFO, " (%s)\n", CODENAME(orc)));
+
+ if (orc == CONFACK && /* Good CI */
+ rc != CONFACK) /* but prior CI wasnt? */
+ continue; /* Don't send this one */
+
+ if (orc == CONFNAK) { /* Nak this CI? */
+ if (reject_if_disagree) /* Getting fed up with sending NAKs? */
+ orc = CONFREJ; /* Get tough if so */
+ else {
+ if (rc == CONFREJ) /* Rejecting prior CI? */
+ continue; /* Don't send this one */
+ if (rc == CONFACK) { /* Ack'd all prior CIs? */
+ rc = CONFNAK; /* Not anymore... */
+ ucp = inp; /* Backup */
+ }
+ }
+ }
+
+ if (orc == CONFREJ && /* Reject this CI */
+ rc != CONFREJ) { /* but no prior ones? */
+ rc = CONFREJ;
+ ucp = inp; /* Backup */
+ }
+
+ /* Need to move CI? */
+ if (ucp != cip)
+ BCOPY(cip, ucp, cilen); /* Move it */
+
+ /* Update output pointer */
+ INCPTR(cilen, ucp);
+ }
+
+ /*
+ * If we aren't rejecting this packet, and we want to negotiate
+ * their address, and they didn't send their address, then we
+ * send a NAK with a CI_ADDR option appended. We assume the
+ * input buffer is long enough that we can append the extra
+ * option safely.
+ */
+ if (rc != CONFREJ && !ho->neg_addr &&
+ wo->req_addr && !reject_if_disagree) {
+ if (rc == CONFACK) {
+ rc = CONFNAK;
+ ucp = inp; /* reset pointer */
+ wo->req_addr = 0; /* don't ask again */
+ }
+ PUTCHAR(CI_ADDR, ucp);
+ PUTCHAR(CILEN_ADDR, ucp);
+ tl = ntohl(wo->hisaddr);
+ PUTLONG(tl, ucp);
+ }
+
+ *len = ucp - inp; /* Compute output length */
+ IPCPDEBUG((LOG_INFO, "ipcp: returning Configure-%s", CODENAME(rc)));
+ return (rc); /* Return final code */
+}
+
+
+/*
+ * ip_check_options - check that any IP-related options are OK,
+ * and assign appropriate defaults.
+ */
+static void
+ip_check_options()
+{
+ struct hostent *hp;
+ u_int32_t local;
+ ipcp_options *wo = &ipcp_wantoptions[0];
+
+ /*
+ * Default our local IP address based on our hostname.
+ * If local IP address already given, don't bother.
+ */
+ if (wo->ouraddr == 0 && !disable_defaultip) {
+ /*
+ * Look up our hostname (possibly with domain name appended)
+ * and take the first IP address as our local IP address.
+ * If there isn't an IP address for our hostname, too bad.
+ */
+ wo->accept_local = 1; /* don't insist on this default value */
+ if ((hp = gethostbyname(hostname)) != NULL) {
+ local = *(u_int32_t *)hp->h_addr;
+ if (local != 0 && !bad_ip_adrs(local))
+ wo->ouraddr = local;
+ }
+ }
+
+ if (demand && wo->hisaddr == 0) {
+ option_error("remote IP address required for demand-dialling\n");
+ exit(1);
+ }
+#if 0
+ if (demand && wo->accept_remote) {
+ option_error("ipcp-accept-remote is incompatible with demand\n");
+ exit(1);
+ }
+#endif
+}
+
+
+/*
+ * ip_demand_conf - configure the interface as though
+ * IPCP were up, for use with dial-on-demand.
+ */
+static int
+ip_demand_conf(u)
+ int u;
+{
+ ipcp_options *wo = &ipcp_wantoptions[u];
+
+ if (!sifaddr(u, wo->ouraddr, wo->hisaddr, GetMask(wo->ouraddr)))
+ return 0;
+ if (!sifup(u))
+ return 0;
+ if (!sifnpmode(u, PPP_IP, NPMODE_QUEUE))
+ return 0;
+ if (wo->default_route)
+ if (sifdefaultroute(u, wo->ouraddr, wo->hisaddr))
+ default_route_set[u] = 1;
+ if (wo->proxy_arp)
+ if (sifproxyarp(u, wo->hisaddr))
+ proxy_arp_set[u] = 1;
+
+ syslog(LOG_NOTICE, "local IP address %s", ip_ntoa(wo->ouraddr));
+ syslog(LOG_NOTICE, "remote IP address %s", ip_ntoa(wo->hisaddr));
+
+ return 1;
+}
+
+
+/*
+ * ipcp_up - IPCP has come UP.
+ *
+ * Configure the IP network interface appropriately and bring it up.
+ */
+static void
+ipcp_up(f)
+ fsm *f;
+{
+ u_int32_t mask;
+ ipcp_options *ho = &ipcp_hisoptions[f->unit];
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ ipcp_options *wo = &ipcp_wantoptions[f->unit];
+
+ np_up(f->unit, PPP_IP);
+ IPCPDEBUG((LOG_INFO, "ipcp: up"));
+
+ /*
+ * We must have a non-zero IP address for both ends of the link.
+ */
+ if (!ho->neg_addr)
+ ho->hisaddr = wo->hisaddr;
+
+ if (ho->hisaddr == 0) {
+ syslog(LOG_ERR, "Could not determine remote IP address");
+ ipcp_close(f->unit, "Could not determine remote IP address");
+ return;
+ }
+ if (go->ouraddr == 0) {
+ syslog(LOG_ERR, "Could not determine local IP address");
+ ipcp_close(f->unit, "Could not determine local IP address");
+ return;
+ }
+ script_setenv("IPLOCAL", ip_ntoa(go->ouraddr));
+ script_setenv("IPREMOTE", ip_ntoa(ho->hisaddr));
+
+ /*
+ * Check that the peer is allowed to use the IP address it wants.
+ */
+ if (!auth_ip_addr(f->unit, ho->hisaddr)) {
+ syslog(LOG_ERR, "Peer is not authorized to use remote address %s",
+ ip_ntoa(ho->hisaddr));
+ ipcp_close(f->unit, "Unauthorized remote IP address");
+ return;
+ }
+
+ /* set tcp compression */
+ sifvjcomp(f->unit, ho->neg_vj, ho->cflag, ho->maxslotindex);
+
+ /*
+ * If we are doing dial-on-demand, the interface is already
+ * configured, so we put out any saved-up packets, then set the
+ * interface to pass IP packets.
+ */
+ if (demand) {
+ if (go->ouraddr != wo->ouraddr || ho->hisaddr != wo->hisaddr) {
+ if (go->ouraddr != wo->ouraddr)
+ syslog(LOG_WARNING, "Local IP address changed to %s",
+ ip_ntoa(go->ouraddr));
+ if (ho->hisaddr != wo->hisaddr)
+ syslog(LOG_WARNING, "Remote IP address changed to %s",
+ ip_ntoa(ho->hisaddr));
+ ipcp_clear_addrs(f->unit);
+
+ /* Set the interface to the new addresses */
+ mask = GetMask(go->ouraddr);
+ if (!sifaddr(f->unit, go->ouraddr, ho->hisaddr, mask)) {
+ IPCPDEBUG((LOG_WARNING, "sifaddr failed"));
+ ipcp_close(f->unit, "Interface configuration failed");
+ return;
+ }
+
+ /* assign a default route through the interface if required */
+ if (ipcp_wantoptions[f->unit].default_route)
+ if (sifdefaultroute(f->unit, go->ouraddr, ho->hisaddr))
+ default_route_set[f->unit] = 1;
+
+ /* Make a proxy ARP entry if requested. */
+ if (ipcp_wantoptions[f->unit].proxy_arp)
+ if (sifproxyarp(f->unit, ho->hisaddr))
+ proxy_arp_set[f->unit] = 1;
+
+ }
+ demand_rexmit(PPP_IP);
+ sifnpmode(f->unit, PPP_IP, NPMODE_PASS);
+
+ } else {
+ /*
+ * Set IP addresses and (if specified) netmask.
+ */
+ mask = GetMask(go->ouraddr);
+
+#if !(defined(SVR4) && (defined(SNI) || defined(__USLC__)))
+ if (!sifaddr(f->unit, go->ouraddr, ho->hisaddr, mask)) {
+ IPCPDEBUG((LOG_WARNING, "sifaddr failed"));
+ ipcp_close(f->unit, "Interface configuration failed");
+ return;
+ }
+#endif
+
+ /* bring the interface up for IP */
+ if (!sifup(f->unit)) {
+ IPCPDEBUG((LOG_WARNING, "sifup failed"));
+ ipcp_close(f->unit, "Interface configuration failed");
+ return;
+ }
+
+#if (defined(SVR4) && (defined(SNI) || defined(__USLC__)))
+ if (!sifaddr(f->unit, go->ouraddr, ho->hisaddr, mask)) {
+ IPCPDEBUG((LOG_WARNING, "sifaddr failed"));
+ ipcp_close(f->unit, "Interface configuration failed");
+ return;
+ }
+#endif
+ sifnpmode(f->unit, PPP_IP, NPMODE_PASS);
+
+ /* assign a default route through the interface if required */
+ if (ipcp_wantoptions[f->unit].default_route)
+ if (sifdefaultroute(f->unit, go->ouraddr, ho->hisaddr))
+ default_route_set[f->unit] = 1;
+
+ /* Make a proxy ARP entry if requested. */
+ if (ipcp_wantoptions[f->unit].proxy_arp)
+ if (sifproxyarp(f->unit, ho->hisaddr))
+ proxy_arp_set[f->unit] = 1;
+
+ syslog(LOG_NOTICE, "local IP address %s", ip_ntoa(go->ouraddr));
+ syslog(LOG_NOTICE, "remote IP address %s", ip_ntoa(ho->hisaddr));
+ }
+
+ /*
+ * Execute the ip-up script, like this:
+ * /etc/ppp/ip-up interface tty speed local-IP remote-IP
+ */
+ ipcp_script(f, _PATH_IPUP);
+
+}
+
+
+/*
+ * ipcp_down - IPCP has gone DOWN.
+ *
+ * Take the IP network interface down, clear its addresses
+ * and delete routes through it.
+ */
+static void
+ipcp_down(f)
+ fsm *f;
+{
+ IPCPDEBUG((LOG_INFO, "ipcp: down"));
+ np_down(f->unit, PPP_IP);
+ sifvjcomp(f->unit, 0, 0, 0);
+
+ /*
+ * If we are doing dial-on-demand, set the interface
+ * to queue up outgoing packets (for now).
+ */
+ if (demand) {
+ sifnpmode(f->unit, PPP_IP, NPMODE_QUEUE);
+ } else {
+ sifdown(f->unit);
+ ipcp_clear_addrs(f->unit);
+ }
+
+ /* Execute the ip-down script */
+ ipcp_script(f, _PATH_IPDOWN);
+}
+
+
+/*
+ * ipcp_clear_addrs() - clear the interface addresses, routes,
+ * proxy arp entries, etc.
+ */
+static void
+ipcp_clear_addrs(unit)
+ int unit;
+{
+ u_int32_t ouraddr, hisaddr;
+
+ ouraddr = ipcp_gotoptions[unit].ouraddr;
+ hisaddr = ipcp_hisoptions[unit].hisaddr;
+ if (proxy_arp_set[unit]) {
+ cifproxyarp(unit, hisaddr);
+ proxy_arp_set[unit] = 0;
+ }
+ if (default_route_set[unit]) {
+ cifdefaultroute(unit, ouraddr, hisaddr);
+ default_route_set[unit] = 0;
+ }
+ cifaddr(unit, ouraddr, hisaddr);
+}
+
+
+/*
+ * ipcp_finished - possibly shut down the lower layers.
+ */
+static void
+ipcp_finished(f)
+ fsm *f;
+{
+ np_finished(f->unit, PPP_IP);
+}
+
+
+/*
+ * ipcp_script - Execute a script with arguments
+ * interface-name tty-name speed local-IP remote-IP.
+ */
+static void
+ipcp_script(f, script)
+ fsm *f;
+ char *script;
+{
+ char strspeed[32], strlocal[32], strremote[32];
+ char *argv[8];
+
+ sprintf(strspeed, "%d", baud_rate);
+ strcpy(strlocal, ip_ntoa(ipcp_gotoptions[f->unit].ouraddr));
+ strcpy(strremote, ip_ntoa(ipcp_hisoptions[f->unit].hisaddr));
+
+ argv[0] = script;
+ argv[1] = ifname;
+ argv[2] = devnam;
+ argv[3] = strspeed;
+ argv[4] = strlocal;
+ argv[5] = strremote;
+ argv[6] = ipparam;
+ argv[7] = NULL;
+ run_program(script, argv, 0);
+}
+
+/*
+ * ipcp_printpkt - print the contents of an IPCP packet.
+ */
+static char *ipcp_codenames[] = {
+ "ConfReq", "ConfAck", "ConfNak", "ConfRej",
+ "TermReq", "TermAck", "CodeRej"
+};
+
+static int
+ipcp_printpkt(p, plen, printer, arg)
+ u_char *p;
+ int plen;
+ void (*printer) __P((void *, char *, ...));
+ void *arg;
+{
+ int code, id, len, olen;
+ u_char *pstart, *optend;
+ u_short cishort;
+ u_int32_t cilong;
+
+ if (plen < HEADERLEN)
+ return 0;
+ pstart = p;
+ GETCHAR(code, p);
+ GETCHAR(id, p);
+ GETSHORT(len, p);
+ if (len < HEADERLEN || len > plen)
+ return 0;
+
+ if (code >= 1 && code <= sizeof(ipcp_codenames) / sizeof(char *))
+ printer(arg, " %s", ipcp_codenames[code-1]);
+ else
+ printer(arg, " code=0x%x", code);
+ printer(arg, " id=0x%x", id);
+ len -= HEADERLEN;
+ switch (code) {
+ case CONFREQ:
+ case CONFACK:
+ case CONFNAK:
+ case CONFREJ:
+ /* print option list */
+ while (len >= 2) {
+ GETCHAR(code, p);
+ GETCHAR(olen, p);
+ p -= 2;
+ if (olen < 2 || olen > len) {
+ break;
+ }
+ printer(arg, " <");
+ len -= olen;
+ optend = p + olen;
+ switch (code) {
+ case CI_ADDRS:
+ if (olen == CILEN_ADDRS) {
+ p += 2;
+ GETLONG(cilong, p);
+ printer(arg, "addrs %I", htonl(cilong));
+ GETLONG(cilong, p);
+ printer(arg, " %I", htonl(cilong));
+ }
+ break;
+ case CI_COMPRESSTYPE:
+ if (olen >= CILEN_COMPRESS) {
+ p += 2;
+ GETSHORT(cishort, p);
+ printer(arg, "compress ");
+ switch (cishort) {
+ case IPCP_VJ_COMP:
+ printer(arg, "VJ");
+ break;
+ case IPCP_VJ_COMP_OLD:
+ printer(arg, "old-VJ");
+ break;
+ default:
+ printer(arg, "0x%x", cishort);
+ }
+ }
+ break;
+ case CI_ADDR:
+ if (olen == CILEN_ADDR) {
+ p += 2;
+ GETLONG(cilong, p);
+ printer(arg, "addr %I", htonl(cilong));
+ }
+ break;
+ case CI_MS_DNS1:
+ case CI_MS_DNS2:
+ p += 2;
+ GETLONG(cilong, p);
+ printer(arg, "ms-dns %I", htonl(cilong));
+ break;
+ case CI_MS_WINS1:
+ case CI_MS_WINS2:
+ p += 2;
+ GETLONG(cilong, p);
+ printer(arg, "ms-wins %I", htonl(cilong));
+ break;
+ }
+ while (p < optend) {
+ GETCHAR(code, p);
+ printer(arg, " %.2x", code);
+ }
+ printer(arg, ">");
+ }
+ break;
+
+ case TERMACK:
+ case TERMREQ:
+ if (len > 0 && *p >= ' ' && *p < 0x7f) {
+ printer(arg, " ");
+ print_string(p, len, printer, arg);
+ p += len;
+ len = 0;
+ }
+ break;
+ }
+
+ /* print the rest of the bytes in the packet */
+ for (; len > 0; --len) {
+ GETCHAR(code, p);
+ printer(arg, " %.2x", code);
+ }
+
+ return p - pstart;
+}
+
+/*
+ * ip_active_pkt - see if this IP packet is worth bringing the link up for.
+ * We don't bring the link up for IP fragments or for TCP FIN packets
+ * with no data.
+ */
+#define IP_HDRLEN 20 /* bytes */
+#define IP_OFFMASK 0x1fff
+#define IPPROTO_TCP 6
+#define TCP_HDRLEN 20
+#define TH_FIN 0x01
+
+/*
+ * We use these macros because the IP header may be at an odd address,
+ * and some compilers might use word loads to get th_off or ip_hl.
+ */
+
+#define net_short(x) (((x)[0] << 8) + (x)[1])
+#define get_iphl(x) (((unsigned char *)(x))[0] & 0xF)
+#define get_ipoff(x) net_short((unsigned char *)(x) + 6)
+#define get_ipproto(x) (((unsigned char *)(x))[9])
+#define get_tcpoff(x) (((unsigned char *)(x))[12] >> 4)
+#define get_tcpflags(x) (((unsigned char *)(x))[13])
+
+static int
+ip_active_pkt(pkt, len)
+ u_char *pkt;
+ int len;
+{
+ u_char *tcp;
+ int hlen;
+
+ len -= PPP_HDRLEN;
+ pkt += PPP_HDRLEN;
+ if (len < IP_HDRLEN)
+ return 0;
+ if ((get_ipoff(pkt) & IP_OFFMASK) != 0)
+ return 0;
+ if (get_ipproto(pkt) != IPPROTO_TCP)
+ return 1;
+ hlen = get_iphl(pkt) * 4;
+ if (len < hlen + TCP_HDRLEN)
+ return 0;
+ tcp = pkt + hlen;
+ if ((get_tcpflags(tcp) & TH_FIN) != 0 && len == hlen + get_tcpoff(tcp) * 4)
+ return 0;
+ return 1;
+}
diff --git a/usr.sbin/pppd/ipcp.h b/usr.sbin/pppd/ipcp.h
new file mode 100644
index 0000000..2bc795d
--- /dev/null
+++ b/usr.sbin/pppd/ipcp.h
@@ -0,0 +1,70 @@
+/*
+ * ipcp.h - IP Control Protocol definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Options.
+ */
+#define CI_ADDRS 1 /* IP Addresses */
+#define CI_COMPRESSTYPE 2 /* Compression Type */
+#define CI_ADDR 3
+
+#define CI_MS_DNS1 129 /* Primary DNS value */
+#define CI_MS_WINS1 130 /* Primary WINS value */
+#define CI_MS_DNS2 131 /* Secondary DNS value */
+#define CI_MS_WINS2 132 /* Secondary WINS value */
+
+#define MAX_STATES 16 /* from slcompress.h */
+
+#define IPCP_VJMODE_OLD 1 /* "old" mode (option # = 0x0037) */
+#define IPCP_VJMODE_RFC1172 2 /* "old-rfc"mode (option # = 0x002d) */
+#define IPCP_VJMODE_RFC1332 3 /* "new-rfc"mode (option # = 0x002d, */
+ /* maxslot and slot number compression) */
+
+#define IPCP_VJ_COMP 0x002d /* current value for VJ compression option*/
+#define IPCP_VJ_COMP_OLD 0x0037 /* "old" (i.e, broken) value for VJ */
+ /* compression option*/
+
+typedef struct ipcp_options {
+ int neg_addr : 1; /* Negotiate IP Address? */
+ int old_addrs : 1; /* Use old (IP-Addresses) option? */
+ int req_addr : 1; /* Ask peer to send IP address? */
+ int default_route : 1; /* Assign default route through interface? */
+ int proxy_arp : 1; /* Make proxy ARP entry for peer? */
+ int neg_vj : 1; /* Van Jacobson Compression? */
+ int old_vj : 1; /* use old (short) form of VJ option? */
+ int accept_local : 1; /* accept peer's value for ouraddr */
+ int accept_remote : 1; /* accept peer's value for hisaddr */
+ u_short vj_protocol; /* protocol value to use in VJ option */
+ u_char maxslotindex, cflag; /* values for RFC1332 VJ compression neg. */
+ u_int32_t ouraddr, hisaddr; /* Addresses in NETWORK BYTE ORDER */
+ u_int32_t dnsaddr[2]; /* Primary and secondary MS DNS entries */
+ u_int32_t winsaddr[2]; /* Primary and secondary MS WINS entries */
+} ipcp_options;
+
+extern fsm ipcp_fsm[];
+extern ipcp_options ipcp_wantoptions[];
+extern ipcp_options ipcp_gotoptions[];
+extern ipcp_options ipcp_allowoptions[];
+extern ipcp_options ipcp_hisoptions[];
+
+char *ip_ntoa __P((u_int32_t));
+
+extern struct protent ipcp_protent;
diff --git a/usr.sbin/pppd/ipxcp.c b/usr.sbin/pppd/ipxcp.c
new file mode 100644
index 0000000..d035709
--- /dev/null
+++ b/usr.sbin/pppd/ipxcp.c
@@ -0,0 +1,1399 @@
+/*
+ * ipxcp.c - PPP IPX Control Protocol.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifdef IPX_CHANGE
+#ifndef lint
+static char rcsid[] = "$FreeBSD$";
+#endif
+
+/*
+ * TODO:
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include "pppd.h"
+#include "fsm.h"
+#include "ipxcp.h"
+#include "pathnames.h"
+
+/* global vars */
+ipxcp_options ipxcp_wantoptions[NUM_PPP]; /* Options that we want to request */
+ipxcp_options ipxcp_gotoptions[NUM_PPP]; /* Options that peer ack'd */
+ipxcp_options ipxcp_allowoptions[NUM_PPP]; /* Options we allow peer to request */
+ipxcp_options ipxcp_hisoptions[NUM_PPP]; /* Options that we ack'd */
+
+#define wo (&ipxcp_wantoptions[0])
+#define ao (&ipxcp_allowoptions[0])
+#define go (&ipxcp_gotoptions[0])
+#define ho (&ipxcp_hisoptions[0])
+
+/*
+ * Callbacks for fsm code. (CI = Configuration Information)
+ */
+static void ipxcp_resetci __P((fsm *)); /* Reset our CI */
+static int ipxcp_cilen __P((fsm *)); /* Return length of our CI */
+static void ipxcp_addci __P((fsm *, u_char *, int *)); /* Add our CI */
+static int ipxcp_ackci __P((fsm *, u_char *, int)); /* Peer ack'd our CI */
+static int ipxcp_nakci __P((fsm *, u_char *, int)); /* Peer nak'd our CI */
+static int ipxcp_rejci __P((fsm *, u_char *, int)); /* Peer rej'd our CI */
+static int ipxcp_reqci __P((fsm *, u_char *, int *, int)); /* Rcv CI */
+static void ipxcp_up __P((fsm *)); /* We're UP */
+static void ipxcp_down __P((fsm *)); /* We're DOWN */
+static void ipxcp_script __P((fsm *, char *)); /* Run an up/down script */
+
+fsm ipxcp_fsm[NUM_PPP]; /* IPXCP fsm structure */
+
+static fsm_callbacks ipxcp_callbacks = { /* IPXCP callback routines */
+ ipxcp_resetci, /* Reset our Configuration Information */
+ ipxcp_cilen, /* Length of our Configuration Information */
+ ipxcp_addci, /* Add our Configuration Information */
+ ipxcp_ackci, /* ACK our Configuration Information */
+ ipxcp_nakci, /* NAK our Configuration Information */
+ ipxcp_rejci, /* Reject our Configuration Information */
+ ipxcp_reqci, /* Request peer's Configuration Information */
+ ipxcp_up, /* Called when fsm reaches OPENED state */
+ ipxcp_down, /* Called when fsm leaves OPENED state */
+ NULL, /* Called when we want the lower layer up */
+ NULL, /* Called when we want the lower layer down */
+ NULL, /* Called when Protocol-Reject received */
+ NULL, /* Retransmission is necessary */
+ NULL, /* Called to handle protocol-specific codes */
+ "IPXCP" /* String name of protocol */
+};
+
+/*
+ * Protocol entry points.
+ */
+
+static void ipxcp_init __P((int));
+static void ipxcp_open __P((int));
+static void ipxcp_close __P((int, char *));
+static void ipxcp_lowerup __P((int));
+static void ipxcp_lowerdown __P((int));
+static void ipxcp_input __P((int, u_char *, int));
+static void ipxcp_protrej __P((int));
+static int ipxcp_printpkt __P((u_char *, int,
+ void (*) __P((void *, char *, ...)), void *));
+
+struct protent ipxcp_protent = {
+ PPP_IPXCP,
+ ipxcp_init,
+ ipxcp_input,
+ ipxcp_protrej,
+ ipxcp_lowerup,
+ ipxcp_lowerdown,
+ ipxcp_open,
+ ipxcp_close,
+ ipxcp_printpkt,
+ NULL,
+ 0,
+ "IPXCP",
+ NULL,
+ NULL,
+ NULL
+};
+
+/*
+ * Lengths of configuration options.
+ */
+
+#define CILEN_VOID 2
+#define CILEN_COMPLETE 2 /* length of complete option */
+#define CILEN_NETN 6 /* network number length option */
+#define CILEN_NODEN 8 /* node number length option */
+#define CILEN_PROTOCOL 4 /* Minimum length of routing protocol */
+#define CILEN_NAME 3 /* Minimum length of router name */
+#define CILEN_COMPRESS 4 /* Minimum length of compression protocol */
+
+#define CODENAME(x) ((x) == CONFACK ? "ACK" : \
+ (x) == CONFNAK ? "NAK" : "REJ")
+
+/* Used in printing the node number */
+#define NODE(base) base[0], base[1], base[2], base[3], base[4], base[5]
+
+/* Used to generate the proper bit mask */
+#define BIT(num) (1 << (num))
+
+/*
+ * Convert from internal to external notation
+ */
+
+static short int
+to_external(internal)
+short int internal;
+{
+ short int external;
+
+ if (internal & IPX_NONE)
+ external = IPX_NONE;
+ else
+ external = RIP_SAP;
+
+ return external;
+}
+
+/*
+ * Make a string representation of a network IP address.
+ */
+
+char *
+ipx_ntoa(ipxaddr)
+u_int32_t ipxaddr;
+{
+ static char b[64];
+ sprintf(b, "%x", ipxaddr);
+ return b;
+}
+
+
+/*
+ * ipxcp_init - Initialize IPXCP.
+ */
+static void
+ipxcp_init(unit)
+ int unit;
+{
+ fsm *f = &ipxcp_fsm[unit];
+
+ f->unit = unit;
+ f->protocol = PPP_IPXCP;
+ f->callbacks = &ipxcp_callbacks;
+ fsm_init(&ipxcp_fsm[unit]);
+
+ memset (wo->name, 0, sizeof (wo->name));
+ memset (wo->our_node, 0, sizeof (wo->our_node));
+ memset (wo->his_node, 0, sizeof (wo->his_node));
+
+ wo->neg_nn = 1;
+ wo->neg_complete = 1;
+ wo->network = 0;
+
+ ao->neg_node = 1;
+ ao->neg_nn = 1;
+ ao->neg_name = 1;
+ ao->neg_complete = 1;
+ ao->neg_router = 1;
+
+ ao->accept_local = 0;
+ ao->accept_remote = 0;
+ ao->accept_network = 0;
+
+ wo->tried_rip = 0;
+ wo->tried_nlsp = 0;
+}
+
+/*
+ * Copy the node number
+ */
+
+static void
+copy_node (src, dst)
+u_char *src, *dst;
+{
+ memcpy (dst, src, sizeof (ipxcp_wantoptions[0].our_node));
+}
+
+/*
+ * Compare node numbers
+ */
+
+static int
+compare_node (src, dst)
+u_char *src, *dst;
+{
+ return memcmp (dst, src, sizeof (ipxcp_wantoptions[0].our_node)) == 0;
+}
+
+/*
+ * Is the node number zero?
+ */
+
+static int
+zero_node (node)
+u_char *node;
+{
+ int indx;
+ for (indx = 0; indx < sizeof (ipxcp_wantoptions[0].our_node); ++indx)
+ if (node [indx] != 0)
+ return 0;
+ return 1;
+}
+
+/*
+ * Increment the node number
+ */
+
+static void
+inc_node (node)
+u_char *node;
+{
+ u_char *outp;
+ u_int32_t magic_num;
+
+ outp = node;
+ magic_num = magic();
+ *outp++ = '\0';
+ *outp++ = '\0';
+ PUTLONG (magic_num, outp);
+}
+
+/*
+ * ipxcp_open - IPXCP is allowed to come up.
+ */
+static void
+ipxcp_open(unit)
+ int unit;
+{
+ fsm_open(&ipxcp_fsm[unit]);
+}
+
+/*
+ * ipxcp_close - Take IPXCP down.
+ */
+static void
+ipxcp_close(unit, reason)
+ int unit;
+ char *reason;
+{
+ fsm_close(&ipxcp_fsm[unit], reason);
+}
+
+
+/*
+ * ipxcp_lowerup - The lower layer is up.
+ */
+static void
+ipxcp_lowerup(unit)
+ int unit;
+{
+ fsm_lowerup(&ipxcp_fsm[unit]);
+}
+
+
+/*
+ * ipxcp_lowerdown - The lower layer is down.
+ */
+static void
+ipxcp_lowerdown(unit)
+ int unit;
+{
+ fsm_lowerdown(&ipxcp_fsm[unit]);
+}
+
+
+/*
+ * ipxcp_input - Input IPXCP packet.
+ */
+static void
+ipxcp_input(unit, p, len)
+ int unit;
+ u_char *p;
+ int len;
+{
+ fsm_input(&ipxcp_fsm[unit], p, len);
+}
+
+
+/*
+ * ipxcp_protrej - A Protocol-Reject was received for IPXCP.
+ *
+ * Pretend the lower layer went down, so we shut up.
+ */
+static void
+ipxcp_protrej(unit)
+ int unit;
+{
+ fsm_lowerdown(&ipxcp_fsm[unit]);
+}
+
+
+/*
+ * ipxcp_resetci - Reset our CI.
+ */
+static void
+ipxcp_resetci(f)
+ fsm *f;
+{
+ wo->req_node = wo->neg_node && ao->neg_node;
+ wo->req_nn = wo->neg_nn && ao->neg_nn;
+
+ if (wo->our_network == 0) {
+ wo->neg_node = 1;
+ ao->accept_network = 1;
+ }
+/*
+ * If our node number is zero then change it.
+ */
+ if (zero_node (wo->our_node)) {
+ inc_node (wo->our_node);
+ ao->accept_local = 1;
+ wo->neg_node = 1;
+ }
+/*
+ * If his node number is zero then change it.
+ */
+ if (zero_node (wo->his_node)) {
+ inc_node (wo->his_node);
+ ao->accept_remote = 1;
+ }
+/*
+ * If no routing agent was specified then we do RIP/SAP according to the
+ * RFC documents. If you have specified something then OK. Otherwise, we
+ * do RIP/SAP.
+ */
+ if (ao->router == 0) {
+ ao->router |= BIT(RIP_SAP);
+ wo->router |= BIT(RIP_SAP);
+ }
+
+ /* Always specify a routing protocol unless it was REJected. */
+ wo->neg_router = 1;
+/*
+ * Start with these default values
+ */
+ *go = *wo;
+}
+
+/*
+ * ipxcp_cilen - Return length of our CI.
+ */
+
+static int
+ipxcp_cilen(f)
+ fsm *f;
+{
+ int len;
+
+ len = go->neg_nn ? CILEN_NETN : 0;
+ len += go->neg_node ? CILEN_NODEN : 0;
+ len += go->neg_name ? CILEN_NAME + strlen (go->name) - 1 : 0;
+
+ /* RFC says that defaults should not be included. */
+ if (go->neg_router && to_external(go->router) != RIP_SAP)
+ len += CILEN_PROTOCOL;
+
+ return (len);
+}
+
+
+/*
+ * ipxcp_addci - Add our desired CIs to a packet.
+ */
+static void
+ipxcp_addci(f, ucp, lenp)
+ fsm *f;
+ u_char *ucp;
+ int *lenp;
+{
+/*
+ * Add the options to the record.
+ */
+ if (go->neg_nn) {
+ PUTCHAR (IPX_NETWORK_NUMBER, ucp);
+ PUTCHAR (CILEN_NETN, ucp);
+ PUTLONG (go->our_network, ucp);
+ }
+
+ if (go->neg_node) {
+ int indx;
+ PUTCHAR (IPX_NODE_NUMBER, ucp);
+ PUTCHAR (CILEN_NODEN, ucp);
+ for (indx = 0; indx < sizeof (go->our_node); ++indx)
+ PUTCHAR (go->our_node[indx], ucp);
+ }
+
+ if (go->neg_name) {
+ int cilen = strlen (go->name);
+ int indx;
+ PUTCHAR (IPX_ROUTER_NAME, ucp);
+ PUTCHAR (CILEN_NAME + cilen - 1, ucp);
+ for (indx = 0; indx < cilen; ++indx)
+ PUTCHAR (go->name [indx], ucp);
+ }
+
+ if (go->neg_router) {
+ short external = to_external (go->router);
+ if (external != RIP_SAP) {
+ PUTCHAR (IPX_ROUTER_PROTOCOL, ucp);
+ PUTCHAR (CILEN_PROTOCOL, ucp);
+ PUTSHORT (external, ucp);
+ }
+ }
+}
+
+/*
+ * ipxcp_ackci - Ack our CIs.
+ *
+ * Returns:
+ * 0 - Ack was bad.
+ * 1 - Ack was good.
+ */
+static int
+ipxcp_ackci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ u_short cilen, citype, cishort;
+ u_char cichar;
+ u_int32_t cilong;
+
+#define ACKCIVOID(opt, neg) \
+ if (neg) { \
+ if ((len -= CILEN_VOID) < 0) \
+ break; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_VOID || \
+ citype != opt) \
+ break; \
+ }
+
+#define ACKCICOMPLETE(opt,neg) ACKCIVOID(opt, neg)
+
+#define ACKCICHARS(opt, neg, val, cnt) \
+ if (neg) { \
+ int indx, count = cnt; \
+ len -= (count + 2); \
+ if (len < 0) \
+ break; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != (count + 2) || \
+ citype != opt) \
+ break; \
+ for (indx = 0; indx < count; ++indx) {\
+ GETCHAR(cichar, p); \
+ if (cichar != ((u_char *) &val)[indx]) \
+ break; \
+ }\
+ if (indx != count) \
+ break; \
+ }
+
+#define ACKCINODE(opt,neg,val) ACKCICHARS(opt,neg,val,sizeof(val))
+#define ACKCINAME(opt,neg,val) ACKCICHARS(opt,neg,val,strlen(val))
+
+#define ACKCINETWORK(opt, neg, val) \
+ if (neg) { \
+ if ((len -= CILEN_NETN) < 0) \
+ break; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_NETN || \
+ citype != opt) \
+ break; \
+ GETLONG(cilong, p); \
+ if (cilong != val) \
+ break; \
+ }
+
+#define ACKCIPROTO(opt, neg, val) \
+ if (neg) { \
+ if (len < 2) \
+ break; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_PROTOCOL || citype != opt) \
+ break; \
+ len -= cilen; \
+ if (len < 0) \
+ break; \
+ GETSHORT(cishort, p); \
+ if (cishort != to_external (val) || cishort == RIP_SAP) \
+ break; \
+ }
+/*
+ * Process the ACK frame in the order in which the frame was assembled
+ */
+ do {
+ ACKCINETWORK (IPX_NETWORK_NUMBER, go->neg_nn, go->our_network);
+ ACKCINODE (IPX_NODE_NUMBER, go->neg_node, go->our_node);
+ ACKCINAME (IPX_ROUTER_NAME, go->neg_name, go->name);
+ ACKCIPROTO (IPX_ROUTER_PROTOCOL, go->neg_router, go->router);
+ ACKCIPROTO (IPX_ROUTER_PROTOCOL, go->neg_router, go->router);
+ ACKCIPROTO (IPX_ROUTER_PROTOCOL, go->neg_router, go->router);
+/*
+ * This is the end of the record.
+ */
+ if (len == 0)
+ return (1);
+ } while (0);
+/*
+ * The frame is invalid
+ */
+ IPXCPDEBUG((LOG_INFO, "ipxcp_ackci: received bad Ack!"));
+ return (0);
+}
+
+/*
+ * ipxcp_nakci - Peer has sent a NAK for some of our CIs.
+ * This should not modify any state if the Nak is bad
+ * or if IPXCP is in the OPENED state.
+ *
+ * Returns:
+ * 0 - Nak was bad.
+ * 1 - Nak was good.
+ */
+
+static int
+ipxcp_nakci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ u_char citype, cilen, *next;
+ u_short s;
+ u_int32_t l;
+ ipxcp_options no; /* options we've seen Naks for */
+ ipxcp_options try; /* options to request next time */
+
+ BZERO(&no, sizeof(no));
+ try = *go;
+
+ while (len > CILEN_VOID) {
+ GETCHAR (citype, p);
+ GETCHAR (cilen, p);
+ len -= cilen;
+ if (len < 0)
+ goto bad;
+ next = &p [cilen - CILEN_VOID];
+
+ switch (citype) {
+ case IPX_NETWORK_NUMBER:
+ if (!go->neg_nn || no.neg_nn || (cilen != CILEN_NETN))
+ goto bad;
+ no.neg_nn = 1;
+
+ GETLONG(l, p);
+ IPXCPDEBUG((LOG_INFO, "local IP address %d", l));
+ if (l && ao->accept_network)
+ try.our_network = l;
+ break;
+
+ case IPX_NODE_NUMBER:
+ if (!go->neg_node || no.neg_node || (cilen != CILEN_NODEN))
+ goto bad;
+ no.neg_node = 1;
+
+ IPXCPDEBUG((LOG_INFO,
+ "local node number %02X%02X%02X%02X%02X%02X",
+ NODE(p)));
+
+ if (!zero_node (p) && ao->accept_local &&
+ ! compare_node (p, ho->his_node))
+ copy_node (p, try.our_node);
+ break;
+
+ /* This has never been sent. Ignore the NAK frame */
+ case IPX_COMPRESSION_PROTOCOL:
+ goto bad;
+
+ case IPX_ROUTER_PROTOCOL:
+ if (!go->neg_router || (cilen < CILEN_PROTOCOL))
+ goto bad;
+
+ GETSHORT (s, p);
+ if (s > 15) /* This is just bad, but ignore for now. */
+ break;
+
+ s = BIT(s);
+ if (no.router & s) /* duplicate NAKs are always bad */
+ goto bad;
+
+ if (no.router == 0) /* Reset on first NAK only */
+ try.router = 0;
+
+ no.router |= s;
+ try.router |= s;
+ try.neg_router = 1;
+
+ IPXCPDEBUG((LOG_INFO, "Router protocol number %d", s));
+ break;
+
+ /* These, according to the RFC, must never be NAKed. */
+ case IPX_ROUTER_NAME:
+ case IPX_COMPLETE:
+ goto bad;
+
+ /* These are for options which we have not seen. */
+ default:
+ break;
+ }
+ p = next;
+ }
+
+ /* If there is still anything left, this packet is bad. */
+ if (len != 0)
+ goto bad;
+
+ /*
+ * Do not permit the peer to force a router protocol which we do not
+ * support. However, default to the condition that will accept "NONE".
+ */
+ try.router &= (ao->router | BIT(IPX_NONE));
+ if (try.router == 0 && ao->router != 0)
+ try.router = BIT(IPX_NONE);
+
+ if (try.router != 0)
+ try.neg_router = 1;
+
+ /*
+ * OK, the Nak is good. Now we can update state.
+ */
+ if (f->state != OPENED)
+ *go = try;
+
+ return 1;
+
+bad:
+ IPXCPDEBUG((LOG_INFO, "ipxcp_nakci: received bad Nak!"));
+ return 0;
+}
+
+/*
+ * ipxcp_rejci - Reject some of our CIs.
+ */
+static int
+ipxcp_rejci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ u_short cilen, citype, cishort;
+ u_char cichar;
+ u_int32_t cilong;
+ ipxcp_options try; /* options to request next time */
+
+#define REJCINETWORK(opt, neg, val) \
+ if (neg && p[0] == opt) { \
+ if ((len -= CILEN_NETN) < 0) \
+ break; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_NETN || \
+ citype != opt) \
+ break; \
+ GETLONG(cilong, p); \
+ if (cilong != val) \
+ break; \
+ IPXCPDEBUG((LOG_INFO,"ipxcp_rejci rejected long opt %d", opt)); \
+ neg = 0; \
+ }
+
+#define REJCICHARS(opt, neg, val, cnt) \
+ if (neg && p[0] == opt) { \
+ int indx, count = cnt; \
+ len -= (count + 2); \
+ if (len < 0) \
+ break; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != (count + 2) || \
+ citype != opt) \
+ break; \
+ for (indx = 0; indx < count; ++indx) {\
+ GETCHAR(cichar, p); \
+ if (cichar != ((u_char *) &val)[indx]) \
+ break; \
+ }\
+ if (indx != count) \
+ break; \
+ IPXCPDEBUG((LOG_INFO,"ipxcp_rejci rejected opt %d", opt)); \
+ neg = 0; \
+ }
+
+#define REJCINODE(opt,neg,val) REJCICHARS(opt,neg,val,sizeof(val))
+#define REJCINAME(opt,neg,val) REJCICHARS(opt,neg,val,strlen(val))
+
+#define REJCIVOID(opt, neg) \
+ if (neg && p[0] == opt) { \
+ if ((len -= CILEN_VOID) < 0) \
+ break; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_VOID || citype != opt) \
+ break; \
+ IPXCPDEBUG((LOG_INFO, "ipxcp_rejci rejected void opt %d", opt)); \
+ neg = 0; \
+ }
+
+/* a reject for RIP/SAP is invalid since we don't send it and you can't
+ reject something which is not sent. (You can NAK, but you can't REJ.) */
+#define REJCIPROTO(opt, neg, val, bit) \
+ if (neg && p[0] == opt) { \
+ if ((len -= CILEN_PROTOCOL) < 0) \
+ break; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_PROTOCOL) \
+ break; \
+ GETSHORT(cishort, p); \
+ if (cishort != to_external (val) || cishort == RIP_SAP) \
+ break; \
+ IPXCPDEBUG((LOG_INFO, "ipxcp_rejci short opt %d", opt)); \
+ neg = 0; \
+ }
+/*
+ * Any Rejected CIs must be in exactly the same order that we sent.
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+ try = *go;
+
+ do {
+ REJCINETWORK (IPX_NETWORK_NUMBER, try.neg_nn, try.our_network);
+ REJCINODE (IPX_NODE_NUMBER, try.neg_node, try.our_node);
+ REJCINAME (IPX_ROUTER_NAME, try.neg_name, try.name);
+ REJCIPROTO (IPX_ROUTER_PROTOCOL, try.neg_router, try.router, 0);
+/*
+ * This is the end of the record.
+ */
+ if (len == 0) {
+ if (f->state != OPENED)
+ *go = try;
+ return (1);
+ }
+ } while (0);
+/*
+ * The frame is invalid at this point.
+ */
+ IPXCPDEBUG((LOG_INFO, "ipxcp_rejci: received bad Reject!"));
+ return 0;
+}
+
+/*
+ * ipxcp_reqci - Check the peer's requested CIs and send appropriate response.
+ *
+ * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified
+ * appropriately. If reject_if_disagree is non-zero, doesn't return
+ * CONFNAK; returns CONFREJ if it can't return CONFACK.
+ */
+static int
+ipxcp_reqci(f, inp, len, reject_if_disagree)
+ fsm *f;
+ u_char *inp; /* Requested CIs */
+ int *len; /* Length of requested CIs */
+ int reject_if_disagree;
+{
+ u_char *cip, *next; /* Pointer to current and next CIs */
+ u_short cilen, citype; /* Parsed len, type */
+ u_short cishort; /* Parsed short value */
+ u_int32_t cinetwork; /* Parsed address values */
+ int rc = CONFACK; /* Final packet return code */
+ int orc; /* Individual option return code */
+ u_char *p; /* Pointer to next char to parse */
+ u_char *ucp = inp; /* Pointer to current output char */
+ int l = *len; /* Length left */
+
+ /*
+ * Reset all his options.
+ */
+ BZERO(ho, sizeof(*ho));
+
+ /*
+ * Process all his options.
+ */
+ next = inp;
+ while (l) {
+ orc = CONFACK; /* Assume success */
+ cip = p = next; /* Remember begining of CI */
+ if (l < 2 || /* Not enough data for CI header or */
+ p[1] < 2 || /* CI length too small or */
+ p[1] > l) { /* CI length too big? */
+ IPXCPDEBUG((LOG_INFO, "ipxcp_reqci: bad CI length!"));
+ orc = CONFREJ; /* Reject bad CI */
+ cilen = l; /* Reject till end of packet */
+ l = 0; /* Don't loop again */
+ goto endswitch;
+ }
+ GETCHAR(citype, p); /* Parse CI type */
+ GETCHAR(cilen, p); /* Parse CI length */
+ l -= cilen; /* Adjust remaining length */
+ next += cilen; /* Step to next CI */
+
+ switch (citype) { /* Check CI type */
+/*
+ * The network number must match. Choose the larger of the two.
+ */
+ case IPX_NETWORK_NUMBER:
+ IPXCPDEBUG((LOG_INFO, "ipxcp: received Network Number request"));
+
+ /* if we wont negotiate the network number or the length is wrong
+ then reject the option */
+ if ( !ao->neg_nn || cilen != CILEN_NETN ) {
+ orc = CONFREJ;
+ break;
+ }
+ GETLONG(cinetwork, p);
+ IPXCPDEBUG((LOG_INFO,"Remote proposed IPX network number is %8Lx",tl));
+
+ /* If the network numbers match then acknowledge them. */
+ if (cinetwork != 0) {
+ ho->his_network = cinetwork;
+ ho->neg_nn = 1;
+ if (wo->our_network == cinetwork)
+ break;
+/*
+ * If the network number is not given or we don't accept their change or
+ * the network number is too small then NAK it.
+ */
+ if (! ao->accept_network || cinetwork < wo->our_network) {
+ DECPTR (sizeof (u_int32_t), p);
+ PUTLONG (wo->our_network, p);
+ orc = CONFNAK;
+ }
+ break;
+ }
+/*
+ * The peer sent '0' for the network. Give it ours if we have one.
+ */
+ if (go->our_network != 0) {
+ DECPTR (sizeof (u_int32_t), p);
+ PUTLONG (wo->our_network, p);
+ orc = CONFNAK;
+/*
+ * We don't have one. Reject the value.
+ */
+ } else
+ orc = CONFREJ;
+
+ break;
+/*
+ * The node number is required
+ */
+ case IPX_NODE_NUMBER:
+ IPXCPDEBUG((LOG_INFO, "ipxcp: received Node Number request"));
+
+ /* if we wont negotiate the node number or the length is wrong
+ then reject the option */
+ if ( cilen != CILEN_NODEN ) {
+ orc = CONFREJ;
+ break;
+ }
+
+ copy_node (p, ho->his_node);
+ ho->neg_node = 1;
+/*
+ * If the remote does not have a number and we do then NAK it with the value
+ * which we have for it. (We never have a default value of zero.)
+ */
+ if (zero_node (ho->his_node)) {
+ orc = CONFNAK;
+ copy_node (wo->his_node, p);
+ INCPTR (sizeof (wo->his_node), p);
+ break;
+ }
+/*
+ * If you have given me the expected network node number then I'll accept
+ * it now.
+ */
+ if (compare_node (wo->his_node, ho->his_node)) {
+ orc = CONFACK;
+ ho->neg_node = 1;
+ INCPTR (sizeof (wo->his_node), p);
+ break;
+ }
+/*
+ * If his node number is the same as ours then ask him to try the next
+ * value.
+ */
+ if (compare_node (ho->his_node, go->our_node)) {
+ inc_node (ho->his_node);
+ orc = CONFNAK;
+ copy_node (ho->his_node, p);
+ INCPTR (sizeof (wo->his_node), p);
+ break;
+ }
+/*
+ * If we don't accept a new value then NAK it.
+ */
+ if (! ao->accept_remote) {
+ copy_node (wo->his_node, p);
+ INCPTR (sizeof (wo->his_node), p);
+ orc = CONFNAK;
+ break;
+ }
+ orc = CONFACK;
+ ho->neg_node = 1;
+ INCPTR (sizeof (wo->his_node), p);
+ break;
+/*
+ * Compression is not desired at this time. It is always rejected.
+ */
+ case IPX_COMPRESSION_PROTOCOL:
+ IPXCPDEBUG((LOG_INFO, "ipxcp: received Compression Protocol request "));
+ orc = CONFREJ;
+ break;
+/*
+ * The routing protocol is a bitmask of various types. Any combination
+ * of the values RIP_SAP and NLSP are permissible. 'IPX_NONE' for no
+ * routing protocol must be specified only once.
+ */
+ case IPX_ROUTER_PROTOCOL:
+ if ( !ao->neg_router || cilen < CILEN_PROTOCOL ) {
+ orc = CONFREJ;
+ break;
+ }
+
+ GETSHORT (cishort, p);
+ IPXCPDEBUG((LOG_INFO,
+ "Remote router protocol number 0x%04x",
+ cishort));
+
+ if (wo->neg_router == 0) {
+ wo->neg_router = 1;
+ wo->router = BIT(IPX_NONE);
+ }
+
+ if ((cishort == IPX_NONE && ho->router != 0) ||
+ (ho->router & BIT(IPX_NONE))) {
+ orc = CONFREJ;
+ break;
+ }
+
+ cishort = BIT(cishort);
+ if (ho->router & cishort) {
+ orc = CONFREJ;
+ break;
+ }
+
+ ho->router |= cishort;
+ ho->neg_router = 1;
+
+ /* Finally do not allow a router protocol which we do not
+ support. */
+
+ if ((cishort & (ao->router | BIT(IPX_NONE))) == 0) {
+ int protocol;
+
+ if (cishort == BIT(NLSP) &&
+ (ao->router & BIT(RIP_SAP)) &&
+ !wo->tried_rip) {
+ protocol = RIP_SAP;
+ wo->tried_rip = 1;
+ } else
+ protocol = IPX_NONE;
+
+ DECPTR (sizeof (u_int16_t), p);
+ PUTSHORT (protocol, p);
+ orc = CONFNAK;
+ }
+ break;
+/*
+ * The router name is advisorary. Just accept it if it is not too large.
+ */
+ case IPX_ROUTER_NAME:
+ IPXCPDEBUG((LOG_INFO, "ipxcp: received Router Name request"));
+ if (cilen >= CILEN_NAME) {
+ int name_size = cilen - CILEN_NAME;
+ if (name_size > sizeof (ho->name))
+ name_size = sizeof (ho->name) - 1;
+ memset (ho->name, 0, sizeof (ho->name));
+ memcpy (ho->name, p, name_size);
+ ho->name [name_size] = '\0';
+ ho->neg_name = 1;
+ orc = CONFACK;
+ break;
+ }
+ orc = CONFREJ;
+ break;
+/*
+ * This is advisorary.
+ */
+ case IPX_COMPLETE:
+ IPXCPDEBUG((LOG_INFO, "ipxcp: received Complete request"));
+ if (cilen != CILEN_COMPLETE)
+ orc = CONFREJ;
+ else {
+ ho->neg_complete = 1;
+ orc = CONFACK;
+ }
+ break;
+/*
+ * All other entries are not known at this time.
+ */
+ default:
+ IPXCPDEBUG((LOG_INFO, "ipxcp: received Complete request"));
+ orc = CONFREJ;
+ break;
+ }
+
+endswitch:
+ IPXCPDEBUG((LOG_INFO, " (%s)\n", CODENAME(orc)));
+
+ if (orc == CONFACK && /* Good CI */
+ rc != CONFACK) /* but prior CI wasnt? */
+ continue; /* Don't send this one */
+
+ if (orc == CONFNAK) { /* Nak this CI? */
+ if (reject_if_disagree) /* Getting fed up with sending NAKs? */
+ orc = CONFREJ; /* Get tough if so */
+ if (rc == CONFREJ) /* Rejecting prior CI? */
+ continue; /* Don't send this one */
+ if (rc == CONFACK) { /* Ack'd all prior CIs? */
+ rc = CONFNAK; /* Not anymore... */
+ ucp = inp; /* Backup */
+ }
+ }
+
+ if (orc == CONFREJ && /* Reject this CI */
+ rc != CONFREJ) { /* but no prior ones? */
+ rc = CONFREJ;
+ ucp = inp; /* Backup */
+ }
+
+ /* Need to move CI? */
+ if (ucp != cip)
+ BCOPY(cip, ucp, cilen); /* Move it */
+
+ /* Update output pointer */
+ INCPTR(cilen, ucp);
+ }
+
+ /*
+ * If we aren't rejecting this packet, and we want to negotiate
+ * their address, and they didn't send their address, then we
+ * send a NAK with 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..0890181
--- /dev/null
+++ b/usr.sbin/pppd/ipxcp.h
@@ -0,0 +1,71 @@
+/*
+ * ipxcp.h - IPX Control Protocol definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Options.
+ */
+#define IPX_NETWORK_NUMBER 1 /* IPX Network Number */
+#define IPX_NODE_NUMBER 2
+#define IPX_COMPRESSION_PROTOCOL 3
+#define IPX_ROUTER_PROTOCOL 4
+#define IPX_ROUTER_NAME 5
+#define IPX_COMPLETE 6
+
+/* Values for the router protocol */
+#define IPX_NONE 0
+#define RIP_SAP 2
+#define NLSP 4
+
+typedef struct ipxcp_options {
+ int neg_node : 1; /* Negotiate IPX node number? */
+ int req_node : 1; /* Ask peer to send IPX node number? */
+
+ int neg_nn : 1; /* Negotiate IPX network number? */
+ int req_nn : 1; /* Ask peer to send IPX network number */
+
+ int neg_name : 1; /* Negotiate IPX router name */
+ int neg_complete : 1; /* Negotiate completion */
+ int neg_router : 1; /* Negotiate IPX router number */
+
+ int accept_local : 1; /* accept peer's value for ournode */
+ int accept_remote : 1; /* accept peer's value for hisnode */
+ int accept_network : 1; /* accept network number */
+
+ int tried_nlsp : 1; /* I have suggested NLSP already */
+ int tried_rip : 1; /* I have suggested RIP/SAP already */
+
+ u_int32_t his_network; /* base network number */
+ u_int32_t our_network; /* our value for network number */
+ u_int32_t network; /* the final network number */
+
+ u_char his_node[6]; /* peer's node number */
+ u_char our_node[6]; /* our node number */
+ u_char name [48]; /* name of the router */
+ int router; /* routing protocol */
+} ipxcp_options;
+
+extern fsm ipxcp_fsm[];
+extern ipxcp_options ipxcp_wantoptions[];
+extern ipxcp_options ipxcp_gotoptions[];
+extern ipxcp_options ipxcp_allowoptions[];
+extern ipxcp_options ipxcp_hisoptions[];
+
+extern struct protent ipxcp_protent;
diff --git a/usr.sbin/pppd/lcp.c b/usr.sbin/pppd/lcp.c
new file mode 100644
index 0000000..21ff02e
--- /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[] = "$FreeBSD$";
+#endif
+
+/*
+ * TODO:
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <assert.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+
+#include "pppd.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "chap.h"
+#include "magic.h"
+
+/* global vars */
+fsm lcp_fsm[NUM_PPP]; /* LCP fsm structure (global)*/
+lcp_options lcp_wantoptions[NUM_PPP]; /* Options that we want to request */
+lcp_options lcp_gotoptions[NUM_PPP]; /* Options that peer ack'd */
+lcp_options lcp_allowoptions[NUM_PPP]; /* Options we allow peer to request */
+lcp_options lcp_hisoptions[NUM_PPP]; /* Options that we ack'd */
+u_int32_t xmit_accm[NUM_PPP][8]; /* extended transmit ACCM */
+
+static u_int32_t lcp_echos_pending = 0; /* Number of outstanding echo msgs */
+static u_int32_t lcp_echo_number = 0; /* ID number of next echo frame */
+static u_int32_t lcp_echo_timer_running = 0; /* TRUE if a timer is running */
+
+static u_char nak_buffer[PPP_MRU]; /* where we construct a nak packet */
+
+/*
+ * Callbacks for fsm code. (CI = Configuration Information)
+ */
+static void lcp_resetci __P((fsm *)); /* Reset our CI */
+static int lcp_cilen __P((fsm *)); /* Return length of our CI */
+static void lcp_addci __P((fsm *, u_char *, int *)); /* Add our CI to pkt */
+static int lcp_ackci __P((fsm *, u_char *, int)); /* Peer ack'd our CI */
+static int lcp_nakci __P((fsm *, u_char *, int)); /* Peer nak'd our CI */
+static int lcp_rejci __P((fsm *, u_char *, int)); /* Peer rej'd our CI */
+static int lcp_reqci __P((fsm *, u_char *, int *, int)); /* Rcv peer CI */
+static void lcp_up __P((fsm *)); /* We're UP */
+static void lcp_down __P((fsm *)); /* We're DOWN */
+static void lcp_starting __P((fsm *)); /* We need lower layer up */
+static void lcp_finished __P((fsm *)); /* We need lower layer down */
+static int lcp_extcode __P((fsm *, int, int, u_char *, int));
+static void lcp_rprotrej __P((fsm *, u_char *, int));
+
+/*
+ * routines to send LCP echos to peer
+ */
+
+static void lcp_echo_lowerup __P((int));
+static void lcp_echo_lowerdown __P((int));
+static void LcpEchoTimeout __P((void *));
+static void lcp_received_echo_reply __P((fsm *, int, u_char *, int));
+static void LcpSendEchoRequest __P((fsm *));
+static void LcpLinkFailure __P((fsm *));
+static void LcpEchoCheck __P((fsm *));
+
+static fsm_callbacks lcp_callbacks = { /* LCP callback routines */
+ lcp_resetci, /* Reset our Configuration Information */
+ lcp_cilen, /* Length of our Configuration Information */
+ lcp_addci, /* Add our Configuration Information */
+ lcp_ackci, /* ACK our Configuration Information */
+ lcp_nakci, /* NAK our Configuration Information */
+ lcp_rejci, /* Reject our Configuration Information */
+ lcp_reqci, /* Request peer's Configuration Information */
+ lcp_up, /* Called when fsm reaches OPENED state */
+ lcp_down, /* Called when fsm leaves OPENED state */
+ lcp_starting, /* Called when we want the lower layer up */
+ lcp_finished, /* Called when we want the lower layer down */
+ NULL, /* Called when Protocol-Reject received */
+ NULL, /* Retransmission is necessary */
+ lcp_extcode, /* Called to handle LCP-specific codes */
+ "LCP" /* String name of protocol */
+};
+
+/*
+ * Protocol entry points.
+ * Some of these are called directly.
+ */
+
+static void lcp_init __P((int));
+static void lcp_input __P((int, u_char *, int));
+static void lcp_protrej __P((int));
+static int lcp_printpkt __P((u_char *, int,
+ void (*) __P((void *, char *, ...)), void *));
+
+struct protent lcp_protent = {
+ PPP_LCP,
+ lcp_init,
+ lcp_input,
+ lcp_protrej,
+ lcp_lowerup,
+ lcp_lowerdown,
+ lcp_open,
+ lcp_close,
+ lcp_printpkt,
+ NULL,
+ 1,
+ "LCP",
+ NULL,
+ NULL,
+ NULL
+};
+
+int lcp_loopbackfail = DEFLOOPBACKFAIL;
+
+/*
+ * Length of each type of configuration option (in octets)
+ */
+#define CILEN_VOID 2
+#define CILEN_CHAR 3
+#define CILEN_SHORT 4 /* CILEN_VOID + sizeof(short) */
+#define CILEN_CHAP 5 /* CILEN_VOID + sizeof(short) + 1 */
+#define CILEN_LONG 6 /* CILEN_VOID + sizeof(long) */
+#define CILEN_LQR 8 /* CILEN_VOID + sizeof(short) + sizeof(long) */
+#define CILEN_CBCP 3
+
+#define CODENAME(x) ((x) == CONFACK ? "ACK" : \
+ (x) == CONFNAK ? "NAK" : "REJ")
+
+
+/*
+ * lcp_init - Initialize LCP.
+ */
+static void
+lcp_init(unit)
+ int unit;
+{
+ fsm *f = &lcp_fsm[unit];
+ lcp_options *wo = &lcp_wantoptions[unit];
+ lcp_options *ao = &lcp_allowoptions[unit];
+
+ f->unit = unit;
+ f->protocol = PPP_LCP;
+ f->callbacks = &lcp_callbacks;
+
+ fsm_init(f);
+
+ wo->passive = 0;
+ wo->silent = 0;
+ wo->restart = 0; /* Set to 1 in kernels or multi-line
+ implementations */
+ wo->neg_mru = 1;
+ wo->mru = DEFMRU;
+ wo->neg_asyncmap = 0;
+ wo->asyncmap = 0;
+ wo->neg_chap = 0; /* Set to 1 on server */
+ wo->neg_upap = 0; /* Set to 1 on server */
+ wo->chap_mdtype = CHAP_DIGEST_MD5;
+ wo->neg_magicnumber = 1;
+ wo->neg_pcompression = 1;
+ wo->neg_accompression = 1;
+ wo->neg_lqr = 0; /* no LQR implementation yet */
+ wo->neg_cbcp = 0;
+
+ ao->neg_mru = 1;
+ ao->mru = MAXMRU;
+ ao->neg_asyncmap = 1;
+ ao->asyncmap = 0;
+ ao->neg_chap = 1;
+ ao->chap_mdtype = CHAP_DIGEST_MD5;
+ ao->neg_upap = 1;
+ ao->neg_magicnumber = 1;
+ ao->neg_pcompression = 1;
+ ao->neg_accompression = 1;
+ ao->neg_lqr = 0; /* no LQR implementation yet */
+#ifdef CBCP_SUPPORT
+ ao->neg_cbcp = 1;
+#else
+ ao->neg_cbcp = 0;
+#endif
+
+ memset(xmit_accm[unit], 0, sizeof(xmit_accm[0]));
+ xmit_accm[unit][3] = 0x60000000;
+}
+
+
+/*
+ * lcp_open - LCP is allowed to come up.
+ */
+void
+lcp_open(unit)
+ int unit;
+{
+ fsm *f = &lcp_fsm[unit];
+ lcp_options *wo = &lcp_wantoptions[unit];
+
+ f->flags = 0;
+ if (wo->passive)
+ f->flags |= OPT_PASSIVE;
+ if (wo->silent)
+ f->flags |= OPT_SILENT;
+ fsm_open(f);
+}
+
+
+/*
+ * lcp_close - Take LCP down.
+ */
+void
+lcp_close(unit, reason)
+ int unit;
+ char *reason;
+{
+ fsm *f = &lcp_fsm[unit];
+
+ if (phase != PHASE_DEAD)
+ phase = PHASE_TERMINATE;
+ if (f->state == STOPPED && f->flags & (OPT_PASSIVE|OPT_SILENT)) {
+ /*
+ * This action is not strictly according to the FSM in RFC1548,
+ * but it does mean that the program terminates if you do a
+ * lcp_close() in passive/silent mode when a connection hasn't
+ * been established.
+ */
+ f->state = CLOSED;
+ lcp_finished(f);
+
+ } else
+ fsm_close(&lcp_fsm[unit], reason);
+}
+
+
+/*
+ * lcp_lowerup - The lower layer is up.
+ */
+void
+lcp_lowerup(unit)
+ int unit;
+{
+ lcp_options *wo = &lcp_wantoptions[unit];
+
+ /*
+ * Don't use A/C or protocol compression on transmission,
+ * but accept A/C and protocol compressed packets
+ * if we are going to ask for A/C and protocol compression.
+ */
+ ppp_set_xaccm(unit, xmit_accm[unit]);
+ ppp_send_config(unit, PPP_MRU, 0xffffffff, 0, 0);
+ ppp_recv_config(unit, PPP_MRU, 0xffffffff,
+ wo->neg_pcompression, wo->neg_accompression);
+ peer_mru[unit] = PPP_MRU;
+ lcp_allowoptions[unit].asyncmap = xmit_accm[unit][0];
+
+ fsm_lowerup(&lcp_fsm[unit]);
+}
+
+
+/*
+ * lcp_lowerdown - The lower layer is down.
+ */
+void
+lcp_lowerdown(unit)
+ int unit;
+{
+ fsm_lowerdown(&lcp_fsm[unit]);
+}
+
+
+/*
+ * lcp_input - Input LCP packet.
+ */
+static void
+lcp_input(unit, p, len)
+ int unit;
+ u_char *p;
+ int len;
+{
+ fsm *f = &lcp_fsm[unit];
+
+ fsm_input(f, p, len);
+}
+
+
+/*
+ * lcp_extcode - Handle a LCP-specific code.
+ */
+static int
+lcp_extcode(f, code, id, inp, len)
+ fsm *f;
+ int code, id;
+ u_char *inp;
+ int len;
+{
+ u_char *magp;
+
+ switch( code ){
+ case PROTREJ:
+ lcp_rprotrej(f, inp, len);
+ break;
+
+ case ECHOREQ:
+ if (f->state != OPENED)
+ break;
+ LCPDEBUG((LOG_INFO, "lcp: Echo-Request, Rcvd id %d", id));
+ magp = inp;
+ PUTLONG(lcp_gotoptions[f->unit].magicnumber, magp);
+ fsm_sdata(f, ECHOREP, id, inp, len);
+ break;
+
+ case ECHOREP:
+ lcp_received_echo_reply(f, id, inp, len);
+ break;
+
+ case DISCREQ:
+ break;
+
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+
+/*
+ * lcp_rprotrej - Receive 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..2a19e26
--- /dev/null
+++ b/usr.sbin/pppd/lcp.h
@@ -0,0 +1,88 @@
+/*
+ * lcp.h - Link Control Protocol definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Options.
+ */
+#define CI_MRU 1 /* Maximum Receive Unit */
+#define CI_ASYNCMAP 2 /* Async Control Character Map */
+#define CI_AUTHTYPE 3 /* Authentication Type */
+#define CI_QUALITY 4 /* Quality Protocol */
+#define CI_MAGICNUMBER 5 /* Magic Number */
+#define CI_PCOMPRESSION 7 /* Protocol Field Compression */
+#define CI_ACCOMPRESSION 8 /* Address/Control Field Compression */
+#define CI_CALLBACK 13 /* callback */
+
+/*
+ * LCP-specific packet types.
+ */
+#define PROTREJ 8 /* Protocol Reject */
+#define ECHOREQ 9 /* Echo Request */
+#define ECHOREP 10 /* Echo Reply */
+#define DISCREQ 11 /* Discard Request */
+#define CBCP_OPT 6 /* Use callback control protocol */
+
+/*
+ * The state of options is described by an lcp_options structure.
+ */
+typedef struct lcp_options {
+ int passive : 1; /* Don't die if we don't get a response */
+ int silent : 1; /* Wait for the other end to start first */
+ int restart : 1; /* Restart vs. exit after close */
+ int neg_mru : 1; /* Negotiate the MRU? */
+ int neg_asyncmap : 1; /* Negotiate the async map? */
+ int neg_upap : 1; /* Ask for UPAP authentication? */
+ int neg_chap : 1; /* Ask for CHAP authentication? */
+ int neg_magicnumber : 1; /* Ask for magic number? */
+ int neg_pcompression : 1; /* HDLC Protocol Field Compression? */
+ int neg_accompression : 1; /* HDLC Address/Control Field Compression? */
+ int neg_lqr : 1; /* Negotiate use of Link Quality Reports */
+ int neg_cbcp : 1; /* Negotiate use of CBCP */
+ u_short mru; /* Value of MRU */
+ u_char chap_mdtype; /* which MD type (hashing algorithm) */
+ u_int32_t asyncmap; /* Value of async map */
+ u_int32_t magicnumber;
+ int numloops; /* Number of loops during magic number neg. */
+ u_int32_t lqr_period; /* Reporting period for LQR 1/100ths second */
+} lcp_options;
+
+extern fsm lcp_fsm[];
+extern lcp_options lcp_wantoptions[];
+extern lcp_options lcp_gotoptions[];
+extern lcp_options lcp_allowoptions[];
+extern lcp_options lcp_hisoptions[];
+extern u_int32_t xmit_accm[][8];
+
+#define DEFMRU 1500 /* Try for this */
+#define MINMRU 128 /* No MRUs below this */
+#define MAXMRU 16384 /* Normally limit MRU to this */
+
+void lcp_open __P((int));
+void lcp_close __P((int, char *));
+void lcp_lowerup __P((int));
+void lcp_lowerdown __P((int));
+void lcp_sprotrej __P((int, u_char *, int)); /* send protocol reject */
+
+extern struct protent lcp_protent;
+
+/* Default number of times we receive our magic number from the peer
+ before deciding the link is looped-back. */
+#define DEFLOOPBACKFAIL 10
diff --git a/usr.sbin/pppd/magic.c b/usr.sbin/pppd/magic.c
new file mode 100644
index 0000000..1e13d02
--- /dev/null
+++ b/usr.sbin/pppd/magic.c
@@ -0,0 +1,87 @@
+/*
+ * magic.c - PPP Magic Number routines.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char rcsid[] = "$FreeBSD$";
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include "pppd.h"
+#include "magic.h"
+
+extern long mrand48 __P((void));
+extern void srand48 __P((long));
+
+/*
+ * magic_init - Initialize the magic number generator.
+ *
+ * Attempts to compute a random number seed which will not repeat.
+ * The current method uses the current hostid, current process ID
+ * and current time, currently.
+ */
+void
+magic_init()
+{
+ long seed;
+ struct timeval t;
+
+ gettimeofday(&t, NULL);
+ seed = get_host_seed() ^ t.tv_sec ^ t.tv_usec ^ getpid();
+ srand48(seed);
+}
+
+/*
+ * magic - Returns the next magic number.
+ */
+u_int32_t
+magic()
+{
+ return (u_int32_t) mrand48();
+}
+
+#ifdef NO_DRAND48
+/*
+ * Substitute procedures for those systems which don't have
+ * drand48 et al.
+ */
+
+double
+drand48()
+{
+ return (double)random() / (double)0x7fffffffL; /* 2**31-1 */
+}
+
+long
+mrand48()
+{
+ return random();
+}
+
+void
+srand48(seedval)
+long seedval;
+{
+ srandom((int)seedval);
+}
+
+#endif
diff --git a/usr.sbin/pppd/magic.h b/usr.sbin/pppd/magic.h
new file mode 100644
index 0000000..82f65cf
--- /dev/null
+++ b/usr.sbin/pppd/magic.h
@@ -0,0 +1,23 @@
+/*
+ * magic.h - PPP Magic Number definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $FreeBSD$
+ */
+
+void magic_init __P((void)); /* Initialize the magic number generator */
+u_int32_t magic __P((void)); /* Returns the next magic number */
diff --git a/usr.sbin/pppd/main.c b/usr.sbin/pppd/main.c
new file mode 100644
index 0000000..ba9c254
--- /dev/null
+++ b/usr.sbin/pppd/main.c
@@ -0,0 +1,1717 @@
+/*
+ * main.c - Point-to-Point Protocol main module
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char rcsid[] = "$FreeBSD$";
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <netdb.h>
+#include <utmp.h>
+#include <pwd.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+
+#include "pppd.h"
+#include "magic.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ipcp.h"
+#include "upap.h"
+#include "chap.h"
+#include "ccp.h"
+#include "pathnames.h"
+#include "patchlevel.h"
+
+#ifdef CBCP_SUPPORT
+#include "cbcp.h"
+#endif
+
+#if defined(SUNOS4)
+extern char *strerror();
+#endif
+
+#ifdef IPX_CHANGE
+#include "ipxcp.h"
+#endif /* IPX_CHANGE */
+#ifdef AT_CHANGE
+#include "atcp.h"
+#endif
+
+/* interface vars */
+char ifname[32]; /* Interface name */
+int ifunit; /* Interface unit number */
+
+char *progname; /* Name of this program */
+char hostname[MAXNAMELEN]; /* Our hostname */
+static char pidfilename[MAXPATHLEN]; /* name of pid file */
+static char iffilename[MAXPATHLEN]; /* name of if file */
+static char default_devnam[MAXPATHLEN]; /* name of default device */
+static pid_t pid; /* Our pid */
+static uid_t uid; /* Our real user-id */
+time_t etime,stime; /* End and Start time */
+int minutes; /* connection duration */
+static int conn_running; /* we have a [dis]connector running */
+
+int ttyfd = -1; /* Serial port file descriptor */
+mode_t tty_mode = -1; /* Original access permissions to tty */
+int baud_rate; /* Actual bits/second for serial device */
+int hungup; /* terminal has been hung up */
+int privileged; /* we're running as real uid root */
+int need_holdoff; /* need holdoff period before restarting */
+int detached; /* have detached from terminal */
+
+int phase; /* where the link is at */
+int kill_link;
+int open_ccp_flag;
+
+char **script_env; /* Env. variable values for scripts */
+int s_env_nalloc; /* # words avail at script_env */
+
+u_char outpacket_buf[PPP_MRU+PPP_HDRLEN]; /* buffer for outgoing packet */
+u_char inpacket_buf[PPP_MRU+PPP_HDRLEN]; /* buffer for incoming packet */
+
+static int n_children; /* # child processes still running */
+
+static int locked; /* lock() has succeeded */
+
+char *no_ppp_msg = "Sorry - this system lacks PPP kernel support\n";
+
+/* Prototypes for procedures local to this file. */
+
+static void create_pidfile __P((void));
+static void cleanup __P((void));
+static void close_tty __P((void));
+static void get_input __P((void));
+static void calltimeout __P((void));
+static struct timeval *timeleft __P((struct timeval *));
+static void kill_my_pg __P((int));
+static void hup __P((int));
+static void term __P((int));
+static void chld __P((int));
+static void toggle_debug __P((int));
+static void open_ccp __P((int));
+static void bad_signal __P((int));
+static void holdoff_end __P((void *));
+static int device_script __P((char *, int, int));
+static void reap_kids __P((void));
+static void pr_log __P((void *, char *, ...));
+
+extern char *ttyname __P((int));
+extern char *getlogin __P((void));
+int main __P((int, char *[]));
+
+#ifdef ultrix
+#undef O_NONBLOCK
+#define O_NONBLOCK O_NDELAY
+#endif
+
+#ifdef ULTRIX
+#define setlogmask(x)
+#endif
+
+/*
+ * PPP Data Link Layer "protocol" table.
+ * One entry per supported protocol.
+ * The last entry must be NULL.
+ */
+struct protent *protocols[] = {
+ &lcp_protent,
+ &pap_protent,
+ &chap_protent,
+#ifdef CBCP_SUPPORT
+ &cbcp_protent,
+#endif
+ &ipcp_protent,
+ &ccp_protent,
+#ifdef IPX_CHANGE
+ &ipxcp_protent,
+#endif
+#ifdef AT_CHANGE
+ &atcp_protent,
+#endif
+ NULL
+};
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int i, n, fdflags;
+ struct sigaction sa;
+ FILE *iffile;
+ char *p;
+ struct passwd *pw;
+ struct timeval timo;
+ sigset_t mask;
+ struct protent *protp;
+ struct stat statbuf;
+ int connect_attempts = 0;
+ char numbuf[16];
+
+ phase = PHASE_INITIALIZE;
+ p = ttyname(0);
+ if (p)
+ strcpy(devnam, p);
+ strcpy(default_devnam, devnam);
+
+ script_env = NULL;
+
+ /* Initialize syslog facilities */
+#ifdef ULTRIX
+ openlog("pppd", LOG_PID);
+#else
+ openlog("pppd", LOG_PID | LOG_NDELAY, LOG_PPP);
+ setlogmask(LOG_UPTO(LOG_INFO));
+#endif
+
+ if (gethostname(hostname, MAXNAMELEN) < 0 ) {
+ option_error("Couldn't get hostname: %m");
+ die(1);
+ }
+ hostname[MAXNAMELEN-1] = 0;
+
+ uid = getuid();
+ privileged = uid == 0;
+ sprintf(numbuf, "%d", uid);
+ script_setenv("UID", numbuf);
+
+ /*
+ * Initialize to the standard option set, then parse, in order,
+ * the system options file, the user's options file,
+ * the tty's options file, and the command line arguments.
+ */
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ (*protp->init)(0);
+
+ progname = *argv;
+
+ if (!options_from_file(_PATH_SYSOPTIONS, !privileged, 0, 1)
+ || !options_from_user())
+ exit(1);
+ scan_args(argc-1, argv+1); /* look for tty name on command line */
+ if (!options_for_tty()
+ || !parse_args(argc-1, argv+1))
+ exit(1);
+
+ /*
+ * Check that we are running as root.
+ */
+ if (geteuid() != 0) {
+ option_error("must be root to run %s, since it is not setuid-root",
+ argv[0]);
+ die(1);
+ }
+
+ if (!ppp_available()) {
+ option_error(no_ppp_msg);
+ exit(1);
+ }
+
+ /*
+ * Check that the options given are valid and consistent.
+ */
+ sys_check_options();
+ auth_check_options();
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (protp->check_options != NULL)
+ (*protp->check_options)();
+ if (demand && connector == 0) {
+ option_error("connect script required for demand-dialling\n");
+ exit(1);
+ }
+
+ script_setenv("DEVICE", devnam);
+ sprintf(numbuf, "%d", baud_rate);
+ script_setenv("SPEED", numbuf);
+
+ /*
+ * If the user has specified the default device name explicitly,
+ * pretend they hadn't.
+ */
+ if (!default_device && strcmp(devnam, default_devnam) == 0)
+ default_device = 1;
+ if (default_device)
+ nodetach = 1;
+
+ /*
+ * Initialize system-dependent stuff and magic number package.
+ */
+ sys_init();
+ magic_init();
+ if (debug)
+ setlogmask(LOG_UPTO(LOG_DEBUG));
+
+ /*
+ * Detach ourselves from the terminal, if required,
+ * and identify who is running us.
+ */
+ if (nodetach == 0)
+ detach();
+ pid = getpid();
+ p = getlogin();
+ stime = time((time_t *) NULL);
+ if (p == NULL) {
+ pw = getpwuid(uid);
+ if (pw != NULL && pw->pw_name != NULL)
+ p = pw->pw_name;
+ else
+ p = "(unknown)";
+ }
+ syslog(LOG_NOTICE, "pppd %s.%d%s started by %s, uid %d",
+ VERSION, PATCHLEVEL, IMPLEMENTATION, p, uid);
+
+ /*
+ * Compute mask of all interesting signals and install signal handlers
+ * for each. Only one signal handler may be active at a time. Therefore,
+ * all other signals should be masked when any handler is executing.
+ */
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGHUP);
+ sigaddset(&mask, SIGINT);
+ sigaddset(&mask, SIGTERM);
+ sigaddset(&mask, SIGCHLD);
+
+#define SIGNAL(s, handler) { \
+ sa.sa_handler = handler; \
+ if (sigaction(s, &sa, NULL) < 0) { \
+ syslog(LOG_ERR, "Couldn't establish signal handler (%d): %m", s); \
+ die(1); \
+ } \
+ }
+
+ sa.sa_mask = mask;
+ sa.sa_flags = 0;
+ SIGNAL(SIGHUP, hup); /* Hangup */
+ SIGNAL(SIGINT, term); /* Interrupt */
+ SIGNAL(SIGTERM, term); /* Terminate */
+ SIGNAL(SIGCHLD, chld);
+
+ SIGNAL(SIGUSR1, toggle_debug); /* Toggle debug flag */
+ SIGNAL(SIGUSR2, open_ccp); /* Reopen CCP */
+
+ /*
+ * Install a handler for other signals which would otherwise
+ * cause pppd to exit without cleaning up.
+ */
+ SIGNAL(SIGABRT, bad_signal);
+ SIGNAL(SIGALRM, bad_signal);
+ SIGNAL(SIGFPE, bad_signal);
+ SIGNAL(SIGILL, bad_signal);
+ SIGNAL(SIGPIPE, bad_signal);
+ SIGNAL(SIGQUIT, bad_signal);
+ SIGNAL(SIGSEGV, bad_signal);
+#ifdef SIGBUS
+ SIGNAL(SIGBUS, bad_signal);
+#endif
+#ifdef SIGEMT
+ SIGNAL(SIGEMT, bad_signal);
+#endif
+#ifdef SIGPOLL
+ SIGNAL(SIGPOLL, bad_signal);
+#endif
+#ifdef SIGPROF
+ SIGNAL(SIGPROF, bad_signal);
+#endif
+#ifdef SIGSYS
+ SIGNAL(SIGSYS, bad_signal);
+#endif
+#ifdef SIGTRAP
+ SIGNAL(SIGTRAP, bad_signal);
+#endif
+#ifdef SIGVTALRM
+ SIGNAL(SIGVTALRM, bad_signal);
+#endif
+#ifdef SIGXCPU
+ SIGNAL(SIGXCPU, bad_signal);
+#endif
+#ifdef SIGXFSZ
+ SIGNAL(SIGXFSZ, bad_signal);
+#endif
+
+ /*
+ * Apparently we can get a SIGPIPE when we call syslog, if
+ * syslogd has died and been restarted. Ignoring it seems
+ * be sufficient.
+ */
+ signal(SIGPIPE, SIG_IGN);
+
+ /*
+ * If we're doing dial-on-demand, set up the interface now.
+ */
+ if (demand) {
+ /*
+ * Open the loopback channel and set it up to be the ppp interface.
+ */
+ open_ppp_loopback();
+
+ syslog(LOG_INFO, "Using interface ppp%d", ifunit);
+ (void) sprintf(ifname, "ppp%d", ifunit);
+ script_setenv("IFNAME", ifname);
+
+ create_pidfile(); /* write pid to file */
+
+ /*
+ * Configure the interface and mark it up, etc.
+ */
+ demand_conf();
+ }
+
+ for (;;) {
+
+ need_holdoff = 1;
+
+ if (demand) {
+ /*
+ * Don't do anything until we see some activity.
+ */
+ phase = PHASE_DORMANT;
+ kill_link = 0;
+ demand_unblock();
+ for (;;) {
+ wait_loop_output(timeleft(&timo));
+ calltimeout();
+ if (kill_link) {
+ if (!persist)
+ die(0);
+ kill_link = 0;
+ }
+ if (get_loop_output())
+ break;
+ reap_kids();
+ }
+
+ /*
+ * Now we want to bring up the link.
+ */
+ demand_block();
+ syslog(LOG_INFO, "Starting link");
+ }
+
+ /*
+ * Lock the device if we've been asked to.
+ */
+ if (lockflag && !default_device) {
+ if (lock(devnam) < 0)
+ goto fail;
+ locked = 1;
+ }
+
+ /*
+ * Open the serial device and set it up to be the ppp interface.
+ * First we open it in non-blocking mode so we can set the
+ * various termios flags appropriately. If we aren't dialling
+ * out and we want to use the modem lines, we reopen it later
+ * in order to wait for the carrier detect signal from the modem.
+ */
+ while ((ttyfd = open(devnam, O_NONBLOCK | O_RDWR, 0)) < 0) {
+ if (errno != EINTR)
+ syslog(LOG_ERR, "Failed to open %s: %m", devnam);
+ if (!persist || errno != EINTR)
+ goto fail;
+ }
+ if ((fdflags = fcntl(ttyfd, F_GETFL)) == -1
+ || fcntl(ttyfd, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
+ syslog(LOG_WARNING,
+ "Couldn't reset non-blocking mode on device: %m");
+
+ hungup = 0;
+ kill_link = 0;
+
+ /*
+ * Do the equivalent of `mesg n' to stop broadcast messages.
+ */
+ if (fstat(ttyfd, &statbuf) < 0
+ || fchmod(ttyfd, statbuf.st_mode & ~(S_IWGRP | S_IWOTH)) < 0) {
+ syslog(LOG_WARNING,
+ "Couldn't restrict write permissions to %s: %m", devnam);
+ } else
+ tty_mode = statbuf.st_mode;
+
+ /* run connection script */
+ if (connector && connector[0]) {
+ MAINDEBUG((LOG_INFO, "Connecting with <%s>", connector));
+
+ /*
+ * Set line speed, flow control, etc.
+ * On most systems we set CLOCAL for now so that we can talk
+ * to the modem before carrier comes up. But this has the
+ * side effect that we might miss it if CD drops before we
+ * get to clear CLOCAL below. On systems where we can talk
+ * successfully to the modem with CLOCAL clear and CD down,
+ * we can clear CLOCAL at this point.
+ */
+ set_up_tty(ttyfd, 1);
+
+ /* drop dtr to hang up in case modem is off hook */
+ if (!default_device && modem) {
+ setdtr(ttyfd, FALSE);
+ sleep(1);
+ setdtr(ttyfd, TRUE);
+ }
+
+ if (device_script(connector, ttyfd, ttyfd) < 0) {
+ syslog(LOG_ERR, "Connect script failed");
+ setdtr(ttyfd, FALSE);
+ connect_attempts++;
+ goto fail;
+ }
+
+
+ syslog(LOG_INFO, "Serial connection established.");
+ sleep(1); /* give it time to set up its terminal */
+ }
+
+ connect_attempts = 0; /* we made it through ok */
+
+ /* set line speed, flow control, etc.; clear CLOCAL if modem option */
+ set_up_tty(ttyfd, 0);
+
+ /* reopen tty if necessary to wait for carrier */
+ if (connector == NULL && modem) {
+ while ((i = open(devnam, O_RDWR)) < 0) {
+ if (errno != EINTR)
+ syslog(LOG_ERR, "Failed to reopen %s: %m", devnam);
+ if (!persist || errno != EINTR ||
+ hungup || kill_link)
+ goto fail;
+ }
+ close(i);
+ }
+
+ /* run welcome script, if any */
+ if (welcomer && welcomer[0]) {
+ if (device_script(welcomer, ttyfd, ttyfd) < 0)
+ syslog(LOG_WARNING, "Welcome script failed");
+ }
+
+ /* set up the serial device as a ppp interface */
+ establish_ppp(ttyfd);
+
+ if (!demand) {
+
+ syslog(LOG_INFO, "Using interface ppp%d", ifunit);
+ (void) sprintf(ifname, "ppp%d", ifunit);
+
+ create_pidfile(); /* write pid to file */
+
+ /* write interface unit number to file */
+ for (n = strlen(devnam); n > 0 ; n--)
+ if (devnam[n] == '/') {
+ n++;
+ break;
+ }
+ (void) sprintf(iffilename, "%s%s.if", _PATH_VARRUN, &devnam[n]);
+ if ((iffile = fopen(iffilename, "w")) != NULL) {
+ fprintf(iffile, "ppp%d\n", ifunit);
+ (void) fclose(iffile);
+ } else {
+ syslog(LOG_ERR, "Failed to create if file %s: %m", iffilename);
+ iffilename[0] = 0;
+ }
+
+ script_setenv("IFNAME", ifname);
+ }
+
+ /*
+ * Start opening the connection and wait for
+ * incoming events (reply, timeout, etc.).
+ */
+ syslog(LOG_NOTICE, "Connect: %s <--> %s", ifname, devnam);
+ stime = time((time_t *) NULL);
+ lcp_lowerup(0);
+ lcp_open(0); /* Start protocol */
+ for (phase = PHASE_ESTABLISH; phase != PHASE_DEAD; ) {
+ wait_input(timeleft(&timo));
+ calltimeout();
+ get_input();
+ if (kill_link) {
+ lcp_close(0, "User request");
+ kill_link = 0;
+ }
+ if (open_ccp_flag) {
+ if (phase == PHASE_NETWORK) {
+ ccp_fsm[0].flags = OPT_RESTART; /* clears OPT_SILENT */
+ (*ccp_protent.open)(0);
+ }
+ open_ccp_flag = 0;
+ }
+ reap_kids(); /* Don't leave dead kids lying around */
+ }
+
+ /*
+ * If we may want to bring the link up again, transfer
+ * the ppp unit back to the loopback. Set the
+ * real serial device back to its normal mode of operation.
+ */
+ clean_check();
+ if (demand)
+ restore_loop();
+ disestablish_ppp(ttyfd);
+
+ /*
+ * Run disconnector script, if requested.
+ * XXX we may not be able to do this if the line has hung up!
+ */
+ if (disconnector && !hungup) {
+ set_up_tty(ttyfd, 1);
+ if (device_script(disconnector, ttyfd, ttyfd) < 0) {
+ syslog(LOG_WARNING, "disconnect script failed");
+ } else {
+ syslog(LOG_INFO, "Serial link disconnected.");
+ }
+ }
+
+ fail:
+ if (ttyfd >= 0)
+ close_tty();
+ if (locked) {
+ unlock();
+ locked = 0;
+ }
+
+ if (!demand) {
+ if (pidfilename[0] != 0
+ && unlink(pidfilename) < 0 && errno != ENOENT)
+ syslog(LOG_WARNING, "unable to delete pid file: %m");
+ pidfilename[0] = 0;
+
+ if (iffile)
+ if (unlink(iffilename) < 0 && errno != ENOENT)
+ syslog(LOG_WARNING, "unable to delete if file: %m");
+ iffilename[0] = 0;
+ }
+
+ /* limit to retries? */
+ if (max_con_attempts)
+ if (connect_attempts >= max_con_attempts)
+ break;
+
+ if (!persist)
+ die(1);
+
+ if (demand)
+ demand_discard();
+ if (holdoff > 0 && need_holdoff) {
+ phase = PHASE_HOLDOFF;
+ TIMEOUT(holdoff_end, NULL, holdoff);
+ do {
+ wait_time(timeleft(&timo));
+ calltimeout();
+ if (kill_link) {
+ if (!persist)
+ die(0);
+ kill_link = 0;
+ phase = PHASE_DORMANT; /* allow signal to end holdoff */
+ }
+ reap_kids();
+ } while (phase == PHASE_HOLDOFF);
+ }
+ }
+
+ die(0);
+ return 0;
+}
+
+/*
+ * detach - detach us from the controlling terminal.
+ */
+void
+detach()
+{
+ if (detached)
+ return;
+ if (daemon(0, 0) < 0) {
+ perror("Couldn't detach from controlling terminal");
+ die(1);
+ }
+ detached = 1;
+ pid = getpid();
+ /* update pid file if it has been written already */
+ if (pidfilename[0])
+ create_pidfile();
+}
+
+/*
+ * Create a file containing our process ID.
+ */
+static void
+create_pidfile()
+{
+ FILE *pidfile;
+
+ (void) sprintf(pidfilename, "%s%s.pid", _PATH_VARRUN, ifname);
+ if ((pidfile = fopen(pidfilename, "w")) != NULL) {
+ fprintf(pidfile, "%d\n", pid);
+ (void) fclose(pidfile);
+ } else {
+ syslog(LOG_ERR, "Failed to create pid file %s: %m", pidfilename);
+ pidfilename[0] = 0;
+ }
+}
+
+/*
+ * holdoff_end - called via a timeout when the holdoff period ends.
+ */
+static void
+holdoff_end(arg)
+ void *arg;
+{
+ phase = PHASE_DORMANT;
+}
+
+/*
+ * get_input - called when incoming data is available.
+ */
+static void
+get_input()
+{
+ int len, i;
+ u_char *p;
+ u_short protocol;
+ struct protent *protp;
+
+ p = inpacket_buf; /* point to beginning of packet buffer */
+
+ len = read_packet(inpacket_buf);
+ if (len < 0)
+ return;
+
+ if (len == 0) {
+ etime = time((time_t *) NULL);
+ minutes = (etime-stime)/60;
+ syslog(LOG_NOTICE, "Modem hangup, connected for %d minutes", (minutes >1) ? minutes : 1);
+ hungup = 1;
+ lcp_lowerdown(0); /* serial link is no longer available */
+ link_terminated(0);
+ return;
+ }
+
+ if (debug /*&& (debugflags & DBG_INPACKET)*/)
+ log_packet(p, len, "rcvd ", LOG_DEBUG);
+
+ if (len < PPP_HDRLEN) {
+ MAINDEBUG((LOG_INFO, "io(): Received short packet."));
+ return;
+ }
+
+ p += 2; /* Skip address and control */
+ GETSHORT(protocol, p);
+ len -= PPP_HDRLEN;
+
+ /*
+ * Toss all non-LCP packets unless LCP is OPEN.
+ */
+ if (protocol != PPP_LCP && lcp_fsm[0].state != OPENED) {
+ MAINDEBUG((LOG_INFO,
+ "get_input: Received non-LCP packet when LCP not open."));
+ return;
+ }
+
+ /*
+ * Until we get past the authentication phase, toss all packets
+ * except LCP, LQR and authentication packets.
+ */
+ if (phase <= PHASE_AUTHENTICATE
+ && !(protocol == PPP_LCP || protocol == PPP_LQR
+ || protocol == PPP_PAP || protocol == PPP_CHAP)) {
+ MAINDEBUG((LOG_INFO, "get_input: discarding proto 0x%x in phase %d",
+ protocol, phase));
+ return;
+ }
+
+ /*
+ * Upcall the proper protocol input routine.
+ */
+ for (i = 0; (protp = protocols[i]) != NULL; ++i) {
+ if (protp->protocol == protocol && protp->enabled_flag) {
+ (*protp->input)(0, p, len);
+ return;
+ }
+ if (protocol == (protp->protocol & ~0x8000) && protp->enabled_flag
+ && protp->datainput != NULL) {
+ (*protp->datainput)(0, p, len);
+ return;
+ }
+ }
+
+ if (debug)
+ syslog(LOG_WARNING, "Unsupported protocol (0x%x) received", protocol);
+ lcp_sprotrej(0, p - PPP_HDRLEN, len + PPP_HDRLEN);
+}
+
+
+/*
+ * quit - Clean up state and exit (with an error indication).
+ */
+void
+quit()
+{
+ die(1);
+}
+
+/*
+ * die - like quit, except we can specify an exit status.
+ */
+void
+die(status)
+ int status;
+{
+ cleanup();
+ syslog(LOG_INFO, "Exit.");
+ exit(status);
+}
+
+/*
+ * cleanup - restore anything which needs to be restored before we exit
+ */
+/* ARGSUSED */
+static void
+cleanup()
+{
+ sys_cleanup();
+
+ if (ttyfd >= 0)
+ close_tty();
+
+ if (pidfilename[0] != 0 && unlink(pidfilename) < 0 && errno != ENOENT)
+ syslog(LOG_WARNING, "unable to delete pid file: %m");
+ pidfilename[0] = 0;
+
+ if (locked)
+ unlock();
+}
+
+/*
+ * close_tty - restore the terminal device and close it.
+ */
+static void
+close_tty()
+{
+ disestablish_ppp(ttyfd);
+
+ /* drop dtr to hang up */
+ if (modem) {
+ setdtr(ttyfd, FALSE);
+ /*
+ * This sleep is in case the serial port has CLOCAL set by default,
+ * and consequently will reassert DTR when we close the device.
+ */
+ sleep(1);
+ }
+
+ restore_tty(ttyfd);
+
+ if (tty_mode != (mode_t) -1)
+ 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..d1e679b
--- /dev/null
+++ b/usr.sbin/pppd/options.c
@@ -0,0 +1,2570 @@
+/*
+ * options.c - handles option processing for PPP.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char rcsid[] = "$FreeBSD$";
+#endif
+
+#include <ctype.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <termios.h>
+#include <syslog.h>
+#include <string.h>
+#include <netdb.h>
+#include <paths.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#ifdef PPP_FILTER
+#include <pcap.h>
+#include <pcap-int.h> /* XXX: To get struct pcap */
+#endif
+
+#include "pppd.h"
+#include "pathnames.h"
+#include "patchlevel.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ipcp.h"
+#include "upap.h"
+#include "chap.h"
+#include "ccp.h"
+#ifdef CBCP_SUPPORT
+#include "cbcp.h"
+#endif
+
+#ifdef IPX_CHANGE
+#include "ipxcp.h"
+#endif /* IPX_CHANGE */
+
+#include <net/ppp_comp.h>
+
+#define FALSE 0
+#define TRUE 1
+
+#if defined(ultrix) || defined(NeXT)
+char *strdup __P((char *));
+#endif
+
+#ifndef GIDSET_TYPE
+#define GIDSET_TYPE gid_t
+#endif
+
+/*
+ * Option variables and default values.
+ */
+#ifdef PPP_FILTER
+int dflag = 0; /* Tell libpcap we want debugging */
+#endif
+int debug = 0; /* Debug flag */
+int kdebugflag = 0; /* Tell kernel to print debug messages */
+int default_device = 1; /* Using /dev/tty or equivalent */
+char devnam[MAXPATHLEN] = _PATH_TTY; /* Device name */
+int crtscts = 0; /* Use hardware flow control */
+int modem = 1; /* Use modem control lines */
+int inspeed = 0; /* Input/Output speed requested */
+u_int32_t netmask = 0; /* IP netmask to set on interface */
+int lockflag = 0; /* Create lock file to lock the serial dev */
+int nodetach = 0; /* Don't detach from controlling tty */
+char *connector = NULL; /* Script to establish physical link */
+char *disconnector = NULL; /* Script to disestablish physical link */
+char *welcomer = NULL; /* Script to run after phys link estab. */
+int max_con_attempts = 0; /* Maximum connect tries in non-demand mode */
+int maxconnect = 0; /* Maximum connect time */
+char user[MAXNAMELEN]; /* Username for PAP */
+char passwd[MAXSECRETLEN]; /* Password for PAP */
+int auth_required = 0; /* Peer is required to authenticate */
+int defaultroute = 0; /* assign default route through interface */
+int proxyarp = 0; /* Set up proxy ARP entry for peer */
+int persist = 0; /* Reopen link after it goes down */
+int uselogin = 0; /* Use /etc/passwd for checking PAP */
+int lcp_echo_interval = 0; /* Interval between LCP echo-requests */
+int lcp_echo_fails = 0; /* Tolerance to unanswered echo-requests */
+char our_name[MAXNAMELEN]; /* Our name for authentication purposes */
+char remote_name[MAXNAMELEN]; /* Peer's name for authentication */
+int explicit_remote = 0; /* User specified explicit remote name */
+int usehostname = 0; /* Use hostname for our_name */
+int disable_defaultip = 0; /* Don't use hostname for default IP adrs */
+int demand = 0; /* do dial-on-demand */
+char *ipparam = NULL; /* Extra parameter for ip up/down scripts */
+int cryptpap; /* Passwords in pap-secrets are encrypted */
+int idle_time_limit = 0; /* Disconnect if idle for this many seconds */
+int holdoff = 30; /* # seconds to pause before reconnecting */
+int refuse_pap = 0; /* Set to say we won't do PAP */
+int refuse_chap = 0; /* Set to say we won't do CHAP */
+
+#ifdef MSLANMAN
+int ms_lanman = 0; /* Nonzero if use LanMan password instead of NT */
+ /* Has meaning only with MS-CHAP challenges */
+#endif
+
+struct option_info auth_req_info;
+struct option_info connector_info;
+struct option_info disconnector_info;
+struct option_info welcomer_info;
+struct option_info devnam_info;
+#ifdef PPP_FILTER
+struct bpf_program pass_filter;/* Filter program for packets to pass */
+struct bpf_program active_filter; /* Filter program for link-active pkts */
+pcap_t pc; /* Fake struct pcap so we can compile expr */
+#endif
+
+/*
+ * Prototypes
+ */
+static int setdevname __P((char *, int));
+static int setspeed __P((char *));
+static int setdebug __P((char **));
+static int setkdebug __P((char **));
+static int setpassive __P((char **));
+static int setsilent __P((char **));
+static int noopt __P((char **));
+static int setnovj __P((char **));
+static int setnovjccomp __P((char **));
+static int setvjslots __P((char **));
+static int reqpap __P((char **));
+static int nopap __P((char **));
+#ifdef OLD_OPTIONS
+static int setupapfile __P((char **));
+#endif
+static int nochap __P((char **));
+static int reqchap __P((char **));
+static int noaccomp __P((char **));
+static int noasyncmap __P((char **));
+static int noip __P((char **));
+static int nomagicnumber __P((char **));
+static int setasyncmap __P((char **));
+static int setescape __P((char **));
+static int setmru __P((char **));
+static int setmtu __P((char **));
+#ifdef CBCP_SUPPORT
+static int setcbcp __P((char **));
+#endif
+static int nomru __P((char **));
+static int nopcomp __P((char **));
+static int setconnector __P((char **));
+static int setdisconnector __P((char **));
+static int setwelcomer __P((char **));
+static int setmaxcon __P((char **));
+static int setmaxconnect __P((char **));
+static int setdomain __P((char **));
+static int setnetmask __P((char **));
+static int setcrtscts __P((char **));
+static int setnocrtscts __P((char **));
+static int setxonxoff __P((char **));
+static int setnodetach __P((char **));
+static int setupdetach __P((char **));
+static int setmodem __P((char **));
+static int setlocal __P((char **));
+static int setlock __P((char **));
+static int setname __P((char **));
+static int setuser __P((char **));
+static int setremote __P((char **));
+static int setauth __P((char **));
+static int setnoauth __P((char **));
+static int readfile __P((char **));
+static int callfile __P((char **));
+static int setdefaultroute __P((char **));
+static int setnodefaultroute __P((char **));
+static int setproxyarp __P((char **));
+static int setnoproxyarp __P((char **));
+static int setpersist __P((char **));
+static int setnopersist __P((char **));
+static int setdologin __P((char **));
+static int setusehostname __P((char **));
+static int setnoipdflt __P((char **));
+static int setlcptimeout __P((char **));
+static int setlcpterm __P((char **));
+static int setlcpconf __P((char **));
+static int setlcpfails __P((char **));
+static int setipcptimeout __P((char **));
+static int setipcpterm __P((char **));
+static int setipcpconf __P((char **));
+static int setipcpfails __P((char **));
+static int setpaptimeout __P((char **));
+static int setpapreqs __P((char **));
+static int setpapreqtime __P((char **));
+static int setchaptimeout __P((char **));
+static int setchapchal __P((char **));
+static int setchapintv __P((char **));
+static int setipcpaccl __P((char **));
+static int setipcpaccr __P((char **));
+static int setlcpechointv __P((char **));
+static int setlcpechofails __P((char **));
+static int noccp __P((char **));
+static int setbsdcomp __P((char **));
+static int setnobsdcomp __P((char **));
+static int setdeflate __P((char **));
+static int setnodeflate __P((char **));
+static int setnodeflatedraft __P((char **));
+static int setdemand __P((char **));
+static int setpred1comp __P((char **));
+static int setnopred1comp __P((char **));
+static int setipparam __P((char **));
+static int setpapcrypt __P((char **));
+static int setidle __P((char **));
+static int setholdoff __P((char **));
+static int setdnsaddr __P((char **));
+static int resetipxproto __P((char **));
+static int setwinsaddr __P((char **));
+static int showversion __P((char **));
+static int showhelp __P((char **));
+
+#ifdef PPP_FILTER
+static int setpdebug __P((char **));
+static int setpassfilter __P((char **));
+static int setactivefilter __P((char **));
+#endif
+
+#ifdef IPX_CHANGE
+static int setipxproto __P((char **));
+static int setipxanet __P((char **));
+static int setipxalcl __P((char **));
+static int setipxarmt __P((char **));
+static int setipxnetwork __P((char **));
+static int setipxnode __P((char **));
+static int setipxrouter __P((char **));
+static int setipxname __P((char **));
+static int setipxcptimeout __P((char **));
+static int setipxcpterm __P((char **));
+static int setipxcpconf __P((char **));
+static int setipxcpfails __P((char **));
+#endif /* IPX_CHANGE */
+
+#ifdef MSLANMAN
+static int setmslanman __P((char **));
+#endif
+
+static int number_option __P((char *, u_int32_t *, int));
+static int int_option __P((char *, int *));
+static int readable __P((int fd));
+
+/*
+ * Valid arguments.
+ */
+static struct cmd {
+ char *cmd_name;
+ int num_args;
+ int (*cmd_func) __P((char **));
+} cmds[] = {
+ {"-all", 0, noopt}, /* Don't request/allow any options (useless) */
+ {"noaccomp", 0, noaccomp}, /* Disable Address/Control compression */
+ {"-ac", 0, noaccomp}, /* Disable Address/Control compress */
+ {"default-asyncmap", 0, noasyncmap}, /* Disable asyncmap negoatiation */
+ {"-am", 0, noasyncmap}, /* Disable asyncmap negotiation */
+ {"-as", 1, setasyncmap}, /* set the desired async map */
+ {"-d", 0, setdebug}, /* Increase debugging level */
+ {"nodetach", 0, setnodetach}, /* Don't detach from controlling tty */
+ {"-detach", 0, setnodetach}, /* don't fork */
+ {"updetach", 0, setupdetach}, /* Detach once an NP has come up */
+ {"noip", 0, noip}, /* Disable IP and IPCP */
+ {"-ip", 0, noip}, /* Disable IP and IPCP */
+ {"nomagic", 0, nomagicnumber}, /* Disable magic number negotiation */
+ {"-mn", 0, nomagicnumber}, /* Disable magic number negotiation */
+ {"default-mru", 0, nomru}, /* Disable MRU negotiation */
+ {"-mru", 0, nomru}, /* Disable mru negotiation */
+ {"-p", 0, setpassive}, /* Set passive mode */
+ {"nopcomp", 0, nopcomp}, /* Disable protocol field compression */
+ {"-pc", 0, nopcomp}, /* Disable protocol field compress */
+#if OLD_OPTIONS
+ {"+ua", 1, setupapfile}, /* Get PAP user and password from file */
+#endif
+ {"require-pap", 0, reqpap}, /* Require PAP authentication from peer */
+ {"+pap", 0, reqpap}, /* Require PAP auth from peer */
+ {"refuse-pap", 0, nopap}, /* Don't agree to auth to peer with PAP */
+ {"-pap", 0, nopap}, /* Don't allow UPAP authentication with peer */
+ {"require-chap", 0, reqchap}, /* Require CHAP authentication from peer */
+ {"+chap", 0, reqchap}, /* Require CHAP authentication from peer */
+ {"refuse-chap", 0, nochap}, /* Don't agree to auth to peer with CHAP */
+ {"-chap", 0, nochap}, /* Don't allow CHAP authentication with peer */
+ {"novj", 0, setnovj}, /* Disable VJ compression */
+ {"-vj", 0, setnovj}, /* disable VJ compression */
+ {"novjccomp", 0, setnovjccomp}, /* disable VJ connection-ID compression */
+ {"-vjccomp", 0, setnovjccomp}, /* disable VJ connection-ID compression */
+ {"vj-max-slots", 1, setvjslots}, /* Set maximum VJ header slots */
+ {"asyncmap", 1, setasyncmap}, /* set the desired async map */
+ {"escape", 1, setescape}, /* set chars to escape on transmission */
+ {"connect", 1, setconnector}, /* A program to set up a connection */
+ {"disconnect", 1, setdisconnector}, /* program to disconnect serial dev. */
+ {"welcome", 1, setwelcomer},/* Script to welcome client */
+ {"connect-max-attempts", 1, setmaxcon}, /* maximum # connect attempts */
+ {"maxconnect", 1, setmaxconnect}, /* specify a maximum connect time */
+ {"crtscts", 0, setcrtscts}, /* set h/w flow control */
+ {"nocrtscts", 0, setnocrtscts}, /* clear h/w flow control */
+ {"-crtscts", 0, setnocrtscts}, /* clear h/w flow control */
+ {"xonxoff", 0, setxonxoff}, /* set s/w flow control */
+ {"debug", 0, setdebug}, /* Increase debugging level */
+ {"kdebug", 1, setkdebug}, /* Enable kernel-level debugging */
+ {"domain", 1, setdomain}, /* Add given domain name to hostname*/
+ {"mru", 1, setmru}, /* Set MRU value for negotiation */
+ {"mtu", 1, setmtu}, /* Set our MTU */
+#ifdef CBCP_SUPPORT
+ {"callback", 1, setcbcp}, /* Ask for callback */
+#endif
+ {"netmask", 1, setnetmask}, /* set netmask */
+ {"passive", 0, setpassive}, /* Set passive mode */
+ {"silent", 0, setsilent}, /* Set silent mode */
+ {"modem", 0, setmodem}, /* Use modem control lines */
+ {"local", 0, setlocal}, /* Don't use modem control lines */
+ {"lock", 0, setlock}, /* Lock serial device (with lock file) */
+ {"name", 1, setname}, /* Set local name for authentication */
+ {"user", 1, setuser}, /* Set name for auth with peer */
+ {"usehostname", 0, setusehostname}, /* Must use hostname for auth. */
+ {"remotename", 1, setremote}, /* Set remote name for authentication */
+ {"auth", 0, setauth}, /* Require authentication from peer */
+ {"noauth", 0, setnoauth}, /* Don't require peer to authenticate */
+ {"file", 1, readfile}, /* Take options from a file */
+ {"call", 1, callfile}, /* Take options from a privileged file */
+ {"defaultroute", 0, setdefaultroute}, /* Add default route */
+ {"nodefaultroute", 0, setnodefaultroute}, /* disable defaultroute option */
+ {"-defaultroute", 0, setnodefaultroute}, /* disable defaultroute option */
+ {"proxyarp", 0, setproxyarp}, /* Add proxy ARP entry */
+ {"noproxyarp", 0, setnoproxyarp}, /* disable proxyarp option */
+ {"-proxyarp", 0, setnoproxyarp}, /* disable proxyarp option */
+ {"persist", 0, setpersist}, /* Keep on reopening connection after close */
+ {"nopersist", 0, setnopersist}, /* Turn off persist option */
+ {"demand", 0, setdemand}, /* Dial on demand */
+ {"login", 0, setdologin}, /* Use system password database for UPAP */
+ {"noipdefault", 0, setnoipdflt}, /* Don't use name for default IP adrs */
+ {"lcp-echo-failure", 1, setlcpechofails}, /* consecutive echo failures */
+ {"lcp-echo-interval", 1, setlcpechointv}, /* time for lcp echo events */
+ {"lcp-restart", 1, setlcptimeout}, /* Set timeout for LCP */
+ {"lcp-max-terminate", 1, setlcpterm}, /* Set max #xmits for term-reqs */
+ {"lcp-max-configure", 1, setlcpconf}, /* Set max #xmits for conf-reqs */
+ {"lcp-max-failure", 1, setlcpfails}, /* Set max #conf-naks for LCP */
+ {"ipcp-restart", 1, setipcptimeout}, /* Set timeout for IPCP */
+ {"ipcp-max-terminate", 1, setipcpterm}, /* Set max #xmits for term-reqs */
+ {"ipcp-max-configure", 1, setipcpconf}, /* Set max #xmits for conf-reqs */
+ {"ipcp-max-failure", 1, setipcpfails}, /* Set max #conf-naks for IPCP */
+ {"pap-restart", 1, setpaptimeout}, /* Set retransmit timeout for PAP */
+ {"pap-max-authreq", 1, setpapreqs}, /* Set max #xmits for auth-reqs */
+ {"pap-timeout", 1, setpapreqtime}, /* Set time limit for peer PAP auth. */
+ {"chap-restart", 1, setchaptimeout}, /* Set timeout for CHAP */
+ {"chap-max-challenge", 1, setchapchal}, /* Set max #xmits for challenge */
+ {"chap-interval", 1, setchapintv}, /* Set interval for rechallenge */
+ {"ipcp-accept-local", 0, setipcpaccl}, /* Accept peer's address for us */
+ {"ipcp-accept-remote", 0, setipcpaccr}, /* Accept peer's address for it */
+ {"noccp", 0, noccp}, /* Disable CCP negotiation */
+ {"-ccp", 0, noccp}, /* Disable CCP negotiation */
+ {"bsdcomp", 1, setbsdcomp}, /* request BSD-Compress */
+ {"nobsdcomp", 0, setnobsdcomp}, /* don't allow BSD-Compress */
+ {"-bsdcomp", 0, setnobsdcomp}, /* don't allow BSD-Compress */
+ {"deflate", 1, setdeflate}, /* request Deflate compression */
+ {"nodeflate", 0, setnodeflate}, /* don't allow Deflate compression */
+ {"-deflate", 0, setnodeflate}, /* don't allow Deflate compression */
+ {"nodeflatedraft", 0, setnodeflatedraft}, /* don't use draft deflate # */
+ {"predictor1", 0, setpred1comp}, /* request Predictor-1 */
+ {"nopredictor1", 0, setnopred1comp},/* don't allow Predictor-1 */
+ {"-predictor1", 0, setnopred1comp}, /* don't allow Predictor-1 */
+ {"ipparam", 1, setipparam}, /* set ip script parameter */
+ {"papcrypt", 0, setpapcrypt}, /* PAP passwords encrypted */
+ {"idle", 1, setidle}, /* idle time limit (seconds) */
+ {"holdoff", 1, setholdoff}, /* set holdoff time (seconds) */
+/* backwards compat hack */
+ {"dns1", 1, setdnsaddr}, /* DNS address for the peer's use */
+ {"dns2", 1, setdnsaddr}, /* DNS address for the peer's use */
+/* end compat hack */
+ {"ms-dns", 1, setdnsaddr}, /* DNS address for the peer's use */
+ {"ms-wins", 1, setwinsaddr}, /* Nameserver for SMB over TCP/IP for peer */
+ {"noipx", 0, resetipxproto}, /* Disable IPXCP (and IPX) */
+ {"-ipx", 0, resetipxproto}, /* Disable IPXCP (and IPX) */
+ {"--version", 0, showversion}, /* Show version number */
+ {"--help", 0, showhelp}, /* Show brief listing of options */
+ {"-h", 0, showhelp}, /* ditto */
+
+#ifdef PPP_FILTER
+ {"pdebug", 1, setpdebug}, /* libpcap debugging */
+ {"pass-filter", 1, setpassfilter}, /* set filter for packets to pass */
+ {"active-filter", 1, setactivefilter}, /* set filter for active pkts */
+#endif
+
+#ifdef IPX_CHANGE
+ {"ipx-network", 1, setipxnetwork}, /* IPX network number */
+ {"ipxcp-accept-network", 0, setipxanet}, /* Accept peer netowrk */
+ {"ipx-node", 1, setipxnode}, /* IPX node number */
+ {"ipxcp-accept-local", 0, setipxalcl}, /* Accept our address */
+ {"ipxcp-accept-remote", 0, setipxarmt}, /* Accept peer's address */
+ {"ipx-routing", 1, setipxrouter}, /* IPX routing proto number */
+ {"ipx-router-name", 1, setipxname}, /* IPX router name */
+ {"ipxcp-restart", 1, setipxcptimeout}, /* Set timeout for IPXCP */
+ {"ipxcp-max-terminate", 1, setipxcpterm}, /* max #xmits for term-reqs */
+ {"ipxcp-max-configure", 1, setipxcpconf}, /* max #xmits for conf-reqs */
+ {"ipxcp-max-failure", 1, setipxcpfails}, /* max #conf-naks for IPXCP */
+#if 0
+ {"ipx-compression", 1, setipxcompression}, /* IPX compression number */
+#endif
+ {"ipx", 0, setipxproto}, /* Enable IPXCP (and IPX) */
+ {"+ipx", 0, setipxproto}, /* Enable IPXCP (and IPX) */
+#endif /* IPX_CHANGE */
+
+#ifdef MSLANMAN
+ {"ms-lanman", 0, setmslanman}, /* Use LanMan psswd when using MS-CHAP */
+#endif
+
+ {NULL, 0, NULL}
+};
+
+
+#ifndef IMPLEMENTATION
+#define IMPLEMENTATION ""
+#endif
+
+static const char usage_string[] = "\
+pppd version %s patch level %d%s\n\
+Usage: %s [ options ], where options are:\n\
+ <device> Communicate over the named device\n\
+ <speed> Set the baud rate to <speed>\n\
+ <loc>:<rem> Set the local and/or remote interface IP\n\
+ addresses. Either one may be omitted.\n\
+ asyncmap <n> Set the desired async map to hex <n>\n\
+ auth Require authentication from peer\n\
+ connect <p> Invoke shell command <p> to set up the serial line\n\
+ crtscts Use hardware RTS/CTS flow control\n\
+ defaultroute Add default route through interface\n\
+ file <f> Take options from file <f>\n\
+ modem Use modem control lines\n\
+ mru <n> Set MRU value to <n> for negotiation\n\
+See pppd(8) for more options.\n\
+";
+
+static char *current_option; /* the name of the option being parsed */
+static int privileged_option; /* set iff the current option came from root */
+static char *option_source; /* string saying where the option came from */
+
+/*
+ * parse_args - parse a string of arguments from the command line.
+ */
+int
+parse_args(argc, argv)
+ int argc;
+ char **argv;
+{
+ char *arg;
+ struct cmd *cmdp;
+ int ret;
+
+ privileged_option = privileged;
+ option_source = "command line";
+ while (argc > 0) {
+ arg = *argv++;
+ --argc;
+
+ /*
+ * First see if it's a command.
+ */
+ for (cmdp = cmds; cmdp->cmd_name; cmdp++)
+ if (!strcmp(arg, cmdp->cmd_name))
+ break;
+
+ if (cmdp->cmd_name != NULL) {
+ if (argc < cmdp->num_args) {
+ option_error("too few parameters for option %s", arg);
+ return 0;
+ }
+ current_option = arg;
+ if (!(*cmdp->cmd_func)(argv))
+ return 0;
+ argc -= cmdp->num_args;
+ argv += cmdp->num_args;
+
+ } else {
+ /*
+ * Maybe a tty name, speed or IP address?
+ */
+ if ((ret = setdevname(arg, 0)) == 0
+ && (ret = setspeed(arg)) == 0
+ && (ret = setipaddr(arg)) == 0) {
+ option_error("unrecognized option '%s'", arg);
+ usage();
+ return 0;
+ }
+ if (ret < 0) /* error */
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/*
+ * scan_args - scan the command line arguments to get the tty name,
+ * if specified.
+ */
+void
+scan_args(argc, argv)
+ int argc;
+ char **argv;
+{
+ char *arg;
+ struct cmd *cmdp;
+
+ while (argc > 0) {
+ arg = *argv++;
+ --argc;
+
+ /* Skip options and their arguments */
+ for (cmdp = cmds; cmdp->cmd_name; cmdp++)
+ if (!strcmp(arg, cmdp->cmd_name))
+ break;
+
+ if (cmdp->cmd_name != NULL) {
+ argc -= cmdp->num_args;
+ argv += cmdp->num_args;
+ continue;
+ }
+
+ /* Check if it's a tty name and copy it if so */
+ (void) setdevname(arg, 1);
+ }
+}
+
+/*
+ * usage - print out a message telling how to use the program.
+ */
+void
+usage()
+{
+ if (phase == PHASE_INITIALIZE)
+ fprintf(stderr, usage_string, VERSION, PATCHLEVEL, IMPLEMENTATION,
+ progname);
+}
+
+/*
+ * showhelp - print out usage message and exit.
+ */
+static int
+showhelp(argv)
+ char **argv;
+{
+ if (phase == PHASE_INITIALIZE) {
+ usage();
+ exit(0);
+ }
+ return 0;
+}
+
+/*
+ * showversion - print out the version number and exit.
+ */
+static int
+showversion(argv)
+ char **argv;
+{
+ if (phase == PHASE_INITIALIZE) {
+ fprintf(stderr, "pppd version %s patch level %d%s\n",
+ VERSION, PATCHLEVEL, IMPLEMENTATION);
+ exit(0);
+ }
+ return 0;
+}
+
+/*
+ * options_from_file - Read a string of options from a file,
+ * and interpret them.
+ */
+int
+options_from_file(filename, must_exist, check_prot, priv)
+ char *filename;
+ int must_exist;
+ int check_prot;
+ int priv;
+{
+ FILE *f;
+ int i, newline, ret;
+ struct cmd *cmdp;
+ int oldpriv;
+ char *argv[MAXARGS];
+ char args[MAXARGS][MAXWORDLEN];
+ char cmd[MAXWORDLEN];
+
+ if ((f = fopen(filename, "r")) == NULL) {
+ if (!must_exist && errno == ENOENT)
+ return 1;
+ option_error("Can't open options file %s: %m", filename);
+ return 0;
+ }
+ if (check_prot && !readable(fileno(f))) {
+ option_error("Can't open options file %s: access denied", filename);
+ fclose(f);
+ return 0;
+ }
+
+ oldpriv = privileged_option;
+ privileged_option = priv;
+ ret = 0;
+ while (getword(f, cmd, &newline, filename)) {
+ /*
+ * First see if it's a command.
+ */
+ for (cmdp = cmds; cmdp->cmd_name; cmdp++)
+ if (!strcmp(cmd, cmdp->cmd_name))
+ break;
+
+ if (cmdp->cmd_name != NULL) {
+ for (i = 0; i < cmdp->num_args; ++i) {
+ if (!getword(f, args[i], &newline, filename)) {
+ option_error(
+ "In file %s: too few parameters for option '%s'",
+ filename, cmd);
+ goto err;
+ }
+ argv[i] = args[i];
+ }
+ current_option = cmd;
+ if (!(*cmdp->cmd_func)(argv))
+ goto err;
+
+ } else {
+ /*
+ * Maybe a tty name, speed or IP address?
+ */
+ if ((i = setdevname(cmd, 0)) == 0
+ && (i = setspeed(cmd)) == 0
+ && (i = setipaddr(cmd)) == 0) {
+ option_error("In file %s: unrecognized option '%s'",
+ filename, cmd);
+ goto err;
+ }
+ if (i < 0) /* error */
+ goto err;
+ }
+ }
+ ret = 1;
+
+err:
+ fclose(f);
+ privileged_option = oldpriv;
+ return ret;
+}
+
+/*
+ * options_from_user - See if the use has a ~/.ppprc file,
+ * and if so, interpret options from it.
+ */
+int
+options_from_user()
+{
+ char *user, *path, *file;
+ int ret;
+ struct passwd *pw;
+
+ pw = getpwuid(getuid());
+ if (pw == NULL || (user = pw->pw_dir) == NULL || user[0] == 0)
+ return 1;
+ file = _PATH_USEROPT;
+ path = malloc(strlen(user) + strlen(file) + 2);
+ if (path == NULL)
+ novm("init file name");
+ strcpy(path, user);
+ strcat(path, "/");
+ strcat(path, file);
+ ret = options_from_file(path, 0, 1, privileged);
+ free(path);
+ return ret;
+}
+
+/*
+ * options_for_tty - See if an options file exists for the serial
+ * device, and if so, interpret options from it.
+ */
+int
+options_for_tty()
+{
+ char *dev, *path, *p;
+ int ret;
+
+ dev = devnam;
+ if (strncmp(dev, _PATH_DEV, sizeof _PATH_DEV - 1) == 0)
+ dev += 5;
+ if (strcmp(dev, "tty") == 0)
+ return 1; /* don't look for /etc/ppp/options.tty */
+ path = malloc(strlen(_PATH_TTYOPT) + strlen(dev) + 1);
+ if (path == NULL)
+ novm("tty init file name");
+ strcpy(path, _PATH_TTYOPT);
+ /* Turn slashes into dots, for Solaris case (e.g. /dev/term/a) */
+ for (p = path + strlen(path); *dev != 0; ++dev)
+ *p++ = (*dev == '/'? '.': *dev);
+ *p = 0;
+ ret = options_from_file(path, 0, 0, 1);
+ free(path);
+ return ret;
+}
+
+/*
+ * option_error - print a message about an error in an option.
+ * The message is logged, and also sent to
+ * stderr if phase == PHASE_INITIALIZE.
+ */
+void
+option_error __V((char *fmt, ...))
+{
+ va_list args;
+ char buf[256];
+
+#if __STDC__
+ va_start(args, fmt);
+#else
+ char *fmt;
+ va_start(args);
+ fmt = va_arg(args, char *);
+#endif
+ vfmtmsg(buf, sizeof(buf), fmt, args);
+ va_end(args);
+ if (phase == PHASE_INITIALIZE)
+ fprintf(stderr, "%s: %s\n", progname, buf);
+ syslog(LOG_ERR, "%s", buf);
+}
+
+/*
+ * readable - check if a file is readable by the real user.
+ */
+static int
+readable(fd)
+ int fd;
+{
+ uid_t uid;
+ int ngroups, i;
+ struct stat sbuf;
+ GIDSET_TYPE groups[NGROUPS_MAX];
+
+ uid = getuid();
+ if (uid == 0)
+ return 1;
+ if (fstat(fd, &sbuf) != 0)
+ return 0;
+ if (sbuf.st_uid == uid)
+ return sbuf.st_mode & S_IRUSR;
+ if (sbuf.st_gid == getgid())
+ return sbuf.st_mode & S_IRGRP;
+ ngroups = getgroups(NGROUPS_MAX, groups);
+ for (i = 0; i < ngroups; ++i)
+ if (sbuf.st_gid == groups[i])
+ return sbuf.st_mode & S_IRGRP;
+ return sbuf.st_mode & S_IROTH;
+}
+
+/*
+ * Read a word from a file.
+ * Words are delimited by white-space or by quotes (" or ').
+ * Quotes, white-space and \ may be escaped with \.
+ * \<newline> is ignored.
+ */
+int
+getword(f, word, newlinep, filename)
+ FILE *f;
+ char *word;
+ int *newlinep;
+ char *filename;
+{
+ int c, len, escape;
+ int quoted, comment;
+ int value, digit, got, n;
+
+#define isoctal(c) ((c) >= '0' && (c) < '8')
+
+ *newlinep = 0;
+ len = 0;
+ escape = 0;
+ comment = 0;
+
+ /*
+ * First skip white-space and comments.
+ */
+ for (;;) {
+ c = getc(f);
+ if (c == EOF)
+ break;
+
+ /*
+ * A newline means the end of a comment; backslash-newline
+ * is ignored. Note that we cannot have escape && comment.
+ */
+ if (c == '\n') {
+ if (!escape) {
+ *newlinep = 1;
+ comment = 0;
+ } else
+ escape = 0;
+ continue;
+ }
+
+ /*
+ * Ignore characters other than newline in a comment.
+ */
+ if (comment)
+ continue;
+
+ /*
+ * If this character is escaped, we have a word start.
+ */
+ if (escape)
+ break;
+
+ /*
+ * If this is the escape character, look at the next character.
+ */
+ if (c == '\\') {
+ escape = 1;
+ continue;
+ }
+
+ /*
+ * If this is the start of a comment, ignore the rest of the line.
+ */
+ if (c == '#') {
+ comment = 1;
+ continue;
+ }
+
+ /*
+ * A non-whitespace character is the start of a word.
+ */
+ if (!isspace(c))
+ break;
+ }
+
+ /*
+ * Save the delimiter for quoted strings.
+ */
+ if (!escape && (c == '"' || c == '\'')) {
+ quoted = c;
+ c = getc(f);
+ } else
+ quoted = 0;
+
+ /*
+ * Process characters until the end of the word.
+ */
+ while (c != EOF) {
+ if (escape) {
+ /*
+ * This character is escaped: backslash-newline is ignored,
+ * various other characters indicate particular values
+ * as for C backslash-escapes.
+ */
+ escape = 0;
+ if (c == '\n') {
+ c = getc(f);
+ continue;
+ }
+
+ got = 0;
+ switch (c) {
+ case 'a':
+ value = '\a';
+ break;
+ case 'b':
+ value = '\b';
+ break;
+ case 'f':
+ value = '\f';
+ break;
+ case 'n':
+ value = '\n';
+ break;
+ case 'r':
+ value = '\r';
+ break;
+ case 's':
+ value = ' ';
+ break;
+ case 't':
+ value = '\t';
+ break;
+
+ default:
+ if (isoctal(c)) {
+ /*
+ * \ddd octal sequence
+ */
+ value = 0;
+ for (n = 0; n < 3 && isoctal(c); ++n) {
+ value = (value << 3) + (c & 07);
+ c = getc(f);
+ }
+ got = 1;
+ break;
+ }
+
+ if (c == 'x') {
+ /*
+ * \x<hex_string> sequence
+ */
+ value = 0;
+ c = getc(f);
+ for (n = 0; n < 2 && isxdigit(c); ++n) {
+ digit = toupper(c) - '0';
+ if (digit > 10)
+ digit += '0' + 10 - 'A';
+ value = (value << 4) + digit;
+ c = getc (f);
+ }
+ got = 1;
+ break;
+ }
+
+ /*
+ * Otherwise the character stands for itself.
+ */
+ value = c;
+ break;
+ }
+
+ /*
+ * Store the resulting character for the escape sequence.
+ */
+ if (len < MAXWORDLEN-1)
+ word[len] = value;
+ ++len;
+
+ if (!got)
+ c = getc(f);
+ continue;
+
+ }
+
+ /*
+ * Not escaped: see if we've reached the end of the word.
+ */
+ if (quoted) {
+ if (c == quoted)
+ break;
+ } else {
+ if (isspace(c) || c == '#') {
+ ungetc (c, f);
+ break;
+ }
+ }
+
+ /*
+ * Backslash starts an escape sequence.
+ */
+ if (c == '\\') {
+ escape = 1;
+ c = getc(f);
+ continue;
+ }
+
+ /*
+ * An ordinary character: store it in the word and get another.
+ */
+ if (len < MAXWORDLEN-1)
+ word[len] = c;
+ ++len;
+
+ c = getc(f);
+ }
+
+ /*
+ * End of the word: check for errors.
+ */
+ if (c == EOF) {
+ if (ferror(f)) {
+ if (errno == 0)
+ errno = EIO;
+ option_error("Error reading %s: %m", filename);
+ die(1);
+ }
+ /*
+ * If len is zero, then we didn't find a word before the
+ * end of the file.
+ */
+ if (len == 0)
+ return 0;
+ }
+
+ /*
+ * Warn if the word was too long, and append a terminating null.
+ */
+ if (len >= MAXWORDLEN) {
+ option_error("warning: word in file %s too long (%.20s...)",
+ filename, word);
+ len = MAXWORDLEN - 1;
+ }
+ word[len] = 0;
+
+ return 1;
+
+#undef isoctal
+
+}
+
+/*
+ * number_option - parse an unsigned numeric parameter for an option.
+ */
+static int
+number_option(str, valp, base)
+ char *str;
+ u_int32_t *valp;
+ int base;
+{
+ char *ptr;
+
+ *valp = strtoul(str, &ptr, base);
+ if (ptr == str) {
+ option_error("invalid numeric parameter '%s' for %s option",
+ str, current_option);
+ return 0;
+ }
+ return 1;
+}
+
+
+/*
+ * int_option - like number_option, but valp is int *,
+ * the base is assumed to be 0, and *valp is not changed
+ * if there is an error.
+ */
+static int
+int_option(str, valp)
+ char *str;
+ int *valp;
+{
+ u_int32_t v;
+
+ if (!number_option(str, &v, 0))
+ return 0;
+ *valp = (int) v;
+ return 1;
+}
+
+
+/*
+ * The following procedures parse options.
+ */
+
+/*
+ * readfile - take commands from a file.
+ */
+static int
+readfile(argv)
+ char **argv;
+{
+ return options_from_file(*argv, 1, 1, privileged_option);
+}
+
+/*
+ * callfile - take commands from /etc/ppp/peers/<name>.
+ * Name may not contain /../, start with / or ../, or end in /..
+ */
+static int
+callfile(argv)
+ char **argv;
+{
+ char *fname, *arg, *p;
+ int l, ok;
+
+ arg = *argv;
+ ok = 1;
+ if (arg[0] == '/' || arg[0] == 0)
+ ok = 0;
+ else {
+ for (p = arg; *p != 0; ) {
+ if (p[0] == '.' && p[1] == '.' && (p[2] == '/' || p[2] == 0)) {
+ ok = 0;
+ break;
+ }
+ while (*p != '/' && *p != 0)
+ ++p;
+ if (*p == '/')
+ ++p;
+ }
+ }
+ if (!ok) {
+ option_error("call option value may not contain .. or start with /");
+ return 0;
+ }
+
+ l = strlen(arg) + strlen(_PATH_PEERFILES) + 1;
+ if ((fname = (char *) malloc(l)) == NULL)
+ novm("call file name");
+ strcpy(fname, _PATH_PEERFILES);
+ strcat(fname, arg);
+
+ ok = options_from_file(fname, 1, 1, 1);
+
+ free(fname);
+ return ok;
+}
+
+
+/*
+ * setdebug - Set debug (command line argument).
+ */
+static int
+setdebug(argv)
+ char **argv;
+{
+ debug++;
+ return (1);
+}
+
+/*
+ * setkdebug - Set kernel debugging level.
+ */
+static int
+setkdebug(argv)
+ char **argv;
+{
+ return int_option(*argv, &kdebugflag);
+}
+
+#ifdef PPP_FILTER
+/*
+ * setpdebug - Set libpcap debugging level.
+ */
+static int
+setpdebug(argv)
+ char **argv;
+{
+ return int_option(*argv, &dflag);
+}
+
+/*
+ * setpassfilter - Set the pass filter for packets
+ */
+static int
+setpassfilter(argv)
+ char **argv;
+{
+ pc.linktype = DLT_PPP;
+ pc.snapshot = PPP_HDRLEN;
+
+ if (pcap_compile(&pc, &pass_filter, *argv, 1, netmask) == 0)
+ return 1;
+ option_error("error in pass-filter expression: %s\n", pcap_geterr(&pc));
+ return 0;
+}
+
+/*
+ * setactivefilter - Set the active filter for packets
+ */
+static int
+setactivefilter(argv)
+ char **argv;
+{
+ pc.linktype = DLT_PPP;
+ pc.snapshot = PPP_HDRLEN;
+
+ if (pcap_compile(&pc, &active_filter, *argv, 1, netmask) == 0)
+ return 1;
+ option_error("error in active-filter expression: %s\n", pcap_geterr(&pc));
+ return 0;
+}
+#endif
+
+/*
+ * noopt - Disable all options.
+ */
+static int
+noopt(argv)
+ char **argv;
+{
+ BZERO((char *) &lcp_wantoptions[0], sizeof (struct lcp_options));
+ BZERO((char *) &lcp_allowoptions[0], sizeof (struct lcp_options));
+ BZERO((char *) &ipcp_wantoptions[0], sizeof (struct ipcp_options));
+ BZERO((char *) &ipcp_allowoptions[0], sizeof (struct ipcp_options));
+
+#ifdef IPX_CHANGE
+ BZERO((char *) &ipxcp_wantoptions[0], sizeof (struct ipxcp_options));
+ BZERO((char *) &ipxcp_allowoptions[0], sizeof (struct ipxcp_options));
+#endif /* IPX_CHANGE */
+
+ return (1);
+}
+
+/*
+ * noaccomp - Disable Address/Control field compression negotiation.
+ */
+static int
+noaccomp(argv)
+ char **argv;
+{
+ lcp_wantoptions[0].neg_accompression = 0;
+ lcp_allowoptions[0].neg_accompression = 0;
+ return (1);
+}
+
+
+/*
+ * noasyncmap - Disable async map negotiation.
+ */
+static int
+noasyncmap(argv)
+ char **argv;
+{
+ lcp_wantoptions[0].neg_asyncmap = 0;
+ lcp_allowoptions[0].neg_asyncmap = 0;
+ return (1);
+}
+
+
+/*
+ * noip - Disable IP and IPCP.
+ */
+static int
+noip(argv)
+ char **argv;
+{
+ ipcp_protent.enabled_flag = 0;
+ return (1);
+}
+
+
+/*
+ * nomagicnumber - Disable magic number negotiation.
+ */
+static int
+nomagicnumber(argv)
+ char **argv;
+{
+ lcp_wantoptions[0].neg_magicnumber = 0;
+ lcp_allowoptions[0].neg_magicnumber = 0;
+ return (1);
+}
+
+
+/*
+ * nomru - Disable mru negotiation.
+ */
+static int
+nomru(argv)
+ char **argv;
+{
+ lcp_wantoptions[0].neg_mru = 0;
+ lcp_allowoptions[0].neg_mru = 0;
+ return (1);
+}
+
+
+/*
+ * setmru - Set MRU for negotiation.
+ */
+static int
+setmru(argv)
+ char **argv;
+{
+ u_int32_t mru;
+
+ if (!number_option(*argv, &mru, 0))
+ return 0;
+ lcp_wantoptions[0].mru = mru;
+ lcp_wantoptions[0].neg_mru = 1;
+ return (1);
+}
+
+
+/*
+ * setmru - Set the largest MTU we'll use.
+ */
+static int
+setmtu(argv)
+ char **argv;
+{
+ u_int32_t mtu;
+
+ if (!number_option(*argv, &mtu, 0))
+ return 0;
+ if (mtu < MINMRU || mtu > MAXMRU) {
+ option_error("mtu option value of %u is too %s", mtu,
+ (mtu < MINMRU? "small": "large"));
+ return 0;
+ }
+ lcp_allowoptions[0].mru = mtu;
+ return (1);
+}
+
+#ifdef CBCP_SUPPORT
+static int
+setcbcp(argv)
+ char **argv;
+{
+ lcp_wantoptions[0].neg_cbcp = 1;
+ cbcp_protent.enabled_flag = 1;
+ cbcp[0].us_number = strdup(*argv);
+ if (cbcp[0].us_number == 0)
+ novm("callback number");
+ cbcp[0].us_type |= (1 << CB_CONF_USER);
+ cbcp[0].us_type |= (1 << CB_CONF_ADMIN);
+ return (1);
+}
+#endif
+
+/*
+ * nopcomp - Disable Protocol field compression negotiation.
+ */
+static int
+nopcomp(argv)
+ char **argv;
+{
+ lcp_wantoptions[0].neg_pcompression = 0;
+ lcp_allowoptions[0].neg_pcompression = 0;
+ return (1);
+}
+
+
+/*
+ * setpassive - Set passive mode (don't give up if we time out sending
+ * LCP configure-requests).
+ */
+static int
+setpassive(argv)
+ char **argv;
+{
+ lcp_wantoptions[0].passive = 1;
+ return (1);
+}
+
+
+/*
+ * setsilent - Set silent mode (don't start sending LCP configure-requests
+ * until we get one from the peer).
+ */
+static int
+setsilent(argv)
+ char **argv;
+{
+ lcp_wantoptions[0].silent = 1;
+ return 1;
+}
+
+
+/*
+ * nopap - Disable PAP authentication with peer.
+ */
+static int
+nopap(argv)
+ char **argv;
+{
+ refuse_pap = 1;
+ return (1);
+}
+
+
+/*
+ * reqpap - Require PAP authentication from peer.
+ */
+static int
+reqpap(argv)
+ char **argv;
+{
+ lcp_wantoptions[0].neg_upap = 1;
+ setauth(NULL);
+ return 1;
+}
+
+#if OLD_OPTIONS
+/*
+ * setupapfile - specifies UPAP info for authenticating with peer.
+ */
+static int
+setupapfile(argv)
+ char **argv;
+{
+ FILE * ufile;
+ int l;
+
+ lcp_allowoptions[0].neg_upap = 1;
+
+ /* open user info file */
+ if ((ufile = fopen(*argv, "r")) == NULL) {
+ option_error("unable to open user login data file %s", *argv);
+ return 0;
+ }
+ if (!readable(fileno(ufile))) {
+ option_error("%s: access denied", *argv);
+ return 0;
+ }
+ check_access(ufile, *argv);
+
+ /* get username */
+ if (fgets(user, MAXNAMELEN - 1, ufile) == NULL
+ || fgets(passwd, MAXSECRETLEN - 1, ufile) == NULL){
+ option_error("unable to read user login data file %s", *argv);
+ return 0;
+ }
+ fclose(ufile);
+
+ /* get rid of newlines */
+ l = strlen(user);
+ if (l > 0 && user[l-1] == '\n')
+ user[l-1] = 0;
+ l = strlen(passwd);
+ if (l > 0 && passwd[l-1] == '\n')
+ passwd[l-1] = 0;
+
+ return (1);
+}
+#endif
+
+/*
+ * nochap - Disable CHAP authentication with peer.
+ */
+static int
+nochap(argv)
+ char **argv;
+{
+ refuse_chap = 1;
+ return (1);
+}
+
+
+/*
+ * reqchap - Require CHAP authentication from peer.
+ */
+static int
+reqchap(argv)
+ char **argv;
+{
+ lcp_wantoptions[0].neg_chap = 1;
+ setauth(NULL);
+ return (1);
+}
+
+
+/*
+ * setnovj - disable vj compression
+ */
+static int
+setnovj(argv)
+ char **argv;
+{
+ ipcp_wantoptions[0].neg_vj = 0;
+ ipcp_allowoptions[0].neg_vj = 0;
+ return (1);
+}
+
+
+/*
+ * setnovjccomp - disable VJ connection-ID compression
+ */
+static int
+setnovjccomp(argv)
+ char **argv;
+{
+ ipcp_wantoptions[0].cflag = 0;
+ ipcp_allowoptions[0].cflag = 0;
+ return 1;
+}
+
+
+/*
+ * setvjslots - set maximum number of connection slots for VJ compression
+ */
+static int
+setvjslots(argv)
+ char **argv;
+{
+ int value;
+
+ if (!int_option(*argv, &value))
+ return 0;
+ if (value < 2 || value > 16) {
+ option_error("vj-max-slots value must be between 2 and 16");
+ return 0;
+ }
+ ipcp_wantoptions [0].maxslotindex =
+ ipcp_allowoptions[0].maxslotindex = value - 1;
+ return 1;
+}
+
+
+/*
+ * setconnector - Set a program to connect to a serial line
+ */
+static int
+setconnector(argv)
+ char **argv;
+{
+ connector = strdup(*argv);
+ if (connector == NULL)
+ novm("connect script");
+ connector_info.priv = privileged_option;
+ connector_info.source = option_source;
+
+ return (1);
+}
+
+/*
+ * setdisconnector - Set a program to disconnect from the serial line
+ */
+static int
+setdisconnector(argv)
+ char **argv;
+{
+ disconnector = strdup(*argv);
+ if (disconnector == NULL)
+ novm("disconnect script");
+ disconnector_info.priv = privileged_option;
+ disconnector_info.source = option_source;
+
+ return (1);
+}
+
+/*
+ * setwelcomer - Set a program to welcome a client after connection
+ */
+static int
+setwelcomer(argv)
+ char **argv;
+{
+ welcomer = strdup(*argv);
+ if (welcomer == NULL)
+ novm("welcome script");
+ welcomer_info.priv = privileged_option;
+ welcomer_info.source = option_source;
+
+ return (1);
+}
+
+static int
+setmaxcon(argv)
+ char **argv;
+{
+ return int_option(*argv, &max_con_attempts);
+}
+
+/*
+ * setmaxconnect - Set the maximum connect time
+ */
+static int
+setmaxconnect(argv)
+ char **argv;
+{
+ int value;
+
+ if (!int_option(*argv, &value))
+ return 0;
+ if (value < 0) {
+ option_error("maxconnect time must be positive");
+ return 0;
+ }
+ if (maxconnect > 0 && (value == 0 || value > maxconnect)) {
+ option_error("maxconnect time cannot be increased");
+ return 0;
+ }
+ maxconnect = value;
+ return 1;
+}
+
+/*
+ * setdomain - Set domain name to append to hostname
+ */
+static int
+setdomain(argv)
+ char **argv;
+{
+ if (!privileged_option) {
+ option_error("using the domain option requires root privilege");
+ return 0;
+ }
+ gethostname(hostname, MAXNAMELEN);
+ if (**argv != 0) {
+ if (**argv != '.')
+ strncat(hostname, ".", MAXNAMELEN - strlen(hostname));
+ strncat(hostname, *argv, MAXNAMELEN - strlen(hostname));
+ }
+ hostname[MAXNAMELEN-1] = 0;
+ return (1);
+}
+
+
+/*
+ * setasyncmap - add bits to asyncmap (what we request peer to escape).
+ */
+static int
+setasyncmap(argv)
+ char **argv;
+{
+ u_int32_t asyncmap;
+
+ if (!number_option(*argv, &asyncmap, 16))
+ return 0;
+ lcp_wantoptions[0].asyncmap |= asyncmap;
+ lcp_wantoptions[0].neg_asyncmap = 1;
+ return(1);
+}
+
+
+/*
+ * setescape - add chars to the set we escape on transmission.
+ */
+static int
+setescape(argv)
+ char **argv;
+{
+ int n, ret;
+ char *p, *endp;
+
+ p = *argv;
+ ret = 1;
+ while (*p) {
+ n = strtol(p, &endp, 16);
+ if (p == endp) {
+ option_error("escape parameter contains invalid hex number '%s'",
+ p);
+ return 0;
+ }
+ p = endp;
+ if (n < 0 || (0x20 <= n && n <= 0x3F) || n == 0x5E || n > 0xFF) {
+ option_error("can't escape character 0x%x", n);
+ ret = 0;
+ } else
+ xmit_accm[0][n >> 5] |= 1 << (n & 0x1F);
+ while (*p == ',' || *p == ' ')
+ ++p;
+ }
+ return ret;
+}
+
+
+/*
+ * setspeed - Set the speed.
+ */
+static int
+setspeed(arg)
+ char *arg;
+{
+ char *ptr;
+ int spd;
+
+ spd = strtol(arg, &ptr, 0);
+ if (ptr == arg || *ptr != 0 || spd == 0)
+ return 0;
+ inspeed = spd;
+ return 1;
+}
+
+
+/*
+ * setdevname - Set the device name.
+ */
+static int
+setdevname(cp, quiet)
+ char *cp;
+ int quiet;
+{
+ struct stat statbuf;
+ char dev[MAXPATHLEN];
+
+ if (*cp == 0)
+ return 0;
+
+ if (strncmp(_PATH_DEV, cp, sizeof _PATH_DEV - 1) != 0) {
+ strcpy(dev, _PATH_DEV);
+ strncat(dev, cp, MAXPATHLEN - sizeof _PATH_DEV - 1);
+ dev[MAXPATHLEN-1] = 0;
+ cp = dev;
+ }
+
+ /*
+ * Check if there is a device by this name.
+ */
+ if (stat(cp, &statbuf) < 0) {
+ if (errno == ENOENT || quiet)
+ return 0;
+ option_error("Couldn't stat %s: %m", cp);
+ return -1;
+ }
+
+ (void) strncpy(devnam, cp, MAXPATHLEN);
+ devnam[MAXPATHLEN-1] = 0;
+ default_device = FALSE;
+ devnam_info.priv = privileged_option;
+ devnam_info.source = option_source;
+
+ return 1;
+}
+
+
+/*
+ * setipaddr - Set the IP address
+ */
+int
+setipaddr(arg)
+ char *arg;
+{
+ struct hostent *hp;
+ char *colon;
+ u_int32_t local, remote;
+ ipcp_options *wo = &ipcp_wantoptions[0];
+
+ /*
+ * IP address pair separated by ":".
+ */
+ if ((colon = strchr(arg, ':')) == NULL)
+ return 0;
+
+ /*
+ * If colon first character, then no local addr.
+ */
+ if (colon != arg) {
+ *colon = '\0';
+ if ((local = inet_addr(arg)) == -1) {
+ if ((hp = gethostbyname(arg)) == NULL) {
+ option_error("unknown host: %s", arg);
+ return -1;
+ } else {
+ local = *(u_int32_t *)hp->h_addr;
+ }
+ }
+ if (bad_ip_adrs(local)) {
+ option_error("bad local IP address %s", ip_ntoa(local));
+ return -1;
+ }
+ if (local != 0)
+ wo->ouraddr = local;
+ *colon = ':';
+ }
+
+ /*
+ * If colon last character, then no remote addr.
+ */
+ if (*++colon != '\0') {
+ if ((remote = inet_addr(colon)) == -1) {
+ if ((hp = gethostbyname(colon)) == NULL) {
+ option_error("unknown host: %s", colon);
+ return -1;
+ } else {
+ remote = *(u_int32_t *)hp->h_addr;
+ if (remote_name[0] == 0) {
+ strncpy(remote_name, colon, MAXNAMELEN);
+ remote_name[MAXNAMELEN-1] = 0;
+ }
+ }
+ }
+ if (bad_ip_adrs(remote)) {
+ option_error("bad remote IP address %s", ip_ntoa(remote));
+ return -1;
+ }
+ if (remote != 0)
+ wo->hisaddr = remote;
+ }
+
+ return 1;
+}
+
+
+/*
+ * setnoipdflt - disable setipdefault()
+ */
+static int
+setnoipdflt(argv)
+ char **argv;
+{
+ disable_defaultip = 1;
+ return 1;
+}
+
+
+/*
+ * setipcpaccl - accept peer's idea of our address
+ */
+static int
+setipcpaccl(argv)
+ char **argv;
+{
+ ipcp_wantoptions[0].accept_local = 1;
+ return 1;
+}
+
+
+/*
+ * setipcpaccr - accept peer's idea of its address
+ */
+static int
+setipcpaccr(argv)
+ char **argv;
+{
+ ipcp_wantoptions[0].accept_remote = 1;
+ return 1;
+}
+
+
+/*
+ * setnetmask - set the netmask to be used on the interface.
+ */
+static int
+setnetmask(argv)
+ char **argv;
+{
+ struct in_addr mask;
+
+ if (!inet_aton(*argv, &mask) || (netmask & ~mask.s_addr)) {
+ fprintf(stderr, "Invalid netmask %s\n", *argv);
+ return (0);
+ }
+
+ netmask = mask.s_addr;
+ return (1);
+}
+
+static int
+setcrtscts(argv)
+ char **argv;
+{
+ crtscts = 1;
+ return (1);
+}
+
+static int
+setnocrtscts(argv)
+ char **argv;
+{
+ crtscts = -1;
+ return (1);
+}
+
+static int
+setxonxoff(argv)
+ char **argv;
+{
+ lcp_wantoptions[0].asyncmap |= 0x000A0000; /* escape ^S and ^Q */
+ lcp_wantoptions[0].neg_asyncmap = 1;
+
+ crtscts = -2;
+ return (1);
+}
+
+static int
+setnodetach(argv)
+ char **argv;
+{
+ nodetach = 1;
+ return (1);
+}
+
+static int
+setupdetach(argv)
+ char **argv;
+{
+ nodetach = -1;
+ return (1);
+}
+
+static int
+setdemand(argv)
+ char **argv;
+{
+ demand = 1;
+ persist = 1;
+ return 1;
+}
+
+static int
+setmodem(argv)
+ char **argv;
+{
+ modem = 1;
+ return 1;
+}
+
+static int
+setlocal(argv)
+ char **argv;
+{
+ modem = 0;
+ return 1;
+}
+
+static int
+setlock(argv)
+ char **argv;
+{
+ lockflag = 1;
+ return 1;
+}
+
+static int
+setusehostname(argv)
+ char **argv;
+{
+ usehostname = 1;
+ return 1;
+}
+
+static int
+setname(argv)
+ char **argv;
+{
+ if (!privileged_option) {
+ option_error("using the name option requires root privilege");
+ return 0;
+ }
+ strncpy(our_name, argv[0], MAXNAMELEN);
+ our_name[MAXNAMELEN-1] = 0;
+ return 1;
+}
+
+static int
+setuser(argv)
+ char **argv;
+{
+ strncpy(user, argv[0], MAXNAMELEN);
+ user[MAXNAMELEN-1] = 0;
+ return 1;
+}
+
+static int
+setremote(argv)
+ char **argv;
+{
+ strncpy(remote_name, argv[0], MAXNAMELEN);
+ remote_name[MAXNAMELEN-1] = 0;
+ return 1;
+}
+
+static int
+setauth(argv)
+ char **argv;
+{
+ auth_required = 1;
+ if (privileged_option > auth_req_info.priv) {
+ auth_req_info.priv = privileged_option;
+ auth_req_info.source = option_source;
+ }
+ return 1;
+}
+
+static int
+setnoauth(argv)
+ char **argv;
+{
+ if (auth_required && privileged_option < auth_req_info.priv) {
+ option_error("cannot override auth option set by %s",
+ auth_req_info.source);
+ return 0;
+ }
+ auth_required = 0;
+ return 1;
+}
+
+static int
+setdefaultroute(argv)
+ char **argv;
+{
+ if (!ipcp_allowoptions[0].default_route) {
+ option_error("defaultroute option is disabled");
+ return 0;
+ }
+ ipcp_wantoptions[0].default_route = 1;
+ return 1;
+}
+
+static int
+setnodefaultroute(argv)
+ char **argv;
+{
+ ipcp_allowoptions[0].default_route = 0;
+ ipcp_wantoptions[0].default_route = 0;
+ return 1;
+}
+
+static int
+setproxyarp(argv)
+ char **argv;
+{
+ if (!ipcp_allowoptions[0].proxy_arp) {
+ option_error("proxyarp option is disabled");
+ return 0;
+ }
+ ipcp_wantoptions[0].proxy_arp = 1;
+ return 1;
+}
+
+static int
+setnoproxyarp(argv)
+ char **argv;
+{
+ ipcp_wantoptions[0].proxy_arp = 0;
+ ipcp_allowoptions[0].proxy_arp = 0;
+ return 1;
+}
+
+static int
+setpersist(argv)
+ char **argv;
+{
+ persist = 1;
+ return 1;
+}
+
+static int
+setnopersist(argv)
+ char **argv;
+{
+ persist = 0;
+ return 1;
+}
+
+static int
+setdologin(argv)
+ char **argv;
+{
+ uselogin = 1;
+ return 1;
+}
+
+/*
+ * Functions to set the echo interval for modem-less monitors
+ */
+
+static int
+setlcpechointv(argv)
+ char **argv;
+{
+ return int_option(*argv, &lcp_echo_interval);
+}
+
+static int
+setlcpechofails(argv)
+ char **argv;
+{
+ return int_option(*argv, &lcp_echo_fails);
+}
+
+/*
+ * Functions to set timeouts, max transmits, etc.
+ */
+static int
+setlcptimeout(argv)
+ char **argv;
+{
+ return int_option(*argv, &lcp_fsm[0].timeouttime);
+}
+
+static int
+setlcpterm(argv)
+ char **argv;
+{
+ return int_option(*argv, &lcp_fsm[0].maxtermtransmits);
+}
+
+static int
+setlcpconf(argv)
+ char **argv;
+{
+ return int_option(*argv, &lcp_fsm[0].maxconfreqtransmits);
+}
+
+static int
+setlcpfails(argv)
+ char **argv;
+{
+ return int_option(*argv, &lcp_fsm[0].maxnakloops);
+}
+
+static int
+setipcptimeout(argv)
+ char **argv;
+{
+ return int_option(*argv, &ipcp_fsm[0].timeouttime);
+}
+
+static int
+setipcpterm(argv)
+ char **argv;
+{
+ return int_option(*argv, &ipcp_fsm[0].maxtermtransmits);
+}
+
+static int
+setipcpconf(argv)
+ char **argv;
+{
+ return int_option(*argv, &ipcp_fsm[0].maxconfreqtransmits);
+}
+
+static int
+setipcpfails(argv)
+ char **argv;
+{
+ return int_option(*argv, &lcp_fsm[0].maxnakloops);
+}
+
+static int
+setpaptimeout(argv)
+ char **argv;
+{
+ return int_option(*argv, &upap[0].us_timeouttime);
+}
+
+static int
+setpapreqtime(argv)
+ char **argv;
+{
+ return int_option(*argv, &upap[0].us_reqtimeout);
+}
+
+static int
+setpapreqs(argv)
+ char **argv;
+{
+ return int_option(*argv, &upap[0].us_maxtransmits);
+}
+
+static int
+setchaptimeout(argv)
+ char **argv;
+{
+ return int_option(*argv, &chap[0].timeouttime);
+}
+
+static int
+setchapchal(argv)
+ char **argv;
+{
+ return int_option(*argv, &chap[0].max_transmits);
+}
+
+static int
+setchapintv(argv)
+ char **argv;
+{
+ return int_option(*argv, &chap[0].chal_interval);
+}
+
+static int
+noccp(argv)
+ char **argv;
+{
+ ccp_protent.enabled_flag = 0;
+ return 1;
+}
+
+static int
+setbsdcomp(argv)
+ char **argv;
+{
+ int rbits, abits;
+ char *str, *endp;
+
+ str = *argv;
+ abits = rbits = strtol(str, &endp, 0);
+ if (endp != str && *endp == ',') {
+ str = endp + 1;
+ abits = strtol(str, &endp, 0);
+ }
+ if (*endp != 0 || endp == str) {
+ option_error("invalid parameter '%s' for bsdcomp option", *argv);
+ return 0;
+ }
+ if ((rbits != 0 && (rbits < BSD_MIN_BITS || rbits > BSD_MAX_BITS))
+ || (abits != 0 && (abits < BSD_MIN_BITS || abits > BSD_MAX_BITS))) {
+ option_error("bsdcomp option values must be 0 or %d .. %d",
+ BSD_MIN_BITS, BSD_MAX_BITS);
+ return 0;
+ }
+ if (rbits > 0) {
+ ccp_wantoptions[0].bsd_compress = 1;
+ ccp_wantoptions[0].bsd_bits = rbits;
+ } else
+ ccp_wantoptions[0].bsd_compress = 0;
+ if (abits > 0) {
+ ccp_allowoptions[0].bsd_compress = 1;
+ ccp_allowoptions[0].bsd_bits = abits;
+ } else
+ ccp_allowoptions[0].bsd_compress = 0;
+ return 1;
+}
+
+static int
+setnobsdcomp(argv)
+ char **argv;
+{
+ ccp_wantoptions[0].bsd_compress = 0;
+ ccp_allowoptions[0].bsd_compress = 0;
+ return 1;
+}
+
+static int
+setdeflate(argv)
+ char **argv;
+{
+ int rbits, abits;
+ char *str, *endp;
+
+ str = *argv;
+ abits = rbits = strtol(str, &endp, 0);
+ if (endp != str && *endp == ',') {
+ str = endp + 1;
+ abits = strtol(str, &endp, 0);
+ }
+ if (*endp != 0 || endp == str) {
+ option_error("invalid parameter '%s' for deflate option", *argv);
+ return 0;
+ }
+ if ((rbits != 0 && (rbits < DEFLATE_MIN_SIZE || rbits > DEFLATE_MAX_SIZE))
+ || (abits != 0 && (abits < DEFLATE_MIN_SIZE
+ || abits > DEFLATE_MAX_SIZE))) {
+ option_error("deflate option values must be 0 or %d .. %d",
+ DEFLATE_MIN_SIZE, DEFLATE_MAX_SIZE);
+ return 0;
+ }
+ if (rbits > 0) {
+ ccp_wantoptions[0].deflate = 1;
+ ccp_wantoptions[0].deflate_size = rbits;
+ } else
+ ccp_wantoptions[0].deflate = 0;
+ if (abits > 0) {
+ ccp_allowoptions[0].deflate = 1;
+ ccp_allowoptions[0].deflate_size = abits;
+ } else
+ ccp_allowoptions[0].deflate = 0;
+
+ /* XXX copy over settings for switch compatibility */
+ ccp_wantoptions[0].baddeflate = ccp_wantoptions[0].deflate;
+ ccp_wantoptions[0].baddeflate_size = ccp_wantoptions[0].deflate_size;
+ ccp_allowoptions[0].baddeflate = ccp_allowoptions[0].deflate;
+ ccp_allowoptions[0].baddeflate_size = ccp_allowoptions[0].deflate_size;
+
+ return 1;
+}
+
+static int
+setnodeflate(argv)
+ char **argv;
+{
+ ccp_wantoptions[0].deflate = 0;
+ ccp_allowoptions[0].deflate = 0;
+ return 1;
+}
+
+static int
+setnodeflatedraft(argv)
+ char **argv;
+{
+ ccp_wantoptions[0].deflate_draft = 0;
+ ccp_allowoptions[0].deflate_draft = 0;
+ return 1;
+}
+
+static int
+setpred1comp(argv)
+ char **argv;
+{
+ ccp_wantoptions[0].predictor_1 = 1;
+ ccp_allowoptions[0].predictor_1 = 1;
+ return 1;
+}
+
+static int
+setnopred1comp(argv)
+ char **argv;
+{
+ ccp_wantoptions[0].predictor_1 = 0;
+ ccp_allowoptions[0].predictor_1 = 0;
+ return 1;
+}
+
+static int
+setipparam(argv)
+ char **argv;
+{
+ ipparam = strdup(*argv);
+ if (ipparam == NULL)
+ novm("ipparam string");
+
+ return 1;
+}
+
+static int
+setpapcrypt(argv)
+ char **argv;
+{
+ cryptpap = 1;
+ return 1;
+}
+
+static int
+setidle(argv)
+ char **argv;
+{
+ return int_option(*argv, &idle_time_limit);
+}
+
+static int
+setholdoff(argv)
+ char **argv;
+{
+ return int_option(*argv, &holdoff);
+}
+
+/*
+ * setdnsaddr - set the dns address(es)
+ */
+static int
+setdnsaddr(argv)
+ char **argv;
+{
+ u_int32_t dns;
+ struct hostent *hp;
+
+ dns = inet_addr(*argv);
+ if (dns == -1) {
+ if ((hp = gethostbyname(*argv)) == NULL) {
+ option_error("invalid address parameter '%s' for ms-dns option",
+ *argv);
+ return 0;
+ }
+ dns = *(u_int32_t *)hp->h_addr;
+ }
+
+ /* if there is no primary then update it. */
+ if (ipcp_allowoptions[0].dnsaddr[0] == 0)
+ ipcp_allowoptions[0].dnsaddr[0] = dns;
+
+ /* always set the secondary address value to the same value. */
+ ipcp_allowoptions[0].dnsaddr[1] = dns;
+
+ return (1);
+}
+
+/*
+ * setwinsaddr - set the wins address(es)
+ * This is primrarly used with the Samba package under UNIX or for pointing
+ * the caller to the existing WINS server on a Windows NT platform.
+ */
+static int
+setwinsaddr(argv)
+ char **argv;
+{
+ u_int32_t wins;
+ struct hostent *hp;
+
+ wins = inet_addr(*argv);
+ if (wins == -1) {
+ if ((hp = gethostbyname(*argv)) == NULL) {
+ option_error("invalid address parameter '%s' for ms-wins option",
+ *argv);
+ return 0;
+ }
+ wins = *(u_int32_t *)hp->h_addr;
+ }
+
+ /* if there is no primary then update it. */
+ if (ipcp_allowoptions[0].winsaddr[0] == 0)
+ ipcp_allowoptions[0].winsaddr[0] = wins;
+
+ /* always set the secondary address value to the same value. */
+ ipcp_allowoptions[0].winsaddr[1] = wins;
+
+ return (1);
+}
+
+#ifdef IPX_CHANGE
+static int
+setipxrouter (argv)
+ char **argv;
+{
+ ipxcp_wantoptions[0].neg_router = 1;
+ ipxcp_allowoptions[0].neg_router = 1;
+ return int_option(*argv, &ipxcp_wantoptions[0].router);
+}
+
+static int
+setipxname (argv)
+ char **argv;
+{
+ char *dest = ipxcp_wantoptions[0].name;
+ char *src = *argv;
+ int count;
+ char ch;
+
+ ipxcp_wantoptions[0].neg_name = 1;
+ ipxcp_allowoptions[0].neg_name = 1;
+ memset (dest, '\0', sizeof (ipxcp_wantoptions[0].name));
+
+ count = 0;
+ while (*src) {
+ ch = *src++;
+ if (! isalnum (ch) && ch != '_') {
+ option_error("IPX router name must be alphanumeric or _");
+ return 0;
+ }
+
+ if (count >= sizeof (ipxcp_wantoptions[0].name)) {
+ option_error("IPX router name is limited to %d characters",
+ sizeof (ipxcp_wantoptions[0].name) - 1);
+ return 0;
+ }
+
+ dest[count++] = toupper (ch);
+ }
+
+ return 1;
+}
+
+static int
+setipxcptimeout (argv)
+ char **argv;
+{
+ return int_option(*argv, &ipxcp_fsm[0].timeouttime);
+}
+
+static int
+setipxcpterm (argv)
+ char **argv;
+{
+ return int_option(*argv, &ipxcp_fsm[0].maxtermtransmits);
+}
+
+static int
+setipxcpconf (argv)
+ char **argv;
+{
+ return int_option(*argv, &ipxcp_fsm[0].maxconfreqtransmits);
+}
+
+static int
+setipxcpfails (argv)
+ char **argv;
+{
+ return int_option(*argv, &ipxcp_fsm[0].maxnakloops);
+}
+
+static int
+setipxnetwork(argv)
+ char **argv;
+{
+ u_int32_t v;
+
+ if (!number_option(*argv, &v, 16))
+ return 0;
+
+ ipxcp_wantoptions[0].our_network = (int) v;
+ ipxcp_wantoptions[0].neg_nn = 1;
+ return 1;
+}
+
+static int
+setipxanet(argv)
+ char **argv;
+{
+ ipxcp_wantoptions[0].accept_network = 1;
+ ipxcp_allowoptions[0].accept_network = 1;
+ return 1;
+}
+
+static int
+setipxalcl(argv)
+ char **argv;
+{
+ ipxcp_wantoptions[0].accept_local = 1;
+ ipxcp_allowoptions[0].accept_local = 1;
+ return 1;
+}
+
+static int
+setipxarmt(argv)
+ char **argv;
+{
+ ipxcp_wantoptions[0].accept_remote = 1;
+ ipxcp_allowoptions[0].accept_remote = 1;
+ return 1;
+}
+
+static u_char *
+setipxnodevalue(src,dst)
+u_char *src, *dst;
+{
+ int indx;
+ int item;
+
+ for (;;) {
+ if (!isxdigit (*src))
+ break;
+
+ for (indx = 0; indx < 5; ++indx) {
+ dst[indx] <<= 4;
+ dst[indx] |= (dst[indx + 1] >> 4) & 0x0F;
+ }
+
+ item = toupper (*src) - '0';
+ if (item > 9)
+ item -= 7;
+
+ dst[5] = (dst[5] << 4) | item;
+ ++src;
+ }
+ return src;
+}
+
+static int
+setipxnode(argv)
+ char **argv;
+{
+ char *end;
+
+ memset (&ipxcp_wantoptions[0].our_node[0], 0, 6);
+ memset (&ipxcp_wantoptions[0].his_node[0], 0, 6);
+
+ end = setipxnodevalue (*argv, &ipxcp_wantoptions[0].our_node[0]);
+ if (*end == ':')
+ end = setipxnodevalue (++end, &ipxcp_wantoptions[0].his_node[0]);
+
+ if (*end == '\0') {
+ ipxcp_wantoptions[0].neg_node = 1;
+ return 1;
+ }
+
+ option_error("invalid parameter '%s' for ipx-node option", *argv);
+ return 0;
+}
+
+static int
+setipxproto(argv)
+ char **argv;
+{
+ ipxcp_protent.enabled_flag = 1;
+ return 1;
+}
+
+static int
+resetipxproto(argv)
+ char **argv;
+{
+ ipxcp_protent.enabled_flag = 0;
+ return 1;
+}
+#else
+
+static int
+resetipxproto(argv)
+ char **argv;
+{
+ return 1;
+}
+#endif /* IPX_CHANGE */
+
+#ifdef MSLANMAN
+static int
+setmslanman(argv)
+ char **argv;
+{
+ ms_lanman = 1;
+ return (1);
+}
+#endif
diff --git a/usr.sbin/pppd/patchlevel.h b/usr.sbin/pppd/patchlevel.h
new file mode 100644
index 0000000..bc49c58
--- /dev/null
+++ b/usr.sbin/pppd/patchlevel.h
@@ -0,0 +1,6 @@
+/* $FreeBSD$ */
+#define PATCHLEVEL 5
+
+#define VERSION "2.3"
+#define IMPLEMENTATION ""
+#define DATE "4 May 1998"
diff --git a/usr.sbin/pppd/pathnames.h b/usr.sbin/pppd/pathnames.h
new file mode 100644
index 0000000..147ae38
--- /dev/null
+++ b/usr.sbin/pppd/pathnames.h
@@ -0,0 +1,32 @@
+/*
+ * define path names
+ *
+ * $FreeBSD$
+ */
+
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+
+#else
+#define _PATH_VARRUN "/etc/ppp/"
+#define _PATH_DEVNULL "/dev/null"
+#endif
+
+#define _PATH_UPAPFILE "/etc/ppp/pap-secrets"
+#define _PATH_CHAPFILE "/etc/ppp/chap-secrets"
+#define _PATH_SYSOPTIONS "/etc/ppp/options"
+#define _PATH_IPUP "/etc/ppp/ip-up"
+#define _PATH_IPDOWN "/etc/ppp/ip-down"
+#define _PATH_AUTHUP "/etc/ppp/auth-up"
+#define _PATH_AUTHDOWN "/etc/ppp/auth-down"
+#define _PATH_TTYOPT "/etc/ppp/options."
+#define _PATH_CONNERRS "/var/log/connect-errors"
+#define _PATH_USEROPT ".ppprc"
+#define _PATH_PEERFILES "/etc/ppp/peers/"
+#define _PATH_PPPDENY "/etc/ppp/ppp.deny"
+#define _PATH_PPPSHELLS "/etc/ppp/ppp.shells"
+
+#ifdef IPX_CHANGE
+#define _PATH_IPXUP "/etc/ppp/ipx-up"
+#define _PATH_IPXDOWN "/etc/ppp/ipx-down"
+#endif /* IPX_CHANGE */
diff --git a/usr.sbin/pppd/pppd.8 b/usr.sbin/pppd/pppd.8
new file mode 100644
index 0000000..c9a0b01
--- /dev/null
+++ b/usr.sbin/pppd/pppd.8
@@ -0,0 +1,1196 @@
+.\" manual page [] for pppd 2.3
+.\" $FreeBSD$
+.\" SH section heading
+.\" SS subsection heading
+.\" LP paragraph
+.\" IP indented paragraph
+.\" TP hanging label
+.TH PPPD 8
+.SH NAME
+pppd \- Point to Point Protocol daemon
+.SH SYNOPSIS
+.B pppd
+[
+.I tty_name
+] [
+.I speed
+] [
+.I options
+]
+.SH DESCRIPTION
+.LP
+The Point-to-Point Protocol (PPP) provides a method for transmitting
+datagrams over serial point-to-point links. PPP
+is composed of three parts: a method for encapsulating datagrams over
+serial links, an extensible Link Control Protocol (LCP), and
+a family of Network Control Protocols (NCP) for establishing
+and configuring different network-layer protocols.
+.LP
+The encapsulation scheme is provided by driver code in the kernel.
+Pppd provides the basic LCP, authentication support, and an NCP for
+establishing and configuring the Internet Protocol (IP) (called the IP
+Control Protocol, IPCP).
+.SH FREQUENTLY USED OPTIONS
+.TP
+.I <tty_name>
+Communicate over the named device. The string "/dev/" is prepended if
+necessary. If no device name is given, or if the name of the terminal
+connected to the standard input is given, pppd
+will use that terminal, and will not fork to put itself in the
+background. This option is privileged if the \fInoauth\fR option is
+used.
+.TP
+.I <speed>
+Set the baud rate to <speed> (a decimal number). On systems such as
+4.4BSD and NetBSD, any speed can be specified, providing that it is
+supported by the serial device driver. Other systems
+(e.g. SunOS, Linux) allow only a limited set of speeds.
+.TP
+.B active-filter \fIfilter-expression
+Specifies a packet filter to be applied to data packets to determine
+which packets are to be regarded as link activity, and therefore reset
+the idle timer, or cause the link to be brought up in demand-dialling
+mode. This option is useful in conjunction with the
+\fBidle\fR option if there are packets being sent or received
+regularly over the link (for example, routing information packets)
+which would otherwise prevent the link from ever appearing to be idle.
+The \fIfilter-expression\fR syntax is as described for tcpdump(1),
+except that qualifiers which are inappropriate for a PPP link, such as
+\fBether\fR and \fBarp\fR, are not permitted. Generally the filter
+expression should be enclosed in single-quotes to prevent whitespace
+in the expression from being interpreted by the shell.
+This option
+only available
+if both the kernel and pppd were compiled with PPP_FILTER defined.
+.TP
+.B asyncmap \fI<map>
+Set the async character map to <map>. This map describes which
+control characters cannot be successfully received over the serial
+line. Pppd will ask the peer to send these characters as a 2-byte
+escape sequence. The argument is a 32 bit hex number with each bit
+representing a character to escape. Bit 0 (00000001) represents the
+character 0x00; bit 31 (80000000) represents the character 0x1f or ^_.
+If multiple \fIasyncmap\fR options are given, the values are ORed
+together. If no \fIasyncmap\fR option is given, no async character
+map will be negotiated for the receive direction; the peer should then
+escape \fIall\fR control characters. To escape transmitted
+characters, use the \fIescape\fR option.
+.TP
+.B auth
+Require the peer to authenticate itself before allowing network
+packets to be sent or received.
+.TP
+.B call \fIname
+Read options from the file /etc/ppp/peers/\fIname\fR. This file may
+contain privileged options, such as \fInoauth\fR, even if pppd
+is not being run by root. The \fIname\fR string may not begin with /
+or include .. as a pathname component. The format of the options file
+is described below.
+.TP
+.B connect \fIscript
+Use the executable or shell command specified by \fIscript\fR to set
+up the serial line. This script would typically use the chat(8)
+program to dial the modem and start the remote ppp session. This
+option is privileged if the \fInoauth\fR option is used.
+.TP
+.B connect-max-attempts \fI<n>
+Attempt dial-out connection to remote system no more than specified number
+of times (default = 1). If the connection is not made, pppd will exit.
+Requires that \fBpersist\fR has been specified.
+.TP
+.B crtscts
+Use hardware flow control (i.e. RTS/CTS) to control the flow of data
+on the serial port. If neither the \fIcrtscts\fR nor the
+\fInocrtscts\fR option is given, the hardware flow control setting
+for the serial port is left unchanged.
+.TP
+.B defaultroute
+Add a default route to the system routing tables, using the peer as
+the gateway, when IPCP negotiation is successfully completed.
+This entry is removed when the PPP connection is broken. This option
+is privileged if the \fInodefaultroute\fR option has been specified.
+.TP
+.B disconnect \fIscript
+Run the executable or shell command specified by \fIscript\fR after
+pppd has terminated the link. This script could, for example, issue
+commands to the modem to cause it to hang up if hardware modem control
+signals were not available. The disconnect script is not run if the
+modem has already hung up. This option is privileged if the
+\fInoauth\fR option is used.
+.TP
+.B escape \fIxx,yy,...
+Specifies that certain characters should be escaped on transmission
+(regardless of whether the peer requests them to be escaped with its
+async control character map). The characters to be escaped are
+specified as a list of hex numbers separated by commas. Note that
+almost any character can be specified for the \fIescape\fR option,
+unlike the \fIasyncmap\fR option which only allows control characters
+to be specified. The characters which may not be escaped are those
+with hex values 0x20 - 0x3f or 0x5e.
+.TP
+.B file \fIname
+Read options from file \fIname\fR (the format is described below).
+The file must be readable by the user who has invoked pppd.
+.TP
+.B lock
+Specifies that pppd should create a UUCP-style lock file for the
+serial device to ensure exclusive access to the device.
+.TP
+.B mru \fIn
+Set the MRU [Maximum Receive Unit] value to \fIn\fR.
+Pppd
+will ask the peer to send packets of no more than \fIn\fR bytes. The
+minimum MRU value is 128. The default MRU value is 1500. A value of
+296 is recommended for slow links (40 bytes for TCP/IP header + 256
+bytes of data).
+.TP
+.B mtu \fIn
+Set the MTU [Maximum Transmit Unit] value to \fIn\fR. Unless the
+peer requests a smaller value via MRU negotiation, pppd will
+request that the kernel networking code send data packets of no more
+than \fIn\fR bytes through the PPP network interface.
+.TP
+.B passive
+Enables the "passive" option in the LCP. With this option, pppd will
+attempt to initiate a connection; if no reply is received from the
+peer, pppd will then just wait passively for a valid LCP packet from
+the peer, instead of exiting, as it would without this option.
+.SH OPTIONS
+.TP
+.I <local_IP_address>\fB:\fI<remote_IP_address>
+Set the local and/or remote interface IP addresses. Either one may be
+omitted. The IP addresses can be specified with a host name or in
+decimal dot notation (e.g. 150.234.56.78). The default local
+address is the (first) IP address of the system (unless the
+\fInoipdefault\fR
+option is given). The remote address will be obtained from the peer
+if not specified in any option. Thus, in simple cases, this option is
+not required. If a local and/or remote IP address is specified with
+this option, pppd
+will not accept a different value from the peer in the IPCP
+negotiation, unless the \fIipcp-accept-local\fR and/or
+\fIipcp-accept-remote\fR options are given, respectively.
+.TP
+.B bsdcomp \fInr,nt
+Request that the peer compress packets that it sends, using the
+BSD-Compress scheme, with a maximum code size of \fInr\fR bits, and
+agree to compress packets sent to the peer with a maximum code size of
+\fInt\fR bits. If \fInt\fR is not specified, it defaults to the value
+given for \fInr\fR. Values in the range 9 to 15 may be used for
+\fInr\fR and \fInt\fR; larger values give better compression but
+consume more kernel memory for compression dictionaries.
+Alternatively, a value of 0 for \fInr\fR or \fInt\fR disables
+compression in the corresponding direction. Use \fInobsdcomp\fR or
+\fIbsdcomp 0\fR to disable BSD-Compress compression entirely.
+.TP
+.B 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.
+.TP
+.B /usr/share/examples/pppd/
+Sample pppd configuration files.
+.SH SEE ALSO
+.IR chat(8),
+.IR ppp(8)
+.TP
+.B RFC1144
+Jacobson, V.
+\fICompressing TCP/IP headers for low-speed serial links.\fR
+February 1990.
+.TP
+.B RFC1321
+Rivest, R.
+.I The MD5 Message-Digest Algorithm.
+April 1992.
+.TP
+.B RFC1332
+McGregor, G.
+.I PPP Internet Protocol Control Protocol (IPCP).
+May 1992.
+.TP
+.B RFC1334
+Lloyd, B.; Simpson, W.A.
+.I PPP authentication protocols.
+October 1992.
+.TP
+.B RFC1661
+Simpson, W.A.
+.I The Point\-to\-Point Protocol (PPP).
+July 1994.
+.TP
+.B RFC1662
+Simpson, W.A.
+.I PPP in HDLC-like Framing.
+July 1994.
+.SH NOTES
+The following signals have the specified effect when sent to pppd.
+.TP
+.B SIGINT, SIGTERM
+These signals cause pppd to terminate the link (by closing LCP),
+restore the serial device settings, and exit.
+.TP
+.B SIGHUP
+This signal causes pppd to terminate the link, restore the serial
+device settings, and close the serial device. If the \fIpersist\fR or
+\fIdemand\fR option has been specified, pppd will try to reopen the
+serial device and start another connection (after the holdoff period).
+Otherwise pppd will exit. If this signal is received during the
+holdoff period, it causes pppd to end the holdoff period immediately.
+.TP
+.B SIGUSR1
+This signal toggles the state of the \fIdebug\fR option.
+.TP
+.B SIGUSR2
+This signal causes pppd to renegotiate compression. This can be
+useful to re-enable compression after it has been disabled as a result
+of a fatal decompression error. (Fatal decompression errors generally
+indicate a bug in one or other implementation.)
+
+.SH AUTHORS
+Paul Mackerras (Paul.Mackerras@cs.anu.edu.au), based on earlier work by
+Drew Perkins,
+Brad Clements,
+Karl Fox,
+Greg Christy,
+and
+Brad Parker.
diff --git a/usr.sbin/pppd/pppd.h b/usr.sbin/pppd/pppd.h
new file mode 100644
index 0000000..248556d
--- /dev/null
+++ b/usr.sbin/pppd/pppd.h
@@ -0,0 +1,493 @@
+/*
+ * pppd.h - PPP daemon global declarations.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * TODO:
+ */
+
+#ifndef __PPPD_H__
+#define __PPPD_H__
+
+#include <stdio.h> /* for FILE */
+#include <sys/param.h> /* for MAXPATHLEN and BSD4_4, if defined */
+#include <sys/types.h> /* for u_int32_t, if defined */
+#include <sys/time.h> /* for struct timeval */
+#include <net/ppp_defs.h>
+
+#if __STDC__
+#include <stdarg.h>
+#define __V(x) x
+#else
+#include <varargs.h>
+#define __V(x) (va_alist) va_dcl
+#define const
+#endif
+
+/*
+ * Limits.
+ */
+
+#define NUM_PPP 1 /* One PPP interface supported (per process) */
+#define MAXWORDLEN 1024 /* max length of word in file (incl null) */
+#define MAXARGS 1 /* max # args to a command */
+#define MAXNAMELEN 256 /* max length of hostname or name for auth */
+#define MAXSECRETLEN 256 /* max length of password or secret */
+
+/*
+ * Global variables.
+ */
+
+extern int hungup; /* Physical layer has disconnected */
+extern int ifunit; /* Interface unit number */
+extern char ifname[]; /* Interface name */
+extern int ttyfd; /* Serial device file descriptor */
+extern char hostname[]; /* Our hostname */
+extern u_char outpacket_buf[]; /* Buffer for outgoing packets */
+extern int phase; /* Current state of link - see values below */
+extern int baud_rate; /* Current link speed in bits/sec */
+extern char *progname; /* Name of this program */
+extern int redirect_stderr;/* Connector's stderr should go to file */
+extern char peer_authname[];/* Authenticated name of peer */
+extern int privileged; /* We were run by real-uid root */
+extern int need_holdoff; /* Need holdoff period after link terminates */
+extern char **script_env; /* Environment variables for scripts */
+extern int detached; /* Have detached from controlling tty */
+
+/*
+ * Variables set by command-line options.
+ */
+
+extern int debug; /* Debug flag */
+extern int kdebugflag; /* Tell kernel to print debug messages */
+extern int default_device; /* Using /dev/tty or equivalent */
+extern char devnam[]; /* Device name */
+extern int crtscts; /* Use hardware flow control */
+extern int modem; /* Use modem control lines */
+extern int inspeed; /* Input/Output speed requested */
+extern u_int32_t netmask; /* IP netmask to set on interface */
+extern int lockflag; /* Create lock file to lock the serial dev */
+extern int nodetach; /* Don't detach from controlling tty */
+extern char *connector; /* Script to establish physical link */
+extern char *disconnector; /* Script to disestablish physical link */
+extern char *welcomer; /* Script to welcome client after connection */
+extern int max_con_attempts;/* Maximum number of times to try dialing */
+extern int maxconnect; /* Maximum connect time (seconds) */
+extern char user[]; /* Our name for authenticating ourselves */
+extern char passwd[]; /* Password for PAP */
+extern int auth_required; /* Peer is required to authenticate */
+extern int proxyarp; /* Set up proxy ARP entry for peer */
+extern int persist; /* Reopen link after it goes down */
+extern int uselogin; /* Use /etc/passwd for checking PAP */
+extern int lcp_echo_interval; /* Interval between LCP echo-requests */
+extern int lcp_echo_fails; /* Tolerance to unanswered echo-requests */
+extern char our_name[]; /* Our name for authentication purposes */
+extern char remote_name[]; /* Peer's name for authentication */
+extern int explicit_remote;/* remote_name specified with remotename opt */
+extern int usehostname; /* Use hostname for our_name */
+extern int disable_defaultip; /* Don't use hostname for default IP adrs */
+extern int demand; /* Do dial-on-demand */
+extern char *ipparam; /* Extra parameter for ip up/down scripts */
+extern int cryptpap; /* Others' PAP passwords are encrypted */
+extern int idle_time_limit;/* Shut down link if idle for this long */
+extern int holdoff; /* Dead time before restarting */
+extern int refuse_pap; /* Don't wanna auth. ourselves with PAP */
+extern int refuse_chap; /* Don't wanna auth. ourselves with CHAP */
+#ifdef PPP_FILTER
+extern struct bpf_program pass_filter; /* Filter for pkts to pass */
+extern struct bpf_program active_filter; /* Filter for link-active pkts */
+#endif
+
+
+#ifdef MSLANMAN
+extern int ms_lanman; /* Nonzero if use LanMan password instead of NT */
+ /* Has meaning only with MS-CHAP challenges */
+#endif
+
+/*
+ * Values for phase.
+ */
+#define PHASE_DEAD 0
+#define PHASE_INITIALIZE 1
+#define PHASE_DORMANT 2
+#define PHASE_ESTABLISH 3
+#define PHASE_AUTHENTICATE 4
+#define PHASE_CALLBACK 5
+#define PHASE_NETWORK 6
+#define PHASE_TERMINATE 7
+#define PHASE_HOLDOFF 8
+
+/*
+ * The following struct gives the addresses of procedures to call
+ * for a particular protocol.
+ */
+struct protent {
+ u_short protocol; /* PPP protocol number */
+ /* Initialization procedure */
+ void (*init) __P((int unit));
+ /* Process a received packet */
+ void (*input) __P((int unit, u_char *pkt, int len));
+ /* Process a received protocol-reject */
+ void (*protrej) __P((int unit));
+ /* Lower layer has come up */
+ void (*lowerup) __P((int unit));
+ /* Lower layer has gone down */
+ void (*lowerdown) __P((int unit));
+ /* Open the protocol */
+ void (*open) __P((int unit));
+ /* Close the protocol */
+ void (*close) __P((int unit, char *reason));
+ /* Print a packet in readable form */
+ int (*printpkt) __P((u_char *pkt, int len,
+ void (*printer) __P((void *, char *, ...)),
+ void *arg));
+ /* Process a received data packet */
+ void (*datainput) __P((int unit, u_char *pkt, int len));
+ int enabled_flag; /* 0 iff protocol is disabled */
+ char *name; /* Text name of protocol */
+ /* Check requested options, assign defaults */
+ void (*check_options) __P((void));
+ /* Configure interface for demand-dial */
+ int (*demand_conf) __P((int unit));
+ /* Say whether to bring up link for this pkt */
+ int (*active_pkt) __P((u_char *pkt, int len));
+};
+
+/* Table of pointers to supported protocols */
+extern struct protent *protocols[];
+
+/*
+ * Prototypes.
+ */
+
+/* Procedures exported from main.c. */
+void detach __P((void)); /* Detach from controlling tty */
+void die __P((int)); /* Cleanup and exit */
+void quit __P((void)); /* like die(1) */
+void novm __P((char *)); /* Say we ran out of memory, and die */
+void timeout __P((void (*func)(void *), void *arg, int t));
+ /* Call func(arg) after t seconds */
+void untimeout __P((void (*func)(void *), void *arg));
+ /* Cancel call to func(arg) */
+int run_program __P((char *prog, char **args, int must_exist));
+ /* Run program prog with args in child */
+void demuxprotrej __P((int, int));
+ /* Demultiplex a Protocol-Reject */
+void format_packet __P((u_char *, int, void (*) (void *, char *, ...),
+ void *)); /* Format a packet in human-readable form */
+void log_packet __P((u_char *, int, char *, int));
+ /* Format a packet and log it with syslog */
+void print_string __P((char *, int, void (*) (void *, char *, ...),
+ void *)); /* Format a string for output */
+int fmtmsg __P((char *, int, char *, ...)); /* sprintf++ */
+int vfmtmsg __P((char *, int, char *, va_list)); /* vsprintf++ */
+void script_setenv __P((char *, char *)); /* set script env var */
+void script_unsetenv __P((char *)); /* unset script env var */
+
+/* Procedures exported from auth.c */
+void link_required __P((int)); /* we are starting to use the link */
+void link_terminated __P((int)); /* we are finished with the link */
+void link_down __P((int)); /* the LCP layer has left the Opened state */
+void link_established __P((int)); /* the link is up; authenticate now */
+void np_up __P((int, int)); /* a network protocol has come up */
+void np_down __P((int, int)); /* a network protocol has gone down */
+void np_finished __P((int, int)); /* a network protocol no longer needs link */
+void auth_peer_fail __P((int, int));
+ /* peer failed to authenticate itself */
+void auth_peer_success __P((int, int, char *, int));
+ /* peer successfully authenticated itself */
+void auth_withpeer_fail __P((int, int));
+ /* we failed to authenticate ourselves */
+void auth_withpeer_success __P((int, int));
+ /* we successfully authenticated ourselves */
+void auth_check_options __P((void));
+ /* check authentication options supplied */
+void auth_reset __P((int)); /* check what secrets we have */
+int check_passwd __P((int, char *, int, char *, int, char **, int *));
+ /* Check peer-supplied username/password */
+int get_secret __P((int, char *, char *, char *, int *, int));
+ /* get "secret" for chap */
+int auth_ip_addr __P((int, u_int32_t));
+ /* check if IP address is authorized */
+int bad_ip_adrs __P((u_int32_t));
+ /* check if IP address is unreasonable */
+void check_access __P((FILE *, char *));
+ /* check permissions on secrets file */
+
+/* Procedures exported from demand.c */
+void demand_conf __P((void)); /* config interface(s) for demand-dial */
+void demand_block __P((void)); /* set all NPs to queue up packets */
+void demand_unblock __P((void)); /* set all NPs to pass packets */
+void demand_discard __P((void)); /* set all NPs to discard packets */
+void demand_rexmit __P((int)); /* retransmit saved frames for an NP */
+int loop_chars __P((unsigned char *, int)); /* process chars from loopback */
+int loop_frame __P((unsigned char *, int)); /* process frame from loopback */
+
+/* Procedures exported from sys-*.c */
+void sys_init __P((void)); /* Do system-dependent initialization */
+void sys_cleanup __P((void)); /* Restore system state before exiting */
+void sys_check_options __P((void)); /* Check options specified */
+void sys_close __P((void)); /* Clean up in a child before execing */
+int ppp_available __P((void)); /* Test whether ppp kernel support exists */
+void open_ppp_loopback __P((void)); /* Open loopback for demand-dialling */
+void establish_ppp __P((int)); /* Turn serial port into a ppp interface */
+void restore_loop __P((void)); /* Transfer ppp unit back to loopback */
+void disestablish_ppp __P((int)); /* Restore port to normal operation */
+void clean_check __P((void)); /* Check if line was 8-bit clean */
+void set_up_tty __P((int, int)); /* Set up port's speed, parameters, etc. */
+void restore_tty __P((int)); /* Restore port's original parameters */
+void setdtr __P((int, int)); /* Raise or lower port's DTR line */
+void output __P((int, u_char *, int)); /* Output a PPP packet */
+void wait_input __P((struct timeval *));
+ /* Wait for input, with timeout */
+void wait_loop_output __P((struct timeval *));
+ /* Wait for pkt from loopback, with timeout */
+void wait_time __P((struct timeval *)); /* Wait for given length of time */
+int read_packet __P((u_char *)); /* Read PPP packet */
+int get_loop_output __P((void)); /* Read pkts from loopback */
+void ppp_send_config __P((int, int, u_int32_t, int, int));
+ /* Configure i/f transmit parameters */
+void ppp_set_xaccm __P((int, ext_accm));
+ /* Set extended transmit ACCM */
+void ppp_recv_config __P((int, int, u_int32_t, int, int));
+ /* Configure i/f receive parameters */
+int ccp_test __P((int, u_char *, int, int));
+ /* Test support for compression scheme */
+void ccp_flags_set __P((int, int, int));
+ /* Set kernel CCP state */
+int ccp_fatal_error __P((int)); /* Test for fatal decomp error in kernel */
+int get_idle_time __P((int, struct ppp_idle *));
+ /* Find out how long link has been idle */
+int sifvjcomp __P((int, int, int, int));
+ /* Configure VJ TCP header compression */
+int sifup __P((int)); /* Configure i/f up (for IP) */
+int sifnpmode __P((int u, int proto, enum NPmode mode));
+ /* Set mode for handling packets for proto */
+int sifdown __P((int)); /* Configure i/f down (for IP) */
+int sifaddr __P((int, u_int32_t, u_int32_t, u_int32_t));
+ /* Configure IP addresses for i/f */
+int cifaddr __P((int, u_int32_t, u_int32_t));
+ /* Reset i/f IP addresses */
+int sifdefaultroute __P((int, u_int32_t, u_int32_t));
+ /* Create default route through i/f */
+int cifdefaultroute __P((int, u_int32_t, u_int32_t));
+ /* Delete default route through i/f */
+int sifproxyarp __P((int, u_int32_t));
+ /* Add proxy ARP entry for peer */
+int cifproxyarp __P((int, u_int32_t));
+ /* Delete proxy ARP entry for peer */
+u_int32_t GetMask __P((u_int32_t)); /* Get appropriate netmask for address */
+int lock __P((char *)); /* Create lock file for device */
+void unlock __P((void)); /* Delete previously-created lock file */
+int daemon __P((int, int)); /* Detach us from terminal session */
+void logwtmp __P((const char *, const char *, const char *));
+ /* Write entry to wtmp file */
+int get_host_seed __P((void)); /* Get host-dependent random number seed */
+#ifdef PPP_FILTER
+int set_filters __P((struct bpf_program *pass, struct bpf_program *active));
+ /* Set filter programs in kernel */
+#endif
+
+/* Procedures exported from options.c */
+int parse_args __P((int argc, char **argv));
+ /* Parse options from arguments given */
+void usage __P((void)); /* Print a usage message */
+int options_from_file __P((char *filename, int must_exist, int check_prot,
+ int privileged));
+ /* Parse options from an options file */
+int options_from_user __P((void)); /* Parse options from user's .ppprc */
+int options_for_tty __P((void)); /* Parse options from /etc/ppp/options.tty */
+void scan_args __P((int argc, char **argv));
+ /* Look for tty name in command-line args */
+int getword __P((FILE *f, char *word, int *newlinep, char *filename));
+ /* Read a word from a file */
+void option_error __P((char *fmt, ...));
+ /* Print an error message about an option */
+int setipaddr __P((char *)); /* set IP addresses */
+
+
+/*
+ * This structure is used to store information about certain
+ * options, such as where the option value came from (/etc/ppp/options,
+ * command line, etc.) and whether it came from a privileged source.
+ */
+
+struct option_info {
+ int priv; /* was value set by sysadmin? */
+ char *source; /* where option came from */
+};
+
+extern struct option_info auth_req_info;
+extern struct option_info connector_info;
+extern struct option_info disconnector_info;
+extern struct option_info welcomer_info;
+extern struct option_info devnam_info;
+
+/*
+ * Inline versions of get/put char/short/long.
+ * Pointer is advanced; we assume that both arguments
+ * are lvalues and will already be in registers.
+ * cp MUST be u_char *.
+ */
+#define GETCHAR(c, cp) { \
+ (c) = *(cp)++; \
+}
+#define PUTCHAR(c, cp) { \
+ *(cp)++ = (u_char) (c); \
+}
+
+
+#define GETSHORT(s, cp) { \
+ (s) = *(cp)++ << 8; \
+ (s) |= *(cp)++; \
+}
+#define PUTSHORT(s, cp) { \
+ *(cp)++ = (u_char) ((s) >> 8); \
+ *(cp)++ = (u_char) (s); \
+}
+
+#define GETLONG(l, cp) { \
+ (l) = *(cp)++ << 8; \
+ (l) |= *(cp)++; (l) <<= 8; \
+ (l) |= *(cp)++; (l) <<= 8; \
+ (l) |= *(cp)++; \
+}
+#define PUTLONG(l, cp) { \
+ *(cp)++ = (u_char) ((l) >> 24); \
+ *(cp)++ = (u_char) ((l) >> 16); \
+ *(cp)++ = (u_char) ((l) >> 8); \
+ *(cp)++ = (u_char) (l); \
+}
+
+#define INCPTR(n, cp) ((cp) += (n))
+#define DECPTR(n, cp) ((cp) -= (n))
+
+#undef FALSE
+#define FALSE 0
+#undef TRUE
+#define TRUE 1
+
+/*
+ * System dependent definitions for user-level 4.3BSD UNIX implementation.
+ */
+
+#define DEMUXPROTREJ(u, p) demuxprotrej(u, p)
+
+#define TIMEOUT(r, f, t) timeout((r), (f), (t))
+#define UNTIMEOUT(r, f) untimeout((r), (f))
+
+#define BCOPY(s, d, l) memcpy(d, s, l)
+#define BZERO(s, n) memset(s, 0, n)
+#define EXIT(u) quit()
+
+#define PRINTMSG(m, l) { m[l] = '\0'; syslog(LOG_INFO, "Remote message: %s", m); }
+
+/*
+ * MAKEHEADER - Add Header fields to a packet.
+ */
+#define MAKEHEADER(p, t) { \
+ PUTCHAR(PPP_ALLSTATIONS, p); \
+ PUTCHAR(PPP_UI, p); \
+ PUTSHORT(t, p); }
+
+
+#ifdef DEBUGALL
+#define DEBUGMAIN 1
+#define DEBUGFSM 1
+#define DEBUGLCP 1
+#define DEBUGIPCP 1
+#define DEBUGUPAP 1
+#define DEBUGCHAP 1
+#endif
+
+#ifndef LOG_PPP /* we use LOG_LOCAL2 for syslog by default */
+#if defined(DEBUGMAIN) || defined(DEBUGFSM) || defined(DEBUGSYS) \
+ || defined(DEBUGLCP) || defined(DEBUGIPCP) || defined(DEBUGUPAP) \
+ || defined(DEBUGCHAP) || defined(DEBUG)
+#define LOG_PPP LOG_LOCAL2
+#else
+#define LOG_PPP LOG_DAEMON
+#endif
+#endif /* LOG_PPP */
+
+#ifdef DEBUGMAIN
+#define MAINDEBUG(x) if (debug) syslog x
+#else
+#define MAINDEBUG(x)
+#endif
+
+#ifdef DEBUGSYS
+#define SYSDEBUG(x) if (debug) syslog x
+#else
+#define SYSDEBUG(x)
+#endif
+
+#ifdef DEBUGFSM
+#define FSMDEBUG(x) if (debug) syslog x
+#else
+#define FSMDEBUG(x)
+#endif
+
+#ifdef DEBUGLCP
+#define LCPDEBUG(x) if (debug) syslog x
+#else
+#define LCPDEBUG(x)
+#endif
+
+#ifdef DEBUGIPCP
+#define IPCPDEBUG(x) if (debug) syslog x
+#else
+#define IPCPDEBUG(x)
+#endif
+
+#ifdef DEBUGUPAP
+#define UPAPDEBUG(x) if (debug) syslog x
+#else
+#define UPAPDEBUG(x)
+#endif
+
+#ifdef DEBUGCHAP
+#define CHAPDEBUG(x) if (debug) syslog x
+#else
+#define CHAPDEBUG(x)
+#endif
+
+#ifdef DEBUGIPXCP
+#define IPXCPDEBUG(x) if (debug) syslog x
+#else
+#define IPXCPDEBUG(x)
+#endif
+
+#ifndef SIGTYPE
+#if defined(sun) || defined(SYSV) || defined(POSIX_SOURCE)
+#define SIGTYPE void
+#else
+#define SIGTYPE int
+#endif /* defined(sun) || defined(SYSV) || defined(POSIX_SOURCE) */
+#endif /* SIGTYPE */
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b)? (a): (b))
+#endif
+#ifndef MAX
+#define MAX(a, b) ((a) > (b)? (a): (b))
+#endif
+
+#endif /* __PPP_H__ */
diff --git a/usr.sbin/pppd/sys-bsd.c b/usr.sbin/pppd/sys-bsd.c
new file mode 100644
index 0000000..a5dcd16
--- /dev/null
+++ b/usr.sbin/pppd/sys-bsd.c
@@ -0,0 +1,1584 @@
+/*
+ * sys-bsd.c - System-dependent procedures for setting up
+ * PPP interfaces on bsd-4.4-ish systems (including 386BSD, NetBSD, etc.)
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * Copyright (c) 1995 The Australian National University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University and The Australian National University.
+ * The names of the Universities may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char rcsid[] = "$FreeBSD$";
+#endif
+/* $NetBSD: sys-bsd.c,v 1.1.1.3 1997/09/26 18:53:04 christos Exp $ */
+
+/*
+ * TODO:
+ */
+
+#include <stdio.h>
+#include <syslog.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#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
+ + MAX(ifr->ifr_addr.sa_len, sizeof(ifr->ifr_addr)))) {
+ 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
+ + MAX(ifr->ifr_addr.sa_len, sizeof(ifr->ifr_addr)));
+ }
+
+ 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
+ + MAX(ifr->ifr_addr.sa_len, sizeof(ifr->ifr_addr)))) {
+ /*
+ * 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..627c7d0
--- /dev/null
+++ b/usr.sbin/pppd/upap.c
@@ -0,0 +1,618 @@
+/*
+ * upap.c - User/Password Authentication Protocol.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char rcsid[] = "$FreeBSD$";
+#endif
+
+/*
+ * TODO:
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <syslog.h>
+
+#include "pppd.h"
+#include "upap.h"
+
+/*
+ * Protocol entry points.
+ */
+static void upap_init __P((int));
+static void upap_lowerup __P((int));
+static void upap_lowerdown __P((int));
+static void upap_input __P((int, u_char *, int));
+static void upap_protrej __P((int));
+static int upap_printpkt __P((u_char *, int,
+ void (*) __P((void *, char *, ...)), void *));
+
+struct protent pap_protent = {
+ PPP_PAP,
+ upap_init,
+ upap_input,
+ upap_protrej,
+ upap_lowerup,
+ upap_lowerdown,
+ NULL,
+ NULL,
+ upap_printpkt,
+ NULL,
+ 1,
+ "PAP",
+ NULL,
+ NULL,
+ NULL
+};
+
+upap_state upap[NUM_PPP]; /* UPAP state; one for each unit */
+
+static void upap_timeout __P((void *));
+static void upap_reqtimeout __P((void *));
+static void upap_rauthreq __P((upap_state *, u_char *, int, int));
+static void upap_rauthack __P((upap_state *, u_char *, int, int));
+static void upap_rauthnak __P((upap_state *, u_char *, int, int));
+static void upap_sauthreq __P((upap_state *));
+static void upap_sresp __P((upap_state *, int, int, char *, int));
+
+
+/*
+ * upap_init - Initialize a UPAP unit.
+ */
+static void
+upap_init(unit)
+ int unit;
+{
+ upap_state *u = &upap[unit];
+
+ u->us_unit = unit;
+ u->us_user = NULL;
+ u->us_userlen = 0;
+ u->us_passwd = NULL;
+ u->us_passwdlen = 0;
+ u->us_clientstate = UPAPCS_INITIAL;
+ u->us_serverstate = UPAPSS_INITIAL;
+ u->us_id = 0;
+ u->us_timeouttime = UPAP_DEFTIMEOUT;
+ u->us_maxtransmits = 10;
+ u->us_reqtimeout = UPAP_DEFREQTIME;
+}
+
+
+/*
+ * upap_authwithpeer - Authenticate us with our peer (start client).
+ *
+ * Set new state and send authenticate's.
+ */
+void
+upap_authwithpeer(unit, user, password)
+ int unit;
+ char *user, *password;
+{
+ upap_state *u = &upap[unit];
+
+ /* Save the username and password we're given */
+ u->us_user = user;
+ u->us_userlen = strlen(user);
+ u->us_passwd = password;
+ u->us_passwdlen = strlen(password);
+ u->us_transmits = 0;
+
+ /* Lower layer up yet? */
+ if (u->us_clientstate == UPAPCS_INITIAL ||
+ u->us_clientstate == UPAPCS_PENDING) {
+ u->us_clientstate = UPAPCS_PENDING;
+ return;
+ }
+
+ upap_sauthreq(u); /* Start protocol */
+}
+
+
+/*
+ * upap_authpeer - Authenticate our peer (start server).
+ *
+ * Set new state.
+ */
+void
+upap_authpeer(unit)
+ int unit;
+{
+ upap_state *u = &upap[unit];
+
+ /* Lower layer up yet? */
+ if (u->us_serverstate == UPAPSS_INITIAL ||
+ u->us_serverstate == UPAPSS_PENDING) {
+ u->us_serverstate = UPAPSS_PENDING;
+ return;
+ }
+
+ u->us_serverstate = UPAPSS_LISTEN;
+ if (u->us_reqtimeout > 0)
+ TIMEOUT(upap_reqtimeout, u, u->us_reqtimeout);
+}
+
+
+/*
+ * upap_timeout - Retransmission timer for sending auth-reqs expired.
+ */
+static void
+upap_timeout(arg)
+ void *arg;
+{
+ upap_state *u = (upap_state *) arg;
+
+ if (u->us_clientstate != UPAPCS_AUTHREQ)
+ return;
+
+ if (u->us_transmits >= u->us_maxtransmits) {
+ /* give up in disgust */
+ syslog(LOG_ERR, "No response to PAP authenticate-requests");
+ u->us_clientstate = UPAPCS_BADAUTH;
+ auth_withpeer_fail(u->us_unit, PPP_PAP);
+ return;
+ }
+
+ upap_sauthreq(u); /* Send Authenticate-Request */
+}
+
+
+/*
+ * upap_reqtimeout - Give up waiting for the peer to send an auth-req.
+ */
+static void
+upap_reqtimeout(arg)
+ void *arg;
+{
+ upap_state *u = (upap_state *) arg;
+
+ if (u->us_serverstate != UPAPSS_LISTEN)
+ return; /* huh?? */
+
+ auth_peer_fail(u->us_unit, PPP_PAP);
+ u->us_serverstate = UPAPSS_BADAUTH;
+}
+
+
+/*
+ * upap_lowerup - The lower layer is up.
+ *
+ * Start authenticating if pending.
+ */
+static void
+upap_lowerup(unit)
+ int unit;
+{
+ upap_state *u = &upap[unit];
+
+ if (u->us_clientstate == UPAPCS_INITIAL)
+ u->us_clientstate = UPAPCS_CLOSED;
+ else if (u->us_clientstate == UPAPCS_PENDING) {
+ upap_sauthreq(u); /* send an auth-request */
+ }
+
+ if (u->us_serverstate == UPAPSS_INITIAL)
+ u->us_serverstate = UPAPSS_CLOSED;
+ else if (u->us_serverstate == UPAPSS_PENDING) {
+ u->us_serverstate = UPAPSS_LISTEN;
+ if (u->us_reqtimeout > 0)
+ TIMEOUT(upap_reqtimeout, u, u->us_reqtimeout);
+ }
+}
+
+
+/*
+ * upap_lowerdown - The lower layer is down.
+ *
+ * Cancel all timeouts.
+ */
+static void
+upap_lowerdown(unit)
+ int unit;
+{
+ upap_state *u = &upap[unit];
+
+ if (u->us_clientstate == UPAPCS_AUTHREQ) /* Timeout pending? */
+ UNTIMEOUT(upap_timeout, u); /* Cancel timeout */
+ if (u->us_serverstate == UPAPSS_LISTEN && u->us_reqtimeout > 0)
+ UNTIMEOUT(upap_reqtimeout, u);
+
+ u->us_clientstate = UPAPCS_INITIAL;
+ u->us_serverstate = UPAPSS_INITIAL;
+}
+
+
+/*
+ * upap_protrej - Peer doesn't speak this protocol.
+ *
+ * This shouldn't happen. In any case, pretend lower layer went down.
+ */
+static void
+upap_protrej(unit)
+ int unit;
+{
+ upap_state *u = &upap[unit];
+
+ if (u->us_clientstate == UPAPCS_AUTHREQ) {
+ syslog(LOG_ERR, "PAP authentication failed due to protocol-reject");
+ auth_withpeer_fail(unit, PPP_PAP);
+ }
+ if (u->us_serverstate == UPAPSS_LISTEN) {
+ syslog(LOG_ERR, "PAP authentication of peer failed (protocol-reject)");
+ auth_peer_fail(unit, PPP_PAP);
+ }
+ upap_lowerdown(unit);
+}
+
+
+/*
+ * upap_input - Input UPAP packet.
+ */
+static void
+upap_input(unit, inpacket, l)
+ int unit;
+ u_char *inpacket;
+ int l;
+{
+ upap_state *u = &upap[unit];
+ u_char *inp;
+ u_char code, id;
+ int len;
+
+ /*
+ * Parse header (code, id and length).
+ * If packet too short, drop it.
+ */
+ inp = inpacket;
+ if (l < UPAP_HEADERLEN) {
+ UPAPDEBUG((LOG_INFO, "pap_input: rcvd short header."));
+ return;
+ }
+ GETCHAR(code, inp);
+ GETCHAR(id, inp);
+ GETSHORT(len, inp);
+ if (len < UPAP_HEADERLEN) {
+ UPAPDEBUG((LOG_INFO, "pap_input: rcvd illegal length."));
+ return;
+ }
+ if (len > l) {
+ UPAPDEBUG((LOG_INFO, "pap_input: rcvd short packet."));
+ return;
+ }
+ len -= UPAP_HEADERLEN;
+
+ /*
+ * Action depends on code.
+ */
+ switch (code) {
+ case UPAP_AUTHREQ:
+ upap_rauthreq(u, inp, id, len);
+ break;
+
+ case UPAP_AUTHACK:
+ upap_rauthack(u, inp, id, len);
+ break;
+
+ case UPAP_AUTHNAK:
+ upap_rauthnak(u, inp, id, len);
+ break;
+
+ default: /* XXX Need code reject */
+ break;
+ }
+}
+
+
+/*
+ * upap_rauth - Receive Authenticate.
+ */
+static void
+upap_rauthreq(u, inp, id, len)
+ upap_state *u;
+ u_char *inp;
+ int id;
+ int len;
+{
+ u_char ruserlen, rpasswdlen;
+ char *ruser, *rpasswd;
+ int retcode;
+ char *msg;
+ int msglen;
+
+ UPAPDEBUG((LOG_INFO, "pap_rauth: Rcvd id %d.", id));
+
+ if (u->us_serverstate < UPAPSS_LISTEN)
+ return;
+
+ /*
+ * If we receive a duplicate authenticate-request, we are
+ * supposed to return the same status as for the first request.
+ */
+ if (u->us_serverstate == UPAPSS_OPEN) {
+ upap_sresp(u, UPAP_AUTHACK, id, "", 0); /* return auth-ack */
+ return;
+ }
+ if (u->us_serverstate == UPAPSS_BADAUTH) {
+ upap_sresp(u, UPAP_AUTHNAK, id, "", 0); /* return auth-nak */
+ return;
+ }
+
+ /*
+ * Parse user/passwd.
+ */
+ if (len < sizeof (u_char)) {
+ UPAPDEBUG((LOG_INFO, "pap_rauth: rcvd short packet."));
+ return;
+ }
+ GETCHAR(ruserlen, inp);
+ len -= sizeof (u_char) + ruserlen + sizeof (u_char);
+ if (len < 0) {
+ UPAPDEBUG((LOG_INFO, "pap_rauth: rcvd short packet."));
+ return;
+ }
+ ruser = (char *) inp;
+ INCPTR(ruserlen, inp);
+ GETCHAR(rpasswdlen, inp);
+ if (len < rpasswdlen) {
+ UPAPDEBUG((LOG_INFO, "pap_rauth: rcvd short packet."));
+ return;
+ }
+ rpasswd = (char *) inp;
+
+ /*
+ * Check the username and password given.
+ */
+ retcode = check_passwd(u->us_unit, ruser, ruserlen, rpasswd,
+ rpasswdlen, &msg, &msglen);
+ BZERO(rpasswd, rpasswdlen);
+
+ upap_sresp(u, retcode, id, msg, msglen);
+
+ if (retcode == UPAP_AUTHACK) {
+ u->us_serverstate = UPAPSS_OPEN;
+ auth_peer_success(u->us_unit, PPP_PAP, ruser, ruserlen);
+ } else {
+ u->us_serverstate = UPAPSS_BADAUTH;
+ auth_peer_fail(u->us_unit, PPP_PAP);
+ }
+
+ if (u->us_reqtimeout > 0)
+ UNTIMEOUT(upap_reqtimeout, u);
+}
+
+
+/*
+ * upap_rauthack - Receive Authenticate-Ack.
+ */
+static void
+upap_rauthack(u, inp, id, len)
+ upap_state *u;
+ u_char *inp;
+ int id;
+ int len;
+{
+ u_char msglen;
+ char *msg;
+
+ UPAPDEBUG((LOG_INFO, "pap_rauthack: Rcvd id %d.", id));
+ if (u->us_clientstate != UPAPCS_AUTHREQ) /* XXX */
+ return;
+
+ /*
+ * Parse message.
+ */
+ if (len < sizeof (u_char)) {
+ UPAPDEBUG((LOG_INFO, "pap_rauthack: rcvd short packet."));
+ return;
+ }
+ GETCHAR(msglen, inp);
+ len -= sizeof (u_char);
+ if (len < msglen) {
+ UPAPDEBUG((LOG_INFO, "pap_rauthack: rcvd short packet."));
+ return;
+ }
+ msg = (char *) inp;
+ PRINTMSG(msg, msglen);
+
+ u->us_clientstate = UPAPCS_OPEN;
+
+ auth_withpeer_success(u->us_unit, PPP_PAP);
+}
+
+
+/*
+ * upap_rauthnak - Receive Authenticate-Nakk.
+ */
+static void
+upap_rauthnak(u, inp, id, len)
+ upap_state *u;
+ u_char *inp;
+ int id;
+ int len;
+{
+ u_char msglen;
+ char *msg;
+
+ UPAPDEBUG((LOG_INFO, "pap_rauthnak: Rcvd id %d.", id));
+ if (u->us_clientstate != UPAPCS_AUTHREQ) /* XXX */
+ return;
+
+ /*
+ * Parse message.
+ */
+ if (len < sizeof (u_char)) {
+ UPAPDEBUG((LOG_INFO, "pap_rauthnak: rcvd short packet."));
+ return;
+ }
+ GETCHAR(msglen, inp);
+ len -= sizeof (u_char);
+ if (len < msglen) {
+ UPAPDEBUG((LOG_INFO, "pap_rauthnak: rcvd short packet."));
+ return;
+ }
+ msg = (char *) inp;
+ PRINTMSG(msg, msglen);
+
+ u->us_clientstate = UPAPCS_BADAUTH;
+
+ syslog(LOG_ERR, "PAP authentication failed");
+ auth_withpeer_fail(u->us_unit, PPP_PAP);
+}
+
+
+/*
+ * upap_sauthreq - Send an Authenticate-Request.
+ */
+static void
+upap_sauthreq(u)
+ upap_state *u;
+{
+ u_char *outp;
+ int outlen;
+
+ outlen = UPAP_HEADERLEN + 2 * sizeof (u_char) +
+ u->us_userlen + u->us_passwdlen;
+ outp = outpacket_buf;
+
+ MAKEHEADER(outp, PPP_PAP);
+
+ PUTCHAR(UPAP_AUTHREQ, outp);
+ PUTCHAR(++u->us_id, outp);
+ PUTSHORT(outlen, outp);
+ PUTCHAR(u->us_userlen, outp);
+ BCOPY(u->us_user, outp, u->us_userlen);
+ INCPTR(u->us_userlen, outp);
+ PUTCHAR(u->us_passwdlen, outp);
+ BCOPY(u->us_passwd, outp, u->us_passwdlen);
+
+ output(u->us_unit, outpacket_buf, outlen + PPP_HDRLEN);
+
+ UPAPDEBUG((LOG_INFO, "pap_sauth: Sent id %d.", u->us_id));
+
+ TIMEOUT(upap_timeout, u, u->us_timeouttime);
+ ++u->us_transmits;
+ u->us_clientstate = UPAPCS_AUTHREQ;
+}
+
+
+/*
+ * upap_sresp - Send a response (ack or nak).
+ */
+static void
+upap_sresp(u, code, id, msg, msglen)
+ upap_state *u;
+ u_char code, id;
+ char *msg;
+ int msglen;
+{
+ u_char *outp;
+ int outlen;
+
+ outlen = UPAP_HEADERLEN + sizeof (u_char) + msglen;
+ outp = outpacket_buf;
+ MAKEHEADER(outp, PPP_PAP);
+
+ PUTCHAR(code, outp);
+ PUTCHAR(id, outp);
+ PUTSHORT(outlen, outp);
+ PUTCHAR(msglen, outp);
+ BCOPY(msg, outp, msglen);
+ output(u->us_unit, outpacket_buf, outlen + PPP_HDRLEN);
+
+ UPAPDEBUG((LOG_INFO, "pap_sresp: Sent code %d, id %d.", code, id));
+}
+
+/*
+ * upap_printpkt - print the contents of a PAP packet.
+ */
+static char *upap_codenames[] = {
+ "AuthReq", "AuthAck", "AuthNak"
+};
+
+static int
+upap_printpkt(p, plen, printer, arg)
+ u_char *p;
+ int plen;
+ void (*printer) __P((void *, char *, ...));
+ void *arg;
+{
+ int code, id, len;
+ int mlen, ulen, wlen;
+ char *user, *pwd, *msg;
+ u_char *pstart;
+
+ if (plen < UPAP_HEADERLEN)
+ return 0;
+ pstart = p;
+ GETCHAR(code, p);
+ GETCHAR(id, p);
+ GETSHORT(len, p);
+ if (len < UPAP_HEADERLEN || len > plen)
+ return 0;
+
+ if (code >= 1 && code <= sizeof(upap_codenames) / sizeof(char *))
+ printer(arg, " %s", upap_codenames[code-1]);
+ else
+ printer(arg, " code=0x%x", code);
+ printer(arg, " id=0x%x", id);
+ len -= UPAP_HEADERLEN;
+ switch (code) {
+ case UPAP_AUTHREQ:
+ if (len < 1)
+ break;
+ ulen = p[0];
+ if (len < ulen + 2)
+ break;
+ wlen = p[ulen + 1];
+ if (len < ulen + wlen + 2)
+ break;
+ user = (char *) (p + 1);
+ pwd = (char *) (p + ulen + 2);
+ p += ulen + wlen + 2;
+ len -= ulen + wlen + 2;
+ printer(arg, " user=");
+ print_string(user, ulen, printer, arg);
+ printer(arg, " password=");
+ print_string(pwd, wlen, printer, arg);
+ break;
+ case UPAP_AUTHACK:
+ case UPAP_AUTHNAK:
+ if (len < 1)
+ break;
+ mlen = p[0];
+ if (len < mlen + 1)
+ break;
+ msg = (char *) (p + 1);
+ p += mlen + 1;
+ len -= mlen + 1;
+ printer(arg, " ");
+ print_string(msg, mlen, printer, arg);
+ break;
+ }
+
+ /* print the rest of the bytes in the packet */
+ for (; len > 0; --len) {
+ GETCHAR(code, p);
+ printer(arg, " %.2x", code);
+ }
+
+ return p - pstart;
+}
diff --git a/usr.sbin/pppd/upap.h b/usr.sbin/pppd/upap.h
new file mode 100644
index 0000000..2cfd86c
--- /dev/null
+++ b/usr.sbin/pppd/upap.h
@@ -0,0 +1,87 @@
+/*
+ * upap.h - User/Password Authentication Protocol definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Packet header = Code, id, length.
+ */
+#define UPAP_HEADERLEN (sizeof (u_char) + sizeof (u_char) + sizeof (u_short))
+
+
+/*
+ * UPAP codes.
+ */
+#define UPAP_AUTHREQ 1 /* Authenticate-Request */
+#define UPAP_AUTHACK 2 /* Authenticate-Ack */
+#define UPAP_AUTHNAK 3 /* Authenticate-Nak */
+
+
+/*
+ * Each interface is described by upap structure.
+ */
+typedef struct upap_state {
+ int us_unit; /* Interface unit number */
+ char *us_user; /* User */
+ int us_userlen; /* User length */
+ char *us_passwd; /* Password */
+ int us_passwdlen; /* Password length */
+ int us_clientstate; /* Client state */
+ int us_serverstate; /* Server state */
+ u_char us_id; /* Current id */
+ int us_timeouttime; /* Timeout (seconds) for auth-req retrans. */
+ int us_transmits; /* Number of auth-reqs sent */
+ int us_maxtransmits; /* Maximum number of auth-reqs to send */
+ int us_reqtimeout; /* Time to wait for auth-req from peer */
+} upap_state;
+
+
+/*
+ * Client states.
+ */
+#define UPAPCS_INITIAL 0 /* Connection down */
+#define UPAPCS_CLOSED 1 /* Connection up, haven't requested auth */
+#define UPAPCS_PENDING 2 /* Connection down, have requested auth */
+#define UPAPCS_AUTHREQ 3 /* We've sent an Authenticate-Request */
+#define UPAPCS_OPEN 4 /* We've received an Ack */
+#define UPAPCS_BADAUTH 5 /* We've received a Nak */
+
+/*
+ * Server states.
+ */
+#define UPAPSS_INITIAL 0 /* Connection down */
+#define UPAPSS_CLOSED 1 /* Connection up, haven't requested auth */
+#define UPAPSS_PENDING 2 /* Connection down, have requested auth */
+#define UPAPSS_LISTEN 3 /* Listening for an Authenticate */
+#define UPAPSS_OPEN 4 /* We've sent an Ack */
+#define UPAPSS_BADAUTH 5 /* We've sent a Nak */
+
+
+/*
+ * Timeouts.
+ */
+#define UPAP_DEFTIMEOUT 3 /* Timeout (seconds) for retransmitting req */
+#define UPAP_DEFREQTIME 30 /* Time to wait for auth-req from peer */
+
+extern upap_state upap[];
+
+void upap_authwithpeer __P((int, char *, char *));
+void upap_authpeer __P((int));
+
+extern struct protent pap_protent;
diff --git a/usr.sbin/pppstats/Makefile b/usr.sbin/pppstats/Makefile
new file mode 100644
index 0000000..0aab78b
--- /dev/null
+++ b/usr.sbin/pppstats/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+#as per policies in handbook
+MAINTAINER= peter@freebsd.org
+
+PROG= pppstats
+MAN= pppstats.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pppstats/pppstats.8 b/usr.sbin/pppstats/pppstats.8
new file mode 100644
index 0000000..5b28ea0
--- /dev/null
+++ b/usr.sbin/pppstats/pppstats.8
@@ -0,0 +1,218 @@
+.\" @(#) $FreeBSD$
+.TH PPPSTATS 8 "26 June 1995"
+.SH NAME
+pppstats \- print PPP statistics
+.SH SYNOPSIS
+.B pppstats
+[
+.B -a
+] [
+.B -v
+] [
+.B -r
+] [
+.B -z
+] [
+.B -c
+.I <count>
+] [
+.B -w
+.I <secs>
+] [
+.I interface
+]
+.ti 12
+.SH DESCRIPTION
+The
+.B pppstats
+utility reports PPP-related statistics at regular intervals for the
+specified PPP interface. If the interface is unspecified, it will
+default to ppp0.
+The display is split horizontally
+into input and output sections containing columns of statistics
+describing the properties and volume of packets received and
+transmitted by the interface.
+.PP
+The options are as follows:
+.TP
+.B -a
+Display absolute values rather than deltas. With this option, all
+reports show statistics for the time since the link was initiated.
+Without this option, the second and subsequent reports show statistics
+for the time since the last report.
+.TP
+.B -c \fIcount
+Repeat the display
+.I count
+times. If this option is not specified, the default repeat count is 1
+if the
+.B -w
+option is not specified, otherwise infinity.
+.TP
+.B -r
+Display additional statistics summarizing the compression ratio
+achieved by the packet compression algorithm in use.
+.TP
+.B -v
+Display additional statistics relating to the performance of the Van
+Jacobson TCP header compression algorithm.
+.TP
+.B -w \fIwait
+Pause
+.I wait
+seconds between each display. If this option is not specified, the
+default interval is 5 seconds.
+.TP
+.B -z
+Instead of the standard display, show statistics indicating the
+performance of the packet compression algorithm in use.
+.PP
+The following fields are printed on the input side when the
+.B -z
+option is not used:
+.TP
+.B IN
+The total number of bytes received by this interface.
+.TP
+.B PACK
+The total number of packets received by this interface.
+.TP
+.B VJCOMP
+The number of header-compressed TCP packets received by this interface.
+.TP
+.B VJUNC
+The number of header-uncompressed TCP packets received by this
+interface. Not reported when the
+.B -r
+option is specified.
+.TP
+.B VJERR
+The number of corrupted or bogus header-compressed TCP packets
+received by this interface. Not reported when the
+.B -r
+option is specified.
+.TP
+.B VJTOSS
+The number of VJ header-compressed TCP packets dropped on reception by
+this interface because of preceding errors. Only reported when the
+.B -v
+option is specified.
+.TP
+.B NON-VJ
+The total number of non-TCP packets received by this interface.
+Only
+reported when the
+.B -v
+option is specified.
+.TP
+.B RATIO
+The compression ratio achieved for received packets by the
+packet compression scheme in use, defined as the uncompressed size
+divided by the compressed size.
+Only reported when the
+.B -r
+option is specified.
+.TP
+.B UBYTE
+The total number of bytes received, after decompression of compressed
+packets. Only reported when the
+.B -r
+option is specified.
+.PP
+The following fields are printed on the output side:
+.TP
+.B OUT
+The total number of bytes transmitted from this interface.
+.TP
+.B PACK
+The total number of packets transmitted from this interface.
+.TP
+.B VJCOMP
+The number of TCP packets transmitted from this interface with
+VJ-compressed TCP headers.
+.TP
+.B VJUNC
+The number of TCP packets transmitted from this interface with
+VJ-uncompressed TCP headers.
+Not reported when the
+.B -r
+option is specified.
+.TP
+.B NON-VJ
+The total number of non-TCP packets transmitted from this interface.
+Not reported when the
+.B -r
+option is specified.
+.TP
+.B VJSRCH
+The number of searches for the cached header entry for a VJ header
+compressed TCP packet. Only reported when the
+.B -v
+option is specified.
+.TP
+.B VJMISS
+The number of failed searches for the cached header entry for a
+VJ header compressed TCP packet. Only reported when the
+.B -v
+option is specified.
+.TP
+.B RATIO
+The compression ratio achieved for transmitted packets by the
+packet compression scheme in use, defined as the size
+before compression divided by the compressed size.
+Only reported when the
+.B -r
+option is specified.
+.TP
+.B UBYTE
+The total number of bytes to be transmitted, before packet compression
+is applied. Only reported when the
+.B -r
+option is specified.
+.PP
+When the
+.B -z
+option is specified,
+.B pppstats
+instead displays the following fields, relating to the packet
+compression algorithm currently in use. If packet compression is not
+in use, these fields will all display zeroes. The fields displayed on
+the input side are:
+.TP
+.B COMPRESSED BYTE
+The number of bytes of compressed packets received.
+.TP
+.B COMPRESSED PACK
+The number of compressed packets received.
+.TP
+.B INCOMPRESSIBLE BYTE
+The number of bytes of incompressible packets (that is, those which
+were transmitted in uncompressed form) received.
+.TP
+.B INCOMPRESSIBLE PACK
+The number of incompressible packets received.
+.TP
+.B COMP RATIO
+The recent compression ratio for incoming packets, defined as the
+uncompressed size divided by the compressed size (including both
+compressible and incompressible packets).
+.PP
+The fields displayed on the output side are:
+.TP
+.B COMPRESSED BYTE
+The number of bytes of compressed packets transmitted.
+.TP
+.B COMPRESSED PACK
+The number of compressed packets transmitted.
+.TP
+.B INCOMPRESSIBLE BYTE
+The number of bytes of incompressible packets transmitted (that is,
+those which were transmitted in uncompressed form).
+.TP
+.B INCOMPRESSIBLE PACK
+The number of incompressible packets transmitted.
+.TP
+.B COMP RATIO
+The recent compression ratio for outgoing packets.
+.SH SEE ALSO
+pppd(8)
diff --git a/usr.sbin/pppstats/pppstats.c b/usr.sbin/pppstats/pppstats.c
new file mode 100644
index 0000000..d3b7b3d
--- /dev/null
+++ b/usr.sbin/pppstats/pppstats.c
@@ -0,0 +1,519 @@
+/*
+ * print PPP statistics:
+ * pppstats [-a|-d] [-v|-r|-z] [-c count] [-w wait] [interface]
+ *
+ * -a Show absolute values rather than deltas
+ * -d Show data rate (kB/s) rather than bytes
+ * -v Show more stats for VJ TCP header compression
+ * -r Show compression ratio
+ * -z Show compression statistics instead of default display
+ *
+ * History:
+ * perkins@cps.msu.edu: Added compression statistics and alternate
+ * display. 11/94
+ * Brad Parker (brad@cayman.com) 6/92
+ *
+ * from the original "slstats" by Van Jacobson
+ *
+ * Copyright (c) 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char rcsid[] = "$FreeBSD$";
+#endif
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <net/ppp_defs.h>
+
+#ifndef STREAMS
+#include <sys/socket.h> /* *BSD, Linux, NeXT, Ultrix etc. */
+#include <net/if.h>
+#include <net/if_ppp.h>
+
+#else /* STREAMS */
+#include <sys/stropts.h> /* SVR4, Solaris 2, SunOS 4, OSF/1, etc. */
+#include <net/pppio.h>
+
+#endif /* STREAMS */
+
+int vflag, rflag, zflag; /* select type of display */
+int aflag; /* print absolute values, not deltas */
+int dflag; /* print data rates, not bytes */
+int interval, count;
+int infinite;
+int unit;
+int s; /* socket or /dev/ppp file descriptor */
+int signalled; /* set if alarm goes off "early" */
+char *progname;
+char *interface;
+
+#if defined(SUNOS4) || defined(ULTRIX) || defined(NeXT)
+extern int optind;
+extern char *optarg;
+#endif
+
+static void usage __P((void));
+static void catchalarm __P((int));
+static void get_ppp_stats __P((struct ppp_stats *));
+static void get_ppp_cstats __P((struct ppp_comp_stats *));
+static void intpr __P((void));
+
+int main __P((int, char *argv[]));
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: %s [-a|-d] [-v|-r|-z] [-c count] [-w wait] [interface]\n",
+ progname);
+ exit(1);
+}
+
+/*
+ * Called if an interval expires before intpr has completed a loop.
+ * Sets a flag to not wait for the alarm.
+ */
+static void
+catchalarm(arg)
+ int arg;
+{
+ signalled = 1;
+}
+
+
+#ifndef STREAMS
+static void
+get_ppp_stats(curp)
+ struct ppp_stats *curp;
+{
+ struct ifpppstatsreq req;
+
+ memset (&req, 0, sizeof (req));
+
+#ifdef _linux_
+ req.stats_ptr = (caddr_t) &req.stats;
+#undef ifr_name
+#define ifr_name ifr__name
+#endif
+
+ strncpy(req.ifr_name, interface, sizeof(req.ifr_name));
+ if (ioctl(s, SIOCGPPPSTATS, &req) < 0) {
+ fprintf(stderr, "%s: ", progname);
+ if (errno == ENOTTY)
+ fprintf(stderr, "kernel support missing\n");
+ else
+ perror("couldn't get PPP statistics");
+ exit(1);
+ }
+ *curp = req.stats;
+}
+
+static void
+get_ppp_cstats(csp)
+ struct ppp_comp_stats *csp;
+{
+ struct ifpppcstatsreq creq;
+
+ memset (&creq, 0, sizeof (creq));
+
+#ifdef _linux_
+ creq.stats_ptr = (caddr_t) &creq.stats;
+#undef ifr_name
+#define ifr_name ifr__name
+#endif
+
+ strncpy(creq.ifr_name, interface, sizeof(creq.ifr_name));
+ if (ioctl(s, SIOCGPPPCSTATS, &creq) < 0) {
+ fprintf(stderr, "%s: ", progname);
+ if (errno == ENOTTY) {
+ fprintf(stderr, "no kernel compression support\n");
+ if (zflag)
+ exit(1);
+ rflag = 0;
+ } else {
+ perror("couldn't get PPP compression stats");
+ exit(1);
+ }
+ }
+
+#ifdef _linux_
+ if (creq.stats.c.bytes_out == 0)
+ creq.stats.c.ratio = 0.0;
+ else
+ creq.stats.c.ratio = (double) creq.stats.c.in_count /
+ (double) creq.stats.c.bytes_out;
+
+ if (creq.stats.d.bytes_out == 0)
+ creq.stats.d.ratio = 0.0;
+ else
+ creq.stats.d.ratio = (double) creq.stats.d.in_count /
+ (double) creq.stats.d.bytes_out;
+#endif
+
+ *csp = creq.stats;
+}
+
+#else /* STREAMS */
+
+int
+strioctl(fd, cmd, ptr, ilen, olen)
+ int fd, cmd, ilen, olen;
+ char *ptr;
+{
+ struct strioctl str;
+
+ str.ic_cmd = cmd;
+ str.ic_timout = 0;
+ str.ic_len = ilen;
+ str.ic_dp = ptr;
+ if (ioctl(fd, I_STR, &str) == -1)
+ return -1;
+ if (str.ic_len != olen)
+ fprintf(stderr, "strioctl: expected %d bytes, got %d for cmd %x\n",
+ olen, str.ic_len, cmd);
+ return 0;
+}
+
+static void
+get_ppp_stats(curp)
+ struct ppp_stats *curp;
+{
+ if (strioctl(s, PPPIO_GETSTAT, curp, 0, sizeof(*curp)) < 0) {
+ fprintf(stderr, "%s: ", progname);
+ if (errno == EINVAL)
+ fprintf(stderr, "kernel support missing\n");
+ else
+ perror("couldn't get PPP statistics");
+ exit(1);
+ }
+}
+
+static void
+get_ppp_cstats(csp)
+ struct ppp_comp_stats *csp;
+{
+ if (strioctl(s, PPPIO_GETCSTAT, csp, 0, sizeof(*csp)) < 0) {
+ fprintf(stderr, "%s: ", progname);
+ if (errno == ENOTTY) {
+ fprintf(stderr, "no kernel compression support\n");
+ if (zflag)
+ exit(1);
+ rflag = 0;
+ } else {
+ perror("couldn't get PPP compression statistics");
+ exit(1);
+ }
+ }
+}
+
+#endif /* STREAMS */
+
+#define MAX0(a) ((int)(a) > 0? (a): 0)
+#define V(offset) MAX0(cur.offset - old.offset)
+#define W(offset) MAX0(ccs.offset - ocs.offset)
+
+#define RATIO(c, i, u) ((c) == 0? 1.0: (u) / ((double)(c) + (i)))
+#define CRATE(x) RATIO(W(x.comp_bytes), W(x.inc_bytes), W(x.unc_bytes))
+
+#define KBPS(n) ((n) / (interval * 1000.0))
+
+/*
+ * Print a running summary of interface statistics.
+ * Repeat display every interval seconds, showing statistics
+ * collected over that interval. Assumes that interval is non-zero.
+ * First line printed is cumulative.
+ */
+static void
+intpr()
+{
+ register int line = 0;
+ sigset_t oldmask, mask;
+ char *bunit;
+ int ratef = 0;
+ struct ppp_stats cur, old;
+ struct ppp_comp_stats ccs, ocs;
+
+ memset(&old, 0, sizeof(old));
+ memset(&ocs, 0, sizeof(ocs));
+
+ while (1) {
+ get_ppp_stats(&cur);
+ if (zflag || rflag)
+ get_ppp_cstats(&ccs);
+
+ (void)signal(SIGALRM, catchalarm);
+ signalled = 0;
+ (void)alarm(interval);
+
+ if ((line % 20) == 0) {
+ if (zflag) {
+ printf("IN: COMPRESSED INCOMPRESSIBLE COMP | ");
+ printf("OUT: COMPRESSED INCOMPRESSIBLE COMP\n");
+ bunit = dflag? "KB/S": "BYTE";
+ printf(" %s PACK %s PACK RATIO | ", bunit, bunit);
+ printf(" %s PACK %s PACK RATIO", bunit, bunit);
+ } else {
+ printf("%8.8s %6.6s %6.6s",
+ "IN", "PACK", "VJCOMP");
+
+ if (!rflag)
+ printf(" %6.6s %6.6s", "VJUNC", "VJERR");
+ if (vflag)
+ printf(" %6.6s %6.6s", "VJTOSS", "NON-VJ");
+ if (rflag)
+ printf(" %6.6s %6.6s", "RATIO", "UBYTE");
+ printf(" | %8.8s %6.6s %6.6s",
+ "OUT", "PACK", "VJCOMP");
+
+ if (!rflag)
+ printf(" %6.6s %6.6s", "VJUNC", "NON-VJ");
+ if (vflag)
+ printf(" %6.6s %6.6s", "VJSRCH", "VJMISS");
+ if (rflag)
+ printf(" %6.6s %6.6s", "RATIO", "UBYTE");
+ }
+ putchar('\n');
+ }
+
+ if (zflag) {
+ if (ratef) {
+ printf("%8.3f %6u %8.3f %6u %6.2f",
+ KBPS(W(d.comp_bytes)),
+ W(d.comp_packets),
+ KBPS(W(d.inc_bytes)),
+ W(d.inc_packets),
+ ccs.d.ratio / 256.0);
+ printf(" | %8.3f %6u %8.3f %6u %6.2f",
+ KBPS(W(c.comp_bytes)),
+ W(c.comp_packets),
+ KBPS(W(c.inc_bytes)),
+ W(c.inc_packets),
+ ccs.c.ratio / 256.0);
+ } else {
+ printf("%8u %6u %8u %6u %6.2f",
+ W(d.comp_bytes),
+ W(d.comp_packets),
+ W(d.inc_bytes),
+ W(d.inc_packets),
+ ccs.d.ratio / 256.0);
+ printf(" | %8u %6u %8u %6u %6.2f",
+ W(c.comp_bytes),
+ W(c.comp_packets),
+ W(c.inc_bytes),
+ W(c.inc_packets),
+ ccs.c.ratio / 256.0);
+ }
+
+ } else {
+ if (ratef)
+ printf("%8.3f", KBPS(V(p.ppp_ibytes)));
+ else
+ printf("%8u", V(p.ppp_ibytes));
+ printf(" %6u %6u",
+ V(p.ppp_ipackets),
+ V(vj.vjs_compressedin));
+ if (!rflag)
+ printf(" %6u %6u",
+ V(vj.vjs_uncompressedin),
+ V(vj.vjs_errorin));
+ if (vflag)
+ printf(" %6u %6u",
+ V(vj.vjs_tossed),
+ V(p.ppp_ipackets) - V(vj.vjs_compressedin)
+ - V(vj.vjs_uncompressedin) - V(vj.vjs_errorin));
+ if (rflag) {
+ printf(" %6.2f ", CRATE(d));
+ if (ratef)
+ printf("%6.2f", KBPS(W(d.unc_bytes)));
+ else
+ printf("%6u", W(d.unc_bytes));
+ }
+ if (ratef)
+ printf(" | %8.3f", KBPS(V(p.ppp_obytes)));
+ else
+ printf(" | %8u", V(p.ppp_obytes));
+ printf(" %6u %6u",
+ V(p.ppp_opackets),
+ V(vj.vjs_compressed));
+ if (!rflag)
+ printf(" %6u %6u",
+ V(vj.vjs_packets) - V(vj.vjs_compressed),
+ V(p.ppp_opackets) - V(vj.vjs_packets));
+ if (vflag)
+ printf(" %6u %6u",
+ V(vj.vjs_searches),
+ V(vj.vjs_misses));
+ if (rflag) {
+ printf(" %6.2f ", CRATE(c));
+ if (ratef)
+ printf("%6.2f", KBPS(W(c.unc_bytes)));
+ else
+ printf("%6u", W(c.unc_bytes));
+ }
+
+ }
+
+ putchar('\n');
+ fflush(stdout);
+ line++;
+
+ count--;
+ if (!infinite && !count)
+ break;
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGALRM);
+ sigprocmask(SIG_BLOCK, &mask, &oldmask);
+ if (!signalled) {
+ sigemptyset(&mask);
+ sigsuspend(&mask);
+ }
+ sigprocmask(SIG_SETMASK, &oldmask, NULL);
+ signalled = 0;
+ (void)alarm(interval);
+
+ if (!aflag) {
+ old = cur;
+ ocs = ccs;
+ ratef = dflag;
+ }
+ }
+}
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int c;
+#ifdef STREAMS
+ char *dev;
+#endif
+
+ interface = "ppp0";
+ if ((progname = strrchr(argv[0], '/')) == NULL)
+ progname = argv[0];
+ else
+ ++progname;
+
+ while ((c = getopt(argc, argv, "advrzc:w:")) != -1) {
+ switch (c) {
+ case 'a':
+ ++aflag;
+ break;
+ case 'd':
+ ++dflag;
+ break;
+ case 'v':
+ ++vflag;
+ break;
+ case 'r':
+ ++rflag;
+ break;
+ case 'z':
+ ++zflag;
+ break;
+ case 'c':
+ count = atoi(optarg);
+ if (count <= 0)
+ usage();
+ break;
+ case 'w':
+ interval = atoi(optarg);
+ if (interval <= 0)
+ usage();
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (!interval && count)
+ interval = 5;
+ if (interval && !count)
+ infinite = 1;
+ if (!interval && !count)
+ count = 1;
+ if (aflag)
+ dflag = 0;
+
+ if (argc > 1)
+ usage();
+ if (argc > 0)
+ interface = argv[0];
+
+ if (sscanf(interface, "ppp%d", &unit) != 1) {
+ fprintf(stderr, "%s: invalid interface '%s' specified\n",
+ progname, interface);
+ }
+
+#ifndef STREAMS
+ {
+ struct ifreq ifr;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ fprintf(stderr, "%s: ", progname);
+ perror("couldn't create IP socket");
+ exit(1);
+ }
+
+#ifdef _linux_
+#undef ifr_name
+#define ifr_name ifr_ifrn.ifrn_name
+#endif
+ strncpy(ifr.ifr_name, interface, sizeof(ifr.ifr_name));
+ if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
+ fprintf(stderr, "%s: nonexistent interface '%s' specified\n",
+ progname, interface);
+ exit(1);
+ }
+ }
+
+#else /* STREAMS */
+#ifdef __osf__
+ dev = "/dev/streams/ppp";
+#else
+ dev = "/dev/ppp";
+#endif
+ if ((s = open(dev, O_RDONLY)) < 0) {
+ fprintf(stderr, "%s: couldn't open ", progname);
+ perror(dev);
+ exit(1);
+ }
+ if (strioctl(s, PPPIO_ATTACH, &unit, sizeof(int), 0) < 0) {
+ fprintf(stderr, "%s: ppp%d is not available\n", progname, unit);
+ exit(1);
+ }
+
+#endif /* STREAMS */
+
+ intpr();
+ exit(0);
+}
diff --git a/usr.sbin/praliases/Makefile b/usr.sbin/praliases/Makefile
new file mode 100644
index 0000000..0b99a8e
--- /dev/null
+++ b/usr.sbin/praliases/Makefile
@@ -0,0 +1,52 @@
+# @(#)Makefile 8.2 (Berkeley) 9/21/96
+# $FreeBSD$
+
+MAINTAINER= gshapiro@FreeBSD.org
+
+SENDMAIL_DIR= ${.CURDIR}/../../contrib/sendmail
+.PATH: ${SENDMAIL_DIR}/praliases
+
+PROG= praliases
+SRCS= praliases.c
+MAN= praliases.8
+
+CFLAGS+= -I${SENDMAIL_DIR}/src -I${SENDMAIL_DIR}/include -I.
+CFLAGS+= -DNEWDB -DNOT_SENDMAIL
+
+.if exists(${.OBJDIR}/../../lib/libsm)
+LIBSMDIR:= ${.OBJDIR}/../../lib/libsm
+.else
+LIBSMDIR!= cd ${.CURDIR}/../../lib/libsm; make -V .OBJDIR
+.endif
+LIBSM:= ${LIBSMDIR}/libsm.a
+
+.if exists(${.OBJDIR}/../../lib/libsmdb)
+LIBSMDBDIR:= ${.OBJDIR}/../../lib/libsmdb
+.else
+LIBSMDBDIR!= cd ${.CURDIR}/../../lib/libsmdb; make -V .OBJDIR
+.endif
+LIBSMDB:= ${LIBSMDBDIR}/libsmdb.a
+
+.if exists(${.OBJDIR}/../../lib/libsmutil)
+LIBSMUTILDIR:= ${.OBJDIR}/../../lib/libsmutil
+.else
+LIBSMUTILDIR!= cd ${.CURDIR}/../../lib/libsmutil; make -V .OBJDIR
+.endif
+LIBSMUTIL:= ${LIBSMUTILDIR}/libsmutil.a
+
+DPADD= ${LIBSMDB} ${LIBSMUTIL} ${LIBSM}
+LDADD= ${LIBSMDB} ${LIBSMUTIL} ${LIBSM}
+
+SRCS+= sm_os.h
+CLEANFILES+=sm_os.h
+
+# User customizations to the sendmail build environment
+CFLAGS+= ${SENDMAIL_CFLAGS}
+LDFLAGS+=${SENDMAIL_LDFLAGS}
+DPADD+= ${SENDMAIL_DPADD}
+LDADD+= ${SENDMAIL_LDADD}
+
+sm_os.h:
+ ln -sf ${SENDMAIL_DIR}/include/sm/os/sm_os_freebsd.h sm_os.h
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/prefix/Makefile b/usr.sbin/prefix/Makefile
new file mode 100644
index 0000000..9155013
--- /dev/null
+++ b/usr.sbin/prefix/Makefile
@@ -0,0 +1,8 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+# $Id: Makefile,v 1.1.1.1 1999/08/08 23:31:13 itojun Exp $
+# $FreeBSD$
+
+SCRIPTS=prefix.sh
+MAN= prefix.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/prefix/prefix.8 b/usr.sbin/prefix/prefix.8
new file mode 100644
index 0000000..b5b2e2f
--- /dev/null
+++ b/usr.sbin/prefix/prefix.8
@@ -0,0 +1,76 @@
+.\" $KAME: prefix.8,v 1.6 2001/02/04 05:20:48 jinmei Exp $
+.\"
+.\" Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the project nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd October 10, 1998
+.Dt PREFIX 8
+.Os
+.Sh NAME
+.Nm prefix
+.Nd configure network interface prefixes
+.Sh SYNOPSIS
+.Nm
+.Bk -words
+.Ar interface prefix
+.Ek
+.Bk -words
+.Op Cm set | delete
+.Ek
+.Sh DESCRIPTION
+.Nm
+is used to assign an prefix
+to a network interface.
+This command is currently just a frontend of
+.Xr ifconfig 8 ,
+and is not recommended to be used.
+.Sh SEE ALSO
+.Xr netstat 1 ,
+.Xr netintro 4 ,
+.Xr ifconfig 8 ,
+.Xr rc 8
+.Sh HISTORY
+The
+.Nm
+command first appeared in WIDE/KAME IPv6 protocol stack kit.
+Previously, this command was used to manipulate prefixes separately
+from interface identifiers, intending to be used for prefix
+renumbering.
+However, it had been known that the manipulation mechanism was not
+friendly with traditional address management engine in the kernel.
+Thus, the KAME developers decided to invalidate the prefix
+manipulation mechanism, and to clarify the entire address/prefix
+manipulation in February 2001.
+Since then, this command has almost been obsoleted.
+All users should now use
+.Xr ifconfig 8 .
+.Pp
+IPv6 and IPsec support based on the KAME Project (http://www.kame.net/) stack
+was initially integrated into
+.Fx 4.0
diff --git a/usr.sbin/prefix/prefix.sh b/usr.sbin/prefix/prefix.sh
new file mode 100644
index 0000000..ee727b3
--- /dev/null
+++ b/usr.sbin/prefix/prefix.sh
@@ -0,0 +1,74 @@
+#!/bin/sh
+
+# $KAME: prefix.sh,v 1.12 2001/05/26 23:38:10 itojun Exp $
+# $FreeBSD$
+
+# Copyright (c) 2001 WIDE Project. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of the project nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+iface=$1
+prefix=$2
+
+usage() {
+ echo "usage: prefix interface prefix [set|delete]"
+}
+
+# We're now invalidating the prefix ioctls and the corresponding command.
+echo "** The prefix command is almost invalidated. Please use ifconfig(8). **"
+
+if [ X"$iface" = X -o X"$prefix" = X ]; then
+ usage
+ exit 1
+fi
+
+if [ -z $3 ]; then
+ command=set
+else
+ command=$3
+fi
+
+case $command in
+ set)
+ laddr=`ifconfig $iface inet6 | grep 'inet6 fe80:' | head -1 | awk '{print $2}'`
+ if [ X"$laddr" = X ]; then
+ echo "prefix: no interface ID found"
+ exit 1
+ fi
+ hostid=`echo $laddr | sed -e 's/^fe80:[0-9a-fA-F]*:/fe80::/' -e 's/^fe80:://' -e 's/%.*//'`
+ address=$2$hostid
+ exec ifconfig $iface inet6 $address prefixlen 64 alias
+ ;;
+ delete)
+ addrs=`ifconfig $iface inet6 | grep "inet6 $prefix" | awk '{print $2}'`
+ for a in $addrs; do
+ ifconfig $iface inet6 $a -alias
+ done
+ ;;
+ *)
+ usage
+ exit 1
+ ;;
+esac
diff --git a/usr.sbin/procctl/Makefile b/usr.sbin/procctl/Makefile
new file mode 100644
index 0000000..5cb35e7
--- /dev/null
+++ b/usr.sbin/procctl/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= procctl
+MAN= procctl.8
+
+WARNS?= 2
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/procctl/procctl.8 b/usr.sbin/procctl/procctl.8
new file mode 100644
index 0000000..9058ac1
--- /dev/null
+++ b/usr.sbin/procctl/procctl.8
@@ -0,0 +1,34 @@
+.\" $FreeBSD$
+.Dd November 23, 1997
+.Dt PROCCTL 8
+.Os
+.Sh NAME
+.Nm procctl
+.Nd clear procfs event flags
+.Sh SYNOPSIS
+.Nm
+.Ar pid ...
+.Sh DESCRIPTION
+The
+.Nm
+command clears the
+.Xr procfs 5
+event mask used by
+.Xr truss 1 .
+This can be used in the event that a process is left stranded, since
+the
+.Xr procfs 5
+events result in a non-killable process.
+The arguments are a list of process IDs;
+.Nm
+goes through the list and clears the event masks for each specified process.
+.Sh SEE ALSO
+.Xr truss 1 ,
+.Xr procfs 5
+.Sh HISTORY
+The
+.Nm
+command was written by
+.An Sean Eric Fagan
+for
+.Fx .
diff --git a/usr.sbin/procctl/procctl.c b/usr.sbin/procctl/procctl.c
new file mode 100644
index 0000000..574523f
--- /dev/null
+++ b/usr.sbin/procctl/procctl.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright 1997 Sean Eric Fagan
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Sean Eric Fagan
+ * 4. Neither the name of the author may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#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];
+
+ snprintf(buf, sizeof(buf), "/proc/%s/mem", av[i]);
+ fd = open(buf, O_RDWR);
+ if (fd == -1) {
+ if (errno == ENOENT)
+ continue;
+ warn("cannot open pid %s", av[i]);
+ continue;
+ }
+ if (ioctl(fd, PIOCBIC, ~0) == -1)
+ warn("cannot clear process %s's event mask", av[i]);
+ if (ioctl(fd, PIOCCONT, 0) == -1 && errno != EINVAL)
+ warn("cannot continue process %s", av[i]);
+ close(fd);
+ }
+ return 0;
+}
diff --git a/usr.sbin/pstat/Makefile b/usr.sbin/pstat/Makefile
new file mode 100644
index 0000000..34493cb
--- /dev/null
+++ b/usr.sbin/pstat/Makefile
@@ -0,0 +1,14 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+PROG= pstat
+WARNS?= 2
+LINKS= ${BINDIR}/pstat ${BINDIR}/swapinfo
+MAN= pstat.8
+MLINKS= pstat.8 swapinfo.8
+WARNS?= 4
+
+DPADD= ${LIBKVM}
+LDADD= -lkvm
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pstat/pstat.8 b/usr.sbin/pstat/pstat.8
new file mode 100644
index 0000000..66d486d
--- /dev/null
+++ b/usr.sbin/pstat/pstat.8
@@ -0,0 +1,262 @@
+.\" 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
+.\" $FreeBSD$
+.\"
+.Dd May 23, 2002
+.Dt PSTAT 8
+.Os
+.Sh NAME
+.Nm pstat ,
+.Nm swapinfo
+.Nd display system data structures
+.Sh SYNOPSIS
+.Nm
+.Op Fl Tfknst
+.Op Fl M Ar core
+.Op Fl N Ar system
+.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.
+.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
+.Ev BLOCKSIZE
+environment variable.
+.It Fl T
+Print the number of used and free slots in several system tables.
+This is useful for checking to see how large system tables have become
+if the system is under heavy load.
+.It Fl f
+Print the open file table with these headings:
+.Bl -tag -width indent
+.It LOC
+The core location of this table entry.
+.It TYPE
+The type of object the file table entry points to.
+.It FLG
+Miscellaneous state variables encoded thus:
+.Pp
+.Bl -tag -width indent -compact
+.It R
+open for reading
+.It W
+open for writing
+.It A
+open for appending
+.It I
+signal pgrp when data ready
+.El
+.It CNT
+Number of processes that know this open file.
+.It MSG
+Number of messages outstanding for this file.
+.It DATA
+The location of the vnode table entry or socket structure for this file.
+.It OFFSET
+The file offset (see
+.Xr lseek 2 ) .
+.El
+.It Fl s
+Print information about swap space usage on all the
+swap areas compiled into the kernel.
+The first column is the device name of the partition. The next column is
+the total space available in the partition. The
+.Ar Used
+column indicates the total blocks used so far; the
+.Ar Available
+column indicates how much space is remaining on each partition.
+The
+.Ar Capacity
+reports the percentage of space used.
+.Pp
+If more than one partition is configured into the system, totals for all
+of the statistics will be reported in the final line of the report.
+.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:
+.Pp
+.Bl -tag -width indent -compact
+.It T
+delay timeout in progress
+.It W
+waiting for open to complete
+.It O
+open
+.It F
+outq has been flushed during DMA
+.It C
+carrier is on
+.It c
+connection open
+.It B
+busy doing output
+.It A
+process is waiting for space in output queue
+.It a
+process is waiting for output to complete
+.It X
+open for exclusive use
+.It S
+output stopped (ixon flow control)
+.It m
+output stopped (carrier flow control)
+.It o
+output stopped (CTS flow control)
+.It d
+output stopped (DSR flow control)
+.It K
+input stopped
+.It Y
+send SIGIO for input events
+.It D
+state for lowercase
+.Ql \e
+work
+.It E
+within a
+.Ql \e.../
+for PRTRUB
+.It L
+next character is literal
+.It P
+retyping suspended input (PENDIN)
+.It N
+counting tab width, ignore FLUSHO
+.It l
+block mode input routine in use
+.It s
+i/o being snooped
+.It Z
+connection lost
+.El
+.It SESS
+Kernel address of the session structure.
+.It PGID
+Process group for which this is controlling terminal.
+.It DISC
+Line discipline;
+.Ql term
+for
+TTYDISC
+or
+.Ql ntty
+for
+NTTYDISC
+or
+.Ql tab
+for
+TABLDISC
+or
+.Ql slip
+for
+SLIPDISC
+or
+.Ql ppp
+for
+PPPDISC.
+.El
+.It Fl M
+Extract values associated with the name list from the specified core
+instead of the default
+.Pa /dev/kmem .
+.It Fl N
+Extract the name list from the specified system instead of the default
+.Pa /boot/kernel/kernel .
+.El
+.Sh FILES
+.Bl -tag -width /boot/kernel/kernel -compact
+.It Pa /boot/kernel/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
+.%T UNIX Implementation
+.%A K. Thompson
+.Re
+.Sh BUGS
+Does not understand
+.Tn NFS
+swap servers.
+.Sh HISTORY
+The
+.Nm
+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..04afd27
--- /dev/null
+++ b/usr.sbin/pstat/pstat.c
@@ -0,0 +1,590 @@
+/*-
+ * Copyright (c) 1980, 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 2002 Networks Associates Technologies, Inc.
+ * All rights reserved.
+ *
+ * Portions of this software were developed for the FreeBSD Project by
+ * ThinkSec AS and NAI Labs, the Security Research Division of Network
+ * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
+ * ("CBOSS"), as part of the DARPA CHATS research program.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#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[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#define _KERNEL
+#include <sys/file.h>
+#include <sys/uio.h>
+#undef _KERNEL
+#include <sys/stat.h>
+#include <sys/stdint.h>
+#include <sys/ioctl.h>
+#include <sys/ioctl_compat.h> /* XXX NTTYDISC is too well hidden */
+#include <sys/tty.h>
+#include <sys/conf.h>
+#include <sys/blist.h>
+
+#include <sys/user.h>
+#include <sys/sysctl.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <kvm.h>
+#include <limits.h>
+#include <nlist.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+enum {
+ NL_CONSTTY,
+ NL_MAXFILES,
+ NL_NFILES,
+ NL_TTY_LIST
+};
+
+static struct nlist nl[] = {
+ { "_constty", 0 },
+ { "_maxfiles", 0 },
+ { "_nfiles", 0 },
+ { "_tty_list", 0 },
+ { "" }
+};
+
+static int usenumflag;
+static int totalflag;
+static int swapflag;
+static char *nlistf;
+static char *memf;
+static kvm_t *kd;
+
+static char *usagestr;
+
+static void filemode(void);
+static int getfiles(char **, size_t *);
+static void swapmode(void);
+static void ttymode(void);
+static void ttyprt(struct xtty *);
+static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ int ch, i, quit, ret;
+ int fileflag, ttyflag;
+ char buf[_POSIX2_LINE_MAX],*opts;
+
+ fileflag = swapflag = ttyflag = 0;
+
+ /* We will behave like good old swapinfo if thus invoked */
+ opts = strrchr(argv[0], '/');
+ if (opts)
+ opts++;
+ else
+ opts = argv[0];
+ if (!strcmp(opts, "swapinfo")) {
+ swapflag = 1;
+ opts = "kM:N:";
+ usagestr = "swapinfo [-k] [-M core] [-N system]";
+ } else {
+ opts = "TM:N:fknst";
+ usagestr = "pstat [-Tfknst] [-M core] [-N system]";
+ }
+
+ while ((ch = getopt(argc, argv, opts)) != -1)
+ switch (ch) {
+ case 'f':
+ fileflag = 1;
+ break;
+ case 'k':
+ putenv("BLOCKSIZE=1K");
+ break;
+ case 'M':
+ memf = optarg;
+ break;
+ case 'N':
+ nlistf = optarg;
+ break;
+ case 'n':
+ usenumflag = 1;
+ break;
+ case 's':
+ ++swapflag;
+ break;
+ case 'T':
+ totalflag = 1;
+ break;
+ case 't':
+ ttyflag = 1;
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (memf != NULL) {
+ kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, buf);
+ if (kd == NULL)
+ errx(1, "kvm_openfiles: %s", buf);
+ if ((ret = kvm_nlist(kd, nl)) != 0) {
+ if (ret == -1)
+ errx(1, "kvm_nlist: %s", kvm_geterr(kd));
+ quit = 0;
+ for (i = 0; nl[i].n_name[0] != '\0'; ++i)
+ if (nl[i].n_value == 0) {
+ quit = 1;
+ warnx("undefined symbol: %s",
+ nl[i].n_name);
+ }
+ if (quit)
+ exit(1);
+ }
+ }
+ if (!(fileflag | ttyflag | swapflag | totalflag))
+ usage();
+ if (fileflag || totalflag)
+ filemode();
+ if (ttyflag)
+ ttymode();
+ if (swapflag || totalflag)
+ swapmode();
+ exit (0);
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: %s\n", usagestr);
+ exit (1);
+}
+
+static const char hdr[] =
+" LINE RAW CAN OUT IHIWT ILOWT OHWT LWT COL STATE SESS PGID DISC\n";
+
+static void
+ttymode_kvm(void)
+{
+ SLIST_HEAD(, tty) tl;
+ struct tty *tp, tty;
+ struct xtty xt;
+
+ (void)printf("%s", hdr);
+ bzero(&xt, sizeof xt);
+ xt.xt_size = sizeof xt;
+ if (kvm_read(kd, nl[NL_TTY_LIST].n_value, &tl, sizeof tl) != sizeof tl)
+ errx(1, "kvm_read(): %s", kvm_geterr(kd));
+ tp = SLIST_FIRST(&tl);
+ while (tp != NULL) {
+ if (kvm_read(kd, (u_long)tp, &tty, sizeof tty) != sizeof tty)
+ errx(1, "kvm_read(): %s", kvm_geterr(kd));
+ xt.xt_rawcc = tty.t_rawq.c_cc;
+ xt.xt_cancc = tty.t_canq.c_cc;
+ xt.xt_outcc = tty.t_outq.c_cc;
+#define XT_COPY(field) xt.xt_##field = tty.t_##field
+ XT_COPY(line);
+ XT_COPY(state);
+ XT_COPY(column);
+ XT_COPY(ihiwat);
+ XT_COPY(ilowat);
+ XT_COPY(ohiwat);
+ XT_COPY(olowat);
+#undef XT_COPY
+ ttyprt(&xt);
+ tp = tty.t_list.sle_next;
+ }
+}
+
+static void
+ttymode_sysctl(void)
+{
+ struct xtty *xt, *end;
+ void *xttys;
+ size_t len;
+
+ (void)printf("%s", hdr);
+ if ((xttys = malloc(len = sizeof *xt)) == NULL)
+ err(1, "malloc()");
+ while (sysctlbyname("kern.ttys", xttys, &len, 0, 0) == -1) {
+ if (errno != ENOMEM)
+ err(1, "sysctlbyname()");
+ len *= 2;
+ if ((xttys = realloc(xttys, len)) == NULL)
+ err(1, "realloc()");
+ }
+ if (len > 0) {
+ end = (struct xtty *)((char *)xttys + len);
+ for (xt = xttys; xt < end; xt++)
+ ttyprt(xt);
+ }
+}
+
+static void
+ttymode(void)
+{
+
+ if (kd != NULL)
+ ttymode_kvm();
+ else
+ ttymode_sysctl();
+}
+
+static struct {
+ int flag;
+ char val;
+} ttystates[] = {
+#ifdef TS_WOPEN
+ { TS_WOPEN, 'W'},
+#endif
+ { TS_ISOPEN, 'O'},
+ { TS_CARR_ON, 'C'},
+#ifdef TS_CONNECTED
+ { TS_CONNECTED, 'c'},
+#endif
+ { TS_TIMEOUT, 'T'},
+ { TS_FLUSH, 'F'},
+ { TS_BUSY, 'B'},
+#ifdef TS_ASLEEP
+ { TS_ASLEEP, 'A'},
+#endif
+#ifdef TS_SO_OLOWAT
+ { TS_SO_OLOWAT, 'A'},
+#endif
+#ifdef TS_SO_OCOMPLETE
+ { TS_SO_OCOMPLETE, 'a'},
+#endif
+ { TS_XCLUDE, 'X'},
+ { TS_TTSTOP, 'S'},
+#ifdef TS_CAR_OFLOW
+ { TS_CAR_OFLOW, 'm'},
+#endif
+#ifdef TS_CTS_OFLOW
+ { TS_CTS_OFLOW, 'o'},
+#endif
+#ifdef TS_DSR_OFLOW
+ { TS_DSR_OFLOW, 'd'},
+#endif
+ { TS_TBLOCK, 'K'},
+ { TS_ASYNC, 'Y'},
+ { TS_BKSL, 'D'},
+ { TS_ERASE, 'E'},
+ { TS_LNCH, 'L'},
+ { TS_TYPEN, 'P'},
+ { TS_CNTTB, 'N'},
+#ifdef TS_CAN_BYPASS_L_RINT
+ { TS_CAN_BYPASS_L_RINT, 'l'},
+#endif
+#ifdef TS_SNOOP
+ { TS_SNOOP, 's'},
+#endif
+#ifdef TS_ZOMBIE
+ { TS_ZOMBIE, 'Z'},
+#endif
+ { 0, '\0'},
+};
+
+static void
+ttyprt(struct xtty *xt)
+{
+ int i, j;
+ pid_t pgid;
+ char *name, state[20];
+
+ if (xt->xt_size != sizeof *xt)
+ errx(1, "struct xtty size mismatch");
+ if (usenumflag || xt->xt_dev == 0 ||
+ (name = devname(xt->xt_dev, S_IFCHR)) == NULL)
+ (void)printf(" %2d,%-2d", major(xt->xt_dev), minor(xt->xt_dev));
+ else
+ (void)printf("%7s ", name);
+ (void)printf("%2ld %3ld ", xt->xt_rawcc, xt->xt_cancc);
+ (void)printf("%3ld %5d %5d %4d %3d %7d ", xt->xt_outcc,
+ xt->xt_ihiwat, xt->xt_ilowat, xt->xt_ohiwat, xt->xt_olowat,
+ xt->xt_column);
+ for (i = j = 0; ttystates[i].flag; i++)
+ if (xt->xt_state & ttystates[i].flag)
+ state[j++] = ttystates[i].val;
+ if (j == 0)
+ state[j++] = '-';
+ state[j] = '\0';
+ (void)printf("%-6s %8d", state, xt->xt_sid);
+ pgid = 0;
+ (void)printf("%6d ", xt->xt_pgid);
+ switch (xt->xt_line) {
+ case TTYDISC:
+ (void)printf("term\n");
+ break;
+ case NTTYDISC:
+ (void)printf("ntty\n");
+ break;
+ case SLIPDISC:
+ (void)printf("slip\n");
+ break;
+ case PPPDISC:
+ (void)printf("ppp\n");
+ break;
+ default:
+ (void)printf("%d\n", xt->xt_line);
+ break;
+ }
+}
+
+static void
+filemode(void)
+{
+ struct file *fp;
+ struct file *addr;
+ char *buf, flagbuf[16], *fbp;
+ int maxf, openf;
+ size_t len;
+ static char *dtypes[] = { "???", "inode", "socket" };
+
+ if (kd != NULL) {
+ if (kvm_read(kd, nl[NL_MAXFILES].n_value,
+ &maxf, sizeof maxf) != sizeof maxf ||
+ kvm_read(kd, nl[NL_NFILES].n_value,
+ &openf, sizeof openf) != sizeof openf)
+ errx(1, "kvm_read(): %s", kvm_geterr(kd));
+ } else {
+ len = sizeof(int);
+ if (sysctlbyname("kern.maxfiles", &maxf, &len, 0, 0) == -1 ||
+ sysctlbyname("kern.openfiles", &openf, &len, 0, 0) == -1)
+ err(1, "sysctlbyname()");
+ }
+
+ if (totalflag) {
+ (void)printf("%3d/%3d files\n", openf, maxf);
+ return;
+ }
+ if (getfiles(&buf, &len) == -1)
+ return;
+ /*
+ * 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 = LIST_FIRST((struct filelist *)buf);
+ fp = (struct file *)(buf + sizeof(struct filelist));
+ openf = (len - sizeof(struct filelist)) / sizeof(struct file);
+
+ (void)printf("%d/%d open files\n", openf, maxf);
+ (void)printf(" LOC TYPE FLG CNT MSG DATA OFFSET\n");
+ for (; (char *)fp < buf + len; addr = LIST_NEXT(fp, f_list), 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';
+ 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);
+ (void)printf(" %jx\n", (uintmax_t)fp->f_offset);
+ }
+ free(buf);
+}
+
+static int
+getfiles(char **abuf, size_t *alen)
+{
+ size_t len;
+ int mib[2];
+ char *buf;
+
+ /*
+ * XXX
+ * Add emulation of KINFO_FILE here.
+ */
+ if (kd != NULL)
+ errx(1, "files on dead kernel, not implemented");
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_FILE;
+ if (sysctl(mib, 2, NULL, &len, NULL, 0) == -1) {
+ warn("sysctl: KERN_FILE");
+ return (-1);
+ }
+ if ((buf = malloc(len)) == NULL)
+ errx(1, "malloc");
+ if (sysctl(mib, 2, buf, &len, NULL, 0) == -1) {
+ warn("sysctl: KERN_FILE");
+ return (-1);
+ }
+ *abuf = buf;
+ *alen = len;
+ return (0);
+}
+
+/*
+ * swapmode is based on a program called swapinfo written
+ * by Kevin Lahey <kml@rokkaku.atl.ga.us>.
+ */
+
+#define CONVERT(v) ((int)((intmax_t)(v) * pagesize / blocksize))
+static struct kvm_swap swtot;
+static int nswdev;
+
+static void
+print_swap_header(void)
+{
+ int hlen;
+ long blocksize;
+ const char *header;
+
+ header = getbsize(&hlen, &blocksize);
+ if (totalflag == 0)
+ (void)printf("%-15s %*s %8s %8s %8s %s\n",
+ "Device", hlen, header,
+ "Used", "Avail", "Capacity", "Type");
+}
+
+static void
+print_swap(struct kvm_swap *ksw)
+{
+ int hlen, pagesize;
+ long blocksize;
+
+ pagesize = getpagesize();
+ getbsize(&hlen, &blocksize);
+ swtot.ksw_total += ksw->ksw_total;
+ swtot.ksw_used += ksw->ksw_used;
+ ++nswdev;
+ if (totalflag == 0) {
+ (void)printf("%-15s %*d ",
+ ksw->ksw_devname, hlen,
+ CONVERT(ksw->ksw_total));
+ (void)printf("%8d %8d %5.0f%% %s\n",
+ CONVERT(ksw->ksw_used),
+ CONVERT(ksw->ksw_total - ksw->ksw_used),
+ (ksw->ksw_used * 100.0) / ksw->ksw_total,
+ (ksw->ksw_flags & SW_SEQUENTIAL) ?
+ "Sequential" : "Interleaved");
+ }
+}
+
+static void
+print_swap_total(void)
+{
+ int hlen, pagesize;
+ long blocksize;
+
+ pagesize = getpagesize();
+ getbsize(&hlen, &blocksize);
+ if (totalflag) {
+ blocksize = 1024 * 1024;
+ (void)printf("%dM/%dM swap space\n",
+ CONVERT(swtot.ksw_used), CONVERT(swtot.ksw_total));
+ } else if (nswdev > 1) {
+ (void)printf("%-15s %*d %8d %8d %5.0f%%\n",
+ "Total", hlen, CONVERT(swtot.ksw_total),
+ CONVERT(swtot.ksw_used),
+ CONVERT(swtot.ksw_total - swtot.ksw_used),
+ (swtot.ksw_used * 100.0) / swtot.ksw_total);
+ }
+}
+
+static void
+swapmode_kvm(void)
+{
+ struct kvm_swap kswap[16];
+ int i, n;
+
+ n = kvm_getswapinfo(kd, kswap, sizeof kswap / sizeof kswap[0],
+ ((swapflag > 1) ? SWIF_DUMP_TREE : 0) | SWIF_DEV_PREFIX);
+
+ print_swap_header();
+ for (i = 0; i < n; ++i)
+ print_swap(&kswap[i]);
+ print_swap_total();
+}
+
+static void
+swapmode_sysctl(void)
+{
+ struct kvm_swap ksw;
+ struct xswdev xsw;
+ size_t mibsize, size;
+ int mib[16], n;
+
+ print_swap_header();
+ mibsize = sizeof mib / sizeof mib[0];
+ if (sysctlnametomib("vm.swap_info", mib, &mibsize) == -1)
+ err(1, "sysctlnametomib()");
+ for (n = 0; ; ++n) {
+ mib[mibsize] = n;
+ size = sizeof xsw;
+ if (sysctl(mib, mibsize + 1, &xsw, &size, NULL, NULL) == -1)
+ break;
+ if (xsw.xsw_version != XSWDEV_VERSION)
+ errx(1, "xswdev version mismatch");
+ snprintf(ksw.ksw_devname, sizeof ksw.ksw_devname,
+ "/dev/%s", devname(xsw.xsw_dev, S_IFCHR));
+ ksw.ksw_used = xsw.xsw_used;
+ ksw.ksw_total = xsw.xsw_nblks;
+ ksw.ksw_flags = xsw.xsw_flags;
+ print_swap(&ksw);
+ }
+ if (errno != ENOENT)
+ err(1, "sysctl()");
+ print_swap_total();
+}
+
+static void
+swapmode(void)
+{
+ if (kd != NULL)
+ swapmode_kvm();
+ else
+ swapmode_sysctl();
+}
diff --git a/usr.sbin/pw/Makefile b/usr.sbin/pw/Makefile
new file mode 100644
index 0000000..630ba60
--- /dev/null
+++ b/usr.sbin/pw/Makefile
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+PROG= pw
+MAN= pw.conf.5 pw.8
+SRCS= pw.c pw_conf.c pw_user.c pw_group.c pw_log.c pw_nis.c pw_vpw.c \
+ grupd.c pwupd.c fileupd.c edgroup.c psdate.c \
+ bitmap.c cpdir.c rm_r.c
+
+LDADD= -lcrypt -lutil
+DPADD= ${LIBCRYPT} ${LIBUTIL}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pw/README b/usr.sbin/pw/README
new file mode 100644
index 0000000..bbb1539
--- /dev/null
+++ b/usr.sbin/pw/README
@@ -0,0 +1,22 @@
+
+pw is a command-line driven passwd/group editor utility that provides
+an easy and safe means of modifying of any/all fields in the system
+password files, and has an add, modify and delete mode for user and
+group records. Command line options have been fashioned to be similar
+to those used by the Sun/shadow commands: useradd, usermod, userdel,
+groupadd, groupmod, groupdel, but combines all operations within the
+single command `pw'.
+
+User add mode also provides a means of easily setting system useradd
+defaults (see pw.conf.5), so that adding a user is as easy as issuing
+the command "pw useradd <loginid>". Creation of a unique primary
+group for each user and automatic membership in secondary groups
+is fully supported.
+
+This program may be FreeBSD specific, but should be trivial to port to
+other bsd4.4 variants.
+
+Author and maintainer: David L. Nugent, <davidn@blaze.net.au>
+
+$FreeBSD$
+
diff --git a/usr.sbin/pw/bitmap.c b/usr.sbin/pw/bitmap.c
new file mode 100644
index 0000000..bcfea7e
--- /dev/null
+++ b/usr.sbin/pw/bitmap.c
@@ -0,0 +1,132 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "bitmap.h"
+
+struct bitmap
+bm_alloc(int size)
+{
+ struct bitmap bm;
+ int szmap = (size / 8) + !!(size % 8);
+
+ bm.size = size;
+ bm.map = malloc(szmap);
+ if (bm.map)
+ memset(bm.map, 0, szmap);
+ return bm;
+}
+
+void
+bm_dealloc(struct bitmap * bm)
+{
+ if (bm->map)
+ free(bm->map);
+}
+
+static void
+bm_getmask(int *pos, unsigned char *bmask)
+{
+ *bmask = (unsigned char) (1 << (*pos % 8));
+ *pos /= 8;
+}
+
+void
+bm_setbit(struct bitmap * bm, int pos)
+{
+ unsigned char bmask;
+
+ bm_getmask(&pos, &bmask);
+ bm->map[pos] |= bmask;
+}
+
+void
+bm_clrbit(struct bitmap * bm, int pos)
+{
+ unsigned char bmask;
+
+ bm_getmask(&pos, &bmask);
+ bm->map[pos] &= ~bmask;
+}
+
+int
+bm_isset(struct bitmap * bm, int pos)
+{
+ unsigned char bmask;
+
+ bm_getmask(&pos, &bmask);
+ return !!(bm->map[pos] & bmask);
+}
+
+int
+bm_firstunset(struct bitmap * bm)
+{
+ int szmap = (bm->size / 8) + !!(bm->size % 8);
+ int at = 0;
+ int pos = 0;
+
+ while (pos < szmap) {
+ unsigned char bmv = bm->map[pos++];
+ unsigned char bmask = 1;
+
+ while (bmask & 0xff) {
+ if ((bmv & bmask) == 0)
+ return at;
+ bmask <<= 1;
+ ++at;
+ }
+ }
+ return at;
+}
+
+int
+bm_lastset(struct bitmap * bm)
+{
+ int szmap = (bm->size / 8) + !!(bm->size % 8);
+ int at = 0;
+ int pos = 0;
+ int ofs = 0;
+
+ while (pos < szmap) {
+ unsigned char bmv = bm->map[pos++];
+ unsigned char bmask = 1;
+
+ while (bmask & 0xff) {
+ if ((bmv & bmask) != 0)
+ ofs = at;
+ bmask <<= 1;
+ ++at;
+ }
+ }
+ return ofs;
+}
diff --git a/usr.sbin/pw/bitmap.h b/usr.sbin/pw/bitmap.h
new file mode 100644
index 0000000..94bd059
--- /dev/null
+++ b/usr.sbin/pw/bitmap.h
@@ -0,0 +1,50 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _BITMAP_H_
+#define _BITMAP_H_
+
+#include <sys/cdefs.h>
+
+struct bitmap
+{
+ int size;
+ unsigned char *map;
+};
+
+__BEGIN_DECLS
+struct bitmap bm_alloc __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..d8fa42b
--- /dev/null
+++ b/usr.sbin/pw/cpdir.c
@@ -0,0 +1,127 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <dirent.h>
+
+#include "pwupd.h"
+
+void
+copymkdir(char const * dir, char const * skel, mode_t mode, uid_t uid, gid_t gid)
+{
+ int rc = 0;
+ char src[MAXPATHLEN];
+ char dst[MAXPATHLEN];
+
+ if (mkdir(dir, mode) != 0 && errno != EEXIST) {
+ warn("mkdir(%s)", dir);
+ } else {
+ int infd, outfd;
+ struct stat st;
+
+ static char counter = 0;
+ static char *copybuf = NULL;
+
+ ++counter;
+ chown(dir, uid, gid);
+ if (skel == NULL || *skel == '\0')
+ rc = 1;
+ else {
+ DIR *d = opendir(skel);
+
+ if (d != NULL) {
+ struct dirent *e;
+
+ while ((e = readdir(d)) != NULL) {
+ char *p = e->d_name;
+
+ if (snprintf(src, sizeof(src), "%s/%s", skel, p) >= (int)sizeof(src))
+ warn("warning: pathname too long '%s/%s' (skel not copied)", skel, p);
+ else if (stat(src, &st) == 0) {
+ if (strncmp(p, "dot.", 4) == 0) /* Conversion */
+ p += 3;
+ if (snprintf(dst, sizeof(dst), "%s/%s", dir, p) >= (int)sizeof(dst))
+ warn("warning: path too long '%s/%s' (skel file skipped)", dir, p);
+ else {
+ if (S_ISDIR(st.st_mode)) { /* Recurse for this */
+ if (strcmp(e->d_name, ".") != 0 && strcmp(e->d_name, "..") != 0)
+ copymkdir(dst, src, (st.st_mode & 0777), uid, gid);
+ chflags(dst, st.st_flags); /* propogate flags */
+ /*
+ * Note: don't propogate special attributes
+ * but do propogate file flags
+ */
+ } else if (S_ISREG(st.st_mode) && (outfd = open(dst, O_RDWR | O_CREAT | O_EXCL, st.st_mode)) != -1) {
+ if ((infd = open(src, O_RDONLY)) == -1) {
+ close(outfd);
+ remove(dst);
+ } else {
+ int b;
+
+ /*
+ * Allocate our copy buffer if we need to
+ */
+ if (copybuf == NULL)
+ copybuf = malloc(4096);
+ while ((b = read(infd, copybuf, 4096)) > 0)
+ write(outfd, copybuf, b);
+ close(infd);
+ /*
+ * Propogate special filesystem flags
+ */
+ fchown(outfd, uid, gid);
+ fchflags(outfd, st.st_flags);
+ close(outfd);
+ chown(dst, uid, gid);
+ }
+ }
+ }
+ }
+ }
+ closedir(d);
+ }
+ }
+ if (--counter == 0 && copybuf != NULL) {
+ free(copybuf);
+ copybuf = NULL;
+ }
+ }
+}
+
diff --git a/usr.sbin/pw/edgroup.c b/usr.sbin/pw/edgroup.c
new file mode 100644
index 0000000..40313d2
--- /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[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <pwd.h>
+#include <grp.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <ctype.h>
+
+#include "pwupd.h"
+
+static int
+isingroup(char const * name, char **mem)
+{
+ int i;
+
+ for (i = 0; mem[i] != NULL; i++)
+ if (strcmp(name, mem[i]) == 0)
+ return i;
+ return -1;
+}
+
+int
+editgroups(char *name, char **groups)
+{
+ int rc = 0;
+ int infd;
+ char groupfile[MAXPATHLEN];
+ char grouptmp[MAXPATHLEN];
+
+ strncpy(groupfile, getgrpath(_GROUP), MAXPATHLEN - 5);
+ groupfile[MAXPATHLEN - 5] = '\0';
+ strcpy(grouptmp, groupfile);
+ strcat(grouptmp, ".new");
+
+ if ((infd = open(groupfile, O_RDWR | O_CREAT, 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..a846513
--- /dev/null
+++ b/usr.sbin/pw/fileupd.c
@@ -0,0 +1,205 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "pwupd.h"
+
+int
+extendline(char **buf, int * buflen, int needed)
+{
+ if (needed > *buflen) {
+ char *tmp = realloc(*buf, needed);
+ if (tmp == NULL)
+ return -1;
+ *buf = tmp;
+ *buflen = needed;
+ }
+ return *buflen;
+}
+
+int
+extendarray(char ***buf, int * buflen, int needed)
+{
+ if (needed > *buflen) {
+ char **tmp = realloc(*buf, needed * sizeof(char *));
+ if (tmp == NULL)
+ return -1;
+ *buf = tmp;
+ *buflen = needed;
+ }
+ return *buflen;
+}
+
+
+int
+fileupdate(char const * filename, mode_t fmode, char const * newline, char const * prefix, int pfxlen, int updmode)
+{
+ int rc = 0;
+
+ if (pfxlen <= 1)
+ rc = EINVAL;
+ else {
+ int infd = open(filename, O_RDWR | O_CREAT, fmode);
+
+ if (infd == -1)
+ rc = errno;
+ else {
+ FILE *infp = fdopen(infd, "r+");
+
+ if (infp == NULL) {
+ rc = errno; /* Assumes fopen(3) sets errno from open(2) */
+ close(infd);
+ } else {
+ int outfd;
+ char file[MAXPATHLEN];
+
+ strcpy(file, filename);
+ strcat(file, ".new");
+ outfd = open(file, O_RDWR | O_CREAT | O_TRUNC | O_EXLOCK, fmode);
+ if (outfd == -1)
+ rc = errno;
+ else {
+ FILE *outfp = fdopen(outfd, "w+");
+
+ if (outfp == NULL) {
+ rc = errno;
+ close(outfd);
+ } else {
+ int updated = UPD_CREATE;
+ int linesize = PWBUFSZ;
+ char *line = malloc(linesize);
+
+ nextline:
+ while (fgets(line, linesize, infp) != NULL) {
+ char *p = strchr(line, '\n');
+
+ while ((p = strchr(line, '\n')) == NULL) {
+ int l;
+ if (extendline(&line, &linesize, linesize + PWBUFSZ) == -1) {
+ int ch;
+ fputs(line, outfp);
+ while ((ch = fgetc(infp)) != EOF) {
+ fputc(ch, outfp);
+ if (ch == '\n')
+ break;
+ }
+ goto nextline;
+ }
+ l = strlen(line);
+ if (fgets(line + l, linesize - l, infp) == NULL)
+ break;
+ }
+ if (*line != '#' && *line != '\n') {
+ if (!updated && strncmp(line, prefix, pfxlen) == 0) {
+ updated = updmode == UPD_REPLACE ? UPD_REPLACE : UPD_DELETE;
+
+ /*
+ * Only actually write changes if updating
+ */
+ if (updmode == UPD_REPLACE)
+ strcpy(line, newline);
+ else if (updmode == UPD_DELETE)
+ continue;
+ }
+ }
+ fputs(line, outfp);
+ }
+
+ /*
+ * Now, we need to decide what to do: If we are in
+ * update mode, and no record was updated, then error If
+ * we are in insert mode, and record already exists,
+ * then error
+ */
+ if (updmode != updated)
+ /* -1 return means:
+ * update,delete=no user entry
+ * create=entry exists
+ */
+ rc = -1;
+ else {
+
+ /*
+ * If adding a new record, append it to the end
+ */
+ if (updmode == UPD_CREATE)
+ fputs(newline, outfp);
+
+ /*
+ * Flush the file and check for the result
+ */
+ if (fflush(outfp) == EOF)
+ rc = errno; /* Failed to update */
+ else {
+ /*
+ * Copy data back into the
+ * original file and truncate
+ */
+ rewind(infp);
+ rewind(outfp);
+ while (fgets(line, linesize, outfp) != NULL)
+ fputs(line, infp);
+
+ /*
+ * If there was a problem with copying
+ * we will just rename 'file.new'
+ * to 'file'.
+ * This is a gross hack, but we may have
+ * corrupted the original file
+ * Unfortunately, it will lose the inode
+ * and hence the lock.
+ */
+ if (fflush(infp) == EOF || ferror(infp))
+ rename(file, filename);
+ else
+ ftruncate(infd, ftell(infp));
+ }
+ }
+ free(line);
+ fclose(outfp);
+ }
+ remove(file);
+ }
+ fclose(infp);
+ }
+ }
+ }
+ return rc;
+}
diff --git a/usr.sbin/pw/grupd.c b/usr.sbin/pw/grupd.c
new file mode 100644
index 0000000..edff76d
--- /dev/null
+++ b/usr.sbin/pw/grupd.c
@@ -0,0 +1,171 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+
+#include "pwupd.h"
+
+static char * grpath = _PATH_PWD;
+
+int
+setgrdir(const char * dir)
+{
+ if (dir == NULL)
+ return -1;
+ else {
+ char * d = malloc(strlen(dir)+1);
+ if (d == NULL)
+ return -1;
+ grpath = strcpy(d, dir);
+ }
+ return 0;
+}
+
+char *
+getgrpath(const char * file)
+{
+ static char pathbuf[MAXPATHLEN];
+
+ snprintf(pathbuf, sizeof pathbuf, "%s/%s", grpath, file);
+ return pathbuf;
+}
+
+int
+grdb(char *arg,...)
+{
+ /*
+ * This is a stub for now, but maybe eventually be functional
+ * if ever an indexed version of /etc/groups is implemented.
+ */
+ arg=arg;
+ return 0;
+}
+
+int
+fmtgrentry(char **buf, int * buflen, struct group * grp, int type)
+{
+ int i, l;
+
+ /*
+ * Since a group line is of arbitrary length,
+ * we need to calculate up-front just how long
+ * it will need to be...
+ */
+ /* groupname : password : gid : */
+ l = strlen(grp->gr_name) + 1 + strlen(grp->gr_passwd) + 1 + 5 + 1;
+ /* group members + comma separator */
+ for (i = 0; grp->gr_mem[i] != NULL; i++) {
+ l += strlen(grp->gr_mem[i]) + 1;
+ }
+ l += 2; /* For newline & NUL */
+ if (extendline(buf, buflen, l) == -1)
+ l = -1;
+ else{
+ /*
+ * Now we can safely format
+ */
+ if (type == PWF_STANDARD)
+ l = sprintf(*buf, "%s:*:%ld:", grp->gr_name, (long) grp->gr_gid);
+ else
+ l = sprintf(*buf, "%s:%s:%ld:", grp->gr_name, grp->gr_passwd, (long) grp->gr_gid);
+
+ /*
+ * List members
+ */
+ for (i = 0; grp->gr_mem[i] != NULL; i++) {
+ l += sprintf(*buf + l, "%s%s", i ? "," : "", grp->gr_mem[i]);
+ }
+
+ (*buf)[l++] = '\n';
+ (*buf)[l] = '\0';
+ }
+ return l;
+}
+
+
+int
+fmtgrent(char **buf, int * buflen, struct group * grp)
+{
+ return fmtgrentry(buf, buflen, grp, PWF_STANDARD);
+}
+
+
+static int
+gr_update(struct group * grp, char const * group, int mode)
+{
+ int l;
+ char pfx[64];
+ int grbuflen = 0;
+ char *grbuf = NULL;
+
+ ENDGRENT();
+ l = snprintf(pfx, sizeof pfx, "%s:", group);
+
+ /*
+ * Update the group file
+ */
+ if (grp != NULL && fmtgrentry(&grbuf, &grbuflen, grp, PWF_PASSWD) == -1)
+ l = -1;
+ else {
+ l = fileupdate(getgrpath(_GROUP), 0644, grbuf, pfx, l, mode);
+ if (l == 0)
+ l = grdb(NULL);
+ }
+ if (grbuf != NULL)
+ free(grbuf);
+ return l;
+}
+
+
+int
+addgrent(struct group * grp)
+{
+ return gr_update(grp, grp->gr_name, UPD_CREATE);
+}
+
+int
+chggrent(char const * login, struct group * grp)
+{
+ return gr_update(grp, login, UPD_REPLACE);
+}
+
+int
+delgrent(struct group * grp)
+{
+ return gr_update(NULL, grp->gr_name, UPD_DELETE);
+}
diff --git a/usr.sbin/pw/psdate.c b/usr.sbin/pw/psdate.c
new file mode 100644
index 0000000..f97253b
--- /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[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "psdate.h"
+
+
+static int
+a2i(char const ** str)
+{
+ int i = 0;
+ char const *s = *str;
+
+ if (isdigit((unsigned char)*s)) {
+ i = atoi(s);
+ while (isdigit((unsigned char)*s))
+ ++s;
+ *str = s;
+ }
+ return i;
+}
+
+static int
+numerics(char const * str)
+{
+ int rc = isdigit((unsigned char)*str);
+
+ if (rc)
+ while (isdigit((unsigned char)*str) || *str == 'x')
+ ++str;
+ return rc && !*str;
+}
+
+static int
+aindex(char const * arr[], char const ** str, int len)
+{
+ int l, i;
+ char mystr[32];
+
+ mystr[len] = '\0';
+ l = strlen(strncpy(mystr, *str, len));
+ for (i = 0; i < l; i++)
+ mystr[i] = (char) tolower((unsigned char)mystr[i]);
+ for (i = 0; arr[i] && strcmp(mystr, arr[i]) != 0; i++);
+ if (arr[i] == NULL)
+ i = -1;
+ else { /* Skip past it */
+ while (**str && isalpha((unsigned char)**str))
+ ++(*str);
+ /* And any following whitespace */
+ while (**str && (**str == ',' || isspace((unsigned char)**str)))
+ ++(*str);
+ } /* Return index */
+ return i;
+}
+
+static int
+weekday(char const ** str)
+{
+ static char const *days[] =
+ {"sun", "mon", "tue", "wed", "thu", "fri", "sat", NULL};
+
+ return aindex(days, str, 3);
+}
+
+static int
+month(char const ** str)
+{
+ static char const *months[] =
+ {"jan", "feb", "mar", "apr", "may", "jun", "jul",
+ "aug", "sep", "oct", "nov", "dec", NULL};
+
+ return aindex(months, str, 3);
+}
+
+static void
+parse_time(char const * str, int *hour, int *min, int *sec)
+{
+ *hour = a2i(&str);
+ if ((str = strchr(str, ':')) == NULL)
+ *min = *sec = 0;
+ else {
+ ++str;
+ *min = a2i(&str);
+ *sec = ((str = strchr(str, ':')) == NULL) ? 0 : atoi(++str);
+ }
+}
+
+
+static void
+parse_datesub(char const * str, int *day, int *mon, int *year)
+{
+ int i;
+
+ static char const nchrs[] = "0123456789 \t,/-.";
+
+ if ((i = month(&str)) != -1) {
+ *mon = i;
+ if ((i = a2i(&str)) != 0)
+ *day = i;
+ } else if ((i = a2i(&str)) != 0) {
+ *day = i;
+ while (*str && strchr(nchrs + 10, *str) != NULL)
+ ++str;
+ if ((i = month(&str)) != -1)
+ *mon = i;
+ else if ((i = a2i(&str)) != 0)
+ *mon = i - 1;
+ } else
+ return;
+
+ while (*str && strchr(nchrs + 10, *str) != NULL)
+ ++str;
+ if (isdigit((unsigned char)*str)) {
+ *year = atoi(str);
+ if (*year > 1900)
+ *year -= 1900;
+ else if (*year < 32)
+ *year += 100;
+ }
+}
+
+
+/*-
+ * Parse time must be flexible, it handles the following formats:
+ * nnnnnnnnnnn UNIX timestamp (all numeric), 0 = now
+ * 0xnnnnnnnn UNIX timestamp in hexadecimal
+ * 0nnnnnnnnn UNIX timestamp in octal
+ * 0 Given time
+ * +nnnn[smhdwoy] Given time + nnnn hours, mins, days, weeks, months or years
+ * -nnnn[smhdwoy] Given time - nnnn hours, mins, days, weeks, months or years
+ * dd[ ./-]mmm[ ./-]yy Date }
+ * hh:mm:ss Time } May be combined
+ */
+
+time_t
+parse_date(time_t dt, char const * str)
+{
+ char *p;
+ int i;
+ long val;
+ struct tm *T;
+
+ if (dt == 0)
+ dt = time(NULL);
+
+ while (*str && isspace((unsigned char)*str))
+ ++str;
+
+ if (numerics(str)) {
+ dt = strtol(str, &p, 0);
+ } else if (*str == '+' || *str == '-') {
+ val = strtol(str, &p, 0);
+ switch (*p) {
+ case 'h':
+ case 'H': /* hours */
+ dt += (val * 3600L);
+ break;
+ case '\0':
+ case 'm':
+ case 'M': /* minutes */
+ dt += (val * 60L);
+ break;
+ case 's':
+ case 'S': /* seconds */
+ dt += val;
+ break;
+ case 'd':
+ case 'D': /* days */
+ dt += (val * 86400L);
+ break;
+ case 'w':
+ case 'W': /* weeks */
+ dt += (val * 604800L);
+ break;
+ case 'o':
+ case 'O': /* months */
+ T = localtime(&dt);
+ T->tm_mon += (int) val;
+ i = T->tm_mday;
+ goto fixday;
+ case 'y':
+ case 'Y': /* years */
+ T = localtime(&dt);
+ T->tm_year += (int) val;
+ i = T->tm_mday;
+ fixday:
+ dt = mktime(T);
+ T = localtime(&dt);
+ if (T->tm_mday != i) {
+ T->tm_mday = 1;
+ dt = mktime(T);
+ dt -= (time_t) 86400L;
+ }
+ default: /* unknown */
+ break; /* leave untouched */
+ }
+ } else {
+ char *q, tmp[64];
+
+ /*
+ * Skip past any weekday prefix
+ */
+ weekday(&str);
+ 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((unsigned char)q[j]))
+ ++j;
+ if (q[j] == '\0')
+ *q = '\0';
+ else
+ break;
+ }
+ }
+
+ /*
+ * See if there is a time hh:mm[:ss]
+ */
+ if ((p = strchr(tmp, ':')) == NULL) {
+
+ /*
+ * No time string involved
+ */
+ T->tm_hour = T->tm_min = T->tm_sec = 0;
+ parse_datesub(tmp, &T->tm_mday, &T->tm_mon, &T->tm_year);
+ } else {
+ char datestr[64], timestr[64];
+
+ /*
+ * Let's chip off the time string
+ */
+ if ((q = strpbrk(p, " \t")) != NULL) { /* Time first? */
+ int l = q - str;
+
+ 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..137427f
--- /dev/null
+++ b/usr.sbin/pw/psdate.h
@@ -0,0 +1,40 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _PSDATE_H_
+#define _PSDATE_H_
+
+#include <time.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+time_t parse_date __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..9be66b5
--- /dev/null
+++ b/usr.sbin/pw/pw.8
@@ -0,0 +1,929 @@
+.\" Copyright (C) 1996
+.\" David L. Nugent. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd December 9, 1996
+.Dt PW 8
+.Os
+.Sh NAME
+.Nm pw
+.Nd create, remove, modify & display system users and groups
+.Sh SYNOPSIS
+.Nm
+.Op Fl V Ar etcdir
+.Ar useradd
+.Op name|uid
+.Op Fl C Ar config
+.Op Fl q
+.Op Fl n Ar name
+.Op Fl u Ar uid
+.Op Fl c Ar comment
+.Op Fl d Ar dir
+.Op Fl e Ar date
+.Op Fl p Ar date
+.Op Fl g Ar group
+.Op Fl G Ar grouplist
+.Op Fl m
+.Op Fl k Ar dir
+.Op Fl w Ar method
+.Op Fl s Ar shell
+.Op Fl o
+.Op Fl L Ar class
+.Op Fl h Ar fd
+.Op Fl N
+.Op Fl P
+.Op Fl Y
+.Nm
+.Op Fl V Ar etcdir
+.Ar useradd
+.Op name|uid
+.Fl D
+.Op Fl C Ar config
+.Op Fl q
+.Op Fl b Ar dir
+.Op Fl e Ar days
+.Op Fl p Ar days
+.Op Fl g Ar group
+.Op Fl G Ar grouplist
+.Op Fl k Ar dir
+.Op Fl u Ar min , Ns Ar max
+.Op Fl i Ar min , Ns Ar max
+.Op Fl w Ar method
+.Op Fl s Ar shell
+.Op Fl y Ar path
+.Nm
+.Op Fl V Ar etcdir
+.Ar userdel
+.Op name|uid
+.Op Fl n Ar name
+.Op Fl u Ar uid
+.Op Fl r
+.Op Fl Y
+.Nm
+.Op Fl V Ar etcdir
+.Ar usermod
+.Op name|uid
+.Op Fl C Ar config
+.Op Fl q
+.Op Fl n Ar name
+.Op Fl u Ar uid
+.Op Fl c Ar comment
+.Op Fl d Ar dir
+.Op Fl e Ar date
+.Op Fl p Ar date
+.Op Fl g Ar group
+.Op Fl G Ar grouplist
+.Op Fl l Ar name
+.Op Fl m
+.Op Fl k Ar dir
+.Op Fl w Ar method
+.Op Fl s Ar shell
+.Op Fl L Ar class
+.Op Fl h Ar fd
+.Op Fl N
+.Op Fl P
+.Op Fl Y
+.Nm
+.Op Fl V Ar etcdir
+.Ar usershow
+.Op name|uid
+.Op Fl n Ar name
+.Op Fl u Ar uid
+.Op Fl F
+.Op Fl P
+.Op Fl 7
+.Op Fl a
+.Nm
+.Op Fl V Ar etcdir
+.Ar usernext
+.Op Fl C Ar config
+.Op Fl q
+.Nm
+.Op Fl V Ar etcdir
+.Ar groupadd
+.Op group|gid
+.Op Fl C Ar config
+.Op Fl q
+.Op Fl n Ar group
+.Op Fl g Ar gid
+.Op Fl M Ar members
+.Op Fl o
+.Op Fl h Ar fd
+.Op Fl N
+.Op Fl P
+.Op Fl Y
+.Nm
+.Op Fl V Ar etcdir
+.Ar groupdel
+.Op group|gid
+.Op Fl n Ar name
+.Op Fl g Ar gid
+.Op Fl Y
+.Nm
+.Op Fl V Ar etcdir
+.Ar groupmod
+.Op group|gid
+.Op Fl C Ar config
+.Op Fl q
+.Op Fl n Ar name
+.Op Fl g Ar gid
+.Op Fl l Ar name
+.Op Fl M Ar members
+.Op Fl m Ar newmembers
+.Op Fl h Ar fd
+.Op Fl N
+.Op Fl P
+.Op Fl Y
+.Nm
+.Op Fl V Ar etcdir
+.Ar groupshow
+.Op group|gid
+.Op Fl n Ar name
+.Op Fl g Ar gid
+.Op Fl F
+.Op Fl P
+.Op Fl a
+.Nm
+.Op Fl V Ar etcdir
+.Ar groupnext
+.Op Fl C Ar config
+.Op Fl q
+.Nm
+.Op Fl V Ar etcdir
+.Ar lock
+.Op name|uid
+.Op Fl C Ar config
+.Op Fl q
+.Nm
+.Op Fl V Ar etcdir
+.Ar unlock
+.Op name|uid
+.Op Fl C Ar config
+.Op Fl q
+.Sh DESCRIPTION
+.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
+.Sh USER OPTIONS
+The following options apply to the
+.Ar useradd
+and
+.Ar usermod
+commands:
+.Pp
+.Bl -tag -width "-G grouplist"
+.It Fl n Ar name
+Specify the user/account name.
+.It Fl u Ar uid
+Specify the user/account numeric id.
+.Pp
+Usually, you only need to provide one or the other of these options, as the account
+name will imply the uid, or vice versa.
+However, there are times when you need to provide both.
+For example, when changing the uid of an existing user with
+.Ar usermod ,
+or overriding the default uid when creating a new account.
+If you wish
+.Nm
+to automatically allocate the uid to a new user with
+.Ar useradd ,
+then you should
+.Em not
+use the
+.Fl u
+option.
+You may also provide either the account or userid immediately after the
+.Ar useradd ,
+.Ar userdel ,
+.Ar usermod
+or
+.Ar usershow
+keywords on the command line without using the
+.Fl n
+or
+.Fl u
+options.
+.El
+.Pp
+.Bl -tag -width "-G grouplist"
+.It Fl c Ar comment
+This field sets the contents of the passwd GECOS field, which normally contains up
+to four comma-separated fields containing the user's full name, office or location,
+and work and home phone numbers.
+These sub-fields are used by convention only, however, and are optional.
+If this field is to contain spaces, you need to quote the comment itself with double
+quotes
+.Ql \&" .
+Avoid using commas in this field as these are used as sub-field separators, and the
+colon
+.Ql \&:
+character also cannot be used as this is the field separator for the passwd
+file itself.
+.It Fl d Ar dir
+This option sets the account's home directory.
+Normally, you will only use this if the home directory is to be different from the
+default determined from
+.Pa /etc/pw.conf
+- normally
+.Pa /home
+with the account name as a subdirectory.
+.It Fl e Ar date
+Set the account's expiration date.
+Format of the date is either a UNIX time in decimal, or a date in
+.Ql dd-mmm-yy[yy]
+format, where dd is the day, mmm is the month, either in numeric or alphabetic format
+('Jan', 'Feb', etc) and year is either a two or four digit year.
+This option also accepts a relative date in the form
+.Ql \&+n[mhdwoy]
+where
+.Ql \&n
+is a decimal, octal (leading 0) or hexadecimal (leading 0x) digit followed by the
+number of Minutes, Hours, Days, Weeks, Months or Years from the current date at
+which the expiration date is to be set.
+.It Fl p Ar date
+Set the account's password expiration date.
+This field is similar to the account expiration date option, except that it
+applies to forced password changes.
+This is set in the same manner as the
+.Fl e
+option.
+.It Fl g Ar group
+Set the account's primary group to the given group.
+.Ar group
+may be defined by either its name or group number.
+.It Fl G Ar grouplist
+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
+and
+.Xr passwd 5
+for more information on user login classes.
+.It Fl m
+This option instructs
+.Nm
+to attempt to create the user's home directory.
+While primarily useful when adding a new account with
+.Ar useradd ,
+this may also be of use when moving an existing user's home directory elsewhere on
+the 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
+.Fl m
+is used on an account with
+.Ar usermod ,
+existing configuration files in the user's home directory are
+.Em not
+overwritten from the skeleton files.
+.Pp
+When a user's home directory is created, it will by default be a subdirectory of the
+.Ar basehome
+directory as specified by the
+.Fl b
+option (see below), bearing the name of the new account.
+This can be overridden by the
+.Fl d
+option on the command line, if desired.
+.It Fl k Ar dir
+Set the
+.Ar skeleton
+directory, from which basic startup and configuration files are copied when
+the user's home directory is created.
+This option only has meaning when used with the
+.Fl d
+or
+.Fl m
+flags.
+.It Fl s Ar shell
+Set or changes the user's login shell to
+.Ar shell .
+If the path to the shell program is omitted,
+.Nm
+searches the
+.Ar shellpath
+specified in
+.Pa /etc/pw.conf
+and fills it in as appropriate.
+Note that unless you have a specific reason to do so, you should avoid
+specifying the path - this will allow
+.Nm
+to validate that the program exists and is executable.
+Specifying a full path (or supplying a blank "" shell) avoids this check
+and allows for such entries as
+.Pa /nonexistent
+that should be set for accounts not intended for interactive login.
+.It Fl h Ar fd
+This option provides a special interface by which interactive scripts can
+set an account password using
+.Nm .
+Because the command line and environment are fundamentally insecure mechanisms
+by which programs can accept information,
+.Nm
+will only allow setting of account and group passwords via a file descriptor
+(usually a pipe between an interactive script and the program).
+.Ar sh ,
+.Ar bash ,
+.Ar ksh
+and
+.Ar perl
+all possess mechanisms by which this can be done.
+Alternatively,
+.Nm
+will prompt for the user's password if
+.Fl h Ar 0
+is given, nominating
+.Em stdin
+as the file descriptor on which to read the password.
+Note that this password will be read only once and is intended
+for use by a script rather than for interactive use.
+If you wish to have new password confirmation along the lines of
+.Xr passwd 1 ,
+this must be implemented as part of an interactive script that calls
+.Nm .
+.Pp
+If a value of
+.Ql \&-
+is given as the argument
+.Ar fd ,
+then the password will be set to
+.Ql \&* ,
+rendering the account inaccessible via password-based login.
+.El
+.Pp
+It is possible to use
+.Ar useradd
+to create a new account that duplicates an existing user id.
+While this is normally considered an error and will be rejected, the
+.Fl o
+option overrides the check for duplicates and allows the duplication of
+the user id.
+This may be useful if you allow the same user to login under
+different contexts (different group allocations, different home
+directory, different shell) while providing basically the same
+permissions for access to the user's files in each account.
+.Pp
+The
+.Ar useradd
+command also has the ability to set new user and group defaults by using the
+.Fl D
+option.
+Instead of adding a new user,
+.Nm
+writes a new set of defaults to its configuration file,
+.Pa /etc/pw.conf .
+When using the
+.Fl D
+option, you must not use either
+.Fl n Ar name
+or
+.Fl u Ar uid
+or an error will result.
+Use of
+.Fl D
+changes the meaning of several command line switches in the
+.Ar useradd
+command.
+These are:
+.Bl -tag -width "-G grouplist"
+.It Fl D
+Set default values in
+.Pa /etc/pw.conf
+configuration file, or a different named configuration file if the
+.Fl C Ar config
+option is used.
+.It Fl b Ar dir
+Set the root directory in which user home directories are created.
+The default value for this is
+.Pa /home ,
+but it may be set elsewhere as desired.
+.It Fl e Ar days
+Set the default account expiration period in days.
+Unlike use without
+.Fl D ,
+the argument must be numeric, which specifies the number of days after creation when
+the account is to expire.
+A value of 0 suppresses automatic calculation of the expiry date.
+.It Fl p Ar days
+Set the default password expiration period in days.
+.It Fl g Ar group
+Set the default group for new users.
+If a blank group is specified using
+.Fl g Ar \&"" ,
+then new users will be allocated their own private primary group
+with the same name as their login name.
+If a group is supplied, either its name or uid may be given as an argument.
+.It Fl G Ar grouplist
+Set the default groups in which new users are granted membership.
+This is a separate set of groups from the primary group, and you should avoid
+nominating the same group as both primary and extra groups.
+In other words, these extra groups determine membership in groups
+.Em other than
+the primary group.
+.Ar grouplist
+is a comma-separated list of group names or ids, and are always
+stored in
+.Pa /etc/pw.conf
+by their symbolic names.
+.It Fl L Ar class
+This option sets the default login class for new users.
+.It Fl k Ar dir
+Set the default
+.Em skeleton
+directory, from which prototype shell and other initialization files are copied when
+.Nm
+creates a user's home directory.
+.It Xo
+.Fl u Ar min , Ns Ar max ,
+.Fl i Ar min , Ns Ar max
+.Xc
+These options set the minimum and maximum user and group ids allocated for new accounts
+and groups created by
+.Nm .
+The default values for each is 1000 minimum and 32000 maximum.
+.Ar min
+and
+.Ar max
+are both numbers, where max must be greater than min, and both must be between 0
+and 32767.
+In general, user and group ids less than 100 are reserved for use by the system,
+and numbers greater than 32000 may also be reserved for special purposes (used by
+some system daemons).
+.It Fl w Ar method
+The
+.Fl w
+option sets the default method used to set passwords for newly created user accounts.
+.Ar method
+is one of:
+.Pp
+.Bl -tag -width random -offset indent -compact
+.It no
+disable login on newly created accounts
+.It yes
+force the password to be the account name
+.It none
+force a blank password
+.It random
+generate a random password
+.El
+.Pp
+The
+.Ql \&random
+or
+.Ql \&no
+methods are the most secure; in the former case,
+.Nm
+generates a password and prints it to stdout, which is suitable where you issue
+users with passwords to access their accounts rather than having the user nominate
+their own (possibly poorly chosen) password.
+The
+.Ql \&no
+method requires that the superuser use
+.Xr passwd 1
+to render the account accessible with a password.
+.It Fl y Ar path
+This sets the pathname of the database used by 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
+.Fl n Ar name
+and
+.Fl u Ar uid
+options have already been covered above.
+The additional option is:
+.Bl -tag -width "-G grouplist"
+.It Fl r
+This tells
+.Nm
+to remove the user's home directory and all of its contents.
+.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
+.Fl P
+option is used, then
+.Nm
+outputs the account details in a more human readable form.
+If the
+.Fl 7
+option is used, the account details are shown in v7 format.
+The
+.Fl a
+option lists all users currently on file.
+Using
+.Fl F
+forces
+.Nm
+to print the details of an account even if it does not exist.
+.Pp
+The command
+.Ar usernext
+returns the next available user and group ids separated by a colon.
+This is normally of interest only to interactive scripts or front-ends
+that use
+.Nm .
+.Sh GROUP OPTIONS
+The
+.Fl C
+and
+.Fl q
+options (explained at the start of the previous section) are available
+with the group manipulation commands.
+Other common options to all group-related commands are:
+.Bl -tag -width "-m newmembers"
+.It Fl n Ar name
+Specify the group name.
+.It Fl g Ar gid
+Specify the group numeric id.
+.Pp
+As with the account name and id fields, you will usually only need
+to supply one of these, as the group name implies the uid and vice
+versa.
+You will only need to use both when setting a specific group id
+against a new group or when changing the uid of an existing group.
+.It Fl M Ar memberlist
+This option provides an alternative way to add existing users to a
+new group (in groupadd) or replace an existing membership list (in
+groupmod).
+.Ar memberlist
+is a comma separated list of valid and existing user names or uids.
+.It Fl m Ar newmembers
+Similar to
+.Fl M ,
+this option allows the
+.Em addition
+of existing users to a group without replacing the existing list of
+members.
+Login names or user ids may be used, and duplicate users are
+silently eliminated.
+.El
+.Pp
+.Ar groupadd
+also has a
+.Fl o
+option that allows allocation of an existing group id to a new group.
+The default action is to reject an attempt to add a group, and this option overrides
+the check for duplicate group ids.
+There is rarely any need to duplicate a group id.
+.Pp
+The
+.Ar groupmod
+command adds one additional option:
+.Pp
+.Bl -tag -width "-m newmembers"
+.It Fl l Ar name
+This option allows changing of an existing group name to
+.Ql \&name .
+The new name must not already exist, and any attempt to duplicate an existing group
+name will be rejected.
+.El
+.Pp
+Options for
+.Ar groupshow
+are the same as for
+.Ar usershow ,
+with the
+.Fl g Ar gid
+replacing
+.Fl u Ar uid
+to specify the group id.
+The
+.Fl 7
+option does not apply to the
+.Ar groupshow
+command.
+.Pp
+The command
+.Ar groupnext
+returns the next available group id on standard output.
+.Sh USER LOCKING
+.Nm Pw
+supports a simple password locking mechanism for users; it works by
+prepending the string
+.Ql *LOCKED*
+to the beginning of the password field in
+.Pa master.passwd
+to prevent successful authentication.
+.Pp
+The
+.Ar lock
+and
+.Ar unlock
+commands take a user name or uid of the account to lock or unlock,
+respectively. The
+.Fl V ,
+.Fl C ,
+and
+.Fl q
+options as described above are accepted by these commands.
+.Sh DIAGNOSTICS
+.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
+.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..01ba43a
--- /dev/null
+++ b/usr.sbin/pw/pw.c
@@ -0,0 +1,451 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <err.h>
+#include <fcntl.h>
+#include <locale.h>
+#include <paths.h>
+#include <sys/wait.h>
+#include "pw.h"
+
+#if !defined(_PATH_YP)
+#define _PATH_YP "/var/yp/"
+#endif
+const char *Modes[] = {
+ "add", "del", "mod", "show", "next",
+ NULL};
+const char *Which[] = {"user", "group", NULL};
+static const char *Combo1[] = {
+ "useradd", "userdel", "usermod", "usershow", "usernext",
+ "lock", "unlock",
+ "groupadd", "groupdel", "groupmod", "groupshow", "groupnext",
+ NULL};
+static const char *Combo2[] = {
+ "adduser", "deluser", "moduser", "showuser", "nextuser",
+ "lock", "unlock",
+ "addgroup", "delgroup", "modgroup", "showgroup", "nextgroup",
+ NULL};
+
+struct pwf PWF =
+{
+ 0,
+ setpwent,
+ endpwent,
+ getpwent,
+ getpwuid,
+ getpwnam,
+ pwdb,
+ setgrent,
+ endgrent,
+ getgrent,
+ getgrgid,
+ getgrnam,
+ grdb
+
+};
+struct pwf VPWF =
+{
+ 1,
+ vsetpwent,
+ vendpwent,
+ vgetpwent,
+ vgetpwuid,
+ vgetpwnam,
+ vpwdb,
+ vsetgrent,
+ vendgrent,
+ vgetgrent,
+ vgetgrgid,
+ vgetgrnam,
+ vgrdb
+};
+
+static struct cargs arglist;
+
+static int getindex(const char *words[], const char *word);
+static void cmdhelp(int mode, int which);
+
+
+int
+main(int argc, char *argv[])
+{
+ int ch;
+ int mode = -1;
+ int which = -1;
+ char *config = NULL;
+ struct userconf *cnf;
+
+ static const char *opts[W_NUM][M_NUM] =
+ {
+ { /* user */
+ "V:C:qn:u:c:d:e:p:g:G:mk:s:oL:i:w:h: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",
+ "V:C:q",
+ "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);
+
+ (void)setlocale(LC_ALL, "");
+
+ /*
+ * Break off the first couple of words to determine what exactly
+ * we're being asked to do
+ */
+ while (argc > 1) {
+ int tmp;
+
+ if (*argv[1] == '-') {
+ /*
+ * Special case, allow pw -V<dir> <operation> [args] for scripts etc.
+ */
+ if (argv[1][1] == 'V') {
+ optarg = &argv[1][2];
+ if (*optarg == '\0') {
+ optarg = argv[2];
+ ++argv;
+ --argc;
+ }
+ addarg(&arglist, 'V', optarg);
+ } else
+ break;
+ }
+ else if (mode == -1 && (tmp = getindex(Modes, argv[1])) != -1)
+ mode = tmp;
+ else if (which == -1 && (tmp = getindex(Which, argv[1])) != -1)
+ which = tmp;
+ else if ((mode == -1 && which == -1) &&
+ ((tmp = getindex(Combo1, argv[1])) != -1 ||
+ (tmp = getindex(Combo2, argv[1])) != -1)) {
+ which = tmp / M_NUM;
+ mode = tmp % M_NUM;
+ } else if (strcmp(argv[1], "help") == 0 && argv[2] == NULL)
+ cmdhelp(mode, which);
+ else if (which != -1 && mode != -1)
+ addarg(&arglist, 'n', argv[1]);
+ else
+ errx(EX_USAGE, "unknown keyword `%s'", argv[1]);
+ ++argv;
+ --argc;
+ }
+
+ /*
+ * Bail out unless the user is specific!
+ */
+ if (mode == -1 || which == -1)
+ cmdhelp(mode, which);
+
+ /*
+ * We know which mode we're in and what we're about to do, so now
+ * let's dispatch the remaining command line args in a genric way.
+ */
+ optarg = NULL;
+
+ while ((ch = getopt(argc, argv, opts[which][mode])) != -1) {
+ if (ch == '?')
+ errx(EX_USAGE, "unknown switch");
+ else
+ addarg(&arglist, ch, optarg);
+ optarg = NULL;
+ }
+
+ /*
+ * Must be root to attempt an update
+ */
+ if (geteuid() != 0 && mode != M_PRINT && mode != M_NEXT && getarg(&arglist, 'N')==NULL)
+ errx(EX_NOPERM, "you must be root to run this program");
+
+ /*
+ * We should immediately look for the -q 'quiet' switch so that we
+ * don't bother with extraneous errors
+ */
+ if (getarg(&arglist, 'q') != NULL)
+ freopen(_PATH_DEVNULL, "w", stderr);
+
+ /*
+ * Set our base working path if not overridden
+ */
+
+ config = getarg(&arglist, 'C') ? getarg(&arglist, 'C')->val : NULL;
+
+ if (getarg(&arglist, 'V') != NULL) {
+ char * etcpath = getarg(&arglist, 'V')->val;
+ if (*etcpath) {
+ if (config == NULL) { /* Only override config location if -C not specified */
+ config = malloc(MAXPATHLEN);
+ snprintf(config, MAXPATHLEN, "%s/pw.conf", etcpath);
+ }
+ memcpy(&PWF, &VPWF, sizeof PWF);
+ setpwdir(etcpath);
+ setgrdir(etcpath);
+ }
+ }
+
+ /*
+ * Now, let's do the common initialisation
+ */
+ cnf = read_userconfig(config);
+
+ ch = funcs[which] (cnf, mode, &arglist);
+
+ /*
+ * If everything went ok, and we've been asked to update
+ * the NIS maps, then do it now
+ */
+ if (ch == EXIT_SUCCESS && getarg(&arglist, 'Y') != NULL) {
+ pid_t pid;
+
+ fflush(NULL);
+ if (chdir(_PATH_YP) == -1)
+ warn("chdir(" _PATH_YP ")");
+ else if ((pid = fork()) == -1)
+ warn("fork()");
+ else if (pid == 0) {
+ /* Is make anywhere else? */
+ execlp("/usr/bin/make", "make", (char *)NULL);
+ _exit(1);
+ } else {
+ int i;
+ waitpid(pid, &i, 0);
+ if ((i = WEXITSTATUS(i)) != 0)
+ errx(ch, "make exited with status %d", i);
+ else
+ pw_log(cnf, mode, which, "NIS maps updated");
+ }
+ }
+ return ch;
+}
+
+
+static int
+getindex(const char *words[], const char *word)
+{
+ int i = 0;
+
+ while (words[i]) {
+ if (strcmp(words[i], word) == 0)
+ return i;
+ i++;
+ }
+ return -1;
+}
+
+
+/*
+ * This is probably an overkill for a cmdline help system, but it reflects
+ * the complexity of the command line.
+ */
+
+static void
+cmdhelp(int mode, int which)
+{
+ if (which == -1)
+ fprintf(stderr, "usage:\n pw [user|group|lock|unlock] [add|del|mod|show|next] [help|switches/values]\n");
+ else if (mode == -1)
+ fprintf(stderr, "usage:\n pw %s [add|del|mod|show|next] [help|switches/values]\n", Which[which]);
+ else {
+
+ /*
+ * We need to give mode specific help
+ */
+ static const char *help[W_NUM][M_NUM] =
+ {
+ {
+ "usage: pw useradd [name] [switches]\n"
+ "\t-V etcdir alternate /etc location\n"
+ "\t-C config configuration file\n"
+ "\t-q quiet operation\n"
+ " Adding users:\n"
+ "\t-n name login name\n"
+ "\t-u uid user id\n"
+ "\t-c comment user name/comment\n"
+ "\t-d directory home directory\n"
+ "\t-e date account expiry date\n"
+ "\t-p date password expiry date\n"
+ "\t-g grp initial group\n"
+ "\t-G grp1,grp2 additional groups\n"
+ "\t-m [ -k dir ] create and set up home\n"
+ "\t-s shell name of login shell\n"
+ "\t-o duplicate uid ok\n"
+ "\t-L class user class\n"
+ "\t-h fd read password on fd\n"
+ "\t-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"
+ "\t-q quiet operation\n",
+ "usage pw: lock [switches]\n"
+ "\t-V etcdir alternate /etc locations\n"
+ "\t-C config configuration file\n"
+ "\t-q quiet operation\n",
+ "usage pw: unlock [switches]\n"
+ "\t-V etcdir alternate /etc locations\n"
+ "\t-C config configuration file\n"
+ "\t-q quiet operation\n"
+ },
+ {
+ "usage: pw groupadd [group|gid] [switches]\n"
+ "\t-V etcdir alternate /etc location\n"
+ "\t-C config configuration file\n"
+ "\t-q quiet operation\n"
+ "\t-n group group name\n"
+ "\t-g gid group id\n"
+ "\t-M usr1,usr2 add users as group members\n"
+ "\t-o duplicate gid ok\n"
+ "\t-Y update NIS maps\n"
+ "\t-N no update\n",
+ "usage: pw groupdel [group|gid] [switches]\n"
+ "\t-V etcdir alternate /etc location\n"
+ "\t-n name group name\n"
+ "\t-g gid group id\n"
+ "\t-Y update NIS maps\n",
+ "usage: pw groupmod [group|gid] [switches]\n"
+ "\t-V etcdir alternate /etc location\n"
+ "\t-C config configuration file\n"
+ "\t-q quiet operation\n"
+ "\t-F force add if not exists\n"
+ "\t-n name group name\n"
+ "\t-g gid group id\n"
+ "\t-M usr1,usr2 replaces users as group members\n"
+ "\t-m usr1,usr2 add users as group members\n"
+ "\t-l name new group name\n"
+ "\t-Y update NIS maps\n"
+ "\t-N no update\n",
+ "usage: pw groupshow [group|gid] [switches]\n"
+ "\t-V etcdir alternate /etc location\n"
+ "\t-n name group name\n"
+ "\t-g gid group id\n"
+ "\t-F force print\n"
+ "\t-P prettier format\n"
+ "\t-a print all accounting groups\n",
+ "usage: pw groupnext [switches]\n"
+ "\t-V etcdir alternate /etc location\n"
+ "\t-C config configuration file\n"
+ "\t-q quiet operation\n"
+ }
+ };
+
+ fprintf(stderr, "%s", help[which][mode]);
+ }
+ exit(EXIT_FAILURE);
+}
+
+struct carg *
+getarg(struct cargs * _args, int ch)
+{
+ struct carg *c = LIST_FIRST(_args);
+
+ while (c != NULL && c->ch != ch)
+ c = LIST_NEXT(c, list);
+ return c;
+}
+
+struct carg *
+addarg(struct cargs * _args, int ch, char *argstr)
+{
+ struct carg *ca = malloc(sizeof(struct carg));
+
+ if (ca == NULL)
+ errx(EX_OSERR, "out of memory");
+ ca->ch = ch;
+ ca->val = argstr;
+ LIST_INSERT_HEAD(_args, ca, list);
+ return ca;
+}
diff --git a/usr.sbin/pw/pw.conf.5 b/usr.sbin/pw/pw.conf.5
new file mode 100644
index 0000000..9511309
--- /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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd December 9, 1996
+.Dt PW.CONF 5
+.Os
+.Sh NAME
+.Nm pw.conf
+.Nd format of the pw.conf configuration file
+.Sh DESCRIPTION
+The file
+.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 Ns 's
+.Fl m
+option causes the user's home directory to be created and populated
+using the files contained in the
+.Ar skeleton
+directory.
+.Pp
+To send an initial email to new users, the
+.Ar newmail
+keyword may be used to specify a path name to a file containing
+the message body of the message to be sent.
+To avoid sending mail when accounts are created, leave this entry
+blank or specify
+.Ql \&no .
+.Pp
+The
+.Ar logfile
+option allows logging of password file modifications into the
+nominated log file.
+To avoid creating or adding to such a logfile, then leave this
+field blank or specify
+.Ql \&no .
+.Pp
+The
+.Ar home
+keyword is mandatory.
+This specifies the location of the directory in which all new user
+home directories are created.
+.Pp
+.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.
+.Sh LIMITS
+The maximum line length of
+.Pa /etc/pw.conf
+is 1024 characters.
+Longer lines will be skipped and treated
+as comments.
+.Sh FILES
+.Bl -tag -width /etc/master.passwd -compact
+.It Pa /etc/pw.conf
+.It Pa /etc/passwd
+.It Pa /etc/master.passwd
+.It Pa /etc/group
+.El
+.Sh SEE ALSO
+.Xr passwd 1 ,
+.Xr group 5 ,
+.Xr login.conf 5 ,
+.Xr passwd 5 ,
+.Xr pw 8
diff --git a/usr.sbin/pw/pw.h b/usr.sbin/pw/pw.h
new file mode 100644
index 0000000..f541b26
--- /dev/null
+++ b/usr.sbin/pw/pw.h
@@ -0,0 +1,131 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <pwd.h>
+#include <grp.h>
+#include <sys/queue.h>
+#include <sysexits.h>
+
+#include "psdate.h"
+#include "pwupd.h"
+
+enum _mode
+{
+ M_ADD,
+ M_DELETE,
+ M_UPDATE,
+ M_PRINT,
+ M_NEXT,
+ M_LOCK,
+ M_UNLOCK,
+ M_NUM
+};
+
+enum _which
+{
+ W_USER,
+ W_GROUP,
+ W_NUM
+};
+
+struct carg
+{
+ int ch;
+ char *val;
+ LIST_ENTRY(carg) list;
+};
+
+extern LIST_HEAD(cargs, carg) arglist;
+
+struct userconf
+{
+ int default_password; /* Default password for new users? */
+ int reuse_uids; /* Reuse uids? */
+ int reuse_gids; /* Reuse gids? */
+ char *nispasswd; /* Path to NIS version of the passwd file */
+ char *dotdir; /* Where to obtain skeleton files */
+ char *newmail; /* Mail to send to new accounts */
+ char *logfile; /* Where to log changes */
+ char *home; /* Where to create home directory */
+ char *shelldir; /* Where shells are located */
+ char **shells; /* List of shells */
+ char *shell_default; /* Default shell */
+ char *default_group; /* Default group number */
+ char **groups; /* Default (additional) groups */
+ char *default_class; /* Default user class */
+ uid_t min_uid, max_uid; /* Allowed range of uids */
+ gid_t min_gid, max_gid; /* Allowed range of gids */
+ int expire_days; /* Days to expiry */
+ int password_days; /* Days to password expiry */
+ int numgroups; /* (internal) size of default_group array */
+};
+
+#define _PATH_PW_CONF "/etc/pw.conf"
+#define _UC_MAXLINE 1024
+#define _UC_MAXSHELLS 32
+
+struct userconf *read_userconfig(char const * file);
+int write_userconfig(char const * file);
+struct carg *addarg(struct cargs * _args, int ch, char *argstr);
+struct carg *getarg(struct cargs * _args, int ch);
+
+int pw_user(struct userconf * cnf, int mode, struct cargs * _args);
+int pw_group(struct userconf * cnf, int mode, struct cargs * _args);
+char *pw_checkname(u_char *name, int gecos);
+
+int addpwent(struct passwd * pwd);
+int delpwent(struct passwd * pwd);
+int chgpwent(char const * login, struct passwd * pwd);
+int fmtpwent(char *buf, struct passwd * pwd);
+
+int addnispwent(const char *path, struct passwd *pwd);
+int delnispwent(const char *path, const char *login);
+int chgnispwent(const char *path, const char *login, struct passwd *pwd);
+
+int addgrent(struct group * grp);
+int delgrent(struct group * grp);
+int chggrent(char const * login, struct group * grp);
+
+int boolean_val(char const * str, int dflt);
+char const *boolean_str(int val);
+char *newstr(char const * p);
+
+void pw_log(struct userconf * cnf, int mode, int which, char const * fmt,...) __printflike(4, 5);
+char *pw_pwcrypt(char *password);
+
+extern const char *Modes[];
+extern const char *Which[];
diff --git a/usr.sbin/pw/pw_conf.c b/usr.sbin/pw/pw_conf.c
new file mode 100644
index 0000000..9377239
--- /dev/null
+++ b/usr.sbin/pw/pw_conf.c
@@ -0,0 +1,501 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <string.h>
+#include <ctype.h>
+#include <fcntl.h>
+
+#include "pw.h"
+
+#define debugging 0
+
+enum {
+ _UC_NONE,
+ _UC_DEFAULTPWD,
+ _UC_REUSEUID,
+ _UC_REUSEGID,
+ _UC_NISPASSWD,
+ _UC_DOTDIR,
+ _UC_NEWMAIL,
+ _UC_LOGFILE,
+ _UC_HOMEROOT,
+ _UC_SHELLPATH,
+ _UC_SHELLS,
+ _UC_DEFAULTSHELL,
+ _UC_DEFAULTGROUP,
+ _UC_EXTRAGROUPS,
+ _UC_DEFAULTCLASS,
+ _UC_MINUID,
+ _UC_MAXUID,
+ _UC_MINGID,
+ _UC_MAXGID,
+ _UC_EXPIRE,
+ _UC_PASSWORD,
+ _UC_FIELDS
+};
+
+static char bourne_shell[] = "sh";
+
+static char *system_shells[_UC_MAXSHELLS] =
+{
+ bourne_shell,
+ "csh",
+ "tcsh"
+};
+
+static char const *booltrue[] =
+{
+ "yes", "true", "1", "on", NULL
+};
+static char const *boolfalse[] =
+{
+ "no", "false", "0", "off", NULL
+};
+
+static struct userconf config =
+{
+ 0, /* Default password for new users? (nologin) */
+ 0, /* Reuse uids? */
+ 0, /* Reuse gids? */
+ NULL, /* NIS version of the passwd file */
+ "/usr/share/skel", /* Where to obtain skeleton files */
+ NULL, /* Mail to send to new accounts */
+ "/var/log/userlog", /* Where to log changes */
+ "/home", /* Where to create home directory */
+ "/bin", /* Where shells are located */
+ system_shells, /* List of shells (first is default) */
+ bourne_shell, /* Default shell */
+ NULL, /* Default group name */
+ NULL, /* Default (additional) groups */
+ NULL, /* Default login class */
+ 1000, 32000, /* Allowed range of uids */
+ 1000, 32000, /* Allowed range of gids */
+ 0, /* Days until account expires */
+ 0, /* Days until password expires */
+ 0 /* size of default_group array */
+};
+
+static char const *comments[_UC_FIELDS] =
+{
+ "#\n# pw.conf - user/group configuration defaults\n#\n",
+ "\n# Password for new users? no=nologin yes=loginid none=blank random=random\n",
+ "\n# Reuse gaps in uid sequence? (yes or no)\n",
+ "\n# Reuse gaps in gid sequence? (yes or no)\n",
+ "\n# Path to the NIS passwd file (blank or 'no' for none)\n",
+ "\n# Obtain default dotfiles from this directory\n",
+ "\n# Mail this file to new user (/etc/newuser.msg or no)\n",
+ "\n# Log add/change/remove information in this file\n",
+ "\n# Root directory in which $HOME directory is created\n",
+ "\n# Colon separated list of directories containing valid shells\n",
+ "\n# Comma separated list of available shells (without paths)\n",
+ "\n# Default shell (without path)\n",
+ "\n# Default group (leave blank for new group per user)\n",
+ "\n# Extra groups for new users\n",
+ "\n# Default login class for new users\n",
+ "\n# Range of valid default user ids\n",
+ NULL,
+ "\n# Range of valid default group ids\n",
+ NULL,
+ "\n# Days after which account expires (0=disabled)\n",
+ "\n# Days after which password expires (0=disabled)\n"
+};
+
+static char const *kwds[] =
+{
+ "",
+ "defaultpasswd",
+ "reuseuids",
+ "reusegids",
+ "nispasswd",
+ "skeleton",
+ "newmail",
+ "logfile",
+ "home",
+ "shellpath",
+ "shells",
+ "defaultshell",
+ "defaultgroup",
+ "extragroups",
+ "defaultclass",
+ "minuid",
+ "maxuid",
+ "mingid",
+ "maxgid",
+ "expire_days",
+ "password_days",
+ NULL
+};
+
+static char *
+unquote(char const * str)
+{
+ if (str && (*str == '"' || *str == '\'')) {
+ char *p = strchr(str + 1, *str);
+
+ if (p != NULL)
+ *p = '\0';
+ return (char *) (*++str ? str : NULL);
+ }
+ return (char *) str;
+}
+
+int
+boolean_val(char const * str, int dflt)
+{
+ if ((str = unquote(str)) != NULL) {
+ int i;
+
+ for (i = 0; booltrue[i]; i++)
+ if (strcmp(str, booltrue[i]) == 0)
+ return 1;
+ for (i = 0; boolfalse[i]; i++)
+ if (strcmp(str, boolfalse[i]) == 0)
+ return 0;
+
+ /*
+ * Special cases for defaultpassword
+ */
+ if (strcmp(str, "random") == 0)
+ return -1;
+ if (strcmp(str, "none") == 0)
+ return -2;
+ }
+ return dflt;
+}
+
+char const *
+boolean_str(int val)
+{
+ if (val == -1)
+ return "random";
+ else if (val == -2)
+ return "none";
+ else
+ return val ? booltrue[0] : boolfalse[0];
+}
+
+char *
+newstr(char const * p)
+{
+ char *q = NULL;
+
+ if ((p = unquote(p)) != NULL) {
+ int l = strlen(p) + 1;
+
+ if ((q = malloc(l)) != NULL)
+ memcpy(q, p, l);
+ }
+ return q;
+}
+
+#define LNBUFSZ 1024
+
+
+struct userconf *
+read_userconfig(char const * file)
+{
+ FILE *fp;
+
+ extendarray(&config.groups, &config.numgroups, 200);
+ memset(config.groups, 0, config.numgroups * sizeof(char *));
+ if (file == NULL)
+ file = _PATH_PW_CONF;
+ if ((fp = fopen(file, "r")) != NULL) {
+ int buflen = LNBUFSZ;
+ char *buf = malloc(buflen);
+
+ nextline:
+ while (fgets(buf, buflen, fp) != NULL) {
+ char *p;
+
+ while ((p = strchr(buf, '\n')) == NULL) {
+ int l;
+ if (extendline(&buf, &buflen, buflen + LNBUFSZ) == -1) {
+ int ch;
+ while ((ch = fgetc(fp)) != '\n' && ch != EOF);
+ goto nextline; /* Ignore it */
+ }
+ l = strlen(buf);
+ if (fgets(buf + l, buflen - l, fp) == NULL)
+ break; /* Unterminated last line */
+ }
+
+ if (p != NULL)
+ *p = '\0';
+
+ if (*buf && (p = strtok(buf, " \t\r\n=")) != NULL && *p != '#') {
+ static char const toks[] = " \t\r\n,=";
+ char *q = strtok(NULL, toks);
+ int i = 0;
+
+ while (i < _UC_FIELDS && strcmp(p, kwds[i]) != 0)
+ ++i;
+#if debugging
+ if (i == _UC_FIELDS)
+ printf("Got unknown kwd `%s' val=`%s'\n", p, q ? q : "");
+ else
+ printf("Got kwd[%s]=%s\n", p, q);
+#endif
+ switch (i) {
+ case _UC_DEFAULTPWD:
+ config.default_password = boolean_val(q, 1);
+ break;
+ case _UC_REUSEUID:
+ config.reuse_uids = boolean_val(q, 0);
+ break;
+ case _UC_REUSEGID:
+ config.reuse_gids = boolean_val(q, 0);
+ break;
+ case _UC_NISPASSWD:
+ config.nispasswd = (q == NULL || !boolean_val(q, 1))
+ ? NULL : newstr(q);
+ break;
+ case _UC_DOTDIR:
+ config.dotdir = (q == NULL || !boolean_val(q, 1))
+ ? NULL : newstr(q);
+ break;
+ case _UC_NEWMAIL:
+ config.newmail = (q == NULL || !boolean_val(q, 1))
+ ? NULL : newstr(q);
+ break;
+ case _UC_LOGFILE:
+ config.logfile = (q == NULL || !boolean_val(q, 1))
+ ? NULL : newstr(q);
+ break;
+ case _UC_HOMEROOT:
+ config.home = (q == NULL || !boolean_val(q, 1))
+ ? "/home" : newstr(q);
+ break;
+ case _UC_SHELLPATH:
+ config.shelldir = (q == NULL || !boolean_val(q, 1))
+ ? "/bin" : newstr(q);
+ break;
+ case _UC_SHELLS:
+ for (i = 0; i < _UC_MAXSHELLS && q != NULL; i++, q = strtok(NULL, toks))
+ system_shells[i] = newstr(q);
+ if (i > 0)
+ while (i < _UC_MAXSHELLS)
+ system_shells[i++] = NULL;
+ break;
+ case _UC_DEFAULTSHELL:
+ config.shell_default = (q == NULL || !boolean_val(q, 1))
+ ? (char *) bourne_shell : newstr(q);
+ break;
+ case _UC_DEFAULTGROUP:
+ q = unquote(q);
+ config.default_group = (q == NULL || !boolean_val(q, 1) || GETGRNAM(q) == NULL)
+ ? NULL : newstr(q);
+ break;
+ case _UC_EXTRAGROUPS:
+ for (i = 0; q != NULL; q = strtok(NULL, toks)) {
+ if (extendarray(&config.groups, &config.numgroups, i + 2) != -1)
+ config.groups[i++] = newstr(q);
+ }
+ if (i > 0)
+ while (i < config.numgroups)
+ config.groups[i++] = NULL;
+ break;
+ case _UC_DEFAULTCLASS:
+ config.default_class = (q == NULL || !boolean_val(q, 1))
+ ? NULL : newstr(q);
+ break;
+ case _UC_MINUID:
+ if ((q = unquote(q)) != NULL && isdigit(*q))
+ config.min_uid = (uid_t) atol(q);
+ break;
+ case _UC_MAXUID:
+ if ((q = unquote(q)) != NULL && isdigit(*q))
+ config.max_uid = (uid_t) atol(q);
+ break;
+ case _UC_MINGID:
+ if ((q = unquote(q)) != NULL && isdigit(*q))
+ config.min_gid = (gid_t) atol(q);
+ break;
+ case _UC_MAXGID:
+ if ((q = unquote(q)) != NULL && isdigit(*q))
+ config.max_gid = (gid_t) atol(q);
+ break;
+ case _UC_EXPIRE:
+ if ((q = unquote(q)) != NULL && isdigit(*q))
+ config.expire_days = atoi(q);
+ break;
+ case _UC_PASSWORD:
+ if ((q = unquote(q)) != NULL && isdigit(*q))
+ config.password_days = atoi(q);
+ break;
+ case _UC_FIELDS:
+ case _UC_NONE:
+ break;
+ }
+ }
+ }
+ free(buf);
+ fclose(fp);
+ }
+ return &config;
+}
+
+
+int
+write_userconfig(char const * file)
+{
+ int fd;
+
+ if (file == NULL)
+ file = _PATH_PW_CONF;
+
+ if ((fd = open(file, O_CREAT | O_RDWR | O_TRUNC | O_EXLOCK, 0644)) != -1) {
+ FILE *fp;
+
+ if ((fp = fdopen(fd, "w")) == NULL)
+ close(fd);
+ else {
+ int i, j, k;
+ int len = LNBUFSZ;
+ char *buf = malloc(len);
+
+ for (i = _UC_NONE; i < _UC_FIELDS; i++) {
+ int quote = 1;
+ char const *val = buf;
+
+ *buf = '\0';
+ switch (i) {
+ case _UC_DEFAULTPWD:
+ val = boolean_str(config.default_password);
+ break;
+ case _UC_REUSEUID:
+ val = boolean_str(config.reuse_uids);
+ break;
+ case _UC_REUSEGID:
+ val = boolean_str(config.reuse_gids);
+ break;
+ case _UC_NISPASSWD:
+ val = config.nispasswd ? config.nispasswd : "";
+ quote = 0;
+ break;
+ case _UC_DOTDIR:
+ val = config.dotdir ? config.dotdir : boolean_str(0);
+ break;
+ case _UC_NEWMAIL:
+ val = config.newmail ? config.newmail : boolean_str(0);
+ break;
+ case _UC_LOGFILE:
+ val = config.logfile ? config.logfile : boolean_str(0);
+ break;
+ case _UC_HOMEROOT:
+ val = config.home;
+ break;
+ case _UC_SHELLPATH:
+ val = config.shelldir;
+ break;
+ case _UC_SHELLS:
+ for (j = k = 0; j < _UC_MAXSHELLS && system_shells[j] != NULL; j++) {
+ char lbuf[64];
+ int l = snprintf(lbuf, sizeof lbuf, "%s\"%s\"", k ? "," : "", system_shells[j]);
+ if (l < 0)
+ l = 0;
+ if (l + k + 1 < len || extendline(&buf, &len, len + LNBUFSZ) != -1) {
+ strcpy(buf + k, lbuf);
+ k += l;
+ }
+ }
+ quote = 0;
+ break;
+ case _UC_DEFAULTSHELL:
+ val = config.shell_default ? config.shell_default : bourne_shell;
+ break;
+ case _UC_DEFAULTGROUP:
+ val = config.default_group ? config.default_group : "";
+ break;
+ case _UC_EXTRAGROUPS:
+ extendarray(&config.groups, &config.numgroups, 200);
+ for (j = k = 0; j < config.numgroups && config.groups[j] != NULL; j++) {
+ char lbuf[64];
+ int l = snprintf(lbuf, sizeof lbuf, "%s\"%s\"", k ? "," : "", config.groups[j]);
+ if (l < 0)
+ l = 0;
+ if (l + k + 1 < len || extendline(&buf, &len, len + 1024) != -1) {
+ strcpy(buf + k, lbuf);
+ k += l;
+ }
+ }
+ quote = 0;
+ break;
+ case _UC_DEFAULTCLASS:
+ val = config.default_class ? config.default_class : "";
+ break;
+ case _UC_MINUID:
+ sprintf(buf, "%lu", (unsigned long) config.min_uid);
+ quote = 0;
+ break;
+ case _UC_MAXUID:
+ sprintf(buf, "%lu", (unsigned long) config.max_uid);
+ quote = 0;
+ break;
+ case _UC_MINGID:
+ sprintf(buf, "%lu", (unsigned long) config.min_gid);
+ quote = 0;
+ break;
+ case _UC_MAXGID:
+ sprintf(buf, "%lu", (unsigned long) config.max_gid);
+ quote = 0;
+ break;
+ case _UC_EXPIRE:
+ sprintf(buf, "%d", config.expire_days);
+ quote = 0;
+ break;
+ case _UC_PASSWORD:
+ sprintf(buf, "%d", config.password_days);
+ quote = 0;
+ break;
+ case _UC_NONE:
+ break;
+ }
+
+ if (comments[i])
+ fputs(comments[i], fp);
+
+ if (*kwds[i]) {
+ if (quote)
+ fprintf(fp, "%s = \"%s\"\n", kwds[i], val);
+ else
+ fprintf(fp, "%s = %s\n", kwds[i], val);
+#if debugging
+ printf("WROTE: %s = %s\n", kwds[i], val);
+#endif
+ }
+ }
+ free(buf);
+ return fclose(fp) != EOF;
+ }
+ }
+ return 0;
+}
diff --git a/usr.sbin/pw/pw_group.c b/usr.sbin/pw/pw_group.c
new file mode 100644
index 0000000..c9af998
--- /dev/null
+++ b/usr.sbin/pw/pw_group.c
@@ -0,0 +1,352 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <ctype.h>
+#include <err.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "pw.h"
+#include "bitmap.h"
+
+
+static int print_group(struct group * grp, int pretty);
+static gid_t gr_gidpolicy(struct userconf * cnf, struct cargs * args);
+
+int
+pw_group(struct userconf * cnf, int mode, struct cargs * args)
+{
+ int rc;
+ struct carg *a_name = getarg(args, 'n');
+ struct carg *a_gid = getarg(args, 'g');
+ struct carg *arg;
+ struct group *grp = NULL;
+ int grmembers = 0;
+ char **members = NULL;
+
+ static struct group fakegroup =
+ {
+ "nogroup",
+ "*",
+ -1,
+ NULL
+ };
+
+ if (mode == M_LOCK || mode == M_UNLOCK)
+ errx(EX_USAGE, "'lock' command is not available for groups");
+
+ /*
+ * With M_NEXT, we only need to return the
+ * next gid to stdout
+ */
+ if (mode == M_NEXT) {
+ gid_t next = gr_gidpolicy(cnf, args);
+ if (getarg(args, 'q'))
+ return next;
+ printf("%ld\n", (long)next);
+ return EXIT_SUCCESS;
+ }
+
+ if (mode == M_PRINT && getarg(args, 'a')) {
+ int pretty = getarg(args, 'P') != NULL;
+
+ SETGRENT();
+ while ((grp = GETGRENT()) != NULL)
+ print_group(grp, pretty);
+ ENDGRENT();
+ return EXIT_SUCCESS;
+ }
+ if (a_gid == NULL) {
+ if (a_name == NULL)
+ errx(EX_DATAERR, "group name or id required");
+
+ if (mode != M_ADD && grp == NULL && isdigit((unsigned char)*a_name->val)) {
+ (a_gid = a_name)->ch = 'g';
+ a_name = NULL;
+ }
+ }
+ grp = (a_name != NULL) ? GETGRNAM(a_name->val) : GETGRGID((gid_t) atoi(a_gid->val));
+
+ if (mode == M_UPDATE || mode == M_DELETE || mode == M_PRINT) {
+ if (a_name == NULL && grp == NULL) /* Try harder */
+ grp = GETGRGID(atoi(a_gid->val));
+
+ if (grp == NULL) {
+ if (mode == M_PRINT && getarg(args, 'F')) {
+ char *fmems[1];
+ fmems[0] = NULL;
+ fakegroup.gr_name = a_name ? a_name->val : "nogroup";
+ fakegroup.gr_gid = a_gid ? (gid_t) atol(a_gid->val) : -1;
+ fakegroup.gr_mem = fmems;
+ return print_group(&fakegroup, getarg(args, 'P') != NULL);
+ }
+ errx(EX_DATAERR, "unknown group `%s'", a_name ? a_name->val : a_gid->val);
+ }
+ if (a_name == NULL) /* Needed later */
+ a_name = addarg(args, 'n', grp->gr_name);
+
+ /*
+ * Handle deletions now
+ */
+ if (mode == M_DELETE) {
+ gid_t gid = grp->gr_gid;
+
+ rc = delgrent(grp);
+ if (rc == -1)
+ err(EX_IOERR, "group '%s' not available (NIS?)", grp->gr_name);
+ else if (rc != 0) {
+ warn("group update");
+ return EX_IOERR;
+ }
+ pw_log(cnf, mode, W_GROUP, "%s(%ld) removed", a_name->val, (long) gid);
+ return EXIT_SUCCESS;
+ } else if (mode == M_PRINT)
+ return print_group(grp, getarg(args, 'P') != NULL);
+
+ if (a_gid)
+ grp->gr_gid = (gid_t) atoi(a_gid->val);
+
+ if ((arg = getarg(args, 'l')) != NULL)
+ grp->gr_name = pw_checkname((u_char *)arg->val, 0);
+ } else {
+ if (a_name == NULL) /* Required */
+ errx(EX_DATAERR, "group name required");
+ else if (grp != NULL) /* Exists */
+ errx(EX_DATAERR, "group name `%s' already exists", a_name->val);
+
+ extendarray(&members, &grmembers, 200);
+ members[0] = NULL;
+ grp = &fakegroup;
+ grp->gr_name = pw_checkname((u_char *)a_name->val, 0);
+ grp->gr_passwd = "*";
+ grp->gr_gid = gr_gidpolicy(cnf, args);
+ grp->gr_mem = members;
+ }
+
+ /*
+ * This allows us to set a group password Group passwords is an
+ * antique idea, rarely used and insecure (no secure database) Should
+ * be discouraged, but it is apparently still supported by some
+ * software.
+ */
+
+ if ((arg = getarg(args, 'h')) != NULL) {
+ 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((unsigned char)*p) || (pwd = getpwuid((uid_t) atoi(p))) == NULL)
+ errx(EX_NOUSER, "user `%s' does not exist", p);
+ }
+ /*
+ * Check for duplicates
+ */
+ for (j = 0; j < i && strcmp(members[j], pwd->pw_name)!=0; j++)
+ ;
+ if (j == i && extendarray(&members, &grmembers, i + 2) != -1)
+ members[i++] = newstr(pwd->pw_name);
+ }
+ while (i < grmembers)
+ members[i++] = NULL;
+ grp->gr_mem = members;
+ }
+
+ if (getarg(args, 'N') != NULL)
+ return print_group(grp, getarg(args, 'P') != NULL);
+
+ if (mode == M_ADD && (rc = addgrent(grp)) != 0) {
+ if (rc == -1)
+ warnx("group '%s' already exists", grp->gr_name);
+ else
+ warn("group update");
+ return EX_IOERR;
+ } else if (mode == M_UPDATE && (rc = chggrent(a_name->val, grp)) != 0) {
+ if (rc == -1)
+ warnx("group '%s' not available (NIS?)", grp->gr_name);
+ else
+ warn("group update");
+ return EX_IOERR;
+ }
+ /* grp may have been invalidated */
+ if ((grp = GETGRNAM(a_name->val)) == NULL)
+ errx(EX_SOFTWARE, "group disappeared during update");
+
+ pw_log(cnf, mode, W_GROUP, "%s(%ld)", grp->gr_name, (long) grp->gr_gid);
+
+ if (members)
+ free(members);
+
+ return EXIT_SUCCESS;
+}
+
+
+static gid_t
+gr_gidpolicy(struct userconf * cnf, struct cargs * args)
+{
+ struct group *grp;
+ gid_t gid = (gid_t) - 1;
+ struct carg *a_gid = getarg(args, 'g');
+
+ /*
+ * Check the given gid, if any
+ */
+ if (a_gid != NULL) {
+ gid = (gid_t) atol(a_gid->val);
+
+ if ((grp = GETGRGID(gid)) != NULL && getarg(args, 'o') == NULL)
+ errx(EX_DATAERR, "gid `%ld' has already been allocated", (long) grp->gr_gid);
+ } else {
+ struct bitmap bm;
+
+ /*
+ * We need to allocate the next available gid under one of
+ * two policies a) Grab the first unused gid b) Grab the
+ * highest possible unused gid
+ */
+ if (cnf->min_gid >= cnf->max_gid) { /* Sanity claus^H^H^H^Hheck */
+ cnf->min_gid = 1000;
+ cnf->max_gid = 32000;
+ }
+ bm = bm_alloc(cnf->max_gid - cnf->min_gid + 1);
+
+ /*
+ * Now, let's fill the bitmap from the password file
+ */
+ SETGRENT();
+ while ((grp = GETGRENT()) != NULL)
+ if ((gid_t)grp->gr_gid >= (gid_t)cnf->min_gid &&
+ (gid_t)grp->gr_gid <= (gid_t)cnf->max_gid)
+ bm_setbit(&bm, grp->gr_gid - cnf->min_gid);
+ ENDGRENT();
+
+ /*
+ * Then apply the policy, with fallback to reuse if necessary
+ */
+ if (cnf->reuse_gids)
+ gid = (gid_t) (bm_firstunset(&bm) + cnf->min_gid);
+ else {
+ gid = (gid_t) (bm_lastset(&bm) + 1);
+ if (!bm_isset(&bm, gid))
+ gid += cnf->min_gid;
+ else
+ gid = (gid_t) (bm_firstunset(&bm) + cnf->min_gid);
+ }
+
+ /*
+ * Another sanity check
+ */
+ if (gid < cnf->min_gid || gid > cnf->max_gid)
+ errx(EX_SOFTWARE, "unable to allocate a new gid - range fully used");
+ bm_dealloc(&bm);
+ }
+ return gid;
+}
+
+
+static int
+print_group(struct group * grp, int pretty)
+{
+ if (!pretty) {
+ int buflen = 0;
+ char *buf = NULL;
+
+ fmtgrent(&buf, &buflen, grp);
+ fputs(buf, stdout);
+ free(buf);
+ } else {
+ int i;
+
+ printf("Group Name: %-15s #%lu\n"
+ " Members: ",
+ grp->gr_name, (long) grp->gr_gid);
+ for (i = 0; grp->gr_mem[i]; i++)
+ printf("%s%s", i ? "," : "", grp->gr_mem[i]);
+ fputs("\n\n", stdout);
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/usr.sbin/pw/pw_log.c b/usr.sbin/pw/pw_log.c
new file mode 100644
index 0000000..fc85828
--- /dev/null
+++ b/usr.sbin/pw/pw_log.c
@@ -0,0 +1,68 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <fcntl.h>
+
+#include "pw.h"
+
+static FILE *logfile = NULL;
+
+void
+pw_log(struct userconf * cnf, int mode, int which, char const * fmt,...)
+{
+ if (cnf->logfile && *cnf->logfile) {
+ if (logfile == NULL) { /* With umask==0 we need to control file access modes on create */
+ int fd = open(cnf->logfile, O_WRONLY | O_CREAT | O_APPEND, 0600);
+
+ if (fd != -1)
+ logfile = fdopen(fd, "a");
+ }
+ if (logfile != NULL) {
+ va_list argp;
+ int l;
+ time_t now = time(NULL);
+ struct tm *t = localtime(&now);
+ char nfmt[256];
+ char *name;
+
+ if ((name = getenv("LOGNAME")) == NULL && (name = getenv("USER")) == NULL)
+ name = "unknown";
+ /* ISO 8601 International Standard Date format */
+ strftime(nfmt, sizeof nfmt, "%Y-%m-%d %T ", t);
+ l = strlen(nfmt);
+ sprintf(nfmt + strlen(nfmt), "[%s:%s%s] %s\n", name, Which[which], Modes[mode], fmt);
+ va_start(argp, fmt);
+ vfprintf(logfile, nfmt, argp);
+ va_end(argp);
+ fflush(logfile);
+ }
+ }
+}
diff --git a/usr.sbin/pw/pw_nis.c b/usr.sbin/pw/pw_nis.c
new file mode 100644
index 0000000..74a3ed0
--- /dev/null
+++ b/usr.sbin/pw/pw_nis.c
@@ -0,0 +1,72 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "pw.h"
+
+static int
+pw_nisupdate(const char * path, struct passwd * pwd, char const * user, int mode)
+{
+ char pfx[32];
+ char pwbuf[PWBUFSZ];
+ int l = sprintf(pfx, "%s:", user);
+
+ /*
+ * Update the passwd file first
+ */
+ if (pwd == NULL)
+ *pwbuf = '\0';
+ else
+ fmtpwentry(pwbuf, pwd, PWF_MASTER);
+ return fileupdate(path, 0600, pwbuf, pfx, l, mode) != 0;
+}
+
+int
+addnispwent(const char *path, struct passwd * pwd)
+{
+ return pw_nisupdate(path, pwd, pwd->pw_name, UPD_CREATE);
+}
+
+int
+chgnispwent(const char *path, char const * login, struct passwd * pwd)
+{
+ return pw_nisupdate(path, pwd, login, UPD_REPLACE);
+}
+
+int
+delnispwent(const char *path, const char *login)
+{
+ return pw_nisupdate(path, NULL, login, UPD_DELETE);
+}
diff --git a/usr.sbin/pw/pw_user.c b/usr.sbin/pw/pw_user.c
new file mode 100644
index 0000000..456c954
--- /dev/null
+++ b/usr.sbin/pw/pw_user.c
@@ -0,0 +1,1267 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <ctype.h>
+#include <err.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <dirent.h>
+#include <paths.h>
+#include <termios.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <unistd.h>
+#include <utmp.h>
+#include <login_cap.h>
+#if defined(USE_MD5RAND)
+#include <md5.h>
+#endif
+#include "pw.h"
+#include "bitmap.h"
+
+#if (MAXLOGNAME-1) > UT_NAMESIZE
+#define LOGNAMESIZE UT_NAMESIZE
+#else
+#define LOGNAMESIZE (MAXLOGNAME-1)
+#endif
+
+static char locked_str[] = "*LOCKED*";
+
+static int print_user(struct passwd * pwd, int pretty, int v7);
+static uid_t pw_uidpolicy(struct userconf * cnf, struct cargs * args);
+static uid_t pw_gidpolicy(struct userconf * cnf, struct cargs * args, char *nam, gid_t prefer);
+static time_t pw_pwdpolicy(struct userconf * cnf, struct cargs * args);
+static time_t pw_exppolicy(struct userconf * cnf, struct cargs * args);
+static char *pw_homepolicy(struct userconf * cnf, struct cargs * args, char const * user);
+static char *pw_shellpolicy(struct userconf * cnf, struct cargs * args, char *newshell);
+static char *pw_password(struct userconf * cnf, struct cargs * args, char const * user);
+static char *shell_path(char const * path, char *shells[], char *sh);
+static void rmat(uid_t uid);
+static void rmopie(char const * name);
+
+/*-
+ * -C config configuration file
+ * -q quiet operation
+ * -n name login name
+ * -u uid user id
+ * -c comment user name/comment
+ * -d directory home directory
+ * -e date account expiry date
+ * -p date password expiry date
+ * -g grp primary group
+ * -G grp1,grp2 additional groups
+ * -m [ -k dir ] create and set up home
+ * -s shell name of login shell
+ * -o duplicate uid ok
+ * -L class user class
+ * -l name new login name
+ * -h fd password filehandle
+ * -F force print or add
+ * Setting defaults:
+ * -D set user defaults
+ * -b dir default home root dir
+ * -e period default expiry period
+ * -p period default password change period
+ * -g group default group
+ * -G grp1,grp2.. default additional groups
+ * -L class default login class
+ * -k dir default home skeleton
+ * -s shell default shell
+ * -w method default password method
+ */
+
+int
+pw_user(struct userconf * cnf, int mode, struct cargs * args)
+{
+ int rc, edited = 0;
+ char *p = NULL;
+ char *passtmp;
+ struct carg *a_name;
+ struct carg *a_uid;
+ struct carg *arg;
+ struct passwd *pwd = NULL;
+ struct group *grp;
+ struct stat st;
+ char line[_PASSWORD_LEN+1];
+ FILE *fp;
+
+ static struct passwd fakeuser =
+ {
+ NULL,
+ "*",
+ -1,
+ -1,
+ 0,
+ "",
+ "User &",
+ "/nonexistent",
+ "/bin/sh",
+ 0
+#if defined(__FreeBSD__)
+ ,0
+#endif
+ };
+
+
+ /*
+ * With M_NEXT, we only need to return the
+ * next uid to stdout
+ */
+ if (mode == M_NEXT)
+ {
+ uid_t next = pw_uidpolicy(cnf, args);
+ if (getarg(args, 'q'))
+ return next;
+ printf("%ld:", (long)next);
+ pw_group(cnf, mode, args);
+ return EXIT_SUCCESS;
+ }
+
+ /*
+ * We can do all of the common legwork here
+ */
+
+ if ((arg = getarg(args, 'b')) != NULL) {
+ cnf->home = arg->val;
+ }
+
+ /*
+ * If we'll need to use it or we're updating it,
+ * then create the base home directory if necessary
+ */
+ if (arg != NULL || getarg(args, 'm') != NULL) {
+ int l = strlen(cnf->home);
+
+ if (l > 1 && cnf->home[l-1] == '/') /* Shave off any trailing path delimiter */
+ cnf->home[--l] = '\0';
+
+ if (l < 2 || *cnf->home != '/') /* Check for absolute path name */
+ errx(EX_DATAERR, "invalid base directory for home '%s'", cnf->home);
+
+ if (stat(cnf->home, &st) == -1) {
+ char dbuf[MAXPATHLEN];
+
+ /*
+ * This is a kludge especially for Joerg :)
+ * If the home directory would be created in the root partition, then
+ * we really create it under /usr which is likely to have more space.
+ * But we create a symlink from cnf->home -> "/usr" -> cnf->home
+ */
+ if (strchr(cnf->home+1, '/') == NULL) {
+ strcpy(dbuf, "/usr");
+ strncat(dbuf, cnf->home, MAXPATHLEN-5);
+ if (mkdir(dbuf, 0755) != -1 || errno == EEXIST) {
+ chown(dbuf, 0, 0);
+ symlink(dbuf, cnf->home);
+ }
+ /* If this falls, fall back to old method */
+ }
+ 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) {
+ if (!*(p = arg->val)) /* Handle empty group list specially */
+ cnf->default_group = "";
+ else {
+ if ((grp = GETGRNAM(p)) == NULL) {
+ if (!isdigit((unsigned char)*p) || (grp = GETGRGID((gid_t) atoi(p))) == NULL)
+ errx(EX_NOUSER, "group `%s' does not exist", p);
+ }
+ cnf->default_group = newstr(grp->gr_name);
+ }
+ }
+ if ((arg = getarg(args, 'L')) != NULL)
+ cnf->default_class = pw_checkname((u_char *)arg->val, 0);
+
+ if ((arg = getarg(args, 'G')) != NULL && arg->val) {
+ int i = 0;
+
+ for (p = strtok(arg->val, ", \t"); p != NULL; p = strtok(NULL, ", \t")) {
+ if ((grp = GETGRNAM(p)) == NULL) {
+ if (!isdigit((unsigned char)*p) || (grp = GETGRGID((gid_t) atoi(p))) == NULL)
+ errx(EX_NOUSER, "group `%s' does not exist", p);
+ }
+ if (extendarray(&cnf->groups, &cnf->numgroups, i + 2) != -1)
+ cnf->groups[i++] = newstr(grp->gr_name);
+ }
+ while (i < cnf->numgroups)
+ cnf->groups[i++] = NULL;
+ }
+
+ if ((arg = getarg(args, 'k')) != NULL) {
+ if (stat(cnf->dotdir = arg->val, &st) == -1 || !S_ISDIR(st.st_mode))
+ errx(EX_OSFILE, "skeleton `%s' is not a directory or does not exist", cnf->dotdir);
+ }
+
+ if ((arg = getarg(args, 's')) != NULL)
+ cnf->shell_default = arg->val;
+
+ if ((arg = getarg(args, 'w')) != NULL)
+ cnf->default_password = boolean_val(arg->val, cnf->default_password);
+ if (mode == M_ADD && getarg(args, 'D')) {
+ if (getarg(args, 'n') != NULL)
+ errx(EX_DATAERR, "can't combine `-D' with `-n name'");
+ if ((arg = getarg(args, 'u')) != NULL && (p = strtok(arg->val, ", \t")) != NULL) {
+ if ((cnf->min_uid = (uid_t) atoi(p)) == 0)
+ cnf->min_uid = 1000;
+ if ((p = strtok(NULL, " ,\t")) == NULL || (cnf->max_uid = (uid_t) atoi(p)) < cnf->min_uid)
+ cnf->max_uid = 32000;
+ }
+ if ((arg = getarg(args, 'i')) != NULL && (p = strtok(arg->val, ", \t")) != NULL) {
+ if ((cnf->min_gid = (gid_t) atoi(p)) == 0)
+ cnf->min_gid = 1000;
+ if ((p = strtok(NULL, " ,\t")) == NULL || (cnf->max_gid = (gid_t) atoi(p)) < cnf->min_gid)
+ cnf->max_gid = 32000;
+ }
+
+ arg = getarg(args, 'C');
+ if (write_userconfig(arg ? arg->val : NULL))
+ return EXIT_SUCCESS;
+ warn("config update");
+ return EX_IOERR;
+ }
+
+ if (mode == M_PRINT && getarg(args, 'a')) {
+ int pretty = getarg(args, 'P') != NULL;
+ int v7 = getarg(args, '7') != NULL;
+
+ SETPWENT();
+ while ((pwd = GETPWENT()) != NULL)
+ print_user(pwd, pretty, v7);
+ ENDPWENT();
+ return EXIT_SUCCESS;
+ }
+
+ if ((a_name = getarg(args, 'n')) != NULL)
+ pwd = GETPWNAM(pw_checkname((u_char *)a_name->val, 0));
+ a_uid = getarg(args, 'u');
+
+ if (a_uid == NULL) {
+ if (a_name == NULL)
+ errx(EX_DATAERR, "user name or id required");
+
+ /*
+ * Determine whether 'n' switch is name or uid - we don't
+ * really don't really care which we have, but we need to
+ * know.
+ */
+ if (mode != M_ADD && pwd == NULL
+ && strspn(a_name->val, "0123456789") == strlen(a_name->val)
+ && atoi(a_name->val) > 0) { /* Assume uid */
+ (a_uid = a_name)->ch = 'u';
+ a_name = NULL;
+ }
+ }
+
+ /*
+ * Update, delete & print require that the user exists
+ */
+ if (mode == M_UPDATE || mode == M_DELETE ||
+ mode == M_PRINT || mode == M_LOCK || mode == M_UNLOCK) {
+
+ if (a_name == NULL && pwd == NULL) /* Try harder */
+ pwd = GETPWUID(atoi(a_uid->val));
+
+ if (pwd == NULL) {
+ if (mode == M_PRINT && getarg(args, 'F')) {
+ fakeuser.pw_name = a_name ? a_name->val : "nouser";
+ fakeuser.pw_uid = a_uid ? (uid_t) atol(a_uid->val) : -1;
+ return print_user(&fakeuser,
+ getarg(args, 'P') != NULL,
+ getarg(args, '7') != NULL);
+ }
+ if (a_name == NULL)
+ errx(EX_NOUSER, "no such uid `%s'", a_uid->val);
+ errx(EX_NOUSER, "no such user `%s'", a_name->val);
+ }
+
+ if (a_name == NULL) /* May be needed later */
+ a_name = addarg(args, 'n', newstr(pwd->pw_name));
+
+ /*
+ * The M_LOCK and M_UNLOCK functions simply add or remove
+ * a "*LOCKED*" prefix from in front of the password to
+ * prevent it decoding correctly, and therefore prevents
+ * access. Of course, this only prevents access via
+ * password authentication (not ssh, kerberos or any
+ * other method that does not use the UNIX password) but
+ * that is a known limitation.
+ */
+
+ if (mode == M_LOCK) {
+ if (strncmp(pwd->pw_passwd, locked_str, sizeof(locked_str)-1) == 0)
+ errx(EX_DATAERR, "user '%s' is already locked", pwd->pw_name);
+ passtmp = malloc(strlen(pwd->pw_passwd) + sizeof(locked_str));
+ if (passtmp == NULL) /* disaster */
+ errx(EX_UNAVAILABLE, "out of memory");
+ strcpy(passtmp, locked_str);
+ strcat(passtmp, pwd->pw_passwd);
+ pwd->pw_passwd = passtmp;
+ edited = 1;
+ } else if (mode == M_UNLOCK) {
+ if (strncmp(pwd->pw_passwd, locked_str, sizeof(locked_str)-1) != 0)
+ errx(EX_DATAERR, "user '%s' is not locked", pwd->pw_name);
+ pwd->pw_passwd += sizeof(locked_str)-1;
+ edited = 1;
+ } else if (mode == M_DELETE) {
+ /*
+ * Handle deletions now
+ */
+ char file[MAXPATHLEN];
+ char home[MAXPATHLEN];
+ uid_t uid = pwd->pw_uid;
+
+ if (strcmp(pwd->pw_name, "root") == 0)
+ errx(EX_DATAERR, "cannot remove user 'root'");
+
+ if (!PWALTDIR()) {
+ /*
+ * Remove opie record from /etc/opiekeys
+ */
+
+ rmopie(pwd->pw_name);
+
+ /*
+ * Remove crontabs
+ */
+ sprintf(file, "/var/cron/tabs/%s", pwd->pw_name);
+ if (access(file, F_OK) == 0) {
+ sprintf(file, "crontab -u %s -r", pwd->pw_name);
+ system(file);
+ }
+ }
+ /*
+ * Save these for later, since contents of pwd may be
+ * invalidated by deletion
+ */
+ sprintf(file, "%s/%s", _PATH_MAILDIR, pwd->pw_name);
+ strncpy(home, pwd->pw_dir, sizeof home);
+ home[sizeof home - 1] = '\0';
+
+ rc = delpwent(pwd);
+ if (rc == -1)
+ err(EX_IOERR, "user '%s' does not exist", pwd->pw_name);
+ else if (rc != 0) {
+ warn("passwd update");
+ return EX_IOERR;
+ }
+
+ if (cnf->nispasswd && *cnf->nispasswd=='/') {
+ rc = delnispwent(cnf->nispasswd, a_name->val);
+ if (rc == -1)
+ warnx("WARNING: user '%s' does not exist in NIS passwd", pwd->pw_name);
+ else if (rc != 0)
+ warn("WARNING: NIS passwd update");
+ /* non-fatal */
+ }
+
+ editgroups(a_name->val, NULL);
+
+ pw_log(cnf, mode, W_USER, "%s(%ld) account removed", a_name->val, (long) uid);
+
+ if (!PWALTDIR()) {
+ /*
+ * Remove mail file
+ */
+ remove(file);
+
+ /*
+ * Remove at jobs
+ */
+ if (getpwuid(uid) == NULL)
+ rmat(uid);
+
+ /*
+ * Remove home directory and contents
+ */
+ if (getarg(args, 'r') != NULL && *home == '/' && getpwuid(uid) == NULL) {
+ if (stat(home, &st) != -1) {
+ rm_r(home, uid);
+ pw_log(cnf, mode, W_USER, "%s(%ld) home '%s' %sremoved",
+ a_name->val, (long) uid, home,
+ stat(home, &st) == -1 ? "" : "not completely ");
+ }
+ }
+ }
+ return EXIT_SUCCESS;
+ } else if (mode == M_PRINT)
+ return print_user(pwd,
+ getarg(args, 'P') != NULL,
+ getarg(args, '7') != NULL);
+
+ /*
+ * The rest is edit code
+ */
+ if ((arg = getarg(args, 'l')) != NULL) {
+ if (strcmp(pwd->pw_name, "root") == 0)
+ errx(EX_DATAERR, "can't rename `root' account");
+ pwd->pw_name = pw_checkname((u_char *)arg->val, 0);
+ edited = 1;
+ }
+
+ if ((arg = getarg(args, 'u')) != NULL && isdigit((unsigned char)*arg->val)) {
+ pwd->pw_uid = (uid_t) atol(arg->val);
+ edited = 1;
+ if (pwd->pw_uid != 0 && strcmp(pwd->pw_name, "root") == 0)
+ errx(EX_DATAERR, "can't change uid of `root' account");
+ if (pwd->pw_uid == 0 && strcmp(pwd->pw_name, "root") != 0)
+ warnx("WARNING: account `%s' will have a uid of 0 (superuser access!)", pwd->pw_name);
+ }
+
+ if ((arg = getarg(args, 'g')) != NULL && pwd->pw_uid != 0) { /* Already checked this */
+ gid_t newgid = (gid_t) GETGRNAM(cnf->default_group)->gr_gid;
+ if (newgid != pwd->pw_gid) {
+ edited = 1;
+ pwd->pw_gid = newgid;
+ }
+ }
+
+ if ((arg = getarg(args, 'p')) != NULL) {
+ if (*arg->val == '\0' || strcmp(arg->val, "0") == 0) {
+ if (pwd->pw_change != 0) {
+ pwd->pw_change = 0;
+ edited = 1;
+ }
+ }
+ else {
+ time_t now = time(NULL);
+ time_t expire = parse_date(now, arg->val);
+
+ if (now == expire)
+ errx(EX_DATAERR, "invalid password change date `%s'", arg->val);
+ if (pwd->pw_change != expire) {
+ pwd->pw_change = expire;
+ edited = 1;
+ }
+ }
+ }
+
+ if ((arg = getarg(args, 'e')) != NULL) {
+ if (*arg->val == '\0' || strcmp(arg->val, "0") == 0) {
+ if (pwd->pw_expire != 0) {
+ pwd->pw_expire = 0;
+ edited = 1;
+ }
+ }
+ else {
+ time_t now = time(NULL);
+ time_t expire = parse_date(now, arg->val);
+
+ if (now == expire)
+ errx(EX_DATAERR, "invalid account expiry date `%s'", arg->val);
+ if (pwd->pw_expire != expire) {
+ pwd->pw_expire = expire;
+ edited = 1;
+ }
+ }
+ }
+
+ if ((arg = getarg(args, 's')) != NULL) {
+ char *shell = shell_path(cnf->shelldir, cnf->shells, arg->val);
+ if (shell == NULL)
+ shell = "";
+ if (strcmp(shell, pwd->pw_shell) != 0) {
+ pwd->pw_shell = shell;
+ edited = 1;
+ }
+ }
+
+ if (getarg(args, 'L')) {
+ if (cnf->default_class == NULL)
+ cnf->default_class = "";
+ if (strcmp(pwd->pw_class, cnf->default_class) != 0) {
+ pwd->pw_class = cnf->default_class;
+ edited = 1;
+ }
+ }
+
+ if ((arg = getarg(args, 'd')) != NULL) {
+ edited = strcmp(pwd->pw_dir, arg->val) != 0;
+ 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) {
+ login_cap_t *lc;
+
+ lc = login_getpwclass(pwd);
+ if (lc == NULL ||
+ login_setcryptfmt(lc, "md5", NULL) == NULL)
+ warn("setting crypt(3) format");
+ login_close(lc);
+ pwd->pw_passwd = pw_password(cnf, args, pwd->pw_name);
+ edited = 1;
+ }
+
+ } else {
+ login_cap_t *lc;
+
+ /*
+ * Add code
+ */
+
+ if (a_name == NULL) /* Required */
+ errx(EX_DATAERR, "login name required");
+ else if ((pwd = GETPWNAM(a_name->val)) != NULL) /* Exists */
+ errx(EX_DATAERR, "login name `%s' already exists", a_name->val);
+
+ /*
+ * Now, set up defaults for a new user
+ */
+ pwd = &fakeuser;
+ pwd->pw_name = a_name->val;
+ pwd->pw_class = cnf->default_class ? cnf->default_class : "";
+ pwd->pw_uid = pw_uidpolicy(cnf, args);
+ pwd->pw_gid = pw_gidpolicy(cnf, args, pwd->pw_name, (gid_t) pwd->pw_uid);
+ pwd->pw_change = pw_pwdpolicy(cnf, args);
+ pwd->pw_expire = pw_exppolicy(cnf, args);
+ pwd->pw_dir = pw_homepolicy(cnf, args, pwd->pw_name);
+ pwd->pw_shell = pw_shellpolicy(cnf, args, NULL);
+ lc = login_getpwclass(pwd);
+ if (lc == NULL || login_setcryptfmt(lc, "md5", NULL) == NULL)
+ warn("setting crypt(3) format");
+ login_close(lc);
+ pwd->pw_passwd = pw_password(cnf, args, pwd->pw_name);
+ edited = 1;
+
+ if (pwd->pw_uid == 0 && strcmp(pwd->pw_name, "root") != 0)
+ warnx("WARNING: new account `%s' has a uid of 0 (superuser access!)", pwd->pw_name);
+ }
+
+ /*
+ * Shared add/edit code
+ */
+ if ((arg = getarg(args, 'c')) != NULL) {
+ char *gecos = pw_checkname((u_char *)arg->val, 1);
+ if (strcmp(pwd->pw_gecos, gecos) != 0) {
+ pwd->pw_gecos = gecos;
+ edited = 1;
+ }
+ }
+
+ if ((arg = getarg(args, 'h')) != NULL) {
+ if (strcmp(arg->val, "-") == 0) {
+ if (!pwd->pw_passwd || *pwd->pw_passwd != '*') {
+ pwd->pw_passwd = "*"; /* No access */
+ edited = 1;
+ }
+ } else {
+ int fd = atoi(arg->val);
+ int b;
+ int istty = isatty(fd);
+ struct termios t;
+ login_cap_t *lc;
+
+ if (istty) {
+ if (tcgetattr(fd, &t) == -1)
+ istty = 0;
+ else {
+ struct termios n = t;
+
+ /* Disable echo */
+ n.c_lflag &= ~(ECHO);
+ tcsetattr(fd, TCSANOW, &n);
+ printf("%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);
+ lc = login_getpwclass(pwd);
+ if (lc == NULL ||
+ login_setcryptfmt(lc, "md5", NULL) == NULL)
+ warn("setting crypt(3) format");
+ login_close(lc);
+ pwd->pw_passwd = pw_pwcrypt(line);
+ edited = 1;
+ }
+ }
+
+ /*
+ * Special case: -N only displays & exits
+ */
+ if (getarg(args, 'N') != NULL)
+ return print_user(pwd,
+ getarg(args, 'P') != NULL,
+ getarg(args, '7') != NULL);
+
+ if (mode == M_ADD) {
+ edited = 1; /* Always */
+ rc = addpwent(pwd);
+ if (rc == -1) {
+ warnx("user '%s' already exists", pwd->pw_name);
+ return EX_IOERR;
+ } else if (rc != 0) {
+ warn("passwd file update");
+ return EX_IOERR;
+ }
+ if (cnf->nispasswd && *cnf->nispasswd=='/') {
+ rc = addnispwent(cnf->nispasswd, pwd);
+ if (rc == -1)
+ warnx("User '%s' already exists in NIS passwd", pwd->pw_name);
+ else
+ warn("NIS passwd update");
+ /* NOTE: we treat NIS-only update errors as non-fatal */
+ }
+ } else if (mode == M_UPDATE || mode == M_LOCK || mode == M_UNLOCK) {
+ if (edited) { /* Only updated this if required */
+ rc = chgpwent(a_name->val, pwd);
+ if (rc == -1) {
+ warnx("user '%s' does not exist (NIS?)", pwd->pw_name);
+ return EX_IOERR;
+ } else if (rc != 0) {
+ warn("passwd file update");
+ return EX_IOERR;
+ }
+ if ( cnf->nispasswd && *cnf->nispasswd=='/') {
+ rc = chgnispwent(cnf->nispasswd, a_name->val, pwd);
+ if (rc == -1)
+ warn("User '%s' not found in NIS passwd", pwd->pw_name);
+ else
+ warn("NIS passwd update");
+ /* NOTE: NIS-only update errors are not fatal */
+ }
+ }
+ }
+
+ /*
+ * Ok, user is created or changed - now edit group file
+ */
+
+ if (mode == M_ADD || getarg(args, 'G') != NULL)
+ editgroups(pwd->pw_name, cnf->groups);
+
+ /* go get a current version of pwd */
+ pwd = GETPWNAM(a_name->val);
+ if (pwd == NULL) {
+ /* This will fail when we rename, so special case that */
+ if (mode == M_UPDATE && (arg = getarg(args, 'l')) != NULL) {
+ a_name->val = arg->val; /* update new name */
+ pwd = GETPWNAM(a_name->val); /* refetch renamed rec */
+ }
+ }
+ if (pwd == NULL) /* can't go on without this */
+ errx(EX_NOUSER, "user '%s' disappeared during update", a_name->val);
+
+ grp = GETGRGID(pwd->pw_gid);
+ pw_log(cnf, mode, W_USER, "%s(%ld):%s(%ld):%s:%s:%s",
+ pwd->pw_name, (long) pwd->pw_uid,
+ grp ? grp->gr_name : "unknown", (long) (grp ? grp->gr_gid : -1),
+ pwd->pw_gecos, pwd->pw_dir, pwd->pw_shell);
+
+ /*
+ * If adding, let's touch and chown the user's mail file. This is not
+ * strictly necessary under BSD with a 0755 maildir but it also
+ * doesn't hurt anything to create the empty mailfile
+ */
+ if (mode == M_ADD) {
+ if (!PWALTDIR()) {
+ sprintf(line, "%s/%s", _PATH_MAILDIR, pwd->pw_name);
+ close(open(line, O_RDWR | O_CREAT, 0600)); /* Preserve contents &
+ * mtime */
+ chown(line, pwd->pw_uid, pwd->pw_gid);
+ }
+ }
+
+ /*
+ * Let's create and populate the user's home directory. Note
+ * that this also `works' for editing users if -m is used, but
+ * existing files will *not* be overwritten.
+ */
+ if (!PWALTDIR() && getarg(args, 'm') != NULL && pwd->pw_dir && *pwd->pw_dir == '/' && pwd->pw_dir[1]) {
+ copymkdir(pwd->pw_dir, cnf->dotdir, 0755, pwd->pw_uid, pwd->pw_gid);
+ pw_log(cnf, mode, W_USER, "%s(%ld) home %s made",
+ pwd->pw_name, (long) pwd->pw_uid, pwd->pw_dir);
+ }
+
+
+ /*
+ * Finally, send mail to the new user as well, if we are asked to
+ */
+ if (mode == M_ADD && !PWALTDIR() && cnf->newmail && *cnf->newmail && (fp = fopen(cnf->newmail, "r")) != NULL) {
+ FILE *pfp = popen(_PATH_SENDMAIL " -t", "w");
+
+ if (pfp == NULL)
+ warn("sendmail");
+ else {
+ fprintf(pfp, "From: root\n" "To: %s\n" "Subject: Welcome!\n\n", pwd->pw_name);
+ while (fgets(line, sizeof(line), fp) != NULL) {
+ /* Do substitutions? */
+ fputs(line, pfp);
+ }
+ pclose(pfp);
+ pw_log(cnf, mode, W_USER, "%s(%ld) new user mail sent",
+ pwd->pw_name, (long) pwd->pw_uid);
+ }
+ fclose(fp);
+ }
+
+ return EXIT_SUCCESS;
+}
+
+
+static uid_t
+pw_uidpolicy(struct userconf * cnf, struct cargs * args)
+{
+ struct passwd *pwd;
+ uid_t uid = (uid_t) - 1;
+ struct carg *a_uid = getarg(args, 'u');
+
+ /*
+ * Check the given uid, if any
+ */
+ if (a_uid != NULL) {
+ uid = (uid_t) atol(a_uid->val);
+
+ if ((pwd = GETPWUID(uid)) != NULL && getarg(args, 'o') == NULL)
+ errx(EX_DATAERR, "uid `%ld' has already been allocated", (long) pwd->pw_uid);
+ } else {
+ struct bitmap bm;
+
+ /*
+ * We need to allocate the next available uid under one of
+ * two policies a) Grab the first unused uid b) Grab the
+ * highest possible unused uid
+ */
+ if (cnf->min_uid >= cnf->max_uid) { /* Sanity
+ * claus^H^H^H^Hheck */
+ cnf->min_uid = 1000;
+ cnf->max_uid = 32000;
+ }
+ bm = bm_alloc(cnf->max_uid - cnf->min_uid + 1);
+
+ /*
+ * Now, let's fill the bitmap from the password file
+ */
+ SETPWENT();
+ while ((pwd = GETPWENT()) != NULL)
+ if (pwd->pw_uid >= (uid_t) cnf->min_uid && pwd->pw_uid <= (uid_t) cnf->max_uid)
+ bm_setbit(&bm, pwd->pw_uid - cnf->min_uid);
+ ENDPWENT();
+
+ /*
+ * Then apply the policy, with fallback to reuse if necessary
+ */
+ if (cnf->reuse_uids || (uid = (uid_t) (bm_lastset(&bm) + cnf->min_uid + 1)) > cnf->max_uid)
+ uid = (uid_t) (bm_firstunset(&bm) + cnf->min_uid);
+
+ /*
+ * Another sanity check
+ */
+ if (uid < cnf->min_uid || uid > cnf->max_uid)
+ errx(EX_SOFTWARE, "unable to allocate a new uid - range fully used");
+ bm_dealloc(&bm);
+ }
+ return uid;
+}
+
+
+static uid_t
+pw_gidpolicy(struct userconf * cnf, struct cargs * args, char *nam, gid_t prefer)
+{
+ struct group *grp;
+ gid_t gid = (uid_t) - 1;
+ struct carg *a_gid = getarg(args, 'g');
+
+ /*
+ * If no arg given, see if default can help out
+ */
+ if (a_gid == NULL && cnf->default_group && *cnf->default_group)
+ a_gid = addarg(args, 'g', cnf->default_group);
+
+ /*
+ * Check the given gid, if any
+ */
+ SETGRENT();
+ if (a_gid != NULL) {
+ if ((grp = GETGRNAM(a_gid->val)) == NULL) {
+ gid = (gid_t) atol(a_gid->val);
+ if ((gid == 0 && !isdigit((unsigned char)*a_gid->val)) || (grp = GETGRGID(gid)) == NULL)
+ errx(EX_NOUSER, "group `%s' is not defined", a_gid->val);
+ }
+ gid = grp->gr_gid;
+ } else if ((grp = GETGRNAM(nam)) != NULL && grp->gr_mem[0] == NULL) {
+ gid = grp->gr_gid; /* Already created? Use it anyway... */
+ } else {
+ struct cargs grpargs;
+ char tmp[32];
+
+ LIST_INIT(&grpargs);
+ addarg(&grpargs, 'n', nam);
+
+ /*
+ * We need to auto-create a group with the user's name. We
+ * can send all the appropriate output to our sister routine
+ * bit first see if we can create a group with gid==uid so we
+ * can keep the user and group ids in sync. We purposely do
+ * NOT check the gid range if we can force the sync. If the
+ * user's name dups an existing group, then the group add
+ * function will happily handle that case for us and exit.
+ */
+ if (GETGRGID(prefer) == NULL) {
+ sprintf(tmp, "%lu", (unsigned long) prefer);
+ addarg(&grpargs, 'g', tmp);
+ }
+ if (getarg(args, 'N'))
+ {
+ addarg(&grpargs, 'N', NULL);
+ addarg(&grpargs, 'q', NULL);
+ gid = pw_group(cnf, M_NEXT, &grpargs);
+ }
+ else
+ {
+ pw_group(cnf, M_ADD, &grpargs);
+ if ((grp = GETGRNAM(nam)) != NULL)
+ gid = grp->gr_gid;
+ }
+ a_gid = LIST_FIRST(&grpargs);
+ while (a_gid != NULL) {
+ struct carg *t = LIST_NEXT(a_gid, list);
+ LIST_REMOVE(a_gid, list);
+ a_gid = t;
+ }
+ }
+ ENDGRENT();
+ return gid;
+}
+
+
+static time_t
+pw_pwdpolicy(struct userconf * cnf, struct cargs * args)
+{
+ time_t result = 0;
+ time_t now = time(NULL);
+ struct carg *arg = getarg(args, 'p');
+
+ if (arg != NULL) {
+ if ((result = parse_date(now, arg->val)) == now)
+ errx(EX_DATAERR, "invalid date/time `%s'", arg->val);
+ } else if (cnf->password_days > 0)
+ result = now + ((long) cnf->password_days * 86400L);
+ return result;
+}
+
+
+static time_t
+pw_exppolicy(struct userconf * cnf, struct cargs * args)
+{
+ time_t result = 0;
+ time_t now = time(NULL);
+ struct carg *arg = getarg(args, 'e');
+
+ if (arg != NULL) {
+ if ((result = parse_date(now, arg->val)) == now)
+ errx(EX_DATAERR, "invalid date/time `%s'", arg->val);
+ } else if (cnf->expire_days > 0)
+ result = now + ((long) cnf->expire_days * 86400L);
+ return result;
+}
+
+
+static char *
+pw_homepolicy(struct userconf * cnf, struct cargs * args, char const * user)
+{
+ struct carg *arg = getarg(args, 'd');
+
+ if (arg)
+ return arg->val;
+ else {
+ static char home[128];
+
+ if (cnf->home == NULL || *cnf->home == '\0')
+ errx(EX_CONFIG, "no base home directory set");
+ sprintf(home, "%s/%s", cnf->home, user);
+ return home;
+ }
+}
+
+static char *
+shell_path(char const * path, char *shells[], char *sh)
+{
+ if (sh != NULL && (*sh == '/' || *sh == '\0'))
+ return sh; /* specified full path or forced none */
+ else {
+ char *p;
+ char paths[_UC_MAXLINE];
+
+ /*
+ * We need to search paths
+ */
+ 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
+ */
+ for (i = 0; i < 8; i++)
+ salt[i] = chars[arc4random() % 63];
+ salt[i] = '\0';
+
+ return strcpy(buf, crypt(password, salt));
+}
+
+#if defined(USE_MD5RAND)
+u_char *
+pw_getrand(u_char *buf, int len) /* cryptographically secure rng */
+{
+ int i;
+ for (i=0;i<len;i+=16) {
+ u_char ubuf[16];
+
+ MD5_CTX md5_ctx;
+ struct timeval tv, tvo;
+ struct rusage ru;
+ int n=0;
+ int t;
+
+ MD5Init (&md5_ctx);
+ t=getpid();
+ MD5Update (&md5_ctx, (u_char*)&t, sizeof t);
+ t=getppid();
+ MD5Update (&md5_ctx, (u_char*)&t, sizeof t);
+ gettimeofday (&tvo, NULL);
+ do {
+ getrusage (RUSAGE_SELF, &ru);
+ MD5Update (&md5_ctx, (u_char*)&ru, sizeof ru);
+ gettimeofday (&tv, NULL);
+ MD5Update (&md5_ctx, (u_char*)&tv, sizeof tv);
+ } while (n++<20 || tv.tv_usec-tvo.tv_usec<100*1000);
+ MD5Final (ubuf, &md5_ctx);
+ memcpy(buf+i, ubuf, MIN(16, len-i));
+ }
+ return buf;
+}
+
+#else /* Portable version */
+
+static u_char *
+pw_getrand(u_char *buf, int len)
+{
+ int i;
+
+ srandomdev();
+ 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 */
+ l = (arc4random() % 8 + 8); /* 8 - 16 chars */
+ pw_getrand(rndbuf, l);
+ for (i = 0; i < l; i++)
+ pwbuf[i] = chars[rndbuf[i] % (sizeof(chars)-1)];
+ pwbuf[i] = '\0';
+
+ /*
+ * We give this information back to the user
+ */
+ if (getarg(args, 'h') == NULL && getarg(args, 'N') == NULL) {
+ if (isatty(STDOUT_FILENO))
+ printf("Password for '%s' is: ", user);
+ printf("%s\n", pwbuf);
+ fflush(stdout);
+ }
+ break;
+
+ case -2: /* No password at all! */
+ return "";
+
+ case 0: /* No login - default */
+ default:
+ return "*";
+
+ case 1: /* user's name */
+ 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((unsigned char)*p);
+ }
+ if (pwd->pw_expire > (time_t)0 && (tptr = localtime(&pwd->pw_expire)) != NULL)
+ strftime(acexpire, sizeof acexpire, "%c", tptr);
+ if (pwd->pw_change > (time_t)0 && (tptr = localtime(&pwd->pw_change)) != NULL)
+ strftime(pwexpire, sizeof pwexpire, "%c", tptr);
+ printf("Login Name: %-15s #%-12ld Group: %-15s #%ld\n"
+ " Full Name: %s\n"
+ " Home: %-26.26s Class: %s\n"
+ " Shell: %-26.26s Office: %s\n"
+ "Work Phone: %-26.26s Home Phone: %s\n"
+ "Acc Expire: %-26.26s Pwd Expire: %s\n",
+ pwd->pw_name, (long) pwd->pw_uid,
+ grp ? grp->gr_name : "(invalid)", (long) pwd->pw_gid,
+ uname, pwd->pw_dir, pwd->pw_class,
+ pwd->pw_shell, office, wphone, hphone,
+ acexpire, pwexpire);
+ SETGRENT();
+ j = 0;
+ while ((grp=GETGRENT()) != NULL)
+ {
+ int i = 0;
+ while (grp->gr_mem[i] != NULL)
+ {
+ if (strcmp(grp->gr_mem[i], pwd->pw_name)==0)
+ {
+ printf(j++ == 0 ? " Groups: %s" : ",%s", grp->gr_name);
+ break;
+ }
+ ++i;
+ }
+ }
+ ENDGRENT();
+ printf("%s", j ? "\n" : "");
+ }
+ return EXIT_SUCCESS;
+}
+
+char *
+pw_checkname(u_char *name, int gecos)
+{
+ 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
+rmopie(char const * name)
+{
+ static const char etcopie[] = "/etc/opiekeys";
+ FILE *fp = fopen(etcopie, "r+");
+
+ if (fp != NULL) {
+ char tmp[1024];
+ off_t atofs = 0;
+ int length = strlen(name);
+
+ while (fgets(tmp, sizeof tmp, fp) != NULL) {
+ if (strncmp(name, tmp, length) == 0 && tmp[length]==' ') {
+ if (fseek(fp, atofs, SEEK_SET) == 0) {
+ fwrite("#", 1, 1, fp); /* Comment username out */
+ }
+ break;
+ }
+ atofs = ftell(fp);
+ }
+ /*
+ * If we got an error of any sort, don't update!
+ */
+ fclose(fp);
+ }
+}
+
diff --git a/usr.sbin/pw/pw_vpw.c b/usr.sbin/pw/pw_vpw.c
new file mode 100644
index 0000000..bc5713e
--- /dev/null
+++ b/usr.sbin/pw/pw_vpw.c
@@ -0,0 +1,318 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/param.h>
+
+#include "pwupd.h"
+
+static FILE * pwd_fp = NULL;
+
+void
+vendpwent(void)
+{
+ if (pwd_fp != NULL) {
+ fclose(pwd_fp);
+ pwd_fp = NULL;
+ }
+}
+
+void
+vsetpwent(void)
+{
+ vendpwent();
+}
+
+static struct passwd *
+vnextpwent(char const * nam, uid_t uid, int doclose)
+{
+ struct passwd * pw = NULL;
+ static char pwtmp[1024];
+
+ 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;
+ }
+}
+
+RET_SETGRENT
+vsetgrent(void)
+{
+ vendgrent();
+#if defined(__FreeBSD__)
+ return 0;
+#endif
+}
+
+static struct group *
+vnextgrent(char const * nam, gid_t gid, int doclose)
+{
+ struct group * gr = NULL;
+
+ static char * grtmp = NULL;
+ static int grlen = 0;
+ static char ** mems = NULL;
+ static int memlen = 0;
+
+ extendline(&grtmp, &grlen, MAXPATHLEN);
+ 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..0483be1
--- /dev/null
+++ b/usr.sbin/pw/pwupd.c
@@ -0,0 +1,211 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/wait.h>
+
+#include "pwupd.h"
+
+#define HAVE_PWDB_C 1
+#define HAVE_PWDB_U 1
+
+static char pathpwd[] = _PATH_PWD;
+static char * pwpath = pathpwd;
+
+int
+setpwdir(const char * dir)
+{
+ if (dir == NULL)
+ return -1;
+ else {
+ char * d = malloc(strlen(dir)+1);
+ if (d == NULL)
+ return -1;
+ pwpath = strcpy(d, dir);
+ }
+ return 0;
+}
+
+char *
+getpwpath(char const * file)
+{
+ static char pathbuf[MAXPATHLEN];
+
+ snprintf(pathbuf, sizeof pathbuf, "%s/%s", pwpath, file);
+ return pathbuf;
+}
+
+int
+pwdb(char *arg,...)
+{
+ int i = 0;
+ pid_t pid;
+ va_list ap;
+ char *args[10];
+
+ args[i++] = _PATH_PWD_MKDB;
+ va_start(ap, arg);
+ while (i < 6 && arg != NULL) {
+ args[i++] = arg;
+ arg = va_arg(ap, char *);
+ }
+ if (pwpath != pathpwd) {
+ args[i++] = "-d";
+ args[i++] = pwpath;
+ }
+ args[i++] = getpwpath(_MASTERPASSWD);
+ args[i] = NULL;
+
+ if ((pid = fork()) == -1) /* Error (errno set) */
+ i = errno;
+ else if (pid == 0) { /* Child */
+ execv(args[0], args);
+ _exit(1);
+ } else { /* Parent */
+ waitpid(pid, &i, 0);
+ if (WEXITSTATUS(i))
+ i = EIO;
+ }
+ return i;
+}
+
+int
+fmtpwentry(char *buf, struct passwd * pwd, int type)
+{
+ int l;
+ char *pw;
+
+ pw = (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[PWBUFSZ];
+ char pwbuf[PWBUFSZ];
+ int l = snprintf(pfx, PWBUFSZ, "%s:", user);
+#ifdef HAVE_PWDB_U
+ int isrename = pwd!=NULL && strcmp(user, pwd->pw_name);
+#endif
+
+ /*
+ * Update the passwd file first
+ */
+ if (pwd == NULL)
+ *pwbuf = '\0';
+ else
+ fmtpwentry(pwbuf, pwd, PWF_PASSWD);
+
+ if (l < 0)
+ l = 0;
+ rc = fileupdate(getpwpath(_PASSWD), 0644, pwbuf, pfx, l, mode);
+ if (rc == 0) {
+
+ /*
+ * Then the master.passwd file
+ */
+ if (pwd != NULL)
+ fmtpwentry(pwbuf, pwd, PWF_MASTER);
+ rc = fileupdate(getpwpath(_MASTERPASSWD), 0600, pwbuf, pfx, l, mode);
+ if (rc == 0) {
+#ifdef HAVE_PWDB_U
+ if (mode == UPD_DELETE || isrename)
+#endif
+ rc = pwdb(NULL);
+#ifdef HAVE_PWDB_U
+ else
+ rc = pwdb("-u", user, NULL);
+#endif
+ }
+ }
+ }
+ return rc;
+}
+
+int
+addpwent(struct passwd * pwd)
+{
+ return pw_update(pwd, pwd->pw_name, UPD_CREATE);
+}
+
+int
+chgpwent(char const * login, struct passwd * pwd)
+{
+ return pw_update(pwd, login, UPD_REPLACE);
+}
+
+int
+delpwent(struct passwd * pwd)
+{
+ return pw_update(NULL, pwd->pw_name, UPD_DELETE);
+}
diff --git a/usr.sbin/pw/pwupd.h b/usr.sbin/pw/pwupd.h
new file mode 100644
index 0000000..8da036d
--- /dev/null
+++ b/usr.sbin/pw/pwupd.h
@@ -0,0 +1,160 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _PWUPD_H_
+#define _PWUPD_H_
+
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include <sys/cdefs.h>
+
+#if defined(__FreeBSD__)
+#define RET_SETGRENT int
+#else
+#define RET_SETGRENT void
+#endif
+
+enum updtype
+{
+ UPD_DELETE = -1,
+ UPD_CREATE = 0,
+ UPD_REPLACE = 1
+};
+
+__BEGIN_DECLS
+int fileupdate __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, ...);
+ RET_SETGRENT (*_setgrent)(void);
+ void (*_endgrent)(void);
+ struct group * (*_getgrent)(void);
+ struct group * (*_getgrgid)(gid_t gid);
+ struct group * (*_getgrnam)(const char * nam);
+ int (*_grdb)(char *arg, ...);
+};
+
+extern struct pwf PWF;
+extern struct pwf VPWF;
+
+#define SETPWENT() PWF._setpwent()
+#define ENDPWENT() PWF._endpwent()
+#define GETPWENT() PWF._getpwent()
+#define GETPWUID(uid) PWF._getpwuid(uid)
+#define GETPWNAM(nam) PWF._getpwnam(nam)
+#define PWDB(args) PWF._pwdb(args)
+
+#define SETGRENT() PWF._setgrent()
+#define ENDGRENT() PWF._endgrent()
+#define GETGRENT() PWF._getgrent()
+#define GETGRGID(gid) PWF._getgrgid(gid)
+#define GETGRNAM(nam) PWF._getgrnam(nam)
+#define GRDB(args) PWF._grdb(args)
+
+#define PWALTDIR() PWF._altdir
+#ifndef _PATH_PWD
+#define _PATH_PWD "/etc"
+#endif
+#ifndef _GROUP
+#define _GROUP "group"
+#endif
+#ifndef _PASSWD
+#define _PASSWD "passwd"
+#endif
+#ifndef _MASTERPASSWD
+#define _MASTERPASSWD "master.passwd"
+#endif
+#ifndef _GROUP
+#define _GROUP "group"
+#endif
+
+__BEGIN_DECLS
+int addpwent __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, ...));
+
+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, ...));
+RET_SETGRENT 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..4ad590b
--- /dev/null
+++ b/usr.sbin/pw/rm_r.c
@@ -0,0 +1,75 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <unistd.h>
+#include <dirent.h>
+
+#include "pwupd.h"
+
+void
+rm_r(char const * dir, uid_t uid)
+{
+ DIR *d = opendir(dir);
+
+ if (d != NULL) {
+ struct dirent *e;
+ struct stat st;
+ char file[MAXPATHLEN];
+
+ while ((e = readdir(d)) != NULL) {
+ if (strcmp(e->d_name, ".") != 0 && strcmp(e->d_name, "..") != 0) {
+ sprintf(file, "%s/%s", dir, e->d_name);
+ if (lstat(file, &st) == 0) { /* Need symlinks, not
+ * linked file */
+ if (S_ISDIR(st.st_mode)) /* Directory - recurse */
+ rm_r(file, uid);
+ else {
+ if (S_ISLNK(st.st_mode) || st.st_uid == uid)
+ remove(file);
+ }
+ }
+ }
+ }
+ closedir(d);
+ if (lstat(dir, &st) == 0) {
+ if (S_ISLNK(st.st_mode))
+ remove(dir);
+ else if (st.st_uid == uid)
+ rmdir(dir);
+ }
+ }
+}
diff --git a/usr.sbin/pwd_mkdb/Makefile b/usr.sbin/pwd_mkdb/Makefile
new file mode 100644
index 0000000..7616629
--- /dev/null
+++ b/usr.sbin/pwd_mkdb/Makefile
@@ -0,0 +1,12 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../lib/libc/gen # for pw_scan.c
+
+PROG= pwd_mkdb
+MAN= pwd_mkdb.8
+SRCS= pw_scan.c pwd_mkdb.c
+
+CFLAGS+= -I${.CURDIR}/../../lib/libc/gen # for pw_scan.h
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pwd_mkdb/pwd_mkdb.8 b/usr.sbin/pwd_mkdb/pwd_mkdb.8
new file mode 100644
index 0000000..55be90b
--- /dev/null
+++ b/usr.sbin/pwd_mkdb/pwd_mkdb.8
@@ -0,0 +1,168 @@
+.\" Copyright (c) 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)pwd_mkdb.8 8.1 (Berkeley) 6/6/93
+.\" $FreeBSD$
+.\"
+.Dd June 6, 1993
+.Dt PWD_MKDB 8
+.Os
+.Sh NAME
+.Nm pwd_mkdb
+.Nd "generate the password databases"
+.Sh SYNOPSIS
+.Nm
+.Op Fl C
+.Op Fl N
+.Op Fl p
+.Op Fl d Ar directory
+.Op Fl s Ar cachesize
+.Op Fl u Ar username
+.Ar file
+.Sh DESCRIPTION
+.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
+to exit with an error if it cannot obtain a lock on the file. By default,
+we block waiting for a lock on the source file. The lock is held through
+the rebuilding of the database.
+.It Fl p
+Create a Version 7 style password file and install it into
+.Pa /etc/passwd .
+.It Fl d Ar directory
+Store databases into specified destination directory instead of
+.Pa /etc .
+.It Fl u Ar username
+Only update the record for the specified user. Utilities that
+operate on a single user can use this option to avoid the
+overhead of rebuilding the entire database.
+.It Fl s Ar cachesize
+Specify in megabytes the size of the memory cache used by the
+hashing library. On systems with a large user base, a small cache
+size can lead to prohibitively long database file rebuild times.
+As a rough guide, the memory usage of
+.Nm
+in megabytes will be a little bit more than twice the figure
+specified here. The default is 2 megabytes.
+.El
+.Pp
+The two databases differ in that the secure version contains the user's
+encrypted password and the insecure version has an asterisk (``*'')
+.Pp
+The databases are used by the C library password routines (see
+.Xr getpwent 3 ) .
+.Pp
+.Nm Pwd_mkdb
+exits zero on success, non-zero on failure.
+.Sh ENVIRONMENT
+If the
+.Ev PW_SCAN_BIG_IDS
+environment variable is set,
+.Nm
+will suppress the warning messages that are
+normally generated for large user and group IDs.
+Such IDs can cause serious problems with software
+that makes assumptions about the values of IDs.
+.Sh FILES
+.Bl -tag -width Pa -compact
+.It Pa /etc/pwd.db
+The insecure password database file.
+.It Pa /etc/pwd.db.tmp
+A temporary file.
+.It Pa /etc/spwd.db
+The secure password database file.
+.It Pa /etc/spwd.db.tmp
+A temporary file.
+.It Pa /etc/master.passwd
+The current password file.
+.It Pa /etc/passwd
+A Version 7 format password file.
+.El
+.Sh BUGS
+Because of the necessity for atomic update of the password files,
+.Nm
+uses
+.Xr rename 2
+to install them.
+This, however, requires that the file specified on the command line live
+on the same filesystem as the
+.Pa /etc
+directory.
+.Pp
+There are the obvious races with multiple people running
+.Nm
+on different password files at the same time.
+The front-ends to
+.Nm ,
+.Xr chpass 1 ,
+.Xr passwd 1
+and
+.Xr vipw 8 ,
+handle the locking necessary to avoid this problem.
+.Sh COMPATIBILITY
+Previous versions of the system had a program similar to
+.Nm ,
+.Xr mkpasswd 8 ,
+which built
+.Xr dbm 3
+style databases for the password file but depended on the calling programs
+to install them.
+The program was renamed in order that previous users of the program
+not be surprised by the changes in functionality.
+.Sh SEE ALSO
+.Xr chpass 1 ,
+.Xr passwd 1 ,
+.Xr db 3 ,
+.Xr getpwent 3 ,
+.Xr passwd 5 ,
+.Xr vipw 8
diff --git a/usr.sbin/pwd_mkdb/pwd_mkdb.c b/usr.sbin/pwd_mkdb/pwd_mkdb.c
new file mode 100644
index 0000000..bb913c4
--- /dev/null
+++ b/usr.sbin/pwd_mkdb/pwd_mkdb.c
@@ -0,0 +1,622 @@
+/*-
+ * 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[] =
+ "$FreeBSD$";
+#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;
+ int32_t pw_change, pw_expire;
+ 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;
+ if (is_comment)
+ --cnt;
+#define COMPACT(e) t = e; while ((*p++ = *t++));
+ if (!is_comment &&
+ (!username || (strcmp(username, pwd.pw_name) == 0))) {
+ pw_change = pwd.pw_change;
+ pw_expire = pwd.pw_expire;
+ /* Create insecure data. */
+ p = buf;
+ COMPACT(pwd.pw_name);
+ COMPACT("*");
+ memmove(p, &pwd.pw_uid, sizeof(pwd.pw_uid));
+ p += sizeof(int);
+ memmove(p, &pwd.pw_gid, sizeof(pwd.pw_gid));
+ p += sizeof(int);
+ memmove(p, &pw_change, sizeof(pw_change));
+ p += sizeof(pw_change);
+ COMPACT(pwd.pw_class);
+ COMPACT(pwd.pw_gecos);
+ COMPACT(pwd.pw_dir);
+ COMPACT(pwd.pw_shell);
+ memmove(p, &pw_expire, sizeof(pw_expire));
+ p += sizeof(pw_expire);
+ memmove(p, &pwd.pw_fields, sizeof pwd.pw_fields);
+ p += sizeof pwd.pw_fields;
+ data.size = p - buf;
+
+ /* Create secure data. */
+ p = sbuf;
+ COMPACT(pwd.pw_name);
+ COMPACT(pwd.pw_passwd);
+ memmove(p, &pwd.pw_uid, sizeof(pwd.pw_uid));
+ p += sizeof(int);
+ memmove(p, &pwd.pw_gid, sizeof(pwd.pw_gid));
+ p += sizeof(int);
+ memmove(p, &pw_change, sizeof(pw_change));
+ p += sizeof(pw_change);
+ COMPACT(pwd.pw_class);
+ COMPACT(pwd.pw_gecos);
+ COMPACT(pwd.pw_dir);
+ COMPACT(pwd.pw_shell);
+ memmove(p, &pw_expire, sizeof(pw_expire));
+ p += sizeof(pw_expire);
+ memmove(p, &pwd.pw_fields, sizeof pwd.pw_fields);
+ p += sizeof pwd.pw_fields;
+ sdata.size = p - sbuf;
+
+ /* Store insecure by name. */
+ tbuf[0] = _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), "%u", pwd.pw_uid);
+ snprintf(gidstr, sizeof(gidstr), "%u", pwd.pw_gid);
+
+ if (fprintf(oldfp, "%s:*:%s:%s:%s:%s:%s\n",
+ pwd.pw_name, pwd.pw_fields & _PWF_UID ? uidstr : "",
+ pwd.pw_fields & _PWF_GID ? gidstr : "",
+ pwd.pw_gecos, pwd.pw_dir, pwd.pw_shell) < 0)
+ error("write old");
+ }
+ }
+ /* If YP enabled, set flag. */
+ if (yp_enabled) {
+ buf[0] = yp_enabled + 2;
+ data.size = 1;
+ 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'))) {
+ /*
+ * XXX: This may also happen if the last line in a
+ * file does not have a trailing newline.
+ */
+ warnx("line #%d too long", lcnt);
+ goto fmt;
+
+ }
+ *p = '\0';
+
+ /*
+ * Ignore comments: ^[ \t]*#
+ */
+ for (p = line; *p != '\0'; p++)
+ if (*p != ' ' && *p != '\t')
+ break;
+ if (*p == '#' || *p == '\0') {
+ is_comment = 1;
+ return(1);
+ } else
+ is_comment = 0;
+
+ if (!__pw_scan(line, pw, _PWSCAN_WARN|_PWSCAN_MASTER)) {
+ warnx("at line #%d", lcnt);
+fmt: errno = EFTYPE; /* XXX */
+ error(pname);
+ }
+
+ return (1);
+}
+
+void
+cp(from, to, mode)
+ char *from, *to;
+ mode_t mode;
+{
+ static char buf[MAXBSIZE];
+ int from_fd, rcount, to_fd, wcount;
+
+ if ((from_fd = open(from, O_RDONLY, 0)) < 0)
+ error(from);
+ if ((to_fd = open(to, O_WRONLY|O_CREAT|O_EXCL, mode)) < 0)
+ error(to);
+ while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
+ wcount = write(to_fd, buf, rcount);
+ if (rcount != wcount || wcount == -1) {
+ int sverrno = errno;
+
+ (void)snprintf(buf, sizeof(buf), "%s to %s", from, to);
+ errno = sverrno;
+ error(buf);
+ }
+ }
+ if (rcount < 0) {
+ int sverrno = errno;
+
+ (void)snprintf(buf, sizeof(buf), "%s to %s", from, to);
+ errno = sverrno;
+ error(buf);
+ }
+}
+
+
+void
+mv(from, to)
+ char *from, *to;
+{
+ char buf[MAXPATHLEN];
+
+ if (rename(from, to)) {
+ int sverrno = errno;
+ (void)snprintf(buf, sizeof(buf), "%s to %s", from, to);
+ errno = sverrno;
+ error(buf);
+ }
+}
+
+void
+error(name)
+ 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..fd0416c
--- /dev/null
+++ b/usr.sbin/quot/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+PROG= quot
+WARNS?= 2
+MAN= 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..238c869
--- /dev/null
+++ b/usr.sbin/quot/quot.8
@@ -0,0 +1,107 @@
+.\" Copyright (C) 1994 Wolfgang Solfrank.
+.\" Copyright (C) 1994 TooLs GmbH.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by TooLs GmbH.
+.\" 4. The name of TooLs GmbH may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+.\" PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+.\" OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+.\" WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+.\" OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+.\" ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd February 8, 1994
+.Dt QUOT 8
+.Os
+.Sh NAME
+.Nm quot
+.Nd display disk space occupied by each user
+.Sh SYNOPSIS
+.Nm
+.Op Fl acfhknv
+.Op Ar filesystem ...
+.Sh DESCRIPTION
+.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
+Cause the numbers to be reported in kilobyte counts.
+By default, all sizes are reported in 512-byte block counts.
+.It Fl n
+Given a list of inodes (plus some optional data on each line)
+in the standard input, for each file print out the owner (plus
+the remainder of the input line). This is traditionally used
+in the pipe:
+.Bd -literal -offset indent
+.\" ncheck filesystem | sort +0n | quot -n filesystem
+ls -i | sed -e 's,^ *,,' | sort +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
+.Fl k
+option is not specified, the block counts will be displayed in units of that
+size block.
+.El
+.Sh BUGS
+.Xr ncheck
+(which would be a lot more useful than
+.Nm ls Fl i
+in the example above)
+does not exist in
+.Fx .
+.Sh SEE ALSO
+.Xr df 1 ,
+.Xr quota 1 ,
+.Xr getmntinfo 3 ,
+.Xr fstab 5 ,
+.Xr mount 8
+.Sh HISTORY
+This implementation of
+.Nm
+is by
+.An Wolfgang Solfrank
+/ TooLs GmbH.
diff --git a/usr.sbin/quot/quot.c b/usr.sbin/quot/quot.c
new file mode 100644
index 0000000..4214844
--- /dev/null
+++ b/usr.sbin/quot/quot.c
@@ -0,0 +1,652 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stdint.h>
+#include <sys/mount.h>
+#include <sys/disklabel.h>
+#include <sys/time.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <fstab.h>
+#include <errno.h>
+#include <paths.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/* some flags of what to do: */
+static char estimate;
+static char count;
+static char unused;
+static void (*func)(int, struct fs *, char *);
+static long blocksize;
+static char *header;
+static int headerlen;
+
+static union dinode *get_inode(int, struct fs *, ino_t);
+static int virtualblocks(struct fs *, union dinode *);
+static int isfree(struct fs *, union dinode *);
+static void inituser(void);
+static void usrrehash(void);
+static struct user *user(uid_t);
+static int cmpusers(const void *, const void *);
+static void uses(uid_t, daddr_t, time_t);
+static void initfsizes(void);
+static void dofsizes(int, struct fs *, char *);
+static void douser(int, struct fs *, char *);
+static void donames(int, struct fs *, char *);
+static void usage(void);
+static void quot(char *, char *);
+
+/*
+ * Original BSD quot doesn't round to number of frags/blocks,
+ * doesn't account for indirection blocks and gets it totally
+ * wrong if the size is a multiple of the blocksize.
+ * The new code always counts the number of 512 byte blocks
+ * instead of the number of kilobytes and converts them to
+ * kByte when done (on request).
+ *
+ * Due to the size of modern disks, we must cast intermediate
+ * values to 64 bits to prevent potential overflows.
+ */
+#ifdef COMPAT
+#define SIZE(n) (n)
+#else
+#define SIZE(n) ((int)(((quad_t)(n) * 512 + blocksize - 1)/blocksize))
+#endif
+
+#define INOCNT(fs) ((fs)->fs_ipg)
+#define INOSZ(fs) \
+ (((fs)->fs_magic == FS_UFS1_MAGIC ? sizeof(struct ufs1_dinode) : \
+ sizeof(struct ufs2_dinode)) * INOCNT(fs))
+
+union dinode {
+ struct ufs1_dinode dp1;
+ struct ufs2_dinode dp2;
+};
+#define DIP(fs, dp, field) \
+ (((fs)->fs_magic == FS_UFS1_MAGIC) ? \
+ (dp)->dp1.field : (dp)->dp2.field)
+
+static union dinode *
+get_inode(fd,super,ino)
+ int fd;
+ struct fs *super;
+ ino_t ino;
+{
+ static caddr_t ipbuf;
+ static ino_t last;
+
+ if (fd < 0) { /* flush cache */
+ if (ipbuf) {
+ free(ipbuf);
+ ipbuf = 0;
+ }
+ return 0;
+ }
+
+ if (!ipbuf || ino < last || ino >= last + INOCNT(super)) {
+ if (!ipbuf
+ && !(ipbuf = malloc(INOSZ(super))))
+ errx(1, "allocate inodes");
+ last = (ino / INOCNT(super)) * INOCNT(super);
+ if (lseek(fd, (off_t)ino_to_fsba(super, last) << super->fs_fshift, 0) < (off_t)0
+ || read(fd, ipbuf, INOSZ(super)) != (ssize_t)INOSZ(super))
+ err(1, "read inodes");
+ }
+
+ if (super->fs_magic == FS_UFS1_MAGIC)
+ return ((union dinode *)
+ &((struct ufs1_dinode *)ipbuf)[ino % INOCNT(super)]);
+ return ((union dinode *)
+ &((struct ufs2_dinode *)ipbuf)[ino % INOCNT(super)]);
+}
+
+#ifdef COMPAT
+#define actualblocks(fs, dp) (DIP(fs, dp, di_blocks) / 2)
+#else
+#define actualblocks(fs, dp) DIP(fs, dp, di_blocks)
+#endif
+
+static int virtualblocks(super, dp)
+ struct fs *super;
+ union dinode *dp;
+{
+ register off_t nblk, sz;
+
+ sz = DIP(super, dp, di_size);
+#ifdef COMPAT
+ if (lblkno(super,sz) >= NDADDR) {
+ nblk = blkroundup(super,sz);
+ if (sz == nblk)
+ nblk += super->fs_bsize;
+ }
+
+ return sz / 1024;
+
+#else /* COMPAT */
+
+ if (lblkno(super,sz) >= NDADDR) {
+ nblk = blkroundup(super,sz);
+ sz = lblkno(super,nblk);
+ sz = (sz - NDADDR + NINDIR(super) - 1) / NINDIR(super);
+ while (sz > 0) {
+ nblk += sz * super->fs_bsize;
+ /* sz - 1 rounded up */
+ sz = (sz - 1 + NINDIR(super) - 1) / NINDIR(super);
+ }
+ } else
+ nblk = fragroundup(super,sz);
+
+ return nblk / 512;
+#endif /* COMPAT */
+}
+
+static int
+isfree(super, dp)
+ struct fs *super;
+ union dinode *dp;
+{
+#ifdef COMPAT
+ return (DIP(super, dp, di_mode) & IFMT) == 0;
+#else /* COMPAT */
+
+ switch (DIP(super, dp, di_mode) & IFMT) {
+ case IFIFO:
+ case IFLNK: /* should check FASTSYMLINK? */
+ case IFDIR:
+ case IFREG:
+ return 0;
+ default:
+ return 1;
+ }
+#endif
+}
+
+static struct user {
+ uid_t uid;
+ char *name;
+ daddr_t space;
+ long count;
+ daddr_t spc30;
+ daddr_t spc60;
+ daddr_t spc90;
+} *users;
+static int nusers;
+
+static void
+inituser()
+{
+ register int i;
+ register struct user *usr;
+
+ if (!nusers) {
+ nusers = 8;
+ if (!(users =
+ (struct user *)calloc(nusers,sizeof(struct user))))
+ errx(1, "allocate users");
+ } else {
+ for (usr = users, i = nusers; --i >= 0; usr++) {
+ usr->space = usr->spc30 = usr->spc60 = usr->spc90 = 0;
+ usr->count = 0;
+ }
+ }
+}
+
+static void
+usrrehash()
+{
+ register int i;
+ register struct user *usr, *usrn;
+ struct user *svusr;
+
+ svusr = users;
+ nusers <<= 1;
+ if (!(users = (struct user *)calloc(nusers,sizeof(struct user))))
+ errx(1, "allocate users");
+ for (usr = svusr, i = nusers >> 1; --i >= 0; usr++) {
+ for (usrn = users + (usr->uid&(nusers - 1)); usrn->name;
+ usrn--) {
+ if (usrn <= users)
+ usrn = users + nusers;
+ }
+ *usrn = *usr;
+ }
+}
+
+static struct user *
+user(uid)
+ uid_t uid;
+{
+ register struct user *usr;
+ register int i;
+ struct passwd *pwd;
+
+ while (1) {
+ for (usr = users + (uid&(nusers - 1)), i = nusers; --i >= 0;
+ usr--) {
+ if (!usr->name) {
+ usr->uid = uid;
+
+ if (!(pwd = getpwuid(uid))) {
+ if ((usr->name = (char *)malloc(7)))
+ sprintf(usr->name,"#%d",uid);
+ } else {
+ if ((usr->name = (char *)
+ malloc(strlen(pwd->pw_name) + 1)))
+ strcpy(usr->name,pwd->pw_name);
+ }
+ if (!usr->name)
+ errx(1, "allocate users");
+
+ return usr;
+
+ } else if (usr->uid == uid)
+ return usr;
+
+ if (usr <= users)
+ usr = users + nusers;
+ }
+ usrrehash();
+ }
+}
+
+static int
+cmpusers(v1,v2)
+ const void *v1, *v2;
+{
+ const struct user *u1, *u2;
+ u1 = (const struct user *)v1;
+ u2 = (const struct user *)v2;
+
+ return u2->space - u1->space;
+}
+
+#define sortusers(users) (qsort((users),nusers,sizeof(struct user), \
+ cmpusers))
+
+static void
+uses(uid,blks,act)
+ uid_t uid;
+ daddr_t blks;
+ time_t act;
+{
+ static time_t today;
+ register struct user *usr;
+
+ if (!today)
+ time(&today);
+
+ usr = user(uid);
+ usr->count++;
+ usr->space += blks;
+
+ if (today - act > 90L * 24L * 60L * 60L)
+ usr->spc90 += blks;
+ if (today - act > 60L * 24L * 60L * 60L)
+ usr->spc60 += blks;
+ if (today - act > 30L * 24L * 60L * 60L)
+ usr->spc30 += blks;
+}
+
+#ifdef COMPAT
+#define FSZCNT 500
+#else
+#define FSZCNT 512
+#endif
+struct fsizes {
+ struct fsizes *fsz_next;
+ daddr_t fsz_first, fsz_last;
+ ino_t fsz_count[FSZCNT];
+ daddr_t fsz_sz[FSZCNT];
+} *fsizes;
+
+static void
+initfsizes()
+{
+ register struct fsizes *fp;
+ register int i;
+
+ for (fp = fsizes; fp; fp = fp->fsz_next) {
+ for (i = FSZCNT; --i >= 0;) {
+ fp->fsz_count[i] = 0;
+ fp->fsz_sz[i] = 0;
+ }
+ }
+}
+
+static void
+dofsizes(fd, super, name)
+ int fd;
+ struct fs *super;
+ char *name;
+{
+ ino_t inode, maxino;
+ union dinode *dp;
+ daddr_t sz, ksz;
+ struct fsizes *fp, **fsp;
+ register int i;
+
+ maxino = super->fs_ncg * super->fs_ipg - 1;
+#ifdef COMPAT
+ if (!(fsizes = (struct fsizes *)malloc(sizeof(struct fsizes))))
+ errx(1, "allocate fsize structure");
+#endif /* COMPAT */
+ for (inode = 0; inode < maxino; inode++) {
+ errno = 0;
+ if ((dp = get_inode(fd,super,inode))
+#ifdef COMPAT
+ && ((DIP(super, dp, di_mode) & IFMT) == IFREG
+ || (DIP(super, dp, di_mode) & IFMT) == IFDIR)
+#else /* COMPAT */
+ && !isfree(super, dp)
+#endif /* COMPAT */
+ ) {
+ sz = estimate ? virtualblocks(super, dp) :
+ actualblocks(super, dp);
+#ifdef COMPAT
+ if (sz >= FSZCNT) {
+ fsizes->fsz_count[FSZCNT-1]++;
+ fsizes->fsz_sz[FSZCNT-1] += sz;
+ } else {
+ fsizes->fsz_count[sz]++;
+ fsizes->fsz_sz[sz] += sz;
+ }
+#else /* COMPAT */
+ ksz = SIZE(sz);
+ for (fsp = &fsizes; (fp = *fsp); fsp = &fp->fsz_next) {
+ if (ksz < fp->fsz_last)
+ break;
+ }
+ if (!fp || ksz < fp->fsz_first) {
+ if (!(fp = (struct fsizes *)
+ malloc(sizeof(struct fsizes))))
+ errx(1, "allocate fsize structure");
+ fp->fsz_next = *fsp;
+ *fsp = fp;
+ fp->fsz_first = (ksz / FSZCNT) * FSZCNT;
+ fp->fsz_last = fp->fsz_first + FSZCNT;
+ for (i = FSZCNT; --i >= 0;) {
+ fp->fsz_count[i] = 0;
+ fp->fsz_sz[i] = 0;
+ }
+ }
+ fp->fsz_count[ksz % FSZCNT]++;
+ fp->fsz_sz[ksz % FSZCNT] += sz;
+#endif /* COMPAT */
+ } else if (errno) {
+ err(1, "%s", name);
+ }
+ }
+ sz = 0;
+ for (fp = fsizes; fp; fp = fp->fsz_next) {
+ for (i = 0; i < FSZCNT; i++) {
+ if (fp->fsz_count[i])
+ printf("%jd\t%jd\t%d\n",
+ (intmax_t)(fp->fsz_first + i),
+ (intmax_t)fp->fsz_count[i],
+ SIZE(sz += fp->fsz_sz[i]));
+ }
+ }
+}
+
+static void
+douser(fd, super, name)
+ int fd;
+ struct fs *super;
+ char *name;
+{
+ ino_t inode, maxino;
+ struct user *usr, *usrs;
+ union dinode *dp;
+ register int n;
+
+ maxino = super->fs_ncg * super->fs_ipg - 1;
+ for (inode = 0; inode < maxino; inode++) {
+ errno = 0;
+ if ((dp = get_inode(fd,super,inode))
+ && !isfree(super, dp))
+ uses(DIP(super, dp, di_uid),
+ estimate ? virtualblocks(super, dp) :
+ actualblocks(super, dp),
+ DIP(super, dp, di_atime));
+ else if (errno) {
+ err(1, "%s", name);
+ }
+ }
+ if (!(usrs = (struct user *)malloc(nusers * sizeof(struct user))))
+ errx(1, "allocate users");
+ bcopy(users,usrs,nusers * sizeof(struct user));
+ sortusers(usrs);
+ for (usr = usrs, n = nusers; --n >= 0 && usr->count; usr++) {
+ printf("%5d",SIZE(usr->space));
+ if (count)
+ printf("\t%5ld",usr->count);
+ printf("\t%-8s",usr->name);
+ if (unused)
+ printf("\t%5d\t%5d\t%5d",
+ SIZE(usr->spc30),
+ SIZE(usr->spc60),
+ SIZE(usr->spc90));
+ printf("\n");
+ }
+ free(usrs);
+}
+
+static void
+donames(fd, super, name)
+ int fd;
+ struct fs *super;
+ char *name;
+{
+ int c;
+ ino_t inode, inode1;
+ ino_t maxino;
+ union dinode *dp;
+
+ maxino = super->fs_ncg * super->fs_ipg - 1;
+ /* first skip the name of the filesystem */
+ while ((c = getchar()) != EOF && (c < '0' || c > '9'))
+ while ((c = getchar()) != EOF && c != '\n');
+ ungetc(c,stdin);
+ inode1 = -1;
+ while (scanf("%u",&inode) == 1) {
+ if (inode > maxino) {
+ warnx("illegal inode %d",inode);
+ return;
+ }
+ errno = 0;
+ if ((dp = get_inode(fd,super,inode))
+ && !isfree(super, dp)) {
+ printf("%s\t",user(DIP(super, dp, di_uid))->name);
+ /* now skip whitespace */
+ while ((c = getchar()) == ' ' || c == '\t');
+ /* and print out the remainder of the input line */
+ while (c != EOF && c != '\n') {
+ putchar(c);
+ c = getchar();
+ }
+ putchar('\n');
+ inode1 = inode;
+ } else {
+ if (errno) {
+ err(1, "%s", name);
+ }
+ /* skip this line */
+ while ((c = getchar()) != EOF && c != '\n');
+ }
+ if (c == EOF)
+ break;
+ }
+}
+
+static void
+usage()
+{
+#ifdef COMPAT
+ fprintf(stderr,"usage: quot [-nfcvha] [filesystem ...]\n");
+#else /* COMPAT */
+ fprintf(stderr,"usage: quot [-acfhknv] [filesystem ...]\n");
+#endif /* COMPAT */
+ exit(1);
+}
+
+/*
+ * Possible superblock locations ordered from most to least likely.
+ */
+static int sblock_try[] = SBLOCKSEARCH;
+static char superblock[SBLOCKSIZE];
+
+void
+quot(name,mp)
+ char *name, *mp;
+{
+ int i, fd;
+ struct fs *fs;
+
+ get_inode(-1, NULL, 0); /* flush cache */
+ inituser();
+ initfsizes();
+ if ((fd = open(name,0)) < 0) {
+ warn("%s", name);
+ close(fd);
+ return;
+ }
+ for (i = 0; sblock_try[i] != -1; i++) {
+ if (lseek(fd, sblock_try[i], 0) != sblock_try[i]) {
+ close(fd);
+ return;
+ }
+ if (read(fd, superblock, SBLOCKSIZE) != SBLOCKSIZE) {
+ close(fd);
+ return;
+ }
+ fs = (struct fs *)superblock;
+ if ((fs->fs_magic == FS_UFS1_MAGIC ||
+ (fs->fs_magic == FS_UFS2_MAGIC &&
+ fs->fs_sblockloc == numfrags(fs, sblock_try[i]))) &&
+ fs->fs_bsize <= MAXBSIZE &&
+ fs->fs_bsize >= sizeof(struct fs))
+ break;
+ }
+ if (sblock_try[i] == -1) {
+ warnx("%s: not a BSD filesystem",name);
+ close(fd);
+ return;
+ }
+ printf("%s:",name);
+ if (mp)
+ printf(" (%s)",mp);
+ putchar('\n');
+ (*func)(fd, fs, name);
+ close(fd);
+}
+
+int
+main(argc,argv)
+ int argc;
+ char **argv;
+{
+ char all = 0;
+ struct statfs *mp;
+ struct fstab *fs;
+ char dev[MNAMELEN + 1];
+ char *nm;
+ int cnt;
+
+ func = douser;
+#ifndef COMPAT
+ header = getbsize(&headerlen,&blocksize);
+#endif
+ while (--argc > 0 && **++argv == '-') {
+ while (*++*argv) {
+ switch (**argv) {
+ case 'n':
+ func = donames;
+ break;
+ case 'c':
+ func = dofsizes;
+ break;
+ case 'a':
+ all = 1;
+ break;
+ case 'f':
+ count = 1;
+ break;
+ case 'h':
+ estimate = 1;
+ break;
+#ifndef COMPAT
+ case 'k':
+ blocksize = 1024;
+ break;
+#endif /* COMPAT */
+ case 'v':
+ unused = 1;
+ break;
+ default:
+ usage();
+ }
+ }
+ }
+ if (all) {
+ cnt = getmntinfo(&mp,MNT_NOWAIT);
+ for (; --cnt >= 0; mp++) {
+ if (!strncmp(mp->f_fstypename, "ufs", MFSNAMELEN)) {
+ if ((nm = strrchr(mp->f_mntfromname,'/'))) {
+ sprintf(dev,"%s%s",_PATH_DEV,nm + 1);
+ nm = dev;
+ } else
+ nm = mp->f_mntfromname;
+ quot(nm,mp->f_mntonname);
+ }
+ }
+ }
+ while (--argc >= 0) {
+ if ((fs = getfsfile(*argv)) != NULL)
+ quot(fs->fs_spec, 0);
+ else
+ quot(*argv,0);
+ argv++;
+ }
+ return 0;
+}
diff --git a/usr.sbin/quotaon/Makefile b/usr.sbin/quotaon/Makefile
new file mode 100644
index 0000000..984008c
--- /dev/null
+++ b/usr.sbin/quotaon/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+PROG= quotaon
+LINKS= ${BINDIR}/quotaon ${BINDIR}/quotaoff
+MAN= quotaon.8
+MLINKS= quotaon.8 quotaoff.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/quotaon/quotaon.8 b/usr.sbin/quotaon/quotaon.8
new file mode 100644
index 0000000..cdf260e
--- /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
+.\" $FreeBSD$
+.\"
+.Dd December 11, 1993
+.Dt QUOTAON 8
+.Os
+.Sh NAME
+.Nm quotaon ,
+.Nm quotaoff
+.Nd turn filesystem quotas on and off
+.Sh SYNOPSIS
+.Nm
+.Op Fl g
+.Op Fl u
+.Op Fl v
+.Ar filesystem Ar ...
+.Nm
+.Op Fl g
+.Op Fl u
+.Op Fl v
+.Fl a
+.Nm quotaoff
+.Op Fl g
+.Op Fl u
+.Op Fl v
+.Ar filesystem Ar ...
+.Nm quotaoff
+.Op Fl g
+.Op Fl u
+.Op Fl v
+.Fl a
+.Sh DESCRIPTION
+.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 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 filesystem.
+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 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
+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..daec695
--- /dev/null
+++ b/usr.sbin/quotaon/quotaon.c
@@ -0,0 +1,266 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#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 <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+char *qfname = QUOTAFILENAME;
+char *qfextension[] = INITQFNAMES;
+
+int aflag; /* all filesystems */
+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 filesystem is mounted and not readonly.
+ */
+int
+readonly(fs)
+ register struct fstab *fs;
+{
+ struct statfs fsbuf;
+
+ if (statfs(fs->fs_file, &fsbuf) < 0 ||
+ strcmp(fsbuf.f_mntonname, fs->fs_file) ||
+ strcmp(fsbuf.f_mntfromname, fs->fs_spec)) {
+ printf("%s: not mounted\n", fs->fs_file);
+ return (1);
+ }
+ if (fsbuf.f_flags & MNT_RDONLY) {
+ printf("%s: mounted read-only\n", fs->fs_file);
+ return (1);
+ }
+ return (0);
+}
diff --git a/usr.sbin/rarpd/Makefile b/usr.sbin/rarpd/Makefile
new file mode 100644
index 0000000..e94ad62
--- /dev/null
+++ b/usr.sbin/rarpd/Makefile
@@ -0,0 +1,12 @@
+# from: @(#)Makefile 5.8 (Berkeley) 7/28/90
+# $FreeBSD$
+
+PROG= rarpd
+MAN= rarpd.8
+
+WARNS?= 2
+# This breaks with format strings returned by expand_syslog_m().. argh!
+#FORMAT_AUDIT?= 1
+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..73f44a6
--- /dev/null
+++ b/usr.sbin/rarpd/rarpd.8
@@ -0,0 +1,145 @@
+.\" @(#) $FreeBSD$ (LBL)
+.\"
+.\" Copyright (c) 1990, 1991, 1993 The Regents of the University of
+.\" California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that: (1) source code distributions
+.\" retain the above copyright notice and this paragraph in its entirety, (2)
+.\" distributions including binary code include the above copyright notice and
+.\" this paragraph in its entirety in the documentation or other materials
+.\" provided with the distribution, and (3) all advertising materials mentioning
+.\" features or use of this software display the following acknowledgement:
+.\" ``This product includes software developed by the University of California,
+.\" Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+.\" the University nor the names of its contributors may be used to endorse
+.\" or promote products derived from this software without specific prior
+.\" written permission.
+.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+.\" WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+.\"
+.Dd November 16, 2001
+.Dt RARPD 8
+.Os
+.Sh NAME
+.Nm rarpd
+.Nd reverse ARP daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl adfsv
+.Op Fl t Ar directory
+.Op Ar interface
+.Sh DESCRIPTION
+.Nm
+services Reverse ARP requests on the Ethernet connected to
+.Ar interface .
+Upon receiving a request,
+.Nm
+maps the target hardware address to an IP address via its name, which
+must be present in both the
+.Xr ethers 5
+and
+.Xr hosts 5
+databases.
+If a host does not exist in both databases, the translation cannot
+proceed and a reply will not be sent.
+.Pp
+By default, a request is honored only if the server
+(i.e., the host that
+.Nm
+is running on)
+can "boot" the target; that is, a file or directory matching the glob
+.Pa /tftpboot/\fIipaddr\fP*
+exists, where
+.Em ipaddr
+is the target IP address in hex.
+For example, the IP address 204.216.27.18 will be replied to if any of
+.Pa /tftpboot/CCD81B12 ,
+.Pa /tftpboot/CCD81B12.SUN3 ,
+or
+.Pa /tftpboot/CCD81B12-boot
+exist.
+This requirement can be overridden with the
+.Fl s
+flag (see below).
+.Pp
+In normal operation,
+.Nm
+forks a copy of itself and runs in the background.
+Anomalies and errors are reported via
+.Xr syslog 3 .
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl a
+Listen on all the Ethernets attached to the system.
+If
+.Fl a
+is omitted, an interface must be specified.
+.It Fl d
+If
+.Fl f
+is also specified,
+.Nm
+logs messages to
+.Em stdout
+and
+.Em stderr
+instead of via
+.Xr syslog 3 .
+.It Fl f
+Run in the foreground.
+.It Fl s
+Supply a response to any RARP request for which an ethernet to IP address
+mapping exists; do not depend on the existence of
+.Pa /tftpboot/\fIipaddr\fP* .
+.It Fl t
+Supply an alternate tftp root directory to
+.Pa /tftpboot ,
+similar to the
+.Fl s
+option of
+.Xr tftpd 8 .
+This permits
+.Nm
+to selectively respond to RARP requests, but use an alternate directory
+for IP checking.
+.It Fl v
+Enable verbose syslogging.
+.El
+.Sh FILES
+.Bl -tag -width /etc/ethers -compact
+.It Pa /etc/ethers
+.It Pa /etc/hosts
+.It Pa /tftpboot
+.El
+.Sh SEE ALSO
+.Xr bpf 4
+.Rs
+.%A "Finlayson, R."
+.%A "Mann, T."
+.%A "Mogul, J.C."
+.%A "Theimer, M."
+.%T "RFC 903: Reverse Address Resolution Protocol"
+.%D "June 1984"
+.%O "4 p"
+.Re
+.Sh AUTHORS
+.An -nosplit
+.An Craig Leres Aq leres@ee.lbl.gov
+and
+.An Steven McCanne Aq mccanne@ee.lbl.gov .
+Lawrence Berkeley Laboratory, University of California, Berkeley, CA.
+.Sh BUGS
+.Nm
+can depend on the DNS to resolve the name discovered from
+.Pa /etc/ethers .
+If this name is not in the DNS but is in
+.Pa /etc/hosts ,
+the DNS lookup can cause a delayed RARP response, so in this situation
+it is recommended to configure
+.Xr nsswitch.conf 5
+to read
+.Pa /etc/hosts
+first.
diff --git a/usr.sbin/rarpd/rarpd.c b/usr.sbin/rarpd/rarpd.c
new file mode 100644
index 0000000..57359ef
--- /dev/null
+++ b/usr.sbin/rarpd/rarpd.c
@@ -0,0 +1,1054 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/*
+ * rarpd - Reverse ARP Daemon
+ *
+ * Usage: rarpd -a [ -dfsv ] [ hostname ]
+ * rarpd [ -dfsv ] interface [ hostname ]
+ *
+ * 'hostname' is optional solely for backwards compatibility with Sun's rarpd.
+ * Currently, the argument is ignored.
+ */
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <net/bpf.h>
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <netdb.h>
+#include <stdarg.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
+
+/*
+ * 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 */
+const char *tftp_dir = TFTP_DIR; /* tftp directory */
+
+int dflag; /* messages to stdout/stderr, not syslog(3) */
+int sflag; /* ignore /tftpboot */
+
+static u_char zero[6];
+
+static int bpf_open(void);
+static u_long choose_ipaddr(u_long **, u_long, u_long);
+static char *eatoa(u_char *);
+static int expand_syslog_m(const char *fmt, char **newfmt);
+static void init(char *);
+static void init_one(struct ifreq *, char *);
+static char *intoa(u_long);
+static u_long ipaddrtonetmask(u_long);
+static void logmsg(int, const char *, ...) __printflike(2, 3);
+static int rarp_bootable(u_long);
+static int rarp_check(u_char *, u_int);
+static void rarp_loop(void);
+static int rarp_open(char *);
+static void rarp_process(struct if_info *, u_char *, u_int);
+static void rarp_reply(struct if_info *, struct ether_header *,
+ u_long, u_int);
+static void update_arptab(u_char *, u_long);
+static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ int op;
+ char *ifname, *hostname, *name;
+
+ int aflag = 0; /* listen on "all" interfaces */
+ int fflag = 0; /* don't fork */
+
+ if ((name = strrchr(argv[0], '/')) != NULL)
+ ++name;
+ else
+ name = argv[0];
+ if (*name == '-')
+ ++name;
+
+ /*
+ * All error reporting is done through syslog, unless -d is specified
+ */
+ openlog(name, LOG_PID | LOG_CONS, LOG_DAEMON);
+
+ opterr = 0;
+ while ((op = getopt(argc, argv, "adfst:v")) != -1) {
+ switch (op) {
+ case 'a':
+ ++aflag;
+ break;
+
+ case 'd':
+ ++dflag;
+ break;
+
+ case 'f':
+ ++fflag;
+ break;
+
+ case 's':
+ ++sflag;
+ break;
+
+ case 't':
+ tftp_dir = optarg;
+ break;
+
+ case 'v':
+ ++verbose;
+ break;
+
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+ 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)) {
+ logmsg(LOG_ERR, "cannot fork");
+ exit(1);
+ }
+ }
+ rarp_loop();
+ return(0);
+}
+
+/*
+ * Add to the interface list.
+ */
+void
+init_one(struct ifreq *ifrp, char *target)
+{
+ struct if_info *ii;
+ 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) == -1) {
+ logmsg(LOG_ERR,
+ "SIOCGIFFLAGS: %.*s: %m",
+ (int)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) {
+ logmsg(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) == -1) {
+ logmsg(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) == -1) {
+ logmsg(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) == -1) {
+ logmsg(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(char *target)
+{
+ u_int n;
+ struct ifreq *ifrp, *ifend;
+ struct if_info *ii, *nii, *lii;
+ struct ifconf ifc;
+ struct ifreq ibuf[16];
+
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
+ logmsg(LOG_ERR, "socket: %m");
+ exit(1);
+ }
+ ifc.ifc_len = sizeof ibuf;
+ ifc.ifc_buf = (caddr_t)ibuf;
+ if ((ioctl(s, SIOCGIFCONF, (char *)&ifc) == -1) ||
+ ((u_int)ifc.ifc_len < sizeof(struct ifreq))) {
+ logmsg(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)
+ logmsg(LOG_DEBUG, "%s %s 0x%08lx %s",
+ ii->ii_ifname, intoa(ntohl(ii->ii_ipaddr)),
+ (u_long)ntohl(ii->ii_netmask), eatoa(ii->ii_eaddr));
+}
+
+void
+usage(void)
+{
+ (void)fprintf(stderr, "usage: rarpd [-adfsv] [interface]\n");
+ exit(1);
+}
+
+int
+bpf_open(void)
+{
+ int fd;
+ int n = 0;
+ char device[sizeof "/dev/bpf000"];
+
+ /*
+ * Go through all the minors and find one that isn't in use.
+ */
+ do {
+ (void)sprintf(device, "/dev/bpf%d", n++);
+ fd = open(device, O_RDWR);
+ } while ((fd == -1) && (errno == EBUSY));
+
+ if (fd == -1) {
+ logmsg(LOG_ERR, "%s: %m", device);
+ exit(1);
+ }
+ return fd;
+}
+
+/*
+ * Open a BPF file and attach it to the interface named 'device'.
+ * Set immediate mode, and set a filter that accepts only RARP requests.
+ */
+int
+rarp_open(char *device)
+{
+ int fd;
+ struct ifreq ifr;
+ u_int dlt;
+ int immediate;
+
+ static struct bpf_insn insns[] = {
+ BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 12),
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, ETHERTYPE_REVARP, 0, 3),
+ BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 20),
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, REVARP_REQUEST, 0, 1),
+ BPF_STMT(BPF_RET|BPF_K, sizeof(struct ether_arp) +
+ sizeof(struct ether_header)),
+ BPF_STMT(BPF_RET|BPF_K, 0),
+ };
+ static struct bpf_program filter = {
+ sizeof insns / sizeof(insns[0]),
+ insns
+ };
+
+ fd = bpf_open();
+ /*
+ * Set immediate mode so packets are processed as they arrive.
+ */
+ immediate = 1;
+ if (ioctl(fd, BIOCIMMEDIATE, &immediate) == -1) {
+ logmsg(LOG_ERR, "BIOCIMMEDIATE: %m");
+ exit(1);
+ }
+ (void)strncpy(ifr.ifr_name, device, sizeof ifr.ifr_name);
+ if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) == -1) {
+ logmsg(LOG_ERR, "BIOCSETIF: %m");
+ exit(1);
+ }
+ /*
+ * Check that the data link layer is an Ethernet; this code won't
+ * work with anything else.
+ */
+ if (ioctl(fd, BIOCGDLT, (caddr_t)&dlt) == -1) {
+ logmsg(LOG_ERR, "BIOCGDLT: %m");
+ exit(1);
+ }
+ if (dlt != DLT_EN10MB) {
+ logmsg(LOG_ERR, "%s is not an ethernet", device);
+ exit(1);
+ }
+ /*
+ * Set filter program.
+ */
+ if (ioctl(fd, BIOCSETF, (caddr_t)&filter) == -1) {
+ logmsg(LOG_ERR, "BIOCSETF: %m");
+ exit(1);
+ }
+ return fd;
+}
+
+/*
+ * Perform various sanity checks on the RARP request packet. Return
+ * false on failure and log the reason.
+ */
+int
+rarp_check(u_char *p, u_int len)
+{
+ struct ether_header *ep = (struct ether_header *)p;
+ struct ether_arp *ap = (struct ether_arp *)(p + sizeof(*ep));
+
+ if (len < sizeof(*ep) + sizeof(*ap)) {
+ logmsg(LOG_ERR, "truncated request, got %u, expected %lu",
+ len, (u_long)(sizeof(*ep) + sizeof(*ap)));
+ return 0;
+ }
+ /*
+ * XXX This test might be better off broken out...
+ */
+ if (ntohs(ep->ether_type) != ETHERTYPE_REVARP ||
+ ntohs(ap->arp_hrd) != ARPHRD_ETHER ||
+ ntohs(ap->arp_op) != REVARP_REQUEST ||
+ ntohs(ap->arp_pro) != ETHERTYPE_IP ||
+ ap->arp_hln != 6 || ap->arp_pln != 4) {
+ logmsg(LOG_DEBUG, "request fails sanity check");
+ return 0;
+ }
+ if (bcmp((char *)&ep->ether_shost, (char *)&ap->arp_sha, 6) != 0) {
+ logmsg(LOG_DEBUG, "ether/arp sender address mismatch");
+ return 0;
+ }
+ if (bcmp((char *)&ap->arp_sha, (char *)&ap->arp_tha, 6) != 0) {
+ logmsg(LOG_DEBUG, "ether/arp target address mismatch");
+ return 0;
+ }
+ return 1;
+}
+
+#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(void)
+{
+ u_char *buf, *bp, *ep;
+ int cc, fd;
+ fd_set fds, listeners;
+ int bufsize, maxfd = 0;
+ struct if_info *ii;
+
+ if (iflist == NULL) {
+ logmsg(LOG_ERR, "no interfaces");
+ exit(1);
+ }
+ if (ioctl(iflist->ii_fd, BIOCGBLEN, (caddr_t)&bufsize) == -1) {
+ logmsg(LOG_ERR, "BIOCGBLEN: %m");
+ exit(1);
+ }
+ buf = malloc(bufsize);
+ if (buf == NULL) {
+ logmsg(LOG_ERR, "malloc: %m");
+ exit(1);
+ }
+
+ while (1) {
+ /*
+ * Find the highest numbered file descriptor for select().
+ * Initialize the set of descriptors to listen to.
+ */
+ FD_ZERO(&fds);
+ for (ii = iflist; ii != NULL; ii = ii->ii_next) {
+ FD_SET(ii->ii_fd, &fds);
+ if (ii->ii_fd > maxfd)
+ maxfd = ii->ii_fd;
+ }
+ listeners = fds;
+ if (select(maxfd + 1, &listeners, NULL, NULL, NULL) == -1) {
+ /* Don't choke when we get ptraced */
+ if (errno == EINTR)
+ continue;
+ logmsg(LOG_ERR, "select: %m");
+ exit(1);
+ }
+ for (ii = iflist; ii != NULL; ii = ii->ii_next) {
+ fd = ii->ii_fd;
+ if (!FD_ISSET(fd, &listeners))
+ continue;
+ again:
+ cc = read(fd, (char *)buf, bufsize);
+ /* Don't choke when we get ptraced */
+ if ((cc == -1) && (errno == EINTR))
+ goto again;
+#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 == -1) {
+ if (errno == EINVAL &&
+ (long)(tell(fd) + bufsize) < 0) {
+ (void)lseek(fd, 0, 0);
+ goto again;
+ }
+ logmsg(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) {
+ 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(u_long addr)
+{
+#ifdef HAVE_DIRENT_H
+ struct dirent *dent;
+#else
+ struct direct *dent;
+#endif
+ DIR *d;
+ char ipname[9];
+ static DIR *dd = NULL;
+
+ (void)sprintf(ipname, "%08lX", (u_long)ntohl(addr));
+
+ /*
+ * If directory is already open, rewind it. Otherwise, open it.
+ */
+ if ((d = dd) != NULL)
+ rewinddir(d);
+ else {
+ if (chdir(tftp_dir) == -1) {
+ logmsg(LOG_ERR, "chdir: %s: %m", tftp_dir);
+ exit(1);
+ }
+ d = opendir(".");
+ if (d == NULL) {
+ logmsg(LOG_ERR, "opendir: %m");
+ exit(1);
+ }
+ dd = d;
+ }
+ while ((dent = readdir(d)) != NULL)
+ if (strncmp(dent->d_name, ipname, 8) == 0)
+ return 1;
+ return 0;
+}
+
+/*
+ * Given a list of IP addresses, 'alist', return the first address that
+ * is on network 'net'; 'netmask' is a mask indicating the network portion
+ * of the address.
+ */
+u_long
+choose_ipaddr(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(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, (struct ether_addr *)&ep->ether_shost) != 0) {
+ logmsg(LOG_ERR, "cannot map %s to name",
+ eatoa(ep->ether_shost));
+ return;
+ }
+
+ if ((hp = gethostbyname(ename)) == NULL) {
+ logmsg(LOG_ERR, "cannot map %s to IP address", ename);
+ return;
+ }
+
+ /*
+ * Choose correct address from list.
+ */
+ if (hp->h_addrtype != AF_INET) {
+ logmsg(LOG_ERR, "cannot handle non IP addresses for %s",
+ ename);
+ return;
+ }
+ target_ipaddr = choose_ipaddr((u_long **)hp->h_addr_list,
+ ii->ii_ipaddr & ii->ii_netmask,
+ ii->ii_netmask);
+ if (target_ipaddr == 0) {
+ logmsg(LOG_ERR, "cannot find %s on net %s",
+ ename, intoa(ntohl(ii->ii_ipaddr & ii->ii_netmask)));
+ return;
+ }
+ if (sflag || rarp_bootable(target_ipaddr))
+ rarp_reply(ii, ep, target_ipaddr, len);
+ else if (verbose > 1)
+ logmsg(LOG_INFO, "%s %s at %s DENIED (not bootable)",
+ ii->ii_ifname,
+ eatoa(ep->ether_shost),
+ intoa(ntohl(target_ipaddr)));
+}
+
+/*
+ * Poke the kernel arp tables with the ethernet/ip address combinataion
+ * given. When processing a reply, we must do this so that the booting
+ * host (i.e. the guy running rarpd), won't try to ARP for the hardware
+ * address of the guy being booted (he cannot answer the ARP).
+ */
+#if BSD >= 199200
+struct sockaddr_inarp sin_inarp = {
+ sizeof(struct sockaddr_inarp), AF_INET, 0,
+ {0},
+ {0},
+ 0, 0
+};
+struct sockaddr_dl sin_dl = {
+ sizeof(struct sockaddr_dl), AF_LINK, 0, IFT_ETHER, 0, 6,
+ 0, ""
+};
+struct {
+ struct rt_msghdr rthdr;
+ char rtspace[512];
+} rtmsg;
+
+void
+update_arptab(u_char *ep, u_long ipaddr)
+{
+ int cc;
+ struct sockaddr_inarp *ar, *ar2;
+ struct sockaddr_dl *ll, *ll2;
+ struct rt_msghdr *rt;
+ int xtype, xindex;
+ static pid_t pid;
+ int r;
+ static int seq;
+
+ r = socket(PF_ROUTE, SOCK_RAW, 0);
+ if (r == -1) {
+ logmsg(LOG_ERR, "raw route socket: %m");
+ exit(1);
+ }
+ pid = getpid();
+
+ ar = &sin_inarp;
+ ar->sin_addr.s_addr = ipaddr;
+ ll = &sin_dl;
+ bcopy(ep, LLADDR(ll), 6);
+
+ /* Get the type and interface index */
+ rt = &rtmsg.rthdr;
+ bzero(rt, sizeof(rtmsg));
+ rt->rtm_version = RTM_VERSION;
+ rt->rtm_addrs = RTA_DST;
+ rt->rtm_type = RTM_GET;
+ rt->rtm_seq = ++seq;
+ ar2 = (struct sockaddr_inarp *)rtmsg.rtspace;
+ bcopy(ar, ar2, sizeof(*ar));
+ rt->rtm_msglen = sizeof(*rt) + sizeof(*ar);
+ errno = 0;
+ if ((write(r, rt, rt->rtm_msglen) == -1) && (errno != ESRCH)) {
+ logmsg(LOG_ERR, "rtmsg get write: %m");
+ close(r);
+ return;
+ }
+ do {
+ cc = read(r, rt, sizeof(rtmsg));
+ } while (cc > 0 && (rt->rtm_seq != seq || rt->rtm_pid != pid));
+ if (cc == -1) {
+ logmsg(LOG_ERR, "rtmsg get read: %m");
+ close(r);
+ return;
+ }
+ ll2 = (struct sockaddr_dl *)((u_char *)ar2 + ar2->sin_len);
+ if (ll2->sdl_family != AF_LINK) {
+ /*
+ * XXX I think this means the ip address is not on a
+ * directly connected network (the family is AF_INET in
+ * this case).
+ */
+ logmsg(LOG_ERR, "bogus link family (%d) wrong net for %08lX?\n",
+ ll2->sdl_family, ipaddr);
+ close(r);
+ return;
+ }
+ xtype = ll2->sdl_type;
+ xindex = ll2->sdl_index;
+
+ /* Set the new arp entry */
+ bzero(rt, sizeof(rtmsg));
+ rt->rtm_version = RTM_VERSION;
+ rt->rtm_addrs = RTA_DST | RTA_GATEWAY;
+ rt->rtm_inits = RTV_EXPIRE;
+ rt->rtm_rmx.rmx_expire = time(0) + ARPSECS;
+ rt->rtm_flags = RTF_HOST | RTF_STATIC;
+ rt->rtm_type = RTM_ADD;
+ rt->rtm_seq = ++seq;
+
+ bcopy(ar, ar2, sizeof(*ar));
+
+ ll2 = (struct sockaddr_dl *)((u_char *)ar2 + sizeof(*ar2));
+ bcopy(ll, ll2, sizeof(*ll));
+ ll2->sdl_type = xtype;
+ ll2->sdl_index = xindex;
+
+ rt->rtm_msglen = sizeof(*rt) + sizeof(*ar2) + sizeof(*ll2);
+ errno = 0;
+ if ((write(r, rt, rt->rtm_msglen) == -1) && (errno != EEXIST)) {
+ logmsg(LOG_ERR, "rtmsg add write: %m");
+ close(r);
+ return;
+ }
+ do {
+ cc = read(r, rt, sizeof(rtmsg));
+ } while (cc > 0 && (rt->rtm_seq != seq || rt->rtm_pid != pid));
+ close(r);
+ if (cc == -1) {
+ logmsg(LOG_ERR, "rtmsg add read: %m");
+ return;
+ }
+}
+#else
+void
+update_arptab(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) == -1)
+ logmsg(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(struct if_info *ii, struct ether_header *ep, u_long ipaddr,
+ u_int len)
+{
+ u_int n;
+ struct ether_arp *ap = (struct ether_arp *)(ep + 1);
+
+ update_arptab((u_char *)&ap->arp_sha, ipaddr);
+
+ /*
+ * Build the rarp reply by modifying the rarp request in place.
+ */
+ ap->arp_op = htons(REVARP_REPLY);
+
+#ifdef BROKEN_BPF
+ ep->ether_type = ETHERTYPE_REVARP;
+#endif
+ bcopy((char *)&ap->arp_sha, (char *)&ep->ether_dhost, 6);
+ bcopy((char *)ii->ii_eaddr, (char *)&ep->ether_shost, 6);
+ bcopy((char *)ii->ii_eaddr, (char *)&ap->arp_sha, 6);
+
+ bcopy((char *)&ipaddr, (char *)ap->arp_tpa, 4);
+ /* Target hardware is unchanged. */
+ bcopy((char *)&ii->ii_ipaddr, (char *)ap->arp_spa, 4);
+
+ /* Zero possible garbage after packet. */
+ bzero((char *)ep + (sizeof(*ep) + sizeof(*ap)),
+ len - (sizeof(*ep) + sizeof(*ap)));
+ n = write(ii->ii_fd, (char *)ep, len);
+ if (n != len)
+ logmsg(LOG_ERR, "write: only %d of %d bytes written", n, len);
+ if (verbose)
+ logmsg(LOG_INFO, "%s %s at %s REPLIED", ii->ii_ifname,
+ eatoa(ap->arp_tha),
+ intoa(ntohl(ipaddr)));
+}
+
+/*
+ * Get the netmask of an IP address. This routine is used if
+ * SIOCGIFNETMASK doesn't work.
+ */
+u_long
+ipaddrtonetmask(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);
+ logmsg(LOG_DEBUG, "unknown IP address class: %08lX", addr);
+ return htonl(0xffffffff);
+}
+
+/*
+ * A faster replacement for inet_ntoa().
+ */
+char *
+intoa(u_long addr)
+{
+ char *cp;
+ u_int byte;
+ int n;
+ static char buf[sizeof(".xxx.xxx.xxx.xxx")];
+
+ cp = &buf[sizeof buf];
+ *--cp = '\0';
+
+ n = 4;
+ do {
+ byte = addr & 0xff;
+ *--cp = byte % 10 + '0';
+ byte /= 10;
+ if (byte > 0) {
+ *--cp = byte % 10 + '0';
+ byte /= 10;
+ if (byte > 0)
+ *--cp = byte + '0';
+ }
+ *--cp = '.';
+ addr >>= 8;
+ } while (--n > 0);
+
+ return cp + 1;
+}
+
+char *
+eatoa(u_char *ea)
+{
+ static char buf[sizeof("xx:xx:xx:xx:xx:xx")];
+
+ (void)sprintf(buf, "%x:%x:%x:%x:%x:%x",
+ ea[0], ea[1], ea[2], ea[3], ea[4], ea[5]);
+ return (buf);
+}
+
+void
+logmsg(int pri, const char *fmt, ...)
+{
+ va_list v;
+ FILE *fp;
+ char *newfmt;
+
+ va_start(v, fmt);
+ if (dflag) {
+ if (pri == LOG_ERR)
+ fp = stderr;
+ else
+ fp = stdout;
+ if (expand_syslog_m(fmt, &newfmt) == -1) {
+ vfprintf(fp, fmt, v);
+ } else {
+ vfprintf(fp, newfmt, v);
+ free(newfmt);
+ }
+ fputs("\n", fp);
+ fflush(fp);
+ } else {
+ vsyslog(pri, fmt, v);
+ }
+ va_end(v);
+}
+
+int
+expand_syslog_m(const char *fmt, char **newfmt) {
+ const char *str, *m;
+ char *p, *np;
+
+ p = strdup("");
+ str = fmt;
+ while ((m = strstr(str, "%m")) != NULL) {
+ asprintf(&np, "%s%.*s%s", p, (int)(m - str),
+ str, strerror(errno));
+ free(p);
+ if (np == NULL) {
+ errno = ENOMEM;
+ return (-1);
+ }
+ p = np;
+ str = m + 2;
+ }
+
+ if (*str != '\0') {
+ asprintf(&np, "%s%s", p, str);
+ free(p);
+ if (np == NULL) {
+ errno = ENOMEM;
+ return (-1);
+ }
+ p = np;
+ }
+
+ *newfmt = p;
+ return (0);
+}
diff --git a/usr.sbin/raycontrol/Makefile b/usr.sbin/raycontrol/Makefile
new file mode 100644
index 0000000..50e7472
--- /dev/null
+++ b/usr.sbin/raycontrol/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= raycontrol
+MAN= raycontrol.8
+
+CFLAGS+= -I${.CURDIR}/../../sys
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/raycontrol/raycontrol.8 b/usr.sbin/raycontrol/raycontrol.8
new file mode 100644
index 0000000..4bbaef8
--- /dev/null
+++ b/usr.sbin/raycontrol/raycontrol.8
@@ -0,0 +1,307 @@
+.\"
+.\"Copyright (C) 2000
+.\"Dr. Duncan McLennan Barclay, dmlb@ragnet.demon.co.uk.
+.\"
+.\" All rights reserved.
+.\"
+.\"Redistribution and use in source and binary forms, with or without
+.\"modification, are permitted provided that the following conditions
+.\"are met:
+.\"1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"3. Neither the name of the author nor the names of any co-contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\"THIS SOFTWARE IS PROVIDED BY DUNCAN BARCLAY AND CONTRIBUTORS ``AS IS'' AND
+.\"ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\"IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\"ARE DISCLAIMED. IN NO EVENT SHALL DUNCAN BARCLAY OR CONTRIBUTORS BE LIABLE
+.\"FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\"DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\"OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\"HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\"LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\"OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\"SUCH DAMAGE.
+.\"
+.\" Copyright (c) 1997, 1998, 1999
+.\" Bill Paul <wpaul@ctr.columbia.edu> All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Bill Paul.
+.\" 4. Neither the name of the author nor the names of any co-contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+.\" THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd March 21, 2000
+.Dt RAYCONTROL 8
+.Os
+.Sh NAME
+.Nm raycontrol
+.Nd configure Raytheon Raylink/Webgear Aviator devices
+.Sh SYNOPSIS
+.Nm
+.Fl i Ar iface Op Fl o
+.Nm
+.Fl i Ar iface Fl t Ar tx rate
+.Nm
+.Fl i Ar iface Fl n Ar network name
+.Nm
+.Fl i Ar iface Fl c Ar ap status
+.Nm
+.Fl i Ar iface Fl p Ar port type
+.Nm
+.Fl i Ar iface Fl m Ar mac address
+.Nm
+.Fl i Ar iface Fl d Ar max data length
+.Nm
+.Fl i Ar iface Fl r Ar RTS threshold
+.Nm
+.Fl i Ar iface Fl f Ar hopset
+.Nm
+.Fl i Ar iface Fl P Ar 0|1
+.Nm
+.Fl i Ar iface Fl S Ar max_sleep_duration
+.Nm
+.Fl i Ar iface Fl Z Ar zero signal cache
+.Nm
+.Fl i Ar iface Fl C Ar display signal cache
+.Sh DESCRIPTION
+The
+.Nm
+command controls the operation of Raylink/Webgear wireless networking
+devices via the
+.Xr ray 4
+driver.
+Most of the parameters that can be changed relate to the
+IEEE 802.11 protocol which the card implements.
+This includes
+the station name, whether the station is operating in ad-hoc
+or infrastructure mode, and the network name of a service
+set to join - the BSS in ad-hoc mode or ESS if infrastructure mode is enabled.
+The
+.Nm
+command can also be used to view the current settings of these parameters
+and to dump out the values of the card's statistics counters.
+.Pp
+The
+.Ar iface
+argument given to
+.Nm
+should be the logical interface name associated with the Raylink/Webgear
+device (ray0, ray1, etc...).
+.Sh OPTIONS
+The options are as follows:
+.Bl -tag -width Fl
+.It Fl i Ar iface Op Fl o
+Display the current settings of the specified Raylink/Webgear interface.
+This retrieves the current card settings from the driver and prints them
+out.
+The results from this command are a snapshot of the card settings.
+Using the additional
+.Fl o
+flag will cause
+.Nm
+to print out the statistics counters instead of the card settings.
+.It Fl i Ar iface Fl t Ar tx rate
+Set the transmit rate of the specified interface.
+The
+NICs support a maximum transmit rate of 2Mbps.
+The following table shows the
+legal transmit rate settings and the corresponding transmit speeds:
+.Bl -column "TX rate " "NIC speed " -offset indent
+.Em "TX rate NIC speed"
+1 Very Low (0.5Mbps)
+2 Low (1Mbps)
+3 Medium (1.5Mbps)
+4 High (2Mbps)
+.El
+.Pp
+The version 4 firmware may ignore this setting.
+Note, that the IEEE 802.11
+standard
+only allows 1Mbps or 2Mbps operation, and that the generally accepted
+reading of the IEEE 802.11 standard is that 2Mbps is only allowed in
+infrastructure mode.
+.It Fl i Ar iface Fl n Ar network name
+Set the name of the service set that this station wishes to
+join.
+The
+.Ar network name
+can be any text string up to 32 characters in length.
+The default name
+is the string "NETWORK_NAME" which should allow the station to connect to
+the default Webgear ad-hoc network.
+.It Fl i Ar iface Fl p Ar port type
+Set the
+.Ar port type
+for a specified interface.
+The legal values for
+.Ar port type
+are 0 (ad-hoc mode) and 1 (infrastructure mode).
+In ad-hoc mode, the station can
+communicate directly with any other stations within direct radio range
+(provided that they are also operating in ad-hoc mode).
+In inrastructure mode,
+hosts must associate with a service set controlled by an access point,
+that relays traffic between end stations.
+The default setting is 0
+(ad-hoc mode).
+.Pp
+When in ad-hoc mode the station will create a BSS with the network name
+specified by the
+.Fl n
+option if it cannot find an existing network of that name on the
+currently configured hopset (see the
+.Fl f
+option).
+.It Fl i Ar iface Fl m Ar mac address
+Set the station address for the specified interface.
+The
+.Ar mac address
+is specified as a series of six hexadecimal values separated by colons,
+e.g.: 00:60:1d:12:34:56.
+This programs the new address into the card
+and updates the interface as well.
+.It Fl i Ar iface Fl d Ar max_data_length
+Set the maximum transmit frame size for a specified interface.
+The
+.Ar max data length
+can be any number from 350 to 2304 or -1 to disable fragmentation.
+The default is -1.
+.It Fl i Ar iface Fl r Ar RTS threshold
+Set the RTS/CTS threshold for a given interface.
+This controls the
+number of bytes used for the RTS/CTS handshake boundary.
+The
+.Ar RTS threshold
+can be any value between -1 and 2047.
+The default is -1 (disable).
+.It Fl i Ar iface Fl f Ar hopset
+Set the radio hopset of a given interface.
+The
+.Ar hopset
+should be specified as a country code as shown in the table below.
+The
+hopset varies both the number of RF channels and their frequencies
+and is dependent on radio regulations specified
+by regional authorities.
+.Bl -column "Hopset ID " "Country " -offset indent
+.Em "Hopset ID Country"
+1 USA
+2 Europe
+3 Japan
+4 Korea
+5 Spain
+6 France
+7 Israel
+8 Australia
+9 Japan Test
+.El
+.Pp
+Whilst the card can be programmed to work with any hopset it makes
+sense to use the hopset for your own region to avoid interference from
+and interfering with other users of the RF spectrum (in places like
+France this is the military).
+.Pp
+Note that all stations must be set to the same hopset in order to
+communicate.
+.It Fl i Ar iface Fl P Ar 0|1
+Enable or disable power management on a given interface.
+Enabling
+power management uses an alternating sleep/wake protocol to help
+conserve power on mobile stations, at the cost of some increased
+receive latency.
+Power management is off by default.
+.Pp
+Note that power
+management requires the cooperation of an access point in order to
+function; it is not functional in ad-hoc mode.
+Legal
+values for this parameter are 0 (off) and 1 (on).
+.It Fl i Ar iface Fl S Ar max_sleep_interval
+Specify the sleep interval to use when power management is enabled.
+The
+.Ar max_sleep_interval
+is specified in milliseconds.
+The default is 100.
+.It Fl i Ar iface Fl Z
+Clear the signal strength cache maintained internally by the
+.Nm ray
+driver.
+.It Fl i Ar iface Fl C
+Display the cached signal strength information maintained by the
+.Nm ray
+driver.
+The driver retains information about signal strength and
+noise level for packets received from different hosts.
+For
+infrastructure networks the cache stores the signal strength of the
+access point.
+.Pp
+The driver also uses the cache to pick the best antenna when
+transmitting.
+.El
+.Sh SEE ALSO
+.Xr ray 4 ,
+.Xr ifconfig 8
+.Sh HISTORY
+The
+.Nm
+command first appeared in
+.Fx 4.0 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+command was written by
+.An Duncan Barclay Aq dmlb@ragnet.demon.co.uk
+and based on the
+.Nm wicontrol
+command by
+.An Bill Paul Aq wpaul@ctr.columbia.edu .
+.Sh BUGS
+The
+.Fl m ,
+.Fl P ,
+.Fl S
+and
+.Fl Z
+options aren't implemented yet.
+No access point was available for testing
+against.
+.Pp
+Not tested with Version 5 firmware.
+.Pp
+Hopset changing may not work with version 4 firmware.
+.Pp
+The
+.Fl W
+option is un-documented on purpose.
diff --git a/usr.sbin/raycontrol/raycontrol.c b/usr.sbin/raycontrol/raycontrol.c
new file mode 100644
index 0000000..e361c5d
--- /dev/null
+++ b/usr.sbin/raycontrol/raycontrol.c
@@ -0,0 +1,488 @@
+/*
+ * Copyright (c) 1999, 2000
+ * Dr. Duncan McLennan Barclay, dmlb@ragnet.demon.co.uk. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DUNCAN BARCLAY AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DUNCAN BARCLAY OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <net/if_ieee80211.h>
+
+#include <dev/ray/if_rayreg.h>
+#include <dev/ray/if_raymib.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <err.h>
+
+static char * ray_printhex (u_int8_t *d, char *s, int len);
+static void ray_getval (char *iface, struct ray_param_req *rreq);
+static void ray_getstats (char *iface, struct ray_stats_req *sreq);
+static int ray_version (char *iface);
+static void ray_dumpstats (char *iface);
+static void ray_dumpinfo (char *iface);
+static void ray_setstr (char *iface, u_int8_t mib, char *s);
+static void ray_setword (char *iface, u_int8_t mib, u_int16_t v);
+static void ray_setval (char *iface, struct ray_param_req *rreq);
+static void usage (char *p);
+
+static char *mib_strings[] = RAY_MIB_STRINGS;
+static char *mib_help_strings[] = RAY_MIB_HELP_STRINGS;
+static int mib_info[RAY_MIB_MAX+1][3] = RAY_MIB_INFO;
+
+static char *
+ray_printhex(u_int8_t *d, char *s, int len)
+{
+ static char buf[3*256];
+ char *p;
+ int i;
+
+ if (len > 256)
+ err(1, "Byte string too long");
+
+ sprintf(buf, "%02x", *d);
+ for (p = buf + 2, i = 1; i < len; i++) {
+ sprintf(p, "%s%02x", s, *(d+i));
+ p = p + 2 + strlen(s);
+ }
+
+ return(buf);
+}
+
+static void
+ray_getval(char *iface, struct ray_param_req *rreq)
+{
+ struct ifreq ifr;
+ int s;
+
+ bzero((char *)&ifr, sizeof(ifr));
+
+ strlcpy(ifr.ifr_name, iface, IFNAMSIZ);
+ ifr.ifr_data = (caddr_t)rreq;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+
+ if (s == -1)
+ err(1, "socket");
+
+ if (ioctl(s, SIOCGRAYPARAM, &ifr) == -1)
+ warn("SIOCGRAYPARAM failed with failcode 0x%02x",
+ rreq->r_failcause);
+
+ close(s);
+}
+
+static void
+ray_getsiglev(char *iface, struct ray_siglev *siglev)
+{
+ struct ifreq ifr;
+ int s;
+
+ bzero((char *)&ifr, sizeof(ifr));
+
+ strcpy(ifr.ifr_name, iface);
+ ifr.ifr_data = (caddr_t)siglev;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+
+ if (s == -1)
+ err(1, "socket");
+
+ if (ioctl(s, SIOCGRAYSIGLEV, &ifr) == -1)
+ err(1, "SIOCGRAYSIGLEV failed");
+
+ close(s);
+}
+
+static void
+ray_getstats(char *iface, struct ray_stats_req *sreq)
+{
+ struct ifreq ifr;
+ int s;
+
+ bzero((char *)&ifr, sizeof(ifr));
+
+ strcpy(ifr.ifr_name, iface);
+ ifr.ifr_data = (caddr_t)sreq;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+
+ if (s == -1)
+ err(1, "socket");
+
+ if (ioctl(s, SIOCGRAYSTATS, &ifr) == -1)
+ err(1, "SIOCGRAYSTATS failed");
+
+ close(s);
+}
+
+static int
+ray_version(char *iface)
+{
+ struct ray_param_req rreq;
+
+ if (iface == NULL)
+ errx(1, "must specify interface name");
+
+ bzero((char *)&rreq, sizeof(rreq));
+ rreq.r_paramid = RAY_MIB_VERSION;
+ ray_getval(iface, &rreq);
+ return(*rreq.r_data);
+}
+
+static void
+ray_dumpinfo(char *iface)
+{
+ struct ray_param_req rreq;
+ u_int8_t mib, version;
+
+ if (iface == NULL)
+ errx(1, "must specify interface name");
+
+ bzero((char *)&rreq, sizeof(rreq));
+
+ version = ray_version(iface);
+ printf("%-26s\t", mib_strings[RAY_MIB_VERSION]);
+ printf("%d\n", 3+version);
+
+ for (mib = RAY_MIB_NET_TYPE; mib <= RAY_MIB_MAX; mib++) {
+
+ if ((mib_info[mib][0] & version) == 0)
+ continue;
+ if (mib == RAY_MIB_VERSION)
+ continue;
+
+ rreq.r_paramid = mib;
+ ray_getval(iface, &rreq);
+ printf("%-26s\t", mib_strings[mib]);
+ switch (rreq.r_len) {
+
+ case 2:
+ printf("0x%02x%02x", *rreq.r_data, *(rreq.r_data+1));
+ break;
+
+ case ETHER_ADDR_LEN:
+ printf("%s",
+ ray_printhex(rreq.r_data, ":", rreq.r_len));
+ break;
+
+ case IEEE80211_NWID_LEN:
+ printf("%-32s", (char *)rreq.r_data);
+ break;
+
+
+ case 1:
+ default:
+ printf("0x%02x", *rreq.r_data);
+ break;
+ }
+ printf("\t%s\n", mib_help_strings[mib]);
+ }
+}
+
+static void
+ray_dumpsiglev(char *iface)
+{
+ struct ray_siglev siglevs[RAY_NSIGLEVRECS];
+ int i;
+
+ if (iface == NULL)
+ errx(1, "must specify interface name");
+
+ bzero((char *)siglevs, sizeof(siglevs));
+
+ ray_getsiglev(iface, siglevs);
+
+ for (i = 0; i < RAY_NSIGLEVRECS; i++) {
+ printf("Slot %d: %s", i,
+ ray_printhex(siglevs[i].rsl_host, ":", ETHER_ADDR_LEN));
+ printf(" %s",
+ ray_printhex(siglevs[i].rsl_siglevs, ",", RAY_NSIGLEV));
+ printf(" %s\n",
+ ray_printhex(siglevs[i].rsl_antennas, "", RAY_NANTENNA));
+ }
+
+}
+
+static void
+ray_dumpstats(char *iface)
+{
+ struct ray_stats_req sreq;
+
+ if (iface == NULL)
+ errx(1, "must specify interface name");
+
+ bzero((char *)&sreq, sizeof(sreq));
+
+ ray_getstats(iface, &sreq);
+
+ printf("Receiver overflows %lu\n",
+ (unsigned long int)sreq.rxoverflow);
+ printf("Receiver checksum errors %lu\n",
+ (unsigned long int)sreq.rxcksum);
+ printf("Header checksum errors %lu\n",
+ (unsigned long int)sreq.rxhcksum);
+ printf("Clear channel noise level %u\n", sreq.rxnoise);
+}
+
+static void
+ray_setval(char *iface, struct ray_param_req *rreq)
+{
+ struct ifreq ifr;
+ int s;
+
+ bzero((char *)&ifr, sizeof(ifr));
+
+ strcpy(ifr.ifr_name, iface);
+ ifr.ifr_data = (caddr_t)rreq;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+
+ if (s == -1)
+ err(1, "socket");
+
+ if (ioctl(s, SIOCSRAYPARAM, &ifr) == -1) {
+ err(1, "SIOCSRAYPARAM failed with failcode 0x%02x",
+ rreq->r_failcause);
+ }
+
+ close(s);
+}
+
+static void
+ray_setword(char *iface, u_int8_t mib, u_int16_t v)
+{
+ struct ray_param_req rreq;
+
+ if (iface == NULL)
+ errx(1, "must specify interface name");
+
+ bzero((char *)&rreq, sizeof(rreq));
+
+ rreq.r_paramid = mib;
+ rreq.r_len = RAY_MIB_SIZE(mib_info, mib, ray_version(iface));
+ switch (rreq.r_len) {
+
+ case 1:
+ *rreq.r_data = (u_int8_t)(v & 0xff);
+ break;
+
+ case 2:
+ *rreq.r_data = (u_int8_t)((v & 0xff00) >> 8);
+ *(rreq.r_data+1) = (u_int8_t)(v & 0xff);
+ break;
+
+ default:
+ break;
+ }
+
+ ray_setval(iface, &rreq);
+}
+
+static void
+ray_setstr(char *iface, u_int8_t mib, char *s)
+{
+ struct ray_param_req rreq;
+
+ if (iface == NULL)
+ errx(1, "must specify interface name");
+ if (s == NULL)
+ errx(1, "must specify string");
+ if (strlen(s) > RAY_MIB_SIZE(mib_info, mib, ray_version(iface)))
+ errx(1, "string too long");
+
+ bzero((char *)&rreq, sizeof(rreq));
+
+ rreq.r_paramid = mib;
+ rreq.r_len = RAY_MIB_SIZE(mib_info, mib, ray_version(iface));
+ bcopy(s, (char *)rreq.r_data, strlen(s));
+
+ ray_setval(iface, &rreq);
+}
+
+static void
+usage(char *p)
+{
+ fprintf(stderr, "usage: %s -i iface\n", p);
+ fprintf(stderr, "\t%s -i iface -o\n", p);
+ fprintf(stderr, "\t%s -i iface -t tx rate\n", p);
+ fprintf(stderr, "\t%s -i iface -n network name\n", p);
+ fprintf(stderr, "\t%s -i iface -p port type\n", p);
+ fprintf(stderr, "\t%s -i iface -m mac address\n", p);
+ fprintf(stderr, "\t%s -i iface -d max data length\n", p);
+ fprintf(stderr, "\t%s -i iface -r RTS threshold\n", p);
+ fprintf(stderr, "\t%s -i iface -f hopset\n", p);
+ fprintf(stderr, "\t%s -i iface -P 0|1\n", p);
+ fprintf(stderr, "\t%s -i iface -S max sleep duration\n", p);
+ fprintf(stderr, "\t%s -i iface -C print signal cache\n", p);
+
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+
+ char *iface, *p;
+ int ch, val;
+
+ iface = NULL;
+ p = argv[0];
+
+ /* Get the interface name */
+ opterr = 0;
+ ch = getopt(argc, argv, "i:");
+ if (ch == 'i') {
+ iface = optarg;
+ } else {
+ if (argc > 1 && *argv[1] != '-') {
+ iface = argv[1];
+ optind = 2;
+ } else {
+ iface = "ray0";
+ optind = 1;
+ }
+ optreset = 1;
+ }
+ opterr = 1;
+
+ while ((ch = getopt(argc, argv, "hoCi:d:f:n:p:r:t:W:")) != -1) {
+ switch (ch) {
+
+ case 'i':
+ iface = optarg;
+ break;
+
+ case 'd':
+ val = atoi(optarg);
+ if (((val < 350) &&
+ (val != -1)) || (val > RAY_MIB_FRAG_THRESH_MAXIMUM))
+ usage(p);
+ if (val == -1)
+ val = 0x7fff;
+ ray_setword(iface, RAY_MIB_FRAG_THRESH, val);
+ exit(0);
+ break;
+
+ case 'f':
+ val = atoi(optarg);
+ if ((val < RAY_MIB_COUNTRY_CODE_MIMIMUM) ||
+ (val > RAY_MIB_COUNTRY_CODE_MAXIMUM))
+ usage(p);
+ ray_setword(iface, RAY_MIB_COUNTRY_CODE, val);
+ exit(0);
+ break;
+
+ case 'n':
+ ray_setstr(iface, RAY_MIB_SSID, optarg);
+ exit(0);
+ break;
+
+ case 'o':
+ ray_dumpstats(iface);
+ exit(0);
+ break;
+
+ case 'p':
+ val = atoi(optarg);
+ if ((val < 0) || (val > 1))
+ usage(p);
+ ray_setword(iface, RAY_MIB_NET_TYPE, val);
+ exit(0);
+ break;
+
+
+ case 'r':
+ val = atoi(optarg);
+ if ((val < -1) || (val > RAY_MIB_RTS_THRESH_MAXIMUM))
+ usage(p);
+ if (val == -1)
+ val = 0x7fff;
+ ray_setword(iface, RAY_MIB_RTS_THRESH, val);
+ exit(0);
+ break;
+
+ case 't':
+ val = atoi(optarg);
+ if ((val < RAY_MIB_BASIC_RATE_SET_MINIMUM) ||
+ (val > RAY_MIB_BASIC_RATE_SET_MAXIMUM))
+ usage(p);
+ ray_setword(iface, RAY_MIB_BASIC_RATE_SET, val);
+ exit(0);
+ break;
+
+ case 'C':
+ ray_dumpsiglev(iface);
+ exit(0);
+ break;
+
+ case 'W':
+ {
+ char *stringp, **ap, *av[5];
+ u_int8_t mib;
+
+ stringp = optarg;
+ ap = av;
+ *ap = strsep(&stringp, ":");
+ ap++;
+ *ap = strsep(&stringp, ":");
+ mib = atoi(av[0]);
+ sscanf(av[1], "%x", &val);
+ printf("mib %d, val 0x%02x\n", mib, val);
+ ray_setword(iface, mib, val);
+ }
+ exit(0);
+ break;
+
+ case 'h':
+ default:
+ usage(p);
+
+ }
+ }
+
+ if (iface == NULL)
+ usage(p);
+
+ ray_dumpinfo(iface);
+
+ exit(0);
+}
diff --git a/usr.sbin/repquota/Makefile b/usr.sbin/repquota/Makefile
new file mode 100644
index 0000000..9b9a078
--- /dev/null
+++ b/usr.sbin/repquota/Makefile
@@ -0,0 +1,8 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+PROG= repquota
+WARNS?= 2
+MAN= 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..b92e206
--- /dev/null
+++ b/usr.sbin/repquota/repquota.8
@@ -0,0 +1,104 @@
+.\" Copyright (c) 1983, 1990, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Robert Elz at The University of Melbourne.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)repquota.8 8.1 (Berkeley) 6/6/93
+.\" $FreeBSD$
+.\"
+.Dd June 6, 1993
+.Dt REPQUOTA 8
+.Os
+.Sh NAME
+.Nm repquota
+.Nd summarize quotas for a filesystem
+.Sh SYNOPSIS
+.Nm
+.Op Fl g
+.Op Fl u
+.Op Fl v
+.Ar filesystem Ar ...
+.Nm
+.Op Fl g
+.Op Fl u
+.Op Fl v
+.Fl a
+.Sh DESCRIPTION
+.Nm Repquota
+prints a summary of the disk usage and quotas for the
+specified filesystems.
+.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 filesystem 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..aadb784
--- /dev/null
+++ b/usr.sbin/repquota/repquota.c
@@ -0,0 +1,410 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/*
+ * Quota report
+ */
+#include <sys/param.h>
+#include <ufs/ufs/quota.h>
+#include <err.h>
+#include <errno.h>
+#include <fstab.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <utmp.h>
+
+/* Let's be paranoid about block size */
+#if 10 > DEV_BSHIFT
+#define dbtokb(db) \
+ ((off_t)(db) >> (10-DEV_BSHIFT))
+#elif 10 < DEV_BSHIFT
+#define dbtokb(db) \
+ ((off_t)(db) << (DEV_BSHIFT-10))
+#else
+#define dbtokb(db) (db)
+#endif
+
+#define max(a,b) ((a) >= (b) ? (a) : (b))
+
+const char *qfname = QUOTAFILENAME;
+const char *qfextension[] = INITQFNAMES;
+
+struct fileusage {
+ struct fileusage *fu_next;
+ struct dqblk fu_dqblk;
+ u_long fu_id;
+ char fu_name[1];
+ /* actually bigger */
+};
+#define FUHASH 1024 /* must be power of two */
+struct fileusage *fuhead[MAXQUOTAS][FUHASH];
+struct fileusage *lookup(u_long, int);
+struct fileusage *addid(u_long, int, char *);
+u_long highid[MAXQUOTAS]; /* highest addid()'ed identifier per type */
+
+int vflag; /* verbose */
+int aflag; /* all filesystems */
+
+int 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 %6s",
+ fup->fu_dqblk.dqb_bsoftlimit &&
+ fup->fu_dqblk.dqb_curblocks >=
+ fup->fu_dqblk.dqb_bsoftlimit ? '+' : '-',
+ fup->fu_dqblk.dqb_isoftlimit &&
+ fup->fu_dqblk.dqb_curinodes >=
+ fup->fu_dqblk.dqb_isoftlimit ? '+' : '-',
+ (u_long)(dbtokb(fup->fu_dqblk.dqb_curblocks)),
+ (u_long)(dbtokb(fup->fu_dqblk.dqb_bsoftlimit)),
+ (u_long)(dbtokb(fup->fu_dqblk.dqb_bhardlimit)),
+ fup->fu_dqblk.dqb_bsoftlimit &&
+ fup->fu_dqblk.dqb_curblocks >=
+ fup->fu_dqblk.dqb_bsoftlimit ?
+ timeprt(fup->fu_dqblk.dqb_btime) : "-");
+ printf(" %7lu %7lu %7lu %6s\n",
+ (u_long)fup->fu_dqblk.dqb_curinodes,
+ (u_long)fup->fu_dqblk.dqb_isoftlimit,
+ (u_long)fup->fu_dqblk.dqb_ihardlimit,
+ fup->fu_dqblk.dqb_isoftlimit &&
+ fup->fu_dqblk.dqb_curinodes >=
+ fup->fu_dqblk.dqb_isoftlimit ?
+ timeprt(fup->fu_dqblk.dqb_itime) : "-");
+ fup->fu_dqblk = zerodqblk;
+ }
+ return (0);
+}
+
+/*
+ * Check to see if target appears in list of size cnt.
+ */
+int
+oneof(target, list, cnt)
+ register char *target, *list[];
+ int cnt;
+{
+ register int i;
+
+ for (i = 0; i < cnt; i++)
+ if (strcmp(target, list[i]) == 0)
+ return (i);
+ return (-1);
+}
+
+/*
+ * Check to see if a particular quota is to be enabled.
+ */
+int
+hasquota(fs, type, qfnamep)
+ register struct fstab *fs;
+ int type;
+ char **qfnamep;
+{
+ register char *opt;
+ char *cp;
+ static char initname, usrname[100], grpname[100];
+ static char buf[BUFSIZ];
+
+ if (!initname) {
+ sprintf(usrname, "%s%s", qfextension[USRQUOTA], qfname);
+ sprintf(grpname, "%s%s", qfextension[GRPQUOTA], qfname);
+ initname = 1;
+ }
+ strcpy(buf, fs->fs_mntops);
+ for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
+ if ((cp = index(opt, '=')))
+ *cp++ = '\0';
+ if (type == USRQUOTA && strcmp(opt, usrname) == 0)
+ break;
+ if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
+ break;
+ }
+ if (!opt)
+ return (0);
+ if (cp) {
+ *qfnamep = cp;
+ return (1);
+ }
+ (void) sprintf(buf, "%s/%s.%s", fs->fs_file, qfname, qfextension[type]);
+ *qfnamep = buf;
+ return (1);
+}
+
+/*
+ * Routines to manage the file usage table.
+ *
+ * Lookup an id of a specific type.
+ */
+struct fileusage *
+lookup(id, type)
+ u_long id;
+ int type;
+{
+ register struct fileusage *fup;
+
+ for (fup = fuhead[type][id & (FUHASH-1)]; fup != 0; fup = fup->fu_next)
+ if (fup->fu_id == id)
+ return (fup);
+ return ((struct fileusage *)0);
+}
+
+/*
+ * Add a new file usage id if it does not already exist.
+ */
+struct fileusage *
+addid(id, type, name)
+ u_long id;
+ int type;
+ char *name;
+{
+ struct fileusage *fup, **fhp;
+ int len;
+
+ if ((fup = lookup(id, type)))
+ return (fup);
+ if (name)
+ len = strlen(name);
+ else
+ len = 10;
+ if ((fup = (struct fileusage *)calloc(1, sizeof(*fup) + len)) == NULL)
+ errx(1, "out of memory for fileusage structures");
+ fhp = &fuhead[type][id & (FUHASH - 1)];
+ fup->fu_next = *fhp;
+ *fhp = fup;
+ fup->fu_id = id;
+ if (id > highid[type])
+ highid[type] = id;
+ if (name) {
+ bcopy(name, fup->fu_name, len + 1);
+ } else {
+ sprintf(fup->fu_name, "%lu", id);
+ }
+ return (fup);
+}
+
+/*
+ * Calculate the grace period and return a printable string for it.
+ */
+char *
+timeprt(seconds)
+ time_t seconds;
+{
+ time_t hours, minutes;
+ static char buf[20];
+ static time_t now;
+
+ if (now == 0)
+ time(&now);
+ if (now > seconds) {
+ strlcpy(buf, "none", sizeof (buf));
+ return (buf);
+ }
+ seconds -= now;
+ minutes = (seconds + 30) / 60;
+ hours = (minutes + 30) / 60;
+ if (hours >= 36) {
+ sprintf(buf, "%lddays", (long)(hours + 12) / 24);
+ return (buf);
+ }
+ if (minutes >= 60) {
+ sprintf(buf, "%2ld:%ld", (long)minutes / 60,
+ (long)minutes % 60);
+ return (buf);
+ }
+ sprintf(buf, "%2ld", (long)minutes);
+ return (buf);
+}
diff --git a/usr.sbin/rip6query/Makefile b/usr.sbin/rip6query/Makefile
new file mode 100644
index 0000000..51438a1
--- /dev/null
+++ b/usr.sbin/rip6query/Makefile
@@ -0,0 +1,10 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+# $FreeBSD$
+
+PROG= rip6query
+MAN= rip6query.8
+
+WARNS?= 2
+CFLAGS+= -DINET6 -I${.CURDIR}/../route6d
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rip6query/rip6query.8 b/usr.sbin/rip6query/rip6query.8
new file mode 100644
index 0000000..210234f
--- /dev/null
+++ b/usr.sbin/rip6query/rip6query.8
@@ -0,0 +1,63 @@
+.\" $KAME: rip6query.8,v 1.4 2001/03/15 12:35:24 itojun Exp $
+.\"
+.\" Copyright (C) 1998 and 1999 WIDE Project.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the project nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: rip6query.8,v 1.2 2000/01/19 06:24:55 itojun Exp $
+.\" $FreeBSD$
+.\"
+.Dd October 7, 1999
+.Dt RIP6QUERY 8
+.Os
+.Sh NAME
+.Nm rip6query
+.Nd RIPng debugging tool
+.\"
+.Sh SYNOPSIS
+.Nm
+.Op Fl I Ar interface
+.Ar destination
+.\"
+.Sh DESCRIPTION
+.Nm
+requests remote RIPng daemon on
+.Ar destination
+to dump RIPng routing information.
+.Fl I
+lets you specify outgoing
+.Ar interface
+for the query packet,
+and is useful when link-local address is specified for
+.Ar destination .
+.\"
+.Sh SEE ALSO
+.Xr route6d 8
+.\"
+.Sh HISTORY
+The
+.Nm
+command first appeared in WIDE Hydrangea IPv6 protocol stack kit.
diff --git a/usr.sbin/rip6query/rip6query.c b/usr.sbin/rip6query/rip6query.c
new file mode 100644
index 0000000..90d65aa
--- /dev/null
+++ b/usr.sbin/rip6query/rip6query.c
@@ -0,0 +1,208 @@
+/* $KAME: rip6query.c,v 1.11 2001/05/08 04:36:37 itojun Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <stdio.h>
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <signal.h>
+#include <errno.h>
+#include <err.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+
+#include <net/if.h>
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+#include <net/if_var.h>
+#endif /* __FreeBSD__ >= 3 */
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include "route6d.h"
+
+/* wrapper for KAME-special getnameinfo() */
+#ifndef NI_WITHSCOPEID
+#define NI_WITHSCOPEID 0
+#endif
+
+int s;
+struct sockaddr_in6 sin6;
+struct rip6 *ripbuf;
+
+#define RIPSIZE(n) (sizeof(struct rip6) + (n-1) * sizeof(struct netinfo6))
+
+int main __P((int, char **));
+static void usage __P((void));
+static const char *sa_n2a __P((struct sockaddr *));
+static const char *inet6_n2a __P((struct in6_addr *));
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ struct netinfo6 *np;
+ struct sockaddr_in6 fsock;
+ int i, n, len, flen;
+ int c;
+ int ifidx = -1;
+ int error;
+ char pbuf[10];
+ struct addrinfo hints, *res;
+
+ while ((c = getopt(argc, argv, "I:")) != -1) {
+ switch (c) {
+ case 'I':
+ ifidx = if_nametoindex(optarg);
+ if (ifidx == 0) {
+ errx(1, "invalid interface %s", optarg);
+ /*NOTREACHED*/
+ }
+ break;
+ default:
+ usage();
+ exit(1);
+ /*NOTREACHED*/
+ }
+ }
+ argv += optind;
+ argc -= optind;
+
+ if (argc != 1) {
+ usage();
+ exit(1);
+ }
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ err(1, "socket");
+ /*NOTREACHED*/
+ }
+
+ /* getaddrinfo is preferred for addr@ifname syntax */
+ snprintf(pbuf, sizeof(pbuf), "%d", RIP6_PORT);
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET6;
+ hints.ai_socktype = SOCK_DGRAM;
+ error = getaddrinfo(argv[0], pbuf, &hints, &res);
+ if (error) {
+ errx(1, "%s: %s", argv[0], gai_strerror(error));
+ /*NOTREACHED*/
+ }
+ if (res->ai_next) {
+ errx(1, "%s: %s", argv[0], "resolved to multiple addrs");
+ /*NOTREACHED*/
+ }
+ if (sizeof(sin6) != res->ai_addrlen) {
+ errx(1, "%s: %s", argv[0], "invalid addrlen");
+ /*NOTREACHED*/
+ }
+ memcpy(&sin6, res->ai_addr, res->ai_addrlen);
+ if (ifidx >= 0)
+ sin6.sin6_scope_id = ifidx;
+
+ if ((ripbuf = (struct rip6 *)malloc(BUFSIZ)) == NULL) {
+ err(1, "malloc");
+ /*NOTREACHED*/
+ }
+ ripbuf->rip6_cmd = RIP6_REQUEST;
+ ripbuf->rip6_vers = RIP6_VERSION;
+ ripbuf->rip6_res1[0] = 0;
+ ripbuf->rip6_res1[1] = 0;
+ np = ripbuf->rip6_nets;
+ bzero(&np->rip6_dest, sizeof(struct in6_addr));
+ np->rip6_tag = 0;
+ np->rip6_plen = 0;
+ np->rip6_metric = HOPCNT_INFINITY6;
+ if (sendto(s, ripbuf, RIPSIZE(1), 0, (struct sockaddr *)&sin6,
+ sizeof(struct sockaddr_in6)) < 0) {
+ err(1, "send");
+ /*NOTREACHED*/
+ }
+ do {
+ flen = sizeof(fsock);
+ if ((len = recvfrom(s, ripbuf, BUFSIZ, 0,
+ (struct sockaddr *)&fsock, &flen)) < 0) {
+ err(1, "recvfrom");
+ /*NOTREACHED*/
+ }
+ printf("Response from %s len %d\n",
+ sa_n2a((struct sockaddr *)&fsock), len);
+ n = (len - sizeof(struct rip6) + sizeof(struct netinfo6)) /
+ sizeof(struct netinfo6);
+ np = ripbuf->rip6_nets;
+ for (i = 0; i < n; i++, np++) {
+ printf("\t%s/%d [%d]", inet6_n2a(&np->rip6_dest),
+ np->rip6_plen, np->rip6_metric);
+ if (np->rip6_tag)
+ printf(" tag=0x%x", ntohs(np->rip6_tag));
+ printf("\n");
+ }
+ } while (len == RIPSIZE(24));
+
+ exit(0);
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: rip6query [-I iface] address\n");
+}
+
+/* getnameinfo() is preferred as we may be able to show ifindex as ifname */
+static const char *
+sa_n2a(sa)
+ struct sockaddr *sa;
+{
+ static char buf[NI_MAXHOST];
+
+ if (getnameinfo(sa, sa->sa_len, buf, sizeof(buf),
+ NULL, 0, NI_NUMERICHOST | NI_WITHSCOPEID) != 0) {
+ snprintf(buf, sizeof(buf), "%s", "(invalid)");
+ }
+ return buf;
+}
+
+static const char *
+inet6_n2a(addr)
+ struct in6_addr *addr;
+{
+ static char buf[NI_MAXHOST];
+
+ return inet_ntop(AF_INET6, addr, buf, sizeof(buf));
+}
diff --git a/usr.sbin/rmt/Makefile b/usr.sbin/rmt/Makefile
new file mode 100644
index 0000000..8a678b6
--- /dev/null
+++ b/usr.sbin/rmt/Makefile
@@ -0,0 +1,12 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+PROG= rmt
+MAN= 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..824d725
--- /dev/null
+++ b/usr.sbin/rmt/rmt.8
@@ -0,0 +1,221 @@
+.\" Copyright (c) 1983, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)rmt.8 8.3 (Berkeley) 6/1/94
+.\" $FreeBSD$
+.\"
+.Dd June 1, 1994
+.Dt RMT 8
+.Os
+.Sh NAME
+.Nm rmt
+.Nd remote magtape protocol module
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+.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 -ragged -offset indent
+.Sm off
+.Sy A Ar number No \en
+.Sm on
+.Ed
+.Pp
+.Ar Number
+is an
+.Tn ASCII
+representation of a decimal number.
+Unsuccessful commands are responded to with:
+.Bd -ragged -offset indent
+.Sm off
+.Xo Sy E Ar error-number
+.No \en Ar error-message
+.No \en
+.Xc
+.Sm on
+.Ed
+.Pp
+.Ar Error-number
+is one of the possible error
+numbers described in
+.Xr intro 2
+and
+.Ar error-message
+is the corresponding error string as printed
+from a call to
+.Xr perror 3 .
+The protocol is comprised of the
+following commands, which are sent as indicated - no spaces are supplied
+between the command and its arguments, or between its arguments, and
+.Ql \en
+indicates that a newline should be supplied:
+.Bl -tag -width Ds
+.Sm off
+.It Xo Sy \&O Ar device
+.No \en Ar mode No \en
+.Xc
+.Sm on
+Open the specified
+.Ar device
+using the indicated
+.Ar mode .
+.Ar Device
+is a full pathname and
+.Ar mode
+is an
+.Tn ASCII
+representation of a decimal
+number suitable for passing to
+.Xr open 2 .
+If a device had already been opened, it is
+closed before a new open is performed.
+.Sm off
+.It Xo Sy C Ar device No \en
+.Xc
+.Sm on
+Close the currently open device. The
+.Ar device
+specified is ignored.
+.Sm off
+.It Xo Sy L
+.Ar whence No \en
+.Ar offset No \en
+.Xc
+.Sm on
+Perform an
+.Xr lseek 2
+operation using the specified parameters.
+The response value is that returned from the
+.Xr lseek
+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 2
+call. The return value is the
+.Ar count
+parameter when the operation is successful.
+.It Sy S
+Return the status of the open device, as
+obtained with a
+.Dv MTIOCGET
+.Xr ioctl 2
+call. If the operation was successful,
+an ``ack'' is sent with the size of the
+status buffer, then the status buffer is
+sent (in binary).
+.El
+.Pp
+Any other command causes
+.Nm
+to exit.
+.Sh DIAGNOSTICS
+All responses are of the form described above.
+.Sh SEE ALSO
+.Xr rcmd 3 ,
+.Xr rexec 3 ,
+.Xr mtio 4 ,
+.Xr rdump 8 ,
+.Xr rrestore 8
+.Sh BUGS
+People should be discouraged from using this for a remote
+file access protocol.
+.Sh HISTORY
+The
+.Nm
+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..5315208
--- /dev/null
+++ b/usr.sbin/rmt/rmt.c
@@ -0,0 +1,261 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#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(STDIN_FILENO, &c, 1) != 1)
+ exit(0);
+ switch (c) {
+
+ case 'O':
+ if (tape >= 0)
+ (void) close(tape);
+ getstring(device);
+ getstring(mode);
+ DEBUG2("rmtd: O %s %s\n", device, mode);
+ /*
+ * XXX the rmt protocol does not provide a means to
+ * specify the permission bits; allow rw for everyone,
+ * as modified by the users umask
+ */
+ tape = open(device, atoi(mode), 0666);
+ if (tape < 0)
+ goto ioerror;
+ goto respond;
+
+ case 'C':
+ DEBUG("rmtd: C\n");
+ getstring(device); /* discard */
+ if (close(tape) < 0)
+ goto ioerror;
+ tape = -1;
+ goto respond;
+
+ case 'L':
+ getstring(count);
+ getstring(pos);
+ DEBUG2("rmtd: L %s %s\n", count, pos);
+ rval = lseek(tape, (off_t)strtoll(count, NULL, 10), atoi(pos));
+ if (rval < 0)
+ goto ioerror;
+ goto respond;
+
+ case 'W':
+ getstring(count);
+ n = atoi(count);
+ DEBUG1("rmtd: W %s\n", count);
+ record = checkbuf(record, n);
+ for (i = 0; i < n; i += cc) {
+ cc = read(STDIN_FILENO, &record[i], n - i);
+ if (cc <= 0) {
+ DEBUG("rmtd: premature eof\n");
+ exit(2);
+ }
+ }
+ rval = write(tape, record, n);
+ if (rval < 0)
+ goto ioerror;
+ goto respond;
+
+ case 'R':
+ getstring(count);
+ DEBUG1("rmtd: R %s\n", count);
+ n = atoi(count);
+ record = checkbuf(record, n);
+ rval = read(tape, record, n);
+ if (rval < 0)
+ goto ioerror;
+ (void)sprintf(resp, "A%d\n", rval);
+ (void)write(STDOUT_FILENO, resp, strlen(resp));
+ (void)write(STDOUT_FILENO, record, rval);
+ goto top;
+
+ case 'I':
+ getstring(op);
+ getstring(count);
+ DEBUG2("rmtd: I %s %s\n", op, count);
+ { struct mtop mtop;
+ mtop.mt_op = atoi(op);
+ mtop.mt_count = atoi(count);
+ if (ioctl(tape, MTIOCTOP, (char *)&mtop) < 0)
+ goto ioerror;
+ rval = mtop.mt_count;
+ }
+ goto respond;
+
+ case 'S': /* status */
+ DEBUG("rmtd: S\n");
+ { struct mtget mtget;
+ if (ioctl(tape, MTIOCGET, (char *)&mtget) < 0)
+ goto ioerror;
+ rval = sizeof (mtget);
+ if (rval > 24) /* original mtget structure size */
+ rval = 24;
+ (void)sprintf(resp, "A%d\n", rval);
+ (void)write(STDOUT_FILENO, resp, strlen(resp));
+ (void)write(STDOUT_FILENO, (char *)&mtget, rval);
+ goto top;
+ }
+
+ case 'V': /* version */
+ getstring(op);
+ DEBUG1("rmtd: V %s\n", op);
+ rval = 2;
+ goto respond;
+
+ default:
+ DEBUG1("rmtd: garbage command %c\n", c);
+ exit(3);
+ }
+respond:
+ DEBUG1("rmtd: A %d\n", rval);
+ (void)sprintf(resp, "A%d\n", rval);
+ (void)write(STDOUT_FILENO, resp, strlen(resp));
+ goto top;
+ioerror:
+ error(errno);
+ goto top;
+}
+
+void
+getstring(bp)
+ char *bp;
+{
+ int i;
+ char *cp = bp;
+
+ for (i = 0; i < SSIZE; i++) {
+ if (read(STDIN_FILENO, cp+i, 1) != 1)
+ exit(0);
+ if (cp[i] == '\n')
+ break;
+ }
+ cp[i] = '\0';
+}
+
+char *
+checkbuf(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(STDOUT_FILENO, resp, strlen(resp));
+}
diff --git a/usr.sbin/route6d/Makefile b/usr.sbin/route6d/Makefile
new file mode 100644
index 0000000..817cd7a
--- /dev/null
+++ b/usr.sbin/route6d/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+# $FreeBSD$
+
+PROG= route6d
+MAN= route6d.8
+
+CFLAGS+= -Dss_len=__ss_len -Dss_family=__ss_family -DINET6 -DHAVE_GETIFADDRS
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/route6d/misc/chkrt b/usr.sbin/route6d/misc/chkrt
new file mode 100755
index 0000000..f57a376
--- /dev/null
+++ b/usr.sbin/route6d/misc/chkrt
@@ -0,0 +1,64 @@
+#!/usr/bin/perl
+#
+# $FreeBSD$
+#
+$dump="/var/tmp/route6d_dump";
+$pidfile="/var/run/route6d.pid";
+
+system("rm -f $dump");
+
+open(FD, "< $pidfile") || die "Can not open $pidfile";
+$_ = <FD>;
+chop;
+close(FD);
+system("kill -INT $_");
+
+open(NS, "/usr/local/v6/bin/netstat -r -n|") || die "Can not open netstat";
+while (<NS>) {
+ chop;
+ next unless (/^3f/ || /^5f/);
+ @f = split(/\s+/);
+ $gw{$f[0]} = $f[1];
+ $int{$f[0]} = $f[3];
+}
+close(NS);
+
+$err=0;
+sleep(2);
+open(FD, "< $dump") || die "Can not open $dump";
+while (<FD>) {
+ chop;
+ next unless (/^ 3f/ || /^ 5f/);
+ @f = split(/\s+/);
+ $dst = $f[1];
+ $f[2] =~ /if\(\d:([a-z0-9]+)\)/;
+ $intf = $1;
+ $f[3] =~ /gw\(([a-z0-9:]+)\)/;
+ $gateway = $1;
+ $f[4] =~ /\[(\d+)\]/;
+ $metric = $1;
+ $f[5] =~ /age\((\d+)\)/;
+ $age = $1;
+ unless (defined($gw{$dst})) {
+ print "NOT FOUND: $dst $intf $gateway $metric $age\n";
+ $err++;
+ next;
+ }
+ if ($gw{$dst} ne $gateway && $gw{$dst} !~ /link#\d+/) {
+ print "WRONG GW: $dst $intf $gateway $metric $age\n";
+ print "kernel gw: $gw{$dst}\n";
+ $err++;
+ next;
+ }
+ if ($int{$dst} ne $intf) {
+ print "WRONG IF: $dst $intf $gateway $metric $age\n";
+ print "kernel if: $int{$dst}\n";
+ $err++;
+ next;
+ }
+}
+close(FD);
+
+if ($err == 0) {
+ print "No error found\n";
+}
diff --git a/usr.sbin/route6d/misc/cksum.c b/usr.sbin/route6d/misc/cksum.c
new file mode 100644
index 0000000..bff7e2d
--- /dev/null
+++ b/usr.sbin/route6d/misc/cksum.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <stdio.h>
+
+unsigned short buf[BUFSIZ];
+
+main()
+{
+ int i;
+ unsigned short *p = buf, *q = &buf[4];
+ unsigned long sum, sum2;
+
+ while (scanf("%x", &i) != EOF) {
+ *p++ = i; printf("%d ", i);
+ }
+ printf("\n");
+
+ sum = buf[2] + (buf[3] >> 8) & 0xff;
+ while (q != p)
+ sum += (*q++ & 0xffff);
+ sum2 = (sum & 0xffff) + (sum >> 16) & 0xffff;
+ printf("%x, %x\n", sum, sum2);
+}
diff --git a/usr.sbin/route6d/route6d.8 b/usr.sbin/route6d/route6d.8
new file mode 100644
index 0000000..b24e84e
--- /dev/null
+++ b/usr.sbin/route6d/route6d.8
@@ -0,0 +1,250 @@
+.\" $FreeBSD$
+.\" $KAME: route6d.8,v 1.10 2000/11/24 11:57:18 itojun Exp $
+.\"
+.\" Copyright (c) 1996 WIDE Project. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modifications, are permitted provided that the above copyright notice
+.\" and this paragraph are duplicated in all such forms and that any
+.\" documentation, advertising materials, and other materials related to
+.\" such distribution and use acknowledge that the software was developed
+.\" by the WIDE Project, Japan. The name of the Project may not be used to
+.\" endorse or promote products derived from this software without
+.\" specific prior written permission. THIS SOFTWARE IS PROVIDED ``AS IS''
+.\" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+.\" LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+.\" A PARTICULAR PURPOSE.
+.Dd January 31, 1997
+.Dt ROUTE6D 8
+.Os
+.Sh NAME
+.Nm route6d
+.Nd RIP6 Routing Daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl adDhlnqsS
+.Bk -words
+.Op Fl R Ar routelog
+.Ek
+.Bk -words
+.Op Fl A Ar prefix/preflen,if1[,if2...\&]
+.Ek
+.Bk -words
+.Op Fl L Ar prefix/preflen,if1[,if2...\&]
+.Ek
+.Bk -words
+.Op Fl N Ar if1[,if2...\&]
+.Ek
+.Bk -words
+.Op Fl O Ar prefix/preflen,if1[,if2...\&]
+.Ek
+.Bk -words
+.Op Fl T Ar if1[,if2...\&]
+.Ek
+.Bk -words
+.Op Fl t Ar tag
+.Ek
+.\"
+.Sh DESCRIPTION
+The
+.Nm
+is a routing daemon which supports RIP over IPv6.
+.Pp
+Options are:
+.Bl -tag -width indent
+.\"
+.It Fl a
+Enables aging of the statically defined routes.
+With this option, any
+statically defined routes will be removed unless corresponding updates
+arrive as if the routes are received at the startup of
+.Nm .
+.\"
+.It Fl R Ar routelog
+This option makes the
+.Nm
+to log the route change (add/delete) to the file
+.Ar routelog .
+.\"
+.It Fl A Ar prefix/preflen,if1[,if2...]
+This option is used for aggregating routes.
+.Ar prefix/preflen
+specifies the prefix and the prefix length of the
+aggregated route.
+When advertising routes,
+.Nm
+filters specific routes covered by the aggregate,
+and advertises the aggregated route
+.Ar prefix/preflen ,
+to the interfaces specified in the comma-separated interface list,
+.Ar if1[,if2...] .
+.Nm
+creates a static route to
+.Ar prefix/preflen
+with
+.Dv RTF_REJECT
+flag, into the kernel routing table.
+.\"
+.It Fl d
+Enables output of debugging message.
+This option also instructs
+.Nm
+to run in foreground mode
+(does not become daemon).
+.\"
+.It Fl D
+Enables extensive output of debugging message.
+This option also instructs
+.Nm
+to run in foreground mode
+(does not become daemon).
+.\"
+.It Fl h
+Disables the split horizon processing.
+.\"
+.It Fl l
+By default,
+.Nm
+will not exchange site local routes for safety reasons.
+This is because semantics of site local address space is rather vague
+(specification is still in being worked),
+and there is no good way to define site local boundary.
+With
+.Fl l
+option,
+.Nm
+will exchange site local routes as well.
+It must not be used on site boundary routers,
+since
+.Fl l
+option assumes that all interfaces are in the same site.
+.\"
+.It Fl L Ar prefix/preflen,if1[,if2...]
+Filter incoming routes from interfaces
+.Ar if1,[if2...] .
+.Nm
+will accept incoming routes that are in
+.Ar prefix/preflen .
+If multiple
+.Fl L
+options are specified, any routes that match one of the options is accepted.
+.Li ::/0
+is treated specially as default route, not
+.Do
+any route that has longer prefix length than, or equal to 0
+.Dc .
+If you would like to accept any route, specify no
+.Fl L
+option.
+For example, with
+.Do
+.Fl L
+.Li 3ffe::/16,if1
+.Fl L
+.Li ::/0,if1
+.Dc
+.Nm
+will accept default route and routes in 6bone test address, but no others.
+.\"
+.It Fl n
+Do not update the kernel routing table.
+.\"
+.It Fl N Ar if1[,if2...]
+Do not listen to, or advertise, route from/to interfaces specified by
+.Ar if1,[if2...] .
+.\"
+.It Fl O Ar prefix/preflen,if1[,if2...]
+Restrict route advertisement toward interfaces specified by
+.Ar if1,[if2...] .
+With this option
+.Nm
+will only advertise routes that matches
+.Ar prefix/preflen .
+.\"
+.It Fl q
+Makes
+.Nm
+in listen-only mode.
+No advertisement is sent.
+.\"
+.It Fl s
+Makes
+.Nm
+to advertise the statically defined routes which exist in the kernel routing
+table when
+.Nm
+invoked.
+Announcements obey the regular split horizon rule.
+.\"
+.It Fl S
+This option is the same as
+.Fl s
+option except that no split horizon rule does apply.
+.\"
+.It Fl T Ar if1[,if2...]
+Advertise only default route, toward
+.Ar if1,[if2...] .
+.\"
+.It Fl t Ar tag
+Attach route tag
+.Ar tag
+to originated route entries.
+.Ar tag
+can be decimal, octal prefixed by
+.Li 0 ,
+or hexadecimal prefixed by
+.Li 0x .
+.\"
+.El
+.Pp
+Upon receipt of signal
+.Dv SIGINT
+or
+.Dv SIGUSR1 ,
+.Nm
+will dump the current internal state into
+.Pa /var/run/route6d_dump .
+.\"
+.Sh FILES
+.Bl -tag -width /var/run/route6d_dump -compact
+.It Pa /var/run/route6d_dump
+dumps internal state on
+.Dv SIGINT
+or
+.Dv SIGUSR1
+.El
+.\"
+.Sh SEE ALSO
+.Rs
+.%A G. Malkin
+.%A R. Minnear
+.%T RIPng for IPv6
+.%R RFC2080
+.%D January 1997
+.Re
+.\"
+.Sh NOTE
+.Nm
+uses IPv6 advanced API,
+defined in RFC2292,
+for communicating with peers using link-local addresses.
+.Pp
+Internally
+.Nm
+embeds interface identifier into bit 32 to 63 of link-local addresses
+.Li ( fe80::xx
+and
+.Li ff02::xx )
+so they will be visible on internal state dump file
+.Pq Pa /var/run/route6d_dump .
+.Pp
+Routing table manipulation differs from IPv6 implementation to implementation.
+Currently
+.Nm
+obeys WIDE Hydrangea/KAME IPv6 kernel,
+and will not be able to run on other platforms.
+.Pp
+Current
+.Nm
+does not reduce the rate of the triggered updates when consecutive updates
+arrive.
diff --git a/usr.sbin/route6d/route6d.c b/usr.sbin/route6d/route6d.c
new file mode 100644
index 0000000..d41efd0
--- /dev/null
+++ b/usr.sbin/route6d/route6d.c
@@ -0,0 +1,3463 @@
+/* $FreeBSD$ */
+/* $KAME: route6d.c,v 1.64 2001/05/08 04:36:37 itojun Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char _rcsid[] = "$KAME: route6d.c,v 1.64 2001/05/08 04:36:37 itojun Exp $";
+#endif
+
+#include <stdio.h>
+
+#include <time.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+#include <syslog.h>
+#include <stddef.h>
+#include <errno.h>
+#include <err.h>
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/sysctl.h>
+#include <sys/uio.h>
+#include <net/if.h>
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+#include <net/if_var.h>
+#endif /* __FreeBSD__ >= 3 */
+#define KERNEL 1
+#define _KERNEL 1
+#include <net/route.h>
+#undef KERNEL
+#undef _KERNEL
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/ip6.h>
+#include <netinet/udp.h>
+#include <netdb.h>
+#include <ifaddrs.h>
+
+#include <arpa/inet.h>
+
+#include "route6d.h"
+
+#define MAXFILTER 40
+
+#ifdef DEBUG
+#define INIT_INTERVAL6 6
+#else
+#define INIT_INTERVAL6 10 /* Wait to submit a initial riprequest */
+#endif
+
+/* alignment constraint for routing socket */
+#define ROUNDUP(a) \
+ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
+
+/*
+ * Following two macros are highly depending on KAME Release
+ */
+#define IN6_LINKLOCAL_IFINDEX(addr) \
+ ((addr).s6_addr[2] << 8 | (addr).s6_addr[3])
+
+#define SET_IN6_LINKLOCAL_IFINDEX(addr, index) \
+ do { \
+ (addr).s6_addr[2] = ((index) >> 8) & 0xff; \
+ (addr).s6_addr[3] = (index) & 0xff; \
+ } while (0)
+
+struct ifc { /* Configuration of an interface */
+ char *ifc_name; /* if name */
+ struct ifc *ifc_next;
+ int ifc_index; /* if index */
+ int ifc_mtu; /* if mtu */
+ int ifc_metric; /* if metric */
+ u_int ifc_flags; /* flags */
+ short ifc_cflags; /* IFC_XXX */
+ struct in6_addr ifc_mylladdr; /* my link-local address */
+ struct sockaddr_in6 ifc_ripsin; /* rip multicast address */
+ struct iff *ifc_filter; /* filter structure */
+ struct ifac *ifc_addr; /* list of AF_INET6 addresses */
+ int ifc_joined; /* joined to ff02::9 */
+};
+
+struct ifac { /* Adddress associated to an interface */
+ struct ifc *ifa_conf; /* back pointer */
+ struct ifac *ifa_next;
+ struct in6_addr ifa_addr; /* address */
+ struct in6_addr ifa_raddr; /* remote address, valid in p2p */
+ int ifa_plen; /* prefix length */
+};
+
+struct iff {
+ int iff_type;
+ struct in6_addr iff_addr;
+ int iff_plen;
+ struct iff *iff_next;
+};
+
+struct ifc *ifc;
+int nifc; /* number of valid ifc's */
+struct ifc **index2ifc;
+int nindex2ifc;
+struct ifc *loopifcp = NULL; /* pointing to loopback */
+int loopifindex = 0; /* ditto */
+fd_set sockvec; /* vector to select() for receiving */
+int rtsock; /* the routing socket */
+int ripsock; /* socket to send/receive RIP datagram */
+
+struct rip6 *ripbuf; /* packet buffer for sending */
+
+/*
+ * Maintain the routes in a linked list. When the number of the routes
+ * grows, somebody would like to introduce a hash based or a radix tree
+ * based structure. I believe the number of routes handled by RIP is
+ * limited and I don't have to manage a complex data structure, however.
+ *
+ * One of the major drawbacks of the linear linked list is the difficulty
+ * of representing the relationship between a couple of routes. This may
+ * be a significant problem when we have to support route aggregation with
+ * supressing the specifices covered by the aggregate.
+ */
+
+struct riprt {
+ struct riprt *rrt_next; /* next destination */
+ struct riprt *rrt_same; /* same destination - future use */
+ struct netinfo6 rrt_info; /* network info */
+ struct in6_addr rrt_gw; /* gateway */
+ u_long rrt_flags; /* kernel routing table flags */
+ u_long rrt_rflags; /* route6d routing table flags */
+ time_t rrt_t; /* when the route validated */
+ int rrt_index; /* ifindex from which this route got */
+};
+
+struct riprt *riprt = 0;
+
+int dflag = 0; /* debug flag */
+int qflag = 0; /* quiet flag */
+int nflag = 0; /* don't update kernel routing table */
+int aflag = 0; /* age out even the statically defined routes */
+int hflag = 0; /* don't split horizon */
+int lflag = 0; /* exchange site local routes */
+int sflag = 0; /* announce static routes w/ split horizon */
+int Sflag = 0; /* announce static routes to every interface */
+unsigned long routetag = 0; /* route tag attached on originating case */
+
+char *filter[MAXFILTER];
+int filtertype[MAXFILTER];
+int nfilter = 0;
+
+pid_t pid;
+
+struct sockaddr_storage ripsin;
+
+struct rtentry rtentry;
+
+int interval = 1;
+time_t nextalarm = 0;
+time_t sup_trig_update = 0;
+
+FILE *rtlog = NULL;
+
+int logopened = 0;
+
+static u_long seq = 0;
+
+volatile int signo;
+volatile sig_atomic_t seenalrm;
+volatile sig_atomic_t seenquit;
+volatile sig_atomic_t seenusr1;
+
+#define RRTF_AGGREGATE 0x08000000
+#define RRTF_NOADVERTISE 0x10000000
+#define RRTF_NH_NOT_LLADDR 0x20000000
+#define RRTF_SENDANYWAY 0x40000000
+#define RRTF_CHANGED 0x80000000
+
+int main __P((int, char **));
+void sighandler __P((int));
+void ripalarm __P((void));
+void riprecv __P((void));
+void ripsend __P((struct ifc *, struct sockaddr_in6 *, int));
+int out_filter __P((struct riprt *, struct ifc *));
+void init __P((void));
+void sockopt __P((struct ifc *));
+void ifconfig __P((void));
+void ifconfig1 __P((const char *, const struct sockaddr *, struct ifc *, int));
+void rtrecv __P((void));
+int rt_del __P((const struct sockaddr_in6 *, const struct sockaddr_in6 *,
+ const struct sockaddr_in6 *));
+int rt_deladdr __P((struct ifc *, const struct sockaddr_in6 *,
+ const struct sockaddr_in6 *));
+void filterconfig __P((void));
+int getifmtu __P((int));
+const char *rttypes __P((struct rt_msghdr *));
+const char *rtflags __P((struct rt_msghdr *));
+const char *ifflags __P((int));
+int ifrt __P((struct ifc *, int));
+void ifrt_p2p __P((struct ifc *, int));
+void applymask __P((struct in6_addr *, struct in6_addr *));
+void applyplen __P((struct in6_addr *, int));
+void ifrtdump __P((int));
+void ifdump __P((int));
+void ifdump0 __P((FILE *, const struct ifc *));
+void rtdump __P((int));
+void rt_entry __P((struct rt_msghdr *, int));
+void rtdexit __P((void));
+void riprequest __P((struct ifc *, struct netinfo6 *, int,
+ struct sockaddr_in6 *));
+void ripflush __P((struct ifc *, struct sockaddr_in6 *));
+void sendrequest __P((struct ifc *));
+int sin6mask2len __P((const struct sockaddr_in6 *));
+int mask2len __P((const struct in6_addr *, int));
+int sendpacket __P((struct sockaddr_in6 *, int));
+int addroute __P((struct riprt *, const struct in6_addr *, struct ifc *));
+int delroute __P((struct netinfo6 *, struct in6_addr *));
+struct in6_addr *getroute __P((struct netinfo6 *, struct in6_addr *));
+void krtread __P((int));
+int tobeadv __P((struct riprt *, struct ifc *));
+char *allocopy __P((char *));
+char *hms __P((void));
+const char *inet6_n2p __P((const struct in6_addr *));
+struct ifac *ifa_match __P((const struct ifc *, const struct in6_addr *, int));
+struct in6_addr *plen2mask __P((int));
+struct riprt *rtsearch __P((struct netinfo6 *, struct riprt **));
+int ripinterval __P((int));
+time_t ripsuptrig __P((void));
+void fatal __P((const char *, ...))
+ __attribute__((__format__(__printf__, 1, 2)));
+void trace __P((int, const char *, ...))
+ __attribute__((__format__(__printf__, 2, 3)));
+void tracet __P((int, const char *, ...))
+ __attribute__((__format__(__printf__, 2, 3)));
+unsigned int if_maxindex __P((void));
+struct ifc *ifc_find __P((char *));
+struct iff *iff_find __P((struct ifc *, int));
+void setindex2ifc __P((int, struct ifc *));
+
+#define MALLOC(type) ((type *)malloc(sizeof(type)))
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int ch;
+ int error = 0;
+ struct ifc *ifcp;
+ sigset_t mask, omask;
+ FILE *pidfile;
+ char *progname;
+ char *ep;
+
+ progname = strrchr(*argv, '/');
+ if (progname)
+ progname++;
+ else
+ progname = *argv;
+
+ pid = getpid();
+ while ((ch = getopt(argc, argv, "A:N:O:R:T:L:t:adDhlnqsS")) != -1) {
+ switch (ch) {
+ case 'A':
+ case 'N':
+ case 'O':
+ case 'T':
+ case 'L':
+ if (nfilter >= MAXFILTER) {
+ fatal("Exceeds MAXFILTER");
+ /*NOTREACHED*/
+ }
+ filtertype[nfilter] = ch;
+ filter[nfilter++] = allocopy(optarg);
+ break;
+ case 't':
+ ep = NULL;
+ routetag = strtoul(optarg, &ep, 0);
+ if (!ep || *ep != '\0' || (routetag & ~0xffff) != 0) {
+ fatal("invalid route tag");
+ /*NOTREACHED*/
+ }
+ break;
+ case 'R':
+ if ((rtlog = fopen(optarg, "w")) == NULL) {
+ fatal("Can not write to routelog");
+ /*NOTREACHED*/
+ }
+ break;
+#define FLAG(c, flag, n) case c: do { flag = n; break; } while(0)
+ FLAG('a', aflag, 1); break;
+ FLAG('d', dflag, 1); break;
+ FLAG('D', dflag, 2); break;
+ FLAG('h', hflag, 1); break;
+ FLAG('l', lflag, 1); break;
+ FLAG('n', nflag, 1); break;
+ FLAG('q', qflag, 1); break;
+ FLAG('s', sflag, 1); break;
+ FLAG('S', Sflag, 1); break;
+#undef FLAG
+ default:
+ fatal("Invalid option specified, terminating");
+ /*NOTREACHED*/
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc > 0) {
+ fatal("bogus extra arguments");
+ /*NOTREACHED*/
+ }
+
+ if (geteuid()) {
+ nflag = 1;
+ fprintf(stderr, "No kernel update is allowed\n");
+ }
+ openlog(progname, LOG_NDELAY|LOG_PID, LOG_DAEMON);
+ logopened++;
+
+ if ((ripbuf = (struct rip6 *)malloc(RIP6_MAXMTU)) == NULL)
+ fatal("malloc");
+ memset(ripbuf, 0, RIP6_MAXMTU);
+ ripbuf->rip6_cmd = RIP6_RESPONSE;
+ ripbuf->rip6_vers = RIP6_VERSION;
+ ripbuf->rip6_res1[0] = 0;
+ ripbuf->rip6_res1[1] = 0;
+
+ init();
+ ifconfig();
+ for (ifcp = ifc; ifcp; ifcp = ifcp->ifc_next) {
+ if (ifcp->ifc_index < 0) {
+ fprintf(stderr,
+"No ifindex found at %s (no link-local address?)\n",
+ ifcp->ifc_name);
+ error++;
+ }
+ }
+ if (error)
+ exit(1);
+ if (loopifcp == NULL) {
+ fatal("No loopback found");
+ /*NOTREACHED*/
+ }
+ loopifindex = loopifcp->ifc_index;
+ for (ifcp = ifc; ifcp; ifcp = ifcp->ifc_next)
+ ifrt(ifcp, 0);
+ filterconfig();
+ krtread(0);
+ if (dflag)
+ ifrtdump(0);
+
+ if (dflag == 0) {
+#if 1
+ if (daemon(0, 0) < 0) {
+ fatal("daemon");
+ /*NOTREACHED*/
+ }
+#else
+ if (fork())
+ exit(0);
+ if (setsid() < 0) {
+ fatal("setid");
+ /*NOTREACHED*/
+ }
+#endif
+ }
+ pid = getpid();
+ if ((pidfile = fopen(ROUTE6D_PID, "w")) != NULL) {
+ fprintf(pidfile, "%d\n", pid);
+ fclose(pidfile);
+ }
+
+ if ((ripbuf = (struct rip6 *)malloc(RIP6_MAXMTU)) == NULL) {
+ fatal("malloc");
+ /*NOTREACHED*/
+ }
+ memset(ripbuf, 0, RIP6_MAXMTU);
+ ripbuf->rip6_cmd = RIP6_RESPONSE;
+ ripbuf->rip6_vers = RIP6_VERSION;
+ ripbuf->rip6_res1[0] = 0;
+ ripbuf->rip6_res1[1] = 0;
+
+ if (signal(SIGALRM, sighandler) == SIG_ERR ||
+ signal(SIGQUIT, sighandler) == SIG_ERR ||
+ signal(SIGTERM, sighandler) == SIG_ERR ||
+ signal(SIGUSR1, sighandler) == SIG_ERR ||
+ signal(SIGHUP, sighandler) == SIG_ERR ||
+ signal(SIGINT, sighandler) == SIG_ERR) {
+ fatal("signal");
+ /*NOTREACHED*/
+ }
+ /*
+ * To avoid rip packet congestion (not on a cable but in this
+ * process), wait for a moment to send the first RIP6_RESPONSE
+ * packets.
+ */
+ alarm(ripinterval(INIT_INTERVAL6));
+
+ for (ifcp = ifc; ifcp; ifcp = ifcp->ifc_next) {
+ if (ifcp->ifc_index > 0 && (ifcp->ifc_flags & IFF_UP))
+ sendrequest(ifcp);
+ }
+
+ syslog(LOG_INFO, "**** Started ****");
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGALRM);
+ while (1) {
+ fd_set recvec;
+
+ if (seenalrm) {
+ ripalarm();
+ seenalrm = 0;
+ continue;
+ }
+ if (seenquit) {
+ rtdexit();
+ seenquit = 0;
+ continue;
+ }
+ if (seenusr1) {
+ ifrtdump(SIGUSR1);
+ seenusr1 = 0;
+ continue;
+ }
+
+ FD_COPY(&sockvec, &recvec);
+ signo = 0;
+ switch (select(FD_SETSIZE, &recvec, 0, 0, 0)) {
+ case -1:
+ if (errno != EINTR) {
+ fatal("select");
+ /*NOTREACHED*/
+ }
+ continue;
+ case 0:
+ continue;
+ default:
+ if (FD_ISSET(ripsock, &recvec)) {
+ sigprocmask(SIG_BLOCK, &mask, &omask);
+ riprecv();
+ sigprocmask(SIG_SETMASK, &omask, NULL);
+ }
+ if (FD_ISSET(rtsock, &recvec)) {
+ sigprocmask(SIG_BLOCK, &mask, &omask);
+ rtrecv();
+ sigprocmask(SIG_SETMASK, &omask, NULL);
+ }
+ }
+ }
+}
+
+void
+sighandler(sig)
+ int sig;
+{
+
+ signo = sig;
+ switch (signo) {
+ case SIGALRM:
+ seenalrm++;
+ break;
+ case SIGQUIT:
+ case SIGTERM:
+ seenquit++;
+ break;
+ case SIGUSR1:
+ case SIGHUP:
+ case SIGINT:
+ seenusr1++;
+ break;
+ }
+}
+
+/*
+ * gracefully exits after resetting sockopts.
+ */
+/* ARGSUSED */
+void
+rtdexit()
+{
+ struct riprt *rrt;
+
+ alarm(0);
+ for (rrt = riprt; rrt; rrt = rrt->rrt_next) {
+ if (rrt->rrt_rflags & RRTF_AGGREGATE) {
+ delroute(&rrt->rrt_info, &rrt->rrt_gw);
+ }
+ }
+ close(ripsock);
+ close(rtsock);
+ syslog(LOG_INFO, "**** Terminated ****");
+ closelog();
+ exit(1);
+}
+
+/*
+ * Called periodically:
+ * 1. age out the learned route. remove it if necessary.
+ * 2. submit RIP6_RESPONSE packets.
+ * Invoked in every SUPPLY_INTERVAL6 (30) seconds. I believe we don't have
+ * to invoke this function in every 1 or 5 or 10 seconds only to age the
+ * routes more precisely.
+ */
+/* ARGSUSED */
+void
+ripalarm()
+{
+ struct ifc *ifcp;
+ struct riprt *rrt, *rrt_prev, *rrt_next;
+ time_t t_lifetime, t_holddown;
+
+ /* age the RIP routes */
+ rrt_prev = 0;
+ t_lifetime = time(NULL) - RIP_LIFETIME;
+ t_holddown = t_lifetime - RIP_HOLDDOWN;
+ for (rrt = riprt; rrt; rrt = rrt_next) {
+ rrt_next = rrt->rrt_next;
+
+ if (rrt->rrt_t == 0) {
+ rrt_prev = rrt;
+ continue;
+ }
+ if (rrt->rrt_t < t_holddown) {
+ if (rrt_prev) {
+ rrt_prev->rrt_next = rrt->rrt_next;
+ } else {
+ riprt = rrt->rrt_next;
+ }
+ delroute(&rrt->rrt_info, &rrt->rrt_gw);
+ free(rrt);
+ continue;
+ }
+ if (rrt->rrt_t < t_lifetime)
+ rrt->rrt_info.rip6_metric = HOPCNT_INFINITY6;
+ rrt_prev = rrt;
+ }
+ /* Supply updates */
+ for (ifcp = ifc; ifcp; ifcp = ifcp->ifc_next) {
+ if (ifcp->ifc_index > 0 && (ifcp->ifc_flags & IFF_UP))
+ ripsend(ifcp, &ifcp->ifc_ripsin, 0);
+ }
+ alarm(ripinterval(SUPPLY_INTERVAL6));
+}
+
+void
+init()
+{
+ int i, int0, int255, error;
+ struct addrinfo hints, *res;
+ char port[10];
+
+ ifc = (struct ifc *)NULL;
+ nifc = 0;
+ nindex2ifc = 0; /*initial guess*/
+ index2ifc = NULL;
+ snprintf(port, sizeof(port), "%d", RIP6_PORT);
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_INET6;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_flags = AI_PASSIVE;
+ error = getaddrinfo(NULL, port, &hints, &res);
+ if (error) {
+ fatal("%s", gai_strerror(error));
+ /*NOTREACHED*/
+ }
+ if (res->ai_next) {
+ fatal(":: resolved to multiple address");
+ /*NOTREACHED*/
+ }
+
+ int0 = 0; int255 = 255;
+ ripsock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+ if (ripsock < 0) {
+ fatal("rip socket");
+ /*NOTREACHED*/
+ }
+ if (bind(ripsock, res->ai_addr, res->ai_addrlen) < 0) {
+ fatal("rip bind");
+ /*NOTREACHED*/
+ }
+ if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
+ &int255, sizeof(int255)) < 0) {
+ fatal("rip IPV6_MULTICAST_HOPS");
+ /*NOTREACHED*/
+ }
+ if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
+ &int0, sizeof(int0)) < 0) {
+ fatal("rip IPV6_MULTICAST_LOOP");
+ /*NOTREACHED*/
+ }
+
+ i = 1;
+#ifdef IPV6_RECVPKTINFO
+ if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &i,
+ sizeof(i)) < 0) {
+ fatal("rip IPV6_RECVPKTINFO");
+ /*NOTREACHED*/
+ }
+#else /* old adv. API */
+ if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_PKTINFO, &i,
+ sizeof(i)) < 0) {
+ fatal("rip IPV6_PKTINFO");
+ /*NOTREACHED*/
+ }
+#endif
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_INET6;
+ hints.ai_socktype = SOCK_DGRAM;
+ error = getaddrinfo(RIP6_DEST, port, &hints, &res);
+ if (error) {
+ fatal("%s", gai_strerror(error));
+ /*NOTREACHED*/
+ }
+ if (res->ai_next) {
+ fatal("%s resolved to multiple address", RIP6_DEST);
+ /*NOTREACHED*/
+ }
+ memcpy(&ripsin, res->ai_addr, res->ai_addrlen);
+
+#ifdef FD_ZERO
+ FD_ZERO(&sockvec);
+#else
+ memset(&sockvec, 0, sizeof(sockvec));
+#endif
+ FD_SET(ripsock, &sockvec);
+
+ if (nflag == 0) {
+ if ((rtsock = socket(PF_ROUTE, SOCK_RAW, 0)) < 0) {
+ fatal("route socket");
+ /*NOTREACHED*/
+ }
+ FD_SET(rtsock, &sockvec);
+ } else
+ rtsock = -1; /*just for safety */
+}
+
+#define RIPSIZE(n) \
+ (sizeof(struct rip6) + ((n)-1) * sizeof(struct netinfo6))
+
+/*
+ * ripflush flushes the rip datagram stored in the rip buffer
+ */
+static int nrt;
+static struct netinfo6 *np;
+
+void
+ripflush(ifcp, sin)
+ struct ifc *ifcp;
+ struct sockaddr_in6 *sin;
+{
+ int i;
+ int error;
+
+ if (ifcp)
+ tracet(1, "Send(%s): info(%d) to %s.%d\n",
+ ifcp->ifc_name, nrt,
+ inet6_n2p(&sin->sin6_addr), ntohs(sin->sin6_port));
+ else
+ tracet(1, "Send: info(%d) to %s.%d\n",
+ nrt, inet6_n2p(&sin->sin6_addr), ntohs(sin->sin6_port));
+ if (dflag >= 2) {
+ np = ripbuf->rip6_nets;
+ for (i = 0; i < nrt; i++, np++) {
+ if (np->rip6_metric == NEXTHOP_METRIC) {
+ if (IN6_IS_ADDR_UNSPECIFIED(&np->rip6_dest))
+ trace(2, " NextHop reset");
+ else {
+ trace(2, " NextHop %s",
+ inet6_n2p(&np->rip6_dest));
+ }
+ } else {
+ trace(2, " %s/%d[%d]",
+ inet6_n2p(&np->rip6_dest),
+ np->rip6_plen, np->rip6_metric);
+ }
+ if (np->rip6_tag) {
+ trace(2, " tag=0x%04x",
+ ntohs(np->rip6_tag) & 0xffff);
+ }
+ trace(2, "\n");
+ }
+ }
+ error = sendpacket(sin, RIPSIZE(nrt));
+ if (error == EAFNOSUPPORT) {
+ /* Protocol not supported */
+ tracet(1, "Could not send info to %s (%s): "
+ "set IFF_UP to 0\n",
+ ifcp->ifc_name, inet6_n2p(&ifcp->ifc_ripsin.sin6_addr));
+ ifcp->ifc_flags &= ~IFF_UP; /* As if down for AF_INET6 */
+ }
+ nrt = 0; np = ripbuf->rip6_nets;
+}
+
+/*
+ * Generate RIP6_RESPONSE packets and send them.
+ */
+void
+ripsend(ifcp, sin, flag)
+ struct ifc *ifcp;
+ struct sockaddr_in6 *sin;
+ int flag;
+{
+ struct riprt *rrt;
+ struct in6_addr *nh; /* next hop */
+ int maxrte;
+
+ if (ifcp == NULL) {
+ /*
+ * Request from non-link local address is not
+ * a regular route6d update.
+ */
+ maxrte = (IFMINMTU - sizeof(struct ip6_hdr) -
+ sizeof(struct udphdr) -
+ sizeof(struct rip6) + sizeof(struct netinfo6)) /
+ sizeof(struct netinfo6);
+ nrt = 0; np = ripbuf->rip6_nets; nh = NULL;
+ for (rrt = riprt; rrt; rrt = rrt->rrt_next) {
+ if (rrt->rrt_rflags & RRTF_NOADVERTISE)
+ continue;
+ /* Put the route to the buffer */
+ *np = rrt->rrt_info;
+ np++; nrt++;
+ if (nrt == maxrte) {
+ ripflush(NULL, sin);
+ nh = NULL;
+ }
+ }
+ if (nrt) /* Send last packet */
+ ripflush(NULL, sin);
+ return;
+ }
+
+ if ((flag & RRTF_SENDANYWAY) == 0 &&
+ (qflag || (ifcp->ifc_flags & IFF_LOOPBACK)))
+ return;
+
+ /* -N: no use */
+ if (iff_find(ifcp, 'N') != NULL)
+ return;
+
+ /* -T: generate default route only */
+ if (iff_find(ifcp, 'T') != NULL) {
+ struct netinfo6 rrt_info;
+ memset(&rrt_info, 0, sizeof(struct netinfo6));
+ rrt_info.rip6_dest = in6addr_any;
+ rrt_info.rip6_plen = 0;
+ rrt_info.rip6_metric = 1;
+ rrt_info.rip6_metric += ifcp->ifc_metric;
+ rrt_info.rip6_tag = htons(routetag & 0xffff);
+ np = ripbuf->rip6_nets;
+ *np = rrt_info;
+ nrt = 1;
+ ripflush(ifcp, sin);
+ return;
+ }
+
+ maxrte = (ifcp->ifc_mtu - sizeof(struct ip6_hdr) -
+ sizeof(struct udphdr) -
+ sizeof(struct rip6) + sizeof(struct netinfo6)) /
+ sizeof(struct netinfo6);
+
+ nrt = 0; np = ripbuf->rip6_nets; nh = NULL;
+ for (rrt = riprt; rrt; rrt = rrt->rrt_next) {
+ if (rrt->rrt_rflags & RRTF_NOADVERTISE)
+ continue;
+
+ /* Need to check filter here */
+ if (out_filter(rrt, ifcp) == 0)
+ continue;
+
+ /* Check split horizon and other conditions */
+ if (tobeadv(rrt, ifcp) == 0)
+ continue;
+
+ /* Only considers the routes with flag if specified */
+ if ((flag & RRTF_CHANGED) &&
+ (rrt->rrt_rflags & RRTF_CHANGED) == 0)
+ continue;
+
+ /* Check nexthop */
+ if (rrt->rrt_index == ifcp->ifc_index &&
+ !IN6_IS_ADDR_UNSPECIFIED(&rrt->rrt_gw) &&
+ (rrt->rrt_rflags & RRTF_NH_NOT_LLADDR) == 0) {
+ if (nh == NULL || !IN6_ARE_ADDR_EQUAL(nh, &rrt->rrt_gw)) {
+ if (nrt == maxrte - 2)
+ ripflush(ifcp, sin);
+ np->rip6_dest = rrt->rrt_gw;
+ if (IN6_IS_ADDR_LINKLOCAL(&np->rip6_dest))
+ SET_IN6_LINKLOCAL_IFINDEX(np->rip6_dest, 0);
+ np->rip6_plen = 0;
+ np->rip6_tag = 0;
+ np->rip6_metric = NEXTHOP_METRIC;
+ nh = &rrt->rrt_gw;
+ np++; nrt++;
+ }
+ } else if (nh && (rrt->rrt_index != ifcp->ifc_index ||
+ !IN6_ARE_ADDR_EQUAL(nh, &rrt->rrt_gw) ||
+ rrt->rrt_rflags & RRTF_NH_NOT_LLADDR)) {
+ /* Reset nexthop */
+ if (nrt == maxrte - 2)
+ ripflush(ifcp, sin);
+ memset(np, 0, sizeof(struct netinfo6));
+ np->rip6_metric = NEXTHOP_METRIC;
+ nh = NULL;
+ np++; nrt++;
+ }
+
+ /* Put the route to the buffer */
+ *np = rrt->rrt_info;
+ np++; nrt++;
+ if (nrt == maxrte) {
+ ripflush(ifcp, sin);
+ nh = NULL;
+ }
+ }
+ if (nrt) /* Send last packet */
+ ripflush(ifcp, sin);
+}
+
+/*
+ * outbound filter logic, per-route/interface.
+ */
+int
+out_filter(rrt, ifcp)
+ struct riprt *rrt;
+ struct ifc *ifcp;
+{
+ struct iff *iffp;
+ struct in6_addr ia;
+ int ok;
+
+ /*
+ * -A: filter out less specific routes, if we have aggregated
+ * route configured.
+ */
+ for (iffp = ifcp->ifc_filter; iffp; iffp = iffp->iff_next) {
+ if (iffp->iff_type != 'A')
+ continue;
+ if (rrt->rrt_info.rip6_plen <= iffp->iff_plen)
+ continue;
+ ia = rrt->rrt_info.rip6_dest;
+ applyplen(&ia, iffp->iff_plen);
+ if (IN6_ARE_ADDR_EQUAL(&ia, &iffp->iff_addr))
+ return 0;
+ }
+
+ /*
+ * if it is an aggregated route, advertise it only to the
+ * interfaces specified on -A.
+ */
+ if ((rrt->rrt_rflags & RRTF_AGGREGATE) != 0) {
+ ok = 0;
+ for (iffp = ifcp->ifc_filter; iffp; iffp = iffp->iff_next) {
+ if (iffp->iff_type != 'A')
+ continue;
+ if (rrt->rrt_info.rip6_plen == iffp->iff_plen &&
+ IN6_ARE_ADDR_EQUAL(&rrt->rrt_info.rip6_dest,
+ &iffp->iff_addr)) {
+ ok = 1;
+ break;
+ }
+ }
+ if (!ok)
+ return 0;
+ }
+
+ /*
+ * -O: advertise only if prefix matches the configured prefix.
+ */
+ if (iff_find(ifcp, 'O')) {
+ ok = 0;
+ for (iffp = ifcp->ifc_filter; iffp; iffp = iffp->iff_next) {
+ if (iffp->iff_type != 'O')
+ continue;
+ if (rrt->rrt_info.rip6_plen < iffp->iff_plen)
+ continue;
+ ia = rrt->rrt_info.rip6_dest;
+ applyplen(&ia, iffp->iff_plen);
+ if (IN6_ARE_ADDR_EQUAL(&ia, &iffp->iff_addr)) {
+ ok = 1;
+ break;
+ }
+ }
+ if (!ok)
+ return 0;
+ }
+
+ /* the prefix should be advertised */
+ return 1;
+}
+
+/*
+ * Determine if the route is to be advertised on the specified interface.
+ * It checks options specified in the arguments and the split horizon rule.
+ */
+int
+tobeadv(rrt, ifcp)
+ struct riprt *rrt;
+ struct ifc *ifcp;
+{
+
+ /* Special care for static routes */
+ if (rrt->rrt_flags & RTF_STATIC) {
+ /* XXX don't advertise reject/blackhole routes */
+ if (rrt->rrt_flags & (RTF_REJECT | RTF_BLACKHOLE))
+ return 0;
+
+ if (Sflag) /* Yes, advertise it anyway */
+ return 1;
+ if (sflag && rrt->rrt_index != ifcp->ifc_index)
+ return 1;
+ return 0;
+ }
+ /* Regular split horizon */
+ if (hflag == 0 && rrt->rrt_index == ifcp->ifc_index)
+ return 0;
+ return 1;
+}
+
+/*
+ * Send a rip packet actually.
+ */
+int
+sendpacket(sin, len)
+ struct sockaddr_in6 *sin;
+ int len;
+{
+ /*
+ * MSG_DONTROUTE should not be specified when it responds with a
+ * RIP6_REQUEST message. SO_DONTROUTE has been specified to
+ * other sockets.
+ */
+ struct msghdr m;
+ struct cmsghdr *cm;
+ struct iovec iov[2];
+ u_char cmsgbuf[256];
+ struct in6_pktinfo *pi;
+ int idx;
+ struct sockaddr_in6 sincopy;
+
+ /* do not overwrite the given sin */
+ sincopy = *sin;
+ sin = &sincopy;
+
+ if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr)
+ || IN6_IS_ADDR_MULTICAST(&sin->sin6_addr)) {
+ idx = IN6_LINKLOCAL_IFINDEX(sin->sin6_addr);
+ SET_IN6_LINKLOCAL_IFINDEX(sin->sin6_addr, 0);
+ } else
+ idx = 0;
+
+ m.msg_name = (caddr_t)sin;
+ m.msg_namelen = sizeof(*sin);
+ iov[0].iov_base = (caddr_t)ripbuf;
+ iov[0].iov_len = len;
+ m.msg_iov = iov;
+ m.msg_iovlen = 1;
+ if (!idx) {
+ m.msg_control = NULL;
+ m.msg_controllen = 0;
+ } else {
+ memset(cmsgbuf, 0, sizeof(cmsgbuf));
+ cm = (struct cmsghdr *)cmsgbuf;
+ m.msg_control = (caddr_t)cm;
+ m.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo));
+
+ cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
+ cm->cmsg_level = IPPROTO_IPV6;
+ cm->cmsg_type = IPV6_PKTINFO;
+ pi = (struct in6_pktinfo *)CMSG_DATA(cm);
+ memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); /*::*/
+ pi->ipi6_ifindex = idx;
+ }
+
+ if (sendmsg(ripsock, &m, 0 /*MSG_DONTROUTE*/) < 0) {
+ trace(1, "sendmsg: %s\n", strerror(errno));
+ return errno;
+ }
+
+ return 0;
+}
+
+/*
+ * Receive and process RIP packets. Update the routes/kernel forwarding
+ * table if necessary.
+ */
+void
+riprecv()
+{
+ struct ifc *ifcp, *ic;
+ struct sockaddr_in6 fsock;
+ struct in6_addr nh; /* next hop */
+ struct rip6 *rp;
+ struct netinfo6 *np, *nq;
+ struct riprt *rrt;
+ int len, nn, need_trigger, idx;
+ char buf[4 * RIP6_MAXMTU];
+ time_t t;
+ struct msghdr m;
+ struct cmsghdr *cm;
+ struct iovec iov[2];
+ u_char cmsgbuf[256];
+ struct in6_pktinfo *pi;
+ struct iff *iffp;
+ struct in6_addr ia;
+ int ok;
+ time_t t_half_lifetime;
+
+ need_trigger = 0;
+
+ m.msg_name = (caddr_t)&fsock;
+ m.msg_namelen = sizeof(fsock);
+ iov[0].iov_base = (caddr_t)buf;
+ iov[0].iov_len = sizeof(buf);
+ m.msg_iov = iov;
+ m.msg_iovlen = 1;
+ cm = (struct cmsghdr *)cmsgbuf;
+ m.msg_control = (caddr_t)cm;
+ m.msg_controllen = sizeof(cmsgbuf);
+ if ((len = recvmsg(ripsock, &m, 0)) < 0) {
+ fatal("recvmsg");
+ /*NOTREACHED*/
+ }
+ idx = 0;
+ for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&m);
+ cm;
+ cm = (struct cmsghdr *)CMSG_NXTHDR(&m, cm)) {
+ if (cm->cmsg_level == IPPROTO_IPV6 &&
+ cm->cmsg_type == IPV6_PKTINFO) {
+ pi = (struct in6_pktinfo *)(CMSG_DATA(cm));
+ idx = pi->ipi6_ifindex;
+ break;
+ }
+ }
+ if (idx && IN6_IS_ADDR_LINKLOCAL(&fsock.sin6_addr))
+ SET_IN6_LINKLOCAL_IFINDEX(fsock.sin6_addr, idx);
+
+ nh = fsock.sin6_addr;
+ nn = (len - sizeof(struct rip6) + sizeof(struct netinfo6)) /
+ sizeof(struct netinfo6);
+ rp = (struct rip6 *)buf;
+ np = rp->rip6_nets;
+
+ if (rp->rip6_vers != RIP6_VERSION) {
+ trace(1, "Incorrect RIP version %d\n", rp->rip6_vers);
+ return;
+ }
+ if (rp->rip6_cmd == RIP6_REQUEST) {
+ if (idx && idx < nindex2ifc) {
+ ifcp = index2ifc[idx];
+ riprequest(ifcp, np, nn, &fsock);
+ } else {
+ riprequest(NULL, np, nn, &fsock);
+ }
+ return;
+ }
+
+ if (!IN6_IS_ADDR_LINKLOCAL(&fsock.sin6_addr)) {
+ trace(1, "Packets from non-ll addr: %s\n",
+ inet6_n2p(&fsock.sin6_addr));
+ return; /* Ignore packets from non-link-local addr */
+ }
+ idx = IN6_LINKLOCAL_IFINDEX(fsock.sin6_addr);
+ ifcp = (idx < nindex2ifc) ? index2ifc[idx] : NULL;
+ if (!ifcp) {
+ trace(1, "Packets to unknown interface index %d\n", idx);
+ return; /* Ignore it */
+ }
+ if (IN6_ARE_ADDR_EQUAL(&ifcp->ifc_mylladdr, &fsock.sin6_addr))
+ return; /* The packet is from me; ignore */
+ if (rp->rip6_cmd != RIP6_RESPONSE) {
+ trace(1, "Invalid command %d\n", rp->rip6_cmd);
+ return;
+ }
+
+ /* -N: no use */
+ if (iff_find(ifcp, 'N') != NULL)
+ return;
+
+ tracet(1, "Recv(%s): from %s.%d info(%d)\n",
+ ifcp->ifc_name, inet6_n2p(&nh), ntohs(fsock.sin6_port), nn);
+
+ t = time(NULL);
+ t_half_lifetime = t - (RIP_LIFETIME/2);
+ for (; nn; nn--, np++) {
+ if (np->rip6_metric == NEXTHOP_METRIC) {
+ /* modify neighbor address */
+ if (IN6_IS_ADDR_LINKLOCAL(&np->rip6_dest)) {
+ nh = np->rip6_dest;
+ SET_IN6_LINKLOCAL_IFINDEX(nh, idx);
+ trace(1, "\tNexthop: %s\n", inet6_n2p(&nh));
+ } else if (IN6_IS_ADDR_UNSPECIFIED(&np->rip6_dest)) {
+ nh = fsock.sin6_addr;
+ trace(1, "\tNexthop: %s\n", inet6_n2p(&nh));
+ } else {
+ nh = fsock.sin6_addr;
+ trace(1, "\tInvalid Nexthop: %s\n",
+ inet6_n2p(&np->rip6_dest));
+ }
+ continue;
+ }
+ if (IN6_IS_ADDR_MULTICAST(&np->rip6_dest)) {
+ trace(1, "\tMulticast netinfo6: %s/%d [%d]\n",
+ inet6_n2p(&np->rip6_dest),
+ np->rip6_plen, np->rip6_metric);
+ continue;
+ }
+ if (IN6_IS_ADDR_LOOPBACK(&np->rip6_dest)) {
+ trace(1, "\tLoopback netinfo6: %s/%d [%d]\n",
+ inet6_n2p(&np->rip6_dest),
+ np->rip6_plen, np->rip6_metric);
+ continue;
+ }
+ if (IN6_IS_ADDR_LINKLOCAL(&np->rip6_dest)) {
+ trace(1, "\tLink Local netinfo6: %s/%d [%d]\n",
+ inet6_n2p(&np->rip6_dest),
+ np->rip6_plen, np->rip6_metric);
+ continue;
+ }
+ /* may need to pass sitelocal prefix in some case, however*/
+ if (IN6_IS_ADDR_SITELOCAL(&np->rip6_dest) && !lflag) {
+ trace(1, "\tSite Local netinfo6: %s/%d [%d]\n",
+ inet6_n2p(&np->rip6_dest),
+ np->rip6_plen, np->rip6_metric);
+ continue;
+ }
+ trace(2, "\tnetinfo6: %s/%d [%d]",
+ inet6_n2p(&np->rip6_dest),
+ np->rip6_plen, np->rip6_metric);
+ if (np->rip6_tag)
+ trace(2, " tag=0x%04x", ntohs(np->rip6_tag) & 0xffff);
+ if (dflag >= 2) {
+ ia = np->rip6_dest;
+ applyplen(&ia, np->rip6_plen);
+ if (!IN6_ARE_ADDR_EQUAL(&ia, &np->rip6_dest))
+ trace(2, " [junk outside prefix]");
+ }
+
+ /*
+ * -L: listen only if the prefix matches the configuration
+ */
+ ok = 1; /* if there's no L filter, it is ok */
+ for (iffp = ifcp->ifc_filter; iffp; iffp = iffp->iff_next) {
+ if (iffp->iff_type != 'L')
+ continue;
+ ok = 0;
+ if (np->rip6_plen < iffp->iff_plen)
+ continue;
+ /* special rule: ::/0 means default, not "in /0" */
+ if (iffp->iff_plen == 0 && np->rip6_plen > 0)
+ continue;
+ ia = np->rip6_dest;
+ applyplen(&ia, iffp->iff_plen);
+ if (IN6_ARE_ADDR_EQUAL(&ia, &iffp->iff_addr)) {
+ ok = 1;
+ break;
+ }
+ }
+ if (!ok) {
+ trace(2, " (filtered)\n");
+ continue;
+ }
+
+ trace(2, "\n");
+ np->rip6_metric++;
+ np->rip6_metric += ifcp->ifc_metric;
+ if (np->rip6_metric > HOPCNT_INFINITY6)
+ np->rip6_metric = HOPCNT_INFINITY6;
+
+ applyplen(&np->rip6_dest, np->rip6_plen);
+ if ((rrt = rtsearch(np, NULL)) != NULL) {
+ if (rrt->rrt_t == 0)
+ continue; /* Intf route has priority */
+ nq = &rrt->rrt_info;
+ if (nq->rip6_metric > np->rip6_metric) {
+ if (rrt->rrt_index == ifcp->ifc_index &&
+ IN6_ARE_ADDR_EQUAL(&nh, &rrt->rrt_gw)) {
+ /* Small metric from the same gateway */
+ nq->rip6_metric = np->rip6_metric;
+ } else {
+ /* Better route found */
+ rrt->rrt_index = ifcp->ifc_index;
+ /* Update routing table */
+ delroute(nq, &rrt->rrt_gw);
+ rrt->rrt_gw = nh;
+ *nq = *np;
+ addroute(rrt, &nh, ifcp);
+ }
+ rrt->rrt_rflags |= RRTF_CHANGED;
+ rrt->rrt_t = t;
+ need_trigger = 1;
+ } else if (nq->rip6_metric < np->rip6_metric &&
+ rrt->rrt_index == ifcp->ifc_index &&
+ IN6_ARE_ADDR_EQUAL(&nh, &rrt->rrt_gw)) {
+ /* Got worse route from same gw */
+ nq->rip6_metric = np->rip6_metric;
+ rrt->rrt_t = t;
+ rrt->rrt_rflags |= RRTF_CHANGED;
+ need_trigger = 1;
+ } else if (nq->rip6_metric == np->rip6_metric &&
+ np->rip6_metric < HOPCNT_INFINITY6) {
+ if (rrt->rrt_index == ifcp->ifc_index &&
+ IN6_ARE_ADDR_EQUAL(&nh, &rrt->rrt_gw)) {
+ /* same metric, same route from same gw */
+ rrt->rrt_t = t;
+ } else if (rrt->rrt_t < t_half_lifetime) {
+ /* Better route found */
+ rrt->rrt_index = ifcp->ifc_index;
+ /* Update routing table */
+ delroute(nq, &rrt->rrt_gw);
+ rrt->rrt_gw = nh;
+ *nq = *np;
+ addroute(rrt, &nh, ifcp);
+ rrt->rrt_rflags |= RRTF_CHANGED;
+ rrt->rrt_t = t;
+ }
+ }
+ /*
+ * if nq->rip6_metric == HOPCNT_INFINITY6 then
+ * do not update age value. Do nothing.
+ */
+ } else if (np->rip6_metric < HOPCNT_INFINITY6) {
+ /* Got a new valid route */
+ if ((rrt = MALLOC(struct riprt)) == NULL) {
+ fatal("malloc: struct riprt");
+ /*NOTREACHED*/
+ }
+ memset(rrt, 0, sizeof(*rrt));
+ nq = &rrt->rrt_info;
+
+ rrt->rrt_same = NULL;
+ rrt->rrt_index = ifcp->ifc_index;
+ rrt->rrt_flags = RTF_UP|RTF_GATEWAY;
+ rrt->rrt_gw = nh;
+ *nq = *np;
+ applyplen(&nq->rip6_dest, nq->rip6_plen);
+ if (nq->rip6_plen == sizeof(struct in6_addr) * 8)
+ rrt->rrt_flags |= RTF_HOST;
+
+ /* Put the route to the list */
+ rrt->rrt_next = riprt;
+ riprt = rrt;
+ /* Update routing table */
+ addroute(rrt, &nh, ifcp);
+ rrt->rrt_rflags |= RRTF_CHANGED;
+ need_trigger = 1;
+ rrt->rrt_t = t;
+ }
+ }
+ /* XXX need to care the interval between triggered updates */
+ if (need_trigger) {
+ if (nextalarm > time(NULL) + RIP_TRIG_INT6_MAX) {
+ for (ic = ifc; ic; ic = ic->ifc_next) {
+ if (ifcp->ifc_index == ic->ifc_index)
+ continue;
+ if (ic->ifc_flags & IFF_UP)
+ ripsend(ic, &ic->ifc_ripsin,
+ RRTF_CHANGED);
+ }
+ }
+ /* Reset the flag */
+ for (rrt = riprt; rrt; rrt = rrt->rrt_next)
+ rrt->rrt_rflags &= ~RRTF_CHANGED;
+ }
+}
+
+/*
+ * Send all routes request packet to the specified interface.
+ */
+void
+sendrequest(ifcp)
+ struct ifc *ifcp;
+{
+ struct netinfo6 *np;
+ int error;
+
+ if (ifcp->ifc_flags & IFF_LOOPBACK)
+ return;
+ ripbuf->rip6_cmd = RIP6_REQUEST;
+ np = ripbuf->rip6_nets;
+ memset(np, 0, sizeof(struct netinfo6));
+ np->rip6_metric = HOPCNT_INFINITY6;
+ tracet(1, "Send rtdump Request to %s (%s)\n",
+ ifcp->ifc_name, inet6_n2p(&ifcp->ifc_ripsin.sin6_addr));
+ error = sendpacket(&ifcp->ifc_ripsin, RIPSIZE(1));
+ if (error == EAFNOSUPPORT) {
+ /* Protocol not supported */
+ tracet(1, "Could not send rtdump Request to %s (%s): "
+ "set IFF_UP to 0\n",
+ ifcp->ifc_name, inet6_n2p(&ifcp->ifc_ripsin.sin6_addr));
+ ifcp->ifc_flags &= ~IFF_UP; /* As if down for AF_INET6 */
+ }
+ ripbuf->rip6_cmd = RIP6_RESPONSE;
+}
+
+/*
+ * Process a RIP6_REQUEST packet.
+ */
+void
+riprequest(ifcp, np, nn, sin)
+ struct ifc *ifcp;
+ struct netinfo6 *np;
+ int nn;
+ struct sockaddr_in6 *sin;
+{
+ int i;
+ struct riprt *rrt;
+
+ if (!(nn == 1 && IN6_IS_ADDR_UNSPECIFIED(&np->rip6_dest) &&
+ np->rip6_plen == 0 && np->rip6_metric == HOPCNT_INFINITY6)) {
+ /* Specific response, don't split-horizon */
+ trace(1, "\tRIP Request\n");
+ for (i = 0; i < nn; i++, np++) {
+ rrt = rtsearch(np, NULL);
+ if (rrt)
+ np->rip6_metric = rrt->rrt_info.rip6_metric;
+ else
+ np->rip6_metric = HOPCNT_INFINITY6;
+ }
+ (void)sendpacket(sin, RIPSIZE(nn));
+ return;
+ }
+ /* Whole routing table dump */
+ trace(1, "\tRIP Request -- whole routing table\n");
+ ripsend(ifcp, sin, RRTF_SENDANYWAY);
+}
+
+/*
+ * Get information of each interface.
+ */
+void
+ifconfig()
+{
+ struct ifaddrs *ifap, *ifa;
+ struct ifc *ifcp;
+ struct ipv6_mreq mreq;
+ int s;
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ fatal("socket");
+ /*NOTREACHED*/
+ }
+
+ if (getifaddrs(&ifap) != 0) {
+ fatal("getifaddrs");
+ /*NOTREACHED*/
+ }
+
+ for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ ifcp = ifc_find(ifa->ifa_name);
+ /* we are interested in multicast-capable interfaces */
+ if ((ifa->ifa_flags & IFF_MULTICAST) == 0)
+ continue;
+ if (!ifcp) {
+ /* new interface */
+ if ((ifcp = MALLOC(struct ifc)) == NULL) {
+ fatal("malloc: struct ifc");
+ /*NOTREACHED*/
+ }
+ memset(ifcp, 0, sizeof(*ifcp));
+ ifcp->ifc_index = -1;
+ ifcp->ifc_next = ifc;
+ ifc = ifcp;
+ nifc++;
+ ifcp->ifc_name = allocopy(ifa->ifa_name);
+ ifcp->ifc_addr = 0;
+ ifcp->ifc_filter = 0;
+ ifcp->ifc_flags = ifa->ifa_flags;
+ trace(1, "newif %s <%s>\n", ifcp->ifc_name,
+ ifflags(ifcp->ifc_flags));
+ if (!strcmp(ifcp->ifc_name, LOOPBACK_IF))
+ loopifcp = ifcp;
+ } else {
+ /* update flag, this may be up again */
+ if (ifcp->ifc_flags != ifa->ifa_flags) {
+ trace(1, "%s: <%s> -> ", ifcp->ifc_name,
+ ifflags(ifcp->ifc_flags));
+ trace(1, "<%s>\n", ifflags(ifa->ifa_flags));
+ ifcp->ifc_cflags |= IFC_CHANGED;
+ }
+ ifcp->ifc_flags = ifa->ifa_flags;
+ }
+ ifconfig1(ifa->ifa_name, ifa->ifa_addr, ifcp, s);
+ if ((ifcp->ifc_flags & (IFF_LOOPBACK | IFF_UP)) == IFF_UP
+ && 0 < ifcp->ifc_index && !ifcp->ifc_joined) {
+ mreq.ipv6mr_multiaddr = ifcp->ifc_ripsin.sin6_addr;
+ mreq.ipv6mr_interface = ifcp->ifc_index;
+ if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_JOIN_GROUP,
+ &mreq, sizeof(mreq)) < 0) {
+ fatal("IPV6_JOIN_GROUP");
+ /*NOTREACHED*/
+ }
+ trace(1, "join %s %s\n", ifcp->ifc_name, RIP6_DEST);
+ ifcp->ifc_joined++;
+ }
+ }
+ close(s);
+ freeifaddrs(ifap);
+}
+
+void
+ifconfig1(name, sa, ifcp, s)
+ const char *name;
+ const struct sockaddr *sa;
+ struct ifc *ifcp;
+ int s;
+{
+ struct in6_ifreq ifr;
+ const struct sockaddr_in6 *sin;
+ struct ifac *ifa;
+ int plen;
+ char buf[BUFSIZ];
+
+ sin = (const struct sockaddr_in6 *)sa;
+ ifr.ifr_addr = *sin;
+ strcpy(ifr.ifr_name, name);
+ if (ioctl(s, SIOCGIFNETMASK_IN6, (char *)&ifr) < 0) {
+ fatal("ioctl: SIOCGIFNETMASK_IN6");
+ /*NOTREACHED*/
+ }
+ plen = sin6mask2len(&ifr.ifr_addr);
+ if ((ifa = ifa_match(ifcp, &sin->sin6_addr, plen)) != NULL) {
+ /* same interface found */
+ /* need check if something changed */
+ /* XXX not yet implemented */
+ return;
+ }
+ /*
+ * New address is found
+ */
+ if ((ifa = MALLOC(struct ifac)) == NULL) {
+ fatal("malloc: struct ifac");
+ /*NOTREACHED*/
+ }
+ memset(ifa, 0, sizeof(*ifa));
+ ifa->ifa_conf = ifcp;
+ ifa->ifa_next = ifcp->ifc_addr;
+ ifcp->ifc_addr = ifa;
+ ifa->ifa_addr = sin->sin6_addr;
+ ifa->ifa_plen = plen;
+ if (ifcp->ifc_flags & IFF_POINTOPOINT) {
+ ifr.ifr_addr = *sin;
+ if (ioctl(s, SIOCGIFDSTADDR_IN6, (char *)&ifr) < 0) {
+ fatal("ioctl: SIOCGIFDSTADDR_IN6");
+ /*NOTREACHED*/
+ }
+ ifa->ifa_raddr = ifr.ifr_dstaddr.sin6_addr;
+ inet_ntop(AF_INET6, (void *)&ifa->ifa_raddr, buf, sizeof(buf));
+ trace(1, "found address %s/%d -- %s\n",
+ inet6_n2p(&ifa->ifa_addr), ifa->ifa_plen, buf);
+ } else {
+ trace(1, "found address %s/%d\n",
+ inet6_n2p(&ifa->ifa_addr), ifa->ifa_plen);
+ }
+ if (ifcp->ifc_index < 0 && IN6_IS_ADDR_LINKLOCAL(&ifa->ifa_addr)) {
+ ifcp->ifc_mylladdr = ifa->ifa_addr;
+ ifcp->ifc_index = IN6_LINKLOCAL_IFINDEX(ifa->ifa_addr);
+ memcpy(&ifcp->ifc_ripsin, &ripsin, ripsin.ss_len);
+ SET_IN6_LINKLOCAL_IFINDEX(ifcp->ifc_ripsin.sin6_addr,
+ ifcp->ifc_index);
+ setindex2ifc(ifcp->ifc_index, ifcp);
+ ifcp->ifc_mtu = getifmtu(ifcp->ifc_index);
+ if (ifcp->ifc_mtu > RIP6_MAXMTU)
+ ifcp->ifc_mtu = RIP6_MAXMTU;
+ if (ioctl(s, SIOCGIFMETRIC, (char *)&ifr) < 0) {
+ fatal("ioctl: SIOCGIFMETRIC");
+ /*NOTREACHED*/
+ }
+ ifcp->ifc_metric = ifr.ifr_metric;
+ trace(1, "\tindex: %d, mtu: %d, metric: %d\n",
+ ifcp->ifc_index, ifcp->ifc_mtu, ifcp->ifc_metric);
+ } else
+ ifcp->ifc_cflags |= IFC_CHANGED;
+}
+
+/*
+ * Receive and process routing messages.
+ * Update interface information as necesssary.
+ */
+void
+rtrecv()
+{
+ char buf[BUFSIZ];
+ char *p, *q;
+ struct rt_msghdr *rtm;
+ struct ifa_msghdr *ifam;
+ struct if_msghdr *ifm;
+ int len;
+ struct ifc *ifcp, *ic;
+ int iface = 0, rtable = 0;
+ struct sockaddr_in6 *rta[RTAX_MAX];
+ struct sockaddr_in6 mask;
+ int i, addrs;
+ struct riprt *rrt;
+
+ if ((len = read(rtsock, buf, sizeof(buf))) < 0) {
+ perror("read from rtsock");
+ exit(1);
+ }
+ if (len < sizeof(*rtm)) {
+ trace(1, "short read from rtsock: %d (should be > %lu)\n",
+ len, (u_long)sizeof(*rtm));
+ return;
+ }
+
+ for (p = buf; p - buf < len; p += ((struct rt_msghdr *)p)->rtm_msglen) {
+ /* safety against bogus message */
+ if (((struct rt_msghdr *)p)->rtm_msglen <= 0) {
+ trace(1, "bogus rtmsg: length=%d\n",
+ ((struct rt_msghdr *)p)->rtm_msglen);
+ break;
+ }
+ rtm = NULL;
+ ifam = NULL;
+ ifm = NULL;
+ switch (((struct rt_msghdr *)p)->rtm_type) {
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ ifam = (struct ifa_msghdr *)p;
+ addrs = ifam->ifam_addrs;
+ q = (char *)(ifam + 1);
+ break;
+ case RTM_IFINFO:
+ ifm = (struct if_msghdr *)p;
+ addrs = ifm->ifm_addrs;
+ q = (char *)(ifm + 1);
+ break;
+ default:
+ rtm = (struct rt_msghdr *)p;
+ addrs = rtm->rtm_addrs;
+ q = (char *)(rtm + 1);
+ if (rtm->rtm_version != RTM_VERSION) {
+ trace(1, "unexpected rtmsg version %d "
+ "(should be %d)\n",
+ rtm->rtm_version, RTM_VERSION);
+ continue;
+ }
+ if (rtm->rtm_pid == pid) {
+#if 0
+ trace(1, "rtmsg looped back to me, ignored\n");
+#endif
+ continue;
+ }
+ break;
+ }
+ memset(&rta, 0, sizeof(rta));
+ for (i = 0; i < RTAX_MAX; i++) {
+ if (addrs & (1 << i)) {
+ rta[i] = (struct sockaddr_in6 *)q;
+ q += ROUNDUP(rta[i]->sin6_len);
+ }
+ }
+
+ trace(1, "rtsock: %s (addrs=%x)\n",
+ rttypes((struct rt_msghdr *)p), addrs);
+ if (dflag >= 2) {
+ for (i = 0;
+ i < ((struct rt_msghdr *)p)->rtm_msglen;
+ i++) {
+ fprintf(stderr, "%02x ", p[i] & 0xff);
+ if (i % 16 == 15) fprintf(stderr, "\n");
+ }
+ fprintf(stderr, "\n");
+ }
+
+ /*
+ * Easy ones first.
+ *
+ * We may be able to optimize by using ifm->ifm_index or
+ * ifam->ifam_index. For simplicity we don't do that here.
+ */
+ switch (((struct rt_msghdr *)p)->rtm_type) {
+ case RTM_NEWADDR:
+ case RTM_IFINFO:
+ iface++;
+ continue;
+ case RTM_ADD:
+ rtable++;
+ continue;
+ case RTM_LOSING:
+ case RTM_MISS:
+ case RTM_RESOLVE:
+ case RTM_GET:
+ case RTM_LOCK:
+ /* nothing to be done here */
+ trace(1, "\tnothing to be done, ignored\n");
+ continue;
+ }
+
+#if 0
+ if (rta[RTAX_DST] == NULL) {
+ trace(1, "\tno destination, ignored\n");
+ continue;
+ }
+ if (rta[RTAX_DST]->sin6_family != AF_INET6) {
+ trace(1, "\taf mismatch, ignored\n");
+ continue;
+ }
+ if (IN6_IS_ADDR_LINKLOCAL(&rta[RTAX_DST]->sin6_addr)) {
+ trace(1, "\tlinklocal destination, ignored\n");
+ continue;
+ }
+ if (IN6_ARE_ADDR_EQUAL(&rta[RTAX_DST]->sin6_addr, &in6addr_loopback)) {
+ trace(1, "\tloopback destination, ignored\n");
+ continue; /* Loopback */
+ }
+ if (IN6_IS_ADDR_MULTICAST(&rta[RTAX_DST]->sin6_addr)) {
+ trace(1, "\tmulticast destination, ignored\n");
+ continue;
+ }
+#endif
+
+ /* hard ones */
+ switch (((struct rt_msghdr *)p)->rtm_type) {
+ case RTM_NEWADDR:
+ case RTM_IFINFO:
+ case RTM_ADD:
+ case RTM_LOSING:
+ case RTM_MISS:
+ case RTM_RESOLVE:
+ case RTM_GET:
+ case RTM_LOCK:
+ /* should already be handled */
+ fatal("rtrecv: never reach here");
+ /*NOTREACHED*/
+ case RTM_DELETE:
+ if (!rta[RTAX_DST] || !rta[RTAX_GATEWAY]) {
+ trace(1, "\tsome of dst/gw/netamsk are "
+ "unavailable, ignored\n");
+ break;
+ }
+ if ((rtm->rtm_flags & RTF_HOST) != 0) {
+ mask.sin6_len = sizeof(mask);
+ memset(&mask.sin6_addr, 0xff,
+ sizeof(mask.sin6_addr));
+ rta[RTAX_NETMASK] = &mask;
+ } else if (!rta[RTAX_NETMASK]) {
+ trace(1, "\tsome of dst/gw/netamsk are "
+ "unavailable, ignored\n");
+ break;
+ }
+ if (rt_del(rta[RTAX_DST], rta[RTAX_GATEWAY],
+ rta[RTAX_NETMASK]) == 0) {
+ rtable++; /*just to be sure*/
+ }
+ break;
+ case RTM_CHANGE:
+ case RTM_REDIRECT:
+ trace(1, "\tnot supported yet, ignored\n");
+ break;
+ case RTM_DELADDR:
+ if (!rta[RTAX_NETMASK] || !rta[RTAX_IFA]) {
+ trace(1, "\tno netmask or ifa given, ignored\n");
+ break;
+ }
+ if (ifam->ifam_index < nindex2ifc)
+ ifcp = index2ifc[ifam->ifam_index];
+ else
+ ifcp = NULL;
+ if (!ifcp) {
+ trace(1, "\tinvalid ifam_index %d, ignored\n",
+ ifam->ifam_index);
+ break;
+ }
+ if (!rt_deladdr(ifcp, rta[RTAX_IFA], rta[RTAX_NETMASK]))
+ iface++;
+ break;
+ case RTM_OLDADD:
+ case RTM_OLDDEL:
+ trace(1, "\tnot supported yet, ignored\n");
+ break;
+ }
+
+ }
+
+ if (iface) {
+ trace(1, "rtsock: reconfigure interfaces, refresh interface routes\n");
+ ifconfig();
+ for (ifcp = ifc; ifcp; ifcp = ifcp->ifc_next)
+ if (ifcp->ifc_cflags & IFC_CHANGED) {
+ if (ifrt(ifcp, 1)) {
+ for (ic = ifc; ic; ic = ic->ifc_next) {
+ if (ifcp->ifc_index == ic->ifc_index)
+ continue;
+ if (ic->ifc_flags & IFF_UP)
+ ripsend(ic, &ic->ifc_ripsin,
+ RRTF_CHANGED);
+ }
+ /* Reset the flag */
+ for (rrt = riprt; rrt; rrt = rrt->rrt_next)
+ rrt->rrt_rflags &= ~RRTF_CHANGED;
+ }
+ ifcp->ifc_cflags &= ~IFC_CHANGED;
+ }
+ }
+ if (rtable) {
+ trace(1, "rtsock: read routing table again\n");
+ krtread(1);
+ }
+}
+
+/*
+ * remove specified route from the internal routing table.
+ */
+int
+rt_del(sdst, sgw, smask)
+ const struct sockaddr_in6 *sdst;
+ const struct sockaddr_in6 *sgw;
+ const struct sockaddr_in6 *smask;
+{
+ const struct in6_addr *dst = NULL;
+ const struct in6_addr *gw = NULL;
+ int prefix;
+ struct netinfo6 ni6;
+ struct riprt *rrt = NULL;
+ time_t t_lifetime;
+
+ if (sdst->sin6_family != AF_INET6) {
+ trace(1, "\tother AF, ignored\n");
+ return -1;
+ }
+ if (IN6_IS_ADDR_LINKLOCAL(&sdst->sin6_addr)
+ || IN6_ARE_ADDR_EQUAL(&sdst->sin6_addr, &in6addr_loopback)
+ || IN6_IS_ADDR_MULTICAST(&sdst->sin6_addr)) {
+ trace(1, "\taddress %s not interesting, ignored\n",
+ inet6_n2p(&sdst->sin6_addr));
+ return -1;
+ }
+ dst = &sdst->sin6_addr;
+ if (sgw->sin6_family == AF_INET6) {
+ /* easy case */
+ gw = &sgw->sin6_addr;
+ prefix = sin6mask2len(smask);
+ } else if (sgw->sin6_family == AF_LINK) {
+ /*
+ * Interface route... a hard case. We need to get the prefix
+ * length from the kernel, but we now are parsing rtmsg.
+ * We'll purge matching routes from my list, then get the
+ * fresh list.
+ */
+ struct riprt *longest;
+ trace(1, "\t%s is a interface route, guessing prefixlen\n",
+ inet6_n2p(dst));
+ longest = NULL;
+ for (rrt = riprt; rrt; rrt = rrt->rrt_next) {
+ if (IN6_ARE_ADDR_EQUAL(&rrt->rrt_info.rip6_dest,
+ &sdst->sin6_addr)
+ && IN6_IS_ADDR_LOOPBACK(&rrt->rrt_gw)) {
+ if (!longest
+ || longest->rrt_info.rip6_plen <
+ rrt->rrt_info.rip6_plen) {
+ longest = rrt;
+ }
+ }
+ }
+ rrt = longest;
+ if (!rrt) {
+ trace(1, "\tno matching interface route found\n");
+ return -1;
+ }
+ gw = &in6addr_loopback;
+ prefix = rrt->rrt_info.rip6_plen;
+ } else {
+ trace(1, "\tunsupported af: (gw=%d)\n", sgw->sin6_family);
+ return -1;
+ }
+
+ trace(1, "\tdeleting %s/%d ", inet6_n2p(dst), prefix);
+ trace(1, "gw %s\n", inet6_n2p(gw));
+ t_lifetime = time(NULL) - RIP_LIFETIME;
+ /* age route for interface address */
+ memset(&ni6, 0, sizeof(ni6));
+ ni6.rip6_dest = *dst;
+ ni6.rip6_plen = prefix;
+ applyplen(&ni6.rip6_dest, ni6.rip6_plen); /*to be sure*/
+ trace(1, "\tfind route %s/%d\n", inet6_n2p(&ni6.rip6_dest),
+ ni6.rip6_plen);
+ if (!rrt && (rrt = rtsearch(&ni6, NULL)) == NULL) {
+ trace(1, "\tno route found\n");
+ return -1;
+ }
+#if 0
+ if ((rrt->rrt_flags & RTF_STATIC) == 0) {
+ trace(1, "\tyou can delete static routes only\n");
+ } else
+#endif
+ if (!IN6_ARE_ADDR_EQUAL(&rrt->rrt_gw, gw)) {
+ trace(1, "\tgw mismatch: %s <-> ",
+ inet6_n2p(&rrt->rrt_gw));
+ trace(1, "%s\n", inet6_n2p(gw));
+ } else {
+ trace(1, "\troute found, age it\n");
+ if (rrt->rrt_t == 0 || rrt->rrt_t > t_lifetime) {
+ rrt->rrt_t = t_lifetime;
+ rrt->rrt_info.rip6_metric = HOPCNT_INFINITY6;
+ }
+ }
+ return 0;
+}
+
+/*
+ * remove specified address from internal interface/routing table.
+ */
+int
+rt_deladdr(ifcp, sifa, smask)
+ struct ifc *ifcp;
+ const struct sockaddr_in6 *sifa;
+ const struct sockaddr_in6 *smask;
+{
+ const struct in6_addr *addr = NULL;
+ int prefix;
+ struct ifac *ifa = NULL;
+ struct netinfo6 ni6;
+ struct riprt *rrt = NULL;
+ time_t t_lifetime;
+ int updated = 0;
+
+ if (sifa->sin6_family != AF_INET6) {
+ trace(1, "\tother AF, ignored\n");
+ return -1;
+ }
+ addr = &sifa->sin6_addr;
+ prefix = sin6mask2len(smask);
+
+ trace(1, "\tdeleting %s/%d from %s\n",
+ inet6_n2p(addr), prefix, ifcp->ifc_name);
+ ifa = ifa_match(ifcp, addr, prefix);
+ if (!ifa) {
+ trace(1, "\tno matching ifa found for %s/%d on %s\n",
+ inet6_n2p(addr), prefix, ifcp->ifc_name);
+ return -1;
+ }
+ if (ifa->ifa_conf != ifcp) {
+ trace(1, "\taddress table corrupt: back pointer does not match "
+ "(%s != %s)\n",
+ ifcp->ifc_name, ifa->ifa_conf->ifc_name);
+ return -1;
+ }
+ /* remove ifa from interface */
+ if (ifcp->ifc_addr == ifa)
+ ifcp->ifc_addr = ifa->ifa_next;
+ else {
+ struct ifac *p;
+ for (p = ifcp->ifc_addr; p; p = p->ifa_next) {
+ if (p->ifa_next == ifa) {
+ p->ifa_next = ifa->ifa_next;
+ break;
+ }
+ }
+ }
+ ifa->ifa_next = NULL;
+ ifa->ifa_conf = NULL;
+ t_lifetime = time(NULL) - RIP_LIFETIME;
+ /* age route for interface address */
+ memset(&ni6, 0, sizeof(ni6));
+ ni6.rip6_dest = ifa->ifa_addr;
+ ni6.rip6_plen = ifa->ifa_plen;
+ applyplen(&ni6.rip6_dest, ni6.rip6_plen);
+ trace(1, "\tfind interface route %s/%d on %d\n",
+ inet6_n2p(&ni6.rip6_dest), ni6.rip6_plen, ifcp->ifc_index);
+ if ((rrt = rtsearch(&ni6, NULL)) != NULL) {
+ struct in6_addr none;
+ memset(&none, 0, sizeof(none));
+ if (rrt->rrt_index == ifcp->ifc_index &&
+ (IN6_ARE_ADDR_EQUAL(&rrt->rrt_gw, &none) ||
+ IN6_IS_ADDR_LOOPBACK(&rrt->rrt_gw))) {
+ trace(1, "\troute found, age it\n");
+ if (rrt->rrt_t == 0 || rrt->rrt_t > t_lifetime) {
+ rrt->rrt_t = t_lifetime;
+ rrt->rrt_info.rip6_metric = HOPCNT_INFINITY6;
+ }
+ updated++;
+ } else {
+ trace(1, "\tnon-interface route found: %s/%d on %d\n",
+ inet6_n2p(&rrt->rrt_info.rip6_dest),
+ rrt->rrt_info.rip6_plen,
+ rrt->rrt_index);
+ }
+ } else
+ trace(1, "\tno interface route found\n");
+ /* age route for p2p destination */
+ if (ifcp->ifc_flags & IFF_POINTOPOINT) {
+ memset(&ni6, 0, sizeof(ni6));
+ ni6.rip6_dest = ifa->ifa_raddr;
+ ni6.rip6_plen = 128;
+ applyplen(&ni6.rip6_dest, ni6.rip6_plen); /*to be sure*/
+ trace(1, "\tfind p2p route %s/%d on %d\n",
+ inet6_n2p(&ni6.rip6_dest), ni6.rip6_plen,
+ ifcp->ifc_index);
+ if ((rrt = rtsearch(&ni6, NULL)) != NULL) {
+ if (rrt->rrt_index == ifcp->ifc_index &&
+ IN6_ARE_ADDR_EQUAL(&rrt->rrt_gw, &ifa->ifa_addr)) {
+ trace(1, "\troute found, age it\n");
+ if (rrt->rrt_t == 0 || rrt->rrt_t > t_lifetime) {
+ rrt->rrt_t = t_lifetime;
+ rrt->rrt_info.rip6_metric =
+ HOPCNT_INFINITY6;
+ updated++;
+ }
+ } else {
+ trace(1, "\tnon-p2p route found: %s/%d on %d\n",
+ inet6_n2p(&rrt->rrt_info.rip6_dest),
+ rrt->rrt_info.rip6_plen,
+ rrt->rrt_index);
+ }
+ } else
+ trace(1, "\tno p2p route found\n");
+ }
+ return updated ? 0 : -1;
+}
+
+/*
+ * Get each interface address and put those interface routes to the route
+ * list.
+ */
+int
+ifrt(ifcp, again)
+ struct ifc *ifcp;
+ int again;
+{
+ struct ifac *ifa;
+ struct riprt *rrt, *search_rrt, *prev_rrt, *loop_rrt;
+ struct netinfo6 *np;
+ time_t t_lifetime;
+ int need_trigger = 0;
+
+ if (ifcp->ifc_flags & IFF_LOOPBACK)
+ return 0; /* ignore loopback */
+ if (ifcp->ifc_flags & IFF_POINTOPOINT) {
+ ifrt_p2p(ifcp, again);
+ return 0;
+ }
+
+ for (ifa = ifcp->ifc_addr; ifa; ifa = ifa->ifa_next) {
+ if (IN6_IS_ADDR_LINKLOCAL(&ifa->ifa_addr)) {
+#if 0
+ trace(1, "route: %s on %s: "
+ "skip linklocal interface address\n",
+ inet6_n2p(&ifa->ifa_addr), ifcp->ifc_name);
+#endif
+ continue;
+ }
+ if (IN6_IS_ADDR_UNSPECIFIED(&ifa->ifa_addr)) {
+#if 0
+ trace(1, "route: %s: skip unspec interface address\n",
+ ifcp->ifc_name);
+#endif
+ continue;
+ }
+ if (ifcp->ifc_flags & IFF_UP) {
+ if ((rrt = MALLOC(struct riprt)) == NULL)
+ fatal("malloc: struct riprt");
+ memset(rrt, 0, sizeof(*rrt));
+ rrt->rrt_same = NULL;
+ rrt->rrt_index = ifcp->ifc_index;
+ rrt->rrt_t = 0; /* don't age */
+ rrt->rrt_info.rip6_dest = ifa->ifa_addr;
+ rrt->rrt_info.rip6_tag = htons(routetag & 0xffff);
+ rrt->rrt_info.rip6_metric = 1 + ifcp->ifc_metric;
+ rrt->rrt_info.rip6_plen = ifa->ifa_plen;
+ rrt->rrt_flags = RTF_CLONING;
+ rrt->rrt_rflags |= RRTF_CHANGED;
+ applyplen(&rrt->rrt_info.rip6_dest, ifa->ifa_plen);
+ memset(&rrt->rrt_gw, 0, sizeof(struct in6_addr));
+#if 0
+ /* XXX why gateway address == network adddress? */
+ rrt->rrt_gw = ifa->ifa_addr;
+#endif
+ np = &rrt->rrt_info;
+ search_rrt = rtsearch(np, &prev_rrt);
+ if (search_rrt != NULL) {
+ if (search_rrt->rrt_info.rip6_metric >
+ rrt->rrt_info.rip6_metric) {
+ if (prev_rrt)
+ prev_rrt->rrt_next = rrt->rrt_next;
+ else
+ riprt = rrt->rrt_next;
+ delroute(&rrt->rrt_info, &rrt->rrt_gw);
+ free(rrt);
+ } else {
+ /* Already have better route */
+ if (!again) {
+ trace(1, "route: %s/%d: "
+ "already registered (%s)\n",
+ inet6_n2p(&np->rip6_dest), np->rip6_plen,
+ ifcp->ifc_name);
+ }
+ free(rrt);
+ continue;
+ }
+ }
+ /* Attach the route to the list */
+ trace(1, "route: %s/%d: register route (%s)\n",
+ inet6_n2p(&np->rip6_dest), np->rip6_plen,
+ ifcp->ifc_name);
+ rrt->rrt_next = riprt;
+ riprt = rrt;
+ addroute(rrt, &rrt->rrt_gw, ifcp);
+ sendrequest(ifcp);
+ ripsend(ifcp, &ifcp->ifc_ripsin, 0);
+ need_trigger = 1;
+ } else {
+ for (loop_rrt = riprt; loop_rrt; loop_rrt = loop_rrt->rrt_next) {
+ if (loop_rrt->rrt_index == ifcp->ifc_index) {
+ t_lifetime = time(NULL) - RIP_LIFETIME;
+ if (loop_rrt->rrt_t == 0 || loop_rrt->rrt_t > t_lifetime) {
+ loop_rrt->rrt_t = t_lifetime;
+ loop_rrt->rrt_info.rip6_metric = HOPCNT_INFINITY6;
+ loop_rrt->rrt_rflags |= RRTF_CHANGED;
+ need_trigger = 1;
+ }
+ }
+ }
+ }
+ }
+ return need_trigger;
+}
+
+/*
+ * there are couple of p2p interface routing models. "behavior" lets
+ * you pick one. it looks that gated behavior fits best with BSDs,
+ * since BSD kernels does not look at prefix length on p2p interfaces.
+ */
+void
+ifrt_p2p(ifcp, again)
+ struct ifc *ifcp;
+ int again;
+{
+ struct ifac *ifa;
+ struct riprt *rrt, *orrt, *prevrrt;
+ struct netinfo6 *np;
+ struct in6_addr addr, dest;
+ int advert, ignore, i;
+#define P2PADVERT_NETWORK 1
+#define P2PADVERT_ADDR 2
+#define P2PADVERT_DEST 4
+#define P2PADVERT_MAX 4
+ const enum { CISCO, GATED, ROUTE6D } behavior = GATED;
+ const char *category = "";
+ const char *noadv;
+
+ for (ifa = ifcp->ifc_addr; ifa; ifa = ifa->ifa_next) {
+ addr = ifa->ifa_addr;
+ dest = ifa->ifa_raddr;
+ applyplen(&addr, ifa->ifa_plen);
+ applyplen(&dest, ifa->ifa_plen);
+ advert = ignore = 0;
+ switch (behavior) {
+ case CISCO:
+ /*
+ * honor addr/plen, just like normal shared medium
+ * interface. this may cause trouble if you reuse
+ * addr/plen on other interfaces.
+ *
+ * advertise addr/plen.
+ */
+ advert |= P2PADVERT_NETWORK;
+ break;
+ case GATED:
+ /*
+ * prefixlen on p2p interface is meaningless.
+ * advertise addr/128 and dest/128.
+ *
+ * do not install network route to route6d routing
+ * table (if we do, it would prevent route installation
+ * for other p2p interface that shares addr/plen).
+ *
+ * XXX what should we do if dest is ::? it will not
+ * get announced anyways (see following filter),
+ * but we need to think.
+ */
+ advert |= P2PADVERT_ADDR;
+ advert |= P2PADVERT_DEST;
+ ignore |= P2PADVERT_NETWORK;
+ break;
+ case ROUTE6D:
+ /*
+ * just for testing. actually the code is redundant
+ * given the current p2p interface address assignment
+ * rule for kame kernel.
+ *
+ * intent:
+ * A/n -> announce A/n
+ * A B/n, A and B share prefix -> A/n (= B/n)
+ * A B/n, do not share prefix -> A/128 and B/128
+ * actually, A/64 and A B/128 are the only cases
+ * permitted by the kernel:
+ * A/64 -> A/64
+ * A B/128 -> A/128 and B/128
+ */
+ if (!IN6_IS_ADDR_UNSPECIFIED(&ifa->ifa_raddr)) {
+ if (IN6_ARE_ADDR_EQUAL(&addr, &dest))
+ advert |= P2PADVERT_NETWORK;
+ else {
+ advert |= P2PADVERT_ADDR;
+ advert |= P2PADVERT_DEST;
+ ignore |= P2PADVERT_NETWORK;
+ }
+ } else
+ advert |= P2PADVERT_NETWORK;
+ break;
+ }
+
+ for (i = 1; i <= P2PADVERT_MAX; i *= 2) {
+ if ((ignore & i) != 0)
+ continue;
+ if ((rrt = MALLOC(struct riprt)) == NULL) {
+ fatal("malloc: struct riprt");
+ /*NOTREACHED*/
+ }
+ memset(rrt, 0, sizeof(*rrt));
+ rrt->rrt_same = NULL;
+ rrt->rrt_index = ifcp->ifc_index;
+ rrt->rrt_t = 0; /* don't age */
+ switch (i) {
+ case P2PADVERT_NETWORK:
+ rrt->rrt_info.rip6_dest = ifa->ifa_addr;
+ rrt->rrt_info.rip6_plen = ifa->ifa_plen;
+ applyplen(&rrt->rrt_info.rip6_dest,
+ ifa->ifa_plen);
+ category = "network";
+ break;
+ case P2PADVERT_ADDR:
+ rrt->rrt_info.rip6_dest = ifa->ifa_addr;
+ rrt->rrt_info.rip6_plen = 128;
+ rrt->rrt_gw = in6addr_loopback;
+ category = "addr";
+ break;
+ case P2PADVERT_DEST:
+ rrt->rrt_info.rip6_dest = ifa->ifa_raddr;
+ rrt->rrt_info.rip6_plen = 128;
+ rrt->rrt_gw = ifa->ifa_addr;
+ category = "dest";
+ break;
+ }
+ if (IN6_IS_ADDR_UNSPECIFIED(&rrt->rrt_info.rip6_dest) ||
+ IN6_IS_ADDR_LINKLOCAL(&rrt->rrt_info.rip6_dest)) {
+#if 0
+ trace(1, "route: %s: skip unspec/linklocal "
+ "(%s on %s)\n", category, ifcp->ifc_name);
+#endif
+ free(rrt);
+ continue;
+ }
+ if ((advert & i) == 0) {
+ rrt->rrt_rflags |= RRTF_NOADVERTISE;
+ noadv = ", NO-ADV";
+ } else
+ noadv = "";
+ rrt->rrt_info.rip6_tag = htons(routetag & 0xffff);
+ rrt->rrt_info.rip6_metric = 1 + ifcp->ifc_metric;
+ np = &rrt->rrt_info;
+ orrt = rtsearch(np, &prevrrt);
+ if (!orrt) {
+ /* Attach the route to the list */
+ trace(1, "route: %s/%d: register route "
+ "(%s on %s%s)\n",
+ inet6_n2p(&np->rip6_dest), np->rip6_plen,
+ category, ifcp->ifc_name, noadv);
+ rrt->rrt_next = riprt;
+ riprt = rrt;
+ } else if (rrt->rrt_index != orrt->rrt_index ||
+ rrt->rrt_info.rip6_metric != orrt->rrt_info.rip6_metric) {
+ /* swap route */
+ rrt->rrt_next = orrt->rrt_next;
+ if (prevrrt)
+ prevrrt->rrt_next = rrt;
+ else
+ riprt = rrt;
+ free(orrt);
+
+ trace(1, "route: %s/%d: update (%s on %s%s)\n",
+ inet6_n2p(&np->rip6_dest), np->rip6_plen,
+ category, ifcp->ifc_name, noadv);
+ } else {
+ /* Already found */
+ if (!again) {
+ trace(1, "route: %s/%d: "
+ "already registered (%s on %s%s)\n",
+ inet6_n2p(&np->rip6_dest),
+ np->rip6_plen, category,
+ ifcp->ifc_name, noadv);
+ }
+ free(rrt);
+ }
+ }
+ }
+#undef P2PADVERT_NETWORK
+#undef P2PADVERT_ADDR
+#undef P2PADVERT_DEST
+#undef P2PADVERT_MAX
+}
+
+int
+getifmtu(ifindex)
+ int ifindex;
+{
+ int mib[6];
+ char *buf;
+ size_t msize;
+ struct if_msghdr *ifm;
+ int mtu;
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = AF_INET6;
+ mib[4] = NET_RT_IFLIST;
+ mib[5] = ifindex;
+ if (sysctl(mib, 6, NULL, &msize, NULL, 0) < 0) {
+ fatal("sysctl estimate NET_RT_IFLIST");
+ /*NOTREACHED*/
+ }
+ if ((buf = malloc(msize)) == NULL) {
+ fatal("malloc");
+ /*NOTREACHED*/
+ }
+ if (sysctl(mib, 6, buf, &msize, NULL, 0) < 0) {
+ fatal("sysctl NET_RT_IFLIST");
+ /*NOTREACHED*/
+ }
+ ifm = (struct if_msghdr *)buf;
+ mtu = ifm->ifm_data.ifi_mtu;
+#ifdef __FreeBSD__
+ if (ifindex != ifm->ifm_index) {
+ fatal("ifindex does not match with ifm_index");
+ /*NOTREACHED*/
+ }
+#endif
+ free(buf);
+ return mtu;
+}
+
+const char *
+rttypes(rtm)
+ struct rt_msghdr *rtm;
+{
+#define RTTYPE(s, f) \
+do { \
+ if (rtm->rtm_type == (f)) \
+ return (s); \
+} while (0)
+ RTTYPE("ADD", RTM_ADD);
+ RTTYPE("DELETE", RTM_DELETE);
+ RTTYPE("CHANGE", RTM_CHANGE);
+ RTTYPE("GET", RTM_GET);
+ RTTYPE("LOSING", RTM_LOSING);
+ RTTYPE("REDIRECT", RTM_REDIRECT);
+ RTTYPE("MISS", RTM_MISS);
+ RTTYPE("LOCK", RTM_LOCK);
+ RTTYPE("OLDADD", RTM_OLDADD);
+ RTTYPE("OLDDEL", RTM_OLDDEL);
+ RTTYPE("RESOLVE", RTM_RESOLVE);
+ RTTYPE("NEWADDR", RTM_NEWADDR);
+ RTTYPE("DELADDR", RTM_DELADDR);
+ RTTYPE("IFINFO", RTM_IFINFO);
+#ifdef RTM_OLDADD
+ RTTYPE("OLDADD", RTM_OLDADD);
+#endif
+#ifdef RTM_OLDDEL
+ RTTYPE("OLDDEL", RTM_OLDDEL);
+#endif
+#ifdef RTM_OIFINFO
+ RTTYPE("OIFINFO", RTM_OIFINFO);
+#endif
+#ifdef RTM_IFANNOUNCE
+ RTTYPE("IFANNOUNCE", RTM_IFANNOUNCE);
+#endif
+#ifdef RTM_NEWMADDR
+ RTTYPE("NEWMADDR", RTM_NEWMADDR);
+#endif
+#ifdef RTM_DELMADDR
+ RTTYPE("DELMADDR", RTM_DELMADDR);
+#endif
+#undef RTTYPE
+ return NULL;
+}
+
+const char *
+rtflags(rtm)
+ struct rt_msghdr *rtm;
+{
+ static char buf[BUFSIZ];
+
+ /*
+ * letter conflict should be okay. painful when *BSD diverges...
+ */
+ strlcpy(buf, "", sizeof(buf));
+#define RTFLAG(s, f) \
+do { \
+ if (rtm->rtm_flags & (f)) \
+ strlcat(buf, (s), sizeof(buf)); \
+} while (0)
+ RTFLAG("U", RTF_UP);
+ RTFLAG("G", RTF_GATEWAY);
+ RTFLAG("H", RTF_HOST);
+ RTFLAG("R", RTF_REJECT);
+ RTFLAG("D", RTF_DYNAMIC);
+ RTFLAG("M", RTF_MODIFIED);
+ RTFLAG("d", RTF_DONE);
+#ifdef RTF_MASK
+ RTFLAG("m", RTF_MASK);
+#endif
+ RTFLAG("C", RTF_CLONING);
+#ifdef RTF_CLONED
+ RTFLAG("c", RTF_CLONED);
+#endif
+#ifdef RTF_PRCLONING
+ RTFLAG("c", RTF_PRCLONING);
+#endif
+#ifdef RTF_WASCLONED
+ RTFLAG("W", RTF_WASCLONED);
+#endif
+ RTFLAG("X", RTF_XRESOLVE);
+ RTFLAG("L", RTF_LLINFO);
+ RTFLAG("S", RTF_STATIC);
+ RTFLAG("B", RTF_BLACKHOLE);
+#ifdef RTF_PROTO3
+ RTFLAG("3", RTF_PROTO3);
+#endif
+ RTFLAG("2", RTF_PROTO2);
+ RTFLAG("1", RTF_PROTO1);
+#ifdef RTF_BROADCAST
+ RTFLAG("b", RTF_BROADCAST);
+#endif
+#ifdef RTF_DEFAULT
+ RTFLAG("d", RTF_DEFAULT);
+#endif
+#ifdef RTF_ISAROUTER
+ RTFLAG("r", RTF_ISAROUTER);
+#endif
+#ifdef RTF_TUNNEL
+ RTFLAG("T", RTF_TUNNEL);
+#endif
+#ifdef RTF_AUTH
+ RTFLAG("A", RTF_AUTH);
+#endif
+#ifdef RTF_CRYPT
+ RTFLAG("E", RTF_CRYPT);
+#endif
+#undef RTFLAG
+ return buf;
+}
+
+const char *
+ifflags(flags)
+ int flags;
+{
+ static char buf[BUFSIZ];
+
+ strlcpy(buf, "", sizeof(buf));
+#define IFFLAG(s, f) \
+do { \
+ if (flags & f) { \
+ if (buf[0]) \
+ strlcat(buf, ",", sizeof(buf)); \
+ strlcat(buf, s, sizeof(buf)); \
+ } \
+} while (0)
+ IFFLAG("UP", IFF_UP);
+ IFFLAG("BROADCAST", IFF_BROADCAST);
+ IFFLAG("DEBUG", IFF_DEBUG);
+ IFFLAG("LOOPBACK", IFF_LOOPBACK);
+ IFFLAG("POINTOPOINT", IFF_POINTOPOINT);
+#ifdef IFF_NOTRAILERS
+ IFFLAG("NOTRAILERS", IFF_NOTRAILERS);
+#endif
+#ifdef IFF_SMART
+ IFFLAG("SMART", IFF_SMART);
+#endif
+ IFFLAG("RUNNING", IFF_RUNNING);
+ IFFLAG("NOARP", IFF_NOARP);
+ IFFLAG("PROMISC", IFF_PROMISC);
+ IFFLAG("ALLMULTI", IFF_ALLMULTI);
+ IFFLAG("OACTIVE", IFF_OACTIVE);
+ IFFLAG("SIMPLEX", IFF_SIMPLEX);
+ IFFLAG("LINK0", IFF_LINK0);
+ IFFLAG("LINK1", IFF_LINK1);
+ IFFLAG("LINK2", IFF_LINK2);
+ IFFLAG("MULTICAST", IFF_MULTICAST);
+#undef IFFLAG
+ return buf;
+}
+
+void
+krtread(again)
+ int again;
+{
+ int mib[6];
+ size_t msize;
+ char *buf, *p, *lim;
+ struct rt_msghdr *rtm;
+ int retry;
+ const char *errmsg;
+
+ retry = 0;
+ buf = NULL;
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = AF_INET6; /* Address family */
+ mib[4] = NET_RT_DUMP; /* Dump the kernel routing table */
+ mib[5] = 0; /* No flags */
+ do {
+ retry++;
+ errmsg = NULL;
+ if (buf)
+ free(buf);
+ if (sysctl(mib, 6, NULL, &msize, NULL, 0) < 0) {
+ errmsg = "sysctl estimate";
+ continue;
+ }
+ if ((buf = malloc(msize)) == NULL) {
+ errmsg = "malloc";
+ continue;
+ }
+ if (sysctl(mib, 6, buf, &msize, NULL, 0) < 0) {
+ errmsg = "sysctl NET_RT_DUMP";
+ continue;
+ }
+ } while (retry < 5 && errmsg != NULL);
+ if (errmsg) {
+ fatal("%s (with %d retries, msize=%lu)", errmsg, retry,
+ (u_long)msize);
+ /*NOTREACHED*/
+ } else if (1 < retry)
+ syslog(LOG_INFO, "NET_RT_DUMP %d retires", retry);
+
+ lim = buf + msize;
+ for (p = buf; p < lim; p += rtm->rtm_msglen) {
+ rtm = (struct rt_msghdr *)p;
+ rt_entry(rtm, again);
+ }
+ free(buf);
+}
+
+void
+rt_entry(rtm, again)
+ struct rt_msghdr *rtm;
+ int again;
+{
+ struct sockaddr_in6 *sin6_dst, *sin6_gw, *sin6_mask;
+ struct sockaddr_in6 *sin6_genmask, *sin6_ifp;
+ char *rtmp, *ifname = NULL;
+ struct riprt *rrt, *orrt;
+ struct netinfo6 *np;
+ int s;
+
+ sin6_dst = sin6_gw = sin6_mask = sin6_genmask = sin6_ifp = 0;
+ if ((rtm->rtm_flags & RTF_UP) == 0 || rtm->rtm_flags &
+ (RTF_CLONING|RTF_XRESOLVE|RTF_LLINFO|RTF_BLACKHOLE)) {
+ return; /* not interested in the link route */
+ }
+ /* do not look at cloned routes */
+#ifdef RTF_WASCLONED
+ if (rtm->rtm_flags & RTF_WASCLONED)
+ return;
+#endif
+#ifdef RTF_CLONED
+ if (rtm->rtm_flags & RTF_CLONED)
+ return;
+#endif
+ /*
+ * do not look at dynamic routes.
+ * netbsd/openbsd cloned routes have UGHD.
+ */
+ if (rtm->rtm_flags & RTF_DYNAMIC)
+ return;
+ rtmp = (char *)(rtm + 1);
+ /* Destination */
+ if ((rtm->rtm_addrs & RTA_DST) == 0)
+ return; /* ignore routes without destination address */
+ sin6_dst = (struct sockaddr_in6 *)rtmp;
+ rtmp += ROUNDUP(sin6_dst->sin6_len);
+ if (rtm->rtm_addrs & RTA_GATEWAY) {
+ sin6_gw = (struct sockaddr_in6 *)rtmp;
+ rtmp += ROUNDUP(sin6_gw->sin6_len);
+ }
+ if (rtm->rtm_addrs & RTA_NETMASK) {
+ sin6_mask = (struct sockaddr_in6 *)rtmp;
+ rtmp += ROUNDUP(sin6_mask->sin6_len);
+ }
+ if (rtm->rtm_addrs & RTA_GENMASK) {
+ sin6_genmask = (struct sockaddr_in6 *)rtmp;
+ rtmp += ROUNDUP(sin6_genmask->sin6_len);
+ }
+ if (rtm->rtm_addrs & RTA_IFP) {
+ sin6_ifp = (struct sockaddr_in6 *)rtmp;
+ rtmp += ROUNDUP(sin6_ifp->sin6_len);
+ }
+
+ /* Destination */
+ if (sin6_dst->sin6_family != AF_INET6)
+ return;
+ if (IN6_IS_ADDR_LINKLOCAL(&sin6_dst->sin6_addr))
+ return; /* Link-local */
+ if (IN6_ARE_ADDR_EQUAL(&sin6_dst->sin6_addr, &in6addr_loopback))
+ return; /* Loopback */
+ if (IN6_IS_ADDR_MULTICAST(&sin6_dst->sin6_addr))
+ return;
+
+ if ((rrt = MALLOC(struct riprt)) == NULL) {
+ fatal("malloc: struct riprt");
+ /*NOTREACHED*/
+ }
+ memset(rrt, 0, sizeof(*rrt));
+ np = &rrt->rrt_info;
+ rrt->rrt_same = NULL;
+ rrt->rrt_t = time(NULL);
+ if (aflag == 0 && (rtm->rtm_flags & RTF_STATIC))
+ rrt->rrt_t = 0; /* Don't age static routes */
+#if 0
+ np->rip6_tag = htons(routetag & 0xffff);
+#else
+ np->rip6_tag = 0;
+#endif
+ np->rip6_metric = rtm->rtm_rmx.rmx_hopcount;
+ if (np->rip6_metric < 1)
+ np->rip6_metric = 1;
+ rrt->rrt_flags = rtm->rtm_flags;
+ np->rip6_dest = sin6_dst->sin6_addr;
+
+ /* Mask or plen */
+ if (rtm->rtm_flags & RTF_HOST)
+ np->rip6_plen = 128; /* Host route */
+ else if (sin6_mask)
+ np->rip6_plen = sin6mask2len(sin6_mask);
+ else
+ np->rip6_plen = 0;
+
+ orrt = rtsearch(np, NULL);
+ if (orrt && orrt->rrt_info.rip6_metric != HOPCNT_INFINITY6) {
+ /* Already found */
+ if (!again) {
+ trace(1, "route: %s/%d flags %s: already registered\n",
+ inet6_n2p(&np->rip6_dest), np->rip6_plen,
+ rtflags(rtm));
+ }
+ free(rrt);
+ return;
+ }
+ /* Gateway */
+ if (!sin6_gw)
+ memset(&rrt->rrt_gw, 0, sizeof(struct in6_addr));
+ else {
+ if (sin6_gw->sin6_family == AF_INET6)
+ rrt->rrt_gw = sin6_gw->sin6_addr;
+ else if (sin6_gw->sin6_family == AF_LINK) {
+ /* XXX in case ppp link? */
+ rrt->rrt_gw = in6addr_loopback;
+ } else
+ memset(&rrt->rrt_gw, 0, sizeof(struct in6_addr));
+ }
+ trace(1, "route: %s/%d flags %s",
+ inet6_n2p(&np->rip6_dest), np->rip6_plen, rtflags(rtm));
+ trace(1, " gw %s", inet6_n2p(&rrt->rrt_gw));
+
+ /* Interface */
+ s = rtm->rtm_index;
+ if (s < nindex2ifc && index2ifc[s])
+ ifname = index2ifc[s]->ifc_name;
+ else {
+ trace(1, " not configured\n");
+ free(rrt);
+ return;
+ }
+ trace(1, " if %s sock %d", ifname, s);
+ rrt->rrt_index = s;
+
+ trace(1, "\n");
+
+ /* Check gateway */
+ if (!IN6_IS_ADDR_LINKLOCAL(&rrt->rrt_gw) &&
+ !IN6_IS_ADDR_LOOPBACK(&rrt->rrt_gw)
+#ifdef __FreeBSD__
+ && (rrt->rrt_flags & RTF_LOCAL) == 0
+#endif
+ ) {
+ trace(0, "***** Gateway %s is not a link-local address.\n",
+ inet6_n2p(&rrt->rrt_gw));
+ trace(0, "***** dest(%s) if(%s) -- Not optimized.\n",
+ inet6_n2p(&rrt->rrt_info.rip6_dest), ifname);
+ rrt->rrt_rflags |= RRTF_NH_NOT_LLADDR;
+ }
+
+ /* Put it to the route list */
+ if (orrt && orrt->rrt_info.rip6_metric == HOPCNT_INFINITY6) {
+ /* replace route list */
+ rrt->rrt_next = orrt->rrt_next;
+ *orrt = *rrt;
+ trace(1, "route: %s/%d flags %s: replace new route\n",
+ inet6_n2p(&np->rip6_dest), np->rip6_plen,
+ rtflags(rtm));
+ free(rrt);
+ } else {
+ rrt->rrt_next = riprt;
+ riprt = rrt;
+ }
+}
+
+int
+addroute(rrt, gw, ifcp)
+ struct riprt *rrt;
+ const struct in6_addr *gw;
+ struct ifc *ifcp;
+{
+ struct netinfo6 *np;
+ u_char buf[BUFSIZ], buf1[BUFSIZ], buf2[BUFSIZ];
+ struct rt_msghdr *rtm;
+ struct sockaddr_in6 *sin;
+ int len;
+
+ np = &rrt->rrt_info;
+ inet_ntop(AF_INET6, (const void *)gw, (char *)buf1, sizeof(buf1));
+ inet_ntop(AF_INET6, (void *)&ifcp->ifc_mylladdr, (char *)buf2, sizeof(buf2));
+ tracet(1, "ADD: %s/%d gw %s [%d] ifa %s\n",
+ inet6_n2p(&np->rip6_dest), np->rip6_plen, buf1,
+ np->rip6_metric - 1, buf2);
+ if (rtlog)
+ fprintf(rtlog, "%s: ADD: %s/%d gw %s [%d] ifa %s\n", hms(),
+ inet6_n2p(&np->rip6_dest), np->rip6_plen, buf1,
+ np->rip6_metric - 1, buf2);
+ if (nflag)
+ return 0;
+
+ memset(buf, 0, sizeof(buf));
+ rtm = (struct rt_msghdr *)buf;
+ rtm->rtm_type = RTM_ADD;
+ rtm->rtm_version = RTM_VERSION;
+ rtm->rtm_seq = ++seq;
+ rtm->rtm_pid = pid;
+ rtm->rtm_flags = rrt->rrt_flags;
+ rtm->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
+ rtm->rtm_rmx.rmx_hopcount = np->rip6_metric - 1;
+ rtm->rtm_inits = RTV_HOPCOUNT;
+ sin = (struct sockaddr_in6 *)&buf[sizeof(struct rt_msghdr)];
+ /* Destination */
+ sin->sin6_len = sizeof(struct sockaddr_in6);
+ sin->sin6_family = AF_INET6;
+ sin->sin6_addr = np->rip6_dest;
+ sin = (struct sockaddr_in6 *)((char *)sin + ROUNDUP(sin->sin6_len));
+ /* Gateway */
+ sin->sin6_len = sizeof(struct sockaddr_in6);
+ sin->sin6_family = AF_INET6;
+ sin->sin6_addr = *gw;
+ sin = (struct sockaddr_in6 *)((char *)sin + ROUNDUP(sin->sin6_len));
+ /* Netmask */
+ sin->sin6_len = sizeof(struct sockaddr_in6);
+ sin->sin6_family = AF_INET6;
+ sin->sin6_addr = *(plen2mask(np->rip6_plen));
+ sin = (struct sockaddr_in6 *)((char *)sin + ROUNDUP(sin->sin6_len));
+
+ len = (char *)sin - (char *)buf;
+ rtm->rtm_msglen = len;
+ if (write(rtsock, buf, len) > 0)
+ return 0;
+
+ if (errno == EEXIST) {
+ trace(0, "ADD: Route already exists %s/%d gw %s\n",
+ inet6_n2p(&np->rip6_dest), np->rip6_plen, buf1);
+ if (rtlog)
+ fprintf(rtlog, "ADD: Route already exists %s/%d gw %s\n",
+ inet6_n2p(&np->rip6_dest), np->rip6_plen, buf1);
+ } else {
+ trace(0, "Can not write to rtsock (addroute): %s\n",
+ strerror(errno));
+ if (rtlog)
+ fprintf(rtlog, "\tCan not write to rtsock: %s\n",
+ strerror(errno));
+ }
+ return -1;
+}
+
+int
+delroute(np, gw)
+ struct netinfo6 *np;
+ struct in6_addr *gw;
+{
+ u_char buf[BUFSIZ], buf2[BUFSIZ];
+ struct rt_msghdr *rtm;
+ struct sockaddr_in6 *sin;
+ int len;
+
+ inet_ntop(AF_INET6, (void *)gw, (char *)buf2, sizeof(buf2));
+ tracet(1, "DEL: %s/%d gw %s\n", inet6_n2p(&np->rip6_dest),
+ np->rip6_plen, buf2);
+ if (rtlog)
+ fprintf(rtlog, "%s: DEL: %s/%d gw %s\n",
+ hms(), inet6_n2p(&np->rip6_dest), np->rip6_plen, buf2);
+ if (nflag)
+ return 0;
+
+ memset(buf, 0, sizeof(buf));
+ rtm = (struct rt_msghdr *)buf;
+ rtm->rtm_type = RTM_DELETE;
+ rtm->rtm_version = RTM_VERSION;
+ rtm->rtm_seq = ++seq;
+ rtm->rtm_pid = pid;
+ rtm->rtm_flags = RTF_UP | RTF_GATEWAY;
+ if (np->rip6_plen == sizeof(struct in6_addr) * 8)
+ rtm->rtm_flags |= RTF_HOST;
+ rtm->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
+ sin = (struct sockaddr_in6 *)&buf[sizeof(struct rt_msghdr)];
+ /* Destination */
+ sin->sin6_len = sizeof(struct sockaddr_in6);
+ sin->sin6_family = AF_INET6;
+ sin->sin6_addr = np->rip6_dest;
+ sin = (struct sockaddr_in6 *)((char *)sin + ROUNDUP(sin->sin6_len));
+ /* Gateway */
+ sin->sin6_len = sizeof(struct sockaddr_in6);
+ sin->sin6_family = AF_INET6;
+ sin->sin6_addr = *gw;
+ sin = (struct sockaddr_in6 *)((char *)sin + ROUNDUP(sin->sin6_len));
+ /* Netmask */
+ sin->sin6_len = sizeof(struct sockaddr_in6);
+ sin->sin6_family = AF_INET6;
+ sin->sin6_addr = *(plen2mask(np->rip6_plen));
+ sin = (struct sockaddr_in6 *)((char *)sin + ROUNDUP(sin->sin6_len));
+
+ len = (char *)sin - (char *)buf;
+ rtm->rtm_msglen = len;
+ if (write(rtsock, buf, len) >= 0)
+ return 0;
+
+ if (errno == ESRCH) {
+ trace(0, "RTDEL: Route does not exist: %s/%d gw %s\n",
+ inet6_n2p(&np->rip6_dest), np->rip6_plen, buf2);
+ if (rtlog)
+ fprintf(rtlog, "RTDEL: Route does not exist: %s/%d gw %s\n",
+ inet6_n2p(&np->rip6_dest), np->rip6_plen, buf2);
+ } else {
+ trace(0, "Can not write to rtsock (delroute): %s\n",
+ strerror(errno));
+ if (rtlog)
+ fprintf(rtlog, "\tCan not write to rtsock: %s\n",
+ strerror(errno));
+ }
+ return -1;
+}
+
+struct in6_addr *
+getroute(np, gw)
+ struct netinfo6 *np;
+ struct in6_addr *gw;
+{
+ u_char buf[BUFSIZ];
+ u_long myseq;
+ int len;
+ struct rt_msghdr *rtm;
+ struct sockaddr_in6 *sin;
+
+ rtm = (struct rt_msghdr *)buf;
+ len = sizeof(struct rt_msghdr) + sizeof(struct sockaddr_in6);
+ memset(rtm, 0, len);
+ rtm->rtm_type = RTM_GET;
+ rtm->rtm_version = RTM_VERSION;
+ myseq = ++seq;
+ rtm->rtm_seq = myseq;
+ rtm->rtm_addrs = RTA_DST;
+ rtm->rtm_msglen = len;
+ sin = (struct sockaddr_in6 *)&buf[sizeof(struct rt_msghdr)];
+ sin->sin6_len = sizeof(struct sockaddr_in6);
+ sin->sin6_family = AF_INET6;
+ sin->sin6_addr = np->rip6_dest;
+ if (write(rtsock, buf, len) < 0) {
+ if (errno == ESRCH) /* No such route found */
+ return NULL;
+ perror("write to rtsock");
+ exit(1);
+ }
+ do {
+ if ((len = read(rtsock, buf, sizeof(buf))) < 0) {
+ perror("read from rtsock");
+ exit(1);
+ }
+ rtm = (struct rt_msghdr *)buf;
+ } while (rtm->rtm_seq != myseq || rtm->rtm_pid != pid);
+ sin = (struct sockaddr_in6 *)&buf[sizeof(struct rt_msghdr)];
+ if (rtm->rtm_addrs & RTA_DST) {
+ sin = (struct sockaddr_in6 *)
+ ((char *)sin + ROUNDUP(sin->sin6_len));
+ }
+ if (rtm->rtm_addrs & RTA_GATEWAY) {
+ *gw = sin->sin6_addr;
+ return gw;
+ }
+ return NULL;
+}
+
+const char *
+inet6_n2p(p)
+ const struct in6_addr *p;
+{
+ static char buf[BUFSIZ];
+
+ return inet_ntop(AF_INET6, (const void *)p, buf, sizeof(buf));
+}
+
+void
+ifrtdump(sig)
+ int sig;
+{
+
+ ifdump(sig);
+ rtdump(sig);
+}
+
+void
+ifdump(sig)
+ int sig;
+{
+ struct ifc *ifcp;
+ FILE *dump;
+ int i;
+
+ if (sig == 0)
+ dump = stderr;
+ else
+ if ((dump = fopen(ROUTE6D_DUMP, "a")) == NULL)
+ dump = stderr;
+
+ fprintf(dump, "%s: Interface Table Dump\n", hms());
+ fprintf(dump, " Number of interfaces: %d\n", nifc);
+ for (i = 0; i < 2; i++) {
+ fprintf(dump, " %sadvertising interfaces:\n", i ? "non-" : "");
+ for (ifcp = ifc; ifcp; ifcp = ifcp->ifc_next) {
+ if (i == 0) {
+ if ((ifcp->ifc_flags & IFF_UP) == 0)
+ continue;
+ if (iff_find(ifcp, 'N') != NULL)
+ continue;
+ } else {
+ if (ifcp->ifc_flags & IFF_UP)
+ continue;
+ }
+ ifdump0(dump, ifcp);
+ }
+ }
+ fprintf(dump, "\n");
+ if (dump != stderr)
+ fclose(dump);
+}
+
+void
+ifdump0(dump, ifcp)
+ FILE *dump;
+ const struct ifc *ifcp;
+{
+ struct ifac *ifa;
+ struct iff *iffp;
+ char buf[BUFSIZ];
+ const char *ft;
+ int addr;
+
+ fprintf(dump, " %s: index(%d) flags(%s) addr(%s) mtu(%d) metric(%d)\n",
+ ifcp->ifc_name, ifcp->ifc_index, ifflags(ifcp->ifc_flags),
+ inet6_n2p(&ifcp->ifc_mylladdr),
+ ifcp->ifc_mtu, ifcp->ifc_metric);
+ for (ifa = ifcp->ifc_addr; ifa; ifa = ifa->ifa_next) {
+ if (ifcp->ifc_flags & IFF_POINTOPOINT) {
+ inet_ntop(AF_INET6, (void *)&ifa->ifa_raddr,
+ buf, sizeof(buf));
+ fprintf(dump, "\t%s/%d -- %s\n",
+ inet6_n2p(&ifa->ifa_addr),
+ ifa->ifa_plen, buf);
+ } else {
+ fprintf(dump, "\t%s/%d\n",
+ inet6_n2p(&ifa->ifa_addr),
+ ifa->ifa_plen);
+ }
+ }
+ if (ifcp->ifc_filter) {
+ fprintf(dump, "\tFilter:");
+ for (iffp = ifcp->ifc_filter; iffp; iffp = iffp->iff_next) {
+ addr = 0;
+ switch (iffp->iff_type) {
+ case 'A':
+ ft = "Aggregate"; addr++; break;
+ case 'N':
+ ft = "No-use"; break;
+ case 'O':
+ ft = "Advertise-only"; addr++; break;
+ case 'T':
+ ft = "Default-only"; break;
+ case 'L':
+ ft = "Listen-only"; addr++; break;
+ default:
+ snprintf(buf, sizeof(buf), "Unknown-%c", iffp->iff_type);
+ ft = buf;
+ addr++;
+ break;
+ }
+ fprintf(dump, " %s", ft);
+ if (addr) {
+ fprintf(dump, "(%s/%d)", inet6_n2p(&iffp->iff_addr),
+ iffp->iff_plen);
+ }
+ }
+ fprintf(dump, "\n");
+ }
+}
+
+void
+rtdump(sig)
+ int sig;
+{
+ struct riprt *rrt;
+ char buf[BUFSIZ];
+ FILE *dump;
+ time_t t, age;
+
+ if (sig == 0)
+ dump = stderr;
+ else
+ if ((dump = fopen(ROUTE6D_DUMP, "a")) == NULL)
+ dump = stderr;
+
+ t = time(NULL);
+ fprintf(dump, "\n%s: Routing Table Dump\n", hms());
+ for (rrt = riprt; rrt; rrt = rrt->rrt_next) {
+ if (rrt->rrt_t == 0)
+ age = 0;
+ else
+ age = t - rrt->rrt_t;
+ inet_ntop(AF_INET6, (void *)&rrt->rrt_info.rip6_dest,
+ buf, sizeof(buf));
+ fprintf(dump, " %s/%d if(%d:%s) gw(%s) [%d] age(%ld)",
+ buf, rrt->rrt_info.rip6_plen, rrt->rrt_index,
+ index2ifc[rrt->rrt_index]->ifc_name,
+ inet6_n2p(&rrt->rrt_gw),
+ rrt->rrt_info.rip6_metric, (long)age);
+ if (rrt->rrt_info.rip6_tag) {
+ fprintf(dump, " tag(0x%04x)",
+ ntohs(rrt->rrt_info.rip6_tag) & 0xffff);
+ }
+ if (rrt->rrt_rflags & RRTF_NH_NOT_LLADDR)
+ fprintf(dump, " NOT-LL");
+ if (rrt->rrt_rflags & RRTF_NOADVERTISE)
+ fprintf(dump, " NO-ADV");
+ fprintf(dump, "\n");
+ }
+ fprintf(dump, "\n");
+ if (dump != stderr)
+ fclose(dump);
+}
+
+/*
+ * Parse the -A (and -O) options and put corresponding filter object to the
+ * specified interface structures. Each of the -A/O option has the following
+ * syntax: -A 5f09:c400::/32,ef0,ef1 (aggregate)
+ * -O 5f09:c400::/32,ef0,ef1 (only when match)
+ */
+void
+filterconfig()
+{
+ int i;
+ char *p, *ap, *iflp, *ifname;
+ struct iff ftmp, *iff_obj;
+ struct ifc *ifcp;
+ struct riprt *rrt;
+#if 0
+ struct in6_addr gw;
+#endif
+
+ for (i = 0; i < nfilter; i++) {
+ ap = filter[i];
+ iflp = NULL;
+ ifcp = NULL;
+ if (filtertype[i] == 'N' || filtertype[i] == 'T') {
+ iflp = ap;
+ goto ifonly;
+ }
+ if ((p = index(ap, ',')) != NULL) {
+ *p++ = '\0';
+ iflp = p;
+ }
+ if ((p = index(ap, '/')) == NULL) {
+ fatal("no prefixlen specified for '%s'", ap);
+ /*NOTREACHED*/
+ }
+ *p++ = '\0';
+ if (inet_pton(AF_INET6, ap, &ftmp.iff_addr) != 1) {
+ fatal("invalid prefix specified for '%s'", ap);
+ /*NOTREACHED*/
+ }
+ ftmp.iff_plen = atoi(p);
+ ftmp.iff_next = NULL;
+ applyplen(&ftmp.iff_addr, ftmp.iff_plen);
+ifonly:
+ ftmp.iff_type = filtertype[i];
+ if (iflp == NULL || *iflp == '\0') {
+ fatal("no interface specified for '%s'", ap);
+ /*NOTREACHED*/
+ }
+ /* parse the interface listing portion */
+ while (iflp) {
+ ifname = iflp;
+ if ((iflp = index(iflp, ',')) != NULL)
+ *iflp++ = '\0';
+ ifcp = ifc_find(ifname);
+ if (ifcp == NULL) {
+ fatal("no interface %s exists", ifname);
+ /*NOTREACHED*/
+ }
+ iff_obj = (struct iff *)malloc(sizeof(struct iff));
+ if (iff_obj == NULL) {
+ fatal("malloc of iff_obj");
+ /*NOTREACHED*/
+ }
+ memcpy((void *)iff_obj, (void *)&ftmp,
+ sizeof(struct iff));
+ /* link it to the interface filter */
+ iff_obj->iff_next = ifcp->ifc_filter;
+ ifcp->ifc_filter = iff_obj;
+ }
+
+ /*
+ * -A: aggregate configuration.
+ */
+ if (filtertype[i] != 'A')
+ continue;
+ /* put the aggregate to the kernel routing table */
+ rrt = (struct riprt *)malloc(sizeof(struct riprt));
+ if (rrt == NULL) {
+ fatal("malloc: rrt");
+ /*NOTREACHED*/
+ }
+ memset(rrt, 0, sizeof(struct riprt));
+ rrt->rrt_info.rip6_dest = ftmp.iff_addr;
+ rrt->rrt_info.rip6_plen = ftmp.iff_plen;
+ rrt->rrt_info.rip6_metric = 1;
+ rrt->rrt_info.rip6_tag = htons(routetag & 0xffff);
+ rrt->rrt_gw = in6addr_loopback;
+ rrt->rrt_flags = RTF_UP | RTF_REJECT;
+ rrt->rrt_rflags = RRTF_AGGREGATE;
+ rrt->rrt_t = 0;
+ rrt->rrt_index = loopifindex;
+#if 0
+ if (getroute(&rrt->rrt_info, &gw)) {
+#if 0
+ /*
+ * When the address has already been registered in the
+ * kernel routing table, it should be removed
+ */
+ delroute(&rrt->rrt_info, &gw);
+#else
+ /* it is safer behavior */
+ errno = EINVAL;
+ fatal("%s/%u already in routing table, "
+ "cannot aggregate",
+ inet6_n2p(&rrt->rrt_info.rip6_dest),
+ rrt->rrt_info.rip6_plen);
+ /*NOTREACHED*/
+#endif
+ }
+#endif
+ /* Put the route to the list */
+ rrt->rrt_next = riprt;
+ riprt = rrt;
+ trace(1, "Aggregate: %s/%d for %s\n",
+ inet6_n2p(&ftmp.iff_addr), ftmp.iff_plen,
+ ifcp->ifc_name);
+ /* Add this route to the kernel */
+ if (nflag) /* do not modify kernel routing table */
+ continue;
+ addroute(rrt, &in6addr_loopback, loopifcp);
+ }
+}
+
+/***************** utility functions *****************/
+
+/*
+ * Returns a pointer to ifac whose address and prefix length matches
+ * with the address and prefix length specified in the arguments.
+ */
+struct ifac *
+ifa_match(ifcp, ia, plen)
+ const struct ifc *ifcp;
+ const struct in6_addr *ia;
+ int plen;
+{
+ struct ifac *ifa;
+
+ for (ifa = ifcp->ifc_addr; ifa; ifa = ifa->ifa_next) {
+ if (IN6_ARE_ADDR_EQUAL(&ifa->ifa_addr, ia) &&
+ ifa->ifa_plen == plen)
+ break;
+ }
+ return ifa;
+}
+
+/*
+ * Return a pointer to riprt structure whose address and prefix length
+ * matches with the address and prefix length found in the argument.
+ * Note: This is not a rtalloc(). Therefore exact match is necessary.
+ */
+struct riprt *
+rtsearch(np, prev_rrt)
+ struct netinfo6 *np;
+ struct riprt **prev_rrt;
+{
+ struct riprt *rrt;
+
+ if (prev_rrt)
+ *prev_rrt = NULL;
+ for (rrt = riprt; rrt; rrt = rrt->rrt_next) {
+ if (rrt->rrt_info.rip6_plen == np->rip6_plen &&
+ IN6_ARE_ADDR_EQUAL(&rrt->rrt_info.rip6_dest,
+ &np->rip6_dest))
+ return rrt;
+ if (prev_rrt)
+ *prev_rrt = rrt;
+ }
+ if (prev_rrt)
+ *prev_rrt = NULL;
+ return 0;
+}
+
+int
+sin6mask2len(sin6)
+ const struct sockaddr_in6 *sin6;
+{
+
+ return mask2len(&sin6->sin6_addr,
+ sin6->sin6_len - offsetof(struct sockaddr_in6, sin6_addr));
+}
+
+int
+mask2len(addr, lenlim)
+ const struct in6_addr *addr;
+ int lenlim;
+{
+ int i = 0, j;
+ const u_char *p = (const u_char *)addr;
+
+ for (j = 0; j < lenlim; j++, p++) {
+ if (*p != 0xff)
+ break;
+ i += 8;
+ }
+ if (j < lenlim) {
+ switch (*p) {
+#define MASKLEN(m, l) case m: do { i += l; break; } while (0)
+ MASKLEN(0xfe, 7); break;
+ MASKLEN(0xfc, 6); break;
+ MASKLEN(0xf8, 5); break;
+ MASKLEN(0xf0, 4); break;
+ MASKLEN(0xe0, 3); break;
+ MASKLEN(0xc0, 2); break;
+ MASKLEN(0x80, 1); break;
+#undef MASKLEN
+ }
+ }
+ return i;
+}
+
+void
+applymask(addr, mask)
+ struct in6_addr *addr, *mask;
+{
+ int i;
+ u_long *p, *q;
+
+ p = (u_long *)addr; q = (u_long *)mask;
+ for (i = 0; i < 4; i++)
+ *p++ &= *q++;
+}
+
+static const u_char plent[8] = {
+ 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe
+};
+
+void
+applyplen(ia, plen)
+ struct in6_addr *ia;
+ int plen;
+{
+ u_char *p;
+ int i;
+
+ p = ia->s6_addr;
+ for (i = 0; i < 16; i++) {
+ if (plen <= 0)
+ *p = 0;
+ else if (plen < 8)
+ *p &= plent[plen];
+ p++, plen -= 8;
+ }
+}
+
+static const int pl2m[9] = {
+ 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff
+};
+
+struct in6_addr *
+plen2mask(n)
+ int n;
+{
+ static struct in6_addr ia;
+ u_char *p;
+ int i;
+
+ memset(&ia, 0, sizeof(struct in6_addr));
+ p = (u_char *)&ia;
+ for (i = 0; i < 16; i++, p++, n -= 8) {
+ if (n >= 8) {
+ *p = 0xff;
+ continue;
+ }
+ *p = pl2m[n];
+ break;
+ }
+ return &ia;
+}
+
+char *
+allocopy(p)
+ char *p;
+{
+ char *q = (char *)malloc(strlen(p) + 1);
+
+ strcpy(q, p);
+ return q;
+}
+
+char *
+hms()
+{
+ static char buf[BUFSIZ];
+ time_t t;
+ struct tm *tm;
+
+ t = time(NULL);
+ if ((tm = localtime(&t)) == 0) {
+ fatal("localtime");
+ /*NOTREACHED*/
+ }
+ snprintf(buf, sizeof(buf), "%02d:%02d:%02d", tm->tm_hour, tm->tm_min,
+ tm->tm_sec);
+ return buf;
+}
+
+#define RIPRANDDEV 1.0 /* 30 +- 15, max - min = 30 */
+
+int
+ripinterval(timer)
+ int timer;
+{
+ double r = rand();
+
+ interval = (int)(timer + timer * RIPRANDDEV * (r / RAND_MAX - 0.5));
+ nextalarm = time(NULL) + interval;
+ return interval;
+}
+
+time_t
+ripsuptrig()
+{
+ time_t t;
+
+ double r = rand();
+ t = (int)(RIP_TRIG_INT6_MIN +
+ (RIP_TRIG_INT6_MAX - RIP_TRIG_INT6_MIN) * (r / RAND_MAX));
+ sup_trig_update = time(NULL) + t;
+ return t;
+}
+
+void
+#ifdef __STDC__
+fatal(const char *fmt, ...)
+#else
+fatal(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+ char buf[1024];
+
+#ifdef __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ perror(buf);
+ syslog(LOG_ERR, "%s: %s", buf, strerror(errno));
+ rtdexit();
+ va_end(ap);
+}
+
+void
+#ifdef __STDC__
+tracet(int level, const char *fmt, ...)
+#else
+tracet(level, fmt, va_alist)
+ int level;
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+
+#ifdef __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ if (level <= dflag) {
+ fprintf(stderr, "%s: ", hms());
+ vfprintf(stderr, fmt, ap);
+ }
+ if (dflag) {
+ if (level > 0)
+ vsyslog(LOG_DEBUG, fmt, ap);
+ else
+ vsyslog(LOG_WARNING, fmt, ap);
+ }
+ va_end(ap);
+}
+
+void
+#ifdef __STDC__
+trace(int level, const char *fmt, ...)
+#else
+trace(level, fmt, va_alist)
+ int level;
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+
+#ifdef __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ if (level <= dflag)
+ vfprintf(stderr, fmt, ap);
+ if (dflag) {
+ if (level > 0)
+ vsyslog(LOG_DEBUG, fmt, ap);
+ else
+ vsyslog(LOG_WARNING, fmt, ap);
+ }
+ va_end(ap);
+}
+
+unsigned int
+if_maxindex()
+{
+ struct if_nameindex *p, *p0;
+ unsigned int max = 0;
+
+ p0 = if_nameindex();
+ for (p = p0; p && p->if_index && p->if_name; p++) {
+ if (max < p->if_index)
+ max = p->if_index;
+ }
+ if_freenameindex(p0);
+ return max;
+}
+
+struct ifc *
+ifc_find(name)
+ char *name;
+{
+ struct ifc *ifcp;
+
+ for (ifcp = ifc; ifcp; ifcp = ifcp->ifc_next) {
+ if (strcmp(name, ifcp->ifc_name) == 0)
+ return ifcp;
+ }
+ return (struct ifc *)NULL;
+}
+
+struct iff *
+iff_find(ifcp, type)
+ struct ifc *ifcp;
+ int type;
+{
+ struct iff *iffp;
+
+ for (iffp = ifcp->ifc_filter; iffp; iffp = iffp->iff_next) {
+ if (iffp->iff_type == type)
+ return iffp;
+ }
+ return NULL;
+}
+
+void
+setindex2ifc(idx, ifcp)
+ int idx;
+ struct ifc *ifcp;
+{
+ int n;
+ struct ifc **p;
+
+ if (!index2ifc) {
+ nindex2ifc = 5; /*initial guess*/
+ index2ifc = (struct ifc **)
+ malloc(sizeof(*index2ifc) * nindex2ifc);
+ if (index2ifc == NULL) {
+ fatal("malloc");
+ /*NOTREACHED*/
+ }
+ memset(index2ifc, 0, sizeof(*index2ifc) * nindex2ifc);
+ }
+ n = nindex2ifc;
+ while (nindex2ifc <= idx)
+ nindex2ifc *= 2;
+ if (n != nindex2ifc) {
+ p = (struct ifc **)realloc(index2ifc,
+ sizeof(*index2ifc) * nindex2ifc);
+ if (p == NULL) {
+ fatal("realloc");
+ /*NOTREACHED*/
+ }
+ memset(p + n, 0, sizeof(*index2ifc) * (nindex2ifc - n));
+ index2ifc = p;
+ }
+ index2ifc[idx] = ifcp;
+}
diff --git a/usr.sbin/route6d/route6d.h b/usr.sbin/route6d/route6d.h
new file mode 100644
index 0000000..5c59d6a
--- /dev/null
+++ b/usr.sbin/route6d/route6d.h
@@ -0,0 +1,90 @@
+/* $FreeBSD$ */
+/* $KAME: route6d.h,v 1.4 2001/01/15 03:50:54 inoue Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* not yet in use
+#define ROUTE6D_CONF "/usr/local/v6/etc/route6d.conf"
+*/
+
+#define ROUTE6D_DUMP "/var/run/route6d_dump"
+#define ROUTE6D_PID "/var/run/route6d.pid"
+
+#define RIP6_VERSION 1
+
+#define RIP6_REQUEST 1
+#define RIP6_RESPONSE 2
+
+#define IFC_CHANGED 1
+
+struct netinfo6 {
+ struct in6_addr rip6_dest;
+ u_short rip6_tag;
+ u_char rip6_plen;
+ u_char rip6_metric;
+};
+
+struct rip6 {
+ u_char rip6_cmd;
+ u_char rip6_vers;
+ u_char rip6_res1[2];
+ union {
+ struct netinfo6 ru6_nets[1];
+ char ru6_tracefile[1];
+ } rip6un;
+#define rip6_nets rip6un.ru6_nets
+#define rip6_tracefile rip6un.ru6_tracefile
+};
+
+#define HOPCNT_INFINITY6 16
+#define NEXTHOP_METRIC 0xff
+#define RIP6_MAXMTU 1500
+
+#define IFMINMTU 576
+
+#ifndef DEBUG
+#define SUPPLY_INTERVAL6 30
+#define RIP_LIFETIME 180
+#define RIP_HOLDDOWN 120
+#define RIP_TRIG_INT6_MAX 5
+#define RIP_TRIG_INT6_MIN 1
+#else
+/* only for debugging; can not wait for 30sec to appear a bug */
+#define SUPPLY_INTERVAL6 10
+#define RIP_LIFETIME 60
+#define RIP_HOLDDOWN 40
+#define RIP_TRIG_INT6_MAX 5
+#define RIP_TRIG_INT6_MIN 1
+#endif
+
+#define RIP6_PORT 521
+#define RIP6_DEST "ff02::9"
+
+#define LOOPBACK_IF "lo0"
diff --git a/usr.sbin/rpc.lockd/Makefile b/usr.sbin/rpc.lockd/Makefile
new file mode 100644
index 0000000..1ed07f6
--- /dev/null
+++ b/usr.sbin/rpc.lockd/Makefile
@@ -0,0 +1,30 @@
+# $NetBSD: Makefile,v 1.12 2000/08/07 16:23:31 thorpej Exp $
+# $FreeBSD$
+
+PROG= rpc.lockd
+MAN= rpc.lockd.8
+MLINKS= rpc.lockd.8 lockd.8
+SRCS= kern.c nlm_prot_svc.c lockd.c lock_proc.c lockd_lock.c
+NO_WERROR= YES
+WARNS?= 4
+
+CFLAGS+= -I. -I${DESTDIR}/usr/include/rpcsvc
+
+DPADD= ${LIBRPCSVC} ${LIBUTIL}
+LDADD= -lrpcsvc -lutil
+
+CLEANFILES= nlm_prot_svc.c nlm_prot.h test
+
+RPCSRC= ${DESTDIR}/usr/include/rpcsvc/nlm_prot.x
+RPCGEN= rpcgen -L -C
+
+nlm_prot_svc.c: ${RPCSRC}
+ ${RPCGEN} -m -o ${.TARGET} ${RPCSRC}
+
+nlm_prot.h: ${RPCSRC}
+ ${RPCGEN} -h -o ${.TARGET} ${RPCSRC}
+
+test: ${.CURDIR}/test.c
+ cc -o test ${.CURDIR}/test.c -lrpcsvc
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rpc.lockd/kern.c b/usr.sbin/rpc.lockd/kern.c
new file mode 100644
index 0000000..e79cfb0
--- /dev/null
+++ b/usr.sbin/rpc.lockd/kern.c
@@ -0,0 +1,590 @@
+/*-
+ * Copyright (c) 1997 Berkeley Software Design, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Berkeley Software Design Inc's name may not be used to endorse or
+ * promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN INC ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN INC BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from BSDI kern.c,v 1.2 1998/11/25 22:38:27 don Exp
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "nlm_prot.h"
+#include <nfs/rpcv2.h>
+#include <nfs/nfsproto.h>
+#include <nfsclient/nfs_lock.h>
+
+#include "lockd.h"
+#include "lockd_lock.h"
+#include <nfsclient/nfs.h>
+
+#define DAEMON_USERNAME "daemon"
+
+#define nfslockdans(_v, _ansp) \
+ ((_ansp)->la_vers = _v, \
+ nfsclnt(NFSCLNT_LOCKDANS, _ansp))
+
+/* Lock request owner. */
+typedef struct __owner {
+ pid_t pid; /* Process ID. */
+ time_t tod; /* Time-of-day. */
+} OWNER;
+static OWNER owner;
+
+static char hostname[MAXHOSTNAMELEN + 1]; /* Hostname. */
+
+static void client_cleanup(void);
+static void set_auth(CLIENT *cl, struct ucred *ucred);
+int lock_request(LOCKD_MSG *);
+int test_request(LOCKD_MSG *);
+void show(LOCKD_MSG *);
+int unlock_request(LOCKD_MSG *);
+
+/*
+ * will break because fifo needs to be repopened when EOF'd
+ */
+#define lockd_seteuid(uid) seteuid(uid)
+
+#define d_calls (debug_level > 1)
+#define d_args (debug_level > 2)
+
+#define from_addr(sockaddr) \
+ (inet_ntoa((sockaddr)->sin_addr))
+
+void
+client_cleanup(void)
+{
+ (void)lockd_seteuid(0);
+ (void)unlink(_PATH_LCKFIFO);
+ exit(-1);
+}
+
+/*
+ * client_request --
+ * Loop around messages from the kernel, forwarding them off to
+ * NLM servers.
+ */
+pid_t
+client_request(void)
+{
+ LOCKD_MSG msg;
+ fd_set rdset;
+ int fd, nr, ret;
+ pid_t child;
+ uid_t daemon_uid;
+ mode_t old_umask;
+ struct passwd *pw;
+
+ /* Recreate the NLM fifo. */
+ (void)unlink(_PATH_LCKFIFO);
+ old_umask = umask(S_IXGRP|S_IXOTH);
+ if (mkfifo(_PATH_LCKFIFO, S_IWUSR | S_IRUSR)) {
+ syslog(LOG_ERR, "mkfifo: %s: %m", _PATH_LCKFIFO);
+ exit (1);
+ }
+ umask(old_umask);
+
+ /*
+ * Create a separate process, the client code is really a separate
+ * daemon that shares a lot of code.
+ */
+ switch (child = fork()) {
+ case -1:
+ err(1, "fork");
+ case 0:
+ break;
+ default:
+ return (child);
+ }
+
+ signal(SIGHUP, (sig_t)client_cleanup);
+ signal(SIGTERM, (sig_t)client_cleanup);
+
+ /* Setup. */
+ (void)time(&owner.tod);
+ owner.pid = getpid();
+ (void)gethostname(hostname, sizeof(hostname) - 1);
+
+ /* Open the fifo for reading. */
+ if ((fd = open(_PATH_LCKFIFO, O_RDONLY | O_NONBLOCK)) == -1) {
+ syslog(LOG_ERR, "open: %s: %m", _PATH_LCKFIFO);
+ goto err;
+ }
+ pw = getpwnam(DAEMON_USERNAME);
+ if (pw == NULL) {
+ syslog(LOG_ERR, "getpwnam: %s: %m", DAEMON_USERNAME);
+ goto err;
+ }
+ daemon_uid = pw->pw_uid;
+ /* drop our root priviledges */
+ (void)lockd_seteuid(daemon_uid);
+
+ for (;;) {
+ /* Wait for contact... fifo's return EAGAIN when read with
+ * no data
+ */
+ /* Set up the select. */
+ FD_ZERO(&rdset);
+ FD_SET(fd, &rdset);
+ (void)select(fd + 1, &rdset, NULL, NULL, NULL);
+
+ /* Read the fixed length message. */
+ if ((nr = read(fd, &msg, sizeof(msg))) == sizeof(msg)) {
+ if (d_args)
+ show(&msg);
+
+ if (msg.lm_version != LOCKD_MSG_VERSION) {
+ syslog(LOG_ERR,
+ "unknown msg type: %d", msg.lm_version);
+ }
+ /*
+ * Send it to the NLM server and don't grant the lock
+ * if we fail for any reason.
+ */
+ switch (msg.lm_fl.l_type) {
+ case F_RDLCK:
+ case F_WRLCK:
+ if (msg.lm_getlk)
+ ret = test_request(&msg);
+ else
+ ret = lock_request(&msg);
+ break;
+ case F_UNLCK:
+ ret = unlock_request(&msg);
+ break;
+ default:
+ ret = 1;
+ syslog(LOG_ERR,
+ "unknown lock type: %d", msg.lm_fl.l_type);
+ break;
+ }
+ if (ret) {
+ struct lockd_ans ans;
+
+ ans.la_msg_ident = msg.lm_msg_ident;
+ ans.la_errno = EHOSTUNREACH;
+
+ if (nfslockdans(LOCKD_ANS_VERSION, &ans)) {
+ syslog((errno == EPIPE ? LOG_INFO :
+ LOG_ERR), "process %lu: %m",
+ (u_long)msg.lm_msg_ident.pid);
+ }
+ }
+ } else if (nr == -1) {
+ if (errno != EAGAIN) {
+ syslog(LOG_ERR, "read: %s: %m", _PATH_LCKFIFO);
+ goto err;
+ }
+ } else if (nr != 0) {
+ syslog(LOG_ERR,
+ "%s: discard %d bytes", _PATH_LCKFIFO, nr);
+ }
+ }
+
+ /* Reached only on error. */
+err:
+ (void)lockd_seteuid(0);
+ (void)unlink(_PATH_LCKFIFO);
+ _exit (1);
+}
+
+void
+set_auth(cl, ucred)
+ CLIENT *cl;
+ struct ucred *ucred;
+{
+ if (cl->cl_auth != NULL)
+ cl->cl_auth->ah_ops->ah_destroy(cl->cl_auth);
+ cl->cl_auth = authunix_create(hostname,
+ ucred->cr_uid,
+ ucred->cr_groups[0],
+ ucred->cr_ngroups-1,
+ &ucred->cr_groups[1]);
+}
+
+
+/*
+ * test_request --
+ * Convert a lock LOCKD_MSG into an NLM request, and send it off.
+ */
+int
+test_request(LOCKD_MSG *msg)
+{
+ CLIENT *cli;
+ struct timeval timeout = {0, 0}; /* No timeout, no response. */
+ char dummy;
+
+ if (d_calls)
+ syslog(LOG_DEBUG, "test request: %s: %s to %s",
+ msg->lm_nfsv3 ? "V4" : "V1/3",
+ msg->lm_fl.l_type == F_WRLCK ? "write" : "read",
+ from_addr((struct sockaddr_in *)&msg->lm_addr));
+
+ if (msg->lm_nfsv3) {
+ struct nlm4_testargs arg4;
+
+ arg4.cookie.n_bytes = (char *)&msg->lm_msg_ident;
+ arg4.cookie.n_len = sizeof(msg->lm_msg_ident);
+ arg4.exclusive = msg->lm_fl.l_type == F_WRLCK ? 1 : 0;
+ arg4.alock.caller_name = hostname;
+ arg4.alock.fh.n_bytes = (char *)&msg->lm_fh;
+ arg4.alock.fh.n_len = msg->lm_fh_len;
+ arg4.alock.oh.n_bytes = (char *)&owner;
+ arg4.alock.oh.n_len = sizeof(owner);
+ arg4.alock.svid = msg->lm_msg_ident.pid;
+ arg4.alock.l_offset = msg->lm_fl.l_start;
+ arg4.alock.l_len = msg->lm_fl.l_len;
+
+ if ((cli = get_client(
+ (struct sockaddr *)&msg->lm_addr,
+ NLM_VERS4)) == NULL)
+ return (1);
+
+ set_auth(cli, &msg->lm_cred);
+ (void)clnt_call(cli, NLM_TEST_MSG,
+ xdr_nlm4_testargs, &arg4, xdr_void, &dummy, timeout);
+ } else {
+ struct nlm_testargs arg;
+
+ arg.cookie.n_bytes = (char *)&msg->lm_msg_ident;
+ arg.cookie.n_len = sizeof(msg->lm_msg_ident);
+ arg.exclusive = msg->lm_fl.l_type == F_WRLCK ? 1 : 0;
+ arg.alock.caller_name = hostname;
+ arg.alock.fh.n_bytes = (char *)&msg->lm_fh;
+ arg.alock.fh.n_len = msg->lm_fh_len;
+ arg.alock.oh.n_bytes = (char *)&owner;
+ arg.alock.oh.n_len = sizeof(owner);
+ arg.alock.svid = msg->lm_msg_ident.pid;
+ arg.alock.l_offset = msg->lm_fl.l_start;
+ arg.alock.l_len = msg->lm_fl.l_len;
+
+ if ((cli = get_client(
+ (struct sockaddr *)&msg->lm_addr,
+ NLM_VERS)) == NULL)
+ return (1);
+
+ set_auth(cli, &msg->lm_cred);
+ (void)clnt_call(cli, NLM_TEST_MSG,
+ xdr_nlm_testargs, &arg, xdr_void, &dummy, timeout);
+ }
+ return (0);
+}
+
+/*
+ * lock_request --
+ * Convert a lock LOCKD_MSG into an NLM request, and send it off.
+ */
+int
+lock_request(LOCKD_MSG *msg)
+{
+ CLIENT *cli;
+ struct nlm4_lockargs arg4;
+ struct nlm_lockargs arg;
+ struct timeval timeout = {0, 0}; /* No timeout, no response. */
+ char dummy;
+
+ if (d_calls)
+ syslog(LOG_DEBUG, "lock request: %s: %s to %s",
+ msg->lm_nfsv3 ? "V4" : "V1/3",
+ msg->lm_fl.l_type == F_WRLCK ? "write" : "read",
+ from_addr((struct sockaddr_in *)&msg->lm_addr));
+
+ if (msg->lm_nfsv3) {
+ arg4.cookie.n_bytes = (char *)&msg->lm_msg_ident;
+ arg4.cookie.n_len = sizeof(msg->lm_msg_ident);
+ arg4.block = msg->lm_wait ? 1 : 0;
+ arg4.exclusive = msg->lm_fl.l_type == F_WRLCK ? 1 : 0;
+ arg4.alock.caller_name = hostname;
+ arg4.alock.fh.n_bytes = (char *)&msg->lm_fh;
+ arg4.alock.fh.n_len = msg->lm_fh_len;
+ arg4.alock.oh.n_bytes = (char *)&owner;
+ arg4.alock.oh.n_len = sizeof(owner);
+ arg4.alock.svid = msg->lm_msg_ident.pid;
+ arg4.alock.l_offset = msg->lm_fl.l_start;
+ arg4.alock.l_len = msg->lm_fl.l_len;
+ arg4.reclaim = 0;
+ arg4.state = nsm_state;
+
+ if ((cli = get_client(
+ (struct sockaddr *)&msg->lm_addr,
+ NLM_VERS4)) == NULL)
+ return (1);
+
+ set_auth(cli, &msg->lm_cred);
+ (void)clnt_call(cli, NLM_LOCK_MSG,
+ xdr_nlm4_lockargs, &arg4, xdr_void, &dummy, timeout);
+ } else {
+ arg.cookie.n_bytes = (char *)&msg->lm_msg_ident;
+ arg.cookie.n_len = sizeof(msg->lm_msg_ident);
+ arg.block = msg->lm_wait ? 1 : 0;
+ arg.exclusive = msg->lm_fl.l_type == F_WRLCK ? 1 : 0;
+ arg.alock.caller_name = hostname;
+ arg.alock.fh.n_bytes = (char *)&msg->lm_fh;
+ arg.alock.fh.n_len = msg->lm_fh_len;
+ arg.alock.oh.n_bytes = (char *)&owner;
+ arg.alock.oh.n_len = sizeof(owner);
+ arg.alock.svid = msg->lm_msg_ident.pid;
+ arg.alock.l_offset = msg->lm_fl.l_start;
+ arg.alock.l_len = msg->lm_fl.l_len;
+ arg.reclaim = 0;
+ arg.state = nsm_state;
+
+ if ((cli = get_client(
+ (struct sockaddr *)&msg->lm_addr,
+ NLM_VERS)) == NULL)
+ return (1);
+
+ set_auth(cli, &msg->lm_cred);
+ (void)clnt_call(cli, NLM_LOCK_MSG,
+ xdr_nlm_lockargs, &arg, xdr_void, &dummy, timeout);
+ }
+ return (0);
+}
+
+/*
+ * unlock_request --
+ * Convert an unlock LOCKD_MSG into an NLM request, and send it off.
+ */
+int
+unlock_request(LOCKD_MSG *msg)
+{
+ CLIENT *cli;
+ struct nlm4_unlockargs arg4;
+ struct nlm_unlockargs arg;
+ struct timeval timeout = {0, 0}; /* No timeout, no response. */
+ char dummy;
+
+ if (d_calls)
+ syslog(LOG_DEBUG, "unlock request: %s: to %s",
+ msg->lm_nfsv3 ? "V4" : "V1/3",
+ from_addr((struct sockaddr_in *)&msg->lm_addr));
+
+ if (msg->lm_nfsv3) {
+ arg4.cookie.n_bytes = (char *)&msg->lm_msg_ident;
+ arg4.cookie.n_len = sizeof(msg->lm_msg_ident);
+ arg4.alock.caller_name = hostname;
+ arg4.alock.fh.n_bytes = (char *)&msg->lm_fh;
+ arg4.alock.fh.n_len = msg->lm_fh_len;
+ arg4.alock.oh.n_bytes = (char *)&owner;
+ arg4.alock.oh.n_len = sizeof(owner);
+ arg4.alock.svid = msg->lm_msg_ident.pid;
+ arg4.alock.l_offset = msg->lm_fl.l_start;
+ arg4.alock.l_len = msg->lm_fl.l_len;
+
+ if ((cli = get_client(
+ (struct sockaddr *)&msg->lm_addr,
+ NLM_VERS4)) == NULL)
+ return (1);
+
+ set_auth(cli, &msg->lm_cred);
+ (void)clnt_call(cli, NLM_UNLOCK_MSG,
+ xdr_nlm4_unlockargs, &arg4, xdr_void, &dummy, timeout);
+ } else {
+ arg.cookie.n_bytes = (char *)&msg->lm_msg_ident;
+ arg.cookie.n_len = sizeof(msg->lm_msg_ident);
+ arg.alock.caller_name = hostname;
+ arg.alock.fh.n_bytes = (char *)&msg->lm_fh;
+ arg.alock.fh.n_len = msg->lm_fh_len;
+ arg.alock.oh.n_bytes = (char *)&owner;
+ arg.alock.oh.n_len = sizeof(owner);
+ arg.alock.svid = msg->lm_msg_ident.pid;
+ arg.alock.l_offset = msg->lm_fl.l_start;
+ arg.alock.l_len = msg->lm_fl.l_len;
+
+ if ((cli = get_client(
+ (struct sockaddr *)&msg->lm_addr,
+ NLM_VERS)) == NULL)
+ return (1);
+
+ set_auth(cli, &msg->lm_cred);
+ (void)clnt_call(cli, NLM_UNLOCK_MSG,
+ xdr_nlm_unlockargs, &arg, xdr_void, &dummy, timeout);
+ }
+
+ return (0);
+}
+
+int
+lock_answer(int pid, netobj *netcookie, int result, int *pid_p, int version)
+{
+ struct lockd_ans ans;
+
+ if (netcookie->n_len != sizeof(ans.la_msg_ident)) {
+ if (pid == -1) { /* we're screwed */
+ syslog(LOG_ERR, "inedible nlm cookie");
+ return -1;
+ }
+ ans.la_msg_ident.pid = pid;
+ ans.la_msg_ident.msg_seq = -1;
+ } else {
+ memcpy(&ans.la_msg_ident, netcookie->n_bytes,
+ sizeof(ans.la_msg_ident));
+ }
+
+ if (d_calls)
+ syslog(LOG_DEBUG, "lock answer: pid %lu: %s %d",
+ (unsigned long)ans.la_msg_ident.pid,
+ version == NLM_VERS4 ? "nlmv4" : "nlmv3",
+ result);
+
+ ans.la_set_getlk_pid = 0;
+ if (version == NLM_VERS4)
+ switch (result) {
+ case nlm4_granted:
+ ans.la_errno = 0;
+ break;
+ default:
+ ans.la_errno = EACCES;
+ break;
+ case nlm4_denied:
+ if (pid_p == NULL)
+ ans.la_errno = EACCES;
+ else {
+ /* this is an answer to a nlm_test msg */
+ ans.la_set_getlk_pid = 1;
+ ans.la_getlk_pid = *pid_p;
+ ans.la_errno = 0;
+ }
+ break;
+ case nlm4_denied_nolocks:
+ ans.la_errno = EAGAIN;
+ break;
+ case nlm4_blocked:
+ return -1;
+ /* NOTREACHED */
+ case nlm4_denied_grace_period:
+ ans.la_errno = EAGAIN;
+ break;
+ case nlm4_deadlck:
+ ans.la_errno = EDEADLK;
+ break;
+ case nlm4_rofs:
+ ans.la_errno = EROFS;
+ break;
+ case nlm4_stale_fh:
+ ans.la_errno = ESTALE;
+ break;
+ case nlm4_fbig:
+ ans.la_errno = EFBIG;
+ break;
+ case nlm4_failed:
+ ans.la_errno = EACCES;
+ break;
+ }
+ else
+ switch (result) {
+ case nlm_granted:
+ ans.la_errno = 0;
+ break;
+ default:
+ ans.la_errno = EACCES;
+ break;
+ case nlm_denied:
+ if (pid_p == NULL)
+ ans.la_errno = EACCES;
+ else {
+ /* this is an answer to a nlm_test msg */
+ ans.la_set_getlk_pid = 1;
+ ans.la_getlk_pid = *pid_p;
+ ans.la_errno = 0;
+ }
+ break;
+ case nlm_denied_nolocks:
+ ans.la_errno = EAGAIN;
+ break;
+ case nlm_blocked:
+ return -1;
+ /* NOTREACHED */
+ case nlm_denied_grace_period:
+ ans.la_errno = EAGAIN;
+ break;
+ case nlm_deadlck:
+ ans.la_errno = EDEADLK;
+ break;
+ }
+
+ if (nfslockdans(LOCKD_ANS_VERSION, &ans)) {
+ syslog(((errno == EPIPE || errno == ESRCH) ?
+ LOG_INFO : LOG_ERR),
+ "process %lu: %m", (u_long)ans.la_msg_ident.pid);
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * show --
+ * Display the contents of a kernel LOCKD_MSG structure.
+ */
+void
+show(LOCKD_MSG *mp)
+{
+ static char hex[] = "0123456789abcdef";
+ struct fid *fidp;
+ fsid_t *fsidp;
+ size_t len;
+ u_int8_t *p, *t, buf[NFS_SMALLFH*3+1];
+
+ syslog(LOG_DEBUG, "process ID: %lu\n", (long)mp->lm_msg_ident.pid);
+
+ fsidp = (fsid_t *)&mp->lm_fh;
+ fidp = (struct fid *)((u_int8_t *)&mp->lm_fh + sizeof(fsid_t));
+
+ for (t = buf, p = (u_int8_t *)mp->lm_fh,
+ len = mp->lm_fh_len;
+ len > 0; ++p, --len) {
+ *t++ = '\\';
+ *t++ = hex[(*p & 0xf0) >> 4];
+ *t++ = hex[*p & 0x0f];
+ }
+ *t = '\0';
+
+ syslog(LOG_DEBUG, "fh_len %d, fh %s\n", mp->lm_fh_len, buf);
+
+ /* Show flock structure. */
+ syslog(LOG_DEBUG, "start %qu; len %qu; pid %lu; type %d; whence %d\n",
+ mp->lm_fl.l_start, mp->lm_fl.l_len, (u_long)mp->lm_fl.l_pid,
+ mp->lm_fl.l_type, mp->lm_fl.l_whence);
+
+ /* Show wait flag. */
+ syslog(LOG_DEBUG, "wait was %s\n", mp->lm_wait ? "set" : "not set");
+}
diff --git a/usr.sbin/rpc.lockd/lock_proc.c b/usr.sbin/rpc.lockd/lock_proc.c
new file mode 100644
index 0000000..f0ded1d
--- /dev/null
+++ b/usr.sbin/rpc.lockd/lock_proc.c
@@ -0,0 +1,1389 @@
+/* $NetBSD: lock_proc.c,v 1.7 2000/10/11 20:23:56 is Exp $ */
+/* $FreeBSD$ */
+/*
+ * Copyright (c) 1995
+ * A.R. Gordon (andrew.gordon@net-tel.co.uk). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the FreeBSD project
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ANDREW GORDON AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: lock_proc.c,v 1.7 2000/10/11 20:23:56 is Exp $");
+#endif
+
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <netdb.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <netconfig.h>
+
+#include <rpc/rpc.h>
+#include <rpcsvc/sm_inter.h>
+
+#include "lockd.h"
+#include <rpcsvc/nlm_prot.h>
+#include "lockd_lock.h"
+
+
+#define CLIENT_CACHE_SIZE 64 /* No. of client sockets cached */
+#define CLIENT_CACHE_LIFETIME 120 /* In seconds */
+
+static void log_from_addr(const char *, struct svc_req *);
+static void log_netobj(netobj *obj);
+static int addrcmp(struct sockaddr *, struct sockaddr *);
+
+/* log_from_addr ----------------------------------------------------------- */
+/*
+ * Purpose: Log name of function called and source address
+ * Returns: Nothing
+ * Notes: Extracts the source address from the transport handle
+ * passed in as part of the called procedure specification
+ */
+static void
+log_from_addr(fun_name, req)
+ const char *fun_name;
+ struct svc_req *req;
+{
+ struct sockaddr *addr;
+ char hostname_buf[NI_MAXHOST];
+
+ addr = svc_getrpccaller(req->rq_xprt)->buf;
+ if (getnameinfo(addr , addr->sa_len, hostname_buf, sizeof hostname_buf,
+ NULL, 0, 0) != 0)
+ return;
+
+ syslog(LOG_DEBUG, "%s from %s", fun_name, hostname_buf);
+}
+
+/* log_netobj ----------------------------------------------------------- */
+/*
+ * Purpose: Log a netobj
+ * Returns: Nothing
+ * Notes: This function should only really be called as part of
+ * a debug subsystem.
+*/
+static void
+log_netobj(obj)
+ netobj *obj;
+{
+ char objvalbuffer[(sizeof(char)*2)*MAX_NETOBJ_SZ+2];
+ char objascbuffer[sizeof(char)*MAX_NETOBJ_SZ+1];
+ unsigned int i, maxlen;
+ char *tmp1, *tmp2;
+
+ /* Notify of potential security attacks */
+ if (obj->n_len > MAX_NETOBJ_SZ) {
+ syslog(LOG_DEBUG, "SOMEONE IS TRYING TO DO SOMETHING NASTY!\n");
+ syslog(LOG_DEBUG, "netobj too large! Should be %d was %d\n",
+ MAX_NETOBJ_SZ, obj->n_len);
+ }
+ /* Prevent the security hazard from the buffer overflow */
+ maxlen = (obj->n_len < MAX_NETOBJ_SZ ? obj->n_len : MAX_NETOBJ_SZ);
+ for (i=0, tmp1 = objvalbuffer, tmp2 = objascbuffer; i < obj->n_len;
+ i++, tmp1 +=2, tmp2 +=1) {
+ sprintf(tmp1,"%02X",*(obj->n_bytes+i));
+ sprintf(tmp2,"%c",*(obj->n_bytes+i));
+ }
+ *tmp1 = '\0';
+ *tmp2 = '\0';
+ syslog(LOG_DEBUG,"netobjvals: %s\n",objvalbuffer);
+ syslog(LOG_DEBUG,"netobjascs: %s\n",objascbuffer);
+}
+/* get_client -------------------------------------------------------------- */
+/*
+ * Purpose: Get a CLIENT* for making RPC calls to lockd on given host
+ * Returns: CLIENT* pointer, from clnt_udp_create, or NULL if error
+ * Notes: Creating a CLIENT* is quite expensive, involving a
+ * conversation with the remote portmapper to get the
+ * port number. Since a given client is quite likely
+ * to make several locking requests in succession, it is
+ * desirable to cache the created CLIENT*.
+ *
+ * Since we are using UDP rather than TCP, there is no cost
+ * to the remote system in keeping these cached indefinitely.
+ * Unfortunately there is a snag: if the remote system
+ * reboots, the cached portmapper results will be invalid,
+ * and we will never detect this since all of the xxx_msg()
+ * calls return no result - we just fire off a udp packet
+ * and hope for the best.
+ *
+ * We solve this by discarding cached values after two
+ * minutes, regardless of whether they have been used
+ * in the meanwhile (since a bad one might have been used
+ * plenty of times, as the host keeps retrying the request
+ * and we keep sending the reply back to the wrong port).
+ *
+ * Given that the entries will always expire in the order
+ * that they were created, there is no point in a LRU
+ * algorithm for when the cache gets full - entries are
+ * always re-used in sequence.
+ */
+static CLIENT *clnt_cache_ptr[CLIENT_CACHE_SIZE];
+static long clnt_cache_time[CLIENT_CACHE_SIZE]; /* time entry created */
+static struct sockaddr_storage clnt_cache_addr[CLIENT_CACHE_SIZE];
+static rpcvers_t clnt_cache_vers[CLIENT_CACHE_SIZE];
+static int clnt_cache_next_to_use = 0;
+
+static int
+addrcmp(sa1, sa2)
+ struct sockaddr *sa1;
+ struct sockaddr *sa2;
+{
+ int len;
+ void *p1, *p2;
+
+ if (sa1->sa_family != sa2->sa_family)
+ return -1;
+
+ switch (sa1->sa_family) {
+ case AF_INET:
+ p1 = &((struct sockaddr_in *)sa1)->sin_addr;
+ p2 = &((struct sockaddr_in *)sa2)->sin_addr;
+ len = 4;
+ break;
+ case AF_INET6:
+ p1 = &((struct sockaddr_in6 *)sa1)->sin6_addr;
+ p2 = &((struct sockaddr_in6 *)sa2)->sin6_addr;
+ len = 16;
+ break;
+ default:
+ return -1;
+ }
+
+ return memcmp(p1, p2, len);
+}
+
+CLIENT *
+get_client(host_addr, vers)
+ struct sockaddr *host_addr;
+ rpcvers_t vers;
+{
+ CLIENT *client;
+ struct timeval retry_time, time_now;
+ int i;
+ const char *netid;
+ struct netconfig *nconf;
+ char host[NI_MAXHOST];
+
+ gettimeofday(&time_now, NULL);
+
+ /*
+ * Search for the given client in the cache, zapping any expired
+ * entries that we happen to notice in passing.
+ */
+ for (i = 0; i < CLIENT_CACHE_SIZE; i++) {
+ client = clnt_cache_ptr[i];
+ if (client && ((clnt_cache_time[i] + CLIENT_CACHE_LIFETIME)
+ < time_now.tv_sec)) {
+ /* Cache entry has expired. */
+ if (debug_level > 3)
+ syslog(LOG_DEBUG, "Expired CLIENT* in cache");
+ clnt_cache_time[i] = 0L;
+ clnt_destroy(client);
+ clnt_cache_ptr[i] = NULL;
+ client = NULL;
+ }
+ if (client && !addrcmp((struct sockaddr *)&clnt_cache_addr[i],
+ host_addr) && clnt_cache_vers[i] == vers) {
+ /* Found it! */
+ if (debug_level > 3)
+ syslog(LOG_DEBUG, "Found CLIENT* in cache");
+ return (client);
+ }
+ }
+
+ if (debug_level > 3)
+ syslog(LOG_DEBUG, "CLIENT* not found in cache, creating");
+
+ /* Not found in cache. Free the next entry if it is in use. */
+ if (clnt_cache_ptr[clnt_cache_next_to_use]) {
+ clnt_destroy(clnt_cache_ptr[clnt_cache_next_to_use]);
+ clnt_cache_ptr[clnt_cache_next_to_use] = NULL;
+ }
+
+ /*
+ * Need a host string for clnt_tp_create. Use NI_NUMERICHOST
+ * to avoid DNS lookups.
+ */
+ if (getnameinfo(host_addr, host_addr->sa_len, host, sizeof host,
+ NULL, 0, NI_NUMERICHOST) != 0) {
+ syslog(LOG_ERR, "unable to get name string for caller");
+ return NULL;
+ }
+
+#if 1
+ if (host_addr->sa_family == AF_INET6)
+ netid = "udp6";
+ else
+ netid = "udp";
+#else
+ if (host_addr->sa_family == AF_INET6)
+ netid = "tcp6";
+ else
+ netid = "tcp";
+#endif
+ nconf = getnetconfigent(netid);
+ if (nconf == NULL) {
+ syslog(LOG_ERR, "could not get netconfig info for '%s': "
+ "no /etc/netconfig file?", netid);
+ return NULL;
+ }
+
+ client = clnt_tp_create(host, NLM_PROG, vers, nconf);
+ freenetconfigent(nconf);
+
+ if (!client) {
+ syslog(LOG_ERR, "%s", clnt_spcreateerror("clntudp_create"));
+ syslog(LOG_ERR, "Unable to return result to %s", host);
+ return NULL;
+ }
+
+ /* Success - update the cache entry */
+ clnt_cache_ptr[clnt_cache_next_to_use] = client;
+ memcpy(&clnt_cache_addr[clnt_cache_next_to_use], host_addr,
+ host_addr->sa_len);
+ clnt_cache_vers[clnt_cache_next_to_use] = vers;
+ clnt_cache_time[clnt_cache_next_to_use] = time_now.tv_sec;
+ if (++clnt_cache_next_to_use > CLIENT_CACHE_SIZE)
+ clnt_cache_next_to_use = 0;
+
+ /*
+ * Disable the default timeout, so we can specify our own in calls
+ * to clnt_call(). (Note that the timeout is a different concept
+ * from the retry period set in clnt_udp_create() above.)
+ */
+ retry_time.tv_sec = -1;
+ retry_time.tv_usec = -1;
+ clnt_control(client, CLSET_TIMEOUT, (char *)&retry_time);
+
+ if (debug_level > 3)
+ syslog(LOG_DEBUG, "Created CLIENT* for %s", host);
+ return client;
+}
+
+
+/* transmit_result --------------------------------------------------------- */
+/*
+ * Purpose: Transmit result for nlm_xxx_msg pseudo-RPCs
+ * Returns: Nothing - we have no idea if the datagram got there
+ * Notes: clnt_call() will always fail (with timeout) as we are
+ * calling it with timeout 0 as a hack to just issue a datagram
+ * without expecting a result
+ */
+void
+transmit_result(opcode, result, addr)
+ int opcode;
+ nlm_res *result;
+ struct sockaddr *addr;
+{
+ static char dummy;
+ CLIENT *cli;
+ struct timeval timeo;
+ int success;
+
+ if ((cli = get_client(addr, NLM_VERS)) != NULL) {
+ timeo.tv_sec = 0; /* No timeout - not expecting response */
+ timeo.tv_usec = 0;
+
+ success = clnt_call(cli, opcode, xdr_nlm_res, result, xdr_void,
+ &dummy, timeo);
+
+ if (debug_level > 2)
+ syslog(LOG_DEBUG, "clnt_call returns %d(%s)",
+ success, clnt_sperrno(success));
+ }
+}
+/* transmit4_result --------------------------------------------------------- */
+/*
+ * Purpose: Transmit result for nlm4_xxx_msg pseudo-RPCs
+ * Returns: Nothing - we have no idea if the datagram got there
+ * Notes: clnt_call() will always fail (with timeout) as we are
+ * calling it with timeout 0 as a hack to just issue a datagram
+ * without expecting a result
+ */
+void
+transmit4_result(opcode, result, addr)
+ int opcode;
+ nlm4_res *result;
+ struct sockaddr *addr;
+{
+ static char dummy;
+ CLIENT *cli;
+ struct timeval timeo;
+ int success;
+
+ if ((cli = get_client(addr, NLM_VERS4)) != NULL) {
+ timeo.tv_sec = 0; /* No timeout - not expecting response */
+ timeo.tv_usec = 0;
+
+ success = clnt_call(cli, opcode, xdr_nlm4_res, result, xdr_void,
+ &dummy, timeo);
+
+ if (debug_level > 2)
+ syslog(LOG_DEBUG, "clnt_call returns %d(%s)",
+ success, clnt_sperrno(success));
+ }
+}
+
+/*
+ * converts a struct nlm_lock to struct nlm4_lock
+ */
+static void nlmtonlm4(struct nlm_lock *, struct nlm4_lock *);
+static void
+nlmtonlm4(arg, arg4)
+ struct nlm_lock *arg;
+ struct nlm4_lock *arg4;
+{
+ memcpy(arg4, arg, sizeof(nlm_lock));
+ arg4->l_offset = arg->l_offset;
+ arg4->l_len = arg->l_len;
+}
+/* ------------------------------------------------------------------------- */
+/*
+ * Functions for Unix<->Unix locking (ie. monitored locking, with rpc.statd
+ * involved to ensure reclaim of locks after a crash of the "stateless"
+ * server.
+ *
+ * These all come in two flavours - nlm_xxx() and nlm_xxx_msg().
+ * The first are standard RPCs with argument and result.
+ * The nlm_xxx_msg() calls implement exactly the same functions, but
+ * use two pseudo-RPCs (one in each direction). These calls are NOT
+ * standard use of the RPC protocol in that they do not return a result
+ * at all (NB. this is quite different from returning a void result).
+ * The effect of this is to make the nlm_xxx_msg() calls simple unacknowledged
+ * datagrams, requiring higher-level code to perform retries.
+ *
+ * Despite the disadvantages of the nlm_xxx_msg() approach (some of which
+ * are documented in the comments to get_client() above), this is the
+ * interface used by all current commercial NFS implementations
+ * [Solaris, SCO, AIX etc.]. This is presumed to be because these allow
+ * implementations to continue using the standard RPC libraries, while
+ * avoiding the block-until-result nature of the library interface.
+ *
+ * No client implementations have been identified so far that make use
+ * of the true RPC version (early SunOS releases would be a likely candidate
+ * for testing).
+ */
+
+/* nlm_test ---------------------------------------------------------------- */
+/*
+ * Purpose: Test whether a specified lock would be granted if requested
+ * Returns: nlm_granted (or error code)
+ * Notes:
+ */
+nlm_testres *
+nlm_test_1_svc(arg, rqstp)
+ nlm_testargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm_testres res;
+ struct nlm4_lock arg4;
+ struct nlm4_holder *holder;
+ nlmtonlm4(&arg->alock, &arg4);
+
+ if (debug_level)
+ log_from_addr("nlm_test", rqstp);
+
+ holder = testlock(&arg4, arg->exclusive, 0);
+ /*
+ * Copy the cookie from the argument into the result. Note that this
+ * is slightly hazardous, as the structure contains a pointer to a
+ * malloc()ed buffer that will get freed by the caller. However, the
+ * main function transmits the result before freeing the argument
+ * so it is in fact safe.
+ */
+ res.cookie = arg->cookie;
+ if (holder == NULL) {
+ res.stat.stat = nlm_granted;
+ } else {
+ res.stat.stat = nlm_denied;
+ memcpy(&res.stat.nlm_testrply_u.holder, holder,
+ sizeof(struct nlm_holder));
+ res.stat.nlm_testrply_u.holder.l_offset = holder->l_offset;
+ res.stat.nlm_testrply_u.holder.l_len = holder->l_len;
+ }
+ return (&res);
+}
+
+void *
+nlm_test_msg_1_svc(arg, rqstp)
+ nlm_testargs *arg;
+ struct svc_req *rqstp;
+{
+ nlm_testres res;
+ static char dummy;
+ struct sockaddr *addr;
+ CLIENT *cli;
+ int success;
+ struct timeval timeo;
+ struct nlm4_lock arg4;
+ struct nlm4_holder *holder;
+
+ nlmtonlm4(&arg->alock, &arg4);
+
+ if (debug_level)
+ log_from_addr("nlm_test_msg", rqstp);
+
+ holder = testlock(&arg4, arg->exclusive, 0);
+
+ res.cookie = arg->cookie;
+ if (holder == NULL) {
+ res.stat.stat = nlm_granted;
+ } else {
+ res.stat.stat = nlm_denied;
+ memcpy(&res.stat.nlm_testrply_u.holder, holder,
+ sizeof(struct nlm_holder));
+ res.stat.nlm_testrply_u.holder.l_offset = holder->l_offset;
+ res.stat.nlm_testrply_u.holder.l_len = holder->l_len;
+ }
+
+ /*
+ * nlm_test has different result type to the other operations, so
+ * can't use transmit_result() in this case
+ */
+ addr = svc_getrpccaller(rqstp->rq_xprt)->buf;
+ if ((cli = get_client(addr, NLM_VERS)) != NULL) {
+ timeo.tv_sec = 0; /* No timeout - not expecting response */
+ timeo.tv_usec = 0;
+
+ success = clnt_call(cli, NLM_TEST_RES, xdr_nlm_testres,
+ &res, xdr_void, &dummy, timeo);
+
+ if (debug_level > 2)
+ syslog(LOG_DEBUG, "clnt_call returns %d", success);
+ }
+ return (NULL);
+}
+
+/* nlm_lock ---------------------------------------------------------------- */
+/*
+ * Purposes: Establish a lock
+ * Returns: granted, denied or blocked
+ * Notes: *** grace period support missing
+ */
+nlm_res *
+nlm_lock_1_svc(arg, rqstp)
+ nlm_lockargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm_res res;
+ struct nlm4_lockargs arg4;
+ nlmtonlm4(&arg->alock, &arg4.alock);
+ arg4.cookie = arg->cookie;
+ arg4.block = arg->block;
+ arg4.exclusive = arg->exclusive;
+ arg4.reclaim = arg->reclaim;
+ arg4.state = arg->state;
+
+ if (debug_level)
+ log_from_addr("nlm_lock", rqstp);
+
+ /* copy cookie from arg to result. See comment in nlm_test_1() */
+ res.cookie = arg->cookie;
+
+ res.stat.stat = getlock(&arg4, rqstp, LOCK_MON);
+ return (&res);
+}
+
+void *
+nlm_lock_msg_1_svc(arg, rqstp)
+ nlm_lockargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm_res res;
+ struct nlm4_lockargs arg4;
+
+ nlmtonlm4(&arg->alock, &arg4.alock);
+ arg4.cookie = arg->cookie;
+ arg4.block = arg->block;
+ arg4.exclusive = arg->exclusive;
+ arg4.reclaim = arg->reclaim;
+ arg4.state = arg->state;
+
+ if (debug_level)
+ log_from_addr("nlm_lock_msg", rqstp);
+
+ res.cookie = arg->cookie;
+ res.stat.stat = getlock(&arg4, rqstp, LOCK_ASYNC | LOCK_MON);
+ transmit_result(NLM_LOCK_RES, &res,
+ (struct sockaddr *)svc_getcaller(rqstp->rq_xprt));
+
+ return (NULL);
+}
+
+/* nlm_cancel -------------------------------------------------------------- */
+/*
+ * Purpose: Cancel a blocked lock request
+ * Returns: granted or denied
+ * Notes:
+ */
+nlm_res *
+nlm_cancel_1_svc(arg, rqstp)
+ nlm_cancargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm_res res;
+ struct nlm4_lock arg4;
+
+ nlmtonlm4(&arg->alock, &arg4);
+
+ if (debug_level)
+ log_from_addr("nlm_cancel", rqstp);
+
+ /* copy cookie from arg to result. See comment in nlm_test_1() */
+ res.cookie = arg->cookie;
+
+ /*
+ * Since at present we never return 'nlm_blocked', there can never be
+ * a lock to cancel, so this call always fails.
+ */
+ res.stat.stat = unlock(&arg4, LOCK_CANCEL);
+ return (&res);
+}
+
+void *
+nlm_cancel_msg_1_svc(arg, rqstp)
+ nlm_cancargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm_res res;
+ struct nlm4_lock arg4;
+
+ nlmtonlm4(&arg->alock, &arg4);
+
+ if (debug_level)
+ log_from_addr("nlm_cancel_msg", rqstp);
+
+ res.cookie = arg->cookie;
+ /*
+ * Since at present we never return 'nlm_blocked', there can never be
+ * a lock to cancel, so this call always fails.
+ */
+ res.stat.stat = unlock(&arg4, LOCK_CANCEL);
+ transmit_result(NLM_CANCEL_RES, &res,
+ (struct sockaddr *)svc_getcaller(rqstp->rq_xprt));
+ return (NULL);
+}
+
+/* nlm_unlock -------------------------------------------------------------- */
+/*
+ * Purpose: Release an existing lock
+ * Returns: Always granted, unless during grace period
+ * Notes: "no such lock" error condition is ignored, as the
+ * protocol uses unreliable UDP datagrams, and may well
+ * re-try an unlock that has already succeeded.
+ */
+nlm_res *
+nlm_unlock_1_svc(arg, rqstp)
+ nlm_unlockargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm_res res;
+ struct nlm4_lock arg4;
+
+ nlmtonlm4(&arg->alock, &arg4);
+
+ if (debug_level)
+ log_from_addr("nlm_unlock", rqstp);
+
+ res.stat.stat = unlock(&arg4, 0);
+ res.cookie = arg->cookie;
+
+ return (&res);
+}
+
+void *
+nlm_unlock_msg_1_svc(arg, rqstp)
+ nlm_unlockargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm_res res;
+ struct nlm4_lock arg4;
+
+ nlmtonlm4(&arg->alock, &arg4);
+
+ if (debug_level)
+ log_from_addr("nlm_unlock_msg", rqstp);
+
+ res.stat.stat = unlock(&arg4, 0);
+ res.cookie = arg->cookie;
+
+ transmit_result(NLM_UNLOCK_RES, &res,
+ (struct sockaddr *)svc_getcaller(rqstp->rq_xprt));
+ return (NULL);
+}
+
+/* ------------------------------------------------------------------------- */
+/*
+ * Client-side pseudo-RPCs for results. Note that for the client there
+ * are only nlm_xxx_msg() versions of each call, since the 'real RPC'
+ * version returns the results in the RPC result, and so the client
+ * does not normally receive incoming RPCs.
+ *
+ * The exception to this is nlm_granted(), which is genuinely an RPC
+ * call from the server to the client - a 'call-back' in normal procedure
+ * call terms.
+ */
+
+/* nlm_granted ------------------------------------------------------------- */
+/*
+ * Purpose: Receive notification that formerly blocked lock now granted
+ * Returns: always success ('granted')
+ * Notes:
+ */
+nlm_res *
+nlm_granted_1_svc(arg, rqstp)
+ nlm_testargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm_res res;
+
+ if (debug_level)
+ log_from_addr("nlm_granted", rqstp);
+
+ res.stat.stat = lock_answer(arg->alock.svid, &arg->cookie,
+ nlm_granted, NULL, NLM_VERS) == 0 ?
+ nlm_granted : nlm_denied;
+
+ /* copy cookie from arg to result. See comment in nlm_test_1() */
+ res.cookie = arg->cookie;
+
+ return (&res);
+}
+
+void *
+nlm_granted_msg_1_svc(arg, rqstp)
+ nlm_testargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm_res res;
+
+ if (debug_level)
+ log_from_addr("nlm_granted_msg", rqstp);
+
+ res.cookie = arg->cookie;
+ res.stat.stat = nlm_granted;
+ transmit_result(NLM_GRANTED_RES, &res,
+ (struct sockaddr *)svc_getcaller(rqstp->rq_xprt));
+ return (NULL);
+}
+
+/* nlm_test_res ------------------------------------------------------------ */
+/*
+ * Purpose: Accept result from earlier nlm_test_msg() call
+ * Returns: Nothing
+ */
+void *
+nlm_test_res_1_svc(arg, rqstp)
+ nlm_testres *arg;
+ struct svc_req *rqstp;
+{
+ if (debug_level)
+ log_from_addr("nlm_test_res", rqstp);
+ (void)lock_answer(-1, &arg->cookie, arg->stat.stat,
+ &arg->stat.nlm_testrply_u.holder.svid, NLM_VERS);
+ return (NULL);
+}
+
+/* nlm_lock_res ------------------------------------------------------------ */
+/*
+ * Purpose: Accept result from earlier nlm_lock_msg() call
+ * Returns: Nothing
+ */
+void *
+nlm_lock_res_1_svc(arg, rqstp)
+ nlm_res *arg;
+ struct svc_req *rqstp;
+{
+ if (debug_level)
+ log_from_addr("nlm_lock_res", rqstp);
+
+ (void)lock_answer(-1, &arg->cookie, arg->stat.stat, NULL, NLM_VERS);
+
+ return (NULL);
+}
+
+/* nlm_cancel_res ---------------------------------------------------------- */
+/*
+ * Purpose: Accept result from earlier nlm_cancel_msg() call
+ * Returns: Nothing
+ */
+void *
+nlm_cancel_res_1_svc(arg, rqstp)
+ nlm_res *arg __unused;
+ struct svc_req *rqstp;
+{
+ if (debug_level)
+ log_from_addr("nlm_cancel_res", rqstp);
+ return (NULL);
+}
+
+/* nlm_unlock_res ---------------------------------------------------------- */
+/*
+ * Purpose: Accept result from earlier nlm_unlock_msg() call
+ * Returns: Nothing
+ */
+void *
+nlm_unlock_res_1_svc(arg, rqstp)
+ nlm_res *arg;
+ struct svc_req *rqstp;
+{
+ if (debug_level)
+ log_from_addr("nlm_unlock_res", rqstp);
+
+ lock_answer(-1, &arg->cookie, arg->stat.stat, NULL, NLM_VERS);
+
+ return (NULL);
+}
+
+/* nlm_granted_res --------------------------------------------------------- */
+/*
+ * Purpose: Accept result from earlier nlm_granted_msg() call
+ * Returns: Nothing
+ */
+void *
+nlm_granted_res_1_svc(arg, rqstp)
+ nlm_res *arg __unused;
+ struct svc_req *rqstp;
+{
+ if (debug_level)
+ log_from_addr("nlm_granted_res", rqstp);
+ return (NULL);
+}
+
+/* ------------------------------------------------------------------------- */
+/*
+ * Calls for PCNFS locking (aka non-monitored locking, no involvement
+ * of rpc.statd).
+ *
+ * These are all genuine RPCs - no nlm_xxx_msg() nonsense here.
+ */
+
+/* nlm_share --------------------------------------------------------------- */
+/*
+ * Purpose: Establish a DOS-style lock
+ * Returns: success or failure
+ * Notes: Blocking locks are not supported - client is expected
+ * to retry if required.
+ */
+nlm_shareres *
+nlm_share_3_svc(arg, rqstp)
+ nlm_shareargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm_shareres res;
+
+ if (debug_level)
+ log_from_addr("nlm_share", rqstp);
+
+ res.cookie = arg->cookie;
+ res.stat = nlm_granted;
+ res.sequence = 1234356; /* X/Open says this field is ignored? */
+ return (&res);
+}
+
+/* nlm_unshare ------------------------------------------------------------ */
+/*
+ * Purpose: Release a DOS-style lock
+ * Returns: nlm_granted, unless in grace period
+ * Notes:
+ */
+nlm_shareres *
+nlm_unshare_3_svc(arg, rqstp)
+ nlm_shareargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm_shareres res;
+
+ if (debug_level)
+ log_from_addr("nlm_unshare", rqstp);
+
+ res.cookie = arg->cookie;
+ res.stat = nlm_granted;
+ res.sequence = 1234356; /* X/Open says this field is ignored? */
+ return (&res);
+}
+
+/* nlm_nm_lock ------------------------------------------------------------ */
+/*
+ * Purpose: non-monitored version of nlm_lock()
+ * Returns: as for nlm_lock()
+ * Notes: These locks are in the same style as the standard nlm_lock,
+ * but the rpc.statd should not be called to establish a
+ * monitor for the client machine, since that machine is
+ * declared not to be running a rpc.statd, and so would not
+ * respond to the statd protocol.
+ */
+nlm_res *
+nlm_nm_lock_3_svc(arg, rqstp)
+ nlm_lockargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm_res res;
+
+ if (debug_level)
+ log_from_addr("nlm_nm_lock", rqstp);
+
+ /* copy cookie from arg to result. See comment in nlm_test_1() */
+ res.cookie = arg->cookie;
+ res.stat.stat = nlm_granted;
+ return (&res);
+}
+
+/* nlm_free_all ------------------------------------------------------------ */
+/*
+ * Purpose: Release all locks held by a named client
+ * Returns: Nothing
+ * Notes: Potential denial of service security problem here - the
+ * locks to be released are specified by a host name, independent
+ * of the address from which the request has arrived.
+ * Should probably be rejected if the named host has been
+ * using monitored locks.
+ */
+void *
+nlm_free_all_3_svc(arg, rqstp)
+ nlm_notify *arg __unused;
+ struct svc_req *rqstp;
+{
+ static char dummy;
+
+ if (debug_level)
+ log_from_addr("nlm_free_all", rqstp);
+ return (&dummy);
+}
+
+/* calls for nlm version 4 (NFSv3) */
+/* nlm_test ---------------------------------------------------------------- */
+/*
+ * Purpose: Test whether a specified lock would be granted if requested
+ * Returns: nlm_granted (or error code)
+ * Notes:
+ */
+nlm4_testres *
+nlm4_test_4_svc(arg, rqstp)
+ nlm4_testargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm4_testres res;
+ struct nlm4_holder *holder;
+
+ if (debug_level)
+ log_from_addr("nlm4_test", rqstp);
+ if (debug_level > 5) {
+ syslog(LOG_DEBUG, "Locking arguments:\n");
+ log_netobj(&(arg->cookie));
+ syslog(LOG_DEBUG, "Alock arguments:\n");
+ syslog(LOG_DEBUG, "Caller Name: %s\n",arg->alock.caller_name);
+ syslog(LOG_DEBUG, "File Handle:\n");
+ log_netobj(&(arg->alock.fh));
+ syslog(LOG_DEBUG, "Owner Handle:\n");
+ log_netobj(&(arg->alock.oh));
+ syslog(LOG_DEBUG, "SVID: %d\n", arg->alock.svid);
+ syslog(LOG_DEBUG, "Lock Offset: %llu\n",
+ (unsigned long long)arg->alock.l_offset);
+ syslog(LOG_DEBUG, "Lock Length: %llu\n",
+ (unsigned long long)arg->alock.l_len);
+ syslog(LOG_DEBUG, "Exclusive: %s\n",
+ (arg->exclusive ? "true" : "false"));
+ }
+
+ holder = testlock(&arg->alock, arg->exclusive, LOCK_V4);
+
+ /*
+ * Copy the cookie from the argument into the result. Note that this
+ * is slightly hazardous, as the structure contains a pointer to a
+ * malloc()ed buffer that will get freed by the caller. However, the
+ * main function transmits the result before freeing the argument
+ * so it is in fact safe.
+ */
+ res.cookie = arg->cookie;
+ if (holder == NULL) {
+ res.stat.stat = nlm4_granted;
+ } else {
+ res.stat.stat = nlm4_denied;
+ memcpy(&res.stat.nlm4_testrply_u.holder, holder,
+ sizeof(struct nlm4_holder));
+ }
+ return (&res);
+}
+
+void *
+nlm4_test_msg_4_svc(arg, rqstp)
+ nlm4_testargs *arg;
+ struct svc_req *rqstp;
+{
+ nlm4_testres res;
+ static char dummy;
+ struct sockaddr *addr;
+ CLIENT *cli;
+ int success;
+ struct timeval timeo;
+ struct nlm4_holder *holder;
+
+ if (debug_level)
+ log_from_addr("nlm4_test_msg", rqstp);
+
+ holder = testlock(&arg->alock, arg->exclusive, LOCK_V4);
+
+ res.cookie = arg->cookie;
+ if (holder == NULL) {
+ res.stat.stat = nlm4_granted;
+ } else {
+ res.stat.stat = nlm4_denied;
+ memcpy(&res.stat.nlm4_testrply_u.holder, holder,
+ sizeof(struct nlm4_holder));
+ }
+
+ /*
+ * nlm_test has different result type to the other operations, so
+ * can't use transmit4_result() in this case
+ */
+ addr = svc_getrpccaller(rqstp->rq_xprt)->buf;
+ if ((cli = get_client(addr, NLM_VERS4)) != NULL) {
+ timeo.tv_sec = 0; /* No timeout - not expecting response */
+ timeo.tv_usec = 0;
+
+ success = clnt_call(cli, NLM4_TEST_RES, xdr_nlm4_testres,
+ &res, xdr_void, &dummy, timeo);
+
+ if (debug_level > 2)
+ syslog(LOG_DEBUG, "clnt_call returns %d", success);
+ }
+ return (NULL);
+}
+
+/* nlm_lock ---------------------------------------------------------------- */
+/*
+ * Purposes: Establish a lock
+ * Returns: granted, denied or blocked
+ * Notes: *** grace period support missing
+ */
+nlm4_res *
+nlm4_lock_4_svc(arg, rqstp)
+ nlm4_lockargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm4_res res;
+
+ if (debug_level)
+ log_from_addr("nlm4_lock", rqstp);
+ if (debug_level > 5) {
+ syslog(LOG_DEBUG, "Locking arguments:\n");
+ log_netobj(&(arg->cookie));
+ syslog(LOG_DEBUG, "Alock arguments:\n");
+ syslog(LOG_DEBUG, "Caller Name: %s\n",arg->alock.caller_name);
+ syslog(LOG_DEBUG, "File Handle:\n");
+ log_netobj(&(arg->alock.fh));
+ syslog(LOG_DEBUG, "Owner Handle:\n");
+ log_netobj(&(arg->alock.oh));
+ syslog(LOG_DEBUG, "SVID: %d\n", arg->alock.svid);
+ syslog(LOG_DEBUG, "Lock Offset: %llu\n",
+ (unsigned long long)arg->alock.l_offset);
+ syslog(LOG_DEBUG, "Lock Length: %llu\n",
+ (unsigned long long)arg->alock.l_len);
+ syslog(LOG_DEBUG, "Block: %s\n", (arg->block ? "true" : "false"));
+ syslog(LOG_DEBUG, "Exclusive: %s\n", (arg->exclusive ? "true" : "false"));
+ syslog(LOG_DEBUG, "Reclaim: %s\n", (arg->reclaim ? "true" : "false"));
+ syslog(LOG_DEBUG, "State num: %d\n", arg->state);
+ }
+
+ /* copy cookie from arg to result. See comment in nlm_test_4() */
+ res.cookie = arg->cookie;
+
+ res.stat.stat = getlock(arg, rqstp, LOCK_MON | LOCK_V4);
+ return (&res);
+}
+
+void *
+nlm4_lock_msg_4_svc(arg, rqstp)
+ nlm4_lockargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm4_res res;
+
+ if (debug_level)
+ log_from_addr("nlm4_lock_msg", rqstp);
+
+ res.cookie = arg->cookie;
+ res.stat.stat = getlock(arg, rqstp, LOCK_MON | LOCK_ASYNC | LOCK_V4);
+ transmit4_result(NLM4_LOCK_RES, &res,
+ (struct sockaddr *)svc_getcaller(rqstp->rq_xprt));
+
+ return (NULL);
+}
+
+/* nlm_cancel -------------------------------------------------------------- */
+/*
+ * Purpose: Cancel a blocked lock request
+ * Returns: granted or denied
+ * Notes:
+ */
+nlm4_res *
+nlm4_cancel_4_svc(arg, rqstp)
+ nlm4_cancargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm4_res res;
+
+ if (debug_level)
+ log_from_addr("nlm4_cancel", rqstp);
+
+ /* copy cookie from arg to result. See comment in nlm_test_1() */
+ res.cookie = arg->cookie;
+
+ /*
+ * Since at present we never return 'nlm_blocked', there can never be
+ * a lock to cancel, so this call always fails.
+ */
+ res.stat.stat = unlock(&arg->alock, LOCK_CANCEL);
+ return (&res);
+}
+
+void *
+nlm4_cancel_msg_4_svc(arg, rqstp)
+ nlm4_cancargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm4_res res;
+
+ if (debug_level)
+ log_from_addr("nlm4_cancel_msg", rqstp);
+
+ res.cookie = arg->cookie;
+ /*
+ * Since at present we never return 'nlm_blocked', there can never be
+ * a lock to cancel, so this call always fails.
+ */
+ res.stat.stat = unlock(&arg->alock, LOCK_CANCEL | LOCK_V4);
+ transmit4_result(NLM4_CANCEL_RES, &res,
+ (struct sockaddr *)svc_getcaller(rqstp->rq_xprt));
+ return (NULL);
+}
+
+/* nlm_unlock -------------------------------------------------------------- */
+/*
+ * Purpose: Release an existing lock
+ * Returns: Always granted, unless during grace period
+ * Notes: "no such lock" error condition is ignored, as the
+ * protocol uses unreliable UDP datagrams, and may well
+ * re-try an unlock that has already succeeded.
+ */
+nlm4_res *
+nlm4_unlock_4_svc(arg, rqstp)
+ nlm4_unlockargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm4_res res;
+
+ if (debug_level)
+ log_from_addr("nlm4_unlock", rqstp);
+
+ res.stat.stat = unlock(&arg->alock, LOCK_V4);
+ res.cookie = arg->cookie;
+
+ return (&res);
+}
+
+void *
+nlm4_unlock_msg_4_svc(arg, rqstp)
+ nlm4_unlockargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm4_res res;
+
+ if (debug_level)
+ log_from_addr("nlm4_unlock_msg", rqstp);
+
+ res.stat.stat = unlock(&arg->alock, LOCK_V4);
+ res.cookie = arg->cookie;
+
+ transmit4_result(NLM4_UNLOCK_RES, &res,
+ (struct sockaddr *)svc_getcaller(rqstp->rq_xprt));
+ return (NULL);
+}
+
+/* ------------------------------------------------------------------------- */
+/*
+ * Client-side pseudo-RPCs for results. Note that for the client there
+ * are only nlm_xxx_msg() versions of each call, since the 'real RPC'
+ * version returns the results in the RPC result, and so the client
+ * does not normally receive incoming RPCs.
+ *
+ * The exception to this is nlm_granted(), which is genuinely an RPC
+ * call from the server to the client - a 'call-back' in normal procedure
+ * call terms.
+ */
+
+/* nlm_granted ------------------------------------------------------------- */
+/*
+ * Purpose: Receive notification that formerly blocked lock now granted
+ * Returns: always success ('granted')
+ * Notes:
+ */
+nlm4_res *
+nlm4_granted_4_svc(arg, rqstp)
+ nlm4_testargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm4_res res;
+
+ if (debug_level)
+ log_from_addr("nlm4_granted", rqstp);
+
+ res.stat.stat = lock_answer(arg->alock.svid, &arg->cookie,
+ nlm4_granted, NULL, NLM_VERS4) == 0 ?
+ nlm4_granted : nlm4_denied;
+
+ /* copy cookie from arg to result. See comment in nlm_test_1() */
+ res.cookie = arg->cookie;
+
+ return (&res);
+}
+
+void *
+nlm4_granted_msg_4_svc(arg, rqstp)
+ nlm4_testargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm4_res res;
+
+ if (debug_level)
+ log_from_addr("nlm4_granted_msg", rqstp);
+
+ res.cookie = arg->cookie;
+ res.stat.stat = nlm4_granted;
+ transmit4_result(NLM4_GRANTED_RES, &res,
+ (struct sockaddr *)svc_getrpccaller(rqstp->rq_xprt)->buf);
+ return (NULL);
+}
+
+/* nlm_test_res ------------------------------------------------------------ */
+/*
+ * Purpose: Accept result from earlier nlm_test_msg() call
+ * Returns: Nothing
+ */
+void *
+nlm4_test_res_4_svc(arg, rqstp)
+ nlm4_testres *arg;
+ struct svc_req *rqstp;
+{
+ if (debug_level)
+ log_from_addr("nlm4_test_res", rqstp);
+
+ (void)lock_answer(-1, &arg->cookie, arg->stat.stat,
+ (int *)&arg->stat.nlm4_testrply_u.holder.svid,
+ NLM_VERS4);
+ return (NULL);
+}
+
+/* nlm_lock_res ------------------------------------------------------------ */
+/*
+ * Purpose: Accept result from earlier nlm_lock_msg() call
+ * Returns: Nothing
+ */
+void *
+nlm4_lock_res_4_svc(arg, rqstp)
+ nlm4_res *arg;
+ struct svc_req *rqstp;
+{
+ if (debug_level)
+ log_from_addr("nlm4_lock_res", rqstp);
+
+ (void)lock_answer(-1, &arg->cookie, arg->stat.stat, NULL, NLM_VERS4);
+
+ return (NULL);
+}
+
+/* nlm_cancel_res ---------------------------------------------------------- */
+/*
+ * Purpose: Accept result from earlier nlm_cancel_msg() call
+ * Returns: Nothing
+ */
+void *
+nlm4_cancel_res_4_svc(arg, rqstp)
+ nlm4_res *arg __unused;
+ struct svc_req *rqstp;
+{
+ if (debug_level)
+ log_from_addr("nlm4_cancel_res", rqstp);
+ return (NULL);
+}
+
+/* nlm_unlock_res ---------------------------------------------------------- */
+/*
+ * Purpose: Accept result from earlier nlm_unlock_msg() call
+ * Returns: Nothing
+ */
+void *
+nlm4_unlock_res_4_svc(arg, rqstp)
+ nlm4_res *arg __unused;
+ struct svc_req *rqstp;
+{
+ if (debug_level)
+ log_from_addr("nlm4_unlock_res", rqstp);
+ return (NULL);
+}
+
+/* nlm_granted_res --------------------------------------------------------- */
+/*
+ * Purpose: Accept result from earlier nlm_granted_msg() call
+ * Returns: Nothing
+ */
+void *
+nlm4_granted_res_4_svc(arg, rqstp)
+ nlm4_res *arg __unused;
+ struct svc_req *rqstp;
+{
+ if (debug_level)
+ log_from_addr("nlm4_granted_res", rqstp);
+ return (NULL);
+}
+
+/* ------------------------------------------------------------------------- */
+/*
+ * Calls for PCNFS locking (aka non-monitored locking, no involvement
+ * of rpc.statd).
+ *
+ * These are all genuine RPCs - no nlm_xxx_msg() nonsense here.
+ */
+
+/* nlm_share --------------------------------------------------------------- */
+/*
+ * Purpose: Establish a DOS-style lock
+ * Returns: success or failure
+ * Notes: Blocking locks are not supported - client is expected
+ * to retry if required.
+ */
+nlm4_shareres *
+nlm4_share_4_svc(arg, rqstp)
+ nlm4_shareargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm4_shareres res;
+
+ if (debug_level)
+ log_from_addr("nlm4_share", rqstp);
+
+ res.cookie = arg->cookie;
+ res.stat = nlm4_granted;
+ res.sequence = 1234356; /* X/Open says this field is ignored? */
+ return (&res);
+}
+
+/* nlm4_unshare ------------------------------------------------------------ */
+/*
+ * Purpose: Release a DOS-style lock
+ * Returns: nlm_granted, unless in grace period
+ * Notes:
+ */
+nlm4_shareres *
+nlm4_unshare_4_svc(arg, rqstp)
+ nlm4_shareargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm4_shareres res;
+
+ if (debug_level)
+ log_from_addr("nlm_unshare", rqstp);
+
+ res.cookie = arg->cookie;
+ res.stat = nlm4_granted;
+ res.sequence = 1234356; /* X/Open says this field is ignored? */
+ return (&res);
+}
+
+/* nlm4_nm_lock ------------------------------------------------------------ */
+/*
+ * Purpose: non-monitored version of nlm4_lock()
+ * Returns: as for nlm4_lock()
+ * Notes: These locks are in the same style as the standard nlm4_lock,
+ * but the rpc.statd should not be called to establish a
+ * monitor for the client machine, since that machine is
+ * declared not to be running a rpc.statd, and so would not
+ * respond to the statd protocol.
+ */
+nlm4_res *
+nlm4_nm_lock_4_svc(arg, rqstp)
+ nlm4_lockargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm4_res res;
+
+ if (debug_level)
+ log_from_addr("nlm4_nm_lock", rqstp);
+
+ /* copy cookie from arg to result. See comment in nlm4_test_1() */
+ res.cookie = arg->cookie;
+ res.stat.stat = nlm4_granted;
+ return (&res);
+}
+
+/* nlm4_free_all ------------------------------------------------------------ */
+/*
+ * Purpose: Release all locks held by a named client
+ * Returns: Nothing
+ * Notes: Potential denial of service security problem here - the
+ * locks to be released are specified by a host name, independent
+ * of the address from which the request has arrived.
+ * Should probably be rejected if the named host has been
+ * using monitored locks.
+ */
+void *
+nlm4_free_all_4_svc(arg, rqstp)
+ struct nlm4_notify *arg __unused;
+ struct svc_req *rqstp;
+{
+ static char dummy;
+
+ if (debug_level)
+ log_from_addr("nlm4_free_all", rqstp);
+ return (&dummy);
+}
+
+/* nlm_sm_notify --------------------------------------------------------- */
+/*
+ * Purpose: called by rpc.statd when a monitored host state changes.
+ * Returns: Nothing
+ */
+void *
+nlm_sm_notify_0_svc(arg, rqstp)
+ struct nlm_sm_status *arg;
+ struct svc_req *rqstp __unused;
+{
+ static char dummy;
+ notify(arg->mon_name, arg->state);
+ return (&dummy);
+}
diff --git a/usr.sbin/rpc.lockd/lockd.c b/usr.sbin/rpc.lockd/lockd.c
new file mode 100644
index 0000000..2f56aba
--- /dev/null
+++ b/usr.sbin/rpc.lockd/lockd.c
@@ -0,0 +1,271 @@
+/* $NetBSD: lockd.c,v 1.7 2000/08/12 18:08:44 thorpej Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (c) 1995
+ * A.R. Gordon (andrew.gordon@net-tel.co.uk). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the FreeBSD project
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ANDREW GORDON AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: lockd.c,v 1.7 2000/08/12 18:08:44 thorpej Exp $");
+#endif
+
+/*
+ * main() function for NFS lock daemon. Most of the code in this
+ * file was generated by running rpcgen /usr/include/rpcsvc/nlm_prot.x.
+ *
+ * The actual program logic is in the file lock_proc.c
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <syslog.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+#include <libutil.h>
+#include <netconfig.h>
+
+#include <rpc/rpc.h>
+#include <rpcsvc/sm_inter.h>
+
+#include "lockd.h"
+#include <rpcsvc/nlm_prot.h>
+
+int debug_level = 0; /* 0 = no debugging syslog() calls */
+int _rpcsvcdirty = 0;
+
+int grace_expired;
+int nsm_state;
+pid_t client_pid;
+struct mon mon_host;
+
+void init_nsm(void);
+void nlm_prog_0(struct svc_req *, SVCXPRT *);
+void nlm_prog_1(struct svc_req *, SVCXPRT *);
+void nlm_prog_3(struct svc_req *, SVCXPRT *);
+void nlm_prog_4(struct svc_req *, SVCXPRT *);
+void usage(void);
+
+void sigalarm_handler(void);
+
+const char *transports[] = { "udp", "tcp", "udp6", "tcp6" };
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ SVCXPRT *transp;
+ int ch, i, maxindex, s;
+ struct sigaction sigalarm;
+ int grace_period = 30;
+ struct netconfig *nconf;
+
+ while ((ch = getopt(argc, argv, "d:g:")) != (-1)) {
+ switch (ch) {
+ case 'd':
+ debug_level = atoi(optarg);
+ if (!debug_level) {
+ usage();
+ /* NOTREACHED */
+ }
+ break;
+ case 'g':
+ grace_period = atoi(optarg);
+ if (!grace_period) {
+ usage();
+ /* NOTREACHED */
+ }
+ break;
+ default:
+ case '?':
+ usage();
+ /* NOTREACHED */
+ }
+ }
+ if (geteuid()) { /* This command allowed only to root */
+ fprintf(stderr, "Sorry. You are not superuser\n");
+ exit(1);
+ }
+
+ (void)rpcb_unset(NLM_PROG, NLM_SM, NULL);
+ (void)rpcb_unset(NLM_PROG, NLM_VERS, NULL);
+ (void)rpcb_unset(NLM_PROG, NLM_VERSX, NULL);
+ (void)rpcb_unset(NLM_PROG, NLM_VERS4, NULL);
+
+ /*
+ * Check if IPv6 support is present.
+ */
+ s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+ if (s < 0)
+ maxindex = 2;
+ else {
+ close(s);
+ maxindex = 4;
+ }
+
+ for (i = 0; i < maxindex; i++) {
+ nconf = getnetconfigent(transports[i]);
+ if (nconf == NULL)
+ errx(1, "cannot get %s netconf: %s.", transports[i],
+ nc_sperror());
+
+ transp = svc_tli_create(RPC_ANYFD, nconf, NULL, 0, 0);
+ if (transp == NULL) {
+ errx(1, "cannot create %s service.", transports[i]);
+ /* NOTREACHED */
+ }
+ if (!svc_reg(transp, NLM_PROG, NLM_SM, nlm_prog_0, nconf)) {
+ errx(1, "unable to register (NLM_PROG, NLM_SM, %s)",
+ transports[i]);
+ /* NOTREACHED */
+ }
+ if (!svc_reg(transp, NLM_PROG, NLM_VERS, nlm_prog_1, nconf)) {
+ errx(1, "unable to register (NLM_PROG, NLM_VERS, %s)",
+ transports[i]);
+ /* NOTREACHED */
+ }
+ if (!svc_reg(transp, NLM_PROG, NLM_VERSX, nlm_prog_3, nconf)) {
+ errx(1, "unable to register (NLM_PROG, NLM_VERSX, %s)",
+ transports[i]);
+ /* NOTREACHED */
+ }
+ if (!svc_reg(transp, NLM_PROG, NLM_VERS4, nlm_prog_4, nconf)) {
+ errx(1, "unable to register (NLM_PROG, NLM_VERS4, %s)",
+ transports[i]);
+ /* NOTREACHED */
+ }
+ freenetconfigent(nconf);
+ }
+
+ /*
+ * Note that it is NOT sensible to run this program from inetd - the
+ * protocol assumes that it will run immediately at boot time.
+ */
+ if (daemon(0, 0)) {
+ err(1, "cannot fork");
+ /* NOTREACHED */
+ }
+
+ openlog("rpc.lockd", 0, LOG_DAEMON);
+ if (debug_level)
+ syslog(LOG_INFO, "Starting, debug level %d", debug_level);
+ else
+ syslog(LOG_INFO, "Starting");
+
+ sigalarm.sa_handler = (sig_t) sigalarm_handler;
+ sigemptyset(&sigalarm.sa_mask);
+ sigalarm.sa_flags = SA_RESETHAND; /* should only happen once */
+ sigalarm.sa_flags |= SA_RESTART;
+ if (sigaction(SIGALRM, &sigalarm, NULL) != 0) {
+ syslog(LOG_WARNING, "sigaction(SIGALRM) failed: %s",
+ strerror(errno));
+ exit(1);
+ }
+ grace_expired = 0;
+ alarm(10);
+
+ init_nsm();
+
+ client_pid = client_request();
+
+ svc_run(); /* Should never return */
+ exit(1);
+}
+
+void
+sigalarm_handler(void)
+{
+
+ grace_expired = 1;
+}
+
+void
+usage()
+{
+ errx(1, "usage: rpc.lockd [-d <debuglevel>] [-g <grace period>]");
+}
+
+/*
+ * init_nsm --
+ * Reset the NSM state-of-the-world and acquire its state.
+ */
+void
+init_nsm(void)
+{
+ enum clnt_stat ret;
+ my_id id;
+ sm_stat stat;
+ char name[] = "NFS NLM";
+ char localhost[] = "localhost";
+
+ /*
+ * !!!
+ * The my_id structure isn't used by the SM_UNMON_ALL call, as far
+ * as I know. Leave it empty for now.
+ */
+ memset(&id, 0, sizeof(id));
+ id.my_name = name;
+
+ /*
+ * !!!
+ * The statd program must already be registered when lockd runs.
+ */
+ do {
+ ret = callrpc("localhost", SM_PROG, SM_VERS, SM_UNMON_ALL,
+ xdr_my_id, &id, xdr_sm_stat, &stat);
+ if (ret == RPC_PROGUNAVAIL) {
+ warnx("%lu %s", SM_PROG, clnt_sperrno(ret));
+ sleep(2);
+ continue;
+ }
+ break;
+ } while (0);
+
+ if (ret != 0) {
+ errx(1, "%lu %s", SM_PROG, clnt_sperrno(ret));
+ }
+
+ nsm_state = stat.state;
+
+ /* setup constant data for SM_MON calls */
+ mon_host.mon_id.my_id.my_name = localhost;
+ mon_host.mon_id.my_id.my_prog = NLM_PROG;
+ mon_host.mon_id.my_id.my_vers = NLM_SM;
+ mon_host.mon_id.my_id.my_proc = NLM_SM_NOTIFY; /* bsdi addition */
+}
diff --git a/usr.sbin/rpc.lockd/lockd.h b/usr.sbin/rpc.lockd/lockd.h
new file mode 100644
index 0000000..0a8e2e6
--- /dev/null
+++ b/usr.sbin/rpc.lockd/lockd.h
@@ -0,0 +1,41 @@
+/* $NetBSD: lockd.h,v 1.2 2000/06/07 14:34:40 bouyer Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (c) 1995
+ * A.R. Gordon (andrew.gordon@net-tel.co.uk). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the FreeBSD project
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ANDREW GORDON AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+extern int debug_level;
+extern int grace_expired;
+pid_t client_request(void);
+extern int nsm_state;
+extern pid_t client_pid;
diff --git a/usr.sbin/rpc.lockd/lockd_lock.c b/usr.sbin/rpc.lockd/lockd_lock.c
new file mode 100644
index 0000000..3d43f34
--- /dev/null
+++ b/usr.sbin/rpc.lockd/lockd_lock.c
@@ -0,0 +1,2253 @@
+/* $NetBSD: lockd_lock.c,v 1.5 2000/11/21 03:47:41 enami Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (c) 2001 Andrew P. Lentvorski, Jr.
+ * Copyright (c) 2000 Manuel Bouyer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#define LOCKD_DEBUG
+
+#include <stdio.h>
+#ifdef LOCKD_DEBUG
+#include <stdarg.h>
+#endif
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include <rpc/rpc.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/wait.h>
+#include <rpcsvc/sm_inter.h>
+#include <rpcsvc/nlm_prot.h>
+#include "lockd_lock.h"
+#include "lockd.h"
+
+#define MAXOBJECTSIZE 64
+#define MAXBUFFERSIZE 1024
+
+/*
+ * SM_MAXSTRLEN is usually 1024. This means that lock requests and
+ * host name monitoring entries are *MUCH* larger than they should be
+ */
+
+/*
+ * A set of utilities for managing file locking
+ *
+ * XXX: All locks are in a linked list, a better structure should be used
+ * to improve search/access effeciency.
+ */
+
+/* struct describing a lock */
+struct file_lock {
+ LIST_ENTRY(file_lock) nfslocklist;
+ fhandle_t filehandle; /* NFS filehandle */
+ struct sockaddr *addr;
+ struct nlm4_holder client; /* lock holder */
+ /* XXX: client_cookie used *only* in send_granted */
+ netobj client_cookie; /* cookie sent by the client */
+ char client_name[SM_MAXSTRLEN];
+ int nsm_status; /* status from the remote lock manager */
+ int status; /* lock status, see below */
+ int flags; /* lock flags, see lockd_lock.h */
+ int blocking; /* blocking lock or not */
+ pid_t locker; /* pid of the child process trying to get the lock */
+ int fd; /* file descriptor for this lock */
+};
+
+LIST_HEAD(nfslocklist_head, file_lock);
+struct nfslocklist_head nfslocklist_head = LIST_HEAD_INITIALIZER(nfslocklist_head);
+
+LIST_HEAD(blockedlocklist_head, file_lock);
+struct blockedlocklist_head blockedlocklist_head = LIST_HEAD_INITIALIZER(blockedlocklist_head);
+
+/* lock status */
+#define LKST_LOCKED 1 /* lock is locked */
+/* XXX: Is this flag file specific or lock specific? */
+#define LKST_WAITING 2 /* file is already locked by another host */
+#define LKST_PROCESSING 3 /* child is trying to aquire the lock */
+#define LKST_DYING 4 /* must dies when we get news from the child */
+
+/* struct describing a monitored host */
+struct host {
+ LIST_ENTRY(host) hostlst;
+ char name[SM_MAXSTRLEN];
+ int refcnt;
+};
+/* list of hosts we monitor */
+LIST_HEAD(hostlst_head, host);
+struct hostlst_head hostlst_head = LIST_HEAD_INITIALIZER(hostlst_head);
+
+/*
+ * File monitoring handlers
+ * XXX: These might be able to be removed when kevent support
+ * is placed into the hardware lock/unlock routines. (ie.
+ * let the kernel do all the file monitoring)
+ */
+
+/* Struct describing a monitored file */
+struct monfile {
+ LIST_ENTRY(monfile) monfilelist;
+ fhandle_t filehandle; /* Local access filehandle */
+ int fd; /* file descriptor: remains open until unlock! */
+ int refcount;
+ int exclusive;
+};
+
+/* List of files we monitor */
+LIST_HEAD(monfilelist_head, monfile);
+struct monfilelist_head monfilelist_head = LIST_HEAD_INITIALIZER(monfilelist_head);
+
+static int debugdelay = 0;
+
+enum nfslock_status { NFS_GRANTED = 0, NFS_GRANTED_DUPLICATE,
+ NFS_DENIED, NFS_DENIED_NOLOCK,
+ NFS_RESERR };
+
+enum hwlock_status { HW_GRANTED = 0, HW_GRANTED_DUPLICATE,
+ HW_DENIED, HW_DENIED_NOLOCK,
+ HW_STALEFH, HW_READONLY, HW_RESERR };
+
+enum partialfilelock_status { PFL_GRANTED=0, PFL_GRANTED_DUPLICATE, PFL_DENIED,
+ PFL_NFSDENIED, PFL_NFSBLOCKED, PFL_NFSDENIED_NOLOCK, PFL_NFSRESERR,
+ PFL_HWDENIED, PFL_HWBLOCKED, PFL_HWDENIED_NOLOCK, PFL_HWRESERR};
+
+enum LFLAGS {LEDGE_LEFT, LEDGE_LBOUNDARY, LEDGE_INSIDE, LEDGE_RBOUNDARY, LEDGE_RIGHT};
+enum RFLAGS {REDGE_LEFT, REDGE_LBOUNDARY, REDGE_INSIDE, REDGE_RBOUNDARY, REDGE_RIGHT};
+/* XXX: WARNING! I HAVE OVERLOADED THIS STATUS ENUM! SPLIT IT APART INTO TWO */
+enum split_status {SPL_DISJOINT=0, SPL_LOCK1=1, SPL_LOCK2=2, SPL_CONTAINED=4, SPL_RESERR=8};
+
+enum partialfilelock_status lock_partialfilelock(struct file_lock *fl);
+
+void send_granted(struct file_lock *fl, int opcode);
+void siglock(void);
+void sigunlock(void);
+void monitor_lock_host(const char *hostname);
+void unmonitor_lock_host(const char *hostname);
+
+void copy_nlm4_lock_to_nlm4_holder(const struct nlm4_lock *src,
+ const bool_t exclusive, struct nlm4_holder *dest);
+struct file_lock * allocate_file_lock(const netobj *lockowner,
+ const netobj *matchcookie);
+void deallocate_file_lock(struct file_lock *fl);
+void fill_file_lock(struct file_lock *fl, const fhandle_t *fh,
+ struct sockaddr *addr, const bool_t exclusive, const int32_t svid,
+ const u_int64_t offset, const u_int64_t len, const char *caller_name,
+ const int state, const int status, const int flags, const int blocking);
+int regions_overlap(const u_int64_t start1, const u_int64_t len1,
+ const u_int64_t start2, const u_int64_t len2);;
+enum split_status region_compare(const u_int64_t starte, const u_int64_t lene,
+ const u_int64_t startu, const u_int64_t lenu,
+ u_int64_t *start1, u_int64_t *len1, u_int64_t *start2, u_int64_t *len2);
+int same_netobj(const netobj *n0, const netobj *n1);
+int same_filelock_identity(const struct file_lock *fl0,
+ const struct file_lock *fl2);
+
+static void debuglog(char const *fmt, ...);
+void dump_static_object(const unsigned char* object, const int sizeof_object,
+ unsigned char* hbuff, const int sizeof_hbuff,
+ unsigned char* cbuff, const int sizeof_cbuff);
+void dump_netobj(const struct netobj *nobj);
+void dump_filelock(const struct file_lock *fl);
+struct file_lock * get_lock_matching_unlock(const struct file_lock *fl);
+enum nfslock_status test_nfslock(const struct file_lock *fl,
+ struct file_lock **conflicting_fl);
+enum nfslock_status lock_nfslock(struct file_lock *fl);
+enum nfslock_status delete_nfslock(struct file_lock *fl);
+enum nfslock_status unlock_nfslock(const struct file_lock *fl,
+ struct file_lock **released_lock, struct file_lock **left_lock,
+ struct file_lock **right_lock);
+enum hwlock_status lock_hwlock(struct file_lock *fl);
+enum split_status split_nfslock(const struct file_lock *exist_lock,
+ const struct file_lock *unlock_lock, struct file_lock **left_lock,
+ struct file_lock **right_lock);
+void add_blockingfilelock(struct file_lock *fl);
+enum hwlock_status unlock_hwlock(const struct file_lock *fl);
+enum hwlock_status test_hwlock(const struct file_lock *fl,
+ struct file_lock **conflicting_fl);
+void remove_blockingfilelock(struct file_lock *fl);
+void clear_blockingfilelock(const char *hostname);
+void retry_blockingfilelocklist(void);
+enum partialfilelock_status unlock_partialfilelock(
+ const struct file_lock *fl);
+void clear_partialfilelock(const char *hostname);
+enum partialfilelock_status test_partialfilelock(
+ const struct file_lock *fl, struct file_lock **conflicting_fl);
+enum nlm_stats do_test(struct file_lock *fl,
+ struct file_lock **conflicting_fl);
+enum nlm_stats do_unlock(struct file_lock *fl);
+enum nlm_stats do_lock(struct file_lock *fl);
+void do_clear(const char *hostname);
+
+
+void
+debuglog(char const *fmt, ...)
+{
+ va_list ap;
+
+ if (debug_level < 1) {
+ return;
+ }
+
+ sleep(debugdelay);
+
+ va_start(ap, fmt);
+ vsyslog(LOG_DEBUG, fmt, ap);
+ va_end(ap);
+}
+
+void
+dump_static_object(object, size_object, hbuff, size_hbuff, cbuff, size_cbuff)
+ const unsigned char *object;
+ const int size_object;
+ unsigned char *hbuff;
+ const int size_hbuff;
+ unsigned char *cbuff;
+ const int size_cbuff;
+{
+ int i, objectsize;
+
+ if (debug_level < 2) {
+ return;
+ }
+
+ objectsize = size_object;
+
+ if (objectsize == 0) {
+ debuglog("object is size 0\n");
+ } else {
+ if (objectsize > MAXOBJECTSIZE) {
+ debuglog("Object of size %d being clamped"
+ "to size %d\n", objectsize, MAXOBJECTSIZE);
+ objectsize = MAXOBJECTSIZE;
+ }
+
+ if (hbuff != NULL) {
+ if (size_hbuff < objectsize*2+1) {
+ debuglog("Hbuff not large enough."
+ " Increase size\n");
+ } else {
+ for(i=0;i<objectsize;i++) {
+ sprintf(hbuff+i*2,"%02x",*(object+i));
+ }
+ *(hbuff+i*2) = '\0';
+ }
+ }
+
+ if (cbuff != NULL) {
+ if (size_cbuff < objectsize+1) {
+ debuglog("Cbuff not large enough."
+ " Increase Size\n");
+ }
+
+ for(i=0;i<objectsize;i++) {
+ if (*(object+i) >= 32 && *(object+i) <= 127) {
+ *(cbuff+i) = *(object+i);
+ } else {
+ *(cbuff+i) = '.';
+ }
+ }
+ *(cbuff+i) = '\0';
+ }
+ }
+}
+
+void
+dump_netobj(const struct netobj *nobj)
+{
+ char hbuff[MAXBUFFERSIZE*2];
+ char cbuff[MAXBUFFERSIZE];
+
+ if (debug_level < 2) {
+ return;
+ }
+
+ if (nobj == NULL) {
+ debuglog("Null netobj pointer\n");
+ }
+ else if (nobj->n_len == 0) {
+ debuglog("Size zero netobj\n");
+ } else {
+ dump_static_object(nobj->n_bytes, nobj->n_len,
+ hbuff, sizeof(hbuff), cbuff, sizeof(cbuff));
+ debuglog("netobj: len: %d data: %s ::: %s\n",
+ nobj->n_len, hbuff, cbuff);
+ }
+}
+
+/* #define DUMP_FILELOCK_VERBOSE */
+void
+dump_filelock(const struct file_lock *fl)
+{
+#ifdef DUMP_FILELOCK_VERBOSE
+ char hbuff[MAXBUFFERSIZE*2];
+ char cbuff[MAXBUFFERSIZE];
+#endif
+
+ if (debug_level < 2) {
+ return;
+ }
+
+ if (fl != NULL) {
+ debuglog("Dumping file lock structure @ %p\n", fl);
+
+#ifdef DUMP_FILELOCK_VERBOSE
+ dump_static_object((unsigned char *)&fl->filehandle,
+ sizeof(fl->filehandle), hbuff, sizeof(hbuff),
+ cbuff, sizeof(cbuff));
+ debuglog("Filehandle: %8s ::: %8s\n", hbuff, cbuff);
+#endif
+
+ debuglog("Dumping nlm4_holder:\n"
+ "exc: %x svid: %x offset:len %llx:%llx\n",
+ fl->client.exclusive, fl->client.svid,
+ fl->client.l_offset, fl->client.l_len);
+
+#ifdef DUMP_FILELOCK_VERBOSE
+ debuglog("Dumping client identity:\n");
+ dump_netobj(&fl->client.oh);
+
+ debuglog("Dumping client cookie:\n");
+ dump_netobj(&fl->client_cookie);
+
+ debuglog("nsm: %d status: %d flags: %d locker: %d"
+ " fd: %d\n", fl->nsm_status, fl->status,
+ fl->flags, fl->locker, fl->fd);
+#endif
+ } else {
+ debuglog("NULL file lock structure\n");
+ }
+}
+
+void
+copy_nlm4_lock_to_nlm4_holder(src, exclusive, dest)
+ const struct nlm4_lock *src;
+ const bool_t exclusive;
+ struct nlm4_holder *dest;
+{
+
+ dest->exclusive = exclusive;
+ dest->oh.n_len = src->oh.n_len;
+ dest->oh.n_bytes = src->oh.n_bytes;
+ dest->svid = src->svid;
+ dest->l_offset = src->l_offset;
+ dest->l_len = src->l_len;
+}
+
+
+/*
+ * allocate_file_lock: Create a lock with the given parameters
+ */
+
+struct file_lock *
+allocate_file_lock(const netobj *lockowner, const netobj *matchcookie)
+{
+ struct file_lock *newfl;
+
+ newfl = malloc(sizeof(struct file_lock));
+ if (newfl == NULL) {
+ return NULL;
+ }
+ bzero(newfl, sizeof(newfl));
+
+ newfl->client.oh.n_bytes = malloc(lockowner->n_len);
+ if (newfl->client.oh.n_bytes == NULL) {
+ free(newfl);
+ return NULL;
+ }
+ newfl->client.oh.n_len = lockowner->n_len;
+ bcopy(lockowner->n_bytes, newfl->client.oh.n_bytes, lockowner->n_len);
+
+ newfl->client_cookie.n_bytes = malloc(matchcookie->n_len);
+ if (newfl->client_cookie.n_bytes == NULL) {
+ free(newfl->client.oh.n_bytes);
+ free(newfl);
+ return NULL;
+ }
+ newfl->client_cookie.n_len = matchcookie->n_len;
+ bcopy(matchcookie->n_bytes, newfl->client_cookie.n_bytes, matchcookie->n_len);
+
+ return newfl;
+}
+
+/*
+ * file_file_lock: Force creation of a valid file lock
+ */
+void
+fill_file_lock(struct file_lock *fl, const fhandle_t *fh,
+ struct sockaddr *addr, const bool_t exclusive, const int32_t svid,
+ const u_int64_t offset, const u_int64_t len, const char *caller_name,
+ const int state, const int status, const int flags, const int blocking)
+{
+ bcopy(fh, &fl->filehandle, sizeof(fhandle_t));
+ fl->addr = addr;
+
+ fl->client.exclusive = exclusive;
+ fl->client.svid = svid;
+ fl->client.l_offset = offset;
+ fl->client.l_len = len;
+
+ strncpy(fl->client_name, caller_name, SM_MAXSTRLEN);
+
+ fl->nsm_status = state;
+ fl->status = status;
+ fl->flags = flags;
+ fl->blocking = blocking;
+}
+
+/*
+ * deallocate_file_lock: Free all storage associated with a file lock
+ */
+void
+deallocate_file_lock(struct file_lock *fl)
+{
+ free(fl->client.oh.n_bytes);
+ free(fl->client_cookie.n_bytes);
+ free(fl);
+}
+
+/*
+ * regions_overlap(): This function examines the two provided regions for
+ * overlap.
+ */
+int
+regions_overlap(start1, len1, start2, len2)
+ const u_int64_t start1, len1, start2, len2;
+{
+ u_int64_t d1,d2,d3,d4;
+ enum split_status result;
+
+ debuglog("Entering region overlap with vals: %llu:%llu--%llu:%llu\n",
+ start1, len1, start2, len2);
+
+ result = region_compare(start1, len1, start2, len2,
+ &d1, &d2, &d3, &d4);
+
+ debuglog("Exiting region overlap with val: %d\n",result);
+
+ if (result == SPL_DISJOINT) {
+ return 0;
+ } else {
+ return 1;
+ }
+
+ return (result);
+}
+
+/*
+ * region_compare(): Examine lock regions and split appropriately
+ *
+ * XXX: Fix 64 bit overflow problems
+ * XXX: Check to make sure I got *ALL* the cases.
+ * XXX: This DESPERATELY needs a regression test.
+ */
+enum split_status
+region_compare(starte, lene, startu, lenu,
+ start1, len1, start2, len2)
+ const u_int64_t starte, lene, startu, lenu;
+ u_int64_t *start1, *len1, *start2, *len2;
+{
+ /*
+ * Please pay attention to the sequential exclusions
+ * of the if statements!!!
+ */
+ enum LFLAGS lflags;
+ enum RFLAGS rflags;
+ enum split_status retval;
+
+ retval = SPL_DISJOINT;
+
+ if (lene == 0 && lenu == 0) {
+ /* Examine left edge of locker */
+ if (startu < starte) {
+ lflags = LEDGE_LEFT;
+ } else if (startu == starte) {
+ lflags = LEDGE_LBOUNDARY;
+ } else {
+ lflags = LEDGE_INSIDE;
+ }
+
+ rflags = REDGE_RBOUNDARY; /* Both are infiinite */
+
+ if (lflags == LEDGE_INSIDE) {
+ *start1 = starte;
+ *len1 = startu - starte;
+ }
+
+ if (lflags == LEDGE_LEFT || lflags == LEDGE_LBOUNDARY) {
+ retval = SPL_CONTAINED;
+ } else {
+ retval = SPL_LOCK1;
+ }
+ } else if (lene == 0 && lenu != 0) {
+ /* Established lock is infinite */
+ /* Examine left edge of unlocker */
+ if (startu < starte) {
+ lflags = LEDGE_LEFT;
+ } else if (startu == starte) {
+ lflags = LEDGE_LBOUNDARY;
+ } else if (startu > starte) {
+ lflags = LEDGE_INSIDE;
+ }
+
+ /* Examine right edge of unlocker */
+ if (startu + lenu < starte) {
+ /* Right edge of unlocker left of established lock */
+ rflags = REDGE_LEFT;
+ return SPL_DISJOINT;
+ } else if (startu + lenu == starte) {
+ /* Right edge of unlocker on start of established lock */
+ rflags = REDGE_LBOUNDARY;
+ return SPL_DISJOINT;
+ } else { /* Infinifty is right of finity */
+ /* Right edge of unlocker inside established lock */
+ rflags = REDGE_INSIDE;
+ }
+
+ if (lflags == LEDGE_INSIDE) {
+ *start1 = starte;
+ *len1 = startu - starte;
+ retval |= SPL_LOCK1;
+ }
+
+ if (rflags == REDGE_INSIDE) {
+ /* Create right lock */
+ *start2 = startu+lenu;
+ *len2 = 0;
+ retval |= SPL_LOCK2;
+ }
+ } else if (lene != 0 && lenu == 0) {
+ /* Unlocker is infinite */
+ /* Examine left edge of unlocker */
+ if (startu < starte) {
+ lflags = LEDGE_LEFT;
+ retval = SPL_CONTAINED;
+ return retval;
+ } else if (startu == starte) {
+ lflags = LEDGE_LBOUNDARY;
+ retval = SPL_CONTAINED;
+ return retval;
+ } else if ((startu > starte) && (startu < starte + lene - 1)) {
+ lflags = LEDGE_INSIDE;
+ } else if (startu == starte + lene - 1) {
+ lflags = LEDGE_RBOUNDARY;
+ } else { /* startu > starte + lene -1 */
+ lflags = LEDGE_RIGHT;
+ return SPL_DISJOINT;
+ }
+
+ rflags = REDGE_RIGHT; /* Infinity is right of finity */
+
+ if (lflags == LEDGE_INSIDE || lflags == LEDGE_RBOUNDARY) {
+ *start1 = starte;
+ *len1 = startu - starte;
+ retval |= SPL_LOCK1;
+ return retval;
+ }
+
+ } else {
+ /* Both locks are finite */
+
+ /* Examine left edge of unlocker */
+ if (startu < starte) {
+ lflags = LEDGE_LEFT;
+ } else if (startu == starte) {
+ lflags = LEDGE_LBOUNDARY;
+ } else if ((startu > starte) && (startu < starte + lene - 1)) {
+ lflags = LEDGE_INSIDE;
+ } else if (startu == starte + lene - 1) {
+ lflags = LEDGE_RBOUNDARY;
+ } else { /* startu > starte + lene -1 */
+ lflags = LEDGE_RIGHT;
+ return SPL_DISJOINT;
+ }
+
+ /* Examine right edge of unlocker */
+ if (startu + lenu < starte) {
+ /* Right edge of unlocker left of established lock */
+ rflags = REDGE_LEFT;
+ return SPL_DISJOINT;
+ } else if (startu + lenu == starte) {
+ /* Right edge of unlocker on start of established lock */
+ rflags = REDGE_LBOUNDARY;
+ return SPL_DISJOINT;
+ } else if (startu + lenu < starte + lene) {
+ /* Right edge of unlocker inside established lock */
+ rflags = REDGE_INSIDE;
+ } else if (startu + lenu == starte + lene) {
+ /* Right edge of unlocker on right edge of established lock */
+ rflags = REDGE_RBOUNDARY;
+ } else { /* startu + lenu > starte + lene */
+ /* Right edge of unlocker is right of established lock */
+ rflags = REDGE_RIGHT;
+ }
+
+ if (lflags == LEDGE_INSIDE || lflags == LEDGE_RBOUNDARY) {
+ /* Create left lock */
+ *start1 = starte;
+ *len1 = (startu - starte);
+ retval |= SPL_LOCK1;
+ }
+
+ if (rflags == REDGE_INSIDE) {
+ /* Create right lock */
+ *start2 = startu+lenu;
+ *len2 = starte+lene-(startu+lenu);
+ retval |= SPL_LOCK2;
+ }
+
+ if ((lflags == LEDGE_LEFT || lflags == LEDGE_LBOUNDARY) &&
+ (rflags == REDGE_RBOUNDARY || rflags == REDGE_RIGHT)) {
+ retval = SPL_CONTAINED;
+ }
+ }
+
+ return retval;
+}
+
+/*
+ * same_netobj: Compares the apprpriate bits of a netobj for identity
+ */
+int
+same_netobj(const netobj *n0, const netobj *n1)
+{
+ int retval;
+
+ retval = 0;
+
+ debuglog("Entering netobj identity check\n");
+
+ if (n0->n_len == n1->n_len) {
+ debuglog("Preliminary length check passed\n");
+ retval = !bcmp(n0->n_bytes, n1->n_bytes, n0->n_len);
+ debuglog("netobj %smatch\n", retval ? "" : "mis");
+ }
+
+ return (retval);
+}
+
+/*
+ * same_filelock_identity: Compares the appropriate bits of a file_lock
+ */
+int
+same_filelock_identity(fl0, fl1)
+ const struct file_lock *fl0, *fl1;
+{
+ int retval;
+
+ retval = 0;
+
+ debuglog("Checking filelock identity\n");
+
+ /*
+ * Check process ids and host information.
+ */
+ retval = (fl0->client.svid == fl1->client.svid &&
+ same_netobj(&(fl0->client.oh), &(fl1->client.oh)));
+
+ debuglog("Exiting checking filelock identity: retval: %d\n",retval);
+
+ return (retval);
+}
+
+/*
+ * Below here are routines associated with manipulating the NFS
+ * lock list.
+ */
+
+/*
+ * get_lock_matching_unlock: Return a lock which matches the given unlock lock
+ * or NULL otehrwise
+ * XXX: It is a shame that this duplicates so much code from test_nfslock.
+ */
+struct file_lock *
+get_lock_matching_unlock(const struct file_lock *fl)
+{
+ struct file_lock *ifl; /* Iterator */
+
+ debuglog("Entering lock_matching_unlock\n");
+ debuglog("********Dump of fl*****************\n");
+ dump_filelock(fl);
+
+ LIST_FOREACH(ifl, &nfslocklist_head, nfslocklist) {
+ debuglog("Pointer to file lock: %p\n",ifl);
+
+ debuglog("****Dump of ifl****\n");
+ dump_filelock(ifl);
+ debuglog("*******************\n");
+
+ /*
+ * XXX: It is conceivable that someone could use the NLM RPC
+ * system to directly access filehandles. This may be a
+ * security hazard as the filehandle code may bypass normal
+ * file access controls
+ */
+ if (bcmp(&fl->filehandle, &ifl->filehandle, sizeof(fhandle_t)))
+ continue;
+
+ debuglog("matching_unlock: Filehandles match, "
+ "checking regions\n");
+
+ /* Filehandles match, check for region overlap */
+ if (!regions_overlap(fl->client.l_offset, fl->client.l_len,
+ ifl->client.l_offset, ifl->client.l_len))
+ continue;
+
+ debuglog("matching_unlock: Region overlap"
+ " found %llu : %llu -- %llu : %llu\n",
+ fl->client.l_offset,fl->client.l_len,
+ ifl->client.l_offset,ifl->client.l_len);
+
+ /* Regions overlap, check the identity */
+ if (!same_filelock_identity(fl,ifl))
+ continue;
+
+ debuglog("matching_unlock: Duplicate lock id. Granting\n");
+ return (ifl);
+ }
+
+ debuglog("Exiting lock_matching_unlock\n");
+
+ return (NULL);
+}
+
+/*
+ * test_nfslock: check for NFS lock in lock list
+ *
+ * This routine makes the following assumptions:
+ * 1) Nothing will adjust the lock list during a lookup
+ *
+ * This routine has an intersting quirk which bit me hard.
+ * The conflicting_fl is the pointer to the conflicting lock.
+ * However, to modify the "*pointer* to the conflicting lock" rather
+ * that the "conflicting lock itself" one must pass in a "pointer to
+ * the pointer of the conflicting lock". Gross.
+ */
+
+enum nfslock_status
+test_nfslock(const struct file_lock *fl, struct file_lock **conflicting_fl)
+{
+ struct file_lock *ifl; /* Iterator */
+ enum nfslock_status retval;
+
+ debuglog("Entering test_nfslock\n");
+
+ retval = NFS_GRANTED;
+ (*conflicting_fl) = NULL;
+
+ debuglog("Entering lock search loop\n");
+
+ debuglog("***********************************\n");
+ debuglog("Dumping match filelock\n");
+ debuglog("***********************************\n");
+ dump_filelock(fl);
+ debuglog("***********************************\n");
+
+ LIST_FOREACH(ifl, &nfslocklist_head, nfslocklist) {
+ if (retval == NFS_DENIED)
+ break;
+
+ debuglog("Top of lock loop\n");
+ debuglog("Pointer to file lock: %p\n",ifl);
+
+ debuglog("***********************************\n");
+ debuglog("Dumping test filelock\n");
+ debuglog("***********************************\n");
+ dump_filelock(ifl);
+ debuglog("***********************************\n");
+
+ /*
+ * XXX: It is conceivable that someone could use the NLM RPC
+ * system to directly access filehandles. This may be a
+ * security hazard as the filehandle code may bypass normal
+ * file access controls
+ */
+ if (bcmp(&fl->filehandle, &ifl->filehandle, sizeof(fhandle_t)))
+ continue;
+
+ debuglog("test_nfslock: filehandle match found\n");
+
+ /* Filehandles match, check for region overlap */
+ if (!regions_overlap(fl->client.l_offset, fl->client.l_len,
+ ifl->client.l_offset, ifl->client.l_len))
+ continue;
+
+ debuglog("test_nfslock: Region overlap found"
+ " %llu : %llu -- %llu : %llu\n",
+ fl->client.l_offset,fl->client.l_len,
+ ifl->client.l_offset,ifl->client.l_len);
+
+ /* Regions overlap, check the exclusivity */
+ if (!(fl->client.exclusive || ifl->client.exclusive))
+ continue;
+
+ debuglog("test_nfslock: Exclusivity failure: %d %d\n",
+ fl->client.exclusive,
+ ifl->client.exclusive);
+
+ if (same_filelock_identity(fl,ifl)) {
+ debuglog("test_nfslock: Duplicate id. Granting\n");
+ (*conflicting_fl) = ifl;
+ retval = NFS_GRANTED_DUPLICATE;
+ } else {
+ /* locking attempt fails */
+ debuglog("test_nfslock: Lock attempt failed\n");
+ debuglog("Desired lock\n");
+ dump_filelock(fl);
+ debuglog("Conflicting lock\n");
+ dump_filelock(ifl);
+ (*conflicting_fl) = ifl;
+ retval = NFS_DENIED;
+ }
+ }
+
+ debuglog("Dumping file locks\n");
+ debuglog("Exiting test_nfslock\n");
+
+ return (retval);
+}
+
+/*
+ * lock_nfslock: attempt to create a lock in the NFS lock list
+ *
+ * This routine tests whether the lock will be granted and then adds
+ * the entry to the lock list if so.
+ *
+ * Argument fl gets modified as its list housekeeping entries get modified
+ * upon insertion into the NFS lock list
+ *
+ * This routine makes several assumptions:
+ * 1) It is perfectly happy to grant a duplicate lock from the same pid.
+ * While this seems to be intuitively wrong, it is required for proper
+ * Posix semantics during unlock. It is absolutely imperative to not
+ * unlock the main lock before the two child locks are established. Thus,
+ * one has be be able to create duplicate locks over an existing lock
+ * 2) It currently accepts duplicate locks from the same id,pid
+ */
+
+enum nfslock_status
+lock_nfslock(struct file_lock *fl)
+{
+ enum nfslock_status retval;
+ struct file_lock *dummy_fl;
+
+ dummy_fl = NULL;
+
+ debuglog("Entering lock_nfslock...\n");
+
+ retval = test_nfslock(fl,&dummy_fl);
+
+ if (retval == NFS_GRANTED || retval == NFS_GRANTED_DUPLICATE) {
+ debuglog("Inserting lock...\n");
+ dump_filelock(fl);
+ LIST_INSERT_HEAD(&nfslocklist_head, fl, nfslocklist);
+ }
+
+ debuglog("Exiting lock_nfslock...\n");
+
+ return (retval);
+}
+
+/*
+ * delete_nfslock: delete an NFS lock list entry
+ *
+ * This routine is used to delete a lock out of the NFS lock list
+ * without regard to status, underlying locks, regions or anything else
+ *
+ * Note that this routine *does not deallocate memory* of the lock.
+ * It just disconnects it from the list. The lock can then be used
+ * by other routines without fear of trashing the list.
+ */
+
+enum nfslock_status
+delete_nfslock(struct file_lock *fl)
+{
+
+ LIST_REMOVE(fl, nfslocklist);
+
+ return (NFS_GRANTED);
+}
+
+enum split_status
+split_nfslock(exist_lock, unlock_lock, left_lock, right_lock)
+ const struct file_lock *exist_lock, *unlock_lock;
+ struct file_lock **left_lock, **right_lock;
+{
+ u_int64_t start1, len1, start2, len2;
+ enum split_status spstatus;
+
+ spstatus = region_compare(exist_lock->client.l_offset, exist_lock->client.l_len,
+ unlock_lock->client.l_offset, unlock_lock->client.l_len,
+ &start1, &len1, &start2, &len2);
+
+ if ((spstatus & SPL_LOCK1) != 0) {
+ *left_lock = allocate_file_lock(&exist_lock->client.oh, &exist_lock->client_cookie);
+ if (*left_lock == NULL) {
+ debuglog("Unable to allocate resource for split 1\n");
+ return SPL_RESERR;
+ }
+
+ fill_file_lock(*left_lock, &exist_lock->filehandle,
+ exist_lock->addr,
+ exist_lock->client.exclusive, exist_lock->client.svid,
+ start1, len1,
+ exist_lock->client_name, exist_lock->nsm_status,
+ exist_lock->status, exist_lock->flags, exist_lock->blocking);
+ }
+
+ if ((spstatus & SPL_LOCK2) != 0) {
+ *right_lock = allocate_file_lock(&exist_lock->client.oh, &exist_lock->client_cookie);
+ if (*right_lock == NULL) {
+ debuglog("Unable to allocate resource for split 1\n");
+ if (*left_lock != NULL) {
+ deallocate_file_lock(*left_lock);
+ }
+ return SPL_RESERR;
+ }
+
+ fill_file_lock(*right_lock, &exist_lock->filehandle,
+ exist_lock->addr,
+ exist_lock->client.exclusive, exist_lock->client.svid,
+ start2, len2,
+ exist_lock->client_name, exist_lock->nsm_status,
+ exist_lock->status, exist_lock->flags, exist_lock->blocking);
+ }
+
+ return spstatus;
+}
+
+enum nfslock_status
+unlock_nfslock(fl, released_lock, left_lock, right_lock)
+ const struct file_lock *fl;
+ struct file_lock **released_lock;
+ struct file_lock **left_lock;
+ struct file_lock **right_lock;
+{
+ struct file_lock *mfl; /* Matching file lock */
+ enum nfslock_status retval;
+ enum split_status spstatus;
+
+ debuglog("Entering unlock_nfslock\n");
+
+ *released_lock = NULL;
+ *left_lock = NULL;
+ *right_lock = NULL;
+
+ retval = NFS_DENIED_NOLOCK;
+
+ printf("Attempting to match lock...\n");
+ mfl = get_lock_matching_unlock(fl);
+
+ if (mfl != NULL) {
+ debuglog("Unlock matched. Querying for split\n");
+
+ spstatus = split_nfslock(mfl, fl, left_lock, right_lock);
+
+ debuglog("Split returned %d %p %p %p %p\n",spstatus,mfl,fl,*left_lock,*right_lock);
+ debuglog("********Split dumps********");
+ dump_filelock(mfl);
+ dump_filelock(fl);
+ dump_filelock(*left_lock);
+ dump_filelock(*right_lock);
+ debuglog("********End Split dumps********");
+
+ if (spstatus == SPL_RESERR) {
+ if (*left_lock != NULL) {
+ deallocate_file_lock(*left_lock);
+ *left_lock = NULL;
+ }
+
+ if (*right_lock != NULL) {
+ deallocate_file_lock(*right_lock);
+ *right_lock = NULL;
+ }
+
+ return NFS_RESERR;
+ }
+
+ /* Insert new locks from split if required */
+ if (*left_lock != NULL) {
+ debuglog("Split left activated\n");
+ LIST_INSERT_HEAD(&nfslocklist_head, *left_lock, nfslocklist);
+ }
+
+ if (*right_lock != NULL) {
+ debuglog("Split right activated\n");
+ LIST_INSERT_HEAD(&nfslocklist_head, *right_lock, nfslocklist);
+ }
+
+ /* Unlock the lock since it matches identity */
+ LIST_REMOVE(mfl, nfslocklist);
+ *released_lock = mfl;
+ retval = NFS_GRANTED;
+ }
+
+ debuglog("Exiting unlock_nfslock\n");
+
+ return retval;
+}
+
+/*
+ * Below here are the routines for manipulating the file lock directly
+ * on the disk hardware itself
+ */
+enum hwlock_status
+lock_hwlock(struct file_lock *fl)
+{
+ struct monfile *imf,*nmf;
+ int lflags, flerror;
+
+ /* Scan to see if filehandle already present */
+ LIST_FOREACH(imf, &monfilelist_head, monfilelist) {
+ if (bcmp(&fl->filehandle, &imf->filehandle,
+ sizeof(fl->filehandle)) == 0) {
+ /* imf is the correct filehandle */
+ break;
+ }
+ }
+
+ /*
+ * Filehandle already exists (we control the file)
+ * *AND* NFS has already cleared the lock for availability
+ * Grant it and bump the refcount.
+ */
+ if (imf != NULL) {
+ ++(imf->refcount);
+ return (HW_GRANTED);
+ }
+
+ /* No filehandle found, create and go */
+ nmf = malloc(sizeof(struct monfile));
+ if (nmf == NULL) {
+ debuglog("hwlock resource allocation failure\n");
+ return (HW_RESERR);
+ }
+
+ /* XXX: Is O_RDWR always the correct mode? */
+ nmf->fd = fhopen(&fl->filehandle, O_RDWR);
+ if (nmf->fd < 0) {
+ debuglog("fhopen failed (from %16s): %32s\n",
+ fl->client_name, strerror(errno));
+ free(nmf);
+ switch (errno) {
+ case ESTALE:
+ return (HW_STALEFH);
+ case EROFS:
+ return (HW_READONLY);
+ default:
+ return (HW_RESERR);
+ }
+ }
+
+ /* File opened correctly, fill the monitor struct */
+ bcopy(&fl->filehandle, &nmf->filehandle, sizeof(fl->filehandle));
+ nmf->refcount = 1;
+ nmf->exclusive = fl->client.exclusive;
+
+ lflags = (nmf->exclusive == 1) ?
+ (LOCK_EX | LOCK_NB) : (LOCK_SH | LOCK_NB);
+
+ flerror = flock(nmf->fd, lflags);
+
+ if (flerror != 0) {
+ debuglog("flock failed (from %16s): %32s\n",
+ fl->client_name, strerror(errno));
+ close(nmf->fd);
+ free(nmf);
+ switch (errno) {
+ case EAGAIN:
+ return (HW_DENIED);
+ case ESTALE:
+ return (HW_STALEFH);
+ case EROFS:
+ return (HW_READONLY);
+ default:
+ return (HW_RESERR);
+ break;
+ }
+ }
+
+ /* File opened and locked */
+ LIST_INSERT_HEAD(&monfilelist_head, nmf, monfilelist);
+
+ debuglog("flock succeeded (from %16s)\n", fl->client_name);
+ return (HW_GRANTED);
+}
+
+enum hwlock_status
+unlock_hwlock(const struct file_lock *fl)
+{
+ struct monfile *imf;
+
+ debuglog("Entering unlock_hwlock\n");
+ debuglog("Entering loop interation\n");
+
+ /* Scan to see if filehandle already present */
+ LIST_FOREACH(imf, &monfilelist_head, monfilelist) {
+ if (bcmp(&fl->filehandle, &imf->filehandle,
+ sizeof(fl->filehandle)) == 0) {
+ /* imf is the correct filehandle */
+ break;
+ }
+ }
+
+ debuglog("Completed iteration. Proceeding\n");
+
+ if (imf == NULL) {
+ /* No lock found */
+ debuglog("Exiting unlock_hwlock (HW_DENIED_NOLOCK)\n");
+ return (HW_DENIED_NOLOCK);
+ }
+
+ /* Lock found */
+ --imf->refcount;
+
+ if (imf->refcount < 0) {
+ debuglog("Negative hardware reference count\n");
+ }
+
+ if (imf->refcount <= 0) {
+ close(imf->fd);
+ LIST_REMOVE(imf, monfilelist);
+ free(imf);
+ }
+ debuglog("Exiting unlock_hwlock (HW_GRANTED)\n");
+ return (HW_GRANTED);
+}
+
+enum hwlock_status
+test_hwlock(fl, conflicting_fl)
+ const struct file_lock *fl __unused;
+ struct file_lock **conflicting_fl __unused;
+{
+
+ /*
+ * XXX: lock tests on hardware are not required until
+ * true partial file testing is done on the underlying file
+ */
+ return (HW_RESERR);
+}
+
+
+
+/*
+ * Below here are routines for manipulating blocked lock requests
+ * They should only be called from the XXX_partialfilelock routines
+ * if at all possible
+ */
+
+void
+add_blockingfilelock(struct file_lock *fl)
+{
+
+ debuglog("Entering add_blockingfilelock\n");
+
+ /*
+ * Clear the blocking flag so that it can be reused without
+ * adding it to the blocking queue a second time
+ */
+
+ fl->blocking = 0;
+ LIST_INSERT_HEAD(&blockedlocklist_head, fl, nfslocklist);
+
+ debuglog("Exiting add_blockingfilelock\n");
+}
+
+void
+remove_blockingfilelock(struct file_lock *fl)
+{
+
+ debuglog("Entering remove_blockingfilelock\n");
+
+ LIST_REMOVE(fl, nfslocklist);
+
+ debuglog("Exiting remove_blockingfilelock\n");
+}
+
+void
+clear_blockingfilelock(const char *hostname)
+{
+ struct file_lock *ifl,*nfl;
+
+ /*
+ * Normally, LIST_FOREACH is called for, but since
+ * the current element *is* the iterator, deleting it
+ * would mess up the iteration. Thus, a next element
+ * must be used explicitly
+ */
+
+ ifl = LIST_FIRST(&blockedlocklist_head);
+
+ while (ifl != NULL) {
+ nfl = LIST_NEXT(ifl, nfslocklist);
+
+ if (strncmp(hostname, ifl->client_name, SM_MAXSTRLEN) == 0) {
+ remove_blockingfilelock(ifl);
+ deallocate_file_lock(ifl);
+ }
+
+ ifl = nfl;
+ }
+}
+
+void
+retry_blockingfilelocklist(void)
+{
+ /* Retry all locks in the blocked list */
+ struct file_lock *ifl, *nfl, *pfl; /* Iterator */
+ enum partialfilelock_status pflstatus;
+
+ debuglog("Entering retry_blockingfilelocklist\n");
+
+ pfl = NULL;
+ ifl = LIST_FIRST(&blockedlocklist_head);
+ debuglog("Iterator choice %p\n",ifl);
+
+ while (ifl != NULL) {
+ /*
+ * SUBTLE BUG: The next element must be worked out before the
+ * current element has been moved
+ */
+ nfl = LIST_NEXT(ifl, nfslocklist);
+ debuglog("Iterator choice %p\n",ifl);
+ debuglog("Prev iterator choice %p\n",pfl);
+ debuglog("Next iterator choice %p\n",nfl);
+
+ /*
+ * SUBTLE BUG: The file_lock must be removed from the
+ * old list so that it's list pointers get disconnected
+ * before being allowed to participate in the new list
+ * which will automatically add it in if necessary.
+ */
+
+ LIST_REMOVE(ifl, nfslocklist);
+ pflstatus = lock_partialfilelock(ifl);
+
+ if (pflstatus == PFL_GRANTED || pflstatus == PFL_GRANTED_DUPLICATE) {
+ debuglog("Granted blocked lock\n");
+ /* lock granted and is now being used */
+ send_granted(ifl,0);
+ } else {
+ /* Reinsert lock back into same place in blocked list */
+ debuglog("Replacing blocked lock\n");
+ if (pfl != NULL)
+ LIST_INSERT_AFTER(pfl, ifl, nfslocklist);
+ else
+ /* ifl is the only elem. in the list */
+ LIST_INSERT_HEAD(&blockedlocklist_head, ifl, nfslocklist);
+ }
+
+ /* Valid increment behavior regardless of state of ifl */
+ ifl = nfl;
+ /* if a lock was granted incrementing pfl would make it nfl */
+ if (pfl != NULL && (LIST_NEXT(pfl, nfslocklist) != nfl))
+ pfl = LIST_NEXT(pfl, nfslocklist);
+ else
+ pfl = LIST_FIRST(&blockedlocklist_head);
+ }
+
+ debuglog("Exiting retry_blockingfilelocklist\n");
+}
+
+/*
+ * Below here are routines associated with manipulating all
+ * aspects of the partial file locking system (list, hardware, etc.)
+ */
+
+/*
+ * Please note that lock monitoring must be done at this level which
+ * keeps track of *individual* lock requests on lock and unlock
+ *
+ * XXX: Split unlocking is going to make the unlock code miserable
+ */
+
+/*
+ * lock_partialfilelock:
+ *
+ * Argument fl gets modified as its list housekeeping entries get modified
+ * upon insertion into the NFS lock list
+ *
+ * This routine makes several assumptions:
+ * 1) It (will) pass locks through to flock to lock the entire underlying file
+ * and then parcel out NFS locks if it gets control of the file.
+ * This matches the old rpc.lockd file semantics (except where it
+ * is now more correct). It is the safe solution, but will cause
+ * overly restrictive blocking if someone is trying to use the
+ * underlying files without using NFS. This appears to be an
+ * acceptable tradeoff since most people use standalone NFS servers.
+ * XXX: The right solution is probably kevent combined with fcntl
+ *
+ * 2) Nothing modifies the lock lists between testing and granting
+ * I have no idea whether this is a useful assumption or not
+ */
+
+enum partialfilelock_status
+lock_partialfilelock(struct file_lock *fl)
+{
+ enum partialfilelock_status retval;
+ enum nfslock_status lnlstatus;
+ enum hwlock_status hwstatus;
+
+ debuglog("Entering lock_partialfilelock\n");
+
+ retval = PFL_DENIED;
+
+ /*
+ * Execute the NFS lock first, if possible, as it is significantly
+ * easier and less expensive to undo than the filesystem lock
+ */
+
+ lnlstatus = lock_nfslock(fl);
+
+ switch (lnlstatus) {
+ case NFS_GRANTED:
+ case NFS_GRANTED_DUPLICATE:
+ /*
+ * At this point, the NFS lock is allocated and active.
+ * Remember to clean it up if the hardware lock fails
+ */
+ hwstatus = lock_hwlock(fl);
+
+ switch (hwstatus) {
+ case HW_GRANTED:
+ case HW_GRANTED_DUPLICATE:
+ debuglog("HW GRANTED\n");
+ /*
+ * XXX: Fixme: Check hwstatus for duplicate when
+ * true partial file locking and accounting is
+ * done on the hardware
+ */
+ if (lnlstatus == NFS_GRANTED_DUPLICATE) {
+ retval = PFL_GRANTED_DUPLICATE;
+ } else {
+ retval = PFL_GRANTED;
+ }
+ monitor_lock_host(fl->client_name);
+ break;
+ case HW_RESERR:
+ debuglog("HW RESERR\n");
+ retval = PFL_HWRESERR;
+ break;
+ case HW_DENIED:
+ debuglog("HW DENIED\n");
+ retval = PFL_HWDENIED;
+ break;
+ default:
+ debuglog("Unmatched hwstatus %d\n",hwstatus);
+ break;
+ }
+
+ if (retval != PFL_GRANTED &&
+ retval != PFL_GRANTED_DUPLICATE) {
+ /* Clean up the NFS lock */
+ debuglog("Deleting trial NFS lock\n");
+ delete_nfslock(fl);
+ }
+ break;
+ case NFS_DENIED:
+ retval = PFL_NFSDENIED;
+ break;
+ case NFS_RESERR:
+ retval = PFL_NFSRESERR;
+ default:
+ debuglog("Unmatched lnlstatus %d\n");
+ retval = PFL_NFSDENIED_NOLOCK;
+ break;
+ }
+
+ /*
+ * By the time fl reaches here, it is completely free again on
+ * failure. The NFS lock done before attempting the
+ * hardware lock has been backed out
+ */
+
+ if (retval == PFL_NFSDENIED || retval == PFL_HWDENIED) {
+ /* Once last chance to check the lock */
+ if (fl->blocking == 1) {
+ /* Queue the lock */
+ debuglog("BLOCKING LOCK RECEIVED\n");
+ retval = (retval == PFL_NFSDENIED ?
+ PFL_NFSBLOCKED : PFL_HWBLOCKED);
+ add_blockingfilelock(fl);
+ dump_filelock(fl);
+ } else {
+ /* Leave retval alone, it's already correct */
+ debuglog("Lock denied. Non-blocking failure\n");
+ dump_filelock(fl);
+ }
+ }
+
+ debuglog("Exiting lock_partialfilelock\n");
+
+ return retval;
+}
+
+/*
+ * unlock_partialfilelock:
+ *
+ * Given a file_lock, unlock all locks which match.
+ *
+ * Note that a given lock might have to unlock ITSELF! See
+ * clear_partialfilelock for example.
+ */
+
+enum partialfilelock_status
+unlock_partialfilelock(const struct file_lock *fl)
+{
+ struct file_lock *lfl,*rfl,*releasedfl,*selffl;
+ enum partialfilelock_status retval;
+ enum nfslock_status unlstatus;
+ enum hwlock_status unlhwstatus, lhwstatus;
+
+ debuglog("Entering unlock_partialfilelock\n");
+
+ selffl = NULL;
+ lfl = NULL;
+ rfl = NULL;
+ releasedfl = NULL;
+ retval = PFL_DENIED;
+
+ /*
+ * There are significant overlap and atomicity issues
+ * with partially releasing a lock. For example, releasing
+ * part of an NFS shared lock does *not* always release the
+ * corresponding part of the file since there is only one
+ * rpc.lockd UID but multiple users could be requesting it
+ * from NFS. Also, an unlock request should never allow
+ * another process to gain a lock on the remaining parts.
+ * ie. Always apply the new locks before releasing the
+ * old one
+ */
+
+ /*
+ * Loop is required since multiple little locks
+ * can be allocated and then deallocated with one
+ * big unlock.
+ *
+ * The loop is required to be here so that the nfs &
+ * hw subsystems do not need to communicate with one
+ * one another
+ */
+
+ do {
+ debuglog("Value of releasedfl: %p\n",releasedfl);
+ /* lfl&rfl are created *AND* placed into the NFS lock list if required */
+ unlstatus = unlock_nfslock(fl, &releasedfl, &lfl, &rfl);
+ debuglog("Value of releasedfl: %p\n",releasedfl);
+
+
+ /* XXX: This is grungy. It should be refactored to be cleaner */
+ if (lfl != NULL) {
+ lhwstatus = lock_hwlock(lfl);
+ if (lhwstatus != HW_GRANTED &&
+ lhwstatus != HW_GRANTED_DUPLICATE) {
+ debuglog("HW duplicate lock failure for left split\n");
+ }
+ monitor_lock_host(lfl->client_name);
+ }
+
+ if (rfl != NULL) {
+ lhwstatus = lock_hwlock(rfl);
+ if (lhwstatus != HW_GRANTED &&
+ lhwstatus != HW_GRANTED_DUPLICATE) {
+ debuglog("HW duplicate lock failure for right split\n");
+ }
+ monitor_lock_host(rfl->client_name);
+ }
+
+ switch (unlstatus) {
+ case NFS_GRANTED:
+ /* Attempt to unlock on the hardware */
+ debuglog("NFS unlock granted. Attempting hardware unlock\n");
+
+ /* This call *MUST NOT* unlock the two newly allocated locks */
+ unlhwstatus = unlock_hwlock(fl);
+ debuglog("HW unlock returned with code %d\n",unlhwstatus);
+
+ switch (unlhwstatus) {
+ case HW_GRANTED:
+ debuglog("HW unlock granted\n");
+ unmonitor_lock_host(releasedfl->client_name);
+ retval = PFL_GRANTED;
+ break;
+ case HW_DENIED_NOLOCK:
+ /* Huh?!?! This shouldn't happen */
+ debuglog("HW unlock denied no lock\n");
+ retval = PFL_HWRESERR;
+ /* Break out of do-while */
+ unlstatus = NFS_RESERR;
+ break;
+ default:
+ debuglog("HW unlock failed\n");
+ retval = PFL_HWRESERR;
+ /* Break out of do-while */
+ unlstatus = NFS_RESERR;
+ break;
+ }
+
+ debuglog("Exiting with status retval: %d\n",retval);
+
+ retry_blockingfilelocklist();
+ break;
+ case NFS_DENIED_NOLOCK:
+ retval = PFL_GRANTED;
+ debuglog("All locks cleaned out\n");
+ break;
+ default:
+ retval = PFL_NFSRESERR;
+ debuglog("NFS unlock failure\n");
+ dump_filelock(fl);
+ break;
+ }
+
+ if (releasedfl != NULL) {
+ if (fl == releasedfl) {
+ /*
+ * XXX: YECHHH!!! Attempt to unlock self succeeded
+ * but we can't deallocate the space yet. This is what
+ * happens when you don't write malloc and free together
+ */
+ debuglog("Attempt to unlock self\n");
+ selffl = releasedfl;
+ } else {
+ /*
+ * XXX: this deallocation *still* needs to migrate closer
+ * to the allocation code way up in get_lock or the allocation
+ * code needs to migrate down (violation of "When you write
+ * malloc you must write free")
+ */
+
+ deallocate_file_lock(releasedfl);
+ }
+ }
+
+ } while (unlstatus == NFS_GRANTED);
+
+ if (selffl != NULL) {
+ /*
+ * This statement wipes out the incoming file lock (fl)
+ * in spite of the fact that it is declared const
+ */
+ debuglog("WARNING! Destroying incoming lock pointer\n");
+ deallocate_file_lock(selffl);
+ }
+
+ debuglog("Exiting unlock_partialfilelock\n");
+
+ return retval;
+}
+
+/*
+ * clear_partialfilelock
+ *
+ * Normally called in response to statd state number change.
+ * Wipe out all locks held by a host. As a bonus, the act of
+ * doing so should automatically clear their statd entries and
+ * unmonitor the host.
+ */
+
+void
+clear_partialfilelock(const char *hostname)
+{
+ struct file_lock *ifl, *nfl;
+
+ /* Clear blocking file lock list */
+ clear_blockingfilelock(hostname);
+
+ /* do all required unlocks */
+ /* Note that unlock can smash the current pointer to a lock */
+
+ /*
+ * Normally, LIST_FOREACH is called for, but since
+ * the current element *is* the iterator, deleting it
+ * would mess up the iteration. Thus, a next element
+ * must be used explicitly
+ */
+
+ ifl = LIST_FIRST(&nfslocklist_head);
+
+ while (ifl != NULL) {
+ nfl = LIST_NEXT(ifl, nfslocklist);
+
+ if (strncmp(hostname, ifl->client_name, SM_MAXSTRLEN) == 0) {
+ /* Unlock destroys ifl out from underneath */
+ unlock_partialfilelock(ifl);
+ /* ifl is NO LONGER VALID AT THIS POINT */
+ }
+ ifl = nfl;
+ }
+}
+
+/*
+ * test_partialfilelock:
+ */
+enum partialfilelock_status
+test_partialfilelock(const struct file_lock *fl,
+ struct file_lock **conflicting_fl)
+{
+ enum partialfilelock_status retval;
+ enum nfslock_status teststatus;
+
+ debuglog("Entering testpartialfilelock...\n");
+
+ retval = PFL_DENIED;
+
+ teststatus = test_nfslock(fl, conflicting_fl);
+ debuglog("test_partialfilelock: teststatus %d\n",teststatus);
+
+ if (teststatus == NFS_GRANTED || teststatus == NFS_GRANTED_DUPLICATE) {
+ /* XXX: Add the underlying filesystem locking code */
+ retval = (teststatus == NFS_GRANTED) ?
+ PFL_GRANTED : PFL_GRANTED_DUPLICATE;
+ debuglog("Dumping locks...\n");
+ dump_filelock(fl);
+ dump_filelock(*conflicting_fl);
+ debuglog("Done dumping locks...\n");
+ } else {
+ retval = PFL_NFSDENIED;
+ debuglog("NFS test denied.\n");
+ dump_filelock(fl);
+ debuglog("Conflicting.\n");
+ dump_filelock(*conflicting_fl);
+ }
+
+ debuglog("Exiting testpartialfilelock...\n");
+
+ return retval;
+}
+
+/*
+ * Below here are routines associated with translating the partial file locking
+ * codes into useful codes to send back to the NFS RPC messaging system
+ */
+
+/*
+ * These routines translate the (relatively) useful return codes back onto
+ * the few return codes which the nlm subsystems wishes to trasmit
+ */
+
+enum nlm_stats
+do_test(struct file_lock *fl, struct file_lock **conflicting_fl)
+{
+ enum partialfilelock_status pfsret;
+ enum nlm_stats retval;
+
+ debuglog("Entering do_test...\n");
+
+ pfsret = test_partialfilelock(fl,conflicting_fl);
+
+ switch (pfsret) {
+ case PFL_GRANTED:
+ debuglog("PFL test lock granted\n");
+ dump_filelock(fl);
+ dump_filelock(*conflicting_fl);
+ retval = (fl->flags & LOCK_V4) ? nlm4_granted : nlm_granted;
+ break;
+ case PFL_GRANTED_DUPLICATE:
+ debuglog("PFL test lock granted--duplicate id detected\n");
+ dump_filelock(fl);
+ dump_filelock(*conflicting_fl);
+ debuglog("Clearing conflicting_fl for call semantics\n");
+ *conflicting_fl = NULL;
+ retval = (fl->flags & LOCK_V4) ? nlm4_granted : nlm_granted;
+ break;
+ case PFL_NFSDENIED:
+ case PFL_HWDENIED:
+ debuglog("PFL test lock denied\n");
+ dump_filelock(fl);
+ dump_filelock(*conflicting_fl);
+ retval = (fl->flags & LOCK_V4) ? nlm4_denied : nlm_denied;
+ break;
+ case PFL_NFSRESERR:
+ case PFL_HWRESERR:
+ debuglog("PFL test lock resource fail\n");
+ dump_filelock(fl);
+ dump_filelock(*conflicting_fl);
+ retval = (fl->flags & LOCK_V4) ? nlm4_denied_nolocks : nlm_denied_nolocks;
+ break;
+ default:
+ debuglog("PFL test lock *FAILED*\n");
+ dump_filelock(fl);
+ dump_filelock(*conflicting_fl);
+ retval = (fl->flags & LOCK_V4) ? nlm4_failed : nlm_denied;
+ break;
+ }
+
+ debuglog("Exiting do_test...\n");
+
+ return retval;
+}
+
+/*
+ * do_lock: Try to acquire a lock
+ *
+ * This routine makes a distinction between NLM versions. I am pretty
+ * convinced that this should be abstracted out and bounced up a level
+ */
+
+enum nlm_stats
+do_lock(struct file_lock *fl)
+{
+ enum partialfilelock_status pfsret;
+ enum nlm_stats retval;
+
+ debuglog("Entering do_lock...\n");
+
+ pfsret = lock_partialfilelock(fl);
+
+ switch (pfsret) {
+ case PFL_GRANTED:
+ debuglog("PFL lock granted");
+ dump_filelock(fl);
+ retval = (fl->flags & LOCK_V4) ? nlm4_granted : nlm_granted;
+ break;
+ case PFL_GRANTED_DUPLICATE:
+ debuglog("PFL lock granted--duplicate id detected");
+ dump_filelock(fl);
+ retval = (fl->flags & LOCK_V4) ? nlm4_granted : nlm_granted;
+ break;
+ case PFL_NFSDENIED:
+ case PFL_HWDENIED:
+ debuglog("PFL_NFS lock denied");
+ dump_filelock(fl);
+ retval = (fl->flags & LOCK_V4) ? nlm4_denied : nlm_denied;
+ break;
+ case PFL_NFSBLOCKED:
+ case PFL_HWBLOCKED:
+ debuglog("PFL_NFS blocking lock denied. Queued.\n");
+ dump_filelock(fl);
+ retval = (fl->flags & LOCK_V4) ? nlm4_blocked : nlm_blocked;
+ break;
+ case PFL_NFSRESERR:
+ case PFL_HWRESERR:
+ debuglog("PFL lock resource alocation fail\n");
+ dump_filelock(fl);
+ retval = (fl->flags & LOCK_V4) ? nlm4_denied_nolocks : nlm_denied_nolocks;
+ break;
+ default:
+ debuglog("PFL lock *FAILED*");
+ dump_filelock(fl);
+ retval = (fl->flags & LOCK_V4) ? nlm4_failed : nlm_denied;
+ break;
+ }
+
+ debuglog("Exiting do_lock...\n");
+
+ return retval;
+}
+
+enum nlm_stats
+do_unlock(struct file_lock *fl)
+{
+ enum partialfilelock_status pfsret;
+ enum nlm_stats retval;
+
+ debuglog("Entering do_unlock...\n");
+ pfsret = unlock_partialfilelock(fl);
+
+ switch (pfsret) {
+ case PFL_GRANTED:
+ debuglog("PFL unlock granted");
+ dump_filelock(fl);
+ retval = (fl->flags & LOCK_V4) ? nlm4_granted : nlm_granted;
+ break;
+ case PFL_NFSDENIED:
+ case PFL_HWDENIED:
+ debuglog("PFL_NFS unlock denied");
+ dump_filelock(fl);
+ retval = (fl->flags & LOCK_V4) ? nlm4_denied : nlm_denied;
+ break;
+ case PFL_NFSDENIED_NOLOCK:
+ case PFL_HWDENIED_NOLOCK:
+ debuglog("PFL_NFS no lock found\n");
+ retval = (fl->flags & LOCK_V4) ? nlm4_granted : nlm_granted;
+ break;
+ case PFL_NFSRESERR:
+ case PFL_HWRESERR:
+ debuglog("PFL unlock resource failure");
+ dump_filelock(fl);
+ retval = (fl->flags & LOCK_V4) ? nlm4_denied_nolocks : nlm_denied_nolocks;
+ break;
+ default:
+ debuglog("PFL unlock *FAILED*");
+ dump_filelock(fl);
+ retval = (fl->flags & LOCK_V4) ? nlm4_failed : nlm_denied;
+ break;
+ }
+
+ debuglog("Exiting do_unlock...\n");
+
+ return retval;
+}
+
+/*
+ * do_clear
+ *
+ * This routine is non-existent because it doesn't have a return code.
+ * It is here for completeness in case someone *does* need to do return
+ * codes later. A decent compiler should optimize this away.
+ */
+
+void
+do_clear(const char *hostname)
+{
+
+ clear_partialfilelock(hostname);
+}
+
+/*
+ * The following routines are all called from the code which the
+ * RPC layer invokes
+ */
+
+/*
+ * testlock(): inform the caller if the requested lock would be granted
+ *
+ * returns NULL if lock would granted
+ * returns pointer to a conflicting nlm4_holder if not
+ */
+
+struct nlm4_holder *
+testlock(struct nlm4_lock *lock, bool_t exclusive, int flags __unused)
+{
+ struct file_lock test_fl, *conflicting_fl;
+
+ bzero(&test_fl, sizeof(test_fl));
+
+ bcopy(lock->fh.n_bytes, &(test_fl.filehandle), sizeof(fhandle_t));
+ copy_nlm4_lock_to_nlm4_holder(lock, exclusive, &test_fl.client);
+
+ siglock();
+ do_test(&test_fl, &conflicting_fl);
+
+ if (conflicting_fl == NULL) {
+ debuglog("No conflicting lock found\n");
+ sigunlock();
+ return NULL;
+ } else {
+ debuglog("Found conflicting lock\n");
+ dump_filelock(conflicting_fl);
+ sigunlock();
+ return (&conflicting_fl->client);
+ }
+}
+
+/*
+ * getlock: try to aquire the lock.
+ * If file is already locked and we can sleep, put the lock in the list with
+ * status LKST_WAITING; it'll be processed later.
+ * Otherwise try to lock. If we're allowed to block, fork a child which
+ * will do the blocking lock.
+ */
+
+enum nlm_stats
+getlock(nlm4_lockargs *lckarg, struct svc_req *rqstp, const int flags)
+{
+ struct file_lock *newfl;
+ enum nlm_stats retval;
+
+ debuglog("Entering getlock...\n");
+
+ if (grace_expired == 0 && lckarg->reclaim == 0)
+ return (flags & LOCK_V4) ?
+ nlm4_denied_grace_period : nlm_denied_grace_period;
+
+ /* allocate new file_lock for this request */
+ newfl = allocate_file_lock(&lckarg->alock.oh, &lckarg->cookie);
+ if (newfl == NULL) {
+ syslog(LOG_NOTICE, "lock allocate failed: %s", strerror(errno));
+ /* failed */
+ return (flags & LOCK_V4) ?
+ nlm4_denied_nolocks : nlm_denied_nolocks;
+ }
+
+ if (lckarg->alock.fh.n_len != sizeof(fhandle_t)) {
+ debuglog("recieved fhandle size %d, local size %d",
+ lckarg->alock.fh.n_len, (int)sizeof(fhandle_t));
+ }
+
+ fill_file_lock(newfl, (fhandle_t *)lckarg->alock.fh.n_bytes,
+ (struct sockaddr *)svc_getrpccaller(rqstp->rq_xprt)->buf,
+ lckarg->exclusive, lckarg->alock.svid, lckarg->alock.l_offset,
+ lckarg->alock.l_len,
+ lckarg->alock.caller_name, lckarg->state, 0, flags, lckarg->block);
+
+ /*
+ * newfl is now fully constructed and deallocate_file_lock
+ * can now be used to delete it
+ */
+
+ siglock();
+ debuglog("Pointer to new lock is %p\n",newfl);
+
+ retval = do_lock(newfl);
+
+ debuglog("Pointer to new lock is %p\n",newfl);
+ sigunlock();
+
+ switch (retval)
+ {
+ case nlm4_granted:
+ /* case nlm_granted: is the same as nlm4_granted */
+ /* do_mon(lckarg->alock.caller_name); */
+ break;
+ case nlm4_blocked:
+ /* case nlm_blocked: is the same as nlm4_blocked */
+ /* do_mon(lckarg->alock.caller_name); */
+ break;
+ default:
+ deallocate_file_lock(newfl);
+ break;
+ }
+
+ debuglog("Exiting getlock...\n");
+
+ return retval;
+}
+
+
+/* unlock a filehandle */
+enum nlm_stats
+unlock(nlm4_lock *lock, const int flags __unused)
+{
+ struct file_lock fl;
+ enum nlm_stats err;
+
+ siglock();
+
+ debuglog("Entering unlock...\n");
+
+ bzero(&fl,sizeof(struct file_lock));
+ bcopy(lock->fh.n_bytes, &fl.filehandle, sizeof(fhandle_t));
+
+ copy_nlm4_lock_to_nlm4_holder(lock, 0, &fl.client);
+
+ err = do_unlock(&fl);
+
+ sigunlock();
+
+ debuglog("Exiting unlock...\n");
+
+ return err;
+}
+
+/*
+ * XXX: The following monitor/unmonitor routines
+ * have not been extensively tested (ie. no regression
+ * script exists like for the locking sections
+ */
+
+/*
+ * monitor_lock_host: monitor lock hosts locally with a ref count and
+ * inform statd
+ */
+void
+monitor_lock_host(const char *hostname)
+{
+ struct host *ihp, *nhp;
+ struct mon smon;
+ struct sm_stat_res sres;
+ int rpcret, statflag;
+
+ rpcret = 0;
+ statflag = 0;
+
+ LIST_FOREACH(ihp, &hostlst_head, hostlst) {
+ if (strncmp(hostname, ihp->name, SM_MAXSTRLEN) == 0) {
+ /* Host is already monitored, bump refcount */
+ ++ihp->refcnt;
+ /* Host should only be in the monitor list once */
+ return;
+ }
+ }
+
+ /* Host is not yet monitored, add it */
+ nhp = malloc(sizeof(struct host));
+
+ if (nhp == NULL) {
+ debuglog("Unable to allocate entry for statd mon\n");
+ return;
+ }
+
+ /* Allocated new host entry, now fill the fields */
+ strncpy(nhp->name, hostname, SM_MAXSTRLEN);
+ nhp->refcnt = 1;
+ debuglog("Locally Monitoring host %16s\n",hostname);
+
+ debuglog("Attempting to tell statd\n");
+
+ bzero(&smon,sizeof(smon));
+
+ smon.mon_id.mon_name = nhp->name;
+ smon.mon_id.my_id.my_name = "localhost\0";
+
+ smon.mon_id.my_id.my_prog = NLM_PROG;
+ smon.mon_id.my_id.my_vers = NLM_SM;
+ smon.mon_id.my_id.my_proc = NLM_SM_NOTIFY;
+
+ rpcret = callrpc("localhost", SM_PROG, SM_VERS, SM_MON, xdr_mon,
+ &smon, xdr_sm_stat_res, &sres);
+
+ if (rpcret == 0) {
+ if (sres.res_stat == stat_fail) {
+ debuglog("Statd call failed\n");
+ statflag = 0;
+ } else {
+ statflag = 1;
+ }
+ } else {
+ debuglog("Rpc call to statd failed with return value: %d\n",
+ rpcret);
+ statflag = 0;
+ }
+
+ if (statflag == 1) {
+ LIST_INSERT_HEAD(&hostlst_head, nhp, hostlst);
+ } else {
+ free(nhp);
+ }
+
+}
+
+/*
+ * unmonitor_lock_host: clear monitor ref counts and inform statd when gone
+ */
+void
+unmonitor_lock_host(const char *hostname)
+{
+ struct host *ihp;
+ struct mon_id smon_id;
+ struct sm_stat smstat;
+ int rpcret;
+
+ rpcret = 0;
+
+ for( ihp=LIST_FIRST(&hostlst_head); ihp != NULL;
+ ihp=LIST_NEXT(ihp, hostlst)) {
+ if (strncmp(hostname, ihp->name, SM_MAXSTRLEN) == 0) {
+ /* Host is monitored, bump refcount */
+ --ihp->refcnt;
+ /* Host should only be in the monitor list once */
+ break;
+ }
+ }
+
+ if (ihp == NULL) {
+ debuglog("Could not find host %16s in mon list\n", hostname);
+ return;
+ }
+
+ if (ihp->refcnt > 0)
+ return;
+
+ if (ihp->refcnt < 0) {
+ debuglog("Negative refcount!: %d\n",
+ ihp->refcnt);
+ }
+
+ debuglog("Attempting to unmonitor host %16s\n", hostname);
+
+ bzero(&smon_id,sizeof(smon_id));
+
+ smon_id.mon_name = hostname;
+ smon_id.my_id.my_name = "localhost";
+ smon_id.my_id.my_prog = NLM_PROG;
+ smon_id.my_id.my_vers = NLM_SM;
+ smon_id.my_id.my_proc = NLM_SM_NOTIFY;
+
+ rpcret = callrpc("localhost", SM_PROG, SM_VERS, SM_UNMON, xdr_mon,
+ &smon_id, xdr_sm_stat_res, &smstat);
+
+ if (rpcret != 0) {
+ debuglog("Rpc call to unmonitor statd failed with "
+ " return value: %d\n", rpcret);
+ }
+
+ LIST_REMOVE(ihp, hostlst);
+ free(ihp);
+}
+
+/*
+ * notify: Clear all locks from a host if statd complains
+ *
+ * XXX: This routine has not been thoroughly tested. However, neither
+ * had the old one been. It used to compare the statd crash state counter
+ * to the current lock state. The upshot of this was that it basically
+ * cleared all locks from the specified host 99% of the time (with the
+ * other 1% being a bug). Consequently, the assumption is that clearing
+ * all locks from a host when notified by statd is acceptable.
+ *
+ * Please note that this routine skips the usual level of redirection
+ * through a do_* type routine. This introduces a possible level of
+ * error and might better be written as do_notify and take this one out.
+
+ */
+
+void
+notify(const char *hostname, const int state)
+{
+ debuglog("notify from %s, new state %d", hostname, state);
+
+ siglock();
+ do_clear(hostname);
+ sigunlock();
+
+ debuglog("Leaving notify\n");
+}
+
+void
+send_granted(fl, opcode)
+ struct file_lock *fl;
+ int opcode __unused;
+{
+ CLIENT *cli;
+ static char dummy;
+ struct timeval timeo;
+ int success;
+ static struct nlm_res retval;
+ static struct nlm4_res retval4;
+
+ debuglog("About to send granted on blocked lock\n");
+ sleep(1);
+ debuglog("Blowing off return send\n");
+
+ cli = get_client(fl->addr,
+ (fl->flags & LOCK_V4) ? NLM_VERS4 : NLM_VERS);
+ if (cli == NULL) {
+ syslog(LOG_NOTICE, "failed to get CLIENT for %s",
+ fl->client_name);
+ /*
+ * We fail to notify remote that the lock has been granted.
+ * The client will timeout and retry, the lock will be
+ * granted at this time.
+ */
+ return;
+ }
+ timeo.tv_sec = 0;
+ timeo.tv_usec = (fl->flags & LOCK_ASYNC) ? 0 : 500000; /* 0.5s */
+
+ if (fl->flags & LOCK_V4) {
+ static nlm4_testargs res;
+ res.cookie = fl->client_cookie;
+ res.exclusive = fl->client.exclusive;
+ res.alock.caller_name = fl->client_name;
+ res.alock.fh.n_len = sizeof(fhandle_t);
+ res.alock.fh.n_bytes = (char*)&fl->filehandle;
+ res.alock.oh = fl->client.oh;
+ res.alock.svid = fl->client.svid;
+ res.alock.l_offset = fl->client.l_offset;
+ res.alock.l_len = fl->client.l_len;
+ debuglog("sending v4 reply%s",
+ (fl->flags & LOCK_ASYNC) ? " (async)":"");
+ if (fl->flags & LOCK_ASYNC) {
+ success = clnt_call(cli, NLM4_GRANTED_MSG,
+ xdr_nlm4_testargs, &res, xdr_void, &dummy, timeo);
+ } else {
+ success = clnt_call(cli, NLM4_GRANTED,
+ xdr_nlm4_testargs, &res, xdr_nlm4_res,
+ &retval4, timeo);
+ }
+ } else {
+ static nlm_testargs res;
+
+ res.cookie = fl->client_cookie;
+ res.exclusive = fl->client.exclusive;
+ res.alock.caller_name = fl->client_name;
+ res.alock.fh.n_len = sizeof(fhandle_t);
+ res.alock.fh.n_bytes = (char*)&fl->filehandle;
+ res.alock.oh = fl->client.oh;
+ res.alock.svid = fl->client.svid;
+ res.alock.l_offset = fl->client.l_offset;
+ res.alock.l_len = fl->client.l_len;
+ debuglog("sending v1 reply%s",
+ (fl->flags & LOCK_ASYNC) ? " (async)":"");
+ if (fl->flags & LOCK_ASYNC) {
+ success = clnt_call(cli, NLM_GRANTED_MSG,
+ xdr_nlm_testargs, &res, xdr_void, &dummy, timeo);
+ } else {
+ success = clnt_call(cli, NLM_GRANTED,
+ xdr_nlm_testargs, &res, xdr_nlm_res,
+ &retval, timeo);
+ }
+ }
+ if (debug_level > 2)
+ debuglog("clnt_call returns %d(%s) for granted",
+ success, clnt_sperrno(success));
+
+}
+
+/*
+ * Routines below here have not been modified in the overhaul
+ */
+
+/*
+ * Are these two routines still required since lockd is not spawning off
+ * children to service locks anymore? Presumably they were originally
+ * put in place to prevent a one child from changing the lock list out
+ * from under another one.
+ */
+
+void
+siglock(void)
+{
+ sigset_t block;
+
+ sigemptyset(&block);
+ sigaddset(&block, SIGCHLD);
+
+ if (sigprocmask(SIG_BLOCK, &block, NULL) < 0) {
+ syslog(LOG_WARNING, "siglock failed: %s", strerror(errno));
+ }
+}
+
+void
+sigunlock(void)
+{
+ sigset_t block;
+
+ sigemptyset(&block);
+ sigaddset(&block, SIGCHLD);
+
+ if (sigprocmask(SIG_UNBLOCK, &block, NULL) < 0) {
+ syslog(LOG_WARNING, "sigunlock failed: %s", strerror(errno));
+ }
+}
+
+
diff --git a/usr.sbin/rpc.lockd/lockd_lock.h b/usr.sbin/rpc.lockd/lockd_lock.h
new file mode 100644
index 0000000..62f6981
--- /dev/null
+++ b/usr.sbin/rpc.lockd/lockd_lock.h
@@ -0,0 +1,25 @@
+/* $NetBSD: lockd_lock.h,v 1.2 2000/06/09 14:00:54 fvdl Exp $ */
+/* $FreeBSD$ */
+
+/* Headers and function declarations for file-locking utilities */
+
+struct nlm4_holder * testlock(struct nlm4_lock *lock, bool_t exclusive,
+ int flags);
+enum nlm_stats getlock(nlm4_lockargs *lckarg, struct svc_req *rqstp,
+ const int flags);
+enum nlm_stats unlock(nlm4_lock *lock, const int flags);
+int lock_answer(int pid, netobj *netcookie, int result, int *pid_p,
+ int version);
+
+void notify(const char *hostname, const int state);
+
+/* flags for testlock, getlock & unlock */
+#define LOCK_ASYNC 0x01 /* async version (getlock only) */
+#define LOCK_V4 0x02 /* v4 version */
+#define LOCK_MON 0x04 /* monitored lock (getlock only) */
+#define LOCK_CANCEL 0x08 /* cancel, not unlock request (unlock only) */
+
+/* callbacks from lock_proc.c */
+void transmit_result(int, nlm_res *, struct sockaddr *);
+void transmit4_result(int, nlm4_res *, struct sockaddr *);
+CLIENT *get_client(struct sockaddr *, rpcvers_t);
diff --git a/usr.sbin/rpc.lockd/rpc.lockd.8 b/usr.sbin/rpc.lockd/rpc.lockd.8
new file mode 100644
index 0000000..9264050
--- /dev/null
+++ b/usr.sbin/rpc.lockd/rpc.lockd.8
@@ -0,0 +1,125 @@
+.\" $NetBSD: rpc.lockd.8,v 1.5 2000/06/09 18:51:47 cgd Exp $
+.\" $FreeBSD$
+.\"
+.\" Copyright (c) 1995 A.R.Gordon, andrew.gordon@net-tel.co.uk
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"
+.Dd September 24, 1995
+.Dt RPC.LOCKD 8
+.Os
+.Sh NAME
+.Nm rpc.lockd
+.Nd NFS file locking daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl d Ar debug_level
+.Op Fl g Ar grace period
+.Sh DESCRIPTION
+The
+.Nm
+daemon provides monitored and unmonitored file and record locking services
+in an NFS environment.
+To monitor the status of hosts requesting locks,
+the locking daemon typically operates in conjunction
+with
+.Xr rpc.statd 8 .
+.Pp
+Options and operands available for
+.Nm :
+.Bl -tag -width indent
+.It Fl d
+The
+.Fl d
+option causes debugging information to be written to syslog, recording
+all RPC transactions to the daemon.
+These messages are logged with level
+.Dv LOG_DEBUG
+and facility
+.Dv LOG_DAEMON .
+Specifying a
+.Ar debug_level
+of 1 results
+in the generation of one log line per protocol operation.
+Higher
+debug levels can be specified, causing display of operation arguments
+and internal operations of the daemon.
+.It Fl g
+The
+.Fl g
+option allow to specify the
+.Ar grace period ,
+in seconds.
+During the grace period
+.Nm
+only accepts requests from hosts which are reinitialising locks which
+existed before the server restart.
+Default is 30 seconds.
+.El
+.Pp
+Error conditions are logged to syslog, irrespective of the debug level,
+using log level
+.Dv LOG_ERR
+and facility
+.Dv LOG_DAEMON .
+.Pp
+The
+.Nm
+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 configured in
+.Xr rc.conf 5
+to run at system startup.
+.Sh FILES
+.Bl -tag -width /usr/include/rpcsvc/nlm_prot.x -compact
+.It Pa /usr/include/rpcsvc/nlm_prot.x
+RPC protocol specification for the network lock manager protocol.
+.El
+.Sh SEE ALSO
+.Xr syslog 3 ,
+.Xr rc.conf 5 ,
+.Xr rpc.statd 8
+.Sh BUGS
+The current implementation serialises locks requests that could be shared.
+.Sh STANDARDS
+The implementation is based on the specification in
+.Rs
+.%B "X/Open CAE Specification C218"
+.%T "Protocols for X/Open PC Interworking: XNFS, Issue 4"
+.%O ISBN 1 872630 66 9
+.Re
+.Sh HISTORY
+A version of
+.Nm
+appeared in
+.Tn SunOS
+4.
diff --git a/usr.sbin/rpc.lockd/test.c b/usr.sbin/rpc.lockd/test.c
new file mode 100644
index 0000000..a751e5c
--- /dev/null
+++ b/usr.sbin/rpc.lockd/test.c
@@ -0,0 +1,365 @@
+/* $NetBSD: test.c,v 1.2 1997/10/18 04:01:21 lukem Exp $ */
+
+#include <sys/cdefs.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/nlm_prot.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "from: @(#)nlm_prot.x 1.8 87/09/21 Copyr 1987 Sun Micro";
+static char sccsid[] = "from: * @(#)nlm_prot.x 2.1 88/08/01 4.0 RPCSRC";
+#else
+__RCSID("$NetBSD: test.c,v 1.2 1997/10/18 04:01:21 lukem Exp $");
+static const char rcsid[] = "$FreeBSD$";
+#endif
+#endif /* not lint */
+
+/* Default timeout can be changed using clnt_control() */
+static struct timeval TIMEOUT = { 0, 0 };
+
+nlm_testres *
+nlm_test_1(argp, clnt)
+ struct nlm_testargs *argp;
+ CLIENT *clnt;
+{
+ static nlm_testres res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_TEST, xdr_nlm_testargs, argp, xdr_nlm_testres, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+nlm_res *
+nlm_lock_1(argp, clnt)
+ struct nlm_lockargs *argp;
+ CLIENT *clnt;
+{
+ enum clnt_stat st;
+ static nlm_res res;
+
+ bzero((char *)&res, sizeof(res));
+ if (st = clnt_call(clnt, NLM_LOCK, xdr_nlm_lockargs, argp, xdr_nlm_res, &res, TIMEOUT) != RPC_SUCCESS) {
+ printf("clnt_call returns %d\n", st);
+ clnt_perror(clnt, "humbug");
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+nlm_res *
+nlm_cancel_1(argp, clnt)
+ struct nlm_cancargs *argp;
+ CLIENT *clnt;
+{
+ static nlm_res res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_CANCEL, xdr_nlm_cancargs, argp, xdr_nlm_res, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+nlm_res *
+nlm_unlock_1(argp, clnt)
+ struct nlm_unlockargs *argp;
+ CLIENT *clnt;
+{
+ static nlm_res res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_UNLOCK, xdr_nlm_unlockargs, argp, xdr_nlm_res, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+nlm_res *
+nlm_granted_1(argp, clnt)
+ struct nlm_testargs *argp;
+ CLIENT *clnt;
+{
+ static nlm_res res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_GRANTED, xdr_nlm_testargs, argp, xdr_nlm_res, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+void *
+nlm_test_msg_1(argp, clnt)
+ struct nlm_testargs *argp;
+ CLIENT *clnt;
+{
+ static char res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_TEST_MSG, xdr_nlm_testargs, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return ((void *)&res);
+}
+
+
+void *
+nlm_lock_msg_1(argp, clnt)
+ struct nlm_lockargs *argp;
+ CLIENT *clnt;
+{
+ static char res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_LOCK_MSG, xdr_nlm_lockargs, argp, xdr_void, NULL, TIMEOUT) != RPC_SUCCESS) {
+ clnt_perror(clnt, "nlm_lock_msg_1");
+ return (NULL);
+ }
+ return ((void *)&res);
+}
+
+
+void *
+nlm_cancel_msg_1(argp, clnt)
+ struct nlm_cancargs *argp;
+ CLIENT *clnt;
+{
+ static char res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_CANCEL_MSG, xdr_nlm_cancargs, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return ((void *)&res);
+}
+
+
+void *
+nlm_unlock_msg_1(argp, clnt)
+ struct nlm_unlockargs *argp;
+ CLIENT *clnt;
+{
+ static char res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_UNLOCK_MSG, xdr_nlm_unlockargs, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return ((void *)&res);
+}
+
+
+void *
+nlm_granted_msg_1(argp, clnt)
+ struct nlm_testargs *argp;
+ CLIENT *clnt;
+{
+ static char res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_GRANTED_MSG, xdr_nlm_testargs, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return ((void *)&res);
+}
+
+
+void *
+nlm_test_res_1(argp, clnt)
+ nlm_testres *argp;
+ CLIENT *clnt;
+{
+ static char res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_TEST_RES, xdr_nlm_testres, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return ((void *)&res);
+}
+
+
+void *
+nlm_lock_res_1(argp, clnt)
+ nlm_res *argp;
+ CLIENT *clnt;
+{
+ static char res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_LOCK_RES, xdr_nlm_res, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return ((void *)&res);
+}
+
+
+void *
+nlm_cancel_res_1(argp, clnt)
+ nlm_res *argp;
+ CLIENT *clnt;
+{
+ static char res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_CANCEL_RES, xdr_nlm_res, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return ((void *)&res);
+}
+
+
+void *
+nlm_unlock_res_1(argp, clnt)
+ nlm_res *argp;
+ CLIENT *clnt;
+{
+ static char res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_UNLOCK_RES, xdr_nlm_res, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return ((void *)&res);
+}
+
+
+void *
+nlm_granted_res_1(argp, clnt)
+ nlm_res *argp;
+ CLIENT *clnt;
+{
+ static char res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_GRANTED_RES, xdr_nlm_res, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return ((void *)&res);
+}
+
+
+nlm_shareres *
+nlm_share_3(argp, clnt)
+ nlm_shareargs *argp;
+ CLIENT *clnt;
+{
+ static nlm_shareres res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_SHARE, xdr_nlm_shareargs, argp, xdr_nlm_shareres, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+nlm_shareres *
+nlm_unshare_3(argp, clnt)
+ nlm_shareargs *argp;
+ CLIENT *clnt;
+{
+ static nlm_shareres res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_UNSHARE, xdr_nlm_shareargs, argp, xdr_nlm_shareres, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+nlm_res *
+nlm_nm_lock_3(argp, clnt)
+ nlm_lockargs *argp;
+ CLIENT *clnt;
+{
+ static nlm_res res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_NM_LOCK, xdr_nlm_lockargs, argp, xdr_nlm_res, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+void *
+nlm_free_all_3(argp, clnt)
+ nlm_notify *argp;
+ CLIENT *clnt;
+{
+ static char res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_FREE_ALL, xdr_nlm_notify, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return ((void *)&res);
+}
+
+
+int main(int argc, char **argv)
+{
+ CLIENT *cli;
+ nlm_res res_block;
+ nlm_res *out;
+ nlm_lockargs arg;
+ struct timeval tim;
+
+ printf("Creating client for host %s\n", argv[1]);
+ cli = clnt_create(argv[1], NLM_PROG, NLM_VERS, "udp");
+ if (!cli) {
+ errx(1, "Failed to create client\n");
+ /* NOTREACHED */
+ }
+ clnt_control(cli, CLGET_TIMEOUT, &tim);
+ printf("Default timeout was %d.%d\n", tim.tv_sec, tim.tv_usec);
+ tim.tv_usec = -1;
+ tim.tv_sec = -1;
+ clnt_control(cli, CLSET_TIMEOUT, &tim);
+ clnt_control(cli, CLGET_TIMEOUT, &tim);
+ printf("timeout now %d.%d\n", tim.tv_sec, tim.tv_usec);
+
+
+ arg.cookie.n_len = 4;
+ arg.cookie.n_bytes = "hello";
+ arg.block = 0;
+ arg.exclusive = 0;
+ arg.reclaim = 0;
+ arg.state = 0x1234;
+ arg.alock.caller_name = "localhost";
+ arg.alock.fh.n_len = 32;
+ arg.alock.fh.n_bytes = "\x04\x04\x02\x00\x01\x00\x00\x00\x0c\x00\x00\x00\xff\xff\xff\xd0\x16\x00\x00\x5b\x7c\xff\xff\xff\xec\x2f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x19\x54\xef\xbf\xd7\x94";
+ arg.alock.oh.n_len = 8;
+ arg.alock.oh.n_bytes = "\x00\x00\x02\xff\xff\xff\xd3";
+ arg.alock.svid = 0x5678;
+ arg.alock.l_offset = 0;
+ arg.alock.l_len = 100;
+
+ res_block.stat.stat = nlm_granted;
+ res_block.cookie.n_bytes = "hello";
+ res_block.cookie.n_len = 5;
+
+#if 0
+ if (nlm_lock_res_1(&res_block, cli))
+ printf("Success!\n");
+ else
+ printf("Fail\n");
+#else
+ if (out = nlm_lock_msg_1(&arg, cli)) {
+ printf("Success!\n");
+ printf("out->stat = %d", out->stat);
+ } else {
+ printf("Fail\n");
+ }
+#endif
+
+ return 0;
+}
diff --git a/usr.sbin/rpc.statd/Makefile b/usr.sbin/rpc.statd/Makefile
new file mode 100644
index 0000000..0eedee5
--- /dev/null
+++ b/usr.sbin/rpc.statd/Makefile
@@ -0,0 +1,26 @@
+# $FreeBSD$
+
+PROG= rpc.statd
+MAN= rpc.statd.8
+SRCS= file.c sm_inter_svc.c sm_inter.h statd.c procs.c
+
+CFLAGS+= -I.
+
+DPADD= ${LIBRPCSVC}
+LDADD= -lrpcsvc
+
+CLEANFILES= sm_inter_svc.c sm_inter.h
+
+RPCSRC= ${DESTDIR}/usr/include/rpcsvc/sm_inter.x
+RPCGEN= rpcgen -L -C
+
+sm_inter_svc.c: ${RPCSRC}
+ ${RPCGEN} -m -o ${.TARGET} ${RPCSRC}
+
+sm_inter.h: ${RPCSRC}
+ ${RPCGEN} -h -o ${.TARGET} ${RPCSRC}
+
+test: test.c
+ cc -o test test.c -lrpcsvc
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rpc.statd/file.c b/usr.sbin/rpc.statd/file.c
new file mode 100644
index 0000000..42d49c3
--- /dev/null
+++ b/usr.sbin/rpc.statd/file.c
@@ -0,0 +1,329 @@
+/*
+ * Copyright (c) 1995
+ * A.R. Gordon (andrew.gordon@net-tel.co.uk). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the FreeBSD project
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ANDREW GORDON AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ */
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/mman.h> /* For mmap() */
+#include <rpc/rpc.h>
+#include <syslog.h>
+
+#include "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++;
+ }
+}
+
+/* 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..b508f33
--- /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[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <rpc/rpc.h>
+#include <syslog.h>
+#include <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;
+ sm_status tx_arg; /* arg sent to callback procedure */
+ MonList *lp;
+ HostInfo *hp;
+ pid_t pid;
+
+ if (debug) syslog(LOG_DEBUG, "notify from host %s, new state %d",
+ arg->mon_name, arg->state);
+
+ hp = find_host(arg->mon_name, FALSE);
+ if (!hp)
+ {
+ /* Never heard of this host - why is it notifying us? */
+ syslog(LOG_ERR, "Unsolicited notification from host %s", arg->mon_name);
+ return;
+ }
+ 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_sm_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..901735e
--- /dev/null
+++ b/usr.sbin/rpc.statd/rpc.statd.8
@@ -0,0 +1,105 @@
+.\" -*- nroff -*-
+.\"
+.\" Copyright (c) 1995 A.R.Gordon, andrew.gordon@net-tel.co.uk
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd September 19, 1995
+.Dt RPC.STATD 8
+.Os
+.Sh NAME
+.Nm rpc.statd
+.Nd host status monitoring daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Sh DESCRIPTION
+.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.
+.Pp
+The protocol requires that symmetric monitor requests are made to both
+the local and remote daemon in order to establish a monitored relationship.
+This is convenient for the NFS locking protocol, but probably reduces the
+usefulness of the monitoring system for other applications.
+.Pp
+The current implementation uses more than 1Kbyte per monitored host in
+the status file (and also in VM). This may be inefficient for NFS servers
+with large numbers of clients.
+.Sh STANDARDS
+The implementation is based on the specification in X/Open CAE Specification
+C218, "Protocols for X/Open PC Interworking: XNFS, Issue 4", ISBN 1 872630 66 9
diff --git a/usr.sbin/rpc.statd/statd.c b/usr.sbin/rpc.statd/statd.c
new file mode 100644
index 0000000..3b47dfe
--- /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[] =
+ "$FreeBSD$";
+#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..7bf7b1d
--- /dev/null
+++ b/usr.sbin/rpc.statd/statd.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 1995
+ * A.R. Gordon (andrew.gordon@net-tel.co.uk). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the FreeBSD project
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ANDREW GORDON AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+
+
+#include "sm_inter.h"
+
+/* ------------------------------------------------------------------------- */
+/*
+ Data structures for recording monitored hosts
+
+ The information held by the status monitor comprises a list of hosts
+ that we have been asked to monitor, and, associated with each monitored
+ host, one or more clients to be called back if the monitored host crashes.
+
+ The list of monitored hosts must be retained over a crash, so that upon
+ re-boot we can call the SM_NOTIFY procedure in all those hosts so as to
+ cause them to start recovery processing. On the other hand, the client
+ call-backs are not required to be preserved: they are assumed (in the
+ protocol design) to be local processes which will have crashed when
+ we did, and so are discarded on restart.
+
+ We handle this by keeping the list of monitored hosts in a file
+ (/var/statd.state) which is mmap()ed and whose format is described
+ by the typedef FileLayout. The lists of client callbacks are chained
+ off this structure, but are held in normal memory and so will be
+ lost after a re-boot. Hence the actual values of MonList * pointers
+ in the copy on disc have no significance, but their NULL/non-NULL
+ status indicates whether this host is actually being monitored or if it
+ is an empty slot in the file.
+*/
+
+typedef struct MonList_s
+{
+ struct MonList_s *next; /* Next in list or NULL */
+ char notifyHost[SM_MAXSTRLEN + 1]; /* Host to notify */
+ int notifyProg; /* RPC program number to call */
+ int notifyVers; /* version number */
+ int notifyProc; /* procedure number */
+ unsigned char notifyData[16]; /* Opaque data from caller */
+} MonList;
+
+typedef struct
+{
+ char hostname[SM_MAXSTRLEN + 1]; /* Name of monitored host */
+ int notifyReqd; /* TRUE if we've crashed and not yet */
+ /* informed the monitored host */
+ MonList *monList; /* List of clients to inform if we */
+ /* hear that the monitored host has */
+ /* crashed, NULL if no longer monitored */
+} HostInfo;
+
+
+/* Overall file layout. */
+
+typedef struct
+{
+ int ourState; /* State number as defined in statd protocol */
+ int noOfHosts; /* Number of elements in hosts[] */
+ char reserved[248]; /* Reserved for future use */
+ HostInfo hosts[1]; /* vector of monitored hosts */
+} FileLayout;
+
+#define HEADER_LEN (sizeof(FileLayout) - sizeof(HostInfo))
+
+/* ------------------------------------------------------------------------- */
+
+/* Global variables */
+
+extern FileLayout *status_info; /* The mmap()ed status file */
+
+extern int debug; /* =1 to enable diagnostics to syslog */
+
+/* Function prototypes */
+
+extern HostInfo *find_host(char * /*hostname*/, int /*create*/);
+extern void init_file(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..6df1501
--- /dev/null
+++ b/usr.sbin/rpc.statd/test.c
@@ -0,0 +1,144 @@
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/sm_inter.h>
+
+
+/* Default timeout can be changed using clnt_control() */
+static struct timeval TIMEOUT = { 25, 0 };
+
+struct sm_stat_res *
+sm_stat_1(argp, clnt)
+ struct sm_name *argp;
+ CLIENT *clnt;
+{
+ static struct sm_stat_res res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, SM_STAT, xdr_sm_name, argp, xdr_sm_stat_res, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+struct sm_stat_res *
+sm_mon_1(argp, clnt)
+ struct mon *argp;
+ CLIENT *clnt;
+{
+ static struct sm_stat_res res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, SM_MON, xdr_mon, argp, xdr_sm_stat_res, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+struct sm_stat *
+sm_unmon_1(argp, clnt)
+ struct mon_id *argp;
+ CLIENT *clnt;
+{
+ static struct sm_stat res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, SM_UNMON, xdr_mon_id, argp, xdr_sm_stat, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+struct sm_stat *
+sm_unmon_all_1(argp, clnt)
+ struct my_id *argp;
+ CLIENT *clnt;
+{
+ static struct sm_stat res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, SM_UNMON_ALL, xdr_my_id, argp, xdr_sm_stat, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+void *
+sm_simu_crash_1(argp, clnt)
+ void *argp;
+ CLIENT *clnt;
+{
+ static char res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, SM_SIMU_CRASH, xdr_void, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return ((void *)&res);
+}
+
+
+int main(int argc, char **argv)
+{
+ CLIENT *cli;
+ char dummy;
+ void *out;
+ struct mon mon;
+
+ if (argc < 2)
+ {
+ fprintf(stderr, "usage: test <hostname> | crash\n");
+ fprintf(stderr, "always talks to statd at localhost\n");
+ exit(1);
+ }
+
+ printf("Creating client for localhost\n" );
+ cli = clnt_create("localhost", SM_PROG, SM_VERS, "udp");
+ if (!cli)
+ {
+ printf("Failed to create client\n");
+ exit(1);
+ }
+
+ mon.mon_id.mon_name = argv[1];
+ mon.mon_id.my_id.my_name = argv[1];
+ mon.mon_id.my_id.my_prog = SM_PROG;
+ mon.mon_id.my_id.my_vers = SM_VERS;
+ mon.mon_id.my_id.my_proc = 1; /* have it call sm_stat() !!! */
+
+ if (strcmp(argv[1], "crash"))
+ {
+ /* Hostname given */
+ struct sm_stat_res *res;
+ if (res = sm_mon_1(&mon, cli))
+ {
+ printf("Success!\n");
+ }
+ else
+ {
+ printf("Fail\n");
+ }
+ }
+ else
+ {
+ if (out = sm_simu_crash_1(&dummy, cli))
+ {
+ printf("Success!\n");
+ }
+ else
+ {
+ printf("Fail\n");
+ }
+ }
+
+ return 0;
+}
diff --git a/usr.sbin/rpc.umntall/Makefile b/usr.sbin/rpc.umntall/Makefile
new file mode 100644
index 0000000..1ea7fe6
--- /dev/null
+++ b/usr.sbin/rpc.umntall/Makefile
@@ -0,0 +1,8 @@
+# @(#)Makefile 8.4 (Berkeley) 6/22/95
+# $FreeBSD$
+
+PROG= rpc.umntall
+MAN= rpc.umntall.8
+SRCS= rpc.umntall.c mounttab.c
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rpc.umntall/mounttab.c b/usr.sbin/rpc.umntall/mounttab.c
new file mode 100644
index 0000000..01ce78c
--- /dev/null
+++ b/usr.sbin/rpc.umntall/mounttab.c
@@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 1999 Martin Blapp
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/syslog.h>
+
+#include <rpc/rpc.h>
+#include <nfs/rpcv2.h>
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "mounttab.h"
+
+struct mtablist *mtabhead;
+
+static void badline(char *field, char *bad);
+
+/*
+ * Add an entry to PATH_MOUNTTAB for each mounted NFS filesystem,
+ * so the client can notify the NFS server even after reboot.
+ */
+int
+add_mtab(char *hostp, char *dirp) {
+ FILE *mtabfile;
+
+ if ((mtabfile = fopen(PATH_MOUNTTAB, "a")) == NULL)
+ return (0);
+ else {
+ fprintf(mtabfile, "%ld\t%s\t%s\n",
+ (long)time(NULL), hostp, dirp);
+ fclose(mtabfile);
+ return (1);
+ }
+}
+
+/*
+ * Read mounttab line for line and return struct mtablist.
+ */
+int
+read_mtab() {
+ struct mtablist **mtabpp, *mtabp;
+ char *hostp, *dirp, *cp;
+ char str[STRSIZ];
+ char *timep, *endp;
+ time_t time;
+ u_long ultmp;
+ FILE *mtabfile;
+
+ if ((mtabfile = fopen(PATH_MOUNTTAB, "r")) == NULL) {
+ if (errno == ENOENT)
+ return (0);
+ else {
+ syslog(LOG_ERR, "can't open %s", PATH_MOUNTTAB);
+ return (0);
+ }
+ }
+ time = 0;
+ mtabpp = &mtabhead;
+ while (fgets(str, STRSIZ, mtabfile) != NULL) {
+ cp = str;
+ errno = 0;
+ if (*cp == '#' || *cp == ' ' || *cp == '\n')
+ continue;
+ timep = strsep(&cp, " \t\n");
+ if (timep == NULL || *timep == '\0') {
+ badline("time", timep);
+ continue;
+ }
+ hostp = strsep(&cp, " \t\n");
+ if (hostp == NULL || *hostp == '\0') {
+ badline("host", hostp);
+ continue;
+ }
+ dirp = strsep(&cp, " \t\n");
+ if (dirp == NULL || *dirp == '\0') {
+ badline("dir", dirp);
+ continue;
+ }
+ ultmp = strtoul(timep, &endp, 10);
+ if (ultmp == ULONG_MAX || *endp != '\0') {
+ badline("time", timep);
+ continue;
+ }
+ time = ultmp;
+ if ((mtabp = malloc(sizeof (struct mtablist))) == NULL) {
+ syslog(LOG_ERR, "malloc");
+ fclose(mtabfile);
+ return (0);
+ }
+ mtabp->mtab_time = time;
+ memmove(mtabp->mtab_host, hostp, RPCMNT_NAMELEN);
+ mtabp->mtab_host[RPCMNT_NAMELEN - 1] = '\0';
+ memmove(mtabp->mtab_dirp, dirp, RPCMNT_PATHLEN);
+ mtabp->mtab_dirp[RPCMNT_PATHLEN - 1] = '\0';
+ mtabp->mtab_next = (struct mtablist *)NULL;
+ *mtabpp = mtabp;
+ mtabpp = &mtabp->mtab_next;
+ }
+ fclose(mtabfile);
+ return (1);
+}
+
+/*
+ * Rewrite PATH_MOUNTTAB from scratch and skip bad entries.
+ * Unlink PATH_MOUNTAB if no entry is left.
+ */
+int
+write_mtab(int verbose) {
+ struct mtablist *mtabp, *mp;
+ FILE *mtabfile;
+ int line;
+
+ if ((mtabfile = fopen(PATH_MOUNTTAB, "w")) == NULL) {
+ syslog(LOG_ERR, "can't write to %s", PATH_MOUNTTAB);
+ return (0);
+ }
+ line = 0;
+ for (mtabp = mtabhead; mtabp != NULL; mtabp = mtabp->mtab_next) {
+ if (mtabp->mtab_host[0] == '\0')
+ continue;
+ /* Skip if a later (hence more recent) entry is identical. */
+ for (mp = mtabp->mtab_next; mp != NULL; mp = mp->mtab_next)
+ if (strcmp(mtabp->mtab_host, mp->mtab_host) == 0 &&
+ strcmp(mtabp->mtab_dirp, mp->mtab_dirp) == 0)
+ break;
+ if (mp != NULL)
+ continue;
+
+ fprintf(mtabfile, "%ld\t%s\t%s\n",
+ (long)mtabp->mtab_time, mtabp->mtab_host,
+ mtabp->mtab_dirp);
+ if (verbose)
+ warnx("write mounttab entry %s:%s",
+ mtabp->mtab_host, mtabp->mtab_dirp);
+ line++;
+ }
+ fclose(mtabfile);
+ if (line == 0) {
+ if (unlink(PATH_MOUNTTAB) == -1) {
+ syslog(LOG_ERR, "can't remove %s", PATH_MOUNTTAB);
+ return (0);
+ }
+ }
+ return (1);
+}
+
+/*
+ * Mark the entries as clean where RPC calls have been done successfully.
+ */
+void
+clean_mtab(char *hostp, char *dirp, int verbose) {
+ struct mtablist *mtabp;
+ char *host;
+
+ /* Copy hostp in case it points to an entry that we are zeroing out. */
+ host = strdup(hostp);
+ for (mtabp = mtabhead; mtabp != NULL; mtabp = mtabp->mtab_next) {
+ if (strcmp(mtabp->mtab_host, host) != 0)
+ continue;
+ if (dirp != NULL && strcmp(mtabp->mtab_dirp, dirp) != 0)
+ continue;
+
+ if (verbose)
+ warnx("delete mounttab entry%s %s:%s",
+ (dirp == NULL) ? " by host" : "",
+ mtabp->mtab_host, mtabp->mtab_dirp);
+ bzero(mtabp->mtab_host, RPCMNT_NAMELEN);
+ }
+ free(host);
+}
+
+/*
+ * Free struct mtablist mtab.
+ */
+void
+free_mtab() {
+ struct mtablist *mtabp;
+
+ while ((mtabp = mtabhead) != NULL) {
+ mtabhead = mtabhead->mtab_next;
+ free(mtabp);
+ }
+}
+
+/*
+ * Print bad lines to syslog.
+ */
+static void
+badline(char *field, char *bad) {
+ syslog(LOG_ERR, "bad mounttab %s field '%s'", field,
+ (bad == NULL) ? "<null>" : bad);
+}
diff --git a/usr.sbin/rpc.umntall/mounttab.h b/usr.sbin/rpc.umntall/mounttab.h
new file mode 100644
index 0000000..acf6c79
--- /dev/null
+++ b/usr.sbin/rpc.umntall/mounttab.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 1999 Martin Blapp
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define STRSIZ (RPCMNT_NAMELEN+RPCMNT_PATHLEN+100)
+#define PATH_MOUNTTAB "/var/db/mounttab"
+
+/* Structure for /var/db/mounttab */
+struct mtablist {
+ time_t mtab_time;
+ char mtab_host[RPCMNT_NAMELEN];
+ char mtab_dirp[RPCMNT_PATHLEN];
+ struct mtablist *mtab_next;
+};
+
+extern struct mtablist *mtabhead;
+
+int add_mtab(char *, char *);
+void clean_mtab(char *, char *, int);
+int read_mtab(void);
+int write_mtab(int);
+void free_mtab(void);
diff --git a/usr.sbin/rpc.umntall/rpc.umntall.8 b/usr.sbin/rpc.umntall/rpc.umntall.8
new file mode 100644
index 0000000..2aa128e
--- /dev/null
+++ b/usr.sbin/rpc.umntall/rpc.umntall.8
@@ -0,0 +1,125 @@
+.\"
+.\" Copyright (c) 1999 Martin Blapp
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd November 17, 1999
+.Dt RPC.UMNTALL 8
+.Os
+.Sh NAME
+.Nm rpc.umntall
+.Nd notify NFS servers about unmounted NFS filesystems
+.Sh SYNOPSIS
+.Nm
+.Op Fl e Ar expire
+.Op Fl h Ar host
+.Op Fl k
+.Op Fl p Ar remotepath
+.Op Fl v
+.Sh DESCRIPTION
+.Nm
+is as command as proposed in the
+.Tn NFS
+RPC specification; see
+.Rs
+.%T "NFS Version 3 Protocol Specification"
+.%O "RFC 1813, Appendix I"
+.Re
+It uses remote procedure calls to remove mount entries from
+.Pa /var/db/mountdtab
+on the remote NFS server.
+It is called automatically
+without any parameters during startup and shutdown of
+the system.
+This ensures that
+.Xr showmount 8
+does not display old and expired entries.
+The
+.Nm
+command
+is only needed on client side, where
+.Xr mount_nfs 8
+adds a mount entry with the current date to
+.Pa /var/db/mounttab ,
+and
+.Xr umount 8
+removes the entry again.
+The
+.Nm
+command
+cares about all remaining entries in this table which result from crashes
+or unproper shutdowns.
+.Pp
+The options are as follows:
+.Bl -tag -width indentxxx
+.It Fl e Ar expire
+All entries which are not actually mounted or older than
+.Ar expire
+(seconds) are removed from
+.Pa /var/db/mounttab .
+This may be the case
+for DNS changes or long out of service periods.
+Default expire time
+is 86400 seconds (one day).
+.It Fl h Ar host
+Only remove the specific hostname.
+Send a UMNTALL RPC to the NFS server.
+.It Fl k
+Keep entries for existing NFS filesystems.
+Compare the NFS filesystems from
+the mounttab against the kernel mountlist and do not send the RPC to
+existing mount entries.
+Useful during startup of the system.
+It may be
+possible that there are already mounted NFS filesystems, so calling
+RPC UMOUNT isn't a good idea.
+This is the case if the user has rebooted
+to 'single user mode' and starts up the system again.
+.It Fl p Ar path
+Only remove the specific mount-path.
+Send a UMOUNT RPC to the NFS server.
+This option implies the
+.Fl host
+option.
+.It Fl v
+Verbose, additional information is printed for each processed mounttab
+entry.
+.El
+.Sh FILES
+.Bl -tag -width /var/db/mounttab -compact
+.It Pa /var/db/mounttab
+mounted nfs-filesystems
+.El
+.Sh HISTORY
+The
+.Nm
+command first appeared in
+.Fx 4.0 .
+.Sh SEE ALSO
+.Xr mountd 8 ,
+.Xr mount_nfs 8 ,
+.Xr umount 8
+.Sh AUTHORS
+.An Martin Blapp Aq mb@imp.ch
diff --git a/usr.sbin/rpc.umntall/rpc.umntall.c b/usr.sbin/rpc.umntall/rpc.umntall.c
new file mode 100644
index 0000000..17a5d0a
--- /dev/null
+++ b/usr.sbin/rpc.umntall/rpc.umntall.c
@@ -0,0 +1,262 @@
+/*
+ * Copyright (c) 1999 Martin Blapp
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/ucred.h>
+#include <sys/mount.h>
+
+#include <rpc/rpc.h>
+#include <nfs/rpcv2.h>
+
+#include <err.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "mounttab.h"
+
+int verbose;
+
+static int do_umount (char *, char *);
+static int do_umntall (char *);
+static int is_mounted (char *, char *);
+static void usage (void);
+int xdr_dir (XDR *, char *);
+
+int
+main(int argc, char **argv) {
+ int ch, keep, success, pathlen;
+ time_t expire, now;
+ char *host, *path;
+ struct mtablist *mtab;
+
+ expire = 0;
+ host = path = NULL;
+ success = keep = verbose = 0;
+ while ((ch = getopt(argc, argv, "h:kp:ve:")) != -1)
+ switch (ch) {
+ case 'h':
+ host = optarg;
+ break;
+ case 'e':
+ expire = atoi(optarg);
+ break;
+ case 'k':
+ keep = 1;
+ break;
+ case 'p':
+ path = optarg;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case '?':
+ usage();
+ default:
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* Default expiretime is one day */
+ if (expire == 0)
+ expire = 86400;
+ time(&now);
+
+ /* Read PATH_MOUNTTAB. */
+ if (!read_mtab()) {
+ if (verbose)
+ warnx("no mounttab entries (%s does not exist)",
+ PATH_MOUNTTAB);
+ mtabhead = NULL;
+ }
+
+ if (host == NULL && path == NULL) {
+ /* Check each entry and do any necessary unmount RPCs. */
+ for (mtab = mtabhead; mtab != NULL; mtab = mtab->mtab_next) {
+ if (*mtab->mtab_host == '\0')
+ continue;
+ if (mtab->mtab_time + expire < now) {
+ /* Clear expired entry. */
+ if (verbose)
+ warnx("remove expired entry %s:%s",
+ mtab->mtab_host, mtab->mtab_dirp);
+ bzero(mtab->mtab_host,
+ sizeof(mtab->mtab_host));
+ continue;
+ }
+ if (keep && is_mounted(mtab->mtab_host,
+ mtab->mtab_dirp)) {
+ if (verbose)
+ warnx("skip entry %s:%s",
+ mtab->mtab_host, mtab->mtab_dirp);
+ continue;
+ }
+ if (do_umount(mtab->mtab_host, mtab->mtab_dirp)) {
+ if (verbose)
+ warnx("umount RPC for %s:%s succeeded",
+ mtab->mtab_host, mtab->mtab_dirp);
+ /* Remove all entries for this host + path. */
+ clean_mtab(mtab->mtab_host, mtab->mtab_dirp,
+ verbose);
+ }
+ }
+ success = 1;
+ } else {
+ if (host == NULL && path != NULL)
+ /* Missing hostname. */
+ usage();
+ if (path == NULL) {
+ /* Do a RPC UMNTALL for this specific host */
+ success = do_umntall(host);
+ if (verbose && success)
+ warnx("umntall RPC for %s succeeded", host);
+ } else {
+ /* Do a RPC UMNTALL for this specific mount */
+ for (pathlen = strlen(path);
+ pathlen > 1 && path[pathlen - 1] == '/'; pathlen--)
+ path[pathlen - 1] = '\0';
+ success = do_umount(host, path);
+ if (verbose && success)
+ warnx("umount RPC for %s:%s succeeded", host,
+ path);
+ }
+ /* If successful, remove any corresponding mounttab entries. */
+ if (success)
+ clean_mtab(host, path, verbose);
+ }
+ /* Write and unlink PATH_MOUNTTAB if necessary */
+ if (success)
+ success = write_mtab(verbose);
+ free_mtab();
+ exit (success ? 0 : 1);
+}
+
+/*
+ * Send a RPC_MNT UMNTALL request to hostname.
+ * XXX This works for all mountd implementations,
+ * but produces a RPC IOERR on non FreeBSD systems.
+ */
+int
+do_umntall(char *hostname) {
+ enum clnt_stat clnt_stat;
+ struct timeval try;
+ CLIENT *clp;
+
+ clp = clnt_create(hostname, RPCPROG_MNT, RPCMNT_VER1, "udp");
+ if (clp == NULL) {
+ warnx("%s: %s", hostname, clnt_spcreateerror("RPCPROG_MNT"));
+ return (0);
+ }
+ clp->cl_auth = authunix_create_default();
+ try.tv_sec = 3;
+ try.tv_usec = 0;
+ clnt_stat = clnt_call(clp, RPCMNT_UMNTALL, xdr_void, (caddr_t)0,
+ xdr_void, (caddr_t)0, try);
+ if (clnt_stat != RPC_SUCCESS)
+ warnx("%s: %s", hostname, clnt_sperror(clp, "RPCMNT_UMNTALL"));
+ auth_destroy(clp->cl_auth);
+ clnt_destroy(clp);
+ return (clnt_stat == RPC_SUCCESS);
+}
+
+/*
+ * Send a RPC_MNT UMOUNT request for dirp to hostname.
+ */
+int
+do_umount(char *hostname, char *dirp) {
+ enum clnt_stat clnt_stat;
+ struct timeval try;
+ CLIENT *clp;
+
+ clp = clnt_create(hostname, RPCPROG_MNT, RPCMNT_VER1, "udp");
+ if (clp == NULL) {
+ warnx("%s: %s", hostname, clnt_spcreateerror("RPCPROG_MNT"));
+ return (0);
+ }
+ clp->cl_auth = authsys_create_default();
+ try.tv_sec = 3;
+ try.tv_usec = 0;
+ clnt_stat = clnt_call(clp, RPCMNT_UMOUNT, xdr_dir, dirp,
+ xdr_void, (caddr_t)0, try);
+ if (clnt_stat != RPC_SUCCESS)
+ warnx("%s: %s", hostname, clnt_sperror(clp, "RPCMNT_UMOUNT"));
+ auth_destroy(clp->cl_auth);
+ clnt_destroy(clp);
+ return (clnt_stat == RPC_SUCCESS);
+}
+
+/*
+ * Check if the entry is still/already mounted.
+ */
+int
+is_mounted(char *hostname, char *dirp) {
+ struct statfs *mntbuf;
+ char name[MNAMELEN + 1];
+ size_t bufsize;
+ int mntsize, i;
+
+ if (strlen(hostname) + strlen(dirp) >= MNAMELEN)
+ return (0);
+ snprintf(name, sizeof(name), "%s:%s", hostname, dirp);
+ mntsize = getfsstat(NULL, 0, MNT_NOWAIT);
+ if (mntsize <= 0)
+ return (0);
+ bufsize = (mntsize + 1) * sizeof(struct statfs);
+ if ((mntbuf = malloc(bufsize)) == NULL)
+ err(1, "malloc");
+ mntsize = getfsstat(mntbuf, (long)bufsize, MNT_NOWAIT);
+ for (i = mntsize - 1; i >= 0; i--) {
+ if (strcmp(mntbuf[i].f_mntfromname, name) == 0) {
+ free(mntbuf);
+ return (1);
+ }
+ }
+ free(mntbuf);
+ return (0);
+}
+
+/*
+ * xdr routines for mount rpc's
+ */
+int
+xdr_dir(XDR *xdrsp, char *dirp) {
+ return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
+}
+
+static void
+usage() {
+ (void)fprintf(stderr, "%s\n",
+ "usage: rpc.umntall [-kv] [-e expire] [-h host] [-p path]");
+ exit(1);
+}
diff --git a/usr.sbin/rpc.yppasswdd/Makefile b/usr.sbin/rpc.yppasswdd/Makefile
new file mode 100644
index 0000000..871a578
--- /dev/null
+++ b/usr.sbin/rpc.yppasswdd/Makefile
@@ -0,0 +1,65 @@
+# $FreeBSD$
+
+RPCDIR= ${DESTDIR}/usr/include/rpcsvc
+
+.PATH: ${.CURDIR}/../../usr.sbin/ypserv ${.CURDIR}/../../usr.bin/chpass \
+ ${.CURDIR}/../../libexec/ypxfr ${RPCDIR}
+
+PROG= rpc.yppasswdd
+MAN= rpc.yppasswdd.8
+SRCS= util.c yp_access.c yp_dblookup.c yp_dbwrite.c \
+ yp_error.c yppasswdd_main.c yppasswdd_server.c ypxfr_misc.c ${GENSRCS}
+GENSRCS=yp.h yp_clnt.c yppasswd.h yppasswd_private.h yppasswd_private_svc.c \
+ yppasswd_private_xdr.c yppasswd_svc.c
+
+CFLAGS+= -I${.CURDIR}/../../usr.sbin/vipw \
+ -I${.CURDIR}/../../usr.sbin/ypserv \
+ -I${.CURDIR}/../../libexec/ypxfr \
+ -I${.CURDIR} -I.
+WARNS?= 4
+
+DPADD= ${LIBRPCSVC} ${LIBCRYPT} ${LIBUTIL}
+LDADD= -lrpcsvc -lcrypt -lutil
+
+CLEANFILES= ${GENSRCS}
+
+RPCGEN= rpcgen -I -C
+
+# We need to remove the 'static' keyword from _rpcsvcstate so that
+# yppasswdd_main.c can see it.
+yppasswd_svc.c: yppasswd.x
+ rm -f ${.TARGET}
+ ${RPCGEN} -m ${RPCDIR}/yppasswd.x | \
+ sed s/"static int _rpcsvcstate"/"int _rpcsvcstate"/g > ${.TARGET}
+
+yppasswd.h: yppasswd.x
+ rm -f ${.TARGET}
+ ${RPCGEN} -h -o ${.TARGET} ${RPCDIR}/yppasswd.x
+
+yp.h: yp.x
+ rm -f ${.TARGET}
+ ${RPCGEN} -h -o ${.TARGET} ${RPCDIR}/yp.x
+
+yp_clnt.c: yp.x
+ rm -f ${.TARGET}
+ ${RPCGEN} -DYPSERV_ONLY -l -o ${.TARGET} ${RPCDIR}/yp.x
+
+yppasswd_private.h: yppasswd_private.x
+ rm -f ${.TARGET}
+ ${RPCGEN} -h -o ${.TARGET} ${.CURDIR}/yppasswd_private.x
+
+yppasswd_private_xdr.c: yppasswd_private.x
+ rm -f ${.TARGET}
+ ${RPCGEN} -c -o ${.TARGET} ${.CURDIR}/yppasswd_private.x
+
+yppasswd_private_svc.c: yppasswd_private.x
+ rm -f ${.TARGET}
+ ${RPCGEN} -m ${.CURDIR}/yppasswd_private.x | \
+ sed s/"static int _rpcsvcstate = _IDLE"/"extern int _rpcsvcstate"/g > ${.TARGET}
+
+afterinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/yppwupdate \
+ ${DESTDIR}/usr/libexec/yppwupdate
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rpc.yppasswdd/rpc.yppasswdd.8 b/usr.sbin/rpc.yppasswdd/rpc.yppasswdd.8
new file mode 100644
index 0000000..46877c1
--- /dev/null
+++ b/usr.sbin/rpc.yppasswdd/rpc.yppasswdd.8
@@ -0,0 +1,356 @@
+.\" Copyright (c) 1995, 1996
+.\" Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Bill Paul.
+.\" 4. Neither the name of the author nor the names of contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd February 8, 1996
+.Dt RPC.YPPASSWDD 8
+.Os
+.Sh NAME
+.Nm rpc.yppasswdd
+.Nd "server for updating NIS passwords"
+.Sh SYNOPSIS
+.Nm
+.Op Fl t Ar master.passwd template file
+.Op Fl d Ar default domain
+.Op Fl p Ar path
+.Op Fl s
+.Op Fl f
+.Op Fl a
+.Op Fl m
+.Op Fl i
+.Op Fl v
+.Op Fl u
+.Op Fl h
+.Sh DESCRIPTION
+The
+.Nm
+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
+.Fx
+version of
+.Nm
+also allows the super-user on the NIS master server to perform more
+sophisticated updates on the NIS passwd maps.
+The super-user can modify
+any field in any user's master.passwd entry in any domain, and can
+do so without knowing the user's existing NIS password (when the server
+receives a request from the super-user, the password authentication
+check is bypassed). Furthermore, if the server is invoked with the
+.Fl a
+flag, the super-user can even add new entries to the maps using
+.Xr ypchpass 1 .
+Again, this only applies to the super-user on the NIS
+master server: none of these special functions can be 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
+.Fx
+.Nm
+attempts to overcome this problem in spite of the inherent limitations
+of the
+.Pa yppasswd
+protocol, which does not allow for a
+.Pa domain
+argument in client requests.
+In multi-domain mode,
+.Nm
+will search through all the passwd maps of all the domains it
+can find under
+.Pa /var/yp
+until it finds an entry that matches the user information specified in
+a given update request.
+(Matches are determined by checking the username,
+UID and GID fields.)
+The matched entry and corresponding domain are then
+used for the update.
+.Pp
+Note that in order for multi-domain mode to work, there have to be
+separate template files for each domain.
+For example, if a server
+supports three domains,
+.Pa foo ,
+.Pa bar ,
+and
+.Pa baz ,
+there should be three separate master.passwd template files called
+.Pa /var/yp/foo/master.passwd ,
+.Pa /var/yp/bar/master.passwd ,
+and
+.Pa /var/yp/baz/master.passwd .
+If
+.Pa foo
+happens to be the system default domain, then its template file can
+be either
+.Pa /var/yp/foo/master.passwd
+or
+.Pa /var/yp/master.passwd .
+The server will check for the latter file first and then use the former
+if it can't find it.
+.Pp
+Multi-domain mode is off by default since it can fail if there are
+duplicate or near-duplicate user entries in different domains.
+The server
+will abort an update request if it finds more than one user entry that
+matches its search criteria.
+Even so, paranoid administrators
+may wish to leave multi-domain mode disabled.
+.It Fl i
+If
+.Nm
+is invoked with this flag, it will perform map updates in place.
+This
+means that instead of just modifying the password template file and
+starting a map update, the server will modify the map databases
+directly.
+This is useful when the password maps are large: if, for
+example, the password database has tens of thousands of entries, it
+can take several minutes for a map update to complete.
+Updating the
+maps in place reduces this time to a few seconds.
+.It Fl v
+Turn on verbose logging mode.
+The server normally only logs messages
+using the
+.Xr syslog 3
+facility when it encounters an error condition, or when processing
+updates for the super-user on the NIS master server.
+Running the server
+with the
+.Fl v
+flag will cause it to log informational messages for all updates.
+.It Fl u
+Many commercial
+.Xr yppasswd 1
+clients do not use a reserved port when sending requests to
+.Nm .
+This is either because the
+.Xr yppasswd 1
+program is not installed set-uid root, or because the RPC
+implementation does not place any emphasis on binding to reserved
+ports when establishing client connections for the super-user.
+By default,
+.Nm
+expects to receive requests from clients using reserved ports; requests
+received from non-privileged ports are rejected.
+Unfortunately, this
+behavior prevents any client systems that to not use privileged
+ports from successfully submitting password updates.
+Specifying
+the
+.Fl u
+flag to
+.Nm
+disables the privileged port check so that it will work with
+.Xr yppasswd 1
+clients that don't use privileged ports.
+This reduces security to
+a certain small degree, but it might be necessary in cases where it
+is not possible to change the client behavior.
+.It Fl h
+Display the list of flags and options understood by
+.Nm .
+.El
+.Sh FILES
+.Bl -tag -width Pa -compact
+.It Pa /usr/libexec/yppwupdate
+The script invoked by
+.Nm
+to update and push the NIS maps after
+an update.
+.It Pa /var/yp/master.passwd
+The template password file for the default domain.
+.It Pa /var/yp/[domainname]/[maps]
+The NIS maps for a particular NIS domain.
+.It Pa /var/yp/[domainname]/master.passwd
+The template password file(s) for non-default domains
+(used only in multi-domain mode).
+.El
+.Sh SEE ALSO
+.Xr yp 8 ,
+.Xr yppush 8 ,
+.Xr ypserv 8 ,
+.Xr ypxfr 8
+.Sh BUGS
+As listed in the yppasswd.x protocol definition, the YPPASSWDPROC_UPDATE
+procedure takes two arguments: a V7-style passwd structure containing
+updated user information and the user's existing unencrypted (cleartext)
+password.
+Since
+.Nm
+is supposed to handle update requests from remote NIS client machines,
+this means that
+.Xr yppasswd 1
+and similar client programs will in fact be transmitting users' cleartext
+passwords over the network.
+.Pp
+This is not a problem for password updates since the plaintext password
+sent with the update will no longer be valid once the new encrypted password
+is put into place, but if the user is only updating his or her 'GECOS'
+information or shell, then the cleartext password sent with the update
+will still be valid once the update is completed.
+If the network is
+insecure, this cleartext password could be intercepted and used to
+gain unauthorized access to the user's account.
+.Sh AUTHORS
+.An Bill Paul Aq wpaul@ctr.columbia.edu
diff --git a/usr.sbin/rpc.yppasswdd/yppasswd_private.x b/usr.sbin/rpc.yppasswdd/yppasswd_private.x
new file mode 100644
index 0000000..f2921e8
--- /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[] =
+% "$FreeBSD$";
+%#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..98cab6e
--- /dev/null
+++ b/usr.sbin/rpc.yppasswdd/yppasswdd_extern.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 1995, 1996
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _YPPASSWDD_EXTERN_H
+#define _YPPASSWDD_EXTERN_H
+
+#ifndef YPLIBDIR
+#define YPLIBDIR "/usr/libexec/"
+#endif
+
+#ifndef _PATH_YP
+#define _PATH_YP "/var/yp/"
+#endif
+
+#define MAP_UPDATE "yppwupdate"
+#define MAP_UPDATE_PATH YPLIBDIR "yppwupdate"
+
+extern char *yp_dir;
+extern char *progname;
+extern void do_master(void);
+extern void yppasswdprog_1(struct svc_req *, register SVCXPRT *);
+extern void master_yppasswdprog_1 __P((struct svc_req *,
+ register SVCXPRT *));
+extern void reaper(int);
+extern void install_reaper(int);
+extern char *ok_shell __P ((char *));
+extern char *passfile;
+extern char *passfile_default;
+extern char *yppasswd_domain;
+extern int no_chsh;
+extern int no_chfn;
+extern int allow_additions;
+extern int multidomain;
+extern int resvport;
+extern int inplace;
+extern int verbose;
+extern int _rpc_dtablesize(void);
+
+#endif
diff --git a/usr.sbin/rpc.yppasswdd/yppasswdd_main.c b/usr.sbin/rpc.yppasswdd/yppasswdd_main.c
new file mode 100644
index 0000000..2f7e1e2
--- /dev/null
+++ b/usr.sbin/rpc.yppasswdd/yppasswdd_main.c
@@ -0,0 +1,348 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <netinet/in.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <memory.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h> /* getenv, exit */
+#include <string.h>
+#include <string.h> /* strcmp */
+#include <syslog.h>
+#include <unistd.h>
+
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h> /* for pmap_unset */
+#include <rpcsvc/yp.h>
+struct dom_binding {};
+#include <rpcsvc/ypclnt.h>
+
+#include "yppasswd.h"
+#include "yppasswdd_extern.h"
+#include "yppasswd_private.h"
+#include "ypxfr_extern.h"
+#include "yp_extern.h"
+
+#ifndef SIG_PF
+#define SIG_PF void(*)(int)
+#endif
+
+#ifdef DEBUG
+#define RPC_SVC_FG
+#endif
+
+#define _RPCSVC_CLOSEDOWN 120
+int _rpcpmstart = 0; /* Started by a port monitor ? */
+static int _rpcfdtype;
+ /* Whether Stream or Datagram ? */
+ /* States a server can be in wrt request */
+
+#define _IDLE 0
+#define _SERVED 1
+#define _SERVING 2
+
+static char _localhost[] = "localhost";
+static char _passwd_byname[] = "passwd.byname";
+extern int _rpcsvcstate; /* Set when a request is serviced */
+static char _progname[] = "rpc.yppasswdd";
+char *progname = _progname;
+static char _yp_dir[] = _PATH_YP;
+char *yp_dir = _yp_dir;
+static char _passfile_default[] = _PATH_YP "master.passwd";
+char *passfile_default = _passfile_default;
+char *passfile;
+char *yppasswd_domain = NULL;
+int no_chsh = 0;
+int no_chfn = 0;
+int allow_additions = 0;
+int multidomain = 0;
+int verbose = 0;
+int resvport = 1;
+int inplace = 0;
+char sockname[] = YP_SOCKNAME;
+
+static void
+terminate(int sig __unused)
+{
+ rpcb_unset(YPPASSWDPROG, YPPASSWDVERS, NULL);
+ rpcb_unset(MASTER_YPPASSWDPROG, MASTER_YPPASSWDVERS, NULL);
+ unlink(sockname);
+ exit(0);
+}
+
+static void
+reload(int sig __unused)
+{
+ load_securenets();
+}
+
+static void
+closedown(int sig __unused)
+{
+ if (_rpcsvcstate == _IDLE) {
+ extern fd_set svc_fdset;
+ static int size;
+ int i, openfd;
+
+ if (_rpcfdtype == SOCK_DGRAM) {
+ unlink(sockname);
+ exit(0);
+ }
+ if (size == 0) {
+ size = getdtablesize();
+ }
+ for (i = 0, openfd = 0; i < size && openfd < 2; i++)
+ if (FD_ISSET(i, &svc_fdset))
+ openfd++;
+ if (openfd <= 1) {
+ unlink(sockname);
+ exit(0);
+ }
+ }
+ if (_rpcsvcstate == _SERVED)
+ _rpcsvcstate = _IDLE;
+
+ (void) signal(SIGALRM, (SIG_PF) closedown);
+ (void) alarm(_RPCSVC_CLOSEDOWN/2);
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "%s\n%s\n",
+"usage: rpc.yppasswdd [-t master.passwd file] [-d domain] [-p path] [-s]",
+" [-f] [-m] [-i] [-a] [-v] [-u] [-h]");
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct rlimit rlim;
+ SVCXPRT *transp = NULL;
+ struct sockaddr_in saddr;
+ int asize = sizeof (saddr);
+ struct netconfig *nconf;
+ void *localhandle;
+ int ch;
+ char *mastername;
+ char myname[MAXHOSTNAMELEN + 2];
+
+ extern int debug;
+
+ debug = 1;
+
+ while ((ch = getopt(argc, argv, "t:d:p:sfamuivh")) != -1) {
+ switch (ch) {
+ case 't':
+ passfile_default = optarg;
+ break;
+ case 'd':
+ yppasswd_domain = optarg;
+ break;
+ case 's':
+ no_chsh++;
+ break;
+ case 'f':
+ no_chfn++;
+ break;
+ case 'p':
+ yp_dir = optarg;
+ break;
+ case 'a':
+ allow_additions++;
+ break;
+ case 'm':
+ multidomain++;
+ break;
+ case 'i':
+ inplace++;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 'u':
+ resvport = 0;
+ break;
+ default:
+ case 'h':
+ usage();
+ break;
+ }
+ }
+
+ if (yppasswd_domain == NULL) {
+ if (yp_get_default_domain(&yppasswd_domain)) {
+ yp_error("no domain specified and system domain \
+name isn't set -- aborting");
+ usage();
+ }
+ }
+
+ load_securenets();
+
+ if (getrpcport(_localhost, YPPROG, YPVERS, IPPROTO_UDP) <= 0) {
+ yp_error("no ypserv processes registered with local portmap");
+ yp_error("this host is not an NIS server -- aborting");
+ exit(1);
+ }
+
+ if ((mastername = ypxfr_get_master(yppasswd_domain,
+ _passwd_byname, _localhost, 0)) == NULL) {
+ yp_error("can't get name of NIS master server for domain %s",
+ yppasswd_domain);
+ exit(1);
+ }
+
+ if (gethostname((char *)&myname, sizeof(myname)) == -1) {
+ yp_error("can't get local hostname: %s", strerror(errno));
+ exit(1);
+ }
+
+ if (strncasecmp(mastername, (char *)&myname, sizeof(myname))) {
+ yp_error("master of %s is %s, but we are %s",
+ "passwd.byname", mastername, myname);
+ yp_error("this host is not the NIS master server for \
+the %s domain -- aborting", yppasswd_domain);
+ exit(1);
+ }
+
+ debug = 0;
+
+ if (getsockname(0, (struct sockaddr *)&saddr, &asize) == 0) {
+ int ssize = sizeof (int);
+ if (saddr.sin_family != AF_INET)
+ exit(1);
+ if (getsockopt(0, SOL_SOCKET, SO_TYPE,
+ (char *)&_rpcfdtype, &ssize) == -1)
+ exit(1);
+ _rpcpmstart = 1;
+ }
+
+ if (!debug && _rpcpmstart == 0) {
+ if (daemon(0,0)) {
+ err(1,"cannot fork");
+ }
+ }
+ openlog("rpc.yppasswdd", LOG_PID, LOG_DAEMON);
+
+ rpcb_unset(YPPASSWDPROG, YPPASSWDVERS, NULL);
+ rpcb_unset(MASTER_YPPASSWDPROG, MASTER_YPPASSWDVERS, NULL);
+
+ if (svc_create(yppasswdprog_1, YPPASSWDPROG, YPPASSWDVERS, "netpath") == 0) {
+ yp_error("cannot create yppasswd service.");
+ exit(1);
+ }
+ if (svc_create(master_yppasswdprog_1, MASTER_YPPASSWDPROG,
+ MASTER_YPPASSWDVERS, "netpath") == 0) {
+ yp_error("cannot create master_yppasswd service.");
+ exit(1);
+ }
+
+ nconf = NULL;
+ localhandle = setnetconfig();
+ while ((nconf = getnetconfig(localhandle)) != NULL) {
+ if (nconf->nc_protofmly != NULL &&
+ strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0)
+ break;
+ }
+ if (nconf == NULL) {
+ yp_error("getnetconfigent unix: %s", nc_sperror());
+ exit(1);
+ }
+ unlink(sockname);
+ transp = svcunix_create(RPC_ANYSOCK, 0, 0, sockname);
+ if (transp == NULL) {
+ yp_error("cannot create AF_LOCAL service.");
+ exit(1);
+ }
+ if (!svc_reg(transp, MASTER_YPPASSWDPROG, MASTER_YPPASSWDVERS,
+ master_yppasswdprog_1, nconf)) {
+ yp_error("unable to register (MASTER_YPPASSWDPROG, \
+ MASTER_YPPASSWDVERS, unix).");
+ exit(1);
+ }
+ endnetconfig(localhandle);
+
+ /* Only root may connect() to the AF_UNIX link. */
+ if (chmod(sockname, 0))
+ err(1, "chmod of %s failed", sockname);
+
+ if (transp == (SVCXPRT *)NULL) {
+ yp_error("could not create a handle");
+ exit(1);
+ }
+ if (_rpcpmstart) {
+ (void) signal(SIGALRM, (SIG_PF) closedown);
+ (void) alarm(_RPCSVC_CLOSEDOWN/2);
+ }
+
+ /* Unlimited resource limits. */
+ rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY;
+ (void)setrlimit(RLIMIT_CPU, &rlim);
+ (void)setrlimit(RLIMIT_FSIZE, &rlim);
+ (void)setrlimit(RLIMIT_STACK, &rlim);
+ (void)setrlimit(RLIMIT_DATA, &rlim);
+ (void)setrlimit(RLIMIT_RSS, &rlim);
+
+ /* Don't drop core (not really necessary, but GP's). */
+ rlim.rlim_cur = rlim.rlim_max = 0;
+ (void)setrlimit(RLIMIT_CORE, &rlim);
+
+ /* Turn off signals. */
+ (void)signal(SIGALRM, SIG_IGN);
+ (void)signal(SIGHUP, (SIG_PF) reload);
+ (void)signal(SIGINT, SIG_IGN);
+ (void)signal(SIGPIPE, SIG_IGN);
+ (void)signal(SIGQUIT, SIG_IGN);
+ (void)signal(SIGTERM, (SIG_PF) terminate);
+
+ svc_run();
+ yp_error("svc_run returned");
+ exit(1);
+ /* NOTREACHED */
+}
diff --git a/usr.sbin/rpc.yppasswdd/yppasswdd_server.c b/usr.sbin/rpc.yppasswdd/yppasswdd_server.c
new file mode 100644
index 0000000..21f3895e
--- /dev/null
+++ b/usr.sbin/rpc.yppasswdd/yppasswdd_server.c
@@ -0,0 +1,832 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/fcntl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#include <ctype.h>
+#include <db.h>
+#include <dirent.h>
+#include <errno.h>
+#include <limits.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <libgen.h>
+#include <libutil.h>
+
+#include <rpc/rpc.h>
+#include <rpcsvc/yp.h>
+struct dom_binding;
+#include <rpcsvc/ypclnt.h>
+#include "yppasswdd_extern.h"
+#include "yppasswd.h"
+#include "yppasswd_private.h"
+#include "ypxfr_extern.h"
+#include "yp_extern.h"
+
+static struct passwd yp_password;
+
+static void
+copy_yp_pass(char *p, int x, int m)
+{
+ char *t, *s = p;
+ static char *buf;
+
+ yp_password.pw_fields = 0;
+
+ buf = realloc(buf, m + 10);
+ bzero(buf, m + 10);
+
+ /* Turn all colons into NULLs */
+ while (strchr(s, ':')) {
+ s = (strchr(s, ':') + 1);
+ *(s - 1)= '\0';
+ }
+
+ t = buf;
+#define EXPAND(e) e = t; while ((*t++ = *p++));
+ EXPAND(yp_password.pw_name);
+ yp_password.pw_fields |= _PWF_NAME;
+ EXPAND(yp_password.pw_passwd);
+ yp_password.pw_fields |= _PWF_PASSWD;
+ yp_password.pw_uid = atoi(p);
+ p += (strlen(p) + 1);
+ yp_password.pw_fields |= _PWF_UID;
+ yp_password.pw_gid = atoi(p);
+ p += (strlen(p) + 1);
+ yp_password.pw_fields |= _PWF_GID;
+ if (x) {
+ EXPAND(yp_password.pw_class);
+ yp_password.pw_fields |= _PWF_CLASS;
+ yp_password.pw_change = atol(p);
+ p += (strlen(p) + 1);
+ yp_password.pw_fields |= _PWF_CHANGE;
+ yp_password.pw_expire = atol(p);
+ p += (strlen(p) + 1);
+ yp_password.pw_fields |= _PWF_EXPIRE;
+ }
+ EXPAND(yp_password.pw_gecos);
+ yp_password.pw_fields |= _PWF_GECOS;
+ EXPAND(yp_password.pw_dir);
+ yp_password.pw_fields |= _PWF_DIR;
+ EXPAND(yp_password.pw_shell);
+ yp_password.pw_fields |= _PWF_SHELL;
+
+ return;
+}
+
+static int
+validchars(char *arg)
+{
+ size_t i;
+
+ for (i = 0; i < strlen(arg); i++) {
+ if (iscntrl(arg[i])) {
+ yp_error("string contains a control character");
+ return(1);
+ }
+ if (arg[i] == ':') {
+ yp_error("string contains a colon");
+ return(1);
+ }
+ /* Be evil: truncate strings with \n in them silently. */
+ if (arg[i] == '\n') {
+ arg[i] = '\0';
+ return(0);
+ }
+ }
+ return(0);
+}
+
+static int
+validate_master(struct passwd *opw __unused, struct x_master_passwd *npw)
+{
+
+ if (npw->pw_name[0] == '+' || npw->pw_name[0] == '-') {
+ yp_error("client tried to modify an NIS entry");
+ return(1);
+ }
+
+ if (validchars(npw->pw_shell)) {
+ yp_error("specified shell contains invalid characters");
+ return(1);
+ }
+
+ if (validchars(npw->pw_gecos)) {
+ yp_error("specified gecos field contains invalid characters");
+ return(1);
+ }
+
+ if (validchars(npw->pw_passwd)) {
+ yp_error("specified password contains invalid characters");
+ return(1);
+ }
+ return(0);
+}
+
+static int
+validate(struct passwd *opw, struct x_passwd *npw)
+{
+
+ if (npw->pw_name[0] == '+' || npw->pw_name[0] == '-') {
+ yp_error("client tried to modify an NIS entry");
+ return(1);
+ }
+
+ if ((uid_t)npw->pw_uid != opw->pw_uid) {
+ yp_error("UID mismatch: client says user %s has UID %d",
+ npw->pw_name, npw->pw_uid);
+ yp_error("database says user %s has UID %d", opw->pw_name,
+ opw->pw_uid);
+ return(1);
+ }
+
+ if ((gid_t)npw->pw_gid != opw->pw_gid) {
+ yp_error("GID mismatch: client says user %s has GID %d",
+ npw->pw_name, npw->pw_gid);
+ yp_error("database says user %s has GID %d", opw->pw_name,
+ opw->pw_gid);
+ return(1);
+ }
+
+ /*
+ * Don't allow the user to shoot himself in the foot,
+ * even on purpose.
+ */
+ if (!ok_shell(npw->pw_shell)) {
+ yp_error("%s is not a valid shell", npw->pw_shell);
+ return(1);
+ }
+
+ if (validchars(npw->pw_shell)) {
+ yp_error("specified shell contains invalid characters");
+ return(1);
+ }
+
+ if (validchars(npw->pw_gecos)) {
+ yp_error("specified gecos field contains invalid characters");
+ return(1);
+ }
+
+ if (validchars(npw->pw_passwd)) {
+ yp_error("specified password contains invalid characters");
+ return(1);
+ }
+ return(0);
+}
+
+/*
+ * Kludge alert:
+ * In order to have one rpc.yppasswdd support multiple domains,
+ * we have to cheat: we search each directory under /var/yp
+ * and try to match the user in each master.passwd.byname
+ * map that we find. If the user matches (username, uid and gid
+ * all agree), then we use that domain. If we match the user in
+ * more than one database, we must abort.
+ */
+static char *
+find_domain(struct x_passwd *pw)
+{
+ struct stat statbuf;
+ struct dirent *dirp;
+ DIR *dird;
+ char yp_mapdir[MAXPATHLEN + 2];
+ static char domain[YPMAXDOMAIN];
+ char *tmp = NULL;
+ DBT key, data;
+ int hit = 0;
+
+ yp_error("performing multidomain lookup");
+
+ if ((dird = opendir(yp_dir)) == NULL) {
+ yp_error("opendir(%s) failed: %s", yp_dir, strerror(errno));
+ return(NULL);
+ }
+
+ while ((dirp = readdir(dird)) != NULL) {
+ snprintf(yp_mapdir, sizeof yp_mapdir, "%s/%s",
+ yp_dir, dirp->d_name);
+ if (stat(yp_mapdir, &statbuf) < 0) {
+ yp_error("stat(%s) failed: %s", yp_mapdir,
+ strerror(errno));
+ closedir(dird);
+ return(NULL);
+ }
+ if (S_ISDIR(statbuf.st_mode)) {
+ tmp = (char *)dirp->d_name;
+ key.data = pw->pw_name;
+ key.size = strlen(pw->pw_name);
+
+ if (yp_get_record(tmp,"master.passwd.byname",
+ &key, &data, 0) != YP_TRUE) {
+ continue;
+ }
+ *((char *)data.data + data.size) = '\0';
+ copy_yp_pass(data.data, 1, data.size);
+ if (yp_password.pw_uid == (uid_t)pw->pw_uid &&
+ yp_password.pw_gid == (gid_t)pw->pw_gid) {
+ hit++;
+ snprintf(domain, YPMAXDOMAIN, "%s", tmp);
+ }
+ }
+ }
+
+ closedir(dird);
+ if (hit > 1) {
+ yp_error("found same user in two different domains");
+ return(NULL);
+ } else
+ return((char *)&domain);
+}
+
+static const char *maps[] = {
+ "master.passwd.byname",
+ "master.passwd.byuid",
+ "passwd.byname",
+ "passwd.byuid"
+};
+
+static const char *formats[] = {
+ "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s",
+ "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s",
+ "%s:%s:%d:%d:%s:%s:%s",
+ "%s:%s:%d:%d:%s:%s:%s"
+};
+
+static int
+update_inplace(struct passwd *pw, char *domain)
+{
+ DB *dbp = NULL;
+ DBT key = { NULL, 0 };
+ DBT data = { NULL, 0 };
+ char pwbuf[YPMAXRECORD];
+ char keybuf[20];
+ int i;
+ char *ptr = NULL;
+ static char yp_last[] = "YP_LAST_MODIFIED";
+ char yplastbuf[YPMAXRECORD];
+
+ snprintf(yplastbuf, sizeof yplastbuf, "%llu",
+ (unsigned long long)time(NULL));
+
+ for (i = 0; i < 4; i++) {
+
+ if (i % 2) {
+ snprintf(keybuf, sizeof keybuf,
+ "%llu", (unsigned long long)pw->pw_uid);
+ key.data = &keybuf;
+ key.size = strlen(keybuf);
+ } else {
+ key.data = pw->pw_name;
+ key.size = strlen(pw->pw_name);
+ }
+
+ /*
+ * XXX The passwd.byname and passwd.byuid maps come in
+ * two flavors: secure and insecure. The secure version
+ * has a '*' in the password field whereas the insecure one
+ * has a real crypted password. The maps will be insecure
+ * if they were built with 'unsecure = TRUE' enabled in
+ * /var/yp/Makefile, but we'd have no way of knowing if
+ * this has been done unless we were to try parsing the
+ * Makefile, which is a disgusting thought. Instead, we
+ * read the records from the maps, skip to the first ':'
+ * in them, and then look at the character immediately
+ * following it. If it's an '*' then the map is 'secure'
+ * and we must not insert a real password into the pw_passwd
+ * field. If it's not an '*', then we put the real crypted
+ * password in.
+ */
+ if (yp_get_record(domain,maps[i],&key,&data,1) != YP_TRUE) {
+ yp_error("couldn't read %s/%s: %s", domain,
+ maps[i], strerror(errno));
+ return(1);
+ }
+
+ if ((ptr = strchr(data.data, ':')) == NULL) {
+ yp_error("no colon in passwd record?!");
+ return(1);
+ }
+
+ /*
+ * XXX Supposing we have more than one user with the same
+ * UID? (Or more than one user with the same name?) We could
+ * end up modifying the wrong record if were not careful.
+ */
+ if (i % 2) {
+ if (strncmp(data.data, pw->pw_name,
+ strlen(pw->pw_name))) {
+ yp_error("warning: found entry for UID %d \
+in map %s@%s with wrong name (%.*s)", pw->pw_uid, maps[i], domain,
+ (int)(ptr - (char *)data.data),
+ (char *)data.data);
+ yp_error("there may be more than one user \
+with the same UID - continuing");
+ continue;
+ }
+ } else {
+ /*
+ * We're really being ultra-paranoid here.
+ * This is generally a 'can't happen' condition.
+ */
+ snprintf(pwbuf, sizeof pwbuf, ":%d:%d:", pw->pw_uid,
+ pw->pw_gid);
+ if (!strstr(data.data, pwbuf)) {
+ yp_error("warning: found entry for user %s \
+in map %s@%s with wrong UID", pw->pw_name, maps[i], domain);
+ yp_error("there may be more than one user \
+with the same name - continuing");
+ continue;
+ }
+ }
+
+ if (i < 2) {
+ snprintf(pwbuf, sizeof pwbuf, formats[i],
+ pw->pw_name, pw->pw_passwd, pw->pw_uid,
+ pw->pw_gid, pw->pw_class, pw->pw_change,
+ pw->pw_expire, pw->pw_gecos, pw->pw_dir,
+ pw->pw_shell);
+ } else {
+ snprintf(pwbuf, sizeof pwbuf, formats[i],
+ pw->pw_name, *(ptr+1) == '*' ? "*" : pw->pw_passwd,
+ pw->pw_uid, pw->pw_gid, pw->pw_gecos, pw->pw_dir,
+ pw->pw_shell);
+ }
+
+#define FLAGS O_RDWR|O_CREAT
+
+ if ((dbp = yp_open_db_rw(domain, maps[i], FLAGS)) == NULL) {
+ yp_error("couldn't open %s/%s r/w: %s",domain,
+ maps[i],strerror(errno));
+ return(1);
+ }
+
+ data.data = pwbuf;
+ data.size = strlen(pwbuf);
+
+ if (yp_put_record(dbp, &key, &data, 1) != YP_TRUE) {
+ yp_error("failed to update record in %s/%s", domain,
+ maps[i]);
+ (void)(dbp->close)(dbp);
+ return(1);
+ }
+
+ key.data = yp_last;
+ key.size = strlen(yp_last);
+ data.data = (char *)&yplastbuf;
+ data.size = strlen(yplastbuf);
+
+ if (yp_put_record(dbp, &key, &data, 1) != YP_TRUE) {
+ yp_error("failed to update timestamp in %s/%s", domain,
+ maps[i]);
+ (void)(dbp->close)(dbp);
+ return(1);
+ }
+
+ (void)(dbp->close)(dbp);
+ }
+
+ return(0);
+}
+
+int *
+yppasswdproc_update_1_svc(yppasswd *argp, struct svc_req *rqstp)
+{
+ static int result;
+ struct sockaddr_in *rqhost;
+ DBT key, data;
+ int rval = 0;
+ int pfd, tfd;
+ int pid;
+ int passwd_changed = 0;
+ int shell_changed = 0;
+ int gecos_changed = 0;
+ char *oldshell = NULL;
+ char *oldgecos = NULL;
+ char *passfile_hold;
+ char passfile_buf[MAXPATHLEN + 2];
+ char *domain = yppasswd_domain;
+ static struct sockaddr_in clntaddr;
+ static struct timeval t_saved, t_test;
+
+ /*
+ * Normal user updates always use the 'default' master.passwd file.
+ */
+
+ passfile = passfile_default;
+ result = 1;
+
+ rqhost = svc_getcaller(rqstp->rq_xprt);
+
+ gettimeofday(&t_test, NULL);
+ if (!bcmp(rqhost, &clntaddr, sizeof *rqhost) &&
+ t_test.tv_sec > t_saved.tv_sec &&
+ t_test.tv_sec - t_saved.tv_sec < 300) {
+
+ bzero(&clntaddr, sizeof clntaddr);
+ bzero(&t_saved, sizeof t_saved);
+ return(NULL);
+ }
+
+ bcopy(rqhost, &clntaddr, sizeof clntaddr);
+ gettimeofday(&t_saved, NULL);
+
+ if (yp_access(resvport ? "master.passwd.byname" : NULL, rqstp)) {
+ yp_error("rejected update request from unauthorized host");
+ svcerr_auth(rqstp->rq_xprt, AUTH_BADCRED);
+ return(&result);
+ }
+
+ /*
+ * Step one: find the user. (It's kinda pointless to
+ * proceed if the user doesn't exist.) We look for the
+ * user in the master.passwd.byname database, _NOT_ by
+ * using getpwent() and friends! We can't use getpwent()
+ * since the NIS master server is not guaranteed to be
+ * configured as an NIS client.
+ */
+
+ if (multidomain) {
+ if ((domain = find_domain(&argp->newpw)) == NULL) {
+ yp_error("multidomain lookup failed - aborting update");
+ return(&result);
+ } else
+ yp_error("updating user %s in domain %s",
+ argp->newpw.pw_name, domain);
+ }
+
+ key.data = argp->newpw.pw_name;
+ key.size = strlen(argp->newpw.pw_name);
+
+ if ((rval = yp_get_record(domain,"master.passwd.byname",
+ &key, &data, 0)) != YP_TRUE) {
+ if (rval == YP_NOKEY) {
+ yp_error("user %s not found in passwd database",
+ argp->newpw.pw_name);
+ } else {
+ yp_error("database access error: %s",
+ yperr_string(rval));
+ }
+ return(&result);
+ }
+
+ /* Nul terminate, please. */
+ *((char *)data.data + data.size) = '\0';
+
+ copy_yp_pass(data.data, 1, data.size);
+
+ /* Step 2: check that the supplied oldpass is valid. */
+
+ if (strcmp(crypt(argp->oldpass, yp_password.pw_passwd),
+ yp_password.pw_passwd)) {
+ yp_error("rejected change attempt -- bad password");
+ yp_error("client address: %s username: %s",
+ inet_ntoa(rqhost->sin_addr),
+ argp->newpw.pw_name);
+ return(&result);
+ }
+
+ /* Step 3: validate the arguments passed to us by the client. */
+
+ if (validate(&yp_password, &argp->newpw)) {
+ yp_error("rejecting change attempt: bad arguments");
+ yp_error("client address: %s username: %s",
+ inet_ntoa(rqhost->sin_addr),
+ argp->newpw.pw_name);
+ svcerr_decode(rqstp->rq_xprt);
+ return(&result);
+ }
+
+ /* Step 4: update the user's passwd structure. */
+
+ if (!no_chsh && strcmp(argp->newpw.pw_shell, yp_password.pw_shell)) {
+ oldshell = yp_password.pw_shell;
+ yp_password.pw_shell = argp->newpw.pw_shell;
+ shell_changed++;
+ }
+
+
+ if (!no_chfn && strcmp(argp->newpw.pw_gecos, yp_password.pw_gecos)) {
+ oldgecos = yp_password.pw_gecos;
+ yp_password.pw_gecos = argp->newpw.pw_gecos;
+ gecos_changed++;
+ }
+
+ if (strcmp(argp->newpw.pw_passwd, yp_password.pw_passwd)) {
+ yp_password.pw_passwd = argp->newpw.pw_passwd;
+ yp_password.pw_change = 0;
+ passwd_changed++;
+ }
+
+ /*
+ * If the caller specified a domain other than our 'default'
+ * domain, change the path to master.passwd accordingly.
+ */
+
+ if (strcmp(domain, yppasswd_domain)) {
+ snprintf(passfile_buf, sizeof(passfile_buf),
+ "%s/%s/master.passwd", yp_dir, domain);
+ passfile = (char *)&passfile_buf;
+ }
+
+ /* Step 5: make a new password file with the updated info. */
+
+ if (pw_init(dirname(passfile), passfile)) {
+ yp_error("pw_init() failed");
+ return &result;
+ }
+ if ((pfd = pw_lock()) == -1) {
+ pw_fini();
+ yp_error("pw_lock() failed");
+ return &result;
+ }
+ if ((tfd = pw_tmp(-1)) == -1) {
+ pw_fini();
+ yp_error("pw_tmp() failed");
+ return &result;
+ }
+ if (pw_copy(pfd, tfd, &yp_password, NULL) == -1) {
+ pw_fini();
+ yp_error("pw_copy() failed");
+ return &result;
+ }
+ if (pw_mkdb(yp_password.pw_name) == -1) {
+ pw_fini();
+ yp_error("pw_mkdb() failed");
+ return &result;
+ }
+ pw_fini();
+
+ if (inplace) {
+ if ((rval = update_inplace(&yp_password, domain))) {
+ yp_error("inplace update failed -- rebuilding maps");
+ }
+ }
+
+ switch ((pid = fork())) {
+ case 0:
+ if (inplace && !rval) {
+ execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile,
+ yppasswd_domain, "pushpw", (char *)NULL);
+ } else {
+ execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile,
+ yppasswd_domain, (char *)NULL);
+ }
+ yp_error("couldn't exec map update process: %s",
+ strerror(errno));
+ unlink(passfile);
+ rename(passfile_hold, passfile);
+ exit(1);
+ break;
+ case -1:
+ yp_error("fork() failed: %s", strerror(errno));
+ unlink(passfile);
+ rename(passfile_hold, passfile);
+ return(&result);
+ break;
+ default:
+ unlink(passfile_hold);
+ break;
+ }
+
+ if (verbose) {
+ yp_error("update completed for user %s (uid %d):",
+ 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);
+}
+
+/*
+ * Note that this function performs a little less sanity checking
+ * than the last one. Since only the superuser is allowed to use it,
+ * it is assumed that the caller knows what he's doing.
+ */
+int *
+yppasswdproc_update_master_1_svc(master_yppasswd *argp,
+ struct svc_req *rqstp)
+{
+ static int result;
+ int pfd, tfd;
+ int pid;
+ uid_t uid;
+ int rval = 0;
+ DBT key, data;
+ char *passfile_hold;
+ char passfile_buf[MAXPATHLEN + 2];
+ struct sockaddr_in *rqhost;
+ SVCXPRT *transp;
+
+ result = 1;
+ transp = rqstp->rq_xprt;
+
+ /*
+ * NO AF_INET CONNETCIONS ALLOWED!
+ */
+ rqhost = svc_getcaller(transp);
+ if (rqhost->sin_family != AF_UNIX) {
+ yp_error("Alert! %s/%d attempted to use superuser-only \
+procedure!\n", inet_ntoa(rqhost->sin_addr), rqhost->sin_port);
+ svcerr_auth(transp, AUTH_BADCRED);
+ return(&result);
+ }
+
+ if (rqstp->rq_cred.oa_flavor != AUTH_SYS) {
+ yp_error("caller didn't send proper credentials");
+ svcerr_auth(transp, AUTH_BADCRED);
+ return(&result);
+ }
+
+ if (__rpc_get_local_uid(transp, &uid) < 0) {
+ yp_error("caller didn't send proper credentials");
+ svcerr_auth(transp, AUTH_BADCRED);
+ return(&result);
+ }
+
+ if (uid) {
+ yp_error("caller euid is %d, expecting 0 -- rejecting request",
+ uid);
+ svcerr_auth(rqstp->rq_xprt, AUTH_BADCRED);
+ return(&result);
+ }
+
+ passfile = passfile_default;
+
+ key.data = argp->newpw.pw_name;
+ key.size = strlen(argp->newpw.pw_name);
+
+ /*
+ * The superuser may add entries to the passwd maps if
+ * rpc.yppasswdd is started with the -a flag. Paranoia
+ * prevents me from allowing additions by default.
+ */
+ if ((rval = yp_get_record(argp->domain, "master.passwd.byname",
+ &key, &data, 0)) != YP_TRUE) {
+ if (rval == YP_NOKEY) {
+ yp_error("user %s not found in passwd database",
+ argp->newpw.pw_name);
+ if (allow_additions)
+ yp_error("notice: adding user %s to \
+master.passwd database for domain %s", argp->newpw.pw_name, argp->domain);
+ else
+ yp_error("restart rpc.yppasswdd with the -a flag to \
+allow additions to be made to the password database");
+ } else {
+ yp_error("database access error: %s",
+ yperr_string(rval));
+ }
+ if (!allow_additions)
+ return(&result);
+ } else {
+
+ /* Nul terminate, please. */
+ *((char *)data.data + data.size) = '\0';
+
+ copy_yp_pass(data.data, 1, data.size);
+ }
+
+ /*
+ * Perform a small bit of sanity checking.
+ */
+ if (validate_master(rval == YP_TRUE ? &yp_password:NULL,&argp->newpw)){
+ yp_error("rejecting update attempt for %s: bad arguments",
+ argp->newpw.pw_name);
+ return(&result);
+ }
+
+ /*
+ * If the caller specified a domain other than our 'default'
+ * domain, change the path to master.passwd accordingly.
+ */
+
+ if (strcmp(argp->domain, yppasswd_domain)) {
+ snprintf(passfile_buf, sizeof(passfile_buf),
+ "%s/%s/master.passwd", yp_dir, argp->domain);
+ passfile = (char *)&passfile_buf;
+ }
+
+ if (pw_init(dirname(passfile), passfile)) {
+ yp_error("pw_init() failed");
+ return &result;
+ }
+ if ((pfd = pw_lock()) == -1) {
+ pw_fini();
+ yp_error("pw_lock() failed");
+ return &result;
+ }
+ if ((tfd = pw_tmp(-1)) == -1) {
+ pw_fini();
+ yp_error("pw_tmp() failed");
+ return &result;
+ }
+ if (pw_copy(pfd, tfd, (struct passwd *)&argp->newpw, NULL) == -1) {
+ pw_fini();
+ yp_error("pw_copy() failed");
+ return &result;
+ }
+ if (pw_mkdb(argp->newpw.pw_name) == -1) {
+ pw_fini();
+ yp_error("pw_mkdb() failed");
+ return &result;
+ }
+ pw_fini();
+
+ if (inplace) {
+ if ((rval = update_inplace((struct passwd *)&argp->newpw,
+ argp->domain))) {
+ yp_error("inplace update failed -- rebuilding maps");
+ }
+ }
+
+ switch ((pid = fork())) {
+ case 0:
+ if (inplace && !rval) {
+ execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile,
+ argp->domain, "pushpw", (char *)NULL);
+ } else {
+ execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile,
+ argp->domain, (char *)NULL);
+ }
+ yp_error("couldn't exec map update process: %s",
+ strerror(errno));
+ unlink(passfile);
+ rename(passfile_hold, passfile);
+ exit(1);
+ break;
+ case -1:
+ yp_error("fork() failed: %s", strerror(errno));
+ unlink(passfile);
+ rename(passfile_hold, passfile);
+ return(&result);
+ break;
+ default:
+ unlink(passfile_hold);
+ break;
+ }
+
+ yp_error("performed update of user %s (uid %d) domain %s",
+ argp->newpw.pw_name,
+ argp->newpw.pw_uid,
+ argp->domain);
+
+ result = 0;
+ return(&result);
+}
diff --git a/usr.sbin/rpc.yppasswdd/yppwupdate b/usr.sbin/rpc.yppasswdd/yppwupdate
new file mode 100644
index 0000000..a9db8f7
--- /dev/null
+++ b/usr.sbin/rpc.yppasswdd/yppwupdate
@@ -0,0 +1,34 @@
+#!/bin/sh
+#
+# This script is invoked by rpc.yppasswdd to update the password
+# maps after the master password file has been modified. It expects
+# to be passed two arguments: the name of the master.passwd template
+# file that was modified by the server, and the name of the domain to
+# update. These are passed to /var/yp/Makefile.
+#
+# Comment out the LOG=yes line to disable logging.
+#
+# $FreeBSD$
+#
+
+PATH=/bin:/usr/bin; export PATH
+LOG=yes
+LOGFILE=/var/yp/ypupdate.log
+
+umask 077
+
+if [ ! -f $LOGFILE ];
+then
+ touch $LOGFILE
+ echo "# Edit /usr/libexec/yppwupdate to disable" >> $LOGFILE
+ echo "# logging to this file from yppasswdd." >> $LOGFILE
+ echo -n "# Log started on: " >> $LOGFILE
+ date >> $LOGFILE
+fi
+
+if [ ! $LOG ];
+then
+ cd /var/yp && make MASTER_PASSWD=$1 UPDATE_DOMAIN=$2 $3 2>&1
+else
+ cd /var/yp && make MASTER_PASSWD=$1 UPDATE_DOMAIN=$2 $3 >> $LOGFILE 2>&1
+fi
diff --git a/usr.sbin/rpc.ypupdated/Makefile b/usr.sbin/rpc.ypupdated/Makefile
new file mode 100644
index 0000000..78dec49
--- /dev/null
+++ b/usr.sbin/rpc.ypupdated/Makefile
@@ -0,0 +1,32 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../ypserv ${.CURDIR}/../../libexec/ypxfr
+
+PROG= rpc.ypupdated
+NOMAN= yes
+SRCS= ypupdate_prot_svc.c ypupdate_prot.h ypupdated_main.c \
+ yp_error.c update.c ypupdated_server.c \
+ yp_dblookup.c yp_dbwrite.c yp_dbdelete.c yp_dbupdate.c
+
+CFLAGS+= -I${.CURDIR}/../ypserv -I. -I${.CURDIR}/../../libexec/ypxfr
+#CFLAGS+= -DYP
+
+DPADD= ${LIBRPCSVC}
+LDADD= -lrpcsvc
+
+CLEANFILES= ypupdate_prot_svc.c ypupdate_prot.h
+
+RPCDIR= ${DESTDIR}/usr/include/rpcsvc
+RPCGEN= rpcgen -I -C
+
+# We need to remove the 'static' keyword from _rpcsvcstate so that
+# ypupdated_main.c can see it.
+ypupdate_prot_svc.c: ${RPCDIR}/ypupdate_prot.x
+ rm -f ${.TARGET}
+ ${RPCGEN} -m ${RPCDIR}/ypupdate_prot.x | \
+ sed s/"static int _rpcsvcstate"/"int _rpcsvcstate"/g > ${.TARGET}
+
+ypupdate_prot.h: ${RPCDIR}/ypupdate_prot.x
+ ${RPCGEN} -h -o ${.TARGET} ${RPCDIR}/ypupdate_prot.x
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rpc.ypupdated/update.c b/usr.sbin/rpc.ypupdated/update.c
new file mode 100644
index 0000000..5cc0d7a
--- /dev/null
+++ b/usr.sbin/rpc.ypupdated/update.c
@@ -0,0 +1,331 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user or with the express written consent of
+ * Sun Microsystems, Inc.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)update.c 1.2 91/03/11 Copyr 1986 Sun Micro";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/*
+ * Copyright (C) 1986, 1989, Sun Microsystems, Inc.
+ */
+
+/*
+ * Administrative tool to add a new user to the publickey database
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <rpc/rpc.h>
+#include <rpc/key_prot.h>
+#ifdef YP
+#include <rpcsvc/yp_prot.h>
+#include <rpcsvc/ypclnt.h>
+#include <sys/wait.h>
+#include <netdb.h>
+#endif /* YP */
+#include <pwd.h>
+#include <string.h>
+#include <sys/resource.h>
+#include <stdlib.h>
+#include "ypupdated_extern.h"
+
+#ifdef YP
+#define MAXMAPNAMELEN 256
+#else
+#define YPOP_CHANGE 1 /* change, do not add */
+#define YPOP_INSERT 2 /* add, do not change */
+#define YPOP_DELETE 3 /* delete this entry */
+#define YPOP_STORE 4 /* add, or change */
+#endif
+
+#ifdef YP
+static char SHELL[] = "/bin/sh";
+static char YPDBPATH[]="/var/yp"; /* This is defined but not used! */
+static char PKMAP[] = "publickey.byname";
+static char UPDATEFILE[] = "updaters";
+static char PKFILE[] = "/etc/publickey";
+#endif /* YP */
+
+#ifdef YP
+static int _openchild(char *, FILE **, FILE **);
+
+/*
+ * Determine if requester is allowed to update the given map,
+ * and update it if so. Returns the yp status, which is zero
+ * if there is no access violation.
+ */
+int
+mapupdate(char *requester, char *mapname, u_int op, u_int keylen, char *key,
+ u_int datalen, char *data)
+{
+ char updater[MAXMAPNAMELEN + 40];
+ FILE *childargs;
+ FILE *childrslt;
+#ifdef WEXITSTATUS
+ int status;
+#else
+ union wait status;
+#endif
+ pid_t pid;
+ u_int yperrno;
+
+
+#ifdef DEBUG
+ printf("%s %s\n", key, data);
+#endif
+ (void)sprintf(updater, "make -s -f %s/%s %s", YPDBPATH, /* !!! */
+ UPDATEFILE, mapname);
+ pid = _openchild(updater, &childargs, &childrslt);
+ if (pid < 0) {
+ return (YPERR_YPERR);
+ }
+
+ /*
+ * Write to child
+ */
+ (void)fprintf(childargs, "%s\n", requester);
+ (void)fprintf(childargs, "%u\n", op);
+ (void)fprintf(childargs, "%u\n", keylen);
+ (void)fwrite(key, (int)keylen, 1, childargs);
+ (void)fprintf(childargs, "\n");
+ (void)fprintf(childargs, "%u\n", datalen);
+ (void)fwrite(data, (int)datalen, 1, childargs);
+ (void)fprintf(childargs, "\n");
+ (void)fclose(childargs);
+
+ /*
+ * Read from child
+ */
+ (void)fscanf(childrslt, "%d", &yperrno);
+ (void)fclose(childrslt);
+
+ (void)wait(&status);
+#ifdef WEXITSTATUS
+ if (WEXITSTATUS(status) != 0)
+#else
+ if (status.w_retcode != 0)
+#endif
+ return (YPERR_YPERR);
+ return (yperrno);
+}
+
+/*
+ * returns pid, or -1 for failure
+ */
+static int
+_openchild(char *command, FILE **fto, FILE **ffrom)
+{
+ int i;
+ pid_t pid;
+ int pdto[2];
+ int pdfrom[2];
+ char *com;
+ struct rlimit rl;
+
+ if (pipe(pdto) < 0) {
+ goto error1;
+ }
+ if (pipe(pdfrom) < 0) {
+ goto error2;
+ }
+ switch (pid = fork()) {
+ case -1:
+ goto error3;
+
+ case 0:
+ /*
+ * child: read from pdto[0], write into pdfrom[1]
+ */
+ (void)close(0);
+ (void)dup(pdto[0]);
+ (void)close(1);
+ (void)dup(pdfrom[1]);
+ getrlimit(RLIMIT_NOFILE, &rl);
+ for (i = rl.rlim_max - 1; i >= 3; i--) {
+ (void) close(i);
+ }
+ com = malloc((unsigned) strlen(command) + 6);
+ if (com == NULL) {
+ _exit(~0);
+ }
+ (void)sprintf(com, "exec %s", command);
+ execl(SHELL, basename(SHELL), "-c", com, (char *)NULL);
+ _exit(~0);
+
+ default:
+ /*
+ * parent: write into pdto[1], read from pdfrom[0]
+ */
+ *fto = fdopen(pdto[1], "w");
+ (void)close(pdto[0]);
+ *ffrom = fdopen(pdfrom[0], "r");
+ (void)close(pdfrom[1]);
+ break;
+ }
+ return (pid);
+
+ /*
+ * error cleanup and return
+ */
+error3:
+ (void)close(pdfrom[0]);
+ (void)close(pdfrom[1]);
+error2:
+ (void)close(pdto[0]);
+ (void)close(pdto[1]);
+error1:
+ return (-1);
+}
+
+static char *
+basename(char *path)
+{
+ char *p;
+
+ p = strrchr(path, '/');
+ if (p == NULL) {
+ return (path);
+ } else {
+ return (p + 1);
+ }
+}
+
+#else /* YP */
+
+static int match(char *, char *);
+
+/*
+ * Determine if requester is allowed to update the given map,
+ * and update it if so. Returns the status, which is zero
+ * if there is no access violation. This function updates
+ * the local file and then shuts up.
+ */
+int
+localupdate(char *name, char *filename, u_int op, u_int keylen __unused,
+ char *key, u_int datalen __unused, char *data)
+{
+ char line[256];
+ FILE *rf;
+ FILE *wf;
+ char *tmpname;
+ int err;
+
+ /*
+ * Check permission
+ */
+ if (strcmp(name, key) != 0) {
+ return (ERR_ACCESS);
+ }
+ if (strcmp(name, "nobody") == 0) {
+ /*
+ * Can't change "nobody"s key.
+ */
+ return (ERR_ACCESS);
+ }
+
+ /*
+ * Open files
+ */
+ tmpname = malloc(strlen(filename) + 4);
+ if (tmpname == NULL) {
+ return (ERR_MALLOC);
+ }
+ sprintf(tmpname, "%s.tmp", filename);
+ rf = fopen(filename, "r");
+ if (rf == NULL) {
+ return (ERR_READ);
+ }
+ wf = fopen(tmpname, "w");
+ if (wf == NULL) {
+ return (ERR_WRITE);
+ }
+ err = -1;
+ while (fgets(line, sizeof (line), rf)) {
+ if (err < 0 && match(line, name)) {
+ switch (op) {
+ case YPOP_INSERT:
+ err = ERR_KEY;
+ break;
+ case YPOP_STORE:
+ case YPOP_CHANGE:
+ fprintf(wf, "%s %s\n", key, data);
+ err = 0;
+ break;
+ case YPOP_DELETE:
+ /* do nothing */
+ err = 0;
+ break;
+ }
+ } else {
+ fputs(line, wf);
+ }
+ }
+ if (err < 0) {
+ switch (op) {
+ case YPOP_CHANGE:
+ case YPOP_DELETE:
+ err = ERR_KEY;
+ break;
+ case YPOP_INSERT:
+ case YPOP_STORE:
+ err = 0;
+ fprintf(wf, "%s %s\n", key, data);
+ break;
+ }
+ }
+ fclose(wf);
+ fclose(rf);
+ if (err == 0) {
+ if (rename(tmpname, filename) < 0) {
+ return (ERR_DBASE);
+ }
+ } else {
+ if (unlink(tmpname) < 0) {
+ return (ERR_DBASE);
+ }
+ }
+ return (err);
+}
+
+static int
+match(char *line, char *name)
+{
+ int len;
+
+ len = strlen(name);
+ return (strncmp(line, name, len) == 0 &&
+ (line[len] == ' ' || line[len] == '\t'));
+}
+#endif /* !YP */
+
diff --git a/usr.sbin/rpc.ypupdated/yp_dbdelete.c b/usr.sbin/rpc.ypupdated/yp_dbdelete.c
new file mode 100644
index 0000000..d38e2be
--- /dev/null
+++ b/usr.sbin/rpc.ypupdated/yp_dbdelete.c
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 1995, 1996
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#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(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..44b2b9c
--- /dev/null
+++ b/usr.sbin/rpc.ypupdated/yp_dbupdate.c
@@ -0,0 +1,148 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#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(char *map, char *domain)
+{
+ int pid;
+
+ switch ((pid = fork())) {
+ case 0:
+ execlp(MAP_UPDATE_PATH, MAP_UPDATE, map, domain, (char *)NULL);
+ yp_error("couldn't exec map update process: %s",
+ strerror(errno));
+ exit(1);
+ break;
+ case -1:
+ yp_error("fork() failed: %s", strerror(errno));
+ return(YPERR_YPERR);
+ break;
+ default:
+ children++;
+ break;
+ }
+
+ return(0);
+}
+
+int
+ypmap_update(char *netname, char *map, unsigned int op, unsigned int keylen,
+ char *keyval, unsigned int datlen, char *datval)
+{
+ DB *dbp;
+ DBT key = { NULL, 0 }, data = { NULL, 0 };
+ char *yp_last = "YP_LAST_MODIFIED";
+ char yplastbuf[YPMAXRECORD];
+ char *domptr;
+ int rval = 0;
+
+ if ((domptr = strchr(netname, '@')) == NULL)
+ return(ERR_ACCESS);
+ domptr++;
+
+
+ dbp = yp_open_db_rw(domptr, map, O_RDWR);
+ if (dbp == NULL)
+ return(ERR_DBASE);
+
+ key.data = keyval;
+ key.size = keylen;
+ data.data = datval;
+ data.size = datlen;
+
+ switch (op) {
+ case YPOP_DELETE: /* delete this entry */
+ rval = yp_del_record(dbp, &key);
+ if (rval == YP_TRUE)
+ rval = 0;
+ break;
+ case YPOP_INSERT: /* add, do not change */
+ rval = yp_put_record(dbp, &key, &data, 0);
+ if (rval == YP_TRUE)
+ rval = 0;
+ break;
+ case YPOP_STORE: /* add, or change */
+ rval = yp_put_record(dbp, &key, &data, 1);
+ if (rval == YP_TRUE)
+ rval = 0;
+ break;
+ case YPOP_CHANGE: /* change, do not add */
+ if (yp_get_record(domptr, map, &key, &data, 0) != YP_TRUE) {
+ rval = ERR_KEY;
+ break;
+ }
+ rval = yp_put_record(dbp, &key, &data, 1);
+ if (rval == YP_TRUE)
+ rval = 0;
+ break;
+ default:
+ yp_error("unknown update command: (%d)", op);
+ }
+
+ if (rval) {
+ (void)(dbp->close)(dbp);
+ return(rval);
+ }
+
+ snprintf(yplastbuf, sizeof(yplastbuf), "%lu", time(NULL));
+ key.data = yp_last;
+ key.size = strlen(yp_last);
+ data.data = (char *)&yplastbuf;
+ data.size = strlen(yplastbuf);
+ if (yp_put_record(dbp, &key, &data, 1) != YP_TRUE) {
+ yp_error("failed to update timestamp in %s/%s", domptr, map);
+ (void)(dbp->close)(dbp);
+ return(ERR_DBASE);
+ }
+
+ (void)(dbp->close)(dbp);
+ return(yp_domake(map, domptr));
+}
diff --git a/usr.sbin/rpc.ypupdated/ypupdate b/usr.sbin/rpc.ypupdated/ypupdate
new file mode 100755
index 0000000..4a26f4b
--- /dev/null
+++ b/usr.sbin/rpc.ypupdated/ypupdate
@@ -0,0 +1,33 @@
+#!/bin/sh
+#
+# This script is invoked by rpc.ypupdatedd to propagate NIS maps
+# after the master map databases have been modified. It expects
+# to be passed two arguments: the name of the map that was updated
+# and the name of the domain where the map resides.
+# These are passed to /var/yp/Makefile.
+#
+# Comment out the LOG=yes line to disable logging.
+#
+# $FreeBSD$
+#
+
+LOG=yes
+LOGFILE=/var/yp/ypupdate.log
+
+umask 077
+
+if [ ! -f $LOGFILE ];
+then
+ /usr/bin/touch $LOGFILE
+ echo "# Edit /usr/libexec/yppwupdate to disable" >> $LOGFILE
+ echo "# logging to this file from yppasswdd." >> $LOGFILE
+ echo -n "# Log started on: " >> $LOGFILE
+ /bin/date >> $LOGFILE
+fi
+
+if [ ! $LOG ];
+then
+ cd /var/yp/$2; /usr/bin/make -f ../Makefile $1 2>&1
+else
+ cd /var/yp/$2; /usr/bin/make -f ../Makefile $1 >> $LOGFILE
+fi
diff --git a/usr.sbin/rpc.ypupdated/ypupdated_extern.h b/usr.sbin/rpc.ypupdated/ypupdated_extern.h
new file mode 100644
index 0000000..2cacb95
--- /dev/null
+++ b/usr.sbin/rpc.ypupdated/ypupdated_extern.h
@@ -0,0 +1,33 @@
+/*
+ * $FreeBSD$
+ */
+
+#include <db.h>
+
+#define YPOP_CHANGE 1 /* change, do not add */
+#define YPOP_INSERT 2 /* add, do not change */
+#define YPOP_DELETE 3 /* delete this entry */
+#define YPOP_STORE 4 /* add, or change */
+
+#define ERR_ACCESS 1
+#define ERR_MALLOC 2
+#define ERR_READ 3
+#define ERR_WRITE 4
+#define ERR_DBASE 5
+#define ERR_KEY 6
+
+#ifndef YPLIBDIR
+#define YPLIBDIR "/usr/libexec/"
+#endif
+
+#ifndef MAP_UPPATE
+#define MAP_UPDATE "ypupdate"
+#endif
+
+#define MAP_UPDATE_PATH YPLIBDIR MAP_UPDATE
+
+extern int children;
+extern void ypu_prog_1(struct svc_req *, register SVCXPRT *);
+extern int localupdate(char *, char *, u_int, u_int, char *, u_int, char *);
+extern int ypmap_update(char *, char *, u_int, u_int, char *, u_int, char *);
+extern int yp_del_record(DB *, DBT *);
diff --git a/usr.sbin/rpc.ypupdated/ypupdated_main.c b/usr.sbin/rpc.ypupdated/ypupdated_main.c
new file mode 100644
index 0000000..64302ab
--- /dev/null
+++ b/usr.sbin/rpc.ypupdated/ypupdated_main.c
@@ -0,0 +1,287 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#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>
+#ifdef __cplusplus
+#include <sysent.h> /* getdtablesize, open */
+#endif /* __cplusplus */
+#include <memory.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <syslog.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <err.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "ypupdated_extern.h"
+#include "yp_extern.h"
+
+#ifndef SIG_PF
+#define SIG_PF void(*)(int)
+#endif
+
+#ifdef DEBUG
+#define RPC_SVC_FG
+#endif
+
+#define _RPCSVC_CLOSEDOWN 120
+int _rpcpmstart; /* Started by a port monitor ? */
+static int _rpcfdtype;
+ /* Whether Stream or Datagram ? */
+ /* States a server can be in wrt request */
+
+#define _IDLE 0
+#define _SERVED 1
+#define _SERVING 2
+
+extern int _rpcsvcstate; /* Set when a request is serviced */
+
+char *progname = "rpc.ypupdated";
+char *yp_dir = "/var/yp/";
+
+static void
+_msgout(char* msg)
+{
+#ifdef RPC_SVC_FG
+ if (_rpcpmstart)
+ syslog(LOG_ERR, "%s", msg);
+ else
+ warnx("%s", msg);
+#else
+ syslog(LOG_ERR, "%s", msg);
+#endif
+}
+
+static void
+closedown(int sig)
+{
+ if (_rpcsvcstate == _IDLE) {
+ extern fd_set svc_fdset;
+ static int size;
+ int i, openfd;
+
+ if (_rpcfdtype == SOCK_DGRAM)
+ exit(0);
+ if (size == 0) {
+ size = getdtablesize();
+ }
+ for (i = 0, openfd = 0; i < size && openfd < 2; i++)
+ if (FD_ISSET(i, &svc_fdset))
+ openfd++;
+ if (openfd <= 1)
+ exit(0);
+ }
+ if (_rpcsvcstate == _SERVED)
+ _rpcsvcstate = _IDLE;
+
+ (void) signal(SIGALRM, (SIG_PF) closedown);
+ (void) alarm(_RPCSVC_CLOSEDOWN/2);
+}
+
+static void
+ypupdated_svc_run(void)
+{
+#ifdef FD_SETSIZE
+ fd_set readfds;
+#else
+ int readfds;
+#endif /* def FD_SETSIZE */
+ extern int forked;
+ int pid;
+ int fd_setsize = _rpc_dtablesize();
+
+ /* Establish the identity of the parent ypupdated process. */
+ pid = getpid();
+
+ for (;;) {
+#ifdef FD_SETSIZE
+ readfds = svc_fdset;
+#else
+ readfds = svc_fds;
+#endif /* def FD_SETSIZE */
+ switch (select(fd_setsize, &readfds, NULL, NULL,
+ (struct timeval *)0)) {
+ case -1:
+ if (errno == EINTR) {
+ continue;
+ }
+ warn("svc_run: - select failed");
+ return;
+ case 0:
+ continue;
+ default:
+ svc_getreqset(&readfds);
+ if (forked && pid != getpid())
+ exit(0);
+ }
+ }
+}
+
+static void
+reaper(int sig)
+{
+ int status;
+
+ if (sig == SIGHUP) {
+#ifdef foo
+ load_securenets();
+#endif
+ return;
+ }
+
+ if (sig == SIGCHLD) {
+ while (wait3(&status, WNOHANG, NULL) > 0)
+ children--;
+ } else {
+ (void) pmap_unset(YPU_PROG, YPU_VERS);
+ exit(0);
+ }
+}
+
+void
+usage(void)
+{
+ fprintf(stderr, "rpc.ypupdatedd [-p path]\n");
+ exit(0);
+}
+
+int
+main(int argc, char *argv[])
+{
+ register SVCXPRT *transp = NULL;
+ int sock;
+ int proto = 0;
+ struct sockaddr_in saddr;
+ int asize = sizeof (saddr);
+ int ch;
+
+ while ((ch = getopt(argc, argv, "p:h")) != -1) {
+ switch (ch) {
+ case 'p':
+ yp_dir = optarg;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+#ifdef foo
+ load_securenets();
+#endif
+
+ if (svc_auth_reg(AUTH_DES, _svcauth_des) == -1) {
+ yp_error("failed to register AUTH_DES flavor");
+ exit(1);
+ }
+
+ if (getsockname(0, (struct sockaddr *)&saddr, &asize) == 0) {
+ int ssize = sizeof (int);
+
+ if (saddr.sin_family != AF_INET)
+ exit(1);
+ if (getsockopt(0, SOL_SOCKET, SO_TYPE,
+ (char *)&_rpcfdtype, &ssize) == -1)
+ exit(1);
+ sock = 0;
+ _rpcpmstart = 1;
+ proto = 0;
+ openlog("rpc.ypupdatedd", LOG_PID, LOG_DAEMON);
+ } else {
+#ifndef RPC_SVC_FG
+ if (daemon(0,0)) {
+ err(1, "cannot fork");
+ }
+ openlog("rpc.ypupdated", LOG_PID, LOG_DAEMON);
+#endif
+ sock = RPC_ANYSOCK;
+ (void) pmap_unset(YPU_PROG, YPU_VERS);
+ }
+
+ if ((_rpcfdtype == 0) || (_rpcfdtype == SOCK_DGRAM)) {
+ transp = svcudp_create(sock);
+ if (transp == NULL) {
+ _msgout("cannot create udp service.");
+ exit(1);
+ }
+ if (!_rpcpmstart)
+ proto = IPPROTO_UDP;
+ if (!svc_register(transp, YPU_PROG, YPU_VERS, ypu_prog_1, proto)) {
+ _msgout("unable to register (YPU_PROG, YPU_VERS, udp).");
+ exit(1);
+ }
+ }
+
+ if ((_rpcfdtype == 0) || (_rpcfdtype == SOCK_STREAM)) {
+ transp = svctcp_create(sock, 0, 0);
+ if (transp == NULL) {
+ _msgout("cannot create tcp service.");
+ exit(1);
+ }
+ if (!_rpcpmstart)
+ proto = IPPROTO_TCP;
+ if (!svc_register(transp, YPU_PROG, YPU_VERS, ypu_prog_1, proto)) {
+ _msgout("unable to register (YPU_PROG, YPU_VERS, tcp).");
+ exit(1);
+ }
+ }
+
+ if (transp == (SVCXPRT *)NULL) {
+ _msgout("could not create a handle");
+ exit(1);
+ }
+ if (_rpcpmstart) {
+ (void) signal(SIGALRM, (SIG_PF) closedown);
+ (void) alarm(_RPCSVC_CLOSEDOWN/2);
+ }
+
+ (void) signal(SIGPIPE, SIG_IGN);
+ (void) signal(SIGCHLD, (SIG_PF) reaper);
+ (void) signal(SIGTERM, (SIG_PF) reaper);
+ (void) signal(SIGINT, (SIG_PF) reaper);
+ (void) signal(SIGHUP, (SIG_PF) reaper);
+
+ ypupdated_svc_run();
+ _msgout("svc_run returned");
+ exit(1);
+ /* NOTREACHED */
+}
diff --git a/usr.sbin/rpc.ypupdated/ypupdated_server.c b/usr.sbin/rpc.ypupdated/ypupdated_server.c
new file mode 100644
index 0000000..fb1017d
--- /dev/null
+++ b/usr.sbin/rpc.ypupdated/ypupdated_server.c
@@ -0,0 +1,230 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <rpc/rpc.h>
+#include <rpc/key_prot.h>
+#include <sys/param.h>
+#include <sys/cdefs.h>
+#include <rpcsvc/yp.h>
+#include "ypupdate_prot.h"
+#include "ypupdated_extern.h"
+#include "yp_extern.h"
+#include "ypxfr_extern.h"
+
+int children = 0;
+int forked = 0;
+
+/*
+ * Try to avoid spoofing: if a client chooses to use a very large
+ * window and then tries a bunch of randomly chosen encrypted timestamps,
+ * there's a chance he might stumble onto a valid combination.
+ * We therefore reject any RPCs with a window size larger than a preset
+ * value.
+ */
+#ifndef WINDOW
+#define WINDOW (60*60)
+#endif
+
+static enum auth_stat
+yp_checkauth(struct svc_req *svcreq)
+{
+ struct authdes_cred *des_cred;
+
+ switch (svcreq->rq_cred.oa_flavor) {
+ case AUTH_DES:
+ des_cred = (struct authdes_cred *) svcreq->rq_clntcred;
+ if (des_cred->adc_fullname.window > WINDOW) {
+ yp_error("warning: client-specified window size \
+was too large -- possible spoof attempt");
+ return(AUTH_BADCRED);
+ }
+ return(AUTH_OK);
+ break;
+ case AUTH_UNIX:
+ case AUTH_NONE:
+ yp_error("warning: client didn't use DES authentication");
+ return(AUTH_TOOWEAK);
+ break;
+ default:
+ yp_error("client used unknown auth flavor");
+ return(AUTH_REJECTEDCRED);
+ break;
+ }
+}
+
+unsigned int *
+ypu_change_1_svc(struct ypupdate_args *args, struct svc_req *svcreq)
+{
+ struct authdes_cred *des_cred;
+ static int res;
+ char *netname;
+ enum auth_stat astat;
+
+ res = 0;
+
+ astat = yp_checkauth(svcreq);
+
+ if (astat != AUTH_OK) {
+ svcerr_auth(svcreq->rq_xprt, astat);
+ return(&res);
+ }
+
+ des_cred = (struct authdes_cred *) svcreq->rq_clntcred;
+ netname = des_cred->adc_fullname.name;
+
+ res = localupdate(netname, "/etc/publickey", YPOP_CHANGE,
+ args->key.yp_buf_len, args->key.yp_buf_val,
+ args->datum.yp_buf_len, args->datum.yp_buf_val);
+
+ if (res)
+ return (&res);
+
+ res = ypmap_update(netname, args->mapname, YPOP_CHANGE,
+ args->key.yp_buf_len, args->key.yp_buf_val,
+ args->datum.yp_buf_len, args->datum.yp_buf_val);
+
+ return (&res);
+}
+
+unsigned int *
+ypu_insert_1_svc(struct ypupdate_args *args, struct svc_req *svcreq)
+{
+ struct authdes_cred *des_cred;
+ static int res;
+ char *netname;
+ enum auth_stat astat;
+
+ res = 0;
+
+ astat = yp_checkauth(svcreq);
+
+ if (astat != AUTH_OK) {
+ svcerr_auth(svcreq->rq_xprt, astat);
+ return(&res);
+ }
+
+ des_cred = (struct authdes_cred *) svcreq->rq_clntcred;
+ netname = des_cred->adc_fullname.name;
+
+ res = localupdate(netname, "/etc/publickey", YPOP_INSERT,
+ args->key.yp_buf_len, args->key.yp_buf_val,
+ args->datum.yp_buf_len, args->datum.yp_buf_val);
+
+ if (res)
+ return (&res);
+
+ res = ypmap_update(netname, args->mapname, YPOP_INSERT,
+ args->key.yp_buf_len, args->key.yp_buf_val,
+ args->datum.yp_buf_len, args->datum.yp_buf_val);
+
+ return (&res);
+}
+
+unsigned int *
+ypu_delete_1_svc(struct ypdelete_args *args, struct svc_req *svcreq)
+{
+ struct authdes_cred *des_cred;
+ static int res;
+ char *netname;
+ enum auth_stat astat;
+
+ res = 0;
+
+ astat = yp_checkauth(svcreq);
+
+ if (astat != AUTH_OK) {
+ svcerr_auth(svcreq->rq_xprt, astat);
+ return(&res);
+ }
+
+ des_cred = (struct authdes_cred *) svcreq->rq_clntcred;
+ netname = des_cred->adc_fullname.name;
+
+ res = localupdate(netname, "/etc/publickey", YPOP_DELETE,
+ args->key.yp_buf_len, args->key.yp_buf_val,
+ 0, NULL);
+
+ if (res)
+ return (&res);
+
+ res = ypmap_update(netname, args->mapname, YPOP_DELETE,
+ args->key.yp_buf_len, args->key.yp_buf_val,
+ 0, NULL);
+
+ return (&res);
+}
+
+unsigned int *
+ypu_store_1_svc(struct ypupdate_args *args, struct svc_req *svcreq)
+{
+ struct authdes_cred *des_cred;
+ static int res;
+ char *netname;
+ enum auth_stat astat;
+
+ res = 0;
+
+ astat = yp_checkauth(svcreq);
+
+ if (astat != AUTH_OK) {
+ svcerr_auth(svcreq->rq_xprt, astat);
+ return(&res);
+ }
+
+ des_cred = (struct authdes_cred *) svcreq->rq_clntcred;
+ netname = des_cred->adc_fullname.name;
+
+ res = localupdate(netname, "/etc/publickey", YPOP_STORE,
+ args->key.yp_buf_len, args->key.yp_buf_val,
+ args->datum.yp_buf_len, args->datum.yp_buf_val);
+
+ if (res)
+ return (&res);
+
+ res = ypmap_update(netname, args->mapname, YPOP_STORE,
+ args->key.yp_buf_len, args->key.yp_buf_val,
+ args->datum.yp_buf_len, args->datum.yp_buf_val);
+
+ return (&res);
+}
diff --git a/usr.sbin/rpc.ypxfrd/Makefile b/usr.sbin/rpc.ypxfrd/Makefile
new file mode 100644
index 0000000..70729b1
--- /dev/null
+++ b/usr.sbin/rpc.ypxfrd/Makefile
@@ -0,0 +1,35 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../usr.sbin/ypserv
+
+PROG= rpc.ypxfrd
+MAN= rpc.ypxfrd.8
+SRCS= ypxfrd_svc.c ypxfrd.h ypxfrd_server.c yp_error.c \
+ yp_access.c ypxfrd_main.c
+
+CFLAGS+= -I. -DXFRBLOCKSIZE=65535
+
+DPADD= ${LIBRPCSVC}
+LDADD= -lrpcsvc
+
+CLEANFILES= ypxfrd_svc.c ypxfrd.h
+
+RPCDIR= ${.CURDIR}/../../include/rpcsvc
+RPCGEN= rpcgen -I -C
+
+# We need to remove the 'static' keyword from _rpcsvcstate so that
+# ypxfrd_main.c can see it.
+ypxfrd_svc.c: ${RPCDIR}/ypxfrd.x
+ rm -f ${.TARGET}
+ ${RPCGEN} -m ${RPCDIR}/ypxfrd.x | \
+ sed s/"static int _rpcsvcstate"/"int _rpcsvcstate"/g > ${.TARGET}
+
+# ypxfrd_xdr.c: ${RPCDIR}/ypxfrd.x
+# rm -f ${.TARGET}
+# ${RPCGEN} -c -o ${.TARGET} ${RPCDIR}/ypxfrd.x
+
+ypxfrd.h: ${RPCDIR}/ypxfrd.x
+ rm -f ${.TARGET}
+ ${RPCGEN} -h -o ${.TARGET} ${RPCDIR}/ypxfrd.x
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rpc.ypxfrd/rpc.ypxfrd.8 b/usr.sbin/rpc.ypxfrd/rpc.ypxfrd.8
new file mode 100644
index 0000000..fc25ca6
--- /dev/null
+++ b/usr.sbin/rpc.ypxfrd/rpc.ypxfrd.8
@@ -0,0 +1,150 @@
+.\" Copyright (c) 1995, 1996
+.\" Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Bill Paul.
+.\" 4. Neither the name of the author nor the names of contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd June 2, 1996
+.Dt RPC.YPXFRD 8
+.Os
+.Sh NAME
+.Nm rpc.ypxfrd
+.Nd "NIS map transfer server"
+.Sh SYNOPSIS
+.Nm
+.Op Fl p Ar path
+.Sh DESCRIPTION
+The
+.Nm
+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 8 ,
+.Xr yppush 8 ,
+.Xr ypserv 8 ,
+.Xr ypxfr 8
+.Sh BUGS
+The
+.Fx
+.Nm ypxfrd
+protocol is not compatible with that used by SunOS.
+This is unfortunate
+but unavoidable: Sun's protocol is not freely available, and even if it
+were it would probably not be useful since the SunOS NIS v2 implementation
+uses the original ndbm package for its map databases whereas the
+.Fx
+implementation uses Berkeley DB.
+These two packages use vastly different
+file formats.
+Furthermore, ndbm is byte-order sensitive and not very
+smart about it, meaning that am ndbm database created on a big endian
+system can't be read on a little endian system.
+.Sh AUTHORS
+.An Bill Paul Aq wpaul@ctr.columbia.edu
diff --git a/usr.sbin/rpc.ypxfrd/ypxfrd_extern.h b/usr.sbin/rpc.ypxfrd/ypxfrd_extern.h
new file mode 100644
index 0000000..5aba934
--- /dev/null
+++ b/usr.sbin/rpc.ypxfrd/ypxfrd_extern.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 1995, 1996
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef MAX_CHILDREN
+#define MAX_CHILDREN 20
+#endif
+
+#ifndef XFRBLOCKSIZE
+#define XFRBLOCKSIZE YPXFRBLOCK
+#endif
+
+extern int forked;
+extern int children;
+extern void load_securenets(void);
+extern void yp_error(const char *, ...);
+extern int yp_access(const char *, const struct svc_req *);
+extern int yp_validdomain(const char *);
+extern char *yp_dir;
+extern void ypxfrd_freebsd_prog_1(struct svc_req *, register SVCXPRT *);
diff --git a/usr.sbin/rpc.ypxfrd/ypxfrd_main.c b/usr.sbin/rpc.ypxfrd/ypxfrd_main.c
new file mode 100644
index 0000000..7fa866a
--- /dev/null
+++ b/usr.sbin/rpc.ypxfrd/ypxfrd_main.c
@@ -0,0 +1,303 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include "ypxfrd.h"
+#include <err.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h> /* getenv, exit */
+#include <unistd.h>
+#include <rpc/pmap_clnt.h> /* for pmap_unset */
+#include <string.h> /* strcmp */
+#include <signal.h>
+#include <sys/ttycom.h> /* TIOCNOTTY */
+#ifdef __cplusplus
+#include <sysent.h> /* getdtablesize, open */
+#endif /* __cplusplus */
+#include <memory.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <syslog.h>
+#include "ypxfrd_extern.h"
+#include <sys/wait.h>
+#include <errno.h>
+
+#ifndef SIG_PF
+#define SIG_PF void(*)(int)
+#endif
+
+#ifdef DEBUG
+#define RPC_SVC_FG
+#endif
+
+#define _RPCSVC_CLOSEDOWN 120
+int _rpcpmstart; /* Started by a port monitor ? */
+static int _rpcfdtype;
+ /* Whether Stream or Datagram ? */
+ /* States a server can be in wrt request */
+
+#define _IDLE 0
+#define _SERVED 1
+#define _SERVING 2
+
+extern int _rpcsvcstate; /* Set when a request is serviced */
+
+char *progname = "rpc.ypxfrd";
+char *yp_dir = "/var/yp/";
+
+static void
+_msgout(char *msg)
+{
+#ifdef RPC_SVC_FG
+ if (_rpcpmstart)
+ syslog(LOG_ERR, "%s", msg);
+ else
+ warnx("%s", msg);
+#else
+ syslog(LOG_ERR, "%s", msg);
+#endif
+}
+
+static void
+closedown(int sig)
+{
+ if (_rpcsvcstate == _IDLE) {
+ extern fd_set svc_fdset;
+ static int size;
+ int i, openfd;
+
+ if (_rpcfdtype == SOCK_DGRAM)
+ exit(0);
+ if (size == 0) {
+ size = getdtablesize();
+ }
+ for (i = 0, openfd = 0; i < size && openfd < 2; i++)
+ if (FD_ISSET(i, &svc_fdset))
+ openfd++;
+ if (openfd <= 1)
+ exit(0);
+ }
+ if (_rpcsvcstate == _SERVED)
+ _rpcsvcstate = _IDLE;
+
+ (void) signal(SIGALRM, (SIG_PF) closedown);
+ (void) alarm(_RPCSVC_CLOSEDOWN/2);
+}
+
+
+static void
+ypxfrd_svc_run(void)
+{
+#ifdef FD_SETSIZE
+ fd_set readfds;
+#else
+ int readfds;
+#endif /* def FD_SETSIZE */
+ extern int forked;
+ int pid;
+ int fd_setsize = _rpc_dtablesize();
+
+ /* Establish the identity of the parent ypserv process. */
+ pid = getpid();
+
+ for (;;) {
+#ifdef FD_SETSIZE
+ readfds = svc_fdset;
+#else
+ readfds = svc_fds;
+#endif /* def FD_SETSIZE */
+ switch (select(fd_setsize, &readfds, NULL, NULL,
+ (struct timeval *)0)) {
+ case -1:
+ if (errno == EINTR) {
+ continue;
+ }
+ warn("svc_run: - select failed");
+ return;
+ case 0:
+ continue;
+ default:
+ svc_getreqset(&readfds);
+ if (forked && pid != getpid())
+ exit(0);
+ }
+ }
+}
+
+static void reaper(int sig)
+{
+ int status;
+ int saved_errno;
+
+ saved_errno = errno;
+
+ if (sig == SIGHUP) {
+ load_securenets();
+ errno = saved_errno;
+ return;
+ }
+
+ if (sig == SIGCHLD) {
+ while (wait3(&status, WNOHANG, NULL) > 0)
+ children--;
+ } else {
+ (void) pmap_unset(YPXFRD_FREEBSD_PROG, YPXFRD_FREEBSD_VERS);
+ exit(0);
+ }
+
+ errno = saved_errno;
+ return;
+}
+
+void
+usage(void)
+{
+ fprintf(stderr, "usage: rpc.ypxfrd [-p path]\n");
+ exit(0);
+}
+
+int
+main(int argc, char *argv[])
+{
+ register SVCXPRT *transp = NULL;
+ int sock;
+ int proto = 0;
+ struct sockaddr_in saddr;
+ int asize = sizeof (saddr);
+ int ch;
+
+ while ((ch = getopt(argc, argv, "p:h")) != -1) {
+ switch (ch) {
+ case 'p':
+ yp_dir = optarg;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+
+ load_securenets();
+
+ if (getsockname(0, (struct sockaddr *)&saddr, &asize) == 0) {
+ int ssize = sizeof (int);
+
+ if (saddr.sin_family != AF_INET)
+ exit(1);
+ if (getsockopt(0, SOL_SOCKET, SO_TYPE,
+ (char *)&_rpcfdtype, &ssize) == -1)
+ exit(1);
+ sock = 0;
+ _rpcpmstart = 1;
+ proto = 0;
+ openlog("rpc.ypxfrd", LOG_PID, LOG_DAEMON);
+ } else {
+#ifndef RPC_SVC_FG
+ int size;
+ int pid, i;
+
+ pid = fork();
+ if (pid < 0)
+ err(1, "fork");
+ if (pid)
+ exit(0);
+ size = getdtablesize();
+ for (i = 0; i < size; i++)
+ (void) close(i);
+ i = open(_PATH_CONSOLE, 2);
+ (void) dup2(i, 1);
+ (void) dup2(i, 2);
+ i = open(_PATH_TTY, 2);
+ if (i >= 0) {
+ (void) ioctl(i, TIOCNOTTY, (char *)NULL);
+ (void) close(i);
+ }
+ openlog("rpc.ypxfrd", LOG_PID, LOG_DAEMON);
+#endif
+ sock = RPC_ANYSOCK;
+ (void) pmap_unset(YPXFRD_FREEBSD_PROG, YPXFRD_FREEBSD_VERS);
+ }
+
+ if ((_rpcfdtype == 0) || (_rpcfdtype == SOCK_DGRAM)) {
+ transp = svcudp_create(sock);
+ if (transp == NULL) {
+ _msgout("cannot create udp service.");
+ exit(1);
+ }
+ if (!_rpcpmstart)
+ proto = IPPROTO_UDP;
+ if (!svc_register(transp, YPXFRD_FREEBSD_PROG, YPXFRD_FREEBSD_VERS, ypxfrd_freebsd_prog_1, proto)) {
+ _msgout("unable to register (YPXFRD_FREEBSD_PROG, YPXFRD_FREEBSD_VERS, udp).");
+ exit(1);
+ }
+ }
+
+ if ((_rpcfdtype == 0) || (_rpcfdtype == SOCK_STREAM)) {
+ transp = svctcp_create(sock, 0, 0);
+ if (transp == NULL) {
+ _msgout("cannot create tcp service.");
+ exit(1);
+ }
+ if (!_rpcpmstart)
+ proto = IPPROTO_TCP;
+ if (!svc_register(transp, YPXFRD_FREEBSD_PROG, YPXFRD_FREEBSD_VERS, ypxfrd_freebsd_prog_1, proto)) {
+ _msgout("unable to register (YPXFRD_FREEBSD_PROG, YPXFRD_FREEBSD_VERS, tcp).");
+ exit(1);
+ }
+ }
+
+ if (transp == (SVCXPRT *)NULL) {
+ _msgout("could not create a handle");
+ exit(1);
+ }
+ if (_rpcpmstart) {
+ (void) signal(SIGALRM, (SIG_PF) closedown);
+ (void) alarm(_RPCSVC_CLOSEDOWN/2);
+ }
+
+ (void) signal(SIGPIPE, SIG_IGN);
+ (void) signal(SIGCHLD, (SIG_PF) reaper);
+ (void) signal(SIGTERM, (SIG_PF) reaper);
+ (void) signal(SIGINT, (SIG_PF) reaper);
+ (void) signal(SIGHUP, (SIG_PF) reaper);
+
+ ypxfrd_svc_run();
+ _msgout("svc_run returned");
+ exit(1);
+ /* NOTREACHED */
+}
diff --git a/usr.sbin/rpc.ypxfrd/ypxfrd_server.c b/usr.sbin/rpc.ypxfrd/ypxfrd_server.c
new file mode 100644
index 0000000..3b53004
--- /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[] =
+ "$FreeBSD$";
+#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(buf, R_OK) == -1) {
+ result.xfr_u.xfrstat = XFR_ACCESS;
+ return(&result);
+ }
+
+ if (argp->xfr_db_type != XFR_DB_BSD_HASH &&
+ argp->xfr_db_type != XFR_DB_ANY) {
+ result.xfr_u.xfrstat = XFR_DB_TYPE_MISMATCH;
+ return(&result);
+ }
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+ if (argp->xfr_byte_order == XFR_ENDIAN_BIG) {
+#else
+ if (argp->xfr_byte_order == XFR_ENDIAN_LITTLE) {
+#endif
+ result.xfr_u.xfrstat = XFR_DB_ENDIAN_MISMATCH;
+ return(&result);
+ }
+
+#ifndef DEBUG
+ if (children < MAX_CHILDREN && fork()) {
+ children++;
+ forked = 0;
+ return (NULL);
+ } else {
+ forked++;
+ }
+#endif
+ if ((fp = open(buf, O_RDONLY)) == -1) {
+ result.xfr_u.xfrstat = XFR_READ_ERR;
+ return(&result);
+ }
+
+ /* Start sending the file. */
+
+ svc_sendreply(rqstp->rq_xprt, (xdrproc_t)xdr_my_xfr, &result);
+
+ close(fp);
+
+ return (NULL);
+}
diff --git a/usr.sbin/rpcbind/Makefile b/usr.sbin/rpcbind/Makefile
new file mode 100644
index 0000000..ba50992
--- /dev/null
+++ b/usr.sbin/rpcbind/Makefile
@@ -0,0 +1,21 @@
+# $NetBSD: Makefile,v 1.3 2000/06/20 13:56:43 fvdl Exp $
+# $FreeBSD$
+
+LIBCDIR= ${.CURDIR}/../../lib/libc
+LIBCRPCDIR= ${LIBCDIR}/rpc
+LIBCINCLUDE= ${LIBCDIR}/include
+
+.PATH: ${LIBCRPCDIR}
+
+PROG= rpcbind
+MAN= rpcbind.8
+SRCS= check_bound.c rpcb_stat.c rpcb_svc_4.c rpcbind.c pmap_svc.c \
+ rpcb_svc.c rpcb_svc_com.c security.c warmstart.c util.c \
+ rpc_generic.c
+
+CFLAGS+= -I${LIBCRPCDIR} -I${LIBCINCLUDE} -DPORTMAP -DINET6 -DLIBWRAP
+
+LDADD= -lwrap -lutil
+DPADD= ${LIBWRAP} ${LIBUTIL}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rpcbind/check_bound.c b/usr.sbin/rpcbind/check_bound.c
new file mode 100644
index 0000000..c955506
--- /dev/null
+++ b/usr.sbin/rpcbind/check_bound.c
@@ -0,0 +1,231 @@
+/* $NetBSD: check_bound.c,v 1.2 2000/06/22 08:09:26 fvdl Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+/*
+ * Copyright (c) 1986 - 1991 by Sun Microsystems, Inc.
+ */
+
+/* #ident "@(#)check_bound.c 1.15 93/07/05 SMI" */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)check_bound.c 1.11 89/04/21 Copyr 1989 Sun Micro";
+#endif
+#endif
+
+/*
+ * check_bound.c
+ * Checks to see whether the program is still bound to the
+ * claimed address and returns the univeral merged address
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <rpc/rpc.h>
+#include <stdio.h>
+#include <netconfig.h>
+#include <syslog.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "rpcbind.h"
+
+struct fdlist {
+ int fd;
+ struct netconfig *nconf;
+ struct fdlist *next;
+ int check_binding;
+};
+
+static struct fdlist *fdhead; /* Link list of the check fd's */
+static struct fdlist *fdtail;
+static char *nullstring = "";
+
+static bool_t check_bound __P((struct fdlist *, char *uaddr));
+
+/*
+ * Returns 1 if the given address is bound for the given addr & transport
+ * For all error cases, we assume that the address is bound
+ * Returns 0 for success.
+ */
+static bool_t
+check_bound(struct fdlist *fdl, char *uaddr)
+{
+ int fd;
+ struct netbuf *na;
+ int ans;
+
+ if (fdl->check_binding == FALSE)
+ return (TRUE);
+
+ na = uaddr2taddr(fdl->nconf, uaddr);
+ if (!na)
+ return (TRUE); /* punt, should never happen */
+
+ fd = __rpc_nconf2fd(fdl->nconf);
+ if (fd < 0) {
+ free(na->buf);
+ free(na);
+ return (TRUE);
+ }
+
+ ans = bind(fd, (struct sockaddr *)na->buf, na->len);
+
+ close(fd);
+ free(na->buf);
+ free(na);
+
+ return (ans == 0 ? FALSE : TRUE);
+}
+
+int
+add_bndlist(struct netconfig *nconf, struct netbuf *baddr)
+{
+ struct fdlist *fdl;
+ struct netconfig *newnconf;
+
+ newnconf = getnetconfigent(nconf->nc_netid);
+ if (newnconf == NULL)
+ return (-1);
+ fdl = malloc(sizeof (struct fdlist));
+ if (fdl == NULL) {
+ freenetconfigent(newnconf);
+ syslog(LOG_ERR, "no memory!");
+ return (-1);
+ }
+ fdl->nconf = newnconf;
+ fdl->next = NULL;
+ if (fdhead == NULL) {
+ fdhead = fdl;
+ fdtail = fdl;
+ } else {
+ fdtail->next = fdl;
+ fdtail = fdl;
+ }
+ /* XXX no bound checking for now */
+ fdl->check_binding = FALSE;
+
+ return 0;
+}
+
+bool_t
+is_bound(char *netid, char *uaddr)
+{
+ struct fdlist *fdl;
+
+ for (fdl = fdhead; fdl; fdl = fdl->next)
+ if (strcmp(fdl->nconf->nc_netid, netid) == 0)
+ break;
+ if (fdl == NULL)
+ return (TRUE);
+ return (check_bound(fdl, uaddr));
+}
+
+/*
+ * Returns NULL if there was some system error.
+ * Returns "" if the address was not bound, i.e the server crashed.
+ * Returns the merged address otherwise.
+ */
+char *
+mergeaddr(SVCXPRT *xprt, char *netid, char *uaddr, char *saddr)
+{
+ struct fdlist *fdl;
+ char *c_uaddr, *s_uaddr, *m_uaddr, *allocated_uaddr = NULL;
+
+ for (fdl = fdhead; fdl; fdl = fdl->next)
+ if (strcmp(fdl->nconf->nc_netid, netid) == 0)
+ break;
+ if (fdl == NULL)
+ return (NULL);
+ if (check_bound(fdl, uaddr) == FALSE)
+ /* that server died */
+ return (nullstring);
+ /*
+ * If saddr is not NULL, the remote client may have included the
+ * address by which it contacted us. Use that for the "client" uaddr,
+ * otherwise use the info from the SVCXPRT.
+ */
+ if (saddr != NULL) {
+ c_uaddr = saddr;
+ } else {
+ c_uaddr = taddr2uaddr(fdl->nconf, svc_getrpccaller(xprt));
+ if (c_uaddr == NULL) {
+ syslog(LOG_ERR, "taddr2uaddr failed for %s",
+ fdl->nconf->nc_netid);
+ return (NULL);
+ }
+ allocated_uaddr = c_uaddr;
+ }
+
+#ifdef ND_DEBUG
+ if (debugging) {
+ if (saddr == NULL) {
+ fprintf(stderr, "mergeaddr: client uaddr = %s\n",
+ c_uaddr);
+ } else {
+ fprintf(stderr, "mergeaddr: contact uaddr = %s\n",
+ c_uaddr);
+ }
+ }
+#endif
+ s_uaddr = uaddr;
+ /*
+ * This is all we should need for IP 4 and 6
+ */
+ m_uaddr = addrmerge(svc_getrpccaller(xprt), s_uaddr, c_uaddr, netid);
+#ifdef ND_DEBUG
+ if (debugging)
+ fprintf(stderr, "mergeaddr: uaddr = %s, merged uaddr = %s\n",
+ uaddr, m_uaddr);
+#endif
+ if (allocated_uaddr != NULL)
+ free(allocated_uaddr);
+ return (m_uaddr);
+}
+
+/*
+ * Returns a netconf structure from its internal list. This
+ * structure should not be freed.
+ */
+struct netconfig *
+rpcbind_get_conf(char *netid)
+{
+ struct fdlist *fdl;
+
+ for (fdl = fdhead; fdl; fdl = fdl->next)
+ if (strcmp(fdl->nconf->nc_netid, netid) == 0)
+ break;
+ if (fdl == NULL)
+ return (NULL);
+ return (fdl->nconf);
+}
diff --git a/usr.sbin/rpcbind/pmap_svc.c b/usr.sbin/rpcbind/pmap_svc.c
new file mode 100644
index 0000000..9adb558
--- /dev/null
+++ b/usr.sbin/rpcbind/pmap_svc.c
@@ -0,0 +1,368 @@
+/* $NetBSD: pmap_svc.c,v 1.2 2000/10/20 11:49:40 fvdl Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+/*
+ * Copyright (c) 1984 - 1991 by Sun Microsystems, Inc.
+ */
+
+/* #ident "@(#)pmap_svc.c 1.14 93/07/05 SMI" */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)pmap_svc.c 1.23 89/04/05 Copyr 1984 Sun Micro";
+#endif
+#endif
+
+/*
+ * pmap_svc.c
+ * The server procedure for the version 2 portmaper.
+ * All the portmapper related interface from the portmap side.
+ */
+
+#ifdef PORTMAP
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <stdio.h>
+#include <rpc/rpc.h>
+#include <rpc/pmap_prot.h>
+#include <rpc/rpcb_prot.h>
+#ifdef RPCBIND_DEBUG
+#include <stdlib.h>
+#endif
+#include "rpcbind.h"
+
+static struct pmaplist *find_service_pmap __P((rpcprog_t, rpcvers_t,
+ rpcprot_t));
+static bool_t pmapproc_change __P((struct svc_req *, SVCXPRT *, u_long));
+static bool_t pmapproc_getport __P((struct svc_req *, SVCXPRT *));
+static bool_t pmapproc_dump __P((struct svc_req *, SVCXPRT *));
+
+/*
+ * Called for all the version 2 inquiries.
+ */
+void
+pmap_service(struct svc_req *rqstp, SVCXPRT *xprt)
+{
+ rpcbs_procinfo(RPCBVERS_2_STAT, rqstp->rq_proc);
+ switch (rqstp->rq_proc) {
+ case PMAPPROC_NULL:
+ /*
+ * Null proc call
+ */
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "PMAPPROC_NULL\n");
+#endif
+ check_access(xprt, rqstp->rq_proc, NULL, PMAPVERS);
+ if ((!svc_sendreply(xprt, (xdrproc_t) xdr_void, NULL)) &&
+ debugging) {
+ if (doabort) {
+ rpcbind_abort();
+ }
+ }
+ break;
+
+ case PMAPPROC_SET:
+ /*
+ * Set a program, version to port mapping
+ */
+ pmapproc_change(rqstp, xprt, rqstp->rq_proc);
+ break;
+
+ case PMAPPROC_UNSET:
+ /*
+ * Remove a program, version to port mapping.
+ */
+ pmapproc_change(rqstp, xprt, rqstp->rq_proc);
+ break;
+
+ case PMAPPROC_GETPORT:
+ /*
+ * Lookup the mapping for a program, version and return its
+ * port number.
+ */
+ pmapproc_getport(rqstp, xprt);
+ break;
+
+ case PMAPPROC_DUMP:
+ /*
+ * Return the current set of mapped program, version
+ */
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "PMAPPROC_DUMP\n");
+#endif
+ pmapproc_dump(rqstp, xprt);
+ break;
+
+ case PMAPPROC_CALLIT:
+ /*
+ * Calls a procedure on the local machine. If the requested
+ * procedure is not registered this procedure does not return
+ * error information!!
+ * This procedure is only supported on rpc/udp and calls via
+ * rpc/udp. It passes null authentication parameters.
+ */
+ rpcbproc_callit_com(rqstp, xprt, PMAPPROC_CALLIT, PMAPVERS);
+ break;
+
+ default:
+ svcerr_noproc(xprt);
+ break;
+ }
+}
+
+/*
+ * returns the item with the given program, version number. If that version
+ * number is not found, it returns the item with that program number, so that
+ * the port number is now returned to the caller. The caller when makes a
+ * call to this program, version number, the call will fail and it will
+ * return with PROGVERS_MISMATCH. The user can then determine the highest
+ * and the lowest version number for this program using clnt_geterr() and
+ * use those program version numbers.
+ */
+static struct pmaplist *
+find_service_pmap(rpcprog_t prog, rpcvers_t vers, rpcprot_t prot)
+{
+ register struct pmaplist *hit = NULL;
+ register struct pmaplist *pml;
+
+ for (pml = list_pml; pml != NULL; pml = pml->pml_next) {
+ if ((pml->pml_map.pm_prog != prog) ||
+ (pml->pml_map.pm_prot != prot))
+ continue;
+ hit = pml;
+ if (pml->pml_map.pm_vers == vers)
+ break;
+ }
+ return (hit);
+}
+
+static bool_t
+pmapproc_change(struct svc_req *rqstp, SVCXPRT *xprt, unsigned long op)
+{
+ struct pmap reg;
+ RPCB rpcbreg;
+ long ans;
+ struct sockaddr_in *who;
+ uid_t uid;
+ char uidbuf[32];
+
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "%s request for (%lu, %lu) : ",
+ op == PMAPPROC_SET ? "PMAP_SET" : "PMAP_UNSET",
+ reg.pm_prog, reg.pm_vers);
+#endif
+
+ if (!svc_getargs(xprt, (xdrproc_t) xdr_pmap, (char *)&reg)) {
+ svcerr_decode(xprt);
+ return (FALSE);
+ }
+
+ if (!check_access(xprt, op, &reg, PMAPVERS)) {
+ svcerr_weakauth(xprt);
+ return FALSE;
+ }
+
+ who = svc_getcaller(xprt);
+
+ /*
+ * Can't use getpwnam here. We might end up calling ourselves
+ * and looping.
+ */
+ if (__rpc_get_local_uid(xprt, &uid) < 0)
+ rpcbreg.r_owner = "unknown";
+ else if (uid == 0)
+ rpcbreg.r_owner = "superuser";
+ else {
+ /* r_owner will be strdup-ed later */
+ snprintf(uidbuf, sizeof uidbuf, "%d", uid);
+ rpcbreg.r_owner = uidbuf;
+ }
+
+ rpcbreg.r_prog = reg.pm_prog;
+ rpcbreg.r_vers = reg.pm_vers;
+
+ if (op == PMAPPROC_SET) {
+ char buf[32];
+
+ snprintf(buf, sizeof buf, "0.0.0.0.%d.%d",
+ (int)((reg.pm_port >> 8) & 0xff),
+ (int)(reg.pm_port & 0xff));
+ rpcbreg.r_addr = buf;
+ if (reg.pm_prot == IPPROTO_UDP) {
+ rpcbreg.r_netid = udptrans;
+ } else if (reg.pm_prot == IPPROTO_TCP) {
+ rpcbreg.r_netid = tcptrans;
+ } else {
+ ans = FALSE;
+ goto done_change;
+ }
+ ans = map_set(&rpcbreg, rpcbreg.r_owner);
+ } else if (op == PMAPPROC_UNSET) {
+ bool_t ans1, ans2;
+
+ rpcbreg.r_addr = NULL;
+ rpcbreg.r_netid = tcptrans;
+ ans1 = map_unset(&rpcbreg, rpcbreg.r_owner);
+ rpcbreg.r_netid = udptrans;
+ ans2 = map_unset(&rpcbreg, rpcbreg.r_owner);
+ ans = ans1 || ans2;
+ } else {
+ ans = FALSE;
+ }
+done_change:
+ if ((!svc_sendreply(xprt, (xdrproc_t) xdr_long, (caddr_t) &ans)) &&
+ debugging) {
+ fprintf(stderr, "portmap: svc_sendreply\n");
+ if (doabort) {
+ rpcbind_abort();
+ }
+ }
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "%s\n", ans == TRUE ? "succeeded" : "failed");
+#endif
+ if (op == PMAPPROC_SET)
+ rpcbs_set(RPCBVERS_2_STAT, ans);
+ else
+ rpcbs_unset(RPCBVERS_2_STAT, ans);
+ return (TRUE);
+}
+
+/* ARGSUSED */
+static bool_t
+pmapproc_getport(struct svc_req *rqstp, SVCXPRT *xprt)
+{
+ struct pmap reg;
+ long lport;
+ int port = 0;
+ struct pmaplist *fnd;
+#ifdef RPCBIND_DEBUG
+ char *uaddr;
+#endif
+
+ if (!svc_getargs(xprt, (xdrproc_t) xdr_pmap, (char *)&reg)) {
+ svcerr_decode(xprt);
+ return (FALSE);
+ }
+
+ if (!check_access(xprt, PMAPPROC_GETPORT, &reg, PMAPVERS)) {
+ svcerr_weakauth(xprt);
+ return FALSE;
+ }
+
+#ifdef RPCBIND_DEBUG
+ if (debugging) {
+ uaddr = taddr2uaddr(rpcbind_get_conf(xprt->xp_netid),
+ svc_getrpccaller(xprt));
+ fprintf(stderr, "PMAP_GETPORT req for (%lu, %lu, %s) from %s :",
+ reg.pm_prog, reg.pm_vers,
+ reg.pm_prot == IPPROTO_UDP ? "udp" : "tcp", uaddr);
+ free(uaddr);
+ }
+#endif
+ fnd = find_service_pmap(reg.pm_prog, reg.pm_vers, reg.pm_prot);
+ if (fnd) {
+ char serveuaddr[32], *ua;
+ int h1, h2, h3, h4, p1, p2;
+ char *netid;
+
+ if (reg.pm_prot == IPPROTO_UDP) {
+ ua = udp_uaddr;
+ netid = udptrans;
+ } else {
+ ua = tcp_uaddr; /* To get the len */
+ netid = tcptrans;
+ }
+ if (ua == NULL) {
+ goto sendreply;
+ }
+ if (sscanf(ua, "%d.%d.%d.%d.%d.%d", &h1, &h2, &h3,
+ &h4, &p1, &p2) == 6) {
+ p1 = (fnd->pml_map.pm_port >> 8) & 0xff;
+ p2 = (fnd->pml_map.pm_port) & 0xff;
+ snprintf(serveuaddr, sizeof serveuaddr,
+ "%d.%d.%d.%d.%d.%d", h1, h2, h3, h4, p1, p2);
+ if (is_bound(netid, serveuaddr)) {
+ port = fnd->pml_map.pm_port;
+ } else { /* this service is dead; delete it */
+ delete_prog(reg.pm_prog);
+ }
+ }
+ }
+sendreply:
+ lport = port;
+ if ((!svc_sendreply(xprt, (xdrproc_t) xdr_long, (caddr_t)&lport)) &&
+ debugging) {
+ (void) fprintf(stderr, "portmap: svc_sendreply\n");
+ if (doabort) {
+ rpcbind_abort();
+ }
+ }
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "port = %d\n", port);
+#endif
+ rpcbs_getaddr(RPCBVERS_2_STAT, reg.pm_prog, reg.pm_vers,
+ reg.pm_prot == IPPROTO_UDP ? udptrans : tcptrans,
+ port ? udptrans : "");
+
+ return (TRUE);
+}
+
+/* ARGSUSED */
+static bool_t
+pmapproc_dump(struct svc_req *rqstp, SVCXPRT *xprt)
+{
+ if (!svc_getargs(xprt, (xdrproc_t)xdr_void, NULL)) {
+ svcerr_decode(xprt);
+ return (FALSE);
+ }
+
+ if (!check_access(xprt, PMAPPROC_DUMP, NULL, PMAPVERS)) {
+ svcerr_weakauth(xprt);
+ return FALSE;
+ }
+
+ if ((!svc_sendreply(xprt, (xdrproc_t) xdr_pmaplist_ptr,
+ (caddr_t)&list_pml)) && debugging) {
+ if (debugging)
+ (void) fprintf(stderr, "portmap: svc_sendreply\n");
+ if (doabort) {
+ rpcbind_abort();
+ }
+ }
+ return (TRUE);
+}
+
+#endif /* PORTMAP */
diff --git a/usr.sbin/rpcbind/rpcb_stat.c b/usr.sbin/rpcbind/rpcb_stat.c
new file mode 100644
index 0000000..0cf219e
--- /dev/null
+++ b/usr.sbin/rpcbind/rpcb_stat.c
@@ -0,0 +1,208 @@
+/*
+ * $NetBSD: rpcb_stat.c,v 1.2 2000/07/04 20:27:40 matt Exp $
+ * $FreeBSD$
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+/* #pragma ident "@(#)rpcb_stat.c 1.7 94/04/25 SMI" */
+
+/*
+ * rpcb_stat.c
+ * Allows for gathering of statistics
+ *
+ * Copyright (c) 1990 by Sun Microsystems, Inc.
+ */
+
+#include <stdio.h>
+#include <netconfig.h>
+#include <rpc/rpc.h>
+#include <rpc/rpcb_prot.h>
+#include <sys/stat.h>
+#ifdef PORTMAP
+#include <rpc/pmap_prot.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include "rpcbind.h"
+
+static rpcb_stat_byvers inf;
+
+void
+rpcbs_init()
+{
+
+}
+
+void
+rpcbs_procinfo(rpcvers_t rtype, rpcproc_t proc)
+{
+ switch (rtype + 2) {
+#ifdef PORTMAP
+ case PMAPVERS: /* version 2 */
+ if (proc > rpcb_highproc_2)
+ return;
+ break;
+#endif
+ case RPCBVERS: /* version 3 */
+ if (proc > rpcb_highproc_3)
+ return;
+ break;
+ case RPCBVERS4: /* version 4 */
+ if (proc > rpcb_highproc_4)
+ return;
+ break;
+ default: return;
+ }
+ inf[rtype].info[proc]++;
+ return;
+}
+
+void
+rpcbs_set(rpcvers_t rtype, bool_t success)
+{
+ if ((rtype >= RPCBVERS_STAT) || (success == FALSE))
+ return;
+ inf[rtype].setinfo++;
+ return;
+}
+
+void
+rpcbs_unset(rpcvers_t rtype, bool_t success)
+{
+ if ((rtype >= RPCBVERS_STAT) || (success == FALSE))
+ return;
+ inf[rtype].unsetinfo++;
+ return;
+}
+
+void
+rpcbs_getaddr(rpcvers_t rtype, rpcprog_t prog, rpcvers_t vers, char *netid,
+ char *uaddr)
+{
+ rpcbs_addrlist *al;
+ struct netconfig *nconf;
+
+ if (rtype >= RPCBVERS_STAT)
+ return;
+ for (al = inf[rtype].addrinfo; al; al = al->next) {
+
+ if(al->netid == NULL)
+ return;
+ if ((al->prog == prog) && (al->vers == vers) &&
+ (strcmp(al->netid, netid) == 0)) {
+ if ((uaddr == NULL) || (uaddr[0] == NULL))
+ al->failure++;
+ else
+ al->success++;
+ return;
+ }
+ }
+ nconf = rpcbind_get_conf(netid);
+ if (nconf == NULL) {
+ return;
+ }
+ al = (rpcbs_addrlist *) malloc(sizeof (rpcbs_addrlist));
+ if (al == NULL) {
+ return;
+ }
+ al->prog = prog;
+ al->vers = vers;
+ al->netid = nconf->nc_netid;
+ if ((uaddr == NULL) || (uaddr[0] == '\0')) {
+ al->failure = 1;
+ al->success = 0;
+ } else {
+ al->failure = 0;
+ al->success = 1;
+ }
+ al->next = inf[rtype].addrinfo;
+ inf[rtype].addrinfo = al;
+}
+
+void
+rpcbs_rmtcall(rpcvers_t rtype, rpcproc_t rpcbproc, rpcprog_t prog,
+ rpcvers_t vers, rpcproc_t proc, char *netid, rpcblist_ptr rbl)
+{
+ rpcbs_rmtcalllist *rl;
+ struct netconfig *nconf;
+
+ if (rtype > RPCBVERS_STAT)
+ return;
+ for (rl = inf[rtype].rmtinfo; rl; rl = rl->next) {
+
+ if(rl->netid == NULL)
+ return;
+
+ if ((rl->prog == prog) && (rl->vers == vers) &&
+ (rl->proc == proc) &&
+ (strcmp(rl->netid, netid) == 0)) {
+ if ((rbl == NULL) ||
+ (rbl->rpcb_map.r_vers != vers))
+ rl->failure++;
+ else
+ rl->success++;
+ if (rpcbproc == RPCBPROC_INDIRECT)
+ rl->indirect++;
+ return;
+ }
+ }
+ nconf = rpcbind_get_conf(netid);
+ if (nconf == NULL) {
+ return;
+ }
+ rl = (rpcbs_rmtcalllist *) malloc(sizeof (rpcbs_rmtcalllist));
+ if (rl == NULL) {
+ return;
+ }
+ rl->prog = prog;
+ rl->vers = vers;
+ rl->proc = proc;
+ rl->netid = nconf->nc_netid;
+ if ((rbl == NULL) ||
+ (rbl->rpcb_map.r_vers != vers)) {
+ rl->failure = 1;
+ rl->success = 0;
+ } else {
+ rl->failure = 0;
+ rl->success = 1;
+ }
+ rl->indirect = 1;
+ rl->next = inf[rtype].rmtinfo;
+ inf[rtype].rmtinfo = rl;
+ return;
+}
+
+/*
+ */
+void *
+rpcbproc_getstat(void *arg, struct svc_req *req, SVCXPRT *xprt,
+ rpcvers_t versnum)
+{
+ return (void *)&inf;
+}
diff --git a/usr.sbin/rpcbind/rpcb_svc.c b/usr.sbin/rpcbind/rpcb_svc.c
new file mode 100644
index 0000000..b01e9ac
--- /dev/null
+++ b/usr.sbin/rpcbind/rpcb_svc.c
@@ -0,0 +1,233 @@
+/* $NetBSD: rpcb_svc.c,v 1.1 2000/06/02 23:15:41 fvdl Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+/*
+ * Copyright (c) 1986 - 1991 by Sun Microsystems, Inc.
+ */
+
+/* #ident "@(#)rpcb_svc.c 1.16 93/07/05 SMI" */
+
+/*
+ * rpcb_svc.c
+ * The server procedure for the version 3 rpcbind (TLI).
+ *
+ * It maintains a separate list of all the registered services with the
+ * version 3 of rpcbind.
+ */
+#include <sys/types.h>
+#include <rpc/rpc.h>
+#include <rpc/rpcb_prot.h>
+#include <netconfig.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "rpcbind.h"
+
+static void *rpcbproc_getaddr_3_local __P((void *, struct svc_req *, SVCXPRT *,
+ rpcvers_t));
+static void *rpcbproc_dump_3_local __P((void *, struct svc_req *, SVCXPRT *,
+ rpcvers_t));
+
+/*
+ * Called by svc_getreqset. There is a separate server handle for
+ * every transport that it waits on.
+ */
+void
+rpcb_service_3(struct svc_req *rqstp, SVCXPRT *transp)
+{
+ union {
+ RPCB rpcbproc_set_3_arg;
+ RPCB rpcbproc_unset_3_arg;
+ RPCB rpcbproc_getaddr_3_local_arg;
+ struct rpcb_rmtcallargs rpcbproc_callit_3_arg;
+ char *rpcbproc_uaddr2taddr_3_arg;
+ struct netbuf rpcbproc_taddr2uaddr_3_arg;
+ } argument;
+ char *result;
+ xdrproc_t xdr_argument, xdr_result;
+ void *(*local) __P((void *, struct svc_req *, SVCXPRT *, rpcvers_t));
+
+ rpcbs_procinfo(RPCBVERS_3_STAT, rqstp->rq_proc);
+
+ switch (rqstp->rq_proc) {
+ case NULLPROC:
+ /*
+ * Null proc call
+ */
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "RPCBPROC_NULL\n");
+#endif
+ /* This call just logs, no actual checks */
+ check_access(transp, rqstp->rq_proc, NULL, RPCBVERS);
+ (void) svc_sendreply(transp, (xdrproc_t)xdr_void, (char *)NULL);
+ return;
+
+ case RPCBPROC_SET:
+ xdr_argument = (xdrproc_t )xdr_rpcb;
+ xdr_result = (xdrproc_t )xdr_bool;
+ local = rpcbproc_set_com;
+ break;
+
+ case RPCBPROC_UNSET:
+ xdr_argument = (xdrproc_t)xdr_rpcb;
+ xdr_result = (xdrproc_t)xdr_bool;
+ local = rpcbproc_unset_com;
+ break;
+
+ case RPCBPROC_GETADDR:
+ xdr_argument = (xdrproc_t)xdr_rpcb;
+ xdr_result = (xdrproc_t)xdr_wrapstring;
+ local = rpcbproc_getaddr_3_local;
+ break;
+
+ case RPCBPROC_DUMP:
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "RPCBPROC_DUMP\n");
+#endif
+ xdr_argument = (xdrproc_t)xdr_void;
+ xdr_result = (xdrproc_t)xdr_rpcblist_ptr;
+ local = rpcbproc_dump_3_local;
+ break;
+
+ case RPCBPROC_CALLIT:
+ rpcbproc_callit_com(rqstp, transp, rqstp->rq_proc, RPCBVERS);
+ return;
+
+ case RPCBPROC_GETTIME:
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "RPCBPROC_GETTIME\n");
+#endif
+ xdr_argument = (xdrproc_t)xdr_void;
+ xdr_result = (xdrproc_t)xdr_u_long;
+ local = rpcbproc_gettime_com;
+ break;
+
+ case RPCBPROC_UADDR2TADDR:
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "RPCBPROC_UADDR2TADDR\n");
+#endif
+ xdr_argument = (xdrproc_t)xdr_wrapstring;
+ xdr_result = (xdrproc_t)xdr_netbuf;
+ local = rpcbproc_uaddr2taddr_com;
+ break;
+
+ case RPCBPROC_TADDR2UADDR:
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "RPCBPROC_TADDR2UADDR\n");
+#endif
+ xdr_argument = (xdrproc_t)xdr_netbuf;
+ xdr_result = (xdrproc_t)xdr_wrapstring;
+ local = rpcbproc_taddr2uaddr_com;
+ break;
+
+ default:
+ svcerr_noproc(transp);
+ return;
+ }
+ (void) memset((char *)&argument, 0, sizeof (argument));
+ if (!svc_getargs(transp, (xdrproc_t) xdr_argument,
+ (char *) &argument)) {
+ svcerr_decode(transp);
+ if (debugging)
+ (void) fprintf(stderr, "rpcbind: could not decode\n");
+ return;
+ }
+ if (!check_access(transp, rqstp->rq_proc, &argument, RPCBVERS)) {
+ svcerr_weakauth(transp);
+ goto done;
+ }
+ result = (*local)(&argument, rqstp, transp, RPCBVERS);
+ if (result != NULL && !svc_sendreply(transp, (xdrproc_t)xdr_result,
+ result)) {
+ svcerr_systemerr(transp);
+ if (debugging) {
+ (void) fprintf(stderr, "rpcbind: svc_sendreply\n");
+ if (doabort) {
+ rpcbind_abort();
+ }
+ }
+ }
+done:
+ if (!svc_freeargs(transp, (xdrproc_t)xdr_argument, (char *)
+ &argument)) {
+ if (debugging) {
+ (void) fprintf(stderr, "unable to free arguments\n");
+ if (doabort) {
+ rpcbind_abort();
+ }
+ }
+ }
+}
+
+/*
+ * Lookup the mapping for a program, version and return its
+ * address. Assuming that the caller wants the address of the
+ * server running on the transport on which the request came.
+ *
+ * We also try to resolve the universal address in terms of
+ * address of the caller.
+ */
+/* ARGSUSED */
+static void *
+rpcbproc_getaddr_3_local(void *arg, struct svc_req *rqstp, SVCXPRT *transp,
+ rpcvers_t versnum)
+{
+ RPCB *regp = (RPCB *)arg;
+#ifdef RPCBIND_DEBUG
+ if (debugging) {
+ char *uaddr;
+
+ uaddr = taddr2uaddr(rpcbind_get_conf(transp->xp_netid),
+ svc_getrpccaller(transp));
+ fprintf(stderr, "RPCB_GETADDR req for (%lu, %lu, %s) from %s: ",
+ (unsigned long)regp->r_prog, (unsigned long)regp->r_vers,
+ regp->r_netid, uaddr);
+ free(uaddr);
+ }
+#endif
+ return (rpcbproc_getaddr_com(regp, rqstp, transp, RPCBVERS,
+ RPCB_ALLVERS));
+}
+
+/* ARGSUSED */
+static void *
+rpcbproc_dump_3_local(void *arg, struct svc_req *rqstp, SVCXPRT *transp,
+ rpcvers_t versnum)
+{
+ return ((void *)&list_rbl);
+}
diff --git a/usr.sbin/rpcbind/rpcb_svc_4.c b/usr.sbin/rpcbind/rpcb_svc_4.c
new file mode 100644
index 0000000..0b2d4a8
--- /dev/null
+++ b/usr.sbin/rpcbind/rpcb_svc_4.c
@@ -0,0 +1,455 @@
+/*
+ * $NetBSD: rpcb_svc_4.c,v 1.1 2000/06/02 23:15:41 fvdl Exp $
+ * $FreeBSD$
+ */
+
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+/*
+ * Copyright (c) 1986 - 1991 by Sun Microsystems, Inc.
+ */
+
+/* #ident "@(#)rpcb_svc_4.c 1.8 93/07/05 SMI" */
+
+/*
+ * rpcb_svc_4.c
+ * The server procedure for the version 4 rpcbind.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <rpc/rpc.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <netconfig.h>
+#include <syslog.h>
+#include <string.h>
+#include <stdlib.h>
+#include "rpcbind.h"
+
+static void *rpcbproc_getaddr_4_local __P((void *, struct svc_req *, SVCXPRT *,
+ rpcvers_t));
+static void *rpcbproc_getversaddr_4_local __P((void *, struct svc_req *, SVCXPRT *, rpcvers_t));
+static void *rpcbproc_getaddrlist_4_local
+ __P((void *, struct svc_req *, SVCXPRT *, rpcvers_t));
+static void free_rpcb_entry_list __P((rpcb_entry_list_ptr *));
+static void *rpcbproc_dump_4_local __P((void *, struct svc_req *, SVCXPRT *, rpcvers_t));
+
+/*
+ * Called by svc_getreqset. There is a separate server handle for
+ * every transport that it waits on.
+ */
+void
+rpcb_service_4(struct svc_req *rqstp, SVCXPRT *transp)
+{
+ union {
+ rpcb rpcbproc_set_4_arg;
+ rpcb rpcbproc_unset_4_arg;
+ rpcb rpcbproc_getaddr_4_local_arg;
+ char *rpcbproc_uaddr2taddr_4_arg;
+ struct netbuf rpcbproc_taddr2uaddr_4_arg;
+ } argument;
+ char *result;
+ xdrproc_t xdr_argument, xdr_result;
+ void *(*local) __P((void *, struct svc_req *, SVCXPRT *, rpcvers_t));
+
+ rpcbs_procinfo(RPCBVERS_4_STAT, rqstp->rq_proc);
+
+ switch (rqstp->rq_proc) {
+ case NULLPROC:
+ /*
+ * Null proc call
+ */
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "RPCBPROC_NULL\n");
+#endif
+ check_access(transp, rqstp->rq_proc, NULL, RPCBVERS4);
+ (void) svc_sendreply(transp, (xdrproc_t) xdr_void,
+ (char *)NULL);
+ return;
+
+ case RPCBPROC_SET:
+ /*
+ * Check to see whether the message came from
+ * loopback transports (for security reasons)
+ */
+ xdr_argument = (xdrproc_t)xdr_rpcb;
+ xdr_result = (xdrproc_t)xdr_bool;
+ local = rpcbproc_set_com;
+ break;
+
+ case RPCBPROC_UNSET:
+ /*
+ * Check to see whether the message came from
+ * loopback transports (for security reasons)
+ */
+ xdr_argument = (xdrproc_t)xdr_rpcb;
+ xdr_result = (xdrproc_t)xdr_bool;
+ local = rpcbproc_unset_com;
+ break;
+
+ case RPCBPROC_GETADDR:
+ xdr_argument = (xdrproc_t)xdr_rpcb;
+ xdr_result = (xdrproc_t)xdr_wrapstring;
+ local = rpcbproc_getaddr_4_local;
+ break;
+
+ case RPCBPROC_GETVERSADDR:
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "RPCBPROC_GETVERSADDR\n");
+#endif
+ xdr_argument = (xdrproc_t)xdr_rpcb;
+ xdr_result = (xdrproc_t)xdr_wrapstring;
+ local = rpcbproc_getversaddr_4_local;
+ break;
+
+ case RPCBPROC_DUMP:
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "RPCBPROC_DUMP\n");
+#endif
+ xdr_argument = (xdrproc_t)xdr_void;
+ xdr_result = (xdrproc_t)xdr_rpcblist_ptr;
+ local = rpcbproc_dump_4_local;
+ break;
+
+ case RPCBPROC_INDIRECT:
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "RPCBPROC_INDIRECT\n");
+#endif
+ rpcbproc_callit_com(rqstp, transp, rqstp->rq_proc, RPCBVERS4);
+ return;
+
+/* case RPCBPROC_CALLIT: */
+ case RPCBPROC_BCAST:
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "RPCBPROC_BCAST\n");
+#endif
+ rpcbproc_callit_com(rqstp, transp, rqstp->rq_proc, RPCBVERS4);
+ return;
+
+ case RPCBPROC_GETTIME:
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "RPCBPROC_GETTIME\n");
+#endif
+ xdr_argument = (xdrproc_t)xdr_void;
+ xdr_result = (xdrproc_t)xdr_u_long;
+ local = rpcbproc_gettime_com;
+ break;
+
+ case RPCBPROC_UADDR2TADDR:
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "RPCBPROC_UADDR2TADDR\n");
+#endif
+ xdr_argument = (xdrproc_t)xdr_wrapstring;
+ xdr_result = (xdrproc_t)xdr_netbuf;
+ local = rpcbproc_uaddr2taddr_com;
+ break;
+
+ case RPCBPROC_TADDR2UADDR:
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "RPCBPROC_TADDR2UADDR\n");
+#endif
+ xdr_argument = (xdrproc_t)xdr_netbuf;
+ xdr_result = (xdrproc_t)xdr_wrapstring;
+ local = rpcbproc_taddr2uaddr_com;
+ break;
+
+ case RPCBPROC_GETADDRLIST:
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "RPCBPROC_GETADDRLIST\n");
+#endif
+ xdr_argument = (xdrproc_t)xdr_rpcb;
+ xdr_result = (xdrproc_t)xdr_rpcb_entry_list_ptr;
+ local = rpcbproc_getaddrlist_4_local;
+ break;
+
+ case RPCBPROC_GETSTAT:
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "RPCBPROC_GETSTAT\n");
+#endif
+ xdr_argument = (xdrproc_t)xdr_void;
+ xdr_result = (xdrproc_t)xdr_rpcb_stat_byvers;
+ local = rpcbproc_getstat;
+ break;
+
+ default:
+ svcerr_noproc(transp);
+ return;
+ }
+ memset((char *)&argument, 0, sizeof (argument));
+ if (!svc_getargs(transp, (xdrproc_t) xdr_argument,
+ (char *)&argument)) {
+ svcerr_decode(transp);
+ if (debugging)
+ (void) fprintf(stderr, "rpcbind: could not decode\n");
+ return;
+ }
+ if (!check_access(transp, rqstp->rq_proc, &argument, RPCBVERS4)) {
+ svcerr_weakauth(transp);
+ goto done;
+ }
+ result = (*local)(&argument, rqstp, transp, RPCBVERS4);
+ if (result != NULL && !svc_sendreply(transp, (xdrproc_t) xdr_result,
+ result)) {
+ svcerr_systemerr(transp);
+ if (debugging) {
+ (void) fprintf(stderr, "rpcbind: svc_sendreply\n");
+ if (doabort) {
+ rpcbind_abort();
+ }
+ }
+ }
+done:
+ if (!svc_freeargs(transp, (xdrproc_t) xdr_argument,
+ (char *)&argument)) {
+ if (debugging) {
+ (void) fprintf(stderr, "unable to free arguments\n");
+ if (doabort) {
+ rpcbind_abort();
+ }
+ }
+ }
+ return;
+}
+
+/*
+ * Lookup the mapping for a program, version and return its
+ * address. Assuming that the caller wants the address of the
+ * server running on the transport on which the request came.
+ * Even if a service with a different version number is available,
+ * it will return that address. The client should check with an
+ * clnt_call to verify whether the service is the one that is desired.
+ * We also try to resolve the universal address in terms of
+ * address of the caller.
+ */
+/* ARGSUSED */
+static void *
+rpcbproc_getaddr_4_local(void *arg, struct svc_req *rqstp, SVCXPRT *transp,
+ rpcvers_t rpcbversnum)
+{
+ RPCB *regp = (RPCB *)arg;
+#ifdef RPCBIND_DEBUG
+ if (debugging) {
+ char *uaddr;
+
+ uaddr = taddr2uaddr(rpcbind_get_conf(transp->xp_netid),
+ svc_getrpccaller(transp));
+ fprintf(stderr, "RPCB_GETADDR req for (%lu, %lu, %s) from %s: ",
+ (unsigned long)regp->r_prog, (unsigned long)regp->r_vers,
+ regp->r_netid, uaddr);
+ free(uaddr);
+ }
+#endif
+ return (rpcbproc_getaddr_com(regp, rqstp, transp, RPCBVERS4,
+ RPCB_ALLVERS));
+}
+
+/*
+ * Lookup the mapping for a program, version and return its
+ * address. Assuming that the caller wants the address of the
+ * server running on the transport on which the request came.
+ *
+ * We also try to resolve the universal address in terms of
+ * address of the caller.
+ */
+/* ARGSUSED */
+static void *
+rpcbproc_getversaddr_4_local(void *arg, struct svc_req *rqstp, SVCXPRT *transp,
+ rpcvers_t versnum)
+{
+ RPCB *regp = (RPCB *)arg;
+#ifdef RPCBIND_DEBUG
+ if (debugging) {
+ char *uaddr;
+
+ uaddr = taddr2uaddr(rpcbind_get_conf(transp->xp_netid),
+ svc_getrpccaller(transp));
+ fprintf(stderr, "RPCB_GETVERSADDR rqst for (%lu, %lu, %s)"
+ " from %s : ",
+ (unsigned long)regp->r_prog, (unsigned long)regp->r_vers,
+ regp->r_netid, uaddr);
+ free(uaddr);
+ }
+#endif
+ return (rpcbproc_getaddr_com(regp, rqstp, transp, RPCBVERS4,
+ RPCB_ONEVERS));
+}
+
+/*
+ * Lookup the mapping for a program, version and return the
+ * addresses for all transports in the current transport family.
+ * We return a merged address.
+ */
+/* ARGSUSED */
+static void *
+rpcbproc_getaddrlist_4_local(void *arg, struct svc_req *rqstp, SVCXPRT *transp,
+ rpcvers_t versnum)
+{
+ RPCB *regp = (RPCB *)arg;
+ static rpcb_entry_list_ptr rlist;
+ register rpcblist_ptr rbl;
+ rpcb_entry_list_ptr rp, tail;
+ rpcprog_t prog;
+ rpcvers_t vers;
+ rpcb_entry *a;
+ struct netconfig *nconf;
+ struct netconfig *reg_nconf;
+ char *saddr, *maddr = NULL;
+
+ free_rpcb_entry_list(&rlist);
+ tail = NULL;
+ prog = regp->r_prog;
+ vers = regp->r_vers;
+ reg_nconf = rpcbind_get_conf(transp->xp_netid);
+ if (reg_nconf == NULL)
+ return (NULL);
+ if (*(regp->r_addr) != '\0') {
+ saddr = regp->r_addr;
+ } else {
+ saddr = NULL;
+ }
+#ifdef RPCBIND_DEBUG
+ if (debugging) {
+ fprintf(stderr, "r_addr: %s r_netid: %s nc_protofmly: %s\n",
+ regp->r_addr, regp->r_netid, reg_nconf->nc_protofmly);
+ }
+#endif
+ for (rbl = list_rbl; rbl != NULL; rbl = rbl->rpcb_next) {
+ if ((rbl->rpcb_map.r_prog == prog) &&
+ (rbl->rpcb_map.r_vers == vers)) {
+ nconf = rpcbind_get_conf(rbl->rpcb_map.r_netid);
+ if (nconf == NULL)
+ goto fail;
+ if (strcmp(nconf->nc_protofmly, reg_nconf->nc_protofmly)
+ != 0) {
+ continue; /* not same proto family */
+ }
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "\tmerge with: %s\n",
+ rbl->rpcb_map.r_addr);
+#endif
+ if ((maddr = mergeaddr(transp, rbl->rpcb_map.r_netid,
+ rbl->rpcb_map.r_addr, saddr)) == NULL) {
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, " FAILED\n");
+#endif
+ continue;
+ } else if (!maddr[0]) {
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, " SUCCEEDED, but port died - maddr: nullstring\n");
+#endif
+ /* The server died. Unset this combination */
+ delete_prog(regp->r_prog);
+ continue;
+ }
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, " SUCCEEDED maddr: %s\n", maddr);
+#endif
+ /*
+ * Add it to rlist.
+ */
+ rp = malloc(sizeof (rpcb_entry_list));
+ if (rp == NULL)
+ goto fail;
+ a = &rp->rpcb_entry_map;
+ a->r_maddr = maddr;
+ a->r_nc_netid = nconf->nc_netid;
+ a->r_nc_semantics = nconf->nc_semantics;
+ a->r_nc_protofmly = nconf->nc_protofmly;
+ a->r_nc_proto = nconf->nc_proto;
+ rp->rpcb_entry_next = NULL;
+ if (rlist == NULL) {
+ rlist = rp;
+ tail = rp;
+ } else {
+ tail->rpcb_entry_next = rp;
+ tail = rp;
+ }
+ rp = NULL;
+ }
+ }
+#ifdef RPCBIND_DEBUG
+ if (debugging) {
+ for (rp = rlist; rp; rp = rp->rpcb_entry_next) {
+ fprintf(stderr, "\t%s %s\n", rp->rpcb_entry_map.r_maddr,
+ rp->rpcb_entry_map.r_nc_proto);
+ }
+ }
+#endif
+ /*
+ * XXX: getaddrlist info is also being stuffed into getaddr.
+ * Perhaps wrong, but better than it not getting counted at all.
+ */
+ rpcbs_getaddr(RPCBVERS4 - 2, prog, vers, transp->xp_netid, maddr);
+ return (void *)&rlist;
+
+fail: free_rpcb_entry_list(&rlist);
+ return (NULL);
+}
+
+/*
+ * Free only the allocated structure, rest is all a pointer to some
+ * other data somewhere else.
+ */
+static void
+free_rpcb_entry_list(rpcb_entry_list_ptr *rlistp)
+{
+ register rpcb_entry_list_ptr rbl, tmp;
+
+ for (rbl = *rlistp; rbl != NULL; ) {
+ tmp = rbl;
+ rbl = rbl->rpcb_entry_next;
+ free((char *)tmp->rpcb_entry_map.r_maddr);
+ free((char *)tmp);
+ }
+ *rlistp = NULL;
+}
+
+/* ARGSUSED */
+static void *
+rpcbproc_dump_4_local(void *arg, struct svc_req *req, SVCXPRT *xprt,
+ rpcvers_t versnum)
+{
+ return ((void *)&list_rbl);
+}
diff --git a/usr.sbin/rpcbind/rpcb_svc_com.c b/usr.sbin/rpcbind/rpcb_svc_com.c
new file mode 100644
index 0000000..ad9275e
--- /dev/null
+++ b/usr.sbin/rpcbind/rpcb_svc_com.c
@@ -0,0 +1,1463 @@
+/* $NetBSD: rpcb_svc_com.c,v 1.6 2000/08/03 00:07:22 fvdl Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+/*
+ * Copyright (c) 1986 - 1991 by Sun Microsystems, Inc.
+ */
+
+/* #ident "@(#)rpcb_svc_com.c 1.18 94/05/02 SMI" */
+
+/*
+ * rpcb_svc_com.c
+ * The commom server procedure for the rpcbind.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+#include <rpc/rpc.h>
+#include <rpc/rpcb_prot.h>
+#include <rpc/svc_dg.h>
+#include <netconfig.h>
+#include <errno.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <stdio.h>
+#ifdef PORTMAP
+#include <netinet/in.h>
+#include <rpc/pmap_prot.h>
+#endif /* PORTMAP */
+#include <string.h>
+#include <stdlib.h>
+
+#include "rpcbind.h"
+
+#define RPC_BUF_MAX 65536 /* can be raised if required */
+
+static char *nullstring = "";
+static int rpcb_rmtcalls;
+
+struct rmtcallfd_list {
+ int fd;
+ SVCXPRT *xprt;
+ char *netid;
+ struct rmtcallfd_list *next;
+};
+
+#define NFORWARD 64
+#define MAXTIME_OFF 300 /* 5 minutes */
+
+struct finfo {
+ int flag;
+#define FINFO_ACTIVE 0x1
+ u_int32_t caller_xid;
+ struct netbuf *caller_addr;
+ u_int32_t forward_xid;
+ int forward_fd;
+ char *uaddr;
+ rpcproc_t reply_type;
+ rpcvers_t versnum;
+ time_t time;
+};
+static struct finfo FINFO[NFORWARD];
+
+
+static bool_t xdr_encap_parms __P((XDR *, struct encap_parms *));
+static bool_t xdr_rmtcall_args __P((XDR *, struct r_rmtcall_args *));
+static bool_t xdr_rmtcall_result __P((XDR *, struct r_rmtcall_args *));
+static bool_t xdr_opaque_parms __P((XDR *, struct r_rmtcall_args *));
+static int find_rmtcallfd_by_netid __P((char *));
+static SVCXPRT *find_rmtcallxprt_by_fd __P((int));
+static int forward_register __P((u_int32_t, struct netbuf *, int, char *,
+ rpcproc_t, rpcvers_t, u_int32_t *));
+static struct finfo *forward_find __P((u_int32_t));
+static int free_slot_by_xid __P((u_int32_t));
+static int free_slot_by_index __P((int));
+static int netbufcmp __P((struct netbuf *, struct netbuf *));
+static struct netbuf *netbufdup __P((struct netbuf *));
+static void netbuffree __P((struct netbuf *));
+static int check_rmtcalls __P((struct pollfd *, int));
+static void xprt_set_caller __P((SVCXPRT *, struct finfo *));
+static void send_svcsyserr __P((SVCXPRT *, struct finfo *));
+static void handle_reply __P((int, SVCXPRT *));
+static void find_versions __P((rpcprog_t, char *, rpcvers_t *, rpcvers_t *));
+static rpcblist_ptr find_service __P((rpcprog_t, rpcvers_t, char *));
+static char *getowner __P((SVCXPRT *, char *, size_t));
+static int add_pmaplist __P((RPCB *));
+static int del_pmaplist __P((RPCB *));
+
+/*
+ * Set a mapping of program, version, netid
+ */
+/* ARGSUSED */
+void *
+rpcbproc_set_com(void *arg, struct svc_req *rqstp, SVCXPRT *transp,
+ rpcvers_t rpcbversnum)
+{
+ RPCB *regp = (RPCB *)arg;
+ static bool_t ans;
+ char owner[64];
+
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "RPCB_SET request for (%lu, %lu, %s, %s) : ",
+ (unsigned long)regp->r_prog, (unsigned long)regp->r_vers,
+ regp->r_netid, regp->r_addr);
+#endif
+ ans = map_set(regp, getowner(transp, owner, sizeof owner));
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "%s\n", ans == TRUE ? "succeeded" : "failed");
+#endif
+ /* XXX: should have used some defined constant here */
+ rpcbs_set(rpcbversnum - 2, ans);
+ return (void *)&ans;
+}
+
+bool_t
+map_set(RPCB *regp, char *owner)
+{
+ RPCB reg, *a;
+ rpcblist_ptr rbl, fnd;
+
+ reg = *regp;
+ /*
+ * check to see if already used
+ * find_service returns a hit even if
+ * the versions don't match, so check for it
+ */
+ fnd = find_service(reg.r_prog, reg.r_vers, reg.r_netid);
+ if (fnd && (fnd->rpcb_map.r_vers == reg.r_vers)) {
+ if (!strcmp(fnd->rpcb_map.r_addr, reg.r_addr))
+ /*
+ * if these match then it is already
+ * registered so just say "OK".
+ */
+ return (TRUE);
+ else
+ return (FALSE);
+ }
+ /*
+ * add to the end of the list
+ */
+ rbl = malloc(sizeof (RPCBLIST));
+ if (rbl == NULL)
+ return (FALSE);
+ a = &(rbl->rpcb_map);
+ a->r_prog = reg.r_prog;
+ a->r_vers = reg.r_vers;
+ a->r_netid = strdup(reg.r_netid);
+ a->r_addr = strdup(reg.r_addr);
+ a->r_owner = strdup(owner);
+ if (!a->r_addr || !a->r_netid || !a->r_owner) {
+ if (a->r_netid)
+ free(a->r_netid);
+ if (a->r_addr)
+ free(a->r_addr);
+ if (a->r_owner)
+ free(a->r_owner);
+ free(rbl);
+ return (FALSE);
+ }
+ rbl->rpcb_next = (rpcblist_ptr)NULL;
+ if (list_rbl == NULL) {
+ list_rbl = rbl;
+ } else {
+ for (fnd = list_rbl; fnd->rpcb_next;
+ fnd = fnd->rpcb_next)
+ ;
+ fnd->rpcb_next = rbl;
+ }
+#ifdef PORTMAP
+ (void) add_pmaplist(regp);
+#endif
+ return (TRUE);
+}
+
+/*
+ * Unset a mapping of program, version, netid
+ */
+/* ARGSUSED */
+void *
+rpcbproc_unset_com(void *arg, struct svc_req *rqstp, SVCXPRT *transp,
+ rpcvers_t rpcbversnum)
+{
+ RPCB *regp = (RPCB *)arg;
+ static bool_t ans;
+ char owner[64];
+
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "RPCB_UNSET request for (%lu, %lu, %s) : ",
+ (unsigned long)regp->r_prog, (unsigned long)regp->r_vers,
+ regp->r_netid);
+#endif
+ ans = map_unset(regp, getowner(transp, owner, sizeof owner));
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "%s\n", ans == TRUE ? "succeeded" : "failed");
+#endif
+ /* XXX: should have used some defined constant here */
+ rpcbs_unset(rpcbversnum - 2, ans);
+ return (void *)&ans;
+}
+
+bool_t
+map_unset(RPCB *regp, char *owner)
+{
+ int ans = 0;
+ rpcblist_ptr rbl, prev, tmp;
+
+ if (owner == NULL)
+ return (0);
+
+ for (prev = NULL, rbl = list_rbl; rbl; /* cstyle */) {
+ if ((rbl->rpcb_map.r_prog != regp->r_prog) ||
+ (rbl->rpcb_map.r_vers != regp->r_vers) ||
+ (regp->r_netid[0] && strcasecmp(regp->r_netid,
+ rbl->rpcb_map.r_netid))) {
+ /* both rbl & prev move forwards */
+ prev = rbl;
+ rbl = rbl->rpcb_next;
+ continue;
+ }
+ /*
+ * Check whether appropriate uid. Unset only
+ * if superuser or the owner itself.
+ */
+ if (strcmp(owner, "superuser") &&
+ strcmp(rbl->rpcb_map.r_owner, owner))
+ return (0);
+ /* found it; rbl moves forward, prev stays */
+ ans = 1;
+ tmp = rbl;
+ rbl = rbl->rpcb_next;
+ if (prev == NULL)
+ list_rbl = rbl;
+ else
+ prev->rpcb_next = rbl;
+ free(tmp->rpcb_map.r_addr);
+ free(tmp->rpcb_map.r_netid);
+ free(tmp->rpcb_map.r_owner);
+ free(tmp);
+ }
+#ifdef PORTMAP
+ if (ans)
+ (void) del_pmaplist(regp);
+#endif
+ /*
+ * We return 1 either when the entry was not there or it
+ * was able to unset it. It can come to this point only if
+ * atleast one of the conditions is true.
+ */
+ return (1);
+}
+
+void
+delete_prog(int prog)
+{
+ RPCB reg;
+ register rpcblist_ptr rbl;
+
+ for (rbl = list_rbl; rbl != NULL; rbl = rbl->rpcb_next) {
+ if ((rbl->rpcb_map.r_prog != prog))
+ continue;
+ if (is_bound(rbl->rpcb_map.r_netid, rbl->rpcb_map.r_addr))
+ continue;
+ reg.r_prog = rbl->rpcb_map.r_prog;
+ reg.r_vers = rbl->rpcb_map.r_vers;
+ reg.r_netid = strdup(rbl->rpcb_map.r_netid);
+ (void) map_unset(&reg, "superuser");
+ free(reg.r_netid);
+ }
+}
+
+void *
+rpcbproc_getaddr_com(RPCB *regp, struct svc_req *rqstp, SVCXPRT *transp,
+ rpcvers_t rpcbversnum, rpcvers_t verstype)
+{
+ static char *uaddr;
+ char *saddr = NULL;
+ rpcblist_ptr fnd;
+
+ if (uaddr != NULL && uaddr != nullstring) {
+ free(uaddr);
+ uaddr = NULL;
+ }
+ fnd = find_service(regp->r_prog, regp->r_vers, transp->xp_netid);
+ if (fnd && ((verstype == RPCB_ALLVERS) ||
+ (regp->r_vers == fnd->rpcb_map.r_vers))) {
+ if (*(regp->r_addr) != '\0') { /* may contain a hint about */
+ saddr = regp->r_addr; /* the interface that we */
+ } /* should use */
+ if (!(uaddr = mergeaddr(transp, transp->xp_netid,
+ fnd->rpcb_map.r_addr, saddr))) {
+ /* Try whatever we have */
+ uaddr = strdup(fnd->rpcb_map.r_addr);
+ } else if (!uaddr[0]) {
+ /*
+ * The server died. Unset all versions of this prog.
+ */
+ delete_prog(regp->r_prog);
+ uaddr = nullstring;
+ }
+ } else {
+ uaddr = nullstring;
+ }
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "getaddr: %s\n", uaddr);
+#endif
+ /* XXX: should have used some defined constant here */
+ rpcbs_getaddr(rpcbversnum - 2, regp->r_prog, regp->r_vers,
+ transp->xp_netid, uaddr);
+ return (void *)&uaddr;
+}
+
+/* ARGSUSED */
+void *
+rpcbproc_gettime_com(void *arg, struct svc_req *rqstp, SVCXPRT *transp,
+ rpcvers_t rpcbversnum)
+{
+ static time_t curtime;
+
+ (void) time(&curtime);
+ return (void *)&curtime;
+}
+
+/*
+ * Convert uaddr to taddr. Should be used only by
+ * local servers/clients. (kernel level stuff only)
+ */
+/* ARGSUSED */
+void *
+rpcbproc_uaddr2taddr_com(void *arg, struct svc_req *rqstp, SVCXPRT *transp,
+ rpcvers_t rpcbversnum)
+{
+ char **uaddrp = (char **)arg;
+ struct netconfig *nconf;
+ static struct netbuf nbuf;
+ static struct netbuf *taddr;
+
+ if (taddr) {
+ free(taddr->buf);
+ free(taddr);
+ taddr = NULL;
+ }
+ if (((nconf = rpcbind_get_conf(transp->xp_netid)) == NULL) ||
+ ((taddr = uaddr2taddr(nconf, *uaddrp)) == NULL)) {
+ (void) memset((char *)&nbuf, 0, sizeof (struct netbuf));
+ return (void *)&nbuf;
+ }
+ return (void *)taddr;
+}
+
+/*
+ * Convert taddr to uaddr. Should be used only by
+ * local servers/clients. (kernel level stuff only)
+ */
+/* ARGSUSED */
+void *
+rpcbproc_taddr2uaddr_com(void *arg, struct svc_req *rqstp, SVCXPRT *transp,
+ rpcvers_t rpcbversnum)
+{
+ struct netbuf *taddr = (struct netbuf *)arg;
+ static char *uaddr;
+ struct netconfig *nconf;
+
+#ifdef CHEW_FDS
+ int fd;
+
+ if ((fd = open("/dev/null", O_RDONLY)) == -1) {
+ uaddr = (char *)strerror(errno);
+ return (&uaddr);
+ }
+#endif /* CHEW_FDS */
+ if (uaddr != NULL && uaddr != nullstring) {
+ free(uaddr);
+ uaddr = NULL;
+ }
+ if (((nconf = rpcbind_get_conf(transp->xp_netid)) == NULL) ||
+ ((uaddr = taddr2uaddr(nconf, taddr)) == NULL)) {
+ uaddr = nullstring;
+ }
+ return (void *)&uaddr;
+}
+
+
+static bool_t
+xdr_encap_parms(XDR *xdrs, struct encap_parms *epp)
+{
+ return (xdr_bytes(xdrs, &(epp->args), (u_int *) &(epp->arglen), ~0));
+}
+
+/*
+ * XDR remote call arguments. It ignores the address part.
+ * written for XDR_DECODE direction only
+ */
+static bool_t
+xdr_rmtcall_args(XDR *xdrs, struct r_rmtcall_args *cap)
+{
+ /* does not get the address or the arguments */
+ if (xdr_u_int32_t(xdrs, &(cap->rmt_prog)) &&
+ xdr_u_int32_t(xdrs, &(cap->rmt_vers)) &&
+ xdr_u_int32_t(xdrs, &(cap->rmt_proc))) {
+ return (xdr_encap_parms(xdrs, &(cap->rmt_args)));
+ }
+ return (FALSE);
+}
+
+/*
+ * XDR remote call results along with the address. Ignore
+ * program number, version number and proc number.
+ * Written for XDR_ENCODE direction only.
+ */
+static bool_t
+xdr_rmtcall_result(XDR *xdrs, struct r_rmtcall_args *cap)
+{
+ bool_t result;
+
+#ifdef PORTMAP
+ if (cap->rmt_localvers == PMAPVERS) {
+ int h1, h2, h3, h4, p1, p2;
+ u_long port;
+
+ /* interpret the universal address for TCP/IP */
+ if (sscanf(cap->rmt_uaddr, "%d.%d.%d.%d.%d.%d",
+ &h1, &h2, &h3, &h4, &p1, &p2) != 6)
+ return (FALSE);
+ port = ((p1 & 0xff) << 8) + (p2 & 0xff);
+ result = xdr_u_long(xdrs, &port);
+ } else
+#endif
+ if ((cap->rmt_localvers == RPCBVERS) ||
+ (cap->rmt_localvers == RPCBVERS4)) {
+ result = xdr_wrapstring(xdrs, &(cap->rmt_uaddr));
+ } else {
+ return (FALSE);
+ }
+ if (result == TRUE)
+ return (xdr_encap_parms(xdrs, &(cap->rmt_args)));
+ return (FALSE);
+}
+
+/*
+ * only worries about the struct encap_parms part of struct r_rmtcall_args.
+ * The arglen must already be set!!
+ */
+static bool_t
+xdr_opaque_parms(XDR *xdrs, struct r_rmtcall_args *cap)
+{
+ return (xdr_opaque(xdrs, cap->rmt_args.args, cap->rmt_args.arglen));
+}
+
+static struct rmtcallfd_list *rmthead;
+static struct rmtcallfd_list *rmttail;
+
+int
+create_rmtcall_fd(struct netconfig *nconf)
+{
+ int fd;
+ struct rmtcallfd_list *rmt;
+ SVCXPRT *xprt;
+
+ if ((fd = __rpc_nconf2fd(nconf)) == -1) {
+ if (debugging)
+ fprintf(stderr,
+ "create_rmtcall_fd: couldn't open \"%s\" (errno %d)\n",
+ nconf->nc_device, errno);
+ return (-1);
+ }
+ xprt = svc_tli_create(fd, 0, (struct t_bind *) 0, 0, 0);
+ if (xprt == NULL) {
+ if (debugging)
+ fprintf(stderr,
+ "create_rmtcall_fd: svc_tli_create failed\n");
+ return (-1);
+ }
+ rmt = malloc(sizeof (struct rmtcallfd_list));
+ if (rmt == NULL) {
+ syslog(LOG_ERR, "create_rmtcall_fd: no memory!");
+ return (-1);
+ }
+ rmt->xprt = xprt;
+ rmt->netid = strdup(nconf->nc_netid);
+ xprt->xp_netid = rmt->netid;
+ rmt->fd = fd;
+ rmt->next = NULL;
+ if (rmthead == NULL) {
+ rmthead = rmt;
+ rmttail = rmt;
+ } else {
+ rmttail->next = rmt;
+ rmttail = rmt;
+ }
+ /* XXX not threadsafe */
+ if (fd > svc_maxfd)
+ svc_maxfd = fd;
+ FD_SET(fd, &svc_fdset);
+ return (fd);
+}
+
+static int
+find_rmtcallfd_by_netid(char *netid)
+{
+ struct rmtcallfd_list *rmt;
+
+ for (rmt = rmthead; rmt != NULL; rmt = rmt->next) {
+ if (strcmp(netid, rmt->netid) == 0) {
+ return (rmt->fd);
+ }
+ }
+ return (-1);
+}
+
+static SVCXPRT *
+find_rmtcallxprt_by_fd(int fd)
+{
+ struct rmtcallfd_list *rmt;
+
+ for (rmt = rmthead; rmt != NULL; rmt = rmt->next) {
+ if (fd == rmt->fd) {
+ return (rmt->xprt);
+ }
+ }
+ return (NULL);
+}
+
+
+/*
+ * Call a remote procedure service. This procedure is very quiet when things
+ * go wrong. The proc is written to support broadcast rpc. In the broadcast
+ * case, a machine should shut-up instead of complain, lest the requestor be
+ * overrun with complaints at the expense of not hearing a valid reply.
+ * When receiving a request and verifying that the service exists, we
+ *
+ * receive the request
+ *
+ * open a new TLI endpoint on the same transport on which we received
+ * the original request
+ *
+ * remember the original request's XID (which requires knowing the format
+ * of the svc_dg_data structure)
+ *
+ * forward the request, with a new XID, to the requested service,
+ * remembering the XID used to send this request (for later use in
+ * reassociating the answer with the original request), the requestor's
+ * address, the file descriptor on which the forwarded request is
+ * made and the service's address.
+ *
+ * mark the file descriptor on which we anticipate receiving a reply from
+ * the service and one to select for in our private svc_run procedure
+ *
+ * At some time in the future, a reply will be received from the service to
+ * which we forwarded the request. At that time, we detect that the socket
+ * used was for forwarding (by looking through the finfo structures to see
+ * whether the fd corresponds to one of those) and call handle_reply() to
+ *
+ * receive the reply
+ *
+ * bundle the reply, along with the service's universal address
+ *
+ * create a SVCXPRT structure and use a version of svc_sendreply
+ * that allows us to specify the reply XID and destination, send the reply
+ * to the original requestor.
+ */
+
+void
+rpcbproc_callit_com(struct svc_req *rqstp, SVCXPRT *transp,
+ rpcproc_t reply_type, rpcvers_t versnum)
+{
+ register rpcblist_ptr rbl;
+ struct netconfig *nconf;
+ struct netbuf *caller;
+ struct r_rmtcall_args a;
+ char *buf_alloc = NULL, *outbufp;
+ char *outbuf_alloc = NULL;
+ char buf[RPC_BUF_MAX], outbuf[RPC_BUF_MAX];
+ struct netbuf *na = (struct netbuf *) NULL;
+ struct rpc_msg call_msg;
+ int outlen;
+ u_int sendsz;
+ XDR outxdr;
+ AUTH *auth;
+ int fd = -1;
+ char *uaddr, *m_uaddr = NULL, *local_uaddr = NULL;
+ u_int32_t *xidp;
+ struct __rpc_sockinfo si;
+ struct sockaddr *localsa;
+ struct netbuf tbuf;
+
+ if (!__rpc_fd2sockinfo(transp->xp_fd, &si)) {
+ if (reply_type == RPCBPROC_INDIRECT)
+ svcerr_systemerr(transp);
+ return;
+ }
+ if (si.si_socktype != SOCK_DGRAM)
+ return; /* Only datagram type accepted */
+ sendsz = __rpc_get_t_size(si.si_af, si.si_proto, UDPMSGSIZE);
+ if (sendsz == 0) { /* data transfer not supported */
+ if (reply_type == RPCBPROC_INDIRECT)
+ svcerr_systemerr(transp);
+ return;
+ }
+ /*
+ * Should be multiple of 4 for XDR.
+ */
+ sendsz = ((sendsz + 3) / 4) * 4;
+ if (sendsz > RPC_BUF_MAX) {
+#ifdef notyet
+ buf_alloc = alloca(sendsz); /* not in IDR2? */
+#else
+ buf_alloc = malloc(sendsz);
+#endif /* notyet */
+ if (buf_alloc == NULL) {
+ if (debugging)
+ fprintf(stderr,
+ "rpcbproc_callit_com: No Memory!\n");
+ if (reply_type == RPCBPROC_INDIRECT)
+ svcerr_systemerr(transp);
+ return;
+ }
+ a.rmt_args.args = buf_alloc;
+ } else {
+ a.rmt_args.args = buf;
+ }
+
+ call_msg.rm_xid = 0; /* For error checking purposes */
+ if (!svc_getargs(transp, (xdrproc_t) xdr_rmtcall_args, (char *) &a)) {
+ if (reply_type == RPCBPROC_INDIRECT)
+ svcerr_decode(transp);
+ if (debugging)
+ fprintf(stderr,
+ "rpcbproc_callit_com: svc_getargs failed\n");
+ goto error;
+ }
+
+ if (!check_callit(transp, &a, versnum)) {
+ svcerr_weakauth(transp);
+ goto error;
+ }
+
+ caller = svc_getrpccaller(transp);
+#ifdef RPCBIND_DEBUG
+ if (debugging) {
+ uaddr = taddr2uaddr(rpcbind_get_conf(transp->xp_netid), caller);
+ fprintf(stderr, "%s %s req for (%lu, %lu, %lu, %s) from %s : ",
+ versnum == PMAPVERS ? "pmap_rmtcall" :
+ versnum == RPCBVERS ? "rpcb_rmtcall" :
+ versnum == RPCBVERS4 ? "rpcb_indirect" : "unknown",
+ reply_type == RPCBPROC_INDIRECT ? "indirect" : "callit",
+ (unsigned long)a.rmt_prog, (unsigned long)a.rmt_vers,
+ (unsigned long)a.rmt_proc, transp->xp_netid,
+ uaddr ? uaddr : "unknown");
+ if (uaddr)
+ free(uaddr);
+ }
+#endif
+
+ rbl = find_service(a.rmt_prog, a.rmt_vers, transp->xp_netid);
+
+ rpcbs_rmtcall(versnum - 2, reply_type, a.rmt_prog, a.rmt_vers,
+ a.rmt_proc, transp->xp_netid, rbl);
+
+ if (rbl == (rpcblist_ptr)NULL) {
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "not found\n");
+#endif
+ if (reply_type == RPCBPROC_INDIRECT)
+ svcerr_noprog(transp);
+ goto error;
+ }
+ if (rbl->rpcb_map.r_vers != a.rmt_vers) {
+ if (reply_type == RPCBPROC_INDIRECT) {
+ rpcvers_t vers_low, vers_high;
+
+ find_versions(a.rmt_prog, transp->xp_netid,
+ &vers_low, &vers_high);
+ svcerr_progvers(transp, vers_low, vers_high);
+ }
+ goto error;
+ }
+
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "found at uaddr %s\n", rbl->rpcb_map.r_addr);
+#endif
+ /*
+ * Check whether this entry is valid and a server is present
+ * Mergeaddr() returns NULL if no such entry is present, and
+ * returns "" if the entry was present but the server is not
+ * present (i.e., it crashed).
+ */
+ if (reply_type == RPCBPROC_INDIRECT) {
+ uaddr = mergeaddr(transp, transp->xp_netid,
+ rbl->rpcb_map.r_addr, NULL);
+ if (uaddr == NULL || uaddr[0] == '\0') {
+ svcerr_noprog(transp);
+ if (uaddr != NULL)
+ free(uaddr);
+ goto error;
+ }
+ free(uaddr);
+ }
+ nconf = rpcbind_get_conf(transp->xp_netid);
+ if (nconf == (struct netconfig *)NULL) {
+ if (reply_type == RPCBPROC_INDIRECT)
+ svcerr_systemerr(transp);
+ if (debugging)
+ fprintf(stderr,
+ "rpcbproc_callit_com: rpcbind_get_conf failed\n");
+ goto error;
+ }
+ localsa = local_sa(((struct sockaddr *)caller->buf)->sa_family);
+ if (localsa == NULL) {
+ if (debugging)
+ fprintf(stderr,
+ "rpcbproc_callit_com: no local address\n");
+ goto error;
+ }
+ tbuf.len = tbuf.maxlen = localsa->sa_len;
+ tbuf.buf = localsa;
+ local_uaddr =
+ addrmerge(&tbuf, rbl->rpcb_map.r_addr, NULL, nconf->nc_netid);
+ m_uaddr = addrmerge(caller, rbl->rpcb_map.r_addr, NULL,
+ nconf->nc_netid);
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "merged uaddr %s\n", m_uaddr);
+#endif
+ if ((fd = find_rmtcallfd_by_netid(nconf->nc_netid)) == -1) {
+ if (reply_type == RPCBPROC_INDIRECT)
+ svcerr_systemerr(transp);
+ goto error;
+ }
+ xidp = __rpcb_get_dg_xidp(transp);
+ switch (forward_register(*xidp, caller, fd, m_uaddr, reply_type,
+ versnum, &call_msg.rm_xid)) {
+ case 1:
+ /* Success; forward_register() will free m_uaddr for us. */
+ m_uaddr = NULL;
+ break;
+ case 0:
+ /*
+ * A duplicate request for the slow server. Let's not
+ * beat on it any more.
+ */
+ if (debugging)
+ fprintf(stderr,
+ "rpcbproc_callit_com: duplicate request\n");
+ goto error;
+ case -1:
+ /* forward_register failed. Perhaps no memory. */
+ if (debugging)
+ fprintf(stderr,
+ "rpcbproc_callit_com: forward_register failed\n");
+ goto error;
+ }
+
+#ifdef DEBUG_RMTCALL
+ if (debugging)
+ fprintf(stderr,
+ "rpcbproc_callit_com: original XID %x, new XID %x\n",
+ *xidp, call_msg.rm_xid);
+#endif
+ call_msg.rm_direction = CALL;
+ call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
+ call_msg.rm_call.cb_prog = a.rmt_prog;
+ call_msg.rm_call.cb_vers = a.rmt_vers;
+ if (sendsz > RPC_BUF_MAX) {
+#ifdef notyet
+ outbuf_alloc = alloca(sendsz); /* not in IDR2? */
+#else
+ outbuf_alloc = malloc(sendsz);
+#endif /* notyet */
+ if (outbuf_alloc == NULL) {
+ if (reply_type == RPCBPROC_INDIRECT)
+ svcerr_systemerr(transp);
+ if (debugging)
+ fprintf(stderr,
+ "rpcbproc_callit_com: No memory!\n");
+ goto error;
+ }
+ xdrmem_create(&outxdr, outbuf_alloc, sendsz, XDR_ENCODE);
+ } else {
+ xdrmem_create(&outxdr, outbuf, sendsz, XDR_ENCODE);
+ }
+ if (!xdr_callhdr(&outxdr, &call_msg)) {
+ if (reply_type == RPCBPROC_INDIRECT)
+ svcerr_systemerr(transp);
+ if (debugging)
+ fprintf(stderr,
+ "rpcbproc_callit_com: xdr_callhdr failed\n");
+ goto error;
+ }
+ if (!xdr_u_int32_t(&outxdr, &(a.rmt_proc))) {
+ if (reply_type == RPCBPROC_INDIRECT)
+ svcerr_systemerr(transp);
+ if (debugging)
+ fprintf(stderr,
+ "rpcbproc_callit_com: xdr_u_long failed\n");
+ goto error;
+ }
+
+ if (rqstp->rq_cred.oa_flavor == AUTH_NULL) {
+ auth = authnone_create();
+ } else if (rqstp->rq_cred.oa_flavor == AUTH_SYS) {
+ struct authunix_parms *au;
+
+ au = (struct authunix_parms *)rqstp->rq_clntcred;
+ auth = authunix_create(au->aup_machname,
+ au->aup_uid, au->aup_gid,
+ au->aup_len, au->aup_gids);
+ if (auth == NULL) /* fall back */
+ auth = authnone_create();
+ } else {
+ /* we do not support any other authentication scheme */
+ if (debugging)
+ fprintf(stderr,
+"rpcbproc_callit_com: oa_flavor != AUTH_NONE and oa_flavor != AUTH_SYS\n");
+ if (reply_type == RPCBPROC_INDIRECT)
+ svcerr_weakauth(transp); /* XXX too strong.. */
+ goto error;
+ }
+ if (auth == NULL) {
+ if (reply_type == RPCBPROC_INDIRECT)
+ svcerr_systemerr(transp);
+ if (debugging)
+ fprintf(stderr,
+ "rpcbproc_callit_com: authwhatever_create returned NULL\n");
+ goto error;
+ }
+ if (!AUTH_MARSHALL(auth, &outxdr)) {
+ if (reply_type == RPCBPROC_INDIRECT)
+ svcerr_systemerr(transp);
+ AUTH_DESTROY(auth);
+ if (debugging)
+ fprintf(stderr,
+ "rpcbproc_callit_com: AUTH_MARSHALL failed\n");
+ goto error;
+ }
+ AUTH_DESTROY(auth);
+ if (!xdr_opaque_parms(&outxdr, &a)) {
+ if (reply_type == RPCBPROC_INDIRECT)
+ svcerr_systemerr(transp);
+ if (debugging)
+ fprintf(stderr,
+ "rpcbproc_callit_com: xdr_opaque_parms failed\n");
+ goto error;
+ }
+ outlen = (int) XDR_GETPOS(&outxdr);
+ if (outbuf_alloc)
+ outbufp = outbuf_alloc;
+ else
+ outbufp = outbuf;
+
+ na = uaddr2taddr(nconf, local_uaddr);
+ if (!na) {
+ if (reply_type == RPCBPROC_INDIRECT)
+ svcerr_systemerr(transp);
+ goto error;
+ }
+
+ if (sendto(fd, outbufp, outlen, 0, (struct sockaddr *)na->buf, na->len)
+ != outlen) {
+ if (debugging)
+ fprintf(stderr,
+ "rpcbproc_callit_com: sendto failed: errno %d\n", errno);
+ if (reply_type == RPCBPROC_INDIRECT)
+ svcerr_systemerr(transp);
+ goto error;
+ }
+ goto out;
+
+error:
+ if (call_msg.rm_xid != 0)
+ (void) free_slot_by_xid(call_msg.rm_xid);
+out:
+ if (local_uaddr)
+ free(local_uaddr);
+ if (buf_alloc)
+ free(buf_alloc);
+ if (outbuf_alloc)
+ free(outbuf_alloc);
+ if (na) {
+ free(na->buf);
+ free(na);
+ }
+ if (m_uaddr != NULL)
+ free(m_uaddr);
+}
+
+/*
+ * Makes an entry into the FIFO for the given request.
+ * Returns 1 on success, 0 if this is a duplicate request, or -1 on error.
+ * *callxidp is set to the xid of the call.
+ */
+static int
+forward_register(u_int32_t caller_xid, struct netbuf *caller_addr,
+ int forward_fd, char *uaddr, rpcproc_t reply_type,
+ rpcvers_t versnum, u_int32_t *callxidp)
+{
+ int i;
+ int j = 0;
+ time_t min_time, time_now;
+ static u_int32_t lastxid;
+ int entry = -1;
+
+ min_time = FINFO[0].time;
+ time_now = time((time_t *)0);
+ /* initialization */
+ if (lastxid == 0)
+ lastxid = time_now * NFORWARD;
+
+ /*
+ * Check if it is an duplicate entry. Then,
+ * try to find an empty slot. If not available, then
+ * use the slot with the earliest time.
+ */
+ for (i = 0; i < NFORWARD; i++) {
+ if (FINFO[i].flag & FINFO_ACTIVE) {
+ if ((FINFO[i].caller_xid == caller_xid) &&
+ (FINFO[i].reply_type == reply_type) &&
+ (FINFO[i].versnum == versnum) &&
+ (!netbufcmp(FINFO[i].caller_addr,
+ caller_addr))) {
+ FINFO[i].time = time((time_t *)0);
+ return (0); /* Duplicate entry */
+ } else {
+ /* Should we wait any longer */
+ if ((time_now - FINFO[i].time) > MAXTIME_OFF)
+ (void) free_slot_by_index(i);
+ }
+ }
+ if (entry == -1) {
+ if ((FINFO[i].flag & FINFO_ACTIVE) == 0) {
+ entry = i;
+ } else if (FINFO[i].time < min_time) {
+ j = i;
+ min_time = FINFO[i].time;
+ }
+ }
+ }
+ if (entry != -1) {
+ /* use this empty slot */
+ j = entry;
+ } else {
+ (void) free_slot_by_index(j);
+ }
+ if ((FINFO[j].caller_addr = netbufdup(caller_addr)) == NULL) {
+ return (-1);
+ }
+ rpcb_rmtcalls++; /* no of pending calls */
+ FINFO[j].flag = FINFO_ACTIVE;
+ FINFO[j].reply_type = reply_type;
+ FINFO[j].versnum = versnum;
+ FINFO[j].time = time_now;
+ FINFO[j].caller_xid = caller_xid;
+ FINFO[j].forward_fd = forward_fd;
+ /*
+ * Though uaddr is not allocated here, it will still be freed
+ * from free_slot_*().
+ */
+ FINFO[j].uaddr = uaddr;
+ lastxid = lastxid + NFORWARD;
+ /* Don't allow a zero xid below. */
+ if ((u_int32_t)(lastxid + NFORWARD) <= NFORWARD)
+ lastxid = NFORWARD;
+ FINFO[j].forward_xid = lastxid + j; /* encode slot */
+ *callxidp = FINFO[j].forward_xid; /* forward on this xid */
+ return (1);
+}
+
+static struct finfo *
+forward_find(u_int32_t reply_xid)
+{
+ int i;
+
+ i = reply_xid % (u_int32_t)NFORWARD;
+ if ((FINFO[i].flag & FINFO_ACTIVE) &&
+ (FINFO[i].forward_xid == reply_xid)) {
+ return (&FINFO[i]);
+ }
+ return (NULL);
+}
+
+static int
+free_slot_by_xid(u_int32_t xid)
+{
+ int entry;
+
+ entry = xid % (u_int32_t)NFORWARD;
+ return (free_slot_by_index(entry));
+}
+
+static int
+free_slot_by_index(int index)
+{
+ struct finfo *fi;
+
+ fi = &FINFO[index];
+ if (fi->flag & FINFO_ACTIVE) {
+ netbuffree(fi->caller_addr);
+ /* XXX may be too big, but can't access xprt array here */
+ if (fi->forward_fd >= svc_maxfd)
+ svc_maxfd--;
+ free(fi->uaddr);
+ fi->flag &= ~FINFO_ACTIVE;
+ rpcb_rmtcalls--;
+ return (1);
+ }
+ return (0);
+}
+
+static int
+netbufcmp(struct netbuf *n1, struct netbuf *n2)
+{
+ return ((n1->len != n2->len) || memcmp(n1->buf, n2->buf, n1->len));
+}
+
+static struct netbuf *
+netbufdup(struct netbuf *ap)
+{
+ struct netbuf *np;
+
+ if ((np = malloc(sizeof(struct netbuf))) == NULL)
+ return (NULL);
+ if ((np->buf = malloc(ap->len)) == NULL) {
+ free(np);
+ return (NULL);
+ }
+ np->maxlen = np->len = ap->len;
+ memcpy(np->buf, ap->buf, ap->len);
+ return (np);
+}
+
+static void
+netbuffree(struct netbuf *ap)
+{
+ free(ap->buf);
+ free(ap);
+}
+
+
+#define MASKVAL (POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND)
+
+void
+my_svc_run()
+{
+ size_t nfds;
+ struct pollfd pollfds[FD_SETSIZE];
+ int poll_ret, check_ret;
+ int n;
+#ifdef SVC_RUN_DEBUG
+ int i;
+#endif
+ register struct pollfd *p;
+
+ for (;;) {
+ p = pollfds;
+ for (n = 0; n <= svc_maxfd; n++) {
+ if (FD_ISSET(n, &svc_fdset)) {
+ p->fd = n;
+ p->events = MASKVAL;
+ p++;
+ }
+ }
+ nfds = p - pollfds;
+ poll_ret = 0;
+#ifdef SVC_RUN_DEBUG
+ if (debugging) {
+ fprintf(stderr, "polling for read on fd < ");
+ for (i = 0, p = pollfds; i < nfds; i++, p++)
+ if (p->events)
+ fprintf(stderr, "%d ", p->fd);
+ fprintf(stderr, ">\n");
+ }
+#endif
+ switch (poll_ret = poll(pollfds, nfds, INFTIM)) {
+ case -1:
+ /*
+ * We ignore all errors, continuing with the assumption
+ * that it was set by the signal handlers (or any
+ * other outside event) and not caused by poll().
+ */
+ case 0:
+ continue;
+ default:
+#ifdef SVC_RUN_DEBUG
+ if (debugging) {
+ fprintf(stderr, "poll returned read fds < ");
+ for (i = 0, p = pollfds; i < nfds; i++, p++)
+ if (p->revents)
+ fprintf(stderr, "%d ", p->fd);
+ fprintf(stderr, ">\n");
+ }
+#endif
+ /*
+ * If we found as many replies on callback fds
+ * as the number of descriptors selectable which
+ * poll() returned, there can be no more so we
+ * don't call svc_getreq_poll. Otherwise, there
+ * must be another so we must call svc_getreq_poll.
+ */
+ if ((check_ret = check_rmtcalls(pollfds, nfds)) ==
+ poll_ret)
+ continue;
+ svc_getreq_poll(pollfds, poll_ret-check_ret);
+ }
+#ifdef SVC_RUN_DEBUG
+ if (debugging) {
+ fprintf(stderr, "svc_maxfd now %u\n", svc_maxfd);
+ }
+#endif
+ }
+}
+
+static int
+check_rmtcalls(struct pollfd *pfds, int nfds)
+{
+ int j, ncallbacks_found = 0, rmtcalls_pending;
+ SVCXPRT *xprt;
+
+ if (rpcb_rmtcalls == 0)
+ return (0);
+
+ rmtcalls_pending = rpcb_rmtcalls;
+ for (j = 0; j < nfds; j++) {
+ if ((xprt = find_rmtcallxprt_by_fd(pfds[j].fd)) != NULL) {
+ if (pfds[j].revents) {
+ ncallbacks_found++;
+#ifdef DEBUG_RMTCALL
+ if (debugging)
+ fprintf(stderr,
+"my_svc_run: polled on forwarding fd %d, netid %s - calling handle_reply\n",
+ pfds[j].fd, xprt->xp_netid);
+#endif
+ handle_reply(pfds[j].fd, xprt);
+ pfds[j].revents = 0;
+ if (ncallbacks_found >= rmtcalls_pending) {
+ break;
+ }
+ }
+ }
+ }
+ return (ncallbacks_found);
+}
+
+static void
+xprt_set_caller(SVCXPRT *xprt, struct finfo *fi)
+{
+ u_int32_t *xidp;
+
+ *(svc_getrpccaller(xprt)) = *(fi->caller_addr);
+ xidp = __rpcb_get_dg_xidp(xprt);
+ *xidp = fi->caller_xid;
+}
+
+/*
+ * Call svcerr_systemerr() only if RPCBVERS4
+ */
+static void
+send_svcsyserr(SVCXPRT *xprt, struct finfo *fi)
+{
+ if (fi->reply_type == RPCBPROC_INDIRECT) {
+ xprt_set_caller(xprt, fi);
+ svcerr_systemerr(xprt);
+ }
+ return;
+}
+
+static void
+handle_reply(int fd, SVCXPRT *xprt)
+{
+ XDR reply_xdrs;
+ struct rpc_msg reply_msg;
+ struct rpc_err reply_error;
+ char *buffer;
+ struct finfo *fi;
+ int inlen, pos, len;
+ struct r_rmtcall_args a;
+ struct sockaddr_storage ss;
+ socklen_t fromlen;
+#ifdef SVC_RUN_DEBUG
+ char *uaddr;
+#endif
+
+ buffer = malloc(RPC_BUF_MAX);
+ if (buffer == NULL)
+ goto done;
+
+ do {
+ inlen = recvfrom(fd, buffer, RPC_BUF_MAX, 0,
+ (struct sockaddr *)&ss, &fromlen);
+ } while (inlen < 0 && errno == EINTR);
+ if (inlen < 0) {
+ if (debugging)
+ fprintf(stderr,
+ "handle_reply: recvfrom returned %d, errno %d\n", inlen, errno);
+ goto done;
+ }
+
+ reply_msg.acpted_rply.ar_verf = _null_auth;
+ reply_msg.acpted_rply.ar_results.where = 0;
+ reply_msg.acpted_rply.ar_results.proc = (xdrproc_t) xdr_void;
+
+ xdrmem_create(&reply_xdrs, buffer, (u_int)inlen, XDR_DECODE);
+ if (!xdr_replymsg(&reply_xdrs, &reply_msg)) {
+ if (debugging)
+ (void) fprintf(stderr,
+ "handle_reply: xdr_replymsg failed\n");
+ goto done;
+ }
+ fi = forward_find(reply_msg.rm_xid);
+#ifdef SVC_RUN_DEBUG
+ if (debugging) {
+ fprintf(stderr, "handle_reply: reply xid: %d fi addr: %p\n",
+ reply_msg.rm_xid, fi);
+ }
+#endif
+ if (fi == NULL) {
+ goto done;
+ }
+ _seterr_reply(&reply_msg, &reply_error);
+ if (reply_error.re_status != RPC_SUCCESS) {
+ if (debugging)
+ (void) fprintf(stderr, "handle_reply: %s\n",
+ clnt_sperrno(reply_error.re_status));
+ send_svcsyserr(xprt, fi);
+ goto done;
+ }
+ pos = XDR_GETPOS(&reply_xdrs);
+ len = inlen - pos;
+ a.rmt_args.args = &buffer[pos];
+ a.rmt_args.arglen = len;
+ a.rmt_uaddr = fi->uaddr;
+ a.rmt_localvers = fi->versnum;
+
+ xprt_set_caller(xprt, fi);
+#ifdef SVC_RUN_DEBUG
+ uaddr = taddr2uaddr(rpcbind_get_conf("udp"),
+ svc_getrpccaller(xprt));
+ if (debugging) {
+ fprintf(stderr, "handle_reply: forwarding address %s to %s\n",
+ a.rmt_uaddr, uaddr ? uaddr : "unknown");
+ }
+ if (uaddr)
+ free(uaddr);
+#endif
+ svc_sendreply(xprt, (xdrproc_t) xdr_rmtcall_result, (char *) &a);
+done:
+ if (buffer)
+ free(buffer);
+
+ if (reply_msg.rm_xid == 0) {
+#ifdef SVC_RUN_DEBUG
+ if (debugging) {
+ fprintf(stderr, "handle_reply: NULL xid on exit!\n");
+ }
+#endif
+ } else
+ (void) free_slot_by_xid(reply_msg.rm_xid);
+ return;
+}
+
+static void
+find_versions(rpcprog_t prog, char *netid, rpcvers_t *lowvp, rpcvers_t *highvp)
+{
+ register rpcblist_ptr rbl;
+ int lowv = 0;
+ int highv = 0;
+
+ for (rbl = list_rbl; rbl != NULL; rbl = rbl->rpcb_next) {
+ if ((rbl->rpcb_map.r_prog != prog) ||
+ ((rbl->rpcb_map.r_netid != NULL) &&
+ (strcasecmp(rbl->rpcb_map.r_netid, netid) != 0)))
+ continue;
+ if (lowv == 0) {
+ highv = rbl->rpcb_map.r_vers;
+ lowv = highv;
+ } else if (rbl->rpcb_map.r_vers < lowv) {
+ lowv = rbl->rpcb_map.r_vers;
+ } else if (rbl->rpcb_map.r_vers > highv) {
+ highv = rbl->rpcb_map.r_vers;
+ }
+ }
+ *lowvp = lowv;
+ *highvp = highv;
+ return;
+}
+
+/*
+ * returns the item with the given program, version number and netid.
+ * If that version number is not found, it returns the item with that
+ * program number, so that address is now returned to the caller. The
+ * caller when makes a call to this program, version number, the call
+ * will fail and it will return with PROGVERS_MISMATCH. The user can
+ * then determine the highest and the lowest version number for this
+ * program using clnt_geterr() and use those program version numbers.
+ *
+ * Returns the RPCBLIST for the given prog, vers and netid
+ */
+static rpcblist_ptr
+find_service(rpcprog_t prog, rpcvers_t vers, char *netid)
+{
+ register rpcblist_ptr hit = NULL;
+ register rpcblist_ptr rbl;
+
+ for (rbl = list_rbl; rbl != NULL; rbl = rbl->rpcb_next) {
+ if ((rbl->rpcb_map.r_prog != prog) ||
+ ((rbl->rpcb_map.r_netid != NULL) &&
+ (strcasecmp(rbl->rpcb_map.r_netid, netid) != 0)))
+ continue;
+ hit = rbl;
+ if (rbl->rpcb_map.r_vers == vers)
+ break;
+ }
+ return (hit);
+}
+
+/*
+ * Copies the name associated with the uid of the caller and returns
+ * a pointer to it. Similar to getwd().
+ */
+static char *
+getowner(SVCXPRT *transp, char *owner, size_t ownersize)
+{
+ uid_t uid;
+
+ if (__rpc_get_local_uid(transp, &uid) < 0)
+ strlcpy(owner, "unknown", ownersize);
+ else if (uid == 0)
+ strlcpy(owner, "superuser", ownersize);
+ else
+ snprintf(owner, ownersize, "%d", uid);
+
+ return owner;
+}
+
+#ifdef PORTMAP
+/*
+ * Add this to the pmap list only if it is UDP or TCP.
+ */
+static int
+add_pmaplist(RPCB *arg)
+{
+ struct pmap pmap;
+ struct pmaplist *pml;
+ int h1, h2, h3, h4, p1, p2;
+
+ if (strcmp(arg->r_netid, udptrans) == 0) {
+ /* It is UDP! */
+ pmap.pm_prot = IPPROTO_UDP;
+ } else if (strcmp(arg->r_netid, tcptrans) == 0) {
+ /* It is TCP */
+ pmap.pm_prot = IPPROTO_TCP;
+ } else
+ /* Not a IP protocol */
+ return (0);
+
+ /* interpret the universal address for TCP/IP */
+ if (sscanf(arg->r_addr, "%d.%d.%d.%d.%d.%d",
+ &h1, &h2, &h3, &h4, &p1, &p2) != 6)
+ return (0);
+ pmap.pm_port = ((p1 & 0xff) << 8) + (p2 & 0xff);
+ pmap.pm_prog = arg->r_prog;
+ pmap.pm_vers = arg->r_vers;
+ /*
+ * add to END of list
+ */
+ pml = malloc(sizeof (struct pmaplist));
+ if (pml == NULL) {
+ (void) syslog(LOG_ERR, "rpcbind: no memory!\n");
+ return (1);
+ }
+ pml->pml_map = pmap;
+ pml->pml_next = NULL;
+ if (list_pml == NULL) {
+ list_pml = pml;
+ } else {
+ struct pmaplist *fnd;
+
+ /* Attach to the end of the list */
+ for (fnd = list_pml; fnd->pml_next; fnd = fnd->pml_next)
+ ;
+ fnd->pml_next = pml;
+ }
+ return (0);
+}
+
+/*
+ * Delete this from the pmap list only if it is UDP or TCP.
+ */
+static int
+del_pmaplist(RPCB *arg)
+{
+ struct pmaplist *pml;
+ struct pmaplist *prevpml, *fnd;
+ long prot;
+
+ if (strcmp(arg->r_netid, udptrans) == 0) {
+ /* It is UDP! */
+ prot = IPPROTO_UDP;
+ } else if (strcmp(arg->r_netid, tcptrans) == 0) {
+ /* It is TCP */
+ prot = IPPROTO_TCP;
+ } else if (arg->r_netid[0] == NULL) {
+ prot = 0; /* Remove all occurrences */
+ } else {
+ /* Not a IP protocol */
+ return (0);
+ }
+ for (prevpml = NULL, pml = list_pml; pml; /* cstyle */) {
+ if ((pml->pml_map.pm_prog != arg->r_prog) ||
+ (pml->pml_map.pm_vers != arg->r_vers) ||
+ (prot && (pml->pml_map.pm_prot != prot))) {
+ /* both pml & prevpml move forwards */
+ prevpml = pml;
+ pml = pml->pml_next;
+ continue;
+ }
+ /* found it; pml moves forward, prevpml stays */
+ fnd = pml;
+ pml = pml->pml_next;
+ if (prevpml == NULL)
+ list_pml = pml;
+ else
+ prevpml->pml_next = pml;
+ free(fnd);
+ }
+ return (0);
+}
+#endif /* PORTMAP */
diff --git a/usr.sbin/rpcbind/rpcbind.8 b/usr.sbin/rpcbind/rpcbind.8
new file mode 100644
index 0000000..1ea7096
--- /dev/null
+++ b/usr.sbin/rpcbind/rpcbind.8
@@ -0,0 +1,112 @@
+.\" @(#)rpcbind.1m 1.19 92/09/14 SMI; from SVr4
+.\" Copyright 1989 AT&T
+.\" Copyright 1991 Sun Microsystems, Inc.
+.\" $FreeBSD$
+.Dd September 14, 1992
+.Dt RPCBIND 8
+.Os
+.Sh NAME
+.Nm rpcbind
+.Nd universal addresses to RPC program number mapper
+.Sh SYNOPSIS
+.Nm
+.Op Fl dilLs
+.Sh DESCRIPTION
+.Nm
+is a server that converts
+.Tn RPC
+program numbers into
+universal addresses.
+It must be running on the host to be able to make
+.Tn RPC
+calls
+on a server on that machine.
+.Pp
+When an
+.Tn RPC
+service is started,
+it tells
+.Nm
+the address at which it is listening,
+and the
+.Tn RPC
+program numbers it is prepared to serve.
+When a client wishes to make an
+.Tn RPC
+call to a given program number,
+it first contacts
+.Nm
+on the server machine to determine
+the address where
+.Tn RPC
+requests should be sent.
+.Pp
+.Nm
+should be started before any other RPC service.
+Normally, standard
+.Tn RPC
+servers are started by port monitors, so
+.Nm
+must be started before port monitors are invoked.
+.Pp
+When
+.Nm
+is started, it checks that certain name-to-address
+translation-calls function correctly.
+If they fail, the network configuration databases may be corrupt.
+Since
+.Tn RPC
+services cannot function correctly in this situation,
+.Nm
+reports the condition and terminates.
+.Pp
+.Nm
+can only be started by the super-user.
+.Sh OPTIONS
+.Bl -tag -width indent
+.It Fl d
+Run in debug mode.
+In this mode,
+.Nm
+will not fork when it starts, will print additional information
+during operation, and will abort on certain errors.
+With this option, the name-to-address translation consistency
+checks are shown in detail.
+.It Fl i
+.Dq Insecure
+mode.
+Allows calls to SET and UNSET from any host.
+Normally
+.Nm
+accepts these requests only from the loopback interface for security reasons.
+This change is necessary for programs that were compiled with earlier
+versions of the rpc library and do not make those requests using the
+loopback interface.
+.It Fl l
+Turns on libwrap connection logging.
+.It Fl s
+Causes
+.Nm
+to change to the user daemon as soon as possible.
+This causes
+.Nm
+to use non-privileged ports for outgoing connections, preventing non-privileged
+clients from using
+.Nm
+to connect to services from a privileged port.
+.It Fl L
+Allow old-style local connections over the loopback interface.
+Without this flag, local connections are only allowed over a local socket,
+.Pa /var/run/rpcbind.sock .
+.El
+.Sh NOTES
+All RPC servers must be restarted if
+.Nm
+is restarted.
+.Sh SEE ALSO
+.Xr rpcbind 3 ,
+.Xr rpcinfo 8
+.Sh FILES
+.Bl -tag -width /var/run/rpcbind.sock -compact
+.It Pa /var/run/rpcbind.sock
+.El
diff --git a/usr.sbin/rpcbind/rpcbind.c b/usr.sbin/rpcbind/rpcbind.c
new file mode 100644
index 0000000..8e93160
--- /dev/null
+++ b/usr.sbin/rpcbind/rpcbind.c
@@ -0,0 +1,568 @@
+/* $NetBSD: rpcbind.c,v 1.1 2000/06/02 23:15:42 fvdl Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+/*
+ * Copyright (c) 1984 - 1991 by Sun Microsystems, Inc.
+ */
+
+/* #ident "@(#)rpcbind.c 1.19 94/04/25 SMI" */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)rpcbind.c 1.35 89/04/21 Copyr 1984 Sun Micro";
+#endif
+#endif
+
+/*
+ * rpcbind.c
+ * Implements the program, version to address mapping for rpc.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <sys/signal.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <rpc/rpc.h>
+#ifdef PORTMAP
+#include <netinet/in.h>
+#endif
+#include <netdb.h>
+#include <stdio.h>
+#include <netconfig.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <err.h>
+#include <libutil.h>
+#include <pwd.h>
+#include <string.h>
+#include <errno.h>
+#include "rpcbind.h"
+
+/* Global variables */
+int debugging = 0; /* Tell me what's going on */
+int doabort = 0; /* When debugging, do an abort on errors */
+rpcblist_ptr list_rbl; /* A list of version 3/4 rpcbind services */
+
+/* who to suid to if -s is given */
+#define RUN_AS "daemon"
+
+int runasdaemon = 0;
+int insecure = 0;
+int oldstyle_local = 0;
+int verboselog = 0;
+
+#ifdef WARMSTART
+/* Local Variable */
+static int warmstart = 0; /* Grab a old copy of registrations */
+#endif
+
+#ifdef PORTMAP
+struct pmaplist *list_pml; /* A list of version 2 rpcbind services */
+char *udptrans; /* Name of UDP transport */
+char *tcptrans; /* Name of TCP transport */
+char *udp_uaddr; /* Universal UDP address */
+char *tcp_uaddr; /* Universal TCP address */
+#endif
+static char servname[] = "rpcbind";
+static char superuser[] = "superuser";
+
+int main __P((int, char *[]));
+
+static int init_transport __P((struct netconfig *));
+static void rbllist_add __P((rpcprog_t, rpcvers_t, struct netconfig *,
+ struct netbuf *));
+static void terminate __P((int));
+static void parseargs __P((int, char *[]));
+
+int
+main(int argc, char *argv[])
+{
+ struct netconfig *nconf;
+ void *nc_handle; /* Net config handle */
+ struct rlimit rl;
+
+ parseargs(argc, argv);
+
+ getrlimit(RLIMIT_NOFILE, &rl);
+ if (rl.rlim_cur < 128) {
+ if (rl.rlim_max <= 128)
+ rl.rlim_cur = rl.rlim_max;
+ else
+ rl.rlim_cur = 128;
+ setrlimit(RLIMIT_NOFILE, &rl);
+ }
+ openlog("rpcbind", LOG_CONS, LOG_DAEMON);
+ if (geteuid()) { /* This command allowed only to root */
+ fprintf(stderr, "Sorry. You are not superuser\n");
+ exit(1);
+ }
+ nc_handle = setnetconfig(); /* open netconfig file */
+ if (nc_handle == NULL) {
+ syslog(LOG_ERR, "could not read /etc/netconfig");
+ exit(1);
+ }
+#ifdef PORTMAP
+ udptrans = "";
+ tcptrans = "";
+#endif
+
+ nconf = getnetconfigent("unix");
+ if (nconf == NULL) {
+ syslog(LOG_ERR, "%s: can't find local transport\n", argv[0]);
+ exit(1);
+ }
+ init_transport(nconf);
+
+ while ((nconf = getnetconfig(nc_handle))) {
+ if (nconf->nc_flag & NC_VISIBLE)
+ init_transport(nconf);
+ }
+ endnetconfig(nc_handle);
+
+ /* catch the usual termination signals for graceful exit */
+ (void) signal(SIGCHLD, reap);
+ (void) signal(SIGINT, terminate);
+ (void) signal(SIGTERM, terminate);
+ (void) signal(SIGQUIT, terminate);
+ /* ignore others that could get sent */
+ (void) signal(SIGPIPE, SIG_IGN);
+ (void) signal(SIGHUP, SIG_IGN);
+ (void) signal(SIGUSR1, SIG_IGN);
+ (void) signal(SIGUSR2, SIG_IGN);
+#ifdef WARMSTART
+ if (warmstart) {
+ read_warmstart();
+ }
+#endif
+ if (debugging) {
+ printf("rpcbind debugging enabled.");
+ if (doabort) {
+ printf(" Will abort on errors!\n");
+ } else {
+ printf("\n");
+ }
+ } else {
+ if (daemon(0, 0))
+ err(1, "fork failed");
+ }
+
+ if (runasdaemon) {
+ struct passwd *p;
+
+ if((p = getpwnam(RUN_AS)) == NULL) {
+ syslog(LOG_ERR, "cannot get uid of daemon: %m");
+ exit(1);
+ }
+ if (setuid(p->pw_uid) == -1) {
+ syslog(LOG_ERR, "setuid to daemon failed: %m");
+ exit(1);
+ }
+ }
+
+ network_init();
+
+ my_svc_run();
+ syslog(LOG_ERR, "svc_run returned unexpectedly");
+ rpcbind_abort();
+ /* NOTREACHED */
+
+ return 0;
+}
+
+/*
+ * Adds the entry into the rpcbind database.
+ * If PORTMAP, then for UDP and TCP, it adds the entries for version 2 also
+ * Returns 0 if succeeds, else fails
+ */
+static int
+init_transport(struct netconfig *nconf)
+{
+ int fd;
+ struct t_bind taddr;
+ struct addrinfo hints, *res = NULL;
+ struct __rpc_sockinfo si;
+ SVCXPRT *my_xprt;
+ int status; /* bound checking ? */
+ int aicode;
+ int addrlen;
+ struct sockaddr *sa;
+ struct sockaddr_un sun;
+ mode_t oldmask;
+
+ if ((nconf->nc_semantics != NC_TPI_CLTS) &&
+ (nconf->nc_semantics != NC_TPI_COTS) &&
+ (nconf->nc_semantics != NC_TPI_COTS_ORD))
+ return (1); /* not my type */
+#ifdef ND_DEBUG
+ if (debugging) {
+ int i;
+ char **s;
+
+ (void) fprintf(stderr, "%s: %ld lookup routines :\n",
+ nconf->nc_netid, nconf->nc_nlookups);
+ for (i = 0, s = nconf->nc_lookups; i < nconf->nc_nlookups;
+ i++, s++)
+ fprintf(stderr, "[%d] - %s\n", i, *s);
+ }
+#endif
+
+ /*
+ * XXX - using RPC library internal functions.
+ */
+ if ((fd = __rpc_nconf2fd(nconf)) < 0) {
+ syslog(LOG_ERR, "cannot create socket for %s", nconf->nc_netid);
+ return (1);
+ }
+
+ if (!__rpc_nconf2sockinfo(nconf, &si)) {
+ syslog(LOG_ERR, "cannot get information for %s",
+ nconf->nc_netid);
+ return (1);
+ }
+
+ if (!strcmp(nconf->nc_netid, "unix")) {
+ memset(&sun, 0, sizeof sun);
+ sun.sun_family = AF_LOCAL;
+ unlink(_PATH_RPCBINDSOCK);
+ strcpy(sun.sun_path, _PATH_RPCBINDSOCK);
+ sun.sun_len = SUN_LEN(&sun);
+ addrlen = sizeof (struct sockaddr_un);
+ sa = (struct sockaddr *)&sun;
+ } else {
+ /* Get rpcbind's address on this transport */
+
+ memset(&hints, 0, sizeof hints);
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = si.si_af;
+ hints.ai_socktype = si.si_socktype;
+ hints.ai_protocol = si.si_proto;
+ if ((aicode = getaddrinfo(NULL, servname, &hints, &res)) != 0) {
+ syslog(LOG_ERR, "cannot get local address for %s: %s",
+ nconf->nc_netid, gai_strerror(aicode));
+ return 1;
+ }
+ addrlen = res->ai_addrlen;
+ sa = (struct sockaddr *)res->ai_addr;
+ }
+ oldmask = umask(S_IXUSR|S_IXGRP|S_IXOTH);
+ if (bind(fd, sa, addrlen) < 0) {
+ syslog(LOG_ERR, "cannot bind %s: %m", nconf->nc_netid);
+ if (res != NULL)
+ freeaddrinfo(res);
+ return 1;
+ }
+ (void) umask(oldmask);
+
+ /* Copy the address */
+ taddr.addr.len = taddr.addr.maxlen = addrlen;
+ taddr.addr.buf = malloc(addrlen);
+ if (taddr.addr.buf == NULL) {
+ syslog(LOG_ERR, "cannot allocate memory for %s address",
+ nconf->nc_netid);
+ if (res != NULL)
+ freeaddrinfo(res);
+ return 1;
+ }
+ memcpy(taddr.addr.buf, sa, addrlen);
+#ifdef ND_DEBUG
+ if (debugging) {
+ /* for debugging print out our universal address */
+ char *uaddr;
+ struct netbuf nb;
+
+ nb.buf = sa;
+ nb.len = nb.maxlen = sa->sa_len;
+ uaddr = taddr2uaddr(nconf, &nb);
+ (void) fprintf(stderr, "rpcbind : my address is %s\n", uaddr);
+ (void) free(uaddr);
+ }
+#endif
+
+ if (res != NULL)
+ freeaddrinfo(res);
+
+ if (nconf->nc_semantics != NC_TPI_CLTS)
+ listen(fd, SOMAXCONN);
+
+ my_xprt = (SVCXPRT *)svc_tli_create(fd, nconf, &taddr, 0, 0);
+ if (my_xprt == (SVCXPRT *)NULL) {
+ syslog(LOG_ERR, "%s: could not create service",
+ nconf->nc_netid);
+ goto error;
+ }
+
+#ifdef PORTMAP
+ /*
+ * Register both the versions for tcp/ip, udp/ip and local.
+ */
+ if ((strcmp(nconf->nc_protofmly, NC_INET) == 0 &&
+ (strcmp(nconf->nc_proto, NC_TCP) == 0 ||
+ strcmp(nconf->nc_proto, NC_UDP) == 0)) ||
+ strcmp(nconf->nc_netid, "unix") == 0) {
+ struct pmaplist *pml;
+
+ if (!svc_register(my_xprt, PMAPPROG, PMAPVERS,
+ pmap_service, NULL)) {
+ syslog(LOG_ERR, "could not register on %s",
+ nconf->nc_netid);
+ goto error;
+ }
+ pml = malloc(sizeof (struct pmaplist));
+ if (pml == NULL) {
+ syslog(LOG_ERR, "no memory!");
+ exit(1);
+ }
+ pml->pml_map.pm_prog = PMAPPROG;
+ pml->pml_map.pm_vers = PMAPVERS;
+ pml->pml_map.pm_port = PMAPPORT;
+ if (strcmp(nconf->nc_proto, NC_TCP) == 0) {
+ if (tcptrans[0]) {
+ syslog(LOG_ERR,
+ "cannot have more than one TCP transport");
+ goto error;
+ }
+ tcptrans = strdup(nconf->nc_netid);
+ pml->pml_map.pm_prot = IPPROTO_TCP;
+
+ /* Let's snarf the universal address */
+ /* "h1.h2.h3.h4.p1.p2" */
+ tcp_uaddr = taddr2uaddr(nconf, &taddr.addr);
+ } else if (strcmp(nconf->nc_proto, NC_UDP) == 0) {
+ if (udptrans[0]) {
+ syslog(LOG_ERR,
+ "cannot have more than one UDP transport");
+ goto error;
+ }
+ udptrans = strdup(nconf->nc_netid);
+ pml->pml_map.pm_prot = IPPROTO_UDP;
+
+ /* Let's snarf the universal address */
+ /* "h1.h2.h3.h4.p1.p2" */
+ udp_uaddr = taddr2uaddr(nconf, &taddr.addr);
+ } else if (strcmp(nconf->nc_netid, "unix") == 0)
+ pml->pml_map.pm_prot = IPPROTO_ST;
+ pml->pml_next = list_pml;
+ list_pml = pml;
+
+ /* Add version 3 information */
+ pml = malloc(sizeof (struct pmaplist));
+ if (pml == NULL) {
+ syslog(LOG_ERR, "no memory!");
+ exit(1);
+ }
+ pml->pml_map = list_pml->pml_map;
+ pml->pml_map.pm_vers = RPCBVERS;
+ pml->pml_next = list_pml;
+ list_pml = pml;
+
+ /* Add version 4 information */
+ pml = malloc (sizeof (struct pmaplist));
+ if (pml == NULL) {
+ syslog(LOG_ERR, "no memory!");
+ exit(1);
+ }
+ pml->pml_map = list_pml->pml_map;
+ pml->pml_map.pm_vers = RPCBVERS4;
+ pml->pml_next = list_pml;
+ list_pml = pml;
+
+ /* Also add version 2 stuff to rpcbind list */
+ rbllist_add(PMAPPROG, PMAPVERS, nconf, &taddr.addr);
+ }
+#endif
+
+ /* version 3 registration */
+ if (!svc_reg(my_xprt, RPCBPROG, RPCBVERS, rpcb_service_3, NULL)) {
+ syslog(LOG_ERR, "could not register %s version 3",
+ nconf->nc_netid);
+ goto error;
+ }
+ rbllist_add(RPCBPROG, RPCBVERS, nconf, &taddr.addr);
+
+ /* version 4 registration */
+ if (!svc_reg(my_xprt, RPCBPROG, RPCBVERS4, rpcb_service_4, NULL)) {
+ syslog(LOG_ERR, "could not register %s version 4",
+ nconf->nc_netid);
+ goto error;
+ }
+ rbllist_add(RPCBPROG, RPCBVERS4, nconf, &taddr.addr);
+
+ /* decide if bound checking works for this transport */
+ status = add_bndlist(nconf, &taddr.addr);
+#ifdef BIND_DEBUG
+ if (debugging) {
+ if (status < 0) {
+ fprintf(stderr, "Error in finding bind status for %s\n",
+ nconf->nc_netid);
+ } else if (status == 0) {
+ fprintf(stderr, "check binding for %s\n",
+ nconf->nc_netid);
+ } else if (status > 0) {
+ fprintf(stderr, "No check binding for %s\n",
+ nconf->nc_netid);
+ }
+ }
+#endif
+ /*
+ * rmtcall only supported on CLTS transports for now.
+ */
+ if (nconf->nc_semantics == NC_TPI_CLTS) {
+ status = create_rmtcall_fd(nconf);
+
+#ifdef BIND_DEBUG
+ if (debugging) {
+ if (status < 0) {
+ fprintf(stderr,
+ "Could not create rmtcall fd for %s\n",
+ nconf->nc_netid);
+ } else {
+ fprintf(stderr, "rmtcall fd for %s is %d\n",
+ nconf->nc_netid, status);
+ }
+ }
+#endif
+ }
+ return (0);
+error:
+ close(fd);
+ return (1);
+}
+
+static void
+rbllist_add(rpcprog_t prog, rpcvers_t vers, struct netconfig *nconf,
+ struct netbuf *addr)
+{
+ rpcblist_ptr rbl;
+
+ rbl = malloc(sizeof (rpcblist));
+ if (rbl == NULL) {
+ syslog(LOG_ERR, "no memory!");
+ exit(1);
+ }
+
+ rbl->rpcb_map.r_prog = prog;
+ rbl->rpcb_map.r_vers = vers;
+ rbl->rpcb_map.r_netid = strdup(nconf->nc_netid);
+ rbl->rpcb_map.r_addr = taddr2uaddr(nconf, addr);
+ rbl->rpcb_map.r_owner = strdup(superuser);
+ rbl->rpcb_next = list_rbl; /* Attach to global list */
+ list_rbl = rbl;
+}
+
+/*
+ * Catch the signal and die
+ */
+static void
+terminate(int dummy)
+{
+#ifdef WARMSTART
+ syslog(LOG_ERR,
+ "rpcbind terminating on signal. Restart with \"rpcbind -w\"");
+ write_warmstart(); /* Dump yourself */
+#endif
+ exit(2);
+}
+
+void
+rpcbind_abort()
+{
+#ifdef WARMSTART
+ write_warmstart(); /* Dump yourself */
+#endif
+ abort();
+}
+
+/* get command line options */
+static void
+parseargs(int argc, char *argv[])
+{
+ int c;
+
+ while ((c = getopt(argc, argv, "dwailLs")) != -1) {
+ switch (c) {
+ case 'a':
+ doabort = 1; /* when debugging, do an abort on */
+ break; /* errors; for rpcbind developers */
+ /* only! */
+ case 'd':
+ debugging = 1;
+ break;
+ case 'i':
+ insecure = 1;
+ break;
+ case 'L':
+ oldstyle_local = 1;
+ break;
+ case 'l':
+ verboselog = 1;
+ break;
+ case 's':
+ runasdaemon = 1;
+ break;
+#ifdef WARMSTART
+ case 'w':
+ warmstart = 1;
+ break;
+#endif
+ default: /* error */
+ fprintf(stderr, "usage: rpcbind [-Idwils]\n");
+ exit (1);
+ }
+ }
+ if (doabort && !debugging) {
+ fprintf(stderr,
+ "-a (abort) specified without -d (debugging) -- ignored.\n");
+ doabort = 0;
+ }
+}
+
+void
+reap(int dummy)
+{
+ int save_errno = errno;
+
+ while (wait3(NULL, WNOHANG, NULL) > 0)
+ ;
+ errno = save_errno;
+}
+
+void
+toggle_verboselog(int dummy)
+{
+ verboselog = !verboselog;
+}
diff --git a/usr.sbin/rpcbind/rpcbind.h b/usr.sbin/rpcbind/rpcbind.h
new file mode 100644
index 0000000..63cf2d4
--- /dev/null
+++ b/usr.sbin/rpcbind/rpcbind.h
@@ -0,0 +1,144 @@
+/* $NetBSD: rpcbind.h,v 1.1 2000/06/03 00:47:21 fvdl Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+/*
+ * Copyright (c) 1986 - 1991 by Sun Microsystems, Inc.
+ */
+
+/* #ident "@(#)rpcbind.h 1.4 90/04/12 SMI" */
+
+/*
+ * rpcbind.h
+ * The common header declarations
+ */
+
+#ifndef rpcbind_h
+#define rpcbind_h
+
+#ifdef PORTMAP
+#include <rpc/pmap_prot.h>
+#endif
+#include <rpc/rpcb_prot.h>
+
+/*
+ * Stuff for the rmtcall service
+ */
+struct encap_parms {
+ u_int32_t arglen;
+ char *args;
+};
+
+struct r_rmtcall_args {
+ u_int32_t rmt_prog;
+ u_int32_t rmt_vers;
+ u_int32_t rmt_proc;
+ int rmt_localvers; /* whether to send port # or uaddr */
+ char *rmt_uaddr;
+ struct encap_parms rmt_args;
+};
+
+extern int debugging;
+extern int doabort;
+extern int verboselog;
+extern int insecure;
+extern int oldstyle_local;
+extern rpcblist_ptr list_rbl; /* A list of version 3 & 4 rpcbind services */
+
+#ifdef PORTMAP
+extern struct pmaplist *list_pml; /* A list of version 2 rpcbind services */
+extern char *udptrans; /* Name of UDP transport */
+extern char *tcptrans; /* Name of TCP transport */
+extern char *udp_uaddr; /* Universal UDP address */
+extern char *tcp_uaddr; /* Universal TCP address */
+#endif
+
+int add_bndlist __P((struct netconfig *, struct netbuf *));
+bool_t is_bound __P((char *, char *));
+char *mergeaddr __P((SVCXPRT *, char *, char *, char *));
+struct netconfig *rpcbind_get_conf __P((char *));
+
+void rpcbs_init __P((void));
+void rpcbs_procinfo __P((rpcvers_t, rpcproc_t));
+void rpcbs_set __P((rpcvers_t, bool_t));
+void rpcbs_unset __P((rpcvers_t, bool_t));
+void rpcbs_getaddr __P((rpcvers_t, rpcprog_t, rpcvers_t, char *, char *));
+void rpcbs_rmtcall __P((rpcvers_t, rpcproc_t, rpcprog_t, rpcvers_t, rpcproc_t,
+ char *, rpcblist_ptr));
+void *rpcbproc_getstat __P((void *, struct svc_req *, SVCXPRT *, rpcvers_t));
+
+void rpcb_service_3 __P((struct svc_req *, SVCXPRT *));
+void rpcb_service_4 __P((struct svc_req *, SVCXPRT *));
+
+/* Common functions shared between versions */
+void *rpcbproc_set_com __P((void *, struct svc_req *, SVCXPRT *, rpcvers_t));
+void *rpcbproc_unset_com __P((void *, struct svc_req *, SVCXPRT *, rpcvers_t));
+bool_t map_set __P((RPCB *, char *));
+bool_t map_unset __P((RPCB *, char *));
+void delete_prog __P((int));
+void *rpcbproc_getaddr_com __P((RPCB *, struct svc_req *, SVCXPRT *, rpcvers_t,
+ rpcvers_t));
+void *rpcbproc_gettime_com __P((void *, struct svc_req *, SVCXPRT *,
+ rpcvers_t));
+void *rpcbproc_uaddr2taddr_com __P((void *, struct svc_req *,
+ SVCXPRT *, rpcvers_t));
+void *rpcbproc_taddr2uaddr_com __P((void *, struct svc_req *, SVCXPRT *,
+ rpcvers_t));
+int create_rmtcall_fd __P((struct netconfig *));
+void rpcbproc_callit_com __P((struct svc_req *, SVCXPRT *, rpcvers_t,
+ rpcvers_t));
+void my_svc_run __P((void));
+
+void rpcbind_abort __P((void));
+void reap __P((int));
+void toggle_verboselog __P((int));
+
+int check_access __P((SVCXPRT *, rpcproc_t, void *, int));
+int check_callit __P((SVCXPRT *, struct r_rmtcall_args *, int));
+void logit __P((int, struct sockaddr *, rpcproc_t, rpcprog_t, const char *));
+int is_loopback __P((struct netbuf *));
+
+#ifdef PORTMAP
+extern void pmap_service __P((struct svc_req *, SVCXPRT *));
+#endif
+
+void write_warmstart __P((void));
+void read_warmstart __P((void));
+
+char *addrmerge __P((struct netbuf *caller, char *serv_uaddr, char *clnt_uaddr,
+ char *netid));
+void network_init __P((void));
+struct sockaddr *local_sa __P((int));
+
+/* For different getaddr semantics */
+#define RPCB_ALLVERS 0
+#define RPCB_ONEVERS 1
+
+#endif /* rpcbind_h */
diff --git a/usr.sbin/rpcbind/security.c b/usr.sbin/rpcbind/security.c
new file mode 100644
index 0000000..8f47f66
--- /dev/null
+++ b/usr.sbin/rpcbind/security.c
@@ -0,0 +1,284 @@
+/* $NetBSD: security.c,v 1.5 2000/06/08 09:01:05 fvdl Exp $ */
+/* $FreeBSD$ */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <rpc/rpc.h>
+#include <rpc/rpcb_prot.h>
+#include <rpc/pmap_prot.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <libutil.h>
+#include <syslog.h>
+#include <netdb.h>
+
+/*
+ * XXX for special case checks in check_callit.
+ */
+#include <rpcsvc/mount.h>
+#include <rpcsvc/rquota.h>
+#include <rpcsvc/nfs_prot.h>
+#include <rpcsvc/yp.h>
+#include <rpcsvc/ypclnt.h>
+#include <rpcsvc/yppasswd.h>
+
+#include "rpcbind.h"
+
+#ifdef LIBWRAP
+# include <tcpd.h>
+#ifndef LIBWRAP_ALLOW_FACILITY
+# define LIBWRAP_ALLOW_FACILITY LOG_AUTH
+#endif
+#ifndef LIBWRAP_ALLOW_SEVERITY
+# define LIBWRAP_ALLOW_SEVERITY LOG_INFO
+#endif
+#ifndef LIBWRAP_DENY_FACILITY
+# define LIBWRAP_DENY_FACILITY LOG_AUTH
+#endif
+#ifndef LIBWRAP_DENY_SEVERITY
+# define LIBWRAP_DENY_SEVERITY LOG_WARNING
+#endif
+int allow_severity = LIBWRAP_ALLOW_FACILITY|LIBWRAP_ALLOW_SEVERITY;
+int deny_severity = LIBWRAP_DENY_FACILITY|LIBWRAP_DENY_SEVERITY;
+#endif
+
+#ifndef PORTMAP_LOG_FACILITY
+# define PORTMAP_LOG_FACILITY LOG_AUTH
+#endif
+#ifndef PORTMAP_LOG_SEVERITY
+# define PORTMAP_LOG_SEVERITY LOG_INFO
+#endif
+int log_severity = PORTMAP_LOG_FACILITY|PORTMAP_LOG_SEVERITY;
+
+extern int verboselog;
+
+int
+check_access(SVCXPRT *xprt, rpcproc_t proc, void *args, int rpcbvers)
+{
+ struct netbuf *caller = svc_getrpccaller(xprt);
+ struct sockaddr *addr = (struct sockaddr *)caller->buf;
+#ifdef LIBWRAP
+ struct request_info req;
+#endif
+ rpcprog_t prog = 0;
+ rpcb *rpcbp;
+ struct pmap *pmap;
+
+ /*
+ * The older PMAP_* equivalents have the same numbers, so
+ * they are accounted for here as well.
+ */
+ switch (proc) {
+ case RPCBPROC_GETADDR:
+ case RPCBPROC_SET:
+ case RPCBPROC_UNSET:
+ if (rpcbvers > PMAPVERS) {
+ rpcbp = (rpcb *)args;
+ prog = rpcbp->r_prog;
+ } else {
+ pmap = (struct pmap *)args;
+ prog = pmap->pm_prog;
+ }
+ if (proc == RPCBPROC_GETADDR)
+ break;
+ if (!insecure && !is_loopback(caller)) {
+ if (verboselog)
+ logit(log_severity, addr, proc, prog,
+ " declined (non-loopback sender)");
+ return 0;
+ }
+ break;
+ case RPCBPROC_CALLIT:
+ case RPCBPROC_INDIRECT:
+ case RPCBPROC_DUMP:
+ case RPCBPROC_GETTIME:
+ case RPCBPROC_UADDR2TADDR:
+ case RPCBPROC_TADDR2UADDR:
+ case RPCBPROC_GETVERSADDR:
+ case RPCBPROC_GETADDRLIST:
+ case RPCBPROC_GETSTAT:
+ default:
+ }
+
+#ifdef LIBWRAP
+ if (addr->sa_family == AF_LOCAL)
+ return 1;
+ request_init(&req, RQ_DAEMON, "rpcbind", RQ_CLIENT_SIN, addr, 0);
+ sock_methods(&req);
+ if(!hosts_access(&req)) {
+ logit(deny_severity, addr, proc, prog, ": request from unauthorized host");
+ return 0;
+ }
+#endif
+ if (verboselog)
+ logit(log_severity, addr, proc, prog, "");
+ return 1;
+}
+
+int
+is_loopback(struct netbuf *nbuf)
+{
+ struct sockaddr *addr = (struct sockaddr *)nbuf->buf;
+ struct sockaddr_in *sin;
+#ifdef INET6
+ struct sockaddr_in6 *sin6;
+#endif
+
+ switch (addr->sa_family) {
+ case AF_INET:
+ if (!oldstyle_local)
+ return 0;
+ sin = (struct sockaddr_in *)addr;
+ return ((sin->sin_addr.s_addr == htonl(INADDR_LOOPBACK)) &&
+ (ntohs(sin->sin_port) < IPPORT_RESERVED));
+#ifdef INET6
+ case AF_INET6:
+ if (!oldstyle_local)
+ return 0;
+ sin6 = (struct sockaddr_in6 *)addr;
+ return (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr) &&
+ (ntohs(sin6->sin6_port) < IPV6PORT_RESERVED));
+#endif
+ case AF_LOCAL:
+ return 1;
+ default:
+ }
+
+ return 0;
+}
+
+
+/* logit - report events of interest via the syslog daemon */
+void
+logit(int severity, struct sockaddr *addr, rpcproc_t procnum, rpcprog_t prognum,
+ const char *text)
+{
+ const char *procname;
+ char procbuf[32];
+ char *progname;
+ char progbuf[32];
+ char fromname[NI_MAXHOST];
+ struct rpcent *rpc;
+ static const char *procmap[] = {
+ /* RPCBPROC_NULL */ "null",
+ /* RPCBPROC_SET */ "set",
+ /* RPCBPROC_UNSET */ "unset",
+ /* RPCBPROC_GETADDR */ "getport/addr",
+ /* RPCBPROC_DUMP */ "dump",
+ /* RPCBPROC_CALLIT */ "callit",
+ /* RPCBPROC_GETTIME */ "gettime",
+ /* RPCBPROC_UADDR2TADDR */ "uaddr2taddr",
+ /* RPCBPROC_TADDR2UADDR */ "taddr2uaddr",
+ /* RPCBPROC_GETVERSADDR */ "getversaddr",
+ /* RPCBPROC_INDIRECT */ "indirect",
+ /* RPCBPROC_GETADDRLIST */ "getaddrlist",
+ /* RPCBPROC_GETSTAT */ "getstat"
+ };
+
+ /*
+ * Fork off a process or the portmap daemon might hang while
+ * getrpcbynumber() or syslog() does its thing.
+ */
+
+ if (fork() == 0) {
+ setproctitle("logit");
+
+ /* Try to map program number to name. */
+
+ if (prognum == 0) {
+ progname = "";
+ } else if ((rpc = getrpcbynumber((int) prognum))) {
+ progname = rpc->r_name;
+ } else {
+ snprintf(progname = progbuf, sizeof(progbuf), "%u",
+ (unsigned)prognum);
+ }
+
+ /* Try to map procedure number to name. */
+
+ if (procnum >= (sizeof procmap / sizeof (char *))) {
+ snprintf(procbuf, sizeof procbuf, "%u",
+ (unsigned)procnum);
+ procname = procbuf;
+ } else
+ procname = procmap[procnum];
+
+ /* Write syslog record. */
+
+ if (addr->sa_family == AF_LOCAL)
+ strcpy(fromname, "unix");
+ else
+ getnameinfo(addr, addr->sa_len, fromname,
+ sizeof fromname, NULL, 0, NI_NUMERICHOST);
+
+ syslog(severity, "connect from %s to %s(%s)%s",
+ fromname, procname, progname, text);
+ _exit(0);
+ }
+}
+
+int
+check_callit(SVCXPRT *xprt, struct r_rmtcall_args *args, int versnum)
+{
+ struct sockaddr *sa = (struct sockaddr *)svc_getrpccaller(xprt)->buf;
+
+ /*
+ * Always allow calling NULLPROC
+ */
+ if (args->rmt_proc == 0)
+ return 1;
+
+ /*
+ * XXX - this special casing sucks.
+ */
+ switch (args->rmt_prog) {
+ case RPCBPROG:
+ /*
+ * Allow indirect calls to ourselves in insecure mode.
+ * The is_loopback checks aren't useful then anyway.
+ */
+ if (!insecure)
+ goto deny;
+ break;
+ case MOUNTPROG:
+ if (args->rmt_proc != MOUNTPROC_MNT &&
+ args->rmt_proc != MOUNTPROC_UMNT)
+ break;
+ goto deny;
+ case YPBINDPROG:
+ if (args->rmt_proc != YPBINDPROC_SETDOM)
+ break;
+ /* FALLTHROUGH */
+ case YPPASSWDPROG:
+ case NFS_PROGRAM:
+ case RQUOTAPROG:
+ goto deny;
+ case YPPROG:
+ switch (args->rmt_proc) {
+ case YPPROC_ALL:
+ case YPPROC_MATCH:
+ case YPPROC_FIRST:
+ case YPPROC_NEXT:
+ goto deny;
+ default:
+ }
+ default:
+ }
+
+ return 1;
+deny:
+#ifdef LIBWRAP
+ logit(deny_severity, sa, args->rmt_proc, args->rmt_prog,
+ ": indirect call not allowed");
+#else
+ logit(0, sa, args->rmt_proc, args->rmt_prog,
+ ": indirect call not allowed");
+#endif
+ return 0;
+}
diff --git a/usr.sbin/rpcbind/util.c b/usr.sbin/rpcbind/util.c
new file mode 100644
index 0000000..f62c828
--- /dev/null
+++ b/usr.sbin/rpcbind/util.c
@@ -0,0 +1,382 @@
+/*
+ * $NetBSD: util.c,v 1.4 2000/08/03 00:04:30 fvdl Exp $
+ * $FreeBSD$
+ */
+
+/*-
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Frank van der Linden.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <ifaddrs.h>
+#include <sys/poll.h>
+#include <rpc/rpc.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <netconfig.h>
+#include <stdio.h>
+#include <arpa/inet.h>
+
+#include "rpcbind.h"
+
+#define SA2SIN(sa) ((struct sockaddr_in *)(sa))
+#define SA2SINADDR(sa) (SA2SIN(sa)->sin_addr)
+#ifdef INET6
+#define SA2SIN6(sa) ((struct sockaddr_in6 *)(sa))
+#define SA2SIN6ADDR(sa) (SA2SIN6(sa)->sin6_addr)
+#endif
+
+static struct sockaddr_in *local_in4;
+#ifdef INET6
+static struct sockaddr_in6 *local_in6;
+#endif
+
+static int bitmaskcmp __P((void *, void *, void *, int));
+#ifdef INET6
+static void in6_fillscopeid __P((struct sockaddr_in6 *));
+#endif
+
+/*
+ * For all bits set in "mask", compare the corresponding bits in
+ * "dst" and "src", and see if they match. Returns 0 if the addresses
+ * match.
+ */
+static int
+bitmaskcmp(void *dst, void *src, void *mask, int bytelen)
+{
+ int i;
+ u_int8_t *p1 = dst, *p2 = src, *netmask = mask;
+
+ for (i = 0; i < bytelen; i++)
+ if ((p1[i] & netmask[i]) != (p2[i] & netmask[i]))
+ return (1);
+ return (0);
+}
+
+/*
+ * Similar to code in ifconfig.c. Fill in the scope ID for link-local
+ * addresses returned by getifaddrs().
+ */
+#ifdef INET6
+static void
+in6_fillscopeid(struct sockaddr_in6 *sin6)
+{
+ u_int16_t ifindex;
+
+ if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
+ ifindex = ntohs(*(u_int16_t *)&sin6->sin6_addr.s6_addr[2]);
+ if (sin6->sin6_scope_id == 0 && ifindex != 0) {
+ sin6->sin6_scope_id = ifindex;
+ *(u_int16_t *)&sin6->sin6_addr.s6_addr[2] = 0;
+ }
+ }
+}
+#endif
+
+/*
+ * Find a server address that can be used by `caller' to contact
+ * the local service specified by `serv_uaddr'. If `clnt_uaddr' is
+ * non-NULL, it is used instead of `caller' as a hint suggesting
+ * the best address (e.g. the `r_addr' field of an rpc, which
+ * contains the rpcbind server address that the caller used).
+ *
+ * Returns the best server address as a malloc'd "universal address"
+ * string which should be freed by the caller. On error, returns NULL.
+ */
+char *
+addrmerge(struct netbuf *caller, char *serv_uaddr, char *clnt_uaddr,
+ char *netid)
+{
+ struct ifaddrs *ifap, *ifp = NULL, *bestif;
+ struct netbuf *serv_nbp = NULL, *hint_nbp = NULL, tbuf;
+ struct sockaddr *caller_sa, *hint_sa, *ifsa, *ifmasksa, *serv_sa;
+ struct sockaddr_storage ss;
+ struct netconfig *nconf;
+ char *caller_uaddr = NULL, *hint_uaddr = NULL;
+ char *ret = NULL;
+
+#ifdef ND_DEBUG
+ if (debugging)
+ fprintf(stderr, "addrmerge(caller, %s, %s, %s\n", serv_uaddr,
+ clnt_uaddr == NULL ? "NULL" : clnt_uaddr, netid);
+#endif
+ caller_sa = caller->buf;
+ if ((nconf = rpcbind_get_conf(netid)) == NULL)
+ goto freeit;
+ if ((caller_uaddr = taddr2uaddr(nconf, caller)) == NULL)
+ goto freeit;
+
+ /*
+ * Use `clnt_uaddr' as the hint if non-NULL, but ignore it if its
+ * address family is different from that of the caller.
+ */
+ hint_sa = NULL;
+ if (clnt_uaddr != NULL) {
+ hint_uaddr = clnt_uaddr;
+ if ((hint_nbp = uaddr2taddr(nconf, clnt_uaddr)) == NULL)
+ goto freeit;
+ hint_sa = hint_nbp->buf;
+ }
+ if (hint_sa == NULL || hint_sa->sa_family != caller_sa->sa_family) {
+ hint_uaddr = caller_uaddr;
+ hint_sa = caller->buf;
+ }
+
+#ifdef ND_DEBUG
+ if (debugging)
+ fprintf(stderr, "addrmerge: hint %s\n", hint_uaddr);
+#endif
+ /* Local caller, just return the server address. */
+ if (strncmp(caller_uaddr, "0.0.0.0.", 8) == 0 ||
+ strncmp(caller_uaddr, "::.", 3) == 0 || caller_uaddr[0] == '/') {
+ ret = strdup(serv_uaddr);
+ goto freeit;
+ }
+
+ if (getifaddrs(&ifp) < 0)
+ goto freeit;
+
+ /*
+ * Loop through all interfaces. For each interface, see if the
+ * network portion of its address is equal to that of the client.
+ * If so, we have found the interface that we want to use.
+ */
+ bestif = NULL;
+ for (ifap = ifp; ifap != NULL; ifap = ifap->ifa_next) {
+ ifsa = ifap->ifa_addr;
+ ifmasksa = ifap->ifa_netmask;
+
+ if (ifsa == NULL || ifsa->sa_family != hint_sa->sa_family ||
+ !(ifap->ifa_flags & IFF_UP))
+ continue;
+
+ switch (hint_sa->sa_family) {
+ case AF_INET:
+ /*
+ * If the hint address matches this interface
+ * address/netmask, then we're done.
+ */
+ if (!bitmaskcmp(&SA2SINADDR(ifsa),
+ &SA2SINADDR(hint_sa), &SA2SINADDR(ifmasksa),
+ sizeof(struct in_addr))) {
+ bestif = ifap;
+ goto found;
+ }
+ break;
+#ifdef INET6
+ case AF_INET6:
+ /*
+ * For v6 link local addresses, if the caller is on
+ * a link-local address then use the scope id to see
+ * which one.
+ */
+ in6_fillscopeid(SA2SIN6(ifsa));
+ if (IN6_IS_ADDR_LINKLOCAL(&SA2SIN6ADDR(ifsa)) &&
+ IN6_IS_ADDR_LINKLOCAL(&SA2SIN6ADDR(caller_sa)) &&
+ IN6_IS_ADDR_LINKLOCAL(&SA2SIN6ADDR(hint_sa))) {
+ if (SA2SIN6(ifsa)->sin6_scope_id ==
+ SA2SIN6(caller_sa)->sin6_scope_id) {
+ bestif = ifap;
+ goto found;
+ }
+ } else if (!bitmaskcmp(&SA2SIN6ADDR(ifsa),
+ &SA2SIN6ADDR(hint_sa), &SA2SIN6ADDR(ifmasksa),
+ sizeof(struct in6_addr))) {
+ bestif = ifap;
+ goto found;
+ }
+ break;
+#endif
+ default:
+ continue;
+ }
+
+ /*
+ * Remember the first possibly useful interface, preferring
+ * "normal" to point-to-point and loopback ones.
+ */
+ if (bestif == NULL ||
+ (!(ifap->ifa_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) &&
+ (bestif->ifa_flags & (IFF_LOOPBACK | IFF_POINTOPOINT))))
+ bestif = ifap;
+ }
+ if (bestif == NULL)
+ goto freeit;
+
+found:
+ /*
+ * Construct the new address using the the address from
+ * `bestif', and the port number from `serv_uaddr'.
+ */
+ serv_nbp = uaddr2taddr(nconf, serv_uaddr);
+ if (serv_nbp == NULL)
+ goto freeit;
+ serv_sa = serv_nbp->buf;
+
+ memcpy(&ss, bestif->ifa_addr, bestif->ifa_addr->sa_len);
+ switch (ss.ss_family) {
+ case AF_INET:
+ SA2SIN(&ss)->sin_port = SA2SIN(serv_sa)->sin_port;
+ break;
+#ifdef INET6
+ case AF_INET6:
+ SA2SIN6(&ss)->sin6_port = SA2SIN6(serv_sa)->sin6_port;
+ break;
+#endif
+ }
+ tbuf.len = ss.ss_len;
+ tbuf.maxlen = sizeof(ss);
+ tbuf.buf = &ss;
+ ret = taddr2uaddr(nconf, &tbuf);
+
+freeit:
+ if (caller_uaddr != NULL)
+ free(caller_uaddr);
+ if (hint_nbp != NULL) {
+ free(hint_nbp->buf);
+ free(hint_nbp);
+ }
+ if (serv_nbp != NULL) {
+ free(serv_nbp->buf);
+ free(serv_nbp);
+ }
+ if (ifp != NULL)
+ freeifaddrs(ifp);
+
+#ifdef ND_DEBUG
+ if (debugging)
+ fprintf(stderr, "addrmerge: returning %s\n", ret);
+#endif
+ return ret;
+}
+
+void
+network_init()
+{
+#ifdef INET6
+ struct ifaddrs *ifap, *ifp;
+ struct ipv6_mreq mreq6;
+ int ifindex, s;
+#endif
+ int ecode;
+ struct addrinfo hints, *res;
+
+ memset(&hints, 0, sizeof hints);
+ hints.ai_family = AF_INET;
+ if ((ecode = getaddrinfo(NULL, "sunrpc", &hints, &res))) {
+ if (debugging)
+ fprintf(stderr, "can't get local ip4 address: %s\n",
+ gai_strerror(ecode));
+ } else {
+ local_in4 = (struct sockaddr_in *)malloc(sizeof *local_in4);
+ if (local_in4 == NULL) {
+ if (debugging)
+ fprintf(stderr, "can't alloc local ip4 addr\n");
+ }
+ memcpy(local_in4, res->ai_addr, sizeof *local_in4);
+ }
+
+#ifdef INET6
+ hints.ai_family = AF_INET6;
+ if ((ecode = getaddrinfo(NULL, "sunrpc", &hints, &res))) {
+ if (debugging)
+ fprintf(stderr, "can't get local ip6 address: %s\n",
+ gai_strerror(ecode));
+ } else {
+ local_in6 = (struct sockaddr_in6 *)malloc(sizeof *local_in6);
+ if (local_in6 == NULL) {
+ if (debugging)
+ fprintf(stderr, "can't alloc local ip6 addr\n");
+ }
+ memcpy(local_in6, res->ai_addr, sizeof *local_in6);
+ }
+
+ /*
+ * Now join the RPC ipv6 multicast group on all interfaces.
+ */
+ if (getifaddrs(&ifp) < 0)
+ return;
+
+ mreq6.ipv6mr_interface = 0;
+ inet_pton(AF_INET6, RPCB_MULTICAST_ADDR, &mreq6.ipv6mr_multiaddr);
+
+ s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+
+ /*
+ * Loop through all interfaces. For each IPv6 multicast-capable
+ * interface, join the RPC multicast group on that interface.
+ */
+ for (ifap = ifp; ifap != NULL; ifap = ifap->ifa_next) {
+ if (ifap->ifa_addr->sa_family != AF_INET6 ||
+ !(ifap->ifa_flags & IFF_MULTICAST))
+ continue;
+ ifindex = if_nametoindex(ifap->ifa_name);
+ if (ifindex == mreq6.ipv6mr_interface)
+ /*
+ * Already did this one.
+ */
+ continue;
+ mreq6.ipv6mr_interface = ifindex;
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq6,
+ sizeof mreq6) < 0)
+ if (debugging)
+ perror("setsockopt v6 multicast");
+ }
+#endif
+
+ /* close(s); */
+}
+
+struct sockaddr *
+local_sa(int af)
+{
+ switch (af) {
+ case AF_INET:
+ return (struct sockaddr *)local_in4;
+#ifdef INET6
+ case AF_INET6:
+ return (struct sockaddr *)local_in6;
+#endif
+ default:
+ return NULL;
+ }
+}
diff --git a/usr.sbin/rpcbind/warmstart.c b/usr.sbin/rpcbind/warmstart.c
new file mode 100644
index 0000000..fea2789
--- /dev/null
+++ b/usr.sbin/rpcbind/warmstart.c
@@ -0,0 +1,179 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+/*
+ * warmstart.c
+ * Allows for gathering of registrations from a earlier dumped file.
+ *
+ * Copyright (c) 1990 by Sun Microsystems, Inc.
+ */
+
+/*
+ * #ident "@(#)warmstart.c 1.7 93/07/05 SMI"
+ * $FreeBSD$/
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <rpc/rpc.h>
+#include <rpc/rpcb_prot.h>
+#include <rpc/xdr.h>
+#ifdef PORTMAP
+#include <netinet/in.h>
+#include <rpc/pmap_prot.h>
+#endif
+#include <syslog.h>
+#include <unistd.h>
+
+#include "rpcbind.h"
+
+/*
+ * XXX this code is unsafe and is not used. It should be made safe.
+ */
+
+
+/* These files keep the pmap_list and rpcb_list in XDR format */
+#define RPCBFILE "/tmp/rpcbind.file"
+#ifdef PORTMAP
+#define PMAPFILE "/tmp/portmap.file"
+#endif
+
+static bool_t write_struct __P((char *, xdrproc_t, void *));
+static bool_t read_struct __P((char *, xdrproc_t, void *));
+
+static bool_t
+write_struct(char *filename, xdrproc_t structproc, void *list)
+{
+ FILE *fp;
+ XDR xdrs;
+ mode_t omask;
+
+ omask = umask(077);
+ fp = fopen(filename, "w");
+ if (fp == NULL) {
+ int i;
+
+ for (i = 0; i < 10; i++)
+ close(i);
+ fp = fopen(filename, "w");
+ if (fp == NULL) {
+ syslog(LOG_ERR,
+ "cannot open file = %s for writing", filename);
+ syslog(LOG_ERR, "cannot save any registration");
+ return (FALSE);
+ }
+ }
+ (void) umask(omask);
+ xdrstdio_create(&xdrs, fp, XDR_ENCODE);
+
+ if (structproc(&xdrs, list) == FALSE) {
+ syslog(LOG_ERR, "rpcbind: xdr_%s: failed", filename);
+ fclose(fp);
+ return (FALSE);
+ }
+ XDR_DESTROY(&xdrs);
+ fclose(fp);
+ return (TRUE);
+}
+
+static bool_t
+read_struct(char *filename, xdrproc_t structproc, void *list)
+{
+ FILE *fp;
+ XDR xdrs;
+ struct stat sbuf;
+
+ if (stat(filename, &sbuf) != 0) {
+ fprintf(stderr,
+ "rpcbind: cannot stat file = %s for reading\n", filename);
+ goto error;
+ }
+ if ((sbuf.st_uid != 0) || (sbuf.st_mode & S_IRWXG) ||
+ (sbuf.st_mode & S_IRWXO)) {
+ fprintf(stderr,
+ "rpcbind: invalid permissions on file = %s for reading\n",
+ filename);
+ goto error;
+ }
+ fp = fopen(filename, "r");
+ if (fp == NULL) {
+ fprintf(stderr,
+ "rpcbind: cannot open file = %s for reading\n", filename);
+ goto error;
+ }
+ xdrstdio_create(&xdrs, fp, XDR_DECODE);
+
+ if (structproc(&xdrs, list) == FALSE) {
+ fprintf(stderr, "rpcbind: xdr_%s: failed\n", filename);
+ fclose(fp);
+ goto error;
+ }
+ XDR_DESTROY(&xdrs);
+ fclose(fp);
+ return (TRUE);
+
+error: fprintf(stderr, "rpcbind: will start from scratch\n");
+ return (FALSE);
+}
+
+void
+write_warmstart()
+{
+ (void) write_struct(RPCBFILE, xdr_rpcblist_ptr, &list_rbl);
+#ifdef PORTMAP
+ (void) write_struct(PMAPFILE, xdr_pmaplist_ptr, &list_pml);
+#endif
+
+}
+
+void
+read_warmstart()
+{
+ rpcblist_ptr tmp_rpcbl = NULL;
+#ifdef PORTMAP
+ struct pmaplist *tmp_pmapl = NULL;
+#endif
+ int ok1, ok2 = TRUE;
+
+ ok1 = read_struct(RPCBFILE, xdr_rpcblist_ptr, &tmp_rpcbl);
+ if (ok1 == FALSE)
+ return;
+#ifdef PORTMAP
+ ok2 = read_struct(PMAPFILE, xdr_pmaplist_ptr, &tmp_pmapl);
+#endif
+ if (ok2 == FALSE) {
+ xdr_free((xdrproc_t) xdr_rpcblist_ptr, (char *)&tmp_rpcbl);
+ return;
+ }
+ xdr_free((xdrproc_t) xdr_rpcblist_ptr, (char *)&list_rbl);
+ list_rbl = tmp_rpcbl;
+#ifdef PORTMAP
+ xdr_free((xdrproc_t) xdr_pmaplist_ptr, (char *)&list_pml);
+ list_pml = tmp_pmapl;
+#endif
+}
diff --git a/usr.sbin/rrenumd/Makefile b/usr.sbin/rrenumd/Makefile
new file mode 100644
index 0000000..21a9489
--- /dev/null
+++ b/usr.sbin/rrenumd/Makefile
@@ -0,0 +1,36 @@
+# Copyright (c) 1996 WIDE Project. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modifications, are permitted provided that the above copyright notice
+# and this paragraph are duplicated in all such forms and that any
+# documentation, advertising materials, and other materials related to
+# such distribution and use acknowledge that the software was developed
+# by the WIDE Project, Japan. The name of the Project may not be used to
+# endorse or promote products derived from this software without
+# specific prior written permission. THIS SOFTWARE IS PROVIDED ``AS IS''
+# AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+# LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE.
+#
+# $FreeBSD$
+
+PROG= rrenumd
+MAN= rrenumd.conf.5 rrenumd.8
+SRCS= rrenumd.c parser.y lexer.l
+
+CFLAGS+= -DINET6 -DIPSEC -I. -I${.CURDIR}
+YFLAGS= -d
+
+LDADD= -lipsec -lcompat -ll -ly
+DPADD= ${LIBIPSEC} ${LIBCOMPAT} ${LIBL} ${LIBY}
+
+CLEANFILES= y.tab.h
+SRCS+= y.tab.h
+y.tab.h: parser.y
+
+.if defined(YACCDEBUG)
+CFLAGS+= -DYYDEBUG
+YFLAGS+= -t -v
+.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rrenumd/lexer.l b/usr.sbin/rrenumd/lexer.l
new file mode 100644
index 0000000..6ad9953
--- /dev/null
+++ b/usr.sbin/rrenumd/lexer.l
@@ -0,0 +1,273 @@
+/* $KAME: lexer.l,v 1.7 2000/11/08 02:40:53 itojun Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+%{
+#define YY_NO_UNPUT
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+
+#include <string.h>
+
+#include <net/if.h>
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+#include <net/if_var.h>
+#endif /* __FreeBSD__ >= 3 */
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/icmp6.h>
+
+#include <arpa/inet.h>
+
+#include "y.tab.h"
+
+int lineno = 1;
+
+#define LINEBUF_SIZE 1000
+char linebuf[LINEBUF_SIZE];
+
+int parse __P((FILE **));
+void yyerror __P((const char *));
+int yylex __P((void));
+%}
+
+/* common section */
+nl \n
+ws [ \t]+
+digit [0-9]
+letter [0-9A-Za-z]
+hexdigit [0-9A-Fa-f]
+special [()+\|\?\*,]
+dot \.
+hyphen \-
+colon \:
+slash \/
+bcl \{
+ecl \}
+semi \;
+usec {dot}{digit}{1,6}
+comment \#.*
+qstring \"[^"]*\"
+decstring {digit}+
+hexpair {hexdigit}{hexdigit}
+hexstring 0[xX]{hexdigit}+
+octetstring {octet}({dot}{octet})+
+ipv4addr {digit}{1,3}({dot}{digit}{1,3}){0,3}
+ipv6addr {hexdigit}{0,4}({colon}{hexdigit}{0,4}){2,7}
+ipaddrmask {slash}{digit}{1,3}
+keyword {letter}{letter}+
+name {letter}(({letter}|{digit}|{hyphen})*({letter}|{digit}))*
+hostname {name}(({dot}{name})+{dot}?)?
+
+timeval {digit}{0,2}
+days d{timeval}
+hours h{timeval}
+minutes m{timeval}
+seconds s{timeval}
+
+mprefix match_prefix|match-prefix
+uprefix use_prefix|use-prefix
+
+%%
+ /* rrenumd keywords */
+debug {
+ return(DEBUG_CMD);
+ }
+dest {
+ return(DEST_CMD);
+ }
+retry {
+ return(RETRY_CMD);
+ }
+seqnum {
+ return(SEQNUM_CMD);
+ }
+add {
+ yylval.num = RPM_PCO_ADD;
+ return(ADD);
+ }
+change {
+ yylval.num = RPM_PCO_CHANGE;
+ return(CHANGE);
+ }
+setglobal {
+ yylval.num = RPM_PCO_SETGLOBAL;
+ return(SETGLOBAL);
+ }
+{mprefix} {
+ return(MATCH_PREFIX_CMD);
+ }
+maxlen {
+ return(MAXLEN_CMD);
+ }
+minlen {
+ return(MINLEN_CMD);
+ }
+{uprefix} {
+ return(USE_PREFIX_CMD);
+ }
+keeplen {
+ return(KEEPLEN_CMD);
+ }
+
+vltime {
+ return(VLTIME_CMD);
+ }
+pltime {
+ return(PLTIME_CMD);
+ }
+raf_onlink {
+ return(RAF_ONLINK_CMD);
+ }
+raf_auto {
+ return(RAF_AUTO_CMD);
+ }
+rrf_decrvalid {
+ return(RAF_DECRVALID_CMD);
+ }
+rrf_decrprefd {
+ return(RAF_DECRPREFD_CMD);
+ }
+{days} {
+ yytext++;
+ yylval.num = atoi(yytext);
+ return(DAYS);
+ }
+{hours} {
+ yytext++;
+ yylval.num = atoi(yytext);
+ return(HOURS);
+ }
+{minutes} {
+ yytext++;
+ yylval.num = atoi(yytext);
+ return(MINUTES);
+ }
+{seconds} {
+ yytext++;
+ yylval.num = atoi(yytext);
+ return(SECONDS);
+ }
+infinity {
+ return(INFINITY);
+ }
+
+on {
+ yylval.num = 1;
+ return(ON);
+ }
+off {
+ yylval.num = 0;
+ return(OFF);
+ }
+
+ /* basic rules */
+{ws} ;
+{nl} {
+ lineno++;
+ }
+{semi} {
+ return EOS;
+ }
+{bcl} {
+ return BCL;
+ }
+{ecl} {
+ return ECL;
+ }
+{qstring} {
+ yylval.cs.cp = yytext;
+ yylval.cs.len = yyleng;
+ return QSTRING;
+ }
+{decstring} {
+ yylval.cs.cp = yytext;
+ yylval.cs.len = yyleng;
+ return DECSTRING;
+ }
+{name} {
+ yylval.cs.cp = yytext;
+ yylval.cs.len = yyleng;
+ return NAME;
+ }
+{ipv4addr} {
+ memset(&yylval.addr4, 0, sizeof(struct in_addr));
+ if (inet_pton(AF_INET, yytext,
+ &yylval.addr4) == 1) {
+ return IPV4ADDR;
+ } else {
+ return ERROR;
+ }
+ }
+{ipv6addr} {
+ memset(&yylval.addr6, 0, sizeof(struct in6_addr));
+ if (inet_pton(AF_INET6, yytext,
+ &yylval.addr6) == 1) {
+ return IPV6ADDR;
+ } else {
+ return ERROR;
+ }
+ }
+{ipaddrmask} {
+ yytext++;
+ yylval.num = atoi(yytext);
+ return(PREFIXLEN);
+ }
+{hostname} {
+ yylval.cs.cp = yytext;
+ yylval.cs.len = yyleng;
+ return HOSTNAME;
+ }
+%%
+
+int parse(FILE **fp)
+{
+ extern int yyparse __P((void));
+
+ yyin = *fp;
+
+ if (yyparse())
+ return(-1);
+
+ return(0);
+
+}
+
+void
+yyerror(const char *s)
+{
+ printf("%s: at %s in line %d\n", s, yytext, lineno);
+}
diff --git a/usr.sbin/rrenumd/parser.y b/usr.sbin/rrenumd/parser.y
new file mode 100644
index 0000000..0cfe3b5
--- /dev/null
+++ b/usr.sbin/rrenumd/parser.y
@@ -0,0 +1,675 @@
+/* $KAME: parser.y,v 1.8 2000/11/08 03:03:34 jinmei Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+%{
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/queue.h>
+
+#include <net/if.h>
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+#include <net/if_var.h>
+#endif /* __FreeBSD__ >= 3 */
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/icmp6.h>
+
+#include <netdb.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "rrenumd.h"
+
+struct config_is_set {
+ u_short cis_dest : 1;
+} cis;
+
+struct dst_list *dl_head;
+struct payload_list *pl_head, ple_cur;
+u_int retry;
+char errbuf[LINE_MAX];
+
+extern int lineno;
+extern void yyerror __P((const char *s));
+extern int yylex __P((void));
+static struct payload_list * pllist_lookup __P((int seqnum));
+static void pllist_enqueue __P((struct payload_list *pl_entry));
+
+#define MAX_RETRYNUM 10 /* upper limit of retry in this rrenumd program */
+#define MAX_SEQNUM 256 /* upper limit of seqnum in this rrenumd program */
+#define NOSPEC -1
+
+%}
+
+%union {
+ u_long num;
+ struct {
+ char *cp;
+ int len;
+ } cs;
+ struct in_addr addr4;
+ struct in6_addr addr6;
+ struct {
+ struct in6_addr addr;
+ u_char plen;
+ } prefix;
+ struct dst_list *dl;
+ struct payload_list *pl;
+ struct sockaddr *sa;
+}
+
+%token <num> ADD CHANGE SETGLOBAL
+%token DEBUG_CMD DEST_CMD RETRY_CMD SEQNUM_CMD
+%token MATCH_PREFIX_CMD MAXLEN_CMD MINLEN_CMD
+%token USE_PREFIX_CMD KEEPLEN_CMD
+%token VLTIME_CMD PLTIME_CMD
+%token RAF_ONLINK_CMD RAF_AUTO_CMD RAF_DECRVALID_CMD RAF_DECRPREFD_CMD
+%token <num> DAYS HOURS MINUTES SECONDS INFINITY
+%token <num> ON OFF
+%token BCL ECL EOS ERROR
+%token <cs> NAME HOSTNAME QSTRING DECSTRING
+%token <addr4> IPV4ADDR
+%token <addr6> IPV6ADDR
+%token <num> PREFIXLEN
+
+%type <num> retrynum seqnum rrenum_cmd
+%type <num> prefixlen maxlen minlen keeplen vltime pltime
+%type <num> lifetime days hours minutes seconds
+%type <num> decstring
+%type <num> raf_onlink raf_auto raf_decrvalid raf_decrprefd flag
+%type <dl> dest_addrs dest_addr sin sin6
+%type <pl> rrenum_statement
+%type <cs> ifname
+%type <prefix> prefixval
+
+%%
+config:
+ /* empty */
+ | statements
+ ;
+
+statements:
+ statement
+ | statements statement
+ ;
+
+statement:
+ debug_statement
+ | destination_statement
+ | rrenum_statement_without_seqnum
+ | rrenum_statement_with_seqnum
+ | error EOS
+ {
+ yyerrok;
+ }
+ | EOS
+ ;
+
+debug_statement:
+ DEBUG_CMD flag EOS
+ {
+#ifdef YYDEBUG
+ yydebug = $2;
+#endif /* YYDEBUG */
+ }
+ ;
+
+destination_statement:
+ DEST_CMD dest_addrs retrynum EOS
+ {
+ dl_head = $2;
+ retry = $3;
+ }
+ ;
+
+dest_addrs:
+ dest_addr
+ | dest_addrs dest_addr
+ {
+ $2->dl_next = $1;
+ $$ = $2;
+ }
+ ;
+
+dest_addr :
+ sin
+ {
+ with_v4dest = 1;
+ }
+ | sin6
+ {
+ with_v6dest = 1;
+ }
+ | sin6 ifname
+ {
+ struct sockaddr_in6 *sin6;
+
+ sin6 = (struct sockaddr_in6 *)$1->dl_dst;
+ sin6->sin6_scope_id = if_nametoindex($2.cp);
+ with_v6dest = 1;
+ $$ = $1;
+ }
+ | HOSTNAME
+ {
+ struct sockaddr_storage *ss;
+ struct addrinfo hints, *res;
+ int error;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_CANONNAME;
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_RAW;
+ hints.ai_protocol = 0;
+ error = getaddrinfo($1.cp, 0, &hints, &res);
+ if (error) {
+ snprintf(errbuf, sizeof(errbuf),
+ "name resolution failed for %s:%s",
+ $1.cp, gai_strerror(error));
+ yyerror(errbuf);
+ }
+ ss = (struct sockaddr_storage *)malloc(sizeof(*ss));
+ memset(ss, 0, sizeof(*ss));
+ memcpy(ss, res->ai_addr, res->ai_addr->sa_len);
+ freeaddrinfo(res);
+
+ $$ = (struct dst_list *)
+ malloc(sizeof(struct dst_list));
+ memset($$, 0, sizeof(struct dst_list));
+ $$->dl_dst = (struct sockaddr *)ss;
+ }
+ ;
+
+sin:
+ IPV4ADDR
+ {
+ struct sockaddr_in *sin;
+
+ sin = (struct sockaddr_in *)malloc(sizeof(*sin));
+ memset(sin, 0, sizeof(*sin));
+ sin->sin_len = sizeof(*sin);
+ sin->sin_family = AF_INET;
+ sin->sin_addr = $1;
+
+ $$ = (struct dst_list *)
+ malloc(sizeof(struct dst_list));
+ memset($$, 0, sizeof(struct dst_list));
+ $$->dl_dst = (struct sockaddr *)sin;
+ }
+ ;
+
+sin6:
+ IPV6ADDR
+ {
+ struct sockaddr_in6 *sin6;
+
+ sin6 = (struct sockaddr_in6 *)malloc(sizeof(*sin6));
+ memset(sin6, 0, sizeof(*sin6));
+ sin6->sin6_len = sizeof(*sin6);
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_addr = $1;
+
+ $$ = (struct dst_list *)
+ malloc(sizeof(struct dst_list));
+ memset($$, 0, sizeof(struct dst_list));
+ $$->dl_dst = (struct sockaddr *)sin6;
+ }
+
+ifname:
+ NAME
+ {
+ $$.cp = strdup($1.cp);
+ $$.len = $1.len;
+ }
+ | QSTRING
+ {
+ $1.cp[$1.len - 1] = 0;
+ $$.cp = strdup(&$1.cp[1]);
+ $$.len = $1.len - 2;
+ }
+ ;
+
+retrynum:
+ /* empty */
+ {
+ $$ = 2;
+ }
+ | RETRY_CMD decstring
+ {
+ if ($2 > MAX_RETRYNUM)
+ $2 = MAX_RETRYNUM;
+ $$ = $2;
+ }
+ ;
+
+rrenum_statement_with_seqnum:
+ SEQNUM_CMD seqnum
+ {
+ if (pllist_lookup($2)) {
+ snprintf(errbuf, sizeof(errbuf),
+ "duplicate seqnum %ld specified at %d",
+ $2, lineno);
+ yyerror(errbuf);
+ }
+ }
+ BCL rrenum_statement EOS ECL EOS
+ {
+ $5->pl_irr.rr_seqnum = $2;
+ pllist_enqueue($5);
+ }
+ ;
+
+seqnum:
+ /* empty */
+ {
+ $$ = 0;
+ }
+ | decstring
+ {
+ if ($1 > MAX_SEQNUM) {
+ snprintf(errbuf, sizeof(errbuf),
+ "seqnum %ld is illegal for this program. "
+ "should be between 0 and %d",
+ $1, MAX_SEQNUM);
+ yyerror(errbuf);
+ }
+ $$ = $1;
+ }
+ ;
+
+rrenum_statement_without_seqnum:
+ rrenum_statement EOS
+ {
+ if (pllist_lookup(0)) {
+ snprintf(errbuf, sizeof(errbuf),
+ "duplicate seqnum %d specified at %d",
+ 0, lineno);
+ yyerror(errbuf);
+ }
+ $1->pl_irr.rr_seqnum = 0;
+ pllist_enqueue($1);
+ }
+ ;
+
+rrenum_statement:
+ match_prefix_definition use_prefix_definition
+ {
+ $$ = (struct payload_list *)
+ malloc(sizeof(struct payload_list));
+ memcpy($$, &ple_cur, sizeof(ple_cur));
+ }
+ ;
+
+match_prefix_definition:
+ rrenum_cmd MATCH_PREFIX_CMD prefixval maxlen minlen
+ {
+ struct icmp6_router_renum *irr;
+ struct rr_pco_match *rpm;
+
+ irr = (struct icmp6_router_renum *)&ple_cur.pl_irr;
+ rpm = (struct rr_pco_match *)(irr + 1);
+ memset(rpm, 0, sizeof(*rpm));
+
+ rpm->rpm_code = $1;
+ rpm->rpm_prefix = $3.addr;
+ rpm->rpm_matchlen = $3.plen;
+ rpm->rpm_maxlen = $4;
+ rpm->rpm_minlen = $5;
+ }
+ ;
+
+rrenum_cmd:
+ /* empty */
+ {
+ $$ = RPM_PCO_ADD;
+ }
+ | ADD
+ | CHANGE
+ | SETGLOBAL
+ ;
+
+prefixval:
+ IPV6ADDR prefixlen
+ {
+ $$.addr = $1;
+ $$.plen = $2;
+ }
+ ;
+
+prefixlen:
+ /* empty */
+ {
+ $$ = 64;
+ }
+ | PREFIXLEN
+ ;
+
+maxlen:
+ /* empty */
+ {
+ $$ = 128;
+ }
+ | MAXLEN_CMD decstring
+ {
+ if ($2 > 128)
+ $2 = 128;
+ $$ = $2;
+ }
+ ;
+
+minlen:
+ /* empty */
+ {
+ $$ = 0;
+ }
+ | MINLEN_CMD decstring
+ {
+ if ($2 > 128)
+ $2 = 128;
+ $$ = $2;
+ }
+ ;
+
+use_prefix_definition:
+ /* empty */
+ {
+ struct icmp6_router_renum *irr;
+ struct rr_pco_match *rpm;
+ struct rr_pco_use *rpu;
+
+ irr = (struct icmp6_router_renum *)&ple_cur.pl_irr;
+ rpm = (struct rr_pco_match *)(irr + 1);
+ rpu = (struct rr_pco_use *)(rpm + 1);
+ memset(rpu, 0, sizeof(*rpu));
+ }
+ | USE_PREFIX_CMD prefixval keeplen use_prefix_values
+ {
+ struct icmp6_router_renum *irr;
+ struct rr_pco_match *rpm;
+ struct rr_pco_use *rpu;
+
+ irr = (struct icmp6_router_renum *)&ple_cur.pl_irr;
+ rpm = (struct rr_pco_match *)(irr + 1);
+ rpu = (struct rr_pco_use *)(rpm + 1);
+
+ rpu->rpu_prefix = $2.addr;
+ rpu->rpu_uselen = $2.plen;
+ rpu->rpu_keeplen = $3;
+ }
+ ;
+
+use_prefix_values:
+ /* empty */
+ {
+ struct icmp6_router_renum *irr;
+ struct rr_pco_match *rpm;
+ struct rr_pco_use *rpu;
+
+ irr = (struct icmp6_router_renum *)&ple_cur.pl_irr;
+ rpm = (struct rr_pco_match *)(irr + 1);
+ rpu = (struct rr_pco_use *)(rpm + 1);
+ memset(rpu, 0, sizeof(*rpu));
+
+ rpu->rpu_vltime = htonl(DEF_VLTIME);
+ rpu->rpu_pltime = htonl(DEF_PLTIME);
+ rpu->rpu_ramask = 0;
+ rpu->rpu_flags = 0;
+ }
+ | BCL vltime pltime raf_onlink raf_auto raf_decrvalid raf_decrprefd ECL
+ {
+ struct icmp6_router_renum *irr;
+ struct rr_pco_match *rpm;
+ struct rr_pco_use *rpu;
+
+ irr = (struct icmp6_router_renum *)&ple_cur.pl_irr;
+ rpm = (struct rr_pco_match *)(irr + 1);
+ rpu = (struct rr_pco_use *)(rpm + 1);
+ memset(rpu, 0, sizeof(*rpu));
+
+ rpu->rpu_vltime = $2;
+ rpu->rpu_pltime = $3;
+ if ($4 == NOSPEC) {
+ rpu->rpu_ramask &=
+ ~ICMP6_RR_PCOUSE_RAFLAGS_ONLINK;
+ } else {
+ rpu->rpu_ramask |=
+ ICMP6_RR_PCOUSE_RAFLAGS_ONLINK;
+ if ($4 == ON) {
+ rpu->rpu_raflags |=
+ ICMP6_RR_PCOUSE_RAFLAGS_ONLINK;
+ } else {
+ rpu->rpu_raflags &=
+ ~ICMP6_RR_PCOUSE_RAFLAGS_ONLINK;
+ }
+ }
+ if ($5 == NOSPEC) {
+ rpu->rpu_ramask &=
+ ICMP6_RR_PCOUSE_RAFLAGS_AUTO;
+ } else {
+ rpu->rpu_ramask |=
+ ICMP6_RR_PCOUSE_RAFLAGS_AUTO;
+ if ($5 == ON) {
+ rpu->rpu_raflags |=
+ ICMP6_RR_PCOUSE_RAFLAGS_AUTO;
+ } else {
+ rpu->rpu_raflags &=
+ ~ICMP6_RR_PCOUSE_RAFLAGS_AUTO;
+ }
+ }
+ rpu->rpu_flags = 0;
+ if ($6 == ON) {
+ rpu->rpu_flags |=
+ ICMP6_RR_PCOUSE_FLAGS_DECRVLTIME;
+ }
+ if ($7 == ON) {
+ rpu->rpu_flags |=
+ ICMP6_RR_PCOUSE_FLAGS_DECRPLTIME;
+ }
+ }
+ ;
+
+keeplen:
+ /* empty */
+ {
+ $$ = 0;
+ }
+ | KEEPLEN_CMD decstring
+ {
+ if ($2 > 128)
+ $2 = 128;
+ $$ = $2;
+ }
+ ;
+
+
+vltime:
+ /* empty */
+ {
+ $$ = htonl(DEF_VLTIME);
+ }
+ | VLTIME_CMD lifetime
+ {
+ $$ = htonl($2);
+ }
+ ;
+
+pltime:
+ /* empty */
+ {
+ $$ = htonl(DEF_PLTIME);
+ }
+ | PLTIME_CMD lifetime
+ {
+ $$ = htonl($2);
+ }
+
+raf_onlink:
+ /* empty */
+ {
+ $$ = NOSPEC;
+ }
+ | RAF_ONLINK_CMD flag
+ {
+ $$ = $2;
+ }
+ ;
+
+raf_auto:
+ /* empty */
+ {
+ $$ = NOSPEC;
+ }
+ | RAF_AUTO_CMD flag
+ {
+ $$ = $2;
+ }
+ ;
+
+raf_decrvalid:
+ /* empty */
+ {
+ $$ = NOSPEC;
+ }
+ | RAF_DECRVALID_CMD flag
+ {
+ $$ = $2;
+ }
+ ;
+
+raf_decrprefd:
+ /* empty */
+ {
+ $$ = NOSPEC;
+ }
+ | RAF_DECRPREFD_CMD flag
+ {
+ $$ = $2;
+ }
+ ;
+
+flag:
+ ON { $$ = ON; }
+ | OFF { $$ = OFF; }
+ ;
+
+lifetime:
+ decstring
+ | INFINITY
+ {
+ $$ = 0xffffffff;
+ }
+ | days hours minutes seconds
+ {
+ int d, h, m, s;
+
+ d = $1 * 24 * 60 * 60;
+ h = $2 * 60 * 60;
+ m = $3 * 60;
+ s = $4;
+ $$ = d + h + m + s;
+ }
+ ;
+
+days:
+ /* empty */
+ {
+ $$ = 0;
+ }
+ | DAYS
+ ;
+
+hours:
+ /* empty */
+ {
+ $$ = 0;
+ }
+ | HOURS
+ ;
+
+minutes:
+ /* empty */
+ {
+ $$ = 0;
+ }
+ | MINUTES
+ ;
+
+seconds:
+ /* empty */
+ {
+ $$ = 0;
+ }
+ | SECONDS
+ ;
+
+decstring:
+ DECSTRING
+ {
+ int dval;
+
+ dval = atoi($1.cp);
+ $$ = dval;
+ }
+ ;
+
+%%
+
+static struct payload_list *
+pllist_lookup(int seqnum)
+{
+ struct payload_list *pl;
+ for (pl = pl_head; pl && pl->pl_irr.rr_seqnum != seqnum;
+ pl = pl->pl_next)
+ continue;
+ return (pl);
+}
+
+static void
+pllist_enqueue(struct payload_list *pl_entry)
+{
+ struct payload_list *pl, *pl_last;
+
+ pl_last = NULL;
+ for (pl = pl_head;
+ pl && pl->pl_irr.rr_seqnum < pl_entry->pl_irr.rr_seqnum;
+ pl_last = pl, pl = pl->pl_next)
+ continue;
+ if (pl_last)
+ pl_last->pl_next = pl_entry;
+ else
+ pl_head = pl_entry;
+
+ return;
+}
diff --git a/usr.sbin/rrenumd/rrenumd.8 b/usr.sbin/rrenumd/rrenumd.8
new file mode 100644
index 0000000..09e3089
--- /dev/null
+++ b/usr.sbin/rrenumd/rrenumd.8
@@ -0,0 +1,102 @@
+.\" $KAME: rrenumd.8,v 1.6 2001/01/22 02:06:24 itojun Exp $
+.\"
+.\" Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the project nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd September 7, 1998
+.Dt RRENUMD 8
+.Os
+.Sh NAME
+.Nm rrenumd
+.Nd router renumbering daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl df
+.Oo
+.Fl c Ar conf_file | Fl s
+.Oc
+.Sh DESCRIPTION
+.Nm
+transmits router renumbering request packets,
+to renumber the routers in the site network.
+.Pp
+On KAME-based systems,
+router renumbering requests are received and processed by
+.Xr rtadvd 8 .
+For other systems, refer to relevant documents.
+.Pp
+The program will daemonize itself on invocation.
+It reads configuration information from standard input if
+.Fl s
+is specified, or from
+.Ar conf_file
+if
+.Fl c Ar conf_file
+is specified.
+.Pp
+The contents of configuration information are described in
+.Xr rrenumd.conf 5 .
+.Pp
+After successful configuration,
+.Nm
+sends router renumbering
+messages periodically to configured destinations.
+Messages contain prefixes configured to be renumbered.
+.Bl -tag -width indent
+.\"
+.It Fl d
+Debug mode.
+.It Fl f
+Foreground mode.
+Do not become daemon.
+.It Fl s
+Script mode.
+Configuration information is obtained from standard input.
+.It Fl c Ar conf_file
+Specify a configuration file where configuration information is kept.
+.El
+.Sh RETURN VALUES
+The program exits with 0 on success, and non-zero on failures.
+.Sh SEE ALSO
+.Xr rrenumd.conf 5 ,
+.Xr rtadvd 8
+.Sh STANDARDS
+.Rs
+.%A Matt Crawford
+.%R RFC
+.%N 2894
+.%D August 2000
+.%T "Router Renumbering for IPv6"
+.Re
+.Sh HISTORY
+The
+.Nm
+command first appeared in KAME IPv6 protocol stack kit.
+.\" .Sh BUGS
+.\" (to be written)
diff --git a/usr.sbin/rrenumd/rrenumd.c b/usr.sbin/rrenumd/rrenumd.c
new file mode 100644
index 0000000..e166789
--- /dev/null
+++ b/usr.sbin/rrenumd/rrenumd.c
@@ -0,0 +1,663 @@
+/* $KAME: rrenumd.c,v 1.20 2000/11/08 02:40:53 itojun Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/time.h>
+
+#include <string.h>
+
+#include <net/route.h>
+
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+
+#include <arpa/inet.h>
+
+#ifdef IPSEC
+#include <netinet6/ipsec.h>
+#endif
+
+#include <stdio.h>
+#include <err.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+
+#include "rrenumd.h"
+
+#define LL_ALLROUTERS "ff02::2"
+#define SL_ALLROUTERS "ff05::2"
+
+#define RR_MCHLIM_DEFAULT 64
+
+#ifndef IN6_IS_SCOPE_LINKLOCAL
+#define IN6_IS_SCOPE_LINKLOCAL(a) \
+ ((IN6_IS_ADDR_LINKLOCAL(a)) || \
+ (IN6_IS_ADDR_MC_LINKLOCAL(a)))
+#endif /* IN6_IS_SCOPE_LINKLOCAL */
+
+struct flags {
+ u_long debug : 1;
+ u_long fg : 1;
+#ifdef IPSEC
+#ifdef IPSEC_POLICY_IPSEC
+ u_long policy : 1;
+#else /* IPSEC_POLICY_IPSEC */
+ u_long auth : 1;
+ u_long encrypt : 1;
+#endif /* IPSEC_POLICY_IPSEC */
+#endif /*IPSEC*/
+};
+
+struct msghdr sndmhdr;
+struct msghdr rcvmhdr;
+struct sockaddr_in6 from;
+struct sockaddr_in6 sin6_ll_allrouters;
+
+int s4, s6;
+int with_v4dest, with_v6dest;
+struct in6_addr prefix; /* ADHOC */
+int prefixlen = 64; /* ADHOC */
+
+extern int parse __P((FILE **));
+
+static void show_usage __P((void));
+static void init_sin6 __P((struct sockaddr_in6 *, const char *));
+#if 0
+static void join_multi __P((const char *));
+#endif
+static void init_globals __P((void));
+static void config __P((FILE **));
+#ifdef IPSEC_POLICY_IPSEC
+static void sock6_open __P((struct flags *, char *));
+static void sock4_open __P((struct flags *, char *));
+#else
+static void sock6_open __P((struct flags *));
+static void sock4_open __P((struct flags *));
+#endif
+static void rrenum_output __P((struct payload_list *, struct dst_list *));
+static void rrenum_snd_eachdst __P((struct payload_list *));
+#if 0
+static void rrenum_snd_fullsequence __P((void));
+#endif
+static void rrenum_input __P((int));
+int main __P((int, char *[]));
+
+
+/* Print usage. Don't call this after daemonized. */
+static void
+show_usage()
+{
+ fprintf(stderr, "usage: rrenumd [-c conf_file|-s] [-df"
+#ifdef IPSEC
+#ifdef IPSEC_POLICY_IPSEC
+ "] [-P policy"
+#else /* IPSEC_POLICY_IPSEC */
+ "AE"
+#endif /* IPSEC_POLICY_IPSEC */
+#endif /* IPSEC */
+ "]\n");
+ exit(1);
+}
+
+static void
+init_sin6(struct sockaddr_in6 *sin6, const char *addr_ascii)
+{
+ memset(sin6, 0, sizeof(*sin6));
+ sin6->sin6_len = sizeof(*sin6);
+ sin6->sin6_family = AF_INET6;
+ if (inet_pton(AF_INET6, addr_ascii, &sin6->sin6_addr) != 1)
+ ; /* XXX do something */
+}
+
+#if 0 /* XXX: not necessary ?? */
+static void
+join_multi(const char *addrname)
+{
+ struct ipv6_mreq mreq;
+
+ if (inet_pton(AF_INET6, addrname, &mreq.ipv6mr_multiaddr.s6_addr)
+ != 1) {
+ syslog(LOG_ERR, "<%s> inet_pton failed(library bug?)",
+ __FUNCTION__);
+ exit(1);
+ }
+ /* ADHOC: currently join only one */
+ {
+ if ((mreq.ipv6mr_interface = if_nametoindex(ifname)) == 0) {
+ syslog(LOG_ERR, "<%s> ifname %s should be invalid: %s",
+ __FUNCTION__, ifname, strerror(errno));
+ exit(1);
+ }
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP,
+ &mreq,
+ sizeof(mreq)) < 0) {
+ syslog(LOG_ERR, "<%s> IPV6_JOIN_GROUP on %s: %s",
+ __FUNCTION__, ifname, strerror(errno));
+ exit(1);
+ }
+ }
+}
+#endif
+
+static void
+init_globals()
+{
+ static struct iovec rcviov;
+ static u_char rprdata[4500]; /* maximal MTU of connected links */
+ static u_char *rcvcmsgbuf = NULL;
+ static u_char *sndcmsgbuf = NULL;
+ int sndcmsglen, rcvcmsglen;
+
+ /* init ll_allrouters */
+ init_sin6(&sin6_ll_allrouters, LL_ALLROUTERS);
+
+ /* initialize msghdr for receiving packets */
+ rcviov.iov_base = (caddr_t)rprdata;
+ rcviov.iov_len = sizeof(rprdata);
+ rcvmhdr.msg_namelen = sizeof(struct sockaddr_in6);
+ rcvmhdr.msg_iov = &rcviov;
+ rcvmhdr.msg_iovlen = 1;
+ rcvcmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) +
+ CMSG_SPACE(sizeof(int));
+ if (rcvcmsgbuf == NULL &&
+ (rcvcmsgbuf = (u_char *)malloc(rcvcmsglen)) == NULL) {
+ syslog(LOG_ERR, "<%s>: malloc failed", __FUNCTION__);
+ exit(1);
+ }
+ rcvmhdr.msg_control = (caddr_t)rcvcmsgbuf;
+ rcvmhdr.msg_controllen = rcvcmsglen;
+
+ /* initialize msghdr for sending packets */
+ sndmhdr.msg_namelen = sizeof(struct sockaddr_in6);
+ sndmhdr.msg_iovlen = 1;
+ sndcmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) +
+ CMSG_SPACE(sizeof(int));
+ if (sndcmsgbuf == NULL &&
+ (sndcmsgbuf = (u_char *)malloc(sndcmsglen)) == NULL) {
+ syslog(LOG_ERR, "<%s>: malloc failed", __FUNCTION__);
+ exit(1);
+ }
+ sndmhdr.msg_control = (caddr_t)sndcmsgbuf;
+ sndmhdr.msg_controllen = sndcmsglen;
+}
+
+static void
+config(FILE **fpp)
+{
+ struct payload_list *pl;
+ struct iovec *iov;
+ struct icmp6_router_renum *irr;
+ struct rr_pco_match *rpm;
+
+ if (parse(fpp) < 0) {
+ syslog(LOG_ERR, "<%s> parse failed", __FUNCTION__);
+ exit(1);
+ }
+
+ /* initialize fields not configured by parser */
+ for (pl = pl_head; pl; pl = pl->pl_next) {
+ iov = (struct iovec *)&pl->pl_sndiov;
+ irr = (struct icmp6_router_renum *)&pl->pl_irr;
+ rpm = (struct rr_pco_match *)&pl->pl_rpm;
+
+ irr->rr_type = ICMP6_ROUTER_RENUMBERING;
+ irr->rr_code = 0;
+ /*
+ * now we don't support multiple PCOs in a rr message.
+ * so segment number is not supported.
+ */
+ /* TODO: rr flags config in parser */
+ irr->rr_flags |= ICMP6_RR_FLAGS_SPECSITE;
+ /* TODO: max delay config in parser */
+
+ /*
+ * means only 1 use_prefix is contained as router-renum-05.txt.
+ * now we don't support multiple PCOs in a rr message,
+ * nor multiple use_prefix in one PCO.
+ */
+ rpm->rpm_len = 4*1 +3;
+ rpm->rpm_ordinal = 0;
+ iov->iov_base = (caddr_t)irr;
+ iov->iov_len = sizeof(struct icmp6_router_renum)
+ + sizeof(struct rr_pco_match)
+ + sizeof(struct rr_pco_use);
+ }
+}
+
+static void
+sock6_open(struct flags *flags
+#ifdef IPSEC_POLICY_IPSEC
+ , char *policy
+#endif /* IPSEC_POLICY_IPSEC */
+ )
+{
+ struct icmp6_filter filt;
+ int on;
+#ifdef IPSEC
+#ifndef IPSEC_POLICY_IPSEC
+ int optval;
+#endif
+#endif
+
+ if (with_v6dest == 0)
+ return;
+ if (with_v6dest &&
+ (s6 = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) {
+ syslog(LOG_ERR, "<%s> socket(v6): %s", __FUNCTION__,
+ strerror(errno));
+ exit(1);
+ }
+
+ /*
+ * join all routers multicast addresses.
+ */
+#if 0 /* XXX: not necessary ?? */
+ join_multi(LL_ALLROUTERS);
+ join_multi(SL_ALLROUTERS);
+#endif
+
+ /* set icmpv6 filter */
+ ICMP6_FILTER_SETBLOCKALL(&filt);
+ ICMP6_FILTER_SETPASS(ICMP6_ROUTER_RENUMBERING, &filt);
+ if (setsockopt(s6, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
+ sizeof(filt)) < 0) {
+ syslog(LOG_ERR, "<%s> IICMP6_FILTER: %s",
+ __FUNCTION__, strerror(errno));
+ exit(1);
+ }
+
+ /* specify to tell receiving interface */
+ on = 1;
+ if (setsockopt(s6, IPPROTO_IPV6, IPV6_PKTINFO, &on,
+ sizeof(on)) < 0) {
+ syslog(LOG_ERR, "<%s> IPV6_PKTINFO: %s",
+ __FUNCTION__, strerror(errno));
+ exit(1);
+ }
+
+#ifdef IPSEC
+#ifdef IPSEC_POLICY_IPSEC
+ if (flags->policy) {
+ char *buf;
+ buf = ipsec_set_policy(policy, strlen(policy));
+ if (buf == NULL)
+ errx(1, "%s", ipsec_strerror());
+ /* XXX should handle in/out bound policy. */
+ if (setsockopt(s6, IPPROTO_IPV6, IPV6_IPSEC_POLICY,
+ buf, ipsec_get_policylen(buf)) < 0)
+ err(1, "setsockopt(IPV6_IPSEC_POLICY)");
+ free(buf);
+ }
+#else /* IPSEC_POLICY_IPSEC */
+ if (flags->auth) {
+ optval = IPSEC_LEVEL_REQUIRE;
+ if (setsockopt(s6, IPPROTO_IPV6, IPV6_AUTH_TRANS_LEVEL,
+ &optval, sizeof(optval)) == -1) {
+ syslog(LOG_ERR, "<%s> IPV6_AUTH_TRANS_LEVEL: %s",
+ __FUNCTION__, strerror(errno));
+ exit(1);
+ }
+ }
+ if (flags->encrypt) {
+ optval = IPSEC_LEVEL_REQUIRE;
+ if (setsockopt(s6, IPPROTO_IPV6, IPV6_ESP_TRANS_LEVEL,
+ &optval, sizeof(optval)) == -1) {
+ syslog(LOG_ERR, "<%s> IPV6_ESP_TRANS_LEVEL: %s",
+ __FUNCTION__, strerror(errno));
+ exit(1);
+ }
+ }
+#endif /* IPSEC_POLICY_IPSEC */
+#endif /* IPSEC */
+
+ return;
+}
+
+static void
+sock4_open(struct flags *flags
+#ifdef IPSEC_POLICY_IPSEC
+ , char *policy
+#endif /* IPSEC_POLICY_IPSEC */
+ )
+{
+#ifdef IPSEC
+#ifndef IPSEC_POLICY_IPSEC
+ int optval;
+#endif
+#endif
+
+ if (with_v4dest == 0)
+ return;
+ if ((s4 = socket(AF_INET, SOCK_RAW, IPPROTO_ICMPV6)) < 0) {
+ syslog(LOG_ERR, "<%s> socket(v4): %s", __FUNCTION__,
+ strerror(errno));
+ exit(1);
+ }
+
+#if 0 /* XXX: not necessary ?? */
+ /*
+ * join all routers multicast addresses.
+ */
+ some_join_function();
+#endif
+
+#ifdef IPSEC
+#ifdef IPSEC_POLICY_IPSEC
+ if (flags->policy) {
+ char *buf;
+ buf = ipsec_set_policy(policy, strlen(policy));
+ if (buf == NULL)
+ errx(1, "%s", ipsec_strerror());
+ /* XXX should handle in/out bound policy. */
+ if (setsockopt(s4, IPPROTO_IP, IP_IPSEC_POLICY,
+ buf, ipsec_get_policylen(buf)) < 0)
+ err(1, "setsockopt(IP_IPSEC_POLICY)");
+ free(buf);
+ }
+#else /* IPSEC_POLICY_IPSEC */
+ if (flags->auth) {
+ optval = IPSEC_LEVEL_REQUIRE;
+ if (setsockopt(s4, IPPROTO_IP, IP_AUTH_TRANS_LEVEL,
+ &optval, sizeof(optval)) == -1) {
+ syslog(LOG_ERR, "<%s> IP_AUTH_TRANS_LEVEL: %s",
+ __FUNCTION__, strerror(errno));
+ exit(1);
+ }
+ }
+ if (flags->encrypt) {
+ optval = IPSEC_LEVEL_REQUIRE;
+ if (setsockopt(s4, IPPROTO_IP, IP_ESP_TRANS_LEVEL,
+ &optval, sizeof(optval)) == -1) {
+ syslog(LOG_ERR, "<%s> IP_ESP_TRANS_LEVEL: %s",
+ __FUNCTION__, strerror(errno));
+ exit(1);
+ }
+ }
+#endif /* IPSEC_POLICY_IPSEC */
+#endif /* IPSEC */
+
+ return;
+}
+
+static void
+rrenum_output(struct payload_list *pl, struct dst_list *dl)
+{
+ int i, msglen = 0;
+ struct cmsghdr *cm;
+ struct in6_pktinfo *pi;
+ struct sockaddr_in6 *sin6 = NULL;
+
+ sndmhdr.msg_name = (caddr_t)dl->dl_dst;
+ if (dl->dl_dst->sa_family == AF_INET6)
+ sin6 = (struct sockaddr_in6 *)dl->dl_dst;
+
+ if (sin6 != NULL &&
+ IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) {
+ int hoplimit = RR_MCHLIM_DEFAULT;
+
+ cm = CMSG_FIRSTHDR(&sndmhdr);
+ /* specify the outgoing interface */
+ cm->cmsg_level = IPPROTO_IPV6;
+ cm->cmsg_type = IPV6_PKTINFO;
+ cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
+ pi = (struct in6_pktinfo *)CMSG_DATA(cm);
+ memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); /*XXX*/
+ pi->ipi6_ifindex = sin6->sin6_scope_id;
+ msglen += CMSG_LEN(sizeof(struct in6_pktinfo));
+
+ /* specify the hop limit of the packet if dest is link local */
+ /* not defined by router-renum-05.txt, but maybe its OK */
+ cm = CMSG_NXTHDR(&sndmhdr, cm);
+ cm->cmsg_level = IPPROTO_IPV6;
+ cm->cmsg_type = IPV6_HOPLIMIT;
+ cm->cmsg_len = CMSG_LEN(sizeof(int));
+ memcpy(CMSG_DATA(cm), &hoplimit, sizeof(int));
+ msglen += CMSG_LEN(sizeof(int));
+ }
+ sndmhdr.msg_controllen = msglen;
+ if (sndmhdr.msg_controllen == 0)
+ sndmhdr.msg_control = 0;
+
+ sndmhdr.msg_iov = &pl->pl_sndiov;
+ i = sendmsg(dl->dl_dst->sa_family == AF_INET ? s4 : s6, &sndmhdr, 0);
+
+ if (i < 0 || i != sndmhdr.msg_iov->iov_len)
+ syslog(LOG_ERR, "<%s> sendmsg: %s", __FUNCTION__,
+ strerror(errno));
+}
+
+static void
+rrenum_snd_eachdst(struct payload_list *pl)
+{
+ struct dst_list *dl;
+
+ for (dl = dl_head; dl; dl = dl->dl_next) {
+ rrenum_output(pl, dl);
+ }
+}
+
+#if 0
+static void
+rrenum_snd_fullsequence()
+{
+ struct payload_list *pl;
+
+ for (pl = pl_head; pl; pl = pl->pl_next) {
+ rrenum_snd_eachdst(pl);
+ }
+}
+#endif
+
+static void
+rrenum_input(int s)
+{
+ int i;
+ struct icmp6_router_renum *rr;
+
+ /* get message */
+ if ((i = recvmsg(s, &rcvmhdr, 0)) < 0) {
+ syslog(LOG_ERR, "<%s> recvmsg: %s", __FUNCTION__,
+ strerror(errno));
+ return;
+ }
+ if (s == s4)
+ i -= sizeof(struct ip);
+ if (i < sizeof(struct icmp6_router_renum)) {
+ syslog(LOG_ERR, "<%s> packet size(%d) is too short",
+ __FUNCTION__, i);
+ return;
+ }
+ if (s == s4) {
+ struct ip *ip = (struct ip *)rcvmhdr.msg_iov->iov_base;
+
+ rr = (struct icmp6_router_renum *)(ip + 1);
+ } else /* s == s6 */
+ rr = (struct icmp6_router_renum *)rcvmhdr.msg_iov->iov_base;
+
+ switch(rr->rr_code) {
+ case ICMP6_ROUTER_RENUMBERING_COMMAND:
+ /* COMMAND will be processed by rtadvd */
+ break;
+ case ICMP6_ROUTER_RENUMBERING_RESULT:
+ /* TODO: receiving result message */
+ break;
+ default:
+ syslog(LOG_ERR, "<%s> received unknown code %d",
+ __FUNCTION__, rr->rr_code);
+ break;
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ FILE *fp = stdin;
+ fd_set fdset;
+ struct timeval timeout;
+ int ch, i, maxfd = 0, send_counter = 0;
+ struct flags flags;
+ struct payload_list *pl;
+#ifdef IPSEC_POLICY_IPSEC
+ char *policy = NULL;
+#endif
+
+ memset(&flags, 0, sizeof(flags));
+ openlog("rrenumd", LOG_PID, LOG_DAEMON);
+
+ /* get options */
+ while ((ch = getopt(argc, argv, "c:sdf"
+#ifdef IPSEC
+#ifdef IPSEC_POLICY_IPSEC
+ "P:"
+#else /* IPSEC_POLICY_IPSEC */
+ "AE"
+#endif /* IPSEC_POLICY_IPSEC */
+#endif /* IPSEC */
+ )) != -1){
+ switch (ch) {
+ case 'c':
+ if((fp = fopen(optarg, "r")) == NULL) {
+ syslog(LOG_ERR,
+ "<%s> config file %s open failed",
+ __FUNCTION__, optarg);
+ exit(1);
+ }
+ break;
+ case 's':
+ fp = stdin;
+ break;
+ case 'd':
+ flags.debug = 1;
+ break;
+ case 'f':
+ flags.fg = 1;
+ break;
+#ifdef IPSEC
+#ifdef IPSEC_POLICY_IPSEC
+ case 'P':
+ flags.policy = 1;
+ policy = strdup(optarg);
+ break;
+#else /* IPSEC_POLICY_IPSEC */
+ case 'A':
+ flags.auth = 1;
+ break;
+ case 'E':
+ flags.encrypt = 1;
+ break;
+#endif /* IPSEC_POLICY_IPSEC */
+#endif /*IPSEC*/
+ default:
+ show_usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* set log level */
+ if (flags.debug == 0)
+ (void)setlogmask(LOG_UPTO(LOG_ERR));
+ if (flags.debug == 1)
+ (void)setlogmask(LOG_UPTO(LOG_INFO));
+
+ /* init global variables */
+ init_globals();
+
+ config(&fp);
+
+ sock6_open(&flags
+#ifdef IPSEC_POLICY_IPSEC
+ , policy
+#endif /* IPSEC_POLICY_IPSEC */
+ );
+ sock4_open(&flags
+#ifdef IPSEC_POLICY_IPSEC
+ , policy
+#endif /* IPSEC_POLICY_IPSEC */
+ );
+
+ if (!flags.fg)
+ daemon(0, 0);
+
+ FD_ZERO(&fdset);
+ if (with_v6dest) {
+ FD_SET(s6, &fdset);
+ if (s6 > maxfd)
+ maxfd = s6;
+ }
+ if (with_v4dest) {
+ FD_SET(s4, &fdset);
+ if (s4 > maxfd)
+ maxfd = s4;
+ }
+
+ /* ADHOC: timeout each 30seconds */
+ memset(&timeout, 0, sizeof(timeout));
+
+ /* init temporary payload_list and send_counter*/
+ pl = pl_head;
+ send_counter = retry + 1;
+ while (1) {
+ struct fd_set select_fd = fdset; /* reinitialize */
+
+ if ((i = select(maxfd + 1, &select_fd, NULL, NULL,
+ &timeout)) < 0){
+ syslog(LOG_ERR, "<%s> select: %s",
+ __FUNCTION__, strerror(errno));
+ continue;
+ }
+ if (i == 0) { /* timeout */
+ if (pl == NULL)
+ exit(0);
+ rrenum_snd_eachdst(pl);
+ send_counter--;
+ timeout.tv_sec = 30;
+ if (send_counter == 0) {
+ timeout.tv_sec = 0;
+ pl = pl->pl_next;
+ send_counter = retry + 1;
+ }
+ }
+ if (FD_ISSET(s4, &select_fd))
+ rrenum_input(s4);
+ if (FD_ISSET(s6, &select_fd))
+ rrenum_input(s6);
+ }
+}
diff --git a/usr.sbin/rrenumd/rrenumd.conf.5 b/usr.sbin/rrenumd/rrenumd.conf.5
new file mode 100644
index 0000000..e9fb46f
--- /dev/null
+++ b/usr.sbin/rrenumd/rrenumd.conf.5
@@ -0,0 +1,369 @@
+.\" $KAME: rrenumd.conf.5,v 1.8 2001/02/06 02:17:23 jinmei Exp $
+.\"
+.\" Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the project nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd November 5, 1998
+.Dt RRENUMD.CONF 5
+.Os
+.Sh NAME
+.\"
+.Nm rrenumd.conf
+.Nd configuration file for router renumbering daemon
+.\"
+.Sh DESCRIPTION
+The rrenumd config file describes how the router renumbering packet
+must be constructed and to which destinations it should be sent.
+This file consists of a sequence of statements terminated by a semi-colon (`;').
+Statements are composed of tokens
+separated by white space, which can be any combination of blanks, tabs
+and newlines.
+This structure simplifies identification of
+the parts of the configuration associated with each other.
+Lines beginning with
+.Ql #
+are comments.
+.\"
+.Sh Meta Syntax
+Keywords and special characters that the parser expects exactly are
+displayed using the
+.Ic bold
+font.
+Parameters are specifying with
+.Ar underline .
+Parameters shown in
+square brackets (`[' and `]') are used to show optional
+keywords and parameters.
+The vertical bar (`|') is used to indicate
+between a choice of optional parameters.
+Curly braces (`{' and
+`}') are used to group keywords and parameters when necessary.
+.\"
+.Sh Interface specification
+There are some statements that may or have to specify interface.
+Interfaces are specified in the form of "name unit", such as
+.Ar lo0
+and
+.Ar ep1 .
+.\"
+.Sh Configuration Statements
+.Bl -tag -width Ds
+.\"
+.It Ic debug on|off ;
+Enables configuration file parser debugging.
+If
+.Ic on
+is specified,
+then debugging is enabled,
+If
+.Ic off
+is specified,
+then debugging is disabled.
+It is disabled by default.
+.\"
+.It Ic dest Ar dest-list Op Ar retrycmd ;
+Specifies destinations to which router renumbering messages should be
+sent.
+.Ar dest-list
+can be any combination of single or multiple numerical IPv6 addrs,
+or Full Qualified Domain Names.
+.Ar retrycmd
+has following syntax.
+.\"
+.Bl -tag -width Ds
+.It Ic retry Ar retry-num
+.Ar retry-num
+specifies how many router renumbering messages are sent repeatedly.
+.El
+.It Op Ic add|change|setglobal
+.Cm match-prefix Ar match-prefix-val
+.Bk -words
+.Op /match-prefix-len
+.Ek
+.Bk -words
+.Op Cm maxlen Ar maxlen-val
+.Ek
+.Bk -words
+.Op Cm minlen Ar minlen-val
+.Ek
+.Bk -words
+.Op Cm use-prefix Ar use-prefix-val
+.Ek
+.Bk -words
+.Op /use-prefix-len
+.Ek
+.Bk -words
+.Op Cm keeplen Ar keeplen-val
+.Ek
+.Bk -words
+.Op Ar use-prefix-values ;
+.Ek
+.Pp
+Specifies contents of sending router renumbering message with seqnum 0.
+If
+.Cm add|change|setglobal
+is not specified, then
+.Cm add
+is assumed.
+.Ar use-prefix-values
+has following syntax.
+.Pp
+{
+.Op Cm vltime Ar vltime-val
+.Bk -words
+.Op Cm pltime Ar pltime-val
+.Ek
+.Bk -words
+.Op Cm raf_onlink Cm on|off
+.Ek
+.Bk -words
+.Op Cm raf_auto Cm on|off
+.Ek
+.Bk -words
+.Op Cm rrf_decrprefd Cm on|off
+.Ek
+.Bk -words
+.Op Cm rrf_decrvalid Cm on|off
+.Ek
+}
+.Pp
+Each value has following meaning.
+.Pp
+.Bl -tag -width Ds -compact
+.It Cm match-prefix Ar match-prefix-val Op /match-prefix-len
+Specify
+.Ar match-prefix-val
+that is used for matching with preassigned prefixes to which
+.Cm add|change|setglobal
+command should be applied.
+.Ar /match-prefix-len
+Specify the starting part of
+.Ar match-prefix-val
+to be used for matching with preassigned prefixes, as decimal bit number.
+.It Cm maxlen Ar maxlen-val
+Specify the maximum length of prefixes which is allowed to be
+matched to
+.Ar match-prefix-val ,
+as decimal bit number.
+.It Cm minlen Ar minlen-val
+Specify the minimum length of prefixes which is allowed to be matched to
+.Ar match-prefix-val ,
+as decimal bit number.
+.It Cm use-prefix Ar use-prefix-val Op /usr-prefix-len
+Specify
+.Ar use-prefix-val
+that is used for prefixes to be added on
+.Cm add|change|setglobal
+command.
+.Ar /use-prefix-len
+Specify the starting part of
+.Ar use-prefix-val
+copied to the starting part of prefixes to be added on
+.Cm add|change|setglobal
+command, as decimal bit number.
+.It Cm keeplen Ar keeplen-val
+Specify the medium part of
+.Ar use-prefix-val
+just next to the starting part specified by
+.Ar use-prefix-len ,
+as decimal bit number.
+Contiguous bits part in the same bit position of an existent prefix
+matched with
+.Ar match-prefix-val
+is copied to the same bit position of prefixes to be added.
+.It Cm vltime Ar vmtime-val
+Assign an
+.Ar time
+as prefix valid life time for a prefix to be added.
+Valid value for
+.Ar time
+is decimal seconds number or special format as "d00h00m00s00",
+where 00 can take any decimal number, and "d" means days, "h" means hours,
+"m" means minutes, "s" means seconds.
+And alternatively, special keyword
+"infinity" can be also be specified.
+.It Cm pltime Ar pltime-val
+Assign an
+.Ar time
+as prefix preferred life time for a prefix to be added.
+Valid value for
+.Ar time
+is same as for
+.Ar vltime-val .
+.It Cm raf_onlink Cm on|off
+Let the prefix to be added to have on-link or off-link nature
+for the assigned interface.
+If
+.Cm on
+is specified, the prefix have on-link nature
+(e.g. the prefix
+belong to the link).
+If
+.Cm off
+is specified, the prefix have off-link nature
+(e.g. the
+prefix does not belong to the link).
+.It Cm raf_auto Cm on|off
+Enable or disable the autonomous address auto configuration
+for the prefix to be added.
+If
+.Cm on
+is specified, autonomous address auto configuration is
+enabled.
+If
+.Cm off
+is specified, it is disabled.
+.It Cm rrf_decrprefd Cm on|off
+Enable or disable the decrementation of the pltime.
+If
+.Cm on
+is specified, decrementation of the pltime is enabled.
+If
+.Cm off
+is specified, decrementation of the pltime is disabled.
+.It Cm rrf_decrvalid Cm on|off
+Enable or disable the decrementation of the vltime.
+If
+.Cm on
+is specified, decrementation of the vltime is enabled.
+If
+.Cm off
+is specified, decrementation of the vltime is disabled.
+.El
+.\"
+.It seqnum Ar seqnum-val { Ar rrenum-cmd } ;
+Specifies contents of sending router renumbering message with some
+specific seqnum.
+Multiple of this statement can be specified if they
+have different
+.Ar seqnum-val
+each other.
+.Ar rrenum-cmd
+has just same syntax with above add|change|setglobal statement.
+.El
+.\"
+.Sh EXAMPLES
+For each configuration file example shown below, we suppose
+every IPv6 subnet has its own prefix beginning with
+fec0:0:0::/48 and with its own subnet number
+(in this case,
+subnet number is 7th and 8th octet value of the prefix).
+.Pp
+If you want to assign prefixes beginning with 3ffe:501:ffff::/48
+to each subnet, then following configuration will be enough,
+if each of your routers supports IPv6 multicast forwarding.
+The subnet number of the existing fec0:0:0::/48 prefix and the
+newly assigned 3ffe:501:ffff::/48 prefix will be same.
+.\"
+.Bd -literal -offset indent
+dest ff05::2;
+
+add match-prefix fec0:0:0:: /48 use-prefix 3ffe:501:ffff:: /48 keeplen 16;
+.Ed
+.Pp
+.\"
+If your routers don't support IPv6 multicast forwarding,
+you'll need to specify each destination at
+.Cm dest
+command.
+.\"
+.Bd -literal -offset indent
+dest fec0:0:0:1:260:8ff:fe24:fb3a fec0:0:0:2:200:eff:fe2e:dfe1 fec0:0:0:3:5254:ff:fedc:5217;
+
+add match-prefix fec0:0:0:: /48 use-prefix 3ffe:501:ffff:: /48 keeplen 16;
+.Ed
+.Pp
+.\"
+If you are going to do renumbering, then following procedure will be natural.
+.Bl -enum -offset indent
+.It
+Assign a new prefix.
+.It
+Set old prefix lifetimes to some appropriate transition
+period.
+In the followng example we use 1 week for valid
+lifetime, and 0 for preferred lifetime.
+Also, enable old prefix lifetime expiration
+(By default, it is static and does not expire).
+.It
+After the transition period, old prefixes should become
+invalid, and may have been deleted.
+To make sure that they are deleted, send new router
+renumbering message, which specifies old prefixes as match
+prefix, and no use prefix.
+.El
+.Pp
+.\"
+The following configuration file will do 1 and 2.
+.\"
+.Bd -literal -offset indent
+dest ff05::2;
+
+seqnum 0 {
+ add match-prefix fec0:0:0:: /48 use-prefix 3ffe:501:fffe:: /48 keeplen 16;
+ };
+
+seqnum 1 {
+ change match-prefix 3ffe:501:ffff:: /48 use-prefix 3ffe:501:ffff:: /48 keeplen 16 vltime d7 pltime 0 rrf_decrvalid on rrf_decrprefd on;
+ };
+.Ed
+.Pp
+.\"
+And the following configuration file will do 3
+(should be
+used for the router renumbering message to be sent 1 week
+afterward).
+.\"
+.Bd -literal -offset indent
+dest ff05::2;
+
+change match-prefix 3ffe:501:ffff:: /48;
+.Ed
+.Pp
+.\"
+In the above example, only
+.Cm add
+and
+.Cm change
+commands are used, and there is no example for
+.Cm setglobal
+command.
+.Cm setglobal
+command is almost same with
+.Cm change
+command except that it deletes all pre-defined IPv6 global address.
+.Sh SEE ALSO
+.Xr prefix 8 ,
+.Xr rrenumd 8
+.Sh HISTORY
+The
+.Nm
+configuration file was first appeared in KAME IPv6 protocol stack kit.
+.\" .Sh BUGS
+.\" (to be written)
diff --git a/usr.sbin/rrenumd/rrenumd.h b/usr.sbin/rrenumd/rrenumd.h
new file mode 100644
index 0000000..df0280b
--- /dev/null
+++ b/usr.sbin/rrenumd/rrenumd.h
@@ -0,0 +1,60 @@
+/* $KAME: rrenumd.h,v 1.2 2000/07/03 02:54:09 itojun Exp $ */
+
+/*
+ * Copyright (C) 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by WIDE Project and
+ * its contributors.
+ * 4. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct dst_list {
+ struct dst_list * dl_next;
+ struct sockaddr * dl_dst;
+};
+
+extern struct dst_list *dl_head;
+
+struct payload_list {
+ struct payload_list * pl_next;
+ struct iovec pl_sndiov;
+ struct icmp6_router_renum
+ pl_irr;
+ struct rr_pco_match pl_rpm;
+ /* currently, support only 1 rr_pco_use field per packet */
+ struct rr_pco_use pl_rpu;
+};
+
+extern struct payload_list *pl_head;
+extern u_int retry;
+extern int with_v4dest, with_v6dest;
+
+#define DEF_VLTIME 2592000
+#define DEF_PLTIME 604800
diff --git a/usr.sbin/rtadvd/Makefile b/usr.sbin/rtadvd/Makefile
new file mode 100644
index 0000000..5b935fa
--- /dev/null
+++ b/usr.sbin/rtadvd/Makefile
@@ -0,0 +1,26 @@
+# Copyright (c) 1996 WIDE Project. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modifications, are permitted provided that the above copyright notice
+# and this paragraph are duplicated in all such forms and that any
+# documentation, advertising materials, and other materials related to
+# such distribution and use acknowledge that the software was developed
+# by the WIDE Project, Japan. The name of the Project may not be used to
+# endorse or promote products derived from this software without
+# specific prior written permission. THIS SOFTWARE IS PROVIDED ``AS IS''
+# AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+# LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE.
+#
+# $FreeBSD$
+
+PROG= rtadvd
+MAN= rtadvd.conf.5 rtadvd.8
+SRCS= rtadvd.c rrenum.c advcap.c if.c config.c timer.c dump.c
+
+CFLAGS+= -DINET6
+
+LDADD= -lcompat
+DPADD= ${LIBCOMPAT}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rtadvd/advcap.c b/usr.sbin/rtadvd/advcap.c
new file mode 100644
index 0000000..d46cb89
--- /dev/null
+++ b/usr.sbin/rtadvd/advcap.c
@@ -0,0 +1,455 @@
+/* $FreeBSD$ */
+/* $KAME: advcap.c,v 1.5 2001/02/01 09:12:08 jinmei Exp $ */
+
+/*
+ * Copyright (c) 1983 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * remcap - routines for dealing with the remote host data base
+ *
+ * derived from termcap
+ */
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <errno.h>
+#include <string.h>
+#include "pathnames.h"
+
+#ifndef BUFSIZ
+#define BUFSIZ 1024
+#endif
+#define MAXHOP 32 /* max number of tc= indirections */
+
+#define tgetent agetent
+#define tnchktc anchktc
+#define tnamatch anamatch
+#define tgetnum agetnum
+#define tgetflag agetflag
+#define tgetstr agetstr
+
+#if 0
+#define V_TERMCAP "REMOTE"
+#define V_TERM "HOST"
+#endif
+
+char *RM;
+
+/*
+ * termcap - routines for dealing with the terminal capability data base
+ *
+ * BUG: Should use a "last" pointer in tbuf, so that searching
+ * for capabilities alphabetically would not be a n**2/2
+ * process when large numbers of capabilities are given.
+ * Note: If we add a last pointer now we will screw up the
+ * tc capability. We really should compile termcap.
+ *
+ * Essentially all the work here is scanning and decoding escapes
+ * in string capabilities. We don't use stdio because the editor
+ * doesn't, and because living w/o it is not hard.
+ */
+
+static char *tbuf;
+static int hopcount; /* detect infinite loops in termcap, init 0 */
+
+static char *remotefile;
+
+extern char *conffile;
+
+int tgetent __P((char *, char *));
+int getent __P((char *, char *, char *));
+int tnchktc __P((void));
+int tnamatch __P((char *));
+static char *tskip __P((char *));
+long long tgetnum __P((char *));
+int tgetflag __P((char *));
+char *tgetstr __P((char *, char **));
+static char *tdecode __P((char *, char **));
+
+/*
+ * Get an entry for terminal name in buffer bp,
+ * from the termcap file. Parse is very rudimentary;
+ * we just notice escaped newlines.
+ */
+int
+tgetent(bp, name)
+ char *bp, *name;
+{
+ char *cp;
+
+ remotefile = cp = conffile ? conffile : _PATH_RTADVDCONF;
+ return (getent(bp, name, cp));
+}
+
+int
+getent(bp, name, cp)
+ char *bp, *name, *cp;
+{
+ register int c;
+ register int i = 0, cnt = 0;
+ char ibuf[BUFSIZ];
+ int tf;
+
+ tbuf = bp;
+ tf = 0;
+ /*
+ * TERMCAP can have one of two things in it. It can be the
+ * name of a file to use instead of /etc/termcap. In this
+ * case it better start with a "/". Or it can be an entry to
+ * use so we don't have to read the file. In this case it
+ * has to already have the newlines crunched out.
+ */
+ if (cp && *cp) {
+ tf = open(RM = cp, O_RDONLY);
+ }
+ if (tf < 0) {
+ syslog(LOG_INFO,
+ "<%s> open: %s", __FUNCTION__, strerror(errno));
+ return (-2);
+ }
+ for (;;) {
+ cp = bp;
+ for (;;) {
+ if (i == cnt) {
+ cnt = read(tf, ibuf, BUFSIZ);
+ if (cnt <= 0) {
+ close(tf);
+ return (0);
+ }
+ i = 0;
+ }
+ c = ibuf[i++];
+ if (c == '\n') {
+ if (cp > bp && cp[-1] == '\\') {
+ cp--;
+ continue;
+ }
+ break;
+ }
+ if (cp >= bp+BUFSIZ) {
+ write(STDERR_FILENO, "Remcap entry too long\n",
+ 23);
+ break;
+ } else
+ *cp++ = c;
+ }
+ *cp = 0;
+
+ /*
+ * The real work for the match.
+ */
+ if (tnamatch(name)) {
+ close(tf);
+ return (tnchktc());
+ }
+ }
+}
+
+/*
+ * tnchktc: check the last entry, see if it's tc=xxx. If so,
+ * recursively find xxx and append that entry (minus the names)
+ * to take the place of the tc=xxx entry. This allows termcap
+ * entries to say "like an HP2621 but doesn't turn on the labels".
+ * Note that this works because of the left to right scan.
+ */
+int
+tnchktc()
+{
+ register char *p, *q;
+ char tcname[16]; /* name of similar terminal */
+ char tcbuf[BUFSIZ];
+ char *holdtbuf = tbuf;
+ int l;
+
+ p = tbuf + strlen(tbuf) - 2; /* before the last colon */
+ while (*--p != ':')
+ if (p<tbuf) {
+ write(STDERR_FILENO, "Bad remcap entry\n", 18);
+ return (0);
+ }
+ p++;
+ /* p now points to beginning of last field */
+ if (p[0] != 't' || p[1] != 'c')
+ return (1);
+ strcpy(tcname, p+3);
+ q = tcname;
+ while (*q && *q != ':')
+ q++;
+ *q = 0;
+ if (++hopcount > MAXHOP) {
+ write(STDERR_FILENO, "Infinite tc= loop\n", 18);
+ return (0);
+ }
+ if (getent(tcbuf, tcname, remotefile) != 1) {
+ return (0);
+ }
+ for (q = tcbuf; *q++ != ':'; )
+ ;
+ l = p - holdtbuf + strlen(q);
+ if (l > BUFSIZ) {
+ write(STDERR_FILENO, "Remcap entry too long\n", 23);
+ q[BUFSIZ - (p-holdtbuf)] = 0;
+ }
+ strcpy(p, q);
+ tbuf = holdtbuf;
+ return (1);
+}
+
+/*
+ * Tnamatch deals with name matching. The first field of the termcap
+ * entry is a sequence of names separated by |'s, so we compare
+ * against each such name. The normal : terminator after the last
+ * name (before the first field) stops us.
+ */
+int
+tnamatch(np)
+ char *np;
+{
+ register char *Np, *Bp;
+
+ Bp = tbuf;
+ if (*Bp == '#')
+ return (0);
+ for (;;) {
+ for (Np = np; *Np && *Bp == *Np; Bp++, Np++)
+ continue;
+ if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0))
+ return (1);
+ while (*Bp && *Bp != ':' && *Bp != '|')
+ Bp++;
+ if (*Bp == 0 || *Bp == ':')
+ return (0);
+ Bp++;
+ }
+}
+
+/*
+ * Skip to the next field. Notice that this is very dumb, not
+ * knowing about \: escapes or any such. If necessary, :'s can be put
+ * into the termcap file in octal.
+ */
+static char *
+tskip(bp)
+ register char *bp;
+{
+ int dquote;
+
+ dquote = 0;
+ while (*bp) {
+ switch (*bp) {
+ case ':':
+ if (!dquote)
+ goto breakbreak;
+ else
+ bp++;
+ break;
+ case '\\':
+ bp++;
+ if (isdigit(*bp)) {
+ while (isdigit(*bp++))
+ ;
+ } else
+ bp++;
+ case '"':
+ dquote = (dquote ? 1 : 0);
+ bp++;
+ break;
+ default:
+ bp++;
+ break;
+ }
+ }
+breakbreak:
+ if (*bp == ':')
+ bp++;
+ return (bp);
+}
+
+/*
+ * Return the (numeric) option id.
+ * Numeric options look like
+ * li#80
+ * i.e. the option string is separated from the numeric value by
+ * a # character. If the option is not found we return -1.
+ * Note that we handle octal numbers beginning with 0.
+ */
+long long
+tgetnum(id)
+ char *id;
+{
+ register long long i;
+ register int base;
+ register char *bp = tbuf;
+
+ for (;;) {
+ bp = tskip(bp);
+ if (*bp == 0)
+ return (-1);
+ if (strncmp(bp, id, strlen(id)) != 0)
+ continue;
+ bp += strlen(id);
+ if (*bp == '@')
+ return (-1);
+ if (*bp != '#')
+ continue;
+ bp++;
+ base = 10;
+ if (*bp == '0')
+ base = 8;
+ i = 0;
+ while (isdigit(*bp))
+ i *= base, i += *bp++ - '0';
+ return (i);
+ }
+}
+
+/*
+ * Handle a flag option.
+ * Flag options are given "naked", i.e. followed by a : or the end
+ * of the buffer. Return 1 if we find the option, or 0 if it is
+ * not given.
+ */
+int
+tgetflag(id)
+ char *id;
+{
+ register char *bp = tbuf;
+
+ for (;;) {
+ bp = tskip(bp);
+ if (!*bp)
+ return (0);
+ if (strncmp(bp, id, strlen(id)) == 0) {
+ bp += strlen(id);
+ if (!*bp || *bp == ':')
+ return (1);
+ else if (*bp == '@')
+ return (0);
+ }
+ }
+}
+
+/*
+ * Get a string valued option.
+ * These are given as
+ * cl=^Z
+ * Much decoding is done on the strings, and the strings are
+ * placed in area, which is a ref parameter which is updated.
+ * No checking on area overflow.
+ */
+char *
+tgetstr(id, area)
+ char *id, **area;
+{
+ register char *bp = tbuf;
+
+ for (;;) {
+ bp = tskip(bp);
+ if (!*bp)
+ return (0);
+ if (strncmp(bp, id, strlen(id)) != 0)
+ continue;
+ bp += strlen(id);
+ if (*bp == '@')
+ return (0);
+ if (*bp != '=')
+ continue;
+ bp++;
+ return (tdecode(bp, area));
+ }
+}
+
+/*
+ * Tdecode does the grung work to decode the
+ * string capability escapes.
+ */
+static char *
+tdecode(str, area)
+ register char *str;
+ char **area;
+{
+ register char *cp;
+ register int c;
+ register char *dp;
+ int i;
+ char term;
+
+ term = ':';
+ cp = *area;
+again:
+ if (*str == '"') {
+ term = '"';
+ str++;
+ }
+ while ((c = *str++) && c != term) {
+ switch (c) {
+
+ case '^':
+ c = *str++ & 037;
+ break;
+
+ case '\\':
+ dp = "E\033^^\\\\::n\nr\rt\tb\bf\f\"\"";
+ c = *str++;
+nextc:
+ if (*dp++ == c) {
+ c = *dp++;
+ break;
+ }
+ dp++;
+ if (*dp)
+ goto nextc;
+ if (isdigit(c)) {
+ c -= '0', i = 2;
+ do
+ c <<= 3, c |= *str++ - '0';
+ while (--i && isdigit(*str));
+ }
+ break;
+ }
+ *cp++ = c;
+ }
+ if (c == term && term != ':') {
+ term = ':';
+ goto again;
+ }
+ *cp++ = 0;
+ str = *area;
+ *area = cp;
+ return (str);
+}
diff --git a/usr.sbin/rtadvd/advcap.h b/usr.sbin/rtadvd/advcap.h
new file mode 100644
index 0000000..7b3715a
--- /dev/null
+++ b/usr.sbin/rtadvd/advcap.h
@@ -0,0 +1,46 @@
+/* $FreeBSD$ */
+/* $KAME: advcap.h,v 1.3 2001/02/01 09:12:08 jinmei Exp $ */
+
+/*
+ * Copyright (C) 1994,1995 by Andrey A. Chernov, Moscow, Russia.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* Based on Id: termcap.h,v 1.8 1996/09/10 12:42:10 peter Exp */
+
+#ifndef _ADVCAP_H_
+#define _ADVCAP_H_
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+extern int agetent __P((char *, const char *));
+extern int agetflag __P((const char *));
+extern long long agetnum __P((const char *));
+extern char *agetstr __P((const char *, char **));
+
+__END_DECLS
+
+#endif /* _ADVCAP_H_ */
diff --git a/usr.sbin/rtadvd/config.c b/usr.sbin/rtadvd/config.c
new file mode 100644
index 0000000..5b7d304
--- /dev/null
+++ b/usr.sbin/rtadvd/config.c
@@ -0,0 +1,1085 @@
+/* $FreeBSD$ */
+/* $KAME: config.c,v 1.37 2001/05/25 07:34:00 itojun Exp $ */
+
+/*
+ * Copyright (C) 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+#include <net/if_var.h>
+#endif /* __FreeBSD__ >= 3 */
+#include <net/route.h>
+#include <net/if_dl.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet/icmp6.h>
+#ifdef MIP6
+#include <netinet6/mip6.h>
+#endif
+
+#include <arpa/inet.h>
+
+#include <stdio.h>
+#include <syslog.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+#include <search.h>
+#endif
+#include <unistd.h>
+#include <ifaddrs.h>
+
+#include "rtadvd.h"
+#include "advcap.h"
+#include "timer.h"
+#include "if.h"
+#include "config.h"
+
+static time_t prefix_timo = (60 * 120); /* 2 hours.
+ * XXX: should be configurable. */
+extern struct rainfo *ralist;
+
+static struct rtadvd_timer *prefix_timeout __P((void *));
+static void makeentry __P((char *, size_t, int, char *, int));
+static void get_prefix __P((struct rainfo *));
+static int getinet6sysctl __P((int));
+
+void
+getconfig(intface)
+ char *intface;
+{
+ int stat, pfxs, i;
+ char tbuf[BUFSIZ];
+ struct rainfo *tmp;
+ long val;
+ long long val64;
+ char buf[BUFSIZ];
+ char *bp = buf;
+ char *addr;
+ static int forwarding = -1;
+
+#define MUSTHAVE(var, cap) \
+ do { \
+ int t; \
+ if ((t = agetnum(cap)) < 0) { \
+ fprintf(stderr, "rtadvd: need %s for interface %s\n", \
+ cap, intface); \
+ exit(1); \
+ } \
+ var = t; \
+ } while (0)
+#define MAYHAVE(var, cap, def) \
+ do { \
+ if ((var = agetnum(cap)) < 0) \
+ var = def; \
+ } while (0)
+
+ if ((stat = agetent(tbuf, intface)) <= 0) {
+ memset(tbuf, 0, sizeof(tbuf));
+ syslog(LOG_INFO,
+ "<%s> %s isn't defined in the configuration file"
+ " or the configuration file doesn't exist."
+ " Treat it as default",
+ __FUNCTION__, intface);
+ }
+
+ tmp = (struct rainfo *)malloc(sizeof(*ralist));
+ memset(tmp, 0, sizeof(*tmp));
+ tmp->prefix.next = tmp->prefix.prev = &tmp->prefix;
+ tmp->route.next = tmp->route.prev = &tmp->route;
+
+ /* check if we are allowed to forward packets (if not determined) */
+ if (forwarding < 0) {
+ if ((forwarding = getinet6sysctl(IPV6CTL_FORWARDING)) < 0)
+ exit(1);
+ }
+
+ /* get interface information */
+ if (agetflag("nolladdr"))
+ tmp->advlinkopt = 0;
+ else
+ tmp->advlinkopt = 1;
+ if (tmp->advlinkopt) {
+ if ((tmp->sdl = if_nametosdl(intface)) == NULL) {
+ syslog(LOG_ERR,
+ "<%s> can't get information of %s",
+ __FUNCTION__, intface);
+ exit(1);
+ }
+ tmp->ifindex = tmp->sdl->sdl_index;
+ } else
+ tmp->ifindex = if_nametoindex(intface);
+ strncpy(tmp->ifname, intface, sizeof(tmp->ifname));
+ if ((tmp->phymtu = if_getmtu(intface)) == 0) {
+ tmp->phymtu = IPV6_MMTU;
+ syslog(LOG_WARNING,
+ "<%s> can't get interface mtu of %s. Treat as %d",
+ __FUNCTION__, intface, IPV6_MMTU);
+ }
+
+ /*
+ * set router configuration variables.
+ */
+ MAYHAVE(val, "maxinterval", DEF_MAXRTRADVINTERVAL);
+ if (val < MIN_MAXINTERVAL || val > MAX_MAXINTERVAL) {
+ syslog(LOG_ERR,
+ "<%s> maxinterval must be between %e and %u",
+ __FUNCTION__, MIN_MAXINTERVAL, MAX_MAXINTERVAL);
+ exit(1);
+ }
+ tmp->maxinterval = (u_int)val;
+ MAYHAVE(val, "mininterval", tmp->maxinterval/3);
+ if (val < MIN_MININTERVAL || val > (tmp->maxinterval * 3) / 4) {
+ syslog(LOG_ERR,
+ "<%s> mininterval must be between %e and %d",
+ __FUNCTION__,
+ MIN_MININTERVAL,
+ (tmp->maxinterval * 3) / 4);
+ exit(1);
+ }
+ tmp->mininterval = (u_int)val;
+
+ MAYHAVE(val, "chlim", DEF_ADVCURHOPLIMIT);
+ tmp->hoplimit = val & 0xff;
+
+ MAYHAVE(val, "raflags", 0);
+ tmp->managedflg = val & ND_RA_FLAG_MANAGED;
+ tmp->otherflg = val & ND_RA_FLAG_OTHER;
+#ifdef MIP6
+ if (mobileip6)
+ tmp->haflg = val & ND_RA_FLAG_HA;
+#endif
+#ifndef ND_RA_FLAG_RTPREF_MASK
+#define ND_RA_FLAG_RTPREF_MASK 0x18 /* 00011000 */
+#define ND_RA_FLAG_RTPREF_RSV 0x10 /* 00010000 */
+#endif
+ tmp->rtpref = val & ND_RA_FLAG_RTPREF_MASK;
+ if (tmp->rtpref == ND_RA_FLAG_RTPREF_RSV) {
+ syslog(LOG_ERR, "<%s> invalid router preference on %s",
+ __FUNCTION__, intface);
+ exit(1);
+ }
+
+ MAYHAVE(val, "rltime", tmp->maxinterval * 3);
+ if (val && (val < tmp->maxinterval || val > MAXROUTERLIFETIME)) {
+ syslog(LOG_ERR,
+ "<%s> router lifetime on %s must be 0 or"
+ " between %d and %d",
+ __FUNCTION__, intface,
+ tmp->maxinterval, MAXROUTERLIFETIME);
+ exit(1);
+ }
+ /*
+ * Basically, hosts MUST NOT send Router Advertisement messages at any
+ * time (RFC 2461, Section 6.2.3). However, it would sometimes be
+ * useful to allow hosts to advertise some parameters such as prefix
+ * information and link MTU. Thus, we allow hosts to invoke rtadvd
+ * only when router lifetime (on every advertising interface) is
+ * explicitly set zero. (see also the above section)
+ */
+ if (val && forwarding == 0) {
+ syslog(LOG_WARNING,
+ "<%s> non zero router lifetime is specified for %s, "
+ "which must not be allowed for hosts.",
+ __FUNCTION__, intface);
+ exit(1);
+ }
+ tmp->lifetime = val & 0xffff;
+
+ MAYHAVE(val, "rtime", DEF_ADVREACHABLETIME);
+ if (val > MAXREACHABLETIME) {
+ syslog(LOG_ERR,
+ "<%s> reachable time must be no greater than %d",
+ __FUNCTION__, MAXREACHABLETIME);
+ exit(1);
+ }
+ tmp->reachabletime = (u_int32_t)val;
+
+ MAYHAVE(val64, "retrans", DEF_ADVRETRANSTIMER);
+ if (val64 < 0 || val64 > 0xffffffff) {
+ syslog(LOG_ERR,
+ "<%s> retrans time out of range", __FUNCTION__);
+ exit(1);
+ }
+ tmp->retranstimer = (u_int32_t)val64;
+
+#ifndef MIP6
+ if (agetstr("hapref", &bp) || agetstr("hatime", &bp)) {
+ syslog(LOG_ERR,
+ "<%s> mobile-ip6 configuration not supported",
+ __FUNCTION__);
+ exit(1);
+ }
+#else
+ if (!mobileip6) {
+ if (agetstr("hapref", &bp) || agetstr("hatime", &bp)) {
+ syslog(LOG_ERR,
+ "<%s> mobile-ip6 configuration without "
+ "proper command line option",
+ __FUNCTION__);
+ exit(1);
+ }
+ } else {
+ tmp->hapref = 0;
+ if ((val = agetnum("hapref")) >= 0)
+ tmp->hapref = (int16_t)val;
+ if (tmp->hapref != 0) {
+ tmp->hatime = 0;
+ MUSTHAVE(val, "hatime");
+ tmp->hatime = (u_int16_t)val;
+ if (tmp->hatime <= 0) {
+ syslog(LOG_ERR,
+ "<%s> home agent lifetime must be greater than 0",
+ __FUNCTION__);
+ exit(1);
+ }
+ }
+ }
+#endif
+
+ /* prefix information */
+
+ /*
+ * This is an implementation specific parameter to consinder
+ * link propagation delays and poorly synchronized clocks when
+ * checking consistency of advertised lifetimes.
+ */
+ MAYHAVE(val, "clockskew", 0);
+ tmp->clockskew = val;
+
+ if ((pfxs = agetnum("addrs")) < 0) {
+ /* auto configure prefix information */
+ if (agetstr("addr", &bp) || agetstr("addr1", &bp)) {
+ syslog(LOG_ERR,
+ "<%s> conflicting prefix configuration for %s: "
+ "automatic and manual config at the same time",
+ __FUNCTION__, intface);
+ exit(1);
+ }
+ get_prefix(tmp);
+ }
+ else {
+ tmp->pfxs = pfxs;
+ for (i = 0; i < pfxs; i++) {
+ struct prefix *pfx;
+ char entbuf[256];
+ int added = (pfxs > 1) ? 1 : 0;
+
+ /* allocate memory to store prefix information */
+ if ((pfx = malloc(sizeof(struct prefix))) == NULL) {
+ syslog(LOG_ERR,
+ "<%s> can't allocate enough memory",
+ __FUNCTION__);
+ exit(1);
+ }
+ memset(pfx, 0, sizeof(*pfx));
+
+ /* link into chain */
+ insque(pfx, &tmp->prefix);
+ pfx->rainfo = tmp;
+
+ pfx->origin = PREFIX_FROM_CONFIG;
+
+ makeentry(entbuf, sizeof(entbuf), i, "prefixlen",
+ added);
+ MAYHAVE(val, entbuf, 64);
+ if (val < 0 || val > 128) {
+ syslog(LOG_ERR,
+ "<%s> prefixlen out of range",
+ __FUNCTION__);
+ exit(1);
+ }
+ pfx->prefixlen = (int)val;
+
+ makeentry(entbuf, sizeof(entbuf), i, "pinfoflags",
+ added);
+#ifdef MIP6
+ if (mobileip6)
+ {
+ MAYHAVE(val, entbuf,
+ (ND_OPT_PI_FLAG_ONLINK|ND_OPT_PI_FLAG_AUTO|
+ ND_OPT_PI_FLAG_ROUTER));
+ } else
+#endif
+ {
+ MAYHAVE(val, entbuf,
+ (ND_OPT_PI_FLAG_ONLINK|ND_OPT_PI_FLAG_AUTO));
+ }
+ pfx->onlinkflg = val & ND_OPT_PI_FLAG_ONLINK;
+ pfx->autoconfflg = val & ND_OPT_PI_FLAG_AUTO;
+#ifdef MIP6
+ pfx->routeraddr = val & ND_OPT_PI_FLAG_ROUTER;
+#endif
+
+ makeentry(entbuf, sizeof(entbuf), i, "vltime", added);
+ MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME);
+ if (val64 < 0 || val64 > 0xffffffff) {
+ syslog(LOG_ERR,
+ "<%s> vltime out of range",
+ __FUNCTION__);
+ exit(1);
+ }
+ pfx->validlifetime = (u_int32_t)val64;
+
+ makeentry(entbuf, sizeof(entbuf), i, "vltimedecr",
+ added);
+ if (agetflag(entbuf)) {
+ struct timeval now;
+ gettimeofday(&now, 0);
+ pfx->vltimeexpire =
+ now.tv_sec + pfx->validlifetime;
+ }
+
+ makeentry(entbuf, sizeof(entbuf), i, "pltime", added);
+ MAYHAVE(val64, entbuf, DEF_ADVPREFERREDLIFETIME);
+ if (val64 < 0 || val64 > 0xffffffff) {
+ syslog(LOG_ERR,
+ "<%s> pltime out of range",
+ __FUNCTION__);
+ exit(1);
+ }
+ pfx->preflifetime = (u_int32_t)val64;
+
+ makeentry(entbuf, sizeof(entbuf), i, "pltimedecr",
+ added);
+ if (agetflag(entbuf)) {
+ struct timeval now;
+ gettimeofday(&now, 0);
+ pfx->pltimeexpire =
+ now.tv_sec + pfx->preflifetime;
+ }
+
+ makeentry(entbuf, sizeof(entbuf), i, "addr", added);
+ addr = (char *)agetstr(entbuf, &bp);
+ if (addr == NULL) {
+ syslog(LOG_ERR,
+ "<%s> need %s as an prefix for "
+ "interface %s",
+ __FUNCTION__, entbuf, intface);
+ exit(1);
+ }
+ if (inet_pton(AF_INET6, addr,
+ &pfx->prefix) != 1) {
+ syslog(LOG_ERR,
+ "<%s> inet_pton failed for %s",
+ __FUNCTION__, addr);
+ exit(1);
+ }
+ if (IN6_IS_ADDR_MULTICAST(&pfx->prefix)) {
+ syslog(LOG_ERR,
+ "<%s> multicast prefix(%s) must "
+ "not be advertised (IF=%s)",
+ __FUNCTION__, addr, intface);
+ exit(1);
+ }
+ if (IN6_IS_ADDR_LINKLOCAL(&pfx->prefix))
+ syslog(LOG_NOTICE,
+ "<%s> link-local prefix(%s) will be"
+ " advertised on %s",
+ __FUNCTION__, addr, intface);
+ }
+ }
+
+ MAYHAVE(val, "mtu", 0);
+ if (val < 0 || val > 0xffffffff) {
+ syslog(LOG_ERR,
+ "<%s> mtu out of range", __FUNCTION__);
+ exit(1);
+ }
+ tmp->linkmtu = (u_int32_t)val;
+ if (tmp->linkmtu == 0) {
+ char *mtustr;
+
+ if ((mtustr = (char *)agetstr("mtu", &bp)) &&
+ strcmp(mtustr, "auto") == 0)
+ tmp->linkmtu = tmp->phymtu;
+ }
+ else if (tmp->linkmtu < IPV6_MMTU || tmp->linkmtu > tmp->phymtu) {
+ syslog(LOG_ERR,
+ "<%s> advertised link mtu must be between"
+ " least MTU and physical link MTU",
+ __FUNCTION__);
+ exit(1);
+ }
+
+ /* route information */
+
+ MAYHAVE(val, "routes", 0);
+ if (val < 0 || val > 0xffffffff) {
+ syslog(LOG_ERR,
+ "<%s> number of route information improper", __FUNCTION__);
+ exit(1);
+ }
+ tmp->routes = val;
+ for (i = 0; i < tmp->routes; i++) {
+ struct rtinfo *rti;
+ char entbuf[256];
+ int added = (tmp->routes > 1) ? 1 : 0;
+
+ /* allocate memory to store prefix information */
+ if ((rti = malloc(sizeof(struct rtinfo))) == NULL) {
+ syslog(LOG_ERR,
+ "<%s> can't allocate enough memory",
+ __FUNCTION__);
+ exit(1);
+ }
+ memset(rti, 0, sizeof(*rti));
+
+ /* link into chain */
+ insque(rti, &tmp->route);
+
+ makeentry(entbuf, sizeof(entbuf), i, "rtrplen", added);
+ MAYHAVE(val, entbuf, 64);
+ if (val < 0 || val > 128) {
+ syslog(LOG_ERR,
+ "<%s> prefixlen out of range",
+ __FUNCTION__);
+ exit(1);
+ }
+ rti->prefixlen = (int)val;
+
+ makeentry(entbuf, sizeof(entbuf), i, "rtrflags", added);
+ MAYHAVE(val, entbuf, 0);
+ rti->rtpref = val & ND_RA_FLAG_RTPREF_MASK;
+ if (rti->rtpref == ND_RA_FLAG_RTPREF_RSV) {
+ syslog(LOG_ERR, "<%s> invalid router preference",
+ __FUNCTION__);
+ exit(1);
+ }
+
+ makeentry(entbuf, sizeof(entbuf), i, "rtrltime", added);
+ /*
+ * XXX: since default value of route lifetime is not defined in
+ * draft-draves-route-selection-01.txt, I took the default
+ * value of valid lifetime of prefix as its default.
+ * It need be much considered.
+ */
+ MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME);
+ if (val64 < 0 || val64 > 0xffffffff) {
+ syslog(LOG_ERR,
+ "<%s> rtrltime out of range",
+ __FUNCTION__);
+ exit(1);
+ }
+ rti->ltime = (u_int32_t)val64;
+
+ makeentry(entbuf, sizeof(entbuf), i, "rtrprefix", added);
+ addr = (char *)agetstr(entbuf, &bp);
+ if (addr == NULL) {
+ syslog(LOG_ERR,
+ "<%s> need %s as an route for "
+ "interface %s",
+ __FUNCTION__, entbuf, intface);
+ exit(1);
+ }
+ if (inet_pton(AF_INET6, addr, &rti->prefix) != 1) {
+ syslog(LOG_ERR,
+ "<%s> inet_pton failed for %s",
+ __FUNCTION__, addr);
+ exit(1);
+ }
+#if 0
+ /*
+ * XXX: currently there's no restriction in route information
+ * prefix according to draft-draves-route-selection-01.txt,
+ * however I think the similar restriction be necessary.
+ */
+ MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME);
+ if (IN6_IS_ADDR_MULTICAST(&rti->prefix)) {
+ syslog(LOG_ERR,
+ "<%s> multicast route (%s) must "
+ "not be advertised (IF=%s)",
+ __FUNCTION__, addr, intface);
+ exit(1);
+ }
+ if (IN6_IS_ADDR_LINKLOCAL(&rti->prefix)) {
+ syslog(LOG_NOTICE,
+ "<%s> link-local route (%s) must "
+ "not be advertised on %s",
+ __FUNCTION__, addr, intface);
+ exit(1);
+ }
+#endif
+ }
+
+ /* okey */
+ tmp->next = ralist;
+ ralist = tmp;
+
+ /* construct the sending packet */
+ make_packet(tmp);
+
+ /* set timer */
+ tmp->timer = rtadvd_add_timer(ra_timeout, ra_timer_update,
+ tmp, tmp);
+ ra_timer_update((void *)tmp, &tmp->timer->tm);
+ rtadvd_set_timer(&tmp->timer->tm, tmp->timer);
+}
+
+static void
+get_prefix(struct rainfo *rai)
+{
+ struct ifaddrs *ifap, *ifa;
+ struct prefix *pp;
+ struct in6_addr *a;
+ u_char *p, *ep, *m, *lim;
+ u_char ntopbuf[INET6_ADDRSTRLEN];
+
+ if (getifaddrs(&ifap) < 0) {
+ syslog(LOG_ERR,
+ "<%s> can't get interface addresses",
+ __FUNCTION__);
+ exit(1);
+ }
+ for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+ int plen;
+
+ if (strcmp(ifa->ifa_name, rai->ifname) != 0)
+ continue;
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ a = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
+ if (IN6_IS_ADDR_LINKLOCAL(a))
+ continue;
+
+ /* get prefix length */
+ m = (u_char *)&((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr;
+ lim = (u_char *)(ifa->ifa_netmask) + ifa->ifa_netmask->sa_len;
+ plen = prefixlen(m, lim);
+ if (plen < 0 || plen > 128) {
+ syslog(LOG_ERR, "<%s> failed to get prefixlen "
+ "or prefix is invalid",
+ __FUNCTION__);
+ exit(1);
+ }
+ if (find_prefix(rai, a, plen)) {
+ /* ignore a duplicated prefix. */
+ continue;
+ }
+
+ /* allocate memory to store prefix info. */
+ if ((pp = malloc(sizeof(*pp))) == NULL) {
+ syslog(LOG_ERR,
+ "<%s> can't get allocate buffer for prefix",
+ __FUNCTION__);
+ exit(1);
+ }
+ memset(pp, 0, sizeof(*pp));
+
+ /* set prefix, sweep bits outside of prefixlen */
+ pp->prefixlen = plen;
+ memcpy(&pp->prefix, a, sizeof(*a));
+ p = (u_char *)&pp->prefix;
+ ep = (u_char *)(&pp->prefix + 1);
+ while (m < lim)
+ *p++ &= *m++;
+ while (p < ep)
+ *p++ = 0x00;
+
+ if (!inet_ntop(AF_INET6, &pp->prefix, ntopbuf,
+ sizeof(ntopbuf))) {
+ syslog(LOG_ERR, "<%s> inet_ntop failed", __FUNCTION__);
+ exit(1);
+ }
+ syslog(LOG_DEBUG,
+ "<%s> add %s/%d to prefix list on %s",
+ __FUNCTION__, ntopbuf, pp->prefixlen, rai->ifname);
+
+ /* set other fields with protocol defaults */
+ pp->validlifetime = DEF_ADVVALIDLIFETIME;
+ pp->preflifetime = DEF_ADVPREFERREDLIFETIME;
+ pp->onlinkflg = 1;
+ pp->autoconfflg = 1;
+ pp->origin = PREFIX_FROM_KERNEL;
+
+ /* link into chain */
+ insque(pp, &rai->prefix);
+
+ /* counter increment */
+ rai->pfxs++;
+ }
+
+ freeifaddrs(ifap);
+}
+
+static void
+makeentry(buf, len, id, string, add)
+ char *buf;
+ size_t len;
+ int id;
+ char *string;
+ int add;
+{
+ char *ep = buf + len;
+
+ strcpy(buf, string);
+ if (add) {
+ char *cp;
+
+ cp = (char *)index(buf, '\0');
+ snprintf(cp, ep - cp, "%d", id);
+ }
+}
+
+/*
+ * Add a prefix to the list of specified interface and reconstruct
+ * the outgoing packet.
+ * The prefix must not be in the list.
+ * XXX: other parameter of the prefix(e.g. lifetime) shoule be
+ * able to be specified.
+ */
+static void
+add_prefix(struct rainfo *rai, struct in6_prefixreq *ipr)
+{
+ struct prefix *prefix;
+ u_char ntopbuf[INET6_ADDRSTRLEN];
+
+ if ((prefix = malloc(sizeof(*prefix))) == NULL) {
+ syslog(LOG_ERR, "<%s> memory allocation failed",
+ __FUNCTION__);
+ return; /* XXX: error or exit? */
+ }
+ memset(prefix, 0, sizeof(*prefix));
+ prefix->prefix = ipr->ipr_prefix.sin6_addr;
+ prefix->prefixlen = ipr->ipr_plen;
+ prefix->validlifetime = ipr->ipr_vltime;
+ prefix->preflifetime = ipr->ipr_pltime;
+ prefix->onlinkflg = ipr->ipr_raf_onlink;
+ prefix->autoconfflg = ipr->ipr_raf_auto;
+ prefix->origin = PREFIX_FROM_DYNAMIC;
+
+ insque(prefix, &rai->prefix);
+ prefix->rainfo = rai;
+
+ syslog(LOG_DEBUG, "<%s> new prefix %s/%d was added on %s",
+ __FUNCTION__, inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ ipr->ipr_plen, rai->ifname);
+
+ /* free the previous packet */
+ free(rai->ra_data);
+ rai->ra_data = NULL;
+
+ /* reconstruct the packet */
+ rai->pfxs++;
+ make_packet(rai);
+
+ /*
+ * reset the timer so that the new prefix will be advertised quickly.
+ */
+ rai->initcounter = 0;
+ ra_timer_update((void *)rai, &rai->timer->tm);
+ rtadvd_set_timer(&rai->timer->tm, rai->timer);
+}
+
+/*
+ * Delete a prefix to the list of specified interface and reconstruct
+ * the outgoing packet.
+ * The prefix must be in the list.
+ */
+void
+delete_prefix(struct prefix *prefix)
+{
+ u_char ntopbuf[INET6_ADDRSTRLEN];
+ struct rainfo *rai = prefix->rainfo;
+
+ remque(prefix);
+ syslog(LOG_DEBUG, "<%s> prefix %s/%d was deleted on %s",
+ __FUNCTION__, inet_ntop(AF_INET6, &prefix->prefix,
+ ntopbuf, INET6_ADDRSTRLEN),
+ prefix->prefixlen, rai->ifname);
+ if (prefix->timer)
+ rtadvd_remove_timer(&prefix->timer);
+ free(prefix);
+ rai->pfxs--;
+}
+
+void
+invalidate_prefix(struct prefix *prefix)
+{
+ u_char ntopbuf[INET6_ADDRSTRLEN];
+ struct timeval timo;
+ struct rainfo *rai = prefix->rainfo;
+
+ if (prefix->timer) { /* sanity check */
+ syslog(LOG_ERR,
+ "<%s> assumption failure: timer already exists",
+ __FUNCTION__);
+ exit(1);
+ }
+
+ syslog(LOG_DEBUG, "<%s> prefix %s/%d was invalidated on %s, "
+ "will expire in %ld seconds", __FUNCTION__,
+ inet_ntop(AF_INET6, &prefix->prefix, ntopbuf, INET6_ADDRSTRLEN),
+ prefix->prefixlen, rai->ifname, (long)prefix_timo);
+
+ /* set the expiration timer */
+ prefix->timer = rtadvd_add_timer(prefix_timeout, NULL, prefix, NULL);
+ if (prefix->timer == NULL) {
+ syslog(LOG_ERR, "<%s> failed to add a timer for a prefix. "
+ "remove the prefix", __FUNCTION__);
+ delete_prefix(prefix);
+ }
+ timo.tv_sec = prefix_timo;
+ timo.tv_usec = 0;
+ rtadvd_set_timer(&timo, prefix->timer);
+}
+
+static struct rtadvd_timer *
+prefix_timeout(void *arg)
+{
+ struct prefix *prefix = (struct prefix *)arg;
+
+ delete_prefix(prefix);
+
+ return(NULL);
+}
+
+void
+update_prefix(struct prefix * prefix)
+{
+ u_char ntopbuf[INET6_ADDRSTRLEN];
+ struct rainfo *rai = prefix->rainfo;
+
+ if (prefix->timer == NULL) { /* sanity check */
+ syslog(LOG_ERR,
+ "<%s> assumption failure: timer does not exist",
+ __FUNCTION__);
+ exit(1);
+ }
+
+ syslog(LOG_DEBUG, "<%s> prefix %s/%d was re-enabled on %s",
+ __FUNCTION__, inet_ntop(AF_INET6, &prefix->prefix, ntopbuf,
+ INET6_ADDRSTRLEN), prefix->prefixlen, rai->ifname);
+
+ /* stop the expiration timer */
+ rtadvd_remove_timer(&prefix->timer);
+}
+
+/*
+ * Try to get an in6_prefixreq contents for a prefix which matches
+ * ipr->ipr_prefix and ipr->ipr_plen and belongs to
+ * the interface whose name is ipr->ipr_name[].
+ */
+static int
+init_prefix(struct in6_prefixreq *ipr)
+{
+#if 0
+ int s;
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ syslog(LOG_ERR, "<%s> socket: %s", __FUNCTION__,
+ strerror(errno));
+ exit(1);
+ }
+
+ if (ioctl(s, SIOCGIFPREFIX_IN6, (caddr_t)ipr) < 0) {
+ syslog(LOG_INFO, "<%s> ioctl:SIOCGIFPREFIX %s", __FUNCTION__,
+ strerror(errno));
+
+ ipr->ipr_vltime = DEF_ADVVALIDLIFETIME;
+ ipr->ipr_pltime = DEF_ADVPREFERREDLIFETIME;
+ ipr->ipr_raf_onlink = 1;
+ ipr->ipr_raf_auto = 1;
+ /* omit other field initialization */
+ }
+ else if (ipr->ipr_origin < PR_ORIG_RR) {
+ u_char ntopbuf[INET6_ADDRSTRLEN];
+
+ syslog(LOG_WARNING, "<%s> Added prefix(%s)'s origin %d is"
+ "lower than PR_ORIG_RR(router renumbering)."
+ "This should not happen if I am router", __FUNCTION__,
+ inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr, ntopbuf,
+ sizeof(ntopbuf)), ipr->ipr_origin);
+ close(s);
+ return 1;
+ }
+
+ close(s);
+ return 0;
+#else
+ ipr->ipr_vltime = DEF_ADVVALIDLIFETIME;
+ ipr->ipr_pltime = DEF_ADVPREFERREDLIFETIME;
+ ipr->ipr_raf_onlink = 1;
+ ipr->ipr_raf_auto = 1;
+ return 0;
+#endif
+}
+
+void
+make_prefix(struct rainfo *rai, int ifindex, struct in6_addr *addr, int plen)
+{
+ struct in6_prefixreq ipr;
+
+ memset(&ipr, 0, sizeof(ipr));
+ if (if_indextoname(ifindex, ipr.ipr_name) == NULL) {
+ syslog(LOG_ERR, "<%s> Prefix added interface No.%d doesn't"
+ "exist. This should not happen! %s", __FUNCTION__,
+ ifindex, strerror(errno));
+ exit(1);
+ }
+ ipr.ipr_prefix.sin6_len = sizeof(ipr.ipr_prefix);
+ ipr.ipr_prefix.sin6_family = AF_INET6;
+ ipr.ipr_prefix.sin6_addr = *addr;
+ ipr.ipr_plen = plen;
+
+ if (init_prefix(&ipr))
+ return; /* init failed by some error */
+ add_prefix(rai, &ipr);
+}
+
+void
+make_packet(struct rainfo *rainfo)
+{
+ size_t packlen, lladdroptlen = 0;
+ char *buf;
+ struct nd_router_advert *ra;
+ struct nd_opt_prefix_info *ndopt_pi;
+ struct nd_opt_mtu *ndopt_mtu;
+#ifdef MIP6
+ struct nd_opt_advinterval *ndopt_advint;
+ struct nd_opt_homeagent_info *ndopt_hai;
+#endif
+ struct nd_opt_route_info *ndopt_rti;
+ struct prefix *pfx;
+ struct rtinfo *rti;
+
+ /* calculate total length */
+ packlen = sizeof(struct nd_router_advert);
+ if (rainfo->advlinkopt) {
+ if ((lladdroptlen = lladdropt_length(rainfo->sdl)) == 0) {
+ syslog(LOG_INFO,
+ "<%s> link-layer address option has"
+ " null length on %s."
+ " Treat as not included.",
+ __FUNCTION__, rainfo->ifname);
+ rainfo->advlinkopt = 0;
+ }
+ packlen += lladdroptlen;
+ }
+ if (rainfo->pfxs)
+ packlen += sizeof(struct nd_opt_prefix_info) * rainfo->pfxs;
+ if (rainfo->linkmtu)
+ packlen += sizeof(struct nd_opt_mtu);
+#ifdef MIP6
+ if (mobileip6 && rainfo->maxinterval)
+ packlen += sizeof(struct nd_opt_advinterval);
+ if (mobileip6 && rainfo->hatime)
+ packlen += sizeof(struct nd_opt_homeagent_info);
+#endif
+#ifdef ND_OPT_ROUTE_INFO
+ for (rti = rainfo->route.next; rti != &rainfo->route; rti = rti->next)
+ packlen += sizeof(struct nd_opt_route_info) +
+ ((rti->prefixlen + 0x3f) >> 6) * 8;
+#endif
+
+ /* allocate memory for the packet */
+ if ((buf = malloc(packlen)) == NULL) {
+ syslog(LOG_ERR,
+ "<%s> can't get enough memory for an RA packet",
+ __FUNCTION__);
+ exit(1);
+ }
+ if (rainfo->ra_data) {
+ /* free the previous packet */
+ free(rainfo->ra_data);
+ rainfo->ra_data = NULL;
+ }
+ rainfo->ra_data = buf;
+ /* XXX: what if packlen > 576? */
+ rainfo->ra_datalen = packlen;
+
+ /*
+ * construct the packet
+ */
+ ra = (struct nd_router_advert *)buf;
+ ra->nd_ra_type = ND_ROUTER_ADVERT;
+ ra->nd_ra_code = 0;
+ ra->nd_ra_cksum = 0;
+ ra->nd_ra_curhoplimit = (u_int8_t)(0xff & rainfo->hoplimit);
+ ra->nd_ra_flags_reserved = 0; /* just in case */
+ /*
+ * XXX: the router preference field, which is a 2-bit field, should be
+ * initialized before other fields.
+ */
+ ra->nd_ra_flags_reserved = 0xff & rainfo->rtpref;
+ ra->nd_ra_flags_reserved |=
+ rainfo->managedflg ? ND_RA_FLAG_MANAGED : 0;
+ ra->nd_ra_flags_reserved |=
+ rainfo->otherflg ? ND_RA_FLAG_OTHER : 0;
+#ifdef MIP6
+ ra->nd_ra_flags_reserved |=
+ rainfo->haflg ? ND_RA_FLAG_HA : 0;
+#endif
+ ra->nd_ra_router_lifetime = htons(rainfo->lifetime);
+ ra->nd_ra_reachable = htonl(rainfo->reachabletime);
+ ra->nd_ra_retransmit = htonl(rainfo->retranstimer);
+ buf += sizeof(*ra);
+
+ if (rainfo->advlinkopt) {
+ lladdropt_fill(rainfo->sdl, (struct nd_opt_hdr *)buf);
+ buf += lladdroptlen;
+ }
+
+ if (rainfo->linkmtu) {
+ ndopt_mtu = (struct nd_opt_mtu *)buf;
+ ndopt_mtu->nd_opt_mtu_type = ND_OPT_MTU;
+ ndopt_mtu->nd_opt_mtu_len = 1;
+ ndopt_mtu->nd_opt_mtu_reserved = 0;
+ ndopt_mtu->nd_opt_mtu_mtu = htonl(rainfo->linkmtu);
+ buf += sizeof(struct nd_opt_mtu);
+ }
+
+#ifdef MIP6
+ if (mobileip6 && rainfo->maxinterval) {
+ ndopt_advint = (struct nd_opt_advinterval *)buf;
+ ndopt_advint->nd_opt_adv_type = ND_OPT_ADVINTERVAL;
+ ndopt_advint->nd_opt_adv_len = 1;
+ ndopt_advint->nd_opt_adv_reserved = 0;
+ ndopt_advint->nd_opt_adv_interval = htonl(rainfo->maxinterval *
+ 1000);
+ buf += sizeof(struct nd_opt_advinterval);
+ }
+#endif
+
+#ifdef MIP6
+ if (rainfo->hatime) {
+ ndopt_hai = (struct nd_opt_homeagent_info *)buf;
+ ndopt_hai->nd_opt_hai_type = ND_OPT_HOMEAGENT_INFO;
+ ndopt_hai->nd_opt_hai_len = 1;
+ ndopt_hai->nd_opt_hai_reserved = 0;
+ ndopt_hai->nd_opt_hai_preference = htons(rainfo->hapref);
+ ndopt_hai->nd_opt_hai_lifetime = htons(rainfo->hatime);
+ buf += sizeof(struct nd_opt_homeagent_info);
+ }
+#endif
+
+ for (pfx = rainfo->prefix.next;
+ pfx != &rainfo->prefix; pfx = pfx->next) {
+ u_int32_t vltime, pltime;
+ struct timeval now;
+
+ ndopt_pi = (struct nd_opt_prefix_info *)buf;
+ ndopt_pi->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION;
+ ndopt_pi->nd_opt_pi_len = 4;
+ ndopt_pi->nd_opt_pi_prefix_len = pfx->prefixlen;
+ ndopt_pi->nd_opt_pi_flags_reserved = 0;
+ if (pfx->onlinkflg)
+ ndopt_pi->nd_opt_pi_flags_reserved |=
+ ND_OPT_PI_FLAG_ONLINK;
+ if (pfx->autoconfflg)
+ ndopt_pi->nd_opt_pi_flags_reserved |=
+ ND_OPT_PI_FLAG_AUTO;
+#ifdef MIP6
+ if (pfx->routeraddr)
+ ndopt_pi->nd_opt_pi_flags_reserved |=
+ ND_OPT_PI_FLAG_ROUTER;
+#endif
+ if (pfx->timer)
+ vltime = 0;
+ else {
+ if (pfx->vltimeexpire || pfx->pltimeexpire)
+ gettimeofday(&now, NULL);
+ if (pfx->vltimeexpire == 0)
+ vltime = pfx->validlifetime;
+ else
+ vltime = (pfx->vltimeexpire > now.tv_sec) ?
+ pfx->vltimeexpire - now.tv_sec : 0;
+ }
+ if (pfx->timer)
+ pltime = 0;
+ else {
+ if (pfx->pltimeexpire == 0)
+ pltime = pfx->preflifetime;
+ else
+ pltime = (pfx->pltimeexpire > now.tv_sec) ?
+ pfx->pltimeexpire - now.tv_sec : 0;
+ }
+ if (vltime < pltime) {
+ /*
+ * this can happen if vltime is decrement but pltime
+ * is not.
+ */
+ pltime = vltime;
+ }
+ ndopt_pi->nd_opt_pi_valid_time = htonl(vltime);
+ ndopt_pi->nd_opt_pi_preferred_time = htonl(pltime);
+ ndopt_pi->nd_opt_pi_reserved2 = 0;
+ ndopt_pi->nd_opt_pi_prefix = pfx->prefix;
+
+ buf += sizeof(struct nd_opt_prefix_info);
+ }
+
+#ifdef ND_OPT_ROUTE_INFO
+ for (rti = rainfo->route.next; rti != &rainfo->route; rti = rti->next) {
+ u_int8_t psize = (rti->prefixlen + 0x3f) >> 6;
+
+ ndopt_rti = (struct nd_opt_route_info *)buf;
+ ndopt_rti->nd_opt_rti_type = ND_OPT_ROUTE_INFO;
+ ndopt_rti->nd_opt_rti_len = 1 + psize;
+ ndopt_rti->nd_opt_rti_prefixlen = rti->prefixlen;
+ ndopt_rti->nd_opt_rti_flags = 0xff & rti->rtpref;
+ ndopt_rti->nd_opt_rti_lifetime = rti->ltime;
+ memcpy(ndopt_rti + 1, &rti->prefix, psize * 8);
+ buf += sizeof(struct nd_opt_route_info) + psize * 8;
+ }
+#endif
+
+ return;
+}
+
+static int
+getinet6sysctl(int code)
+{
+ int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, 0 };
+ int value;
+ size_t size;
+
+ mib[3] = code;
+ size = sizeof(value);
+ if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), &value, &size, NULL, 0)
+ < 0) {
+ syslog(LOG_ERR, "<%s>: failed to get ip6 sysctl(%d): %s",
+ __FUNCTION__, code,
+ strerror(errno));
+ return(-1);
+ }
+ else
+ return(value);
+}
diff --git a/usr.sbin/rtadvd/config.h b/usr.sbin/rtadvd/config.h
new file mode 100644
index 0000000..0bb137b
--- /dev/null
+++ b/usr.sbin/rtadvd/config.h
@@ -0,0 +1,38 @@
+/* $FreeBSD$ */
+/* $KAME: config.h,v 1.3 2000/05/16 13:34:13 itojun Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern void getconfig __P((char *));
+extern void delete_prefix __P((struct prefix *));
+extern void invalidate_prefix __P((struct prefix *));
+extern void update_prefix __P((struct prefix *));
+extern void make_prefix __P((struct rainfo *, int, struct in6_addr *, int));
+extern void make_packet __P((struct rainfo *));
diff --git a/usr.sbin/rtadvd/dump.c b/usr.sbin/rtadvd/dump.c
new file mode 100644
index 0000000..cb451f6
--- /dev/null
+++ b/usr.sbin/rtadvd/dump.c
@@ -0,0 +1,251 @@
+/* $FreeBSD$ */
+/* $KAME: dump.c,v 1.16 2001/03/21 17:41:13 jinmei Exp $ */
+
+/*
+ * Copyright (C) 2000 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+
+#include <net/if.h>
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+#include <net/if_var.h>
+#endif /* __FreeBSD__ >= 3 */
+#include <net/if_dl.h>
+
+#include <netinet/in.h>
+
+/* XXX: the following two are non-standard include files */
+#include <netinet6/in6_var.h>
+#include <netinet6/nd6.h>
+
+#include <arpa/inet.h>
+
+#include <time.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <string.h>
+#include <errno.h>
+
+#include "rtadvd.h"
+#include "timer.h"
+#include "if.h"
+#include "dump.h"
+
+static FILE *fp;
+
+extern struct rainfo *ralist;
+
+static char *ether_str __P((struct sockaddr_dl *));
+static void if_dump __P((void));
+
+#ifdef __FreeBSD__ /* XXX: see PORTABILITY */
+#define LONGLONG "%qu"
+#else
+#define LONGLONG "%llu"
+#endif
+
+static char *rtpref_str[] = {
+ "medium", /* 00 */
+ "high", /* 01 */
+ "rsv", /* 10 */
+ "low" /* 11 */
+};
+
+static char *
+ether_str(sdl)
+ struct sockaddr_dl *sdl;
+{
+ static char ebuf[32];
+ u_char *cp;
+
+ if (sdl->sdl_alen && sdl->sdl_alen > 5) {
+ cp = (u_char *)LLADDR(sdl);
+ sprintf(ebuf, "%x:%x:%x:%x:%x:%x",
+ cp[0], cp[1], cp[2], cp[3], cp[4], cp[5]);
+ }
+ else {
+ sprintf(ebuf, "NONE");
+ }
+
+ return(ebuf);
+}
+
+static void
+if_dump()
+{
+ struct rainfo *rai;
+ struct prefix *pfx;
+ char prefixbuf[INET6_ADDRSTRLEN];
+ int first;
+ struct timeval now;
+
+ gettimeofday(&now, NULL); /* XXX: unused in most cases */
+ for (rai = ralist; rai; rai = rai->next) {
+ fprintf(fp, "%s:\n", rai->ifname);
+
+ fprintf(fp, " Status: %s\n",
+ (iflist[rai->ifindex]->ifm_flags & IFF_UP) ? "UP" :
+ "DOWN");
+
+ /* control information */
+ if (rai->lastsent.tv_sec) {
+ /* note that ctime() appends CR by itself */
+ fprintf(fp, " Last RA sent: %s",
+ ctime((time_t *)&rai->lastsent.tv_sec));
+ }
+ if (rai->timer) {
+ fprintf(fp, " Next RA will be sent: %s",
+ ctime((time_t *)&rai->timer->tm.tv_sec));
+ }
+ else
+ fprintf(fp, " RA timer is stopped");
+ fprintf(fp, " waits: %d, initcount: %d\n",
+ rai->waiting, rai->initcounter);
+
+ /* statistics */
+ fprintf(fp,
+ " statistics: RA(out/in/inconsistent): "
+ LONGLONG "/" LONGLONG "/" LONGLONG ", ",
+ (unsigned long long)rai->raoutput,
+ (unsigned long long)rai->rainput,
+ (unsigned long long)rai->rainconsistent);
+ fprintf(fp, "RS(input): " LONGLONG "\n",
+ (unsigned long long)rai->rsinput);
+
+ /* interface information */
+ if (rai->advlinkopt)
+ fprintf(fp, " Link-layer address: %s\n",
+ ether_str(rai->sdl));
+ fprintf(fp, " MTU: %d\n", rai->phymtu);
+
+ /* Router configuration variables */
+ fprintf(fp,
+ " DefaultLifetime: %d, MaxAdvInterval: %d, "
+ "MinAdvInterval: %d\n",
+ rai->lifetime, rai->maxinterval, rai->mininterval);
+ fprintf(fp, " Flags: %s%s%s, ",
+ rai->managedflg ? "M" : "", rai->otherflg ? "O" : "",
+#ifdef MIP6
+ rai->haflg ? "H" :
+#endif
+ "");
+ fprintf(fp, "Preference: %s, ",
+ rtpref_str[(rai->rtpref >> 3) & 0xff]);
+ fprintf(fp, "MTU: %d\n", rai->linkmtu);
+ fprintf(fp, " ReachableTime: %d, RetransTimer: %d, "
+ "CurHopLimit: %d\n", rai->reachabletime,
+ rai->retranstimer, rai->hoplimit);
+#ifdef MIP6
+ fprintf(fp, " HAPreference: %d, HALifetime: %d\n",
+ rai->hapref, rai->hatime);
+#endif
+
+ if (rai->clockskew)
+ fprintf(fp, " Clock skew: %ldsec\n",
+ rai->clockskew);
+ for (first = 1, pfx = rai->prefix.next; pfx != &rai->prefix;
+ pfx = pfx->next) {
+ if (first) {
+ fprintf(fp, " Prefixes:\n");
+ first = 0;
+ }
+ fprintf(fp, " %s/%d(",
+ inet_ntop(AF_INET6, &pfx->prefix,
+ prefixbuf, sizeof(prefixbuf)),
+ pfx->prefixlen);
+ switch(pfx->origin) {
+ case PREFIX_FROM_KERNEL:
+ fprintf(fp, "KERNEL, ");
+ break;
+ case PREFIX_FROM_CONFIG:
+ fprintf(fp, "CONFIG, ");
+ break;
+ case PREFIX_FROM_DYNAMIC:
+ fprintf(fp, "DYNAMIC, ");
+ break;
+ }
+ if (pfx->validlifetime == ND6_INFINITE_LIFETIME)
+ fprintf(fp, "vltime: infinity");
+ else
+ fprintf(fp, "vltime: %ld",
+ (long)pfx->validlifetime);
+ if (pfx->vltimeexpire != 0)
+ fprintf(fp, "(decr,expire %ld), ", (long)
+ pfx->vltimeexpire > now.tv_sec ?
+ pfx->vltimeexpire - now.tv_sec : 0);
+ else
+ fprintf(fp, ", ");
+ if (pfx->preflifetime == ND6_INFINITE_LIFETIME)
+ fprintf(fp, "pltime: infinity");
+ else
+ fprintf(fp, "pltime: %ld",
+ (long)pfx->preflifetime);
+ if (pfx->pltimeexpire != 0)
+ fprintf(fp, "(decr,expire %ld), ", (long)
+ pfx->pltimeexpire > now.tv_sec ?
+ pfx->pltimeexpire - now.tv_sec : 0);
+ else
+ fprintf(fp, ", ");
+ fprintf(fp, "flags: %s%s%s",
+ pfx->onlinkflg ? "L" : "",
+ pfx->autoconfflg ? "A" : "",
+#ifdef MIP6
+ pfx->routeraddr ? "R" :
+#endif
+ "");
+ if (pfx->timer) {
+ struct timeval *rest;
+
+ rest = rtadvd_timer_rest(pfx->timer);
+ if (rest) { /* XXX: what if not? */
+ fprintf(fp, ", expire in: %ld",
+ (long)rest->tv_sec);
+ }
+ }
+ fprintf(fp, ")\n");
+ }
+ }
+}
+
+void
+rtadvd_dump_file(dumpfile)
+ char *dumpfile;
+{
+ if ((fp = fopen(dumpfile, "w")) == NULL) {
+ syslog(LOG_WARNING, "<%s> open a dump file(%s)",
+ __FUNCTION__, dumpfile);
+ return;
+ }
+
+ if_dump();
+
+ fclose(fp);
+}
diff --git a/usr.sbin/rtadvd/dump.h b/usr.sbin/rtadvd/dump.h
new file mode 100644
index 0000000..cc3b472
--- /dev/null
+++ b/usr.sbin/rtadvd/dump.h
@@ -0,0 +1,33 @@
+/* $FreeBSD$ */
+/* $KAME: dump.h,v 1.1 2000/05/23 11:31:26 itojun Exp $ */
+
+/*
+ * Copyright (C) 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern void rtadvd_dump_file __P((char *));
diff --git a/usr.sbin/rtadvd/if.c b/usr.sbin/rtadvd/if.c
new file mode 100644
index 0000000..c6aa2e6
--- /dev/null
+++ b/usr.sbin/rtadvd/if.c
@@ -0,0 +1,589 @@
+/* $FreeBSD$ */
+/* $KAME: if.c,v 1.17 2001/01/21 15:27:30 itojun Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <net/if_types.h>
+#ifdef __FreeBSD__
+# include <net/ethernet.h>
+#endif
+#include <ifaddrs.h>
+#ifdef __NetBSD__
+#include <net/if_ether.h>
+#endif
+#include <net/route.h>
+#include <net/if_dl.h>
+#include <netinet/in.h>
+#include <netinet/icmp6.h>
+#ifdef __bsdi__
+# include <netinet/if_ether.h>
+#endif
+#ifdef __OpenBSD__
+#include <netinet/if_ether.h>
+#endif
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include "rtadvd.h"
+#include "if.h"
+
+#define ROUNDUP(a, size) \
+ (((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a))
+
+#define NEXT_SA(ap) (ap) = (struct sockaddr *) \
+ ((caddr_t)(ap) + ((ap)->sa_len ? ROUNDUP((ap)->sa_len,\
+ sizeof(u_long)) :\
+ sizeof(u_long)))
+
+struct if_msghdr **iflist;
+int iflist_init_ok;
+size_t ifblock_size;
+char *ifblock;
+
+static void get_iflist __P((char **buf, size_t *size));
+static void parse_iflist __P((struct if_msghdr ***ifmlist_p, char *buf,
+ size_t bufsize));
+
+static void
+get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
+{
+ int i;
+
+ for (i = 0; i < RTAX_MAX; i++) {
+ if (addrs & (1 << i)) {
+ rti_info[i] = sa;
+ NEXT_SA(sa);
+ }
+ else
+ rti_info[i] = NULL;
+ }
+}
+
+struct sockaddr_dl *
+if_nametosdl(char *name)
+{
+ int mib[6] = {CTL_NET, AF_ROUTE, 0, 0, NET_RT_IFLIST, 0};
+ char *buf, *next, *lim;
+ size_t len;
+ struct if_msghdr *ifm;
+ struct sockaddr *sa, *rti_info[RTAX_MAX];
+ struct sockaddr_dl *sdl = NULL, *ret_sdl;
+
+ if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0)
+ return(NULL);
+ if ((buf = malloc(len)) == NULL)
+ return(NULL);
+ if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
+ free(buf);
+ return(NULL);
+ }
+
+ lim = buf + len;
+ for (next = buf; next < lim; next += ifm->ifm_msglen) {
+ ifm = (struct if_msghdr *)next;
+ if (ifm->ifm_type == RTM_IFINFO) {
+ sa = (struct sockaddr *)(ifm + 1);
+ get_rtaddrs(ifm->ifm_addrs, sa, rti_info);
+ if ((sa = rti_info[RTAX_IFP]) != NULL) {
+ if (sa->sa_family == AF_LINK) {
+ sdl = (struct sockaddr_dl *)sa;
+ if (strlen(name) != sdl->sdl_nlen)
+ continue; /* not same len */
+ if (strncmp(&sdl->sdl_data[0],
+ name,
+ sdl->sdl_nlen) == 0) {
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (next == lim) {
+ /* search failed */
+ free(buf);
+ return(NULL);
+ }
+
+ if ((ret_sdl = malloc(sdl->sdl_len)) == NULL)
+ return(NULL);
+ memcpy((caddr_t)ret_sdl, (caddr_t)sdl, sdl->sdl_len);
+ return(ret_sdl);
+}
+
+int
+if_getmtu(char *name)
+{
+ struct ifaddrs *ifap, *ifa;
+ struct if_data *ifd;
+ u_long mtu = 0;
+
+ if (getifaddrs(&ifap) < 0)
+ return(0);
+ for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+ if (strcmp(ifa->ifa_name, name) == 0) {
+ ifd = ifa->ifa_data;
+ if (ifd)
+ mtu = ifd->ifi_mtu;
+ break;
+ }
+ }
+ freeifaddrs(ifap);
+
+#ifdef SIOCGIFMTU /* XXX: this ifdef may not be necessary */
+ if (mtu == 0) {
+ struct ifreq ifr;
+ int s;
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
+ return(0);
+
+ ifr.ifr_addr.sa_family = AF_INET6;
+ strncpy(ifr.ifr_name, name,
+ sizeof(ifr.ifr_name));
+ if (ioctl(s, SIOCGIFMTU, (caddr_t)&ifr) < 0) {
+ close(s);
+ return(0);
+ }
+ close(s);
+
+ mtu = ifr.ifr_mtu;
+ }
+#endif
+
+ return(mtu);
+}
+
+/* give interface index and its old flags, then new flags returned */
+int
+if_getflags(int ifindex, int oifflags)
+{
+ struct ifreq ifr;
+ int s;
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ syslog(LOG_ERR, "<%s> socket: %s", __FUNCTION__,
+ strerror(errno));
+ return (oifflags & ~IFF_UP);
+ }
+
+ if_indextoname(ifindex, ifr.ifr_name);
+ if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
+ syslog(LOG_ERR, "<%s> ioctl:SIOCGIFFLAGS: failed for %s",
+ __FUNCTION__, ifr.ifr_name);
+ close(s);
+ return (oifflags & ~IFF_UP);
+ }
+ close(s);
+ return (ifr.ifr_flags);
+}
+
+#define ROUNDUP8(a) (1 + (((a) - 1) | 7))
+int
+lladdropt_length(struct sockaddr_dl *sdl)
+{
+ switch(sdl->sdl_type) {
+ case IFT_ETHER:
+ return(ROUNDUP8(ETHER_ADDR_LEN + 2));
+ default:
+ return(0);
+ }
+}
+
+void
+lladdropt_fill(struct sockaddr_dl *sdl, struct nd_opt_hdr *ndopt)
+{
+ char *addr;
+
+ ndopt->nd_opt_type = ND_OPT_SOURCE_LINKADDR; /* fixed */
+
+ switch(sdl->sdl_type) {
+ case IFT_ETHER:
+ ndopt->nd_opt_len = (ROUNDUP8(ETHER_ADDR_LEN + 2)) >> 3;
+ addr = (char *)(ndopt + 1);
+ memcpy(addr, LLADDR(sdl), ETHER_ADDR_LEN);
+ break;
+ default:
+ syslog(LOG_ERR,
+ "<%s> unsupported link type(%d)",
+ __FUNCTION__, sdl->sdl_type);
+ exit(1);
+ }
+
+ return;
+}
+
+int
+rtbuf_len()
+{
+ size_t len;
+
+ int mib[6] = {CTL_NET, AF_ROUTE, 0, AF_INET6, NET_RT_DUMP, 0};
+
+ if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0)
+ return(-1);
+
+ return(len);
+}
+
+#define FILTER_MATCH(type, filter) ((0x1 << type) & filter)
+#define SIN6(s) ((struct sockaddr_in6 *)(s))
+#define SDL(s) ((struct sockaddr_dl *)(s))
+char *
+get_next_msg(char *buf, char *lim, int ifindex, size_t *lenp, int filter)
+{
+ struct rt_msghdr *rtm;
+ struct ifa_msghdr *ifam;
+ struct sockaddr *sa, *dst, *gw, *ifa, *rti_info[RTAX_MAX];
+
+ *lenp = 0;
+ for (rtm = (struct rt_msghdr *)buf;
+ rtm < (struct rt_msghdr *)lim;
+ rtm = (struct rt_msghdr *)(((char *)rtm) + rtm->rtm_msglen)) {
+ /* just for safety */
+ if (!rtm->rtm_msglen) {
+ syslog(LOG_WARNING, "<%s> rtm_msglen is 0 "
+ "(buf=%p lim=%p rtm=%p)", __FUNCTION__,
+ buf, lim, rtm);
+ break;
+ }
+ if (FILTER_MATCH(rtm->rtm_type, filter) == 0) {
+ continue;
+ }
+
+ switch (rtm->rtm_type) {
+ case RTM_GET:
+ case RTM_ADD:
+ case RTM_DELETE:
+ /* address related checks */
+ sa = (struct sockaddr *)(rtm + 1);
+ get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
+ if ((dst = rti_info[RTAX_DST]) == NULL ||
+ dst->sa_family != AF_INET6)
+ continue;
+
+ if (IN6_IS_ADDR_LINKLOCAL(&SIN6(dst)->sin6_addr) ||
+ IN6_IS_ADDR_MULTICAST(&SIN6(dst)->sin6_addr))
+ continue;
+
+ if ((gw = rti_info[RTAX_GATEWAY]) == NULL ||
+ gw->sa_family != AF_LINK)
+ continue;
+ if (ifindex && SDL(gw)->sdl_index != ifindex)
+ continue;
+
+ if (rti_info[RTAX_NETMASK] == NULL)
+ continue;
+
+ /* found */
+ *lenp = rtm->rtm_msglen;
+ return (char *)rtm;
+ /* NOTREACHED */
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ ifam = (struct ifa_msghdr *)rtm;
+
+ /* address related checks */
+ sa = (struct sockaddr *)(ifam + 1);
+ get_rtaddrs(ifam->ifam_addrs, sa, rti_info);
+ if ((ifa = rti_info[RTAX_IFA]) == NULL ||
+ (ifa->sa_family != AF_INET &&
+ ifa->sa_family != AF_INET6))
+ continue;
+
+ if (ifa->sa_family == AF_INET6 &&
+ (IN6_IS_ADDR_LINKLOCAL(&SIN6(ifa)->sin6_addr) ||
+ IN6_IS_ADDR_MULTICAST(&SIN6(ifa)->sin6_addr)))
+ continue;
+
+ if (ifindex && ifam->ifam_index != ifindex)
+ continue;
+
+ /* found */
+ *lenp = ifam->ifam_msglen;
+ return (char *)rtm;
+ /* NOTREACHED */
+ case RTM_IFINFO:
+ /* found */
+ *lenp = rtm->rtm_msglen;
+ return (char *)rtm;
+ /* NOTREACHED */
+ }
+ }
+
+ return (char *)rtm;
+}
+#undef FILTER_MATCH
+
+struct in6_addr *
+get_addr(char *buf)
+{
+ struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
+ struct sockaddr *sa, *rti_info[RTAX_MAX];
+
+ sa = (struct sockaddr *)(rtm + 1);
+ get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
+
+ return(&SIN6(rti_info[RTAX_DST])->sin6_addr);
+}
+
+int
+get_rtm_ifindex(char *buf)
+{
+ struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
+ struct sockaddr *sa, *rti_info[RTAX_MAX];
+
+ sa = (struct sockaddr *)(rtm + 1);
+ get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
+
+ return(((struct sockaddr_dl *)rti_info[RTAX_GATEWAY])->sdl_index);
+}
+
+int
+get_ifm_ifindex(char *buf)
+{
+ struct if_msghdr *ifm = (struct if_msghdr *)buf;
+
+ return ((int)ifm->ifm_index);
+}
+
+int
+get_ifam_ifindex(char *buf)
+{
+ struct ifa_msghdr *ifam = (struct ifa_msghdr *)buf;
+
+ return ((int)ifam->ifam_index);
+}
+
+int
+get_ifm_flags(char *buf)
+{
+ struct if_msghdr *ifm = (struct if_msghdr *)buf;
+
+ return (ifm->ifm_flags);
+}
+
+int
+get_prefixlen(char *buf)
+{
+ struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
+ struct sockaddr *sa, *rti_info[RTAX_MAX];
+ u_char *p, *lim;
+
+ sa = (struct sockaddr *)(rtm + 1);
+ get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
+ sa = rti_info[RTAX_NETMASK];
+
+ p = (u_char *)(&SIN6(sa)->sin6_addr);
+ lim = (u_char *)sa + sa->sa_len;
+ return prefixlen(p, lim);
+}
+
+int
+prefixlen(u_char *p, u_char *lim)
+{
+ int masklen;
+
+ for (masklen = 0; p < lim; p++) {
+ switch (*p) {
+ case 0xff:
+ masklen += 8;
+ break;
+ case 0xfe:
+ masklen += 7;
+ break;
+ case 0xfc:
+ masklen += 6;
+ break;
+ case 0xf8:
+ masklen += 5;
+ break;
+ case 0xf0:
+ masklen += 4;
+ break;
+ case 0xe0:
+ masklen += 3;
+ break;
+ case 0xc0:
+ masklen += 2;
+ break;
+ case 0x80:
+ masklen += 1;
+ break;
+ case 0x00:
+ break;
+ default:
+ return(-1);
+ }
+ }
+
+ return(masklen);
+}
+
+int
+rtmsg_type(char *buf)
+{
+ struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
+
+ return(rtm->rtm_type);
+}
+
+int
+rtmsg_len(char *buf)
+{
+ struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
+
+ return(rtm->rtm_msglen);
+}
+
+int
+ifmsg_len(char *buf)
+{
+ struct if_msghdr *ifm = (struct if_msghdr *)buf;
+
+ return(ifm->ifm_msglen);
+}
+
+/*
+ * alloc buffer and get if_msghdrs block from kernel,
+ * and put them into the buffer
+ */
+static void
+get_iflist(char **buf, size_t *size)
+{
+ int mib[6];
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = AF_INET6;
+ mib[4] = NET_RT_IFLIST;
+ mib[5] = 0;
+
+ if (sysctl(mib, 6, NULL, size, NULL, 0) < 0) {
+ syslog(LOG_ERR, "<%s> sysctl: iflist size get failed",
+ __FUNCTION__);
+ exit(1);
+ }
+ if ((*buf = malloc(*size)) == NULL) {
+ syslog(LOG_ERR, "<%s> malloc failed", __FUNCTION__);
+ exit(1);
+ }
+ if (sysctl(mib, 6, *buf, size, NULL, 0) < 0) {
+ syslog(LOG_ERR, "<%s> sysctl: iflist get failed",
+ __FUNCTION__);
+ exit(1);
+ }
+ return;
+}
+
+/*
+ * alloc buffer and parse if_msghdrs block passed as arg,
+ * and init the buffer as list of pointers ot each of the if_msghdr.
+ */
+static void
+parse_iflist(struct if_msghdr ***ifmlist_p, char *buf, size_t bufsize)
+{
+ int iflentry_size, malloc_size;
+ struct if_msghdr *ifm;
+ struct ifa_msghdr *ifam;
+ char *lim;
+
+ /*
+ * Estimate least size of an iflist entry, to be obtained from kernel.
+ * Should add sizeof(sockaddr) ??
+ */
+ iflentry_size = sizeof(struct if_msghdr);
+ /* roughly estimate max list size of pointers to each if_msghdr */
+ malloc_size = (bufsize/iflentry_size) * sizeof(size_t);
+ if ((*ifmlist_p = (struct if_msghdr **)malloc(malloc_size)) == NULL) {
+ syslog(LOG_ERR, "<%s> malloc failed", __FUNCTION__);
+ exit(1);
+ }
+
+ lim = buf + bufsize;
+ for (ifm = (struct if_msghdr *)buf; ifm < (struct if_msghdr *)lim;) {
+ if (ifm->ifm_msglen == 0) {
+ syslog(LOG_WARNING, "<%s> ifm_msglen is 0 "
+ "(buf=%p lim=%p ifm=%p)", __FUNCTION__,
+ buf, lim, ifm);
+ return;
+ }
+
+ if (ifm->ifm_type == RTM_IFINFO) {
+ (*ifmlist_p)[ifm->ifm_index] = ifm;
+ } else {
+ syslog(LOG_ERR, "out of sync parsing NET_RT_IFLIST\n"
+ "expected %d, got %d\n msglen = %d\n"
+ "buf:%p, ifm:%p, lim:%p\n",
+ RTM_IFINFO, ifm->ifm_type, ifm->ifm_msglen,
+ buf, ifm, lim);
+ exit (1);
+ }
+ for (ifam = (struct ifa_msghdr *)
+ ((char *)ifm + ifm->ifm_msglen);
+ ifam < (struct ifa_msghdr *)lim;
+ ifam = (struct ifa_msghdr *)
+ ((char *)ifam + ifam->ifam_msglen)) {
+ /* just for safety */
+ if (!ifam->ifam_msglen) {
+ syslog(LOG_WARNING, "<%s> ifa_msglen is 0 "
+ "(buf=%p lim=%p ifam=%p)", __FUNCTION__,
+ buf, lim, ifam);
+ return;
+ }
+ if (ifam->ifam_type != RTM_NEWADDR)
+ break;
+ }
+ ifm = (struct if_msghdr *)ifam;
+ }
+}
+
+void
+init_iflist()
+{
+ if (ifblock) {
+ free(ifblock);
+ ifblock_size = 0;
+ }
+ if (iflist)
+ free(iflist);
+ /* get iflist block from kernel */
+ get_iflist(&ifblock, &ifblock_size);
+
+ /* make list of pointers to each if_msghdr */
+ parse_iflist(&iflist, ifblock, ifblock_size);
+}
diff --git a/usr.sbin/rtadvd/if.h b/usr.sbin/rtadvd/if.h
new file mode 100644
index 0000000..23f9a98
--- /dev/null
+++ b/usr.sbin/rtadvd/if.h
@@ -0,0 +1,58 @@
+/* $FreeBSD$ */
+/* $KAME: if.h,v 1.6 2001/01/21 15:37:14 itojun Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define RTADV_TYPE2BITMASK(type) (0x1 << type)
+
+extern struct if_msghdr **iflist;
+extern size_t ifblock_size;
+extern char *ifblock;
+
+struct nd_opt_hdr;
+struct sockaddr_dl *if_nametosdl __P((char *));
+int if_getmtu __P((char *));
+int if_getflags __P((int, int));
+int lladdropt_length __P((struct sockaddr_dl *));
+void lladdropt_fill __P((struct sockaddr_dl *, struct nd_opt_hdr *));
+int rtbuf_len __P((void));
+char *get_next_msg __P((char *, char *, int, size_t *, int));
+struct in6_addr *get_addr __P((char *));
+int get_rtm_ifindex __P((char *));
+int get_ifm_ifindex __P((char *));
+int get_ifam_ifindex __P((char *));
+int get_ifm_flags __P((char *));
+int get_prefixlen __P((char *));
+int prefixlen __P((u_char *, u_char *));
+int rtmsg_type __P((char *));
+int ifmsg_type __P((char *));
+int rtmsg_len __P((char *));
+int ifmsg_len __P((char *));
+void init_iflist __P((void));
diff --git a/usr.sbin/rtadvd/pathnames.h b/usr.sbin/rtadvd/pathnames.h
new file mode 100644
index 0000000..3afee55
--- /dev/null
+++ b/usr.sbin/rtadvd/pathnames.h
@@ -0,0 +1,4 @@
+/* $KAME: pathnames.h,v 1.2 2000/05/16 13:34:13 itojun Exp $ */
+/* $FreeBSD$ */
+
+#define _PATH_RTADVDCONF "/etc/rtadvd.conf"
diff --git a/usr.sbin/rtadvd/rrenum.c b/usr.sbin/rtadvd/rrenum.c
new file mode 100644
index 0000000..a5fbe20
--- /dev/null
+++ b/usr.sbin/rtadvd/rrenum.c
@@ -0,0 +1,488 @@
+/* $FreeBSD$ */
+/* $KAME: rrenum.c,v 1.10 2001/01/21 15:32:16 itojun Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+#include <net/if_var.h>
+#endif /* __FreeBSD__ >= 3 */
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/icmp6.h>
+
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include "rtadvd.h"
+#include "rrenum.h"
+#include "if.h"
+
+#define RR_ISSET_SEGNUM(segnum_bits, segnum) \
+ ((((segnum_bits)[(segnum) >> 5]) & (1 << ((segnum) & 31))) != 0)
+#define RR_SET_SEGNUM(segnum_bits, segnum) \
+ (((segnum_bits)[(segnum) >> 5]) |= (1 << ((segnum) & 31)))
+
+struct rr_operation {
+ u_long rro_seqnum;
+ u_long rro_segnum_bits[8];
+};
+
+static struct rr_operation rro;
+static int rr_rcvifindex;
+static int rrcmd2pco[RPM_PCO_MAX] = {
+ 0,
+ SIOCAIFPREFIX_IN6,
+ SIOCCIFPREFIX_IN6,
+ SIOCSGIFPREFIX_IN6
+};
+static int s = -1;
+
+/*
+ * Check validity of a Prefix Control Operation(PCO).
+ * Return 0 on success, 1 on failure.
+ */
+static int
+rr_pco_check(int len, struct rr_pco_match *rpm)
+{
+ struct rr_pco_use *rpu, *rpulim;
+ int checklen;
+
+ /* rpm->rpm_len must be (4N * 3) as router-renum-05.txt */
+ if ((rpm->rpm_len - 3) < 0 || /* must be at least 3 */
+ (rpm->rpm_len - 3) & 0x3) { /* must be multiple of 4 */
+ syslog(LOG_WARNING, "<%s> rpm_len %d is not 4N * 3",
+ __FUNCTION__, rpm->rpm_len);
+ return 1;
+ }
+ /* rpm->rpm_code must be valid value */
+ switch(rpm->rpm_code) {
+ case RPM_PCO_ADD:
+ case RPM_PCO_CHANGE:
+ case RPM_PCO_SETGLOBAL:
+ break;
+ default:
+ syslog(LOG_WARNING, "<%s> unknown rpm_code %d", __FUNCTION__,
+ rpm->rpm_code);
+ return 1;
+ }
+ /* rpm->rpm_matchlen must be 0 to 128 inclusive */
+ if (rpm->rpm_matchlen > 128) {
+ syslog(LOG_WARNING, "<%s> rpm_matchlen %d is over 128",
+ __FUNCTION__, rpm->rpm_matchlen);
+ return 1;
+ }
+
+ /*
+ * rpu->rpu_uselen, rpu->rpu_keeplen, and sum of them must be
+ * between 0 and 128 inclusive
+ */
+ for (rpu = (struct rr_pco_use *)(rpm + 1),
+ rpulim = (struct rr_pco_use *)((char *)rpm + len);
+ rpu < rpulim;
+ rpu += 1) {
+ checklen = rpu->rpu_uselen;
+ checklen += rpu->rpu_keeplen;
+ /*
+ * omit these check, because either of rpu_uselen
+ * and rpu_keeplen is unsigned char
+ * (128 > rpu_uselen > 0)
+ * (128 > rpu_keeplen > 0)
+ * (rpu_uselen + rpu_keeplen > 0)
+ */
+ if (checklen > 128) {
+ syslog(LOG_WARNING, "<%s> sum of rpu_uselen %d and"
+ " rpu_keeplen %d is %d(over 128)",
+ __FUNCTION__, rpu->rpu_uselen,
+ rpu->rpu_keeplen,
+ rpu->rpu_uselen + rpu->rpu_keeplen);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void
+do_use_prefix(int len, struct rr_pco_match *rpm,
+ struct in6_rrenumreq *irr, int ifindex)
+{
+ struct rr_pco_use *rpu, *rpulim;
+ struct rainfo *rai;
+ struct prefix *pp;
+
+ rpu = (struct rr_pco_use *)(rpm + 1);
+ rpulim = (struct rr_pco_use *)((char *)rpm + len);
+
+ if (rpu == rpulim) { /* no use prefix */
+ if (rpm->rpm_code == RPM_PCO_ADD)
+ return;
+
+ irr->irr_u_uselen = 0;
+ irr->irr_u_keeplen = 0;
+ irr->irr_raf_mask_onlink = 0;
+ irr->irr_raf_mask_auto = 0;
+ irr->irr_vltime = 0;
+ irr->irr_pltime = 0;
+ memset(&irr->irr_flags, 0, sizeof(irr->irr_flags));
+ irr->irr_useprefix.sin6_len = 0; /* let it mean, no addition */
+ irr->irr_useprefix.sin6_family = 0;
+ irr->irr_useprefix.sin6_addr = in6addr_any;
+ if (ioctl(s, rrcmd2pco[rpm->rpm_code], (caddr_t)irr) < 0 &&
+ errno != EADDRNOTAVAIL)
+ syslog(LOG_ERR, "<%s> ioctl: %s", __FUNCTION__,
+ strerror(errno));
+ return;
+ }
+
+ for (rpu = (struct rr_pco_use *)(rpm + 1),
+ rpulim = (struct rr_pco_use *)((char *)rpm + len);
+ rpu < rpulim;
+ rpu += 1) {
+ /* init in6_rrenumreq fields */
+ irr->irr_u_uselen = rpu->rpu_uselen;
+ irr->irr_u_keeplen = rpu->rpu_keeplen;
+ irr->irr_raf_mask_onlink =
+ (rpu->rpu_ramask & ICMP6_RR_PCOUSE_RAFLAGS_ONLINK);
+ irr->irr_raf_mask_auto =
+ (rpu->rpu_ramask & ICMP6_RR_PCOUSE_RAFLAGS_AUTO);
+ irr->irr_vltime = ntohl(rpu->rpu_vltime);
+ irr->irr_pltime = ntohl(rpu->rpu_pltime);
+ irr->irr_raf_onlink =
+ (rpu->rpu_raflags & ICMP6_RR_PCOUSE_RAFLAGS_ONLINK) == 0 ? 0 : 1;
+ irr->irr_raf_auto =
+ (rpu->rpu_raflags & ICMP6_RR_PCOUSE_RAFLAGS_AUTO) == 0 ? 0 : 1;
+ irr->irr_rrf_decrvalid =
+ (rpu->rpu_flags & ICMP6_RR_PCOUSE_FLAGS_DECRVLTIME) == 0 ? 0 : 1;
+ irr->irr_rrf_decrprefd =
+ (rpu->rpu_flags & ICMP6_RR_PCOUSE_FLAGS_DECRPLTIME) == 0 ? 0 : 1;
+ irr->irr_useprefix.sin6_len = sizeof(irr->irr_useprefix);
+ irr->irr_useprefix.sin6_family = AF_INET6;
+ irr->irr_useprefix.sin6_addr = rpu->rpu_prefix;
+
+ if (ioctl(s, rrcmd2pco[rpm->rpm_code], (caddr_t)irr) < 0 &&
+ errno != EADDRNOTAVAIL)
+ syslog(LOG_ERR, "<%s> ioctl: %s", __FUNCTION__,
+ strerror(errno));
+
+ /* very adhoc: should be rewritten */
+ if (rpm->rpm_code == RPM_PCO_CHANGE &&
+ IN6_ARE_ADDR_EQUAL(&rpm->rpm_prefix, &rpu->rpu_prefix) &&
+ rpm->rpm_matchlen == rpu->rpu_uselen &&
+ rpu->rpu_uselen == rpu->rpu_keeplen) {
+ if ((rai = if_indextorainfo(ifindex)) == NULL)
+ continue; /* non-advertising IF */
+
+ for (pp = rai->prefix.next; pp != &rai->prefix;
+ pp = pp->next) {
+ struct timeval now;
+
+ if (prefix_match(&pp->prefix, pp->prefixlen,
+ &rpm->rpm_prefix,
+ rpm->rpm_matchlen)) {
+ /* change parameters */
+ pp->validlifetime = ntohl(rpu->rpu_vltime);
+ pp->preflifetime = ntohl(rpu->rpu_pltime);
+ if (irr->irr_rrf_decrvalid) {
+ gettimeofday(&now, 0);
+ pp->vltimeexpire =
+ now.tv_sec + pp->validlifetime;
+ } else
+ pp->vltimeexpire = 0;
+ if (irr->irr_rrf_decrprefd) {
+ gettimeofday(&now, 0);
+ pp->pltimeexpire =
+ now.tv_sec + pp->preflifetime;
+ } else
+ pp->pltimeexpire = 0;
+ }
+ }
+ }
+ }
+}
+
+/*
+ * process a Prefix Control Operation(PCO).
+ * return 0 on success, 1 on failure
+ */
+static int
+do_pco(struct icmp6_router_renum *rr, int len, struct rr_pco_match *rpm)
+{
+ int ifindex = 0;
+ struct in6_rrenumreq irr;
+
+ if ((rr_pco_check(len, rpm) != NULL))
+ return 1;
+
+ if (s == -1 && (s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ syslog(LOG_ERR, "<%s> socket: %s", __FUNCTION__,
+ strerror(errno));
+ exit(1);
+ }
+
+ memset(&irr, 0, sizeof(irr));
+ irr.irr_origin = PR_ORIG_RR;
+ irr.irr_m_len = rpm->rpm_matchlen;
+ irr.irr_m_minlen = rpm->rpm_minlen;
+ irr.irr_m_maxlen = rpm->rpm_maxlen;
+ irr.irr_matchprefix.sin6_len = sizeof(irr.irr_matchprefix);
+ irr.irr_matchprefix.sin6_family = AF_INET6;
+ irr.irr_matchprefix.sin6_addr = rpm->rpm_prefix;
+
+ while (if_indextoname(++ifindex, irr.irr_name)) {
+ /*
+ * if ICMP6_RR_FLAGS_FORCEAPPLY(A flag) is 0 and IFF_UP is off,
+ * the interface is not applied
+ */
+ if ((rr->rr_flags & ICMP6_RR_FLAGS_FORCEAPPLY) == 0 &&
+ (iflist[ifindex]->ifm_flags & IFF_UP) == 0)
+ continue;
+ /* TODO: interface scope check */
+ do_use_prefix(len, rpm, &irr, ifindex);
+ }
+ if (errno == ENXIO)
+ return 0;
+ else if (errno) {
+ syslog(LOG_ERR, "<%s> if_indextoname: %s", __FUNCTION__,
+ strerror(errno));
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * call do_pco() for each Prefix Control Operations(PCOs) in a received
+ * Router Renumbering Command packet.
+ * return 0 on success, 1 on failure
+ */
+static int
+do_rr(int len, struct icmp6_router_renum *rr)
+{
+ struct rr_pco_match *rpm;
+ char *cp, *lim;
+
+ lim = (char *)rr + len;
+ cp = (char *)(rr + 1);
+ len -= sizeof(struct icmp6_router_renum);
+
+ /* get iflist block from kernel again, to get up-to-date information */
+ init_iflist();
+
+ while (cp < lim) {
+ int rpmlen;
+
+ rpm = (struct rr_pco_match *)cp;
+ if (len < sizeof(struct rr_pco_match)) {
+ tooshort:
+ syslog(LOG_ERR, "<%s> pkt too short. left len = %d. "
+ "gabage at end of pkt?", __FUNCTION__, len);
+ return 1;
+ }
+ rpmlen = rpm->rpm_len << 3;
+ if (len < rpmlen)
+ goto tooshort;
+
+ if (do_pco(rr, rpmlen, rpm)) {
+ syslog(LOG_WARNING, "<%s> invalid PCO", __FUNCTION__);
+ goto next;
+ }
+
+ next:
+ cp += rpmlen;
+ len -= rpmlen;
+ }
+
+ return 0;
+}
+
+/*
+ * check validity of a router renumbering command packet
+ * return 0 on success, 1 on failure
+ */
+static int
+rr_command_check(int len, struct icmp6_router_renum *rr, struct in6_addr *from,
+ struct in6_addr *dst)
+{
+ u_char ntopbuf[INET6_ADDRSTRLEN];
+
+ /* omit rr minimal length check. hope kernel have done it. */
+ /* rr_command length check */
+ if (len < (sizeof(struct icmp6_router_renum) +
+ sizeof(struct rr_pco_match))) {
+ syslog(LOG_ERR, "<%s> rr_command len %d is too short",
+ __FUNCTION__, len);
+ return 1;
+ }
+
+ /* destination check. only for multicast. omit unicast check. */
+ if (IN6_IS_ADDR_MULTICAST(dst) && !IN6_IS_ADDR_MC_LINKLOCAL(dst) &&
+ !IN6_IS_ADDR_MC_SITELOCAL(dst)) {
+ syslog(LOG_ERR, "<%s> dst mcast addr %s is illegal",
+ __FUNCTION__,
+ inet_ntop(AF_INET6, dst, ntopbuf, INET6_ADDRSTRLEN));
+ return 1;
+ }
+
+ /* seqnum and segnum check */
+ if (rro.rro_seqnum > rr->rr_seqnum) {
+ syslog(LOG_WARNING,
+ "<%s> rcvd old seqnum %d from %s",
+ __FUNCTION__, (u_int32_t)ntohl(rr->rr_seqnum),
+ inet_ntop(AF_INET6, from, ntopbuf, INET6_ADDRSTRLEN));
+ return 1;
+ }
+ if (rro.rro_seqnum == rr->rr_seqnum &&
+ (rr->rr_flags & ICMP6_RR_FLAGS_TEST) == 0 &&
+ RR_ISSET_SEGNUM(rro.rro_segnum_bits, rr->rr_segnum)) {
+ if ((rr->rr_flags & ICMP6_RR_FLAGS_REQRESULT) != 0)
+ syslog(LOG_WARNING,
+ "<%s> rcvd duped segnum %d from %s",
+ __FUNCTION__, rr->rr_segnum,
+ inet_ntop(AF_INET6, from, ntopbuf,
+ INET6_ADDRSTRLEN));
+ return 0;
+ }
+
+ /* update seqnum */
+ if (rro.rro_seqnum != rr->rr_seqnum) {
+ /* then must be "<" */
+
+ /* init rro_segnum_bits */
+ memset(rro.rro_segnum_bits, 0,
+ sizeof(rro.rro_segnum_bits));
+ }
+ rro.rro_seqnum = rr->rr_seqnum;
+
+ return 0;
+}
+
+static void
+rr_command_input(int len, struct icmp6_router_renum *rr,
+ struct in6_addr *from, struct in6_addr *dst)
+{
+ /* rr_command validity check */
+ if (rr_command_check(len, rr, from, dst))
+ goto failed;
+ if ((rr->rr_flags & (ICMP6_RR_FLAGS_TEST|ICMP6_RR_FLAGS_REQRESULT)) ==
+ ICMP6_RR_FLAGS_TEST)
+ return;
+
+ /* do router renumbering */
+ if (do_rr(len, rr)) {
+ goto failed;
+ }
+
+ /* update segnum */
+ RR_SET_SEGNUM(rro.rro_segnum_bits, rr->rr_segnum);
+
+ return;
+
+ failed:
+ syslog(LOG_ERR, "<%s> received RR was invalid", __FUNCTION__);
+ return;
+}
+
+void
+rr_input(int len, struct icmp6_router_renum *rr, struct in6_pktinfo *pi,
+ struct sockaddr_in6 *from, struct in6_addr *dst)
+{
+ u_char ntopbuf[2][INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
+
+ syslog(LOG_DEBUG,
+ "<%s> RR received from %s to %s on %s",
+ __FUNCTION__,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf[0], INET6_ADDRSTRLEN),
+ inet_ntop(AF_INET6, &dst, ntopbuf[1], INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+
+ /* packet validation based on Section 4.1 of RFC2894 */
+ if (len < sizeof(struct icmp6_router_renum)) {
+ syslog(LOG_NOTICE,
+ "<%s>: RR short message (size %d) from %s to %s on %s",
+ __FUNCTION__, len,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf[0], INET6_ADDRSTRLEN),
+ inet_ntop(AF_INET6, &dst, ntopbuf[1], INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+
+ /*
+ * If the IPv6 destination address is neither an All Routers multicast
+ * address [AARCH] nor one of the receiving router's unicast addresses,
+ * the message MUST be discarded and SHOULD be logged to network
+ * management.
+ * We rely on the kernel input routine for unicast addresses, and thus
+ * check multicast destinations only.
+ */
+ if (IN6_IS_ADDR_MULTICAST(&pi->ipi6_addr) &&
+ !IN6_ARE_ADDR_EQUAL(&in6a_site_allrouters, &pi->ipi6_addr)) {
+ syslog(LOG_NOTICE,
+ "<%s>: RR message with invalid destination (%s) "
+ "from %s on %s",
+ __FUNCTION__,
+ inet_ntop(AF_INET6, &dst, ntopbuf[0], INET6_ADDRSTRLEN),
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf[1], INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+
+ rr_rcvifindex = pi->ipi6_ifindex;
+
+ switch (rr->rr_code) {
+ case ICMP6_ROUTER_RENUMBERING_COMMAND:
+ rr_command_input(len, rr, &from->sin6_addr, dst);
+ /* TODO: send reply msg */
+ break;
+ case ICMP6_ROUTER_RENUMBERING_RESULT:
+ /* RESULT will be processed by rrenumd */
+ break;
+ case ICMP6_ROUTER_RENUMBERING_SEQNUM_RESET:
+ /* TODO: sequence number reset */
+ break;
+ default:
+ syslog(LOG_ERR, "<%s> received unknown code %d",
+ __FUNCTION__, rr->rr_code);
+ break;
+
+ }
+
+ return;
+}
diff --git a/usr.sbin/rtadvd/rrenum.h b/usr.sbin/rtadvd/rrenum.h
new file mode 100644
index 0000000..ce94015
--- /dev/null
+++ b/usr.sbin/rtadvd/rrenum.h
@@ -0,0 +1,34 @@
+/* $FreeBSD$ */
+/* $KAME: rrenum.h,v 1.3 2001/01/21 15:37:14 itojun Exp $ */
+
+/*
+ * Copyright (C) 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+void rr_input __P((int, struct icmp6_router_renum *, struct in6_pktinfo *,
+ struct sockaddr_in6 *, struct in6_addr *));
diff --git a/usr.sbin/rtadvd/rtadvd.8 b/usr.sbin/rtadvd/rtadvd.8
new file mode 100644
index 0000000..519d735
--- /dev/null
+++ b/usr.sbin/rtadvd/rtadvd.8
@@ -0,0 +1,201 @@
+.\" $FreeBSD$
+.\" $KAME: rtadvd.8,v 1.17 2001/02/04 05:34:38 jinmei Exp $
+.\"
+.\" Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the project nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd May 17, 1998
+.Dt RTADVD 8
+.Os
+.Sh NAME
+.Nm rtadvd
+.Nd router advertisement daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl dDfMRs
+.Op Fl c Ar configfile
+.Ar interface ...
+.Sh DESCRIPTION
+.Nm
+sends router advertisement packets to the specified
+.Ar interfaces .
+.Pp
+The program will daemonize itself on invocation.
+It will then send router advertisement packets periodically, as well
+as in response to router solicitation messages sent by end hosts.
+.Pp
+Router advertisements can be configured on a per-interface basis, as
+described in
+.Xr rtadvd.conf 5 .
+.Pp
+If there is no configuration file entry for an interface,
+or if the configuration file does not exist altogether,
+.Nm
+sets all the parameters to their default values.
+In particular,
+.Nm
+reads all the interface routes from the routing table and advertises
+them as on-link prefixes.
+.Pp
+.Nm
+also watches the routing table.
+If an interface direct route is
+added on an advertising interface and no static prefixes are
+specified by the configuration file,
+.Nm
+adds the corresponding prefix to its advertising list.
+.Pp
+Similarly, when an interface direct route is deleted,
+.Nm
+will start advertising the prefixes with zero valid and preferred
+lifetimes to help the receiving hosts switch to a new prefix when
+renumbering.
+Note, however, that the zero valid lifetime cannot invalidate the
+autoconfigured addresses at a receiving host immediately.
+According to the specification, the host will retain the address
+for a certain period, which will typically be two hours.
+The zero lifetimes rather intend to make the address deprecated,
+indicating that a new non-deprecated address should be used as the
+source address of a new connection.
+This behavior will last for two hours.
+Then
+.Nm
+will completely remove the prefix from the advertising list,
+and succeeding advertisements will not contain the prefix information.
+.Pp
+Moreover, if the status of an advertising interface changes,
+.Nm
+will start or stop sending router advertisements according
+to the latest status.
+.Pp
+The
+.Fl s
+option may be used to disable this behavior;
+.Nm
+will not watch the routing table and the whole functionality described
+above will be suppressed.
+.Pp
+Basically, hosts MUST NOT send Router Advertisement messages at any
+time (RFC 2461, Section 6.2.3).
+However, it would sometimes be useful to allow hosts to advertise some
+parameters such as prefix information and link MTU.
+Thus,
+.Nm
+can be invoked if router lifetime is explicitly set zero on every
+advertising interface.
+.Pp
+The command line options are:
+.Bl -tag -width indent
+.\"
+.It Fl c
+Specify an alternate location,
+.Ar configfile ,
+for the configuration file.
+By default,
+.Pa /etc/rtadvd.conf
+is used.
+.It Fl d
+Print debugging information.
+.It Fl D
+Even more debugging information is printed.
+.It Fl f
+Foreground mode (useful when debugging).
+.It Fl M
+Specify an interface to join the all-routers site-local multicast group.
+By default,
+.Nm
+tries to join the first advertising interface appeared in the command
+line.
+This option has meaning only with the
+.Fl R
+option, which enables routing renumbering protocol support.
+.\".It Fl m
+.\"Enables mobile IPv6 support.
+.\"This changes the content of router advertisement option, as well as
+.\"permitted configuration directives.
+.It Fl R
+Accept router renumbering requests.
+If you enable it, certain IPsec setup is suggested for security reasons.
+On KAME-based systems,
+.Xr rrenumd 8
+generates router renumbering request packets.
+This option is currently disabled, and is ignored by
+.Nm
+with a warning message.
+.It Fl s
+Do not add or delete prefixes dynamically.
+Only statically configured prefixes, if any, will be advertised.
+.El
+.Pp
+Upon receipt of signal
+.Dv SIGUSR1 ,
+.Nm
+will dump the current internal state into
+.Pa /var/run/rtadvd.dump .
+.Pp
+Use
+.Dv SIGTERM
+to kill
+.Nm
+gracefully.
+In this case,
+.Nm
+will transmit router advertisement with router lifetime 0
+to all the interfaces
+(in accordance with RFC2461 6.2.5).
+.Sh DIAGNOSTICS
+.Ex -std
+.Sh FILES
+.Bl -tag -width Pa -compact
+.It Pa /etc/rtadvd.conf
+The default configuration file.
+.It Pa /var/run/rtadvd.pid
+contains the pid of the currently running
+.Nm .
+.It Pa /var/run/rtadvd.dump
+in which
+.Nm
+dumps its internal state.
+.El
+.Sh SEE ALSO
+.Xr rtadvd.conf 5 ,
+.Xr rrenumd 8 ,
+.Xr rtsol 8
+.Sh HISTORY
+The
+.Nm
+command first appeared in WIDE Hydrangea IPv6 protocol stack kit.
+.Sh CAVEAT
+There used to be some text that recommended users not to let
+.Nm
+advertise Router Advertisement messages on an upstream link to avoid
+undesirable
+.Xr icmp6 4
+redirect messages.
+However, based on the later discussion in the IETF ipng working group,
+all routers should rather advertise the messages regardless of
+the network topology, in order to ensure reachability.
diff --git a/usr.sbin/rtadvd/rtadvd.c b/usr.sbin/rtadvd/rtadvd.c
new file mode 100644
index 0000000..9e8c81e
--- /dev/null
+++ b/usr.sbin/rtadvd/rtadvd.c
@@ -0,0 +1,1613 @@
+/* $FreeBSD$ */
+/* $KAME: rtadvd.c,v 1.50 2001/02/04 06:15:15 itojun Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/time.h>
+#include <sys/queue.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+#include <netinet/in.h>
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet/icmp6.h>
+
+#include <arpa/inet.h>
+
+#include <time.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <err.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include "rtadvd.h"
+#include "rrenum.h"
+#include "advcap.h"
+#include "timer.h"
+#include "if.h"
+#include "config.h"
+#include "dump.h"
+
+struct msghdr rcvmhdr;
+static u_char *rcvcmsgbuf;
+static size_t rcvcmsgbuflen;
+static u_char *sndcmsgbuf = NULL;
+static size_t sndcmsgbuflen;
+static int do_dump;
+static int do_die;
+struct msghdr sndmhdr;
+struct iovec rcviov[2];
+struct iovec sndiov[2];
+struct sockaddr_in6 from;
+struct sockaddr_in6 sin6_allnodes = {sizeof(sin6_allnodes), AF_INET6};
+struct in6_addr in6a_site_allrouters;
+static char *dumpfilename = "/var/run/rtadvd.dump"; /* XXX: should be configurable */
+static char *pidfilename = "/var/run/rtadvd.pid"; /* should be configurable */
+static char *mcastif;
+int sock;
+int rtsock = -1;
+#ifdef MIP6
+int mobileip6 = 0;
+#endif
+int accept_rr = 0;
+int dflag = 0, sflag = 0;
+
+u_char *conffile = NULL;
+
+struct rainfo *ralist = NULL;
+struct nd_optlist {
+ struct nd_optlist *next;
+ struct nd_opt_hdr *opt;
+};
+union nd_opts {
+ struct nd_opt_hdr *nd_opt_array[7];
+ struct {
+ struct nd_opt_hdr *zero;
+ struct nd_opt_hdr *src_lladdr;
+ struct nd_opt_hdr *tgt_lladdr;
+ struct nd_opt_prefix_info *pi;
+ struct nd_opt_rd_hdr *rh;
+ struct nd_opt_mtu *mtu;
+ struct nd_optlist *list;
+ } nd_opt_each;
+};
+#define nd_opts_src_lladdr nd_opt_each.src_lladdr
+#define nd_opts_tgt_lladdr nd_opt_each.tgt_lladdr
+#define nd_opts_pi nd_opt_each.pi
+#define nd_opts_rh nd_opt_each.rh
+#define nd_opts_mtu nd_opt_each.mtu
+#define nd_opts_list nd_opt_each.list
+
+#define NDOPT_FLAG_SRCLINKADDR 0x1
+#define NDOPT_FLAG_TGTLINKADDR 0x2
+#define NDOPT_FLAG_PREFIXINFO 0x4
+#define NDOPT_FLAG_RDHDR 0x8
+#define NDOPT_FLAG_MTU 0x10
+
+u_int32_t ndopt_flags[] = {
+ 0, NDOPT_FLAG_SRCLINKADDR, NDOPT_FLAG_TGTLINKADDR,
+ NDOPT_FLAG_PREFIXINFO, NDOPT_FLAG_RDHDR, NDOPT_FLAG_MTU
+};
+
+int main __P((int, char *[]));
+static void set_die __P((int));
+static void die __P((void));
+static void sock_open __P((void));
+static void rtsock_open __P((void));
+static void rtadvd_input __P((void));
+static void rs_input __P((int, struct nd_router_solicit *,
+ struct in6_pktinfo *, struct sockaddr_in6 *));
+static void ra_input __P((int, struct nd_router_advert *,
+ struct in6_pktinfo *, struct sockaddr_in6 *));
+static int prefix_check __P((struct nd_opt_prefix_info *, struct rainfo *,
+ struct sockaddr_in6 *));
+static int nd6_options __P((struct nd_opt_hdr *, int,
+ union nd_opts *, u_int32_t));
+static void free_ndopts __P((union nd_opts *));
+static void ra_output __P((struct rainfo *));
+static void rtmsg_input __P((void));
+static void rtadvd_set_dump_file __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ fd_set fdset;
+ int maxfd = 0;
+ struct timeval *timeout;
+ int i, ch;
+ int fflag = 0;
+ FILE *pidfp;
+ pid_t pid;
+
+ openlog("rtadvd", LOG_NDELAY|LOG_PID, LOG_DAEMON);
+
+ /* get command line options and arguments */
+#ifdef MIP6
+#define OPTIONS "c:dDfM:mRs"
+#else
+#define OPTIONS "c:dDfM:Rs"
+#endif
+ while ((ch = getopt(argc, argv, OPTIONS)) != -1) {
+#undef OPTIONS
+ switch (ch) {
+ case 'c':
+ conffile = optarg;
+ break;
+ case 'd':
+ dflag = 1;
+ break;
+ case 'D':
+ dflag = 2;
+ break;
+ case 'f':
+ fflag = 1;
+ break;
+ case 'M':
+ mcastif = optarg;
+ break;
+#ifdef MIP6
+ case 'm':
+ mobileip6 = 1;
+ break;
+#endif
+ case 'R':
+ fprintf(stderr, "rtadvd: "
+ "the -R option is currently ignored.\n");
+ /* accept_rr = 1; */
+ /* run anyway... */
+ break;
+ case 's':
+ sflag = 1;
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc == 0) {
+ fprintf(stderr,
+#ifdef MIP6
+ "usage: rtadvd [-dDfMmRs] [-c conffile] "
+#else
+ "usage: rtadvd [-dDfMRs] [-c conffile] "
+#endif
+ "interfaces...\n");
+ exit(1);
+ }
+
+ /* set log level */
+ if (dflag == 0)
+ (void)setlogmask(LOG_UPTO(LOG_ERR));
+ if (dflag == 1)
+ (void)setlogmask(LOG_UPTO(LOG_INFO));
+
+ /* timer initialization */
+ rtadvd_timer_init();
+
+ /* random value initialization */
+ srandom((u_long)time(NULL));
+
+ /* get iflist block from kernel */
+ init_iflist();
+
+ while (argc--)
+ getconfig(*argv++);
+
+ if (inet_pton(AF_INET6, ALLNODES, &sin6_allnodes.sin6_addr) != 1) {
+ fprintf(stderr, "fatal: inet_pton failed\n");
+ exit(1);
+ }
+ sock_open();
+
+ if (!fflag)
+ daemon(1, 0);
+
+ /* record the current PID */
+ pid = getpid();
+ if ((pidfp = fopen(pidfilename, "w")) == NULL) {
+ syslog(LOG_ERR,
+ "<%s> failed to open a log file(%s), run anyway.",
+ __FUNCTION__, pidfilename);
+ } else {
+ fprintf(pidfp, "%d\n", pid);
+ fclose(pidfp);
+ }
+
+ FD_ZERO(&fdset);
+ FD_SET(sock, &fdset);
+ maxfd = sock;
+ if (sflag == 0) {
+ rtsock_open();
+ FD_SET(rtsock, &fdset);
+ if (rtsock > sock)
+ maxfd = rtsock;
+ } else
+ rtsock = -1;
+
+ signal(SIGTERM, (void *)set_die);
+ signal(SIGUSR1, (void *)rtadvd_set_dump_file);
+
+ while (1) {
+ struct fd_set select_fd = fdset; /* reinitialize */
+
+ if (do_dump) { /* SIGUSR1 */
+ do_dump = 0;
+ rtadvd_dump_file(dumpfilename);
+ }
+
+ if (do_die) {
+ die();
+ /*NOTREACHED*/
+ }
+
+ /* timer expiration check and reset the timer */
+ timeout = rtadvd_check_timer();
+
+ if (timeout != NULL) {
+ syslog(LOG_DEBUG,
+ "<%s> set timer to %ld:%ld. waiting for "
+ "inputs or timeout", __FUNCTION__,
+ (long int)timeout->tv_sec,
+ (long int)timeout->tv_usec);
+ } else {
+ syslog(LOG_DEBUG,
+ "<%s> there's no timer. waiting for inputs",
+ __FUNCTION__);
+ }
+
+ if ((i = select(maxfd + 1, &select_fd,
+ NULL, NULL, timeout)) < 0) {
+ /* EINTR would occur upon SIGUSR1 for status dump */
+ if (errno != EINTR)
+ syslog(LOG_ERR, "<%s> select: %s",
+ __FUNCTION__, strerror(errno));
+ continue;
+ }
+ if (i == 0) /* timeout */
+ continue;
+ if (rtsock != -1 && FD_ISSET(rtsock, &select_fd))
+ rtmsg_input();
+ if (FD_ISSET(sock, &select_fd))
+ rtadvd_input();
+ }
+ exit(0); /* NOTREACHED */
+}
+
+static void
+rtadvd_set_dump_file()
+{
+ do_dump = 1;
+}
+
+static void
+set_die(sig)
+ int sig;
+{
+ do_die = 1;
+}
+
+static void
+die()
+{
+ struct rainfo *ra;
+ int i;
+ const int retrans = MAX_FINAL_RTR_ADVERTISEMENTS;
+
+ if (dflag > 1) {
+ syslog(LOG_DEBUG, "<%s> cease to be an advertising router\n",
+ __FUNCTION__);
+ }
+
+ for (ra = ralist; ra; ra = ra->next) {
+ ra->lifetime = 0;
+ make_packet(ra);
+ }
+ for (i = 0; i < retrans; i++) {
+ for (ra = ralist; ra; ra = ra->next)
+ ra_output(ra);
+ sleep(MIN_DELAY_BETWEEN_RAS);
+ }
+ exit(0);
+ /*NOTREACHED*/
+}
+
+static void
+rtmsg_input()
+{
+ int n, type, ifindex = 0, plen;
+ size_t len;
+ char msg[2048], *next, *lim;
+ u_char ifname[IF_NAMESIZE];
+ struct prefix *prefix;
+ struct rainfo *rai;
+ struct in6_addr *addr;
+ char addrbuf[INET6_ADDRSTRLEN];
+
+ n = read(rtsock, msg, sizeof(msg));
+ if (dflag > 1) {
+ syslog(LOG_DEBUG, "<%s> received a routing message "
+ "(type = %d, len = %d)", __FUNCTION__, rtmsg_type(msg), n);
+ }
+ if (n > rtmsg_len(msg)) {
+ /*
+ * This usually won't happen for messages received on
+ * a routing socket.
+ */
+ if (dflag > 1)
+ syslog(LOG_DEBUG,
+ "<%s> received data length is larger than "
+ "1st routing message len. multiple messages? "
+ "read %d bytes, but 1st msg len = %d",
+ __FUNCTION__, n, rtmsg_len(msg));
+#if 0
+ /* adjust length */
+ n = rtmsg_len(msg);
+#endif
+ }
+
+ lim = msg + n;
+ for (next = msg; next < lim; next += len) {
+ int oldifflags;
+
+ next = get_next_msg(next, lim, 0, &len,
+ RTADV_TYPE2BITMASK(RTM_ADD) |
+ RTADV_TYPE2BITMASK(RTM_DELETE) |
+ RTADV_TYPE2BITMASK(RTM_NEWADDR) |
+ RTADV_TYPE2BITMASK(RTM_DELADDR) |
+ RTADV_TYPE2BITMASK(RTM_IFINFO));
+ if (len == 0)
+ break;
+ type = rtmsg_type(next);
+ switch (type) {
+ case RTM_ADD:
+ case RTM_DELETE:
+ ifindex = get_rtm_ifindex(next);
+ break;
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ ifindex = get_ifam_ifindex(next);
+ break;
+ case RTM_IFINFO:
+ ifindex = get_ifm_ifindex(next);
+ break;
+ default:
+ /* should not reach here */
+ if (dflag > 1) {
+ syslog(LOG_DEBUG,
+ "<%s:%d> unknown rtmsg %d on %s",
+ __FUNCTION__, __LINE__, type,
+ if_indextoname(ifindex, ifname));
+ }
+ continue;
+ }
+
+ if ((rai = if_indextorainfo(ifindex)) == NULL) {
+ if (dflag > 1) {
+ syslog(LOG_DEBUG,
+ "<%s> route changed on "
+ "non advertising interface(%s)",
+ __FUNCTION__,
+ if_indextoname(ifindex, ifname));
+ }
+ continue;
+ }
+ oldifflags = iflist[ifindex]->ifm_flags;
+
+ switch (type) {
+ case RTM_ADD:
+ /* init ifflags because it may have changed */
+ iflist[ifindex]->ifm_flags =
+ if_getflags(ifindex, iflist[ifindex]->ifm_flags);
+
+ if (sflag)
+ break; /* we aren't interested in prefixes */
+
+ addr = get_addr(msg);
+ plen = get_prefixlen(msg);
+ /* sanity check for plen */
+ /* as RFC2373, prefixlen is at least 4 */
+ if (plen < 4 || plen > 127) {
+ syslog(LOG_INFO, "<%s> new interface route's"
+ "plen %d is invalid for a prefix",
+ __FUNCTION__, plen);
+ break;
+ }
+ prefix = find_prefix(rai, addr, plen);
+ if (prefix) {
+ if (prefix->timer) {
+ /*
+ * If the prefix has been invalidated,
+ * make it available again.
+ */
+ update_prefix(prefix);
+ } else if (dflag > 1) {
+ syslog(LOG_DEBUG,
+ "<%s> new prefix(%s/%d) "
+ "added on %s, "
+ "but it was already in list",
+ __FUNCTION__,
+ inet_ntop(AF_INET6, addr,
+ (char *)addrbuf, INET6_ADDRSTRLEN),
+ plen, rai->ifname);
+ }
+ break;
+ }
+ make_prefix(rai, ifindex, addr, plen);
+ break;
+ case RTM_DELETE:
+ /* init ifflags because it may have changed */
+ iflist[ifindex]->ifm_flags =
+ if_getflags(ifindex, iflist[ifindex]->ifm_flags);
+
+ if (sflag)
+ break;
+
+ addr = get_addr(msg);
+ plen = get_prefixlen(msg);
+ /* sanity check for plen */
+ /* as RFC2373, prefixlen is at least 4 */
+ if (plen < 4 || plen > 127) {
+ syslog(LOG_INFO,
+ "<%s> deleted interface route's "
+ "plen %d is invalid for a prefix",
+ __FUNCTION__, plen);
+ break;
+ }
+ prefix = find_prefix(rai, addr, plen);
+ if (prefix == NULL) {
+ if (dflag > 1) {
+ syslog(LOG_DEBUG,
+ "<%s> prefix(%s/%d) was "
+ "deleted on %s, "
+ "but it was not in list",
+ __FUNCTION__,
+ inet_ntop(AF_INET6, addr,
+ (char *)addrbuf, INET6_ADDRSTRLEN),
+ plen, rai->ifname);
+ }
+ break;
+ }
+ invalidate_prefix(prefix);
+ break;
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ /* init ifflags because it may have changed */
+ iflist[ifindex]->ifm_flags =
+ if_getflags(ifindex, iflist[ifindex]->ifm_flags);
+ break;
+ case RTM_IFINFO:
+ iflist[ifindex]->ifm_flags = get_ifm_flags(next);
+ break;
+ default:
+ /* should not reach here */
+ if (dflag > 1) {
+ syslog(LOG_DEBUG,
+ "<%s:%d> unknown rtmsg %d on %s",
+ __FUNCTION__, __LINE__, type,
+ if_indextoname(ifindex, ifname));
+ }
+ return;
+ }
+
+ /* check if an interface flag is changed */
+ if ((oldifflags & IFF_UP) != 0 && /* UP to DOWN */
+ (iflist[ifindex]->ifm_flags & IFF_UP) == 0) {
+ syslog(LOG_INFO,
+ "<%s> interface %s becomes down. stop timer.",
+ __FUNCTION__, rai->ifname);
+ rtadvd_remove_timer(&rai->timer);
+ } else if ((oldifflags & IFF_UP) == 0 && /* DOWN to UP */
+ (iflist[ifindex]->ifm_flags & IFF_UP) != 0) {
+ syslog(LOG_INFO,
+ "<%s> interface %s becomes up. restart timer.",
+ __FUNCTION__, rai->ifname);
+
+ rai->initcounter = 0; /* reset the counter */
+ rai->waiting = 0; /* XXX */
+ rai->timer = rtadvd_add_timer(ra_timeout,
+ ra_timer_update, rai, rai);
+ ra_timer_update((void *)rai, &rai->timer->tm);
+ rtadvd_set_timer(&rai->timer->tm, rai->timer);
+ }
+ }
+
+ return;
+}
+
+void
+rtadvd_input()
+{
+ int i;
+ int *hlimp = NULL;
+#ifdef OLDRAWSOCKET
+ struct ip6_hdr *ip;
+#endif
+ struct icmp6_hdr *icp;
+ int ifindex = 0;
+ struct cmsghdr *cm;
+ struct in6_pktinfo *pi = NULL;
+ u_char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
+ struct in6_addr dst = in6addr_any;
+
+ /*
+ * Get message. We reset msg_controllen since the field could
+ * be modified if we had received a message before setting
+ * receive options.
+ */
+ rcvmhdr.msg_controllen = rcvcmsgbuflen;
+ if ((i = recvmsg(sock, &rcvmhdr, 0)) < 0)
+ return;
+
+ /* extract optional information via Advanced API */
+ for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&rcvmhdr);
+ cm;
+ cm = (struct cmsghdr *)CMSG_NXTHDR(&rcvmhdr, cm)) {
+ if (cm->cmsg_level == IPPROTO_IPV6 &&
+ cm->cmsg_type == IPV6_PKTINFO &&
+ cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) {
+ pi = (struct in6_pktinfo *)(CMSG_DATA(cm));
+ ifindex = pi->ipi6_ifindex;
+ dst = pi->ipi6_addr;
+ }
+ if (cm->cmsg_level == IPPROTO_IPV6 &&
+ cm->cmsg_type == IPV6_HOPLIMIT &&
+ cm->cmsg_len == CMSG_LEN(sizeof(int)))
+ hlimp = (int *)CMSG_DATA(cm);
+ }
+ if (ifindex == 0) {
+ syslog(LOG_ERR,
+ "<%s> failed to get receiving interface",
+ __FUNCTION__);
+ return;
+ }
+ if (hlimp == NULL) {
+ syslog(LOG_ERR,
+ "<%s> failed to get receiving hop limit",
+ __FUNCTION__);
+ return;
+ }
+
+ /*
+ * If we happen to receive data on an interface which is now down,
+ * just discard the data.
+ */
+ if ((iflist[pi->ipi6_ifindex]->ifm_flags & IFF_UP) == 0) {
+ syslog(LOG_INFO,
+ "<%s> received data on a disabled interface (%s)",
+ __FUNCTION__,
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+
+#ifdef OLDRAWSOCKET
+ if (i < sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr)) {
+ syslog(LOG_ERR,
+ "<%s> packet size(%d) is too short",
+ __FUNCTION__, i);
+ return;
+ }
+
+ ip = (struct ip6_hdr *)rcvmhdr.msg_iov[0].iov_base;
+ icp = (struct icmp6_hdr *)(ip + 1); /* XXX: ext. hdr? */
+#else
+ if (i < sizeof(struct icmp6_hdr)) {
+ syslog(LOG_ERR,
+ "<%s> packet size(%d) is too short",
+ __FUNCTION__, i);
+ return;
+ }
+
+ icp = (struct icmp6_hdr *)rcvmhdr.msg_iov[0].iov_base;
+#endif
+
+ switch (icp->icmp6_type) {
+ case ND_ROUTER_SOLICIT:
+ /*
+ * Message verification - RFC-2461 6.1.1
+ * XXX: these checks must be done in the kernel as well,
+ * but we can't completely rely on them.
+ */
+ if (*hlimp != 255) {
+ syslog(LOG_NOTICE,
+ "<%s> RS with invalid hop limit(%d) "
+ "received from %s on %s",
+ __FUNCTION__, *hlimp,
+ inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
+ INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+ if (icp->icmp6_code) {
+ syslog(LOG_NOTICE,
+ "<%s> RS with invalid ICMP6 code(%d) "
+ "received from %s on %s",
+ __FUNCTION__, icp->icmp6_code,
+ inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
+ INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+ if (i < sizeof(struct nd_router_solicit)) {
+ syslog(LOG_NOTICE,
+ "<%s> RS from %s on %s does not have enough "
+ "length (len = %d)",
+ __FUNCTION__,
+ inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
+ INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf), i);
+ return;
+ }
+ rs_input(i, (struct nd_router_solicit *)icp, pi, &from);
+ break;
+ case ND_ROUTER_ADVERT:
+ /*
+ * Message verification - RFC-2461 6.1.2
+ * XXX: there's a same dilemma as above...
+ */
+ if (*hlimp != 255) {
+ syslog(LOG_NOTICE,
+ "<%s> RA with invalid hop limit(%d) "
+ "received from %s on %s",
+ __FUNCTION__, *hlimp,
+ inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
+ INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+ if (icp->icmp6_code) {
+ syslog(LOG_NOTICE,
+ "<%s> RA with invalid ICMP6 code(%d) "
+ "received from %s on %s",
+ __FUNCTION__, icp->icmp6_code,
+ inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
+ INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+ if (i < sizeof(struct nd_router_advert)) {
+ syslog(LOG_NOTICE,
+ "<%s> RA from %s on %s does not have enough "
+ "length (len = %d)",
+ __FUNCTION__,
+ inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
+ INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf), i);
+ return;
+ }
+ ra_input(i, (struct nd_router_advert *)icp, pi, &from);
+ break;
+ case ICMP6_ROUTER_RENUMBERING:
+ if (accept_rr == 0) {
+ syslog(LOG_ERR, "<%s> received a router renumbering "
+ "message, but not allowed to be accepted",
+ __FUNCTION__);
+ break;
+ }
+ rr_input(i, (struct icmp6_router_renum *)icp, pi, &from,
+ &dst);
+ break;
+ default:
+ /*
+ * Note that this case is POSSIBLE, especially just
+ * after invocation of the daemon. This is because we
+ * could receive message after opening the socket and
+ * before setting ICMP6 type filter(see sock_open()).
+ */
+ syslog(LOG_ERR, "<%s> invalid icmp type(%d)",
+ __FUNCTION__, icp->icmp6_type);
+ return;
+ }
+
+ return;
+}
+
+static void
+rs_input(int len, struct nd_router_solicit *rs,
+ struct in6_pktinfo *pi, struct sockaddr_in6 *from)
+{
+ u_char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
+ union nd_opts ndopts;
+ struct rainfo *ra;
+
+ syslog(LOG_DEBUG,
+ "<%s> RS received from %s on %s",
+ __FUNCTION__,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+
+ /* ND option check */
+ memset(&ndopts, 0, sizeof(ndopts));
+ if (nd6_options((struct nd_opt_hdr *)(rs + 1),
+ len - sizeof(struct nd_router_solicit),
+ &ndopts, NDOPT_FLAG_SRCLINKADDR)) {
+ syslog(LOG_DEBUG,
+ "<%s> ND option check failed for an RS from %s on %s",
+ __FUNCTION__,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+
+ /*
+ * If the IP source address is the unspecified address, there
+ * must be no source link-layer address option in the message.
+ * (RFC-2461 6.1.1)
+ */
+ if (IN6_IS_ADDR_UNSPECIFIED(&from->sin6_addr) &&
+ ndopts.nd_opts_src_lladdr) {
+ syslog(LOG_ERR,
+ "<%s> RS from unspecified src on %s has a link-layer"
+ " address option",
+ __FUNCTION__,
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ goto done;
+ }
+
+ ra = ralist;
+ while (ra != NULL) {
+ if (pi->ipi6_ifindex == ra->ifindex)
+ break;
+ ra = ra->next;
+ }
+ if (ra == NULL) {
+ syslog(LOG_INFO,
+ "<%s> RS received on non advertising interface(%s)",
+ __FUNCTION__,
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ goto done;
+ }
+
+ ra->rsinput++; /* increment statistics */
+
+ /*
+ * Decide whether to send RA according to the rate-limit
+ * consideration.
+ */
+ {
+ long delay; /* must not be greater than 1000000 */
+ struct timeval interval, now, min_delay, tm_tmp, *rest;
+ struct soliciter *sol;
+
+ /*
+ * record sockaddr waiting for RA, if possible
+ */
+ sol = (struct soliciter *)malloc(sizeof(*sol));
+ if (sol) {
+ sol->addr = *from;
+ /*XXX RFC2553 need clarification on flowinfo */
+ sol->addr.sin6_flowinfo = 0;
+ sol->next = ra->soliciter;
+ ra->soliciter = sol->next;
+ }
+
+ /*
+ * If there is already a waiting RS packet, don't
+ * update the timer.
+ */
+ if (ra->waiting++)
+ goto done;
+
+ /*
+ * Compute a random delay. If the computed value
+ * corresponds to a time later than the time the next
+ * multicast RA is scheduled to be sent, ignore the random
+ * delay and send the advertisement at the
+ * already-scheduled time. RFC-2461 6.2.6
+ */
+ delay = random() % MAX_RA_DELAY_TIME;
+ interval.tv_sec = 0;
+ interval.tv_usec = delay;
+ rest = rtadvd_timer_rest(ra->timer);
+ if (TIMEVAL_LT(*rest, interval)) {
+ syslog(LOG_DEBUG,
+ "<%s> random delay is larger than "
+ "the rest of normal timer",
+ __FUNCTION__);
+ interval = *rest;
+ }
+
+ /*
+ * If we sent a multicast Router Advertisement within
+ * the last MIN_DELAY_BETWEEN_RAS seconds, schedule
+ * the advertisement to be sent at a time corresponding to
+ * MIN_DELAY_BETWEEN_RAS plus the random value after the
+ * previous advertisement was sent.
+ */
+ gettimeofday(&now, NULL);
+ TIMEVAL_SUB(&now, &ra->lastsent, &tm_tmp);
+ min_delay.tv_sec = MIN_DELAY_BETWEEN_RAS;
+ min_delay.tv_usec = 0;
+ if (TIMEVAL_LT(tm_tmp, min_delay)) {
+ TIMEVAL_SUB(&min_delay, &tm_tmp, &min_delay);
+ TIMEVAL_ADD(&min_delay, &interval, &interval);
+ }
+ rtadvd_set_timer(&interval, ra->timer);
+ goto done;
+ }
+
+ done:
+ free_ndopts(&ndopts);
+ return;
+}
+
+static void
+ra_input(int len, struct nd_router_advert *ra,
+ struct in6_pktinfo *pi, struct sockaddr_in6 *from)
+{
+ struct rainfo *rai;
+ u_char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
+ union nd_opts ndopts;
+ char *on_off[] = {"OFF", "ON"};
+ u_int32_t reachabletime, retranstimer, mtu;
+ int inconsistent = 0;
+
+ syslog(LOG_DEBUG,
+ "<%s> RA received from %s on %s",
+ __FUNCTION__,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+
+ /* ND option check */
+ memset(&ndopts, 0, sizeof(ndopts));
+ if (nd6_options((struct nd_opt_hdr *)(ra + 1),
+ len - sizeof(struct nd_router_advert),
+ &ndopts, NDOPT_FLAG_SRCLINKADDR |
+ NDOPT_FLAG_PREFIXINFO | NDOPT_FLAG_MTU)) {
+ syslog(LOG_ERR,
+ "<%s> ND option check failed for an RA from %s on %s",
+ __FUNCTION__,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+
+ /*
+ * RA consistency check according to RFC-2461 6.2.7
+ */
+ if ((rai = if_indextorainfo(pi->ipi6_ifindex)) == 0) {
+ syslog(LOG_INFO,
+ "<%s> received RA from %s on non-advertising"
+ " interface(%s)",
+ __FUNCTION__,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ goto done;
+ }
+ rai->rainput++; /* increment statistics */
+
+ /* Cur Hop Limit value */
+ if (ra->nd_ra_curhoplimit && rai->hoplimit &&
+ ra->nd_ra_curhoplimit != rai->hoplimit) {
+ syslog(LOG_INFO,
+ "<%s> CurHopLimit inconsistent on %s:"
+ " %d from %s, %d from us",
+ __FUNCTION__,
+ rai->ifname,
+ ra->nd_ra_curhoplimit,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ rai->hoplimit);
+ inconsistent++;
+ }
+ /* M flag */
+ if ((ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) !=
+ rai->managedflg) {
+ syslog(LOG_INFO,
+ "<%s> M flag inconsistent on %s:"
+ " %s from %s, %s from us",
+ __FUNCTION__,
+ rai->ifname,
+ on_off[!rai->managedflg],
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ on_off[rai->managedflg]);
+ inconsistent++;
+ }
+ /* O flag */
+ if ((ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER) !=
+ rai->otherflg) {
+ syslog(LOG_INFO,
+ "<%s> O flag inconsistent on %s:"
+ " %s from %s, %s from us",
+ __FUNCTION__,
+ rai->ifname,
+ on_off[!rai->otherflg],
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ on_off[rai->otherflg]);
+ inconsistent++;
+ }
+ /* Reachable Time */
+ reachabletime = ntohl(ra->nd_ra_reachable);
+ if (reachabletime && rai->reachabletime &&
+ reachabletime != rai->reachabletime) {
+ syslog(LOG_INFO,
+ "<%s> ReachableTime inconsistent on %s:"
+ " %d from %s, %d from us",
+ __FUNCTION__,
+ rai->ifname,
+ reachabletime,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ rai->reachabletime);
+ inconsistent++;
+ }
+ /* Retrans Timer */
+ retranstimer = ntohl(ra->nd_ra_retransmit);
+ if (retranstimer && rai->retranstimer &&
+ retranstimer != rai->retranstimer) {
+ syslog(LOG_INFO,
+ "<%s> RetranceTimer inconsistent on %s:"
+ " %d from %s, %d from us",
+ __FUNCTION__,
+ rai->ifname,
+ retranstimer,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ rai->retranstimer);
+ inconsistent++;
+ }
+ /* Values in the MTU options */
+ if (ndopts.nd_opts_mtu) {
+ mtu = ntohl(ndopts.nd_opts_mtu->nd_opt_mtu_mtu);
+ if (mtu && rai->linkmtu && mtu != rai->linkmtu) {
+ syslog(LOG_INFO,
+ "<%s> MTU option value inconsistent on %s:"
+ " %d from %s, %d from us",
+ __FUNCTION__,
+ rai->ifname, mtu,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ rai->linkmtu);
+ inconsistent++;
+ }
+ }
+ /* Preferred and Valid Lifetimes for prefixes */
+ {
+ struct nd_optlist *optp = ndopts.nd_opts_list;
+
+ if (ndopts.nd_opts_pi) {
+ if (prefix_check(ndopts.nd_opts_pi, rai, from))
+ inconsistent++;
+ }
+ while (optp) {
+ if (prefix_check((struct nd_opt_prefix_info *)optp->opt,
+ rai, from))
+ inconsistent++;
+ optp = optp->next;
+ }
+ }
+
+ if (inconsistent)
+ rai->rainconsistent++;
+
+ done:
+ free_ndopts(&ndopts);
+ return;
+}
+
+/* return a non-zero value if the received prefix is inconsitent with ours */
+static int
+prefix_check(struct nd_opt_prefix_info *pinfo,
+ struct rainfo *rai, struct sockaddr_in6 *from)
+{
+ u_int32_t preferred_time, valid_time;
+ struct prefix *pp;
+ int inconsistent = 0;
+ u_char ntopbuf[INET6_ADDRSTRLEN], prefixbuf[INET6_ADDRSTRLEN];
+ struct timeval now;
+
+#if 0 /* impossible */
+ if (pinfo->nd_opt_pi_type != ND_OPT_PREFIX_INFORMATION)
+ return(0);
+#endif
+
+ /*
+ * log if the adveritsed prefix has link-local scope(sanity check?)
+ */
+ if (IN6_IS_ADDR_LINKLOCAL(&pinfo->nd_opt_pi_prefix)) {
+ syslog(LOG_INFO,
+ "<%s> link-local prefix %s/%d is advertised "
+ "from %s on %s",
+ __FUNCTION__,
+ inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix,
+ prefixbuf, INET6_ADDRSTRLEN),
+ pinfo->nd_opt_pi_prefix_len,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ rai->ifname);
+ }
+
+ if ((pp = find_prefix(rai, &pinfo->nd_opt_pi_prefix,
+ pinfo->nd_opt_pi_prefix_len)) == NULL) {
+ syslog(LOG_INFO,
+ "<%s> prefix %s/%d from %s on %s is not in our list",
+ __FUNCTION__,
+ inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix,
+ prefixbuf, INET6_ADDRSTRLEN),
+ pinfo->nd_opt_pi_prefix_len,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ rai->ifname);
+ return(0);
+ }
+
+ preferred_time = ntohl(pinfo->nd_opt_pi_preferred_time);
+ if (pp->pltimeexpire) {
+ /*
+ * The lifetime is decremented in real time, so we should
+ * compare the expiration time.
+ * (RFC 2461 Section 6.2.7.)
+ * XXX: can we really expect that all routers on the link
+ * have synchronized clocks?
+ */
+ gettimeofday(&now, NULL);
+ preferred_time += now.tv_sec;
+
+ if (rai->clockskew &&
+ abs(preferred_time - pp->pltimeexpire) > rai->clockskew) {
+ syslog(LOG_INFO,
+ "<%s> prefeerred lifetime for %s/%d"
+ " (decr. in real time) inconsistent on %s:"
+ " %d from %s, %ld from us",
+ __FUNCTION__,
+ inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix,
+ prefixbuf, INET6_ADDRSTRLEN),
+ pinfo->nd_opt_pi_prefix_len,
+ rai->ifname, preferred_time,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ pp->pltimeexpire);
+ inconsistent++;
+ }
+ } else if (preferred_time != pp->preflifetime) {
+ syslog(LOG_INFO,
+ "<%s> prefeerred lifetime for %s/%d"
+ " inconsistent on %s:"
+ " %d from %s, %d from us",
+ __FUNCTION__,
+ inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix,
+ prefixbuf, INET6_ADDRSTRLEN),
+ pinfo->nd_opt_pi_prefix_len,
+ rai->ifname, preferred_time,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ pp->preflifetime);
+ }
+
+ valid_time = ntohl(pinfo->nd_opt_pi_valid_time);
+ if (pp->vltimeexpire) {
+ gettimeofday(&now, NULL);
+ valid_time += now.tv_sec;
+
+ if (rai->clockskew &&
+ abs(valid_time - pp->vltimeexpire) > rai->clockskew) {
+ syslog(LOG_INFO,
+ "<%s> valid lifetime for %s/%d"
+ " (decr. in real time) inconsistent on %s:"
+ " %d from %s, %ld from us",
+ __FUNCTION__,
+ inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix,
+ prefixbuf, INET6_ADDRSTRLEN),
+ pinfo->nd_opt_pi_prefix_len,
+ rai->ifname, preferred_time,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ pp->vltimeexpire);
+ inconsistent++;
+ }
+ } else if (valid_time != pp->validlifetime) {
+ syslog(LOG_INFO,
+ "<%s> valid lifetime for %s/%d"
+ " inconsistent on %s:"
+ " %d from %s, %d from us",
+ __FUNCTION__,
+ inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix,
+ prefixbuf, INET6_ADDRSTRLEN),
+ pinfo->nd_opt_pi_prefix_len,
+ rai->ifname, valid_time,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ pp->validlifetime);
+ inconsistent++;
+ }
+
+ return(inconsistent);
+}
+
+struct prefix *
+find_prefix(struct rainfo *rai, struct in6_addr *prefix, int plen)
+{
+ struct prefix *pp;
+ int bytelen, bitlen;
+
+ for (pp = rai->prefix.next; pp != &rai->prefix; pp = pp->next) {
+ if (plen != pp->prefixlen)
+ continue;
+ bytelen = plen / 8;
+ bitlen = plen % 8;
+ if (memcmp((void *)prefix, (void *)&pp->prefix, bytelen))
+ continue;
+ if (prefix->s6_addr[bytelen] >> (8 - bitlen) ==
+ pp->prefix.s6_addr[bytelen] >> (8 - bitlen))
+ return(pp);
+ }
+
+ return(NULL);
+}
+
+/* check if p0/plen0 matches p1/plen1; return 1 if matches, otherwise 0. */
+int
+prefix_match(struct in6_addr *p0, int plen0,
+ struct in6_addr *p1, int plen1)
+{
+ int bytelen, bitlen;
+
+ if (plen0 < plen1)
+ return(0);
+ bytelen = plen1 / 8;
+ bitlen = plen1 % 8;
+ if (memcmp((void *)p0, (void *)p1, bytelen))
+ return(0);
+ if (p0->s6_addr[bytelen] >> (8 - bitlen) ==
+ p1->s6_addr[bytelen] >> (8 - bitlen))
+ return(1);
+
+ return(0);
+}
+
+static int
+nd6_options(struct nd_opt_hdr *hdr, int limit,
+ union nd_opts *ndopts, u_int32_t optflags)
+{
+ int optlen = 0;
+
+ for (; limit > 0; limit -= optlen) {
+ hdr = (struct nd_opt_hdr *)((caddr_t)hdr + optlen);
+ optlen = hdr->nd_opt_len << 3;
+ if (hdr->nd_opt_len == 0) {
+ syslog(LOG_ERR,
+ "<%s> bad ND option length(0) (type = %d)",
+ __FUNCTION__, hdr->nd_opt_type);
+ goto bad;
+ }
+
+ if (hdr->nd_opt_type > ND_OPT_MTU) {
+ syslog(LOG_INFO,
+ "<%s> unknown ND option(type %d)",
+ __FUNCTION__, hdr->nd_opt_type);
+ continue;
+ }
+
+ if ((ndopt_flags[hdr->nd_opt_type] & optflags) == 0) {
+ syslog(LOG_INFO,
+ "<%s> unexpected ND option(type %d)",
+ __FUNCTION__, hdr->nd_opt_type);
+ continue;
+ }
+
+ switch (hdr->nd_opt_type) {
+ case ND_OPT_SOURCE_LINKADDR:
+ case ND_OPT_TARGET_LINKADDR:
+ case ND_OPT_REDIRECTED_HEADER:
+ case ND_OPT_MTU:
+ if (ndopts->nd_opt_array[hdr->nd_opt_type]) {
+ syslog(LOG_INFO,
+ "<%s> duplicated ND option (type = %d)",
+ __FUNCTION__, hdr->nd_opt_type);
+ }
+ ndopts->nd_opt_array[hdr->nd_opt_type] = hdr;
+ break;
+ case ND_OPT_PREFIX_INFORMATION:
+ {
+ struct nd_optlist *pfxlist;
+
+ if (ndopts->nd_opts_pi == 0) {
+ ndopts->nd_opts_pi =
+ (struct nd_opt_prefix_info *)hdr;
+ continue;
+ }
+ if ((pfxlist = malloc(sizeof(*pfxlist))) == NULL) {
+ syslog(LOG_ERR, "<%s> can't allocate memory",
+ __FUNCTION__);
+ goto bad;
+ }
+ pfxlist->next = ndopts->nd_opts_list;
+ pfxlist->opt = hdr;
+ ndopts->nd_opts_list = pfxlist;
+
+ break;
+ }
+ default: /* impossible */
+ break;
+ }
+ }
+
+ return(0);
+
+ bad:
+ free_ndopts(ndopts);
+
+ return(-1);
+}
+
+static void
+free_ndopts(union nd_opts *ndopts)
+{
+ struct nd_optlist *opt = ndopts->nd_opts_list, *next;
+
+ while (opt) {
+ next = opt->next;
+ free(opt);
+ opt = next;
+ }
+}
+
+void
+sock_open()
+{
+ struct icmp6_filter filt;
+ struct ipv6_mreq mreq;
+ struct rainfo *ra = ralist;
+ int on;
+ /* XXX: should be max MTU attached to the node */
+ static u_char answer[1500];
+
+ rcvcmsgbuflen = CMSG_SPACE(sizeof(struct in6_pktinfo)) +
+ CMSG_SPACE(sizeof(int));
+ rcvcmsgbuf = (u_char *)malloc(rcvcmsgbuflen);
+ if (rcvcmsgbuf == NULL) {
+ syslog(LOG_ERR, "<%s> not enough core", __FUNCTION__);
+ exit(1);
+ }
+
+ sndcmsgbuflen = CMSG_SPACE(sizeof(struct in6_pktinfo)) +
+ CMSG_SPACE(sizeof(int));
+ sndcmsgbuf = (u_char *)malloc(sndcmsgbuflen);
+ if (sndcmsgbuf == NULL) {
+ syslog(LOG_ERR, "<%s> not enough core", __FUNCTION__);
+ exit(1);
+ }
+
+ if ((sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) {
+ syslog(LOG_ERR, "<%s> socket: %s", __FUNCTION__,
+ strerror(errno));
+ exit(1);
+ }
+
+ /* specify to tell receiving interface */
+ on = 1;
+#ifdef IPV6_RECVPKTINFO
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on,
+ sizeof(on)) < 0) {
+ syslog(LOG_ERR, "<%s> IPV6_RECVPKTINFO: %s",
+ __FUNCTION__, strerror(errno));
+ exit(1);
+ }
+#else /* old adv. API */
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO, &on,
+ sizeof(on)) < 0) {
+ syslog(LOG_ERR, "<%s> IPV6_PKTINFO: %s",
+ __FUNCTION__, strerror(errno));
+ exit(1);
+ }
+#endif
+
+ on = 1;
+ /* specify to tell value of hoplimit field of received IP6 hdr */
+#ifdef IPV6_RECVHOPLIMIT
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on,
+ sizeof(on)) < 0) {
+ syslog(LOG_ERR, "<%s> IPV6_RECVHOPLIMIT: %s",
+ __FUNCTION__, strerror(errno));
+ exit(1);
+ }
+#else /* old adv. API */
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_HOPLIMIT, &on,
+ sizeof(on)) < 0) {
+ syslog(LOG_ERR, "<%s> IPV6_HOPLIMIT: %s",
+ __FUNCTION__, strerror(errno));
+ exit(1);
+ }
+#endif
+
+ ICMP6_FILTER_SETBLOCKALL(&filt);
+ ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filt);
+ ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt);
+ if (accept_rr)
+ ICMP6_FILTER_SETPASS(ICMP6_ROUTER_RENUMBERING, &filt);
+ if (setsockopt(sock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
+ sizeof(filt)) < 0) {
+ syslog(LOG_ERR, "<%s> IICMP6_FILTER: %s",
+ __FUNCTION__, strerror(errno));
+ exit(1);
+ }
+
+ /*
+ * join all routers multicast address on each advertising interface.
+ */
+ if (inet_pton(AF_INET6, ALLROUTERS_LINK,
+ &mreq.ipv6mr_multiaddr.s6_addr)
+ != 1) {
+ syslog(LOG_ERR, "<%s> inet_pton failed(library bug?)",
+ __FUNCTION__);
+ exit(1);
+ }
+ while (ra) {
+ mreq.ipv6mr_interface = ra->ifindex;
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq,
+ sizeof(mreq)) < 0) {
+ syslog(LOG_ERR, "<%s> IPV6_JOIN_GROUP(link) on %s: %s",
+ __FUNCTION__, ra->ifname, strerror(errno));
+ exit(1);
+ }
+ ra = ra->next;
+ }
+
+ /*
+ * When attending router renumbering, join all-routers site-local
+ * multicast group.
+ */
+ if (accept_rr) {
+ if (inet_pton(AF_INET6, ALLROUTERS_SITE,
+ &in6a_site_allrouters) != 1) {
+ syslog(LOG_ERR, "<%s> inet_pton failed(library bug?)",
+ __FUNCTION__);
+ exit(1);
+ }
+ mreq.ipv6mr_multiaddr = in6a_site_allrouters;
+ if (mcastif) {
+ if ((mreq.ipv6mr_interface = if_nametoindex(mcastif))
+ == 0) {
+ syslog(LOG_ERR,
+ "<%s> invalid interface: %s",
+ __FUNCTION__, mcastif);
+ exit(1);
+ }
+ } else
+ mreq.ipv6mr_interface = ralist->ifindex;
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP,
+ &mreq, sizeof(mreq)) < 0) {
+ syslog(LOG_ERR,
+ "<%s> IPV6_JOIN_GROUP(site) on %s: %s",
+ __FUNCTION__,
+ mcastif ? mcastif : ralist->ifname,
+ strerror(errno));
+ exit(1);
+ }
+ }
+
+ /* initialize msghdr for receiving packets */
+ rcviov[0].iov_base = (caddr_t)answer;
+ rcviov[0].iov_len = sizeof(answer);
+ rcvmhdr.msg_name = (caddr_t)&from;
+ rcvmhdr.msg_namelen = sizeof(from);
+ rcvmhdr.msg_iov = rcviov;
+ rcvmhdr.msg_iovlen = 1;
+ rcvmhdr.msg_control = (caddr_t) rcvcmsgbuf;
+ rcvmhdr.msg_controllen = rcvcmsgbuflen;
+
+ /* initialize msghdr for sending packets */
+ sndmhdr.msg_namelen = sizeof(struct sockaddr_in6);
+ sndmhdr.msg_iov = sndiov;
+ sndmhdr.msg_iovlen = 1;
+ sndmhdr.msg_control = (caddr_t)sndcmsgbuf;
+ sndmhdr.msg_controllen = sndcmsgbuflen;
+
+ return;
+}
+
+/* open a routing socket to watch the routing table */
+static void
+rtsock_open()
+{
+ if ((rtsock = socket(PF_ROUTE, SOCK_RAW, 0)) < 0) {
+ syslog(LOG_ERR,
+ "<%s> socket: %s", __FUNCTION__, strerror(errno));
+ exit(1);
+ }
+}
+
+struct rainfo *
+if_indextorainfo(int index)
+{
+ struct rainfo *rai = ralist;
+
+ for (rai = ralist; rai; rai = rai->next) {
+ if (rai->ifindex == index)
+ return(rai);
+ }
+
+ return(NULL); /* search failed */
+}
+
+static void
+ra_output(rainfo)
+struct rainfo *rainfo;
+{
+ int i;
+ struct cmsghdr *cm;
+ struct in6_pktinfo *pi;
+ struct soliciter *sol, *nextsol;
+
+ if ((iflist[rainfo->ifindex]->ifm_flags & IFF_UP) == 0) {
+ syslog(LOG_DEBUG, "<%s> %s is not up, skip sending RA",
+ __FUNCTION__, rainfo->ifname);
+ return;
+ }
+
+ make_packet(rainfo); /* XXX: inefficient */
+
+ sndmhdr.msg_name = (caddr_t)&sin6_allnodes;
+ sndmhdr.msg_iov[0].iov_base = (caddr_t)rainfo->ra_data;
+ sndmhdr.msg_iov[0].iov_len = rainfo->ra_datalen;
+
+ cm = CMSG_FIRSTHDR(&sndmhdr);
+ /* specify the outgoing interface */
+ cm->cmsg_level = IPPROTO_IPV6;
+ cm->cmsg_type = IPV6_PKTINFO;
+ cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
+ pi = (struct in6_pktinfo *)CMSG_DATA(cm);
+ memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); /*XXX*/
+ pi->ipi6_ifindex = rainfo->ifindex;
+
+ /* specify the hop limit of the packet */
+ {
+ int hoplimit = 255;
+
+ cm = CMSG_NXTHDR(&sndmhdr, cm);
+ cm->cmsg_level = IPPROTO_IPV6;
+ cm->cmsg_type = IPV6_HOPLIMIT;
+ cm->cmsg_len = CMSG_LEN(sizeof(int));
+ memcpy(CMSG_DATA(cm), &hoplimit, sizeof(int));
+ }
+
+ syslog(LOG_DEBUG,
+ "<%s> send RA on %s, # of waitings = %d",
+ __FUNCTION__, rainfo->ifname, rainfo->waiting);
+
+ i = sendmsg(sock, &sndmhdr, 0);
+
+ if (i < 0 || i != rainfo->ra_datalen) {
+ if (i < 0) {
+ syslog(LOG_ERR, "<%s> sendmsg on %s: %s",
+ __FUNCTION__, rainfo->ifname,
+ strerror(errno));
+ }
+ }
+
+ /*
+ * unicast advertisements
+ * XXX commented out. reason: though spec does not forbit it, unicast
+ * advert does not really help
+ */
+ for (sol = rainfo->soliciter; sol; sol = nextsol) {
+ nextsol = sol->next;
+
+#if 0
+ sndmhdr.msg_name = (caddr_t)&sol->addr;
+ i = sendmsg(sock, &sndmhdr, 0);
+ if (i < 0 || i != rainfo->ra_datalen) {
+ if (i < 0) {
+ syslog(LOG_ERR,
+ "<%s> unicast sendmsg on %s: %s",
+ __FUNCTION__, rainfo->ifname,
+ strerror(errno));
+ }
+ }
+#endif
+
+ sol->next = NULL;
+ free(sol);
+ }
+ rainfo->soliciter = NULL;
+
+ /* update counter */
+ if (rainfo->initcounter < MAX_INITIAL_RTR_ADVERTISEMENTS)
+ rainfo->initcounter++;
+ rainfo->raoutput++;
+
+ /* update timestamp */
+ gettimeofday(&rainfo->lastsent, NULL);
+
+ /* reset waiting conter */
+ rainfo->waiting = 0;
+}
+
+/* process RA timer */
+struct rtadvd_timer *
+ra_timeout(void *data)
+{
+ struct rainfo *rai = (struct rainfo *)data;
+
+#ifdef notyet
+ /* if necessary, reconstruct the packet. */
+#endif
+
+ syslog(LOG_DEBUG,
+ "<%s> RA timer on %s is expired",
+ __FUNCTION__, rai->ifname);
+
+ ra_output(rai);
+
+ return(rai->timer);
+}
+
+/* update RA timer */
+void
+ra_timer_update(void *data, struct timeval *tm)
+{
+ struct rainfo *rai = (struct rainfo *)data;
+ long interval;
+
+ /*
+ * Whenever a multicast advertisement is sent from an interface,
+ * the timer is reset to a uniformly-distributed random value
+ * between the interface's configured MinRtrAdvInterval and
+ * MaxRtrAdvInterval (RFC2461 6.2.4).
+ */
+ interval = rai->mininterval;
+ interval += random() % (rai->maxinterval - rai->mininterval);
+
+ /*
+ * For the first few advertisements (up to
+ * MAX_INITIAL_RTR_ADVERTISEMENTS), if the randomly chosen interval
+ * is greater than MAX_INITIAL_RTR_ADVERT_INTERVAL, the timer
+ * SHOULD be set to MAX_INITIAL_RTR_ADVERT_INTERVAL instead.
+ * (RFC-2461 6.2.4)
+ */
+ if (rai->initcounter < MAX_INITIAL_RTR_ADVERTISEMENTS &&
+ interval > MAX_INITIAL_RTR_ADVERT_INTERVAL)
+ interval = MAX_INITIAL_RTR_ADVERT_INTERVAL;
+
+ tm->tv_sec = interval;
+ tm->tv_usec = 0;
+
+ syslog(LOG_DEBUG,
+ "<%s> RA timer on %s is set to %ld:%ld",
+ __FUNCTION__, rai->ifname,
+ (long int)tm->tv_sec, (long int)tm->tv_usec);
+
+ return;
+}
diff --git a/usr.sbin/rtadvd/rtadvd.conf b/usr.sbin/rtadvd/rtadvd.conf
new file mode 100644
index 0000000..e202a6c
--- /dev/null
+++ b/usr.sbin/rtadvd/rtadvd.conf
@@ -0,0 +1,21 @@
+# $FreeBSD$
+# $KAME: rtadvd.conf,v 1.12 2001/01/21 14:56:38 itojun Exp $
+#
+# Note: All of the following parameters have default values defined
+# in specifications, and hence you usually do not have to set them
+# by hand unless you need special non-default values.
+#
+# You even do not need to create the configuration file. rtadvd
+# would usually work well without a configuration file.
+# See also: rtadvd(8)
+
+# per-interface definitions.
+# Mainly IPv6 prefixes are configured in this part. However, rtadvd
+# automatically learns appropriate prefixes from the kernel's routing
+# table, and advertises the prefixes, so you don't have to configure
+# this part, either.
+# If you don't want the automatic advertisement, (uncomment and) configure
+# this part by hand, and then invoke rtadvd with the -s option.
+
+#ef0:\
+# :addrs#1:addr="3ffe:501:ffff:1000::":prefixlen#64:
diff --git a/usr.sbin/rtadvd/rtadvd.conf.5 b/usr.sbin/rtadvd/rtadvd.conf.5
new file mode 100644
index 0000000..cd5fc24
--- /dev/null
+++ b/usr.sbin/rtadvd/rtadvd.conf.5
@@ -0,0 +1,386 @@
+.\" $FreeBSD$
+.\" $KAME: rtadvd.conf.5,v 1.35 2001/05/25 07:40:22 jinmei Exp $
+.\"
+.\" Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the project nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd May 17, 1998
+.Dt RTADVD.CONF 5
+.Os
+.Sh NAME
+.Nm rtadvd.conf
+.Nd config file for router advertisement daemon
+.Sh DESCRIPTION
+This file describes how the router advertisement packets must be constructed
+for each of the interfaces.
+.Pp
+As described in
+.Xr rtadvd 8 ,
+you do not have to set this configuration file up at all,
+unless you need some special configurations.
+You may even omit the file as a whole.
+In such cases, the
+.Nm rtadvd
+daemon will automatically configure itself using default values
+specified in the specification.
+.Pp
+It obeys the famous
+.Xr termcap 5
+file format.
+Each line in the file describes a network interface.
+Fields are separated by a colon
+.Pq Sq \&: ,
+and each field contains one capability description.
+Lines may be concatenated by the
+.Sq \e
+character.
+The comment marker is the
+.Sq \&#
+character.
+.Sh CAPABILITIES
+Capabilities describe the value to be filled into ICMPv6 router
+advertisement messages and to control
+.Xr rtadvd 8
+behavior.
+Therefore, you are encouraged to read IETF neighbor discovery documents
+if you would like to modify the sample configuration file.
+.Pp
+Note that almost all items have default values.
+If you omit an item, the default value of the item will be used.
+.Pp
+There are two items which control the interval of sending router advertisements.
+These items can be omitted, then
+.Nm rtadvd
+will use the default values.
+.Bl -tag -width indent
+.It Cm \&maxinterval
+(num) The maximum time allowed between sending unsolicited
+multicast router advertisements
+(unit: seconds).
+The default value is 600.
+Its value must be no less than 4 seconds
+and no greater than 1800 seconds.
+.It Cm \&mininterval
+(num) The minimum time allowed between sending unsolicited multicast
+router advertisements
+(unit: seconds).
+The default value is the one third of value of
+.Cm maxinterval .
+Its value must be no less than 3 seconds and no greater than .75 *
+the value of
+.Cm maxinterval .
+.El
+.Pp
+The following items are for ICMPv6 router advertisement message
+header.
+These items can be omitted, then
+.Nm rtadvd
+will use the default values.
+.Bl -tag -width indent
+.It Cm \&chlim
+(num) The value for Cur Hop Limit field.
+The default value is 64.
+.It Cm \&raflags
+(num) Flags field in router advertisement message header.
+Bit 7
+.Pq Li 0x80
+means Managed address configuration flag bit,
+and Bit 6
+.Pq Li 0x40
+means Other stateful configuration flag bit.
+Bit 4
+.Pq Li 0x10
+and Bit 3
+.Pq Li 0x08
+are used to encode router preference.
+0x01 means high, 0x00 means medium, and 0x11 means low.
+The default value is 0.
+.It Cm \&rltime
+(num) Router lifetime field
+(unit: seconds).
+Its value must be no greater than 3600000.
+When
+.Nm rtadvd
+runs on a host, this value must explicitly set 0 on all the
+advertising interfaces as described in
+.Xr rtadvd 8 .
+The default value is 1800.
+.It Cm \&rtime
+(num) Reachable time field
+(unit: milliseconds).
+The default value is 0, which means unspecified by this router.
+.It Cm \&retrans
+(num) Retrans Timer field
+(unit: milliseconds).
+The default value is 0, which means unspecified by this router.
+.El
+.Pp
+The following items are for ICMPv6 prefix information option,
+which will be attached to router advertisement header.
+These items can be omitted, then
+.Nm rtadvd
+will automatically get appropriate prefixes from the kernel's routing table,
+and advertise the prefixes with the default parameters.
+.Bl -tag -width indent
+.It Cm \&clockskew
+(num) Time skew to adjust link propagation delays and clock skews
+betwen routers on the link
+(unit: seconds).
+This value is used in consistency check for locally-configured and
+advertised prefix lifetimes, and has its meaning when the local router
+configures a prefix on the link with a lifetime that decrements in
+real time.
+If the value is 0, it means the consistency check will be skipped
+for such prefixes.
+The default value is 0.
+.It Cm \&addrs
+(num) Number of prefixes.
+Its default is 0, so it must explicitly be set to positve values
+if you want to specify any prefix information option.
+If its value is 0,
+.Xr rtadvd 8
+looks up the system routing table and
+advertise the prefixes corresponding to interface routes
+on the interface.
+If its value is more than 1, you must specify the index of the prefix
+for each item below.
+Indices vary from 0 to N-1, where N is the
+value of
+.Cm addrs .
+Each index shall follow the name of each item, e.g.,
+.Dq prefixlen2 .
+.It Cm \&prefixlen
+(num) Prefix length field.
+The default value is 64.
+.It Cm \&pinfoflags
+(num) Flags field in prefix information option.
+Bit 7
+.Pq Li 0x80
+means On-link flag bit,
+and Bit 6
+.Pq Li 0x40
+means Autonomous address-configuration flag bit.
+The default value is 0xc0, i.e., both bits are set.
+.It Cm \&addr
+(str) The address filled into Prefix field.
+Since
+.Dq \&:
+is used for
+.Xr termcap 5
+file format as well as IPv6 numeric address, the field MUST be quoted by
+doublequote character.
+This field cannot be
+omitted if the value of
+.Cm addrs
+is more than 0.
+.It Cm \&vltime
+(num) Valid lifetime field
+(unit: seconds).
+The default value is 2592000 (30 days).
+.It Cm \&vltimedecr
+(bool) This item means the advertised valid lifetime will decrements
+in real time, which is disabled by default.
+.It Cm \&pltime
+(num) Preferred lifetime field
+(unit: seconds).
+The default value is 604800 (7 days).
+.It Cm \&pltimedecr
+(bool) This item means the advertised preferred lifetime will decrements
+in real time, which is disabled by default.
+.El
+.Pp
+The following item is for ICMPv6 MTU option,
+which will be attached to router advertisement header.
+This item can be omitted, then
+.Nm rtadvd
+will use the default value.
+.Bl -tag -width indent
+.It Cm \&mtu
+(num or str) MTU (maximum transmission unit) field.
+If 0 is specified, it means that the option will not be included.
+The default value is 0.
+If the special string
+.Dq auto
+is specified for this item, MTU option will be included and its value
+will be set to the interface MTU automatically.
+.El
+.Pp
+The following item controls ICMPv6 source link-layer address option,
+which will be attached to router advertisement header.
+As noted above, you can just omit the item, then
+.Nm rtadvd
+will use the default value.
+.Bl -tag -width indent
+.It Cm \&nolladdr
+(bool) By default
+(if
+.Cm \&nolladdr
+is not specified),
+.Xr rtadvd 8
+will try to get link-layer address for the interface from the kernel,
+and attach that in source link-layer address option.
+If this capability exists,
+.Xr rtadvd 8
+will not attach source link-layer address option to
+router advertisement packets.
+.El
+.Pp
+The following item controls ICMPV6 home agent information option,
+which was defined with mobile IPv6 support.
+It will be attached to router advertisement header just like other options do.
+.Bl -tag -width indent
+.It Cm \&hapref
+(num) Specifies home agent preference.
+If set to non-zero,
+.Cm \&hatime
+must be present as well.
+.It Cm \&hatime
+(num) Specifies home agent lifetime.
+.El
+.Pp
+When mobile IPv6 support is turned on for
+.Xr rtadvd 8 ,
+advertisement interval option will be attached to router advertisement
+packet, by configuring
+.Cm \&maxinterval
+explicitly.
+.Pp
+The following items are for ICMPv6 route information option,
+which will be attached to router advertisement header.
+These items are optional.
+.Bl -tag -width indent
+.It Cm \&routes
+(num) Number of routes.
+Its default is 0, so it must explicitly be set to positve values
+if you want to specify any route information option.
+If its value is 0, no route information is sent.
+If its value is more than 1, you must specify the index of the routes
+for each item below.
+Indices vary from 0 to N-1, where N is the
+value of
+.Cm routes.
+Each index shall follow the name of each item, e.g.,
+.Dq rtrplen2 .
+.It Cm \&rtrplen
+(num) Prefix length field in route information option.
+The default value is 64.
+.It Cm \&rtrflags
+(num) Flags field in route information option.
+Bit 4
+.Pq Li 0x10
+and
+and Bit 3
+.Pq Li 0x08
+are used to encode router preference for the route.
+The default value is 0x00, i.e. medium router preference.
+.It Cm \&rtrprefix
+(str) The prefix filled into the Prefix field of route information option.
+Since
+.Dq \&:
+is used for
+.Xr termcap 5
+file format as well as IPv6 numeric address, the field MUST be quoted by
+doublequote character.
+This field cannot be
+omitted if the value of
+.Cm addrs
+is more than 0.
+.It Cm \&rtrltime
+(num) route lifetime field in route information option.
+(unit: seconds).
+The default value is 2592000 (30 days). (not specified in draft-draves-router-selection-01.txt now)
+.El
+You can also refer one line from another by using
+.Cm tc
+capability.
+See
+.Xr termcap 5
+for details on the capability.
+.Sh EXAMPLES
+As presented above, all of the advertised parameters have default values
+defined in specifications, and hence you usually do not have to set them
+by hand, unless you need special non-default values.
+It can cause interoperability problem if you use an ill-configured
+parameter.
+.Pp
+To override a configuration parameter, you can specify the parameter alone.
+With the following configuration,
+.Xr rtadvd 8
+overrides the router lifetime parameter for the
+.Li ne0
+interface.
+.Bd -literal -offset
+ne0:\\
+ :rltime#0:
+.Ed
+.Pp
+The following example manually configures prefixes advertised from the
+.Li ef0
+interface.
+The configuration must be used with the
+.Fl s
+option to
+.Xr rtadvd 8 .
+.Bd -literal -offset
+ef0:\\
+ :addrs#1:addr="3ffe:501:ffff:1000::":prefixlen#64:
+.Ed
+.Pp
+The following example presents the default values in an explicit manner.
+The configuration is provided just for reference purposes;
+YOU DO NOT NEED TO HAVE IT AT ALL.
+.Bd -literal -offset
+default:\\
+ :chlim#64:raflags#0:rltime#1800:rtime#0:retrans#0:\\
+ :pinfoflags#192:vltime#2592000:pltime#604800:mtu#0:
+ef0:\\
+ :addrs#1:addr="3ffe:501:ffff:1000::":prefixlen#64:tc=default:
+.Ed
+.Sh SEE ALSO
+.Xr termcap 5 ,
+.Xr rtadvd 8 ,
+.Xr rtsol 8
+.Pp
+Thomas Narten, Erik Nordmark and W. A. Simpson,
+.Do
+Neighbor Discovery for IP version 6 (IPv6)
+.Dc ,
+RFC 2461
+.Pp
+Richard Draves,
+.Do
+Default Router Preferences and More-Specific Routes
+.Dc ,
+draft-ietf-ipngwg-router-selection-01.txt
+.Sh HISTORY
+The
+.Xr rtadvd 8
+and the configuration file
+.Nm
+first appeared in WIDE Hydrangea IPv6 protocol stack kit.
+.\" .Sh BUGS
+.\" (to be written)
diff --git a/usr.sbin/rtadvd/rtadvd.h b/usr.sbin/rtadvd/rtadvd.h
new file mode 100644
index 0000000..674ab12
--- /dev/null
+++ b/usr.sbin/rtadvd/rtadvd.h
@@ -0,0 +1,178 @@
+/* $FreeBSD$ */
+/* $KAME: rtadvd.h,v 1.16 2001/04/10 15:08:31 suz Exp $ */
+
+/*
+ * Copyright (C) 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define ALLNODES "ff02::1"
+#define ALLROUTERS_LINK "ff02::2"
+#define ALLROUTERS_SITE "ff05::2"
+#define ANY "::"
+#define RTSOLLEN 8
+
+/* protocol constants and default values */
+#define DEF_MAXRTRADVINTERVAL 600
+#define DEF_ADVLINKMTU 0
+#define DEF_ADVREACHABLETIME 0
+#define DEF_ADVRETRANSTIMER 0
+#define DEF_ADVCURHOPLIMIT 64
+#define DEF_ADVVALIDLIFETIME 2592000
+#define DEF_ADVPREFERREDLIFETIME 604800
+
+/*XXX int-to-double comparison for INTERVAL items */
+#ifndef MIP6
+#define mobileip6 0
+#endif
+
+#define MAXROUTERLIFETIME 9000
+#define MIN_MAXINTERVAL (mobileip6 ? 1.5 : 4.0)
+#define MAX_MAXINTERVAL 1800
+#define MIN_MININTERVAL (mobileip6 ? 0.5 : 3)
+#define MAXREACHABLETIME 3600000
+
+#ifndef MIP6
+#undef miobileip6
+#endif
+
+#define MAX_INITIAL_RTR_ADVERT_INTERVAL 16
+#define MAX_INITIAL_RTR_ADVERTISEMENTS 3
+#define MAX_FINAL_RTR_ADVERTISEMENTS 3
+#define MIN_DELAY_BETWEEN_RAS 3
+#define MAX_RA_DELAY_TIME 500000 /* usec */
+
+#define PREFIX_FROM_KERNEL 1
+#define PREFIX_FROM_CONFIG 2
+#define PREFIX_FROM_DYNAMIC 3
+
+struct prefix {
+ struct prefix *next; /* forward link */
+ struct prefix *prev; /* previous link */
+
+ struct rainfo *rainfo; /* back pointer to the interface */
+
+ struct rtadvd_timer *timer; /* expiration timer. used when a prefix
+ * derived from the kernel is deleted.
+ */
+
+ u_int32_t validlifetime; /* AdvValidLifetime */
+ long vltimeexpire; /* expiration of vltime; decrement case only */
+ u_int32_t preflifetime; /* AdvPreferredLifetime */
+ long pltimeexpire; /* expiration of pltime; decrement case only */
+ u_int onlinkflg; /* bool: AdvOnLinkFlag */
+ u_int autoconfflg; /* bool: AdvAutonomousFlag */
+#ifdef MIP6
+ u_int routeraddr; /* bool: RouterAddress */
+#endif
+ int prefixlen;
+ int origin; /* from kernel or cofig */
+ struct in6_addr prefix;
+};
+
+struct rtinfo {
+ struct rtinfo *prev; /* previous link */
+ struct rtinfo *next; /* forward link */
+
+ u_int32_t ltime; /* route lifetime */
+ u_int rtpref; /* router preference */
+ int prefixlen;
+ struct in6_addr prefix;
+};
+
+struct soliciter {
+ struct soliciter *next;
+ struct sockaddr_in6 addr;
+};
+
+struct rainfo {
+ /* pointer for list */
+ struct rainfo *next;
+
+ /* timer related parameters */
+ struct rtadvd_timer *timer;
+ int initcounter; /* counter for the first few advertisements */
+ struct timeval lastsent; /* timestamp when the latest RA was sent */
+ int waiting; /* number of RS waiting for RA */
+
+ /* interface information */
+ int ifindex;
+ int advlinkopt; /* bool: whether include link-layer addr opt */
+ struct sockaddr_dl *sdl;
+ char ifname[16];
+ int phymtu; /* mtu of the physical interface */
+
+ /* Router configuration variables */
+ u_short lifetime; /* AdvDefaultLifetime */
+ u_int maxinterval; /* MaxRtrAdvInterval */
+ u_int mininterval; /* MinRtrAdvInterval */
+ int managedflg; /* AdvManagedFlag */
+ int otherflg; /* AdvOtherConfigFlag */
+#ifdef MIP6
+ int haflg; /* HAFlag */
+#endif
+ int rtpref; /* router preference */
+ u_int32_t linkmtu; /* AdvLinkMTU */
+ u_int32_t reachabletime; /* AdvReachableTime */
+ u_int32_t retranstimer; /* AdvRetransTimer */
+ u_int hoplimit; /* AdvCurHopLimit */
+ struct prefix prefix; /* AdvPrefixList(link head) */
+ int pfxs; /* number of prefixes */
+ long clockskew; /* used for consisitency check of lifetimes */
+
+#ifdef MIP6
+ u_short hapref; /* Home Agent Preference */
+ u_short hatime; /* Home Agent Lifetime */
+#endif
+ struct rtinfo route; /* route information option (link head) */
+ int routes; /* number of route information options */
+
+ /* actual RA packet data and its length */
+ size_t ra_datalen;
+ u_char *ra_data;
+
+ /* statistics */
+ u_quad_t raoutput; /* number of RAs sent */
+ u_quad_t rainput; /* number of RAs received */
+ u_quad_t rainconsistent; /* number of RAs inconsistent with ours */
+ u_quad_t rsinput; /* number of RSs received */
+
+ /* info about soliciter */
+ struct soliciter *soliciter; /* recent solication source */
+};
+
+struct rtadvd_timer *ra_timeout __P((void *));
+void ra_timer_update __P((void *, struct timeval *));
+
+int prefix_match __P((struct in6_addr *, int, struct in6_addr *, int));
+struct rainfo *if_indextorainfo __P((int));
+struct prefix *find_prefix __P((struct rainfo *, struct in6_addr *, int));
+
+extern struct in6_addr in6a_site_allrouters;
+#ifdef MIP6
+extern int mobileip6;
+#endif
diff --git a/usr.sbin/rtadvd/timer.c b/usr.sbin/rtadvd/timer.c
new file mode 100644
index 0000000..646b5ee
--- /dev/null
+++ b/usr.sbin/rtadvd/timer.c
@@ -0,0 +1,213 @@
+/* $FreeBSD$ */
+/* $KAME: timer.c,v 1.4 2000/05/27 11:30:43 jinmei Exp $ */
+
+/*
+ * Copyright (C) 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/time.h>
+
+#include <unistd.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include <string.h>
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+#include <search.h>
+#endif
+#include "timer.h"
+
+static struct rtadvd_timer timer_head;
+
+#define MILLION 1000000
+#define TIMEVAL_EQUAL(t1,t2) ((t1)->tv_sec == (t2)->tv_sec &&\
+ (t1)->tv_usec == (t2)->tv_usec)
+
+static struct timeval tm_max = {0x7fffffff, 0x7fffffff};
+
+void
+rtadvd_timer_init()
+{
+ memset(&timer_head, 0, sizeof(timer_head));
+
+ timer_head.next = timer_head.prev = &timer_head;
+ timer_head.tm = tm_max;
+}
+
+struct rtadvd_timer *
+rtadvd_add_timer(struct rtadvd_timer *(*timeout) __P((void *)),
+ void (*update) __P((void *, struct timeval *)),
+ void *timeodata, void *updatedata)
+{
+ struct rtadvd_timer *newtimer;
+
+ if ((newtimer = malloc(sizeof(*newtimer))) == NULL) {
+ syslog(LOG_ERR,
+ "<%s> can't allocate memory", __FUNCTION__);
+ exit(1);
+ }
+
+ memset(newtimer, 0, sizeof(*newtimer));
+
+ if (timeout == NULL) {
+ syslog(LOG_ERR,
+ "<%s> timeout function unspecfied", __FUNCTION__);
+ exit(1);
+ }
+ newtimer->expire = timeout;
+ newtimer->update = update;
+ newtimer->expire_data = timeodata;
+ newtimer->update_data = updatedata;
+ newtimer->tm = tm_max;
+
+ /* link into chain */
+ insque(newtimer, &timer_head);
+
+ return(newtimer);
+}
+
+void
+rtadvd_remove_timer(struct rtadvd_timer **timer)
+{
+ remque(*timer);
+ free(*timer);
+ *timer = NULL;
+}
+
+void
+rtadvd_set_timer(struct timeval *tm, struct rtadvd_timer *timer)
+{
+ struct timeval now;
+
+ /* reset the timer */
+ gettimeofday(&now, NULL);
+
+ TIMEVAL_ADD(&now, tm, &timer->tm);
+
+ /* update the next expiration time */
+ if (TIMEVAL_LT(timer->tm, timer_head.tm))
+ timer_head.tm = timer->tm;
+
+ return;
+}
+
+/*
+ * Check expiration for each timer. If a timer expires,
+ * call the expire function for the timer and update the timer.
+ * Return the next interval for select() call.
+ */
+struct timeval *
+rtadvd_check_timer()
+{
+ static struct timeval returnval;
+ struct timeval now;
+ struct rtadvd_timer *tm = timer_head.next, *tm_next;
+
+ gettimeofday(&now, NULL);
+
+ timer_head.tm = tm_max;
+
+ for (tm = timer_head.next; tm != &timer_head; tm = tm_next) {
+ tm_next = tm->next;
+
+ if (TIMEVAL_LEQ(tm->tm, now)) {
+ if (((*tm->expire)(tm->expire_data) == NULL))
+ continue; /* the timer was removed */
+ if (tm->update)
+ (*tm->update)(tm->update_data, &tm->tm);
+ TIMEVAL_ADD(&tm->tm, &now, &tm->tm);
+ }
+
+ if (TIMEVAL_LT(tm->tm, timer_head.tm))
+ timer_head.tm = tm->tm;
+ }
+
+ if (TIMEVAL_EQUAL(&tm_max, &timer_head.tm)) {
+ /* no need to timeout */
+ return(NULL);
+ }
+ else if (TIMEVAL_LT(timer_head.tm, now)) {
+ /* this may occur when the interval is too small */
+ returnval.tv_sec = returnval.tv_usec = 0;
+ }
+ else
+ TIMEVAL_SUB(&timer_head.tm, &now, &returnval);
+ return(&returnval);
+}
+
+struct timeval *
+rtadvd_timer_rest(struct rtadvd_timer *timer)
+{
+ static struct timeval returnval, now;
+
+ gettimeofday(&now, NULL);
+ if (TIMEVAL_LEQ(timer->tm, now)) {
+ syslog(LOG_DEBUG,
+ "<%s> a timer must be expired, but not yet",
+ __FUNCTION__);
+ returnval.tv_sec = returnval.tv_usec = 0;
+ }
+ else
+ TIMEVAL_SUB(&timer->tm, &now, &returnval);
+
+ return(&returnval);
+}
+
+/* result = a + b */
+void
+TIMEVAL_ADD(struct timeval *a, struct timeval *b, struct timeval *result)
+{
+ long l;
+
+ if ((l = a->tv_usec + b->tv_usec) < MILLION) {
+ result->tv_usec = l;
+ result->tv_sec = a->tv_sec + b->tv_sec;
+ }
+ else {
+ result->tv_usec = l - MILLION;
+ result->tv_sec = a->tv_sec + b->tv_sec + 1;
+ }
+}
+
+/*
+ * result = a - b
+ * XXX: this function assumes that a >= b.
+ */
+void
+TIMEVAL_SUB(struct timeval *a, struct timeval *b, struct timeval *result)
+{
+ long l;
+
+ if ((l = a->tv_usec - b->tv_usec) >= 0) {
+ result->tv_usec = l;
+ result->tv_sec = a->tv_sec - b->tv_sec;
+ }
+ else {
+ result->tv_usec = MILLION + l;
+ result->tv_sec = a->tv_sec - b->tv_sec - 1;
+ }
+}
diff --git a/usr.sbin/rtadvd/timer.h b/usr.sbin/rtadvd/timer.h
new file mode 100644
index 0000000..2eebf15
--- /dev/null
+++ b/usr.sbin/rtadvd/timer.h
@@ -0,0 +1,65 @@
+/* $FreeBSD$ */
+/* $KAME: timer.h,v 1.3 2000/05/27 11:30:43 jinmei Exp $ */
+
+/*
+ * Copyright (C) 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* a < b */
+#define TIMEVAL_LT(a, b) (((a).tv_sec < (b).tv_sec) ||\
+ (((a).tv_sec == (b).tv_sec) && \
+ ((a).tv_usec < (b).tv_usec)))
+
+/* a <= b */
+#define TIMEVAL_LEQ(a, b) (((a).tv_sec < (b).tv_sec) ||\
+ (((a).tv_sec == (b).tv_sec) &&\
+ ((a).tv_usec <= (b).tv_usec)))
+
+struct rtadvd_timer {
+ struct rtadvd_timer *next;
+ struct rtadvd_timer *prev;
+ struct rainfo *rai;
+ struct timeval tm;
+
+ struct rtadvd_timer *(*expire) __P((void *)); /* expiration function */
+ void *expire_data;
+ void (*update) __P((void *, struct timeval *)); /* update function */
+ void *update_data;
+};
+
+void rtadvd_timer_init __P((void));
+struct rtadvd_timer *rtadvd_add_timer __P((struct rtadvd_timer *(*) __P((void *)),
+ void (*) __P((void *, struct timeval *)), void *, void *));
+void rtadvd_set_timer __P((struct timeval *, struct rtadvd_timer *));
+void rtadvd_remove_timer __P((struct rtadvd_timer **));
+struct timeval * rtadvd_check_timer __P((void));
+struct timeval * rtadvd_timer_rest __P((struct rtadvd_timer *));
+void TIMEVAL_ADD __P((struct timeval *, struct timeval *,
+ struct timeval *));
+void TIMEVAL_SUB __P((struct timeval *, struct timeval *,
+ struct timeval *));
diff --git a/usr.sbin/rtprio/Makefile b/usr.sbin/rtprio/Makefile
new file mode 100644
index 0000000..ebc7bd1
--- /dev/null
+++ b/usr.sbin/rtprio/Makefile
@@ -0,0 +1,8 @@
+# from: @(#)Makefile 5.5 (Berkeley) 5/11/90
+# $FreeBSD$
+
+PROG= rtprio
+LINKS= ${BINDIR}/rtprio ${BINDIR}/idprio
+MLINKS= rtprio.1 idprio.1
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rtprio/rtprio.1 b/usr.sbin/rtprio/rtprio.1
new file mode 100644
index 0000000..6d4d260
--- /dev/null
+++ b/usr.sbin/rtprio/rtprio.1
@@ -0,0 +1,210 @@
+.\"
+.\" Copyright (c) 1994, Henrik Vestergaard Draboel
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Henrik Vestergaard Draboel.
+.\" 4. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd July 23, 1994
+.Dt RTPRIO 1
+.Os
+.Sh NAME
+.Nm rtprio ,
+.Nm idprio
+.Nd execute, examine or modify a utility's or process's realtime
+or idletime scheduling priority
+.Sh SYNOPSIS
+.Nm [id|rt]prio
+.Nm [id|rt]prio
+.Oo Fl Oc Ns Ar pid
+.Nm [id|rt]prio
+.Ar priority
+.Ar command
+.Op args
+.Nm [id|rt]prio
+.Ar priority
+.Fl Ar pid
+.Nm [id|rt]prio
+.Fl t
+.Ar command
+.Op args
+.Nm [id|rt]prio
+.Fl t
+.Fl Ar pid
+.Sh DESCRIPTION
+The
+.Nm
+utility is used for controlling realtime process scheduling.
+.Pp
+The
+.Nm idprio
+utility is used for controlling idletime process scheduling, and can be called
+with the same options as
+.Nm .
+.Pp
+A process with a realtime priority is not subject to priority
+degradation, and will only be preempted by another process of equal or
+higher realtime priority.
+.Pp
+A process with an idle priority will run only when no other
+process is runnable and then only if its idle priority is equal or
+greater than all other runnable idle priority processes.
+.Pp
+Both
+.Nm
+or
+.Nm idprio
+when called without arguments will return the realtime priority
+of the current process.
+.Pp
+If
+.Nm
+is called with 1 argument, it will return the realtime priority
+of the process with the specified
+.Ar pid .
+.Pp
+If
+.Ar priority
+is specified, the process or program is run at that realtime priority.
+If
+.Fl t
+is specified, the process or program is run as a normal (non-realtime)
+process.
+.Pp
+If
+.Ar -pid
+is specified, the process with the process identifier
+.Ar pid
+will be modified, else if
+.Ar command
+is specified, that program is run with its arguments.
+.Pp
+.Ar Priority
+is an integer between 0 and RTP_PRIO_MAX (usually 31). 0 is the
+highest priority
+.Pp
+.Ar Pid
+of 0 means "the current process".
+.Pp
+Only root is allowed to set realtime or idle priority for a process.
+.Sh DIAGNOSTICS
+If
+.Nm
+execute a command, the exit value is that of the command executed.
+In all other cases,
+.Nm
+exits 0 on success, and 1 for all other errors.
+.Sh EXAMPLES
+To see which realtime priority the current process is at:
+.Bd -literal -offset indent -compact
+.Sy "rtprio"
+.Ed
+.Pp
+To see which realtime priority of process
+.Em 1423 :
+.Bd -literal -offset indent -compact
+.Sy "rtprio 1423"
+.Ed
+.Pp
+To run
+.Xr cron 8
+at the lowest realtime priority:
+.Bd -literal -offset indent -compact
+.Sy "rtprio 31 cron"
+.Ed
+.Pp
+To change the realtime priority of process
+.Em 1423
+to
+.Em 16 :
+.Bd -literal -offset indent -compact
+.Sy "rtprio 16 -1423"
+.Ed
+.Pp
+To run
+.Xr tcpdump 1
+without realtime priority:
+.Bd -literal -offset indent -compact
+.Sy "rtprio -t tcpdump"
+.Ed
+.Pp
+To change the realtime priority of process
+.Em 1423
+to
+.Dv RTP_PRIO_NORMAL
+(non-realtime/normal priority):
+.Bd -literal -offset indent -compact
+.Sy "rtprio -t -1423"
+.Ed
+.Pp
+To make depend while not disturbing other machine usage:
+.Bd -literal -offset indent -compact
+.Sy "idprio 31 make depend"
+.Ed
+.Sh SEE ALSO
+.Xr nice 1 ,
+.Xr ps 1 ,
+.Xr rtprio 2 ,
+.Xr setpriority 2 ,
+.Xr nice 3 ,
+.Xr renice 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Fx 2.0 ,
+but is similar to the HP-UX version.
+.Sh CAVEATS
+You can lock yourself out of the system by placing a cpu-heavy
+process in a realtime priority.
+.Sh BUGS
+There is no way to set/view the realtime priority of process 0
+(swapper) (see
+.Xr ps 1 ) .
+.Pp
+There is in
+.Fx
+no way to ensure that a process page is present in memory therefore
+the process may be stopped for pagein (see
+.Xr mprotect 2 ,
+.Xr madvise 2 ) .
+.Pp
+Under
+.Fx
+system calls are currently never preempted, therefore non-realtime
+processes can starve realtime processes, or idletime processes can
+starve normal priority processes.
+.Sh AUTHORS
+.An -nosplit
+.An Henrik Vestergaard Draboel Aq hvd@terry.ping.dk
+is the original author.
+This
+implementation in
+.Fx
+was substantially rewritten by
+.An David Greenman .
diff --git a/usr.sbin/rtprio/rtprio.c b/usr.sbin/rtprio/rtprio.c
new file mode 100644
index 0000000..fc6ab41
--- /dev/null
+++ b/usr.sbin/rtprio/rtprio.c
@@ -0,0 +1,142 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#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(0);
+ }
+ exit (1);
+}
+
+static void
+usage()
+{
+ (void) fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n",
+ "usage: [id|rt]prio",
+ " [id|rt]prio [-]pid",
+ " [id|rt]prio priority command [args]",
+ " [id|rt]prio priority -pid",
+ " [id|rt]prio -t command [args]",
+ " [id|rt]prio -t -pid");
+ exit(1);
+}
diff --git a/usr.sbin/rtsold/Makefile b/usr.sbin/rtsold/Makefile
new file mode 100644
index 0000000..eb9b905
--- /dev/null
+++ b/usr.sbin/rtsold/Makefile
@@ -0,0 +1,27 @@
+# Copyright (c) 1996 WIDE Project. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modifications, are permitted provided that the above copyright notice
+# and this paragraph are duplicated in all such forms and that any
+# documentation, advertising materials, and other materials related to
+# such distribution and use acknowledge that the software was developed
+# by the WIDE Project, Japan. The name of the Project may not be used to
+# endorse or promote products derived from this software without
+# specific prior written permission. THIS SOFTWARE IS PROVIDED ``AS IS''
+# AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+# LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE.
+#
+# $FreeBSD$
+
+PROG= rtsold
+MAN= rtsold.8
+MLINKS= rtsold.8 rtsol.8
+SRCS= rtsold.c rtsol.c if.c probe.c dump.c rtsock.c
+
+CFLAGS+= -DINET6 -DHAVE_GETIFADDRS
+
+LDADD= -lkvm
+DPADD= ${LIBKVM}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rtsold/dump.c b/usr.sbin/rtsold/dump.c
new file mode 100644
index 0000000..0f75971
--- /dev/null
+++ b/usr.sbin/rtsold/dump.c
@@ -0,0 +1,142 @@
+/* $KAME: dump.c,v 1.8 2000/10/05 22:20:39 itojun Exp $ */
+
+/*
+ * Copyright (C) 1999 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/icmp6.h>
+
+#include <syslog.h>
+#include <time.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include "rtsold.h"
+
+static FILE *fp;
+
+extern struct ifinfo *iflist;
+
+static void dump_interface_status __P((void));
+static char *sec2str __P((time_t));
+char *ifstatstr[] = {"IDLE", "DELAY", "PROBE", "DOWN", "TENTATIVE"};
+
+static void
+dump_interface_status()
+{
+ struct ifinfo *ifinfo;
+ struct timeval now;
+
+ gettimeofday(&now, NULL);
+
+ for (ifinfo = iflist; ifinfo; ifinfo = ifinfo->next) {
+ fprintf(fp, "Interface %s\n", ifinfo->ifname);
+ fprintf(fp, " probe interval: ");
+ if (ifinfo->probeinterval) {
+ fprintf(fp, "%d\n", ifinfo->probeinterval);
+ fprintf(fp, " probe timer: %d\n", ifinfo->probetimer);
+ }
+ else {
+ fprintf(fp, "infinity\n");
+ fprintf(fp, " no probe timer\n");
+ }
+ fprintf(fp, " interface status: %s\n",
+ ifinfo->active > 0 ? "active" : "inactive");
+ fprintf(fp, " rtsold status: %s\n", ifstatstr[ifinfo->state]);
+ fprintf(fp, " carrier detection: %s\n",
+ ifinfo->mediareqok ? "available" : "unavailable");
+ fprintf(fp, " probes: %d, dadcount = %d\n",
+ ifinfo->probes, ifinfo->dadcount);
+ if (ifinfo->timer.tv_sec == tm_max.tv_sec &&
+ ifinfo->timer.tv_usec == tm_max.tv_usec)
+ fprintf(fp, " no timer\n");
+ else {
+ fprintf(fp, " timer: interval=%d:%d, expire=%s\n",
+ (int)ifinfo->timer.tv_sec,
+ (int)ifinfo->timer.tv_usec,
+ (ifinfo->expire.tv_sec < now.tv_sec) ? "expired"
+ : sec2str(ifinfo->expire.tv_sec - now.tv_sec));
+ }
+ fprintf(fp, " number of valid RAs: %d\n", ifinfo->racnt);
+ }
+}
+
+void
+rtsold_dump_file(dumpfile)
+ char *dumpfile;
+{
+ if ((fp = fopen(dumpfile, "w")) == NULL) {
+ warnmsg(LOG_WARNING, __FUNCTION__, "open a dump file(%s): %s",
+ dumpfile, strerror(errno));
+ return;
+ }
+
+ dump_interface_status();
+
+ fclose(fp);
+}
+
+static char *
+sec2str(total)
+ time_t total;
+{
+ static char result[256];
+ int days, hours, mins, secs;
+ int first = 1;
+ char *p = result;
+
+ days = total / 3600 / 24;
+ hours = (total / 3600) % 24;
+ mins = (total / 60) % 60;
+ secs = total % 60;
+
+ if (days) {
+ first = 0;
+ p += sprintf(p, "%dd", days);
+ }
+ if (!first || hours) {
+ first = 0;
+ p += sprintf(p, "%dh", hours);
+ }
+ if (!first || mins) {
+ first = 0;
+ p += sprintf(p, "%dm", mins);
+ }
+ sprintf(p, "%ds", secs);
+
+ return(result);
+}
diff --git a/usr.sbin/rtsold/if.c b/usr.sbin/rtsold/if.c
new file mode 100644
index 0000000..4566b73
--- /dev/null
+++ b/usr.sbin/rtsold/if.c
@@ -0,0 +1,468 @@
+/* $KAME: if.c,v 1.15 2001/05/22 06:04:17 jinmei Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/ioctl.h>
+#include <sys/queue.h>
+
+#include <net/if.h>
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+#include <net/if_var.h>
+#endif /* __FreeBSD__ >= 3 */
+#include <net/if_types.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#ifdef __FreeBSD__
+# include <net/ethernet.h>
+#endif
+#ifdef __NetBSD__
+#include <net/if_ether.h>
+#endif
+#if defined(__bsdi__) || defined(__OpenBSD__)
+# include <netinet/in.h>
+# include <netinet/if_ether.h>
+#endif
+#include <netinet/in.h>
+#include <netinet/icmp6.h>
+
+#include <netinet6/in6_var.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <limits.h>
+#ifdef HAVE_GETIFADDRS
+#include <ifaddrs.h>
+#endif
+
+#include "rtsold.h"
+
+extern int rssock;
+static int ifsock;
+
+static int get_llflag __P((const char *name));
+#ifndef HAVE_GETIFADDRS
+static unsigned int if_maxindex __P((void));
+#endif
+static void get_rtaddrs __P((int addrs, struct sockaddr *sa,
+ struct sockaddr **rti_info));
+
+int
+ifinit()
+{
+ ifsock = rssock;
+
+ return(0);
+}
+
+int
+interface_up(char *name)
+{
+ struct ifreq ifr;
+ int llflag;
+
+ strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
+
+ if (ioctl(ifsock, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
+ warnmsg(LOG_WARNING, __FUNCTION__, "ioctl(SIOCGIFFLAGS): %s",
+ strerror(errno));
+ return(-1);
+ }
+ if (!(ifr.ifr_flags & IFF_UP)) {
+ ifr.ifr_flags |= IFF_UP;
+ if (ioctl(ifsock, SIOCSIFFLAGS, (caddr_t)&ifr) < 0) {
+ warnmsg(LOG_ERR, __FUNCTION__,
+ "ioctl(SIOCSIFFLAGS): %s", strerror(errno));
+ }
+ return(-1);
+ }
+
+ warnmsg(LOG_DEBUG, __FUNCTION__, "checking if %s is ready...", name);
+
+ llflag = get_llflag(name);
+ if (llflag < 0) {
+ warnmsg(LOG_WARNING, __FUNCTION__,
+ "get_llflag() failed, anyway I'll try");
+ return 0;
+ }
+
+ if (!(llflag & IN6_IFF_NOTREADY)) {
+ warnmsg(LOG_DEBUG, __FUNCTION__,
+ "%s is ready", name);
+ return(0);
+ } else {
+ if (llflag & IN6_IFF_TENTATIVE) {
+ warnmsg(LOG_DEBUG, __FUNCTION__, "%s is tentative",
+ name);
+ return IFS_TENTATIVE;
+ }
+ if (llflag & IN6_IFF_DUPLICATED)
+ warnmsg(LOG_DEBUG, __FUNCTION__, "%s is duplicated",
+ name);
+ return -1;
+ }
+}
+
+int
+interface_status(struct ifinfo *ifinfo)
+{
+ char *ifname = ifinfo->ifname;
+ struct ifreq ifr;
+ struct ifmediareq ifmr;
+
+ /* get interface flags */
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ if (ioctl(ifsock, SIOCGIFFLAGS, &ifr) < 0) {
+ warnmsg(LOG_ERR, __FUNCTION__, "ioctl(SIOCGIFFLAGS) on %s: %s",
+ ifname, strerror(errno));
+ return(-1);
+ }
+ /*
+ * if one of UP and RUNNING flags is dropped,
+ * the interface is not active.
+ */
+ if ((ifr.ifr_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
+ goto inactive;
+ }
+
+ /* Next, check carrier on the interface, if possible */
+ if (!ifinfo->mediareqok)
+ goto active;
+ memset(&ifmr, 0, sizeof(ifmr));
+ strncpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
+
+ if (ioctl(ifsock, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) {
+ if (errno != EINVAL) {
+ warnmsg(LOG_DEBUG, __FUNCTION__,
+ "ioctl(SIOCGIFMEDIA) on %s: %s",
+ ifname, strerror(errno));
+ return(-1);
+ }
+ /*
+ * EINVAL simply means that the interface does not support
+ * the SIOCGIFMEDIA ioctl. We regard it alive.
+ */
+ ifinfo->mediareqok = 0;
+ goto active;
+ }
+
+ if (ifmr.ifm_status & IFM_AVALID) {
+ switch(ifmr.ifm_active & IFM_NMASK) {
+ case IFM_ETHER:
+ if (ifmr.ifm_status & IFM_ACTIVE)
+ goto active;
+ else
+ goto inactive;
+ break;
+ default:
+ goto inactive;
+ }
+ }
+
+ inactive:
+ return(0);
+
+ active:
+ return(1);
+}
+
+#define ROUNDUP(a, size) \
+ (((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a))
+
+#define NEXT_SA(ap) (ap) = (struct sockaddr *) \
+ ((caddr_t)(ap) + ((ap)->sa_len ? ROUNDUP((ap)->sa_len,\
+ sizeof(u_long)) :\
+ sizeof(u_long)))
+#define ROUNDUP8(a) (1 + (((a) - 1) | 7))
+
+int
+lladdropt_length(struct sockaddr_dl *sdl)
+{
+ switch(sdl->sdl_type) {
+ case IFT_ETHER:
+#ifdef IFT_IEEE80211
+ case IFT_IEEE80211:
+#endif
+ return(ROUNDUP8(ETHER_ADDR_LEN + 2));
+ default:
+ return(0);
+ }
+}
+
+void
+lladdropt_fill(struct sockaddr_dl *sdl, struct nd_opt_hdr *ndopt)
+{
+ char *addr;
+
+ ndopt->nd_opt_type = ND_OPT_SOURCE_LINKADDR; /* fixed */
+
+ switch(sdl->sdl_type) {
+ case IFT_ETHER:
+#ifdef IFT_IEEE80211
+ case IFT_IEEE80211:
+#endif
+ ndopt->nd_opt_len = (ROUNDUP8(ETHER_ADDR_LEN + 2)) >> 3;
+ addr = (char *)(ndopt + 1);
+ memcpy(addr, LLADDR(sdl), ETHER_ADDR_LEN);
+ break;
+ default:
+ warnmsg(LOG_ERR, __FUNCTION__,
+ "unsupported link type(%d)", sdl->sdl_type);
+ exit(1);
+ }
+
+ return;
+}
+
+struct sockaddr_dl *
+if_nametosdl(char *name)
+{
+ int mib[6] = {CTL_NET, AF_ROUTE, 0, 0, NET_RT_IFLIST, 0};
+ char *buf, *next, *lim;
+ size_t len;
+ struct if_msghdr *ifm;
+ struct sockaddr *sa, *rti_info[RTAX_MAX];
+ struct sockaddr_dl *sdl = NULL, *ret_sdl;
+
+ if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0)
+ return(NULL);
+ if ((buf = malloc(len)) == NULL)
+ return(NULL);
+ if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
+ free(buf);
+ return(NULL);
+ }
+
+ lim = buf + len;
+ for (next = buf; next < lim; next += ifm->ifm_msglen) {
+ ifm = (struct if_msghdr *)next;
+ if (ifm->ifm_type == RTM_IFINFO) {
+ sa = (struct sockaddr *)(ifm + 1);
+ get_rtaddrs(ifm->ifm_addrs, sa, rti_info);
+ if ((sa = rti_info[RTAX_IFP]) != NULL) {
+ if (sa->sa_family == AF_LINK) {
+ sdl = (struct sockaddr_dl *)sa;
+ if (strlen(name) != sdl->sdl_nlen)
+ continue; /* not same len */
+ if (strncmp(&sdl->sdl_data[0],
+ name,
+ sdl->sdl_nlen) == 0) {
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (next == lim) {
+ /* search failed */
+ free(buf);
+ return(NULL);
+ }
+
+ if ((ret_sdl = malloc(sdl->sdl_len)) == NULL)
+ return(NULL);
+ memcpy((caddr_t)ret_sdl, (caddr_t)sdl, sdl->sdl_len);
+
+ free(buf);
+ return(ret_sdl);
+}
+
+int
+getinet6sysctl(int code)
+{
+ int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, 0 };
+ int value;
+ size_t size;
+
+ mib[3] = code;
+ size = sizeof(value);
+ if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), &value, &size, NULL, 0) < 0)
+ return -1;
+ else
+ return value;
+}
+
+/*------------------------------------------------------------*/
+
+/* get ia6_flags for link-local addr on if. returns -1 on error. */
+static int
+get_llflag(const char *name)
+{
+#ifdef HAVE_GETIFADDRS
+ struct ifaddrs *ifap, *ifa;
+ struct in6_ifreq ifr6;
+ struct sockaddr_in6 *sin6;
+ int s;
+
+ if ((s = socket(PF_INET6, SOCK_DGRAM, 0)) < 0) {
+ warnmsg(LOG_ERR, __FUNCTION__, "socket(SOCK_DGRAM): %s",
+ strerror(errno));
+ exit(1);
+ }
+ if (getifaddrs(&ifap) != 0) {
+ warnmsg(LOG_ERR, __FUNCTION__, "etifaddrs: %s",
+ strerror(errno));
+ exit(1);
+ }
+
+ for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+ if (strlen(ifa->ifa_name) != strlen(name)
+ || strncmp(ifa->ifa_name, name, strlen(name)) != 0)
+ continue;
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
+ if (!IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
+ continue;
+
+ memset(&ifr6, 0, sizeof(ifr6));
+ strcpy(ifr6.ifr_name, name);
+ memcpy(&ifr6.ifr_ifru.ifru_addr, sin6, sin6->sin6_len);
+ if (ioctl(s, SIOCGIFAFLAG_IN6, &ifr6) < 0) {
+ warnmsg(LOG_ERR, __FUNCTION__,
+ "ioctl(SIOCGIFAFLAG_IN6): %s", strerror(errno));
+ exit(1);
+ }
+
+ freeifaddrs(ifap);
+ close(s);
+ return ifr6.ifr_ifru.ifru_flags6;
+ }
+
+ freeifaddrs(ifap);
+ close(s);
+ return -1;
+#else
+ int s;
+ unsigned int maxif;
+ struct ifreq *iflist;
+ struct ifconf ifconf;
+ struct ifreq *ifr, *ifr_end;
+ struct sockaddr_in6 *sin6;
+ struct in6_ifreq ifr6;
+
+ maxif = if_maxindex() + 1;
+ iflist = (struct ifreq *)malloc(maxif * BUFSIZ); /* XXX */
+ if (iflist == NULL) {
+ warnmsg(LOG_ERR, __FUNCTION__, "not enough core");
+ exit(1);
+ }
+
+ if ((s = socket(PF_INET6, SOCK_DGRAM, 0)) < 0) {
+ warnmsg(LOG_ERR, __FUNCTION__, "socket(SOCK_DGRAM): %s",
+ strerror(errno));
+ exit(1);
+ }
+ memset(&ifconf, 0, sizeof(ifconf));
+ ifconf.ifc_req = iflist;
+ ifconf.ifc_len = maxif * BUFSIZ; /* XXX */
+ if (ioctl(s, SIOCGIFCONF, &ifconf) < 0) {
+ warnmsg(LOG_ERR, __FUNCTION__, "ioctl(SIOCGIFCONF): %s",
+ strerror(errno));
+ exit(1);
+ }
+
+ /* Look for this interface in the list */
+ ifr_end = (struct ifreq *) (ifconf.ifc_buf + ifconf.ifc_len);
+ for (ifr = ifconf.ifc_req;
+ ifr < ifr_end;
+ ifr = (struct ifreq *) ((char *) &ifr->ifr_addr
+ + ifr->ifr_addr.sa_len)) {
+ if (strlen(ifr->ifr_name) != strlen(name)
+ || strncmp(ifr->ifr_name, name, strlen(name)) != 0)
+ continue;
+ if (ifr->ifr_addr.sa_family != AF_INET6)
+ continue;
+ sin6 = (struct sockaddr_in6 *)&ifr->ifr_addr;
+ if (!IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
+ continue;
+
+ memset(&ifr6, 0, sizeof(ifr6));
+ strcpy(ifr6.ifr_name, name);
+ memcpy(&ifr6.ifr_ifru.ifru_addr, sin6, sin6->sin6_len);
+ if (ioctl(s, SIOCGIFAFLAG_IN6, &ifr6) < 0) {
+ warnmsg(LOG_ERR, __FUNCTION__,
+ "ioctl(SIOCGIFAFLAG_IN6): %s", strerror(errno));
+ exit(1);
+ }
+
+ free(iflist);
+ close(s);
+ return ifr6.ifr_ifru.ifru_flags6;
+ }
+
+ free(iflist);
+ close(s);
+ return -1;
+#endif
+}
+
+#ifndef HAVE_GETIFADDRS
+static unsigned int
+if_maxindex()
+{
+ struct if_nameindex *p, *p0;
+ unsigned int max = 0;
+
+ p0 = if_nameindex();
+ for (p = p0; p && p->if_index && p->if_name; p++) {
+ if (max < p->if_index)
+ max = p->if_index;
+ }
+ if_freenameindex(p0);
+ return max;
+}
+#endif
+
+static void
+get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
+{
+ int i;
+
+ for (i = 0; i < RTAX_MAX; i++) {
+ if (addrs & (1 << i)) {
+ rti_info[i] = sa;
+ NEXT_SA(sa);
+ }
+ else
+ rti_info[i] = NULL;
+ }
+}
diff --git a/usr.sbin/rtsold/probe.c b/usr.sbin/rtsold/probe.c
new file mode 100644
index 0000000..34bb66e
--- /dev/null
+++ b/usr.sbin/rtsold/probe.c
@@ -0,0 +1,191 @@
+/* $KAME: probe.c,v 1.10 2000/08/13 06:14:59 itojun Exp $ */
+
+/*
+ * Copyright (C) 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/queue.h>
+
+#include <net/if.h>
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+#include <net/if_var.h>
+#endif /* __FreeBSD__ >= 3 */
+
+#include <netinet/in.h>
+#include <netinet6/in6_var.h>
+#include <netinet/icmp6.h>
+#include <netinet6/nd6.h>
+
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <syslog.h>
+#include <stdlib.h>
+
+#include "rtsold.h"
+
+static struct msghdr sndmhdr;
+static struct iovec sndiov[2];
+static int probesock;
+static void sendprobe __P((struct in6_addr *addr, int ifindex));
+
+
+int
+probe_init()
+{
+ int scmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) +
+ CMSG_SPACE(sizeof(int));
+ static u_char *sndcmsgbuf = NULL;
+
+ if (sndcmsgbuf == NULL &&
+ (sndcmsgbuf = (u_char *)malloc(scmsglen)) == NULL) {
+ warnmsg(LOG_ERR, __FUNCTION__, "malloc failed");
+ return(-1);
+ }
+
+ if ((probesock = socket(AF_INET6, SOCK_RAW, IPPROTO_NONE)) < 0) {
+ warnmsg(LOG_ERR, __FUNCTION__, "socket: %s", strerror(errno));
+ return(-1);
+ }
+
+ /* make the socket send-only */
+ if (shutdown(probesock, 0)) {
+ warnmsg(LOG_ERR, __FUNCTION__, "shutdown: %s", strerror(errno));
+ return(-1);
+ }
+
+ /* initialize msghdr for sending packets */
+ sndmhdr.msg_namelen = sizeof(struct sockaddr_in6);
+ sndmhdr.msg_iov = sndiov;
+ sndmhdr.msg_iovlen = 1;
+ sndmhdr.msg_control = (caddr_t)sndcmsgbuf;
+ sndmhdr.msg_controllen = scmsglen;
+
+ return(0);
+}
+
+/*
+ * Probe if each router in the default router list is still alive.
+ */
+void
+defrouter_probe(int ifindex)
+{
+ struct in6_drlist dr;
+ int s, i;
+ u_char ntopbuf[INET6_ADDRSTRLEN];
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ warnmsg(LOG_ERR, __FUNCTION__, "socket: %s", strerror(errno));
+ return;
+ }
+ bzero(&dr, sizeof(dr));
+ strcpy(dr.ifname, "lo0"); /* dummy interface */
+ if (ioctl(s, SIOCGDRLST_IN6, (caddr_t)&dr) < 0) {
+ warnmsg(LOG_ERR, __FUNCTION__, "ioctl(SIOCGDRLST_IN6): %s",
+ strerror(errno));
+ goto closeandend;
+ }
+
+ for(i = 0; dr.defrouter[i].if_index && i < PRLSTSIZ; i++) {
+ if (ifindex && dr.defrouter[i].if_index == ifindex) {
+ /* sanity check */
+ if (!IN6_IS_ADDR_LINKLOCAL(&dr.defrouter[i].rtaddr)) {
+ warnmsg(LOG_ERR, __FUNCTION__,
+ "default router list contains a "
+ "non-linklocal address(%s)",
+ inet_ntop(AF_INET6,
+ &dr.defrouter[i].rtaddr,
+ ntopbuf, INET6_ADDRSTRLEN));
+ continue; /* ignore the address */
+ }
+ sendprobe(&dr.defrouter[i].rtaddr,
+ dr.defrouter[i].if_index);
+ }
+ }
+
+ closeandend:
+ close(s);
+ return;
+}
+
+static void
+sendprobe(struct in6_addr *addr, int ifindex)
+{
+ struct sockaddr_in6 sa6_probe;
+ struct in6_pktinfo *pi;
+ struct cmsghdr *cm;
+ u_char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];;
+
+ bzero(&sa6_probe, sizeof(sa6_probe));
+ sa6_probe.sin6_family = AF_INET6;
+ sa6_probe.sin6_len = sizeof(sa6_probe);
+ sa6_probe.sin6_addr = *addr;
+
+ sndmhdr.msg_name = (caddr_t)&sa6_probe;
+ sndmhdr.msg_iov[0].iov_base = NULL;
+ sndmhdr.msg_iov[0].iov_len = 0;
+
+ cm = CMSG_FIRSTHDR(&sndmhdr);
+ /* specify the outgoing interface */
+ cm->cmsg_level = IPPROTO_IPV6;
+ cm->cmsg_type = IPV6_PKTINFO;
+ cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
+ pi = (struct in6_pktinfo *)CMSG_DATA(cm);
+ memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); /*XXX*/
+ pi->ipi6_ifindex = ifindex;
+
+ /* specify the hop limit of the packet for safety */
+ {
+ int hoplimit = 1;
+
+ cm = CMSG_NXTHDR(&sndmhdr, cm);
+ cm->cmsg_level = IPPROTO_IPV6;
+ cm->cmsg_type = IPV6_HOPLIMIT;
+ cm->cmsg_len = CMSG_LEN(sizeof(int));
+ memcpy(CMSG_DATA(cm), &hoplimit, sizeof(int));
+ }
+
+ warnmsg(LOG_DEBUG, __FUNCTION__, "probe a router %s on %s",
+ inet_ntop(AF_INET6, addr, ntopbuf, INET6_ADDRSTRLEN),
+ if_indextoname(ifindex, ifnamebuf));
+
+ if (sendmsg(probesock, &sndmhdr, 0))
+ warnmsg(LOG_ERR, __FUNCTION__, "sendmsg on %s: %s",
+ if_indextoname(ifindex, ifnamebuf), strerror(errno));
+
+ return;
+}
diff --git a/usr.sbin/rtsold/rtsock.c b/usr.sbin/rtsold/rtsock.c
new file mode 100644
index 0000000..f1c4be8
--- /dev/null
+++ b/usr.sbin/rtsold/rtsock.c
@@ -0,0 +1,179 @@
+/* $KAME: rtsock.c,v 1.3 2000/10/10 08:46:45 itojun Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (C) 2000 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/time.h>
+#include <sys/queue.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+
+#include <netinet/in.h>
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+
+#include <time.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <err.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include "rtsold.h"
+
+#define ROUNDUP(a, size) \
+ (((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a))
+
+#define NEXT_SA(ap) (ap) = (struct sockaddr *) \
+ ((caddr_t)(ap) + \
+ ((ap)->sa_len ? ROUNDUP((ap)->sa_len, sizeof(u_long)) \
+ : sizeof(u_long)))
+
+#ifdef RTM_IFANNOUNCE /*NetBSD 1.5 or later*/
+static int rtsock_input_ifannounce __P((int, struct rt_msghdr *, char *));
+#endif
+
+static struct {
+ u_char type;
+ size_t minlen;
+ int (*func) __P((int, struct rt_msghdr *, char *));
+} rtsock_dispatch[] = {
+#ifdef RTM_IFANNOUNCE /*NetBSD 1.5 or later*/
+ { RTM_IFANNOUNCE, sizeof(struct if_announcemsghdr),
+ rtsock_input_ifannounce },
+#endif
+ { 0, NULL },
+};
+
+int
+rtsock_open()
+{
+
+ return socket(PF_ROUTE, SOCK_RAW, 0);
+}
+
+int
+rtsock_input(s)
+ int s;
+{
+ ssize_t n;
+ char msg[2048];
+ char *lim, *next;
+ struct rt_msghdr *rtm;
+ int idx;
+ size_t len;
+ int ret = 0;
+ const size_t lenlim =
+ offsetof(struct rt_msghdr, rtm_msglen) + sizeof(rtm->rtm_msglen);
+
+ n = read(s, msg, sizeof(msg));
+
+ lim = msg + n;
+ for (next = msg; next < lim; next += len) {
+ rtm = (struct rt_msghdr *)next;
+ if (lim - next < lenlim)
+ break;
+ len = rtm->rtm_msglen;
+ if (len < lenlim)
+ break;
+
+ if (dflag > 1) {
+ warnmsg(LOG_INFO, __FUNCTION__,
+ "rtmsg type %d, len=%lu", rtm->rtm_type,
+ (u_long)len);
+ }
+
+ for (idx = 0; rtsock_dispatch[idx].func; idx++) {
+ if (rtm->rtm_type != rtsock_dispatch[idx].type)
+ continue;
+ if (rtm->rtm_msglen < rtsock_dispatch[idx].minlen) {
+ warnmsg(LOG_INFO, __FUNCTION__,
+ "rtmsg type %d too short!", rtm->rtm_type);
+ continue;
+ }
+
+ ret = (*rtsock_dispatch[idx].func)(s, rtm, lim);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+#ifdef RTM_IFANNOUNCE /*NetBSD 1.5 or later*/
+static int
+rtsock_input_ifannounce(s, rtm, lim)
+ int s;
+ struct rt_msghdr *rtm;
+ char *lim;
+{
+ struct if_announcemsghdr *ifan;
+ struct ifinfo *ifinfo;
+
+ ifan = (struct if_announcemsghdr *)rtm;
+ if ((char *)(ifan + 1) > lim)
+ return -1;
+
+ switch (ifan->ifan_what) {
+ case IFAN_ARRIVAL:
+ /*
+ * XXX for NetBSD 1.5, interface index will monotonically be
+ * increased as new pcmcia card gets inserted.
+ * we may be able to do a name-based interface match,
+ * and call ifreconfig() to enable the interface again.
+ */
+ warnmsg(LOG_INFO, __FUNCTION__,
+ "interface %s inserted", ifan->ifan_name);
+ break;
+ case IFAN_DEPARTURE:
+ warnmsg(LOG_WARNING, __FUNCTION__,
+ "interface %s removed", ifan->ifan_name);
+ ifinfo = find_ifinfo(ifan->ifan_index);
+ if (ifinfo) {
+ if (dflag > 1) {
+ warnmsg(LOG_INFO, __FUNCTION__,
+ "bring interface %s to DOWN state",
+ ifan->ifan_name);
+ }
+ ifinfo->state = IFS_DOWN;
+ }
+ break;
+ }
+
+ return 0;
+}
+#endif
diff --git a/usr.sbin/rtsold/rtsol.c b/usr.sbin/rtsold/rtsol.c
new file mode 100644
index 0000000..5086ea9
--- /dev/null
+++ b/usr.sbin/rtsold/rtsol.c
@@ -0,0 +1,345 @@
+/* $KAME: rtsol.c,v 1.12 2001/11/12 11:47:11 jinmei Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/time.h>
+#include <sys/queue.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+
+#include <netinet/in.h>
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet/icmp6.h>
+
+#include <arpa/inet.h>
+
+#include <time.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <err.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include "rtsold.h"
+
+#define ALLROUTER "ff02::2"
+
+static struct msghdr rcvmhdr;
+static struct msghdr sndmhdr;
+static struct iovec rcviov[2];
+static struct iovec sndiov[2];
+static struct sockaddr_in6 from;
+
+int rssock;
+
+static struct sockaddr_in6 sin6_allrouters = {sizeof(sin6_allrouters), AF_INET6};
+
+int
+sockopen()
+{
+ int on;
+ struct icmp6_filter filt;
+ static u_char answer[1500];
+ int rcvcmsglen, sndcmsglen;
+ static u_char *rcvcmsgbuf = NULL, *sndcmsgbuf = NULL;
+
+ sndcmsglen = rcvcmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) +
+ CMSG_SPACE(sizeof(int));
+ if (rcvcmsgbuf == NULL && (rcvcmsgbuf = malloc(rcvcmsglen)) == NULL) {
+ warnmsg(LOG_ERR, __FUNCTION__,
+ "malloc for receive msghdr failed");
+ return(-1);
+ }
+ if (sndcmsgbuf == NULL && (sndcmsgbuf = malloc(sndcmsglen)) == NULL) {
+ warnmsg(LOG_ERR, __FUNCTION__,
+ "malloc for send msghdr failed");
+ return(-1);
+ }
+ memset(&sin6_allrouters, 0, sizeof(struct sockaddr_in6));
+ sin6_allrouters.sin6_family = AF_INET6;
+ sin6_allrouters.sin6_len = sizeof(sin6_allrouters);
+ if (inet_pton(AF_INET6, ALLROUTER,
+ &sin6_allrouters.sin6_addr.s6_addr) != 1) {
+ warnmsg(LOG_ERR, __FUNCTION__, "inet_pton failed for %s",
+ ALLROUTER);
+ return(-1);
+ }
+
+ if ((rssock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) {
+ warnmsg(LOG_ERR, __FUNCTION__, "socket: %s", strerror(errno));
+ return(-1);
+ }
+
+ /* specify to tell receiving interface */
+ on = 1;
+#ifdef IPV6_RECVPKTINFO
+ if (setsockopt(rssock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on,
+ sizeof(on)) < 0) {
+ warnmsg(LOG_ERR, __FUNCTION__, "IPV6_RECVPKTINFO: %s",
+ strerror(errno));
+ exit(1);
+ }
+#else /* old adv. API */
+ if (setsockopt(rssock, IPPROTO_IPV6, IPV6_PKTINFO, &on,
+ sizeof(on)) < 0) {
+ warnmsg(LOG_ERR, __FUNCTION__, "IPV6_PKTINFO: %s",
+ strerror(errno));
+ exit(1);
+ }
+#endif
+
+ on = 1;
+ /* specify to tell value of hoplimit field of received IP6 hdr */
+#ifdef IPV6_RECVHOPLIMIT
+ if (setsockopt(rssock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on,
+ sizeof(on)) < 0) {
+ warnmsg(LOG_ERR, __FUNCTION__, "IPV6_RECVHOPLIMIT: %s",
+ strerror(errno));
+ exit(1);
+ }
+#else /* old adv. API */
+ if (setsockopt(rssock, IPPROTO_IPV6, IPV6_HOPLIMIT, &on,
+ sizeof(on)) < 0) {
+ warnmsg(LOG_ERR, __FUNCTION__, "IPV6_HOPLIMIT: %s",
+ strerror(errno));
+ exit(1);
+ }
+#endif
+
+ /* specfiy to accept only router advertisements on the socket */
+ ICMP6_FILTER_SETBLOCKALL(&filt);
+ ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt);
+ if (setsockopt(rssock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
+ sizeof(filt)) == -1) {
+ warnmsg(LOG_ERR, __FUNCTION__, "setsockopt(ICMP6_FILTER): %s",
+ strerror(errno));
+ return(-1);
+ }
+
+ /* initialize msghdr for receiving packets */
+ rcviov[0].iov_base = (caddr_t)answer;
+ rcviov[0].iov_len = sizeof(answer);
+ rcvmhdr.msg_name = (caddr_t)&from;
+ rcvmhdr.msg_namelen = sizeof(from);
+ rcvmhdr.msg_iov = rcviov;
+ rcvmhdr.msg_iovlen = 1;
+ rcvmhdr.msg_control = (caddr_t) rcvcmsgbuf;
+ rcvmhdr.msg_controllen = rcvcmsglen;
+
+ /* initialize msghdr for sending packets */
+ sndmhdr.msg_namelen = sizeof(struct sockaddr_in6);
+ sndmhdr.msg_iov = sndiov;
+ sndmhdr.msg_iovlen = 1;
+ sndmhdr.msg_control = (caddr_t)sndcmsgbuf;
+ sndmhdr.msg_controllen = sndcmsglen;
+
+ return(rssock);
+}
+
+void
+sendpacket(struct ifinfo *ifinfo)
+{
+ int i;
+ struct cmsghdr *cm;
+ struct in6_pktinfo *pi;
+
+ sndmhdr.msg_name = (caddr_t)&sin6_allrouters;
+ sndmhdr.msg_iov[0].iov_base = (caddr_t)ifinfo->rs_data;
+ sndmhdr.msg_iov[0].iov_len = ifinfo->rs_datalen;
+
+ cm = CMSG_FIRSTHDR(&sndmhdr);
+ /* specify the outgoing interface */
+ cm->cmsg_level = IPPROTO_IPV6;
+ cm->cmsg_type = IPV6_PKTINFO;
+ cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
+ pi = (struct in6_pktinfo *)CMSG_DATA(cm);
+ memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); /*XXX*/
+ pi->ipi6_ifindex = ifinfo->sdl->sdl_index;
+
+ /* specify the hop limit of the packet */
+ {
+ int hoplimit = 255;
+
+ cm = CMSG_NXTHDR(&sndmhdr, cm);
+ cm->cmsg_level = IPPROTO_IPV6;
+ cm->cmsg_type = IPV6_HOPLIMIT;
+ cm->cmsg_len = CMSG_LEN(sizeof(int));
+ memcpy(CMSG_DATA(cm), &hoplimit, sizeof(int));
+ }
+
+ warnmsg(LOG_DEBUG,
+ __FUNCTION__, "send RS on %s, whose state is %d",
+ ifinfo->ifname, ifinfo->state);
+
+ i = sendmsg(rssock, &sndmhdr, 0);
+
+ if (i < 0 || i != ifinfo->rs_datalen) {
+ /*
+ * ENETDOWN is not so serious, especially when using several
+ * network cards on a mobile node. We ignore it.
+ */
+ if (errno != ENETDOWN || dflag > 0)
+ warnmsg(LOG_ERR, __FUNCTION__, "sendmsg on %s: %s",
+ ifinfo->ifname, strerror(errno));
+ }
+
+ /* update counter */
+ ifinfo->probes++;
+}
+
+void
+rtsol_input(int s)
+{
+ int i;
+ int *hlimp = NULL;
+ struct icmp6_hdr *icp;
+ int ifindex = 0;
+ struct cmsghdr *cm;
+ struct in6_pktinfo *pi = NULL;
+ struct ifinfo *ifi = NULL;
+ u_char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
+
+ /* get message */
+ if ((i = recvmsg(s, &rcvmhdr, 0)) < 0) {
+ warnmsg(LOG_ERR, __FUNCTION__, "recvmsg: %s", strerror(errno));
+ return;
+ }
+
+ /* extract optional information via Advanced API */
+ for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&rcvmhdr);
+ cm;
+ cm = (struct cmsghdr *)CMSG_NXTHDR(&rcvmhdr, cm)) {
+ if (cm->cmsg_level == IPPROTO_IPV6 &&
+ cm->cmsg_type == IPV6_PKTINFO &&
+ cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) {
+ pi = (struct in6_pktinfo *)(CMSG_DATA(cm));
+ ifindex = pi->ipi6_ifindex;
+ }
+ if (cm->cmsg_level == IPPROTO_IPV6 &&
+ cm->cmsg_type == IPV6_HOPLIMIT &&
+ cm->cmsg_len == CMSG_LEN(sizeof(int)))
+ hlimp = (int *)CMSG_DATA(cm);
+ }
+
+ if (ifindex == 0) {
+ warnmsg(LOG_ERR,
+ __FUNCTION__, "failed to get receiving interface");
+ return;
+ }
+ if (hlimp == NULL) {
+ warnmsg(LOG_ERR,
+ __FUNCTION__, "failed to get receiving hop limit");
+ return;
+ }
+
+ if (i < sizeof(struct nd_router_advert)) {
+ warnmsg(LOG_ERR,
+ __FUNCTION__, "packet size(%d) is too short", i);
+ return;
+ }
+
+ icp = (struct icmp6_hdr *)rcvmhdr.msg_iov[0].iov_base;
+
+ if (icp->icmp6_type != ND_ROUTER_ADVERT) {
+ warnmsg(LOG_ERR, __FUNCTION__,
+ "invalid icmp type(%d) from %s on %s", icp->icmp6_type,
+ inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
+ INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+
+ if (icp->icmp6_code != 0) {
+ warnmsg(LOG_ERR, __FUNCTION__,
+ "invalid icmp code(%d) from %s on %s", icp->icmp6_code,
+ inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
+ INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+
+ if (*hlimp != 255) {
+ warnmsg(LOG_NOTICE, __FUNCTION__,
+ "invalid RA with hop limit(%d) from %s on %s",
+ *hlimp,
+ inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
+ INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+
+ if (pi && !IN6_IS_ADDR_LINKLOCAL(&from.sin6_addr)) {
+ warnmsg(LOG_NOTICE, __FUNCTION__,
+ "invalid RA with non link-local source from %s on %s",
+ inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
+ INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+
+ /* xxx: more validation? */
+
+ if ((ifi = find_ifinfo(pi->ipi6_ifindex)) == NULL) {
+ warnmsg(LOG_NOTICE, __FUNCTION__,
+ "received RA from %s on an unexpeced IF(%s)",
+ inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
+ INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+
+ warnmsg(LOG_DEBUG, __FUNCTION__,
+ "received RA from %s on %s, state is %d",
+ inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
+ INET6_ADDRSTRLEN),
+ ifi->ifname, ifi->state);
+
+ ifi->racnt++;
+
+ switch(ifi->state) {
+ case IFS_IDLE: /* should be ignored */
+ case IFS_DELAY: /* right? */
+ break;
+ case IFS_PROBE:
+ ifi->state = IFS_IDLE;
+ ifi->probes = 0;
+ rtsol_timer_update(ifi);
+ break;
+ }
+}
diff --git a/usr.sbin/rtsold/rtsold.8 b/usr.sbin/rtsold/rtsold.8
new file mode 100644
index 0000000..f148694
--- /dev/null
+++ b/usr.sbin/rtsold/rtsold.8
@@ -0,0 +1,224 @@
+.\" $KAME: rtsold.8,v 1.16 2000/10/15 13:19:05 itojun Exp $
+.\"
+.\" Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the project nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd May 17, 1998
+.Dt RTSOLD 8
+.Os
+.\"
+.Sh NAME
+.Nm rtsold , rtsol
+.Nd router solicitation daemon
+.\"
+.Sh SYNOPSIS
+.Nm
+.Op Fl dDfm1
+.Ar interface ...
+.Nm
+.Op Fl dDfm1
+.Fl a
+.Nm rtsol
+.Op Fl dD
+.Ar interface ...
+.Nm rtsol
+.Op Fl dD
+.Fl a
+.\"
+.Sh DESCRIPTION
+.Nm
+is the daemon program to send ICMPv6 Router Solicitation messages
+on the specified interfaces.
+If a node (re)attaches to a link,
+.Nm
+sends some Router Solicitations on the link destined to the link-local scope
+all-routers multicast address to discover new routers
+and to get non link-local addresses.
+.Pp
+.Nm
+should be used on IPv6 hosts
+(non-router nodes)
+only.
+.Pp
+If you invoke the program as
+.Nm rtsol ,
+it will transmit probes from the specified
+.Ar interface ,
+without becoming a daemon.
+In other words,
+.Nm rtsol
+behaves as
+.Do
+.Nm
+.Fl f1
+.Ar interfaces
+.Dc .
+.Pp
+Specifically,
+.Nm
+sends at most 3 Router Solicitations on an interface
+after one of the following events:
+.Pp
+.Bl -bullet -compact
+.It
+Just after invocation of
+.Nm
+daemon.
+.It
+The interface is up after a temporary interface failure.
+.Nm
+detects such failures by periodically probing to see if the status
+of the interface is active or not.
+Note that some network cards and drivers do not allow the extraction
+of link state.
+In such cases,
+.Nm
+cannot detect the change of the interface status.
+.It
+Every 60 seconds if the
+.Fl m
+option is specified and the
+.Nm
+daemon cannot get the interface status.
+This feature does not conform to the IPv6 neighbor discovery
+specification, but is provided for mobile stations.
+The default interval for router advertisements, which is on the order of 10
+minutes, is slightly long for mobile stations.
+This feature is provided
+for such stations so that they can find new routers as soon as possible
+when they attach to another link.
+.El
+.Lp
+Once
+.Nm
+has sent a Router Solicitation, and has received a valid Router Advertisement,
+it refrains from sending additional solicitations on that interface, until
+the next time one of the above events occurs.
+.Lp
+When sending a Router Solicitation on an interface,
+.Nm
+includes a Source Link-layer address option if the interface
+has a link-layer address.
+.Pp
+Upon receipt of signal
+.Dv SIGUSR1 ,
+.Nm
+will dump the current internal state into
+.Pa /var/run/rtsold.dump .
+.\"
+.Sh OPTIONS
+.Bl -tag -width indent
+.It Fl a
+Autoprobe outgoing interface.
+.Nm
+will try to find a non-loopback, non-point-to-point, IPv6-capable interface.
+If
+.Nm
+finds multiple interfaces,
+.Nm
+will exit with error.
+.\"
+.It Fl d
+Enable debugging.
+.It Fl D
+Enable more debugging including the printing of internal timer information.
+.It Fl f
+.Fl f
+prevents
+.Nm
+from becoming a daemon (foreground mode).
+Warning messages are generated to standard error
+instead of
+.Xr syslog 3 .
+.It Fl m
+Enable mobility support.
+If this option is specified,
+.Nm
+sends probing packets to default routers that have advertised Router
+Advertisements
+when the node (re)attaches to an interface.
+Moreover, if the option is specified,
+.Nm
+periodically sends Router Solicitation on an interface that does not support
+.Dv SIOCGIFMEDIA
+ioctl.
+.It Fl 1
+Perform only one probe.
+Transmit Router Solicitation packets until at least one valid Router
+Advertisement packet has arrived on each
+.Ar interface ,
+then exit.
+.El
+.Sh DIAGNOSTICS
+.Ex -std rtsold rtsol
+.\"
+.Sh FILES
+.Bl -tag -width /var/run/rtsold.dump -compact
+.It Pa /var/run/rtsold.pid
+the pid of the currently running
+.Nm .
+.It Pa /var/run/rtsold.dump
+dumps internal state on.
+.El
+.\"
+.Sh SEE ALSO
+.Xr rtadvd 8 ,
+.Xr sysctl 8
+.\"
+.Sh HISTORY
+The
+.Nm
+command is based on the
+.Nm rtsol
+command, which first appeared in WIDE/KAME IPv6 protocol stack kit.
+.Nm rtsol
+is now integrated into
+.Xr rtsold 8 .
+.\"
+.Sh BUGS
+In some operating systems, when a PCMCIA network card is removed
+and reinserted, the corresponding interface index is changed.
+However,
+.Nm
+assumes such changes will not occur, and always uses the index that
+it got at invocation. As a result,
+.Nm
+may not work if you reinsert a network card.
+In such a case,
+.Nm
+should be killed and restarted.
+.Pp
+The IPv6 autoconfiguration specification assumes a single-interface host.
+You may see kernel error messages if you try to autoconfigure a host with
+multiple interfaces.
+Also, it seems contradictory for
+.Nm
+to accept multiple
+.Ar interface
+arguments.
diff --git a/usr.sbin/rtsold/rtsold.c b/usr.sbin/rtsold/rtsold.c
new file mode 100644
index 0000000..82a7560
--- /dev/null
+++ b/usr.sbin/rtsold/rtsold.c
@@ -0,0 +1,792 @@
+/* $KAME: rtsold.c,v 1.31 2001/05/22 06:03:06 jinmei Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+
+#include <netinet/in.h>
+#include <netinet/icmp6.h>
+
+#include <signal.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <err.h>
+#include <stdarg.h>
+#include <ifaddrs.h>
+#include "rtsold.h"
+
+struct ifinfo *iflist;
+struct timeval tm_max = {0x7fffffff, 0x7fffffff};
+int aflag = 0;
+int dflag = 0;
+static int log_upto = 999;
+static int fflag = 0;
+
+/* protocol constatns */
+#define MAX_RTR_SOLICITATION_DELAY 1 /* second */
+#define RTR_SOLICITATION_INTERVAL 4 /* seconds */
+#define MAX_RTR_SOLICITATIONS 3 /* times */
+
+/* implementation dependent constants */
+#define PROBE_INTERVAL 60 /* secondes XXX: should be configurable */
+
+/* utility macros */
+/* a < b */
+#define TIMEVAL_LT(a, b) (((a).tv_sec < (b).tv_sec) ||\
+ (((a).tv_sec == (b).tv_sec) && \
+ ((a).tv_usec < (b).tv_usec)))
+
+/* a <= b */
+#define TIMEVAL_LEQ(a, b) (((a).tv_sec < (b).tv_sec) ||\
+ (((a).tv_sec == (b).tv_sec) &&\
+ ((a).tv_usec <= (b).tv_usec)))
+
+/* a == b */
+#define TIMEVAL_EQ(a, b) (((a).tv_sec==(b).tv_sec) && ((a).tv_usec==(b).tv_usec))
+
+int main __P((int argc, char *argv[]));
+
+/* static variables and functions */
+static int mobile_node = 0;
+static int do_dump;
+static char *dumpfilename = "/var/run/rtsold.dump"; /* XXX: should be configurable */
+static char *pidfilename = "/var/run/rtsold.pid"; /* should be configurable */
+
+static int ifconfig __P((char *ifname));
+#if 0
+static int ifreconfig __P((char *ifname));
+#endif
+static int make_packet __P((struct ifinfo *ifinfo));
+static struct timeval *rtsol_check_timer __P((void));
+static void TIMEVAL_ADD __P((struct timeval *a, struct timeval *b,
+ struct timeval *result));
+static void TIMEVAL_SUB __P((struct timeval *a, struct timeval *b,
+ struct timeval *result));
+
+static void rtsold_set_dump_file __P((void));
+static void usage __P((char *progname));
+static char **autoifprobe __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int s, rtsock, maxfd, ch;
+ int once = 0;
+ struct timeval *timeout;
+ struct fd_set fdset;
+ char *argv0;
+ char *opts;
+
+ /*
+ * Initialization
+ */
+ argv0 = argv[0];
+
+ /* get option */
+ if (argv0 && argv0[strlen(argv0) - 1] != 'd') {
+ fflag = 1;
+ once = 1;
+ opts = "adD";
+ } else
+ opts = "adDfm1";
+
+ while ((ch = getopt(argc, argv, opts)) != -1) {
+ switch (ch) {
+ case 'a':
+ aflag = 1;
+ break;
+ case 'd':
+ dflag = 1;
+ break;
+ case 'D':
+ dflag = 2;
+ break;
+ case 'f':
+ fflag = 1;
+ break;
+ case 'm':
+ mobile_node = 1;
+ break;
+ case '1':
+ once = 1;
+ break;
+ default:
+ usage(argv0);
+ /*NOTREACHED*/
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (aflag) {
+ int i;
+
+ if (argc != 0) {
+ usage(argv0);
+ /*NOTREACHED*/
+ }
+
+ argv = autoifprobe();
+ if (!argv) {
+ errx(1, "could not autoprobe interface");
+ /*NOTREACHED*/
+ }
+
+ for (i = 0; argv[i]; i++)
+ ;
+ argc = i;
+ }
+ if (argc == 0) {
+ usage(argv0);
+ /*NOTREACHED*/
+ }
+
+ /* set log level */
+ if (dflag == 0)
+ log_upto = LOG_NOTICE;
+ if (!fflag) {
+ char *ident;
+ ident = strrchr(argv0, '/');
+ if (!ident)
+ ident = argv0;
+ else
+ ident++;
+ openlog(ident, LOG_NDELAY|LOG_PID, LOG_DAEMON);
+ if (log_upto >= 0)
+ setlogmask(LOG_UPTO(log_upto));
+ }
+
+#ifndef HAVE_ARC4RANDOM
+ /* random value initilization */
+ srandom((u_long)time(NULL));
+#endif
+
+ /* warn if accept_rtadv is down */
+ if (!getinet6sysctl(IPV6CTL_ACCEPT_RTADV))
+ warnx("kernel is configured not to accept RAs");
+ /* warn if forwarding is up */
+ if (getinet6sysctl(IPV6CTL_FORWARDING))
+ warnx("kernel is configured as a router, not a host");
+
+ /* initialization to dump internal status to a file */
+ if (signal(SIGUSR1, (void *)rtsold_set_dump_file) < 0) {
+ errx(1, "failed to set signal for dump status");
+ /*NOTREACHED*/
+ }
+
+ /*
+ * Open a socket for sending RS and receiving RA.
+ * This should be done before calling ifinit(), since the function
+ * uses the socket.
+ */
+ if ((s = sockopen()) < 0) {
+ errx(1, "failed to open a socket");
+ /*NOTREACHED*/
+ }
+ maxfd = s;
+ if ((rtsock = rtsock_open()) < 0) {
+ errx(1, "failed to open a socket");
+ /*NOTREACHED*/
+ }
+ if (rtsock > maxfd)
+ maxfd = rtsock;
+
+ /* configuration per interface */
+ if (ifinit()) {
+ errx(1, "failed to initilizatoin interfaces");
+ /*NOTREACHED*/
+ }
+ while (argc--) {
+ if (ifconfig(*argv)) {
+ errx(1, "failed to initialize %s", *argv);
+ /*NOTREACHED*/
+ }
+ argv++;
+ }
+
+ /* setup for probing default routers */
+ if (probe_init()) {
+ errx(1, "failed to setup for probing routers");
+ /*NOTREACHED*/
+ }
+
+ if (!fflag)
+ daemon(0, 0); /* act as a daemon */
+
+ /* dump the current pid */
+ if (!once) {
+ pid_t pid = getpid();
+ FILE *fp;
+
+ if ((fp = fopen(pidfilename, "w")) == NULL)
+ warnmsg(LOG_ERR, __FUNCTION__,
+ "failed to open a log file(%s): %s",
+ pidfilename, strerror(errno));
+ else {
+ fprintf(fp, "%d\n", pid);
+ fclose(fp);
+ }
+ }
+
+ FD_ZERO(&fdset);
+ FD_SET(s, &fdset);
+ FD_SET(rtsock, &fdset);
+ while (1) { /* main loop */
+ int e;
+ struct fd_set select_fd = fdset;
+
+ if (do_dump) { /* SIGUSR1 */
+ do_dump = 0;
+ rtsold_dump_file(dumpfilename);
+ }
+
+ timeout = rtsol_check_timer();
+
+ if (once) {
+ struct ifinfo *ifi;
+
+ /* if we have no timeout, we are done (or failed) */
+ if (timeout == NULL)
+ break;
+
+ /* if all interfaces have got RA packet, we are done */
+ for (ifi = iflist; ifi; ifi = ifi->next) {
+ if (ifi->state != IFS_DOWN && ifi->racnt == 0)
+ break;
+ }
+ if (ifi == NULL)
+ break;
+ }
+ e = select(maxfd + 1, &select_fd, NULL, NULL, timeout);
+ if (e < 1) {
+ if (e < 0 && errno != EINTR) {
+ warnmsg(LOG_ERR, __FUNCTION__, "select: %s",
+ strerror(errno));
+ }
+ continue;
+ }
+
+ /* packet reception */
+ if (FD_ISSET(rtsock, &select_fd))
+ rtsock_input(rtsock);
+ if (FD_ISSET(s, &select_fd))
+ rtsol_input(s);
+ }
+ /* NOTREACHED */
+
+ return 0;
+}
+
+static int
+ifconfig(char *ifname)
+{
+ struct ifinfo *ifinfo;
+ struct sockaddr_dl *sdl;
+ int flags;
+
+ if ((sdl = if_nametosdl(ifname)) == NULL) {
+ warnmsg(LOG_ERR, __FUNCTION__,
+ "failed to get link layer information for %s", ifname);
+ return(-1);
+ }
+ if (find_ifinfo(sdl->sdl_index)) {
+ warnmsg(LOG_ERR, __FUNCTION__,
+ "interface %s was already configured", ifname);
+ free(sdl);
+ return(-1);
+ }
+
+ if ((ifinfo = malloc(sizeof(*ifinfo))) == NULL) {
+ warnmsg(LOG_ERR, __FUNCTION__, "memory allocation failed");
+ free(sdl);
+ return(-1);
+ }
+ memset(ifinfo, 0, sizeof(*ifinfo));
+ ifinfo->sdl = sdl;
+
+ strncpy(ifinfo->ifname, ifname, sizeof(ifinfo->ifname));
+
+ /* construct a router solicitation message */
+ if (make_packet(ifinfo))
+ goto bad;
+
+ /*
+ * check if the interface is available.
+ * also check if SIOCGIFMEDIA ioctl is OK on the interface.
+ */
+ ifinfo->mediareqok = 1;
+ ifinfo->active = interface_status(ifinfo);
+ if (!ifinfo->mediareqok) {
+ /*
+ * probe routers periodically even if the link status
+ * does not change.
+ */
+ ifinfo->probeinterval = PROBE_INTERVAL;
+ }
+
+ /* activate interface: interface_up returns 0 on success */
+ flags = interface_up(ifinfo->ifname);
+ if (flags == 0)
+ ifinfo->state = IFS_DELAY;
+ else if (flags == IFS_TENTATIVE)
+ ifinfo->state = IFS_TENTATIVE;
+ else
+ ifinfo->state = IFS_DOWN;
+
+ rtsol_timer_update(ifinfo);
+
+ /* link into chain */
+ if (iflist)
+ ifinfo->next = iflist;
+ iflist = ifinfo;
+
+ return(0);
+
+ bad:
+ free(ifinfo->sdl);
+ free(ifinfo);
+ return(-1);
+}
+
+#if 0
+static int
+ifreconfig(char *ifname)
+{
+ struct ifinfo *ifi, *prev;
+ int rv;
+
+ prev = NULL;
+ for (ifi = iflist; ifi; ifi = ifi->next) {
+ if (strncmp(ifi->ifname, ifname, sizeof(ifi->ifname)) == 0)
+ break;
+ prev = ifi;
+ }
+ prev->next = ifi->next;
+
+ rv = ifconfig(ifname);
+
+ /* reclaim it after ifconfig() in case ifname is pointer inside ifi */
+ if (ifi->rs_data)
+ free(ifi->rs_data);
+ free(ifi->sdl);
+ free(ifi);
+
+ return rv;
+}
+#endif
+
+struct ifinfo *
+find_ifinfo(int ifindex)
+{
+ struct ifinfo *ifi;
+
+ for (ifi = iflist; ifi; ifi = ifi->next)
+ if (ifi->sdl->sdl_index == ifindex)
+ return(ifi);
+
+ return(NULL);
+}
+
+static int
+make_packet(struct ifinfo *ifinfo)
+{
+ char *buf;
+ struct nd_router_solicit *rs;
+ size_t packlen = sizeof(struct nd_router_solicit), lladdroptlen = 0;
+
+ if ((lladdroptlen = lladdropt_length(ifinfo->sdl)) == 0) {
+ warnmsg(LOG_INFO, __FUNCTION__,
+ "link-layer address option has null length"
+ " on %s. Treat as not included.", ifinfo->ifname);
+ }
+ packlen += lladdroptlen;
+ ifinfo->rs_datalen = packlen;
+
+ /* allocate buffer */
+ if ((buf = malloc(packlen)) == NULL) {
+ warnmsg(LOG_ERR, __FUNCTION__,
+ "memory allocation failed for %s", ifinfo->ifname);
+ return(-1);
+ }
+ ifinfo->rs_data = buf;
+
+ /* fill in the message */
+ rs = (struct nd_router_solicit *)buf;
+ rs->nd_rs_type = ND_ROUTER_SOLICIT;
+ rs->nd_rs_code = 0;
+ rs->nd_rs_cksum = 0;
+ rs->nd_rs_reserved = 0;
+ buf += sizeof(*rs);
+
+ /* fill in source link-layer address option */
+ if (lladdroptlen)
+ lladdropt_fill(ifinfo->sdl, (struct nd_opt_hdr *)buf);
+
+ return(0);
+}
+
+static struct timeval *
+rtsol_check_timer()
+{
+ static struct timeval returnval;
+ struct timeval now, rtsol_timer;
+ struct ifinfo *ifinfo;
+ int flags;
+
+ gettimeofday(&now, NULL);
+
+ rtsol_timer = tm_max;
+
+ for (ifinfo = iflist; ifinfo; ifinfo = ifinfo->next) {
+ if (TIMEVAL_LEQ(ifinfo->expire, now)) {
+ if (dflag > 1)
+ warnmsg(LOG_DEBUG, __FUNCTION__,
+ "timer expiration on %s, "
+ "state = %d", ifinfo->ifname,
+ ifinfo->state);
+
+ switch (ifinfo->state) {
+ case IFS_DOWN:
+ case IFS_TENTATIVE:
+ /* interface_up returns 0 on success */
+ flags = interface_up(ifinfo->ifname);
+ if (flags == 0)
+ ifinfo->state = IFS_DELAY;
+ else if (flags == IFS_TENTATIVE)
+ ifinfo->state = IFS_TENTATIVE;
+ else
+ ifinfo->state = IFS_DOWN;
+ break;
+ case IFS_IDLE:
+ {
+ int oldstatus = ifinfo->active;
+ int probe = 0;
+
+ ifinfo->active =
+ interface_status(ifinfo);
+
+ if (oldstatus != ifinfo->active) {
+ warnmsg(LOG_DEBUG, __FUNCTION__,
+ "%s status is changed"
+ " from %d to %d",
+ ifinfo->ifname,
+ oldstatus, ifinfo->active);
+ probe = 1;
+ ifinfo->state = IFS_DELAY;
+ }
+ else if (ifinfo->probeinterval &&
+ (ifinfo->probetimer -=
+ ifinfo->timer.tv_sec) <= 0) {
+ /* probe timer expired */
+ ifinfo->probetimer =
+ ifinfo->probeinterval;
+ probe = 1;
+ ifinfo->state = IFS_PROBE;
+ }
+
+ if (probe && mobile_node)
+ defrouter_probe(ifinfo->sdl->sdl_index);
+ break;
+ }
+ case IFS_DELAY:
+ ifinfo->state = IFS_PROBE;
+ sendpacket(ifinfo);
+ break;
+ case IFS_PROBE:
+ if (ifinfo->probes < MAX_RTR_SOLICITATIONS)
+ sendpacket(ifinfo);
+ else {
+ warnmsg(LOG_INFO, __FUNCTION__,
+ "No answer "
+ "after sending %d RSs",
+ ifinfo->probes);
+ ifinfo->probes = 0;
+ ifinfo->state = IFS_IDLE;
+ }
+ break;
+ }
+ rtsol_timer_update(ifinfo);
+ }
+
+ if (TIMEVAL_LT(ifinfo->expire, rtsol_timer))
+ rtsol_timer = ifinfo->expire;
+ }
+
+ if (TIMEVAL_EQ(rtsol_timer, tm_max)) {
+ warnmsg(LOG_DEBUG, __FUNCTION__, "there is no timer");
+ return(NULL);
+ }
+ else if (TIMEVAL_LT(rtsol_timer, now))
+ /* this may occur when the interval is too small */
+ returnval.tv_sec = returnval.tv_usec = 0;
+ else
+ TIMEVAL_SUB(&rtsol_timer, &now, &returnval);
+
+ if (dflag > 1)
+ warnmsg(LOG_DEBUG, __FUNCTION__, "New timer is %ld:%08ld",
+ (long)returnval.tv_sec, (long)returnval.tv_usec);
+
+ return(&returnval);
+}
+
+void
+rtsol_timer_update(struct ifinfo *ifinfo)
+{
+#define MILLION 1000000
+#define DADRETRY 10 /* XXX: adhoc */
+ long interval;
+ struct timeval now;
+
+ bzero(&ifinfo->timer, sizeof(ifinfo->timer));
+
+ switch (ifinfo->state) {
+ case IFS_DOWN:
+ case IFS_TENTATIVE:
+ if (++ifinfo->dadcount > DADRETRY) {
+ ifinfo->dadcount = 0;
+ ifinfo->timer.tv_sec = PROBE_INTERVAL;
+ }
+ else
+ ifinfo->timer.tv_sec = 1;
+ break;
+ case IFS_IDLE:
+ if (mobile_node) {
+ /* XXX should be configurable */
+ ifinfo->timer.tv_sec = 3;
+ }
+ else
+ ifinfo->timer = tm_max; /* stop timer(valid?) */
+ break;
+ case IFS_DELAY:
+#ifndef HAVE_ARC4RANDOM
+ interval = random() % (MAX_RTR_SOLICITATION_DELAY * MILLION);
+#else
+ interval = arc4random() % (MAX_RTR_SOLICITATION_DELAY * MILLION);
+#endif
+ ifinfo->timer.tv_sec = interval / MILLION;
+ ifinfo->timer.tv_usec = interval % MILLION;
+ break;
+ case IFS_PROBE:
+ if (ifinfo->probes < MAX_RTR_SOLICITATIONS)
+ ifinfo->timer.tv_sec = RTR_SOLICITATION_INTERVAL;
+ else {
+ /*
+ * After sending MAX_RTR_SOLICITATIONS solicitations,
+ * we're just waiting for possible replies; there
+ * will be no more solicatation. Thus, we change
+ * the timer value to MAX_RTR_SOLICITATION_DELAY based
+ * on RFC 2461, Section 6.3.7.
+ */
+ ifinfo->timer.tv_sec = MAX_RTR_SOLICITATION_DELAY;
+ }
+ break;
+ default:
+ warnmsg(LOG_ERR, __FUNCTION__,
+ "illegal interface state(%d) on %s",
+ ifinfo->state, ifinfo->ifname);
+ return;
+ }
+
+ /* reset the timer */
+ if (TIMEVAL_EQ(ifinfo->timer, tm_max)) {
+ ifinfo->expire = tm_max;
+ warnmsg(LOG_DEBUG, __FUNCTION__,
+ "stop timer for %s", ifinfo->ifname);
+ }
+ else {
+ gettimeofday(&now, NULL);
+ TIMEVAL_ADD(&now, &ifinfo->timer, &ifinfo->expire);
+
+ if (dflag > 1)
+ warnmsg(LOG_DEBUG, __FUNCTION__,
+ "set timer for %s to %d:%d", ifinfo->ifname,
+ (int)ifinfo->timer.tv_sec,
+ (int)ifinfo->timer.tv_usec);
+ }
+
+#undef MILLION
+}
+
+/* timer related utility functions */
+#define MILLION 1000000
+
+/* result = a + b */
+static void
+TIMEVAL_ADD(struct timeval *a, struct timeval *b, struct timeval *result)
+{
+ long l;
+
+ if ((l = a->tv_usec + b->tv_usec) < MILLION) {
+ result->tv_usec = l;
+ result->tv_sec = a->tv_sec + b->tv_sec;
+ }
+ else {
+ result->tv_usec = l - MILLION;
+ result->tv_sec = a->tv_sec + b->tv_sec + 1;
+ }
+}
+
+/*
+ * result = a - b
+ * XXX: this function assumes that a >= b.
+ */
+void
+TIMEVAL_SUB(struct timeval *a, struct timeval *b, struct timeval *result)
+{
+ long l;
+
+ if ((l = a->tv_usec - b->tv_usec) >= 0) {
+ result->tv_usec = l;
+ result->tv_sec = a->tv_sec - b->tv_sec;
+ }
+ else {
+ result->tv_usec = MILLION + l;
+ result->tv_sec = a->tv_sec - b->tv_sec - 1;
+ }
+}
+
+static void
+rtsold_set_dump_file()
+{
+ do_dump = 1;
+}
+
+static void
+usage(char *progname)
+{
+ if (progname && progname[strlen(progname) - 1] != 'd') {
+ fprintf(stderr, "usage: rtsol [-dD] interfaces...\n");
+ fprintf(stderr, "usage: rtsol [-dD] -a\n");
+ } else {
+ fprintf(stderr, "usage: rtsold [-adDfm1] interfaces...\n");
+ fprintf(stderr, "usage: rtsold [-dDfm1] -a\n");
+ }
+ exit(1);
+}
+
+void
+#if __STDC__
+warnmsg(int priority, const char *func, const char *msg, ...)
+#else
+warnmsg(priority, func, msg, va_alist)
+ int priority;
+ const char *func;
+ const char *msg;
+ va_dcl
+#endif
+{
+ va_list ap;
+ char buf[BUFSIZ];
+
+ va_start(ap, msg);
+ if (fflag) {
+ if (priority <= log_upto) {
+ (void)vfprintf(stderr, msg, ap);
+ (void)fprintf(stderr, "\n");
+ }
+ } else {
+ snprintf(buf, sizeof(buf), "<%s> %s", func, msg);
+ msg = buf;
+ vsyslog(priority, msg, ap);
+ }
+ va_end(ap);
+}
+
+static char **
+autoifprobe()
+{
+#ifndef HAVE_GETIFADDRS
+ errx(1, "-a is not available with the configuration");
+#else
+ static char ifname[IFNAMSIZ + 1];
+ static char *argv[2];
+ struct ifaddrs *ifap, *ifa, *target;
+
+ if (getifaddrs(&ifap) != 0)
+ return NULL;
+
+ target = NULL;
+ /* find an ethernet */
+ for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+ if ((ifa->ifa_flags & IFF_UP) == 0)
+ continue;
+ if ((ifa->ifa_flags & IFF_POINTOPOINT) != 0)
+ continue;
+ if ((ifa->ifa_flags & IFF_LOOPBACK) != 0)
+ continue;
+ if ((ifa->ifa_flags & IFF_MULTICAST) == 0)
+ continue;
+
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+
+ if (target && strcmp(target->ifa_name, ifa->ifa_name) == 0)
+ continue;
+
+ if (!target)
+ target = ifa;
+ else {
+ /* if we find multiple candidates, failure. */
+ if (dflag > 1)
+ warnx("multiple interfaces found");
+ target = NULL;
+ break;
+ }
+ }
+
+ if (target) {
+ strncpy(ifname, target->ifa_name, sizeof(ifname) - 1);
+ ifname[sizeof(ifname) - 1] = '\0';
+ argv[0] = ifname;
+ argv[1] = NULL;
+
+ if (dflag > 0)
+ warnx("probing %s", argv[0]);
+ }
+ freeifaddrs(ifap);
+ if (target)
+ return argv;
+ else
+ return (char **)NULL;
+#endif
+}
diff --git a/usr.sbin/rtsold/rtsold.h b/usr.sbin/rtsold/rtsold.h
new file mode 100644
index 0000000..52175fa
--- /dev/null
+++ b/usr.sbin/rtsold/rtsold.h
@@ -0,0 +1,95 @@
+/* $KAME: rtsold.h,v 1.11 2000/10/10 06:18:04 itojun Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct ifinfo {
+ struct ifinfo *next; /* pointer to the next interface */
+
+ struct sockaddr_dl *sdl; /* link-layer address */
+ char ifname[IF_NAMESIZE]; /* interface name */
+ int active; /* interface status */
+ int probeinterval; /* interval of probe timer(if necessary) */
+ int probetimer; /* rest of probe timer */
+ int mediareqok; /* wheter the IF supports SIOCGIFMEDIA */
+ int state;
+ int probes;
+ int dadcount;
+ struct timeval timer;
+ struct timeval expire;
+ int errors; /* # of errors we've got - detect wedge */
+
+ int racnt; /* total # of valid RAs it have got */
+
+ size_t rs_datalen;
+ u_char *rs_data;
+};
+
+/* per interface status */
+#define IFS_IDLE 0
+#define IFS_DELAY 1
+#define IFS_PROBE 2
+#define IFS_DOWN 3
+#define IFS_TENTATIVE 4
+
+/* rtsold.c */
+extern struct timeval tm_max;
+extern int dflag;
+struct ifinfo *find_ifinfo __P((int ifindex));
+void rtsol_timer_update __P((struct ifinfo *ifinfo));
+extern void warnmsg __P((int, const char *, const char *, ...))
+ __attribute__((__format__(__printf__, 3, 4)));
+
+/* if.c */
+extern int ifinit __P((void));
+extern int interface_up __P((char *name));
+extern int interface_status __P((struct ifinfo*));
+extern int lladdropt_length __P((struct sockaddr_dl *sdl));
+extern void lladdropt_fill __P((struct sockaddr_dl *sdl,
+ struct nd_opt_hdr *ndopt));
+extern struct sockaddr_dl *if_nametosdl __P((char *name));
+extern int getinet6sysctl __P((int code));
+
+/* rtsol.c */
+extern int sockopen __P((void));
+extern void sendpacket __P((struct ifinfo *ifinfo));
+extern void rtsol_input __P((int s));
+
+/* probe.c */
+extern int probe_init __P((void));
+extern void defrouter_probe __P((int ifindex));
+
+/* dump.c */
+extern void rtsold_dump_file __P((char *));
+
+/* rtsock.c */
+extern int rtsock_open __P((void));
+extern int rtsock_input __P((int));
diff --git a/usr.sbin/rwhod/Makefile b/usr.sbin/rwhod/Makefile
new file mode 100644
index 0000000..c502052
--- /dev/null
+++ b/usr.sbin/rwhod/Makefile
@@ -0,0 +1,7 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+PROG= rwhod
+MAN= 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..97a3191
--- /dev/null
+++ b/usr.sbin/rwhod/rwhod.8
@@ -0,0 +1,235 @@
+.\" Copyright (c) 1983, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)rwhod.8 8.2 (Berkeley) 12/11/93
+.\" $FreeBSD$
+.\"
+.Dd December 11, 1993
+.Dt RWHOD 8
+.Os
+.Sh NAME
+.Nm rwhod
+.Nd system status server
+.Sh SYNOPSIS
+.Nm
+.Op Fl i
+.Op Fl p
+.Op Fl l
+.Op Fl m Op Ar ttl
+.Sh DESCRIPTION
+.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 p
+option tells
+.Nm
+to ignore all
+.Dv POINTOPOINT
+interfaces. This is useful if you do not wish to keep dial on demand
+interfaces permanently active.
+.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 from
+.Fl m ,
+in that
+.Fl m Ar 1
+specifies transmission on one interface only.
+.Pp
+When
+.Fl m
+is used without a
+.Ar ttl
+argument, the program accepts multicast
+.Nm
+reports from all multicast-capable interfaces. If a
+.Ar ttl
+argument is given, it accepts multicast reports from only one interface, the
+one on which reports are sent (which may be controlled via the host's routing
+table). Regardless of the
+.Fl m
+option, the program accepts broadcast or
+unicast reports from all interfaces. Thus, this program will hear the
+reports of old, non-multicasting
+.Nm Ns s ,
+but, if multicasting is used,
+those old
+.Nm Ns s
+won't hear the reports generated by this program.
+.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 /boot/kernel/kernel
+every 30 minutes to guard against
+the possibility that this file is not the system
+image currently operating.
+.Sh SEE ALSO
+.Xr ruptime 1 ,
+.Xr rwho 1
+.Sh BUGS
+Status information should be sent only upon request rather than continuously.
+People often interpret the server dying
+or network communication failures
+as a machine going down.
+.Sh HISTORY
+The
+.Nm
+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..3e521fd
--- /dev/null
+++ b/usr.sbin/rwhod/rwhod.c
@@ -0,0 +1,747 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#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 <arpa/inet.h>
+#include <protocols/rwhod.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <utmp.h>
+#include <pwd.h>
+#include <grp.h>
+
+/*
+ * This version of Berkeley's rwhod has been modified to use IP multicast
+ * datagrams, under control of a new command-line option:
+ *
+ * rwhod -m causes rwhod to use IP multicast (instead of
+ * broadcast or unicast) on all interfaces that have
+ * the IFF_MULTICAST flag set in their "ifnet" structs
+ * (excluding the loopback interface). The multicast
+ * reports are sent with a time-to-live of 1, to prevent
+ * forwarding beyond the directly-connected subnet(s).
+ *
+ * rwhod -m <ttl> causes rwhod to send IP multicast datagrams with a
+ * time-to-live of <ttl>, via a SINGLE interface rather
+ * than all interfaces. <ttl> must be between 0 and
+ * MAX_MULTICAST_SCOPE, defined below. Note that "-m 1"
+ * is different than "-m", in that "-m 1" specifies
+ * transmission on one interface only.
+ *
+ * When "-m" is used without a <ttl> argument, the program accepts multicast
+ * rwhod reports from all multicast-capable interfaces. If a <ttl> argument
+ * is given, it accepts multicast reports from only one interface, the one
+ * on which reports are sent (which may be controlled via the host's routing
+ * table). Regardless of the "-m" option, the program accepts broadcast or
+ * unicast reports from all interfaces. Thus, this program will hear the
+ * reports of old, non-multicasting rwhods, but, if multicasting is used,
+ * those old rwhods won't hear the reports generated by this program.
+ *
+ * -- Steve Deering, Stanford University, February 1989
+ */
+
+#define UNPRIV_USER "daemon"
+#define UNPRIV_GROUP "daemon"
+
+#define NO_MULTICAST 0 /* multicast modes */
+#define PER_INTERFACE_MULTICAST 1
+#define SCOPED_MULTICAST 2
+
+#define MAX_MULTICAST_SCOPE 32 /* "site-wide", by convention */
+
+#define INADDR_WHOD_GROUP (u_long)0xe0000103 /* 224.0.1.3 */
+ /* (belongs in protocols/rwhod.h) */
+
+int insecure_mode;
+int quiet_mode;
+int iff_flag = IFF_POINTOPOINT;
+int multicast_mode = NO_MULTICAST;
+int multicast_scope;
+struct sockaddr_in multicast_addr = { sizeof multicast_addr, AF_INET };
+
+/*
+ * 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 if (strcmp(*argv, "-p") == 0)
+ iff_flag = 0;
+ else
+ usage();
+ argv++, argc--;
+ }
+ if (argc > 0)
+ usage();
+#ifndef DEBUG
+ daemon(1, 0);
+#endif
+ (void) signal(SIGHUP, getboottime);
+ openlog("rwhod", LOG_PID, LOG_DAEMON);
+ sp = getservbyname("who", "udp");
+ if (sp == NULL) {
+ syslog(LOG_ERR, "udp/who: unknown service");
+ exit(1);
+ }
+ if (chdir(_PATH_RWHODIR) < 0) {
+ syslog(LOG_ERR, "%s: %m", _PATH_RWHODIR);
+ exit(1);
+ }
+ /*
+ * Establish host name as returned by system.
+ */
+ if (gethostname(myname, sizeof(myname) - 1) < 0) {
+ syslog(LOG_ERR, "gethostname: %m");
+ exit(1);
+ }
+ if ((cp = index(myname, '.')) != NULL)
+ *cp = '\0';
+ strncpy(mywd.wd_hostname, myname, sizeof(mywd.wd_hostname) - 1);
+ mywd.wd_hostname[sizeof(mywd.wd_hostname) - 1] = '\0';
+ utmpf = open(_PATH_UTMP, O_RDONLY|O_CREAT, 0644);
+ if (utmpf < 0) {
+ syslog(LOG_ERR, "%s: %m", _PATH_UTMP);
+ exit(1);
+ }
+ getboottime(0);
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ syslog(LOG_ERR, "socket: %m");
+ exit(1);
+ }
+ if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) {
+ syslog(LOG_ERR, "setsockopt SO_BROADCAST: %m");
+ exit(1);
+ }
+ memset(&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);
+ time_t t;
+
+ cc = recvfrom(s, (char *)&wd, sizeof(struct whod), 0,
+ (struct sockaddr *)&from, &len);
+ if (cc <= 0) {
+ if (cc < 0 && errno != EINTR)
+ syslog(LOG_WARNING, "recv: %m");
+ continue;
+ }
+ if (from.sin_port != sp->s_port && !insecure_mode) {
+ syslog(LOG_WARNING, "%d: bad source port from %s",
+ ntohs(from.sin_port), inet_ntoa(from.sin_addr));
+ continue;
+ }
+ if (cc < WHDRSIZE) {
+ syslog(LOG_WARNING, "short packet from %s",
+ inet_ntoa(from.sin_addr));
+ continue;
+ }
+ if (wd.wd_vers != WHODVERSION)
+ continue;
+ if (wd.wd_type != WHODTYPE_STATUS)
+ continue;
+ if (!verify(wd.wd_hostname, sizeof wd.wd_hostname)) {
+ syslog(LOG_WARNING, "malformed host name from %s",
+ inet_ntoa(from.sin_addr));
+ continue;
+ }
+ (void) snprintf(path, sizeof path, "whod.%s", wd.wd_hostname);
+ /*
+ * Rather than truncating and growing the file each time,
+ * use ftruncate if size is less than previous size.
+ */
+ whod = open(path, O_WRONLY | O_CREAT, 0644);
+ if (whod < 0) {
+ syslog(LOG_WARNING, "%s: %m", path);
+ continue;
+ }
+#if ENDIAN != BIG_ENDIAN
+ {
+ int i, n = (cc - WHDRSIZE)/sizeof(struct whoent);
+ struct whoent *we;
+
+ /* undo header byte swapping before writing to file */
+ wd.wd_sendtime = ntohl(wd.wd_sendtime);
+ for (i = 0; i < 3; i++)
+ wd.wd_loadav[i] = ntohl(wd.wd_loadav[i]);
+ wd.wd_boottime = ntohl(wd.wd_boottime);
+ we = wd.wd_we;
+ for (i = 0; i < n; i++) {
+ we->we_idle = ntohl(we->we_idle);
+ we->we_utmp.out_time =
+ ntohl(we->we_utmp.out_time);
+ we++;
+ }
+ }
+#endif
+ (void) time(&t);
+ wd.wd_recvtime = _time_to_int(t);
+ (void) write(whod, (char *)&wd, cc);
+ if (fstat(whod, &st) < 0 || st.st_size > cc)
+ ftruncate(whod, cc);
+ (void) close(whod);
+ }
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: rwhod [-i] [-p] [-l] [-m [ttl]]\n");
+ exit(1);
+}
+
+void
+run_as(uid, gid)
+ uid_t *uid;
+ gid_t *gid;
+{
+ struct passwd *pw;
+ struct group *gr;
+
+ pw = getpwnam(UNPRIV_USER);
+ if (!pw) {
+ syslog(LOG_ERR, "getpwnam(%s): %m", UNPRIV_USER);
+ exit(1);
+ }
+ *uid = pw->pw_uid;
+
+ gr = getgrnam(UNPRIV_GROUP);
+ if (!gr) {
+ syslog(LOG_ERR, "getgrnam(%s): %m", UNPRIV_GROUP);
+ exit(1);
+ }
+ *gid = gr->gr_gid;
+}
+
+/*
+ * Check out host name for unprintables
+ * and other funnies before allowing a file
+ * to be created. Sorry, but blanks aren't allowed.
+ */
+int
+verify(name, maxlen)
+ register char *name;
+ register int maxlen;
+{
+ register int size = 0;
+
+ while (*name && size < maxlen - 1) {
+ if (!isascii(*name) || !(isalnum(*name) || ispunct(*name)))
+ return (0);
+ name++, size++;
+ }
+ *name = '\0';
+ return (size > 0);
+}
+
+int utmptime;
+int utmpent;
+int utmpsize = 0;
+struct utmp *utmp;
+int alarmcount;
+
+void
+onalrm(signo)
+ int signo;
+{
+ register struct neighbor *np;
+ register struct whoent *we = mywd.wd_we, *wlast;
+ register int i;
+ struct stat stb;
+ double avenrun[3];
+ time_t now;
+ int cc;
+
+ now = time(NULL);
+ if (alarmcount % 10 == 0)
+ getboottime(0);
+ alarmcount++;
+ (void) fstat(utmpf, &stb);
+ if ((stb.st_mtime != utmptime) || (stb.st_size > utmpsize)) {
+ utmptime = stb.st_mtime;
+ if (stb.st_size > utmpsize) {
+ utmpsize = stb.st_size + 10 * sizeof(struct utmp);
+ if (utmp)
+ utmp = (struct utmp *)realloc(utmp, utmpsize);
+ else
+ utmp = (struct utmp *)malloc(utmpsize);
+ if (! utmp) {
+ syslog(LOG_WARNING, "malloc failed");
+ utmpsize = 0;
+ goto done;
+ }
+ }
+ (void) lseek(utmpf, (off_t)0, L_SET);
+ cc = read(utmpf, (char *)utmp, stb.st_size);
+ if (cc < 0) {
+ syslog(LOG_ERR, "read(%s): %m", _PATH_UTMP);
+ goto done;
+ }
+ wlast = &mywd.wd_we[1024 / sizeof(struct whoent) - 1];
+ utmpent = cc / sizeof(struct utmp);
+ for (i = 0; i < utmpent; i++)
+ if (utmp[i].ut_name[0]) {
+ memcpy(we->we_utmp.out_line, utmp[i].ut_line,
+ sizeof(utmp[i].ut_line));
+ memcpy(we->we_utmp.out_name, utmp[i].ut_name,
+ sizeof(utmp[i].ut_name));
+ we->we_utmp.out_time = htonl(utmp[i].ut_time);
+ if (we >= wlast)
+ break;
+ we++;
+ }
+ utmpent = we - mywd.wd_we;
+ }
+
+ /*
+ * The test on utmpent looks silly---after all, if no one is
+ * logged on, why worry about efficiency?---but is useful on
+ * (e.g.) compute servers.
+ */
+ if (utmpent && chdir(_PATH_DEV)) {
+ syslog(LOG_ERR, "chdir(%s): %m", _PATH_DEV);
+ exit(1);
+ }
+ we = mywd.wd_we;
+ for (i = 0; i < utmpent; i++) {
+ if (stat(we->we_utmp.out_line, &stb) >= 0)
+ we->we_idle = htonl(now - stb.st_atime);
+ we++;
+ }
+ (void)getloadavg(avenrun, sizeof(avenrun)/sizeof(avenrun[0]));
+ for (i = 0; i < 3; i++)
+ mywd.wd_loadav[i] = htonl((u_long)(avenrun[i] * 100));
+ cc = (char *)we - (char *)&mywd;
+ mywd.wd_sendtime = htonl(_time_to_time32(time(NULL)));
+ mywd.wd_vers = WHODVERSION;
+ mywd.wd_type = WHODTYPE_STATUS;
+ if (multicast_mode == SCOPED_MULTICAST) {
+ (void) sendto(s, (char *)&mywd, cc, 0,
+ (struct sockaddr *)&multicast_addr,
+ sizeof(multicast_addr));
+ }
+ else for (np = neighbors; np != NULL; np = np->n_next) {
+ if (multicast_mode == PER_INTERFACE_MULTICAST &&
+ np->n_flags & IFF_MULTICAST) {
+ /*
+ * Select the outgoing interface for the multicast.
+ */
+ if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF,
+ &(((struct sockaddr_in *)np->n_addr)->sin_addr),
+ sizeof(struct in_addr)) < 0) {
+ syslog(LOG_ERR,
+ "setsockopt IP_MULTICAST_IF: %m");
+ exit(1);
+ }
+ (void) sendto(s, (char *)&mywd, cc, 0,
+ (struct sockaddr *)&multicast_addr,
+ sizeof(multicast_addr));
+ } else (void) sendto(s, (char *)&mywd, cc, 0,
+ np->n_addr, np->n_addrlen);
+ }
+ if (utmpent && chdir(_PATH_RWHODIR)) {
+ syslog(LOG_ERR, "chdir(%s): %m", _PATH_RWHODIR);
+ exit(1);
+ }
+done:
+ (void) alarm(AL_INTERVAL);
+}
+
+void
+getboottime(signo)
+ int signo;
+{
+ int mib[2];
+ size_t size;
+ struct timeval tm;
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_BOOTTIME;
+ size = sizeof(tm);
+ if (sysctl(mib, 2, &tm, &size, NULL, 0) == -1) {
+ syslog(LOG_ERR, "cannot get boottime: %m");
+ exit(1);
+ }
+ mywd.wd_boottime = htonl(_time_to_time32(tm.tv_sec));
+}
+
+void
+quit(msg)
+ char *msg;
+{
+ syslog(LOG_ERR, "%s", 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_flag)) == 0)
+ continue;
+ if (ifm->ifm_type != RTM_NEWADDR)
+ quit("out of sync parsing NET_RT_IFLIST");
+ ifam = (struct ifa_msghdr *)ifm;
+ info.rti_addrs = ifam->ifam_addrs;
+ rt_xaddrs((char *)(ifam + 1), ifam->ifam_msglen + (char *)ifam,
+ &info);
+ /* gag, wish we could get rid of Internet dependencies */
+#define dstaddr info.rti_info[RTAX_BRD]
+#define ifaddr info.rti_info[RTAX_IFA]
+#define IPADDR_SA(x) ((struct sockaddr_in *)(x))->sin_addr.s_addr
+#define PORT_SA(x) ((struct sockaddr_in *)(x))->sin_port
+ if (dstaddr == 0 || dstaddr->sa_family != AF_INET)
+ continue;
+ PORT_SA(dstaddr) = sp->s_port;
+ for (np = neighbors; np != NULL; np = np->n_next)
+ if (memcmp(sdl->sdl_data, np->n_name,
+ sdl->sdl_nlen) == 0 &&
+ IPADDR_SA(np->n_addr) == IPADDR_SA(dstaddr))
+ break;
+ if (np != NULL)
+ continue;
+ len = sizeof(*np) + dstaddr->sa_len + sdl->sdl_nlen + 1;
+ np = (struct neighbor *)malloc(len);
+ if (np == NULL)
+ quit("malloc of neighbor structure");
+ memset(np, 0, len);
+ np->n_flags = flags;
+ np->n_addr = (struct sockaddr *)(np + 1);
+ np->n_addrlen = dstaddr->sa_len;
+ np->n_name = np->n_addrlen + (char *)np->n_addr;
+ memcpy((char *)np->n_addr, (char *)dstaddr, np->n_addrlen);
+ memcpy(np->n_name, sdl->sdl_data, sdl->sdl_nlen);
+ if (multicast_mode == PER_INTERFACE_MULTICAST &&
+ (flags & IFF_MULTICAST) &&
+ !(flags & IFF_LOOPBACK)) {
+ struct ip_mreq mreq;
+
+ memcpy((char *)np->n_addr, (char *)ifaddr,
+ np->n_addrlen);
+ mreq.imr_multiaddr.s_addr = htonl(INADDR_WHOD_GROUP);
+ mreq.imr_interface.s_addr =
+ ((struct sockaddr_in *)np->n_addr)->sin_addr.s_addr;
+ if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ &mreq, sizeof(mreq)) < 0) {
+ syslog(LOG_ERR,
+ "setsockopt IP_ADD_MEMBERSHIP: %m");
+#if 0
+ /* Fall back to broadcast on this if. */
+ np->n_flags &= ~IFF_MULTICAST;
+#else
+ free((char *)np);
+ continue;
+#endif
+ }
+ }
+ np->n_next = neighbors;
+ neighbors = np;
+ }
+ free(buf);
+ return (1);
+}
+
+#ifdef DEBUG
+void
+Sendto(s, buf, cc, flags, to, tolen)
+ int s;
+ const void *buf;
+ size_t cc;
+ int flags;
+ const struct sockaddr *to;
+ int tolen;
+{
+ register struct whod *w = (struct whod *)buf;
+ register struct whoent *we;
+ struct sockaddr_in *sin = (struct sockaddr_in *)to;
+
+ printf("sendto %x.%d\n", ntohl(sin->sin_addr.s_addr),
+ ntohs(sin->sin_port));
+ printf("hostname %s %s\n", w->wd_hostname,
+ interval(ntohl(w->wd_sendtime) - ntohl(w->wd_boottime), " up"));
+ printf("load %4.2f, %4.2f, %4.2f\n",
+ ntohl(w->wd_loadav[0]) / 100.0, ntohl(w->wd_loadav[1]) / 100.0,
+ ntohl(w->wd_loadav[2]) / 100.0);
+ cc -= WHDRSIZE;
+ for (we = w->wd_we, cc /= sizeof(struct whoent); cc > 0; cc--, we++) {
+ time_t t = _time32_to_time(ntohl(we->we_utmp.out_time));
+ printf("%-8.8s %s:%s %.12s",
+ we->we_utmp.out_name,
+ w->wd_hostname, we->we_utmp.out_line,
+ ctime(&t)+4);
+ we->we_idle = ntohl(we->we_idle) / 60;
+ if (we->we_idle) {
+ if (we->we_idle >= 100*60)
+ we->we_idle = 100*60 - 1;
+ if (we->we_idle >= 60)
+ printf(" %2d", we->we_idle / 60);
+ else
+ printf(" ");
+ printf(":%02d", we->we_idle % 60);
+ }
+ printf("\n");
+ }
+}
+
+char *
+interval(time, updown)
+ int time;
+ char *updown;
+{
+ static char resbuf[32];
+ int days, hours, minutes;
+
+ if (time < 0 || time > 3*30*24*60*60) {
+ (void) sprintf(resbuf, " %s ??:??", updown);
+ return (resbuf);
+ }
+ minutes = (time + 59) / 60; /* round to minutes */
+ hours = minutes / 60; minutes %= 60;
+ days = hours / 24; hours %= 24;
+ if (days)
+ (void) sprintf(resbuf, "%s %2d+%02d:%02d",
+ updown, days, hours, minutes);
+ else
+ (void) sprintf(resbuf, "%s %2d:%02d",
+ updown, hours, minutes);
+ return (resbuf);
+}
+#endif
diff --git a/usr.sbin/sa/Makefile b/usr.sbin/sa/Makefile
new file mode 100644
index 0000000..c6868b5
--- /dev/null
+++ b/usr.sbin/sa/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+PROG= sa
+MAN= 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..5804aa2
--- /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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <db.h>
+
+/* structures */
+
+struct cmdinfo {
+ char ci_comm[MAXCOMLEN+2]; /* command name (+ '*') */
+ u_long ci_uid; /* user id */
+ u_quad_t ci_calls; /* number of calls */
+ u_quad_t ci_etime; /* elapsed time */
+ u_quad_t ci_utime; /* user time */
+ u_quad_t ci_stime; /* system time */
+ u_quad_t ci_mem; /* memory use */
+ u_quad_t ci_io; /* number of disk i/o ops */
+ u_int ci_flags; /* flags; see below */
+};
+#define CI_UNPRINTABLE 0x0001 /* unprintable chars in name */
+
+struct userinfo {
+ u_long ui_uid; /* user id; for consistency */
+ u_quad_t ui_calls; /* number of invocations */
+ u_quad_t ui_utime; /* user time */
+ u_quad_t ui_stime; /* system time */
+ u_quad_t ui_mem; /* memory use */
+ u_quad_t ui_io; /* number of disk i/o ops */
+};
+
+/* typedefs */
+
+typedef int (*cmpf_t) __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..3686c62
--- /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[] =
+ "$FreeBSD$";
+#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 = 0;
+
+ while ((ch = getopt(argc, argv, "abcdDfijkKlmnqrstuv:")) != -1)
+ switch (ch) {
+ case 'a':
+ /* print all commands */
+ aflag = 1;
+ break;
+ case 'b':
+ /* sort by per-call user/system time average */
+ bflag = 1;
+ sa_cmp = cmp_avgusrsys;
+ break;
+ case 'c':
+ /* print percentage total time */
+ cflag = 1;
+ break;
+ case 'd':
+ /* sort by averge number of disk I/O ops */
+ dflag = 1;
+ sa_cmp = cmp_avgdkio;
+ break;
+ case 'D':
+ /* print and sort by total disk I/O ops */
+ Dflag = 1;
+ sa_cmp = cmp_dkio;
+ break;
+ case 'f':
+ /* force no interactive threshold comprison */
+ fflag = 1;
+ break;
+ case 'i':
+ /* do not read in summary file */
+ iflag = 1;
+ break;
+ case 'j':
+ /* instead of total minutes, give sec/call */
+ jflag = 1;
+ break;
+ case 'k':
+ /* sort by cpu-time average memory usage */
+ kflag = 1;
+ sa_cmp = cmp_avgcpumem;
+ break;
+ case 'K':
+ /* print and sort by cpu-storage integral */
+ sa_cmp = cmp_cpumem;
+ Kflag = 1;
+ break;
+ case 'l':
+ /* separate system and user time */
+ lflag = 1;
+ break;
+ case 'm':
+ /* print procs and time per-user */
+ mflag = 1;
+ break;
+ case 'n':
+ /* sort by number of calls */
+ sa_cmp = cmp_calls;
+ break;
+ case 'q':
+ /* quiet; error messages only */
+ qflag = 1;
+ break;
+ case 'r':
+ /* reverse order of sort */
+ rflag = 1;
+ break;
+ case 's':
+ /* merge accounting file into summaries */
+ sflag = 1;
+ break;
+ case 't':
+ /* report ratio of user and system times */
+ tflag = 1;
+ break;
+ case 'u':
+ /* first, print uid and command name */
+ uflag = 1;
+ break;
+ case 'v':
+ /* cull junk */
+ vflag = 1;
+ cutoff = atoi(optarg);
+ break;
+ case '?':
+ default:
+ usage();
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ /* various argument checking */
+ if (fflag && !vflag)
+ errx(1, "only one of -f requires -v");
+ if (fflag && aflag)
+ errx(1, "only one of -a and -v may be specified");
+ /* XXX need more argument checking */
+
+ if (!uflag) {
+ /* initialize tables */
+ if ((sflag || (!mflag && !qflag)) && pacct_init() != 0)
+ errx(1, "process accounting initialization failed");
+ if ((sflag || (mflag && !qflag)) && usracct_init() != 0)
+ errx(1, "user accounting initialization failed");
+ }
+
+ if (argc == 0) {
+ argc = dfltargc;
+ argv = dfltargv;
+ }
+
+ /* for each file specified */
+ for (; argc > 0; argc--, argv++) {
+ int fd;
+
+ /*
+ * load the accounting data from the file.
+ * if it fails, go on to the next file.
+ */
+ fd = acct_load(argv[0], sflag);
+ if (fd < 0)
+ continue;
+
+ if (!uflag && sflag) {
+#ifndef DEBUG
+ sigset_t nmask, omask;
+ int unmask = 1;
+
+ /*
+ * block most signals so we aren't interrupted during
+ * the update.
+ */
+ if (sigfillset(&nmask) == -1) {
+ warn("sigfillset");
+ unmask = 0;
+ error = 1;
+ }
+ if (unmask &&
+ (sigprocmask(SIG_BLOCK, &nmask, &omask) == -1)) {
+ warn("couldn't set signal mask");
+ unmask = 0;
+ error = 1;
+ }
+#endif /* DEBUG */
+
+ /*
+ * truncate the accounting data file ASAP, to avoid
+ * losing data. don't worry about errors in updating
+ * the saved stats; better to underbill than overbill,
+ * but we want every accounting record intact.
+ */
+ if (ftruncate(fd, 0) == -1) {
+ warn("couldn't truncate %s", argv);
+ error = 1;
+ }
+
+ /*
+ * update saved user and process accounting data.
+ * note errors for later.
+ */
+ if (pacct_update() != 0 || usracct_update() != 0)
+ error = 1;
+
+#ifndef DEBUG
+ /*
+ * restore signals
+ */
+ if (unmask &&
+ (sigprocmask(SIG_SETMASK, &omask, NULL) == -1)) {
+ warn("couldn't restore signal mask");
+ error = 1;
+ }
+#endif /* DEBUG */
+ }
+
+ /*
+ * close the opened accounting file
+ */
+ if (close(fd) == -1) {
+ warn("close %s", argv);
+ error = 1;
+ }
+ }
+
+ if (!uflag && !qflag) {
+ /* print any results we may have obtained. */
+ if (!mflag)
+ pacct_print();
+ else
+ usracct_print();
+ }
+
+ if (!uflag) {
+ /* finally, deallocate databases */
+ if (sflag || (!mflag && !qflag))
+ pacct_destroy();
+ if (sflag || (mflag && !qflag))
+ usracct_destroy();
+ }
+
+ exit(error);
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: sa [-abcdDfijkKlmnqrstu] [-v cutoff] [file ...]\n");
+ exit(1);
+}
+
+static int
+acct_load(pn, wr)
+ char *pn;
+ int wr;
+{
+ struct acct ac;
+ struct cmdinfo ci;
+ ssize_t rv;
+ int fd, i;
+
+ /*
+ * open the file
+ */
+ fd = open(pn, wr ? O_RDWR : O_RDONLY, 0);
+ if (fd == -1) {
+ warn("open %s %s", pn, wr ? "for read/write" : "read-only");
+ return (-1);
+ }
+
+ /*
+ * read all we can; don't stat and open because more processes
+ * could exit, and we'd miss them
+ */
+ while (1) {
+ /* get one accounting entry and punt if there's an error */
+ rv = read(fd, &ac, sizeof(struct acct));
+ if (rv == -1)
+ warn("error reading %s", pn);
+ else if (rv > 0 && rv < 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;
+
+ memcpy(&c1, d1->data, sizeof(c1));
+ memcpy(&c2, d2->data, sizeof(c2));
+
+ t1 = c1.ci_utime + c1.ci_stime;
+ t2 = c2.ci_utime + c2.ci_stime;
+
+ if (t1 < t2)
+ return -1;
+ else if (t1 == t2)
+ return (cmp_comm(c1.ci_comm, c2.ci_comm));
+ else
+ return 1;
+}
+
+/* sort by average user and system time */
+static int
+cmp_avgusrsys(d1, d2)
+ const DBT *d1, *d2;
+{
+ struct cmdinfo c1, c2;
+ double t1, t2;
+
+ memcpy(&c1, d1->data, sizeof(c1));
+ memcpy(&c2, d2->data, sizeof(c2));
+
+ t1 = c1.ci_utime + c1.ci_stime;
+ t1 /= (double) (c1.ci_calls ? c1.ci_calls : 1);
+
+ t2 = c2.ci_utime + c2.ci_stime;
+ t2 /= (double) (c2.ci_calls ? c2.ci_calls : 1);
+
+ if (t1 < t2)
+ return -1;
+ else if (t1 == t2)
+ return (cmp_comm(c1.ci_comm, c2.ci_comm));
+ else
+ return 1;
+}
+
+/* sort by total number of disk I/O operations */
+static int
+cmp_dkio(d1, d2)
+ const DBT *d1, *d2;
+{
+ struct cmdinfo c1, c2;
+
+ memcpy(&c1, d1->data, sizeof(c1));
+ memcpy(&c2, d2->data, sizeof(c2));
+
+ if (c1.ci_io < c2.ci_io)
+ return -1;
+ else if (c1.ci_io == c2.ci_io)
+ return (cmp_comm(c1.ci_comm, c2.ci_comm));
+ else
+ return 1;
+}
+
+/* sort by average number of disk I/O operations */
+static int
+cmp_avgdkio(d1, d2)
+ const DBT *d1, *d2;
+{
+ struct cmdinfo c1, c2;
+ double n1, n2;
+
+ memcpy(&c1, d1->data, sizeof(c1));
+ memcpy(&c2, d2->data, sizeof(c2));
+
+ n1 = (double) c1.ci_io / (double) (c1.ci_calls ? c1.ci_calls : 1);
+ n2 = (double) c2.ci_io / (double) (c2.ci_calls ? c2.ci_calls : 1);
+
+ if (n1 < n2)
+ return -1;
+ else if (n1 == n2)
+ return (cmp_comm(c1.ci_comm, c2.ci_comm));
+ else
+ return 1;
+}
+
+/* sort by the cpu-storage integral */
+static int
+cmp_cpumem(d1, d2)
+ const DBT *d1, *d2;
+{
+ struct cmdinfo c1, c2;
+
+ memcpy(&c1, d1->data, sizeof(c1));
+ memcpy(&c2, d2->data, sizeof(c2));
+
+ if (c1.ci_mem < c2.ci_mem)
+ return -1;
+ else if (c1.ci_mem == c2.ci_mem)
+ return (cmp_comm(c1.ci_comm, c2.ci_comm));
+ else
+ return 1;
+}
+
+/* sort by the cpu-time average memory usage */
+static int
+cmp_avgcpumem(d1, d2)
+ const DBT *d1, *d2;
+{
+ struct cmdinfo c1, c2;
+ u_quad_t t1, t2;
+ double n1, n2;
+
+ memcpy(&c1, d1->data, sizeof(c1));
+ memcpy(&c2, d2->data, sizeof(c2));
+
+ t1 = c1.ci_utime + c1.ci_stime;
+ t2 = c2.ci_utime + c2.ci_stime;
+
+ n1 = (double) c1.ci_mem / (double) (t1 ? t1 : 1);
+ n2 = (double) c2.ci_mem / (double) (t2 ? t2 : 1);
+
+ if (n1 < n2)
+ return -1;
+ else if (n1 == n2)
+ return (cmp_comm(c1.ci_comm, c2.ci_comm));
+ else
+ return 1;
+}
+
+/* sort by the number of invocations */
+static int
+cmp_calls(d1, d2)
+ const DBT *d1, *d2;
+{
+ struct cmdinfo c1, c2;
+
+ memcpy(&c1, d1->data, sizeof(c1));
+ memcpy(&c2, d2->data, sizeof(c2));
+
+ if (c1.ci_calls < c2.ci_calls)
+ return -1;
+ else if (c1.ci_calls == c2.ci_calls)
+ return (cmp_comm(c1.ci_comm, c2.ci_comm));
+ else
+ return 1;
+}
diff --git a/usr.sbin/sa/pathnames.h b/usr.sbin/sa/pathnames.h
new file mode 100644
index 0000000..7a7edf6
--- /dev/null
+++ b/usr.sbin/sa/pathnames.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 1994 Christopher G. Demetriou
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Christopher G. Demetriou.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define _PATH_ACCT "/var/account/acct"
+#define _PATH_SAVACCT "/var/account/savacct"
+#define _PATH_USRACCT "/var/account/usracct"
diff --git a/usr.sbin/sa/pdb.c b/usr.sbin/sa/pdb.c
new file mode 100644
index 0000000..eafdf59
--- /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[] =
+ "$FreeBSD$";
+#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..5eb22d7
--- /dev/null
+++ b/usr.sbin/sa/sa.8
@@ -0,0 +1,236 @@
+.\"
+.\" Copyright (c) 1994 Christopher G. Demetriou
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Christopher G. Demetriou.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd February 25, 1994
+.Dt SA 8
+.Os
+.Sh NAME
+.Nm sa
+.Nd print system accounting statistics
+.Sh SYNOPSIS
+.Nm
+.Op Fl abcdDfijkKlmnqrstu
+.Op Fl v Ar cutoff
+.Op Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility reports on, cleans up,
+and generally maintains system
+accounting files.
+.Pp
+.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.
+.Sh DIAGNOSTICS
+.Ex -std
+.Sh FILES
+.Bl -tag -width /var/account/usracct -compact
+.It Pa /var/account/acct
+raw accounting data file
+.It Pa /var/account/savacct
+per-command accounting summary database
+.It Pa /var/account/usracct
+per-user accounting summary database
+.El
+.Sh SEE ALSO
+.Xr lastcomm 1 ,
+.Xr acct 5 ,
+.Xr ac 8 ,
+.Xr accton 8
+.Sh BUGS
+The number of options to this program is absurd, especially considering
+that there's not much logic behind their lettering.
+.Pp
+The field labels should be more consistent.
+.Pp
+The VM system does not record the CPU storage integral.
+.Sh CAVEATS
+While the behavior of the options in this version of
+.Nm
+was modeled after the original version, there are some intentional
+differences and undoubtedly some unintentional ones as well. In
+particular, the
+.Fl q
+option has been added, and the
+.Fl m
+option now understands more options than it used to.
+.Pp
+The formats of the summary files created by this version of
+.Nm
+are very different from the those used by the original version.
+This is not considered a problem, however, because the accounting record
+format has changed as well (since user ids are now 32 bits).
+.Sh AUTHORS
+.An Chris G. Demetriou Aq cgd@postgres.berkeley.edu
diff --git a/usr.sbin/sa/usrdb.c b/usr.sbin/sa/usrdb.c
new file mode 100644
index 0000000..3bce1bd
--- /dev/null
+++ b/usr.sbin/sa/usrdb.c
@@ -0,0 +1,286 @@
+/*
+ * Copyright (c) 1994 Christopher G. Demetriou
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Christopher G. Demetriou.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/acct.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "extern.h"
+#include "pathnames.h"
+
+static int uid_compare __P((const DBT *, const DBT *));
+
+static DB *usracct_db;
+
+int
+usracct_init()
+{
+ DB *saved_usracct_db;
+ BTREEINFO bti;
+ int error;
+
+ bzero(&bti, sizeof bti);
+ bti.compare = uid_compare;
+
+ usracct_db = dbopen(NULL, O_RDWR, 0, DB_BTREE, &bti);
+ if (usracct_db == NULL)
+ return (-1);
+
+ error = 0;
+ if (!iflag) {
+ DBT key, data;
+ int serr, nerr;
+
+ saved_usracct_db = dbopen(_PATH_USRACCT, O_RDONLY, 0, DB_BTREE,
+ &bti);
+ if (saved_usracct_db == NULL) {
+ error = (errno == ENOENT) ? 0 : -1;
+ if (error)
+ warn("retrieving user accounting summary");
+ goto out;
+ }
+
+ serr = DB_SEQ(saved_usracct_db, &key, &data, R_FIRST);
+ if (serr < 0) {
+ warn("retrieving user accounting summary");
+ error = -1;
+ goto closeout;
+ }
+ while (serr == 0) {
+ nerr = DB_PUT(usracct_db, &key, &data, 0);
+ if (nerr < 0) {
+ warn("initializing user accounting stats");
+ error = -1;
+ break;
+ }
+
+ serr = DB_SEQ(saved_usracct_db, &key, &data, R_NEXT);
+ if (serr < 0) {
+ warn("retrieving user accounting summary");
+ error = -1;
+ break;
+ }
+ }
+
+closeout:
+ if (DB_CLOSE(saved_usracct_db) < 0) {
+ warn("closing user accounting summary");
+ error = -1;
+ }
+ }
+
+out:
+ if (error != 0)
+ usracct_destroy();
+ return (error);
+}
+
+void
+usracct_destroy()
+{
+ if (DB_CLOSE(usracct_db) < 0)
+ warn("destroying user accounting stats");
+}
+
+int
+usracct_add(ci)
+ const struct cmdinfo *ci;
+{
+ DBT key, data;
+ struct userinfo newui;
+ u_long uid;
+ int rv;
+
+ uid = ci->ci_uid;
+ key.data = &uid;
+ key.size = sizeof uid;
+
+ rv = DB_GET(usracct_db, &key, &data, 0);
+ if (rv < 0) {
+ warn("get key %lu from user accounting stats", uid);
+ return (-1);
+ } else if (rv == 0) { /* it's there; copy whole thing */
+ /* add the old data to the new data */
+ bcopy(data.data, &newui, data.size);
+ if (newui.ui_uid != uid) {
+ warnx("key %lu != expected record number %lu",
+ newui.ui_uid, uid);
+ warnx("inconsistent user accounting stats");
+ return (-1);
+ }
+ } else { /* it's not there; zero it and copy the key */
+ bzero(&newui, sizeof newui);
+ newui.ui_uid = ci->ci_uid;
+ }
+
+ newui.ui_calls += ci->ci_calls;
+ newui.ui_utime += ci->ci_utime;
+ newui.ui_stime += ci->ci_stime;
+ newui.ui_mem += ci->ci_mem;
+ newui.ui_io += ci->ci_io;
+
+ data.data = &newui;
+ data.size = sizeof newui;
+ rv = DB_PUT(usracct_db, &key, &data, 0);
+ if (rv < 0) {
+ warn("add key %lu to user accounting stats", uid);
+ return (-1);
+ } else if (rv != 0) {
+ warnx("DB_PUT returned 1");
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+usracct_update()
+{
+ DB *saved_usracct_db;
+ DBT key, data;
+ BTREEINFO bti;
+ int error, serr, nerr;
+
+ bzero(&bti, sizeof bti);
+ bti.compare = uid_compare;
+
+ saved_usracct_db = dbopen(_PATH_USRACCT, O_RDWR|O_CREAT|O_TRUNC, 0644,
+ DB_BTREE, &bti);
+ if (saved_usracct_db == NULL) {
+ warn("creating user accounting summary");
+ return (-1);
+ }
+
+ error = 0;
+
+ serr = DB_SEQ(usracct_db, &key, &data, R_FIRST);
+ if (serr < 0) {
+ warn("retrieving user accounting stats");
+ error = -1;
+ }
+ while (serr == 0) {
+ nerr = DB_PUT(saved_usracct_db, &key, &data, 0);
+ if (nerr < 0) {
+ warn("saving user accounting summary");
+ error = -1;
+ break;
+ }
+
+ serr = DB_SEQ(usracct_db, &key, &data, R_NEXT);
+ if (serr < 0) {
+ warn("retrieving user accounting stats");
+ error = -1;
+ break;
+ }
+ }
+
+ if (DB_SYNC(saved_usracct_db, 0) < 0) {
+ warn("syncing process accounting summary");
+ error = -1;
+ }
+ if (DB_CLOSE(saved_usracct_db) < 0) {
+ warn("closing process accounting summary");
+ error = -1;
+ }
+ return error;
+}
+
+void
+usracct_print()
+{
+ DBT key, data;
+ struct userinfo uistore, *ui = &uistore;
+ double t;
+ int rv;
+
+ rv = DB_SEQ(usracct_db, &key, &data, R_FIRST);
+ if (rv < 0)
+ warn("retrieving user accounting stats");
+
+ while (rv == 0) {
+ memcpy(ui, data.data, sizeof(struct userinfo));
+
+ printf("%-*s %9qu ", MAXLOGNAME - 1,
+ 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("%12.0f%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..1c4f221
--- /dev/null
+++ b/usr.sbin/sade/Makefile
@@ -0,0 +1,93 @@
+# $FreeBSD$
+
+PROG= sysinstall
+MAN= sysinstall.8
+SRCS= anonFTP.c cdrom.c command.c config.c devices.c dhcp.c \
+ disks.c dispatch.c dist.c dmenu.c doc.c dos.c floppy.c \
+ ftp.c globals.c http.c index.c install.c installUpgrade.c keymap.c \
+ label.c main.c makedevs.c media.c menus.c misc.c modules.c \
+ mouse.c msg.c network.c nfs.c options.c package.c \
+ system.c tape.c tcpip.c termcap.c ttys.c ufs.c usb.c user.c \
+ variable.c wizard.c keymap.h
+
+.if ${MACHINE_ARCH} == "i386"
+SRCS+= pccard.c
+.endif
+
+CFLAGS+= -I${.CURDIR}/../../gnu/lib/libdialog -I.
+CFLAGS+= -DX_AS_PKG -DUSE_GZIP=1
+.if ${MACHINE} == "pc98"
+CFLAGS+= -DPC98
+.endif
+
+DPADD= ${LIBDIALOG} ${LIBNCURSES} ${LIBUTIL} ${LIBDISK} ${LIBFTPIO}
+LDADD= -ldialog -lncurses -lutil -ldisk -lftpio
+
+CLEANFILES= makedevs.c rtermcap
+CLEANFILES+= keymap.tmp keymap.h
+
+makedevs.c: Makefile rtermcap
+ echo '#include <sys/types.h>' > makedevs.c
+ ./rtermcap ansi | \
+ file2c 'const char termcap_ansi[] = {' ',0};' \
+ >> makedevs.c
+ ./rtermcap cons25w | \
+ file2c 'const char termcap_cons25w[] = {' ',0};' \
+ >> makedevs.c
+ ./rtermcap cons25 | \
+ file2c 'const char termcap_cons25[] = {' ',0};' \
+ >> makedevs.c
+ ./rtermcap cons25-m | \
+ file2c 'const char termcap_cons25_m[] = {' ',0};' \
+ >> makedevs.c
+ ./rtermcap cons25r | \
+ file2c 'const char termcap_cons25r[] = {' ',0};' \
+ >> makedevs.c
+ ./rtermcap cons25r-m | \
+ file2c 'const char termcap_cons25r_m[] = {' ',0};' \
+ >> makedevs.c
+ ./rtermcap cons25l1 | \
+ file2c 'const char termcap_cons25l1[] = {' ',0};' \
+ >> makedevs.c
+ ./rtermcap cons25l1-m | \
+ file2c 'const char termcap_cons25l1_m[] = {' ',0};' \
+ >> makedevs.c
+ ./rtermcap vt100 | \
+ file2c 'const char termcap_vt100[] = {' ',0};' \
+ >> makedevs.c
+ ./rtermcap xterm | \
+ file2c 'const char termcap_xterm[] = {' ',0};' \
+ >> makedevs.c
+
+build-tools: rtermcap
+
+rtermcap: rtermcap.c
+ ${CC} -o ${.TARGET} ${.ALLSRC} -ltermcap
+
+.if ${MACHINE} == "pc98"
+KEYMAPS= jp.pc98 jp.pc98.iso
+.else
+KEYMAPS= be.iso bg.bds.ctrlcaps bg.phonetic.ctrlcaps br275.iso \
+ cs.latin2.qwertz danish.iso finnish.iso fr.iso \
+ german.iso hr.iso hu.iso2.101keys it.iso icelandic.iso jp.106 \
+ norwegian.iso pl_PL.ISO8859-2 pt.iso ru.koi8-r si.iso \
+ spanish.iso swedish.iso swissfrench.iso swissgerman.iso ua.koi8-u \
+ ua.koi8-u.shift.alt uk.iso us.dvorak us.iso us.pc-ctrl us.unix
+.endif
+
+keymap.h:
+ rm -f keymap.tmp
+ for map in ${KEYMAPS} ; do \
+ KEYMAP_PATH=${.CURDIR}/../../share/syscons/keymaps \
+ kbdcontrol -L $$map | \
+ sed -e '/^static accentmap_t/,$$d' >> keymap.tmp ; \
+ done
+ echo "static struct keymapInfo keymapInfos[] = {" >> keymap.tmp
+ for map in ${KEYMAPS} ; do \
+ echo -n ' { "'$$map'", ' >> keymap.tmp ; \
+ echo "&keymap_$$map }," | tr '[-.]' '_' >> keymap.tmp ; \
+ done
+ ( echo " { 0 }"; echo "};" ; echo "" ) >> keymap.tmp
+ mv keymap.tmp keymap.h
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/sade/command.c b/usr.sbin/sade/command.c
new file mode 100644
index 0000000..65d57ae
--- /dev/null
+++ b/usr.sbin/sade/command.c
@@ -0,0 +1,184 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+
+#define MAX_NUM_COMMANDS 10
+
+typedef struct {
+ char key[FILENAME_MAX];
+ struct {
+ enum { CMD_SHELL, CMD_FUNCTION } type;
+ void *ptr, *data;
+ } cmds[MAX_NUM_COMMANDS];
+ int ncmds;
+} Command;
+
+#define MAX_CMDS 200
+static Command *commandStack[MAX_CMDS];
+int numCommands;
+
+/* Nuke the command stack */
+void
+command_clear(void)
+{
+ int i, j;
+
+ for (i = 0; i < numCommands; i++)
+ for (j = 0; j < commandStack[i]->ncmds; j++)
+ if (commandStack[i]->cmds[j].type == CMD_SHELL)
+ free(commandStack[i]->cmds[j].ptr);
+ free(commandStack[i]);
+ numCommands = 0;
+}
+
+static void
+addit(char *key, int type, void *cmd, void *data)
+{
+ int i;
+
+ /* First, look for the key already present and add a command to it if found */
+ for (i = 0; i < numCommands; i++) {
+ if (!strcmp(commandStack[i]->key, key)) {
+ if (commandStack[i]->ncmds == MAX_NUM_COMMANDS)
+ msgFatal("More than %d commands stacked up behind %s??", MAX_NUM_COMMANDS, key);
+ commandStack[i]->cmds[commandStack[i]->ncmds].type = type;
+ commandStack[i]->cmds[commandStack[i]->ncmds].ptr = cmd;
+ commandStack[i]->cmds[commandStack[i]->ncmds].data = data;
+ ++(commandStack[i]->ncmds);
+ return;
+ }
+ }
+ if (numCommands == MAX_CMDS)
+ msgFatal("More than %d commands accumulated??", MAX_CMDS);
+
+ /* If we fell to here, it's a new key */
+ commandStack[numCommands] = safe_malloc(sizeof(Command));
+ strcpy(commandStack[numCommands]->key, key);
+ commandStack[numCommands]->ncmds = 1;
+ commandStack[numCommands]->cmds[0].type = type;
+ commandStack[numCommands]->cmds[0].ptr = cmd;
+ commandStack[numCommands]->cmds[0].data = data;
+ ++numCommands;
+}
+
+/* Add a shell command under a given key */
+void
+command_shell_add(char *key, char *fmt, ...)
+{
+ va_list args;
+ char *cmd;
+
+ cmd = (char *)safe_malloc(256);
+ va_start(args, fmt);
+ vsnprintf(cmd, 256, fmt, args);
+ va_end(args);
+
+ addit(key, CMD_SHELL, cmd, NULL);
+}
+
+/* Add a shell command under a given key */
+void
+command_func_add(char *key, commandFunc func, void *data)
+{
+ addit(key, CMD_FUNCTION, func, data);
+}
+
+static int
+sort_compare(Command *p1, Command *p2)
+{
+ if (!p1 && !p2)
+ return 0;
+ else if (!p1 && p2) /* NULL has a "greater" value for commands */
+ return 1;
+ else if (p1 && !p2)
+ return -1;
+ else
+ return strcmp(p1->key, p2->key);
+}
+
+void
+command_sort(void)
+{
+ int i, j;
+
+ commandStack[numCommands] = NULL;
+ /* Just do a crude bubble sort since the list is small */
+ for (i = 0; i < numCommands; i++) {
+ for (j = 0; j < numCommands; j++) {
+ if (sort_compare(commandStack[j], commandStack[j + 1]) > 0) {
+ Command *tmp = commandStack[j];
+
+ commandStack[j] = commandStack[j + 1];
+ commandStack[j + 1] = tmp;
+ }
+ }
+ }
+}
+
+/* Run all accumulated commands in sorted order */
+void
+command_execute(void)
+{
+ int i, j, ret;
+ commandFunc func;
+
+ for (i = 0; i < numCommands; i++) {
+ for (j = 0; j < commandStack[i]->ncmds; j++) {
+ /* If it's a shell command, run system on it */
+ if (commandStack[i]->cmds[j].type == CMD_SHELL) {
+ msgNotify("Doing %s", (char *)commandStack[i]->cmds[j].ptr);
+ ret = vsystem("%s", (char *)commandStack[i]->cmds[j].ptr);
+ if (isDebug())
+ msgDebug("Command `%s' returns status %d\n",
+ (char *)commandStack[i]->cmds[j].ptr, ret);
+ }
+ else {
+ /* It's a function pointer - call it with the key and
+ the data */
+ func = (commandFunc)commandStack[i]->cmds[j].ptr;
+ if (isDebug())
+ msgDebug("%p: Execute(%s, %s)",
+ func, commandStack[i]->key,
+ (char *)commandStack[i]->cmds[j].data);
+ ret = (*func)(commandStack[i]->key, commandStack[i]->cmds[j].data);
+ if (isDebug())
+ msgDebug("Function @ %p returns status %d\n",
+ commandStack[i]->cmds[j].ptr, ret);
+ }
+ }
+ }
+}
diff --git a/usr.sbin/sade/config.c b/usr.sbin/sade/config.c
new file mode 100644
index 0000000..8878d5b
--- /dev/null
+++ b/usr.sbin/sade/config.c
@@ -0,0 +1,1021 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <sys/disklabel.h>
+#include <sys/wait.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/fcntl.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/mount.h>
+#include <time.h>
+
+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 "msdosfs";
+ else if (c1->type == part) {
+ if (c1->subtype != FS_SWAP)
+ return "ufs";
+ else
+ return "swap";
+ }
+ return "bogus";
+}
+
+static char *
+fstype_short(Chunk *c1)
+{
+ if (c1->type == part) {
+ if (c1->subtype != FS_SWAP) {
+ if (rootdev_is_od == 0 && strncmp(c1->name, "od", 2) == 0)
+ return "rw,noauto";
+ else
+ return "rw";
+ }
+ else
+ return "sw";
+ }
+ else if (c1->type == fat) {
+ if (strncmp(c1->name, "od", 2) == 0)
+ return "ro,noauto";
+ else
+ return "ro";
+ }
+ 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\t\tcd9660\tro,noauto\t0\t0\n", devs[i]->name, cdname);
+ }
+
+ fclose(fstab);
+ if (isDebug())
+ msgDebug("Wrote out /etc/fstab file\n");
+ return DITEM_SUCCESS;
+}
+
+/* Do the work of sucking in a config file.
+ * config is the filename to read in.
+ * lines is a fixed (max) sized array of char*
+ * returns number of lines read. line contents
+ * are malloc'd and must be freed by the caller.
+ */
+int
+readConfig(char *config, char **lines, int max)
+{
+ FILE *fp;
+ char line[256];
+ int i, nlines;
+
+ fp = fopen(config, "r");
+ if (!fp)
+ return -1;
+
+ nlines = 0;
+ /* Read in the entire file */
+ for (i = 0; i < max; i++) {
+ if (!fgets(line, sizeof line, fp))
+ break;
+ lines[nlines++] = strdup(line);
+ }
+ fclose(fp);
+ if (isDebug())
+ msgDebug("readConfig: Read %d lines from %s.\n", nlines, config);
+ return nlines;
+}
+
+#define MAX_LINES 2000 /* Some big number we're not likely to ever reach - I'm being really lazy here, I know */
+
+static void
+readConfigFile(char *config, int marked)
+{
+ char *lines[MAX_LINES], *cp, *cp2;
+ int i, nlines;
+
+ nlines = readConfig(config, lines, MAX_LINES);
+ if (nlines == -1)
+ return;
+
+ for (i = 0; i < nlines; i++) {
+ /* Skip the comments & non-variable settings */
+ if (lines[i][0] == '#' || !(cp = index(lines[i], '='))) {
+ free(lines[i]);
+ continue;
+ }
+ *cp++ = '\0';
+ /* Find quotes */
+ if ((cp2 = index(cp, '"')) || (cp2 = index(cp, '\047'))) {
+ cp = cp2 + 1;
+ cp2 = index(cp, *cp2);
+ }
+ /* If valid quotes, use it */
+ if (cp2) {
+ *cp2 = '\0';
+ /* If we have a legit value, set it */
+ if (strlen(cp))
+ variable_set2(lines[i], cp, marked);
+ }
+ free(lines[i]);
+ }
+}
+
+/* Load the environment from rc.conf file(s) */
+void
+configEnvironmentRC_conf(void)
+{
+ static struct {
+ char *fname;
+ int marked;
+ } configs[] = {
+ { "/etc/defaults/rc.conf", 0 },
+ { "/etc/rc.conf", 0 },
+ { "/etc/rc.conf.local", 0 },
+ { NULL, 0 },
+ };
+ int i;
+
+ for (i = 0; configs[i].fname; i++) {
+ if (file_readable(configs[i].fname))
+ readConfigFile(configs[i].fname, configs[i].marked);
+ }
+}
+
+/* Load the environment from a resolv.conf file */
+void
+configEnvironmentResolv(char *config)
+{
+ char *lines[MAX_LINES];
+ int i, nlines;
+
+ nlines = readConfig(config, lines, MAX_LINES);
+ if (nlines == -1)
+ return;
+ for (i = 0; i < nlines; i++) {
+ Boolean name_set = variable_get(VAR_NAMESERVER) ? 1 : 0;
+
+ if (!strncmp(lines[i], "domain", 6) && !variable_get(VAR_DOMAINNAME))
+ variable_set2(VAR_DOMAINNAME, string_skipwhite(string_prune(lines[i] + 6)), 0);
+ else if (!name_set && !strncmp(lines[i], "nameserver", 10)) {
+ /* Only take the first nameserver setting - we're lame */
+ variable_set2(VAR_NAMESERVER, string_skipwhite(string_prune(lines[i] + 10)), 0);
+ }
+ free(lines[i]);
+ }
+}
+
+/* Version of below for dispatch routines */
+int
+configRC(dialogMenuItem *unused)
+{
+ configRC_conf();
+ return DITEM_SUCCESS;
+}
+
+void
+configRC_conf(void)
+{
+ FILE *rcSite;
+ Variable *v;
+ int write_header;
+ time_t t_loc;
+ char *cp;
+ static int did_marker = 0;
+ time_t tp;
+
+ configTtys();
+ 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, not to /etc/defaults/rc.conf.\n\n");
+ fprintf(rcSite, "# Enable network daemons for user convenience.\n");
+ if ((t_loc = time(NULL)) != -1 && (cp = ctime(&t_loc)))
+ fprintf(rcSite, "# Created: %s", cp);
+ }
+
+ /* Now do variable substitutions */
+ for (v = VarHead; v; v = v->next) {
+ if (v->dirty) {
+ if (!did_marker) {
+ time(&tp);
+ fprintf(rcSite, "# -- sysinstall generated deltas -- # "
+ "%s", ctime(&tp));
+ did_marker = 1;
+ }
+ fprintf(rcSite, "%s=\"%s\"\n", v->name, v->value);
+ v->dirty = 0;
+ }
+ }
+ fclose(rcSite);
+ /* Tidy up the resulting file if it's late enough in the installation
+ for sort and uniq to be available */
+ if (RunningAsInit && file_readable("/usr/bin/sort") && file_readable("/usr/bin/uniq"))
+ (void)vsystem("sort /etc/rc.conf | uniq > /etc/rc.conf.new && mv /etc/rc.conf.new /etc/rc.conf");
+}
+
+int
+configSaver(dialogMenuItem *self)
+{
+ variable_set((char *)self->data, 1);
+ if (!variable_get(VAR_BLANKTIME))
+ variable_set2(VAR_BLANKTIME, "300", 1);
+ return DITEM_SUCCESS;
+}
+
+int
+configSaverTimeout(dialogMenuItem *self)
+{
+ return (variable_get_value(VAR_BLANKTIME,
+ "Enter time-out period in seconds for screen saver", 1) ?
+ DITEM_SUCCESS : DITEM_FAILURE);
+}
+
+int
+configNTP(dialogMenuItem *self)
+{
+ int status;
+
+ status = variable_get_value(VAR_NTPDATE_FLAGS,
+ "Enter the name of an NTP server", 1)
+ ? DITEM_SUCCESS : DITEM_FAILURE;
+ if (status == DITEM_SUCCESS) {
+ static char tmp[255];
+
+ snprintf(tmp, sizeof(tmp), "ntpdate_enable=YES,ntpdate_flags=%s",
+ variable_get(VAR_NTPDATE_FLAGS));
+ self->data = tmp;
+ dmenuSetVariables(self);
+ }
+ return status;
+}
+
+int
+configUsers(dialogMenuItem *self)
+{
+ WINDOW *w = savescr();
+
+ dialog_clear_norefresh();
+ dmenuOpenSimple(&MenuUsermgmt, FALSE);
+ restorescr(w);
+ return DITEM_SUCCESS;
+}
+
+int
+configLinux(dialogMenuItem *self)
+{
+ WINDOW *w = savescr();
+ int i;
+
+ dialog_clear_norefresh();
+ variable_set2(VAR_LINUX_ENABLE, "YES", 1);
+ Mkdir("/compat/linux");
+ msgNotify("Installing Linux compatibility library...");
+ i = package_add("linux_base");
+ restorescr(w);
+ return i;
+}
+
+int
+configSecurity(dialogMenuItem *self)
+{
+ WINDOW *w = savescr();
+
+ dialog_clear_norefresh();
+ dmenuOpenSimple(&MenuSecurity, FALSE);
+ restorescr(w);
+ return DITEM_SUCCESS;
+}
+
+int
+configSecurityProfile(dialogMenuItem *self)
+{
+ WINDOW *w = savescr();
+
+ dialog_clear_norefresh();
+ dmenuOpenSimple(&MenuSecurityProfile, FALSE);
+ restorescr(w);
+ return DITEM_SUCCESS;
+}
+
+/* Use the most extreme security settings */
+int
+configSecurityExtreme(dialogMenuItem *self)
+{
+ WINDOW *w = savescr();
+
+ variable_set2("nfs_server_enable", "NO", 1);
+ variable_set2("sendmail_enable", "NO", 1);
+ variable_set2("sshd_enable", "NO", 1);
+ variable_set2("kern_securelevel_enable", "YES", 1);
+ variable_set2("kern_securelevel", "2", 1);
+
+ if (self)
+ msgConfirm("Extreme security settings have been selected.\n\n"
+ "Sendmail, sshd, and NFS services have been disabled, and\n"
+ "securelevels have been enabled.\n\n"
+ "PLEASE NOTE that this still does not save you from having\n"
+ "to properly secure your system in other ways or exercise\n"
+ "due diligence in your administration, this simply picks\n"
+ "a more secure set of out-of-box defaults to start with.\n\n"
+ "To change any of these settings later, edit /etc/rc.conf");
+
+ restorescr(w);
+ return DITEM_SUCCESS;
+}
+
+int
+configSecurityModerate(dialogMenuItem *self)
+{
+ WINDOW *w = savescr();
+
+ variable_set2("sendmail_enable", "YES", 1);
+ variable_set2("sshd_enable", "YES", 1);
+ variable_set2("kern_securelevel_enable", "NO", 1);
+
+ if (self)
+ msgConfirm("Moderate security settings have been selected.\n\n"
+ "Sendmail and sshd have been enabled, securelevels are\n"
+ "disabled, and NFS server settings have been left intact.\n\n"
+ "PLEASE NOTE that this still does not save you from having\n"
+ "to properly secure your system in other ways or exercise\n"
+ "due diligence in your administration, this simply picks\n"
+ "a standard set of out-of-box defaults to start with.\n\n"
+ "To change any of these settings later, edit /etc/rc.conf");
+
+ restorescr(w);
+ return DITEM_SUCCESS;
+}
+
+static void
+write_root_xprofile(char *str)
+{
+ FILE *fp;
+ int len;
+ char **cp;
+ static char *flist[] = { /* take care of both xdm and startx */
+ "/root/.xinitrc",
+ "/root/.xsession",
+ "/usr/share/skel/dot.xinitrc",
+ "/usr/share/skel/dot.xsession",
+ NULL,
+ };
+
+ len = strlen(str);
+ for (cp = flist; *cp; cp++) {
+ fp = fopen(*cp, "w");
+ if (fp) {
+ fwrite(str, 1, len, fp);
+ fchmod(fileno(fp), 0755);
+ fclose(fp);
+ }
+ }
+}
+
+static int
+gotit(char *fname)
+{
+ char tmp[FILENAME_MAX];
+
+ snprintf(tmp, sizeof tmp, "/usr/X11R6/bin/%s", fname);
+ if (file_executable(tmp))
+ return TRUE;
+ snprintf(tmp, sizeof tmp, "/usr/local/bin/%s", fname);
+ return file_executable(tmp);
+}
+
+int
+configXDesktop(dialogMenuItem *self)
+{
+ char *desk;
+ int ret = DITEM_SUCCESS;
+ WINDOW *w = savescr();
+
+ dialog_clear_norefresh();
+ if (!dmenuOpenSimple(&MenuXDesktops, FALSE) || !(desk = variable_get(VAR_DESKSTYLE))) {
+ restorescr(w);
+ return DITEM_FAILURE;
+ }
+ if (!strcmp(desk, "kde")) {
+ ret = package_add("kde");
+ if (DITEM_STATUS(ret) != DITEM_FAILURE && gotit("startkde"))
+ write_root_xprofile("exec startkde\n");
+ }
+ else if (!strcmp(desk, "gnome")) {
+ ret = package_add("gnomecore");
+ if (DITEM_STATUS(ret) != DITEM_FAILURE && gotit("gnome-session")) {
+ ret = package_add("sawfish-gnome");
+ if (DITEM_STATUS(ret) != DITEM_FAILURE && gotit("sawfish"))
+ write_root_xprofile("exec gnome-session\n");
+ }
+ }
+ else if (!strcmp(desk, "enlightenment")) {
+ ret = package_add("gnomecore");
+ if (DITEM_STATUS(ret) != DITEM_FAILURE && gotit("gnome-session")) {
+ ret = package_add("enlightenment");
+ if (DITEM_STATUS(ret) != DITEM_FAILURE && gotit("enlightenment"))
+ write_root_xprofile("exec gnome-session\n");
+ }
+ }
+ else if (!strcmp(desk, "afterstep")) {
+ ret = package_add("afterstep");
+ if (DITEM_STATUS(ret) != DITEM_FAILURE && gotit("afterstep"))
+ write_root_xprofile("exec afterstep\n");
+ }
+ else if (!strcmp(desk, "windowmaker")) {
+ ret = package_add("windowmaker");
+ if (DITEM_STATUS(ret) != DITEM_FAILURE && gotit("wmaker.inst")) {
+ write_root_xprofile("xterm &\n[ ! -d $HOME/GNUstep/Library/WindowMaker ] && /usr/X11R6/bin/wmaker.inst\nexec /usr/X11R6/bin/wmaker\n");
+ }
+ }
+ else if (!strcmp(desk, "fvwm2")) {
+ ret = package_add("fvwm");
+ if (DITEM_STATUS(ret) != DITEM_FAILURE && gotit("fvwm"))
+ write_root_xprofile("exec fvwm\n");
+ }
+ if (DITEM_STATUS(ret) == DITEM_FAILURE)
+ msgConfirm("An error occurred while adding the package(s) required\n"
+ "by this desktop type. Please change installation media\n"
+ "and/or select a different, perhaps simpler, desktop\n"
+ "environment and try again.");
+ restorescr(w);
+ return ret;
+}
+
+int
+configXSetup(dialogMenuItem *self)
+{
+ char *config, *execfile, *execcmd, *style, *tmp;
+ char *moused;
+ WINDOW *w = savescr();
+
+ setenv("XWINHOME", "/usr/X11R6", 1);
+tryagain:
+ variable_unset(VAR_DESKSTYLE);
+ variable_unset(VAR_XF86_CONFIG);
+ dialog_clear_norefresh();
+ if (!dmenuOpenSimple(&MenuXF86Config, FALSE)) {
+ restorescr(w);
+ return DITEM_FAILURE;
+ }
+ config = variable_get(VAR_XF86_CONFIG);
+ style = variable_get(VAR_DESKSTYLE);
+ if (!config) {
+ if (style)
+ goto config_desktop;
+ else {
+ restorescr(w);
+ return DITEM_FAILURE;
+ }
+ }
+
+ if (file_readable("/var/run/ld-elf.so.hints"))
+ vsystem("/sbin/ldconfig -m /usr/lib /usr/X11R6/lib /usr/local/lib /usr/lib/compat");
+ else
+ vsystem("/sbin/ldconfig /usr/lib /usr/X11R6/lib /usr/local/lib /usr/lib/compat");
+ if (file_readable("/var/run/ld.so.hints"))
+ vsystem("ldconfig -m -aout /usr/lib/aout /usr/lib/compat/aout /usr/local/lib/aout /usr/X11R6/lib/aout");
+ else
+ vsystem("ldconfig -aout /usr/lib/aout /usr/lib/compat/aout /usr/local/lib/aout /usr/X11R6/lib/aout");
+
+ vsystem("/sbin/ifconfig lo0 127.0.0.1");
+
+ /*
+ * execcmd may have been passed in as a command name with
+ * arguments. Therefore, before determining if it is suitable for
+ * execution, we must split off the filename component from the
+ * command line arguments.
+ */
+
+ execcmd = string_concat("/usr/X11R6/bin/", config);
+ execfile = strdup(execcmd);
+ if ((tmp = strchr(execfile, ' ')))
+ *tmp = '\0';
+ if (file_executable(execfile)) {
+ free(execfile);
+ moused = variable_get(VAR_MOUSED);
+ while (!moused || strcmp(moused, "YES")) {
+ if (msgYesNo("The X server may access the mouse in two ways: direct access\n"
+ "or indirect access via the mouse daemon. You have not\n"
+ "configured the mouse daemon. Would you like to configure it\n"
+ "now? If you intend to let the X server access the mouse\n"
+ "directly, choose \"No\" at this time."))
+ break;
+ dialog_clear_norefresh();
+ dmenuOpenSimple(&MenuMouse, FALSE);
+ moused = variable_get(VAR_MOUSED);
+ }
+ if (moused && !strcmp(moused, "YES"))
+ msgConfirm("You have configured and are now running the mouse daemon.\n"
+ "Choose \"/dev/sysmouse\" as the mouse port and \"SysMouse\" or\n"
+ "\"MouseSystems\" as the mouse protocol in the X configuration\n"
+ "utility.");
+ Mkdir("/etc/X11"); /* XXX:Remove this later after we are happy mtree will have created this for us. */
+ systemExecute(execcmd);
+ if (!file_readable("/etc/X11/XF86Config")) {
+ if (!msgYesNo("The XFree86 configuration process seems to have\nfailed. Would you like to try again?"))
+ goto tryagain;
+ else {
+ restorescr(w);
+ return DITEM_FAILURE;
+ }
+ }
+config_desktop:
+ configXDesktop(self);
+ restorescr(w);
+ return DITEM_SUCCESS;
+ }
+ else {
+ free(execfile);
+ msgConfirm("The XFree86 setup utility you chose does not appear to be installed!\n"
+ "Please install this before attempting to configure XFree86.");
+ restorescr(w);
+ return DITEM_FAILURE;
+ }
+}
+
+int
+configResolv(dialogMenuItem *ditem)
+{
+ FILE *fp;
+ char *cp, *c6p, *dp, *hp;
+
+ cp = variable_get(VAR_NAMESERVER);
+ if (!cp || !*cp)
+ goto skip;
+ Mkdir("/etc");
+ fp = fopen("/etc/resolv.conf", "w");
+ if (!fp)
+ return DITEM_FAILURE;
+ if (variable_get(VAR_DOMAINNAME))
+ fprintf(fp, "domain\t%s\n", variable_get(VAR_DOMAINNAME));
+ fprintf(fp, "nameserver\t%s\n", cp);
+ fclose(fp);
+ if (isDebug())
+ msgDebug("Wrote out /etc/resolv.conf\n");
+
+skip:
+ dp = variable_get(VAR_DOMAINNAME);
+ cp = variable_get(VAR_IPADDR);
+ c6p = variable_get(VAR_IPV6ADDR);
+ hp = variable_get(VAR_HOSTNAME);
+ /* Tack ourselves into /etc/hosts */
+ fp = fopen("/etc/hosts", "w");
+ if (!fp)
+ return DITEM_FAILURE;
+ /* Add an entry for localhost */
+ if (!variable_cmp(VAR_IPV6_ENABLE, "YES")) {
+ if (dp)
+ fprintf(fp, "::1\t\t\tlocalhost.%s localhost\n", dp);
+ else
+ fprintf(fp, "::1\t\t\tlocalhost\n");
+ }
+ 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') || (c6p && c6p[0] != '0')) && hp) {
+ char cp2[255];
+
+ if (!index(hp, '.'))
+ cp2[0] = '\0';
+ else {
+ SAFE_STRCPY(cp2, hp);
+ *(index(cp2, '.')) = '\0';
+ }
+ if (c6p && c6p[0] != '0') {
+ fprintf(fp, "%s\t%s %s\n", c6p, hp, cp2);
+ fprintf(fp, "%s\t%s.\n", c6p, hp);
+ }
+ if (cp && cp[0] != '0') {
+ fprintf(fp, "%s\t\t%s %s\n", cp, hp, cp2);
+ fprintf(fp, "%s\t\t%s.\n", cp, hp);
+ }
+ }
+ fclose(fp);
+ if (isDebug())
+ msgDebug("Wrote out /etc/hosts\n");
+ return DITEM_SUCCESS;
+}
+
+int
+configRouter(dialogMenuItem *self)
+{
+ int ret;
+
+ ret = variable_get_value(VAR_ROUTER,
+ "Please specify the router you wish to use. Routed is\n"
+ "provided with the stock system and gated is provided\n"
+ "as an optional package which this installation system\n"
+ "will attempt to load if you select gated. Any other\n"
+ "choice of routing daemon will be assumed to be something\n"
+ "the user intends to install themselves before rebooting\n"
+ "the system. If you don't want any routing daemon, choose NO", 1)
+ ? DITEM_SUCCESS : DITEM_FAILURE;
+
+ if (ret == DITEM_SUCCESS) {
+ char *cp = variable_get(VAR_ROUTER);
+
+ if (cp && strcmp(cp, "NO")) {
+ variable_set2(VAR_ROUTER_ENABLE, "YES", 1);
+ if (!strcmp(cp, "gated")) {
+ if (package_add("gated") != DITEM_SUCCESS) {
+ msgConfirm("Unable to load gated package. Falling back to no router.");
+ variable_unset(VAR_ROUTER);
+ variable_unset(VAR_ROUTERFLAGS);
+ variable_set2(VAR_ROUTER_ENABLE, "NO", 1);
+ cp = NULL;
+ }
+ }
+ if (cp) {
+ /* Now get the flags, if they chose a router */
+ ret = variable_get_value(VAR_ROUTERFLAGS,
+ "Please Specify the routing daemon flags; if you're running routed\n"
+ "then -q is the right choice for nodes and -s for gateway hosts.\n", 1)
+ ? DITEM_SUCCESS : DITEM_FAILURE;
+ if (ret != DITEM_SUCCESS)
+ variable_unset(VAR_ROUTERFLAGS);
+ }
+ }
+ else {
+ /* No router case */
+ variable_set2(VAR_ROUTER_ENABLE, "NO", 1);
+ variable_unset(VAR_ROUTERFLAGS);
+ variable_unset(VAR_ROUTER);
+ }
+ }
+ else {
+ variable_set2(VAR_ROUTER_ENABLE, "NO", 1);
+ variable_unset(VAR_ROUTERFLAGS);
+ variable_unset(VAR_ROUTER);
+ }
+ return ret;
+}
+
+/* Shared between us and index_initialize() */
+extern PkgNode Top, Plist;
+
+int
+configPackages(dialogMenuItem *self)
+{
+ int i, restoreflag = 0;
+ PkgNodePtr tmp;
+
+ /* Did we get an INDEX? */
+ i = index_initialize("packages/INDEX");
+ if (DITEM_STATUS(i) == DITEM_FAILURE)
+ return i;
+
+ while (1) {
+ int ret, pos, scroll;
+
+ /* Bring up the packages menu */
+ pos = scroll = 0;
+ index_menu(&Top, &Top, &Plist, &pos, &scroll);
+
+ if (Plist.kids && Plist.kids->name) {
+ /* Now show the packing list menu */
+ pos = scroll = 0;
+ ret = index_menu(&Plist, &Plist, NULL, &pos, &scroll);
+ if (ret & DITEM_LEAVE_MENU)
+ break;
+ else if (DITEM_STATUS(ret) != DITEM_FAILURE) {
+ dialog_clear();
+ restoreflag = 1;
+ for (tmp = Plist.kids; tmp && tmp->name; tmp = tmp->next)
+ (void)index_extract(mediaDevice, &Top, tmp, FALSE);
+ break;
+ }
+ }
+ else {
+ msgConfirm("No packages were selected for extraction.");
+ break;
+ }
+ }
+ tmp = Plist.kids;
+ while (tmp) {
+ PkgNodePtr tmp2 = tmp->next;
+
+ safe_free(tmp);
+ tmp = tmp2;
+ }
+ index_init(NULL, &Plist);
+ return DITEM_SUCCESS | (restoreflag ? DITEM_RESTORE : 0);
+}
+
+/* Load pcnfsd package */
+int
+configPCNFSD(dialogMenuItem *self)
+{
+ int ret;
+
+ ret = package_add("pcnfsd");
+ if (DITEM_STATUS(ret) == DITEM_SUCCESS) {
+ variable_set2(VAR_PCNFSD, "YES", 0);
+ variable_set2("mountd_flags", "-n", 1);
+ }
+ return ret;
+}
+
+int
+configInetd(dialogMenuItem *self)
+{
+ char cmd[256];
+
+ WINDOW *w = savescr();
+
+ if (msgYesNo("The Internet Super Server (inetd) allows a number of simple Internet\n"
+ "services to be enabled, including finger, ftp, and telnetd. Enabling\n"
+ "these services may increase risk of security problems by increasing\n"
+ "the exposure of your system.\n\n"
+ "With this in mind, do you wish to enable inetd?\n")) {
+ variable_set2("inetd_enable", "NO", 1);
+ } else {
+ /* If inetd is enabled, we'll need an inetd.conf */
+
+ if (!msgYesNo("inetd(8) relies on its configuration file, /etc/inetd.conf, to determine\n"
+ "which of its Internet services will be available. The default FreeBSD\n"
+ "inetd.conf(5) leaves all services disabled by default, so they must be\n"
+ "specifically enabled in the configuration file before they will\n"
+ "function, even once inetd(8) is enabled. Note that services for\n"
+ "IPv6 must be seperately enabled from IPv4 services.\n\n"
+ "Select [Yes] now to invoke an editor on /etc/inetd.conf, or [No] to\n"
+ "use the current settings.\n")) {
+ sprintf(cmd, "%s /etc/inetd.conf", variable_get(VAR_EDITOR));
+ dialog_clear();
+ systemExecute(cmd);
+ variable_set2("inetd_enable", "YES", 1);
+ }
+ }
+ restorescr(w);
+ return DITEM_SUCCESS;
+}
+
+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 '#/usr/src and /usr/ports read-only to machines named after trouble makers' >> /etc/exports");
+ vsystem("echo '#/home and all directories under it to machines named after dead rock stars' >> /etc/exports");
+ vsystem("echo '#and, /a to a network of privileged machines allowed to write on it as root.' >> /etc/exports");
+ vsystem("echo '#/usr huey louie dewie' >> /etc/exports");
+ vsystem("echo '#/usr/src /usr/obj -ro calvin hobbes' >> /etc/exports");
+ vsystem("echo '#/home -alldirs janice jimmy frank' >> /etc/exports");
+ vsystem("echo '#/a -maproot=0 -network 10.0.1.0 -mask 255.255.248.0' >> /etc/exports");
+ vsystem("echo '#' >> /etc/exports");
+ vsystem("echo '# You should replace these lines with your actual exported filesystems.' >> /etc/exports");
+ vsystem("echo '# Note that BSD's export synatx is \"host-centric\" vs. Sun\'s \"FS-centric\" one.' >> /etc/exports");
+ vsystem("echo >> /etc/exports");
+ sprintf(cmd, "%s /etc/exports", variable_get(VAR_EDITOR));
+ dialog_clear();
+ systemExecute(cmd);
+ }
+ variable_set2(VAR_NFS_SERVER, "YES", 1);
+ restorescr(w);
+ }
+ else if (variable_get(VAR_NFS_SERVER)) { /* We want to turn it off again? */
+ vsystem("mv -f /etc/exports /etc/exports.disabled");
+ variable_unset(VAR_NFS_SERVER);
+ }
+ return DITEM_SUCCESS;
+}
+
+int
+configEtcTtys(dialogMenuItem *self)
+{
+ char cmd[256];
+
+ WINDOW *w = savescr();
+
+ /* Simply prompt for confirmation, then edit away. */
+ if (msgYesNo("Configuration of system TTYs requires editing the /etc/ttys file.\n"
+ "Typical configuration activities might include enabling getty(8)\n"
+ "on the first serial port to allow login via serial console after\n"
+ "reboot, or to enable xdm. The default ttys file enables normal\n"
+ "virtual consoles, and most sites will not need to perform manual\n"
+ "configuration.\n\n"
+ "To load /etc/ttys in the editor, select [Yes], otherwise, [No].")) {
+ } else {
+ configTtys();
+ sprintf(cmd, "%s /etc/ttys", variable_get(VAR_EDITOR));
+ dialog_clear();
+ systemExecute(cmd);
+ }
+
+ restorescr(w);
+ return DITEM_SUCCESS;
+}
diff --git a/usr.sbin/sade/devices.c b/usr.sbin/sade/devices.c
new file mode 100644
index 0000000..7df1b3e
--- /dev/null
+++ b/usr.sbin/sade/devices.c
@@ -0,0 +1,573 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <sys/fcntl.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_dl.h>
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+
+/* how much to bias minor number for a given /dev/<ct#><un#>s<s#> slice */
+#define SLICE_DELTA (0x10000)
+
+static Device *Devices[DEV_MAX];
+static int numDevs;
+
+static struct _devname {
+ DeviceType type;
+ char *name;
+ char *description;
+ int major, minor, delta, max;
+} device_names[] = {
+ { DEVICE_TYPE_CDROM, "cd%dc", "SCSI CDROM drive", 15, 2, 8, 4 },
+ { DEVICE_TYPE_CDROM, "mcd%da", "Mitsumi (old model) CDROM drive", 29, 0, 8, 4 },
+ { DEVICE_TYPE_CDROM, "scd%da", "Sony CDROM drive - CDU31/33A type", 45, 0, 8, 4 },
+#ifdef notdef
+ { DEVICE_TYPE_CDROM, "matcd%da", "Matsushita CDROM ('sound blaster' type)", 46, 0, 8, 4 },
+#endif
+ { DEVICE_TYPE_CDROM, "acd%dc", "ATAPI/IDE CDROM", 117, 0, 8, 4 },
+ { DEVICE_TYPE_TAPE, "rsa%d", "SCSI tape drive", 14, 0, 16, 4 },
+ { DEVICE_TYPE_TAPE, "rwt%d", "Wangtek tape drive", 10, 0, 1, 4 },
+ { DEVICE_TYPE_DISK, "da%d", "SCSI disk device", 13, 65538, 8, 16 },
+ { DEVICE_TYPE_DISK, "ad%d", "ATA/IDE disk device", 116, 65538, 8, 16 },
+ { DEVICE_TYPE_DISK, "ar%d", "ATA/IDE RAID device", 157, 65538, 8, 16 },
+ { DEVICE_TYPE_DISK, "fla%d", "M-Systems DiskOnChip Flash devicee", 102, 65538, 8, 16 },
+ { DEVICE_TYPE_DISK, "afd%d", "ATAPI/IDE floppy device", 118, 65538, 8, 4 },
+ { DEVICE_TYPE_DISK, "mlxd%d", "Mylex RAID disk", 131, 65538, 8, 4 },
+ { DEVICE_TYPE_DISK, "amrd%d", "AMI MegaRAID drive", 133, 65538, 8, 4 },
+ { DEVICE_TYPE_DISK, "idad%d", "Compaq RAID array", 109, 65538, 8, 4 },
+ { DEVICE_TYPE_DISK, "twed%d", "3ware ATA RAID array", 147, 65538, 8, 4 },
+ { DEVICE_TYPE_DISK, "aacd%d", "Adaptec FSA RAID array", 151, 65538, 8, 4 },
+ { DEVICE_TYPE_FLOPPY, "fd%d", "floppy drive unit A", 9, 0, 64, 4 },
+ { DEVICE_TYPE_NETWORK, "an", "Aironet 4500/4800 802.11 wireless adapter" },
+ { DEVICE_TYPE_NETWORK, "aue", "ADMtek USB ethernet adapter" },
+ { DEVICE_TYPE_NETWORK, "bge", "Broadcom BCM570x PCI gigabit ethernet card" },
+ { DEVICE_TYPE_NETWORK, "cue", "CATC USB ethernet adapter" },
+ { DEVICE_TYPE_NETWORK, "fpa", "DEC DEFPA PCI FDDI card" },
+ { DEVICE_TYPE_NETWORK, "sr", "SDL T1/E1 sync serial PCI card" },
+ { DEVICE_TYPE_NETWORK, "cc3i", "SDL HSSI sync serial PCI card" },
+ { DEVICE_TYPE_NETWORK, "en", "Efficient Networks ATM PCI card" },
+ { DEVICE_TYPE_NETWORK, "dc", "DEC/Intel 21143 (and clones) PCI fast ethernet card" },
+ { DEVICE_TYPE_NETWORK, "de", "DEC DE435 PCI NIC or other DC21040-AA based card" },
+ { DEVICE_TYPE_NETWORK, "fxp", "Intel EtherExpress Pro/100B PCI Fast Ethernet card" },
+ { DEVICE_TYPE_NETWORK, "ed", "Novell NE1000/2000; 3C503; NE2000-compatible PCMCIA" },
+ { DEVICE_TYPE_NETWORK, "ep", "3Com 3C509 ethernet card/3C589 PCMCIA" },
+ { DEVICE_TYPE_NETWORK, "el", "3Com 3C501 ethernet card" },
+ { DEVICE_TYPE_NETWORK, "ex", "Intel EtherExpress Pro/10 ethernet card" },
+ { DEVICE_TYPE_NETWORK, "fe", "Fujitsu MB86960A/MB86965A ethernet card" },
+ { DEVICE_TYPE_NETWORK, "ie", "AT&T StarLAN 10 and EN100; 3Com 3C507; NI5210" },
+ { DEVICE_TYPE_NETWORK, "ix", "Intel Etherexpress ethernet card" },
+ { DEVICE_TYPE_NETWORK, "kue", "Kawasaki LSI USB ethernet adapter" },
+ { DEVICE_TYPE_NETWORK, "le", "DEC EtherWorks 2 or 3 ethernet card" },
+ { DEVICE_TYPE_NETWORK, "lnc", "Lance/PCnet (Isolan/Novell NE2100/NE32-VL) ethernet" },
+ { DEVICE_TYPE_NETWORK, "lge", "Level 1 LXT1001 gigabit ethernet card" },
+ { DEVICE_TYPE_NETWORK, "nge", "NatSemi PCI gigabit ethernet card" },
+ { DEVICE_TYPE_NETWORK, "pcn", "AMD Am79c79x PCI ethernet card" },
+ { DEVICE_TYPE_NETWORK, "ray", "Raytheon Raylink 802.11 wireless adaptor" },
+ { DEVICE_TYPE_NETWORK, "rl", "RealTek 8129/8139 PCI ethernet card" },
+ { DEVICE_TYPE_NETWORK, "sf", "Adaptec AIC-6915 PCI ethernet card" },
+ { DEVICE_TYPE_NETWORK, "sis", "SiS 900/SiS 7016 PCI ethernet card" },
+#ifdef PC98
+ { DEVICE_TYPE_NETWORK, "snc", "SONIC ethernet card" },
+#endif
+ { DEVICE_TYPE_NETWORK, "sn", "SMC/Megahertz ethernet card" },
+ { DEVICE_TYPE_NETWORK, "ste", "Sundance ST201 PCI ethernet card" },
+ { DEVICE_TYPE_NETWORK, "sk", "SysKonnect PCI gigabit ethernet card" },
+ { DEVICE_TYPE_NETWORK, "tx", "SMC 9432TX ethernet card" },
+ { DEVICE_TYPE_NETWORK, "txp", "3Com 3cR990 ethernet card" },
+ { DEVICE_TYPE_NETWORK, "ti", "Alteon Networks PCI gigabit ethernet card" },
+ { DEVICE_TYPE_NETWORK, "tl", "Texas Instruments ThunderLAN PCI ethernet card" },
+ { DEVICE_TYPE_NETWORK, "vr", "VIA VT3043/VT86C100A Rhine PCI ethernet card" },
+ { DEVICE_TYPE_NETWORK, "vx", "3COM 3c590 / 3c595 ethernet card" },
+ { DEVICE_TYPE_NETWORK, "wb", "Winbond W89C840F PCI ethernet card" },
+ { DEVICE_TYPE_NETWORK, "wi", "Lucent WaveLAN/IEEE 802.11 wireless adapter" },
+ { DEVICE_TYPE_NETWORK, "wx", "Intel Gigabit Ethernet (82452) card" },
+ { DEVICE_TYPE_NETWORK, "xe", "Xircom/Intel EtherExpress Pro100/16 ethernet card" },
+ { DEVICE_TYPE_NETWORK, "xl", "3COM 3c90x / 3c90xB PCI ethernet card" },
+ { DEVICE_TYPE_NETWORK, "cuaa%d", "%s on device %s (COM%d)", 28, 128, 1, 16 },
+ { DEVICE_TYPE_NETWORK, "lp", "Parallel Port IP (PLIP) peer connection" },
+ { DEVICE_TYPE_NETWORK, "lo", "Loop-back (local) network interface" },
+#ifdef PC98
+ { DEVICE_TYPE_DISK, "wd%d", "IDE disk device", 3, 65538, 8, 16 },
+ { DEVICE_TYPE_CDROM, "wcd%dc", "ATAPI IDE CDROM", 69, 2, 8, 4 },
+ { DEVICE_TYPE_FLOPPY, "wfd%d", "ATAPI floppy drive unit A", 87, 0, 8, 4 },
+ { DEVICE_TYPE_DISK, "wfd%d", "ATAPI floppy device", 87, 65538, 8, 4 },
+#endif
+ { 0 },
+};
+
+Device *
+new_device(char *name)
+{
+ Device *dev;
+
+ dev = safe_malloc(sizeof(Device));
+ bzero(dev, sizeof(Device));
+ if (name)
+ SAFE_STRCPY(dev->name, name);
+ return dev;
+}
+
+/* Stubs for unimplemented strategy routines */
+Boolean
+dummyInit(Device *dev)
+{
+ return TRUE;
+}
+
+FILE *
+dummyGet(Device *dev, char *dist, Boolean probe)
+{
+ return NULL;
+}
+
+void
+dummyShutdown(Device *dev)
+{
+ return;
+}
+
+static int
+deviceTry(struct _devname dev, char *try, int i)
+{
+ int fd;
+ char unit[80];
+ mode_t m;
+ dev_t d;
+ int fail;
+
+ snprintf(unit, sizeof unit, dev.name, i);
+ snprintf(try, FILENAME_MAX, "/dev/%s", unit);
+ if (isDebug())
+ msgDebug("deviceTry: attempting to open %s\n", try);
+ fd = open(try, O_RDONLY);
+ if (fd >= 0) {
+ if (isDebug())
+ msgDebug("deviceTry: open of %s succeeded on first try.\n", try);
+ return fd;
+ }
+ m = 0640 | S_IFCHR;
+ d = makedev(dev.major, dev.minor + (i * dev.delta));
+ if (isDebug())
+ msgDebug("deviceTry: Making %s device for %s [%d, %d]\n", m & S_IFCHR ? "raw" : "block", try, dev.major, dev.minor + (i * dev.delta));
+ fail = mknod(try, m, d);
+ fd = open(try, O_RDONLY);
+ if (fd >= 0) {
+ if (isDebug())
+ msgDebug("deviceTry: open of %s succeeded on second try.\n", try);
+ return fd;
+ }
+ else if (!fail)
+ (void)unlink(try);
+ /* Don't try a "make-under" here since we're using a fixit floppy in this case */
+ snprintf(try, FILENAME_MAX, "/mnt/dev/%s", unit);
+ fd = open(try, O_RDONLY);
+ if (isDebug())
+ msgDebug("deviceTry: final attempt for %s returns %d\n", try, fd);
+ return fd;
+}
+
+/* Register a new device in the devices array */
+Device *
+deviceRegister(char *name, char *desc, char *devname, DeviceType type, Boolean enabled,
+ Boolean (*init)(Device *), FILE * (*get)(Device *, char *, Boolean),
+ void (*shutdown)(Device *), void *private)
+{
+ Device *newdev = NULL;
+
+ if (numDevs == DEV_MAX)
+ msgFatal("Too many devices found!");
+ else {
+ newdev = new_device(name);
+ newdev->description = desc;
+ newdev->devname = devname;
+ newdev->type = type;
+ newdev->enabled = enabled;
+ newdev->init = init ? init : dummyInit;
+ newdev->get = get ? get : dummyGet;
+ newdev->shutdown = shutdown ? shutdown : dummyShutdown;
+ newdev->private = private;
+ Devices[numDevs] = newdev;
+ Devices[++numDevs] = NULL;
+ }
+ return newdev;
+}
+
+/* Reset the registered device chain */
+void
+deviceReset(void)
+{
+ int i;
+
+ for (i = 0; i < numDevs; i++) {
+ DEVICE_SHUTDOWN(Devices[i]);
+
+ /* XXX this potentially leaks Devices[i]->private if it's being
+ * used to point to something dynamic, but you're not supposed
+ * to call this routine at such times that some open instance
+ * has its private ptr pointing somewhere anyway. XXX
+ */
+ free(Devices[i]);
+ }
+ Devices[numDevs = 0] = NULL;
+}
+
+/* Get all device information for devices we have attached */
+void
+deviceGetAll(void)
+{
+ int i, j, fd, s;
+ struct ifconf ifc;
+ struct ifreq *ifptr, *end;
+ int ifflags;
+ char buffer[INTERFACE_MAX * sizeof(struct ifreq)];
+ char **names;
+
+ msgNotify("Probing devices, please wait (this can take a while)...");
+ /* First go for the network interfaces. Stolen shamelessly from ifconfig! */
+ ifc.ifc_len = sizeof(buffer);
+ ifc.ifc_buf = buffer;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0)
+ goto skipif; /* Jump over network iface probing */
+
+ if (ioctl(s, SIOCGIFCONF, (char *) &ifc) < 0)
+ goto skipif; /* Jump over network iface probing */
+
+ close(s);
+ ifflags = ifc.ifc_req->ifr_flags;
+ end = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
+ for (ifptr = ifc.ifc_req; ifptr < end; ifptr++) {
+ char *descr;
+
+ /* If it's not a link entry, forget it */
+ if (ifptr->ifr_ifru.ifru_addr.sa_family != AF_LINK)
+ goto loopend;
+
+ /* Eliminate network devices that don't make sense */
+ if (!strncmp(ifptr->ifr_name, "lo", 2))
+ goto loopend;
+
+ /* If we have a slip device, don't register it */
+ if (!strncmp(ifptr->ifr_name, "sl", 2)) {
+ goto loopend;
+ }
+ /* And the same for ppp */
+ if (!strncmp(ifptr->ifr_name, "tun", 3) || !strncmp(ifptr->ifr_name, "ppp", 3)) {
+ goto loopend;
+ }
+ /* Try and find its description */
+ for (i = 0, descr = NULL; device_names[i].name; i++) {
+ int len = strlen(device_names[i].name);
+
+ if (!ifptr->ifr_name || !ifptr->ifr_name[0])
+ continue;
+ else if (!strncmp(ifptr->ifr_name, device_names[i].name, len)) {
+ descr = device_names[i].description;
+ break;
+ }
+ }
+ if (!descr)
+ descr = "<unknown network interface type>";
+
+ deviceRegister(ifptr->ifr_name, descr, strdup(ifptr->ifr_name), DEVICE_TYPE_NETWORK, TRUE,
+ mediaInitNetwork, NULL, mediaShutdownNetwork, NULL);
+ if (isDebug())
+ msgDebug("Found a network device named %s\n", ifptr->ifr_name);
+ close(s);
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+ continue;
+
+loopend:
+ if (ifptr->ifr_addr.sa_len) /* I'm not sure why this is here - it's inherited */
+ ifptr = (struct ifreq *)((caddr_t)ifptr + ifptr->ifr_addr.sa_len - sizeof(struct sockaddr));
+ close(s);
+ }
+
+skipif:
+ /* Next, try to find all the types of devices one might need
+ * during the second stage of the installation.
+ */
+ for (i = 0; device_names[i].name; i++) {
+ for (j = 0; j < device_names[i].max; j++) {
+ char try[FILENAME_MAX];
+
+ switch(device_names[i].type) {
+ case DEVICE_TYPE_CDROM:
+ fd = deviceTry(device_names[i], try, j);
+ if (fd >= 0 || errno == EBUSY) { /* EBUSY if already mounted */
+ char n[BUFSIZ];
+
+ if (fd >= 0) close(fd);
+ snprintf(n, sizeof n, device_names[i].name, j);
+ deviceRegister(strdup(n), device_names[i].description, strdup(try),
+ DEVICE_TYPE_CDROM, TRUE, mediaInitCDROM, mediaGetCDROM,
+ mediaShutdownCDROM, NULL);
+ if (isDebug())
+ msgDebug("Found a CDROM device for %s\n", try);
+ }
+ break;
+
+ case DEVICE_TYPE_TAPE:
+ fd = deviceTry(device_names[i], try, j);
+ if (fd >= 0) {
+ char n[BUFSIZ];
+
+ close(fd);
+ snprintf(n, sizeof n, device_names[i].name, j);
+ deviceRegister(strdup(n), device_names[i].description, strdup(try),
+ DEVICE_TYPE_TAPE, TRUE, mediaInitTape, mediaGetTape, mediaShutdownTape, NULL);
+ if (isDebug())
+ msgDebug("Found a TAPE device for %s\n", try);
+ }
+ break;
+
+ case DEVICE_TYPE_DISK:
+ fd = deviceTry(device_names[i], try, j);
+ if (fd >= 0 && RunningAsInit) {
+ dev_t d;
+ mode_t m;
+ int s, fail;
+ char unit[80], slice[80];
+
+ close(fd);
+ /* Make associated slice entries */
+ for (s = 1; s < 8; s++) {
+ snprintf(unit, sizeof unit, device_names[i].name, j);
+ snprintf(slice, sizeof slice, "/dev/%ss%d", unit, s);
+ d = makedev(device_names[i].major, device_names[i].minor +
+ (j * device_names[i].delta) + (s * SLICE_DELTA));
+ m = 0640 | S_IFCHR;
+ fail = mknod(slice, m, d);
+ fd = open(slice, O_RDONLY);
+ if (fd >= 0)
+ close(fd);
+ else if (!fail)
+ (void)unlink(slice);
+ }
+ }
+ break;
+
+ case DEVICE_TYPE_FLOPPY:
+ fd = deviceTry(device_names[i], try, j);
+ if (fd >= 0) {
+ char n[BUFSIZ];
+
+ close(fd);
+ snprintf(n, sizeof n, device_names[i].name, j);
+ deviceRegister(strdup(n), device_names[i].description, strdup(try),
+ DEVICE_TYPE_FLOPPY, TRUE, mediaInitFloppy, mediaGetFloppy,
+ mediaShutdownFloppy, NULL);
+ if (isDebug())
+ msgDebug("Found a floppy device for %s\n", try);
+ }
+ break;
+
+ case DEVICE_TYPE_NETWORK:
+ fd = deviceTry(device_names[i], try, j);
+ /* The only network devices that you can open this way are serial ones */
+ if (fd >= 0) {
+ char *newdesc, *cp;
+
+ close(fd);
+ cp = device_names[i].description;
+ /* Serial devices get a slip and ppp device each, if supported */
+ newdesc = safe_malloc(strlen(cp) + 40);
+ sprintf(newdesc, cp, "SLIP interface", try, j + 1);
+ deviceRegister("sl0", newdesc, strdup(try), DEVICE_TYPE_NETWORK, TRUE, mediaInitNetwork,
+ NULL, mediaShutdownNetwork, NULL);
+ msgDebug("Add mapping for %s to sl0\n", try);
+ newdesc = safe_malloc(strlen(cp) + 50);
+ sprintf(newdesc, cp, "PPP interface", try, j + 1);
+ deviceRegister("ppp0", newdesc, strdup(try), DEVICE_TYPE_NETWORK, TRUE, mediaInitNetwork,
+ NULL, mediaShutdownNetwork, NULL);
+ if (isDebug())
+ msgDebug("Add mapping for %s to ppp0\n", try);
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ /* Finally, go get the disks and look for DOS partitions to register */
+ if ((names = Disk_Names()) != NULL) {
+ int i;
+
+ for (i = 0; names[i]; i++) {
+ Chunk *c1;
+ Disk *d;
+
+ /* Ignore memory disks */
+ if (!strncmp(names[i], "md", 2))
+ continue;
+
+ d = Open_Disk(names[i]);
+ if (!d) {
+ msgDebug("Unable to open disk %s", names[i]);
+ continue;
+ }
+
+ deviceRegister(names[i], names[i], d->name, DEVICE_TYPE_DISK, FALSE,
+ dummyInit, dummyGet, dummyShutdown, d);
+ if (isDebug())
+ msgDebug("Found a disk device named %s\n", names[i]);
+
+ /* Look for existing DOS partitions to register as "DOS media devices" */
+ for (c1 = d->chunks->part; c1; c1 = c1->next) {
+ if (c1->type == fat || c1->type == extended) {
+ Device *dev;
+ char devname[80];
+
+ /* Got one! */
+ snprintf(devname, sizeof devname, "/dev/%s", c1->name);
+ dev = deviceRegister(c1->name, c1->name, strdup(devname), DEVICE_TYPE_DOS, TRUE,
+ mediaInitDOS, mediaGetDOS, mediaShutdownDOS, NULL);
+ dev->private = c1;
+ if (isDebug())
+ msgDebug("Found a DOS partition %s on drive %s\n", c1->name, d->name);
+ }
+ }
+ }
+ free(names);
+ }
+ dialog_clear_norefresh();
+}
+
+/* Rescan all devices, after closing previous set - convenience function */
+void
+deviceRescan(void)
+{
+ deviceReset();
+ deviceGetAll();
+}
+
+/*
+ * Find all devices that match the criteria, allowing "wildcarding" as well
+ * by allowing NULL or ANY values to match all. The array returned is static
+ * and may be used until the next invocation of deviceFind().
+ */
+Device **
+deviceFind(char *name, DeviceType class)
+{
+ static Device *found[DEV_MAX];
+ int i, j;
+
+ j = 0;
+ for (i = 0; i < numDevs; i++) {
+ if ((!name || !strcmp(Devices[i]->name, name))
+ && (class == DEVICE_TYPE_ANY || class == Devices[i]->type))
+ found[j++] = Devices[i];
+ }
+ found[j] = NULL;
+ return j ? found : NULL;
+}
+
+Device **
+deviceFindDescr(char *name, char *desc, DeviceType class)
+{
+ static Device *found[DEV_MAX];
+ int i, j;
+
+ j = 0;
+ for (i = 0; i < numDevs; i++) {
+ if ((!name || !strcmp(Devices[i]->name, name)) &&
+ (!desc || !strcmp(Devices[i]->description, desc)) &&
+ (class == DEVICE_TYPE_ANY || class == Devices[i]->type))
+ found[j++] = Devices[i];
+ }
+ found[j] = NULL;
+ return j ? found : NULL;
+}
+
+int
+deviceCount(Device **devs)
+{
+ int i;
+
+ if (!devs)
+ return 0;
+ for (i = 0; devs[i]; i++);
+ return i;
+}
+
+/*
+ * Create a menu listing all the devices of a certain type in the system.
+ * The passed-in menu is expected to be a "prototype" from which the new
+ * menu is cloned.
+ */
+DMenu *
+deviceCreateMenu(DMenu *menu, DeviceType type, int (*hook)(dialogMenuItem *d), int (*check)(dialogMenuItem *d))
+{
+ Device **devs;
+ int numdevs;
+ DMenu *tmp = NULL;
+ int i, j;
+
+ devs = deviceFind(NULL, type);
+ numdevs = deviceCount(devs);
+ if (!numdevs)
+ return NULL;
+ tmp = (DMenu *)safe_malloc(sizeof(DMenu) + (sizeof(dialogMenuItem) * (numdevs + 1)));
+ bcopy(menu, tmp, sizeof(DMenu));
+ for (i = 0; devs[i]; i++) {
+ tmp->items[i].prompt = devs[i]->name;
+ for (j = 0; j < numDevs; j++) {
+ if (devs[i] == Devices[j]) {
+ tmp->items[i].title = Devices[j]->description;
+ break;
+ }
+ }
+ if (j == numDevs)
+ tmp->items[i].title = "<unknown device type>";
+ tmp->items[i].fire = hook;
+ tmp->items[i].checked = check;
+ }
+ tmp->items[i].title = NULL;
+ return tmp;
+}
diff --git a/usr.sbin/sade/disks.c b/usr.sbin/sade/disks.c
new file mode 100644
index 0000000..60091d3
--- /dev/null
+++ b/usr.sbin/sade/disks.c
@@ -0,0 +1,964 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <ctype.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/disklabel.h>
+
+enum size_units_t { UNIT_BLOCKS, UNIT_KILO, UNIT_MEG, UNIT_SIZE };
+
+#ifdef PC98
+#define SUBTYPE_FREEBSD 50324
+#define SUBTYPE_FAT 37218
+#else
+#define SUBTYPE_FREEBSD 165
+#define SUBTYPE_FAT 6
+#endif
+
+/* Where we start displaying chunk information on the screen */
+#define CHUNK_START_ROW 5
+
+/* Where we keep track of MBR chunks */
+static struct chunk *chunk_info[16];
+static int current_chunk;
+
+static void diskPartitionNonInteractive(Device *dev);
+static u_char * bootalloc(char *name, size_t *size);
+
+static void
+record_chunks(Disk *d)
+{
+ struct chunk *c1 = NULL;
+ int i = 0;
+ 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 u)
+{
+ int row;
+ int i;
+ int sz;
+ char *szstr;
+
+ szstr = (u == UNIT_MEG ? "MB" : (u == UNIT_KILO ? "KB" : "ST"));
+
+ for (i = Total = 0; chunk_info[i]; i++)
+ Total += chunk_info[i]->size;
+#ifndef PC98
+ if (d->bios_cyl > 65536 || d->bios_hd > 256 || d->bios_sect >= 64) {
+ dialog_clear_norefresh();
+ msgConfirm("WARNING: A geometry of %lu/%lu/%lu for %s is incorrect. Using\n"
+ "a more likely geometry. If this geometry is incorrect or you\n"
+ "are unsure as to whether or not it's correct, please consult\n"
+ "the Hardware Guide in the Documentation submenu or use the\n"
+ "(G)eometry command to change it now.\n\n"
+ "Remember: you need to enter whatever your BIOS thinks the\n"
+ "geometry is! For IDE, it's what you were told in the BIOS\n"
+ "setup. For SCSI, it's the translation mode your controller is\n"
+ "using. Do NOT use a ``physical geometry''.",
+ d->bios_cyl, d->bios_hd, d->bios_sect, d->name);
+ Sanitize_Bios_Geom(d);
+ }
+#endif
+ 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 (%luMB)",
+ d->bios_cyl, d->bios_hd, d->bios_sect,
+ d->bios_cyl * d->bios_hd * d->bios_sect,
+ d->bios_cyl * d->bios_hd * d->bios_sect / (1024/512) / 1024);
+ mvprintw(3, 0, "%6s %10s(%s) %10s %8s %6s %10s %8s %8s",
+ "Offset", "Size", szstr, "End", "Name", "PType", "Desc",
+ "Subtype", "Flags");
+ for (i = 0, row = CHUNK_START_ROW; chunk_info[i]; i++, row++) {
+ switch(u) {
+ default: /* fall thru */
+ case UNIT_BLOCKS:
+ sz = chunk_info[i]->size;
+ break;
+ case UNIT_KILO:
+ sz = chunk_info[i]->size / (1024/512);
+ break;
+ case UNIT_MEG:
+ sz = chunk_info[i]->size / (1024/512) / 1024;
+ break;
+ }
+ if (i == current_chunk)
+ attrset(ATTR_SELECTED);
+ mvprintw(row, 0, "%10ld %10lu %10lu %8s %6d %10s %8d\t%-6s",
+ chunk_info[i]->offset, sz,
+ chunk_info[i]->end, chunk_info[i]->name,
+ chunk_info[i]->type,
+ slice_type_name(chunk_info[i]->type, chunk_info[i]->subtype),
+ chunk_info[i]->subtype, ShowChunkFlags(chunk_info[i]));
+ if (i == current_chunk)
+ attrset(A_NORMAL);
+ }
+}
+
+static void
+print_command_summary()
+{
+ mvprintw(14, 0, "The following commands are supported (in upper or lower case):");
+ mvprintw(16, 0, "A = Use Entire Disk G = set Drive Geometry C = Create Slice F = `DD' mode");
+ mvprintw(17, 0, "D = Delete Slice Z = Toggle Size Units S = Set Bootable | = Wizard m.");
+ mvprintw(18, 0, "T = Change Type U = Undo All Changes Q = Finish");
+ if (!RunningAsInit)
+ mvprintw(18, 47, "W = Write Changes");
+ mvprintw(21, 0, "Use F1 or ? to get more help, arrow keys to select.");
+ move(0, 0);
+}
+
+#ifdef PC98
+static void
+getBootMgr(char *dname, u_char **bootipl, size_t *bootipl_size,
+ u_char **bootmenu, size_t *bootmenu_size)
+{
+ static u_char *boot0;
+ static size_t boot0_size;
+ static u_char *boot05;
+ static size_t boot05_size;
+
+ char str[80];
+ char *cp;
+ int i = 0;
+
+ cp = variable_get(VAR_BOOTMGR);
+ if (!cp) {
+ /* Figure out what kind of 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
+ BootMgr = 2;
+ }
+ if (cp || i) {
+ switch (BootMgr) {
+ case 0:
+ if (!boot0) boot0 = bootalloc("boot0", &boot0_size);
+ *bootipl = boot0;
+ *bootipl_size = boot0_size;
+ if (!boot05) boot05 = bootalloc("boot0.5", &boot05_size);
+ *bootmenu = boot05;
+ *bootmenu_size = boot05_size;
+ return;
+ case 2:
+ default:
+ break;
+ }
+ }
+ *bootipl = NULL;
+ *bootipl_size = 0;
+ *bootmenu = NULL;
+ *bootmenu_size = 0;
+}
+#else
+static void
+getBootMgr(char *dname, u_char **bootCode, size_t *bootCodeSize)
+{
+#ifndef __alpha__ /* only meaningful on x86 */
+ static u_char *mbr, *boot0;
+ static size_t mbr_size, boot0_size;
+ char str[80];
+ char *cp;
+ int i = 0;
+
+ cp = variable_get(VAR_BOOTMGR);
+ if (!cp) {
+ /* Figure out what kind of MBR the user wants */
+ sprintf(str, "Install Boot Manager for drive %s?", dname);
+ MenuMBRType.title = str;
+ i = dmenuOpenSimple(&MenuMBRType, FALSE);
+ }
+ else {
+ if (!strncmp(cp, "boot", 4))
+ BootMgr = 0;
+ else if (!strcmp(cp, "standard"))
+ BootMgr = 1;
+ else
+ BootMgr = 2;
+ }
+ if (cp || i) {
+ switch (BootMgr) {
+ case 0:
+ if (!boot0) boot0 = bootalloc("boot0", &boot0_size);
+ *bootCode = boot0;
+ *bootCodeSize = boot0_size;
+ return;
+ case 1:
+ if (!mbr) mbr = bootalloc("mbr", &mbr_size);
+ *bootCode = mbr;
+ *bootCodeSize = mbr_size;
+ return;
+ case 2:
+ default:
+ break;
+ }
+ }
+#endif
+ *bootCode = NULL;
+ *bootCodeSize = 0;
+}
+#endif
+
+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;
+#ifdef PC98
+ u_char *bootipl;
+ size_t bootipl_size;
+ u_char *bootmenu;
+ size_t bootmenu_size;
+#else
+ u_char *mbrContents;
+ size_t mbrSize;
+#endif
+ WINDOW *w = savescr();
+ Disk *d = (Disk *)dev->private;
+ int size_unit;
+
+ size_unit = UNIT_BLOCKS;
+ chunking = TRUE;
+ keypad(stdscr, TRUE);
+
+ /* Flush both the dialog and curses library views of the screen
+ since we don't always know who called us */
+ dialog_clear_norefresh(), clear();
+ current_chunk = 0;
+
+ /* Set up the chunk array */
+ record_chunks(d);
+
+ while (chunking) {
+ char *val, geometry[80];
+
+ /* Now print our overall state */
+ if (d)
+ print_chunks(d, size_unit);
+ print_command_summary();
+ if (msg) {
+ attrset(title_attr); mvprintw(23, 0, msg); attrset(A_NORMAL);
+ beep();
+ msg = NULL;
+ }
+ else {
+ move(23, 0);
+ clrtoeol();
+ }
+
+ /* Get command character */
+ key = getch();
+ switch (toupper(key)) {
+ case '\014': /* ^L (redraw) */
+ clear();
+ msg = NULL;
+ break;
+
+ case '\020': /* ^P */
+ case KEY_UP:
+ case '-':
+ if (current_chunk != 0)
+ --current_chunk;
+ break;
+
+ case '\016': /* ^N */
+ case KEY_DOWN:
+ case '+':
+ case '\r':
+ case '\n':
+ if (chunk_info[current_chunk + 1])
+ ++current_chunk;
+ break;
+
+ case KEY_HOME:
+ current_chunk = 0;
+ break;
+
+ case KEY_END:
+ while (chunk_info[current_chunk + 1])
+ ++current_chunk;
+ break;
+
+ case KEY_F(1):
+ case '?':
+ systemDisplayHelp("slice");
+ clear();
+ break;
+
+ case 'A':
+ case 'F': /* Undocumented magic Dangerously Dedicated mode */
+#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 if (toupper(key) == 'A')
+ rv = 0;
+ else {
+ rv = msgYesNo("Do you want to do this with a true partition entry\n"
+ "so as to remain cooperative with any future possible\n"
+ "operating systems on the drive(s)?\n"
+ "(See also the section about ``dangerously dedicated''\n"
+ "disks in the FreeBSD FAQ.)");
+ if (rv == -1)
+ rv = 0;
+ }
+#endif
+ All_FreeBSD(d, rv);
+ variable_set2(DISK_PARTITIONED, "yes", 0);
+ record_chunks(d);
+ clear();
+ break;
+
+ case 'C':
+ if (chunk_info[current_chunk]->type != unused)
+ msg = "Slice in use, delete it first or move to an unused one.";
+ else {
+ char *val, tmp[20], *cp;
+ int size;
+#ifdef PC98
+ char name[16];
+
+ snprintf(name, 16, "%s", "FreeBSD");
+ val = msgGetInput(name,
+ "Please specify the name for new FreeBSD slice.");
+ if (val)
+ strncpy(name, val, 16);
+#else
+ int subtype;
+ chunk_e partitiontype;
+#endif
+ 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;
+ else if (*cp && toupper(*cp) == 'G')
+ size *= ONE_GIG;
+#ifdef PC98
+ Create_Chunk(d, chunk_info[current_chunk]->offset, size,
+ freebsd, 3,
+ (chunk_info[current_chunk]->flags & CHUNK_ALIGN),
+ name);
+ variable_set2(DISK_PARTITIONED, "yes", 0);
+ record_chunks(d);
+#else
+ sprintf(tmp, "%d", SUBTYPE_FREEBSD);
+ val = msgGetInput(tmp, "Enter type of partition to create:\n\n"
+ "Pressing Enter will choose the default, a native FreeBSD\n"
+ "slice (type 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 == SUBTYPE_FREEBSD)
+ partitiontype = freebsd;
+ else if (subtype == SUBTYPE_FAT)
+ 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);
+ }
+#endif /* PC98 */
+ }
+ clear();
+ }
+ break;
+
+ case KEY_DC:
+ case 'D':
+ if (chunk_info[current_chunk]->type == unused)
+ msg = "Slice is already unused!";
+ else {
+ Delete_Chunk(d, chunk_info[current_chunk]);
+ variable_set2(DISK_PARTITIONED, "yes", 0);
+ record_chunks(d);
+ }
+ break;
+
+ case 'T':
+ if (chunk_info[current_chunk]->type == unused)
+ msg = "Slice is currently unused (use create instead)";
+ else {
+ char *val, tmp[20];
+ int subtype;
+ chunk_e partitiontype;
+
+ sprintf(tmp, "%d", SUBTYPE_FREEBSD);
+#ifdef PC98
+ val = msgGetInput(tmp, "New partition type:\n\n"
+ "Pressing Enter will choose the default, a native FreeBSD\n"
+ "slice (type 50324). Other popular values are 37218 for\n"
+ "DOS FAT 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.");
+#else
+ 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.");
+#endif /* PC98 */
+ if (val && (subtype = strtol(val, NULL, 0)) > 0) {
+ if (subtype == SUBTYPE_FREEBSD)
+ partitiontype = freebsd;
+ else if (subtype == SUBTYPE_FAT)
+ partitiontype = fat;
+ else
+ partitiontype = unknown;
+ chunk_info[current_chunk]->type = partitiontype;
+ chunk_info[current_chunk]->subtype = subtype;
+ }
+ }
+ break;
+
+ case 'G':
+ snprintf(geometry, 80, "%lu/%lu/%lu", d->bios_cyl, d->bios_hd, d->bios_sect);
+ val = msgGetInput(geometry, "Please specify the new geometry in cyl/hd/sect format.\n"
+ "Don't forget to use the two slash (/) separator characters!\n"
+ "It's not possible to parse the field without them.");
+ if (val) {
+ long nc, nh, ns;
+ nc = strtol(val, &val, 0);
+ nh = strtol(val + 1, &val, 0);
+ ns = strtol(val + 1, 0, 0);
+ Set_Bios_Geom(d, nc, nh, ns);
+ }
+ clear();
+ break;
+
+ case 'S':
+ /* Set Bootable */
+ chunk_info[current_chunk]->flags |= CHUNK_ACTIVE;
+ break;
+
+ case 'U':
+ if (!variable_cmp(DISK_LABELLED, "written")) {
+ msgConfirm("You've already written this information out - you\n"
+ "can't undo it.");
+ }
+ else if (!msgNoYes("Are you SURE you want to Undo everything?")) {
+ char cp[BUFSIZ];
+
+ sstrncpy(cp, d->name, sizeof cp);
+ Free_Disk(dev->private);
+ d = Open_Disk(cp);
+ if (!d)
+ msgConfirm("Can't reopen disk %s! Internal state is probably corrupted", cp);
+ dev->private = d;
+ variable_unset(DISK_PARTITIONED);
+ variable_unset(DISK_LABELLED);
+ if (d)
+ record_chunks(d);
+ }
+ clear();
+ break;
+
+ case 'W':
+ if (!msgNoYes("WARNING: This should only be used when modifying an EXISTING\n"
+ "installation. If you are installing FreeBSD for the first time\n"
+ "then you should simply type Q when you're finished here and your\n"
+ "changes will be committed in one batch automatically at the end of\n"
+ "these questions. If you're adding a disk, you should NOT write\n"
+ "from this screen, you should do it from the label editor.\n\n"
+ "Are you absolutely sure you want to do this now?")) {
+ variable_set2(DISK_PARTITIONED, "yes", 0);
+
+ /*
+ * Don't trash the MBR if the first (and therefore only) chunk
+ * is marked for a truly dedicated disk (i.e., the disklabel
+ * starts at sector 0), even in cases where the user has
+ * requested booteasy or a "standard" MBR -- both would be
+ * fatal in this case.
+ */
+ /*
+ * Don't offer to update the MBR on this disk if the first
+ * "real" chunk looks like a FreeBSD "all disk" partition,
+ * or the disk is entirely FreeBSD.
+ */
+#ifdef PC98
+ if ((d->chunks->part->type != freebsd) ||
+ (d->chunks->part->offset > 1))
+ getBootMgr(d->name, &bootipl, &bootipl_size,
+ &bootmenu, &bootmenu_size);
+ else {
+ bootipl = NULL;
+ bootipl_size = 0;
+ bootmenu = NULL;
+ bootmenu_size = 0;
+ }
+ Set_Boot_Mgr(d, bootipl, bootipl_size, bootmenu, bootmenu_size);
+#else
+ if ((d->chunks->part->type != freebsd) ||
+ (d->chunks->part->offset > 1))
+ getBootMgr(d->name, &mbrContents, &mbrSize);
+ else {
+ mbrContents = NULL;
+ mbrSize = 0;
+ }
+ Set_Boot_Mgr(d, mbrContents, mbrSize);
+#endif
+
+ if (DITEM_STATUS(diskPartitionWrite(NULL)) != DITEM_SUCCESS)
+ msgConfirm("Disk partition write returned an error status!");
+ else
+ msgConfirm("Wrote FDISK partition information out successfully.");
+ }
+ clear();
+ break;
+
+ case '|':
+ if (!msgNoYes("Are you SURE you want to go into Wizard mode?\n"
+ "No seat belts whatsoever are provided!")) {
+ clear();
+ refresh();
+ slice_wizard(d);
+ variable_set2(DISK_PARTITIONED, "yes", 0);
+ record_chunks(d);
+ }
+ else
+ msg = "Wise choice!";
+ clear();
+ break;
+
+ case '\033': /* ESC */
+ case 'Q':
+ chunking = FALSE;
+ /*
+ * Don't trash the MBR if the first (and therefore only) chunk
+ * is marked for a truly dedicated disk (i.e., the disklabel
+ * starts at sector 0), even in cases where the user has requested
+ * booteasy or a "standard" MBR -- both would be fatal in this case.
+ */
+ /*
+ * Don't offer to update the MBR on this disk if the first "real"
+ * chunk looks like a FreeBSD "all disk" partition, or the disk is
+ * entirely FreeBSD.
+ */
+ if ((d->chunks->part->type != freebsd) ||
+ (d->chunks->part->offset > 1)) {
+ if (variable_cmp(DISK_PARTITIONED, "written")) {
+#ifdef PC98
+ getBootMgr(d->name, &bootipl, &bootipl_size,
+ &bootmenu, &bootmenu_size);
+ if (bootipl != NULL && bootmenu != NULL)
+ Set_Boot_Mgr(d, bootipl, bootipl_size,
+ bootmenu, bootmenu_size);
+#else
+ getBootMgr(d->name, &mbrContents, &mbrSize);
+ if (mbrContents != NULL)
+ Set_Boot_Mgr(d, mbrContents, mbrSize);
+#endif
+ }
+ }
+ break;
+
+ case 'Z':
+ size_unit = (size_unit + 1) % UNIT_SIZE;
+ break;
+
+ default:
+ beep();
+ msg = "Type F1 or ? for help";
+ break;
+ }
+ }
+ p = CheckRules(d);
+ if (p) {
+ char buf[FILENAME_MAX];
+
+ use_helpline("Press F1 to read more about disk slices.");
+ use_helpfile(systemHelpFile("partition", buf));
+ if (!variable_get(VAR_NO_WARN))
+ dialog_mesgbox("Disk slicing warning:", p, -1, -1);
+ free(p);
+ }
+ restorescr(w);
+}
+
+static u_char *
+bootalloc(char *name, size_t *size)
+{
+ char buf[FILENAME_MAX];
+ struct stat sb;
+
+ snprintf(buf, sizeof buf, "/boot/%s", name);
+ if (stat(buf, &sb) != -1) {
+ int fd;
+
+ fd = open(buf, O_RDONLY);
+ if (fd != -1) {
+ u_char *cp;
+
+ cp = malloc(sb.st_size);
+ if (read(fd, cp, sb.st_size) != sb.st_size) {
+ free(cp);
+ close(fd);
+ msgDebug("bootalloc: couldn't read %ld bytes from %s\n", (long)sb.st_size, buf);
+ return NULL;
+ }
+ close(fd);
+ if (size != NULL)
+ *size = sb.st_size;
+ return cp;
+ }
+ msgDebug("bootalloc: couldn't open %s\n", buf);
+ }
+ else
+ msgDebug("bootalloc: can't stat %s\n", buf);
+ return NULL;
+}
+
+static int
+partitionHook(dialogMenuItem *selected)
+{
+ Device **devs = NULL;
+
+ devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
+ if (!devs) {
+ msgConfirm("Unable to find disk %s!", selected->prompt);
+ return DITEM_FAILURE;
+ }
+ /* Toggle enabled status? */
+ if (!devs[0]->enabled) {
+ devs[0]->enabled = TRUE;
+ diskPartition(devs[0]);
+ }
+ else
+ devs[0]->enabled = FALSE;
+ return DITEM_SUCCESS;
+}
+
+static int
+partitionCheck(dialogMenuItem *selected)
+{
+ Device **devs = NULL;
+
+ devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
+ if (!devs || devs[0]->enabled == FALSE)
+ return FALSE;
+ return TRUE;
+}
+
+int
+diskPartitionEditor(dialogMenuItem *self)
+{
+ DMenu *menu;
+ Device **devs;
+ int i, cnt, devcnt;
+
+ cnt = diskGetSelectCount(&devs);
+ devcnt = deviceCount(devs);
+ if (cnt == -1) {
+ msgConfirm("No disks found! Please verify that your disk controller is being\n"
+ "properly probed at boot time. See the Hardware Guide on the\n"
+ "Documentation menu for clues on diagnosing this type of problem.");
+ return DITEM_FAILURE;
+ }
+ else if (cnt) {
+ /* Some are already selected */
+ for (i = 0; i < devcnt; i++) {
+ if (devs[i]->enabled) {
+ if (variable_get(VAR_NONINTERACTIVE) &&
+ !variable_get(VAR_DISKINTERACTIVE))
+ diskPartitionNonInteractive(devs[i]);
+ else
+ diskPartition(devs[i]);
+ }
+ }
+ }
+ else {
+ /* No disks are selected, fall-back case now */
+ if (devcnt == 1) {
+ devs[0]->enabled = TRUE;
+ if (variable_get(VAR_NONINTERACTIVE) &&
+ !variable_get(VAR_DISKINTERACTIVE))
+ diskPartitionNonInteractive(devs[0]);
+ else
+ diskPartition(devs[0]);
+ return DITEM_SUCCESS;
+ }
+ else {
+ menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, partitionHook, partitionCheck);
+ if (!menu) {
+ msgConfirm("No devices suitable for installation found!\n\n"
+ "Please verify that your disk controller (and attached drives)\n"
+ "were detected properly. This can be done by pressing the\n"
+ "[Scroll Lock] key and using the Arrow keys to move back to\n"
+ "the boot messages. Press [Scroll Lock] again to return.");
+ return DITEM_FAILURE;
+ }
+ else {
+ i = dmenuOpenSimple(menu, FALSE) ? DITEM_SUCCESS : DITEM_FAILURE;
+ free(menu);
+ }
+ return i;
+ }
+ }
+ return DITEM_SUCCESS;
+}
+
+int
+diskPartitionWrite(dialogMenuItem *self)
+{
+ Device **devs;
+ int i;
+
+ if (!variable_cmp(DISK_PARTITIONED, "written"))
+ return DITEM_SUCCESS;
+
+ devs = deviceFind(NULL, DEVICE_TYPE_DISK);
+ if (!devs) {
+ msgConfirm("Unable to find any disks to write to??");
+ return DITEM_FAILURE;
+ }
+ if (isDebug())
+ msgDebug("diskPartitionWrite: Examining %d devices\n", deviceCount(devs));
+ for (i = 0; devs[i]; i++) {
+ Disk *d = (Disk *)devs[i]->private;
+ static u_char *boot1;
+#ifndef __alpha__
+ static u_char *boot2;
+#endif
+
+ if (!devs[i]->enabled)
+ continue;
+
+#ifdef __alpha__
+ if (!boot1) boot1 = bootalloc("boot1", NULL);
+ Set_Boot_Blocks(d, boot1, NULL);
+#else
+ if (!boot1) boot1 = bootalloc("boot1", NULL);
+ if (!boot2) boot2 = bootalloc("boot2", NULL);
+ 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;
+ }
+ }
+ /* Now it's not "yes", but "written" */
+ variable_set2(DISK_PARTITIONED, "written", 0);
+ return DITEM_SUCCESS | DITEM_RESTORE;
+}
+
+/* Partition a disk based wholly on which variables are set */
+static void
+diskPartitionNonInteractive(Device *dev)
+{
+ char *cp;
+ int i, sz, all_disk = 0;
+#ifdef PC98
+ u_char *bootipl;
+ size_t bootipl_size;
+ u_char *bootmenu;
+ size_t bootmenu_size;
+#else
+ u_char *mbrContents;
+ size_t mbrSize;
+#endif
+ Disk *d = (Disk *)dev->private;
+
+ record_chunks(d);
+ cp = variable_get(VAR_GEOMETRY);
+ if (cp) {
+ msgDebug("Setting geometry from script to: %s\n", cp);
+ d->bios_cyl = strtol(cp, &cp, 0);
+ d->bios_hd = strtol(cp + 1, &cp, 0);
+ d->bios_sect = strtol(cp + 1, 0, 0);
+ }
+
+ cp = variable_get(VAR_PARTITION);
+ if (cp) {
+ if (!strcmp(cp, "free")) {
+ /* Do free disk space case */
+ for (i = 0; chunk_info[i]; i++) {
+ /* If a chunk is at least 10MB in size, use it. */
+ if (chunk_info[i]->type == unused && chunk_info[i]->size > (10 * ONE_MEG)) {
+#ifdef PC98
+ Create_Chunk(d, chunk_info[i]->offset, chunk_info[i]->size,
+ freebsd, 3,
+ (chunk_info[i]->flags & CHUNK_ALIGN),
+ "FreeBSD");
+#else
+ Create_Chunk(d, chunk_info[i]->offset, chunk_info[i]->size,
+ freebsd, 3,
+ (chunk_info[i]->flags & CHUNK_ALIGN));
+#endif
+ variable_set2(DISK_PARTITIONED, "yes", 0);
+ break;
+ }
+ }
+ if (!chunk_info[i]) {
+ msgConfirm("Unable to find any free space on this disk!");
+ return;
+ }
+ }
+ else if (!strcmp(cp, "all")) {
+ /* Do all disk space case */
+ msgDebug("Warning: Devoting all of disk %s to FreeBSD.\n", d->name);
+
+ All_FreeBSD(d, FALSE);
+ }
+ else if (!strcmp(cp, "exclusive")) {
+ /* Do really-all-the-disk-space case */
+ msgDebug("Warning: Devoting all of disk %s to FreeBSD.\n", d->name);
+
+ All_FreeBSD(d, all_disk = TRUE);
+ }
+ else if ((sz = strtol(cp, &cp, 0))) {
+ /* Look for sz bytes free */
+ if (*cp && toupper(*cp) == 'M')
+ sz *= ONE_MEG;
+ else if (*cp && toupper(*cp) == 'G')
+ sz *= ONE_GIG;
+ for (i = 0; chunk_info[i]; i++) {
+ /* If a chunk is at least sz MB, use it. */
+ if (chunk_info[i]->type == unused && chunk_info[i]->size >= sz) {
+#ifdef PC98
+ Create_Chunk(d, chunk_info[i]->offset, sz, freebsd, 3,
+ (chunk_info[i]->flags & CHUNK_ALIGN),
+ "FreeBSD");
+#else
+ Create_Chunk(d, chunk_info[i]->offset, sz, freebsd, 3,
+ (chunk_info[i]->flags & CHUNK_ALIGN));
+#endif
+ variable_set2(DISK_PARTITIONED, "yes", 0);
+ break;
+ }
+ }
+ if (!chunk_info[i]) {
+ 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]) {
+ msgConfirm("Unable to find any existing FreeBSD partitions on this disk!");
+ return;
+ }
+ }
+ else {
+ msgConfirm("`%s' is an invalid value for %s - is config file valid?", cp, VAR_PARTITION);
+ return;
+ }
+ if (!all_disk) {
+#ifdef PC98
+ getBootMgr(d->name, &bootipl, &bootipl_size,
+ &bootmenu, &bootmenu_size);
+ Set_Boot_Mgr(d, bootipl, bootipl_size, bootmenu, bootmenu_size);
+#else
+ getBootMgr(d->name, &mbrContents, &mbrSize);
+ Set_Boot_Mgr(d, mbrContents, mbrSize);
+#endif
+ }
+ variable_set2(DISK_PARTITIONED, "yes", 0);
+ }
+}
+
diff --git a/usr.sbin/sade/dispatch.c b/usr.sbin/sade/dispatch.c
new file mode 100644
index 0000000..357cd60
--- /dev/null
+++ b/usr.sbin/sade/dispatch.c
@@ -0,0 +1,444 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <ctype.h>
+#include <errno.h>
+#include <sys/signal.h>
+#include <sys/fcntl.h>
+
+#include "list.h"
+
+static int dispatch_shutdown(dialogMenuItem *unused);
+static int dispatch_systemExecute(dialogMenuItem *unused);
+static int dispatch_msgConfirm(dialogMenuItem *unused);
+static int dispatch_mediaClose(dialogMenuItem *unused);
+
+static struct _word {
+ char *name;
+ int (*handler)(dialogMenuItem *self);
+} resWords[] = {
+ { "configAnonFTP", configAnonFTP },
+ { "configRouter", configRouter },
+ { "configInetd", configInetd },
+ { "configNFSServer", configNFSServer },
+ { "configNTP", configNTP },
+ { "configPCNFSD", configPCNFSD },
+ { "configPackages", configPackages },
+ { "configUsers", configUsers },
+ { "configXSetup", configXSetup },
+ { "configXDesktop", configXDesktop },
+ { "diskPartitionEditor", diskPartitionEditor },
+ { "diskPartitionWrite", diskPartitionWrite },
+ { "diskLabelEditor", diskLabelEditor },
+ { "diskLabelCommit", diskLabelCommit },
+ { "distReset", distReset },
+ { "distSetCustom", distSetCustom },
+ { "distUnsetCustom", distUnsetCustom },
+ { "distSetDeveloper", distSetDeveloper },
+ { "distSetXDeveloper", distSetXDeveloper },
+ { "distSetKernDeveloper", distSetKernDeveloper },
+ { "distSetUser", distSetUser },
+ { "distSetXUser", distSetXUser },
+ { "distSetMinimum", distSetMinimum },
+ { "distSetEverything", distSetEverything },
+ { "distSetSrc", distSetSrc },
+ { "distSetXF86", distSetXF86 },
+ { "distExtractAll", distExtractAll },
+ { "docBrowser", docBrowser },
+ { "docShowDocument", docShowDocument },
+ { "installCommit", installCommit },
+ { "installExpress", installExpress },
+ { "installStandard", installStandard },
+ { "installUpgrade", installUpgrade },
+ { "installFixupBin", installFixupBin },
+#ifndef X_AS_PKG
+ { "installFixupXFree", installFixupXFree },
+#endif
+ { "installFixitHoloShell", installFixitHoloShell },
+ { "installFixitCDROM", installFixitCDROM },
+ { "installFixitFloppy", installFixitFloppy },
+ { "installFilesystems", installFilesystems },
+ { "installVarDefaults", installVarDefaults },
+ { "loadConfig", dispatch_load_file },
+ { "loadFloppyConfig", dispatch_load_floppy },
+ { "mediaClose", dispatch_mediaClose },
+ { "mediaSetCDROM", mediaSetCDROM },
+ { "mediaSetFloppy", mediaSetFloppy },
+ { "mediaSetDOS", mediaSetDOS },
+ { "mediaSetTape", mediaSetTape },
+ { "mediaSetFTP", mediaSetFTP },
+ { "mediaSetFTPActive", mediaSetFTPActive },
+ { "mediaSetFTPPassive", mediaSetFTPPassive },
+ { "mediaSetHTTP", mediaSetHTTP },
+ { "mediaSetUFS", mediaSetUFS },
+ { "mediaSetNFS", mediaSetNFS },
+ { "mediaSetFTPUserPass", mediaSetFTPUserPass },
+ { "mediaSetCPIOVerbosity", mediaSetCPIOVerbosity },
+ { "mediaGetType", mediaGetType },
+ { "msgConfirm", dispatch_msgConfirm },
+ { "optionsEditor", optionsEditor },
+ { "packageAdd", packageAdd },
+ { "addGroup", userAddGroup },
+ { "addUser", userAddUser },
+ { "shutdown", dispatch_shutdown },
+ { "system", dispatch_systemExecute },
+ { "dumpVariables", dump_variables },
+ { "tcpMenuSelect", tcpMenuSelect },
+ { NULL, NULL },
+};
+
+/*
+ * Helper routines for buffering data.
+ *
+ * We read an entire configuration into memory before executing it
+ * so that we are truely standalone and can do things like nuke the
+ * file or disk we're working on.
+ */
+
+typedef struct command_buffer_ {
+ qelement queue;
+ char * string;
+} command_buffer;
+
+static void
+dispatch_free_command(command_buffer *item)
+{
+ REMQUE(item);
+ free(item->string);
+ free(item);
+}
+
+static void
+dispatch_free_all(qelement *head)
+{
+ command_buffer *item;
+
+ while (!EMPTYQUE(*head)) {
+ item = (command_buffer *) head->q_forw;
+ dispatch_free_command(item);
+ }
+}
+
+static command_buffer *
+dispatch_add_command(qelement *head, char *string)
+{
+ command_buffer *new;
+
+ new = malloc(sizeof(command_buffer));
+
+ if (!new)
+ return NULL;
+
+ new->string = strdup(string);
+ INSQUEUE(new, head->q_back);
+
+ return new;
+}
+
+/*
+ * Command processing
+ */
+
+/* Just convenience */
+static int
+dispatch_shutdown(dialogMenuItem *unused)
+{
+ systemShutdown(0);
+ return DITEM_FAILURE;
+}
+
+static int
+dispatch_systemExecute(dialogMenuItem *unused)
+{
+ char *cmd = variable_get(VAR_COMMAND);
+
+ if (cmd)
+ return systemExecute(cmd) ? DITEM_FAILURE : DITEM_SUCCESS;
+ else
+ msgDebug("_systemExecute: No command passed in `command' variable.\n");
+ return DITEM_FAILURE;
+}
+
+static int
+dispatch_msgConfirm(dialogMenuItem *unused)
+{
+ char *msg = variable_get(VAR_COMMAND);
+
+ if (msg) {
+ msgConfirm("%s", msg);
+ return DITEM_SUCCESS;
+ }
+
+ msgDebug("_msgConfirm: No message passed in `command' variable.\n");
+ return DITEM_FAILURE;
+}
+
+static int
+dispatch_mediaClose(dialogMenuItem *unused)
+{
+ mediaClose();
+ return DITEM_SUCCESS;
+}
+
+static int
+call_possible_resword(char *name, dialogMenuItem *value, int *status)
+{
+ int i, rval;
+
+ rval = 0;
+ for (i = 0; resWords[i].name; i++) {
+ if (!strcmp(name, resWords[i].name)) {
+ *status = resWords[i].handler(value);
+ rval = 1;
+ break;
+ }
+ }
+ return rval;
+}
+
+/* For a given string, call it or spit out an undefined command diagnostic */
+int
+dispatchCommand(char *str)
+{
+ int i;
+ char *cp;
+
+ if (!str || !*str) {
+ msgConfirm("Null or zero-length string passed to dispatchCommand");
+ return DITEM_FAILURE;
+ }
+ /* If it's got a newline, trim it */
+ if ((cp = index(str, '\n')) != NULL)
+ *cp = '\0';
+
+ /* If it's got a `=' sign in there, assume it's a variable setting */
+ if (index(str, '=')) {
+ if (isDebug())
+ msgDebug("dispatch: setting variable `%s'\n", str);
+ variable_set(str, 0);
+ i = DITEM_SUCCESS;
+ }
+ else {
+ /* A command might be a pathname if it's encoded in argv[0], which
+ we also support */
+ if ((cp = rindex(str, '/')) != NULL)
+ str = cp + 1;
+ if (isDebug())
+ msgDebug("dispatch: calling resword `%s'\n", str);
+ if (!call_possible_resword(str, NULL, &i)) {
+ msgNotify("Warning: No such command ``%s''", str);
+ i = DITEM_FAILURE;
+ }
+ /*
+ * Allow a user to prefix a command with "noError" to cause
+ * us to ignore any errors for that one command.
+ */
+ if (i != DITEM_SUCCESS && variable_get(VAR_NO_ERROR))
+ i = DITEM_SUCCESS;
+ variable_unset(VAR_NO_ERROR);
+ }
+ return i;
+}
+
+
+/*
+ * File processing
+ */
+
+static qelement *
+dispatch_load_fp(FILE *fp)
+{
+ qelement *head;
+ char buf[BUFSIZ], *cp;
+
+ head = malloc(sizeof(qelement));
+
+ if (!head)
+ return NULL;
+
+ INITQUE(*head);
+
+ while (fgets(buf, sizeof buf, fp)) {
+
+ if ((cp = strchr(buf, '\n')) != NULL)
+ *cp = '\0';
+ if (*buf == '\0' || *buf == '#')
+ continue;
+
+ if (!dispatch_add_command(head, buf))
+ return NULL;
+ }
+
+ return head;
+}
+
+static int
+dispatch_execute(qelement *head)
+{
+ int result = DITEM_SUCCESS;
+ command_buffer *item;
+ char *old_interactive;
+
+ if (!head)
+ return result | DITEM_FAILURE;
+
+ old_interactive = variable_get(VAR_NONINTERACTIVE);
+ if (old_interactive)
+ old_interactive = strdup(old_interactive); /* save copy */
+
+ /* Hint to others that we're running from a script, should they care */
+ variable_set2(VAR_NONINTERACTIVE, "yes", 0);
+
+ while (!EMPTYQUE(*head)) {
+ item = (command_buffer *) head->q_forw;
+
+ if (DITEM_STATUS(dispatchCommand(item->string)) != DITEM_SUCCESS) {
+ msgConfirm("Command `%s' failed - rest of script aborted.\n",
+ item->string);
+ result |= DITEM_FAILURE;
+ break;
+ }
+ dispatch_free_command(item);
+ }
+
+ dispatch_free_all(head);
+
+ if (!old_interactive)
+ variable_unset(VAR_NONINTERACTIVE);
+ else {
+ variable_set2(VAR_NONINTERACTIVE, old_interactive, 0);
+ free(old_interactive);
+ }
+
+ return result;
+}
+
+int
+dispatch_load_file_int(int quiet)
+{
+ FILE *fp;
+ char *cp;
+ int i;
+ qelement *list;
+
+ static const char *names[] = {
+ "install.cfg",
+ "/stand/install.cfg",
+ "/tmp/install.cfg",
+ NULL
+ };
+
+ fp = NULL;
+ cp = variable_get(VAR_CONFIG_FILE);
+ if (!cp) {
+ for (i = 0; names[i]; i++)
+ if ((fp = fopen(names[i], "r")) != NULL)
+ break;
+ } else
+ fp = fopen(cp, "r");
+
+ if (!fp) {
+ if (!quiet)
+ msgConfirm("Unable to open %s: %s", cp, strerror(errno));
+ return DITEM_FAILURE;
+ }
+
+ list = dispatch_load_fp(fp);
+ fclose(fp);
+
+ return dispatch_execute(list);
+}
+
+int
+dispatch_load_file(dialogMenuItem *self)
+{
+ return dispatch_load_file_int(FALSE);
+}
+
+int
+dispatch_load_floppy(dialogMenuItem *self)
+{
+ int what = DITEM_SUCCESS;
+ extern char *distWanted;
+ char *cp;
+ FILE *fp;
+ qelement *list;
+
+ mediaClose();
+ cp = variable_get_value(VAR_INSTALL_CFG,
+ "Specify the name of a configuration file\n"
+ "residing on a MSDOS or UFS floppy.", 0);
+ if (!cp || !*cp) {
+ variable_unset(VAR_INSTALL_CFG);
+ what |= DITEM_FAILURE;
+ return what;
+ }
+
+ distWanted = cp;
+ /* Try to open the floppy drive */
+ if (DITEM_STATUS(mediaSetFloppy(NULL)) == DITEM_FAILURE) {
+ msgConfirm("Unable to set media device to floppy.");
+ what |= DITEM_FAILURE;
+ mediaClose();
+ return what;
+ }
+
+ if (!DEVICE_INIT(mediaDevice)) {
+ msgConfirm("Unable to mount floppy filesystem.");
+ what |= DITEM_FAILURE;
+ mediaClose();
+ return what;
+ }
+
+ fp = DEVICE_GET(mediaDevice, cp, TRUE);
+ if (fp) {
+ list = dispatch_load_fp(fp);
+ fclose(fp);
+ mediaClose();
+
+ what |= dispatch_execute(list);
+ }
+ else {
+ if (!variable_get(VAR_NO_ERROR))
+ msgConfirm("Configuration file '%s' not found.", cp);
+ variable_unset(VAR_INSTALL_CFG);
+ what |= DITEM_FAILURE;
+ mediaClose();
+ }
+ return what;
+}
+
diff --git a/usr.sbin/sade/dmenu.c b/usr.sbin/sade/dmenu.c
new file mode 100644
index 0000000..7f113fc
--- /dev/null
+++ b/usr.sbin/sade/dmenu.c
@@ -0,0 +1,316 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated for what's essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <errno.h>
+
+#define MAX_MENU 15
+
+static Boolean exited;
+
+int
+dmenuDisplayFile(dialogMenuItem *tmp)
+{
+ systemDisplayHelp((char *)tmp->data);
+ return DITEM_SUCCESS;
+}
+
+int
+dmenuSubmenu(dialogMenuItem *tmp)
+{
+ return (dmenuOpenSimple((DMenu *)(tmp->data), FALSE) ? DITEM_SUCCESS : DITEM_FAILURE);
+}
+
+int
+dmenuSystemCommand(dialogMenuItem *self)
+{
+ WINDOW *w = NULL; /* Keep lint happy */
+
+ /* If aux is set, the command is known not to produce any screen-spoiling output */
+ if (!self->aux)
+ w = savescr();
+ systemExecute((char *)self->data);
+ if (!self->aux)
+ restorescr(w);
+ return DITEM_SUCCESS;
+}
+
+int
+dmenuSystemCommandBox(dialogMenuItem *tmp)
+{
+ WINDOW *w = savescr();
+
+ use_helpfile(NULL);
+ use_helpline("Select OK to dismiss this dialog");
+ dialog_prgbox(tmp->title, (char *)tmp->data, 22, 76, 1, 1);
+ restorescr(w);
+ return DITEM_SUCCESS;
+}
+
+int
+dmenuExit(dialogMenuItem *tmp)
+{
+ exited = TRUE;
+ return DITEM_LEAVE_MENU;
+}
+
+int
+dmenuSetVariable(dialogMenuItem *tmp)
+{
+ variable_set((char *)tmp->data, *((char *)tmp->data) != '_');
+ return DITEM_SUCCESS;
+}
+
+int
+dmenuSetVariables(dialogMenuItem *tmp)
+{
+ char *cp1, *cp2;
+ char *copy = strdup((char *)tmp->data);
+
+ for (cp1 = copy; cp1 != NULL;) {
+ cp2 = index(cp1, ',');
+ if (cp2 != NULL) *cp2++ = '\0';
+ variable_set(cp1, *cp1 != '_');
+ cp1 = cp2;
+ }
+ free(copy);
+ return DITEM_SUCCESS;
+}
+
+int
+dmenuSetKmapVariable(dialogMenuItem *tmp)
+{
+ char *lang;
+ int err;
+
+ variable_set((char *)tmp->data, TRUE);
+ lang = variable_get(VAR_KEYMAP);
+ if (lang != NULL)
+ {
+ err = loadKeymap(lang);
+ if (err == -1)
+ msgConfirm("No appropriate keyboard map found, sorry.");
+ else if (err == -2)
+ msgConfirm("Error installing keyboard map, errno = %d.", errno);
+ }
+ return DITEM_SUCCESS;
+}
+
+int
+dmenuToggleVariable(dialogMenuItem *tmp)
+{
+ char *var, *cp;
+ int status;
+
+ if (!(var = strdup((char *)tmp->data))) {
+ msgConfirm("Incorrect data field for `%s'!", tmp->title);
+ return DITEM_FAILURE;
+ }
+ if (!(cp = index(var, '='))) {
+ msgConfirm("Data field for %s is not in var=value format!", tmp->title);
+ return DITEM_FAILURE;
+ }
+ status = variable_check(var);
+ *cp = '\0';
+ variable_set2(var, status ? "NO" : "YES", *var != '_');
+ free(var);
+ return DITEM_SUCCESS;
+}
+
+int
+dmenuISetVariable(dialogMenuItem *tmp)
+{
+ char *ans, *var;
+
+ if (!(var = (char *)tmp->data)) {
+ msgConfirm("Incorrect data field for `%s'!", tmp->title);
+ return DITEM_FAILURE;
+ }
+ ans = msgGetInput(variable_get(var), tmp->title, 1);
+ if (!ans)
+ return DITEM_FAILURE;
+ else if (!*ans)
+ variable_unset(var);
+ else
+ variable_set2(var, ans, *var != '_');
+ return DITEM_SUCCESS;
+}
+
+int
+dmenuSetFlag(dialogMenuItem *tmp)
+{
+ if (*((unsigned int *)tmp->data) & tmp->aux)
+ *((unsigned int *)tmp->data) &= ~tmp->aux;
+ else
+ *((unsigned int *)tmp->data) |= tmp->aux;
+ return DITEM_SUCCESS;
+}
+
+int
+dmenuSetValue(dialogMenuItem *tmp)
+{
+ *((unsigned int *)tmp->data) = tmp->aux;
+ return DITEM_SUCCESS;
+}
+
+/* Traverse menu but give user no control over positioning */
+Boolean
+dmenuOpenSimple(DMenu *menu, Boolean buttons)
+{
+ int choice, scroll, curr, max;
+
+ choice = scroll = curr = max = 0;
+ return dmenuOpen(menu, &choice, &scroll, &curr, &max, buttons);
+}
+
+/* Work functions for the state hook */
+int
+dmenuFlagCheck(dialogMenuItem *item)
+{
+ return (*((unsigned int *)item->data) & item->aux);
+}
+
+int
+dmenuVarCheck(dialogMenuItem *item)
+{
+ char *w;
+
+ w = (char *)item->aux;
+ if (!w)
+ w = (char *)item->data;
+ return variable_check(w);
+}
+
+int
+dmenuVarsCheck(dialogMenuItem *item)
+{
+ int res, init;
+ char *w, *cp1, *cp2;
+ char *copy;
+
+ w = (char *)item->aux;
+ if (!w)
+ w = (char *)item->data;
+ if (!w)
+ return FALSE;
+
+ copy = strdup(w);
+ res = TRUE;
+ init = FALSE;
+ for (cp1 = copy; cp1 != NULL;) {
+ init = TRUE;
+ cp2 = index(cp1, ',');
+ if (cp2 != NULL)
+ *cp2++ = '\0';
+ res = res && variable_check(cp1);
+ cp1 = cp2;
+ }
+ free(copy);
+ return res && init;
+}
+
+int
+dmenuRadioCheck(dialogMenuItem *item)
+{
+ return (*((unsigned int *)item->data) == item->aux);
+}
+
+static int
+menu_height(DMenu *menu, int n)
+{
+ int max;
+ char *t;
+
+ max = MAX_MENU;
+ if (StatusLine > 24)
+ max += StatusLine - 24;
+ for (t = menu->prompt; *t; t++) {
+ if (*t == '\n')
+ --max;
+ }
+ return n > max ? max : n;
+}
+
+/* Traverse over an internal menu */
+Boolean
+dmenuOpen(DMenu *menu, int *choice, int *scroll, int *curr, int *max, Boolean buttons)
+{
+ int n, rval = 0;
+ dialogMenuItem *items;
+
+ items = menu->items;
+ if (buttons)
+ items += 2;
+ /* Count up all the items */
+ for (n = 0; items[n].title; n++);
+
+ while (1) {
+ char buf[FILENAME_MAX];
+ WINDOW *w = savescr();
+
+ /* Any helpful hints, put 'em up! */
+ use_helpline(menu->helpline);
+ use_helpfile(systemHelpFile(menu->helpfile, buf));
+ dialog_clear_norefresh();
+ /* Pop up that dialog! */
+ if (menu->type & DMENU_NORMAL_TYPE)
+ rval = dialog_menu((u_char *)menu->title, (u_char *)menu->prompt, -1, -1,
+ menu_height(menu, n), -n, items, (char *)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);
+ if (exited) {
+ exited = FALSE;
+ restorescr(w);
+ return TRUE;
+ }
+ else if (rval) {
+ restorescr(w);
+ return FALSE;
+ }
+ else if (menu->type & DMENU_SELECTION_RETURNS) {
+ restorescr(w);
+ return TRUE;
+ }
+ }
+}
diff --git a/usr.sbin/sade/globals.c b/usr.sbin/sade/globals.c
new file mode 100644
index 0000000..4323bd0
--- /dev/null
+++ b/usr.sbin/sade/globals.c
@@ -0,0 +1,73 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+
+/*
+ * Various global variables and an initialization hook to set them to
+ * whatever values we feel are appropriate.
+ */
+
+int DebugFD; /* Where diagnostic output goes */
+Boolean Fake; /* Only pretend to be useful */
+Boolean RunningAsInit; /* Are we running as init? */
+Boolean DialogActive; /* Is libdialog initialized? */
+Boolean ColorDisplay; /* Are we on a color display? */
+Boolean OnVTY; /* Are we on a VTY? */
+Boolean Restarting; /* Are we restarting sysinstall? */
+Variable *VarHead; /* The head of the variable chain */
+Device *mediaDevice; /* Where we're installing from */
+int BootMgr; /* Which boot manager we're using */
+int StatusLine; /* Where to stick our status messages */
+jmp_buf BailOut; /* Beam me up, scotty! The natives are pissed! */
+
+/*
+ * Yes, I know some of these are already automatically initialized as
+ * globals. I simply find it clearer to set everything explicitly.
+ */
+void
+globalsInit(void)
+{
+ DebugFD = -1;
+ ColorDisplay = FALSE;
+ Fake = FALSE;
+ Restarting = FALSE;
+ OnVTY = FALSE;
+ DialogActive = FALSE;
+ VarHead = NULL;
+ mediaDevice = NULL;
+ RunningAsInit = FALSE;
+}
diff --git a/usr.sbin/sade/help/partition.hlp b/usr.sbin/sade/help/partition.hlp
new file mode 100644
index 0000000..ef328df
--- /dev/null
+++ b/usr.sbin/sade/help/partition.hlp
@@ -0,0 +1,135 @@
+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).
+
+If you set (S)oftUpdates on a filesystem, it will cause the
+"Soft Updates" policy to be in effect for it. This basically causes
+both metadata and data blocks to be written asynchronously to disk,
+but with extra state information which causes the metadata and any
+related data blocks to be committed in a single transaction. This
+results in async metadata update speeds (which are considerably
+faster than the default sync) without the potential for data loss
+which could occur if you simply mounted the filesystem with purely
+"async" update policy and then had a power failure. If you wish
+to later turn the softupdates policy back off, use the command
+"tunefs -n disable devicename". NOTE: It is probably not wise
+to use this on your root filesystem unless you have a large
+(e.g. non-standard size) root. The reason is that smaller filesystems
+with significant activity can temporarily overflow if the soft updates
+policy results in free'd blocks not being "garbage collected" as fast
+as they're being requested.
+
+When you're done, type `Q' to exit.
+
+No actual changes will be made to the disk until you (C)ommit from the
+Install menu or (W)rite directly from this one. You're working with
+what is essentially a copy of the disk label(s), both here and in the
+FDISK Partition Editor, and the actual on-disk labels won't be
+affected by any changes you make until you explicitly say so.
diff --git a/usr.sbin/sade/help/slice.hlp b/usr.sbin/sade/help/slice.hlp
new file mode 100644
index 0000000..b6f6a83
--- /dev/null
+++ b/usr.sbin/sade/help/slice.hlp
@@ -0,0 +1,65 @@
+This is the Main Slice (``FDISK'' or PC-style Partition) Editor.
+
+Possible commands are printed at the bottom and the Master Boot Record
+contents are shown at the top. You can move up and down with the
+arrow keys and (C)reate a new slice whenever the highlighted
+selection bar is over a slice whose type is marked as "unused."
+
+You are expected to leave this screen with at least one slice
+marked "FreeBSD." Note that unlike Linux, you don't need to create
+multiple FreeBSD FDISK partition entries for different things like
+swap, file systems, etc. The usual convention is to create ONE
+FreeBSD slice (FDISK partition) per drive and then subsection this slice
+into swap and file systems with the Label editor.
+
+No actual changes will be made to the disk until you (C)ommit from the
+Install menu or use the (W)rite option here! You're working with what
+is essentially a copy of the disk label(s), both here and in the Label
+Editor.
+
+If you want to use the entire disk for FreeBSD, type `A'. You'll be
+asked whether or not you wish to keep the disk (potentially) compatible
+with other operating systems, i.e. the information in the FDISK table
+should be kept valid. If you select the default of `Yes', slices will be
+aligned to fictitious cylinder boundaries and space will be reserved
+in front of the FreeBSD slice for a [future] possible boot manager.
+
+For the truly dedicated disk case, you can select `No' at the
+compatibility prompt. In that case, all BIOS geometry considerations
+will no longer be in effect and you can safely ignore any
+``The detected geometry is invalid'' warning messages you may later
+see. It is also not necessary in this case to set a slice bootable
+or install an MBR boot manager as both things are then irrelevant.
+
+The FreeBSD slice will start at absolute sector 0 of the disk (so that
+FreeBSD's disk label is identical to the Master Boot Record) and
+extend to the very last sector of the disk medium. Needless to say,
+such a disk cannot have any sort of a boot manager, `disk manager',
+or anything else that has to interact with the BIOS. This option is
+therefore only considered safe for SCSI disks and most IDE disks and
+is primarily intended for people who are going to set up a dedicated
+FreeBSD server or workstation, not a typical `home PC'.
+
+The flags field has the following legend:
+
+ '=' -- This slice is properly aligned.
+ '>' -- This slice doesn't end before cylinder 1024
+ 'R' -- This slice contains the root (/) filesystem
+ 'B' -- Slice employs BAD144 bad-spot handling
+ 'C' -- This is the FreeBSD 2.0-compatibility slice (default)
+ 'A' -- This slice is marked active.
+
+If you select a slice for Bad144 handling, it will be scanned
+for bad blocks before any new filesystems are made on it.
+
+If no slice is marked Active, you will need to either install
+a Boot Manager (the option for which will be presented later in the
+installation) or set one Active before leaving this screen.
+
+To leave the slice editor, type `Q'.
+
+Final Note: If you're absolutely sure you know what you're doing
+ and you want to use the old "Dangerously Dedicated" mode
+ which is now deprecated by sysinstall, use the (purposely)
+ undocumented `F' key.
+
diff --git a/usr.sbin/sade/install.c b/usr.sbin/sade/install.c
new file mode 100644
index 0000000..d54d7f6
--- /dev/null
+++ b/usr.sbin/sade/install.c
@@ -0,0 +1,1202 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <ctype.h>
+#include <sys/disklabel.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/fcntl.h>
+#include <sys/wait.h>
+#include <sys/param.h>
+#define MSDOSFS
+#include <sys/mount.h>
+#include <ufs/ufs/ufsmount.h>
+#include <fs/msdosfs/msdosfsmount.h>
+#undef MSDOSFS
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <unistd.h>
+#include <termios.h>
+
+/* Hack for rsaref package add, which displays interactive license.
+ * Used by package.c
+ */
+int _interactiveHack;
+int FixItMode = 0;
+
+static void create_termcap(void);
+static void fixit_common(void);
+
+#define TERMCAP_FILE "/usr/share/misc/termcap"
+
+static void installConfigure(void);
+
+Boolean
+checkLabels(Boolean whinge, Chunk **rdev, Chunk **sdev, Chunk **udev, Chunk **vdev, Chunk **tdev, Chunk **hdev)
+{
+ Device **devs;
+ Boolean status;
+ Disk *disk;
+ Chunk *c1, *c2, *rootdev, *swapdev, *usrdev, *vardev, *tmpdev, *homedev;
+ int i;
+
+ /* Don't allow whinging if noWarn is set */
+ if (variable_get(VAR_NO_WARN))
+ whinge = FALSE;
+
+ status = TRUE;
+ if (rdev)
+ *rdev = NULL;
+ if (sdev)
+ *sdev = NULL;
+ if (udev)
+ *udev = NULL;
+ if (vdev)
+ *vdev = NULL;
+ if (tdev)
+ *tdev = NULL;
+ if (hdev)
+ *hdev = NULL;
+ rootdev = swapdev = usrdev = vardev = tmpdev = homedev = NULL;
+
+ /* We don't need to worry about root/usr/swap if we're already multiuser */
+ if (!RunningAsInit)
+ return status;
+
+ devs = deviceFind(NULL, DEVICE_TYPE_DISK);
+ /* First verify that we have a root device */
+ for (i = 0; devs[i]; i++) {
+ if (!devs[i]->enabled)
+ continue;
+ disk = (Disk *)devs[i]->private;
+ msgDebug("Scanning disk %s for root filesystem\n", disk->name);
+ if (!disk->chunks)
+ msgFatal("No chunk list found for %s!", disk->name);
+ for (c1 = disk->chunks->part; c1; c1 = c1->next) {
+ 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);
+ }
+ } else if (!strcmp(((PartInfo *)c2->private_data)->mountpoint, "/tmp")) {
+ if (tmpdev) {
+ if (whinge)
+ msgConfirm("WARNING: You have more than one /tmp filesystem.\n"
+ "Using the first one found.");
+ continue;
+ }
+ else {
+ tmpdev = c2;
+ if (isDebug())
+ msgDebug("Found tmpdev at %s!\n", tmpdev->name);
+ }
+ } else if (!strcmp(((PartInfo *)c2->private_data)->mountpoint, "/home")) {
+ if (homedev) {
+ if (whinge)
+ msgConfirm("WARNING: You have more than one /home filesystem.\n"
+ "Using the first one found.");
+ continue;
+ }
+ else {
+ homedev = c2;
+ if (isDebug())
+ msgDebug("Found homedev at %s!\n", homedev->name);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* 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 */
+ if (rdev)
+ *rdev = rootdev;
+ if (sdev)
+ *sdev = swapdev;
+ if (udev)
+ *udev = usrdev;
+ if (vdev)
+ *vdev = vardev;
+ if (tdev)
+ *tdev = tmpdev;
+ if (hdev)
+ *hdev = homedev;
+
+ if (!rootdev && whinge) {
+ msgConfirm("No root device found - you must label a partition as /\n"
+ "in the label editor.");
+ status = FALSE;
+ }
+ if (!swapdev && whinge) {
+ msgConfirm("No swap devices found - you must create at least one\n"
+ "swap partition.");
+ status = FALSE;
+ }
+ return status;
+}
+
+static int
+installInitial(void)
+{
+ static Boolean alreadyDone = FALSE;
+ int status = DITEM_SUCCESS;
+
+ if (alreadyDone)
+ return DITEM_SUCCESS;
+
+ if (!variable_get(DISK_LABELLED)) {
+ msgConfirm("You need to assign disk labels before you can proceed with\n"
+ "the installation.");
+ return DITEM_FAILURE;
+ }
+ /* If it's labelled, assume it's also partitioned */
+ if (!variable_get(DISK_PARTITIONED))
+ variable_set2(DISK_PARTITIONED, "yes", 0);
+
+ /* If we refuse to proceed, bail. */
+ dialog_clear_norefresh();
+ if (!variable_get(VAR_NO_WARN)) {
+ if (msgYesNo(
+ "Last Chance! Are you SURE you want continue the installation?\n\n"
+ "If you're running this on a disk with data you wish to save\n"
+ "then WE STRONGLY ENCOURAGE YOU TO MAKE PROPER BACKUPS before\n"
+ "proceeding!\n\n"
+ "We can take no responsibility for lost disk contents!") != 0)
+ return DITEM_FAILURE;
+ }
+
+ if (DITEM_STATUS(diskLabelCommit(NULL)) != DITEM_SUCCESS) {
+ msgConfirm("Couldn't make filesystems properly. Aborting.");
+ return DITEM_FAILURE;
+ }
+
+ if (!copySelf()) {
+ msgConfirm("installInitial: Couldn't clone the boot floppy onto the\n"
+ "root file system. Aborting!");
+ return DITEM_FAILURE;
+ }
+
+ if (!Restarting && chroot("/mnt") == -1) {
+ msgConfirm("installInitial: Unable to chroot to %s - this is bad!",
+ "/mnt");
+ return DITEM_FAILURE;
+ }
+
+ chdir("/");
+ variable_set2(RUNNING_ON_ROOT, "yes", 0);
+
+ /* Configure various files in /etc */
+ if (DITEM_STATUS(configResolv(NULL)) == DITEM_FAILURE)
+ status = DITEM_FAILURE;
+ if (DITEM_STATUS(configFstab(NULL)) == DITEM_FAILURE)
+ status = DITEM_FAILURE;
+
+ /* stick a helpful shell over on the 4th VTY */
+ if (!variable_get(VAR_NO_HOLOSHELL))
+ systemCreateHoloshell();
+
+ alreadyDone = TRUE;
+ return status;
+}
+
+int
+installFixitHoloShell(dialogMenuItem *self)
+{
+ FixItMode = 1;
+ systemCreateHoloshell();
+ return DITEM_SUCCESS;
+ FixItMode = 0;
+}
+
+int
+installFixitCDROM(dialogMenuItem *self)
+{
+ struct stat sb;
+
+ if (!RunningAsInit)
+ return DITEM_SUCCESS;
+
+ variable_set2(SYSTEM_STATE, "fixit", 0);
+ (void)unlink("/mnt2");
+ (void)rmdir("/mnt2");
+
+ while (1) {
+ msgConfirm("Please insert a FreeBSD live filesystem CD/DVD and press return");
+ if (DITEM_STATUS(mediaSetCDROM(NULL)) != DITEM_SUCCESS
+ || !DEVICE_INIT(mediaDevice)) {
+ /* If we can't initialize it, it's probably not a FreeBSD CDROM so punt on it */
+ mediaClose();
+ if (msgYesNo("Unable to mount the disc - do you want to try again?") != 0)
+ return DITEM_FAILURE;
+ }
+ else
+ break;
+ }
+
+ /* Since the fixit code expects everything to be in /mnt2, and the CDROM mounting stuff /dist, do
+ * a little kludge dance here..
+ */
+ if (symlink("/dist", "/mnt2")) {
+ msgConfirm("Unable to symlink /mnt2 to the disc mount point. Please report this\n"
+ "unexpected failure to freebsd-bugs@FreeBSD.org.");
+ return DITEM_FAILURE;
+ }
+
+ /*
+ * If /tmp points to /mnt2/tmp from a previous fixit floppy session, it's
+ * not very good for us if we point it to the CDROM now. Rather make it
+ * a directory in the root MFS then. Experienced admins will still be
+ * able to mount their disk's /tmp over this if they need.
+ */
+ if (lstat("/tmp", &sb) == 0 && (sb.st_mode & S_IFMT) == S_IFLNK)
+ (void)unlink("/tmp");
+ Mkdir("/tmp");
+
+ /*
+ * Since setuid binaries ignore LD_LIBRARY_PATH, we indeed need the
+ * ld.so.hints file. Fortunately, it's fairly small (~ 3 KB).
+ */
+ if (!file_readable("/var/run/ld.so.hints")) {
+ Mkdir("/var/run");
+ if (vsystem("/mnt2/sbin/ldconfig -s /mnt2/usr/lib")) {
+ msgConfirm("Warning: ldconfig could not create the ld.so hints file.\n"
+ "Dynamic executables from the disc likely won't work.");
+ }
+ }
+
+ /* Yet more iggly hardcoded pathnames. */
+ Mkdir("/usr/libexec");
+ if (!file_readable("/usr/libexec/ld.so") && file_readable("/mnt2/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 disc likely won't work.");
+ }
+ }
+ /* optional nicety */
+ if (!file_readable("/usr/bin/vi"))
+ symlink("/mnt2/usr/bin/vi", "/usr/bin/vi");
+ fixit_common();
+ mediaClose();
+ msgConfirm("Please remove the FreeBSD fixit CDROM/DVD now.");
+ return DITEM_SUCCESS;
+}
+
+int
+installFixitFloppy(dialogMenuItem *self)
+{
+ struct ufs_args args;
+ extern char *distWanted;
+
+ if (!RunningAsInit)
+ return DITEM_SUCCESS;
+
+ /* Try to open the floppy drive */
+ if (DITEM_STATUS(mediaSetFloppy(NULL)) == DITEM_FAILURE || !mediaDevice) {
+ msgConfirm("Unable to set media device to floppy.");
+ mediaClose();
+ return DITEM_FAILURE;
+ }
+
+ memset(&args, 0, sizeof(args));
+ args.fspec = mediaDevice->devname;
+ mediaDevice->private = "/mnt2";
+ distWanted = NULL;
+ Mkdir("/mnt2");
+
+ variable_set2(SYSTEM_STATE, "fixit", 0);
+
+ while (1) {
+ if (!DEVICE_INIT(mediaDevice)) {
+ if (msgYesNo("The attempt to mount the fixit floppy failed, bad floppy\n"
+ "or unclean filesystem. Do you want to try again?"))
+ return DITEM_FAILURE;
+ }
+ else
+ break;
+ }
+ if (!directory_exists("/tmp"))
+ (void)symlink("/mnt2/tmp", "/tmp");
+ fixit_common();
+ mediaClose();
+ msgConfirm("Please remove the fixit floppy now.");
+ return DITEM_SUCCESS;
+}
+
+/*
+ * The common code for both fixit variants.
+ */
+static void
+fixit_common(void)
+{
+ pid_t child;
+ int waitstatus;
+
+ if (!directory_exists("/var/tmp/vi.recover")) {
+ if (DITEM_STATUS(Mkdir("/var/tmp/vi.recover")) != DITEM_SUCCESS) {
+ msgConfirm("Warning: Was unable to create a /var/tmp/vi.recover directory.\n"
+ "vi will kvetch and moan about it as a result but should still\n"
+ "be essentially usable.");
+ }
+ }
+ if (!directory_exists("/bin"))
+ (void)Mkdir("/bin");
+ (void)symlink("/stand/sh", "/bin/sh");
+ /* Link the /etc/ files */
+ if (DITEM_STATUS(Mkdir("/etc")) != DITEM_SUCCESS)
+ msgConfirm("Unable to create an /etc directory! Things are weird on this floppy..");
+ else if ((symlink("/mnt2/etc/spwd.db", "/etc/spwd.db") == -1 && errno != EEXIST) ||
+ (symlink("/mnt2/etc/protocols", "/etc/protocols") == -1 && errno != EEXIST) ||
+ (symlink("/mnt2/etc/services", "/etc/services") == -1 && errno != EEXIST))
+ msgConfirm("Couldn't symlink the /etc/ files! I'm not sure I like this..");
+ if (!file_readable(TERMCAP_FILE))
+ create_termcap();
+ if (strcmp(variable_get(VAR_FIXIT_TTY), "serial") == 0)
+ systemSuspendDialog(); /* must be before the fork() */
+ if (!(child = fork())) {
+ int i, fd;
+ struct termios foo;
+ extern int login_tty(int);
+
+ ioctl(0, TIOCNOTTY, NULL);
+ for (i = getdtablesize(); i >= 0; --i)
+ close(i);
+
+ if (strcmp(variable_get(VAR_FIXIT_TTY), "serial") == 0)
+ fd = open("/dev/console", O_RDWR);
+ else
+ fd = open("/dev/ttyv3", O_RDWR);
+ ioctl(0, TIOCSCTTY, &fd);
+ dup2(0, 1);
+ dup2(0, 2);
+ DebugFD = 2;
+ if (login_tty(fd) == -1)
+ msgDebug("fixit: I can't set the controlling terminal.\n");
+
+ signal(SIGTTOU, SIG_IGN);
+ if (tcgetattr(0, &foo) != -1) {
+ foo.c_cc[VERASE] = '\010';
+ if (tcsetattr(0, TCSANOW, &foo) == -1)
+ msgDebug("fixit shell: Unable to set erase character.\n");
+ }
+ else
+ msgDebug("fixit shell: Unable to get terminal attributes!\n");
+ setenv("PATH", "/bin:/sbin:/usr/bin:/usr/sbin:/stand:"
+ "/mnt2/stand:/mnt2/bin:/mnt2/sbin:/mnt2/usr/bin:/mnt2/usr/sbin", 1);
+ setenv("MAKEDEVPATH", "/sbin:/bin:/stand:"
+ "/mnt2/sbin:/mnt2/bin:/mnt2/stand", 1);
+ if (strcmp(variable_get(VAR_FIXIT_TTY), "serial") == 0) {
+ printf("Waiting for fixit shell to exit.\n"
+ "When you are done, type ``exit'' to exit\n"
+ "the fixit shell and be returned here.\n\n");
+ fflush(stdout);
+ }
+
+ /* use the .profile from the fixit medium */
+ setenv("HOME", "/mnt2", 1);
+ chdir("/mnt2");
+ execlp("sh", "-sh", (char *)0);
+ msgDebug("fixit shell: Failed to execute shell!\n");
+ _exit(1);;
+ }
+ else {
+ if (strcmp(variable_get(VAR_FIXIT_TTY), "standard") == 0) {
+ dialog_clear_norefresh();
+ msgNotify("Waiting for fixit shell to exit. Go to VTY4 now by\n"
+ "typing ALT-F4. When you are done, type ``exit'' to exit\n"
+ "the fixit shell and be returned here\n.");
+ }
+ (void)waitpid(child, &waitstatus, 0);
+ if (strcmp(variable_get(VAR_FIXIT_TTY), "serial") == 0)
+ systemResumeDialog();
+ }
+ dialog_clear();
+}
+
+
+int
+installExpress(dialogMenuItem *self)
+{
+ int i;
+
+ dialog_clear_norefresh();
+ variable_set2(SYSTEM_STATE, "express", 0);
+#ifndef __alpha__
+ if (DITEM_STATUS((i = diskPartitionEditor(self))) == DITEM_FAILURE)
+ return i;
+#endif
+
+ if (DITEM_STATUS((i = diskLabelEditor(self))) == DITEM_FAILURE)
+ return i;
+
+ if (DITEM_STATUS((i = installCommit(self))) == DITEM_SUCCESS) {
+ i |= DITEM_LEAVE_MENU;
+ /* Set default security level */
+ configSecurityModerate(NULL);
+
+ /* Give user the option of one last configuration spree */
+ installConfigure();
+ }
+ return i;
+}
+
+/* Standard mode installation */
+int
+installStandard(dialogMenuItem *self)
+{
+ int i, tries = 0;
+ Device **devs;
+
+ variable_set2(SYSTEM_STATE, "standard", 0);
+ dialog_clear_norefresh();
+#ifndef __alpha__
+ 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
+
+#ifdef __alpha__
+ msgConfirm("Now 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("Now you need to create BSD partitions inside of the fdisk partition(s)\n"
+ "just created. If you have a reasonable amount of disk space (200MB or more)\n"
+ "and don't have any special requirements, simply use the (A)uto command to\n"
+ "allocate space automatically. If you have more specific needs or just don't\n"
+ "care for the layout chosen by (A)uto, press F1 for more information on\n"
+ "manual layout.");
+#endif
+
+ if (DITEM_STATUS(diskLabelEditor(self)) == DITEM_FAILURE)
+ return DITEM_FAILURE;
+
+ if (DITEM_STATUS((i = installCommit(self))) == DITEM_FAILURE) {
+ dialog_clear();
+ msgConfirm("Installation completed with some errors. You may wish to\n"
+ "scroll through the debugging messages on VTY1 with the\n"
+ "scroll-lock feature. You can also choose \"No\" at the next\n"
+ "prompt and go back into the installation menus to try and retry\n"
+ "whichever operations have failed.");
+ return i;
+
+ }
+ else {
+ dialog_clear();
+ msgConfirm("Congratulations! You now have FreeBSD installed on your system.\n\n"
+ "We will now move on to the final configuration questions.\n"
+ "For any option you do not wish to configure, simply select\n"
+ "No.\n\n"
+ "If you wish to re-enter this utility after the system is up, you\n"
+ "may do so by typing: /usr/sbin/sysinstall.");
+ }
+ if (mediaDevice->type != DEVICE_TYPE_FTP && mediaDevice->type != DEVICE_TYPE_NFS) {
+ if (!msgYesNo("Would you like to configure any Ethernet or SLIP/PPP network devices?")) {
+ Device *tmp = tcpDeviceSelect();
+
+ if (tmp && !((DevInfo *)tmp->private)->use_dhcp && !msgYesNo("Would you like to bring the %s interface up right now?", tmp->name))
+ if (!DEVICE_INIT(tmp))
+ msgConfirm("Initialization of %s device failed.", tmp->name);
+ }
+ dialog_clear_norefresh();
+ }
+
+ if (!msgNoYes("Do you want this machine to function as a network gateway?"))
+ variable_set2("gateway_enable", "YES", 1);
+
+ dialog_clear_norefresh();
+ if (!msgNoYes("Do you want to configure inetd and the network services that it provides?"))
+ configInetd(self);
+
+ dialog_clear_norefresh();
+ if (!msgNoYes("Do you want to have anonymous FTP access to this machine?"))
+ configAnonFTP(self);
+
+ dialog_clear_norefresh();
+ if (!msgNoYes("Do you want to configure this machine as an NFS server?"))
+ configNFSServer(self);
+
+ dialog_clear_norefresh();
+ if (!msgNoYes("Do you want to configure this machine as an NFS client?"))
+ variable_set2("nfs_client_enable", "YES", 1);
+
+ if (!msgNoYes("Do you want to select a default security profile for\n"
+ "this host (select No for \"moderate\" security)?"))
+ configSecurityProfile(self);
+ else
+ configSecurityModerate(self);
+
+ dialog_clear_norefresh();
+ if (!msgNoYes("Would you like to customize your system console settings?"))
+ dmenuOpenSimple(&MenuSyscons, FALSE);
+
+ dialog_clear_norefresh();
+ if (!msgYesNo("Would you like to set this machine's time zone now?"))
+ systemExecute("tzsetup");
+
+#ifdef __i386__
+ dialog_clear_norefresh();
+ if (!msgYesNo("Would you like to enable Linux binary compatibility?"))
+ (void)configLinux(self);
+#endif
+
+ dialog_clear_norefresh();
+ if (!msgNoYes("Does this system have a non-USB mouse attached to it?"))
+ dmenuOpenSimple(&MenuMouse, FALSE);
+
+ /* Now would be a good time to checkpoint the configuration data */
+ configRC_conf();
+ sync();
+
+ if (directory_exists("/usr/X11R6")) {
+ dialog_clear_norefresh();
+ if (!msgYesNo("Would you like to configure your X server at this time?"))
+ (void)configXSetup(self);
+ }
+
+ dialog_clear_norefresh();
+ if (!msgYesNo("The FreeBSD package collection is a collection of thousands of ready-to-run\n"
+ "applications, from text editors to games to WEB servers and more. Would you\n"
+ "like to browse the collection now?")) {
+ (void)configPackages(self);
+ }
+
+ if (!msgYesNo("Would you like to add any initial user accounts to the system?\n"
+ "Adding at least one account for yourself at this stage is suggested\n"
+ "since working as the \"root\" user is dangerous (it is easy to do\n"
+ "things which adversely affect the entire system)."))
+ (void)configUsers(self);
+
+ msgConfirm("Now you must set the system manager's password.\n"
+ "This is the password you'll use to log in as \"root\".");
+ if (!systemExecute("passwd root"))
+ variable_set2("root_password", "YES", 0);
+
+ /* XXX Put whatever other nice configuration questions you'd like to ask the user here XXX */
+
+ /* Give user the option of one last configuration spree */
+ dialog_clear_norefresh();
+ installConfigure();
+ return DITEM_LEAVE_MENU;
+}
+
+/* The version of commit we call from the Install Custom menu */
+int
+installCustomCommit(dialogMenuItem *self)
+{
+ int i;
+
+ i = installCommit(self);
+ if (DITEM_STATUS(i) == DITEM_SUCCESS) {
+ /* Set default security level */
+ configSecurityModerate(NULL);
+
+ /* Give user the option of one last configuration spree */
+ installConfigure();
+ return i;
+ }
+ else
+ msgConfirm("The commit operation completed with errors. Not\n"
+ "updating /etc files.");
+ return i;
+}
+
+/*
+ * What happens when we finally decide to going ahead with the installation.
+ *
+ * This is broken into multiple stages so that the user can do a full
+ * installation but come back here again to load more distributions,
+ * perhaps from a different media type. This would allow, for
+ * example, the user to load the majority of the system from CDROM and
+ * then use ftp to load just the CRYPTO dist.
+ */
+int
+installCommit(dialogMenuItem *self)
+{
+ int i;
+ char *str;
+
+ dialog_clear_norefresh();
+ if (!Dists)
+ distConfig(NULL);
+
+ if (!Dists) {
+ (void)dmenuOpenSimple(&MenuDistributions, FALSE);
+ /* select reasonable defaults if necessary */
+ if (!Dists)
+ Dists = _DIST_USER;
+ }
+
+ if (!mediaVerify())
+ return DITEM_FAILURE;
+
+ str = variable_get(SYSTEM_STATE);
+ if (isDebug())
+ msgDebug("installCommit: System state is `%s'\n", str);
+
+ /* Installation stuff we wouldn't do to a running system */
+ if (RunningAsInit && DITEM_STATUS((i = installInitial())) == DITEM_FAILURE)
+ return i;
+
+try_media:
+ if (!DEVICE_INIT(mediaDevice)) {
+ if (!msgYesNo("Unable to initialize selected media. Would you like to\n"
+ "adjust your media configuration and try again?")) {
+ mediaDevice = NULL;
+ if (!mediaVerify())
+ return DITEM_FAILURE;
+ else
+ goto try_media;
+ }
+ else
+ return DITEM_FAILURE;
+ }
+
+ /* Now go get it all */
+ i = distExtractAll(self);
+
+ /* When running as init, *now* it's safe to grab the rc.foo vars */
+ installEnvironment();
+
+ variable_set2(SYSTEM_STATE, DITEM_STATUS(i) == DITEM_FAILURE ? "error-install" : "full-install", 0);
+
+ return i;
+}
+
+static void
+installConfigure(void)
+{
+ /* Final menu of last resort */
+ if (!msgNoYes("Visit the general configuration menu for a chance to set\n"
+ "any last options?"))
+ dmenuOpenSimple(&MenuConfigure, FALSE);
+ configRC_conf();
+ sync();
+}
+
+int
+installFixupBin(dialogMenuItem *self)
+{
+ Device **devs;
+ char *cp;
+ int i;
+ FILE *fp;
+ int kstat = 1;
+
+ /* All of this is done only as init, just to be safe */
+ if (RunningAsInit) {
+#ifdef __i386__
+ if ((fp = fopen("/boot/loader.conf", "a")) != NULL) {
+ if (!kstat || !OnVTY)
+ fprintf(fp, "# -- sysinstall generated deltas -- #\n");
+ if (!kstat)
+ fprintf(fp, "userconfig_script_load=\"YES\"\n");
+ if (!OnVTY)
+ fprintf(fp, "console=\"comconsole\"\n");
+ fclose(fp);
+ }
+#endif
+ /* BOGON #1: Resurrect /dev after bin distribution screws it up */
+ dialog_clear_norefresh();
+ msgNotify("Remaking all devices.. Please wait!");
+ if (!Fake)
+ (void)unmount("/dev", MNT_FORCE);
+ if (vsystem("cd /dev; sh MAKEDEV all")) {
+ msgConfirm("MAKEDEV returned non-zero status");
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+
+ dialog_clear_norefresh();
+ 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) {
+ dialog_clear_norefresh();
+ 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 | DITEM_RESTORE;
+ }
+ }
+ }
+ }
+
+ /* BOGON #2: We leave /etc in a bad state */
+ chmod("/etc", 0755);
+
+ /* BOGON #3: No /var/db/mountdtab complains */
+ Mkdir("/var/db");
+ creat("/var/db/mountdtab", 0644);
+
+ /* BOGON #4: /compat created by default in root fs */
+ Mkdir("/usr/compat");
+ vsystem("ln -s usr/compat /compat");
+
+ /* BOGON #5: aliases database not build for bin */
+ vsystem("newaliases");
+
+ /* Now run all the mtree stuff to fix things up */
+ vsystem("mtree -deU -f /etc/mtree/BSD.root.dist -p /");
+ vsystem("mtree -deU -f /etc/mtree/BSD.var.dist -p /var");
+ vsystem("mtree -deU -f /etc/mtree/BSD.usr.dist -p /usr");
+
+ /* Do all the last ugly work-arounds here */
+ }
+ return DITEM_SUCCESS | DITEM_RESTORE;
+}
+
+#ifdef X_AS_PKG
+int
+installX11package(dialogMenuItem *self)
+{
+ WINDOW *w = savescr();
+ int i;
+
+ dialog_clear_norefresh();
+ msgNotify("Installing XFree86 package...");
+ i = package_add("XFree86-4");
+ restorescr(w);
+ return i;
+}
+#endif
+
+/* Fix side-effects from the the XFree86 installation */
+int
+installFixupXFree(dialogMenuItem *self)
+{
+ /* BOGON #1: XFree86 requires various specialized fixups */
+ if (directory_exists("/usr/X11R6")) {
+ dialog_clear_norefresh();
+ msgNotify("Fixing permissions in XFree86 tree..");
+ vsystem("chmod -R a+r /usr/X11R6");
+ vsystem("find /usr/X11R6 -type d | xargs chmod a+x");
+
+#ifndef X_AS_PKG
+ /* Also do bogus minimal package registration so ports don't whine */
+ if (file_readable("/usr/X11R6/lib/X11/pkgreg.tar.gz")) {
+ dialog_clear_norefresh();
+ msgNotify("Installing package metainfo..");
+ vsystem("tar xpzf /usr/X11R6/lib/X11/pkgreg.tar.gz -C / && rm /usr/X11R6/lib/X11/pkgreg.tar.gz");
+ }
+#endif
+ }
+ return DITEM_SUCCESS | DITEM_RESTORE;
+}
+
+/* Go newfs and/or mount all the filesystems we've been asked to */
+int
+installFilesystems(dialogMenuItem *self)
+{
+ int i, mountfailed;
+ Disk *disk;
+ Chunk *c1, *c2, *rootdev, *swapdev;
+ 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, NULL, NULL, NULL, NULL))
+ return DITEM_FAILURE;
+
+ if (rootdev)
+ root = (PartInfo *)rootdev->private_data;
+ else
+ root = NULL;
+
+ command_clear();
+ if (swapdev && RunningAsInit) {
+ /* As the very first thing, try to get ourselves some swap space */
+ sprintf(dname, "/dev/%s", swapdev->name);
+ if (!Fake && (!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)) {
+ dialog_clear_norefresh();
+ msgNotify("Added %s as initial swap device", dname);
+ }
+ else {
+ msgConfirm("WARNING! Unable to swap to %s: %s\n"
+ "This may cause the installation to fail at some point\n"
+ "if you don't have a lot of memory.", dname, strerror(errno));
+ }
+ }
+ }
+
+ if (rootdev && RunningAsInit) {
+ /* Next, create and/or mount the root device */
+ sprintf(dname, "/dev/%s", rootdev->name);
+ if (!Fake && (!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 | DITEM_RESTORE;
+ }
+ if (strcmp(root->mountpoint, "/"))
+ msgConfirm("Warning: %s is marked as a root partition but is mounted on %s", rootdev->name, root->mountpoint);
+
+ if (root->newfs && (!upgrade || !msgNoYes("You are upgrading - are you SURE you want to newfs the root partition?"))) {
+ int i;
+
+ dialog_clear_norefresh();
+ msgNotify("Making a new root filesystem on %s", dname);
+ i = 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 | DITEM_RESTORE;
+ }
+ }
+ else {
+ if (!upgrade) {
+ msgConfirm("Warning: Using existing root partition. It will be assumed\n"
+ "that you have the appropriate device entries already in /dev.");
+ }
+ dialog_clear_norefresh();
+ msgNotify("Checking integrity of existing %s filesystem.", dname);
+ i = vsystem("fsck_ffs -y %s", dname);
+ if (i)
+ msgConfirm("Warning: fsck returned status of %d for %s.\n"
+ "This partition may be unsafe to use.", i, dname);
+ }
+ if (root->soft) {
+ i = vsystem("tunefs -n enable %s", dname);
+ if (i)
+ msgConfirm("Warning: Unable to enable softupdates for root filesystem on %s", dname);
+ }
+
+ /* Switch to block device */
+ sprintf(dname, "/dev/%s", rootdev->name);
+ if (Mount("/mnt", dname)) {
+ msgConfirm("Unable to mount the root file system on %s! Giving up.", dname);
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+
+ /* Mount devfs for other partitions to mount */
+ Mkdir("/mnt/dev");
+ if (!Fake)
+ mountfailed = mount("devfs", "/mnt/dev", 0, NULL);
+
+ if (mountfailed) {
+ dialog_clear_norefresh();
+ 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 | DITEM_RESTORE;
+ }
+ }
+ }
+
+ /* Now buzz through the rest of the partitions and mount them too */
+ devs = deviceFind(NULL, DEVICE_TYPE_DISK);
+ for (i = 0; devs[i]; i++) {
+ if (!devs[i]->enabled)
+ continue;
+
+ disk = (Disk *)devs[i]->private;
+ if (!disk->chunks) {
+ msgConfirm("No chunk list found for %s!", disk->name);
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+ if (mountfailed) {
+ 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 || !msgNoYes("You are upgrading - are you SURE you want to newfs /dev/%s?", c2->name)))
+ command_shell_add(tmp->mountpoint, "%s %s/dev/%s", tmp->newfs_cmd, RunningAsInit ? "/mnt" : "", c2->name);
+ else
+ command_shell_add(tmp->mountpoint, "fsck_ffs -y %s/dev/%s", RunningAsInit ? "/mnt" : "", c2->name);
+ if (tmp->soft)
+ command_shell_add(tmp->mountpoint, "tunefs -n enable %s/dev/%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) {
+ dialog_clear_norefresh();
+ msgNotify("Added %s as an additional swap device", fname);
+ }
+ else {
+ msgConfirm("Unable to add %s as a swap device: %s", fname, strerror(errno));
+ }
+ }
+ }
+ }
+ else if (c1->type == fat && c1->private_data && (root->newfs || upgrade)) {
+ char name[FILENAME_MAX];
+
+ sprintf(name, "%s/%s", RunningAsInit ? "/mnt" : "", ((PartInfo *)c1->private_data)->mountpoint);
+ Mkdir(name);
+ }
+ }
+ }
+
+ command_sort();
+ command_execute();
+ dialog_clear_norefresh();
+ return DITEM_SUCCESS | DITEM_RESTORE;
+}
+
+static char *
+getRelname(void)
+{
+ static char buf[64];
+ size_t sz = (sizeof buf) - 1;
+
+ if (sysctlbyname("kern.osrelease", buf, &sz, NULL, 0) != -1) {
+ buf[sz] = '\0';
+ return buf;
+ }
+ else
+ return "<unknown>";
+}
+
+/* Initialize various user-settable values to their defaults */
+int
+installVarDefaults(dialogMenuItem *self)
+{
+ char *cp;
+
+ /* Set default startup options */
+ variable_set2(VAR_RELNAME, getRelname(), 0);
+ variable_set2(VAR_CPIO_VERBOSITY, "high", 0);
+ variable_set2(VAR_TAPE_BLOCKSIZE, DEFAULT_TAPE_BLOCKSIZE, 0);
+ variable_set2(VAR_INSTALL_ROOT, "/", 0);
+ variable_set2(VAR_INSTALL_CFG, "install.cfg", 0);
+ cp = getenv("EDITOR");
+ if (!cp)
+ cp = "/usr/bin/ee";
+ variable_set2(VAR_EDITOR, cp, 0);
+ variable_set2(VAR_FTP_USER, "ftp", 0);
+ variable_set2(VAR_BROWSER_PACKAGE, "links", 0);
+ variable_set2(VAR_BROWSER_BINARY, "/usr/local/bin/links", 0);
+ variable_set2(VAR_FTP_STATE, "passive", 0);
+ variable_set2(VAR_NFS_SECURE, "NO", -1);
+ if (OnVTY)
+ variable_set2(VAR_FIXIT_TTY, "standard", 0);
+ else
+ variable_set2(VAR_FIXIT_TTY, "serial", 0);
+ variable_set2(VAR_PKG_TMPDIR, "/var/tmp", 0);
+ variable_set2(VAR_MEDIA_TIMEOUT, itoa(MEDIA_TIMEOUT), 0);
+ if (getpid() != 1)
+ variable_set2(SYSTEM_STATE, "update", 0);
+ else
+ variable_set2(SYSTEM_STATE, "init", 0);
+ variable_set2(VAR_NEWFS_ARGS, "-b 16384 -f 2048", 0);
+ variable_set2(VAR_CONSTERM, "NO", 0);
+ return DITEM_SUCCESS;
+}
+
+/* Load the environment up from various system configuration files */
+void
+installEnvironment(void)
+{
+ configEnvironmentRC_conf();
+ if (file_readable("/etc/resolv.conf"))
+ configEnvironmentResolv("/etc/resolv.conf");
+}
+
+/* Copy the boot floppy contents into /stand */
+Boolean
+copySelf(void)
+{
+ int i;
+
+ if (file_readable("/boot.help"))
+ vsystem("cp /boot.help /mnt");
+ msgWeHaveOutput("Copying the boot floppy to /stand on root filesystem");
+ i = vsystem("find -x /stand | cpio %s -pdum /mnt", cpioVerbosity());
+ if (i) {
+ msgConfirm("Copy returned error status of %d!", i);
+ return FALSE;
+ }
+
+ /* Copy the /etc files into their rightful place */
+ if (vsystem("cd /mnt/stand; find etc | cpio %s -pdum /mnt", cpioVerbosity())) {
+ msgConfirm("Couldn't copy up the /etc files!");
+ return TRUE;
+ }
+ return TRUE;
+}
+
+static void
+create_termcap(void)
+{
+ FILE *fp;
+
+ const char *caps[] = {
+ termcap_vt100, termcap_cons25, termcap_cons25_m, termcap_cons25r,
+ termcap_cons25r_m, termcap_cons25l1, termcap_cons25l1_m,
+ termcap_xterm, NULL,
+ };
+ const char **cp;
+
+ if (!file_readable(TERMCAP_FILE)) {
+ Mkdir("/usr/share/misc");
+ fp = fopen(TERMCAP_FILE, "w");
+ if (!fp) {
+ msgConfirm("Unable to initialize termcap file. Some screen-oriented\nutilities may not work.");
+ return;
+ }
+ cp = caps;
+ while (*cp)
+ fprintf(fp, "%s\n", *(cp++));
+ fclose(fp);
+ }
+}
diff --git a/usr.sbin/sade/keymap.c b/usr.sbin/sade/keymap.c
new file mode 100644
index 0000000..3c53a00
--- /dev/null
+++ b/usr.sbin/sade/keymap.c
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 1996 Joerg Wunsch
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ */
+
+#include "sysinstall.h"
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/kbio.h>
+
+struct keymapInfo {
+ const char *name;
+ const struct keymap *map;
+};
+
+#include "keymap.h"
+
+/*
+ * keymap.h is being automatically generated by the Makefile. It
+ * contains definitions for all desired keymaps. Note that since we
+ * don't support font loading nor screen mapping during installation,
+ * we simply don't care for any other keys than the ASCII subset.
+ *
+ * Therefore, if no keymap with the exact name has been found in the
+ * first pass, we make a second pass over the table looking just for
+ * the language name only.
+ */
+
+/*
+ * Return values:
+ *
+ * 0: OK
+ * -1: no appropriate keymap found
+ * -2: error installing map (other than ENXIO which means we're not on syscons)
+ */
+
+int
+loadKeymap(const char *lang)
+{
+ int passno, err;
+ char *llang;
+ size_t l;
+ struct keymapInfo *kip;
+
+ llang = strdup(lang);
+ if (llang == NULL)
+ abort();
+
+ for (passno = 0; passno < 2; passno++)
+ {
+ if (passno > 0)
+ {
+ /* make the match more fuzzy */
+ l = strspn(llang, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
+ llang[l] = '\0';
+ }
+
+ l = strlen(llang);
+
+ for (kip = keymapInfos; kip->name; kip++)
+ if (strncmp(kip->name, llang, l) == 0)
+ {
+ /* Yep, got it! */
+ err = ioctl(0, PIO_KEYMAP, kip->map);
+ free(llang);
+ return (err == -1 && errno != ENOTTY)? -2: 0;
+ }
+ }
+ free(llang);
+ return -1;
+}
diff --git a/usr.sbin/sade/label.c b/usr.sbin/sade/label.c
new file mode 100644
index 0000000..f40f3ae
--- /dev/null
+++ b/usr.sbin/sade/label.c
@@ -0,0 +1,1481 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <ctype.h>
+#include <sys/disklabel.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+
+#define AUTO_HOME 0 /* do not create /home automatically */
+
+/*
+ * Everything to do with editing the contents of disk labels.
+ */
+
+/* A nice message we use a lot in the disklabel editor */
+#define MSG_NOT_APPLICABLE "That option is not applicable here"
+
+/* Where to start printing the freebsd slices */
+#define CHUNK_SLICE_START_ROW 2
+#define CHUNK_PART_START_ROW 11
+
+/* The smallest filesystem we're willing to create */
+#define FS_MIN_SIZE ONE_MEG
+
+/*
+ * Minimum partition sizes
+ */
+#ifdef __alpha__
+#define ROOT_MIN_SIZE 40
+#else
+#define ROOT_MIN_SIZE 30
+#endif
+#define SWAP_MIN_SIZE 32
+#define USR_MIN_SIZE 80
+#define VAR_MIN_SIZE 20
+#define TMP_MIN_SIZE 20
+#define HOME_MIN_SIZE 20
+
+/*
+ * Swap size limit for auto-partitioning (4G).
+ */
+#define SWAP_AUTO_LIMIT_SIZE 4096
+
+/*
+ * Default partition sizes. If we do not have sufficient disk space
+ * for this configuration we scale things relative to the NOM vs DEFAULT
+ * sizes. If the disk is larger then /home will get any remaining space.
+ */
+#define ROOT_DEFAULT_SIZE 128
+#define USR_DEFAULT_SIZE 3072
+#define VAR_DEFAULT_SIZE 256
+#define TMP_DEFAULT_SIZE 256
+#define HOME_DEFAULT_SIZE USR_DEFAULT_SIZE
+
+/*
+ * Nominal partition sizes. These are used to scale the default sizes down
+ * when we have insufficient disk space. If this isn't sufficient we scale
+ * down using the MIN sizes instead.
+ */
+#define ROOT_NOMINAL_SIZE 128
+#define USR_NOMINAL_SIZE 512
+#define VAR_NOMINAL_SIZE 64
+#define TMP_NOMINAL_SIZE 64
+#define HOME_NOMINAL_SIZE USR_NOMINAL_SIZE
+
+/* The bottom-most row we're allowed to scribble on */
+#define CHUNK_ROW_MAX 16
+
+
+/* All the chunks currently displayed on the screen */
+static struct {
+ struct chunk *c;
+ PartType type;
+} label_chunk_info[MAX_CHUNKS + 1];
+static int here;
+
+/*** with this value we try to track the most recently added label ***/
+static int label_focus = 0, pslice_focus = 0;
+
+static int diskLabel(Device *dev);
+static int diskLabelNonInteractive(Device *dev);
+static char *try_auto_label(Device **devs, Device *dev, int perc, int *req);
+
+static int
+labelHook(dialogMenuItem *selected)
+{
+ Device **devs = NULL;
+
+ devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
+ if (!devs) {
+ msgConfirm("Unable to find disk %s!", selected->prompt);
+ return DITEM_FAILURE;
+ }
+ /* Toggle enabled status? */
+ if (!devs[0]->enabled) {
+ devs[0]->enabled = TRUE;
+ diskLabel(devs[0]);
+ }
+ else
+ devs[0]->enabled = FALSE;
+ return DITEM_SUCCESS;
+}
+
+static int
+labelCheck(dialogMenuItem *selected)
+{
+ Device **devs = NULL;
+
+ devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
+ if (!devs || devs[0]->enabled == FALSE)
+ return FALSE;
+ return TRUE;
+}
+
+int
+diskLabelEditor(dialogMenuItem *self)
+{
+ DMenu *menu;
+ Device **devs;
+ int i, cnt;
+
+ i = 0;
+ cnt = diskGetSelectCount(&devs);
+ if (cnt == -1) {
+ msgConfirm("No disks found! Please verify that your disk controller is being\n"
+ "properly probed at boot time. See the Hardware Guide on the\n"
+ "Documentation menu for clues on diagnosing this type of problem.");
+ return DITEM_FAILURE;
+ }
+ else if (cnt) {
+ /* Some are already selected */
+ if (variable_get(VAR_NONINTERACTIVE) &&
+ !variable_get(VAR_DISKINTERACTIVE))
+ i = diskLabelNonInteractive(NULL);
+ else
+ i = diskLabel(NULL);
+ }
+ else {
+ /* No disks are selected, fall-back case now */
+ cnt = deviceCount(devs);
+ if (cnt == 1) {
+ devs[0]->enabled = TRUE;
+ if (variable_get(VAR_NONINTERACTIVE) &&
+ !variable_get(VAR_DISKINTERACTIVE))
+ i = diskLabelNonInteractive(devs[0]);
+ else
+ i = diskLabel(devs[0]);
+ }
+ else {
+ menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, labelHook, labelCheck);
+ if (!menu) {
+ msgConfirm("No devices suitable for installation found!\n\n"
+ "Please verify that your disk controller (and attached drives)\n"
+ "were detected properly. This can be done by pressing the\n"
+ "[Scroll Lock] key and using the Arrow keys to move back to\n"
+ "the boot messages. Press [Scroll Lock] again to return.");
+ i = DITEM_FAILURE;
+ }
+ else {
+ i = dmenuOpenSimple(menu, FALSE) ? DITEM_SUCCESS : DITEM_FAILURE;
+ free(menu);
+ }
+ }
+ }
+ if (DITEM_STATUS(i) != DITEM_FAILURE) {
+ if (variable_cmp(DISK_LABELLED, "written"))
+ variable_set2(DISK_LABELLED, "yes", 0);
+ }
+ return i;
+}
+
+int
+diskLabelCommit(dialogMenuItem *self)
+{
+ char *cp;
+ int i;
+
+ /* Already done? */
+ if ((cp = variable_get(DISK_LABELLED)) && strcmp(cp, "yes"))
+ i = DITEM_SUCCESS;
+ else if (!cp) {
+ msgConfirm("You must assign disk labels before this option can be used.");
+ i = DITEM_FAILURE;
+ }
+ /* The routine will guard against redundant writes, just as this one does */
+ else if (DITEM_STATUS(diskPartitionWrite(self)) != DITEM_SUCCESS)
+ i = DITEM_FAILURE;
+ else if (DITEM_STATUS(installFilesystems(self)) != DITEM_SUCCESS)
+ i = DITEM_FAILURE;
+ else {
+ msgInfo("All filesystem information written successfully.");
+ variable_set2(DISK_LABELLED, "written", 0);
+ i = DITEM_SUCCESS;
+ }
+ return i;
+}
+
+/* See if we're already using a desired partition name */
+static Boolean
+check_conflict(char *name)
+{
+ int i;
+
+ for (i = 0; label_chunk_info[i].c; i++)
+ if ((label_chunk_info[i].type == PART_FILESYSTEM || label_chunk_info[i].type == PART_FAT)
+ && label_chunk_info[i].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 ");
+ strcat(ret->newfs_cmd, variable_get(VAR_NEWFS_ARGS));
+ ret->newfs = newfs;
+ ret->soft = strcmp(mpoint, "/") ? 1 : 0;
+ 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;
+ val = msgGetInput(tmp ? tmp->mountpoint : NULL, "Please specify a mount point for the partition");
+ if (!val || !*val) {
+ if (!old)
+ return NULL;
+ else {
+ free(old->private_data);
+ old->private_data = NULL;
+ }
+ return NULL;
+ }
+
+ /* Is it just the same value? */
+ if (tmp && !strcmp(tmp->mountpoint, val))
+ return NULL;
+
+ /* Did we use it already? */
+ if (check_conflict(val)) {
+ msgConfirm("You already have a mount point for %s assigned!", val);
+ return NULL;
+ }
+
+ /* Is it bogus? */
+ if (*val != '/') {
+ msgConfirm("Mount point must start with a / character");
+ return NULL;
+ }
+
+ /* Is it going to be mounted on root? */
+ if (!strcmp(val, "/")) {
+ if (old)
+ old->flags |= CHUNK_IS_ROOT;
+ }
+ else if (old)
+ old->flags &= ~CHUNK_IS_ROOT;
+
+ safe_free(tmp);
+ val = string_skipwhite(string_prune(val));
+ 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.",
+ };
+ WINDOW *w = savescr();
+
+ i = dialog_menu("Please choose a partition type",
+ "If you want to use this partition for swap space, select Swap.\n"
+ "If you want to put a filesystem on it, choose FS.",
+ -1, -1, 2, 2, fs_types, selection, NULL, NULL);
+ restorescr(w);
+ 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 9
+
+#define PART_PART_COL 0
+#define PART_MOUNT_COL 10
+#define PART_SIZE_COL (PART_MOUNT_COL + MAX_MOUNT_NAME + 3)
+#define PART_NEWFS_COL (PART_SIZE_COL + 8)
+#define PART_OFF 38
+
+#define TOTAL_AVAIL_LINES (10)
+#define PSLICE_SHOWABLE (4)
+
+
+/* stick this all up on the screen */
+static void
+print_label_chunks(void)
+{
+ int i, j, srow, prow, pcol;
+ 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) + 3, "Size");
+ mvaddstr(ChunkPartStartRow - 1, PART_SIZE_COL + (i * PART_OFF) + 3, "----");
+
+ mvaddstr(ChunkPartStartRow - 2, PART_NEWFS_COL + (i * PART_OFF), "Newfs");
+ mvaddstr(ChunkPartStartRow - 1, PART_NEWFS_COL + (i * PART_OFF), "-----");
+ }
+ srow = CHUNK_SLICE_START_ROW;
+ prow = 0;
+ pcol = 0;
+
+ /*** these variables indicate that the focused item is shown currently ***/
+ label_focus_found = 0;
+ pslice_focus_found = 0;
+
+ label_count = 0;
+ pslice_count = 0;
+ mvprintw(CHUNK_SLICE_START_ROW - 1, 0, " ");
+ mvprintw(CHUNK_SLICE_START_ROW + pslice_max, 0, " ");
+
+ ChunkWin = newwin(CHUNK_ROW_MAX - ChunkPartStartRow, 76, ChunkPartStartRow, 0);
+
+ wclear(ChunkWin);
+ /*** wrefresh(ChunkWin); ***/
+
+ for (i = 0; label_chunk_info[i].c; i++) {
+ /* Is it a slice entry displayed at the top? */
+ if (label_chunk_info[i].type == PART_SLICE) {
+ /*** This causes the new pslice to replace the previous display ***/
+ /*** focus must remain on the most recently active pslice ***/
+ if (pslice_count == pslice_max) {
+ if (pslice_focus_found) {
+ /*** This is where we can mark the more following ***/
+ attrset(A_BOLD);
+ mvprintw(CHUNK_SLICE_START_ROW + pslice_max, 0, "***MORE***");
+ attrset(A_NORMAL);
+ continue;
+ }
+ else {
+ /*** this is where we set the more previous ***/
+ attrset(A_BOLD);
+ mvprintw(CHUNK_SLICE_START_ROW - 1, 0, "***MORE***");
+ attrset(A_NORMAL);
+ pslice_count = 0;
+ srow = CHUNK_SLICE_START_ROW;
+ }
+ }
+
+ sz = space_free(label_chunk_info[i].c);
+ if (i == here)
+ attrset(ATTR_SELECTED);
+ if (i == pslice_focus)
+ pslice_focus_found = -1;
+
+ 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[10];
+
+ /*
+ * 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)
+ strcpy(newfs, "DOS");
+ else if (label_chunk_info[i].c->private_data && label_chunk_info[i].type == PART_FILESYSTEM) {
+ strcpy(newfs, "UFS");
+ strcat(newfs,
+ ((PartInfo *)label_chunk_info[i].c->private_data)->soft ?
+ "+S" : " ");
+ strcat(newfs,
+ ((PartInfo *)label_chunk_info[i].c->private_data)->newfs ?
+ " Y" : " N");
+ }
+ else if (label_chunk_info[i].type == PART_SWAP)
+ strcpy(newfs, "SWAP");
+ else
+ strcpy(newfs, "*");
+ for (j = 0; j < MAX_MOUNT_NAME && mountpoint[j]; j++)
+ onestr[PART_MOUNT_COL + j] = mountpoint[j];
+ snprintf(num, 10, "%5ldMB", 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 expensively padding this string ***/
+ while (strlen(onestr) < 37)
+ strcat(onestr, " ");
+
+ mvwaddstr(ChunkWin, prow, pcol, onestr);
+ wattrset(ChunkWin, A_NORMAL);
+ move(0, 0);
+ ++prow;
+ ++label_count;
+ }
+ }
+
+ /*** this will erase all the extra stuff ***/
+ memset(clrmsg, ' ', 37);
+ clrmsg[37] = '\0';
+
+ while (pslice_count < pslice_max) {
+ mvprintw(srow++, 0, clrmsg);
+ clrtoeol();
+ ++pslice_count;
+ }
+ while (label_count < (2 * (label_max - 1))) {
+ mvwaddstr(ChunkWin, prow++, pcol, clrmsg);
+ ++label_count;
+ if (prow == (label_max - 1)) {
+ prow = 0;
+ pcol = PART_OFF;
+ }
+ }
+ refresh();
+ wrefresh(ChunkWin);
+}
+
+static void
+print_command_summary(void)
+{
+ mvprintw(17, 0, "The following commands are valid here (upper or lower case):");
+ mvprintw(18, 0, "C = Create D = Delete M = Mount pt.");
+ if (!RunningAsInit)
+ mvprintw(18, 47, "W = Write");
+ mvprintw(19, 0, "N = Newfs Opts Q = Finish S = Toggle SoftUpdates");
+ mvprintw(20, 0, "T = Toggle Newfs U = Undo A = Auto Defaults R = Delete+Merge");
+ mvprintw(22, 0, "Use F1 or ? to get more help, arrow keys to select.");
+ move(0, 0);
+}
+
+static void
+clear_wins(void)
+{
+ extern void print_label_chunks();
+ clear();
+ print_label_chunks();
+}
+
+#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
+ WINDOW *w = savescr();
+
+ label_focus = 0;
+ pslice_focus = 0;
+ here = 0;
+
+ devs = deviceFind(NULL, DEVICE_TYPE_DISK);
+ if (!devs) {
+ msgConfirm("No disks found!");
+ restorescr(w);
+ return DITEM_FAILURE;
+ }
+ labeling = TRUE;
+ keypad(stdscr, TRUE);
+#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;
+ int rflags = DELCHUNK_NORMAL;
+
+ print_label_chunks();
+ print_command_summary();
+ if (msg) {
+ attrset(title_attr); mvprintw(23, 0, msg); attrset(A_NORMAL);
+ clrtoeol();
+ beep();
+ msg = NULL;
+ }
+ else {
+ move(23, 0);
+ clrtoeol();
+ }
+
+ refresh();
+ key = getch();
+ switch (toupper(key)) {
+ int i;
+ static char _msg[40];
+
+ case '\014': /* ^L */
+ clear_wins();
+ break;
+
+ case '\020': /* ^P */
+ case KEY_UP:
+ case '-':
+ if (here != 0)
+ --here;
+ else
+ while (label_chunk_info[here + 1].c)
+ ++here;
+ break;
+
+ case '\016': /* ^N */
+ case KEY_DOWN:
+ case '+':
+ case '\r':
+ case '\n':
+ if (label_chunk_info[here + 1].c)
+ ++here;
+ else
+ here = 0;
+ break;
+
+ case KEY_HOME:
+ here = 0;
+ break;
+
+ case KEY_END:
+ while (label_chunk_info[here + 1].c)
+ ++here;
+ break;
+
+ case KEY_F(1):
+ case '?':
+ systemDisplayHelp("partition");
+ clear_wins();
+ break;
+
+ case 'A':
+ if (label_chunk_info[here].type != PART_SLICE) {
+ msg = "You can only do this in a disk slice (at top of screen)";
+ break;
+ }
+ /*
+ * Generate standard partitions automatically. If we do not
+ * have sufficient space we attempt to scale-down the size
+ * of the partitions within certain bounds.
+ */
+ {
+ int perc;
+ int req = 0;
+
+ for (perc = 100; perc > 0; perc -= 5) {
+ req = 0; /* reset for each loop */
+ if ((msg = try_auto_label(devs, dev, perc, &req)) == NULL)
+ break;
+ }
+ if (msg) {
+ if (req) {
+ msgConfirm(msg);
+ clear_wins();
+ msg = NULL;
+ }
+ }
+ }
+ break;
+
+ case 'C':
+ if (label_chunk_info[here].type != PART_SLICE) {
+ msg = "You can only do this in a master partition (see top of screen)";
+ break;
+ }
+ sz = space_free(label_chunk_info[here].c);
+ if (sz <= FS_MIN_SIZE) {
+ msg = "Not enough space to create an additional FreeBSD partition";
+ break;
+ }
+ else {
+ char *val;
+ int size;
+ struct chunk *tmp;
+ char osize[80];
+ u_long flags = 0;
+
+ sprintf(osize, "%d", sz);
+ val = msgGetInput(osize,
+ "Please specify the partition size in blocks or append a trailing G for\n"
+ "gigabytes, M for megabytes, or C for cylinders.\n"
+ "%d blocks (%dMB) are free.",
+ sz, sz / ONE_MEG);
+ if (!val || (size = strtol(val, &cp, 0)) <= 0) {
+ clear_wins();
+ break;
+ }
+
+ if (*cp) {
+ if (toupper(*cp) == 'M')
+ size *= ONE_MEG;
+ else if (toupper(*cp) == 'G')
+ size *= ONE_GIG;
+ 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) && (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;
+ }
+
+#ifdef __alpha__
+ /*
+ * SRM requires that the root partition is at the
+ * begining of the disk and cannot boot otherwise.
+ * Warn Alpha users if they are about to shoot themselves in
+ * the foot in this way.
+ *
+ * Since partitions may not start precisely at offset 0 we
+ * check for a "close to 0" instead. :-(
+ */
+ if ((flags & CHUNK_IS_ROOT) && (tmp->offset > 1024)) {
+ msgConfirm("Your root partition `a' does not seem to be the first\n"
+ "partition. The Alpha's firmware can only boot from the\n"
+ "first partition. So it is unlikely that your current\n"
+ "disk layout will be bootable boot after installation.\n"
+ "\n"
+ "Please allocate the root partition before allocating\n"
+ "any others.\n");
+ }
+#endif /* alpha */
+
+ 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 (variable_cmp(DISK_LABELLED, "written"))
+ variable_set2(DISK_LABELLED, "yes", 0);
+ record_label_chunks(devs, dev);
+ clear_wins();
+ /* This is where we assign focus to new label so it shows. */
+ {
+ int i;
+ label_focus = -1;
+ for (i = 0; label_chunk_info[i].c; ++i) {
+ if (label_chunk_info[i].c == tmp) {
+ label_focus = i;
+ break;
+ }
+ }
+ if (label_focus == -1)
+ label_focus = i - 1;
+ }
+ }
+ break;
+
+ case KEY_DC:
+ case 'R': /* recover space (delete w/ recover) */
+ /*
+ * Delete the partition w/ space recovery.
+ */
+ rflags = DELCHUNK_RECOVER;
+ /* fall through */
+ case 'D': /* delete */
+ if (label_chunk_info[here].type == PART_SLICE) {
+ msg = MSG_NOT_APPLICABLE;
+ break;
+ }
+ else if (label_chunk_info[here].type == PART_FAT) {
+ msg = "Use the Disk Partition Editor to delete DOS partitions";
+ break;
+ }
+ Delete_Chunk2(label_chunk_info[here].c->disk, label_chunk_info[here].c, rflags);
+ if (variable_cmp(DISK_LABELLED, "written"))
+ variable_set2(DISK_LABELLED, "yes", 0);
+ record_label_chunks(devs, dev);
+ break;
+
+ case 'M': /* mount */
+ switch(label_chunk_info[here].type) {
+ case PART_SLICE:
+ msg = MSG_NOT_APPLICABLE;
+ break;
+
+ case PART_SWAP:
+ msg = "You don't need to specify a mountpoint for a swap partition.";
+ break;
+
+ case PART_FAT:
+ case PART_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 (variable_cmp(DISK_LABELLED, "written"))
+ variable_set2(DISK_LABELLED, "yes", 0);
+ record_label_chunks(devs, dev);
+ clear_wins();
+ break;
+
+ default:
+ msgFatal("Bogus partition under cursor???");
+ break;
+ }
+ break;
+
+ case 'N': /* Set newfs options */
+ if (label_chunk_info[here].c->private_data &&
+ ((PartInfo *)label_chunk_info[here].c->private_data)->newfs)
+ getNewfsCmd(label_chunk_info[here].c->private_data);
+ else
+ msg = MSG_NOT_APPLICABLE;
+ clear_wins();
+ break;
+
+ case 'S': /* Toggle soft updates flag */
+ if (label_chunk_info[here].type == PART_FILESYSTEM) {
+ PartInfo *pi = ((PartInfo *)label_chunk_info[here].c->private_data);
+ if (pi)
+ pi->soft = !pi->soft;
+ else
+ msg = MSG_NOT_APPLICABLE;
+ }
+ else
+ msg = MSG_NOT_APPLICABLE;
+ break;
+
+ case 'T': /* Toggle newfs state */
+ if ((label_chunk_info[here].type == PART_FILESYSTEM) &&
+ (label_chunk_info[here].c->private_data)) {
+ PartInfo *pi = ((PartInfo *)label_chunk_info[here].c->private_data);
+ if (!pi->newfs)
+ label_chunk_info[here].c->flags |= CHUNK_NEWFS;
+ else
+ label_chunk_info[here].c->flags &= ~CHUNK_NEWFS;
+
+ label_chunk_info[here].c->private_data =
+ new_part(pi ? pi->mountpoint : NULL, pi ? !pi->newfs : TRUE, label_chunk_info[here].c->size);
+ if (pi && pi->soft)
+ ((PartInfo *)label_chunk_info[here].c->private_data)->soft = 1;
+ safe_free(pi);
+ label_chunk_info[here].c->private_free = safe_free;
+ if (variable_cmp(DISK_LABELLED, "written"))
+ variable_set2(DISK_LABELLED, "yes", 0);
+ }
+ else
+ msg = MSG_NOT_APPLICABLE;
+ break;
+
+ case 'U':
+ clear();
+ if (!variable_cmp(DISK_LABELLED, "written")) {
+ msgConfirm("You've already written out your changes -\n"
+ "it's too late to undo!");
+ }
+ else if (!msgNoYes("Are you SURE you want to Undo everything?")) {
+ variable_unset(DISK_PARTITIONED);
+ variable_unset(DISK_LABELLED);
+ for (i = 0; devs[i]; i++) {
+ Disk *d;
+
+ if (!devs[i]->enabled)
+ continue;
+ else if ((d = Open_Disk(devs[i]->name)) != NULL) {
+ Free_Disk(devs[i]->private);
+ devs[i]->private = d;
+ diskPartition(devs[i]);
+ }
+ }
+ record_label_chunks(devs, dev);
+ }
+ clear_wins();
+ break;
+
+ case 'W':
+ if (!variable_cmp(DISK_LABELLED, "written")) {
+ msgConfirm("You've already written out your changes - if you\n"
+ "wish to overwrite them, you'll have to restart\n"
+ "sysinstall first.");
+ }
+ else if (!msgNoYes("WARNING: This should only be used when modifying an EXISTING\n"
+ "installation. If you are installing FreeBSD for the first time\n"
+ "then you should simply type Q when you're finished here and your\n"
+ "changes will be committed in one batch automatically at the end of\n"
+ "these questions.\n\n"
+ "Are you absolutely sure you want to do this now?")) {
+ variable_set2(DISK_LABELLED, "yes", 0);
+ diskLabelCommit(NULL);
+ }
+ clear_wins();
+ break;
+
+ case '|':
+ if (!msgNoYes("Are you sure you want to go into Wizard mode?\n\n"
+ "This is an entirely undocumented feature which you are not\n"
+ "expected to understand!")) {
+ int i;
+ Device **devs;
+
+ dialog_clear();
+ end_dialog();
+ DialogActive = FALSE;
+ devs = deviceFind(NULL, DEVICE_TYPE_DISK);
+ if (!devs) {
+ msgConfirm("Can't find any disk devices!");
+ break;
+ }
+ for (i = 0; devs[i] && ((Disk *)devs[i]->private); i++) {
+ if (devs[i]->enabled)
+ slice_wizard(((Disk *)devs[i]->private));
+ }
+ if (variable_cmp(DISK_LABELLED, "written"))
+ variable_set2(DISK_LABELLED, "yes", 0);
+ DialogActive = TRUE;
+ record_label_chunks(devs, dev);
+ clear_wins();
+ }
+ else
+ msg = "A most prudent choice!";
+ break;
+
+ case '\033': /* ESC */
+ case 'Q':
+ labeling = FALSE;
+ break;
+
+ default:
+ beep();
+ sprintf(_msg, "Invalid key %d - Type F1 or ? for help", key);
+ msg = _msg;
+ break;
+ }
+ if (label_chunk_info[here].type == PART_SLICE)
+ pslice_focus = here;
+ else
+ label_focus = here;
+ }
+ restorescr(w);
+ return DITEM_SUCCESS;
+}
+
+static __inline int
+requested_part_size(char *varName, int nom, int def, int perc)
+{
+ char *cp;
+ int sz;
+
+ if ((cp = variable_get(varName)) != NULL)
+ sz = atoi(cp);
+ else
+ sz = nom + (def - nom) * perc / 100;
+ return(sz * ONE_MEG);
+}
+
+/*
+ * Attempt to auto-label the disk. 'perc' (0-100) scales
+ * the size of the various partitions within appropriate
+ * bounds (NOMINAL through DEFAULT sizes). The procedure
+ * succeeds of NULL is returned. A non-null return message
+ * is either a failure-status message (*req == 0), or
+ * a confirmation requestor (*req == 1). *req is 0 on
+ * entry to this call.
+ *
+ * We autolabel the following partitions: /, swap, /var, /tmp, /usr,
+ * and /home. /home receives any extra left over disk space.
+ */
+static char *
+try_auto_label(Device **devs, Device *dev, int perc, int *req)
+{
+ int sz;
+ struct chunk *root_chunk = NULL;
+ struct chunk *swap_chunk = NULL;
+ struct chunk *usr_chunk = NULL;
+ struct chunk *var_chunk = NULL;
+ struct chunk *tmp_chunk = NULL;
+ struct chunk *home_chunk = NULL;
+ int mib[2];
+ unsigned int physmem;
+ size_t size;
+ Chunk *rootdev, *swapdev, *usrdev, *vardev;
+ Chunk *tmpdev, *homedev;
+ char *msg = NULL;
+
+ sz = space_free(label_chunk_info[here].c);
+ if (sz <= FS_MIN_SIZE)
+ return("Not enough free space to create a new partition in the slice");
+
+ (void)checkLabels(FALSE, &rootdev, &swapdev, &usrdev,
+ &vardev, &tmpdev, &homedev);
+ if (!rootdev) {
+ sz = requested_part_size(VAR_ROOT_SIZE, ROOT_NOMINAL_SIZE, ROOT_DEFAULT_SIZE, perc);
+
+ root_chunk = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
+ label_chunk_info[here].c, sz, part,
+ FS_BSDFFS, CHUNK_IS_ROOT | CHUNK_AUTO_SIZE);
+ if (!root_chunk) {
+ *req = 1;
+ msg = "Unable to create the root partition. Too big?";
+ goto done;
+ }
+ root_chunk->private_data = new_part("/", TRUE, root_chunk->size);
+ root_chunk->private_free = safe_free;
+ root_chunk->flags |= CHUNK_NEWFS;
+ record_label_chunks(devs, dev);
+ }
+ if (!swapdev) {
+ sz = requested_part_size(VAR_SWAP_SIZE, 0, 0, perc);
+ if (sz == 0) {
+ int nom;
+ int def;
+
+ mib[0] = CTL_HW;
+ mib[1] = HW_PHYSMEM;
+ size = sizeof physmem;
+ sysctl(mib, 2, &physmem, &size, (void *)0, (size_t)0);
+ def = 2 * (int)(physmem / 512);
+ if (def < SWAP_MIN_SIZE * ONE_MEG)
+ def = SWAP_MIN_SIZE * ONE_MEG;
+ if (def > SWAP_AUTO_LIMIT_SIZE * ONE_MEG)
+ def = SWAP_AUTO_LIMIT_SIZE * ONE_MEG;
+ nom = (int)(physmem / 512) / 2;
+ sz = nom + (def - nom) * perc / 100;
+ }
+ swap_chunk = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
+ label_chunk_info[here].c, sz, part,
+ FS_SWAP, CHUNK_AUTO_SIZE);
+ if (!swap_chunk) {
+ *req = 1;
+ msg = "Unable to create the swap partition. Too big?";
+ goto done;
+ }
+ swap_chunk->private_data = 0;
+ swap_chunk->private_free = safe_free;
+ record_label_chunks(devs, dev);
+ }
+ if (!vardev) {
+ sz = requested_part_size(VAR_VAR_SIZE, VAR_NOMINAL_SIZE, VAR_DEFAULT_SIZE, perc);
+
+ var_chunk = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
+ label_chunk_info[here].c, sz, part,
+ FS_BSDFFS, CHUNK_AUTO_SIZE);
+ if (!var_chunk) {
+ *req = 1;
+ msg = "Not enough free space for /var - you will need to\n"
+ "partition your disk manually with a custom install!";
+ goto done;
+ }
+ var_chunk->private_data = new_part("/var", TRUE, var_chunk->size);
+ var_chunk->private_free = safe_free;
+ var_chunk->flags |= CHUNK_NEWFS;
+ record_label_chunks(devs, dev);
+ }
+ if (!tmpdev && !variable_get(VAR_NO_TMP)) {
+ sz = requested_part_size(VAR_TMP_SIZE, TMP_NOMINAL_SIZE, TMP_DEFAULT_SIZE, perc);
+
+ tmp_chunk = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
+ label_chunk_info[here].c, sz, part,
+ FS_BSDFFS, CHUNK_AUTO_SIZE);
+ if (!tmp_chunk) {
+ *req = 1;
+ msg = "Not enough free space for /tmp - you will need to\n"
+ "partition your disk manually with a custom install!";
+ goto done;
+ }
+ tmp_chunk->private_data = new_part("/tmp", TRUE, tmp_chunk->size);
+ tmp_chunk->private_free = safe_free;
+ tmp_chunk->flags |= CHUNK_NEWFS;
+ record_label_chunks(devs, dev);
+ }
+ if (!usrdev && !variable_get(VAR_NO_USR)) {
+ sz = requested_part_size(VAR_USR_SIZE, USR_NOMINAL_SIZE, USR_DEFAULT_SIZE, perc);
+#if AUTO_HOME == 0
+ sz = space_free(label_chunk_info[here].c);
+#endif
+ if (sz) {
+ if (sz < (USR_MIN_SIZE * ONE_MEG)) {
+ *req = 1;
+ msg = "Not enough free space for /usr - you will need to\n"
+ "partition your disk manually with a custom install!";
+ }
+
+ usr_chunk = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
+ label_chunk_info[here].c, sz, part,
+ FS_BSDFFS, CHUNK_AUTO_SIZE);
+ if (!usr_chunk) {
+ msg = "Unable to create the /usr partition. Not enough space?\n"
+ "You will need to partition your disk manually with a custom install!";
+ goto done;
+ }
+ usr_chunk->private_data = new_part("/usr", TRUE, usr_chunk->size);
+ usr_chunk->private_free = safe_free;
+ usr_chunk->flags |= CHUNK_NEWFS;
+ record_label_chunks(devs, dev);
+ }
+ }
+#if AUTO_HOME == 1
+ if (!homedev && !variable_get(VAR_NO_HOME)) {
+ sz = requested_part_size(VAR_HOME_SIZE, HOME_NOMINAL_SIZE, HOME_DEFAULT_SIZE, perc);
+ if (sz < space_free(label_chunk_info[here].c))
+ sz = space_free(label_chunk_info[here].c);
+ if (sz) {
+ if (sz < (HOME_MIN_SIZE * ONE_MEG)) {
+ *req = 1;
+ msg = "Not enough free space for /home - you will need to\n"
+ "partition your disk manually with a custom install!";
+ goto done;
+ }
+
+ home_chunk = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
+ label_chunk_info[here].c, sz, part,
+ FS_BSDFFS, CHUNK_AUTO_SIZE);
+ if (!home_chunk) {
+ msg = "Unable to create the /home partition. Not enough space?\n"
+ "You will need to partition your disk manually with a custom install!";
+ goto done;
+ }
+ home_chunk->private_data = new_part("/home", TRUE, home_chunk->size);
+ home_chunk->private_free = safe_free;
+ home_chunk->flags |= CHUNK_NEWFS;
+ record_label_chunks(devs, dev);
+ }
+ }
+#endif
+
+ /* At this point, we're reasonably "labelled" */
+ if (variable_cmp(DISK_LABELLED, "written"))
+ variable_set2(DISK_LABELLED, "yes", 0);
+
+done:
+ if (msg) {
+ if (root_chunk)
+ Delete_Chunk(root_chunk->disk, root_chunk);
+ if (swap_chunk)
+ Delete_Chunk(swap_chunk->disk, swap_chunk);
+ if (var_chunk)
+ Delete_Chunk(var_chunk->disk, var_chunk);
+ if (tmp_chunk)
+ Delete_Chunk(tmp_chunk->disk, tmp_chunk);
+ if (usr_chunk)
+ Delete_Chunk(usr_chunk->disk, usr_chunk);
+ if (home_chunk)
+ Delete_Chunk(home_chunk->disk, home_chunk);
+ record_label_chunks(devs, dev);
+ }
+ return(msg);
+}
+
+static int
+diskLabelNonInteractive(Device *dev)
+{
+ char *cp;
+ PartType type;
+ PartInfo *p;
+ u_long flags = 0;
+ int i, status;
+ Device **devs;
+ Disk *d;
+
+ status = DITEM_SUCCESS;
+ cp = variable_get(VAR_DISK);
+ if (!cp) {
+ msgConfirm("diskLabel: No disk selected - can't label automatically.");
+ return DITEM_FAILURE;
+ }
+ devs = deviceFind(cp, DEVICE_TYPE_DISK);
+ if (!devs) {
+ msgConfirm("diskLabel: No disk device %s found!", cp);
+ return DITEM_FAILURE;
+ }
+ if (dev)
+ d = dev->private;
+ else
+ d = devs[0]->private;
+#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, soft = 0;
+ char typ[10], mpoint[50];
+
+ if (sscanf(cp, "%s %d %s %d", typ, &sz, mpoint, &soft) < 3) {
+ msgConfirm("For slice entry %s, got an invalid detail entry of: %s", c1->name, cp);
+ status = DITEM_FAILURE;
+ 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;
+ ((PartInfo *)tmp->private_data)->soft = soft;
+ 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) {
+ 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..8300173
--- /dev/null
+++ b/usr.sbin/sade/list.h
@@ -0,0 +1,60 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated for what's essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1997 FreeBSD, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY PAUL TRAINA ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PAUL TRAINA OR HIS KILLER RATS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/* The structure */
+typedef struct _qelement {
+ struct _qelement *q_forw;
+ struct _qelement *q_back;
+} qelement;
+
+#define INITQUE(Xhead) { \
+ (Xhead).q_forw = &(Xhead); \
+ (Xhead).q_back = &(Xhead); \
+}
+
+#define EMPTYQUE(Xhead) \
+ ((Xhead).q_forw == &(Xhead))
+
+#define INSQUEUE(elem, pred) { \
+ register qelement *Xe = (qelement *) (elem); \
+ register qelement *Xp = (qelement *) (pred); \
+ Xp->q_forw = (Xe->q_forw = (Xe->q_back = Xp)->q_forw)->q_back = Xe; \
+}
+
+#define REMQUE(elem) { \
+ register qelement *Xe = (qelement *) (elem); \
+ (Xe->q_back->q_forw = Xe->q_forw)->q_back = Xe->q_back; \
+}
diff --git a/usr.sbin/sade/main.c b/usr.sbin/sade/main.c
new file mode 100644
index 0000000..144a617
--- /dev/null
+++ b/usr.sbin/sade/main.c
@@ -0,0 +1,179 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated for what's essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <sys/signal.h>
+#include <sys/fcntl.h>
+
+const char *StartName; /* Initial contents of argv[0] */
+
+static void
+screech(int sig)
+{
+ msgDebug("\007Signal %d caught! That's bad!\n", sig);
+ longjmp(BailOut, sig);
+}
+
+int
+main(int argc, char **argv)
+{
+ int choice, scroll, curr, max, status;
+
+ /* Record name to be able to restart */
+ StartName = argv[0];
+
+ /* Catch fatal signals and complain about them if running as init */
+ if (getpid() == 1) {
+ signal(SIGBUS, screech);
+ signal(SIGSEGV, screech);
+ }
+ signal(SIGPIPE, SIG_IGN);
+
+ /* We don't work too well when running as non-root anymore */
+ if (geteuid() != 0) {
+ fprintf(stderr, "Error: This utility should only be run as root.\n");
+ return 1;
+ }
+
+#ifdef PC98
+ {
+ /* XXX */
+ char *p = getenv("TERM");
+ if (p && strcmp(p, "cons25") == 0)
+ putenv("TERM=cons25w");
+ }
+#endif
+
+ /* Set up whatever things need setting up */
+ systemInitialize(argc, argv);
+
+ /* Set default flag and variable values */
+ installVarDefaults(NULL);
+ /* only when multi-user is it reasonable to do this here */
+ if (!RunningAsInit)
+ installEnvironment();
+
+ if (argc > 1 && !strcmp(argv[1], "-fake")) {
+ variable_set2(VAR_DEBUG, "YES", 0);
+ Fake = TRUE;
+ msgConfirm("I'll be just faking it from here on out, OK?");
+ }
+ if (argc > 1 && !strcmp(argv[1], "-restart"))
+ Restarting = TRUE;
+
+ /* Try to preserve our scroll-back buffer */
+ if (OnVTY) {
+ for (curr = 0; curr < 25; curr++)
+ putchar('\n');
+ }
+ /* Move stderr aside */
+ if (DebugFD)
+ dup2(DebugFD, 2);
+
+ /* Initialize driver modules, if we haven't already done so (ie,
+ the user hit Ctrl-C -> Restart. */
+ if (!pvariable_get("modulesInitialize")) {
+ moduleInitialize();
+ pvariable_set("modulesInitialize=1");
+ }
+
+ /* Initialize PC-card, if we haven't already done so. */
+#ifdef PCCARD_ARCH
+ if (!pvariable_get("pccardInitialize")) {
+ pccardInitialize();
+ pvariable_set("pccardInitialize=1");
+ }
+#endif
+
+ /* Initialize USB, if we haven't already done so. */
+ if (!pvariable_get("usbInitialize")) {
+ usbInitialize();
+ pvariable_set("usbInitialize=1");
+ }
+
+ /* Probe for all relevant devices on the system */
+ deviceGetAll();
+
+ /* First, see if we have any arguments to process (and argv[0] counts if it's not "sysinstall") */
+ if (!RunningAsInit) {
+ int i, start_arg;
+
+ if (!strstr(argv[0], "sysinstall"))
+ start_arg = 0;
+ else if (Fake || Restarting)
+ start_arg = 2;
+ else
+ start_arg = 1;
+ for (i = start_arg; i < argc; i++) {
+ if (DITEM_STATUS(dispatchCommand(argv[i])) != DITEM_SUCCESS)
+ systemShutdown(1);
+ }
+ if (argc > start_arg)
+ systemShutdown(0);
+ }
+ else
+ dispatch_load_file_int(TRUE);
+
+ status = setjmp(BailOut);
+ if (status) {
+ msgConfirm("A signal %d was caught - I'm saving what I can and shutting\n"
+ "down. If you can reproduce the problem, please turn Debug on\n"
+ "in the Options menu for the extra information it provides\n"
+ "in debugging problems like this.", status);
+ systemShutdown(status);
+ }
+
+ /* Begin user dialog at outer menu */
+ dialog_clear();
+ while (1) {
+ choice = scroll = curr = max = 0;
+ dmenuOpen(&MenuInitial, &choice, &scroll, &curr, &max, TRUE);
+ if (getpid() != 1
+#ifdef __alpha__
+ || !msgNoYes("Are you sure you wish to exit? The system will halt.")
+#else
+ || !msgNoYes("Are you sure you wish to exit? The system will reboot\n"
+ "(be sure to remove any floppies/CDs/DVDs from the drives).")
+#endif
+ )
+ break;
+ }
+
+ /* Say goodnight, Gracie */
+ systemShutdown(0);
+
+ return 0; /* We should never get here */
+}
diff --git a/usr.sbin/sade/menus.c b/usr.sbin/sade/menus.c
new file mode 100644
index 0000000..1b6132a
--- /dev/null
+++ b/usr.sbin/sade/menus.c
@@ -0,0 +1,2157 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif
+
+#include "sysinstall.h"
+
+/* Miscellaneous work routines for menus */
+static int
+setSrc(dialogMenuItem *self)
+{
+ Dists |= DIST_SRC;
+ SrcDists = DIST_SRC_ALL;
+ CRYPTODists |= (DIST_CRYPTO_SCRYPTO | DIST_CRYPTO_SSECURE |
+ DIST_CRYPTO_SKERBEROS4 | DIST_CRYPTO_SKERBEROS5);
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+clearSrc(dialogMenuItem *self)
+{
+ Dists &= ~DIST_SRC;
+ SrcDists = 0;
+ CRYPTODists &= ~(DIST_CRYPTO_SCRYPTO | DIST_CRYPTO_SSECURE |
+ DIST_CRYPTO_SKERBEROS4 | DIST_CRYPTO_SKERBEROS5);
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+setX11Misc(dialogMenuItem *self)
+{
+ XF86Dists |= DIST_XF86_MISC_ALL;
+ Dists |= DIST_XF86;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+clearX11Misc(dialogMenuItem *self)
+{
+ XF86Dists &= ~DIST_XF86_MISC_ALL;
+ if (!XF86ServerDists && !XF86FontDists)
+ Dists &= ~DIST_XF86;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+setX11Servers(dialogMenuItem *self)
+{
+ XF86Dists |= DIST_XF86_SERVER;
+ XF86ServerDists = DIST_XF86_SERVER_ALL;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+clearX11Servers(dialogMenuItem *self)
+{
+ XF86Dists &= ~DIST_XF86_SERVER;
+ XF86ServerDists = 0;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+setX11Fonts(dialogMenuItem *self)
+{
+ XF86Dists |= DIST_XF86_FONTS;
+ XF86FontDists = DIST_XF86_FONTS_ALL;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+clearX11Fonts(dialogMenuItem *self)
+{
+ XF86Dists &= ~DIST_XF86_FONTS;
+ XF86FontDists = 0;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+#define _IS_SET(dist, set) (((dist) & (set)) == (set))
+
+#define IS_DEVELOPER(dist, extra) (_IS_SET(dist, _DIST_DEVELOPER | extra) || \
+ _IS_SET(dist, _DIST_DEVELOPER | extra))
+
+#define IS_USER(dist, extra) (_IS_SET(dist, _DIST_USER | extra) || \
+ _IS_SET(dist, _DIST_USER | extra))
+
+static int
+checkDistDeveloper(dialogMenuItem *self)
+{
+ return IS_DEVELOPER(Dists, 0) && _IS_SET(SrcDists, DIST_SRC_ALL);
+}
+
+static int
+checkDistXDeveloper(dialogMenuItem *self)
+{
+ return IS_DEVELOPER(Dists, DIST_XF86) && _IS_SET(SrcDists, DIST_SRC_ALL);
+}
+
+static int
+checkDistKernDeveloper(dialogMenuItem *self)
+{
+ return IS_DEVELOPER(Dists, 0) && _IS_SET(SrcDists, DIST_SRC_SYS);
+}
+
+static int
+checkDistXKernDeveloper(dialogMenuItem *self)
+{
+ return IS_DEVELOPER(Dists, DIST_XF86) && _IS_SET(SrcDists, DIST_SRC_SYS);
+}
+
+static int
+checkDistUser(dialogMenuItem *self)
+{
+ return IS_USER(Dists, 0);
+}
+
+static int
+checkDistXUser(dialogMenuItem *self)
+{
+ return IS_USER(Dists, DIST_XF86);
+}
+
+static int
+checkDistMinimum(dialogMenuItem *self)
+{
+ return Dists == (DIST_BASE | DIST_CRYPTO);
+}
+
+static int
+checkDistEverything(dialogMenuItem *self)
+{
+ return Dists == DIST_ALL && CRYPTODists == DIST_CRYPTO_ALL && \
+ _IS_SET(SrcDists, DIST_SRC_ALL) && \
+ _IS_SET(XF86Dists, DIST_XF86_ALL) && \
+ _IS_SET(XF86ServerDists, DIST_XF86_SERVER_ALL) && \
+ _IS_SET(XF86FontDists, DIST_XF86_FONTS_ALL);
+}
+
+static int
+srcFlagCheck(dialogMenuItem *item)
+{
+ return SrcDists;
+}
+
+static int
+x11FlagCheck(dialogMenuItem *item)
+{
+ return Dists & DIST_XF86;
+}
+
+static int
+checkTrue(dialogMenuItem *item)
+{
+ return TRUE;
+}
+
+/* All the system menus go here.
+ *
+ * Hardcoded things like version number strings will disappear from
+ * these menus just as soon as I add the code for doing inline variable
+ * expansion.
+ */
+
+DMenu MenuIndex = {
+ DMENU_NORMAL_TYPE,
+ "Glossary of functions",
+ "This menu contains an alphabetized index of the top level functions in\n"
+ "this program (sysinstall). Invoke an option by pressing [SPACE] or\n"
+ "[ENTER]. To exit, use [TAB] to move to the Cancel button.",
+ "Use PageUp or PageDown to move through this menu faster!",
+ NULL,
+ { { " Anon FTP", "Configure anonymous FTP logins.", dmenuVarCheck, configAnonFTP, NULL, "anon_ftp" },
+ { " Commit", "Commit any pending actions (dangerous!)", NULL, installCustomCommit },
+ { " 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, 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, Errata", "The distribution errata.", NULL, dmenuDisplayFile, NULL, "ERRATA" },
+ { " Doc, Hardware", "The distribution hardware guide.", NULL, dmenuDisplayFile, NULL, "HARDWARE" },
+ { " Doc, Install", "The distribution installation guide.", NULL, dmenuDisplayFile, NULL, "INSTALL" },
+ { " Doc, Copyright", "The distribution copyright notices.", NULL, dmenuDisplayFile, NULL, "COPYRIGHT" },
+ { " Doc, Release", "The distribution release notes.", NULL, dmenuDisplayFile, NULL, "RELNOTES" },
+ { " Doc, HTML", "The HTML documentation menu.", NULL, docBrowser },
+ { " Dump Vars", "(debugging) dump out internal variables.", NULL, dump_variables },
+ { " Emergency shell", "Start an Emergency Holographic shell.", NULL, installFixitHoloShell },
+#ifdef __i386__
+ { " Fdisk", "The disk Partition Editor", NULL, diskPartitionEditor },
+#endif
+ { " Fixit", "Repair mode with CDROM or fixit floppy.", NULL, dmenuSubmenu, NULL, &MenuFixit },
+ { " FTP sites", "The FTP mirror site listing.", NULL, dmenuSubmenu, NULL, &MenuMediaFTP },
+ { " Gateway", "Set flag to route packets between interfaces.", dmenuVarCheck, dmenuToggleVariable, NULL, "gateway=YES" },
+ { " HTML Docs", "The HTML documentation menu", NULL, docBrowser },
+ { " inetd Configuration", "Configure inetd and simple internet services.", dmenuVarCheck, configInetd, NULL, "inetd_enable=YES" },
+ { " Install, Standard", "A standard system installation.", NULL, installStandard },
+ { " Install, Express", "An express system installation.", NULL, installExpress },
+ { " Install, Custom", "The custom installation menu", NULL, dmenuSubmenu, NULL, &MenuInstallCustom },
+ { " Label", "The disk Label editor", NULL, diskLabelEditor },
+ { " Media", "Top level media selection menu.", NULL, dmenuSubmenu, NULL, &MenuMedia },
+ { " Media, Tape", "Select tape installation media.", NULL, mediaSetTape },
+ { " Media, NFS", "Select NFS installation media.", NULL, mediaSetNFS },
+ { " Media, Floppy", "Select floppy installation media.", NULL, mediaSetFloppy },
+ { " Media, CDROM/DVD", "Select CDROM/DVD installation media.", NULL, mediaSetCDROM },
+ { " Media, DOS", "Select DOS installation media.", NULL, mediaSetDOS },
+ { " Media, UFS", "Select UFS installation media.", NULL, mediaSetUFS },
+ { " Media, FTP", "Select FTP installation media.", NULL, mediaSetFTP },
+ { " Media, FTP Passive", "Select passive FTP installation media.", NULL, mediaSetFTPPassive },
+ { " Media, HTTP", "Select FTP via HTTP proxy installation media.", NULL, mediaSetHTTP },
+ { " Network Interfaces", "Configure network interfaces", NULL, tcpMenuSelect },
+ { " Networking Services", "The network services menu.", NULL, dmenuSubmenu, NULL, &MenuNetworking },
+ { " NFS, client", "Set NFS client flag.", dmenuVarCheck, dmenuToggleVariable, NULL, "nfs_client_enable=YES" },
+ { " NFS, server", "Set NFS server flag.", dmenuVarCheck, configNFSServer, NULL, "nfs_server_enable=YES" },
+ { " NTP Menu", "The NTP configuration menu.", NULL, dmenuSubmenu, NULL, &MenuNTP },
+ { " Options", "The options editor.", NULL, optionsEditor },
+ { " Packages", "The packages collection", NULL, configPackages },
+ { " Partition", "The disk Slice (PC-style partition) Editor", NULL, diskPartitionEditor },
+ { " PCNFSD", "Run authentication server for PC-NFS.", dmenuVarCheck, configPCNFSD, NULL, "pcnfsd" },
+ { " Root Password", "Set the system manager's password.", NULL, dmenuSystemCommand, NULL, "passwd root" },
+ { " Router", "Select routing daemon (default: routed)", NULL, configRouter, NULL, "router_enable" },
+ { " Security", "Configure system security options", NULL, dmenuSubmenu, NULL, &MenuSecurity },
+ { " 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 },
+ { " Syscons, Ttys", "The console terminal type menu.", NULL, dmenuSubmenu, NULL, &MenuSysconsTtys },
+ { " Time Zone", "Set the system's time zone.", NULL, dmenuSystemCommand, NULL, "tzsetup" },
+ { " TTYs", "Configure system ttys.", NULL, configEtcTtys, NULL, "ttys" },
+ { " Upgrade", "Upgrade an existing system.", NULL, installUpgrade },
+ { " Usage", "Quick start - How to use this menu system.", NULL, dmenuDisplayFile, NULL, "usage" },
+ { " User Management", "Add user and group information.", NULL, dmenuSubmenu, NULL, &MenuUsermgmt },
+ { " XFree86, Fonts", "XFree86 Font selection menu.", NULL, dmenuSubmenu, NULL, &MenuXF86SelectFonts },
+ { " XFree86, Server", "XFree86 Server selection menu.", NULL, dmenuSubmenu, NULL, &MenuXF86SelectServer },
+#if defined(__i386__) && defined(PC98)
+ { " XFree86, PC98 Server", "XFree86 PC98 Server selection menu.", NULL, dmenuSubmenu, NULL, &MenuXF86SelectPC98Server },
+#endif
+ { NULL } },
+};
+
+/* The initial installation menu */
+DMenu MenuInitial = {
+ DMENU_NORMAL_TYPE,
+ "sysinstall Main Menu", /* title */
+ "Welcome to the FreeBSD installation and configuration tool. Please\n" /* prompt */
+ "select one of the options below by using the arrow keys or typing the\n"
+ "first character of the option name you're interested in. Invoke an\n"
+ "option with [SPACE] or [ENTER]. To exit, use [TAB] to move to Exit.",
+ "Press F1 for Installation Guide", /* help line */
+ "INSTALL", /* help file */
+ { { "Select" },
+ { "X Exit Install", NULL, NULL, dmenuExit },
+ { " Usage", "Quick start - How to use this menu system", NULL, dmenuDisplayFile, NULL, "usage" },
+ { "Standard", "Begin a standard installation (recommended)", NULL, installStandard },
+ { "Express", "Begin a quick installation (for experts)", NULL, installExpress },
+ { " Custom", "Begin a custom installation (for experts)", NULL, dmenuSubmenu, NULL, &MenuInstallCustom },
+ { "Configure", "Do post-install configuration of FreeBSD", NULL, dmenuSubmenu, NULL, &MenuConfigure },
+ { "Doc", "Installation instructions, README, etc.", NULL, dmenuSubmenu, NULL, &MenuDocumentation },
+ { "Keymap", "Select keyboard type", NULL, dmenuSubmenu, NULL, &MenuSysconsKeymap },
+ { "Options", "View/Set various installation options", NULL, optionsEditor },
+ { "Fixit", "Repair mode with CDROM/DVD/floppy or start shell", NULL, dmenuSubmenu, NULL, &MenuFixit },
+ { "Upgrade", "Upgrade an existing system", NULL, installUpgrade },
+ { "Load Config","Load default install configuration", NULL, dispatch_load_floppy },
+ { "Index", "Glossary of functions", NULL, dmenuSubmenu, NULL, &MenuIndex },
+ { NULL } },
+};
+
+/* The main documentation menu */
+DMenu MenuDocumentation = {
+ DMENU_NORMAL_TYPE,
+ "FreeBSD Documentation Menu",
+ "If you are at all unsure about the configuration of your hardware\n"
+ "or are looking to build a system specifically for FreeBSD, read the\n"
+ "Hardware guide! New users should also read the Install document for\n"
+ "a step-by-step tutorial on installing FreeBSD. For general information,\n"
+ "consult the README file.",
+ "Confused? Press F1 for help.",
+ "usage",
+ { { "X Exit", "Exit this menu (returning to previous)", NULL, dmenuExit },
+ { "1 README", "A general description of FreeBSD. Read this!", NULL, dmenuDisplayFile, NULL, "README" },
+ { "2 Errata", "Late-breaking, post-release news.", NULL, dmenuDisplayFile, NULL, "ERRATA" },
+ { "3 Hardware", "The FreeBSD survival guide for PC hardware.", NULL, dmenuDisplayFile, NULL, "HARDWARE" },
+ { "4 Install", "A step-by-step guide to installing FreeBSD.", NULL, dmenuDisplayFile, NULL, "INSTALL" },
+ { "5 Copyright", "The FreeBSD Copyright notices.", NULL, dmenuDisplayFile, NULL, "COPYRIGHT" },
+ { "6 Release" ,"The release notes for this version of FreeBSD.", NULL, dmenuDisplayFile, NULL, "RELNOTES" },
+ { "7 Shortcuts", "Creating shortcuts to sysinstall.", NULL, dmenuDisplayFile, NULL, "shortcuts" },
+ { "8 HTML Docs", "Go to the HTML documentation menu (post-install).", NULL, docBrowser },
+ { NULL } },
+};
+
+DMenu MenuMouseType = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Select a protocol type for your mouse",
+ "If your mouse is attached to the PS/2 mouse port or the bus mouse port,\n"
+ "you should always choose \"Auto\", regardless of the model and the brand\n"
+ "of the mouse. All other protocol types are for serial mice and should\n"
+ "not be used with the PS/2 port mouse or the bus mouse. If you have\n"
+ "a serial mouse and are not sure about its protocol, you should also try\n"
+ "\"Auto\". It may not work for the serial mouse if the mouse does not\n"
+ "support the PnP standard. But, it won't hurt. Many 2-button serial mice\n"
+ "are compatible with \"Microsoft\" or \"MouseMan\". 3-button serial mice\n"
+ "may be compatible with \"MouseSystems\" or \"MouseMan\". If the serial\n"
+ "mouse has a wheel, it may be compatible with \"IntelliMouse\".",
+ NULL,
+ NULL,
+ { { "1 Auto", "Bus mouse, PS/2 style mouse or PnP serial mouse",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=auto" },
+ { "2 GlidePoint", "ALPS GlidePoint pad (serial)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=glidepoint" },
+ { "3 Hitachi","Hitachi tablet (serial)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=mmhittab" },
+ { "4 IntelliMouse", "Microsoft IntelliMouse (serial)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=intellimouse" },
+ { "5 Logitech", "Logitech protocol (old models) (serial)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=logitech" },
+ { "6 Microsoft", "Microsoft protocol (serial)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=microsoft" },
+ { "7 MM Series","MM Series protocol (serial)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=mmseries" },
+ { "8 MouseMan", "Logitech MouseMan/TrackMan models (serial)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=mouseman" },
+ { "9 MouseSystems", "MouseSystems protocol (serial)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=mousesystems" },
+ { "A ThinkingMouse","Kensington ThinkingMouse (serial)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=thinkingmouse" },
+ { NULL } },
+};
+
+DMenu MenuMousePort = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Select your mouse port from the following menu",
+ "The built-in pointing device of laptop/notebook computers is usually\n"
+ "a PS/2 style device.",
+ NULL,
+ NULL,
+ { { "1 PS/2", "PS/2 style mouse (/dev/psm0)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_PORT "=/dev/psm0" },
+ { "2 COM1", "Serial mouse on COM1 (/dev/cuaa0)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_PORT "=/dev/cuaa0" },
+ { "3 COM2", "Serial mouse on COM2 (/dev/cuaa1)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_PORT "=/dev/cuaa1" },
+ { "4 COM3", "Serial mouse on COM3 (/dev/cuaa2)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_PORT "=/dev/cuaa2" },
+ { "5 COM4", "Serial mouse on COM4 (/dev/cuaa3)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_PORT "=/dev/cuaa3" },
+ { "6 BusMouse", "Logitech, ATI or MS bus mouse (/dev/mse0)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_PORT "=/dev/mse0" },
+ { NULL } },
+};
+
+DMenu MenuMouse = {
+ DMENU_NORMAL_TYPE,
+ "Please configure your mouse",
+ "You can cut and paste text in the text console by running the mouse\n"
+ "daemon. Specify a port and a protocol type of your mouse and enable\n"
+ "the mouse daemon. If you don't want this feature, select 6 to disable\n"
+ "the daemon.\n"
+ "Once you've enabled the mouse daemon, you can specify \"/dev/sysmouse\"\n"
+ "as your mouse device and \"SysMouse\" or \"MouseSystems\" as mouse\n"
+ "protocol when running the X configuration utility (see Configuration\n"
+ "menu).",
+ NULL,
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)", NULL, dmenuExit },
+ { "2 Enable", "Test and run the mouse daemon", NULL, mousedTest, NULL, NULL },
+ { "3 Type", "Select mouse protocol type", NULL, dmenuSubmenu, NULL, &MenuMouseType },
+ { "4 Port", "Select mouse port", NULL, dmenuSubmenu, NULL, &MenuMousePort },
+ { "5 Flags", "Set additional flags", dmenuVarCheck, setMouseFlags,
+ NULL, VAR_MOUSED_FLAGS "=" },
+ { "6 Disable", "Disable the mouse daemon", NULL, mousedDisable, NULL, NULL },
+ { NULL } },
+};
+
+DMenu MenuMediaCDROM = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Choose a CD/DVD type",
+ "FreeBSD can be installed directly from a CD/DVD containing a valid\n"
+ "FreeBSD distribution. If you are seeing this menu it is because\n"
+ "more than one CD/DVD drive was found on your system. Please select one\n"
+ "of the following CD/DVD drives as your installation drive.",
+ "Press F1 to read the installation guide",
+ "INSTALL",
+ { { NULL } },
+};
+
+DMenu MenuMediaFloppy = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Choose a Floppy drive",
+ "You have more than one floppy drive. Please choose which drive\n"
+ "you would like to use.",
+ NULL,
+ NULL,
+ { { NULL } },
+};
+
+DMenu MenuMediaDOS = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Choose a DOS partition",
+ "FreeBSD can be installed directly from a DOS partition\n"
+ "assuming, of course, that you have copied the relevant\n"
+ "distributions into your DOS partition before starting this\n"
+ "installation. If this is not the case then you should reboot\n"
+ "DOS at this time and copy the distributions you wish to install\n"
+ "into a \"FREEBSD\" subdirectory on one of your DOS partitions.\n"
+ "Otherwise, please select the DOS partition containing the FreeBSD\n"
+ "distribution files.",
+ "Press F1 to read the installation guide",
+ "INSTALL",
+ { { NULL } },
+};
+
+DMenu MenuMediaFTP = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Please select a FreeBSD FTP distribution site",
+ "Please select the site closest to you or \"other\" if you'd like to\n"
+ "specify a different choice. Also note that not every site listed here\n"
+ "carries more than the base distribution kits. Only 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 "=ftp://ftp.freebsd.org" },
+ { "URL", "Specify some other ftp site by URL", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=other" },
+ { " 5.0 SNAP Server", "current.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://current.freebsd.org" },
+ { " 4.x SNAP Server", "releng4.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://releng4.freebsd.org" },
+ { " jp.FreeBSD.org SNAP Server", "snapshots.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://snapshots.jp.freebsd.org" },
+ { " IPv6 Ready", "ftp2.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.jp.freebsd.org" },
+ { " IPv6 Ready #2", "ftp7.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp7.jp.freebsd.org" },
+ { "Argentina", "ftp.ar.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.ar.freebsd.org" },
+ { " Australia", "ftp.au.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.au.freebsd.org" },
+ { " Australia #2","ftp2.au.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.au.freebsd.org" },
+ { " Australia #3","ftp3.au.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.au.freebsd.org" },
+ { " Australia #4","ftp4.au.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp4.au.freebsd.org" },
+ { " Australia #5","ftp5.au.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp5.au.freebsd.org" },
+ { "Brazil", "ftp.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.br.freebsd.org" },
+ { " Brazil #2", "ftp2.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.br.freebsd.org" },
+ { " Brazil #3", "ftp3.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.br.freebsd.org" },
+ { " Brazil #4", "ftp4.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp4.br.freebsd.org" },
+ { " Brazil #5", "ftp5.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp5.br.freebsd.org" },
+ { " Brazil #6", "ftp6.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp6.br.freebsd.org" },
+ { " Brazil #7", "ftp7.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp7.br.freebsd.org" },
+ { " Canada", "ftp.ca.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.ca.freebsd.org" },
+ { " Czech Republic", "ftp.cz.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.cz.freebsd.org" },
+ { "Denmark (Primary)", "ftp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.dk.freebsd.org" },
+ { " Denmark", "ftp.dk.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.dk.freebsd.org" },
+ { " Denmark #2", "ftp2.dk.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.dk.freebsd.org" },
+ { "Estonia", "ftp.ee.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.ee.freebsd.org" },
+ { "Finland", "ftp.fi.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.fi.freebsd.org" },
+ { " France", "ftp.fr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.fr.freebsd.org" },
+ { " France #1", "ftp.fr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.fr.freebsd.org" },
+ { " France #2", "ftp2.fr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.fr.freebsd.org" },
+ { " France #3", "ftp3.fr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.fr.freebsd.org" },
+ { " France #4", "ftp4.fr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp4.fr.freebsd.org" },
+ { " France #5", "ftp5.fr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp5.fr.freebsd.org" },
+ { " France #6", "ftp6.fr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp6.fr.freebsd.org" },
+ { " France #7", "ftp7.fr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp7.fr.freebsd.org" },
+ { " France #8", "ftp8.fr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp8.fr.freebsd.org" },
+ { "Germany", "ftp.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.de.freebsd.org" },
+ { " Germany #2", "ftp2.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.de.freebsd.org" },
+ { " Germany #3", "ftp3.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.de.freebsd.org" },
+ { " Germany #4", "ftp4.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp4.de.freebsd.org" },
+ { " Germany #5", "ftp5.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp5.de.freebsd.org" },
+ { " Germany #6", "ftp6.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp6.de.freebsd.org" },
+ { " Germany #7", "ftp7.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp7.de.freebsd.org" },
+ { " Greece", "ftp.gr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.gr.freebsd.org" },
+ { " Greece #2", "ftp2.gr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.gr.freebsd.org" },
+ { "Holland", "ftp.nl.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.nl.freebsd.org" },
+ { " Hong Kong", "ftp.hk.super.net", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.hk.super.net" },
+ { " Hungary", "ftp.hu.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.hu.freebsd.org" },
+ { "Iceland", "ftp.is.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.is.freebsd.org" },
+ { " Ireland", "ftp.ie.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.ie.freebsd.org" },
+ { " Israel", "ftp.il.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.il.freebsd.org" },
+ { " Israel #2", "ftp2.il.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.il.freebsd.org" },
+ { "Japan", "ftp.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.jp.freebsd.org" },
+ { " Japan #2", "ftp2.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.jp.freebsd.org" },
+ { " Japan #3", "ftp3.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.jp.freebsd.org" },
+ { " Japan #4", "ftp4.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp4.jp.freebsd.org" },
+ { " Japan #5", "ftp5.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp5.jp.freebsd.org" },
+ { " Japan #6", "ftp6.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp6.jp.freebsd.org" },
+ { " Japan #7", "ftp7.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp7.jp.freebsd.org" },
+ { "Korea", "ftp.kr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.kr.freebsd.org" },
+ { " Korea #2", "ftp2.kr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.kr.freebsd.org" },
+ { " Korea #3", "ftp3.kr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.kr.freebsd.org" },
+ { " Korea #4", "ftp4.kr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp4.kr.freebsd.org" },
+ { " Korea #5", "ftp5.kr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp5.kr.freebsd.org" },
+ { "Lithuania", "ftp.lt.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.lt.freebsd.org" },
+ { "New Zealand", "ftp.nz.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.nz.freebsd.org" },
+ { "Norway", "ftp.no.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.no.freebsd.org" },
+ { "Poland", "ftp.pl.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.pl.freebsd.org" },
+ { " Portugal", "ftp.pt.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.pt.freebsd.org" },
+ { " Portugal #2", "ftp2.pt.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.pt.freebsd.org" },
+ { "Romania", "ftp.ro.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.ro.freebsd.org" },
+ { " Russia", "ftp.ru.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.ru.freebsd.org" },
+ { " Russia #2", "ftp2.ru.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.ru.freebsd.org" },
+ { " Russia #3", "ftp3.ru.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.ru.freebsd.org" },
+ { " Russia #4", "ftp4.ru.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp4.ru.freebsd.org" },
+ { "Slovak Republic", "ftp.sk.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.sk.freebsd.org" },
+ { "Slovenia", "ftp.si.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.si.freebsd.org" },
+ { " South Africa", "ftp.za.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.za.freebsd.org" },
+ { " South Africa #2", "ftp2.za.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.za.freebsd.org" },
+ { " South Africa #3", "ftp3.za.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.za.freebsd.org" },
+ { " South Africa #4", "ftp4.za.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp4.za.freebsd.org" },
+ { " Spain", "ftp.es.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.es.freebsd.org" },
+ { " Spain #2", "ftp2.es.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.es.freebsd.org" },
+ { " Spain #3", "ftp3.es.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.es.freebsd.org" },
+ { " Sweden", "ftp.se.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.se.freebsd.org" },
+ { " Sweden #2", "ftp2.se.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.se.freebsd.org" },
+ { " Sweden #3", "ftp3.se.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.se.freebsd.org" },
+ { "Taiwan", "ftp.tw.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.tw.freebsd.org" },
+ { " Taiwan #2", "ftp2.tw.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.tw.freebsd.org" },
+ { " Taiwan #3", "ftp3.tw.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.tw.freebsd.org" },
+ { " Taiwan #4", "ftp4.tw.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp4.tw.freebsd.org" },
+ { " Thailand", "ftp.nectec.or.th", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.nectec.or.th/pub/mirrors/FreeBSD/" },
+ { "UK", "ftp.uk.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.uk.freebsd.org" },
+ { " UK #2", "ftp2.uk.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.uk.freebsd.org" },
+ { " UK #3", "ftp3.uk.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.uk.freebsd.org" },
+ { " UK #4", "ftp4.uk.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp4.uk.freebsd.org" },
+ { " UK #5", "ftp5.uk.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp5.uk.freebsd.org" },
+ { " Ukraine", "ftp.ua.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.ua.freebsd.org" },
+ { " Ukraine #2", "ftp2.ua.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.ua.freebsd.org" },
+ { " Ukraine #3", "ftp3.ua.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.ua.freebsd.org" },
+ { " USA #2", "ftp2.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.freebsd.org" },
+ { " USA #3", "ftp3.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.freebsd.org" },
+ { " USA #4", "ftp4.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp4.freebsd.org" },
+ { " USA #5", "ftp5.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp5.freebsd.org" },
+ { " USA #6", "ftp6.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=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 } },
+};
+
+/* Prototype KLD load menu */
+DMenu MenuKLD = {
+ DMENU_NORMAL_TYPE,
+ "KLD Menu",
+ "Load a KLD from a floppy\n",
+ NULL,
+ NULL,
+ { { NULL } },
+};
+
+/* The media selection menu */
+DMenu MenuMedia = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Choose Installation Media",
+ "FreeBSD can be installed from a variety of different installation\n"
+ "media, ranging from floppies to an Internet FTP server. If you're\n"
+ "installing FreeBSD from a supported CD/DVD drive then this is generally\n"
+ "the best media to use if you have no overriding reason for using other\n"
+ "media.",
+ "Press F1 for more information on the various media types",
+ "media",
+ { { "1 CD/DVD", "Install from a FreeBSD CD/DVD", NULL, mediaSetCDROM },
+ { "2 FTP", "Install from an FTP server", NULL, mediaSetFTPActive },
+ { "3 FTP Passive", "Install from an FTP server through a firewall", NULL, mediaSetFTPPassive },
+ { "4 HTTP", "Install from an FTP server through a http proxy", NULL, mediaSetHTTP },
+ { "5 DOS", "Install from a DOS partition", NULL, mediaSetDOS },
+ { "6 NFS", "Install over NFS", NULL, mediaSetNFS },
+ { "7 File System", "Install from an existing filesystem", NULL, mediaSetUFS },
+ { "8 Floppy", "Install from a floppy disk set", NULL, mediaSetFloppy },
+ { "9 Tape", "Install from SCSI or QIC tape", NULL, mediaSetTape },
+ { "X Options", "Go to the Options screen", NULL, optionsEditor },
+ { NULL } },
+};
+
+/* The distributions menu */
+DMenu MenuDistributions = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "Choose Distributions",
+ "As a convenience, we provide several \"canned\" distribution sets.\n"
+ "These select what we consider to be the most reasonable defaults for the\n"
+ "type of system in question. If you would prefer to pick and choose the\n"
+ "list of distributions yourself, simply select \"Custom\". You can also\n"
+ "pick a canned distribution set and then fine-tune it with the Custom item.\n\n"
+ "Choose an item by pressing [SPACE] or [ENTER]. When finished, choose the\n"
+ "Exit item or move to the OK button with [TAB].",
+ "Press F1 for more information on these options.",
+ "distributions",
+ { { "X Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { "All", "All system sources, binaries and X Window System)",
+ checkDistEverything, distSetEverything, NULL, NULL, ' ', ' ', ' ' },
+ { "Reset", "Reset selected distribution list to nothing",
+ NULL, distReset, NULL, NULL, ' ', ' ', ' ' },
+ { "4 Developer", "Full sources, binaries and doc but no games",
+ checkDistDeveloper, distSetDeveloper },
+ { "5 X-Developer", "Same as above + X Window System",
+ checkDistXDeveloper, distSetXDeveloper },
+ { "6 Kern-Developer", "Full binaries and doc, kernel sources only",
+ checkDistKernDeveloper, distSetKernDeveloper },
+ { "7 X-Kern-Developer", "Same as above + X Window System",
+ checkDistXKernDeveloper, distSetXKernDeveloper },
+ { "8 User", "Average user - binaries and doc only",
+ checkDistUser, distSetUser },
+ { "9 X-User", "Same as above + X Window System",
+ checkDistXUser, distSetXUser },
+ { "A Minimal", "The smallest configuration possible",
+ checkDistMinimum, distSetMinimum },
+ { "B Custom", "Specify your own distribution set",
+ NULL, dmenuSubmenu, NULL, &MenuSubDistributions, '>', '>', '>' },
+ { NULL } },
+};
+
+DMenu MenuSubDistributions = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "Select the distributions you wish to install.",
+ "Please check off the distributions you wish to install. At the\n"
+ "very minimum, this should be \"base\".",
+ NULL,
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { "All", "All system sources, binaries and X Window System",
+ NULL, distSetEverything, NULL, NULL, ' ', ' ', ' ' },
+ { "Reset", "Reset all of the below",
+ NULL, distReset, NULL, NULL, ' ', ' ', ' ' },
+ { " base", "Binary base distribution (required)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_BASE },
+#ifdef __i386__
+ { " compat1x", "FreeBSD 1.x binary compatibility",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_COMPAT1X },
+ { " compat20", "FreeBSD 2.0 binary compatibility",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_COMPAT20 },
+ { " compat21", "FreeBSD 2.1 binary compatibility",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_COMPAT21 },
+ { " compat22", "FreeBSD 2.2.x and 3.0 a.out binary compatibility",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_COMPAT22 },
+#if __FreeBSD__ > 3
+ { " compat3x", "FreeBSD 3.x binary compatibility",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_COMPAT3X },
+#endif
+#if __FreeBSD__ >= 4
+ { " compat4x", "FreeBSD 4.x binary compatibility",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_COMPAT4X },
+#endif
+#endif
+ { " crypto", "Basic encryption services",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CRYPTODists, '[', 'X', ']', DIST_CRYPTO_CRYPTO, },
+#if __FreeBSD__ <= 3
+ { " krb", "KerberosIV authentication services",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CRYPTODists, '[', 'X', ']', DIST_CRYPTO_KERBEROS },
+#else
+ { " krb4", "KerberosIV authentication services",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CRYPTODists, '[', 'X', ']', DIST_CRYPTO_KERBEROS4 },
+ { " krb5", "Kerberos5 authentication services",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CRYPTODists, '[', 'X', ']', DIST_CRYPTO_KERBEROS5 },
+#endif
+ { " dict", "Spelling checker dictionary files",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_DICT },
+ { " doc", "Miscellaneous FreeBSD online docs",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_DOC },
+ { " games", "Games (non-commercial)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_GAMES },
+ { " info", "GNU info files",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_INFO },
+ { " man", "System manual pages - recommended",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_MANPAGES },
+ { " catman", "Preformatted system manual pages",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_CATPAGES },
+ { " proflibs", "Profiled versions of the libraries",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_PROFLIBS },
+ { " src", "Sources for everything",
+ srcFlagCheck, distSetSrc },
+ { " ports", "The FreeBSD Ports collection",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_PORTS },
+ { " local", "Local additions collection",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_LOCAL},
+ { " XFree86", "The XFree86 distribution",
+#ifdef X_AS_PKG
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_XF86 },
+#else
+ x11FlagCheck, distSetXF86 },
+#endif
+ { NULL } },
+};
+
+DMenu MenuSrcDistributions = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "Select the sub-components of src you wish to install.",
+ "Please check off those portions of the FreeBSD source tree\n"
+ "you wish to install.",
+ NULL,
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { "All", "Select all of the below",
+ NULL, setSrc, NULL, NULL, ' ', ' ', ' ' },
+ { "Reset", "Reset all of the below",
+ NULL, clearSrc, NULL, NULL, ' ', ' ', ' ' },
+ { " base", "top-level files in /usr/src",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_BASE },
+ { " contrib", "/usr/src/contrib (contributed software)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_CONTRIB },
+ { " gnu", "/usr/src/gnu (software from the GNU Project)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_GNU },
+ { " etc", "/usr/src/etc (miscellaneous system files)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_ETC },
+ { " games", "/usr/src/games (the obvious!)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_GAMES },
+ { " include", "/usr/src/include (header files)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_INCLUDE },
+ { " lib", "/usr/src/lib (system libraries)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_LIB },
+ { " libexec", "/usr/src/libexec (system programs)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_LIBEXEC },
+ { " release", "/usr/src/release (release-generation tools)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_RELEASE },
+ { " bin", "/usr/src/bin (system binaries)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_BIN },
+ { " sbin", "/usr/src/sbin (system binaries)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_SBIN },
+ { " scrypto", "/usr/src/crypto (contrib encryption sources)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CRYPTODists, '[', 'X', ']', DIST_CRYPTO_SCRYPTO },
+ { " share", "/usr/src/share (documents and shared files)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_SHARE },
+ { " skrb4", "/usr/src/kerberosIV (sources for KerberosIV)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CRYPTODists, '[', 'X', ']', DIST_CRYPTO_SKERBEROS4 },
+ { " skrb5", "/usr/src/kerberos5 (sources for Kerberos5)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CRYPTODists, '[', 'X', ']', DIST_CRYPTO_SKERBEROS5 },
+ { " ssecure", "/usr/src/secure (BSD encryption sources)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CRYPTODists, '[', 'X', ']', DIST_CRYPTO_SSECURE },
+ { " sys", "/usr/src/sys (FreeBSD kernel)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_SYS },
+ { " tools", "/usr/src/tools (miscellaneous tools)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_TOOLS },
+ { " ubin", "/usr/src/usr.bin (user binaries)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_UBIN },
+ { " usbin", "/usr/src/usr.sbin (aux system binaries)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_USBIN },
+ { NULL } },
+};
+
+DMenu MenuXF86Config = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Please select the XFree86 configuration tool you want to use.",
+ "The first option, xf86cfg, is fully graphical.\n"
+ "The second option provides a menu-based interface similar to\n"
+ "what you are currently using. "
+ "The third option, xf86config, is\n"
+ "a more simplistic shell-script based tool and less friendly to\n"
+ "new users, but it may work in situations where the other options\n"
+ "do not.",
+ NULL,
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)",
+ NULL, dmenuExit },
+ { "2 xf86cfg", "Fully graphical XFree86 configuration tool.",
+ NULL, dmenuSetVariable, NULL, VAR_XF86_CONFIG "=xf86cfg" },
+ { "3 xf86cfg -textmode", "ncurses-based XFree86 configuration tool.",
+ NULL, dmenuSetVariable, NULL, VAR_XF86_CONFIG "=xf86cfg -textmode" },
+ { "4 xf86config", "Shell-script based XFree86 configuration tool.",
+ NULL, dmenuSetVariable, NULL, VAR_XF86_CONFIG "=xf86config" },
+ { "D XDesktop", "X already set up, just do desktop configuration.",
+ NULL, dmenuSubmenu, NULL, &MenuXDesktops },
+ { NULL } },
+};
+
+DMenu MenuXDesktops = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Please select the default X desktop to use.",
+ "By default, XFree86 comes with a fairly vanilla desktop which\n"
+ "is based around the twm(1) window manager and does not offer\n"
+ "much in the way of features. It does have the advantage of\n"
+ "being a standard part of X so you don't need to load anything\n"
+ "extra in order to use it. If, however, you have access to a\n"
+ "reasonably full packages collection on your installation media,\n"
+ "you can choose any one of the following desktops as alternatives.",
+ NULL,
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)",
+ NULL, dmenuExit },
+ { "2 KDE", "The K Desktop Environment.",
+ NULL, dmenuSetVariable, NULL, VAR_DESKSTYLE "=kde" },
+ { "3 GNOME + Sawfish", "GNOME + Sawfish window manager.",
+ NULL, dmenuSetVariable, NULL, VAR_DESKSTYLE "=gnome" },
+ { "4 GNOME + Enlightenment","GNOME + The E window manager",
+ NULL, dmenuSetVariable, NULL, VAR_DESKSTYLE "=enlightenment" },
+ { "5 Afterstep", "The Afterstep window manager",
+ NULL, dmenuSetVariable, NULL, VAR_DESKSTYLE "=afterstep" },
+ { "6 Windowmaker", "The Windowmaker window manager",
+ NULL, dmenuSetVariable, NULL, VAR_DESKSTYLE "=windowmaker" },
+ { "7 fvwm", "The fvwm window manager",
+ NULL, dmenuSetVariable, NULL, VAR_DESKSTYLE "=fvwm2" },
+ { NULL } },
+};
+
+DMenu MenuXF86Select = {
+ DMENU_NORMAL_TYPE,
+ "XFree86 Distribution",
+ "Please select the components you need from the XFree86\n"
+ "distribution sets.",
+ NULL,
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)", NULL, dmenuExit },
+ { "Basic", "Basic component menu (required)", NULL, dmenuSubmenu, NULL, &MenuXF86SelectCore },
+ { "Server", "X server menu", NULL, dmenuSubmenu, NULL, &MenuXF86SelectServer },
+ { "Fonts", "Font set menu", NULL, dmenuSubmenu, NULL, &MenuXF86SelectFonts },
+ { NULL } },
+};
+
+DMenu MenuXF86SelectCore = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "XFree86 base distribution types",
+ "Please check off the basic XFree86 components you wish to install.\n"
+ "Bin, lib, and set are recommended for a minimum installaion.",
+ NULL,
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { "All", "Select all below",
+ NULL, setX11Misc, NULL, NULL, ' ', ' ', ' ' },
+ { "Reset", "Reset all below",
+ NULL, clearX11Misc, NULL, NULL, ' ', ' ', ' ' },
+ { " bin", "Client applications and shared libs",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_BIN },
+ { " lib", "Data files needed at runtime",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_LIB },
+ { " cfg", "Configuration files",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_CFG },
+ { " set", "XFree86 Setup Utility",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_SET },
+ { " man", "Manual pages",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_MAN },
+ { " 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 },
+ { " lkit", "Server link kit for all other machines",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_LKIT },
+ { " prog", "Programmer's header and library files",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_PROG },
+#if defined(__i386__) && defined(PC98)
+ { " 9set", "XFree86 Setup Utility for PC98 machines",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_9SET },
+ { " lk98", "Server link kit for PC98 machines",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_LKIT98 },
+#endif
+ { NULL } },
+};
+
+DMenu MenuXF86SelectFonts = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "Font distribution selection.",
+ "Please check off the individual font distributions you wish to\n\
+install. At the minimum, you should install the standard\n\
+75 DPI and misc fonts if you're also installing a server\n\
+(these are selected by default).",
+ NULL,
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { "All", "All fonts",
+ NULL, setX11Fonts, NULL, NULL, ' ', ' ', ' ' },
+ { "Reset", "Reset font selections",
+ NULL, clearX11Fonts, NULL, NULL, ' ', ' ', ' ' },
+ { " fnts", "Standard 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 },
+ { 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.",
+ NULL,
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { "All", "Select all of the above",
+ NULL, setX11Servers, NULL, NULL, ' ', ' ', ' ' },
+ { "Reset", "Reset all of the above",
+ NULL, clearX11Servers, NULL, NULL, ' ', ' ', ' ' },
+ { " 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 },
+ { " 3DL", "8, 16 and 24 bit color 3D Labs boards",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_3DL },
+ { " 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 },
+#if defined(__i386__) && defined(PC98)
+ { " PC98", "Select an X server for a NEC PC98 [Submenu]",
+ NULL, dmenuSubmenu, NULL, &MenuXF86SelectPC98Server, '>', ' ', '>', 0 },
+#elif __alpha__
+ { " TGA", "TGA cards (alpha architecture only)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_TGA },
+#endif
+ { NULL } },
+};
+
+#if defined(__i386__) && defined(PC98)
+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).",
+ NULL,
+ NULL,
+ { { " 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 },
+ { NULL } }
+};
+#endif
+
+DMenu MenuDiskDevices = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "Select Drive(s)",
+ "Please select the drive, or drives, on which you wish to perform\n"
+ "this operation. If you are attempting to install a boot partition\n"
+ "on a drive other than the first one or have multiple operating\n"
+ "systems on your machine, you will have the option to install a boot\n"
+ "manager later. To select a drive, use the arrow keys to move to it\n"
+ "and press [SPACE] or [ENTER]. To de-select it, press it again.\n\n"
+ "Use [TAB] to get to the buttons and leave this menu.",
+ "Press F1 for important information regarding disk geometry!",
+ "drives",
+ { { NULL } },
+};
+
+DMenu MenuHTMLDoc = {
+ DMENU_NORMAL_TYPE,
+ "Select HTML Documentation pointer",
+ "Please select the body of documentation you're interested in, the main\n"
+ "ones right now being the FAQ and the Handbook. You can also choose \"other\"\n"
+ "to enter an arbitrary URL for browsing.",
+ "Press F1 for more help on what you see here.",
+ "html",
+ { { "X Exit", "Exit this menu (returning to previous)", NULL, dmenuExit },
+ { "2 Handbook", "The FreeBSD Handbook.", NULL, docShowDocument },
+ { "3 FAQ", "The Frequently Asked Questions guide.", NULL, docShowDocument },
+ { "4 Home", "The Home Pages for the FreeBSD Project (requires net)", NULL, docShowDocument },
+ { "5 Other", "Enter a URL.", NULL, docShowDocument },
+ { NULL } },
+};
+
+/* The main installation menu */
+DMenu MenuInstallCustom = {
+ DMENU_NORMAL_TYPE,
+ "Choose Custom Installation Options",
+ "This is the custom installation menu. You may use this menu to specify\n"
+ "details on the type of distribution you wish to have, where you wish\n"
+ "to install it from and how you wish to allocate disk storage to FreeBSD.",
+ "Press F1 to read the installation guide",
+ "INSTALL",
+ { { "X Exit", "Exit this menu (returning to previous)", NULL, dmenuExit },
+ { "2 Options", "View/Set various installation options", NULL, optionsEditor },
+#ifdef __alpha__
+ { "3 Label", "Label disk partitions", NULL, diskLabelEditor },
+ { "4 Distributions", "Select distribution(s) to extract", NULL, dmenuSubmenu, NULL, &MenuDistributions },
+ { "5 Media", "Choose the installation media type", NULL, dmenuSubmenu, NULL, &MenuMedia },
+ { "6 Commit", "Perform any pending Partition/Label/Extract actions", NULL, installCustomCommit },
+#else
+ { "3 Partition", "Allocate disk space for FreeBSD", NULL, diskPartitionEditor },
+ { "4 Label", "Label allocated disk partitions", NULL, diskLabelEditor },
+ { "5 Distributions", "Select distribution(s) to extract", NULL, dmenuSubmenu, NULL, &MenuDistributions },
+ { "6 Media", "Choose the installation media type", NULL, dmenuSubmenu, NULL, &MenuMedia },
+ { "7 Commit", "Perform any pending Partition/Label/Extract actions", NULL, installCustomCommit },
+#endif
+ { NULL } },
+};
+
+/* MBR type menu */
+DMenu MenuMBRType = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "overwrite me", /* will be disk specific label */
+ "FreeBSD comes with a boot selector that allows you to easily\n"
+ "select between FreeBSD and any other operating systems on your machine\n"
+ "at boot time. If you have more than one drive and want to boot\n"
+ "from the second one, the boot selector will also make it possible\n"
+ "to do so (limitations in the PC BIOS usually prevent this otherwise).\n"
+ "If you do not want a boot selector, or wish to replace an existing\n"
+ "one, select \"standard\". If you would prefer your Master Boot\n"
+ "Record to remain untouched then select \"None\".\n\n"
+ " NOTE: PC-DOS users will almost certainly require \"None\"!",
+ "Press F1 to read about drive setup",
+ "drives",
+ { { "BootMgr", "Install the FreeBSD Boot Manager",
+ dmenuRadioCheck, dmenuSetValue, NULL, &BootMgr },
+#ifndef PC98
+ { "Standard", "Install a standard MBR (no boot manager)",
+ dmenuRadioCheck, dmenuSetValue, NULL, &BootMgr, '(', '*', ')', 1 },
+#endif
+ { "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",
+ { { "X Exit", "Exit this menu (returning to previous)",
+ NULL, dmenuExit },
+ { " Distributions", "Install additional distribution sets",
+ NULL, distExtractAll },
+ { " Packages", "Install pre-packaged software for FreeBSD",
+ NULL, configPackages },
+ { " Root Password", "Set the system manager's password",
+ NULL, dmenuSystemCommand, NULL, "passwd root" },
+#ifdef __i386__
+ { " Fdisk", "The disk Slice (PC-style partition) Editor",
+ NULL, diskPartitionEditor },
+#endif
+ { " Label", "The disk Label editor",
+ NULL, diskLabelEditor },
+ { " User Management", "Add user and group information",
+ NULL, dmenuSubmenu, NULL, &MenuUsermgmt },
+ { " Console", "Customize system console behavior",
+ NULL, dmenuSubmenu, NULL, &MenuSyscons },
+ { " Time Zone", "Set which time zone you're in",
+ NULL, dmenuSystemCommand, NULL, "tzsetup" },
+ { " Media", "Change the installation media type",
+ NULL, dmenuSubmenu, NULL, &MenuMedia },
+ { " Mouse", "Configure your mouse",
+ NULL, dmenuSubmenu, NULL, &MenuMouse, NULL },
+ { " Networking", "Configure additional network services",
+ NULL, dmenuSubmenu, NULL, &MenuNetworking },
+ { " Security", "Configure system security options",
+ NULL, dmenuSubmenu, NULL, &MenuSecurity },
+ { " Startup", "Configure system startup options",
+ NULL, dmenuSubmenu, NULL, &MenuStartup },
+ { " TTYs", "Configure system ttys.",
+ NULL, configEtcTtys, NULL, "ttys" },
+ { " Options", "View/Set various installation options",
+ NULL, optionsEditor },
+ { " XFree86", "Configure XFree86 Server",
+ NULL, configXSetup },
+ { " Desktop", "Configure XFree86 Desktop",
+ NULL, configXDesktop },
+ { " HTML Docs", "Go to the HTML documentation menu (post-install)",
+ NULL, docBrowser },
+ { " Load KLD", "Load a KLD from a floppy",
+ NULL, kldBrowser },
+ { NULL } },
+};
+
+DMenu MenuStartup = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "Startup Services Menu",
+ "This menu allows you to configure various aspects of your system's\n"
+ "startup configuration. Use [SPACE] or [ENTER] to select items, and\n"
+ "[TAB] to move to the buttons. Select Exit to leave this menu.",
+ NULL,
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { " APM", "Auto-power management services (typically laptops)",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "apm_enable=YES" },
+#ifdef PCCARD_ARCH
+ { " pccard", "Enable PCCARD (AKA PCMCIA) services (also laptops)",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "pccard_enable=YES" },
+ { " pccard mem", "Set PCCARD memory address (if enabled)",
+ dmenuVarCheck, dmenuISetVariable, NULL, "pccard_mem" },
+ { " pccard ifconfig", "List of PCCARD ethernet devices to configure",
+ dmenuVarCheck, dmenuISetVariable, NULL, "pccard_ifconfig" },
+#endif
+ { " usbd", "Enable USB daemon (detect USB attach / detach)",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "usbd_enable=YES" },
+ { " usbd flags", "Set default flags to usbd (if enabled)",
+ dmenuVarCheck, dmenuISetVariable, NULL, "usbd_flags" },
+ { " ", " -- ", NULL, NULL, NULL, NULL, ' ', ' ', ' ' },
+ { " startup dirs", "Set the list of dirs to look for startup scripts",
+ dmenuVarCheck, dmenuISetVariable, NULL, "local_startup" },
+ { " named", "Run a local name server on this host",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "named_enable=YES" },
+ { " named flags", "Set default flags to named (if enabled)",
+ dmenuVarCheck, dmenuISetVariable, NULL, "named_flags" },
+ { " nis client", "This host wishes to be an NIS client.",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "nis_client_enable=YES" },
+ { " nis domainname", "Set NIS domainname (if enabled)",
+ dmenuVarCheck, dmenuISetVariable, NULL, "nisdomainname" },
+ { " nis server", "This host wishes to be an NIS server.",
+ dmenuVarCheck, 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" },
+#ifdef __i386__
+ { " linux", "This host wants to be able to run linux binaries.",
+ dmenuVarCheck, configLinux, NULL, VAR_LINUX_ENABLE "=YES" },
+ { " SVR4", "This host wants to be able to run SVR4 binaries.",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "svr4_enable=YES" },
+ { " SCO", "This host wants to be able to run IBCS2 binaries.",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "ibcs2_enable=YES" },
+#elif __alpha__
+ { " OSF/1", "This host wants to be able to run DEC OSF/1 binaries.",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "osf1_enable=YES" },
+#endif
+ { " quotas", "This host wishes to check quotas on startup.",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "check_quotas=YES" },
+ { NULL } },
+};
+
+DMenu MenuNetworking = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "Network Services Menu",
+ "You may have already configured one network device (and the other\n"
+ "various hostname/gateway/name server parameters) in the process\n"
+ "of installing FreeBSD. This menu allows you to configure other\n"
+ "aspects of your system's network configuration.",
+ NULL,
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { " Interfaces", "Configure additional network interfaces",
+ NULL, tcpMenuSelect },
+ { " AMD", "This machine wants to run the auto-mounter service",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "amd_enable=YES" },
+ { " AMD Flags", "Set flags to AMD service (if enabled)",
+ dmenuVarCheck, dmenuISetVariable, NULL, "amd_flags" },
+ { " Anon FTP", "This machine wishes to allow anonymous FTP.",
+ dmenuVarCheck, configAnonFTP, NULL, "anon_ftp" },
+ { " Gateway", "This machine will route packets between interfaces",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "gateway_enable=YES" },
+ { " inetd", "This machine wants to run the inet daemon",
+ dmenuVarCheck, configInetd, NULL, "inetd_enable=YES" },
+ { " NFS client", "This machine will be an NFS client",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "nfs_client_enable=YES" },
+ { " NFS server", "This machine will be an NFS server",
+ dmenuVarCheck, configNFSServer, NULL, "nfs_server_enable=YES" },
+ { " Ntpdate", "Select a clock-synchronization server",
+ dmenuVarCheck, dmenuSubmenu, NULL, &MenuNTP, '[', 'X', ']', "ntpdate_enable=YES" },
+ { " PCNFSD", "Run authentication server for clients with PC-NFS.",
+ dmenuVarCheck, configPCNFSD, NULL, "pcnfsd" },
+ { " portmap", "This machine wants to run the portmapper daemon",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "portmap_enable=YES" },
+ { " Routed", "Select routing daemon (default: routed)",
+ dmenuVarCheck, configRouter, NULL, "router_enable=YES" },
+ { " Rwhod", "This machine wants to run the rwho daemon",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "rwhod_enable=YES" },
+ { " Sendmail", "This machine wants to run the sendmail daemon",
+ NULL, dmenuSubmenu, NULL, &MenuSendmail },
+ { " Sshd", "This machine wants to run the ssh daemon",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "sshd_enable=YES" },
+ { " TCP Extensions", "Allow RFC1323 and RFC1644 TCP extensions?",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "tcp_extensions=YES" },
+ { NULL } },
+};
+
+DMenu MenuSendmail = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Sendmail Invocation Selection",
+ "There are three options for invoking sendmail at startup.\n"
+ "Please select Yes if you want to use sendmail as your mail transfer\n"
+ "agent. Selecting No disables sendmail's network socket for incoming\n"
+ "email, but still enables sendmail for local and outbound mail.\n"
+ "None disables sendmail completely at startup and disables inbound,\n"
+ "outbound, and local mail. See /etc/mail/README for more\n"
+ "information.\n",
+ NULL,
+ NULL,
+ {
+ { " Yes", "Start sendmail",
+ dmenuVarCheck, dmenuSetVariable, NULL, "sendmail_enable=YES" },
+ { " No", "Start sendmail, but don't listen from network",
+ dmenuVarCheck, dmenuSetVariable, NULL, "sendmail_enable=NO" },
+ { " None", "Don't start any sendmail processes",
+ dmenuVarCheck, dmenuSetVariable, NULL, "sendmail_enable=NONE" },
+ { NULL } },
+};
+
+DMenu MenuNTP = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "NTPDATE Server Selection",
+ "There are a number of time synchronization servers available\n"
+ "for public use around the Internet. Please select one reasonably\n"
+ "close to you to have your system time synchronized accordingly.",
+ "These are the primary open-access NTP servers",
+ NULL,
+ { { "None", "No NTP server",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=NO,ntpdate_flags=none" },
+ { "Other", "Select a site not on this list",
+ dmenuVarsCheck, configNTP, NULL, NULL },
+ { "Argentina", "tick.nap.com.ar",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=tick.nap.com.ar" },
+ { "Argentina #2", "time.sinectis.com.ar",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=time.sinectis.com.ar" },
+ { "Argentina #3", "tock.nap.com.ar",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=tock.nap.com.ar" },
+ { "Australia", "augean.eleceng.adelaide.edu.au",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=augean.eleceng.adelaide.edu.au" },
+ { "Australia #2", "ntp.adelaide.edu.au",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.adelaide.edu.au" },
+ { "Australia #3", "ntp.saard.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.saard.net" },
+ { "Australia #4", "time.deakin.edu.au",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=time.deakin.edu.au" },
+ { "Australia #5", "time.esec.com.au",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=time.esec.com.au" },
+ { "Belgium", "ntp1.belbone.be",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp1.belbone.be" },
+ { "Belgium #2", "ntp2.belbone.be",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2.belbone.be" },
+ { "Brazil", "ntp.cais.rnp.br",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.cais.rnp.br" },
+ { "Brazil #2", "ntp.pop-df.rnp.br",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.pop-df.rnp.br" },
+ { "Brazil #3", "ntp.ufes.br",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.ufes.br" },
+ { "Brazil #4", "ntp1.pucpr.br",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp1.pucpr.br" },
+ { "Canada", "ntp.cpsc.ucalgary.ca",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.cpsc.ucalgary.ca" },
+ { "Canada #2", "ntp1.cmc.ec.gc.ca",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp1.cmc.ec.gc.ca" },
+ { "Canada #3", "ntp2.cmc.ec.gc.ca",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2.cmc.ec.gc.ca" },
+ { "Canada #4", "tick.utoronto.ca",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=tick.utoronto.ca" },
+ { "Canada #5", "time.chu.nrc.ca",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=time.chu.nrc.ca" },
+ { "Canada #6", "time.nrc.ca",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=time.nrc.ca" },
+ { "Canada #7", "timelord.uregina.ca",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=timelord.uregina.ca" },
+ { "Canada #8", "tock.utoronto.ca",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=tock.utoronto.ca" },
+ { "Czech", "ntp.karpo.cz",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.karpo.cz" },
+ { "Denmark", "clock.netcetera.dk",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=clock.netcetera.dk" },
+ { "Denmark", "clock2.netcetera.dk",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=clock2.netcetera.dk" },
+ { "Spain", "slug.ctv.es",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=slug.ctv.es" },
+ { "Finland", "tick.keso.fi",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=tick.keso.fi" },
+ { "Finland #2", "tock.keso.fi",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=tock.keso.fi" },
+ { "France", "ntp.obspm.fr",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.obspm.fr" },
+ { "France #2", "ntp.univ-lyon1.fr",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.univ-lyon1.fr" },
+ { "France #3", "ntp.via.ecp.fr",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.via.ecp.fr" },
+ { "Croatia", "zg1.ntp.carnet.hr",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=zg1.ntp.carnet.hr" },
+ { "Croatia #2", "zg2.ntp.carnet.hr",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=zg2.ntp.carnet.hr" },
+ { "Croatia #3", "st.ntp.carnet.hr",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=st.ntp.carnet.hr" },
+ { "Croatia #4", "ri.ntp.carnet.hr",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ri.ntp.carnet.hr" },
+ { "Croatia #5", "os.ntp.carnet.hr",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=os.ntp.carnet.hr" },
+ { "Hungary", "time.kfki.hu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=time.kfki.hu" },
+ { "Indonesia", "ntp.incaf.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.incaf.net" },
+ { "Ireland", "ntp.maths.tcd.ie",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.maths.tcd.ie" },
+ { "Italy", "ntps.net4u.it",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=" },
+ { "Japan", "ntp.cyber-fleet.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.cyber-fleet.net" },
+ { "Korea", "time.nuri.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=time.nuri.net" },
+ { "Mexico", "ntp2a.audiotel.com.mx",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2a.audiotel.com.mx" },
+ { "Mexico #2", "ntp2b.audiotel.com.mx",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2b.audiotel.com.mx" },
+ { "Mexico #3", "ntp2c.audiotel.com.mx",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2c.audiotel.com.mx" },
+ { "Nigeria", "ntp.supernet300.com",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.supernet300.com" },
+ { "Netherlands", "ntp1.theinternetone.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp1.theinternetone.net" },
+ { "Netherlands #2", "ntp2.theinternetone.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2.theinternetone.net" },
+ { "Netherlands #3", "ntp3.theinternetone.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp3.theinternetone.net" },
+ { "Norway", "fartein.ifi.uio.no",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=fartein.ifi.uio.no" },
+ { "Norway #2", "time.alcanet.no",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=time.alcanet.no" },
+ { "New Zealand", "ntp.massey.ac.nz",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.massey.ac.nz" },
+ { "New Zealand #2", "ntp.public.otago.ac.nz",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.public.otago.ac.nz" },
+ { "New Zealand #3", "tk1.ihug.co.nz",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=tk1.ihug.co.nz" },
+ { "New Zealand #4", "ntp.waikato.ac.nz",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.waikato.ac.nz" },
+ { "Poland", "info.cyf-kr.edu.pl",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=info.cyf-kr.edu.pl" },
+ { "Portugal", "bug.fe.up.pt",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=bug.fe.up.pt" },
+ { "Romania", "ntp.ip.ro",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.ip.ro" },
+ { "Russia", "ntp.psn.ru",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.psn.ru" },
+ { "Russia #2", "sign.chg.ru",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=sign.chg.ru" },
+ { "Sweden", "ntp.lth.se",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.lth.se" },
+ { "Singapore", "ntp.shim.org",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.shim.org" },
+ { "Slovenia", "calvus.rzs-hm.si",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=calvus.rzs-hm.si" },
+ { "Slovenia #2", "sizif.mf.uni-lj.si",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=sizif.mf.uni-lj.si" },
+ { "Slovenia #3", "ntp1.arnes.si",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp1.arnes.si" },
+ { "Slovenia #4", "ntp2.arnes.si",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2.arnes.si" },
+ { "Slovenia #5", "time.ijs.si",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=time.ijs.si" },
+ { "Scotland", "ntp.cs.strath.ac.uk",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.cs.strath.ac.uk" },
+ { "United Kingdom", "ntp.exnet.com",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.exnet.com" },
+ { "United Kingdom #2", "ntp0.uk.uu.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp0.uk.uu.net" },
+ { "United Kingdom #3", "ntp1.uk.uu.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp1.uk.uu.net" },
+ { "United Kingdom #4", "ntp2.uk.uu.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2.uk.uu.net" },
+ { "United Kingdom #5", "ntp2a.mcc.ac.uk",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2a.mcc.ac.uk" },
+ { "United Kingdom #6", "ntp2b.mcc.ac.uk",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2b.mcc.ac.uk" },
+ { "United Kingdom #7", "ntp2c.mcc.ac.uk",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2c.mcc.ac.uk" },
+ { "United Kingdom #8", "ntp2d.mcc.ac.uk",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2d.mcc.ac.uk" },
+ { "United Kingdom #9", "tick.tanac.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=tick.tanac.net" },
+ { "U.S. AR", "sushi.compsci.lyon.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=sushi.compsci.lyon.edu" },
+ { "U.S. AZ", "ntp.drydog.com",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.drydog.com" },
+ { "U.S. CA", "ntp.ucsd.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.ucsd.edu" },
+ { "U.S. CA #2", "ntp1.mainecoon.com",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp1.mainecoon.com" },
+ { "U.S. CA #3", "ntp2.mainecoon.com",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2.mainecoon.com" },
+ { "U.S. CA #4", "reloj.kjsl.com",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=reloj.kjsl.com" },
+ { "U.S. CA #5", "time.five-ten-sg.com",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=time.five-ten-sg.com" },
+ { "U.S. DE", "louie.udel.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=louie.udel.edu" },
+ { "U.S. GA", "ntp.shorty.com",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.shorty.com" },
+ { "U.S. GA #2", "rolex.usg.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=rolex.usg.edu" },
+ { "U.S. GA #3", "timex.usg.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=timex.usg.edu" },
+ { "U.S. IL", "ntp-0.cso.uiuc.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp-0.cso.uiuc.edu" },
+ { "U.S. IL #2", "ntp-1.cso.uiuc.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp-1.cso.uiuc.edu" },
+ { "U.S. IL #3", "ntp-1.mcs.anl.gov",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp-1.mcs.anl.gov" },
+ { "U.S. IL #4", "ntp-2.cso.uiuc.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp-2.cso.uiuc.edu" },
+ { "U.S. IL #5", "ntp-2.mcs.anl.gov",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp-2.mcs.anl.gov" },
+ { "U.S. IN", "gilbreth.ecn.purdue.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=gilbreth.ecn.purdue.edu" },
+ { "U.S. IN #2", "harbor.ecn.purdue.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=harbor.ecn.purdue.edu" },
+ { "U.S. IN #3", "molecule.ecn.purdue.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=molecule.ecn.purdue.edu" },
+ { "U.S. KS", "ntp1.kansas.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp1.kansas.net" },
+ { "U.S. KS #2", "ntp2.kansas.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2.kansas.net" },
+ { "U.S. MA", "ntp.ourconcord.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.ourconcord.net" },
+ { "U.S. MA #2", "timeserver.cs.umb.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=timeserver.cs.umb.edu" },
+ { "U.S. MN", "ns.nts.umn.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ns.nts.umn.edu" },
+ { "U.S. MN #2", "nss.nts.umn.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=nss.nts.umn.edu" },
+ { "U.S. MO", "time-ext.missouri.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=time-ext.missouri.edu" },
+ { "U.S. MT", "chronos1.umt.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=chronos1.umt.edu" },
+ { "U.S. MT #2", "chronos2.umt.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=chronos2.umt.edu" },
+ { "U.S. MT #3", "chronos3.umt.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=chronos3.umt.edu" },
+ { "U.S. NC", "clock1.unc.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=clock1.unc.edu" },
+ { "U.S. NV", "cuckoo.nevada.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=cuckoo.nevada.edu" },
+ { "U.S. NV #2", "tick.cs.unlv.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=tick.cs.unlv.edu" },
+ { "U.S. NV #3", "tock.cs.unlv.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=tock.cs.unlv.edu" },
+ { "U.S. NY", "clock.linuxshell.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=clock.linuxshell.net" },
+ { "U.S. NY #2", "ntp.ctr.columbia.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.ctr.columbia.edu" },
+ { "U.S. NY #3", "ntp0.cornell.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp0.cornell.edu" },
+ { "U.S. NY #4", "ntp1.mpis.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp1.mpis.net" },
+ { "U.S. NY #5", "ntp2.mpis.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2.mpis.net" },
+ { "U.S. NY #6", "sundial.columbia.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=sundial.columbia.edu" },
+ { "U.S. NY #7", "timex.cs.columbia.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=timex.cs.columbia.edu" },
+ { "U.S. OK", "constellation.ecn.uoknor.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=constellation.ecn.uoknor.edu" },
+ { "U.S. PA", "clock-1.cs.cmu.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=clock-1.cs.cmu.edu" },
+ { "U.S. PA #2", "clock-2.cs.cmu.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=clock-2.cs.cmu.edu" },
+ { "U.S. PA #3", "clock.psu.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=clock.psu.edu" },
+ { "U.S. PA #4", "fuzz.psc.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=fuzz.psc.edu" },
+ { "U.S. PA #5", "ntp-1.ece.cmu.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp-1.ece.cmu.edu" },
+ { "U.S. PA #6", "ntp-2.ece.cmu.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp-2.ece.cmu.edu" },
+ { "U.S. TX", "ntp.cox.smu.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.cox.smu.edu" },
+ { "U.S. TX #2", "ntp.fnbhs.com",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.fnbhs.com" },
+ { "U.S. TX #3", "ntp.tmc.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.tmc.edu" },
+ { "U.S. TX #4", "ntp5.tamu.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp5.tamu.edu" },
+ { "U.S. TX #5", "tick.greyware.com",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=tick.greyware.com" },
+ { "U.S. TX #6", "tock.greyware.com",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=tock.greyware.com" },
+ { "U.S. VA", "ntp-1.vt.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp-1.vt.edu" },
+ { "U.S. VA #2", "ntp-2.vt.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp-2.vt.edu" },
+ { "U.S. VA #3", "ntp.cmr.gov",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.cmr.gov" },
+ { "U.S. VT", "ntp0.state.vt.us",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp0.state.vt.us" },
+ { "U.S. VT #2", "ntp1.state.vt.us",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp1.state.vt.us" },
+ { "U.S. VT #3", "ntp2.state.vt.us",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2.state.vt.us" },
+ { "U.S. WA", "clock.tricity.wsu.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=clock.tricity.wsu.edu" },
+ { "U.S. WA #2", "ntp.tcp-udp.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.tcp-udp.net" },
+ { "U.S. WI", "ntp1.cs.wisc.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp1.cs.wisc.edu" },
+ { "U.S. WI #2", "ntp3.cs.wisc.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp3.cs.wisc.edu" },
+ { "Venezuela", "ntp.linux.org.ve",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.linux.org.ve" },
+ { "South Africa", "ntp.cs.unp.ac.za",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.cs.unp.ac.za" },
+ { NULL } },
+};
+
+DMenu MenuSyscons = {
+ DMENU_NORMAL_TYPE,
+ "System Console Configuration",
+ "The default system console driver for FreeBSD (syscons) has a\n"
+ "number of configuration options which may be set according to\n"
+ "your preference.\n\n"
+ "When you are done setting configuration options, select Cancel.",
+ "Configure your system console settings",
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)", NULL, dmenuExit },
+ { "2 Font", "Choose an alternate screen font", NULL, dmenuSubmenu, NULL, &MenuSysconsFont },
+ { "3 Keymap", "Choose an alternate keyboard map", NULL, dmenuSubmenu, NULL, &MenuSysconsKeymap },
+ { "4 Repeat", "Set the rate at which keys repeat", NULL, dmenuSubmenu, NULL, &MenuSysconsKeyrate },
+ { "5 Saver", "Configure the screen saver", NULL, dmenuSubmenu, NULL, &MenuSysconsSaver },
+ { "6 Screenmap", "Choose an alternate screenmap", NULL, dmenuSubmenu, NULL, &MenuSysconsScrnmap },
+ { "7 Ttys", "Choose console terminal type", NULL, dmenuSubmenu, NULL, &MenuSysconsTtys },
+ { NULL } },
+};
+
+#ifdef PC98
+DMenu MenuSysconsKeymap = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "System Console Keymap",
+ "The default system console driver for FreeBSD (syscons) defaults\n"
+ "to a standard \"PC-98x1\" keyboard map. Users may wish to choose\n"
+ "one of the other keymaps below.\n"
+ "Note that sysinstall itself only uses the part of the keyboard map\n"
+ "which is required to generate the ANSI character subset, but your\n"
+ "choice of keymap will also be saved for later (fuller) use.",
+ "Choose a keyboard map",
+ NULL,
+ { { "Japanese PC-98x1", "Japanese PC-98x1 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=jp.pc98" },
+ { " Japanese PC-98x1 (ISO)", "Japanese PC-98x1 (ISO) keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=jp.pc98.iso" },
+ { NULL } },
+};
+#else
+DMenu MenuSysconsKeymap = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "System Console Keymap",
+ "The default system console driver for FreeBSD (syscons) defaults\n"
+ "to a standard \"American\" keyboard map. Users in other countries\n"
+ "(or with different keyboard preferences) may wish to choose one of\n"
+ "the other keymaps below.\n"
+ "Note that sysinstall itself only uses the part of the keyboard map\n"
+ "which is required to generate the ANSI character subset, but your\n"
+ "choice of keymap will also be saved for later (fuller) use.",
+ "Choose a keyboard map",
+ NULL,
+ { { "Belgian", "Belgian ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=be.iso" },
+ { " Brazil CP850", "Brazil CP850 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=br275.cp850" },
+ { " Brazil ISO (accent)", "Brazil ISO keymap (accent keys)", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=br275.iso.acc" },
+ { " Brazil ISO", "Brazil ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=br275.iso" },
+ { " Bulgarian BDS", "Bulgarian BDS keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=bg.bds.ctrlcaps" },
+ { " Bulgarian Phonetic", "Bulgarian Phonetic keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=bg.phonetic.ctrlcaps" },
+ { " Croatian ISO", "Croatian ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=hr.iso" },
+ { " Czech ISO (accent)", "Czech ISO keymap (accent keys)", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=cs.latin2.qwertz" },
+ { "Danish CP865", "Danish Code Page 865 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=danish.cp865" },
+ { " Danish ISO", "Danish ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=danish.iso" },
+ { "Estonian ISO", "Estonian ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=estonian.iso" },
+ { " Estonian ISO 15", "Estonian ISO 8859-15 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=estonian.iso15" },
+ { " Estonian CP850", "Estonian Code Page 850 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=estonian.cp850" },
+ { "Finnish CP850","Finnish Code Page 850 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=finnish.cp850" },
+ { " Finnish ISO", "Finnish ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=finnish.iso" },
+ { " French ISO (accent)", "French ISO keymap (accent keys)", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=fr.iso.acc" },
+ { " French ISO", "French ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=fr.iso" },
+ { "German CP850", "German Code Page 850 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=german.cp850" },
+ { " German ISO", "German ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=german.iso" },
+ { "Hungarian 101", "Hungarian ISO keymap (101 key)", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=hu.iso2.101keys" },
+ { " Hungarian 102", "Hungarian ISO keymap (102 key)", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=hu.iso2.102keys" },
+ { "Icelandic (accent)", "Icelandic ISO keymap (accent keys)", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=icelandic.iso.acc" },
+ { " Icelandic", "Icelandic ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=icelandic.iso" },
+ { " Italian", "Italian ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=it.iso" },
+ { "Japanese 106", "Japanese 106 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=jp.106" },
+ { "Latin American", "Latin American ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=lat-amer" },
+ { "Norway ISO", "Norwegian ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=norwegian.iso" },
+ { "Polish ISO", "Polish ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=pl_PL.ISO8859-2" },
+ { " Portuguese (accent)", "Portuguese ISO keymap (accent keys)", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=pt.iso.acc" },
+ { " Portuguese", "Portuguese ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=pt.iso" },
+ { "Russia KOI8-R", "Russian KOI8-R keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=ru.koi8-r" },
+ { "Slovenian", "Slovenian ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=si.iso" },
+ { " Spanish (accent)", "Spanish ISO keymap (accent keys)", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=spanish.iso.acc" },
+ { " Spanish", "Spanish ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=spanish.iso" },
+ { " Swedish CP850", "Swedish Code Page 850 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=swedish.cp850" },
+ { " Swedish ISO", "Swedish ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=swedish.iso" },
+ { " Swiss French ISO (accent)", "Swiss French ISO keymap (accent keys)", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=swissfrench.iso.acc" },
+ { " Swiss French ISO", "Swiss French ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=swissfrench.iso" },
+ { " Swiss French CP850", "Swiss French Code Page 850 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=swissfrench.cp850" },
+ { " Swiss German ISO (accent)", "Swiss German ISO keymap (accent keys)", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=swissgerman.iso.acc" },
+ { " Swiss German ISO", "Swiss German ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=swissgerman.iso" },
+ { " Swiss German CP850", "Swiss German Code Page 850 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=swissgerman.cp850" },
+ { "UK CP850", "UK Code Page 850 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=uk.cp850" },
+ { " UK ISO", "UK ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=uk.iso" },
+ { " Ukrainian KOI8-U", "Ukrainian KOI8-U keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=ua.koi8-u" },
+ { " Ukrainian KOI8-U+KOI8-R", "Ukrainian KOI8-U+KOI8-R keymap (alter)", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=ua.koi8-u.shift.alt" },
+ { " USA CapsLock->Ctrl", "US standard (Caps as L-Control)", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=us.pc-ctrl" },
+ { " USA Dvorak", "US Dvorak keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=us.dvorak" },
+ { " USA Dvorak (left)", "US left handed Dvorak keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=us.dvorakl" },
+ { " USA Dvorak (right)", "US right handed Dvorak keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=us.dvorakr" },
+ { " USA Emacs", "US standard optimized for EMACS", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=us.emacs" },
+ { " USA ISO", "US ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=us.iso" },
+ { " USA UNIX", "US traditional UNIX-workstation", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=us.unix" },
+ { NULL } },
+};
+#endif
+
+DMenu MenuSysconsKeyrate = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "System Console Keyboard Repeat Rate",
+ "This menu allows you to set the speed at which keys repeat\n"
+ "when held down.",
+ "Choose a keyboard repeat rate",
+ NULL,
+ { { "Slow", "Slow keyboard repeat rate", dmenuVarCheck, dmenuSetVariable, NULL, "keyrate=slow" },
+ { "Normal", "\"Normal\" keyboard repeat rate", dmenuVarCheck, dmenuSetVariable, NULL, "keyrate=normal" },
+ { "Fast", "Fast keyboard repeat rate", dmenuVarCheck, dmenuSetVariable, NULL, "keyrate=fast" },
+ { "Default", "Use default keyboard repeat rate", dmenuVarCheck, dmenuSetVariable, NULL, "keyrate=NO" },
+ { NULL } },
+};
+
+DMenu MenuSysconsSaver = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "System Console Screen Saver",
+ "By default, the console driver will not attempt to do anything\n"
+ "special with your screen when it's idle. If you expect to leave your\n"
+ "monitor switched on and idle for long periods of time then you should\n"
+ "probably enable one of these screen savers to prevent phosphor burn-in.",
+ "Choose a nifty-looking screen saver",
+ NULL,
+ { { "1 Blank", "Simply blank the screen",
+ dmenuVarCheck, configSaver, NULL, "saver=blank" },
+ { "2 Daemon", "\"BSD Daemon\" animated screen saver (text)",
+ dmenuVarCheck, configSaver, NULL, "saver=daemon" },
+ { "3 Fade", "Fade out effect screen saver",
+ dmenuVarCheck, configSaver, NULL, "saver=fade" },
+ { "4 Fire", "Flames effect screen saver",
+ dmenuVarCheck, configSaver, NULL, "saver=fire" },
+ { "5 Green", "\"Green\" power saving mode (if supported by monitor)",
+ dmenuVarCheck, configSaver, NULL, "saver=green" },
+ { "6 Logo", "\"BSD Daemon\" animated screen saver (graphics)",
+ dmenuVarCheck, configSaver, NULL, "saver=logo" },
+ { "7 Rain", "Rain drops screen saver",
+ dmenuVarCheck, configSaver, NULL, "saver=rain" },
+ { "8 Snake", "Draw a FreeBSD \"snake\" on your screen",
+ dmenuVarCheck, configSaver, NULL, "saver=snake" },
+ { "9 Star", "A \"twinkling stars\" effect",
+ dmenuVarCheck, configSaver, NULL, "saver=star" },
+ { "Warp", "A \"stars warping\" effect",
+ dmenuVarCheck, configSaver, NULL, "saver=warp" },
+ { "Timeout", "Set the screen saver timeout interval",
+ NULL, configSaverTimeout, NULL, NULL, ' ', ' ', ' ' },
+ { NULL } },
+};
+
+DMenu MenuSysconsScrnmap = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "System Console Screenmap",
+ "Unless you load a specific font, most PC hardware defaults to\n"
+ "displaying characters in the IBM 437 character set. However,\n"
+ "in the Unix world, this character set is very rarely used. Most\n"
+ "Western European countries, for example, prefer ISO 8859-1.\n"
+ "American users won't notice the difference since the bottom half\n"
+ "of all these character sets is ANSI anyway.\n"
+ "If your hardware is capable of downloading a new display font,\n"
+ "you should probably choose that option. However, for hardware\n"
+ "where this is not possible (e.g. monochrome adapters), a screen\n"
+ "map will give you the best approximation that your hardware can\n"
+ "display at all.",
+ "Choose a screen map",
+ NULL,
+ { { "1 None", "No screenmap, don't touch font", dmenuVarCheck, dmenuSetVariable, NULL, "scrnmap=NO" },
+ { "2 ISO 8859-1 to IBM437", "W-Europe ISO 8859-1 to IBM 437 screenmap", dmenuVarCheck, dmenuSetVariable, NULL, "scrnmap=iso-8859-1_to_cp437" },
+ { "3 ISO 8859-7 to IBM437", "Greek ISO 8859-7 to IBM 437 screenmap", dmenuVarCheck, dmenuSetVariable, NULL, "scrnmap=iso-8859-7_to_cp437" },
+ { "4 US-ASCII to IBM437", "US-ASCII to IBM 437 screenmap", dmenuVarCheck, dmenuSetVariable, NULL, "scrnmap=us-ascii_to_cp437" },
+ { "5 KOI8-R to IBM866", "Russian KOI8-R to IBM 866 screenmap", dmenuVarCheck, dmenuSetVariable, NULL, "scrnmap=koi8-r2cp866" },
+ { "6 KOI8-U to IBM866u", "Ukrainian KOI8-U to IBM 866u screenmap", dmenuVarCheck, dmenuSetVariable, NULL, "scrnmap=koi8-u2cp866u" },
+ { NULL } },
+};
+
+DMenu MenuSysconsTtys = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "System Console Terminal Type",
+ "For various console encodings, a corresponding terminal type\n"
+ "must be chosen in /etc/ttys.\n\n"
+ "WARNING: For compatibility reasons, only entries starting with\n"
+ "ttyv and terminal types starting with cons[0-9] can be changed\n"
+ "via this menu.\n",
+ "Choose a terminal type",
+ NULL,
+ { { "1 None", "Don't touch anything", dmenuVarCheck, dmenuSetVariable, NULL, VAR_CONSTERM "=NO" },
+ { "2 IBM437 (VGA default)", "cons25", dmenuVarCheck, dmenuSetVariable, NULL, VAR_CONSTERM "=cons25" },
+ { "3 ISO 8859-1", "cons25l1", dmenuVarCheck, dmenuSetVariable, NULL, VAR_CONSTERM "=cons25l1" },
+ { "4 ISO 8859-2", "cons25l2", dmenuVarCheck, dmenuSetVariable, NULL, VAR_CONSTERM "=cons25l2" },
+ { "5 ISO 8859-7", "cons25l7", dmenuVarCheck, dmenuSetVariable, NULL, VAR_CONSTERM "=cons25l7" },
+ { "6 KOI8-R", "cons25r", dmenuVarCheck, dmenuSetVariable, NULL, VAR_CONSTERM "=cons25r" },
+ { "7 KOI8-U", "cons25u", dmenuVarCheck, dmenuSetVariable, NULL, VAR_CONSTERM "=cons25u" },
+ { "8 US-ASCII", "cons25w", dmenuVarCheck, dmenuSetVariable, NULL, VAR_CONSTERM "=cons25w" },
+ { NULL } },
+};
+
+DMenu MenuSysconsFont = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "System Console Font",
+ "Most PC hardware defaults to displaying characters in the\n"
+ "IBM 437 character set. However, in the Unix world, this\n"
+ "character set is very rarely used. Most Western European\n"
+ "countries, for example, prefer ISO 8859-1.\n"
+ "American users won't notice the difference since the bottom half\n"
+ "of all these charactersets is ANSI anyway. However, they might\n"
+ "want to load a font anyway to use the 30- or 50-line displays.\n"
+ "If your hardware is capable of downloading a new display font,\n"
+ "you can select the appropriate font below.",
+ "Choose a font",
+ NULL,
+ { { "1 None", "Use hardware default font", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=NO,font8x14=NO,font8x16=NO" },
+ { "2 IBM 437", "English and others, VGA default", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=cp437-8x8,font8x14=cp437-8x14,font8x16=cp437-8x16" },
+ { "3 IBM 850", "Western Europe, IBM encoding", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=cp850-8x8,font8x14=cp850-8x14,font8x16=cp850-8x16" },
+ { "4 IBM 865", "Norwegian, IBM encoding", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=cp865-8x8,font8x14=cp865-8x14,font8x16=cp865-8x16" },
+ { "5 IBM 866", "Russian, IBM encoding (use with KOI8-R screenmap)", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=cp866-8x8,font8x14=cp866-8x14,font8x16=cp866b-8x16,mousechar_start=3" },
+ { "6 IBM 866u", "Ukrainian, IBM encoding (use with KOI8-U screenmap)", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=cp866u-8x8,font8x14=cp866u-8x14,font8x16=cp866u-8x16,mousechar_start=3" },
+ { "7 IBM 1251", "Cyrillic, MS Windows encoding", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=cp1251-8x8,font8x14=cp1251-8x14,font8x16=cp1251-8x16,mousechar_start=3" },
+ { "8 ISO 8859-1", "Western Europe, ISO encoding", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=iso-8x8,font8x14=iso-8x14,font8x16=iso-8x16" },
+ { "9 ISO 8859-2", "Eastern Europe, ISO encoding", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=iso02-8x8,font8x14=iso02-8x14,font8x16=iso02-8x16" },
+ { "a ISO 8859-4", "Baltic, ISO encoding", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=iso04-8x8,font8x14=iso04-8x14,font8x16=iso04-8x16" },
+ { "b ISO 8859-7", "Greek, ISO encoding", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=iso07-8x8,font8x14=iso07-8x14,font8x16=iso07-8x16" },
+ { "c ISO 8859-8", "Hebrew, ISO encoding", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=iso08-8x8,font8x14=iso08-8x14,font8x16=iso08-8x16" },
+ { "d ISO 8859-15", "Europe, ISO encoding", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=iso15-8x8,font8x14=iso15-8x14,font8x16=iso15-8x16" },
+ { "e SWISS", "English, better resolution", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=swiss-8x8,font8x14=NO,font8x16=swiss-8x16" },
+ { NULL } },
+};
+
+DMenu MenuUsermgmt = {
+ DMENU_NORMAL_TYPE,
+ "User and group management",
+ "The submenus here allow to manipulate user groups and\n"
+ "login accounts.\n",
+ "Configure your user groups and users",
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)", NULL, dmenuExit },
+ { "User", "Add a new user to the system.", NULL, userAddUser },
+ { "Group", "Add a new user group to the system.", NULL, userAddGroup },
+ { NULL } },
+};
+
+DMenu MenuSecurity = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "System Security Options Menu",
+ "This menu allows you to configure aspects of the operating system security\n"
+ "policy. Please read the system documentation carefully before modifying\n"
+ "these settings, as they may cause service disruption if used improperly.\n"
+ "\n"
+ "Most settings will take affect only following a system reboot.",
+ "Configure system security options",
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { " Security Profile", "Select a security profile for the system",
+ NULL, configSecurityProfile },
+ { " LOMAC", "Use Low Watermark Mandatory Access Control at boot",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "lomac_enable=YES" },
+ { " NFS port", "Require that the NFS clients used reserved ports",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "nfs_reserved_port_only=YES" },
+ { NULL } },
+};
+
+DMenu MenuSecurityProfile = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Default system security profile",
+ "Each item in this list will set what it considers to\n"
+ "be \"appropriate\" values in that category for various\n"
+ "security-related knobs in /etc/rc.conf.",
+ "Select a canned security profile - F1 for help",
+ "security", /* help file */
+ { { "X Exit", "Exit this menu (returning to previous)", NULL, dmenuExit },
+ { "Medium", "Moderate security settings.", NULL, configSecurityModerate },
+ { "Extreme", "Very restrictive security settings.", NULL, configSecurityExtreme },
+ { NULL } },
+};
+
+DMenu MenuFixit = {
+ DMENU_NORMAL_TYPE,
+ "Please choose a fixit option",
+ "There are three ways of going into \"fixit\" mode:\n"
+ "- you can use the live filesystem CDROM/DVD, in which case there will be\n"
+ " full access to the complete set of FreeBSD commands and utilities,\n"
+ "- you can use the more limited (but perhaps customized) fixit floppy,\n"
+ "- or you can start an Emergency Holographic Shell now, which is\n"
+ " limited to the subset of commands that is already available right now.",
+ "Press F1 for more detailed repair instructions",
+ "fixit",
+{ { "X Exit", "Exit this menu (returning to previous)", NULL, dmenuExit },
+ { "2 CDROM/DVD", "Use the \"live\" filesystem CDROM/DVD", NULL, installFixitCDROM },
+ { "3 Floppy", "Use a floppy generated from the fixit image", NULL, installFixitFloppy },
+ { "4 Shell", "Start an Emergency Holographic Shell", NULL, installFixitHoloShell },
+ { NULL } },
+};
diff --git a/usr.sbin/sade/misc.c b/usr.sbin/sade/misc.c
new file mode 100644
index 0000000..0965828
--- /dev/null
+++ b/usr.sbin/sade/misc.c
@@ -0,0 +1,484 @@
+/*
+ * Miscellaneous support routines..
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <sys/file.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/wait.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <ufs/ufs/ufsmount.h>
+#include <sys/reboot.h>
+#include <sys/disklabel.h>
+
+/* Quick check to see if a file is readable */
+Boolean
+file_readable(char *fname)
+{
+ if (!access(fname, F_OK))
+ return TRUE;
+ return FALSE;
+}
+
+/* Quick check to see if a file is executable */
+Boolean
+file_executable(char *fname)
+{
+ if (!access(fname, X_OK))
+ return TRUE;
+ return FALSE;
+}
+
+/* Concatenate two strings into static storage */
+char *
+string_concat(char *one, char *two)
+{
+ static char tmp[FILENAME_MAX];
+
+ /* Yes, we're deliberately cavalier about not checking for overflow */
+ strcpy(tmp, one);
+ strcat(tmp, two);
+ return tmp;
+}
+
+/* sane strncpy() function */
+char *
+sstrncpy(char *dst, const char *src, int size)
+{
+ dst[size] = '\0';
+ return strncpy(dst, src, size);
+}
+
+/* Concatenate three strings into static storage */
+char *
+string_concat3(char *one, char *two, char *three)
+{
+ static char tmp[FILENAME_MAX];
+
+ /* Yes, we're deliberately cavalier about not checking for overflow */
+ strcpy(tmp, one);
+ strcat(tmp, two);
+ strcat(tmp, three);
+ return tmp;
+}
+
+/* Clip the whitespace off the end of a string */
+char *
+string_prune(char *str)
+{
+ int len = str ? strlen(str) : 0;
+
+ while (len && isspace(str[len - 1]))
+ str[--len] = '\0';
+ return str;
+}
+
+/* run the whitespace off the front of a string */
+char *
+string_skipwhite(char *str)
+{
+ while (*str && isspace(*str))
+ ++str;
+ return str;
+}
+
+/* copy optionally and allow second arg to be null */
+char *
+string_copy(char *s1, char *s2)
+{
+ if (!s1)
+ return NULL;
+ if (!s2)
+ s1[0] = '\0';
+ else
+ strcpy(s1, s2);
+ return s1;
+}
+
+/* convert an integer to a string, using a static buffer */
+char *
+itoa(int value)
+{
+ static char buf[13];
+
+ snprintf(buf, 12, "%d", value);
+ return buf;
+}
+
+Boolean
+directory_exists(const char *dirname)
+{
+ DIR *tptr;
+
+ if (!dirname)
+ return FALSE;
+ if (!strlen(dirname))
+ return FALSE;
+
+ tptr = opendir(dirname);
+ if (!tptr)
+ return (FALSE);
+
+ closedir(tptr);
+ return (TRUE);
+}
+
+char *
+pathBaseName(const char *path)
+{
+ char *pt;
+ char *ret = (char *)path;
+
+ pt = strrchr(path,(int)'/');
+
+ if (pt != 0) /* if there is a slash */
+ {
+ ret = ++pt; /* start the file after it */
+ }
+
+ return(ret);
+}
+
+/* A free guaranteed to take NULL ptrs */
+void
+safe_free(void *ptr)
+{
+ if (ptr)
+ free(ptr);
+}
+
+/* A malloc that checks errors */
+void *
+safe_malloc(size_t size)
+{
+ void *ptr;
+
+ if (size <= 0)
+ msgFatal("Invalid malloc size of %ld!", (long)size);
+ ptr = malloc(size);
+ if (!ptr)
+ msgFatal("Out of memory!");
+ bzero(ptr, size);
+ return ptr;
+}
+
+/* A realloc that checks errors */
+void *
+safe_realloc(void *orig, size_t size)
+{
+ void *ptr;
+
+ if (size <= 0)
+ msgFatal("Invalid realloc size of %ld!", (long)size);
+ ptr = 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 = (long)aux;
+ return list;
+}
+
+/* Toss the items out */
+void
+items_free(dialogMenuItem *list, int *curr, int *max)
+{
+ int i;
+
+ for (i = 0; list[i].prompt; i++) {
+ safe_free(list[i].prompt);
+ safe_free(list[i].title);
+ }
+ safe_free(list);
+ *curr = *max = 0;
+}
+
+int
+Mkdir(char *ipath)
+{
+ struct stat sb;
+ int final;
+ char *p, *path;
+
+ if (file_readable(ipath) || Fake)
+ return DITEM_SUCCESS;
+
+ path = strcpy(alloca(strlen(ipath) + 1), ipath);
+ if (isDebug())
+ msgDebug("mkdir(%s)\n", path);
+ p = path;
+ if (p[0] == '/') /* Skip leading '/'. */
+ ++p;
+ for (final = FALSE; !final; ++p) {
+ if (p[0] == '\0' || (p[0] == '/' && p[1] == '\0'))
+ final = TRUE;
+ else if (p[0] != '/')
+ continue;
+ *p = '\0';
+ if (stat(path, &sb)) {
+ if (errno != ENOENT) {
+ msgConfirm("Couldn't stat directory %s: %s", path, strerror(errno));
+ return DITEM_FAILURE;
+ }
+ if (isDebug())
+ msgDebug("mkdir(%s..)\n", path);
+ if (mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO) < 0) {
+ msgConfirm("Couldn't create directory %s: %s", path,strerror(errno));
+ return DITEM_FAILURE;
+ }
+ }
+ *p = '/';
+ }
+ return DITEM_SUCCESS;
+}
+
+int
+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..4625ce2
--- /dev/null
+++ b/usr.sbin/sade/msg.c
@@ -0,0 +1,356 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <stdarg.h>
+#include <sys/ioctl.h>
+#include <sys/consio.h>
+
+Boolean
+isDebug(void)
+{
+ char *cp;
+
+ return (cp = variable_get(VAR_DEBUG)) && strcmp(cp, "no");
+}
+
+/* Whack up an informational message on the status line, in stand-out */
+void
+msgYap(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ int attrs;
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ va_start(args, fmt);
+ vsnprintf(errstr, FILENAME_MAX, fmt, args);
+ va_end(args);
+ attrs = getattrs(stdscr);
+ attrset(A_REVERSE);
+ mvaddstr(StatusLine, 0, errstr);
+ attrset(attrs);
+ refresh();
+}
+
+/* Whack up an informational message on the status line */
+void
+msgInfo(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ int i, attrs;
+ char line[81];
+
+ attrs = getattrs(stdscr);
+ /* NULL is a special convention meaning "erase the old stuff" */
+ if (!fmt) {
+ move(StatusLine, 0);
+ clrtoeol();
+ return;
+ }
+ errstr = (char *)alloca(FILENAME_MAX);
+ va_start(args, fmt);
+ vsnprintf(errstr, FILENAME_MAX, fmt, args);
+ va_end(args);
+ memset(line, ' ', 80);
+ for (i = 0; i < 80; i++) {
+ if (errstr[i])
+ line[i] = errstr[i];
+ else
+ break;
+ }
+ line[80] = '\0';
+ attrset(ATTR_TITLE);
+ mvaddstr(StatusLine, 0, line);
+ attrset(attrs);
+ move(StatusLine, 79);
+ refresh();
+}
+
+/* Whack up a warning on the status line */
+void
+msgWarn(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ int attrs;
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ strcpy(errstr, "Warning: ");
+ va_start(args, fmt);
+ vsnprintf((char *)(errstr + strlen(errstr)), FILENAME_MAX, fmt, args);
+ va_end(args);
+ attrs = getattrs(stdscr);
+ beep();
+ attrset(ATTR_TITLE);
+ mvaddstr(StatusLine, 0, errstr);
+ attrset(attrs);
+ refresh();
+ if (OnVTY && isDebug())
+ msgDebug("Warning message `%s'\n", errstr);
+}
+
+/* Whack up an error on the status line */
+void
+msgError(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ int attrs;
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ strcpy(errstr, "Error: ");
+ va_start(args, fmt);
+ vsnprintf((char *)(errstr + strlen(errstr)), FILENAME_MAX, fmt, args);
+ va_end(args);
+ beep();
+ attrs = getattrs(stdscr);
+ attrset(ATTR_TITLE);
+ mvaddstr(StatusLine, 0, errstr);
+ attrset(attrs);
+ refresh();
+ if (OnVTY && isDebug())
+ msgDebug("Error message `%s'\n", errstr);
+}
+
+/* Whack up a fatal error on the status line */
+void
+msgFatal(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ int attrs;
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ strcpy(errstr, "Fatal Error: ");
+ va_start(args, fmt);
+ vsnprintf((char *)(errstr + strlen(errstr)), FILENAME_MAX, fmt, args);
+ va_end(args);
+ beep();
+ attrs = getattrs(stdscr);
+ attrset(ATTR_TITLE);
+ mvaddstr(StatusLine, 0, errstr);
+ addstr(" - ");
+ addstr("PRESS ANY KEY TO ");
+ if (getpid() == 1)
+ addstr("REBOOT");
+ else
+ addstr("QUIT");
+ attrset(attrs);
+ refresh();
+ if (OnVTY)
+ msgDebug("Fatal error `%s'!\n", errstr);
+ getch();
+ systemShutdown(1);
+}
+
+/* Put up a message in a popup confirmation box */
+void
+msgConfirm(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ WINDOW *w = savescr();
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ va_start(args, fmt);
+ vsnprintf(errstr, FILENAME_MAX, fmt, args);
+ va_end(args);
+ use_helpline(NULL);
+ use_helpfile(NULL);
+ if (OnVTY) {
+ ioctl(0, VT_ACTIVATE, 1);
+ msgInfo(NULL);
+ }
+ dialog_notify(errstr);
+ restorescr(w);
+}
+
+/* Put up a message in a popup information box */
+void
+msgNotify(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ va_start(args, fmt);
+ vsnprintf(errstr, FILENAME_MAX, fmt, args);
+ va_end(args);
+ use_helpline(NULL);
+ use_helpfile(NULL);
+ if (isDebug())
+ msgDebug("Notify: %s\n", errstr);
+ dialog_msgbox(NULL, errstr, -1, -1, 0);
+}
+
+/* Put up a message in a popup yes/no box and return 0 for YES, 1 for NO */
+int
+msgYesNo(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ int ret;
+ WINDOW *w = savescr();
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ va_start(args, fmt);
+ vsnprintf(errstr, FILENAME_MAX, fmt, args);
+ va_end(args);
+ use_helpline(NULL);
+ use_helpfile(NULL);
+ if (OnVTY) {
+ ioctl(0, VT_ACTIVATE, 1); /* Switch back */
+ msgInfo(NULL);
+ }
+ if (variable_get(VAR_NONINTERACTIVE))
+ return 0; /* If non-interactive, return YES all the time */
+ ret = dialog_yesno("User Confirmation Requested", errstr, -1, -1);
+ restorescr(w);
+ return ret;
+}
+
+/* Put up a message in a popup no/yes box and return 0 for YES, 1 for NO */
+int
+msgNoYes(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ int ret;
+ WINDOW *w = savescr();
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ va_start(args, fmt);
+ vsnprintf(errstr, FILENAME_MAX, fmt, args);
+ va_end(args);
+ use_helpline(NULL);
+ use_helpfile(NULL);
+ if (OnVTY) {
+ ioctl(0, VT_ACTIVATE, 1); /* Switch back */
+ msgInfo(NULL);
+ }
+ if (variable_get(VAR_NONINTERACTIVE))
+ return 1; /* If non-interactive, return NO all the time */
+ ret = dialog_noyes("User Confirmation Requested", errstr, -1, -1);
+ restorescr(w);
+ return ret;
+}
+
+/* Put up a message in an input box and return the value */
+char *
+msgGetInput(char *buf, char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ static char input_buffer[256];
+ int rval;
+ WINDOW *w = savescr();
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ va_start(args, fmt);
+ vsnprintf(errstr, FILENAME_MAX, fmt, args);
+ va_end(args);
+ use_helpline(NULL);
+ use_helpfile(NULL);
+ if (buf)
+ SAFE_STRCPY(input_buffer, buf);
+ else
+ input_buffer[0] = '\0';
+ if (OnVTY) {
+ ioctl(0, VT_ACTIVATE, 1); /* Switch back */
+ msgInfo(NULL);
+ }
+ rval = dialog_inputbox("Value Required", errstr, -1, -1, input_buffer);
+ restorescr(w);
+ if (!rval)
+ return input_buffer;
+ else
+ return NULL;
+}
+
+/* Write something to the debugging port */
+void
+msgDebug(char *fmt, ...)
+{
+ va_list args;
+ char *dbg;
+
+ if (DebugFD == -1)
+ return;
+ dbg = (char *)alloca(FILENAME_MAX);
+ strcpy(dbg, "DEBUG: ");
+ va_start(args, fmt);
+ vsnprintf((char *)(dbg + strlen(dbg)), FILENAME_MAX, fmt, args);
+ va_end(args);
+ write(DebugFD, dbg, strlen(dbg));
+}
+
+/* Tell the user there's some output to go look at */
+void
+msgWeHaveOutput(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ WINDOW *w = savescr();
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ va_start(args, fmt);
+ vsnprintf(errstr, FILENAME_MAX, fmt, args);
+ va_end(args);
+ use_helpline(NULL);
+ use_helpfile(NULL);
+ msgDebug("Notify: %s\n", errstr);
+ dialog_clear_norefresh();
+ sleep(2);
+ dialog_msgbox(NULL, errstr, -1, -1, 0);
+ restorescr(w);
+}
+
+/* Simple versions of msgConfirm() and msgNotify() for calling from scripts */
+int
+msgSimpleConfirm(char *str)
+{
+ msgConfirm("%s", str);
+ return DITEM_SUCCESS;
+}
+
+int
+msgSimpleNotify(char *str)
+{
+ msgNotify("%s", str);
+ return DITEM_SUCCESS;
+}
diff --git a/usr.sbin/sade/rtermcap.c b/usr.sbin/sade/rtermcap.c
new file mode 100644
index 0000000..84b3feb
--- /dev/null
+++ b/usr.sbin/sade/rtermcap.c
@@ -0,0 +1,15 @@
+#include <stdio.h>
+#include <termcap.h>
+
+int
+main(int argc, char **argv)
+{
+ char buf[4096];
+ int i;
+
+ if (argc < 2)
+ return 1;
+ i = tgetent(buf, argv[1]);
+ printf("%s",buf);
+ return 0;
+}
diff --git a/usr.sbin/sade/sade.8 b/usr.sbin/sade/sade.8
new file mode 100644
index 0000000..201b8e7
--- /dev/null
+++ b/usr.sbin/sade/sade.8
@@ -0,0 +1,1003 @@
+.\" Copyright (c) 1997
+.\" Jordan Hubbard <jkh@FreeBSD.org>. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY Jordan Hubbard AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL Jordan Hubbard OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd August 9, 1997
+.Dt SYSINSTALL 8
+.Os
+.Sh NAME
+.Nm sysinstall
+.Nd system installation and configuration tool
+.Sh SYNOPSIS
+.Nm
+.Op Ar var=value
+.Op Ar function
+.Op Ar ...
+.Sh DESCRIPTION
+.Nm
+is a utility for installing and configuring
+.Fx
+systems.
+It is the first utility invoked by the
+.Fx
+installation boot
+floppy and is also copied into
+.Pa /stand/sysinstall
+on newly installed
+.Fx
+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.
+.Pp
+On those occasions where it is deemed necessary to invoke a subsystem
+of sysinstall directly, however, it is also possible to do so by
+naming the appropriate function entry points on the command line.
+Since this action is essentially identical to running an installation
+script, each command-line argument corresponding to a line of script,
+the reader is encouraged to read the section on scripting for more
+information on this feature.
+.Sh NOTES
+.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
+.Fx
+systems. It also contains some extra intelligence
+for running as a replacement for
+.Xr init 8
+when it's invoked by the
+.Fx
+installation boot procedure. It
+assumes very little in the way of additional utility support and
+performs most file system operations by calling the relevant syscalls
+(such as
+.Xr mount 2 )
+directly.
+.Pp
+.Nm
+currently uses the
+.Xr dialog 3
+library to do user interaction with simple ANSI line graphics, color
+support for which is enabled by either running on a syscons VTY or some
+other color-capable terminal emulator (newer versions of xterm will support
+color when using the
+.Dq xterm-color
+termcap entry).
+.Pp
+This product is currently at the end of its life cycle and will
+eventually be replaced.
+.Sh RUNNING SCRIPTS
+.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
+.It Sy "LOAD_CONFIG_FILE"
+If
+.Nm
+is compiled with LOAD_CONFIG_FILE set in the environment
+(or in the Makefile) to some value, then that value will
+be used as the filename to automatically look for and load
+when
+.Nm
+starts up and with no user interaction required.
+This option is aimed primarily at large sites who wish to create a
+single prototype install for multiple machines with largely identical
+configurations and/or installation options.
+.It Sy "MAIN MENU"
+If
+.Nm
+is run interactively, that is to say in the default manner, it will
+bring up a main menu which contains a "load config file" option.
+Selecting this option will prompt for the name of a script file which
+it then will attempt to load from a DOS or UFS formatted floppy.
+.It Sy "COMMAND LINE"
+Each command line argument is treated as a script directive
+when
+.Nm
+is run in multi-user mode. Execution ends either by explicit request
+(e.g. calling the
+.Ar shutdown
+directive), upon reaching the end of the argument list or on error.
+.Pp
+For example:
+.Bd -literal
+/stand/sysinstall _ftpPath=ftp://ziggy/pub/ mediaSetFTP configPackages
+.Ed
+.Pp
+Would initialize
+.Nm
+for FTP installation media (using the server `ziggy') and then
+bring up the package installation editor, exiting when finished.
+.El
+.Sh SCRIPT SYNTAX
+A script is a list of one or more directives, each directive taking
+the form of:
+.Pp
+.Ar var=value
+.Pp
+.Ar function
+.Pp
+or
+.Ar #somecomment
+.Pp
+Where
+.Ar var=value
+is the assignment of some internal
+.Nm
+variable, e.g. "ftpPass=FuNkYChiKn", and
+.Ar function
+is the name of an internal
+.Nm
+function, e.g. "mediaSetFTP", and
+.Ar #comment
+is a single-line comment for documentation purposes (ignored by
+sysinstall). Each directive must be by itself on a single line,
+functions taking their arguments by examining known variable names.
+This requires that you be sure to assign the relevant variables before
+calling a function which requires them.
+.Pp
+The
+.Ar noError
+variable can be assigned before each directive: this will cause any error
+detected while processing the directive itself to be ignored.
+The value of
+.Ar noError
+will automatically reset to the default "unassigned" every time a directive is
+processed.
+.Pp
+When and where a function depends on the settings of one or more variables
+will be noted in the following table:
+.Pp
+.Sy "Function Glossary" :
+.Pp
+.Bl -tag -width indent
+.It configAnonFTP
+Invoke the Anonymous FTP configuration menu.
+.Pp
+.Sy Variables :
+None
+.It configRouter
+Select which routing daemon you wish to use, potentially
+loading any required 3rd-party routing daemons as necessary.
+.Pp
+.Sy Variables :
+.Bl -tag -width indent
+.It router
+can be set to the name of the desired routing daemon,
+e.g.\&
+.Dq routed
+or
+.Dq gated ,
+otherwise it is prompted for.
+.El
+.It configNFSServer
+Configure host as an NFS server.
+.Pp
+.Sy Variables :
+None
+.It configNTP
+Configure host as a user of the Network Time Protocol.
+.Pp
+.Sy Variables :
+.Bl -tag -width indent
+.It ntpdate_flags
+The flags to
+.Xr ntpdate 8 ,
+that is to say the name of the server to sync from.
+.El
+.It configPCNFSD
+Configure host to support PC NFS.
+.Pp
+.Sy Variables :
+.Bl -tag -width indent
+.It pcnfsd_pkg
+The name of the PCNFSD package to load if necessary (defaults to hard coded
+version).
+.El
+.It configPackages
+Bring up the interactive package management menu.
+.Pp
+.Sy Variables :
+None
+.It configUsers
+Add users and/or groups to the system.
+.Pp
+.Sy Variables :
+None
+.It configXEnvironment
+Configure the X display subsystem.
+.Pp
+.Sy Variables :
+None
+.It diskPartitionEditor
+Invokes the disk partition (MBR) editor.
+.Pp
+.Sy Variables :
+.Bl -tag -width findx
+.It geometry
+The disk geometry, as a cyls/heads/sectors formatted string. Default: no
+change to geometry.
+.It partition
+Set to disk partitioning type or size, its value being
+.Ar free
+in order to use only remaining free space for
+.Fx ,
+.Ar all
+to use the entire disk for
+.Fx
+but maintain a proper partition
+table,
+.Ar existing
+to use an existing
+.Fx
+partition (first found),
+.Ar exclusive
+to use the disk in
+.Dq dangerously dedicated
+mode or, finally,
+.Ar somenumber
+to allocate
+.Ar somenumber
+blocks of available free space to a new
+.Fx
+partition.
+Default: Interactive mode.
+.It bootManager
+is set to one of
+.Ar boot
+to signify the installation of a boot manager,
+.Ar standard
+to signify installation of a "standard" non-boot MGR DOS
+MBR or
+.Ar none
+to indicate that no change to the boot manager is desired.
+Default: none.
+.It diskInteractive
+If set, bring up the interactive disk partition editor.
+.El
+.Pp
+Note: Nothing is actually written to disk by this function, 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
+.Sy Variables :
+None
+.It diskLabelEditor
+Invokes the disk label editor. This is a bit trickier from a script
+since you need to essentially label everything inside each
+.Fx
+(type 0xA5) partition created by the
+.Ar diskPartitionEditor
+function, and that requires knowing a few rules about how things are
+laid out. When creating a script to automatically allocate disk space
+and partition it up, it is suggested that you first perform the
+installation interactively at least once and take careful notes as to
+what the slice names will be, then and only then hardwiring them into
+the script.
+.Pp
+For example, let's say you have a SCSI disk on which you've created a new
+.Fx
+partition in slice 2 (your DOS partition residing in slice 1).
+The slice name would be
+.Ar da0s2
+for the whole
+.Fx
+partition
+.Ar ( da0s1
+being your DOS primary
+partition). Now let's further assume that you have 500MB in this
+partition and you want to sub-partition that space into root, swap,
+var and usr file systems for
+.Fx .
+Your invocation of the
+.Ar diskLabelEditor
+function might involve setting the following variables:
+.Bl -tag -width findx
+.It Li "da0s2-1=ufs 40960 /"
+A 20MB root file system (all sizes are in 512 byte blocks).
+.It Li "da0s2-2=swap 131072 /"
+A 64MB swap partition.
+.It Li "da0s2-3=ufs 204800 /var"
+A 100MB /var file system.
+.It Li "da0s2-4=ufs 0 /usr 1"
+With the balance of free space (around 316MB) going to the /usr
+file system and with soft-updates enabled (the argument following
+the mount point, if non-zero, means to set the soft updates flag).
+.El
+.Pp
+One can also use the
+.Ar diskLabelEditor
+for mounting or erasing existing partitions as well as creating new
+ones. Using the previous example again, let's say that we also wanted
+to mount our DOS partition and make sure that an
+.Pa /etc/fstab
+entry is created for it in the new installation. Before calling the
+.Ar diskLabelEditor
+function, we simply add an additional line:
+.Pp
+.Dl "da0s1=/dos_c N"
+.Pp
+before the call. This tells the label editor that you want to mount
+the first slice on
+.Pa /dos_c
+and not to attempt to newfs it (not that
+.Nm
+would attempt this for a DOS partition in any case, but it could just
+as easily be an existing UFS partition being named here and the 2nd
+field is non-optional).
+.Pp
+You can also set the
+.Ar diskInteractive
+variable to request that the disk label editor use an interactive dialog
+to partition the disk instead of using variables to explicitly layout the
+disk as described above.
+.Pp
+Note: No file system data is actually written to disk until an
+explicit call to
+.Ar diskLabelCommit
+is made.
+.It diskLabelCommit
+Writes out all pending disklabel information and creates and/or mounts any
+file systems which have requests pending from the
+.Ar diskLabelEditor
+function.
+.Pp
+.Sy Variables :
+None
+.It distReset
+Resets all selected distributions to the empty set (no distributions selected).
+.Pp
+.Sy Variables :
+None
+.It distSetCustom
+Allows the selection of a custom distribution set (e.g. not just on of the
+existing "canned" sets) with no user interaction.
+.Pp
+.Sy Variables :
+.Bl -tag -width indent
+.It dists
+List of distributions to load. Possible distribution values are:
+.Bl -tag -width indentxx
+.It Li 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 crypto
+Encryption binaries and libraries.
+.It Li compat1x
+Compatibility with
+.Fx
+1.x
+.It Li compat20
+Compatibility with
+.Fx 2.0
+.It Li compat21
+Compatibility with
+.Fx 2.1
+.It Li compat22
+.Fx 2.2
+and
+.Fx 3.0
+a.out binary compatibility
+.It Li compat3x
+Compatibility with
+.Fx
+3.x
+(available for
+.Fx 4.0
+systems only)
+.It Li compat4x
+Compatibility with
+.Fx
+4.x
+(available for
+.Fx 5.0
+systems only)
+.It Li ports
+The ports collection.
+.It Li krb4
+KerberosIV binaries.
+.It Li krb5
+Kerberos5 binaries.
+.It Li ssecure
+/usr/src/secure
+.It Li sbase
+/usr/src/[top level files]
+.It Li scontrib
+/usr/src/contrib
+.It Li sgnu
+/usr/src/gnu
+.It Li setc
+/usr/src/etc
+.It Li sgames
+/usr/src/games
+.It Li sinclude
+/usr/src/include
+.It Li slib
+/usr/src/lib
+.It Li slibexec
+/usr/src/libexec
+.It Li srelease
+/usr/src/release
+.It Li sbin
+/usr/src/bin
+.It Li ssbin
+/usr/src/sbin
+.It Li sshare
+/usr/src/share
+.It Li ssys
+/usr/src/sys
+.It Li subin
+/usr/src/usr.bin
+.It Li susbin
+/usr/src/usr.sbin
+.It Li ssmailcf
+/usr/src/usr.sbin/sendmail/cf
+.It Li XF86-xc
+XFree86 official sources.
+.It Li XF86-co
+XFree86 contributed sources.
+.It Li Xbin
+XFree86 binaries.
+.It Li Xcfg
+XFree86 configuration files.
+.It Li Xdoc
+XFree86 documentation.
+.It Li Xhtml
+XFree86 HTML documentation.
+.It Li Xlib
+XFree86 libraries.
+.It Li Xlk98
+XFree86 server link-kit for PC98 machines.
+.It Li Xlkit
+XFree86 server link-kit for standard machines.
+.It Li Xman
+XFree86 manual pages.
+.It Li Xprog
+XFree86 programmer's distribution.
+.It Li Xps
+XFree86 postscript documentation.
+.It Li Xset
+XFree86 graphical setup tool.
+.It Li PC98-Servers/X9480
+XFree86 PC98 8-bit (256 color) PEGC-480 server.
+.It Li PC98-Servers/X9EGC
+XFree86 PC98 4-bit (16 color) EGC server.
+.It Li PC98-Servers/X9GA9
+XFree86 PC98 GA-968V4/PCI (S3 968) server.
+.It Li PC98-Servers/X9GAN
+XFree86 PC98 GANB-WAP (cirrus) server.
+.It Li PC98-Servers/X9LPW
+XFree86 PC98 PowerWindowLB (S3) server.
+.It Li PC98-Servers/X9MGA
+[DESCRIPTION MISSING]
+.It Li PC98-Servers/X9NKV
+XFree86 PC98 NKV-NEC (cirrus) server.
+.It Li PC98-Servers/X9NS3
+XFree86 PC98 NEC (S3) server.
+.It Li PC98-Servers/X9SPW
+XFree86 PC98 SKB-PowerWindow (S3) server.
+.It Li PC98-Servers/X9SVG
+[DESCRIPTION MISSING]
+.It Li PC98-Servers/X9TGU
+XFree86 PC98 Cyber9320 and TGUI9680 server.
+.It Li PC98-Servers/X9WEP
+XFree86 PC98 WAB-EP (cirrus) server.
+.It Li PC98-Servers/X9WS
+XFree86 PC98 WABS (cirrus) server.
+.It Li PC98-Servers/X9WSN
+XFree86 PC98 WSN-A2F (cirrus) server.
+.It Li Servers/X3DL
+XFree86 3D Labs server.
+.It Li Servers/X8514
+XFree86 8514 server.
+.It Li Servers/XAGX
+XFree86 8 bit AGX server.
+.It Li Servers/XI128
+XFree86 #9 Imagine I128 server.
+.It Li Servers/XMa8
+XFree86 ATI Mach8 server.
+.It Li Servers/XMa32
+XFree86 ATI Mach32 server.
+.It Li Servers/XMa64
+XFree86 ATI Mach64 server.
+.It Li Servers/XMono
+XFree86 monochrome server.
+.It Li Servers/XP9K
+XFree86 P9000 server.
+.It Li Servers/XS3
+XFree86 S3 server.
+.It Li Servers/XS3V
+XFree86 S3 Virge server.
+.It Li Servers/XSVGA
+XFree86 SVGA server.
+.It Li Servers/XVG16
+XFree86 VGA16 server.
+.It Li Servers/XW32
+XFree86 ET4000/W32, /W32i and /W32p server.
+.It Li Servers/XTGA
+Server for TGA cards (alpha architecture only).
+.It Li Servers/Xnest
+XFree86 nested X server.
+.It Li Servers/Xvfb
+XFree86 virtual frame-buffer X server.
+.It Li Xfnts
+XFree86 base font set.
+.It Li Xf100
+XFree86 100DPI font set.
+.It Li Xfcyr
+XFree86 Cyrillic font set.
+.It Li Xfscl
+XFree86 scalable font set.
+.It Li Xfnon
+XFree86 non-english font set.
+.It Li Xfsrv
+XFree86 font server.
+.El
+.El
+.It distSetDeveloper
+Selects the standard Developer's distribution set.
+.Pp
+.Sy Variables :
+None
+.It distSetXDeveloper
+Selects the standard X Developer's distribution set.
+.Pp
+.Sy Variables :
+None
+.It distSetKernDeveloper
+Selects the standard kernel Developer's distribution set.
+.Pp
+.Sy Variables :
+None
+.It distSetUser
+Selects the standard user distribution set.
+.Pp
+.Sy Variables :
+None
+.It distSetXUser
+Selects the standard X user's distribution set.
+.Pp
+.Sy Variables :
+None
+.It distSetMinimum
+Selects the very minimum distribution set.
+.Pp
+.Sy Variables :
+None
+.It distSetEverything
+Selects the full whack - all available distributions.
+.Pp
+.Sy Variables :
+None
+.It distSetCRYPTO
+Interactively select encryption subcomponents.
+.Pp
+.Sy Variables :
+None
+.It distSetSrc
+Interactively select source subcomponents.
+.Pp
+.Sy Variables :
+None
+.It distSetXF86
+Interactively select XFree86 subcomponents.
+.Pp
+.Sy Variables :
+None
+.It distExtractAll
+Install all currently selected distributions (requires that
+media device also be selected).
+.Pp
+.Sy Variables :
+None
+.It docBrowser
+Install (if necessary) an HTML documentation browser and go to the
+HTML documentation submenu.
+.Pp
+.Sy Variables :
+.Bl -tag -width indent
+.It browserPackage
+The name of the browser package to try and install as necessary.
+Defaults to latest links package.
+.It browserBinary
+The name of the browser binary itself (if overriding the
+.Ar browserPackage
+variable). Defaults to links.
+.El
+.It installCommit
+Commit any and all pending changes to disk. This function
+is essentially shorthand for a number of more granular "commit"
+functions.
+.Pp
+.Sy Variables :
+None
+.It installExpress
+Start an "express" installation, asking few questions of
+the user.
+.Pp
+.Sy Variables :
+None
+.It installStandard
+Start a "standard" installation, the most user-friendly
+installation type available.
+.Pp
+.Sy Variables :
+None
+.It installUpgrade
+Start an upgrade installation.
+.Pp
+.Sy Variables :
+None
+.It installFixitHoloShell
+Start up the "emergency holographic shell" over on VTY4
+if running as init. This will also happen automatically
+as part of the installation process unless
+.Ar noHoloShell
+is set.
+.Pp
+.Sy Variables :
+None
+.It installFixitCDROM
+Go into "fixit" mode, assuming a live file system CDROM
+currently in the drive.
+.Pp
+.Sy Variables :
+None
+.It installFixitFloppy
+Go into "fixit" mode, assuming an available fixit floppy
+disk (user will be prompted for it).
+.Pp
+.Sy Variables :
+None
+.It installFilesystems
+Do just the file system initialization part of an install.
+.Pp
+.Sy Variables :
+None
+.It installVarDefaults
+Initialize all variables to their defaults, overriding any
+previous settings.
+.Pp
+.Sy Variables :
+None
+.It loadConfig
+Sort of like an #include statement, it allows you to load one
+configuration file from another.
+.Pp
+.Sy Variables :
+.Bl -tag -width indent
+.It configFile
+The fully qualified pathname of the file to load.
+.El
+.It mediaClose
+If a media device is open, close it.
+.Pp
+.Sy Variables :
+None
+.It mediaSetCDROM
+Select a
+.Fx
+CDROM as the installation media.
+.Pp
+.Sy Variables :
+None
+.It mediaSetFloppy
+Select a pre-made floppy installation set as the installation media.
+.Pp
+.Sy Variables :
+None
+.It mediaSetDOS
+Select an existing DOS primary partition as the installation media.
+The first primary partition found is used (e.g. C:).
+.Pp
+.Sy Variables :
+None
+.It mediaSetTape
+Select a tape device as the installation media.
+.Pp
+.Sy Variables :
+None
+.It mediaSetFTP
+Select an FTP site as the installation media.
+.Pp
+.Sy Variables :
+.Bl -tag -width indent
+.It hostname
+The name of the host being installed (non-optional).
+.It domainname
+The domain name of the host being installed (optional).
+.It defaultrouter
+The default router for this host (non-optional).
+.It netDev
+Which host interface to use
+.Ar ( ed0
+or
+.Ar ep0 ,
+for example. Non-optional).
+.It netInteractive
+If set, bring up the interactive network setup form even
+if all relevant configuration variables are already set (optional).
+.It ipaddr
+The IP address for the selected host interface (non-optional).
+.It netmask
+The netmask for the selected host interface (non-optional).
+.It _ftpPath
+The fully qualified URL of the FTP site containing the
+.Fx
+distribution you're interested in, e.g.\&
+.Ar ftp://ftp.FreeBSD.org/pub/FreeBSD/ .
+.El
+.It mediaSetFTPActive
+Alias for
+.Ar mediaSetFTP
+using "active" FTP transfer mode.
+.Pp
+.Sy Variables :
+Same as for
+.Ar mediaSetFTP .
+.It mediaSetFTPPassive
+Alias for
+.Ar mediaSetFTP
+using "passive" FTP transfer mode.
+.Pp
+.Sy Variables :
+Same as for
+.Ar mediaSetFTP .
+.It mediaSetHTTP
+Alias for
+.Ar mediaSetFTP
+using an HTTP proxy.
+.Pp
+.Sy Variables :
+See
+.Ar mediaSetFTP ,
+plus
+.Bl -tag -width indent
+.It _httpPath
+The proxy to use (host:port) (non-optional).
+.El
+.It mediaSetUFS
+Select an existing UFS partition (mounted with the label editor) as
+the installation media.
+.Pp
+.Sy Variables :
+.Bl -tag -width indent
+.It ufs
+full /path to directory containing the
+.Fx
+distribution you're
+interested in.
+.El
+.It mediaSetNFS
+.Pp
+.Sy Variables :
+.Bl -tag -width indent
+.It hostname
+The name of the host being installed (non-optional).
+.It domainname
+The domain name of the host being installed (optional).
+.It defaultrouter
+The default router for this host (non-optional).
+.It netDev
+Which host interface to use
+.Ar ( ed0
+or
+.Ar ep0 ,
+for example. Non-optional).
+.It netInteractive
+If set, bring up the interactive network setup form even
+if all relevant configuration variables are already set (optional).
+.It ipaddr
+The IP address for the selected host interface (non-optional).
+.It netmask
+The netmask for the selected host interface (non-optional).
+.It nfs
+full hostname:/path specification for directory containing
+the
+.Fx
+distribution you're interested in.
+.El
+.It mediaSetFTPUserPass
+.Pp
+.Sy Variables :
+.Bl -tag -width indent
+.It ftpUser
+The username to log in as on the ftp server site.
+Default: ftp
+.It ftpPass
+The password to use for this username on the ftp
+server site.
+Default: user@host
+.El
+.It mediaSetCPIOVerbosity
+.Pp
+.Sy Variables :
+.Bl -tag -width indent
+.It cpioVerbose
+Can be used to set the verbosity of cpio extractions to low, medium or
+high.
+.El
+.It mediaGetType
+Interactively get the user to specify some type of media.
+.Pp
+.Sy Variables :
+None
+.It optionsEditor
+Invoke the interactive options editor.
+.Pp
+.Sy Variables :
+None
+.It packageAdd
+Try to fetch and add a package to the system (requires
+that a media type be set),
+.Pp
+.Sy Variables :
+.Bl -tag -width indent
+.It package
+The name of the package to add, e.g. bash-1.14.7 or ncftp-2.4.2.
+.El
+.It addGroup
+Invoke the interactive group editor.
+.Pp
+.Sy Variables :
+None
+.It addUser
+Invoke the interactive user editor.
+.Pp
+.Sy Variables :
+None
+.It shutdown
+Stop the script and terminate sysinstall.
+.Pp
+.Sy Variables :
+None
+.It system
+Execute an arbitrary command with
+.Xr system 3
+.Pp
+.Sy Variables :
+.Bl -tag -width indent
+.It command
+The name of the command to execute. When running
+from a boot floppy, very minimal expectations should
+be made as to what's available until/unless a relatively
+full system installation has just been done.
+.El
+.It tcpMenuSelect
+Configure a network device.
+.Pp
+.Sy Variables :
+Same as for
+.Ar mediaSetFTP
+except that
+.Ar _ftpPath
+is not used.
+.El
+.Sh DISTRIBUTION MEDIA
+The following files can be used to affect the operation of
+.Nm
+when used during initial system installation.
+.Bl -tag -width ".Pa packages/INDEX"
+.It Pa cdrom.inf
+A text file of properties, listed one per line, that describe the
+contents of the media in use.
+The syntax for each line is simply
+.Dq Ar property No = Ar value .
+Currently, only the following properties are recognized.
+.Bl -tag -width ".Va CD_MACHINE_ARCH"
+.It Va CD_VERSION
+This property should be set to the
+.Fx
+version on the current
+media volume.
+For example,
+.Dq Li "CD_VERSION = 4.6" .
+.It Va CD_MACHINE_ARCH
+This property should be set to the architecture of the contents on
+this volume.
+This property is normally only used with
+.Fx
+products that contain
+CDs for different architectures, to provide better error messages if
+users try to install Alpha packages on an i386 machine.
+For example,
+.Dq Li "CD_MACHINE_ARCH = alpha" .
+.It Va VOLUME
+In a multi-volume collection (such as the
+.Fx
+4-CD set), the
+.Pa ports/INDEX
+file on each disc should contain the full package index for the set.
+The last field of the
+.Pa INDEX
+file denotes which volume the package
+appears on, and the
+.Va VOLUME
+property here defines the volume ID of the current disc.
+.El
+.It Pa packages/INDEX
+The package index file.
+Each package is listed on a separate line with additional meta-data
+such as the required dependencies.
+This index is generated by
+.Dq Li "make index"
+from the
+.Xr ports 7
+collection.
+When multi-volume support is enabled, an additional field should be
+added to each line indicating which media volume contains the given
+package.
+.El
+.Pp
+For information about building a full release of
+.Fx ,
+please see
+.Xr release 7 .
+.Sh FILES
+This utility may edit the contents of
+.Pa /etc/rc.conf ,
+.Pa /etc/hosts ,
+and
+.Pa /etc/resolv.conf
+as necessary to reflect changes in the network configuration.
+.Sh SEE ALSO
+If you have a reasonably complete source tree online, take
+a look at
+.Pa /usr/src/usr.sbin/sysinstall/install.cfg
+for a sample installation script.
+.Sh BUGS
+This utility is a prototype which lasted several years past
+its expiration date and is greatly in need of death.
+.Sh AUTHORS
+.An Jordan K. Hubbard Aq jkh@FreeBSD.org
+.Sh HISTORY
+This version of
+.Nm
+first appeared in
+.Fx 2.0 .
diff --git a/usr.sbin/sade/sade.h b/usr.sbin/sade/sade.h
new file mode 100644
index 0000000..438a28e
--- /dev/null
+++ b/usr.sbin/sade/sade.h
@@ -0,0 +1,815 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated to essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#ifndef _SYSINSTALL_H_INCLUDE
+#define _SYSINSTALL_H_INCLUDE
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <setjmp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <dialog.h>
+#include "ui_objects.h"
+#include "dir.h"
+#include "colors.h"
+#include "libdisk.h"
+#include "dist.h"
+
+/*** Defines ***/
+
+#if defined(__i386__) || defined(PC98)
+#define PCCARD_ARCH 1 /* Support PCCARD installations */
+#endif
+/* 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_DESKSTYLE "_deskStyle"
+#define VAR_DISK "disk"
+#define VAR_DISKINTERACTIVE "diskInteractive"
+#define VAR_DISTS "dists"
+#define VAR_DIST_MAIN "distMain"
+#define VAR_DIST_CRYPTO "distCRYPTO"
+#define VAR_DIST_SRC "distSRC"
+#define VAR_DIST_X11 "distX11"
+#define VAR_DIST_XSERVER "distXserver"
+#define VAR_DIST_XFONTS "distXfonts"
+#define VAR_DEDICATE_DISK "dedicateDisk"
+#define VAR_DOMAINNAME "domainname"
+#define VAR_EDITOR "editor"
+#define VAR_EXTRAS "ifconfig_"
+#define VAR_COMMAND "command"
+#define VAR_CONFIG_FILE "configFile"
+#define VAR_FIXIT_TTY "fixitTty"
+#define VAR_FTP_DIR "ftpDirectory"
+#define VAR_FTP_PASS "ftpPass"
+#define VAR_FTP_PATH "_ftpPath"
+#define VAR_FTP_PORT "ftpPort"
+#define VAR_FTP_STATE "ftpState"
+#define VAR_FTP_USER "ftpUser"
+#define VAR_FTP_HOST "ftpHost"
+#define VAR_HTTP_PATH "_httpPath"
+#define VAR_HTTP_PROXY "httpProxy"
+#define VAR_HTTP_PORT "httpPort"
+#define VAR_HTTP_HOST "httpHost"
+#define VAR_HTTP_FTP_MODE "httpFtpMode"
+#define VAR_GATEWAY "defaultrouter"
+#define VAR_GEOMETRY "geometry"
+#define VAR_HOSTNAME "hostname"
+#define VAR_IFCONFIG "ifconfig_"
+#define VAR_INSTALL_CFG "installConfig"
+#define VAR_INSTALL_ROOT "installRoot"
+#define VAR_IPADDR "ipaddr"
+#define VAR_IPV6_ENABLE "ipv6_enable"
+#define VAR_IPV6ADDR "ipv6addr"
+#define VAR_KEYMAP "keymap"
+#define VAR_LABEL "label"
+#define VAR_LABEL_COUNT "labelCount"
+#define VAR_LINUX_ENABLE "linux_enable"
+#define VAR_MEDIA_TYPE "mediaType"
+#define VAR_MEDIA_TIMEOUT "MEDIA_TIMEOUT"
+#define VAR_MOUSED "moused_enable"
+#define VAR_MOUSED_FLAGS "moused_flags"
+#define VAR_MOUSED_PORT "moused_port"
+#define VAR_MOUSED_TYPE "moused_type"
+#define VAR_NAMESERVER "nameserver"
+#define VAR_NETINTERACTIVE "netInteractive"
+#define VAR_NETMASK "netmask"
+#define VAR_NETWORK_DEVICE "netDev"
+#define VAR_NEWFS_ARGS "newfsArgs"
+#define VAR_NFS_PATH "nfs"
+#define VAR_NFS_HOST "nfsHost"
+#define VAR_NFS_SECURE "nfs_reserved_port_only"
+#define VAR_NFS_SERVER "nfs_server_enable"
+#define VAR_NO_CONFIRM "noConfirm"
+#define VAR_NO_ERROR "noError"
+#define VAR_NO_HOLOSHELL "noHoloShell"
+#define VAR_NO_WARN "noWarn"
+#define VAR_NO_USR "noUsr"
+#define VAR_NO_TMP "noTmp"
+#define VAR_NO_HOME "noHome"
+#define VAR_NONINTERACTIVE "nonInteractive"
+#define VAR_NOVELL "novell"
+#define VAR_NTPDATE_FLAGS "ntpdate_flags"
+#define VAR_PACKAGE "package"
+#define VAR_PARTITION "partition"
+#define VAR_PCNFSD "pcnfsd"
+#define VAR_PKG_TMPDIR "PKG_TMPDIR"
+#define VAR_PORTS_PATH "ports"
+#define VAR_PPP_ENABLE "ppp_enable"
+#define VAR_PPP_PROFILE "ppp_profile"
+#define VAR_RELNAME "releaseName"
+#define VAR_ROOT_SIZE "rootSize"
+#define VAR_ROUTER "router"
+#define VAR_ROUTER_ENABLE "router_enable"
+#define VAR_ROUTERFLAGS "router_flags"
+#define VAR_SERIAL_SPEED "serialSpeed"
+#define VAR_SLOW_ETHER "slowEthernetCard"
+#define VAR_SWAP_SIZE "swapSize"
+#define VAR_TAPE_BLOCKSIZE "tapeBlocksize"
+#define VAR_TRY_DHCP "tryDHCP"
+#define VAR_TRY_RTSOL "tryRTSOL"
+#define VAR_UFS_PATH "ufs"
+#define VAR_USR_SIZE "usrSize"
+#define VAR_VAR_SIZE "varSize"
+#define VAR_TMP_SIZE "tmpSize"
+#define VAR_HOME_SIZE "homeSize"
+#define VAR_XF86_CONFIG "_xf86config"
+#define VAR_TERM "TERM"
+#define VAR_CONSTERM "_consterm"
+
+#define DEFAULT_TAPE_BLOCKSIZE "20"
+
+/* One MB worth of blocks */
+#define ONE_MEG 2048
+#define ONE_GIG (ONE_MEG * 1024)
+
+/* Which selection attributes to use */
+#define ATTR_SELECTED (ColorDisplay ? item_selected_attr : item_attr)
+#define ATTR_TITLE button_active_attr
+
+/* Handy strncpy() macro */
+#define SAFE_STRCPY(to, from) sstrncpy((to), (from), sizeof (to) - 1)
+
+/*** Types ***/
+typedef int Boolean;
+typedef struct disk Disk;
+typedef struct chunk Chunk;
+
+/* Bitfields for menu options */
+#define DMENU_NORMAL_TYPE 0x1 /* Normal dialog menu */
+#define DMENU_RADIO_TYPE 0x2 /* Radio dialog menu */
+#define DMENU_CHECKLIST_TYPE 0x4 /* Multiple choice menu */
+#define DMENU_SELECTION_RETURNS 0x8 /* Immediate return on item selection */
+
+typedef struct _dmenu {
+ int type; /* What sort of menu we are */
+ char *title; /* Our title */
+ char *prompt; /* Our prompt */
+ char *helpline; /* Line of help at bottom */
+ char *helpfile; /* Help file for "F1" */
+#if (__STDC_VERSION__ >= 199901L) || (__GNUC__ >= 3)
+ dialogMenuItem items[]; /* Array of menu items */
+#elif __GNUC__
+ dialogMenuItem items[0]; /* Array of menu items */
+#else
+#error "Create hack for C89 and K&R compilers."
+#endif
+} DMenu;
+
+/* An rc.conf variable */
+typedef struct _variable {
+ struct _variable *next;
+ char *name;
+ char *value;
+ int dirty;
+} Variable;
+
+#define NO_ECHO_OBJ(type) ((type) | (DITEM_NO_ECHO << 16))
+#define TYPE_OF_OBJ(type) ((type) & 0xff)
+#define ATTR_OF_OBJ(type) ((type) >> 16)
+
+/* A screen layout structure */
+typedef struct _layout {
+ int y; /* x & Y co-ordinates */
+ int x;
+ int len; /* The size of the dialog on the screen */
+ int maxlen; /* How much the user can type in ... */
+ char *prompt; /* The string for the prompt */
+ char *help; /* The display for the help line */
+ void *var; /* The var to set when this changes */
+ int type; /* The type of the dialog to create */
+ void *obj; /* The obj pointer returned by libdialog */
+} Layout;
+
+typedef enum {
+ DEVICE_TYPE_NONE,
+ DEVICE_TYPE_DISK,
+ DEVICE_TYPE_FLOPPY,
+ DEVICE_TYPE_FTP,
+ DEVICE_TYPE_NETWORK,
+ DEVICE_TYPE_CDROM,
+ DEVICE_TYPE_TAPE,
+ DEVICE_TYPE_DOS,
+ DEVICE_TYPE_UFS,
+ DEVICE_TYPE_NFS,
+ DEVICE_TYPE_ANY,
+ DEVICE_TYPE_HTTP,
+} DeviceType;
+
+/* CDROM mount codes */
+#define CD_UNMOUNTED 0
+#define CD_ALREADY_MOUNTED 1
+#define CD_WE_MOUNTED_IT 2
+
+/* A "device" from sysinstall's point of view */
+typedef struct _device {
+ char name[DEV_NAME_MAX];
+ char *description;
+ char *devname;
+ DeviceType type;
+ Boolean enabled;
+ Boolean (*init)(struct _device *dev);
+ FILE * (*get)(struct _device *dev, char *file, Boolean probe);
+ void (*shutdown)(struct _device *dev);
+ void *private;
+ unsigned int flags;
+ unsigned int volume;
+} Device;
+
+/* Some internal representations of partitions */
+typedef enum {
+ PART_NONE,
+ PART_SLICE,
+ PART_SWAP,
+ PART_FILESYSTEM,
+ PART_FAT,
+} 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];
+ int soft;
+} PartInfo;
+
+/* An option */
+typedef struct _opt {
+ char *name;
+ char *desc;
+ enum { OPT_IS_STRING, OPT_IS_INT, OPT_IS_FUNC, OPT_IS_VAR } type;
+ void *data;
+ void *aux;
+ char *(*check)();
+} Option;
+
+/* Weird index nodey things we use for keeping track of package information */
+typedef enum { PACKAGE, PLACE } node_type; /* Types of nodes */
+
+typedef struct _pkgnode { /* A node in the reconstructed hierarchy */
+ struct _pkgnode *next; /* My next sibling */
+ node_type type; /* What am I? */
+ char *name; /* My name */
+ char *desc; /* My description (Hook) */
+ struct _pkgnode *kids; /* My little children */
+ void *data; /* A place to hang my data */
+} PkgNode;
+typedef PkgNode *PkgNodePtr;
+
+/* A single package */
+typedef struct _indexEntry { /* A single entry in an INDEX file */
+ char *name; /* name */
+ char *path; /* full path to port */
+ char *prefix; /* port prefix */
+ char *comment; /* one line description */
+ char *descrfile; /* path to description file */
+ char *deps; /* packages this depends on */
+ int depc; /* how many depend on me */
+ int installed; /* indicates if it is installed */
+ char *maintainer; /* maintainer */
+ unsigned int volume; /* Volume of package */
+} IndexEntry;
+typedef IndexEntry *IndexEntryPtr;
+
+typedef int (*commandFunc)(char *key, void *data);
+
+#define HOSTNAME_FIELD_LEN 128
+#define IPADDR_FIELD_LEN 16
+#define EXTRAS_FIELD_LEN 128
+
+/* This is the structure that Network devices carry around in their private, erm, structures */
+typedef struct _devPriv {
+ int use_rtsol;
+ int use_dhcp;
+ char ipaddr[IPADDR_FIELD_LEN];
+ char netmask[IPADDR_FIELD_LEN];
+ char extras[EXTRAS_FIELD_LEN];
+} DevInfo;
+
+
+/*** Externs ***/
+extern jmp_buf BailOut; /* Used to get the heck out */
+extern int DebugFD; /* Where diagnostic output goes */
+extern Boolean Fake; /* Don't actually modify anything - testing */
+extern Boolean Restarting; /* Are we restarting sysinstall? */
+extern Boolean SystemWasInstalled; /* Did we install it? */
+extern Boolean RunningAsInit; /* Are we running stand-alone? */
+extern Boolean DialogActive; /* Is the dialog() stuff up? */
+extern Boolean ColorDisplay; /* Are we on a color display? */
+extern Boolean OnVTY; /* On a syscons VTY? */
+extern Variable *VarHead; /* The head of the variable chain */
+extern Device *mediaDevice; /* Where we're getting our distribution from */
+extern unsigned int Dists; /* Which distributions we want */
+extern unsigned int CRYPTODists; /* Which naughty distributions we want */
+extern unsigned int SrcDists; /* Which src distributions we want */
+extern unsigned int XF86Dists; /* Which XFree86 dists we want */
+extern unsigned int XF86ServerDists; /* The XFree86 servers we want */
+extern unsigned int XF86FontDists; /* The XFree86 fonts we want */
+extern int BootMgr; /* Which boot manager to use */
+extern int StatusLine; /* Where to print our status messages */
+extern DMenu MenuInitial; /* Initial installation menu */
+extern DMenu MenuFixit; /* Fixit repair menu */
+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 MenuKLD; /* Prototype KLD 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 MenuSecurity; /* System security options menu */
+extern DMenu MenuSecurityProfile; /* Security profile 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 MenuSysconsTtys; /* System console terminal type menu */
+extern DMenu MenuNetworking; /* Network configuration menu */
+extern DMenu MenuSendmail; /* Sendmail configuration menu */
+extern DMenu MenuInstallCustom; /* Custom Installation menu */
+extern DMenu MenuDistributions; /* Distribution menu */
+extern DMenu MenuDiskDevices; /* Disk type devices */
+extern DMenu MenuSubDistributions; /* Custom distribution menu */
+extern DMenu MenuSrcDistributions; /* Source distribution menu */
+extern DMenu MenuXF86; /* XFree86 main menu */
+extern DMenu MenuXF86Select; /* XFree86 distribution selection menu */
+extern DMenu MenuXF86SelectCore; /* XFree86 core distribution menu */
+extern DMenu MenuXF86SelectServer; /* XFree86 server distribution menu */
+extern DMenu MenuXF86SelectPC98Server; /* XFree86 server distribution menu */
+extern DMenu MenuXF86SelectFonts; /* XFree86 font selection menu */
+extern DMenu MenuXF86SelectFonts; /* XFree86 font selection menu */
+extern DMenu MenuXDesktops; /* Disk devices menu */
+extern DMenu MenuHTMLDoc; /* HTML Documentation menu */
+extern DMenu MenuUsermgmt; /* User management menu */
+extern DMenu MenuFixit; /* Fixit floppy/CDROM/shell menu */
+extern DMenu MenuXF86Config; /* Select XFree86 configuration type */
+extern int FixItMode; /* FixItMode starts shell onc urrent device (ie Serial port) */
+extern const char * StartName; /* Which name we were started as */
+
+/* Stuff from libdialog which isn't properly declared outside */
+extern void display_helpfile(void);
+extern void display_helpline(WINDOW *w, int y, int width);
+
+/*** Prototypes ***/
+
+/* anonFTP.c */
+extern int configAnonFTP(dialogMenuItem *self);
+
+/* cdrom.c */
+extern Boolean mediaInitCDROM(Device *dev);
+extern FILE *mediaGetCDROM(Device *dev, char *file, Boolean probe);
+extern void mediaShutdownCDROM(Device *dev);
+
+/* command.c */
+extern void command_clear(void);
+extern void command_sort(void);
+extern void command_execute(void);
+extern void command_shell_add(char *key, char *fmt, ...) __printflike(2, 3);
+extern void command_func_add(char *key, commandFunc func, void *data);
+
+/* config.c */
+extern void configEnvironmentRC_conf(void);
+extern void configEnvironmentResolv(char *config);
+extern void configRC_conf(void);
+extern int configFstab(dialogMenuItem *self);
+extern int configRC(dialogMenuItem *self);
+extern int configResolv(dialogMenuItem *self);
+extern int configPackages(dialogMenuItem *self);
+extern int configSaver(dialogMenuItem *self);
+extern int configSaverTimeout(dialogMenuItem *self);
+extern int configLinux(dialogMenuItem *self);
+extern int configNTP(dialogMenuItem *self);
+extern int configUsers(dialogMenuItem *self);
+extern int configXSetup(dialogMenuItem *self);
+extern int configXDesktop(dialogMenuItem *self);
+extern int configRouter(dialogMenuItem *self);
+extern int configPCNFSD(dialogMenuItem *self);
+extern int configInetd(dialogMenuItem *self);
+extern int configNFSServer(dialogMenuItem *self);
+extern int configWriteRC_conf(dialogMenuItem *self);
+extern int configSecurityProfile(dialogMenuItem *self);
+extern int configSecurityExtreme(dialogMenuItem *self);
+extern int configSecurityModerate(dialogMenuItem *self);
+extern int configEtcTtys(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);
+
+/* dhcp.c */
+extern int dhcpParseLeases(char *file, char *hostname, char *domain, char *nameserver,
+ char *ipaddr, char *gateway, char *netmask);
+
+/* 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 distUnsetCustom(dialogMenuItem *self);
+extern int distSetDeveloper(dialogMenuItem *self);
+extern int distSetXDeveloper(dialogMenuItem *self);
+extern int distSetKernDeveloper(dialogMenuItem *self);
+extern int distSetXKernDeveloper(dialogMenuItem *self);
+extern int distSetUser(dialogMenuItem *self);
+extern int distSetXUser(dialogMenuItem *self);
+extern int distSetMinimum(dialogMenuItem *self);
+extern int distSetEverything(dialogMenuItem *self);
+extern int distSetSrc(dialogMenuItem *self);
+extern int distSetXF86(dialogMenuItem *self);
+extern int distExtractAll(dialogMenuItem *self);
+
+/* dmenu.c */
+extern int dmenuDisplayFile(dialogMenuItem *tmp);
+extern int dmenuSubmenu(dialogMenuItem *tmp);
+extern int dmenuSystemCommand(dialogMenuItem *tmp);
+extern int dmenuSystemCommandBox(dialogMenuItem *tmp);
+extern int dmenuExit(dialogMenuItem *tmp);
+extern int dmenuISetVariable(dialogMenuItem *tmp);
+extern int dmenuSetVariable(dialogMenuItem *tmp);
+extern int dmenuSetKmapVariable(dialogMenuItem *tmp);
+extern int dmenuSetVariables(dialogMenuItem *tmp);
+extern int dmenuToggleVariable(dialogMenuItem *tmp);
+extern int dmenuSetFlag(dialogMenuItem *tmp);
+extern int dmenuSetValue(dialogMenuItem *tmp);
+extern Boolean dmenuOpen(DMenu *menu, int *choice, int *scroll, int *curr, int *max, Boolean buttons);
+extern Boolean dmenuOpenSimple(DMenu *menu, Boolean buttons);
+extern int dmenuVarCheck(dialogMenuItem *item);
+extern int dmenuVarsCheck(dialogMenuItem *item);
+extern int dmenuFlagCheck(dialogMenuItem *item);
+extern int dmenuRadioCheck(dialogMenuItem *item);
+
+/* doc.c */
+extern int docBrowser(dialogMenuItem *self);
+extern int docShowDocument(dialogMenuItem *self);
+
+/* dos.c */
+extern Boolean mediaCloseDOS(Device *dev, FILE *fp);
+extern Boolean mediaInitDOS(Device *dev);
+extern FILE *mediaGetDOS(Device *dev, char *file, Boolean probe);
+extern void mediaShutdownDOS(Device *dev);
+
+/* floppy.c */
+extern int getRootFloppy(void);
+extern Boolean mediaInitFloppy(Device *dev);
+extern FILE *mediaGetFloppy(Device *dev, char *file, Boolean probe);
+extern void mediaShutdownFloppy(Device *dev);
+
+/* ftp_strat.c */
+extern Boolean mediaCloseFTP(Device *dev, FILE *fp);
+extern Boolean mediaInitFTP(Device *dev);
+extern FILE *mediaGetFTP(Device *dev, char *file, Boolean probe);
+extern void mediaShutdownFTP(Device *dev);
+
+/* http.c */
+extern Boolean mediaInitHTTP(Device *dev);
+extern FILE *mediaGetHTTP(Device *dev, char *file, Boolean probe);
+
+/* globals.c */
+extern void globalsInit(void);
+
+/* index.c */
+int index_read(FILE *fp, PkgNodePtr papa);
+int index_menu(PkgNodePtr root, PkgNodePtr top, PkgNodePtr plist, int *pos, int *scroll);
+void index_init(PkgNodePtr top, PkgNodePtr plist);
+void index_node_free(PkgNodePtr top, PkgNodePtr plist);
+void index_sort(PkgNodePtr top);
+void index_print(PkgNodePtr top, int level);
+int index_extract(Device *dev, PkgNodePtr top, PkgNodePtr who, Boolean depended);
+int index_initialize(char *path);
+PkgNodePtr index_search(PkgNodePtr top, char *str, PkgNodePtr *tp);
+
+/* install.c */
+extern Boolean checkLabels(Boolean whinge, Chunk **rdev, Chunk **sdev, Chunk **udev, Chunk **vdev, Chunk **vtdev, Chunk **hdev);
+extern int installCommit(dialogMenuItem *self);
+extern int installCustomCommit(dialogMenuItem *self);
+extern int installExpress(dialogMenuItem *self);
+extern int installStandard(dialogMenuItem *self);
+extern int installFixitHoloShell(dialogMenuItem *self);
+extern int installFixitCDROM(dialogMenuItem *self);
+extern int installFixitFloppy(dialogMenuItem *self);
+extern int 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 int installX11package(dialogMenuItem *self);
+extern Boolean copySelf(void);
+
+/* kget.c */
+extern int kget(char *out);
+
+/* keymap.c */
+extern int loadKeymap(const char *lang);
+
+/* label.c */
+extern int diskLabelEditor(dialogMenuItem *self);
+extern int diskLabelCommit(dialogMenuItem *self);
+
+/* makedevs.c (auto-generated) */
+extern const char termcap_ansi[];
+extern const char termcap_vt100[];
+extern const char termcap_cons25w[];
+extern const char termcap_cons25[];
+extern const char termcap_cons25_m[];
+extern const char termcap_cons25r[];
+extern const char termcap_cons25r_m[];
+extern const char termcap_cons25l1[];
+extern const char termcap_cons25l1_m[];
+extern const char termcap_xterm[];
+extern const u_char font_iso_8x16[];
+extern const u_char font_cp850_8x16[];
+extern const u_char font_cp866_8x16[];
+extern const u_char koi8_r2cp866[];
+extern u_char default_scrnmap[];
+
+/* media.c */
+extern char *cpioVerbosity(void);
+extern void mediaClose(void);
+extern int mediaTimeout(void);
+extern int mediaSetCDROM(dialogMenuItem *self);
+extern int mediaSetFloppy(dialogMenuItem *self);
+extern int mediaSetDOS(dialogMenuItem *self);
+extern int mediaSetTape(dialogMenuItem *self);
+extern int mediaSetFTP(dialogMenuItem *self);
+extern int mediaSetFTPActive(dialogMenuItem *self);
+extern int mediaSetFTPPassive(dialogMenuItem *self);
+extern int mediaSetHTTP(dialogMenuItem *self);
+extern int mediaSetUFS(dialogMenuItem *self);
+extern int mediaSetNFS(dialogMenuItem *self);
+extern int mediaSetFTPUserPass(dialogMenuItem *self);
+extern int mediaSetCPIOVerbosity(dialogMenuItem *self);
+extern int mediaGetType(dialogMenuItem *self);
+extern Boolean mediaExtractDist(char *dir, char *dist, FILE *fp);
+extern Boolean mediaExtractDistBegin(char *dir, int *fd, int *zpid, int *cpic);
+extern Boolean mediaExtractDistEnd(int zpid, int cpid);
+extern Boolean mediaVerify(void);
+extern FILE *mediaGenericGet(char *base, const char *file);
+
+/* misc.c */
+extern Boolean file_readable(char *fname);
+extern Boolean file_executable(char *fname);
+extern Boolean directory_exists(const char *dirname);
+extern char *root_bias(char *path);
+extern char *itoa(int value);
+extern char *string_concat(char *p1, char *p2);
+extern char *string_concat3(char *p1, char *p2, char *p3);
+extern char *string_prune(char *str);
+extern char *string_skipwhite(char *str);
+extern char *string_copy(char *s1, char *s2);
+extern char *pathBaseName(const char *path);
+extern void safe_free(void *ptr);
+extern void *safe_malloc(size_t size);
+extern void *safe_realloc(void *orig, size_t size);
+extern dialogMenuItem *item_add(dialogMenuItem *list, char *prompt, char *title,
+ int (*checked)(dialogMenuItem *self),
+ int (*fire)(dialogMenuItem *self),
+ void (*selected)(dialogMenuItem *self, int is_selected),
+ void *data, int *aux, int *curr, int *max);
+extern void items_free(dialogMenuItem *list, int *curr, int *max);
+extern int Mkdir(char *);
+extern int 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);
+
+/* modules.c */
+extern void moduleInitialize(void);
+extern int kldBrowser(dialogMenuItem *self);
+
+/* mouse.c */
+extern int mousedTest(dialogMenuItem *self);
+extern int mousedDisable(dialogMenuItem *self);
+extern int setMouseFlags(dialogMenuItem *self);
+
+/* msg.c */
+extern Boolean isDebug(void);
+extern void msgInfo(char *fmt, ...) __printf0like(1, 2);
+extern void msgYap(char *fmt, ...) __printflike(1, 2);
+extern void msgWarn(char *fmt, ...) __printflike(1, 2);
+extern void msgDebug(char *fmt, ...) __printflike(1, 2);
+extern void msgError(char *fmt, ...) __printflike(1, 2);
+extern void msgFatal(char *fmt, ...) __printflike(1, 2);
+extern void msgConfirm(char *fmt, ...) __printflike(1, 2);
+extern void msgNotify(char *fmt, ...) __printflike(1, 2);
+extern void msgWeHaveOutput(char *fmt, ...) __printflike(1, 2);
+extern int msgYesNo(char *fmt, ...) __printflike(1, 2);
+extern int msgNoYes(char *fmt, ...) __printflike(1, 2);
+extern char *msgGetInput(char *buf, char *fmt, ...) __printflike(2, 3);
+extern int msgSimpleConfirm(char *);
+extern int msgSimpleNotify(char *);
+
+/* network.c */
+extern Boolean mediaInitNetwork(Device *dev);
+extern void mediaShutdownNetwork(Device *dev);
+
+/* nfs.c */
+extern Boolean mediaInitNFS(Device *dev);
+extern FILE *mediaGetNFS(Device *dev, char *file, Boolean probe);
+extern void mediaShutdownNFS(Device *dev);
+
+/* options.c */
+extern int optionsEditor(dialogMenuItem *self);
+
+/* package.c */
+extern int packageAdd(dialogMenuItem *self);
+extern int package_add(char *name);
+extern int package_extract(Device *dev, char *name, Boolean depended);
+extern Boolean package_exists(char *name);
+
+/* pccard.c */
+extern void pccardInitialize(void);
+
+/* system.c */
+extern void systemInitialize(int argc, char **argv);
+extern void systemShutdown(int status);
+extern int execExecute(char *cmd, char *name);
+extern int systemExecute(char *cmd);
+extern void systemSuspendDialog(void);
+extern void systemResumeDialog(void);
+extern int systemDisplayHelp(char *file);
+extern char *systemHelpFile(char *file, char *buf);
+extern void systemChangeFont(const u_char font[]);
+extern void systemChangeLang(char *lang);
+extern void systemChangeTerminal(char *color, const u_char c_termcap[], char *mono, const u_char m_termcap[]);
+extern void systemChangeScreenmap(const u_char newmap[]);
+extern void systemCreateHoloshell(void);
+extern int vsystem(char *fmt, ...) __printflike(1, 2);
+
+/* tape.c */
+extern char *mediaTapeBlocksize(void);
+extern Boolean mediaInitTape(Device *dev);
+extern FILE *mediaGetTape(Device *dev, char *file, Boolean probe);
+extern void mediaShutdownTape(Device *dev);
+
+/* tcpip.c */
+extern int tcpOpenDialog(Device *dev);
+extern int tcpMenuSelect(dialogMenuItem *self);
+extern Device *tcpDeviceSelect(void);
+
+/* termcap.c */
+extern int set_termcap(void);
+
+/* ttys.c */
+extern void configTtys(void);
+
+/* ufs.c */
+extern void mediaShutdownUFS(Device *dev);
+extern Boolean mediaInitUFS(Device *dev);
+extern FILE *mediaGetUFS(Device *dev, char *file, Boolean probe);
+
+/* usb.c */
+extern void usbInitialize(void);
+
+/* user.c */
+extern int userAddGroup(dialogMenuItem *self);
+extern int userAddUser(dialogMenuItem *self);
+
+/* variable.c */
+extern void variable_set(char *var, int dirty);
+extern void variable_set2(char *name, char *value, int dirty);
+extern char *variable_get(char *var);
+extern int variable_cmp(char *var, char *value);
+extern void variable_unset(char *var);
+extern char *variable_get_value(char *var, char *prompt, int dirty);
+extern int variable_check(char *data);
+extern int dump_variables(dialogMenuItem *self);
+extern void free_variables(void);
+extern void pvariable_set(char *var);
+extern char *pvariable_get(char *var);
+
+/* wizard.c */
+extern void slice_wizard(Disk *d);
+
+/*
+ * Macros. Please find a better place for us!
+ */
+#define DEVICE_INIT(d) ((d) != NULL ? (d)->init((d)) : NULL)
+#define DEVICE_GET(d, b, f) ((d) != NULL ? (d)->get((d), (b), (f)) : NULL)
+#define DEVICE_SHUTDOWN(d) ((d) != NULL ? (d)->shutdown((d)) : (void)0)
+
+#ifdef USE_GZIP
+#define UNZIPPER "gunzip"
+#else
+#define UNZIPPER "bunzip2"
+#endif
+
+#endif
+/* _SYSINSTALL_H_INCLUDE */
diff --git a/usr.sbin/sade/system.c b/usr.sbin/sade/system.c
new file mode 100644
index 0000000..2ad3a48
--- /dev/null
+++ b/usr.sbin/sade/system.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.
+ *
+ * $FreeBSD$
+ *
+ * Jordan Hubbard
+ *
+ * My contributions are in the public domain.
+ *
+ * Parts of this file are also blatently stolen from Poul-Henning Kamp's
+ * previous version of sysinstall, and as such fall under his "BEERWARE license"
+ * so buy him a beer if you like it! Buy him a beer for me, too!
+ * Heck, get him completely drunk and send me pictures! :-)
+ */
+
+#include "sysinstall.h"
+#include <signal.h>
+#include <termios.h>
+#include <sys/reboot.h>
+#include <sys/consio.h>
+#include <sys/fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
+
+
+/* Where we stick our temporary expanded doc file */
+#define DOC_TMP_DIR "/tmp/.doc"
+#define DOC_TMP_FILE "/tmp/.doc/doc.tmp"
+
+static pid_t ehs_pid;
+
+/*
+ * Handle interrupt signals - this probably won't work in all cases
+ * due to our having bogotified the internal state of dialog or curses,
+ * but we'll give it a try.
+ */
+static int
+intr_continue(dialogMenuItem *self)
+{
+ return DITEM_LEAVE_MENU;
+}
+
+static int
+intr_reboot(dialogMenuItem *self)
+{
+ systemShutdown(-1);
+ /* NOTREACHED */
+ return 0;
+}
+
+static int
+intr_restart(dialogMenuItem *self)
+{
+ int ret, fd, fdmax;
+
+ mediaClose();
+ free_variables();
+ fdmax = getdtablesize();
+ for (fd = 3; fd < fdmax; fd++)
+ close(fd);
+ ret = execl(StartName, StartName, "-restart", (char *)NULL);
+ msgDebug("execl failed (%s)\n", strerror(errno));
+ /* NOTREACHED */
+ return -1;
+}
+
+static dialogMenuItem intrmenu[] = {
+ { "Abort", "Abort the installation", NULL, intr_reboot },
+ { "Restart", "Restart the installation program", NULL, intr_restart },
+ { "Continue", "Continue the installation", NULL, intr_continue },
+};
+
+
+static void
+handle_intr(int sig)
+{
+ WINDOW *save = savescr();
+
+ use_helpline(NULL);
+ use_helpfile(NULL);
+ if (OnVTY) {
+ ioctl(0, VT_ACTIVATE, 1); /* Switch back */
+ msgInfo(NULL);
+ }
+ (void)dialog_menu("Installation interrupt",
+ "Do you want to abort the installation?",
+ -1, -1, 3, -3, intrmenu, NULL, NULL, NULL);
+ restorescr(save);
+}
+
+/*
+ * Harvest children if we are init.
+ */
+static void
+reap_children(int sig)
+{
+ int errbak = errno;
+
+ while (waitpid(-1, NULL, WNOHANG) > 0)
+ ;
+ errno = errbak;
+}
+
+/* Expand a file into a convenient location, nuking it each time */
+static char *
+expand(char *fname)
+{
+ char *gunzip = RunningAsInit ? "/stand/gunzip" : "/usr/bin/gunzip";
+
+ if (!directory_exists(DOC_TMP_DIR)) {
+ Mkdir(DOC_TMP_DIR);
+ if (chown(DOC_TMP_DIR, 0, 0) < 0)
+ return NULL;
+ if (chmod(DOC_TMP_DIR, S_IRWXU) < 0)
+ return NULL;
+ }
+ else
+ unlink(DOC_TMP_FILE);
+ if (!file_readable(fname) || vsystem("%s < %s > %s", gunzip, fname, DOC_TMP_FILE))
+ return NULL;
+ return DOC_TMP_FILE;
+}
+
+/* Initialize system defaults */
+void
+systemInitialize(int argc, char **argv)
+{
+ size_t i;
+ int boothowto;
+ sigset_t signalset;
+
+ signal(SIGINT, SIG_IGN);
+ globalsInit();
+
+ i = sizeof(boothowto);
+ if (!sysctlbyname("debug.boothowto", &boothowto, &i, NULL, NULL) &&
+ (i == sizeof(boothowto)) && (boothowto & RB_VERBOSE))
+ variable_set2(VAR_DEBUG, "YES", 0);
+
+ /* Are we running as init? */
+ if (getpid() == 1) {
+ int fd;
+
+ RunningAsInit = 1;
+ setsid();
+ close(0);
+ fd = open("/dev/ttyv0", O_RDWR);
+ if (fd == -1) {
+ fd = open("/dev/console", O_RDWR); /* fallback */
+ variable_set2(VAR_FIXIT_TTY, "serial", 0); /* give fixit a hint */
+ } else
+ OnVTY = TRUE;
+ /*
+ * To make _sure_ we're on a VTY and don't have /dev/console switched
+ * away to a serial port or something, attempt to set the cursor appearance.
+ */
+ if (OnVTY) {
+ int fd2, type;
+
+ type = 0; /* normal */
+ if ((fd2 = open("/dev/console", O_RDWR)) != -1) {
+ if (ioctl(fd2, CONS_CURSORTYPE, &type) == -1) {
+ OnVTY = FALSE;
+ variable_set2(VAR_FIXIT_TTY, "serial", 0); /* Tell Fixit
+ the console
+ type */
+ close(fd); close(fd2);
+ open("/dev/console", O_RDWR);
+ }
+ else
+ close(fd2);
+ }
+ }
+ close(1); dup(0);
+ close(2); dup(0);
+ printf("%s running as init on %s\n", argv[0], OnVTY ? "vty0" : "serial console");
+ ioctl(0, TIOCSCTTY, (char *)NULL);
+ setlogin("root");
+ setenv("PATH", "/stand:/bin:/sbin:/usr/sbin:/usr/bin:/mnt/bin:/mnt/sbin:/mnt/usr/sbin:/mnt/usr/bin:/usr/X11R6/bin", 1);
+ setbuf(stdin, 0);
+ setbuf(stderr, 0);
+#ifdef __alpha__
+ i = 0;
+ sysctlbyname("machdep.unaligned_print", NULL, 0, &i, sizeof(i));
+#endif
+#if 0
+ signal(SIGCHLD, reap_children);
+#endif
+ }
+ else {
+ char hname[256];
+
+ /* Initalize various things for a multi-user environment */
+ if (!gethostname(hname, sizeof hname))
+ variable_set2(VAR_HOSTNAME, hname, 0);
+ }
+
+ if (set_termcap() == -1) {
+ printf("Can't find terminal entry\n");
+ exit(-1);
+ }
+
+ /* XXX - libdialog has particularly bad return value checking */
+ init_dialog();
+
+ /* If we haven't crashed I guess dialog is running ! */
+ DialogActive = TRUE;
+
+ /* Make sure HOME is set for those utilities that need it */
+ if (!getenv("HOME"))
+ setenv("HOME", "/", 1);
+ signal(SIGINT, handle_intr);
+ /*
+ * Make sure we can be interrupted even if we were re-executed
+ * from an interrupt.
+ */
+ sigemptyset(&signalset);
+ sigaddset(&signalset, SIGINT);
+ sigprocmask(SIG_UNBLOCK, &signalset, NULL);
+
+ (void)vsystem("rm -rf %s", DOC_TMP_DIR);
+}
+
+/* Close down and prepare to exit */
+void
+systemShutdown(int status)
+{
+ /* If some media is open, close it down */
+ if (status >=0)
+ mediaClose();
+
+ /* write out any changes to rc.conf .. */
+ configRC_conf();
+
+ /* Shut down the dialog library */
+ if (DialogActive) {
+ end_dialog();
+ DialogActive = FALSE;
+ }
+
+ /* Shut down curses */
+ endwin();
+
+ /* If we have a temporary doc dir lying around, nuke it */
+ (void)vsystem("rm -rf %s", DOC_TMP_DIR);
+
+ /* REALLY exit! */
+ if (RunningAsInit) {
+ /* Put the console back */
+ ioctl(0, VT_ACTIVATE, 2);
+#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;
+ WINDOW *w = savescr();
+
+ dialog_clear();
+ dialog_update();
+ end_dialog();
+ DialogActive = FALSE;
+ if (tcgetattr(0, &foo) != -1) {
+ foo.c_cc[VERASE] = '\010';
+ tcsetattr(0, TCSANOW, &foo);
+ }
+ if (!Fake)
+ status = system(command);
+ else {
+ status = 0;
+ msgDebug("systemExecute: Faked execution of `%s'\n", command);
+ }
+ DialogActive = TRUE;
+ restorescr(w);
+ return status;
+}
+
+/* suspend/resume libdialog/curses screen */
+static WINDOW *oldW;
+
+void
+systemSuspendDialog(void)
+{
+
+ oldW = savescr();
+ dialog_clear();
+ dialog_update();
+ end_dialog();
+ DialogActive = FALSE;
+}
+
+void
+systemResumeDialog(void)
+{
+
+ DialogActive = TRUE;
+ restorescr(oldW);
+}
+
+/* Display a help file in a filebox */
+int
+systemDisplayHelp(char *file)
+{
+ char *fname = NULL;
+ char buf[FILENAME_MAX];
+ int ret = 0;
+ WINDOW *w = savescr();
+
+ fname = systemHelpFile(file, buf);
+ if (!fname) {
+ snprintf(buf, FILENAME_MAX, "The %s file is not provided on this particular floppy image.", file);
+ use_helpfile(NULL);
+ use_helpline(NULL);
+ dialog_mesgbox("Sorry!", buf, -1, -1);
+ ret = 1;
+ }
+ else {
+ use_helpfile(NULL);
+ use_helpline(NULL);
+ dialog_textbox(file, fname, LINES, COLS);
+ }
+ restorescr(w);
+ return ret;
+}
+
+char *
+systemHelpFile(char *file, char *buf)
+{
+ if (!file)
+ return NULL;
+ if (file[0] == '/')
+ return file;
+ snprintf(buf, FILENAME_MAX, "/stand/help/%s.hlp.gz", file);
+ if (file_readable(buf))
+ return expand(buf);
+ snprintf(buf, FILENAME_MAX, "/stand/help/%s.TXT.gz", file);
+ if (file_readable(buf))
+ return expand(buf);
+ snprintf(buf, FILENAME_MAX, "/usr/src/usr.sbin/sysinstall/help/%s.hlp", file);
+ if (file_readable(buf))
+ return buf;
+ snprintf(buf, FILENAME_MAX, "/usr/src/usr.sbin/sysinstall/help/%s.TXT", file);
+ if (file_readable(buf))
+ return buf;
+ return NULL;
+}
+
+void
+systemChangeTerminal(char *color, const u_char c_term[],
+ char *mono, const u_char m_term[])
+{
+ if (OnVTY) {
+ int setupterm(char *color, int, int *);
+
+ if (ColorDisplay) {
+ setenv("TERM", color, 1);
+ setenv("TERMCAP", c_term, 1);
+ reset_shell_mode();
+ setterm(color);
+ cbreak(); noecho();
+ }
+ else {
+ setenv("TERM", mono, 1);
+ setenv("TERMCAP", m_term, 1);
+ reset_shell_mode();
+ setterm(mono);
+ cbreak(); noecho();
+ }
+ }
+ clear();
+ refresh();
+ dialog_clear();
+}
+
+int
+vsystem(char *fmt, ...)
+{
+ va_list args;
+ int pstat;
+ pid_t pid;
+ int omask;
+ sig_t intsave, quitsave;
+ char *cmd;
+ int i;
+
+ cmd = (char *)alloca(FILENAME_MAX);
+ cmd[0] = '\0';
+ va_start(args, fmt);
+ vsnprintf(cmd, FILENAME_MAX, fmt, args);
+ va_end(args);
+
+ omask = sigblock(sigmask(SIGCHLD));
+ if (Fake) {
+ msgDebug("vsystem: Faked execution of `%s'\n", cmd);
+ return 0;
+ }
+ if (isDebug())
+ msgDebug("Executing command `%s'\n", cmd);
+ pid = fork();
+ if (pid == -1) {
+ (void)sigsetmask(omask);
+ i = 127;
+ }
+ else if (!pid) { /* Junior */
+ (void)sigsetmask(omask);
+ if (DebugFD != -1) {
+ dup2(DebugFD, 0);
+ dup2(DebugFD, 1);
+ dup2(DebugFD, 2);
+ }
+ else {
+ close(1); open("/dev/null", O_WRONLY);
+ dup2(1, 2);
+ }
+ if (!RunningAsInit)
+ execl("/bin/sh", "/bin/sh", "-c", cmd, (char *)NULL);
+ else
+ execl("/stand/sh", "/stand/sh", "-c", cmd, (char *)NULL);
+ exit(1);
+ }
+ else {
+ intsave = signal(SIGINT, SIG_IGN);
+ quitsave = signal(SIGQUIT, SIG_IGN);
+ pid = waitpid(pid, &pstat, 0);
+ (void)sigsetmask(omask);
+ (void)signal(SIGINT, intsave);
+ (void)signal(SIGQUIT, quitsave);
+ i = (pid == -1) ? -1 : WEXITSTATUS(pstat);
+ if (isDebug())
+ msgDebug("Command `%s' returns status of %d\n", cmd, i);
+ }
+ return i;
+}
+
+void
+systemCreateHoloshell(void)
+{
+ int waitstatus;
+
+ if ((FixItMode || OnVTY) && RunningAsInit) {
+
+ if (ehs_pid != 0) {
+ int pstat;
+
+ if (kill(ehs_pid, 0) == 0) {
+
+ if (msgNoYes("There seems to be an emergency holographic shell\n"
+ "already running on VTY 4.\n\n"
+ "Kill it and start a new one?"))
+ return;
+
+ /* try cleaning up as much as possible */
+ (void) kill(ehs_pid, SIGHUP);
+ sleep(1);
+ (void) kill(ehs_pid, SIGKILL);
+ }
+
+ /* avoid too many zombies */
+ (void) waitpid(ehs_pid, &pstat, WNOHANG);
+ }
+
+ if (strcmp(variable_get(VAR_FIXIT_TTY), "serial") == 0)
+ systemSuspendDialog(); /* must be before the fork() */
+ if ((ehs_pid = fork()) == 0) {
+ int i, fd;
+ struct termios foo;
+ extern int login_tty(int);
+
+ ioctl(0, TIOCNOTTY, NULL);
+ for (i = getdtablesize(); i >= 0; --i)
+ close(i);
+ if (strcmp(variable_get(VAR_FIXIT_TTY), "serial") == 0)
+ fd = open("/dev/console", O_RDWR);
+ else
+ fd = open("/dev/ttyv3", O_RDWR);
+ ioctl(0, TIOCSCTTY, &fd);
+ dup2(0, 1);
+ dup2(0, 2);
+ DebugFD = 2;
+ if (login_tty(fd) == -1)
+ msgDebug("Doctor: I can't set the controlling terminal.\n");
+ signal(SIGTTOU, SIG_IGN);
+ if (tcgetattr(fd, &foo) != -1) {
+ foo.c_cc[VERASE] = '\010';
+ if (tcsetattr(fd, TCSANOW, &foo) == -1)
+ msgDebug("Doctor: I'm unable to set the erase character.\n");
+ }
+ else
+ msgDebug("Doctor: I'm unable to get the terminal attributes!\n");
+ if (strcmp(variable_get(VAR_FIXIT_TTY), "serial") == 0) {
+ printf("Type ``exit'' in this fixit shell to resume sysinstall.\n\n");
+ fflush(stdout);
+ }
+ execlp("sh", "-sh", 0);
+ msgDebug("Was unable to execute sh for Holographic shell!\n");
+ exit(1);
+ }
+ else {
+ if (strcmp(variable_get(VAR_FIXIT_TTY), "standard") == 0) {
+ WINDOW *w = savescr();
+
+ msgNotify("Starting an emergency holographic shell on VTY4");
+ sleep(2);
+ restorescr(w);
+ }
+ else {
+ (void)waitpid(ehs_pid, &waitstatus, 0); /* we only wait for
+ shell to finish
+ it serial mode
+ since there is no
+ virtual console */
+ systemResumeDialog();
+ }
+ }
+ }
+}
diff --git a/usr.sbin/sade/termcap.c b/usr.sbin/sade/termcap.c
new file mode 100644
index 0000000..1d8e047
--- /dev/null
+++ b/usr.sbin/sade/termcap.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 1994, Paul Richards.
+ *
+ * All rights reserved.
+ *
+ * This software may be used, modified, copied, distributed, and sold, in both
+ * source and binary form provided that the above copyright and these terms
+ * are retained, verbatim, as the first lines of this file. Under no
+ * circumstances is the author responsible for the proper functioning of this
+ * software, nor does the author assume any responsibility for damages
+ * incurred with its use.
+ *
+ * $FreeBSD$
+ */
+
+#include "sysinstall.h"
+#include <stdarg.h>
+#include <fcntl.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/consio.h>
+
+#define VTY_STATUS_LINE 24
+#define TTY_STATUS_LINE 23
+
+static void
+prompt_term(char **termp, char **termcapp)
+{
+ char str[80];
+ static struct {
+ const char *term, *termcap;
+ } lookup[] = { { "ansi", termcap_ansi },
+ { "vt100", termcap_vt100 },
+ { "cons25", termcap_cons25 },
+ { "cons25-m", termcap_cons25_m },
+ { "xterm", termcap_xterm },
+ { "cons25w", termcap_cons25w } }; /* must be last */
+
+ if (RunningAsInit) {
+ while (1) {
+ int i;
+
+ printf("\nThese are the predefined terminal types available to\n");
+ printf("sysinstall when running stand-alone. Please choose the\n");
+ printf("closest match for your particular terminal.\n\n");
+ printf("1 ...................... Standard ANSI terminal.\n");
+ printf("2 ...................... VT100 or compatible terminal.\n");
+ printf("3 ...................... FreeBSD system console (color).\n");
+ printf("4 ...................... FreeBSD system console (monochrome).\n\n");
+ printf("5 ...................... xterm terminal emulator.\n\n");
+ printf("Your choice: (1-5) ");
+ fflush(stdout);
+ fgets(str, 80, stdin);
+ i = str[0] - '0';
+ if (i > 0 && i < 6) {
+ *termp = (char *)lookup[i - 1].term;
+ *termcapp = (char *)lookup[i - 1].termcap;
+ break;
+ }
+ else
+ printf("\007Invalid choice, please try again.\n\n");
+ }
+ }
+ else {
+ printf("\nPlease set your TERM variable before running this program.\n");
+ printf("Defaulting to an ANSI compatible terminal - please press RETURN\n");
+ fgets(str, 80, stdin); /* Just to make it interactive */
+ *termp = (char *)"ansi";
+ *termcapp = (char *)termcap_ansi;
+ }
+}
+
+int
+set_termcap(void)
+{
+ char *term;
+ int stat;
+ struct ttysize ts;
+
+ term = getenv("TERM");
+ stat = ioctl(STDERR_FILENO, GIO_COLOR, &ColorDisplay);
+
+ if (!RunningAsInit) {
+ if (isDebug())
+ DebugFD = open("sysinstall.debug", O_WRONLY|O_CREAT|O_TRUNC, 0644);
+ else
+ DebugFD = -1;
+ if (DebugFD < 0)
+ DebugFD = open("/dev/null", O_RDWR, 0);
+ }
+
+ if (!OnVTY || (stat < 0)) {
+ if (!term) {
+ char *term, *termcap;
+
+ prompt_term(&term, &termcap);
+ if (setenv("TERM", term, 1) < 0)
+ return -1;
+ if (setenv("TERMCAP", termcap, 1) < 0)
+ return -1;
+ }
+ if (DebugFD < 0)
+ DebugFD = open("/dev/null", O_RDWR, 0);
+ }
+ else {
+ int i, on;
+
+ if (getpid() == 1) {
+ DebugFD = open("/dev/ttyv1", O_WRONLY);
+ if (DebugFD != -1) {
+ on = 1;
+ i = ioctl(DebugFD, TIOCCONS, (char *)&on);
+ msgDebug("ioctl(%d, TIOCCONS, NULL) = %d (%s)\n",
+ DebugFD, i, !i ? "success" : strerror(errno));
+ }
+ }
+
+#ifdef PC98
+ if (!term) {
+ if (setenv("TERM", "cons25w", 1) < 0)
+ return -1;
+ if (setenv("TERMCAP", termcap_cons25w, 1) < 0)
+ return -1;
+ }
+#else
+ if (ColorDisplay) {
+ if (!term) {
+ if (setenv("TERM", "cons25", 1) < 0)
+ return -1;
+ if (setenv("TERMCAP", termcap_cons25, 1) < 0)
+ return -1;
+ }
+ }
+ else {
+ if (!term) {
+ if (setenv("TERM", "cons25-m", 1) < 0)
+ return -1;
+ if (setenv("TERMCAP", termcap_cons25_m, 1) < 0)
+ return -1;
+ }
+ }
+#endif
+ }
+ if (ioctl(0, TIOCGSIZE, &ts) == -1) {
+ msgDebug("Unable to get terminal size - errno %d\n", errno);
+ ts.ts_lines = 0;
+ }
+ StatusLine = ts.ts_lines ? ts.ts_lines - 1: (OnVTY ? VTY_STATUS_LINE : TTY_STATUS_LINE);
+ return 0;
+}
diff --git a/usr.sbin/sade/usb.c b/usr.sbin/sade/usb.c
new file mode 100644
index 0000000..4eedbd5
--- /dev/null
+++ b/usr.sbin/sade/usb.c
@@ -0,0 +1,44 @@
+/*
+ * USB support for sysinstall
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 2000 John Baldwin <jhb@FreeBSD.org>. All rights reserved.
+ *
+ * This software may be used, modified, copied, and distributed, in
+ * both source and binary form provided that the above copyright and
+ * these terms are retained. Under no circumstances is the author
+ * responsible for the proper functioning of this software, nor does
+ * the author assume any responsibility for damages incurred with its
+ * use.
+ */
+
+#include "sysinstall.h"
+#include <sys/fcntl.h>
+#include <sys/time.h>
+
+void
+usbInitialize(void)
+{
+ int fd;
+ WINDOW *w;
+
+ if (!RunningAsInit && !Fake) {
+ /* It's not my job... */
+ return;
+ }
+
+ if ((fd = open("/dev/usb", O_RDONLY)) < 0) {
+ msgDebug("Can't open USB controller.\n");
+ return;
+ }
+ close(fd);
+
+ w = savescr();
+ msgNotify("Initializing USB controller....");
+
+ variable_set2("usbd_enable", "YES", 1);
+
+ vsystem("/stand/usbd");
+ restorescr(w);
+}
diff --git a/usr.sbin/sade/variable.c b/usr.sbin/sade/variable.c
new file mode 100644
index 0000000..6f896ee
--- /dev/null
+++ b/usr.sbin/sade/variable.c
@@ -0,0 +1,296 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ * Copyright (c) 2001
+ * Murray Stokely. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+
+/* Routines for dealing with variable lists */
+
+static void
+make_variable(char *var, char *value, int dirty)
+{
+ Variable *vp;
+
+ /* Trim leading and trailing whitespace */
+ var = string_skipwhite(string_prune(var));
+
+ if (!var || !*var)
+ return;
+
+
+ /* Now search to see if it's already in the list */
+ for (vp = VarHead; vp; vp = vp->next) {
+ if (!strcmp(vp->name, var)) {
+ if (vp->dirty && !dirty)
+ return;
+ setenv(var, value, 1);
+ free(vp->value);
+ vp->value = strdup(value);
+ if (dirty != -1)
+ vp->dirty = dirty;
+ return;
+ }
+ }
+
+ setenv(var, value, 1);
+ /* No? Create a new one */
+ vp = (Variable *)safe_malloc(sizeof(Variable));
+ vp->name = strdup(var);
+ vp->value = strdup(value);
+ if (dirty == -1)
+ dirty = 0;
+ vp->dirty = dirty;
+ vp->next = VarHead;
+ VarHead = vp;
+}
+
+void
+variable_set(char *var, int dirty)
+{
+ char tmp[1024], *cp;
+
+ if (!var)
+ msgFatal("NULL variable name & value passed.");
+ else if (!*var)
+ msgDebug("Warning: Zero length name & value passed to variable_set()\n");
+ SAFE_STRCPY(tmp, var);
+ if ((cp = index(tmp, '=')) == NULL)
+ msgFatal("Invalid variable format: %s", var);
+ *(cp++) = '\0';
+ make_variable(tmp, string_skipwhite(cp), dirty);
+}
+
+void
+variable_set2(char *var, char *value, int dirty)
+{
+ if (!var || !value)
+ msgFatal("Null name or value passed to set_variable2(%s) = %s!",
+ var ? var : "", value ? value : "");
+ else if (!*var || !*value)
+ msgDebug("Warning: Zero length name or value passed to variable_set2(%s) = %s\n",
+ var, value);
+ make_variable(var, value, dirty);
+}
+
+char *
+variable_get(char *var)
+{
+ return getenv(var);
+}
+
+int
+variable_cmp(char *var, char *value)
+{
+ char *val;
+
+ if ((val = variable_get(var)))
+ return strcmp(val, value);
+ return -1;
+}
+
+void
+variable_unset(char *var)
+{
+ Variable *vp;
+ char name[512], *cp;
+
+ if ((cp = index(var, '=')) != NULL)
+ sstrncpy(name, var, cp - var);
+ else
+ SAFE_STRCPY(name, var);
+ unsetenv(name);
+ /* Now search to see if it's in our list, if we have one.. */
+ if (!VarHead)
+ return;
+ else if (!VarHead->next && !strcmp(VarHead->name, name)) {
+ safe_free(VarHead->name);
+ safe_free(VarHead->value);
+ free(VarHead);
+ VarHead = NULL;
+ }
+ else {
+ for (vp = VarHead; vp; vp = vp->next) {
+ if (!strcmp(vp->name, name)) {
+ Variable *save = vp->next;
+
+ safe_free(vp->name);
+ safe_free(vp->value);
+ *vp = *save;
+ safe_free(save);
+ break;
+ }
+ }
+ }
+}
+
+/* Prompt user for the name of a variable */
+char *
+variable_get_value(char *var, char *prompt, int dirty)
+{
+ char *cp;
+
+ cp = variable_get(var);
+ if (cp && variable_get(VAR_NONINTERACTIVE))
+ return cp;
+ else if ((cp = msgGetInput(cp, "%s", prompt)) != NULL)
+ variable_set2(var, cp, dirty);
+ else
+ cp = NULL;
+ return cp;
+}
+
+/* Check if value passed in data (in the form "variable=value") is
+ 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 = variable_get(tmp);
+ if (cp2) {
+ if (!*cp)
+ return TRUE;
+ else
+ return !strcmp(cp, cp2);
+ }
+ else
+ return FALSE;
+ }
+ else
+ return variable_get(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;
+}
+
+/* Free all of the variables, useful to really start over as when the
+ user selects "restart" from the interrupt menu. */
+void
+free_variables(void)
+{
+ Variable *vp;
+
+ /* Free the variables from our list, if we have one.. */
+ if (!VarHead)
+ return;
+ else if (!VarHead->next) {
+ unsetenv(VarHead->name);
+ safe_free(VarHead->name);
+ safe_free(VarHead->value);
+ free(VarHead);
+ VarHead = NULL;
+ }
+ else {
+ for (vp = VarHead; vp; ) {
+ Variable *save = vp;
+ unsetenv(vp->name);
+ safe_free(vp->name);
+ safe_free(vp->value);
+ vp = vp->next;
+ safe_free(save);
+ }
+ VarHead = NULL;
+ }
+}
+
+/*
+ * Persistent variables. The variables modified by these functions
+ * are not cleared between invocations of sysinstall. This is useful
+ * to allow the user to completely restart sysinstall, without having
+ * it load all of the modules again from the installation media which
+ * are still in memory.
+ */
+
+void
+pvariable_set(char *var)
+{
+ char tmp[1024];
+
+ if (!var)
+ msgFatal("NULL variable name & value passed.");
+ else if (!*var)
+ msgDebug("Warning: Zero length name & value passed to variable_set()\n");
+ /* Add a trivial namespace to whatever name the caller chooses. */
+ SAFE_STRCPY(tmp, "SYSINSTALL_PVAR");
+ if (index(var, '=') == NULL)
+ msgFatal("Invalid variable format: %s", var);
+ strlcat(tmp, var, 1024);
+ putenv(tmp);
+}
+
+char *
+pvariable_get(char *var)
+{
+ char tmp[1024];
+
+ SAFE_STRCPY(tmp, "SYSINSTALL_PVAR");
+ strlcat(tmp, var, 1024);
+ return getenv(tmp);
+}
diff --git a/usr.sbin/sade/wizard.c b/usr.sbin/sade/wizard.c
new file mode 100644
index 0000000..6dec01d
--- /dev/null
+++ b/usr.sbin/sade/wizard.c
@@ -0,0 +1,202 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ *
+ * $FreeBSD$
+ *
+ */
+
+#include "sysinstall.h"
+#include <fcntl.h>
+#include <err.h>
+
+int
+scan_block(int fd, daddr_t block)
+{
+ u_char foo[512];
+
+ if (-1 == lseek(fd,block * 512,SEEK_SET))
+ err(1,"lseek");
+ if (512 != read(fd,foo, 512))
+ return 1;
+ return 0;
+}
+
+void
+Scan_Disk(Disk *d)
+{
+ char device[64];
+ u_long l;
+ int i,j,fd;
+
+ strcpy(device,"/dev/");
+ strcat(device,d->name);
+
+ fd = open(device,O_RDWR);
+ if (fd < 0) {
+ msgWarn("open(%s) failed", device);
+ return;
+ }
+ for(i=-1,l=0;;l++) {
+ j = scan_block(fd,l);
+ if (j != i) {
+ if (i == -1) {
+ printf("%c: %lu.",j ? 'B' : 'G', l);
+ fflush(stdout);
+ } else if (i == 0) {
+ printf(".%lu\nB: %lu.",l-1,l);
+ fflush(stdout);
+ } else {
+ printf(".%lu\nG: %lu.",l-1,l);
+ fflush(stdout);
+ }
+ i = j;
+ }
+ }
+ close(fd);
+}
+
+void
+slice_wizard(Disk *d)
+{
+ Disk *db;
+ char myprompt[BUFSIZ];
+ char input[BUFSIZ];
+ char *p,*q=0;
+ char **cp,*cmds[200];
+ int ncmd,i;
+
+ systemSuspendDialog();
+ sprintf(myprompt,"%s> ", d->name);
+ while(1) {
+ printf("--==##==--\n");
+ Debug_Disk(d);
+ p = CheckRules(d);
+ if (p) {
+ printf("%s",p);
+ free(p);
+ }
+ printf("%s", myprompt);
+ fflush(stdout);
+ q = p = fgets(input,sizeof(input),stdin);
+ if(!p)
+ break;
+ for(cp = cmds; (*cp = strsep(&p, " \t\n")) != NULL;)
+ if (**cp != '\0')
+ cp++;
+ ncmd = cp - cmds;
+ if(!ncmd)
+ continue;
+ if (!strcasecmp(*cmds,"quit")) { break; }
+ if (!strcasecmp(*cmds,"exit")) { break; }
+ if (!strcasecmp(*cmds,"q")) { break; }
+ if (!strcasecmp(*cmds,"x")) { break; }
+ if (!strcasecmp(*cmds,"delete") && ncmd == 2) {
+ printf("delete = %d\n",
+ Delete_Chunk(d,
+ (struct chunk *)strtol(cmds[1],0,0)));
+ continue;
+ }
+ if (!strcasecmp(*cmds,"allfreebsd")) {
+ All_FreeBSD(d, 0);
+ continue;
+ }
+ if (!strcasecmp(*cmds,"dedicate")) {
+ All_FreeBSD(d, 1);
+ continue;
+ }
+ if (!strcasecmp(*cmds,"bios") && ncmd == 4) {
+ Set_Bios_Geom(d,
+ strtol(cmds[1],0,0),
+ strtol(cmds[2],0,0),
+ strtol(cmds[3],0,0));
+ continue;
+ }
+ if (!strcasecmp(*cmds,"list")) {
+ cp = Disk_Names();
+ printf("Disks:");
+ for(i=0;cp[i];i++) {
+ printf(" %s",cp[i]);
+ free(cp[i]);
+ }
+ free(cp);
+ continue;
+ }
+#ifdef PC98
+ if (!strcasecmp(*cmds,"create") && ncmd == 7) {
+ printf("Create=%d\n",
+ Create_Chunk(d,
+ strtol(cmds[1],0,0),
+ strtol(cmds[2],0,0),
+ strtol(cmds[3],0,0),
+ strtol(cmds[4],0,0),
+ strtol(cmds[5],0,0),
+ cmds[6]));
+ continue;
+ }
+#else
+ if (!strcasecmp(*cmds,"create") && ncmd == 6) {
+ printf("Create=%d\n",
+ Create_Chunk(d,
+ strtol(cmds[1],0,0),
+ strtol(cmds[2],0,0),
+ strtol(cmds[3],0,0),
+ strtol(cmds[4],0,0),
+ strtol(cmds[5],0,0)));
+ continue;
+ }
+#endif
+ if (!strcasecmp(*cmds,"read")) {
+ db = d;
+ if (ncmd > 1)
+ d = Open_Disk(cmds[1]);
+ else
+ d = Open_Disk(d->name);
+ if (d)
+ Free_Disk(db);
+ else
+ d = db;
+ continue;
+ }
+ if (!strcasecmp(*cmds,"scan")) {
+ Scan_Disk(d);
+ continue;
+ }
+ if (!strcasecmp(*cmds,"write")) {
+ printf("Write=%d\n",
+ Fake ? 0 : Write_Disk(d));
+ 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");
+#ifdef PC98
+ printf("create offset size enum subtype flags name\n");
+#else
+ printf("create offset size enum subtype flags\n");
+#endif
+ printf("subtype(part): swap=1, ffs=7\t\t");
+ printf("delete pointer\n");
+ printf("list\t\t");
+ printf("quit\n");
+ printf("read [disk]\t\t");
+ printf("scan\n");
+ printf("write\t\t");
+ printf("ENUM:\n\t");
+ for(i=0;chunk_n[i];i++)
+ printf("%d = %s%s",i,chunk_n[i],i == 4 ? "\n\t" : " ");
+ printf("\n");
+
+ }
+ systemResumeDialog();
+}
diff --git a/usr.sbin/sendmail/Makefile b/usr.sbin/sendmail/Makefile
new file mode 100644
index 0000000..32b5e89
--- /dev/null
+++ b/usr.sbin/sendmail/Makefile
@@ -0,0 +1,80 @@
+# @(#)Makefile 8.8 (Berkeley) 3/28/97
+# $FreeBSD$
+
+MAINTAINER= gshapiro@FreeBSD.org
+
+SENDMAIL_DIR=${.CURDIR}/../../contrib/sendmail
+SMDIR= ${SENDMAIL_DIR}/src
+.PATH: ${SMDIR}
+
+BINDIR= /usr/libexec/sendmail
+
+PROG= sendmail
+MAN= mailq.1 newaliases.1 aliases.5 sendmail.8
+SRCS= alias.c arpadate.c bf.c collect.c conf.c control.c \
+ convtime.c daemon.c deliver.c domain.c envelope.c err.c headers.c \
+ macro.c main.c map.c mci.c milter.c mime.c parseaddr.c queue.c \
+ readcf.c recipient.c savemail.c sasl.c sfsasl.c shmticklib.c \
+ sm_resolve.c srvrsmtp.c stab.c stats.c sysexits.c timers.c \
+ tls.c trace.c udb.c usersmtp.c util.c version.c
+BINOWN= root
+BINGRP= smmsp
+.ifdef SENDMAIL_SET_USER_ID
+BINMODE=4555
+.else
+BINMODE=2555
+.endif
+
+# Define the database format to use for aliases et al.
+DBMDEF= -DNEWDB
+# If you don't want NIS alias/map support, comment out this line
+NIS= -DNIS
+# Map extensions
+MAPS= -DMAP_REGEX -DDNSMAP
+
+CFLAGS+= -I${SMDIR} -I${SENDMAIL_DIR}/include -I.
+CFLAGS+= ${DBMDEF} ${NIS} -DMILTER -DNETINET6 -DTCPWRAPPERS ${MAPS}
+
+DPADD= ${LIBUTIL} ${LIBWRAP}
+LDADD= -lutil -lwrap
+
+.if exists(${.OBJDIR}/../../lib/libsm)
+LIBSMDIR:= ${.OBJDIR}/../../lib/libsm
+.else
+LIBSMDIR!= cd ${.CURDIR}/../../lib/libsm; make -V .OBJDIR
+.endif
+LIBSM:= ${LIBSMDIR}/libsm.a
+
+.if exists(${.OBJDIR}/../../lib/libsmutil)
+LIBSMUTILDIR:= ${.OBJDIR}/../../lib/libsmutil
+.else
+LIBSMUTILDIR!= cd ${.CURDIR}/../../lib/libsmutil; make -V .OBJDIR
+.endif
+LIBSMUTIL:= ${LIBSMUTILDIR}/libsmutil.a
+
+DPADD+= ${LIBSMUTIL} ${LIBSM}
+LDADD+= ${LIBSMUTIL} ${LIBSM}
+
+SRCS+= sm_os.h
+CLEANFILES+=sm_os.h
+
+.if exists(${.CURDIR}/../../secure) && !defined(NOCRYPT) && \
+ !defined(NOSECURE) && !defined(NO_OPENSSL) && \
+ !defined(RELEASE_CRUNCH)
+# STARTTLS support
+DISTRIBUTION= crypto
+CFLAGS+= -DSTARTTLS -D_FFR_TLS_1
+LDADD+= -lssl -lcrypto
+DPADD+= ${LIBSSL} ${LIBCRYPTO}
+.endif
+
+# User customizations to the sendmail build environment
+CFLAGS+=${SENDMAIL_CFLAGS}
+DPADD+=${SENDMAIL_DPADD}
+LDADD+=${SENDMAIL_LDADD}
+LDFLAGS+=${SENDMAIL_LDFLAGS}
+
+sm_os.h:
+ ln -sf ${SENDMAIL_DIR}/include/sm/os/sm_os_freebsd.h sm_os.h
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/setextattr/Makefile b/usr.sbin/setextattr/Makefile
new file mode 100644
index 0000000..7eaf132
--- /dev/null
+++ b/usr.sbin/setextattr/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= setextattr
+MAN= setextattr.8
+
+WARNS?= 2
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/setextattr/setextattr.8 b/usr.sbin/setextattr/setextattr.8
new file mode 100644
index 0000000..fbe64d6
--- /dev/null
+++ b/usr.sbin/setextattr/setextattr.8
@@ -0,0 +1,85 @@
+.\"-
+.\" Copyright (c) 2000, 2001 Robert N. M. Watson
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd March 30, 2000
+.Dt SETEXTATTR 8
+.Os
+.Sh NAME
+.Nm setextattr
+.Nd set a named extended attribute
+.Sh SYNOPSIS
+.Nm
+.Ar attrnamespace
+.Ar attrname
+.Ar filename
+.Ar attrvalue
+.Sh DESCRIPTION
+.Nm
+is a user tool to set a named extended attribute on a file or directory to
+the provided string.
+The
+.Ar attrnamespace
+argument should be the namespace of the attribute to retrieve: legal
+values are "user" and "system".
+The
+.Ar attrname
+argument should be the name of the attribute,
+.Ar filename
+the name of the file or directory to set the attribute for, and
+.Ar attrvalue
+a string to store in the attribute.
+.Nm
+will store the string in the file's attribute.
+In order for
+.Nm
+to succeed, the attribute service must be available on the file system,
+and appropriate privilege may be required.
+.Sh EXAMPLES
+.Dl # setextattr system md5 /boot/kernel/kernel `md5 -q /boot/kernel/kernel`
+.Pp
+Set the
+.Dq md5
+extended attribute on the file /boot/kernel/kernel to the string
+containing the output of
+.Dq md5 -q /boot/kernel/kernel .
+.Sh SEE ALSO
+.Xr extattr 2 ,
+.Xr extattr 3 ,
+.Xr extattrctl 8 ,
+.Xr getextattr 8 ,
+.Xr extattr 9
+.Sh HISTORY
+Extended attribute support was developed as part of the TrustedBSD Project,
+and introduced in
+.Fx 5.0 .
+It was developed to support security extensions requiring additional labels
+to be associated with each file or directory.
+.Sh AUTHORS
+Robert N M Watson
+.Sh BUGS
+.Nm
+can only be used to set attributes to strings.
diff --git a/usr.sbin/setextattr/setextattr.c b/usr.sbin/setextattr/setextattr.c
new file mode 100644
index 0000000..de68e68
--- /dev/null
+++ b/usr.sbin/setextattr/setextattr.c
@@ -0,0 +1,74 @@
+/*-
+ * Copyright (c) 1999, 2000, 2001 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+/*
+ * TrustedBSD Project - extended attribute support for UFS-like file systems
+ */
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/extattr.h>
+
+#include <libutil.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+void usage(void);
+
+void
+usage(void)
+{
+
+ fprintf(stderr, "setextattr [attrnamespace] [attrname] [filename] "
+ "[attrvalue]\n");
+ exit(-1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int error, attrnamespace;
+
+ if (argc != 5)
+ usage();
+
+ error = extattr_string_to_namespace(argv[1], &attrnamespace);
+ if (error) {
+ perror(argv[1]);
+ return (-1);
+ }
+
+ error = extattr_set_file(argv[3], attrnamespace, argv[2], argv[4],
+ strlen(argv[4]));
+ if (error == -1) {
+ perror(argv[3]);
+ return (-1);
+ }
+
+ return (0);
+}
diff --git a/usr.sbin/setkey/Makefile b/usr.sbin/setkey/Makefile
new file mode 100644
index 0000000..782b478
--- /dev/null
+++ b/usr.sbin/setkey/Makefile
@@ -0,0 +1,62 @@
+# Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of the project nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+
+PROG= setkey
+MAN= setkey.8
+SRCS= setkey.c parse.y token.l
+
+CFLAGS+= -I${.CURDIR} -I${.CURDIR}/../../lib/libipsec
+YFLAGS= -d
+
+DPADD= ${LIBL} ${LIBY}
+LDADD= -ll -ly
+
+CLEANFILES= y.tab.c y.tab.h key_test.o keytest
+
+# libpfkey.
+# ipsec_strerror.c is for avoiding shlib reference to non-exported function.
+.PATH: ${.CURDIR}/../../lib/libipsec ${.CURDIR}/../../sys/netkey
+SRCS+= pfkey.c pfkey_dump.c key_debug.c ipsec_strerror.c
+CFLAGS+= -I${.CURDIR}/../../lib/libipsec -I${.CURDIR}/../../sys/netkey
+
+SRCS+= y.tab.h
+y.tab.h: parse.y
+CFLAGS+= -DIPSEC_DEBUG -DINET6 -DYY_NO_UNPUT -I.
+DPADD+= ${LIBIPSEC}
+LDADD+= -lipsec
+CLEANFILES+= scriptdump y.tab.h
+
+#SCRIPTS= scriptdump
+
+LOCALPREFIX= /usr
+
+scriptdump: scriptdump.pl
+ sed -e 's#@LOCALPREFIX@#${LOCALPREFIX}#' < $> > scriptdump
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/setkey/parse.y b/usr.sbin/setkey/parse.y
new file mode 100644
index 0000000..1d43dc4
--- /dev/null
+++ b/usr.sbin/setkey/parse.y
@@ -0,0 +1,933 @@
+/* $FreeBSD$ */
+/* $KAME: kame/kame/kame/setkey/parse.y,v 1.36 2001/06/07 15:53:12 sakane Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+%{
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <net/route.h>
+#include <netinet/in.h>
+#include <net/pfkeyv2.h>
+#include <netkey/key_var.h>
+#include <netinet6/ipsec.h>
+#include <arpa/inet.h>
+
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "libpfkey.h"
+#include "vchar.h"
+
+#define ATOX(c) \
+ (isdigit(c) ? (c - '0') : (isupper(c) ? (c - 'A' + 10) : (c - 'a' + 10) ))
+
+u_int p_type;
+u_int32_t p_spi;
+int p_no_spi;
+struct sockaddr *p_src, *p_dst;
+u_int p_prefs, p_prefd, p_upper;
+u_int p_satype, p_ext, p_alg_enc, p_alg_auth, p_replay, p_mode;
+u_int32_t p_reqid;
+u_int p_key_enc_len, p_key_auth_len;
+caddr_t p_key_enc, p_key_auth;
+time_t p_lt_hard, p_lt_soft;
+
+u_int p_policy_len;
+char *p_policy;
+
+/* temporary buffer */
+static struct sockaddr *pp_addr;
+static u_int pp_prefix;
+static u_int pp_port;
+static caddr_t pp_key;
+
+extern u_char m_buf[BUFSIZ];
+extern int m_len;
+extern char cmdarg[8192];
+extern int f_debug;
+
+static struct addrinfo *parse_addr __P((char *, char *, int));
+static int setvarbuf __P((int *, struct sadb_ext *, int, caddr_t, int));
+void parse_init __P((void));
+void free_buffer __P((void));
+
+extern int setkeymsg __P((void));
+extern int sendkeymsg __P((void));
+
+extern int yylex __P((void));
+extern void yyfatal __P((const char *));
+extern void yyerror __P((const char *));
+%}
+
+%union {
+ unsigned long num;
+ vchar_t val;
+}
+
+%token EOT
+%token ADD GET DELETE FLUSH DUMP
+%token ADDRESS PREFIX PORT PORTANY
+%token UP_PROTO PR_ESP PR_AH PR_IPCOMP
+%token F_PROTOCOL F_AUTH F_ENC F_REPLAY F_COMP F_RAWCPI
+%token F_MODE MODE F_REQID
+%token F_EXT EXTENSION NOCYCLICSEQ
+%token ALG_AUTH ALG_ENC ALG_ENC_DESDERIV ALG_ENC_DES32IV ALG_COMP
+%token F_LIFETIME_HARD F_LIFETIME_SOFT
+%token DECSTRING QUOTEDSTRING HEXSTRING STRING ANY
+ /* SPD management */
+%token SPDADD SPDDELETE SPDDUMP SPDFLUSH
+%token F_POLICY PL_REQUESTS
+
+%type <num> PORT PREFIX EXTENSION MODE
+%type <num> UP_PROTO PR_ESP PR_AH PR_IPCOMP
+%type <num> ALG_AUTH ALG_ENC ALG_ENC_DESDERIV ALG_ENC_DES32IV ALG_COMP
+%type <num> DECSTRING
+%type <val> ADDRESS PL_REQUESTS
+%type <val> key_string policy_requests
+%type <val> QUOTEDSTRING HEXSTRING STRING
+
+%%
+commands
+ : /*NOTHING*/
+ | commands command
+ {
+ if (f_debug) {
+ printf("cmdarg:\n%s\n", cmdarg);
+ } else {
+ setkeymsg();
+ sendkeymsg();
+ }
+ free_buffer();
+ parse_init();
+ }
+ ;
+
+command
+ : add_command
+ | get_command
+ | delete_command
+ | deleteall_command
+ | flush_command
+ | dump_command
+ | spdadd_command
+ | spddelete_command
+ | spddump_command
+ | spdflush_command
+ ;
+ /* commands concerned with management, there is in tail of this file. */
+
+ /* add command */
+add_command
+ : ADD { p_type = SADB_ADD; }
+ sa_selector_spec extension_spec algorithm_spec EOT
+ ;
+
+ /* delete */
+delete_command
+ : DELETE { p_type = SADB_DELETE; }
+ sa_selector_spec extension_spec
+ {
+ if (p_mode != IPSEC_MODE_ANY)
+ yyerror("WARNING: mode is obsoleted.");
+ }
+ EOT
+ ;
+
+ /* deleteall command */
+deleteall_command
+ : DELETEALL { p_type = SADB_DELETE; }
+ ipaddress { p_src = pp_addr; }
+ ipaddress { p_dst = pp_addr; }
+ protocol_spec
+ { p_no_spi = 1; }
+ EOT
+ ;
+
+ /* get command */
+get_command
+ : GET { p_type = SADB_GET; }
+ sa_selector_spec extension_spec
+ {
+ if (p_mode != IPSEC_MODE_ANY)
+ yyerror("WARNING: mode is obsoleted.");
+ }
+ EOT
+ ;
+
+ /* flush */
+flush_command
+ : FLUSH { p_type = SADB_FLUSH; }
+ protocol_spec EOT
+ ;
+
+ /* dump */
+dump_command
+ : DUMP { p_type = SADB_DUMP; }
+ protocol_spec EOT
+ ;
+
+ /* sa_selector_spec */
+sa_selector_spec
+ : ipaddress { p_src = pp_addr; }
+ ipaddress { p_dst = pp_addr; }
+ protocol_spec spi
+ ;
+
+protocol_spec
+ : /*NOTHING*/ { p_satype = SADB_SATYPE_UNSPEC; }
+ | PR_ESP
+ {
+ p_satype = SADB_SATYPE_ESP;
+ if ($1 == 1)
+ p_ext |= SADB_X_EXT_OLD;
+ else
+ p_ext &= ~SADB_X_EXT_OLD;
+ }
+ | PR_AH
+ {
+ p_satype = SADB_SATYPE_AH;
+ if ($1 == 1)
+ p_ext |= SADB_X_EXT_OLD;
+ else
+ p_ext &= ~SADB_X_EXT_OLD;
+ }
+ | PR_IPCOMP
+ {
+ p_satype = SADB_X_SATYPE_IPCOMP;
+ }
+ ;
+
+spi
+ : DECSTRING { p_spi = $1; }
+ | HEXSTRING
+ {
+ caddr_t bp;
+ caddr_t yp = $1.buf;
+ char buf0[4], buf[4];
+ int i, j;
+
+ /* sanity check */
+ if ($1.len > 4) {
+ yyerror("SPI too big.");
+ free($1.buf);
+ return -1;
+ }
+
+ bp = buf0;
+ while (*yp) {
+ *bp = (ATOX(yp[0]) << 4) | ATOX(yp[1]);
+ yp += 2, bp++;
+ }
+
+ /* initialize */
+ for (i = 0; i < 4; i++) buf[i] = 0;
+
+ for (j = $1.len - 1, i = 3; j >= 0; j--, i--)
+ buf[i] = buf0[j];
+
+ /* XXX: endian */
+ p_spi = ntohl(*(u_int32_t *)buf);
+
+ free($1.buf);
+ }
+ ;
+
+algorithm_spec
+ : esp_spec
+ | ah_spec
+ | ipcomp_spec
+ ;
+
+esp_spec
+ : F_ENC enc_alg enc_key F_AUTH auth_alg auth_key
+ | F_ENC enc_alg enc_key
+ ;
+
+ah_spec
+ : F_AUTH auth_alg auth_key
+ ;
+
+ipcomp_spec
+ : F_COMP ALG_COMP { p_alg_enc = $2; }
+ | F_COMP ALG_COMP { p_alg_enc = $2; }
+ F_RAWCPI { p_ext |= SADB_X_EXT_RAWCPI; }
+ ;
+
+enc_alg
+ : ALG_ENC { p_alg_enc = $1; }
+ | ALG_ENC_DESDERIV
+ {
+ p_alg_enc = $1;
+ if (p_ext & SADB_X_EXT_OLD) {
+ yyerror("algorithm mismatched.");
+ return -1;
+ }
+ p_ext |= SADB_X_EXT_DERIV;
+ }
+ | ALG_ENC_DES32IV
+ {
+ p_alg_enc = $1;
+ if (!(p_ext & SADB_X_EXT_OLD)) {
+ yyerror("algorithm mismatched.");
+ return -1;
+ }
+ p_ext |= SADB_X_EXT_IV4B;
+ }
+ ;
+
+enc_key
+ : /*NOTHING*/
+ {
+ if (p_alg_enc != SADB_EALG_NULL) {
+ yyerror("no key found.");
+ return -1;
+ }
+ }
+ | key_string
+ {
+ p_key_enc_len = $1.len;
+ p_key_enc = pp_key;
+
+ if (ipsec_check_keylen(SADB_EXT_SUPPORTED_ENCRYPT,
+ p_alg_enc,
+ PFKEY_UNUNIT64(p_key_enc_len)) < 0) {
+ yyerror(ipsec_strerror());
+ return -1;
+ }
+ }
+ ;
+
+auth_alg
+ : ALG_AUTH { p_alg_auth = $1; }
+ ;
+
+auth_key
+ : /*NOTHING*/
+ {
+ if (p_alg_auth != SADB_X_AALG_NULL) {
+ yyerror("no key found.");
+ return -1;
+ }
+ }
+ | key_string
+ {
+ p_key_auth_len = $1.len;
+ p_key_auth = pp_key;
+
+ if (ipsec_check_keylen(SADB_EXT_SUPPORTED_AUTH,
+ p_alg_auth,
+ PFKEY_UNUNIT64(p_key_auth_len)) < 0) {
+ yyerror(ipsec_strerror());
+ return -1;
+ }
+ }
+ ;
+
+key_string
+ : QUOTEDSTRING
+ {
+ pp_key = $1.buf;
+ /* free pp_key later */
+ }
+ | HEXSTRING
+ {
+ caddr_t bp;
+ caddr_t yp = $1.buf;
+
+ if ((pp_key = malloc($1.len)) == 0) {
+ free($1.buf);
+ yyerror("not enough core");
+ return -1;
+ }
+ memset(pp_key, 0, $1.len);
+
+ bp = pp_key;
+ while (*yp) {
+ *bp = (ATOX(yp[0]) << 4) | ATOX(yp[1]);
+ yp += 2, bp++;
+ }
+
+ free($1.buf);
+ }
+ ;
+
+extension_spec
+ : /*NOTHING*/
+ | extension_spec extension
+ ;
+
+extension
+ : F_EXT EXTENSION { p_ext |= $2; }
+ | F_EXT NOCYCLICSEQ { p_ext &= ~SADB_X_EXT_CYCSEQ; }
+ | F_MODE MODE { p_mode = $2; }
+ | F_MODE ANY { p_mode = IPSEC_MODE_ANY; }
+ | F_REQID DECSTRING { p_reqid = $2; }
+ | F_REPLAY DECSTRING
+ {
+ if (p_ext & SADB_X_EXT_OLD) {
+ yyerror("replay prevention "
+ "only use on new spec.");
+ return -1;
+ }
+ p_replay = $2;
+ }
+ | F_LIFETIME_HARD DECSTRING { p_lt_hard = $2; }
+ | F_LIFETIME_SOFT DECSTRING { p_lt_soft = $2; }
+ ;
+
+ /* definition about command for SPD management */
+ /* spdadd */
+spdadd_command
+ : SPDADD
+ {
+ p_type = SADB_X_SPDADD;
+ p_satype = SADB_SATYPE_UNSPEC;
+ }
+ sp_selector_spec policy_spec EOT
+ ;
+
+spddelete_command:
+ SPDDELETE
+ {
+ p_type = SADB_X_SPDDELETE;
+ p_satype = SADB_SATYPE_UNSPEC;
+ }
+ sp_selector_spec policy_spec EOT
+ ;
+
+spddump_command:
+ SPDDUMP
+ {
+ p_type = SADB_X_SPDDUMP;
+ p_satype = SADB_SATYPE_UNSPEC;
+ }
+ EOT
+ ;
+
+spdflush_command:
+ SPDFLUSH
+ {
+ p_type = SADB_X_SPDFLUSH;
+ p_satype = SADB_SATYPE_UNSPEC;
+ }
+ EOT
+ ;
+
+ /* sp_selector_spec */
+sp_selector_spec
+ : ipaddress { p_src = pp_addr; }
+ prefix { p_prefs = pp_prefix; }
+ port
+ {
+ switch (p_src->sa_family) {
+ case AF_INET:
+ ((struct sockaddr_in *)p_src)->sin_port =
+ htons(pp_port);
+ break;
+#ifdef INET6
+ case AF_INET6:
+ ((struct sockaddr_in6 *)p_src)->sin6_port =
+ htons(pp_port);
+ break;
+#endif
+ default:
+ exit(1); /*XXX*/
+ }
+ }
+ ipaddress { p_dst = pp_addr; }
+ prefix { p_prefd = pp_prefix; }
+ port
+ {
+ switch (p_dst->sa_family) {
+ case AF_INET:
+ ((struct sockaddr_in *)p_dst)->sin_port =
+ htons(pp_port);
+ break;
+#ifdef INET6
+ case AF_INET6:
+ ((struct sockaddr_in6 *)p_dst)->sin6_port =
+ htons(pp_port);
+ break;
+#endif
+ default:
+ exit(1); /*XXX*/
+ }
+ }
+ upper_spec
+ {
+ /* XXX is it something userland should check? */
+#if 0
+ switch (p_upper) {
+ case IPPROTO_ICMP:
+ case IPPROTO_ICMPV6:
+ if (_INPORTBYSA(p_src) != IPSEC_PORT_ANY
+ || _INPORTBYSA(p_dst) != IPSEC_PORT_ANY) {
+ yyerror("port number must be \"any\".");
+ return -1;
+ }
+ if ((pp_addr->sa_family == AF_INET6
+ && p_upper == IPPROTO_ICMP)
+ || (pp_addr->sa_family == AF_INET
+ && p_upper == IPPROTO_ICMPV6)) {
+ yyerror("upper layer protocol "
+ "mismatched.\n");
+ return -1;
+ }
+ break;
+ default:
+ break;
+ }
+#endif
+ }
+ ;
+
+ipaddress
+ : ADDRESS
+ {
+ struct addrinfo *res;
+
+ res = parse_addr($1.buf, NULL, AI_NUMERICHOST);
+ if (res == NULL) {
+ free($1.buf);
+ return -1;
+ }
+ pp_addr = (struct sockaddr *)malloc(res->ai_addrlen);
+ if (!pp_addr) {
+ yyerror("not enough core");
+ goto end;
+ }
+
+ memcpy(pp_addr, res->ai_addr, res->ai_addrlen);
+ end:
+ freeaddrinfo(res);
+ free($1.buf);
+ }
+ ;
+
+prefix
+ : /*NOTHING*/ { pp_prefix = ~0; }
+ | PREFIX { pp_prefix = $1; }
+ ;
+
+port
+ : /*NOTHING*/ { pp_port = IPSEC_PORT_ANY; }
+ | PORT { pp_port = $1; }
+ | PORTANY { pp_port = IPSEC_PORT_ANY; }
+ ;
+
+upper_spec
+ : DECSTRING { p_upper = $1; }
+ | UP_PROTO { p_upper = $1; }
+ | ANY { p_upper = IPSEC_ULPROTO_ANY; }
+ | STRING
+ {
+ struct protoent *ent;
+
+ ent = getprotobyname($1.buf);
+ if (ent)
+ p_upper = ent->p_proto;
+ else {
+ if (strcmp("icmp6", $1.buf) == 0) {
+ p_upper = IPPROTO_ICMPV6;
+ } else if(strcmp("ip4", $1.buf) == 0) {
+ p_upper = IPPROTO_IPV4;
+ } else {
+ yyerror("invalid upper layer protocol");
+ free($1.buf);
+ return -1;
+ }
+ }
+ free($1.buf);
+ }
+ ;
+
+policy_spec
+ : F_POLICY policy_requests
+ {
+ p_policy = ipsec_set_policy($2.buf, $2.len);
+ if (p_policy == NULL) {
+ free($2.buf);
+ p_policy = NULL;
+ yyerror(ipsec_strerror());
+ return -1;
+ }
+
+ p_policy_len = ipsec_get_policylen(p_policy);
+
+ free($2.buf);
+ }
+ ;
+
+policy_requests
+ : PL_REQUESTS { $$ = $1; }
+ ;
+
+%%
+
+int
+setkeymsg()
+{
+ struct sadb_msg m_msg;
+
+ m_msg.sadb_msg_version = PF_KEY_V2;
+ m_msg.sadb_msg_type = p_type;
+ m_msg.sadb_msg_errno = 0;
+ m_msg.sadb_msg_satype = p_satype;
+ m_msg.sadb_msg_reserved = 0;
+ m_msg.sadb_msg_seq = 0;
+ m_msg.sadb_msg_pid = getpid();
+
+ m_len = sizeof(struct sadb_msg);
+ memcpy(m_buf, &m_msg, m_len);
+
+ switch (p_type) {
+ case SADB_FLUSH:
+ case SADB_DUMP:
+ break;
+
+ case SADB_ADD:
+ /* set encryption algorithm, if present. */
+ if (p_satype != SADB_X_SATYPE_IPCOMP && p_alg_enc != SADB_EALG_NONE) {
+ struct sadb_key m_key;
+
+ m_key.sadb_key_len =
+ PFKEY_UNIT64(sizeof(m_key)
+ + PFKEY_ALIGN8(p_key_enc_len));
+ m_key.sadb_key_exttype = SADB_EXT_KEY_ENCRYPT;
+ m_key.sadb_key_bits = p_key_enc_len * 8;
+ m_key.sadb_key_reserved = 0;
+
+ setvarbuf(&m_len,
+ (struct sadb_ext *)&m_key, sizeof(m_key),
+ (caddr_t)p_key_enc, p_key_enc_len);
+ }
+
+ /* set authentication algorithm, if present. */
+ if (p_alg_auth != SADB_AALG_NONE) {
+ struct sadb_key m_key;
+
+ m_key.sadb_key_len =
+ PFKEY_UNIT64(sizeof(m_key)
+ + PFKEY_ALIGN8(p_key_auth_len));
+ m_key.sadb_key_exttype = SADB_EXT_KEY_AUTH;
+ m_key.sadb_key_bits = p_key_auth_len * 8;
+ m_key.sadb_key_reserved = 0;
+
+ setvarbuf(&m_len,
+ (struct sadb_ext *)&m_key, sizeof(m_key),
+ (caddr_t)p_key_auth, p_key_auth_len);
+ }
+
+ /* set lifetime for HARD */
+ if (p_lt_hard != 0) {
+ struct sadb_lifetime m_lt;
+ u_int len = sizeof(struct sadb_lifetime);
+
+ m_lt.sadb_lifetime_len = PFKEY_UNIT64(len);
+ m_lt.sadb_lifetime_exttype = SADB_EXT_LIFETIME_HARD;
+ m_lt.sadb_lifetime_allocations = 0;
+ m_lt.sadb_lifetime_bytes = 0;
+ m_lt.sadb_lifetime_addtime = p_lt_hard;
+ m_lt.sadb_lifetime_usetime = 0;
+
+ memcpy(m_buf + m_len, &m_lt, len);
+ m_len += len;
+ }
+
+ /* set lifetime for SOFT */
+ if (p_lt_soft != 0) {
+ struct sadb_lifetime m_lt;
+ u_int len = sizeof(struct sadb_lifetime);
+
+ m_lt.sadb_lifetime_len = PFKEY_UNIT64(len);
+ m_lt.sadb_lifetime_exttype = SADB_EXT_LIFETIME_SOFT;
+ m_lt.sadb_lifetime_allocations = 0;
+ m_lt.sadb_lifetime_bytes = 0;
+ m_lt.sadb_lifetime_addtime = p_lt_soft;
+ m_lt.sadb_lifetime_usetime = 0;
+
+ memcpy(m_buf + m_len, &m_lt, len);
+ m_len += len;
+ }
+ /* FALLTHROUGH */
+
+ case SADB_DELETE:
+ case SADB_GET:
+ {
+ struct sadb_sa m_sa;
+ struct sadb_x_sa2 m_sa2;
+ struct sadb_address m_addr;
+ u_int len;
+
+ if (p_no_spi == 0) {
+ len = sizeof(struct sadb_sa);
+ m_sa.sadb_sa_len = PFKEY_UNIT64(len);
+ m_sa.sadb_sa_exttype = SADB_EXT_SA;
+ m_sa.sadb_sa_spi = htonl(p_spi);
+ m_sa.sadb_sa_replay = p_replay;
+ m_sa.sadb_sa_state = 0;
+ m_sa.sadb_sa_auth = p_alg_auth;
+ m_sa.sadb_sa_encrypt = p_alg_enc;
+ m_sa.sadb_sa_flags = p_ext;
+
+ memcpy(m_buf + m_len, &m_sa, len);
+ m_len += len;
+
+ len = sizeof(struct sadb_x_sa2);
+ m_sa2.sadb_x_sa2_len = PFKEY_UNIT64(len);
+ m_sa2.sadb_x_sa2_exttype = SADB_X_EXT_SA2;
+ m_sa2.sadb_x_sa2_mode = p_mode;
+ m_sa2.sadb_x_sa2_reqid = p_reqid;
+
+ memcpy(m_buf + m_len, &m_sa2, len);
+ m_len += len;
+ }
+
+ /* set src */
+ m_addr.sadb_address_len =
+ PFKEY_UNIT64(sizeof(m_addr)
+ + PFKEY_ALIGN8(p_src->sa_len));
+ m_addr.sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
+ m_addr.sadb_address_proto = IPSEC_ULPROTO_ANY;
+ switch (p_src->sa_family) {
+ case AF_INET:
+ m_addr.sadb_address_prefixlen =
+ sizeof(struct in_addr) << 3;
+ break;
+#ifdef INET6
+ case AF_INET6:
+ m_addr.sadb_address_prefixlen =
+ sizeof(struct in6_addr) << 3;
+ break;
+#endif
+ default:
+ yyerror("unsupported address family");
+ exit(1); /*XXX*/
+ }
+ m_addr.sadb_address_reserved = 0;
+
+ setvarbuf(&m_len,
+ (struct sadb_ext *)&m_addr, sizeof(m_addr),
+ (caddr_t)p_src, p_src->sa_len);
+
+ /* set dst */
+ m_addr.sadb_address_len =
+ PFKEY_UNIT64(sizeof(m_addr)
+ + PFKEY_ALIGN8(p_dst->sa_len));
+ m_addr.sadb_address_exttype = SADB_EXT_ADDRESS_DST;
+ m_addr.sadb_address_proto = IPSEC_ULPROTO_ANY;
+ switch (p_dst->sa_family) {
+ case AF_INET:
+ m_addr.sadb_address_prefixlen =
+ sizeof(struct in_addr) << 3;
+ break;
+#ifdef INET6
+ case AF_INET6:
+ m_addr.sadb_address_prefixlen =
+ sizeof(struct in6_addr) << 3;
+ break;
+#endif
+ default:
+ yyerror("unsupported address family");
+ exit(1); /*XXX*/
+ }
+ m_addr.sadb_address_reserved = 0;
+
+ setvarbuf(&m_len,
+ (struct sadb_ext *)&m_addr, sizeof(m_addr),
+ (caddr_t)p_dst, p_dst->sa_len);
+ }
+ break;
+
+ /* for SPD management */
+ case SADB_X_SPDFLUSH:
+ case SADB_X_SPDDUMP:
+ break;
+
+ case SADB_X_SPDADD:
+ case SADB_X_SPDDELETE:
+ {
+ struct sadb_address m_addr;
+ u_int8_t plen;
+
+ memcpy(m_buf + m_len, p_policy, p_policy_len);
+ m_len += p_policy_len;
+ free(p_policy);
+ p_policy = NULL;
+
+ /* set src */
+ m_addr.sadb_address_len =
+ PFKEY_UNIT64(sizeof(m_addr)
+ + PFKEY_ALIGN8(p_src->sa_len));
+ m_addr.sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
+ m_addr.sadb_address_proto = p_upper;
+ switch (p_src->sa_family) {
+ case AF_INET:
+ plen = sizeof(struct in_addr) << 3;
+ break;
+#ifdef INET6
+ case AF_INET6:
+ plen = sizeof(struct in6_addr) << 3;
+ break;
+#endif
+ default:
+ yyerror("unsupported address family");
+ exit(1); /*XXX*/
+ }
+ m_addr.sadb_address_prefixlen =
+ (p_prefs != ~0 ? p_prefs : plen);
+ m_addr.sadb_address_reserved = 0;
+
+ setvarbuf(&m_len,
+ (struct sadb_ext *)&m_addr, sizeof(m_addr),
+ (caddr_t)p_src, p_src->sa_len);
+
+ /* set dst */
+ m_addr.sadb_address_len =
+ PFKEY_UNIT64(sizeof(m_addr)
+ + PFKEY_ALIGN8(p_dst->sa_len));
+ m_addr.sadb_address_exttype = SADB_EXT_ADDRESS_DST;
+ m_addr.sadb_address_proto = p_upper;
+ switch (p_dst->sa_family) {
+ case AF_INET:
+ plen = sizeof(struct in_addr) << 3;
+ break;
+#ifdef INET6
+ case AF_INET6:
+ plen = sizeof(struct in6_addr) << 3;
+ break;
+#endif
+ default:
+ yyerror("unsupported address family");
+ exit(1); /*XXX*/
+ }
+ m_addr.sadb_address_prefixlen =
+ (p_prefd != ~0 ? p_prefd : plen);
+ m_addr.sadb_address_reserved = 0;
+
+ setvarbuf(&m_len,
+ (struct sadb_ext *)&m_addr, sizeof(m_addr),
+ (caddr_t)p_dst, p_dst->sa_len);
+ }
+ break;
+ }
+
+ ((struct sadb_msg *)m_buf)->sadb_msg_len = PFKEY_UNIT64(m_len);
+
+ return 0;
+}
+
+static struct addrinfo *
+parse_addr(host, port, flag)
+ char *host;
+ char *port;
+ int flag;
+{
+ struct addrinfo hints, *res = NULL;
+ int error;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_flags = flag;
+ error = getaddrinfo(host, port, &hints, &res);
+ if (error != 0) {
+ yyerror(gai_strerror(error));
+ return NULL;
+ }
+ if (res->ai_next != NULL) {
+ yyerror(gai_strerror(error));
+ }
+ return res;
+}
+
+static int
+setvarbuf(off, ebuf, elen, vbuf, vlen)
+ caddr_t vbuf;
+ struct sadb_ext *ebuf;
+ int *off, elen, vlen;
+{
+ memset(m_buf + *off, 0, PFKEY_UNUNIT64(ebuf->sadb_ext_len));
+ memcpy(m_buf + *off, (caddr_t)ebuf, elen);
+ memcpy(m_buf + *off + elen, vbuf, vlen);
+ (*off) += PFKEY_ALIGN8(elen + vlen);
+
+ return 0;
+}
+
+void
+parse_init()
+{
+ p_type = 0;
+ p_spi = 0;
+ p_no_spi = 0;
+
+ p_src = 0, p_dst = 0;
+ pp_prefix = p_prefs = p_prefd = ~0;
+ pp_port = IPSEC_PORT_ANY;
+ p_upper = 0;
+
+ p_satype = 0;
+ p_ext = SADB_X_EXT_CYCSEQ;
+ p_alg_enc = SADB_EALG_NONE;
+ p_alg_auth = SADB_AALG_NONE;
+ p_mode = IPSEC_MODE_ANY;
+ p_reqid = 0;
+ p_replay = 0;
+ p_key_enc_len = p_key_auth_len = 0;
+ p_key_enc = p_key_auth = 0;
+ p_lt_hard = p_lt_soft = 0;
+
+ p_policy_len = 0;
+ p_policy = NULL;
+
+ memset(cmdarg, 0, sizeof(cmdarg));
+
+ return;
+}
+
+void
+free_buffer()
+{
+ if (p_src) free(p_src);
+ if (p_dst) free(p_dst);
+ if (p_key_enc) free(p_key_enc);
+ if (p_key_auth) free(p_key_auth);
+
+ return;
+}
+
diff --git a/usr.sbin/setkey/sample.cf b/usr.sbin/setkey/sample.cf
new file mode 100644
index 0000000..978289d
--- /dev/null
+++ b/usr.sbin/setkey/sample.cf
@@ -0,0 +1,219 @@
+# Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of the project nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+
+# There are sample scripts for IPsec configuration by manual keying.
+# A security association is uniquely identified by a triple consisting
+# of a Security Parameter Index (SPI), an IP Destination Address, and a
+# security protocol (AH or ESP) identifier. You must take care of these
+# parameters when you configure by manual keying.
+
+# ESP transport mode is recommended for TCP port number 110 between
+# Host-A and Host-B. Encryption algorithm is blowfish-cbc whose key
+# is "kamekame", and authentication algorithm is hmac-sha1 whose key
+# is "this is the test key".
+#
+# ============ ESP ============
+# | |
+# Host-A Host-B
+# fec0::10 -------------------- fec0::11
+#
+# At Host-A and Host-B,
+spdadd fec0::10[any] fec0::11[110] tcp -P out ipsec
+ esp/transport/fec0::10-fec0::11/use ;
+spdadd fec0::11[110] fec0::10[any] tcp -P in ipsec
+ esp/transport/fec0::11-fec0::10/use ;
+add fec0::10 fec0::11 esp 0x10001
+ -m transport
+ -E blowfish-cbc "kamekame"
+ -A hmac-sha1 "this is the test key" ;
+add fec0::11 fec0::10 esp 0x10002
+ -m transport
+ -E blowfish-cbc "kamekame"
+ -A hmac-sha1 "this is the test key" ;
+
+# "[any]" is wildcard of port number. Note that "[0]" is the number of
+# zero in port number.
+
+# Security protocol is old AH tunnel mode, i.e. RFC1826, with keyed-md5
+# whose key is "this is the test" as authentication algorithm.
+# That protocol takes place between Gateway-A and Gateway-B.
+#
+# ======= AH =======
+# | |
+# Network-A Gateway-A Gateway-B Network-B
+# 10.0.1.0/24 ---- 172.16.0.1 ----- 172.16.0.2 ---- 10.0.2.0/24
+#
+# At Gateway-A:
+spdadd 10.0.1.0/24 10.0.2.0/24 any -P out ipsec
+ ah/tunnel/172.16.0.1-172.16.0.2/require ;
+spdadd 10.0.2.0/24 10.0.1.0/24 any -P in ipsec
+ ah/tunnel/172.16.0.2-172.16.0.1/require ;
+add 172.16.0.1 172.16.0.2 ah-old 0x10003
+ -m any
+ -A keyed-md5 "this is the test" ;
+add 172.16.0.2 172.16.0.1 ah-old 0x10004
+ -m any
+ -A keyed-md5 "this is the test" ;
+
+# If port number field is omitted such above then "[any]" is employed.
+# -m specifies the mode of SA to be used. "-m any" means wildcard of
+# mode of security protocol. You can use this SAs for both tunnel and
+# transport mode.
+
+# At Gateway-B. Attention to the selector and peer's IP address for tunnel.
+spdadd 10.0.2.0/24 10.0.1.0/24 any -P out ipsec
+ ah/tunnel/172.16.0.2-172.16.0.1/require ;
+spdadd 10.0.1.0/24 10.0.2.0/24 any -P in ipsec
+ ah/tunnel/172.16.0.1-172.16.0.2/require ;
+add 172.16.0.1 172.16.0.2 ah-old 0x10003
+ -m tunnel
+ -A keyed-md5 "this is the test" ;
+add 172.16.0.2 172.16.0.1 ah-old 0x10004
+ -m tunnel
+ -A keyed-md5 "this is the test" ;
+
+# AH transport mode followed by ESP tunnel mode is required between
+# Gateway-A and Gateway-B.
+# Encryption algorithm is 3des-cbc, and authentication algorithm for ESP
+# is hmac-sha1. Authentication algorithm for AH is hmac-md5.
+#
+# ========== AH =========
+# | ======= ESP ===== |
+# | | | |
+# Network-A Gateway-A Gateway-B Network-B
+# fec0:0:0:1::/64 --- fec0:0:0:1::1 ---- fec0:0:0:2::1 --- fec0:0:0:2::/64
+#
+# At Gateway-A:
+spdadd fec0:0:0:1::/64 fec0:0:0:2::/64 any -P out ipsec
+ esp/tunnel/fec0:0:0:1::1-fec0:0:0:2::1/require
+ ah/transport/fec0:0:0:1::1-fec0:0:0:2::1/require ;
+spdadd fec0:0:0:2::/64 fec0:0:0:1::/64 any -P in ipsec
+ esp/tunnel/fec0:0:0:2::1-fec0:0:0:1::1/require
+ ah/transport/fec0:0:0:2::1-fec0:0:0:1::1/require ;
+add fec0:0:0:1::1 fec0:0:0:2::1 esp 0x10001
+ -m tunnel
+ -E 3des-cbc "kamekame12341234kame1234"
+ -A hmac-sha1 "this is the test key" ;
+add fec0:0:0:1::1 fec0:0:0:2::1 ah 0x10001
+ -m transport
+ -A hmac-md5 "this is the test" ;
+add fec0:0:0:2::1 fec0:0:0:1::1 esp 0x10001
+ -m tunnel
+ -E 3des-cbc "kamekame12341234kame1234"
+ -A hmac-sha1 "this is the test key" ;
+add fec0:0:0:2::1 fec0:0:0:1::1 ah 0x10001
+ -m transport
+ -A hmac-md5 "this is the test" ;
+
+# ESP tunnel mode is required between Host-A and Gateway-A.
+# Encryption algorithm is cast128-cbc, and authentication algorithm
+# for ESP is hmac-sha1.
+# ESP transport mode is recommended between Host-A and Host-B.
+# Encryption algorithm is rc5-cbc, and authentication algorithm
+# for ESP is hmac-md5.
+#
+# ================== ESP =================
+# | ======= ESP ======= |
+# | | | |
+# Host-A Gateway-A Host-B
+# fec0:0:0:1::1 ---- fec0:0:0:2::1 ---- fec0:0:0:2::2
+#
+# At Host-A:
+spdadd fec0:0:0:1::1[any] fec0:0:0:2::2[80] tcp -P out ipsec
+ esp/transport/fec0:0:0:1::1-fec0:0:0:2::2/use
+ esp/tunnel/fec0:0:0:1::1-fec0:0:0:2::1/require ;
+spdadd fec0:0:0:2::1[80] fec0:0:0:1::1[any] tcp -P in ipsec
+ esp/transport/fec0:0:0:2::2-fec0:0:0:1::1/use
+ esp/tunnel/fec0:0:0:2::1-fec0:0:0:1::1/require ;
+add fec0:0:0:1::1 fec0:0:0:2::2 esp 0x10001
+ -m transport
+ -E cast128-cbc "12341234"
+ -A hmac-sha1 "this is the test key" ;
+add fec0:0:0:1::1 fec0:0:0:2::1 esp 0x10002
+ -E rc5-cbc "kamekame"
+ -A hmac-md5 "this is the test" ;
+add fec0:0:0:2::2 fec0:0:0:1::1 esp 0x10003
+ -m transport
+ -E cast128-cbc "12341234"
+ -A hmac-sha1 "this is the test key" ;
+add fec0:0:0:2::1 fec0:0:0:1::1 esp 0x10004
+ -E rc5-cbc "kamekame"
+ -A hmac-md5 "this is the test" ;
+
+# By "get" command, you can get a entry of either SP or SA.
+get fec0:0:0:1::1 fec0:0:0:2::2 ah 0x10004 ;
+
+# Also delete command, you can delete a entry of either SP or SA.
+spddelete fec0:0:0:1::/64 fec0:0:0:2::/64 any -P out;
+delete fec0:0:0:1::1 fec0:0:0:2::2 ah 0x10004 ;
+
+# By dump command, you can dump all entry of either SP or SA.
+dump ;
+spddump ;
+dump esp ;
+flush esp ;
+
+# By flush command, you can flush all entry of either SP or SA.
+flush ;
+spdflush ;
+
+# "flush" and "dump" commands can specify a security protocol.
+dump esp ;
+flush ah ;
+
+# XXX
+add ::1 ::1 esp 10001 -m transport -E simple ;
+add ::1 ::1 esp 10002 -m transport -E des-deriv "12341234" ;
+add ::1 ::1 esp-old 10003 -m transport -E des-32iv "12341234" ;
+add ::1 ::1 esp 10004 -m transport -E simple -A null ;
+add ::1 ::1 esp 10005 -m transport -E simple -A hmac-md5 "1234123412341234" ;
+add ::1 ::1 esp 10006 -m tunnel -E simple -A hmac-sha1 "12341234123412341234" ;
+add ::1 ::1 esp 10007 -m transport -E simple -A keyed-md5 "1234123412341234" ;
+add ::1 ::1 esp 10008 -m any -E simple -A keyed-sha1 "12341234123412341234" ;
+add ::1 ::1 esp 10009 -m transport -E des-cbc "testtest" ;
+add ::1 ::1 esp 10010 -m transport -E 3des-cbc "testtest12341234testtest" ;
+add ::1 ::1 esp 10011 -m tunnel -E cast128-cbc "testtest1234" ;
+add ::1 ::1 esp 10012 -m tunnel -E blowfish-cbc "testtest1234" ;
+add ::1 ::1 esp 10013 -m tunnel -E rc5-cbc "testtest1234" ;
+add ::1 ::1 esp 10014 -m any -E rc5-cbc "testtest1234" ;
+add ::1 ::1 esp 10015 -m transport -f zero-pad -E simple ;
+add ::1 ::1 esp 10016 -m tunnel -f random-pad -r 8 -lh 100 -ls 80 -E simple ;
+add ::1 ::1 esp 10017 -m transport -f seq-pad -f nocyclic-seq -E simple ;
+add ::1 ::1 esp 10018 -m transport -E simple ;
+#add ::1 ::1 ah 20000 -m transport -A null ;
+add ::1 ::1 ah 20001 -m any -A hmac-md5 "1234123412341234";
+add ::1 ::1 ah 20002 -m tunnel -A hmac-sha1 "12341234123412341234";
+add ::1 ::1 ah 20003 -m transport -A keyed-md5 "1234123412341234";
+add ::1 ::1 ah-old 20004 -m transport -A keyed-md5 "1234123412341234";
+add ::1 ::1 ah 20005 -m transport -A keyed-sha1 "12341234123412341234";
+#add ::1 ::1 ipcomp 30000 -C oui ;
+add ::1 ::1 ipcomp 30001 -C deflate ;
+#add ::1 ::1 ipcomp 30002 -C lzs ;
+
+# enjoy.
diff --git a/usr.sbin/setkey/scriptdump.pl b/usr.sbin/setkey/scriptdump.pl
new file mode 100644
index 0000000..a1d8adb
--- /dev/null
+++ b/usr.sbin/setkey/scriptdump.pl
@@ -0,0 +1,56 @@
+#! @LOCALPREFIX@/bin/perl
+# $FreeBSD$
+
+if ($< != 0) {
+ print STDERR "must be root to invoke this\n";
+ exit 1;
+}
+
+$mode = 'add';
+while ($i = shift @ARGV) {
+ if ($i eq '-d') {
+ $mode = 'delete';
+ } else {
+ print STDERR "usage: scriptdump [-d]\n";
+ exit 1;
+ }
+}
+
+open(IN, "setkey -D |") || die;
+foreach $_ (<IN>) {
+ if (/^[^\t]/) {
+ ($src, $dst) = split(/\s+/, $_);
+ } elsif (/^\t(esp|ah) mode=(\S+) spi=(\d+).*reqid=(\d+)/) {
+ ($proto, $ipsecmode, $spi, $reqid) = ($1, $2, $3, $4);
+ } elsif (/^\tE: (\S+) (.*)/) {
+ $ealgo = $1;
+ $ekey = $2;
+ $ekey =~ s/\s//g;
+ $ekey =~ s/^/0x/g;
+ } elsif (/^\tA: (\S+) (.*)/) {
+ $aalgo = $1;
+ $akey = $2;
+ $akey =~ s/\s//g;
+ $akey =~ s/^/0x/g;
+ } elsif (/^\tseq=(0x\d+) replay=(\d+) flags=(0x\d+) state=/) {
+ print "$mode $src $dst $proto $spi";
+ $replay = $2;
+ print " -u $reqid" if $reqid;
+ if ($mode eq 'add') {
+ print " -m $ipsecmode -r $replay" if $replay;
+ if ($proto eq 'esp') {
+ print " -E $ealgo $ekey" if $ealgo;
+ print " -A $aalgo $akey" if $aalgo;
+ } elsif ($proto eq 'ah') {
+ print " -A $aalgo $akey" if $aalgo;
+ }
+ }
+ print ";\n";
+
+ $src = $dst = $upper = $proxy = '';
+ $ealgo = $ekey = $aalgo = $akey = '';
+ }
+}
+close(IN);
+
+exit 0;
diff --git a/usr.sbin/setkey/setkey.8 b/usr.sbin/setkey/setkey.8
new file mode 100644
index 0000000..f56e22b
--- /dev/null
+++ b/usr.sbin/setkey/setkey.8
@@ -0,0 +1,629 @@
+.\" $KAME: setkey.8,v 1.49 2001/05/18 05:49:51 sakane Exp $
+.\" $FreeBSD$
+.\"
+.\" Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the project nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd November 20, 2000
+.Dt SETKEY 8
+.Os
+.\"
+.Sh NAME
+.Nm setkey
+.Nd "manually manipulate the IPsec SA/SP database"
+.\"
+.Sh SYNOPSIS
+.Nm
+.Op Fl dv
+.Fl c
+.Nm
+.Op Fl dv
+.Fl f Ar filename
+.Nm
+.Op Fl adPlv
+.Fl D
+.Nm
+.Op Fl dPv
+.Fl F
+.Nm
+.Op Fl h
+.Fl x
+.\"
+.Sh DESCRIPTION
+.Nm
+adds, updates, dumps, or flushes
+Security Association Database (SAD) entries
+as well as Security Policy Database (SPD) entries in the kernel.
+.Pp
+.Nm
+takes a series of operations from the standard input
+(if invoked with
+.Fl c )
+or the file named
+.Ar filename
+(if invoked with
+.Fl f Ar filename ) .
+.Bl -tag -width Ds
+.It Fl D
+Dump the SAD entries.
+If with
+.Fl P ,
+the SPD entries are dumped.
+.It Fl F
+Flush the SAD entries.
+If with
+.Fl P ,
+the SPD entries are flushed.
+.It Fl a
+.Nm
+usually does not display dead SAD entries with
+.Fl D .
+If with
+.Fl a ,
+the dead SAD entries will be displayed as well.
+A dead SAD entry means that
+it has been expired but remains
+because it is referenced by SPD entries.
+.It Fl d
+Enable to print debugging messages for command parser,
+without talking to kernel.
+It is not used usually.
+.It Fl x
+Loop forever and dump all the messages transmitted to
+.Dv PF_KEY
+socket.
+.Fl xx
+makes each timestamps unformatted.
+.It Fl h
+Add hexadecimal dump on
+.Fl x
+mode.
+.It Fl l
+Loop forever with short output on
+.Fl D .
+.It Fl v
+Be verbose.
+The program will dump messages exchanged on
+.Dv PF_KEY
+socket, including messages sent from other processes to the kernel.
+.El
+.Pp
+Operations have the following grammar.
+Note that lines starting with
+hashmarks ('#') are treated as comment lines.
+.Bl -tag -width Ds
+.It Xo
+.Li add
+.Ar src Ar dst Ar protocol Ar spi
+.Op Ar extensions
+.Ar algorithm...
+.Li ;
+.Xc
+Add an SAD entry.
+.\"
+.It Xo
+.Li get
+.Ar src Ar dst Ar protocol Ar spi
+.Li ;
+.Xc
+Show an SAD entry.
+.\"
+.It Xo
+.Li delete
+.Ar src Ar dst Ar protocol Ar spi
+.Li ;
+.Xc
+Remove an SAD entry.
+.\"
+.It Xo
+.Li deleteall
+.Ar src Ar dst Ar protocol
+.Li ;
+.Xc
+Remove all SAD entries that match the specification.
+.\"
+.It Xo
+.Li flush
+.Op Ar protocol
+.Li ;
+.Xc
+Clear all SAD entries matched by the options.
+.\"
+.It Xo
+.Li dump
+.Op Ar protocol
+.Li ;
+.Xc
+Dumps all SAD entries matched by the options.
+.\"
+.It Xo
+.Li spdadd
+.Ar src_range Ar dst_range Ar upperspec Ar policy
+.Li ;
+.Xc
+Add an SPD entry.
+.\"
+.It Xo
+.Li spddelete
+.Ar src_range Ar dst_range Ar upperspec Fl P Ar direction
+.Li ;
+.Xc
+Delete an SPD entry.
+.\"
+.It Xo
+.Li spdflush
+.Li ;
+.Xc
+Clear all SPD entries.
+.\"
+.It Xo
+.Li spddump
+.Li ;
+.Xc
+Dumps all SPD entries.
+.El
+.\"
+.Pp
+Meta-arguments are as follows:
+.Pp
+.Bl -tag -compact -width Ds
+.It Ar src
+.It Ar dst
+Source/destination of the secure communication is specified as
+IPv4/v6 address.
+.Nm
+does not consult hostname-to-address for arguments
+.Ar src
+and
+.Ar dst .
+They must be in numeric form.
+.\"
+.Pp
+.It Ar protocol
+.Ar protocol
+is one of following:
+.Bl -tag -width Fl -compact
+.It Li esp
+ESP based on rfc2405
+.It Li esp-old
+ESP based on rfc1827
+.It Li ah
+AH based on rfc2402
+.It Li ah-old
+AH based on rfc1826
+.It Li ipcomp
+IPCOMP
+.El
+.\"
+.Pp
+.It Ar spi
+Security Parameter Index (SPI) for the SAD and the SPD.
+It must be decimal number or hexadecimal number
+You cannot use the set of SPI values in the range 0 through 255.
+(with
+.Li 0x
+attached).
+.\"
+.Pp
+.It Ar extensions
+takes some of the following:
+.Bl -tag -width Fl -compact
+.\"
+.It Fl m Ar mode
+Specify a security protocol mode for use.
+.Ar mode
+is one of following:
+.Li transport , tunnel
+or
+.Li any .
+The default value is
+.Li any .
+.\"
+.It Fl r Ar size
+Specify window size of bytes for replay prevention.
+.Ar size
+must be decimal number in 32-bit word.
+If
+.Ar size
+is zero or not specified, replay check don't take place.
+.\"
+.It Fl u Ar id
+Specify the identifier of the policy entry in SPD.
+See
+.Ar policy .
+.\"
+.It Fl f Ar pad_option
+defines the content of the ESP padding.
+.Ar pad_option
+is one of following:
+.Bl -tag -width random-pad -compact
+.It Li zero-pad
+All of the padding are zero.
+.It Li random-pad
+A series of randomized values are set.
+.It Li seq-pad
+A series of sequential increasing numbers started from 1 are set.
+.El
+.\"
+.It Fl f Li nocyclic-seq
+Don't allow cyclic sequence number.
+.\"
+.It Fl lh Ar time
+.It Fl ls Ar time
+Specify hard/soft life time duration of the SA.
+.El
+.\"
+.Pp
+.It Ar algorithm
+.Bl -tag -width Fl -compact
+.It Fl E Ar ealgo Ar key
+Specify a encryption algorithm.
+.It Fl A Ar aalgo Ar key
+Specify a authentication algorithm.
+If
+.Fl A
+is used with
+.Ar protocol Li esp ,
+it will be treated as ESP payload authentication algorithm.
+.It Fl C Ar calgo Op Fl R
+Specify compression algorithm.
+If
+.Fl R
+is not specified with
+.Li ipcomp
+line, the kernel will use well-known IPComp CPI
+(compression parameter index)
+on IPComp CPI field on packets, and
+.Ar spi
+field will be ignored.
+.Ar spi
+field is only for kernel internal use in this case.
+.\"Therefore, compression protocol number will appear on IPComp CPI field.
+If
+.Fl R
+is used,
+the value on
+.Ar spi
+field will appear on IPComp CPI field on outgoing packets.
+.Ar spi
+field needs to be smaller than
+.Li 0x10000
+in this case.
+.El
+.Pp
+.Ar protocol Li esp
+accepts
+.Fl E
+and
+.Fl A .
+.Ar protocol Li esp-old
+accepts
+.Fl E
+only.
+.Ar protocol Li ah
+and
+.Li ah-old
+accept
+.Fl A
+only.
+.Ar protocol Li ipcomp
+accepts
+.Fl C
+only.
+.Pp
+.Ar key
+must be double-quoted character string or series of hexadecimal digits.
+.Pp
+Possible values for
+.Ar ealgo ,
+.Ar aalgo
+and
+.Ar calgo
+are specified in separate section.
+.\"
+.Pp
+.It Ar src_range
+.It Ar dst_range
+These are selections of the secure communication specified as
+IPv4/v6 address or IPv4/v6 address range, and it may accompany
+TCP/UDP port specification.
+This takes the following form:
+.Bd -literal -offset
+.Ar address
+.Ar address/prefixlen
+.Ar address[port]
+.Ar address/prefixlen[port]
+.Ed
+.Pp
+.Ar prefixlen
+and
+.Ar port
+must be decimal number.
+The square bracket around
+.Ar port
+is really necessary.
+They are not manpage metacharacters.
+.Pp
+.Nm
+does not consult hostname-to-address for arguments
+.Ar src
+and
+.Ar dst .
+They must be in numeric form.
+.\"
+.Pp
+.It Ar upperspec
+Upper-layer protocol to be used.
+You can use one of words in
+.Pa /etc/protocols
+as
+.Ar upperspec .
+Or
+.Li icmp6 ,
+.Li ip4 ,
+and
+.Li any
+can be specified.
+.Li any
+stands for
+.Dq any protocol .
+Also you can use the protocol number.
+.Pp
+NOTE:
+.Ar upperspec
+does not work against forwarding case at this moment,
+as it requires extra reassembly at forwarding node
+(not implemented at this moment).
+We have many protocols in
+.Pa /etc/protocols ,
+but protocols except of TCP, UDP and ICMP may not be suitable to use with IPSec.
+You have to consider and be careful to use them.
+.Li icmp
+.Li tcp
+.Li udp
+all protocols
+.\"
+.Pp
+.It Ar policy
+.Ar policy
+is the one of following:
+.Bd -literal -offset
+.Xo
+.Fl P
+.Ar direction
+.Li discard
+.Xc
+.Xo
+.Fl P
+.Ar direction
+.Li none
+.Xc
+.Xo
+.Fl P
+.Ar direction
+.Li ipsec
+.Ar protocol/mode/src-dst/level
+.Xc
+.Ed
+.Pp
+You must specify the direction of its policy as
+.Ar direction .
+Either
+.Li out
+or
+.Li in
+are used.
+.Li discard
+means the packet matching indexes will be discarded.
+.Li none
+means that IPsec operation will not take place onto the packet.
+.Li ipsec
+means that IPsec operation will take place onto the packet.
+Either
+.Li ah ,
+.Li esp
+or
+.Li ipcomp
+is to be set as
+.Ar protocol .
+.Ar mode
+is either
+.Li transport
+or
+.Li tunnel .
+If
+.Ar mode
+is
+.Li tunnel ,
+you must specify the end-points addresses of the SA as
+.Ar src
+and
+.Ar dst
+with
+.Sq -
+between these addresses which is used to specify the SA to use.
+If
+.Ar mode
+is
+.Li transport ,
+both
+.Ar src
+and
+.Ar dst
+can be omited.
+.Ar level
+is to be one of the following:
+.Li default , use , require
+or
+.Li unique .
+If the SA is not available in every level, the kernel will request
+getting SA to the key exchange daemon.
+.Li default
+means the kernel consults to the system wide default against protocol you
+specified, e.g.\&
+.Li esp_trans_deflev
+sysctl variable, when the kernel processes the packet.
+.Li use
+means that the kernel use a SA if it's available,
+otherwise the kernel keeps normal operation.
+.Li require
+means SA is required whenever the kernel sends a packet matched
+with the policy.
+.Li unique
+is the same to require.
+In addition, it allows the policy to bind with the unique out-bound SA.
+If you use the SA by manual keying,
+you can put the decimal number as the policy identifier after
+.Li unique
+separated by colon
+.Sq \:
+like the following;
+.Li unique:number .
+.Li number
+must be between 1 and 32767.
+It corresponds to
+.Ar extensions Fl u .
+.Pp
+Note that
+.Dq Li discard
+and
+.Dq Li none
+are not in the syntax described in
+.Xr ipsec_set_policy 3 .
+There are little differences in the syntax.
+See
+.Xr ipsec_set_policy 3
+for detail.
+.Pp
+.El
+.Pp
+.\"
+.Sh ALGORITHMS
+The following list shows the supported algorithms.
+.Sy protocol
+and
+.Sy algorithm
+are almost orthogonal.
+Followings are the list of authentication algorithms that can be used as
+.Ar aalgo
+in
+.Fl A Ar aalgo
+of
+.Ar protocol
+parameter:
+.Pp
+.Bd -literal -offset indent
+algorithm keylen (bits) comment
+hmac-md5 128 ah: rfc2403
+ 128 ah-old: rfc2085
+hmac-sha1 160 ah: rfc2404
+ 160 ah-old: 128bit ICV (no document)
+keyed-md5 128 ah: 96bit ICV (no document)
+ 128 ah-old: rfc1828
+keyed-sha1 160 ah: 96bit ICV (no document)
+ 160 ah-old: 128bit ICV (no document)
+null 0 to 2048 for debugging
+hmac-sha2-256 256 ah: 96bit ICV (no document)
+ 256 ah-old: 128bit ICV (no document)
+hmac-sha2-384 384 ah: 96bit ICV (no document)
+ 384 ah-old: 128bit ICV (no document)
+hmac-sha2-512 512 ah: 96bit ICV (no document)
+ 512 ah-old: 128bit ICV (no document)
+.Ed
+.Pp
+Followings are the list of encryption algorithms that can be used as
+.Ar ealgo
+in
+.Fl E Ar ealgo
+of
+.Ar protocol
+parameter:
+.Pp
+.Bd -literal -offset indent
+algorithm keylen (bits) comment
+des-cbc 64 esp-old: rfc1829, esp: rfc2405
+3des-cbc 192 rfc2451
+simple 0 to 2048 rfc2410
+blowfish-cbc 40 to 448 rfc2451
+cast128-cbc 40 to 128 rfc2451
+des-deriv 64 ipsec-ciph-des-derived-01 (expired)
+3des-deriv 192 no document
+rijndael-cbc 128/192/256 draft-ietf-ipsec-ciph-aes-cbc-00
+.Ed
+.Pp
+Followings are the list of compression algorithms that can be used as
+.Ar calgo
+in
+.Fl C Ar calgo
+of
+.Ar protocol
+parameter:
+.Pp
+.Bd -literal -offset indent
+algorithm comment
+deflate rfc2394
+lzs rfc2395
+.Ed
+.\"
+.Sh EXAMPLES
+.Bd -literal -offset
+add 3ffe:501:4819::1 3ffe:501:481d::1 esp 123457
+ -E des-cbc "ESP SA!!" ;
+
+add 3ffe:501:4819::1 3ffe:501:481d::1 ah 123456
+ -A hmac-sha1 "AH SA configuration!" ;
+
+add 10.0.11.41 10.0.11.33 esp 0x10001
+ -E des-cbc "ESP with"
+ -A hmac-md5 "authentication!!" ;
+
+get 3ffe:501:4819::1 3ffe:501:481d::1 ah 123456 ;
+
+flush ;
+
+dump esp ;
+
+spdadd 10.0.11.41/32[21] 10.0.11.33/32[any] any
+ -P out ipsec esp/tunnel/192.168.0.1-192.168.1.2/require ;
+
+.Ed
+.\"
+.Sh RETURN VALUES
+The command exits with 0 on success, and non-zero on errors.
+.\"
+.Sh SEE ALSO
+.Xr ipsec_set_policy 3 ,
+.Xr racoon 8 ,
+.Xr sysctl 8
+.\"
+.Sh HISTORY
+The
+.Nm
+command first appeared in WIDE Hydrangea IPv6 protocol stack kit.
+The command was completely re-designed in June 1998.
+.\"
+.\" .Sh BUGS
diff --git a/usr.sbin/setkey/setkey.c b/usr.sbin/setkey/setkey.c
new file mode 100644
index 0000000..e729e7d
--- /dev/null
+++ b/usr.sbin/setkey/setkey.c
@@ -0,0 +1,648 @@
+/* $FreeBSD$ */
+/* $KAME: setkey.c,v 1.18 2001/05/08 04:36:39 itojun Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <err.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <net/pfkeyv2.h>
+#include <netkey/keydb.h>
+#include <netkey/key_debug.h>
+#include <netinet6/ipsec.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <errno.h>
+#include <netdb.h>
+
+#include "libpfkey.h"
+
+void Usage __P((void));
+int main __P((int, char **));
+int get_supported __P((void));
+void sendkeyshort __P((u_int));
+void promisc __P((void));
+int sendkeymsg __P((void));
+int postproc __P((struct sadb_msg *, int));
+const char *numstr __P((int));
+void shortdump_hdr __P((void));
+void shortdump __P((struct sadb_msg *));
+static void printdate __P((void));
+static int32_t gmt2local __P((time_t));
+
+#define MODE_SCRIPT 1
+#define MODE_CMDDUMP 2
+#define MODE_CMDFLUSH 3
+#define MODE_PROMISC 4
+
+int so;
+
+int f_forever = 0;
+int f_all = 0;
+int f_debug = 0;
+int f_verbose = 0;
+int f_mode = 0;
+int f_cmddump = 0;
+int f_policy = 0;
+int f_hexdump = 0;
+int f_tflag = 0;
+char *pname;
+
+u_char m_buf[BUFSIZ];
+u_int m_len;
+
+static time_t thiszone;
+
+extern int lineno;
+
+extern int parse __P((FILE **));
+
+void
+Usage()
+{
+ printf("Usage:\t%s [-dv] -c\n", pname);
+ printf("\t%s [-dv] -f (file)\n", pname);
+ printf("\t%s [-Padlv] -D\n", pname);
+ printf("\t%s [-Pdv] -F\n", pname);
+ printf("\t%s [-h] -x\n", pname);
+ pfkey_close(so);
+ exit(1);
+}
+
+int
+main(ac, av)
+ int ac;
+ char **av;
+{
+ FILE *fp = stdin;
+ int c;
+
+ pname = *av;
+
+ if (ac == 1) Usage();
+
+ thiszone = gmt2local(0);
+
+ while ((c = getopt(ac, av, "acdf:hlvxDFP")) != -1) {
+ switch (c) {
+ case 'c':
+ f_mode = MODE_SCRIPT;
+ fp = stdin;
+ break;
+ case 'f':
+ f_mode = MODE_SCRIPT;
+ if ((fp = fopen(optarg, "r")) == NULL) {
+ err(-1, "fopen");
+ /*NOTREACHED*/
+ }
+ break;
+ case 'D':
+ f_mode = MODE_CMDDUMP;
+ break;
+ case 'F':
+ f_mode = MODE_CMDFLUSH;
+ break;
+ case 'a':
+ f_all = 1;
+ break;
+ case 'l':
+ f_forever = 1;
+ break;
+ case 'h':
+ f_hexdump = 1;
+ break;
+ case 'x':
+ f_mode = MODE_PROMISC;
+ f_tflag++;
+ break;
+ case 'P':
+ f_policy = 1;
+ break;
+ case 'd':
+ f_debug = 1;
+ break;
+ case 'v':
+ f_verbose = 1;
+ break;
+ default:
+ Usage();
+ /*NOTREACHED*/
+ }
+ }
+
+ switch (f_mode) {
+ case MODE_CMDDUMP:
+ sendkeyshort(f_policy ? SADB_X_SPDDUMP: SADB_DUMP);
+ break;
+ case MODE_CMDFLUSH:
+ sendkeyshort(f_policy ? SADB_X_SPDFLUSH: SADB_FLUSH);
+ pfkey_close(so);
+ break;
+ case MODE_SCRIPT:
+ if (get_supported() < 0) {
+ errx(-1, "%s", ipsec_strerror());
+ /*NOTREACHED*/
+ }
+ if (parse(&fp))
+ exit (1);
+ break;
+ case MODE_PROMISC:
+ promisc();
+ /*NOTREACHED*/
+ default:
+ Usage();
+ /*NOTREACHED*/
+ }
+
+ exit(0);
+}
+
+int
+get_supported()
+{
+ int so;
+
+ if ((so = pfkey_open()) < 0) {
+ perror("pfkey_open");
+ return -1;
+ }
+
+ /* debug mode ? */
+ if (f_debug)
+ return 0;
+
+ if (pfkey_send_register(so, SADB_SATYPE_UNSPEC) < 0)
+ return -1;
+
+ if (pfkey_recv_register(so) < 0)
+ return -1;
+
+ return 0;
+}
+
+void
+sendkeyshort(type)
+ u_int type;
+{
+ struct sadb_msg *m_msg = (struct sadb_msg *)m_buf;
+
+ m_len = sizeof(struct sadb_msg);
+
+ m_msg->sadb_msg_version = PF_KEY_V2;
+ m_msg->sadb_msg_type = type;
+ m_msg->sadb_msg_errno = 0;
+ m_msg->sadb_msg_satype = SADB_SATYPE_UNSPEC;
+ m_msg->sadb_msg_len = PFKEY_UNIT64(m_len);
+ m_msg->sadb_msg_reserved = 0;
+ m_msg->sadb_msg_seq = 0;
+ m_msg->sadb_msg_pid = getpid();
+
+ sendkeymsg();
+
+ return;
+}
+
+void
+promisc()
+{
+ struct sadb_msg *m_msg = (struct sadb_msg *)m_buf;
+ u_char rbuf[1024 * 32]; /* XXX: Enough ? Should I do MSG_PEEK ? */
+ int so, len;
+
+ m_len = sizeof(struct sadb_msg);
+
+ m_msg->sadb_msg_version = PF_KEY_V2;
+ m_msg->sadb_msg_type = SADB_X_PROMISC;
+ m_msg->sadb_msg_errno = 0;
+ m_msg->sadb_msg_satype = 1;
+ m_msg->sadb_msg_len = PFKEY_UNIT64(m_len);
+ m_msg->sadb_msg_reserved = 0;
+ m_msg->sadb_msg_seq = 0;
+ m_msg->sadb_msg_pid = getpid();
+
+ if ((so = socket(PF_KEY, SOCK_RAW, PF_KEY_V2)) < 0) {
+ err(1, "socket(PF_KEY)");
+ /*NOTREACHED*/
+ }
+
+ if ((len = send(so, m_buf, m_len, 0)) < 0) {
+ err(1, "send");
+ /*NOTREACHED*/
+ }
+
+ while (1) {
+ struct sadb_msg *base;
+
+ if ((len = recv(so, rbuf, sizeof(*base), MSG_PEEK)) < 0) {
+ err(1, "recv");
+ /*NOTREACHED*/
+ }
+
+ if (len != sizeof(*base))
+ continue;
+
+ base = (struct sadb_msg *)rbuf;
+ if ((len = recv(so, rbuf, PFKEY_UNUNIT64(base->sadb_msg_len),
+ 0)) < 0) {
+ err(1, "recv");
+ /*NOTREACHED*/
+ }
+ printdate();
+ if (f_hexdump) {
+ int i;
+ for (i = 0; i < len; i++) {
+ if (i % 16 == 0)
+ printf("%08x: ", i);
+ printf("%02x ", rbuf[i] & 0xff);
+ if (i % 16 == 15)
+ printf("\n");
+ }
+ if (len % 16)
+ printf("\n");
+ }
+ /* adjust base pointer for promisc mode */
+ if (base->sadb_msg_type == SADB_X_PROMISC) {
+ if (sizeof(*base) < len)
+ base++;
+ else
+ base = NULL;
+ }
+ if (base) {
+ kdebug_sadb(base);
+ printf("\n");
+ fflush(stdout);
+ }
+ }
+}
+
+int
+sendkeymsg()
+{
+ int so;
+
+ u_char rbuf[1024 * 32]; /* XXX: Enough ? Should I do MSG_PEEK ? */
+ int len;
+ struct sadb_msg *msg;
+
+ if ((so = pfkey_open()) < 0) {
+ perror("pfkey_open");
+ return -1;
+ }
+
+ {
+ struct timeval tv;
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ if (setsockopt(so, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
+ perror("setsockopt");
+ goto end;
+ }
+ }
+
+ if (f_forever)
+ shortdump_hdr();
+again:
+ if (f_verbose) {
+ kdebug_sadb((struct sadb_msg *)m_buf);
+ printf("\n");
+ }
+
+ if ((len = send(so, m_buf, m_len, 0)) < 0) {
+ perror("send");
+ goto end;
+ }
+
+ msg = (struct sadb_msg *)rbuf;
+ do {
+ if ((len = recv(so, rbuf, sizeof(rbuf), 0)) < 0) {
+ perror("recv");
+ goto end;
+ }
+
+ if (PFKEY_UNUNIT64(msg->sadb_msg_len) != len) {
+ warnx("invalid keymsg length");
+ break;
+ }
+
+ if (f_verbose) {
+ kdebug_sadb((struct sadb_msg *)rbuf);
+ printf("\n");
+ }
+ if (postproc(msg, len) < 0)
+ break;
+ } while (msg->sadb_msg_errno || msg->sadb_msg_seq);
+
+ if (f_forever) {
+ fflush(stdout);
+ sleep(1);
+ goto again;
+ }
+
+end:
+ pfkey_close(so);
+ return(0);
+}
+
+int
+postproc(msg, len)
+ struct sadb_msg *msg;
+ int len;
+{
+
+ if (msg->sadb_msg_errno != 0) {
+ char inf[80];
+ char *errmsg = NULL;
+
+ if (f_mode == MODE_SCRIPT)
+ snprintf(inf, sizeof(inf), "The result of line %d: ", lineno);
+ else
+ inf[0] = '\0';
+
+ switch (msg->sadb_msg_errno) {
+ case ENOENT:
+ switch (msg->sadb_msg_type) {
+ case SADB_DELETE:
+ case SADB_GET:
+ case SADB_X_SPDDELETE:
+ errmsg = "No entry";
+ break;
+ case SADB_DUMP:
+ errmsg = "No SAD entries";
+ break;
+ case SADB_X_SPDDUMP:
+ errmsg = "No SPD entries";
+ break;
+ }
+ break;
+ default:
+ errmsg = strerror(msg->sadb_msg_errno);
+ }
+ printf("%s%s.\n", inf, errmsg);
+ return(-1);
+ }
+
+ switch (msg->sadb_msg_type) {
+ case SADB_GET:
+ pfkey_sadump(msg);
+ break;
+
+ case SADB_DUMP:
+ /* filter out DEAD SAs */
+ if (!f_all) {
+ caddr_t mhp[SADB_EXT_MAX + 1];
+ struct sadb_sa *sa;
+ pfkey_align(msg, mhp);
+ pfkey_check(mhp);
+ if ((sa = (struct sadb_sa *)mhp[SADB_EXT_SA]) != NULL) {
+ if (sa->sadb_sa_state == SADB_SASTATE_DEAD)
+ break;
+ }
+ }
+ if (f_forever)
+ shortdump(msg);
+ else
+ pfkey_sadump(msg);
+ msg = (struct sadb_msg *)((caddr_t)msg +
+ PFKEY_UNUNIT64(msg->sadb_msg_len));
+ if (f_verbose) {
+ kdebug_sadb((struct sadb_msg *)msg);
+ printf("\n");
+ }
+ break;
+
+ case SADB_X_SPDDUMP:
+ pfkey_spdump(msg);
+ if (msg->sadb_msg_seq == 0) break;
+ msg = (struct sadb_msg *)((caddr_t)msg +
+ PFKEY_UNUNIT64(msg->sadb_msg_len));
+ if (f_verbose) {
+ kdebug_sadb((struct sadb_msg *)msg);
+ printf("\n");
+ }
+ break;
+ }
+
+ return(0);
+}
+
+/*------------------------------------------------------------*/
+static char *satype[] = {
+ NULL, NULL, "ah", "esp"
+};
+static char *sastate[] = {
+ "L", "M", "D", "d"
+};
+static char *ipproto[] = {
+/*0*/ "ip", "icmp", "igmp", "ggp", "ip4",
+ NULL, "tcp", NULL, "egp", NULL,
+/*10*/ NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, "udp", NULL, NULL,
+/*20*/ NULL, NULL, "idp", NULL, NULL,
+ NULL, NULL, NULL, NULL, "tp",
+/*30*/ NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+/*40*/ NULL, "ip6", NULL, "rt6", "frag6",
+ NULL, "rsvp", "gre", NULL, NULL,
+/*50*/ "esp", "ah", NULL, NULL, NULL,
+ NULL, NULL, NULL, "icmp6", "none",
+/*60*/ "dst6",
+};
+
+#define STR_OR_ID(x, tab) \
+ (((x) < sizeof(tab)/sizeof(tab[0]) && tab[(x)]) ? tab[(x)] : numstr(x))
+
+const char *
+numstr(x)
+ int x;
+{
+ static char buf[20];
+ snprintf(buf, sizeof(buf), "#%d", x);
+ return buf;
+}
+
+void
+shortdump_hdr()
+{
+ printf("%-4s %-3s %-1s %-8s %-7s %s -> %s\n",
+ "time", "p", "s", "spi", "ltime", "src", "dst");
+}
+
+void
+shortdump(msg)
+ struct sadb_msg *msg;
+{
+ caddr_t mhp[SADB_EXT_MAX + 1];
+ char buf[1024], pbuf[10];
+ struct sadb_sa *sa;
+ struct sadb_address *saddr;
+ struct sadb_lifetime *lts, *lth, *ltc;
+ struct sockaddr *s;
+ u_int t;
+ time_t cur = time(0);
+
+ pfkey_align(msg, mhp);
+ pfkey_check(mhp);
+
+ printf("%02lu%02lu", (u_long)(cur % 3600) / 60, (u_long)(cur % 60));
+
+ printf(" %-3s", STR_OR_ID(msg->sadb_msg_satype, satype));
+
+ if ((sa = (struct sadb_sa *)mhp[SADB_EXT_SA]) != NULL) {
+ printf(" %-1s", STR_OR_ID(sa->sadb_sa_state, sastate));
+ printf(" %08x", (u_int32_t)ntohl(sa->sadb_sa_spi));
+ } else
+ printf("%-1s %-8s", "?", "?");
+
+ lts = (struct sadb_lifetime *)mhp[SADB_EXT_LIFETIME_SOFT];
+ lth = (struct sadb_lifetime *)mhp[SADB_EXT_LIFETIME_HARD];
+ ltc = (struct sadb_lifetime *)mhp[SADB_EXT_LIFETIME_CURRENT];
+ if (lts && lth && ltc) {
+ if (ltc->sadb_lifetime_addtime == 0)
+ t = (u_long)0;
+ else
+ t = (u_long)(cur - ltc->sadb_lifetime_addtime);
+ if (t >= 1000)
+ strcpy(buf, " big/");
+ else
+ snprintf(buf, sizeof(buf), " %3lu/", (u_long)t);
+ printf("%s", buf);
+
+ t = (u_long)lth->sadb_lifetime_addtime;
+ if (t >= 1000)
+ strcpy(buf, "big");
+ else
+ snprintf(buf, sizeof(buf), "%-3lu", (u_long)t);
+ printf("%s", buf);
+ } else
+ printf(" ??\?/???"); /* backslash to avoid trigraph ??/ */
+
+ printf(" ");
+
+ if ((saddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_SRC]) != NULL) {
+ if (saddr->sadb_address_proto)
+ printf("%s ", STR_OR_ID(saddr->sadb_address_proto, ipproto));
+ s = (struct sockaddr *)(saddr + 1);
+ getnameinfo(s, s->sa_len, buf, sizeof(buf),
+ pbuf, sizeof(pbuf), NI_NUMERICHOST|NI_NUMERICSERV);
+ if (strcmp(pbuf, "0") != 0)
+ printf("%s[%s]", buf, pbuf);
+ else
+ printf("%s", buf);
+ } else
+ printf("?");
+
+ printf(" -> ");
+
+ if ((saddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_DST]) != NULL) {
+ if (saddr->sadb_address_proto)
+ printf("%s ", STR_OR_ID(saddr->sadb_address_proto, ipproto));
+
+ s = (struct sockaddr *)(saddr + 1);
+ getnameinfo(s, s->sa_len, buf, sizeof(buf),
+ pbuf, sizeof(pbuf), NI_NUMERICHOST|NI_NUMERICSERV);
+ if (strcmp(pbuf, "0") != 0)
+ printf("%s[%s]", buf, pbuf);
+ else
+ printf("%s", buf);
+ } else
+ printf("?");
+
+ printf("\n");
+}
+
+/* From: tcpdump(1):gmt2local.c and util.c */
+/*
+ * Print the timestamp
+ */
+static void
+printdate()
+{
+ struct timeval tp;
+ int s;
+
+ if (gettimeofday(&tp, NULL) == -1) {
+ perror("gettimeofday");
+ return;
+ }
+
+ if (f_tflag == 1) {
+ /* Default */
+ s = (tp.tv_sec + thiszone ) % 86400;
+ (void)printf("%02d:%02d:%02d.%06u ",
+ s / 3600, (s % 3600) / 60, s % 60, (u_int32_t)tp.tv_usec);
+ } else if (f_tflag > 1) {
+ /* Unix timeval style */
+ (void)printf("%u.%06u ",
+ (u_int32_t)tp.tv_sec, (u_int32_t)tp.tv_usec);
+ }
+
+ printf("\n");
+}
+
+/*
+ * Returns the difference between gmt and local time in seconds.
+ * Use gmtime() and localtime() to keep things simple.
+ */
+int32_t
+gmt2local(time_t t)
+{
+ register int dt, dir;
+ register struct tm *gmt, *loc;
+ struct tm sgmt;
+
+ if (t == 0)
+ t = time(NULL);
+ gmt = &sgmt;
+ *gmt = *gmtime(&t);
+ loc = localtime(&t);
+ dt = (loc->tm_hour - gmt->tm_hour) * 60 * 60 +
+ (loc->tm_min - gmt->tm_min) * 60;
+
+ /*
+ * If the year or julian day is different, we span 00:00 GMT
+ * and must add or subtract a day. Check the year first to
+ * avoid problems when the julian day wraps.
+ */
+ dir = loc->tm_year - gmt->tm_year;
+ if (dir == 0)
+ dir = loc->tm_yday - gmt->tm_yday;
+ dt += dir * 24 * 60 * 60;
+
+ return (dt);
+}
diff --git a/usr.sbin/setkey/test-pfkey.c b/usr.sbin/setkey/test-pfkey.c
new file mode 100644
index 0000000..b1fb238
--- /dev/null
+++ b/usr.sbin/setkey/test-pfkey.c
@@ -0,0 +1,531 @@
+/* $FreeBSD$ */
+/* $KAME: test-pfkey.c,v 1.4 2000/06/07 00:29:14 itojun Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/route.h>
+#include <net/pfkeyv2.h>
+#include <netinet/in.h>
+#include <netkey/keydb.h>
+#include <netkey/key_var.h>
+#include <netkey/key_debug.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <errno.h>
+#include <netdb.h>
+
+u_char m_buf[BUFSIZ];
+u_int m_len;
+char *pname;
+
+void Usage __P((void));
+int sendkeymsg __P((void));
+void key_setsadbmsg __P((u_int));
+void key_setsadbsens __P((void));
+void key_setsadbprop __P((void));
+void key_setsadbid __P((u_int, caddr_t));
+void key_setsadblft __P((u_int, u_int));
+void key_setspirange __P((void));
+void key_setsadbkey __P((u_int, caddr_t));
+void key_setsadbsa __P((void));
+void key_setsadbaddr __P((u_int, u_int, caddr_t));
+void key_setsadbextbuf __P((caddr_t, int, caddr_t, int, caddr_t, int));
+
+void
+Usage()
+{
+ printf("Usage:\t%s number\n", pname);
+ exit(0);
+}
+
+int
+main(ac, av)
+ int ac;
+ char **av;
+{
+ pname = *av;
+
+ if (ac == 1) Usage();
+
+ key_setsadbmsg(atoi(*(av+1)));
+ sendkeymsg();
+
+ exit(0);
+}
+
+/* %%% */
+int
+sendkeymsg()
+{
+ u_char rbuf[1024 * 32]; /* XXX: Enough ? Should I do MSG_PEEK ? */
+ int so, len;
+
+ if ((so = socket(PF_KEY, SOCK_RAW, PF_KEY_V2)) < 0) {
+ perror("socket(PF_KEY)");
+ goto end;
+ }
+#if 0
+ {
+#include <sys/time.h>
+ struct timeval tv;
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ if (setsockopt(so, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
+ perror("setsockopt");
+ goto end;
+ }
+ }
+#endif
+
+ pfkey_sadump((struct sadb_msg *)m_buf);
+
+ if ((len = send(so, m_buf, m_len, 0)) < 0) {
+ perror("send");
+ goto end;
+ }
+
+ if ((len = recv(so, rbuf, sizeof(rbuf), 0)) < 0) {
+ perror("recv");
+ goto end;
+ }
+
+ pfkey_sadump((struct sadb_msg *)rbuf);
+
+end:
+ (void)close(so);
+ return(0);
+}
+
+void
+key_setsadbmsg(type)
+ u_int type;
+{
+ struct sadb_msg m_msg;
+
+ memset(&m_msg, 0, sizeof(m_msg));
+ m_msg.sadb_msg_version = PF_KEY_V2;
+ m_msg.sadb_msg_type = type;
+ m_msg.sadb_msg_errno = 0;
+ m_msg.sadb_msg_satype = SADB_SATYPE_ESP;
+#if 0
+ m_msg.sadb_msg_reserved = 0;
+#endif
+ m_msg.sadb_msg_seq = 0;
+ m_msg.sadb_msg_pid = getpid();
+
+ m_len = sizeof(struct sadb_msg);
+ memcpy(m_buf, &m_msg, m_len);
+
+ switch (type) {
+ case SADB_GETSPI:
+ /*<base, address(SD), SPI range>*/
+ key_setsadbaddr(SADB_EXT_ADDRESS_SRC, AF_INET, "10.0.3.4");
+ key_setsadbaddr(SADB_EXT_ADDRESS_DST, AF_INET, "127.0.0.1");
+ key_setspirange();
+ /*<base, SA(*), address(SD)>*/
+ break;
+
+ case SADB_ADD:
+ /* <base, SA, (lifetime(HSC),) address(SD), (address(P),)
+ key(AE), (identity(SD),) (sensitivity)> */
+ key_setsadbaddr(SADB_EXT_ADDRESS_PROXY, AF_INET6, "3ffe::1");
+ case SADB_UPDATE:
+ key_setsadbsa();
+ key_setsadblft(SADB_EXT_LIFETIME_HARD, 10);
+ key_setsadblft(SADB_EXT_LIFETIME_SOFT, 5);
+ key_setsadbaddr(SADB_EXT_ADDRESS_SRC, AF_INET, "192.168.1.1");
+ key_setsadbaddr(SADB_EXT_ADDRESS_DST, AF_INET, "10.0.3.4");
+ /* XXX key_setsadbkey(SADB_EXT_KEY_AUTH, "abcde"); */
+ key_setsadbkey(SADB_EXT_KEY_AUTH, "1234567812345678");
+ key_setsadbkey(SADB_EXT_KEY_ENCRYPT, "12345678");
+ key_setsadbid(SADB_EXT_IDENTITY_SRC, "hoge1234@hoge.com");
+ key_setsadbid(SADB_EXT_IDENTITY_DST, "hage5678@hage.net");
+ key_setsadbsens();
+ /* <base, SA, (lifetime(HSC),) address(SD), (address(P),)
+ (identity(SD),) (sensitivity)> */
+ break;
+
+ case SADB_DELETE:
+ /* <base, SA(*), address(SDP)> */
+ key_setsadbsa();
+ key_setsadbaddr(SADB_EXT_ADDRESS_SRC, AF_INET, "192.168.1.1");
+ key_setsadbaddr(SADB_EXT_ADDRESS_DST, AF_INET, "10.0.3.4");
+ key_setsadbaddr(SADB_EXT_ADDRESS_PROXY, AF_INET6, "3ffe::1");
+ /* <base, SA(*), address(SDP)> */
+ break;
+
+ case SADB_GET:
+ /* <base, SA(*), address(SDP)> */
+ key_setsadbsa();
+ key_setsadbaddr(SADB_EXT_ADDRESS_SRC, AF_INET, "192.168.1.1");
+ key_setsadbaddr(SADB_EXT_ADDRESS_DST, AF_INET, "10.0.3.4");
+ key_setsadbaddr(SADB_EXT_ADDRESS_PROXY, AF_INET6, "3ffe::1");
+ /* <base, SA, (lifetime(HSC),) address(SD), (address(P),)
+ key(AE), (identity(SD),) (sensitivity)> */
+ break;
+
+ case SADB_ACQUIRE:
+ /* <base, address(SD), (address(P),) (identity(SD),)
+ (sensitivity,) proposal> */
+ key_setsadbaddr(SADB_EXT_ADDRESS_SRC, AF_INET, "192.168.1.1");
+ key_setsadbaddr(SADB_EXT_ADDRESS_DST, AF_INET, "10.0.3.4");
+ key_setsadbaddr(SADB_EXT_ADDRESS_PROXY, AF_INET6, "3ffe::1");
+ key_setsadbid(SADB_EXT_IDENTITY_SRC, "hoge1234@hoge.com");
+ key_setsadbid(SADB_EXT_IDENTITY_DST, "hage5678@hage.net");
+ key_setsadbsens();
+ key_setsadbprop();
+ /* <base, address(SD), (address(P),) (identity(SD),)
+ (sensitivity,) proposal> */
+ break;
+
+ case SADB_REGISTER:
+ /* <base> */
+ /* <base, supported> */
+ break;
+
+ case SADB_EXPIRE:
+ case SADB_FLUSH:
+ break;
+
+ case SADB_DUMP:
+ break;
+
+ case SADB_X_PROMISC:
+ /* <base> */
+ /* <base, base(, others)> */
+ break;
+
+ case SADB_X_PCHANGE:
+ break;
+
+ /* for SPD management */
+ case SADB_X_SPDFLUSH:
+ case SADB_X_SPDDUMP:
+ break;
+
+ case SADB_X_SPDADD:
+#if 0
+ {
+ struct sadb_x_policy m_policy;
+
+ m_policy.sadb_x_policy_len = PFKEY_UNIT64(sizeof(m_policy));
+ m_policy.sadb_x_policy_exttype = SADB_X_EXT_POLICY;
+ m_policy.sadb_x_policy_type = SADB_X_PL_IPSEC;
+ m_policy.sadb_x_policy_esp_trans = 1;
+ m_policy.sadb_x_policy_ah_trans = 2;
+ m_policy.sadb_x_policy_esp_network = 3;
+ m_policy.sadb_x_policy_ah_network = 4;
+ m_policy.sadb_x_policy_reserved = 0;
+
+ memcpy(m_buf + m_len, &m_policy, sizeof(struct sadb_x_policy));
+ m_len += sizeof(struct sadb_x_policy);
+ }
+#endif
+
+ case SADB_X_SPDDELETE:
+ key_setsadbaddr(SADB_EXT_ADDRESS_SRC, AF_INET, "192.168.1.1");
+ key_setsadbaddr(SADB_EXT_ADDRESS_DST, AF_INET, "10.0.3.4");
+ break;
+ }
+
+ ((struct sadb_msg *)m_buf)->sadb_msg_len = PFKEY_UNIT64(m_len);
+
+ return;
+}
+
+void
+key_setsadbsens()
+{
+ struct sadb_sens m_sens;
+ u_char buf[64];
+ u_int s, i, slen, ilen, len;
+
+ /* make sens & integ */
+ s = htonl(0x01234567);
+ i = htonl(0x89abcdef);
+ slen = sizeof(s);
+ ilen = sizeof(i);
+ memcpy(buf, &s, slen);
+ memcpy(buf + slen, &i, ilen);
+
+ len = sizeof(m_sens) + PFKEY_ALIGN8(slen) + PFKEY_ALIGN8(ilen);
+ m_sens.sadb_sens_len = PFKEY_UNIT64(len);
+ m_sens.sadb_sens_exttype = SADB_EXT_SENSITIVITY;
+ m_sens.sadb_sens_dpd = 1;
+ m_sens.sadb_sens_sens_level = 2;
+ m_sens.sadb_sens_sens_len = PFKEY_ALIGN8(slen);
+ m_sens.sadb_sens_integ_level = 3;
+ m_sens.sadb_sens_integ_len = PFKEY_ALIGN8(ilen);
+ m_sens.sadb_sens_reserved = 0;
+
+ key_setsadbextbuf(m_buf, m_len,
+ (caddr_t)&m_sens, sizeof(struct sadb_sens),
+ buf, slen + ilen);
+ m_len += len;
+
+ return;
+}
+
+void
+key_setsadbprop()
+{
+ struct sadb_prop m_prop;
+ struct sadb_comb *m_comb;
+ u_char buf[256];
+ u_int len = sizeof(m_prop) + sizeof(m_comb) * 2;
+
+ /* make prop & comb */
+ m_prop.sadb_prop_len = PFKEY_UNIT64(len);
+ m_prop.sadb_prop_exttype = SADB_EXT_PROPOSAL;
+ m_prop.sadb_prop_replay = 0;
+ m_prop.sadb_prop_reserved[0] = 0;
+ m_prop.sadb_prop_reserved[1] = 0;
+ m_prop.sadb_prop_reserved[2] = 0;
+
+ /* the 1st is ESP DES-CBC HMAC-MD5 */
+ m_comb = (struct sadb_comb *)buf;
+ m_comb->sadb_comb_auth = SADB_AALG_MD5HMAC;
+ m_comb->sadb_comb_encrypt = SADB_EALG_DESCBC;
+ m_comb->sadb_comb_flags = 0;
+ m_comb->sadb_comb_auth_minbits = 8;
+ m_comb->sadb_comb_auth_maxbits = 96;
+ m_comb->sadb_comb_encrypt_minbits = 64;
+ m_comb->sadb_comb_encrypt_maxbits = 64;
+ m_comb->sadb_comb_reserved = 0;
+ m_comb->sadb_comb_soft_allocations = 0;
+ m_comb->sadb_comb_hard_allocations = 0;
+ m_comb->sadb_comb_soft_bytes = 0;
+ m_comb->sadb_comb_hard_bytes = 0;
+ m_comb->sadb_comb_soft_addtime = 0;
+ m_comb->sadb_comb_hard_addtime = 0;
+ m_comb->sadb_comb_soft_usetime = 0;
+ m_comb->sadb_comb_hard_usetime = 0;
+
+ /* the 2st is ESP 3DES-CBC and AH HMAC-SHA1 */
+ m_comb = (struct sadb_comb *)(buf + sizeof(*m_comb));
+ m_comb->sadb_comb_auth = SADB_AALG_SHA1HMAC;
+ m_comb->sadb_comb_encrypt = SADB_EALG_3DESCBC;
+ m_comb->sadb_comb_flags = 0;
+ m_comb->sadb_comb_auth_minbits = 8;
+ m_comb->sadb_comb_auth_maxbits = 96;
+ m_comb->sadb_comb_encrypt_minbits = 64;
+ m_comb->sadb_comb_encrypt_maxbits = 64;
+ m_comb->sadb_comb_reserved = 0;
+ m_comb->sadb_comb_soft_allocations = 0;
+ m_comb->sadb_comb_hard_allocations = 0;
+ m_comb->sadb_comb_soft_bytes = 0;
+ m_comb->sadb_comb_hard_bytes = 0;
+ m_comb->sadb_comb_soft_addtime = 0;
+ m_comb->sadb_comb_hard_addtime = 0;
+ m_comb->sadb_comb_soft_usetime = 0;
+ m_comb->sadb_comb_hard_usetime = 0;
+
+ key_setsadbextbuf(m_buf, m_len,
+ (caddr_t)&m_prop, sizeof(struct sadb_prop),
+ buf, sizeof(*m_comb) * 2);
+ m_len += len;
+
+ return;
+}
+
+void
+key_setsadbid(ext, str)
+ u_int ext;
+ caddr_t str;
+{
+ struct sadb_ident m_id;
+ u_int idlen = strlen(str), len;
+
+ len = sizeof(m_id) + PFKEY_ALIGN8(idlen);
+ m_id.sadb_ident_len = PFKEY_UNIT64(len);
+ m_id.sadb_ident_exttype = ext;
+ m_id.sadb_ident_type = SADB_IDENTTYPE_USERFQDN;
+ m_id.sadb_ident_reserved = 0;
+ m_id.sadb_ident_id = getpid();
+
+ key_setsadbextbuf(m_buf, m_len,
+ (caddr_t)&m_id, sizeof(struct sadb_ident),
+ str, idlen);
+ m_len += len;
+
+ return;
+}
+
+void
+key_setsadblft(ext, time)
+ u_int ext, time;
+{
+ struct sadb_lifetime m_lft;
+
+ m_lft.sadb_lifetime_len = PFKEY_UNIT64(sizeof(m_lft));
+ m_lft.sadb_lifetime_exttype = ext;
+ m_lft.sadb_lifetime_allocations = 0x2;
+ m_lft.sadb_lifetime_bytes = 0x1000;
+ m_lft.sadb_lifetime_addtime = time;
+ m_lft.sadb_lifetime_usetime = 0x0020;
+
+ memcpy(m_buf + m_len, &m_lft, sizeof(struct sadb_lifetime));
+ m_len += sizeof(struct sadb_lifetime);
+
+ return;
+}
+
+void
+key_setspirange()
+{
+ struct sadb_spirange m_spi;
+
+ m_spi.sadb_spirange_len = PFKEY_UNIT64(sizeof(m_spi));
+ m_spi.sadb_spirange_exttype = SADB_EXT_SPIRANGE;
+ m_spi.sadb_spirange_min = 0x00001000;
+ m_spi.sadb_spirange_max = 0x00002000;
+ m_spi.sadb_spirange_reserved = 0;
+
+ memcpy(m_buf + m_len, &m_spi, sizeof(struct sadb_spirange));
+ m_len += sizeof(struct sadb_spirange);
+
+ return;
+}
+
+void
+key_setsadbkey(ext, str)
+ u_int ext;
+ caddr_t str;
+{
+ struct sadb_key m_key;
+ u_int keylen = strlen(str);
+ u_int len;
+
+ len = sizeof(struct sadb_key) + PFKEY_ALIGN8(keylen);
+ m_key.sadb_key_len = PFKEY_UNIT64(len);
+ m_key.sadb_key_exttype = ext;
+ m_key.sadb_key_bits = keylen * 8;
+ m_key.sadb_key_reserved = 0;
+
+ key_setsadbextbuf(m_buf, m_len,
+ (caddr_t)&m_key, sizeof(struct sadb_key),
+ str, keylen);
+ m_len += len;
+
+ return;
+}
+
+void
+key_setsadbsa()
+{
+ struct sadb_sa m_sa;
+
+ m_sa.sadb_sa_len = PFKEY_UNIT64(sizeof(struct sadb_sa));
+ m_sa.sadb_sa_exttype = SADB_EXT_SA;
+ m_sa.sadb_sa_spi = htonl(0x12345678);
+ m_sa.sadb_sa_replay = 4;
+ m_sa.sadb_sa_state = 0;
+ m_sa.sadb_sa_auth = SADB_AALG_MD5HMAC;
+ m_sa.sadb_sa_encrypt = SADB_EALG_DESCBC;
+ m_sa.sadb_sa_flags = 0;
+
+ memcpy(m_buf + m_len, &m_sa, sizeof(struct sadb_sa));
+ m_len += sizeof(struct sadb_sa);
+
+ return;
+}
+
+void
+key_setsadbaddr(ext, af, str)
+ u_int ext, af;
+ caddr_t str;
+{
+ struct sadb_address m_addr;
+ u_int len;
+ struct addrinfo hints, *res;
+ const char *serv;
+ int plen;
+
+ switch (af) {
+ case AF_INET:
+ plen = sizeof(struct in_addr) << 3;
+ break;
+ case AF_INET6:
+ plen = sizeof(struct in6_addr) << 3;
+ break;
+ default:
+ /* XXX bark */
+ exit(1);
+ }
+
+ /* make sockaddr buffer */
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = af;
+ hints.ai_socktype = SOCK_DGRAM; /*dummy*/
+ hints.ai_flags = AI_NUMERICHOST;
+ serv = (ext == SADB_EXT_ADDRESS_PROXY ? "0" : "4660"); /*0x1234*/
+ if (getaddrinfo(str, serv, &hints, &res) != 0 || res->ai_next) {
+ /* XXX bark */
+ exit(1);
+ }
+
+ len = sizeof(struct sadb_address) + PFKEY_ALIGN8(res->ai_addrlen);
+ m_addr.sadb_address_len = PFKEY_UNIT64(len);
+ m_addr.sadb_address_exttype = ext;
+ m_addr.sadb_address_proto =
+ (ext == SADB_EXT_ADDRESS_PROXY ? 0 : IPPROTO_TCP);
+ m_addr.sadb_address_prefixlen = plen;
+ m_addr.sadb_address_reserved = 0;
+
+ key_setsadbextbuf(m_buf, m_len,
+ (caddr_t)&m_addr, sizeof(struct sadb_address),
+ (caddr_t)res->ai_addr, res->ai_addrlen);
+ m_len += len;
+
+ freeaddrinfo(res);
+
+ return;
+}
+
+void
+key_setsadbextbuf(dst, off, ebuf, elen, vbuf, vlen)
+ caddr_t dst, ebuf, vbuf;
+ int off, elen, vlen;
+{
+ memset(dst + off, 0, elen + vlen);
+ memcpy(dst + off, (caddr_t)ebuf, elen);
+ memcpy(dst + off + elen, vbuf, vlen);
+
+ return;
+}
+
diff --git a/usr.sbin/setkey/test-policy.c b/usr.sbin/setkey/test-policy.c
new file mode 100644
index 0000000..27cd478
--- /dev/null
+++ b/usr.sbin/setkey/test-policy.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet6/in6.h>
+#include <netkey/keyv2.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <netinet6/ipsec.h>
+
+char *requests[] = {
+"must_error", /* must be error */
+"ipsec must_error", /* must be error */
+"ipsec esp/must_error", /* must be error */
+"discard",
+"none",
+"entrust",
+"bypass", /* may be error */
+"ipsec esp", /* must be error */
+"ipsec ah/require",
+"ipsec ah/use/",
+"ipsec esp/require ah/default/203.178.141.194",
+"ipsec ah/use/203.178.141.195 esp/use/203.178.141.194",
+"ipsec esp/elf.wide.ydc.co.jp esp/www.wide.ydc.co.jp"
+"
+ipsec esp/require ah/use esp/require/10.0.0.1
+ah/use/3ffe:501:481d::1 ah/use/3ffe:501:481d::1 ah/use/3ffe:501:481d::1
+ah/use/3ffe:501:481d::1 ah/use/3ffe:501:481d::1 ah/use/3ffe:501:481d::1
+ah/use/3ffe:501:481d::1 ah/use/3ffe:501:481d::1 ah/use/3ffe:501:481d::1
+ah/use/3ffe:501:481d::1 ah/use/3ffe:501:481d::1 ah/use/3ffe:501:481d::1
+ah/use/3ffe:501:481d::1 ah/use/3ffe:501:481d::1 ah/use/3ffe:501:481d::1
+ah/use/3ffe:501:481d::1 ah/use/3ffe:501:481d::1 ah/use/3ffe:501:481d::1
+ah/use/3ffe:501:481d::1 ah/use/3ffe:501:481d::1 ah/use/3ffe:501:481d::1
+ah/use/3ffe:501:481d::1 ah/use/3ffe:501:481d::1ah/use/3ffe:501:481d::1
+",
+};
+
+u_char *p_secpolicy;
+
+int test(char *buf, int family);
+char *setpolicy(char *req);
+
+main()
+{
+ int i;
+ char *buf;
+
+ for (i = 0; i < sizeof(requests)/sizeof(requests[0]); i++) {
+ printf("* requests:[%s]\n", requests[i]);
+ if ((buf = setpolicy(requests[i])) == NULL)
+ continue;
+ printf("\tsetlen:%d\n", PFKEY_EXTLEN(buf));
+
+ printf("\tPF_INET:\n");
+ test(buf, PF_INET);
+
+ printf("\tPF_INET6:\n");
+ test(buf, PF_INET6);
+ free(buf);
+ }
+}
+
+int test(char *policy, int family)
+{
+ int so, proto, optname;
+ int len;
+ char getbuf[1024];
+
+ switch (family) {
+ case PF_INET:
+ proto = IPPROTO_IP;
+ optname = IP_IPSEC_POLICY;
+ break;
+ case PF_INET6:
+ proto = IPPROTO_IPV6;
+ optname = IPV6_IPSEC_POLICY;
+ break;
+ }
+
+ if ((so = socket(family, SOCK_DGRAM, 0)) < 0)
+ perror("socket");
+
+ if (setsockopt(so, proto, optname, policy, PFKEY_EXTLEN(policy)) < 0)
+ perror("setsockopt");
+
+ len = sizeof(getbuf);
+ memset(getbuf, 0, sizeof(getbuf));
+ if (getsockopt(so, proto, optname, getbuf, &len) < 0)
+ perror("getsockopt");
+
+ {
+ char *buf = NULL;
+
+ printf("\tgetlen:%d\n", len);
+
+ if ((buf = ipsec_dump_policy(getbuf, NULL)) == NULL)
+ ipsec_strerror();
+ else
+ printf("\t[%s]\n", buf);
+
+ free(buf);
+ }
+
+ close (so);
+}
+
+char *setpolicy(char *req)
+{
+ int len;
+ char *buf;
+
+ if ((len = ipsec_get_policylen(req)) < 0) {
+ printf("ipsec_get_policylen: %s\n", ipsec_strerror());
+ return NULL;
+ }
+
+ if ((buf = malloc(len)) == NULL) {
+ perror("malloc");
+ return NULL;
+ }
+
+ if ((len = ipsec_set_policy(buf, len, req)) < 0) {
+ printf("ipsec_set_policy: %s\n", ipsec_strerror());
+ free(buf);
+ return NULL;
+ }
+
+ return buf;
+}
diff --git a/usr.sbin/setkey/token.l b/usr.sbin/setkey/token.l
new file mode 100644
index 0000000..208196e
--- /dev/null
+++ b/usr.sbin/setkey/token.l
@@ -0,0 +1,322 @@
+/* $FreeBSD$ */
+/* $KAME: token.l,v 1.21 2001/05/18 05:35:01 sakane Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+%{
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/route.h>
+#include <net/pfkeyv2.h>
+#include <netkey/keydb.h>
+#include <netkey/key_debug.h>
+#include <netinet/in.h>
+#include <netinet6/ipsec.h>
+
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include "vchar.h"
+#ifdef __NetBSD__
+#include "parse.h"
+#else
+#include "y.tab.h"
+#endif
+
+#define DECHO \
+ if (f_debug) {printf("<%d>", yy_start); ECHO ; printf("\n"); }
+
+#define CMDARG \
+{ \
+ char *__buf__ = strdup(yytext), *__p__; \
+ for (__p__ = __buf__; *__p__ != NULL; __p__++) \
+ if (*__p__ == '\n' || *__p__ == '\t') \
+ *__p__ = ' '; \
+ strcat(cmdarg, __buf__); \
+ free(__buf__); \
+}
+
+#define PREPROC DECHO CMDARG
+
+int lineno = 1;
+char cmdarg[8192]; /* XXX: BUFSIZ is the better ? */
+
+extern u_char m_buf[BUFSIZ];
+extern u_int m_len;
+extern int f_debug;
+
+int yylex __P((void));
+void yyfatal __P((const char *s));
+void yyerror __P((const char *s));
+extern void parse_init __P((void));
+int parse __P((FILE **));
+int yyparse __P((void));
+
+%}
+
+/* common section */
+nl \n
+ws [ \t]+
+digit [0-9]
+letter [0-9A-Za-z]
+hexdigit [0-9A-Fa-f]
+/*octet (([01]?{digit}?{digit})|((2([0-4]{digit}))|(25[0-5])))*/
+special [()+\|\?\*,]
+dot \.
+comma \,
+hyphen \-
+colon \:
+slash \/
+bcl \{
+ecl \}
+blcl \[
+elcl \]
+percent \%
+semi \;
+usec {dot}{digit}{1,6}
+comment \#.*
+ccomment "/*"
+bracketstring \<[^>]*\>
+quotedstring \"[^"]*\"
+decstring {digit}+
+hexpair {hexdigit}{hexdigit}
+hexstring 0[xX]{hexdigit}+
+octetstring {octet}({dot}{octet})+
+ipaddress [a-fA-F0-9:]([a-fA-F0-9:\.]*|[a-fA-F0-9:\.]*%[a-zA-Z0-9]*)
+ipaddrmask {slash}{digit}{1,3}
+ipaddrport {blcl}{decstring}{elcl}
+keyword {letter}{letter}+
+name {letter}(({letter}|{digit}|{hyphen})*({letter}|{digit}))*
+hostname {name}(({dot}{name})+{dot}?)?
+
+%s S_PL
+
+%%
+
+add { PREPROC; return(ADD); }
+delete { PREPROC; return(DELETE); }
+deleteall { PREPROC; return(DELETEALL); }
+get { PREPROC; return(GET); }
+flush { PREPROC; return(FLUSH); }
+dump { PREPROC; return(DUMP); }
+
+ /* for management SPD */
+spdadd { PREPROC; return(SPDADD); }
+spddelete { PREPROC; return(SPDDELETE); }
+spddump { PREPROC; return(SPDDUMP); }
+spdflush { PREPROC; return(SPDFLUSH); }
+{hyphen}P { BEGIN S_PL; PREPROC; return(F_POLICY); }
+<S_PL>[a-zA-Z0-9:\.\-_/ \n\t][a-zA-Z0-9:\.\-_/ \n\t]* {
+ yymore();
+
+ /* count up for nl */
+ {
+ char *p;
+ for (p = yytext; *p != NULL; p++)
+ if (*p == '\n')
+ lineno++;
+ }
+
+ yylval.val.len = strlen(yytext);
+ yylval.val.buf = strdup(yytext);
+
+ return(PL_REQUESTS);
+}
+<S_PL>{semi} { PREPROC; BEGIN INITIAL; return(EOT); }
+
+ /* security protocols */
+ah { PREPROC; yylval.num = 0; return(PR_AH); }
+esp { PREPROC; yylval.num = 0; return(PR_ESP); }
+ah-old { PREPROC; yylval.num = 1; return(PR_AH); }
+esp-old { PREPROC; yylval.num = 1; return(PR_ESP); }
+ipcomp { PREPROC; yylval.num = 0; return(PR_IPCOMP); }
+
+ /* authentication alogorithm */
+{hyphen}A { PREPROC; return(F_AUTH); }
+hmac-md5 { PREPROC; yylval.num = SADB_AALG_MD5HMAC; return(ALG_AUTH); }
+hmac-sha1 { PREPROC; yylval.num = SADB_AALG_SHA1HMAC; return(ALG_AUTH); }
+keyed-md5 { PREPROC; yylval.num = SADB_X_AALG_MD5; return(ALG_AUTH); }
+keyed-sha1 { PREPROC; yylval.num = SADB_X_AALG_SHA; return(ALG_AUTH); }
+hmac-sha2-256 { PREPROC; yylval.num = SADB_X_AALG_SHA2_256; return(ALG_AUTH); }
+hmac-sha2-384 { PREPROC; yylval.num = SADB_X_AALG_SHA2_384; return(ALG_AUTH); }
+hmac-sha2-512 { PREPROC; yylval.num = SADB_X_AALG_SHA2_512; return(ALG_AUTH); }
+null { PREPROC; yylval.num = SADB_X_AALG_NULL; return(ALG_AUTH); }
+
+ /* encryption alogorithm */
+{hyphen}E { PREPROC; return(F_ENC); }
+des-cbc { PREPROC; yylval.num = SADB_EALG_DESCBC; return(ALG_ENC); }
+3des-cbc { PREPROC; yylval.num = SADB_EALG_3DESCBC; return(ALG_ENC); }
+simple { PREPROC; yylval.num = SADB_EALG_NULL; return(ALG_ENC); }
+blowfish-cbc { PREPROC; yylval.num = SADB_X_EALG_BLOWFISHCBC; return(ALG_ENC); }
+cast128-cbc { PREPROC; yylval.num = SADB_X_EALG_CAST128CBC; return(ALG_ENC); }
+des-deriv { PREPROC; yylval.num = SADB_EALG_DESCBC; return(ALG_ENC_DESDERIV); }
+des-32iv { PREPROC; yylval.num = SADB_EALG_DESCBC; return(ALG_ENC_DES32IV); }
+rijndael-cbc { PREPROC; yylval.num = SADB_X_EALG_RIJNDAELCBC; return(ALG_ENC); }
+
+ /* compression algorithms */
+{hyphen}C { PREPROC; return(F_COMP); }
+oui { PREPROC; yylval.num = SADB_X_CALG_OUI; return(ALG_COMP); }
+deflate { PREPROC; yylval.num = SADB_X_CALG_DEFLATE; return(ALG_COMP); }
+lzs { PREPROC; yylval.num = SADB_X_CALG_LZS; return(ALG_COMP); }
+{hyphen}R { PREPROC; return(F_RAWCPI); }
+
+ /* extension */
+{hyphen}m { PREPROC; return(F_MODE); }
+transport { PREPROC; yylval.num = IPSEC_MODE_TRANSPORT; return(MODE); }
+tunnel { PREPROC; yylval.num = IPSEC_MODE_TUNNEL; return(MODE); }
+{hyphen}u { PREPROC; return(F_REQID); }
+{hyphen}f { PREPROC; return(F_EXT); }
+random-pad { PREPROC; yylval.num = SADB_X_EXT_PRAND; return(EXTENSION); }
+seq-pad { PREPROC; yylval.num = SADB_X_EXT_PSEQ; return(EXTENSION); }
+zero-pad { PREPROC; yylval.num = SADB_X_EXT_PZERO; return(EXTENSION); }
+nocyclic-seq { PREPROC; return(NOCYCLICSEQ); }
+{hyphen}r { PREPROC; return(F_REPLAY); }
+{hyphen}lh { PREPROC; return(F_LIFETIME_HARD); }
+{hyphen}ls { PREPROC; return(F_LIFETIME_SOFT); }
+
+ /* ... */
+any { PREPROC; return(ANY); }
+{ws} { PREPROC; }
+{nl} { lineno++; }
+{comment}
+{semi} { PREPROC; return(EOT); }
+
+ /* parameter */
+{decstring} {
+ char *bp;
+
+ PREPROC;
+ yylval.num = strtoul(yytext, &bp, 10);
+ return(DECSTRING);
+ }
+
+{ipaddress} {
+ PREPROC;
+
+ yylval.val.len = yyleng;
+ yylval.val.buf = strdup(yytext);
+
+ return(ADDRESS);
+ }
+
+{ipaddrmask} {
+ PREPROC;
+ yytext++;
+ yylval.num = atoi(yytext);
+ return(PREFIX);
+ }
+
+{ipaddrport} {
+ char *p = yytext;
+ PREPROC;
+ while (*++p != ']') ;
+ *p = NULL;
+ yytext++;
+ yylval.num = atoi(yytext);
+ return(PORT);
+ }
+
+{blcl}any{elcl} {
+ PREPROC;
+ return(PORTANY);
+ }
+
+{hexstring} {
+ int len = yyleng - 2; /* (str - "0x") */
+ PREPROC;
+ yylval.val.len = (len & 1) + (len / 2);
+ /* fixed string if length is odd. */
+ if (len & 1) {
+ yytext[1] = '0';
+ yylval.val.buf = strdup(yytext + 1);
+ } else
+ yylval.val.buf = strdup(yytext + 2);
+
+ return(HEXSTRING);
+ }
+
+{quotedstring} {
+ char *p = yytext;
+ PREPROC;
+ while (*++p != '"') ;
+ *p = NULL;
+ yytext++;
+ yylval.val.len = yyleng - 2;
+ yylval.val.buf = strdup(yytext);
+
+ return(QUOTEDSTRING);
+ }
+
+[a-z0-9.\-]* {
+ yylval.val.len = yyleng;
+ yylval.val.buf = strdup(yytext);
+ return(STRING);
+ }
+
+. {
+ yyfatal("Syntax error");
+ /*NOTREACHED*/
+ }
+
+%%
+
+void
+yyfatal(s)
+ const char *s;
+{
+ yyerror(s);
+ exit(1);
+}
+
+void
+yyerror(s)
+ const char *s;
+{
+ printf("line %d: %s at [%s]\n", lineno, s, yytext);
+}
+
+int
+parse(fp)
+ FILE **fp;
+{
+ yyin = *fp;
+
+ parse_init();
+
+ if (yyparse()) {
+ printf("parse failed, line %d.\n", lineno);
+ return(-1);
+ }
+
+ return(0);
+}
diff --git a/usr.sbin/setkey/vchar.h b/usr.sbin/setkey/vchar.h
new file mode 100644
index 0000000..f3251c7
--- /dev/null
+++ b/usr.sbin/setkey/vchar.h
@@ -0,0 +1,36 @@
+/* $FreeBSD$ */
+/* $KAME: vchar.h,v 1.2 2000/06/07 00:29:14 itojun Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+typedef struct {
+ u_int len;
+ caddr_t buf;
+} vchar_t;
diff --git a/usr.sbin/sgsc/Makefile b/usr.sbin/sgsc/Makefile
new file mode 100644
index 0000000..5f14a66
--- /dev/null
+++ b/usr.sbin/sgsc/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+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..0b02f92
--- /dev/null
+++ b/usr.sbin/sgsc/sgsc.1
@@ -0,0 +1,104 @@
+.\" 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
+.\"
+.\" $FreeBSD$
+.\"
+.\" 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
+.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.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl s Bq Dv 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 Bq Dv GSC_SRES
+Set the resolution in dpi.
+.It Fl w Ar width Bq Dv GSC_SWIDHT
+Set the width of the bitmap in pixels.
+.It Fl h Ar height Bq Dv GSC_SHEIGHT
+Set the height of the bitmap in lines of pixels.
+.It Fl b Ar len Bq Dv GSC_SBLEN
+Set the internal dma buffer to be
+.Ar len
+lines in size.
+.It Fl t Ar timeout Bq Dv 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..94960a3
--- /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[] =
+ "$FreeBSD$";
+#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..2f3a888
--- /dev/null
+++ b/usr.sbin/sicontrol/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= sicontrol
+MAN= sicontrol.8
+
+CFLAGS+= -I${.CURDIR}/../../sys
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/sicontrol/sicontrol.8 b/usr.sbin/sicontrol/sicontrol.8
new file mode 100644
index 0000000..53b88cd
--- /dev/null
+++ b/usr.sbin/sicontrol/sicontrol.8
@@ -0,0 +1,104 @@
+.\" $FreeBSD$
+.\"
+.Dd September 26, 1995
+.Dt SICONTROL 8
+.Os
+.Sh NAME
+.Nm sicontrol
+.Nd Specialix SI/XIO driver configuration and debugging
+.Sh SYNOPSIS
+.Nm
+device
+.Ar command Op Ar param ...
+.Sh DESCRIPTION
+.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
+.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 DIAGNOSTICS
+Generally self explanatory.....
+.Sh SEE ALSO
+.Xr stty 1 ,
+.Xr si 4 ,
+.Xr termios 4 ,
+.Xr tty 4 ,
+.Xr comcontrol 8
+.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..eb6560f
--- /dev/null
+++ b/usr.sbin/sicontrol/sicontrol.c
@@ -0,0 +1,572 @@
+/*
+ * Device driver for Specialix range (SLXOS) of serial line multiplexors.
+ * SLXOS configuration and debug interface
+ *
+ * Copyright (C) 1990, 1992 Specialix International,
+ * Copyright (C) 1993, Andy Rutter <andy@acronym.co.uk>
+ * Copyright (C) 1995, Peter Wemm <peter@haywire.dialix.com>
+ *
+ * Derived from: SunOS 4.x version
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notices, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notices, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Andy Rutter of
+ * Advanced Methods and Tools Ltd. based on original information
+ * from Specialix International.
+ * 4. Neither the name of Advanced Methods and Tools, nor Specialix
+ * International may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHORS BE LIABLE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <ctype.h>
+#include <err.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/tty.h>
+
+#include <dev/si/si.h>
+
+struct lv {
+ char *lv_name;
+ int lv_bit;
+} lv[] = {
+ {"entry", DBG_ENTRY},
+ {"open", DBG_OPEN},
+ {"close", DBG_CLOSE},
+ {"read", DBG_READ},
+ {"write", DBG_WRITE},
+ {"param", DBG_PARAM},
+ {"modem", DBG_MODEM},
+ {"select", DBG_SELECT},
+ {"optim", DBG_OPTIM},
+ {"intr", DBG_INTR},
+ {"start", DBG_START},
+ {"lstart", DBG_LSTART},
+ {"ioctl", DBG_IOCTL},
+ {"fail", DBG_FAIL},
+ {"autoboot", DBG_AUTOBOOT},
+ {"download", DBG_DOWNLOAD},
+ {"drain", DBG_DRAIN},
+ {"poll", DBG_POLL},
+ {0, 0}
+};
+
+static int alldev = 0;
+
+void ccb_stat(int, char **);
+void debug(int, char **);
+void dostat(void);
+int getnum(char *);
+int islevel(char *);
+int lvls2bits(char *);
+void mstate(int, char **);
+void nport(int, char **);
+void onoff(int, char **, int, char *, char *, int);
+int opencontrol(void);
+void prlevels(int);
+void prusage(int, int);
+void rxint(int, char **);
+void tty_stat(int, char **);
+void txint(int, char **);
+
+struct opt {
+ char *o_name;
+ void (*o_func)(int, char **);
+} opt[] = {
+ {"debug", debug},
+ {"rxint_throttle", rxint},
+ {"int_throttle", txint},
+ {"nport", nport},
+ {"mstate", mstate},
+ {"ccbstat", ccb_stat},
+ {"ttystat", tty_stat},
+ {0, 0}
+};
+
+struct stat_list {
+ void (*st_func)(int, char **);
+} stat_list[] = {
+ {mstate},
+ {0}
+};
+
+#define U_DEBUG 0
+#define U_TXINT 1
+#define U_RXINT 2
+#define U_NPORT 3
+#define U_MSTATE 4
+#define U_STAT_CCB 5
+#define U_STAT_TTY 6
+
+#define U_MAX 7
+#define U_ALL -1
+char *usage[] = {
+ "debug [[add|del|set debug_levels] | [off]]\n",
+ "int_throttle [newvalue]\n",
+ "rxint_throttle [newvalue]\n",
+ "nport\n",
+ "mstate\n",
+ "ccbstat\n",
+ "ttystat\n",
+ 0
+};
+
+int ctlfd;
+char *Devname;
+struct si_tcsi tc;
+
+int
+main(int argc, char **argv)
+{
+ struct opt *op;
+ void (*func)(int, char **) = NULL;
+
+ if (argc < 2)
+ prusage(U_ALL, 1);
+ Devname = argv[1];
+ if (strcmp(Devname, "-") == 0) {
+ alldev = 1;
+ } else {
+ sidev_t dev;
+ struct stat st;
+
+ if (strchr(Devname, '/') == NULL) {
+ char *acp = malloc(6 + strlen(Devname));
+ strcpy(acp, _PATH_DEV);
+ strcat(acp, Devname);
+ Devname = acp;
+ }
+ if (stat(Devname, &st) < 0)
+ errx(1, "can't stat %s", Devname);
+ dev.sid_card = SI_CARD(minor(st.st_rdev));
+ dev.sid_port = SI_PORT(minor(st.st_rdev));
+ tc.tc_dev = dev;
+ }
+ ctlfd = opencontrol();
+ if (argc == 2) {
+ dostat();
+ exit(0);
+ }
+
+ argc--; argv++;
+ for (op = opt; op->o_name; op++) {
+ if (strcmp(argv[1], op->o_name) == 0) {
+ func = op->o_func;
+ break;
+ }
+ }
+ if (func == NULL)
+ prusage(U_ALL, 1);
+
+ argc -= 2;
+ argv += 2;
+ (*func)(argc, argv);
+ exit(0);
+}
+
+int
+opencontrol(void)
+{
+ int fd;
+
+ fd = open(CONTROLDEV, O_RDWR|O_NDELAY);
+ if (fd < 0)
+ err(1, "open on %s", CONTROLDEV);
+ return(fd);
+}
+
+/*
+ * Print a usage message - this relies on U_DEBUG==0 and U_BOOT==1.
+ * Don't print the DEBUG usage string unless explicity requested.
+ */
+void
+prusage(int strn, int eflag)
+{
+ char **cp;
+
+ if (strn == U_ALL) {
+ fprintf(stderr, "usage: sicontrol %s", usage[1]);
+ fprintf(stderr, " sicontrol %s", usage[2]);
+ fprintf(stderr, " sicontrol %s", usage[3]);
+ fprintf(stderr, " sicontrol devname %s", usage[4]);
+ for (cp = &usage[5]; *cp; cp++)
+ fprintf(stderr, " sicontrol devname %s", *cp);
+ }
+ else if (strn >= 0 && strn <= U_MAX)
+ fprintf(stderr, "usage: sicontrol devname %s", usage[strn]);
+ else
+ fprintf(stderr, "sicontrol: usage ???\n");
+ exit(eflag);
+}
+
+/* print port status */
+void
+dostat(void)
+{
+ char *av[1], *acp;
+ struct stat_list *stp;
+ struct si_tcsi stc;
+ int donefirst = 0;
+
+ printf("%s: ", alldev ? "ALL" : Devname);
+ acp = malloc(strlen(Devname) + 3);
+ memset(acp, ' ', strlen(Devname));
+ strcat(acp, " ");
+ stc = tc;
+ for (stp = stat_list; stp->st_func != NULL; stp++) {
+ if (donefirst)
+ fputs(acp, stdout);
+ else
+ donefirst++;
+ av[0] = NULL;
+ tc = stc;
+ (*stp->st_func)(-1, av);
+ }
+}
+
+/*
+ * debug
+ * debug [[set|add|del debug_lvls] | [off]]
+ */
+void
+debug(int ac, char **av)
+{
+ int level;
+
+ if (ac > 2)
+ prusage(U_DEBUG, 1);
+ if (alldev) {
+ if (ioctl(ctlfd, TCSIGDBG_ALL, &tc.tc_dbglvl) < 0)
+ err(1, "TCSIGDBG_ALL on %s", Devname);
+ } else {
+ if (ioctl(ctlfd, TCSIGDBG_LEVEL, &tc) < 0)
+ err(1, "TCSIGDBG_LEVEL on %s", Devname);
+ }
+
+ switch (ac) {
+ case 0:
+ printf("%s: debug levels - ", Devname);
+ prlevels(tc.tc_dbglvl);
+ return;
+ case 1:
+ if (strcmp(av[0], "off") == 0) {
+ tc.tc_dbglvl = 0;
+ break;
+ }
+ prusage(U_DEBUG, 1);
+ /* no return */
+ case 2:
+ level = lvls2bits(av[1]);
+ if (strcmp(av[0], "add") == 0)
+ tc.tc_dbglvl |= level;
+ else if (strcmp(av[0], "del") == 0)
+ tc.tc_dbglvl &= ~level;
+ else if (strcmp(av[0], "set") == 0)
+ tc.tc_dbglvl = level;
+ else
+ prusage(U_DEBUG, 1);
+ }
+ if (alldev) {
+ if (ioctl(ctlfd, TCSISDBG_ALL, &tc.tc_dbglvl) < 0)
+ err(1, "TCSISDBG_ALL on %s", Devname);
+ } else {
+ if (ioctl(ctlfd, TCSISDBG_LEVEL, &tc) < 0)
+ err(1, "TCSISDBG_LEVEL on %s", Devname);
+ }
+}
+
+void
+rxint(int ac, char **av)
+{
+ tc.tc_port = 0;
+ switch (ac) {
+ case 0:
+ printf("%s: ", Devname);
+ case -1:
+ if (ioctl(ctlfd, TCSIGRXIT, &tc) < 0)
+ err(1, "TCSIGRXIT");
+ printf("RX interrupt throttle: %d msec\n", tc.tc_int*10);
+ break;
+ case 1:
+ tc.tc_int = getnum(av[0]) / 10;
+ if (tc.tc_int == 0)
+ tc.tc_int = 1;
+ if (ioctl(ctlfd, TCSIRXIT, &tc) < 0)
+ err(1, "TCSIRXIT on %s at %d msec",
+ Devname, tc.tc_int*10);
+ break;
+ default:
+ prusage(U_RXINT, 1);
+ }
+}
+
+void
+txint(int ac, char **av)
+{
+
+ tc.tc_port = 0;
+ switch (ac) {
+ case 0:
+ printf("%s: ", Devname);
+ case -1:
+ if (ioctl(ctlfd, TCSIGIT, &tc) < 0)
+ err(1, "TCSIGIT");
+ printf("aggregate interrupt throttle: %d\n", tc.tc_int);
+ break;
+ case 1:
+ tc.tc_int = getnum(av[0]);
+ if (ioctl(ctlfd, TCSIIT, &tc) < 0)
+ err(1, "TCSIIT on %s at %d", Devname, tc.tc_int);
+ break;
+ default:
+ prusage(U_TXINT, 1);
+ }
+}
+
+void
+onoff(int ac, char **av, int cmd, char *cmdstr, char *prstr, int usage)
+{
+ if (ac > 1)
+ prusage(usage, 1);
+ if (ac == 1) {
+ if (strcmp(av[0], "on") == 0)
+ tc.tc_int = 1;
+ else if (strcmp(av[0], "off") == 0)
+ tc.tc_int = 0;
+ else
+ prusage(usage, 1);
+ } else
+ tc.tc_int = -1;
+ if (ioctl(ctlfd, cmd, &tc) < 0)
+ err(1, "%s on %s", cmdstr, Devname);
+ switch (ac) {
+ case 0:
+ printf("%s: ", Devname);
+ case -1:
+ printf("%s ", prstr);
+ if (tc.tc_int)
+ printf("on\n");
+ else
+ printf("off\n");
+ }
+}
+
+void
+mstate(int ac, char **av)
+{
+ switch (ac) {
+ case 0:
+ printf("%s: ", Devname);
+ case -1:
+ break;
+ default:
+ prusage(U_MSTATE, 1);
+ }
+ if (ioctl(ctlfd, TCSISTATE, &tc) < 0)
+ err(1, "TCSISTATE on %s", Devname);
+ printf("modem bits state - (0x%x)", tc.tc_int);
+ if (tc.tc_int & IP_DCD) printf(" DCD");
+ if (tc.tc_int & IP_DTR) printf(" DTR");
+ if (tc.tc_int & IP_RTS) printf(" RTS");
+ printf("\n");
+}
+
+void
+nport(int ac, char **av)
+{
+ int ports;
+
+ if (ac != 0)
+ prusage(U_NPORT, 1);
+ if (ioctl(ctlfd, TCSIPORTS, &ports) < 0)
+ err(1, "TCSIPORTS on %s", Devname);
+ printf("SLXOS: total of %d ports\n", ports);
+}
+
+void
+ccb_stat(int ac, char **av)
+{
+ struct si_pstat sip;
+#define CCB sip.tc_ccb
+
+ if (ac != 0)
+ prusage(U_STAT_CCB, 1);
+ sip.tc_dev = tc.tc_dev;
+ if (ioctl(ctlfd, TCSI_CCB, &sip) < 0)
+ err(1, "TCSI_CCB on %s", Devname);
+ printf("%s: ", Devname);
+
+ /* WORD next - Next Channel */
+ /* WORD addr_uart - Uart address */
+ /* WORD module - address of module struct */
+ printf("\tuart_type 0x%x\n", CCB.type); /* BYTE type - Uart type */
+ /* BYTE fill - */
+ printf("\tx_status 0x%x\n", CCB.x_status); /* BYTE x_status - XON / XOFF status */
+ printf("\tc_status 0x%x\n", CCB.c_status); /* BYTE c_status - cooking status */
+ printf("\thi_rxipos 0x%x\n", CCB.hi_rxipos); /* BYTE hi_rxipos - stuff into rx buff */
+ printf("\thi_rxopos 0x%x\n", CCB.hi_rxopos); /* BYTE hi_rxopos - stuff out of rx buffer */
+ printf("\thi_txopos 0x%x\n", CCB.hi_txopos); /* BYTE hi_txopos - Stuff into tx ptr */
+ printf("\thi_txipos 0x%x\n", CCB.hi_txipos); /* BYTE hi_txipos - ditto out */
+ printf("\thi_stat 0x%x\n", CCB.hi_stat); /* BYTE hi_stat - Command register */
+ printf("\tdsr_bit 0x%x\n", CCB.dsr_bit); /* BYTE dsr_bit - Magic bit for DSR */
+ printf("\ttxon 0x%x\n", CCB.txon); /* BYTE txon - TX XON char */
+ printf("\ttxoff 0x%x\n", CCB.txoff); /* BYTE txoff - ditto XOFF */
+ printf("\trxon 0x%x\n", CCB.rxon); /* BYTE rxon - RX XON char */
+ printf("\trxoff 0x%x\n", CCB.rxoff); /* BYTE rxoff - ditto XOFF */
+ printf("\thi_mr1 0x%x\n", CCB.hi_mr1); /* BYTE hi_mr1 - mode 1 image */
+ printf("\thi_mr2 0x%x\n", CCB.hi_mr2); /* BYTE hi_mr2 - mode 2 image */
+ printf("\thi_csr 0x%x\n", CCB.hi_csr); /* BYTE hi_csr - clock register */
+ printf("\thi_op 0x%x\n", CCB.hi_op); /* BYTE hi_op - Op control */
+ printf("\thi_ip 0x%x\n", CCB.hi_ip); /* BYTE hi_ip - Input pins */
+ printf("\thi_state 0x%x\n", CCB.hi_state); /* BYTE hi_state - status */
+ printf("\thi_prtcl 0x%x\n", CCB.hi_prtcl); /* BYTE hi_prtcl - Protocol */
+ printf("\thi_txon 0x%x\n", CCB.hi_txon); /* BYTE hi_txon - host copy tx xon stuff */
+ printf("\thi_txoff 0x%x\n", CCB.hi_txoff); /* BYTE hi_txoff - */
+ printf("\thi_rxon 0x%x\n", CCB.hi_rxon); /* BYTE hi_rxon - */
+ printf("\thi_rxoff 0x%x\n", CCB.hi_rxoff); /* BYTE hi_rxoff - */
+ printf("\tclose_prev 0x%x\n", CCB.close_prev); /* BYTE close_prev - Was channel previously closed */
+ printf("\thi_break 0x%x\n", CCB.hi_break); /* BYTE hi_break - host copy break process */
+ printf("\tbreak_state 0x%x\n", CCB.break_state); /* BYTE break_state - local copy ditto */
+ printf("\thi_mask 0x%x\n", CCB.hi_mask); /* BYTE hi_mask - Mask for CS7 etc. */
+ printf("\tmask_z280 0x%x\n", CCB.mask_z280); /* BYTE mask_z280 - Z280's copy */
+ /* BYTE res[0x60 - 36] - */
+ /* BYTE hi_txbuf[SLXOS_BUFFERSIZE] - */
+ /* BYTE hi_rxbuf[SLXOS_BUFFERSIZE] - */
+ /* BYTE res1[0xA0] - */
+}
+
+void
+tty_stat(int ac, char **av)
+{
+ struct si_pstat sip;
+#define TTY sip.tc_tty
+
+ if (ac != 0)
+ prusage(U_STAT_TTY, 1);
+ sip.tc_dev = tc.tc_dev;
+ if (ioctl(ctlfd, TCSI_TTY, &sip) < 0)
+ err(1, "TCSI_TTY on %s", Devname);
+ printf("%s: ", Devname);
+
+ printf("\tt_outq.c_cc %d.\n", TTY.t_outq.c_cc); /* struct clist t_outq */
+ printf("\tt_dev 0x%x\n", TTY.t_dev); /* dev_t t_dev */
+ printf("\tt_flags 0x%x\n", TTY.t_flags); /* int t_flags */
+ printf("\tt_state 0x%x\n", TTY.t_state); /* int t_state */
+ printf("\tt_ihiwat %d.\n", TTY.t_ihiwat); /* int t_ihiwat */
+ printf("\tt_ilowat %d.\n", TTY.t_ilowat); /* int t_ilowat */
+ printf("\tt_ohiwat %d.\n", TTY.t_ohiwat); /* int t_ohiwat */
+ printf("\tt_olowat %d.\n", TTY.t_olowat); /* int t_olowat */
+ printf("\tt_iflag 0x%x\n", TTY.t_iflag); /* t_iflag */
+ printf("\tt_oflag 0x%x\n", TTY.t_oflag); /* t_oflag */
+ printf("\tt_cflag 0x%x\n", TTY.t_cflag); /* t_cflag */
+ printf("\tt_lflag 0x%x\n", TTY.t_lflag); /* t_lflag */
+ printf("\tt_cc %p\n", (void *)TTY.t_cc); /* t_cc */
+ printf("\tt_termios.c_ispeed 0x%x\n", TTY.t_termios.c_ispeed); /* t_termios.c_ispeed */
+ printf("\tt_termios.c_ospeed 0x%x\n", TTY.t_termios.c_ospeed); /* t_termios.c_ospeed */
+}
+
+int
+islevel(char *tk)
+{
+ struct lv *lvp;
+ char *acp;
+
+ for (acp = tk; *acp; acp++)
+ if (isupper(*acp))
+ *acp = tolower(*acp);
+ for (lvp = lv; lvp->lv_name; lvp++)
+ if (strcmp(lvp->lv_name, tk) == 0)
+ return(lvp->lv_bit);
+ return(0);
+}
+
+/*
+ * Convert a string consisting of tokens separated by white space, commas
+ * or `|' into a bitfield - flag any unrecognised tokens.
+ */
+int
+lvls2bits(char *str)
+{
+ int i, bits = 0;
+ int errflag = 0;
+ char token[20];
+
+ while (sscanf(str, "%[^,| \t]", token) == 1) {
+ str += strlen(token);
+ while (isspace(*str) || *str==',' || *str=='|')
+ str++;
+ if (strcmp(token, "all") == 0)
+ return(0xffffffff);
+ if ((i = islevel(token)) == 0) {
+ warnx("unknown token '%s'", token);
+ errflag++;
+ } else
+ bits |= i;
+ }
+ if (errflag)
+ exit(1);
+
+ return(bits);
+}
+
+int
+getnum(char *str)
+{
+ int x;
+ char *acp = str;
+
+ x = 0;
+ while (*acp) {
+ if (!isdigit(*acp))
+ errx(1, "%s is not a number", str);
+ x *= 10;
+ x += (*acp - '0');
+ acp++;
+ }
+ return(x);
+}
+
+void
+prlevels(int x)
+{
+ struct lv *lvp;
+
+ switch (x) {
+ case 0:
+ printf("(none)\n");
+ break;
+ case 0xffffffff:
+ printf("all\n");
+ break;
+ default:
+ for (lvp = lv; lvp->lv_name; lvp++)
+ if (x & lvp->lv_bit)
+ printf(" %s", lvp->lv_name);
+ printf("\n");
+ }
+}
diff --git a/usr.sbin/sliplogin/Makefile b/usr.sbin/sliplogin/Makefile
new file mode 100644
index 0000000..860b9b0
--- /dev/null
+++ b/usr.sbin/sliplogin/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 8.1 (Berkeley) 7/19/93
+# $FreeBSD$
+
+PROG= sliplogin
+MAN= sliplogin.8
+BINOWN= root
+BINGRP= network
+BINMODE=4550
+INSTALLFLAGS=-fschg
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/sliplogin/pathnames.h b/usr.sbin/sliplogin/pathnames.h
new file mode 100644
index 0000000..3ed5d98
--- /dev/null
+++ b/usr.sbin/sliplogin/pathnames.h
@@ -0,0 +1,48 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * From: @(#)pathnames.h 8.1 (Berkeley) 6/6/93
+ * $FreeBSD$
+ */
+
+#ifndef COMPAT
+#include <paths.h>
+#else
+#define _PATH_DEVNULL "/dev/null"
+#endif
+
+#define _PATH_ACCESS "/etc/sliphome/slip.hosts"
+#define _PATH_SLPARMS "/etc/sliphome/slip.slparms"
+#define _PATH_SLIP_LOGIN "/etc/sliphome/slip.login"
+#define _PATH_SLIP_LOGOUT "/etc/sliphome/slip.logout"
+#define _PATH_DEBUG "/tmp/sliplogin.XXXXXX"
+
diff --git a/usr.sbin/sliplogin/sliplogin.8 b/usr.sbin/sliplogin/sliplogin.8
new file mode 100644
index 0000000..38c7f98
--- /dev/null
+++ b/usr.sbin/sliplogin/sliplogin.8
@@ -0,0 +1,311 @@
+.\" Copyright (c) 1990, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)sliplogin.8 8.2 (Berkeley) 1/5/94
+.\" $FreeBSD$
+.\"
+.Dd January 5, 1994
+.Dt SLIPLOGIN 8
+.Os
+.Sh NAME
+.Nm sliplogin
+.Nd attach a serial line network interface
+.Sh SYNOPSIS
+.Nm
+.Op Ar loginname Op Ar device
+.Sh DESCRIPTION
+.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.
+.Sh FreeBSD Additions
+An additional SLIP configuration file (if present) is
+.Pa /etc/sliphome/slip.slparms .
+If particular hosts need different configurations, the file
+.Pa /etc/sliphome/slip.slparms. Ns Ar loginname
+will be parsed instead if it exists.
+.Ss Format of /etc/sliphome/slip.slparms*
+Comments (lines starting with a `#') and blank lines (or started with
+space) are ignored.
+This file contains from one to three numeric parameters separated with spaces,
+in order:
+.Ar keepalive ,
+.Ar outfill
+and
+.Ar slunit .
+.Bl -tag -width keepalive
+.It Ar keepalive
+Set SLIP "keep alive" timeout in seconds.
+If FRAME_END is not received in
+this amount of time,
+.Nm
+closes the line and exits.
+The default value is no timeout (zero).
+.It Ar outfill
+Set SLIP "out fill" timeout in seconds.
+It forces at least one FRAME_END
+to be sent during this time period, which is necessary for the "keep alive"
+timeout on the remote side.
+The default value is no timeout (zero).
+.It Ar slunit
+Set the SLIP unit number directly.
+Use with caution, because no check is made
+for two interfaces with same number.
+By default sliplogin dynamically assigns the unit number.
+.El
+.Pp
+If latter two parameters are omitted, they will not affect the
+corresponding SLIP configuration.
+If any of first two parameters is equal to zero, it will not affect
+the corresponding SLIP configuration.
+.Sh EXAMPLES
+The normal use of
+.Nm
+is to create a
+.Pa /etc/passwd
+entry for each legal, remote slip site with
+.Nm
+as the shell for that entry. E.g.,
+.Bd -literal
+Sfoo:ikhuy6:2010:1:slip line to foo:/tmp:/usr/sbin/sliplogin
+.Ed
+.Pp
+(Our convention is to name the account used by remote host
+.Ar hostname
+as
+.Em Shostname . )
+Then an entry is added to
+.Pa slip.hosts
+that looks like:
+.Pp
+.Bd -literal -offset indent -compact
+Sfoo `hostname` foo netmask
+.Ed
+.Pp
+where
+.Em `hostname`
+will be evaluated by
+.Xr sh
+to the local host name and
+.Em netmask
+is the local host IP netmask.
+.Pp
+Note that
+.Nm
+must be setuid to root and, while not a security hole, moral defectives
+can use it to place terminal lines in an unusable state and/or deny
+access to legitimate users of a remote slip line. To prevent this,
+.Nm
+is installed as user
+.Em root ,
+group
+.Em network
+and mode 4550 so that only members of group
+.Em network
+may run
+.Nm .
+The system administrator should make sure that all legitimate users
+are a member of the correct group.
+.Sh DIAGNOSTICS
+.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
+.Bl -tag -width indent
+.It Pa /etc/sliphome/slip.hosts
+list of host login names and parameters.
+.It Pa /etc/sliphome/slip.login
+script executed when a connection is made.
+.It Pa /etc/sliphome/slip.login. Ns Ar loginname
+script executed when a connection is made by
+.Ar loginname .
+.It Pa /etc/sliphome/slip.logout
+script executed when a connection is lost.
+.It Pa /etc/sliphome/slip.logout. Ns Ar loginname
+script executed when a connection is lost by
+.Ar loginname .
+.It Pa /etc/sliphome/slip.slparms
+extra parameters file.
+.It Pa /etc/sliphome/slip.slparms. Ns Ar loginname
+extra parameters file for
+.Ar loginname .
+.It Pa /var/run/ Ns Ar ttyXn Ns Pa .if
+contains the name of the network interface used by the sliplogin process on
+.Ar ttyXn .
+.It Pa /var/run/ Ns Ar slX Ns Pa .pid
+contains the PID of the sliplogin process which is using interface
+.Ar slX .
+.El
+.Sh SEE ALSO
+.Xr slattach 8 ,
+.Xr syslogd 8
+.Pp
+.Pa /usr/share/examples/sliplogin
+.Sh HISTORY
+The
+.Nm
+command
+appeared in
+.Bx 4.3 Reno .
diff --git a/usr.sbin/sliplogin/sliplogin.c b/usr.sbin/sliplogin/sliplogin.c
new file mode 100644
index 0000000..8a3bc5e
--- /dev/null
+++ b/usr.sbin/sliplogin/sliplogin.c
@@ -0,0 +1,548 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1990, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)sliplogin.c 8.2 (Berkeley) 2/1/94";
+static char rscid[] = "@(#)$FreeBSD$";
+#endif /* not lint */
+
+/*
+ * sliplogin.c
+ * [MUST BE RUN SUID, SLOPEN DOES A SUSER()!]
+ *
+ * This program initializes its own tty port to be an async TCP/IP interface.
+ * It sets the line discipline to slip, invokes a shell script to initialize
+ * the network interface, then pauses forever waiting for hangup.
+ *
+ * It is a remote descendant of several similar programs with incestuous ties:
+ * - Kirk Smith's slipconf, modified by Richard Johnsson @ DEC WRL.
+ * - slattach, probably by Rick Adams but touched by countless hordes.
+ * - the original sliplogin for 4.2bsd, Doug Kingston the mover behind it.
+ *
+ * There are two forms of usage:
+ *
+ * "sliplogin"
+ * Invoked simply as "sliplogin", the program looks up the username
+ * in the file /etc/slip.hosts.
+ * If an entry is found, the line on fd0 is configured for SLIP operation
+ * as specified in the file.
+ *
+ * "sliplogin IPhostlogin </dev/ttyb"
+ * Invoked by root with a username, the name is looked up in the
+ * /etc/slip.hosts file and if found fd0 is configured as in case 1.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <syslog.h>
+#include <netdb.h>
+
+#include <termios.h>
+#include <sys/ioctl.h>
+#include <net/slip.h>
+#include <net/if.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <paths.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include "pathnames.h"
+
+extern char **environ;
+
+static char *restricted_environ[] = {
+ "PATH=" _PATH_STDPATH,
+ NULL
+};
+
+int unit;
+int slip_mode;
+speed_t speed;
+int uid;
+int keepal;
+int outfill;
+int slunit;
+char loginargs[BUFSIZ];
+char loginfile[MAXPATHLEN];
+char loginname[BUFSIZ];
+static char raddr[32]; /* remote address */
+char ifname[IFNAMSIZ]; /* interface name */
+static char pidfilename[MAXPATHLEN]; /* name of pid file */
+static char iffilename[MAXPATHLEN]; /* name of if file */
+static pid_t pid; /* our pid */
+
+char *
+make_ipaddr(void)
+{
+static char address[20] ="";
+struct hostent *he;
+unsigned long ipaddr;
+
+address[0] = '\0';
+if ((he = gethostbyname(raddr)) != NULL) {
+ ipaddr = ntohl(*(long *)he->h_addr_list[0]);
+ sprintf(address, "%lu.%lu.%lu.%lu",
+ ipaddr >> 24,
+ (ipaddr & 0x00ff0000) >> 16,
+ (ipaddr & 0x0000ff00) >> 8,
+ (ipaddr & 0x000000ff));
+ }
+
+return address;
+}
+
+struct slip_modes {
+ char *sm_name;
+ int sm_or_flag;
+ int sm_and_flag;
+} modes[] = {
+ "normal", 0 , 0 ,
+ "compress", IFF_LINK0, IFF_LINK2,
+ "noicmp", IFF_LINK1, 0 ,
+ "autocomp", IFF_LINK2, IFF_LINK0,
+};
+
+void
+findid(name)
+ char *name;
+{
+ FILE *fp;
+ static char slopt[5][16];
+ static char laddr[16];
+ static char mask[16];
+ char slparmsfile[MAXPATHLEN];
+ char user[16];
+ char buf[128];
+ int i, j, n;
+
+ environ = restricted_environ; /* minimal protection for system() */
+
+ (void)strncpy(loginname, name, sizeof(loginname)-1);
+ loginname[sizeof(loginname)-1] = '\0';
+
+ if ((fp = fopen(_PATH_ACCESS, "r")) == NULL) {
+ accfile_err:
+ syslog(LOG_ERR, "%s: %m\n", _PATH_ACCESS);
+ exit(1);
+ }
+ while (fgets(loginargs, sizeof(loginargs) - 1, fp)) {
+ if (ferror(fp))
+ goto accfile_err;
+ if (loginargs[0] == '#' || isspace(loginargs[0]))
+ continue;
+ n = sscanf(loginargs, "%15s%*[ \t]%15s%*[ \t]%15s%*[ \t]%15s%*[ \t]%15s%*[ \t]%15s%*[ \t]%15s%*[ \t]%15s%*[ \t]%15s\n",
+ user, laddr, raddr, mask, slopt[0], slopt[1],
+ slopt[2], slopt[3], slopt[4]);
+ if (n < 4) {
+ syslog(LOG_ERR, "%s: wrong format\n", _PATH_ACCESS);
+ exit(1);
+ }
+ if (strcmp(user, name) != 0)
+ continue;
+
+ (void) fclose(fp);
+
+ slip_mode = 0;
+ for (i = 0; i < n - 4; i++) {
+ for (j = 0; j < sizeof(modes)/sizeof(struct slip_modes);
+ j++) {
+ if (strcmp(modes[j].sm_name, slopt[i]) == 0) {
+ slip_mode |= (modes[j].sm_or_flag);
+ slip_mode &= ~(modes[j].sm_and_flag);
+ break;
+ }
+ }
+ }
+
+ /*
+ * we've found the guy we're looking for -- see if
+ * there's a login file we can use. First check for
+ * one specific to this host. If none found, try for
+ * a generic one.
+ */
+ (void)snprintf(loginfile, sizeof(loginfile), "%s.%s",
+ _PATH_SLIP_LOGIN, name);
+ if (access(loginfile, R_OK|X_OK) != 0) {
+ (void)strncpy(loginfile, _PATH_SLIP_LOGIN,
+ sizeof(loginfile) - 1);
+ loginfile[sizeof(loginfile) - 1] = '\0';
+ if (access(loginfile, R_OK|X_OK)) {
+ syslog(LOG_ERR,
+ "access denied for %s - no %s\n",
+ name, _PATH_SLIP_LOGIN);
+ exit(5);
+ }
+ }
+ (void)snprintf(slparmsfile, sizeof(slparmsfile), "%s.%s", _PATH_SLPARMS, name);
+ if (access(slparmsfile, R_OK|X_OK) != 0) {
+ (void)strncpy(slparmsfile, _PATH_SLPARMS, sizeof(slparmsfile)-1);
+ slparmsfile[sizeof(slparmsfile)-1] = '\0';
+ if (access(slparmsfile, R_OK|X_OK))
+ *slparmsfile = '\0';
+ }
+ keepal = outfill = 0;
+ slunit = -1;
+ if (*slparmsfile) {
+ if ((fp = fopen(slparmsfile, "r")) == NULL) {
+ slfile_err:
+ syslog(LOG_ERR, "%s: %m\n", slparmsfile);
+ exit(1);
+ }
+ n = 0;
+ while (fgets(buf, sizeof(buf) - 1, fp) != NULL) {
+ if (ferror(fp))
+ goto slfile_err;
+ if (buf[0] == '#' || isspace(buf[0]))
+ continue;
+ n = sscanf(buf, "%d %d %d", &keepal, &outfill, &slunit);
+ if (n < 1) {
+ slwrong_fmt:
+ syslog(LOG_ERR, "%s: wrong format\n", slparmsfile);
+ exit(1);
+ }
+ (void) fclose(fp);
+ break;
+ }
+ if (n == 0)
+ goto slwrong_fmt;
+ }
+
+ return;
+ }
+ syslog(LOG_ERR, "SLIP access denied for %s\n", name);
+ exit(4);
+ /* NOTREACHED */
+}
+
+char *
+sigstr(s)
+ int s;
+{
+ static char buf[32];
+
+ switch (s) {
+ case SIGHUP: return("HUP");
+ case SIGINT: return("INT");
+ case SIGQUIT: return("QUIT");
+ case SIGILL: return("ILL");
+ case SIGTRAP: return("TRAP");
+ case SIGIOT: return("IOT");
+ case SIGEMT: return("EMT");
+ case SIGFPE: return("FPE");
+ case SIGKILL: return("KILL");
+ case SIGBUS: return("BUS");
+ case SIGSEGV: return("SEGV");
+ case SIGSYS: return("SYS");
+ case SIGPIPE: return("PIPE");
+ case SIGALRM: return("ALRM");
+ case SIGTERM: return("TERM");
+ case SIGURG: return("URG");
+ case SIGSTOP: return("STOP");
+ case SIGTSTP: return("TSTP");
+ case SIGCONT: return("CONT");
+ case SIGCHLD: return("CHLD");
+ case SIGTTIN: return("TTIN");
+ case SIGTTOU: return("TTOU");
+ case SIGIO: return("IO");
+ case SIGXCPU: return("XCPU");
+ case SIGXFSZ: return("XFSZ");
+ case SIGVTALRM: return("VTALRM");
+ case SIGPROF: return("PROF");
+ case SIGWINCH: return("WINCH");
+#ifdef SIGLOST
+ case SIGLOST: return("LOST");
+#endif
+ case SIGUSR1: return("USR1");
+ case SIGUSR2: return("USR2");
+ }
+ (void)snprintf(buf, sizeof(buf), "sig %d", s);
+ return(buf);
+}
+
+void
+hup_handler(s)
+ int s;
+{
+ char logoutfile[MAXPATHLEN];
+
+ (void) close(0);
+ seteuid(0);
+ (void)snprintf(logoutfile, sizeof(logoutfile), "%s.%s",
+ _PATH_SLIP_LOGOUT, loginname);
+ if (access(logoutfile, R_OK|X_OK) != 0) {
+ (void)strncpy(logoutfile, _PATH_SLIP_LOGOUT,
+ sizeof(logoutfile) - 1);
+ logoutfile[sizeof(logoutfile) - 1] = '\0';
+ }
+ if (access(logoutfile, R_OK|X_OK) == 0) {
+ char logincmd[2*MAXPATHLEN+32];
+
+ (void) snprintf(logincmd, sizeof(logincmd), "%s %d %ld %s", logoutfile, unit, speed, loginargs);
+ (void) system(logincmd);
+ }
+ syslog(LOG_INFO, "closed %s slip unit %d (%s)\n", loginname, unit,
+ sigstr(s));
+ if (unlink(pidfilename) < 0 && errno != ENOENT)
+ syslog(LOG_WARNING, "unable to delete pid file: %m");
+ if (unlink(iffilename) < 0 && errno != ENOENT)
+ syslog(LOG_WARNING, "unable to delete if file: %m");
+ exit(1);
+ /* NOTREACHED */
+}
+
+
+/* Modify the slip line mode and add any compression or no-icmp flags. */
+void line_flags(unit)
+ int unit;
+{
+ struct ifreq ifr;
+ int s;
+
+ /* open a socket as the handle to the interface */
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ syslog(LOG_ERR, "socket: %m");
+ exit(1);
+ }
+ sprintf(ifr.ifr_name, "sl%d", unit);
+
+ /* get the flags for the interface */
+ if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
+ syslog(LOG_ERR, "ioctl (SIOCGIFFLAGS): %m");
+ exit(1);
+ }
+
+ /* Assert any compression or no-icmp flags. */
+#define SLMASK (~(IFF_LINK0 | IFF_LINK1 | IFF_LINK2))
+ ifr.ifr_flags &= SLMASK;
+ ifr.ifr_flags |= slip_mode;
+ if (ioctl(s, SIOCSIFFLAGS, (caddr_t)&ifr) < 0) {
+ syslog(LOG_ERR, "ioctl (SIOCSIFFLAGS): %m");
+ exit(1);
+ }
+ close(s);
+}
+
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int fd, s, ldisc;
+ char *name;
+ struct termios tios, otios;
+ char logincmd[2*BUFSIZ+32];
+ extern uid_t getuid();
+
+ FILE *pidfile; /* pid file */
+ FILE *iffile; /* interfaces file */
+ char *p;
+ int n;
+ char devnam[MAXPATHLEN] = _PATH_TTY; /* Device name */
+
+ if ((name = strrchr(argv[0], '/')) == NULL)
+ name = argv[0];
+ s = getdtablesize();
+ for (fd = 3 ; fd < s ; fd++)
+ (void) close(fd);
+ openlog(name, LOG_PID|LOG_PERROR, LOG_DAEMON);
+ uid = getuid();
+ if (argc > 1) {
+ findid(argv[1]);
+
+ /*
+ * Disassociate from current controlling terminal, if any,
+ * and ensure that the slip line is our controlling terminal.
+ */
+ if (daemon(1, 1)) {
+ syslog(LOG_ERR, "daemon(1, 1): %m");
+ exit(1);
+ }
+ if (argc > 2) {
+ if ((fd = open(argv[2], O_RDWR)) == -1) {
+ syslog(LOG_ERR, "open %s: %m", argv[2]);
+ exit(2);
+ }
+ (void) dup2(fd, 0);
+ if (fd > 2)
+ close(fd);
+ }
+ if (ioctl(0, TIOCSCTTY, 0) == -1) {
+ syslog(LOG_ERR, "ioctl (TIOCSCTTY): %m");
+ exit(1);
+ }
+ if (tcsetpgrp(0, getpid()) < 0) {
+ syslog(LOG_ERR, "tcsetpgrp failed: %m");
+ exit(1);
+ }
+ } else {
+ if ((name = getlogin()) == NULL) {
+ syslog(LOG_ERR, "access denied - login name not found\n");
+ exit(1);
+ }
+ findid(name);
+ }
+ (void) fchmod(0, 0600);
+ (void) fprintf(stderr, "starting slip login for %s\n", loginname);
+ (void) fprintf(stderr, "your address is %s\n\n", make_ipaddr());
+
+ (void) fflush(stderr);
+ sleep(1);
+
+ /* set up the line parameters */
+ if (tcgetattr(0, &tios) < 0) {
+ syslog(LOG_ERR, "tcgetattr: %m");
+ exit(1);
+ }
+ otios = tios;
+ cfmakeraw(&tios);
+ if (tcsetattr(0, TCSAFLUSH, &tios) < 0) {
+ syslog(LOG_ERR, "tcsetattr: %m");
+ exit(1);
+ }
+ speed = cfgetispeed(&tios);
+
+ ldisc = SLIPDISC;
+ if (ioctl(0, TIOCSETD, &ldisc) < 0) {
+ syslog(LOG_ERR, "ioctl(TIOCSETD): %m");
+ exit(1);
+ }
+ if (slunit >= 0 && ioctl(0, SLIOCSUNIT, &slunit) < 0) {
+ syslog(LOG_ERR, "ioctl (SLIOCSUNIT): %m");
+ exit(1);
+ }
+ /* find out what unit number we were assigned */
+ if (ioctl(0, SLIOCGUNIT, &unit) < 0) {
+ syslog(LOG_ERR, "ioctl (SLIOCGUNIT): %m");
+ exit(1);
+ }
+ (void) signal(SIGHUP, hup_handler);
+ (void) signal(SIGTERM, hup_handler);
+
+ if (keepal > 0) {
+ (void) signal(SIGURG, hup_handler);
+ if (ioctl(0, SLIOCSKEEPAL, &keepal) < 0) {
+ syslog(LOG_ERR, "ioctl(SLIOCSKEEPAL): %m");
+ exit(1);
+ }
+ }
+ if (outfill > 0 && ioctl(0, SLIOCSOUTFILL, &outfill) < 0) {
+ syslog(LOG_ERR, "ioctl(SLIOCSOUTFILL): %m");
+ exit(1);
+ }
+
+ /* write pid to file */
+ pid = getpid();
+ (void) sprintf(ifname, "sl%d", unit);
+ (void) sprintf(pidfilename, "%s%s.pid", _PATH_VARRUN, ifname);
+ if ((pidfile = fopen(pidfilename, "w")) != NULL) {
+ fprintf(pidfile, "%d\n", pid);
+ (void) fclose(pidfile);
+ } else {
+ syslog(LOG_ERR, "Failed to create pid file %s: %m",
+ pidfilename);
+ pidfilename[0] = 0;
+ }
+
+ /* write interface unit number to file */
+ p = ttyname(0);
+ if (p)
+ strcpy(devnam, p);
+ for (n = strlen(devnam); n > 0; n--)
+ if (devnam[n] == '/') {
+ n++;
+ break;
+ }
+ (void) sprintf(iffilename, "%s%s.if", _PATH_VARRUN, &devnam[n]);
+ if ((iffile = fopen(iffilename, "w")) != NULL) {
+ fprintf(iffile, "sl%d\n", unit);
+ (void) fclose(iffile);
+ } else {
+ syslog(LOG_ERR, "Failed to create if file %s: %m", iffilename);
+ iffilename[0] = 0;
+ }
+
+
+ syslog(LOG_INFO, "attaching slip unit %d for %s\n", unit, loginname);
+ (void)snprintf(logincmd, sizeof(logincmd), "%s %d %ld %s", loginfile, unit, speed,
+ loginargs);
+ /*
+ * aim stdout and errout at /dev/null so logincmd output won't
+ * babble into the slip tty line.
+ */
+ (void) close(1);
+ if ((fd = open(_PATH_DEVNULL, O_WRONLY)) != 1) {
+ if (fd < 0) {
+ syslog(LOG_ERR, "open %s: %m", _PATH_DEVNULL);
+ exit(1);
+ }
+ (void) dup2(fd, 1);
+ (void) close(fd);
+ }
+ (void) dup2(1, 2);
+
+ /*
+ * Run login and logout scripts as root (real and effective);
+ * current route(8) is setuid root, and checks the real uid
+ * to see whether changes are allowed (or just "route get").
+ */
+ (void) setuid(0);
+ if (s = system(logincmd)) {
+ syslog(LOG_ERR, "%s login failed: exit status %d from %s",
+ loginname, s, loginfile);
+ exit(6);
+ }
+
+ /* Handle any compression or no-icmp flags. */
+ line_flags(unit);
+
+ /* reset uid to users' to allow the user to give a signal. */
+ seteuid(uid);
+ /* twiddle thumbs until we get a signal */
+ while (1)
+ sigpause(0);
+
+ /* NOTREACHED */
+}
diff --git a/usr.sbin/slstat/Makefile b/usr.sbin/slstat/Makefile
new file mode 100644
index 0000000..ff0b9ca
--- /dev/null
+++ b/usr.sbin/slstat/Makefile
@@ -0,0 +1,7 @@
+# from: @(#)Makefile 5.6 (Berkeley) 4/23/91
+# $FreeBSD$
+
+PROG= slstat
+MAN= 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..26e8345
--- /dev/null
+++ b/usr.sbin/slstat/slstat.8
@@ -0,0 +1,129 @@
+.\" Copyright (c) 1986 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" From: @(#)slstat.8 6.8 (Berkeley) 6/20/91
+.\" $FreeBSD$
+.\"
+.Dd October 11, 1996
+.Dt SLSTAT 8
+.Os
+.Sh NAME
+.Nm slstat
+.Nd report serial line IP statistics
+.Sh SYNOPSIS
+.Nm
+.Op Fl i Ar interval
+.Op Fl vr
+.Op Ar unit
+.Sh DESCRIPTION
+The
+.Nm
+utility
+reports certain kernel statistics kept about serial line internet
+protocol traffic.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl i
+Repeat the display indefinitely every
+.Ar interval
+seconds.
+If no
+.Ar interval
+is specified, the default is 5 seconds.
+.It Fl v
+Verbose display of extra fields of information.
+.It Fl r
+Display all values in rate per second rather than amount per interval.
+.It Ar unit
+Is number specifying the
+.Tn SLIP
+interface, or a
+.Tn SLIP
+interface name.
+The default unit is
+.Sy 0
+for interface
+.Sy sl0 .
+.El
+.Pp
+By default,
+.Nm
+displays the following information:
+.Pp
+.Bl -tag -width indent
+.It in
+bytes received
+.It out
+bytes sent
+.It pack
+packets received or sent
+.It comp
+compressed packets received or sent
+.It uncomp
+uncompressed packets received or sent
+.It unknwn
+inbound packets of unknown type
+.It toss
+inbound packets tossed because of error
+.It other
+all other inbound or outbound ip packets
+.It err
+input or output errors
+.It search
+searches for connection state
+.It miss
+times we could not find a connection state
+.It coll
+collisions with end of clists.
+If you get many collisions (more than one or two
+a day) you probably do not have enough clists
+and you should increase
+.Dv nclist
+in param.c.
+.El
+.Sh EXAMPLES
+The command:
+.Dl slstat -i 5
+will print what the system is doing every five
+seconds.
+.Sh SEE ALSO
+.Xr fstat 1 ,
+.Xr netstat 1 ,
+.Xr nfsstat 1 ,
+.Xr ps 1 ,
+.Xr ststat 1 ,
+.Xr iostat 8 ,
+.Xr pppstats 8 ,
+.Xr pstat 8
+.Pp
+The sections starting with ``Interpreting system activity'' in
+.%T "Installing and Operating 4.3BSD" .
diff --git a/usr.sbin/slstat/slstat.c b/usr.sbin/slstat/slstat.c
new file mode 100644
index 0000000..ac1fa83
--- /dev/null
+++ b/usr.sbin/slstat/slstat.c
@@ -0,0 +1,247 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#endif
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_mib.h>
+#include <net/if_types.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <net/slcompress.h>
+#include <net/if_slvar.h>
+
+static void usage __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) {
+ if (errno == ENOENT)
+ continue;
+
+ err(1, "sysctl");
+ }
+ if (strncmp(interface, ifmd.ifmd_name, IFNAMSIZ) == 0
+ && ifmd.ifmd_data.ifi_type == IFT_SLIP) {
+ indx = i;
+ break;
+ }
+ if (i >= maxifno)
+ errx(1, "interface %s does not exist", interface);
+ }
+
+ name[4] = indx;
+ name[5] = IFDATA_LINKSPECIFIC;
+ intpr();
+ exit(0);
+}
+
+#define V(offset) ((line % 20)? ((sc->offset - osc->offset) / \
+ (rflag ? interval : 1)) : sc->offset)
+#define AMT (sizeof(*sc) - 2 * sizeof(sc->sc_comp.tstate))
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: slstat [-i interval] [-vr] [unit]\n");
+ exit(1);
+}
+
+u_char signalled; /* set if alarm goes off "early" */
+
+/*
+ * Print a running summary of interface statistics.
+ * Repeat display every interval seconds, showing statistics
+ * collected over that interval. Assumes that interval is non-zero.
+ * First line printed at top of screen is always cumulative.
+ */
+static void
+intpr()
+{
+ register int line = 0;
+ int oldmask;
+ struct sl_softc *sc, *osc;
+ size_t len;
+
+ sc = (struct sl_softc *)malloc(AMT);
+ osc = (struct sl_softc *)malloc(AMT);
+ bzero((char *)osc, AMT);
+ len = AMT;
+
+ while (1) {
+ if (sysctl(name, 6, sc, &len, 0, 0) < 0 &&
+ (errno != ENOMEM || len != AMT))
+ err(1, "sysctl linkspecific");
+
+ (void)signal(SIGALRM, catchalarm);
+ signalled = 0;
+ (void)alarm(interval);
+
+ if ((line % 20) == 0) {
+ printf("%8.8s %6.6s %6.6s %6.6s %6.6s",
+ "in", "pack", "comp", "uncomp", "unknwn");
+ if (vflag)
+ printf(" %6.6s %6.6s %6.6s",
+ "toss", "other", "err");
+ printf(" | %8.8s %6.6s %6.6s %6.6s %6.6s",
+ "out", "pack", "comp", "uncomp", "other");
+ if (vflag)
+ printf(" %6.6s %6.6s %6.6s %6.6s",
+ "search", "miss", "err", "coll");
+ putchar('\n');
+ }
+ printf("%8lu %6ld %6u %6u %6u",
+ V(sc_if.if_ibytes),
+ V(sc_if.if_ipackets),
+ V(sc_comp.sls_compressedin),
+ V(sc_comp.sls_uncompressedin),
+ V(sc_comp.sls_errorin));
+ if (vflag)
+ printf(" %6u %6lu %6lu",
+ V(sc_comp.sls_tossed),
+ V(sc_if.if_ipackets) -
+ V(sc_comp.sls_compressedin) -
+ V(sc_comp.sls_uncompressedin) -
+ V(sc_comp.sls_errorin),
+ V(sc_if.if_ierrors));
+ printf(" | %8lu %6ld %6u %6u %6lu",
+ V(sc_if.if_obytes) / (rflag ? interval : 1),
+ V(sc_if.if_opackets),
+ V(sc_comp.sls_compressed),
+ V(sc_comp.sls_packets) - V(sc_comp.sls_compressed),
+ V(sc_if.if_opackets) - V(sc_comp.sls_packets));
+ if (vflag)
+ printf(" %6u %6u %6lu %6lu",
+ V(sc_comp.sls_searches),
+ V(sc_comp.sls_misses),
+ V(sc_if.if_oerrors),
+ V(sc_if.if_collisions));
+ putchar('\n');
+ fflush(stdout);
+ line++;
+ oldmask = sigblock(sigmask(SIGALRM));
+ if (! signalled) {
+ sigpause(0);
+ }
+ sigsetmask(oldmask);
+ signalled = 0;
+ (void)alarm(interval);
+ bcopy((char *)sc, (char *)osc, AMT);
+ }
+}
+
+/*
+ * Called if an interval expires before sidewaysintpr has completed a loop.
+ * Sets a flag to not wait for the alarm.
+ */
+static void
+catchalarm(sig)
+ int sig;
+{
+ signalled = 1;
+}
diff --git a/usr.sbin/spkrtest/Makefile b/usr.sbin/spkrtest/Makefile
new file mode 100644
index 0000000..8e9245a
--- /dev/null
+++ b/usr.sbin/spkrtest/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+SCRIPTS=spkrtest.sh
+MAN= spkrtest.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/spkrtest/spkrtest.8 b/usr.sbin/spkrtest/spkrtest.8
new file mode 100644
index 0000000..1128afb
--- /dev/null
+++ b/usr.sbin/spkrtest/spkrtest.8
@@ -0,0 +1,47 @@
+.\" Copyright (c) May 1995 Wolfram Schneider <wosch@FreeBSD.org>. Berlin.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.Dd July 23, 1995
+.Dt SPKRTEST 8
+.Os
+.Sh NAME
+.Nm spkrtest
+.Nd test script for the speaker driver
+.Sh DESCRIPTION
+.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 spkr 4
+.Sh HISTORY
+The
+.Nm
+script appeared in
+.Fx 1.0
diff --git a/usr.sbin/spkrtest/spkrtest.sh b/usr.sbin/spkrtest/spkrtest.sh
new file mode 100644
index 0000000..c682b03
--- /dev/null
+++ b/usr.sbin/spkrtest/spkrtest.sh
@@ -0,0 +1,113 @@
+#!/bin/sh
+
+#
+# Copyright (c) 2002 The FreeBSD Project
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+#
+# Inspired on spkrtest.pl, rewritten from scratch to remove perl dependency
+# $VER: spkrtest 0.3 (9.5.2002) Riccardo "VIC" Torrini <riccardo@torrini.org>
+# $FreeBSD$
+#
+
+cleanExit() {
+ rm -f ${choices}
+ exit ${1:-0}
+}
+
+trap 'cleanExit 1' 1 2 3 5 15 # HUP, INT, QUIT, TRAP, TERM
+
+choices=${TMP:-/tmp}/_spkrtest_choices.$$
+speaker=/dev/speaker
+
+test -w ${speaker}
+if [ $? -ne 0 ]
+then
+ echo "You have no write access to $speaker or the speaker device is"
+ echo "not enabled in kernel. Cannot play any melody! See spkr(4)."
+ sleep 2
+ cleanExit 1
+fi
+
+/usr/bin/dialog --title "Speaker test" --checklist \
+ "Please select the melodies you wish to play (space for select)" \
+ -1 -1 10 \
+ reveille "Reveille" OFF \
+ contact "Contact theme from Close Encounters" OFF \
+ dance "Lord of the Dance (aka Simple Gifts)" OFF \
+ loony "Loony Toons theme" OFF \
+ sinister "Standard villain's entrance music" OFF \
+ rightstuff "A trope from 'The Right Stuff' score by Bill Conti" OFF \
+ toccata "Opening bars of Bach's Toccata and Fugue in D Minor" OFF \
+ startrek "Opening bars of the theme from Star Trek Classic" OFF \
+ 2> ${choices} || cleanExit 0
+
+echo ""
+tunes="`cat ${choices} | tr -d '\"'`"
+for tune in ${tunes:-DEFAULT}
+do
+ case ${tune:-NULL} in
+ DEFAULT)
+ title="(default melody)"
+ music="ec"
+ ;;
+ reveille)
+ title="Reveille"
+ music="t255l8c.f.afc~c.f.afc~c.f.afc.f.a..f.~c.f.afc~c.f.afc~c.f.afc~c.f.."
+ ;;
+ contact)
+ title="Contact theme from Close Encounters"
+ music="<cd<a#~<a#>f"
+ ;;
+ dance)
+ title="Lord of the Dance (aka Simple Gifts)"
+ music="t240<cfcfgagaa#b#>dc<a#a.~fg.gaa#.agagegc.~cfcfgagaa#b#>dc<a#a.~fg.gga.agfgfgf."
+ ;;
+ loony)
+ title="Loony Toons theme"
+ music="t255cf8f8edc<a>~cf8f8edd#e~ce8cdce8cd.<a>c8c8c#def8af8"
+ ;;
+ sinister)
+ title="standard villain's entrance music"
+ music="mst200o2ola.l8bc.~a.~>l2d#"
+ ;;
+ rightstuff)
+ title="a trope from 'The Right Stuff' score by Bill Conti"
+ music="olcega.a8f>cd2bgc.c8dee2"
+ ;;
+ toccata)
+ title="opening bars of Bach's Toccata and Fugue in D Minor"
+ music="msl16oldcd4mll8pcb-agf+4.g4p4<msl16dcd4mll8pa.a+f+4p16g4"
+ ;;
+ startrek)
+ title="opening bars of the theme from Star Trek Classic"
+ music="l2b.f+.p16a.c+.p l4mn<b.>e8a2mspg+e8c+f+8b2"
+ ;;
+ esac
+ echo "Title: ${title}"
+ echo ${music} > ${speaker}
+ sleep 1
+done
+cleanExit 0
diff --git a/usr.sbin/spray/Makefile b/usr.sbin/spray/Makefile
new file mode 100644
index 0000000..c2515e0
--- /dev/null
+++ b/usr.sbin/spray/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PROG= spray
+MAN= spray.8
+
+DPADD= ${LIBRPCSVC}
+LDADD= -lrpcsvc
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/spray/spray.8 b/usr.sbin/spray/spray.8
new file mode 100644
index 0000000..6718e60
--- /dev/null
+++ b/usr.sbin/spray/spray.8
@@ -0,0 +1,73 @@
+.\"
+.\" Copyright (c) 1994 James A. Jegers
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd July 10, 1995
+.Dt SPRAY 8
+.Os
+.Sh NAME
+.Nm spray
+.Nd send many packets to host
+.Sh SYNOPSIS
+.Nm
+.Op Fl c Ar count
+.Op Fl d Ar delay
+.Op Fl l Ar length
+.Ar host
+.Sh DESCRIPTION
+.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" .
+.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..39759eb
--- /dev/null
+++ b/usr.sbin/spray/spray.c
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 1993 Winning Strategies, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Winning Strategies, Inc.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <rpc/rpc.h>
+#include <rpcsvc/spray.h>
+
+#ifndef SPRAYOVERHEAD
+#define SPRAYOVERHEAD 86
+#endif
+
+static void usage(void);
+static void print_xferstats(unsigned int, int, double);
+
+/* spray buffer */
+char spray_buffer[SPRAYMAX];
+
+/* RPC timeouts */
+struct timeval NO_DEFAULT = { -1, -1 };
+struct timeval ONE_WAY = { 0, 0 };
+struct timeval TIMEOUT = { 25, 0 };
+
+int
+main(int argc, char *argv[])
+{
+ spraycumul host_stats;
+ sprayarr host_array;
+ CLIENT *cl;
+ int c;
+ u_int i;
+ u_int count = 0;
+ int delay = 0;
+ int length = 0;
+ double xmit_time; /* time to receive data */
+
+ while ((c = getopt(argc, argv, "c:d:l:")) != -1) {
+ switch (c) {
+ case 'c':
+ count = atoi(optarg);
+ break;
+ case 'd':
+ delay = atoi(optarg);
+ break;
+ case 'l':
+ length = atoi(optarg);
+ break;
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1) {
+ usage();
+ /* NOTREACHED */
+ }
+
+
+ /* Correct packet length. */
+ if (length > SPRAYMAX) {
+ length = SPRAYMAX;
+ } else if (length < SPRAYOVERHEAD) {
+ length = SPRAYOVERHEAD;
+ } else {
+ /* The RPC portion of the packet is a multiple of 32 bits. */
+ length -= SPRAYOVERHEAD - 3;
+ length &= ~3;
+ length += SPRAYOVERHEAD;
+ }
+
+
+ /*
+ * The default value of count is the number of packets required
+ * to make the total stream size 100000 bytes.
+ */
+ if (!count) {
+ count = 100000 / length;
+ }
+
+ /* Initialize spray argument */
+ host_array.sprayarr_len = length - SPRAYOVERHEAD;
+ host_array.sprayarr_val = spray_buffer;
+
+
+ /* create connection with server */
+ cl = clnt_create(*argv, SPRAYPROG, SPRAYVERS, "udp");
+ if (cl == NULL)
+ errx(1, "%s", clnt_spcreateerror(NULL));
+
+
+ /*
+ * For some strange reason, RPC 4.0 sets the default timeout,
+ * thus timeouts specified in clnt_call() are always ignored.
+ *
+ * The following (undocumented) hack resets the internal state
+ * of the client handle.
+ */
+ clnt_control(cl, CLSET_TIMEOUT, &NO_DEFAULT);
+
+
+ /* Clear server statistics */
+ if (clnt_call(cl, SPRAYPROC_CLEAR, (xdrproc_t)xdr_void, NULL,
+ (xdrproc_t)xdr_void, NULL, TIMEOUT) != RPC_SUCCESS)
+ errx(1, "%s", clnt_sperror(cl, NULL));
+
+
+ /* Spray server with packets */
+ printf ("sending %u packets of lnth %d to %s ...", count, length,
+ *argv);
+ fflush (stdout);
+
+ for (i = 0; i < count; i++) {
+ clnt_call(cl, SPRAYPROC_SPRAY, (xdrproc_t)xdr_sprayarr,
+ &host_array, (xdrproc_t)xdr_void, NULL, ONE_WAY);
+
+ if (delay) {
+ usleep(delay);
+ }
+ }
+
+
+ /* Collect statistics from server */
+ if (clnt_call(cl, SPRAYPROC_GET, (xdrproc_t)xdr_void, NULL,
+ (xdrproc_t)xdr_spraycumul, &host_stats, TIMEOUT) != RPC_SUCCESS)
+ errx(1, "%s", clnt_sperror(cl, NULL));
+
+ xmit_time = host_stats.clock.sec +
+ (host_stats.clock.usec / 1000000.0);
+
+ printf ("\n\tin %.2f seconds elapsed time\n", xmit_time);
+
+
+ /* report dropped packets */
+ if (host_stats.counter != count) {
+ int packets_dropped = count - host_stats.counter;
+
+ printf("\t%d packets (%.2f%%) dropped\n",
+ packets_dropped,
+ 100.0 * packets_dropped / count );
+ } else {
+ printf("\tno packets dropped\n");
+ }
+
+ printf("Sent:");
+ print_xferstats(count, length, xmit_time);
+
+ printf("Rcvd:");
+ print_xferstats(host_stats.counter, length, xmit_time);
+
+ exit (0);
+}
+
+
+static void
+print_xferstats(u_int packets, int packetlen, double xfertime)
+{
+ int datalen;
+ double pps; /* packets per second */
+ double bps; /* bytes per second */
+
+ datalen = packets * packetlen;
+ pps = packets / xfertime;
+ bps = datalen / xfertime;
+
+ printf("\t%.0f packets/sec, ", pps);
+
+ if (bps >= 1024)
+ printf ("%.1fK ", bps / 1024);
+ else
+ printf ("%.0f ", bps);
+
+ printf("bytes/sec\n");
+}
+
+
+static void
+usage(void)
+{
+ fprintf(stderr,
+ "usage: spray [-c count] [-l length] [-d delay] host\n");
+ exit(1);
+}
diff --git a/usr.sbin/stallion/Makefile b/usr.sbin/stallion/Makefile
new file mode 100644
index 0000000..4b017fc
--- /dev/null
+++ b/usr.sbin/stallion/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+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..314a85b
--- /dev/null
+++ b/usr.sbin/stallion/Makefile.inc
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+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..febb8e2
--- /dev/null
+++ b/usr.sbin/stallion/bootcode/Makefile
@@ -0,0 +1,16 @@
+# $FreeBSD$
+
+.SUFFIXES: .uu
+.uu:
+ rm -f ${.TARGET}
+ uudecode ${.IMPSRC}
+
+FILES= 2681.sys cdk.sys
+FILESDIR= ${BOOTDIR}
+CLEANFILES= ${FILES}
+
+MAN= stl.4
+MLINKS= stl.4 stli.4
+MANSUBDIR=/i386
+
+.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..7fd9362
--- /dev/null
+++ b/usr.sbin/stallion/bootcode/stl.4
@@ -0,0 +1,327 @@
+.\" 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd December 2, 1996
+.Os
+.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
+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
+or
+.Nm stli
+drivers will be used. The
+.Nm
+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
+driver.
+ISA board configuration entries for the
+.Nm
+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
+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
+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
+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
+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
+.Pp
+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
+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..d53e932
--- /dev/null
+++ b/usr.sbin/stallion/stlload/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PROG= stlload
+MAN= stlload.8
+MANSUBDIR=/i386
+
+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..e472475
--- /dev/null
+++ b/usr.sbin/stallion/stlload/stlload.8
@@ -0,0 +1,130 @@
+.\" 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd December 2, 1996
+.Os
+.Dt STLLOAD 8 i386
+.Sh NAME
+.Nm stlload
+.Nd "Stallion Technologies multiport serial board down loader"
+.Sh SYNOPSIS
+.Nm
+.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 Stallion EasyConnection 8/64 Host Adapter PCI cards DO NOT require
+firmware to be loaded.
+.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
+.El
+.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..6f327f8
--- /dev/null
+++ b/usr.sbin/stallion/stlload/stlload.c
@@ -0,0 +1,503 @@
+/*****************************************************************************/
+
+/*
+ * 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[] =
+ "$FreeBSD$";
+#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";
+const 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)");
+ printf(" (Perhaps you're trying to download firmare to a PCI card that\n doesn't require this?)\n");
+ 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);
+}
+
+/*****************************************************************************/
+
+int 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..1902900
--- /dev/null
+++ b/usr.sbin/stallion/stlstats/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+PROG= stlstats
+MAN= stlstats.8
+MANSUBDIR=/i386
+
+DPADD= ${LIBNCURSES}
+LDADD= -lncurses
+
+.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..985c67e
--- /dev/null
+++ b/usr.sbin/stallion/stlstats/stlstats.8
@@ -0,0 +1,138 @@
+.\" 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd December 2, 1996
+.Os
+.Dt STLSTATS 8 i386
+.Sh NAME
+.Nm stlstats
+.Nd "Stallion Technologies multiport serial statistics display"
+.Sh SYNOPSIS
+.Nm
+.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
+.El
+.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..59fedbd
--- /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[] =
+ "$FreeBSD$";
+#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);
+}
+
+/*****************************************************************************/
+
+int 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/sysinstall/Makefile b/usr.sbin/sysinstall/Makefile
new file mode 100644
index 0000000..1c4f221
--- /dev/null
+++ b/usr.sbin/sysinstall/Makefile
@@ -0,0 +1,93 @@
+# $FreeBSD$
+
+PROG= sysinstall
+MAN= sysinstall.8
+SRCS= anonFTP.c cdrom.c command.c config.c devices.c dhcp.c \
+ disks.c dispatch.c dist.c dmenu.c doc.c dos.c floppy.c \
+ ftp.c globals.c http.c index.c install.c installUpgrade.c keymap.c \
+ label.c main.c makedevs.c media.c menus.c misc.c modules.c \
+ mouse.c msg.c network.c nfs.c options.c package.c \
+ system.c tape.c tcpip.c termcap.c ttys.c ufs.c usb.c user.c \
+ variable.c wizard.c keymap.h
+
+.if ${MACHINE_ARCH} == "i386"
+SRCS+= pccard.c
+.endif
+
+CFLAGS+= -I${.CURDIR}/../../gnu/lib/libdialog -I.
+CFLAGS+= -DX_AS_PKG -DUSE_GZIP=1
+.if ${MACHINE} == "pc98"
+CFLAGS+= -DPC98
+.endif
+
+DPADD= ${LIBDIALOG} ${LIBNCURSES} ${LIBUTIL} ${LIBDISK} ${LIBFTPIO}
+LDADD= -ldialog -lncurses -lutil -ldisk -lftpio
+
+CLEANFILES= makedevs.c rtermcap
+CLEANFILES+= keymap.tmp keymap.h
+
+makedevs.c: Makefile rtermcap
+ echo '#include <sys/types.h>' > makedevs.c
+ ./rtermcap ansi | \
+ file2c 'const char termcap_ansi[] = {' ',0};' \
+ >> makedevs.c
+ ./rtermcap cons25w | \
+ file2c 'const char termcap_cons25w[] = {' ',0};' \
+ >> makedevs.c
+ ./rtermcap cons25 | \
+ file2c 'const char termcap_cons25[] = {' ',0};' \
+ >> makedevs.c
+ ./rtermcap cons25-m | \
+ file2c 'const char termcap_cons25_m[] = {' ',0};' \
+ >> makedevs.c
+ ./rtermcap cons25r | \
+ file2c 'const char termcap_cons25r[] = {' ',0};' \
+ >> makedevs.c
+ ./rtermcap cons25r-m | \
+ file2c 'const char termcap_cons25r_m[] = {' ',0};' \
+ >> makedevs.c
+ ./rtermcap cons25l1 | \
+ file2c 'const char termcap_cons25l1[] = {' ',0};' \
+ >> makedevs.c
+ ./rtermcap cons25l1-m | \
+ file2c 'const char termcap_cons25l1_m[] = {' ',0};' \
+ >> makedevs.c
+ ./rtermcap vt100 | \
+ file2c 'const char termcap_vt100[] = {' ',0};' \
+ >> makedevs.c
+ ./rtermcap xterm | \
+ file2c 'const char termcap_xterm[] = {' ',0};' \
+ >> makedevs.c
+
+build-tools: rtermcap
+
+rtermcap: rtermcap.c
+ ${CC} -o ${.TARGET} ${.ALLSRC} -ltermcap
+
+.if ${MACHINE} == "pc98"
+KEYMAPS= jp.pc98 jp.pc98.iso
+.else
+KEYMAPS= be.iso bg.bds.ctrlcaps bg.phonetic.ctrlcaps br275.iso \
+ cs.latin2.qwertz danish.iso finnish.iso fr.iso \
+ german.iso hr.iso hu.iso2.101keys it.iso icelandic.iso jp.106 \
+ norwegian.iso pl_PL.ISO8859-2 pt.iso ru.koi8-r si.iso \
+ spanish.iso swedish.iso swissfrench.iso swissgerman.iso ua.koi8-u \
+ ua.koi8-u.shift.alt uk.iso us.dvorak us.iso us.pc-ctrl us.unix
+.endif
+
+keymap.h:
+ rm -f keymap.tmp
+ for map in ${KEYMAPS} ; do \
+ KEYMAP_PATH=${.CURDIR}/../../share/syscons/keymaps \
+ kbdcontrol -L $$map | \
+ sed -e '/^static accentmap_t/,$$d' >> keymap.tmp ; \
+ done
+ echo "static struct keymapInfo keymapInfos[] = {" >> keymap.tmp
+ for map in ${KEYMAPS} ; do \
+ echo -n ' { "'$$map'", ' >> keymap.tmp ; \
+ echo "&keymap_$$map }," | tr '[-.]' '_' >> keymap.tmp ; \
+ done
+ ( echo " { 0 }"; echo "};" ; echo "" ) >> keymap.tmp
+ mv keymap.tmp keymap.h
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/sysinstall/anonFTP.c b/usr.sbin/sysinstall/anonFTP.c
new file mode 100644
index 0000000..1902a5d
--- /dev/null
+++ b/usr.sbin/sysinstall/anonFTP.c
@@ -0,0 +1,327 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Coranth Gryphon. All rights reserved.
+ * Copyright (c) 1996
+ * Jordan K. Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR THEIR PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <sys/param.h>
+#include <pwd.h>
+#include <grp.h>
+
+/* This doesn't change until FTP itself changes */
+
+#define FTP_NAME "ftp"
+#define MOTD_FILE "ftpmotd"
+
+/* These change if we want to use different defaults */
+
+#define FTP_UID 14
+#define FTP_GID 5
+#define FTP_GROUP "operator"
+#define FTP_UPLOAD "incoming"
+#define FTP_COMMENT "Anonymous FTP Admin"
+#define FTP_HOMEDIR "/var/ftp"
+
+#define ANONFTP_HELPFILE "anonftp"
+
+/* Set up the structure to hold configuration information */
+/* Note that this is only what we could fit onto the one screen */
+
+typedef struct
+{
+ char homedir[64]; /* Home Dir for Anon FTP */
+ char group[32]; /* Group */
+ char uid[8]; /* UID */
+ char comment[64]; /* PWD Comment */
+ char upload[32]; /* Upload Dir */
+} FTPConf;
+
+static FTPConf tconf;
+
+#define ANONFTP_HOMEDIR_LEN 64
+#define ANONFTP_COMMENT_LEN 64
+#define ANONFTP_UPLOAD_LEN 32
+#define ANONFTP_GROUP_LEN 32
+#define ANONFTP_UID_LEN 8
+
+static int okbutton, cancelbutton;
+
+/* What the screen size is meant to be */
+#define ANONFTP_DIALOG_Y 0
+#define ANONFTP_DIALOG_X 8
+#define ANONFTP_DIALOG_WIDTH COLS - 16
+#define ANONFTP_DIALOG_HEIGHT LINES - 2
+
+static Layout layout[] = {
+#define LAYOUT_UID 0
+ { 2, 3, 8, ANONFTP_UID_LEN - 1,
+ "UID:", "What user ID to assign to FTP Admin",
+ tconf.uid, STRINGOBJ, NULL },
+#define LAYOUT_GROUP 1
+ { 2, 15, 15, ANONFTP_GROUP_LEN - 1,
+ "Group:", "Group name that ftp process belongs to",
+ tconf.group, STRINGOBJ, NULL },
+#define LAYOUT_COMMENT 2
+ { 2, 35, 24, ANONFTP_COMMENT_LEN - 1,
+ "Comment:", "Password file comment for FTP Admin",
+ tconf.comment, STRINGOBJ, NULL },
+#define LAYOUT_HOMEDIR 3
+ { 9, 10, 43, ANONFTP_HOMEDIR_LEN - 1,
+ "FTP Root Directory:",
+ "The top directory to chroot to when doing anonymous ftp",
+ tconf.homedir, STRINGOBJ, NULL },
+#define LAYOUT_UPLOAD 4
+ { 14, 20, 22, ANONFTP_UPLOAD_LEN - 1,
+ "Upload Subdirectory:", "Designated sub-directory that holds uploads",
+ tconf.upload, STRINGOBJ, NULL },
+#define LAYOUT_OKBUTTON 5
+ { 19, 15, 0, 0,
+ "OK", "Select this if you are happy with these settings",
+ &okbutton, BUTTONOBJ, NULL },
+#define LAYOUT_CANCELBUTTON 6
+ { 19, 35, 0, 0,
+ "CANCEL", "Select this if you wish to cancel this screen",
+ &cancelbutton, BUTTONOBJ, NULL },
+ { NULL },
+};
+
+static int
+createFtpUser(void)
+{
+ struct passwd *tpw;
+ struct group *tgrp;
+ char pwline[256];
+ char *tptr;
+ int gid;
+ FILE *fptr;
+
+ if ((gid = atoi(tconf.group)) <= 0) {
+ if (!(tgrp = getgrnam(tconf.group))) {
+ /* group does not exist, create it by name */
+
+ tptr = msgGetInput("14", "What group ID to use for group %s ?", tconf.group);
+ if (tptr && *tptr && ((gid = atoi(tptr)) > 0)) {
+ if ((fptr = fopen(_PATH_GROUP,"a"))) {
+ fprintf(fptr,"%s:*:%d:%s\n",tconf.group,gid,FTP_NAME);
+ fclose(fptr);
+ }
+ }
+ else
+ gid = FTP_GID;
+ }
+ else
+ gid = tgrp->gr_gid;
+ }
+ else if (!getgrgid(gid)) {
+ /* group does not exist, create it by number */
+
+ tptr = msgGetInput("14", "What group name to use for gid %d ?", gid);
+ if (tptr && *tptr) {
+ SAFE_STRCPY(tconf.group, tptr);
+ if ((tgrp = getgrnam(tconf.group))) {
+ gid = tgrp->gr_gid;
+ }
+ else if ((fptr = fopen(_PATH_GROUP,"a"))) {
+ fprintf(fptr,"%s:*:%d:%s\n",tconf.group,gid,FTP_NAME);
+ fclose(fptr);
+ }
+ }
+ }
+
+ if ((tpw = getpwnam(FTP_NAME))) {
+ if (tpw->pw_uid != FTP_UID)
+ msgConfirm("FTP user already exists with a different uid.");
+
+ return DITEM_SUCCESS; /* succeeds if already exists */
+ }
+
+ sprintf(pwline, "%s:*:%s:%d::0:0:%s:%s:/nonexistent\n", FTP_NAME, tconf.uid, gid, tconf.comment, tconf.homedir);
+
+ fptr = fopen(_PATH_MASTERPASSWD,"a");
+ if (! fptr) {
+ msgConfirm("Could not open master password file.");
+ return DITEM_FAILURE;
+ }
+ fprintf(fptr, "%s", pwline);
+ fclose(fptr);
+ msgNotify("Remaking password file: %s", _PATH_MASTERPASSWD);
+ vsystem("pwd_mkdb -p %s", _PATH_MASTERPASSWD);
+ return DITEM_SUCCESS | DITEM_RESTORE;
+}
+
+/* This is it - how to get the setup values */
+static int
+anonftpOpenDialog(void)
+{
+ WINDOW *ds_win;
+ ComposeObj *obj = NULL;
+ int n = 0, cancel = FALSE;
+ int max;
+ char title[80];
+ WINDOW *w = savescr();
+
+ /* We need a curses window */
+ if (!(ds_win = openLayoutDialog(ANONFTP_HELPFILE, " Anonymous FTP Configuration ",
+ ANONFTP_DIALOG_X, ANONFTP_DIALOG_Y, ANONFTP_DIALOG_WIDTH, ANONFTP_DIALOG_HEIGHT))) {
+ beep();
+ msgConfirm("Cannot open anonymous ftp dialog window!!");
+ restorescr(w);
+ return DITEM_FAILURE;
+ }
+
+ /* Draw a sub-box for the path configuration */
+ draw_box(ds_win, ANONFTP_DIALOG_Y + 7, ANONFTP_DIALOG_X + 8,
+ ANONFTP_DIALOG_HEIGHT - 11, ANONFTP_DIALOG_WIDTH - 17,
+ dialog_attr, border_attr);
+ wattrset(ds_win, dialog_attr);
+ sprintf(title, " Path Configuration ");
+ mvwaddstr(ds_win, ANONFTP_DIALOG_Y + 7, ANONFTP_DIALOG_X + 22, title);
+
+ /** Initialize the config Data Structure **/
+ bzero(&tconf, sizeof(tconf));
+
+ SAFE_STRCPY(tconf.group, FTP_GROUP);
+ SAFE_STRCPY(tconf.upload, FTP_UPLOAD);
+ SAFE_STRCPY(tconf.comment, FTP_COMMENT);
+ SAFE_STRCPY(tconf.homedir, FTP_HOMEDIR);
+ sprintf(tconf.uid, "%d", FTP_UID);
+
+ /* Some more initialisation before we go into the main input loop */
+ obj = initLayoutDialog(ds_win, layout, ANONFTP_DIALOG_X, ANONFTP_DIALOG_Y, &max);
+
+ cancelbutton = okbutton = 0;
+ while (layoutDialogLoop(ds_win, layout, &obj, &n, max, &cancelbutton, &cancel));
+
+ /* Clear this crap off the screen */
+ delwin(ds_win);
+ use_helpfile(NULL);
+ restorescr(w);
+ if (cancel)
+ return DITEM_FAILURE;
+ return DITEM_SUCCESS;
+}
+
+int
+configAnonFTP(dialogMenuItem *self __unused)
+{
+ int i;
+
+
+ if (msgYesNo("Anonymous FTP permits un-authenticated users to connect to the system\n"
+ "FTP server, if FTP service is enabled. Anonymous users are\n"
+ "restricted to a specific subset of the file system, and the default\n"
+ "configuration provides a drop-box incoming directory to which uploads\n"
+ "are permitted. You must seperately enable both inetd(8), and enable\n"
+ "ftpd(8) in inetd.conf(5) for FTP services to be available. If you\n"
+ "did not do so earlier, you will have the opportunity to enable inetd(8)\n"
+ "again later.\n\n"
+ "Do you wish to continue configuring anonymous FTP?")) {
+ return DITEM_FAILURE;
+ }
+
+ /* Be optimistic */
+ i = DITEM_SUCCESS;
+
+ i = anonftpOpenDialog();
+ if (DITEM_STATUS(i) != DITEM_SUCCESS) {
+ msgConfirm("Configuration of Anonymous FTP cancelled per user request.");
+ return i;
+ }
+
+ /*** Use defaults for any invalid values ***/
+ if (atoi(tconf.uid) <= 0)
+ sprintf(tconf.uid, "%d", FTP_UID);
+
+ if (!tconf.group[0])
+ SAFE_STRCPY(tconf.group, FTP_GROUP);
+
+ if (!tconf.upload[0])
+ SAFE_STRCPY(tconf.upload, FTP_UPLOAD);
+
+ /*** If the user did not specify a directory, use default ***/
+
+ if (tconf.homedir[strlen(tconf.homedir) - 1] == '/')
+ tconf.homedir[strlen(tconf.homedir) - 1] = '\0';
+
+ if (!tconf.homedir[0])
+ SAFE_STRCPY(tconf.homedir, FTP_HOMEDIR);
+
+ /*** If HomeDir does not exist, create it ***/
+
+ if (!directory_exists(tconf.homedir))
+ vsystem("mkdir -p %s", tconf.homedir);
+
+ if (directory_exists(tconf.homedir)) {
+ msgNotify("Configuring %s for use by anon FTP.", tconf.homedir);
+ vsystem("chmod 555 %s && chown root.%s %s", tconf.homedir, tconf.group, tconf.homedir);
+ vsystem("mkdir %s/bin && chmod 555 %s/bin", tconf.homedir, tconf.homedir);
+ vsystem("cp /bin/ls %s/bin && chmod 111 %s/bin/ls", tconf.homedir, tconf.homedir);
+ vsystem("cp /bin/date %s/bin && chmod 111 %s/bin/date", tconf.homedir, tconf.homedir);
+ vsystem("mkdir %s/etc && chmod 555 %s/etc", tconf.homedir, tconf.homedir);
+ vsystem("mkdir -p %s/pub", tconf.homedir);
+ vsystem("mkdir -p %s/%s", tconf.homedir, tconf.upload);
+ vsystem("chmod 1777 %s/%s", tconf.homedir, tconf.upload);
+
+ if (DITEM_STATUS(createFtpUser()) == DITEM_SUCCESS) {
+ msgNotify("Copying password information for anon FTP.");
+ vsystem("awk -F: '{if ($3 < 10 || $1 == \"ftp\") print $0}' /etc/passwd > %s/etc/passwd && chmod 444 %s/etc/passwd", tconf.homedir, tconf.homedir);
+ vsystem("awk -F: '{if ($3 < 100) print $0}' /etc/group > %s/etc/group && chmod 444 %s/etc/group", tconf.homedir, tconf.homedir);
+ vsystem("chown -R root.%s %s/pub", tconf.group, tconf.homedir);
+ }
+ else {
+ msgConfirm("Unable to create FTP user! Anonymous FTP setup failed.");
+ i = DITEM_FAILURE;
+ }
+
+ if (!msgYesNo("Create a welcome message file for anonymous FTP users?")) {
+ char cmd[256];
+ vsystem("echo Your welcome message here. > %s/etc/%s", tconf.homedir, MOTD_FILE);
+ sprintf(cmd, "%s %s/etc/%s", variable_get(VAR_EDITOR), tconf.homedir, MOTD_FILE);
+ if (!systemExecute(cmd))
+ i = DITEM_SUCCESS;
+ else
+ i = DITEM_FAILURE;
+ }
+ }
+ else {
+ msgConfirm("Invalid Directory: %s\n"
+ "Anonymous FTP will not be set up.", tconf.homedir);
+ i = DITEM_FAILURE;
+ }
+ if (DITEM_STATUS(i) == DITEM_SUCCESS)
+ variable_set2("anon_ftp", "YES", 0);
+ return i | DITEM_RESTORE;
+}
diff --git a/usr.sbin/sysinstall/cdrom.c b/usr.sbin/sysinstall/cdrom.c
new file mode 100644
index 0000000..3b55be7
--- /dev/null
+++ b/usr.sbin/sysinstall/cdrom.c
@@ -0,0 +1,208 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated to essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ * Copyright (c) 1995
+ * Gary J Palmer. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/* These routines deal with getting things off of CDROM media */
+
+#include "sysinstall.h"
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <sys/param.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <grp.h>
+#include <fcntl.h>
+#include <libutil.h>
+
+#define CD9660
+#include <sys/mount.h>
+#include <isofs/cd9660/cd9660_mount.h>
+#undef CD9660
+
+static Boolean cdromMounted;
+static Boolean previouslyMounted; /* Was the disc already mounted? */
+static char mountpoint[MAXPATHLEN] = "/dist";
+
+static properties
+read_props(char *name)
+{
+ int fd;
+ properties n;
+
+ fd = open(name, O_RDONLY);
+ if (fd == -1)
+ return NULL;
+ n = properties_read(fd);
+ close(fd);
+ return n;
+}
+
+Boolean
+mediaInitCDROM(Device *dev)
+{
+ struct iso_args args;
+ properties cd_attr = NULL;
+ char *cp = NULL;
+ Boolean readInfo = TRUE;
+ static Boolean bogusCDOK = FALSE;
+
+ if (cdromMounted)
+ return TRUE;
+
+ Mkdir(mountpoint);
+ bzero(&args, sizeof(args));
+ args.fspec = dev->devname;
+ args.flags = 0;
+ if (mount("cd9660", mountpoint, MNT_RDONLY, (caddr_t) &args) == -1) {
+ if (errno == EINVAL) {
+ msgConfirm("The disc in your drive looks more like an Audio disc than a FreeBSD release.");
+ return FALSE;
+ } else if (errno == EBUSY) {
+ /* Perhaps the CDROM drive is already mounted as /cdrom */
+ if (file_readable("/cdrom/cdrom.inf")) {
+ previouslyMounted = TRUE;
+ strlcpy(mountpoint, "/cdrom", 7);
+ }
+ } else {
+ msgConfirm("Error mounting %s on %s: %s (%u)", dev->devname, mountpoint, strerror(errno), errno);
+ return FALSE;
+ }
+ }
+ cdromMounted = TRUE;
+
+ if (!file_readable(string_concat(mountpoint, "/cdrom.inf")) && !bogusCDOK) {
+ if (msgYesNo("Warning: The disc currently in the drive is either not a FreeBSD\n"
+ "disc or it is an older (pre 2.1.5) FreeBSD CD which does not\n"
+ "have a version number on it. Do you wish to use this disc anyway?") != 0) {
+ if (!previouslyMounted)
+ unmount(mountpoint, MNT_FORCE);
+ cdromMounted = FALSE;
+ return FALSE;
+ }
+ else {
+ readInfo = FALSE;
+ bogusCDOK = TRUE;
+ }
+ }
+
+ if (readInfo) {
+ if (!(cd_attr = read_props(string_concat(mountpoint, "/cdrom.inf")))
+ || !(cp = property_find(cd_attr, "CD_VERSION"))) {
+ msgConfirm("Unable to find a %s/cdrom.inf file.\n"
+ "Either this is not a FreeBSD disc, there is a problem with\n"
+ "the CDROM driver or something is wrong with your hardware.\n"
+ "Please fix this problem (check the console logs on VTY2) and\n"
+ "try again.", mountpoint);
+ }
+ else {
+ if (variable_cmp(VAR_RELNAME, cp) &&
+ variable_cmp(VAR_RELNAME, "any") &&
+ variable_cmp(cp, "any") &&
+ !bogusCDOK) {
+ msgConfirm("Warning: The version of the FreeBSD disc currently in the drive\n"
+ "(%s) does not match the version of the boot floppy\n"
+ "(%s).\n\n"
+ "If this is intentional, to avoid this message in the future\n"
+ "please visit the Options editor to set the boot floppy version\n"
+ "string to match that of the disc before selecting it as your\n"
+ "installation media.", cp, variable_get(VAR_RELNAME));
+
+ if (msgYesNo("Would you like to try and use this disc anyway?") != 0) {
+ if (!previouslyMounted)
+ unmount(mountpoint, MNT_FORCE);
+ cdromMounted = FALSE;
+ properties_free(cd_attr);
+ return FALSE;
+ }
+ else
+ bogusCDOK = TRUE;
+ }
+ if ((cp = property_find(cd_attr, "CD_MACHINE_ARCH")) != NULL) {
+ if (strcmp(cp, "any") &&
+#ifdef __alpha__
+ strcmp(cp, "alpha")) {
+#else
+ strcmp(cp, "x86")) {
+#endif
+ msgConfirm("Fatal: The FreeBSD install CD/DVD currently in the drive\n"
+ "is for the %s architecture, not the machine you're using.\n\n"
+
+ "Please use the correct installation CD/DVD for your machine type.", cp);
+
+ if (!previouslyMounted)
+ unmount(mountpoint, MNT_FORCE);
+ cdromMounted = FALSE;
+ properties_free(cd_attr);
+ return FALSE;
+ }
+ }
+ if ((cp = property_find(cd_attr, "CD_VOLUME")) != NULL) {
+ dev->volume = atoi(cp);
+ /* XXX - Sanity check the volume here? */
+ msgDebug("CD Volume %d initialized!\n", dev->volume);
+ } else {
+ dev->volume = 0;
+ }
+ }
+ }
+ if (cd_attr)
+ properties_free(cd_attr);
+ return TRUE;
+}
+
+FILE *
+mediaGetCDROM(Device *dev, char *file, Boolean probe)
+{
+ return mediaGenericGet(mountpoint, file);
+}
+
+void
+mediaShutdownCDROM(Device *dev)
+{
+ if (!cdromMounted)
+ return;
+
+ if (previouslyMounted) {
+ cdromMounted = FALSE;
+ return;
+ }
+
+ if (unmount(mountpoint, MNT_FORCE) != 0)
+ msgConfirm("Could not unmount the CDROM/DVD from %s: %s", mountpoint, strerror(errno));
+ else
+ cdromMounted = FALSE;
+}
diff --git a/usr.sbin/sysinstall/command.c b/usr.sbin/sysinstall/command.c
new file mode 100644
index 0000000..65d57ae
--- /dev/null
+++ b/usr.sbin/sysinstall/command.c
@@ -0,0 +1,184 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+
+#define MAX_NUM_COMMANDS 10
+
+typedef struct {
+ char key[FILENAME_MAX];
+ struct {
+ enum { CMD_SHELL, CMD_FUNCTION } type;
+ void *ptr, *data;
+ } cmds[MAX_NUM_COMMANDS];
+ int ncmds;
+} Command;
+
+#define MAX_CMDS 200
+static Command *commandStack[MAX_CMDS];
+int numCommands;
+
+/* Nuke the command stack */
+void
+command_clear(void)
+{
+ int i, j;
+
+ for (i = 0; i < numCommands; i++)
+ for (j = 0; j < commandStack[i]->ncmds; j++)
+ if (commandStack[i]->cmds[j].type == CMD_SHELL)
+ free(commandStack[i]->cmds[j].ptr);
+ free(commandStack[i]);
+ numCommands = 0;
+}
+
+static void
+addit(char *key, int type, void *cmd, void *data)
+{
+ int i;
+
+ /* First, look for the key already present and add a command to it if found */
+ for (i = 0; i < numCommands; i++) {
+ if (!strcmp(commandStack[i]->key, key)) {
+ if (commandStack[i]->ncmds == MAX_NUM_COMMANDS)
+ msgFatal("More than %d commands stacked up behind %s??", MAX_NUM_COMMANDS, key);
+ commandStack[i]->cmds[commandStack[i]->ncmds].type = type;
+ commandStack[i]->cmds[commandStack[i]->ncmds].ptr = cmd;
+ commandStack[i]->cmds[commandStack[i]->ncmds].data = data;
+ ++(commandStack[i]->ncmds);
+ return;
+ }
+ }
+ if (numCommands == MAX_CMDS)
+ msgFatal("More than %d commands accumulated??", MAX_CMDS);
+
+ /* If we fell to here, it's a new key */
+ commandStack[numCommands] = safe_malloc(sizeof(Command));
+ strcpy(commandStack[numCommands]->key, key);
+ commandStack[numCommands]->ncmds = 1;
+ commandStack[numCommands]->cmds[0].type = type;
+ commandStack[numCommands]->cmds[0].ptr = cmd;
+ commandStack[numCommands]->cmds[0].data = data;
+ ++numCommands;
+}
+
+/* Add a shell command under a given key */
+void
+command_shell_add(char *key, char *fmt, ...)
+{
+ va_list args;
+ char *cmd;
+
+ cmd = (char *)safe_malloc(256);
+ va_start(args, fmt);
+ vsnprintf(cmd, 256, fmt, args);
+ va_end(args);
+
+ addit(key, CMD_SHELL, cmd, NULL);
+}
+
+/* Add a shell command under a given key */
+void
+command_func_add(char *key, commandFunc func, void *data)
+{
+ addit(key, CMD_FUNCTION, func, data);
+}
+
+static int
+sort_compare(Command *p1, Command *p2)
+{
+ if (!p1 && !p2)
+ return 0;
+ else if (!p1 && p2) /* NULL has a "greater" value for commands */
+ return 1;
+ else if (p1 && !p2)
+ return -1;
+ else
+ return strcmp(p1->key, p2->key);
+}
+
+void
+command_sort(void)
+{
+ int i, j;
+
+ commandStack[numCommands] = NULL;
+ /* Just do a crude bubble sort since the list is small */
+ for (i = 0; i < numCommands; i++) {
+ for (j = 0; j < numCommands; j++) {
+ if (sort_compare(commandStack[j], commandStack[j + 1]) > 0) {
+ Command *tmp = commandStack[j];
+
+ commandStack[j] = commandStack[j + 1];
+ commandStack[j + 1] = tmp;
+ }
+ }
+ }
+}
+
+/* Run all accumulated commands in sorted order */
+void
+command_execute(void)
+{
+ int i, j, ret;
+ commandFunc func;
+
+ for (i = 0; i < numCommands; i++) {
+ for (j = 0; j < commandStack[i]->ncmds; j++) {
+ /* If it's a shell command, run system on it */
+ if (commandStack[i]->cmds[j].type == CMD_SHELL) {
+ msgNotify("Doing %s", (char *)commandStack[i]->cmds[j].ptr);
+ ret = vsystem("%s", (char *)commandStack[i]->cmds[j].ptr);
+ if (isDebug())
+ msgDebug("Command `%s' returns status %d\n",
+ (char *)commandStack[i]->cmds[j].ptr, ret);
+ }
+ else {
+ /* It's a function pointer - call it with the key and
+ the data */
+ func = (commandFunc)commandStack[i]->cmds[j].ptr;
+ if (isDebug())
+ msgDebug("%p: Execute(%s, %s)",
+ func, commandStack[i]->key,
+ (char *)commandStack[i]->cmds[j].data);
+ ret = (*func)(commandStack[i]->key, commandStack[i]->cmds[j].data);
+ if (isDebug())
+ msgDebug("Function @ %p returns status %d\n",
+ commandStack[i]->cmds[j].ptr, ret);
+ }
+ }
+ }
+}
diff --git a/usr.sbin/sysinstall/config.c b/usr.sbin/sysinstall/config.c
new file mode 100644
index 0000000..8878d5b
--- /dev/null
+++ b/usr.sbin/sysinstall/config.c
@@ -0,0 +1,1021 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <sys/disklabel.h>
+#include <sys/wait.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/fcntl.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/mount.h>
+#include <time.h>
+
+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 "msdosfs";
+ else if (c1->type == part) {
+ if (c1->subtype != FS_SWAP)
+ return "ufs";
+ else
+ return "swap";
+ }
+ return "bogus";
+}
+
+static char *
+fstype_short(Chunk *c1)
+{
+ if (c1->type == part) {
+ if (c1->subtype != FS_SWAP) {
+ if (rootdev_is_od == 0 && strncmp(c1->name, "od", 2) == 0)
+ return "rw,noauto";
+ else
+ return "rw";
+ }
+ else
+ return "sw";
+ }
+ else if (c1->type == fat) {
+ if (strncmp(c1->name, "od", 2) == 0)
+ return "ro,noauto";
+ else
+ return "ro";
+ }
+ 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\t\tcd9660\tro,noauto\t0\t0\n", devs[i]->name, cdname);
+ }
+
+ fclose(fstab);
+ if (isDebug())
+ msgDebug("Wrote out /etc/fstab file\n");
+ return DITEM_SUCCESS;
+}
+
+/* Do the work of sucking in a config file.
+ * config is the filename to read in.
+ * lines is a fixed (max) sized array of char*
+ * returns number of lines read. line contents
+ * are malloc'd and must be freed by the caller.
+ */
+int
+readConfig(char *config, char **lines, int max)
+{
+ FILE *fp;
+ char line[256];
+ int i, nlines;
+
+ fp = fopen(config, "r");
+ if (!fp)
+ return -1;
+
+ nlines = 0;
+ /* Read in the entire file */
+ for (i = 0; i < max; i++) {
+ if (!fgets(line, sizeof line, fp))
+ break;
+ lines[nlines++] = strdup(line);
+ }
+ fclose(fp);
+ if (isDebug())
+ msgDebug("readConfig: Read %d lines from %s.\n", nlines, config);
+ return nlines;
+}
+
+#define MAX_LINES 2000 /* Some big number we're not likely to ever reach - I'm being really lazy here, I know */
+
+static void
+readConfigFile(char *config, int marked)
+{
+ char *lines[MAX_LINES], *cp, *cp2;
+ int i, nlines;
+
+ nlines = readConfig(config, lines, MAX_LINES);
+ if (nlines == -1)
+ return;
+
+ for (i = 0; i < nlines; i++) {
+ /* Skip the comments & non-variable settings */
+ if (lines[i][0] == '#' || !(cp = index(lines[i], '='))) {
+ free(lines[i]);
+ continue;
+ }
+ *cp++ = '\0';
+ /* Find quotes */
+ if ((cp2 = index(cp, '"')) || (cp2 = index(cp, '\047'))) {
+ cp = cp2 + 1;
+ cp2 = index(cp, *cp2);
+ }
+ /* If valid quotes, use it */
+ if (cp2) {
+ *cp2 = '\0';
+ /* If we have a legit value, set it */
+ if (strlen(cp))
+ variable_set2(lines[i], cp, marked);
+ }
+ free(lines[i]);
+ }
+}
+
+/* Load the environment from rc.conf file(s) */
+void
+configEnvironmentRC_conf(void)
+{
+ static struct {
+ char *fname;
+ int marked;
+ } configs[] = {
+ { "/etc/defaults/rc.conf", 0 },
+ { "/etc/rc.conf", 0 },
+ { "/etc/rc.conf.local", 0 },
+ { NULL, 0 },
+ };
+ int i;
+
+ for (i = 0; configs[i].fname; i++) {
+ if (file_readable(configs[i].fname))
+ readConfigFile(configs[i].fname, configs[i].marked);
+ }
+}
+
+/* Load the environment from a resolv.conf file */
+void
+configEnvironmentResolv(char *config)
+{
+ char *lines[MAX_LINES];
+ int i, nlines;
+
+ nlines = readConfig(config, lines, MAX_LINES);
+ if (nlines == -1)
+ return;
+ for (i = 0; i < nlines; i++) {
+ Boolean name_set = variable_get(VAR_NAMESERVER) ? 1 : 0;
+
+ if (!strncmp(lines[i], "domain", 6) && !variable_get(VAR_DOMAINNAME))
+ variable_set2(VAR_DOMAINNAME, string_skipwhite(string_prune(lines[i] + 6)), 0);
+ else if (!name_set && !strncmp(lines[i], "nameserver", 10)) {
+ /* Only take the first nameserver setting - we're lame */
+ variable_set2(VAR_NAMESERVER, string_skipwhite(string_prune(lines[i] + 10)), 0);
+ }
+ free(lines[i]);
+ }
+}
+
+/* Version of below for dispatch routines */
+int
+configRC(dialogMenuItem *unused)
+{
+ configRC_conf();
+ return DITEM_SUCCESS;
+}
+
+void
+configRC_conf(void)
+{
+ FILE *rcSite;
+ Variable *v;
+ int write_header;
+ time_t t_loc;
+ char *cp;
+ static int did_marker = 0;
+ time_t tp;
+
+ configTtys();
+ 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, not to /etc/defaults/rc.conf.\n\n");
+ fprintf(rcSite, "# Enable network daemons for user convenience.\n");
+ if ((t_loc = time(NULL)) != -1 && (cp = ctime(&t_loc)))
+ fprintf(rcSite, "# Created: %s", cp);
+ }
+
+ /* Now do variable substitutions */
+ for (v = VarHead; v; v = v->next) {
+ if (v->dirty) {
+ if (!did_marker) {
+ time(&tp);
+ fprintf(rcSite, "# -- sysinstall generated deltas -- # "
+ "%s", ctime(&tp));
+ did_marker = 1;
+ }
+ fprintf(rcSite, "%s=\"%s\"\n", v->name, v->value);
+ v->dirty = 0;
+ }
+ }
+ fclose(rcSite);
+ /* Tidy up the resulting file if it's late enough in the installation
+ for sort and uniq to be available */
+ if (RunningAsInit && file_readable("/usr/bin/sort") && file_readable("/usr/bin/uniq"))
+ (void)vsystem("sort /etc/rc.conf | uniq > /etc/rc.conf.new && mv /etc/rc.conf.new /etc/rc.conf");
+}
+
+int
+configSaver(dialogMenuItem *self)
+{
+ variable_set((char *)self->data, 1);
+ if (!variable_get(VAR_BLANKTIME))
+ variable_set2(VAR_BLANKTIME, "300", 1);
+ return DITEM_SUCCESS;
+}
+
+int
+configSaverTimeout(dialogMenuItem *self)
+{
+ return (variable_get_value(VAR_BLANKTIME,
+ "Enter time-out period in seconds for screen saver", 1) ?
+ DITEM_SUCCESS : DITEM_FAILURE);
+}
+
+int
+configNTP(dialogMenuItem *self)
+{
+ int status;
+
+ status = variable_get_value(VAR_NTPDATE_FLAGS,
+ "Enter the name of an NTP server", 1)
+ ? DITEM_SUCCESS : DITEM_FAILURE;
+ if (status == DITEM_SUCCESS) {
+ static char tmp[255];
+
+ snprintf(tmp, sizeof(tmp), "ntpdate_enable=YES,ntpdate_flags=%s",
+ variable_get(VAR_NTPDATE_FLAGS));
+ self->data = tmp;
+ dmenuSetVariables(self);
+ }
+ return status;
+}
+
+int
+configUsers(dialogMenuItem *self)
+{
+ WINDOW *w = savescr();
+
+ dialog_clear_norefresh();
+ dmenuOpenSimple(&MenuUsermgmt, FALSE);
+ restorescr(w);
+ return DITEM_SUCCESS;
+}
+
+int
+configLinux(dialogMenuItem *self)
+{
+ WINDOW *w = savescr();
+ int i;
+
+ dialog_clear_norefresh();
+ variable_set2(VAR_LINUX_ENABLE, "YES", 1);
+ Mkdir("/compat/linux");
+ msgNotify("Installing Linux compatibility library...");
+ i = package_add("linux_base");
+ restorescr(w);
+ return i;
+}
+
+int
+configSecurity(dialogMenuItem *self)
+{
+ WINDOW *w = savescr();
+
+ dialog_clear_norefresh();
+ dmenuOpenSimple(&MenuSecurity, FALSE);
+ restorescr(w);
+ return DITEM_SUCCESS;
+}
+
+int
+configSecurityProfile(dialogMenuItem *self)
+{
+ WINDOW *w = savescr();
+
+ dialog_clear_norefresh();
+ dmenuOpenSimple(&MenuSecurityProfile, FALSE);
+ restorescr(w);
+ return DITEM_SUCCESS;
+}
+
+/* Use the most extreme security settings */
+int
+configSecurityExtreme(dialogMenuItem *self)
+{
+ WINDOW *w = savescr();
+
+ variable_set2("nfs_server_enable", "NO", 1);
+ variable_set2("sendmail_enable", "NO", 1);
+ variable_set2("sshd_enable", "NO", 1);
+ variable_set2("kern_securelevel_enable", "YES", 1);
+ variable_set2("kern_securelevel", "2", 1);
+
+ if (self)
+ msgConfirm("Extreme security settings have been selected.\n\n"
+ "Sendmail, sshd, and NFS services have been disabled, and\n"
+ "securelevels have been enabled.\n\n"
+ "PLEASE NOTE that this still does not save you from having\n"
+ "to properly secure your system in other ways or exercise\n"
+ "due diligence in your administration, this simply picks\n"
+ "a more secure set of out-of-box defaults to start with.\n\n"
+ "To change any of these settings later, edit /etc/rc.conf");
+
+ restorescr(w);
+ return DITEM_SUCCESS;
+}
+
+int
+configSecurityModerate(dialogMenuItem *self)
+{
+ WINDOW *w = savescr();
+
+ variable_set2("sendmail_enable", "YES", 1);
+ variable_set2("sshd_enable", "YES", 1);
+ variable_set2("kern_securelevel_enable", "NO", 1);
+
+ if (self)
+ msgConfirm("Moderate security settings have been selected.\n\n"
+ "Sendmail and sshd have been enabled, securelevels are\n"
+ "disabled, and NFS server settings have been left intact.\n\n"
+ "PLEASE NOTE that this still does not save you from having\n"
+ "to properly secure your system in other ways or exercise\n"
+ "due diligence in your administration, this simply picks\n"
+ "a standard set of out-of-box defaults to start with.\n\n"
+ "To change any of these settings later, edit /etc/rc.conf");
+
+ restorescr(w);
+ return DITEM_SUCCESS;
+}
+
+static void
+write_root_xprofile(char *str)
+{
+ FILE *fp;
+ int len;
+ char **cp;
+ static char *flist[] = { /* take care of both xdm and startx */
+ "/root/.xinitrc",
+ "/root/.xsession",
+ "/usr/share/skel/dot.xinitrc",
+ "/usr/share/skel/dot.xsession",
+ NULL,
+ };
+
+ len = strlen(str);
+ for (cp = flist; *cp; cp++) {
+ fp = fopen(*cp, "w");
+ if (fp) {
+ fwrite(str, 1, len, fp);
+ fchmod(fileno(fp), 0755);
+ fclose(fp);
+ }
+ }
+}
+
+static int
+gotit(char *fname)
+{
+ char tmp[FILENAME_MAX];
+
+ snprintf(tmp, sizeof tmp, "/usr/X11R6/bin/%s", fname);
+ if (file_executable(tmp))
+ return TRUE;
+ snprintf(tmp, sizeof tmp, "/usr/local/bin/%s", fname);
+ return file_executable(tmp);
+}
+
+int
+configXDesktop(dialogMenuItem *self)
+{
+ char *desk;
+ int ret = DITEM_SUCCESS;
+ WINDOW *w = savescr();
+
+ dialog_clear_norefresh();
+ if (!dmenuOpenSimple(&MenuXDesktops, FALSE) || !(desk = variable_get(VAR_DESKSTYLE))) {
+ restorescr(w);
+ return DITEM_FAILURE;
+ }
+ if (!strcmp(desk, "kde")) {
+ ret = package_add("kde");
+ if (DITEM_STATUS(ret) != DITEM_FAILURE && gotit("startkde"))
+ write_root_xprofile("exec startkde\n");
+ }
+ else if (!strcmp(desk, "gnome")) {
+ ret = package_add("gnomecore");
+ if (DITEM_STATUS(ret) != DITEM_FAILURE && gotit("gnome-session")) {
+ ret = package_add("sawfish-gnome");
+ if (DITEM_STATUS(ret) != DITEM_FAILURE && gotit("sawfish"))
+ write_root_xprofile("exec gnome-session\n");
+ }
+ }
+ else if (!strcmp(desk, "enlightenment")) {
+ ret = package_add("gnomecore");
+ if (DITEM_STATUS(ret) != DITEM_FAILURE && gotit("gnome-session")) {
+ ret = package_add("enlightenment");
+ if (DITEM_STATUS(ret) != DITEM_FAILURE && gotit("enlightenment"))
+ write_root_xprofile("exec gnome-session\n");
+ }
+ }
+ else if (!strcmp(desk, "afterstep")) {
+ ret = package_add("afterstep");
+ if (DITEM_STATUS(ret) != DITEM_FAILURE && gotit("afterstep"))
+ write_root_xprofile("exec afterstep\n");
+ }
+ else if (!strcmp(desk, "windowmaker")) {
+ ret = package_add("windowmaker");
+ if (DITEM_STATUS(ret) != DITEM_FAILURE && gotit("wmaker.inst")) {
+ write_root_xprofile("xterm &\n[ ! -d $HOME/GNUstep/Library/WindowMaker ] && /usr/X11R6/bin/wmaker.inst\nexec /usr/X11R6/bin/wmaker\n");
+ }
+ }
+ else if (!strcmp(desk, "fvwm2")) {
+ ret = package_add("fvwm");
+ if (DITEM_STATUS(ret) != DITEM_FAILURE && gotit("fvwm"))
+ write_root_xprofile("exec fvwm\n");
+ }
+ if (DITEM_STATUS(ret) == DITEM_FAILURE)
+ msgConfirm("An error occurred while adding the package(s) required\n"
+ "by this desktop type. Please change installation media\n"
+ "and/or select a different, perhaps simpler, desktop\n"
+ "environment and try again.");
+ restorescr(w);
+ return ret;
+}
+
+int
+configXSetup(dialogMenuItem *self)
+{
+ char *config, *execfile, *execcmd, *style, *tmp;
+ char *moused;
+ WINDOW *w = savescr();
+
+ setenv("XWINHOME", "/usr/X11R6", 1);
+tryagain:
+ variable_unset(VAR_DESKSTYLE);
+ variable_unset(VAR_XF86_CONFIG);
+ dialog_clear_norefresh();
+ if (!dmenuOpenSimple(&MenuXF86Config, FALSE)) {
+ restorescr(w);
+ return DITEM_FAILURE;
+ }
+ config = variable_get(VAR_XF86_CONFIG);
+ style = variable_get(VAR_DESKSTYLE);
+ if (!config) {
+ if (style)
+ goto config_desktop;
+ else {
+ restorescr(w);
+ return DITEM_FAILURE;
+ }
+ }
+
+ if (file_readable("/var/run/ld-elf.so.hints"))
+ vsystem("/sbin/ldconfig -m /usr/lib /usr/X11R6/lib /usr/local/lib /usr/lib/compat");
+ else
+ vsystem("/sbin/ldconfig /usr/lib /usr/X11R6/lib /usr/local/lib /usr/lib/compat");
+ if (file_readable("/var/run/ld.so.hints"))
+ vsystem("ldconfig -m -aout /usr/lib/aout /usr/lib/compat/aout /usr/local/lib/aout /usr/X11R6/lib/aout");
+ else
+ vsystem("ldconfig -aout /usr/lib/aout /usr/lib/compat/aout /usr/local/lib/aout /usr/X11R6/lib/aout");
+
+ vsystem("/sbin/ifconfig lo0 127.0.0.1");
+
+ /*
+ * execcmd may have been passed in as a command name with
+ * arguments. Therefore, before determining if it is suitable for
+ * execution, we must split off the filename component from the
+ * command line arguments.
+ */
+
+ execcmd = string_concat("/usr/X11R6/bin/", config);
+ execfile = strdup(execcmd);
+ if ((tmp = strchr(execfile, ' ')))
+ *tmp = '\0';
+ if (file_executable(execfile)) {
+ free(execfile);
+ moused = variable_get(VAR_MOUSED);
+ while (!moused || strcmp(moused, "YES")) {
+ if (msgYesNo("The X server may access the mouse in two ways: direct access\n"
+ "or indirect access via the mouse daemon. You have not\n"
+ "configured the mouse daemon. Would you like to configure it\n"
+ "now? If you intend to let the X server access the mouse\n"
+ "directly, choose \"No\" at this time."))
+ break;
+ dialog_clear_norefresh();
+ dmenuOpenSimple(&MenuMouse, FALSE);
+ moused = variable_get(VAR_MOUSED);
+ }
+ if (moused && !strcmp(moused, "YES"))
+ msgConfirm("You have configured and are now running the mouse daemon.\n"
+ "Choose \"/dev/sysmouse\" as the mouse port and \"SysMouse\" or\n"
+ "\"MouseSystems\" as the mouse protocol in the X configuration\n"
+ "utility.");
+ Mkdir("/etc/X11"); /* XXX:Remove this later after we are happy mtree will have created this for us. */
+ systemExecute(execcmd);
+ if (!file_readable("/etc/X11/XF86Config")) {
+ if (!msgYesNo("The XFree86 configuration process seems to have\nfailed. Would you like to try again?"))
+ goto tryagain;
+ else {
+ restorescr(w);
+ return DITEM_FAILURE;
+ }
+ }
+config_desktop:
+ configXDesktop(self);
+ restorescr(w);
+ return DITEM_SUCCESS;
+ }
+ else {
+ free(execfile);
+ msgConfirm("The XFree86 setup utility you chose does not appear to be installed!\n"
+ "Please install this before attempting to configure XFree86.");
+ restorescr(w);
+ return DITEM_FAILURE;
+ }
+}
+
+int
+configResolv(dialogMenuItem *ditem)
+{
+ FILE *fp;
+ char *cp, *c6p, *dp, *hp;
+
+ cp = variable_get(VAR_NAMESERVER);
+ if (!cp || !*cp)
+ goto skip;
+ Mkdir("/etc");
+ fp = fopen("/etc/resolv.conf", "w");
+ if (!fp)
+ return DITEM_FAILURE;
+ if (variable_get(VAR_DOMAINNAME))
+ fprintf(fp, "domain\t%s\n", variable_get(VAR_DOMAINNAME));
+ fprintf(fp, "nameserver\t%s\n", cp);
+ fclose(fp);
+ if (isDebug())
+ msgDebug("Wrote out /etc/resolv.conf\n");
+
+skip:
+ dp = variable_get(VAR_DOMAINNAME);
+ cp = variable_get(VAR_IPADDR);
+ c6p = variable_get(VAR_IPV6ADDR);
+ hp = variable_get(VAR_HOSTNAME);
+ /* Tack ourselves into /etc/hosts */
+ fp = fopen("/etc/hosts", "w");
+ if (!fp)
+ return DITEM_FAILURE;
+ /* Add an entry for localhost */
+ if (!variable_cmp(VAR_IPV6_ENABLE, "YES")) {
+ if (dp)
+ fprintf(fp, "::1\t\t\tlocalhost.%s localhost\n", dp);
+ else
+ fprintf(fp, "::1\t\t\tlocalhost\n");
+ }
+ 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') || (c6p && c6p[0] != '0')) && hp) {
+ char cp2[255];
+
+ if (!index(hp, '.'))
+ cp2[0] = '\0';
+ else {
+ SAFE_STRCPY(cp2, hp);
+ *(index(cp2, '.')) = '\0';
+ }
+ if (c6p && c6p[0] != '0') {
+ fprintf(fp, "%s\t%s %s\n", c6p, hp, cp2);
+ fprintf(fp, "%s\t%s.\n", c6p, hp);
+ }
+ if (cp && cp[0] != '0') {
+ fprintf(fp, "%s\t\t%s %s\n", cp, hp, cp2);
+ fprintf(fp, "%s\t\t%s.\n", cp, hp);
+ }
+ }
+ fclose(fp);
+ if (isDebug())
+ msgDebug("Wrote out /etc/hosts\n");
+ return DITEM_SUCCESS;
+}
+
+int
+configRouter(dialogMenuItem *self)
+{
+ int ret;
+
+ ret = variable_get_value(VAR_ROUTER,
+ "Please specify the router you wish to use. Routed is\n"
+ "provided with the stock system and gated is provided\n"
+ "as an optional package which this installation system\n"
+ "will attempt to load if you select gated. Any other\n"
+ "choice of routing daemon will be assumed to be something\n"
+ "the user intends to install themselves before rebooting\n"
+ "the system. If you don't want any routing daemon, choose NO", 1)
+ ? DITEM_SUCCESS : DITEM_FAILURE;
+
+ if (ret == DITEM_SUCCESS) {
+ char *cp = variable_get(VAR_ROUTER);
+
+ if (cp && strcmp(cp, "NO")) {
+ variable_set2(VAR_ROUTER_ENABLE, "YES", 1);
+ if (!strcmp(cp, "gated")) {
+ if (package_add("gated") != DITEM_SUCCESS) {
+ msgConfirm("Unable to load gated package. Falling back to no router.");
+ variable_unset(VAR_ROUTER);
+ variable_unset(VAR_ROUTERFLAGS);
+ variable_set2(VAR_ROUTER_ENABLE, "NO", 1);
+ cp = NULL;
+ }
+ }
+ if (cp) {
+ /* Now get the flags, if they chose a router */
+ ret = variable_get_value(VAR_ROUTERFLAGS,
+ "Please Specify the routing daemon flags; if you're running routed\n"
+ "then -q is the right choice for nodes and -s for gateway hosts.\n", 1)
+ ? DITEM_SUCCESS : DITEM_FAILURE;
+ if (ret != DITEM_SUCCESS)
+ variable_unset(VAR_ROUTERFLAGS);
+ }
+ }
+ else {
+ /* No router case */
+ variable_set2(VAR_ROUTER_ENABLE, "NO", 1);
+ variable_unset(VAR_ROUTERFLAGS);
+ variable_unset(VAR_ROUTER);
+ }
+ }
+ else {
+ variable_set2(VAR_ROUTER_ENABLE, "NO", 1);
+ variable_unset(VAR_ROUTERFLAGS);
+ variable_unset(VAR_ROUTER);
+ }
+ return ret;
+}
+
+/* Shared between us and index_initialize() */
+extern PkgNode Top, Plist;
+
+int
+configPackages(dialogMenuItem *self)
+{
+ int i, restoreflag = 0;
+ PkgNodePtr tmp;
+
+ /* Did we get an INDEX? */
+ i = index_initialize("packages/INDEX");
+ if (DITEM_STATUS(i) == DITEM_FAILURE)
+ return i;
+
+ while (1) {
+ int ret, pos, scroll;
+
+ /* Bring up the packages menu */
+ pos = scroll = 0;
+ index_menu(&Top, &Top, &Plist, &pos, &scroll);
+
+ if (Plist.kids && Plist.kids->name) {
+ /* Now show the packing list menu */
+ pos = scroll = 0;
+ ret = index_menu(&Plist, &Plist, NULL, &pos, &scroll);
+ if (ret & DITEM_LEAVE_MENU)
+ break;
+ else if (DITEM_STATUS(ret) != DITEM_FAILURE) {
+ dialog_clear();
+ restoreflag = 1;
+ for (tmp = Plist.kids; tmp && tmp->name; tmp = tmp->next)
+ (void)index_extract(mediaDevice, &Top, tmp, FALSE);
+ break;
+ }
+ }
+ else {
+ msgConfirm("No packages were selected for extraction.");
+ break;
+ }
+ }
+ tmp = Plist.kids;
+ while (tmp) {
+ PkgNodePtr tmp2 = tmp->next;
+
+ safe_free(tmp);
+ tmp = tmp2;
+ }
+ index_init(NULL, &Plist);
+ return DITEM_SUCCESS | (restoreflag ? DITEM_RESTORE : 0);
+}
+
+/* Load pcnfsd package */
+int
+configPCNFSD(dialogMenuItem *self)
+{
+ int ret;
+
+ ret = package_add("pcnfsd");
+ if (DITEM_STATUS(ret) == DITEM_SUCCESS) {
+ variable_set2(VAR_PCNFSD, "YES", 0);
+ variable_set2("mountd_flags", "-n", 1);
+ }
+ return ret;
+}
+
+int
+configInetd(dialogMenuItem *self)
+{
+ char cmd[256];
+
+ WINDOW *w = savescr();
+
+ if (msgYesNo("The Internet Super Server (inetd) allows a number of simple Internet\n"
+ "services to be enabled, including finger, ftp, and telnetd. Enabling\n"
+ "these services may increase risk of security problems by increasing\n"
+ "the exposure of your system.\n\n"
+ "With this in mind, do you wish to enable inetd?\n")) {
+ variable_set2("inetd_enable", "NO", 1);
+ } else {
+ /* If inetd is enabled, we'll need an inetd.conf */
+
+ if (!msgYesNo("inetd(8) relies on its configuration file, /etc/inetd.conf, to determine\n"
+ "which of its Internet services will be available. The default FreeBSD\n"
+ "inetd.conf(5) leaves all services disabled by default, so they must be\n"
+ "specifically enabled in the configuration file before they will\n"
+ "function, even once inetd(8) is enabled. Note that services for\n"
+ "IPv6 must be seperately enabled from IPv4 services.\n\n"
+ "Select [Yes] now to invoke an editor on /etc/inetd.conf, or [No] to\n"
+ "use the current settings.\n")) {
+ sprintf(cmd, "%s /etc/inetd.conf", variable_get(VAR_EDITOR));
+ dialog_clear();
+ systemExecute(cmd);
+ variable_set2("inetd_enable", "YES", 1);
+ }
+ }
+ restorescr(w);
+ return DITEM_SUCCESS;
+}
+
+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 '#/usr/src and /usr/ports read-only to machines named after trouble makers' >> /etc/exports");
+ vsystem("echo '#/home and all directories under it to machines named after dead rock stars' >> /etc/exports");
+ vsystem("echo '#and, /a to a network of privileged machines allowed to write on it as root.' >> /etc/exports");
+ vsystem("echo '#/usr huey louie dewie' >> /etc/exports");
+ vsystem("echo '#/usr/src /usr/obj -ro calvin hobbes' >> /etc/exports");
+ vsystem("echo '#/home -alldirs janice jimmy frank' >> /etc/exports");
+ vsystem("echo '#/a -maproot=0 -network 10.0.1.0 -mask 255.255.248.0' >> /etc/exports");
+ vsystem("echo '#' >> /etc/exports");
+ vsystem("echo '# You should replace these lines with your actual exported filesystems.' >> /etc/exports");
+ vsystem("echo '# Note that BSD's export synatx is \"host-centric\" vs. Sun\'s \"FS-centric\" one.' >> /etc/exports");
+ vsystem("echo >> /etc/exports");
+ sprintf(cmd, "%s /etc/exports", variable_get(VAR_EDITOR));
+ dialog_clear();
+ systemExecute(cmd);
+ }
+ variable_set2(VAR_NFS_SERVER, "YES", 1);
+ restorescr(w);
+ }
+ else if (variable_get(VAR_NFS_SERVER)) { /* We want to turn it off again? */
+ vsystem("mv -f /etc/exports /etc/exports.disabled");
+ variable_unset(VAR_NFS_SERVER);
+ }
+ return DITEM_SUCCESS;
+}
+
+int
+configEtcTtys(dialogMenuItem *self)
+{
+ char cmd[256];
+
+ WINDOW *w = savescr();
+
+ /* Simply prompt for confirmation, then edit away. */
+ if (msgYesNo("Configuration of system TTYs requires editing the /etc/ttys file.\n"
+ "Typical configuration activities might include enabling getty(8)\n"
+ "on the first serial port to allow login via serial console after\n"
+ "reboot, or to enable xdm. The default ttys file enables normal\n"
+ "virtual consoles, and most sites will not need to perform manual\n"
+ "configuration.\n\n"
+ "To load /etc/ttys in the editor, select [Yes], otherwise, [No].")) {
+ } else {
+ configTtys();
+ sprintf(cmd, "%s /etc/ttys", variable_get(VAR_EDITOR));
+ dialog_clear();
+ systemExecute(cmd);
+ }
+
+ restorescr(w);
+ return DITEM_SUCCESS;
+}
diff --git a/usr.sbin/sysinstall/dev2c.sh b/usr.sbin/sysinstall/dev2c.sh
new file mode 100644
index 0000000..2681f8e
--- /dev/null
+++ b/usr.sbin/sysinstall/dev2c.sh
@@ -0,0 +1,80 @@
+:
+#
+# ----------------------------------------------------------------------------
+# "THE BEER-WARE LICENSE" (Revision 42):
+# <phk@FreeBSD.org> wrote this file. As long as you retain this notice you
+# can do whatever you want with this stuff. If we meet some day, and you think
+# this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+# ----------------------------------------------------------------------------
+#
+# $FreeBSD$
+#
+# 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..7df1b3e
--- /dev/null
+++ b/usr.sbin/sysinstall/devices.c
@@ -0,0 +1,573 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <sys/fcntl.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_dl.h>
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+
+/* how much to bias minor number for a given /dev/<ct#><un#>s<s#> slice */
+#define SLICE_DELTA (0x10000)
+
+static Device *Devices[DEV_MAX];
+static int numDevs;
+
+static struct _devname {
+ DeviceType type;
+ char *name;
+ char *description;
+ int major, minor, delta, max;
+} device_names[] = {
+ { DEVICE_TYPE_CDROM, "cd%dc", "SCSI CDROM drive", 15, 2, 8, 4 },
+ { DEVICE_TYPE_CDROM, "mcd%da", "Mitsumi (old model) CDROM drive", 29, 0, 8, 4 },
+ { DEVICE_TYPE_CDROM, "scd%da", "Sony CDROM drive - CDU31/33A type", 45, 0, 8, 4 },
+#ifdef notdef
+ { DEVICE_TYPE_CDROM, "matcd%da", "Matsushita CDROM ('sound blaster' type)", 46, 0, 8, 4 },
+#endif
+ { DEVICE_TYPE_CDROM, "acd%dc", "ATAPI/IDE CDROM", 117, 0, 8, 4 },
+ { DEVICE_TYPE_TAPE, "rsa%d", "SCSI tape drive", 14, 0, 16, 4 },
+ { DEVICE_TYPE_TAPE, "rwt%d", "Wangtek tape drive", 10, 0, 1, 4 },
+ { DEVICE_TYPE_DISK, "da%d", "SCSI disk device", 13, 65538, 8, 16 },
+ { DEVICE_TYPE_DISK, "ad%d", "ATA/IDE disk device", 116, 65538, 8, 16 },
+ { DEVICE_TYPE_DISK, "ar%d", "ATA/IDE RAID device", 157, 65538, 8, 16 },
+ { DEVICE_TYPE_DISK, "fla%d", "M-Systems DiskOnChip Flash devicee", 102, 65538, 8, 16 },
+ { DEVICE_TYPE_DISK, "afd%d", "ATAPI/IDE floppy device", 118, 65538, 8, 4 },
+ { DEVICE_TYPE_DISK, "mlxd%d", "Mylex RAID disk", 131, 65538, 8, 4 },
+ { DEVICE_TYPE_DISK, "amrd%d", "AMI MegaRAID drive", 133, 65538, 8, 4 },
+ { DEVICE_TYPE_DISK, "idad%d", "Compaq RAID array", 109, 65538, 8, 4 },
+ { DEVICE_TYPE_DISK, "twed%d", "3ware ATA RAID array", 147, 65538, 8, 4 },
+ { DEVICE_TYPE_DISK, "aacd%d", "Adaptec FSA RAID array", 151, 65538, 8, 4 },
+ { DEVICE_TYPE_FLOPPY, "fd%d", "floppy drive unit A", 9, 0, 64, 4 },
+ { DEVICE_TYPE_NETWORK, "an", "Aironet 4500/4800 802.11 wireless adapter" },
+ { DEVICE_TYPE_NETWORK, "aue", "ADMtek USB ethernet adapter" },
+ { DEVICE_TYPE_NETWORK, "bge", "Broadcom BCM570x PCI gigabit ethernet card" },
+ { DEVICE_TYPE_NETWORK, "cue", "CATC USB ethernet adapter" },
+ { DEVICE_TYPE_NETWORK, "fpa", "DEC DEFPA PCI FDDI card" },
+ { DEVICE_TYPE_NETWORK, "sr", "SDL T1/E1 sync serial PCI card" },
+ { DEVICE_TYPE_NETWORK, "cc3i", "SDL HSSI sync serial PCI card" },
+ { DEVICE_TYPE_NETWORK, "en", "Efficient Networks ATM PCI card" },
+ { DEVICE_TYPE_NETWORK, "dc", "DEC/Intel 21143 (and clones) PCI fast ethernet card" },
+ { DEVICE_TYPE_NETWORK, "de", "DEC DE435 PCI NIC or other DC21040-AA based card" },
+ { DEVICE_TYPE_NETWORK, "fxp", "Intel EtherExpress Pro/100B PCI Fast Ethernet card" },
+ { DEVICE_TYPE_NETWORK, "ed", "Novell NE1000/2000; 3C503; NE2000-compatible PCMCIA" },
+ { DEVICE_TYPE_NETWORK, "ep", "3Com 3C509 ethernet card/3C589 PCMCIA" },
+ { DEVICE_TYPE_NETWORK, "el", "3Com 3C501 ethernet card" },
+ { DEVICE_TYPE_NETWORK, "ex", "Intel EtherExpress Pro/10 ethernet card" },
+ { DEVICE_TYPE_NETWORK, "fe", "Fujitsu MB86960A/MB86965A ethernet card" },
+ { DEVICE_TYPE_NETWORK, "ie", "AT&T StarLAN 10 and EN100; 3Com 3C507; NI5210" },
+ { DEVICE_TYPE_NETWORK, "ix", "Intel Etherexpress ethernet card" },
+ { DEVICE_TYPE_NETWORK, "kue", "Kawasaki LSI USB ethernet adapter" },
+ { DEVICE_TYPE_NETWORK, "le", "DEC EtherWorks 2 or 3 ethernet card" },
+ { DEVICE_TYPE_NETWORK, "lnc", "Lance/PCnet (Isolan/Novell NE2100/NE32-VL) ethernet" },
+ { DEVICE_TYPE_NETWORK, "lge", "Level 1 LXT1001 gigabit ethernet card" },
+ { DEVICE_TYPE_NETWORK, "nge", "NatSemi PCI gigabit ethernet card" },
+ { DEVICE_TYPE_NETWORK, "pcn", "AMD Am79c79x PCI ethernet card" },
+ { DEVICE_TYPE_NETWORK, "ray", "Raytheon Raylink 802.11 wireless adaptor" },
+ { DEVICE_TYPE_NETWORK, "rl", "RealTek 8129/8139 PCI ethernet card" },
+ { DEVICE_TYPE_NETWORK, "sf", "Adaptec AIC-6915 PCI ethernet card" },
+ { DEVICE_TYPE_NETWORK, "sis", "SiS 900/SiS 7016 PCI ethernet card" },
+#ifdef PC98
+ { DEVICE_TYPE_NETWORK, "snc", "SONIC ethernet card" },
+#endif
+ { DEVICE_TYPE_NETWORK, "sn", "SMC/Megahertz ethernet card" },
+ { DEVICE_TYPE_NETWORK, "ste", "Sundance ST201 PCI ethernet card" },
+ { DEVICE_TYPE_NETWORK, "sk", "SysKonnect PCI gigabit ethernet card" },
+ { DEVICE_TYPE_NETWORK, "tx", "SMC 9432TX ethernet card" },
+ { DEVICE_TYPE_NETWORK, "txp", "3Com 3cR990 ethernet card" },
+ { DEVICE_TYPE_NETWORK, "ti", "Alteon Networks PCI gigabit ethernet card" },
+ { DEVICE_TYPE_NETWORK, "tl", "Texas Instruments ThunderLAN PCI ethernet card" },
+ { DEVICE_TYPE_NETWORK, "vr", "VIA VT3043/VT86C100A Rhine PCI ethernet card" },
+ { DEVICE_TYPE_NETWORK, "vx", "3COM 3c590 / 3c595 ethernet card" },
+ { DEVICE_TYPE_NETWORK, "wb", "Winbond W89C840F PCI ethernet card" },
+ { DEVICE_TYPE_NETWORK, "wi", "Lucent WaveLAN/IEEE 802.11 wireless adapter" },
+ { DEVICE_TYPE_NETWORK, "wx", "Intel Gigabit Ethernet (82452) card" },
+ { DEVICE_TYPE_NETWORK, "xe", "Xircom/Intel EtherExpress Pro100/16 ethernet card" },
+ { DEVICE_TYPE_NETWORK, "xl", "3COM 3c90x / 3c90xB PCI ethernet card" },
+ { DEVICE_TYPE_NETWORK, "cuaa%d", "%s on device %s (COM%d)", 28, 128, 1, 16 },
+ { DEVICE_TYPE_NETWORK, "lp", "Parallel Port IP (PLIP) peer connection" },
+ { DEVICE_TYPE_NETWORK, "lo", "Loop-back (local) network interface" },
+#ifdef PC98
+ { DEVICE_TYPE_DISK, "wd%d", "IDE disk device", 3, 65538, 8, 16 },
+ { DEVICE_TYPE_CDROM, "wcd%dc", "ATAPI IDE CDROM", 69, 2, 8, 4 },
+ { DEVICE_TYPE_FLOPPY, "wfd%d", "ATAPI floppy drive unit A", 87, 0, 8, 4 },
+ { DEVICE_TYPE_DISK, "wfd%d", "ATAPI floppy device", 87, 65538, 8, 4 },
+#endif
+ { 0 },
+};
+
+Device *
+new_device(char *name)
+{
+ Device *dev;
+
+ dev = safe_malloc(sizeof(Device));
+ bzero(dev, sizeof(Device));
+ if (name)
+ SAFE_STRCPY(dev->name, name);
+ return dev;
+}
+
+/* Stubs for unimplemented strategy routines */
+Boolean
+dummyInit(Device *dev)
+{
+ return TRUE;
+}
+
+FILE *
+dummyGet(Device *dev, char *dist, Boolean probe)
+{
+ return NULL;
+}
+
+void
+dummyShutdown(Device *dev)
+{
+ return;
+}
+
+static int
+deviceTry(struct _devname dev, char *try, int i)
+{
+ int fd;
+ char unit[80];
+ mode_t m;
+ dev_t d;
+ int fail;
+
+ snprintf(unit, sizeof unit, dev.name, i);
+ snprintf(try, FILENAME_MAX, "/dev/%s", unit);
+ if (isDebug())
+ msgDebug("deviceTry: attempting to open %s\n", try);
+ fd = open(try, O_RDONLY);
+ if (fd >= 0) {
+ if (isDebug())
+ msgDebug("deviceTry: open of %s succeeded on first try.\n", try);
+ return fd;
+ }
+ m = 0640 | S_IFCHR;
+ d = makedev(dev.major, dev.minor + (i * dev.delta));
+ if (isDebug())
+ msgDebug("deviceTry: Making %s device for %s [%d, %d]\n", m & S_IFCHR ? "raw" : "block", try, dev.major, dev.minor + (i * dev.delta));
+ fail = mknod(try, m, d);
+ fd = open(try, O_RDONLY);
+ if (fd >= 0) {
+ if (isDebug())
+ msgDebug("deviceTry: open of %s succeeded on second try.\n", try);
+ return fd;
+ }
+ else if (!fail)
+ (void)unlink(try);
+ /* Don't try a "make-under" here since we're using a fixit floppy in this case */
+ snprintf(try, FILENAME_MAX, "/mnt/dev/%s", unit);
+ fd = open(try, O_RDONLY);
+ if (isDebug())
+ msgDebug("deviceTry: final attempt for %s returns %d\n", try, fd);
+ return fd;
+}
+
+/* Register a new device in the devices array */
+Device *
+deviceRegister(char *name, char *desc, char *devname, DeviceType type, Boolean enabled,
+ Boolean (*init)(Device *), FILE * (*get)(Device *, char *, Boolean),
+ void (*shutdown)(Device *), void *private)
+{
+ Device *newdev = NULL;
+
+ if (numDevs == DEV_MAX)
+ msgFatal("Too many devices found!");
+ else {
+ newdev = new_device(name);
+ newdev->description = desc;
+ newdev->devname = devname;
+ newdev->type = type;
+ newdev->enabled = enabled;
+ newdev->init = init ? init : dummyInit;
+ newdev->get = get ? get : dummyGet;
+ newdev->shutdown = shutdown ? shutdown : dummyShutdown;
+ newdev->private = private;
+ Devices[numDevs] = newdev;
+ Devices[++numDevs] = NULL;
+ }
+ return newdev;
+}
+
+/* Reset the registered device chain */
+void
+deviceReset(void)
+{
+ int i;
+
+ for (i = 0; i < numDevs; i++) {
+ DEVICE_SHUTDOWN(Devices[i]);
+
+ /* XXX this potentially leaks Devices[i]->private if it's being
+ * used to point to something dynamic, but you're not supposed
+ * to call this routine at such times that some open instance
+ * has its private ptr pointing somewhere anyway. XXX
+ */
+ free(Devices[i]);
+ }
+ Devices[numDevs = 0] = NULL;
+}
+
+/* Get all device information for devices we have attached */
+void
+deviceGetAll(void)
+{
+ int i, j, fd, s;
+ struct ifconf ifc;
+ struct ifreq *ifptr, *end;
+ int ifflags;
+ char buffer[INTERFACE_MAX * sizeof(struct ifreq)];
+ char **names;
+
+ msgNotify("Probing devices, please wait (this can take a while)...");
+ /* First go for the network interfaces. Stolen shamelessly from ifconfig! */
+ ifc.ifc_len = sizeof(buffer);
+ ifc.ifc_buf = buffer;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0)
+ goto skipif; /* Jump over network iface probing */
+
+ if (ioctl(s, SIOCGIFCONF, (char *) &ifc) < 0)
+ goto skipif; /* Jump over network iface probing */
+
+ close(s);
+ ifflags = ifc.ifc_req->ifr_flags;
+ end = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
+ for (ifptr = ifc.ifc_req; ifptr < end; ifptr++) {
+ char *descr;
+
+ /* If it's not a link entry, forget it */
+ if (ifptr->ifr_ifru.ifru_addr.sa_family != AF_LINK)
+ goto loopend;
+
+ /* Eliminate network devices that don't make sense */
+ if (!strncmp(ifptr->ifr_name, "lo", 2))
+ goto loopend;
+
+ /* If we have a slip device, don't register it */
+ if (!strncmp(ifptr->ifr_name, "sl", 2)) {
+ goto loopend;
+ }
+ /* And the same for ppp */
+ if (!strncmp(ifptr->ifr_name, "tun", 3) || !strncmp(ifptr->ifr_name, "ppp", 3)) {
+ goto loopend;
+ }
+ /* Try and find its description */
+ for (i = 0, descr = NULL; device_names[i].name; i++) {
+ int len = strlen(device_names[i].name);
+
+ if (!ifptr->ifr_name || !ifptr->ifr_name[0])
+ continue;
+ else if (!strncmp(ifptr->ifr_name, device_names[i].name, len)) {
+ descr = device_names[i].description;
+ break;
+ }
+ }
+ if (!descr)
+ descr = "<unknown network interface type>";
+
+ deviceRegister(ifptr->ifr_name, descr, strdup(ifptr->ifr_name), DEVICE_TYPE_NETWORK, TRUE,
+ mediaInitNetwork, NULL, mediaShutdownNetwork, NULL);
+ if (isDebug())
+ msgDebug("Found a network device named %s\n", ifptr->ifr_name);
+ close(s);
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+ continue;
+
+loopend:
+ if (ifptr->ifr_addr.sa_len) /* I'm not sure why this is here - it's inherited */
+ ifptr = (struct ifreq *)((caddr_t)ifptr + ifptr->ifr_addr.sa_len - sizeof(struct sockaddr));
+ close(s);
+ }
+
+skipif:
+ /* Next, try to find all the types of devices one might need
+ * during the second stage of the installation.
+ */
+ for (i = 0; device_names[i].name; i++) {
+ for (j = 0; j < device_names[i].max; j++) {
+ char try[FILENAME_MAX];
+
+ switch(device_names[i].type) {
+ case DEVICE_TYPE_CDROM:
+ fd = deviceTry(device_names[i], try, j);
+ if (fd >= 0 || errno == EBUSY) { /* EBUSY if already mounted */
+ char n[BUFSIZ];
+
+ if (fd >= 0) close(fd);
+ snprintf(n, sizeof n, device_names[i].name, j);
+ deviceRegister(strdup(n), device_names[i].description, strdup(try),
+ DEVICE_TYPE_CDROM, TRUE, mediaInitCDROM, mediaGetCDROM,
+ mediaShutdownCDROM, NULL);
+ if (isDebug())
+ msgDebug("Found a CDROM device for %s\n", try);
+ }
+ break;
+
+ case DEVICE_TYPE_TAPE:
+ fd = deviceTry(device_names[i], try, j);
+ if (fd >= 0) {
+ char n[BUFSIZ];
+
+ close(fd);
+ snprintf(n, sizeof n, device_names[i].name, j);
+ deviceRegister(strdup(n), device_names[i].description, strdup(try),
+ DEVICE_TYPE_TAPE, TRUE, mediaInitTape, mediaGetTape, mediaShutdownTape, NULL);
+ if (isDebug())
+ msgDebug("Found a TAPE device for %s\n", try);
+ }
+ break;
+
+ case DEVICE_TYPE_DISK:
+ fd = deviceTry(device_names[i], try, j);
+ if (fd >= 0 && RunningAsInit) {
+ dev_t d;
+ mode_t m;
+ int s, fail;
+ char unit[80], slice[80];
+
+ close(fd);
+ /* Make associated slice entries */
+ for (s = 1; s < 8; s++) {
+ snprintf(unit, sizeof unit, device_names[i].name, j);
+ snprintf(slice, sizeof slice, "/dev/%ss%d", unit, s);
+ d = makedev(device_names[i].major, device_names[i].minor +
+ (j * device_names[i].delta) + (s * SLICE_DELTA));
+ m = 0640 | S_IFCHR;
+ fail = mknod(slice, m, d);
+ fd = open(slice, O_RDONLY);
+ if (fd >= 0)
+ close(fd);
+ else if (!fail)
+ (void)unlink(slice);
+ }
+ }
+ break;
+
+ case DEVICE_TYPE_FLOPPY:
+ fd = deviceTry(device_names[i], try, j);
+ if (fd >= 0) {
+ char n[BUFSIZ];
+
+ close(fd);
+ snprintf(n, sizeof n, device_names[i].name, j);
+ deviceRegister(strdup(n), device_names[i].description, strdup(try),
+ DEVICE_TYPE_FLOPPY, TRUE, mediaInitFloppy, mediaGetFloppy,
+ mediaShutdownFloppy, NULL);
+ if (isDebug())
+ msgDebug("Found a floppy device for %s\n", try);
+ }
+ break;
+
+ case DEVICE_TYPE_NETWORK:
+ fd = deviceTry(device_names[i], try, j);
+ /* The only network devices that you can open this way are serial ones */
+ if (fd >= 0) {
+ char *newdesc, *cp;
+
+ close(fd);
+ cp = device_names[i].description;
+ /* Serial devices get a slip and ppp device each, if supported */
+ newdesc = safe_malloc(strlen(cp) + 40);
+ sprintf(newdesc, cp, "SLIP interface", try, j + 1);
+ deviceRegister("sl0", newdesc, strdup(try), DEVICE_TYPE_NETWORK, TRUE, mediaInitNetwork,
+ NULL, mediaShutdownNetwork, NULL);
+ msgDebug("Add mapping for %s to sl0\n", try);
+ newdesc = safe_malloc(strlen(cp) + 50);
+ sprintf(newdesc, cp, "PPP interface", try, j + 1);
+ deviceRegister("ppp0", newdesc, strdup(try), DEVICE_TYPE_NETWORK, TRUE, mediaInitNetwork,
+ NULL, mediaShutdownNetwork, NULL);
+ if (isDebug())
+ msgDebug("Add mapping for %s to ppp0\n", try);
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ /* Finally, go get the disks and look for DOS partitions to register */
+ if ((names = Disk_Names()) != NULL) {
+ int i;
+
+ for (i = 0; names[i]; i++) {
+ Chunk *c1;
+ Disk *d;
+
+ /* Ignore memory disks */
+ if (!strncmp(names[i], "md", 2))
+ continue;
+
+ d = Open_Disk(names[i]);
+ if (!d) {
+ msgDebug("Unable to open disk %s", names[i]);
+ continue;
+ }
+
+ deviceRegister(names[i], names[i], d->name, DEVICE_TYPE_DISK, FALSE,
+ dummyInit, dummyGet, dummyShutdown, d);
+ if (isDebug())
+ msgDebug("Found a disk device named %s\n", names[i]);
+
+ /* Look for existing DOS partitions to register as "DOS media devices" */
+ for (c1 = d->chunks->part; c1; c1 = c1->next) {
+ if (c1->type == fat || c1->type == extended) {
+ Device *dev;
+ char devname[80];
+
+ /* Got one! */
+ snprintf(devname, sizeof devname, "/dev/%s", c1->name);
+ dev = deviceRegister(c1->name, c1->name, strdup(devname), DEVICE_TYPE_DOS, TRUE,
+ mediaInitDOS, mediaGetDOS, mediaShutdownDOS, NULL);
+ dev->private = c1;
+ if (isDebug())
+ msgDebug("Found a DOS partition %s on drive %s\n", c1->name, d->name);
+ }
+ }
+ }
+ free(names);
+ }
+ dialog_clear_norefresh();
+}
+
+/* Rescan all devices, after closing previous set - convenience function */
+void
+deviceRescan(void)
+{
+ deviceReset();
+ deviceGetAll();
+}
+
+/*
+ * Find all devices that match the criteria, allowing "wildcarding" as well
+ * by allowing NULL or ANY values to match all. The array returned is static
+ * and may be used until the next invocation of deviceFind().
+ */
+Device **
+deviceFind(char *name, DeviceType class)
+{
+ static Device *found[DEV_MAX];
+ int i, j;
+
+ j = 0;
+ for (i = 0; i < numDevs; i++) {
+ if ((!name || !strcmp(Devices[i]->name, name))
+ && (class == DEVICE_TYPE_ANY || class == Devices[i]->type))
+ found[j++] = Devices[i];
+ }
+ found[j] = NULL;
+ return j ? found : NULL;
+}
+
+Device **
+deviceFindDescr(char *name, char *desc, DeviceType class)
+{
+ static Device *found[DEV_MAX];
+ int i, j;
+
+ j = 0;
+ for (i = 0; i < numDevs; i++) {
+ if ((!name || !strcmp(Devices[i]->name, name)) &&
+ (!desc || !strcmp(Devices[i]->description, desc)) &&
+ (class == DEVICE_TYPE_ANY || class == Devices[i]->type))
+ found[j++] = Devices[i];
+ }
+ found[j] = NULL;
+ return j ? found : NULL;
+}
+
+int
+deviceCount(Device **devs)
+{
+ int i;
+
+ if (!devs)
+ return 0;
+ for (i = 0; devs[i]; i++);
+ return i;
+}
+
+/*
+ * Create a menu listing all the devices of a certain type in the system.
+ * The passed-in menu is expected to be a "prototype" from which the new
+ * menu is cloned.
+ */
+DMenu *
+deviceCreateMenu(DMenu *menu, DeviceType type, int (*hook)(dialogMenuItem *d), int (*check)(dialogMenuItem *d))
+{
+ Device **devs;
+ int numdevs;
+ DMenu *tmp = NULL;
+ int i, j;
+
+ devs = deviceFind(NULL, type);
+ numdevs = deviceCount(devs);
+ if (!numdevs)
+ return NULL;
+ tmp = (DMenu *)safe_malloc(sizeof(DMenu) + (sizeof(dialogMenuItem) * (numdevs + 1)));
+ bcopy(menu, tmp, sizeof(DMenu));
+ for (i = 0; devs[i]; i++) {
+ tmp->items[i].prompt = devs[i]->name;
+ for (j = 0; j < numDevs; j++) {
+ if (devs[i] == Devices[j]) {
+ tmp->items[i].title = Devices[j]->description;
+ break;
+ }
+ }
+ if (j == numDevs)
+ tmp->items[i].title = "<unknown device type>";
+ tmp->items[i].fire = hook;
+ tmp->items[i].checked = check;
+ }
+ tmp->items[i].title = NULL;
+ return tmp;
+}
diff --git a/usr.sbin/sysinstall/dhcp.c b/usr.sbin/sysinstall/dhcp.c
new file mode 100644
index 0000000..a74a72b
--- /dev/null
+++ b/usr.sbin/sysinstall/dhcp.c
@@ -0,0 +1,158 @@
+/*
+ * $FreeBSD$
+ *
+ * Copyright (c) 1999
+ * C. Stone. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY C. STONE ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL C STONE OR HIS BODILY PARASITES BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE BY THE VOICES IN YOUR HEAD BEFOREHAND.
+ *
+ */
+
+#include "sysinstall.h"
+
+#include <ctype.h>
+
+int
+dhcpParseLeases(char *file, char *hostname, char *domain, char *nameserver,
+ char *ipaddr, char *gateway, char *netmask)
+{
+ char tempbuf[1024];
+ char optbuf[1024], *optname = NULL;
+ char *tptr;
+ int endedflag = 0;
+ int leaseflag = 0;
+ FILE *fp;
+ enum { P_NOSTMT, P_NOSTMT1, P_STMT, P_STMTLINE } state;
+
+ if ((fp = fopen(file, "r")) == NULL) {
+ msgDebug("error opening file %s: %s\n", file, strerror(errno));
+ return -1;
+ }
+
+ state = P_NOSTMT;
+ while (fscanf(fp, "%1023s", tempbuf) > 0) {
+ switch (state) {
+ case P_NOSTMT:
+ state = P_NOSTMT1;
+ if (!strncasecmp(tempbuf, "lease", 5)) {
+ if (!leaseflag)
+ leaseflag = 1;
+ else {
+ fclose(fp);
+ return 0;
+ }
+ }
+ break;
+
+ case P_NOSTMT1:
+ if (tempbuf[0] != '{') {
+ msgWarn("dhcpParseLeases: '{' expected");
+ fclose(fp);
+ return -1;
+ }
+ state = P_STMT;
+ break;
+
+ case P_STMT:
+ if (!strncasecmp("option", tempbuf, 6))
+ continue;
+ if (tempbuf[0] == '}') {
+ state = P_NOSTMT;
+ leaseflag = 0;
+ continue;
+ }
+ if (!leaseflag)
+ break;
+ if (tempbuf[0] == ';') { /* play it safe */
+ state = P_STMT;
+ continue;
+ }
+ if ((tptr = (char *)strchr(tempbuf, ';')) && (*(tptr + 1) == 0)) {
+ *tptr = NULL;
+ endedflag = 1;
+ }
+ if (!isalnum(tempbuf[0])) {
+ msgWarn("dhcpParseLeases: bad option");
+ fclose(fp);
+ return -1;
+ }
+ if (optname)
+ free(optname);
+ optname = strdup(tempbuf);
+ if (endedflag) {
+ state = P_STMT;
+ endedflag = 0;
+ continue;
+ }
+ state = P_STMTLINE;
+ break;
+
+ case P_STMTLINE:
+ if (tempbuf[0] == ';') {
+ state = P_STMT;
+ continue;
+ }
+ if ((tptr = (char *)strchr(tempbuf, ';')) && (*(tptr + 1) == 0)) {
+ *tptr = NULL;
+ endedflag = 1;
+ }
+ if (tempbuf[0] == '"') {
+ if (sscanf(tempbuf, "\"%[^\" ]\"", optbuf) < 1) {
+ msgWarn("dhcpParseLeases: bad option value");
+ fclose(fp);
+ return -1;
+ }
+ }
+ else
+ strcpy(optbuf, tempbuf);
+
+ if (!strcasecmp("host-name", optname)) {
+ strcpy(hostname, optbuf);
+ } else if (!strcasecmp("domain-name", optname)) {
+ strcpy(domain, optbuf);
+ } else if (!strcasecmp("fixed-address", optname)) {
+ strcpy(ipaddr, optbuf);
+ } else if (!strcasecmp("routers", optname)) {
+ if((tptr = (char *)strchr(optbuf, ',')))
+ *tptr = NULL;
+ strcpy(gateway, optbuf);
+ } else if (!strcasecmp("subnet-mask", optname)) {
+ strcpy(netmask, optbuf);
+ } else if (!strcasecmp("domain-name-servers", optname)) {
+ /* <jkh> ...one value per property */
+ if((tptr = (char *)strchr(optbuf, ',')))
+ *tptr = NULL;
+ strcpy(nameserver, optbuf);
+ }
+ if (endedflag) {
+ state = P_STMT;
+ endedflag = 0;
+ continue;
+ }
+ break;
+ }
+ }
+ fclose(fp);
+ return 0;
+}
diff --git a/usr.sbin/sysinstall/disks.c b/usr.sbin/sysinstall/disks.c
new file mode 100644
index 0000000..60091d3
--- /dev/null
+++ b/usr.sbin/sysinstall/disks.c
@@ -0,0 +1,964 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <ctype.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/disklabel.h>
+
+enum size_units_t { UNIT_BLOCKS, UNIT_KILO, UNIT_MEG, UNIT_SIZE };
+
+#ifdef PC98
+#define SUBTYPE_FREEBSD 50324
+#define SUBTYPE_FAT 37218
+#else
+#define SUBTYPE_FREEBSD 165
+#define SUBTYPE_FAT 6
+#endif
+
+/* Where we start displaying chunk information on the screen */
+#define CHUNK_START_ROW 5
+
+/* Where we keep track of MBR chunks */
+static struct chunk *chunk_info[16];
+static int current_chunk;
+
+static void diskPartitionNonInteractive(Device *dev);
+static u_char * bootalloc(char *name, size_t *size);
+
+static void
+record_chunks(Disk *d)
+{
+ struct chunk *c1 = NULL;
+ int i = 0;
+ 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 u)
+{
+ int row;
+ int i;
+ int sz;
+ char *szstr;
+
+ szstr = (u == UNIT_MEG ? "MB" : (u == UNIT_KILO ? "KB" : "ST"));
+
+ for (i = Total = 0; chunk_info[i]; i++)
+ Total += chunk_info[i]->size;
+#ifndef PC98
+ if (d->bios_cyl > 65536 || d->bios_hd > 256 || d->bios_sect >= 64) {
+ dialog_clear_norefresh();
+ msgConfirm("WARNING: A geometry of %lu/%lu/%lu for %s is incorrect. Using\n"
+ "a more likely geometry. If this geometry is incorrect or you\n"
+ "are unsure as to whether or not it's correct, please consult\n"
+ "the Hardware Guide in the Documentation submenu or use the\n"
+ "(G)eometry command to change it now.\n\n"
+ "Remember: you need to enter whatever your BIOS thinks the\n"
+ "geometry is! For IDE, it's what you were told in the BIOS\n"
+ "setup. For SCSI, it's the translation mode your controller is\n"
+ "using. Do NOT use a ``physical geometry''.",
+ d->bios_cyl, d->bios_hd, d->bios_sect, d->name);
+ Sanitize_Bios_Geom(d);
+ }
+#endif
+ 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 (%luMB)",
+ d->bios_cyl, d->bios_hd, d->bios_sect,
+ d->bios_cyl * d->bios_hd * d->bios_sect,
+ d->bios_cyl * d->bios_hd * d->bios_sect / (1024/512) / 1024);
+ mvprintw(3, 0, "%6s %10s(%s) %10s %8s %6s %10s %8s %8s",
+ "Offset", "Size", szstr, "End", "Name", "PType", "Desc",
+ "Subtype", "Flags");
+ for (i = 0, row = CHUNK_START_ROW; chunk_info[i]; i++, row++) {
+ switch(u) {
+ default: /* fall thru */
+ case UNIT_BLOCKS:
+ sz = chunk_info[i]->size;
+ break;
+ case UNIT_KILO:
+ sz = chunk_info[i]->size / (1024/512);
+ break;
+ case UNIT_MEG:
+ sz = chunk_info[i]->size / (1024/512) / 1024;
+ break;
+ }
+ if (i == current_chunk)
+ attrset(ATTR_SELECTED);
+ mvprintw(row, 0, "%10ld %10lu %10lu %8s %6d %10s %8d\t%-6s",
+ chunk_info[i]->offset, sz,
+ chunk_info[i]->end, chunk_info[i]->name,
+ chunk_info[i]->type,
+ slice_type_name(chunk_info[i]->type, chunk_info[i]->subtype),
+ chunk_info[i]->subtype, ShowChunkFlags(chunk_info[i]));
+ if (i == current_chunk)
+ attrset(A_NORMAL);
+ }
+}
+
+static void
+print_command_summary()
+{
+ mvprintw(14, 0, "The following commands are supported (in upper or lower case):");
+ mvprintw(16, 0, "A = Use Entire Disk G = set Drive Geometry C = Create Slice F = `DD' mode");
+ mvprintw(17, 0, "D = Delete Slice Z = Toggle Size Units S = Set Bootable | = Wizard m.");
+ mvprintw(18, 0, "T = Change Type U = Undo All Changes Q = Finish");
+ if (!RunningAsInit)
+ mvprintw(18, 47, "W = Write Changes");
+ mvprintw(21, 0, "Use F1 or ? to get more help, arrow keys to select.");
+ move(0, 0);
+}
+
+#ifdef PC98
+static void
+getBootMgr(char *dname, u_char **bootipl, size_t *bootipl_size,
+ u_char **bootmenu, size_t *bootmenu_size)
+{
+ static u_char *boot0;
+ static size_t boot0_size;
+ static u_char *boot05;
+ static size_t boot05_size;
+
+ char str[80];
+ char *cp;
+ int i = 0;
+
+ cp = variable_get(VAR_BOOTMGR);
+ if (!cp) {
+ /* Figure out what kind of 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
+ BootMgr = 2;
+ }
+ if (cp || i) {
+ switch (BootMgr) {
+ case 0:
+ if (!boot0) boot0 = bootalloc("boot0", &boot0_size);
+ *bootipl = boot0;
+ *bootipl_size = boot0_size;
+ if (!boot05) boot05 = bootalloc("boot0.5", &boot05_size);
+ *bootmenu = boot05;
+ *bootmenu_size = boot05_size;
+ return;
+ case 2:
+ default:
+ break;
+ }
+ }
+ *bootipl = NULL;
+ *bootipl_size = 0;
+ *bootmenu = NULL;
+ *bootmenu_size = 0;
+}
+#else
+static void
+getBootMgr(char *dname, u_char **bootCode, size_t *bootCodeSize)
+{
+#ifndef __alpha__ /* only meaningful on x86 */
+ static u_char *mbr, *boot0;
+ static size_t mbr_size, boot0_size;
+ char str[80];
+ char *cp;
+ int i = 0;
+
+ cp = variable_get(VAR_BOOTMGR);
+ if (!cp) {
+ /* Figure out what kind of MBR the user wants */
+ sprintf(str, "Install Boot Manager for drive %s?", dname);
+ MenuMBRType.title = str;
+ i = dmenuOpenSimple(&MenuMBRType, FALSE);
+ }
+ else {
+ if (!strncmp(cp, "boot", 4))
+ BootMgr = 0;
+ else if (!strcmp(cp, "standard"))
+ BootMgr = 1;
+ else
+ BootMgr = 2;
+ }
+ if (cp || i) {
+ switch (BootMgr) {
+ case 0:
+ if (!boot0) boot0 = bootalloc("boot0", &boot0_size);
+ *bootCode = boot0;
+ *bootCodeSize = boot0_size;
+ return;
+ case 1:
+ if (!mbr) mbr = bootalloc("mbr", &mbr_size);
+ *bootCode = mbr;
+ *bootCodeSize = mbr_size;
+ return;
+ case 2:
+ default:
+ break;
+ }
+ }
+#endif
+ *bootCode = NULL;
+ *bootCodeSize = 0;
+}
+#endif
+
+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;
+#ifdef PC98
+ u_char *bootipl;
+ size_t bootipl_size;
+ u_char *bootmenu;
+ size_t bootmenu_size;
+#else
+ u_char *mbrContents;
+ size_t mbrSize;
+#endif
+ WINDOW *w = savescr();
+ Disk *d = (Disk *)dev->private;
+ int size_unit;
+
+ size_unit = UNIT_BLOCKS;
+ chunking = TRUE;
+ keypad(stdscr, TRUE);
+
+ /* Flush both the dialog and curses library views of the screen
+ since we don't always know who called us */
+ dialog_clear_norefresh(), clear();
+ current_chunk = 0;
+
+ /* Set up the chunk array */
+ record_chunks(d);
+
+ while (chunking) {
+ char *val, geometry[80];
+
+ /* Now print our overall state */
+ if (d)
+ print_chunks(d, size_unit);
+ print_command_summary();
+ if (msg) {
+ attrset(title_attr); mvprintw(23, 0, msg); attrset(A_NORMAL);
+ beep();
+ msg = NULL;
+ }
+ else {
+ move(23, 0);
+ clrtoeol();
+ }
+
+ /* Get command character */
+ key = getch();
+ switch (toupper(key)) {
+ case '\014': /* ^L (redraw) */
+ clear();
+ msg = NULL;
+ break;
+
+ case '\020': /* ^P */
+ case KEY_UP:
+ case '-':
+ if (current_chunk != 0)
+ --current_chunk;
+ break;
+
+ case '\016': /* ^N */
+ case KEY_DOWN:
+ case '+':
+ case '\r':
+ case '\n':
+ if (chunk_info[current_chunk + 1])
+ ++current_chunk;
+ break;
+
+ case KEY_HOME:
+ current_chunk = 0;
+ break;
+
+ case KEY_END:
+ while (chunk_info[current_chunk + 1])
+ ++current_chunk;
+ break;
+
+ case KEY_F(1):
+ case '?':
+ systemDisplayHelp("slice");
+ clear();
+ break;
+
+ case 'A':
+ case 'F': /* Undocumented magic Dangerously Dedicated mode */
+#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 if (toupper(key) == 'A')
+ rv = 0;
+ else {
+ rv = msgYesNo("Do you want to do this with a true partition entry\n"
+ "so as to remain cooperative with any future possible\n"
+ "operating systems on the drive(s)?\n"
+ "(See also the section about ``dangerously dedicated''\n"
+ "disks in the FreeBSD FAQ.)");
+ if (rv == -1)
+ rv = 0;
+ }
+#endif
+ All_FreeBSD(d, rv);
+ variable_set2(DISK_PARTITIONED, "yes", 0);
+ record_chunks(d);
+ clear();
+ break;
+
+ case 'C':
+ if (chunk_info[current_chunk]->type != unused)
+ msg = "Slice in use, delete it first or move to an unused one.";
+ else {
+ char *val, tmp[20], *cp;
+ int size;
+#ifdef PC98
+ char name[16];
+
+ snprintf(name, 16, "%s", "FreeBSD");
+ val = msgGetInput(name,
+ "Please specify the name for new FreeBSD slice.");
+ if (val)
+ strncpy(name, val, 16);
+#else
+ int subtype;
+ chunk_e partitiontype;
+#endif
+ 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;
+ else if (*cp && toupper(*cp) == 'G')
+ size *= ONE_GIG;
+#ifdef PC98
+ Create_Chunk(d, chunk_info[current_chunk]->offset, size,
+ freebsd, 3,
+ (chunk_info[current_chunk]->flags & CHUNK_ALIGN),
+ name);
+ variable_set2(DISK_PARTITIONED, "yes", 0);
+ record_chunks(d);
+#else
+ sprintf(tmp, "%d", SUBTYPE_FREEBSD);
+ val = msgGetInput(tmp, "Enter type of partition to create:\n\n"
+ "Pressing Enter will choose the default, a native FreeBSD\n"
+ "slice (type 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 == SUBTYPE_FREEBSD)
+ partitiontype = freebsd;
+ else if (subtype == SUBTYPE_FAT)
+ 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);
+ }
+#endif /* PC98 */
+ }
+ clear();
+ }
+ break;
+
+ case KEY_DC:
+ case 'D':
+ if (chunk_info[current_chunk]->type == unused)
+ msg = "Slice is already unused!";
+ else {
+ Delete_Chunk(d, chunk_info[current_chunk]);
+ variable_set2(DISK_PARTITIONED, "yes", 0);
+ record_chunks(d);
+ }
+ break;
+
+ case 'T':
+ if (chunk_info[current_chunk]->type == unused)
+ msg = "Slice is currently unused (use create instead)";
+ else {
+ char *val, tmp[20];
+ int subtype;
+ chunk_e partitiontype;
+
+ sprintf(tmp, "%d", SUBTYPE_FREEBSD);
+#ifdef PC98
+ val = msgGetInput(tmp, "New partition type:\n\n"
+ "Pressing Enter will choose the default, a native FreeBSD\n"
+ "slice (type 50324). Other popular values are 37218 for\n"
+ "DOS FAT 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.");
+#else
+ 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.");
+#endif /* PC98 */
+ if (val && (subtype = strtol(val, NULL, 0)) > 0) {
+ if (subtype == SUBTYPE_FREEBSD)
+ partitiontype = freebsd;
+ else if (subtype == SUBTYPE_FAT)
+ partitiontype = fat;
+ else
+ partitiontype = unknown;
+ chunk_info[current_chunk]->type = partitiontype;
+ chunk_info[current_chunk]->subtype = subtype;
+ }
+ }
+ break;
+
+ case 'G':
+ snprintf(geometry, 80, "%lu/%lu/%lu", d->bios_cyl, d->bios_hd, d->bios_sect);
+ val = msgGetInput(geometry, "Please specify the new geometry in cyl/hd/sect format.\n"
+ "Don't forget to use the two slash (/) separator characters!\n"
+ "It's not possible to parse the field without them.");
+ if (val) {
+ long nc, nh, ns;
+ nc = strtol(val, &val, 0);
+ nh = strtol(val + 1, &val, 0);
+ ns = strtol(val + 1, 0, 0);
+ Set_Bios_Geom(d, nc, nh, ns);
+ }
+ clear();
+ break;
+
+ case 'S':
+ /* Set Bootable */
+ chunk_info[current_chunk]->flags |= CHUNK_ACTIVE;
+ break;
+
+ case 'U':
+ if (!variable_cmp(DISK_LABELLED, "written")) {
+ msgConfirm("You've already written this information out - you\n"
+ "can't undo it.");
+ }
+ else if (!msgNoYes("Are you SURE you want to Undo everything?")) {
+ char cp[BUFSIZ];
+
+ sstrncpy(cp, d->name, sizeof cp);
+ Free_Disk(dev->private);
+ d = Open_Disk(cp);
+ if (!d)
+ msgConfirm("Can't reopen disk %s! Internal state is probably corrupted", cp);
+ dev->private = d;
+ variable_unset(DISK_PARTITIONED);
+ variable_unset(DISK_LABELLED);
+ if (d)
+ record_chunks(d);
+ }
+ clear();
+ break;
+
+ case 'W':
+ if (!msgNoYes("WARNING: This should only be used when modifying an EXISTING\n"
+ "installation. If you are installing FreeBSD for the first time\n"
+ "then you should simply type Q when you're finished here and your\n"
+ "changes will be committed in one batch automatically at the end of\n"
+ "these questions. If you're adding a disk, you should NOT write\n"
+ "from this screen, you should do it from the label editor.\n\n"
+ "Are you absolutely sure you want to do this now?")) {
+ variable_set2(DISK_PARTITIONED, "yes", 0);
+
+ /*
+ * Don't trash the MBR if the first (and therefore only) chunk
+ * is marked for a truly dedicated disk (i.e., the disklabel
+ * starts at sector 0), even in cases where the user has
+ * requested booteasy or a "standard" MBR -- both would be
+ * fatal in this case.
+ */
+ /*
+ * Don't offer to update the MBR on this disk if the first
+ * "real" chunk looks like a FreeBSD "all disk" partition,
+ * or the disk is entirely FreeBSD.
+ */
+#ifdef PC98
+ if ((d->chunks->part->type != freebsd) ||
+ (d->chunks->part->offset > 1))
+ getBootMgr(d->name, &bootipl, &bootipl_size,
+ &bootmenu, &bootmenu_size);
+ else {
+ bootipl = NULL;
+ bootipl_size = 0;
+ bootmenu = NULL;
+ bootmenu_size = 0;
+ }
+ Set_Boot_Mgr(d, bootipl, bootipl_size, bootmenu, bootmenu_size);
+#else
+ if ((d->chunks->part->type != freebsd) ||
+ (d->chunks->part->offset > 1))
+ getBootMgr(d->name, &mbrContents, &mbrSize);
+ else {
+ mbrContents = NULL;
+ mbrSize = 0;
+ }
+ Set_Boot_Mgr(d, mbrContents, mbrSize);
+#endif
+
+ if (DITEM_STATUS(diskPartitionWrite(NULL)) != DITEM_SUCCESS)
+ msgConfirm("Disk partition write returned an error status!");
+ else
+ msgConfirm("Wrote FDISK partition information out successfully.");
+ }
+ clear();
+ break;
+
+ case '|':
+ if (!msgNoYes("Are you SURE you want to go into Wizard mode?\n"
+ "No seat belts whatsoever are provided!")) {
+ clear();
+ refresh();
+ slice_wizard(d);
+ variable_set2(DISK_PARTITIONED, "yes", 0);
+ record_chunks(d);
+ }
+ else
+ msg = "Wise choice!";
+ clear();
+ break;
+
+ case '\033': /* ESC */
+ case 'Q':
+ chunking = FALSE;
+ /*
+ * Don't trash the MBR if the first (and therefore only) chunk
+ * is marked for a truly dedicated disk (i.e., the disklabel
+ * starts at sector 0), even in cases where the user has requested
+ * booteasy or a "standard" MBR -- both would be fatal in this case.
+ */
+ /*
+ * Don't offer to update the MBR on this disk if the first "real"
+ * chunk looks like a FreeBSD "all disk" partition, or the disk is
+ * entirely FreeBSD.
+ */
+ if ((d->chunks->part->type != freebsd) ||
+ (d->chunks->part->offset > 1)) {
+ if (variable_cmp(DISK_PARTITIONED, "written")) {
+#ifdef PC98
+ getBootMgr(d->name, &bootipl, &bootipl_size,
+ &bootmenu, &bootmenu_size);
+ if (bootipl != NULL && bootmenu != NULL)
+ Set_Boot_Mgr(d, bootipl, bootipl_size,
+ bootmenu, bootmenu_size);
+#else
+ getBootMgr(d->name, &mbrContents, &mbrSize);
+ if (mbrContents != NULL)
+ Set_Boot_Mgr(d, mbrContents, mbrSize);
+#endif
+ }
+ }
+ break;
+
+ case 'Z':
+ size_unit = (size_unit + 1) % UNIT_SIZE;
+ break;
+
+ default:
+ beep();
+ msg = "Type F1 or ? for help";
+ break;
+ }
+ }
+ p = CheckRules(d);
+ if (p) {
+ char buf[FILENAME_MAX];
+
+ use_helpline("Press F1 to read more about disk slices.");
+ use_helpfile(systemHelpFile("partition", buf));
+ if (!variable_get(VAR_NO_WARN))
+ dialog_mesgbox("Disk slicing warning:", p, -1, -1);
+ free(p);
+ }
+ restorescr(w);
+}
+
+static u_char *
+bootalloc(char *name, size_t *size)
+{
+ char buf[FILENAME_MAX];
+ struct stat sb;
+
+ snprintf(buf, sizeof buf, "/boot/%s", name);
+ if (stat(buf, &sb) != -1) {
+ int fd;
+
+ fd = open(buf, O_RDONLY);
+ if (fd != -1) {
+ u_char *cp;
+
+ cp = malloc(sb.st_size);
+ if (read(fd, cp, sb.st_size) != sb.st_size) {
+ free(cp);
+ close(fd);
+ msgDebug("bootalloc: couldn't read %ld bytes from %s\n", (long)sb.st_size, buf);
+ return NULL;
+ }
+ close(fd);
+ if (size != NULL)
+ *size = sb.st_size;
+ return cp;
+ }
+ msgDebug("bootalloc: couldn't open %s\n", buf);
+ }
+ else
+ msgDebug("bootalloc: can't stat %s\n", buf);
+ return NULL;
+}
+
+static int
+partitionHook(dialogMenuItem *selected)
+{
+ Device **devs = NULL;
+
+ devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
+ if (!devs) {
+ msgConfirm("Unable to find disk %s!", selected->prompt);
+ return DITEM_FAILURE;
+ }
+ /* Toggle enabled status? */
+ if (!devs[0]->enabled) {
+ devs[0]->enabled = TRUE;
+ diskPartition(devs[0]);
+ }
+ else
+ devs[0]->enabled = FALSE;
+ return DITEM_SUCCESS;
+}
+
+static int
+partitionCheck(dialogMenuItem *selected)
+{
+ Device **devs = NULL;
+
+ devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
+ if (!devs || devs[0]->enabled == FALSE)
+ return FALSE;
+ return TRUE;
+}
+
+int
+diskPartitionEditor(dialogMenuItem *self)
+{
+ DMenu *menu;
+ Device **devs;
+ int i, cnt, devcnt;
+
+ cnt = diskGetSelectCount(&devs);
+ devcnt = deviceCount(devs);
+ if (cnt == -1) {
+ msgConfirm("No disks found! Please verify that your disk controller is being\n"
+ "properly probed at boot time. See the Hardware Guide on the\n"
+ "Documentation menu for clues on diagnosing this type of problem.");
+ return DITEM_FAILURE;
+ }
+ else if (cnt) {
+ /* Some are already selected */
+ for (i = 0; i < devcnt; i++) {
+ if (devs[i]->enabled) {
+ if (variable_get(VAR_NONINTERACTIVE) &&
+ !variable_get(VAR_DISKINTERACTIVE))
+ diskPartitionNonInteractive(devs[i]);
+ else
+ diskPartition(devs[i]);
+ }
+ }
+ }
+ else {
+ /* No disks are selected, fall-back case now */
+ if (devcnt == 1) {
+ devs[0]->enabled = TRUE;
+ if (variable_get(VAR_NONINTERACTIVE) &&
+ !variable_get(VAR_DISKINTERACTIVE))
+ diskPartitionNonInteractive(devs[0]);
+ else
+ diskPartition(devs[0]);
+ return DITEM_SUCCESS;
+ }
+ else {
+ menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, partitionHook, partitionCheck);
+ if (!menu) {
+ msgConfirm("No devices suitable for installation found!\n\n"
+ "Please verify that your disk controller (and attached drives)\n"
+ "were detected properly. This can be done by pressing the\n"
+ "[Scroll Lock] key and using the Arrow keys to move back to\n"
+ "the boot messages. Press [Scroll Lock] again to return.");
+ return DITEM_FAILURE;
+ }
+ else {
+ i = dmenuOpenSimple(menu, FALSE) ? DITEM_SUCCESS : DITEM_FAILURE;
+ free(menu);
+ }
+ return i;
+ }
+ }
+ return DITEM_SUCCESS;
+}
+
+int
+diskPartitionWrite(dialogMenuItem *self)
+{
+ Device **devs;
+ int i;
+
+ if (!variable_cmp(DISK_PARTITIONED, "written"))
+ return DITEM_SUCCESS;
+
+ devs = deviceFind(NULL, DEVICE_TYPE_DISK);
+ if (!devs) {
+ msgConfirm("Unable to find any disks to write to??");
+ return DITEM_FAILURE;
+ }
+ if (isDebug())
+ msgDebug("diskPartitionWrite: Examining %d devices\n", deviceCount(devs));
+ for (i = 0; devs[i]; i++) {
+ Disk *d = (Disk *)devs[i]->private;
+ static u_char *boot1;
+#ifndef __alpha__
+ static u_char *boot2;
+#endif
+
+ if (!devs[i]->enabled)
+ continue;
+
+#ifdef __alpha__
+ if (!boot1) boot1 = bootalloc("boot1", NULL);
+ Set_Boot_Blocks(d, boot1, NULL);
+#else
+ if (!boot1) boot1 = bootalloc("boot1", NULL);
+ if (!boot2) boot2 = bootalloc("boot2", NULL);
+ 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;
+ }
+ }
+ /* Now it's not "yes", but "written" */
+ variable_set2(DISK_PARTITIONED, "written", 0);
+ return DITEM_SUCCESS | DITEM_RESTORE;
+}
+
+/* Partition a disk based wholly on which variables are set */
+static void
+diskPartitionNonInteractive(Device *dev)
+{
+ char *cp;
+ int i, sz, all_disk = 0;
+#ifdef PC98
+ u_char *bootipl;
+ size_t bootipl_size;
+ u_char *bootmenu;
+ size_t bootmenu_size;
+#else
+ u_char *mbrContents;
+ size_t mbrSize;
+#endif
+ Disk *d = (Disk *)dev->private;
+
+ record_chunks(d);
+ cp = variable_get(VAR_GEOMETRY);
+ if (cp) {
+ msgDebug("Setting geometry from script to: %s\n", cp);
+ d->bios_cyl = strtol(cp, &cp, 0);
+ d->bios_hd = strtol(cp + 1, &cp, 0);
+ d->bios_sect = strtol(cp + 1, 0, 0);
+ }
+
+ cp = variable_get(VAR_PARTITION);
+ if (cp) {
+ if (!strcmp(cp, "free")) {
+ /* Do free disk space case */
+ for (i = 0; chunk_info[i]; i++) {
+ /* If a chunk is at least 10MB in size, use it. */
+ if (chunk_info[i]->type == unused && chunk_info[i]->size > (10 * ONE_MEG)) {
+#ifdef PC98
+ Create_Chunk(d, chunk_info[i]->offset, chunk_info[i]->size,
+ freebsd, 3,
+ (chunk_info[i]->flags & CHUNK_ALIGN),
+ "FreeBSD");
+#else
+ Create_Chunk(d, chunk_info[i]->offset, chunk_info[i]->size,
+ freebsd, 3,
+ (chunk_info[i]->flags & CHUNK_ALIGN));
+#endif
+ variable_set2(DISK_PARTITIONED, "yes", 0);
+ break;
+ }
+ }
+ if (!chunk_info[i]) {
+ msgConfirm("Unable to find any free space on this disk!");
+ return;
+ }
+ }
+ else if (!strcmp(cp, "all")) {
+ /* Do all disk space case */
+ msgDebug("Warning: Devoting all of disk %s to FreeBSD.\n", d->name);
+
+ All_FreeBSD(d, FALSE);
+ }
+ else if (!strcmp(cp, "exclusive")) {
+ /* Do really-all-the-disk-space case */
+ msgDebug("Warning: Devoting all of disk %s to FreeBSD.\n", d->name);
+
+ All_FreeBSD(d, all_disk = TRUE);
+ }
+ else if ((sz = strtol(cp, &cp, 0))) {
+ /* Look for sz bytes free */
+ if (*cp && toupper(*cp) == 'M')
+ sz *= ONE_MEG;
+ else if (*cp && toupper(*cp) == 'G')
+ sz *= ONE_GIG;
+ for (i = 0; chunk_info[i]; i++) {
+ /* If a chunk is at least sz MB, use it. */
+ if (chunk_info[i]->type == unused && chunk_info[i]->size >= sz) {
+#ifdef PC98
+ Create_Chunk(d, chunk_info[i]->offset, sz, freebsd, 3,
+ (chunk_info[i]->flags & CHUNK_ALIGN),
+ "FreeBSD");
+#else
+ Create_Chunk(d, chunk_info[i]->offset, sz, freebsd, 3,
+ (chunk_info[i]->flags & CHUNK_ALIGN));
+#endif
+ variable_set2(DISK_PARTITIONED, "yes", 0);
+ break;
+ }
+ }
+ if (!chunk_info[i]) {
+ 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]) {
+ msgConfirm("Unable to find any existing FreeBSD partitions on this disk!");
+ return;
+ }
+ }
+ else {
+ msgConfirm("`%s' is an invalid value for %s - is config file valid?", cp, VAR_PARTITION);
+ return;
+ }
+ if (!all_disk) {
+#ifdef PC98
+ getBootMgr(d->name, &bootipl, &bootipl_size,
+ &bootmenu, &bootmenu_size);
+ Set_Boot_Mgr(d, bootipl, bootipl_size, bootmenu, bootmenu_size);
+#else
+ getBootMgr(d->name, &mbrContents, &mbrSize);
+ Set_Boot_Mgr(d, mbrContents, mbrSize);
+#endif
+ }
+ variable_set2(DISK_PARTITIONED, "yes", 0);
+ }
+}
+
diff --git a/usr.sbin/sysinstall/dispatch.c b/usr.sbin/sysinstall/dispatch.c
new file mode 100644
index 0000000..357cd60
--- /dev/null
+++ b/usr.sbin/sysinstall/dispatch.c
@@ -0,0 +1,444 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <ctype.h>
+#include <errno.h>
+#include <sys/signal.h>
+#include <sys/fcntl.h>
+
+#include "list.h"
+
+static int dispatch_shutdown(dialogMenuItem *unused);
+static int dispatch_systemExecute(dialogMenuItem *unused);
+static int dispatch_msgConfirm(dialogMenuItem *unused);
+static int dispatch_mediaClose(dialogMenuItem *unused);
+
+static struct _word {
+ char *name;
+ int (*handler)(dialogMenuItem *self);
+} resWords[] = {
+ { "configAnonFTP", configAnonFTP },
+ { "configRouter", configRouter },
+ { "configInetd", configInetd },
+ { "configNFSServer", configNFSServer },
+ { "configNTP", configNTP },
+ { "configPCNFSD", configPCNFSD },
+ { "configPackages", configPackages },
+ { "configUsers", configUsers },
+ { "configXSetup", configXSetup },
+ { "configXDesktop", configXDesktop },
+ { "diskPartitionEditor", diskPartitionEditor },
+ { "diskPartitionWrite", diskPartitionWrite },
+ { "diskLabelEditor", diskLabelEditor },
+ { "diskLabelCommit", diskLabelCommit },
+ { "distReset", distReset },
+ { "distSetCustom", distSetCustom },
+ { "distUnsetCustom", distUnsetCustom },
+ { "distSetDeveloper", distSetDeveloper },
+ { "distSetXDeveloper", distSetXDeveloper },
+ { "distSetKernDeveloper", distSetKernDeveloper },
+ { "distSetUser", distSetUser },
+ { "distSetXUser", distSetXUser },
+ { "distSetMinimum", distSetMinimum },
+ { "distSetEverything", distSetEverything },
+ { "distSetSrc", distSetSrc },
+ { "distSetXF86", distSetXF86 },
+ { "distExtractAll", distExtractAll },
+ { "docBrowser", docBrowser },
+ { "docShowDocument", docShowDocument },
+ { "installCommit", installCommit },
+ { "installExpress", installExpress },
+ { "installStandard", installStandard },
+ { "installUpgrade", installUpgrade },
+ { "installFixupBin", installFixupBin },
+#ifndef X_AS_PKG
+ { "installFixupXFree", installFixupXFree },
+#endif
+ { "installFixitHoloShell", installFixitHoloShell },
+ { "installFixitCDROM", installFixitCDROM },
+ { "installFixitFloppy", installFixitFloppy },
+ { "installFilesystems", installFilesystems },
+ { "installVarDefaults", installVarDefaults },
+ { "loadConfig", dispatch_load_file },
+ { "loadFloppyConfig", dispatch_load_floppy },
+ { "mediaClose", dispatch_mediaClose },
+ { "mediaSetCDROM", mediaSetCDROM },
+ { "mediaSetFloppy", mediaSetFloppy },
+ { "mediaSetDOS", mediaSetDOS },
+ { "mediaSetTape", mediaSetTape },
+ { "mediaSetFTP", mediaSetFTP },
+ { "mediaSetFTPActive", mediaSetFTPActive },
+ { "mediaSetFTPPassive", mediaSetFTPPassive },
+ { "mediaSetHTTP", mediaSetHTTP },
+ { "mediaSetUFS", mediaSetUFS },
+ { "mediaSetNFS", mediaSetNFS },
+ { "mediaSetFTPUserPass", mediaSetFTPUserPass },
+ { "mediaSetCPIOVerbosity", mediaSetCPIOVerbosity },
+ { "mediaGetType", mediaGetType },
+ { "msgConfirm", dispatch_msgConfirm },
+ { "optionsEditor", optionsEditor },
+ { "packageAdd", packageAdd },
+ { "addGroup", userAddGroup },
+ { "addUser", userAddUser },
+ { "shutdown", dispatch_shutdown },
+ { "system", dispatch_systemExecute },
+ { "dumpVariables", dump_variables },
+ { "tcpMenuSelect", tcpMenuSelect },
+ { NULL, NULL },
+};
+
+/*
+ * Helper routines for buffering data.
+ *
+ * We read an entire configuration into memory before executing it
+ * so that we are truely standalone and can do things like nuke the
+ * file or disk we're working on.
+ */
+
+typedef struct command_buffer_ {
+ qelement queue;
+ char * string;
+} command_buffer;
+
+static void
+dispatch_free_command(command_buffer *item)
+{
+ REMQUE(item);
+ free(item->string);
+ free(item);
+}
+
+static void
+dispatch_free_all(qelement *head)
+{
+ command_buffer *item;
+
+ while (!EMPTYQUE(*head)) {
+ item = (command_buffer *) head->q_forw;
+ dispatch_free_command(item);
+ }
+}
+
+static command_buffer *
+dispatch_add_command(qelement *head, char *string)
+{
+ command_buffer *new;
+
+ new = malloc(sizeof(command_buffer));
+
+ if (!new)
+ return NULL;
+
+ new->string = strdup(string);
+ INSQUEUE(new, head->q_back);
+
+ return new;
+}
+
+/*
+ * Command processing
+ */
+
+/* Just convenience */
+static int
+dispatch_shutdown(dialogMenuItem *unused)
+{
+ systemShutdown(0);
+ return DITEM_FAILURE;
+}
+
+static int
+dispatch_systemExecute(dialogMenuItem *unused)
+{
+ char *cmd = variable_get(VAR_COMMAND);
+
+ if (cmd)
+ return systemExecute(cmd) ? DITEM_FAILURE : DITEM_SUCCESS;
+ else
+ msgDebug("_systemExecute: No command passed in `command' variable.\n");
+ return DITEM_FAILURE;
+}
+
+static int
+dispatch_msgConfirm(dialogMenuItem *unused)
+{
+ char *msg = variable_get(VAR_COMMAND);
+
+ if (msg) {
+ msgConfirm("%s", msg);
+ return DITEM_SUCCESS;
+ }
+
+ msgDebug("_msgConfirm: No message passed in `command' variable.\n");
+ return DITEM_FAILURE;
+}
+
+static int
+dispatch_mediaClose(dialogMenuItem *unused)
+{
+ mediaClose();
+ return DITEM_SUCCESS;
+}
+
+static int
+call_possible_resword(char *name, dialogMenuItem *value, int *status)
+{
+ int i, rval;
+
+ rval = 0;
+ for (i = 0; resWords[i].name; i++) {
+ if (!strcmp(name, resWords[i].name)) {
+ *status = resWords[i].handler(value);
+ rval = 1;
+ break;
+ }
+ }
+ return rval;
+}
+
+/* For a given string, call it or spit out an undefined command diagnostic */
+int
+dispatchCommand(char *str)
+{
+ int i;
+ char *cp;
+
+ if (!str || !*str) {
+ msgConfirm("Null or zero-length string passed to dispatchCommand");
+ return DITEM_FAILURE;
+ }
+ /* If it's got a newline, trim it */
+ if ((cp = index(str, '\n')) != NULL)
+ *cp = '\0';
+
+ /* If it's got a `=' sign in there, assume it's a variable setting */
+ if (index(str, '=')) {
+ if (isDebug())
+ msgDebug("dispatch: setting variable `%s'\n", str);
+ variable_set(str, 0);
+ i = DITEM_SUCCESS;
+ }
+ else {
+ /* A command might be a pathname if it's encoded in argv[0], which
+ we also support */
+ if ((cp = rindex(str, '/')) != NULL)
+ str = cp + 1;
+ if (isDebug())
+ msgDebug("dispatch: calling resword `%s'\n", str);
+ if (!call_possible_resword(str, NULL, &i)) {
+ msgNotify("Warning: No such command ``%s''", str);
+ i = DITEM_FAILURE;
+ }
+ /*
+ * Allow a user to prefix a command with "noError" to cause
+ * us to ignore any errors for that one command.
+ */
+ if (i != DITEM_SUCCESS && variable_get(VAR_NO_ERROR))
+ i = DITEM_SUCCESS;
+ variable_unset(VAR_NO_ERROR);
+ }
+ return i;
+}
+
+
+/*
+ * File processing
+ */
+
+static qelement *
+dispatch_load_fp(FILE *fp)
+{
+ qelement *head;
+ char buf[BUFSIZ], *cp;
+
+ head = malloc(sizeof(qelement));
+
+ if (!head)
+ return NULL;
+
+ INITQUE(*head);
+
+ while (fgets(buf, sizeof buf, fp)) {
+
+ if ((cp = strchr(buf, '\n')) != NULL)
+ *cp = '\0';
+ if (*buf == '\0' || *buf == '#')
+ continue;
+
+ if (!dispatch_add_command(head, buf))
+ return NULL;
+ }
+
+ return head;
+}
+
+static int
+dispatch_execute(qelement *head)
+{
+ int result = DITEM_SUCCESS;
+ command_buffer *item;
+ char *old_interactive;
+
+ if (!head)
+ return result | DITEM_FAILURE;
+
+ old_interactive = variable_get(VAR_NONINTERACTIVE);
+ if (old_interactive)
+ old_interactive = strdup(old_interactive); /* save copy */
+
+ /* Hint to others that we're running from a script, should they care */
+ variable_set2(VAR_NONINTERACTIVE, "yes", 0);
+
+ while (!EMPTYQUE(*head)) {
+ item = (command_buffer *) head->q_forw;
+
+ if (DITEM_STATUS(dispatchCommand(item->string)) != DITEM_SUCCESS) {
+ msgConfirm("Command `%s' failed - rest of script aborted.\n",
+ item->string);
+ result |= DITEM_FAILURE;
+ break;
+ }
+ dispatch_free_command(item);
+ }
+
+ dispatch_free_all(head);
+
+ if (!old_interactive)
+ variable_unset(VAR_NONINTERACTIVE);
+ else {
+ variable_set2(VAR_NONINTERACTIVE, old_interactive, 0);
+ free(old_interactive);
+ }
+
+ return result;
+}
+
+int
+dispatch_load_file_int(int quiet)
+{
+ FILE *fp;
+ char *cp;
+ int i;
+ qelement *list;
+
+ static const char *names[] = {
+ "install.cfg",
+ "/stand/install.cfg",
+ "/tmp/install.cfg",
+ NULL
+ };
+
+ fp = NULL;
+ cp = variable_get(VAR_CONFIG_FILE);
+ if (!cp) {
+ for (i = 0; names[i]; i++)
+ if ((fp = fopen(names[i], "r")) != NULL)
+ break;
+ } else
+ fp = fopen(cp, "r");
+
+ if (!fp) {
+ if (!quiet)
+ msgConfirm("Unable to open %s: %s", cp, strerror(errno));
+ return DITEM_FAILURE;
+ }
+
+ list = dispatch_load_fp(fp);
+ fclose(fp);
+
+ return dispatch_execute(list);
+}
+
+int
+dispatch_load_file(dialogMenuItem *self)
+{
+ return dispatch_load_file_int(FALSE);
+}
+
+int
+dispatch_load_floppy(dialogMenuItem *self)
+{
+ int what = DITEM_SUCCESS;
+ extern char *distWanted;
+ char *cp;
+ FILE *fp;
+ qelement *list;
+
+ mediaClose();
+ cp = variable_get_value(VAR_INSTALL_CFG,
+ "Specify the name of a configuration file\n"
+ "residing on a MSDOS or UFS floppy.", 0);
+ if (!cp || !*cp) {
+ variable_unset(VAR_INSTALL_CFG);
+ what |= DITEM_FAILURE;
+ return what;
+ }
+
+ distWanted = cp;
+ /* Try to open the floppy drive */
+ if (DITEM_STATUS(mediaSetFloppy(NULL)) == DITEM_FAILURE) {
+ msgConfirm("Unable to set media device to floppy.");
+ what |= DITEM_FAILURE;
+ mediaClose();
+ return what;
+ }
+
+ if (!DEVICE_INIT(mediaDevice)) {
+ msgConfirm("Unable to mount floppy filesystem.");
+ what |= DITEM_FAILURE;
+ mediaClose();
+ return what;
+ }
+
+ fp = DEVICE_GET(mediaDevice, cp, TRUE);
+ if (fp) {
+ list = dispatch_load_fp(fp);
+ fclose(fp);
+ mediaClose();
+
+ what |= dispatch_execute(list);
+ }
+ else {
+ if (!variable_get(VAR_NO_ERROR))
+ msgConfirm("Configuration file '%s' not found.", cp);
+ variable_unset(VAR_INSTALL_CFG);
+ what |= DITEM_FAILURE;
+ mediaClose();
+ }
+ return what;
+}
+
diff --git a/usr.sbin/sysinstall/dist.c b/usr.sbin/sysinstall/dist.c
new file mode 100644
index 0000000..b35ec9e
--- /dev/null
+++ b/usr.sbin/sysinstall/dist.c
@@ -0,0 +1,933 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <libutil.h>
+
+unsigned int Dists;
+unsigned int CRYPTODists;
+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 CRYPTODistTable[];
+extern Distribution SrcDistTable[];
+extern Distribution XF86DistTable[];
+extern Distribution XF86FontDistTable[];
+extern Distribution XF86ServerDistTable[];
+
+/* The top-level distribution categories */
+static Distribution DistTable[] = {
+{ "base", "/", &Dists, DIST_BASE, 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 },
+{ "crypto", "/", &Dists, DIST_CRYPTO, CRYPTODistTable },
+#ifdef __i386__
+{ "compat1x", "/", &Dists, DIST_COMPAT1X, NULL },
+{ "compat20", "/", &Dists, DIST_COMPAT20, NULL },
+{ "compat21", "/", &Dists, DIST_COMPAT21, NULL },
+{ "compat22", "/", &Dists, DIST_COMPAT22, NULL },
+{ "compat3x", "/", &Dists, DIST_COMPAT3X, NULL },
+{ "compat4x", "/", &Dists, DIST_COMPAT4X, NULL },
+#endif
+{ "ports", "/usr", &Dists, DIST_PORTS, NULL },
+{ "local", "/", &Dists, DIST_LOCAL, NULL },
+{ "XF86336", "/usr", &Dists, DIST_XF86, XF86DistTable },
+{ NULL },
+};
+
+/* The CRYPTO distribution */
+static Distribution CRYPTODistTable[] = {
+{ "crypto", "/", &CRYPTODists, DIST_CRYPTO_CRYPTO, NULL },
+{ "krb4", "/", &CRYPTODists, DIST_CRYPTO_KERBEROS4, NULL },
+{ "krb5", "/", &CRYPTODists, DIST_CRYPTO_KERBEROS5, NULL },
+{ "ssecure", "/usr/src", &CRYPTODists, DIST_CRYPTO_SSECURE, NULL },
+{ "scrypto", "/usr/src", &CRYPTODists, DIST_CRYPTO_SCRYPTO, NULL },
+{ "skrb4", "/usr/src", &CRYPTODists, DIST_CRYPTO_SKERBEROS4, NULL },
+{ "skrb5", "/usr/src", &CRYPTODists, DIST_CRYPTO_SKERBEROS5, 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 },
+{ "stools", "/usr/src", &SrcDists, DIST_SRC_TOOLS, NULL },
+{ NULL },
+};
+
+/* The XFree86 distribution */
+static Distribution XF86DistTable[] = {
+{ "XF86336", "/usr/X11R6", &XF86Dists, DIST_XF86_FONTS, XF86FontDistTable },
+{ "XF86336", "/usr/X11R6", &XF86Dists, DIST_XF86_SERVER, XF86ServerDistTable },
+{ "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 },
+#if defined(__i386__) && defined(PC98)
+{ "Xlk98", "/usr/X11R6", &XF86Dists, DIST_XF86_LKIT98, NULL },
+#endif
+{ "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 },
+#if defined(__i386__) && defined(PC98)
+{ "X9set", "/usr/X11R6", &XF86Dists, DIST_XF86_9SET, NULL },
+#endif
+{ NULL },
+};
+
+/* The XFree86 server distribution */
+static Distribution XF86ServerDistTable[] = {
+#if defined(__i386__) && defined(PC98)
+{ "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 },
+#endif
+{ "Servers/X3DL", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_3DL, NULL },
+#ifdef __i386__
+{ "Servers/X8514", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_8514, NULL },
+{ "Servers/XAGX", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_AGX, NULL },
+#endif
+{ "Servers/XI128", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_I128, NULL },
+#ifdef __i386__
+{ "Servers/XMa8", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_MACH8, NULL },
+{ "Servers/XMa32", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_MACH32,NULL },
+#endif
+{ "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 },
+#ifdef __i386__
+{ "Servers/XVG16", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_VGA16, NULL },
+{ "Servers/XW32", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_W32, NULL },
+#endif
+#ifdef __alpha__
+{ "Servers/XTGA", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_TGA, NULL },
+#endif
+{ 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 distMaybeSetPorts(dialogMenuItem *self);
+
+static void
+distVerifyFlags(void)
+{
+ if (SrcDists)
+ Dists |= DIST_SRC;
+ if (CRYPTODists) {
+ if (CRYPTODists & (DIST_CRYPTO_KERBEROS4 | DIST_CRYPTO_KERBEROS5))
+ CRYPTODists |= DIST_CRYPTO_CRYPTO;
+ Dists |= DIST_CRYPTO;
+ }
+ else if ((Dists & DIST_CRYPTO) && !CRYPTODists)
+ CRYPTODists |= DIST_CRYPTO_ALL;
+ 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 (isDebug())
+ msgDebug("Dist Masks: Dists: %0x, CRYPTO: %0x, Srcs: %0x\nXServer: %0x, XFonts: %0x, XDists: %0x\n",
+ Dists, CRYPTODists, SrcDists, XF86ServerDists, XF86FontDists, XF86Dists);
+}
+
+int
+distReset(dialogMenuItem *self)
+{
+ Dists = 0;
+ CRYPTODists = 0;
+ SrcDists = 0;
+ XF86Dists = 0;
+ XF86ServerDists = 0;
+ XF86FontDists = 0;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+int
+distConfig(dialogMenuItem *self)
+{
+ char *cp;
+
+ distReset(NULL);
+
+ if ((cp = variable_get(VAR_DIST_MAIN)) != NULL)
+ Dists = atoi(cp);
+
+ if ((cp = variable_get(VAR_DIST_CRYPTO)) != NULL)
+ CRYPTODists = atoi(cp);
+
+ if ((cp = variable_get(VAR_DIST_SRC)) != NULL)
+ SrcDists = atoi(cp);
+
+ if ((cp = variable_get(VAR_DIST_X11)) != NULL)
+ XF86Dists = atoi(cp);
+
+ if ((cp = variable_get(VAR_DIST_XSERVER)) != NULL)
+ XF86ServerDists = atoi(cp);
+
+ if ((cp = variable_get(VAR_DIST_XFONTS)) != NULL)
+ XF86FontDists = atoi(cp);
+ distVerifyFlags();
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+distSetX(void)
+{
+ Dists |= DIST_XF86;
+ XF86Dists = DIST_XF86_BIN | DIST_XF86_SET | DIST_XF86_CFG | DIST_XF86_LIB | DIST_XF86_PROG | DIST_XF86_MAN | DIST_XF86_DOC | DIST_XF86_SERVER | DIST_XF86_FONTS;
+ XF86ServerDists = DIST_XF86_SERVER_SVGA | DIST_XF86_SERVER_VGA16;
+ XF86FontDists = DIST_XF86_FONTS_MISC;
+#ifndef X_AS_PKG
+ return distSetXF86(NULL);
+#endif
+ return DITEM_SUCCESS;
+}
+
+int
+distSetDeveloper(dialogMenuItem *self)
+{
+ int i;
+
+ distReset(NULL);
+ Dists = _DIST_DEVELOPER;
+ SrcDists = DIST_SRC_ALL;
+ CRYPTODists = DIST_CRYPTO_ALL;
+ i = distMaybeSetPorts(self);
+ distVerifyFlags();
+ return i;
+}
+
+int
+distSetXDeveloper(dialogMenuItem *self)
+{
+ int i;
+
+ i = distSetDeveloper(self);
+ i |= distSetX();
+ distVerifyFlags();
+ return i;
+}
+
+int
+distSetKernDeveloper(dialogMenuItem *self)
+{
+ int i;
+
+ distReset(NULL);
+ Dists = _DIST_DEVELOPER;
+ SrcDists = DIST_SRC_SYS;
+ CRYPTODists |= DIST_CRYPTO_BIN;
+ i = distMaybeSetPorts(self);
+ distVerifyFlags();
+ return i;
+}
+
+int
+distSetXKernDeveloper(dialogMenuItem *self)
+{
+ int i;
+
+ i = distSetKernDeveloper(self);
+ i |= distSetX();
+ distVerifyFlags();
+ return i;
+}
+
+int
+distSetUser(dialogMenuItem *self)
+{
+ int i;
+
+ distReset(NULL);
+ Dists = _DIST_USER;
+ CRYPTODists |= DIST_CRYPTO_CRYPTO;
+ i = distMaybeSetPorts(self);
+ distVerifyFlags();
+ return i;
+}
+
+int
+distSetXUser(dialogMenuItem *self)
+{
+ int i;
+
+ i = distSetUser(self);
+ i |= distSetX();
+ distVerifyFlags();
+ return i;
+}
+
+int
+distSetMinimum(dialogMenuItem *self)
+{
+ distReset(NULL);
+ Dists = DIST_BASE | DIST_CRYPTO;
+ CRYPTODists |= DIST_CRYPTO_CRYPTO;
+ distVerifyFlags();
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+int
+distSetEverything(dialogMenuItem *self)
+{
+ int i;
+
+ Dists = DIST_ALL | DIST_XF86;
+ SrcDists = DIST_SRC_ALL;
+ CRYPTODists = DIST_CRYPTO_ALL;
+ XF86Dists = DIST_XF86_ALL;
+ XF86ServerDists = DIST_XF86_SERVER_ALL;
+ XF86FontDists = DIST_XF86_FONTS_ALL;
+ i = distMaybeSetPorts(self);
+ distVerifyFlags();
+ return i;
+}
+
+static int
+distMaybeSetPorts(dialogMenuItem *self)
+{
+ dialog_clear_norefresh();
+ if (!msgYesNo("Would you like to install the FreeBSD ports collection?\n\n"
+ "This will give you ready access to over 7000 ported software packages,\n"
+ "at a cost of around 165MB of disk space when \"clean\" and possibly\n"
+ "much more than that when a lot of the distribution tarballs are loaded\n"
+ "(unless you have the extra discs available from a FreeBSD CD/DVD distribution\n"
+ "and can mount them on /cdrom, in which case this is far less of a problem).\n\n"
+ "The ports collection is a very valuable resource and well worth having\n"
+ "on your /usr partition, so it is advisable to say Yes to this option.\n\n"
+ "For more information on the ports collection & the latest ports, visit:\n"
+ " http://www.freebsd.org/ports\n"))
+ Dists |= DIST_PORTS;
+ else
+ Dists &= ~DIST_PORTS;
+ return DITEM_SUCCESS | DITEM_RESTORE;
+}
+
+static Boolean
+distSetByName(Distribution *dist, char *name)
+{
+ int i, status = FALSE;
+
+ /* Loop through current set */
+ for (i = 0; dist[i].my_name; i++) {
+ /* 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;
+}
+
+static Boolean
+distUnsetByName(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 (distUnsetByName(dist[i].my_dist, name)) {
+ status = TRUE;
+ }
+ }
+ }
+ return status;
+}
+
+/* Just for the dispatch stuff */
+int
+distSetCustom(dialogMenuItem *self)
+{
+ char *cp, *cp2, *tmp;
+
+ if (!(tmp = variable_get(VAR_DISTS))) {
+ msgDebug("distSetCustom() called without %s variable set.\n", VAR_DISTS);
+ return DITEM_FAILURE;
+ }
+
+ cp = alloca(strlen(tmp) + 1);
+ if (!cp)
+ msgFatal("Couldn't alloca() %d bytes!\n", (int)(strlen(tmp) + 1));
+ strcpy(cp, tmp);
+ while (cp) {
+ if ((cp2 = index(cp, ' ')) != NULL)
+ *(cp2++) = '\0';
+ if (!distSetByName(DistTable, cp))
+ msgDebug("distSetCustom: Warning, no such release \"%s\"\n", cp);
+ cp = cp2;
+ }
+ distVerifyFlags();
+ return DITEM_SUCCESS;
+}
+
+/* Just for the dispatch stuff */
+int
+distUnsetCustom(dialogMenuItem *self)
+{
+ char *cp, *cp2, *tmp;
+
+ if (!(tmp = variable_get(VAR_DISTS))) {
+ msgDebug("distUnsetCustom() called without %s variable set.\n", VAR_DISTS);
+ return DITEM_FAILURE;
+ }
+
+ cp = alloca(strlen(tmp) + 1);
+ if (!cp)
+ msgFatal("Couldn't alloca() %d bytes!\n", (int)(strlen(tmp) + 1));
+ strcpy(cp, tmp);
+ while (cp) {
+ if ((cp2 = index(cp, ' ')) != NULL)
+ *(cp2++) = '\0';
+ if (!distUnsetByName(DistTable, cp))
+ msgDebug("distUnsetCustom: Warning, no such release \"%s\"\n", cp);
+ cp = cp2;
+ }
+ return DITEM_SUCCESS;
+}
+
+int
+distSetSrc(dialogMenuItem *self)
+{
+ int i;
+
+ dialog_clear_norefresh();
+ if (!dmenuOpenSimple(&MenuSrcDistributions, FALSE))
+ i = DITEM_FAILURE;
+ else
+ i = DITEM_SUCCESS;
+ distVerifyFlags();
+ return i | DITEM_RESTORE;
+}
+
+int
+distSetXF86(dialogMenuItem *self)
+{
+ int i = DITEM_SUCCESS;
+
+ dialog_clear_norefresh();
+ if (!dmenuOpenSimple(&MenuXF86Select, FALSE))
+ i = DITEM_FAILURE;
+ distVerifyFlags();
+ return i | DITEM_RESTORE;
+}
+
+static Boolean got_intr = FALSE;
+
+/* timeout handler */
+static void
+handle_intr(int sig)
+{
+ msgDebug("User generated interrupt.\n");
+ got_intr = TRUE;
+}
+
+static int
+check_for_interrupt(void)
+{
+ if (got_intr) {
+ got_intr = FALSE;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static Boolean
+distExtract(char *parent, Distribution *me)
+{
+ int i,j, status, total, intr, unmounted_dev;
+ int cpid, zpid, fd2, chunk, numchunks;
+ char *path, *dist, buf[300000];
+ const char *tmp;
+ FILE *fp;
+ WINDOW *w = savescr();
+ struct timeval start, stop;
+ struct sigaction old, new;
+ properties dist_attr = NULL;
+
+ status = TRUE;
+ if (isDebug())
+ msgDebug("distExtract: parent: %s, me: %s\n", parent ? parent : "(none)", me->my_name);
+
+ /* Make ^C fake a sudden timeout */
+ new.sa_handler = handle_intr;
+ new.sa_flags = 0;
+ (void)sigemptyset(&new.sa_mask);
+ dialog_clear_norefresh();
+ dialog_msgbox("Please Wait", "Extracting all requested distributions...", -1, -1, 0);
+ sigaction(SIGINT, &new, &old);
+
+ /* Loop through to see if we're in our parent's plans */
+ for (i = 0; me[i].my_name; i++) {
+ dist = me[i].my_name;
+ path = parent ? parent : dist;
+
+ /* If our bit isn't set, go to the next */
+ if (!(me[i].my_bit & *(me[i].my_mask)))
+ continue;
+
+ /* 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 = DEVICE_GET(mediaDevice, buf, TRUE);
+ intr = check_for_interrupt();
+ if (fp == (FILE *)IO_ERROR || intr || !mediaDevice) {
+ /* Hard error, can't continue */
+ if (!msgYesNo("Unable to open %s: %s.\nReinitialize media?",
+ buf, !intr ? "I/O error." : "User interrupt.")) {
+ DEVICE_SHUTDOWN(mediaDevice);
+ if (!DEVICE_INIT(mediaDevice)) {
+ status = FALSE;
+ goto done;
+ }
+ else
+ goto getinfo;
+ }
+ else {
+ status = FALSE;
+ goto done;
+ }
+ }
+ else if (fp > 0) {
+ if (isDebug())
+ msgDebug("Parsing attributes file for distribution %s\n", dist);
+
+ dist_attr = properties_read(fileno(fp));
+ intr = check_for_interrupt();
+ if (intr || !dist_attr) {
+ msgConfirm("Cannot parse information file for the %s distribution: %s\n"
+ "Please verify that your media is valid and try again.",
+ dist, !intr ? "I/O error" : "User interrupt");
+ }
+ else {
+ tmp = property_find(dist_attr, "Pieces");
+ if (tmp)
+ numchunks = strtol(tmp, 0, 0);
+ }
+ fclose(fp);
+ if (!numchunks)
+ continue;
+ }
+ else {
+ /* Try to get the distribution as a single file */
+ snprintf(buf, sizeof buf, "%s/%s.%s", path, dist,
+ USE_GZIP ? "tgz" : "tbz");
+ /*
+ * Passing TRUE as 3rd parm to get routine makes this a "probing"
+ * get, for which errors are not considered too significant.
+ */
+ getsingle:
+ fp = DEVICE_GET(mediaDevice, buf, TRUE);
+ intr = check_for_interrupt();
+ if (fp == (FILE *)IO_ERROR || intr || !mediaDevice) {
+ /* 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);
+ DEVICE_SHUTDOWN(mediaDevice);
+ if (!DEVICE_INIT(mediaDevice)) {
+ status = FALSE;
+ goto done;
+ }
+ else
+ goto getsingle;
+ }
+ else if (fp > 0) {
+ char *dir = root_bias(me[i].my_dir);
+
+ dialog_clear_norefresh();
+ msgNotify("Extracting %s into %s directory...", dist, dir);
+ status = mediaExtractDist(dir, dist, fp);
+ fclose(fp);
+ goto done;
+ }
+ else {
+ status = FALSE;
+ goto done;
+ }
+ }
+
+ /* 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);
+
+ if (me[i].my_bit == DIST_BASE && RunningAsInit && !Fake) {
+ unmounted_dev = 1;
+ unmount("/dev", MNT_FORCE);
+ } else
+ unmounted_dev = 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 */
+ dialog_clear_norefresh();
+ for (chunk = 0; chunk < numchunks; chunk++) {
+ int n, retval, last_msg, chunksize, realsize;
+ char prompt[80];
+
+ last_msg = 0;
+
+ getchunk:
+ snprintf(buf, sizeof buf, "cksum.%c%c", (chunk / 26) + 'a', (chunk % 26) + 'a');
+ tmp = property_find(dist_attr, buf);
+ chunksize = 0;
+ if (tmp) {
+ tmp=index(tmp, ' ');
+ chunksize = strtol(tmp, 0, 0);
+ }
+ 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 = DEVICE_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");
+ DEVICE_SHUTDOWN(mediaDevice);
+ if (!DEVICE_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));
+
+ realsize = 0;
+ while (1) {
+ int seconds;
+
+ n = fread(buf + realsize, 1, BUFSIZ, fp);
+ if (check_for_interrupt()) {
+ msgConfirm("Media read error: User interrupt.");
+ fclose(fp);
+ goto punt;
+ }
+ else if (n <= 0)
+ break;
+ total += n;
+ realsize += n;
+
+ /* Print statistics about how we're doing */
+ (void) gettimeofday(&stop, (struct timezone *)0);
+ stop.tv_sec = stop.tv_sec - start.tv_sec;
+ stop.tv_usec = stop.tv_usec - start.tv_usec;
+ if (stop.tv_usec < 0)
+ stop.tv_sec--, stop.tv_usec += 1000000;
+ seconds = stop.tv_sec + (stop.tv_usec / 1000000.0);
+ if (!seconds)
+ seconds = 1;
+
+ if (seconds != last_msg) {
+ last_msg = seconds;
+ msgInfo("%10d bytes read from %s dist, chunk %2d of %2d @ %.1f KBytes/sec.",
+ total, dist, chunk + 1, numchunks, (total / seconds) / 1000.0);
+ }
+ }
+ fclose(fp);
+
+ if (!chunksize || (realsize == chunksize)) {
+ /* No substitution necessary */
+ retval = write(fd2, buf, realsize);
+ if (retval != realsize) {
+ fclose(fp);
+ dialog_clear_norefresh();
+ msgConfirm("Write failure on transfer! (wrote %d bytes of %d bytes)", retval, realsize);
+ goto punt;
+ }
+ } else {
+ for (j = 0; j < realsize; j++) {
+ /* On finding CRLF, skip the CR; don't exceed end of buffer. */
+ if ((buf[j] != 0x0d) || (j == total - 1) || (buf[j + 1] != 0x0a)) {
+ retval = write(fd2, buf + j, 1);
+ if (retval != 1) {
+ fclose(fp);
+ dialog_clear_norefresh();
+ msgConfirm("Write failure on transfer! (wrote %d bytes of %d bytes)", j, chunksize);
+ goto punt;
+ }
+ }
+ }
+ }
+ }
+ close(fd2);
+ status = mediaExtractDistEnd(zpid, cpid);
+ goto done;
+
+ punt:
+ close(fd2);
+ mediaExtractDistEnd(zpid, cpid);
+ status = FALSE;
+
+ done:
+ if (!status) {
+ dialog_clear_norefresh();
+ if (me[i].my_dist) {
+ msgConfirm("Unable to transfer all components of the %s distribution.\n"
+ "You may wish to switch media types and try again.\n", me[i].my_name);
+ }
+ else if (me[i].my_bit != DIST_LOCAL) {
+ status = msgYesNo("Unable to transfer the %s distribution from\n%s.\n\n"
+ "Do you want to try to retrieve it again?",
+ me[i].my_name, mediaDevice->name);
+ if (!status)
+ --i;
+ }
+ }
+ /* If extract was successful, remove ourselves from further consideration */
+ if (status)
+ *(me[i].my_mask) &= ~(me[i].my_bit);
+ else
+ continue;
+ if (unmounted_dev) {
+ (void)mount("devfs", "/dev", 0, NULL);
+ unmounted_dev = 0;
+ }
+ }
+ properties_free(dist_attr);
+ 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];
+ WINDOW *w;
+#ifdef X_AS_PKG
+ int want_x_package = 0;
+#endif
+
+ /* paranoia */
+ if (!Dists) {
+ if (!dmenuOpenSimple(&MenuSubDistributions, FALSE) || !Dists)
+ return DITEM_FAILURE;
+ }
+
+ if (!mediaVerify() || !DEVICE_INIT(mediaDevice))
+ return DITEM_FAILURE;
+
+ old_dists = Dists;
+ distVerifyFlags();
+
+ dialog_clear_norefresh();
+ w = savescr();
+ msgNotify("Attempting to install all selected distributions..");
+
+#ifdef X_AS_PKG
+ /* Clear any XFree86 dist flags, but remember they were present. */
+ if(Dists & DIST_XF86)
+ want_x_package = 1;
+ Dists &= ~DIST_XF86;
+ /*Dists &= ~(DIST_XF86 | XF86Dists | XF86ServerDists | XF86FontDists);*/
+#endif
+
+ /* Try for 3 times around the loop, then give up. */
+ while (Dists && ++retries < 3)
+ distExtract(NULL, DistTable);
+
+#ifdef X_AS_PKG
+ if (want_x_package)
+ status |= installX11package(NULL);
+#endif
+
+ dialog_clear_norefresh();
+ /* Only do bin fixup if bin dist was successfully extracted */
+ if ((old_dists & DIST_BASE) && !(Dists & DIST_BASE))
+ status |= installFixupBin(self);
+#ifndef X_AS_PKG
+ if (old_dists & DIST_XF86)
+ status |= installFixupXFree(self);
+#endif
+
+ /* Clear any local dist flags now */
+ Dists &= ~DIST_LOCAL;
+
+ if (Dists) {
+ int col = 0;
+
+ buf[0] = '\0';
+ dialog_clear_norefresh();
+ printSelected(buf, Dists, DistTable, &col);
+ dialog_clear_norefresh();
+ if (col) {
+ msgConfirm("Couldn't extract the following distributions. This may\n"
+ "be because they were not available on the installation\n"
+ "media you've chosen:\n\n\t%s", buf);
+ }
+ }
+ restorescr(w);
+ return status;
+}
diff --git a/usr.sbin/sysinstall/dist.h b/usr.sbin/sysinstall/dist.h
new file mode 100644
index 0000000..2ab9688
--- /dev/null
+++ b/usr.sbin/sysinstall/dist.h
@@ -0,0 +1,138 @@
+/* $FreeBSD$ */
+
+#ifndef _DIST_H_INCLUDE
+#define _DIST_H_INCLUDE
+
+/* Bitfields for distributions - hope we never have more than 32! :-) */
+#define DIST_BASE 0x00001
+#define DIST_GAMES 0x00002
+#define DIST_MANPAGES 0x00004
+#define DIST_PROFLIBS 0x00008
+#define DIST_DICT 0x00010
+#define DIST_SRC 0x00020
+#define DIST_DOC 0x00040
+#define DIST_INFO 0x00080
+#ifdef __i386__ /* only applicable on x86 */
+#define DIST_COMPAT1X 0x00100
+#define DIST_COMPAT20 0x00200
+#define DIST_COMPAT21 0x00400
+#define DIST_COMPAT22 0x00800
+#define DIST_COMPAT3X 0x01000
+#endif
+#if defined(__i386__) || defined(__alpha__)
+#define DIST_COMPAT4X 0x02000
+#endif
+#define DIST_XF86 0x04000
+#define DIST_CRYPTO 0x08000
+#define DIST_CATPAGES 0x10000
+#define DIST_PORTS 0x20000
+#define DIST_LOCAL 0x40000
+#define DIST_ALL 0x7FFFF
+
+/* Subtypes for CRYPTO distribution */
+#define DIST_CRYPTO_CRYPTO 0x0001
+#define DIST_CRYPTO_SCRYPTO 0x0002
+#define DIST_CRYPTO_SSECURE 0x0004
+#if __FreeBSD__ <= 3
+#define DIST_CRYPTO_KERBEROS 0x0008
+#else
+#define DIST_CRYPTO_KERBEROS4 0x0008
+#define DIST_CRYPTO_KERBEROS5 0x0010
+#define DIST_CRYPTO_SKERBEROS4 0x0020
+#define DIST_CRYPTO_SKERBEROS5 0x0040
+#endif
+#define DIST_CRYPTO_ALL 0x007F
+
+/* Subtypes for SRC distribution */
+#define DIST_SRC_BASE 0x00001
+#define DIST_SRC_CONTRIB 0x00002
+#define DIST_SRC_GNU 0x00004
+#define DIST_SRC_ETC 0x00008
+#define DIST_SRC_GAMES 0x00010
+#define DIST_SRC_INCLUDE 0x00020
+#define DIST_SRC_LIB 0x00040
+#define DIST_SRC_LIBEXEC 0x00080
+#define DIST_SRC_TOOLS 0x00100
+#define DIST_SRC_RELEASE 0x00200
+#define DIST_SRC_SBIN 0x00400
+#define DIST_SRC_SHARE 0x00800
+#define DIST_SRC_SYS 0x01000
+#define DIST_SRC_UBIN 0x02000
+#define DIST_SRC_USBIN 0x04000
+#define DIST_SRC_BIN 0x08000
+#define DIST_SRC_ALL 0x0FFFF
+
+/* Subtypes for XFree86 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_MISC_ALL 0x0FFF
+#define DIST_XF86_SERVER 0x8000
+#ifdef __i386__
+#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
+#elif __alpha__
+#define DIST_XF86_SERVER_TGA 0x0000001
+#endif
+#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_3DL 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
+
+/* Canned distribution sets */
+#define _DIST_USER \
+ ( DIST_BASE | DIST_DOC | DIST_MANPAGES | DIST_DICT | DIST_CRYPTO )
+
+#define _DIST_DEVELOPER \
+ ( _DIST_USER | DIST_PROFLIBS | DIST_INFO | DIST_SRC )
+
+#define DIST_CRYPTO_BIN \
+ ( DIST_CRYPTO_CRYPTO | DIST_CRYPTO_KERBEROS4 | DIST_CRYPTO_KERBEROS5 )
+
+#endif /* _DIST_H_INCLUDE */
+
+
+
+
+
diff --git a/usr.sbin/sysinstall/dmenu.c b/usr.sbin/sysinstall/dmenu.c
new file mode 100644
index 0000000..7f113fc
--- /dev/null
+++ b/usr.sbin/sysinstall/dmenu.c
@@ -0,0 +1,316 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated for what's essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <errno.h>
+
+#define MAX_MENU 15
+
+static Boolean exited;
+
+int
+dmenuDisplayFile(dialogMenuItem *tmp)
+{
+ systemDisplayHelp((char *)tmp->data);
+ return DITEM_SUCCESS;
+}
+
+int
+dmenuSubmenu(dialogMenuItem *tmp)
+{
+ return (dmenuOpenSimple((DMenu *)(tmp->data), FALSE) ? DITEM_SUCCESS : DITEM_FAILURE);
+}
+
+int
+dmenuSystemCommand(dialogMenuItem *self)
+{
+ WINDOW *w = NULL; /* Keep lint happy */
+
+ /* If aux is set, the command is known not to produce any screen-spoiling output */
+ if (!self->aux)
+ w = savescr();
+ systemExecute((char *)self->data);
+ if (!self->aux)
+ restorescr(w);
+ return DITEM_SUCCESS;
+}
+
+int
+dmenuSystemCommandBox(dialogMenuItem *tmp)
+{
+ WINDOW *w = savescr();
+
+ use_helpfile(NULL);
+ use_helpline("Select OK to dismiss this dialog");
+ dialog_prgbox(tmp->title, (char *)tmp->data, 22, 76, 1, 1);
+ restorescr(w);
+ return DITEM_SUCCESS;
+}
+
+int
+dmenuExit(dialogMenuItem *tmp)
+{
+ exited = TRUE;
+ return DITEM_LEAVE_MENU;
+}
+
+int
+dmenuSetVariable(dialogMenuItem *tmp)
+{
+ variable_set((char *)tmp->data, *((char *)tmp->data) != '_');
+ return DITEM_SUCCESS;
+}
+
+int
+dmenuSetVariables(dialogMenuItem *tmp)
+{
+ char *cp1, *cp2;
+ char *copy = strdup((char *)tmp->data);
+
+ for (cp1 = copy; cp1 != NULL;) {
+ cp2 = index(cp1, ',');
+ if (cp2 != NULL) *cp2++ = '\0';
+ variable_set(cp1, *cp1 != '_');
+ cp1 = cp2;
+ }
+ free(copy);
+ return DITEM_SUCCESS;
+}
+
+int
+dmenuSetKmapVariable(dialogMenuItem *tmp)
+{
+ char *lang;
+ int err;
+
+ variable_set((char *)tmp->data, TRUE);
+ lang = variable_get(VAR_KEYMAP);
+ if (lang != NULL)
+ {
+ err = loadKeymap(lang);
+ if (err == -1)
+ msgConfirm("No appropriate keyboard map found, sorry.");
+ else if (err == -2)
+ msgConfirm("Error installing keyboard map, errno = %d.", errno);
+ }
+ return DITEM_SUCCESS;
+}
+
+int
+dmenuToggleVariable(dialogMenuItem *tmp)
+{
+ char *var, *cp;
+ int status;
+
+ if (!(var = strdup((char *)tmp->data))) {
+ msgConfirm("Incorrect data field for `%s'!", tmp->title);
+ return DITEM_FAILURE;
+ }
+ if (!(cp = index(var, '='))) {
+ msgConfirm("Data field for %s is not in var=value format!", tmp->title);
+ return DITEM_FAILURE;
+ }
+ status = variable_check(var);
+ *cp = '\0';
+ variable_set2(var, status ? "NO" : "YES", *var != '_');
+ free(var);
+ return DITEM_SUCCESS;
+}
+
+int
+dmenuISetVariable(dialogMenuItem *tmp)
+{
+ char *ans, *var;
+
+ if (!(var = (char *)tmp->data)) {
+ msgConfirm("Incorrect data field for `%s'!", tmp->title);
+ return DITEM_FAILURE;
+ }
+ ans = msgGetInput(variable_get(var), tmp->title, 1);
+ if (!ans)
+ return DITEM_FAILURE;
+ else if (!*ans)
+ variable_unset(var);
+ else
+ variable_set2(var, ans, *var != '_');
+ return DITEM_SUCCESS;
+}
+
+int
+dmenuSetFlag(dialogMenuItem *tmp)
+{
+ if (*((unsigned int *)tmp->data) & tmp->aux)
+ *((unsigned int *)tmp->data) &= ~tmp->aux;
+ else
+ *((unsigned int *)tmp->data) |= tmp->aux;
+ return DITEM_SUCCESS;
+}
+
+int
+dmenuSetValue(dialogMenuItem *tmp)
+{
+ *((unsigned int *)tmp->data) = tmp->aux;
+ return DITEM_SUCCESS;
+}
+
+/* Traverse menu but give user no control over positioning */
+Boolean
+dmenuOpenSimple(DMenu *menu, Boolean buttons)
+{
+ int choice, scroll, curr, max;
+
+ choice = scroll = curr = max = 0;
+ return dmenuOpen(menu, &choice, &scroll, &curr, &max, buttons);
+}
+
+/* Work functions for the state hook */
+int
+dmenuFlagCheck(dialogMenuItem *item)
+{
+ return (*((unsigned int *)item->data) & item->aux);
+}
+
+int
+dmenuVarCheck(dialogMenuItem *item)
+{
+ char *w;
+
+ w = (char *)item->aux;
+ if (!w)
+ w = (char *)item->data;
+ return variable_check(w);
+}
+
+int
+dmenuVarsCheck(dialogMenuItem *item)
+{
+ int res, init;
+ char *w, *cp1, *cp2;
+ char *copy;
+
+ w = (char *)item->aux;
+ if (!w)
+ w = (char *)item->data;
+ if (!w)
+ return FALSE;
+
+ copy = strdup(w);
+ res = TRUE;
+ init = FALSE;
+ for (cp1 = copy; cp1 != NULL;) {
+ init = TRUE;
+ cp2 = index(cp1, ',');
+ if (cp2 != NULL)
+ *cp2++ = '\0';
+ res = res && variable_check(cp1);
+ cp1 = cp2;
+ }
+ free(copy);
+ return res && init;
+}
+
+int
+dmenuRadioCheck(dialogMenuItem *item)
+{
+ return (*((unsigned int *)item->data) == item->aux);
+}
+
+static int
+menu_height(DMenu *menu, int n)
+{
+ int max;
+ char *t;
+
+ max = MAX_MENU;
+ if (StatusLine > 24)
+ max += StatusLine - 24;
+ for (t = menu->prompt; *t; t++) {
+ if (*t == '\n')
+ --max;
+ }
+ return n > max ? max : n;
+}
+
+/* Traverse over an internal menu */
+Boolean
+dmenuOpen(DMenu *menu, int *choice, int *scroll, int *curr, int *max, Boolean buttons)
+{
+ int n, rval = 0;
+ dialogMenuItem *items;
+
+ items = menu->items;
+ if (buttons)
+ items += 2;
+ /* Count up all the items */
+ for (n = 0; items[n].title; n++);
+
+ while (1) {
+ char buf[FILENAME_MAX];
+ WINDOW *w = savescr();
+
+ /* Any helpful hints, put 'em up! */
+ use_helpline(menu->helpline);
+ use_helpfile(systemHelpFile(menu->helpfile, buf));
+ dialog_clear_norefresh();
+ /* Pop up that dialog! */
+ if (menu->type & DMENU_NORMAL_TYPE)
+ rval = dialog_menu((u_char *)menu->title, (u_char *)menu->prompt, -1, -1,
+ menu_height(menu, n), -n, items, (char *)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);
+ if (exited) {
+ exited = FALSE;
+ restorescr(w);
+ return TRUE;
+ }
+ else if (rval) {
+ restorescr(w);
+ return FALSE;
+ }
+ else if (menu->type & DMENU_SELECTION_RETURNS) {
+ restorescr(w);
+ return TRUE;
+ }
+ }
+}
diff --git a/usr.sbin/sysinstall/doc.c b/usr.sbin/sysinstall/doc.c
new file mode 100644
index 0000000..c122592
--- /dev/null
+++ b/usr.sbin/sysinstall/doc.c
@@ -0,0 +1,125 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated for what's essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+
+ *
+ */
+
+#include "sysinstall.h"
+
+/*
+ * 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;
+ }
+
+ /* Run browser on the appropriate doc */
+ if (dmenuOpenSimple(&MenuHTMLDoc, FALSE))
+ return DITEM_SUCCESS;
+ else
+ return DITEM_FAILURE;
+}
+
+/* Try to show one of the documents requested from the HTML doc menu */
+int
+docShowDocument(dialogMenuItem *self)
+{
+ char tmp[512], target[512];
+ char *where, *browser = variable_get(VAR_BROWSER_BINARY);
+ char *str = self->prompt;
+
+ if (!file_executable(browser)) {
+ msgConfirm("Can't find the browser in %s! Please ensure that it's\n"
+ "properly set in the Options editor.", browser);
+ return DITEM_FAILURE;
+ }
+ /* Default to Home */
+ where = strcpy(target, "http://www.freebsd.org");
+ if (strstr(str, "Other")) {
+ where = msgGetInput("http://www.freebsd.org", "Please enter the URL of the location you wish to visit.");
+ if (where)
+ strcpy(target, where);
+ }
+ else if (strstr(str, "FAQ")) {
+ where = strcpy(target, "/usr/share/doc/faq/index.html");
+ if (!file_readable(target))
+ where = strcpy(target, "http://www.freebsd.org/FAQ");
+ }
+ else if (strstr(str, "Handbook")) {
+ where = strcpy(target, "/usr/share/doc/handbook/index.html");
+ if (!file_readable(target))
+ where = strcpy(target, "http://www.freebsd.org/handbook");
+ }
+ if (where) {
+ sprintf(tmp, "%s %s", browser, target);
+ systemExecute(tmp);
+ return DITEM_SUCCESS;
+ }
+ else {
+ msgConfirm("Hmmmmm! I can't seem to access the documentation you selected!\n"
+ "Have you loaded the base distribution? Is your network connected?");
+ return DITEM_FAILURE;
+ }
+}
diff --git a/usr.sbin/sysinstall/dos.c b/usr.sbin/sysinstall/dos.c
new file mode 100644
index 0000000..564e33f
--- /dev/null
+++ b/usr.sbin/sysinstall/dos.c
@@ -0,0 +1,94 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated to essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ * Copyright (c) 1995
+ * Gary J Palmer. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <sys/param.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <grp.h>
+#define MSDOSFS
+#include <sys/mount.h>
+#include <fs/msdosfs/msdosfsmount.h>
+#undef MSDOSFS
+
+static Boolean DOSMounted;
+static char mountpoint[] = "/dist";
+
+Boolean
+mediaInitDOS(Device *dev)
+{
+ struct msdosfs_args args;
+
+ if (DOSMounted)
+ return TRUE;
+
+ Mkdir(mountpoint);
+ memset(&args, 0, sizeof(args));
+ args.fspec = dev->devname;
+ args.uid = args.gid = 0;
+ args.mask = 0777;
+
+ if (mount("msdosfs", mountpoint, MNT_RDONLY, (caddr_t)&args) == -1) {
+ msgConfirm("Error mounting %s on %s: %s (%u)", args.fspec, mountpoint, strerror(errno), errno);
+ return FALSE;
+ }
+ DOSMounted = TRUE;
+ return TRUE;
+}
+
+FILE *
+mediaGetDOS(Device *dev, char *file, Boolean probe)
+{
+ return mediaGenericGet(mountpoint, file);
+}
+
+void
+mediaShutdownDOS(Device *dev)
+{
+ if (!DOSMounted)
+ return;
+ if (unmount(mountpoint, MNT_FORCE) != 0)
+ msgConfirm("Could not unmount the DOS partition from %s: %s",
+ mountpoint, strerror(errno));
+ else
+ DOSMounted = FALSE;
+ return;
+}
diff --git a/usr.sbin/sysinstall/floppy.c b/usr.sbin/sysinstall/floppy.c
new file mode 100644
index 0000000..d91813f
--- /dev/null
+++ b/usr.sbin/sysinstall/floppy.c
@@ -0,0 +1,189 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated to essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ * Copyright (c) 1995
+ * Gary J Palmer. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/* These routines deal with getting things off of floppy media */
+
+#include "sysinstall.h"
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <sys/param.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <grp.h>
+
+#define MSDOSFS
+#include <sys/mount.h>
+#include <fs/msdosfs/msdosfsmount.h>
+#undef MSDOSFS
+
+#include <ufs/ufs/ufsmount.h>
+static Boolean floppyMounted;
+
+char *distWanted;
+static char mountpoint[] = "/dist";
+
+Boolean
+mediaInitFloppy(Device *dev)
+{
+ struct msdosfs_args dosargs;
+ struct ufs_args u_args;
+ char *mp;
+#ifdef PC98
+ char fddev[24];
+#endif
+
+ if (floppyMounted)
+ return TRUE;
+
+ mp = dev->private ? (char *)dev->private : mountpoint;
+ if (Mkdir(mp)) {
+ msgConfirm("Unable to make %s directory mountpoint for %s!", mp, dev->devname);
+ return FALSE;
+ }
+
+ msgDebug("Init floppy called for %s distribution.\n", distWanted ? distWanted : "some");
+
+ if (!variable_get(VAR_NONINTERACTIVE)) {
+ if (!distWanted)
+ msgConfirm("Please insert floppy in %s", dev->description);
+ else
+ msgConfirm("Please insert floppy containing %s in %s",
+ distWanted, dev->description);
+ }
+
+ memset(&dosargs, 0, sizeof dosargs);
+#ifdef PC98
+ dosargs.fspec = fddev;
+#else
+ dosargs.fspec = dev->devname;
+#endif
+ dosargs.uid = dosargs.gid = 0;
+ dosargs.mask = 0777;
+
+ memset(&u_args, 0, sizeof(u_args));
+#ifdef PC98
+ u_args.fspec = fddev;
+#else
+ u_args.fspec = dev->devname;
+#endif
+
+#ifdef PC98
+ sprintf(fddev, "%s.1200", dev->devname);
+ if (mount("msdosfs", mp, MNT_RDONLY, (caddr_t)&dosargs) != -1)
+ goto success;
+ if (mount("ufs", mp, MNT_RDONLY, (caddr_t)&u_args) != -1)
+ goto success;
+
+ sprintf(fddev, "%s.1232", dev->devname);
+ if (mount("msdosfs", mp, MNT_RDONLY, (caddr_t)&dosargs) != -1)
+ goto success;
+ if (mount("ufs", mp, MNT_RDONLY, (caddr_t)&u_args) != -1)
+ goto success;
+
+ sprintf(fddev, "%s.1440", dev->devname);
+ if (mount("msdosfs", mp, MNT_RDONLY, (caddr_t)&dosargs) != -1)
+ goto success;
+ if (mount("ufs", mp, MNT_RDONLY, (caddr_t)&u_args) != -1)
+ goto success;
+#else
+ if (mount("msdosfs", mp, MNT_RDONLY, (caddr_t)&dosargs) != -1)
+ goto success;
+ if (mount("ufs", mp, MNT_RDONLY, (caddr_t)&u_args) != -1)
+ goto success;
+#endif /* PC98 */
+
+ msgConfirm("Error mounting floppy %s (%s) on %s : %s",
+ dev->name, dev->devname, mp, strerror(errno));
+ return FALSE;
+
+success:
+ floppyMounted = TRUE;
+ distWanted = NULL;
+ return TRUE;
+}
+
+FILE *
+mediaGetFloppy(Device *dev, char *file, Boolean probe)
+{
+ char buf[PATH_MAX], *mp;
+ FILE *fp;
+ int nretries = 5;
+
+ /*
+ * floppies don't use mediaGenericGet() because it's too expensive
+ * to speculatively open files on a floppy disk. Make user get it
+ * right or give up with floppies.
+ */
+ mp = dev->private ? (char *)dev->private : mountpoint;
+ snprintf(buf, PATH_MAX, "%s/%s", mp, file);
+ if (!file_readable(buf)) {
+ if (probe)
+ return NULL;
+ else {
+ while (!file_readable(buf)) {
+ if (!--nretries) {
+ msgConfirm("GetFloppy: Failed to get %s after retries;\ngiving up.", buf);
+ return NULL;
+ }
+ distWanted = buf;
+ mediaShutdownFloppy(dev);
+ if (!mediaInitFloppy(dev))
+ return NULL;
+ }
+ }
+ }
+ fp = fopen(buf, "r");
+ return fp;
+}
+
+void
+mediaShutdownFloppy(Device *dev)
+{
+ if (floppyMounted) {
+ char *mp = dev->private ? (char *)dev->private : mountpoint;
+
+ if (unmount(mp, MNT_FORCE) != 0)
+ msgDebug("Umount of floppy on %s failed: %s (%d)\n", mp, strerror(errno), errno);
+ else {
+ floppyMounted = FALSE;
+ if (!variable_get(VAR_NONINTERACTIVE) && variable_cmp(SYSTEM_STATE, "fixit"))
+ msgConfirm("You may remove the floppy from %s", dev->description);
+ }
+ }
+}
diff --git a/usr.sbin/sysinstall/ftp.c b/usr.sbin/sysinstall/ftp.c
new file mode 100644
index 0000000..14fd74b
--- /dev/null
+++ b/usr.sbin/sysinstall/ftp.c
@@ -0,0 +1,282 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated to essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/param.h>
+#include <sys/wait.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <ftpio.h>
+
+Boolean ftpInitted = FALSE;
+static FILE *OpenConn;
+int FtpPort;
+
+/* List of sub directories to look for under a given FTP server. */
+const char *ftp_dirs[] = { ".", "releases/"MACHINE, "snapshots/"MACHINE,
+ "pub/FreeBSD", "pub/FreeBSD/releases/"MACHINE,
+ "pub/FreeBSD/snapshots/"MACHINE, NULL };
+
+/* Brings up attached network device, if any - takes FTP device as arg */
+static Boolean
+netUp(Device *dev)
+{
+ Device *netdev = (Device *)dev->private;
+
+ if (netdev)
+ return DEVICE_INIT(netdev);
+ else
+ return TRUE; /* No net == happy net */
+}
+
+/* Brings down attached network device, if any - takes FTP device as arg */
+static void
+netDown(Device *dev)
+{
+ Device *netdev = (Device *)dev->private;
+
+ if (netdev)
+ DEVICE_SHUTDOWN(netdev);
+}
+
+Boolean
+mediaInitFTP(Device *dev)
+{
+ int i, code, af, fdir;
+ char *cp, *rel, *hostname, *dir;
+ char *user, *login_name, password[80];
+
+ if (ftpInitted)
+ return TRUE;
+
+ if (OpenConn) {
+ fclose(OpenConn);
+ OpenConn = NULL;
+ }
+
+ /* If we can't initialize the network, bag it! */
+ if (!netUp(dev))
+ return FALSE;
+
+try:
+ cp = variable_get(VAR_FTP_PATH);
+ if (!cp) {
+ if (DITEM_STATUS(mediaSetFTP(NULL)) == DITEM_FAILURE || (cp = variable_get(VAR_FTP_PATH)) == NULL) {
+ msgConfirm("Unable to get proper FTP path. FTP media not initialized.");
+ netDown(dev);
+ return FALSE;
+ }
+ }
+
+ hostname = variable_get(VAR_FTP_HOST);
+ dir = variable_get(VAR_FTP_DIR);
+ if (!hostname || !dir) {
+ msgConfirm("Missing FTP host or directory specification. FTP media not initialized,");
+ netDown(dev);
+ return FALSE;
+ }
+ user = variable_get(VAR_FTP_USER);
+ login_name = (!user || !*user) ? "anonymous" : user;
+
+ if (variable_get(VAR_FTP_PASS))
+ SAFE_STRCPY(password, variable_get(VAR_FTP_PASS));
+ else if (RunningAsInit)
+ sprintf(password, "installer@%s", variable_get(VAR_HOSTNAME));
+ else {
+ struct passwd *pw;
+ char *user;
+
+ pw = getpwuid(getuid());
+ user = pw ? pw->pw_name : "ftp";
+ sprintf(password, "%s@%s", user, variable_get(VAR_HOSTNAME));
+ }
+ af = variable_cmp(VAR_IPV6_ENABLE, "YES") ? AF_INET : AF_UNSPEC;
+ msgNotify("Logging in to %s@%s..", login_name, hostname);
+ if ((OpenConn = ftpLoginAf(hostname, af, login_name, password, FtpPort, isDebug(), &code)) == NULL) {
+ msgConfirm("Couldn't open FTP connection to %s:\n %s.", hostname, ftpErrString(code));
+ goto punt;
+ }
+
+ ftpPassive(OpenConn, !strcmp(variable_get(VAR_FTP_STATE), "passive"));
+ ftpBinary(OpenConn);
+ if (dir && *dir != '\0') {
+ if ((i = ftpChdir(OpenConn, dir)) != 0) {
+ if (i == 550)
+ msgConfirm("No such directory ftp://%s/%s\n"
+ "please check your URL and try again.", hostname, dir);
+ else
+ msgConfirm("FTP chdir to ftp://%s/%s returned error status:\n %s.", hostname, dir, ftpErrString(i));
+ goto punt;
+ }
+ }
+
+ /*
+ * Now that we've verified that the path we're given is ok, let's try to
+ * be a bit intelligent in locating the release we are looking for. First
+ * off, if the release is specified as "__RELEASE" or "any", then just
+ * assume that the current directory is the one we want and give up.
+ */
+ rel = variable_get(VAR_RELNAME);
+ if (strcmp(rel, "__RELEASE") && strcmp(rel, "any")) {
+ /*
+ * Ok, since we have a release variable, let's walk through the list
+ * of directories looking for a release directory. The first one to
+ * match wins. For each case, we chdir to ftp_dirs[fdir] first. If
+ * that fails, we skip to the next one. Otherwise, we try to chdir to
+ * rel. If it succeeds we break out. If it fails, then we go back to
+ * the base directory and try again. Lots of chdirs, but oh well. :)
+ */
+ for (fdir = 0; ftp_dirs[fdir]; fdir++) {
+ /* Avoid sending CWD . commands which confuse some ftp servers */
+ if (strcmp(ftp_dirs[fdir], ".") &&
+ (ftpChdir(OpenConn, (char *)ftp_dirs[fdir]) != 0))
+ continue;
+ if (ftpChdir(OpenConn, rel) == 0) {
+ ftpInitted = TRUE;
+ return TRUE;
+ }
+ else /* reset to "root" dir for a fresh try */
+ ftpChdir(OpenConn, "/");
+ }
+
+ /*
+ * If we get here, then all of the directories we tried failed, so
+ * print out the error message and ask the user if they want to try
+ * again.
+ */
+ if (!msgYesNo("Warning: Can't find the `%s' distribution on this\n"
+ "FTP server. You may need to visit a different server for\n"
+ "the release you are trying to fetch or go to the Options\n"
+ "menu and to set the release name to explicitly match what's\n"
+ "available on %s (or set to \"any\").\n\n"
+ "Would you like to select another FTP server?",
+ rel, hostname)) {
+ variable_unset(VAR_FTP_PATH);
+ if (DITEM_STATUS(mediaSetFTP(NULL)) != DITEM_FAILURE)
+ goto try;
+ }
+ } else {
+ ftpInitted = TRUE;
+ return TRUE;
+ }
+
+punt:
+ ftpInitted = FALSE;
+ if (OpenConn != NULL) {
+ fclose(OpenConn);
+ OpenConn = NULL;
+ }
+ netDown(dev);
+ variable_unset(VAR_FTP_PATH);
+ return FALSE;
+}
+
+FILE *
+mediaGetFTP(Device *dev, char *file, Boolean probe)
+{
+ int nretries = 1;
+ FILE *fp;
+ char *try, buf[PATH_MAX];
+
+ if (!OpenConn) {
+ msgDebug("No FTP connection open, can't get file %s\n", file);
+ return NULL;
+ }
+
+ try = file;
+ while ((fp = ftpGet(OpenConn, try, 0)) == NULL) {
+ int ftperr = ftpErrno(OpenConn);
+
+ /* If a hard fail, try to "bounce" the ftp server to clear it */
+ if (ftperr != 550) {
+ if (ftperr != 421) /* Timeout? */
+ variable_unset(VAR_FTP_PATH);
+ /* If we can't re-initialize, just forget it */
+ DEVICE_SHUTDOWN(dev);
+ if (!DEVICE_INIT(dev)) {
+ netDown(dev);
+ if (OpenConn) {
+ fclose(OpenConn);
+ OpenConn = NULL;
+ }
+ variable_unset(VAR_FTP_PATH);
+ return NULL;
+ }
+ }
+ else if (probe)
+ return NULL;
+ else {
+ /* Try some alternatives */
+ switch (nretries++) {
+ case 1:
+ sprintf(buf, "releases/%s", file);
+ try = buf;
+ break;
+
+ case 2:
+ sprintf(buf, "%s/%s", variable_get(VAR_RELNAME), file);
+ try = buf;
+ break;
+
+ case 3:
+ sprintf(buf, "%s/releases/%s", variable_get(VAR_RELNAME), file);
+ try = buf;
+ break;
+
+ case 4:
+ try = file;
+ break;
+ }
+ }
+ }
+ return fp;
+}
+
+void
+mediaShutdownFTP(Device *dev)
+{
+ if (!ftpInitted)
+ return;
+
+ if (OpenConn != NULL) {
+ fclose(OpenConn);
+ OpenConn = NULL;
+ }
+ ftpInitted = FALSE;
+}
diff --git a/usr.sbin/sysinstall/globals.c b/usr.sbin/sysinstall/globals.c
new file mode 100644
index 0000000..4323bd0
--- /dev/null
+++ b/usr.sbin/sysinstall/globals.c
@@ -0,0 +1,73 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+
+/*
+ * Various global variables and an initialization hook to set them to
+ * whatever values we feel are appropriate.
+ */
+
+int DebugFD; /* Where diagnostic output goes */
+Boolean Fake; /* Only pretend to be useful */
+Boolean RunningAsInit; /* Are we running as init? */
+Boolean DialogActive; /* Is libdialog initialized? */
+Boolean ColorDisplay; /* Are we on a color display? */
+Boolean OnVTY; /* Are we on a VTY? */
+Boolean Restarting; /* Are we restarting sysinstall? */
+Variable *VarHead; /* The head of the variable chain */
+Device *mediaDevice; /* Where we're installing from */
+int BootMgr; /* Which boot manager we're using */
+int StatusLine; /* Where to stick our status messages */
+jmp_buf BailOut; /* Beam me up, scotty! The natives are pissed! */
+
+/*
+ * Yes, I know some of these are already automatically initialized as
+ * globals. I simply find it clearer to set everything explicitly.
+ */
+void
+globalsInit(void)
+{
+ DebugFD = -1;
+ ColorDisplay = FALSE;
+ Fake = FALSE;
+ Restarting = FALSE;
+ OnVTY = FALSE;
+ DialogActive = FALSE;
+ VarHead = NULL;
+ mediaDevice = NULL;
+ RunningAsInit = FALSE;
+}
diff --git a/usr.sbin/sysinstall/help/anonftp.hlp b/usr.sbin/sysinstall/help/anonftp.hlp
new file mode 100644
index 0000000..e90985e
--- /dev/null
+++ b/usr.sbin/sysinstall/help/anonftp.hlp
@@ -0,0 +1,19 @@
+This screen allows you to configure the anonymous FTP user.
+
+The following configuration values are editable:
+
+UID: The user ID you wish to assign to the anonymous FTP user.
+ All files uploaded will be owned by this ID.
+
+Group: Which group you wish the anonymous FTP user to be in.
+
+Comment: String describing this user in /etc/passwd
+
+
+FTP Root Directory:
+
+ Where files available for anonymous FTP will be kept.
+
+Upload subdirectory:
+
+ Where files uploaded by anonymous FTP users will go.
diff --git a/usr.sbin/sysinstall/help/configure.hlp b/usr.sbin/sysinstall/help/configure.hlp
new file mode 100644
index 0000000..65be877
--- /dev/null
+++ b/usr.sbin/sysinstall/help/configure.hlp
@@ -0,0 +1,10 @@
+This menu allows you to configure your system after the installation
+process is complete. At the minimum, you should probably set the
+system manager's password and the system time zone.
+
+For extra goodies like bash, emacs, Pascal, etc., you should look at
+the Packages item in this menu.
+
+For setting the timezone after the system is installed, type
+``tzsetup''. For more information on the overall general system
+configuration, see the ``/etc/defaults/rc.conf'' file.
diff --git a/usr.sbin/sysinstall/help/distributions.hlp b/usr.sbin/sysinstall/help/distributions.hlp
new file mode 100644
index 0000000..53b691f
--- /dev/null
+++ b/usr.sbin/sysinstall/help/distributions.hlp
@@ -0,0 +1,42 @@
+DISTRIBUTION INFORMATION
+------------------------
+
+An ``X-'' prefixed before a distribution set means that the XFree86
+base distribution, libraries, manual pages, SVGA server and a set
+of default fonts will be selected in addition to the set itself.
+If you select such a set, you will also be presented with a set of
+menus for customizing the selections to your desired X Window System
+setup.
+
+Any distribution may be further customized by selecting the `Custom'
+item before leaving the menu.
+
+N.B. All references in this document to `complete source' mean the
+complete source tree minus any legally encumbered cryptography code.
+
+The current "canned" installations are provided:
+
+Developer: Base ("bin") distribution, man pages, dictionary
+ files, profiling libraries and the complete source tree.
+
+Kern-Developer: As above, but with only kernel sources instead of
+ the complete source tree.
+
+User: The base distribution, man pages, dictionary files and
+ the FreeBSD 1.x, 2.0, 2.1.x, 2.2.x and 3.x compatibility sets.
+
+Minimal: Only the base distribution.
+
+Everything: The base distribution, man pages, dictionary files,
+ profiling libraries, the FreeBSD compatibility libraries,
+ the complete source tree, games and your choice of XFree86
+ distribution components.
+
+ Note that the cryptography source code is NOT included
+ in this collection. You will need to select that by
+ hand if you're inside the United States.
+
+Custom: Allows you to create or modify your distribution set on
+ a piece-by-piece basis.
+
+Reset: Clear all currently selected distributions.
diff --git a/usr.sbin/sysinstall/help/drives.hlp b/usr.sbin/sysinstall/help/drives.hlp
new file mode 100644
index 0000000..946a1b2
--- /dev/null
+++ b/usr.sbin/sysinstall/help/drives.hlp
@@ -0,0 +1,92 @@
+Boot Manager Selection:
+-----------------------
+
+If you wish to switch between multiple operating systems on your
+machine, or if you are trying to install FreeBSD on a drive other than
+your 1st drive, then you must install a boot manager. In the case
+where you wish to boot off an alternate drive, it should also be noted
+that you still need to install a boot manager on the FIRST drive!
+Even if you do not intend to create a FreeBSD partition on that drive
+(e.g. it's being wholly used by something else), the boot manager
+still needs to reside on the first disk in order to function as a
+"redirector" for the boot process.
+
+To do this, simply select your 1st drive in the drive selection menu
+and when the partition editor comes up, don't make any changes - just
+(Q)uit. At the boot manager menu which follows, select the first
+option (install a boot manager) and then proceed to setup the other
+drive(s) for FreeBSD as normal.
+
+It should also be noted that "operating systems" such as Windows 95
+will completely overwrite your boot manager without so much as a
+polite "may I please destroy your boot manager?" prompt if you make
+the mistake of installing them second. If this happens to you after
+FreeBSD is already installed, all is not lost! Simply revisit your
+FreeBSD distribution directory and look for a tools/ subdirectory, in
+which you'll find "bootinst.exe" and "boot.bin". To reinstall, simply
+say "bootinst boot.bin" while in the tools/ subdirectory.
+
+
+If you see the boot manager displaying ``F?'' when you try to come up
+for the first time and it refuses to change, no matter how often you
+whap on the function key assigned to FreeBSD, then you have a geometry
+mismatch problem and you should read the next section for important
+information on how to prevent that exact problem from happening!
+
+
+Geometry Translation / Sharing the disk(s) with another OS:
+----------------------------------------------------------
+
+If you are going to actually install some portion of FreeBSD on a
+drive then PLEASE BE VERY CERTAIN that the Geometry reported in the
+Partition Editor is the correct one for your drive and controller
+combination!
+
+IDE drives often have a certain geometry set during the PC BIOS setup,
+or (in the case of larger IDE drives) have their geometry "translated"
+by either the IDE controller or a special boot-sector translation
+utility such as that by OnTrack Systems. In these cases, knowing the
+correct geometry gets even more complicated as it's not something you
+can easily tell by looking at the drive or the PC BIOS setup. The
+best way of verifying that your geometry is being correctly calculated
+in such situations is to boot DOS (from the hard disk, not a floppy!)
+and run the ``pfdisk'' utility provided in the tools/ subdirectory of
+the FreeBSD CDROM or FTP site. It will report the geometry that DOS
+sees, which is generally the correct one.
+
+If you have no DOS partition sharing the disk at all, then you may
+find that you have better luck with Geometry detection if you create a
+very small DOS partition first, before installing FreeBSD. Once
+FreeBSD is installed you can always delete it again if you need the
+space.
+
+It's actually not a bad idea (believe it or not) to have a small
+bootable DOS partition on your FreeBSD machine anyway: Should the
+machine become unstable or exhibit strange behavior at some point in
+the future (which is not uncommon behavior for PC hardware!) you can
+then at least use DOS for installing and running one of the
+commercially available system diagnostic utilities.
+
+IMPORTANT NOTE:
+
+Any root partition you try to boot from must also reside below the
+1024th cylinder. If you're using a translated geometry then this is
+probably not a problem, but if you are using a native disk geometry
+which exceeds 1024 cylinders then you could have a failure to boot if
+you end up installing a root partition (or even just the kernel file
+in a root partition) out past cylinder 1024. If you are trying to
+share your first disk with FreeBSD and another OS which was installed
+previously, you are particularly susceptible to this problem and should
+check your disk addresses very carefully.
+
+If you find that you have insufficient space below cylinder 1024 to
+make a root partition for FreeBSD (and again, this ONLY applies to the
+root partition - once FreeBSD's kernel is loaded, it doesn't care
+about the geometry issues) then you will probably need to install on a
+completely different disk (see the boot manager section above) or
+resize your existing partitions so that both operating systems can
+have boot partitions below cylinder 1024.
+
+You may blame IBM for the limitations of a 10 bit cylinder address.
+"No one will have a disk with more than 1024 cylinders." I'm sure
+someone said.
diff --git a/usr.sbin/sysinstall/help/fixit.hlp b/usr.sbin/sysinstall/help/fixit.hlp
new file mode 100644
index 0000000..e47394e
--- /dev/null
+++ b/usr.sbin/sysinstall/help/fixit.hlp
@@ -0,0 +1,7 @@
+A special shell will be launched by this option with a fixit floppy
+(or 2nd CDROM) mounted as /mnt2. This provides access to extra
+commands under /mnt2 as well as a more complete set of device files in
+/mnt2/dev. Some device operations, like fsck and disklabel, may
+therefore require you to go to /mnt2/dev and use the entries there
+rather than assuming that they will be present in the default /dev
+(which came from the boot floppy and is very minimal).
diff --git a/usr.sbin/sysinstall/help/html.hlp b/usr.sbin/sysinstall/help/html.hlp
new file mode 100644
index 0000000..e6f22c4
--- /dev/null
+++ b/usr.sbin/sysinstall/help/html.hlp
@@ -0,0 +1,20 @@
+In this screen, you can jump to remote or local HTML
+resources such as the FreeBSD Handbook & FreeBSD FAQ
+(Frequently Asked Questions) documents located in:
+
+ file:/usr/share/doc/
+
+If you've loaded the doc distribution.
+
+The default browser package used is links (a text based
+browser which can render tables), which will be
+automatically loaded from the installation media if it
+is available. You may change the selection of browser
+& browser package to auto-load by visiting the Options
+editor.
+
+In order to visit remote URLs, you naturally must have
+some sort of working Internet connection. If you have not
+yet brought up any network interfaces, please visit
+the ``Networking'' item in the Configuration menu
+before attempting to reference any http://.. style URLs.
diff --git a/usr.sbin/sysinstall/help/media.hlp b/usr.sbin/sysinstall/help/media.hlp
new file mode 100644
index 0000000..57ab36f
--- /dev/null
+++ b/usr.sbin/sysinstall/help/media.hlp
@@ -0,0 +1,54 @@
+You can install from the following types of media:
+
+ CDROM requires one of the following supported CDROM drives:
+ ATAPI - Any standard ATAPI CDROM driver hooked to
+ a supported controller <see Hardware Guide).
+ SCSI - Any standard SCSI CDROM drive hooked to
+ a supported controller (see Hardware Guide).
+
+
+ DOS A DOS primary partition with the required FreeBSD
+ distribution files copied onto it (e.g. C:\FREEBSD\)
+
+
+ FS Assuming a disk or partition with an existing
+ FreeBSD file system and distribution set on it,
+ get the distribution files from there.
+
+
+ Floppy Get distribution files from one or more DOS or UFS
+ formatted floppies. Such floppies are assumed to
+ contain the appropriate distribution pieces - see
+ ABOUT.TXT for more information about making floppy
+ distribution media.
+
+
+ FTP Get the distribution files from an anonymous ftp server
+ (you will be presented with a list). Please note that
+ you may invoke FTP in "Active" mode, "Passive" mode, or
+ via an HTTP proxy.
+
+ Active mode is the standard way of fetching files and
+ Passive mode is for use when you're behind a firewall or
+ some other security mechanism that blocks active FTP
+ connections. Using an HTTP proxy is sometimes necessary
+ for firewalls which block all FTP connections.
+
+ If you chose to enter your own URL in the FTP menu, please
+ note that all paths are *relative* to the home directory
+ of the user being logged in as. By default, this is the
+ user "ftp" (anonymous ftp) but you may change this in the
+ Options screen.
+
+
+ NFS Get the distribution files from an NFS server somewhere
+ (make sure that permissions on the server allow this!).
+ If this install method hangs on you or refuses to work
+ properly, you may need to set some special options for
+ your NFS server. See the Options screen for more details.
+
+
+ Tape Extract distribution files from tape into a temporary
+ directory and install from there. If the tape was created
+ with blocksize other than 20, you may wish to change this
+ in the Options screen.
diff --git a/usr.sbin/sysinstall/help/network_device.hlp b/usr.sbin/sysinstall/help/network_device.hlp
new file mode 100644
index 0000000..a65467b
--- /dev/null
+++ b/usr.sbin/sysinstall/help/network_device.hlp
@@ -0,0 +1,58 @@
+You can do network installations over 3 types of communications links:
+
+ Serial port: SLIP / PPP
+ Parallel port: PLIP (laplink cable)
+ Ethernet: A standard Ethernet controller (includes some
+ PCMCIA networking cards).
+
+SLIP support is rather primitive and limited primarily to directly
+connected links, such as a serial cable running between a laptop
+computer and another PC. The link must be hard-wired as the SLIP
+installation doesn't currently offer a dialing capability (that
+facility is offered by the PPP utility, which should be used in
+preference to SLIP whenever possible). When you choose the SLIP
+option, you'll be given the option of later editing the slattach
+command before it's run on the serial line. It is expected that
+you'll run slattach (or some equivalent command) on the other end of
+the link at that time and bring up the line. FreeBSD will then
+install itself at serial speeds of up to 115.2K/baud (the recommended
+speed for a hardwired cable).
+
+If you're using a modem then PPP is almost certainly your only choice.
+Make sure that you have your service provider's information handy as
+you'll need to know it fairly early in the installation process. You
+will need to know your service provider's IP address, the IP address
+of your provider's DNS server, and possibly your own IP address unless
+your ISP supports dynamic negotiation, most do. If you do not choose
+a PAP or CHAP login you will also need to know how to use the various
+"AT commands" to dial the ISP with your particular brand of modem as
+the PPP dialer provides only a very simple terminal emulator and has no
+"modem capabilities database". If you choose a PAP or CHAP login you
+can simply enter `dial' (without the quotes) at the ppp prompt if your
+modem uses the Hayes compatible AT command set.
+
+If a hard-wired connection to another FreeBSD (2.0R or later) machine
+is available, you might also consider installing over a "laplink"
+parallel port cable. The data rate over the parallel port is much
+higher than what is typically possible over a serial line, and speeds
+of over 50KB/sec are not uncommon.
+
+Finally, for the fastest possible network installation, an Ethernet
+adaptor is always a good choice! FreeBSD supports most common PC
+Ethernet cards, a table of which is provided in the FreeBSD Hardware
+Guide (see the Documentation menu on the boot floppy). If you are
+using one of the supported PCMCIA Ethernet cards, also be sure that
+it's plugged in _before_ the laptop is powered on! FreeBSD does not,
+unfortunately, currently support "hot insertion" of PCMCIA cards.
+
+You will also need to know your IP address on the network, the
+"netmask" value for your address class, and the name of your machine.
+Your system administrator can tell you which values to use for your
+particular network setup. If you will be referring to other hosts by
+name rather than IP address, you'll also need a name server and
+possibly the address of a gateway (if you're using PPP, it's your
+provider's IP address) to use in talking to it. If you do not know
+the answers to all or most of these questions then you should really
+probably talk to your system administrator FIRST before trying this
+type of installation! Choosing the wrong IP address on a busy network
+will NOT make you popular with your systems administrator! :-)
diff --git a/usr.sbin/sysinstall/help/options.hlp b/usr.sbin/sysinstall/help/options.hlp
new file mode 100644
index 0000000..1f32172
--- /dev/null
+++ b/usr.sbin/sysinstall/help/options.hlp
@@ -0,0 +1,124 @@
+The following options may be set from this screen. Use the SPACE key
+to toggle an option's value, Q to leave when you're done.
+
+NFS Secure: NFS server talks only on a secure port
+
+ This is most commonly used when talking to Sun workstations, which
+ will not talk NFS over "non privileged" ports.
+
+
+NFS Slow: User is using a slow PC or Ethernet card
+
+ Use this option if you have a slow PC (386) or an Ethernet card
+ with poor performance being "fed" by NFS on a higher-performance
+ workstation. This will throttle the workstation back to prevent
+ the PC from becoming swamped with data.
+
+
+Debugging: Turn on the extra debugging flag
+
+ This turns on a lot of extra noise over on the second screen
+ (ALT-F2 to see it, ALT-F1 to switch back). If your installation
+ should fail for any reason, PLEASE turn this flag on when
+ attempting to reproduce the problem. It will provide a lot of
+ extra debugging at the failure point and may be very helpful to
+ the developers in tracking such problems down!
+
+
+Yes To All: Assume "Yes" answers to all non-critical dialogs
+
+ This flag should be used with caution. It will essentially
+ decide NOT to ask the user about any "boundary" conditions that
+ might not constitute actual errors but may be warnings indicative
+ of other problems. It's most useful to those who are doing unattended
+ installs.
+
+
+FTP username: Specify username and password instead of anonymous.
+
+ By default, the installation attempts to log in as the
+ anonymous user. If you wish to log in as someone else,
+ specify the username and password with this option.
+
+
+Install Root: Specify some directory other than / as your "root".
+
+ This should be left as / unless you have a really good reason to
+ change it. One good reason might be if you were installing to a
+ disk other than your own, as might happen if you needed to prepare a
+ disk for another machine which couldn't load FreeBSD directly
+ for some reason.
+
+ Note: If you set this option, you will only be able to install
+ packages if the bin distribution is also installed (usually
+ the case anyway) since /usr/sbin/pkg_add will otherwise not be
+ found after the chroot() call.
+
+
+Editor: Specify which screen editor to use.
+
+ At various points during the installation it may be necessary
+ to customize some text file, at which point the user will be
+ thrown unceremoniously into a screen editor. A relatively
+ simplistic editor which shows its command set on-screen is
+ selected by default, but UNIX purists may wish to change this
+ setting to /usr/bin/vi
+
+
+Tape Blocksize: Specify block size in 512 byte blocks of tape.
+
+ This defaults to 20 blocks, which should work with most
+ tape drive + tar combinations. It may not allow your particular
+ drive to win any records for speed, however, and the more
+ adventurous among you might try experimenting with larger sizes.
+
+
+Extract Detail: How to show filenames on debug screen as they're extracted.
+
+ While a distribution is being extracted, the default detail level
+ of "high" will show the full file names as they're extracted.
+ If you would prefer a more terse form for this, namely dots, select
+ the "medium" detail level. If you want nothing to be printed
+ on the debugging screen during extraction, select "low".
+
+
+Release Name: Which release to attempt to load from installation media.
+
+ You should only change this option if you're really sure you know
+ what you are doing! This will change the release name used by
+ sysinstall when fetching components of any distributions, and
+ is a useful way of using a more recent installation boot floppy
+ with an older release (say, on CDROM).
+
+
+Browser Package: Which package to load for an HTML browser.
+
+ By default, this is set to links but may also be set to any other
+ text capable HTML browser for which a package exists. If you set this
+ to an X based browser, you will not be able to use it if you're running
+ in text mode! :)
+
+
+Browser Exec: Which binary to run for the HTML browser.
+
+ The full pathname to the main executable in Browser Package
+
+
+Media Type: Which media type is being used.
+
+ This is mostly informational and indicates which media type (if any)
+ was last selected in the Media menu. It's also a convenient short-cut
+ to the media menu itself.
+
+
+Package Temp: Where package temporary files should go
+
+ Some packages, like emacs, can use a LOT of temporary space - up to
+ 20 or 30MB. If you are going to configure a small / directory (and
+ hence a small /tmp) then you may wish to set this to point at another
+ location (say, /usr/tmp).
+
+
+Use Defaults: Use default values.
+
+ Reset all options back to their default values.
diff --git a/usr.sbin/sysinstall/help/partition.hlp b/usr.sbin/sysinstall/help/partition.hlp
new file mode 100644
index 0000000..ef328df
--- /dev/null
+++ b/usr.sbin/sysinstall/help/partition.hlp
@@ -0,0 +1,135 @@
+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).
+
+If you set (S)oftUpdates on a filesystem, it will cause the
+"Soft Updates" policy to be in effect for it. This basically causes
+both metadata and data blocks to be written asynchronously to disk,
+but with extra state information which causes the metadata and any
+related data blocks to be committed in a single transaction. This
+results in async metadata update speeds (which are considerably
+faster than the default sync) without the potential for data loss
+which could occur if you simply mounted the filesystem with purely
+"async" update policy and then had a power failure. If you wish
+to later turn the softupdates policy back off, use the command
+"tunefs -n disable devicename". NOTE: It is probably not wise
+to use this on your root filesystem unless you have a large
+(e.g. non-standard size) root. The reason is that smaller filesystems
+with significant activity can temporarily overflow if the soft updates
+policy results in free'd blocks not being "garbage collected" as fast
+as they're being requested.
+
+When you're done, type `Q' to exit.
+
+No actual changes will be made to the disk until you (C)ommit from the
+Install menu or (W)rite directly from this one. You're working with
+what is essentially a copy of the disk label(s), both here and in the
+FDISK Partition Editor, and the actual on-disk labels won't be
+affected by any changes you make until you explicitly say so.
diff --git a/usr.sbin/sysinstall/help/register.hlp b/usr.sbin/sysinstall/help/register.hlp
new file mode 100644
index 0000000..811f71d
--- /dev/null
+++ b/usr.sbin/sysinstall/help/register.hlp
@@ -0,0 +1,76 @@
+This screen allows you to register yourself with the FreeBSD Project's
+user counter & statistics database.
+
+** IT IS VERY IMPORTANT THAT YOU DO THIS! **
+
+Believe me, I hate filling out forms as much as anyone, and most
+people's understandable reaction to a registration form is to say "Eh,
+what's this? They want to send me junk mail and then on top of that
+they expect me to go to *extra* trouble in order to make it easy for
+them?! Forget it!"
+
+This is not that kind of registration, and I strongly urge you to take
+just a few minutes to read this and find out how much the simple act
+of registering can help both you and FreeBSD.
+
+1. It is very much in your best interest, as a FreeBSD user, to stand
+ up and be counted so that various software vendors will begin to
+ take you and your operating system seriously. There are numerous
+ ISVs (Independent Software Vendors) who would be only too happy
+ to port the kinds of applications that many FreeBSD users are
+ currently screaming for (everything from spreadsheets and word
+ processing packages to games) if they only had some idea that it
+ might be worth the trouble. The only way to convince the ISVs that
+ FreeBSD is worth their trouble is to show them how many users
+ we have, and to do that we need your registration! At this time
+ we literally do not know how many users FreeBSD has, and that's
+ hardly helpful when you're trying to convince someone to port
+ software to it.
+
+
+2. We will not send you *anything* you do not ask for. Some people
+ are genuinely interested in new product announcements for FreeBSD
+ or want to hear about security issues & other important advisories
+ as they come up, and for such people we've added registration
+ options for selecting various types of additional material they
+ might be interested in receiving as a side-effect of registration.
+
+ The default behavior is to NOT put the user on any special mailing
+ lists or provide their names in mailing list data sent to
+ (carefully screened) FreeBSD product advertisers - all of that must
+ be specifically requested during the registration.
+
+Most fields in the form are fairly self-explanatory. At the minimum,
+you should enter your first and last name as well as your email
+address so that we can weed obvious duplicates from the counter. You
+will NOT be sent any mail at this address unless you also sign up for
+one of the additional notification services, and it's only used to
+provide us with a way of differentiating "John Smith <smith@foo.org>"
+from "John Smith <jsm@bar.com>" in the simple, no-frills registration
+case. If you do not have an email address, some sort of postal
+address will serve the same purpose.
+
+If you also wish to receive the FreeBSD Newsletter, published and
+distributed free of charge by Walnut Creek CDROM in printed form,
+then you must specify some sort of postal address. Likewise, if you
+elect to receive notification on the email version then you should
+specify a valid Email address. Back-issues of the FreeBSD newsletter
+are available at ftp://ftp.freebsd.org/pub/FreeBSD/doc/newsletter/ .
+
+Should you wish to unsubscribe to the FreeBSD Newsletter or otherwise
+de-register yourself at a later time, you can simply send mail to
+register-request@freebsd.org. If you subscribe to the announce
+mailing list (and it's a good idea) then you can modify your
+subscription at any time by sending mail to majordomo@freebsd.org
+
+
+Your cooperation with this new registration service is greatly
+appreciated, and by taking just 5 minutes to fill this out now you
+will be helping us to gather data which will greatly assist FreeBSD in
+firmly establishing a position as a serious UN*X operating system
+contender.
+
+Regards,
+
+ Jordan Hubbard,
+ FreeBSD PR Officer
diff --git a/usr.sbin/sysinstall/help/security.hlp b/usr.sbin/sysinstall/help/security.hlp
new file mode 100644
index 0000000..33e52e2
--- /dev/null
+++ b/usr.sbin/sysinstall/help/security.hlp
@@ -0,0 +1,10 @@
+Please see the FreeBSD FAQ for more detailed information on security
+profiles. The following table is intended to give you a rough idea just
+which services are enabled (or disabled) by each of the canned security
+profiles:
+
+ Extreme Medium
+ ------- ------
+sendmail NO YES
+sshd NO YES
+securelevel YES (2) NO
diff --git a/usr.sbin/sysinstall/help/shortcuts.hlp b/usr.sbin/sysinstall/help/shortcuts.hlp
new file mode 100644
index 0000000..8964ead
--- /dev/null
+++ b/usr.sbin/sysinstall/help/shortcuts.hlp
@@ -0,0 +1,114 @@
+/stand/sysinstall now supports command-line "shortcuts" which can
+often replace outdated commands, like pkg_manage. Multiple commands
+can be invoked in sequence, and variables may be set on-the-fly to
+customize the installation program's behavior in various ways.
+
+Syntax:
+
+/stand/sysinstall [var=value ..] [command ..]
+
+Where "var" can be one or more of:
+
+blanktime Screen blank time setting in seconds
+bootManager Select boot manager: booteasy, standard or none
+browserBinary Which doc browser to use (default: links)
+browserPackage Which package to get browser from (default: links)
+cpioVerbose How verbose to be with cpio: high, medium or low
+debug Extra debugging?
+disk Which disk to operate on (wd0, da0, etc).
+domainname Domain name
+editor Which screen editor to use
+ifconfig_<iface> For each <iface> in network_devices
+ftpDirectory Root of the FreeBSD distribution tree on FTP server
+ftpOnError Set to retry or abort
+ftpPass Which password to use when logging into FTP server
+ftp Which FTP site/dir to use (URL ftp://site/dir/..)
+ftpPort Which FTP port to use (default: 21)
+ftpRetryCount How many times to retry a fetch operation
+ftpUser Which username to use when logging into FTP server
+ftpHost Which FTP hostname to use (overrides ftp variable)
+gated Use gated instead of routed
+defaultrouter IP address of default route
+geometry Geometry to use for selected disk ("cyl/hd/sec")
+hostname Fully qualified domain name for host.
+network_interfaces Which network interfaces to configure
+ipaddr IP address for this host's primary interface
+nameserver IP address of name server
+netmask Netmask for this host's primary interface
+nfs Full host:/path/ specification to NFS media
+nfsHost Host portion of nfs path
+nfsSecure Use NFS secure mount (-P flag)
+nfs_server Configure this machine as an NFS server
+noConfirm Don't ask for confirmation on non-fatal errors
+ntpDate Which ntp clock synchronization server to use
+pcnfsd Install the PCNFSD package
+ports Path to the ports collection
+releaseName Which FreeBSD release to install
+rootSize Size of the root partition to create for Auto
+routedflags Which flags to pass to routed, if enabled
+serialSpeed How fast to run a SLIP/PPP connection
+slowEthernetCard PC ethernet card is uncommonly slow
+swapSize Size of the swap partition to create for Auto
+tapeBlocksize Tape size in blocks
+ufs Full path to UFS media directory
+usrSize Size of the /usr partition to create for Auto
+varSize Size of the /var partition to create for Auto
+
+And "command" can be one or more of:
+
+addUser Add a new user to the system
+addGroup Add a new group to the system
+configAnonFTP Configure system for anonymous FTP
+configGated Configure and install gated
+configNFSServer Configure host as an NFS server
+configPackages Browse / install packages
+diskPartitionEditor Partition a new or existing disk
+diskPartitionWrite Write out any changed partition information
+diskLabelEditor Label/Newfs/Mount new or existing filesystems
+diskLabelCommit Write out any changed label information
+distReset Reset distribution information
+distSetDeveloper Select developer distribution
+distSetXDeveloper Select X developer distribution
+distSetKernDeveloper Select kernel developer distribution
+distSetUser Select user distribution
+distSetXUser Select X user distribution
+distSetMinimum Select minimal distribution
+distSetEverything Select all distributions
+distSetDES Select DES sub-distributions
+distSetSrc Select source sub-distributions
+distSetXF86 Select XFree86 sub-distributions
+distExtractAll Extract all selected distributions
+docBrowser Browse documentation
+installCommit Commit any pending installation operations
+installExpress Express installation
+installUpgrade Upgrade installation
+installFixup Go into "fixit" mode
+mediaSetCDROM Select CDROM media
+mediaSetFloppy Select floppy media
+mediaSetDOS Select DOS media
+mediaSetTape Select tape media
+mediaSetFTP Select FTP media
+mediaSetFTPPassive Select FTP media in passive mode
+mediaSetUFS Select UFS media
+mediaSetNFS Select NFS media
+mediaSetFtpUserPass Prompt for FTP username and password
+mediaSetCPIOVerbosity Prompt for CPIO verbosity
+mediaGetType Prompt for media type
+optionsEditor Go to options editor
+register Go to registration editor.
+
+Examples:
+
+/stand/sysinstall mediaSetFTP configPackages
+
+Selects an FTP site and then goes to the package configuration menu.
+
+
+/stand/sysinstall disk=da0 diskPartitionEditor
+
+Invokes the disk partition editor on disk da0.
+
+
+If /stand/sysinstall is linked to another filename, say
+`/usr/local/bin/configPackages', then the basename will be used
+as an implicit command name.
diff --git a/usr.sbin/sysinstall/help/slice.hlp b/usr.sbin/sysinstall/help/slice.hlp
new file mode 100644
index 0000000..b6f6a83
--- /dev/null
+++ b/usr.sbin/sysinstall/help/slice.hlp
@@ -0,0 +1,65 @@
+This is the Main Slice (``FDISK'' or PC-style Partition) Editor.
+
+Possible commands are printed at the bottom and the Master Boot Record
+contents are shown at the top. You can move up and down with the
+arrow keys and (C)reate a new slice whenever the highlighted
+selection bar is over a slice whose type is marked as "unused."
+
+You are expected to leave this screen with at least one slice
+marked "FreeBSD." Note that unlike Linux, you don't need to create
+multiple FreeBSD FDISK partition entries for different things like
+swap, file systems, etc. The usual convention is to create ONE
+FreeBSD slice (FDISK partition) per drive and then subsection this slice
+into swap and file systems with the Label editor.
+
+No actual changes will be made to the disk until you (C)ommit from the
+Install menu or use the (W)rite option here! You're working with what
+is essentially a copy of the disk label(s), both here and in the Label
+Editor.
+
+If you want to use the entire disk for FreeBSD, type `A'. You'll be
+asked whether or not you wish to keep the disk (potentially) compatible
+with other operating systems, i.e. the information in the FDISK table
+should be kept valid. If you select the default of `Yes', slices will be
+aligned to fictitious cylinder boundaries and space will be reserved
+in front of the FreeBSD slice for a [future] possible boot manager.
+
+For the truly dedicated disk case, you can select `No' at the
+compatibility prompt. In that case, all BIOS geometry considerations
+will no longer be in effect and you can safely ignore any
+``The detected geometry is invalid'' warning messages you may later
+see. It is also not necessary in this case to set a slice bootable
+or install an MBR boot manager as both things are then irrelevant.
+
+The FreeBSD slice will start at absolute sector 0 of the disk (so that
+FreeBSD's disk label is identical to the Master Boot Record) and
+extend to the very last sector of the disk medium. Needless to say,
+such a disk cannot have any sort of a boot manager, `disk manager',
+or anything else that has to interact with the BIOS. This option is
+therefore only considered safe for SCSI disks and most IDE disks and
+is primarily intended for people who are going to set up a dedicated
+FreeBSD server or workstation, not a typical `home PC'.
+
+The flags field has the following legend:
+
+ '=' -- This slice is properly aligned.
+ '>' -- This slice doesn't end before cylinder 1024
+ 'R' -- This slice contains the root (/) filesystem
+ 'B' -- Slice employs BAD144 bad-spot handling
+ 'C' -- This is the FreeBSD 2.0-compatibility slice (default)
+ 'A' -- This slice is marked active.
+
+If you select a slice for Bad144 handling, it will be scanned
+for bad blocks before any new filesystems are made on it.
+
+If no slice is marked Active, you will need to either install
+a Boot Manager (the option for which will be presented later in the
+installation) or set one Active before leaving this screen.
+
+To leave the slice editor, type `Q'.
+
+Final Note: If you're absolutely sure you know what you're doing
+ and you want to use the old "Dangerously Dedicated" mode
+ which is now deprecated by sysinstall, use the (purposely)
+ undocumented `F' key.
+
diff --git a/usr.sbin/sysinstall/help/tcp.hlp b/usr.sbin/sysinstall/help/tcp.hlp
new file mode 100644
index 0000000..d79cf27
--- /dev/null
+++ b/usr.sbin/sysinstall/help/tcp.hlp
@@ -0,0 +1,34 @@
+This screen allows you to set up your general network parameters
+(hostname, domain name, DNS server, etc) as well as the settings for a
+given interface (which was selected from the menu before this screen).
+
+PLIP/SLIP users - please read through to the end of this doc!
+
+You can move through the fields with the TAB, BACK-TAB and ENTER
+keys. To edit a field, use DELETE or BACKSPACE. You may also use ^A
+(control-A) to go to the beginning of the line, ^E (control-E) to go
+to the end, ^F (control-F) to go forward a character, ^B (control-B)
+to go backward one character, ^D (control-D) to delete the character
+under the cursor and ^K (control-K) to delete to the end of the line.
+Basically, the standard EMACS motion sequences.
+
+The "Extra options to ifconfig" field is kind of special (read: a
+hack :-):
+
+If you're running SLIP or PLIP, you also need to use it for specifying
+the remote end of the link (simply type the foreign IP address in).
+In the specific case where you're running PLIP with a Linux host peer
+rather than a FreeBSD one, you also must add the "-link0" flag after the
+foreign address.
+
+
+If you're dealing with an ethernet adaptor with multiple media
+connectors (e.g. AUI, 10BT, 10B2, etc), you can use this field to
+specify which one to use. The following strings are recognized:
+
+ "media 10base5/AUI" - Select the AUI port.
+ "media 10baseT/UTP" - Select the twisted pair port.
+ "media 10base2/BNC" - Select the BNC connector.
+ "media 100baseTX" - Select 100BaseT on a 100/10 dual adaptor.
+
+When you're done with this form, select OK.
diff --git a/usr.sbin/sysinstall/help/usage.hlp b/usr.sbin/sysinstall/help/usage.hlp
new file mode 100644
index 0000000..e67d548
--- /dev/null
+++ b/usr.sbin/sysinstall/help/usage.hlp
@@ -0,0 +1,65 @@
+HOW TO USE THIS SYSTEM
+======================
+
+[press the PageDown key to go to the next screen when you finish
+ reading this one]
+
+The following keys are recognized in most of the dialogs you'll
+encounter during this installation:
+
+KEY ACTION
+--- ------
+SPACE Select or toggle the current item.
+ENTER Finish with a menu or item.
+UP ARROW Move to previous item (or up, in a text display box).
+DOWN ARROW Move to next item (or down, in a text display box).
+TAB Move to next item or group.
+RIGHT ARROW Move to next item or group (same as TAB).
+SHIFT-TAB Move to previous item or group.
+LEFT ARROW Move to previous item or group (same as SHIFT-TAB).
+PAGE UP In text display boxes, scrolls up one page.
+PAGE DOWN In text display boxes, scrolls down one page.
+F1 Display associated help text.
+
+If you see small "^(-)" or "v(+)" symbols at the edges of a menu, it
+means that there are more items above or below the current one that
+aren't being shown (due to insufficient screen space). In text
+display boxes, the amount of text above the current point will be
+displayed as a percentage in the lower right corner. Using the
+Up/Down arrow keys will cause the object to scroll by line. The
+PageUp and PageDown keys will scroll by entire screens.
+
+Selecting OK in a menu will confirm whatever action it's controlling.
+Selecting Cancel will cancel the operation and generally return you to
+the previous menu. Use TAB to move the cursor around and select the
+buttons.
+
+Most screens obey the Help key (F1) - USE IT! It generally offers useful
+context-specific hints on what to do at each stage of the installation,
+and if you're at all unsure about what to do at a given stage in the
+installation, hit F1!
+
+
+SPECIAL FEATURES:
+=================
+
+It is possible to select a menu item by typing the first character of
+its name, if unique. This will generally be an item number.
+
+The console driver contains a scroll-back buffer for reviewing things
+that may have scrolled off the screen. To use scroll-back, press the
+"Scroll Lock" key on your keyboard and use the arrow or Page Up/Page
+Down keys to move through the saved text. To leave scroll-back mode,
+press the Scroll Lock key again. This feature is most useful for
+reading back through your boot messages (go ahead, try it now!) though
+it's also useful when dealing with sub-shells or other "wizard modes"
+that don't use menus and tend to scroll their output off the top of
+the screen.
+
+FreeBSD also supports multiple "virtual consoles" which you can use to
+in order to have several active sessions at once. Use ALT-F<n> to
+switch between screens, where `F<n>' is the function key corresponding
+to the screen you wish to see. By default, the system comes with 8
+virtual consoles enabled - you can enable more by editing the
+/etc/ttys file and turning the "off" field to "on" in the relevant vty
+entries (up to 12).
diff --git a/usr.sbin/sysinstall/help/usermgmt.hlp b/usr.sbin/sysinstall/help/usermgmt.hlp
new file mode 100644
index 0000000..d0819fe
--- /dev/null
+++ b/usr.sbin/sysinstall/help/usermgmt.hlp
@@ -0,0 +1,89 @@
+These screens allow you to add groups and users to your system.
+
+You can move through the fields with the TAB, BACK-TAB and ENTER
+keys. To edit a field, use DELETE or BACKSPACE. You may also use ^A
+(control-A) to go to the beginning of the line, ^E (control-E) to go
+to the end, ^F (control-F) to go forward a character, ^B (control-B)
+to go backward one character, ^D (control-D) to delete the character
+under the cursor and ^K (control-K) to delete to the end of the line.
+Basically, the standard EMACS motion sequences.
+
+When you're done with this form, select OK.
+
+Many of the settings get reasonable defaults if you leave them blank.
+The first time you have entered the name of the new group or user, the
+system will show you what it would chose for most of these fields.
+You are free to change them, of course.
+
+
+User groups
+===========
+
+It's certainly almost generally a good idea to first create a new
+group for your users. Common names for such a group are "users", or
+even simply "other". Group names are used to control file access
+permissions for users that belong to the same group. Several group
+names are already used for system files.
+
+The numerical user or group IDs are often nothing you want to care for
+explicitly. If you don't fill in these fields, the system will chose
+reasonable defaults. However, these numbers (rather than the
+associated names) are what the operating system actually uses to
+distinguish users and groups -- hence they should normally be unique
+to each person or group, respectively.
+
+(The initial membership list for a new group is currently
+unimplemented, sorry.)
+
+
+Users
+=====
+
+The user's login ID is a short (up to 15 characters) alphanumeric ID
+the user must enter when logging into the system. It's often the
+initial letters of the user's name, and commonly used in lower case.
+It's also the local mail name for this user (though it's possible to
+also setup more descriptive mail alias names later).
+
+The user's login group determines which group access rights the user
+will initially get when logging in. If an additional list of groups is
+provided where the user will become a member of, (s)he will also be
+able to access files of those groups later without providing any
+additional password etc. Except for the "wheel" case mentioned below,
+the additional group membership list should normally not contain the
+login group again.
+
+The user's password can also be set here, and should be chosen with
+care - 6 or more characters, intermixing punctuation and numerics, and
+*not* a word from the dictionary or related to the username is a good
+password choice.
+
+Some of the system's groups have a special meaning. In particular,
+members of group "wheel" are the only people who are later allowed to
+become superuser using the command su(1). So if you're going to add a
+new user who should later perform administrative tasks, don't forget
+to add him to this group! (Well, ``he'' will most likely be yourself
+in the very first place. :)
+
+Also, members of group "operator" will by default get permissions for
+minor administrative operations, like performing system backups, or
+shutting down the system -- without first becoming superuser! So,
+take care with adding people to this group.
+
+The ``full name'' field serves as a comment only. It is also used by
+mail front ends to determine the real name of the user, hence you
+should actually fill in the first and last name of this user. By
+convention, this field can be divided into comma-separated subfields,
+where the office location, the work phone number, and the home phone
+number follow the full name of the user.
+
+The home directory is the directory in the filesystem where the user
+is being logged into, and where his personalized setup files (``dot
+files'', since they usually begin with a `.' and are not displayed by
+the ls(1) command by default) will be looked up. It is often created
+under /usr/home/ or /home/.
+
+Finally, the shell is the user's initial command interpreter. The
+default shell is /bin/sh, some users prefer the more historic
+/bin/csh. Other, often more user-friendly and comfortable shells can
+be found in the ports and packages collection.
diff --git a/usr.sbin/sysinstall/http.c b/usr.sbin/sysinstall/http.c
new file mode 100644
index 0000000..c8742d0
--- /dev/null
+++ b/usr.sbin/sysinstall/http.c
@@ -0,0 +1,279 @@
+/*
+ * Copyright (c) 1999
+ * Philipp Mergenthaler <philipp.mergenthaler@stud.uni-karlsruhe.de>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "sysinstall.h"
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/param.h>
+#include <netdb.h>
+
+extern const char *ftp_dirs[]; /* defined in ftp.c */
+
+Boolean
+checkAccess(Boolean proxyCheckOnly)
+{
+/*
+ * Some proxies fetch files with certain extensions in "ascii mode" instead
+ * of "binary mode" for FTP. The FTP server then translates all LF to CRLF.
+ *
+ * You can force Squid to use binary mode by appending ";type=i" to the URL,
+ * which is what I do here. For other proxies, the LF->CRLF substitution
+ * is reverted in distExtract().
+ */
+
+ int rv, s, af;
+ bool el, found=FALSE; /* end of header line */
+ char *cp, buf[PATH_MAX], req[BUFSIZ];
+ struct addrinfo hints, *res, *res0;
+
+ af = variable_cmp(VAR_IPV6_ENABLE, "YES") ? AF_INET : AF_UNSPEC;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = af;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = 0;
+ if ((rv = getaddrinfo(variable_get(VAR_HTTP_HOST),
+ variable_get(VAR_HTTP_PORT), &hints, &res0)) != 0) {
+ msgConfirm("%s", gai_strerror(rv));
+ variable_unset(VAR_HTTP_HOST);
+ return FALSE;
+ }
+ s = -1;
+ for (res = res0; res; res = res->ai_next) {
+ if ((s = socket(res->ai_family, res->ai_socktype,
+ res->ai_protocol)) < 0)
+ continue;
+ if (connect(s, res->ai_addr, res->ai_addrlen) >= 0)
+ break;
+ close(s);
+ s = -1;
+ }
+ freeaddrinfo(res0);
+ if (s == -1) {
+ msgConfirm("Couldn't connect to proxy %s:%s",
+ variable_get(VAR_HTTP_HOST),variable_get(VAR_HTTP_PORT));
+ variable_unset(VAR_HTTP_HOST);
+ return FALSE;
+ }
+ if (proxyCheckOnly) {
+ close(s);
+ return TRUE;
+ }
+
+ msgNotify("Checking access to\n %s", variable_get(VAR_HTTP_PATH));
+ sprintf(req,"GET %s/ HTTP/1.0\r\n\r\n", variable_get(VAR_HTTP_PATH));
+ write(s,req,strlen(req));
+/*
+ * scan the headers of the response
+ * this is extremely quick'n dirty
+ *
+ */
+ bzero(buf, PATH_MAX);
+ cp=buf;
+ el=FALSE;
+ rv=read(s,cp,1);
+ variable_set2(VAR_HTTP_FTP_MODE,"",0);
+ while (rv>0) {
+ if ((*cp == '\012') && el) {
+ /* reached end of a header line */
+ if (!strncmp(buf,"HTTP",4)) {
+ if (strtol((char *)(buf+9),0,0) == 200) {
+ found = TRUE;
+ }
+ }
+
+ if (!strncmp(buf,"Server: ",8)) {
+ if (!strncmp(buf,"Server: Squid",13)) {
+ variable_set2(VAR_HTTP_FTP_MODE,";type=i",0);
+ } else {
+ variable_set2(VAR_HTTP_FTP_MODE,"",0);
+ }
+ }
+ /* ignore other headers */
+ /* check for "\015\012" at beginning of line, i.e. end of headers */
+ if ((cp-buf) == 1)
+ break;
+ cp=buf;
+ rv=read(s,cp,1);
+ } else {
+ el=FALSE;
+ if (*cp == '\015')
+ el=TRUE;
+ cp++;
+ rv=read(s,cp,1);
+ }
+ }
+ close(s);
+ return found;
+}
+
+Boolean
+mediaInitHTTP(Device *dev)
+{
+ bool found=FALSE; /* end of header line */
+ char *rel, req[BUFSIZ];
+ int fdir;
+
+ /*
+ * First verify the proxy access
+ */
+ checkAccess(TRUE);
+ while (variable_get(VAR_HTTP_HOST) == NULL) {
+ if (DITEM_STATUS(mediaSetHTTP(NULL)) == DITEM_FAILURE)
+ return FALSE;
+ checkAccess(TRUE);
+ }
+again:
+ /* If the release is specified as "__RELEASE" or "any", then just
+ * assume that the path the user gave is ok.
+ */
+ rel = variable_get(VAR_RELNAME);
+ /*
+ msgConfirm("rel: -%s-", rel);
+ */
+
+ if (strcmp(rel, "__RELEASE") && strcmp(rel, "any")) {
+ for (fdir = 0; ftp_dirs[fdir]; fdir++) {
+ sprintf(req, "%s/%s/%s", variable_get(VAR_FTP_PATH),
+ ftp_dirs[fdir], rel);
+ variable_set2(VAR_HTTP_PATH, req, 0);
+ if (checkAccess(FALSE)) {
+ found = TRUE;
+ break;
+ }
+ }
+ } else {
+ variable_set2(VAR_HTTP_PATH, variable_get(VAR_FTP_PATH), 0);
+ found = checkAccess(FALSE);
+ }
+ if (!found) {
+ msgConfirm("No such directory: %s\n"
+ "please check the URL and try again.", variable_get(VAR_HTTP_PATH));
+ variable_unset(VAR_HTTP_PATH);
+ dialog_clear_norefresh();
+ clear();
+ if (DITEM_STATUS(mediaSetHTTP(NULL)) != DITEM_FAILURE) goto again;
+ }
+ return found;
+}
+
+FILE *
+mediaGetHTTP(Device *dev, char *file, Boolean probe)
+{
+ FILE *fp;
+ int rv, s, af;
+ bool el; /* end of header line */
+ char *cp, buf[PATH_MAX], req[BUFSIZ];
+ struct addrinfo hints, *res, *res0;
+
+ af = variable_cmp(VAR_IPV6_ENABLE, "YES") ? AF_INET : AF_UNSPEC;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = af;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = 0;
+ if ((rv = getaddrinfo(variable_get(VAR_HTTP_HOST),
+ variable_get(VAR_HTTP_PORT), &hints, &res0)) != 0) {
+ msgConfirm("%s", gai_strerror(rv));
+ return NULL;
+ }
+ s = -1;
+ for (res = res0; res; res = res->ai_next) {
+ if ((s = socket(res->ai_family, res->ai_socktype,
+ res->ai_protocol)) < 0)
+ continue;
+ if (connect(s, res->ai_addr, res->ai_addrlen) >= 0)
+ break;
+ close(s);
+ s = -1;
+ }
+ freeaddrinfo(res0);
+ if (s == -1) {
+ msgConfirm("Couldn't connect to proxy %s:%s",
+ variable_get(VAR_HTTP_HOST),variable_get(VAR_HTTP_PORT));
+ return NULL;
+ }
+
+ sprintf(req,"GET %s/%s%s HTTP/1.0\r\n\r\n",
+ variable_get(VAR_HTTP_PATH), file, variable_get(VAR_HTTP_FTP_MODE));
+
+ if (isDebug()) {
+ msgDebug("sending http request: %s",req);
+ }
+ write(s,req,strlen(req));
+
+/*
+ * scan the headers of the response
+ * this is extremely quick'n dirty
+ *
+ */
+ cp=buf;
+ el=FALSE;
+ rv=read(s,cp,1);
+ while (rv>0) {
+ if ((*cp == '\012') && el) {
+ /* reached end of a header line */
+ if (!strncmp(buf,"HTTP",4)) {
+ rv=strtol((char *)(buf+9),0,0);
+ *(cp-1)='\0'; /* chop the CRLF off */
+ if (probe && (rv != 200)) {
+ return NULL;
+ } else if (rv >= 500) {
+ msgConfirm("Server error %s when sending %s, you could try an other server",buf, req);
+ return NULL;
+ } else if (rv == 404) {
+ msgConfirm("%s was not found, maybe directory or release-version are wrong?",req);
+ return NULL;
+ } else if (rv >= 400) {
+ msgConfirm("Client error %s, you could try an other server",buf);
+ return NULL;
+ } else if (rv >= 300) {
+ msgConfirm("Error %s,",buf);
+ return NULL;
+ } else if (rv != 200) {
+ msgConfirm("Error %s when sending %s, you could try an other server",buf, req);
+ return NULL;
+ }
+ }
+ /* ignore other headers */
+ /* check for "\015\012" at beginning of line, i.e. end of headers */
+ if ((cp-buf) == 1)
+ break;
+ cp=buf;
+ rv=read(s,cp,1);
+ } else {
+ el=FALSE;
+ if (*cp == '\015')
+ el=TRUE;
+ cp++;
+ rv=read(s,cp,1);
+ }
+ }
+ fp=fdopen(s,"r");
+ return fp;
+}
diff --git a/usr.sbin/sysinstall/index.c b/usr.sbin/sysinstall/index.c
new file mode 100644
index 0000000..19c3781
--- /dev/null
+++ b/usr.sbin/sysinstall/index.c
@@ -0,0 +1,817 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ncurses.h>
+#include <dialog.h>
+#include "sysinstall.h"
+
+/* Macros and magic values */
+#define MAX_MENU 12
+#define _MAX_DESC 55
+
+/* A structure holding the root, top and plist pointer at once */
+struct ListPtrs
+{
+ PkgNodePtr root; /* root of tree */
+ PkgNodePtr top; /* part of tree we handle */
+ PkgNodePtr plist; /* list of selected packages */
+};
+typedef struct ListPtrs* ListPtrsPtr;
+
+static void index_recorddeps(Boolean add, PkgNodePtr root, IndexEntryPtr ie);
+
+/* Shared between index_initialize() and the various clients of it */
+PkgNode Top, Plist;
+
+/* Smarter strdup */
+inline char *
+_strdup(char *ptr)
+{
+ return ptr ? strdup(ptr) : NULL;
+}
+
+static char *descrs[] = {
+ "Package Selection", "To mark a package, move to it and press SPACE. If the package is\n"
+ "already marked, it will be unmarked or deleted (if installed).\n"
+ "Items marked with a `D' are dependencies which will be auto-loaded.\n"
+ "To search for a package by name, press ESC. To select a category,\n"
+ "press RETURN. NOTE: The All category selection creates a very large\n"
+ "submenu! If you select it, please be patient while it comes up.",
+ "Package Targets", "These are the packages you've selected for extraction.\n\n"
+ "If you're sure of these choices, select OK.\n"
+ "If not, select Cancel to go back to the package selection menu.\n",
+ "All", "All available packages in all categories.",
+ "afterstep", "Ports to support the AfterStep window manager.",
+ "applications", "User application software.",
+ "archivers", "Utilities for archiving and unarchiving data.",
+ "astro", "Applications related to astronomy.",
+ "audio", "Audio utilities - most require a supported sound card.",
+ "benchmarks", "Utilities for measuring system performance.",
+ "biology", "Software related to biology.",
+ "cad", "Computer Aided Design utilities.",
+ "chinese", "Ported software for the Chinese market.",
+ "comms", "Communications utilities.",
+ "converters", "Format conversion utilities.",
+ "databases", "Database software.",
+ "deskutils", "Various Desktop utilities.",
+ "devel", "Software development utilities and libraries.",
+ "documentation", "Document preparation utilities.",
+ "editors", "Common text editors.",
+ "elisp", "Things related to Emacs Lisp.",
+ "emulators", "Utilities for emulating other OS types.",
+ "french", "Ported software for French countries.",
+ "ftp", "FTP client and server utilities.",
+ "games", "Various and sundry amusements.",
+ "german", "Ported software for Germanic countries.",
+ "gnome", "Components of the Gnome Desktop environment.",
+ "graphics", "Graphics libraries and utilities.",
+ "ipv6", "IPv6 related software.",
+ "hebrew", "Ported software for Hebrew language.",
+ "irc", "Internet Relay Chat utilities.",
+ "japanese", "Ported software for the Japanese market.",
+ "java", "Java language support.",
+ "kde", "Software for the K Desktop Environment.",
+ "korean", "Ported software for the Korean market.",
+ "lang", "Computer languages.",
+ "languages", "Computer languages.",
+ "libraries", "Software development libraries.",
+ "linux", "Linux programs that can be run under binary compatibility.",
+ "mail", "Electronic mail packages and utilities.",
+ "math", "Mathematical computation software.",
+ "mbone", "Applications and utilities for the MBONE.",
+ "misc", "Miscellaneous utilities.",
+ "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.",
+ "perl5", "Utilities/modules for the PERL5 language.",
+ "picobsd", "Ports to support PicoBSD.",
+ "pilot", "Software support for the 3Com Palm Pilot(tm) series.",
+ "plan9", "Software from the Plan9 operating system.",
+ "print", "Utilities for dealing with printing.",
+ "printing", "Utilities for dealing with printing.",
+ "programming", "Software development utilities and libraries.",
+ "python", "Software related to the Python language.",
+ "ruby", "Software related to the Ruby language.",
+ "russian", "Ported software for the Russian market.",
+ "science", "Scientific software.",
+ "security", "System security software.",
+ "shells", "Various shells (tcsh, bash, etc).",
+ "sysutils", "Various system utilities.",
+ "tcl75", "TCL v7.5 and packages that depend on it.",
+ "tcl76", "TCL v7.6 and packages that depend on it.",
+ "tcl80", "TCL v8.0 and packages that depend on it.",
+ "tcl82", "TCL v8.2 and packages that depend on it.",
+ "tcl83", "TCL v8.3 and packages that depend on it.",
+ "textproc", "Text processing/search utilities.",
+ "tk41", "Tk4.1 and packages that depend on it.",
+ "tk42", "Tk4.2 and packages that depend on it.",
+ "tk80", "Tk8.0 and packages that depend on it.",
+ "tk81", "Tk8.1 and packages that depend on it.",
+ "tk82", "Tk8.2 and packages that depend on it.",
+ "tk83", "Tk8.3 and packages that depend on it.",
+ "tkstep80", "tkstep wm and packages that depend on it.",
+ "troff", "TROFF text formatting utilities.",
+ "ukrainian", "Ported software for the Ukrainian market.",
+ "vietnamese", "Ported software for the Vietnamese market.",
+ "windowmaker", "Ports to support the WindowMaker window manager.",
+ "www", "WEB utilities (browers, HTTP servers, etc).",
+ "x11", "X Window System based utilities.",
+ "x11-clocks", "X Window System based clocks.",
+ "x11-fm", "X Window System based file managers.",
+ "x11-fonts", "X Window System fonts and font utilities.",
+ "x11-servers", "X Window System servers.",
+ "x11-toolkits", "X Window System based development toolkits.",
+ "x11-wm", "X Window System window managers.",
+ "zope", "Software related to the Zope platform.",
+ NULL, NULL,
+};
+
+static char *
+fetch_desc(char *name)
+{
+ int i;
+
+ for (i = 0; descrs[i]; i += 2) {
+ if (!strcmp(descrs[i], name))
+ return descrs[i + 1];
+ }
+ return "No description provided";
+}
+
+static PkgNodePtr
+new_pkg_node(char *name, node_type type)
+{
+ PkgNodePtr tmp = safe_malloc(sizeof(PkgNode));
+
+ tmp->name = _strdup(name);
+ tmp->type = type;
+ return tmp;
+}
+
+static char *
+strip(char *buf)
+{
+ int i;
+
+ for (i = 0; buf[i]; i++)
+ if (buf[i] == '\t' || buf[i] == '\n')
+ buf[i] = ' ';
+ return buf;
+}
+
+static IndexEntryPtr
+new_index(char *name, char *pathto, char *prefix, char *comment, char *descr, char *maint, char *deps, int volume)
+{
+ IndexEntryPtr tmp = safe_malloc(sizeof(IndexEntry));
+
+ tmp->name = _strdup(name);
+ tmp->path = _strdup(pathto);
+ tmp->prefix = _strdup(prefix);
+ tmp->comment = _strdup(comment);
+ tmp->descrfile = strip(_strdup(descr));
+ tmp->maintainer = _strdup(maint);
+ tmp->deps = _strdup(deps);
+ tmp->depc = 0;
+ tmp->installed = package_exists(name);
+ tmp->volume = volume;
+ return tmp;
+}
+
+static void
+index_register(PkgNodePtr top, char *where, IndexEntryPtr ptr)
+{
+ PkgNodePtr p, q;
+
+ for (q = NULL, p = top->kids; p; p = p->next) {
+ if (!strcmp(p->name, where)) {
+ q = p;
+ break;
+ }
+ }
+ if (!p) {
+ /* Add new category */
+ q = new_pkg_node(where, PLACE);
+ q->desc = fetch_desc(where);
+ q->next = top->kids;
+ top->kids = q;
+ }
+ p = new_pkg_node(ptr->name, PACKAGE);
+ p->desc = ptr->comment;
+ p->data = ptr;
+ p->next = q->kids;
+ q->kids = p;
+}
+
+static int
+copy_to_sep(char *to, char *from, int sep)
+{
+ char *tok;
+
+ tok = strchr(from, sep);
+ if (!tok) {
+ *to = '\0';
+ return 0;
+ }
+ *tok = '\0';
+ strcpy(to, from);
+ return tok + 1 - from;
+}
+
+static int
+readline(FILE *fp, char *buf, int max)
+{
+ int rv, i = 0;
+ char ch;
+
+ while ((rv = fread(&ch, 1, 1, fp)) == 1 && ch != '\n' && i < max)
+ buf[i++] = ch;
+ if (i < max)
+ buf[i] = '\0';
+ return rv;
+}
+
+/*
+ * XXX - this function should do error checking, and skip corrupted INDEX
+ * lines without a set number of '|' delimited fields.
+ */
+
+int
+index_parse(FILE *fp, char *name, char *pathto, char *prefix, char *comment, char *descr, char *maint, char *cats, char *rdeps, int *volume)
+{
+ char line[10240];
+ char junk[2048];
+ char volstr[2048];
+ char *cp;
+ int i;
+
+ i = readline(fp, line, sizeof line);
+ if (i <= 0)
+ return EOF;
+ cp = line;
+ cp += copy_to_sep(name, cp, '|');
+ cp += copy_to_sep(pathto, cp, '|');
+ cp += copy_to_sep(prefix, cp, '|');
+ cp += copy_to_sep(comment, cp, '|');
+ cp += copy_to_sep(descr, cp, '|');
+ cp += copy_to_sep(maint, cp, '|');
+ cp += copy_to_sep(cats, cp, '|');
+ cp += copy_to_sep(junk, cp, '|'); /* build deps - not used */
+ cp += copy_to_sep(rdeps, cp, '|');
+ if (index(cp, '|'))
+ cp += copy_to_sep(junk, cp, '|'); /* url - not used */
+ else {
+ strncpy(junk, cp, 1023);
+ *volume = 0;
+ return 0;
+ }
+ if (index(cp, '|'))
+ cp += copy_to_sep(volstr, cp, '|'); /* media volume */
+ else {
+ strncpy(volstr, cp, 1023);
+ }
+ *volume = atoi(volstr);
+ return 0;
+}
+
+int
+index_read(FILE *fp, PkgNodePtr papa)
+{
+ char name[127], pathto[255], prefix[255], comment[255], descr[127], maint[127], cats[511], deps[2048];
+ int volume;
+ PkgNodePtr i;
+
+ while (index_parse(fp, name, pathto, prefix, comment, descr, maint, cats, deps, &volume) != EOF) {
+ char *cp, *cp2, tmp[1024];
+ IndexEntryPtr idx;
+
+ idx = new_index(name, pathto, prefix, comment, descr, maint, deps, volume);
+ /* For now, we only add things to menus if they're in categories. Keywords are ignored */
+ for (cp = strcpy(tmp, cats); (cp2 = strchr(cp, ' ')) != NULL; cp = cp2 + 1) {
+ *cp2 = '\0';
+ index_register(papa, cp, idx);
+ }
+ index_register(papa, cp, idx);
+
+ /* Add to special "All" category */
+ index_register(papa, "All", idx);
+ }
+
+ /* Adjust dependency counts */
+ for (i = papa->kids; i != NULL; i = i->next)
+ if (strcmp(i->name, "All") == 0)
+ break;
+ for (i = i->kids; i != NULL; i = i->next)
+ if (((IndexEntryPtr)i->data)->installed)
+ index_recorddeps(TRUE, papa, i->data);
+
+ return 0;
+}
+
+void
+index_init(PkgNodePtr top, PkgNodePtr plist)
+{
+ if (top) {
+ top->next = top->kids = NULL;
+ top->name = "Package Selection";
+ top->type = PLACE;
+ top->desc = fetch_desc(top->name);
+ top->data = NULL;
+ }
+ if (plist) {
+ plist->next = plist->kids = NULL;
+ plist->name = "Package Targets";
+ plist->type = PLACE;
+ plist->desc = fetch_desc(plist->name);
+ plist->data = NULL;
+ }
+}
+
+void
+index_print(PkgNodePtr top, int level)
+{
+ int i;
+
+ while (top) {
+ for (i = 0; i < level; i++) putchar('\t');
+ printf("name [%s]: %s\n", top->type == PLACE ? "place" : "package", top->name);
+ for (i = 0; i < level; i++) putchar('\t');
+ printf("desc: %s\n", top->desc);
+ if (top->kids)
+ index_print(top->kids, level + 1);
+ top = top->next;
+ }
+}
+
+/* Swap one node for another */
+static void
+swap_nodes(PkgNodePtr a, PkgNodePtr b)
+{
+ PkgNode tmp;
+
+ tmp = *a;
+ *a = *b;
+ a->next = tmp.next;
+ tmp.next = b->next;
+ *b = tmp;
+}
+
+/* Use a disgustingly simplistic bubble sort to put our lists in order */
+void
+index_sort(PkgNodePtr top)
+{
+ PkgNodePtr p, q;
+
+ /* Sort everything at the top level */
+ for (p = top->kids; p; p = p->next) {
+ for (q = top->kids; q; q = q->next) {
+ if (q->next && strcmp(q->name, q->next->name) > 0)
+ swap_nodes(q, q->next);
+ }
+ }
+
+ /* Now sub-sort everything n levels down */
+ for (p = top->kids; p; p = p->next) {
+ if (p->kids)
+ index_sort(p);
+ }
+}
+
+/* Delete an entry out of the list it's in (only the plist, at present) */
+void
+index_delete(PkgNodePtr n)
+{
+ if (n->next) {
+ PkgNodePtr p = n->next;
+
+ *n = *(n->next);
+ safe_free(p);
+ }
+ else /* Kludgy end sentinal */
+ n->name = NULL;
+}
+
+/*
+ * Search for a given node by name, returning the category in if
+ * tp is non-NULL.
+ */
+PkgNodePtr
+index_search(PkgNodePtr top, char *str, PkgNodePtr *tp)
+{
+ PkgNodePtr p, sp;
+
+ for (p = top->kids; p && p->name; p = p->next) {
+ if (p->type == PACKAGE) {
+ /* If tp == NULL, we're looking for an exact package match */
+ if (!tp && !strcmp(p->name, str))
+ return p;
+
+ /* If tp, we're looking for both a package and a pointer to the place it's in */
+ if (tp && !strncmp(p->name, str, strlen(str))) {
+ *tp = top;
+ return p;
+ }
+ }
+ else if (p->kids) {
+ /* The usual recursion-out-of-laziness ploy */
+ if ((sp = index_search(p, str, tp)) != NULL)
+ return sp;
+ }
+ }
+ if (p && !p->name)
+ p = NULL;
+ return p;
+}
+
+int
+pkg_checked(dialogMenuItem *self)
+{
+ ListPtrsPtr lists = (ListPtrsPtr)self->aux;
+ PkgNodePtr kp = self->data, plist = lists->plist;
+ int i;
+
+ i = index_search(plist, kp->name, NULL) ? TRUE : FALSE;
+ if (kp->type == PACKAGE && plist) {
+ IndexEntryPtr ie = kp->data;
+ int markD, markX;
+
+ markD = ie->depc > 0; /* needed as dependency */
+ markX = i || ie->installed; /* selected or installed */
+ self->mark = markX ? 'X' : 'D';
+ return markD || markX;
+ } else
+ return FALSE;
+}
+
+int
+pkg_fire(dialogMenuItem *self)
+{
+ int ret;
+ ListPtrsPtr lists = (ListPtrsPtr)self->aux;
+ PkgNodePtr sp, kp = self->data, plist = lists->plist;
+
+ if (!plist)
+ ret = DITEM_FAILURE;
+ else if (kp->type == PACKAGE) {
+ IndexEntryPtr ie = kp->data;
+
+ sp = index_search(plist, kp->name, NULL);
+ /* Not already selected? */
+ if (!sp) {
+ if (!ie->installed) {
+ PkgNodePtr np = (PkgNodePtr)safe_malloc(sizeof(PkgNode));
+
+ *np = *kp;
+ np->next = plist->kids;
+ plist->kids = np;
+ index_recorddeps(TRUE, lists->root, ie);
+ msgInfo("Added %s to selection list", kp->name);
+ }
+ else if (ie->depc == 0) {
+ if (!msgNoYes("Do you really want to delete %s from the system?", kp->name)) {
+ if (vsystem("pkg_delete %s %s", isDebug() ? "-v" : "", kp->name)) {
+ msgConfirm("Warning: pkg_delete of %s failed.\n Check debug output for details.", kp->name);
+ }
+ else {
+ ie->installed = 0;
+ index_recorddeps(FALSE, lists->root, ie);
+ }
+ }
+ }
+ else
+ msgConfirm("Warning: Package %s is needed by\n %d other installed package%s.",
+ kp->name, ie->depc, (ie->depc != 1) ? "s" : "");
+ }
+ else {
+ index_recorddeps(FALSE, lists->root, ie);
+ msgInfo("Removed %s from selection list", kp->name);
+ index_delete(sp);
+ }
+ ret = DITEM_SUCCESS;
+ /* Mark menu for redraw if we had dependencies */
+ if (strlen(ie->deps) > 0)
+ ret |= DITEM_REDRAW;
+ }
+ else { /* Not a package, must be a directory */
+ int p, s;
+
+ p = s = 0;
+ index_menu(lists->root, kp, plist, &p, &s);
+ ret = DITEM_SUCCESS | DITEM_CONTINUE;
+ }
+ return ret;
+}
+
+void
+pkg_selected(dialogMenuItem *self, int is_selected)
+{
+ PkgNodePtr kp = self->data;
+
+ if (!is_selected || kp->type != PACKAGE)
+ return;
+ msgInfo("%s", kp->desc);
+}
+
+int
+index_menu(PkgNodePtr root, PkgNodePtr top, PkgNodePtr plist, int *pos, int *scroll)
+{
+ struct ListPtrs lists;
+ int n, rval, maxname;
+ int curr, max;
+ PkgNodePtr kp;
+ dialogMenuItem *nitems;
+ Boolean hasPackages;
+ WINDOW *w;
+
+ lists.root = root;
+ lists.top = top;
+ lists.plist = plist;
+
+ hasPackages = FALSE;
+ nitems = NULL;
+ n = maxname = 0;
+
+ /* Figure out if this menu is full of "leaves" or "branches" */
+ for (kp = top->kids; kp && kp->name; kp = kp->next) {
+ int len;
+
+ ++n;
+ if (kp->type == PACKAGE && plist) {
+ hasPackages = TRUE;
+ if ((len = strlen(kp->name)) > maxname)
+ maxname = len;
+ }
+ }
+ if (!n && plist) {
+ msgConfirm("The %s menu is empty.", top->name);
+ return DITEM_LEAVE_MENU;
+ }
+
+ w = savescr();
+ while (1) {
+ n = 0;
+ curr = max = 0;
+ use_helpline(NULL);
+ use_helpfile(NULL);
+ kp = top->kids;
+ if (!hasPackages && plist) {
+ nitems = item_add(nitems, "OK", NULL, NULL, NULL, NULL, NULL, NULL, &curr, &max);
+ nitems = item_add(nitems, "Install", NULL, NULL, NULL, NULL, NULL, NULL, &curr, &max);
+ }
+ while (kp && kp->name) {
+ char buf[256];
+ IndexEntryPtr ie = kp->data;
+
+ /* Brutally adjust description to fit in menu */
+ if (kp->type == PACKAGE)
+ snprintf(buf, sizeof buf, "[%s]", ie->path ? ie->path : "External vendor");
+ else
+ SAFE_STRCPY(buf, kp->desc);
+ if (strlen(buf) > (_MAX_DESC - maxname))
+ buf[_MAX_DESC - maxname] = '\0';
+ nitems = item_add(nitems, kp->name, (char *)buf, pkg_checked,
+ pkg_fire, pkg_selected, kp, (int *)(&lists),
+ &curr, &max);
+ ++n;
+ kp = kp->next;
+ }
+ /* NULL delimiter so item_free() knows when to stop later */
+ nitems = item_add(nitems, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ &curr, &max);
+
+recycle:
+ dialog_clear_norefresh();
+ if (hasPackages)
+ rval = dialog_checklist(top->name, top->desc, -1, -1, n > MAX_MENU ? MAX_MENU : n, -n, nitems, NULL);
+ else
+ rval = dialog_menu(top->name, top->desc, -1, -1, n > MAX_MENU ? MAX_MENU : n, -n, nitems + (plist ? 2 : 0), (char *)plist, pos, scroll);
+ if (rval == -1 && plist) {
+ static char *cp;
+ PkgNodePtr menu;
+
+ /* Search */
+ if ((cp = msgGetInput(cp, "Search by package name. Please enter search string:")) != NULL) {
+ PkgNodePtr p = index_search(top, cp, &menu);
+
+ if (p) {
+ int pos, scroll;
+
+ /* These need to be set to point at the found item, actually. Hmmm! */
+ pos = scroll = 0;
+ index_menu(root, menu, plist, &pos, &scroll);
+ }
+ else
+ msgConfirm("Search string: %s yielded no hits.", cp);
+ }
+ goto recycle;
+ }
+ items_free(nitems, &curr, &max);
+ restorescr(w);
+ return rval ? DITEM_FAILURE : DITEM_SUCCESS;
+ }
+}
+
+int
+index_extract(Device *dev, PkgNodePtr top, PkgNodePtr who, Boolean depended)
+{
+ int status = DITEM_SUCCESS;
+ PkgNodePtr tmp2;
+ IndexEntryPtr id = who->data;
+ WINDOW *w = savescr();
+
+ /*
+ * Short-circuit the package dependency checks. We're already
+ * maintaining a data structure of installed packages, so if a
+ * package is already installed, don't try to check to make sure
+ * that all of its dependencies are installed. At best this
+ * wastes a ton of cycles and can cause minor delays between
+ * package extraction. At worst it can cause an infinite loop with
+ * a certain faulty INDEX file.
+ */
+
+ if (id->installed == 1)
+ return DITEM_SUCCESS;
+
+ /*
+ * What if the package is not available on the current media volume?
+ *
+ */
+ if (id->volume != dev->volume) {
+ if (!msgYesNo("This is disc #%d. Package %s is on disc #%d\n"
+ "Would you like to switch discs now?\n", dev->volume,
+ id->name, id->volume)) {
+ DEVICE_SHUTDOWN(mediaDevice);
+ msgConfirm("Please remove disc #%d from your drive, and add disc #%d\n",
+ dev->volume, id->volume);
+ DEVICE_INIT(mediaDevice);
+ /* XXX, at this point we check to see if this is the
+ * correct disc, and if not, we loop */
+ } else {
+ return DITEM_FAILURE;
+ }
+ }
+
+ if (id && id->deps && strlen(id->deps)) {
+ char t[2048], *cp, *cp2;
+
+ SAFE_STRCPY(t, id->deps);
+ cp = t;
+ while (cp && DITEM_STATUS(status) == DITEM_SUCCESS) {
+ if ((cp2 = index(cp, ' ')) != NULL)
+ *cp2 = '\0';
+ if ((tmp2 = index_search(top, cp, NULL)) != NULL) {
+ status = index_extract(dev, top, tmp2, TRUE);
+ if (DITEM_STATUS(status) != DITEM_SUCCESS) {
+ if (variable_get(VAR_NO_CONFIRM))
+ msgNotify("Loading of dependent package %s failed", cp);
+ else
+ msgConfirm("Loading of dependent package %s failed", cp);
+ }
+ }
+ else if (!package_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;
+ }
+ restorescr(w);
+ 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;
+ }
+}
+
+static Boolean index_initted;
+
+/* Read and initialize global index */
+int
+index_initialize(char *path)
+{
+ FILE *fp;
+ WINDOW *w = NULL;
+
+ if (!index_initted) {
+ w = savescr();
+ dialog_clear_norefresh();
+
+ /* Got any media? */
+ if (!mediaVerify()) {
+ restorescr(w);
+ return DITEM_FAILURE;
+ }
+
+ /* Does it move when you kick it? */
+ if (!DEVICE_INIT(mediaDevice)) {
+ restorescr(w);
+ return DITEM_FAILURE;
+ }
+
+ dialog_clear_norefresh();
+ msgNotify("Attempting to fetch %s file from selected media.", path);
+ fp = DEVICE_GET(mediaDevice, path, TRUE);
+ if (!fp) {
+ msgConfirm("Unable to get packages/INDEX file from selected media.\n\n"
+ "This may be because the packages collection is not available\n"
+ "on the distribution media you've chosen, most likely an FTP site\n"
+ "without the packages collection mirrored. Please verify that\n"
+ "your media, or your path to the media, is correct and try again.");
+ DEVICE_SHUTDOWN(mediaDevice);
+ restorescr(w);
+ return DITEM_FAILURE;
+ }
+ dialog_clear_norefresh();
+ msgNotify("Located INDEX, now reading package data from it...");
+ index_init(&Top, &Plist);
+ if (index_read(fp, &Top)) {
+ msgConfirm("I/O or format error on packages/INDEX file.\n"
+ "Please verify media (or path to media) and try again.");
+ fclose(fp);
+ restorescr(w);
+ return DITEM_FAILURE;
+ }
+ fclose(fp);
+ index_sort(&Top);
+ index_initted = TRUE;
+ restorescr(w);
+ }
+ return DITEM_SUCCESS;
+}
diff --git a/usr.sbin/sysinstall/install.c b/usr.sbin/sysinstall/install.c
new file mode 100644
index 0000000..d54d7f6
--- /dev/null
+++ b/usr.sbin/sysinstall/install.c
@@ -0,0 +1,1202 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <ctype.h>
+#include <sys/disklabel.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/fcntl.h>
+#include <sys/wait.h>
+#include <sys/param.h>
+#define MSDOSFS
+#include <sys/mount.h>
+#include <ufs/ufs/ufsmount.h>
+#include <fs/msdosfs/msdosfsmount.h>
+#undef MSDOSFS
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <unistd.h>
+#include <termios.h>
+
+/* Hack for rsaref package add, which displays interactive license.
+ * Used by package.c
+ */
+int _interactiveHack;
+int FixItMode = 0;
+
+static void create_termcap(void);
+static void fixit_common(void);
+
+#define TERMCAP_FILE "/usr/share/misc/termcap"
+
+static void installConfigure(void);
+
+Boolean
+checkLabels(Boolean whinge, Chunk **rdev, Chunk **sdev, Chunk **udev, Chunk **vdev, Chunk **tdev, Chunk **hdev)
+{
+ Device **devs;
+ Boolean status;
+ Disk *disk;
+ Chunk *c1, *c2, *rootdev, *swapdev, *usrdev, *vardev, *tmpdev, *homedev;
+ int i;
+
+ /* Don't allow whinging if noWarn is set */
+ if (variable_get(VAR_NO_WARN))
+ whinge = FALSE;
+
+ status = TRUE;
+ if (rdev)
+ *rdev = NULL;
+ if (sdev)
+ *sdev = NULL;
+ if (udev)
+ *udev = NULL;
+ if (vdev)
+ *vdev = NULL;
+ if (tdev)
+ *tdev = NULL;
+ if (hdev)
+ *hdev = NULL;
+ rootdev = swapdev = usrdev = vardev = tmpdev = homedev = NULL;
+
+ /* We don't need to worry about root/usr/swap if we're already multiuser */
+ if (!RunningAsInit)
+ return status;
+
+ devs = deviceFind(NULL, DEVICE_TYPE_DISK);
+ /* First verify that we have a root device */
+ for (i = 0; devs[i]; i++) {
+ if (!devs[i]->enabled)
+ continue;
+ disk = (Disk *)devs[i]->private;
+ msgDebug("Scanning disk %s for root filesystem\n", disk->name);
+ if (!disk->chunks)
+ msgFatal("No chunk list found for %s!", disk->name);
+ for (c1 = disk->chunks->part; c1; c1 = c1->next) {
+ 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);
+ }
+ } else if (!strcmp(((PartInfo *)c2->private_data)->mountpoint, "/tmp")) {
+ if (tmpdev) {
+ if (whinge)
+ msgConfirm("WARNING: You have more than one /tmp filesystem.\n"
+ "Using the first one found.");
+ continue;
+ }
+ else {
+ tmpdev = c2;
+ if (isDebug())
+ msgDebug("Found tmpdev at %s!\n", tmpdev->name);
+ }
+ } else if (!strcmp(((PartInfo *)c2->private_data)->mountpoint, "/home")) {
+ if (homedev) {
+ if (whinge)
+ msgConfirm("WARNING: You have more than one /home filesystem.\n"
+ "Using the first one found.");
+ continue;
+ }
+ else {
+ homedev = c2;
+ if (isDebug())
+ msgDebug("Found homedev at %s!\n", homedev->name);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* 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 */
+ if (rdev)
+ *rdev = rootdev;
+ if (sdev)
+ *sdev = swapdev;
+ if (udev)
+ *udev = usrdev;
+ if (vdev)
+ *vdev = vardev;
+ if (tdev)
+ *tdev = tmpdev;
+ if (hdev)
+ *hdev = homedev;
+
+ if (!rootdev && whinge) {
+ msgConfirm("No root device found - you must label a partition as /\n"
+ "in the label editor.");
+ status = FALSE;
+ }
+ if (!swapdev && whinge) {
+ msgConfirm("No swap devices found - you must create at least one\n"
+ "swap partition.");
+ status = FALSE;
+ }
+ return status;
+}
+
+static int
+installInitial(void)
+{
+ static Boolean alreadyDone = FALSE;
+ int status = DITEM_SUCCESS;
+
+ if (alreadyDone)
+ return DITEM_SUCCESS;
+
+ if (!variable_get(DISK_LABELLED)) {
+ msgConfirm("You need to assign disk labels before you can proceed with\n"
+ "the installation.");
+ return DITEM_FAILURE;
+ }
+ /* If it's labelled, assume it's also partitioned */
+ if (!variable_get(DISK_PARTITIONED))
+ variable_set2(DISK_PARTITIONED, "yes", 0);
+
+ /* If we refuse to proceed, bail. */
+ dialog_clear_norefresh();
+ if (!variable_get(VAR_NO_WARN)) {
+ if (msgYesNo(
+ "Last Chance! Are you SURE you want continue the installation?\n\n"
+ "If you're running this on a disk with data you wish to save\n"
+ "then WE STRONGLY ENCOURAGE YOU TO MAKE PROPER BACKUPS before\n"
+ "proceeding!\n\n"
+ "We can take no responsibility for lost disk contents!") != 0)
+ return DITEM_FAILURE;
+ }
+
+ if (DITEM_STATUS(diskLabelCommit(NULL)) != DITEM_SUCCESS) {
+ msgConfirm("Couldn't make filesystems properly. Aborting.");
+ return DITEM_FAILURE;
+ }
+
+ if (!copySelf()) {
+ msgConfirm("installInitial: Couldn't clone the boot floppy onto the\n"
+ "root file system. Aborting!");
+ return DITEM_FAILURE;
+ }
+
+ if (!Restarting && chroot("/mnt") == -1) {
+ msgConfirm("installInitial: Unable to chroot to %s - this is bad!",
+ "/mnt");
+ return DITEM_FAILURE;
+ }
+
+ chdir("/");
+ variable_set2(RUNNING_ON_ROOT, "yes", 0);
+
+ /* Configure various files in /etc */
+ if (DITEM_STATUS(configResolv(NULL)) == DITEM_FAILURE)
+ status = DITEM_FAILURE;
+ if (DITEM_STATUS(configFstab(NULL)) == DITEM_FAILURE)
+ status = DITEM_FAILURE;
+
+ /* stick a helpful shell over on the 4th VTY */
+ if (!variable_get(VAR_NO_HOLOSHELL))
+ systemCreateHoloshell();
+
+ alreadyDone = TRUE;
+ return status;
+}
+
+int
+installFixitHoloShell(dialogMenuItem *self)
+{
+ FixItMode = 1;
+ systemCreateHoloshell();
+ return DITEM_SUCCESS;
+ FixItMode = 0;
+}
+
+int
+installFixitCDROM(dialogMenuItem *self)
+{
+ struct stat sb;
+
+ if (!RunningAsInit)
+ return DITEM_SUCCESS;
+
+ variable_set2(SYSTEM_STATE, "fixit", 0);
+ (void)unlink("/mnt2");
+ (void)rmdir("/mnt2");
+
+ while (1) {
+ msgConfirm("Please insert a FreeBSD live filesystem CD/DVD and press return");
+ if (DITEM_STATUS(mediaSetCDROM(NULL)) != DITEM_SUCCESS
+ || !DEVICE_INIT(mediaDevice)) {
+ /* If we can't initialize it, it's probably not a FreeBSD CDROM so punt on it */
+ mediaClose();
+ if (msgYesNo("Unable to mount the disc - do you want to try again?") != 0)
+ return DITEM_FAILURE;
+ }
+ else
+ break;
+ }
+
+ /* Since the fixit code expects everything to be in /mnt2, and the CDROM mounting stuff /dist, do
+ * a little kludge dance here..
+ */
+ if (symlink("/dist", "/mnt2")) {
+ msgConfirm("Unable to symlink /mnt2 to the disc mount point. Please report this\n"
+ "unexpected failure to freebsd-bugs@FreeBSD.org.");
+ return DITEM_FAILURE;
+ }
+
+ /*
+ * If /tmp points to /mnt2/tmp from a previous fixit floppy session, it's
+ * not very good for us if we point it to the CDROM now. Rather make it
+ * a directory in the root MFS then. Experienced admins will still be
+ * able to mount their disk's /tmp over this if they need.
+ */
+ if (lstat("/tmp", &sb) == 0 && (sb.st_mode & S_IFMT) == S_IFLNK)
+ (void)unlink("/tmp");
+ Mkdir("/tmp");
+
+ /*
+ * Since setuid binaries ignore LD_LIBRARY_PATH, we indeed need the
+ * ld.so.hints file. Fortunately, it's fairly small (~ 3 KB).
+ */
+ if (!file_readable("/var/run/ld.so.hints")) {
+ Mkdir("/var/run");
+ if (vsystem("/mnt2/sbin/ldconfig -s /mnt2/usr/lib")) {
+ msgConfirm("Warning: ldconfig could not create the ld.so hints file.\n"
+ "Dynamic executables from the disc likely won't work.");
+ }
+ }
+
+ /* Yet more iggly hardcoded pathnames. */
+ Mkdir("/usr/libexec");
+ if (!file_readable("/usr/libexec/ld.so") && file_readable("/mnt2/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 disc likely won't work.");
+ }
+ }
+ /* optional nicety */
+ if (!file_readable("/usr/bin/vi"))
+ symlink("/mnt2/usr/bin/vi", "/usr/bin/vi");
+ fixit_common();
+ mediaClose();
+ msgConfirm("Please remove the FreeBSD fixit CDROM/DVD now.");
+ return DITEM_SUCCESS;
+}
+
+int
+installFixitFloppy(dialogMenuItem *self)
+{
+ struct ufs_args args;
+ extern char *distWanted;
+
+ if (!RunningAsInit)
+ return DITEM_SUCCESS;
+
+ /* Try to open the floppy drive */
+ if (DITEM_STATUS(mediaSetFloppy(NULL)) == DITEM_FAILURE || !mediaDevice) {
+ msgConfirm("Unable to set media device to floppy.");
+ mediaClose();
+ return DITEM_FAILURE;
+ }
+
+ memset(&args, 0, sizeof(args));
+ args.fspec = mediaDevice->devname;
+ mediaDevice->private = "/mnt2";
+ distWanted = NULL;
+ Mkdir("/mnt2");
+
+ variable_set2(SYSTEM_STATE, "fixit", 0);
+
+ while (1) {
+ if (!DEVICE_INIT(mediaDevice)) {
+ if (msgYesNo("The attempt to mount the fixit floppy failed, bad floppy\n"
+ "or unclean filesystem. Do you want to try again?"))
+ return DITEM_FAILURE;
+ }
+ else
+ break;
+ }
+ if (!directory_exists("/tmp"))
+ (void)symlink("/mnt2/tmp", "/tmp");
+ fixit_common();
+ mediaClose();
+ msgConfirm("Please remove the fixit floppy now.");
+ return DITEM_SUCCESS;
+}
+
+/*
+ * The common code for both fixit variants.
+ */
+static void
+fixit_common(void)
+{
+ pid_t child;
+ int waitstatus;
+
+ if (!directory_exists("/var/tmp/vi.recover")) {
+ if (DITEM_STATUS(Mkdir("/var/tmp/vi.recover")) != DITEM_SUCCESS) {
+ msgConfirm("Warning: Was unable to create a /var/tmp/vi.recover directory.\n"
+ "vi will kvetch and moan about it as a result but should still\n"
+ "be essentially usable.");
+ }
+ }
+ if (!directory_exists("/bin"))
+ (void)Mkdir("/bin");
+ (void)symlink("/stand/sh", "/bin/sh");
+ /* Link the /etc/ files */
+ if (DITEM_STATUS(Mkdir("/etc")) != DITEM_SUCCESS)
+ msgConfirm("Unable to create an /etc directory! Things are weird on this floppy..");
+ else if ((symlink("/mnt2/etc/spwd.db", "/etc/spwd.db") == -1 && errno != EEXIST) ||
+ (symlink("/mnt2/etc/protocols", "/etc/protocols") == -1 && errno != EEXIST) ||
+ (symlink("/mnt2/etc/services", "/etc/services") == -1 && errno != EEXIST))
+ msgConfirm("Couldn't symlink the /etc/ files! I'm not sure I like this..");
+ if (!file_readable(TERMCAP_FILE))
+ create_termcap();
+ if (strcmp(variable_get(VAR_FIXIT_TTY), "serial") == 0)
+ systemSuspendDialog(); /* must be before the fork() */
+ if (!(child = fork())) {
+ int i, fd;
+ struct termios foo;
+ extern int login_tty(int);
+
+ ioctl(0, TIOCNOTTY, NULL);
+ for (i = getdtablesize(); i >= 0; --i)
+ close(i);
+
+ if (strcmp(variable_get(VAR_FIXIT_TTY), "serial") == 0)
+ fd = open("/dev/console", O_RDWR);
+ else
+ fd = open("/dev/ttyv3", O_RDWR);
+ ioctl(0, TIOCSCTTY, &fd);
+ dup2(0, 1);
+ dup2(0, 2);
+ DebugFD = 2;
+ if (login_tty(fd) == -1)
+ msgDebug("fixit: I can't set the controlling terminal.\n");
+
+ signal(SIGTTOU, SIG_IGN);
+ if (tcgetattr(0, &foo) != -1) {
+ foo.c_cc[VERASE] = '\010';
+ if (tcsetattr(0, TCSANOW, &foo) == -1)
+ msgDebug("fixit shell: Unable to set erase character.\n");
+ }
+ else
+ msgDebug("fixit shell: Unable to get terminal attributes!\n");
+ setenv("PATH", "/bin:/sbin:/usr/bin:/usr/sbin:/stand:"
+ "/mnt2/stand:/mnt2/bin:/mnt2/sbin:/mnt2/usr/bin:/mnt2/usr/sbin", 1);
+ setenv("MAKEDEVPATH", "/sbin:/bin:/stand:"
+ "/mnt2/sbin:/mnt2/bin:/mnt2/stand", 1);
+ if (strcmp(variable_get(VAR_FIXIT_TTY), "serial") == 0) {
+ printf("Waiting for fixit shell to exit.\n"
+ "When you are done, type ``exit'' to exit\n"
+ "the fixit shell and be returned here.\n\n");
+ fflush(stdout);
+ }
+
+ /* use the .profile from the fixit medium */
+ setenv("HOME", "/mnt2", 1);
+ chdir("/mnt2");
+ execlp("sh", "-sh", (char *)0);
+ msgDebug("fixit shell: Failed to execute shell!\n");
+ _exit(1);;
+ }
+ else {
+ if (strcmp(variable_get(VAR_FIXIT_TTY), "standard") == 0) {
+ dialog_clear_norefresh();
+ msgNotify("Waiting for fixit shell to exit. Go to VTY4 now by\n"
+ "typing ALT-F4. When you are done, type ``exit'' to exit\n"
+ "the fixit shell and be returned here\n.");
+ }
+ (void)waitpid(child, &waitstatus, 0);
+ if (strcmp(variable_get(VAR_FIXIT_TTY), "serial") == 0)
+ systemResumeDialog();
+ }
+ dialog_clear();
+}
+
+
+int
+installExpress(dialogMenuItem *self)
+{
+ int i;
+
+ dialog_clear_norefresh();
+ variable_set2(SYSTEM_STATE, "express", 0);
+#ifndef __alpha__
+ if (DITEM_STATUS((i = diskPartitionEditor(self))) == DITEM_FAILURE)
+ return i;
+#endif
+
+ if (DITEM_STATUS((i = diskLabelEditor(self))) == DITEM_FAILURE)
+ return i;
+
+ if (DITEM_STATUS((i = installCommit(self))) == DITEM_SUCCESS) {
+ i |= DITEM_LEAVE_MENU;
+ /* Set default security level */
+ configSecurityModerate(NULL);
+
+ /* Give user the option of one last configuration spree */
+ installConfigure();
+ }
+ return i;
+}
+
+/* Standard mode installation */
+int
+installStandard(dialogMenuItem *self)
+{
+ int i, tries = 0;
+ Device **devs;
+
+ variable_set2(SYSTEM_STATE, "standard", 0);
+ dialog_clear_norefresh();
+#ifndef __alpha__
+ 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
+
+#ifdef __alpha__
+ msgConfirm("Now 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("Now you need to create BSD partitions inside of the fdisk partition(s)\n"
+ "just created. If you have a reasonable amount of disk space (200MB or more)\n"
+ "and don't have any special requirements, simply use the (A)uto command to\n"
+ "allocate space automatically. If you have more specific needs or just don't\n"
+ "care for the layout chosen by (A)uto, press F1 for more information on\n"
+ "manual layout.");
+#endif
+
+ if (DITEM_STATUS(diskLabelEditor(self)) == DITEM_FAILURE)
+ return DITEM_FAILURE;
+
+ if (DITEM_STATUS((i = installCommit(self))) == DITEM_FAILURE) {
+ dialog_clear();
+ msgConfirm("Installation completed with some errors. You may wish to\n"
+ "scroll through the debugging messages on VTY1 with the\n"
+ "scroll-lock feature. You can also choose \"No\" at the next\n"
+ "prompt and go back into the installation menus to try and retry\n"
+ "whichever operations have failed.");
+ return i;
+
+ }
+ else {
+ dialog_clear();
+ msgConfirm("Congratulations! You now have FreeBSD installed on your system.\n\n"
+ "We will now move on to the final configuration questions.\n"
+ "For any option you do not wish to configure, simply select\n"
+ "No.\n\n"
+ "If you wish to re-enter this utility after the system is up, you\n"
+ "may do so by typing: /usr/sbin/sysinstall.");
+ }
+ if (mediaDevice->type != DEVICE_TYPE_FTP && mediaDevice->type != DEVICE_TYPE_NFS) {
+ if (!msgYesNo("Would you like to configure any Ethernet or SLIP/PPP network devices?")) {
+ Device *tmp = tcpDeviceSelect();
+
+ if (tmp && !((DevInfo *)tmp->private)->use_dhcp && !msgYesNo("Would you like to bring the %s interface up right now?", tmp->name))
+ if (!DEVICE_INIT(tmp))
+ msgConfirm("Initialization of %s device failed.", tmp->name);
+ }
+ dialog_clear_norefresh();
+ }
+
+ if (!msgNoYes("Do you want this machine to function as a network gateway?"))
+ variable_set2("gateway_enable", "YES", 1);
+
+ dialog_clear_norefresh();
+ if (!msgNoYes("Do you want to configure inetd and the network services that it provides?"))
+ configInetd(self);
+
+ dialog_clear_norefresh();
+ if (!msgNoYes("Do you want to have anonymous FTP access to this machine?"))
+ configAnonFTP(self);
+
+ dialog_clear_norefresh();
+ if (!msgNoYes("Do you want to configure this machine as an NFS server?"))
+ configNFSServer(self);
+
+ dialog_clear_norefresh();
+ if (!msgNoYes("Do you want to configure this machine as an NFS client?"))
+ variable_set2("nfs_client_enable", "YES", 1);
+
+ if (!msgNoYes("Do you want to select a default security profile for\n"
+ "this host (select No for \"moderate\" security)?"))
+ configSecurityProfile(self);
+ else
+ configSecurityModerate(self);
+
+ dialog_clear_norefresh();
+ if (!msgNoYes("Would you like to customize your system console settings?"))
+ dmenuOpenSimple(&MenuSyscons, FALSE);
+
+ dialog_clear_norefresh();
+ if (!msgYesNo("Would you like to set this machine's time zone now?"))
+ systemExecute("tzsetup");
+
+#ifdef __i386__
+ dialog_clear_norefresh();
+ if (!msgYesNo("Would you like to enable Linux binary compatibility?"))
+ (void)configLinux(self);
+#endif
+
+ dialog_clear_norefresh();
+ if (!msgNoYes("Does this system have a non-USB mouse attached to it?"))
+ dmenuOpenSimple(&MenuMouse, FALSE);
+
+ /* Now would be a good time to checkpoint the configuration data */
+ configRC_conf();
+ sync();
+
+ if (directory_exists("/usr/X11R6")) {
+ dialog_clear_norefresh();
+ if (!msgYesNo("Would you like to configure your X server at this time?"))
+ (void)configXSetup(self);
+ }
+
+ dialog_clear_norefresh();
+ if (!msgYesNo("The FreeBSD package collection is a collection of thousands of ready-to-run\n"
+ "applications, from text editors to games to WEB servers and more. Would you\n"
+ "like to browse the collection now?")) {
+ (void)configPackages(self);
+ }
+
+ if (!msgYesNo("Would you like to add any initial user accounts to the system?\n"
+ "Adding at least one account for yourself at this stage is suggested\n"
+ "since working as the \"root\" user is dangerous (it is easy to do\n"
+ "things which adversely affect the entire system)."))
+ (void)configUsers(self);
+
+ msgConfirm("Now you must set the system manager's password.\n"
+ "This is the password you'll use to log in as \"root\".");
+ if (!systemExecute("passwd root"))
+ variable_set2("root_password", "YES", 0);
+
+ /* XXX Put whatever other nice configuration questions you'd like to ask the user here XXX */
+
+ /* Give user the option of one last configuration spree */
+ dialog_clear_norefresh();
+ installConfigure();
+ return DITEM_LEAVE_MENU;
+}
+
+/* The version of commit we call from the Install Custom menu */
+int
+installCustomCommit(dialogMenuItem *self)
+{
+ int i;
+
+ i = installCommit(self);
+ if (DITEM_STATUS(i) == DITEM_SUCCESS) {
+ /* Set default security level */
+ configSecurityModerate(NULL);
+
+ /* Give user the option of one last configuration spree */
+ installConfigure();
+ return i;
+ }
+ else
+ msgConfirm("The commit operation completed with errors. Not\n"
+ "updating /etc files.");
+ return i;
+}
+
+/*
+ * What happens when we finally decide to going ahead with the installation.
+ *
+ * This is broken into multiple stages so that the user can do a full
+ * installation but come back here again to load more distributions,
+ * perhaps from a different media type. This would allow, for
+ * example, the user to load the majority of the system from CDROM and
+ * then use ftp to load just the CRYPTO dist.
+ */
+int
+installCommit(dialogMenuItem *self)
+{
+ int i;
+ char *str;
+
+ dialog_clear_norefresh();
+ if (!Dists)
+ distConfig(NULL);
+
+ if (!Dists) {
+ (void)dmenuOpenSimple(&MenuDistributions, FALSE);
+ /* select reasonable defaults if necessary */
+ if (!Dists)
+ Dists = _DIST_USER;
+ }
+
+ if (!mediaVerify())
+ return DITEM_FAILURE;
+
+ str = variable_get(SYSTEM_STATE);
+ if (isDebug())
+ msgDebug("installCommit: System state is `%s'\n", str);
+
+ /* Installation stuff we wouldn't do to a running system */
+ if (RunningAsInit && DITEM_STATUS((i = installInitial())) == DITEM_FAILURE)
+ return i;
+
+try_media:
+ if (!DEVICE_INIT(mediaDevice)) {
+ if (!msgYesNo("Unable to initialize selected media. Would you like to\n"
+ "adjust your media configuration and try again?")) {
+ mediaDevice = NULL;
+ if (!mediaVerify())
+ return DITEM_FAILURE;
+ else
+ goto try_media;
+ }
+ else
+ return DITEM_FAILURE;
+ }
+
+ /* Now go get it all */
+ i = distExtractAll(self);
+
+ /* When running as init, *now* it's safe to grab the rc.foo vars */
+ installEnvironment();
+
+ variable_set2(SYSTEM_STATE, DITEM_STATUS(i) == DITEM_FAILURE ? "error-install" : "full-install", 0);
+
+ return i;
+}
+
+static void
+installConfigure(void)
+{
+ /* Final menu of last resort */
+ if (!msgNoYes("Visit the general configuration menu for a chance to set\n"
+ "any last options?"))
+ dmenuOpenSimple(&MenuConfigure, FALSE);
+ configRC_conf();
+ sync();
+}
+
+int
+installFixupBin(dialogMenuItem *self)
+{
+ Device **devs;
+ char *cp;
+ int i;
+ FILE *fp;
+ int kstat = 1;
+
+ /* All of this is done only as init, just to be safe */
+ if (RunningAsInit) {
+#ifdef __i386__
+ if ((fp = fopen("/boot/loader.conf", "a")) != NULL) {
+ if (!kstat || !OnVTY)
+ fprintf(fp, "# -- sysinstall generated deltas -- #\n");
+ if (!kstat)
+ fprintf(fp, "userconfig_script_load=\"YES\"\n");
+ if (!OnVTY)
+ fprintf(fp, "console=\"comconsole\"\n");
+ fclose(fp);
+ }
+#endif
+ /* BOGON #1: Resurrect /dev after bin distribution screws it up */
+ dialog_clear_norefresh();
+ msgNotify("Remaking all devices.. Please wait!");
+ if (!Fake)
+ (void)unmount("/dev", MNT_FORCE);
+ if (vsystem("cd /dev; sh MAKEDEV all")) {
+ msgConfirm("MAKEDEV returned non-zero status");
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+
+ dialog_clear_norefresh();
+ 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) {
+ dialog_clear_norefresh();
+ 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 | DITEM_RESTORE;
+ }
+ }
+ }
+ }
+
+ /* BOGON #2: We leave /etc in a bad state */
+ chmod("/etc", 0755);
+
+ /* BOGON #3: No /var/db/mountdtab complains */
+ Mkdir("/var/db");
+ creat("/var/db/mountdtab", 0644);
+
+ /* BOGON #4: /compat created by default in root fs */
+ Mkdir("/usr/compat");
+ vsystem("ln -s usr/compat /compat");
+
+ /* BOGON #5: aliases database not build for bin */
+ vsystem("newaliases");
+
+ /* Now run all the mtree stuff to fix things up */
+ vsystem("mtree -deU -f /etc/mtree/BSD.root.dist -p /");
+ vsystem("mtree -deU -f /etc/mtree/BSD.var.dist -p /var");
+ vsystem("mtree -deU -f /etc/mtree/BSD.usr.dist -p /usr");
+
+ /* Do all the last ugly work-arounds here */
+ }
+ return DITEM_SUCCESS | DITEM_RESTORE;
+}
+
+#ifdef X_AS_PKG
+int
+installX11package(dialogMenuItem *self)
+{
+ WINDOW *w = savescr();
+ int i;
+
+ dialog_clear_norefresh();
+ msgNotify("Installing XFree86 package...");
+ i = package_add("XFree86-4");
+ restorescr(w);
+ return i;
+}
+#endif
+
+/* Fix side-effects from the the XFree86 installation */
+int
+installFixupXFree(dialogMenuItem *self)
+{
+ /* BOGON #1: XFree86 requires various specialized fixups */
+ if (directory_exists("/usr/X11R6")) {
+ dialog_clear_norefresh();
+ msgNotify("Fixing permissions in XFree86 tree..");
+ vsystem("chmod -R a+r /usr/X11R6");
+ vsystem("find /usr/X11R6 -type d | xargs chmod a+x");
+
+#ifndef X_AS_PKG
+ /* Also do bogus minimal package registration so ports don't whine */
+ if (file_readable("/usr/X11R6/lib/X11/pkgreg.tar.gz")) {
+ dialog_clear_norefresh();
+ msgNotify("Installing package metainfo..");
+ vsystem("tar xpzf /usr/X11R6/lib/X11/pkgreg.tar.gz -C / && rm /usr/X11R6/lib/X11/pkgreg.tar.gz");
+ }
+#endif
+ }
+ return DITEM_SUCCESS | DITEM_RESTORE;
+}
+
+/* Go newfs and/or mount all the filesystems we've been asked to */
+int
+installFilesystems(dialogMenuItem *self)
+{
+ int i, mountfailed;
+ Disk *disk;
+ Chunk *c1, *c2, *rootdev, *swapdev;
+ 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, NULL, NULL, NULL, NULL))
+ return DITEM_FAILURE;
+
+ if (rootdev)
+ root = (PartInfo *)rootdev->private_data;
+ else
+ root = NULL;
+
+ command_clear();
+ if (swapdev && RunningAsInit) {
+ /* As the very first thing, try to get ourselves some swap space */
+ sprintf(dname, "/dev/%s", swapdev->name);
+ if (!Fake && (!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)) {
+ dialog_clear_norefresh();
+ msgNotify("Added %s as initial swap device", dname);
+ }
+ else {
+ msgConfirm("WARNING! Unable to swap to %s: %s\n"
+ "This may cause the installation to fail at some point\n"
+ "if you don't have a lot of memory.", dname, strerror(errno));
+ }
+ }
+ }
+
+ if (rootdev && RunningAsInit) {
+ /* Next, create and/or mount the root device */
+ sprintf(dname, "/dev/%s", rootdev->name);
+ if (!Fake && (!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 | DITEM_RESTORE;
+ }
+ if (strcmp(root->mountpoint, "/"))
+ msgConfirm("Warning: %s is marked as a root partition but is mounted on %s", rootdev->name, root->mountpoint);
+
+ if (root->newfs && (!upgrade || !msgNoYes("You are upgrading - are you SURE you want to newfs the root partition?"))) {
+ int i;
+
+ dialog_clear_norefresh();
+ msgNotify("Making a new root filesystem on %s", dname);
+ i = 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 | DITEM_RESTORE;
+ }
+ }
+ else {
+ if (!upgrade) {
+ msgConfirm("Warning: Using existing root partition. It will be assumed\n"
+ "that you have the appropriate device entries already in /dev.");
+ }
+ dialog_clear_norefresh();
+ msgNotify("Checking integrity of existing %s filesystem.", dname);
+ i = vsystem("fsck_ffs -y %s", dname);
+ if (i)
+ msgConfirm("Warning: fsck returned status of %d for %s.\n"
+ "This partition may be unsafe to use.", i, dname);
+ }
+ if (root->soft) {
+ i = vsystem("tunefs -n enable %s", dname);
+ if (i)
+ msgConfirm("Warning: Unable to enable softupdates for root filesystem on %s", dname);
+ }
+
+ /* Switch to block device */
+ sprintf(dname, "/dev/%s", rootdev->name);
+ if (Mount("/mnt", dname)) {
+ msgConfirm("Unable to mount the root file system on %s! Giving up.", dname);
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+
+ /* Mount devfs for other partitions to mount */
+ Mkdir("/mnt/dev");
+ if (!Fake)
+ mountfailed = mount("devfs", "/mnt/dev", 0, NULL);
+
+ if (mountfailed) {
+ dialog_clear_norefresh();
+ 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 | DITEM_RESTORE;
+ }
+ }
+ }
+
+ /* Now buzz through the rest of the partitions and mount them too */
+ devs = deviceFind(NULL, DEVICE_TYPE_DISK);
+ for (i = 0; devs[i]; i++) {
+ if (!devs[i]->enabled)
+ continue;
+
+ disk = (Disk *)devs[i]->private;
+ if (!disk->chunks) {
+ msgConfirm("No chunk list found for %s!", disk->name);
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+ if (mountfailed) {
+ 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 || !msgNoYes("You are upgrading - are you SURE you want to newfs /dev/%s?", c2->name)))
+ command_shell_add(tmp->mountpoint, "%s %s/dev/%s", tmp->newfs_cmd, RunningAsInit ? "/mnt" : "", c2->name);
+ else
+ command_shell_add(tmp->mountpoint, "fsck_ffs -y %s/dev/%s", RunningAsInit ? "/mnt" : "", c2->name);
+ if (tmp->soft)
+ command_shell_add(tmp->mountpoint, "tunefs -n enable %s/dev/%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) {
+ dialog_clear_norefresh();
+ msgNotify("Added %s as an additional swap device", fname);
+ }
+ else {
+ msgConfirm("Unable to add %s as a swap device: %s", fname, strerror(errno));
+ }
+ }
+ }
+ }
+ else if (c1->type == fat && c1->private_data && (root->newfs || upgrade)) {
+ char name[FILENAME_MAX];
+
+ sprintf(name, "%s/%s", RunningAsInit ? "/mnt" : "", ((PartInfo *)c1->private_data)->mountpoint);
+ Mkdir(name);
+ }
+ }
+ }
+
+ command_sort();
+ command_execute();
+ dialog_clear_norefresh();
+ return DITEM_SUCCESS | DITEM_RESTORE;
+}
+
+static char *
+getRelname(void)
+{
+ static char buf[64];
+ size_t sz = (sizeof buf) - 1;
+
+ if (sysctlbyname("kern.osrelease", buf, &sz, NULL, 0) != -1) {
+ buf[sz] = '\0';
+ return buf;
+ }
+ else
+ return "<unknown>";
+}
+
+/* Initialize various user-settable values to their defaults */
+int
+installVarDefaults(dialogMenuItem *self)
+{
+ char *cp;
+
+ /* Set default startup options */
+ variable_set2(VAR_RELNAME, getRelname(), 0);
+ variable_set2(VAR_CPIO_VERBOSITY, "high", 0);
+ variable_set2(VAR_TAPE_BLOCKSIZE, DEFAULT_TAPE_BLOCKSIZE, 0);
+ variable_set2(VAR_INSTALL_ROOT, "/", 0);
+ variable_set2(VAR_INSTALL_CFG, "install.cfg", 0);
+ cp = getenv("EDITOR");
+ if (!cp)
+ cp = "/usr/bin/ee";
+ variable_set2(VAR_EDITOR, cp, 0);
+ variable_set2(VAR_FTP_USER, "ftp", 0);
+ variable_set2(VAR_BROWSER_PACKAGE, "links", 0);
+ variable_set2(VAR_BROWSER_BINARY, "/usr/local/bin/links", 0);
+ variable_set2(VAR_FTP_STATE, "passive", 0);
+ variable_set2(VAR_NFS_SECURE, "NO", -1);
+ if (OnVTY)
+ variable_set2(VAR_FIXIT_TTY, "standard", 0);
+ else
+ variable_set2(VAR_FIXIT_TTY, "serial", 0);
+ variable_set2(VAR_PKG_TMPDIR, "/var/tmp", 0);
+ variable_set2(VAR_MEDIA_TIMEOUT, itoa(MEDIA_TIMEOUT), 0);
+ if (getpid() != 1)
+ variable_set2(SYSTEM_STATE, "update", 0);
+ else
+ variable_set2(SYSTEM_STATE, "init", 0);
+ variable_set2(VAR_NEWFS_ARGS, "-b 16384 -f 2048", 0);
+ variable_set2(VAR_CONSTERM, "NO", 0);
+ return DITEM_SUCCESS;
+}
+
+/* Load the environment up from various system configuration files */
+void
+installEnvironment(void)
+{
+ configEnvironmentRC_conf();
+ if (file_readable("/etc/resolv.conf"))
+ configEnvironmentResolv("/etc/resolv.conf");
+}
+
+/* Copy the boot floppy contents into /stand */
+Boolean
+copySelf(void)
+{
+ int i;
+
+ if (file_readable("/boot.help"))
+ vsystem("cp /boot.help /mnt");
+ msgWeHaveOutput("Copying the boot floppy to /stand on root filesystem");
+ i = vsystem("find -x /stand | cpio %s -pdum /mnt", cpioVerbosity());
+ if (i) {
+ msgConfirm("Copy returned error status of %d!", i);
+ return FALSE;
+ }
+
+ /* Copy the /etc files into their rightful place */
+ if (vsystem("cd /mnt/stand; find etc | cpio %s -pdum /mnt", cpioVerbosity())) {
+ msgConfirm("Couldn't copy up the /etc files!");
+ return TRUE;
+ }
+ return TRUE;
+}
+
+static void
+create_termcap(void)
+{
+ FILE *fp;
+
+ const char *caps[] = {
+ termcap_vt100, termcap_cons25, termcap_cons25_m, termcap_cons25r,
+ termcap_cons25r_m, termcap_cons25l1, termcap_cons25l1_m,
+ termcap_xterm, NULL,
+ };
+ const char **cp;
+
+ if (!file_readable(TERMCAP_FILE)) {
+ Mkdir("/usr/share/misc");
+ fp = fopen(TERMCAP_FILE, "w");
+ if (!fp) {
+ msgConfirm("Unable to initialize termcap file. Some screen-oriented\nutilities may not work.");
+ return;
+ }
+ cp = caps;
+ while (*cp)
+ fprintf(fp, "%s\n", *(cp++));
+ fclose(fp);
+ }
+}
diff --git a/usr.sbin/sysinstall/install.cfg b/usr.sbin/sysinstall/install.cfg
new file mode 100644
index 0000000..fb7ad88
--- /dev/null
+++ b/usr.sbin/sysinstall/install.cfg
@@ -0,0 +1,99 @@
+# This is the installation configuration file for my test machine,
+# crate.cdrom.com.
+# It is included here merely as a sort-of-documented example.
+#
+# $FreeBSD$
+
+# Turn on extra debugging.
+debug=yes
+
+################################
+# My host specific data
+hostname=crate.cdrom.com
+domainname=cdrom.com
+nameserver=204.216.27.3
+defaultrouter=204.216.27.228
+ipaddr=204.216.27.230
+netmask=255.255.255.240
+################################
+
+################################
+# Which installation device to use - ftp is pointed directly at my local
+# machine and the installation device is my WD8013 ethernet interface.
+_ftpPath=ftp://time.cdrom.com/pub
+netDev=ed0
+mediaSetFTP
+################################
+
+################################
+# Select which distributions we want.
+dists=bin doc manpages info compat21 des src sbase ssys
+distSetCustom
+################################
+
+################################
+# Now set the parameters for the partition editor on ad0. Set to use the
+# disk exclusively (could also be "all" to use the whole disk but
+# respecting the MBR or "free" to use only unallocated space for FreeBSD).
+disk=ad0
+partition=exclusive
+diskPartitionEditor
+
+# Uncomment this instead to use only the free space and install boot manager.
+#partition=free
+#bootManager=booteasy
+#diskPartitionEditor
+################################
+
+################################
+
+# This assumes that slice 1 is a DOS partition and mounts it as /dos,
+# which is the case on my laptop.
+#
+# All sizes are expressed in 512 byte blocks!
+
+# A 20MB root partition
+ad0s1-1=ufs 40960 /
+# And a 20MB swap partition
+ad0s1-2=swap 40960 none
+# Followed by a /usr partition using all remaining space (size 0 = free space)
+# and with softupdates enabled (non-zero arg following mountpoint).
+ad0s1-3=ufs 0 /usr 1
+# Let's do it!
+diskLabelEditor
+
+################################
+
+################################
+# Now partition the 2nd disk.
+disk=ad1
+partition=exclusive
+diskPartitionEditor
+
+ad1s1-1=ufs 40960 /var
+ad1s1-2=ufs 0 /usr/src
+diskLabelEditor
+################################
+
+################################
+# And the 3rd.
+disk=da0
+partition=exclusive
+diskPartitionEditor
+
+da0s1-1=swap 40960 none
+da0s1-2=ufs 0 /tmp
+diskLabelEditor
+################################
+
+
+# OK, everything is set. Do it!
+installCommit
+
+# Install some packages at the end.
+package=bash-1.14.7
+packageAdd
+package=ncftp-2.4.2
+packageAdd
+package=tcsh-6.07.02
+packageAdd
diff --git a/usr.sbin/sysinstall/installUpgrade.c b/usr.sbin/sysinstall/installUpgrade.c
new file mode 100644
index 0000000..613ad19
--- /dev/null
+++ b/usr.sbin/sysinstall/installUpgrade.c
@@ -0,0 +1,500 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <sys/disklabel.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/fcntl.h>
+#include <sys/wait.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/mount.h>
+
+static int installUpgradeNonInteractive(dialogMenuItem *self);
+
+typedef struct _hitList {
+ enum { JUST_COPY, CALL_HANDLER } action ;
+ char *name;
+ Boolean optional;
+ void (*handler)(struct _hitList *self);
+} HitList;
+
+/* These are the only meaningful files I know about */
+static HitList etc_files [] = {
+ { JUST_COPY, "Xaccel.ini", TRUE, NULL },
+ { JUST_COPY, "X11", TRUE, NULL },
+ { JUST_COPY, "adduser.conf", TRUE, NULL },
+ { JUST_COPY, "aliases", TRUE, NULL },
+ { JUST_COPY, "aliases.db", TRUE, NULL },
+ { JUST_COPY, "amd.map", TRUE, NULL },
+ { JUST_COPY, "auth.conf", TRUE, NULL },
+ { JUST_COPY, "crontab", TRUE, NULL },
+ { JUST_COPY, "csh.cshrc", TRUE, NULL },
+ { JUST_COPY, "csh.login", TRUE, NULL },
+ { JUST_COPY, "csh.logout", TRUE, NULL },
+ { JUST_COPY, "cvsupfile", TRUE, NULL },
+ { JUST_COPY, "dhclient.conf", TRUE, NULL },
+ { JUST_COPY, "disktab", TRUE, NULL },
+ { JUST_COPY, "dm.conf", TRUE, NULL },
+ { JUST_COPY, "dumpdates", TRUE, NULL },
+ { JUST_COPY, "exports", TRUE, NULL },
+ { JUST_COPY, "fbtab", TRUE, NULL },
+ { JUST_COPY, "fstab", FALSE, NULL },
+ { JUST_COPY, "ftpusers", TRUE, NULL },
+ { JUST_COPY, "gettytab", TRUE, NULL },
+ { JUST_COPY, "gnats", TRUE, NULL },
+ { JUST_COPY, "group", FALSE, NULL },
+ { JUST_COPY, "hosts", TRUE, NULL },
+ { JUST_COPY, "hosts.allow", TRUE, NULL },
+ { JUST_COPY, "hosts.equiv", TRUE, NULL },
+ { JUST_COPY, "hosts.lpd", TRUE, NULL },
+ { JUST_COPY, "inetd.conf", TRUE, NULL },
+ { JUST_COPY, "kerberosIV", TRUE, NULL },
+ { JUST_COPY, "localtime", TRUE, NULL },
+ { JUST_COPY, "login.access", TRUE, NULL },
+ { JUST_COPY, "login.conf", TRUE, NULL },
+ { JUST_COPY, "mail", TRUE, NULL },
+ { JUST_COPY, "mail.rc", TRUE, NULL },
+ { JUST_COPY, "make.conf", TRUE, NULL },
+ { JUST_COPY, "manpath.config", TRUE, NULL },
+ { JUST_COPY, "master.passwd", FALSE, NULL },
+ { JUST_COPY, "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, "nsmb.conf", TRUE, NULL },
+ { JUST_COPY, "nsswitch.conf", TRUE, NULL },
+ { JUST_COPY, "pam.conf", TRUE, NULL },
+ { JUST_COPY, "passwd", TRUE, NULL },
+ { JUST_COPY, "periodic", TRUE, NULL },
+ { JUST_COPY, "ppp", TRUE, NULL },
+ { JUST_COPY, "printcap", TRUE, NULL },
+ { JUST_COPY, "profile", TRUE, NULL },
+ { JUST_COPY, "pwd.db", TRUE, NULL },
+ { JUST_COPY, "rc.local", TRUE, NULL },
+ { JUST_COPY, "rc.firewall", TRUE, NULL },
+ { JUST_COPY, "rc.conf.local", TRUE, NULL },
+ { JUST_COPY, "remote", TRUE, NULL },
+ { JUST_COPY, "resolv.conf", TRUE, NULL },
+ { JUST_COPY, "rmt", TRUE, NULL },
+ { JUST_COPY, "sendmail.cf", TRUE, NULL },
+ { JUST_COPY, "sendmail.cw", TRUE, NULL },
+ { JUST_COPY, "services", TRUE, NULL },
+ { JUST_COPY, "shells", TRUE, NULL },
+ { JUST_COPY, "skeykeys", TRUE, NULL },
+ { JUST_COPY, "spwd.db", TRUE, NULL },
+ { JUST_COPY, "ssh", TRUE, NULL },
+ { JUST_COPY, "syslog.conf", TRUE, NULL },
+ { JUST_COPY, "ttys", TRUE, NULL },
+ { JUST_COPY, "uucp", TRUE, NULL },
+ { 0 },
+};
+
+void
+traverseHitlist(HitList *h)
+{
+ system("rm -rf /etc/upgrade");
+ Mkdir("/etc/upgrade");
+ while (h->name) {
+ if (!file_readable(h->name)) {
+ if (!h->optional)
+ msgConfirm("Unable to find an old /etc/%s file! That is decidedly non-standard and\n"
+ "your upgraded system may function a little strangely as a result.", h->name);
+ }
+ else {
+ if (h->action == JUST_COPY) {
+ /* Move the just-loaded copy aside */
+ vsystem("mv /etc/%s /etc/upgrade/%s", h->name, h->name);
+
+ /* Copy the old one into its place */
+ msgNotify("Resurrecting %s..", h->name);
+ /* Do this with tar so that symlinks and such are preserved */
+ if (vsystem("tar cf - %s | tar xpf - -C /etc", h->name))
+ msgConfirm("Unable to resurrect your old /etc/%s! Hmmmm.", h->name);
+ }
+ else /* call handler */
+ h->handler(h);
+ }
+ ++h;
+ }
+}
+
+int
+installUpgrade(dialogMenuItem *self)
+{
+ char saved_etc[FILENAME_MAX];
+ Boolean extractingBin = TRUE;
+
+ if (variable_get(VAR_NONINTERACTIVE))
+ return installUpgradeNonInteractive(self);
+
+ variable_set2(SYSTEM_STATE, "upgrade", 0);
+ dialog_clear();
+
+ if (msgYesNo("Before beginning a binary upgrade, please review the upgrade instructions,\n"
+ "which are located in the \"Install\" document under the main documentation\n"
+ "menu. Given that you have read these instructions and understand the risks\n"
+ "and precautions involved, are you sure that you want to proceed with\n"
+ "this upgrade?") != 0)
+ return DITEM_FAILURE;
+
+ if (!Dists) {
+ msgConfirm("First, you must select some distribution components. The upgrade procedure\n"
+ "will only upgrade the distributions you select in the next set of menus.");
+ if (!dmenuOpenSimple(&MenuDistributions, FALSE) || !Dists)
+ return DITEM_FAILURE;
+ }
+ else if (!(Dists & DIST_BASE)) { /* No base selected? Not much of an upgrade.. */
+ if (msgYesNo("You didn't select the base distribution as one of the distributons to load.\n"
+ "This one is pretty vital to a successful upgrade. Are you SURE you don't\n"
+ "want to select the base distribution? Chose No to bring up the Distributions\n"
+ "menu again.") != 0) {
+ if (!dmenuOpenSimple(&MenuDistributions, FALSE))
+ return DITEM_FAILURE;
+ }
+ }
+
+ /* Still?! OK! They must know what they're doing.. */
+ if (!(Dists & DIST_BASE))
+ extractingBin = FALSE;
+
+ if (RunningAsInit) {
+ Device **devs;
+ int i, cnt;
+ char *cp;
+
+ cp = variable_get(VAR_DISK);
+ devs = deviceFind(cp, DEVICE_TYPE_DISK);
+ cnt = deviceCount(devs);
+ if (!cnt) {
+ msgConfirm("No disks found! Please verify that your disk controller is being\n"
+ "properly probed at boot time. See the Hardware Guide on the\n"
+ "Documentation menu for clues on diagnosing this type of problem.");
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+ else {
+ /* Enable all the drives before we start */
+ for (i = 0; i < cnt; i++)
+ devs[i]->enabled = TRUE;
+ }
+
+ msgConfirm("OK. First, we're going to go to the disk label editor. In this editor\n"
+ "you will be expected to Mount any partitions you're interested in\n"
+ "upgrading. DO NOT set the Newfs flag to Y on anything in the label editor\n"
+ "unless you're absolutely sure you know what you're doing! In this\n"
+ "instance, you'll be using the label editor as little more than a fancy\n"
+ "screen-oriented partition mounting tool.\n\n"
+ "Once you're done in the label editor, press Q to return here for the next\n"
+ "step.");
+
+ if (DITEM_STATUS(diskLabelEditor(self)) == DITEM_FAILURE) {
+ msgConfirm("The disk label editor returned an error status. Upgrade operation\n"
+ "aborted.");
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+
+ /* Don't write out MBR info */
+ variable_set2(DISK_PARTITIONED, "written", 0);
+ if (DITEM_STATUS(diskLabelCommit(self)) == DITEM_FAILURE) {
+ msgConfirm("Not all file systems were properly mounted. Upgrade operation\n"
+ "aborted.");
+ variable_unset(DISK_PARTITIONED);
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+
+ msgNotify("Updating /stand on root filesystem");
+ (void)vsystem("find -x /stand | cpio %s -pdum /mnt", cpioVerbosity());
+
+ if (DITEM_STATUS(chroot("/mnt")) == DITEM_FAILURE) {
+ msgConfirm("Unable to chroot to /mnt - something is wrong with the\n"
+ "root partition or the way it's mounted if this doesn't work.");
+ variable_unset(DISK_PARTITIONED);
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+ chdir("/");
+ installEnvironment();
+ systemCreateHoloshell();
+ }
+
+ saved_etc[0] = '\0';
+
+ /* Don't allow sources to be upgraded unless if we have src already */
+ if (directory_exists("/usr/src/") && (Dists & DIST_SRC)) {
+ Dists &= ~DIST_SRC;
+ SrcDists = 0;
+ msgConfirm("Warning: /usr/src exists and sources were selected as upgrade\n"
+ "targets. Unfortunately, this is not the way to upgrade your\n"
+ "sources - please use CTM or CVSup or some other method which\n"
+ "handles ``deletion events'', unlike this particular feature.\n\n"
+ "Your existing /usr/src will not be affected by this upgrade.\n");
+ }
+
+ if (extractingBin) {
+ while (!*saved_etc) {
+ char *cp = msgGetInput("/var/tmp/etc", "Under which directory do you wish to save your current /etc?");
+
+ if (!cp || !*cp || Mkdir(cp)) {
+ if (msgYesNo("Directory was not specified, was invalid or user selected Cancel.\n\n"
+ "Doing an upgrade without first backing up your /etc directory is a very\n"
+ "bad idea! Do you want to go back and specify the save directory again?") != 0)
+ break;
+ }
+ else {
+ SAFE_STRCPY(saved_etc, cp);
+ }
+ }
+
+ if (saved_etc[0]) {
+ msgNotify("Preserving /etc directory..");
+ if (vsystem("tar -cBpf - -C /etc . | tar --unlink -xBpf - -C %s", saved_etc))
+ if (msgYesNo("Unable to backup your /etc into %s.\n"
+ "Do you want to continue anyway?", saved_etc) != 0)
+ return DITEM_FAILURE;
+ msgNotify("Preserving /root directory..");
+ vsystem("tar -cBpf - -C / root | tar --unlink -xBpf - -C %s", saved_etc);
+ }
+
+ msgNotify("chflags'ing old binaries - please wait.");
+ (void)vsystem("chflags -R noschg /bin /sbin /usr/sbin /usr/bin /usr/lib /usr/libexec /kernel*");
+
+ if (file_readable("/kernel")) {
+ msgNotify("Moving old kernel to /kernel.prev");
+ if (system("mv /kernel /kernel.prev")) {
+ if (!msgYesNo("Hmmm! I couldn't move the old kernel over! Do you want to\n"
+ "treat this as a big problem and abort the upgrade? Due to the\n"
+ "way that this upgrade process works, you will have to reboot\n"
+ "and start over from the beginning. Select Yes to reboot now"))
+ systemShutdown(1);
+ }
+ else
+ msgConfirm("NOTICE: Your old kernel is in /kernel.prev should this upgrade\n"
+ "fail for any reason and you need to boot your old kernel");
+ }
+ }
+
+media:
+ /* We do this very late, but we unfortunately need to back up /etc first */
+ if (!mediaVerify())
+ return DITEM_FAILURE;
+
+ if (!DEVICE_INIT(mediaDevice)) {
+ if (!msgYesNo("Couldn't initialize the media. Would you like\n"
+ "to adjust your media selection and try again?")) {
+ mediaDevice = NULL;
+ goto media;
+ }
+ else
+ return DITEM_FAILURE | DITEM_REDRAW | DITEM_RESTORE;
+ }
+
+ msgNotify("Beginning extraction of distributions..");
+ if (DITEM_STATUS(distExtractAll(self)) == DITEM_FAILURE) {
+ msgConfirm("Hmmmm. We couldn't even extract the base distribution. This upgrade\n"
+ "should be considered a failure and started from the beginning, sorry!\n"
+ "The system will reboot now.");
+ dialog_clear();
+ systemShutdown(1);
+ }
+ else if (Dists) {
+ if (!extractingBin || !(Dists & DIST_BASE)) {
+ msgNotify("The extraction process seems to have had some problems, but we got most\n"
+ "of the essentials. We'll treat this as a warning since it may have been\n"
+ "only non-essential distributions which failed to load.");
+ }
+ else {
+ msgConfirm("Hmmmm. We couldn't even extract the base distribution. This upgrade\n"
+ "should be considered a failure and started from the beginning, sorry!\n"
+ "The system will reboot now.");
+ dialog_clear();
+ systemShutdown(1);
+ }
+ }
+
+ if (extractingBin)
+ vsystem("disklabel -B `awk '$2~/\\/$/ {print substr($1, 6, 3)}' /etc/fstab`");
+ msgNotify("First stage of upgrade completed successfully!\n\n"
+ "Next comes stage 2, where we attempt to resurrect your /etc\n"
+ "directory!");
+
+ if (saved_etc && chdir(saved_etc)) {
+ msgConfirm("Unable to go to your saved /etc directory in %s?! Argh!\n"
+ "Something went seriously wrong! It's quite possible that\n"
+ "your former /etc is toast. I hope you didn't have any\n"
+ "important customizations you wanted to keep in there.. :(", saved_etc);
+ }
+ else {
+ /* Now try to resurrect the /etc files */
+ traverseHitlist(etc_files);
+ /* Resurrect the root dotfiles */
+ vsystem("tar -cBpf - root | tar -xBpf - -C / && rm -rf root");
+ }
+
+ msgConfirm("Upgrade completed! All of your old /etc files have been restored.\n"
+ "For your reference, the new /etc files are in /etc/upgrade/ in case\n"
+ "you wish to upgrade these files by hand (though that should not be\n"
+ "strictly necessary). If your root partition is specified in /etc/fstab\n"
+ "using the old \"compatibility\" slice, you may also wish to update it to\n"
+ "use a fully qualified slice name in order to avoid warnings on startup.\n\n"
+ "When you're ready to reboot into the new system, simply exit the installation.");
+ return DITEM_SUCCESS | DITEM_REDRAW | DITEM_RESTORE;
+}
+
+static int
+installUpgradeNonInteractive(dialogMenuItem *self)
+{
+ char *saved_etc;
+ Boolean extractingBin = TRUE;
+
+ variable_set2(SYSTEM_STATE, "upgrade", 0);
+
+ /* Make sure at least BIN is selected */
+ Dists |= DIST_BASE;
+
+ if (RunningAsInit) {
+ Device **devs;
+ int i, cnt;
+ char *cp;
+
+ cp = variable_get(VAR_DISK);
+ devs = deviceFind(cp, DEVICE_TYPE_DISK);
+ cnt = deviceCount(devs);
+ if (!cnt) {
+ msgConfirm("No disks found! Please verify that your disk controller is being\n"
+ "properly probed at boot time. See the Hardware Guide on the\n"
+ "Documentation menu for clues on diagnosing this type of problem.");
+ return DITEM_FAILURE;
+ }
+ else {
+ /* Enable all the drives befor we start */
+ for (i = 0; i < cnt; i++)
+ devs[i]->enabled = TRUE;
+ }
+
+ msgConfirm("OK. First, we're going to go to the disk label editor. In this editor\n"
+ "you will be expected to Mount any partitions you're interested in\n"
+ "upgrading. DO NOT set the Newfs flag to Y on anything in the label editor\n"
+ "unless you're absolutely sure you know what you're doing! In this\n"
+ "instance, you'll be using the label editor as little more than a fancy\n"
+ "screen-oriented partition mounting tool.\n\n"
+ "Once you're done in the label editor, press Q to return here for the next\n"
+ "step.");
+
+ if (DITEM_STATUS(diskLabelEditor(self)) == DITEM_FAILURE) {
+ msgConfirm("The disk label editor returned an error status. Upgrade operation\n"
+ "aborted.");
+ return DITEM_FAILURE;
+ }
+
+ /* Don't write out MBR info */
+ variable_set2(DISK_PARTITIONED, "written", 0);
+ if (DITEM_STATUS(diskLabelCommit(self)) == DITEM_FAILURE) {
+ msgConfirm("Not all file systems were properly mounted. Upgrade operation\n"
+ "aborted.");
+ variable_unset(DISK_PARTITIONED);
+ return DITEM_FAILURE;
+ }
+
+ if (extractingBin) {
+ msgNotify("chflags'ing old binaries - please wait.");
+ (void)vsystem("chflags -R noschg /mnt/");
+ }
+ msgNotify("Updating /stand on root filesystem");
+ (void)vsystem("find -x /stand | cpio %s -pdum /mnt", cpioVerbosity());
+
+ if (DITEM_STATUS(chroot("/mnt")) == DITEM_FAILURE) {
+ msgConfirm("Unable to chroot to /mnt - something is wrong with the\n"
+ "root partition or the way it's mounted if this doesn't work.");
+ variable_unset(DISK_PARTITIONED);
+ return DITEM_FAILURE;
+ }
+ chdir("/");
+ systemCreateHoloshell();
+ }
+
+ if (!mediaVerify() || !DEVICE_INIT(mediaDevice)) {
+ msgNotify("Upgrade: Couldn't initialize media.");
+ return DITEM_FAILURE;
+ }
+
+ saved_etc = "/var/tmp/etc";
+ Mkdir(saved_etc);
+ msgNotify("Preserving /etc directory..");
+ if (vsystem("tar -cpBf - -C /etc . | tar -xpBf - -C %s", saved_etc)) {
+ msgNotify("Unable to backup your /etc into %s.", saved_etc);
+ return DITEM_FAILURE;
+ }
+
+ if (file_readable("/kernel")) {
+ msgNotify("Moving old kernel to /kernel.prev");
+ if (!system("chflags noschg /kernel && mv /kernel /kernel.prev")) {
+ /* Give us a working kernel in case we crash and reboot */
+ system("cp /kernel.prev /kernel");
+ }
+ }
+
+ msgNotify("Beginning extraction of distributions..");
+ if (DITEM_STATUS(distExtractAll(self)) == DITEM_FAILURE) {
+ msgConfirm("Hmmmm. We couldn't even extract the base distribution. This upgrade\n"
+ "should be considered a failure and started from the beginning, sorry!\n"
+ "The system will reboot now.");
+ dialog_clear();
+ systemShutdown(1);
+ }
+ else if (Dists) {
+ if (!(Dists & DIST_BASE)) {
+ msgNotify("The extraction process seems to have had some problems, but we got most\n"
+ "of the essentials. We'll treat this as a warning since it may have been\n"
+ "only non-essential distributions which failed to upgrade.");
+ }
+ else {
+ msgConfirm("Hmmmm. We couldn't even extract the base distribution. This upgrade\n"
+ "should be considered a failure and started from the beginning, sorry!\n"
+ "The system will reboot now.");
+ dialog_clear();
+ systemShutdown(1);
+ }
+ }
+
+ msgNotify("First stage of upgrade completed successfully.");
+ if (vsystem("tar -cpBf - -C %s . | tar --unlink -xpBf - -C /etc", saved_etc)) {
+ msgNotify("Unable to resurrect your old /etc!");
+ return DITEM_FAILURE;
+ }
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
diff --git a/usr.sbin/sysinstall/keymap.c b/usr.sbin/sysinstall/keymap.c
new file mode 100644
index 0000000..3c53a00
--- /dev/null
+++ b/usr.sbin/sysinstall/keymap.c
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 1996 Joerg Wunsch
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ */
+
+#include "sysinstall.h"
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/kbio.h>
+
+struct keymapInfo {
+ const char *name;
+ const struct keymap *map;
+};
+
+#include "keymap.h"
+
+/*
+ * keymap.h is being automatically generated by the Makefile. It
+ * contains definitions for all desired keymaps. Note that since we
+ * don't support font loading nor screen mapping during installation,
+ * we simply don't care for any other keys than the ASCII subset.
+ *
+ * Therefore, if no keymap with the exact name has been found in the
+ * first pass, we make a second pass over the table looking just for
+ * the language name only.
+ */
+
+/*
+ * Return values:
+ *
+ * 0: OK
+ * -1: no appropriate keymap found
+ * -2: error installing map (other than ENXIO which means we're not on syscons)
+ */
+
+int
+loadKeymap(const char *lang)
+{
+ int passno, err;
+ char *llang;
+ size_t l;
+ struct keymapInfo *kip;
+
+ llang = strdup(lang);
+ if (llang == NULL)
+ abort();
+
+ for (passno = 0; passno < 2; passno++)
+ {
+ if (passno > 0)
+ {
+ /* make the match more fuzzy */
+ l = strspn(llang, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
+ llang[l] = '\0';
+ }
+
+ l = strlen(llang);
+
+ for (kip = keymapInfos; kip->name; kip++)
+ if (strncmp(kip->name, llang, l) == 0)
+ {
+ /* Yep, got it! */
+ err = ioctl(0, PIO_KEYMAP, kip->map);
+ free(llang);
+ return (err == -1 && errno != ENOTTY)? -2: 0;
+ }
+ }
+ free(llang);
+ return -1;
+}
diff --git a/usr.sbin/sysinstall/label.c b/usr.sbin/sysinstall/label.c
new file mode 100644
index 0000000..f40f3ae
--- /dev/null
+++ b/usr.sbin/sysinstall/label.c
@@ -0,0 +1,1481 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <ctype.h>
+#include <sys/disklabel.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+
+#define AUTO_HOME 0 /* do not create /home automatically */
+
+/*
+ * Everything to do with editing the contents of disk labels.
+ */
+
+/* A nice message we use a lot in the disklabel editor */
+#define MSG_NOT_APPLICABLE "That option is not applicable here"
+
+/* Where to start printing the freebsd slices */
+#define CHUNK_SLICE_START_ROW 2
+#define CHUNK_PART_START_ROW 11
+
+/* The smallest filesystem we're willing to create */
+#define FS_MIN_SIZE ONE_MEG
+
+/*
+ * Minimum partition sizes
+ */
+#ifdef __alpha__
+#define ROOT_MIN_SIZE 40
+#else
+#define ROOT_MIN_SIZE 30
+#endif
+#define SWAP_MIN_SIZE 32
+#define USR_MIN_SIZE 80
+#define VAR_MIN_SIZE 20
+#define TMP_MIN_SIZE 20
+#define HOME_MIN_SIZE 20
+
+/*
+ * Swap size limit for auto-partitioning (4G).
+ */
+#define SWAP_AUTO_LIMIT_SIZE 4096
+
+/*
+ * Default partition sizes. If we do not have sufficient disk space
+ * for this configuration we scale things relative to the NOM vs DEFAULT
+ * sizes. If the disk is larger then /home will get any remaining space.
+ */
+#define ROOT_DEFAULT_SIZE 128
+#define USR_DEFAULT_SIZE 3072
+#define VAR_DEFAULT_SIZE 256
+#define TMP_DEFAULT_SIZE 256
+#define HOME_DEFAULT_SIZE USR_DEFAULT_SIZE
+
+/*
+ * Nominal partition sizes. These are used to scale the default sizes down
+ * when we have insufficient disk space. If this isn't sufficient we scale
+ * down using the MIN sizes instead.
+ */
+#define ROOT_NOMINAL_SIZE 128
+#define USR_NOMINAL_SIZE 512
+#define VAR_NOMINAL_SIZE 64
+#define TMP_NOMINAL_SIZE 64
+#define HOME_NOMINAL_SIZE USR_NOMINAL_SIZE
+
+/* The bottom-most row we're allowed to scribble on */
+#define CHUNK_ROW_MAX 16
+
+
+/* All the chunks currently displayed on the screen */
+static struct {
+ struct chunk *c;
+ PartType type;
+} label_chunk_info[MAX_CHUNKS + 1];
+static int here;
+
+/*** with this value we try to track the most recently added label ***/
+static int label_focus = 0, pslice_focus = 0;
+
+static int diskLabel(Device *dev);
+static int diskLabelNonInteractive(Device *dev);
+static char *try_auto_label(Device **devs, Device *dev, int perc, int *req);
+
+static int
+labelHook(dialogMenuItem *selected)
+{
+ Device **devs = NULL;
+
+ devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
+ if (!devs) {
+ msgConfirm("Unable to find disk %s!", selected->prompt);
+ return DITEM_FAILURE;
+ }
+ /* Toggle enabled status? */
+ if (!devs[0]->enabled) {
+ devs[0]->enabled = TRUE;
+ diskLabel(devs[0]);
+ }
+ else
+ devs[0]->enabled = FALSE;
+ return DITEM_SUCCESS;
+}
+
+static int
+labelCheck(dialogMenuItem *selected)
+{
+ Device **devs = NULL;
+
+ devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
+ if (!devs || devs[0]->enabled == FALSE)
+ return FALSE;
+ return TRUE;
+}
+
+int
+diskLabelEditor(dialogMenuItem *self)
+{
+ DMenu *menu;
+ Device **devs;
+ int i, cnt;
+
+ i = 0;
+ cnt = diskGetSelectCount(&devs);
+ if (cnt == -1) {
+ msgConfirm("No disks found! Please verify that your disk controller is being\n"
+ "properly probed at boot time. See the Hardware Guide on the\n"
+ "Documentation menu for clues on diagnosing this type of problem.");
+ return DITEM_FAILURE;
+ }
+ else if (cnt) {
+ /* Some are already selected */
+ if (variable_get(VAR_NONINTERACTIVE) &&
+ !variable_get(VAR_DISKINTERACTIVE))
+ i = diskLabelNonInteractive(NULL);
+ else
+ i = diskLabel(NULL);
+ }
+ else {
+ /* No disks are selected, fall-back case now */
+ cnt = deviceCount(devs);
+ if (cnt == 1) {
+ devs[0]->enabled = TRUE;
+ if (variable_get(VAR_NONINTERACTIVE) &&
+ !variable_get(VAR_DISKINTERACTIVE))
+ i = diskLabelNonInteractive(devs[0]);
+ else
+ i = diskLabel(devs[0]);
+ }
+ else {
+ menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, labelHook, labelCheck);
+ if (!menu) {
+ msgConfirm("No devices suitable for installation found!\n\n"
+ "Please verify that your disk controller (and attached drives)\n"
+ "were detected properly. This can be done by pressing the\n"
+ "[Scroll Lock] key and using the Arrow keys to move back to\n"
+ "the boot messages. Press [Scroll Lock] again to return.");
+ i = DITEM_FAILURE;
+ }
+ else {
+ i = dmenuOpenSimple(menu, FALSE) ? DITEM_SUCCESS : DITEM_FAILURE;
+ free(menu);
+ }
+ }
+ }
+ if (DITEM_STATUS(i) != DITEM_FAILURE) {
+ if (variable_cmp(DISK_LABELLED, "written"))
+ variable_set2(DISK_LABELLED, "yes", 0);
+ }
+ return i;
+}
+
+int
+diskLabelCommit(dialogMenuItem *self)
+{
+ char *cp;
+ int i;
+
+ /* Already done? */
+ if ((cp = variable_get(DISK_LABELLED)) && strcmp(cp, "yes"))
+ i = DITEM_SUCCESS;
+ else if (!cp) {
+ msgConfirm("You must assign disk labels before this option can be used.");
+ i = DITEM_FAILURE;
+ }
+ /* The routine will guard against redundant writes, just as this one does */
+ else if (DITEM_STATUS(diskPartitionWrite(self)) != DITEM_SUCCESS)
+ i = DITEM_FAILURE;
+ else if (DITEM_STATUS(installFilesystems(self)) != DITEM_SUCCESS)
+ i = DITEM_FAILURE;
+ else {
+ msgInfo("All filesystem information written successfully.");
+ variable_set2(DISK_LABELLED, "written", 0);
+ i = DITEM_SUCCESS;
+ }
+ return i;
+}
+
+/* See if we're already using a desired partition name */
+static Boolean
+check_conflict(char *name)
+{
+ int i;
+
+ for (i = 0; label_chunk_info[i].c; i++)
+ if ((label_chunk_info[i].type == PART_FILESYSTEM || label_chunk_info[i].type == PART_FAT)
+ && label_chunk_info[i].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 ");
+ strcat(ret->newfs_cmd, variable_get(VAR_NEWFS_ARGS));
+ ret->newfs = newfs;
+ ret->soft = strcmp(mpoint, "/") ? 1 : 0;
+ 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;
+ val = msgGetInput(tmp ? tmp->mountpoint : NULL, "Please specify a mount point for the partition");
+ if (!val || !*val) {
+ if (!old)
+ return NULL;
+ else {
+ free(old->private_data);
+ old->private_data = NULL;
+ }
+ return NULL;
+ }
+
+ /* Is it just the same value? */
+ if (tmp && !strcmp(tmp->mountpoint, val))
+ return NULL;
+
+ /* Did we use it already? */
+ if (check_conflict(val)) {
+ msgConfirm("You already have a mount point for %s assigned!", val);
+ return NULL;
+ }
+
+ /* Is it bogus? */
+ if (*val != '/') {
+ msgConfirm("Mount point must start with a / character");
+ return NULL;
+ }
+
+ /* Is it going to be mounted on root? */
+ if (!strcmp(val, "/")) {
+ if (old)
+ old->flags |= CHUNK_IS_ROOT;
+ }
+ else if (old)
+ old->flags &= ~CHUNK_IS_ROOT;
+
+ safe_free(tmp);
+ val = string_skipwhite(string_prune(val));
+ 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.",
+ };
+ WINDOW *w = savescr();
+
+ i = dialog_menu("Please choose a partition type",
+ "If you want to use this partition for swap space, select Swap.\n"
+ "If you want to put a filesystem on it, choose FS.",
+ -1, -1, 2, 2, fs_types, selection, NULL, NULL);
+ restorescr(w);
+ 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 9
+
+#define PART_PART_COL 0
+#define PART_MOUNT_COL 10
+#define PART_SIZE_COL (PART_MOUNT_COL + MAX_MOUNT_NAME + 3)
+#define PART_NEWFS_COL (PART_SIZE_COL + 8)
+#define PART_OFF 38
+
+#define TOTAL_AVAIL_LINES (10)
+#define PSLICE_SHOWABLE (4)
+
+
+/* stick this all up on the screen */
+static void
+print_label_chunks(void)
+{
+ int i, j, srow, prow, pcol;
+ 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) + 3, "Size");
+ mvaddstr(ChunkPartStartRow - 1, PART_SIZE_COL + (i * PART_OFF) + 3, "----");
+
+ mvaddstr(ChunkPartStartRow - 2, PART_NEWFS_COL + (i * PART_OFF), "Newfs");
+ mvaddstr(ChunkPartStartRow - 1, PART_NEWFS_COL + (i * PART_OFF), "-----");
+ }
+ srow = CHUNK_SLICE_START_ROW;
+ prow = 0;
+ pcol = 0;
+
+ /*** these variables indicate that the focused item is shown currently ***/
+ label_focus_found = 0;
+ pslice_focus_found = 0;
+
+ label_count = 0;
+ pslice_count = 0;
+ mvprintw(CHUNK_SLICE_START_ROW - 1, 0, " ");
+ mvprintw(CHUNK_SLICE_START_ROW + pslice_max, 0, " ");
+
+ ChunkWin = newwin(CHUNK_ROW_MAX - ChunkPartStartRow, 76, ChunkPartStartRow, 0);
+
+ wclear(ChunkWin);
+ /*** wrefresh(ChunkWin); ***/
+
+ for (i = 0; label_chunk_info[i].c; i++) {
+ /* Is it a slice entry displayed at the top? */
+ if (label_chunk_info[i].type == PART_SLICE) {
+ /*** This causes the new pslice to replace the previous display ***/
+ /*** focus must remain on the most recently active pslice ***/
+ if (pslice_count == pslice_max) {
+ if (pslice_focus_found) {
+ /*** This is where we can mark the more following ***/
+ attrset(A_BOLD);
+ mvprintw(CHUNK_SLICE_START_ROW + pslice_max, 0, "***MORE***");
+ attrset(A_NORMAL);
+ continue;
+ }
+ else {
+ /*** this is where we set the more previous ***/
+ attrset(A_BOLD);
+ mvprintw(CHUNK_SLICE_START_ROW - 1, 0, "***MORE***");
+ attrset(A_NORMAL);
+ pslice_count = 0;
+ srow = CHUNK_SLICE_START_ROW;
+ }
+ }
+
+ sz = space_free(label_chunk_info[i].c);
+ if (i == here)
+ attrset(ATTR_SELECTED);
+ if (i == pslice_focus)
+ pslice_focus_found = -1;
+
+ 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[10];
+
+ /*
+ * 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)
+ strcpy(newfs, "DOS");
+ else if (label_chunk_info[i].c->private_data && label_chunk_info[i].type == PART_FILESYSTEM) {
+ strcpy(newfs, "UFS");
+ strcat(newfs,
+ ((PartInfo *)label_chunk_info[i].c->private_data)->soft ?
+ "+S" : " ");
+ strcat(newfs,
+ ((PartInfo *)label_chunk_info[i].c->private_data)->newfs ?
+ " Y" : " N");
+ }
+ else if (label_chunk_info[i].type == PART_SWAP)
+ strcpy(newfs, "SWAP");
+ else
+ strcpy(newfs, "*");
+ for (j = 0; j < MAX_MOUNT_NAME && mountpoint[j]; j++)
+ onestr[PART_MOUNT_COL + j] = mountpoint[j];
+ snprintf(num, 10, "%5ldMB", 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 expensively padding this string ***/
+ while (strlen(onestr) < 37)
+ strcat(onestr, " ");
+
+ mvwaddstr(ChunkWin, prow, pcol, onestr);
+ wattrset(ChunkWin, A_NORMAL);
+ move(0, 0);
+ ++prow;
+ ++label_count;
+ }
+ }
+
+ /*** this will erase all the extra stuff ***/
+ memset(clrmsg, ' ', 37);
+ clrmsg[37] = '\0';
+
+ while (pslice_count < pslice_max) {
+ mvprintw(srow++, 0, clrmsg);
+ clrtoeol();
+ ++pslice_count;
+ }
+ while (label_count < (2 * (label_max - 1))) {
+ mvwaddstr(ChunkWin, prow++, pcol, clrmsg);
+ ++label_count;
+ if (prow == (label_max - 1)) {
+ prow = 0;
+ pcol = PART_OFF;
+ }
+ }
+ refresh();
+ wrefresh(ChunkWin);
+}
+
+static void
+print_command_summary(void)
+{
+ mvprintw(17, 0, "The following commands are valid here (upper or lower case):");
+ mvprintw(18, 0, "C = Create D = Delete M = Mount pt.");
+ if (!RunningAsInit)
+ mvprintw(18, 47, "W = Write");
+ mvprintw(19, 0, "N = Newfs Opts Q = Finish S = Toggle SoftUpdates");
+ mvprintw(20, 0, "T = Toggle Newfs U = Undo A = Auto Defaults R = Delete+Merge");
+ mvprintw(22, 0, "Use F1 or ? to get more help, arrow keys to select.");
+ move(0, 0);
+}
+
+static void
+clear_wins(void)
+{
+ extern void print_label_chunks();
+ clear();
+ print_label_chunks();
+}
+
+#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
+ WINDOW *w = savescr();
+
+ label_focus = 0;
+ pslice_focus = 0;
+ here = 0;
+
+ devs = deviceFind(NULL, DEVICE_TYPE_DISK);
+ if (!devs) {
+ msgConfirm("No disks found!");
+ restorescr(w);
+ return DITEM_FAILURE;
+ }
+ labeling = TRUE;
+ keypad(stdscr, TRUE);
+#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;
+ int rflags = DELCHUNK_NORMAL;
+
+ print_label_chunks();
+ print_command_summary();
+ if (msg) {
+ attrset(title_attr); mvprintw(23, 0, msg); attrset(A_NORMAL);
+ clrtoeol();
+ beep();
+ msg = NULL;
+ }
+ else {
+ move(23, 0);
+ clrtoeol();
+ }
+
+ refresh();
+ key = getch();
+ switch (toupper(key)) {
+ int i;
+ static char _msg[40];
+
+ case '\014': /* ^L */
+ clear_wins();
+ break;
+
+ case '\020': /* ^P */
+ case KEY_UP:
+ case '-':
+ if (here != 0)
+ --here;
+ else
+ while (label_chunk_info[here + 1].c)
+ ++here;
+ break;
+
+ case '\016': /* ^N */
+ case KEY_DOWN:
+ case '+':
+ case '\r':
+ case '\n':
+ if (label_chunk_info[here + 1].c)
+ ++here;
+ else
+ here = 0;
+ break;
+
+ case KEY_HOME:
+ here = 0;
+ break;
+
+ case KEY_END:
+ while (label_chunk_info[here + 1].c)
+ ++here;
+ break;
+
+ case KEY_F(1):
+ case '?':
+ systemDisplayHelp("partition");
+ clear_wins();
+ break;
+
+ case 'A':
+ if (label_chunk_info[here].type != PART_SLICE) {
+ msg = "You can only do this in a disk slice (at top of screen)";
+ break;
+ }
+ /*
+ * Generate standard partitions automatically. If we do not
+ * have sufficient space we attempt to scale-down the size
+ * of the partitions within certain bounds.
+ */
+ {
+ int perc;
+ int req = 0;
+
+ for (perc = 100; perc > 0; perc -= 5) {
+ req = 0; /* reset for each loop */
+ if ((msg = try_auto_label(devs, dev, perc, &req)) == NULL)
+ break;
+ }
+ if (msg) {
+ if (req) {
+ msgConfirm(msg);
+ clear_wins();
+ msg = NULL;
+ }
+ }
+ }
+ break;
+
+ case 'C':
+ if (label_chunk_info[here].type != PART_SLICE) {
+ msg = "You can only do this in a master partition (see top of screen)";
+ break;
+ }
+ sz = space_free(label_chunk_info[here].c);
+ if (sz <= FS_MIN_SIZE) {
+ msg = "Not enough space to create an additional FreeBSD partition";
+ break;
+ }
+ else {
+ char *val;
+ int size;
+ struct chunk *tmp;
+ char osize[80];
+ u_long flags = 0;
+
+ sprintf(osize, "%d", sz);
+ val = msgGetInput(osize,
+ "Please specify the partition size in blocks or append a trailing G for\n"
+ "gigabytes, M for megabytes, or C for cylinders.\n"
+ "%d blocks (%dMB) are free.",
+ sz, sz / ONE_MEG);
+ if (!val || (size = strtol(val, &cp, 0)) <= 0) {
+ clear_wins();
+ break;
+ }
+
+ if (*cp) {
+ if (toupper(*cp) == 'M')
+ size *= ONE_MEG;
+ else if (toupper(*cp) == 'G')
+ size *= ONE_GIG;
+ 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) && (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;
+ }
+
+#ifdef __alpha__
+ /*
+ * SRM requires that the root partition is at the
+ * begining of the disk and cannot boot otherwise.
+ * Warn Alpha users if they are about to shoot themselves in
+ * the foot in this way.
+ *
+ * Since partitions may not start precisely at offset 0 we
+ * check for a "close to 0" instead. :-(
+ */
+ if ((flags & CHUNK_IS_ROOT) && (tmp->offset > 1024)) {
+ msgConfirm("Your root partition `a' does not seem to be the first\n"
+ "partition. The Alpha's firmware can only boot from the\n"
+ "first partition. So it is unlikely that your current\n"
+ "disk layout will be bootable boot after installation.\n"
+ "\n"
+ "Please allocate the root partition before allocating\n"
+ "any others.\n");
+ }
+#endif /* alpha */
+
+ 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 (variable_cmp(DISK_LABELLED, "written"))
+ variable_set2(DISK_LABELLED, "yes", 0);
+ record_label_chunks(devs, dev);
+ clear_wins();
+ /* This is where we assign focus to new label so it shows. */
+ {
+ int i;
+ label_focus = -1;
+ for (i = 0; label_chunk_info[i].c; ++i) {
+ if (label_chunk_info[i].c == tmp) {
+ label_focus = i;
+ break;
+ }
+ }
+ if (label_focus == -1)
+ label_focus = i - 1;
+ }
+ }
+ break;
+
+ case KEY_DC:
+ case 'R': /* recover space (delete w/ recover) */
+ /*
+ * Delete the partition w/ space recovery.
+ */
+ rflags = DELCHUNK_RECOVER;
+ /* fall through */
+ case 'D': /* delete */
+ if (label_chunk_info[here].type == PART_SLICE) {
+ msg = MSG_NOT_APPLICABLE;
+ break;
+ }
+ else if (label_chunk_info[here].type == PART_FAT) {
+ msg = "Use the Disk Partition Editor to delete DOS partitions";
+ break;
+ }
+ Delete_Chunk2(label_chunk_info[here].c->disk, label_chunk_info[here].c, rflags);
+ if (variable_cmp(DISK_LABELLED, "written"))
+ variable_set2(DISK_LABELLED, "yes", 0);
+ record_label_chunks(devs, dev);
+ break;
+
+ case 'M': /* mount */
+ switch(label_chunk_info[here].type) {
+ case PART_SLICE:
+ msg = MSG_NOT_APPLICABLE;
+ break;
+
+ case PART_SWAP:
+ msg = "You don't need to specify a mountpoint for a swap partition.";
+ break;
+
+ case PART_FAT:
+ case PART_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 (variable_cmp(DISK_LABELLED, "written"))
+ variable_set2(DISK_LABELLED, "yes", 0);
+ record_label_chunks(devs, dev);
+ clear_wins();
+ break;
+
+ default:
+ msgFatal("Bogus partition under cursor???");
+ break;
+ }
+ break;
+
+ case 'N': /* Set newfs options */
+ if (label_chunk_info[here].c->private_data &&
+ ((PartInfo *)label_chunk_info[here].c->private_data)->newfs)
+ getNewfsCmd(label_chunk_info[here].c->private_data);
+ else
+ msg = MSG_NOT_APPLICABLE;
+ clear_wins();
+ break;
+
+ case 'S': /* Toggle soft updates flag */
+ if (label_chunk_info[here].type == PART_FILESYSTEM) {
+ PartInfo *pi = ((PartInfo *)label_chunk_info[here].c->private_data);
+ if (pi)
+ pi->soft = !pi->soft;
+ else
+ msg = MSG_NOT_APPLICABLE;
+ }
+ else
+ msg = MSG_NOT_APPLICABLE;
+ break;
+
+ case 'T': /* Toggle newfs state */
+ if ((label_chunk_info[here].type == PART_FILESYSTEM) &&
+ (label_chunk_info[here].c->private_data)) {
+ PartInfo *pi = ((PartInfo *)label_chunk_info[here].c->private_data);
+ if (!pi->newfs)
+ label_chunk_info[here].c->flags |= CHUNK_NEWFS;
+ else
+ label_chunk_info[here].c->flags &= ~CHUNK_NEWFS;
+
+ label_chunk_info[here].c->private_data =
+ new_part(pi ? pi->mountpoint : NULL, pi ? !pi->newfs : TRUE, label_chunk_info[here].c->size);
+ if (pi && pi->soft)
+ ((PartInfo *)label_chunk_info[here].c->private_data)->soft = 1;
+ safe_free(pi);
+ label_chunk_info[here].c->private_free = safe_free;
+ if (variable_cmp(DISK_LABELLED, "written"))
+ variable_set2(DISK_LABELLED, "yes", 0);
+ }
+ else
+ msg = MSG_NOT_APPLICABLE;
+ break;
+
+ case 'U':
+ clear();
+ if (!variable_cmp(DISK_LABELLED, "written")) {
+ msgConfirm("You've already written out your changes -\n"
+ "it's too late to undo!");
+ }
+ else if (!msgNoYes("Are you SURE you want to Undo everything?")) {
+ variable_unset(DISK_PARTITIONED);
+ variable_unset(DISK_LABELLED);
+ for (i = 0; devs[i]; i++) {
+ Disk *d;
+
+ if (!devs[i]->enabled)
+ continue;
+ else if ((d = Open_Disk(devs[i]->name)) != NULL) {
+ Free_Disk(devs[i]->private);
+ devs[i]->private = d;
+ diskPartition(devs[i]);
+ }
+ }
+ record_label_chunks(devs, dev);
+ }
+ clear_wins();
+ break;
+
+ case 'W':
+ if (!variable_cmp(DISK_LABELLED, "written")) {
+ msgConfirm("You've already written out your changes - if you\n"
+ "wish to overwrite them, you'll have to restart\n"
+ "sysinstall first.");
+ }
+ else if (!msgNoYes("WARNING: This should only be used when modifying an EXISTING\n"
+ "installation. If you are installing FreeBSD for the first time\n"
+ "then you should simply type Q when you're finished here and your\n"
+ "changes will be committed in one batch automatically at the end of\n"
+ "these questions.\n\n"
+ "Are you absolutely sure you want to do this now?")) {
+ variable_set2(DISK_LABELLED, "yes", 0);
+ diskLabelCommit(NULL);
+ }
+ clear_wins();
+ break;
+
+ case '|':
+ if (!msgNoYes("Are you sure you want to go into Wizard mode?\n\n"
+ "This is an entirely undocumented feature which you are not\n"
+ "expected to understand!")) {
+ int i;
+ Device **devs;
+
+ dialog_clear();
+ end_dialog();
+ DialogActive = FALSE;
+ devs = deviceFind(NULL, DEVICE_TYPE_DISK);
+ if (!devs) {
+ msgConfirm("Can't find any disk devices!");
+ break;
+ }
+ for (i = 0; devs[i] && ((Disk *)devs[i]->private); i++) {
+ if (devs[i]->enabled)
+ slice_wizard(((Disk *)devs[i]->private));
+ }
+ if (variable_cmp(DISK_LABELLED, "written"))
+ variable_set2(DISK_LABELLED, "yes", 0);
+ DialogActive = TRUE;
+ record_label_chunks(devs, dev);
+ clear_wins();
+ }
+ else
+ msg = "A most prudent choice!";
+ break;
+
+ case '\033': /* ESC */
+ case 'Q':
+ labeling = FALSE;
+ break;
+
+ default:
+ beep();
+ sprintf(_msg, "Invalid key %d - Type F1 or ? for help", key);
+ msg = _msg;
+ break;
+ }
+ if (label_chunk_info[here].type == PART_SLICE)
+ pslice_focus = here;
+ else
+ label_focus = here;
+ }
+ restorescr(w);
+ return DITEM_SUCCESS;
+}
+
+static __inline int
+requested_part_size(char *varName, int nom, int def, int perc)
+{
+ char *cp;
+ int sz;
+
+ if ((cp = variable_get(varName)) != NULL)
+ sz = atoi(cp);
+ else
+ sz = nom + (def - nom) * perc / 100;
+ return(sz * ONE_MEG);
+}
+
+/*
+ * Attempt to auto-label the disk. 'perc' (0-100) scales
+ * the size of the various partitions within appropriate
+ * bounds (NOMINAL through DEFAULT sizes). The procedure
+ * succeeds of NULL is returned. A non-null return message
+ * is either a failure-status message (*req == 0), or
+ * a confirmation requestor (*req == 1). *req is 0 on
+ * entry to this call.
+ *
+ * We autolabel the following partitions: /, swap, /var, /tmp, /usr,
+ * and /home. /home receives any extra left over disk space.
+ */
+static char *
+try_auto_label(Device **devs, Device *dev, int perc, int *req)
+{
+ int sz;
+ struct chunk *root_chunk = NULL;
+ struct chunk *swap_chunk = NULL;
+ struct chunk *usr_chunk = NULL;
+ struct chunk *var_chunk = NULL;
+ struct chunk *tmp_chunk = NULL;
+ struct chunk *home_chunk = NULL;
+ int mib[2];
+ unsigned int physmem;
+ size_t size;
+ Chunk *rootdev, *swapdev, *usrdev, *vardev;
+ Chunk *tmpdev, *homedev;
+ char *msg = NULL;
+
+ sz = space_free(label_chunk_info[here].c);
+ if (sz <= FS_MIN_SIZE)
+ return("Not enough free space to create a new partition in the slice");
+
+ (void)checkLabels(FALSE, &rootdev, &swapdev, &usrdev,
+ &vardev, &tmpdev, &homedev);
+ if (!rootdev) {
+ sz = requested_part_size(VAR_ROOT_SIZE, ROOT_NOMINAL_SIZE, ROOT_DEFAULT_SIZE, perc);
+
+ root_chunk = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
+ label_chunk_info[here].c, sz, part,
+ FS_BSDFFS, CHUNK_IS_ROOT | CHUNK_AUTO_SIZE);
+ if (!root_chunk) {
+ *req = 1;
+ msg = "Unable to create the root partition. Too big?";
+ goto done;
+ }
+ root_chunk->private_data = new_part("/", TRUE, root_chunk->size);
+ root_chunk->private_free = safe_free;
+ root_chunk->flags |= CHUNK_NEWFS;
+ record_label_chunks(devs, dev);
+ }
+ if (!swapdev) {
+ sz = requested_part_size(VAR_SWAP_SIZE, 0, 0, perc);
+ if (sz == 0) {
+ int nom;
+ int def;
+
+ mib[0] = CTL_HW;
+ mib[1] = HW_PHYSMEM;
+ size = sizeof physmem;
+ sysctl(mib, 2, &physmem, &size, (void *)0, (size_t)0);
+ def = 2 * (int)(physmem / 512);
+ if (def < SWAP_MIN_SIZE * ONE_MEG)
+ def = SWAP_MIN_SIZE * ONE_MEG;
+ if (def > SWAP_AUTO_LIMIT_SIZE * ONE_MEG)
+ def = SWAP_AUTO_LIMIT_SIZE * ONE_MEG;
+ nom = (int)(physmem / 512) / 2;
+ sz = nom + (def - nom) * perc / 100;
+ }
+ swap_chunk = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
+ label_chunk_info[here].c, sz, part,
+ FS_SWAP, CHUNK_AUTO_SIZE);
+ if (!swap_chunk) {
+ *req = 1;
+ msg = "Unable to create the swap partition. Too big?";
+ goto done;
+ }
+ swap_chunk->private_data = 0;
+ swap_chunk->private_free = safe_free;
+ record_label_chunks(devs, dev);
+ }
+ if (!vardev) {
+ sz = requested_part_size(VAR_VAR_SIZE, VAR_NOMINAL_SIZE, VAR_DEFAULT_SIZE, perc);
+
+ var_chunk = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
+ label_chunk_info[here].c, sz, part,
+ FS_BSDFFS, CHUNK_AUTO_SIZE);
+ if (!var_chunk) {
+ *req = 1;
+ msg = "Not enough free space for /var - you will need to\n"
+ "partition your disk manually with a custom install!";
+ goto done;
+ }
+ var_chunk->private_data = new_part("/var", TRUE, var_chunk->size);
+ var_chunk->private_free = safe_free;
+ var_chunk->flags |= CHUNK_NEWFS;
+ record_label_chunks(devs, dev);
+ }
+ if (!tmpdev && !variable_get(VAR_NO_TMP)) {
+ sz = requested_part_size(VAR_TMP_SIZE, TMP_NOMINAL_SIZE, TMP_DEFAULT_SIZE, perc);
+
+ tmp_chunk = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
+ label_chunk_info[here].c, sz, part,
+ FS_BSDFFS, CHUNK_AUTO_SIZE);
+ if (!tmp_chunk) {
+ *req = 1;
+ msg = "Not enough free space for /tmp - you will need to\n"
+ "partition your disk manually with a custom install!";
+ goto done;
+ }
+ tmp_chunk->private_data = new_part("/tmp", TRUE, tmp_chunk->size);
+ tmp_chunk->private_free = safe_free;
+ tmp_chunk->flags |= CHUNK_NEWFS;
+ record_label_chunks(devs, dev);
+ }
+ if (!usrdev && !variable_get(VAR_NO_USR)) {
+ sz = requested_part_size(VAR_USR_SIZE, USR_NOMINAL_SIZE, USR_DEFAULT_SIZE, perc);
+#if AUTO_HOME == 0
+ sz = space_free(label_chunk_info[here].c);
+#endif
+ if (sz) {
+ if (sz < (USR_MIN_SIZE * ONE_MEG)) {
+ *req = 1;
+ msg = "Not enough free space for /usr - you will need to\n"
+ "partition your disk manually with a custom install!";
+ }
+
+ usr_chunk = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
+ label_chunk_info[here].c, sz, part,
+ FS_BSDFFS, CHUNK_AUTO_SIZE);
+ if (!usr_chunk) {
+ msg = "Unable to create the /usr partition. Not enough space?\n"
+ "You will need to partition your disk manually with a custom install!";
+ goto done;
+ }
+ usr_chunk->private_data = new_part("/usr", TRUE, usr_chunk->size);
+ usr_chunk->private_free = safe_free;
+ usr_chunk->flags |= CHUNK_NEWFS;
+ record_label_chunks(devs, dev);
+ }
+ }
+#if AUTO_HOME == 1
+ if (!homedev && !variable_get(VAR_NO_HOME)) {
+ sz = requested_part_size(VAR_HOME_SIZE, HOME_NOMINAL_SIZE, HOME_DEFAULT_SIZE, perc);
+ if (sz < space_free(label_chunk_info[here].c))
+ sz = space_free(label_chunk_info[here].c);
+ if (sz) {
+ if (sz < (HOME_MIN_SIZE * ONE_MEG)) {
+ *req = 1;
+ msg = "Not enough free space for /home - you will need to\n"
+ "partition your disk manually with a custom install!";
+ goto done;
+ }
+
+ home_chunk = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
+ label_chunk_info[here].c, sz, part,
+ FS_BSDFFS, CHUNK_AUTO_SIZE);
+ if (!home_chunk) {
+ msg = "Unable to create the /home partition. Not enough space?\n"
+ "You will need to partition your disk manually with a custom install!";
+ goto done;
+ }
+ home_chunk->private_data = new_part("/home", TRUE, home_chunk->size);
+ home_chunk->private_free = safe_free;
+ home_chunk->flags |= CHUNK_NEWFS;
+ record_label_chunks(devs, dev);
+ }
+ }
+#endif
+
+ /* At this point, we're reasonably "labelled" */
+ if (variable_cmp(DISK_LABELLED, "written"))
+ variable_set2(DISK_LABELLED, "yes", 0);
+
+done:
+ if (msg) {
+ if (root_chunk)
+ Delete_Chunk(root_chunk->disk, root_chunk);
+ if (swap_chunk)
+ Delete_Chunk(swap_chunk->disk, swap_chunk);
+ if (var_chunk)
+ Delete_Chunk(var_chunk->disk, var_chunk);
+ if (tmp_chunk)
+ Delete_Chunk(tmp_chunk->disk, tmp_chunk);
+ if (usr_chunk)
+ Delete_Chunk(usr_chunk->disk, usr_chunk);
+ if (home_chunk)
+ Delete_Chunk(home_chunk->disk, home_chunk);
+ record_label_chunks(devs, dev);
+ }
+ return(msg);
+}
+
+static int
+diskLabelNonInteractive(Device *dev)
+{
+ char *cp;
+ PartType type;
+ PartInfo *p;
+ u_long flags = 0;
+ int i, status;
+ Device **devs;
+ Disk *d;
+
+ status = DITEM_SUCCESS;
+ cp = variable_get(VAR_DISK);
+ if (!cp) {
+ msgConfirm("diskLabel: No disk selected - can't label automatically.");
+ return DITEM_FAILURE;
+ }
+ devs = deviceFind(cp, DEVICE_TYPE_DISK);
+ if (!devs) {
+ msgConfirm("diskLabel: No disk device %s found!", cp);
+ return DITEM_FAILURE;
+ }
+ if (dev)
+ d = dev->private;
+ else
+ d = devs[0]->private;
+#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, soft = 0;
+ char typ[10], mpoint[50];
+
+ if (sscanf(cp, "%s %d %s %d", typ, &sz, mpoint, &soft) < 3) {
+ msgConfirm("For slice entry %s, got an invalid detail entry of: %s", c1->name, cp);
+ status = DITEM_FAILURE;
+ 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;
+ ((PartInfo *)tmp->private_data)->soft = soft;
+ 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) {
+ 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..8300173
--- /dev/null
+++ b/usr.sbin/sysinstall/list.h
@@ -0,0 +1,60 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated for what's essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1997 FreeBSD, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY PAUL TRAINA ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PAUL TRAINA OR HIS KILLER RATS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/* The structure */
+typedef struct _qelement {
+ struct _qelement *q_forw;
+ struct _qelement *q_back;
+} qelement;
+
+#define INITQUE(Xhead) { \
+ (Xhead).q_forw = &(Xhead); \
+ (Xhead).q_back = &(Xhead); \
+}
+
+#define EMPTYQUE(Xhead) \
+ ((Xhead).q_forw == &(Xhead))
+
+#define INSQUEUE(elem, pred) { \
+ register qelement *Xe = (qelement *) (elem); \
+ register qelement *Xp = (qelement *) (pred); \
+ Xp->q_forw = (Xe->q_forw = (Xe->q_back = Xp)->q_forw)->q_back = Xe; \
+}
+
+#define REMQUE(elem) { \
+ register qelement *Xe = (qelement *) (elem); \
+ (Xe->q_back->q_forw = Xe->q_forw)->q_back = Xe->q_back; \
+}
diff --git a/usr.sbin/sysinstall/main.c b/usr.sbin/sysinstall/main.c
new file mode 100644
index 0000000..144a617
--- /dev/null
+++ b/usr.sbin/sysinstall/main.c
@@ -0,0 +1,179 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated for what's essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <sys/signal.h>
+#include <sys/fcntl.h>
+
+const char *StartName; /* Initial contents of argv[0] */
+
+static void
+screech(int sig)
+{
+ msgDebug("\007Signal %d caught! That's bad!\n", sig);
+ longjmp(BailOut, sig);
+}
+
+int
+main(int argc, char **argv)
+{
+ int choice, scroll, curr, max, status;
+
+ /* Record name to be able to restart */
+ StartName = argv[0];
+
+ /* Catch fatal signals and complain about them if running as init */
+ if (getpid() == 1) {
+ signal(SIGBUS, screech);
+ signal(SIGSEGV, screech);
+ }
+ signal(SIGPIPE, SIG_IGN);
+
+ /* We don't work too well when running as non-root anymore */
+ if (geteuid() != 0) {
+ fprintf(stderr, "Error: This utility should only be run as root.\n");
+ return 1;
+ }
+
+#ifdef PC98
+ {
+ /* XXX */
+ char *p = getenv("TERM");
+ if (p && strcmp(p, "cons25") == 0)
+ putenv("TERM=cons25w");
+ }
+#endif
+
+ /* Set up whatever things need setting up */
+ systemInitialize(argc, argv);
+
+ /* Set default flag and variable values */
+ installVarDefaults(NULL);
+ /* only when multi-user is it reasonable to do this here */
+ if (!RunningAsInit)
+ installEnvironment();
+
+ if (argc > 1 && !strcmp(argv[1], "-fake")) {
+ variable_set2(VAR_DEBUG, "YES", 0);
+ Fake = TRUE;
+ msgConfirm("I'll be just faking it from here on out, OK?");
+ }
+ if (argc > 1 && !strcmp(argv[1], "-restart"))
+ Restarting = TRUE;
+
+ /* Try to preserve our scroll-back buffer */
+ if (OnVTY) {
+ for (curr = 0; curr < 25; curr++)
+ putchar('\n');
+ }
+ /* Move stderr aside */
+ if (DebugFD)
+ dup2(DebugFD, 2);
+
+ /* Initialize driver modules, if we haven't already done so (ie,
+ the user hit Ctrl-C -> Restart. */
+ if (!pvariable_get("modulesInitialize")) {
+ moduleInitialize();
+ pvariable_set("modulesInitialize=1");
+ }
+
+ /* Initialize PC-card, if we haven't already done so. */
+#ifdef PCCARD_ARCH
+ if (!pvariable_get("pccardInitialize")) {
+ pccardInitialize();
+ pvariable_set("pccardInitialize=1");
+ }
+#endif
+
+ /* Initialize USB, if we haven't already done so. */
+ if (!pvariable_get("usbInitialize")) {
+ usbInitialize();
+ pvariable_set("usbInitialize=1");
+ }
+
+ /* Probe for all relevant devices on the system */
+ deviceGetAll();
+
+ /* First, see if we have any arguments to process (and argv[0] counts if it's not "sysinstall") */
+ if (!RunningAsInit) {
+ int i, start_arg;
+
+ if (!strstr(argv[0], "sysinstall"))
+ start_arg = 0;
+ else if (Fake || Restarting)
+ start_arg = 2;
+ else
+ start_arg = 1;
+ for (i = start_arg; i < argc; i++) {
+ if (DITEM_STATUS(dispatchCommand(argv[i])) != DITEM_SUCCESS)
+ systemShutdown(1);
+ }
+ if (argc > start_arg)
+ systemShutdown(0);
+ }
+ else
+ dispatch_load_file_int(TRUE);
+
+ status = setjmp(BailOut);
+ if (status) {
+ msgConfirm("A signal %d was caught - I'm saving what I can and shutting\n"
+ "down. If you can reproduce the problem, please turn Debug on\n"
+ "in the Options menu for the extra information it provides\n"
+ "in debugging problems like this.", status);
+ systemShutdown(status);
+ }
+
+ /* Begin user dialog at outer menu */
+ dialog_clear();
+ while (1) {
+ choice = scroll = curr = max = 0;
+ dmenuOpen(&MenuInitial, &choice, &scroll, &curr, &max, TRUE);
+ if (getpid() != 1
+#ifdef __alpha__
+ || !msgNoYes("Are you sure you wish to exit? The system will halt.")
+#else
+ || !msgNoYes("Are you sure you wish to exit? The system will reboot\n"
+ "(be sure to remove any floppies/CDs/DVDs from the drives).")
+#endif
+ )
+ break;
+ }
+
+ /* Say goodnight, Gracie */
+ systemShutdown(0);
+
+ return 0; /* We should never get here */
+}
diff --git a/usr.sbin/sysinstall/media.c b/usr.sbin/sysinstall/media.c
new file mode 100644
index 0000000..26e5f46
--- /dev/null
+++ b/usr.sbin/sysinstall/media.c
@@ -0,0 +1,867 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated to essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <signal.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/errno.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <resolv.h>
+
+static Boolean got_intr = FALSE;
+static Boolean ftp_skip_resolve = FALSE;
+
+/* timeout handler */
+static void
+handle_intr(int sig)
+{
+ msgDebug("User generated interrupt.\n");
+ got_intr = TRUE;
+}
+
+static int
+check_for_interrupt(void)
+{
+ if (got_intr) {
+ got_intr = FALSE;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static int
+genericHook(dialogMenuItem *self, DeviceType type)
+{
+ Device **devs;
+
+ devs = deviceFind(self->prompt, type);
+ if (devs)
+ mediaDevice = devs[0];
+ return (devs ? DITEM_LEAVE_MENU : DITEM_FAILURE);
+}
+
+static int
+cdromHook(dialogMenuItem *self)
+{
+ return genericHook(self, DEVICE_TYPE_CDROM);
+}
+
+static void
+kickstart_dns(void)
+{
+ static Boolean initted = FALSE;
+ int time;
+ char *cp;
+
+ cp = variable_get(VAR_MEDIA_TIMEOUT);
+ if (!cp)
+ time = MEDIA_TIMEOUT;
+ else
+ time = atoi(cp);
+ if (!time)
+ time = 100;
+ if (!initted) {
+ res_init();
+ _res.retry = 2; /* 2 times seems a reasonable number to me */
+ _res.retrans = time / 2; /* so spend half our alloted time on each try */
+ initted = TRUE;
+ }
+}
+
+char *
+cpioVerbosity()
+{
+ char *cp = variable_get(VAR_CPIO_VERBOSITY);
+
+ if (cp && !strcmp(cp, "high"))
+ return "-v";
+ else if (cp && !strcmp(cp, "medium"))
+ return "-V";
+ return "";
+}
+
+void
+mediaClose(void)
+{
+ if (mediaDevice)
+ DEVICE_SHUTDOWN(mediaDevice);
+ mediaDevice = NULL;
+}
+
+/*
+ * Return 1 if we successfully found and set the installation type to
+ * be a CD.
+ */
+int
+mediaSetCDROM(dialogMenuItem *self)
+{
+ Device **devs;
+ int cnt;
+
+ mediaClose();
+ devs = deviceFind(NULL, DEVICE_TYPE_CDROM);
+ cnt = deviceCount(devs);
+ if (!cnt) {
+ if (self) /* Interactive? */
+ msgConfirm("No CD/DVD devices found! Please check that your system's\n"
+ "configuration is correct and that the CD/DVD drive is of a supported\n"
+ "type. For more information, consult the hardware guide\n"
+ "in the Doc menu.");
+ return DITEM_FAILURE | DITEM_CONTINUE;
+ }
+ else if (cnt > 1) {
+ DMenu *menu;
+ int status;
+
+ menu = deviceCreateMenu(&MenuMediaCDROM, DEVICE_TYPE_CDROM, cdromHook, NULL);
+ if (!menu)
+ msgFatal("Unable to create CDROM menu! Something is seriously wrong.");
+ status = dmenuOpenSimple(menu, FALSE);
+ free(menu);
+ if (!status)
+ return DITEM_FAILURE;
+ }
+ else
+ mediaDevice = devs[0];
+ return (mediaDevice ? DITEM_SUCCESS | DITEM_LEAVE_MENU : DITEM_FAILURE);
+}
+
+static int
+floppyHook(dialogMenuItem *self)
+{
+ return genericHook(self, DEVICE_TYPE_FLOPPY);
+}
+
+/*
+ * Return 1 if we successfully found and set the installation type to
+ * be a floppy
+ */
+int
+mediaSetFloppy(dialogMenuItem *self)
+{
+ Device **devs;
+ int cnt;
+
+ mediaClose();
+ devs = deviceFind(NULL, DEVICE_TYPE_FLOPPY);
+ cnt = deviceCount(devs);
+ if (!cnt) {
+ msgConfirm("No floppy devices found! Please check that your system's configuration\n"
+ "is correct. For more information, consult the hardware guide in the Doc\n"
+ "menu.");
+ return DITEM_FAILURE | DITEM_CONTINUE;
+ }
+ else if (cnt > 1) {
+ DMenu *menu;
+ int status;
+
+ menu = deviceCreateMenu(&MenuMediaFloppy, DEVICE_TYPE_FLOPPY, floppyHook, NULL);
+ if (!menu)
+ msgFatal("Unable to create Floppy menu! Something is seriously wrong.");
+ status = dmenuOpenSimple(menu, FALSE);
+ free(menu);
+ if (!status)
+ return DITEM_FAILURE;
+ }
+ else
+ mediaDevice = devs[0];
+ if (mediaDevice)
+ mediaDevice->private = NULL;
+ return (mediaDevice ? DITEM_LEAVE_MENU : DITEM_FAILURE);
+}
+
+static int
+DOSHook(dialogMenuItem *self)
+{
+ return genericHook(self, DEVICE_TYPE_DOS);
+}
+
+/*
+ * Return 1 if we successfully found and set the installation type to
+ * be a DOS partition.
+ */
+int
+mediaSetDOS(dialogMenuItem *self)
+{
+ Device **devs;
+ int cnt;
+
+ mediaClose();
+ devs = deviceFind(NULL, DEVICE_TYPE_DOS);
+ cnt = deviceCount(devs);
+ if (!cnt) {
+ msgConfirm("No DOS primary partitions found! This installation method is unavailable");
+ return DITEM_FAILURE | DITEM_CONTINUE;
+ }
+ else if (cnt > 1) {
+ DMenu *menu;
+ int status;
+
+ menu = deviceCreateMenu(&MenuMediaDOS, DEVICE_TYPE_DOS, DOSHook, NULL);
+ if (!menu)
+ msgFatal("Unable to create DOS menu! Something is seriously wrong.");
+ status = dmenuOpenSimple(menu, FALSE);
+ free(menu);
+ if (!status)
+ return DITEM_FAILURE;
+ }
+ else
+ mediaDevice = devs[0];
+ return (mediaDevice ? DITEM_LEAVE_MENU : DITEM_FAILURE);
+}
+
+static int
+tapeHook(dialogMenuItem *self)
+{
+ return genericHook(self, DEVICE_TYPE_TAPE);
+}
+
+/*
+ * Return 1 if we successfully found and set the installation type to
+ * be a tape drive.
+ */
+int
+mediaSetTape(dialogMenuItem *self)
+{
+ Device **devs;
+ int cnt;
+
+ mediaClose();
+ devs = deviceFind(NULL, DEVICE_TYPE_TAPE);
+ cnt = deviceCount(devs);
+ if (!cnt) {
+ msgConfirm("No tape drive devices found! Please check that your system's configuration\n"
+ "is correct. For more information, consult the hardware guide in the Doc\n"
+ "menu.");
+ return DITEM_FAILURE | DITEM_CONTINUE;
+ }
+ else if (cnt > 1) {
+ DMenu *menu;
+ int status;
+
+ menu = deviceCreateMenu(&MenuMediaTape, DEVICE_TYPE_TAPE, tapeHook, NULL);
+ if (!menu)
+ msgFatal("Unable to create tape drive menu! Something is seriously wrong.");
+ status = dmenuOpenSimple(menu, FALSE);
+ free(menu);
+ if (!status)
+ return DITEM_FAILURE;
+ }
+ else
+ mediaDevice = devs[0];
+ if (mediaDevice) {
+ char *val;
+
+ val = msgGetInput("/var/tmp", "Please enter the name of a temporary directory containing\n"
+ "sufficient space for holding the contents of this tape (or\n"
+ "tapes). The contents of this directory will be removed\n"
+ "after installation, so be sure to specify a directory that\n"
+ "can be erased afterwards!\n");
+ if (!val)
+ mediaDevice = NULL;
+ else
+ mediaDevice->private = strdup(val);
+ }
+ return (mediaDevice ? DITEM_LEAVE_MENU : DITEM_FAILURE);
+}
+
+/*
+ * Return 0 if we successfully found and set the installation type to
+ * be an ftp server
+ */
+int
+mediaSetFTP(dialogMenuItem *self)
+{
+ static Device ftpDevice;
+ char *cp, hbuf[MAXHOSTNAMELEN], *hostname, *dir;
+ struct addrinfo hints, *res;
+ int af;
+ extern int FtpPort;
+ static Device *networkDev = NULL;
+
+ mediaClose();
+ cp = variable_get(VAR_FTP_PATH);
+ /* If we've been through here before ... */
+ if (networkDev && cp && msgYesNo("Re-use old FTP site selection values?"))
+ cp = NULL;
+ if (!cp) {
+ if (!dmenuOpenSimple(&MenuMediaFTP, FALSE))
+ return DITEM_FAILURE;
+ else
+ cp = variable_get(VAR_FTP_PATH);
+ }
+ if (!cp)
+ return DITEM_FAILURE;
+ else if (!strcmp(cp, "other")) {
+ variable_set2(VAR_FTP_PATH, "ftp://", 0);
+ cp = variable_get_value(VAR_FTP_PATH, "Please specify the URL of a FreeBSD distribution on a\n"
+ "remote ftp site. This site must accept either anonymous\n"
+ "ftp or you should have set an ftp username and password\n"
+ "in the Options screen.\n\n"
+ "A URL looks like this: ftp://<hostname>/<path>\n"
+ "Where <path> is relative to the anonymous ftp directory or the\n"
+ "home directory of the user being logged in as.", 0);
+ if (!cp || !*cp || !strcmp(cp, "ftp://")) {
+ variable_unset(VAR_FTP_PATH);
+ return DITEM_FAILURE;
+ }
+ }
+ if (strncmp("ftp://", cp, 6)) {
+ msgConfirm("Sorry, %s is an invalid URL!", cp);
+ variable_unset(VAR_FTP_PATH);
+ return DITEM_FAILURE;
+ }
+ SAFE_STRCPY(ftpDevice.name, cp);
+ SAFE_STRCPY(hbuf, cp + 6);
+ hostname = hbuf;
+
+ if (!networkDev || msgYesNo("You've already done the network configuration once,\n"
+ "would you like to skip over it now?") != 0) {
+ if (networkDev)
+ DEVICE_SHUTDOWN(networkDev);
+ if (!(networkDev = tcpDeviceSelect())) {
+ variable_unset(VAR_FTP_PATH);
+ return DITEM_FAILURE;
+ }
+ }
+ if (!DEVICE_INIT(networkDev)) {
+ if (isDebug())
+ msgDebug("mediaSetFTP: Net device init failed.\n");
+ variable_unset(VAR_FTP_PATH);
+ return DITEM_FAILURE;
+ }
+ if (*hostname == '[' && (cp = index(hostname + 1, ']')) != NULL &&
+ (*++cp == '\0' || *cp == '/' || *cp == ':')) {
+ ++hostname;
+ *(cp - 1) = '\0';
+ }
+ else
+ cp = index(hostname, ':');
+ if (cp != NULL && *cp == ':') {
+ *(cp++) = '\0';
+ FtpPort = strtol(cp, 0, 0);
+ }
+ else
+ FtpPort = 21;
+ if ((dir = index(cp ? cp : hostname, '/')) != NULL)
+ *(dir++) = '\0';
+ if (isDebug()) {
+ msgDebug("hostname = `%s'\n", hostname);
+ msgDebug("dir = `%s'\n", dir ? dir : "/");
+ msgDebug("port # = `%d'\n", FtpPort);
+ }
+ if (!ftp_skip_resolve && variable_get(VAR_NAMESERVER)) {
+ msgNotify("Looking up host %s.", hostname);
+ if (isDebug())
+ msgDebug("Starting DNS.\n");
+ kickstart_dns();
+ if (isDebug())
+ msgDebug("Looking up hostname, %s, using getaddrinfo(AI_NUMERICHOST).\n", hostname);
+ af = variable_cmp(VAR_IPV6_ENABLE, "YES") ? AF_INET : AF_UNSPEC;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = af;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
+ if (getaddrinfo(hostname, NULL, &hints, &res) != 0) {
+ if (isDebug())
+ msgDebug("Looking up hostname, %s, using getaddrinfo().\n",
+ hostname);
+ hints.ai_flags = AI_PASSIVE;
+ if (getaddrinfo(hostname, NULL, &hints, &res) != 0) {
+ msgConfirm("Cannot resolve hostname `%s'! Are you sure that"
+ " your\nname server, gateway and network interface are"
+ " correctly configured?", hostname);
+ if (networkDev)
+ DEVICE_SHUTDOWN(networkDev);
+ networkDev = NULL;
+ variable_unset(VAR_FTP_PATH);
+ return DITEM_FAILURE;
+ }
+ }
+ freeaddrinfo(res);
+ if (isDebug())
+ msgDebug("Found DNS entry for %s successfully..\n", hostname);
+ }
+ variable_set2(VAR_FTP_HOST, hostname, 0);
+ variable_set2(VAR_FTP_DIR, dir ? dir : "/", 0);
+ variable_set2(VAR_FTP_PORT, itoa(FtpPort), 0);
+ ftpDevice.type = DEVICE_TYPE_FTP;
+ ftpDevice.init = mediaInitFTP;
+ ftpDevice.get = mediaGetFTP;
+ ftpDevice.shutdown = mediaShutdownFTP;
+ ftpDevice.private = networkDev;
+ mediaDevice = &ftpDevice;
+ return DITEM_SUCCESS | DITEM_LEAVE_MENU | DITEM_RESTORE;
+}
+
+int
+mediaSetFTPActive(dialogMenuItem *self)
+{
+ variable_set2(VAR_FTP_STATE, "active", 0);
+ return mediaSetFTP(self);
+}
+
+int
+mediaSetFTPPassive(dialogMenuItem *self)
+{
+ variable_set2(VAR_FTP_STATE, "passive", 0);
+ return mediaSetFTP(self);
+}
+
+int mediaSetHTTP(dialogMenuItem *self)
+{
+ Boolean tmp;
+ int result;
+ char *cp, *idx, hbuf[MAXHOSTNAMELEN], *hostname;
+ int HttpPort;
+ int what = DITEM_RESTORE;
+
+
+ tmp = ftp_skip_resolve;
+ ftp_skip_resolve = TRUE;
+ result = mediaSetFTP(self);
+ ftp_skip_resolve = tmp;
+
+ if (DITEM_STATUS(result) != DITEM_SUCCESS)
+ return result;
+
+ cp = variable_get_value(VAR_HTTP_PROXY,
+ "Please enter the address of the HTTP proxy in this format:\n"
+ " hostname:port (the ':port' is optional, default is 3128)",0);
+ if (!cp)
+ return DITEM_FAILURE;
+ SAFE_STRCPY(hbuf, cp);
+ hostname = hbuf;
+ if (*hostname == '[' && (idx = index(hostname + 1, ']')) != NULL &&
+ (*++idx == '\0' || *idx == ':')) {
+ ++hostname;
+ *(idx - 1) = '\0';
+ } else
+ idx = index(hostname, ':');
+ if (idx == NULL || *idx != ':')
+ HttpPort = 3128; /* try this as default */
+ else {
+ *(idx++) = '\0';
+ HttpPort = strtol(idx, 0, 0);
+ }
+
+ variable_set2(VAR_HTTP_HOST, hostname, 0);
+ variable_set2(VAR_HTTP_PORT, itoa(HttpPort), 0);
+ if (isDebug()) {
+ msgDebug("VAR_FTP_PATH : %s",variable_get(VAR_FTP_PATH));
+ msgDebug("VAR_HTTP_HOST, _PORT: %s:%s",variable_get(VAR_HTTP_HOST),
+ variable_get(VAR_HTTP_PORT));
+ }
+
+ /* mediaDevice has been set by mediaSetFTP(), overwrite partly: */
+ mediaDevice->type = DEVICE_TYPE_HTTP;
+ mediaDevice->init = mediaInitHTTP;
+ mediaDevice->get = mediaGetHTTP;
+ mediaDevice->shutdown = dummyShutdown;
+ return DITEM_SUCCESS | DITEM_LEAVE_MENU | what;
+}
+
+
+int
+mediaSetUFS(dialogMenuItem *self)
+{
+ static Device ufsDevice;
+ struct statfs st;
+ char *cp;
+
+ mediaClose();
+ cp = variable_get_value(VAR_UFS_PATH, "Enter a fully qualified pathname for the directory\n"
+ "containing the FreeBSD distribution files:", 0);
+ if (!cp)
+ return DITEM_FAILURE;
+
+ /* If they gave us a CDROM or something, try and pick a better name */
+ if (statfs(cp, &st))
+ strcpy(ufsDevice.name, "ufs");
+ else
+ strcpy(ufsDevice.name, st.f_fstypename);
+
+ ufsDevice.type = DEVICE_TYPE_UFS;
+ ufsDevice.init = dummyInit;
+ ufsDevice.get = mediaGetUFS;
+ ufsDevice.shutdown = dummyShutdown;
+ ufsDevice.private = strdup(cp);
+ mediaDevice = &ufsDevice;
+ return DITEM_LEAVE_MENU;
+}
+
+int
+mediaSetNFS(dialogMenuItem *self)
+{
+ static Device nfsDevice;
+ static Device *networkDev = NULL;
+ char *cp, *idx;
+ char hostname[MAXPATHLEN];
+
+ mediaClose();
+ cp = variable_get_value(VAR_NFS_PATH, "Please enter the full NFS file specification for the remote\n"
+ "host and directory containing the FreeBSD distribution files.\n"
+ "This should be in the format: hostname:/some/freebsd/dir", 0);
+ if (!cp)
+ return DITEM_FAILURE;
+ SAFE_STRCPY(hostname, cp);
+ if (!(idx = index(hostname, ':'))) {
+ msgConfirm("Invalid NFS path specification. Must be of the form:\n"
+ "host:/full/pathname/to/FreeBSD/distdir");
+ return DITEM_FAILURE;
+ }
+ SAFE_STRCPY(nfsDevice.name, hostname);
+ *idx = '\0';
+ if (!networkDev || msgYesNo("You've already done the network configuration once,\n"
+ "would you like to skip over it now?") != 0) {
+ if (networkDev)
+ DEVICE_SHUTDOWN(networkDev);
+ if (!(networkDev = tcpDeviceSelect()))
+ return DITEM_FAILURE;
+ }
+ if (!DEVICE_INIT(networkDev)) {
+ if (isDebug())
+ msgDebug("mediaSetNFS: Net device init failed\n");
+ }
+ if (variable_get(VAR_NAMESERVER)) {
+ kickstart_dns();
+ if ((inet_addr(hostname) == INADDR_NONE) && (gethostbyname(hostname) == NULL)) {
+ msgConfirm("Cannot resolve hostname `%s'! Are you sure that your\n"
+ "name server, gateway and network interface are correctly configured?", hostname);
+ if (networkDev)
+ DEVICE_SHUTDOWN(networkDev);
+ networkDev = NULL;
+ variable_unset(VAR_NFS_PATH);
+ return DITEM_FAILURE;
+ }
+ else {
+ if (isDebug())
+ msgDebug("Found DNS entry for %s successfully..", hostname);
+ }
+ }
+ variable_set2(VAR_NFS_HOST, hostname, 0);
+ nfsDevice.type = DEVICE_TYPE_NFS;
+ nfsDevice.init = mediaInitNFS;
+ nfsDevice.get = mediaGetNFS;
+ nfsDevice.shutdown = mediaShutdownNFS;
+ nfsDevice.private = networkDev;
+ mediaDevice = &nfsDevice;
+ return DITEM_LEAVE_MENU;
+}
+
+Boolean
+mediaExtractDistBegin(char *dir, int *fd, int *zpid, int *cpid)
+{
+ int i, pfd[2],qfd[2];
+
+ if (!dir)
+ dir = "/";
+ Mkdir(dir);
+ chdir(dir);
+ pipe(pfd);
+ pipe(qfd);
+ *zpid = fork();
+ if (!*zpid) {
+ char *unzipper = RunningAsInit ? "/stand/" UNZIPPER
+ : "/usr/bin/" UNZIPPER;
+
+ dup2(qfd[0], 0); close(qfd[0]);
+ dup2(pfd[1], 1); close(pfd[1]);
+ if (DebugFD != -1)
+ dup2(DebugFD, 2);
+ else {
+ close(2);
+ open("/dev/null", O_WRONLY);
+ }
+ close(qfd[1]);
+ close(pfd[0]);
+ i = execl(unzipper, unzipper, (char *)0);
+ if (isDebug())
+ msgDebug("%s command returns %d status\n", unzipper, i);
+ exit(i);
+ }
+ *fd = qfd[1];
+ close(qfd[0]);
+ *cpid = fork();
+ if (!*cpid) {
+ char *cpio = RunningAsInit ? "/stand/cpio" : "/usr/bin/cpio";
+
+ dup2(pfd[0], 0); close(pfd[0]);
+ close(pfd[1]);
+ close(qfd[1]);
+ if (DebugFD != -1) {
+ dup2(DebugFD, 1);
+ dup2(DebugFD, 2);
+ }
+ else {
+ close(1); open("/dev/null", O_WRONLY);
+ dup2(1, 2);
+ }
+ if (strlen(cpioVerbosity()))
+ i = execl(cpio, cpio, "-idum", cpioVerbosity(), "--block-size", mediaTapeBlocksize(), (char *)0);
+ else
+ i = execl(cpio, cpio, "-idum", "--block-size", mediaTapeBlocksize(), (char *)0);
+ if (isDebug())
+ msgDebug("%s command returns %d status\n", cpio, i);
+ exit(i);
+ }
+ close(pfd[0]);
+ close(pfd[1]);
+ return TRUE;
+}
+
+Boolean
+mediaExtractDistEnd(int zpid, int cpid)
+{
+ int i,j;
+
+ i = waitpid(zpid, &j, 0);
+ /* Don't check exit status - gunzip seems to return a bogus one! */
+ if (i < 0) {
+ if (isDebug())
+ msgDebug("wait for %s returned status of %d!\n",
+ USE_GZIP ? "gunzip" : "bunzip2", i);
+ return FALSE;
+ }
+ i = waitpid(cpid, &j, 0);
+ if (i < 0 || WEXITSTATUS(j)) {
+ if (isDebug())
+ msgDebug("cpio returned error status of %d!\n", WEXITSTATUS(j));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+Boolean
+mediaExtractDist(char *dir, char *dist, FILE *fp)
+{
+ int i, j, total, seconds, zpid, cpid, pfd[2], qfd[2];
+ char buf[BUFSIZ];
+ struct timeval start, stop;
+ struct sigaction new, old;
+
+ if (!dir)
+ dir = "/";
+
+ Mkdir(dir);
+ chdir(dir);
+ pipe(pfd); /* read end */
+ pipe(qfd); /* write end */
+ zpid = fork();
+ if (!zpid) {
+ char *unzipper = RunningAsInit ? "/stand/" UNZIPPER
+ : "/usr/bin/" UNZIPPER;
+
+ fclose(fp);
+ close(qfd[1]);
+ dup2(qfd[0], 0); close(qfd[0]);
+
+ close(pfd[0]);
+ dup2(pfd[1], 1); close(pfd[1]);
+
+ if (DebugFD != -1)
+ dup2(DebugFD, 2);
+ else {
+ close(2);
+ open("/dev/null", O_WRONLY);
+ }
+ i = execl(unzipper, unzipper, (char *)0);
+ if (isDebug())
+ msgDebug("%s command returns %d status\n", unzipper, i);
+ exit(i);
+ }
+ cpid = fork();
+ if (!cpid) {
+ char *cpio = RunningAsInit ? "/stand/cpio" : "/usr/bin/cpio";
+
+ close(pfd[1]);
+ dup2(pfd[0], 0); close(pfd[0]);
+ close (qfd[0]); close(qfd[1]);
+ fclose(fp);
+ if (DebugFD != -1) {
+ dup2(DebugFD, 1);
+ dup2(DebugFD, 2);
+ }
+ else {
+ dup2(open("/dev/null", O_WRONLY), 1);
+ dup2(1, 2);
+ }
+ if (strlen(cpioVerbosity()))
+ i = execl(cpio, cpio, "-idum", cpioVerbosity(), "--block-size", mediaTapeBlocksize(), (char *)0);
+ else
+ i = execl(cpio, cpio, "-idum", "--block-size", mediaTapeBlocksize(), (char *)0);
+ if (isDebug())
+ msgDebug("%s command returns %d status\n", cpio, i);
+ exit(i);
+ }
+ close(pfd[0]); close(pfd[1]);
+ close(qfd[0]);
+
+ total = 0;
+ (void)gettimeofday(&start, (struct timezone *)0);
+
+ /* Make ^C abort the current transfer rather than the whole show */
+ new.sa_handler = handle_intr;
+ new.sa_flags = 0;
+ (void)sigemptyset(&new.sa_mask);
+ sigaction(SIGINT, &new, &old);
+
+ while ((i = fread(buf, 1, BUFSIZ, fp)) > 0) {
+ if (check_for_interrupt()) {
+ msgConfirm("Failure to read from media: User interrupt.");
+ break;
+ }
+ if (write(qfd[1], buf, i) != i) {
+ msgConfirm("Write error on transfer to cpio process, try of %d bytes.", i);
+ break;
+ }
+ else {
+ (void)gettimeofday(&stop, (struct timezone *)0);
+ stop.tv_sec = stop.tv_sec - start.tv_sec;
+ stop.tv_usec = stop.tv_usec - start.tv_usec;
+ if (stop.tv_usec < 0)
+ stop.tv_sec--, stop.tv_usec += 1000000;
+ seconds = stop.tv_sec + (stop.tv_usec / 1000000.0);
+ if (!seconds)
+ seconds = 1;
+ total += i;
+ msgInfo("%10d bytes read from %s dist @ %.1f KB/sec.",
+ total, dist, (total / seconds) / 1024.0);
+ }
+ }
+ sigaction(SIGINT, &old, NULL); /* restore sigint */
+ close(qfd[1]);
+
+ i = waitpid(zpid, &j, 0);
+ /* Don't check exit status - gunzip seems to return a bogus one! */
+ if (i < 0) {
+ if (isDebug())
+ msgDebug("wait for %s returned status of %d!\n",
+ USE_GZIP ? "gunzip" : "bunzip2", i);
+ return FALSE;
+ }
+ i = waitpid(cpid, &j, 0);
+ if (i < 0 || WEXITSTATUS(j)) {
+ if (isDebug())
+ msgDebug("cpio returned error status of %d!\n", WEXITSTATUS(j));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+int
+mediaGetType(dialogMenuItem *self)
+{
+ return ((dmenuOpenSimple(&MenuMedia, FALSE) && mediaDevice) ? DITEM_SUCCESS : DITEM_FAILURE);
+}
+
+/* Return TRUE if all the media variables are set up correctly */
+Boolean
+mediaVerify(void)
+{
+ if (!mediaDevice)
+ return (DITEM_STATUS(mediaGetType(NULL)) == DITEM_SUCCESS);
+ return TRUE;
+}
+
+/* Set the FTP username and password fields */
+int
+mediaSetFTPUserPass(dialogMenuItem *self)
+{
+ char *pass;
+
+ if (variable_get_value(VAR_FTP_USER, "Please enter the username you wish to login as:", 0)) {
+ DialogInputAttrs |= DITEM_NO_ECHO;
+ pass = variable_get_value(VAR_FTP_PASS, "Please enter the password for this user:", 0);
+ DialogInputAttrs &= ~DITEM_NO_ECHO;
+ }
+ else
+ pass = NULL;
+ return (pass ? DITEM_SUCCESS : DITEM_FAILURE);
+}
+
+/* Set CPIO verbosity level */
+int
+mediaSetCPIOVerbosity(dialogMenuItem *self)
+{
+ char *cp = variable_get(VAR_CPIO_VERBOSITY);
+
+ if (!cp) {
+ msgConfirm("CPIO Verbosity is not set to anything!");
+ return DITEM_FAILURE;
+ }
+ else {
+ if (!strcmp(cp, "low"))
+ variable_set2(VAR_CPIO_VERBOSITY, "medium", 0);
+ else if (!strcmp(cp, "medium"))
+ variable_set2(VAR_CPIO_VERBOSITY, "high", 0);
+ else /* must be "high" - wrap around */
+ variable_set2(VAR_CPIO_VERBOSITY, "low", 0);
+ }
+ return DITEM_SUCCESS;
+}
+
+/* A generic open which follows a well-known "path" of places to look */
+FILE *
+mediaGenericGet(char *base, const char *file)
+{
+ char buf[PATH_MAX];
+
+ snprintf(buf, PATH_MAX, "%s/%s", base, file);
+ if (file_readable(buf))
+ return fopen(buf, "r");
+ snprintf(buf, PATH_MAX, "%s/FreeBSD/%s", base, file);
+ if (file_readable(buf))
+ return fopen(buf, "r");
+ snprintf(buf, PATH_MAX, "%s/releases/%s", base, file);
+ if (file_readable(buf))
+ return fopen(buf, "r");
+ snprintf(buf, PATH_MAX, "%s/%s/%s", base, variable_get(VAR_RELNAME), file);
+ if (file_readable(buf))
+ return fopen(buf, "r");
+ snprintf(buf, PATH_MAX, "%s/releases/%s/%s", base, variable_get(VAR_RELNAME), file);
+ return fopen(buf, "r");
+}
+
diff --git a/usr.sbin/sysinstall/menus.c b/usr.sbin/sysinstall/menus.c
new file mode 100644
index 0000000..1b6132a
--- /dev/null
+++ b/usr.sbin/sysinstall/menus.c
@@ -0,0 +1,2157 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif
+
+#include "sysinstall.h"
+
+/* Miscellaneous work routines for menus */
+static int
+setSrc(dialogMenuItem *self)
+{
+ Dists |= DIST_SRC;
+ SrcDists = DIST_SRC_ALL;
+ CRYPTODists |= (DIST_CRYPTO_SCRYPTO | DIST_CRYPTO_SSECURE |
+ DIST_CRYPTO_SKERBEROS4 | DIST_CRYPTO_SKERBEROS5);
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+clearSrc(dialogMenuItem *self)
+{
+ Dists &= ~DIST_SRC;
+ SrcDists = 0;
+ CRYPTODists &= ~(DIST_CRYPTO_SCRYPTO | DIST_CRYPTO_SSECURE |
+ DIST_CRYPTO_SKERBEROS4 | DIST_CRYPTO_SKERBEROS5);
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+setX11Misc(dialogMenuItem *self)
+{
+ XF86Dists |= DIST_XF86_MISC_ALL;
+ Dists |= DIST_XF86;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+clearX11Misc(dialogMenuItem *self)
+{
+ XF86Dists &= ~DIST_XF86_MISC_ALL;
+ if (!XF86ServerDists && !XF86FontDists)
+ Dists &= ~DIST_XF86;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+setX11Servers(dialogMenuItem *self)
+{
+ XF86Dists |= DIST_XF86_SERVER;
+ XF86ServerDists = DIST_XF86_SERVER_ALL;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+clearX11Servers(dialogMenuItem *self)
+{
+ XF86Dists &= ~DIST_XF86_SERVER;
+ XF86ServerDists = 0;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+setX11Fonts(dialogMenuItem *self)
+{
+ XF86Dists |= DIST_XF86_FONTS;
+ XF86FontDists = DIST_XF86_FONTS_ALL;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+clearX11Fonts(dialogMenuItem *self)
+{
+ XF86Dists &= ~DIST_XF86_FONTS;
+ XF86FontDists = 0;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+#define _IS_SET(dist, set) (((dist) & (set)) == (set))
+
+#define IS_DEVELOPER(dist, extra) (_IS_SET(dist, _DIST_DEVELOPER | extra) || \
+ _IS_SET(dist, _DIST_DEVELOPER | extra))
+
+#define IS_USER(dist, extra) (_IS_SET(dist, _DIST_USER | extra) || \
+ _IS_SET(dist, _DIST_USER | extra))
+
+static int
+checkDistDeveloper(dialogMenuItem *self)
+{
+ return IS_DEVELOPER(Dists, 0) && _IS_SET(SrcDists, DIST_SRC_ALL);
+}
+
+static int
+checkDistXDeveloper(dialogMenuItem *self)
+{
+ return IS_DEVELOPER(Dists, DIST_XF86) && _IS_SET(SrcDists, DIST_SRC_ALL);
+}
+
+static int
+checkDistKernDeveloper(dialogMenuItem *self)
+{
+ return IS_DEVELOPER(Dists, 0) && _IS_SET(SrcDists, DIST_SRC_SYS);
+}
+
+static int
+checkDistXKernDeveloper(dialogMenuItem *self)
+{
+ return IS_DEVELOPER(Dists, DIST_XF86) && _IS_SET(SrcDists, DIST_SRC_SYS);
+}
+
+static int
+checkDistUser(dialogMenuItem *self)
+{
+ return IS_USER(Dists, 0);
+}
+
+static int
+checkDistXUser(dialogMenuItem *self)
+{
+ return IS_USER(Dists, DIST_XF86);
+}
+
+static int
+checkDistMinimum(dialogMenuItem *self)
+{
+ return Dists == (DIST_BASE | DIST_CRYPTO);
+}
+
+static int
+checkDistEverything(dialogMenuItem *self)
+{
+ return Dists == DIST_ALL && CRYPTODists == DIST_CRYPTO_ALL && \
+ _IS_SET(SrcDists, DIST_SRC_ALL) && \
+ _IS_SET(XF86Dists, DIST_XF86_ALL) && \
+ _IS_SET(XF86ServerDists, DIST_XF86_SERVER_ALL) && \
+ _IS_SET(XF86FontDists, DIST_XF86_FONTS_ALL);
+}
+
+static int
+srcFlagCheck(dialogMenuItem *item)
+{
+ return SrcDists;
+}
+
+static int
+x11FlagCheck(dialogMenuItem *item)
+{
+ return Dists & DIST_XF86;
+}
+
+static int
+checkTrue(dialogMenuItem *item)
+{
+ return TRUE;
+}
+
+/* All the system menus go here.
+ *
+ * Hardcoded things like version number strings will disappear from
+ * these menus just as soon as I add the code for doing inline variable
+ * expansion.
+ */
+
+DMenu MenuIndex = {
+ DMENU_NORMAL_TYPE,
+ "Glossary of functions",
+ "This menu contains an alphabetized index of the top level functions in\n"
+ "this program (sysinstall). Invoke an option by pressing [SPACE] or\n"
+ "[ENTER]. To exit, use [TAB] to move to the Cancel button.",
+ "Use PageUp or PageDown to move through this menu faster!",
+ NULL,
+ { { " Anon FTP", "Configure anonymous FTP logins.", dmenuVarCheck, configAnonFTP, NULL, "anon_ftp" },
+ { " Commit", "Commit any pending actions (dangerous!)", NULL, installCustomCommit },
+ { " 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, 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, Errata", "The distribution errata.", NULL, dmenuDisplayFile, NULL, "ERRATA" },
+ { " Doc, Hardware", "The distribution hardware guide.", NULL, dmenuDisplayFile, NULL, "HARDWARE" },
+ { " Doc, Install", "The distribution installation guide.", NULL, dmenuDisplayFile, NULL, "INSTALL" },
+ { " Doc, Copyright", "The distribution copyright notices.", NULL, dmenuDisplayFile, NULL, "COPYRIGHT" },
+ { " Doc, Release", "The distribution release notes.", NULL, dmenuDisplayFile, NULL, "RELNOTES" },
+ { " Doc, HTML", "The HTML documentation menu.", NULL, docBrowser },
+ { " Dump Vars", "(debugging) dump out internal variables.", NULL, dump_variables },
+ { " Emergency shell", "Start an Emergency Holographic shell.", NULL, installFixitHoloShell },
+#ifdef __i386__
+ { " Fdisk", "The disk Partition Editor", NULL, diskPartitionEditor },
+#endif
+ { " Fixit", "Repair mode with CDROM or fixit floppy.", NULL, dmenuSubmenu, NULL, &MenuFixit },
+ { " FTP sites", "The FTP mirror site listing.", NULL, dmenuSubmenu, NULL, &MenuMediaFTP },
+ { " Gateway", "Set flag to route packets between interfaces.", dmenuVarCheck, dmenuToggleVariable, NULL, "gateway=YES" },
+ { " HTML Docs", "The HTML documentation menu", NULL, docBrowser },
+ { " inetd Configuration", "Configure inetd and simple internet services.", dmenuVarCheck, configInetd, NULL, "inetd_enable=YES" },
+ { " Install, Standard", "A standard system installation.", NULL, installStandard },
+ { " Install, Express", "An express system installation.", NULL, installExpress },
+ { " Install, Custom", "The custom installation menu", NULL, dmenuSubmenu, NULL, &MenuInstallCustom },
+ { " Label", "The disk Label editor", NULL, diskLabelEditor },
+ { " Media", "Top level media selection menu.", NULL, dmenuSubmenu, NULL, &MenuMedia },
+ { " Media, Tape", "Select tape installation media.", NULL, mediaSetTape },
+ { " Media, NFS", "Select NFS installation media.", NULL, mediaSetNFS },
+ { " Media, Floppy", "Select floppy installation media.", NULL, mediaSetFloppy },
+ { " Media, CDROM/DVD", "Select CDROM/DVD installation media.", NULL, mediaSetCDROM },
+ { " Media, DOS", "Select DOS installation media.", NULL, mediaSetDOS },
+ { " Media, UFS", "Select UFS installation media.", NULL, mediaSetUFS },
+ { " Media, FTP", "Select FTP installation media.", NULL, mediaSetFTP },
+ { " Media, FTP Passive", "Select passive FTP installation media.", NULL, mediaSetFTPPassive },
+ { " Media, HTTP", "Select FTP via HTTP proxy installation media.", NULL, mediaSetHTTP },
+ { " Network Interfaces", "Configure network interfaces", NULL, tcpMenuSelect },
+ { " Networking Services", "The network services menu.", NULL, dmenuSubmenu, NULL, &MenuNetworking },
+ { " NFS, client", "Set NFS client flag.", dmenuVarCheck, dmenuToggleVariable, NULL, "nfs_client_enable=YES" },
+ { " NFS, server", "Set NFS server flag.", dmenuVarCheck, configNFSServer, NULL, "nfs_server_enable=YES" },
+ { " NTP Menu", "The NTP configuration menu.", NULL, dmenuSubmenu, NULL, &MenuNTP },
+ { " Options", "The options editor.", NULL, optionsEditor },
+ { " Packages", "The packages collection", NULL, configPackages },
+ { " Partition", "The disk Slice (PC-style partition) Editor", NULL, diskPartitionEditor },
+ { " PCNFSD", "Run authentication server for PC-NFS.", dmenuVarCheck, configPCNFSD, NULL, "pcnfsd" },
+ { " Root Password", "Set the system manager's password.", NULL, dmenuSystemCommand, NULL, "passwd root" },
+ { " Router", "Select routing daemon (default: routed)", NULL, configRouter, NULL, "router_enable" },
+ { " Security", "Configure system security options", NULL, dmenuSubmenu, NULL, &MenuSecurity },
+ { " 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 },
+ { " Syscons, Ttys", "The console terminal type menu.", NULL, dmenuSubmenu, NULL, &MenuSysconsTtys },
+ { " Time Zone", "Set the system's time zone.", NULL, dmenuSystemCommand, NULL, "tzsetup" },
+ { " TTYs", "Configure system ttys.", NULL, configEtcTtys, NULL, "ttys" },
+ { " Upgrade", "Upgrade an existing system.", NULL, installUpgrade },
+ { " Usage", "Quick start - How to use this menu system.", NULL, dmenuDisplayFile, NULL, "usage" },
+ { " User Management", "Add user and group information.", NULL, dmenuSubmenu, NULL, &MenuUsermgmt },
+ { " XFree86, Fonts", "XFree86 Font selection menu.", NULL, dmenuSubmenu, NULL, &MenuXF86SelectFonts },
+ { " XFree86, Server", "XFree86 Server selection menu.", NULL, dmenuSubmenu, NULL, &MenuXF86SelectServer },
+#if defined(__i386__) && defined(PC98)
+ { " XFree86, PC98 Server", "XFree86 PC98 Server selection menu.", NULL, dmenuSubmenu, NULL, &MenuXF86SelectPC98Server },
+#endif
+ { NULL } },
+};
+
+/* The initial installation menu */
+DMenu MenuInitial = {
+ DMENU_NORMAL_TYPE,
+ "sysinstall Main Menu", /* title */
+ "Welcome to the FreeBSD installation and configuration tool. Please\n" /* prompt */
+ "select one of the options below by using the arrow keys or typing the\n"
+ "first character of the option name you're interested in. Invoke an\n"
+ "option with [SPACE] or [ENTER]. To exit, use [TAB] to move to Exit.",
+ "Press F1 for Installation Guide", /* help line */
+ "INSTALL", /* help file */
+ { { "Select" },
+ { "X Exit Install", NULL, NULL, dmenuExit },
+ { " Usage", "Quick start - How to use this menu system", NULL, dmenuDisplayFile, NULL, "usage" },
+ { "Standard", "Begin a standard installation (recommended)", NULL, installStandard },
+ { "Express", "Begin a quick installation (for experts)", NULL, installExpress },
+ { " Custom", "Begin a custom installation (for experts)", NULL, dmenuSubmenu, NULL, &MenuInstallCustom },
+ { "Configure", "Do post-install configuration of FreeBSD", NULL, dmenuSubmenu, NULL, &MenuConfigure },
+ { "Doc", "Installation instructions, README, etc.", NULL, dmenuSubmenu, NULL, &MenuDocumentation },
+ { "Keymap", "Select keyboard type", NULL, dmenuSubmenu, NULL, &MenuSysconsKeymap },
+ { "Options", "View/Set various installation options", NULL, optionsEditor },
+ { "Fixit", "Repair mode with CDROM/DVD/floppy or start shell", NULL, dmenuSubmenu, NULL, &MenuFixit },
+ { "Upgrade", "Upgrade an existing system", NULL, installUpgrade },
+ { "Load Config","Load default install configuration", NULL, dispatch_load_floppy },
+ { "Index", "Glossary of functions", NULL, dmenuSubmenu, NULL, &MenuIndex },
+ { NULL } },
+};
+
+/* The main documentation menu */
+DMenu MenuDocumentation = {
+ DMENU_NORMAL_TYPE,
+ "FreeBSD Documentation Menu",
+ "If you are at all unsure about the configuration of your hardware\n"
+ "or are looking to build a system specifically for FreeBSD, read the\n"
+ "Hardware guide! New users should also read the Install document for\n"
+ "a step-by-step tutorial on installing FreeBSD. For general information,\n"
+ "consult the README file.",
+ "Confused? Press F1 for help.",
+ "usage",
+ { { "X Exit", "Exit this menu (returning to previous)", NULL, dmenuExit },
+ { "1 README", "A general description of FreeBSD. Read this!", NULL, dmenuDisplayFile, NULL, "README" },
+ { "2 Errata", "Late-breaking, post-release news.", NULL, dmenuDisplayFile, NULL, "ERRATA" },
+ { "3 Hardware", "The FreeBSD survival guide for PC hardware.", NULL, dmenuDisplayFile, NULL, "HARDWARE" },
+ { "4 Install", "A step-by-step guide to installing FreeBSD.", NULL, dmenuDisplayFile, NULL, "INSTALL" },
+ { "5 Copyright", "The FreeBSD Copyright notices.", NULL, dmenuDisplayFile, NULL, "COPYRIGHT" },
+ { "6 Release" ,"The release notes for this version of FreeBSD.", NULL, dmenuDisplayFile, NULL, "RELNOTES" },
+ { "7 Shortcuts", "Creating shortcuts to sysinstall.", NULL, dmenuDisplayFile, NULL, "shortcuts" },
+ { "8 HTML Docs", "Go to the HTML documentation menu (post-install).", NULL, docBrowser },
+ { NULL } },
+};
+
+DMenu MenuMouseType = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Select a protocol type for your mouse",
+ "If your mouse is attached to the PS/2 mouse port or the bus mouse port,\n"
+ "you should always choose \"Auto\", regardless of the model and the brand\n"
+ "of the mouse. All other protocol types are for serial mice and should\n"
+ "not be used with the PS/2 port mouse or the bus mouse. If you have\n"
+ "a serial mouse and are not sure about its protocol, you should also try\n"
+ "\"Auto\". It may not work for the serial mouse if the mouse does not\n"
+ "support the PnP standard. But, it won't hurt. Many 2-button serial mice\n"
+ "are compatible with \"Microsoft\" or \"MouseMan\". 3-button serial mice\n"
+ "may be compatible with \"MouseSystems\" or \"MouseMan\". If the serial\n"
+ "mouse has a wheel, it may be compatible with \"IntelliMouse\".",
+ NULL,
+ NULL,
+ { { "1 Auto", "Bus mouse, PS/2 style mouse or PnP serial mouse",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=auto" },
+ { "2 GlidePoint", "ALPS GlidePoint pad (serial)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=glidepoint" },
+ { "3 Hitachi","Hitachi tablet (serial)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=mmhittab" },
+ { "4 IntelliMouse", "Microsoft IntelliMouse (serial)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=intellimouse" },
+ { "5 Logitech", "Logitech protocol (old models) (serial)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=logitech" },
+ { "6 Microsoft", "Microsoft protocol (serial)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=microsoft" },
+ { "7 MM Series","MM Series protocol (serial)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=mmseries" },
+ { "8 MouseMan", "Logitech MouseMan/TrackMan models (serial)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=mouseman" },
+ { "9 MouseSystems", "MouseSystems protocol (serial)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=mousesystems" },
+ { "A ThinkingMouse","Kensington ThinkingMouse (serial)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=thinkingmouse" },
+ { NULL } },
+};
+
+DMenu MenuMousePort = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Select your mouse port from the following menu",
+ "The built-in pointing device of laptop/notebook computers is usually\n"
+ "a PS/2 style device.",
+ NULL,
+ NULL,
+ { { "1 PS/2", "PS/2 style mouse (/dev/psm0)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_PORT "=/dev/psm0" },
+ { "2 COM1", "Serial mouse on COM1 (/dev/cuaa0)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_PORT "=/dev/cuaa0" },
+ { "3 COM2", "Serial mouse on COM2 (/dev/cuaa1)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_PORT "=/dev/cuaa1" },
+ { "4 COM3", "Serial mouse on COM3 (/dev/cuaa2)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_PORT "=/dev/cuaa2" },
+ { "5 COM4", "Serial mouse on COM4 (/dev/cuaa3)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_PORT "=/dev/cuaa3" },
+ { "6 BusMouse", "Logitech, ATI or MS bus mouse (/dev/mse0)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_PORT "=/dev/mse0" },
+ { NULL } },
+};
+
+DMenu MenuMouse = {
+ DMENU_NORMAL_TYPE,
+ "Please configure your mouse",
+ "You can cut and paste text in the text console by running the mouse\n"
+ "daemon. Specify a port and a protocol type of your mouse and enable\n"
+ "the mouse daemon. If you don't want this feature, select 6 to disable\n"
+ "the daemon.\n"
+ "Once you've enabled the mouse daemon, you can specify \"/dev/sysmouse\"\n"
+ "as your mouse device and \"SysMouse\" or \"MouseSystems\" as mouse\n"
+ "protocol when running the X configuration utility (see Configuration\n"
+ "menu).",
+ NULL,
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)", NULL, dmenuExit },
+ { "2 Enable", "Test and run the mouse daemon", NULL, mousedTest, NULL, NULL },
+ { "3 Type", "Select mouse protocol type", NULL, dmenuSubmenu, NULL, &MenuMouseType },
+ { "4 Port", "Select mouse port", NULL, dmenuSubmenu, NULL, &MenuMousePort },
+ { "5 Flags", "Set additional flags", dmenuVarCheck, setMouseFlags,
+ NULL, VAR_MOUSED_FLAGS "=" },
+ { "6 Disable", "Disable the mouse daemon", NULL, mousedDisable, NULL, NULL },
+ { NULL } },
+};
+
+DMenu MenuMediaCDROM = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Choose a CD/DVD type",
+ "FreeBSD can be installed directly from a CD/DVD containing a valid\n"
+ "FreeBSD distribution. If you are seeing this menu it is because\n"
+ "more than one CD/DVD drive was found on your system. Please select one\n"
+ "of the following CD/DVD drives as your installation drive.",
+ "Press F1 to read the installation guide",
+ "INSTALL",
+ { { NULL } },
+};
+
+DMenu MenuMediaFloppy = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Choose a Floppy drive",
+ "You have more than one floppy drive. Please choose which drive\n"
+ "you would like to use.",
+ NULL,
+ NULL,
+ { { NULL } },
+};
+
+DMenu MenuMediaDOS = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Choose a DOS partition",
+ "FreeBSD can be installed directly from a DOS partition\n"
+ "assuming, of course, that you have copied the relevant\n"
+ "distributions into your DOS partition before starting this\n"
+ "installation. If this is not the case then you should reboot\n"
+ "DOS at this time and copy the distributions you wish to install\n"
+ "into a \"FREEBSD\" subdirectory on one of your DOS partitions.\n"
+ "Otherwise, please select the DOS partition containing the FreeBSD\n"
+ "distribution files.",
+ "Press F1 to read the installation guide",
+ "INSTALL",
+ { { NULL } },
+};
+
+DMenu MenuMediaFTP = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Please select a FreeBSD FTP distribution site",
+ "Please select the site closest to you or \"other\" if you'd like to\n"
+ "specify a different choice. Also note that not every site listed here\n"
+ "carries more than the base distribution kits. Only 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 "=ftp://ftp.freebsd.org" },
+ { "URL", "Specify some other ftp site by URL", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=other" },
+ { " 5.0 SNAP Server", "current.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://current.freebsd.org" },
+ { " 4.x SNAP Server", "releng4.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://releng4.freebsd.org" },
+ { " jp.FreeBSD.org SNAP Server", "snapshots.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://snapshots.jp.freebsd.org" },
+ { " IPv6 Ready", "ftp2.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.jp.freebsd.org" },
+ { " IPv6 Ready #2", "ftp7.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp7.jp.freebsd.org" },
+ { "Argentina", "ftp.ar.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.ar.freebsd.org" },
+ { " Australia", "ftp.au.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.au.freebsd.org" },
+ { " Australia #2","ftp2.au.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.au.freebsd.org" },
+ { " Australia #3","ftp3.au.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.au.freebsd.org" },
+ { " Australia #4","ftp4.au.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp4.au.freebsd.org" },
+ { " Australia #5","ftp5.au.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp5.au.freebsd.org" },
+ { "Brazil", "ftp.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.br.freebsd.org" },
+ { " Brazil #2", "ftp2.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.br.freebsd.org" },
+ { " Brazil #3", "ftp3.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.br.freebsd.org" },
+ { " Brazil #4", "ftp4.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp4.br.freebsd.org" },
+ { " Brazil #5", "ftp5.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp5.br.freebsd.org" },
+ { " Brazil #6", "ftp6.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp6.br.freebsd.org" },
+ { " Brazil #7", "ftp7.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp7.br.freebsd.org" },
+ { " Canada", "ftp.ca.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.ca.freebsd.org" },
+ { " Czech Republic", "ftp.cz.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.cz.freebsd.org" },
+ { "Denmark (Primary)", "ftp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.dk.freebsd.org" },
+ { " Denmark", "ftp.dk.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.dk.freebsd.org" },
+ { " Denmark #2", "ftp2.dk.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.dk.freebsd.org" },
+ { "Estonia", "ftp.ee.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.ee.freebsd.org" },
+ { "Finland", "ftp.fi.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.fi.freebsd.org" },
+ { " France", "ftp.fr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.fr.freebsd.org" },
+ { " France #1", "ftp.fr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.fr.freebsd.org" },
+ { " France #2", "ftp2.fr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.fr.freebsd.org" },
+ { " France #3", "ftp3.fr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.fr.freebsd.org" },
+ { " France #4", "ftp4.fr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp4.fr.freebsd.org" },
+ { " France #5", "ftp5.fr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp5.fr.freebsd.org" },
+ { " France #6", "ftp6.fr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp6.fr.freebsd.org" },
+ { " France #7", "ftp7.fr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp7.fr.freebsd.org" },
+ { " France #8", "ftp8.fr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp8.fr.freebsd.org" },
+ { "Germany", "ftp.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.de.freebsd.org" },
+ { " Germany #2", "ftp2.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.de.freebsd.org" },
+ { " Germany #3", "ftp3.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.de.freebsd.org" },
+ { " Germany #4", "ftp4.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp4.de.freebsd.org" },
+ { " Germany #5", "ftp5.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp5.de.freebsd.org" },
+ { " Germany #6", "ftp6.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp6.de.freebsd.org" },
+ { " Germany #7", "ftp7.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp7.de.freebsd.org" },
+ { " Greece", "ftp.gr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.gr.freebsd.org" },
+ { " Greece #2", "ftp2.gr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.gr.freebsd.org" },
+ { "Holland", "ftp.nl.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.nl.freebsd.org" },
+ { " Hong Kong", "ftp.hk.super.net", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.hk.super.net" },
+ { " Hungary", "ftp.hu.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.hu.freebsd.org" },
+ { "Iceland", "ftp.is.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.is.freebsd.org" },
+ { " Ireland", "ftp.ie.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.ie.freebsd.org" },
+ { " Israel", "ftp.il.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.il.freebsd.org" },
+ { " Israel #2", "ftp2.il.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.il.freebsd.org" },
+ { "Japan", "ftp.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.jp.freebsd.org" },
+ { " Japan #2", "ftp2.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.jp.freebsd.org" },
+ { " Japan #3", "ftp3.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.jp.freebsd.org" },
+ { " Japan #4", "ftp4.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp4.jp.freebsd.org" },
+ { " Japan #5", "ftp5.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp5.jp.freebsd.org" },
+ { " Japan #6", "ftp6.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp6.jp.freebsd.org" },
+ { " Japan #7", "ftp7.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp7.jp.freebsd.org" },
+ { "Korea", "ftp.kr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.kr.freebsd.org" },
+ { " Korea #2", "ftp2.kr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.kr.freebsd.org" },
+ { " Korea #3", "ftp3.kr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.kr.freebsd.org" },
+ { " Korea #4", "ftp4.kr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp4.kr.freebsd.org" },
+ { " Korea #5", "ftp5.kr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp5.kr.freebsd.org" },
+ { "Lithuania", "ftp.lt.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.lt.freebsd.org" },
+ { "New Zealand", "ftp.nz.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.nz.freebsd.org" },
+ { "Norway", "ftp.no.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.no.freebsd.org" },
+ { "Poland", "ftp.pl.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.pl.freebsd.org" },
+ { " Portugal", "ftp.pt.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.pt.freebsd.org" },
+ { " Portugal #2", "ftp2.pt.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.pt.freebsd.org" },
+ { "Romania", "ftp.ro.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.ro.freebsd.org" },
+ { " Russia", "ftp.ru.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.ru.freebsd.org" },
+ { " Russia #2", "ftp2.ru.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.ru.freebsd.org" },
+ { " Russia #3", "ftp3.ru.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.ru.freebsd.org" },
+ { " Russia #4", "ftp4.ru.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp4.ru.freebsd.org" },
+ { "Slovak Republic", "ftp.sk.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.sk.freebsd.org" },
+ { "Slovenia", "ftp.si.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.si.freebsd.org" },
+ { " South Africa", "ftp.za.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.za.freebsd.org" },
+ { " South Africa #2", "ftp2.za.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.za.freebsd.org" },
+ { " South Africa #3", "ftp3.za.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.za.freebsd.org" },
+ { " South Africa #4", "ftp4.za.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp4.za.freebsd.org" },
+ { " Spain", "ftp.es.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.es.freebsd.org" },
+ { " Spain #2", "ftp2.es.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.es.freebsd.org" },
+ { " Spain #3", "ftp3.es.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.es.freebsd.org" },
+ { " Sweden", "ftp.se.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.se.freebsd.org" },
+ { " Sweden #2", "ftp2.se.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.se.freebsd.org" },
+ { " Sweden #3", "ftp3.se.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.se.freebsd.org" },
+ { "Taiwan", "ftp.tw.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.tw.freebsd.org" },
+ { " Taiwan #2", "ftp2.tw.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.tw.freebsd.org" },
+ { " Taiwan #3", "ftp3.tw.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.tw.freebsd.org" },
+ { " Taiwan #4", "ftp4.tw.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp4.tw.freebsd.org" },
+ { " Thailand", "ftp.nectec.or.th", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.nectec.or.th/pub/mirrors/FreeBSD/" },
+ { "UK", "ftp.uk.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.uk.freebsd.org" },
+ { " UK #2", "ftp2.uk.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.uk.freebsd.org" },
+ { " UK #3", "ftp3.uk.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.uk.freebsd.org" },
+ { " UK #4", "ftp4.uk.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp4.uk.freebsd.org" },
+ { " UK #5", "ftp5.uk.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp5.uk.freebsd.org" },
+ { " Ukraine", "ftp.ua.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.ua.freebsd.org" },
+ { " Ukraine #2", "ftp2.ua.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.ua.freebsd.org" },
+ { " Ukraine #3", "ftp3.ua.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.ua.freebsd.org" },
+ { " USA #2", "ftp2.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.freebsd.org" },
+ { " USA #3", "ftp3.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.freebsd.org" },
+ { " USA #4", "ftp4.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp4.freebsd.org" },
+ { " USA #5", "ftp5.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp5.freebsd.org" },
+ { " USA #6", "ftp6.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=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 } },
+};
+
+/* Prototype KLD load menu */
+DMenu MenuKLD = {
+ DMENU_NORMAL_TYPE,
+ "KLD Menu",
+ "Load a KLD from a floppy\n",
+ NULL,
+ NULL,
+ { { NULL } },
+};
+
+/* The media selection menu */
+DMenu MenuMedia = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Choose Installation Media",
+ "FreeBSD can be installed from a variety of different installation\n"
+ "media, ranging from floppies to an Internet FTP server. If you're\n"
+ "installing FreeBSD from a supported CD/DVD drive then this is generally\n"
+ "the best media to use if you have no overriding reason for using other\n"
+ "media.",
+ "Press F1 for more information on the various media types",
+ "media",
+ { { "1 CD/DVD", "Install from a FreeBSD CD/DVD", NULL, mediaSetCDROM },
+ { "2 FTP", "Install from an FTP server", NULL, mediaSetFTPActive },
+ { "3 FTP Passive", "Install from an FTP server through a firewall", NULL, mediaSetFTPPassive },
+ { "4 HTTP", "Install from an FTP server through a http proxy", NULL, mediaSetHTTP },
+ { "5 DOS", "Install from a DOS partition", NULL, mediaSetDOS },
+ { "6 NFS", "Install over NFS", NULL, mediaSetNFS },
+ { "7 File System", "Install from an existing filesystem", NULL, mediaSetUFS },
+ { "8 Floppy", "Install from a floppy disk set", NULL, mediaSetFloppy },
+ { "9 Tape", "Install from SCSI or QIC tape", NULL, mediaSetTape },
+ { "X Options", "Go to the Options screen", NULL, optionsEditor },
+ { NULL } },
+};
+
+/* The distributions menu */
+DMenu MenuDistributions = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "Choose Distributions",
+ "As a convenience, we provide several \"canned\" distribution sets.\n"
+ "These select what we consider to be the most reasonable defaults for the\n"
+ "type of system in question. If you would prefer to pick and choose the\n"
+ "list of distributions yourself, simply select \"Custom\". You can also\n"
+ "pick a canned distribution set and then fine-tune it with the Custom item.\n\n"
+ "Choose an item by pressing [SPACE] or [ENTER]. When finished, choose the\n"
+ "Exit item or move to the OK button with [TAB].",
+ "Press F1 for more information on these options.",
+ "distributions",
+ { { "X Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { "All", "All system sources, binaries and X Window System)",
+ checkDistEverything, distSetEverything, NULL, NULL, ' ', ' ', ' ' },
+ { "Reset", "Reset selected distribution list to nothing",
+ NULL, distReset, NULL, NULL, ' ', ' ', ' ' },
+ { "4 Developer", "Full sources, binaries and doc but no games",
+ checkDistDeveloper, distSetDeveloper },
+ { "5 X-Developer", "Same as above + X Window System",
+ checkDistXDeveloper, distSetXDeveloper },
+ { "6 Kern-Developer", "Full binaries and doc, kernel sources only",
+ checkDistKernDeveloper, distSetKernDeveloper },
+ { "7 X-Kern-Developer", "Same as above + X Window System",
+ checkDistXKernDeveloper, distSetXKernDeveloper },
+ { "8 User", "Average user - binaries and doc only",
+ checkDistUser, distSetUser },
+ { "9 X-User", "Same as above + X Window System",
+ checkDistXUser, distSetXUser },
+ { "A Minimal", "The smallest configuration possible",
+ checkDistMinimum, distSetMinimum },
+ { "B Custom", "Specify your own distribution set",
+ NULL, dmenuSubmenu, NULL, &MenuSubDistributions, '>', '>', '>' },
+ { NULL } },
+};
+
+DMenu MenuSubDistributions = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "Select the distributions you wish to install.",
+ "Please check off the distributions you wish to install. At the\n"
+ "very minimum, this should be \"base\".",
+ NULL,
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { "All", "All system sources, binaries and X Window System",
+ NULL, distSetEverything, NULL, NULL, ' ', ' ', ' ' },
+ { "Reset", "Reset all of the below",
+ NULL, distReset, NULL, NULL, ' ', ' ', ' ' },
+ { " base", "Binary base distribution (required)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_BASE },
+#ifdef __i386__
+ { " compat1x", "FreeBSD 1.x binary compatibility",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_COMPAT1X },
+ { " compat20", "FreeBSD 2.0 binary compatibility",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_COMPAT20 },
+ { " compat21", "FreeBSD 2.1 binary compatibility",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_COMPAT21 },
+ { " compat22", "FreeBSD 2.2.x and 3.0 a.out binary compatibility",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_COMPAT22 },
+#if __FreeBSD__ > 3
+ { " compat3x", "FreeBSD 3.x binary compatibility",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_COMPAT3X },
+#endif
+#if __FreeBSD__ >= 4
+ { " compat4x", "FreeBSD 4.x binary compatibility",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_COMPAT4X },
+#endif
+#endif
+ { " crypto", "Basic encryption services",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CRYPTODists, '[', 'X', ']', DIST_CRYPTO_CRYPTO, },
+#if __FreeBSD__ <= 3
+ { " krb", "KerberosIV authentication services",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CRYPTODists, '[', 'X', ']', DIST_CRYPTO_KERBEROS },
+#else
+ { " krb4", "KerberosIV authentication services",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CRYPTODists, '[', 'X', ']', DIST_CRYPTO_KERBEROS4 },
+ { " krb5", "Kerberos5 authentication services",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CRYPTODists, '[', 'X', ']', DIST_CRYPTO_KERBEROS5 },
+#endif
+ { " dict", "Spelling checker dictionary files",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_DICT },
+ { " doc", "Miscellaneous FreeBSD online docs",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_DOC },
+ { " games", "Games (non-commercial)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_GAMES },
+ { " info", "GNU info files",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_INFO },
+ { " man", "System manual pages - recommended",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_MANPAGES },
+ { " catman", "Preformatted system manual pages",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_CATPAGES },
+ { " proflibs", "Profiled versions of the libraries",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_PROFLIBS },
+ { " src", "Sources for everything",
+ srcFlagCheck, distSetSrc },
+ { " ports", "The FreeBSD Ports collection",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_PORTS },
+ { " local", "Local additions collection",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_LOCAL},
+ { " XFree86", "The XFree86 distribution",
+#ifdef X_AS_PKG
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_XF86 },
+#else
+ x11FlagCheck, distSetXF86 },
+#endif
+ { NULL } },
+};
+
+DMenu MenuSrcDistributions = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "Select the sub-components of src you wish to install.",
+ "Please check off those portions of the FreeBSD source tree\n"
+ "you wish to install.",
+ NULL,
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { "All", "Select all of the below",
+ NULL, setSrc, NULL, NULL, ' ', ' ', ' ' },
+ { "Reset", "Reset all of the below",
+ NULL, clearSrc, NULL, NULL, ' ', ' ', ' ' },
+ { " base", "top-level files in /usr/src",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_BASE },
+ { " contrib", "/usr/src/contrib (contributed software)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_CONTRIB },
+ { " gnu", "/usr/src/gnu (software from the GNU Project)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_GNU },
+ { " etc", "/usr/src/etc (miscellaneous system files)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_ETC },
+ { " games", "/usr/src/games (the obvious!)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_GAMES },
+ { " include", "/usr/src/include (header files)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_INCLUDE },
+ { " lib", "/usr/src/lib (system libraries)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_LIB },
+ { " libexec", "/usr/src/libexec (system programs)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_LIBEXEC },
+ { " release", "/usr/src/release (release-generation tools)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_RELEASE },
+ { " bin", "/usr/src/bin (system binaries)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_BIN },
+ { " sbin", "/usr/src/sbin (system binaries)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_SBIN },
+ { " scrypto", "/usr/src/crypto (contrib encryption sources)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CRYPTODists, '[', 'X', ']', DIST_CRYPTO_SCRYPTO },
+ { " share", "/usr/src/share (documents and shared files)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_SHARE },
+ { " skrb4", "/usr/src/kerberosIV (sources for KerberosIV)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CRYPTODists, '[', 'X', ']', DIST_CRYPTO_SKERBEROS4 },
+ { " skrb5", "/usr/src/kerberos5 (sources for Kerberos5)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CRYPTODists, '[', 'X', ']', DIST_CRYPTO_SKERBEROS5 },
+ { " ssecure", "/usr/src/secure (BSD encryption sources)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CRYPTODists, '[', 'X', ']', DIST_CRYPTO_SSECURE },
+ { " sys", "/usr/src/sys (FreeBSD kernel)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_SYS },
+ { " tools", "/usr/src/tools (miscellaneous tools)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_TOOLS },
+ { " ubin", "/usr/src/usr.bin (user binaries)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_UBIN },
+ { " usbin", "/usr/src/usr.sbin (aux system binaries)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_USBIN },
+ { NULL } },
+};
+
+DMenu MenuXF86Config = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Please select the XFree86 configuration tool you want to use.",
+ "The first option, xf86cfg, is fully graphical.\n"
+ "The second option provides a menu-based interface similar to\n"
+ "what you are currently using. "
+ "The third option, xf86config, is\n"
+ "a more simplistic shell-script based tool and less friendly to\n"
+ "new users, but it may work in situations where the other options\n"
+ "do not.",
+ NULL,
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)",
+ NULL, dmenuExit },
+ { "2 xf86cfg", "Fully graphical XFree86 configuration tool.",
+ NULL, dmenuSetVariable, NULL, VAR_XF86_CONFIG "=xf86cfg" },
+ { "3 xf86cfg -textmode", "ncurses-based XFree86 configuration tool.",
+ NULL, dmenuSetVariable, NULL, VAR_XF86_CONFIG "=xf86cfg -textmode" },
+ { "4 xf86config", "Shell-script based XFree86 configuration tool.",
+ NULL, dmenuSetVariable, NULL, VAR_XF86_CONFIG "=xf86config" },
+ { "D XDesktop", "X already set up, just do desktop configuration.",
+ NULL, dmenuSubmenu, NULL, &MenuXDesktops },
+ { NULL } },
+};
+
+DMenu MenuXDesktops = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Please select the default X desktop to use.",
+ "By default, XFree86 comes with a fairly vanilla desktop which\n"
+ "is based around the twm(1) window manager and does not offer\n"
+ "much in the way of features. It does have the advantage of\n"
+ "being a standard part of X so you don't need to load anything\n"
+ "extra in order to use it. If, however, you have access to a\n"
+ "reasonably full packages collection on your installation media,\n"
+ "you can choose any one of the following desktops as alternatives.",
+ NULL,
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)",
+ NULL, dmenuExit },
+ { "2 KDE", "The K Desktop Environment.",
+ NULL, dmenuSetVariable, NULL, VAR_DESKSTYLE "=kde" },
+ { "3 GNOME + Sawfish", "GNOME + Sawfish window manager.",
+ NULL, dmenuSetVariable, NULL, VAR_DESKSTYLE "=gnome" },
+ { "4 GNOME + Enlightenment","GNOME + The E window manager",
+ NULL, dmenuSetVariable, NULL, VAR_DESKSTYLE "=enlightenment" },
+ { "5 Afterstep", "The Afterstep window manager",
+ NULL, dmenuSetVariable, NULL, VAR_DESKSTYLE "=afterstep" },
+ { "6 Windowmaker", "The Windowmaker window manager",
+ NULL, dmenuSetVariable, NULL, VAR_DESKSTYLE "=windowmaker" },
+ { "7 fvwm", "The fvwm window manager",
+ NULL, dmenuSetVariable, NULL, VAR_DESKSTYLE "=fvwm2" },
+ { NULL } },
+};
+
+DMenu MenuXF86Select = {
+ DMENU_NORMAL_TYPE,
+ "XFree86 Distribution",
+ "Please select the components you need from the XFree86\n"
+ "distribution sets.",
+ NULL,
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)", NULL, dmenuExit },
+ { "Basic", "Basic component menu (required)", NULL, dmenuSubmenu, NULL, &MenuXF86SelectCore },
+ { "Server", "X server menu", NULL, dmenuSubmenu, NULL, &MenuXF86SelectServer },
+ { "Fonts", "Font set menu", NULL, dmenuSubmenu, NULL, &MenuXF86SelectFonts },
+ { NULL } },
+};
+
+DMenu MenuXF86SelectCore = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "XFree86 base distribution types",
+ "Please check off the basic XFree86 components you wish to install.\n"
+ "Bin, lib, and set are recommended for a minimum installaion.",
+ NULL,
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { "All", "Select all below",
+ NULL, setX11Misc, NULL, NULL, ' ', ' ', ' ' },
+ { "Reset", "Reset all below",
+ NULL, clearX11Misc, NULL, NULL, ' ', ' ', ' ' },
+ { " bin", "Client applications and shared libs",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_BIN },
+ { " lib", "Data files needed at runtime",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_LIB },
+ { " cfg", "Configuration files",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_CFG },
+ { " set", "XFree86 Setup Utility",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_SET },
+ { " man", "Manual pages",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_MAN },
+ { " 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 },
+ { " lkit", "Server link kit for all other machines",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_LKIT },
+ { " prog", "Programmer's header and library files",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_PROG },
+#if defined(__i386__) && defined(PC98)
+ { " 9set", "XFree86 Setup Utility for PC98 machines",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_9SET },
+ { " lk98", "Server link kit for PC98 machines",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_LKIT98 },
+#endif
+ { NULL } },
+};
+
+DMenu MenuXF86SelectFonts = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "Font distribution selection.",
+ "Please check off the individual font distributions you wish to\n\
+install. At the minimum, you should install the standard\n\
+75 DPI and misc fonts if you're also installing a server\n\
+(these are selected by default).",
+ NULL,
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { "All", "All fonts",
+ NULL, setX11Fonts, NULL, NULL, ' ', ' ', ' ' },
+ { "Reset", "Reset font selections",
+ NULL, clearX11Fonts, NULL, NULL, ' ', ' ', ' ' },
+ { " fnts", "Standard 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 },
+ { 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.",
+ NULL,
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { "All", "Select all of the above",
+ NULL, setX11Servers, NULL, NULL, ' ', ' ', ' ' },
+ { "Reset", "Reset all of the above",
+ NULL, clearX11Servers, NULL, NULL, ' ', ' ', ' ' },
+ { " 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 },
+ { " 3DL", "8, 16 and 24 bit color 3D Labs boards",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_3DL },
+ { " 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 },
+#if defined(__i386__) && defined(PC98)
+ { " PC98", "Select an X server for a NEC PC98 [Submenu]",
+ NULL, dmenuSubmenu, NULL, &MenuXF86SelectPC98Server, '>', ' ', '>', 0 },
+#elif __alpha__
+ { " TGA", "TGA cards (alpha architecture only)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_TGA },
+#endif
+ { NULL } },
+};
+
+#if defined(__i386__) && defined(PC98)
+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).",
+ NULL,
+ NULL,
+ { { " 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 },
+ { NULL } }
+};
+#endif
+
+DMenu MenuDiskDevices = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "Select Drive(s)",
+ "Please select the drive, or drives, on which you wish to perform\n"
+ "this operation. If you are attempting to install a boot partition\n"
+ "on a drive other than the first one or have multiple operating\n"
+ "systems on your machine, you will have the option to install a boot\n"
+ "manager later. To select a drive, use the arrow keys to move to it\n"
+ "and press [SPACE] or [ENTER]. To de-select it, press it again.\n\n"
+ "Use [TAB] to get to the buttons and leave this menu.",
+ "Press F1 for important information regarding disk geometry!",
+ "drives",
+ { { NULL } },
+};
+
+DMenu MenuHTMLDoc = {
+ DMENU_NORMAL_TYPE,
+ "Select HTML Documentation pointer",
+ "Please select the body of documentation you're interested in, the main\n"
+ "ones right now being the FAQ and the Handbook. You can also choose \"other\"\n"
+ "to enter an arbitrary URL for browsing.",
+ "Press F1 for more help on what you see here.",
+ "html",
+ { { "X Exit", "Exit this menu (returning to previous)", NULL, dmenuExit },
+ { "2 Handbook", "The FreeBSD Handbook.", NULL, docShowDocument },
+ { "3 FAQ", "The Frequently Asked Questions guide.", NULL, docShowDocument },
+ { "4 Home", "The Home Pages for the FreeBSD Project (requires net)", NULL, docShowDocument },
+ { "5 Other", "Enter a URL.", NULL, docShowDocument },
+ { NULL } },
+};
+
+/* The main installation menu */
+DMenu MenuInstallCustom = {
+ DMENU_NORMAL_TYPE,
+ "Choose Custom Installation Options",
+ "This is the custom installation menu. You may use this menu to specify\n"
+ "details on the type of distribution you wish to have, where you wish\n"
+ "to install it from and how you wish to allocate disk storage to FreeBSD.",
+ "Press F1 to read the installation guide",
+ "INSTALL",
+ { { "X Exit", "Exit this menu (returning to previous)", NULL, dmenuExit },
+ { "2 Options", "View/Set various installation options", NULL, optionsEditor },
+#ifdef __alpha__
+ { "3 Label", "Label disk partitions", NULL, diskLabelEditor },
+ { "4 Distributions", "Select distribution(s) to extract", NULL, dmenuSubmenu, NULL, &MenuDistributions },
+ { "5 Media", "Choose the installation media type", NULL, dmenuSubmenu, NULL, &MenuMedia },
+ { "6 Commit", "Perform any pending Partition/Label/Extract actions", NULL, installCustomCommit },
+#else
+ { "3 Partition", "Allocate disk space for FreeBSD", NULL, diskPartitionEditor },
+ { "4 Label", "Label allocated disk partitions", NULL, diskLabelEditor },
+ { "5 Distributions", "Select distribution(s) to extract", NULL, dmenuSubmenu, NULL, &MenuDistributions },
+ { "6 Media", "Choose the installation media type", NULL, dmenuSubmenu, NULL, &MenuMedia },
+ { "7 Commit", "Perform any pending Partition/Label/Extract actions", NULL, installCustomCommit },
+#endif
+ { NULL } },
+};
+
+/* MBR type menu */
+DMenu MenuMBRType = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "overwrite me", /* will be disk specific label */
+ "FreeBSD comes with a boot selector that allows you to easily\n"
+ "select between FreeBSD and any other operating systems on your machine\n"
+ "at boot time. If you have more than one drive and want to boot\n"
+ "from the second one, the boot selector will also make it possible\n"
+ "to do so (limitations in the PC BIOS usually prevent this otherwise).\n"
+ "If you do not want a boot selector, or wish to replace an existing\n"
+ "one, select \"standard\". If you would prefer your Master Boot\n"
+ "Record to remain untouched then select \"None\".\n\n"
+ " NOTE: PC-DOS users will almost certainly require \"None\"!",
+ "Press F1 to read about drive setup",
+ "drives",
+ { { "BootMgr", "Install the FreeBSD Boot Manager",
+ dmenuRadioCheck, dmenuSetValue, NULL, &BootMgr },
+#ifndef PC98
+ { "Standard", "Install a standard MBR (no boot manager)",
+ dmenuRadioCheck, dmenuSetValue, NULL, &BootMgr, '(', '*', ')', 1 },
+#endif
+ { "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",
+ { { "X Exit", "Exit this menu (returning to previous)",
+ NULL, dmenuExit },
+ { " Distributions", "Install additional distribution sets",
+ NULL, distExtractAll },
+ { " Packages", "Install pre-packaged software for FreeBSD",
+ NULL, configPackages },
+ { " Root Password", "Set the system manager's password",
+ NULL, dmenuSystemCommand, NULL, "passwd root" },
+#ifdef __i386__
+ { " Fdisk", "The disk Slice (PC-style partition) Editor",
+ NULL, diskPartitionEditor },
+#endif
+ { " Label", "The disk Label editor",
+ NULL, diskLabelEditor },
+ { " User Management", "Add user and group information",
+ NULL, dmenuSubmenu, NULL, &MenuUsermgmt },
+ { " Console", "Customize system console behavior",
+ NULL, dmenuSubmenu, NULL, &MenuSyscons },
+ { " Time Zone", "Set which time zone you're in",
+ NULL, dmenuSystemCommand, NULL, "tzsetup" },
+ { " Media", "Change the installation media type",
+ NULL, dmenuSubmenu, NULL, &MenuMedia },
+ { " Mouse", "Configure your mouse",
+ NULL, dmenuSubmenu, NULL, &MenuMouse, NULL },
+ { " Networking", "Configure additional network services",
+ NULL, dmenuSubmenu, NULL, &MenuNetworking },
+ { " Security", "Configure system security options",
+ NULL, dmenuSubmenu, NULL, &MenuSecurity },
+ { " Startup", "Configure system startup options",
+ NULL, dmenuSubmenu, NULL, &MenuStartup },
+ { " TTYs", "Configure system ttys.",
+ NULL, configEtcTtys, NULL, "ttys" },
+ { " Options", "View/Set various installation options",
+ NULL, optionsEditor },
+ { " XFree86", "Configure XFree86 Server",
+ NULL, configXSetup },
+ { " Desktop", "Configure XFree86 Desktop",
+ NULL, configXDesktop },
+ { " HTML Docs", "Go to the HTML documentation menu (post-install)",
+ NULL, docBrowser },
+ { " Load KLD", "Load a KLD from a floppy",
+ NULL, kldBrowser },
+ { NULL } },
+};
+
+DMenu MenuStartup = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "Startup Services Menu",
+ "This menu allows you to configure various aspects of your system's\n"
+ "startup configuration. Use [SPACE] or [ENTER] to select items, and\n"
+ "[TAB] to move to the buttons. Select Exit to leave this menu.",
+ NULL,
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { " APM", "Auto-power management services (typically laptops)",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "apm_enable=YES" },
+#ifdef PCCARD_ARCH
+ { " pccard", "Enable PCCARD (AKA PCMCIA) services (also laptops)",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "pccard_enable=YES" },
+ { " pccard mem", "Set PCCARD memory address (if enabled)",
+ dmenuVarCheck, dmenuISetVariable, NULL, "pccard_mem" },
+ { " pccard ifconfig", "List of PCCARD ethernet devices to configure",
+ dmenuVarCheck, dmenuISetVariable, NULL, "pccard_ifconfig" },
+#endif
+ { " usbd", "Enable USB daemon (detect USB attach / detach)",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "usbd_enable=YES" },
+ { " usbd flags", "Set default flags to usbd (if enabled)",
+ dmenuVarCheck, dmenuISetVariable, NULL, "usbd_flags" },
+ { " ", " -- ", NULL, NULL, NULL, NULL, ' ', ' ', ' ' },
+ { " startup dirs", "Set the list of dirs to look for startup scripts",
+ dmenuVarCheck, dmenuISetVariable, NULL, "local_startup" },
+ { " named", "Run a local name server on this host",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "named_enable=YES" },
+ { " named flags", "Set default flags to named (if enabled)",
+ dmenuVarCheck, dmenuISetVariable, NULL, "named_flags" },
+ { " nis client", "This host wishes to be an NIS client.",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "nis_client_enable=YES" },
+ { " nis domainname", "Set NIS domainname (if enabled)",
+ dmenuVarCheck, dmenuISetVariable, NULL, "nisdomainname" },
+ { " nis server", "This host wishes to be an NIS server.",
+ dmenuVarCheck, 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" },
+#ifdef __i386__
+ { " linux", "This host wants to be able to run linux binaries.",
+ dmenuVarCheck, configLinux, NULL, VAR_LINUX_ENABLE "=YES" },
+ { " SVR4", "This host wants to be able to run SVR4 binaries.",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "svr4_enable=YES" },
+ { " SCO", "This host wants to be able to run IBCS2 binaries.",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "ibcs2_enable=YES" },
+#elif __alpha__
+ { " OSF/1", "This host wants to be able to run DEC OSF/1 binaries.",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "osf1_enable=YES" },
+#endif
+ { " quotas", "This host wishes to check quotas on startup.",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "check_quotas=YES" },
+ { NULL } },
+};
+
+DMenu MenuNetworking = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "Network Services Menu",
+ "You may have already configured one network device (and the other\n"
+ "various hostname/gateway/name server parameters) in the process\n"
+ "of installing FreeBSD. This menu allows you to configure other\n"
+ "aspects of your system's network configuration.",
+ NULL,
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { " Interfaces", "Configure additional network interfaces",
+ NULL, tcpMenuSelect },
+ { " AMD", "This machine wants to run the auto-mounter service",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "amd_enable=YES" },
+ { " AMD Flags", "Set flags to AMD service (if enabled)",
+ dmenuVarCheck, dmenuISetVariable, NULL, "amd_flags" },
+ { " Anon FTP", "This machine wishes to allow anonymous FTP.",
+ dmenuVarCheck, configAnonFTP, NULL, "anon_ftp" },
+ { " Gateway", "This machine will route packets between interfaces",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "gateway_enable=YES" },
+ { " inetd", "This machine wants to run the inet daemon",
+ dmenuVarCheck, configInetd, NULL, "inetd_enable=YES" },
+ { " NFS client", "This machine will be an NFS client",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "nfs_client_enable=YES" },
+ { " NFS server", "This machine will be an NFS server",
+ dmenuVarCheck, configNFSServer, NULL, "nfs_server_enable=YES" },
+ { " Ntpdate", "Select a clock-synchronization server",
+ dmenuVarCheck, dmenuSubmenu, NULL, &MenuNTP, '[', 'X', ']', "ntpdate_enable=YES" },
+ { " PCNFSD", "Run authentication server for clients with PC-NFS.",
+ dmenuVarCheck, configPCNFSD, NULL, "pcnfsd" },
+ { " portmap", "This machine wants to run the portmapper daemon",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "portmap_enable=YES" },
+ { " Routed", "Select routing daemon (default: routed)",
+ dmenuVarCheck, configRouter, NULL, "router_enable=YES" },
+ { " Rwhod", "This machine wants to run the rwho daemon",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "rwhod_enable=YES" },
+ { " Sendmail", "This machine wants to run the sendmail daemon",
+ NULL, dmenuSubmenu, NULL, &MenuSendmail },
+ { " Sshd", "This machine wants to run the ssh daemon",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "sshd_enable=YES" },
+ { " TCP Extensions", "Allow RFC1323 and RFC1644 TCP extensions?",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "tcp_extensions=YES" },
+ { NULL } },
+};
+
+DMenu MenuSendmail = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Sendmail Invocation Selection",
+ "There are three options for invoking sendmail at startup.\n"
+ "Please select Yes if you want to use sendmail as your mail transfer\n"
+ "agent. Selecting No disables sendmail's network socket for incoming\n"
+ "email, but still enables sendmail for local and outbound mail.\n"
+ "None disables sendmail completely at startup and disables inbound,\n"
+ "outbound, and local mail. See /etc/mail/README for more\n"
+ "information.\n",
+ NULL,
+ NULL,
+ {
+ { " Yes", "Start sendmail",
+ dmenuVarCheck, dmenuSetVariable, NULL, "sendmail_enable=YES" },
+ { " No", "Start sendmail, but don't listen from network",
+ dmenuVarCheck, dmenuSetVariable, NULL, "sendmail_enable=NO" },
+ { " None", "Don't start any sendmail processes",
+ dmenuVarCheck, dmenuSetVariable, NULL, "sendmail_enable=NONE" },
+ { NULL } },
+};
+
+DMenu MenuNTP = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "NTPDATE Server Selection",
+ "There are a number of time synchronization servers available\n"
+ "for public use around the Internet. Please select one reasonably\n"
+ "close to you to have your system time synchronized accordingly.",
+ "These are the primary open-access NTP servers",
+ NULL,
+ { { "None", "No NTP server",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=NO,ntpdate_flags=none" },
+ { "Other", "Select a site not on this list",
+ dmenuVarsCheck, configNTP, NULL, NULL },
+ { "Argentina", "tick.nap.com.ar",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=tick.nap.com.ar" },
+ { "Argentina #2", "time.sinectis.com.ar",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=time.sinectis.com.ar" },
+ { "Argentina #3", "tock.nap.com.ar",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=tock.nap.com.ar" },
+ { "Australia", "augean.eleceng.adelaide.edu.au",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=augean.eleceng.adelaide.edu.au" },
+ { "Australia #2", "ntp.adelaide.edu.au",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.adelaide.edu.au" },
+ { "Australia #3", "ntp.saard.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.saard.net" },
+ { "Australia #4", "time.deakin.edu.au",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=time.deakin.edu.au" },
+ { "Australia #5", "time.esec.com.au",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=time.esec.com.au" },
+ { "Belgium", "ntp1.belbone.be",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp1.belbone.be" },
+ { "Belgium #2", "ntp2.belbone.be",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2.belbone.be" },
+ { "Brazil", "ntp.cais.rnp.br",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.cais.rnp.br" },
+ { "Brazil #2", "ntp.pop-df.rnp.br",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.pop-df.rnp.br" },
+ { "Brazil #3", "ntp.ufes.br",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.ufes.br" },
+ { "Brazil #4", "ntp1.pucpr.br",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp1.pucpr.br" },
+ { "Canada", "ntp.cpsc.ucalgary.ca",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.cpsc.ucalgary.ca" },
+ { "Canada #2", "ntp1.cmc.ec.gc.ca",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp1.cmc.ec.gc.ca" },
+ { "Canada #3", "ntp2.cmc.ec.gc.ca",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2.cmc.ec.gc.ca" },
+ { "Canada #4", "tick.utoronto.ca",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=tick.utoronto.ca" },
+ { "Canada #5", "time.chu.nrc.ca",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=time.chu.nrc.ca" },
+ { "Canada #6", "time.nrc.ca",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=time.nrc.ca" },
+ { "Canada #7", "timelord.uregina.ca",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=timelord.uregina.ca" },
+ { "Canada #8", "tock.utoronto.ca",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=tock.utoronto.ca" },
+ { "Czech", "ntp.karpo.cz",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.karpo.cz" },
+ { "Denmark", "clock.netcetera.dk",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=clock.netcetera.dk" },
+ { "Denmark", "clock2.netcetera.dk",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=clock2.netcetera.dk" },
+ { "Spain", "slug.ctv.es",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=slug.ctv.es" },
+ { "Finland", "tick.keso.fi",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=tick.keso.fi" },
+ { "Finland #2", "tock.keso.fi",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=tock.keso.fi" },
+ { "France", "ntp.obspm.fr",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.obspm.fr" },
+ { "France #2", "ntp.univ-lyon1.fr",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.univ-lyon1.fr" },
+ { "France #3", "ntp.via.ecp.fr",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.via.ecp.fr" },
+ { "Croatia", "zg1.ntp.carnet.hr",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=zg1.ntp.carnet.hr" },
+ { "Croatia #2", "zg2.ntp.carnet.hr",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=zg2.ntp.carnet.hr" },
+ { "Croatia #3", "st.ntp.carnet.hr",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=st.ntp.carnet.hr" },
+ { "Croatia #4", "ri.ntp.carnet.hr",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ri.ntp.carnet.hr" },
+ { "Croatia #5", "os.ntp.carnet.hr",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=os.ntp.carnet.hr" },
+ { "Hungary", "time.kfki.hu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=time.kfki.hu" },
+ { "Indonesia", "ntp.incaf.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.incaf.net" },
+ { "Ireland", "ntp.maths.tcd.ie",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.maths.tcd.ie" },
+ { "Italy", "ntps.net4u.it",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=" },
+ { "Japan", "ntp.cyber-fleet.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.cyber-fleet.net" },
+ { "Korea", "time.nuri.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=time.nuri.net" },
+ { "Mexico", "ntp2a.audiotel.com.mx",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2a.audiotel.com.mx" },
+ { "Mexico #2", "ntp2b.audiotel.com.mx",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2b.audiotel.com.mx" },
+ { "Mexico #3", "ntp2c.audiotel.com.mx",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2c.audiotel.com.mx" },
+ { "Nigeria", "ntp.supernet300.com",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.supernet300.com" },
+ { "Netherlands", "ntp1.theinternetone.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp1.theinternetone.net" },
+ { "Netherlands #2", "ntp2.theinternetone.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2.theinternetone.net" },
+ { "Netherlands #3", "ntp3.theinternetone.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp3.theinternetone.net" },
+ { "Norway", "fartein.ifi.uio.no",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=fartein.ifi.uio.no" },
+ { "Norway #2", "time.alcanet.no",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=time.alcanet.no" },
+ { "New Zealand", "ntp.massey.ac.nz",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.massey.ac.nz" },
+ { "New Zealand #2", "ntp.public.otago.ac.nz",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.public.otago.ac.nz" },
+ { "New Zealand #3", "tk1.ihug.co.nz",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=tk1.ihug.co.nz" },
+ { "New Zealand #4", "ntp.waikato.ac.nz",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.waikato.ac.nz" },
+ { "Poland", "info.cyf-kr.edu.pl",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=info.cyf-kr.edu.pl" },
+ { "Portugal", "bug.fe.up.pt",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=bug.fe.up.pt" },
+ { "Romania", "ntp.ip.ro",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.ip.ro" },
+ { "Russia", "ntp.psn.ru",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.psn.ru" },
+ { "Russia #2", "sign.chg.ru",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=sign.chg.ru" },
+ { "Sweden", "ntp.lth.se",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.lth.se" },
+ { "Singapore", "ntp.shim.org",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.shim.org" },
+ { "Slovenia", "calvus.rzs-hm.si",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=calvus.rzs-hm.si" },
+ { "Slovenia #2", "sizif.mf.uni-lj.si",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=sizif.mf.uni-lj.si" },
+ { "Slovenia #3", "ntp1.arnes.si",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp1.arnes.si" },
+ { "Slovenia #4", "ntp2.arnes.si",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2.arnes.si" },
+ { "Slovenia #5", "time.ijs.si",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=time.ijs.si" },
+ { "Scotland", "ntp.cs.strath.ac.uk",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.cs.strath.ac.uk" },
+ { "United Kingdom", "ntp.exnet.com",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.exnet.com" },
+ { "United Kingdom #2", "ntp0.uk.uu.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp0.uk.uu.net" },
+ { "United Kingdom #3", "ntp1.uk.uu.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp1.uk.uu.net" },
+ { "United Kingdom #4", "ntp2.uk.uu.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2.uk.uu.net" },
+ { "United Kingdom #5", "ntp2a.mcc.ac.uk",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2a.mcc.ac.uk" },
+ { "United Kingdom #6", "ntp2b.mcc.ac.uk",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2b.mcc.ac.uk" },
+ { "United Kingdom #7", "ntp2c.mcc.ac.uk",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2c.mcc.ac.uk" },
+ { "United Kingdom #8", "ntp2d.mcc.ac.uk",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2d.mcc.ac.uk" },
+ { "United Kingdom #9", "tick.tanac.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=tick.tanac.net" },
+ { "U.S. AR", "sushi.compsci.lyon.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=sushi.compsci.lyon.edu" },
+ { "U.S. AZ", "ntp.drydog.com",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.drydog.com" },
+ { "U.S. CA", "ntp.ucsd.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.ucsd.edu" },
+ { "U.S. CA #2", "ntp1.mainecoon.com",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp1.mainecoon.com" },
+ { "U.S. CA #3", "ntp2.mainecoon.com",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2.mainecoon.com" },
+ { "U.S. CA #4", "reloj.kjsl.com",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=reloj.kjsl.com" },
+ { "U.S. CA #5", "time.five-ten-sg.com",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=time.five-ten-sg.com" },
+ { "U.S. DE", "louie.udel.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=louie.udel.edu" },
+ { "U.S. GA", "ntp.shorty.com",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.shorty.com" },
+ { "U.S. GA #2", "rolex.usg.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=rolex.usg.edu" },
+ { "U.S. GA #3", "timex.usg.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=timex.usg.edu" },
+ { "U.S. IL", "ntp-0.cso.uiuc.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp-0.cso.uiuc.edu" },
+ { "U.S. IL #2", "ntp-1.cso.uiuc.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp-1.cso.uiuc.edu" },
+ { "U.S. IL #3", "ntp-1.mcs.anl.gov",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp-1.mcs.anl.gov" },
+ { "U.S. IL #4", "ntp-2.cso.uiuc.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp-2.cso.uiuc.edu" },
+ { "U.S. IL #5", "ntp-2.mcs.anl.gov",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp-2.mcs.anl.gov" },
+ { "U.S. IN", "gilbreth.ecn.purdue.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=gilbreth.ecn.purdue.edu" },
+ { "U.S. IN #2", "harbor.ecn.purdue.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=harbor.ecn.purdue.edu" },
+ { "U.S. IN #3", "molecule.ecn.purdue.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=molecule.ecn.purdue.edu" },
+ { "U.S. KS", "ntp1.kansas.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp1.kansas.net" },
+ { "U.S. KS #2", "ntp2.kansas.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2.kansas.net" },
+ { "U.S. MA", "ntp.ourconcord.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.ourconcord.net" },
+ { "U.S. MA #2", "timeserver.cs.umb.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=timeserver.cs.umb.edu" },
+ { "U.S. MN", "ns.nts.umn.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ns.nts.umn.edu" },
+ { "U.S. MN #2", "nss.nts.umn.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=nss.nts.umn.edu" },
+ { "U.S. MO", "time-ext.missouri.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=time-ext.missouri.edu" },
+ { "U.S. MT", "chronos1.umt.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=chronos1.umt.edu" },
+ { "U.S. MT #2", "chronos2.umt.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=chronos2.umt.edu" },
+ { "U.S. MT #3", "chronos3.umt.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=chronos3.umt.edu" },
+ { "U.S. NC", "clock1.unc.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=clock1.unc.edu" },
+ { "U.S. NV", "cuckoo.nevada.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=cuckoo.nevada.edu" },
+ { "U.S. NV #2", "tick.cs.unlv.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=tick.cs.unlv.edu" },
+ { "U.S. NV #3", "tock.cs.unlv.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=tock.cs.unlv.edu" },
+ { "U.S. NY", "clock.linuxshell.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=clock.linuxshell.net" },
+ { "U.S. NY #2", "ntp.ctr.columbia.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.ctr.columbia.edu" },
+ { "U.S. NY #3", "ntp0.cornell.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp0.cornell.edu" },
+ { "U.S. NY #4", "ntp1.mpis.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp1.mpis.net" },
+ { "U.S. NY #5", "ntp2.mpis.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2.mpis.net" },
+ { "U.S. NY #6", "sundial.columbia.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=sundial.columbia.edu" },
+ { "U.S. NY #7", "timex.cs.columbia.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=timex.cs.columbia.edu" },
+ { "U.S. OK", "constellation.ecn.uoknor.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=constellation.ecn.uoknor.edu" },
+ { "U.S. PA", "clock-1.cs.cmu.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=clock-1.cs.cmu.edu" },
+ { "U.S. PA #2", "clock-2.cs.cmu.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=clock-2.cs.cmu.edu" },
+ { "U.S. PA #3", "clock.psu.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=clock.psu.edu" },
+ { "U.S. PA #4", "fuzz.psc.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=fuzz.psc.edu" },
+ { "U.S. PA #5", "ntp-1.ece.cmu.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp-1.ece.cmu.edu" },
+ { "U.S. PA #6", "ntp-2.ece.cmu.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp-2.ece.cmu.edu" },
+ { "U.S. TX", "ntp.cox.smu.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.cox.smu.edu" },
+ { "U.S. TX #2", "ntp.fnbhs.com",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.fnbhs.com" },
+ { "U.S. TX #3", "ntp.tmc.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.tmc.edu" },
+ { "U.S. TX #4", "ntp5.tamu.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp5.tamu.edu" },
+ { "U.S. TX #5", "tick.greyware.com",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=tick.greyware.com" },
+ { "U.S. TX #6", "tock.greyware.com",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=tock.greyware.com" },
+ { "U.S. VA", "ntp-1.vt.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp-1.vt.edu" },
+ { "U.S. VA #2", "ntp-2.vt.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp-2.vt.edu" },
+ { "U.S. VA #3", "ntp.cmr.gov",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.cmr.gov" },
+ { "U.S. VT", "ntp0.state.vt.us",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp0.state.vt.us" },
+ { "U.S. VT #2", "ntp1.state.vt.us",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp1.state.vt.us" },
+ { "U.S. VT #3", "ntp2.state.vt.us",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2.state.vt.us" },
+ { "U.S. WA", "clock.tricity.wsu.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=clock.tricity.wsu.edu" },
+ { "U.S. WA #2", "ntp.tcp-udp.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.tcp-udp.net" },
+ { "U.S. WI", "ntp1.cs.wisc.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp1.cs.wisc.edu" },
+ { "U.S. WI #2", "ntp3.cs.wisc.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp3.cs.wisc.edu" },
+ { "Venezuela", "ntp.linux.org.ve",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.linux.org.ve" },
+ { "South Africa", "ntp.cs.unp.ac.za",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.cs.unp.ac.za" },
+ { NULL } },
+};
+
+DMenu MenuSyscons = {
+ DMENU_NORMAL_TYPE,
+ "System Console Configuration",
+ "The default system console driver for FreeBSD (syscons) has a\n"
+ "number of configuration options which may be set according to\n"
+ "your preference.\n\n"
+ "When you are done setting configuration options, select Cancel.",
+ "Configure your system console settings",
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)", NULL, dmenuExit },
+ { "2 Font", "Choose an alternate screen font", NULL, dmenuSubmenu, NULL, &MenuSysconsFont },
+ { "3 Keymap", "Choose an alternate keyboard map", NULL, dmenuSubmenu, NULL, &MenuSysconsKeymap },
+ { "4 Repeat", "Set the rate at which keys repeat", NULL, dmenuSubmenu, NULL, &MenuSysconsKeyrate },
+ { "5 Saver", "Configure the screen saver", NULL, dmenuSubmenu, NULL, &MenuSysconsSaver },
+ { "6 Screenmap", "Choose an alternate screenmap", NULL, dmenuSubmenu, NULL, &MenuSysconsScrnmap },
+ { "7 Ttys", "Choose console terminal type", NULL, dmenuSubmenu, NULL, &MenuSysconsTtys },
+ { NULL } },
+};
+
+#ifdef PC98
+DMenu MenuSysconsKeymap = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "System Console Keymap",
+ "The default system console driver for FreeBSD (syscons) defaults\n"
+ "to a standard \"PC-98x1\" keyboard map. Users may wish to choose\n"
+ "one of the other keymaps below.\n"
+ "Note that sysinstall itself only uses the part of the keyboard map\n"
+ "which is required to generate the ANSI character subset, but your\n"
+ "choice of keymap will also be saved for later (fuller) use.",
+ "Choose a keyboard map",
+ NULL,
+ { { "Japanese PC-98x1", "Japanese PC-98x1 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=jp.pc98" },
+ { " Japanese PC-98x1 (ISO)", "Japanese PC-98x1 (ISO) keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=jp.pc98.iso" },
+ { NULL } },
+};
+#else
+DMenu MenuSysconsKeymap = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "System Console Keymap",
+ "The default system console driver for FreeBSD (syscons) defaults\n"
+ "to a standard \"American\" keyboard map. Users in other countries\n"
+ "(or with different keyboard preferences) may wish to choose one of\n"
+ "the other keymaps below.\n"
+ "Note that sysinstall itself only uses the part of the keyboard map\n"
+ "which is required to generate the ANSI character subset, but your\n"
+ "choice of keymap will also be saved for later (fuller) use.",
+ "Choose a keyboard map",
+ NULL,
+ { { "Belgian", "Belgian ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=be.iso" },
+ { " Brazil CP850", "Brazil CP850 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=br275.cp850" },
+ { " Brazil ISO (accent)", "Brazil ISO keymap (accent keys)", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=br275.iso.acc" },
+ { " Brazil ISO", "Brazil ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=br275.iso" },
+ { " Bulgarian BDS", "Bulgarian BDS keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=bg.bds.ctrlcaps" },
+ { " Bulgarian Phonetic", "Bulgarian Phonetic keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=bg.phonetic.ctrlcaps" },
+ { " Croatian ISO", "Croatian ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=hr.iso" },
+ { " Czech ISO (accent)", "Czech ISO keymap (accent keys)", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=cs.latin2.qwertz" },
+ { "Danish CP865", "Danish Code Page 865 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=danish.cp865" },
+ { " Danish ISO", "Danish ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=danish.iso" },
+ { "Estonian ISO", "Estonian ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=estonian.iso" },
+ { " Estonian ISO 15", "Estonian ISO 8859-15 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=estonian.iso15" },
+ { " Estonian CP850", "Estonian Code Page 850 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=estonian.cp850" },
+ { "Finnish CP850","Finnish Code Page 850 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=finnish.cp850" },
+ { " Finnish ISO", "Finnish ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=finnish.iso" },
+ { " French ISO (accent)", "French ISO keymap (accent keys)", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=fr.iso.acc" },
+ { " French ISO", "French ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=fr.iso" },
+ { "German CP850", "German Code Page 850 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=german.cp850" },
+ { " German ISO", "German ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=german.iso" },
+ { "Hungarian 101", "Hungarian ISO keymap (101 key)", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=hu.iso2.101keys" },
+ { " Hungarian 102", "Hungarian ISO keymap (102 key)", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=hu.iso2.102keys" },
+ { "Icelandic (accent)", "Icelandic ISO keymap (accent keys)", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=icelandic.iso.acc" },
+ { " Icelandic", "Icelandic ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=icelandic.iso" },
+ { " Italian", "Italian ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=it.iso" },
+ { "Japanese 106", "Japanese 106 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=jp.106" },
+ { "Latin American", "Latin American ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=lat-amer" },
+ { "Norway ISO", "Norwegian ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=norwegian.iso" },
+ { "Polish ISO", "Polish ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=pl_PL.ISO8859-2" },
+ { " Portuguese (accent)", "Portuguese ISO keymap (accent keys)", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=pt.iso.acc" },
+ { " Portuguese", "Portuguese ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=pt.iso" },
+ { "Russia KOI8-R", "Russian KOI8-R keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=ru.koi8-r" },
+ { "Slovenian", "Slovenian ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=si.iso" },
+ { " Spanish (accent)", "Spanish ISO keymap (accent keys)", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=spanish.iso.acc" },
+ { " Spanish", "Spanish ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=spanish.iso" },
+ { " Swedish CP850", "Swedish Code Page 850 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=swedish.cp850" },
+ { " Swedish ISO", "Swedish ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=swedish.iso" },
+ { " Swiss French ISO (accent)", "Swiss French ISO keymap (accent keys)", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=swissfrench.iso.acc" },
+ { " Swiss French ISO", "Swiss French ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=swissfrench.iso" },
+ { " Swiss French CP850", "Swiss French Code Page 850 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=swissfrench.cp850" },
+ { " Swiss German ISO (accent)", "Swiss German ISO keymap (accent keys)", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=swissgerman.iso.acc" },
+ { " Swiss German ISO", "Swiss German ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=swissgerman.iso" },
+ { " Swiss German CP850", "Swiss German Code Page 850 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=swissgerman.cp850" },
+ { "UK CP850", "UK Code Page 850 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=uk.cp850" },
+ { " UK ISO", "UK ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=uk.iso" },
+ { " Ukrainian KOI8-U", "Ukrainian KOI8-U keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=ua.koi8-u" },
+ { " Ukrainian KOI8-U+KOI8-R", "Ukrainian KOI8-U+KOI8-R keymap (alter)", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=ua.koi8-u.shift.alt" },
+ { " USA CapsLock->Ctrl", "US standard (Caps as L-Control)", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=us.pc-ctrl" },
+ { " USA Dvorak", "US Dvorak keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=us.dvorak" },
+ { " USA Dvorak (left)", "US left handed Dvorak keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=us.dvorakl" },
+ { " USA Dvorak (right)", "US right handed Dvorak keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=us.dvorakr" },
+ { " USA Emacs", "US standard optimized for EMACS", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=us.emacs" },
+ { " USA ISO", "US ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=us.iso" },
+ { " USA UNIX", "US traditional UNIX-workstation", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=us.unix" },
+ { NULL } },
+};
+#endif
+
+DMenu MenuSysconsKeyrate = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "System Console Keyboard Repeat Rate",
+ "This menu allows you to set the speed at which keys repeat\n"
+ "when held down.",
+ "Choose a keyboard repeat rate",
+ NULL,
+ { { "Slow", "Slow keyboard repeat rate", dmenuVarCheck, dmenuSetVariable, NULL, "keyrate=slow" },
+ { "Normal", "\"Normal\" keyboard repeat rate", dmenuVarCheck, dmenuSetVariable, NULL, "keyrate=normal" },
+ { "Fast", "Fast keyboard repeat rate", dmenuVarCheck, dmenuSetVariable, NULL, "keyrate=fast" },
+ { "Default", "Use default keyboard repeat rate", dmenuVarCheck, dmenuSetVariable, NULL, "keyrate=NO" },
+ { NULL } },
+};
+
+DMenu MenuSysconsSaver = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "System Console Screen Saver",
+ "By default, the console driver will not attempt to do anything\n"
+ "special with your screen when it's idle. If you expect to leave your\n"
+ "monitor switched on and idle for long periods of time then you should\n"
+ "probably enable one of these screen savers to prevent phosphor burn-in.",
+ "Choose a nifty-looking screen saver",
+ NULL,
+ { { "1 Blank", "Simply blank the screen",
+ dmenuVarCheck, configSaver, NULL, "saver=blank" },
+ { "2 Daemon", "\"BSD Daemon\" animated screen saver (text)",
+ dmenuVarCheck, configSaver, NULL, "saver=daemon" },
+ { "3 Fade", "Fade out effect screen saver",
+ dmenuVarCheck, configSaver, NULL, "saver=fade" },
+ { "4 Fire", "Flames effect screen saver",
+ dmenuVarCheck, configSaver, NULL, "saver=fire" },
+ { "5 Green", "\"Green\" power saving mode (if supported by monitor)",
+ dmenuVarCheck, configSaver, NULL, "saver=green" },
+ { "6 Logo", "\"BSD Daemon\" animated screen saver (graphics)",
+ dmenuVarCheck, configSaver, NULL, "saver=logo" },
+ { "7 Rain", "Rain drops screen saver",
+ dmenuVarCheck, configSaver, NULL, "saver=rain" },
+ { "8 Snake", "Draw a FreeBSD \"snake\" on your screen",
+ dmenuVarCheck, configSaver, NULL, "saver=snake" },
+ { "9 Star", "A \"twinkling stars\" effect",
+ dmenuVarCheck, configSaver, NULL, "saver=star" },
+ { "Warp", "A \"stars warping\" effect",
+ dmenuVarCheck, configSaver, NULL, "saver=warp" },
+ { "Timeout", "Set the screen saver timeout interval",
+ NULL, configSaverTimeout, NULL, NULL, ' ', ' ', ' ' },
+ { NULL } },
+};
+
+DMenu MenuSysconsScrnmap = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "System Console Screenmap",
+ "Unless you load a specific font, most PC hardware defaults to\n"
+ "displaying characters in the IBM 437 character set. However,\n"
+ "in the Unix world, this character set is very rarely used. Most\n"
+ "Western European countries, for example, prefer ISO 8859-1.\n"
+ "American users won't notice the difference since the bottom half\n"
+ "of all these character sets is ANSI anyway.\n"
+ "If your hardware is capable of downloading a new display font,\n"
+ "you should probably choose that option. However, for hardware\n"
+ "where this is not possible (e.g. monochrome adapters), a screen\n"
+ "map will give you the best approximation that your hardware can\n"
+ "display at all.",
+ "Choose a screen map",
+ NULL,
+ { { "1 None", "No screenmap, don't touch font", dmenuVarCheck, dmenuSetVariable, NULL, "scrnmap=NO" },
+ { "2 ISO 8859-1 to IBM437", "W-Europe ISO 8859-1 to IBM 437 screenmap", dmenuVarCheck, dmenuSetVariable, NULL, "scrnmap=iso-8859-1_to_cp437" },
+ { "3 ISO 8859-7 to IBM437", "Greek ISO 8859-7 to IBM 437 screenmap", dmenuVarCheck, dmenuSetVariable, NULL, "scrnmap=iso-8859-7_to_cp437" },
+ { "4 US-ASCII to IBM437", "US-ASCII to IBM 437 screenmap", dmenuVarCheck, dmenuSetVariable, NULL, "scrnmap=us-ascii_to_cp437" },
+ { "5 KOI8-R to IBM866", "Russian KOI8-R to IBM 866 screenmap", dmenuVarCheck, dmenuSetVariable, NULL, "scrnmap=koi8-r2cp866" },
+ { "6 KOI8-U to IBM866u", "Ukrainian KOI8-U to IBM 866u screenmap", dmenuVarCheck, dmenuSetVariable, NULL, "scrnmap=koi8-u2cp866u" },
+ { NULL } },
+};
+
+DMenu MenuSysconsTtys = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "System Console Terminal Type",
+ "For various console encodings, a corresponding terminal type\n"
+ "must be chosen in /etc/ttys.\n\n"
+ "WARNING: For compatibility reasons, only entries starting with\n"
+ "ttyv and terminal types starting with cons[0-9] can be changed\n"
+ "via this menu.\n",
+ "Choose a terminal type",
+ NULL,
+ { { "1 None", "Don't touch anything", dmenuVarCheck, dmenuSetVariable, NULL, VAR_CONSTERM "=NO" },
+ { "2 IBM437 (VGA default)", "cons25", dmenuVarCheck, dmenuSetVariable, NULL, VAR_CONSTERM "=cons25" },
+ { "3 ISO 8859-1", "cons25l1", dmenuVarCheck, dmenuSetVariable, NULL, VAR_CONSTERM "=cons25l1" },
+ { "4 ISO 8859-2", "cons25l2", dmenuVarCheck, dmenuSetVariable, NULL, VAR_CONSTERM "=cons25l2" },
+ { "5 ISO 8859-7", "cons25l7", dmenuVarCheck, dmenuSetVariable, NULL, VAR_CONSTERM "=cons25l7" },
+ { "6 KOI8-R", "cons25r", dmenuVarCheck, dmenuSetVariable, NULL, VAR_CONSTERM "=cons25r" },
+ { "7 KOI8-U", "cons25u", dmenuVarCheck, dmenuSetVariable, NULL, VAR_CONSTERM "=cons25u" },
+ { "8 US-ASCII", "cons25w", dmenuVarCheck, dmenuSetVariable, NULL, VAR_CONSTERM "=cons25w" },
+ { NULL } },
+};
+
+DMenu MenuSysconsFont = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "System Console Font",
+ "Most PC hardware defaults to displaying characters in the\n"
+ "IBM 437 character set. However, in the Unix world, this\n"
+ "character set is very rarely used. Most Western European\n"
+ "countries, for example, prefer ISO 8859-1.\n"
+ "American users won't notice the difference since the bottom half\n"
+ "of all these charactersets is ANSI anyway. However, they might\n"
+ "want to load a font anyway to use the 30- or 50-line displays.\n"
+ "If your hardware is capable of downloading a new display font,\n"
+ "you can select the appropriate font below.",
+ "Choose a font",
+ NULL,
+ { { "1 None", "Use hardware default font", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=NO,font8x14=NO,font8x16=NO" },
+ { "2 IBM 437", "English and others, VGA default", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=cp437-8x8,font8x14=cp437-8x14,font8x16=cp437-8x16" },
+ { "3 IBM 850", "Western Europe, IBM encoding", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=cp850-8x8,font8x14=cp850-8x14,font8x16=cp850-8x16" },
+ { "4 IBM 865", "Norwegian, IBM encoding", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=cp865-8x8,font8x14=cp865-8x14,font8x16=cp865-8x16" },
+ { "5 IBM 866", "Russian, IBM encoding (use with KOI8-R screenmap)", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=cp866-8x8,font8x14=cp866-8x14,font8x16=cp866b-8x16,mousechar_start=3" },
+ { "6 IBM 866u", "Ukrainian, IBM encoding (use with KOI8-U screenmap)", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=cp866u-8x8,font8x14=cp866u-8x14,font8x16=cp866u-8x16,mousechar_start=3" },
+ { "7 IBM 1251", "Cyrillic, MS Windows encoding", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=cp1251-8x8,font8x14=cp1251-8x14,font8x16=cp1251-8x16,mousechar_start=3" },
+ { "8 ISO 8859-1", "Western Europe, ISO encoding", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=iso-8x8,font8x14=iso-8x14,font8x16=iso-8x16" },
+ { "9 ISO 8859-2", "Eastern Europe, ISO encoding", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=iso02-8x8,font8x14=iso02-8x14,font8x16=iso02-8x16" },
+ { "a ISO 8859-4", "Baltic, ISO encoding", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=iso04-8x8,font8x14=iso04-8x14,font8x16=iso04-8x16" },
+ { "b ISO 8859-7", "Greek, ISO encoding", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=iso07-8x8,font8x14=iso07-8x14,font8x16=iso07-8x16" },
+ { "c ISO 8859-8", "Hebrew, ISO encoding", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=iso08-8x8,font8x14=iso08-8x14,font8x16=iso08-8x16" },
+ { "d ISO 8859-15", "Europe, ISO encoding", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=iso15-8x8,font8x14=iso15-8x14,font8x16=iso15-8x16" },
+ { "e SWISS", "English, better resolution", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=swiss-8x8,font8x14=NO,font8x16=swiss-8x16" },
+ { NULL } },
+};
+
+DMenu MenuUsermgmt = {
+ DMENU_NORMAL_TYPE,
+ "User and group management",
+ "The submenus here allow to manipulate user groups and\n"
+ "login accounts.\n",
+ "Configure your user groups and users",
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)", NULL, dmenuExit },
+ { "User", "Add a new user to the system.", NULL, userAddUser },
+ { "Group", "Add a new user group to the system.", NULL, userAddGroup },
+ { NULL } },
+};
+
+DMenu MenuSecurity = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "System Security Options Menu",
+ "This menu allows you to configure aspects of the operating system security\n"
+ "policy. Please read the system documentation carefully before modifying\n"
+ "these settings, as they may cause service disruption if used improperly.\n"
+ "\n"
+ "Most settings will take affect only following a system reboot.",
+ "Configure system security options",
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { " Security Profile", "Select a security profile for the system",
+ NULL, configSecurityProfile },
+ { " LOMAC", "Use Low Watermark Mandatory Access Control at boot",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "lomac_enable=YES" },
+ { " NFS port", "Require that the NFS clients used reserved ports",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "nfs_reserved_port_only=YES" },
+ { NULL } },
+};
+
+DMenu MenuSecurityProfile = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Default system security profile",
+ "Each item in this list will set what it considers to\n"
+ "be \"appropriate\" values in that category for various\n"
+ "security-related knobs in /etc/rc.conf.",
+ "Select a canned security profile - F1 for help",
+ "security", /* help file */
+ { { "X Exit", "Exit this menu (returning to previous)", NULL, dmenuExit },
+ { "Medium", "Moderate security settings.", NULL, configSecurityModerate },
+ { "Extreme", "Very restrictive security settings.", NULL, configSecurityExtreme },
+ { NULL } },
+};
+
+DMenu MenuFixit = {
+ DMENU_NORMAL_TYPE,
+ "Please choose a fixit option",
+ "There are three ways of going into \"fixit\" mode:\n"
+ "- you can use the live filesystem CDROM/DVD, in which case there will be\n"
+ " full access to the complete set of FreeBSD commands and utilities,\n"
+ "- you can use the more limited (but perhaps customized) fixit floppy,\n"
+ "- or you can start an Emergency Holographic Shell now, which is\n"
+ " limited to the subset of commands that is already available right now.",
+ "Press F1 for more detailed repair instructions",
+ "fixit",
+{ { "X Exit", "Exit this menu (returning to previous)", NULL, dmenuExit },
+ { "2 CDROM/DVD", "Use the \"live\" filesystem CDROM/DVD", NULL, installFixitCDROM },
+ { "3 Floppy", "Use a floppy generated from the fixit image", NULL, installFixitFloppy },
+ { "4 Shell", "Start an Emergency Holographic Shell", NULL, installFixitHoloShell },
+ { NULL } },
+};
diff --git a/usr.sbin/sysinstall/misc.c b/usr.sbin/sysinstall/misc.c
new file mode 100644
index 0000000..0965828
--- /dev/null
+++ b/usr.sbin/sysinstall/misc.c
@@ -0,0 +1,484 @@
+/*
+ * Miscellaneous support routines..
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <sys/file.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/wait.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <ufs/ufs/ufsmount.h>
+#include <sys/reboot.h>
+#include <sys/disklabel.h>
+
+/* Quick check to see if a file is readable */
+Boolean
+file_readable(char *fname)
+{
+ if (!access(fname, F_OK))
+ return TRUE;
+ return FALSE;
+}
+
+/* Quick check to see if a file is executable */
+Boolean
+file_executable(char *fname)
+{
+ if (!access(fname, X_OK))
+ return TRUE;
+ return FALSE;
+}
+
+/* Concatenate two strings into static storage */
+char *
+string_concat(char *one, char *two)
+{
+ static char tmp[FILENAME_MAX];
+
+ /* Yes, we're deliberately cavalier about not checking for overflow */
+ strcpy(tmp, one);
+ strcat(tmp, two);
+ return tmp;
+}
+
+/* sane strncpy() function */
+char *
+sstrncpy(char *dst, const char *src, int size)
+{
+ dst[size] = '\0';
+ return strncpy(dst, src, size);
+}
+
+/* Concatenate three strings into static storage */
+char *
+string_concat3(char *one, char *two, char *three)
+{
+ static char tmp[FILENAME_MAX];
+
+ /* Yes, we're deliberately cavalier about not checking for overflow */
+ strcpy(tmp, one);
+ strcat(tmp, two);
+ strcat(tmp, three);
+ return tmp;
+}
+
+/* Clip the whitespace off the end of a string */
+char *
+string_prune(char *str)
+{
+ int len = str ? strlen(str) : 0;
+
+ while (len && isspace(str[len - 1]))
+ str[--len] = '\0';
+ return str;
+}
+
+/* run the whitespace off the front of a string */
+char *
+string_skipwhite(char *str)
+{
+ while (*str && isspace(*str))
+ ++str;
+ return str;
+}
+
+/* copy optionally and allow second arg to be null */
+char *
+string_copy(char *s1, char *s2)
+{
+ if (!s1)
+ return NULL;
+ if (!s2)
+ s1[0] = '\0';
+ else
+ strcpy(s1, s2);
+ return s1;
+}
+
+/* convert an integer to a string, using a static buffer */
+char *
+itoa(int value)
+{
+ static char buf[13];
+
+ snprintf(buf, 12, "%d", value);
+ return buf;
+}
+
+Boolean
+directory_exists(const char *dirname)
+{
+ DIR *tptr;
+
+ if (!dirname)
+ return FALSE;
+ if (!strlen(dirname))
+ return FALSE;
+
+ tptr = opendir(dirname);
+ if (!tptr)
+ return (FALSE);
+
+ closedir(tptr);
+ return (TRUE);
+}
+
+char *
+pathBaseName(const char *path)
+{
+ char *pt;
+ char *ret = (char *)path;
+
+ pt = strrchr(path,(int)'/');
+
+ if (pt != 0) /* if there is a slash */
+ {
+ ret = ++pt; /* start the file after it */
+ }
+
+ return(ret);
+}
+
+/* A free guaranteed to take NULL ptrs */
+void
+safe_free(void *ptr)
+{
+ if (ptr)
+ free(ptr);
+}
+
+/* A malloc that checks errors */
+void *
+safe_malloc(size_t size)
+{
+ void *ptr;
+
+ if (size <= 0)
+ msgFatal("Invalid malloc size of %ld!", (long)size);
+ ptr = malloc(size);
+ if (!ptr)
+ msgFatal("Out of memory!");
+ bzero(ptr, size);
+ return ptr;
+}
+
+/* A realloc that checks errors */
+void *
+safe_realloc(void *orig, size_t size)
+{
+ void *ptr;
+
+ if (size <= 0)
+ msgFatal("Invalid realloc size of %ld!", (long)size);
+ ptr = 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 = (long)aux;
+ return list;
+}
+
+/* Toss the items out */
+void
+items_free(dialogMenuItem *list, int *curr, int *max)
+{
+ int i;
+
+ for (i = 0; list[i].prompt; i++) {
+ safe_free(list[i].prompt);
+ safe_free(list[i].title);
+ }
+ safe_free(list);
+ *curr = *max = 0;
+}
+
+int
+Mkdir(char *ipath)
+{
+ struct stat sb;
+ int final;
+ char *p, *path;
+
+ if (file_readable(ipath) || Fake)
+ return DITEM_SUCCESS;
+
+ path = strcpy(alloca(strlen(ipath) + 1), ipath);
+ if (isDebug())
+ msgDebug("mkdir(%s)\n", path);
+ p = path;
+ if (p[0] == '/') /* Skip leading '/'. */
+ ++p;
+ for (final = FALSE; !final; ++p) {
+ if (p[0] == '\0' || (p[0] == '/' && p[1] == '\0'))
+ final = TRUE;
+ else if (p[0] != '/')
+ continue;
+ *p = '\0';
+ if (stat(path, &sb)) {
+ if (errno != ENOENT) {
+ msgConfirm("Couldn't stat directory %s: %s", path, strerror(errno));
+ return DITEM_FAILURE;
+ }
+ if (isDebug())
+ msgDebug("mkdir(%s..)\n", path);
+ if (mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO) < 0) {
+ msgConfirm("Couldn't create directory %s: %s", path,strerror(errno));
+ return DITEM_FAILURE;
+ }
+ }
+ *p = '/';
+ }
+ return DITEM_SUCCESS;
+}
+
+int
+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/modules.c b/usr.sbin/sysinstall/modules.c
new file mode 100644
index 0000000..b969ce4
--- /dev/null
+++ b/usr.sbin/sysinstall/modules.c
@@ -0,0 +1,205 @@
+/*-
+ * Copyright (c) 2000 "HOSOKAWA, Tatsumi" <hosokawa@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "sysinstall.h"
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/linker.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <fnmatch.h>
+
+/* Prototypes */
+static int kldModuleFire(dialogMenuItem *self);
+
+#define MODULESDIR "/stand/modules"
+#define DISTMOUNT "/dist"
+
+void
+moduleInitialize(void)
+{
+ int fd, len;
+ DIR *dirp;
+ struct dirent *dp;
+ char module[MAXPATHLEN], desc[MAXPATHLEN];
+ char desc_str[BUFSIZ];
+
+ if (!RunningAsInit && !Fake) {
+ /* It's not my job... */
+ return;
+ }
+
+ dirp = opendir(MODULESDIR);
+ if (dirp) {
+ while ((dp = readdir(dirp))) {
+ if (dp->d_namlen < (sizeof(".ko") - 1)) continue;
+ if (strcmp(dp->d_name + dp->d_namlen - (sizeof(".ko") - 1), ".ko") == 0) {
+ strcpy(module, MODULESDIR);
+ strcat(module, "/");
+ strcat(module, dp->d_name);
+ strcpy(desc, module);
+ len = strlen(desc);
+ strcpy(desc + (len - (sizeof(".ko") - 1)), ".dsc");
+ fd = open(module, O_RDONLY);
+ if (fd < 0) continue;
+ close(fd);
+ fd = open(desc, O_RDONLY);
+ if (fd < 0) {
+ desc_str[0] = 0;
+ }
+ else {
+ len = read(fd, desc_str, BUFSIZ);
+ close(fd);
+ if (len < BUFSIZ) desc_str[len] = 0;
+ }
+ if (desc_str[0])
+ msgDebug("Loading module %s (%s)\n", dp->d_name, desc_str);
+ else
+ msgDebug("Loading module %s\n", dp->d_name);
+ if (kldload(module) < 0 && errno != EEXIST) {
+ if (desc_str[0])
+ msgConfirm("Loading module %s failed\n%s", dp->d_name, desc_str);
+ else
+ msgConfirm("Loading module %s failed", dp->d_name);
+ }
+ }
+ }
+ closedir(dirp);
+ }
+}
+
+int
+kldBrowser(dialogMenuItem *self)
+{
+ DMenu *menu;
+ int i, what = DITEM_SUCCESS, msize, count;
+ DIR *dir;
+ struct dirent *de;
+ char *err;
+
+ err = NULL;
+
+ if (DITEM_STATUS(mediaSetFloppy(NULL)) == DITEM_FAILURE) {
+ msgConfirm("Unable to set media device to floppy.");
+ what |= DITEM_FAILURE;
+ mediaClose();
+ return what;
+ }
+
+ if (!DEVICE_INIT(mediaDevice)) {
+ msgConfirm("Unable to mount floppy filesystem.");
+ what |= DITEM_FAILURE;
+ mediaClose();
+ return what;
+ }
+
+ msize = sizeof(DMenu) + (sizeof(dialogMenuItem) * 2);
+ count = 0;
+ if ((menu = malloc(msize)) == NULL) {
+ err = "Failed to allocate memory for menu";
+ goto errout;
+ }
+
+ bcopy(&MenuKLD, menu, sizeof(DMenu));
+
+ bzero(&menu->items[count], sizeof(menu->items[0]));
+ menu->items[count].prompt = strdup("X Exit");
+ menu->items[count].title = strdup("Exit this menu (returning to previous)");
+ menu->items[count].fire = dmenuExit;
+ count++;
+
+ if ((dir = opendir(DISTMOUNT)) == NULL) {
+ err = "Couldn't open directory";
+ goto errout;
+ }
+
+ while ((de = readdir(dir)) != NULL) {
+ if (fnmatch("*.ko", de->d_name, FNM_CASEFOLD))
+ continue;
+
+ msize += sizeof(dialogMenuItem);
+ if ((menu = realloc(menu, msize)) == NULL) {
+ err = "Failed to allocate memory for menu item";
+ goto errout;
+ }
+
+ bzero(&menu->items[count], sizeof(menu->items[0]));
+ menu->items[count].fire = kldModuleFire;
+
+ menu->items[count].prompt = strdup(de->d_name);
+ menu->items[count].title = menu->items[count].prompt;
+
+ count++;
+ }
+
+ closedir(dir);
+
+ menu->items[count].prompt = NULL;
+ menu->items[count].title = NULL;
+
+ dmenuOpenSimple(menu, FALSE);
+
+ mediaClose();
+
+ deviceRescan();
+
+ errout:
+ for (i = 0; i < count; i++)
+ free(menu->items[i].prompt);
+
+ free(menu);
+
+ if (err != NULL) {
+ what |= DITEM_FAILURE;
+ if (!variable_get(VAR_NO_ERROR))
+ msgConfirm(err);
+ }
+
+ return (what);
+}
+
+static int
+kldModuleFire(dialogMenuItem *self) {
+ char fname[256];
+
+ bzero(fname, sizeof(fname));
+ snprintf(fname, sizeof(fname), "%s/%s", DISTMOUNT, self->prompt);
+
+ if (kldload(fname) < 0 && errno != EEXIST) {
+ if (!variable_get(VAR_NO_ERROR))
+ msgConfirm("Loading module %s failed\n", fname);
+ } else {
+ if (!variable_get(VAR_NO_ERROR))
+ msgConfirm("Loaded module %s OK", fname);
+ }
+
+ return(0);
+ }
diff --git a/usr.sbin/sysinstall/mouse.c b/usr.sbin/sysinstall/mouse.c
new file mode 100644
index 0000000..574fd72
--- /dev/null
+++ b/usr.sbin/sysinstall/mouse.c
@@ -0,0 +1,103 @@
+/*-
+ * Copyright (c) 1998 Kazutaka YOKOTA (yokota@zodiac.mech.utsunomiya-u.ac.jp)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHRO AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHRO OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "sysinstall.h"
+#include <string.h>
+
+int
+mousedTest(dialogMenuItem *self)
+{
+ char *type;
+ char *port;
+ char *flags;
+ int ret;
+
+ type = variable_get(VAR_MOUSED_TYPE);
+ port = variable_get(VAR_MOUSED_PORT);
+ flags = variable_get(VAR_MOUSED_FLAGS);
+ if ((type == NULL) || (port == NULL)
+ || (strlen(type) <= 0) || (strlen(port) <= 0)
+ || (strcmp(type, "NO") == 0)) {
+ msgConfirm("Please select a mouse protocol and a port first.");
+ return DITEM_FAILURE;
+ }
+
+ msgNotify("Trying to start the mouse daemon...");
+ if (file_readable("/var/run/moused.pid"))
+ vsystem("kill `cat /var/run/moused.pid`");
+ systemExecute("vidcontrol -m on");
+ if (flags != NULL)
+ vsystem("moused -t %s -p %s %s", type, port, flags);
+ else
+ vsystem("moused -t %s -p %s", type, port);
+
+ ret = msgYesNo("Now move the mouse and see if it works.\n"
+ "(Note that buttons don't have any effect for now.)\n\n"
+ " Is the mouse cursor moving?\n");
+ systemExecute("vidcontrol -m off");
+ if (ret) {
+ if (file_readable("/var/run/moused.pid"))
+ vsystem("kill `cat /var/run/moused.pid`");
+ variable_set2(VAR_MOUSED, "NO", 1);
+ } else {
+ variable_set2(VAR_MOUSED, "YES", 1);
+ vsystem("ln -fs /dev/sysmouse /dev/mouse"); /* backwards compat */
+ }
+
+ return DITEM_SUCCESS | DITEM_RESTORE;
+}
+
+int
+mousedDisable(dialogMenuItem *self)
+{
+ if (file_readable("/var/run/moused.pid"))
+ vsystem("kill `cat /var/run/moused.pid`");
+ variable_set2(VAR_MOUSED, "NO", 1);
+ variable_set2(VAR_MOUSED_TYPE, "NO", 1);
+ variable_unset(VAR_MOUSED_PORT);
+ variable_unset(VAR_MOUSED_FLAGS);
+ msgConfirm("The mouse daemon is disabled.");
+ return DITEM_SUCCESS;
+}
+
+int
+setMouseFlags(dialogMenuItem *self)
+{
+ int ret;
+ ret = variable_get_value(VAR_MOUSED_FLAGS,
+ "Please Specify the mouse daemon flags. If you would like to\n"
+ "emulate 3 buttons, use -3 here.\n", 1)
+ ? DITEM_SUCCESS : DITEM_FAILURE;
+ if (ret != DITEM_SUCCESS)
+ variable_unset(VAR_MOUSED_FLAGS);
+ return ret;
+}
+
diff --git a/usr.sbin/sysinstall/msg.c b/usr.sbin/sysinstall/msg.c
new file mode 100644
index 0000000..4625ce2
--- /dev/null
+++ b/usr.sbin/sysinstall/msg.c
@@ -0,0 +1,356 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <stdarg.h>
+#include <sys/ioctl.h>
+#include <sys/consio.h>
+
+Boolean
+isDebug(void)
+{
+ char *cp;
+
+ return (cp = variable_get(VAR_DEBUG)) && strcmp(cp, "no");
+}
+
+/* Whack up an informational message on the status line, in stand-out */
+void
+msgYap(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ int attrs;
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ va_start(args, fmt);
+ vsnprintf(errstr, FILENAME_MAX, fmt, args);
+ va_end(args);
+ attrs = getattrs(stdscr);
+ attrset(A_REVERSE);
+ mvaddstr(StatusLine, 0, errstr);
+ attrset(attrs);
+ refresh();
+}
+
+/* Whack up an informational message on the status line */
+void
+msgInfo(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ int i, attrs;
+ char line[81];
+
+ attrs = getattrs(stdscr);
+ /* NULL is a special convention meaning "erase the old stuff" */
+ if (!fmt) {
+ move(StatusLine, 0);
+ clrtoeol();
+ return;
+ }
+ errstr = (char *)alloca(FILENAME_MAX);
+ va_start(args, fmt);
+ vsnprintf(errstr, FILENAME_MAX, fmt, args);
+ va_end(args);
+ memset(line, ' ', 80);
+ for (i = 0; i < 80; i++) {
+ if (errstr[i])
+ line[i] = errstr[i];
+ else
+ break;
+ }
+ line[80] = '\0';
+ attrset(ATTR_TITLE);
+ mvaddstr(StatusLine, 0, line);
+ attrset(attrs);
+ move(StatusLine, 79);
+ refresh();
+}
+
+/* Whack up a warning on the status line */
+void
+msgWarn(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ int attrs;
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ strcpy(errstr, "Warning: ");
+ va_start(args, fmt);
+ vsnprintf((char *)(errstr + strlen(errstr)), FILENAME_MAX, fmt, args);
+ va_end(args);
+ attrs = getattrs(stdscr);
+ beep();
+ attrset(ATTR_TITLE);
+ mvaddstr(StatusLine, 0, errstr);
+ attrset(attrs);
+ refresh();
+ if (OnVTY && isDebug())
+ msgDebug("Warning message `%s'\n", errstr);
+}
+
+/* Whack up an error on the status line */
+void
+msgError(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ int attrs;
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ strcpy(errstr, "Error: ");
+ va_start(args, fmt);
+ vsnprintf((char *)(errstr + strlen(errstr)), FILENAME_MAX, fmt, args);
+ va_end(args);
+ beep();
+ attrs = getattrs(stdscr);
+ attrset(ATTR_TITLE);
+ mvaddstr(StatusLine, 0, errstr);
+ attrset(attrs);
+ refresh();
+ if (OnVTY && isDebug())
+ msgDebug("Error message `%s'\n", errstr);
+}
+
+/* Whack up a fatal error on the status line */
+void
+msgFatal(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ int attrs;
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ strcpy(errstr, "Fatal Error: ");
+ va_start(args, fmt);
+ vsnprintf((char *)(errstr + strlen(errstr)), FILENAME_MAX, fmt, args);
+ va_end(args);
+ beep();
+ attrs = getattrs(stdscr);
+ attrset(ATTR_TITLE);
+ mvaddstr(StatusLine, 0, errstr);
+ addstr(" - ");
+ addstr("PRESS ANY KEY TO ");
+ if (getpid() == 1)
+ addstr("REBOOT");
+ else
+ addstr("QUIT");
+ attrset(attrs);
+ refresh();
+ if (OnVTY)
+ msgDebug("Fatal error `%s'!\n", errstr);
+ getch();
+ systemShutdown(1);
+}
+
+/* Put up a message in a popup confirmation box */
+void
+msgConfirm(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ WINDOW *w = savescr();
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ va_start(args, fmt);
+ vsnprintf(errstr, FILENAME_MAX, fmt, args);
+ va_end(args);
+ use_helpline(NULL);
+ use_helpfile(NULL);
+ if (OnVTY) {
+ ioctl(0, VT_ACTIVATE, 1);
+ msgInfo(NULL);
+ }
+ dialog_notify(errstr);
+ restorescr(w);
+}
+
+/* Put up a message in a popup information box */
+void
+msgNotify(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ va_start(args, fmt);
+ vsnprintf(errstr, FILENAME_MAX, fmt, args);
+ va_end(args);
+ use_helpline(NULL);
+ use_helpfile(NULL);
+ if (isDebug())
+ msgDebug("Notify: %s\n", errstr);
+ dialog_msgbox(NULL, errstr, -1, -1, 0);
+}
+
+/* Put up a message in a popup yes/no box and return 0 for YES, 1 for NO */
+int
+msgYesNo(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ int ret;
+ WINDOW *w = savescr();
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ va_start(args, fmt);
+ vsnprintf(errstr, FILENAME_MAX, fmt, args);
+ va_end(args);
+ use_helpline(NULL);
+ use_helpfile(NULL);
+ if (OnVTY) {
+ ioctl(0, VT_ACTIVATE, 1); /* Switch back */
+ msgInfo(NULL);
+ }
+ if (variable_get(VAR_NONINTERACTIVE))
+ return 0; /* If non-interactive, return YES all the time */
+ ret = dialog_yesno("User Confirmation Requested", errstr, -1, -1);
+ restorescr(w);
+ return ret;
+}
+
+/* Put up a message in a popup no/yes box and return 0 for YES, 1 for NO */
+int
+msgNoYes(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ int ret;
+ WINDOW *w = savescr();
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ va_start(args, fmt);
+ vsnprintf(errstr, FILENAME_MAX, fmt, args);
+ va_end(args);
+ use_helpline(NULL);
+ use_helpfile(NULL);
+ if (OnVTY) {
+ ioctl(0, VT_ACTIVATE, 1); /* Switch back */
+ msgInfo(NULL);
+ }
+ if (variable_get(VAR_NONINTERACTIVE))
+ return 1; /* If non-interactive, return NO all the time */
+ ret = dialog_noyes("User Confirmation Requested", errstr, -1, -1);
+ restorescr(w);
+ return ret;
+}
+
+/* Put up a message in an input box and return the value */
+char *
+msgGetInput(char *buf, char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ static char input_buffer[256];
+ int rval;
+ WINDOW *w = savescr();
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ va_start(args, fmt);
+ vsnprintf(errstr, FILENAME_MAX, fmt, args);
+ va_end(args);
+ use_helpline(NULL);
+ use_helpfile(NULL);
+ if (buf)
+ SAFE_STRCPY(input_buffer, buf);
+ else
+ input_buffer[0] = '\0';
+ if (OnVTY) {
+ ioctl(0, VT_ACTIVATE, 1); /* Switch back */
+ msgInfo(NULL);
+ }
+ rval = dialog_inputbox("Value Required", errstr, -1, -1, input_buffer);
+ restorescr(w);
+ if (!rval)
+ return input_buffer;
+ else
+ return NULL;
+}
+
+/* Write something to the debugging port */
+void
+msgDebug(char *fmt, ...)
+{
+ va_list args;
+ char *dbg;
+
+ if (DebugFD == -1)
+ return;
+ dbg = (char *)alloca(FILENAME_MAX);
+ strcpy(dbg, "DEBUG: ");
+ va_start(args, fmt);
+ vsnprintf((char *)(dbg + strlen(dbg)), FILENAME_MAX, fmt, args);
+ va_end(args);
+ write(DebugFD, dbg, strlen(dbg));
+}
+
+/* Tell the user there's some output to go look at */
+void
+msgWeHaveOutput(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ WINDOW *w = savescr();
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ va_start(args, fmt);
+ vsnprintf(errstr, FILENAME_MAX, fmt, args);
+ va_end(args);
+ use_helpline(NULL);
+ use_helpfile(NULL);
+ msgDebug("Notify: %s\n", errstr);
+ dialog_clear_norefresh();
+ sleep(2);
+ dialog_msgbox(NULL, errstr, -1, -1, 0);
+ restorescr(w);
+}
+
+/* Simple versions of msgConfirm() and msgNotify() for calling from scripts */
+int
+msgSimpleConfirm(char *str)
+{
+ msgConfirm("%s", str);
+ return DITEM_SUCCESS;
+}
+
+int
+msgSimpleNotify(char *str)
+{
+ msgNotify("%s", str);
+ return DITEM_SUCCESS;
+}
diff --git a/usr.sbin/sysinstall/network.c b/usr.sbin/sysinstall/network.c
new file mode 100644
index 0000000..a42cee0
--- /dev/null
+++ b/usr.sbin/sysinstall/network.c
@@ -0,0 +1,355 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated to essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/* These routines deal with getting things off of network media */
+
+#include "sysinstall.h"
+#include <signal.h>
+#include <termios.h>
+#include <sys/fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+
+static Boolean networkInitialized;
+static pid_t startPPP(Device *devp);
+
+static pid_t pppPID;
+
+Boolean
+mediaInitNetwork(Device *dev)
+{
+ int i;
+ char *rp;
+ char *cp, ifconfig[255];
+ WINDOW *w;
+
+ if (!RunningAsInit || networkInitialized)
+ return TRUE;
+
+ if (isDebug())
+ msgDebug("Init routine called for network device %s.\n", dev->name);
+
+ if (!file_readable("/etc/resolv.conf")) {
+ if (DITEM_STATUS(configResolv(NULL)) == DITEM_FAILURE) {
+ msgConfirm("Can't seem to write out /etc/resolv.conf. Net cannot be used.");
+ return FALSE;
+ }
+ }
+
+ w = savescr();
+ dialog_clear_norefresh();
+
+ /* Old PPP process lying around? */
+ if (pppPID) {
+ msgConfirm("Killing previous PPP process %d.", pppPID);
+ kill(pppPID, SIGTERM);
+ pppPID = 0;
+ }
+ if (!strncmp("ppp", dev->name, 3)) { /* PPP? */
+ if (!(pppPID = startPPP(dev))) {
+ msgConfirm("Unable to start PPP! This installation method cannot be used.");
+ return FALSE;
+ }
+ networkInitialized = TRUE;
+ return TRUE;
+ }
+ else if (!strncmp("sl", dev->name, 2)) { /* SLIP? */
+ char *val;
+ char attach[256];
+
+ /* Cheesy slip attach */
+ snprintf(attach, 256, "slattach -a -h -l -s 9600 %s", dev->devname);
+ val = msgGetInput(attach,
+ "Warning: SLIP is rather poorly supported in this revision\n"
+ "of the installation due to the lack of a dialing utility.\n"
+ "If you can use PPP for this instead then you're much better\n"
+ "off doing so, otherwise SLIP works fairly well for *hardwired*\n"
+ "links. Please edit the following slattach command for\n"
+ "correctness (default here is: VJ compression, Hardware flow-\n"
+ "control, ignore carrier and 9600 baud data rate). When you're\n"
+ "ready, press [ENTER] to execute it.");
+ if (!val) {
+ msgConfirm("slattach command was empty. Try again!");
+ restorescr(w);
+ return FALSE;
+ }
+ else
+ SAFE_STRCPY(attach, val);
+ /*
+ * Doing this with vsystem() is actually bogus since we should be storing the pid of slattach
+ * for later killing. It's just too convenient to call vsystem(), however, rather than
+ * constructing a proper argument for exec() so we punt on doing slip right for now.
+ */
+ if (vsystem("%s", attach)) {
+ msgConfirm("slattach returned a bad status! Please verify that\n"
+ "the command is correct and try this operation again.");
+ restorescr(w);
+ return FALSE;
+ }
+ restorescr(w);
+ }
+
+ snprintf(ifconfig, 255, "%s%s", VAR_IFCONFIG, dev->name);
+ cp = variable_get(ifconfig);
+ if (cp) {
+ if (strcmp(cp, "DHCP")) {
+ msgDebug("ifconfig %s %s", dev->name, cp);
+ i = vsystem("ifconfig %s %s", dev->name, cp);
+ if (i) {
+ msgConfirm("Unable to configure the %s interface!\n"
+ "This installation method cannot be used.",
+ dev->name);
+ return FALSE;
+ }
+ rp = variable_get(VAR_GATEWAY);
+ if (!rp || *rp == '0') {
+ msgConfirm("No gateway has been set. You may be unable to access hosts\n"
+ "not on your local network");
+ }
+ else {
+ msgDebug("Adding default route to %s.", rp);
+ vsystem("route -n add default %s", rp);
+ }
+ }
+ } else if ((cp = variable_get(VAR_IPV6ADDR)) == NULL || *cp == '\0') {
+ msgConfirm("The %s device is not configured. You will need to do so\n"
+ "in the Networking configuration menu before proceeding.", dev->name);
+ return FALSE;
+ }
+
+ if (isDebug())
+ msgDebug("Network initialized successfully.\n");
+ networkInitialized = TRUE;
+ return TRUE;
+}
+
+void
+mediaShutdownNetwork(Device *dev)
+{
+ char *cp;
+
+ if (!RunningAsInit || !networkInitialized)
+ return;
+
+ msgDebug("Shutdown called for network device %s\n", dev->name);
+ /* Not a serial device? */
+ if (strncmp("sl", dev->name, 2) && strncmp("ppp", dev->name, 3)) {
+ int i;
+ char ifconfig[255];
+
+ snprintf(ifconfig, 255, "%s%s", VAR_IFCONFIG, dev->name);
+ cp = variable_get(ifconfig);
+ if (!cp)
+ return;
+ msgDebug("ifconfig %s down", dev->name);
+ i = vsystem("ifconfig %s down", dev->name);
+ if (i)
+ msgConfirm("Warning: Unable to down the %s interface properly", dev->name);
+ cp = variable_get(VAR_GATEWAY);
+ if (cp) {
+ msgDebug("Deleting default route.");
+ vsystem("route -n delete default");
+ }
+ }
+ else if (pppPID) {
+ msgConfirm("Killing previous PPP process %d.", pppPID);
+ kill(pppPID, SIGTERM);
+ pppPID = 0;
+ }
+ networkInitialized = FALSE;
+}
+
+/* Start PPP on the 3rd screen */
+static pid_t
+startPPP(Device *devp)
+{
+ int fd2, pulse;
+ FILE *fp;
+ char *val;
+ pid_t pid = 0;
+ char myaddr[16], provider[16], speed[16], authname[32], authkey[16];
+ char phone[16];
+ WINDOW *w = savescr();
+
+ /* These are needed to make ppp work */
+ Mkdir("/var/log");
+ Mkdir("/var/run");
+ Mkdir("/var/spool/lock");
+ Mkdir("/etc/ppp");
+
+ dialog_clear_norefresh();
+ if (!variable_get(VAR_SERIAL_SPEED))
+ variable_set2(VAR_SERIAL_SPEED, "115200", 0);
+ /* Get any important user values */
+ val = variable_get_value(VAR_SERIAL_SPEED,
+ "Enter the baud rate for your modem - this can be higher than the actual\n"
+ "maximum data rate since most modems can talk at one speed to the\n"
+ "computer and at another speed to the remote end.\n\n"
+ "If you're not sure what to put here, just select the default.", 0);
+ SAFE_STRCPY(speed, (val && *val) ? val : "115200");
+
+ val = variable_get(VAR_GATEWAY);
+ SAFE_STRCPY(provider, (val && *val) ? val : "0");
+
+ dialog_clear_norefresh();
+ val = msgGetInput(provider, "Enter the IP address of your service provider or 0 if you\n"
+ "don't know it and would prefer to negotiate it dynamically.");
+ SAFE_STRCPY(provider, (val && *val) ? val : "0");
+
+ if (devp->private && ((DevInfo *)devp->private)->ipaddr[0])
+ SAFE_STRCPY(myaddr, ((DevInfo *)devp->private)->ipaddr);
+ else
+ strcpy(myaddr, "0");
+
+ if (!Fake)
+ fp = fopen("/etc/ppp/ppp.linkup", "w");
+ else
+ fp = fopen("/dev/stderr", "w");
+ if (fp != NULL) {
+ fprintf(fp, "MYADDR:\n");
+ fprintf(fp, " delete ALL\n");
+ fprintf(fp, " add 0 0 HISADDR\n");
+ fchmod(fileno(fp), 0755);
+ fclose(fp);
+ }
+ if (!Fake)
+ fd2 = open("/etc/ppp/ppp.secret", O_CREAT);
+ else
+ fd2 = -1;
+ if (fd2 != -1) {
+ fchmod(fd2, 0700);
+ close(fd2);
+ }
+ if (!Fake)
+ fp = fopen("/etc/ppp/ppp.conf", "a");
+ else
+ fp = fopen("/dev/stderr", "w");
+ if (!fp) {
+ msgConfirm("Couldn't open /etc/ppp/ppp.conf file! This isn't going to work");
+ restorescr(w);
+ return 0;
+ }
+ authname[0] = '\0';
+ pulse = 0;
+ dialog_clear_norefresh();
+ if (!dialog_yesno("", "Does your ISP support PAP or CHAP ppp logins?", -1, -1)) {
+ val = msgGetInput(NULL, "Enter the name you use to login to your provider.");
+ SAFE_STRCPY(authname, val);
+ dialog_clear_norefresh();
+ val = msgGetInput(NULL, "Enter the password you use to login to your provider.");
+ SAFE_STRCPY(authkey, val);
+ dialog_clear_norefresh();
+ val = msgGetInput(NULL, "Enter the your provider's login phone number.");
+ SAFE_STRCPY(phone, val);
+ dialog_clear_norefresh();
+ pulse = dialog_yesno("", "Does your telephone line support tone dialing?", -1, -1);
+ }
+ fprintf(fp, "\ninstall:\n");
+ fprintf(fp, " set speed %s\n", speed);
+ fprintf(fp, " set device %s\n", devp->devname);
+ fprintf(fp, " set ifaddr %s %s 255.255.255.0 0.0.0.0\n", myaddr, provider);
+ fprintf(fp, " add! default HISADDR\n");
+ fprintf(fp, " set timeout 0\n");
+ fprintf(fp, " enable dns\n");
+ fprintf(fp, " set log local phase\n");
+ if(authname[0] != '\0'){
+ fprintf(fp, " set dial \"ABORT BUSY ABORT NO\\\\sCARRIER TIMEOUT 5 \\\"\\\" AT OK-AT-OK ATE1Q0 OK \\\\dATD%c\\\\T TIMEOUT 40 CONNECT\"\n", pulse ? 'P' : 'T');
+ fprintf(fp, " set login\n");
+ fprintf(fp, " set authname %s\n", authname);
+ fprintf(fp, " set authkey %s\n", authkey);
+ fprintf(fp, " set phone %s\n", phone);
+ }
+ if (fchmod(fileno(fp), 0600) != 0)
+ msgConfirm("Warning: Failed to fix permissions on /etc/ppp/ppp.conf !");
+ fclose(fp);
+
+ /* Make the ppp config persistent */
+ variable_set2(VAR_PPP_ENABLE, "YES", 0);
+ variable_set2(VAR_PPP_PROFILE, "install", 0);
+
+ if (!Fake && !file_readable("/dev/tun0") && mknod("/dev/tun0", 0600 | S_IFCHR, makedev(52, 0))) {
+ msgConfirm("Warning: No /dev/tun0 device. PPP will not work!");
+ restorescr(w);
+ return 0;
+ }
+
+ if (isDebug())
+ msgDebug("About to start PPP on device %s @ %s baud. Provider = %s\n", devp->devname, speed, provider);
+
+ if (!Fake && !(pid = fork())) {
+ int i, fd;
+ struct termios foo;
+ extern int login_tty(int);
+
+ for (i = getdtablesize(); i >= 0; i--)
+ close(i);
+
+ /* We're going over to VTY2 */
+ fd = open("/dev/ttyv2", O_RDWR);
+ ioctl(0, TIOCSCTTY, &fd);
+ dup2(0, 1);
+ dup2(0, 2);
+ DebugFD = 2;
+ if (login_tty(fd) == -1)
+ msgDebug("ppp: Can't set the controlling terminal.\n");
+ signal(SIGTTOU, SIG_IGN);
+ if (tcgetattr(fd, &foo) != -1) {
+ foo.c_cc[VERASE] = '\010';
+ if (tcsetattr(fd, TCSANOW, &foo) == -1)
+ msgDebug("ppp: Unable to set the erase character.\n");
+ }
+ else
+ msgDebug("ppp: Unable to get the terminal attributes!\n");
+ execlp("ppp", "ppp", "install", (char *)NULL);
+ msgDebug("PPP process failed to exec!\n");
+ exit(1);
+ }
+ else {
+ dialog_clear_norefresh();
+ msgConfirm("NOTICE: The PPP command is now started on VTY3 (type ALT-F3 to\n"
+ "interact with it, ALT-F1 to switch back here). If you are using\n"
+ "a PAP or CHAP login simply enter \"dial\", otherwise you'll need\n"
+ "to use the \"term\" command which starts a terminal emulator\n"
+ "which you can use to talk to your modem and dial the service\n"
+ "provider. Once you're connected, come back to this screen and\n"
+ "press return.\n\n"
+ "DO NOT PRESS [ENTER] HERE UNTIL THE CONNECTION IS FULLY\n"
+ "ESTABLISHED!");
+ }
+ restorescr(w);
+ return pid;
+}
diff --git a/usr.sbin/sysinstall/nfs.c b/usr.sbin/sysinstall/nfs.c
new file mode 100644
index 0000000..2f09637
--- /dev/null
+++ b/usr.sbin/sysinstall/nfs.c
@@ -0,0 +1,96 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated to essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <sys/errno.h>
+#include <sys/fcntl.h>
+#include <sys/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;
+ WINDOW *w = savescr();
+
+ if (NFSMounted)
+ return TRUE;
+
+ if (!DEVICE_INIT(netDevice))
+ return FALSE;
+
+ if (Mkdir(mountpoint))
+ return FALSE;
+
+ msgNotify("Mounting %s over NFS on %s", dev->name, mountpoint);
+ if (vsystem("mount_nfs %s %s %s %s",
+ 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)
+ DEVICE_SHUTDOWN(netDevice);
+ restorescr(w);
+ return FALSE;
+ }
+ NFSMounted = TRUE;
+ if (isDebug())
+ msgDebug("Mounted NFS device %s onto %s\n", dev->name, mountpoint);
+ restorescr(w);
+ return TRUE;
+}
+
+FILE *
+mediaGetNFS(Device *dev, char *file, Boolean probe)
+{
+ return mediaGenericGet(mountpoint, file);
+}
+
+void
+mediaShutdownNFS(Device *dev)
+{
+ if (!NFSMounted)
+ return;
+
+ msgDebug("Unmounting NFS partition on %s", 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..9bd0360
--- /dev/null
+++ b/usr.sbin/sysinstall/options.c
@@ -0,0 +1,335 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated for what's essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <ctype.h>
+#include <curses.h>
+#include <term.h>
+
+int fixitTtyWhich(dialogMenuItem *);
+
+static char *
+varCheck(Option opt)
+{
+ char *cp = NULL;
+
+ if (opt.aux)
+ cp = variable_get((char *)opt.aux);
+ if (!cp)
+ return "NO";
+ return cp;
+}
+
+/* Show our little logo */
+static char *
+resetLogo(char *str)
+{
+ return "[RESET!]";
+}
+
+static char *
+mediaCheck(Option opt)
+{
+ if (mediaDevice) {
+ switch(mediaDevice->type) {
+ case DEVICE_TYPE_UFS:
+ case DEVICE_TYPE_DISK:
+ return "File system";
+
+ case DEVICE_TYPE_FLOPPY:
+ return "Floppy";
+
+ case DEVICE_TYPE_FTP:
+ return "FTP";
+
+ case DEVICE_TYPE_CDROM:
+ return "CDROM";
+
+ case DEVICE_TYPE_TAPE:
+ return "Tape";
+
+ case DEVICE_TYPE_DOS:
+ return "DOS";
+
+ case DEVICE_TYPE_NFS:
+ return "NFS";
+
+ case DEVICE_TYPE_NONE:
+ case DEVICE_TYPE_NETWORK:
+ case DEVICE_TYPE_ANY:
+ default:
+ return "<unknown>";
+ }
+ }
+ return "<not yet set>";
+}
+
+#define TAPE_PROMPT "Please enter the tape block size in 512 byte blocks:"
+#define NEWFS_PROMPT "Please enter newfs(8) parameters:"
+#define RELNAME_PROMPT "Please specify the release you wish to load or\n\"any\" for a generic release install:"
+#define BPKG_PROMPT "Please specify the name of the HTML browser package:"
+#define BBIN_PROMPT "Please specify a full pathname to the HTML browser binary:"
+#define EDITOR_PROMPT "Please specify the name of the text editor you wish to use:"
+#define PKG_PROMPT "Please specify a temporary directory with lots of free space:"
+#define INSTROOT_PROMPT "Please specify a root directory if installing somewhere other than /"
+#define TIMEOUT_PROMPT "Please specify the number of seconds to wait for slow media:"
+
+static Option Options[] = {
+{ "NFS Secure", "NFS server talks only on a secure port",
+ OPT_IS_VAR, NULL, VAR_NFS_SECURE, varCheck },
+{ "NFS Slow", "User is using a slow PC or ethernet card",
+ OPT_IS_VAR, NULL, VAR_SLOW_ETHER, varCheck },
+{ "Debugging", "Emit extra debugging output on VTY2 (ALT-F2)",
+ OPT_IS_VAR, NULL, VAR_DEBUG, varCheck },
+{ "No Warnings", "Don't Warn the user when a setting seems incorrect",
+ OPT_IS_VAR, NULL, VAR_NO_WARN, varCheck },
+{ "Yes to All", "Assume \"Yes\" answers to all non-critical dialogs",
+ OPT_IS_VAR, NULL, VAR_NO_CONFIRM, varCheck },
+{ "DHCP", "Attempt automatic DHCP configuration of interfaces",
+ OPT_IS_VAR, NULL, VAR_TRY_DHCP, varCheck },
+{ "IPv6", "Attempt IPv6 configuration of interfaces",
+ OPT_IS_VAR, NULL, VAR_TRY_RTSOL, varCheck },
+{ "FTP username", "Username and password to use instead of anonymous",
+ OPT_IS_FUNC, mediaSetFTPUserPass, VAR_FTP_USER, varCheck },
+{ "Editor", "Which text editor to use during installation",
+ OPT_IS_VAR, EDITOR_PROMPT, VAR_EDITOR, varCheck },
+{ "Tape Blocksize", "Tape media block size in 512 byte blocks",
+ OPT_IS_VAR, TAPE_PROMPT, VAR_TAPE_BLOCKSIZE, varCheck },
+{ "Extract Detail", "How verbosely to display file name information during extractions",
+ OPT_IS_FUNC, mediaSetCPIOVerbosity, VAR_CPIO_VERBOSITY, varCheck },
+{ "Release Name", "Which release to attempt to load from installation media",
+ OPT_IS_VAR, RELNAME_PROMPT, VAR_RELNAME, varCheck },
+{ "Install Root", "Which directory to unpack distributions or packages relative to",
+ OPT_IS_VAR, INSTROOT_PROMPT, VAR_INSTALL_ROOT, varCheck },
+{ "Browser package", "This is the browser package that will be used for viewing HTML docs",
+ OPT_IS_VAR, BPKG_PROMPT, VAR_BROWSER_PACKAGE, varCheck },
+{ "Browser Exec", "This is the path to the main binary of the browser package",
+ OPT_IS_VAR, BBIN_PROMPT, VAR_BROWSER_BINARY, varCheck },
+{ "Media Type", "The current installation media type.",
+ OPT_IS_FUNC, mediaGetType, VAR_MEDIA_TYPE, mediaCheck },
+{ "Media Timeout", "Timeout value in seconds for slow media.",
+ OPT_IS_VAR, TIMEOUT_PROMPT, VAR_MEDIA_TIMEOUT, varCheck },
+{ "Package Temp", "The directory where package temporary files should go",
+ OPT_IS_VAR, PKG_PROMPT, VAR_PKG_TMPDIR, varCheck },
+{ "Newfs Args", "Default parameters for newfs(8)",
+ OPT_IS_VAR, NEWFS_PROMPT, VAR_NEWFS_ARGS, varCheck },
+{ "Fixit Console", "Which tty to use for the Fixit action.",
+ OPT_IS_FUNC, fixitTtyWhich, VAR_FIXIT_TTY, varCheck },
+{ "Re-scan Devices", "Re-run sysinstall's initial device probe",
+ OPT_IS_FUNC, deviceRescan },
+{ "Use Defaults", "Reset all values to startup defaults",
+ OPT_IS_FUNC, installVarDefaults, 0, resetLogo },
+{ NULL },
+};
+
+#define OPT_START_ROW 4
+#define OPT_END_ROW 19
+#define OPT_NAME_COL 0
+#define OPT_VALUE_COL 16
+#define GROUP_OFFSET 40
+
+static char *
+value_of(Option opt)
+{
+ static char ival[40];
+
+ switch (opt.type) {
+ case OPT_IS_STRING:
+ return (char *)opt.data;
+
+ case OPT_IS_INT:
+ sprintf(ival, "%lu", (long)opt.data);
+ return ival;
+
+ case OPT_IS_FUNC:
+ case OPT_IS_VAR:
+ if (opt.check)
+ return opt.check(opt);
+ else
+ return "<*>";
+ }
+ return "<unknown>";
+}
+
+static int
+fire(Option opt)
+{
+ int status = 0;
+
+ if (opt.type == OPT_IS_FUNC) {
+ int (*cp)(char *) = opt.data, rcode;
+
+ rcode = cp(NULL);
+ status = 1;
+ }
+ else if (opt.type == OPT_IS_VAR) {
+ if (opt.data) {
+ (void)variable_get_value(opt.aux, opt.data, -1);
+ status = 1;
+ }
+ else if (variable_get(opt.aux)) {
+ if (!variable_cmp(opt.aux, "YES"))
+ variable_set2(opt.aux, "NO", -1);
+ else
+ variable_set2(opt.aux, "YES", -1);
+ }
+ else
+ variable_set2(opt.aux, "YES", 0);
+ }
+ if (opt.check)
+ opt.check(opt);
+ refresh();
+ return status;
+}
+
+int
+optionsEditor(dialogMenuItem *self)
+{
+ int i, optcol, optrow, key;
+ static int currOpt = 0;
+ WINDOW *w = savescr();
+
+ dialog_clear();
+ clear();
+
+ while (1) {
+ /* Whap up the header */
+ attrset(A_REVERSE); mvaddstr(0, 0, "Options Editor"); attrset(A_NORMAL);
+ for (i = 0; i < 2; i++) {
+ mvaddstr(OPT_START_ROW - 2, OPT_NAME_COL + (i * GROUP_OFFSET), "Name");
+ mvaddstr(OPT_START_ROW - 1, OPT_NAME_COL + (i * GROUP_OFFSET), "----");
+
+ mvaddstr(OPT_START_ROW - 2, OPT_VALUE_COL + (i * GROUP_OFFSET), "Value");
+ mvaddstr(OPT_START_ROW - 1, OPT_VALUE_COL + (i * GROUP_OFFSET), "-----");
+ }
+ /* And the footer */
+ mvprintw(OPT_END_ROW + 1, 0, "Use SPACE to select/toggle an option, arrow keys to move,");
+ mvprintw(OPT_END_ROW + 2, 0, "? or F1 for more help. When you're done, type Q to Quit.");
+
+ optrow = OPT_START_ROW;
+ optcol = OPT_NAME_COL;
+ for (i = 0; Options[i].name; i++) {
+ /* Names are painted somewhat gratuitously each time, but it's easier this way */
+ mvprintw(optrow, OPT_NAME_COL + optcol, Options[i].name);
+ if (currOpt == i)
+ attrset(ATTR_SELECTED);
+ mvprintw(optrow++, OPT_VALUE_COL + optcol, value_of(Options[i]));
+ if (currOpt == i)
+ attrset(A_NORMAL);
+ if (optrow == OPT_END_ROW) {
+ optrow = OPT_START_ROW;
+ optcol += GROUP_OFFSET;
+ }
+ clrtoeol();
+ }
+ attrset(ATTR_TITLE);
+ mvaddstr(OPT_END_ROW + 4, 0, Options[currOpt].desc);
+ attrset(A_NORMAL);
+ clrtoeol();
+ move(0, 14);
+ refresh();
+
+ /* Start the edit loop */
+ key = toupper(getch());
+ switch (key) {
+ case KEY_F(1):
+ case '?':
+ systemDisplayHelp("options");
+ clear();
+ break;
+
+ case '\020': /* ^P */
+ case KEY_UP:
+ if (currOpt)
+ --currOpt;
+ else
+ for (currOpt = 0; Options[currOpt + 1].name; currOpt++);
+ continue;
+
+ case '\016': /* ^N */
+ case KEY_DOWN:
+ if (Options[currOpt + 1].name)
+ ++currOpt;
+ else
+ currOpt = 0;
+ continue;
+
+ case KEY_HOME:
+ currOpt = 0;
+ continue;
+
+ case KEY_END:
+ while (Options[currOpt + 1].name)
+ ++currOpt;
+ continue;
+
+ case ' ':
+ if (fire(Options[currOpt]))
+ clear();
+ continue;
+
+ case '\033': /* ESC */
+ case 'Q':
+ clear();
+ dialog_clear();
+ restorescr(w);
+ return DITEM_SUCCESS;
+
+ default:
+ beep();
+ }
+ }
+ /* NOTREACHED */
+ return DITEM_SUCCESS;
+}
+
+int
+fixitTtyWhich(dialogMenuItem *self)
+{
+ char *cp = variable_get(VAR_FIXIT_TTY);
+
+ if (!cp) {
+ msgConfirm("The Fix-it TTY setting is not set to anything!");
+ return DITEM_FAILURE;
+ }
+ else {
+ if (!strcmp(cp, "standard"))
+ variable_set2(VAR_FIXIT_TTY, "serial", 0);
+ else /* must be "serial" - wrap around */
+ variable_set2(VAR_FIXIT_TTY, "standard", 0);
+ }
+ return DITEM_SUCCESS;
+}
diff --git a/usr.sbin/sysinstall/package.c b/usr.sbin/sysinstall/package.c
new file mode 100644
index 0000000..a160eb1
--- /dev/null
+++ b/usr.sbin/sysinstall/package.c
@@ -0,0 +1,266 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+static Boolean sigpipe_caught;
+
+static void
+catch_pipe(int sig)
+{
+ sigpipe_caught = TRUE;
+}
+
+extern PkgNode Top;
+
+/* Like package_extract, but assumes current media device and chases deps */
+int
+package_add(char *name)
+{
+ PkgNodePtr tmp;
+ int i;
+
+ if (!mediaVerify())
+ return DITEM_FAILURE;
+
+ if (!DEVICE_INIT(mediaDevice))
+ return DITEM_FAILURE;
+
+ i = index_initialize("packages/INDEX");
+ if (DITEM_STATUS(i) != DITEM_SUCCESS)
+ return i;
+
+ tmp = index_search(&Top, name, &tmp);
+ if (tmp)
+ return index_extract(mediaDevice, &Top, tmp, FALSE);
+ else {
+ msgConfirm("Sorry, package %s was not found in the INDEX.", name);
+ return DITEM_FAILURE;
+ }
+}
+
+/* For use by dispatch */
+int
+packageAdd(dialogMenuItem *self)
+{
+ char *cp;
+
+ cp = variable_get(VAR_PACKAGE);
+ if (!cp) {
+ msgDebug("packageAdd: No package name passed in package variable\n");
+ return DITEM_FAILURE;
+ }
+ else
+ return package_add(cp);
+}
+
+Boolean
+package_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[MAXPATHLEN];
+ const char *PkgExts[] = { "", ".tbz", ".tbz2", ".tgz" };
+ int ext, last_msg, pathend, ret;
+ FILE *fp;
+
+ last_msg = 0;
+
+ /* Check to make sure it's not already there */
+ if (package_exists(name))
+ return DITEM_SUCCESS;
+
+ if (!DEVICE_INIT(dev)) {
+ msgConfirm("Unable to initialize media type for package extract.");
+ return DITEM_FAILURE;
+ }
+
+ /* If necessary, initialize the ldconfig hints */
+ if (!file_readable("/var/run/ld-elf.so.hints"))
+ vsystem("ldconfig /usr/lib /usr/lib/compat /usr/local/lib /usr/X11R6/lib");
+ if (!file_readable("/var/run/ld.so.hints"))
+ vsystem("ldconfig -aout /usr/lib/aout /usr/lib/compat/aout /usr/local/lib/aout /usr/X11R6/lib/aout");
+
+ /* Be initially optimistic */
+ ret = DITEM_SUCCESS;
+ /* Make a couple of paranoid locations for temp files to live if user specified none */
+ if (!variable_get(VAR_PKG_TMPDIR)) {
+ /* Set it to a location with as much space as possible */
+ variable_set2(VAR_PKG_TMPDIR, "/var/tmp", 0);
+ }
+ Mkdir(variable_get(VAR_PKG_TMPDIR));
+ vsystem("chmod 1777 %s", variable_get(VAR_PKG_TMPDIR));
+
+ if (!index(name, '/')) {
+ if (!strpbrk(name, "-_"))
+ pathend = snprintf(path, sizeof path, "packages/Latest/%s", name);
+ else
+ pathend = snprintf(path, sizeof path, "packages/All/%s", name);
+ }
+ else
+ pathend = snprintf(path, sizeof path, "%s", name);
+
+ /* We have a path, call the device strategy routine to get the file */
+ for (ext = 0 ; ext < sizeof PkgExts / sizeof PkgExts[0]; ++ext) {
+ strlcpy(path + pathend, PkgExts[ext], sizeof path - pathend);
+ if ((fp = DEVICE_GET(dev, path, TRUE)))
+ break;
+ }
+
+ if (fp) {
+ int i = 0, tot, pfd[2];
+ pid_t pid;
+ WINDOW *w = savescr();
+
+ sigpipe_caught = FALSE;
+ signal(SIGPIPE, catch_pipe);
+
+ dialog_clear_norefresh();
+ msgNotify("Adding %s%s\nfrom %s", path, depended ? " (as a dependency)" : "", dev->name);
+ pipe(pfd);
+ pid = fork();
+ if (!pid) {
+ dup2(pfd[0], 0); close(pfd[0]);
+ dup2(DebugFD, 1); dup2(1, 2);
+ close(pfd[1]);
+
+ /* Prevent pkg_add from wanting to interact in bad ways */
+ setenv("PACKAGE_BUILDING", "t", 1);
+ setenv("BATCH", "t", 1);
+
+ if (isDebug())
+ i = execl("/usr/sbin/pkg_add", "/usr/sbin/pkg_add", "-v", "-",
+ (char *)0);
+ else
+ i = execl("/usr/sbin/pkg_add", "/usr/sbin/pkg_add", "-",
+ (char *)0);
+ }
+ else {
+ char buf[BUFSIZ];
+ struct timeval start, stop;
+
+ close(pfd[0]);
+ tot = 0;
+ (void)gettimeofday(&start, (struct timezone *)0);
+
+ while (!sigpipe_caught && (i = fread(buf, 1, BUFSIZ, fp)) > 0) {
+ int seconds;
+
+ tot += i;
+ /* Print statistics about how we're doing */
+ (void) gettimeofday(&stop, (struct timezone *)0);
+ stop.tv_sec = stop.tv_sec - start.tv_sec;
+ stop.tv_usec = stop.tv_usec - start.tv_usec;
+ if (stop.tv_usec < 0)
+ stop.tv_sec--, stop.tv_usec += 1000000;
+ seconds = stop.tv_sec + (stop.tv_usec / 1000000.0);
+ if (!seconds)
+ seconds = 1;
+ if (seconds != last_msg) {
+ last_msg = seconds;
+ msgInfo("%10d bytes read from package %s @ %4.1f KBytes/second", tot, name, (tot / seconds) / 1000.0);
+ }
+ /* Write it out */
+ if (sigpipe_caught || write(pfd[1], buf, i) != i) {
+ msgInfo("Write failure to pkg_add! Package may be corrupt.");
+ break;
+ }
+ }
+ close(pfd[1]);
+ fclose(fp);
+ if (sigpipe_caught)
+ msgInfo("pkg_add(1) apparently did not like the %s package.", name);
+ else if (i == -1)
+ msgInfo("I/O error while reading in the %s package.", name);
+ else
+ msgInfo("Package %s read successfully - waiting for pkg_add(1)", name);
+ refresh();
+ i = waitpid(pid, &tot, 0);
+ dialog_clear_norefresh();
+ if (sigpipe_caught || i < 0 || WEXITSTATUS(tot)) {
+ ret = DITEM_FAILURE;
+ if (variable_get(VAR_NO_CONFIRM))
+ msgNotify("Add of package %s aborted, error code %d -\n"
+ "Please check the debug screen for more info.", name, WEXITSTATUS(tot));
+ else
+ msgConfirm("Add of package %s aborted, error code %d -\n"
+ "Please check the debug screen for more info.", name, WEXITSTATUS(tot));
+ }
+ else
+ msgNotify("Package %s was added successfully", name);
+
+ /* Now catch any stragglers */
+ while (wait3(&tot, WNOHANG, NULL) > 0);
+
+ sleep(1);
+ restorescr(w);
+ }
+ }
+ else {
+ dialog_clear_norefresh();
+ if (variable_get(VAR_NO_CONFIRM))
+ msgNotify("Unable to fetch package %s from selected media.\n"
+ "No package add will be done.", name);
+ else
+ msgConfirm("Unable to fetch package %s from selected media.\n"
+ "No package add will be done.", name);
+ ret = DITEM_FAILURE;
+ }
+ signal(SIGPIPE, SIG_IGN);
+ return ret;
+}
diff --git a/usr.sbin/sysinstall/pccard.c b/usr.sbin/sysinstall/pccard.c
new file mode 100644
index 0000000..2a4dc2f
--- /dev/null
+++ b/usr.sbin/sysinstall/pccard.c
@@ -0,0 +1,290 @@
+/*
+ * PC-card support for sysinstall
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1997-1999
+ * Tatsumi Hosokawa <hosokawa@jp.FreeBSD.org>. All rights reserved.
+ *
+ * This software may be used, modified, copied, and distributed, in
+ * both source and binary form provided that the above copyright and
+ * these terms are retained. Under no circumstances is the author
+ * responsible for the proper functioning of this software, nor does
+ * the author assume any responsibility for damages incurred with its
+ * use.
+ */
+
+#include "sysinstall.h"
+#include <sys/fcntl.h>
+#include <sys/time.h>
+#include <pccard/cardinfo.h>
+
+int pccard_mode = 0;
+
+/*
+ * Set up defines for pccardd interrupt selection.
+ */
+#ifdef PC98
+#define IRQ_COUNT 11
+#else
+#define IRQ_COUNT 9
+#endif /* PC98 */
+#define IRQ_10 0x00001
+#define IRQ_11 0x00002
+#define IRQ_03 0x00004
+#define IRQ_09 0x00008
+#define IRQ_04 0x00010
+#define IRQ_07 0x00020
+#define IRQ_05 0x00040
+#define IRQ_06 0x00080
+#define IRQ_15 0x00100
+#ifdef PC98
+#define IRQ_12 0x00200
+#define IRQ_13 0x00400
+#endif /* PC98 */
+
+unsigned int CardIrq;
+
+typedef struct _irq {
+ char *my_name;
+ char *my_flag;
+ unsigned int my_mask;
+ unsigned int my_bit;
+} Irq;
+
+/* Fill in with potential free IRQs for pccardd */
+static Irq IrqTable[] = {
+ { "irq_03", "-i 3", ~IRQ_03, IRQ_03 },
+ { "irq_04", "-i 4", ~IRQ_04, IRQ_04 },
+ { "irq_05", "-i 5", ~IRQ_05, IRQ_05 },
+ { "irq_06", "-i 6", ~IRQ_06, IRQ_06 },
+ { "irq_07", "-i 7", ~IRQ_07, IRQ_07 },
+ { "irq_09", "-i 9", ~IRQ_09, IRQ_09 },
+ { "irq_10", "-i 10", ~IRQ_10, IRQ_10 },
+ { "irq_11", "-i 11", ~IRQ_11, IRQ_11 },
+ { "irq_15", "-i 15", ~IRQ_15, IRQ_15 },
+#ifdef PC98
+ { "irq_12", "-i 12", ~IRQ_12, IRQ_12 },
+ { "irq_13", "-i 13", ~IRQ_13, IRQ_13 },
+#endif /* PC98 */
+ {NULL},
+};
+
+int
+pccardIrqReset(dialogMenuItem *self)
+{
+ CardIrq = 0;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+checkTrue(dialogMenuItem *item)
+{
+ return TRUE;
+}
+
+DMenu MenuPCICMem = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Please select free address area used by PC-card controller",
+ "PC-card controller uses memory area to get card information.\n"
+ "Please specify an address that is not used by other devices.\n"
+ "If you're uncertain of detailed specification of your hardware,\n"
+#ifdef PC98
+ "leave it untouched (default == 0xd0000).\n"
+ "If you use PC-9801 P, NS/A, NX/C, NL/R or PC-9821 Ne please \n"
+ "select [DA] here.",
+#else
+ "leave it untouched (default == 0xd0000).",
+#endif /* PC98 */
+ "Press F1 for more HELP",
+ "pccard",
+ { { "Default", "I/O address 0xd0000 - 0xd3fff",
+ NULL, dmenuSetVariable, NULL, "_pcicmem=0"},
+ { "D4", "I/O address 0xd4000 - 0xd7fff",
+ NULL, dmenuSetVariable, NULL, "_pcicmem=1"},
+ { "D8", "I/O address 0xd8000 - 0xdbfff",
+ NULL, dmenuSetVariable, NULL, "_pcicmem=2"},
+ { "DC", "I/O address 0xdc000 - 0xdffff",
+ NULL, dmenuSetVariable, NULL, "_pcicmem=3"},
+#ifdef PC98
+ { "DA", "I/O address 0xda000 - 0xdbfff",
+ NULL, dmenuSetVariable, NULL, "_pcicmem=4"},
+#endif /* PC98 */
+ { NULL } },
+};
+
+DMenu MenuCardIRQ = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "Please specify the IRQs that may be used by PC-Cards",
+ "(NOTE: remove any cards that will NOT be used for installation).\n"
+ "The IRQs that you choose must be free (unshared), or you risk \n"
+ "subpar performance and/or a complete system lockup (choose wisely).\n"
+ "One way to determine which IRQs are available is to \"cheat\" and\n"
+ "use the Windows 9x/2000 Device Manager as a reference prior to the\n"
+ "installation.\n",
+ "Select Free IRQ for PC-Cardd",
+ NULL,
+ { { "X Exit", "Exit this menu",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { "Reset", "Reset selected IRQ list",
+ NULL, pccardIrqReset, NULL, NULL, ' ', ' ', ' ' },
+#ifdef PC98
+ { "3 IRQ 3", "(INT 0) is 2nd serial port, internal modem",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CardIrq, '[', 'X', ']', IRQ_03 },
+ { "4 IRQ 5", "(INT 1) is Infrared communication, SCSI I/F, (2nd serial)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CardIrq, '[', 'X', ']', IRQ_05 },
+ { "5 IRQ 6", "(INT 2) is PC-card Controller",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CardIrq, '[', 'X', ']', IRQ_06 },
+ { "6 IRQ 9", "(INT 3) is IDE disk Controller",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CardIrq, '[', 'X', ']', IRQ_09 },
+ { "7 IRQ 10", "(INT 41) is often free",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CardIrq, '[', 'X', ']', IRQ_10 },
+ { "8 IRQ 12", "(INT 5) is Internal sound",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CardIrq, '[', 'X', ']', IRQ_12 },
+ { "9 IRQ 13", "(INT 6) is Bus Mouse",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CardIrq, '[', 'X', ']', IRQ_13 },
+#else
+ { "3 IRQ 10", "IRQ 10 is often free (verify in BIOS)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CardIrq, '[', 'X', ']', IRQ_10 },
+ { "4 IRQ 11", "Verify IRQ 11 is not being used as PCI shared interrupt",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CardIrq, '[', 'X', ']', IRQ_11 },
+ { "5 IRQ 3", "IRQ 3 is often free in most laptops",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CardIrq, '[', 'X', ']', IRQ_03 },
+ { "6 IRQ 9", "IRQ 9 may be used by video or sound or USB",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CardIrq, '[', 'X', ']', IRQ_09 },
+ { "7 IRQ 4", "IRQ 4, usually COM1 (disable in BIOS to make free)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CardIrq, '[', 'X', ']', IRQ_04 },
+ { "8 IRQ 7", "IRQ 7, usually LPT1 (disable in BIOS to make free)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CardIrq, '[', 'X', ']', IRQ_07 },
+ { "9 IRQ 5", "IRQ 5, usually ISA Audio (disable in BIOS to make free)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CardIrq, '[', 'X', ']', IRQ_05 },
+ { "10 IRQ 15", "IRQ 15, usually Secondary IDE channel",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CardIrq, '[', 'X', ']', IRQ_15 },
+ { "11 IRQ 6", "IRQ 6 will be free if laptop only has USB floppy drive",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CardIrq, '[', 'X', ']', IRQ_06 },
+#endif /* PC98 */
+ { NULL } },
+};
+
+void
+pccardInitialize(void)
+{
+ int fd;
+ int t;
+ int i;
+ int pcic_mem = 0xd0000;
+ int beep_newstat;
+ char card_device[16];
+ char card_irq[256] = "";
+ char temp[256];
+ char *spcic_mem;
+ char pccardd_cmd[256];
+ WINDOW *w;
+
+ pccard_mode = 1;
+
+ if (!RunningAsInit && !Fake) {
+ /* It's not my job... */
+ return;
+ }
+
+ sprintf(card_device, CARD_DEVICE, 0);
+
+ if ((fd = open(card_device, O_RDWR)) < 0) {
+ msgDebug("Can't open PC-card controller %s.\n", card_device);
+ return;
+ }
+ else if (msgYesNo("Found PC-card slot(s).\n"
+ "Use PC-card device as installation media?\n")) {
+ return;
+ }
+ close(fd);
+
+ variable_set2("_pccard_install", "YES", 0);
+
+ dmenuOpenSimple(&MenuPCICMem, FALSE);
+ spcic_mem = variable_get("_pcicmem");
+ dmenuOpenSimple(&MenuCardIRQ, FALSE);
+
+ sscanf(spcic_mem, "%d", &t);
+ switch (t) {
+ case 0:
+ pcic_mem = 0xd0000;
+ variable_set2("pccard_mem", "DEFAULT", 1);
+ break;
+ case 1:
+ pcic_mem = 0xd4000;
+ variable_set2("pccard_mem", "0xd4000", 1);
+ break;
+ case 2:
+ pcic_mem = 0xd8000;
+ variable_set2("pccard_mem", "0xd8000", 1);
+ break;
+ case 3:
+ pcic_mem = 0xdc000;
+ variable_set2("pccard_mem", "0xdc000", 1);
+ break;
+#ifdef PC98
+ case 4:
+ pcic_mem = 0xda000;
+ variable_set2("pccard_mem", "0xda000", 1);
+ break;
+#endif /* PC98 */
+ }
+
+ /* get card_irq out of CardIrq somehow */
+ if (CardIrq) {
+ for (i = 0; i < IRQ_COUNT; i++) {
+ if ((CardIrq & IrqTable[i].my_bit) != 0) {
+ sprintf(temp, "%s %s", card_irq, IrqTable[i].my_flag);
+ strcpy(card_irq, temp);
+ }
+ }
+ }
+
+ w = savescr();
+ dialog_clear_norefresh();
+ msgConfirm("Now we start initializing PC-card controller and cards.\n"
+ "If you've executed this installer from a PC-card floppy\n"
+ "drive, this is the last chance to replace it with\n"
+ "installation media (PC-card Ethernet, CD, DVD, etc.).\n"
+ "Please insert installation media and press [Enter].\n"
+ "If you've not plugged the PC-card installation media\n"
+ "in yet, please plug it in now and press [Enter].\n"
+ "Otherwise, just press [Enter] to proceed.");
+
+ dialog_clear();
+ msgNotify("Initializing PC-card controller....");
+
+ if (!Fake) {
+ if ((fd = open(card_device, O_RDWR)) < 1) {
+ msgNotify("Can't open PC-card controller %s.\n", card_device);
+ restorescr(w);
+ return;
+ }
+
+ if (ioctl(fd, PIOCRWMEM, &pcic_mem) < 0) {
+ msgNotify("ioctl %s failed.\n", card_device);
+ restorescr(w);
+ return;
+ }
+ beep_newstat = 2;
+ if (ioctl(fd, PIOCSBEEP, &beep_newstat) < 0) {
+ msgNotify("Warning: unable to set pccard insertion beep type for %s",
+ card_device);
+ restorescr(w);
+ return;
+ }
+
+ }
+
+ strcpy(pccardd_cmd, "/stand/pccardd ");
+ strcat(pccardd_cmd, card_irq);
+ strcat(pccardd_cmd, " -z");
+
+ variable_set2("pccardd_flags", card_irq, 1);
+ variable_set2("pccard_enable", "YES", 1);
+
+ vsystem("%s", pccardd_cmd);
+ restorescr(w);
+}
diff --git a/usr.sbin/sysinstall/rtermcap.c b/usr.sbin/sysinstall/rtermcap.c
new file mode 100644
index 0000000..84b3feb
--- /dev/null
+++ b/usr.sbin/sysinstall/rtermcap.c
@@ -0,0 +1,15 @@
+#include <stdio.h>
+#include <termcap.h>
+
+int
+main(int argc, char **argv)
+{
+ char buf[4096];
+ int i;
+
+ if (argc < 2)
+ return 1;
+ i = tgetent(buf, argv[1]);
+ printf("%s",buf);
+ return 0;
+}
diff --git a/usr.sbin/sysinstall/sysinstall.8 b/usr.sbin/sysinstall/sysinstall.8
new file mode 100644
index 0000000..201b8e7
--- /dev/null
+++ b/usr.sbin/sysinstall/sysinstall.8
@@ -0,0 +1,1003 @@
+.\" Copyright (c) 1997
+.\" Jordan Hubbard <jkh@FreeBSD.org>. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY Jordan Hubbard AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL Jordan Hubbard OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd August 9, 1997
+.Dt SYSINSTALL 8
+.Os
+.Sh NAME
+.Nm sysinstall
+.Nd system installation and configuration tool
+.Sh SYNOPSIS
+.Nm
+.Op Ar var=value
+.Op Ar function
+.Op Ar ...
+.Sh DESCRIPTION
+.Nm
+is a utility for installing and configuring
+.Fx
+systems.
+It is the first utility invoked by the
+.Fx
+installation boot
+floppy and is also copied into
+.Pa /stand/sysinstall
+on newly installed
+.Fx
+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.
+.Pp
+On those occasions where it is deemed necessary to invoke a subsystem
+of sysinstall directly, however, it is also possible to do so by
+naming the appropriate function entry points on the command line.
+Since this action is essentially identical to running an installation
+script, each command-line argument corresponding to a line of script,
+the reader is encouraged to read the section on scripting for more
+information on this feature.
+.Sh NOTES
+.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
+.Fx
+systems. It also contains some extra intelligence
+for running as a replacement for
+.Xr init 8
+when it's invoked by the
+.Fx
+installation boot procedure. It
+assumes very little in the way of additional utility support and
+performs most file system operations by calling the relevant syscalls
+(such as
+.Xr mount 2 )
+directly.
+.Pp
+.Nm
+currently uses the
+.Xr dialog 3
+library to do user interaction with simple ANSI line graphics, color
+support for which is enabled by either running on a syscons VTY or some
+other color-capable terminal emulator (newer versions of xterm will support
+color when using the
+.Dq xterm-color
+termcap entry).
+.Pp
+This product is currently at the end of its life cycle and will
+eventually be replaced.
+.Sh RUNNING SCRIPTS
+.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
+.It Sy "LOAD_CONFIG_FILE"
+If
+.Nm
+is compiled with LOAD_CONFIG_FILE set in the environment
+(or in the Makefile) to some value, then that value will
+be used as the filename to automatically look for and load
+when
+.Nm
+starts up and with no user interaction required.
+This option is aimed primarily at large sites who wish to create a
+single prototype install for multiple machines with largely identical
+configurations and/or installation options.
+.It Sy "MAIN MENU"
+If
+.Nm
+is run interactively, that is to say in the default manner, it will
+bring up a main menu which contains a "load config file" option.
+Selecting this option will prompt for the name of a script file which
+it then will attempt to load from a DOS or UFS formatted floppy.
+.It Sy "COMMAND LINE"
+Each command line argument is treated as a script directive
+when
+.Nm
+is run in multi-user mode. Execution ends either by explicit request
+(e.g. calling the
+.Ar shutdown
+directive), upon reaching the end of the argument list or on error.
+.Pp
+For example:
+.Bd -literal
+/stand/sysinstall _ftpPath=ftp://ziggy/pub/ mediaSetFTP configPackages
+.Ed
+.Pp
+Would initialize
+.Nm
+for FTP installation media (using the server `ziggy') and then
+bring up the package installation editor, exiting when finished.
+.El
+.Sh SCRIPT SYNTAX
+A script is a list of one or more directives, each directive taking
+the form of:
+.Pp
+.Ar var=value
+.Pp
+.Ar function
+.Pp
+or
+.Ar #somecomment
+.Pp
+Where
+.Ar var=value
+is the assignment of some internal
+.Nm
+variable, e.g. "ftpPass=FuNkYChiKn", and
+.Ar function
+is the name of an internal
+.Nm
+function, e.g. "mediaSetFTP", and
+.Ar #comment
+is a single-line comment for documentation purposes (ignored by
+sysinstall). Each directive must be by itself on a single line,
+functions taking their arguments by examining known variable names.
+This requires that you be sure to assign the relevant variables before
+calling a function which requires them.
+.Pp
+The
+.Ar noError
+variable can be assigned before each directive: this will cause any error
+detected while processing the directive itself to be ignored.
+The value of
+.Ar noError
+will automatically reset to the default "unassigned" every time a directive is
+processed.
+.Pp
+When and where a function depends on the settings of one or more variables
+will be noted in the following table:
+.Pp
+.Sy "Function Glossary" :
+.Pp
+.Bl -tag -width indent
+.It configAnonFTP
+Invoke the Anonymous FTP configuration menu.
+.Pp
+.Sy Variables :
+None
+.It configRouter
+Select which routing daemon you wish to use, potentially
+loading any required 3rd-party routing daemons as necessary.
+.Pp
+.Sy Variables :
+.Bl -tag -width indent
+.It router
+can be set to the name of the desired routing daemon,
+e.g.\&
+.Dq routed
+or
+.Dq gated ,
+otherwise it is prompted for.
+.El
+.It configNFSServer
+Configure host as an NFS server.
+.Pp
+.Sy Variables :
+None
+.It configNTP
+Configure host as a user of the Network Time Protocol.
+.Pp
+.Sy Variables :
+.Bl -tag -width indent
+.It ntpdate_flags
+The flags to
+.Xr ntpdate 8 ,
+that is to say the name of the server to sync from.
+.El
+.It configPCNFSD
+Configure host to support PC NFS.
+.Pp
+.Sy Variables :
+.Bl -tag -width indent
+.It pcnfsd_pkg
+The name of the PCNFSD package to load if necessary (defaults to hard coded
+version).
+.El
+.It configPackages
+Bring up the interactive package management menu.
+.Pp
+.Sy Variables :
+None
+.It configUsers
+Add users and/or groups to the system.
+.Pp
+.Sy Variables :
+None
+.It configXEnvironment
+Configure the X display subsystem.
+.Pp
+.Sy Variables :
+None
+.It diskPartitionEditor
+Invokes the disk partition (MBR) editor.
+.Pp
+.Sy Variables :
+.Bl -tag -width findx
+.It geometry
+The disk geometry, as a cyls/heads/sectors formatted string. Default: no
+change to geometry.
+.It partition
+Set to disk partitioning type or size, its value being
+.Ar free
+in order to use only remaining free space for
+.Fx ,
+.Ar all
+to use the entire disk for
+.Fx
+but maintain a proper partition
+table,
+.Ar existing
+to use an existing
+.Fx
+partition (first found),
+.Ar exclusive
+to use the disk in
+.Dq dangerously dedicated
+mode or, finally,
+.Ar somenumber
+to allocate
+.Ar somenumber
+blocks of available free space to a new
+.Fx
+partition.
+Default: Interactive mode.
+.It bootManager
+is set to one of
+.Ar boot
+to signify the installation of a boot manager,
+.Ar standard
+to signify installation of a "standard" non-boot MGR DOS
+MBR or
+.Ar none
+to indicate that no change to the boot manager is desired.
+Default: none.
+.It diskInteractive
+If set, bring up the interactive disk partition editor.
+.El
+.Pp
+Note: Nothing is actually written to disk by this function, 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
+.Sy Variables :
+None
+.It diskLabelEditor
+Invokes the disk label editor. This is a bit trickier from a script
+since you need to essentially label everything inside each
+.Fx
+(type 0xA5) partition created by the
+.Ar diskPartitionEditor
+function, and that requires knowing a few rules about how things are
+laid out. When creating a script to automatically allocate disk space
+and partition it up, it is suggested that you first perform the
+installation interactively at least once and take careful notes as to
+what the slice names will be, then and only then hardwiring them into
+the script.
+.Pp
+For example, let's say you have a SCSI disk on which you've created a new
+.Fx
+partition in slice 2 (your DOS partition residing in slice 1).
+The slice name would be
+.Ar da0s2
+for the whole
+.Fx
+partition
+.Ar ( da0s1
+being your DOS primary
+partition). Now let's further assume that you have 500MB in this
+partition and you want to sub-partition that space into root, swap,
+var and usr file systems for
+.Fx .
+Your invocation of the
+.Ar diskLabelEditor
+function might involve setting the following variables:
+.Bl -tag -width findx
+.It Li "da0s2-1=ufs 40960 /"
+A 20MB root file system (all sizes are in 512 byte blocks).
+.It Li "da0s2-2=swap 131072 /"
+A 64MB swap partition.
+.It Li "da0s2-3=ufs 204800 /var"
+A 100MB /var file system.
+.It Li "da0s2-4=ufs 0 /usr 1"
+With the balance of free space (around 316MB) going to the /usr
+file system and with soft-updates enabled (the argument following
+the mount point, if non-zero, means to set the soft updates flag).
+.El
+.Pp
+One can also use the
+.Ar diskLabelEditor
+for mounting or erasing existing partitions as well as creating new
+ones. Using the previous example again, let's say that we also wanted
+to mount our DOS partition and make sure that an
+.Pa /etc/fstab
+entry is created for it in the new installation. Before calling the
+.Ar diskLabelEditor
+function, we simply add an additional line:
+.Pp
+.Dl "da0s1=/dos_c N"
+.Pp
+before the call. This tells the label editor that you want to mount
+the first slice on
+.Pa /dos_c
+and not to attempt to newfs it (not that
+.Nm
+would attempt this for a DOS partition in any case, but it could just
+as easily be an existing UFS partition being named here and the 2nd
+field is non-optional).
+.Pp
+You can also set the
+.Ar diskInteractive
+variable to request that the disk label editor use an interactive dialog
+to partition the disk instead of using variables to explicitly layout the
+disk as described above.
+.Pp
+Note: No file system data is actually written to disk until an
+explicit call to
+.Ar diskLabelCommit
+is made.
+.It diskLabelCommit
+Writes out all pending disklabel information and creates and/or mounts any
+file systems which have requests pending from the
+.Ar diskLabelEditor
+function.
+.Pp
+.Sy Variables :
+None
+.It distReset
+Resets all selected distributions to the empty set (no distributions selected).
+.Pp
+.Sy Variables :
+None
+.It distSetCustom
+Allows the selection of a custom distribution set (e.g. not just on of the
+existing "canned" sets) with no user interaction.
+.Pp
+.Sy Variables :
+.Bl -tag -width indent
+.It dists
+List of distributions to load. Possible distribution values are:
+.Bl -tag -width indentxx
+.It Li 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 crypto
+Encryption binaries and libraries.
+.It Li compat1x
+Compatibility with
+.Fx
+1.x
+.It Li compat20
+Compatibility with
+.Fx 2.0
+.It Li compat21
+Compatibility with
+.Fx 2.1
+.It Li compat22
+.Fx 2.2
+and
+.Fx 3.0
+a.out binary compatibility
+.It Li compat3x
+Compatibility with
+.Fx
+3.x
+(available for
+.Fx 4.0
+systems only)
+.It Li compat4x
+Compatibility with
+.Fx
+4.x
+(available for
+.Fx 5.0
+systems only)
+.It Li ports
+The ports collection.
+.It Li krb4
+KerberosIV binaries.
+.It Li krb5
+Kerberos5 binaries.
+.It Li ssecure
+/usr/src/secure
+.It Li sbase
+/usr/src/[top level files]
+.It Li scontrib
+/usr/src/contrib
+.It Li sgnu
+/usr/src/gnu
+.It Li setc
+/usr/src/etc
+.It Li sgames
+/usr/src/games
+.It Li sinclude
+/usr/src/include
+.It Li slib
+/usr/src/lib
+.It Li slibexec
+/usr/src/libexec
+.It Li srelease
+/usr/src/release
+.It Li sbin
+/usr/src/bin
+.It Li ssbin
+/usr/src/sbin
+.It Li sshare
+/usr/src/share
+.It Li ssys
+/usr/src/sys
+.It Li subin
+/usr/src/usr.bin
+.It Li susbin
+/usr/src/usr.sbin
+.It Li ssmailcf
+/usr/src/usr.sbin/sendmail/cf
+.It Li XF86-xc
+XFree86 official sources.
+.It Li XF86-co
+XFree86 contributed sources.
+.It Li Xbin
+XFree86 binaries.
+.It Li Xcfg
+XFree86 configuration files.
+.It Li Xdoc
+XFree86 documentation.
+.It Li Xhtml
+XFree86 HTML documentation.
+.It Li Xlib
+XFree86 libraries.
+.It Li Xlk98
+XFree86 server link-kit for PC98 machines.
+.It Li Xlkit
+XFree86 server link-kit for standard machines.
+.It Li Xman
+XFree86 manual pages.
+.It Li Xprog
+XFree86 programmer's distribution.
+.It Li Xps
+XFree86 postscript documentation.
+.It Li Xset
+XFree86 graphical setup tool.
+.It Li PC98-Servers/X9480
+XFree86 PC98 8-bit (256 color) PEGC-480 server.
+.It Li PC98-Servers/X9EGC
+XFree86 PC98 4-bit (16 color) EGC server.
+.It Li PC98-Servers/X9GA9
+XFree86 PC98 GA-968V4/PCI (S3 968) server.
+.It Li PC98-Servers/X9GAN
+XFree86 PC98 GANB-WAP (cirrus) server.
+.It Li PC98-Servers/X9LPW
+XFree86 PC98 PowerWindowLB (S3) server.
+.It Li PC98-Servers/X9MGA
+[DESCRIPTION MISSING]
+.It Li PC98-Servers/X9NKV
+XFree86 PC98 NKV-NEC (cirrus) server.
+.It Li PC98-Servers/X9NS3
+XFree86 PC98 NEC (S3) server.
+.It Li PC98-Servers/X9SPW
+XFree86 PC98 SKB-PowerWindow (S3) server.
+.It Li PC98-Servers/X9SVG
+[DESCRIPTION MISSING]
+.It Li PC98-Servers/X9TGU
+XFree86 PC98 Cyber9320 and TGUI9680 server.
+.It Li PC98-Servers/X9WEP
+XFree86 PC98 WAB-EP (cirrus) server.
+.It Li PC98-Servers/X9WS
+XFree86 PC98 WABS (cirrus) server.
+.It Li PC98-Servers/X9WSN
+XFree86 PC98 WSN-A2F (cirrus) server.
+.It Li Servers/X3DL
+XFree86 3D Labs server.
+.It Li Servers/X8514
+XFree86 8514 server.
+.It Li Servers/XAGX
+XFree86 8 bit AGX server.
+.It Li Servers/XI128
+XFree86 #9 Imagine I128 server.
+.It Li Servers/XMa8
+XFree86 ATI Mach8 server.
+.It Li Servers/XMa32
+XFree86 ATI Mach32 server.
+.It Li Servers/XMa64
+XFree86 ATI Mach64 server.
+.It Li Servers/XMono
+XFree86 monochrome server.
+.It Li Servers/XP9K
+XFree86 P9000 server.
+.It Li Servers/XS3
+XFree86 S3 server.
+.It Li Servers/XS3V
+XFree86 S3 Virge server.
+.It Li Servers/XSVGA
+XFree86 SVGA server.
+.It Li Servers/XVG16
+XFree86 VGA16 server.
+.It Li Servers/XW32
+XFree86 ET4000/W32, /W32i and /W32p server.
+.It Li Servers/XTGA
+Server for TGA cards (alpha architecture only).
+.It Li Servers/Xnest
+XFree86 nested X server.
+.It Li Servers/Xvfb
+XFree86 virtual frame-buffer X server.
+.It Li Xfnts
+XFree86 base font set.
+.It Li Xf100
+XFree86 100DPI font set.
+.It Li Xfcyr
+XFree86 Cyrillic font set.
+.It Li Xfscl
+XFree86 scalable font set.
+.It Li Xfnon
+XFree86 non-english font set.
+.It Li Xfsrv
+XFree86 font server.
+.El
+.El
+.It distSetDeveloper
+Selects the standard Developer's distribution set.
+.Pp
+.Sy Variables :
+None
+.It distSetXDeveloper
+Selects the standard X Developer's distribution set.
+.Pp
+.Sy Variables :
+None
+.It distSetKernDeveloper
+Selects the standard kernel Developer's distribution set.
+.Pp
+.Sy Variables :
+None
+.It distSetUser
+Selects the standard user distribution set.
+.Pp
+.Sy Variables :
+None
+.It distSetXUser
+Selects the standard X user's distribution set.
+.Pp
+.Sy Variables :
+None
+.It distSetMinimum
+Selects the very minimum distribution set.
+.Pp
+.Sy Variables :
+None
+.It distSetEverything
+Selects the full whack - all available distributions.
+.Pp
+.Sy Variables :
+None
+.It distSetCRYPTO
+Interactively select encryption subcomponents.
+.Pp
+.Sy Variables :
+None
+.It distSetSrc
+Interactively select source subcomponents.
+.Pp
+.Sy Variables :
+None
+.It distSetXF86
+Interactively select XFree86 subcomponents.
+.Pp
+.Sy Variables :
+None
+.It distExtractAll
+Install all currently selected distributions (requires that
+media device also be selected).
+.Pp
+.Sy Variables :
+None
+.It docBrowser
+Install (if necessary) an HTML documentation browser and go to the
+HTML documentation submenu.
+.Pp
+.Sy Variables :
+.Bl -tag -width indent
+.It browserPackage
+The name of the browser package to try and install as necessary.
+Defaults to latest links package.
+.It browserBinary
+The name of the browser binary itself (if overriding the
+.Ar browserPackage
+variable). Defaults to links.
+.El
+.It installCommit
+Commit any and all pending changes to disk. This function
+is essentially shorthand for a number of more granular "commit"
+functions.
+.Pp
+.Sy Variables :
+None
+.It installExpress
+Start an "express" installation, asking few questions of
+the user.
+.Pp
+.Sy Variables :
+None
+.It installStandard
+Start a "standard" installation, the most user-friendly
+installation type available.
+.Pp
+.Sy Variables :
+None
+.It installUpgrade
+Start an upgrade installation.
+.Pp
+.Sy Variables :
+None
+.It installFixitHoloShell
+Start up the "emergency holographic shell" over on VTY4
+if running as init. This will also happen automatically
+as part of the installation process unless
+.Ar noHoloShell
+is set.
+.Pp
+.Sy Variables :
+None
+.It installFixitCDROM
+Go into "fixit" mode, assuming a live file system CDROM
+currently in the drive.
+.Pp
+.Sy Variables :
+None
+.It installFixitFloppy
+Go into "fixit" mode, assuming an available fixit floppy
+disk (user will be prompted for it).
+.Pp
+.Sy Variables :
+None
+.It installFilesystems
+Do just the file system initialization part of an install.
+.Pp
+.Sy Variables :
+None
+.It installVarDefaults
+Initialize all variables to their defaults, overriding any
+previous settings.
+.Pp
+.Sy Variables :
+None
+.It loadConfig
+Sort of like an #include statement, it allows you to load one
+configuration file from another.
+.Pp
+.Sy Variables :
+.Bl -tag -width indent
+.It configFile
+The fully qualified pathname of the file to load.
+.El
+.It mediaClose
+If a media device is open, close it.
+.Pp
+.Sy Variables :
+None
+.It mediaSetCDROM
+Select a
+.Fx
+CDROM as the installation media.
+.Pp
+.Sy Variables :
+None
+.It mediaSetFloppy
+Select a pre-made floppy installation set as the installation media.
+.Pp
+.Sy Variables :
+None
+.It mediaSetDOS
+Select an existing DOS primary partition as the installation media.
+The first primary partition found is used (e.g. C:).
+.Pp
+.Sy Variables :
+None
+.It mediaSetTape
+Select a tape device as the installation media.
+.Pp
+.Sy Variables :
+None
+.It mediaSetFTP
+Select an FTP site as the installation media.
+.Pp
+.Sy Variables :
+.Bl -tag -width indent
+.It hostname
+The name of the host being installed (non-optional).
+.It domainname
+The domain name of the host being installed (optional).
+.It defaultrouter
+The default router for this host (non-optional).
+.It netDev
+Which host interface to use
+.Ar ( ed0
+or
+.Ar ep0 ,
+for example. Non-optional).
+.It netInteractive
+If set, bring up the interactive network setup form even
+if all relevant configuration variables are already set (optional).
+.It ipaddr
+The IP address for the selected host interface (non-optional).
+.It netmask
+The netmask for the selected host interface (non-optional).
+.It _ftpPath
+The fully qualified URL of the FTP site containing the
+.Fx
+distribution you're interested in, e.g.\&
+.Ar ftp://ftp.FreeBSD.org/pub/FreeBSD/ .
+.El
+.It mediaSetFTPActive
+Alias for
+.Ar mediaSetFTP
+using "active" FTP transfer mode.
+.Pp
+.Sy Variables :
+Same as for
+.Ar mediaSetFTP .
+.It mediaSetFTPPassive
+Alias for
+.Ar mediaSetFTP
+using "passive" FTP transfer mode.
+.Pp
+.Sy Variables :
+Same as for
+.Ar mediaSetFTP .
+.It mediaSetHTTP
+Alias for
+.Ar mediaSetFTP
+using an HTTP proxy.
+.Pp
+.Sy Variables :
+See
+.Ar mediaSetFTP ,
+plus
+.Bl -tag -width indent
+.It _httpPath
+The proxy to use (host:port) (non-optional).
+.El
+.It mediaSetUFS
+Select an existing UFS partition (mounted with the label editor) as
+the installation media.
+.Pp
+.Sy Variables :
+.Bl -tag -width indent
+.It ufs
+full /path to directory containing the
+.Fx
+distribution you're
+interested in.
+.El
+.It mediaSetNFS
+.Pp
+.Sy Variables :
+.Bl -tag -width indent
+.It hostname
+The name of the host being installed (non-optional).
+.It domainname
+The domain name of the host being installed (optional).
+.It defaultrouter
+The default router for this host (non-optional).
+.It netDev
+Which host interface to use
+.Ar ( ed0
+or
+.Ar ep0 ,
+for example. Non-optional).
+.It netInteractive
+If set, bring up the interactive network setup form even
+if all relevant configuration variables are already set (optional).
+.It ipaddr
+The IP address for the selected host interface (non-optional).
+.It netmask
+The netmask for the selected host interface (non-optional).
+.It nfs
+full hostname:/path specification for directory containing
+the
+.Fx
+distribution you're interested in.
+.El
+.It mediaSetFTPUserPass
+.Pp
+.Sy Variables :
+.Bl -tag -width indent
+.It ftpUser
+The username to log in as on the ftp server site.
+Default: ftp
+.It ftpPass
+The password to use for this username on the ftp
+server site.
+Default: user@host
+.El
+.It mediaSetCPIOVerbosity
+.Pp
+.Sy Variables :
+.Bl -tag -width indent
+.It cpioVerbose
+Can be used to set the verbosity of cpio extractions to low, medium or
+high.
+.El
+.It mediaGetType
+Interactively get the user to specify some type of media.
+.Pp
+.Sy Variables :
+None
+.It optionsEditor
+Invoke the interactive options editor.
+.Pp
+.Sy Variables :
+None
+.It packageAdd
+Try to fetch and add a package to the system (requires
+that a media type be set),
+.Pp
+.Sy Variables :
+.Bl -tag -width indent
+.It package
+The name of the package to add, e.g. bash-1.14.7 or ncftp-2.4.2.
+.El
+.It addGroup
+Invoke the interactive group editor.
+.Pp
+.Sy Variables :
+None
+.It addUser
+Invoke the interactive user editor.
+.Pp
+.Sy Variables :
+None
+.It shutdown
+Stop the script and terminate sysinstall.
+.Pp
+.Sy Variables :
+None
+.It system
+Execute an arbitrary command with
+.Xr system 3
+.Pp
+.Sy Variables :
+.Bl -tag -width indent
+.It command
+The name of the command to execute. When running
+from a boot floppy, very minimal expectations should
+be made as to what's available until/unless a relatively
+full system installation has just been done.
+.El
+.It tcpMenuSelect
+Configure a network device.
+.Pp
+.Sy Variables :
+Same as for
+.Ar mediaSetFTP
+except that
+.Ar _ftpPath
+is not used.
+.El
+.Sh DISTRIBUTION MEDIA
+The following files can be used to affect the operation of
+.Nm
+when used during initial system installation.
+.Bl -tag -width ".Pa packages/INDEX"
+.It Pa cdrom.inf
+A text file of properties, listed one per line, that describe the
+contents of the media in use.
+The syntax for each line is simply
+.Dq Ar property No = Ar value .
+Currently, only the following properties are recognized.
+.Bl -tag -width ".Va CD_MACHINE_ARCH"
+.It Va CD_VERSION
+This property should be set to the
+.Fx
+version on the current
+media volume.
+For example,
+.Dq Li "CD_VERSION = 4.6" .
+.It Va CD_MACHINE_ARCH
+This property should be set to the architecture of the contents on
+this volume.
+This property is normally only used with
+.Fx
+products that contain
+CDs for different architectures, to provide better error messages if
+users try to install Alpha packages on an i386 machine.
+For example,
+.Dq Li "CD_MACHINE_ARCH = alpha" .
+.It Va VOLUME
+In a multi-volume collection (such as the
+.Fx
+4-CD set), the
+.Pa ports/INDEX
+file on each disc should contain the full package index for the set.
+The last field of the
+.Pa INDEX
+file denotes which volume the package
+appears on, and the
+.Va VOLUME
+property here defines the volume ID of the current disc.
+.El
+.It Pa packages/INDEX
+The package index file.
+Each package is listed on a separate line with additional meta-data
+such as the required dependencies.
+This index is generated by
+.Dq Li "make index"
+from the
+.Xr ports 7
+collection.
+When multi-volume support is enabled, an additional field should be
+added to each line indicating which media volume contains the given
+package.
+.El
+.Pp
+For information about building a full release of
+.Fx ,
+please see
+.Xr release 7 .
+.Sh FILES
+This utility may edit the contents of
+.Pa /etc/rc.conf ,
+.Pa /etc/hosts ,
+and
+.Pa /etc/resolv.conf
+as necessary to reflect changes in the network configuration.
+.Sh SEE ALSO
+If you have a reasonably complete source tree online, take
+a look at
+.Pa /usr/src/usr.sbin/sysinstall/install.cfg
+for a sample installation script.
+.Sh BUGS
+This utility is a prototype which lasted several years past
+its expiration date and is greatly in need of death.
+.Sh AUTHORS
+.An Jordan K. Hubbard Aq jkh@FreeBSD.org
+.Sh HISTORY
+This version of
+.Nm
+first appeared in
+.Fx 2.0 .
diff --git a/usr.sbin/sysinstall/sysinstall.h b/usr.sbin/sysinstall/sysinstall.h
new file mode 100644
index 0000000..438a28e
--- /dev/null
+++ b/usr.sbin/sysinstall/sysinstall.h
@@ -0,0 +1,815 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated to essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#ifndef _SYSINSTALL_H_INCLUDE
+#define _SYSINSTALL_H_INCLUDE
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <setjmp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <dialog.h>
+#include "ui_objects.h"
+#include "dir.h"
+#include "colors.h"
+#include "libdisk.h"
+#include "dist.h"
+
+/*** Defines ***/
+
+#if defined(__i386__) || defined(PC98)
+#define PCCARD_ARCH 1 /* Support PCCARD installations */
+#endif
+/* 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_DESKSTYLE "_deskStyle"
+#define VAR_DISK "disk"
+#define VAR_DISKINTERACTIVE "diskInteractive"
+#define VAR_DISTS "dists"
+#define VAR_DIST_MAIN "distMain"
+#define VAR_DIST_CRYPTO "distCRYPTO"
+#define VAR_DIST_SRC "distSRC"
+#define VAR_DIST_X11 "distX11"
+#define VAR_DIST_XSERVER "distXserver"
+#define VAR_DIST_XFONTS "distXfonts"
+#define VAR_DEDICATE_DISK "dedicateDisk"
+#define VAR_DOMAINNAME "domainname"
+#define VAR_EDITOR "editor"
+#define VAR_EXTRAS "ifconfig_"
+#define VAR_COMMAND "command"
+#define VAR_CONFIG_FILE "configFile"
+#define VAR_FIXIT_TTY "fixitTty"
+#define VAR_FTP_DIR "ftpDirectory"
+#define VAR_FTP_PASS "ftpPass"
+#define VAR_FTP_PATH "_ftpPath"
+#define VAR_FTP_PORT "ftpPort"
+#define VAR_FTP_STATE "ftpState"
+#define VAR_FTP_USER "ftpUser"
+#define VAR_FTP_HOST "ftpHost"
+#define VAR_HTTP_PATH "_httpPath"
+#define VAR_HTTP_PROXY "httpProxy"
+#define VAR_HTTP_PORT "httpPort"
+#define VAR_HTTP_HOST "httpHost"
+#define VAR_HTTP_FTP_MODE "httpFtpMode"
+#define VAR_GATEWAY "defaultrouter"
+#define VAR_GEOMETRY "geometry"
+#define VAR_HOSTNAME "hostname"
+#define VAR_IFCONFIG "ifconfig_"
+#define VAR_INSTALL_CFG "installConfig"
+#define VAR_INSTALL_ROOT "installRoot"
+#define VAR_IPADDR "ipaddr"
+#define VAR_IPV6_ENABLE "ipv6_enable"
+#define VAR_IPV6ADDR "ipv6addr"
+#define VAR_KEYMAP "keymap"
+#define VAR_LABEL "label"
+#define VAR_LABEL_COUNT "labelCount"
+#define VAR_LINUX_ENABLE "linux_enable"
+#define VAR_MEDIA_TYPE "mediaType"
+#define VAR_MEDIA_TIMEOUT "MEDIA_TIMEOUT"
+#define VAR_MOUSED "moused_enable"
+#define VAR_MOUSED_FLAGS "moused_flags"
+#define VAR_MOUSED_PORT "moused_port"
+#define VAR_MOUSED_TYPE "moused_type"
+#define VAR_NAMESERVER "nameserver"
+#define VAR_NETINTERACTIVE "netInteractive"
+#define VAR_NETMASK "netmask"
+#define VAR_NETWORK_DEVICE "netDev"
+#define VAR_NEWFS_ARGS "newfsArgs"
+#define VAR_NFS_PATH "nfs"
+#define VAR_NFS_HOST "nfsHost"
+#define VAR_NFS_SECURE "nfs_reserved_port_only"
+#define VAR_NFS_SERVER "nfs_server_enable"
+#define VAR_NO_CONFIRM "noConfirm"
+#define VAR_NO_ERROR "noError"
+#define VAR_NO_HOLOSHELL "noHoloShell"
+#define VAR_NO_WARN "noWarn"
+#define VAR_NO_USR "noUsr"
+#define VAR_NO_TMP "noTmp"
+#define VAR_NO_HOME "noHome"
+#define VAR_NONINTERACTIVE "nonInteractive"
+#define VAR_NOVELL "novell"
+#define VAR_NTPDATE_FLAGS "ntpdate_flags"
+#define VAR_PACKAGE "package"
+#define VAR_PARTITION "partition"
+#define VAR_PCNFSD "pcnfsd"
+#define VAR_PKG_TMPDIR "PKG_TMPDIR"
+#define VAR_PORTS_PATH "ports"
+#define VAR_PPP_ENABLE "ppp_enable"
+#define VAR_PPP_PROFILE "ppp_profile"
+#define VAR_RELNAME "releaseName"
+#define VAR_ROOT_SIZE "rootSize"
+#define VAR_ROUTER "router"
+#define VAR_ROUTER_ENABLE "router_enable"
+#define VAR_ROUTERFLAGS "router_flags"
+#define VAR_SERIAL_SPEED "serialSpeed"
+#define VAR_SLOW_ETHER "slowEthernetCard"
+#define VAR_SWAP_SIZE "swapSize"
+#define VAR_TAPE_BLOCKSIZE "tapeBlocksize"
+#define VAR_TRY_DHCP "tryDHCP"
+#define VAR_TRY_RTSOL "tryRTSOL"
+#define VAR_UFS_PATH "ufs"
+#define VAR_USR_SIZE "usrSize"
+#define VAR_VAR_SIZE "varSize"
+#define VAR_TMP_SIZE "tmpSize"
+#define VAR_HOME_SIZE "homeSize"
+#define VAR_XF86_CONFIG "_xf86config"
+#define VAR_TERM "TERM"
+#define VAR_CONSTERM "_consterm"
+
+#define DEFAULT_TAPE_BLOCKSIZE "20"
+
+/* One MB worth of blocks */
+#define ONE_MEG 2048
+#define ONE_GIG (ONE_MEG * 1024)
+
+/* Which selection attributes to use */
+#define ATTR_SELECTED (ColorDisplay ? item_selected_attr : item_attr)
+#define ATTR_TITLE button_active_attr
+
+/* Handy strncpy() macro */
+#define SAFE_STRCPY(to, from) sstrncpy((to), (from), sizeof (to) - 1)
+
+/*** Types ***/
+typedef int Boolean;
+typedef struct disk Disk;
+typedef struct chunk Chunk;
+
+/* Bitfields for menu options */
+#define DMENU_NORMAL_TYPE 0x1 /* Normal dialog menu */
+#define DMENU_RADIO_TYPE 0x2 /* Radio dialog menu */
+#define DMENU_CHECKLIST_TYPE 0x4 /* Multiple choice menu */
+#define DMENU_SELECTION_RETURNS 0x8 /* Immediate return on item selection */
+
+typedef struct _dmenu {
+ int type; /* What sort of menu we are */
+ char *title; /* Our title */
+ char *prompt; /* Our prompt */
+ char *helpline; /* Line of help at bottom */
+ char *helpfile; /* Help file for "F1" */
+#if (__STDC_VERSION__ >= 199901L) || (__GNUC__ >= 3)
+ dialogMenuItem items[]; /* Array of menu items */
+#elif __GNUC__
+ dialogMenuItem items[0]; /* Array of menu items */
+#else
+#error "Create hack for C89 and K&R compilers."
+#endif
+} DMenu;
+
+/* An rc.conf variable */
+typedef struct _variable {
+ struct _variable *next;
+ char *name;
+ char *value;
+ int dirty;
+} Variable;
+
+#define NO_ECHO_OBJ(type) ((type) | (DITEM_NO_ECHO << 16))
+#define TYPE_OF_OBJ(type) ((type) & 0xff)
+#define ATTR_OF_OBJ(type) ((type) >> 16)
+
+/* A screen layout structure */
+typedef struct _layout {
+ int y; /* x & Y co-ordinates */
+ int x;
+ int len; /* The size of the dialog on the screen */
+ int maxlen; /* How much the user can type in ... */
+ char *prompt; /* The string for the prompt */
+ char *help; /* The display for the help line */
+ void *var; /* The var to set when this changes */
+ int type; /* The type of the dialog to create */
+ void *obj; /* The obj pointer returned by libdialog */
+} Layout;
+
+typedef enum {
+ DEVICE_TYPE_NONE,
+ DEVICE_TYPE_DISK,
+ DEVICE_TYPE_FLOPPY,
+ DEVICE_TYPE_FTP,
+ DEVICE_TYPE_NETWORK,
+ DEVICE_TYPE_CDROM,
+ DEVICE_TYPE_TAPE,
+ DEVICE_TYPE_DOS,
+ DEVICE_TYPE_UFS,
+ DEVICE_TYPE_NFS,
+ DEVICE_TYPE_ANY,
+ DEVICE_TYPE_HTTP,
+} DeviceType;
+
+/* CDROM mount codes */
+#define CD_UNMOUNTED 0
+#define CD_ALREADY_MOUNTED 1
+#define CD_WE_MOUNTED_IT 2
+
+/* A "device" from sysinstall's point of view */
+typedef struct _device {
+ char name[DEV_NAME_MAX];
+ char *description;
+ char *devname;
+ DeviceType type;
+ Boolean enabled;
+ Boolean (*init)(struct _device *dev);
+ FILE * (*get)(struct _device *dev, char *file, Boolean probe);
+ void (*shutdown)(struct _device *dev);
+ void *private;
+ unsigned int flags;
+ unsigned int volume;
+} Device;
+
+/* Some internal representations of partitions */
+typedef enum {
+ PART_NONE,
+ PART_SLICE,
+ PART_SWAP,
+ PART_FILESYSTEM,
+ PART_FAT,
+} 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];
+ int soft;
+} PartInfo;
+
+/* An option */
+typedef struct _opt {
+ char *name;
+ char *desc;
+ enum { OPT_IS_STRING, OPT_IS_INT, OPT_IS_FUNC, OPT_IS_VAR } type;
+ void *data;
+ void *aux;
+ char *(*check)();
+} Option;
+
+/* Weird index nodey things we use for keeping track of package information */
+typedef enum { PACKAGE, PLACE } node_type; /* Types of nodes */
+
+typedef struct _pkgnode { /* A node in the reconstructed hierarchy */
+ struct _pkgnode *next; /* My next sibling */
+ node_type type; /* What am I? */
+ char *name; /* My name */
+ char *desc; /* My description (Hook) */
+ struct _pkgnode *kids; /* My little children */
+ void *data; /* A place to hang my data */
+} PkgNode;
+typedef PkgNode *PkgNodePtr;
+
+/* A single package */
+typedef struct _indexEntry { /* A single entry in an INDEX file */
+ char *name; /* name */
+ char *path; /* full path to port */
+ char *prefix; /* port prefix */
+ char *comment; /* one line description */
+ char *descrfile; /* path to description file */
+ char *deps; /* packages this depends on */
+ int depc; /* how many depend on me */
+ int installed; /* indicates if it is installed */
+ char *maintainer; /* maintainer */
+ unsigned int volume; /* Volume of package */
+} IndexEntry;
+typedef IndexEntry *IndexEntryPtr;
+
+typedef int (*commandFunc)(char *key, void *data);
+
+#define HOSTNAME_FIELD_LEN 128
+#define IPADDR_FIELD_LEN 16
+#define EXTRAS_FIELD_LEN 128
+
+/* This is the structure that Network devices carry around in their private, erm, structures */
+typedef struct _devPriv {
+ int use_rtsol;
+ int use_dhcp;
+ char ipaddr[IPADDR_FIELD_LEN];
+ char netmask[IPADDR_FIELD_LEN];
+ char extras[EXTRAS_FIELD_LEN];
+} DevInfo;
+
+
+/*** Externs ***/
+extern jmp_buf BailOut; /* Used to get the heck out */
+extern int DebugFD; /* Where diagnostic output goes */
+extern Boolean Fake; /* Don't actually modify anything - testing */
+extern Boolean Restarting; /* Are we restarting sysinstall? */
+extern Boolean SystemWasInstalled; /* Did we install it? */
+extern Boolean RunningAsInit; /* Are we running stand-alone? */
+extern Boolean DialogActive; /* Is the dialog() stuff up? */
+extern Boolean ColorDisplay; /* Are we on a color display? */
+extern Boolean OnVTY; /* On a syscons VTY? */
+extern Variable *VarHead; /* The head of the variable chain */
+extern Device *mediaDevice; /* Where we're getting our distribution from */
+extern unsigned int Dists; /* Which distributions we want */
+extern unsigned int CRYPTODists; /* Which naughty distributions we want */
+extern unsigned int SrcDists; /* Which src distributions we want */
+extern unsigned int XF86Dists; /* Which XFree86 dists we want */
+extern unsigned int XF86ServerDists; /* The XFree86 servers we want */
+extern unsigned int XF86FontDists; /* The XFree86 fonts we want */
+extern int BootMgr; /* Which boot manager to use */
+extern int StatusLine; /* Where to print our status messages */
+extern DMenu MenuInitial; /* Initial installation menu */
+extern DMenu MenuFixit; /* Fixit repair menu */
+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 MenuKLD; /* Prototype KLD 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 MenuSecurity; /* System security options menu */
+extern DMenu MenuSecurityProfile; /* Security profile 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 MenuSysconsTtys; /* System console terminal type menu */
+extern DMenu MenuNetworking; /* Network configuration menu */
+extern DMenu MenuSendmail; /* Sendmail configuration menu */
+extern DMenu MenuInstallCustom; /* Custom Installation menu */
+extern DMenu MenuDistributions; /* Distribution menu */
+extern DMenu MenuDiskDevices; /* Disk type devices */
+extern DMenu MenuSubDistributions; /* Custom distribution menu */
+extern DMenu MenuSrcDistributions; /* Source distribution menu */
+extern DMenu MenuXF86; /* XFree86 main menu */
+extern DMenu MenuXF86Select; /* XFree86 distribution selection menu */
+extern DMenu MenuXF86SelectCore; /* XFree86 core distribution menu */
+extern DMenu MenuXF86SelectServer; /* XFree86 server distribution menu */
+extern DMenu MenuXF86SelectPC98Server; /* XFree86 server distribution menu */
+extern DMenu MenuXF86SelectFonts; /* XFree86 font selection menu */
+extern DMenu MenuXF86SelectFonts; /* XFree86 font selection menu */
+extern DMenu MenuXDesktops; /* Disk devices menu */
+extern DMenu MenuHTMLDoc; /* HTML Documentation menu */
+extern DMenu MenuUsermgmt; /* User management menu */
+extern DMenu MenuFixit; /* Fixit floppy/CDROM/shell menu */
+extern DMenu MenuXF86Config; /* Select XFree86 configuration type */
+extern int FixItMode; /* FixItMode starts shell onc urrent device (ie Serial port) */
+extern const char * StartName; /* Which name we were started as */
+
+/* Stuff from libdialog which isn't properly declared outside */
+extern void display_helpfile(void);
+extern void display_helpline(WINDOW *w, int y, int width);
+
+/*** Prototypes ***/
+
+/* anonFTP.c */
+extern int configAnonFTP(dialogMenuItem *self);
+
+/* cdrom.c */
+extern Boolean mediaInitCDROM(Device *dev);
+extern FILE *mediaGetCDROM(Device *dev, char *file, Boolean probe);
+extern void mediaShutdownCDROM(Device *dev);
+
+/* command.c */
+extern void command_clear(void);
+extern void command_sort(void);
+extern void command_execute(void);
+extern void command_shell_add(char *key, char *fmt, ...) __printflike(2, 3);
+extern void command_func_add(char *key, commandFunc func, void *data);
+
+/* config.c */
+extern void configEnvironmentRC_conf(void);
+extern void configEnvironmentResolv(char *config);
+extern void configRC_conf(void);
+extern int configFstab(dialogMenuItem *self);
+extern int configRC(dialogMenuItem *self);
+extern int configResolv(dialogMenuItem *self);
+extern int configPackages(dialogMenuItem *self);
+extern int configSaver(dialogMenuItem *self);
+extern int configSaverTimeout(dialogMenuItem *self);
+extern int configLinux(dialogMenuItem *self);
+extern int configNTP(dialogMenuItem *self);
+extern int configUsers(dialogMenuItem *self);
+extern int configXSetup(dialogMenuItem *self);
+extern int configXDesktop(dialogMenuItem *self);
+extern int configRouter(dialogMenuItem *self);
+extern int configPCNFSD(dialogMenuItem *self);
+extern int configInetd(dialogMenuItem *self);
+extern int configNFSServer(dialogMenuItem *self);
+extern int configWriteRC_conf(dialogMenuItem *self);
+extern int configSecurityProfile(dialogMenuItem *self);
+extern int configSecurityExtreme(dialogMenuItem *self);
+extern int configSecurityModerate(dialogMenuItem *self);
+extern int configEtcTtys(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);
+
+/* dhcp.c */
+extern int dhcpParseLeases(char *file, char *hostname, char *domain, char *nameserver,
+ char *ipaddr, char *gateway, char *netmask);
+
+/* 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 distUnsetCustom(dialogMenuItem *self);
+extern int distSetDeveloper(dialogMenuItem *self);
+extern int distSetXDeveloper(dialogMenuItem *self);
+extern int distSetKernDeveloper(dialogMenuItem *self);
+extern int distSetXKernDeveloper(dialogMenuItem *self);
+extern int distSetUser(dialogMenuItem *self);
+extern int distSetXUser(dialogMenuItem *self);
+extern int distSetMinimum(dialogMenuItem *self);
+extern int distSetEverything(dialogMenuItem *self);
+extern int distSetSrc(dialogMenuItem *self);
+extern int distSetXF86(dialogMenuItem *self);
+extern int distExtractAll(dialogMenuItem *self);
+
+/* dmenu.c */
+extern int dmenuDisplayFile(dialogMenuItem *tmp);
+extern int dmenuSubmenu(dialogMenuItem *tmp);
+extern int dmenuSystemCommand(dialogMenuItem *tmp);
+extern int dmenuSystemCommandBox(dialogMenuItem *tmp);
+extern int dmenuExit(dialogMenuItem *tmp);
+extern int dmenuISetVariable(dialogMenuItem *tmp);
+extern int dmenuSetVariable(dialogMenuItem *tmp);
+extern int dmenuSetKmapVariable(dialogMenuItem *tmp);
+extern int dmenuSetVariables(dialogMenuItem *tmp);
+extern int dmenuToggleVariable(dialogMenuItem *tmp);
+extern int dmenuSetFlag(dialogMenuItem *tmp);
+extern int dmenuSetValue(dialogMenuItem *tmp);
+extern Boolean dmenuOpen(DMenu *menu, int *choice, int *scroll, int *curr, int *max, Boolean buttons);
+extern Boolean dmenuOpenSimple(DMenu *menu, Boolean buttons);
+extern int dmenuVarCheck(dialogMenuItem *item);
+extern int dmenuVarsCheck(dialogMenuItem *item);
+extern int dmenuFlagCheck(dialogMenuItem *item);
+extern int dmenuRadioCheck(dialogMenuItem *item);
+
+/* doc.c */
+extern int docBrowser(dialogMenuItem *self);
+extern int docShowDocument(dialogMenuItem *self);
+
+/* dos.c */
+extern Boolean mediaCloseDOS(Device *dev, FILE *fp);
+extern Boolean mediaInitDOS(Device *dev);
+extern FILE *mediaGetDOS(Device *dev, char *file, Boolean probe);
+extern void mediaShutdownDOS(Device *dev);
+
+/* floppy.c */
+extern int getRootFloppy(void);
+extern Boolean mediaInitFloppy(Device *dev);
+extern FILE *mediaGetFloppy(Device *dev, char *file, Boolean probe);
+extern void mediaShutdownFloppy(Device *dev);
+
+/* ftp_strat.c */
+extern Boolean mediaCloseFTP(Device *dev, FILE *fp);
+extern Boolean mediaInitFTP(Device *dev);
+extern FILE *mediaGetFTP(Device *dev, char *file, Boolean probe);
+extern void mediaShutdownFTP(Device *dev);
+
+/* http.c */
+extern Boolean mediaInitHTTP(Device *dev);
+extern FILE *mediaGetHTTP(Device *dev, char *file, Boolean probe);
+
+/* globals.c */
+extern void globalsInit(void);
+
+/* index.c */
+int index_read(FILE *fp, PkgNodePtr papa);
+int index_menu(PkgNodePtr root, PkgNodePtr top, PkgNodePtr plist, int *pos, int *scroll);
+void index_init(PkgNodePtr top, PkgNodePtr plist);
+void index_node_free(PkgNodePtr top, PkgNodePtr plist);
+void index_sort(PkgNodePtr top);
+void index_print(PkgNodePtr top, int level);
+int index_extract(Device *dev, PkgNodePtr top, PkgNodePtr who, Boolean depended);
+int index_initialize(char *path);
+PkgNodePtr index_search(PkgNodePtr top, char *str, PkgNodePtr *tp);
+
+/* install.c */
+extern Boolean checkLabels(Boolean whinge, Chunk **rdev, Chunk **sdev, Chunk **udev, Chunk **vdev, Chunk **vtdev, Chunk **hdev);
+extern int installCommit(dialogMenuItem *self);
+extern int installCustomCommit(dialogMenuItem *self);
+extern int installExpress(dialogMenuItem *self);
+extern int installStandard(dialogMenuItem *self);
+extern int installFixitHoloShell(dialogMenuItem *self);
+extern int installFixitCDROM(dialogMenuItem *self);
+extern int installFixitFloppy(dialogMenuItem *self);
+extern int 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 int installX11package(dialogMenuItem *self);
+extern Boolean copySelf(void);
+
+/* kget.c */
+extern int kget(char *out);
+
+/* keymap.c */
+extern int loadKeymap(const char *lang);
+
+/* label.c */
+extern int diskLabelEditor(dialogMenuItem *self);
+extern int diskLabelCommit(dialogMenuItem *self);
+
+/* makedevs.c (auto-generated) */
+extern const char termcap_ansi[];
+extern const char termcap_vt100[];
+extern const char termcap_cons25w[];
+extern const char termcap_cons25[];
+extern const char termcap_cons25_m[];
+extern const char termcap_cons25r[];
+extern const char termcap_cons25r_m[];
+extern const char termcap_cons25l1[];
+extern const char termcap_cons25l1_m[];
+extern const char termcap_xterm[];
+extern const u_char font_iso_8x16[];
+extern const u_char font_cp850_8x16[];
+extern const u_char font_cp866_8x16[];
+extern const u_char koi8_r2cp866[];
+extern u_char default_scrnmap[];
+
+/* media.c */
+extern char *cpioVerbosity(void);
+extern void mediaClose(void);
+extern int mediaTimeout(void);
+extern int mediaSetCDROM(dialogMenuItem *self);
+extern int mediaSetFloppy(dialogMenuItem *self);
+extern int mediaSetDOS(dialogMenuItem *self);
+extern int mediaSetTape(dialogMenuItem *self);
+extern int mediaSetFTP(dialogMenuItem *self);
+extern int mediaSetFTPActive(dialogMenuItem *self);
+extern int mediaSetFTPPassive(dialogMenuItem *self);
+extern int mediaSetHTTP(dialogMenuItem *self);
+extern int mediaSetUFS(dialogMenuItem *self);
+extern int mediaSetNFS(dialogMenuItem *self);
+extern int mediaSetFTPUserPass(dialogMenuItem *self);
+extern int mediaSetCPIOVerbosity(dialogMenuItem *self);
+extern int mediaGetType(dialogMenuItem *self);
+extern Boolean mediaExtractDist(char *dir, char *dist, FILE *fp);
+extern Boolean mediaExtractDistBegin(char *dir, int *fd, int *zpid, int *cpic);
+extern Boolean mediaExtractDistEnd(int zpid, int cpid);
+extern Boolean mediaVerify(void);
+extern FILE *mediaGenericGet(char *base, const char *file);
+
+/* misc.c */
+extern Boolean file_readable(char *fname);
+extern Boolean file_executable(char *fname);
+extern Boolean directory_exists(const char *dirname);
+extern char *root_bias(char *path);
+extern char *itoa(int value);
+extern char *string_concat(char *p1, char *p2);
+extern char *string_concat3(char *p1, char *p2, char *p3);
+extern char *string_prune(char *str);
+extern char *string_skipwhite(char *str);
+extern char *string_copy(char *s1, char *s2);
+extern char *pathBaseName(const char *path);
+extern void safe_free(void *ptr);
+extern void *safe_malloc(size_t size);
+extern void *safe_realloc(void *orig, size_t size);
+extern dialogMenuItem *item_add(dialogMenuItem *list, char *prompt, char *title,
+ int (*checked)(dialogMenuItem *self),
+ int (*fire)(dialogMenuItem *self),
+ void (*selected)(dialogMenuItem *self, int is_selected),
+ void *data, int *aux, int *curr, int *max);
+extern void items_free(dialogMenuItem *list, int *curr, int *max);
+extern int Mkdir(char *);
+extern int 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);
+
+/* modules.c */
+extern void moduleInitialize(void);
+extern int kldBrowser(dialogMenuItem *self);
+
+/* mouse.c */
+extern int mousedTest(dialogMenuItem *self);
+extern int mousedDisable(dialogMenuItem *self);
+extern int setMouseFlags(dialogMenuItem *self);
+
+/* msg.c */
+extern Boolean isDebug(void);
+extern void msgInfo(char *fmt, ...) __printf0like(1, 2);
+extern void msgYap(char *fmt, ...) __printflike(1, 2);
+extern void msgWarn(char *fmt, ...) __printflike(1, 2);
+extern void msgDebug(char *fmt, ...) __printflike(1, 2);
+extern void msgError(char *fmt, ...) __printflike(1, 2);
+extern void msgFatal(char *fmt, ...) __printflike(1, 2);
+extern void msgConfirm(char *fmt, ...) __printflike(1, 2);
+extern void msgNotify(char *fmt, ...) __printflike(1, 2);
+extern void msgWeHaveOutput(char *fmt, ...) __printflike(1, 2);
+extern int msgYesNo(char *fmt, ...) __printflike(1, 2);
+extern int msgNoYes(char *fmt, ...) __printflike(1, 2);
+extern char *msgGetInput(char *buf, char *fmt, ...) __printflike(2, 3);
+extern int msgSimpleConfirm(char *);
+extern int msgSimpleNotify(char *);
+
+/* network.c */
+extern Boolean mediaInitNetwork(Device *dev);
+extern void mediaShutdownNetwork(Device *dev);
+
+/* nfs.c */
+extern Boolean mediaInitNFS(Device *dev);
+extern FILE *mediaGetNFS(Device *dev, char *file, Boolean probe);
+extern void mediaShutdownNFS(Device *dev);
+
+/* options.c */
+extern int optionsEditor(dialogMenuItem *self);
+
+/* package.c */
+extern int packageAdd(dialogMenuItem *self);
+extern int package_add(char *name);
+extern int package_extract(Device *dev, char *name, Boolean depended);
+extern Boolean package_exists(char *name);
+
+/* pccard.c */
+extern void pccardInitialize(void);
+
+/* system.c */
+extern void systemInitialize(int argc, char **argv);
+extern void systemShutdown(int status);
+extern int execExecute(char *cmd, char *name);
+extern int systemExecute(char *cmd);
+extern void systemSuspendDialog(void);
+extern void systemResumeDialog(void);
+extern int systemDisplayHelp(char *file);
+extern char *systemHelpFile(char *file, char *buf);
+extern void systemChangeFont(const u_char font[]);
+extern void systemChangeLang(char *lang);
+extern void systemChangeTerminal(char *color, const u_char c_termcap[], char *mono, const u_char m_termcap[]);
+extern void systemChangeScreenmap(const u_char newmap[]);
+extern void systemCreateHoloshell(void);
+extern int vsystem(char *fmt, ...) __printflike(1, 2);
+
+/* tape.c */
+extern char *mediaTapeBlocksize(void);
+extern Boolean mediaInitTape(Device *dev);
+extern FILE *mediaGetTape(Device *dev, char *file, Boolean probe);
+extern void mediaShutdownTape(Device *dev);
+
+/* tcpip.c */
+extern int tcpOpenDialog(Device *dev);
+extern int tcpMenuSelect(dialogMenuItem *self);
+extern Device *tcpDeviceSelect(void);
+
+/* termcap.c */
+extern int set_termcap(void);
+
+/* ttys.c */
+extern void configTtys(void);
+
+/* ufs.c */
+extern void mediaShutdownUFS(Device *dev);
+extern Boolean mediaInitUFS(Device *dev);
+extern FILE *mediaGetUFS(Device *dev, char *file, Boolean probe);
+
+/* usb.c */
+extern void usbInitialize(void);
+
+/* user.c */
+extern int userAddGroup(dialogMenuItem *self);
+extern int userAddUser(dialogMenuItem *self);
+
+/* variable.c */
+extern void variable_set(char *var, int dirty);
+extern void variable_set2(char *name, char *value, int dirty);
+extern char *variable_get(char *var);
+extern int variable_cmp(char *var, char *value);
+extern void variable_unset(char *var);
+extern char *variable_get_value(char *var, char *prompt, int dirty);
+extern int variable_check(char *data);
+extern int dump_variables(dialogMenuItem *self);
+extern void free_variables(void);
+extern void pvariable_set(char *var);
+extern char *pvariable_get(char *var);
+
+/* wizard.c */
+extern void slice_wizard(Disk *d);
+
+/*
+ * Macros. Please find a better place for us!
+ */
+#define DEVICE_INIT(d) ((d) != NULL ? (d)->init((d)) : NULL)
+#define DEVICE_GET(d, b, f) ((d) != NULL ? (d)->get((d), (b), (f)) : NULL)
+#define DEVICE_SHUTDOWN(d) ((d) != NULL ? (d)->shutdown((d)) : (void)0)
+
+#ifdef USE_GZIP
+#define UNZIPPER "gunzip"
+#else
+#define UNZIPPER "bunzip2"
+#endif
+
+#endif
+/* _SYSINSTALL_H_INCLUDE */
diff --git a/usr.sbin/sysinstall/system.c b/usr.sbin/sysinstall/system.c
new file mode 100644
index 0000000..2ad3a48
--- /dev/null
+++ b/usr.sbin/sysinstall/system.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.
+ *
+ * $FreeBSD$
+ *
+ * Jordan Hubbard
+ *
+ * My contributions are in the public domain.
+ *
+ * Parts of this file are also blatently stolen from Poul-Henning Kamp's
+ * previous version of sysinstall, and as such fall under his "BEERWARE license"
+ * so buy him a beer if you like it! Buy him a beer for me, too!
+ * Heck, get him completely drunk and send me pictures! :-)
+ */
+
+#include "sysinstall.h"
+#include <signal.h>
+#include <termios.h>
+#include <sys/reboot.h>
+#include <sys/consio.h>
+#include <sys/fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
+
+
+/* Where we stick our temporary expanded doc file */
+#define DOC_TMP_DIR "/tmp/.doc"
+#define DOC_TMP_FILE "/tmp/.doc/doc.tmp"
+
+static pid_t ehs_pid;
+
+/*
+ * Handle interrupt signals - this probably won't work in all cases
+ * due to our having bogotified the internal state of dialog or curses,
+ * but we'll give it a try.
+ */
+static int
+intr_continue(dialogMenuItem *self)
+{
+ return DITEM_LEAVE_MENU;
+}
+
+static int
+intr_reboot(dialogMenuItem *self)
+{
+ systemShutdown(-1);
+ /* NOTREACHED */
+ return 0;
+}
+
+static int
+intr_restart(dialogMenuItem *self)
+{
+ int ret, fd, fdmax;
+
+ mediaClose();
+ free_variables();
+ fdmax = getdtablesize();
+ for (fd = 3; fd < fdmax; fd++)
+ close(fd);
+ ret = execl(StartName, StartName, "-restart", (char *)NULL);
+ msgDebug("execl failed (%s)\n", strerror(errno));
+ /* NOTREACHED */
+ return -1;
+}
+
+static dialogMenuItem intrmenu[] = {
+ { "Abort", "Abort the installation", NULL, intr_reboot },
+ { "Restart", "Restart the installation program", NULL, intr_restart },
+ { "Continue", "Continue the installation", NULL, intr_continue },
+};
+
+
+static void
+handle_intr(int sig)
+{
+ WINDOW *save = savescr();
+
+ use_helpline(NULL);
+ use_helpfile(NULL);
+ if (OnVTY) {
+ ioctl(0, VT_ACTIVATE, 1); /* Switch back */
+ msgInfo(NULL);
+ }
+ (void)dialog_menu("Installation interrupt",
+ "Do you want to abort the installation?",
+ -1, -1, 3, -3, intrmenu, NULL, NULL, NULL);
+ restorescr(save);
+}
+
+/*
+ * Harvest children if we are init.
+ */
+static void
+reap_children(int sig)
+{
+ int errbak = errno;
+
+ while (waitpid(-1, NULL, WNOHANG) > 0)
+ ;
+ errno = errbak;
+}
+
+/* Expand a file into a convenient location, nuking it each time */
+static char *
+expand(char *fname)
+{
+ char *gunzip = RunningAsInit ? "/stand/gunzip" : "/usr/bin/gunzip";
+
+ if (!directory_exists(DOC_TMP_DIR)) {
+ Mkdir(DOC_TMP_DIR);
+ if (chown(DOC_TMP_DIR, 0, 0) < 0)
+ return NULL;
+ if (chmod(DOC_TMP_DIR, S_IRWXU) < 0)
+ return NULL;
+ }
+ else
+ unlink(DOC_TMP_FILE);
+ if (!file_readable(fname) || vsystem("%s < %s > %s", gunzip, fname, DOC_TMP_FILE))
+ return NULL;
+ return DOC_TMP_FILE;
+}
+
+/* Initialize system defaults */
+void
+systemInitialize(int argc, char **argv)
+{
+ size_t i;
+ int boothowto;
+ sigset_t signalset;
+
+ signal(SIGINT, SIG_IGN);
+ globalsInit();
+
+ i = sizeof(boothowto);
+ if (!sysctlbyname("debug.boothowto", &boothowto, &i, NULL, NULL) &&
+ (i == sizeof(boothowto)) && (boothowto & RB_VERBOSE))
+ variable_set2(VAR_DEBUG, "YES", 0);
+
+ /* Are we running as init? */
+ if (getpid() == 1) {
+ int fd;
+
+ RunningAsInit = 1;
+ setsid();
+ close(0);
+ fd = open("/dev/ttyv0", O_RDWR);
+ if (fd == -1) {
+ fd = open("/dev/console", O_RDWR); /* fallback */
+ variable_set2(VAR_FIXIT_TTY, "serial", 0); /* give fixit a hint */
+ } else
+ OnVTY = TRUE;
+ /*
+ * To make _sure_ we're on a VTY and don't have /dev/console switched
+ * away to a serial port or something, attempt to set the cursor appearance.
+ */
+ if (OnVTY) {
+ int fd2, type;
+
+ type = 0; /* normal */
+ if ((fd2 = open("/dev/console", O_RDWR)) != -1) {
+ if (ioctl(fd2, CONS_CURSORTYPE, &type) == -1) {
+ OnVTY = FALSE;
+ variable_set2(VAR_FIXIT_TTY, "serial", 0); /* Tell Fixit
+ the console
+ type */
+ close(fd); close(fd2);
+ open("/dev/console", O_RDWR);
+ }
+ else
+ close(fd2);
+ }
+ }
+ close(1); dup(0);
+ close(2); dup(0);
+ printf("%s running as init on %s\n", argv[0], OnVTY ? "vty0" : "serial console");
+ ioctl(0, TIOCSCTTY, (char *)NULL);
+ setlogin("root");
+ setenv("PATH", "/stand:/bin:/sbin:/usr/sbin:/usr/bin:/mnt/bin:/mnt/sbin:/mnt/usr/sbin:/mnt/usr/bin:/usr/X11R6/bin", 1);
+ setbuf(stdin, 0);
+ setbuf(stderr, 0);
+#ifdef __alpha__
+ i = 0;
+ sysctlbyname("machdep.unaligned_print", NULL, 0, &i, sizeof(i));
+#endif
+#if 0
+ signal(SIGCHLD, reap_children);
+#endif
+ }
+ else {
+ char hname[256];
+
+ /* Initalize various things for a multi-user environment */
+ if (!gethostname(hname, sizeof hname))
+ variable_set2(VAR_HOSTNAME, hname, 0);
+ }
+
+ if (set_termcap() == -1) {
+ printf("Can't find terminal entry\n");
+ exit(-1);
+ }
+
+ /* XXX - libdialog has particularly bad return value checking */
+ init_dialog();
+
+ /* If we haven't crashed I guess dialog is running ! */
+ DialogActive = TRUE;
+
+ /* Make sure HOME is set for those utilities that need it */
+ if (!getenv("HOME"))
+ setenv("HOME", "/", 1);
+ signal(SIGINT, handle_intr);
+ /*
+ * Make sure we can be interrupted even if we were re-executed
+ * from an interrupt.
+ */
+ sigemptyset(&signalset);
+ sigaddset(&signalset, SIGINT);
+ sigprocmask(SIG_UNBLOCK, &signalset, NULL);
+
+ (void)vsystem("rm -rf %s", DOC_TMP_DIR);
+}
+
+/* Close down and prepare to exit */
+void
+systemShutdown(int status)
+{
+ /* If some media is open, close it down */
+ if (status >=0)
+ mediaClose();
+
+ /* write out any changes to rc.conf .. */
+ configRC_conf();
+
+ /* Shut down the dialog library */
+ if (DialogActive) {
+ end_dialog();
+ DialogActive = FALSE;
+ }
+
+ /* Shut down curses */
+ endwin();
+
+ /* If we have a temporary doc dir lying around, nuke it */
+ (void)vsystem("rm -rf %s", DOC_TMP_DIR);
+
+ /* REALLY exit! */
+ if (RunningAsInit) {
+ /* Put the console back */
+ ioctl(0, VT_ACTIVATE, 2);
+#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;
+ WINDOW *w = savescr();
+
+ dialog_clear();
+ dialog_update();
+ end_dialog();
+ DialogActive = FALSE;
+ if (tcgetattr(0, &foo) != -1) {
+ foo.c_cc[VERASE] = '\010';
+ tcsetattr(0, TCSANOW, &foo);
+ }
+ if (!Fake)
+ status = system(command);
+ else {
+ status = 0;
+ msgDebug("systemExecute: Faked execution of `%s'\n", command);
+ }
+ DialogActive = TRUE;
+ restorescr(w);
+ return status;
+}
+
+/* suspend/resume libdialog/curses screen */
+static WINDOW *oldW;
+
+void
+systemSuspendDialog(void)
+{
+
+ oldW = savescr();
+ dialog_clear();
+ dialog_update();
+ end_dialog();
+ DialogActive = FALSE;
+}
+
+void
+systemResumeDialog(void)
+{
+
+ DialogActive = TRUE;
+ restorescr(oldW);
+}
+
+/* Display a help file in a filebox */
+int
+systemDisplayHelp(char *file)
+{
+ char *fname = NULL;
+ char buf[FILENAME_MAX];
+ int ret = 0;
+ WINDOW *w = savescr();
+
+ fname = systemHelpFile(file, buf);
+ if (!fname) {
+ snprintf(buf, FILENAME_MAX, "The %s file is not provided on this particular floppy image.", file);
+ use_helpfile(NULL);
+ use_helpline(NULL);
+ dialog_mesgbox("Sorry!", buf, -1, -1);
+ ret = 1;
+ }
+ else {
+ use_helpfile(NULL);
+ use_helpline(NULL);
+ dialog_textbox(file, fname, LINES, COLS);
+ }
+ restorescr(w);
+ return ret;
+}
+
+char *
+systemHelpFile(char *file, char *buf)
+{
+ if (!file)
+ return NULL;
+ if (file[0] == '/')
+ return file;
+ snprintf(buf, FILENAME_MAX, "/stand/help/%s.hlp.gz", file);
+ if (file_readable(buf))
+ return expand(buf);
+ snprintf(buf, FILENAME_MAX, "/stand/help/%s.TXT.gz", file);
+ if (file_readable(buf))
+ return expand(buf);
+ snprintf(buf, FILENAME_MAX, "/usr/src/usr.sbin/sysinstall/help/%s.hlp", file);
+ if (file_readable(buf))
+ return buf;
+ snprintf(buf, FILENAME_MAX, "/usr/src/usr.sbin/sysinstall/help/%s.TXT", file);
+ if (file_readable(buf))
+ return buf;
+ return NULL;
+}
+
+void
+systemChangeTerminal(char *color, const u_char c_term[],
+ char *mono, const u_char m_term[])
+{
+ if (OnVTY) {
+ int setupterm(char *color, int, int *);
+
+ if (ColorDisplay) {
+ setenv("TERM", color, 1);
+ setenv("TERMCAP", c_term, 1);
+ reset_shell_mode();
+ setterm(color);
+ cbreak(); noecho();
+ }
+ else {
+ setenv("TERM", mono, 1);
+ setenv("TERMCAP", m_term, 1);
+ reset_shell_mode();
+ setterm(mono);
+ cbreak(); noecho();
+ }
+ }
+ clear();
+ refresh();
+ dialog_clear();
+}
+
+int
+vsystem(char *fmt, ...)
+{
+ va_list args;
+ int pstat;
+ pid_t pid;
+ int omask;
+ sig_t intsave, quitsave;
+ char *cmd;
+ int i;
+
+ cmd = (char *)alloca(FILENAME_MAX);
+ cmd[0] = '\0';
+ va_start(args, fmt);
+ vsnprintf(cmd, FILENAME_MAX, fmt, args);
+ va_end(args);
+
+ omask = sigblock(sigmask(SIGCHLD));
+ if (Fake) {
+ msgDebug("vsystem: Faked execution of `%s'\n", cmd);
+ return 0;
+ }
+ if (isDebug())
+ msgDebug("Executing command `%s'\n", cmd);
+ pid = fork();
+ if (pid == -1) {
+ (void)sigsetmask(omask);
+ i = 127;
+ }
+ else if (!pid) { /* Junior */
+ (void)sigsetmask(omask);
+ if (DebugFD != -1) {
+ dup2(DebugFD, 0);
+ dup2(DebugFD, 1);
+ dup2(DebugFD, 2);
+ }
+ else {
+ close(1); open("/dev/null", O_WRONLY);
+ dup2(1, 2);
+ }
+ if (!RunningAsInit)
+ execl("/bin/sh", "/bin/sh", "-c", cmd, (char *)NULL);
+ else
+ execl("/stand/sh", "/stand/sh", "-c", cmd, (char *)NULL);
+ exit(1);
+ }
+ else {
+ intsave = signal(SIGINT, SIG_IGN);
+ quitsave = signal(SIGQUIT, SIG_IGN);
+ pid = waitpid(pid, &pstat, 0);
+ (void)sigsetmask(omask);
+ (void)signal(SIGINT, intsave);
+ (void)signal(SIGQUIT, quitsave);
+ i = (pid == -1) ? -1 : WEXITSTATUS(pstat);
+ if (isDebug())
+ msgDebug("Command `%s' returns status of %d\n", cmd, i);
+ }
+ return i;
+}
+
+void
+systemCreateHoloshell(void)
+{
+ int waitstatus;
+
+ if ((FixItMode || OnVTY) && RunningAsInit) {
+
+ if (ehs_pid != 0) {
+ int pstat;
+
+ if (kill(ehs_pid, 0) == 0) {
+
+ if (msgNoYes("There seems to be an emergency holographic shell\n"
+ "already running on VTY 4.\n\n"
+ "Kill it and start a new one?"))
+ return;
+
+ /* try cleaning up as much as possible */
+ (void) kill(ehs_pid, SIGHUP);
+ sleep(1);
+ (void) kill(ehs_pid, SIGKILL);
+ }
+
+ /* avoid too many zombies */
+ (void) waitpid(ehs_pid, &pstat, WNOHANG);
+ }
+
+ if (strcmp(variable_get(VAR_FIXIT_TTY), "serial") == 0)
+ systemSuspendDialog(); /* must be before the fork() */
+ if ((ehs_pid = fork()) == 0) {
+ int i, fd;
+ struct termios foo;
+ extern int login_tty(int);
+
+ ioctl(0, TIOCNOTTY, NULL);
+ for (i = getdtablesize(); i >= 0; --i)
+ close(i);
+ if (strcmp(variable_get(VAR_FIXIT_TTY), "serial") == 0)
+ fd = open("/dev/console", O_RDWR);
+ else
+ fd = open("/dev/ttyv3", O_RDWR);
+ ioctl(0, TIOCSCTTY, &fd);
+ dup2(0, 1);
+ dup2(0, 2);
+ DebugFD = 2;
+ if (login_tty(fd) == -1)
+ msgDebug("Doctor: I can't set the controlling terminal.\n");
+ signal(SIGTTOU, SIG_IGN);
+ if (tcgetattr(fd, &foo) != -1) {
+ foo.c_cc[VERASE] = '\010';
+ if (tcsetattr(fd, TCSANOW, &foo) == -1)
+ msgDebug("Doctor: I'm unable to set the erase character.\n");
+ }
+ else
+ msgDebug("Doctor: I'm unable to get the terminal attributes!\n");
+ if (strcmp(variable_get(VAR_FIXIT_TTY), "serial") == 0) {
+ printf("Type ``exit'' in this fixit shell to resume sysinstall.\n\n");
+ fflush(stdout);
+ }
+ execlp("sh", "-sh", 0);
+ msgDebug("Was unable to execute sh for Holographic shell!\n");
+ exit(1);
+ }
+ else {
+ if (strcmp(variable_get(VAR_FIXIT_TTY), "standard") == 0) {
+ WINDOW *w = savescr();
+
+ msgNotify("Starting an emergency holographic shell on VTY4");
+ sleep(2);
+ restorescr(w);
+ }
+ else {
+ (void)waitpid(ehs_pid, &waitstatus, 0); /* we only wait for
+ shell to finish
+ it serial mode
+ since there is no
+ virtual console */
+ systemResumeDialog();
+ }
+ }
+ }
+}
diff --git a/usr.sbin/sysinstall/tape.c b/usr.sbin/sysinstall/tape.c
new file mode 100644
index 0000000..a5ce339
--- /dev/null
+++ b/usr.sbin/sysinstall/tape.c
@@ -0,0 +1,127 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated to essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/* These routines deal with getting things off of tape media */
+
+#include "sysinstall.h"
+#include <sys/fcntl.h>
+#include <sys/param.h>
+
+static Boolean tapeInitted;
+
+char *
+mediaTapeBlocksize(void)
+{
+ char *cp = variable_get(VAR_TAPE_BLOCKSIZE);
+
+ return cp ? cp : DEFAULT_TAPE_BLOCKSIZE;
+}
+
+Boolean
+mediaInitTape(Device *dev)
+{
+ /* This is REALLY gross, but we need to do the init later in get due to the fact
+ * that media is initialized BEFORE a filesystem is mounted now.
+ */
+ return TRUE;
+}
+
+FILE *
+mediaGetTape(Device *dev, char *file, Boolean probe)
+{
+ char buf[PATH_MAX];
+ FILE *fp;
+
+ int i;
+
+ if (!tapeInitted) {
+ WINDOW *w = savescr();
+
+ msgDebug("Tape init routine called for %s (private dir is %s)\n",
+ dev->name, (char *)dev->private);
+ Mkdir(dev->private);
+ if (chdir(dev->private)) {
+ msgConfirm("Unable to CD to %s before extracting tape!\n"
+ "Tape media is not selected and thus cannot be installed from.",
+ (char *)dev->private);
+ return (FILE *)IO_ERROR;
+ }
+ /* We know the tape is already in the drive, so go for it */
+ msgNotify("First extracting distributions from %s...", dev->description);
+ if (!strcmp(dev->name, "rft0"))
+ i = vsystem("ft | cpio -idum %s --block-size %s", cpioVerbosity(), mediaTapeBlocksize());
+ else
+ i = vsystem("cpio -idum %s --block-size %s -I %s", cpioVerbosity(), mediaTapeBlocksize(), dev->devname);
+ if (!i) {
+ tapeInitted = TRUE;
+ msgDebug("Tape initialized successfully.\n");
+ }
+ else {
+ msgConfirm("Tape extract command failed with status %d!\n"
+ "Unable to use tape media.", i);
+ restorescr(w);
+ return (FILE *)IO_ERROR;
+ }
+ restorescr(w);
+ }
+
+ sprintf(buf, "%s/%s", (char *)dev->private, file);
+ if (isDebug())
+ msgDebug("Request for %s from tape (looking in %s)\n", file, buf);
+ if (file_readable(buf))
+ fp = fopen(buf, "r");
+ else {
+ sprintf(buf, "%s/releases/%s", (char *)dev->private, file);
+ fp = fopen(buf, "r");
+ }
+ /* Nuke the files behind us to save space */
+ if (fp)
+ unlink(buf);
+ return fp;
+}
+
+void
+mediaShutdownTape(Device *dev)
+{
+ if (!tapeInitted)
+ return;
+ if (file_readable((char *)dev->private)) {
+ msgDebug("Cleaning up results of tape extract in %s..",
+ (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..27f353f
--- /dev/null
+++ b/usr.sbin/sysinstall/tcpip.c
@@ -0,0 +1,693 @@
+/*
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Gary J Palmer. All rights reserved.
+ * Copyright (c) 1996
+ * Jordan K. Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/*
+ * All kinds of hacking also performed by jkh on this code. Don't
+ * blame Gary for every bogosity you see here.. :-)
+ *
+ * -jkh
+ */
+
+#include "sysinstall.h"
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+/* The help file for the TCP/IP setup screen */
+#define TCP_HELPFILE "tcp"
+
+/* These are nasty, but they make the layout structure a lot easier ... */
+
+static char hostname[HOSTNAME_FIELD_LEN], domainname[HOSTNAME_FIELD_LEN],
+ gateway[IPADDR_FIELD_LEN], nameserver[INET6_ADDRSTRLEN];
+static int okbutton, cancelbutton;
+static char ipaddr[IPADDR_FIELD_LEN], netmask[IPADDR_FIELD_LEN], extras[EXTRAS_FIELD_LEN];
+static char ipv6addr[INET6_ADDRSTRLEN];
+
+/* What the screen size is meant to be */
+#define TCP_DIALOG_Y 0
+#define TCP_DIALOG_X 8
+#define TCP_DIALOG_WIDTH COLS - 16
+#define TCP_DIALOG_HEIGHT LINES - 2
+
+static Layout layout[] = {
+#define LAYOUT_HOSTNAME 0
+ { 1, 2, 25, HOSTNAME_FIELD_LEN - 1,
+ "Host:", "Your fully-qualified hostname, e.g. foo.bar.com",
+ hostname, STRINGOBJ, NULL },
+#define LAYOUT_DOMAINNAME 1
+ { 1, 35, 20, HOSTNAME_FIELD_LEN - 1,
+ "Domain:",
+ "The name of the domain that your machine is in, e.g. bar.com",
+ domainname, STRINGOBJ, NULL },
+#define LAYOUT_GATEWAY 2
+ { 5, 2, 18, IPADDR_FIELD_LEN - 1,
+ "IPv4 Gateway:",
+ "IPv4 address of host forwarding packets to non-local destinations",
+ gateway, STRINGOBJ, NULL },
+#define LAYOUT_NAMESERVER 3
+ { 5, 35, 18, INET6_ADDRSTRLEN - 1,
+ "Name server:", "IPv4 or IPv6 address of your local DNS server",
+ nameserver, STRINGOBJ, NULL },
+#define LAYOUT_IPADDR 4
+ { 10, 10, 18, IPADDR_FIELD_LEN - 1,
+ "IPv4 Address:",
+ "The IPv4 address to be used for this interface",
+ ipaddr, STRINGOBJ, NULL },
+#define LAYOUT_NETMASK 5
+ { 10, 35, 18, IPADDR_FIELD_LEN - 1,
+ "Netmask:",
+ "The netmask for this interface, e.g. 0xffffff00 for a class C network",
+ netmask, STRINGOBJ, NULL },
+#define LAYOUT_EXTRAS 6
+ { 14, 10, 37, HOSTNAME_FIELD_LEN - 1,
+ "Extra options to ifconfig:",
+ "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("%s", msg);
+}
+
+/* Verify IP address integrity */
+static int
+verifyIP(char *ip, unsigned long *mask, unsigned long *out)
+{
+ long a, b, c, d;
+ char *endptr;
+
+ unsigned long parsedip;
+ unsigned long max_addr = (255 << 24) | (255 << 16) | (255 << 8) | 255;
+
+ if (ip == NULL)
+ return 0;
+ a = strtol(ip, &endptr, 10);
+ if (*endptr++ != '.')
+ return 0;
+ b = strtol(endptr, &endptr, 10);
+ if (*endptr++ != '.')
+ return 0;
+ c = strtol(endptr, &endptr, 10);
+ if (*endptr++ != '.')
+ return 0;
+ d = strtol(endptr, &endptr, 10);
+ if (*endptr != '\0')
+ return 0;
+ if (!_validByte(a) || !_validByte(b) || !_validByte(c) || !_validByte(d))
+ return 0;
+ parsedip = (a << 24) | (b << 16) | (c << 8) | d;
+ if (out)
+ *out = parsedip;
+ /*
+ * The ip address must not be network or broadcast address.
+ */
+ if (mask && ((parsedip == (parsedip & *mask)) ||
+ (parsedip == ((parsedip & *mask) + max_addr - *mask))))
+ return 0;
+ return 1;
+}
+
+static int
+verifyIP6(char *ip)
+{
+ struct addrinfo hints, *res;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET6;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
+ if (getaddrinfo(ip, NULL, &hints, &res) == 0) {
+ freeaddrinfo(res);
+ return 1;
+ }
+ return 0;
+}
+
+/* Verify IPv4 netmask as being well-formed as
+ a 0x or AAA.BBB.CCC.DDD mask */
+static int
+verifyNetmask(const char *netmask, unsigned long *out)
+{
+ unsigned long mask;
+ unsigned long tmp;
+ char *endptr;
+
+ if (netmask[0] == '0' && (netmask[1] == 'x' || netmask[1] == 'X')) {
+ /* Parse out hex mask */
+ mask = strtoul(netmask, &endptr, 0);
+ if (*endptr != '\0')
+ return 0;
+ } else {
+ /* Parse out quad decimal mask */
+ mask = strtoul(netmask, &endptr, 10);
+ if (!_validByte(mask) || *endptr++ != '.')
+ return 0;
+ tmp = strtoul(endptr, &endptr, 10);
+ if (!_validByte(tmp) || *endptr++ != '.')
+ return 0;
+ mask = (mask << 8) + tmp;
+ tmp = strtoul(endptr, &endptr, 10);
+ if (!_validByte(tmp) || *endptr++ != '.')
+ return 0;
+ mask = (mask << 8) + tmp;
+ tmp = strtoul(endptr, &endptr, 10);
+ if (!_validByte(tmp) || *endptr++ != '\0')
+ return 0;
+ mask = (mask << 8) + tmp;
+ }
+ /* Verify that we have a continous netmask */
+ if ((((-mask & mask) - 1) | mask) != 0xffffffff)
+ return 0;
+ if (out)
+ *out = mask;
+ return 1;
+}
+
+static int
+verifyGW(char *gw, unsigned long *ip, unsigned long *mask)
+{
+ unsigned long parsedgw;
+
+ if (!verifyIP(gw, mask, &parsedgw))
+ return 0;
+ /* Gateway needs to be within the set of IPs reachable through the
+ interface */
+ if (ip && mask && ((parsedgw & *mask) != (*ip & *mask)))
+ return 0;
+ return 1;
+}
+
+/* Check for the settings on the screen - the per-interface stuff is
+ moved to the main handling code now to do it on the fly - sigh */
+static int
+verifySettings(void)
+{
+ unsigned long parsedip;
+ unsigned long parsednetmask;
+
+ if (!hostname[0])
+ feepout("Must specify a host name of some sort!");
+ else if (netmask[0] && !verifyNetmask(netmask, &parsednetmask))
+ feepout("Invalid netmask value");
+ else if (nameserver[0] && !verifyIP(nameserver, NULL, NULL) &&
+ !verifyIP6(nameserver))
+ feepout("Invalid name server IP address specified");
+ else if (ipaddr[0] && !verifyIP(ipaddr, &parsednetmask, &parsedip))
+ feepout("Invalid IPv4 address");
+ else if (gateway[0] && strcmp(gateway, "NO") &&
+ !verifyGW(gateway, ipaddr[0] ? &parsedip : NULL,
+ netmask[0] ? &parsednetmask : NULL))
+ feepout("Invalid gateway IPv4 address specified");
+ else
+ return 1;
+ return 0;
+}
+
+static void
+dhcpGetInfo(Device *devp)
+{
+ /* If it fails, do it the old-fashioned way */
+ if (dhcpParseLeases("/var/db/dhclient.leases", hostname, domainname,
+ nameserver, ipaddr, gateway, netmask) == -1) {
+ FILE *ifp;
+ char *cp, cmd[256], data[2048];
+ int i, j;
+
+ /* Bah, now we have to kludge getting the information from ifconfig */
+ snprintf(cmd, sizeof cmd, "ifconfig %s", devp->name);
+ ifp = popen(cmd, "r");
+ if (ifp) {
+ j = fread(data, 1, sizeof(data), ifp);
+ fclose(ifp);
+ if (j < 0) /* paranoia */
+ j = 0;
+ data[j] = '\0';
+ if (isDebug())
+ msgDebug("DHCP configured interface returns %s\n", data);
+ /* XXX This is gross as it assumes a certain ordering to
+ ifconfig's output! XXX */
+ if ((cp = strstr(data, "inet ")) != NULL) {
+ i = 0;
+ cp += 5; /* move over keyword */
+ while (*cp != ' ')
+ ipaddr[i++] = *(cp++);
+ ipaddr[i] = '\0';
+ if (!strncmp(++cp, "netmask", 7)) {
+ i = 0;
+ cp += 8;
+ while (*cp != ' ')
+ netmask[i++] = *(cp++);
+ netmask[i] = '\0';
+ }
+ }
+ }
+ }
+
+ /* If we didn't get a name server value, hunt for it in resolv.conf */
+ if (!nameserver[0] && file_readable("/etc/resolv.conf"))
+ configEnvironmentResolv("/etc/resolv.conf");
+ if (hostname[0])
+ variable_set2(VAR_HOSTNAME, hostname, 0);
+}
+
+static void
+rtsolGetInfo(Device *devp)
+{
+ FILE *ifp;
+ char *cp, cmd[256], data[2048];
+ int i;
+
+ snprintf(cmd, sizeof cmd, "ifconfig %s", devp->name);
+ if ((ifp = popen(cmd, "r")) == NULL)
+ return;
+ while (fgets(data, sizeof(data), ifp) != NULL) {
+ if (isDebug())
+ msgDebug("RTSOL configured interface returns %s", data);
+ if ((cp = strstr(data, "inet6 ")) != NULL) {
+ cp += 6; /* move over keyword */
+ if (strncmp(cp, "fe80:", 5)) {
+ i = 0;
+ while (*cp != ' ')
+ ipv6addr[i++] = *(cp++);
+ ipv6addr[i] = '\0';
+ }
+ }
+ }
+ fclose(ifp);
+}
+
+/* This is it - how to get TCP setup values */
+int
+tcpOpenDialog(Device *devp)
+{
+ WINDOW *ds_win, *save = NULL;
+ ComposeObj *obj = NULL;
+ int n = 0, filled = 0, cancel = FALSE;
+ int max, ret = DITEM_SUCCESS;
+ int use_dhcp = FALSE;
+ int use_rtsol = FALSE;
+ char *tmp;
+ char title[80];
+
+ save = savescr();
+ /* Initialise vars from previous device values */
+ if (devp->private) {
+ DevInfo *di = (DevInfo *)devp->private;
+
+ SAFE_STRCPY(ipaddr, di->ipaddr);
+ SAFE_STRCPY(netmask, di->netmask);
+ SAFE_STRCPY(extras, di->extras);
+ use_dhcp = di->use_dhcp;
+ use_rtsol = di->use_rtsol;
+ }
+ else { /* See if there are any defaults */
+ char *cp;
+ char *old_interactive = NULL;
+
+ /*
+ * This is a hack so that the dialogs below are interactive in a
+ * script if we have requested interactive behavior.
+ */
+ if (variable_get(VAR_NONINTERACTIVE) &&
+ variable_get(VAR_NETINTERACTIVE)) {
+ old_interactive = strdup(VAR_NONINTERACTIVE);
+ variable_unset(VAR_NONINTERACTIVE);
+ }
+
+
+ /*
+ * Try a RTSOL scan if such behavior is desired.
+ * If the variable was configured and is YES, do it.
+ * If it was configured to anything else, treat it as NO.
+ * Otherwise, ask the question interactively.
+ */
+ if (!variable_cmp(VAR_TRY_RTSOL, "YES") ||
+ (variable_get(VAR_TRY_RTSOL)==0 && !msgNoYes("Do you want to try IPv6 configuration of the interface?"))) {
+ int i;
+ size_t len;
+
+ i = 0;
+ sysctlbyname("net.inet6.ip6.forwarding", NULL, 0, &i, sizeof(i));
+ i = 1;
+ sysctlbyname("net.inet6.ip6.accept_rtadv", NULL, 0, &i, sizeof(i));
+ vsystem("ifconfig %s up", devp->name);
+ len = sizeof(i);
+ sysctlbyname("net.inet6.ip6.dad_count", &i, &len, NULL, 0);
+ sleep(i + 1);
+ Mkdir("/var/run");
+ msgNotify("Scanning for RA servers...");
+ if (0 == vsystem("rtsol %s", devp->name)) {
+ len = sizeof(i);
+ sysctlbyname("net.inet6.ip6.dad_count", &i, &len, NULL, 0);
+ sleep(i + 1);
+ rtsolGetInfo(devp);
+ use_rtsol = TRUE;
+ } else
+ use_rtsol = FALSE;
+ }
+
+
+ /*
+ * First try a DHCP scan if such behavior is desired.
+ * If the variable was configured and is YES, do it.
+ * If it was configured to anything else, treat it as NO.
+ * Otherwise, ask the question interactively.
+ */
+ if (!variable_cmp(VAR_TRY_DHCP, "YES") ||
+ (variable_get(VAR_TRY_DHCP)==0 && !msgNoYes("Do you want to try DHCP configuration of the interface?"))) {
+ Mkdir("/var/db");
+ Mkdir("/var/run");
+ Mkdir("/tmp");
+ msgNotify("Scanning for DHCP servers...");
+ if (0 == vsystem("dhclient -1 %s", devp->name)) {
+ dhcpGetInfo(devp);
+ use_dhcp = TRUE;
+ }
+ else
+ use_dhcp = FALSE;
+ }
+
+ /* Restore old VAR_NONINTERACTIVE if needed. */
+ if (old_interactive != NULL) {
+ variable_set2(VAR_NONINTERACTIVE, old_interactive, 0);
+ free(old_interactive);
+ }
+
+ /* Special hack so it doesn't show up oddly in the tcpip setup menu */
+ if (!strcmp(gateway, "NO"))
+ gateway[0] = '\0';
+
+ /* Get old IP address from variable space, if available */
+ if (!ipaddr[0]) {
+ if ((cp = variable_get(VAR_IPADDR)) != NULL)
+ SAFE_STRCPY(ipaddr, cp);
+ else if ((cp = variable_get(string_concat3(devp->name, "_", VAR_IPADDR))) != NULL)
+ SAFE_STRCPY(ipaddr, cp);
+ }
+
+ /* Get old netmask from variable space, if available */
+ if (!netmask[0]) {
+ if ((cp = variable_get(VAR_NETMASK)) != NULL)
+ SAFE_STRCPY(netmask, cp);
+ else if ((cp = variable_get(string_concat3(devp->name, "_", VAR_NETMASK))) != NULL)
+ SAFE_STRCPY(netmask, cp);
+ }
+
+ /* Get old extras string from variable space, if available */
+ if (!extras[0]) {
+ if ((cp = variable_get(VAR_EXTRAS)) != NULL)
+ SAFE_STRCPY(extras, cp);
+ else if ((cp = variable_get(string_concat3(devp->name, "_", VAR_EXTRAS))) != NULL)
+ SAFE_STRCPY(extras, cp);
+ }
+ }
+
+ /* Look up values already recorded with the system, or blank the string variables ready to accept some new data */
+ if (!hostname[0]) {
+ tmp = variable_get(VAR_HOSTNAME);
+ if (tmp)
+ SAFE_STRCPY(hostname, tmp);
+ }
+ if (!domainname[0]) {
+ tmp = variable_get(VAR_DOMAINNAME);
+ if (tmp)
+ SAFE_STRCPY(domainname, tmp);
+ }
+ if (!gateway[0]) {
+ tmp = variable_get(VAR_GATEWAY);
+ if (tmp && strcmp(tmp, "NO"))
+ SAFE_STRCPY(gateway, tmp);
+ }
+ if (!nameserver[0]) {
+ tmp = variable_get(VAR_NAMESERVER);
+ if (tmp)
+ SAFE_STRCPY(nameserver, tmp);
+ }
+
+ /* If non-interactive, jump straight over the dialog crap and into config section */
+ if (variable_get(VAR_NONINTERACTIVE) &&
+ !variable_get(VAR_NETINTERACTIVE)) {
+ if (!hostname[0])
+ msgConfirm("WARNING: hostname variable not set and is a non-optional\n"
+ "parameter. Please add this to your installation script\n"
+ "or set the netInteractive variable (see sysinstall man page)");
+ else
+ goto netconfig;
+ }
+
+ /* Now do all the screen I/O */
+ dialog_clear_norefresh();
+
+ /* Modify the help line for PLIP config */
+ if (!strncmp(devp->name, "lp", 2))
+ layout[LAYOUT_EXTRAS].help =
+ "For PLIP configuration, you must enter the peer's IP address here.";
+
+ /* We need a curses window */
+ tmp = " Network Configuration ";
+ if (ipv6addr[0])
+ tmp = string_concat(tmp, "(IPv6 ready) ");
+ if (!(ds_win = openLayoutDialog(TCP_HELPFILE, tmp,
+ TCP_DIALOG_X, TCP_DIALOG_Y, TCP_DIALOG_WIDTH, TCP_DIALOG_HEIGHT))) {
+ beep();
+ msgConfirm("Cannot open TCP/IP dialog window!!");
+ restorescr(save);
+ return DITEM_FAILURE;
+ }
+
+ /* Draw interface configuration box */
+ draw_box(ds_win, TCP_DIALOG_Y + 9, TCP_DIALOG_X + 8, TCP_DIALOG_HEIGHT - 13, TCP_DIALOG_WIDTH - 17,
+ dialog_attr, border_attr);
+ wattrset(ds_win, dialog_attr);
+ sprintf(title, " Configuration for Interface %s ", devp->name);
+ mvwaddstr(ds_win, TCP_DIALOG_Y + 9, TCP_DIALOG_X + 14, title);
+
+ /* Some more initialisation before we go into the main input loop */
+ obj = initLayoutDialog(ds_win, layout, TCP_DIALOG_X, TCP_DIALOG_Y, &max);
+
+reenter:
+ cancelbutton = okbutton = 0;
+ while (layoutDialogLoop(ds_win, layout, &obj, &n, max, &cancelbutton, &cancel)) {
+ /* Prevent this from being irritating if user really means NO */
+ if (filled < 3) {
+ /* Insert a default value for the netmask, 0xffffff00 is
+ * the most appropriate one (entire class C, or subnetted
+ * class A/B network).
+ */
+ if (!netmask[0]) {
+ strcpy(netmask, "255.255.255.0");
+ RefreshStringObj(layout[LAYOUT_NETMASK].obj);
+ ++filled;
+ }
+ if (!index(hostname, '.') && domainname[0]) {
+ strcat(hostname, ".");
+ strcat(hostname, domainname);
+ RefreshStringObj(layout[LAYOUT_HOSTNAME].obj);
+ ++filled;
+ }
+ else if (((tmp = index(hostname, '.')) != NULL) && !domainname[0]) {
+ SAFE_STRCPY(domainname, tmp + 1);
+ RefreshStringObj(layout[LAYOUT_DOMAINNAME].obj);
+ ++filled;
+ }
+ }
+ }
+ if (!cancel && !verifySettings())
+ goto reenter;
+
+ /* Clear this crap off the screen */
+ delwin(ds_win);
+ dialog_clear_norefresh();
+ use_helpfile(NULL);
+
+ /* We actually need to inform the rest of sysinstall about this
+ data now if the user hasn't selected cancel. Save the stuff
+ out to the environment via the variable_set() mechanism */
+
+netconfig:
+ if (!cancel) {
+ DevInfo *di;
+ char temp[512], ifn[255];
+ char *pccard;
+ int ipv4_enable = FALSE;
+
+ if (hostname[0]) {
+ variable_set2(VAR_HOSTNAME, hostname, 1);
+ sethostname(hostname, strlen(hostname));
+ }
+ if (domainname[0])
+ variable_set2(VAR_DOMAINNAME, domainname, 0);
+ if (gateway[0])
+ variable_set2(VAR_GATEWAY, gateway, use_dhcp ? 0 : 1);
+ if (nameserver[0])
+ variable_set2(VAR_NAMESERVER, nameserver, 0);
+ if (ipaddr[0])
+ variable_set2(VAR_IPADDR, ipaddr, 0);
+ if (ipv6addr[0])
+ variable_set2(VAR_IPV6ADDR, ipv6addr, 0);
+
+ if (!devp->private)
+ devp->private = (DevInfo *)safe_malloc(sizeof(DevInfo));
+ di = devp->private;
+ SAFE_STRCPY(di->ipaddr, ipaddr);
+ SAFE_STRCPY(di->netmask, netmask);
+ SAFE_STRCPY(di->extras, extras);
+ di->use_dhcp = use_dhcp;
+ di->use_rtsol = use_rtsol;
+
+ if (use_dhcp || ipaddr[0])
+ ipv4_enable = TRUE;
+ if (ipv4_enable) {
+ sprintf(ifn, "%s%s", VAR_IFCONFIG, devp->name);
+ if (use_dhcp)
+ sprintf(temp, "DHCP");
+ else
+ sprintf(temp, "inet %s %s netmask %s",
+ ipaddr, extras, netmask);
+ variable_set2(ifn, temp, 1);
+ }
+#ifdef PCCARD_ARCH
+ pccard = variable_get("_pccard_install");
+ if (pccard && strcmp(pccard, "YES") == 0 && ipv4_enable) {
+ variable_set2("pccard_ifconfig", temp, 1);
+ }
+#endif
+ if (use_rtsol)
+ variable_set2(VAR_IPV6_ENABLE, "YES", 1);
+ if (!use_dhcp)
+ configResolv(NULL); /* XXX this will do it on the MFS copy XXX */
+ ret = DITEM_SUCCESS;
+ }
+ else
+ ret = DITEM_FAILURE;
+ restorescr(save);
+ return ret;
+}
+
+static Device *NetDev;
+
+static int
+netHook(dialogMenuItem *self)
+{
+ Device **devs;
+
+ devs = deviceFindDescr(self->prompt, self->title, DEVICE_TYPE_NETWORK);
+ if (devs) {
+ if (DITEM_STATUS(tcpOpenDialog(devs[0])) != DITEM_FAILURE)
+ NetDev = devs[0];
+ else
+ NetDev = NULL;
+ }
+ return devs ? DITEM_LEAVE_MENU : DITEM_FAILURE;
+}
+
+/* Get a network device */
+Device *
+tcpDeviceSelect(void)
+{
+ DMenu *menu;
+ Device **devs, *rval;
+ int cnt;
+
+ devs = deviceFind(variable_get(VAR_NETWORK_DEVICE), DEVICE_TYPE_NETWORK);
+ cnt = deviceCount(devs);
+ rval = NULL;
+
+ if (!cnt) {
+ msgConfirm("No network devices available!");
+ return NULL;
+ }
+ else if ((!RunningAsInit) && (variable_check("NETWORK_CONFIGURED=NO") != TRUE)) {
+ if (!msgYesNo("Running multi-user, assume that the network is already configured?"))
+ return devs[0];
+ }
+ if (cnt == 1) {
+ if (DITEM_STATUS(tcpOpenDialog(devs[0]) == DITEM_SUCCESS))
+ rval = devs[0];
+ }
+ else if (variable_get(VAR_NONINTERACTIVE) && variable_get(VAR_NETWORK_DEVICE)) {
+ devs = deviceFind(variable_get(VAR_NETWORK_DEVICE), DEVICE_TYPE_NETWORK);
+ cnt = deviceCount(devs);
+ if (cnt) {
+ if (DITEM_STATUS(tcpOpenDialog(devs[0]) == DITEM_SUCCESS))
+ rval = devs[0];
+ }
+ }
+ else {
+ int status;
+
+ menu = deviceCreateMenu(&MenuNetworkDevice, DEVICE_TYPE_NETWORK, netHook, NULL);
+ if (!menu)
+ msgFatal("Unable to create network device menu! Argh!");
+ status = dmenuOpenSimple(menu, FALSE);
+ free(menu);
+ if (status)
+ rval = NetDev;
+ }
+ return rval;
+}
+
+/* Do it from a menu that doesn't care about status */
+int
+tcpMenuSelect(dialogMenuItem *self)
+{
+ Device *tmp;
+ WINDOW *save;
+
+ variable_set("NETWORK_CONFIGURED=NO",0);
+ tmp = tcpDeviceSelect();
+ variable_unset("NETWORK_CONFIGURED");
+ save = savescr();
+ if (tmp && tmp->private && !((DevInfo *)tmp->private)->use_dhcp && !msgYesNo("Would you like to bring the %s interface up right now?", tmp->name))
+ if (!DEVICE_INIT(tmp))
+ msgConfirm("Initialization of %s device failed.", tmp->name);
+ restorescr(save);
+ return DITEM_SUCCESS;
+}
diff --git a/usr.sbin/sysinstall/termcap.c b/usr.sbin/sysinstall/termcap.c
new file mode 100644
index 0000000..1d8e047
--- /dev/null
+++ b/usr.sbin/sysinstall/termcap.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 1994, Paul Richards.
+ *
+ * All rights reserved.
+ *
+ * This software may be used, modified, copied, distributed, and sold, in both
+ * source and binary form provided that the above copyright and these terms
+ * are retained, verbatim, as the first lines of this file. Under no
+ * circumstances is the author responsible for the proper functioning of this
+ * software, nor does the author assume any responsibility for damages
+ * incurred with its use.
+ *
+ * $FreeBSD$
+ */
+
+#include "sysinstall.h"
+#include <stdarg.h>
+#include <fcntl.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/consio.h>
+
+#define VTY_STATUS_LINE 24
+#define TTY_STATUS_LINE 23
+
+static void
+prompt_term(char **termp, char **termcapp)
+{
+ char str[80];
+ static struct {
+ const char *term, *termcap;
+ } lookup[] = { { "ansi", termcap_ansi },
+ { "vt100", termcap_vt100 },
+ { "cons25", termcap_cons25 },
+ { "cons25-m", termcap_cons25_m },
+ { "xterm", termcap_xterm },
+ { "cons25w", termcap_cons25w } }; /* must be last */
+
+ if (RunningAsInit) {
+ while (1) {
+ int i;
+
+ printf("\nThese are the predefined terminal types available to\n");
+ printf("sysinstall when running stand-alone. Please choose the\n");
+ printf("closest match for your particular terminal.\n\n");
+ printf("1 ...................... Standard ANSI terminal.\n");
+ printf("2 ...................... VT100 or compatible terminal.\n");
+ printf("3 ...................... FreeBSD system console (color).\n");
+ printf("4 ...................... FreeBSD system console (monochrome).\n\n");
+ printf("5 ...................... xterm terminal emulator.\n\n");
+ printf("Your choice: (1-5) ");
+ fflush(stdout);
+ fgets(str, 80, stdin);
+ i = str[0] - '0';
+ if (i > 0 && i < 6) {
+ *termp = (char *)lookup[i - 1].term;
+ *termcapp = (char *)lookup[i - 1].termcap;
+ break;
+ }
+ else
+ printf("\007Invalid choice, please try again.\n\n");
+ }
+ }
+ else {
+ printf("\nPlease set your TERM variable before running this program.\n");
+ printf("Defaulting to an ANSI compatible terminal - please press RETURN\n");
+ fgets(str, 80, stdin); /* Just to make it interactive */
+ *termp = (char *)"ansi";
+ *termcapp = (char *)termcap_ansi;
+ }
+}
+
+int
+set_termcap(void)
+{
+ char *term;
+ int stat;
+ struct ttysize ts;
+
+ term = getenv("TERM");
+ stat = ioctl(STDERR_FILENO, GIO_COLOR, &ColorDisplay);
+
+ if (!RunningAsInit) {
+ if (isDebug())
+ DebugFD = open("sysinstall.debug", O_WRONLY|O_CREAT|O_TRUNC, 0644);
+ else
+ DebugFD = -1;
+ if (DebugFD < 0)
+ DebugFD = open("/dev/null", O_RDWR, 0);
+ }
+
+ if (!OnVTY || (stat < 0)) {
+ if (!term) {
+ char *term, *termcap;
+
+ prompt_term(&term, &termcap);
+ if (setenv("TERM", term, 1) < 0)
+ return -1;
+ if (setenv("TERMCAP", termcap, 1) < 0)
+ return -1;
+ }
+ if (DebugFD < 0)
+ DebugFD = open("/dev/null", O_RDWR, 0);
+ }
+ else {
+ int i, on;
+
+ if (getpid() == 1) {
+ DebugFD = open("/dev/ttyv1", O_WRONLY);
+ if (DebugFD != -1) {
+ on = 1;
+ i = ioctl(DebugFD, TIOCCONS, (char *)&on);
+ msgDebug("ioctl(%d, TIOCCONS, NULL) = %d (%s)\n",
+ DebugFD, i, !i ? "success" : strerror(errno));
+ }
+ }
+
+#ifdef PC98
+ if (!term) {
+ if (setenv("TERM", "cons25w", 1) < 0)
+ return -1;
+ if (setenv("TERMCAP", termcap_cons25w, 1) < 0)
+ return -1;
+ }
+#else
+ if (ColorDisplay) {
+ if (!term) {
+ if (setenv("TERM", "cons25", 1) < 0)
+ return -1;
+ if (setenv("TERMCAP", termcap_cons25, 1) < 0)
+ return -1;
+ }
+ }
+ else {
+ if (!term) {
+ if (setenv("TERM", "cons25-m", 1) < 0)
+ return -1;
+ if (setenv("TERMCAP", termcap_cons25_m, 1) < 0)
+ return -1;
+ }
+ }
+#endif
+ }
+ if (ioctl(0, TIOCGSIZE, &ts) == -1) {
+ msgDebug("Unable to get terminal size - errno %d\n", errno);
+ ts.ts_lines = 0;
+ }
+ StatusLine = ts.ts_lines ? ts.ts_lines - 1: (OnVTY ? VTY_STATUS_LINE : TTY_STATUS_LINE);
+ return 0;
+}
diff --git a/usr.sbin/sysinstall/ttys.c b/usr.sbin/sysinstall/ttys.c
new file mode 100644
index 0000000..0de954c
--- /dev/null
+++ b/usr.sbin/sysinstall/ttys.c
@@ -0,0 +1,162 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 2001
+ * Andrey A. Chernov. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ANDREY A. CHERNOV ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL ANDREY A. CHERNOV OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <sys/stat.h>
+#include <ctype.h>
+#include <ttyent.h>
+
+#define _X_EXTENSION ".XXXXXX"
+
+void
+configTtys(void)
+{
+ size_t len;
+ int t, tlen, changed;
+ FILE *fp, *np;
+ char sq, *line, *p, *q, *cp, *tptr;
+ char templ[sizeof(_PATH_TTYS) + sizeof(_X_EXTENSION) - 1];
+ struct ttyent *tnam;
+
+ if ((cp = variable_get(VAR_CONSTERM)) == NULL ||
+ strcmp(cp, "NO") == 0)
+ return;
+ if (!file_readable(_PATH_TTYS)) {
+ msgConfirm("%s not exist or not readable", _PATH_TTYS);
+ return;
+ }
+ if ((fp = fopen(_PATH_TTYS, "r")) == NULL) {
+ msgConfirm("Can't open %s for read: %s", _PATH_TTYS,
+ strerror(errno));
+ return;
+ }
+ strcpy(templ, _PATH_TTYS _X_EXTENSION);
+ if ((t = mkstemp(templ)) < 0) {
+ msgConfirm("Can't create %s: %s", templ, strerror(errno));
+ (void)fclose(fp);
+ return;
+ }
+ if (fchmod(t, 0644)) {
+ msgConfirm("Can't fchmod %s: %s", templ, strerror(errno));
+ (void)fclose(fp);
+ return;
+ }
+ if ((np = fdopen(t, "w")) == NULL) {
+ msgConfirm("Can't fdopen %s: %s", templ, strerror(errno));
+ (void)close(t);
+ (void)fclose(fp);
+ (void)unlink(templ);
+ return;
+ }
+ changed = 0;
+ while ((line = fgetln(fp, &len)) != NULL) {
+ p = line;
+ while (p < (line + len) && isspace((unsigned char)*p))
+ ++p;
+ if (strncmp(p, "ttyv", 4) != 0) {
+ dump:
+ if (fwrite(line, len, 1, np) != 1) {
+ wrerr:
+ msgConfirm("%s: write error: %s", templ, strerror(errno));
+ (void)fclose(fp);
+ (void)fclose(np);
+ (void)unlink(templ);
+ return;
+ }
+ } else {
+ q = p;
+ while(q < (line + len) && !isspace((unsigned char)*q))
+ ++q;
+ if (!isspace((unsigned char)*q))
+ goto dump;
+ sq = *q;
+ *q = '\0';
+ tnam = getttynam(p);
+ *q = sq;
+ if (tnam == NULL || tnam->ty_type == NULL ||
+ strcmp(tnam->ty_type, cp) == 0 ||
+ strncmp(tnam->ty_type, "cons", 4) != 0 ||
+ !isdigit((unsigned char)tnam->ty_type[4])
+ )
+ goto dump;
+ tlen = strlen(tnam->ty_type);
+ tptr = NULL;
+ p = ++q;
+ while(p < (line + len)) {
+ if (strncmp(p, tnam->ty_type, tlen) == 0) {
+ tptr = p;
+ break;
+ }
+ ++p;
+ }
+ if (tptr == NULL)
+ goto dump;
+ changed = 1;
+ if (fwrite(line, tptr - line, 1, np) != 1 ||
+ fputs(cp, np) ||
+ fwrite(tptr + tlen,
+ len - (tptr + tlen - line), 1, np) != 1)
+ goto wrerr;
+ }
+ }
+ if (!feof(fp)) {
+ msgConfirm("%s: read error: %s", _PATH_TTYS, strerror(errno));
+ (void)fclose(fp);
+ (void)fclose(np);
+ (void)unlink(templ);
+ return;
+ }
+ (void)fclose(fp);
+ if (fclose(np)) {
+ if (changed)
+ msgConfirm("%s: close error: %s", templ, strerror(errno));
+ else
+ variable_set2(VAR_CONSTERM, "NO", 0);
+ (void)unlink(templ);
+ return;
+ }
+ if (!changed) {
+ (void)unlink(templ);
+ variable_set2(VAR_CONSTERM, "NO", 0);
+ return;
+ }
+ if (rename(templ, _PATH_TTYS)) {
+ msgConfirm("Can't rename %s to %s: %s", templ, _PATH_TTYS,
+ strerror(errno));
+ return;
+ }
+ variable_set2(VAR_CONSTERM, "NO", 0);
+}
diff --git a/usr.sbin/sysinstall/ufs.c b/usr.sbin/sysinstall/ufs.c
new file mode 100644
index 0000000..c9515ae
--- /dev/null
+++ b/usr.sbin/sysinstall/ufs.c
@@ -0,0 +1,49 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated to essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ * Copyright (c) 1995
+ * Gary J Palmer. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <sys/fcntl.h>
+#include <sys/param.h>
+
+/* No init or shutdown routines necessary - all done in mediaSetUFS() */
+
+FILE *
+mediaGetUFS(Device *dev, char *file, Boolean probe)
+{
+ return mediaGenericGet((char *)dev->private, file);
+}
diff --git a/usr.sbin/sysinstall/usb.c b/usr.sbin/sysinstall/usb.c
new file mode 100644
index 0000000..4eedbd5
--- /dev/null
+++ b/usr.sbin/sysinstall/usb.c
@@ -0,0 +1,44 @@
+/*
+ * USB support for sysinstall
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 2000 John Baldwin <jhb@FreeBSD.org>. All rights reserved.
+ *
+ * This software may be used, modified, copied, and distributed, in
+ * both source and binary form provided that the above copyright and
+ * these terms are retained. Under no circumstances is the author
+ * responsible for the proper functioning of this software, nor does
+ * the author assume any responsibility for damages incurred with its
+ * use.
+ */
+
+#include "sysinstall.h"
+#include <sys/fcntl.h>
+#include <sys/time.h>
+
+void
+usbInitialize(void)
+{
+ int fd;
+ WINDOW *w;
+
+ if (!RunningAsInit && !Fake) {
+ /* It's not my job... */
+ return;
+ }
+
+ if ((fd = open("/dev/usb", O_RDONLY)) < 0) {
+ msgDebug("Can't open USB controller.\n");
+ return;
+ }
+ close(fd);
+
+ w = savescr();
+ msgNotify("Initializing USB controller....");
+
+ variable_set2("usbd_enable", "YES", 1);
+
+ vsystem("/stand/usbd");
+ restorescr(w);
+}
diff --git a/usr.sbin/sysinstall/user.c b/usr.sbin/sysinstall/user.c
new file mode 100644
index 0000000..3e4a65c
--- /dev/null
+++ b/usr.sbin/sysinstall/user.c
@@ -0,0 +1,741 @@
+/*
+ * $FreeBSD$
+ *
+ * Copyright (c) 1996
+ * Jörg Wunsch. All rights reserved.
+ *
+ * The basic structure has been taken from tcpip.c, which is:
+ *
+ * Copyright (c) 1995
+ * Gary J Palmer. All rights reserved.
+ * Jordan K Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <utmp.h>
+#include <ctype.h>
+#include <sys/param.h>
+#include <sysexits.h>
+
+/* The help file for the user mgmt screen */
+#define USER_HELPFILE "usermgmt"
+
+/* XXX should they be moved out to sysinstall.h? */
+#define GNAME_FIELD_LEN 32
+#define GID_FIELD_LEN 10
+#define GMEMB_FIELD_LEN 64
+
+#define UID_FIELD_LEN 10
+#define UGROUP_FIELD_LEN GNAME_FIELD_LEN
+#define GECOS_FIELD_LEN 64
+#define UMEMB_FIELD_LEN GMEMB_FIELD_LEN
+#define HOMEDIR_FIELD_LEN 48
+#define SHELL_FIELD_LEN 48
+#define PASSWD_FIELD_LEN 32
+
+/* These are nasty, but they make the layout structure a lot easier ... */
+
+static char gname[GNAME_FIELD_LEN],
+ gid[GID_FIELD_LEN],
+ gmemb[GMEMB_FIELD_LEN],
+ uname[UT_NAMESIZE + 1],
+ passwd[PASSWD_FIELD_LEN],
+ uid[UID_FIELD_LEN],
+ ugroup[UGROUP_FIELD_LEN],
+ gecos[GECOS_FIELD_LEN],
+ umemb[UMEMB_FIELD_LEN],
+ homedir[HOMEDIR_FIELD_LEN],
+ shell[SHELL_FIELD_LEN];
+#define CLEAR(v) memset(v, 0, sizeof v)
+
+static int okbutton, cancelbutton;
+
+
+/* What the screen size is meant to be */
+#define USER_DIALOG_Y 0
+#define USER_DIALOG_X 8
+#define USER_DIALOG_WIDTH COLS - 16
+#define USER_DIALOG_HEIGHT LINES - 2
+
+/* The group configuration menu. */
+static Layout groupLayout[] = {
+#define LAYOUT_GNAME 0
+ { 4, 10, 20, GNAME_FIELD_LEN - 1,
+ "Group name:", "The alphanumeric name of the new group (mandatory)",
+ gname, STRINGOBJ, NULL },
+#define LAYOUT_GID 1
+ { 4, 38, 10, GID_FIELD_LEN - 1,
+ "GID:", "The numerical ID for this group (leave blank for automatic choice)",
+ gid, STRINGOBJ, NULL },
+#define LAYOUT_GMEMB 2
+ { 11, 10, 40, GMEMB_FIELD_LEN - 1,
+ "Group members:", "Who belongs to this group (i.e., gets access rights for it)",
+ gmemb, STRINGOBJ, NULL },
+#define LAYOUT_OKBUTTON 3
+ { 18, 15, 0, 0,
+ "OK", "Select this if you are happy with these settings",
+ &okbutton, BUTTONOBJ, NULL },
+#define LAYOUT_CANCELBUTTON 4
+ { 18, 35, 0, 0,
+ "CANCEL", "Select this if you wish to cancel this screen",
+ &cancelbutton, BUTTONOBJ, NULL },
+ { 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("%s", tmp) == 0) {
+ feepout("This group name is already in use.");
+ return 0;
+ }
+ if (strlen(gid) > 0) {
+ lgid = strtol(gid, &cp, 10);
+ if (lgid < 0 || lgid >= 65536 || (*cp != '\0' && !isspace(*cp))) {
+ feepout("The GID must be a number between 1 and 65535.");
+ return 0;
+ }
+ }
+ if (strlen(gmemb) > 0) {
+ if (strpbrk(gmemb, " \t") != NULL) {
+ feepout("The group member list must not contain any whitespace;\n"
+ "use commas to separate the names.");
+ return 0;
+ }
+#ifndef notyet /* XXX */
+ feepout("Sorry, the group member list feature\n"
+ "is currently not yet implemented.");
+ return 0;
+#endif
+ }
+
+ return 1;
+}
+
+/*
+ * Ask pw(8) to fill in the blanks for us.
+ * Works solely on the global variables.
+ */
+
+static void
+completeGroup(void)
+{
+ int pfd[2], i;
+ char tmp[256], *cp;
+ ssize_t l;
+ size_t amnt;
+ pid_t pid;
+ char *vec[4] =
+ {
+ "pw", "group", "next", 0
+ };
+
+ pipe (pfd);
+ if ((pid = fork()) == 0)
+ {
+ /* The kiddy. */
+ dup2(pfd[1], 1);
+ dup2(pfd[1], 2);
+ for (i = getdtablesize(); i > 2; i--)
+ close(i);
+
+ execv("/usr/sbin/pw", vec);
+ msgDebug("Cannot execv() /usr/sbin/pw.\n");
+ _exit(99);
+ }
+ else
+ {
+ /* The oldie. */
+ close(pfd[1]);
+ amnt = sizeof tmp;
+ i = 0;
+ while((l = read(pfd[0], &tmp[i], amnt)) > 0)
+ {
+ amnt -= l;
+ i += l;
+ if (amnt == 0)
+ {
+ close(pfd[0]);
+ break;
+ }
+ }
+ close(pfd[0]);
+ tmp[i] = '\0';
+ waitpid(pid, &i, 0);
+ if (WIFSIGNALED(i) || WEXITSTATUS(i) != 0)
+ /* ignore by now */
+ return;
+ if ((cp = strchr(tmp, '\n')) != NULL)
+ *cp = '\0';
+ strncpy(gid, tmp, sizeof gid);
+ }
+}
+
+static void
+addGroup(WINDOW *ds_win)
+{
+ char tmp[256];
+ int pfd[2], i;
+ ssize_t l;
+ size_t amnt;
+ pid_t pid;
+ char *vec[8] =
+ {
+ "pw", "group", "add", "-n", 0, "-g", 0, 0
+ };
+#define VEC_GNAME 4
+#define VEC_GID 6
+
+ msgNotify("Adding group \"%s\"...", gname);
+
+ pipe (pfd);
+ if ((pid = fork()) == 0)
+ {
+ /* The kiddy. */
+ dup2(pfd[1], 1);
+ dup2(pfd[1], 2);
+ for (i = getdtablesize(); i > 2; i--)
+ close(i);
+
+ vec[VEC_GNAME] = gname;
+
+ if (strlen(gid) > 0)
+ vec[VEC_GID] = gid;
+ else
+ vec[VEC_GID - 1] = 0;
+
+ execv("/usr/sbin/pw", vec);
+ msgDebug("Cannot execv() /usr/sbin/pw.\n");
+ _exit(99);
+ }
+ else
+ {
+ /* The oldie. */
+ close(pfd[1]);
+ amnt = sizeof tmp;
+ i = 0;
+ while((l = read(pfd[0], &tmp[i], amnt)) > 0)
+ {
+ amnt -= l;
+ i += l;
+ if (amnt == 0)
+ {
+ close(pfd[0]);
+ break;
+ }
+ }
+ close(pfd[0]);
+ tmp[i] = '\0';
+ waitpid(pid, &i, 0);
+ if (WIFSIGNALED(i))
+ msgDebug("pw(8) exited with signal %d.\n", WTERMSIG(i));
+ else if(WEXITSTATUS(i))
+ {
+ i = 0;
+ if(strncmp(tmp, "pw: ", 4) == 0)
+ i = 4;
+ tmp[sizeof tmp - 1] = '\0'; /* sanity */
+ msgConfirm("The `pw' command exited with an error status.\n"
+ "Its error message was:\n\n%s",
+ &tmp[i]);
+ }
+ }
+#undef VEC_GNAME
+#undef VEC_GID
+}
+
+int
+userAddGroup(dialogMenuItem *self)
+{
+ WINDOW *ds_win, *save;
+ ComposeObj *obj = NULL;
+ int n = 0, cancel = FALSE, ret;
+ int max, firsttime = TRUE;
+
+ if (RunningAsInit && !strstr(variable_get(SYSTEM_STATE), "install")) {
+ msgConfirm("This option may only be used after the system is installed, sorry!");
+ return DITEM_FAILURE;
+ }
+
+ save = savescr();
+ dialog_clear_norefresh();
+ /* We need a curses window */
+ if (!(ds_win = openLayoutDialog(USER_HELPFILE, " User and Group Management ",
+ USER_DIALOG_X, USER_DIALOG_Y, USER_DIALOG_WIDTH, USER_DIALOG_HEIGHT))) {
+ beep();
+ msgConfirm("Cannot open addgroup dialog window!!");
+ return(DITEM_FAILURE);
+ }
+
+ /* Draw a group entry box */
+ draw_box(ds_win, USER_DIALOG_Y + 2, USER_DIALOG_X + 8, USER_DIALOG_HEIGHT - 8,
+ USER_DIALOG_WIDTH - 17, dialog_attr, border_attr);
+ wattrset(ds_win, dialog_attr);
+ mvwaddstr(ds_win, USER_DIALOG_Y + 2, USER_DIALOG_X + 22, " Add a new group ");
+
+ CLEAR(gname);
+ CLEAR(gid);
+ CLEAR(gmemb);
+
+ /* Some more initialisation before we go into the main input loop */
+ obj = initLayoutDialog(ds_win, groupLayout, USER_DIALOG_X, USER_DIALOG_Y, &max);
+
+reenter:
+ cancelbutton = okbutton = 0;
+ if (firsttime) {
+ /* fill in the blanks, well, just the GID */
+ completeGroup();
+ RefreshStringObj(groupLayout[LAYOUT_GID].obj);
+ firsttime = FALSE;
+ }
+
+ while (layoutDialogLoop(ds_win, groupLayout, &obj, &n, max, &cancelbutton, &cancel));
+
+ if (!cancel && !verifyGroupSettings())
+ goto reenter;
+
+ /* Clear this crap off the screen */
+ delwin(ds_win);
+ dialog_clear_norefresh();
+ use_helpfile(NULL);
+
+ if (!cancel) {
+ addGroup(ds_win);
+ ret = DITEM_SUCCESS;
+ }
+ else
+ ret = DITEM_FAILURE;
+ restorescr(save);
+ return ret;
+}
+
+/* Check for the settings on the screen. */
+
+static int
+verifyUserSettings(WINDOW *ds_win)
+{
+ char tmp[256], *cp;
+ long luid;
+ WINDOW *save;
+ int rv;
+
+ if (strlen(uname) == 0) {
+ feepout("The user name field must not be empty!");
+ return 0;
+ }
+ snprintf(tmp, 256, "pw user show -q -n %s > /dev/null", uname);
+ if (vsystem("%s", tmp) == 0) {
+ feepout("This user name is already in use.");
+ return 0;
+ }
+ if (strlen(uid) > 0) {
+ luid = strtol(uid, &cp, 10);
+ if (luid < 0 || luid >= 65536 || (*cp != '\0' && !isspace(*cp))) {
+ feepout("The UID must be a number between 1 and 65535.");
+ return 0;
+ }
+ }
+ if ((homedir[0]!=0) && (homedir[0]!='/')) {
+ feepout("The pathname for home directories must begin with a '/'.");
+ return 0;
+ }
+ if (strlen(shell) > 0) {
+ while((cp = getusershell()) != NULL)
+ if (strcmp(cp, shell) == 0)
+ break;
+ endusershell();
+ if (cp == NULL) {
+ save = savescr();
+ rv = msgYesNo("Warning:\n\n"
+ "The requested shell \"%s\" is not\n"
+ "a valid user shell.\n\n"
+ "Use it anyway?\n", shell);
+ restorescr(save);
+ wrefresh(ds_win);
+ if (rv != DITEM_SUCCESS)
+ return 0;
+ }
+
+ }
+
+ if (strlen(umemb) > 0) {
+ if (strpbrk(umemb, " \t") != NULL) {
+ feepout("The member groups list must not contain any whitespace;\n"
+ "use commas to separate the names.");
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/*
+ * Ask pw(8) to fill in the blanks for us.
+ * Works solely on the global variables.
+ */
+
+static void
+completeUser(void)
+{
+ int pfd[2], i;
+ char tmp[256], *cp, *cp2;
+ ssize_t l;
+ size_t amnt;
+ pid_t pid;
+ char *vec[7] =
+ {
+ "pw", "user", "add", "-N", "-n", 0, 0
+ };
+#define VEC_UNAME 5
+
+ pipe (pfd);
+ if ((pid = fork()) == 0)
+ {
+ /* The kiddy. */
+ dup2(pfd[1], 1);
+ dup2(pfd[1], 2);
+ for (i = getdtablesize(); i > 2; i--)
+ close(i);
+
+ vec[VEC_UNAME] = uname;
+
+ execv("/usr/sbin/pw", vec);
+ msgDebug("Cannot execv() /usr/sbin/pw.\n");
+ _exit(99);
+ }
+ else
+ {
+ /* The oldie. */
+ close(pfd[1]);
+ amnt = sizeof tmp;
+ i = 0;
+ while((l = read(pfd[0], &tmp[i], amnt)) > 0)
+ {
+ amnt -= l;
+ i += l;
+ if (amnt == 0)
+ {
+ close(pfd[0]);
+ break;
+ }
+ }
+ close(pfd[0]);
+ tmp[i] = '\0';
+ waitpid(pid, &i, 0);
+ if (WIFSIGNALED(i) || WEXITSTATUS(i) != 0)
+ /* ignore by now */
+ return;
+ if ((cp = strchr(tmp, '\n')) != NULL)
+ *cp = '\0';
+ if ((cp = strchr(tmp, ':')) == NULL || (cp = strchr(++cp, ':')) == NULL)
+ return;
+ cp++;
+ if ((cp2 = strchr(cp, ':')) == NULL)
+ return;
+ *cp2++ = '\0';
+ strncpy(uid, cp, sizeof uid);
+ cp = cp2;
+ if ((cp2 = strchr(cp, ':')) == NULL)
+ return;
+ *cp2++ = '\0';
+#ifdef notyet /* XXX pw user add -g doesn't accept a numerical GID */
+ strncpy(ugroup, cp, sizeof ugroup);
+#endif
+ cp = cp2;
+ if ((cp2 = strchr(cp, ':')) == NULL || (cp2 = strchr(++cp2, ':')) == NULL ||
+ (cp = cp2 = strchr(++cp2, ':')) == NULL || (cp2 = strchr(++cp2, ':')) == NULL)
+ return;
+ *cp2++ = '\0';
+ cp++;
+ strncpy(gecos, cp, sizeof gecos);
+ cp = cp2;
+ if ((cp2 = strchr(cp, ':')) == NULL)
+ return;
+ *cp2++ = '\0';
+ if (*cp2)
+ strncpy(shell, cp2, sizeof shell);
+ }
+#undef VEC_UNAME
+}
+
+static void
+addUser(WINDOW *ds_win)
+{
+ char tmp[256], *msg;
+ int pfd[2], ipfd[2], i, j;
+ ssize_t l;
+ size_t amnt;
+ pid_t pid;
+ /*
+ * Maximal list:
+ * pw user add -m -n uname -g grp -u uid -c comment -d homedir -s shell -G grplist -h 0
+ */
+ char *vec[21] =
+ {
+ "pw", "user", "add", "-m", "-n", /* ... */
+ };
+#define VEC_UNAME 5
+
+ msgNotify("Adding user \"%s\"...", uname);
+
+ pipe (pfd);
+ pipe (ipfd);
+ if ((pid = fork()) == 0)
+ {
+ /* The kiddy. */
+ dup2(ipfd[0], 0);
+ dup2(pfd[1], 1);
+ dup2(pfd[1], 2);
+ for (i = getdtablesize(); i > 2; i--)
+ close(i);
+
+ vec[i = VEC_UNAME] = uname;
+ i++;
+#define ADDVEC(var, option) do { if (strlen(var) > 0) { vec[i++] = option; vec[i++] = var; } } while (0)
+ ADDVEC(ugroup, "-g");
+ ADDVEC(uid, "-u");
+ ADDVEC(gecos, "-c");
+ ADDVEC(homedir, "-d");
+ ADDVEC(shell, "-s");
+ ADDVEC(umemb, "-G");
+ if (passwd[0]) {
+ vec[i++] = "-h";
+ vec[i++] = "0";
+ }
+ vec[i] = 0;
+
+ execv("/usr/sbin/pw", vec);
+ msgDebug("Cannot execv() /usr/sbin/pw.\n");
+ _exit(99);
+ }
+ else
+ {
+ /* The oldie. */
+ close(pfd[1]);
+ close(ipfd[0]);
+
+ if (passwd[0])
+ write(ipfd[1], passwd, strlen(passwd));
+ close(ipfd[1]);
+ amnt = sizeof tmp;
+ i = 0;
+ while((l = read(pfd[0], &tmp[i], amnt)) > 0)
+ {
+ amnt -= l;
+ i += l;
+ if (amnt == 0)
+ {
+ close(pfd[0]);
+ break;
+ }
+ }
+ close(pfd[0]);
+ tmp[i] = '\0';
+ waitpid(pid, &i, 0);
+ if (WIFSIGNALED(i))
+ {
+ j = WTERMSIG(i);
+ msg = "The `pw' command exited with signal %d.\n";
+ goto sysfail;
+ }
+ else if((j = WEXITSTATUS(i)))
+ {
+ i = 0;
+ if(strncmp(tmp, "pw: ", 4) == 0)
+ i = 4;
+ tmp[sizeof tmp - 1] = '\0'; /* sanity */
+ if (j == EX_DATAERR || j == EX_NOUSER || j == EX_SOFTWARE)
+ msgConfirm("The `pw' command exited with an error status.\n"
+ "Its error message was:\n\n%s",
+ &tmp[i]);
+ else
+ {
+ msg = "The `pw' command exited with unexpected status %d.\n";
+ sysfail:
+ msgDebug(msg, j);
+ msgDebug("Command stdout and stderr was:\n\n%s", tmp);
+ msgConfirm(msg, j);
+ }
+ }
+ else if (!passwd[0])
+ msgConfirm("You will need to enter a password for this user\n"
+ "later, using the passwd(1) command from the shell.\n\n"
+ "The account for `%s' is currently still disabled.",
+ uname);
+ }
+#undef VEC_UNAME
+#undef ADDVEC
+}
+
+int
+userAddUser(dialogMenuItem *self)
+{
+ WINDOW *ds_win, *save;
+ ComposeObj *obj = NULL;
+ int n = 0, cancel = FALSE, ret;
+ int max, firsttime = TRUE, filled=0;
+
+ if (RunningAsInit && !strstr(variable_get(SYSTEM_STATE), "install")) {
+ msgConfirm("This option may only be used after the system is installed, sorry!");
+ return DITEM_FAILURE;
+ }
+
+ save = savescr();
+ dialog_clear_norefresh();
+
+ /* We need a curses window */
+ if (!(ds_win = openLayoutDialog(USER_HELPFILE, " User and Group Management ",
+ USER_DIALOG_X, USER_DIALOG_Y, USER_DIALOG_WIDTH, USER_DIALOG_HEIGHT))) {
+ beep();
+ msgConfirm("Cannot open adduser dialog window!!");
+ return(DITEM_FAILURE);
+ }
+
+ /* Draw a user entry box */
+ draw_box(ds_win, USER_DIALOG_Y + 1, USER_DIALOG_X + 3, USER_DIALOG_HEIGHT - 6,
+ USER_DIALOG_WIDTH - 6, dialog_attr, border_attr);
+ wattrset(ds_win, dialog_attr);
+ mvwaddstr(ds_win, USER_DIALOG_Y + 1, USER_DIALOG_X + 22, " Add a new user ");
+
+ CLEAR(uname);
+ CLEAR(uid);
+ CLEAR(ugroup);
+ CLEAR(gecos);
+ CLEAR(passwd);
+ CLEAR(umemb);
+ CLEAR(homedir);
+ CLEAR(shell);
+
+ /* Some more initialisation before we go into the main input loop */
+ obj = initLayoutDialog(ds_win, userLayout, USER_DIALOG_X, USER_DIALOG_Y, &max);
+
+reenter:
+ cancelbutton = okbutton = 0;
+ if (firsttime) {
+ /* fill in the blanks, well, just the GID */
+ completeUser();
+ RefreshStringObj(userLayout[LAYOUT_UID].obj);
+ RefreshStringObj(userLayout[LAYOUT_UGROUP].obj);
+ RefreshStringObj(userLayout[LAYOUT_GECOS].obj);
+ RefreshStringObj(userLayout[LAYOUT_UMEMB].obj);
+ RefreshStringObj(userLayout[LAYOUT_HOMEDIR].obj);
+ RefreshStringObj(userLayout[LAYOUT_SHELL].obj);
+ firsttime = FALSE;
+ }
+
+ while (layoutDialogLoop(ds_win, userLayout, &obj, &n, max, &cancelbutton, &cancel)) {
+ /* Prevent this from being irritating if user really means NO */
+ if (filled < 3) {
+ if ((uname[0]) && !homedir[0]) {
+ SAFE_STRCPY(homedir,"/home/");
+ strcat(homedir,uname);
+ RefreshStringObj(userLayout[LAYOUT_HOMEDIR].obj);
+ ++filled;
+ }
+ }
+ };
+
+ if (!cancel && !verifyUserSettings(ds_win))
+ goto reenter;
+
+ /* Clear this crap off the screen */
+ delwin(ds_win);
+ dialog_clear_norefresh();
+ use_helpfile(NULL);
+
+ if (!cancel) {
+ addUser(ds_win);
+ ret = DITEM_SUCCESS;
+ }
+ else
+ ret = DITEM_FAILURE;
+ restorescr(save);
+ return ret;
+}
+
diff --git a/usr.sbin/sysinstall/variable.c b/usr.sbin/sysinstall/variable.c
new file mode 100644
index 0000000..6f896ee
--- /dev/null
+++ b/usr.sbin/sysinstall/variable.c
@@ -0,0 +1,296 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ * Copyright (c) 2001
+ * Murray Stokely. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+
+/* Routines for dealing with variable lists */
+
+static void
+make_variable(char *var, char *value, int dirty)
+{
+ Variable *vp;
+
+ /* Trim leading and trailing whitespace */
+ var = string_skipwhite(string_prune(var));
+
+ if (!var || !*var)
+ return;
+
+
+ /* Now search to see if it's already in the list */
+ for (vp = VarHead; vp; vp = vp->next) {
+ if (!strcmp(vp->name, var)) {
+ if (vp->dirty && !dirty)
+ return;
+ setenv(var, value, 1);
+ free(vp->value);
+ vp->value = strdup(value);
+ if (dirty != -1)
+ vp->dirty = dirty;
+ return;
+ }
+ }
+
+ setenv(var, value, 1);
+ /* No? Create a new one */
+ vp = (Variable *)safe_malloc(sizeof(Variable));
+ vp->name = strdup(var);
+ vp->value = strdup(value);
+ if (dirty == -1)
+ dirty = 0;
+ vp->dirty = dirty;
+ vp->next = VarHead;
+ VarHead = vp;
+}
+
+void
+variable_set(char *var, int dirty)
+{
+ char tmp[1024], *cp;
+
+ if (!var)
+ msgFatal("NULL variable name & value passed.");
+ else if (!*var)
+ msgDebug("Warning: Zero length name & value passed to variable_set()\n");
+ SAFE_STRCPY(tmp, var);
+ if ((cp = index(tmp, '=')) == NULL)
+ msgFatal("Invalid variable format: %s", var);
+ *(cp++) = '\0';
+ make_variable(tmp, string_skipwhite(cp), dirty);
+}
+
+void
+variable_set2(char *var, char *value, int dirty)
+{
+ if (!var || !value)
+ msgFatal("Null name or value passed to set_variable2(%s) = %s!",
+ var ? var : "", value ? value : "");
+ else if (!*var || !*value)
+ msgDebug("Warning: Zero length name or value passed to variable_set2(%s) = %s\n",
+ var, value);
+ make_variable(var, value, dirty);
+}
+
+char *
+variable_get(char *var)
+{
+ return getenv(var);
+}
+
+int
+variable_cmp(char *var, char *value)
+{
+ char *val;
+
+ if ((val = variable_get(var)))
+ return strcmp(val, value);
+ return -1;
+}
+
+void
+variable_unset(char *var)
+{
+ Variable *vp;
+ char name[512], *cp;
+
+ if ((cp = index(var, '=')) != NULL)
+ sstrncpy(name, var, cp - var);
+ else
+ SAFE_STRCPY(name, var);
+ unsetenv(name);
+ /* Now search to see if it's in our list, if we have one.. */
+ if (!VarHead)
+ return;
+ else if (!VarHead->next && !strcmp(VarHead->name, name)) {
+ safe_free(VarHead->name);
+ safe_free(VarHead->value);
+ free(VarHead);
+ VarHead = NULL;
+ }
+ else {
+ for (vp = VarHead; vp; vp = vp->next) {
+ if (!strcmp(vp->name, name)) {
+ Variable *save = vp->next;
+
+ safe_free(vp->name);
+ safe_free(vp->value);
+ *vp = *save;
+ safe_free(save);
+ break;
+ }
+ }
+ }
+}
+
+/* Prompt user for the name of a variable */
+char *
+variable_get_value(char *var, char *prompt, int dirty)
+{
+ char *cp;
+
+ cp = variable_get(var);
+ if (cp && variable_get(VAR_NONINTERACTIVE))
+ return cp;
+ else if ((cp = msgGetInput(cp, "%s", prompt)) != NULL)
+ variable_set2(var, cp, dirty);
+ else
+ cp = NULL;
+ return cp;
+}
+
+/* Check if value passed in data (in the form "variable=value") is
+ 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 = variable_get(tmp);
+ if (cp2) {
+ if (!*cp)
+ return TRUE;
+ else
+ return !strcmp(cp, cp2);
+ }
+ else
+ return FALSE;
+ }
+ else
+ return variable_get(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;
+}
+
+/* Free all of the variables, useful to really start over as when the
+ user selects "restart" from the interrupt menu. */
+void
+free_variables(void)
+{
+ Variable *vp;
+
+ /* Free the variables from our list, if we have one.. */
+ if (!VarHead)
+ return;
+ else if (!VarHead->next) {
+ unsetenv(VarHead->name);
+ safe_free(VarHead->name);
+ safe_free(VarHead->value);
+ free(VarHead);
+ VarHead = NULL;
+ }
+ else {
+ for (vp = VarHead; vp; ) {
+ Variable *save = vp;
+ unsetenv(vp->name);
+ safe_free(vp->name);
+ safe_free(vp->value);
+ vp = vp->next;
+ safe_free(save);
+ }
+ VarHead = NULL;
+ }
+}
+
+/*
+ * Persistent variables. The variables modified by these functions
+ * are not cleared between invocations of sysinstall. This is useful
+ * to allow the user to completely restart sysinstall, without having
+ * it load all of the modules again from the installation media which
+ * are still in memory.
+ */
+
+void
+pvariable_set(char *var)
+{
+ char tmp[1024];
+
+ if (!var)
+ msgFatal("NULL variable name & value passed.");
+ else if (!*var)
+ msgDebug("Warning: Zero length name & value passed to variable_set()\n");
+ /* Add a trivial namespace to whatever name the caller chooses. */
+ SAFE_STRCPY(tmp, "SYSINSTALL_PVAR");
+ if (index(var, '=') == NULL)
+ msgFatal("Invalid variable format: %s", var);
+ strlcat(tmp, var, 1024);
+ putenv(tmp);
+}
+
+char *
+pvariable_get(char *var)
+{
+ char tmp[1024];
+
+ SAFE_STRCPY(tmp, "SYSINSTALL_PVAR");
+ strlcat(tmp, var, 1024);
+ return getenv(tmp);
+}
diff --git a/usr.sbin/sysinstall/wizard.c b/usr.sbin/sysinstall/wizard.c
new file mode 100644
index 0000000..6dec01d
--- /dev/null
+++ b/usr.sbin/sysinstall/wizard.c
@@ -0,0 +1,202 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ *
+ * $FreeBSD$
+ *
+ */
+
+#include "sysinstall.h"
+#include <fcntl.h>
+#include <err.h>
+
+int
+scan_block(int fd, daddr_t block)
+{
+ u_char foo[512];
+
+ if (-1 == lseek(fd,block * 512,SEEK_SET))
+ err(1,"lseek");
+ if (512 != read(fd,foo, 512))
+ return 1;
+ return 0;
+}
+
+void
+Scan_Disk(Disk *d)
+{
+ char device[64];
+ u_long l;
+ int i,j,fd;
+
+ strcpy(device,"/dev/");
+ strcat(device,d->name);
+
+ fd = open(device,O_RDWR);
+ if (fd < 0) {
+ msgWarn("open(%s) failed", device);
+ return;
+ }
+ for(i=-1,l=0;;l++) {
+ j = scan_block(fd,l);
+ if (j != i) {
+ if (i == -1) {
+ printf("%c: %lu.",j ? 'B' : 'G', l);
+ fflush(stdout);
+ } else if (i == 0) {
+ printf(".%lu\nB: %lu.",l-1,l);
+ fflush(stdout);
+ } else {
+ printf(".%lu\nG: %lu.",l-1,l);
+ fflush(stdout);
+ }
+ i = j;
+ }
+ }
+ close(fd);
+}
+
+void
+slice_wizard(Disk *d)
+{
+ Disk *db;
+ char myprompt[BUFSIZ];
+ char input[BUFSIZ];
+ char *p,*q=0;
+ char **cp,*cmds[200];
+ int ncmd,i;
+
+ systemSuspendDialog();
+ sprintf(myprompt,"%s> ", d->name);
+ while(1) {
+ printf("--==##==--\n");
+ Debug_Disk(d);
+ p = CheckRules(d);
+ if (p) {
+ printf("%s",p);
+ free(p);
+ }
+ printf("%s", myprompt);
+ fflush(stdout);
+ q = p = fgets(input,sizeof(input),stdin);
+ if(!p)
+ break;
+ for(cp = cmds; (*cp = strsep(&p, " \t\n")) != NULL;)
+ if (**cp != '\0')
+ cp++;
+ ncmd = cp - cmds;
+ if(!ncmd)
+ continue;
+ if (!strcasecmp(*cmds,"quit")) { break; }
+ if (!strcasecmp(*cmds,"exit")) { break; }
+ if (!strcasecmp(*cmds,"q")) { break; }
+ if (!strcasecmp(*cmds,"x")) { break; }
+ if (!strcasecmp(*cmds,"delete") && ncmd == 2) {
+ printf("delete = %d\n",
+ Delete_Chunk(d,
+ (struct chunk *)strtol(cmds[1],0,0)));
+ continue;
+ }
+ if (!strcasecmp(*cmds,"allfreebsd")) {
+ All_FreeBSD(d, 0);
+ continue;
+ }
+ if (!strcasecmp(*cmds,"dedicate")) {
+ All_FreeBSD(d, 1);
+ continue;
+ }
+ if (!strcasecmp(*cmds,"bios") && ncmd == 4) {
+ Set_Bios_Geom(d,
+ strtol(cmds[1],0,0),
+ strtol(cmds[2],0,0),
+ strtol(cmds[3],0,0));
+ continue;
+ }
+ if (!strcasecmp(*cmds,"list")) {
+ cp = Disk_Names();
+ printf("Disks:");
+ for(i=0;cp[i];i++) {
+ printf(" %s",cp[i]);
+ free(cp[i]);
+ }
+ free(cp);
+ continue;
+ }
+#ifdef PC98
+ if (!strcasecmp(*cmds,"create") && ncmd == 7) {
+ printf("Create=%d\n",
+ Create_Chunk(d,
+ strtol(cmds[1],0,0),
+ strtol(cmds[2],0,0),
+ strtol(cmds[3],0,0),
+ strtol(cmds[4],0,0),
+ strtol(cmds[5],0,0),
+ cmds[6]));
+ continue;
+ }
+#else
+ if (!strcasecmp(*cmds,"create") && ncmd == 6) {
+ printf("Create=%d\n",
+ Create_Chunk(d,
+ strtol(cmds[1],0,0),
+ strtol(cmds[2],0,0),
+ strtol(cmds[3],0,0),
+ strtol(cmds[4],0,0),
+ strtol(cmds[5],0,0)));
+ continue;
+ }
+#endif
+ if (!strcasecmp(*cmds,"read")) {
+ db = d;
+ if (ncmd > 1)
+ d = Open_Disk(cmds[1]);
+ else
+ d = Open_Disk(d->name);
+ if (d)
+ Free_Disk(db);
+ else
+ d = db;
+ continue;
+ }
+ if (!strcasecmp(*cmds,"scan")) {
+ Scan_Disk(d);
+ continue;
+ }
+ if (!strcasecmp(*cmds,"write")) {
+ printf("Write=%d\n",
+ Fake ? 0 : Write_Disk(d));
+ 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");
+#ifdef PC98
+ printf("create offset size enum subtype flags name\n");
+#else
+ printf("create offset size enum subtype flags\n");
+#endif
+ printf("subtype(part): swap=1, ffs=7\t\t");
+ printf("delete pointer\n");
+ printf("list\t\t");
+ printf("quit\n");
+ printf("read [disk]\t\t");
+ printf("scan\n");
+ printf("write\t\t");
+ printf("ENUM:\n\t");
+ for(i=0;chunk_n[i];i++)
+ printf("%d = %s%s",i,chunk_n[i],i == 4 ? "\n\t" : " ");
+ printf("\n");
+
+ }
+ systemResumeDialog();
+}
diff --git a/usr.sbin/syslogd/Makefile b/usr.sbin/syslogd/Makefile
new file mode 100644
index 0000000..3551042
--- /dev/null
+++ b/usr.sbin/syslogd/Makefile
@@ -0,0 +1,13 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../usr.bin/wall
+
+PROG= syslogd
+MAN= syslog.conf.5 syslogd.8
+SRCS= syslogd.c ttymsg.c
+
+CFLAGS+=-DINET6 -I${.CURDIR}/../../usr.bin/wall
+WARNS?= 1
+
+.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..acf6ebc
--- /dev/null
+++ b/usr.sbin/syslogd/syslog.conf.5
@@ -0,0 +1,435 @@
+.\" Copyright (c) 1990, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)syslog.conf.5 8.1 (Berkeley) 6/9/93
+.\" $FreeBSD$
+.\"
+.Dd June 9, 1993
+.Dt SYSLOG.CONF 5
+.Os
+.Sh NAME
+.Nm syslog.conf
+.Nd
+.Xr syslogd 8
+configuration file
+.Sh DESCRIPTION
+The
+.Nm
+file is the configuration file for the
+.Xr syslogd 8
+program.
+It consists of
+blocks of lines separated by
+.Em program
+and
+.Em hostname
+specifications,
+with each line containing two fields: the
+.Em selector
+field which specifies the types of messages and priorities to which the
+line applies, and an
+.Em action
+field which specifies the action to be taken if a message
+.Xr syslogd 8
+receives matches the selection criteria.
+The
+.Em selector
+field is separated from the
+.Em action
+field by one or more tab characters or spaces.
+.Pp
+Note that if you use spaces as separators, your
+.Nm
+might be incompatible with other Unices or Unix-like systems.
+This functionality was added for the ease of configuration
+(e.g. it is possible to cut-and-paste into
+.Nm ) ,
+and to avoid possible mistakes.
+This change however preserves
+backwards compatibility with the old style of the
+.Nm
+(i.e. tab characters only).
+.Pp
+The
+.Em selectors
+are encoded as a
+.Em facility ,
+a period
+.Pq Dq \&. ,
+an optional set of comparison flags
+.Pq 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, console, cron, daemon, ftp, kern,
+lpr, mail, mark, news, ntp, security, syslog, user, uucp and local0 through
+local7.
+These keywords (with the exception of mark) correspond to 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,
+.Dq >= ) ,
+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 3
+library routine.
+.Pp
+Each block of lines is separated from the previous block by a
+.Em program
+or
+.Em hostname
+specification.
+A block will only log messages corresponding to the most recent
+.Em program
+and
+.Em hostname
+specifications given.
+Thus, a block which selects
+.Ql ppp
+as the
+.Em program ,
+directly followed by a block that selects messages from the
+.Em hostname
+.Ql dialhost ,
+then the second block will only log messages
+from the
+.Xr ppp 8
+program on dialhost.
+.Pp
+A
+.Em program
+specification is a line beginning with
+.Ql #!prog
+or
+.Ql !prog
+(the former is for compatibility with the previous syslogd, if one is sharing
+.Nm
+files, for example)
+and the following blocks will be associated with calls to
+.Xr syslog 3
+from that specific program.
+A
+.Em program
+specification for
+.Ql foo
+will also match any message logged by the kernel with the prefix
+.Ql "foo: " .
+A
+.Em hostname
+specification of the form
+.Ql #+hostname
+or
+.Ql +hostname
+and the following blocks will be applied to messages
+received from the specified hostname.
+Alternatively, a
+.Em hostname
+specification
+.Ql #-hostname
+or
+.Ql -hostname
+causes the following blocks to be applied to messages
+from any host but the one specified.
+If the hostname is given as
+.Ql @ ,
+the local hostname will be used.
+A
+.Em program
+or
+.Em hostname
+specification may be reset by giving the program or hostname as
+.Ql * .
+.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 8
+program on the named host.
+.It
+A comma separated list of users.
+Selected messages are written to those users
+if they are logged in.
+.It
+An asterisk.
+Selected messages are written to all logged-in users.
+.It
+A vertical bar
+.Pq Dq \&| ,
+followed by a command to pipe the selected
+messages to. The command is passed to
+.Xr sh 1
+for evaluation, so usual shell metacharacters or input/output
+redirection can occur. (Note however that redirecting
+.Xr stdio 3
+buffered output from the invoked command can cause additional delays,
+or even lost output data in case a logging subprocess exited with a
+signal.) The command itself runs with
+.Em stdout
+and
+.Em stderr
+redirected to
+.Pa /dev/null .
+Upon receipt of a
+.Dv SIGHUP ,
+.Xr syslogd 8
+will close the pipe to the process. If the process didn't exit
+voluntarily, it will be sent a
+.Dv SIGTERM
+signal after a grace period of up to 60 seconds.
+.Pp
+The command will only be started once data arrives that should be piped
+to it. If it exited later, it will be restarted as necessary. So if it
+is desired that the subprocess should get exactly one line of input only
+(which can be very resource-consuming if there are a lot of messages
+flowing quickly), this can be achieved by exiting after just one line of
+input. If necessary, a script wrapper can be written to this effect.
+.Pp
+Unless the command is a full pipeline, it's probably useful to
+start the command with
+.Em exec
+so that the invoking shell process does not wait for the command to
+complete. Warning: the process is started under the UID invoking
+.Xr syslogd 8 ,
+normally the superuser.
+.El
+.Pp
+Blank lines and lines whose first non-blank character is a hash
+.Pq Dq #
+character are ignored.
+.Sh EXAMPLES
+A configuration file might appear as follows:
+.Bd -literal
+# Log all kernel messages, authentication messages of
+# level notice or higher and anything of level err or
+# higher to the console.
+# Don't log private authentication messages!
+*.err;kern.*;auth.notice;authpriv.none /dev/console
+
+# Log anything (except mail) of level info or higher.
+# Don't log private authentication messages!
+*.info;mail.none;authpriv.none /var/log/messages
+
+# Log daemon messages at debug level only
+daemon.=debug /var/log/daemon.debug
+
+# The authpriv file has restricted access.
+authpriv.* /var/log/secure
+
+# Log all the mail messages in one place.
+mail.* /var/log/maillog
+
+# Everybody gets emergency messages, plus log them on another
+# machine.
+*.emerg *
+*.emerg @arpa.berkeley.edu
+
+# Root and Eric get alert and higher messages.
+*.alert root,eric
+
+# Save mail and news errors of level err and higher in a
+# special file.
+uucp,news.crit /var/log/spoolerr
+
+# Pipe all authentication messages to a filter.
+auth.* |exec /usr/local/sbin/authfilter
+
+# Save ftpd transactions along with mail and news
+!ftpd
+*.* /var/log/spoolerr
+
+# Log all security messages to a separate file.
+security.* /var/log/security
+
+# Log all writes to /dev/console to a separate file.
+console.* /var/log/console.log
+.Ed
+.Sh IMPLEMENTATION NOTES
+The
+.Dq kern
+facility is usually reserved for messages
+generated by the local kernel.
+Other messages logged with facility
+.Dq kern
+are usually translated to facility
+.Dq user .
+This translation can be disabled,
+see
+.Xr syslogd 8
+for details.
+.Sh FILES
+.Bl -tag -width /etc/syslog.conf -compact
+.It Pa /etc/syslog.conf
+.Xr syslogd 8
+configuration file
+.El
+.Sh BUGS
+The effects of multiple
+.Em selectors
+are sometimes not intuitive.
+For example
+.Dq mail.crit,*.err
+will select
+.Dq mail
+facility messages at the level of
+.Dq err
+or higher, not at the level of
+.Dq crit
+or higher.
+.Pp
+In networked environments, note that not all operating systems
+implement the same set of facilities. The facilities
+authpriv, cron, ftp, and ntp that are known to this implementation
+might be absent on the target system. Even worse, DEC UNIX uses
+facility number 10 (which is authpriv in this implementation) to
+log events for their AdvFS filesystem.
+.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..57d8b41
--- /dev/null
+++ b/usr.sbin/syslogd/syslogd.8
@@ -0,0 +1,331 @@
+.\" Copyright (c) 1983, 1986, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)syslogd.8 8.1 (Berkeley) 6/6/93
+.\" $FreeBSD$
+.\"
+.Dd November 24, 2001
+.Dt SYSLOGD 8
+.Os
+.Sh NAME
+.Nm syslogd
+.Nd log systems messages
+.Sh SYNOPSIS
+.Nm
+.Op Fl 46Adknosuv
+.Op Fl a Ar allowed_peer
+.Op Fl b Ar bind_address
+.Op Fl f Ar config_file
+.Op Fl m Ar mark_interval
+.Op Fl p Ar log_socket
+.Op Fl P Ar pid_file
+.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.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl 4
+Forces
+.Nm
+to use IPv4 addresses only.
+.It Fl 6
+Forces
+.Nm
+to use IPv6 addresses only.
+.It Fl A
+Ordinarily,
+.Nm
+tries to send the message to only one address
+even if the host has more than one A or AAAA record.
+If this option is specified,
+.Nm
+tries to send the message to all addresses.
+.It Fl a Ar allowed_peer
+Allow
+.Ar allowed_peer
+to log to this
+.Nm
+using UDP datagrams. Multiple
+.Fl a
+options may be specified.
+.Pp
+.Ar Allowed_peer
+can be any of the following:
+.Bl -tag -width "ipaddr/masklen[:service]XX"
+.It Xo
+.Sm off
+.Ar ipaddr
+.No / Ar masklen
+.Op : Ar service
+.Sm on
+.Xc
+Accept datagrams from
+.Ar ipaddr
+(in the usual dotted quad notation) with
+.Ar masklen
+bits being taken into account when doing the address comparison.
+.Ar ipaddr
+can be also IPv6 address by enclosing the address with
+.Ql \&[
+and
+.Ql \&] .
+If specified,
+.Ar service
+is the name or number of an UDP service (see
+.Xr services 5 )
+the source packet must belong to. A
+.Ar service
+of
+.Ql \&*
+allows packets being sent from any UDP port. The default
+.Ar service
+is
+.Ql syslog .
+If
+.Ar ipaddr
+is IPv4 address, a missing
+.Ar masklen
+will be substituted by the historic class A or class B netmasks if
+.Ar ipaddr
+belongs into the address range of class A or B, respectively, or
+by 24 otherwise. If
+.Ar ipaddr
+is IPv6 address, a missing
+.Ar masklen
+will be substituted by 128.
+.It Xo
+.Sm off
+.Ar domainname Op : Ar service
+.Sm on
+.Xc
+Accept datagrams where the reverse address lookup yields
+.Ar domainname
+for the sender address. The meaning of
+.Ar service
+is as explained above.
+.It Xo
+.Sm off
+.No * Ar domainname Op : Ar service
+.Sm on
+.Xc
+Same as before, except that any source host whose name
+.Em ends
+in
+.Ar domainname
+will get permission.
+.El
+.Pp
+The
+.Fl a
+options are ignored if the
+.Fl s
+option is also specified.
+.It Fl b Ar bind_address
+Specify one specific IP address or hostname to bind to.
+If a hostname is specified,
+the IPv4 or IPv6 address which corresponds to it is used.
+.It Fl c
+Disable the compression of repeated instances of the same line
+into a single line of the form
+.Dq Li "last message repeated N times"
+when the output is a pipe to another program.
+If specified twice, disable this compression in all cases.
+.It Fl d
+Put
+.Nm
+into debugging mode. This is probably only of use to developers working on
+.Nm .
+.It Fl f
+Specify the pathname of an alternate configuration file;
+the default is
+.Pa /etc/syslog.conf .
+.It Fl k
+Disable the translation of
+messages received with facility
+.Dq kern
+to facility
+.Dq user .
+Usually the
+.Dq kern
+facility is reserved for messages read directly from
+.Pa /dev/klog .
+.It Fl m
+Select the number of minutes between
+.Dq mark
+messages; the default is 20 minutes.
+.It Fl n
+Disable dns query for every request.
+.It Fl o
+Prefix kernel messages with the full kernel boot file as determined by
+.Xr getbootfile 3 .
+Without this, the kernel message prefix is always
+.Dq Li kernel: .
+.It Fl p
+Specify the pathname of an alternate log socket to be used instead;
+the default is
+.Pa /var/run/log .
+.It Fl P
+Specify an alternative file in which to store the process ID.
+The default is
+.Pa /var/run/syslog.pid .
+.It Fl l
+Specify a location where
+.Nm
+should place an additional log socket.
+Up to 19 additional logging sockets can be specified.
+The primary use for this is to place additional log sockets in
+.Pa /var/run/log
+of various chroot filespaces.
+.It Fl s
+Operate in secure mode. Do not log messages from remote machines. If
+specified twice, no network socket will be opened at all, which also
+disables logging to remote machines.
+.It Fl u
+Unique priority logging. Only log messages at the specified priority.
+Without this option, messages at the stated priority or higher are logged.
+This option changes the default comparison from
+.Dq =>
+to
+.Dq = .
+.It Fl v
+Verbose logging. If specified once, the numeric facility and priority are
+logged with each locally-written message. If specified more than once,
+the names of the facility and priority are logged with each locally-written
+message.
+.El
+.Pp
+The
+.Nm
+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 its process ID file,
+by default
+.Pa /var/run/syslog.pid ,
+and stores its process
+ID there.
+This can be used to kill or reconfigure
+.Nm .
+.Pp
+The message sent to
+.Nm
+should consist of a single line.
+The message can contain a priority code, which should be a preceding
+decimal number in angle braces, for example,
+.Sq Aq 5 .
+This priority code should map into the priorities defined in the
+include file
+.Aq Pa sys/syslog.h .
+.Pp
+For security reasons,
+.Nm
+will not append to log files that do not exist;
+therefore, they must be created manually before running
+.Nm .
+.Sh FILES
+.Bl -tag -width /var/run/syslog.pid -compact
+.It Pa /etc/syslog.conf
+configuration file
+.It Pa /var/run/syslog.pid
+default process ID file
+.It Pa /var/run/log
+name of the
+.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 comparison. Since the allowed
+peer list is being walked linearly, peer groups where frequent messages
+are being anticipated from should be put early into the
+.Fl a
+list.
+.Pp
+The log socket was moved from
+.Pa /dev
+to ease the use of a read-only root 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..dc7e8b4
--- /dev/null
+++ b/usr.sbin/syslogd/syslogd.c
@@ -0,0 +1,2405 @@
+/*
+ * Copyright (c) 1983, 1988, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1983, 1988, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)syslogd.c 8.3 (Berkeley) 4/4/94";
+#endif
+#endif /* not lint */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * syslogd -- log system messages
+ *
+ * This program implements a system log. It takes a series of lines.
+ * Each line may have a priority, signified as "<n>" as
+ * the first characters of the line. If this is
+ * not present, a default priority is used.
+ *
+ * To kill syslogd, send a signal 15 (terminate). A signal 1 (hup) will
+ * cause it to reread its configuration file.
+ *
+ * Defined Constants:
+ *
+ * MAXLINE -- the 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 <netinet/in.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <utmp.h>
+
+#include "pathnames.h"
+#include "ttymsg.h"
+
+#define SYSLOG_NAMES
+#include <sys/syslog.h>
+
+#ifdef NI_WITHSCOPEID
+static const int withscopeid = NI_WITHSCOPEID;
+#else
+static const int withscopeid;
+#endif
+
+const char *ConfFile = _PATH_LOGCONF;
+const char *PidFile = _PATH_LOGPID;
+const char ctty[] = _PATH_CONSOLE;
+
+#define dprintf if (Debug) printf
+
+#define MAXUNAMES 20 /* maximum number of user names */
+
+#define MAXFUNIX 20
+
+int nfunix = 1;
+const char *funixn[MAXFUNIX] = { _PATH_LOG };
+int funix[MAXFUNIX];
+
+/*
+ * Flags to logmsg().
+ */
+
+#define IGN_CONS 0x001 /* don't print on console */
+#define SYNC_FILE 0x002 /* do fsync on file after printing */
+#define ADDDATE 0x004 /* add a date to the message */
+#define MARK 0x008 /* this message is a mark */
+#define ISKERNEL 0x010 /* kernel generated message */
+
+/*
+ * This structure represents the files that will have log
+ * copies printed.
+ */
+
+struct filed {
+ struct filed *f_next; /* next in linked list */
+ short f_type; /* entry type, see below */
+ short f_file; /* file descriptor */
+ time_t f_time; /* time this was last written */
+ char *f_host; /* host from which to recd. */
+ u_char f_pmask[LOG_NFACILITIES+1]; /* priority mask */
+ u_char f_pcmp[LOG_NFACILITIES+1]; /* compare priority */
+#define PRI_LT 0x1
+#define PRI_EQ 0x2
+#define PRI_GT 0x4
+ char *f_program; /* program this applies to */
+ union {
+ char f_uname[MAXUNAMES][UT_NAMESIZE+1];
+ struct {
+ char f_hname[MAXHOSTNAMELEN];
+ struct addrinfo *f_addr;
+
+ } f_forw; /* forwarding address */
+ char f_fname[MAXPATHLEN];
+ struct {
+ char f_pname[MAXPATHLEN];
+ pid_t f_pid;
+ } f_pipe;
+ } f_un;
+ char f_prevline[MAXSVLINE]; /* last message logged */
+ char f_lasttime[16]; /* time of last occurrence */
+ char f_prevhost[MAXHOSTNAMELEN]; /* host from which recd. */
+ int f_prevpri; /* pri of f_prevline */
+ int f_prevlen; /* length of f_prevline */
+ int f_prevcount; /* repetition cnt of prevline */
+ u_int f_repeatcount; /* number of "repeated" msgs */
+};
+
+/*
+ * Queue of about-to-be dead processes we should watch out for.
+ */
+
+TAILQ_HEAD(stailhead, deadq_entry) deadq_head;
+struct stailhead *deadq_headp;
+
+struct deadq_entry {
+ pid_t dq_pid;
+ int dq_timeout;
+ TAILQ_ENTRY(deadq_entry) dq_entries;
+};
+
+/*
+ * The timeout to apply to processes waiting on the dead queue. Unit
+ * of measure is `mark intervals', i.e. 20 minutes by default.
+ * Processes on the dead queue will be terminated after that time.
+ */
+
+#define DQ_TIMO_INIT 2
+
+typedef struct deadq_entry *dq_t;
+
+
+/*
+ * Struct to hold records of network addresses that are allowed to log
+ * to us.
+ */
+struct allowedpeer {
+ int isnumeric;
+ u_short port;
+ union {
+ struct {
+ struct sockaddr_storage addr;
+ struct sockaddr_storage mask;
+ } numeric;
+ char *name;
+ } u;
+#define a_addr u.numeric.addr
+#define a_mask u.numeric.mask
+#define a_name u.name
+};
+
+
+/*
+ * Intervals at which we flush out "message repeated" messages,
+ * in seconds after previous message is logged. After each flush,
+ * we move to the next interval until we reach the largest.
+ */
+int repeatinterval[] = { 30, 120, 600 }; /* # of secs before flush */
+#define MAXREPEAT ((sizeof(repeatinterval) / sizeof(repeatinterval[0])) - 1)
+#define REPEATTIME(f) ((f)->f_time + repeatinterval[(f)->f_repeatcount])
+#define BACKOFF(f) { if (++(f)->f_repeatcount > MAXREPEAT) \
+ (f)->f_repeatcount = MAXREPEAT; \
+ }
+
+/* values for f_type */
+#define F_UNUSED 0 /* unused entry */
+#define F_FILE 1 /* regular file */
+#define F_TTY 2 /* terminal */
+#define F_CONSOLE 3 /* console terminal */
+#define F_FORW 4 /* remote machine */
+#define F_USERS 5 /* list of users */
+#define F_WALL 6 /* everyone logged on */
+#define F_PIPE 7 /* pipe to program */
+
+const char *TypeNames[8] = {
+ "UNUSED", "FILE", "TTY", "CONSOLE",
+ "FORW", "USERS", "WALL", "PIPE"
+};
+
+static struct filed *Files; /* Log files that we write to */
+static struct filed consfile; /* Console */
+
+static int Debug; /* debug flag */
+static int resolve = 1; /* resolve hostname */
+static char LocalHostName[MAXHOSTNAMELEN]; /* our hostname */
+static char *LocalDomain; /* our local domain name */
+static int *finet; /* Internet datagram socket */
+static int fklog = -1; /* /dev/klog */
+static int Initialized; /* set when we have initialized ourselves */
+static int MarkInterval = 20 * 60; /* interval between marks in seconds */
+static int MarkSeq; /* mark sequence number */
+static int SecureMode; /* when true, receive only unix domain socks */
+#ifdef INET6
+static int family = PF_UNSPEC; /* protocol family (IPv4, IPv6 or both) */
+#else
+static int family = PF_INET; /* protocol family (IPv4 only) */
+#endif
+static int send_to_all; /* send message to all IPv4/IPv6 addresses */
+static int use_bootfile; /* log entire bootfile for every kern msg */
+static int no_compress; /* don't compress messages (1=pipes, 2=all) */
+
+static char bootfile[MAXLINE+1]; /* booted kernel file */
+
+struct allowedpeer *AllowedPeers; /* List of allowed peers */
+static int NumAllowed; /* Number of entries in AllowedPeers */
+
+static int UniquePriority; /* Only log specified priority? */
+static int LogFacPri; /* Put facility and priority in log message: */
+ /* 0=no, 1=numeric, 2=names */
+static int KeepKernFac; /* Keep remotely logged kernel facility */
+
+volatile sig_atomic_t MarkSet, WantDie;
+
+static int allowaddr(char *);
+static void cfline(const char *, struct filed *,
+ const char *, const char *);
+static const char *cvthname(struct sockaddr *);
+static void deadq_enter(pid_t, const char *);
+static int deadq_remove(pid_t);
+static int decode(const char *, CODE *);
+static void die(int);
+static void dodie(int);
+static void domark(int);
+static void fprintlog(struct filed *, int, const char *);
+static int *socksetup(int, const char *);
+static void init(int);
+static void logerror(const char *);
+static void logmsg(int, const char *, const char *, int);
+static void log_deadchild(pid_t, int, const char *);
+static void markit(void);
+static void printline(const char *, char *);
+static void printsys(char *);
+static int p_open(const char *, pid_t *);
+static void readklog(void);
+static void reapchild(int);
+static void usage(void);
+static int validate(struct sockaddr *, const char *);
+static void unmapped(struct sockaddr *);
+static void wallmsg(struct filed *, struct iovec *);
+static int waitdaemon(int, int, int);
+static void timedout(int);
+
+int
+main(int argc, char *argv[])
+{
+ int ch, i, fdsrmax = 0, l;
+ struct sockaddr_un sunx, fromunix;
+ struct sockaddr_storage frominet;
+ fd_set *fdsr = NULL;
+ FILE *fp;
+ char line[MAXLINE + 1];
+ const char *bindhostname, *hname;
+ struct timeval tv, *tvp;
+ struct sigaction sact;
+ sigset_t mask;
+ pid_t ppid = 1;
+ socklen_t len;
+
+ bindhostname = NULL;
+ while ((ch = getopt(argc, argv, "46Aa:b:cdf:kl:m:nop:P:suv")) != -1)
+ switch (ch) {
+ case '4':
+ family = PF_INET;
+ break;
+#ifdef INET6
+ case '6':
+ family = PF_INET6;
+ break;
+#endif
+ case 'A':
+ send_to_all++;
+ break;
+ case 'a': /* allow specific network addresses only */
+ if (allowaddr(optarg) == -1)
+ usage();
+ break;
+ case 'b':
+ bindhostname = optarg;
+ break;
+ case 'c':
+ no_compress++;
+ break;
+ case 'd': /* debug */
+ Debug++;
+ break;
+ case 'f': /* configuration file */
+ ConfFile = optarg;
+ break;
+ case 'k': /* keep remote kern fac */
+ KeepKernFac = 1;
+ break;
+ case 'l':
+ if (nfunix < MAXFUNIX)
+ funixn[nfunix++] = optarg;
+ else
+ warnx("out of descriptors, ignoring %s",
+ optarg);
+ break;
+ case 'm': /* mark interval */
+ MarkInterval = atoi(optarg) * 60;
+ break;
+ case 'n':
+ resolve = 0;
+ break;
+ case 'o':
+ use_bootfile = 1;
+ break;
+ case 'p': /* path */
+ funixn[0] = optarg;
+ break;
+ case 'P': /* path for alt. PID */
+ PidFile = optarg;
+ break;
+ case 's': /* no network mode */
+ SecureMode++;
+ break;
+ case 'u': /* only log specified priority */
+ UniquePriority++;
+ break;
+ case 'v': /* log facility and priority */
+ LogFacPri++;
+ break;
+ 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)strlcpy(consfile.f_un.f_fname, ctty + sizeof _PATH_DEV - 1,
+ sizeof(consfile.f_un.f_fname));
+ (void)strlcpy(bootfile, getbootfile(), sizeof(bootfile));
+ (void)signal(SIGTERM, dodie);
+ (void)signal(SIGINT, Debug ? dodie : SIG_IGN);
+ (void)signal(SIGQUIT, Debug ? dodie : SIG_IGN);
+ /*
+ * We don't want the SIGCHLD and SIGHUP handlers to interfere
+ * with each other; they are likely candidates for being called
+ * simultaneously (SIGHUP closes pipe descriptor, process dies,
+ * SIGCHLD happens).
+ */
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGHUP);
+ sact.sa_handler = reapchild;
+ sact.sa_mask = mask;
+ sact.sa_flags = SA_RESTART;
+ (void)sigaction(SIGCHLD, &sact, NULL);
+ (void)signal(SIGALRM, domark);
+ (void)signal(SIGPIPE, SIG_IGN); /* We'll catch EPIPE instead. */
+ (void)alarm(TIMERINTVL);
+
+ TAILQ_INIT(&deadq_head);
+
+#ifndef SUN_LEN
+#define SUN_LEN(unp) (strlen((unp)->sun_path) + 2)
+#endif
+ for (i = 0; i < nfunix; i++) {
+ (void)unlink(funixn[i]);
+ memset(&sunx, 0, sizeof(sunx));
+ sunx.sun_family = AF_UNIX;
+ (void)strlcpy(sunx.sun_path, funixn[i], sizeof(sunx.sun_path));
+ funix[i] = socket(AF_UNIX, SOCK_DGRAM, 0);
+ if (funix[i] < 0 ||
+ bind(funix[i], (struct sockaddr *)&sunx,
+ SUN_LEN(&sunx)) < 0 ||
+ chmod(funixn[i], 0666) < 0) {
+ (void)snprintf(line, sizeof line,
+ "cannot create %s", funixn[i]);
+ logerror(line);
+ dprintf("cannot create %s (%d)\n", funixn[i], errno);
+ if (i == 0)
+ die(0);
+ }
+ }
+ if (SecureMode <= 1)
+ finet = socksetup(family, bindhostname);
+
+ if (finet) {
+ if (SecureMode) {
+ for (i = 0; i < *finet; i++) {
+ if (shutdown(finet[i+1], SHUT_RD) < 0) {
+ logerror("shutdown");
+ if (!Debug)
+ die(0);
+ }
+ }
+ } else {
+ dprintf("listening on inet and/or inet6 socket\n");
+ }
+ dprintf("sending on inet and/or inet6 socket\n");
+ }
+
+ if ((fklog = open(_PATH_KLOG, O_RDONLY, 0)) >= 0)
+ if (fcntl(fklog, F_SETFL, O_NONBLOCK) < 0)
+ fklog = -1;
+ if (fklog < 0)
+ dprintf("can't open %s (%d)\n", _PATH_KLOG, errno);
+
+ /* tuck my process id away */
+ fp = fopen(PidFile, "w");
+ if (fp != NULL) {
+ fprintf(fp, "%d\n", getpid());
+ (void)fclose(fp);
+ }
+
+ dprintf("off & running....\n");
+
+ init(0);
+ /* prevent SIGHUP and SIGCHLD handlers from running in parallel */
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGCHLD);
+ sact.sa_handler = init;
+ sact.sa_mask = mask;
+ sact.sa_flags = SA_RESTART;
+ (void)sigaction(SIGHUP, &sact, NULL);
+
+ tvp = &tv;
+ tv.tv_sec = tv.tv_usec = 0;
+
+ if (fklog != -1 && fklog > fdsrmax)
+ fdsrmax = fklog;
+ if (finet && !SecureMode) {
+ for (i = 0; i < *finet; i++) {
+ if (finet[i+1] != -1 && finet[i+1] > fdsrmax)
+ fdsrmax = finet[i+1];
+ }
+ }
+ for (i = 0; i < nfunix; i++) {
+ if (funix[i] != -1 && funix[i] > fdsrmax)
+ fdsrmax = funix[i];
+ }
+
+ fdsr = (fd_set *)calloc(howmany(fdsrmax+1, NFDBITS),
+ sizeof(fd_mask));
+ if (fdsr == NULL)
+ errx(1, "calloc fd_set");
+
+ for (;;) {
+ if (MarkSet)
+ markit();
+ if (WantDie)
+ die(WantDie);
+
+ bzero(fdsr, howmany(fdsrmax+1, NFDBITS) *
+ sizeof(fd_mask));
+
+ if (fklog != -1)
+ FD_SET(fklog, fdsr);
+ if (finet && !SecureMode) {
+ for (i = 0; i < *finet; i++) {
+ if (finet[i+1] != -1)
+ FD_SET(finet[i+1], fdsr);
+ }
+ }
+ for (i = 0; i < nfunix; i++) {
+ if (funix[i] != -1)
+ FD_SET(funix[i], fdsr);
+ }
+
+ i = select(fdsrmax+1, fdsr, NULL, NULL, tvp);
+ switch (i) {
+ case 0:
+ if (tvp) {
+ tvp = NULL;
+ if (ppid != 1)
+ kill(ppid, SIGALRM);
+ }
+ continue;
+ case -1:
+ if (errno != EINTR)
+ logerror("select");
+ continue;
+ }
+ if (fklog != -1 && FD_ISSET(fklog, fdsr))
+ readklog();
+ if (finet && !SecureMode) {
+ for (i = 0; i < *finet; i++) {
+ if (FD_ISSET(finet[i+1], fdsr)) {
+ len = sizeof(frominet);
+ l = recvfrom(finet[i+1], line, MAXLINE,
+ 0, (struct sockaddr *)&frominet,
+ &len);
+ if (l > 0) {
+ line[l] = '\0';
+ hname = cvthname((struct sockaddr *)&frominet);
+ unmapped((struct sockaddr *)&frominet);
+ if (validate((struct sockaddr *)&frominet, hname))
+ printline(hname, line);
+ } else if (l < 0 && errno != EINTR)
+ logerror("recvfrom inet");
+ }
+ }
+ }
+ for (i = 0; i < nfunix; i++) {
+ if (funix[i] != -1 && FD_ISSET(funix[i], fdsr)) {
+ len = sizeof(fromunix);
+ l = recvfrom(funix[i], line, MAXLINE, 0,
+ (struct sockaddr *)&fromunix, &len);
+ if (l > 0) {
+ line[l] = '\0';
+ printline(LocalHostName, line);
+ } else if (l < 0 && errno != EINTR)
+ logerror("recvfrom unix");
+ }
+ }
+ }
+ if (fdsr)
+ free(fdsr);
+}
+
+static void
+unmapped(struct sockaddr *sa)
+{
+ struct sockaddr_in6 *sin6;
+ struct sockaddr_in sin4;
+
+ if (sa->sa_family != AF_INET6)
+ return;
+ if (sa->sa_len != sizeof(struct sockaddr_in6) ||
+ sizeof(sin4) > sa->sa_len)
+ return;
+ sin6 = (struct sockaddr_in6 *)sa;
+ if (!IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
+ return;
+
+ memset(&sin4, 0, sizeof(sin4));
+ sin4.sin_family = AF_INET;
+ sin4.sin_len = sizeof(struct sockaddr_in);
+ memcpy(&sin4.sin_addr, &sin6->sin6_addr.s6_addr[12],
+ sizeof(sin4.sin_addr));
+ sin4.sin_port = sin6->sin6_port;
+
+ memcpy(sa, &sin4, sin4.sin_len);
+}
+
+static void
+usage(void)
+{
+
+ fprintf(stderr, "%s\n%s\n%s\n",
+ "usage: syslogd [-46Adnsuv] [-a allowed_peer] [-f config_file]",
+ " [-m mark_interval] [-l log_socket]",
+ " [-p log_socket] [-P pid_file]");
+ exit(1);
+}
+
+/*
+ * Take a raw input line, decode the message, and print the message
+ * on the appropriate log files.
+ */
+static void
+printline(const char *hname, char *msg)
+{
+ int 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 && !KeepKernFac)
+ pri = LOG_MAKEPRI(LOG_USER, LOG_PRI(pri));
+
+ q = line;
+
+ while ((c = (unsigned char)*p++) != '\0' &&
+ q < &line[sizeof(line) - 4]) {
+ if ((c & 0x80) && c < 0xA0) {
+ c &= 0x7F;
+ *q++ = 'M';
+ *q++ = '-';
+ }
+ if (isascii(c) && iscntrl(c)) {
+ if (c == '\n') {
+ *q++ = ' ';
+ } else if (c == '\t') {
+ *q++ = '\t';
+ } else {
+ *q++ = '^';
+ *q++ = c ^ 0100;
+ }
+ } else {
+ *q++ = c;
+ }
+ }
+ *q = '\0';
+
+ logmsg(pri, line, hname, 0);
+}
+
+/*
+ * Read /dev/klog while data are available, split into lines.
+ */
+static void
+readklog(void)
+{
+ char *p, *q, line[MAXLINE + 1];
+ int len, i;
+
+ len = 0;
+ for (;;) {
+ i = read(fklog, line + len, MAXLINE - 1 - len);
+ if (i > 0) {
+ line[i + len] = '\0';
+ } else {
+ if (i < 0 && errno != EINTR && errno != EAGAIN) {
+ logerror("klog");
+ fklog = -1;
+ }
+ break;
+ }
+
+ for (p = line; (q = strchr(p, '\n')) != NULL; p = q + 1) {
+ *q = '\0';
+ printsys(p);
+ }
+ len = strlen(p);
+ if (len >= MAXLINE - 1) {
+ printsys(p);
+ len = 0;
+ }
+ if (len > 0)
+ memmove(line, p, len + 1);
+ }
+ if (len > 0)
+ printsys(line);
+}
+
+/*
+ * Take a raw input line from /dev/klog, format similar to syslog().
+ */
+static void
+printsys(char *p)
+{
+ int pri, flags;
+
+ 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;
+ if ((pri & LOG_FACMASK) == LOG_CONSOLE)
+ flags |= IGN_CONS;
+ } else {
+ /* kernel printf's come out on console */
+ flags |= IGN_CONS;
+ }
+ if (pri &~ (LOG_FACMASK|LOG_PRIMASK))
+ pri = DEFSPRI;
+ logmsg(pri, p, LocalHostName, flags);
+}
+
+static time_t now;
+
+/*
+ * Log a message to the appropriate log files, users, etc. based on
+ * the priority.
+ */
+static void
+logmsg(int pri, const char *msg, const char *from, int flags)
+{
+ struct filed *f;
+ int i, fac, msglen, omask, prilev;
+ const char *timestamp;
+ char prog[NAME_MAX+1];
+ char buf[MAXLINE+1];
+
+ dprintf("logmsg: pri %o, flags %x, from %s, msg %s\n",
+ pri, flags, from, msg);
+
+ omask = sigblock(sigmask(SIGHUP)|sigmask(SIGALRM));
+
+ /*
+ * Check to see if msg looks non-standard.
+ */
+ msglen = strlen(msg);
+ if (msglen < 16 || msg[3] != ' ' || msg[6] != ' ' ||
+ msg[9] != ':' || msg[12] != ':' || msg[15] != ' ')
+ flags |= ADDDATE;
+
+ (void)time(&now);
+ if (flags & ADDDATE) {
+ timestamp = ctime(&now) + 4;
+ } else {
+ timestamp = msg;
+ msg += 16;
+ msglen -= 16;
+ }
+
+ /* skip leading blanks */
+ while (isspace(*msg)) {
+ msg++;
+ msglen--;
+ }
+
+ /* extract facility and priority level */
+ if (flags & MARK)
+ fac = LOG_NFACILITIES;
+ else
+ fac = LOG_FAC(pri);
+ prilev = LOG_PRI(pri);
+
+ /* extract program name */
+ for (i = 0; i < NAME_MAX; i++) {
+ if (!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",
+ use_bootfile ? bootfile : "kernel", msg);
+ msg = buf;
+ msglen = strlen(buf);
+ }
+
+ /* log the message to the particular outputs */
+ if (!Initialized) {
+ f = &consfile;
+ f->f_file = open(ctty, O_WRONLY, 0);
+
+ if (f->f_file >= 0) {
+ fprintlog(f, flags, msg);
+ (void)close(f->f_file);
+ }
+ (void)sigsetmask(omask);
+ return;
+ }
+ for (f = Files; f; f = f->f_next) {
+ /* skip messages that are incorrect priority */
+ if (!(((f->f_pcmp[fac] & PRI_EQ) && (f->f_pmask[fac] == prilev))
+ ||((f->f_pcmp[fac] & PRI_LT) && (f->f_pmask[fac] < prilev))
+ ||((f->f_pcmp[fac] & PRI_GT) && (f->f_pmask[fac] > prilev))
+ )
+ || f->f_pmask[fac] == INTERNAL_NOPRI)
+ continue;
+ /* skip messages with the incorrect hostname */
+ if (f->f_host)
+ switch (f->f_host[0]) {
+ case '+':
+ if (strcmp(from, f->f_host + 1) != 0)
+ continue;
+ break;
+ case '-':
+ if (strcmp(from, f->f_host + 1) == 0)
+ continue;
+ break;
+ }
+
+ /* 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 (no_compress - (f->f_type != F_PIPE) < 1 &&
+ (flags & MARK) == 0 && msglen == f->f_prevlen &&
+ !strcmp(msg, f->f_prevline) &&
+ !strcasecmp(from, f->f_prevhost)) {
+ (void)strlcpy(f->f_lasttime, timestamp, 16);
+ f->f_prevcount++;
+ dprintf("msg repeated %d times, %ld sec of %d\n",
+ f->f_prevcount, (long)(now - f->f_time),
+ repeatinterval[f->f_repeatcount]);
+ /*
+ * If domark would have logged this by now,
+ * flush it now (so we don't hold isolated messages),
+ * but back off so we'll flush less often
+ * in the future.
+ */
+ if (now > REPEATTIME(f)) {
+ fprintlog(f, flags, (char *)NULL);
+ BACKOFF(f);
+ }
+ } else {
+ /* new line, save it */
+ if (f->f_prevcount)
+ fprintlog(f, 0, (char *)NULL);
+ f->f_repeatcount = 0;
+ f->f_prevpri = pri;
+ (void)strlcpy(f->f_lasttime, timestamp, 16);
+ (void)strlcpy(f->f_prevhost, from,
+ sizeof(f->f_prevhost));
+ if (msglen < MAXSVLINE) {
+ f->f_prevlen = msglen;
+ (void)strlcpy(f->f_prevline, msg, sizeof(f->f_prevline));
+ fprintlog(f, flags, (char *)NULL);
+ } else {
+ f->f_prevline[0] = 0;
+ f->f_prevlen = 0;
+ fprintlog(f, flags, msg);
+ }
+ }
+ }
+ (void)sigsetmask(omask);
+}
+
+static void
+fprintlog(struct filed *f, int flags, const char *msg)
+{
+ struct iovec iov[7];
+ struct iovec *v;
+ struct addrinfo *r;
+ int i, l, lsent = 0;
+ char line[MAXLINE + 1], repbuf[80], greetings[200], *wmsg = NULL;
+ const char *msgret;
+
+ v = iov;
+ if (f->f_type == F_WALL) {
+ v->iov_base = greetings;
+ v->iov_len = snprintf(greetings, sizeof greetings,
+ "\r\n\7Message from syslogd@%s at %.24s ...\r\n",
+ f->f_prevhost, ctime(&now));
+ if (v->iov_len > 0)
+ v++;
+ v->iov_base = "";
+ 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);
+ const char *f_s = NULL;
+ char f_n[5]; /* Hollow laugh */
+ const char *p_s = NULL;
+ char p_n[5]; /* Hollow laugh */
+
+ if (LogFacPri > 1) {
+ CODE *c;
+
+ for (c = facilitynames; c->c_name; c++) {
+ if (c->c_val == fac) {
+ f_s = c->c_name;
+ break;
+ }
+ }
+ for (c = prioritynames; c->c_name; c++) {
+ if (c->c_val == pri) {
+ p_s = c->c_name;
+ break;
+ }
+ }
+ }
+ if (!f_s) {
+ snprintf(f_n, sizeof f_n, "%d", LOG_FAC(fac));
+ f_s = f_n;
+ }
+ if (!p_s) {
+ snprintf(p_n, sizeof p_n, "%d", pri);
+ p_s = p_n;
+ }
+ snprintf(fp_buf, sizeof fp_buf, "<%s.%s> ", f_s, p_s);
+ v->iov_base = fp_buf;
+ v->iov_len = strlen(fp_buf);
+ } else {
+ v->iov_base="";
+ 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) {
+ wmsg = strdup(msg); /* XXX iov_base needs a `const' sibling. */
+ if (wmsg == NULL) {
+ logerror("strdup");
+ exit(1);
+ }
+ v->iov_base = wmsg;
+ v->iov_len = strlen(msg);
+ } else if (f->f_prevcount > 1) {
+ v->iov_base = repbuf;
+ v->iov_len = snprintf(repbuf, sizeof repbuf,
+ "last message repeated %d times", f->f_prevcount);
+ } else {
+ v->iov_base = f->f_prevline;
+ v->iov_len = f->f_prevlen;
+ }
+ v++;
+
+ dprintf("Logging to %s", TypeNames[f->f_type]);
+ f->f_time = now;
+
+ switch (f->f_type) {
+ case F_UNUSED:
+ dprintf("\n");
+ break;
+
+ case F_FORW:
+ dprintf(" %s\n", f->f_un.f_forw.f_hname);
+ /* check for local vs remote messages */
+ if (strcasecmp(f->f_prevhost, LocalHostName))
+ l = snprintf(line, sizeof line - 1,
+ "<%d>%.15s Forwarded from %s: %s",
+ f->f_prevpri, 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 < 0)
+ l = 0;
+ else if (l > MAXLINE)
+ l = MAXLINE;
+
+ if (finet) {
+ for (r = f->f_un.f_forw.f_addr; r; r = r->ai_next) {
+ for (i = 0; i < *finet; i++) {
+#if 0
+ /*
+ * should we check AF first, or just
+ * trial and error? FWD
+ */
+ if (r->ai_family ==
+ address_family_of(finet[i+1]))
+#endif
+ lsent = sendto(finet[i+1], line, l, 0,
+ r->ai_addr, r->ai_addrlen);
+ if (lsent == l)
+ break;
+ }
+ if (lsent == l && !send_to_all)
+ break;
+ }
+ if (lsent != l) {
+ int e = errno;
+ (void)close(f->f_file);
+ errno = e;
+ f->f_type = F_UNUSED;
+ 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_pname);
+ f->f_un.f_pipe.f_pid = 0;
+ errno = e;
+ logerror(f->f_un.f_pipe.f_pname);
+ }
+ break;
+
+ case F_CONSOLE:
+ if (flags & IGN_CONS) {
+ dprintf(" (ignored)\n");
+ break;
+ }
+ /* FALLTHROUGH */
+
+ case F_TTY:
+ dprintf(" %s%s\n", _PATH_DEV, f->f_un.f_fname);
+ v->iov_base = "\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;
+ if (msg)
+ free(wmsg);
+}
+
+/*
+ * WALLMSG -- Write a message to the world at large
+ *
+ * Write the specified message to either the entire
+ * world, or a list of approved users.
+ */
+static void
+wallmsg(struct filed *f, struct iovec *iov)
+{
+ static int reenter; /* avoid calling ourselves */
+ FILE *uf;
+ struct utmp ut;
+ int i;
+ const char *p;
+ char line[sizeof(ut.ut_line) + 1];
+
+ if (reenter++)
+ return;
+ if ((uf = fopen(_PATH_UTMP, "r")) == NULL) {
+ logerror(_PATH_UTMP);
+ reenter = 0;
+ return;
+ }
+ /* NOSTRICT */
+ while (fread((char *)&ut, sizeof(ut), 1, uf) == 1) {
+ if (ut.ut_name[0] == '\0')
+ continue;
+ (void)strlcpy(line, ut.ut_line, sizeof(line));
+ if (f->f_type == F_WALL) {
+ if ((p = ttymsg(iov, 7, line, TTYMSGTIME)) != NULL) {
+ errno = 0; /* already in msg */
+ logerror(p);
+ }
+ continue;
+ }
+ /* should we send the message to this user? */
+ for (i = 0; i < MAXUNAMES; i++) {
+ if (!f->f_un.f_uname[i][0])
+ break;
+ if (!strncmp(f->f_un.f_uname[i], ut.ut_name,
+ UT_NAMESIZE)) {
+ if ((p = ttymsg(iov, 7, line, TTYMSGTIME))
+ != NULL) {
+ errno = 0; /* already in msg */
+ logerror(p);
+ }
+ break;
+ }
+ }
+ }
+ (void)fclose(uf);
+ reenter = 0;
+}
+
+static void
+reapchild(int signo __unused)
+{
+ int status;
+ pid_t pid;
+ struct filed *f;
+
+ while ((pid = wait3(&status, WNOHANG, (struct rusage *)NULL)) > 0) {
+ if (!Initialized)
+ /* Don't tell while we are initting. */
+ continue;
+
+ /* First, look if it's a process from the dead queue. */
+ if (deadq_remove(pid))
+ goto oncemore;
+
+ /* Now, look in list of active processes. */
+ for (f = Files; f; f = f->f_next)
+ if (f->f_type == F_PIPE &&
+ f->f_un.f_pipe.f_pid == pid) {
+ (void)close(f->f_file);
+ f->f_un.f_pipe.f_pid = 0;
+ log_deadchild(pid, status,
+ f->f_un.f_pipe.f_pname);
+ break;
+ }
+ oncemore:
+ continue;
+ }
+}
+
+/*
+ * Return a printable representation of a host address.
+ */
+static const char *
+cvthname(struct sockaddr *f)
+{
+ int error;
+ sigset_t omask, nmask;
+ char *p;
+ static char hname[NI_MAXHOST], ip[NI_MAXHOST];
+
+ error = getnameinfo((struct sockaddr *)f,
+ ((struct sockaddr *)f)->sa_len,
+ ip, sizeof ip, NULL, 0,
+ NI_NUMERICHOST | withscopeid);
+ dprintf("cvthname(%s)\n", ip);
+
+ if (error) {
+ dprintf("Malformed from address %s\n", gai_strerror(error));
+ return ("???");
+ }
+ if (!resolve)
+ return (ip);
+
+ sigemptyset(&nmask);
+ sigaddset(&nmask, SIGHUP);
+ sigprocmask(SIG_BLOCK, &nmask, &omask);
+ error = getnameinfo((struct sockaddr *)f,
+ ((struct sockaddr *)f)->sa_len,
+ hname, sizeof hname, NULL, 0,
+ NI_NAMEREQD | withscopeid);
+ sigprocmask(SIG_SETMASK, &omask, NULL);
+ if (error) {
+ dprintf("Host name for your address (%s) unknown\n", ip);
+ return (ip);
+ }
+ /* XXX Not quite correct, but close enough for government work. */
+ if ((p = strchr(hname, '.')) && strcasecmp(p + 1, LocalDomain) == 0)
+ *p = '\0';
+ return (hname);
+}
+
+static void
+dodie(int signo)
+{
+
+ WantDie = signo;
+}
+
+static void
+domark(int signo __unused)
+{
+
+ MarkSet = 1;
+}
+
+/*
+ * Print syslogd errors some place.
+ */
+static void
+logerror(const char *type)
+{
+ char buf[512];
+
+ 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);
+}
+
+static void
+die(int signo)
+{
+ struct filed *f;
+ int was_initialized;
+ char buf[100];
+ int i;
+
+ was_initialized = Initialized;
+ Initialized = 0; /* Don't log SIGCHLDs. */
+ for (f = Files; f != NULL; f = f->f_next) {
+ /* flush any pending output */
+ if (f->f_prevcount)
+ fprintlog(f, 0, (char *)NULL);
+ if (f->f_type == F_PIPE)
+ (void)close(f->f_file);
+ }
+ Initialized = was_initialized;
+ if (signo) {
+ dprintf("syslogd: exiting on signal %d\n", signo);
+ (void)snprintf(buf, sizeof(buf), "exiting on signal %d", signo);
+ errno = 0;
+ logerror(buf);
+ }
+ for (i = 0; i < nfunix; i++)
+ if (funixn[i] && funix[i] != -1)
+ (void)unlink(funixn[i]);
+ exit(1);
+}
+
+/*
+ * INIT -- Initialize syslogd from configuration table
+ */
+static void
+init(int signo)
+{
+ int i;
+ FILE *cf;
+ struct filed *f, *next, **nextp;
+ char *p;
+ char cline[LINE_MAX];
+ char prog[NAME_MAX+1];
+ char host[MAXHOSTNAMELEN];
+ char oldLocalHostName[MAXHOSTNAMELEN];
+ char hostMsg[2*MAXHOSTNAMELEN+40];
+ char bootfileMsg[LINE_MAX];
+
+ dprintf("init\n");
+
+ /*
+ * Load hostname (may have changed).
+ */
+ if (signo != 0)
+ (void)strlcpy(oldLocalHostName, LocalHostName,
+ sizeof(oldLocalHostName));
+ if (gethostname(LocalHostName, sizeof(LocalHostName)))
+ err(EX_OSERR, "gethostname() failed");
+ if ((p = strchr(LocalHostName, '.')) != NULL) {
+ *p++ = '\0';
+ LocalDomain = p;
+ } else {
+ LocalDomain = "";
+ }
+
+ /*
+ * Close all open log files.
+ */
+ Initialized = 0;
+ for (f = Files; f != NULL; f = next) {
+ /* flush any pending output */
+ if (f->f_prevcount)
+ fprintlog(f, 0, (char *)NULL);
+
+ switch (f->f_type) {
+ case F_FILE:
+ case F_FORW:
+ case F_CONSOLE:
+ case F_TTY:
+ (void)close(f->f_file);
+ break;
+ case F_PIPE:
+ (void)close(f->f_file);
+ if (f->f_un.f_pipe.f_pid > 0)
+ deadq_enter(f->f_un.f_pipe.f_pid,
+ f->f_un.f_pipe.f_pname);
+ f->f_un.f_pipe.f_pid = 0;
+ break;
+ }
+ next = f->f_next;
+ if (f->f_program) free(f->f_program);
+ if (f->f_host) free(f->f_host);
+ free((char *)f);
+ }
+ Files = NULL;
+ nextp = &Files;
+
+ /* open the configuration file */
+ if ((cf = fopen(ConfFile, "r")) == NULL) {
+ dprintf("cannot open %s\n", ConfFile);
+ *nextp = (struct filed *)calloc(1, sizeof(*f));
+ if (*nextp == NULL) {
+ logerror("calloc");
+ exit(1);
+ }
+ cfline("*.ERR\t/dev/console", *nextp, "*", "*");
+ (*nextp)->f_next = (struct filed *)calloc(1, sizeof(*f));
+ if ((*nextp)->f_next == NULL) {
+ logerror("calloc");
+ exit(1);
+ }
+ cfline("*.PANIC\t*", (*nextp)->f_next, "*", "*");
+ Initialized = 1;
+ return;
+ }
+
+ /*
+ * Foreach line in the conf table, open that file.
+ */
+ f = NULL;
+ (void)strlcpy(host, "*", sizeof(host));
+ (void)strlcpy(prog, "*", sizeof(prog));
+ while (fgets(cline, sizeof(cline), cf) != NULL) {
+ /*
+ * check for end-of-section, comments, strip off trailing
+ * spaces and newline character. #!prog is treated specially:
+ * following lines apply only to that program.
+ */
+ for (p = cline; isspace(*p); ++p)
+ continue;
+ if (*p == 0)
+ continue;
+ if (*p == '#') {
+ p++;
+ if (*p != '!' && *p != '+' && *p != '-')
+ continue;
+ }
+ if (*p == '+' || *p == '-') {
+ host[0] = *p++;
+ while (isspace(*p))
+ p++;
+ if ((!*p) || (*p == '*')) {
+ (void)strlcpy(host, "*", sizeof(host));
+ continue;
+ }
+ if (*p == '@')
+ p = LocalHostName;
+ for (i = 1; i < MAXHOSTNAMELEN - 1; i++) {
+ if (!isalnum(*p) && *p != '.' && *p != '-')
+ break;
+ host[i] = *p++;
+ }
+ host[i] = '\0';
+ continue;
+ }
+ if (*p == '!') {
+ p++;
+ while (isspace(*p)) p++;
+ if ((!*p) || (*p == '*')) {
+ (void)strlcpy(prog, "*", sizeof(prog));
+ continue;
+ }
+ for (i = 0; i < NAME_MAX; i++) {
+ if (!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));
+ if (f == NULL) {
+ logerror("calloc");
+ exit(1);
+ }
+ *nextp = f;
+ nextp = &f->f_next;
+ cfline(cline, f, prog, host);
+ }
+
+ /* close the configuration file */
+ (void)fclose(cf);
+
+ Initialized = 1;
+
+ if (Debug) {
+ for (f = Files; f; f = f->f_next) {
+ for (i = 0; i <= LOG_NFACILITIES; i++)
+ if (f->f_pmask[i] == INTERNAL_NOPRI)
+ printf("X ");
+ else
+ printf("%d ", f->f_pmask[i]);
+ printf("%s: ", TypeNames[f->f_type]);
+ switch (f->f_type) {
+ case F_FILE:
+ printf("%s", f->f_un.f_fname);
+ break;
+
+ case F_CONSOLE:
+ case F_TTY:
+ printf("%s%s", _PATH_DEV, f->f_un.f_fname);
+ break;
+
+ case F_FORW:
+ printf("%s", f->f_un.f_forw.f_hname);
+ break;
+
+ case F_PIPE:
+ printf("%s", f->f_un.f_pipe.f_pname);
+ break;
+
+ case F_USERS:
+ for (i = 0; i < MAXUNAMES && *f->f_un.f_uname[i]; i++)
+ printf("%s, ", f->f_un.f_uname[i]);
+ break;
+ }
+ if (f->f_program)
+ printf(" (%s)", f->f_program);
+ printf("\n");
+ }
+ }
+
+ logmsg(LOG_SYSLOG|LOG_INFO, "syslogd: restart", LocalHostName, ADDDATE);
+ dprintf("syslogd: restarted\n");
+ /*
+ * Log a change in hostname, but only on a restart.
+ */
+ if (signo != 0 && strcmp(oldLocalHostName, LocalHostName) != 0) {
+ (void)snprintf(hostMsg, sizeof(hostMsg),
+ "syslogd: hostname changed, \"%s\" to \"%s\"",
+ oldLocalHostName, LocalHostName);
+ logmsg(LOG_SYSLOG|LOG_INFO, hostMsg, LocalHostName, ADDDATE);
+ dprintf("%s\n", hostMsg);
+ }
+ /*
+ * Log the kernel boot file if we aren't going to use it as
+ * the prefix, and if this is *not* a restart.
+ */
+ if (signo == 0 && !use_bootfile) {
+ (void)snprintf(bootfileMsg, sizeof(bootfileMsg),
+ "syslogd: kernel boot file is %s", bootfile);
+ logmsg(LOG_KERN|LOG_INFO, bootfileMsg, LocalHostName, ADDDATE);
+ dprintf("%s\n", bootfileMsg);
+ }
+}
+
+/*
+ * Crack a configuration file line
+ */
+static void
+cfline(const char *line, struct filed *f, const char *prog, const char *host)
+{
+ struct addrinfo hints, *res;
+ int error, i, pri;
+ const char *p, *q;
+ char *bp;
+ char buf[MAXLINE], ebuf[100];
+
+ dprintf("cfline(\"%s\", f, \"%s\", \"%s\")\n", line, prog, host);
+
+ errno = 0; /* keep strerror() stuff out of logerror messages */
+
+ /* clear out file entry */
+ memset(f, 0, sizeof(*f));
+ for (i = 0; i <= LOG_NFACILITIES; i++)
+ f->f_pmask[i] = INTERNAL_NOPRI;
+
+ /* save hostname if any */
+ if (host && *host == '*')
+ host = NULL;
+ if (host) {
+ int hl, dl;
+
+ f->f_host = strdup(host);
+ if (f->f_host == NULL) {
+ logerror("strdup");
+ exit(1);
+ }
+ hl = strlen(f->f_host);
+ if (f->f_host[hl-1] == '.')
+ f->f_host[--hl] = '\0';
+ dl = strlen(LocalDomain) + 1;
+ if (hl > dl && f->f_host[hl-dl] == '.' &&
+ strcasecmp(f->f_host + hl - dl + 1, LocalDomain) == 0)
+ f->f_host[hl-dl] = '\0';
+ }
+
+ /* save program name if any */
+ if (prog && *prog == '*')
+ prog = NULL;
+ if (prog) {
+ f->f_program = strdup(prog);
+ if (f->f_program == NULL) {
+ logerror("strdup");
+ exit(1);
+ }
+ }
+
+ /* scan through the list of selectors */
+ for (p = line; *p && *p != '\t' && *p != ' ';) {
+ int pri_done;
+ int pri_cmp;
+
+ /* 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)strlcpy(f->f_un.f_forw.f_hname, ++p,
+ sizeof(f->f_un.f_forw.f_hname));
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = family;
+ hints.ai_socktype = SOCK_DGRAM;
+ error = getaddrinfo(f->f_un.f_forw.f_hname, "syslog", &hints,
+ &res);
+ if (error) {
+ logerror(gai_strerror(error));
+ break;
+ }
+ f->f_un.f_forw.f_addr = res;
+ f->f_type = F_FORW;
+ break;
+
+ case '/':
+ if ((f->f_file = open(p, O_WRONLY|O_APPEND, 0)) < 0) {
+ f->f_type = F_UNUSED;
+ logerror(p);
+ break;
+ }
+ if (isatty(f->f_file)) {
+ if (strcmp(p, ctty) == 0)
+ f->f_type = F_CONSOLE;
+ else
+ f->f_type = F_TTY;
+ (void)strlcpy(f->f_un.f_fname, p + sizeof(_PATH_DEV) - 1,
+ sizeof(f->f_un.f_fname));
+ } else {
+ (void)strlcpy(f->f_un.f_fname, p, sizeof(f->f_un.f_fname));
+ f->f_type = F_FILE;
+ }
+ break;
+
+ case '|':
+ f->f_un.f_pipe.f_pid = 0;
+ (void)strlcpy(f->f_un.f_fname, p + 1, sizeof(f->f_un.f_fname));
+ f->f_type = F_PIPE;
+ break;
+
+ case '*':
+ f->f_type = F_WALL;
+ break;
+
+ default:
+ for (i = 0; i < MAXUNAMES && *p; i++) {
+ for (q = p; *q && *q != ','; )
+ q++;
+ (void)strncpy(f->f_un.f_uname[i], p, UT_NAMESIZE);
+ if ((q - p) > UT_NAMESIZE)
+ f->f_un.f_uname[i][UT_NAMESIZE] = '\0';
+ else
+ f->f_un.f_uname[i][q - p] = '\0';
+ while (*q == ',' || *q == ' ')
+ q++;
+ p = q;
+ }
+ f->f_type = F_USERS;
+ break;
+ }
+}
+
+
+/*
+ * Decode a symbolic name to a numeric value
+ */
+static int
+decode(const char *name, CODE *codetab)
+{
+ CODE *c;
+ char *p, buf[40];
+
+ if (isdigit(*name))
+ return (atoi(name));
+
+ for (p = buf; *name && p < &buf[sizeof(buf) - 1]; p++, name++) {
+ if (isupper(*name))
+ *p = tolower(*name);
+ else
+ *p = *name;
+ }
+ *p = '\0';
+ for (c = codetab; c->c_name; c++)
+ if (!strcmp(buf, c->c_name))
+ return (c->c_val);
+
+ return (-1);
+}
+
+static void
+markit(void)
+{
+ struct filed *f;
+ dq_t q;
+
+ 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. */
+ TAILQ_FOREACH(q, &deadq_head, dq_entries) {
+ switch (q->dq_timeout) {
+ case 0:
+ /* Already signalled once, try harder now. */
+ if (kill(q->dq_pid, SIGKILL) != 0)
+ (void)deadq_remove(q->dq_pid);
+ break;
+
+ case 1:
+ /*
+ * Timed out on dead queue, send terminate
+ * signal. Note that we leave the removal
+ * from the dead queue to reapchild(), which
+ * will also log the event (unless the process
+ * didn't even really exist, in case we simply
+ * drop it from the dead queue).
+ */
+ if (kill(q->dq_pid, SIGTERM) != 0)
+ (void)deadq_remove(q->dq_pid);
+ /* FALLTHROUGH */
+
+ default:
+ q->dq_timeout--;
+ }
+ }
+ MarkSet = 0;
+ (void)alarm(TIMERINTVL);
+}
+
+/*
+ * fork off and become a daemon, but wait for the child to come online
+ * before returing to the parent, or we get disk thrashing at boot etc.
+ * Set a timer so we don't hang forever if it wedges.
+ */
+static int
+waitdaemon(int nochdir, int noclose, int maxwait)
+{
+ int fd;
+ int status;
+ pid_t pid, childpid;
+
+ switch (childpid = fork()) {
+ case -1:
+ return (-1);
+ case 0:
+ break;
+ default:
+ signal(SIGALRM, timedout);
+ alarm(maxwait);
+ while ((pid = wait3(&status, 0, NULL)) != -1) {
+ if (WIFEXITED(status))
+ errx(1, "child pid %d exited with return code %d",
+ pid, WEXITSTATUS(status));
+ if (WIFSIGNALED(status))
+ errx(1, "child pid %d exited on signal %d%s",
+ pid, WTERMSIG(status),
+ WCOREDUMP(status) ? " (core dumped)" :
+ "");
+ if (pid == childpid) /* it's gone... */
+ break;
+ }
+ exit(0);
+ }
+
+ if (setsid() == -1)
+ return (-1);
+
+ if (!nochdir)
+ (void)chdir("/");
+
+ if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
+ (void)dup2(fd, STDIN_FILENO);
+ (void)dup2(fd, STDOUT_FILENO);
+ (void)dup2(fd, STDERR_FILENO);
+ if (fd > 2)
+ (void)close (fd);
+ }
+ return (getppid());
+}
+
+/*
+ * We get a SIGALRM from the child when it's running and finished doing it's
+ * fsync()'s or O_SYNC writes for all the boot messages.
+ *
+ * We also get a signal from the kernel if the timer expires, so check to
+ * see what happened.
+ */
+static void
+timedout(int sig __unused)
+{
+ int left;
+ left = alarm(0);
+ signal(SIGALRM, SIG_DFL);
+ if (left == 0)
+ errx(1, "timed out waiting for child");
+ else
+ _exit(0);
+}
+
+/*
+ * Add `s' to the list of allowable peer addresses to accept messages
+ * from.
+ *
+ * `s' is a string in the form:
+ *
+ * [*]domainname[:{servicename|portnumber|*}]
+ *
+ * or
+ *
+ * netaddr/maskbits[:{servicename|portnumber|*}]
+ *
+ * Returns -1 on error, 0 if the argument was valid.
+ */
+static int
+allowaddr(char *s)
+{
+ char *cp1, *cp2;
+ struct allowedpeer ap;
+ struct servent *se;
+ int masklen = -1, i;
+ struct addrinfo hints, *res;
+ struct in_addr *addrp, *maskp;
+ u_int32_t *addr6p, *mask6p;
+ char ip[NI_MAXHOST];
+
+#ifdef INET6
+ if (*s != '[' || (cp1 = strchr(s + 1, ']')) == NULL)
+#endif
+ cp1 = s;
+ if ((cp1 = strrchr(cp1, ':'))) {
+ /* service/port provided */
+ *cp1++ = '\0';
+ if (strlen(cp1) == 1 && *cp1 == '*')
+ /* any port allowed */
+ ap.port = 0;
+ else if ((se = getservbyname(cp1, "udp"))) {
+ ap.port = ntohs(se->s_port);
+ } else {
+ ap.port = strtol(cp1, &cp2, 0);
+ if (*cp2 != '\0')
+ return (-1); /* port not numeric */
+ }
+ } else {
+ if ((se = getservbyname("syslog", "udp")))
+ ap.port = ntohs(se->s_port);
+ else
+ /* sanity, should not happen */
+ ap.port = 514;
+ }
+
+ if ((cp1 = strchr(s, '/')) != NULL &&
+ strspn(cp1 + 1, "0123456789") == strlen(cp1 + 1)) {
+ *cp1 = '\0';
+ if ((masklen = atoi(cp1 + 1)) < 0)
+ return (-1);
+ }
+#ifdef INET6
+ if (*s == '[') {
+ cp2 = s + strlen(s) - 1;
+ if (*cp2 == ']') {
+ ++s;
+ *cp2 = '\0';
+ } else {
+ cp2 = NULL;
+ }
+ } else {
+ cp2 = NULL;
+ }
+#endif
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
+ if (getaddrinfo(s, NULL, &hints, &res) == 0) {
+ ap.isnumeric = 1;
+ memcpy(&ap.a_addr, res->ai_addr, res->ai_addrlen);
+ memset(&ap.a_mask, 0, sizeof(ap.a_mask));
+ ap.a_mask.ss_family = res->ai_family;
+ if (res->ai_family == AF_INET) {
+ ap.a_mask.ss_len = sizeof(struct sockaddr_in);
+ maskp = &((struct sockaddr_in *)&ap.a_mask)->sin_addr;
+ addrp = &((struct sockaddr_in *)&ap.a_addr)->sin_addr;
+ if (masklen < 0) {
+ /* use default netmask */
+ if (IN_CLASSA(ntohl(addrp->s_addr)))
+ maskp->s_addr = htonl(IN_CLASSA_NET);
+ else if (IN_CLASSB(ntohl(addrp->s_addr)))
+ maskp->s_addr = htonl(IN_CLASSB_NET);
+ else
+ maskp->s_addr = htonl(IN_CLASSC_NET);
+ } else if (masklen <= 32) {
+ /* convert masklen to netmask */
+ if (masklen == 0)
+ maskp->s_addr = 0;
+ else
+ maskp->s_addr = htonl(~((1 << (32 - masklen)) - 1));
+ } else {
+ freeaddrinfo(res);
+ return (-1);
+ }
+ /* Lose any host bits in the network number. */
+ addrp->s_addr &= maskp->s_addr;
+ }
+#ifdef INET6
+ else if (res->ai_family == AF_INET6 && masklen <= 128) {
+ ap.a_mask.ss_len = sizeof(struct sockaddr_in6);
+ if (masklen < 0)
+ masklen = 128;
+ mask6p = (u_int32_t *)&((struct sockaddr_in6 *)&ap.a_mask)->sin6_addr;
+ /* convert masklen to netmask */
+ while (masklen > 0) {
+ if (masklen < 32) {
+ *mask6p = htonl(~(0xffffffff >> masklen));
+ break;
+ }
+ *mask6p++ = 0xffffffff;
+ masklen -= 32;
+ }
+ /* Lose any host bits in the network number. */
+ mask6p = (u_int32_t *)&((struct sockaddr_in6 *)&ap.a_mask)->sin6_addr;
+ addr6p = (u_int32_t *)&((struct sockaddr_in6 *)&ap.a_addr)->sin6_addr;
+ for (i = 0; i < 4; i++)
+ addr6p[i] &= mask6p[i];
+ }
+#endif
+ else {
+ freeaddrinfo(res);
+ return (-1);
+ }
+ freeaddrinfo(res);
+ } else {
+ /* arg `s' is domain name */
+ ap.isnumeric = 0;
+ ap.a_name = s;
+ if (cp1)
+ *cp1 = '/';
+#ifdef INET6
+ if (cp2) {
+ *cp2 = ']';
+ --s;
+ }
+#endif
+ }
+
+ if (Debug) {
+ printf("allowaddr: rule %d: ", NumAllowed);
+ if (ap.isnumeric) {
+ printf("numeric, ");
+ getnameinfo((struct sockaddr *)&ap.a_addr,
+ ((struct sockaddr *)&ap.a_addr)->sa_len,
+ ip, sizeof ip, NULL, 0,
+ NI_NUMERICHOST | withscopeid);
+ printf("addr = %s, ", ip);
+ getnameinfo((struct sockaddr *)&ap.a_mask,
+ ((struct sockaddr *)&ap.a_mask)->sa_len,
+ ip, sizeof ip, NULL, 0,
+ NI_NUMERICHOST | withscopeid);
+ printf("mask = %s; ", ip);
+ } else {
+ printf("domainname = %s; ", ap.a_name);
+ }
+ printf("port = %d\n", ap.port);
+ }
+
+ if ((AllowedPeers = realloc(AllowedPeers,
+ ++NumAllowed * sizeof(struct allowedpeer)))
+ == NULL) {
+ logerror("realloc");
+ exit(1);
+ }
+ memcpy(&AllowedPeers[NumAllowed - 1], &ap, sizeof(struct allowedpeer));
+ return (0);
+}
+
+/*
+ * Validate that the remote peer has permission to log to us.
+ */
+static int
+validate(struct sockaddr *sa, const char *hname)
+{
+ int i, j, reject;
+ size_t l1, l2;
+ char *cp, name[NI_MAXHOST], ip[NI_MAXHOST], port[NI_MAXSERV];
+ struct allowedpeer *ap;
+ struct sockaddr_in *sin4, *a4p = NULL, *m4p = NULL;
+ struct sockaddr_in6 *sin6, *a6p = NULL, *m6p = NULL;
+ struct addrinfo hints, *res;
+ u_short sport;
+
+ if (NumAllowed == 0)
+ /* traditional behaviour, allow everything */
+ return (1);
+
+ (void)strlcpy(name, hname, sizeof(name));
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
+ if (getaddrinfo(name, NULL, &hints, &res) == 0)
+ freeaddrinfo(res);
+ else if (strchr(name, '.') == NULL) {
+ strlcat(name, ".", sizeof name);
+ strlcat(name, LocalDomain, sizeof name);
+ }
+ if (getnameinfo(sa, sa->sa_len, ip, sizeof ip, port, sizeof port,
+ NI_NUMERICHOST | withscopeid | NI_NUMERICSERV) != 0)
+ return (0); /* for safety, should not occur */
+ dprintf("validate: dgram from IP %s, port %s, name %s;\n",
+ ip, port, name);
+ sport = atoi(port);
+
+ /* now, walk down the list */
+ for (i = 0, ap = AllowedPeers; i < NumAllowed; i++, ap++) {
+ if (ap->port != 0 && ap->port != sport) {
+ dprintf("rejected in rule %d due to port mismatch.\n", i);
+ continue;
+ }
+
+ if (ap->isnumeric) {
+ if (ap->a_addr.ss_family != sa->sa_family) {
+ dprintf("rejected in rule %d due to address family mismatch.\n", i);
+ continue;
+ }
+ if (ap->a_addr.ss_family == AF_INET) {
+ sin4 = (struct sockaddr_in *)sa;
+ a4p = (struct sockaddr_in *)&ap->a_addr;
+ m4p = (struct sockaddr_in *)&ap->a_mask;
+ if ((sin4->sin_addr.s_addr & m4p->sin_addr.s_addr)
+ != a4p->sin_addr.s_addr) {
+ dprintf("rejected in rule %d due to IP mismatch.\n", i);
+ continue;
+ }
+ }
+#ifdef INET6
+ else if (ap->a_addr.ss_family == AF_INET6) {
+ sin6 = (struct sockaddr_in6 *)sa;
+ a6p = (struct sockaddr_in6 *)&ap->a_addr;
+ m6p = (struct sockaddr_in6 *)&ap->a_mask;
+#ifdef NI_WITHSCOPEID
+ if (a6p->sin6_scope_id != 0 &&
+ sin6->sin6_scope_id != a6p->sin6_scope_id) {
+ dprintf("rejected in rule %d due to scope mismatch.\n", i);
+ continue;
+ }
+#endif
+ reject = 0;
+ for (j = 0; j < 16; j += 4) {
+ if ((*(u_int32_t *)&sin6->sin6_addr.s6_addr[j] & *(u_int32_t *)&m6p->sin6_addr.s6_addr[j])
+ != *(u_int32_t *)&a6p->sin6_addr.s6_addr[j]) {
+ ++reject;
+ break;
+ }
+ }
+ if (reject) {
+ dprintf("rejected in rule %d due to IP mismatch.\n", i);
+ continue;
+ }
+ }
+#endif
+ else
+ continue;
+ } else {
+ cp = ap->a_name;
+ l1 = strlen(name);
+ if (*cp == '*') {
+ /* allow wildmatch */
+ cp++;
+ l2 = strlen(cp);
+ if (l2 > l1 || memcmp(cp, &name[l1 - l2], l2) != 0) {
+ dprintf("rejected in rule %d due to name mismatch.\n", i);
+ continue;
+ }
+ } else {
+ /* exact match */
+ l2 = strlen(cp);
+ if (l2 != l1 || memcmp(cp, name, l1) != 0) {
+ dprintf("rejected in rule %d due to name mismatch.\n", i);
+ continue;
+ }
+ }
+ }
+ dprintf("accepted in rule %d.\n", i);
+ return (1); /* hooray! */
+ }
+ return (0);
+}
+
+/*
+ * Fairly similar to popen(3), but returns an open descriptor, as
+ * opposed to a FILE *.
+ */
+static int
+p_open(const char *prog, pid_t *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:
+ /* XXX should check for NULL return */
+ argv[0] = strdup("sh");
+ argv[1] = strdup("-c");
+ argv[2] = strdup(prog);
+ argv[3] = NULL;
+ if (argv[0] == NULL || argv[1] == NULL || argv[2] == NULL) {
+ logerror("strdup");
+ exit(1);
+ }
+
+ alarm(0);
+ (void)setsid(); /* Avoid catching SIGHUPs. */
+
+ /*
+ * Throw away pending signals, and reset signal
+ * behaviour to standard values.
+ */
+ signal(SIGALRM, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+ sigprocmask(SIG_SETMASK, &omask, 0);
+ signal(SIGPIPE, SIG_DFL);
+ signal(SIGQUIT, SIG_DFL);
+ signal(SIGALRM, SIG_DFL);
+ signal(SIGHUP, SIG_DFL);
+
+ dup2(pfd[0], STDIN_FILENO);
+ dup2(nulldesc, STDOUT_FILENO);
+ dup2(nulldesc, STDERR_FILENO);
+ for (i = getdtablesize(); i > 2; i--)
+ (void)close(i);
+
+ (void)execvp(_PATH_BSHELL, argv);
+ _exit(255);
+ }
+
+ sigprocmask(SIG_SETMASK, &omask, 0);
+ close(nulldesc);
+ close(pfd[0]);
+ /*
+ * Avoid blocking on a hung pipe. With O_NONBLOCK, we are
+ * supposed to get an EWOULDBLOCK on writev(2), which is
+ * caught by the logic above anyway, which will in turn close
+ * the pipe, and fork a new logging subprocess if necessary.
+ * The stale subprocess will be killed some time later unless
+ * it terminated itself due to closing its input pipe (so we
+ * get rid of really dead puppies).
+ */
+ if (fcntl(pfd[1], F_SETFL, O_NONBLOCK) == -1) {
+ /* This is bad. */
+ (void)snprintf(errmsg, sizeof errmsg,
+ "Warning: cannot change pipe to PID %d to "
+ "non-blocking behaviour.",
+ (int)*pid);
+ logerror(errmsg);
+ }
+ return (pfd[1]);
+}
+
+static void
+deadq_enter(pid_t pid, const char *name)
+{
+ dq_t p;
+ int status;
+
+ /*
+ * Be paranoid, if we can't signal the process, don't enter it
+ * into the dead queue (perhaps it's already dead). If possible,
+ * we try to fetch and log the child's status.
+ */
+ if (kill(pid, 0) != 0) {
+ if (waitpid(pid, &status, WNOHANG) > 0)
+ log_deadchild(pid, status, name);
+ return;
+ }
+
+ p = malloc(sizeof(struct deadq_entry));
+ if (p == NULL) {
+ logerror("malloc");
+ exit(1);
+ }
+
+ p->dq_pid = pid;
+ p->dq_timeout = DQ_TIMO_INIT;
+ TAILQ_INSERT_TAIL(&deadq_head, p, dq_entries);
+}
+
+static int
+deadq_remove(pid_t pid)
+{
+ dq_t q;
+
+ TAILQ_FOREACH(q, &deadq_head, dq_entries) {
+ if (q->dq_pid == pid) {
+ TAILQ_REMOVE(&deadq_head, q, dq_entries);
+ free(q);
+ return (1);
+ }
+ }
+
+ return (0);
+}
+
+static void
+log_deadchild(pid_t pid, int status, const char *name)
+{
+ int code;
+ char buf[256];
+ const char *reason;
+
+ errno = 0; /* Keep strerror() stuff out of logerror messages. */
+ if (WIFSIGNALED(status)) {
+ reason = "due to signal";
+ code = WTERMSIG(status);
+ } else {
+ reason = "with status";
+ code = WEXITSTATUS(status);
+ if (code == 0)
+ return;
+ }
+ (void)snprintf(buf, sizeof buf,
+ "Logging subprocess %d (%s) exited %s %d.",
+ pid, name, reason, code);
+ logerror(buf);
+}
+
+static int *
+socksetup(int af, const char *bindhostname)
+{
+ struct addrinfo hints, *res, *r;
+ int error, maxs, *s, *socks;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = af;
+ hints.ai_socktype = SOCK_DGRAM;
+ error = getaddrinfo(bindhostname, "syslog", &hints, &res);
+ if (error) {
+ logerror(gai_strerror(error));
+ errno = 0;
+ die(0);
+ }
+
+ /* Count max number of sockets we may open */
+ for (maxs = 0, r = res; r; r = r->ai_next, maxs++);
+ socks = malloc((maxs+1) * sizeof(int));
+ if (socks == NULL) {
+ logerror("couldn't allocate memory for sockets");
+ die(0);
+ }
+
+ *socks = 0; /* num of sockets counter at start of array */
+ s = socks + 1;
+ for (r = res; r; r = r->ai_next) {
+ *s = socket(r->ai_family, r->ai_socktype, r->ai_protocol);
+ if (*s < 0) {
+ logerror("socket");
+ continue;
+ }
+#ifdef IPV6_BINDV6ONLY
+ if (r->ai_family == AF_INET6) {
+ int on = 1;
+ if (setsockopt(*s, IPPROTO_IPV6, IPV6_BINDV6ONLY,
+ (char *)&on, sizeof (on)) < 0) {
+ logerror("setsockopt");
+ close(*s);
+ continue;
+ }
+ }
+#endif
+ if (bind(*s, r->ai_addr, r->ai_addrlen) < 0) {
+ close(*s);
+ logerror("bind");
+ continue;
+ }
+
+ (*socks)++;
+ s++;
+ }
+
+ if (*socks == 0) {
+ free(socks);
+ if (Debug)
+ return (NULL);
+ else
+ die(0);
+ }
+ if (res)
+ freeaddrinfo(res);
+
+ return (socks);
+}
diff --git a/usr.sbin/tcpdchk/Makefile b/usr.sbin/tcpdchk/Makefile
new file mode 100644
index 0000000..3c5a040
--- /dev/null
+++ b/usr.sbin/tcpdchk/Makefile
@@ -0,0 +1,21 @@
+# $FreeBSD$
+
+MAINTAINER= markm@FreeBSD.org
+
+.PATH: ${.CURDIR}/../../contrib/tcp_wrappers
+
+PROG= tcpdchk
+MAN= tcpdchk.8
+SRCS= tcpdchk.c fakelog.c inetcf.c scaffold.c
+
+CFLAGS+=-DREAL_DAEMON_DIR=\"/usr/libexec\" \
+ -DSEVERITY=LOG_INFO -DRFC931_TIMEOUT=10 \
+ -DHOSTS_DENY=\"/etc/hosts.deny\" -DHOSTS_ALLOW=\"/etc/hosts.allow\"
+.if !defined(NOINET6)
+CFLAGS+=-DINET6
+.endif
+
+DPADD= ${LIBWRAP}
+LDADD= -lwrap
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/tcpdmatch/Makefile b/usr.sbin/tcpdmatch/Makefile
new file mode 100644
index 0000000..161d54b
--- /dev/null
+++ b/usr.sbin/tcpdmatch/Makefile
@@ -0,0 +1,20 @@
+# $FreeBSD$
+
+MAINTAINER= markm@FreeBSD.org
+
+.PATH: ${.CURDIR}/../../contrib/tcp_wrappers
+
+PROG= tcpdmatch
+MAN= tcpdmatch.8
+SRCS= tcpdmatch.c fakelog.c inetcf.c scaffold.c
+
+CFLAGS+=-DREAL_DAEMON_DIR=\"/usr/libexec\" \
+ -DSEVERITY=LOG_INFO -DRFC931_TIMEOUT=10
+.if !defined(NOINET6)
+CFLAGS+=-DINET6
+.endif
+
+DPADD= ${LIBWRAP}
+LDADD= -lwrap
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/tcpdump/Makefile b/usr.sbin/tcpdump/Makefile
new file mode 100644
index 0000000..c549530
--- /dev/null
+++ b/usr.sbin/tcpdump/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 0.1 (RGrimes) 4/4/93
+# $FreeBSD$
+
+SUBDIR= tcpdump tcpslice
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/tcpdump/Makefile.inc b/usr.sbin/tcpdump/Makefile.inc
new file mode 100644
index 0000000..1e4d46f
--- /dev/null
+++ b/usr.sbin/tcpdump/Makefile.inc
@@ -0,0 +1,4 @@
+# @(#)Makefile.inc 5.1 (Berkeley) 5/11/90
+# $FreeBSD$
+
+BINDIR?= /usr/sbin
diff --git a/usr.sbin/tcpdump/tcpdump/Makefile b/usr.sbin/tcpdump/tcpdump/Makefile
new file mode 100644
index 0000000..bb6968a
--- /dev/null
+++ b/usr.sbin/tcpdump/tcpdump/Makefile
@@ -0,0 +1,55 @@
+# $FreeBSD$
+
+TCPDUMP_DISTDIR?= ${.CURDIR}/../../../contrib/tcpdump
+.PATH: ${TCPDUMP_DISTDIR}
+
+PROG= tcpdump
+SRCS= addrtoname.c gmt2local.c machdep.c parsenfsfh.c \
+ print-802_11.c print-ah.c print-arcnet.c print-arp.c \
+ print-ascii.c print-atalk.c print-atm.c print-bgp.c \
+ print-bootp.c print-beep.c print-cdp.c print-chdlc.c \
+ print-cip.c print-cnfp.c print-decnet.c print-domain.c \
+ print-dvmrp.c print-egp.c print-esp.c print-ether.c \
+ print-fddi.c print-gre.c print-hsrp.c print-icmp.c \
+ print-igmp.c print-igrp.c print-ip.c print-ipcomp.c \
+ print-ipx.c print-isakmp.c print-isoclns.c print-krb.c \
+ print-l2tp.c print-lane.c print-lcp.c print-llc.c print-lwres.c \
+ print-msdp.c print-mobile.c print-mpls.c print-nfs.c \
+ print-ntp.c print-null.c print-ospf.c print-pim.c \
+ print-ppp.c print-pppoe.c print-pptp.c print-radius.c \
+ print-raw.c print-rip.c print-rx.c print-sctp.c \
+ print-sl.c print-sll.c print-smb.c print-snmp.c \
+ print-stp.c print-sunrpc.c print-tcp.c \
+ print-telnet.c print-tftp.c print-timed.c print-token.c \
+ print-udp.c print-vjc.c print-vrrp.c print-wb.c print-zephyr.c \
+ setsignal.c smbutil.c tcpdump.c util.c version.c
+CLEANFILES+= version.c
+
+CFLAGS+= -I${.CURDIR} -I${TCPDUMP_DISTDIR}
+CFLAGS+= -DHAVE_CONFIG_H
+
+.if !defined(NOINET6)
+SRCS+= print-ip6.c print-ip6opts.c print-ripng.c print-icmp6.c \
+ print-frag6.c print-rt6.c print-ospf6.c print-dhcp6.c
+CFLAGS+= -DINET6
+.endif
+.if ${MACHINE_ARCH} != "i386"
+CFLAGS+= -DLBL_ALIGN
+.endif
+
+DPADD= ${LIBL} ${LIBPCAP}
+LDADD= -ll -lpcap
+.if exists(../../../secure) && !defined(NOCRYPT) && !defined(NOSECURE) && \
+ !defined(NO_OPENSSL) && !defined(RELEASE_CRUNCH)
+DISTRIBUTION=crypto
+DPADD+= ${LIBCRYPTO}
+LDADD+= -lcrypto
+CFLAGS+= -I${DESTDIR}/usr/include/openssl -DHAVE_LIBCRYPTO -DHAVE_RC5_H -DHAVE_CAST_H
+.endif
+
+version.c: ${TCPDUMP_DISTDIR}/VERSION
+ rm -f version.c ; \
+ sed 's/.*/char version[] = "&";/' ${TCPDUMP_DISTDIR}/VERSION \
+ > version.c
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/tcpdump/tcpdump/config.h b/usr.sbin/tcpdump/tcpdump/config.h
new file mode 100644
index 0000000..b84ee2c
--- /dev/null
+++ b/usr.sbin/tcpdump/tcpdump/config.h
@@ -0,0 +1,201 @@
+/* $FreeBSD$ */
+/* This is an edited copy of the config.h generated by configure. */
+
+/* config.h. Generated automatically by configure. */
+/* config.h.in. Generated automatically from configure.in by autoheader. */
+/* "generated automatically" means DO NOT MAKE CHANGES TO config.h.in --
+ * make them to acconfig.h and rerun autoheader */
+
+/* Define to empty if the keyword does not work. */
+/* #undef const */
+
+/* Define as __inline if that's what the C compiler calls it. */
+/* #undef inline */
+
+/* Define as the return type of signal handlers (int or void). */
+#define RETSIGTYPE void
+
+/* Define if you can safely include both <sys/time.h> and <time.h>. */
+#define TIME_WITH_SYS_TIME 1
+
+/* Define if you have SSLeay 0.9.0b with the buggy cast128. */
+/* #undef HAVE_BUGGY_CAST128 */
+
+/* Define if you enable IPv6 support */
+/* Handled by Makefile, to allow NOINET6 */
+/* #define INET6 1 */
+
+/* Define if you enable support for the libsmi. */
+/* #undef LIBSMI */
+
+/* define if you have struct __res_state_ext */
+/* #undef HAVE_RES_STATE_EXT */
+
+/* define if your struct __res_state has the nsort member */
+/* #undef HAVE_NEW_RES_STATE */
+
+/* define if you have the addrinfo function. */
+#define HAVE_ADDRINFO 1
+
+/* define if you need to include missing/addrinfoh.h. */
+/* #undef NEED_ADDRINFO_H */
+
+/* define ifyou have the h_errno variable. */
+#define HAVE_H_ERRNO 1
+
+/* define if IN6ADDRSZ is defined (XXX not used!) */
+#define HAVE_IN6ADDRSZ 1
+
+/* define if INADDRSZ is defined (XXX not used!) */
+#define HAVE_INADDRSZ 1
+
+/* define if this is a development version, to use additional prototypes. */
+/* #undef HAVE_OS_PROTO_H */
+
+/* define if RES_USE_INET6 is defined */
+#define HAVE_RES_USE_INET6 1
+
+/* define if struct sockaddr has the sa_len member */
+#define HAVE_SOCKADDR_SA_LEN 1
+
+/* define if you have struct sockaddr_storage */
+#define HAVE_SOCKADDR_STORAGE 1
+
+/* define if you have ether_ntohost() and it works */
+#define USE_ETHER_NTOHOST 1
+
+/* define if unaligned memory accesses fail */
+/* #undef LBL_ALIGN */
+
+/* The successful return value from signal (?)XXX */
+#define RETSIGVAL
+
+/* Define this on IRIX */
+/* #undef _BSD_SIGNALS */
+
+/* For HP/UX ANSI compiler? */
+/* #undef _HPUX_SOURCE */
+
+/* AIX hack. */
+/* #undef _SUN */
+
+/* Workaround for missing sized types */
+/* XXX this should move to the more standard uint*_t */
+/* #undef int16_t */
+/* #undef int32_t */
+/* #undef u_int16_t */
+/* #undef u_int32_t */
+/* #undef u_int8_t */
+
+/* Whether or not to include the possibly-buggy SMB printer */
+#define TCPDUMP_DO_SMB 1
+
+/* Long story short: aclocal.m4 depends on autoconf 2.13
+ * implementation details wrt "const"; newer versions
+ * have different implementation details so for now we
+ * put "const" here. This may cause duplicate definitions
+ * in config.h but that should be OK since they're the same.
+ */
+/* #undef const */
+
+/* The number of bytes in a char. */
+#define SIZEOF_CHAR 1
+
+/* The number of bytes in a int. */
+#define SIZEOF_INT 4
+
+/* The number of bytes in a long. */
+#define SIZEOF_LONG 4
+
+/* The number of bytes in a short. */
+#define SIZEOF_SHORT 2
+
+/* Define if you have the bpf_dump function. */
+#define HAVE_BPF_DUMP 1
+
+/* Define if you have the ether_ntohost function. */
+#define HAVE_ETHER_NTOHOST 1
+
+/* Define if you have the getaddrinfo function. */
+#define HAVE_GETADDRINFO 1
+
+/* Define if you have the getnameinfo function. */
+#define HAVE_GETNAMEINFO 1
+
+/* Define if you have the inet_aton function. */
+/* #undef HAVE_INET_ATON */
+
+/* Define if you have the inet_ntop function. */
+/* #undef HAVE_INET_NTOP */
+
+/* Define if you have the inet_pton function. */
+/* #undef HAVE_INET_PTON */
+
+/* Define if you have the pfopen function. */
+/* #undef HAVE_PFOPEN */
+
+/* Define if you have the setlinebuf function. */
+#define HAVE_SETLINEBUF 1
+
+/* Define if you have the sigaction function. */
+#define HAVE_SIGACTION 1
+
+/* Define if you have the sigset function. */
+/* #undef HAVE_SIGSET */
+
+/* Define if you have the snprintf function. */
+#define HAVE_SNPRINTF 1
+
+/* Define if you have the strcasecmp function. */
+#define HAVE_STRCASECMP 1
+
+/* Define if you have the strdup function. */
+#define HAVE_STRDUP 1
+
+/* Define if you have the strlcat function. */
+#define HAVE_STRLCAT 1
+
+/* Define if you have the strlcpy function. */
+#define HAVE_STRLCPY 1
+
+/* Define if you have the vfprintf function. */
+#define HAVE_VFPRINTF 1
+
+/* Define if you have the vsnprintf function. */
+#define HAVE_VSNPRINTF 1
+
+/* Define if you have the <cast.h> header file. */
+/* Handled by Makefile, to allow optional crypto */
+/* #define HAVE_CAST_H 1 */
+
+/* Define if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define if you have the <netinet/if_ether.h> header file. */
+#define HAVE_NETINET_IF_ETHER_H 1
+
+/* Define if you have the <rc5.h> header file. */
+/* #undef HAVE_RC5_H */
+
+/* Define if you have the <rpc/rpcent.h> header file. */
+#define HAVE_RPC_RPCENT_H 1
+
+/* Define if you have the <smi.h> header file. */
+/* #undef HAVE_SMI_H */
+
+/* Define if you have the crypto library (-lcrypto). */
+/* Handled by Makefile, to allow optional crypto */
+/* #define HAVE_LIBCRYPTO 1 */
+
+/* Define if you have the dnet library (-ldnet). */
+/* #undef HAVE_LIBDNET */
+
+/* Define if you have the rpc library (-lrpc). */
+/* #undef HAVE_LIBRPC */
+
+/* Define if you have the smi library (-lsmi). */
+/* #undef HAVE_LIBSMI */
+
+/* define if your compiler has __attribute__ */
+#define HAVE___ATTRIBUTE__ 1
+
diff --git a/usr.sbin/tcpdump/tcpslice/Makefile b/usr.sbin/tcpdump/tcpslice/Makefile
new file mode 100644
index 0000000..2c44459
--- /dev/null
+++ b/usr.sbin/tcpdump/tcpslice/Makefile
@@ -0,0 +1,23 @@
+# @(#)Makefile 0.1 (RWGrimes) 3/24/93
+# $FreeBSD$
+
+TCPDUMP_DISTDIR?= ${.CURDIR}/../../../contrib/tcpdump
+
+PROG= tcpslice
+SRCS= gwtm2secs.c search.c tcpslice.c util.c version.c version.h
+CLEANFILES= version.c version.h
+
+CFLAGS+= -I.
+
+DPADD= ${LIBPCAP}
+LDADD= -lpcap
+
+.ORDER: version.c version.h
+version.c version.h: ${TCPDUMP_DISTDIR}/VERSION
+ rm -f version.c ; \
+ sed 's/.*/char version[] = "&";/' ${TCPDUMP_DISTDIR}/VERSION > version.c
+ set `sed 's/\([0-9]*\)\.\([0-9]*\).*/\1 \2/' ${TCPDUMP_DISTDIR}/VERSION` ; \
+ { echo '#define VERSION_MAJOR' $$1 ; \
+ echo '#define VERSION_MINOR' $$2 ; } > version.h
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/tcpdump/tcpslice/gwtm2secs.c b/usr.sbin/tcpdump/tcpslice/gwtm2secs.c
new file mode 100644
index 0000000..249454e
--- /dev/null
+++ b/usr.sbin/tcpdump/tcpslice/gwtm2secs.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+#if !defined(lint) && !defined(__GNUC__)
+static char rcsid[] =
+ "@(#)$FreeBSD$ (LBL)";
+#endif
+
+/*
+ * gwtm2secs.c - convert "tm" structs for Greenwich time to Unix timestamp
+ */
+
+#include "tcpslice.h"
+
+static int days_in_month[] =
+ /* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */
+ { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+
+#define IS_LEAP_YEAR(year) \
+ (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))
+
+time_t gwtm2secs( struct tm *tm )
+ {
+ int i, days, year;
+
+ year = tm->tm_year;
+
+ /* Allow for year being specified with either 2 digits or 4 digits.
+ * 2-digit years are either 19xx or 20xx - a simple heuristic
+ * distinguishes them, since we can't represent any time < 1970.
+ */
+ if ( year < 100 )
+ if ( year >= 70 )
+ year += 1900;
+ else
+ year += 2000;
+
+ days = 0;
+ for ( i = 1970; i < year; ++i )
+ {
+ days += 365;
+ if ( IS_LEAP_YEAR(i) )
+ ++days;
+ }
+
+ for ( i = 0; i < tm->tm_mon; ++i )
+ days += days_in_month[i];
+
+ if ( IS_LEAP_YEAR(year) && tm->tm_mon > 1 ) /* 1 is February */
+ ++days;
+
+ days += tm->tm_mday - 1; /* -1 since days are numbered starting at 1 */
+
+ return days * 86400 + tm->tm_hour * 3600 + tm->tm_min * 60 + tm->tm_sec;
+ }
diff --git a/usr.sbin/tcpdump/tcpslice/search.c b/usr.sbin/tcpdump/tcpslice/search.c
new file mode 100644
index 0000000..a460ffa
--- /dev/null
+++ b/usr.sbin/tcpdump/tcpslice/search.c
@@ -0,0 +1,566 @@
+/*
+ * Copyright (c) 1990, 1991, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+#if !defined(lint) && !defined(__GNUC__)
+static char rcsid[] =
+ "@(#)$FreeBSD$ (LBL)";
+#endif
+
+/*
+ * search.c - supports fast searching through tcpdump files for timestamps
+ */
+
+#include "tcpslice.h"
+
+
+/* Maximum number of seconds that we can conceive of a dump file spanning. */
+#define MAX_REASONABLE_FILE_SPAN (3600*24*366) /* one year */
+
+/* Maximum packet length we ever expect to see. */
+#define MAX_REASONABLE_PACKET_LENGTH 65535
+
+/* Size of a packet header in bytes; easier than typing the sizeof() all
+ * the time ...
+ */
+#define PACKET_HDR_LEN (sizeof( struct pcap_pkthdr ))
+
+extern int snaplen;
+
+/* The maximum size of a packet, including its header. */
+#define MAX_PACKET_SIZE (PACKET_HDR_LEN + snaplen)
+
+/* Number of contiguous bytes from a dumpfile in which there's guaranteed
+ * to be enough information to find a "definite" header if one exists
+ * therein. This takes 3 full packets - the first to be just misaligned
+ * (one byte short of a full packet), missing its timestamp; the second
+ * to have the legitimate timestamp; and the third to provide confirmation
+ * that the second is legit, making it a "definite" header. We could
+ * scrimp a bit here since not the entire third packet is required, but
+ * it doesn't seem worth it
+ */
+#define MAX_BYTES_FOR_DEFINITE_HEADER (3 * MAX_PACKET_SIZE)
+
+/* Maximum number of seconds that might reasonably separate two headers. */
+#define MAX_REASONABLE_HDR_SEPARATION (3600 * 24 * 7) /* one week */
+
+/* When searching a file for a packet, if we think we're within this many
+ * bytes of the packet we just search linearly. Since linear searches are
+ * probably much faster than random ones (random ones require searching for
+ * the beginning of the packet, which may be unaligned in memory), we make
+ * this value pretty hefty.
+ */
+#define STRAIGHT_SCAN_THRESHOLD (100 * MAX_PACKET_SIZE)
+
+
+/* Given a header and an acceptable first and last time stamp, returns non-zero
+ * if the header looks reasonable and zero otherwise.
+ */
+static int
+reasonable_header( struct pcap_pkthdr *hdr, long first_time, long last_time )
+ {
+ if ( last_time == 0 )
+ last_time = first_time + MAX_REASONABLE_FILE_SPAN;
+
+ return hdr->ts.tv_sec >= first_time &&
+ hdr->ts.tv_sec <= last_time &&
+ hdr->len > 0 &&
+ hdr->len <= MAX_REASONABLE_PACKET_LENGTH &&
+ hdr->caplen > 0 &&
+ hdr->caplen <= MAX_REASONABLE_PACKET_LENGTH;
+ }
+
+
+#define SWAPLONG(y) \
+((((y)&0xff)<<24) | (((y)&0xff00)<<8) | (((y)&0xff0000)>>8) | (((y)>>24)&0xff))
+#define SWAPSHORT(y) \
+ ( (((y)&0xff)<<8) | (((y)&0xff00)>>8) )
+
+/* Given a buffer, extracts a (properly aligned) packet header from it. */
+
+static void
+extract_header( pcap_t *p, u_char *buf, struct pcap_pkthdr *hdr )
+ {
+ bcopy((char *) buf, (char *) hdr, sizeof(struct pcap_pkthdr));
+
+ if ( pcap_is_swapped( p ) )
+ {
+ hdr->ts.tv_sec = SWAPLONG(hdr->ts.tv_sec);
+ hdr->ts.tv_usec = SWAPLONG(hdr->ts.tv_usec);
+ hdr->len = SWAPLONG(hdr->len);
+ hdr->caplen = SWAPLONG(hdr->caplen);
+ }
+
+ /*
+ * From bpf/libpcap/savefile.c:
+ *
+ * We interchanged the caplen and len fields at version 2.3,
+ * in order to match the bpf header layout. But unfortunately
+ * some files were written with version 2.3 in their headers
+ * but without the interchanged fields.
+ */
+ if ( pcap_minor_version( p ) < 3 ||
+ (pcap_minor_version( p ) == 3 && hdr->caplen > hdr->len) )
+ {
+ int t = hdr->caplen;
+ hdr->caplen = hdr->len;
+ hdr->len = t;
+ }
+ }
+
+
+/* Search a buffer to locate the first header within it. Return values
+ * are HEADER_NONE, HEADER_CLASH, HEADER_PERHAPS, and HEADER_DEFINITELY.
+ * The first indicates that no evidence of a header was found; the second
+ * that two or more possible headers were found, neither more convincing
+ * than the other(s); the third that exactly one "possible" header was
+ * found; and the fourth that exactly one "definite" header was found.
+ *
+ * Headers are detected by looking for positions in the buffer which have
+ * reasonable timestamps and lengths. If there is enough room in the buffer
+ * for another header to follow a candidate header, a check is made for
+ * that following header. If it is present then the header is *definite*
+ * (unless another "perhaps" or "definite" header is found); if not, then
+ * the header is discarded. If there is not enough room in the buffer for
+ * another header then the candidate is *perhaps* (unless another header
+ * is subsequently found). A "tie" between a "definite" header and a
+ * "perhaps" header is resolved in favor of the definite header. Any
+ * other tie leads to HEADER_CLASH.
+ *
+ * The buffer position of the header is returned in hdrpos_addr and
+ * for convenience the corresponding header in return_hdr.
+ *
+ * first_time is the earliest possible acceptable timestamp in the
+ * header. last_time, if non-zero, is the last such timestamp. If
+ * zero, then up to MAX_REASONABLE_FILE_SPAN seconds after first_time
+ * is acceptable.
+ */
+
+#define HEADER_NONE 0
+#define HEADER_CLASH 1
+#define HEADER_PERHAPS 2
+#define HEADER_DEFINITELY 3
+
+static int
+find_header( pcap_t *p, u_char *buf, int buf_len,
+ long first_time, long last_time,
+ u_char **hdrpos_addr, struct pcap_pkthdr *return_hdr )
+ {
+ u_char *bufptr, *bufend, *last_pos_to_try;
+ struct pcap_pkthdr hdr, hdr2;
+ int status = HEADER_NONE;
+ int saw_PERHAPS_clash = 0;
+
+ /* Initially, try each buffer position to see whether it looks like
+ * a valid packet header. We may later restrict the positions we look
+ * at to avoid seeing a sequence of legitimate headers as conflicting
+ * with one another.
+ */
+ bufend = buf + buf_len;
+ last_pos_to_try = bufend - PACKET_HDR_LEN;
+
+ for ( bufptr = buf; bufptr < last_pos_to_try; ++bufptr )
+ {
+ extract_header( p, bufptr, &hdr );
+
+ if ( reasonable_header( &hdr, first_time, last_time ) )
+ {
+ u_char *next_header = bufptr + PACKET_HDR_LEN + hdr.caplen;
+
+ if ( next_header + PACKET_HDR_LEN < bufend )
+ { /* check for another good header */
+ extract_header( p, next_header, &hdr2 );
+
+ if ( reasonable_header( &hdr2, hdr.ts.tv_sec,
+ hdr.ts.tv_sec + MAX_REASONABLE_HDR_SEPARATION ) )
+ { /* a confirmed header */
+ switch ( status )
+ {
+ case HEADER_NONE:
+ case HEADER_PERHAPS:
+ status = HEADER_DEFINITELY;
+ *hdrpos_addr = bufptr;
+ *return_hdr = hdr;
+
+ /* Make sure we don't demote this "definite"
+ * to a "clash" if we stumble across its
+ * successor.
+ */
+ last_pos_to_try = next_header - PACKET_HDR_LEN;
+ break;
+
+ case HEADER_DEFINITELY:
+ return HEADER_CLASH;
+
+ default:
+ error( "bad status in find_header()" );
+ }
+ }
+
+ /* ... else the header is bogus - we've verified that it's
+ * not followed by a reasonable header.
+ */
+ }
+
+ else
+ { /* can't check for another good header */
+ switch ( status )
+ {
+ case HEADER_NONE:
+ status = HEADER_PERHAPS;
+ *hdrpos_addr = bufptr;
+ *return_hdr = hdr;
+ break;
+
+ case HEADER_PERHAPS:
+ /* We don't immediately turn this into a
+ * clash because perhaps we'll later see a
+ * "definite" which will save us ...
+ */
+ saw_PERHAPS_clash = 1;
+ break;
+
+ case HEADER_DEFINITELY:
+ /* Keep the definite in preference to this one. */
+ break;
+
+ default:
+ error( "bad status in find_header()" );
+ }
+ }
+ }
+ }
+
+ if ( status == HEADER_PERHAPS && saw_PERHAPS_clash )
+ status = HEADER_CLASH;
+
+ return status;
+ }
+
+
+/* Positions the sf_readfile stream such that the next sf_read() will
+ * read the final full packet in the file. Returns non-zero if
+ * successful, zero if unsuccessful. If successful, returns the
+ * timestamp of the last packet in last_timestamp.
+ *
+ * Note that this routine is a special case of sf_find_packet(). In
+ * order to use sf_find_packet(), one first must use this routine in
+ * order to give sf_find_packet() an upper bound on the timestamps
+ * present in the dump file.
+ */
+int
+sf_find_end( pcap_t *p, struct timeval *first_timestamp,
+ struct timeval *last_timestamp )
+ {
+ long first_time = first_timestamp->tv_sec;
+ u_int num_bytes;
+ u_char *buf, *bufpos, *bufend;
+ u_char *hdrpos;
+ struct pcap_pkthdr hdr, successor_hdr;
+ int status;
+
+ /* Allow enough room for at least two full (untruncated) packets,
+ * perhaps followed by a truncated packet, so we have a shot at
+ * finding a "definite" header and following its chain to the
+ * end of the file.
+ */
+ num_bytes = MAX_BYTES_FOR_DEFINITE_HEADER;
+ if ( fseek( pcap_file( p ), (long) -num_bytes, 2 ) < 0 )
+ return 0;
+
+ buf = (u_char *)malloc((u_int) num_bytes);
+ if ( ! buf )
+ return 0;
+
+ status = 0;
+ bufpos = buf;
+ bufend = buf + num_bytes;
+
+ if ( fread( (char *) bufpos, num_bytes, 1, pcap_file( p ) ) != 1 )
+ goto done;
+
+ if ( find_header( p, bufpos, num_bytes,
+ first_time, 0L, &hdrpos, &hdr ) != HEADER_DEFINITELY )
+ goto done;
+
+ /* Okay, we have a definite header in our hands. Follow its
+ * chain till we find the last valid packet in the file ...
+ */
+ for ( ; ; )
+ {
+ /* move to the next header position */
+ bufpos = hdrpos + PACKET_HDR_LEN + hdr.caplen;
+
+ /* bufpos now points to a candidate packet, which if valid
+ * should replace the current packet pointed to by hdrpos as
+ * the last valid packet ...
+ */
+ if ( bufpos >= bufend - PACKET_HDR_LEN )
+ /* not enough room for another header */
+ break;
+
+ extract_header( p, bufpos, &successor_hdr );
+
+ first_time = hdr.ts.tv_sec;
+ if ( ! reasonable_header( &successor_hdr, first_time, 0L ) )
+ /* this bodes ill - it means bufpos is perhaps a
+ * bogus packet header after all ...
+ */
+ break;
+
+ /* Note that the following test is for whether the next
+ * packet starts at a position > bufend, *not* for a
+ * position >= bufend. If this is the last packet in the
+ * file and there isn't a subsequent partial packet, then
+ * we expect the first buffer position beyond this packet
+ * to be just beyond the end of the buffer, i.e., at bufend
+ * itself.
+ */
+ if ( bufpos + PACKET_HDR_LEN + successor_hdr.caplen > bufend )
+ /* the packet is truncated */
+ break;
+
+ /* Accept this packet as fully legit. */
+ hdrpos = bufpos;
+ hdr = successor_hdr;
+ }
+
+ /* Success! Last valid packet is at hdrpos. */
+ *last_timestamp = hdr.ts;
+ status = 1;
+
+ /* Seek so that the next read will start at last valid packet. */
+ if ( fseek( pcap_file( p ), (long) -(bufend - hdrpos), 2 ) < 0 )
+ error( "final fseek() failed in sf_find_end()" );
+
+ done:
+ free( (char *) buf );
+
+ return status;
+ }
+
+
+/* Takes two timeval's and returns the difference, tv2 - tv1, as a double. */
+
+static double
+timeval_diff( struct timeval *tv1, struct timeval *tv2 )
+ {
+ double result = (tv2->tv_sec - tv1->tv_sec);
+ result += (tv2->tv_usec - tv1->tv_usec) / 1000000.0;
+
+ return result;
+ }
+
+
+/* Returns true if timestamp t1 is chronologically less than timestamp t2. */
+
+int
+sf_timestamp_less_than( struct timeval *t1, struct timeval *t2 )
+ {
+ return t1->tv_sec < t2->tv_sec ||
+ (t1->tv_sec == t2->tv_sec &&
+ t1->tv_usec < t2->tv_usec);
+ }
+
+
+/* Given two timestamps on either side of desired_time and their positions,
+ * returns the interpolated position of the desired_time packet. Returns a
+ * negative value if the desired_time is outside the given range.
+ */
+
+static long
+interpolated_position( struct timeval *min_time, long min_pos,
+ struct timeval *max_time, long max_pos,
+ struct timeval *desired_time )
+ {
+ double full_span = timeval_diff( max_time, min_time );
+ double desired_span = timeval_diff( desired_time, min_time );
+ long full_span_pos = max_pos - min_pos;
+ double fractional_offset = desired_span / full_span;
+
+ if ( fractional_offset < 0.0 || fractional_offset > 1.0 )
+ return -1;
+
+ return min_pos + (long) (fractional_offset * (double) full_span_pos);
+ }
+
+
+/* Reads packets linearly until one with a time >= the given desired time
+ * is found; positions the dump file so that the next read will start
+ * at the given packet. Returns non-zero on success, 0 if an EOF was
+ * first encountered.
+ */
+
+static int
+read_up_to( pcap_t *p, struct timeval *desired_time )
+ {
+ struct pcap_pkthdr hdr;
+ const u_char *buf;
+ long pos;
+ int status;
+
+ for ( ; ; )
+ {
+ struct timeval *timestamp;
+
+ pos = ftell( pcap_file( p ) );
+ buf = pcap_next( p, &hdr );
+
+ if ( buf == 0 )
+ {
+ if ( feof( pcap_file( p ) ) )
+ {
+ status = 0;
+ clearerr( pcap_file( p ) );
+ break;
+ }
+
+ error( "bad status in read_up_to()" );
+ }
+
+ timestamp = &hdr.ts;
+
+ if ( ! sf_timestamp_less_than( timestamp, desired_time ) )
+ {
+ status = 1;
+ break;
+ }
+ }
+
+ if ( fseek( pcap_file( p ), pos, 0 ) < 0 )
+ error( "fseek() failed in read_up_to()" );
+
+ return (status);
+ }
+
+
+/* Positions the sf_readfile stream so that the next sf_read() will
+ * return the first packet with a time greater than or equal to
+ * desired_time. desired_time must be greater than min_time and less
+ * than max_time, which should correspond to actual packets in the
+ * file. min_pos is the file position (byte offset) corresponding to
+ * the min_time packet and max_pos is the same for the max_time packet.
+ *
+ * Returns non-zero on success, 0 if the given position is beyond max_pos.
+ *
+ * NOTE: when calling this routine, the sf_readfile stream *must* be
+ * already aligned so that the next call to sf_next_packet() will yield
+ * a valid packet.
+ */
+
+int
+sf_find_packet( pcap_t *p,
+ struct timeval *min_time, long min_pos,
+ struct timeval *max_time, long max_pos,
+ struct timeval *desired_time )
+ {
+ int status = 1;
+ struct timeval min_time_copy, max_time_copy;
+ u_int num_bytes = MAX_BYTES_FOR_DEFINITE_HEADER;
+ int num_bytes_read;
+ long desired_pos, present_pos;
+ u_char *buf, *hdrpos;
+ struct pcap_pkthdr hdr;
+
+ buf = (u_char *) malloc( num_bytes );
+ if ( ! buf )
+ error( "malloc() failured in sf_find_packet()" );
+
+ min_time_copy = *min_time;
+ min_time = &min_time_copy;
+
+ max_time_copy = *max_time;
+ max_time = &max_time_copy;
+
+ for ( ; ; ) /* loop until positioned correctly */
+ {
+ desired_pos =
+ interpolated_position( min_time, min_pos,
+ max_time, max_pos,
+ desired_time );
+
+ if ( desired_pos < 0 )
+ {
+ status = 0;
+ break;
+ }
+
+ present_pos = ftell( pcap_file( p ) );
+
+ if ( present_pos <= desired_pos &&
+ desired_pos - present_pos < STRAIGHT_SCAN_THRESHOLD )
+ { /* we're close enough to just blindly read ahead */
+ status = read_up_to( p, desired_time );
+ break;
+ }
+
+ /* Undershoot the target a little bit - it's much easier to
+ * then scan straight forward than to try to read backwards ...
+ */
+ desired_pos -= STRAIGHT_SCAN_THRESHOLD / 2;
+ if ( desired_pos < min_pos )
+ desired_pos = min_pos;
+
+ if ( fseek( pcap_file( p ), desired_pos, 0 ) < 0 )
+ error( "fseek() failed in sf_find_packet()" );
+
+ num_bytes_read =
+ fread( (char *) buf, 1, num_bytes, pcap_file( p ) );
+
+ if ( num_bytes_read == 0 )
+ /* This shouldn't ever happen because we try to
+ * undershoot, unless the dump file has only a
+ * couple packets in it ...
+ */
+ error( "fread() failed in sf_find_packet()" );
+
+ if ( find_header( p, buf, num_bytes, min_time->tv_sec,
+ max_time->tv_sec, &hdrpos, &hdr ) !=
+ HEADER_DEFINITELY )
+ error( "can't find header at position %ld in dump file",
+ desired_pos );
+
+ /* Correct desired_pos to reflect beginning of packet. */
+ desired_pos += (hdrpos - buf);
+
+ /* Seek to the beginning of the header. */
+ if ( fseek( pcap_file( p ), desired_pos, 0 ) < 0 )
+ error( "fseek() failed in sf_find_packet()" );
+
+ if ( sf_timestamp_less_than( &hdr.ts, desired_time ) )
+ { /* too early in the file */
+ *min_time = hdr.ts;
+ min_pos = desired_pos;
+ }
+
+ else if ( sf_timestamp_less_than( desired_time, &hdr.ts ) )
+ { /* too late in the file */
+ *max_time = hdr.ts;
+ max_pos = desired_pos;
+ }
+
+ else
+ /* got it! */
+ break;
+ }
+
+ free( (char *) buf );
+
+ return status;
+ }
diff --git a/usr.sbin/tcpdump/tcpslice/tcpslice.1 b/usr.sbin/tcpdump/tcpslice/tcpslice.1
new file mode 100644
index 0000000..d804cf6
--- /dev/null
+++ b/usr.sbin/tcpdump/tcpslice/tcpslice.1
@@ -0,0 +1,273 @@
+.\" Copyright (c) 1988-1990 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that: (1) source code distributions
+.\" retain the above copyright notice and this paragraph in its entirety, (2)
+.\" distributions including binary code include the above copyright notice and
+.\" this paragraph in its entirety in the documentation or other materials
+.\" provided with the distribution, and (3) all advertising materials mentioning
+.\" features or use of this software display the following acknowledgement:
+.\" ``This product includes software developed by the University of California,
+.\" Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+.\" the University nor the names of its contributors may be used to endorse
+.\" or promote products derived from this software without specific prior
+.\" written permission.
+.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+.\" WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd October 14, 1991
+.Dt TCPSLICE 1
+.Os
+.Sh NAME
+.Nm tcpslice
+.Nd extract pieces of and/or glue together tcpdump files
+.Sh SYNOPSIS
+.Nm
+.Op Fl dRrt
+.Op Fl w Ar file
+.Op Ar start-time Op end-time
+.Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility extracts portions of packet-trace files generated using
+.Xr tcpdump 1 Ns 's
+.Fl w
+flag.
+It can also be used to glue together several such files, as discussed
+below.
+.Pp
+The basic operation of
+.Nm
+is to copy to
+.Pa stdout
+all packets from its input file(s) whose timestamps fall
+within a given range. The starting and ending times of the range
+may be specified on the command line. All ranges are inclusive.
+The starting time defaults
+to the time of the first packet in the first input file; we call
+this the
+.Em first time .
+The ending time defaults to ten years after the starting time.
+Thus, the command
+.Nm
+.Ar trace-file
+simply copies
+.Ar trace-file
+to
+.Pa stdout
+(assuming the file does not include more than
+ten years' worth of data).
+.Pp
+There are a number of ways to specify times. The first is using
+Unix timestamps of the form
+.Em sssssssss.uuuuuu
+(this is the format specified by
+.Xr tcpdump 1 Ns 's
+.Fl tt
+flag).
+For example,
+.Em 654321098.7654
+specifies 38 seconds and 765,400 microseconds
+after 8:51PM PDT, Sept. 25, 1990.
+.Pp
+All examples in this manual are given
+for PDT times, but when displaying times and interpreting times symbolically
+as discussed below,
+.Nm
+uses the local timezone, regardless of the timezone in which the
+.Xr tcpdump 1
+file was generated. The daylight-savings setting used is that which is
+appropriate for the local timezone at the date in question. For example,
+times associated with summer months will usually include daylight-savings
+effects, and those with winter months will not.
+.Pp
+Times may also be specified relative
+to either the
+.Em first time
+(when specifying a starting time)
+or the starting time (when specifying an ending time)
+by preceding a numeric value in seconds with a `+'.
+For example, a starting time of
+.Em +200
+indicates 200 seconds after the
+.Em first time ,
+and the two arguments
+.Em +200 +300
+indicate from 200 seconds after the
+.Em first time
+through 500 seconds after the
+.Em first time .
+.Pp
+Times may also be specified in terms of years (y), months (m), days (d),
+hours (h), minutes (m), seconds (s), and microseconds(u). For example,
+the Unix timestamp 654321098.7654 discussed above could also be expressed
+as
+.Em 90y9m25d20h51m38s765400u .
+.Pp
+When specifying times using this style, fields that are omitted default
+as follows. If the omitted field is a unit
+.Em greater
+than that of the first specified field, then its value defaults to
+the corresponding value taken from either
+.Em first time
+(if the starting time is being specified) or the starting time
+(if the ending time is being specified).
+If the omitted field is a unit
+.Em less
+than that of the first specified field, then it defaults to zero.
+For example, suppose that the input file has a
+.Em first time
+of the Unix timestamp mentioned above, i.e., 38 seconds and 765,400 microseconds
+after 8:51PM PDT, Sept. 25, 1990. To specify 9:36PM PDT (exactly) on the
+same date we could use
+.Em 21h36m .
+To specify a range from 9:36PM PDT through 1:54AM PDT the next day we
+could use
+.Em 21h36m 26d1h54m .
+.Pp
+Relative times can also be specified when using the
+.Em ymdhmsu
+format. Omitted fields then default to 0 if the unit of the field is
+.Em greater
+than that of the first specified field, and to the corresponding value
+taken from either the
+.Em first time
+or the starting time if the omitted field's unit is
+.Em less
+than that of the first specified field. Given a
+.Em first time
+of the Unix timestamp mentioned above,
+.Em 22h +1h10m
+specifies a range from 10:00PM PDT on that date through 11:10PM PDT, and
+.Em +1h +1h10m
+specifies a range from 38.7654 seconds after 9:51PM PDT through 38.7654
+seconds after 11:01PM PDT. The first hour of the file could be extracted
+using
+.Em +0 +1h .
+.Pp
+Note that with the
+.Em ymdhmsu
+format there is an ambiguity between using
+.Em m
+for `month' or for `minute'. The ambiguity is resolved as follows: if an
+.Em m
+field is followed by a
+.Em d
+field then it is interpreted as specifying months; otherwise it
+specifies minutes.
+.Pp
+If more than one input file is specified then
+.Nm
+first copies packets lying in the given range from the first file; it
+then increases the starting time of the range to lie just beyond the
+timestamp of the last packet in the first file, repeats the process
+with the second file, and so on. Thus files with interleaved packets
+are
+.Em not
+merged. For a given file, only packets that are newer than any in the
+preceding files will be considered. This mechanism avoids any possibility
+of a packet occurring more than once in the output.
+.Sh OPTIONS
+If any of
+.Fl R ,
+.Fl r
+or
+.Fl t
+are specified then
+.Nm
+reports the timestamps of the first and last packets in each input file
+and exits. Only one of these three options may be specified.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl d
+Dump the start and end times specified by the given range and
+exit. This option is useful for checking that the given range actually
+specifies the times you think it does. If one of
+.Fl R ,
+.Fl r
+or
+.Fl t
+has been specified then the times are dumped in the corresponding
+format; otherwise, raw format
+.Pq Fl R
+is used.
+.It Fl R
+Dump the timestamps of the first and last packets in each input file
+as raw timestamps (i.e., in the form
+.Em sssssssss.uuuuuu ) .
+.It Fl r
+Same as
+.Fl R
+except the timestamps are dumped in human-readable format, similar
+to that used by
+.Xr date 1 .
+.It Fl t
+Same as
+.Fl R
+except the timestamps are dumped in
+.Nm
+format, i.e., in the
+.Em ymdhmsu
+format discussed above.
+.It Fl w Ar file
+Direct the output to
+.Ar file
+rather than
+.Pa stdout .
+.El
+.Sh SEE ALSO
+.Xr tcpdump 1
+.Sh AUTHORS
+.An Vern Paxson Aq vern@ee.lbl.gov ,
+of Lawrence Berkeley Laboratory, University of California, Berkeley, CA.
+.Sh BUGS
+An input filename that beings with a digit or a `+' can be confused
+with a start/end time. Such filenames can be specified with a
+leading `./'; for example, specify the file `04Jul76.trace' as
+`./04Jul76.trace'.
+.Pp
+The
+.Nm
+utility cannot read its input from
+.Pa stdin ,
+since it uses random-access
+to rummage through its input files.
+.Pp
+The
+.Nm
+utility refuses to write to its output if it is a terminal
+(as indicated by
+.Xr isatty 3 ) .
+This is not a bug but a feature,
+to prevent it from spraying binary data to the user's terminal.
+Note that this means you must either redirect
+.Pa stdout
+or specify an
+output file via
+.Fl w .
+.Pp
+The
+.Nm
+utility will not work properly on
+.Xr tcpdump 1
+files spanning more than one year;
+with files containing portions of packets whose original length was
+more than 65,535 bytes; nor with files containing fewer than three packets.
+Such files result in
+the error message: `couldn't find final packet in file'. These problems
+are due to the interpolation scheme used by
+.Nm
+to greatly speed up its processing when dealing with large trace files.
+Note that
+.Nm
+can efficiently extract slices from the middle of trace files of any
+size, and can also work with truncated trace files (i.e., the final packet
+in the file is only partially present, typically due to
+.Xr tcpdump 1
+being ungracefully killed).
diff --git a/usr.sbin/tcpdump/tcpslice/tcpslice.c b/usr.sbin/tcpdump/tcpslice/tcpslice.c
new file mode 100644
index 0000000..adca2f5
--- /dev/null
+++ b/usr.sbin/tcpdump/tcpslice/tcpslice.c
@@ -0,0 +1,626 @@
+/*
+ * Copyright (c) 1987-1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1987-1990\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/*
+ * tcpslice - extract pieces of and/or glue together tcpdump files
+ */
+
+#include <err.h>
+#include "tcpslice.h"
+
+int tflag = 0; /* global that util routines are sensitive to */
+int fddipad; /* XXX: libpcap needs this global */
+
+/* Style in which to print timestamps; RAW is "secs.usecs"; READABLE is
+ * ala the Unix "date" tool; and PARSEABLE is tcpslice's custom format,
+ * designed to be easy to parse. The default is RAW.
+ */
+enum stamp_styles { TIMESTAMP_RAW, TIMESTAMP_READABLE, TIMESTAMP_PARSEABLE };
+enum stamp_styles timestamp_style = TIMESTAMP_RAW;
+
+#ifndef __FreeBSD__
+extern int getopt( int argc, char **argv, char *optstring );
+#endif
+
+int is_timestamp( char *str );
+long local_time_zone(long timestamp);
+struct timeval parse_time(char *time_string, struct timeval base_time);
+void fill_tm(char *time_string, int is_delta, struct tm *t, time_t *usecs_addr);
+void get_file_range( char filename[], pcap_t **p,
+ struct timeval *first_time, struct timeval *last_time );
+struct timeval first_packet_time(char filename[], pcap_t **p_addr);
+void extract_slice(char filename[], char write_file_name[],
+ struct timeval *start_time, struct timeval *stop_time);
+char *timestamp_to_string(struct timeval *timestamp);
+void dump_times(pcap_t **p, char filename[]);
+static void usage(void);
+
+
+pcap_dumper_t *dumper = 0;
+
+int
+main(int argc, char **argv)
+{
+ int op;
+ int dump_flag = 0;
+ int report_times = 0;
+ char *start_time_string = 0;
+ char *stop_time_string = 0;
+ char *write_file_name = "-"; /* default is stdout */
+ struct timeval first_time, start_time, stop_time;
+ pcap_t *pcap;
+
+ opterr = 0;
+ while ((op = getopt(argc, argv, "dRrtw:")) != -1)
+ switch (op) {
+
+ case 'd':
+ dump_flag = 1;
+ break;
+
+ case 'R':
+ ++report_times;
+ timestamp_style = TIMESTAMP_RAW;
+ break;
+
+ case 'r':
+ ++report_times;
+ timestamp_style = TIMESTAMP_READABLE;
+ break;
+
+ case 't':
+ ++report_times;
+ timestamp_style = TIMESTAMP_PARSEABLE;
+ break;
+
+ case 'w':
+ write_file_name = optarg;
+ break;
+
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+
+ if ( report_times > 1 )
+ error( "only one of -R, -r, or -t can be specified" );
+
+
+ if (optind < argc)
+ /* See if the next argument looks like a possible
+ * start time, and if so assume it is one.
+ */
+ if (isdigit(argv[optind][0]) || argv[optind][0] == '+')
+ start_time_string = argv[optind++];
+
+ if (optind < argc)
+ if (isdigit(argv[optind][0]) || argv[optind][0] == '+')
+ stop_time_string = argv[optind++];
+
+
+ if (optind >= argc)
+ error("at least one input file must be given");
+
+
+ first_time = first_packet_time(argv[optind], &pcap);
+ pcap_close(pcap);
+
+
+ if (start_time_string)
+ start_time = parse_time(start_time_string, first_time);
+ else
+ start_time = first_time;
+
+ if (stop_time_string)
+ stop_time = parse_time(stop_time_string, start_time);
+
+ else
+ {
+ stop_time = start_time;
+ stop_time.tv_sec += 86400*3660; /* + 10 years; "forever" */
+ }
+
+
+ if (report_times) {
+ for (; optind < argc; ++optind)
+ dump_times(&pcap, argv[optind]);
+ }
+
+ if (dump_flag) {
+ printf( "start\t%s\nstop\t%s\n",
+ timestamp_to_string( &start_time ),
+ timestamp_to_string( &stop_time ) );
+ }
+
+ if (! report_times && ! dump_flag) {
+ if ( ! strcmp( write_file_name, "-" ) &&
+ isatty( fileno(stdout) ) )
+ error("stdout is a terminal; redirect or use -w");
+
+ for (; optind < argc; ++optind)
+ extract_slice(argv[optind], write_file_name,
+ &start_time, &stop_time);
+ }
+
+ return 0;
+}
+
+
+/* Returns non-zero if a string matches the format for a timestamp,
+ * 0 otherwise.
+ */
+int is_timestamp( char *str )
+ {
+ while ( isdigit(*str) || *str == '.' )
+ ++str;
+
+ return *str == '\0';
+ }
+
+
+/* Return the correction in seconds for the local time zone with respect
+ * to Greenwich time.
+ */
+long local_time_zone(long timestamp)
+{
+ struct timeval now;
+ struct timezone tz;
+ long localzone;
+ time_t t = _long_to_time(timestamp);
+
+ if (gettimeofday(&now, &tz) < 0)
+ err(1, "gettimeofday");
+ localzone = tz.tz_minuteswest * -60;
+
+ if (localtime(&t)->tm_isdst)
+ localzone += 3600;
+
+ return localzone;
+}
+
+/* Given a string specifying a time (or a time offset) and a "base time"
+ * from which to compute offsets and fill in defaults, returns a timeval
+ * containing the specified time.
+ */
+
+struct timeval
+parse_time(char *time_string, struct timeval base_time)
+{
+ time_t tt = _long_to_time(base_time.tv_sec);
+ struct tm *bt = localtime(&tt);
+ struct tm t;
+ struct timeval result;
+ time_t usecs = 0;
+ int is_delta = (time_string[0] == '+');
+
+ if ( is_delta )
+ ++time_string; /* skip over '+' sign */
+
+ if ( is_timestamp( time_string ) )
+ { /* interpret as a raw timestamp or timestamp offset */
+ char *time_ptr;
+
+ result.tv_sec = atoi( time_string );
+ time_ptr = strchr( time_string, '.' );
+
+ if ( time_ptr )
+ { /* microseconds are specified, too */
+ int num_digits = strlen( time_ptr + 1 );
+ result.tv_usec = atoi( time_ptr + 1 );
+
+ /* turn 123.456 into 123 seconds plus 456000 usec */
+ while ( num_digits++ < 6 )
+ result.tv_usec *= 10;
+ }
+
+ else
+ result.tv_usec = 0;
+
+ if ( is_delta )
+ {
+ result.tv_sec += base_time.tv_sec;
+ result.tv_usec += base_time.tv_usec;
+
+ if ( result.tv_usec >= 1000000 )
+ {
+ result.tv_usec -= 1000000;
+ ++result.tv_sec;
+ }
+ }
+
+ return result;
+ }
+
+ if (is_delta) {
+ t = *bt;
+ usecs = base_time.tv_usec;
+ } else {
+ /* Zero struct (easy way around lack of tm_gmtoff/tm_zone
+ * under older systems) */
+ bzero((char *)&t, sizeof(t));
+
+ /* Set values to "not set" flag so we can later identify
+ * and default them.
+ */
+ t.tm_sec = t.tm_min = t.tm_hour = t.tm_mday = t.tm_mon =
+ t.tm_year = -1;
+ }
+
+ fill_tm(time_string, is_delta, &t, &usecs);
+
+ /* Now until we reach a field that was specified, fill in the
+ * missing fields from the base time.
+ */
+#define CHECK_FIELD(field_name) \
+ if (t.field_name < 0) \
+ t.field_name = bt->field_name; \
+ else \
+ break
+
+ do { /* bogus do-while loop so "break" in CHECK_FIELD will work */
+ CHECK_FIELD(tm_year);
+ CHECK_FIELD(tm_mon);
+ CHECK_FIELD(tm_mday);
+ CHECK_FIELD(tm_hour);
+ CHECK_FIELD(tm_min);
+ CHECK_FIELD(tm_sec);
+ } while ( 0 );
+
+ /* Set remaining unspecified fields to 0. */
+#define ZERO_FIELD_IF_NOT_SET(field_name,zero_val) \
+ if (t.field_name < 0) \
+ t.field_name = zero_val
+
+ if (! is_delta) {
+ ZERO_FIELD_IF_NOT_SET(tm_year,90); /* should never happen */
+ ZERO_FIELD_IF_NOT_SET(tm_mon,0);
+ ZERO_FIELD_IF_NOT_SET(tm_mday,1);
+ ZERO_FIELD_IF_NOT_SET(tm_hour,0);
+ ZERO_FIELD_IF_NOT_SET(tm_min,0);
+ ZERO_FIELD_IF_NOT_SET(tm_sec,0);
+ }
+
+ result.tv_sec = gwtm2secs(&t);
+ result.tv_sec -= local_time_zone(result.tv_sec);
+ result.tv_usec = usecs;
+
+ return result;
+}
+
+
+/* Fill in (or add to, if is_delta is true) the time values in the
+ * tm struct "t" as specified by the time specified in the string
+ * "time_string". "usecs_addr" is updated with the specified number
+ * of microseconds, if any.
+ */
+void
+fill_tm(char *time_string, int is_delta, struct tm *t, time_t *usecs_addr)
+{
+ char *t_start, *t_stop, format_ch;
+ int val;
+
+#define SET_VAL(lhs,rhs) \
+ if (is_delta) \
+ lhs += rhs; \
+ else \
+ lhs = rhs
+
+ /* Loop through the time string parsing one specification at
+ * a time. Each specification has the form <number><letter>
+ * where <number> indicates the amount of time and <letter>
+ * the units.
+ */
+ for (t_stop = t_start = time_string; *t_start; t_start = ++t_stop) {
+ if (! isdigit(*t_start))
+ error("bad date format %s, problem starting at %s",
+ time_string, t_start);
+
+ while (isdigit(*t_stop))
+ ++t_stop;
+ if (! t_stop)
+ error("bad date format %s, problem starting at %s",
+ time_string, t_start);
+
+ val = atoi(t_start);
+
+ format_ch = *t_stop;
+ if ( isupper( format_ch ) )
+ format_ch = tolower( format_ch );
+
+ switch (format_ch) {
+ case 'y':
+ if ( val >= 1900 )
+ val -= 1900;
+ else if (val < 100 && !is_delta) {
+ if (val < 69) /* Same hack as date */
+ val += 100;
+ }
+ SET_VAL(t->tm_year, val);
+ break;
+
+ case 'm':
+ if (strchr(t_stop+1, 'D') ||
+ strchr(t_stop+1, 'd'))
+ /* it's months */
+ SET_VAL(t->tm_mon, val - 1);
+ else /* it's minutes */
+ SET_VAL(t->tm_min, val);
+ break;
+
+ case 'd':
+ SET_VAL(t->tm_mday, val);
+ break;
+
+ case 'h':
+ SET_VAL(t->tm_hour, val);
+ break;
+
+ case 's':
+ SET_VAL(t->tm_sec, val);
+ break;
+
+ case 'u':
+ SET_VAL(*usecs_addr, val);
+ break;
+
+ default:
+ error(
+ "bad date format %s, problem starting at %s",
+ time_string, t_start);
+ }
+ }
+}
+
+
+/* Return in first_time and last_time the timestamps of the first and
+ * last packets in the given file.
+ */
+void
+get_file_range( char filename[], pcap_t **p,
+ struct timeval *first_time, struct timeval *last_time )
+{
+ *first_time = first_packet_time( filename, p );
+
+ if ( ! sf_find_end( *p, first_time, last_time ) )
+ error( "couldn't find final packet in file %s", filename );
+}
+
+int snaplen;
+
+/* Returns the timestamp of the first packet in the given tcpdump save
+ * file, which as a side-effect is initialized for further save-file
+ * reading.
+ */
+
+struct timeval
+first_packet_time(char filename[], pcap_t **p_addr)
+{
+ struct pcap_pkthdr hdr;
+ pcap_t *p;
+ char errbuf[PCAP_ERRBUF_SIZE];
+
+ p = *p_addr = pcap_open_offline(filename, errbuf);
+ if (! p)
+ error( "bad tcpdump file %s: %s", filename, errbuf );
+
+ snaplen = pcap_snapshot( p );
+
+ if (pcap_next(p, &hdr) == 0)
+ error( "bad status reading first packet in %s", filename );
+
+ return hdr.ts;
+}
+
+
+/* Extract from the given file all packets with timestamps between
+ * the two time values given (inclusive). These packets are written
+ * to the save file given by write_file_name.
+ *
+ * Upon return, start_time is adjusted to reflect a time just after
+ * that of the last packet written to the output.
+ */
+
+void
+extract_slice(char filename[], char write_file_name[],
+ struct timeval *start_time, struct timeval *stop_time)
+{
+ long start_pos, stop_pos;
+ struct timeval file_start_time, file_stop_time;
+ struct pcap_pkthdr hdr;
+ pcap_t *p;
+ char errbuf[PCAP_ERRBUF_SIZE];
+
+ p = pcap_open_offline(filename, errbuf);
+ if (! p)
+ error( "bad tcpdump file %s: %s", filename, errbuf );
+
+ snaplen = pcap_snapshot( p );
+ start_pos = ftell( pcap_file( p ) );
+
+ if ( ! dumper )
+ {
+ dumper = pcap_dump_open(p, write_file_name);
+ if ( ! dumper )
+ error( "error creating output file %s: ",
+ write_file_name, pcap_geterr( p ) );
+ }
+
+ if (pcap_next(p, &hdr) == 0)
+ error( "error reading packet in %s: ",
+ filename, pcap_geterr( p ) );
+
+ file_start_time = hdr.ts;
+
+
+ if ( ! sf_find_end( p, &file_start_time, &file_stop_time ) )
+ error( "problems finding end packet of file %s",
+ filename );
+
+ stop_pos = ftell( pcap_file( p ) );
+
+
+ /* sf_find_packet() requires that the time it's passed as its last
+ * argument be in the range [min_time, max_time], so we enforce
+ * that constraint here.
+ */
+ if ( sf_timestamp_less_than( start_time, &file_start_time ) )
+ *start_time = file_start_time;
+
+ if ( sf_timestamp_less_than( &file_stop_time, start_time ) )
+ return; /* there aren't any packets of interest in the file */
+
+
+ sf_find_packet( p, &file_start_time, start_pos,
+ &file_stop_time, stop_pos,
+ start_time );
+
+ for ( ; ; )
+ {
+ struct timeval *timestamp;
+ const u_char *pkt = pcap_next( p, &hdr );
+
+ if ( pkt == 0 )
+ {
+#ifdef notdef
+ int status;
+ if ( status != SFERR_EOF )
+ error( "bad status %d reading packet in %s",
+ status, filename );
+#endif
+ break;
+ }
+
+ timestamp = &hdr.ts;
+
+ if ( ! sf_timestamp_less_than( timestamp, start_time ) )
+ { /* packet is recent enough */
+ if ( sf_timestamp_less_than( stop_time, timestamp ) )
+ /* We've gone beyond the end of the region
+ * of interest ... We're done with this file.
+ */
+ break;
+
+ pcap_dump((u_char *) dumper, &hdr, pkt);
+
+ *start_time = *timestamp;
+
+ /* We know that each packet is guaranteed to have
+ * a unique timestamp, so we push forward the
+ * allowed minimum time to weed out duplicate
+ * packets.
+ */
+ ++start_time->tv_usec;
+ }
+ }
+
+ pcap_close( p );
+}
+
+
+/* Translates a timestamp to the time format specified by the user.
+ * Returns a pointer to the translation residing in a static buffer.
+ * There are two such buffers, which are alternated on subseqeuent
+ * calls, so two calls may be made to this routine without worrying
+ * about the results of the first call being overwritten by the
+ * results of the second.
+ */
+
+char *
+timestamp_to_string(struct timeval *timestamp)
+{
+ struct tm *t;
+ time_t tt;
+#define NUM_BUFFERS 2
+ static char buffers[NUM_BUFFERS][128];
+ static int buffer_to_use = 0;
+ char *buf;
+
+ buf = buffers[buffer_to_use];
+ buffer_to_use = (buffer_to_use + 1) % NUM_BUFFERS;
+
+ switch ( timestamp_style )
+ {
+ case TIMESTAMP_RAW:
+ sprintf(buf, "%lu.%06lu", timestamp->tv_sec, timestamp->tv_usec);
+ break;
+
+ case TIMESTAMP_READABLE:
+ tt = _long_to_time(timestamp->tv_sec);
+ t = localtime(&tt);
+ strcpy( buf, asctime( t ) );
+ buf[24] = '\0'; /* nuke final newline */
+ break;
+
+ case TIMESTAMP_PARSEABLE:
+ tt = _long_to_time(timestamp->tv_sec);
+ t = localtime(&tt);
+ if (t->tm_year >= 100)
+ t->tm_year += 1900;
+ sprintf( buf, "%02dy%02dm%02dd%02dh%02dm%02ds%06ldu",
+ t->tm_year, t->tm_mon + 1, t->tm_mday, t->tm_hour,
+ t->tm_min, t->tm_sec, timestamp->tv_usec );
+ break;
+
+ }
+
+ return buf;
+}
+
+
+/* Given a tcpdump save filename, reports on the times of the first
+ * and last packets in the file.
+ */
+
+void
+dump_times(pcap_t **p, char filename[])
+{
+ struct timeval first_time, last_time;
+
+ get_file_range( filename, p, &first_time, &last_time );
+
+ printf( "%s\t%s\t%s\n",
+ filename,
+ timestamp_to_string( &first_time ),
+ timestamp_to_string( &last_time ) );
+}
+
+static void
+usage(void)
+{
+ (void)fprintf(stderr, "tcpslice for tcpdump version %d.%d\n",
+ VERSION_MAJOR, VERSION_MINOR);
+ (void)fprintf(stderr,
+"usage: tcpslice [-dRrt] [-w file] [start-time [end-time]] file ... \n");
+
+ exit(1);
+}
+
diff --git a/usr.sbin/tcpdump/tcpslice/tcpslice.h b/usr.sbin/tcpdump/tcpslice/tcpslice.h
new file mode 100644
index 0000000..14c69a7
--- /dev/null
+++ b/usr.sbin/tcpdump/tcpslice/tcpslice.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 1987-1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+
+#include <net/bpf.h>
+
+#include <ctype.h>
+#ifdef SOLARIS
+#include <fcntl.h>
+#endif
+#ifdef __STDC__
+#include <stdlib.h>
+#endif
+#include <stdio.h>
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+#include <string.h>
+#include <unistd.h>
+
+#include "pcap.h"
+#include "version.h"
+
+
+time_t gwtm2secs( struct tm *tm );
+
+int sf_find_end( struct pcap *p, struct timeval *first_timestamp,
+ struct timeval *last_timestamp );
+int sf_timestamp_less_than( struct timeval *t1, struct timeval *t2 );
+int sf_find_packet( struct pcap *p,
+ struct timeval *min_time, long min_pos,
+ struct timeval *max_time, long max_pos,
+ struct timeval *desired_time );
+
+void error(const char *fmt, ...);
diff --git a/usr.sbin/tcpdump/tcpslice/util.c b/usr.sbin/tcpdump/tcpslice/util.c
new file mode 100644
index 0000000..8904352
--- /dev/null
+++ b/usr.sbin/tcpdump/tcpslice/util.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 1988-1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#if !defined(lint) && !defined(__GNUC__)
+static char rcsid[] =
+ "@(#) $FreeBSD$ (LBL)";
+#endif
+
+#include "tcpslice.h"
+
+/* VARARGS */
+void
+#if __STDC__
+error(const char *fmt, ...)
+#else
+error(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+
+ (void)fprintf(stderr, "tcpslice: ");
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ if (*fmt) {
+ fmt += strlen(fmt);
+ if (fmt[-1] != '\n')
+ (void)fputc('\n', stderr);
+ }
+ exit(1);
+ /* NOTREACHED */
+}
diff --git a/usr.sbin/timed/Makefile b/usr.sbin/timed/Makefile
new file mode 100644
index 0000000..2f56510
--- /dev/null
+++ b/usr.sbin/timed/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+SUBDIR= timed timedc
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/timed/SMM.doc/timed/Makefile b/usr.sbin/timed/SMM.doc/timed/Makefile
new file mode 100644
index 0000000..9afa6c6
--- /dev/null
+++ b/usr.sbin/timed/SMM.doc/timed/Makefile
@@ -0,0 +1,12 @@
+# @(#)Makefile 8.1 (Berkeley) 6/8/93
+# $FreeBSD$
+
+DIR= smm/12.timed
+SRCS= timed.ms
+MACROS= -ms
+PRINTER=Pdp
+
+paper.${PRINTER}: ${SRCS}
+ ${SOELIM} ${SRCS} | ${TBL} | ${ROFF} > ${.TARGET}
+
+.include <bsd.doc.mk>
diff --git a/usr.sbin/timed/SMM.doc/timed/date b/usr.sbin/timed/SMM.doc/timed/date
new file mode 100644
index 0000000..e4e4d58
--- /dev/null
+++ b/usr.sbin/timed/SMM.doc/timed/date
@@ -0,0 +1,53 @@
+.\" Copyright (c) 1986, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)date 8.1 (Berkeley) 6/8/93
+.\"
+.ft B
+.TS
+center;
+ce | ce | ce | ce
+| c | c | c | s |
+| c s s s |.
+Byte 1 Byte 2 Byte 3 Byte 4
+=
+Type Version No. Sequence No.
+_
+Seconds of Time to Set
+_
+Microseconds of Time to Set
+_
+Machine Name
+_
+\&. . .
+_
+.TE
+.ft R
diff --git a/usr.sbin/timed/SMM.doc/timed/loop b/usr.sbin/timed/SMM.doc/timed/loop
new file mode 100644
index 0000000..11ccb4d
--- /dev/null
+++ b/usr.sbin/timed/SMM.doc/timed/loop
@@ -0,0 +1,54 @@
+.\" Copyright (c) 1986, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)loop 8.1 (Berkeley) 6/8/93
+.\"
+.ft B
+.TS
+center;
+ce | ce | ce | ce
+| c | c | c | s |
+| c | c s s |
+| c s s s |.
+Byte 1 Byte 2 Byte 3 Byte 4
+=
+Type Version No. Sequence No.
+_
+Hop Count ( unused )
+_
+( unused )
+_
+Machine Name
+_
+\&. . .
+_
+.TE
+.ft R
diff --git a/usr.sbin/timed/SMM.doc/timed/spell.ok b/usr.sbin/timed/SMM.doc/timed/spell.ok
new file mode 100644
index 0000000..8ecfe15
--- /dev/null
+++ b/usr.sbin/timed/SMM.doc/timed/spell.ok
@@ -0,0 +1,34 @@
+ACK
+ADJTIME
+Adjtime
+CS
+CSELT
+Candidature
+DATEACK
+DoD
+Gusella
+MASTERACK
+MASTERREQ
+MASTERUP
+MSITE
+MSITEREQ
+Protocol''SMM:22
+Riccardo
+SETDATE
+SETDATEREQ
+SETTIME
+SLAVEUP
+SMM:22
+Stefano
+TRACEOFF
+TRACEON
+TSP
+Timedc
+UDP
+USENIX
+Zatti
+candidature
+ce
+daemon
+daemons
+timedc
diff --git a/usr.sbin/timed/SMM.doc/timed/time b/usr.sbin/timed/SMM.doc/timed/time
new file mode 100644
index 0000000..619d171
--- /dev/null
+++ b/usr.sbin/timed/SMM.doc/timed/time
@@ -0,0 +1,53 @@
+.\" Copyright (c) 1986, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)time 8.1 (Berkeley) 6/8/93
+.\"
+.ft B
+.TS
+center;
+ce | ce | ce | ce
+| c | c | c | s |
+| c s s s |.
+Byte 1 Byte 2 Byte 3 Byte 4
+=
+Type Version No. Sequence No.
+_
+Seconds of Adjustment
+_
+Microseconds of Adjustment
+_
+Machine Name
+_
+\&. . .
+_
+.TE
+.ft R
diff --git a/usr.sbin/timed/SMM.doc/timed/timed.ms b/usr.sbin/timed/SMM.doc/timed/timed.ms
new file mode 100644
index 0000000..031f255
--- /dev/null
+++ b/usr.sbin/timed/SMM.doc/timed/timed.ms
@@ -0,0 +1,462 @@
+.\" $FreeBSD$
+.\"
+.\" Copyright (c) 1986, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)timed.ms 8.1 (Berkeley) 6/8/93
+.\"
+.TL
+The Berkeley
+.UX
+.br
+Time Synchronization Protocol
+.AU
+Riccardo Gusella, Stefano Zatti, and James M. Bloom
+.AI
+Computer Systems Research Group
+Computer Science Division
+Department of Electrical Engineering and Computer Science
+University of California, Berkeley
+Berkeley, CA 94720
+.FS
+This work was sponsored by the Defense Advanced Research Projects Agency
+(DoD), monitored by the Naval Electronics Systems
+Command under contract No. N00039-84-C-0089, and by the Italian CSELT
+Corporation.
+The views and conclusions contained in this document are those of the
+authors and should not be interpreted as representing official policies,
+either expressed or implied, of the Defense Research Projects Agency,
+of the US Government, or of CSELT.
+.FE
+.LP
+.OH 'The Berkeley UNIX Time Synchronization Protocol''SMM:12-%'
+.EH 'SMM:12-%''The Berkeley UNIX Time Synchronization Protocol'
+.SH
+Introduction
+.PP
+The Time Synchronization Protocol (TSP)
+has been designed for specific use by the program \fItimed\fP,
+a local area network clock synchronizer for
+the UNIX 4.3BSD operating
+system.
+Timed is built on the DARPA UDP protocol [4] and
+is based on a master slave scheme.
+.PP
+TSP serves a dual purpose.
+First, it supports messages for the synchronization of the clocks
+of the various hosts in a local area network.
+Second, it supports messages for the election that occurs
+among slave time daemons when, for any reason, the master disappears.
+The synchronization mechanism and the election procedure
+employed by the program timed are described
+in other documents [1,2,3].
+.PP
+Briefly, the synchronization software, which works in a
+local area network, consists of a collection of \fItime daemons\fP
+(one per machine) and is based on a master-slave
+structure.
+The present implementation keeps processor clocks synchronized
+within 20 milliseconds.
+A \fImaster time daemon\fP measures the time
+difference between the clock of the machine on which it
+is running and those of all other machines. The current implementation
+uses ICMP \fITime Stamp Requests\fP [5] to measure the clock difference
+between machines.
+The master computes the \fInetwork time\fP as the average of the
+times provided by nonfaulty clocks.\**
+.FS
+A clock is considered to be faulty when its value
+is more than a small specified
+interval apart from the majority of the clocks
+of the machines on the same network.
+See [1,2] for more details.
+.FE
+It then sends to each \fIslave time daemon\fP the
+correction that should be performed on the clock of its machine.
+This process is repeated periodically.
+Since the correction is expressed as a time difference rather than an
+absolute time, transmission delays do not interfere with synchronization.
+When a machine comes up and joins the network,
+it starts a slave time daemon, which
+will ask the master for the correct time and will reset the machine's clock
+before any user activity can begin.
+The time daemons therefore maintain a single network time in spite of
+the drift of clocks away from each other.
+.PP
+Additionally, a time daemon on gateway machines may run as
+a \fIsubmaster\fP.
+A submaster time daemon functions as a slave on one network that
+already has a master and as master on other networks.
+In addition, a submaster is responsible for propagating broadcast
+packets from one network to the other.
+.PP
+To ensure that service provided is continuous and reliable,
+it is necessary to implement an election algorithm that will elect a
+new master should the machine running the current master crash, the master
+terminate (for example, because of a run-time error), or the network be
+partitioned.
+Under our algorithm, slaves are able to realize when the master has
+stopped functioning and to elect a new master from among themselves.
+It is important to note that since the failure of the master results
+only in a gradual divergence of clock values, the election
+need not occur immediately.
+.PP
+All the communication occurring among time daemons uses the TSP
+protocol.
+While some messages need not be sent in a reliable way,
+most communication in TSP requires reliability not provided by the underlying
+protocol.
+Reliability is achieved by the use of acknowledgements, sequence numbers, and
+retransmission when message losses occur.
+When a message that requires acknowledgment is not acknowledged after
+multiple attempts,
+the time daemon that has sent the message will assume that the
+addressee is down.
+This document will not describe the details of how reliability is
+implemented, but will only point out when
+a message type requires a reliable transport mechanism.
+.PP
+The message format in TSP is the same for all message types;
+however, in some instances, one or more fields are not used.
+The next section describes the message format.
+The following sections describe
+in detail the different message types, their use and the contents
+of each field. NOTE: The message format is likely to change in
+future versions of timed.
+.sp 2
+.SH
+Message Format
+.PP
+All fields are based upon 8-bit bytes. Fields should be sent in
+network byte order if they are more than one byte long.
+The structure of a TSP message is the following:
+.IP 1)
+A one byte message type.
+.IP 2)
+A one byte version number, specifying the protocol version which the
+message uses.
+.IP 3)
+A two byte sequence number to be used for recognizing duplicate messages
+that occur when messages are retransmitted.
+.IP 4)
+Eight bytes of packet specific data. This field contains two 4 byte time
+values, a one byte hop count, or may be unused depending on the type
+of the packet.
+.IP 5)
+A zero-terminated string of up to 256 \s-2ASCII\s+2 characters with the name of
+the machine sending the message.
+.PP
+The following charts describe the message types,
+show their fields, and explain their usages.
+For the purpose of the following discussion, a time daemon can
+be considered to be in
+one of three states: slave, master, or candidate for election to master.
+Also, the term \fIbroadcast\fP refers to
+the sending of a message to all active time daemons.
+.sp 1
+.SH
+Adjtime Message
+.so time
+.LP
+Type: TSP_ADJTIME (1)
+.sp 1
+.PP
+The master sends this message to a slave to communicate
+the difference between
+the clock of the slave and
+the network time the master has just computed.
+The slave will accordingly
+adjust the time of its machine.
+This message requires an acknowledgment.
+.sp 1
+.SH
+Acknowledgment Message
+.so unused
+.LP
+Type: TSP_ACK (2)
+.sp 1
+.PP
+Both the master and the slaves use this message for
+acknowledgment only.
+It is used in several different contexts, for example
+in reply to an Adjtime message.
+.sp 1
+.SH
+Master Request Message
+.so unused
+.LP
+Type: TSP_MASTERREQ (3)
+.sp 1
+.PP
+A newly-started time daemon broadcasts this message to
+locate a master. No other action is implied by this packet.
+It requires a Master Acknowledgment.
+.sp 1
+.SH
+Master Acknowledgement
+.so unused
+.LP
+Type: TSP_MASTERACK (4)
+.sp 1
+.PP
+The master sends this message to acknowledge the Master Request message
+and the Conflict Resolution Message.
+.sp 1
+.SH
+Set Network Time Message
+.so date
+.LP
+Type: TSP_SETTIME (5)
+.sp 1
+.PP
+The master sends this message to slave time daemons to set their time.
+This packet is sent to newly started time daemons and when the network
+date is changed.
+It contains the master's time as an approximation of the network time.
+It requires an acknowledgment.
+The next
+synchronization round will eliminate the small time difference
+caused by the random delay in the communication channel.
+.sp 1
+.SH
+Master Active Message
+.so unused
+.LP
+Type: TSP_MASTERUP (6)
+.sp 1
+.PP
+The master broadcasts this message to
+solicit the names of the active slaves.
+Slaves will reply with a Slave Active message.
+.sp 1
+.SH
+Slave Active Message
+.so unused
+.LP
+Type: TSP_SLAVEUP (7)
+.sp 1
+.PP
+A slave sends this message to the master in answer to a Master Active message.
+This message is also sent when a new slave starts up to inform the master that
+it wants to be synchronized.
+.sp 1
+.SH
+Master Candidature Message
+.so unused
+.LP
+Type: TSP_ELECTION (8)
+.sp 1
+.PP
+A slave eligible to become a master broadcasts this message when its election
+timer expires.
+The message declares that the slave wishes to become the new master.
+.sp 1
+.SH
+Candidature Acceptance Message
+.so unused
+.LP
+Type: TSP_ACCEPT (9)
+.sp 1
+.PP
+A slave sends this message to accept the candidature of the time daemon
+that has broadcast an Election message.
+The candidate will add the slave's name to the list of machines that it
+will control should it become the master.
+.sp 1
+.SH
+Candidature Rejection Message
+.so unused
+.LP
+Type: TSP_REFUSE (10)
+.sp 1
+.PP
+After a slave accepts the candidature of a time daemon, it will reply
+to any election messages from other slaves
+with this message.
+This rejects any candidature other than the first received.
+.sp 1
+.SH
+Multiple Master Notification Message
+.so unused
+.LP
+Type: TSP_CONFLICT (11)
+.sp 1
+.PP
+When two or more masters reply to a Master Request message, the slave
+uses this message to inform one of them that more than one master exists.
+.sp 1
+.SH
+Conflict Resolution Message
+.so unused
+.LP
+Type: TSP_RESOLVE (12)
+.sp 1
+.PP
+A master which has been informed of the existence of other masters
+broadcasts this message to determine who the other masters are.
+.sp 1
+.SH
+Quit Message
+.so unused
+.LP
+Type: TSP_QUIT (13)
+.sp 1
+.PP
+This message is sent by the master in three different contexts:
+1) to a candidate that broadcasts 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
+.SH
+Set Date Message
+.so date
+.LP
+Type: TSP_SETDATE (22)
+.sp 1
+.PP
+The program \fIdate\fP\|(1) sends this message to the local time daemon
+when a super-user wants to set the network date.
+If the local time daemon is the master, it will set the date;
+if it is a slave, it will communicate the desired date to the master.
+.sp 1
+.SH
+Set Date Request Message
+.so date
+.LP
+Type: TSP_SETDATEREQ (23)
+.sp 1
+.PP
+A slave that has received a Set Date message will communicate the
+desired date to the master using this message.
+.sp 1
+.SH
+Set Date Acknowledgment Message
+.so unused
+.LP
+Type: TSP_DATEACK (16)
+.sp 1
+.PP
+The master sends this message to a slave in acknowledgment of a
+Set Date Request Message.
+The same message is sent by the local time daemon to the program
+\fIdate(1)\fP to confirm that the network date has been set by the
+master.
+.sp 1
+.SH
+Start Tracing Message
+.so unused
+.LP
+Type: TSP_TRACEON (17)
+.sp 1
+.PP
+The controlling program \fItimedc\fP sends this message to the local
+time daemon to start the recording in a system file of
+all messages received.
+.sp 1
+.SH
+Stop Tracing Message
+.so unused
+.LP
+Type: TSP_TRACEOFF (18)
+.sp 1
+.PP
+\fITimedc\fP sends this message to the local
+time daemon to stop the recording of
+messages received.
+.sp 1
+.SH
+Master Site Message
+.so unused
+.LP
+Type: TSP_MSITE (19)
+.sp 1
+.PP
+\fITimedc\fP sends this message to the local time daemon to find out
+where the master is running.
+.sp 1
+.SH
+Remote Master Site Message
+.so unused
+.LP
+Type: TSP_MSITEREQ (20)
+.sp 1
+.PP
+A local time daemon broadcasts this message to find the location
+of the master.
+It then uses the Acknowledgement message to
+communicate this location to \fItimedc\fP.
+.sp 1
+.SH
+Test Message
+.so unused
+.LP
+Type: TSP_TEST (21)
+.sp 1
+.PP
+For testing purposes, \fItimedc\fP sends this message to a slave
+to cause its election timer to expire. NOTE: \fItimed\fP
+is not normally compiled to support this.
+.sp 1
+.SH
+.SH
+Loop Detection Message
+.so loop
+.LP
+Type: TSP_LOOP (24)
+.sp 1
+.PP
+This packet is initiated by all masters occasionally to attempt to detect loops.
+All submasters forward this packet onto the networks over which they are master.
+If a master receives a packet it sent out initially,
+it knows that a loop exists and tries to correct the problem.
+.SH
+References
+.IP 1.
+R. Gusella and S. Zatti,
+\fITEMPO: A Network Time Controller for Distributed Berkeley UNIX System\fP,
+USENIX Summer Conference Proceedings, Salt Lake City, June 1984.
+.IP 2.
+R. Gusella and S. Zatti, \fIClock Synchronization in a Local Area Network\fP,
+University of California, Berkeley, Technical Report, \fIto appear\fP.
+.IP 3.
+R. Gusella and S. Zatti,
+\fIAn Election Algorithm for a Distributed Clock Synchronization Program\fP,
+University of California, Berkeley, CS Technical Report #275, Dec. 1985.
+.IP 4.
+Postel, J., \fIUser Datagram Protocol\fP, RFC 768.
+Network Information Center, SRI International, Menlo Park, California,
+August 1980.
+.IP 5.
+Postel, J., \fIInternet Control Message Protocol\fP, RFC 792.
+Network Information Center, SRI International, Menlo Park, California,
+September 1981.
diff --git a/usr.sbin/timed/SMM.doc/timed/unused b/usr.sbin/timed/SMM.doc/timed/unused
new file mode 100644
index 0000000..adadfc3
--- /dev/null
+++ b/usr.sbin/timed/SMM.doc/timed/unused
@@ -0,0 +1,53 @@
+.\" Copyright (c) 1986, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)unused 8.1 (Berkeley) 6/8/93
+.\"
+.ft B
+.TS
+center;
+ce | ce | ce | ce
+| c | c | c | s |
+| c s s s |.
+Byte 1 Byte 2 Byte 3 Byte 4
+=
+Type Version No. Sequence No.
+_
+( unused )
+_
+( unused )
+_
+Machine Name
+_
+\&. . .
+_
+.TE
+.ft R
diff --git a/usr.sbin/timed/SMM.doc/timedop/Makefile b/usr.sbin/timed/SMM.doc/timedop/Makefile
new file mode 100644
index 0000000..7d52a32
--- /dev/null
+++ b/usr.sbin/timed/SMM.doc/timedop/Makefile
@@ -0,0 +1,8 @@
+# @(#)Makefile 8.1 (Berkeley) 6/8/93
+# $FreeBSD$
+
+DIR= smm/11.timedop
+SRCS= timed.ms
+MACROS= -ms
+
+.include <bsd.doc.mk>
diff --git a/usr.sbin/timed/SMM.doc/timedop/timed.ms b/usr.sbin/timed/SMM.doc/timedop/timed.ms
new file mode 100644
index 0000000..feea0b5
--- /dev/null
+++ b/usr.sbin/timed/SMM.doc/timedop/timed.ms
@@ -0,0 +1,279 @@
+.\" Copyright (c) 1986, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)timed.ms 8.1 (Berkeley) 6/8/93
+.\"
+.TL
+Timed Installation and Operation Guide
+.AU
+Riccardo Gusella, Stefano Zatti, James M. Bloom
+.AI
+Computer Systems Research Group
+Computer Science Division
+Department of Electrical Engineering and Computer Science
+University of California, Berkeley
+Berkeley, CA 94720
+.AU
+Kirk Smith
+.AI
+Engineering Computer Network
+Department of Electrical Engineering
+Purdue University
+West Lafayette, IN 47906
+.FS
+This work was sponsored by the Defense Advanced Research Projects Agency
+(DoD), monitored by the Naval Electronics Systems
+Command under contract No. N00039-84-C-0089, and by the CSELT
+Corporation of Italy.
+The views and conclusions contained in this document are those of the
+authors and should not be interpreted as representing official policies,
+either expressed or implied, of the Defense Research Projects Agency,
+of the US Government, or of CSELT.
+.FE
+.LP
+.EH 'SMM:11-%''Timed Installation and Operation'
+.OH 'Timed Installation and Operation''SMM:11-%'
+.SH
+Introduction
+.PP
+The clock synchronization service for
+the UNIX 4.3BSD operating system is composed of a collection of
+time daemons (\fItimed\fP) running on the machines in a local
+area network.
+The algorithms implemented by the service is based on a master-slave scheme.
+The time daemons communicate with each other using the
+\fITime Synchronization Protocol\fP (TSP) which
+is built on the DARPA UDP protocol and described in detail in [4].
+.PP
+A time daemon has a twofold function.
+First, it supports the synchronization of the clocks
+of the various hosts in a local area network.
+Second, it starts (or takes part in) the election that occurs
+among slave time daemons when, for any reason, the master disappears.
+The synchronization mechanism and the election procedure
+employed by the program \fItimed\fP are described
+in other documents [1,2,3].
+The next paragraphs are a brief overview of how the time daemon works.
+This document is mainly concerned with the administrative and technical
+issues of running \fItimed\fP at a particular site.
+.PP
+A \fImaster time daemon\fP measures the time
+differences between the clock of the machine on which it
+is running and those of all other machines.
+The master computes the \fInetwork time\fP as the average of the
+times provided by nonfaulty clocks.\**
+.FS
+A clock is considered to be faulty when its value
+is more than a small specified
+interval apart from the majority of the clocks
+of the other machines [1,2].
+.FE
+It then sends to each \fIslave time daemon\fP the
+correction that should be performed on the clock of its machine.
+This process is repeated periodically.
+Since the correction is expressed as a time difference rather than an
+absolute time, transmission delays do not interfere with
+the accuracy of the synchronization.
+When a machine comes up and joins the network,
+it starts a slave time daemon which
+will ask the master for the correct time and will reset the machine's clock
+before any user activity can begin.
+The time daemons are able to maintain a single network time in spite of
+the drift of clocks away from each other.
+The present implementation keeps processor clocks synchronized
+within 20 milliseconds.
+.PP
+To ensure that the service provided is continuous and reliable,
+it is necessary to implement an election algorithm to elect a
+new master should the machine running the current master crash, the master
+terminate (for example, because of a run-time error), or
+the network be partitioned.
+Under our algorithm, slaves are able to realize when the master has
+stopped functioning and to elect a new master from among themselves.
+It is important to note that, since the failure of the master results
+only in a gradual divergence of clock values, the election
+need not occur immediately.
+.PP
+The machines that are gateways between distinct local area
+networks require particular care.
+A time daemon on such machines may act as a \fIsubmaster\fP.
+This artifact depends on the current inability of
+transmission protocols to broadcast a message on a network
+other than the one to which the broadcasting machine is connected.
+The submaster appears as a slave on one network, and as a master
+on one or more of the other networks to which it is connected.
+.PP
+A submaster classifies each network as one of three types.
+A \fIslave network\fP is a network on which the submaster acts as a slave.
+There can only be one slave network.
+A \fImaster network\fP is a network on which the submaster acts as a master.
+An \fIignored network\fP is any other network which already has a valid master.
+The submaster tries periodically to become master on an ignored
+network, but gives up immediately if a master already exists.
+.SH
+Guidelines
+.PP
+While the synchronization algorithm is quite general, the election
+one, requiring a broadcast mechanism, puts constraints on
+the kind of network on which time daemons can run.
+The time daemon will only work on networks with broadcast capability
+augmented with point-to-point links.
+Machines that are only connected to point-to-point,
+non-broadcast networks may not use the time daemon.
+.PP
+If we exclude submasters, there will normally be, at most, one master time
+daemon in a local area internetwork.
+During an election, only one of the slave time daemons
+will become the new master.
+However, because of the characteristics of its machine,
+a slave can be prevented from becoming the master.
+Therefore, a subset of machines must be designated as potential
+master time daemons.
+A master time daemon will require CPU resources
+proportional to the number of slaves, in general, more than
+a slave time daemon, so it may be advisable to limit master time
+daemons to machines with more powerful processors or lighter loads.
+Also, machines with inaccurate clocks should not be used as masters.
+This is a purely administrative decision: an organization may
+well allow all of its machines to run master time daemons.
+.PP
+At the administrative level, a time daemon on a machine
+with multiple network interfaces, may be told to ignore all
+but one network or to ignore one network.
+This is done with the \fI\-n network\fP and \fI\-i network\fP
+options respectively at start-up time.
+Typically, the time daemon would be instructed to ignore all but
+the networks belonging to the local administrative control.
+.PP
+There are some limitations to the current
+implementation of the time daemon.
+It is expected that these limitations will be removed in future releases.
+The constant NHOSTS in /usr/src/etc/timed/globals.h limits the
+maximum number of machines that may be directly controlled by one
+master time daemon.
+The current maximum is 29 (NHOSTS \- 1).
+The constant must be changed and the program recompiled if a site wishes to
+run \fItimed\fP on a larger (inter)network.
+.PP
+In addition, there is a \fIpathological situation\fP to
+be avoided at all costs, that might occur when
+time daemons run on multiply-connected local area networks.
+In this case, as we have seen, time daemons running on gateway machines
+will be submasters and they will act on some of those
+networks as master time daemons.
+Consider machines A and B that are both gateways between
+networks X and Y.
+If time daemons were started on both A and B without constraints, it would be
+possible for submaster time daemon A to be a slave on network X
+and the master on network Y, while submaster time daemon B is a slave on
+network Y and the master on network X.
+This \fIloop\fP of master time daemons will not function properly
+or guarantee a unique time on both networks, and will cause
+the submasters to use large amounts of system resources in the form
+of network bandwidth and CPU time.
+In fact, this kind of \fIloop\fP can also be generated with more
+than two master time daemons,
+when several local area networks are interconnected.
+.SH
+Installation
+.PP
+In order to start the time daemon on a given machine,
+the following lines should be
+added to the \fIlocal daemons\fP section in the file \fI/etc/rc.local\fP:
+.sp 2
+.in 1i
+.nf
+if [ -f /etc/timed ]; then
+ /etc/timed \fIflags\fP & echo -n ' timed' >/dev/console
+fi
+.fi
+.in -1i
+.sp
+.LP
+In any case, they must appear after the network
+is configured via ifconfig(8).
+.PP
+Also, the file \fI/etc/services\fP should contain the following
+line:
+.sp 2
+.ti 1i
+timed 525/udp timeserver
+.sp
+.LP
+The \fIflags\fP are:
+.IP "-n network" 13
+to consider the named network.
+.IP "-i network"
+to ignore the named network.
+.IP -t
+to place tracing information in \fI/usr/adm/timed.log\fP.
+.IP -M
+to allow this time daemon to become a master.
+A time daemon run without this option will be forced in the state of
+slave during an election.
+.SH
+Daily Operation
+.PP
+\fITimedc(8)\fP is used to control the operation of the time daemon.
+It may be used to:
+.IP \(bu
+measure the differences between machines' clocks,
+.IP \(bu
+find the location where the master \fItimed\fP is running,
+.IP \(bu
+cause election timers on several machines to expire at the same time,
+.IP \(bu
+enable or disable tracing of messages received by \fItimed\fP.
+.LP
+See the manual page on \fItimed\fP\|(8) and \fItimedc\fP\|(8)
+for more detailed information.
+.PP
+The \fIdate(1)\fP command can be used to set the network date.
+In order to set the time on a single machine, the \fI-n\fP flag
+can be given to date(1).
+.bp
+.SH
+References
+.IP 1.
+R. Gusella and S. Zatti,
+\fITEMPO: A Network Time Controller for Distributed Berkeley UNIX System\fP,
+USENIX Summer Conference Proceedings, Salt Lake City, June 1984.
+.IP 2.
+R. Gusella and S. Zatti, \fIClock Synchronization in a Local Area Network\fP,
+University of California, Berkeley, Technical Report, \fIto appear\fP.
+.IP 3.
+R. Gusella and S. Zatti,
+\fIAn Election Algorithm for a Distributed Clock Synchronization Program\fP,
+University of California, Berkeley, CS Technical Report #275, Dec. 1985.
+.IP 4.
+R. Gusella and S. Zatti,
+\fIThe Berkeley UNIX 4.3BSD Time Synchronization Protocol\fP,
+UNIX Programmer's Manual, 4.3 Berkeley Software Distribution, Volume 2c.
diff --git a/usr.sbin/timed/timed/CHANGES b/usr.sbin/timed/timed/CHANGES
new file mode 100644
index 0000000..424ead2
--- /dev/null
+++ b/usr.sbin/timed/timed/CHANGES
@@ -0,0 +1,145 @@
+# @(#)CHANGES 5.1 (Berkeley) 5/11/93
+# $FreeBSD$
+
+This new version is almost identical to the timed and timedc code
+that has been shipped for years by a workstation vendor.
+
+Among the many changes:
+
+improve `timedc msite` to accept a list of hostnames.
+
+change slave-masters to answer the packets generated by `timedc msite`
+ with the name of the real master, not their own. This makes it
+ possible to "chase the chain" of slave servers to the ultimate
+ master.
+
+much improve the log caused by `timedc trace on`:
+ -made `timed -t` work.
+ -suppression of repeated entries, which both slowed down the daemon
+ (sometimes catastrophically) and tended to make disks fill up
+ even more quickly.
+ -better time stamps on log entries
+ -more messages
+ -dump information about slaves, master, and so on each time
+ a message asking the log be turned on is received, and
+ when the log is turned off.
+ -fewer CPU cycles
+
+use a hash table to keep track of slaves, instead of the stupid linear
+ list. This becomes handy with hundreds of slaves, instead of
+ the original design limit of "a room with a few VAX's."
+
+separate the main protocol timer from that used to look for other networks
+ to master.
+
+time stamp packets received by the daemon, so that time corrections
+ are not made (even more) inaccurate by waiting in the internal,
+ timed queue while the daemon is processing other messages.
+
+made -n and -i work with subnets not named in /etc/networks
+
+compute the median of the measured clocks, instead of the average
+ of "good" times.
+
+vastly improve the accuracy of the clock difference measure by
+ `timedc clockdiff`.
+
+use adjtime() when possible, and directly set the clock only when
+ necessary.
+
+when the requested adjustment is small, perform only part of it, to
+ damp oscillations and improve the long term accuracy of the
+ adjustments.
+
+fix uncounted core-dumps on machines that do not allow dereferencing 0
+ in both the daemon and timedc.
+
+fix "master loop detection".
+
+fix several cases in which multi-homed masters could get into shouting
+ matches, consuming all available network bandwidth and CPU cycles
+ (which ever runs out first), and convincing all bystanders to stop
+ advancing their own clocks.
+
+refuse to behave badly when other machines do. Instead of arguing forever,
+ go off and sulk when other machines refuse to play by the rules.
+
+increase the maximum number of clients.
+
+add "-F host,host2,..." to "freerun" or "trust" only some hosts. This
+ is handy both when only some machines should be trusted to let
+ root use the `date` command to change time in the network.
+
+ It is also handy when one machine has some other way of adjusting
+ its clock, whether NTP or a direct radio or atomic connection.
+ "-F localhost" causes `timed` to "trust" only itself.
+
+ It is also handy to build a hierarchy of timed masters crossing
+ networks. The TSP protocol has no provision of "goodness of clock",
+ no natural way to completely heal network paritions. Judicious
+ use of -F or -G can cause each gateway to trust only itself and
+ machines closer to a central machine with a radio or atomic clock.
+
+add #ifdef code that supports NIS "netgroups" of trusted hosts, which
+ can be easier to administer than -F.
+
+add #ifdef code to compute an aged total adjustment. This can be used
+ in systems that can make long term changes in their system clock
+ frequency, e.g. "timetrim" in the Silicon Graphics kernel.
+
+
+Problems observed by others that are unresolved include:
+
+Practically any users can send to the master TSP messages and this
+ way corrupt the reliability of the system. Authentication
+ of messages should be provided. Unfortunately, that would
+ require changing the protocol with all of the implied
+ compatiblity problems. Fortunately, the new -F and -G args
+ can be used to cause the daemon to ignore time changes from
+ untrusted machines.
+
+MAN. The limit of 1013 on the number of slaves hosts should be doc'ed.
+
+ It should be dynamically allocated with no limit. On a
+ large network, one host could possibly master over many
+ more than 30 hosts. Given the timers in the code and
+ effectively in the protocol, and the time required by each
+ master to talk to each slave, it is not practical to have
+ more than 200-300 slaves. The master cannot keep up because
+ the slave-chatting is single-threaded. when the master
+ gets behind, slaves start demanding elections. To
+ significantly increase the number of slaves would require
+ multi-treading things, and given that a network with more
+ than 300 directly addressable machines has worse problems
+ than keep the time of day right, not worth worrying about.
+
+UGLY,CODE. timedc/cmds.c has a lots of repeated code in it.
+
+**** The first thing is that each command is set up as if it
+ were an individual program taking argc and argv. A more
+ conventional calling style should be used. I don't think
+ any of the routines take more than a couple arguments.
+
+UGLY. fxn definition syntax does't follow convention:
+ has type on same line.
+
+**** It needs to be fixed at least enough that tags
+ will work on it. An entire cleanup might be nice later, but
+ is noncritical.
+
+LOBBY(mildly),CODE: Would be very convenient if date(1) took a
+ +-<number> argument to set the time relatively. With
+ the advent of timed it is now reasonable to synchronize
+ with WWV, which is nearly impossible to do "by hand"
+ with just an absolute date, and scripts are too slow.
+ format could be +-nn...nn.ss, where the '.' is required
+ to remove ambiguity.
+
+**** If you want to do it go ahead. It sounds useful. As far as
+ syntax goes, the normal format for the date should work just
+ fine for this. If the date is preceded by a plus or minus,
+ the change is relative, otherwise it is absolute.
+
+
+Vernon Schryver.
+vjs@sgi.com
diff --git a/usr.sbin/timed/timed/Makefile b/usr.sbin/timed/timed/Makefile
new file mode 100644
index 0000000..6f73824
--- /dev/null
+++ b/usr.sbin/timed/timed/Makefile
@@ -0,0 +1,15 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+#
+# optional flags are: MEASURE TESTING DEBUG
+
+PROG= timed
+MAN= timed.8
+SRCS= acksend.c candidate.c correct.c master.c networkdelta.c readmsg.c \
+ slave.c timed.c byteorder.c measure.c cksum.c
+
+DPADD= ${LIBUTIL}
+LDADD= -lutil
+
+.include "../../Makefile.inc"
+.include <bsd.prog.mk>
diff --git a/usr.sbin/timed/timed/acksend.c b/usr.sbin/timed/timed/acksend.c
new file mode 100644
index 0000000..8bde743
--- /dev/null
+++ b/usr.sbin/timed/timed/acksend.c
@@ -0,0 +1,132 @@
+/*-
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)acksend.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include "globals.h"
+
+struct tsp *answer;
+
+extern u_short sequence;
+
+void
+xmit(type, seq, addr)
+ int type;
+ u_int seq;
+ struct sockaddr_in *addr;
+{
+ static struct tsp msg;
+
+ msg.tsp_type = type;
+ msg.tsp_seq = seq;
+ msg.tsp_vers = TSPVERSION;
+ (void)strcpy(msg.tsp_name, hostname);
+ bytenetorder(&msg);
+ if (sendto(sock, (char *)&msg, sizeof(struct tsp), 0,
+ (struct sockaddr*)addr, sizeof(struct sockaddr)) < 0) {
+ trace_sendto_err(addr->sin_addr);
+ }
+}
+
+
+/*
+ * Acksend implements reliable datagram transmission by using sequence
+ * numbers and retransmission when necessary.
+ * If `name' is ANYADDR, this routine implements reliable broadcast.
+ *
+ * Because this function calls readmsg(), none of its args may be in
+ * a message provided by readmsg().
+ */
+struct tsp *
+acksend(message, addr, name, ack, net, bad)
+ struct tsp *message; /* this message */
+ struct sockaddr_in *addr; /* to here */
+ char *name;
+ int ack; /* look for this ack */
+ struct netinfo *net; /* receive from this network */
+ int bad; /* 1=losing patience */
+{
+ struct timeval twait;
+ int count;
+ long msec;
+
+ message->tsp_vers = TSPVERSION;
+ message->tsp_seq = sequence;
+ if (trace) {
+ fprintf(fd, "acksend: to %s: ",
+ (name == ANYADDR ? "broadcast" : name));
+ print(message, addr);
+ }
+ bytenetorder(message);
+
+ msec = 200;
+ count = bad ? 1 : 5; /* 5 packets in 6.4 seconds */
+ answer = 0;
+ do {
+ if (!answer) {
+ /* do not go crazy transmitting just because the
+ * other guy cannot keep our sequence numbers
+ * straight.
+ */
+ if (sendto(sock, (char *)message, sizeof(struct tsp),
+ 0, (struct sockaddr*)addr,
+ sizeof(struct sockaddr)) < 0) {
+ trace_sendto_err(addr->sin_addr);
+ break;
+ }
+ }
+
+ mstotvround(&twait, msec);
+ answer = readmsg(ack, name, &twait, net);
+ if (answer != 0) {
+ if (answer->tsp_seq != sequence) {
+ if (trace)
+ fprintf(fd,"acksend: seq # %u!=%u\n",
+ answer->tsp_seq, sequence);
+ continue;
+ }
+ break;
+ }
+
+ msec *= 2;
+ } while (--count > 0);
+ sequence++;
+
+ return(answer);
+}
diff --git a/usr.sbin/timed/timed/byteorder.c b/usr.sbin/timed/timed/byteorder.c
new file mode 100644
index 0000000..038dd56
--- /dev/null
+++ b/usr.sbin/timed/timed/byteorder.c
@@ -0,0 +1,86 @@
+/*-
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)byteorder.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include "globals.h"
+
+/*
+ * Two routines to do the necessary byte swapping for timed protocol
+ * messages. Protocol is defined in /usr/include/protocols/timed.h
+ */
+void
+bytenetorder(ptr)
+ struct tsp *ptr;
+{
+ ptr->tsp_seq = htons((u_short)ptr->tsp_seq);
+ switch (ptr->tsp_type) {
+
+ case TSP_SETTIME:
+ case TSP_ADJTIME:
+ case TSP_SETDATE:
+ case TSP_SETDATEREQ:
+ ptr->tsp_time.tv_sec = htonl((u_long)ptr->tsp_time.tv_sec);
+ ptr->tsp_time.tv_usec = htonl((u_long)ptr->tsp_time.tv_usec);
+ break;
+
+ default:
+ break; /* nothing more needed */
+ }
+}
+
+void
+bytehostorder(ptr)
+ struct tsp *ptr;
+{
+ ptr->tsp_seq = ntohs((u_short)ptr->tsp_seq);
+ switch (ptr->tsp_type) {
+
+ case TSP_SETTIME:
+ case TSP_ADJTIME:
+ case TSP_SETDATE:
+ case TSP_SETDATEREQ:
+ ptr->tsp_time.tv_sec = ntohl((u_long)ptr->tsp_time.tv_sec);
+ ptr->tsp_time.tv_usec = ntohl((u_long)ptr->tsp_time.tv_usec);
+ break;
+
+ default:
+ break; /* nothing more needed */
+ }
+}
diff --git a/usr.sbin/timed/timed/candidate.c b/usr.sbin/timed/timed/candidate.c
new file mode 100644
index 0000000..79f1f5a
--- /dev/null
+++ b/usr.sbin/timed/timed/candidate.c
@@ -0,0 +1,167 @@
+/*-
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)candidate.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include "globals.h"
+
+/*
+ * `election' candidates a host as master: it is called by a slave
+ * which runs with the -M option set when its election timeout expires.
+ * Note the conservative approach: if a new timed comes up, or another
+ * candidate sends an election request, the candidature is withdrawn.
+ */
+int
+election(net)
+ struct netinfo *net;
+{
+ struct tsp *resp, msg;
+ struct timeval then, wait;
+ struct tsp *answer;
+ struct hosttbl *htp;
+ char loop_lim = 0;
+
+/* This code can get totally confused if it gets slightly behind. For
+ * example, if readmsg() has some QUIT messages waiting from the last
+ * round, we would send an ELECTION message, get the stale QUIT,
+ * and give up. This results in network storms when several machines
+ * do it at once.
+ */
+ wait.tv_sec = 0;
+ wait.tv_usec = 0;
+ while (0 != readmsg(TSP_REFUSE, ANYADDR, &wait, net)) {
+ if (trace)
+ fprintf(fd, "election: discarded stale REFUSE\n");
+ }
+ while (0 != readmsg(TSP_QUIT, ANYADDR, &wait, net)) {
+ if (trace)
+ fprintf(fd, "election: discarded stale QUIT\n");
+ }
+
+again:
+ syslog(LOG_INFO, "This machine is a candidate time master");
+ if (trace)
+ fprintf(fd, "This machine is a candidate time master\n");
+ msg.tsp_type = TSP_ELECTION;
+ msg.tsp_vers = TSPVERSION;
+ (void)strcpy(msg.tsp_name, hostname);
+ bytenetorder(&msg);
+ if (sendto(sock, (char *)&msg, sizeof(struct tsp), 0,
+ (struct sockaddr*)&net->dest_addr,
+ sizeof(struct sockaddr)) < 0) {
+ trace_sendto_err(net->dest_addr.sin_addr);
+ return(SLAVE);
+ }
+
+ (void)gettimeofday(&then, 0);
+ then.tv_sec += 3;
+ for (;;) {
+ (void)gettimeofday(&wait, 0);
+ timevalsub(&wait,&then,&wait);
+ resp = readmsg(TSP_ANY, ANYADDR, &wait, net);
+ if (!resp)
+ return(MASTER);
+
+ switch (resp->tsp_type) {
+
+ case TSP_ACCEPT:
+ (void)addmach(resp->tsp_name, &from,fromnet);
+ break;
+
+ case TSP_MASTERUP:
+ case TSP_MASTERREQ:
+ /*
+ * If another timedaemon is coming up at the same
+ * time, give up, and let it be the master.
+ */
+ if (++loop_lim < 5
+ && !good_host_name(resp->tsp_name)) {
+ (void)addmach(resp->tsp_name, &from,fromnet);
+ suppress(&from, resp->tsp_name, net);
+ goto again;
+ }
+ rmnetmachs(net);
+ return(SLAVE);
+
+ case TSP_QUIT:
+ case TSP_REFUSE:
+ /*
+ * Collision: change value of election timer
+ * using exponential backoff.
+ *
+ * Fooey.
+ * An exponential backoff on a delay starting at
+ * 6 to 15 minutes for a process that takes
+ * milliseconds is silly. It is particularly
+ * strange that the original code would increase
+ * the backoff without bound.
+ */
+ rmnetmachs(net);
+ return(SLAVE);
+
+ case TSP_ELECTION:
+ /* no master for another round */
+ htp = addmach(resp->tsp_name,&from,fromnet);
+ msg.tsp_type = TSP_REFUSE;
+ (void)strcpy(msg.tsp_name, hostname);
+ answer = acksend(&msg, &htp->addr, htp->name,
+ TSP_ACK, 0, htp->noanswer);
+ if (!answer) {
+ syslog(LOG_ERR, "error in election from %s",
+ htp->name);
+ }
+ break;
+
+ case TSP_SLAVEUP:
+ (void)addmach(resp->tsp_name, &from,fromnet);
+ break;
+
+ case TSP_SETDATE:
+ case TSP_SETDATEREQ:
+ break;
+
+ default:
+ if (trace) {
+ fprintf(fd, "candidate: ");
+ print(resp, &from);
+ }
+ break;
+ }
+ }
+}
diff --git a/usr.sbin/timed/timed/cksum.c b/usr.sbin/timed/timed/cksum.c
new file mode 100644
index 0000000..50a7da2
--- /dev/null
+++ b/usr.sbin/timed/timed/cksum.c
@@ -0,0 +1,87 @@
+/*-
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)cksum.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+/*
+ * I N _ C K S U M
+ *
+ * Checksum routine for Internet Protocol family headers (C Version)
+ *
+ * There is no profit in a specialized version of the checksum
+ * function for any machine where int's are 32 bits and shorts are 16.
+ *
+ * All timed packets are smaller than 32K shorts, so there is no need to
+ * worry about carries except at the end.
+ */
+int
+in_cksum(addr, len)
+ u_short *addr;
+ int len;
+{
+ register int nleft = len;
+ register u_short *w = addr;
+ register u_short answer;
+ register int sum = 0;
+
+ /*
+ * Our algorithm is simple, using a 32 bit accumulator (sum),
+ * we add sequential 16 bit words to it, and at the end, fold
+ * back all the carry bits from the top 16 bits into the lower
+ * 16 bits.
+ */
+ while( nleft > 1 ) {
+ sum += *w++;
+ nleft -= 2;
+ }
+
+ /* mop up an odd byte, if necessary */
+ if( nleft == 1 )
+ sum += (*(u_char *)w) << 8;
+
+ /*
+ * add back carry outs from top 16 bits to low 16 bits
+ */
+ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
+ sum += (sum >> 16); /* add carry */
+ answer = ~sum; /* truncate to 16 bits */
+ return (answer);
+}
diff --git a/usr.sbin/timed/timed/correct.c b/usr.sbin/timed/timed/correct.c
new file mode 100644
index 0000000..a5865a8
--- /dev/null
+++ b/usr.sbin/timed/timed/correct.c
@@ -0,0 +1,198 @@
+/*-
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)correct.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include "globals.h"
+#include <math.h>
+#include <sys/types.h>
+#include <sys/times.h>
+
+static void adjclock __P((struct timeval *));
+
+/*
+ * sends to the slaves the corrections for their clocks after fixing our
+ * own
+ */
+void
+correct(avdelta)
+ long avdelta;
+{
+ struct hosttbl *htp;
+ int corr;
+ struct timeval adjlocal, tmptv;
+ struct tsp to;
+ struct tsp *answer;
+
+ mstotvround(&adjlocal, avdelta);
+
+ for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) {
+ if (htp->delta != HOSTDOWN) {
+ corr = avdelta - htp->delta;
+/* If the other machine is off in the weeds, set its time directly.
+ * If a slave gets the wrong day, the original code would simply
+ * fix the minutes. If you fix a network partition, you can get
+ * into such situations.
+ */
+ if (htp->need_set
+ || corr >= MAXADJ*1000
+ || corr <= -MAXADJ*1000) {
+ htp->need_set = 0;
+ (void)gettimeofday(&tmptv,0);
+ timevaladd(&tmptv, &adjlocal);
+ to.tsp_time.tv_sec = tmptv.tv_sec;
+ to.tsp_time.tv_usec = tmptv.tv_usec;
+ to.tsp_type = TSP_SETTIME;
+ } else {
+ tmptv.tv_sec = to.tsp_time.tv_sec;
+ tmptv.tv_usec = to.tsp_time.tv_usec;
+ mstotvround(&tmptv, corr);
+ to.tsp_time.tv_sec = tmptv.tv_sec;
+ to.tsp_time.tv_usec = tmptv.tv_usec;
+ to.tsp_type = TSP_ADJTIME;
+ }
+ (void)strcpy(to.tsp_name, hostname);
+ answer = acksend(&to, &htp->addr, htp->name,
+ TSP_ACK, 0, 0);
+ if (!answer) {
+ htp->delta = HOSTDOWN;
+ syslog(LOG_WARNING,
+ "no reply to time correction from %s",
+ htp->name);
+ if (++htp->noanswer >= LOSTHOST) {
+ if (trace) {
+ fprintf(fd,
+ "purging %s for not answering\n",
+ htp->name);
+ (void)fflush(fd);
+ }
+ htp = remmach(htp);
+ }
+ }
+ }
+ }
+
+ /*
+ * adjust our own clock now that we are not sending it out
+ */
+ adjclock(&adjlocal);
+}
+
+
+static void
+adjclock(corr)
+ struct timeval *corr;
+{
+ static int passes = 0;
+ static int smoother = 0;
+ long delta; /* adjustment in usec */
+ long ndelta;
+ struct timeval now;
+ struct timeval adj;
+
+ if (!timerisset(corr))
+ return;
+
+ adj = *corr;
+ if (adj.tv_sec < MAXADJ && adj.tv_sec > - MAXADJ) {
+ delta = adj.tv_sec*1000000 + adj.tv_usec;
+ /* If the correction is less than the minimum round
+ * trip time for an ICMP packet, and thus
+ * less than the likely error in the measurement,
+ * do not do the entire correction. Do half
+ * or a quarter of it.
+ */
+
+ if (delta > -MIN_ROUND*1000
+ && delta < MIN_ROUND*1000) {
+ if (smoother <= 4)
+ smoother++;
+ ndelta = delta >> smoother;
+ if (trace)
+ fprintf(fd,
+ "trimming delta %ld usec to %ld\n",
+ delta, ndelta);
+ adj.tv_usec = ndelta;
+ adj.tv_sec = 0;
+ } else if (smoother > 0) {
+ smoother--;
+ }
+ if (0 > adjtime(corr, 0)) {
+ syslog(LOG_ERR, "adjtime: %m");
+ }
+ if (passes > 1
+ && (delta < -BIG_ADJ || delta > BIG_ADJ)) {
+ smoother = 0;
+ passes = 0;
+ syslog(LOG_WARNING,
+ "large time adjustment of %+.3f sec",
+ delta/1000000.0);
+ }
+ } else {
+ syslog(LOG_WARNING,
+ "clock correction %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");
+ }
+}
+
+
+/* adjust the time in a message by the time it
+ * spent in the queue
+ */
+void
+adj_msg_time(msg, now)
+ struct tsp *msg;
+ struct timeval *now;
+{
+ msg->tsp_time.tv_sec += (now->tv_sec - from_when.tv_sec);
+ msg->tsp_time.tv_usec += (now->tv_usec - from_when.tv_usec);
+
+ while (msg->tsp_time.tv_usec < 0) {
+ msg->tsp_time.tv_sec--;
+ msg->tsp_time.tv_usec += 1000000;
+ }
+ while (msg->tsp_time.tv_usec >= 1000000) {
+ msg->tsp_time.tv_sec++;
+ msg->tsp_time.tv_usec -= 1000000;
+ }
+}
diff --git a/usr.sbin/timed/timed/extern.h b/usr.sbin/timed/timed/extern.h
new file mode 100644
index 0000000..09dfaaa
--- /dev/null
+++ b/usr.sbin/timed/timed/extern.h
@@ -0,0 +1,89 @@
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)extern.h 8.1 (Berkeley) 6/6/93
+ */
+
+struct hosttbl;
+struct netinfo;
+struct sockaddr_in;
+struct timeval;
+struct tsp;
+
+struct hosttbl *addmach __P((char *, struct sockaddr_in *, struct netinfo *));
+struct hosttbl *findhost __P((char *));
+struct hosttbl *remmach __P((struct hosttbl *));
+
+struct tsp *readmsg __P((int,
+ char *, struct timeval *, struct netinfo *));
+struct tsp *acksend __P((struct tsp *,
+ struct sockaddr_in *, char *, int, struct netinfo *, int));
+
+void addnetname __P((char *));
+void adj_msg_time __P((struct tsp *, struct timeval *));
+void bytehostorder __P((struct tsp *));
+void bytenetorder __P((struct tsp *));
+void byteorder __P((struct tsp *));
+long casual __P((long, long));
+int cksum __P((u_short *, int));
+void correct __P((long));
+char *date __P((void));
+void doquit __P((struct tsp *));
+int election __P((struct netinfo *));
+void get_goodgroup __P((int));
+int good_host_name __P((char *));
+void ignoreack __P((void));
+int in_cksum __P((u_short *, int));
+void lookformaster __P((struct netinfo *));
+void makeslave __P((struct netinfo *));
+int master __P((void));
+void masterack __P((void));
+void masterup __P((struct netinfo *));
+int measure __P((u_long, u_long, char *, struct sockaddr_in *, int));
+void msterup __P((struct netinfo *));
+void mstotvround __P((struct timeval *, long));
+long networkdelta __P((void));
+void newslave __P((struct tsp *));
+void print __P((struct tsp *, struct sockaddr_in *));
+void prthp __P((clock_t));
+void rmnetmachs __P((struct netinfo *));
+void setstatus __P((void));
+int slave __P((void));
+void slaveack __P((void));
+void spreadtime __P((void));
+void suppress __P((struct sockaddr_in *, char *, struct netinfo *));
+void synch __P((long));
+void timevaladd __P((struct timeval *, struct timeval *));
+void timevalsub __P((struct timeval *, struct timeval *, struct timeval *));
+void traceoff __P((char *));
+void traceon __P((void));
+void xmit __P((int, u_int, struct sockaddr_in *));
diff --git a/usr.sbin/timed/timed/globals.h b/usr.sbin/timed/timed/globals.h
new file mode 100644
index 0000000..6fe0bd1
--- /dev/null
+++ b/usr.sbin/timed/timed/globals.h
@@ -0,0 +1,175 @@
+/*-
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)globals.h 8.1 (Berkeley) 6/6/93
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include <protocols/timed.h>
+#define SECHR (60*60)
+#define SECDAY (24*SECHR)
+
+extern int sock;
+
+/* Best expected round trip for a measurement.
+ * This is essentially the number of milliseconds per CPU tick (CLK_TCK?).
+ * All delays shorter than this are usually reported as 0.
+ */
+#define MIN_ROUND ((1000-1)/CLK_TCK)
+
+
+#define SAMPLEINTVL 240 /* synch() freq for master in sec */
+#define MAXADJ 20 /* max adjtime() correction in sec */
+
+#define MAX_TRIM 3000000 /* max drift in nsec/sec, 0.3% */
+#define BIG_ADJ (MAX_TRIM/1000*SAMPLEINTVL*2) /* max good adj */
+
+#define MINTOUT 360 /* election delays, 6-15 minutes */
+#define MAXTOUT 900
+
+#define BAD_STATUS (-1)
+#define GOOD 1
+#define UNREACHABLE 2
+#define NONSTDTIME 3
+#define HOSTDOWN 0x7fffffff
+
+#define OFF 0
+#define ON 1
+
+#define MAX_HOPCNT 10 /* max value for tsp_hpcnt */
+
+#define LOSTHOST 3 /* forget after this many failures */
+
+#define VALID_RANGE (MAXADJ*1000) /* good times in milliseconds */
+#define GOOD_RANGE (MIN_ROUND*2)
+#define VGOOD_RANGE (MIN_ROUND-1)
+
+
+/*
+ * Global and per-network states.
+ */
+#define NOMASTER 0 /* no good master */
+#define SLAVE 1
+#define MASTER 2
+#define IGNORE 4
+#define ALL (SLAVE|MASTER|IGNORE)
+#define SUBMASTER (SLAVE|MASTER)
+
+#define NHOSTS 1013 /* max of hosts controlled by timed
+ * This must be a prime number.
+ */
+struct hosttbl {
+ struct hosttbl *h_bak; /* hash chain */
+ struct hosttbl *h_fwd;
+ struct hosttbl *l_bak; /* "sequential" list */
+ struct hosttbl *l_fwd;
+ struct netinfo *ntp;
+ struct sockaddr_in addr;
+ char name[MAXHOSTNAMELEN];
+ u_char head; /* 1=head of hash chain */
+ u_char good; /* 0=trusted host, for averaging */
+ u_char noanswer; /* count of failures to answer */
+ u_char need_set; /* need a SETTIME */
+ u_short seq;
+ long delta;
+};
+
+/* closed hash table with internal chaining */
+extern struct hosttbl hosttbl[NHOSTS+1];
+#define self hosttbl[0]
+#define hostname (self.name)
+
+
+struct netinfo {
+ struct netinfo *next;
+ struct in_addr net;
+ u_int32_t mask;
+ struct in_addr my_addr;
+ struct sockaddr_in dest_addr; /* broadcast addr or point-point */
+ long status;
+ struct timeval slvwait; /* delay before sending our time */
+ int quit_count; /* recent QUITs */
+};
+
+#include "extern.h"
+
+#define tvtomsround(tv) ((tv).tv_sec*1000 + ((tv).tv_usec + 500)/1000)
+
+extern struct netinfo *nettab;
+extern int status;
+extern int trace;
+extern int sock;
+extern struct sockaddr_in from;
+extern struct timeval from_when; /* when the last msg arrived */
+extern u_short sequence; /* TSP message sequence number */
+extern struct netinfo *fromnet, *slavenet;
+extern FILE *fd;
+extern long delay1, delay2;
+extern int nslavenets; /* nets were I could be a slave */
+extern int nmasternets; /* nets were I could be a master */
+extern int nignorednets; /* ignored nets */
+extern int nnets; /* nets I am connected to */
+
+
+#define trace_msg(msg) {if (trace) fprintf(fd, msg);}
+
+#define trace_sendto_err(addr) { \
+ int st_errno = errno; \
+ syslog(LOG_ERR, "%s %d: sendto %s: %m", \
+ __FILE__, __LINE__, inet_ntoa(addr)); \
+ if (trace) \
+ fprintf(fd, "%s %d: sendto %s: %d", __FILE__, __LINE__, \
+ inet_ntoa(addr), st_errno); \
+}
+
+
+# define max(a,b) (a<b ? b : a)
+# define min(a,b) (a>b ? b : a)
+# define abs(x) (x>=0 ? x : -(x))
diff --git a/usr.sbin/timed/timed/master.c b/usr.sbin/timed/timed/master.c
new file mode 100644
index 0000000..fb5014b
--- /dev/null
+++ b/usr.sbin/timed/timed/master.c
@@ -0,0 +1,853 @@
+/*-
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)master.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include "globals.h"
+#include <sys/file.h>
+#include <sys/types.h>
+#include <sys/times.h>
+#include <setjmp.h>
+#include <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 *));
+
+extern void logwtmp __P((char *, char *, char *));
+
+/*
+ * The main function of `master' is to periodically compute the differences
+ * (deltas) between its clock and the clocks of the slaves, to compute the
+ * network average delta, and to send to the slaves the differences between
+ * their individual deltas and the network delta.
+ * While waiting, it receives messages from the slaves (i.e. requests for
+ * master's name, remote requests to set the network time, ...), and
+ * takes the appropriate action.
+ */
+int
+master()
+{
+ struct hosttbl *htp;
+ long pollingtime;
+#define POLLRATE 4
+ int polls;
+ struct timeval wait, ntime;
+ time_t tsp_time_sec;
+ struct tsp *msg, *answer, to;
+ char newdate[32];
+ struct sockaddr_in taddr;
+ char tname[MAXHOSTNAMELEN];
+ struct netinfo *ntp;
+ int i;
+
+ syslog(LOG_NOTICE, "This machine is master");
+ if (trace)
+ fprintf(fd, "This machine is master\n");
+ for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
+ if (ntp->status == MASTER)
+ masterup(ntp);
+ }
+ (void)gettimeofday(&ntime, 0);
+ pollingtime = ntime.tv_sec+3;
+ if (justquit)
+ polls = 0;
+ else
+ polls = POLLRATE-1;
+
+/* Process all outstanding messages before spending the long time necessary
+ * to update all timers.
+ */
+loop:
+ (void)gettimeofday(&ntime, 0);
+ wait.tv_sec = pollingtime - ntime.tv_sec;
+ if (wait.tv_sec < 0)
+ wait.tv_sec = 0;
+ wait.tv_usec = 0;
+ msg = readmsg(TSP_ANY, ANYADDR, &wait, 0);
+ if (!msg) {
+ (void)gettimeofday(&ntime, 0);
+ if (ntime.tv_sec >= pollingtime) {
+ pollingtime = ntime.tv_sec + SAMPLEINTVL;
+ get_goodgroup(0);
+
+/* If a bogus master told us to quit, we can have decided to ignore a
+ * network. Therefore, periodically try to take over everything.
+ */
+ polls = (polls + 1) % POLLRATE;
+ if (0 == polls && nignorednets > 0) {
+ trace_msg("Looking for nets to re-master\n");
+ for (ntp = nettab; ntp; ntp = ntp->next) {
+ if (ntp->status == IGNORE
+ || ntp->status == NOMASTER) {
+ lookformaster(ntp);
+ if (ntp->status == MASTER) {
+ masterup(ntp);
+ polls = POLLRATE-1;
+ }
+ }
+ if (ntp->status == MASTER
+ && --ntp->quit_count < 0)
+ ntp->quit_count = 0;
+ }
+ if (polls != 0)
+ setstatus();
+ }
+
+ synch(0L);
+
+ for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
+ to.tsp_type = TSP_LOOP;
+ to.tsp_vers = TSPVERSION;
+ to.tsp_seq = sequence++;
+ to.tsp_hopcnt = MAX_HOPCNT;
+ (void)strcpy(to.tsp_name, hostname);
+ bytenetorder(&to);
+ if (sendto(sock, (char *)&to,
+ sizeof(struct tsp), 0,
+ (struct sockaddr*)&ntp->dest_addr,
+ sizeof(ntp->dest_addr)) < 0) {
+ trace_sendto_err(ntp->dest_addr.sin_addr);
+ }
+ }
+ }
+
+
+ } else {
+ switch (msg->tsp_type) {
+
+ case TSP_MASTERREQ:
+ break;
+
+ case TSP_SLAVEUP:
+ newslave(msg);
+ break;
+
+ case TSP_SETDATE:
+ /*
+ * XXX check to see it is from ourself
+ */
+ tsp_time_sec = msg->tsp_time.tv_sec;
+ (void)strcpy(newdate, ctime(&tsp_time_sec));
+ if (!good_host_name(msg->tsp_name)) {
+ syslog(LOG_NOTICE,
+ "attempted date change by %s to %s",
+ msg->tsp_name, newdate);
+ spreadtime();
+ break;
+ }
+
+ mchgdate(msg);
+ (void)gettimeofday(&ntime, 0);
+ pollingtime = ntime.tv_sec + SAMPLEINTVL;
+ break;
+
+ case TSP_SETDATEREQ:
+ if (!fromnet || fromnet->status != MASTER)
+ break;
+ tsp_time_sec = msg->tsp_time.tv_sec;
+ (void)strcpy(newdate, ctime(&tsp_time_sec));
+ htp = findhost(msg->tsp_name);
+ if (htp == 0) {
+ syslog(LOG_ERR,
+ "attempted SET DATEREQ by uncontrolled %s to %s",
+ msg->tsp_name, newdate);
+ break;
+ }
+ if (htp->seq == msg->tsp_seq)
+ break;
+ htp->seq = msg->tsp_seq;
+ if (!htp->good) {
+ syslog(LOG_NOTICE,
+ "attempted SET DATEREQ by untrusted %s to %s",
+ msg->tsp_name, newdate);
+ spreadtime();
+ break;
+ }
+
+ mchgdate(msg);
+ (void)gettimeofday(&ntime, 0);
+ pollingtime = ntime.tv_sec + SAMPLEINTVL;
+ break;
+
+ case TSP_MSITE:
+ xmit(TSP_ACK, msg->tsp_seq, &from);
+ break;
+
+ case TSP_MSITEREQ:
+ break;
+
+ case TSP_TRACEON:
+ traceon();
+ break;
+
+ case TSP_TRACEOFF:
+ traceoff("Tracing ended at %s\n");
+ break;
+
+ case TSP_ELECTION:
+ if (!fromnet)
+ break;
+ if (fromnet->status == MASTER) {
+ pollingtime = 0;
+ (void)addmach(msg->tsp_name, &from,fromnet);
+ }
+ taddr = from;
+ (void)strcpy(tname, msg->tsp_name);
+ to.tsp_type = TSP_QUIT;
+ (void)strcpy(to.tsp_name, hostname);
+ answer = acksend(&to, &taddr, tname,
+ TSP_ACK, 0, 1);
+ if (answer == NULL) {
+ syslog(LOG_ERR, "election error by %s",
+ tname);
+ }
+ break;
+
+ case TSP_CONFLICT:
+ /*
+ * After a network partition, there can be
+ * more than one master: the first slave to
+ * come up will notify here the situation.
+ */
+ if (!fromnet || fromnet->status != MASTER)
+ break;
+ (void)strcpy(to.tsp_name, hostname);
+
+ /* The other master often gets into the same state,
+ * with boring results if we stay at it forever.
+ */
+ ntp = fromnet; /* (acksend() can leave fromnet=0 */
+ for (i = 0; i < 3; i++) {
+ to.tsp_type = TSP_RESOLVE;
+ (void)strcpy(to.tsp_name, hostname);
+ answer = acksend(&to, &ntp->dest_addr,
+ ANYADDR, TSP_MASTERACK,
+ ntp, 0);
+ if (!answer)
+ break;
+ htp = addmach(answer->tsp_name,&from,ntp);
+ to.tsp_type = TSP_QUIT;
+ msg = acksend(&to, &htp->addr, htp->name,
+ TSP_ACK, 0, htp->noanswer);
+ if (msg == NULL) {
+ syslog(LOG_ERR,
+ "no response from %s to CONFLICT-QUIT",
+ htp->name);
+ }
+ }
+ masterup(ntp);
+ pollingtime = 0;
+ break;
+
+ case TSP_RESOLVE:
+ if (!fromnet || fromnet->status != MASTER)
+ break;
+ /*
+ * do not want to call synch() while waiting
+ * to be killed!
+ */
+ (void)gettimeofday(&ntime, (struct timezone *)0);
+ pollingtime = ntime.tv_sec + SAMPLEINTVL;
+ break;
+
+ case TSP_QUIT:
+ doquit(msg); /* become a slave */
+ break;
+
+ case TSP_LOOP:
+ if (!fromnet || fromnet->status != MASTER
+ || !strcmp(msg->tsp_name, hostname))
+ break;
+ /*
+ * We should not have received this from a net
+ * we are master on. There must be two masters.
+ */
+ htp = addmach(msg->tsp_name, &from,fromnet);
+ to.tsp_type = TSP_QUIT;
+ (void)strcpy(to.tsp_name, hostname);
+ answer = acksend(&to, &htp->addr, htp->name,
+ TSP_ACK, 0, 1);
+ if (!answer) {
+ syslog(LOG_WARNING,
+ "loop breakage: no reply from %s=%s to QUIT",
+ htp->name, inet_ntoa(htp->addr.sin_addr));
+ (void)remmach(htp);
+ }
+
+ case TSP_TEST:
+ if (trace) {
+ fprintf(fd,
+ "\tnets = %d, masters = %d, slaves = %d, ignored = %d\n",
+ nnets, nmasternets, nslavenets, nignorednets);
+ setstatus();
+ }
+ pollingtime = 0;
+ polls = POLLRATE-1;
+ break;
+
+ default:
+ if (trace) {
+ fprintf(fd, "garbage message: ");
+ print(msg, &from);
+ }
+ break;
+ }
+ }
+ goto loop;
+}
+
+
+/*
+ * change the system date on the master
+ */
+static void
+mchgdate(msg)
+ struct tsp *msg;
+{
+ char tname[MAXHOSTNAMELEN];
+ char olddate[32];
+ struct timeval otime, ntime, tmptv;
+
+ (void)strcpy(tname, msg->tsp_name);
+
+ xmit(TSP_DATEACK, msg->tsp_seq, &from);
+
+ (void)strcpy(olddate, date());
+
+ /* adjust time for residence on the queue */
+ (void)gettimeofday(&otime, 0);
+ adj_msg_time(msg,&otime);
+
+ tmptv.tv_sec = msg->tsp_time.tv_sec;
+ tmptv.tv_usec = msg->tsp_time.tv_usec;
+ timevalsub(&ntime, &tmptv, &otime);
+ if (ntime.tv_sec < MAXADJ && ntime.tv_sec > -MAXADJ) {
+ /*
+ * do not change the clock if we can adjust it
+ */
+ dictate = 3;
+ synch(tvtomsround(ntime));
+ } else {
+ logwtmp("|", "date", "");
+ (void)settimeofday(&tmptv, 0);
+ logwtmp("{", "date", "");
+ spreadtime();
+ }
+
+ syslog(LOG_NOTICE, "date changed by %s from %s",
+ tname, olddate);
+}
+
+
+/*
+ * synchronize all of the slaves
+ */
+void
+synch(mydelta)
+ long mydelta;
+{
+ struct hosttbl *htp;
+ int measure_status;
+ struct timeval check, stop, wait;
+
+ if (slvcount > 0) {
+ if (trace)
+ fprintf(fd, "measurements starting at %s\n", date());
+ (void)gettimeofday(&check, 0);
+ for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) {
+ if (htp->noanswer != 0) {
+ measure_status = measure(500, 100,
+ htp->name,
+ &htp->addr,0);
+ } else {
+ measure_status = measure(3000, 100,
+ htp->name,
+ &htp->addr,0);
+ }
+ if (measure_status != GOOD) {
+ /* The slave did not respond. We have
+ * just wasted lots of time on it.
+ */
+ htp->delta = HOSTDOWN;
+ if (++htp->noanswer >= LOSTHOST) {
+ if (trace) {
+ fprintf(fd,
+ "purging %s for not answering ICMP\n",
+ htp->name);
+ (void)fflush(fd);
+ }
+ htp = remmach(htp);
+ }
+ } else {
+ htp->delta = measure_delta;
+ }
+ (void)gettimeofday(&stop, 0);
+ timevalsub(&stop, &stop, &check);
+ if (stop.tv_sec >= 1) {
+ if (trace)
+ (void)fflush(fd);
+ /*
+ * ack messages periodically
+ */
+ wait.tv_sec = 0;
+ wait.tv_usec = 0;
+ if (0 != readmsg(TSP_TRACEON,ANYADDR,
+ &wait,0))
+ traceon();
+ (void)gettimeofday(&check, 0);
+ }
+ }
+ if (trace)
+ fprintf(fd, "measurements finished at %s\n", date());
+ }
+ if (!(status & SLAVE)) {
+ if (!dictate) {
+ mydelta = networkdelta();
+ } else {
+ dictate--;
+ }
+ }
+ if (trace && (mydelta != 0 || (status & SLAVE)))
+ fprintf(fd,"local correction of %ld ms.\n", mydelta);
+ correct(mydelta);
+}
+
+/*
+ * sends the time to each slave after the master
+ * has received the command to set the network time
+ */
+void
+spreadtime()
+{
+ struct hosttbl *htp;
+ struct tsp to;
+ struct tsp *answer;
+ struct timeval tmptv;
+
+/* Do not listen to the consensus after forcing the time. This is because
+ * the consensus takes a while to reach the time we are dictating.
+ */
+ dictate = 2;
+ for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) {
+ to.tsp_type = TSP_SETTIME;
+ (void)strcpy(to.tsp_name, hostname);
+ (void)gettimeofday(&tmptv, 0);
+ to.tsp_time.tv_sec = tmptv.tv_sec;
+ to.tsp_time.tv_usec = tmptv.tv_usec;
+ answer = acksend(&to, &htp->addr, htp->name,
+ TSP_ACK, 0, htp->noanswer);
+ if (answer == 0) {
+ /* We client does not respond, then we have
+ * just wasted lots of time on it.
+ */
+ syslog(LOG_WARNING,
+ "no reply to SETTIME from %s", htp->name);
+ if (++htp->noanswer >= LOSTHOST) {
+ if (trace) {
+ fprintf(fd,
+ "purging %s for not answering",
+ htp->name);
+ (void)fflush(fd);
+ }
+ htp = remmach(htp);
+ }
+ }
+ }
+}
+
+void
+prthp(delta)
+ clock_t delta;
+{
+ static time_t next_time;
+ time_t this_time;
+ struct tms tm;
+ struct hosttbl *htp;
+ int length, l;
+ int i;
+
+ if (!fd) /* quit if tracing already off */
+ return;
+
+ this_time = times(&tm);
+ if (this_time + delta < next_time)
+ return;
+ next_time = this_time + CLK_TCK;
+
+ fprintf(fd, "host table: %d entries at %s\n", slvcount, date());
+ htp = self.l_fwd;
+ length = 1;
+ for (i = 1; i <= slvcount; i++, htp = htp->l_fwd) {
+ l = strlen(htp->name) + 1;
+ if (length+l >= 80) {
+ fprintf(fd, "\n");
+ length = 0;
+ }
+ length += l;
+ fprintf(fd, " %s", htp->name);
+ }
+ fprintf(fd, "\n");
+}
+
+
+static struct hosttbl *newhost_hash;
+static struct hosttbl *lasthfree = &hosttbl[0];
+
+
+struct hosttbl * /* answer or 0 */
+findhost(name)
+ char *name;
+{
+ int i, j;
+ struct hosttbl *htp;
+ char *p;
+
+ j= 0;
+ for (p = name, i = 0; i < 8 && *p != '\0'; i++, p++)
+ j = (j << 2) ^ *p;
+ newhost_hash = &hosttbl[j % NHOSTS];
+
+ htp = newhost_hash;
+ if (htp->name[0] == '\0')
+ return(0);
+ do {
+ if (!strcmp(name, htp->name))
+ return(htp);
+ htp = htp->h_fwd;
+ } while (htp != newhost_hash);
+ return(0);
+}
+
+/*
+ * add a host to the list of controlled machines if not already there
+ */
+struct hosttbl *
+addmach(name, addr, ntp)
+ char *name;
+ struct sockaddr_in *addr;
+ struct netinfo *ntp;
+{
+ struct hosttbl *ret, *p, *b, *f;
+
+ ret = findhost(name);
+ if (ret == 0) {
+ if (slvcount >= NHOSTS) {
+ if (trace) {
+ fprintf(fd, "no more slots in host table\n");
+ prthp(CLK_TCK);
+ }
+ syslog(LOG_ERR, "no more slots in host table");
+ Mflag = 0;
+ longjmp(jmpenv, 2); /* give up and be a slave */
+ }
+
+ /* if our home hash slot is occupied, find a free entry
+ * in the hash table
+ */
+ if (newhost_hash->name[0] != '\0') {
+ do {
+ ret = lasthfree;
+ if (++lasthfree > &hosttbl[NHOSTS])
+ lasthfree = &hosttbl[1];
+ } while (ret->name[0] != '\0');
+
+ if (!newhost_hash->head) {
+ /* Move an interloper using our home. Use
+ * scratch pointers in case the new head is
+ * pointing to itself.
+ */
+ f = newhost_hash->h_fwd;
+ b = newhost_hash->h_bak;
+ f->h_bak = ret;
+ b->h_fwd = ret;
+ f = newhost_hash->l_fwd;
+ b = newhost_hash->l_bak;
+ f->l_bak = ret;
+ b->l_fwd = ret;
+ bcopy(newhost_hash,ret,sizeof(*ret));
+ ret = newhost_hash;
+ ret->head = 1;
+ ret->h_fwd = ret;
+ ret->h_bak = ret;
+ } else {
+ /* link to an existing chain in our home
+ */
+ ret->head = 0;
+ p = newhost_hash->h_bak;
+ ret->h_fwd = newhost_hash;
+ ret->h_bak = p;
+ p->h_fwd = ret;
+ newhost_hash->h_bak = ret;
+ }
+ } else {
+ ret = newhost_hash;
+ ret->head = 1;
+ ret->h_fwd = ret;
+ ret->h_bak = ret;
+ }
+ ret->addr = *addr;
+ ret->ntp = ntp;
+ (void)strncpy(ret->name, name, sizeof(ret->name));
+ ret->good = good_host_name(name);
+ ret->l_fwd = &self;
+ ret->l_bak = self.l_bak;
+ self.l_bak->l_fwd = ret;
+ self.l_bak = ret;
+ slvcount++;
+
+ ret->noanswer = 0;
+ ret->need_set = 1;
+
+ } else {
+ ret->noanswer = (ret->noanswer != 0);
+ }
+
+ /* need to clear sequence number anyhow */
+ ret->seq = 0;
+ return(ret);
+}
+
+/*
+ * remove the machine with the given index in the host table.
+ */
+struct hosttbl *
+remmach(htp)
+ struct hosttbl *htp;
+{
+ struct hosttbl *lprv, *hnxt, *f, *b;
+
+ if (trace)
+ fprintf(fd, "remove %s\n", htp->name);
+
+ /* get out of the lists */
+ htp->l_fwd->l_bak = lprv = htp->l_bak;
+ htp->l_bak->l_fwd = htp->l_fwd;
+ htp->h_fwd->h_bak = htp->h_bak;
+ htp->h_bak->h_fwd = hnxt = htp->h_fwd;
+
+ /* If we are in the home slot, pull up the chain */
+ if (htp->head && hnxt != htp) {
+ if (lprv == hnxt)
+ lprv = htp;
+
+ /* Use scratch pointers in case the new head is pointing to
+ * itself.
+ */
+ f = hnxt->h_fwd;
+ b = hnxt->h_bak;
+ f->h_bak = htp;
+ b->h_fwd = htp;
+ f = hnxt->l_fwd;
+ b = hnxt->l_bak;
+ f->l_bak = htp;
+ b->l_fwd = htp;
+ hnxt->head = 1;
+ bcopy(hnxt, htp, sizeof(*htp));
+ lasthfree = hnxt;
+ } else {
+ lasthfree = htp;
+ }
+
+ lasthfree->name[0] = '\0';
+ lasthfree->h_fwd = 0;
+ lasthfree->l_fwd = 0;
+ slvcount--;
+
+ return lprv;
+}
+
+
+/*
+ * Remove all the machines from the host table that exist on the given
+ * network. This is called when a master transitions to a slave on a
+ * given network.
+ */
+void
+rmnetmachs(ntp)
+ struct netinfo *ntp;
+{
+ struct hosttbl *htp;
+
+ if (trace)
+ prthp(CLK_TCK);
+ for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) {
+ if (ntp == htp->ntp)
+ htp = remmach(htp);
+ }
+ if (trace)
+ prthp(CLK_TCK);
+}
+
+void
+masterup(net)
+ struct netinfo *net;
+{
+ xmit(TSP_MASTERUP, 0, &net->dest_addr);
+
+ /*
+ * Do not tell new slaves our time for a while. This ensures
+ * we do not tell them to start using our time, before we have
+ * found a good master.
+ */
+ (void)gettimeofday(&net->slvwait, 0);
+}
+
+void
+newslave(msg)
+ struct tsp *msg;
+{
+ struct hosttbl *htp;
+ struct tsp *answer, to;
+ struct timeval now, tmptv;
+
+ if (!fromnet || fromnet->status != MASTER)
+ return;
+
+ htp = addmach(msg->tsp_name, &from,fromnet);
+ htp->seq = msg->tsp_seq;
+ if (trace)
+ prthp(0);
+
+ /*
+ * If we are stable, send our time to the slave.
+ * Do not go crazy if the date has been changed.
+ */
+ (void)gettimeofday(&now, 0);
+ if (now.tv_sec >= fromnet->slvwait.tv_sec+3
+ || now.tv_sec < fromnet->slvwait.tv_sec) {
+ to.tsp_type = TSP_SETTIME;
+ (void)strcpy(to.tsp_name, hostname);
+ (void)gettimeofday(&tmptv, 0);
+ to.tsp_time.tv_sec = tmptv.tv_sec;
+ to.tsp_time.tv_usec = tmptv.tv_usec;
+ answer = acksend(&to, &htp->addr,
+ htp->name, TSP_ACK,
+ 0, htp->noanswer);
+ if (answer) {
+ htp->need_set = 0;
+ } else {
+ syslog(LOG_WARNING,
+ "no reply to initial SETTIME from %s",
+ htp->name);
+ htp->noanswer = LOSTHOST;
+ }
+ }
+}
+
+
+/*
+ * react to a TSP_QUIT:
+ */
+void
+doquit(msg)
+ struct tsp *msg;
+{
+ if (fromnet->status == MASTER) {
+ if (!good_host_name(msg->tsp_name)) {
+ if (fromnet->quit_count <= 0) {
+ syslog(LOG_NOTICE,"untrusted %s told us QUIT",
+ msg->tsp_name);
+ suppress(&from, msg->tsp_name, fromnet);
+ fromnet->quit_count = 1;
+ return;
+ }
+ syslog(LOG_NOTICE, "untrusted %s told us QUIT twice",
+ msg->tsp_name);
+ fromnet->quit_count = 2;
+ fromnet->status = NOMASTER;
+ } else {
+ fromnet->status = SLAVE;
+ }
+ rmnetmachs(fromnet);
+ longjmp(jmpenv, 2); /* give up and be a slave */
+
+ } else {
+ if (!good_host_name(msg->tsp_name)) {
+ syslog(LOG_NOTICE, "untrusted %s told us QUIT",
+ msg->tsp_name);
+ fromnet->quit_count = 2;
+ }
+ }
+}
+
+void
+traceon()
+{
+ if (!fd) {
+ fd = fopen(_PATH_TIMEDLOG, "w");
+ if (!fd) {
+ trace = 0;
+ return;
+ }
+ fprintf(fd,"Tracing started at %s\n", date());
+ }
+ trace = 1;
+ get_goodgroup(1);
+ setstatus();
+ prthp(CLK_TCK);
+}
+
+
+void
+traceoff(msg)
+ char *msg;
+{
+ get_goodgroup(1);
+ setstatus();
+ prthp(CLK_TCK);
+ if (trace) {
+ fprintf(fd, msg, date());
+ (void)fclose(fd);
+ fd = 0;
+ }
+#ifdef GPROF
+ moncontrol(0);
+ _mcleanup();
+ moncontrol(1);
+#endif
+ trace = OFF;
+}
diff --git a/usr.sbin/timed/timed/measure.c b/usr.sbin/timed/timed/measure.c
new file mode 100644
index 0000000..3856a34
--- /dev/null
+++ b/usr.sbin/timed/timed/measure.c
@@ -0,0 +1,346 @@
+/*-
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)measure.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include "globals.h"
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+
+#define MSEC_DAY (SECDAY*1000)
+
+#define PACKET_IN 1024
+
+#define MSGS 5 /* timestamps to average */
+#define TRIALS 10 /* max # of timestamps sent */
+
+extern int sock_raw;
+
+int measure_delta;
+
+static n_short seqno = 0;
+
+/*
+ * Measures the differences between machines' clocks using
+ * ICMP timestamp messages.
+ */
+int /* status val defined in globals.h */
+measure(maxmsec, wmsec, hname, addr, print)
+ u_long maxmsec; /* wait this many msec at most */
+ u_long wmsec; /* msec to wait for an answer */
+ char *hname;
+ struct sockaddr_in *addr;
+ int print; /* print complaints on stderr */
+{
+ int length;
+ int measure_status;
+ int rcvcount, trials;
+ int cc, count;
+ fd_set ready;
+ long sendtime, recvtime, histime1, histime2;
+ long idelta, odelta, total;
+ long min_idelta, min_odelta;
+ struct timeval tdone, tcur, ttrans, twait, tout;
+ u_char packet[PACKET_IN], opacket[64];
+ register struct icmp *icp = (struct icmp *) packet;
+ register struct icmp *oicp = (struct icmp *) opacket;
+ struct ip *ip = (struct ip *) packet;
+
+ min_idelta = min_odelta = 0x7fffffff;
+ measure_status = HOSTDOWN;
+ measure_delta = HOSTDOWN;
+ errno = 0;
+
+ /* open raw socket used to measure time differences */
+ if (sock_raw < 0) {
+ sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
+ if (sock_raw < 0) {
+ syslog(LOG_ERR, "opening raw socket: %m");
+ goto quit;
+ }
+ }
+
+
+ /*
+ * empty the icmp input queue
+ */
+ FD_ZERO(&ready);
+ for (;;) {
+ tout.tv_sec = tout.tv_usec = 0;
+ FD_SET(sock_raw, &ready);
+ if (select(sock_raw+1, &ready, 0,0, &tout)) {
+ length = sizeof(struct sockaddr_in);
+ cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0,
+ 0,&length);
+ if (cc < 0)
+ goto quit;
+ continue;
+ }
+ break;
+ }
+
+ /*
+ * Choose the smallest transmission time in each of the two
+ * directions. Use these two latter quantities to compute the delta
+ * between the two clocks.
+ */
+
+ oicp->icmp_type = ICMP_TSTAMP;
+ oicp->icmp_code = 0;
+ oicp->icmp_id = getpid();
+ oicp->icmp_rtime = 0;
+ oicp->icmp_ttime = 0;
+ oicp->icmp_seq = seqno;
+
+ FD_ZERO(&ready);
+
+ (void)gettimeofday(&tdone, 0);
+ mstotvround(&tout, maxmsec);
+ timevaladd(&tdone, &tout); /* when we give up */
+
+ mstotvround(&twait, wmsec);
+
+ rcvcount = 0;
+ trials = 0;
+ while (rcvcount < MSGS) {
+ (void)gettimeofday(&tcur, 0);
+
+ /*
+ * keep sending until we have sent the max
+ */
+ if (trials < TRIALS) {
+ trials++;
+ oicp->icmp_otime = htonl((tcur.tv_sec % SECDAY) * 1000
+ + tcur.tv_usec / 1000);
+ oicp->icmp_cksum = 0;
+ oicp->icmp_cksum = in_cksum((u_short*)oicp,
+ sizeof(*oicp));
+
+ count = sendto(sock_raw, opacket, sizeof(*oicp), 0,
+ (struct sockaddr*)addr,
+ sizeof(struct sockaddr));
+ if (count < 0) {
+ if (measure_status == HOSTDOWN)
+ measure_status = UNREACHABLE;
+ goto quit;
+ }
+ ++oicp->icmp_seq;
+
+ ttrans = tcur;
+ timevaladd(&ttrans, &twait);
+ } else {
+ ttrans = tdone;
+ }
+
+ while (rcvcount < trials) {
+ timevalsub(&tout, &ttrans, &tcur);
+ if (tout.tv_sec < 0)
+ tout.tv_sec = 0;
+
+ FD_SET(sock_raw, &ready);
+ count = select(sock_raw+1, &ready, (fd_set *)0,
+ (fd_set *)0, &tout);
+ (void)gettimeofday(&tcur, (struct timezone *)0);
+ if (count <= 0)
+ break;
+
+ length = sizeof(struct sockaddr_in);
+ cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0,
+ 0,&length);
+ if (cc < 0)
+ goto quit;
+
+ /*
+ * got something. See if it is ours
+ */
+ icp = (struct icmp *)(packet + (ip->ip_hl << 2));
+ if (cc < sizeof(*ip)
+ || icp->icmp_type != ICMP_TSTAMPREPLY
+ || icp->icmp_id != oicp->icmp_id
+ || icp->icmp_seq < seqno
+ || icp->icmp_seq >= oicp->icmp_seq)
+ continue;
+
+
+ sendtime = ntohl(icp->icmp_otime);
+ recvtime = ((tcur.tv_sec % SECDAY) * 1000 +
+ tcur.tv_usec / 1000);
+
+ total = recvtime-sendtime;
+ if (total < 0) /* do not hassle midnight */
+ continue;
+
+ rcvcount++;
+ histime1 = ntohl(icp->icmp_rtime);
+ histime2 = ntohl(icp->icmp_ttime);
+ /*
+ * a host using a time format different from
+ * msec. since midnight UT (as per RFC792) should
+ * set the high order bit of the 32-bit time
+ * value it transmits.
+ */
+ if ((histime1 & 0x80000000) != 0) {
+ measure_status = NONSTDTIME;
+ goto quit;
+ }
+ measure_status = GOOD;
+
+ idelta = recvtime-histime2;
+ odelta = histime1-sendtime;
+
+ /* do not be confused by midnight */
+ if (idelta < -MSEC_DAY/2) idelta += MSEC_DAY;
+ else if (idelta > MSEC_DAY/2) idelta -= MSEC_DAY;
+
+ if (odelta < -MSEC_DAY/2) odelta += MSEC_DAY;
+ else if (odelta > MSEC_DAY/2) odelta -= MSEC_DAY;
+
+ /* save the quantization error so that we can get a
+ * measurement finer than our system clock.
+ */
+ if (total < MIN_ROUND) {
+ measure_delta = (odelta - idelta)/2;
+ goto quit;
+ }
+
+ if (idelta < min_idelta)
+ min_idelta = idelta;
+ if (odelta < min_odelta)
+ min_odelta = odelta;
+
+ measure_delta = (min_odelta - min_idelta)/2;
+ }
+
+ if (tcur.tv_sec > tdone.tv_sec
+ || (tcur.tv_sec == tdone.tv_sec
+ && tcur.tv_usec >= tdone.tv_usec))
+ break;
+ }
+
+quit:
+ seqno += TRIALS; /* allocate our sequence numbers */
+
+ /*
+ * If no answer is received for TRIALS consecutive times,
+ * the machine is assumed to be down
+ */
+ if (measure_status == GOOD) {
+ if (trace) {
+ fprintf(fd,
+ "measured delta %4d, %d trials to %-15s %s\n",
+ measure_delta, trials,
+ inet_ntoa(addr->sin_addr), hname);
+ }
+ } else if (print) {
+ if (errno != 0)
+ warn("measure %s", hname);
+ } else {
+ if (errno != 0) {
+ syslog(LOG_ERR, "measure %s: %m", hname);
+ } else {
+ syslog(LOG_ERR, "measure: %s did not respond", hname);
+ }
+ if (trace) {
+ fprintf(fd,
+ "measure: %s failed after %d trials\n",
+ hname, trials);
+ (void)fflush(fd);
+ }
+ }
+
+ return(measure_status);
+}
+
+
+
+
+
+/*
+ * round a number of milliseconds into a struct timeval
+ */
+void
+mstotvround(res, x)
+ struct timeval *res;
+ long x;
+{
+ if (x < 0)
+ x = -((-x + 3)/5);
+ else
+ x = (x+3)/5;
+ x *= 5;
+ res->tv_sec = x/1000;
+ res->tv_usec = (x-res->tv_sec*1000)*1000;
+ if (res->tv_usec < 0) {
+ res->tv_usec += 1000000;
+ res->tv_sec--;
+ }
+}
+
+void
+timevaladd(tv1, tv2)
+ struct timeval *tv1, *tv2;
+{
+ tv1->tv_sec += tv2->tv_sec;
+ tv1->tv_usec += tv2->tv_usec;
+ if (tv1->tv_usec >= 1000000) {
+ tv1->tv_sec++;
+ tv1->tv_usec -= 1000000;
+ }
+ if (tv1->tv_usec < 0) {
+ tv1->tv_sec--;
+ tv1->tv_usec += 1000000;
+ }
+}
+
+void
+timevalsub(res, tv1, tv2)
+ struct timeval *res, *tv1, *tv2;
+{
+ res->tv_sec = tv1->tv_sec - tv2->tv_sec;
+ res->tv_usec = tv1->tv_usec - tv2->tv_usec;
+ if (res->tv_usec >= 1000000) {
+ res->tv_sec++;
+ res->tv_usec -= 1000000;
+ }
+ if (res->tv_usec < 0) {
+ res->tv_sec--;
+ res->tv_usec += 1000000;
+ }
+}
diff --git a/usr.sbin/timed/timed/networkdelta.c b/usr.sbin/timed/timed/networkdelta.c
new file mode 100644
index 0000000..cbbde58
--- /dev/null
+++ b/usr.sbin/timed/timed/networkdelta.c
@@ -0,0 +1,264 @@
+/*-
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)networkdelta.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include "globals.h"
+
+static long median __P((float, float *, long *, long *, unsigned int));
+
+/*
+ * Compute a corrected date.
+ * Compute the median of the reasonable differences. First compute
+ * the median of all authorized differences, and then compute the
+ * median of all differences that are reasonably close to the first
+ * median.
+ *
+ * This differs from the original BSD implementation, which looked for
+ * the largest group of machines with essentially the same date.
+ * That assumed that machines with bad clocks would be uniformly
+ * distributed. Unfortunately, in real life networks, the distribution
+ * of machines is not uniform among models of machines, and the
+ * distribution of errors in clocks tends to be quite consistent
+ * for a given model. In other words, all model VI Supre Servres
+ * from GoFast Inc. tend to have about the same error.
+ * The original BSD implementation would chose the clock of the
+ * most common model, and discard all others.
+ *
+ * Therefore, get best we can do is to try to average over all
+ * of the machines in the network, while discarding "obviously"
+ * bad values.
+ */
+long
+networkdelta()
+{
+ struct hosttbl *htp;
+ long med;
+ long lodelta, hidelta;
+ long logood, higood;
+ long x[NHOSTS];
+ long *xp;
+ int numdelta;
+ float eps;
+
+ /*
+ * compute the median of the good values
+ */
+ med = 0;
+ numdelta = 1;
+ xp = &x[0];
+ *xp = 0; /* account for ourself */
+ for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) {
+ if (htp->good
+ && htp->noanswer == 0
+ && htp->delta != HOSTDOWN) {
+ med += htp->delta;
+ numdelta++;
+ *++xp = htp->delta;
+ }
+ }
+
+ /*
+ * If we are the only trusted time keeper, then do not change our
+ * clock. There may be another time keeping service active.
+ */
+ if (numdelta == 1)
+ return 0;
+
+ med /= numdelta;
+ eps = med - x[0];
+ if (trace)
+ fprintf(fd, "median of %d values starting at %ld is about ",
+ numdelta, med);
+ med = median(med, &eps, &x[0], xp+1, VALID_RANGE);
+
+ /*
+ * compute the median of all values near the good median
+ */
+ hidelta = med + GOOD_RANGE;
+ lodelta = med - GOOD_RANGE;
+ higood = med + VGOOD_RANGE;
+ logood = med - VGOOD_RANGE;
+ xp = &x[0];
+ htp = &self;
+ do {
+ if (htp->noanswer == 0
+ && htp->delta >= lodelta
+ && htp->delta <= hidelta
+ && (htp->good
+ || (htp->delta >= logood
+ && htp->delta <= higood))) {
+ *xp++ = htp->delta;
+ }
+ } while (&self != (htp = htp->l_fwd));
+
+ if (xp == &x[0]) {
+ if (trace)
+ fprintf(fd, "nothing close to median %ld\n", med);
+ return med;
+ }
+
+ if (xp == &x[1]) {
+ if (trace)
+ fprintf(fd, "only value near median is %ld\n", x[0]);
+ return x[0];
+ }
+
+ if (trace)
+ fprintf(fd, "median of %d values starting at %ld is ",
+ xp-&x[0], med);
+ return median(med, &eps, &x[0], xp, 1);
+}
+
+
+/*
+ * compute the median of an array of signed integers, using the idea
+ * in <<Numerical Recipes>>.
+ */
+static long
+median(a, eps_ptr, x, xlim, gnuf)
+ float a; /* initial guess for the median */
+ float *eps_ptr; /* spacing near the median */
+ long *x, *xlim; /* the data */
+ unsigned int gnuf; /* good enough estimate */
+{
+ long *xptr;
+ float ap = LONG_MAX; /* bounds on the median */
+ float am = -LONG_MAX;
+ float aa;
+ int npts; /* # of points above & below guess */
+ float xp; /* closet point above the guess */
+ float xm; /* closet point below the guess */
+ float eps;
+ float dum, sum, sumx;
+ int pass;
+#define AMP 1.5 /* smoothing constants */
+#define AFAC 1.5
+
+ eps = *eps_ptr;
+ if (eps < 1.0) {
+ eps = -eps;
+ if (eps < 1.0)
+ eps = 1.0;
+ }
+
+ for (pass = 1; ; pass++) { /* loop over the data */
+ sum = 0.0;
+ sumx = 0.0;
+ npts = 0;
+ xp = LONG_MAX;
+ xm = -LONG_MAX;
+
+ for (xptr = x; xptr != xlim; xptr++) {
+ float xx = *xptr;
+
+ dum = xx - a;
+ if (dum != 0.0) { /* avoid dividing by 0 */
+ if (dum > 0.0) {
+ npts++;
+ if (xx < xp)
+ xp = xx;
+ } else {
+ npts--;
+ if (xx > xm)
+ xm = xx;
+ dum = -dum;
+ }
+ dum = 1.0/(eps + dum);
+ sum += dum;
+ sumx += xx * dum;
+ }
+ }
+
+ if (ap-am < gnuf || sum == 0) {
+ if (trace)
+ fprintf(fd,
+ "%ld in %d passes; early out balance=%d\n",
+ (long)a, pass, npts);
+ return a; /* guess was good enough */
+ }
+
+ aa = (sumx/sum-a)*AMP;
+ if (npts >= 2) { /* guess was too low */
+ am = a;
+ aa = xp + max(0.0, aa);
+ if (aa > ap)
+ aa = (a + ap)/2;
+
+ } else if (npts <= -2) { /* guess was two high */
+ ap = a;
+ aa = xm + min(0.0, aa);
+ if (aa < am)
+ aa = (a + am)/2;
+
+ } else {
+ break; /* got it */
+ }
+
+ if (a == aa) {
+ if (trace)
+ fprintf(fd,
+ "%ld in %d passes; force out balance=%d\n",
+ (long)a, pass, npts);
+ return a;
+ }
+ eps = AFAC*abs(aa - a);
+ *eps_ptr = eps;
+ a = aa;
+ }
+
+ if (((x - xlim) % 2) != 0) { /* even number of points? */
+ if (npts == 0) /* yes, return an average */
+ a = (xp+xm)/2;
+ else if (npts > 0)
+ a = (a+xp)/2;
+ else
+ a = (xm+a)/2;
+
+ } else if (npts != 0) { /* odd number of points */
+ if (npts > 0)
+ a = xp;
+ else
+ a = xm;
+ }
+
+ if (trace)
+ fprintf(fd, "%ld in %d passes\n", (long)a, pass);
+ return a;
+}
diff --git a/usr.sbin/timed/timed/pathnames.h b/usr.sbin/timed/timed/pathnames.h
new file mode 100644
index 0000000..e5fdcde
--- /dev/null
+++ b/usr.sbin/timed/timed/pathnames.h
@@ -0,0 +1,41 @@
+/*-
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/6/93
+ *
+ * $FreeBSD$
+ */
+
+#include <paths.h>
+
+#define _PATH_MASTERLOG "/var/log/timed.masterlog"
+#define _PATH_TIMEDLOG "/var/log/timed.log"
diff --git a/usr.sbin/timed/timed/readmsg.c b/usr.sbin/timed/timed/readmsg.c
new file mode 100644
index 0000000..74a6f5f
--- /dev/null
+++ b/usr.sbin/timed/timed/readmsg.c
@@ -0,0 +1,513 @@
+/*-
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)readmsg.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include "globals.h"
+
+extern char *tsptype[];
+
+/*
+ * LOOKAT checks if the message is of the requested type and comes from
+ * the right machine, returning 1 in case of affirmative answer
+ */
+#define LOOKAT(msg, mtype, mfrom, netp, froms) \
+ (((mtype) == TSP_ANY || (mtype) == (msg).tsp_type) && \
+ ((mfrom) == 0 || !strcmp((mfrom), (msg).tsp_name)) && \
+ ((netp) == 0 || \
+ ((netp)->mask & (froms).sin_addr.s_addr) == (netp)->net.s_addr))
+
+struct timeval rtime, rwait, rtout;
+struct tsp msgin;
+static struct tsplist {
+ struct tsp info;
+ struct timeval when;
+ struct sockaddr_in addr;
+ struct tsplist *p;
+} msgslist;
+struct sockaddr_in from;
+struct netinfo *fromnet;
+struct timeval from_when;
+
+/*
+ * `readmsg' returns message `type' sent by `machfrom' if it finds it
+ * either in the receive queue, or in a linked list of previously received
+ * messages that it maintains.
+ * Otherwise it waits to see if the appropriate message arrives within
+ * `intvl' seconds. If not, it returns NULL.
+ */
+
+struct tsp *
+readmsg(type, machfrom, intvl, netfrom)
+ int type;
+ char *machfrom;
+ struct timeval *intvl;
+ struct netinfo *netfrom;
+{
+ int length;
+ fd_set ready;
+ static struct tsplist *head = &msgslist;
+ static struct tsplist *tail = &msgslist;
+ static int msgcnt = 0;
+ struct tsplist *prev;
+ register struct netinfo *ntp;
+ register struct tsplist *ptr;
+ ssize_t n;
+
+ if (trace) {
+ fprintf(fd, "readmsg: looking for %s from %s, %s\n",
+ tsptype[type], machfrom == NULL ? "ANY" : machfrom,
+ netfrom == NULL ? "ANYNET" : inet_ntoa(netfrom->net));
+ if (head->p != 0) {
+ length = 1;
+ for (ptr = head->p; ptr != 0; ptr = ptr->p) {
+ /* do not repeat the hundreds of messages */
+ if (++length > 3) {
+ if (ptr == tail) {
+ fprintf(fd,"\t ...%d skipped\n",
+ length);
+ } else {
+ continue;
+ }
+ }
+ fprintf(fd, length > 1 ? "\t" : "queue:\t");
+ print(&ptr->info, &ptr->addr);
+ }
+ }
+ }
+
+ ptr = head->p;
+ prev = head;
+
+ /*
+ * Look for the requested message scanning through the
+ * linked list. If found, return it and free the space
+ */
+
+ while (ptr != NULL) {
+ if (LOOKAT(ptr->info, type, machfrom, netfrom, ptr->addr)) {
+again:
+ msgin = ptr->info;
+ from = ptr->addr;
+ from_when = ptr->when;
+ prev->p = ptr->p;
+ if (ptr == tail)
+ tail = prev;
+ free((char *)ptr);
+ fromnet = NULL;
+ if (netfrom == NULL)
+ for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
+ if ((ntp->mask & from.sin_addr.s_addr) ==
+ ntp->net.s_addr) {
+ fromnet = ntp;
+ break;
+ }
+ }
+ else
+ fromnet = netfrom;
+ if (trace) {
+ fprintf(fd, "readmsg: found ");
+ print(&msgin, &from);
+ }
+
+/* The protocol can get far behind. When it does, it gets
+ * hopelessly confused. So delete duplicate messages.
+ */
+ for (ptr = prev; (ptr = ptr->p) != NULL; prev = ptr) {
+ if (ptr->addr.sin_addr.s_addr
+ == from.sin_addr.s_addr
+ && ptr->info.tsp_type == msgin.tsp_type) {
+ if (trace)
+ fprintf(fd, "\tdup ");
+ goto again;
+ }
+ }
+ msgcnt--;
+ return(&msgin);
+ } else {
+ prev = ptr;
+ ptr = ptr->p;
+ }
+ }
+
+ /*
+ * If the message was not in the linked list, it may still be
+ * coming from the network. Set the timer and wait
+ * on a select to read the next incoming message: if it is the
+ * right one, return it, otherwise insert it in the linked list.
+ */
+
+ (void)gettimeofday(&rtout, 0);
+ timevaladd(&rtout, intvl);
+ FD_ZERO(&ready);
+ for (;;) {
+ (void)gettimeofday(&rtime, 0);
+ timevalsub(&rwait, &rtout, &rtime);
+ if (rwait.tv_sec < 0)
+ rwait.tv_sec = rwait.tv_usec = 0;
+ else if (rwait.tv_sec == 0
+ && rwait.tv_usec < 1000000/CLK_TCK)
+ rwait.tv_usec = 1000000/CLK_TCK;
+
+ if (trace) {
+ fprintf(fd, "readmsg: wait %ld.%6ld at %s\n",
+ rwait.tv_sec, rwait.tv_usec, date());
+ /* Notice a full disk, as we flush trace info.
+ * It is better to flush periodically than at
+ * every line because the tracing consists of bursts
+ * of many lines. Without care, tracing slows
+ * down the code enough to break the protocol.
+ */
+ if (rwait.tv_sec != 0
+ && EOF == fflush(fd))
+ traceoff("Tracing ended for cause at %s\n");
+ }
+
+ FD_SET(sock, &ready);
+ if (!select(sock+1, &ready, (fd_set *)0, (fd_set *)0,
+ &rwait)) {
+ if (rwait.tv_sec == 0 && rwait.tv_usec == 0)
+ return(0);
+ continue;
+ }
+ length = sizeof(from);
+ if ((n = recvfrom(sock, (char *)&msgin, sizeof(struct tsp), 0,
+ (struct sockaddr*)&from, &length)) < 0) {
+ syslog(LOG_ERR, "recvfrom: %m");
+ exit(1);
+ }
+ /*
+ * The 4.3BSD protocol spec had a 32-byte tsp_name field, and
+ * this is still OS-dependent. Demand that the packet is at
+ * least long enough to hold a 4.3BSD packet.
+ */
+ if (n < (ssize_t)(sizeof(struct tsp) - MAXHOSTNAMELEN + 32)) {
+ syslog(LOG_NOTICE,
+ "short packet (%u/%u bytes) from %s",
+ n, sizeof(struct tsp) - MAXHOSTNAMELEN + 32,
+ inet_ntoa(from.sin_addr));
+ continue;
+ }
+ (void)gettimeofday(&from_when, (struct timezone *)0);
+ bytehostorder(&msgin);
+
+ if (msgin.tsp_vers > TSPVERSION) {
+ if (trace) {
+ fprintf(fd,"readmsg: version mismatch\n");
+ /* should do a dump of the packet */
+ }
+ continue;
+ }
+
+ if (memchr(msgin.tsp_name,
+ '\0', sizeof msgin.tsp_name) == NULL) {
+ syslog(LOG_NOTICE, "hostname field not NUL terminated "
+ "in packet from %s", inet_ntoa(from.sin_addr));
+ continue;
+ }
+
+ fromnet = NULL;
+ for (ntp = nettab; ntp != NULL; ntp = ntp->next)
+ if ((ntp->mask & from.sin_addr.s_addr) ==
+ ntp->net.s_addr) {
+ fromnet = ntp;
+ break;
+ }
+
+ /*
+ * drop packets from nets we are ignoring permanently
+ */
+ if (fromnet == NULL) {
+ /*
+ * The following messages may originate on
+ * this host with an ignored network address
+ */
+ if (msgin.tsp_type != TSP_TRACEON &&
+ msgin.tsp_type != TSP_SETDATE &&
+ msgin.tsp_type != TSP_MSITE &&
+ msgin.tsp_type != TSP_TEST &&
+ msgin.tsp_type != TSP_TRACEOFF) {
+ if (trace) {
+ fprintf(fd,"readmsg: discard null net ");
+ print(&msgin, &from);
+ }
+ continue;
+ }
+ }
+
+ /*
+ * Throw away messages coming from this machine,
+ * unless they are of some particular type.
+ * This gets rid of broadcast messages and reduces
+ * master processing time.
+ */
+ if (!strcmp(msgin.tsp_name, hostname)
+ && msgin.tsp_type != TSP_SETDATE
+ && msgin.tsp_type != TSP_TEST
+ && msgin.tsp_type != TSP_MSITE
+ && msgin.tsp_type != TSP_TRACEON
+ && msgin.tsp_type != TSP_TRACEOFF
+ && msgin.tsp_type != TSP_LOOP) {
+ if (trace) {
+ fprintf(fd, "readmsg: discard own ");
+ print(&msgin, &from);
+ }
+ continue;
+ }
+
+ /*
+ * Send acknowledgements here; this is faster and
+ * avoids deadlocks that would occur if acks were
+ * sent from a higher level routine. Different
+ * acknowledgements are necessary, depending on
+ * status.
+ */
+ if (fromnet == NULL) /* do not de-reference 0 */
+ ignoreack();
+ else if (fromnet->status == MASTER)
+ masterack();
+ else if (fromnet->status == SLAVE)
+ slaveack();
+ else
+ ignoreack();
+
+ if (LOOKAT(msgin, type, machfrom, netfrom, from)) {
+ if (trace) {
+ fprintf(fd, "readmsg: ");
+ print(&msgin, &from);
+ }
+ return(&msgin);
+ } else if (++msgcnt > NHOSTS*3) {
+
+/* The protocol gets hopelessly confused if it gets too far
+* behind. However, it seems able to recover from all cases of lost
+* packets. Therefore, if we are swamped, throw everything away.
+*/
+ if (trace)
+ fprintf(fd,
+ "readmsg: discarding %d msgs\n",
+ msgcnt);
+ msgcnt = 0;
+ while ((ptr=head->p) != NULL) {
+ head->p = ptr->p;
+ free((char *)ptr);
+ }
+ tail = head;
+ } else {
+ tail->p = (struct tsplist *)
+ malloc(sizeof(struct tsplist));
+ tail = tail->p;
+ tail->p = NULL;
+ tail->info = msgin;
+ tail->addr = from;
+ /* timestamp msgs so SETTIMEs are correct */
+ tail->when = from_when;
+ }
+ }
+}
+
+/*
+ * Send the necessary acknowledgements:
+ * only the type ACK is to be sent by a slave
+ */
+void
+slaveack()
+{
+ switch(msgin.tsp_type) {
+
+ case TSP_ADJTIME:
+ case TSP_SETTIME:
+ case TSP_ACCEPT:
+ case TSP_REFUSE:
+ case TSP_TRACEON:
+ case TSP_TRACEOFF:
+ case TSP_QUIT:
+ if (trace) {
+ fprintf(fd, "Slaveack: ");
+ print(&msgin, &from);
+ }
+ xmit(TSP_ACK,msgin.tsp_seq, &from);
+ break;
+
+ default:
+ if (trace) {
+ fprintf(fd, "Slaveack: no ack: ");
+ print(&msgin, &from);
+ }
+ break;
+ }
+}
+
+/*
+ * Certain packets may arrive from this machine on ignored networks.
+ * These packets should be acknowledged.
+ */
+void
+ignoreack()
+{
+ switch(msgin.tsp_type) {
+
+ case TSP_TRACEON:
+ case TSP_TRACEOFF:
+ case TSP_QUIT:
+ if (trace) {
+ fprintf(fd, "Ignoreack: ");
+ print(&msgin, &from);
+ }
+ xmit(TSP_ACK,msgin.tsp_seq, &from);
+ break;
+
+ default:
+ if (trace) {
+ fprintf(fd, "Ignoreack: no ack: ");
+ print(&msgin, &from);
+ }
+ break;
+ }
+}
+
+/*
+ * `masterack' sends the necessary acknowledgments
+ * to the messages received by a master
+ */
+void
+masterack()
+{
+ struct tsp resp;
+
+ resp = msgin;
+ resp.tsp_vers = TSPVERSION;
+ (void)strcpy(resp.tsp_name, hostname);
+
+ switch(msgin.tsp_type) {
+
+ case TSP_QUIT:
+ case TSP_TRACEON:
+ case TSP_TRACEOFF:
+ case TSP_MSITEREQ:
+ if (trace) {
+ fprintf(fd, "Masterack: ");
+ print(&msgin, &from);
+ }
+ xmit(TSP_ACK,msgin.tsp_seq, &from);
+ break;
+
+ case TSP_RESOLVE:
+ case TSP_MASTERREQ:
+ if (trace) {
+ fprintf(fd, "Masterack: ");
+ print(&msgin, &from);
+ }
+ xmit(TSP_MASTERACK,msgin.tsp_seq, &from);
+ break;
+
+ default:
+ if (trace) {
+ fprintf(fd,"Masterack: no ack: ");
+ print(&msgin, &from);
+ }
+ break;
+ }
+}
+
+/*
+ * Print a TSP message
+ */
+void
+print(msg, addr)
+ struct tsp *msg;
+ struct sockaddr_in *addr;
+{
+ char tm[26];
+ time_t tsp_time_sec;
+
+ if (msg->tsp_type >= TSPTYPENUMBER) {
+ fprintf(fd, "bad type (%u) on packet from %s\n",
+ msg->tsp_type, inet_ntoa(addr->sin_addr));
+ return;
+ }
+
+ switch (msg->tsp_type) {
+
+ case TSP_LOOP:
+ fprintf(fd, "%s %d %-6u #%d %-15s %s\n",
+ tsptype[msg->tsp_type],
+ msg->tsp_vers,
+ msg->tsp_seq,
+ msg->tsp_hopcnt,
+ inet_ntoa(addr->sin_addr),
+ msg->tsp_name);
+ break;
+
+ case TSP_SETTIME:
+ case TSP_SETDATE:
+ case TSP_SETDATEREQ:
+ tsp_time_sec = msg->tsp_time.tv_sec;
+ strncpy(tm, ctime(&tsp_time_sec)+3+1, sizeof(tm));
+ tm[15] = '\0'; /* ugh */
+ fprintf(fd, "%s %d %-6u %s %-15s %s\n",
+ tsptype[msg->tsp_type],
+ msg->tsp_vers,
+ msg->tsp_seq,
+ tm,
+ inet_ntoa(addr->sin_addr),
+ msg->tsp_name);
+ break;
+
+ case TSP_ADJTIME:
+ fprintf(fd, "%s %d %-6u (%ld,%ld) %-15s %s\n",
+ tsptype[msg->tsp_type],
+ msg->tsp_vers,
+ msg->tsp_seq,
+ msg->tsp_time.tv_sec,
+ msg->tsp_time.tv_usec,
+ inet_ntoa(addr->sin_addr),
+ msg->tsp_name);
+ break;
+
+ default:
+ fprintf(fd, "%s %d %-6u %-15s %s\n",
+ tsptype[msg->tsp_type],
+ msg->tsp_vers,
+ msg->tsp_seq,
+ inet_ntoa(addr->sin_addr),
+ msg->tsp_name);
+ break;
+ }
+}
diff --git a/usr.sbin/timed/timed/slave.c b/usr.sbin/timed/timed/slave.c
new file mode 100644
index 0000000..db4d2dd
--- /dev/null
+++ b/usr.sbin/timed/timed/slave.c
@@ -0,0 +1,693 @@
+/*-
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)slave.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include "globals.h"
+#include <setjmp.h>
+#include "pathnames.h"
+
+extern jmp_buf jmpenv;
+extern int Mflag;
+extern int justquit;
+
+extern u_short sequence;
+
+static char master_name[MAXHOSTNAMELEN];
+static struct netinfo *old_slavenet;
+static int old_status;
+
+static void schgdate __P((struct tsp *, char *));
+static void setmaster __P((struct tsp *));
+static void answerdelay __P((void));
+
+extern void logwtmp __P((char *, char *, char *));
+
+int
+slave()
+{
+ int tries;
+ long electiontime, refusetime, looktime, looptime, adjtime;
+ u_short seq;
+ long fastelection;
+#define FASTTOUT 3
+ struct in_addr cadr;
+ struct timeval otime;
+ struct sockaddr_in taddr;
+ char tname[MAXHOSTNAMELEN];
+ struct tsp *msg, to;
+ struct timeval ntime, wait, tmptv;
+ time_t tsp_time_sec;
+ struct tsp *answer;
+ int timeout();
+ char olddate[32];
+ char newdate[32];
+ struct netinfo *ntp;
+ struct hosttbl *htp;
+
+
+ old_slavenet = 0;
+ seq = 0;
+ refusetime = 0;
+ adjtime = 0;
+
+ (void)gettimeofday(&ntime, 0);
+ electiontime = ntime.tv_sec + delay2;
+ fastelection = ntime.tv_sec + FASTTOUT;
+ if (justquit)
+ looktime = electiontime;
+ else
+ looktime = fastelection;
+ looptime = fastelection;
+
+ if (slavenet)
+ xmit(TSP_SLAVEUP, 0, &slavenet->dest_addr);
+ if (status & MASTER) {
+ for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
+ if (ntp->status == MASTER)
+ masterup(ntp);
+ }
+ }
+
+loop:
+ get_goodgroup(0);
+ (void)gettimeofday(&ntime, (struct timezone *)0);
+ if (ntime.tv_sec > electiontime) {
+ if (trace)
+ fprintf(fd, "election timer expired\n");
+ longjmp(jmpenv, 1);
+ }
+
+ if (ntime.tv_sec >= looktime) {
+ if (trace)
+ fprintf(fd, "Looking for nets to master\n");
+
+ if (Mflag && nignorednets > 0) {
+ for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
+ if (ntp->status == IGNORE
+ || ntp->status == NOMASTER) {
+ lookformaster(ntp);
+ if (ntp->status == MASTER) {
+ masterup(ntp);
+ } else if (ntp->status == MASTER) {
+ ntp->status = NOMASTER;
+ }
+ }
+ if (ntp->status == MASTER
+ && --ntp->quit_count < 0)
+ ntp->quit_count = 0;
+ }
+ makeslave(slavenet); /* prune extras */
+ setstatus();
+ }
+ (void)gettimeofday(&ntime, 0);
+ looktime = ntime.tv_sec + delay2;
+ }
+ if (ntime.tv_sec >= looptime) {
+ if (trace)
+ fprintf(fd, "Looking for loops\n");
+ for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
+ if (ntp->status == MASTER) {
+ to.tsp_type = TSP_LOOP;
+ to.tsp_vers = TSPVERSION;
+ to.tsp_seq = sequence++;
+ to.tsp_hopcnt = MAX_HOPCNT;
+ (void)strcpy(to.tsp_name, hostname);
+ bytenetorder(&to);
+ if (sendto(sock, (char *)&to, sizeof(struct tsp), 0,
+ (struct sockaddr*)&ntp->dest_addr,
+ sizeof(ntp->dest_addr)) < 0) {
+ trace_sendto_err(ntp->dest_addr.sin_addr);
+ }
+ }
+ }
+ (void)gettimeofday(&ntime, 0);
+ looptime = ntime.tv_sec + delay2;
+ }
+
+ wait.tv_sec = min(electiontime,min(looktime,looptime)) - ntime.tv_sec;
+ if (wait.tv_sec < 0)
+ wait.tv_sec = 0;
+ wait.tv_sec += FASTTOUT;
+ wait.tv_usec = 0;
+ msg = readmsg(TSP_ANY, ANYADDR, &wait, 0);
+
+ if (msg != NULL) {
+ /*
+ * filter stuff not for us
+ */
+ switch (msg->tsp_type) {
+ case TSP_SETDATE:
+ case TSP_TRACEOFF:
+ case TSP_TRACEON:
+ /*
+ * XXX check to see they are from ourself
+ */
+ break;
+
+ case TSP_TEST:
+ case TSP_MSITE:
+ break;
+
+ case TSP_MASTERUP:
+ if (!fromnet) {
+ if (trace) {
+ fprintf(fd, "slave ignored: ");
+ print(msg, &from);
+ }
+ goto loop;
+ }
+ break;
+
+ default:
+ if (!fromnet
+ || fromnet->status == IGNORE
+ || fromnet->status == NOMASTER) {
+ if (trace) {
+ fprintf(fd, "slave ignored: ");
+ print(msg, &from);
+ }
+ goto loop;
+ }
+ break;
+ }
+
+
+ /*
+ * now process the message
+ */
+ switch (msg->tsp_type) {
+
+ case TSP_ADJTIME:
+ if (fromnet != slavenet)
+ break;
+ if (!good_host_name(msg->tsp_name)) {
+ syslog(LOG_NOTICE,
+ "attempted time adjustment by %s",
+ msg->tsp_name);
+ suppress(&from, msg->tsp_name, fromnet);
+ break;
+ }
+ /*
+ * Speed up loop detection in case we have a loop.
+ * Otherwise the clocks can race until the loop
+ * is found.
+ */
+ (void)gettimeofday(&otime, 0);
+ if (adjtime < otime.tv_sec)
+ looptime -= (looptime-otime.tv_sec)/2 + 1;
+
+ setmaster(msg);
+ if (seq != msg->tsp_seq) {
+ seq = msg->tsp_seq;
+ synch(tvtomsround(msg->tsp_time));
+ }
+ (void)gettimeofday(&ntime, 0);
+ electiontime = ntime.tv_sec + delay2;
+ fastelection = ntime.tv_sec + FASTTOUT;
+ adjtime = ntime.tv_sec + SAMPLEINTVL*2;
+ break;
+
+ case TSP_SETTIME:
+ if (fromnet != slavenet)
+ break;
+ if (seq == msg->tsp_seq)
+ break;
+ seq = msg->tsp_seq;
+
+ /* adjust time for residence on the queue */
+ (void)gettimeofday(&otime, 0);
+ adj_msg_time(msg,&otime);
+ /*
+ * the following line is necessary due to syslog
+ * calling ctime() which clobbers the static buffer
+ */
+ (void)strcpy(olddate, date());
+ tsp_time_sec = msg->tsp_time.tv_sec;
+ (void)strcpy(newdate, ctime(&tsp_time_sec));
+
+ if (!good_host_name(msg->tsp_name)) {
+ syslog(LOG_NOTICE,
+ "attempted time setting by untrusted %s to %s",
+ msg->tsp_name, newdate);
+ suppress(&from, msg->tsp_name, fromnet);
+ break;
+ }
+
+ setmaster(msg);
+ tmptv.tv_sec = msg->tsp_time.tv_sec;
+ tmptv.tv_usec = msg->tsp_time.tv_usec;
+ timevalsub(&ntime, &tmptv, &otime);
+ if (ntime.tv_sec < MAXADJ && ntime.tv_sec > -MAXADJ) {
+ /*
+ * do not change the clock if we can adjust it
+ */
+ synch(tvtomsround(ntime));
+ } else {
+ logwtmp("|", "date", "");
+ (void)settimeofday(&tmptv, 0);
+ logwtmp("{", "date", "");
+ syslog(LOG_NOTICE,
+ "date changed by %s from %s",
+ msg->tsp_name, olddate);
+ if (status & MASTER)
+ spreadtime();
+ }
+ (void)gettimeofday(&ntime, 0);
+ electiontime = ntime.tv_sec + delay2;
+ fastelection = ntime.tv_sec + FASTTOUT;
+
+/* This patches a bad protocol bug. Imagine a system with several networks,
+ * where there are a pair of redundant gateways between a pair of networks,
+ * each running timed. Assume that we start with a third machine mastering
+ * one of the networks, and one of the gateways mastering the other.
+ * Imagine that the third machine goes away and the non-master gateway
+ * decides to replace it. If things are timed just 'right,' we will have
+ * each gateway mastering one network for a little while. If a SETTIME
+ * message gets into the network at that time, perhaps from the newly
+ * masterful gateway as it was taking control, the SETTIME will loop
+ * forever. Each time a gateway receives it on its slave side, it will
+ * call spreadtime to forward it on its mastered network. We are now in
+ * a permanent loop, since the SETTIME msgs will keep any clock
+ * in the network from advancing. Normally, the 'LOOP' stuff will detect
+ * and correct the situation. However, with the clocks stopped, the
+ * 'looptime' timer cannot expire. While they are in this state, the
+ * masters will try to saturate the network with SETTIME packets.
+ */
+ looptime = ntime.tv_sec + (looptime-otime.tv_sec)/2-1;
+ break;
+
+ case TSP_MASTERUP:
+ if (slavenet && fromnet != slavenet)
+ break;
+ if (!good_host_name(msg->tsp_name)) {
+ suppress(&from, msg->tsp_name, fromnet);
+ if (electiontime > fastelection)
+ electiontime = fastelection;
+ break;
+ }
+ makeslave(fromnet);
+ setmaster(msg);
+ setstatus();
+ answerdelay();
+ xmit(TSP_SLAVEUP, 0, &from);
+ (void)gettimeofday(&ntime, 0);
+ electiontime = ntime.tv_sec + delay2;
+ fastelection = ntime.tv_sec + FASTTOUT;
+ refusetime = 0;
+ break;
+
+ case TSP_MASTERREQ:
+ if (fromnet->status != SLAVE)
+ break;
+ (void)gettimeofday(&ntime, 0);
+ electiontime = ntime.tv_sec + delay2;
+ break;
+
+ case TSP_SETDATE:
+ tsp_time_sec = msg->tsp_time.tv_sec;
+ (void)strcpy(newdate, ctime(&tsp_time_sec));
+ schgdate(msg, newdate);
+ break;
+
+ case TSP_SETDATEREQ:
+ if (fromnet->status != MASTER)
+ break;
+ tsp_time_sec = msg->tsp_time.tv_sec;
+ (void)strcpy(newdate, ctime(&tsp_time_sec));
+ htp = findhost(msg->tsp_name);
+ if (0 == htp) {
+ syslog(LOG_WARNING,
+ "DATEREQ from uncontrolled machine");
+ break;
+ }
+ if (!htp->good) {
+ syslog(LOG_WARNING,
+ "attempted date change by untrusted %s to %s",
+ htp->name, newdate);
+ spreadtime();
+ break;
+ }
+ schgdate(msg, newdate);
+ break;
+
+ case TSP_TRACEON:
+ traceon();
+ break;
+
+ case TSP_TRACEOFF:
+ traceoff("Tracing ended at %s\n");
+ break;
+
+ case TSP_SLAVEUP:
+ newslave(msg);
+ break;
+
+ case TSP_ELECTION:
+ if (fromnet->status == SLAVE) {
+ (void)gettimeofday(&ntime, 0);
+ electiontime = ntime.tv_sec + delay2;
+ fastelection = ntime.tv_sec + FASTTOUT;
+ seq = 0;
+ if (!good_host_name(msg->tsp_name)) {
+ syslog(LOG_NOTICE,
+ "suppress election of %s",
+ msg->tsp_name);
+ to.tsp_type = TSP_QUIT;
+ electiontime = fastelection;
+ } else if (cadr.s_addr != from.sin_addr.s_addr
+ && ntime.tv_sec < refusetime) {
+/* if the candidate has to repeat itself, the old code would refuse it
+ * the second time. That would prevent elections.
+ */
+ to.tsp_type = TSP_REFUSE;
+ } else {
+ cadr.s_addr = from.sin_addr.s_addr;
+ to.tsp_type = TSP_ACCEPT;
+ refusetime = ntime.tv_sec + 30;
+ }
+ taddr = from;
+ (void)strcpy(tname, msg->tsp_name);
+ (void)strcpy(to.tsp_name, hostname);
+ answerdelay();
+ if (!acksend(&to, &taddr, tname,
+ TSP_ACK, 0, 0))
+ syslog(LOG_WARNING,
+ "no answer from candidate %s\n",
+ tname);
+
+ } else { /* fromnet->status == MASTER */
+ htp = addmach(msg->tsp_name, &from,fromnet);
+ to.tsp_type = TSP_QUIT;
+ (void)strcpy(to.tsp_name, hostname);
+ if (!acksend(&to, &htp->addr, htp->name,
+ TSP_ACK, 0, htp->noanswer)) {
+ syslog(LOG_ERR,
+ "no reply from %s to ELECTION-QUIT",
+ htp->name);
+ (void)remmach(htp);
+ }
+ }
+ break;
+
+ case TSP_CONFLICT:
+ if (fromnet->status != MASTER)
+ break;
+ /*
+ * After a network partition, there can be
+ * more than one master: the first slave to
+ * come up will notify here the situation.
+ */
+ (void)strcpy(to.tsp_name, hostname);
+
+ /* The other master often gets into the same state,
+ * with boring results.
+ */
+ ntp = fromnet; /* (acksend() can leave fromnet=0 */
+ for (tries = 0; tries < 3; tries++) {
+ to.tsp_type = TSP_RESOLVE;
+ answer = acksend(&to, &ntp->dest_addr,
+ ANYADDR, TSP_MASTERACK,
+ ntp, 0);
+ if (answer == NULL)
+ break;
+ htp = addmach(answer->tsp_name,&from,ntp);
+ to.tsp_type = TSP_QUIT;
+ answer = acksend(&to, &htp->addr, htp->name,
+ TSP_ACK, 0, htp->noanswer);
+ if (!answer) {
+ syslog(LOG_WARNING,
+ "conflict error: no reply from %s to QUIT",
+ htp->name);
+ (void)remmach(htp);
+ }
+ }
+ masterup(ntp);
+ break;
+
+ case TSP_MSITE:
+ if (!slavenet)
+ break;
+ taddr = from;
+ to.tsp_type = TSP_MSITEREQ;
+ to.tsp_vers = TSPVERSION;
+ to.tsp_seq = 0;
+ (void)strcpy(to.tsp_name, hostname);
+ answer = acksend(&to, &slavenet->dest_addr,
+ ANYADDR, TSP_ACK,
+ slavenet, 0);
+ if (answer != NULL
+ && good_host_name(answer->tsp_name)) {
+ setmaster(answer);
+ to.tsp_type = TSP_ACK;
+ (void)strcpy(to.tsp_name, answer->tsp_name);
+ bytenetorder(&to);
+ if (sendto(sock, (char *)&to,
+ sizeof(struct tsp), 0,
+ (struct sockaddr*)&taddr,
+ sizeof(taddr)) < 0) {
+ trace_sendto_err(taddr.sin_addr);
+ }
+ }
+ break;
+
+ case TSP_MSITEREQ:
+ break;
+
+ case TSP_ACCEPT:
+ case TSP_REFUSE:
+ case TSP_RESOLVE:
+ break;
+
+ case TSP_QUIT:
+ doquit(msg); /* become a slave */
+ break;
+
+ case TSP_TEST:
+ electiontime = 0;
+ break;
+
+ case TSP_LOOP:
+ /* looking for loops of masters */
+ if (!(status & MASTER))
+ break;
+ if (fromnet->status == SLAVE) {
+ if (!strcmp(msg->tsp_name, hostname)) {
+ /*
+ * Someone forwarded our message back to
+ * us. There must be a loop. Tell the
+ * master of this network to quit.
+ *
+ * The other master often gets into
+ * the same state, with boring results.
+ */
+ ntp = fromnet;
+ for (tries = 0; tries < 3; tries++) {
+ to.tsp_type = TSP_RESOLVE;
+ answer = acksend(&to, &ntp->dest_addr,
+ ANYADDR, TSP_MASTERACK,
+ ntp,0);
+ if (answer == NULL)
+ break;
+ taddr = from;
+ (void)strcpy(tname, answer->tsp_name);
+ to.tsp_type = TSP_QUIT;
+ (void)strcpy(to.tsp_name, hostname);
+ if (!acksend(&to, &taddr, tname,
+ TSP_ACK, 0, 1)) {
+ syslog(LOG_ERR,
+ "no reply from %s to slave LOOP-QUIT",
+ tname);
+ } else {
+ electiontime = 0;
+ }
+ }
+ (void)gettimeofday(&ntime, 0);
+ looptime = ntime.tv_sec + FASTTOUT;
+ } else {
+ if (msg->tsp_hopcnt-- < 1)
+ break;
+ bytenetorder(msg);
+ for (ntp = nettab; ntp != 0; ntp = ntp->next) {
+ if (ntp->status == MASTER
+ && 0 > sendto(sock, (char *)msg,
+ sizeof(struct tsp), 0,
+ (struct sockaddr*)&ntp->dest_addr,
+ sizeof(ntp->dest_addr)))
+ trace_sendto_err(ntp->dest_addr.sin_addr);
+ }
+ }
+ } else { /* fromnet->status == MASTER */
+ /*
+ * We should not have received this from a net
+ * we are master on. There must be two masters,
+ * unless the packet was really from us.
+ */
+ if (from.sin_addr.s_addr
+ == fromnet->my_addr.s_addr) {
+ if (trace)
+ fprintf(fd,"discarding forwarded LOOP\n");
+ break;
+ }
+
+ /*
+ * The other master often gets into the same
+ * state, with boring results.
+ */
+ ntp = fromnet;
+ for (tries = 0; tries < 3; tries++) {
+ to.tsp_type = TSP_RESOLVE;
+ answer = acksend(&to, &ntp->dest_addr,
+ ANYADDR, TSP_MASTERACK,
+ ntp,0);
+ if (!answer)
+ break;
+ htp = addmach(answer->tsp_name,
+ &from,ntp);
+ to.tsp_type = TSP_QUIT;
+ (void)strcpy(to.tsp_name, hostname);
+ if (!acksend(&to,&htp->addr,htp->name,
+ TSP_ACK, 0, htp->noanswer)) {
+ syslog(LOG_ERR,
+ "no reply from %s to master LOOP-QUIT",
+ htp->name);
+ (void)remmach(htp);
+ }
+ }
+ (void)gettimeofday(&ntime, 0);
+ looptime = ntime.tv_sec + FASTTOUT;
+ }
+ break;
+ default:
+ if (trace) {
+ fprintf(fd, "garbage message: ");
+ print(msg, &from);
+ }
+ break;
+ }
+ }
+ goto loop;
+}
+
+
+/*
+ * tell the world who our master is
+ */
+static void
+setmaster(msg)
+ struct tsp *msg;
+{
+ if (slavenet
+ && (slavenet != old_slavenet
+ || strcmp(msg->tsp_name, master_name)
+ || old_status != status)) {
+ (void)strcpy(master_name, msg->tsp_name);
+ old_slavenet = slavenet;
+ old_status = status;
+
+ if (status & MASTER) {
+ syslog(LOG_NOTICE, "submaster to %s", master_name);
+ if (trace)
+ fprintf(fd, "submaster to %s\n", master_name);
+
+ } else {
+ syslog(LOG_NOTICE, "slave to %s", master_name);
+ if (trace)
+ fprintf(fd, "slave to %s\n", master_name);
+ }
+ }
+}
+
+
+
+/*
+ * handle date change request on a slave
+ */
+static void
+schgdate(msg, newdate)
+ struct tsp *msg;
+ char *newdate;
+{
+ struct tsp to;
+ u_short seq;
+ struct sockaddr_in taddr;
+ struct timeval otime;
+
+ if (!slavenet)
+ return; /* no where to forward */
+
+ taddr = from;
+ seq = msg->tsp_seq;
+
+ syslog(LOG_INFO,
+ "forwarding date change by %s to %s",
+ msg->tsp_name, newdate);
+
+ /* adjust time for residence on the queue */
+ (void)gettimeofday(&otime, 0);
+ adj_msg_time(msg, &otime);
+
+ to.tsp_type = TSP_SETDATEREQ;
+ to.tsp_time = msg->tsp_time;
+ (void)strcpy(to.tsp_name, hostname);
+ if (!acksend(&to, &slavenet->dest_addr,
+ ANYADDR, TSP_DATEACK,
+ slavenet, 0))
+ return; /* no answer */
+
+ xmit(TSP_DATEACK, seq, &taddr);
+}
+
+
+/*
+ * Used before answering a broadcast message to avoid network
+ * contention and likely collisions.
+ */
+static void
+answerdelay()
+{
+ struct timeval timeout;
+
+ timeout.tv_sec = 0;
+ timeout.tv_usec = delay1;
+
+ (void)select(0, (fd_set *)NULL, (fd_set *)NULL, (fd_set *)NULL,
+ &timeout);
+ return;
+}
diff --git a/usr.sbin/timed/timed/timed.8 b/usr.sbin/timed/timed/timed.8
new file mode 100644
index 0000000..8c36e9e
--- /dev/null
+++ b/usr.sbin/timed/timed/timed.8
@@ -0,0 +1,264 @@
+.\" Copyright (c) 1980, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)timed.8 8.1 (Berkeley) 6/6/93
+.\" $FreeBSD$
+.\"
+.Dd June 6, 1993
+.Dt TIMED 8
+.Os
+.Sh NAME
+.Nm timed
+.Nd time server daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl dtM
+.Op Fl i Ar network
+.Op Fl n Ar network
+.Op Fl F Ar host ...
+.Sh DESCRIPTION
+The
+.Nm
+daemon is a time server daemon
+which is normally invoked at boot time from the
+.Xr rc.network 8
+file.
+It synchronizes the host's time with the time of other
+machines, which are also running
+.Nm ,
+in a local area network.
+These time servers will slow down the clocks of some machines
+and speed up the clocks of others to bring them to the average network time.
+The average network time is computed from measurements of clock differences
+using the
+.Tn ICMP
+timestamp request message.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl d
+Enable debugging mode;
+do not detach from the terminal.
+.It Fl i Ar network
+Add
+.Ar network
+to the list of networks to ignore.
+All other networks
+to which the machine is directly connected
+are used by
+.Nm .
+This option may be specified multiple times
+to add more than one network to the list.
+.It Fl F Ar host ...
+Create a list of trusted hosts.
+.Nm
+will only accept trusted hosts as masters.
+If it finds an untrusted host claiming to be master,
+.Nm
+will suppress incoming messages from that host
+and call for a new election.
+This option implies the
+.Fl M
+option.
+If this option is not specified,
+all hosts on the connected networks are treated as trustworthy.
+.It Fl M
+Allow this host to become a
+.Nm
+master if necessary.
+.It Fl n Ar network
+Add
+.Ar network
+to the list of allowed networks.
+All other networks
+to which the machine is directly connected
+are ignored by
+.Nm .
+This option may be specified multiple times
+to add more than one network to the list.
+.It Fl t
+Enable tracing of received messages
+and log to the file
+.It Pa /var/log/timed.log .
+Tracing can be turned on or off while
+.Nm
+is running with the
+.Xr timedc 8
+utility.
+.El
+.Pp
+The
+.Fl n
+and
+.Fl i
+are mutually exclusive
+and require as arguments real networks to which
+the host is connected
+(see
+.Xr networks 5 ) .
+If neither flag is specified,
+.Nm
+will listen on all connected networks.
+.Pp
+A
+.Nm
+running without the
+.Fl M
+nor
+.Fl F
+flags will always remain a slave.
+If the
+.Fl F
+flag is not used,
+.Nm
+will treat all machines as trustworthy.
+.Pp
+.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
+.Nm
+control program.
+If the machine running the master becomes unreachable,
+the slaves will elect a new master
+from among those slaves
+which are running with at least one of the
+.Fl M
+and
+.Fl F
+flags.
+.Pp
+At startup
+.Nm
+normally checks for a master time server on each network to which
+it is connected, except as modified by the
+.Fl n
+and
+.Fl i
+options described above.
+It will request synchronization service from the first master server
+located.
+If permitted by the
+.Fl M
+or
+.Fl F
+flags, it will provide synchronization service on any attached networks
+on which no trusted master server was detected.
+Such a server propagates the time computed by the top-level master.
+.Nm
+will periodically check for the presence of a master
+on those networks for which it is operating as a slave.
+If it finds that there are no trusted masters on a network,
+it will begin the election process on that network.
+.Pp
+One way to synchronize a group of machines is to use 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
+Temporal chaos will result if two or more time daemons attempt
+to adjust the same clock.
+If both
+.Nm
+and another time daemon are run on the same machine,
+ensure that the
+.Fl F
+flag is used, so that
+.Nm
+never attempts to adjust the local clock.
+.Pp
+The protocol is based on
+.Tn UDP/IP
+broadcasts. All machines within
+the range of a broadcast that are using the
+.Tn TSP
+protocol must cooperate.
+There cannot be more than a single administrative domain using the
+.Fl F
+flag among all machines reached by a broadcast packet.
+Failure to follow this rule is usually indicated by complaints concerning
+"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 networks 5 ,
+.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..2daadb1
--- /dev/null
+++ b/usr.sbin/timed/timed/timed.c
@@ -0,0 +1,850 @@
+/*-
+ * 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[] =
+ "$FreeBSD$";
+#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>
+
+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));
+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 lint
+ ntip = NULL;
+#endif
+
+ on = 1;
+ nflag = OFF;
+ iflag = OFF;
+
+
+ opterr = 0;
+ while ((c = getopt(argc, argv, "Mtdn:i:F:G:P:")) != -1) {
+ switch (c) {
+ case 'M':
+ Mflag = 1;
+ break;
+
+ case 't':
+ trace = 1;
+ break;
+
+ case 'n':
+ if (iflag) {
+ errx(1, "-i and -n make no sense together");
+ } else {
+ nflag = ON;
+ addnetname(optarg);
+ }
+ break;
+
+ case 'i':
+ if (nflag) {
+ errx(1, "-i and -n make no sense together");
+ } else {
+ iflag = ON;
+ addnetname(optarg);
+ }
+ break;
+
+ case 'F':
+ add_good_host(optarg,1);
+ while (optind < argc && argv[optind][0] != '-')
+ add_good_host(argv[optind++], 1);
+ break;
+
+ case 'd':
+ debug = 1;
+ break;
+ case 'G':
+ if (goodgroup != 0)
+ errx(1, "only one net group");
+ goodgroup = optarg;
+ break;
+
+ default:
+ usage();
+ break;
+ }
+ }
+ if (optind < argc)
+ usage();
+
+ /* If we care about which machine is the master, then we must
+ * be willing to be a master
+ */
+ if (0 != goodgroup || 0 != goodhosts)
+ Mflag = 1;
+
+ if (gethostname(hostname, sizeof(hostname) - 1) < 0)
+ err(1, "gethostname");
+ self.l_bak = &self;
+ self.l_fwd = &self;
+ self.h_bak = &self;
+ self.h_fwd = &self;
+ self.head = 1;
+ self.good = 1;
+
+ if (goodhosts != 0) /* trust ourself */
+ add_good_host(hostname,1);
+
+ srvp = getservbyname("timed", "udp");
+ if (srvp == 0)
+ errx(1, "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);
+ }
+
+ /* choose a unique seed for random number generation */
+ (void)gettimeofday(&ntime, 0);
+ srandom(ntime.tv_sec + ntime.tv_usec);
+
+ sequence = random(); /* initial seq number */
+
+ /* rounds kernel variable time to multiple of 5 ms. */
+ ntime.tv_sec = 0;
+ ntime.tv_usec = -((ntime.tv_usec/1000) % 5) * 1000;
+ (void)adjtime(&ntime, (struct timeval *)0);
+
+ for (nt = nets; nt; nt = nt->next) {
+ nentp = getnetbyname(nt->name);
+ if (nentp == 0) {
+ nt->net = inet_network(nt->name);
+ if (nt->net != INADDR_NONE)
+ nentp = getnetbyaddr(nt->net, AF_INET);
+ }
+ if (nentp != 0) {
+ nt->net = nentp->n_net;
+ } else if (nt->net == INADDR_NONE) {
+ errx(1, "unknown net %s", nt->name);
+ } else if (nt->net == INADDR_ANY) {
+ errx(1, "bad net %s", nt->name);
+ } else {
+ warnx("warning: %s unknown in /etc/networks",
+ nt->name);
+ }
+
+ if (0 == (nt->net & 0xff000000))
+ nt->net <<= 8;
+ if (0 == (nt->net & 0xff000000))
+ nt->net <<= 8;
+ if (0 == (nt->net & 0xff000000))
+ nt->net <<= 8;
+ }
+ ifc.ifc_len = sizeof(buf);
+ ifc.ifc_buf = buf;
+ if (ioctl(sock, SIOCGIFCONF, (char *)&ifc) < 0)
+ err(1, "get interface configuration");
+ ntp = NULL;
+#define size(p) max((p).sa_len, sizeof(p))
+ cplim = buf + ifc.ifc_len; /*skip over if's with big ifr_addr's */
+ for (cp = buf; cp < cplim;
+ cp += sizeof (ifr->ifr_name) + size(ifr->ifr_addr)) {
+ ifr = (struct ifreq *)cp;
+ if (ifr->ifr_addr.sa_family != AF_INET)
+ continue;
+ if (!ntp)
+ ntp = (struct netinfo*)malloc(sizeof(struct netinfo));
+ bzero(ntp,sizeof(*ntp));
+ ntp->my_addr=((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr;
+ ntp->status = NOMASTER;
+ ifreq = *ifr;
+ ifreqf = *ifr;
+
+ if (ioctl(sock, SIOCGIFFLAGS, (char *)&ifreqf) < 0) {
+ warn("get interface flags");
+ continue;
+ }
+ if ((ifreqf.ifr_flags & IFF_UP) == 0)
+ continue;
+ if ((ifreqf.ifr_flags & IFF_BROADCAST) == 0 &&
+ (ifreqf.ifr_flags & IFF_POINTOPOINT) == 0) {
+ continue;
+ }
+
+
+ if (ioctl(sock, SIOCGIFNETMASK, (char *)&ifreq) < 0) {
+ warn("get netmask");
+ continue;
+ }
+ ntp->mask = ((struct sockaddr_in *)
+ &ifreq.ifr_addr)->sin_addr.s_addr;
+
+ if (ifreqf.ifr_flags & IFF_BROADCAST) {
+ if (ioctl(sock, SIOCGIFBRDADDR, (char *)&ifreq) < 0) {
+ warn("get broadaddr");
+ continue;
+ }
+ ntp->dest_addr = *(struct sockaddr_in *)&ifreq.ifr_broadaddr;
+ /* What if the broadcast address is all ones?
+ * So we cannot just mask ntp->dest_addr. */
+ ntp->net = ntp->my_addr;
+ ntp->net.s_addr &= ntp->mask;
+ } else {
+ if (ioctl(sock, SIOCGIFDSTADDR,
+ (char *)&ifreq) < 0) {
+ warn("get destaddr");
+ continue;
+ }
+ ntp->dest_addr = *(struct sockaddr_in *)&ifreq.ifr_dstaddr;
+ ntp->net = ntp->dest_addr.sin_addr;
+ }
+
+ ntp->dest_addr.sin_port = port;
+
+ for (nt = nets; nt; nt = nt->next) {
+ if (ntp->net.s_addr == htonl(nt->net))
+ break;
+ }
+ if ((nflag && !nt) || (iflag && nt))
+ continue;
+
+ ntp->next = NULL;
+ if (nettab == NULL) {
+ nettab = ntp;
+ } else {
+ ntip->next = ntp;
+ }
+ ntip = ntp;
+ ntp = NULL;
+ }
+ if (ntp)
+ (void) free((char *)ntp);
+ if (nettab == NULL)
+ errx(1, "no network usable");
+
+ /* microseconds to delay before responding to a broadcast */
+ delay1 = casual(1, 100*1000);
+
+ /* election timer delay in secs. */
+ delay2 = casual(MINTOUT, MAXTOUT);
+
+ if (!debug)
+ daemon(debug, 0);
+
+ if (trace)
+ traceon();
+ openlog("timed", LOG_CONS|LOG_PID, LOG_DAEMON);
+
+ /*
+ * keep returning here
+ */
+ ret = setjmp(jmpenv);
+ savefromnet = fromnet;
+ setstatus();
+
+ if (Mflag) {
+ switch (ret) {
+
+ case 0:
+ checkignorednets();
+ pickslavenet(0);
+ break;
+ case 1:
+ /* Just lost our master */
+ if (slavenet != 0)
+ slavenet->status = election(slavenet);
+ if (!slavenet || slavenet->status == MASTER) {
+ checkignorednets();
+ pickslavenet(0);
+ } else {
+ makeslave(slavenet); /* prune extras */
+ }
+ break;
+
+ case 2:
+ /* Just been told to quit */
+ justquit = 1;
+ pickslavenet(savefromnet);
+ break;
+ }
+
+ setstatus();
+ if (!(status & MASTER) && sock_raw != -1) {
+ /* sock_raw is not being used now */
+ (void)close(sock_raw);
+ sock_raw = -1;
+ }
+
+ if (status == MASTER)
+ master();
+ else
+ slave();
+
+ } else {
+ if (sock_raw != -1) {
+ (void)close(sock_raw);
+ sock_raw = -1;
+ }
+
+ if (ret) {
+ /* we just lost our master or were told to quit */
+ justquit = 1;
+ }
+ for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
+ if (ntp->status == MASTER)
+ rmnetmachs(ntp);
+ ntp->status = NOMASTER;
+ }
+ checkignorednets();
+ pickslavenet(0);
+ setstatus();
+
+ slave();
+ }
+ /* NOTREACHED */
+ return(0);
+}
+
+static void
+usage()
+{
+#ifdef HAVENIS
+ fprintf(stderr,
+"usage: timed [-dtM] [-i net|-n net] [-F host1 host2 ...] [-G netgp]\n");
+#else
+ fprintf(stderr,
+"usage: timed [-dtM] [-i net|-n net] [-F host1 host2 ...]\n");
+#endif /* HAVENIS */
+ exit(1);
+}
+
+/*
+ * suppress an upstart, untrustworthy, self-appointed master
+ */
+void
+suppress(addr, name,net)
+ struct sockaddr_in *addr;
+ char *name;
+ struct netinfo *net;
+{
+ struct sockaddr_in tgt;
+ char tname[MAXHOSTNAMELEN];
+ struct tsp msg;
+ static struct timeval wait;
+
+ if (trace)
+ fprintf(fd, "suppress: %s\n", name);
+ tgt = *addr;
+ (void)strcpy(tname, name);
+
+ while (0 != readmsg(TSP_ANY, ANYADDR, &wait, net)) {
+ if (trace)
+ fprintf(fd, "suppress:\tdiscarded packet from %s\n",
+ name);
+ }
+
+ syslog(LOG_NOTICE, "suppressing false master %s", tname);
+ msg.tsp_type = TSP_QUIT;
+ (void)strcpy(msg.tsp_name, hostname);
+ (void)acksend(&msg, &tgt, tname, TSP_ACK, 0, 1);
+}
+
+void
+lookformaster(ntp)
+ struct netinfo *ntp;
+{
+ struct tsp resp, conflict, *answer;
+ struct timeval ntime;
+ char mastername[MAXHOSTNAMELEN];
+ struct sockaddr_in masteraddr;
+
+ get_goodgroup(0);
+ ntp->status = SLAVE;
+
+ /* look for master */
+ resp.tsp_type = TSP_MASTERREQ;
+ (void)strcpy(resp.tsp_name, hostname);
+ answer = acksend(&resp, &ntp->dest_addr, ANYADDR,
+ TSP_MASTERACK, ntp, 0);
+ if (answer != 0 && !good_host_name(answer->tsp_name)) {
+ suppress(&from, answer->tsp_name, ntp);
+ ntp->status = NOMASTER;
+ answer = 0;
+ }
+ if (answer == 0) {
+ /*
+ * Various conditions can cause conflict: races between
+ * two just started timedaemons when no master is
+ * present, or timedaemons started during an election.
+ * A conservative approach is taken. Give up and became a
+ * slave, postponing election of a master until first
+ * timer expires.
+ */
+ ntime.tv_sec = ntime.tv_usec = 0;
+ answer = readmsg(TSP_MASTERREQ, ANYADDR, &ntime, ntp);
+ if (answer != 0) {
+ if (!good_host_name(answer->tsp_name)) {
+ suppress(&from, answer->tsp_name, ntp);
+ ntp->status = NOMASTER;
+ }
+ return;
+ }
+
+ ntime.tv_sec = ntime.tv_usec = 0;
+ answer = readmsg(TSP_MASTERUP, ANYADDR, &ntime, ntp);
+ if (answer != 0) {
+ if (!good_host_name(answer->tsp_name)) {
+ suppress(&from, answer->tsp_name, ntp);
+ ntp->status = NOMASTER;
+ }
+ return;
+ }
+
+ ntime.tv_sec = ntime.tv_usec = 0;
+ answer = readmsg(TSP_ELECTION, ANYADDR, &ntime, ntp);
+ if (answer != 0) {
+ if (!good_host_name(answer->tsp_name)) {
+ suppress(&from, answer->tsp_name, ntp);
+ ntp->status = NOMASTER;
+ }
+ return;
+ }
+
+ if (Mflag)
+ ntp->status = MASTER;
+ else
+ ntp->status = NOMASTER;
+ return;
+ }
+
+ ntp->status = SLAVE;
+ (void)strcpy(mastername, answer->tsp_name);
+ masteraddr = from;
+
+ /*
+ * If network has been partitioned, there might be other
+ * masters; tell the one we have just acknowledged that
+ * it has to gain control over the others.
+ */
+ ntime.tv_sec = 0;
+ ntime.tv_usec = 300000;
+ answer = readmsg(TSP_MASTERACK, ANYADDR, &ntime, ntp);
+ /*
+ * checking also not to send CONFLICT to ack'ed master
+ * due to duplicated MASTERACKs
+ */
+ if (answer != NULL &&
+ strcmp(answer->tsp_name, mastername) != 0) {
+ conflict.tsp_type = TSP_CONFLICT;
+ (void)strcpy(conflict.tsp_name, hostname);
+ if (!acksend(&conflict, &masteraddr, mastername,
+ TSP_ACK, 0, 0)) {
+ syslog(LOG_ERR,
+ "error on sending TSP_CONFLICT");
+ }
+ }
+}
+
+/*
+ * based on the current network configuration, set the status, and count
+ * networks;
+ */
+void
+setstatus()
+{
+ struct netinfo *ntp;
+
+ status = 0;
+ nmasternets = nslavenets = nnets = nignorednets = 0;
+ if (trace)
+ fprintf(fd, "Net status:\n");
+ for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
+ switch ((int)ntp->status) {
+ case MASTER:
+ nmasternets++;
+ break;
+ case SLAVE:
+ nslavenets++;
+ break;
+ case NOMASTER:
+ case IGNORE:
+ nignorednets++;
+ break;
+ }
+ if (trace) {
+ fprintf(fd, "\t%-16s", inet_ntoa(ntp->net));
+ switch ((int)ntp->status) {
+ case NOMASTER:
+ fprintf(fd, "NOMASTER\n");
+ break;
+ case MASTER:
+ fprintf(fd, "MASTER\n");
+ break;
+ case SLAVE:
+ fprintf(fd, "SLAVE\n");
+ break;
+ case IGNORE:
+ fprintf(fd, "IGNORE\n");
+ break;
+ default:
+ fprintf(fd, "invalid state %d\n",
+ (int)ntp->status);
+ break;
+ }
+ }
+ nnets++;
+ status |= ntp->status;
+ }
+ status &= ~IGNORE;
+ if (trace)
+ fprintf(fd,
+ "\tnets=%d masters=%d slaves=%d ignored=%d delay2=%ld\n",
+ nnets, nmasternets, nslavenets, nignorednets, delay2);
+}
+
+void
+makeslave(net)
+ struct netinfo *net;
+{
+ register struct netinfo *ntp;
+
+ for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
+ if (ntp->status == SLAVE && ntp != net)
+ ntp->status = IGNORE;
+ }
+ slavenet = net;
+}
+
+/*
+ * Try to become master over ignored nets..
+ */
+static void
+checkignorednets()
+{
+ register struct netinfo *ntp;
+
+ for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
+ if (!Mflag && ntp->status == SLAVE)
+ break;
+
+ if (ntp->status == IGNORE || ntp->status == NOMASTER) {
+ lookformaster(ntp);
+ if (!Mflag && ntp->status == SLAVE)
+ break;
+ }
+ }
+}
+
+/*
+ * choose a good network on which to be a slave
+ * The ignored networks must have already been checked.
+ * Take a hint about for a good network.
+ */
+static void
+pickslavenet(ntp)
+ struct netinfo *ntp;
+{
+ if (slavenet != 0 && slavenet->status == SLAVE) {
+ makeslave(slavenet); /* prune extras */
+ return;
+ }
+
+ if (ntp == 0 || ntp->status != SLAVE) {
+ for (ntp = nettab; ntp != 0; ntp = ntp->next) {
+ if (ntp->status == SLAVE)
+ break;
+ }
+ }
+ makeslave(ntp);
+}
+
+/*
+ * returns a random number in the range [inf, sup]
+ */
+long
+casual(inf, sup)
+ long inf, sup;
+{
+ double value;
+
+ value = ((double)(random() & 0x7fffffff)) / (0x7fffffff*1.0);
+ return(inf + (sup - inf)*value);
+}
+
+char *
+date()
+{
+ struct timeval tv;
+ time_t tv_sec;
+
+ (void)gettimeofday(&tv, (struct timezone *)0);
+ tv_sec = tv.tv_sec;
+ return (ctime(&tv_sec));
+}
+
+void
+addnetname(name)
+ char *name;
+{
+ register struct nets **netlist = &nets;
+
+ while (*netlist)
+ netlist = &((*netlist)->next);
+ *netlist = (struct nets *)malloc(sizeof **netlist);
+ if (*netlist == 0)
+ errx(1, "malloc failed");
+ bzero((char *)*netlist, sizeof(**netlist));
+ (*netlist)->name = name;
+}
+
+/* note a host as trustworthy */
+static void
+add_good_host(name, perm)
+ char *name;
+ int perm; /* 1=not part of the netgroup */
+{
+ register struct goodhost *ghp;
+ register struct hostent *hentp;
+
+ ghp = (struct goodhost*)malloc(sizeof(*ghp));
+ if (!ghp) {
+ syslog(LOG_ERR, "malloc failed");
+ exit(1);
+ }
+
+ bzero((char*)ghp, sizeof(*ghp));
+ (void)strncpy(&ghp->name[0], name, sizeof(ghp->name));
+ ghp->next = goodhosts;
+ ghp->perm = perm;
+ goodhosts = ghp;
+
+ hentp = gethostbyname(name);
+ if (0 == hentp && perm)
+ warnx("unknown host %s", name);
+}
+
+
+/* update our image of the net-group of trustworthy hosts
+ */
+void
+get_goodgroup(force)
+ int force;
+{
+# define NG_DELAY (30*60*CLK_TCK) /* 30 minutes */
+ static unsigned long last_update = -NG_DELAY;
+ unsigned long new_update;
+ struct goodhost *ghp, **ghpp;
+#ifdef HAVENIS
+ struct hosttbl *htp;
+ char *mach, *usr, *dom;
+#endif /* HAVENIS */
+ struct tms tm;
+
+
+ /* if no netgroup, then we are finished */
+ if (goodgroup == 0 || !Mflag)
+ return;
+
+ /* Do not chatter with the netgroup master too often.
+ */
+ new_update = times(&tm);
+ if (new_update < last_update + NG_DELAY
+ && !force)
+ return;
+ last_update = new_update;
+
+ /* forget the old temporary entries */
+ ghpp = &goodhosts;
+ while (0 != (ghp = *ghpp)) {
+ if (!ghp->perm) {
+ *ghpp = ghp->next;
+ free((char*)ghp);
+ } else {
+ ghpp = &ghp->next;
+ }
+ }
+
+#ifdef HAVENIS
+ /* quit now if we are not one of the trusted masters
+ */
+ if (!innetgr(goodgroup, &hostname[0], 0,0)) {
+ if (trace)
+ (void)fprintf(fd, "get_goodgroup: %s not in %s\n",
+ &hostname[0], goodgroup);
+ return;
+ }
+ if (trace)
+ (void)fprintf(fd, "get_goodgroup: %s in %s\n",
+ &hostname[0], goodgroup);
+
+ /* mark the entire netgroup as trusted */
+ (void)setnetgrent(goodgroup);
+ while (getnetgrent(&mach,&usr,&dom)) {
+ if (0 != mach)
+ add_good_host(mach,0);
+ }
+ (void)endnetgrent();
+
+ /* update list of slaves */
+ for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) {
+ htp->good = good_host_name(&htp->name[0]);
+ }
+#endif /* HAVENIS */
+}
+
+
+/* see if a machine is trustworthy
+ */
+int /* 1=trust hp to change our date */
+good_host_name(name)
+ char *name;
+{
+ register struct goodhost *ghp = goodhosts;
+ register char c;
+
+ if (!ghp || !Mflag) /* trust everyone if no one named */
+ return 1;
+
+ c = *name;
+ do {
+ if (c == ghp->name[0]
+ && !strcasecmp(name, ghp->name))
+ return 1; /* found him, so say so */
+ } while (0 != (ghp = ghp->next));
+
+ if (!strcasecmp(name,hostname)) /* trust ourself */
+ return 1;
+
+ return 0; /* did not find him */
+}
diff --git a/usr.sbin/timed/timedc/Makefile b/usr.sbin/timed/timedc/Makefile
new file mode 100644
index 0000000..5262f46
--- /dev/null
+++ b/usr.sbin/timed/timedc/Makefile
@@ -0,0 +1,13 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../timed
+
+PROG= timedc
+MAN= timedc.8
+SRCS= cmds.c cmdtab.c timedc.c byteorder.c measure.c cksum.c
+BINOWN= root
+BINMODE= 4555
+
+.include "../../Makefile.inc"
+.include <bsd.prog.mk>
diff --git a/usr.sbin/timed/timedc/cmds.c b/usr.sbin/timed/timedc/cmds.c
new file mode 100644
index 0000000..edb41d1
--- /dev/null
+++ b/usr.sbin/timed/timedc/cmds.c
@@ -0,0 +1,555 @@
+/*-
+ * 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[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include "timedc.h"
+#include <sys/file.h>
+
+#include <arpa/inet.h>
+
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+
+#include <err.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+
+#define TSPTYPES
+#include <protocols/timed.h>
+
+#define SECHR (60*60)
+#define SECDAY (24*SECHR)
+
+# define DATE_PROTO "udp"
+# define DATE_PORT "time"
+
+
+int sock;
+int sock_raw;
+char myname[MAXHOSTNAMELEN];
+struct hostent *hp;
+struct sockaddr_in server;
+struct sockaddr_in dayaddr;
+extern int measure_delta;
+
+void bytenetorder(struct tsp *);
+void bytehostorder(struct tsp *);
+
+
+#define BU (2208988800UL) /* seconds before UNIX epoch */
+
+
+/* compute the difference between our date and another machine
+ */
+static int /* difference in days from our time */
+daydiff(hostname)
+ char *hostname;
+{
+ int i;
+ int trials;
+ struct timeval tout, now;
+ fd_set ready;
+ struct sockaddr from;
+ int fromlen;
+ unsigned long sec;
+
+
+ /* wait 2 seconds between 10 tries */
+ tout.tv_sec = 2;
+ tout.tv_usec = 0;
+ for (trials = 0; trials < 10; trials++) {
+ /* ask for the time */
+ sec = 0;
+ if (sendto(sock, &sec, sizeof(sec), 0,
+ (struct sockaddr*)&dayaddr, sizeof(dayaddr)) < 0) {
+ warn("sendto(sock)");
+ return 0;
+ }
+
+ for (;;) {
+ FD_ZERO(&ready);
+ FD_SET(sock, &ready);
+ i = select(sock+1, &ready, (fd_set *)0,
+ (fd_set *)0, &tout);
+ if (i < 0) {
+ if (errno == EINTR)
+ continue;
+ warn("select(date read)");
+ return 0;
+ }
+ if (0 == i)
+ break;
+
+ fromlen = sizeof(from);
+ if (recvfrom(sock,&sec,sizeof(sec),0,
+ &from,&fromlen) < 0) {
+ warn("recvfrom(date read)");
+ return 0;
+ }
+
+ sec = ntohl(sec);
+ if (sec < BU) {
+ warnx("%s says it is before 1970: %lu",
+ hostname, sec);
+ return 0;
+ }
+ sec -= BU;
+
+ (void)gettimeofday(&now, (struct timezone*)0);
+ return (sec - now.tv_sec);
+ }
+ }
+
+ /* if we get here, we tried too many times */
+ warnx("%s will not tell us the date", hostname);
+ return 0;
+}
+
+
+/*
+ * Clockdiff computes the difference between the time of the machine on
+ * which it is called and the time of the machines given as argument.
+ * The time differences measured by clockdiff are obtained using a sequence
+ * of ICMP TSTAMP messages which are returned to the sender by the IP module
+ * in the remote machine.
+ * In order to compare clocks of machines in different time zones, the time
+ * is transmitted (as a 32-bit value) in milliseconds since midnight UT.
+ * If a hosts uses a different time format, it should set the high order
+ * bit of the 32-bit quantity it transmits.
+ * However, VMS apparently transmits the time in milliseconds since midnight
+ * local time (rather than GMT) without setting the high order bit.
+ * Furthermore, it does not understand daylight-saving time. This makes
+ * clockdiff behaving inconsistently with hosts running VMS.
+ *
+ * In order to reduce the sensitivity to the variance of message transmission
+ * time, clockdiff sends a sequence of messages. Yet, measures between
+ * two `distant' hosts can be affected by a small error. The error can,
+ * however, be reduced by increasing the number of messages sent in each
+ * measurement.
+ */
+void
+clockdiff(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int measure_status;
+ extern int measure(u_long, u_long, char *, struct sockaddr_in*, int);
+ register int avg_cnt;
+ register long avg;
+ struct servent *sp;
+
+ if (argc < 2) {
+ printf("usage: timedc clockdiff host ...\n");
+ return;
+ }
+
+ if (gethostname(myname, sizeof(myname) - 1) < 0)
+ err(1, "gethostname");
+
+ /* get the address for the date ready */
+ sp = getservbyname(DATE_PORT, DATE_PROTO);
+ if (!sp) {
+ warnx("%s/%s 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_in from;
+ struct timeval tout;
+ struct tsp msg;
+ struct servent *srvp;
+ char *tgtname;
+
+ if (argc < 1) {
+ printf("usage: timedc msite [host ...]\n");
+ return;
+ }
+
+ srvp = getservbyname("timed", "udp");
+ if (srvp == 0) {
+ warnx("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)strlcpy(msg.tsp_name, myname, sizeof(msg.tsp_name));
+ msg.tsp_type = TSP_MSITE;
+ msg.tsp_vers = TSPVERSION;
+ bytenetorder(&msg);
+ if (sendto(sock, &msg, sizeof(struct tsp), 0,
+ (struct sockaddr*)&dest,
+ sizeof(struct sockaddr)) < 0) {
+ warn("sendto");
+ continue;
+ }
+
+ tout.tv_sec = 15;
+ tout.tv_usec = 0;
+ FD_ZERO(&ready);
+ FD_SET(sock, &ready);
+ if (select(FD_SETSIZE, &ready, (fd_set *)0, (fd_set *)0,
+ &tout)) {
+ length = sizeof(from);
+ cc = recvfrom(sock, &msg, sizeof(struct tsp), 0,
+ (struct sockaddr *)&from, &length);
+ if (cc < 0) {
+ warn("recvfrom");
+ continue;
+ }
+ /*
+ * The 4.3BSD protocol spec had a 32-byte tsp_name field, and
+ * this is still OS-dependent. Demand that the packet is at
+ * least long enough to hold a 4.3BSD packet.
+ */
+ if (cc < (sizeof(struct tsp) - MAXHOSTNAMELEN + 32)) {
+ fprintf(stderr,
+ "short packet (%u/%u bytes) from %s\n",
+ cc, sizeof(struct tsp) - MAXHOSTNAMELEN + 32,
+ inet_ntoa(from.sin_addr));
+ continue;
+ }
+ bytehostorder(&msg);
+ if (msg.tsp_type == TSP_ACK) {
+ printf("master timedaemon at %s is %s\n",
+ tgtname, msg.tsp_name);
+ } else {
+ if (msg.tsp_type >= TSPTYPENUMBER)
+ printf("unknown ack received: %u\n",
+ msg.tsp_type);
+ else
+ printf("wrong ack received: %s\n",
+ tsptype[msg.tsp_type]);
+ }
+ } else {
+ printf("communication error with %s\n", tgtname);
+ }
+ } while (++i < argc);
+}
+
+/*
+ * quits timedc
+ */
+void
+quit()
+{
+ exit(0);
+}
+
+
+/*
+ * Causes the election timer to expire on the selected hosts
+ * It sends just one udp message per machine, relying on
+ * reliability of communication channel.
+ */
+void
+testing(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct servent *srvp;
+ struct sockaddr_in sin;
+ struct tsp msg;
+
+ if (argc < 2) {
+ printf("usage: timedc election host1 [host2 ...]\n");
+ return;
+ }
+
+ srvp = getservbyname("timed", "udp");
+ if (srvp == 0) {
+ warnx("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)strlcpy(msg.tsp_name, myname, sizeof(msg.tsp_name));
+ bytenetorder(&msg);
+ if (sendto(sock, &msg, sizeof(struct tsp), 0,
+ (struct sockaddr*)&sin,
+ sizeof(struct sockaddr)) < 0) {
+ warn("sendto");
+ }
+ }
+}
+
+
+/*
+ * Enables or disables tracing on local timedaemon
+ */
+void
+tracing(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int onflag;
+ int length;
+ int cc;
+ fd_set ready;
+ struct sockaddr_in dest;
+ struct sockaddr_in from;
+ struct timeval tout;
+ struct tsp msg;
+ struct servent *srvp;
+
+ if (argc != 2) {
+ printf("usage: timedc trace { on | off }\n");
+ return;
+ }
+
+ srvp = getservbyname("timed", "udp");
+ if (srvp == 0) {
+ warnx("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(from);
+ cc = recvfrom(sock, &msg, sizeof(struct tsp), 0,
+ (struct sockaddr *)&from, &length);
+ if (cc < 0) {
+ warn("recvfrom");
+ return;
+ }
+ /*
+ * The 4.3BSD protocol spec had a 32-byte tsp_name field, and
+ * this is still OS-dependent. Demand that the packet is at
+ * least long enough to hold a 4.3BSD packet.
+ */
+ if (cc < (sizeof(struct tsp) - MAXHOSTNAMELEN + 32)) {
+ fprintf(stderr, "short packet (%u/%u bytes) from %s\n",
+ cc, sizeof(struct tsp) - MAXHOSTNAMELEN + 32,
+ inet_ntoa(from.sin_addr));
+ return;
+ }
+ bytehostorder(&msg);
+ if (msg.tsp_type == TSP_ACK)
+ if (onflag)
+ printf("timed tracing enabled\n");
+ else
+ printf("timed tracing disabled\n");
+ else {
+ if (msg.tsp_type >= TSPTYPENUMBER)
+ printf("unknown ack received: %u\n",
+ msg.tsp_type);
+ else
+ printf("wrong ack received: %s\n",
+ tsptype[msg.tsp_type]);
+ }
+ } else
+ printf("communication error\n");
+}
+
+int
+priv_resources()
+{
+ int port;
+ struct sockaddr_in sin;
+
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock < 0) {
+ warn("opening socket");
+ return(-1);
+ }
+
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = 0;
+ for (port = IPPORT_RESERVED - 1; port > IPPORT_RESERVED / 2; port--) {
+ sin.sin_port = htons((u_short)port);
+ if (bind(sock, (struct sockaddr*)&sin, sizeof (sin)) >= 0)
+ break;
+ if (errno != EADDRINUSE && errno != EADDRNOTAVAIL) {
+ warn("bind");
+ (void) close(sock);
+ return(-1);
+ }
+ }
+ if (port == IPPORT_RESERVED / 2) {
+ warnx("all reserved ports in use");
+ (void) close(sock);
+ return(-1);
+ }
+
+ sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
+ if (sock_raw < 0) {
+ warn("opening raw socket");
+ (void) close(sock);
+ return(-1);
+ }
+ return(1);
+}
diff --git a/usr.sbin/timed/timedc/cmdtab.c b/usr.sbin/timed/timedc/cmdtab.c
new file mode 100644
index 0000000..8e4c3d5
--- /dev/null
+++ b/usr.sbin/timed/timedc/cmdtab.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)cmdtab.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include "timedc.h"
+
+char clockdiffhelp[] = "measures clock differences between machines";
+char helphelp[] = "gets help on commands";
+char msitehelp[] = "finds location of master";
+char quithelp[] = "exits timedc";
+char testinghelp[] = "causes election timers to expire";
+char tracinghelp[] = "turns tracing on or off";
+
+struct cmd cmdtab[] = {
+ { "clockdiff", clockdiffhelp, clockdiff, 0 },
+ { "election", testinghelp, testing, 1 },
+ { "help", helphelp, help, 0 },
+ { "msite", msitehelp, msite, 0 },
+ { "quit", quithelp, quit, 0 },
+ { "trace", tracinghelp, tracing, 1 },
+ { "?", helphelp, help, 0 },
+};
+
+int NCMDS = sizeof (cmdtab) / sizeof (cmdtab[0]);
diff --git a/usr.sbin/timed/timedc/extern.h b/usr.sbin/timed/timedc/extern.h
new file mode 100644
index 0000000..7f33362
--- /dev/null
+++ b/usr.sbin/timed/timedc/extern.h
@@ -0,0 +1,52 @@
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)extern.h 8.1 (Berkeley) 6/6/93
+ */
+
+#if __STDC__
+struct tsp;
+#endif
+
+extern struct cmd cmdtab[];
+
+void bytehostorder __P((struct tsp *));
+void bytenetorder __P((struct tsp *));
+void clockdiff __P((int, char *[]));
+void help __P((int, char *[]));
+void intr __P((int));
+void makeargv __P((void));
+void msite __P((int, char *[]));
+int priv_resources __P((void));
+void quit __P((void));
+void testing __P((int, char *[]));
+void tracing __P((int, char *[]));
diff --git a/usr.sbin/timed/timedc/timedc.8 b/usr.sbin/timed/timedc/timedc.8
new file mode 100644
index 0000000..c431c56
--- /dev/null
+++ b/usr.sbin/timed/timedc/timedc.8
@@ -0,0 +1,143 @@
+.\" Copyright (c) 1980, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)timedc.8 8.1 (Berkeley) 6/6/93
+.\" $FreeBSD$
+.\"
+.Dd June 6, 1993
+.Dt TIMEDC 8
+.Os
+.Sh NAME
+.Nm timedc
+.Nd timed control program
+.Sh SYNOPSIS
+.Nm
+.Op Ar command Op Ar argument ...
+.Sh DESCRIPTION
+.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 -diag
+.It ?Ambiguous command
+abbreviation matches more than one command
+.It ?Invalid command
+no match found
+.It ?Privileged command
+command can be executed by root only
+.El
+.Sh HISTORY
+The
+.Nm
+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..71ac9b3
--- /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[] =
+ "$FreeBSD$";
+#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..f6ee8a6
--- /dev/null
+++ b/usr.sbin/timed/timedc/timedc.h
@@ -0,0 +1,63 @@
+/*-
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)timedc.h 8.1 (Berkeley) 6/6/93
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <netdb.h>
+#include <stdio.h>
+
+#define ON 1
+#define OFF 0
+
+#define GOOD 1
+#define UNREACHABLE 2
+#define NONSTDTIME 3
+#define HOSTDOWN 0x7fffffff
+
+struct cmd {
+ char *c_name; /* command name */
+ char *c_help; /* help message */
+ void (*c_handler)(); /* routine to do the work */
+ int c_priv; /* privileged command */
+};
+
+#include "extern.h"
diff --git a/usr.sbin/traceroute/Makefile b/usr.sbin/traceroute/Makefile
new file mode 100644
index 0000000..bdd65b4
--- /dev/null
+++ b/usr.sbin/traceroute/Makefile
@@ -0,0 +1,41 @@
+# $FreeBSD$
+
+TRACEROUTE_DISTDIR?= ${.CURDIR}/../../contrib/traceroute
+.PATH: ${TRACEROUTE_DISTDIR}
+
+PROG= traceroute
+MAN= traceroute.8
+SRCS= version.c traceroute.c
+BINOWN= root
+BINMODE=4555
+CLEANFILES= version.c
+
+CFLAGS+= -DHAVE_SYS_SELECT_H=1 -DHAVE_SYS_SYSCTL_H=1 \
+ -DHAVE_SETLINEBUF=1 -DHAVE_RAW_OPTIONS=1 \
+ -DSTDC_HEADERS=1
+.ifndef (NOIPSEC)
+CFLAGS+= -DIPSEC
+.endif
+# RTT Jitter on the internet these days means printing 3 decimal places on
+# > 1000ms times is plain useless. Uncomment this to enable variable precision
+# reporting, ie: print a variable precision from 0.001ms through 1000ms
+# CFLAGS+= -DSANE_PRECISION
+
+.ifndef (NOIPSEC)
+DPADD= ${LIBIPSEC}
+LDADD= -lipsec
+.endif
+
+.if ${MACHINE_ARCH} == "alpha"
+# gcc builtin memcpy causes unaligned access
+CFLAGS+= -fno-builtin
+.endif
+
+CFLAGS+= -I${TRACEROUTE_DISTDIR}/lbl
+
+version.c: ${TRACEROUTE_DISTDIR}/VERSION
+ @rm -f ${.TARGET}
+ sed -e 's/.*/char version[] = "&";/' ${TRACEROUTE_DISTDIR}/VERSION \
+ > ${.TARGET}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/traceroute6/Makefile b/usr.sbin/traceroute6/Makefile
new file mode 100644
index 0000000..d4e63ac
--- /dev/null
+++ b/usr.sbin/traceroute6/Makefile
@@ -0,0 +1,26 @@
+# Copyright (c) 1996 WIDE Project. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modifications, are permitted provided that the above copyright notice
+# and this paragraph are duplicated in all such forms and that any
+# documentation, advertising materials, and other materials related to
+# such distribution and use acknowledge that the software was developed
+# by the WIDE Project, Japan. The name of the Project may not be used to
+# endorse or promote products derived from this software without
+# specific prior written permission. THIS SOFTWARE IS PROVIDED ``AS IS''
+# AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+# LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE.
+# $FreeBSD$
+
+PROG= traceroute6
+MAN= traceroute6.8
+BINOWN= root
+BINMODE= 4555
+
+CFLAGS+= -DINET6 -DIPSEC -DHAVE_POLL
+
+DPADD= ${LIBIPSEC}
+LDADD= -lipsec
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/traceroute6/traceroute6.8 b/usr.sbin/traceroute6/traceroute6.8
new file mode 100644
index 0000000..879e34e
--- /dev/null
+++ b/usr.sbin/traceroute6/traceroute6.8
@@ -0,0 +1,123 @@
+.\" $KAME: traceroute6.8,v 1.8 2000/06/12 16:29:18 itojun Exp $
+.\"
+.\" Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the project nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd May 17, 1998
+.Dt TRACEROUTE6 8
+.Os
+.\"
+.Sh NAME
+.Nm traceroute6
+.Nd "print the route IPv6 packets will take to the destination"
+.\"
+.Sh SYNOPSIS
+.Nm
+.Bk -words
+.Op Fl dlnrv
+.Ek
+.Bk -words
+.Op Fl f Ar firsthop
+.Ek
+.Bk -words
+.Op Fl g Ar gateway
+.Ek
+.Bk -words
+.Op Fl m Ar hoplimit
+.Ek
+.Bk -words
+.Op Fl p Ar port
+.Ek
+.Bk -words
+.Op Fl q Ar probes
+.Ek
+.Bk -words
+.Op Fl s Ar src
+.Ek
+.Bk -words
+.Op Fl w Ar waittime
+.Ek
+.Bk -words
+.Ar target
+.Op Ar datalen
+.Ek
+.\"
+.Sh DESCRIPTION
+.Bl -tag -width Ds
+.It Fl d
+Debug mode.
+.It Fl f Ar firsthop
+Specify how many hops to skip in trace.
+.It Fl g Ar gateway
+Specify intermediate gateway
+.Nm (
+uses routing header).
+.It Fl m Ar hoplimit
+Specify maximum hoplimit.
+.It Fl l
+Print both host hostnames and numeric addresses.
+Normally
+.Nm
+prints only hostnames if
+.Fl n
+is not specified, and only numeric addresses if
+.Fl n
+is specified.
+.It Fl n
+Do not resolve numeric address to hostname.
+.It Fl p Ar port
+Set UDP port number to
+.Ar port .
+.It Fl q Ar probes
+Set the number of probe per hop count to
+.Ar probes .
+.It Fl r
+.It Fl s Ar src
+.Ar Src
+specifies the source IPv6 address to be used.
+.It Fl v
+Be verbose.
+.It Fl w Ar waittime
+Specify the delay time between probes.
+.El
+.\"
+.Sh RETURN VALUES
+The
+.Nm
+command will exit with 0 on success, and non-zero on errors.
+.\"
+.Sh SEE ALSO
+.Xr ping 8 ,
+.Xr ping6 8 ,
+.Xr traceroute 8
+.\"
+.Sh HISTORY
+The
+.Nm
+command first appeared in WIDE hydrangea IPv6 protocol stack kit.
diff --git a/usr.sbin/traceroute6/traceroute6.c b/usr.sbin/traceroute6/traceroute6.c
new file mode 100644
index 0000000..f5f3fbe
--- /dev/null
+++ b/usr.sbin/traceroute6/traceroute6.c
@@ -0,0 +1,1357 @@
+/* $KAME: traceroute6.c,v 1.42 2001/05/08 04:36:41 itojun Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Van Jacobson.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1990, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)traceroute.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/*
+ * traceroute host - trace the route ip packets follow going to "host".
+ *
+ * Attempt to trace the route an ip packet would follow to some
+ * internet host. We find out intermediate hops by launching probe
+ * packets with a small ttl (time to live) then listening for an
+ * icmp "time exceeded" reply from a gateway. We start our probes
+ * with a ttl of one and increase by one until we get an icmp "port
+ * unreachable" (which means we got to "host") or hit a max (which
+ * defaults to 30 hops & can be changed with the -m flag). Three
+ * probes (change with -q flag) are sent at each ttl setting and a
+ * line is printed showing the ttl, address of the gateway and
+ * round trip time of each probe. If the probe answers come from
+ * different gateways, the address of each responding system will
+ * be printed. If there is no response within a 5 sec. timeout
+ * interval (changed with the -w flag), a "*" is printed for that
+ * probe.
+ *
+ * Probe packets are UDP format. We don't want the destination
+ * host to process them so the destination port is set to an
+ * unlikely value (if some clod on the destination is using that
+ * value, it can be changed with the -p flag).
+ *
+ * A sample use might be:
+ *
+ * [yak 71]% traceroute nis.nsf.net.
+ * traceroute to nis.nsf.net (35.1.1.48), 30 hops max, 56 byte packet
+ * 1 helios.ee.lbl.gov (128.3.112.1) 19 ms 19 ms 0 ms
+ * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 39 ms 19 ms
+ * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 39 ms 19 ms
+ * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 39 ms 40 ms 39 ms
+ * 5 ccn-nerif22.Berkeley.EDU (128.32.168.22) 39 ms 39 ms 39 ms
+ * 6 128.32.197.4 (128.32.197.4) 40 ms 59 ms 59 ms
+ * 7 131.119.2.5 (131.119.2.5) 59 ms 59 ms 59 ms
+ * 8 129.140.70.13 (129.140.70.13) 99 ms 99 ms 80 ms
+ * 9 129.140.71.6 (129.140.71.6) 139 ms 239 ms 319 ms
+ * 10 129.140.81.7 (129.140.81.7) 220 ms 199 ms 199 ms
+ * 11 nic.merit.edu (35.1.1.48) 239 ms 239 ms 239 ms
+ *
+ * Note that lines 2 & 3 are the same. This is due to a buggy
+ * kernel on the 2nd hop system -- lbl-csam.arpa -- that forwards
+ * packets with a zero ttl.
+ *
+ * A more interesting example is:
+ *
+ * [yak 72]% traceroute allspice.lcs.mit.edu.
+ * traceroute to allspice.lcs.mit.edu (18.26.0.115), 30 hops max
+ * 1 helios.ee.lbl.gov (128.3.112.1) 0 ms 0 ms 0 ms
+ * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 19 ms 19 ms 19 ms
+ * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 19 ms 19 ms
+ * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 19 ms 39 ms 39 ms
+ * 5 ccn-nerif22.Berkeley.EDU (128.32.168.22) 20 ms 39 ms 39 ms
+ * 6 128.32.197.4 (128.32.197.4) 59 ms 119 ms 39 ms
+ * 7 131.119.2.5 (131.119.2.5) 59 ms 59 ms 39 ms
+ * 8 129.140.70.13 (129.140.70.13) 80 ms 79 ms 99 ms
+ * 9 129.140.71.6 (129.140.71.6) 139 ms 139 ms 159 ms
+ * 10 129.140.81.7 (129.140.81.7) 199 ms 180 ms 300 ms
+ * 11 129.140.72.17 (129.140.72.17) 300 ms 239 ms 239 ms
+ * 12 * * *
+ * 13 128.121.54.72 (128.121.54.72) 259 ms 499 ms 279 ms
+ * 14 * * *
+ * 15 * * *
+ * 16 * * *
+ * 17 * * *
+ * 18 ALLSPICE.LCS.MIT.EDU (18.26.0.115) 339 ms 279 ms 279 ms
+ *
+ * (I start to see why I'm having so much trouble with mail to
+ * MIT.) Note that the gateways 12, 14, 15, 16 & 17 hops away
+ * either don't send ICMP "time exceeded" messages or send them
+ * with a ttl too small to reach us. 14 - 17 are running the
+ * MIT C Gateway code that doesn't send "time exceeded"s. God
+ * only knows what's going on with 12.
+ *
+ * The silent gateway 12 in the above may be the result of a bug in
+ * the 4.[23]BSD network code (and its derivatives): 4.x (x <= 3)
+ * sends an unreachable message using whatever ttl remains in the
+ * original datagram. Since, for gateways, the remaining ttl is
+ * zero, the icmp "time exceeded" is guaranteed to not make it back
+ * to us. The behavior of this bug is slightly more interesting
+ * when it appears on the destination system:
+ *
+ * 1 helios.ee.lbl.gov (128.3.112.1) 0 ms 0 ms 0 ms
+ * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 19 ms 39 ms
+ * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 19 ms 39 ms 19 ms
+ * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 39 ms 40 ms 19 ms
+ * 5 ccn-nerif35.Berkeley.EDU (128.32.168.35) 39 ms 39 ms 39 ms
+ * 6 csgw.Berkeley.EDU (128.32.133.254) 39 ms 59 ms 39 ms
+ * 7 * * *
+ * 8 * * *
+ * 9 * * *
+ * 10 * * *
+ * 11 * * *
+ * 12 * * *
+ * 13 rip.Berkeley.EDU (128.32.131.22) 59 ms ! 39 ms ! 39 ms !
+ *
+ * Notice that there are 12 "gateways" (13 is the final
+ * destination) and exactly the last half of them are "missing".
+ * What's really happening is that rip (a Sun-3 running Sun OS3.5)
+ * is using the ttl from our arriving datagram as the ttl in its
+ * icmp reply. So, the reply will time out on the return path
+ * (with no notice sent to anyone since icmp's aren't sent for
+ * icmp's) until we probe with a ttl that's at least twice the path
+ * length. I.e., rip is really only 7 hops away. A reply that
+ * returns with a ttl of 1 is a clue this problem exists.
+ * Traceroute prints a "!" after the time if the ttl is <= 1.
+ * Since vendors ship a lot of obsolete (DEC's Ultrix, Sun 3.x) or
+ * non-standard (HPUX) software, expect to see this problem
+ * frequently and/or take care picking the target host of your
+ * probes.
+ *
+ * Other possible annotations after the time are !H, !N, !P (got a host,
+ * network or protocol unreachable, respectively), !S or !F (source
+ * route failed or fragmentation needed -- neither of these should
+ * ever occur and the associated gateway is busted if you see one). If
+ * almost all the probes result in some kind of unreachable, traceroute
+ * will give up and exit.
+ *
+ * Notes
+ * -----
+ * This program must be run by root or be setuid. (I suggest that
+ * you *don't* make it setuid -- casual use could result in a lot
+ * of unnecessary traffic on our poor, congested nets.)
+ *
+ * This program requires a kernel mod that does not appear in any
+ * system available from Berkeley: A raw ip socket using proto
+ * IPPROTO_RAW must interpret the data sent as an ip datagram (as
+ * opposed to data to be wrapped in a ip datagram). See the README
+ * file that came with the source to this program for a description
+ * of the mods I made to /sys/netinet/raw_ip.c. Your mileage may
+ * vary. But, again, ANY 4.x (x < 4) BSD KERNEL WILL HAVE TO BE
+ * MODIFIED TO RUN THIS PROGRAM.
+ *
+ * The udp port usage may appear bizarre (well, ok, it is bizarre).
+ * The problem is that an icmp message only contains 8 bytes of
+ * data from the original datagram. 8 bytes is the size of a udp
+ * header so, if we want to associate replies with the original
+ * datagram, the necessary information must be encoded into the
+ * udp header (the ip id could be used but there's no way to
+ * interlock with the kernel's assignment of ip id's and, anyway,
+ * it would have taken a lot more kernel hacking to allow this
+ * code to set the ip id). So, to allow two or more users to
+ * use traceroute simultaneously, we use this task's pid as the
+ * source port (the high bit is set to move the port number out
+ * of the "likely" range). To keep track of which probe is being
+ * replied to (so times and/or hop counts don't get confused by a
+ * reply that was delayed in transit), we increment the destination
+ * port number before each probe.
+ *
+ * Don't use this as a coding example. I was trying to find a
+ * routing problem and this code sort-of popped out after 48 hours
+ * without sleep. I was amazed it ever compiled, much less ran.
+ *
+ * I stole the idea for this program from Steve Deering. Since
+ * the first release, I've learned that had I attended the right
+ * IETF working group meetings, I also could have stolen it from Guy
+ * Almes or Matt Mathis. I don't know (or care) who came up with
+ * the idea first. I envy the originators' perspicacity and I'm
+ * glad they didn't keep the idea a secret.
+ *
+ * Tim Seaver, Ken Adelman and C. Philip Wood provided bug fixes and/or
+ * enhancements to the original distribution.
+ *
+ * I've hacked up a round-trip-route version of this that works by
+ * sending a loose-source-routed udp datagram through the destination
+ * back to yourself. Unfortunately, SO many gateways botch source
+ * routing, the thing is almost worthless. Maybe one day...
+ *
+ * -- Van Jacobson (van@helios.ee.lbl.gov)
+ * Tue Dec 20 03:50:13 PST 1988
+ */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+
+#include <netinet/in.h>
+
+#include <arpa/inet.h>
+
+#include <netdb.h>
+#include <stdio.h>
+#include <err.h>
+#ifdef HAVE_POLL
+#include <poll.h>
+#endif
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+#include <netinet/udp.h>
+
+#ifdef IPSEC
+#include <net/route.h>
+#include <netinet6/ipsec.h>
+#endif
+
+#define DUMMY_PORT 10010
+
+#define MAXPACKET 65535 /* max ip packet size */
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 64
+#endif
+
+#ifndef FD_SET
+#define NFDBITS (8*sizeof(fd_set))
+#define FD_SETSIZE NFDBITS
+#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) bzero((char *)(p), sizeof(*(p)))
+#endif
+
+#define Fprintf (void)fprintf
+#define Sprintf (void)sprintf
+#define Printf (void)printf
+
+#ifndef HAVE_GETIPNODEBYNAME
+#define getipnodebyname(x, y, z, u) gethostbyname2((x), (y))
+#define freehostent(x)
+#endif
+
+/*
+ * format of a (udp) probe packet.
+ */
+struct opacket {
+ u_char seq; /* sequence number of this packet */
+ u_char hops; /* hop limit of the packet */
+ struct timeval tv; /* time packet left */
+};
+
+u_char packet[512]; /* last inbound (icmp) packet */
+struct opacket *outpacket; /* last output (udp) packet */
+
+int main __P((int, char *[]));
+int wait_for_reply __P((int, struct msghdr *));
+#ifdef IPSEC
+#ifdef IPSEC_POLICY_IPSEC
+int setpolicy __P((int so, char *policy));
+#endif
+#endif
+void send_probe __P((int, int));
+struct udphdr *get_udphdr __P((struct ip6_hdr *, u_char *));
+int get_hoplim __P((struct msghdr *));
+double deltaT __P((struct timeval *, struct timeval *));
+char *pr_type __P((int));
+int packet_ok __P((struct msghdr *, int, int));
+void print __P((struct msghdr *, int));
+void tvsub __P((struct timeval *, struct timeval *));
+const char *inetname __P((struct sockaddr *));
+void usage __P((void));
+
+int rcvsock; /* receive (icmp) socket file descriptor */
+int sndsock; /* send (udp) socket file descriptor */
+struct timezone tz; /* leftover */
+
+struct msghdr rcvmhdr;
+struct iovec rcviov[2];
+int rcvhlim;
+struct in6_pktinfo *rcvpktinfo;
+
+struct sockaddr_in6 Src, Dst, Rcv;
+int datalen; /* How much data */
+/* XXX: 2064 = 127(max hops in type 0 rthdr) * sizeof(ip6_hdr) + 16(margin) */
+char rtbuf[2064];
+#ifdef USE_RFC2292BIS
+struct ip6_rthdr *rth;
+#endif
+struct cmsghdr *cmsg;
+
+char *source = 0;
+char *hostname;
+
+int nprobes = 3;
+int first_hop = 1;
+int max_hops = 30;
+u_short ident;
+u_short port = 32768+666; /* start udp dest port # for probe packets */
+int options; /* socket options */
+int verbose;
+int waittime = 5; /* time to wait for response (in seconds) */
+int nflag; /* print addresses numerically */
+int lflag; /* print both numerical address & hostname */
+
+#ifdef KAME_SCOPEID
+const int niflag = NI_WITHSCOPEID;
+#else
+const int niflag = 0;
+#endif
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct hostent *hp;
+ int error;
+ struct addrinfo hints, *res;
+ int ch, i, on, probe, seq, hops, rcvcmsglen;
+ static u_char *rcvcmsgbuf;
+ char hbuf[NI_MAXHOST], src0[NI_MAXHOST];
+ char *ep;
+
+ /*
+ * Receive ICMP
+ */
+ if ((rcvsock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) {
+ perror("socket(ICMPv6)");
+ exit(5);
+ }
+
+ /* revoke privs */
+ seteuid(getuid());
+ setuid(getuid());
+
+ /* set a minimum set of socket options */
+ on = 1;
+ /* specify to tell receiving interface */
+#ifdef IPV6_RECVPKTINFO
+ if (setsockopt(rcvsock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on,
+ sizeof(on)) < 0)
+ err(1, "setsockopt(IPV6_RECVPKTINFO)");
+#else /* old adv. API */
+ if (setsockopt(rcvsock, IPPROTO_IPV6, IPV6_PKTINFO, &on,
+ sizeof(on)) < 0)
+ err(1, "setsockopt(IPV6_PKTINFO)");
+#endif
+
+ /* specify to tell value of hoplimit field of received IP6 hdr */
+#ifdef IPV6_RECVHOPLIMIT
+ if (setsockopt(rcvsock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on,
+ sizeof(on)) < 0)
+ err(1, "setsockopt(IPV6_RECVHOPLIMIT)");
+#else /* old adv. API */
+ if (setsockopt(rcvsock, IPPROTO_IPV6, IPV6_HOPLIMIT, &on,
+ sizeof(on)) < 0)
+ err(1, "setsockopt(IPV6_HOPLIMIT)");
+#endif
+
+ seq = 0;
+
+ while ((ch = getopt(argc, argv, "df:g:lm:np:q:rs:w:v")) != -1)
+ switch(ch) {
+ case 'd':
+ options |= SO_DEBUG;
+ break;
+ case 'f':
+ ep = NULL;
+ first_hop = strtoul(optarg, &ep, 0);
+ if (!*argv || *ep) {
+ Fprintf(stderr,
+ "traceroute6: invalid min hoplimit.\n");
+ exit(1);
+ }
+ if (first_hop > max_hops) {
+ Fprintf(stderr,
+ "traceroute6: min hoplimit must be <= %d.\n", max_hops);
+ exit(1);
+ }
+ break;
+ case 'g':
+ hp = getipnodebyname(optarg, AF_INET6, 0, &h_errno);
+ if (hp == NULL) {
+ Fprintf(stderr,
+ "traceroute6: unknown host %s\n", optarg);
+ exit(1);
+ }
+#ifdef USE_RFC2292BIS
+ if (rth == NULL) {
+ /*
+ * XXX: We can't detect the number of
+ * intermediate nodes yet.
+ */
+ if ((rth = inet6_rth_init((void *)rtbuf,
+ sizeof(rtbuf),
+ IPV6_RTHDR_TYPE_0,
+ 0)) == NULL) {
+ Fprintf(stderr,
+ "inet6_rth_init failed.\n");
+ exit(1);
+ }
+ }
+ if (inet6_rth_add((void *)rth,
+ (struct in6_addr *)hp->h_addr)) {
+ Fprintf(stderr,
+ "inet6_rth_add failed for %s\n",
+ optarg);
+ exit(1);
+ }
+#else /* old advanced API */
+ if (cmsg == NULL)
+ cmsg = inet6_rthdr_init(rtbuf, IPV6_RTHDR_TYPE_0);
+ inet6_rthdr_add(cmsg, (struct in6_addr *)hp->h_addr, IPV6_RTHDR_LOOSE);
+#endif
+ freehostent(hp);
+ break;
+ case 'l':
+ lflag++;
+ break;
+ case 'm':
+ ep = NULL;
+ max_hops = strtoul(optarg, &ep, 0);
+ if (!*argv || *ep) {
+ Fprintf(stderr,
+ "traceroute6: invalid max hoplimit.\n");
+ exit(1);
+ }
+ if (max_hops < first_hop) {
+ Fprintf(stderr,
+ "traceroute6: max hoplimit must be >= %d.\n", first_hop);
+ exit(1);
+ }
+ break;
+ case 'n':
+ nflag++;
+ break;
+ case 'p':
+ ep = NULL;
+ port = strtoul(optarg, &ep, 0);
+ if (!*argv || *ep) {
+ Fprintf(stderr,
+ "traceroute6: port.\n");
+ exit(1);
+ }
+ if (port < 1) {
+ Fprintf(stderr,
+ "traceroute6: port must be >0.\n");
+ exit(1);
+ }
+ break;
+ case 'q':
+ ep = NULL;
+ nprobes = strtoul(optarg, &ep, 0);
+ if (!*argv || *ep) {
+ Fprintf(stderr,
+ "traceroute6: invalid nprobes.\n");
+ exit(1);
+ }
+ if (nprobes < 1) {
+ Fprintf(stderr,
+ "traceroute6: nprobes must be >0.\n");
+ exit(1);
+ }
+ break;
+ case 'r':
+ options |= SO_DONTROUTE;
+ break;
+ case 's':
+ /*
+ * set the ip source address of the outbound
+ * probe (e.g., on a multi-homed host).
+ */
+ source = optarg;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 'w':
+ ep = NULL;
+ waittime = strtoul(optarg, &ep, 0);
+ if (!*argv || *ep) {
+ Fprintf(stderr,
+ "traceroute6: invalid wait time.\n");
+ exit(1);
+ }
+ if (waittime <= 1) {
+ Fprintf(stderr,
+ "traceroute6: wait must be >1 sec.\n");
+ exit(1);
+ }
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1 || argc > 2)
+ usage();
+
+#if 1
+ setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
+#else
+ setlinebuf (stdout);
+#endif
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_INET6;
+ hints.ai_socktype = SOCK_RAW;
+ hints.ai_protocol = IPPROTO_ICMPV6;
+ hints.ai_flags = AI_CANONNAME;
+ error = getaddrinfo(*argv, NULL, &hints, &res);
+ if (error) {
+ (void)fprintf(stderr,
+ "traceroute6: %s\n", gai_strerror(error));
+ exit(1);
+ }
+ if (res->ai_addrlen != sizeof(Dst)) {
+ (void)fprintf(stderr,
+ "traceroute6: size of sockaddr mismatch\n");
+ exit(1);
+ }
+ memcpy(&Dst, res->ai_addr, res->ai_addrlen);
+ hostname = res->ai_canonname ? strdup(res->ai_canonname) : *argv;
+ if (!hostname) {
+ (void)fprintf(stderr, "traceroute6: not enough core\n");
+ exit(1);
+ }
+
+ if (*++argv) {
+ ep = NULL;
+ datalen = strtoul(*argv, &ep, 0);
+ if (!*argv || *ep) {
+ Fprintf(stderr,
+ "traceroute6: invalid packet length.\n");
+ exit(1);
+ }
+ }
+ if (datalen < 0 || datalen >= MAXPACKET - sizeof(struct opacket)) {
+ Fprintf(stderr,
+ "traceroute6: packet size must be 0 <= s < %ld.\n",
+ (long)(MAXPACKET - sizeof(struct opacket)));
+ exit(1);
+ }
+ datalen += sizeof(struct opacket);
+ outpacket = (struct opacket *)malloc((unsigned)datalen);
+ if (! outpacket) {
+ perror("malloc");
+ exit(1);
+ }
+ (void) bzero((char *)outpacket, datalen);
+
+ /* initialize msghdr for receiving packets */
+ rcviov[0].iov_base = (caddr_t)packet;
+ rcviov[0].iov_len = sizeof(packet);
+ rcvmhdr.msg_name = (caddr_t)&Rcv;
+ rcvmhdr.msg_namelen = sizeof(Rcv);
+ rcvmhdr.msg_iov = rcviov;
+ rcvmhdr.msg_iovlen = 1;
+ rcvcmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo))
+ + CMSG_SPACE(sizeof(int));
+ if ((rcvcmsgbuf = malloc(rcvcmsglen)) == NULL) {
+ Fprintf(stderr, "traceroute6: malloc failed\n");
+ exit(1);
+ }
+ rcvmhdr.msg_control = (caddr_t) rcvcmsgbuf;
+ rcvmhdr.msg_controllen = rcvcmsglen;
+
+ if (options & SO_DEBUG)
+ (void) setsockopt(rcvsock, SOL_SOCKET, SO_DEBUG,
+ (char *)&on, sizeof(on));
+ if (options & SO_DONTROUTE)
+ (void) setsockopt(rcvsock, SOL_SOCKET, SO_DONTROUTE,
+ (char *)&on, sizeof(on));
+#ifdef IPSEC
+#ifdef IPSEC_POLICY_IPSEC
+ /*
+ * do not raise error even if setsockopt fails, kernel may have ipsec
+ * turned off.
+ */
+ if (setpolicy(rcvsock, "in bypass") < 0)
+ errx(1, "%s", ipsec_strerror());
+ if (setpolicy(rcvsock, "out bypass") < 0)
+ errx(1, "%s", ipsec_strerror());
+#else
+ {
+ int level = IPSEC_LEVEL_NONE;
+
+ (void)setsockopt(rcvsock, IPPROTO_IPV6, IPV6_ESP_TRANS_LEVEL, &level,
+ sizeof(level));
+ (void)setsockopt(rcvsock, IPPROTO_IPV6, IPV6_ESP_NETWORK_LEVEL, &level,
+ sizeof(level));
+#ifdef IP_AUTH_TRANS_LEVEL
+ (void)setsockopt(rcvsock, IPPROTO_IPV6, IPV6_AUTH_TRANS_LEVEL, &level,
+ sizeof(level));
+#else
+ (void)setsockopt(rcvsock, IPPROTO_IPV6, IPV6_AUTH_LEVEL, &level,
+ sizeof(level));
+#endif
+#ifdef IP_AUTH_NETWORK_LEVEL
+ (void)setsockopt(rcvsock, IPPROTO_IPV6, IPV6_AUTH_NETWORK_LEVEL, &level,
+ sizeof(level));
+#endif
+ }
+#endif /*IPSEC_POLICY_IPSEC*/
+#endif /*IPSEC*/
+
+ /*
+ * Send UDP
+ */
+ if ((sndsock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ perror("socket(SOCK_DGRAM)");
+ exit(5);
+ }
+#ifdef SO_SNDBUF
+ if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, (char *)&datalen,
+ sizeof(datalen)) < 0) {
+ perror("setsockopt(SO_SNDBUF)");
+ exit(6);
+ }
+#endif /* SO_SNDBUF */
+ if (options & SO_DEBUG)
+ (void) setsockopt(sndsock, SOL_SOCKET, SO_DEBUG,
+ (char *)&on, sizeof(on));
+ if (options & SO_DONTROUTE)
+ (void) setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE,
+ (char *)&on, sizeof(on));
+#ifdef USE_RFC2292BIS
+ if (rth) {/* XXX: there is no library to finalize the header... */
+ rth->ip6r_len = rth->ip6r_segleft * 2;
+ if (setsockopt(sndsock, IPPROTO_IPV6, IPV6_RTHDR,
+ (void *)rth, (rth->ip6r_len + 1) << 3)) {
+ Fprintf(stderr, "setsockopt(IPV6_RTHDR): %s\n",
+ strerror(errno));
+ exit(1);
+ }
+ }
+#else /* old advanced API */
+ if (cmsg != NULL) {
+ inet6_rthdr_lasthop(cmsg, IPV6_RTHDR_LOOSE);
+ if (setsockopt(sndsock, IPPROTO_IPV6, IPV6_PKTOPTIONS,
+ rtbuf, cmsg->cmsg_len) < 0) {
+ Fprintf(stderr, "setsockopt(IPV6_PKTOPTIONS): %s\n",
+ strerror(errno));
+ exit(1);
+ }
+ }
+#endif /* USE_RFC2292BIS */
+#ifdef IPSEC
+#ifdef IPSEC_POLICY_IPSEC
+ /*
+ * do not raise error even if setsockopt fails, kernel may have ipsec
+ * turned off.
+ */
+ if (setpolicy(sndsock, "in bypass") < 0)
+ errx(1, "%s", ipsec_strerror());
+ if (setpolicy(sndsock, "out bypass") < 0)
+ errx(1, "%s", ipsec_strerror());
+#else
+ {
+ int level = IPSEC_LEVEL_BYPASS;
+
+ (void)setsockopt(sndsock, IPPROTO_IPV6, IPV6_ESP_TRANS_LEVEL, &level,
+ sizeof(level));
+ (void)setsockopt(sndsock, IPPROTO_IPV6, IPV6_ESP_NETWORK_LEVEL, &level,
+ sizeof(level));
+#ifdef IP_AUTH_TRANS_LEVEL
+ (void)setsockopt(sndsock, IPPROTO_IPV6, IPV6_AUTH_TRANS_LEVEL, &level,
+ sizeof(level));
+#else
+ (void)setsockopt(sndsock, IPPROTO_IPV6, IPV6_AUTH_LEVEL, &level,
+ sizeof(level));
+#endif
+#ifdef IP_AUTH_NETWORK_LEVEL
+ (void)setsockopt(sndsock, IPPROTO_IPV6, IPV6_AUTH_NETWORK_LEVEL, &level,
+ sizeof(level));
+#endif
+ }
+#endif /*IPSEC_POLICY_IPSEC*/
+#endif /*IPSEC*/
+
+ /*
+ * Source selection
+ */
+ bzero(&Src, sizeof(Src));
+ if (source) {
+ struct addrinfo hints, *res;
+ int error;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET6;
+ hints.ai_socktype = SOCK_DGRAM; /*dummy*/
+ hints.ai_flags = AI_NUMERICHOST;
+ error = getaddrinfo(source, "0", &hints, &res);
+ if (error) {
+ Printf("traceroute6: %s: %s\n", source,
+ gai_strerror(error));
+ exit(1);
+ }
+ if (res->ai_addrlen > sizeof(Src)) {
+ Printf("traceroute6: %s: %s\n", source,
+ gai_strerror(error));
+ exit(1);
+ }
+ memcpy(&Src, res->ai_addr, res->ai_addrlen);
+ freeaddrinfo(res);
+ } else {
+ struct sockaddr_in6 Nxt;
+ int dummy, len;
+
+ Nxt = Dst;
+ Nxt.sin6_port = htons(DUMMY_PORT);
+ if (cmsg != NULL)
+ bcopy(inet6_rthdr_getaddr(cmsg, 1), &Nxt.sin6_addr,
+ sizeof(Nxt.sin6_addr));
+ if ((dummy = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ perror("socket");
+ exit(1);
+ }
+ if (connect(dummy, (struct sockaddr *)&Nxt, Nxt.sin6_len) < 0) {
+ perror("connect");
+ exit(1);
+ }
+ len = sizeof(Src);
+ if (getsockname(dummy, (struct sockaddr *)&Src, &len) < 0) {
+ perror("getsockname");
+ exit(1);
+ }
+ if (getnameinfo((struct sockaddr *)&Src, Src.sin6_len,
+ src0, sizeof(src0), NULL, 0,
+ NI_NUMERICHOST | niflag)) {
+ Fprintf(stderr, "getnameinfo failed for source\n");
+ exit(1);
+ }
+ source = src0;
+ close(dummy);
+ }
+
+#if 1
+ ident = (getpid() & 0xffff) | 0x8000;
+#else
+ ident = 0; /*let the kernel pick one*/
+#endif
+ Src.sin6_port = htons(ident);
+ if (bind(sndsock, (struct sockaddr *)&Src, Src.sin6_len) < 0) {
+ perror("bind");
+ exit(1);
+ }
+
+ if (ident == 0) {
+ int len;
+
+ len = sizeof(Src);
+ if (getsockname(sndsock, (struct sockaddr *)&Src, &i) < 0) {
+ perror("getsockname");
+ exit(1);
+ }
+ ident = ntohs(Src.sin6_port);
+ }
+
+ /*
+ * Message to users
+ */
+ if (getnameinfo((struct sockaddr *)&Dst, Dst.sin6_len, hbuf,
+ sizeof(hbuf), NULL, 0, NI_NUMERICHOST | niflag))
+ strlcpy(hbuf, "(invalid)", sizeof(hbuf));
+ Fprintf(stderr, "traceroute6");
+ Fprintf(stderr, " to %s (%s)", hostname, hbuf);
+ if (source)
+ Fprintf(stderr, " from %s", source);
+ Fprintf(stderr,
+ ", %d hops max, %d byte packets\n",
+ max_hops, datalen);
+ (void) fflush(stderr);
+
+ if (first_hop > 1)
+ Printf("Skipping %d intermediate hops\n", first_hop - 1);
+
+ /*
+ * Main loop
+ */
+ for (hops = first_hop; hops <= max_hops; ++hops) {
+ struct in6_addr lastaddr;
+ int got_there = 0;
+ int unreachable = 0;
+
+ Printf("%2d ", hops);
+ bzero(&lastaddr, sizeof(lastaddr));
+ for (probe = 0; probe < nprobes; ++probe) {
+ int cc;
+ struct timeval t1, t2;
+ struct timezone tz;
+
+ (void) gettimeofday(&t1, &tz);
+ send_probe(++seq, hops);
+ while ((cc = wait_for_reply(rcvsock, &rcvmhdr))) {
+ (void) gettimeofday(&t2, &tz);
+ if ((i = packet_ok(&rcvmhdr, cc, seq))) {
+ if (! IN6_ARE_ADDR_EQUAL(&Rcv.sin6_addr,
+ &lastaddr)) {
+ print(&rcvmhdr, cc);
+ lastaddr = Rcv.sin6_addr;
+ }
+ Printf(" %g ms", deltaT(&t1, &t2));
+ switch(i - 1) {
+ case ICMP6_DST_UNREACH_NOROUTE:
+ ++unreachable;
+ Printf(" !N");
+ break;
+ case ICMP6_DST_UNREACH_ADMIN:
+ ++unreachable;
+ Printf(" !P");
+ break;
+ case ICMP6_DST_UNREACH_NOTNEIGHBOR:
+ ++unreachable;
+ Printf(" !S");
+ break;
+ case ICMP6_DST_UNREACH_ADDR:
+ ++unreachable;
+ Printf(" !A");
+ break;
+ case ICMP6_DST_UNREACH_NOPORT:
+ if (rcvhlim >= 0 &&
+ rcvhlim <= 1)
+ Printf(" !");
+ ++got_there;
+ break;
+ }
+ break;
+ }
+ }
+ if (cc == 0)
+ Printf(" *");
+ (void) fflush(stdout);
+ }
+ putchar('\n');
+ if (got_there ||
+ (unreachable > 0 && unreachable >= ((nprobes + 1) / 2))) {
+ exit(0);
+ }
+ }
+
+ exit(0);
+}
+
+int
+wait_for_reply(sock, mhdr)
+ int sock;
+ struct msghdr *mhdr;
+{
+#ifdef HAVE_POLL
+ struct pollfd pfd[1];
+ int cc = 0;
+
+ pfd[0].fd = sock;
+ pfd[0].events = POLLIN;
+ pfd[0].revents = 0;
+
+ if (poll(pfd, 1, waittime * 1000) > 0)
+ cc = recvmsg(rcvsock, mhdr, 0);
+
+ return(cc);
+#else
+ fd_set *fdsp;
+ struct timeval wait;
+ int cc = 0, fdsn;
+
+ fdsn = howmany(sock + 1, NFDBITS) * sizeof(fd_mask);
+ if ((fdsp = (fd_set *)malloc(fdsn)) == NULL)
+ err(1, "malloc");
+ memset(fdsp, 0, fdsn);
+ FD_SET(sock, fdsp);
+ wait.tv_sec = waittime; wait.tv_usec = 0;
+
+ if (select(sock+1, fdsp, (fd_set *)0, (fd_set *)0, &wait) > 0)
+ cc = recvmsg(rcvsock, mhdr, 0);
+
+ free(fdsp);
+ return(cc);
+#endif
+}
+
+#ifdef IPSEC
+#ifdef IPSEC_POLICY_IPSEC
+int
+setpolicy(so, policy)
+ int so;
+ char *policy;
+{
+ char *buf;
+
+ buf = ipsec_set_policy(policy, strlen(policy));
+ if (buf == NULL) {
+ warnx("%s", ipsec_strerror());
+ return -1;
+ }
+ (void)setsockopt(so, IPPROTO_IPV6, IPV6_IPSEC_POLICY,
+ buf, ipsec_get_policylen(buf));
+
+ free(buf);
+
+ return 0;
+}
+#endif
+#endif
+
+void
+send_probe(seq, hops)
+ int seq, hops;
+{
+ struct opacket *op = outpacket;
+ int i;
+
+ if(setsockopt(sndsock, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
+ (char *)&hops, sizeof(hops)) < 0) {
+ perror("setsockopt IPV6_UNICAST_HOPS");
+ }
+
+ Dst.sin6_port = htons(port + seq);
+
+ op->seq = seq;
+ op->hops = hops;
+ (void) gettimeofday(&op->tv, &tz);
+
+ i = sendto(sndsock, (char *)outpacket, datalen , 0,
+ (struct sockaddr *)&Dst, Dst.sin6_len);
+ if (i < 0 || i != datalen) {
+ if (i<0)
+ perror("sendto");
+ Printf("traceroute6: wrote %s %d chars, ret=%d\n", hostname,
+ datalen, i);
+ (void) fflush(stdout);
+ }
+}
+
+int
+get_hoplim(mhdr)
+ struct msghdr *mhdr;
+{
+ struct cmsghdr *cm;
+
+ for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm;
+ cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) {
+ if (cm->cmsg_level == IPPROTO_IPV6 &&
+ cm->cmsg_type == IPV6_HOPLIMIT &&
+ cm->cmsg_len == CMSG_LEN(sizeof(int)))
+ return(*(int *)CMSG_DATA(cm));
+ }
+
+ return(-1);
+}
+
+double
+deltaT(t1p, t2p)
+ struct timeval *t1p, *t2p;
+{
+ register double dt;
+
+ dt = (double)(t2p->tv_sec - t1p->tv_sec) * 1000.0 +
+ (double)(t2p->tv_usec - t1p->tv_usec) / 1000.0;
+ return (dt);
+}
+
+
+/*
+ * Convert an ICMP "type" field to a printable string.
+ */
+char *
+pr_type(t0)
+ int t0;
+{
+ u_char t = t0 & 0xff;
+ char *cp;
+
+ switch (t) {
+ case ICMP6_DST_UNREACH:
+ cp = "Destination Unreachable";
+ break;
+ case ICMP6_PACKET_TOO_BIG:
+ cp = "Pakcet Too Big";
+ break;
+ case ICMP6_TIME_EXCEEDED:
+ cp = "Time Exceeded";
+ break;
+ case ICMP6_PARAM_PROB:
+ cp = "Parameter Problem";
+ break;
+ case ICMP6_ECHO_REQUEST:
+ cp = "Echo Request";
+ break;
+ case ICMP6_ECHO_REPLY:
+ cp = "Echo Reply";
+ break;
+ case ICMP6_MEMBERSHIP_QUERY:
+ cp = "Group Membership Query";
+ break;
+ case ICMP6_MEMBERSHIP_REPORT:
+ cp = "Group Membership Report";
+ break;
+ case ICMP6_MEMBERSHIP_REDUCTION:
+ cp = "Group Membership Reduction";
+ break;
+ case ND_ROUTER_SOLICIT:
+ cp = "Router Solicitation";
+ break;
+ case ND_ROUTER_ADVERT:
+ cp = "Router Advertisement";
+ break;
+ case ND_NEIGHBOR_SOLICIT:
+ cp = "Neighbor Solicitation";
+ break;
+ case ND_NEIGHBOR_ADVERT:
+ cp = "Neighbor Advertisement";
+ break;
+ case ND_REDIRECT:
+ cp = "Redirect";
+ break;
+ default:
+ cp = "Unknown";
+ break;
+ }
+ return cp;
+}
+
+
+int
+packet_ok(mhdr, cc, seq)
+ struct msghdr *mhdr;
+ int cc;
+ int seq;
+{
+ register struct icmp6_hdr *icp;
+ struct sockaddr_in6 *from = (struct sockaddr_in6 *)mhdr->msg_name;
+ u_char type, code;
+ char *buf = (char *)mhdr->msg_iov[0].iov_base;
+ struct cmsghdr *cm;
+ int *hlimp;
+ char hbuf[NI_MAXHOST];
+
+#ifdef OLDRAWSOCKET
+ int hlen;
+ struct ip6_hdr *ip;
+#endif
+
+#ifdef OLDRAWSOCKET
+ ip = (struct ip6_hdr *) buf;
+ hlen = sizeof(struct ip6_hdr);
+ if (cc < hlen + sizeof(struct icmp6_hdr)) {
+ if (verbose) {
+ if (getnameinfo((struct sockaddr *)from, from->sin6_len,
+ hbuf, sizeof(hbuf), NULL, 0,
+ NI_NUMERICHOST | niflag) != 0)
+ strlcpy(hbuf, "invalid", sizeof(hbuf));
+ Printf("packet too short (%d bytes) from %s\n", cc,
+ hbuf);
+ }
+ return (0);
+ }
+ cc -= hlen;
+ icp = (struct icmp6_hdr *)(buf + hlen);
+#else
+ if (cc < sizeof(struct icmp6_hdr)) {
+ if (verbose) {
+ if (getnameinfo((struct sockaddr *)from, from->sin6_len,
+ hbuf, sizeof(hbuf), NULL, 0,
+ NI_NUMERICHOST | niflag) != 0)
+ strlcpy(hbuf, "invalid", sizeof(hbuf));
+ Printf("data too short (%d bytes) from %s\n", cc, hbuf);
+ }
+ return(0);
+ }
+ icp = (struct icmp6_hdr *)buf;
+#endif
+ /* get optional information via advanced API */
+ rcvpktinfo = NULL;
+ hlimp = NULL;
+ for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm;
+ cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) {
+ if (cm->cmsg_level == IPPROTO_IPV6 &&
+ cm->cmsg_type == IPV6_PKTINFO &&
+ cm->cmsg_len ==
+ CMSG_LEN(sizeof(struct in6_pktinfo)))
+ rcvpktinfo = (struct in6_pktinfo *)(CMSG_DATA(cm));
+
+ if (cm->cmsg_level == IPPROTO_IPV6 &&
+ cm->cmsg_type == IPV6_HOPLIMIT &&
+ cm->cmsg_len == CMSG_LEN(sizeof(int)))
+ hlimp = (int *)CMSG_DATA(cm);
+ }
+ if (rcvpktinfo == NULL || hlimp == NULL) {
+ warnx("failed to get received hop limit or packet info");
+#if 0
+ return(0);
+#else
+ rcvhlim = 0; /*XXX*/
+#endif
+ }
+ else
+ rcvhlim = *hlimp;
+
+ type = icp->icmp6_type;
+ code = icp->icmp6_code;
+ if ((type == ICMP6_TIME_EXCEEDED && code == ICMP6_TIME_EXCEED_TRANSIT)
+ || type == ICMP6_DST_UNREACH) {
+ struct ip6_hdr *hip;
+ struct udphdr *up;
+
+ hip = (struct ip6_hdr *)(icp + 1);
+ if ((up = get_udphdr(hip, (u_char *)(buf + cc))) == NULL) {
+ if (verbose)
+ warnx("failed to get upper layer header");
+ return(0);
+ }
+ if (up->uh_sport == htons(ident) &&
+ up->uh_dport == htons(port+seq))
+ return (type == ICMP6_TIME_EXCEEDED ? -1 : code + 1);
+ }
+ if (verbose) {
+ int i;
+ u_int8_t *p;
+ char sbuf[NI_MAXHOST+1], dbuf[INET6_ADDRSTRLEN];
+
+ if (getnameinfo((struct sockaddr *)from, from->sin6_len,
+ sbuf, sizeof(sbuf), NULL, 0, NI_NUMERICHOST | niflag) != 0)
+ strlcpy(sbuf, "invalid", sizeof(hbuf));
+ Printf("\n%d bytes from %s to %s", cc, sbuf,
+ rcvpktinfo ? inet_ntop(AF_INET6, &rcvpktinfo->ipi6_addr,
+ dbuf, sizeof(dbuf))
+ : "?");
+ Printf(": icmp type %d (%s) code %d\n", type, pr_type(type),
+ icp->icmp6_code);
+ p = (u_int8_t *)(icp + 1);
+#define WIDTH 16
+ for (i = 0; i < cc; i++) {
+ if (i % WIDTH == 0)
+ Printf("%04x:", i);
+ if (i % 4 == 0)
+ Printf(" ");
+ Printf("%02x", p[i]);
+ if (i % WIDTH == WIDTH - 1)
+ Printf("\n");
+ }
+ if (cc % WIDTH != 0)
+ Printf("\n");
+ }
+ return(0);
+}
+
+/*
+ * Increment pointer until find the UDP header.
+ */
+struct udphdr *
+get_udphdr(ip6, lim)
+ struct ip6_hdr *ip6;
+ u_char *lim;
+{
+ u_char *cp = (u_char *)ip6, nh;
+ int hlen;
+
+ if (cp + sizeof(*ip6) >= lim)
+ return(NULL);
+
+ nh = ip6->ip6_nxt;
+ cp += sizeof(struct ip6_hdr);
+
+ while(lim - cp >= 8) {
+ switch(nh) {
+ case IPPROTO_ESP:
+ case IPPROTO_TCP:
+ case IPPROTO_ICMPV6:
+ return(NULL);
+ case IPPROTO_UDP:
+ return((struct udphdr *)cp);
+ case IPPROTO_FRAGMENT:
+ hlen = sizeof(struct ip6_frag);
+ nh = ((struct ip6_frag *)cp)->ip6f_nxt;
+ break;
+ case IPPROTO_AH:
+ hlen = (((struct ip6_ext *)cp)->ip6e_len + 2) << 2;
+ nh = ((struct ip6_ext *)cp)->ip6e_nxt;
+ break;
+ default:
+ hlen = (((struct ip6_ext *)cp)->ip6e_len + 1) << 3;
+ nh = ((struct ip6_ext *)cp)->ip6e_nxt;
+ break;
+ }
+
+ cp += hlen;
+ }
+
+ return(NULL);
+}
+
+void
+print(mhdr, cc)
+ struct msghdr *mhdr;
+ int cc;
+{
+ struct sockaddr_in6 *from = (struct sockaddr_in6 *)mhdr->msg_name;
+ char hbuf[NI_MAXHOST];
+
+ if (getnameinfo((struct sockaddr *)from, from->sin6_len,
+ hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST | niflag) != 0)
+ strlcpy(hbuf, "invalid", sizeof(hbuf));
+ if (nflag)
+ Printf(" %s", hbuf);
+ else if (lflag)
+ Printf(" %s (%s)", inetname((struct sockaddr *)from), hbuf);
+ else
+ Printf(" %s", inetname((struct sockaddr *)from));
+
+ if (verbose) {
+#ifdef OLDRAWSOCKET
+ Printf(" %d bytes to %s", cc,
+ rcvpktinfo ? inet_ntop(AF_INET6, &rcvpktinfo->ipi6_addr,
+ hbuf, sizeof(hbuf))
+ : "?");
+#else
+ Printf(" %d bytes of data to %s", cc,
+ rcvpktinfo ? inet_ntop(AF_INET6, &rcvpktinfo->ipi6_addr,
+ hbuf, sizeof(hbuf))
+ : "?");
+#endif
+ }
+}
+
+/*
+ * Subtract 2 timeval structs: out = out - in.
+ * Out is assumed to be >= in.
+ */
+void
+tvsub(out, in)
+ register struct timeval *out, *in;
+{
+ if ((out->tv_usec -= in->tv_usec) < 0) {
+ out->tv_sec--;
+ out->tv_usec += 1000000;
+ }
+ out->tv_sec -= in->tv_sec;
+}
+
+
+/*
+ * Construct an Internet address representation.
+ * If the nflag has been supplied, give
+ * numeric value, otherwise try for symbolic name.
+ */
+const char *
+inetname(sa)
+ struct sockaddr *sa;
+{
+ register char *cp;
+ static char line[NI_MAXHOST];
+ static char domain[MAXHOSTNAMELEN + 1];
+ static int first = 1;
+
+ if (first && !nflag) {
+ first = 0;
+ if (gethostname(domain, MAXHOSTNAMELEN) == 0 &&
+ (cp = index(domain, '.')))
+ (void) strlcpy(domain, cp + 1, sizeof(domain));
+ else
+ domain[0] = 0;
+ }
+ cp = NULL;
+ if (!nflag) {
+ if (getnameinfo(sa, sa->sa_len, line, sizeof(line), NULL, 0,
+ NI_NAMEREQD) == 0) {
+ if ((cp = index(line, '.')) &&
+ !strcmp(cp + 1, domain))
+ *cp = 0;
+ cp = line;
+ }
+ }
+ if (cp)
+ return cp;
+
+ if (getnameinfo(sa, sa->sa_len, line, sizeof(line), NULL, 0,
+ NI_NUMERICHOST | niflag) != 0)
+ strlcpy(line, "invalid", sizeof(line));
+ return line;
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr,
+"usage: traceroute6 [-dlnrv] [-f firsthop] [-g gateway] [-m hoplimit] [-p port]\n"
+" [-q probes] [-s src] [-w waittime] target [datalen]\n");
+ exit(1);
+}
diff --git a/usr.sbin/trpt/Makefile b/usr.sbin/trpt/Makefile
new file mode 100644
index 0000000..dff5bc8
--- /dev/null
+++ b/usr.sbin/trpt/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+PROG= trpt
+MAN= trpt.8
+BINGRP= kmem
+BINMODE= 2555
+
+CFLAGS+= -DINET6
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/trpt/trpt.8 b/usr.sbin/trpt/trpt.8
new file mode 100644
index 0000000..1cbd6ae
--- /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
+.\" $FreeBSD$
+.\"
+.Dd December 11, 1993
+.Dt TRPT 8
+.Os
+.Sh NAME
+.Nm trpt
+.Nd transliterate protocol trace
+.Sh SYNOPSIS
+.Nm
+.Op Fl a
+.Op Fl f
+.Op Fl j
+.Op Fl p Ar hex-address
+.Op Fl s
+.Op Fl t
+.Oo
+.Ar system Op Ar core
+.Oc
+.Sh DESCRIPTION
+.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 .
+.Pp
+The following options may be used to
+alter this behavior:
+.Bl -tag -width indent
+.It Fl a
+In addition to the normal output,
+print the values of the source and destination
+addresses for each packet recorded.
+.It Fl f
+Follow the trace as it occurs, waiting a short time for additional records
+each time the end of the log is reached.
+.It Fl j
+Just give a list of the protocol control block
+addresses for which there are trace records.
+.It Fl p
+Show only trace records associated with the protocol
+control block at the given address
+.Ar hex-address .
+.It Fl s
+In addition to the normal output,
+print a detailed description of the packet
+sequencing information.
+.It Fl t
+In addition to the normal output,
+print the values for all timers at each
+point in the trace.
+.El
+.Pp
+The recommended use of
+.Nm
+is as follows.
+Isolate the problem and enable debugging on the
+socket(s) involved in the connection.
+Find the address of the protocol control blocks
+associated with the sockets using the
+.Fl A
+option to
+.Xr netstat 1 .
+Then run
+.Nm
+with the
+.Fl p
+option, supplying the associated
+protocol control block addresses.
+The
+.Fl f
+option can be used to follow the trace log once the trace is located.
+If there are
+many sockets using the debugging option, the
+.Fl j
+option may be useful in checking to see if
+any trace records are present for the socket in
+question.
+.Pp
+If debugging is being performed on a system or
+core file other than the default, the last two
+arguments may be used to supplant the defaults.
+.Sh FILES
+.Bl -tag -width /boot/kernel/kernel -compact
+.It Pa /boot/kernel/kernel
+.It Pa /dev/kmem
+.El
+.Sh SEE ALSO
+.Xr netstat 1 ,
+.Xr setsockopt 2
+.Sh DIAGNOSTICS
+.Bl -diag
+.It no namelist
+When the system image doesn't
+contain the proper symbols to find the trace buffer;
+others which should be self explanatory.
+.El
+.Sh BUGS
+Should also print the data for each input or output,
+but this is not saved in the trace record.
+.Pp
+The output format is inscrutable and should be described
+here.
+.Sh HISTORY
+The
+.Nm
+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..656b05c
--- /dev/null
+++ b/usr.sbin/trpt/trpt.c
@@ -0,0 +1,477 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#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>
+#ifdef INET6
+#include <netinet/ip6.h>
+#endif
+#include <netinet/ip_var.h>
+#include <netinet/tcp.h>
+#define TCPSTATES
+#include <netinet/tcp_fsm.h>
+#include <netinet/tcp_seq.h>
+#define TCPTIMERS
+#include <netinet/tcp_timer.h>
+#include <netinet/tcp_var.h>
+#include <netinet/tcpip.h>
+#define TANAMES
+#include <netinet/tcp_debug.h>
+
+#include <arpa/inet.h>
+
+#include <err.h>
+#include <nlist.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <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((const void *, const void *));
+void tcp_trace __P((short, short, struct tcpcb *, struct tcpcb *,
+ int, void *, struct tcphdr *, 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);
+ setgid(getgid());
+ if (kflag)
+ errx(1, "can't do core files yet");
+ (void)klseek(memf, (off_t)nl[N_TCP_DEBX].n_value, L_SET);
+ if (read(memf, (char *)&tcp_debx, sizeof(tcp_debx)) !=
+ sizeof(tcp_debx))
+ err(3, "tcp_debx");
+ (void)klseek(memf, (off_t)nl[N_TCP_DEBUG].n_value, L_SET);
+ if (read(memf, (char *)tcp_debug, sizeof(tcp_debug)) !=
+ sizeof(tcp_debug))
+ err(3, "tcp_debug");
+ /*
+ * If no control blocks have been specified, figure
+ * out how many distinct one we have and summarize
+ * them in tcp_pcbs for sorting the trace records
+ * below.
+ */
+ if (!npcbs) {
+ for (i = 0; i < TCP_NDEBUG; i++) {
+ register struct tcp_debug *td = &tcp_debug[i];
+ register int j;
+
+ if (td->td_tcb == 0)
+ continue;
+ for (j = 0; j < npcbs; j++)
+ if (tcp_pcbs[j] == td->td_tcb)
+ break;
+ if (j >= npcbs)
+ tcp_pcbs[npcbs++] = td->td_tcb;
+ }
+ if (!npcbs)
+ exit(0);
+ }
+ qsort(tcp_pcbs, npcbs, sizeof(caddr_t), numeric);
+ if (jflag) {
+ for (i = 0;;) {
+ printf("%x", 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, family;
+
+again: if (--tcp_debx < 0)
+ tcp_debx = TCP_NDEBUG - 1;
+ for (i = prev_debx % TCP_NDEBUG; i < TCP_NDEBUG; i++) {
+ td = &tcp_debug[i];
+ if (tcpcb && td->td_tcb != tcpcb)
+ continue;
+ ntime = ntohl(td->td_time);
+#ifdef INET6
+ family = td->td_family;
+#else
+ family = AF_INET;
+#endif
+ switch(family) {
+ case AF_INET:
+ tcp_trace(td->td_act, td->td_ostate,
+ (struct tcpcb *)td->td_tcb,
+ &td->td_cb, td->td_family, &td->td_ti.ti_i,
+ &td->td_ti.ti_t, td->td_req);
+ break;
+#ifdef INET6
+ case AF_INET6:
+ tcp_trace(td->td_act, td->td_ostate,
+ (struct tcpcb *)td->td_tcb,
+ &td->td_cb, td->td_family, &td->td_ti6.ip6,
+ &td->td_ti6.th, td->td_req);
+ break;
+#endif
+ }
+ if (i == tcp_debx)
+ goto done;
+ }
+ for (i = 0; i <= tcp_debx % TCP_NDEBUG; i++) {
+ td = &tcp_debug[i];
+ if (tcpcb && td->td_tcb != tcpcb)
+ continue;
+ ntime = ntohl(td->td_time);
+#ifdef INET6
+ family = td->td_family;
+#else
+ family = AF_INET;
+#endif
+ switch(family) {
+ case AF_INET:
+ tcp_trace(td->td_act, td->td_ostate,
+ (struct tcpcb *)td->td_tcb,
+ &td->td_cb, td->td_family, &td->td_ti.ti_i,
+ &td->td_ti.ti_t, td->td_req);
+ break;
+#ifdef INET6
+ case AF_INET6:
+ tcp_trace(td->td_act, td->td_ostate,
+ (struct tcpcb *)td->td_tcb,
+ &td->td_cb, td->td_family, &td->td_ti6.ip6,
+ &td->td_ti6.th, td->td_req);
+ break;
+#endif
+ }
+ }
+done: if (follow) {
+ prev_debx = tcp_debx + 1;
+ if (prev_debx >= TCP_NDEBUG)
+ prev_debx = 0;
+ do {
+ sleep(1);
+ (void)klseek(memf, (off_t)nl[N_TCP_DEBX].n_value, L_SET);
+ if (read(memf, (char *)&tcp_debx, sizeof(tcp_debx)) !=
+ sizeof(tcp_debx))
+ err(3, "tcp_debx");
+ } while (tcp_debx == prev_debx);
+ (void)klseek(memf, (off_t)nl[N_TCP_DEBUG].n_value, L_SET);
+ if (read(memf, (char *)tcp_debug, sizeof(tcp_debug)) !=
+ sizeof(tcp_debug))
+ err(3, "tcp_debug");
+ goto again;
+ }
+}
+
+/*
+ * Tcp debug routines
+ */
+/*ARGSUSED*/
+void
+tcp_trace(act, ostate, atp, tp, family, ip, th, req)
+ short act, ostate;
+ struct tcpcb *atp, *tp;
+ int family;
+ void *ip;
+ struct tcphdr *th;
+ int req;
+{
+ tcp_seq seq, ack;
+ int flags, len, win, timer;
+ struct ip *ip4;
+#ifdef INET6
+ int isipv6, nopkt = 1;
+ struct ip6_hdr *ip6;
+ char ntop_buf[INET6_ADDRSTRLEN];
+#endif
+
+#ifdef INET6
+ switch (family) {
+ case AF_INET:
+ nopkt = 0;
+ isipv6 = 0;
+ ip4 = (struct ip *)ip;
+ break;
+ case AF_INET6:
+ nopkt = 0;
+ isipv6 = 1;
+ ip6 = (struct ip6_hdr *)ip;
+ case 0:
+ default:
+ break;
+ }
+#else
+ ip4 = (struct ip *)ip;
+#endif
+ printf("%03ld %s:%s ",(ntime/10) % 1000, tcpstates[ostate],
+ tanames[act]);
+ switch (act) {
+ case TA_INPUT:
+ case TA_OUTPUT:
+ case TA_DROP:
+#ifdef INET6
+ if (nopkt != 0)
+ break;
+#endif
+ if (aflag) {
+ printf("(src=%s,%u, ",
+
+#ifdef INET6
+ isipv6
+ ? inet_ntop(AF_INET6, &ip6->ip6_src, ntop_buf,
+ sizeof(ntop_buf)) :
+#endif
+ inet_ntoa(ip4->ip_src),
+ ntohs(th->th_sport));
+ printf("dst=%s,%u)",
+#ifdef INET6
+ isipv6
+ ? inet_ntop(AF_INET6, &ip6->ip6_dst, ntop_buf,
+ sizeof(ntop_buf)) :
+#endif
+ inet_ntoa(ip4->ip_dst),
+ ntohs(th->th_dport));
+ }
+ seq = th->th_seq;
+ ack = th->th_ack;
+
+ len =
+#ifdef INET6
+ isipv6 ? ip6->ip6_plen :
+#endif
+ ip4->ip_len;
+ win = th->th_win;
+ if (act == TA_OUTPUT) {
+ seq = ntohl(seq);
+ ack = ntohl(ack);
+ len = ntohs(len);
+ win = ntohs(win);
+ }
+ if (act == TA_OUTPUT)
+ len -= sizeof(struct tcphdr);
+ if (len)
+ printf("[%lx..%lx)", seq, seq + len);
+ else
+ printf("%lx", seq);
+ printf("@%lx", ack);
+ if (win)
+ printf("(win=%x)", win);
+ flags = th->th_flags;
+ if (flags) {
+ register char *cp = "<";
+#define pf(flag, string) { \
+ if (th->th_flags&flag) { \
+ (void)printf("%s%s", cp, string); \
+ cp = ","; \
+ } \
+}
+ pf(TH_SYN, "SYN");
+ pf(TH_ACK, "ACK");
+ pf(TH_FIN, "FIN");
+ pf(TH_RST, "RST");
+ pf(TH_PUSH, "PUSH");
+ pf(TH_URG, "URG");
+ printf(">");
+ }
+ break;
+ case TA_USER:
+ timer = req >> 8;
+ req &= 0xff;
+ printf("%s", prurequests[req]);
+ if (req == PRU_SLOWTIMO || req == PRU_FASTTIMO)
+ printf("<%s>", tcptimers[timer]);
+ break;
+ }
+ printf(" -> %s", tcpstates[tp->t_state]);
+ /* print out internal state of tp !?! */
+ printf("\n");
+ if (sflag) {
+ printf("\trcv_nxt %lx rcv_wnd %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 0
+ /*
+ * XXX
+ * kernel now uses callouts, not integer time values.
+ */
+ if (tflag) {
+ register char *cp = "\t";
+ register int i;
+
+ for (i = 0; i < TCPT_NTIMERS; i++) {
+ if (tp->t_timer[i] == 0)
+ continue;
+ printf("%s%s=%d", cp, tcptimers[i], tp->t_timer[i]);
+ if (i == TCPT_REXMT)
+ printf(" (t_rxtshft=%d)", tp->t_rxtshift);
+ cp = ", ";
+ }
+ if (*cp != '\t')
+ putchar('\n');
+ }
+#endif
+}
+
+int
+numeric(v1, v2)
+ const void *v1, *v2;
+{
+ const caddr_t *c1 = v1, *c2 = v2;
+ return(*c1 - *c2);
+}
+
+void
+klseek(fd, base, off)
+ int fd, off;
+ off_t base;
+{
+ (void)lseek(fd, base, off);
+}
diff --git a/usr.sbin/tzsetup/Makefile b/usr.sbin/tzsetup/Makefile
new file mode 100644
index 0000000..7b557cf
--- /dev/null
+++ b/usr.sbin/tzsetup/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+PROG= tzsetup
+MAN= tzsetup.8
+
+CFLAGS+= -I${.CURDIR}
+
+LDADD= -ldialog -lncurses
+DPADD= ${LIBDIALOG} ${LIBNCURSES}
+
+.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..6d36891
--- /dev/null
+++ b/usr.sbin/tzsetup/tzsetup.8
@@ -0,0 +1,129 @@
+.\" Copyright (c) 1996 Wolfram Schneider <wosch@FreeBSD.org>. Berlin.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.Dd January 24, 1996
+.Dt TZSETUP 8
+.Os
+.Sh NAME
+.Nm tzsetup
+.Nd set local timezone
+.Sh SYNOPSIS
+.Nm
+.Op Fl n
+.Op Ar default
+.Sh DESCRIPTION
+The
+.Nm
+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
+(in
+.Tn ISO
+6709 format),
+and location within the territory.
+.Pp
+The maintainers of the database maintain the following policies:
+.Bl -enum -offset indent
+.It
+At least one zone for every country or inhabited geographical territory.
+.It
+One zone for every distinct, documented timezone history since the
+beginning of the
+.Ux
+epoch (January 1, 1970,
+.Tn GMT ) .
+.It
+Each zone is named for the most populous city therein. (Where possible,
+the database includes pre-1970 history for its city.)
+.El
+The source code to the database
+.Pq Pa /usr/src/share/zoneinfo/[a-z]*
+contains many additional comments and documentation references for the
+historically minded.
+.Sh BUGS
+Programs which are already running when
+.Nm
+creates or updates
+.Pa /etc/localtime
+will not reflect the updated timezone.
+When the system is first configured for a
+.Pf non- Tn UTC
+hardware clock, it is necessary to run
+.Xr adjkerntz 8
+(which normally happens as a part of system startup) in order to update
+the kernel's idea of the correct timezone offset.
+.Sh FILES
+.Bl -tag -width /usr/share/zoneinfo/zone.tab -compact
+.It Pa /etc/localtime
+current time zone file
+.It Pa /etc/wall_cmos_clock
+see
+.Xr adjkerntz 8 .
+.It Pa /usr/share/misc/iso3166
+mapping of
+.Tn ISO
+3166 territory codes to names
+.It Pa /usr/share/zoneinfo
+directory for zoneinfo files
+.It Pa /usr/share/zoneinfo/zone.tab
+mapping of timezone file to country and location
+.El
+.Sh SEE ALSO
+.Xr date 1 ,
+.Xr adjtime 2 ,
+.Xr ctime 3 ,
+.Xr timezone 3 ,
+.Xr tzfile 5 ,
+.Xr adjkerntz 8 ,
+.Xr zdump 8 ,
+.Xr zic 8
+.Sh DISCLAIMER
+The representation of certain localities as being associated with certain
+countries and/or territories is for the purposes of identification only,
+and does not imply any endorsement or rejection on the part of the
+.Fx
+Project of the territorial claims of any entity.
diff --git a/usr.sbin/tzsetup/tzsetup.c b/usr.sbin/tzsetup/tzsetup.c
new file mode 100644
index 0000000..bb17cb6
--- /dev/null
+++ b/usr.sbin/tzsetup/tzsetup.c
@@ -0,0 +1,710 @@
+/*
+ * Copyright 1996 Massachusetts Institute of Technology
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that both the above copyright notice and this
+ * permission notice appear in all copies, that both the above
+ * copyright notice and this permission notice appear in all
+ * supporting documentation, and that the name of M.I.T. not be used
+ * in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission. M.I.T. makes
+ * no representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
+ * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Second attempt at a `tzmenu' program, using the separate description
+ * files provided in newer tzdata releases.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <dialog.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <sys/fcntl.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+
+#include "paths.h"
+
+static int reallydoit = 1;
+
+static int continent_country_menu(dialogMenuItem *);
+static int set_zone_multi(dialogMenuItem *);
+static int set_zone_whole_country(dialogMenuItem *);
+static int set_zone_menu(dialogMenuItem *);
+
+struct continent {
+ dialogMenuItem *menu;
+ int nitems;
+ int ch;
+ int sc;
+};
+
+static struct continent africa, america, antarctica, arctic, asia, atlantic;
+static struct continent australia, europe, indian, pacific;
+
+static struct continent_names {
+ char *name;
+ struct continent *continent;
+} continent_names[] = {
+ { "Africa", &africa }, { "America", &america },
+ { "Antarctica", &antarctica }, { "Arctic", &arctic },
+ { "Asia", &asia },
+ { "Atlantic", &atlantic }, { "Australia", &australia },
+ { "Europe", &europe }, { "Indian", &indian }, { "Pacific", &pacific }
+};
+
+static dialogMenuItem continents[] = {
+ { "1", "Africa", 0, continent_country_menu, 0, &africa },
+ { "2", "America -- North and South", 0, continent_country_menu, 0,
+ &america },
+ { "3", "Antarctica", 0, continent_country_menu, 0, &antarctica },
+ { "4", "Arctic Ocean", 0, continent_country_menu, 0, &arctic },
+ { "5", "Asia", 0, continent_country_menu, 0, &asia },
+ { "6", "Atlantic Ocean", 0, continent_country_menu, 0, &atlantic },
+ { "7", "Australia", 0, continent_country_menu, 0, &australia },
+ { "8", "Europe", 0, continent_country_menu, 0, &europe },
+ { "9", "Indian Ocean", 0, continent_country_menu, 0, &indian },
+ { "0", "Pacific Ocean", 0, continent_country_menu, 0, &pacific }
+};
+#define NCONTINENTS (int)((sizeof continents)/(sizeof continents[0]))
+#define OCEANP(x) ((x) == 3 || (x) == 5 || (x) == 8 || (x) == 9)
+
+static int
+continent_country_menu(dialogMenuItem *continent)
+{
+ int rv;
+ struct continent *contp = continent->data;
+ char title[256];
+ int isocean = OCEANP(continent - continents);
+ int menulen;
+
+ /* Short cut -- if there's only one country, don't post a menu. */
+ if (contp->nitems == 1) {
+ return set_zone_menu(&contp->menu[0]);
+ }
+
+ /* It's amazing how much good grammar really matters... */
+ if (!isocean)
+ snprintf(title, sizeof title, "Countries in %s",
+ continent->title);
+ else
+ snprintf(title, sizeof title, "Islands and groups in the %s",
+ continent->title);
+
+ menulen = contp->nitems < 16 ? contp->nitems : 16;
+ rv = dialog_menu(title, (isocean ? "Select an island or group"
+ : "Select a country"), -1, -1, menulen,
+ -contp->nitems, contp->menu, 0, &contp->ch,
+ &contp->sc);
+ if (rv == 0)
+ return DITEM_LEAVE_MENU;
+ return DITEM_RECREATE;
+}
+
+static struct continent *
+find_continent(const char *name)
+{
+ int i;
+
+ for (i = 0; i < NCONTINENTS; i++) {
+ if (strcmp(name, continent_names[i].name) == 0)
+ return continent_names[i].continent;
+ }
+ return 0;
+}
+
+struct country {
+ char *name;
+ char *tlc;
+ int nzones;
+ char *filename; /* use iff nzones < 0 */
+ struct continent *continent; /* use iff nzones < 0 */
+ TAILQ_HEAD(, zone) zones; /* use iff nzones > 0 */
+ dialogMenuItem *submenu; /* use iff nzones > 0 */
+};
+
+struct zone {
+ TAILQ_ENTRY(zone) link;
+ char *descr;
+ char *filename;
+ struct continent *continent;
+};
+
+/*
+ * This is the easiest organization... we use ISO 3166 country codes,
+ * of the two-letter variety, so we just size this array to suit.
+ * Beats worrying about dynamic allocation.
+ */
+#define NCOUNTRIES (26*26)
+static struct country countries[NCOUNTRIES];
+#define CODE2INT(s) ((s[0] - 'A') * 26 + (s[1] - 'A'))
+
+/*
+ * Read the ISO 3166 country code database in _PATH_ISO3166
+ * (/usr/share/misc/iso3166). On error, exit via err(3).
+ */
+static void
+read_iso3166_table(void)
+{
+ FILE *fp;
+ char *s, *t, *name;
+ size_t len;
+ int lineno;
+ struct country *cp;
+
+ fp = fopen(_PATH_ISO3166, "r");
+ if (!fp)
+ err(1, _PATH_ISO3166);
+ lineno = 0;
+
+ while ((s = fgetln(fp, &len)) != 0) {
+ lineno++;
+ if (s[len - 1] != '\n')
+ errx(1, _PATH_ISO3166 ":%d: invalid format", lineno);
+ s[len - 1] = '\0';
+ if (s[0] == '#' || strspn(s, " \t") == len - 1)
+ continue;
+
+ /* Isolate the two-letter code. */
+ t = strsep(&s, "\t");
+ if (t == 0 || strlen(t) != 2)
+ errx(1, _PATH_ISO3166 ":%d: invalid format", lineno);
+ if (t[0] < 'A' || t[0] > 'Z' || t[1] < 'A' || t[1] > 'Z')
+ errx(1, _PATH_ISO3166 ":%d: invalid code `%s'",
+ lineno, t);
+
+ /* Now skip past the three-letter and numeric codes. */
+ name = strsep(&s, "\t"); /* 3-let */
+ if (name == 0 || strlen(name) != 3)
+ errx(1, _PATH_ISO3166 ":%d: invalid format", lineno);
+ name = strsep(&s, "\t"); /* numeric */
+ if (name == 0 || strlen(name) != 3)
+ errx(1, _PATH_ISO3166 ":%d: invalid format", lineno);
+
+ name = s;
+
+ cp = &countries[CODE2INT(t)];
+ if (cp->name)
+ errx(1, _PATH_ISO3166
+ ":%d: country code `%s' multiply defined: %s",
+ lineno, t, cp->name);
+ cp->name = strdup(name);
+ if (cp->name == NULL)
+ errx(1, "malloc failed");
+ cp->tlc = strdup(t);
+ if (cp->tlc == NULL)
+ errx(1, "malloc failed");
+ }
+
+ fclose(fp);
+}
+
+static void
+add_zone_to_country(int lineno, const char *tlc, const char *descr,
+ const char *file, struct continent *cont)
+{
+ struct zone *zp;
+ struct country *cp;
+
+ if (tlc[0] < 'A' || tlc[0] > 'Z' || tlc[1] < 'A' || tlc[1] > 'Z')
+ errx(1, _PATH_ZONETAB ":%d: country code `%s' invalid",
+ lineno, tlc);
+
+ cp = &countries[CODE2INT(tlc)];
+ if (cp->name == 0)
+ errx(1, _PATH_ZONETAB ":%d: country code `%s' unknown",
+ lineno, tlc);
+
+ if (descr) {
+ if (cp->nzones < 0)
+ errx(1, _PATH_ZONETAB
+ ":%d: conflicting zone definition", lineno);
+
+ zp = malloc(sizeof *zp);
+ if (zp == 0)
+ errx(1, "malloc(%lu)", (unsigned long)sizeof *zp);
+
+ if (cp->nzones == 0)
+ TAILQ_INIT(&cp->zones);
+
+ zp->descr = strdup(descr);
+ if (zp->descr == NULL)
+ errx(1, "malloc failed");
+ zp->filename = strdup(file);
+ if (zp->filename == NULL)
+ errx(1, "malloc failed");
+ zp->continent = cont;
+ TAILQ_INSERT_TAIL(&cp->zones, zp, link);
+ cp->nzones++;
+ } else {
+ if (cp->nzones > 0)
+ errx(1, _PATH_ZONETAB
+ ":%d: zone must have description", lineno);
+ if (cp->nzones < 0)
+ errx(1, _PATH_ZONETAB
+ ":%d: zone multiply defined", lineno);
+ cp->nzones = -1;
+ cp->filename = strdup(file);
+ if (cp->filename == NULL)
+ errx(1, "malloc failed");
+ cp->continent = cont;
+ }
+}
+
+/*
+ * This comparison function intentionally sorts all of the null-named
+ * ``countries''---i.e., the codes that don't correspond to a real
+ * country---to the end. Everything else is lexical by country name.
+ */
+static int
+compare_countries(const void *xa, const void *xb)
+{
+ const struct country *a = xa, *b = xb;
+
+ if (a->name == 0 && b->name == 0)
+ return 0;
+ if (a->name == 0 && b->name != 0)
+ return 1;
+ if (b->name == 0)
+ return -1;
+
+ return strcmp(a->name, b->name);
+}
+
+/*
+ * This must be done AFTER all zone descriptions are read, since it breaks
+ * CODE2INT().
+ */
+static void
+sort_countries(void)
+{
+ qsort(countries, NCOUNTRIES, sizeof countries[0], compare_countries);
+}
+
+static void
+read_zones(void)
+{
+ FILE *fp;
+ char *line;
+ size_t len;
+ int lineno;
+ char *tlc, *coord, *file, *descr, *p;
+ char contbuf[16];
+ struct continent *cont;
+
+ fp = fopen(_PATH_ZONETAB, "r");
+ if (!fp)
+ err(1, _PATH_ZONETAB);
+ lineno = 0;
+
+ while ((line = fgetln(fp, &len)) != 0) {
+ lineno++;
+ if (line[len - 1] != '\n')
+ errx(1, _PATH_ZONETAB ":%d: invalid format", lineno);
+ line[len - 1] = '\0';
+ if (line[0] == '#')
+ continue;
+
+ tlc = strsep(&line, "\t");
+ if (strlen(tlc) != 2)
+ errx(1, _PATH_ZONETAB ":%d: invalid country code `%s'",
+ lineno, tlc);
+ coord = strsep(&line, "\t");
+ file = strsep(&line, "\t");
+ p = strchr(file, '/');
+ if (p == 0)
+ errx(1, _PATH_ZONETAB ":%d: invalid zone name `%s'",
+ lineno, file);
+ contbuf[0] = '\0';
+ strncat(contbuf, file, p - file);
+ cont = find_continent(contbuf);
+ if (!cont)
+ errx(1, _PATH_ZONETAB ":%d: invalid region `%s'",
+ lineno, contbuf);
+
+ descr = (line && *line) ? line : 0;
+
+ add_zone_to_country(lineno, tlc, descr, file, cont);
+ }
+ fclose(fp);
+}
+
+static void
+make_menus(void)
+{
+ struct country *cp;
+ struct zone *zp, *zp2;
+ struct continent *cont;
+ dialogMenuItem *dmi;
+ int i;
+
+ /*
+ * First, count up all the countries in each continent/ocean.
+ * Be careful to count those countries which have multiple zones
+ * only once for each. NB: some countries are in multiple
+ * continents/oceans.
+ */
+ for (cp = countries; cp->name; cp++) {
+ if (cp->nzones == 0)
+ continue;
+ if (cp->nzones < 0) {
+ cp->continent->nitems++;
+ } else {
+ TAILQ_FOREACH(zp, &cp->zones, link) {
+ cont = zp->continent;
+ for (zp2 = TAILQ_FIRST(&cp->zones);
+ zp2->continent != cont;
+ zp2 = TAILQ_NEXT(zp2, link))
+ ;
+ if (zp2 == zp)
+ zp->continent->nitems++;
+ }
+ }
+ }
+
+ /*
+ * Now allocate memory for the country menus. We set
+ * nitems back to zero so that we can use it for counting
+ * again when we actually build the menus.
+ */
+ for (i = 0; i < NCONTINENTS; i++) {
+ continent_names[i].continent->menu =
+ malloc(sizeof(dialogMenuItem) *
+ continent_names[i].continent->nitems);
+ if (continent_names[i].continent->menu == 0)
+ errx(1, "malloc for continent menu");
+ continent_names[i].continent->nitems = 0;
+ }
+
+ /*
+ * Now that memory is allocated, create the menu items for
+ * each continent. For multiple-zone countries, also create
+ * the country's zone submenu.
+ */
+ for (cp = countries; cp->name; cp++) {
+ if (cp->nzones == 0)
+ continue;
+ if (cp->nzones < 0) {
+ dmi = &cp->continent->menu[cp->continent->nitems];
+ memset(dmi, 0, sizeof *dmi);
+ asprintf(&dmi->prompt, "%d",
+ ++cp->continent->nitems);
+ dmi->title = cp->name;
+ dmi->checked = 0;
+ dmi->fire = set_zone_whole_country;
+ dmi->selected = 0;
+ dmi->data = cp;
+ } else {
+ cp->submenu = malloc(cp->nzones * sizeof *dmi);
+ if (cp->submenu == 0)
+ errx(1, "malloc for submenu");
+ cp->nzones = 0;
+ TAILQ_FOREACH(zp, &cp->zones, link) {
+ cont = zp->continent;
+ dmi = &cp->submenu[cp->nzones];
+ memset(dmi, 0, sizeof *dmi);
+ asprintf(&dmi->prompt, "%d",
+ ++cp->nzones);
+ dmi->title = zp->descr;
+ dmi->checked = 0;
+ dmi->fire = set_zone_multi;
+ dmi->selected = 0;
+ dmi->data = zp;
+
+ for (zp2 = TAILQ_FIRST(&cp->zones);
+ zp2->continent != cont;
+ zp2 = TAILQ_NEXT(zp2, link))
+ ;
+ if (zp2 != zp)
+ continue;
+
+ dmi = &cont->menu[cont->nitems];
+ memset(dmi, 0, sizeof *dmi);
+ asprintf(&dmi->prompt, "%d", ++cont->nitems);
+ dmi->title = cp->name;
+ dmi->checked = 0;
+ dmi->fire = set_zone_menu;
+ dmi->selected = 0;
+ dmi->data = cp;
+ }
+ }
+ }
+}
+
+static int
+set_zone_menu(dialogMenuItem *dmi)
+{
+ int rv;
+ char buf[256];
+ struct country *cp = dmi->data;
+ int menulen;
+
+ snprintf(buf, sizeof buf, "%s Time Zones", cp->name);
+ menulen = cp->nzones < 16 ? cp->nzones : 16;
+ rv = dialog_menu(buf, "Select a zone which observes the same time as "
+ "your locality.", -1, -1, menulen, -cp->nzones,
+ cp->submenu, 0, 0, 0);
+ if (rv != 0)
+ return DITEM_RECREATE;
+ return DITEM_LEAVE_MENU;
+}
+
+static int
+install_zone_file(const char *filename)
+{
+ struct stat sb;
+ int fd1, fd2;
+ int copymode;
+ char *msg;
+ ssize_t len;
+ char buf[1024];
+
+ if (lstat(_PATH_LOCALTIME, &sb) < 0)
+ /* Nothing there yet... */
+ copymode = 1;
+ else if(S_ISLNK(sb.st_mode))
+ copymode = 0;
+ else
+ copymode = 1;
+
+#ifdef VERBOSE
+ if (copymode)
+ asprintf(&msg, "Copying %s to " _PATH_LOCALTIME, filename);
+ else
+ asprintf(&msg, "Creating symbolic link " _PATH_LOCALTIME
+ " to %s", filename);
+
+ dialog_notify(msg);
+ free(msg);
+#endif
+
+ if (reallydoit) {
+ if (copymode) {
+ fd1 = open(filename, O_RDONLY, 0);
+ if (fd1 < 0) {
+ asprintf(&msg, "Could not open %s: %s",
+ filename, strerror(errno));
+ dialog_mesgbox("Error", msg, 8, 72);
+ free(msg);
+ return DITEM_FAILURE | DITEM_RECREATE;
+ }
+
+ unlink(_PATH_LOCALTIME);
+ fd2 = open(_PATH_LOCALTIME,
+ O_CREAT|O_EXCL|O_WRONLY,
+ S_IRUSR|S_IRGRP|S_IROTH);
+ if (fd2 < 0) {
+ asprintf(&msg, "Could not open "
+ _PATH_LOCALTIME ": %s",
+ strerror(errno));
+ dialog_mesgbox("Error", msg, 8, 72);
+ free(msg);
+ return DITEM_FAILURE | DITEM_RECREATE;
+ }
+
+ while ((len = read(fd1, buf, sizeof buf)) > 0)
+ len = write(fd2, buf, len);
+
+ if (len == -1) {
+ asprintf(&msg, "Error copying %s to "
+ _PATH_LOCALTIME ": %s",
+ filename, strerror(errno));
+ dialog_mesgbox("Error", msg, 8, 72);
+ free(msg);
+ /* Better to leave none than a corrupt one. */
+ unlink(_PATH_LOCALTIME);
+ return DITEM_FAILURE | DITEM_RECREATE;
+ }
+ close(fd1);
+ close(fd2);
+ } else {
+ if (access(filename, R_OK) != 0) {
+ asprintf(&msg, "Cannot access %s: %s",
+ filename, strerror(errno));
+ dialog_mesgbox("Error", msg, 8, 72);
+ free(msg);
+ return DITEM_FAILURE | DITEM_RECREATE;
+ }
+ unlink(_PATH_LOCALTIME);
+ if (symlink(filename, _PATH_LOCALTIME) < 0) {
+ asprintf(&msg, "Cannot create symbolic link "
+ _PATH_LOCALTIME " to %s: %s",
+ filename, strerror(errno));
+ dialog_mesgbox("Error", msg, 8, 72);
+ free(msg);
+ return DITEM_FAILURE | DITEM_RECREATE;
+ }
+ }
+ }
+
+#ifdef VERBOSE
+ if (copymode)
+ asprintf(&msg, "Copied timezone file from %s to "
+ _PATH_LOCALTIME, filename);
+ else
+ asprintf(&msg, "Created symbolic link from " _PATH_LOCALTIME
+ " to %s", filename);
+
+ dialog_mesgbox("Done", msg, 8, 72);
+ free(msg);
+#endif
+ return DITEM_LEAVE_MENU;
+}
+
+static int
+confirm_zone(const char *filename)
+{
+ char *msg;
+ struct tm *tm;
+ time_t t = time(0);
+ int rv;
+
+ setenv("TZ", filename, 1);
+ tzset();
+ tm = localtime(&t);
+
+ asprintf(&msg, "Does the abbreviation `%s' look reasonable?",
+ tm->tm_zone);
+ rv = !dialog_yesno("Confirmation", msg, 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;
+ int (*dialog_utc)(unsigned char *, unsigned char *, int, int);
+
+#if defined(__alpha__) || defined(__sparc64__)
+ dialog_utc = dialog_yesno;
+#else
+ dialog_utc = dialog_noyes;
+#endif
+
+ while ((c = getopt(argc, argv, "n")) != -1) {
+ switch(c) {
+ case 'n':
+ reallydoit = 0;
+ break;
+
+ default:
+ usage();
+ }
+ }
+
+ if (argc - optind > 1)
+ usage();
+
+ /* Override the user-supplied umask. */
+ (void)umask(S_IWGRP|S_IWOTH);
+
+ read_iso3166_table();
+ read_zones();
+ sort_countries();
+ make_menus();
+
+ init_dialog();
+ if (!dialog_utc("Select local or UTC (Greenwich Mean Time) clock",
+ "Is this machine's CMOS clock set to UTC? If it is set to local time,\n"
+ "or you don't know, please choose NO here!", 7, 72)) {
+ if (reallydoit)
+ unlink(_PATH_WALL_CMOS_CLOCK);
+ } else {
+ if (reallydoit) {
+ fd = open(_PATH_WALL_CMOS_CLOCK,
+ O_WRONLY|O_CREAT|O_TRUNC,
+ S_IRUSR|S_IRGRP|S_IROTH);
+ if (fd < 0)
+ err(1, "create %s", _PATH_WALL_CMOS_CLOCK);
+ close(fd);
+ }
+ }
+ dialog_clear_norefresh();
+ if (optind == argc - 1) {
+ char *msg;
+ asprintf(&msg, "\nUse the default `%s' zone?", argv[optind]);
+ if (!dialog_yesno("Default timezone provided", msg, 7, 72)) {
+ install_zone_file(argv[optind]);
+ dialog_clear();
+ end_dialog();
+ return 0;
+ }
+ free(msg);
+ dialog_clear_norefresh();
+ }
+ dialog_menu("Time Zone Selector", "Select a region", -1, -1,
+ NCONTINENTS, -NCONTINENTS, continents, 0, NULL, NULL);
+
+ dialog_clear();
+ end_dialog();
+ return 0;
+}
+
diff --git a/usr.sbin/usbd/Makefile b/usr.sbin/usbd/Makefile
new file mode 100644
index 0000000..aadecee
--- /dev/null
+++ b/usr.sbin/usbd/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+PROG= usbd
+MAN= usbd.conf.5 usbd.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/usbd/usbd.8 b/usr.sbin/usbd/usbd.8
new file mode 100644
index 0000000..5cd61a6
--- /dev/null
+++ b/usr.sbin/usbd/usbd.8
@@ -0,0 +1,149 @@
+.\" $NetBSD: usbd.8,v 1.2 1998/07/13 11:01:50 augustss Exp $
+.\" Copyright (c) 1998 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" Author: Lennart Augustsson
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the NetBSD
+.\" Foundation, Inc. and its contributors.
+.\" 4. Neither the name of The NetBSD Foundation nor the names of its
+.\" contributors may be used to endorse or promote products derived
+.\" from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd July 12, 1998
+.Dt USBD 8
+.Os
+.Sh NAME
+.Nm usbd
+.Nd supervise USB attach/detach
+.Sh SYNOPSIS
+.Nm
+.Op Fl c Ar configfile
+.Op Fl d
+.Op Fl e
+.Op Fl f Ar device
+.Op Fl n
+.Op Fl t Ar timeout
+.Op Fl v
+.Sh DESCRIPTION
+.Nm
+handles the USB device attachment and detachment.
+It does two things.
+Through opening the
+.Pa /dev/usb0 ,
+.Pa /dev/usb1 ,
+etc. devices, it enables the kernel to do handle change requests from
+attached hubs.
+This functionality will be removed when the kernel has
+kernel threads.
+The (multiple)
+.Fl f Ar device
+command line options specify which controllers it should handle.
+Normally this option is not needed.
+.Pp
+If the
+.Dq usb ,
+.Dq ohci
+and
+.Dq uhci
+modules are not loaded, it will load them automatically.
+.Pp
+The second part is the handling of the attachment and detachment of USB
+devices.
+The device
+.Pa /dev/usb
+is opened and events are read from it.
+Whenever a device is attached or
+detached the list of actions read from
+.Pa /etc/usbd.conf
+are searched for a matching entry.
+If found, the corresponding action is
+executed.
+.Pp
+The command line options are as follows:
+.Bl -tag -width Ds
+.It Fl c Ar filename
+Name of configuration file.
+The default is
+.Pa /etc/usbd.conf .
+.It Fl d
+Enable debugging to the standard output,
+and do not disassociate from the controlling terminal.
+.It Fl e
+Do one device tree exploration, no event queue handling and then exit.
+.It Fl f Ar device
+Specify the pathname of a USB controller device file.
+The flag may be repeated to watch more than one USB controller.
+The default is
+.Pa /dev/usb0
+through
+.Pa /dev/usb3 .
+Do not specify the device
+.Pa /dev/usb
+here.
+It is used for events only.
+.It Fl n
+Do not handle the event queue on /dev/usb.
+.It Fl t Ar timeout
+Set the timeout interval (in seconds) before an exploration happens
+without being triggered by a connect or disconnect.
+A timeout of 0 means that there is no timeout. The default is 30.
+.It Fl v
+Be verbose.
+Repeating the flag makes
+.Nm
+more verbose.
+.El
+.Sh FILES
+.Bl -tag -width /etc/usbd.conf -compact
+.It Pa /etc/usbd.conf
+.It Pa /dev/usb
+.It Pa /dev/usb0
+.It Pa /dev/usb1
+.It etc .
+.El
+.Sh SEE ALSO
+.Xr usb 4 ,
+.Xr usbd.conf 5
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Nx 1.4 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was written by
+.An Lennart Augustsson Aq augustss@carlstedt.se
+for the
+.Nx
+project.
+The event queue handling in
+.Nm
+was added by
+.An Nick Hibma Aq n_hibma@FreeBSD.org .
diff --git a/usr.sbin/usbd/usbd.c b/usr.sbin/usbd/usbd.c
new file mode 100644
index 0000000..85230af
--- /dev/null
+++ b/usr.sbin/usbd/usbd.c
@@ -0,0 +1,1115 @@
+/* $NetBSD: usbd.c,v 1.4 1998/12/09 00:57:19 augustss Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (augustss@netbsd.org).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* USBD creates 'threads' in the kernel, used for doing discovery when a
+ * device has attached or detached. This functionality should be removed
+ * once kernel threads have been added to the kernel.
+ * It also handles the event queue, and executing commands based on those
+ * events.
+ *
+ * See usbd(8).
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <signal.h>
+#include <paths.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+#include <sys/queue.h>
+#include <sys/wait.h>
+#include <regex.h>
+
+#include <dev/usb/usb.h>
+
+/* default name of configuration file
+ */
+
+#define CONFIGFILE "/etc/usbd.conf"
+
+/* the name of the device spitting out usb attach/detach events as well as
+ * the prefix for the individual busses (used as a semi kernel thread).
+ */
+#define USBDEV "/dev/usb"
+
+/* Maximum number of USB busses expected to be in a system
+ * XXX should be replaced by dynamic allocation.
+ */
+#define MAXUSBDEV 4
+
+/* Sometimes a device does not respond in time for interrupt
+ * driven explore to find it. Therefore we run an exploration
+ * at regular intervals to catch those.
+ */
+#define TIMEOUT 30
+
+/* The wildcard used in actions for strings and integers
+ */
+#define WILDCARD_STRING NULL
+#define WILDCARD_INT -1
+
+
+extern char *__progname; /* name of program */
+
+char *configfile = CONFIGFILE; /* name of configuration file */
+
+char *devs[MAXUSBDEV]; /* device names */
+int fds[MAXUSBDEV]; /* file descriptors for USBDEV\d+ */
+int ndevs = 0; /* number of entries in fds / devs */
+int fd = -1; /* file descriptor for USBDEV */
+
+int lineno;
+int verbose = 0; /* print message on what it is doing */
+
+typedef struct event_name_s {
+ int type; /* event number (from usb.h) */
+ char *name;
+} event_name_t;
+
+event_name_t event_names[] = {
+ {USB_EVENT_CTRLR_ATTACH, "ctrlr-attach"},
+ {USB_EVENT_CTRLR_DETACH, "ctrlr-detach"},
+ {USB_EVENT_DRIVER_ATTACH, "driver-attach"},
+ {USB_EVENT_DRIVER_DETACH, "driver-detach"},
+ {USB_EVENT_DEVICE_ATTACH, "device-attach"},
+ {USB_EVENT_DEVICE_DETACH, "device-detach"},
+ {0, NULL} /* NULL indicates end of list, not 0 */
+};
+
+#define DEVICE_FIELD 0 /* descriptive field */
+
+#define VENDOR_FIELD 1 /* selective fields */
+#define PRODUCT_FIELD 2
+#define RELEASE_FIELD 3
+#define CLASS_FIELD 4
+#define SUBCLASS_FIELD 5
+#define PROTOCOL_FIELD 6
+#define DEVNAME_FIELD 7
+
+#define ATTACH_FIELD 8 /* command fields */
+#define DETACH_FIELD 9
+
+
+typedef struct action_s {
+ char *name; /* descriptive string */
+
+ int vendor; /* selection criteria */
+ int product;
+ int release;
+ int class;
+ int subclass;
+ int protocol;
+ char *devname;
+ regex_t devname_regex;
+
+ char *attach; /* commands to execute */
+ char *detach;
+
+ STAILQ_ENTRY(action_s) next;
+} action_t;
+
+STAILQ_HEAD(action_list, action_s) actions = STAILQ_HEAD_INITIALIZER(actions);
+
+typedef struct action_match_s {
+ action_t *action;
+ char *devname;
+} action_match_t;
+
+
+/* the function returns 0 for failure, 1 for all arguments found and 2 for
+ * arguments left over in trail.
+ */
+typedef int (*config_field_fn) __P((action_t *action, char *args,
+ char **trail));
+
+int set_device_field(action_t *action, char *args, char **trail);
+int set_vendor_field(action_t *action, char *args, char **trail);
+int set_product_field(action_t *action, char *args, char **trail);
+int set_release_field(action_t *action, char *args, char **trail);
+int set_class_field(action_t *action, char *args, char **trail);
+int set_subclass_field(action_t *action, char *args, char **trail);
+int set_protocol_field(action_t *action, char *args, char **trail);
+int set_devname_field(action_t *action, char *args, char **trail);
+int set_attach_field(action_t *action, char *args, char **trail);
+int set_detach_field(action_t *action, char *args, char **trail);
+
+/* the list of fields supported in an entry */
+typedef struct config_field_s {
+ int event;
+ char *name;
+ config_field_fn function;
+} config_field_t;
+
+config_field_t config_fields[] = {
+ {DEVICE_FIELD, "device", set_device_field},
+
+ {VENDOR_FIELD, "vendor", set_vendor_field},
+ {PRODUCT_FIELD, "product", set_product_field},
+ {RELEASE_FIELD, "release", set_release_field},
+ {CLASS_FIELD, "class", set_class_field},
+ {SUBCLASS_FIELD, "subclass", set_subclass_field},
+ {PROTOCOL_FIELD, "protocol", set_protocol_field},
+ {DEVNAME_FIELD, "devname", set_devname_field},
+
+ {ATTACH_FIELD, "attach", set_attach_field},
+ {DETACH_FIELD, "detach", set_detach_field},
+
+ {0, NULL, NULL} /* NULL is EOL marker, not the 0 */
+};
+
+
+/* prototypes for some functions */
+void print_event __P((struct usb_event *event));
+void print_action __P((action_t *action, int i));
+void print_actions __P((void));
+int find_action __P((struct usb_device_info *devinfo,
+ action_match_t *action_match));
+
+
+void
+usage(void)
+{
+ fprintf(stderr, "usage: %s [-d] [-v] [-t timeout] [-e] [-f dev]\n"
+ " [-n] [-c config]\n",
+ __progname);
+ exit(1);
+}
+
+
+/* generic helper functions for the functions to set the fields of actions */
+int
+get_string(char *src, char **rdst, char **rsrc)
+{
+ /* Takes the first string from src, taking quoting into account.
+ * rsrc (if not NULL) is set to the first byte not included in the
+ * string returned in rdst.
+ *
+ * Input is:
+ * src = 'fir"st \'par"t second part';
+ * Returned is:
+ * *dst = 'hello \'world';
+ * if (rsrc != NULL)
+ * *rsrc = 'second part';
+ *
+ * Notice the fact that the single quote enclosed in double quotes is
+ * returned. Also notice that before second part there is more than
+ * one space, which is removed in rsrc.
+ *
+ * The string in src is not modified.
+ */
+
+ char *dst; /* destination string */
+ int i; /* index into src */
+ int j; /* index into dst */
+ int quoted = 0; /* 1 for single, 2 for double quoted */
+
+ dst = malloc(strlen(src)+1); /* XXX allocation is too big, realloc?*/
+ if (dst == NULL) { /* should not happen, really */
+ fprintf(stderr, "%s:%d: Out of memory\n", configfile, lineno);
+ exit(2);
+ }
+
+ /* find the end of the current string. If quotes are found the search
+ * continues until the corresponding quote is found.
+ * So,
+ * hel'lo" "wor'ld
+ * represents the string
+ * hello" "world
+ * and not (hello world).
+ */
+ for (i = 0, j = 0; i < strlen(src); i++) {
+ if (src[i] == '\'' && (quoted == 0 || quoted == 1)) {
+ quoted = (quoted? 0:1);
+ } else if (src[i] == '"' && (quoted == 0 || quoted == 2)) {
+ quoted = (quoted? 0:2);
+ } else if (isspace(src[i]) && !quoted) {
+ /* found a space outside quotes -> terminates src */
+ break;
+ } else {
+ dst[j++] = src[i]; /* copy character */
+ }
+ }
+
+ /* quotes being left open? */
+ if (quoted) {
+ fprintf(stderr, "%s:%d: Missing %s quote at end of '%s'\n",
+ configfile, lineno,
+ (quoted == 1? "single":"double"), src);
+ exit(2);
+ }
+
+ /* skip whitespace for second part */
+ for (/*i is set*/; i < strlen(src) && isspace(src[i]); i++)
+ ; /* nop */
+
+ dst[j] = '\0'; /* make sure it's NULL terminated */
+
+ *rdst = dst; /* and return the pointers */
+ if (rsrc != NULL) /* if info wanted */
+ *rsrc = &src[i];
+
+ if (*dst == '\0') { /* empty string */
+ return 0;
+ } else if (src[i] == '\0') { /* completely used (1 argument) */
+ return 1;
+ } else { /* 2 or more args, *rsrc is rest */
+ return 2;
+ }
+}
+
+int
+get_integer(char *src, int *dst, char **rsrc)
+{
+ char *endptr;
+
+ /* Converts str to a number. If one argument was found in
+ * str, 1 is returned and *dst is set to the value of the integer.
+ * If 2 or more arguments were presented, 2 is returned,
+ * *dst is set to the converted value and rsrc, if not null, points
+ * at the start of the next argument (whitespace skipped).
+ * Else 0 is returned and nothing else is valid.
+ */
+
+ if (src == NULL || *src == '\0') /* empty src */
+ return(0);
+
+ *dst = (int) strtol(src, &endptr, 0);
+
+ /* skip over whitespace of second argument */
+ while (isspace(*endptr))
+ endptr++;
+
+ if (rsrc)
+ *rsrc = endptr;
+
+ if (isspace(*endptr)) { /* partial match, 2 or more arguments */
+ return(2);
+ } else if (*endptr == '\0') { /* full match, 1 argument */
+ return(1);
+ } else { /* invalid src, no match */
+ return(0);
+ }
+}
+
+/* functions to set the fields of the actions appropriately */
+int
+set_device_field(action_t *action, char *args, char **trail)
+{
+ return(get_string(args, &action->name, trail));
+}
+int
+set_vendor_field(action_t *action, char *args, char **trail)
+{
+ return(get_integer(args, &action->vendor, trail));
+}
+int
+set_product_field(action_t *action, char *args, char **trail)
+{
+ return(get_integer(args, &action->product, trail));
+}
+int
+set_release_field(action_t *action, char *args, char **trail)
+{
+ return(get_integer(args, &action->release, trail));
+}
+int
+set_class_field(action_t *action, char *args, char **trail)
+{
+ return(get_integer(args, &action->class, trail));
+}
+int
+set_subclass_field(action_t *action, char *args, char **trail)
+{
+ return(get_integer(args, &action->subclass, trail));
+}
+int
+set_protocol_field(action_t *action, char *args, char **trail)
+{
+ return(get_integer(args, &action->protocol, trail));
+}
+int
+set_devname_field(action_t *action, char *args, char **trail)
+{
+ int match = get_string(args, &action->devname, trail);
+ int len;
+ int error;
+ char *string;
+# define ERRSTR_SIZE 100
+ char errstr[ERRSTR_SIZE];
+
+ if (match == 0)
+ return(0);
+
+ len = strlen(action->devname);
+ string = malloc(len + 15);
+ if (string == NULL)
+ return(0);
+
+ bcopy(action->devname, string+7, len); /* make some space for */
+ bcopy("[[:<:]]", string, 7); /* beginning of word */
+ bcopy("[[:>:]]", string+7+len, 7); /* and end of word */
+ string[len + 14] = '\0';
+
+ error = regcomp(&action->devname_regex, string, REG_NOSUB|REG_EXTENDED);
+ if (error) {
+ errstr[0] = '\0';
+ regerror(error, &action->devname_regex, errstr, ERRSTR_SIZE);
+ fprintf(stderr, "%s:%d: %s\n", configfile, lineno, errstr);
+ return(0);
+ }
+
+ return(match);
+}
+int
+set_attach_field(action_t *action, char *args, char **trail)
+{
+ return(get_string(args, &action->attach, trail));
+}
+int
+set_detach_field(action_t *action, char *args, char **trail)
+{
+ return(get_string(args, &action->detach, trail));
+}
+
+
+void
+read_configuration(void)
+{
+ FILE *file; /* file descriptor */
+ char *line; /* current line */
+ char *linez; /* current line, NULL terminated */
+ char *field; /* first part, the field name */
+ char *args; /* second part, arguments */
+ char *trail; /* remaining part after parsing, should be '' */
+ int len; /* length of current line */
+ int i,j; /* loop counters */
+ action_t *action = NULL; /* current action */
+
+ file = fopen(configfile, "r");
+ if (file == NULL) {
+ fprintf(stderr, "%s: Could not open for reading, %s\n",
+ configfile, strerror(errno));
+ exit(2);
+ }
+
+ for (lineno = 1; /* nop */;lineno++) {
+
+ line = fgetln(file, &len);
+ if (line == NULL) {
+ if (feof(file)) /* EOF */
+ break;
+ if (ferror(file)) {
+ fprintf(stderr, "%s:%d: Could not read, %s\n",
+ configfile, lineno, strerror(errno));
+ exit(2);
+ }
+ }
+
+ /* skip initial spaces */
+ while (*line != '\0' && isspace(*line)) {
+ line++;
+ len--;
+ }
+
+ if (len == 0) /* empty line */
+ continue;
+ if (line[0] == '#') /* comment line */
+ continue;
+
+ /* make a NULL terminated copy of the string */
+ linez = malloc(len+1);
+ if (linez == NULL) {
+ fprintf(stderr, "%s:%d: Out of memory\n",
+ configfile, lineno);
+ exit(2);
+ }
+ strncpy(linez, line, len);
+ linez[len] = '\0';
+
+ /* find the end of the current word (is field), that's the
+ * start of the arguments
+ */
+ field = linez;
+ args = linez;
+ while (*args != '\0' && !isspace(*args))
+ args++;
+
+ /* If arguments is not the empty string, NULL terminate the
+ * field and move the argument pointer to the first character
+ * of the arguments.
+ * If arguments is the empty string field and arguments both
+ * are terminated (strlen(field) >= 0, strlen(arguments) == 0).
+ */
+ if (*args != '\0') {
+ *args = '\0';
+ args++;
+ }
+
+ /* Skip initial spaces */
+ while (*args != '\0' && isspace(*args))
+ args++;
+
+ /* Cut off trailing whitespace */
+ for (i = 0, j = 0; args[i] != '\0'; i++)
+ if (!isspace(args[i]))
+ j = i+1;
+ args[j] = '\0';
+
+ /* We now have the field and the argument separated into
+ * two strings that are NULL terminated
+ */
+
+ /* If the field is 'device' we have to start a new action. */
+ if (strcmp(field, "device") == 0) {
+ /* Allocate a new action and set defaults */
+ action = malloc(sizeof(*action));
+ if (action == NULL) {
+ fprintf(stderr, "%s:%d: Out of memory\n",
+ configfile, lineno);
+ exit(2);
+ }
+ memset(action, 0, sizeof(*action));
+ action->product = WILDCARD_INT;
+ action->vendor = WILDCARD_INT;
+ action->release = WILDCARD_INT;
+ action->class = WILDCARD_INT;
+ action->subclass = WILDCARD_INT;
+ action->protocol = WILDCARD_INT;
+ action->devname = WILDCARD_STRING;
+
+ /* Add it to the end of the list to preserve order */
+ STAILQ_INSERT_TAIL(&actions, action, next);
+ }
+
+ if (action == NULL) {
+ line[len] = '\0'; /* XXX zero terminate */
+ fprintf(stderr, "%s:%d: Doesn't start with 'device' "
+ "but '%s'\n", configfile, lineno, field);
+ exit(2);
+ }
+
+ for (i = 0; config_fields[i].name ; i++) {
+ /* does the field name match? */
+ if (strcmp(config_fields[i].name, field) == 0) {
+ /* execute corresponding set-field function */
+ if ((config_fields[i].function)(action, args,
+ &trail)
+ != 1) {
+ fprintf(stderr,"%s:%d: "
+ "Syntax error in '%s'\n",
+ configfile, lineno, linez);
+ exit(2);
+ }
+ break;
+ }
+ }
+ if (config_fields[i].name == NULL) { /* Reached end of list*/
+ fprintf(stderr, "%s:%d: Unknown field '%s'\n",
+ configfile, lineno, field);
+ exit(2);
+ }
+ }
+
+ fclose(file);
+
+ if (verbose >= 2)
+ print_actions();
+}
+
+
+void
+print_event(struct usb_event *event)
+{
+ int i;
+ struct timespec *timespec = &event->ue_time;
+ struct usb_device_info *devinfo = &event->u.ue_device;
+
+ printf("%s: ", __progname);
+ for (i = 0; event_names[i].name != NULL; i++) {
+ if (event->ue_type == event_names[i].type) {
+ printf("%s event", event_names[i].name);
+ break;
+ }
+ }
+ if (event_names[i].name == NULL)
+ printf("unknown event %d", event->ue_type);
+
+ if (event->ue_type == USB_EVENT_DEVICE_ATTACH ||
+ event->ue_type == USB_EVENT_DEVICE_DETACH) {
+ devinfo = &event->u.ue_device;
+
+ printf(" at %ld.%09ld, %s, %s:\n",
+ timespec->tv_sec, timespec->tv_nsec,
+ devinfo->udi_product, devinfo->udi_vendor);
+
+ printf(" vndr=0x%04x prdct=0x%04x rlse=0x%04x "
+ "clss=0x%04x subclss=0x%04x prtcl=0x%04x\n",
+ devinfo->udi_vendorNo, devinfo->udi_productNo,
+ devinfo->udi_releaseNo,
+ devinfo->udi_class, devinfo->udi_subclass, devinfo->udi_protocol);
+
+ if (devinfo->udi_devnames[0][0] != '\0') {
+ char c = ' ';
+
+ printf(" device names:");
+ for (i = 0; i < USB_MAX_DEVNAMES; i++) {
+ if (devinfo->udi_devnames[i][0] == '\0')
+ break;
+
+ printf("%c%s", c, devinfo->udi_devnames[i]);
+ c = ',';
+ }
+ }
+ } else if (event->ue_type == USB_EVENT_CTRLR_ATTACH ||
+ event->ue_type == USB_EVENT_CTRLR_DETACH) {
+ printf(" bus=%d", &event->u.ue_ctrlr.ue_bus);
+ } else if (event->ue_type == USB_EVENT_DRIVER_ATTACH ||
+ event->ue_type == USB_EVENT_DRIVER_DETACH) {
+ printf(" cookie=%u devname=%s",
+ &event->u.ue_driver.ue_cookie.cookie,
+ &event->u.ue_driver.ue_devname);
+ }
+ printf("\n");
+}
+
+void
+print_action(action_t *action, int i)
+{
+ if (action == NULL)
+ return;
+
+ printf("%s: action %d: %s\n",
+ __progname, i,
+ (action->name? action->name:""));
+ if (action->product != WILDCARD_INT ||
+ action->vendor != WILDCARD_INT ||
+ action->release != WILDCARD_INT ||
+ action->class != WILDCARD_INT ||
+ action->subclass != WILDCARD_INT ||
+ action->protocol != WILDCARD_INT)
+ printf(" ");
+ if (action->vendor != WILDCARD_INT)
+ printf(" vndr=0x%04x", action->vendor);
+ if (action->product != WILDCARD_INT)
+ printf(" prdct=0x%04x", action->product);
+ if (action->release != WILDCARD_INT)
+ printf(" rlse=0x%04x", action->release);
+ if (action->class != WILDCARD_INT)
+ printf(" clss=0x%04x", action->class);
+ if (action->subclass != WILDCARD_INT)
+ printf(" subclss=0x%04x", action->subclass);
+ if (action->protocol != WILDCARD_INT)
+ printf(" prtcl=0x%04x", action->protocol);
+ if (action->vendor != WILDCARD_INT ||
+ action->product != WILDCARD_INT ||
+ action->release != WILDCARD_INT ||
+ action->class != WILDCARD_INT ||
+ action->subclass != WILDCARD_INT ||
+ action->protocol != WILDCARD_INT)
+ printf("\n");
+ if (action->devname != WILDCARD_STRING)
+ printf(" devname: %s\n", action->devname);
+
+ if (action->attach != NULL)
+ printf(" attach='%s'\n",
+ action->attach);
+ if (action->detach != NULL)
+ printf(" detach='%s'\n",
+ action->detach);
+}
+
+void
+print_actions()
+{
+ int i = 0;
+ action_t *action;
+
+ STAILQ_FOREACH(action, &actions, next)
+ print_action(action, ++i);
+
+ printf("%s: %d action%s\n", __progname, i, (i == 1? "":"s"));
+}
+
+
+int
+match_devname(action_t *action, struct usb_device_info *devinfo)
+{
+ int i;
+ regmatch_t match;
+ int error;
+
+ for (i = 0; i < USB_MAX_DEVNAMES; i++) {
+ if (devinfo->udi_devnames[i][0] == '\0')
+ break;
+
+ error = regexec(&action->devname_regex, devinfo->udi_devnames[i],
+ 1, &match, 0);
+ if (error == 0) {
+ if (verbose >= 2)
+ printf("%s: %s matches %s\n", __progname,
+ devinfo->udi_devnames[i], action->devname);
+ return(i);
+ }
+ }
+
+ return(-1);
+}
+
+
+int
+find_action(struct usb_device_info *devinfo, action_match_t *action_match)
+{
+ action_t *action;
+ char *devname = NULL;
+ int match = -1;
+
+ STAILQ_FOREACH(action, &actions, next) {
+ if ((action->vendor == WILDCARD_INT ||
+ action->vendor == devinfo->udi_vendorNo) &&
+ (action->product == WILDCARD_INT ||
+ action->product == devinfo->udi_productNo) &&
+ (action->release == WILDCARD_INT ||
+ action->release == devinfo->udi_releaseNo) &&
+ (action->class == WILDCARD_INT ||
+ action->class == devinfo->udi_class) &&
+ (action->subclass == WILDCARD_INT ||
+ action->subclass == devinfo->udi_subclass) &&
+ (action->protocol == WILDCARD_INT ||
+ action->protocol == devinfo->udi_protocol) &&
+ (action->devname == WILDCARD_STRING ||
+ (match = match_devname(action, devinfo)) != -1)) {
+ /* found match !*/
+
+ /* Find a devname for pretty printing. Either
+ * the matched one or otherwise, if there is only
+ * one devname for that device, use that.
+ */
+ if (match >= 0)
+ devname = devinfo->udi_devnames[match];
+ else if (devinfo->udi_devnames[0][0] != '\0' &&
+ devinfo->udi_devnames[1][0] == '\0')
+ /* if we have exactly 1 device name */
+ devname = devinfo->udi_devnames[0];
+
+ if (verbose) {
+ printf("%s: Found action '%s' for %s, %s",
+ __progname, action->name,
+ devinfo->udi_product, devinfo->udi_vendor);
+ if (devname)
+ printf(" at %s", devname);
+ printf("\n");
+ }
+
+ action_match->action = action;
+ action_match->devname = devname;
+
+ return(1);
+ }
+ }
+
+ return(0);
+}
+
+void
+execute_command(char *cmd)
+{
+ pid_t pid;
+ struct sigaction ign, intact, quitact;
+ sigset_t newsigblock, oldsigblock;
+ int status;
+ int i;
+
+ if (verbose)
+ printf("%s: Executing '%s'\n", __progname, cmd);
+ if (cmd == NULL)
+ return;
+
+ /* The code below is directly taken from the system(3) call.
+ * Added to it is the closing of open file descriptors.
+ */
+ /*
+ * Ignore SIGINT and SIGQUIT, block SIGCHLD. Remember to save
+ * existing signal dispositions.
+ */
+ ign.sa_handler = SIG_IGN;
+ (void) sigemptyset(&ign.sa_mask);
+ ign.sa_flags = 0;
+ (void) sigaction(SIGINT, &ign, &intact);
+ (void) sigaction(SIGQUIT, &ign, &quitact);
+ (void) sigemptyset(&newsigblock);
+ (void) sigaddset(&newsigblock, SIGCHLD);
+ (void) sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock);
+ pid = fork();
+ if (pid == -1) {
+ fprintf(stderr, "%s: fork failed, %s\n",
+ __progname, strerror(errno));
+ } else if (pid == 0) {
+ /* child here */
+
+ /* close all open file handles for USBDEV\d* devices */
+ for (i = 0; i < ndevs; i++)
+ close(fds[i]); /* USBDEV\d+ */
+ close(fd); /* USBDEV */
+
+ /* Restore original signal dispositions and exec the command. */
+ (void) sigaction(SIGINT, &intact, NULL);
+ (void) sigaction(SIGQUIT, &quitact, NULL);
+ (void) sigprocmask(SIG_SETMASK, &oldsigblock, NULL);
+
+ execl(_PATH_BSHELL, "sh", "-c", cmd, (char *)NULL);
+
+ /* should only be reached in case of error */
+ exit(127);
+ } else {
+ /* parent here */
+ do {
+ pid = waitpid(pid, &status, 0);
+ } while (pid == -1 && errno == EINTR);
+ }
+ (void) sigaction(SIGINT, &intact, NULL);
+ (void) sigaction(SIGQUIT, &quitact, NULL);
+ (void) sigprocmask(SIG_SETMASK, &oldsigblock, NULL);
+
+ if (pid == -1) {
+ fprintf(stderr, "%s: waitpid returned: %s\n",
+ __progname, strerror(errno));
+ } else if (pid == 0) {
+ fprintf(stderr, "%s: waitpid returned 0 ?!\n",
+ __progname);
+ } else {
+ if (status == -1) {
+ fprintf(stderr, "%s: Could not start '%s'\n",
+ __progname, cmd);
+ } else if (status == 127) {
+ fprintf(stderr, "%s: Shell failed for '%s'\n",
+ __progname, cmd);
+ } else if (WIFEXITED(status) && WEXITSTATUS(status)) {
+ fprintf(stderr, "%s: '%s' returned %d\n",
+ __progname, cmd, WEXITSTATUS(status));
+ } else if (WIFSIGNALED(status)) {
+ fprintf(stderr, "%s: '%s' caught signal %d\n",
+ __progname, cmd, WTERMSIG(status));
+ } else if (verbose >= 2) {
+ printf("%s: '%s' is ok\n", __progname, cmd);
+ }
+ }
+}
+
+void
+process_event_queue(int fd)
+{
+ struct usb_event event;
+ int error;
+ int len;
+ action_match_t action_match;
+
+ for (;;) {
+ len = read(fd, &event, sizeof(event));
+ if (len == -1) {
+ if (errno == EWOULDBLOCK) {
+ /* no more events */
+ break;
+ } else {
+ fprintf(stderr,"%s: Could not read event, %s\n",
+ __progname, strerror(errno));
+ exit(1);
+ }
+ }
+ if (len == 0)
+ break;
+ if (len != sizeof(event)) {
+ fprintf(stderr, "partial read on %s\n", USBDEV);
+ exit(1);
+ }
+
+ /* we seem to have gotten a valid event */
+
+ if (verbose)
+ print_event(&event);
+
+ /* handle the event appropriately */
+ switch (event.ue_type) {
+ case USB_EVENT_CTRLR_ATTACH:
+ if (verbose)
+ printf("USB_EVENT_CTRLR_ATTACH\n");
+ break;
+ case USB_EVENT_CTRLR_DETACH:
+ if (verbose)
+ printf("USB_EVENT_CTRLR_DETACH\n");
+ break;
+ case USB_EVENT_DEVICE_ATTACH:
+ case USB_EVENT_DEVICE_DETACH:
+ if (find_action(&event.u.ue_device, &action_match) == 0)
+ /* nothing found */
+ break;
+
+ if (verbose >= 2)
+ print_action(action_match.action, 0);
+
+ if (action_match.devname) {
+ if (verbose >= 2)
+ printf("%s: Setting DEVNAME='%s'\n",
+ __progname, action_match.devname);
+
+ error = setenv("DEVNAME", action_match.devname, 1);
+ if (error)
+ fprintf(stderr, "%s: setenv(\"DEVNAME\", \"%s\",1) failed, %s\n",
+ __progname, action_match.devname, strerror(errno));
+ }
+
+ if (USB_EVENT_IS_ATTACH(event.ue_type) &&
+ action_match.action->attach)
+ execute_command(action_match.action->attach);
+ if (USB_EVENT_IS_DETACH(event.ue_type) &&
+ action_match.action->detach)
+ execute_command(action_match.action->detach);
+ break;
+ case USB_EVENT_DRIVER_ATTACH:
+ if (verbose)
+ printf("USB_EVENT_DRIVER_DETACH\n");
+ break;
+ case USB_EVENT_DRIVER_DETACH:
+ if (verbose)
+ printf("USB_EVENT_DRIVER_DETACH\n");
+ break;
+ default:
+ printf("Unknown USB event %d\n", event.ue_type);
+ }
+ }
+}
+
+
+int
+main(int argc, char **argv)
+{
+ int error, i;
+ int ch; /* getopt option */
+ extern char *optarg; /* from getopt */
+ extern int optind; /* from getopt */
+ int debug = 0; /* print debugging output */
+ int explore_once = 0; /* don't do only explore */
+ int handle_events = 1; /* do handle the event queue */
+ int maxfd; /* maximum fd in use */
+ char buf[50]; /* for creation of the filename */
+ fd_set r,w;
+ int itimeout = TIMEOUT; /* timeout for select */
+ struct timeval tv;
+
+ if (modfind(USB_UHUB) < 0) {
+ if (kldload(USB_KLD) < 0 || modfind(USB_UHUB) < 0) {
+ perror(USB_KLD ": Kernel module not available");
+ return 1;
+ }
+ }
+
+ while ((ch = getopt(argc, argv, "c:def:nt:v")) != -1) {
+ switch(ch) {
+ case 'c':
+ configfile = strdup(optarg);
+ if (configfile == NULL) {
+ fprintf(stderr, "strdup returned NULL\n");
+ return 1;
+ }
+ break;
+ case 'd':
+ debug++;
+ break;
+ case 'e':
+ explore_once = 1;
+ break;
+ case 'f':
+ if (ndevs < MAXUSBDEV)
+ devs[ndevs++] = optarg;
+ break;
+ case 'n':
+ handle_events = 0;
+ break;
+ case 't':
+ itimeout = atoi(optarg);
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ maxfd = 0;
+ if (ndevs == 0) {
+ /* open all the USBDEVS\d+ devices */
+ for (i = 0; i < MAXUSBDEV; i++) {
+ sprintf(buf, "%s%d", USBDEV, i);
+ fds[ndevs] = open(buf, O_RDWR);
+ if (fds[ndevs] >= 0) {
+ devs[ndevs] = strdup(buf);
+ if (devs[ndevs] == NULL) {
+ fprintf(stderr, "strdup returned NULL\n");
+ return 1;
+ }
+ if (verbose)
+ printf("%s: opened %s\n",
+ __progname, devs[ndevs]);
+ if (fds[ndevs] > maxfd)
+ maxfd = fds[ndevs];
+ ndevs++;
+ } else if (errno != ENXIO && errno != ENOENT) {
+ /* there was an error, on a device that does
+ * exist (device is configured)
+ */
+ fprintf(stderr, "%s: Could not open %s, %s\n",
+ __progname, buf, strerror(errno));
+ exit(1);
+ }
+ }
+ } else {
+ /* open all the files specified with -f */
+ for (i = 0; i < ndevs; i++) {
+ fds[i] = open(devs[i], O_RDWR);
+ if (fds[i] < 0) {
+ fprintf(stderr, "%s: Could not open %s, %s\n",
+ __progname, devs[i], strerror(errno));
+ exit(1);
+ } else {
+ if (verbose)
+ printf("%s: opened %s\n",
+ __progname, devs[i]);
+ if (fds[i] > maxfd)
+ maxfd = fds[i];
+ }
+ }
+ }
+
+ if (ndevs == 0) {
+ fprintf(stderr, "No USB host controllers found\n");
+ exit(1);
+ }
+
+
+ /* Do the explore once and exit */
+ if (explore_once) {
+ for (i = 0; i < ndevs; i++) {
+ error = ioctl(fds[i], USB_DISCOVER);
+ if (error < 0) {
+ fprintf(stderr, "%s: ioctl(%s, USB_DISCOVER) "
+ "failed, %s\n",
+ __progname, devs[i], strerror(errno));
+ exit(1);
+ }
+ }
+ exit(0);
+ }
+
+ if (handle_events) {
+ if (verbose)
+ printf("%s: reading configuration file %s\n",
+ __progname, configfile);
+ read_configuration();
+
+ fd = open(USBDEV, O_RDONLY | O_NONBLOCK);
+ if (fd < 0) {
+ fprintf(stderr, "%s: Could not open %s, %s\n",
+ __progname, USBDEV, strerror(errno));
+ exit(1);
+ }
+ if (verbose)
+ printf("%s: opened %s\n", __progname, USBDEV);
+ if (fd > maxfd)
+ maxfd = fd;
+
+ process_event_queue(fd); /* dequeue the initial events */
+ }
+
+ /* move to the background */
+ if (!debug)
+ daemon(0, 0);
+
+ /* start select on all the open file descriptors */
+ for (;;) {
+ FD_ZERO(&r);
+ FD_ZERO(&w);
+ if (handle_events)
+ FD_SET(fd, &r); /* device USBDEV */
+ for (i = 0; i < ndevs; i++)
+ FD_SET(fds[i], &w); /* device USBDEV\d+ */
+ tv.tv_usec = 0;
+ tv.tv_sec = itimeout;
+ error = select(maxfd+1, &r, &w, 0, itimeout ? &tv : 0);
+ if (error < 0) {
+ fprintf(stderr, "%s: Select failed, %s\n",
+ __progname, strerror(errno));
+ exit(1);
+ }
+
+ /* USBDEV\d+ devices have signaled change, do a usb_discover */
+ for (i = 0; i < ndevs; i++) {
+ if (error == 0 || FD_ISSET(fds[i], &w)) {
+ if (verbose >= 2)
+ printf("%s: doing %sdiscovery on %s\n",
+ __progname,
+ (error? "":"timeout "), devs[i]);
+ if (ioctl(fds[i], USB_DISCOVER) < 0) {
+ fprintf(stderr, "%s: ioctl(%s, "
+ "USB_DISCOVER) failed, %s\n",
+ __progname, devs[i],
+ strerror(errno));
+ exit(1);
+ }
+ }
+ }
+
+ /* check the event queue */
+ if (handle_events && (FD_ISSET(fd, &r) || error == 0)) {
+ if (verbose >= 2)
+ printf("%s: processing event queue %son %s\n",
+ __progname,
+ (error? "":"due to timeout "), USBDEV);
+ process_event_queue(fd);
+ }
+ }
+}
diff --git a/usr.sbin/usbd/usbd.conf.5 b/usr.sbin/usbd/usbd.conf.5
new file mode 100644
index 0000000..720b730
--- /dev/null
+++ b/usr.sbin/usbd/usbd.conf.5
@@ -0,0 +1,163 @@
+.\"
+.\" Copyright (c) 1999 Nick Hibma. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.\" Many parts of this manual have been snarfed from the pccard.conf (5) man
+.\" page, copyright by Andrew McRae.
+.\"
+.Dd November 19, 1999
+.Dt USBD.CONF 5
+.Os
+.Sh NAME
+.Nm usbd.conf
+.Nd
+.Xr usbd 8
+configuration file
+.Sh DESCRIPTION
+The
+.Nm
+file is the configuration file for the
+.Xr usbd 8
+daemon.
+It provides information to allow execution of userland commands
+on events reported by the
+.Xr usb 4
+subsystem in the kernel.
+Currently the only events are device attach and
+detach, but could in the future be extended to include power management
+functions.
+.Pp
+The configuration file consists of a sorted list of entries.
+Each entry
+describes a set of criteria commands.
+When an event occurs, the criteria
+are checked and if met, the commands for that event are executed through
+a shell.
+The list is sorted and scanned from top to bottom.
+The first
+matching entry is used for an event.
+.Pp
+Each entry contains a number of fields.
+There are 3 types of fields:
+descriptive fields, selection criteria and commands to execute on
+events.
+The field name is case sensitive and should be all lower case.
+Each field can have one or more arguments.
+.Pp
+The following fields are available:
+.Bl -tag -width devicename\ <Id>
+.It device Ar string
+Start a new entry.
+.Ar string
+is an arbitrary string used for pretty printing.
+.It product Ar id
+Product Id
+.It vendor Ar id
+Vendor Id
+.It release Ar id
+Release Id, also called revision Id sometimes.
+.It class Ar id
+Device Class
+.It subclass Ar id
+Device Subclass
+.It protocol Ar id
+Device Protocol
+.It devname Ar string
+Device name, for example umass2, or ums0. These device names can contain
+regular expressions.
+See
+.Xr regex 3
+and
+.Xr re_format 7 .
+The device name that is matched can be used in the commands below
+through adding ${DEVNAME} somewhere in that string.
+.El
+.Pp
+String arguments may be quoted.
+If a string argument contains a space or
+tab character it needs to be enclosed in single or double quotes.
+If an
+argument contains a single or double quote, that quote needs to be
+enclosed in double or single quotes respectively.
+See below for
+examples.
+.Pp
+Numeric arguments can either be specified in decimal (42), octal (052) or
+hexadeximal (0x2a).
+.Pp
+The values for the fields
+.Li product , vendor , release, class , subclass
+and
+.Li protocol
+can be retrieved by killing the
+.Nm usbd
+daemon and running it with the
+.Fl d
+and
+.Fl v
+flags.
+.Pp
+Commands to be executed when the action is matched:
+.Bl -tag -width devicename\ <Id>
+.It attach Ar string
+Shell command to execute when a device is attached.
+.It detach Ar string
+Shell command to execute when a device is detached.
+.El
+.Sh EXAMPLES
+A sample entry to rescan the SCSI bus on connection of a
+.Tn "Iomega USB Zip Drive" :
+.Bd -literal
+ device "USB Zip drive"
+ product 0x0001
+ vendor 0x059b
+ release 0x0100
+ attach "/usr/bin/camcontrol rescan bus 0"
+.Ed
+.Pp
+To start up moused for a newly attached mouse:
+.Bd -literal
+ device "Mouse"
+ devname "ums[0-9]+"
+ attach "/usr/sbin/moused -p /dev/${DEVNAME} -I /var/run/moused.${DEVNAME}.pid"
+.Ed
+.Sh FILES
+.Bl -tag -width /etc/pccard.conf -compact
+.It Pa /etc/usbd.conf
+The
+.Nm usbd
+configuration file.
+.El
+.Sh SEE ALSO
+.Xr usb 4 ,
+.Xr usbd 8 ,
+.Xr usbdevs 8
+.Sh BUGS
+It is currently not possible to use a selection criterium more than once. For
+example it is not possible to specify more than one vendor Id.
+.Sh AUTHORS
+The man page for the usbd configuration file was written by
+.An Nick Hibma Aq n_hibma@FreeBSD.org .
diff --git a/usr.sbin/usbdevs/Makefile b/usr.sbin/usbdevs/Makefile
new file mode 100644
index 0000000..985bc7a
--- /dev/null
+++ b/usr.sbin/usbdevs/Makefile
@@ -0,0 +1,9 @@
+# $NetBSD: Makefile,v 1.2 1998/07/12 20:40:45 augustss Exp $
+# FreeBSD $FreeBSD$
+
+PROG= usbdevs
+MAN= usbdevs.8
+
+WARNS?= 2
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/usbdevs/usbdevs.8 b/usr.sbin/usbdevs/usbdevs.8
new file mode 100644
index 0000000..e469231
--- /dev/null
+++ b/usr.sbin/usbdevs/usbdevs.8
@@ -0,0 +1,72 @@
+.\" $NetBSD: usbdevs.8,v 1.5 2000/10/15 12:44:11 bjh21 Exp $
+.\" Copyright (c) 1999 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" Author: Lennart Augustsson
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the NetBSD
+.\" Foundation, Inc. and its contributors.
+.\" 4. Neither the name of The NetBSD Foundation nor the names of its
+.\" contributors may be used to endorse or promote products derived
+.\" from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd October 15, 2000
+.Dt USBDEVS 8
+.Os
+.Sh NAME
+.Nm usbdevs
+.Nd show USB devices connected to the system
+.Sh SYNOPSIS
+.Nm
+.Op Fl a Ar addr
+.Op Fl d
+.Op Fl f Ar dev
+.Op Fl 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 ".Fl a Ar addr"
+.It Fl a Ar addr
+only print information about the device at the given address.
+.It Fl d
+Show the device drivers associated with each device.
+.It Fl f Ar dev
+only print information for the given USB controller.
+.It Fl 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..71bf824
--- /dev/null
+++ b/usr.sbin/usbdevs/usbdevs.c
@@ -0,0 +1,227 @@
+/* $NetBSD: usbdevs.c,v 1.17 2001/02/19 23:22:48 cgd Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (augustss@netbsd.org).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <err.h>
+#include <errno.h>
+#include <dev/usb/usb.h>
+#if defined(__FreeBSD__)
+#include <sys/ioctl.h>
+#endif
+
+#define USBDEV "/dev/usb"
+
+int verbose = 0;
+int showdevs = 0;
+
+void usage(void);
+void usbdev(int f, int a, int rec);
+void usbdump(int f);
+void dumpone(char *name, int f, int addr);
+int main(int, char **);
+
+void
+usage()
+{
+ fprintf(stderr, "usage: %s [-a addr] [-d] [-f dev] [-v]\n",
+ getprogname());
+ exit(1);
+}
+
+char done[USB_MAX_DEVICES];
+int indent;
+
+void
+usbdev(int f, int a, int rec)
+{
+ struct usb_device_info di;
+ int e, p, i;
+
+ di.udi_addr = a;
+ e = ioctl(f, USB_DEVICEINFO, &di);
+ if (e) {
+ if (errno != ENXIO)
+ printf("addr %d: I/O error\n", a);
+ return;
+ }
+ printf("addr %d: ", a);
+ done[a] = 1;
+ if (verbose) {
+ switch (di.udi_speed) {
+ case USB_SPEED_LOW: printf("low speed, "); break;
+ case USB_SPEED_FULL: printf("full speed, "); break;
+ case USB_SPEED_HIGH: printf("high speed, "); break;
+ default: break;
+ }
+ if (di.udi_power)
+ printf("power %d mA, ", di.udi_power);
+ else
+ printf("self powered, ");
+ if (di.udi_config)
+ printf("config %d, ", di.udi_config);
+ else
+ printf("unconfigured, ");
+ }
+ if (verbose) {
+ printf("%s(0x%04x), %s(0x%04x), rev %s",
+ di.udi_product, di.udi_productNo,
+ di.udi_vendor, di.udi_vendorNo, di.udi_release);
+ } else
+ printf("%s, %s", di.udi_product, di.udi_vendor);
+ printf("\n");
+ if (showdevs) {
+ for (i = 0; i < USB_MAX_DEVNAMES; i++)
+ if (di.udi_devnames[i][0])
+ printf("%*s %s\n", indent, "",
+ di.udi_devnames[i]);
+ }
+ if (!rec)
+ return;
+ for (p = 0; p < di.udi_nports; p++) {
+ int s = di.udi_ports[p];
+ if (s >= USB_MAX_DEVICES) {
+ if (verbose) {
+ printf("%*sport %d %s\n", indent+1, "", p+1,
+ s == USB_PORT_ENABLED ? "enabled" :
+ s == USB_PORT_SUSPENDED ? "suspended" :
+ s == USB_PORT_POWERED ? "powered" :
+ s == USB_PORT_DISABLED ? "disabled" :
+ "???");
+
+ }
+ continue;
+ }
+ indent++;
+ printf("%*s", indent, "");
+ if (verbose)
+ printf("port %d ", p+1);
+ if (s == 0)
+ printf("addr 0 should never happen!\n");
+ else
+ usbdev(f, s, 1);
+ indent--;
+ }
+}
+
+void
+usbdump(int f)
+{
+ int a;
+
+ for (a = 1; a < USB_MAX_DEVICES; a++) {
+ if (!done[a])
+ usbdev(f, a, 1);
+ }
+}
+
+void
+dumpone(char *name, int f, int addr)
+{
+ if (verbose)
+ printf("Controller %s:\n", name);
+ indent = 0;
+ memset(done, 0, sizeof done);
+ if (addr)
+ usbdev(f, addr, 0);
+ else
+ usbdump(f);
+}
+
+int
+main(int argc, char **argv)
+{
+ int ch, i, f;
+ char buf[50];
+ char *dev = 0;
+ int addr = 0;
+ int ncont;
+
+ while ((ch = getopt(argc, argv, "a:df:v?")) != -1) {
+ switch(ch) {
+ case 'a':
+ addr = atoi(optarg);
+ break;
+ case 'd':
+ showdevs++;
+ 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) {
+ dumpone(buf, f, addr);
+ close(f);
+ } else {
+ if (errno == ENOENT || errno == ENXIO)
+ continue;
+ warn("%s", buf);
+ }
+ ncont++;
+ }
+ if (verbose && ncont == 0)
+ printf("%s: no USB controllers found\n",
+ getprogname());
+ } else {
+ f = open(dev, O_RDONLY);
+ if (f >= 0)
+ dumpone(dev, f, addr);
+ else
+ err(1, "%s", dev);
+ }
+ exit(0);
+}
diff --git a/usr.sbin/vidcontrol/Makefile b/usr.sbin/vidcontrol/Makefile
new file mode 100644
index 0000000..8c8f6bb
--- /dev/null
+++ b/usr.sbin/vidcontrol/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+PROG= vidcontrol
+SRCS= vidcontrol.c decode.c
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/vidcontrol/decode.c b/usr.sbin/vidcontrol/decode.c
new file mode 100644
index 0000000..fddc3d5
--- /dev/null
+++ b/usr.sbin/vidcontrol/decode.c
@@ -0,0 +1,99 @@
+/*-
+ * Copyright (c) 1994 Søren Schmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <string.h>
+#include "decode.h"
+
+int decode(FILE *fd, char *buffer, int len)
+{
+ int n, pos = 0, tpos;
+ char *bp, *p;
+ char tbuffer[3];
+ char temp[128];
+
+#define DEC(c) (((c) - ' ') & 0x3f)
+
+ do {
+ if (!fgets(temp, sizeof(temp), fd))
+ return(0);
+ } while (strncmp(temp, "begin ", 6));
+ sscanf(temp, "begin %o %s", &n, temp);
+ bp = buffer;
+ for (;;) {
+ if (!fgets(p = temp, sizeof(temp), fd))
+ return(0);
+ if ((n = DEC(*p)) <= 0)
+ break;
+ for (++p; n > 0; p += 4, n -= 3) {
+ tpos = 0;
+ if (n >= 3) {
+ tbuffer[tpos++] = DEC(p[0])<<2 | DEC(p[1])>>4;
+ tbuffer[tpos++] = DEC(p[1])<<4 | DEC(p[2])>>2;
+ tbuffer[tpos++] = DEC(p[2])<<6 | DEC(p[3]);
+ }
+ else {
+ if (n >= 1) {
+ tbuffer[tpos++] =
+ DEC(p[0])<<2 | DEC(p[1])>>4;
+ }
+ if (n >= 2) {
+ tbuffer[tpos++] =
+ DEC(p[1])<<4 | DEC(p[2])>>2;
+ }
+ if (n >= 3) {
+ tbuffer[tpos++] =
+ DEC(p[2])<<6 | DEC(p[3]);
+ }
+ }
+ if (tpos == 0)
+ continue;
+ if (tpos + pos > len) {
+ tpos = len - pos;
+ /*
+ * Arrange return value > len to indicate
+ * overflow.
+ */
+ pos++;
+ }
+ bcopy(tbuffer, bp, tpos);
+ pos += tpos;
+ bp += tpos;
+ if (pos > len)
+ return(pos);
+ }
+ }
+ if (!fgets(temp, sizeof(temp), fd) || strcmp(temp, "end\n"))
+ return(0);
+ return(pos);
+}
diff --git a/usr.sbin/vidcontrol/decode.h b/usr.sbin/vidcontrol/decode.h
new file mode 100644
index 0000000..1f1cba4
--- /dev/null
+++ b/usr.sbin/vidcontrol/decode.h
@@ -0,0 +1,3 @@
+/* $FreeBSD$ */
+
+int decode(FILE *fd, char *buffer, int len);
diff --git a/usr.sbin/vidcontrol/path.h b/usr.sbin/vidcontrol/path.h
new file mode 100644
index 0000000..709acbc
--- /dev/null
+++ b/usr.sbin/vidcontrol/path.h
@@ -0,0 +1,4 @@
+#define KEYMAP_PATH "/usr/share/syscons/keymaps/"
+#define FONT_PATH "/usr/share/syscons/fonts/"
+#define SCRNMAP_PATH "/usr/share/syscons/scrnmaps/"
+
diff --git a/usr.sbin/vidcontrol/vidcontrol.1 b/usr.sbin/vidcontrol/vidcontrol.1
new file mode 100644
index 0000000..332643c
--- /dev/null
+++ b/usr.sbin/vidcontrol/vidcontrol.1
@@ -0,0 +1,512 @@
+.\"
+.\" vidcontrol - a utility for manipulating the syscons video driver
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" @(#)vidcontrol.1
+.\" $FreeBSD$
+.\"
+.Dd May 27, 2001
+.Dt VIDCONTROL 1
+.Os
+.Sh NAME
+.Nm vidcontrol
+.Nd system console control and configuration utility
+.Sh SYNOPSIS
+.Nm
+.Op Fl CdLPpx
+.Op Fl b Ar color
+.Op Fl c Ar appearance
+.Oo
+.Fl f
+.Op Ar size
+.Ar file
+.Oc
+.Op Fl g Ar geometry
+.Op Fl h Ar size
+.Op Fl i Cm adapter | mode
+.Op Fl l Ar screen_map
+.Op Fl M Ar char
+.Op Fl m Cm on | off
+.Op Fl r Ar foreground Ar background
+.Op Fl s Ar number
+.Op Fl t Ar N | Cm off
+.Op Ar mode
+.Op Ar foreground Op Ar background
+.Op Cm show
+.Sh DESCRIPTION
+The
+.Nm
+command is used to set various options for the
+.Xr syscons 4
+console driver,
+such as video mode, colors, cursor shape, screen output map, font and screen
+saver timeout.
+.Pp
+The following command line options are supported:
+.Bl -tag -width indent
+.It Ar mode
+Select a new video mode.
+The modes currently recognized are:
+.Ar 80x25 ,
+.Ar 80x30 ,
+.Ar 80x43 ,
+.Ar 80x50 ,
+.Ar 80x60 ,
+.Ar 132x25 ,
+.Ar 132x30 ,
+.Ar 132x43 ,
+.Ar 132x50 ,
+.Ar 132x60 ,
+.Ar VGA_40x25 ,
+.Ar VGA_80x25 ,
+.Ar VGA_80x30 ,
+.Ar VGA_80x50 ,
+.Ar VGA_80x60 ,
+.Ar VGA_90x25 ,
+.Ar VGA_90x30 ,
+.Ar VGA_90x43 ,
+.Ar VGA_90x50 ,
+.Ar VGA_90x60 ,
+.Ar EGA_80x25 ,
+.Ar EGA_80x43 ,
+.Ar VESA_132x25 ,
+.Ar VESA_132x43 ,
+.Ar VESA_132x50 ,
+.Ar VESA_132x60 .
+.\"The graphic mode
+.\".Ar VGA_320x200
+.\"and
+The raster text mode
+.Ar VESA_800x600
+can also be chosen.
+See
+.Sx Video Mode Support
+below.
+.It Ar foreground Op Ar background
+Change colors when displaying text.
+Specify the foreground color
+(e.g.\&
+.Dq vidcontrol white ) ,
+or both a foreground and background colors
+(e.g.\&
+.Dq vidcontrol yellow blue ) .
+Use the
+.Cm show
+command below to see available colors.
+.It Cm show
+See the supported colors on a given platform.
+.It Fl b Ar color
+Set border color to
+.Ar color .
+This option may not be always supported by the video driver.
+.It Fl C
+Clear the history buffer.
+.It Fl c Cm normal | blink | destructive
+Change the cursor appearance.
+The cursor is either an inverting block
+.Pq Cm normal
+that can optionally
+.Cm blink ,
+or it can be like the old hardware cursor
+.Pq Cm destructive .
+The latter is actually a simulation.
+.It Fl d
+Print out current output screen map.
+.It Xo
+.Fl f
+.Op Ar size
+.Ar file
+.Xc
+Load font
+.Ar file
+for
+.Ar size
+(currently, only
+.Cm 8x8 ,
+.Cm 8x14
+or
+.Cm 8x16 ) .
+The font file can be either uuencoded or in raw binary format.
+You can also use the menu-driven
+.Xr vidfont 1
+command to load the font of your choice.
+.Pp
+.Ar Size
+may be omitted, in this case
+.Nm
+will try to guess it from the size of font file.
+.Pp
+Note that older video cards, such as MDA and CGA, do not support
+software font.
+See also
+.Sx Video Mode Support
+and
+.Sx EXAMPLES
+below and the man page for
+.Xr syscons 4 .
+.It Fl g Ar geometry
+Set the
+.Ar geometry
+of the text mode for the modes with selectable
+geometry.
+Currently only raster modes, such as
+.Ar VESA_800x600 ,
+support this option.
+See also
+.Sx Video Mode Support
+and
+.Sx EXAMPLES
+below.
+.It Fl h Ar size
+Set the size of the history (scrollback) buffer to
+.Ar size
+lines.
+.It Fl i Cm adapter
+Shows info about the current video adapter.
+.It Fl i Cm mode
+Shows the possible video modes with the current video hardware.
+.It Fl l Ar screen_map
+Install screen output map file from
+.Ar screen_map .
+See also
+.Xr syscons 4 .
+.It Fl L
+Install default screen output map.
+.It Fl M Ar char
+Sets the base character used to render the mouse pointer to
+.Ar char .
+.It Fl m Cm on | off
+Switch the mouse pointer
+.Cm on
+or
+.Cm off .
+Used together with the
+.Xr moused 8
+daemon for text mode cut & paste functionality.
+.It Fl p
+Capture the current contents of the video buffer corresponding
+to the terminal device referred to by standard input.
+The
+.Nm
+utility writes contents of the video buffer to the standard
+output in a raw binary format.
+For details about that
+format see
+.Sx Format of Video Buffer Dump
+below.
+.It Fl P
+Same as
+.Fl p ,
+but dump contents of the video buffer in a plain text format
+ignoring nonprintable characters and information about text
+attributes.
+.It Fl r Ar foreground background
+Change reverse mode colors to
+.Ar foreground
+and
+.Ar background .
+.It Fl s Ar number
+Set the current vty to
+.Ar number .
+.It Fl t Ar N | Cm off
+Set the screensaver timeout to
+.Ar N
+seconds, or turns it
+.Cm off .
+.It Fl x
+Use hexadecimal digits for output.
+.El
+.Ss Video Mode Support
+Note that not all modes listed above may be supported by the video
+hardware.
+You can verify which mode is supported by the video hardware, using the
+.Fl i Cm mode
+option.
+.Pp
+The VESA BIOS support must be linked to the kernel
+or loaded as a KLD module if you wish to use VESA video modes
+or 132 column modes
+(see
+.Xr vga 4 ) .
+.Pp
+You need to compile your kernel with the
+.Ar VGA_WIDTH90
+option if you wish to use VGA 90 column modes
+(see
+.Xr vga 4 ) .
+.Pp
+Video modes other than 25 and 30 line modes may require specific size of font.
+Use
+.Fl f
+option above to load a font file to the kernel.
+If the required size of font has not been loaded to the kernel,
+.Nm
+will fail if the user attempts to set a new video mode.
+.Pp
+.Bl -column "25 line modes" "8x16 (VGA), 8x14 (EGA)" -compact
+.Sy Modes Ta Sy Font size
+.Li 25 line modes Ta 8x16 (VGA), 8x14 (EGA)
+.Li 30 line modes Ta 8x16
+.Li 43 line modes Ta 8x8
+.Li 50 line modes Ta 8x8
+.Li 60 line modes Ta 8x8
+.El
+.Pp
+It is better to always load all three sizes (8x8, 8x14 and 8x16)
+of the same font.
+.Pp
+You may set variables in
+.Pa /etc/rc.conf
+or
+.Pa /etc/rc.conf.local
+so that desired font files will be automatically loaded
+when the system starts up.
+See below.
+.Pp
+If you want to use the raster text mode
+.Ar VESA_800x600 ,
+you need to recompile your kernel with the
+.Dv SC_PIXEL_MODE
+option.
+See
+.Xr syscons 4
+for more details on this kernel option.
+.Ss Format of Video Buffer Dump
+The
+.Nm
+utility uses the
+.Xr syscons 4
+.Dv CONS_SCRSHOT
+.Xr ioctl 2
+to capture the current contents of the video buffer.
+The
+.Nm
+utility writes version and additional information to the standard
+output, followed by the contents of the terminal device.
+.Pp
+VGA video memory is typically arranged in two byte tuples,
+one per character position.
+In each tuple, the first byte will be the character code,
+and the second byte is the character's color attribute.
+.Pp
+The VGA color attribute byte looks like this:
+.Pp
+.Bl -column "X:X" "<00000000>" "width" "bright foreground color"
+.Sy "bits# width meaning"
+.Li "7 <X0000000> 1 character blinking"
+.Li "6:4 <0XXX0000> 3 background color"
+.Li "3 <0000X000> 1 bright foreground color"
+.Li "2:0 <00000XXX> 3 foreground color"
+.El
+.Pp
+Here is a list of the three bit wide base colors:
+.Pp
+.Bl -hang -offset indent -compact
+.It 0
+Black
+.It 1
+Blue
+.It 2
+Green
+.It 3
+Cyan
+.It 4
+Red
+.It 5
+Magenta
+.It 6
+Brown
+.It 7
+Light Grey
+.El
+.Pp
+Base colors with bit 3 (the bright foreground flag) set:
+.Pp
+.Bl -hang -offset indent -compact
+.It 0
+Dark Grey
+.It 1
+Light Blue
+.It 2
+Light Green
+.It 3
+Light Cyan
+.It 4
+Light Red
+.It 5
+Light Magenta
+.It 6
+Yellow
+.It 7
+White
+.El
+.Pp
+For example, the two bytes
+.Pp
+.Dl "65 158"
+.Pp
+specify an uppercase A (character code 65), blinking
+(bit 7 set) in yellow (bits 3:0) on a blue background
+(bits 6:4).
+.Pp
+The
+.Nm
+output contains a small header which includes additional
+information which may be useful to utilities processing
+the output.
+.Pp
+The first 10 bytes are always arranged as follows:
+.Bl -column "Byte range" "Contents" -offset indent
+.It Sy "Byte Range Contents"
+.It "1 thru 8 Literal text" Dq Li SCRSHOT_
+.It "9 File format version number"
+.It "10 Remaining number of bytes in the header"
+.El
+.Pp
+Subsequent bytes depend on the version number.
+.Bl -column "Version" "13 and up" -offset indent
+.It Sy "Version Byte Meaning"
+.It "1 11 Terminal width, in characters"
+.It " 12 Terminal depth, in characters"
+.It " 13 and up The snapshot data"
+.El
+.Pp
+So a dump of an 80x25 screen would start (in hex)
+.Bd -literal -offset indent
+53 43 52 53 48 4f 54 5f 01 02 50 19
+----------------------- -- -- -- --
+ | | | | ` 25 decimal
+ | | | `--- 80 decimal
+ | | `------ 2 remaining bytes of header data
+ | `--------- File format version 1
+ `------------------------ Literal "SCRSHOT_"
+.Ed
+.Sh VIDEO OUTPUT CONFIGURATION
+.Ss Boot Time Configuration
+You may set the following variables in
+.Pa /etc/rc.conf
+or
+.Pa /etc/rc.conf.local
+in order to configure the video output at boot time.
+.Pp
+.Bl -tag -width foo_bar_var -compact
+.It Ar blanktime
+Sets the timeout value for the
+.Fl t
+option.
+.It Ar font8x16 , font8x14 , font8x8
+Specifies font files for the
+.Fl f
+option.
+.It Ar scrnmap
+Specifies a screen output map file for the
+.Fl l
+option.
+.El
+.Pp
+See
+.Xr rc.conf 5
+for more details.
+.Ss Driver Configuration
+The video card driver may let you change default configuration
+options, such as the default font, so that you do not need to set up
+the options at boot time.
+See video card driver manuals, (e.g.\&
+.Xr vga 4 )
+for details.
+.Sh FILES
+.Bl -tag -width /usr/share/syscons/scrnmaps/foo-bar -compact
+.It Pa /usr/share/syscons/fonts/*
+font files.
+.It Pa /usr/share/syscons/scrnmaps/*
+screen output map files.
+.El
+.Sh EXAMPLES
+If you want to load
+.Pa /usr/share/syscons/fonts/iso-8x16.fnt
+to the kernel, run
+.Nm
+as:
+.Pp
+.Dl vidcontrol -f 8x16 /usr/share/syscons/fonts/iso-8x16.fnt
+.Pp
+So long as the font file is in
+.Pa /usr/share/syscons/fonts ,
+you may abbreviate the file name as
+.Pa iso-8x16 :
+.Pp
+.Dl vidcontrol -f 8x16 iso-8x16
+.Pp
+Furthermore, you can also omit font size
+.Dq Li 8x16 :
+.Pp
+.Dl vidcontrol -f iso-8x16
+.Pp
+Moreover, the suffix specifying the font size can be also omitted; in
+this case,
+.Nm
+will use the size of the currently displayed font to construct the
+suffix:
+.Pp
+.Dl vidcontrol -f iso
+.Pp
+Likewise, you can also abbreviate the screen output map file name for
+the
+.Fl l
+option if the file is found in
+.Pa /usr/share/syscons/scrnmaps .
+.Pp
+.Dl vidcontrol -l iso-8859-1_to_cp437
+.Pp
+The above command will load
+.Pa /usr/share/syscons/scrnmaps/iso-8859-1_to_cp437.scm .
+.Pp
+The following command will set-up a 100x37 raster text mode (useful for
+some LCD models):
+.Pp
+.Dl vidcontrol -g 100x37 VESA_800x600
+.Pp
+The following command will capture the contents of the first virtual
+terminal, and redirect the output to the
+.Pa shot.scr
+file:
+.Pp
+.Dl vidcontrol -p < /dev/ttyv0 > shot.scr
+.Pp
+The following command will dump contents of the fourth virtual terminal
+to the standard output in the human readable format:
+.Pp
+.Dl vidcontrol -P < /dev/ttyv3
+.Sh SEE ALSO
+.Xr kbdcontrol 1 ,
+.Xr vidfont 1 ,
+.Xr keyboard 4 ,
+.Xr screen 4 ,
+.Xr syscons 4 ,
+.Xr vga 4 ,
+.Xr rc.conf 5 ,
+.Xr kldload 8 ,
+.Xr moused 8 ,
+.Xr watch 8
+.Pp
+The various
+.Li scr2*
+utilities in the
+.Li graphics
+and
+.Li textproc
+categories of the
+.Em "Ports Collection" .
+.Sh AUTHORS
+.An S\(/oren Schmidt Aq sos@FreeBSD.org
+.Sh CONTRIBUTORS
+.An Maxim Sobolev Aq sobomax@FreeBSD.org ,
+.An Nik Clayton Aq nik@FreeBSD.org
diff --git a/usr.sbin/vidcontrol/vidcontrol.c b/usr.sbin/vidcontrol/vidcontrol.c
new file mode 100644
index 0000000..3bc73d1
--- /dev/null
+++ b/usr.sbin/vidcontrol/vidcontrol.c
@@ -0,0 +1,831 @@
+/*-
+ * Copyright (c) 1994-1996 Søren Schmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <ctype.h>
+#include <err.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/fbio.h>
+#include <sys/consio.h>
+#include <sys/errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "path.h"
+#include "decode.h"
+
+#define _VESA_800x600_DFL_COLS 80
+#define _VESA_800x600_DFL_ROWS 25
+#define _VESA_800x600_DFL_FNSZ 16
+
+#define DUMP_RAW 0
+#define DUMP_TXT 1
+
+#define DUMP_FMT_REV 1
+
+char legal_colors[16][16] = {
+ "black", "blue", "green", "cyan",
+ "red", "magenta", "brown", "white",
+ "grey", "lightblue", "lightgreen", "lightcyan",
+ "lightred", "lightmagenta", "yellow", "lightwhite"
+};
+int hex = 0;
+int number;
+int vesa_cols = _VESA_800x600_DFL_COLS;
+int vesa_rows = _VESA_800x600_DFL_ROWS;
+char letter;
+struct vid_info info;
+
+
+static void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n%s\n%s\n",
+"usage: vidcontrol [-CdLPpx] [-b color] [-c appearance] [-f [size] file]",
+" [-g geometry] [-h size] [-i adapter | mode] [-l screen_map]",
+" [-m on | off] [-M char] [-r foreground background] [-s num]",
+" [-t N | off] [mode] [foreground [background]] [show]");
+ exit(1);
+}
+
+char *
+nextarg(int ac, char **av, int *indp, int oc, int strict)
+{
+ if (*indp < ac)
+ return(av[(*indp)++]);
+ if (strict != 0)
+ errx(1, "option requires two arguments -- %c", oc);
+ return(NULL);
+}
+
+FILE *
+openguess(char *a[], char *b[], char *c[], char *d[], char **name)
+{
+ FILE *f;
+ int i, j, k, l;
+
+ for (i = 0; a[i] != NULL; i++) {
+ for (j = 0; b[j] != NULL; j++) {
+ for (k = 0; c[k] != NULL; k++) {
+ for (l = 0; d[l] != NULL; l++) {
+ asprintf(name, "%s%s%s%s", a[i], b[j],
+ c[k], d[l]);
+ f = fopen(*name, "r");
+ if (f != NULL)
+ return (f);
+ free(*name);
+ }
+ }
+ }
+ }
+ return (NULL);
+}
+
+void
+load_scrnmap(char *filename)
+{
+ FILE *fd;
+ int size;
+ char *name;
+ scrmap_t scrnmap;
+ char *a[] = {"", SCRNMAP_PATH, NULL};
+ char *b[] = {filename, NULL};
+ char *c[] = {"", ".scm", NULL};
+ char *d[] = {"", NULL};
+
+ fd = openguess(a, b, c, d, &name);
+ if (fd == NULL) {
+ warn("screenmap file not found");
+ return;
+ }
+ size = sizeof(scrnmap);
+ if (decode(fd, (char *)&scrnmap, size) != size) {
+ rewind(fd);
+ if (fread(&scrnmap, 1, size, fd) != size) {
+ warnx("bad screenmap file");
+ fclose(fd);
+ return;
+ }
+ }
+ if (ioctl(0, PIO_SCRNMAP, &scrnmap) < 0)
+ warn("can't load screenmap");
+ fclose(fd);
+}
+
+void
+load_default_scrnmap()
+{
+ scrmap_t scrnmap;
+ int i;
+
+ for (i=0; i<256; i++)
+ *((char*)&scrnmap + i) = i;
+ if (ioctl(0, PIO_SCRNMAP, &scrnmap) < 0)
+ warn("can't load default screenmap");
+}
+
+void
+print_scrnmap()
+{
+ unsigned char map[256];
+ int i;
+
+ if (ioctl(0, GIO_SCRNMAP, &map) < 0) {
+ warn("getting screenmap");
+ return;
+ }
+ for (i=0; i<sizeof(map); i++) {
+ if (i > 0 && i % 16 == 0)
+ fprintf(stdout, "\n");
+ if (hex)
+ fprintf(stdout, " %02x", map[i]);
+ else
+ fprintf(stdout, " %03d", map[i]);
+ }
+ fprintf(stdout, "\n");
+
+}
+
+int
+fsize(FILE *file)
+{
+ struct stat sb;
+
+ if (fstat(fileno(file), &sb) == 0)
+ return sb.st_size;
+ else
+ return -1;
+}
+
+#define DATASIZE(x) ((x).w * (x).h * 256 / 8)
+
+void
+load_font(char *type, char *filename)
+{
+ FILE *fd;
+ int h, i, size, w;
+ unsigned long io = 0; /* silence stupid gcc(1) in the Wall mode */
+ char *name, *fontmap, size_sufx[6];
+ char *a[] = {"", FONT_PATH, NULL};
+ char *b[] = {filename, NULL};
+ char *c[] = {"", size_sufx, NULL};
+ char *d[] = {"", ".fnt", NULL};
+ vid_info_t info;
+
+ struct sizeinfo {
+ int w;
+ int h;
+ unsigned long io;
+ } sizes[] = {{8, 16, PIO_FONT8x16},
+ {8, 14, PIO_FONT8x14},
+ {8, 8, PIO_FONT8x8},
+ {0, 0, 0}};
+
+ info.size = sizeof(info);
+ if (ioctl(0, CONS_GETINFO, &info) == -1) {
+ warn("failed to obtain current video mode parameters");
+ return;
+ }
+ snprintf(size_sufx, sizeof(size_sufx), "-8x%d", info.font_size);
+ fd = openguess(a, b, c, d, &name);
+ if (fd == NULL) {
+ warn("%s: can't load font file", filename);
+ return;
+ }
+ if (type != NULL) {
+ size = 0;
+ if (sscanf(type, "%dx%d", &w, &h) == 2)
+ for (i = 0; sizes[i].w != 0; i++)
+ if (sizes[i].w == w && sizes[i].h == h) {
+ size = DATASIZE(sizes[i]);
+ io = sizes[i].io;
+ }
+
+ if (size == 0) {
+ warnx("%s: bad font size specification", type);
+ fclose(fd);
+ return;
+ }
+ } else {
+ /* Apply heuristics */
+ int j;
+ int dsize[2];
+
+ size = DATASIZE(sizes[0]);
+ fontmap = (char*) malloc(size);
+ dsize[0] = decode(fd, fontmap, size);
+ dsize[1] = fsize(fd);
+ free(fontmap);
+
+ size = 0;
+ for (j = 0; j < 2; j++)
+ for (i = 0; sizes[i].w != 0; i++)
+ if (DATASIZE(sizes[i]) == dsize[j]) {
+ size = dsize[j];
+ io = sizes[i].io;
+ j = 2; /* XXX */
+ break;
+ }
+
+ if (size == 0) {
+ warnx("%s: can't guess font size", filename);
+ fclose(fd);
+ return;
+ }
+ rewind(fd);
+ }
+
+ fontmap = (char*) malloc(size);
+ if (decode(fd, fontmap, size) != size) {
+ rewind(fd);
+ if (fsize(fd) != size || fread(fontmap, 1, size, fd) != size) {
+ warnx("%s: bad font file", filename);
+ fclose(fd);
+ free(fontmap);
+ return;
+ }
+ }
+ if (ioctl(0, io, fontmap) < 0)
+ warn("can't load font");
+ fclose(fd);
+ free(fontmap);
+}
+
+void
+set_screensaver_timeout(char *arg)
+{
+ int nsec;
+
+ if (!strcmp(arg, "off"))
+ nsec = 0;
+ else {
+ nsec = atoi(arg);
+ if ((*arg == '\0') || (nsec < 1)) {
+ warnx("argument must be a positive number");
+ return;
+ }
+ }
+ if (ioctl(0, CONS_BLANKTIME, &nsec) == -1)
+ warn("setting screensaver period");
+}
+
+void
+set_cursor_type(char *appearence)
+{
+ int type;
+
+ if (!strcmp(appearence, "normal"))
+ type = 0;
+ else if (!strcmp(appearence, "blink"))
+ type = 1;
+ else if (!strcmp(appearence, "destructive"))
+ type = 3;
+ else {
+ warnx("argument to -c must be normal, blink or destructive");
+ return;
+ }
+ ioctl(0, CONS_CURSORTYPE, &type);
+}
+
+void
+video_mode(int argc, char **argv, int *index)
+{
+ static struct {
+ char *name;
+ unsigned long mode;
+ } modes[] = {
+ { "80x25", SW_TEXT_80x25 },
+ { "80x30", SW_TEXT_80x30 },
+ { "80x43", SW_TEXT_80x43 },
+ { "80x50", SW_TEXT_80x50 },
+ { "80x60", SW_TEXT_80x60 },
+ { "132x25", SW_TEXT_132x25 },
+ { "132x30", SW_TEXT_132x30 },
+ { "132x43", SW_TEXT_132x43 },
+ { "132x50", SW_TEXT_132x50 },
+ { "132x60", SW_TEXT_132x60 },
+ { "VGA_40x25", SW_VGA_C40x25 },
+ { "VGA_80x25", SW_VGA_C80x25 },
+ { "VGA_80x30", SW_VGA_C80x30 },
+ { "VGA_80x50", SW_VGA_C80x50 },
+ { "VGA_80x60", SW_VGA_C80x60 },
+#ifdef SW_VGA_C90x25
+ { "VGA_90x25", SW_VGA_C90x25 },
+ { "VGA_90x30", SW_VGA_C90x30 },
+ { "VGA_90x43", SW_VGA_C90x43 },
+ { "VGA_90x50", SW_VGA_C90x50 },
+ { "VGA_90x60", SW_VGA_C90x60 },
+#endif
+ { "VGA_320x200", SW_VGA_CG320 },
+ { "EGA_80x25", SW_ENH_C80x25 },
+ { "EGA_80x43", SW_ENH_C80x43 },
+ { "VESA_132x25", SW_VESA_C132x25 },
+ { "VESA_132x43", SW_VESA_C132x43 },
+ { "VESA_132x50", SW_VESA_C132x50 },
+ { "VESA_132x60", SW_VESA_C132x60 },
+ { "VESA_800x600", SW_VESA_800x600 },
+ { NULL },
+ };
+ unsigned long mode = 0;
+ int cur_mode;
+ int ioerr;
+ int size[3];
+ int i;
+
+ if (ioctl(0, CONS_GET, &cur_mode) < 0)
+ err(1, "cannot get the current video mode");
+ if (*index < argc) {
+ for (i = 0; modes[i].name != NULL; ++i) {
+ if (!strcmp(argv[*index], modes[i].name)) {
+ mode = modes[i].mode;
+ break;
+ }
+ }
+ if (modes[i].name == NULL)
+ return;
+ if (ioctl(0, mode, NULL) < 0)
+ warn("cannot set videomode");
+ if (mode == SW_VESA_800x600) {
+ /* columns */
+ if ((vesa_cols * 8 > 800) || (vesa_cols <= 0)) {
+ warnx("incorrect number of columns: %d",
+ vesa_cols);
+ size[0] = _VESA_800x600_DFL_COLS;
+ } else {
+ size[0] = vesa_cols;
+ }
+ /* rows */
+ if ((vesa_rows * _VESA_800x600_DFL_FNSZ > 600) ||
+ (vesa_rows <=0)) {
+ warnx("incorrect number of rows: %d",
+ vesa_rows);
+ size[1] = _VESA_800x600_DFL_ROWS;
+ } else {
+ size[1] = vesa_rows;
+ }
+ /* font size */
+ size[2] = _VESA_800x600_DFL_FNSZ;
+ if (ioctl(0, KDRASTER, size)) {
+ ioerr = errno;
+ if (cur_mode >= M_VESA_BASE)
+ ioctl(0,
+ _IO('V', cur_mode - M_VESA_BASE),
+ NULL);
+ else
+ ioctl(0, _IO('S', cur_mode), NULL);
+ warnc(ioerr, "cannot activate raster display");
+ }
+ }
+ (*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 > 16) {
+ warnx("console number out of range");
+ } else if (ioctl(0, VT_ACTIVATE, (caddr_t) (long) n) == -1)
+ warn("ioctl(VT_ACTIVATE)");
+}
+
+void
+set_border_color(char *arg)
+{
+ int color;
+
+ if ((color = get_color_number(arg)) != -1) {
+ fprintf(stderr, "[=%dA", color);
+ }
+ else
+ usage();
+}
+
+void
+set_mouse_char(char *arg)
+{
+ struct mouse_info mouse;
+ long l;
+
+ l = strtol(arg, NULL, 0);
+ if ((l < 0) || (l > UCHAR_MAX - 3)) {
+ warnx("argument to -M must be 0 through %d", UCHAR_MAX - 3);
+ return;
+ }
+ mouse.operation = MOUSE_MOUSECHAR;
+ mouse.u.mouse_char = (int)l;
+ ioctl(0, CONS_MOUSECTL, &mouse);
+}
+
+void
+set_mouse(char *arg)
+{
+ struct mouse_info mouse;
+
+ if (!strcmp(arg, "on"))
+ mouse.operation = MOUSE_SHOW;
+ else if (!strcmp(arg, "off"))
+ mouse.operation = MOUSE_HIDE;
+ else {
+ warnx("argument to -m must 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" },
+ { KD_TGA, "TGA" },
+ { -1, "Unknown" },
+ };
+ int i;
+
+ for (i = 0; names[i].type != -1; ++i)
+ if (names[i].type == type)
+ break;
+ return names[i].name;
+}
+
+void
+show_adapter_info(void)
+{
+ struct video_adapter_info ad;
+
+ ad.va_index = 0;
+ if (ioctl(0, CONS_ADPINFO, &ad)) {
+ warn("failed to obtain adapter information");
+ return;
+ }
+
+ printf("fb%d:\n", ad.va_index);
+ printf(" %.*s%d, type:%s%s (%d), flags:0x%x\n",
+ (int)sizeof(ad.va_name), ad.va_name, ad.va_unit,
+ (ad.va_flags & V_ADP_VESA) ? "VESA " : "",
+ adapter_name(ad.va_type), ad.va_type, ad.va_flags);
+ printf(" initial mode:%d, current mode:%d, BIOS mode:%d\n",
+ ad.va_initial_mode, ad.va_mode, ad.va_initial_bios_mode);
+ printf(" frame buffer window:0x%x, buffer size:0x%x\n",
+ ad.va_window, ad.va_buffer_size);
+ printf(" window size:0x%x, origin:0x%x\n",
+ ad.va_window_size, ad.va_window_orig);
+ printf(" display start address (%d, %d), scan line width:%d\n",
+ ad.va_disp_start.x, ad.va_disp_start.y, ad.va_line_width);
+ printf(" reserved:0x%x\n", ad.va_unused0);
+}
+
+void
+show_mode_info(void)
+{
+ struct video_info info;
+ char buf[80];
+ int mode;
+ int c;
+
+ printf(" mode# flags type size "
+ "font window linear buffer\n");
+ printf("---------------------------------------"
+ "---------------------------------------\n");
+ for (mode = 0; mode < M_VESA_MODE_MAX; ++mode) {
+ info.vi_mode = mode;
+ if (ioctl(0, CONS_MODEINFO, &info))
+ continue;
+ if (info.vi_mode != mode)
+ continue;
+
+ printf("%3d (0x%03x)", mode, mode);
+ printf(" 0x%08x", info.vi_flags);
+ if (info.vi_flags & V_INFO_GRAPHICS) {
+ c = 'G';
+ snprintf(buf, sizeof(buf), "%dx%dx%d %d",
+ info.vi_width, info.vi_height,
+ info.vi_depth, info.vi_planes);
+ } else {
+ c = 'T';
+ snprintf(buf, sizeof(buf), "%dx%d",
+ info.vi_width, info.vi_height);
+ }
+ printf(" %c %-15s", c, buf);
+ snprintf(buf, sizeof(buf), "%dx%d",
+ info.vi_cwidth, info.vi_cheight);
+ printf(" %-5s", buf);
+ printf(" 0x%05x %2dk %2dk",
+ info.vi_window, (int)info.vi_window_size/1024,
+ (int)info.vi_window_gran/1024);
+ printf(" 0x%08x %dk\n",
+ info.vi_buffer, (int)info.vi_buffer_size/1024);
+ }
+}
+
+void
+show_info(char *arg)
+{
+ if (!strcmp(arg, "adapter"))
+ show_adapter_info();
+ else if (!strcmp(arg, "mode"))
+ show_mode_info();
+ else {
+ warnx("argument to -i must either adapter or mode");
+ return;
+ }
+}
+
+void
+test_frame()
+{
+ int i;
+
+ fprintf(stdout, "[=0G\n\n");
+ for (i=0; i<8; i++) {
+ fprintf(stdout, "[=15F[=0G %2d [=%dF%-16s"
+ "[=15F[=0G %2d [=%dF%-16s "
+ "[=15F %2d [=%dGBACKGROUND[=0G\n",
+ i, i, legal_colors[i], i+8, i+8,
+ legal_colors[i+8], i, i);
+ }
+ fprintf(stdout, "[=%dF[=%dG[=%dH[=%dI\n",
+ info.mv_norm.fore, info.mv_norm.back,
+ info.mv_rev.fore, info.mv_rev.back);
+}
+
+/*
+ * Snapshot the video memory of that terminal, using the CONS_SCRSHOT
+ * ioctl, and writes the results to stdout either in the special
+ * binary format (see manual page for details), or in the plain
+ * text format.
+ */
+void
+dump_screen(int mode)
+{
+ scrshot_t shot;
+ vid_info_t info;
+
+ info.size = sizeof(info);
+ if (ioctl(0, CONS_GETINFO, &info) == -1) {
+ warn("failed to obtain current video mode parameters");
+ return;
+ }
+
+ shot.buf = alloca(info.mv_csz * info.mv_rsz * sizeof(u_int16_t));
+ if (shot.buf == NULL) {
+ warn("failed to allocate memory for dump");
+ return;
+ }
+
+ shot.xsize = info.mv_csz;
+ shot.ysize = info.mv_rsz;
+ if (ioctl(0, CONS_SCRSHOT, &shot) == -1) {
+ warn("failed to get dump of the screen");
+ return;
+ }
+
+ if (mode == DUMP_RAW) {
+ printf("SCRSHOT_%c%c%c%c", DUMP_FMT_REV, 2,
+ shot.xsize, shot.ysize);
+ fflush(stdout);
+
+ (void)write(STDOUT_FILENO, shot.buf,
+ shot.xsize * shot.ysize * sizeof(u_int16_t));
+ } else {
+ char *line;
+ int x, y;
+ u_int16_t ch;
+
+ line = alloca(shot.xsize + 1);
+ if (line == NULL) {
+ warn("failed to allocate memory for line buffer");
+ return;
+ }
+
+ for (y = 0; y < shot.ysize; y++) {
+ for (x = 0; x < shot.xsize; x++) {
+ ch = shot.buf[x + (y * shot.xsize)];
+ ch &= 0xff;
+ if (isprint(ch) == 0)
+ ch = ' ';
+ line[x] = (char)ch;
+ }
+
+ /* Trim trailing spaces */
+ do {
+ line[x--] = '\0';
+ } while (line[x] == ' ' && x != 0);
+
+ puts(line);
+ }
+ fflush(stdout);
+ }
+
+ return;
+}
+
+void
+set_history(char *opt)
+{
+ int size;
+
+ size = atoi(opt);
+ if ((*opt == '\0') || size < 0) {
+ warnx("argument must be a positive number");
+ return;
+ }
+ if (ioctl(0, CONS_HISTORY, &size) == -1)
+ warn("setting history buffer size");
+}
+
+void
+clear_history()
+{
+
+ if (ioctl(0, CONS_CLRHIST) == -1)
+ warn("clear history buffer");
+}
+
+int
+main(int argc, char **argv)
+{
+ char *font, *type;
+ int opt;
+
+
+ info.size = sizeof(info);
+ if (argc == 1)
+ usage();
+ /* Not reached */
+ if (ioctl(0, CONS_GETINFO, &info) < 0)
+ err(1, "must be on a virtual console");
+ while((opt = getopt(argc, argv, "b:Cc:df:g:h:i:l:LM:m:pPr:s:t:x")) != -1)
+ switch(opt) {
+ case 'b':
+ set_border_color(optarg);
+ break;
+ case 'C':
+ clear_history();
+ break;
+ case 'c':
+ set_cursor_type(optarg);
+ break;
+ case 'd':
+ print_scrnmap();
+ break;
+ case 'f':
+ type = optarg;
+ font = nextarg(argc, argv, &optind, 'f', 0);
+ if (font == NULL) {
+ type = NULL;
+ font = optarg;
+ }
+ load_font(type, font);
+ break;
+ case 'g':
+ if (sscanf(optarg, "%dx%d", &vesa_cols,
+ &vesa_rows) != 2) {
+ warnx("incorrect geometry: %s", optarg);
+ usage();
+ }
+ break;
+ case 'h':
+ set_history(optarg);
+ break;
+ case 'i':
+ show_info(optarg);
+ break;
+ case 'l':
+ load_scrnmap(optarg);
+ break;
+ case 'L':
+ load_default_scrnmap();
+ break;
+ case 'M':
+ set_mouse_char(optarg);
+ break;
+ case 'm':
+ set_mouse(optarg);
+ break;
+ case 'p':
+ dump_screen(DUMP_RAW);
+ break;
+ case 'P':
+ dump_screen(DUMP_TXT);
+ 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..1dd091a
--- /dev/null
+++ b/usr.sbin/vipw/Makefile
@@ -0,0 +1,10 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+PROG= vipw
+MAN= vipw.8
+DPADD= ${LIBUTIL}
+LDADD= -lutil
+WARNS?= 4
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/vipw/vipw.8 b/usr.sbin/vipw/vipw.8
new file mode 100644
index 0000000..a4f704a
--- /dev/null
+++ b/usr.sbin/vipw/vipw.8
@@ -0,0 +1,109 @@
+.\" Copyright (c) 1983, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)vipw.8 8.1 (Berkeley) 6/6/93
+.\" $FreeBSD$
+.\"
+.Dd June 6, 1993
+.Dt VIPW 8
+.Os
+.Sh NAME
+.Nm vipw
+.Nd edit the password file
+.Sh SYNOPSIS
+.Nm
+.Op Fl d Ar directory
+.Sh DESCRIPTION
+.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
+When run without options,
+.Nm
+will work with the password files in
+.Pa /etc .
+The
+.Fl d
+option may be used to specify an alternative
+.Ar directory
+to work with.
+.Pp
+.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 :
+.Bl -tag -width PW_SCAN_BIG_IDS
+.It Ev EDITOR
+The editor specified by the string
+.Ev EDITOR
+will be invoked instead of the default editor
+.Xr vi 1 .
+.It Ev PW_SCAN_BIG_IDS
+See
+.Xr pwd_mkdb 8 .
+.El
+.Sh SEE ALSO
+.Xr chpass 1 ,
+.Xr passwd 1 ,
+.Xr passwd 5 ,
+.Xr adduser 8 ,
+.Xr pwd_mkdb 8
+.Sh HISTORY
+The
+.Nm
+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..5914b62
--- /dev/null
+++ b/usr.sbin/vipw/vipw.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 1987, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 2002 Networks Associates Technology, Inc.
+ * All rights reserved.
+ *
+ * Portions of this software were developed for the FreeBSD Project by
+ * ThinkSec AS and NAI Labs, the Security Research Division of Network
+ * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
+ * ("CBOSS"), as part of the DARPA CHATS research program.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#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[] =
+ "$FreeBSD$";
+#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 <libutil.h> /* must be after pwd.h */
+
+static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ const char *passwd_dir = NULL;
+ int ch, pfd, tfd;
+ char *line;
+ size_t len;
+
+ while ((ch = getopt(argc, argv, "d:")) != -1)
+ switch (ch) {
+ case 'd':
+ passwd_dir = optarg;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 0)
+ usage();
+
+ if (pw_init(passwd_dir, NULL) == -1)
+ err(1, "pw_init()");
+ if ((pfd = pw_lock()) == -1) {
+ pw_fini();
+ err(1, "pw_lock()");
+ }
+ if ((tfd = pw_tmp(pfd)) == -1) {
+ pw_fini();
+ err(1, "pw_tmp()");
+ }
+ (void)close(tfd);
+ /* Force umask for partial writes made in the edit phase */
+ (void)umask(077);
+
+ for (;;) {
+ switch (pw_edit(NULL)) {
+ case -1:
+ pw_fini();
+ err(1, "pw_edit()");
+ case 0:
+ pw_fini();
+ errx(0, "no changes made");
+ default:
+ break;
+ }
+ if (pw_mkdb(NULL) == 0) {
+ pw_fini();
+ errx(0, "password list updated");
+ }
+ printf("re-edit the password file? ");
+ fflush(stdout);
+ if ((line = fgetln(stdin, &len)) == NULL) {
+ pw_fini();
+ err(1, "fgetln()");
+ }
+ if (len > 0 && (*line == 'N' || *line == 'n'))
+ break;
+ }
+ pw_fini();
+ exit(0);
+}
+
+static void
+usage(void)
+{
+
+ (void)fprintf(stderr, "usage: vipw [-d directory]\n");
+ exit(1);
+}
diff --git a/usr.sbin/vnconfig/Makefile b/usr.sbin/vnconfig/Makefile
new file mode 100644
index 0000000..c7aa34b
--- /dev/null
+++ b/usr.sbin/vnconfig/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+PROG= vnconfig
+NOMAN= "mdconfig supplies manpage"
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/vnconfig/vnconfig.c b/usr.sbin/vnconfig/vnconfig.c
new file mode 100644
index 0000000..294250d
--- /dev/null
+++ b/usr.sbin/vnconfig/vnconfig.c
@@ -0,0 +1,25 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+int
+main(int argc, char **argv)
+{
+
+ fprintf(stderr, "ERROR: vnconfig(8) has been discontinued\n");
+ fprintf(stderr, "\tPlease use mdconfig(8).\n");
+ exit (1);
+}
diff --git a/usr.sbin/watch/Makefile b/usr.sbin/watch/Makefile
new file mode 100644
index 0000000..2718d16
--- /dev/null
+++ b/usr.sbin/watch/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+PROG= watch
+MAN= watch.8
+
+WARNS?= 2
+
+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..f855ec1
--- /dev/null
+++ b/usr.sbin/watch/watch.8
@@ -0,0 +1,113 @@
+.\"
+.\" $FreeBSD$
+.\"
+.Dd November 24, 2001
+.Dt WATCH 8
+.Os
+.Sh NAME
+.Nm watch
+.Nd snoop on another tty line
+.Sh SYNOPSIS
+.Nm
+.Op Fl cinotW
+.Op Fl f Ar snpdev
+.Op Ar tty
+.Sh DESCRIPTION
+.Nm Watch
+allows the user to examine all data coming through a specified tty
+using the
+.Xr snp 4
+device.
+If the
+.Xr snp 4
+device is not available,
+.Nm
+will attempt to load the module
+.Pq Nm snp .
+.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 f Ar snpdev
+If this option is specified,
+.Nm
+will use
+.Ar snpdev
+as the
+.Xr snp 4
+device.
+Without this option,
+.Nm
+will attempt to find the next available
+.Xr snp 4
+device.
+.It Fl i
+Force interactive mode.
+Interactive mode is a default if
+.Nm
+is started from a tty.
+If output is redirected to a file, interactive mode can still be requested
+by specifying this option.
+.It Fl n
+Disable the ability to switch the watched tty interactively. This disables
+both change requests made with <control-X> as well as automatic prompting
+when the current tty is closed or overflows. In all cases where a prompt
+would be displayed,
+.Nm
+will exit. The reconnect flags are unaffected by
+this option.
+When this flag is used, <control-X> is passed through to the terminal.
+.It Fl o
+Reconnect on overflow.
+The behavior of
+.Nm
+if the observed tty overflows is similar to the behavior if the observed tty
+is closed.
+For more info see
+.Xr snp 4 .
+.It Fl t
+Print the date and time when observation of a given tty is started.
+.It Fl W
+Allow write access to observed tty.
+.It Ar tty
+Tty may be specified as an tty-style device, such as a pseudo tty device,
+a virtual console, or a serial line, etc.
+Names may be preceded by
+.Pa /dev/ .
+.El
+.Sh OPERATION
+While running in interactive mode, all user input is discarded except for:
+.Pp
+.Bl -tag -width "XXXX" -compact
+.It Sy "<control-G>"
+Exit
+.Nm .
+.It Sy "<control-W>"
+Clear screen.
+.It Sy "<control-X>"
+Change attached tty, unless this feature is disabled, in which case
+control-X is passed to the terminal as with other control characters.
+.El
+.Sh SEE ALSO
+.Xr pty 4 ,
+.Xr sio 4 ,
+.Xr snp 4 ,
+.Xr kldload 8
+.Sh BUGS
+No terminal emulation is performed.
+All user output is reproduced as-is.
+.Sh AUTHORS
+.An Ugen J.S. Antsilevich Aq ugen@NetVision.net.il
+.Sh HISTORY
+.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..b2572ff
--- /dev/null
+++ b/usr.sbin/watch/watch.c
@@ -0,0 +1,442 @@
+/*
+ * Copyright (c) 1995 Ugen J.S.Antsilevich
+ *
+ * Redistribution and use in source forms, with and without modification,
+ * are permitted provided that this entire comment appears intact.
+ *
+ * Redistribution in binary form may occur without any restrictions.
+ * Obviously, it would be nice if you gave credit where credit is due
+ * but requiring it would be too onerous.
+ *
+ * This software is provided ``AS IS'' without any warranties of any kind.
+ *
+ * Snoop stuff.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/fcntl.h>
+#include <sys/filio.h>
+#include <sys/snoop.h>
+#include <sys/stat.h>
+#include <sys/linker.h>
+#include <sys/module.h>
+
+#include <err.h>
+#include <locale.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <termcap.h>
+#include <termios.h>
+#include <time.h>
+#include <unistd.h>
+
+#define MSG_INIT "Snoop started."
+#define MSG_OFLOW "Snoop stopped due to overflow. Reconnecting."
+#define MSG_CLOSED "Snoop stopped due to tty close. Reconnecting."
+#define MSG_CHANGE "Snoop device change by user request."
+#define MSG_NOWRITE "Snoop device change due to write failure."
+
+
+#define DEV_NAME_LEN 1024 /* for /dev/ttyXX++ */
+#define MIN_SIZE 256
+
+#define CHR_SWITCH 24 /* Ctrl+X */
+#define CHR_CLEAR 23 /* Ctrl+V */
+
+static void clear(void);
+static void timestamp(const char *);
+static void set_tty(void);
+static void unset_tty(void);
+static void fatal(int, const char *);
+static int open_snp(void);
+static void cleanup(int);
+static void usage(void) __dead2;
+static void setup_scr(void);
+static void attach_snp(void);
+static void detach_snp(void);
+static void set_dev(const char *);
+static void ask_dev(char *, const char *);
+
+int opt_reconn_close = 0;
+int opt_reconn_oflow = 0;
+int opt_interactive = 1;
+int opt_timestamp = 0;
+int opt_write = 0;
+int opt_no_switch = 0;
+const char *opt_snpdev;
+
+char dev_name[DEV_NAME_LEN];
+int snp_io;
+dev_t snp_tty;
+int std_in = 0, std_out = 1;
+
+
+int clear_ok = 0;
+struct termios otty;
+char tbuf[1024], gbuf[1024];
+
+
+static void
+clear(void)
+{
+ if (clear_ok)
+ tputs(gbuf, 1, putchar);
+ fflush(stdout);
+}
+
+static void
+timestamp(const char *buf)
+{
+ time_t t;
+ char btmp[1024];
+ clear();
+ printf("\n---------------------------------------------\n");
+ t = time(NULL);
+ strftime(btmp, 1024, "Time: %d %b %H:%M", localtime(&t));
+ printf("%s\n", btmp);
+ printf("%s\n", buf);
+ printf("---------------------------------------------\n");
+ fflush(stdout);
+}
+
+static void
+set_tty(void)
+{
+ struct termios ntty;
+
+ tcgetattr (std_in, &otty);
+ ntty = otty;
+ ntty.c_lflag &= ~ICANON; /* disable canonical operation */
+ ntty.c_lflag &= ~ECHO;
+#ifdef FLUSHO
+ ntty.c_lflag &= ~FLUSHO;
+#endif
+#ifdef PENDIN
+ ntty.c_lflag &= ~PENDIN;
+#endif
+#ifdef IEXTEN
+ ntty.c_lflag &= ~IEXTEN;
+#endif
+ ntty.c_cc[VMIN] = 1; /* minimum of one character */
+ ntty.c_cc[VTIME] = 0; /* timeout value */
+
+ ntty.c_cc[VINTR] = 07; /* ^G */
+ ntty.c_cc[VQUIT] = 07; /* ^G */
+ tcsetattr (std_in, TCSANOW, &ntty);
+}
+
+static void
+unset_tty(void)
+{
+ tcsetattr (std_in, TCSANOW, &otty);
+}
+
+
+static void
+fatal(int error, const char *buf)
+{
+ unset_tty();
+ if (buf)
+ errx(error, "fatal: %s", buf);
+ else
+ exit(error);
+}
+
+static int
+open_snp(void)
+{
+ char snp[] = {_PATH_DEV "snpX"};
+ char c;
+ int f, mode;
+
+ if (opt_write)
+ mode = O_RDWR;
+ else
+ mode = O_RDONLY;
+
+ if (opt_snpdev == NULL)
+ for (c = '0'; c <= '9'; c++) {
+ snp[8] = c;
+ if ((f = open(snp, mode)) < 0)
+ continue;
+ return f;
+ }
+ else
+ if ((f = open(opt_snpdev, mode)) != -1)
+ return (f);
+ fatal(EX_OSFILE, "cannot open snoop device");
+ return (0);
+}
+
+
+static void
+cleanup(int signo __unused)
+{
+ if (opt_timestamp)
+ timestamp("Logging Exited.");
+ close(snp_io);
+ unset_tty();
+ exit(EX_OK);
+}
+
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: watch [-ciotnW] [tty name]\n");
+ exit(EX_USAGE);
+}
+
+static void
+setup_scr(void)
+{
+ char *cbuf = gbuf, *term;
+ if (!opt_interactive)
+ return;
+ if ((term = getenv("TERM")))
+ if (tgetent(tbuf, term) == 1)
+ if (tgetstr("cl", &cbuf))
+ clear_ok = 1;
+ set_tty();
+ clear();
+}
+
+static void
+detach_snp(void)
+{
+ dev_t dev;
+
+ dev = NODEV;
+ ioctl(snp_io, SNPSTTY, &dev);
+}
+
+static void
+attach_snp(void)
+{
+ if (ioctl(snp_io, SNPSTTY, &snp_tty) != 0)
+ fatal(EX_UNAVAILABLE, "cannot attach to tty");
+ if (opt_timestamp)
+ timestamp("Logging Started.");
+}
+
+
+static void
+set_dev(const char *name)
+{
+ char buf[DEV_NAME_LEN];
+ struct stat sb;
+
+ if (strlen(name) > 5 && !strncmp(name, _PATH_DEV, sizeof _PATH_DEV - 1)) {
+ snprintf(buf, sizeof buf, "%s", name);
+ } else {
+ if (strlen(name) == 2)
+ sprintf(buf, "%s%s", _PATH_TTY, name);
+ else
+ sprintf(buf, "%s%s", _PATH_DEV, name);
+ }
+
+ if (*name == '\0' || stat(buf, &sb) < 0)
+ fatal(EX_DATAERR, "bad device name");
+
+ if ((sb.st_mode & S_IFMT) != S_IFCHR)
+ fatal(EX_DATAERR, "must be a character device");
+
+ snp_tty = sb.st_rdev;
+ attach_snp();
+}
+
+void
+ask_dev(char *dbuf, const char *msg)
+{
+ char buf[DEV_NAME_LEN];
+ int len;
+
+ clear();
+ unset_tty();
+
+ if (msg)
+ printf("%s\n", msg);
+ if (dbuf)
+ printf("Enter device name [%s]:", dbuf);
+ else
+ printf("Enter device name:");
+
+ if (fgets(buf, DEV_NAME_LEN - 1, stdin)) {
+ len = strlen(buf);
+ if (buf[len - 1] == '\n')
+ buf[len - 1] = '\0';
+ if (buf[0] != '\0' && buf[0] != ' ')
+ strcpy(dbuf, buf);
+ }
+ set_tty();
+}
+
+#define READB_LEN 5
+
+int
+main(int ac, char *av[])
+{
+ int res, idata, rv;
+ size_t nread, b_size = MIN_SIZE;
+ 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, "Wciotnf:")) != -1)
+ switch (ch) {
+ case 'W':
+ opt_write = 1;
+ break;
+ case 'c':
+ opt_reconn_close = 1;
+ break;
+ case 'i':
+ opt_interactive = 1;
+ break;
+ case 'o':
+ opt_reconn_oflow = 1;
+ break;
+ case 't':
+ opt_timestamp = 1;
+ break;
+ case 'n':
+ opt_no_switch = 1;
+ break;
+ case 'f':
+ opt_snpdev = optarg;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+
+ if (modfind("snp") == -1)
+ if (kldload("snp") == -1 || modfind("snp") == -1)
+ warn("snp module not available");
+
+ signal(SIGINT, cleanup);
+
+ setup_scr();
+ snp_io = open_snp();
+
+ if (*(av += optind) == NULL) {
+ if (opt_interactive && !opt_no_switch)
+ ask_dev(dev_name, MSG_INIT);
+ else
+ fatal(EX_DATAERR, "no device name given");
+ } else
+ strncpy(dev_name, *av, DEV_NAME_LEN);
+
+ set_dev(dev_name);
+
+ if (!(buf = (char *) malloc(b_size)))
+ fatal(EX_UNAVAILABLE, "malloc failed");
+
+ FD_ZERO(&fd_s);
+
+ while (1) {
+ if (opt_interactive)
+ FD_SET(std_in, &fd_s);
+ FD_SET(snp_io, &fd_s);
+ res = select(snp_io + 1, &fd_s, NULL, NULL, NULL);
+ if (opt_interactive && FD_ISSET(std_in, &fd_s)) {
+
+ if ((res = ioctl(std_in, FIONREAD, &nread)) != 0)
+ fatal(EX_OSERR, "ioctl(FIONREAD)");
+ if (nread > READB_LEN)
+ nread = READB_LEN;
+ rv = read(std_in, chb, nread);
+ if (rv == -1 || (unsigned)rv != nread)
+ fatal(EX_IOERR, "read (stdin) failed");
+
+ switch (chb[0]) {
+ case CHR_CLEAR:
+ clear();
+ break;
+ case CHR_SWITCH:
+ if (!opt_no_switch) {
+ detach_snp();
+ ask_dev(dev_name, MSG_CHANGE);
+ set_dev(dev_name);
+ break;
+ }
+ default:
+ if (opt_write) {
+ rv = write(snp_io, chb, nread);
+ if (rv == -1 || (unsigned)rv != nread) {
+ detach_snp();
+ if (opt_no_switch)
+ fatal(EX_IOERR,
+ "write failed");
+ ask_dev(dev_name, MSG_NOWRITE);
+ set_dev(dev_name);
+ }
+ }
+
+ }
+ }
+ if (!FD_ISSET(snp_io, &fd_s))
+ continue;
+
+ if ((res = ioctl(snp_io, FIONREAD, &idata)) != 0)
+ fatal(EX_OSERR, "ioctl(FIONREAD)");
+
+ switch (idata) {
+ case SNP_OFLOW:
+ if (opt_reconn_oflow)
+ attach_snp();
+ else if (opt_interactive && !opt_no_switch) {
+ ask_dev(dev_name, MSG_OFLOW);
+ set_dev(dev_name);
+ } else
+ cleanup(-1);
+ case SNP_DETACH:
+ case SNP_TTYCLOSE:
+ if (opt_reconn_close)
+ attach_snp();
+ else if (opt_interactive && !opt_no_switch) {
+ ask_dev(dev_name, MSG_CLOSED);
+ set_dev(dev_name);
+ } else
+ cleanup(-1);
+ default:
+ nread = (unsigned)idata;
+ if (nread < (b_size / 2) && (b_size / 2) > MIN_SIZE) {
+ free(buf);
+ if (!(buf = (char *) malloc(b_size / 2)))
+ fatal(EX_UNAVAILABLE, "malloc failed");
+ b_size = b_size / 2;
+ }
+ if (nread > b_size) {
+ b_size = (nread % 2) ? (nread + 1) : (nread);
+ free(buf);
+ if (!(buf = (char *) malloc(b_size)))
+ fatal(EX_UNAVAILABLE, "malloc failed");
+ }
+ rv = read(snp_io, buf, nread);
+ if (rv == -1 || (unsigned)rv != nread)
+ fatal(EX_IOERR, "read failed");
+ rv = write(std_out, buf, nread);
+ if (rv == -1 || (unsigned)rv != nread)
+ fatal(EX_IOERR, "write failed");
+ }
+ } /* While */
+ return(0);
+}
+
diff --git a/usr.sbin/wicontrol/Makefile b/usr.sbin/wicontrol/Makefile
new file mode 100644
index 0000000..e336ad8
--- /dev/null
+++ b/usr.sbin/wicontrol/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+PROG= wicontrol
+MAN= wicontrol.8
+
+CFLAGS+= -DWICACHE
+
+WARNS?= 2
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/wicontrol/wicontrol.8 b/usr.sbin/wicontrol/wicontrol.8
new file mode 100644
index 0000000..9c0d968
--- /dev/null
+++ b/usr.sbin/wicontrol/wicontrol.8
@@ -0,0 +1,466 @@
+.\" Copyright (c) 1997, 1998, 1999
+.\" Bill Paul <wpaul@ctr.columbia.edu> All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Bill Paul.
+.\" 4. Neither the name of the author nor the names of any co-contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+.\" THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd April 21, 1999
+.Dt WICONTROL 8
+.Os
+.Sh NAME
+.Nm wicontrol
+.Nd configure WaveLAN/IEEE devices
+.Sh SYNOPSIS
+.Nm
+.Op Fl i
+.Ar iface Op Fl o
+.Nm
+.Op Fl i
+.Ar iface Fl t Ar tx_rate
+.Nm
+.Op Fl i
+.Ar iface Fl n Ar network_name
+.Nm
+.Op Fl i
+.Ar iface Fl s Ar station_name
+.Nm
+.Op Fl i
+.Ar iface Fl c Cm 0 | 1
+.Nm
+.Op Fl i
+.Ar iface Fl q Ar SSID
+.Nm
+.Op Fl i
+.Ar iface Fl p Ar port_type
+.Nm
+.Op Fl i
+.Ar iface Fl a Ar access_point_density
+.Nm
+.Op Fl i
+.Ar iface Fl m Ar mac_address
+.Nm
+.Op Fl i
+.Ar iface Fl d Ar max_data_length
+.Nm
+.Op Fl i
+.Ar iface Fl e Cm 0 | 1
+.Nm
+.Op Fl i
+.Ar iface Fl k Ar key
+.Op Fl v Cm 1 | 2 | 3 | 4
+.Nm
+.Op Fl i
+.Ar iface Fl T Cm 1 | 2 | 3 | 4
+.Nm
+.Op Fl i
+.Ar iface Fl r Ar RTS_threshold
+.Nm
+.Op Fl i
+.Ar iface Fl f Ar frequency
+.Nm
+.Op Fl i
+.Ar iface Fl P Cm 0 | 1
+.Nm
+.Op Fl i
+.Ar iface Fl S Ar max_sleep_duration
+.Nm
+.Op Fl i
+.Ar iface Fl Z
+(zero signal cache)
+.Nm
+.Op Fl i
+.Ar iface Fl C
+(display signal cache)
+.Nm
+.Op Fl i
+.Ar iface Fl L
+(list avail access points)
+.Nm
+.Op Fl i
+.Ar iface Fl l
+(dump associated stations)
+.Sh DESCRIPTION
+The
+.Nm
+command controls the operation of WaveLAN/IEEE wireless networking
+devices via the
+.Xr wi 4
+driver.
+Most of the parameters that can be changed relate to the
+IEEE 802.11 protocol which the WaveLAN implements.
+This includes
+the station name, whether the station is operating in ad-hoc (point
+to point) or BSS (service set) mode, and the network name of a service
+set to join (IBSS) if BSS mode is enabled.
+The
+.Nm
+command can also be used to view the current settings of these parameters
+and to dump out the values of the card's statistics counters.
+.Pp
+The
+.Ar iface
+argument given to
+.Nm
+should be the logical interface name associated with the WaveLAN/IEEE
+device
+.Li ( wi0 , wi1 ,
+etc.).
+If none is specified then
+.Dq Li wi0
+is used as default.
+.Sh OPTIONS
+The options are as follows:
+.Bl -tag -width indent
+.It Oo Fl i Oc Ar iface Op Fl o
+Display the current settings of the specified WaveLAN/IEEE interface.
+This retrieves the current card settings from the driver and prints them
+out.
+Using the additional
+.Fl o
+flag will cause
+.Nm
+to print out the statistics counters instead of the card settings.
+Encryption keys are only displayed if
+.Nm
+is run as root.
+.It Oo Fl i Oc Ar iface Fl t Ar tx_rate
+Set the transmit rate of the specified interface.
+The legal values
+for the transmit rate vary depending on whether the interface is a
+standard WaveLAN/IEEE or a WaveLAN/IEEE Turbo adapter.
+The standard
+NICs support a maximum transmit rate of 2Mbps while the turbo NICs
+support a maximum speed of 6Mbps.
+The following table shows the
+legal transmit rate settings and the corresponding transmit speeds:
+.Bl -column ".Em TX\ rate" ".Em NIC\ speed" -offset indent
+.Em "TX rate NIC speed"
+.It Cm 1 Ta "Fixed Low (1Mbps)"
+.It Cm 2 Ta "Fixed Standard (2Mbps)"
+.It Cm 3 Ta "Auto Rate Select (High)"
+.It Cm 4 Ta "Fixed Medium (4Mbps)"
+.It Cm 5 Ta "Fixed High (6Mbps)"
+.It Cm 6 Ta "Auto Rate Select (Standard)"
+.It Cm 7 Ta "Auto Rate Select (Medium)"
+.El
+.Pp
+The standard NICs support only settings
+.Cm 1
+through
+.Cm 3 .
+Turbo NICs support all the above listed speed settings.
+The default driver setting is
+.Cm 3
+(auto rate select).
+.It Oo Fl i Oc Ar iface Fl n Ar network_name
+Set the name of the service set (IBSS) that this station wishes to
+join.
+The
+.Ar network_name
+can be any text string up to 30 characters in length.
+The default name
+is the string
+.Dq Li ANY
+which should allow the station to connect to the first
+available access point.
+The interface should be set for BSS mode using
+the
+.Fl p
+flag in order for this to work.
+.Pp
+Note: the WaveLAN manual indicates that an empty string will allow the
+host to connect to any access point, however I have also seen a reference
+in another driver which indicates that the
+.Dq Li ANY
+string works as well.
+.It Oo Fl i Oc Ar iface Fl s Ar station_name
+Sets the
+station name
+for the specified interface.
+The
+.Ar station_name
+is used for diagnostic purposes.
+The
+.Tn "Lucent WaveMANAGER"
+software can
+poll the names of remote hosts.
+.It Oo Fl i Oc Ar iface Fl c Cm 0 | 1
+Allow the station to create a service set (IBSS).
+Permitted values are
+.Cm 0
+(don't create IBSS) and
+.Cm 1
+(enable creation of IBSS).
+The default is
+.Cm 0 .
+.Pp
+Note: this option is provided for experimental purposes only: enabling
+the creation of an IBSS on a host system doesn't appear to actually work.
+.It Oo Fl i Oc Ar iface Fl q Ar SSID
+Specify the name of an IBSS (SSID) to create on a given interface.
+The
+.Ar SSID
+can be any text string up to 30 characters long.
+.Pp
+Note: this option is provided for experimental purposes only: enabling
+the creation of an IBSS on a host system doesn't appear to actually work.
+.It Oo Fl i Oc Ar iface Fl p Ar port_type
+Set the
+port type
+for a specified interface.
+The legal values for
+.Ar port_type
+are
+.Cm 1
+(BSS mode) and
+.Cm 3
+(ad-hoc) mode.
+In ad-hoc mode, the station can
+communicate directly with any other stations within direct radio range
+(provided that they are also operating in ad-hoc mode).
+In BSS mode,
+hosts must associate with a service set controlled by an access point,
+which relays traffic between end stations.
+The default setting is
+.Cm 3
+(ad-hoc mode).
+.It Oo Fl i Oc Ar iface Fl a Ar access_point_density
+Specify the
+access point density
+for a given interface.
+Legal values are
+.Cm 1
+(low),
+.Cm 2
+(medium) and
+.Cm 3
+(high).
+This setting influences some of the radio modem threshold settings.
+.It Oo Fl i Oc Ar iface Fl m Ar mac_address
+Set the station address for the specified interface.
+The
+.Ar mac_address
+is specified as a series of six hexadecimal values separated by colons,
+e.g.,
+.Dq Li 00:60:1d:12:34:56 .
+This programs the new address into the card
+and updates the interface as well.
+.It Oo Fl i Oc Ar iface Fl d Ar max_data_length
+Set the maximum receive and transmit frame size for a specified interface.
+The
+.Ar max_data_length
+can be any number from 350 to 2304.
+The default is 2304.
+.It Oo Fl i Oc Ar iface Fl e Cm 0 | 1
+Enable or disable WEP encryption.
+Permitted values are
+.Cm 0
+(encryption disabled) or
+.Cm 1
+(encryption enabled).
+Encryption is off by default.
+.Pp
+Both 128-bit and 64-bit WEP have been broken.
+See the
+.Sx BUGS
+section for details.
+.It Oo Fl i Oc Ar iface Fl k Ar key Op Fl v Cm 1 | 2 | 3 | 4
+Set WEP encryption keys.
+There are four default encryption keys
+that can be programmed.
+A specific key can be set using
+the
+.Fl v
+flag.
+If the
+.Fl v
+flag is not specified, the first key will be set.
+Encryption keys
+can either be normal text (i.e.\&
+.Dq Li hello )
+or a series of hexadecimal digits (i.e.\&
+.Dq Li 0x1234512345 ) .
+For
+WaveLAN Turbo Silver cards, the key is restricted to 40 bits, hence
+the key can be either a 5 character text string or 10 hex digits.
+For WaveLAN Turbo Gold cards, the key can also be 104 bits,
+which means the key can be specified as either a 13 character text
+string or 26 hex digits in addition to the formats supported by the
+Silver cards.
+.Pp
+Note: Both 128-bit and 64-bit WEP have been broken.
+See the
+.Sx BUGS
+section for details.
+.It Oo Fl i Oc Ar iface Fl T Cm 1 | 2 | 3 | 4
+Specify which of the four WEP encryption keys will be used to
+encrypt transmitted packets.
+.Pp
+Note: Both 128-bit and 64-bit WEP have been broken.
+See the
+.Sx BUGS
+section for details.
+.It Oo Fl i Oc Ar iface Fl r Ar RTS_threshold
+Set the RTS/CTS threshold for a given interface.
+This controls the
+number of bytes used for the RTS/CTS handshake boundary.
+The
+.Ar RTS_threshold
+can be any value between 0 and 2347.
+The default is 2347.
+.It Oo Fl i Oc Ar iface Fl f Ar frequency
+Set the radio frequency of a given interface.
+The
+.Ar frequency
+should be specified as a channel ID as shown in the table below.
+The
+list of available frequencies is dependent on radio regulations specified
+by regional authorities.
+Recognized regulatory authorities include
+the FCC (United States), ETSI (Europe), France and Japan.
+Frequencies
+in the table are specified in Mhz.
+.Bl -column ".Em Channel\ ID" ".Em FCC" ".Em ETSI" ".Em France" ".Em Japan" -offset indent
+.Em "Channel ID FCC ETSI France Japan"
+.It Cm 1 Ta "2412 2412 - 2412"
+.It Cm 2 Ta "2417 2417 - 2417"
+.It Cm 3 Ta "2422 2422 - 2422"
+.It Cm 4 Ta "2427 2427 - 2427"
+.It Cm 5 Ta "2432 2432 - 2432"
+.It Cm 6 Ta "2437 2437 - 2437"
+.It Cm 7 Ta "2442 2442 - 2442"
+.It Cm 8 Ta "2447 2447 - 2447"
+.It Cm 9 Ta "2452 2452 - 2452"
+.It Cm 10 Ta "2457 2457 2457 2457"
+.It Cm 11 Ta "2462 2462 2462 2462"
+.It Cm 12 Ta "- 2467 2467 2467"
+.It Cm 13 Ta "- 2472 2472 2472"
+.It Cm 14 Ta "- - - 2484"
+.El
+.Pp
+If an illegal channel is specified, the
+NIC will revert to its default channel.
+For NICs sold in the United States
+and Europe, the default channel is
+.Cm 3 .
+For NICs sold in France, the default channel is
+.Cm 11 .
+For NICs sold in Japan, the default channel is
+.Cm 14 ,
+and it is the only available channel for pre-11Mbps NICs.
+Note that two stations must be set to the same channel in order to
+communicate.
+.It Oo Fl i Oc Ar iface Fl P Cm 0 | 1
+Enable or disable power management on a given interface.
+Enabling
+power management uses an alternating sleep/wake protocol to help
+conserve power on mobile stations, at the cost of some increased
+receive latency.
+Power management is off by default.
+Note that power
+management requires the cooperation of an access point in order to
+function; it is not functional in ad-hoc mode.
+Also, power management
+is only implemented in Lucent WavePOINT firmware version 2.03 or
+later, and in WaveLAN PCMCIA adapter firmware 2.00 or later.
+Older
+revisions will silently ignore the power management setting.
+Legal
+values for this parameter are
+.Cm 0
+(off) and
+.Cm 1
+(on).
+.It Oo Fl i Oc Ar iface Fl S Ar max_sleep_interval
+Specify the sleep interval to use when power management is enabled.
+The
+.Ar max_sleep_interval
+is specified in milliseconds.
+The default is 100.
+.It Oo Fl i Oc Ar iface Fl Z
+Clear the signal strength cache maintained internally by the
+.Xr wi 4
+driver.
+.It Oo Fl i Oc Ar iface Fl C
+Display the cached signal strength information maintained by the
+.Xr wi 4
+driver.
+The driver retains information about signal strength and
+noise level for packets received from different hosts.
+The signal
+strength and noise level values are displayed in units of dBms.
+The signal quality values is produced by subtracting the noise level
+from the signal strength (i.e. less noise and better signal yields
+better signal quality).
+.El
+.Sh SEE ALSO
+.Xr ipsec 4 ,
+.Xr wi 4 ,
+.Xr ifconfig 8
+.Sh BUGS
+The WEP encryption method has been broken so that third parties
+can recover the keys in use relatively quickly at distances that are
+surprising to most people.
+Do not rely on WEP for anything but the most basic, remedial security.
+IPSEC will give you a higher level of security and should be used
+whenever possible.
+Do not trust access points or wireless machines that connect through
+them as they can provide no assurance that the traffic is legitimate.
+MAC addresses can easily be forged and should therefore not be used as
+the only access control.
+.Pp
+The attack on WEP is a passive attack, requiring only the ability to
+sniff packets on the network.
+The passive attack can be launched at a distance larger, up to many
+miles, than one might otherwise expect given a specialized antenna
+used in point to point applications.
+The attacker can recover the keys from a 128-bit WEP network with only
+5,000,000 to 6,000,000 packets.
+While this may sound like a large number of packets, emperical
+evidence suggests that this amount of traffic is generated in a few
+hours on a partially loaded network.
+Once a key has been compromised, the only remedial action is to
+discontinue it and use a new key.
+.Pp
+See
+.Pa http://www.cs.rice.edu/~astubble/wep/wep_attack.html
+for details of the attack.
+.Pp
+If you must use WEP, you are strongly encouraged to pick keys whose
+bytes are random and not confined to ASCII characters.
+.Sh HISTORY
+The
+.Nm
+command first appeared in
+.Fx 3.0 .
+.Sh AUTHORS
+The
+.Nm
+command was written by
+.An Bill Paul Aq wpaul@ctr.columbia.edu .
diff --git a/usr.sbin/wicontrol/wicontrol.c b/usr.sbin/wicontrol/wicontrol.c
new file mode 100644
index 0000000..a6dc729
--- /dev/null
+++ b/usr.sbin/wicontrol/wicontrol.c
@@ -0,0 +1,1015 @@
+/*
+ * Copyright (c) 1997, 1998, 1999
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] = "@(#) Copyright (c) 1997, 1998, 1999\
+ Bill Paul. All rights reserved.";
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/ethernet.h>
+
+#include <net/if_ieee80211.h>
+#include <dev/wi/if_wavelan_ieee.h>
+#include <dev/wi/wi_hostap.h>
+#include <dev/wi/if_wireg.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <errno.h>
+#include <err.h>
+
+static void wi_getval(const char *, struct wi_req *);
+static void wi_setval(const char *, struct wi_req *);
+static void wi_printstr(struct wi_req *);
+static void wi_setstr(const char *, int, char *);
+static void wi_setbytes(const char *, int, char *, int);
+static void wi_setword(const char *, int, int);
+static void wi_sethex(const char *, int, char *);
+static void wi_printwords(struct wi_req *);
+static void wi_printbool(struct wi_req *);
+static void wi_printhex(struct wi_req *);
+static void wi_dumpinfo(const char *);
+static void wi_dumpstats(const char *);
+static void wi_setkeys(const char *, char *, int);
+static void wi_printkeys(struct wi_req *);
+static void wi_printaplist(const char *);
+static int wi_hex2int(char);
+static void wi_str2key(char *, struct wi_key *);
+#ifdef WICACHE
+static void wi_zerocache(const char *);
+static void wi_readcache(const char *);
+#endif
+static void usage(const char *);
+
+int listaps;
+
+/*
+ * Print a value a la the %b format of the kernel's printf
+ * (ripped screaming from ifconfig/ifconfig.c)
+ */
+void
+printb(s, v, bits)
+ char *s;
+ char *bits;
+ unsigned short v;
+{
+ int i, any = 0;
+ char c;
+
+ if (bits && *bits == 8)
+ printf("%s=%o", s, v);
+ else
+ printf("%s=%x", s, v);
+ bits++;
+ if (bits) {
+ putchar('<');
+ while ((i = *bits++)) {
+ if (v & (1 << (i-1))) {
+ if (any)
+ putchar(',');
+ any = 1;
+ for (; (c = *bits) > 32; bits++)
+ putchar(c);
+ } else
+ for (; *bits > 32; bits++)
+ ;
+ }
+ putchar('>');
+ }
+}
+
+static void
+wi_getval(const char *iface, struct wi_req *wreq)
+{
+ struct ifreq ifr;
+ int s;
+
+ bzero((char *)&ifr, sizeof(ifr));
+
+ strlcpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name));
+ ifr.ifr_data = (caddr_t)wreq;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+
+ if (s == -1)
+ err(1, "socket");
+
+ if (ioctl(s, SIOCGWAVELAN, &ifr) == -1)
+ err(1, "SIOCGWAVELAN");
+
+ close(s);
+
+ return;
+}
+
+static void
+wi_setval(const char *iface, struct wi_req *wreq)
+{
+ struct ifreq ifr;
+ int s;
+
+ bzero((char *)&ifr, sizeof(ifr));
+
+ strlcpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name));
+ ifr.ifr_data = (caddr_t)wreq;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+
+ if (s == -1)
+ err(1, "socket");
+
+ if (ioctl(s, SIOCSWAVELAN, &ifr) == -1)
+ err(1, "SIOCSWAVELAN");
+
+ close(s);
+
+ return;
+}
+
+void
+wi_printstr(struct wi_req *wreq)
+{
+ char *ptr;
+ int i;
+
+ if (wreq->wi_type == WI_RID_SERIALNO) {
+ ptr = (char *)&wreq->wi_val;
+ for (i = 0; i < (wreq->wi_len - 1) * 2; i++) {
+ if (ptr[i] == '\0')
+ ptr[i] = ' ';
+ }
+ } else {
+ ptr = (char *)&wreq->wi_val[1];
+ for (i = 0; i < wreq->wi_val[0]; i++) {
+ if (ptr[i] == '\0')
+ ptr[i] = ' ';
+ }
+ }
+
+ ptr[i] = '\0';
+ printf("[ %s ]", ptr);
+
+ return;
+}
+
+void
+wi_setstr(const char *iface, int code, char *str)
+{
+ struct wi_req wreq;
+
+ if (iface == NULL)
+ errx(1, "must specify interface name");
+
+ if (str == NULL)
+ errx(1, "must specify string");
+
+ bzero((char *)&wreq, sizeof(wreq));
+
+ if (strlen(str) > 30)
+ errx(1, "string too long");
+
+ wreq.wi_type = code;
+ wreq.wi_len = 18;
+ wreq.wi_val[0] = strlen(str);
+ bcopy(str, (char *)&wreq.wi_val[1], strlen(str));
+
+ wi_setval(iface, &wreq);
+
+ return;
+}
+
+void
+wi_setbytes(const char *iface, int code, char *bytes, int len)
+{
+ struct wi_req wreq;
+
+ if (iface == NULL)
+ errx(1, "must specify interface name");
+
+ bzero((char *)&wreq, sizeof(wreq));
+
+ wreq.wi_type = code;
+ wreq.wi_len = (len / 2) + 1;
+ bcopy(bytes, (char *)&wreq.wi_val[0], len);
+
+ wi_setval(iface, &wreq);
+
+ return;
+}
+
+void
+wi_setword(const char *iface, int code, int word)
+{
+ struct wi_req wreq;
+
+ bzero((char *)&wreq, sizeof(wreq));
+
+ wreq.wi_type = code;
+ wreq.wi_len = 2;
+ wreq.wi_val[0] = word;
+
+ wi_setval(iface, &wreq);
+
+ return;
+}
+
+void
+wi_sethex(const char *iface, int code, char *str)
+{
+ struct ether_addr *addr;
+
+ if (str == NULL)
+ errx(1, "must specify address");
+
+ addr = ether_aton(str);
+
+ if (addr == NULL)
+ errx(1, "badly formatted address");
+
+ wi_setbytes(iface, code, (char *)addr, ETHER_ADDR_LEN);
+
+ return;
+}
+
+static int
+wi_hex2int(char c)
+{
+ if (c >= '0' && c <= '9')
+ return (c - '0');
+ if (c >= 'A' && c <= 'F')
+ return (c - 'A' + 10);
+ if (c >= 'a' && c <= 'f')
+ return (c - 'a' + 10);
+
+ return (0);
+}
+
+static void
+wi_str2key(char *s, struct wi_key *k)
+{
+ int n, i;
+ char *p;
+
+ /* Is this a hex string? */
+ if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
+ /* Yes, convert to int. */
+ n = 0;
+ p = (char *)&k->wi_keydat[0];
+ for (i = 2; s[i] != '\0' && s[i + 1] != '\0'; i+= 2) {
+ *p++ = (wi_hex2int(s[i]) << 4) + wi_hex2int(s[i + 1]);
+ n++;
+ }
+ if (s[i] != '\0')
+ errx(1, "hex strings must be of even length");
+ k->wi_keylen = n;
+ } else {
+ /* No, just copy it in. */
+ bcopy(s, k->wi_keydat, strlen(s));
+ k->wi_keylen = strlen(s);
+ }
+
+ return;
+}
+
+static void
+wi_setkeys(const char *iface, char *key, int idx)
+{
+ int keylen;
+ struct wi_req wreq;
+ struct wi_ltv_keys *keys;
+ struct wi_key *k;
+
+ bzero((char *)&wreq, sizeof(wreq));
+ wreq.wi_len = WI_MAX_DATALEN;
+ wreq.wi_type = WI_RID_WEP_AVAIL;
+
+ wi_getval(iface, &wreq);
+ if (wreq.wi_val[0] == 0)
+ errx(1, "no WEP option available on this card");
+
+ bzero((char *)&wreq, sizeof(wreq));
+ wreq.wi_len = WI_MAX_DATALEN;
+ wreq.wi_type = WI_RID_DEFLT_CRYPT_KEYS;
+
+ wi_getval(iface, &wreq);
+ keys = (struct wi_ltv_keys *)&wreq;
+
+ keylen = strlen(key);
+ if (key[0] == '0' && (key[1] == 'x' || key[1] == 'X')) {
+ if (keylen != 2 && keylen != 12 && keylen != 28) {
+ errx(1, "encryption key must be 0, 10, or 26 "
+ "hex digits long");
+ }
+ } else {
+ if (keylen != 0 && keylen != 5 && keylen != 13) {
+ errx(1, "encryption key must be 0, 5, or 13 "
+ "bytes long");
+ }
+ }
+
+ if (idx > 3)
+ errx(1, "only 4 encryption keys available");
+
+ k = &keys->wi_keys[idx];
+ wi_str2key(key, k);
+
+ wreq.wi_len = (sizeof(struct wi_ltv_keys) / 2) + 1;
+ wreq.wi_type = WI_RID_DEFLT_CRYPT_KEYS;
+ wi_setval(iface, &wreq);
+
+ return;
+}
+
+static void
+wi_printkeys(struct wi_req *wreq)
+{
+ int i, j;
+ int isprintable;
+ struct wi_key *k;
+ struct wi_ltv_keys *keys;
+ char *ptr;
+
+ keys = (struct wi_ltv_keys *)wreq;
+
+ for (i = 0; i < 4; i++) {
+ k = &keys->wi_keys[i];
+ ptr = (char *)k->wi_keydat;
+ isprintable = 1;
+ for (j = 0; j < k->wi_keylen; j++) {
+ if (!isprint(ptr[j])) {
+ isprintable = 0;
+ break;
+ }
+ }
+ if (isprintable) {
+ ptr[j] = '\0';
+ printf("[ %s ]", ptr);
+ } else {
+ printf("[ 0x");
+ for (j = 0; j < k->wi_keylen; j++) {
+ printf("%02x", ptr[j] & 0xFF);
+ }
+ printf(" ]");
+
+ }
+ }
+
+ return;
+};
+
+void
+wi_printwords(struct wi_req *wreq)
+{
+ int i;
+
+ printf("[ ");
+ for (i = 0; i < wreq->wi_len - 1; i++)
+ printf("%d ", wreq->wi_val[i]);
+ printf("]");
+
+ return;
+}
+
+void
+wi_printbool(struct wi_req *wreq)
+{
+ if (wreq->wi_val[0])
+ printf("[ On ]");
+ else
+ printf("[ Off ]");
+
+ return;
+}
+
+void
+wi_printhex(struct wi_req *wreq)
+{
+ int i;
+ unsigned char *c;
+
+ c = (unsigned char *)&wreq->wi_val;
+
+ printf("[ ");
+ for (i = 0; i < (wreq->wi_len - 1) * 2; i++) {
+ printf("%02x", c[i]);
+ if (i < ((wreq->wi_len - 1) * 2) - 1)
+ printf(":");
+ }
+
+ printf(" ]");
+ return;
+}
+
+void
+wi_printaplist(const char *iface)
+{
+ int prism2, len, i = 0, j;
+ struct wi_req wreq;
+ struct wi_scan_p2_hdr *wi_p2_h;
+ struct wi_scan_res *res;
+
+ printf("Available APs:\n");
+
+ /* first determine if this is a prism2 card or not */
+ wreq.wi_len = WI_MAX_DATALEN;
+ wreq.wi_type = WI_RID_PRISM2;
+
+ wi_getval(iface, &wreq);
+ prism2 = wreq.wi_val[0];
+
+ /* send out a scan request */
+ wreq.wi_len = prism2 ? 3 : 1;
+ wreq.wi_type = WI_RID_SCAN_REQ;
+
+ if (prism2) {
+ wreq.wi_val[0] = 0x3FFF;
+ wreq.wi_val[1] = 0x000F;
+ }
+
+ wi_setval(iface, &wreq);
+
+ /*
+ * sleep for 100 milliseconds so there's enough time for the card to
+ * respond... prism2's take a little longer.
+ */
+ usleep(prism2 ? 500000 : 100000);
+
+ /* get the scan results */
+ wreq.wi_len = WI_MAX_DATALEN;
+ wreq.wi_type = WI_RID_SCAN_RES;
+
+ wi_getval(iface, &wreq);
+
+ if (prism2) {
+ wi_p2_h = (struct wi_scan_p2_hdr *)wreq.wi_val;
+
+ /* if the reason is 0, this info is invalid */
+ if (wi_p2_h->wi_reason == 0)
+ return;
+
+ i = 4;
+ }
+
+ len = prism2 ? WI_PRISM2_RES_SIZE : WI_WAVELAN_RES_SIZE;
+
+ for (; i < (wreq.wi_len * 2) - len; i += len) {
+ res = (struct wi_scan_res *)((char *)wreq.wi_val + i);
+
+ res->wi_ssid[res->wi_ssid_len] = '\0';
+
+ printf(" %-8s [ %02x:%02x:%02x:%02x:%02x:%02x ] [ %-2d ] "
+ "[ %d %d %d ] %-3d ", res->wi_ssid,
+ res->wi_bssid[0], res->wi_bssid[1], res->wi_bssid[2],
+ res->wi_bssid[3], res->wi_bssid[4], res->wi_bssid[5],
+ res->wi_chan, res->wi_signal - res->wi_noise,
+ res->wi_signal, res->wi_noise, res->wi_interval);
+
+ if (res->wi_capinfo) {
+ printf("[ ");
+ if (res->wi_capinfo & WI_CAPINFO_ESS)
+ printf("ess ");
+ if (res->wi_capinfo & WI_CAPINFO_IBSS)
+ printf("ibss ");
+ if (res->wi_capinfo & WI_CAPINFO_PRIV)
+ printf("priv ");
+ printf("] ");
+ }
+
+ if (prism2) {
+ printf("\n [ ");
+ for (j = 0; res->wi_srates[j] != 0; j++) {
+ res->wi_srates[j] = res->wi_srates[j] &
+ WI_VAR_SRATES_MASK;
+ printf("%d.%d ", res->wi_srates[j] / 2,
+ (res->wi_srates[j] % 2) * 5);
+ }
+ printf("] ");
+
+ printf("* %2.1f *", res->wi_rate == 0xa ? 1 :
+ (res->wi_rate == 0x14 ? 2 :
+ (res->wi_rate == 0x37 ? 5.5 :
+ (res->wi_rate == 0x6e ? 11 : 0))));
+ }
+
+ putchar('\n');
+ }
+
+ return;
+}
+
+#define WI_STRING 0x01
+#define WI_BOOL 0x02
+#define WI_WORDS 0x03
+#define WI_HEXBYTES 0x04
+#define WI_KEYSTRUCT 0x05
+
+struct wi_table {
+ int wi_code;
+ int wi_type;
+ const char *wi_str;
+};
+
+static struct wi_table wi_table[] = {
+ { WI_RID_SERIALNO, WI_STRING, "NIC serial number:\t\t\t" },
+ { WI_RID_NODENAME, WI_STRING, "Station name:\t\t\t\t" },
+ { WI_RID_OWN_SSID, WI_STRING, "SSID for IBSS creation:\t\t\t" },
+ { WI_RID_CURRENT_SSID, WI_STRING, "Current netname (SSID):\t\t\t" },
+ { WI_RID_DESIRED_SSID, WI_STRING, "Desired netname (SSID):\t\t\t" },
+ { WI_RID_CURRENT_BSSID, WI_HEXBYTES, "Current BSSID:\t\t\t\t" },
+ { WI_RID_CHANNEL_LIST, WI_WORDS, "Channel list:\t\t\t\t" },
+ { WI_RID_OWN_CHNL, WI_WORDS, "IBSS channel:\t\t\t\t" },
+ { WI_RID_CURRENT_CHAN, WI_WORDS, "Current channel:\t\t\t" },
+ { WI_RID_COMMS_QUALITY, WI_WORDS, "Comms quality/signal/noise:\t\t" },
+ { WI_RID_PROMISC, WI_BOOL, "Promiscuous mode:\t\t\t" },
+ { WI_RID_PROCFRAME, WI_BOOL, "Process 802.11b Frame:\t\t\t" },
+ { WI_RID_PRISM2, WI_WORDS, "Intersil-Prism2 based card:\t\t" },
+ { WI_RID_PORTTYPE, WI_WORDS, "Port type (1=BSS, 3=ad-hoc):\t\t"},
+ { WI_RID_MAC_NODE, WI_HEXBYTES, "MAC address:\t\t\t\t"},
+ { WI_RID_TX_RATE, WI_WORDS, "TX rate (selection):\t\t\t"},
+ { WI_RID_CUR_TX_RATE, WI_WORDS, "TX rate (actual speed):\t\t\t"},
+ { WI_RID_RTS_THRESH, WI_WORDS, "RTS/CTS handshake threshold:\t\t"},
+ { WI_RID_CREATE_IBSS, WI_BOOL, "Create IBSS:\t\t\t\t" },
+ { WI_RID_SYSTEM_SCALE, WI_WORDS, "Access point density:\t\t\t" },
+ { WI_RID_PM_ENABLED, WI_WORDS, "Power Mgmt (1=on, 0=off):\t\t" },
+ { WI_RID_MAX_SLEEP, WI_WORDS, "Max sleep time:\t\t\t\t" },
+ { 0, 0, NULL }
+};
+
+static struct wi_table wi_crypt_table[] = {
+ { WI_RID_ENCRYPTION, WI_BOOL, "WEP encryption:\t\t\t\t" },
+ { WI_RID_TX_CRYPT_KEY, WI_WORDS, "TX encryption key:\t\t\t" },
+ { WI_RID_DEFLT_CRYPT_KEYS, WI_KEYSTRUCT, "Encryption keys:\t\t\t" },
+ { 0, 0, NULL }
+};
+
+static void
+wi_dumpinfo(const char *iface)
+{
+ struct wi_req wreq;
+ int i, has_wep;
+ struct wi_table *w;
+
+ bzero((char *)&wreq, sizeof(wreq));
+
+ wreq.wi_len = WI_MAX_DATALEN;
+ wreq.wi_type = WI_RID_WEP_AVAIL;
+
+ wi_getval(iface, &wreq);
+ has_wep = wreq.wi_val[0];
+
+ w = wi_table;
+
+ for (i = 0; w[i].wi_type; i++) {
+ bzero((char *)&wreq, sizeof(wreq));
+
+ wreq.wi_len = WI_MAX_DATALEN;
+ wreq.wi_type = w[i].wi_code;
+
+ wi_getval(iface, &wreq);
+ printf("%s", w[i].wi_str);
+ switch(w[i].wi_type) {
+ case WI_STRING:
+ wi_printstr(&wreq);
+ break;
+ case WI_WORDS:
+ wi_printwords(&wreq);
+ break;
+ case WI_BOOL:
+ wi_printbool(&wreq);
+ break;
+ case WI_HEXBYTES:
+ wi_printhex(&wreq);
+ break;
+ default:
+ break;
+ }
+ printf("\n");
+ }
+
+ if (has_wep) {
+ w = wi_crypt_table;
+ for (i = 0; w[i].wi_type; i++) {
+ bzero((char *)&wreq, sizeof(wreq));
+
+ wreq.wi_len = WI_MAX_DATALEN;
+ wreq.wi_type = w[i].wi_code;
+
+ wi_getval(iface, &wreq);
+ printf("%s", w[i].wi_str);
+ switch(w[i].wi_type) {
+ case WI_STRING:
+ wi_printstr(&wreq);
+ break;
+ case WI_WORDS:
+ if (wreq.wi_type == WI_RID_TX_CRYPT_KEY)
+ wreq.wi_val[0]++;
+ wi_printwords(&wreq);
+ break;
+ case WI_BOOL:
+ wi_printbool(&wreq);
+ break;
+ case WI_HEXBYTES:
+ wi_printhex(&wreq);
+ break;
+ case WI_KEYSTRUCT:
+ wi_printkeys(&wreq);
+ break;
+ default:
+ break;
+ }
+ printf("\n");
+ }
+ }
+
+ if (listaps)
+ wi_printaplist(iface);
+
+ return;
+}
+
+static void
+wi_dumpstats(const char *iface)
+{
+ struct wi_req wreq;
+ struct wi_counters *c;
+
+ if (iface == NULL)
+ errx(1, "must specify interface name");
+
+ bzero((char *)&wreq, sizeof(wreq));
+ wreq.wi_len = WI_MAX_DATALEN;
+ wreq.wi_type = WI_RID_IFACE_STATS;
+
+ wi_getval(iface, &wreq);
+
+ c = (struct wi_counters *)&wreq.wi_val;
+
+ printf("Transmitted unicast frames:\t\t%d\n",
+ c->wi_tx_unicast_frames);
+ printf("Transmitted multicast frames:\t\t%d\n",
+ c->wi_tx_multicast_frames);
+ printf("Transmitted fragments:\t\t\t%d\n",
+ c->wi_tx_fragments);
+ printf("Transmitted unicast octets:\t\t%d\n",
+ c->wi_tx_unicast_octets);
+ printf("Transmitted multicast octets:\t\t%d\n",
+ c->wi_tx_multicast_octets);
+ printf("Single transmit retries:\t\t%d\n",
+ c->wi_tx_single_retries);
+ printf("Multiple transmit retries:\t\t%d\n",
+ c->wi_tx_multi_retries);
+ printf("Transmit retry limit exceeded:\t\t%d\n",
+ c->wi_tx_retry_limit);
+ printf("Transmit discards:\t\t\t%d\n",
+ c->wi_tx_discards);
+ printf("Transmit discards due to wrong SA:\t%d\n",
+ c->wi_tx_discards_wrong_sa);
+ printf("Received unicast frames:\t\t%d\n",
+ c->wi_rx_unicast_frames);
+ printf("Received multicast frames:\t\t%d\n",
+ c->wi_rx_multicast_frames);
+ printf("Received fragments:\t\t\t%d\n",
+ c->wi_rx_fragments);
+ printf("Received unicast octets:\t\t%d\n",
+ c->wi_rx_unicast_octets);
+ printf("Received multicast octets:\t\t%d\n",
+ c->wi_rx_multicast_octets);
+ printf("Receive FCS errors:\t\t\t%d\n",
+ c->wi_rx_fcs_errors);
+ printf("Receive discards due to no buffer:\t%d\n",
+ c->wi_rx_discards_nobuf);
+ printf("Can't decrypt WEP frame:\t\t%d\n",
+ c->wi_rx_WEP_cant_decrypt);
+ printf("Received message fragments:\t\t%d\n",
+ c->wi_rx_msg_in_msg_frags);
+ printf("Received message bad fragments:\t\t%d\n",
+ c->wi_rx_msg_in_bad_msg_frags);
+
+ return;
+}
+
+static void
+usage(const char *p)
+{
+ fprintf(stderr, "usage: %s -i iface\n", p);
+ fprintf(stderr, "\t%s -i iface -o\n", p);
+ fprintf(stderr, "\t%s -i iface -l\n", p);
+ fprintf(stderr, "\t%s -i iface -t tx rate\n", p);
+ fprintf(stderr, "\t%s -i iface -n network name\n", p);
+ fprintf(stderr, "\t%s -i iface -s station name\n", p);
+ fprintf(stderr, "\t%s -i iface -c 0|1\n", p);
+ fprintf(stderr, "\t%s -i iface -q SSID\n", p);
+ fprintf(stderr, "\t%s -i iface -p port type\n", p);
+ fprintf(stderr, "\t%s -i iface -a access point density\n", p);
+ fprintf(stderr, "\t%s -i iface -m mac address\n", p);
+ fprintf(stderr, "\t%s -i iface -d max data length\n", p);
+ fprintf(stderr, "\t%s -i iface -e 0|1\n", p);
+ fprintf(stderr, "\t%s -i iface -k encryption key [-v 1|2|3|4]\n", p);
+ fprintf(stderr, "\t%s -i iface -r RTS threshold\n", p);
+ fprintf(stderr, "\t%s -i iface -f frequency\n", p);
+ fprintf(stderr, "\t%s -i iface -F 0|1\n", p);
+ fprintf(stderr, "\t%s -i iface -P 0|1\n", p);
+ fprintf(stderr, "\t%s -i iface -S max sleep duration\n", p);
+ fprintf(stderr, "\t%s -i iface -T 1|2|3|4\n", p);
+#ifdef WICACHE
+ fprintf(stderr, "\t%s -i iface -Z zero out signal cache\n", p);
+ fprintf(stderr, "\t%s -i iface -C print signal cache\n", p);
+#endif
+
+ exit(1);
+}
+
+static void
+wi_dumpstations(const char *iface)
+{
+ struct hostap_getall reqall;
+ struct hostap_sta stas[WIHAP_MAX_STATIONS];
+ struct ifreq ifr;
+ int i, s;
+
+ bzero(&ifr, sizeof(ifr));
+ strlcpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name));
+ ifr.ifr_data = (caddr_t) & reqall;
+ bzero(&reqall, sizeof(reqall));
+ reqall.size = sizeof(stas);
+ reqall.addr = stas;
+ bzero(&stas, sizeof(stas));
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s == -1)
+ err(1, "socket");
+
+ if (ioctl(s, SIOCHOSTAP_GETALL, &ifr) < 0)
+ err(1, "SIOCHOSTAP_GETALL");
+
+ printf("%d station%s:\n", reqall.nstations, reqall.nstations>1?"s":"");
+ for (i = 0; i < reqall.nstations; i++) {
+ struct hostap_sta *info = &stas[i];
+
+ printf("%02x:%02x:%02x:%02x:%02x:%02x asid=%04x",
+ info->addr[0], info->addr[1], info->addr[2],
+ info->addr[3], info->addr[4], info->addr[5],
+ info->asid - 0xc001);
+ printb(", flags", info->flags, HOSTAP_FLAGS_BITS);
+ printb(", caps", info->capinfo, IEEE80211_CAPINFO_BITS);
+ printb(", rates", info->rates, WI_RATES_BITS);
+ if (info->sig_info)
+ printf(", sig=%d/%d",
+ info->sig_info >> 8, info->sig_info & 0xff);
+ putchar('\n');
+ }
+}
+
+#ifdef WICACHE
+static void
+wi_zerocache(const char *iface)
+{
+ struct wi_req wreq;
+
+ if (iface == NULL)
+ errx(1, "must specify interface name");
+
+ bzero((char *)&wreq, sizeof(wreq));
+ wreq.wi_len = 0;
+ wreq.wi_type = WI_RID_ZERO_CACHE;
+
+ wi_getval(iface, &wreq);
+}
+
+static void
+wi_readcache(const char *iface)
+{
+ struct wi_req wreq;
+ int *wi_sigitems;
+ struct wi_sigcache *sc;
+ char * pt;
+ int i;
+
+ if (iface == NULL)
+ errx(1, "must specify interface name");
+
+ bzero((char *)&wreq, sizeof(wreq));
+ wreq.wi_len = WI_MAX_DATALEN;
+ wreq.wi_type = WI_RID_READ_CACHE;
+
+ wi_getval(iface, &wreq);
+
+ wi_sigitems = (int *) &wreq.wi_val;
+ pt = ((char *) &wreq.wi_val);
+ pt += sizeof(int);
+ sc = (struct wi_sigcache *) pt;
+
+ for (i = 0; i < *wi_sigitems; i++) {
+ printf("[%d/%d]:", i+1, *wi_sigitems);
+ printf(" %02x:%02x:%02x:%02x:%02x:%02x,",
+ sc->macsrc[0]&0xff,
+ sc->macsrc[1]&0xff,
+ sc->macsrc[2]&0xff,
+ sc->macsrc[3]&0xff,
+ sc->macsrc[4]&0xff,
+ sc->macsrc[5]&0xff);
+ printf(" %d.%d.%d.%d,",((sc->ipsrc >> 0) & 0xff),
+ ((sc->ipsrc >> 8) & 0xff),
+ ((sc->ipsrc >> 16) & 0xff),
+ ((sc->ipsrc >> 24) & 0xff));
+ printf(" sig: %d, noise: %d, qual: %d\n",
+ sc->signal,
+ sc->noise,
+ sc->quality);
+ sc++;
+ }
+
+ return;
+}
+#endif
+
+int
+main(int argc, char *argv[])
+{
+ int ch;
+ const char *iface = NULL;
+ char *p = argv[0];
+ char *key = NULL;
+ int modifier = 0;
+
+ /* Get the interface name */
+ opterr = 0;
+ ch = getopt(argc, argv, "i:");
+ if (ch == 'i') {
+ iface = optarg;
+ } else {
+ if (argc > 1 && *argv[1] != '-') {
+ iface = argv[1];
+ optind = 2;
+ } else {
+ iface = "wi0";
+ optind = 1;
+ }
+ optreset = 1;
+ }
+ opterr = 1;
+
+ while((ch = getopt(argc, argv,
+ "a:hoc:d:e:f:i:k:lp:r:q:t:n:s:m:v:F:LP:S:T:ZC")) != -1) {
+ switch(ch) {
+ case 'Z':
+#ifdef WICACHE
+ wi_zerocache(iface);
+#else
+ printf("WICACHE not available\n");
+#endif
+ exit(0);
+ break;
+ case 'C':
+#ifdef WICACHE
+ wi_readcache(iface);
+#else
+ printf("WICACHE not available\n");
+#endif
+ exit(0);
+ break;
+ case 'o':
+ wi_dumpstats(iface);
+ exit(0);
+ break;
+ case 'c':
+ wi_setword(iface, WI_RID_CREATE_IBSS, atoi(optarg));
+ exit(0);
+ break;
+ case 'd':
+ wi_setword(iface, WI_RID_MAX_DATALEN, atoi(optarg));
+ exit(0);
+ break;
+ case 'e':
+ wi_setword(iface, WI_RID_ENCRYPTION, atoi(optarg));
+ exit(0);
+ break;
+ case 'f':
+ wi_setword(iface, WI_RID_OWN_CHNL, atoi(optarg));
+ exit(0);
+ break;
+ case 'F':
+ wi_setword(iface, WI_RID_PROCFRAME, atoi(optarg));
+ exit(0);
+ break;
+ case 'k':
+ key = optarg;
+ break;
+ case 'L':
+ listaps = 1;
+ break;
+ case 'l':
+ wi_dumpstations(iface);
+ exit(0);
+ break;
+ case 'p':
+ wi_setword(iface, WI_RID_PORTTYPE, atoi(optarg));
+ exit(0);
+ break;
+ case 'r':
+ wi_setword(iface, WI_RID_RTS_THRESH, atoi(optarg));
+ exit(0);
+ break;
+ case 't':
+ wi_setword(iface, WI_RID_TX_RATE, atoi(optarg));
+ exit(0);
+ break;
+ case 'n':
+ wi_setstr(iface, WI_RID_DESIRED_SSID, optarg);
+ exit(0);
+ break;
+ case 's':
+ wi_setstr(iface, WI_RID_NODENAME, optarg);
+ exit(0);
+ break;
+ case 'm':
+ wi_sethex(iface, WI_RID_MAC_NODE, optarg);
+ exit(0);
+ break;
+ case 'q':
+ wi_setstr(iface, WI_RID_OWN_SSID, optarg);
+ exit(0);
+ break;
+ case 'S':
+ wi_setword(iface, WI_RID_MAX_SLEEP, atoi(optarg));
+ exit(0);
+ break;
+ case 'T':
+ wi_setword(iface,
+ WI_RID_TX_CRYPT_KEY, atoi(optarg) - 1);
+ exit(0);
+ break;
+ case 'P':
+ wi_setword(iface, WI_RID_PM_ENABLED, atoi(optarg));
+ exit(0);
+ break;
+ case 'a':
+ wi_setword(iface, WI_RID_SYSTEM_SCALE, atoi(optarg));
+ exit(0);
+ break;
+ case 'v':
+ modifier = atoi(optarg);
+ modifier--;
+ break;
+ case 'h':
+ default:
+ usage(p);
+ break;
+ }
+ }
+
+ if (iface == NULL)
+ usage(p);
+
+ if (key != NULL) {
+ wi_setkeys(iface, key, modifier);
+ exit(0);
+ }
+
+ wi_dumpinfo(iface);
+
+ exit(0);
+}
diff --git a/usr.sbin/wlconfig/Makefile b/usr.sbin/wlconfig/Makefile
new file mode 100644
index 0000000..0cd34c0
--- /dev/null
+++ b/usr.sbin/wlconfig/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+PROG= wlconfig
+MAN= wlconfig.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/wlconfig/wlconfig.8 b/usr.sbin/wlconfig/wlconfig.8
new file mode 100644
index 0000000..cfbd4b3
--- /dev/null
+++ b/usr.sbin/wlconfig/wlconfig.8
@@ -0,0 +1,138 @@
+.\" $FreeBSD$
+.\"
+.Dd December 26, 1996
+.Os
+.Dt WLCONFIG 8
+.Sh NAME
+.Nm wlconfig
+.Nd read/write wavelan config parameters
+.Sh SYNOPSIS
+.Nm
+.Ar ifname
+.Op Ar param value ...
+.Sh DESCRIPTION
+The
+.Nm
+command can be used to read and set parameters for the NCR/AT&T Wavelan
+radio LAN card. Various parameters stored in the non-volatile Parameter
+Storage Area (PSA) on the card can be modified with this program, replacing
+the DOS-based
+.Nm instconf.exe
+program. It can also be used to interrogate the optional signal
+strength cache which may have been compiled into the driver.
+.Pp
+The
+.Ar ifname
+parameter specifies the wavelan interface name (eg.
+.Pa wl0 ) .
+If no other arguments are supplied, the current contents of the PSA
+are interpreted and displayed.
+.Pp
+The
+.Ar param
+and
+.Ar value
+arguments can be used to change the value of several parameters.
+Any number of
+.Ar param value
+pairs may be supplied.
+.Bl -tag -width 15n -offset indent
+.It Va param
+.Va value
+.It irq
+IRQ value (used at next reset), may be one of 3,4,5,6,10,11,12,15.
+.It mac
+Local MAC value (ethernet address).
+.It macsel
+.Sq soft
+(as set by the
+.Sq mac
+parameter) or
+.Sq default
+(as set at the factory).
+.It nwid
+The NWID is a 2-byte parameter passed to the card's radio modem.
+NWIDs allow multiple logically discrete networks to operate
+independently whilst occupying the same airspace.
+Packets with a different NWID are simply ignored by the modem.
+In the hardware, NWIDs are stored long-term in non-volatile memory
+(called the PSA or programmable storage area), and are loaded by
+software into the radio modem when the driver is
+initialized. This sets the default NWID loaded at startup.
+.It currnwid
+This sets the current operating NWID (but does not save it to the
+PSA).
+.It cache
+The driver may maintain a per interface fixed size cache of signal strength,
+silence, and quality levels, which are indexed by sender MAC addresses.
+Input packets are stored in the cache, and when received, the values
+stored in the radio modem are interrogated and stored.
+There are also two sysctl values (iponly and multicast only) which
+can be used for filtering out some input packets. By default, the
+cache mechanism stores only non-unicast IP packets, but this can
+be changed with
+.Xr sysctl 8 .
+Each non-filtered
+input packet causes a cache update, hence one can monitor
+the antennae signal strength to a remote system.
+There are three commands that can be given as values:
+.Sq raw ,
+which prints out the raw signal strength data as found in the radio
+modem hardware value,
+.Sq scale ,
+which scales the raw hardware values to 0..100%, and
+.Sq zero
+which clears out the cache in case you want to store new samples.
+.El
+.Pp
+Note that if the IRQ on the Wavelan card is incorrect, the interface
+will be configured, but will not function. The
+.Nm
+program should then be used to reconfigure the card to a sensible
+value.
+.Sh EXAMPLES
+Set the NWID to 0x1234:
+.Bd -literal -offset
+# wlconfig wl0 nwid 0x1234
+.Ed
+.Pp
+Show the current settings:
+.Bd -literal -offset
+# wlconfig wl0
+Board type : ISA
+Base address options : 0x300, 0x390, 0x3c0, 0x3e0
+Waitstates : 0
+Bus mode : ISA
+IRQ : 10
+Default MAC address : 08:00:0e:20:3d:4b
+Soft MAC address : 00:00:00:00:00:00
+Current MAC address : Default
+Adapter compatibility : PC-AT 2.4GHz
+Threshold preset : 1
+Call code required : NO
+Subband : 2425MHz
+Quality threshold : 3
+Hardware version : 0 (Rel1/Rel2)
+Network ID enable : YES
+NWID : 0xdead
+Datalink security : NO
+Databus width : 16 (variable)
+Configuration state : unconfigured
+CRC-16 : 0x3c26
+CRC status : OK
+.Ed
+.Pp
+Print a scaled version of the signal strength cache:
+.Bd -literal -offset
+# wlconfig wl0 cache scale
+.Ed
+.Sh SEE ALSO
+.Xr wl 4 ,
+.Xr sysctl 8
+.Sh HISTORY
+This implementation of the
+.Nm
+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
new file mode 100644
index 0000000..f361b2c
--- /dev/null
+++ b/usr.sbin/wlconfig/wlconfig.c
@@ -0,0 +1,417 @@
+/*
+ * Copyright (C) 1996
+ * Michael Smith. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Michael Smith AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Michael Smith OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/*
+ * wlconfig.c
+ *
+ * utility to read out and change various WAVELAN parameters.
+ * Currently supports NWID and IRQ values.
+ *
+ * The NWID is used by 2 or more wavelan controllers to determine
+ * if packets should be received or not. It is a filter that
+ * is roughly analogous to the "channel" setting with a garage
+ * door controller. Two companies side by side with wavelan devices
+ * that could actually hear each other can use different NWIDs
+ * and ignore packets. In truth however, the air space is shared,
+ * and the NWID is a virtual filter.
+ *
+ * In the current set of wavelan drivers, ioctls changed only
+ * the runtime radio modem registers which act in a manner analogous
+ * to an ethernet transceiver. The ioctls do not change the
+ * stored nvram PSA (or parameter storage area). At boot, the PSA
+ * values are stored in the radio modem. Thus when the
+ * system reboots it will restore the wavelan NWID to the value
+ * stored in the PSA. The NCR/ATT dos utilities must be used to
+ * change the initial NWID values in the PSA. The wlconfig utility
+ * may be used to set a different NWID at runtime; this is only
+ * permitted while the interface is up and running.
+ *
+ * By contrast, the IRQ value can only be changed while the
+ * Wavelan card is down and unconfigured, and it will remain
+ * disabled after an IRQ change until reboot.
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <machine/if_wl_wavelan.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+
+/* translate IRQ bit to number */
+/* array for maping irq numbers to values for the irq parameter register */
+static int irqvals[16] = {
+ 0, 0, 0, 0x01, 0x02, 0x04, 0, 0x08, 0, 0, 0x10, 0x20, 0x40, 0, 0, 0x80
+};
+
+/* cache */
+static int w_sigitems; /* count of valid items */
+static struct w_sigcache wsc[MAXCACHEITEMS];
+
+int
+wlirq(int irqval)
+{
+ int irq;
+
+ for(irq = 0; irq < 16; irq++)
+ if(irqvals[irq] == irqval)
+ return(irq);
+ return 0;
+}
+
+char *compat_type[] = {
+ "PC-AT 915MHz",
+ "PC-MC 915MHz",
+ "PC-AT 2.4GHz",
+ "PC-MC 2.4GHz",
+ "PCCARD or 1/2 size AT, 915MHz or 2.4GHz"
+};
+
+char *subband[] = {
+ "915MHz/see WaveModem",
+ "2425MHz",
+ "2460MHz",
+ "2484MHz",
+ "2430.5MHz"
+};
+
+
+/*
+** print_psa
+**
+** Given a pointer to a PSA structure, print it out
+*/
+void
+print_psa(u_char *psa, int currnwid)
+{
+ int nwid;
+
+ /*
+ ** Work out what sort of board we have
+ */
+ if (psa[0] == 0x14) {
+ printf("Board type : Microchannel\n");
+ } else {
+ if (psa[1] == 0) {
+ printf("Board type : PCCARD\n");
+ } else {
+ printf("Board type : ISA");
+ if ((psa[4] == 0) &&
+ (psa[5] == 0) &&
+ (psa[6] == 0))
+ printf(" (DEC OEM)");
+ printf("\n");
+ printf("Base address options : 0x300, 0x%02x0, 0x%02x0, 0x%02x0\n",
+ (int)psa[1], (int)psa[2], (int)psa[3]);
+ printf("Waitstates : %d\n",psa[7] & 0xf);
+ printf("Bus mode : %s\n",(psa[7] & 0x10) ? "EISA" : "ISA");
+ printf("IRQ : %d\n",wlirq(psa[8]));
+ }
+ }
+ printf("Default MAC address : %02x:%02x:%02x:%02x:%02x:%02x\n",
+ psa[0x10],psa[0x11],psa[0x12],psa[0x13],psa[0x14],psa[0x15]);
+ printf("Soft MAC address : %02x:%02x:%02x:%02x:%02x:%02x\n",
+ psa[0x16],psa[0x17],psa[0x18],psa[0x19],psa[0x1a],psa[0x1b]);
+ printf("Current MAC address : %s\n",(psa[0x1c] & 0x1) ? "Soft" : "Default");
+ printf("Adapter compatibility : ");
+ if (psa[0x1d] < 5) {
+ printf("%s\n",compat_type[psa[0x1d]]);
+ } else {
+ printf("unknown\n");
+ }
+ printf("Threshold preset : %d\n",psa[0x1e]);
+ printf("Call code required : %s\n",(psa[0x1f] & 0x1) ? "YES" : "NO");
+ if (psa[0x1f] & 0x1)
+ printf("Call code : 0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
+ psa[0x30],psa[0x31],psa[0x32],psa[0x33],psa[0x34],psa[0x35],psa[0x36],psa[0x37]);
+ printf("Subband : %s\n",subband[psa[0x20] & 0xf]);
+ printf("Quality threshold : %d\n",psa[0x21]);
+ printf("Hardware version : %d (%s)\n",psa[0x22],psa[0x22] ? "Rel3" : "Rel1/Rel2");
+ printf("Network ID enable : %s\n",(psa[0x25] & 0x1) ? "YES" : "NO");
+ if (psa[0x25] & 0x1) {
+ nwid = (psa[0x23] << 8) + psa[0x24];
+ printf("NWID : 0x%04x\n",nwid);
+ if (nwid != currnwid) {
+ printf("Current NWID : 0x%04x\n",currnwid);
+ }
+ }
+ printf("Datalink security : %s\n",(psa[0x26] & 0x1) ? "YES" : "NO");
+ if (psa[0x26] & 0x1) {
+ printf("Encryption key : ");
+ if (psa[0x27] == 0) {
+ printf("DENIED\n");
+ } else {
+ printf("0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
+ psa[0x27],psa[0x28],psa[0x29],psa[0x2a],psa[0x2b],psa[0x2c],psa[0x2d],psa[0x2e]);
+ }
+ }
+ printf("Databus width : %d (%s)\n",
+ (psa[0x2f] & 0x1) ? 16 : 8, (psa[0x2f] & 0x80) ? "fixed" : "variable");
+ printf("Configuration state : %sconfigured\n",(psa[0x38] & 0x1) ? "" : "un");
+ printf("CRC-16 : 0x%02x%02x\n",psa[0x3e],psa[0x3d]);
+ printf("CRC status : ");
+ switch(psa[0x3f]) {
+ case 0xaa:
+ printf("OK\n");
+ break;
+ case 0x55:
+ printf("BAD\n");
+ break;
+ default:
+ printf("Error\n");
+ break;
+ }
+}
+
+
+static void
+usage()
+{
+ fprintf(stderr,"usage: wlconfig ifname [param value ...]\n");
+ exit(1);
+}
+
+
+void
+get_cache(int sd, struct ifreq *ifr)
+{
+ /* get the cache count */
+ if (ioctl(sd, SIOCGWLCITEM, (caddr_t)ifr))
+ err(1, "SIOCGWLCITEM - get cache count");
+ w_sigitems = (int) ifr->ifr_data;
+
+ ifr->ifr_data = (caddr_t) &wsc;
+ /* get the cache */
+ if (ioctl(sd, SIOCGWLCACHE, (caddr_t)ifr))
+ err(1, "SIOCGWLCACHE - get cache count");
+}
+
+static int
+scale_value(int value, int max)
+{
+ double dmax = (double) max;
+ if (value > max)
+ return(100);
+ return((value/dmax) * 100);
+}
+
+static void
+dump_cache(int rawFlag)
+{
+ int i;
+ int signal, silence, quality;
+
+ if (rawFlag)
+ printf("signal range 0..63: silence 0..63: quality 0..15\n");
+ else
+ printf("signal range 0..100: silence 0..100: quality 0..100\n");
+
+ /* after you read it, loop through structure,i.e. wsc
+ * print each item:
+ */
+ for(i = 0; i < w_sigitems; i++) {
+ printf("[%d:%d]>\n", i+1, w_sigitems);
+ printf("\tip: %d.%d.%d.%d,",((wsc[i].ipsrc >> 0) & 0xff),
+ ((wsc[i].ipsrc >> 8) & 0xff),
+ ((wsc[i].ipsrc >> 16) & 0xff),
+ ((wsc[i].ipsrc >> 24) & 0xff));
+ printf(" mac: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ wsc[i].macsrc[0]&0xff,
+ wsc[i].macsrc[1]&0xff,
+ wsc[i].macsrc[2]&0xff,
+ wsc[i].macsrc[3]&0xff,
+ wsc[i].macsrc[4]&0xff,
+ wsc[i].macsrc[5]&0xff);
+ if (rawFlag) {
+ signal = wsc[i].signal;
+ silence = wsc[i].silence;
+ quality = wsc[i].quality;
+ }
+ else {
+ signal = scale_value(wsc[i].signal, 63);
+ silence = scale_value(wsc[i].silence, 63);
+ quality = scale_value(wsc[i].quality, 15);
+ }
+ printf("\tsignal: %d, silence: %d, quality: %d, ",
+ signal,
+ silence,
+ quality);
+ printf("snr: %d\n", signal - silence);
+ }
+}
+
+#define raw_cache() dump_cache(1)
+#define scale_cache() dump_cache(0)
+
+int
+main(int argc, char *argv[])
+{
+ int sd;
+ struct ifreq ifr;
+ u_char psabuf[0x40];
+ int val, argind, i;
+ char *cp, *param, *value;
+ struct ether_addr *ea;
+ int work = 0;
+ int currnwid;
+
+ if ((argc < 2) || (argc % 2))
+ usage();
+
+ /* get a socket */
+ sd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sd < 0)
+ err(1,"socket");
+ strncpy(ifr.ifr_name, argv[1], sizeof(ifr.ifr_name));
+ ifr.ifr_addr.sa_family = AF_INET;
+
+ /* get the PSA */
+ ifr.ifr_data = (caddr_t)psabuf;
+ if (ioctl(sd, SIOCGWLPSA, (caddr_t)&ifr))
+ err(1,"get PSA");
+
+ /* get the current NWID */
+ if (ioctl(sd, SIOCGWLCNWID, (caddr_t)&ifr))
+ err(1,"get NWID");
+ currnwid = (int)ifr.ifr_data;
+
+ /* just dump and exit? */
+ if (argc == 2) {
+ print_psa(psabuf, currnwid);
+ exit(0);
+ }
+
+ /* loop reading arg pairs */
+ for (argind = 2; argind < argc; argind += 2) {
+
+ param = argv[argind];
+ value = argv[argind+1];
+
+ /* What to do? */
+
+ if (!strcasecmp(param,"currnwid")) { /* set current NWID */
+ val = strtol(value,&cp,0);
+ if ((val < 0) || (val > 0xffff) || (cp == value))
+ errx(1,"bad NWID '%s'",value);
+
+ ifr.ifr_data = (caddr_t)val;
+ if (ioctl(sd, SIOCSWLCNWID, (caddr_t)&ifr))
+ err(1,"set NWID (interface not up?)");
+ continue ;
+ }
+
+ if (!strcasecmp(param,"irq")) {
+ val = strtol(value,&cp,0);
+ val = irqvals[val];
+ if ((val == 0) || (cp == value))
+ errx(1,"bad IRQ '%s'",value);
+ psabuf[WLPSA_IRQNO] = (u_char)val;
+ work = 1;
+ continue;
+ }
+
+ if (!strcasecmp(param,"mac")) {
+ if ((ea = ether_aton(value)) == NULL)
+ errx(1,"bad ethernet address '%s'",value);
+ for (i = 0; i < 6; i++)
+ psabuf[WLPSA_LOCALMAC + i] = ea->octet[i];
+ work = 1;
+ continue;
+ }
+
+ if (!strcasecmp(param,"macsel")) {
+ if (!strcasecmp(value,"local")) {
+ psabuf[WLPSA_MACSEL] |= 0x1;
+ work = 1;
+ continue;
+ }
+ if (!strcasecmp(value,"universal")) {
+ psabuf[WLPSA_MACSEL] &= ~0x1;
+ work = 1;
+ continue;
+ }
+ errx(1,"bad macsel value '%s'",value);
+ }
+
+ if (!strcasecmp(param,"nwid")) {
+ val = strtol(value,&cp,0);
+ if ((val < 0) || (val > 0xffff) || (cp == value))
+ errx(1,"bad NWID '%s'",value);
+ psabuf[WLPSA_NWID] = (val >> 8) & 0xff;
+ psabuf[WLPSA_NWID+1] = val & 0xff;
+ work = 1;
+ continue;
+ }
+ if (!strcasecmp(param,"cache")) {
+
+ /* raw cache dump
+ */
+ if (!strcasecmp(value,"raw")) {
+ get_cache(sd, &ifr);
+ raw_cache();
+ continue;
+ }
+ /* scaled cache dump
+ */
+ else if (!strcasecmp(value,"scale")) {
+ get_cache(sd, &ifr);
+ scale_cache();
+ continue;
+ }
+ /* zero out cache
+ */
+ else if (!strcasecmp(value,"zero")) {
+ if (ioctl(sd, SIOCDWLCACHE, (caddr_t)&ifr))
+ err(1,"zero cache");
+ continue;
+ }
+ errx(1,"unknown value '%s'", value);
+ }
+ errx(1,"unknown parameter '%s'",param);
+ }
+ if (work) {
+ ifr.ifr_data = (caddr_t)psabuf;
+ if (ioctl(sd, SIOCSWLPSA, (caddr_t)&ifr))
+ err(1,"set PSA");
+ }
+ return(0);
+}
diff --git a/usr.sbin/xten/Makefile b/usr.sbin/xten/Makefile
new file mode 100644
index 0000000..0475200
--- /dev/null
+++ b/usr.sbin/xten/Makefile
@@ -0,0 +1,8 @@
+# Makefile for xten (Stark) 10/30/93
+# $FreeBSD$
+
+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..45a37e3
--- /dev/null
+++ b/usr.sbin/xten/xten.1
@@ -0,0 +1,115 @@
+.\" 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd October 30, 1993
+.Dt XTEN 1
+.Os
+.Sh NAME
+.Nm xten
+.Nd transmit X-10 commands
+.Sh SYNOPSIS
+.Nm
+.Op Fl ""
+.Ar house
+.Ar key Ns Op : Ns Ar cnt
+.Oo
+.Op Ar house
+.Ar key Ns Op : Ns Ar cnt
+.Ar ...
+.Oc
+.Sh DESCRIPTION
+The
+.Nm
+utility 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..f4c2019
--- /dev/null
+++ b/usr.sbin/xten/xten.c
@@ -0,0 +1,180 @@
+/*-
+ * 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[] =
+ "$FreeBSD$";
+#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(char *, char *[]);
+static void usage(void) __dead2;
+
+int
+main(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(void)
+{
+ fprintf(stderr,
+ "usage: xten house key[:cnt] [[house] key[:cnt] ...]\n");
+ exit(1);
+}
+
+int
+find(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..54cc30c
--- /dev/null
+++ b/usr.sbin/yp_mkdb/Makefile
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../libexec/ypxfr ${.CURDIR}/../ypserv
+
+PROG= yp_mkdb
+MAN= yp_mkdb.8
+SRCS= yp_mkdb.c yp_dblookup.c yp_dbwrite.c
+
+CFLAGS+= -Dyp_error=warnx -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..f0d3651
--- /dev/null
+++ b/usr.sbin/yp_mkdb/yp_mkdb.8
@@ -0,0 +1,205 @@
+.\" Copyright (c) 1995, 1996
+.\" Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Bill Paul
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd March 12, 1996
+.Dt YP_MKDB 8
+.Os
+.Sh NAME
+.Nm yp_mkdb
+.Nd "generate the NIS databases"
+.Sh SYNOPSIS
+.Nm
+.Fl c
+.Nm
+.Fl u Ar dbname
+.Nm
+.Op Fl c
+.Op Fl b
+.Op Fl s
+.Op Fl f
+.Op Fl i Ar inputfile
+.Op Fl o Ar outputfile
+.Op Fl d Ar domainname
+.Op Fl m Ar mastername
+.Ar inputfile
+.Ar dbname
+.Sh DESCRIPTION
+.Nm Yp_mkdb
+creates
+.Xr db 3
+style databases for use with
+.Fx Ns '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).
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl c
+Cause
+.Nm
+to send a YPPROC_CLEAR request to
+.Xr ypserv 8
+on the local host.
+This signal tells the server to close any open
+database descriptors and flush out its database cache.
+If used alone,
+this flag signals the server and does nothing else.
+If used as part
+of a database creation command,
+.Nm
+will send the signal only after the new database has been successfully
+created.
+.It Fl b
+Cause
+.Nm
+to add a special entry to the database with a key of
+.Em YP_INTERDOMAIN
+and an empty data field.
+If this key is present in a map, it alters the
+behavior of the 'match' procedure in
+.Xr ypserv 8
+slightly.
+If a match query fails (because the server couldn't find
+a record that matched the supplied key), and the
+.Em YP_INTERDOMAIN
+key exists within the queried map,
+.Xr ypserv 8
+will try to match the entry again using a DNS lookup.
+Note that this
+special behavior only applies to the
+.Em hosts
+maps.
+Using the
+.Fl b
+flag for other maps has no effect.
+.It Fl s
+This flag is used to add a special entry to the database with a key of
+.Em YP_SECURE
+and an empty data field.
+If this key is present in a map,
+.Xr ypserv 8
+will deny access to the map to any client that is not using a
+reserved port for its query.
+This is used mainly for the
+.Em master.passwd
+maps, which should be restricted to privileged access only.
+.It Fl f
+This flag is used to turn on filtering of lines in the source file
+input that start with ``+'' or ``-'' characters.
+These characters
+have special meaning for the
+.Pa group ,
+.Pa passwd
+and
+.Pa master.passwd
+maps and hence should not be allowed to appear in them as the first
+character of a key or datum.
+If the
+.Fl f
+flag is used,
+.Nm
+will reject any source line that starts with a ``+'' or ``-''
+character and issue a warning message displaying the line that
+was dropped.
+.It Fl u Ar dbname
+Dump (or 'unwind') an NIS database.
+This option can be used to
+inspect the contents of an existing NIS database.
+.It Fl i Ar inputfile
+When generating an NIS map, encode
+.Ar inputfile
+as a special entry in the database with a key of
+.Em YP_INPUT_FILE .
+.It Fl o Ar outputfile
+When generating an NIS map, encode
+.Ar outputfile
+as a special entry in the database with a key of
+.Em YP_OUTPUT_FILE .
+.It Fl d Ar domainname
+When generating an NIS map, encode
+.Ar domainname
+as a special entry in the database with a key of
+.Em YP_DOMAIN_NAME .
+.It Fl m Ar mastername
+When generating an NIS map, encode
+.Ar mastername
+as a special entry in the database with a key of
+.Em YP_MASTER_NAME .
+This entry in the database is frequently used by various NIS utilities
+to determine the name of an NIS master server for a domain.
+By default,
+.Nm
+assumes that the local host is the NIS master; the
+.Fl m
+option is used to override this default.
+.El
+.Sh FILES
+.Bl -tag -width /var/yp/Makefile -compact
+.It Pa /var/yp/Makefile
+the Makefile that calls
+.Nm
+to build the NIS databases
+.El
+.Sh SEE ALSO
+.Xr db 3 ,
+.Xr ypserv 8
+.Sh AUTHORS
+.An Bill Paul Aq wpaul@ctr.columbia.edu
diff --git a/usr.sbin/yp_mkdb/yp_mkdb.c b/usr.sbin/yp_mkdb/yp_mkdb.c
new file mode 100644
index 0000000..611218e
--- /dev/null
+++ b/usr.sbin/yp_mkdb/yp_mkdb.c
@@ -0,0 +1,344 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#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(void)
+{
+ fprintf(stderr, "%s\n%s\n%s\n%s\n",
+ "usage: yp_mkdb -c",
+ " yp_mkdb -u dbname",
+ " yp_mkdb [-c] [-b] [-s] [-f] [-i inputfile] [-o outputfile]",
+ " [-d domainname ] [-m mastername] inputfile dbname");
+ exit(1);
+}
+
+#define PERM_SECURE (S_IRUSR|S_IWUSR)
+
+static DB *
+open_db(char *path, int flags)
+{
+ extern HASHINFO openinfo;
+
+ return(dbopen(path, flags, PERM_SECURE, DB_HASH, &openinfo));
+}
+
+static void
+unwind(char *map)
+{
+ DB *dbp;
+ DBT key, data;
+
+ dbp = open_db(map, O_RDONLY);
+
+ if (dbp == NULL)
+ err(1, "open_db(%s) failed", map);
+
+ key.data = NULL;
+ while (yp_next_record(dbp, &key, &data, 1, 1) == YP_TRUE)
+ printf("%.*s %.*s\n", key.size,key.data,data.size,data.data);
+
+ (void)(dbp->close)(dbp);
+ return;
+}
+
+int
+main(int argc, char *argv[])
+{
+ int ch;
+ int un = 0;
+ int clear = 0;
+ int filter_plusminus = 0;
+ char *infile = NULL;
+ char *map = NULL;
+ char *domain = NULL;
+ char *infilename = NULL;
+ char *outfilename = NULL;
+ char *mastername = NULL;
+ int interdom = 0;
+ int secure = 0;
+ DB *dbp;
+ DBT key, data;
+ char buf[10240];
+ char *keybuf, *datbuf;
+ FILE *ifp;
+ char hname[MAXHOSTNAMELEN + 2];
+
+ while ((ch = getopt(argc, argv, "uhcbsfd:i:o:m:")) != -1) {
+ switch (ch) {
+ case 'f':
+ filter_plusminus++;
+ break;
+ case 'u':
+ un++;
+ break;
+ case 'c':
+ clear++;
+ break;
+ case 'b':
+ interdom++;
+ break;
+ case 's':
+ secure++;
+ break;
+ case 'd':
+ domain = optarg;
+ break;
+ case 'i':
+ infilename = optarg;
+ break;
+ case 'o':
+ outfilename = optarg;
+ break;
+ case 'm':
+ mastername = optarg;
+ break;
+ case 'h':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (un) {
+ map = argv[0];
+ if (map == NULL)
+ usage();
+ unwind(map);
+ exit(0);
+
+ }
+
+ infile = argv[0];
+ map = argv[1];
+
+ if (infile == NULL || map == NULL) {
+ if (clear)
+ goto doclear;
+ usage();
+ }
+
+ if (mastername == NULL) {
+ if (gethostname((char *)&hname, sizeof(hname)) == -1)
+ err(1, "gethostname() failed");
+ mastername = (char *)&hname;
+ }
+
+ /*
+ * Note that while we can read from stdin, we can't
+ * write to stdout; the db library doesn't let you
+ * write to a file stream like that.
+ */
+
+ if (!strcmp(infile, "-")) {
+ ifp = stdin;
+ } else {
+ if ((ifp = fopen(infile, "r")) == NULL)
+ err(1, "failed to open %s", infile);
+ }
+
+ if ((dbp = open_db(map, O_RDWR|O_EXLOCK|O_EXCL|O_CREAT)) == NULL)
+ err(1, "open_db(%s) failed", map);
+
+ if (interdom) {
+ key.data = "YP_INTERDOMAIN";
+ key.size = sizeof("YP_INTERDOMAIN") - 1;
+ data.data = "";
+ data.size = 0;
+ yp_put_record(dbp, &key, &data, 0);
+ }
+
+ if (secure) {
+ key.data = "YP_SECURE";
+ key.size = sizeof("YP_SECURE") - 1;
+ data.data = "";
+ data.size = 0;
+ yp_put_record(dbp, &key, &data, 0);
+ }
+
+ key.data = "YP_MASTER_NAME";
+ key.size = sizeof("YP_MASTER_NAME") - 1;
+ data.data = mastername;
+ data.size = strlen(mastername);
+ yp_put_record(dbp, &key, &data, 0);
+
+ key.data = "YP_LAST_MODIFIED";
+ key.size = sizeof("YP_LAST_MODIFIED") - 1;
+ snprintf(buf, sizeof(buf), "%lu", time(NULL));
+ data.data = (char *)&buf;
+ data.size = strlen(buf);
+ yp_put_record(dbp, &key, &data, 0);
+
+ if (infilename) {
+ key.data = "YP_INPUT_FILE";
+ key.size = sizeof("YP_INPUT_FILE") - 1;
+ data.data = infilename;
+ data.size = strlen(infilename);
+ yp_put_record(dbp, &key, &data, 0);
+ }
+
+ if (outfilename) {
+ key.data = "YP_OUTPUT_FILE";
+ key.size = sizeof("YP_OUTPUT_FILE") - 1;
+ data.data = outfilename;
+ data.size = strlen(outfilename);
+ yp_put_record(dbp, &key, &data, 0);
+ }
+
+ if (domain) {
+ key.data = "YP_DOMAIN_NAME";
+ key.size = sizeof("YP_DOMAIN_NAME") - 1;
+ data.data = domain;
+ data.size = strlen(domain);
+ yp_put_record(dbp, &key, &data, 0);
+ }
+
+ while (fgets((char *)&buf, sizeof(buf), ifp)) {
+ char *sep = NULL;
+ int rval;
+
+ /* NUL terminate */
+ if ((sep = strchr(buf, '\n')))
+ *sep = '\0';
+
+ /* handle backslash line continuations */
+ while (buf[strlen(buf) - 1] == '\\') {
+ fgets((char *)&buf[strlen(buf) - 1],
+ sizeof(buf) - strlen(buf), ifp);
+ if ((sep = strchr(buf, '\n')))
+ *sep = '\0';
+ }
+
+ /* find the separation between the key and data */
+ if ((sep = strpbrk(buf, " \t")) == NULL) {
+ warnx("bad input -- no white space: %s", buf);
+ continue;
+ }
+
+ /* separate the strings */
+ keybuf = (char *)&buf;
+ datbuf = sep + 1;
+ *sep = '\0';
+
+ /* set datbuf to start at first non-whitespace character */
+ while (*datbuf == ' ' || *datbuf == '\t')
+ datbuf++;
+
+ /* Check for silliness. */
+ if (filter_plusminus) {
+ if (*keybuf == '+' || *keybuf == '-' ||
+ *datbuf == '+' || *datbuf == '-') {
+ warnx("bad character at "
+ "start of line: %s", buf);
+ continue;
+ }
+ }
+
+ if (strlen(keybuf) > YPMAXRECORD) {
+ warnx("key too long: %s", keybuf);
+ continue;
+ }
+
+ if (!strlen(keybuf)) {
+ warnx("no key -- check source file for blank lines");
+ continue;
+ }
+
+ if (strlen(datbuf) > YPMAXRECORD) {
+ warnx("data too long: %s", datbuf);
+ continue;
+ }
+
+ key.data = keybuf;
+ key.size = strlen(keybuf);
+ data.data = datbuf;
+ data.size = strlen(datbuf);
+
+ if ((rval = yp_put_record(dbp, &key, &data, 0)) != YP_TRUE) {
+ switch (rval) {
+ case YP_FALSE:
+ warnx("duplicate key '%s' - skipping", keybuf);
+ break;
+ case YP_BADDB:
+ default:
+ err(1,"failed to write new record - exiting");
+ break;
+ }
+ }
+
+ }
+
+ (void)(dbp->close)(dbp);
+
+doclear:
+
+ if (clear) {
+ char in = 0;
+ char *out = NULL;
+ int stat;
+ if ((stat = callrpc("localhost", YPPROG,YPVERS, YPPROC_CLEAR,
+ (xdrproc_t)xdr_void, &in,
+ (xdrproc_t)xdr_void, out)) != RPC_SUCCESS) {
+ warnx("failed to send 'clear' to local ypserv: %s",
+ clnt_sperrno((enum clnt_stat) stat));
+ }
+ }
+
+ exit(0);
+}
diff --git a/usr.sbin/ypbind/Makefile b/usr.sbin/ypbind/Makefile
new file mode 100644
index 0000000..915d001
--- /dev/null
+++ b/usr.sbin/ypbind/Makefile
@@ -0,0 +1,10 @@
+# from: @(#)Makefile 5.8 (Berkeley) 7/28/90
+# $FreeBSD$
+
+PROG= ypbind
+MAN= ypbind.8
+SRCS= ypbind.c yp_ping.c
+
+CFLAGS+= -DDAEMON
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ypbind/yp_ping.c b/usr.sbin/ypbind/yp_ping.c
new file mode 100644
index 0000000..9e77d16
--- /dev/null
+++ b/usr.sbin/ypbind/yp_ping.c
@@ -0,0 +1,328 @@
+/*
+ * Copyright (c) 1996, 1997
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+#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[] =
+ "$FreeBSD$";
+#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 <pthread.h>
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+#include <rpc/pmap_prot.h>
+#include <rpcsvc/yp.h>
+#include <sys/types.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+#include <sys/signal.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+
+#include "yp_ping.h"
+
+struct cu_data {
+ int cu_fd; /* connections fd */
+ bool_t cu_closeit; /* opened by library */
+ struct sockaddr_storage cu_raddr; /* remote address */
+ int cu_rlen;
+ struct timeval cu_wait; /* retransmit interval */
+ struct timeval cu_total; /* total time for the call */
+ struct rpc_err cu_error;
+ XDR cu_outxdrs;
+ u_int cu_xdrpos;
+ u_int cu_sendsz; /* send size */
+ char *cu_outbuf;
+ u_int cu_recvsz; /* recv size */
+ struct pollfd pfdp;
+ int cu_async;
+ char cu_inbuf[1];
+};
+
+/*
+ * pmap_getport.c
+ * Client interface to pmap rpc service.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+
+
+static struct timeval timeout = { 1, 0 };
+static struct timeval tottimeout = { 1, 0 };
+
+/*
+ * Find the mapped port for program,version.
+ * Calls the pmap service remotely to do the lookup.
+ * Returns 0 if no map exists.
+ */
+static u_short
+__pmap_getport(struct sockaddr_in *address, u_long program, u_long version,
+ u_int protocol)
+{
+ u_short port = 0;
+ int sock = -1;
+ register CLIENT *client;
+ struct pmap parms;
+
+ address->sin_port = htons(PMAPPORT);
+
+ client = clntudp_bufcreate(address, PMAPPROG,
+ PMAPVERS, timeout, &sock, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
+ if (client != (CLIENT *)NULL) {
+ parms.pm_prog = program;
+ parms.pm_vers = version;
+ parms.pm_prot = protocol;
+ parms.pm_port = 0; /* not needed or used */
+ if (CLNT_CALL(client, PMAPPROC_GETPORT,
+ (xdrproc_t)xdr_pmap, &parms,
+ (xdrproc_t)xdr_u_short, &port,
+ tottimeout) != RPC_SUCCESS){
+ rpc_createerr.cf_stat = RPC_PMAPFAILURE;
+ clnt_geterr(client, &rpc_createerr.cf_error);
+ } else if (port == 0) {
+ rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
+ }
+ CLNT_DESTROY(client);
+ }
+ if (sock != -1)
+ (void)close(sock);
+ address->sin_port = 0;
+ return (port);
+}
+
+/*
+ * Transmit to YPPROC_DOMAIN_NONACK, return immediately.
+ */
+static bool_t *
+ypproc_domain_nonack_2_send(domainname *argp, CLIENT *clnt)
+{
+ static bool_t clnt_res;
+ struct timeval TIMEOUT = { 0, 0 };
+
+ memset((char *)&clnt_res, 0, sizeof (clnt_res));
+ if (clnt_call(clnt, YPPROC_DOMAIN_NONACK,
+ (xdrproc_t) xdr_domainname, (caddr_t) argp,
+ (xdrproc_t) xdr_bool, (caddr_t) &clnt_res,
+ TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&clnt_res);
+}
+
+/*
+ * Receive response from YPPROC_DOMAIN_NONACK asynchronously.
+ */
+static bool_t *
+ypproc_domain_nonack_2_recv(domainname *argp, CLIENT *clnt)
+{
+ static bool_t clnt_res;
+ struct timeval TIMEOUT = { 0, 0 };
+
+ memset((char *)&clnt_res, 0, sizeof (clnt_res));
+ if (clnt_call(clnt, YPPROC_DOMAIN_NONACK,
+ (xdrproc_t) NULL, (caddr_t) argp,
+ (xdrproc_t) xdr_bool, (caddr_t) &clnt_res,
+ TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&clnt_res);
+}
+
+/*
+ * "We have the machine that goes 'ping!'" -- Monty Python
+ *
+ * This function blasts packets at the YPPROC_DOMAIN_NONACK procedures
+ * of the NIS servers listed in restricted_addrs structure.
+ * Whoever replies the fastest becomes our chosen server.
+ *
+ * Note: THIS IS NOT A BROADCAST OPERATION! We could use clnt_broadcast()
+ * for this, but that has the following problems:
+ * - We only get the address of the machine that replied in the
+ * 'eachresult' callback, and on multi-homed machines this can
+ * lead to confusion.
+ * - clnt_broadcast() only transmits to local networks, whereas with
+ * NIS+ you can have a perfectly good server located anywhere on or
+ * off the local network.
+ * - clnt_broadcast() blocks for an arbitrary amount of time which the
+ * caller can't control -- we want to avoid that.
+ *
+ * Also note that this has nothing to do with the NIS_PING procedure used
+ * for replica updates.
+ */
+
+struct ping_req {
+ struct sockaddr_in sin;
+ unsigned long xid;
+};
+
+int
+__yp_ping(struct in_addr *restricted_addrs, int cnt, char *dom, short *port)
+{
+ struct timeval tv = { 5, 0 };
+ struct ping_req **reqs;
+ unsigned long i;
+ int async;
+ struct sockaddr_in sin, *any = NULL;
+ int winner = -1;
+ time_t xid_seed, xid_lookup;
+ int sock, dontblock = 1;
+ CLIENT *clnt;
+ char *foo = dom;
+ struct cu_data *cu;
+ int validsrvs = 0;
+
+ /* Set up handles. */
+ reqs = calloc(1, sizeof(struct ping_req *) * cnt);
+ xid_seed = time(NULL) ^ getpid();
+
+ for (i = 0; i < cnt; i++) {
+ bzero((char *)&sin, sizeof(sin));
+ sin.sin_family = AF_INET;
+ bcopy((char *)&restricted_addrs[i],
+ (char *)&sin.sin_addr, sizeof(struct in_addr));
+ sin.sin_port = htons(__pmap_getport(&sin, YPPROG,
+ YPVERS, IPPROTO_UDP));
+ if (sin.sin_port == 0)
+ continue;
+ reqs[i] = calloc(1, sizeof(struct ping_req));
+ bcopy((char *)&sin, (char *)&reqs[i]->sin, sizeof(sin));
+ any = &reqs[i]->sin;
+ reqs[i]->xid = xid_seed;
+ xid_seed++;
+ validsrvs++;
+ }
+
+ /* Make sure at least one server was assigned */
+ if (!validsrvs) {
+ free(reqs);
+ return(-1);
+ }
+
+ /* Create RPC handle */
+ sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ clnt = clntudp_create(any, YPPROG, YPVERS, tv, &sock);
+ if (clnt == NULL) {
+ close(sock);
+ for (i = 0; i < cnt; i++)
+ if (reqs[i] != NULL)
+ free(reqs[i]);
+ free(reqs);
+ return(-1);
+ }
+ clnt->cl_auth = authunix_create_default();
+ cu = (struct cu_data *)clnt->cl_private;
+ tv.tv_sec = 0;
+
+ clnt_control(clnt, CLSET_TIMEOUT, (char *)&tv);
+ async = TRUE;
+ clnt_control(clnt, CLSET_ASYNC, (char *)&async);
+ ioctl(sock, FIONBIO, &dontblock);
+
+ /* Transmit */
+ for (i = 0; i < cnt; i++) {
+ if (reqs[i] != NULL) {
+ clnt_control(clnt, CLSET_XID, (char *)&reqs[i]->xid);
+ bcopy((char *)&reqs[i]->sin, (char *)&cu->cu_raddr,
+ sizeof(struct sockaddr_in));
+ ypproc_domain_nonack_2_send(&foo, clnt);
+ }
+ }
+
+ /* Receive reply */
+ ypproc_domain_nonack_2_recv(&foo, clnt);
+
+ /* Got a winner -- look him up. */
+ clnt_control(clnt, CLGET_XID, (char *)&xid_lookup);
+ for (i = 0; i < cnt; i++) {
+ if (reqs[i] != NULL && reqs[i]->xid == xid_lookup) {
+ winner = i;
+ *port = reqs[i]->sin.sin_port;
+ }
+ }
+
+ /* Shut everything down */
+ auth_destroy(clnt->cl_auth);
+ clnt_destroy(clnt);
+ close(sock);
+
+ for (i = 0; i < cnt; i++)
+ if (reqs[i] != NULL)
+ free(reqs[i]);
+ free(reqs);
+
+ return(winner);
+}
diff --git a/usr.sbin/ypbind/yp_ping.h b/usr.sbin/ypbind/yp_ping.h
new file mode 100644
index 0000000..80e6a78
--- /dev/null
+++ b/usr.sbin/ypbind/yp_ping.h
@@ -0,0 +1,5 @@
+/*
+ * $FreeBSD$
+ */
+
+extern int __yp_ping(struct in_addr *, int, char *, short *);
diff --git a/usr.sbin/ypbind/ypbind.8 b/usr.sbin/ypbind/ypbind.8
new file mode 100644
index 0000000..3c3d4f7
--- /dev/null
+++ b/usr.sbin/ypbind/ypbind.8
@@ -0,0 +1,203 @@
+.\" Copyright (c) 1991, 1993, 1995
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd April 9, 1995
+.Dt YPBIND 8
+.Os
+.Sh NAME
+.Nm ypbind
+.Nd "NIS domain binding daemon"
+.Sh SYNOPSIS
+.Nm
+.Op Fl ypset
+.Op Fl ypsetme
+.Op Fl s
+.Op Fl m
+.Oo
+.Fl S
+.Sm off
+.Ar domainname , server1 , server2 , ...
+.Sm on
+.Oc
+.Sh DESCRIPTION
+.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.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl ypset
+It is possible to force
+.Nm
+to bind to a particular NIS server host for a given domain by using the
+.Xr ypset 8
+command.
+However,
+.Nm
+refuses YPBINDPROC_SETDOM requests by default since it has no way of
+knowing exactly who is sending them.
+Using the
+.Fl ypset
+flag causes
+.Nm
+to accept YPBINDPROC_SETDOM requests from any host.
+This option should only
+be used for diagnostic purposes and only for limited periods since allowing
+arbitrary users to reset the binding of an NIS client poses a severe
+security risk.
+.It Fl ypsetme
+This is similar to the
+.Fl ypset
+flag, except that it only permits YPBINDPROC_SETDOM requests to be processed
+if they originated from the local host.
+.It Fl s
+Cause
+.Nm
+to run in secure mode: it will refuse to bind to any NIS server
+that is not running as root (i.e. that is not using privileged
+TCP ports).
+.It Fl S Xo
+.Sm off
+.Ar domainname , server1 , server2 , server3 , ...
+.Sm on
+.Xc
+Allow the system administrator to lock
+.Nm
+to a particular
+domain and group of NIS servers.
+Up to ten servers can be specified.
+There must not be any spaces between the commas in the domain/server
+specification.
+This option is used to insure that the system binds
+only to one domain and only to one of the specified servers, which
+is useful for systems that are both NIS servers and NIS
+clients: it provides a way to restrict what machines the system can
+bind to without the need for specifying the
+.Fl ypset
+or
+.Fl ypsetme
+options, which are often considered to be security holes.
+The specified
+servers must have valid entries in the local
+.Pa /etc/hosts
+file. IP addresses may be specified in place of hostnames.
+If
+.Nm
+can't make sense 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 8 ,
+.Xr ypserv 8 ,
+.Xr ypset 8
+.Sh AUTHORS
+.An Theo de Raadt Aq deraadt@fsa.ca
diff --git a/usr.sbin/ypbind/ypbind.c b/usr.sbin/ypbind/ypbind.c
new file mode 100644
index 0000000..3000e7f
--- /dev/null
+++ b/usr.sbin/ypbind/ypbind.c
@@ -0,0 +1,993 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#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(void);
+void *ypbindproc_null_2_yp(SVCXPRT *, void *, CLIENT *);
+void *ypbindproc_setdom_2_yp(SVCXPRT *, struct ypbind_setdom *, CLIENT *);
+void rpc_received(char *, struct sockaddr_in *, int);
+void broadcast(struct _dom_binding *);
+int ping(struct _dom_binding *);
+int tell_parent(char *, struct sockaddr_in *);
+void handle_children(struct _dom_binding *);
+void reaper(int);
+void terminate(int);
+void yp_restricted_mode(char *);
+int verify(struct in_addr);
+
+char *domain_name;
+struct _dom_binding *ypbindlist;
+static struct _dom_binding *broad_domain;
+
+#define YPSET_NO 0
+#define YPSET_LOCAL 1
+#define YPSET_ALL 2
+int ypsetmode = YPSET_NO;
+int ypsecuremode = 0;
+int ppid;
+
+/*
+ * Special restricted mode variables: when in restricted mode, only the
+ * specified restricted_domain will be bound, and only the servers listed
+ * in restricted_addrs will be used for binding.
+ */
+#define RESTRICTED_SERVERS 10
+int yp_restricted = 0;
+int yp_manycast = 0;
+struct in_addr restricted_addrs[RESTRICTED_SERVERS];
+
+/* No more than MAX_CHILDREN child broadcasters at a time. */
+#ifndef MAX_CHILDREN
+#define MAX_CHILDREN 5
+#endif
+/* No more than MAX_DOMAINS simultaneous domains */
+#ifndef MAX_DOMAINS
+#define MAX_DOMAINS 200
+#endif
+/* RPC timeout value */
+#ifndef FAIL_THRESHOLD
+#define FAIL_THRESHOLD 20
+#endif
+
+/* Number of times to fish for a response froma particular set of hosts */
+#ifndef MAX_RETRIES
+#define MAX_RETRIES 30
+#endif
+
+int retries = 0;
+int children = 0;
+int domains = 0;
+int yplockfd;
+fd_set fdsr;
+
+SVCXPRT *udptransp, *tcptransp;
+
+void *
+ypbindproc_null_2_yp(SVCXPRT *transp, void *argp, CLIENT *clnt)
+{
+ static char res;
+
+ bzero(&res, sizeof(res));
+ return &res;
+}
+
+struct ypbind_resp *
+ypbindproc_domain_2_yp(SVCXPRT *transp, domainname *argp, CLIENT *clnt)
+{
+ static struct ypbind_resp res;
+ struct _dom_binding *ypdb;
+ char path[MAXPATHLEN];
+
+ bzero(&res, sizeof res);
+ res.ypbind_status = YPBIND_FAIL_VAL;
+ res.ypbind_resp_u.ypbind_error = YPBIND_ERR_NOSERV;
+
+ if (strchr(*argp, '/')) {
+ syslog(LOG_WARNING, "Domain name '%s' has embedded slash -- \
+rejecting.", *argp);
+ return(&res);
+ }
+
+ for (ypdb = ypbindlist; ypdb; ypdb = ypdb->dom_pnext) {
+ if (strcmp(ypdb->dom_domain, *argp) == 0)
+ break;
+ }
+
+ if (ypdb == NULL) {
+ if (yp_restricted) {
+ syslog(LOG_NOTICE, "Running in restricted mode -- request to bind domain \"%s\" rejected.\n", *argp);
+ return (&res);
+ }
+
+ if (domains >= MAX_DOMAINS) {
+ syslog(LOG_WARNING, "domain limit (%d) exceeded",
+ MAX_DOMAINS);
+ res.ypbind_resp_u.ypbind_error = YPBIND_ERR_RESC;
+ return (&res);
+ }
+ ypdb = (struct _dom_binding *)malloc(sizeof *ypdb);
+ if (ypdb == NULL) {
+ syslog(LOG_WARNING, "malloc: %m");
+ res.ypbind_resp_u.ypbind_error = YPBIND_ERR_RESC;
+ return (&res);
+ }
+ bzero(ypdb, sizeof *ypdb);
+ strncpy(ypdb->dom_domain, *argp, sizeof ypdb->dom_domain);
+ ypdb->dom_vers = YPVERS;
+ ypdb->dom_alive = 0;
+ ypdb->dom_default = 0;
+ ypdb->dom_lockfd = -1;
+ sprintf(path, "%s/%s.%ld", BINDINGDIR,
+ ypdb->dom_domain, ypdb->dom_vers);
+ unlink(path);
+ ypdb->dom_pnext = ypbindlist;
+ ypbindlist = ypdb;
+ domains++;
+ }
+
+ if (ping(ypdb)) {
+ return (&res);
+ }
+
+ res.ypbind_status = YPBIND_SUCC_VAL;
+ res.ypbind_resp_u.ypbind_error = 0; /* Success */
+ *(u_int32_t *)&res.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_addr =
+ ypdb->dom_server_addr.sin_addr.s_addr;
+ *(u_short *)&res.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_port =
+ ypdb->dom_server_addr.sin_port;
+ /*printf("domain %s at %s/%d\n", ypdb->dom_domain,
+ inet_ntoa(ypdb->dom_server_addr.sin_addr),
+ ntohs(ypdb->dom_server_addr.sin_port));*/
+ return (&res);
+}
+
+void *
+ypbindproc_setdom_2_yp(SVCXPRT *transp, ypbind_setdom *argp, CLIENT *clnt)
+{
+ struct sockaddr_in *fromsin, bindsin;
+ static char *result = NULL;
+
+ if (strchr(argp->ypsetdom_domain, '/')) {
+ syslog(LOG_WARNING, "Domain name '%s' has embedded slash -- \
+rejecting.", argp->ypsetdom_domain);
+ return(NULL);
+ }
+ fromsin = svc_getcaller(transp);
+
+ switch (ypsetmode) {
+ case YPSET_LOCAL:
+ if (fromsin->sin_addr.s_addr != htonl(INADDR_LOOPBACK)) {
+ svcerr_noprog(transp);
+ return(NULL);
+ }
+ break;
+ case YPSET_ALL:
+ break;
+ case YPSET_NO:
+ default:
+ svcerr_noprog(transp);
+ return(NULL);
+ }
+
+ if (ntohs(fromsin->sin_port) >= IPPORT_RESERVED) {
+ svcerr_noprog(transp);
+ return(NULL);
+ }
+
+ if (argp->ypsetdom_vers != YPVERS) {
+ svcerr_noprog(transp);
+ return(NULL);
+ }
+
+ bzero(&bindsin, sizeof bindsin);
+ bindsin.sin_family = AF_INET;
+ bindsin.sin_addr.s_addr = *(u_int32_t *)argp->ypsetdom_binding.ypbind_binding_addr;
+ bindsin.sin_port = *(u_short *)argp->ypsetdom_binding.ypbind_binding_port;
+ rpc_received(argp->ypsetdom_domain, &bindsin, 1);
+
+ return((void *) &result);
+}
+
+void
+ypbindprog_2(struct svc_req *rqstp, register SVCXPRT *transp)
+{
+ union {
+ domainname ypbindproc_domain_2_arg;
+ struct ypbind_setdom ypbindproc_setdom_2_arg;
+ } argument;
+ struct authunix_parms *creds;
+ char *result;
+ bool_t (*xdr_argument)(), (*xdr_result)();
+ char *(*local)();
+
+ switch (rqstp->rq_proc) {
+ case YPBINDPROC_NULL:
+ xdr_argument = xdr_void;
+ xdr_result = xdr_void;
+ local = (char *(*)()) ypbindproc_null_2_yp;
+ break;
+
+ case YPBINDPROC_DOMAIN:
+ xdr_argument = xdr_domainname;
+ xdr_result = xdr_ypbind_resp;
+ local = (char *(*)()) ypbindproc_domain_2_yp;
+ break;
+
+ case YPBINDPROC_SETDOM:
+ switch (rqstp->rq_cred.oa_flavor) {
+ case AUTH_UNIX:
+ creds = (struct authunix_parms *)rqstp->rq_clntcred;
+ if (creds->aup_uid != 0) {
+ svcerr_auth(transp, AUTH_BADCRED);
+ return;
+ }
+ break;
+ default:
+ svcerr_auth(transp, AUTH_TOOWEAK);
+ return;
+ }
+
+ xdr_argument = xdr_ypbind_setdom;
+ xdr_result = xdr_void;
+ local = (char *(*)()) ypbindproc_setdom_2_yp;
+ break;
+
+ default:
+ svcerr_noproc(transp);
+ return;
+ }
+ bzero(&argument, sizeof(argument));
+ if (!svc_getargs(transp, (xdrproc_t)xdr_argument, &argument)) {
+ svcerr_decode(transp);
+ return;
+ }
+ result = (*local)(transp, &argument, rqstp);
+ if (result != NULL &&
+ !svc_sendreply(transp, (xdrproc_t)xdr_result, result)) {
+ svcerr_systemerr(transp);
+ }
+ return;
+}
+
+/* Jack the reaper */
+void
+reaper(int sig)
+{
+ int st;
+
+ while (wait3(&st, WNOHANG, NULL) > 0)
+ children--;
+}
+
+void
+terminate(int sig)
+{
+ struct _dom_binding *ypdb;
+ char path[MAXPATHLEN];
+
+ if (ppid != getpid())
+ exit(0);
+
+ for (ypdb = ypbindlist; ypdb; ypdb = ypdb->dom_pnext) {
+ close(ypdb->dom_lockfd);
+ if (ypdb->dom_broadcast_pid)
+ kill(ypdb->dom_broadcast_pid, SIGINT);
+ sprintf(path, "%s/%s.%ld", BINDINGDIR,
+ ypdb->dom_domain, ypdb->dom_vers);
+ unlink(path);
+ }
+ close(yplockfd);
+ unlink(YPBINDLOCK);
+ pmap_unset(YPBINDPROG, YPBINDVERS);
+ exit(0);
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct timeval tv;
+ int i;
+ DIR *dird;
+ struct dirent *dirp;
+ struct _dom_binding *ypdb, *next;
+
+ /* Check that another ypbind isn't already running. */
+ if ((yplockfd = (open(YPBINDLOCK, O_RDONLY|O_CREAT, 0444))) == -1)
+ err(1, "%s", YPBINDLOCK);
+
+ if (flock(yplockfd, LOCK_EX|LOCK_NB) == -1 && errno == EWOULDBLOCK)
+ errx(1, "another ypbind is already running. Aborting");
+
+ /* XXX domainname will be overriden if we use restricted mode */
+ yp_get_default_domain(&domain_name);
+ if (domain_name[0] == '\0')
+ errx(1, "domainname not set. Aborting");
+
+ for (i = 1; i<argc; i++) {
+ if (strcmp("-ypset", argv[i]) == 0)
+ ypsetmode = YPSET_ALL;
+ else if (strcmp("-ypsetme", argv[i]) == 0)
+ ypsetmode = YPSET_LOCAL;
+ else if (strcmp("-s", argv[i]) == 0)
+ ypsecuremode++;
+ else if (strcmp("-S", argv[i]) == 0 && argc > i)
+ yp_restricted_mode(argv[++i]);
+ else if (strcmp("-m", argv[i]) == 0)
+ yp_manycast++;
+ else
+ errx(1, "unknown option: %s", argv[i]);
+ }
+
+ /* blow away everything in BINDINGDIR (if it exists) */
+
+ if ((dird = opendir(BINDINGDIR)) != NULL) {
+ char path[MAXPATHLEN];
+ while ((dirp = readdir(dird)) != NULL)
+ if (strcmp(dirp->d_name, ".") &&
+ strcmp(dirp->d_name, "..")) {
+ sprintf(path,"%s/%s",BINDINGDIR,dirp->d_name);
+ unlink(path);
+ }
+ closedir(dird);
+ }
+
+#ifdef DAEMON
+ if (daemon(0,0))
+ err(1, "fork");
+#endif
+
+ pmap_unset(YPBINDPROG, YPBINDVERS);
+
+ udptransp = svcudp_create(RPC_ANYSOCK);
+ if (udptransp == NULL)
+ errx(1, "cannot create udp service");
+ if (!svc_register(udptransp, YPBINDPROG, YPBINDVERS, ypbindprog_2,
+ IPPROTO_UDP))
+ errx(1, "unable to register (YPBINDPROG, YPBINDVERS, udp)");
+
+ tcptransp = svctcp_create(RPC_ANYSOCK, 0, 0);
+ if (tcptransp == NULL)
+ errx(1, "cannot create tcp service");
+
+ if (!svc_register(tcptransp, YPBINDPROG, YPBINDVERS, ypbindprog_2,
+ IPPROTO_TCP))
+ errx(1, "unable to register (YPBINDPROG, YPBINDVERS, tcp)");
+
+ /* build initial domain binding, make it "unsuccessful" */
+ ypbindlist = (struct _dom_binding *)malloc(sizeof *ypbindlist);
+ if (ypbindlist == NULL)
+ errx(1, "malloc");
+ bzero(ypbindlist, sizeof *ypbindlist);
+ strncpy(ypbindlist->dom_domain, domain_name, sizeof ypbindlist->dom_domain);
+ ypbindlist->dom_vers = YPVERS;
+ ypbindlist->dom_alive = 0;
+ ypbindlist->dom_lockfd = -1;
+ ypbindlist->dom_default = 1;
+ domains++;
+
+ signal(SIGCHLD, reaper);
+ signal(SIGTERM, terminate);
+
+ ppid = getpid(); /* Remember who we are. */
+
+ openlog(argv[0], LOG_PID, LOG_DAEMON);
+
+ /* Kick off the default domain */
+ broadcast(ypbindlist);
+
+ while (1) {
+ fdsr = svc_fdset;
+
+ tv.tv_sec = 60;
+ tv.tv_usec = 0;
+
+ switch (select(_rpc_dtablesize(), &fdsr, NULL, NULL, &tv)) {
+ case 0:
+ checkwork();
+ break;
+ case -1:
+ if (errno != EINTR)
+ syslog(LOG_WARNING, "select: %m");
+ break;
+ default:
+ for (ypdb = ypbindlist; ypdb; ypdb = next) {
+ next = ypdb->dom_pnext;
+ if (READFD > 0 && FD_ISSET(READFD, &fdsr)) {
+ handle_children(ypdb);
+ if (children == (MAX_CHILDREN - 1))
+ checkwork();
+ }
+ }
+ svc_getreqset(&fdsr);
+ break;
+ }
+ }
+
+ /* NOTREACHED */
+ exit(1);
+}
+
+void
+checkwork(void)
+{
+ struct _dom_binding *ypdb;
+
+ for (ypdb = ypbindlist; ypdb; ypdb = ypdb->dom_pnext)
+ ping(ypdb);
+}
+
+/* The clnt_broadcast() callback mechanism sucks. */
+
+/*
+ * Receive results from broadcaster. Don't worry about passing
+ * bogus info to rpc_received() -- it can handle it. Note that we
+ * must be sure to invalidate the dom_pipe_fds descriptors here:
+ * since descriptors can be re-used, we have to make sure we
+ * don't mistake one of the RPC descriptors for one of the pipes.
+ * What's weird is that forgetting to invalidate the pipe descriptors
+ * doesn't always result in an error (otherwise I would have caught
+ * the mistake much sooner), even though logically it should.
+ */
+void
+handle_children(struct _dom_binding *ypdb)
+{
+ char buf[YPMAXDOMAIN + 1];
+ struct sockaddr_in addr;
+ int d = 0, a = 0;
+ struct _dom_binding *y, *prev = NULL;
+ char path[MAXPATHLEN];
+
+ if ((d = read(READFD, &buf, sizeof(buf))) <= 0)
+ syslog(LOG_WARNING, "could not read from child: %m");
+
+ if ((a = read(READFD, &addr, sizeof(struct sockaddr_in))) < 0)
+ syslog(LOG_WARNING, "could not read from child: %m");
+
+ close(READFD);
+ FD_CLR(READFD, &fdsr);
+ FD_CLR(READFD, &svc_fdset);
+ READFD = WRITEFD = -1;
+ if (d > 0 && a > 0)
+ rpc_received(buf, &addr, 0);
+ else {
+ for (y = ypbindlist; y; y = y->dom_pnext) {
+ if (y == ypdb)
+ break;
+ prev = y;
+ }
+ switch (ypdb->dom_default) {
+ case 0:
+ if (prev == NULL)
+ ypbindlist = y->dom_pnext;
+ else
+ prev->dom_pnext = y->dom_pnext;
+ sprintf(path, "%s/%s.%ld", BINDINGDIR,
+ ypdb->dom_domain, YPVERS);
+ close(ypdb->dom_lockfd);
+ unlink(path);
+ free(ypdb);
+ domains--;
+ return;
+ case 1:
+ ypdb->dom_broadcast_pid = 0;
+ ypdb->dom_alive = 0;
+ broadcast(ypdb);
+ return;
+ default:
+ break;
+ }
+ }
+
+ return;
+}
+
+/*
+ * Send our dying words back to our parent before we perish.
+ */
+int
+tell_parent(char *dom, struct sockaddr_in *addr)
+{
+ char buf[YPMAXDOMAIN + 1];
+ struct timeval timeout;
+ fd_set fds;
+
+ timeout.tv_sec = 5;
+ timeout.tv_usec = 0;
+
+ sprintf(buf, "%s", broad_domain->dom_domain);
+ if (write(BROADFD, &buf, sizeof(buf)) < 0)
+ return(1);
+
+ /*
+ * Stay in sync with parent: wait for it to read our first
+ * message before sending the second.
+ */
+
+ FD_ZERO(&fds);
+ FD_SET(BROADFD, &fds);
+ if (select(FD_SETSIZE, NULL, &fds, NULL, &timeout) == -1)
+ return(1);
+ if (FD_ISSET(BROADFD, &fds)) {
+ if (write(BROADFD, addr, sizeof(struct sockaddr_in)) < 0)
+ return(1);
+ } else {
+ return(1);
+ }
+
+ close(BROADFD);
+ return (0);
+}
+
+bool_t broadcast_result(out, addr)
+bool_t *out;
+struct sockaddr_in *addr;
+{
+ if (retries >= MAX_RETRIES) {
+ bzero(addr, sizeof(struct sockaddr_in));
+ if (tell_parent(broad_domain->dom_domain, addr))
+ syslog(LOG_WARNING, "lost connection to parent");
+ return (TRUE);
+ }
+
+ if (yp_restricted && verify(addr->sin_addr)) {
+ retries++;
+ syslog(LOG_NOTICE, "NIS server at %s not in restricted mode access list -- rejecting.\n",inet_ntoa(addr->sin_addr));
+ return (FALSE);
+ } else {
+ if (tell_parent(broad_domain->dom_domain, addr))
+ syslog(LOG_WARNING, "lost connection to parent");
+ return (TRUE);
+ }
+}
+
+/*
+ * The right way to send RPC broadcasts.
+ * Use the clnt_broadcast() RPC service. Unfortunately, clnt_broadcast()
+ * blocks while waiting for replies, so we have to fork off separate
+ * broadcaster processes that do the waiting and then transmit their
+ * results back to the parent for processing. We also have to remember
+ * to save the name of the domain we're trying to bind in a global
+ * variable since clnt_broadcast() provides no way to pass things to
+ * the 'eachresult' callback function.
+ */
+void
+broadcast(struct _dom_binding *ypdb)
+{
+ bool_t out = FALSE;
+ enum clnt_stat stat;
+
+ if (children >= MAX_CHILDREN || ypdb->dom_broadcast_pid)
+ return;
+
+ if (pipe(ypdb->dom_pipe_fds) < 0) {
+ syslog(LOG_WARNING, "pipe: %m");
+ return;
+ }
+
+ if (ypdb->dom_vers == -1 && (long)ypdb->dom_server_addr.sin_addr.s_addr)
+ syslog(LOG_WARNING, "NIS server [%s] for domain \"%s\" not responding",
+ inet_ntoa(ypdb->dom_server_addr.sin_addr), ypdb->dom_domain);
+
+ broad_domain = ypdb;
+ flock(ypdb->dom_lockfd, LOCK_UN);
+
+ switch ((ypdb->dom_broadcast_pid = fork())) {
+ case 0:
+ close(READFD);
+ signal(SIGCHLD, SIG_DFL);
+ signal(SIGTERM, SIG_DFL);
+ break;
+ case -1:
+ syslog(LOG_WARNING, "fork: %m");
+ close(READFD);
+ close(WRITEFD);
+ return;
+ default:
+ close(WRITEFD);
+ FD_SET(READFD, &svc_fdset);
+ children++;
+ return;
+ }
+
+ /* Release all locks before doing anything else. */
+ while (ypbindlist) {
+ close(ypbindlist->dom_lockfd);
+ ypbindlist = ypbindlist->dom_pnext;
+ }
+ close(yplockfd);
+
+ /*
+ * Special 'many-cast' behavior. If we're in restricted mode,
+ * we have a list of possible server addresses to try. What
+ * we can do is transmit to each ypserv's YPPROC_DOMAIN_NONACK
+ * procedure and time the replies. Whoever replies fastest
+ * gets to be our server. Note that this is not a broadcast
+ * operation: we transmit uni-cast datagrams only.
+ */
+ if (yp_restricted && yp_manycast) {
+ short port;
+ int i;
+ struct sockaddr_in sin;
+
+ i = __yp_ping(restricted_addrs, yp_restricted,
+ ypdb->dom_domain, &port);
+ if (i == -1) {
+ bzero(&ypdb->dom_server_addr,
+ sizeof(struct sockaddr_in));
+ if (tell_parent(ypdb->dom_domain,
+ &ypdb->dom_server_addr))
+ syslog(LOG_WARNING, "lost connection to parent");
+ } else {
+ bzero(&sin, sizeof(struct sockaddr_in));
+ bcopy(&restricted_addrs[i],
+ &sin.sin_addr, sizeof(struct in_addr));
+ sin.sin_family = AF_INET;
+ sin.sin_port = port;
+ if (tell_parent(broad_domain->dom_domain, &sin))
+ syslog(LOG_WARNING,
+ "lost connection to parent");
+ }
+ _exit(0);
+ }
+
+ retries = 0;
+
+ {
+ char *ptr;
+
+ ptr = ypdb->dom_domain;
+ stat = clnt_broadcast(YPPROG, YPVERS, YPPROC_DOMAIN_NONACK,
+ (xdrproc_t)xdr_domainname, &ptr,
+ (xdrproc_t)xdr_bool, &out,
+ (resultproc_t)broadcast_result);
+ }
+
+ if (stat != RPC_SUCCESS) {
+ bzero(&ypdb->dom_server_addr,
+ sizeof(struct sockaddr_in));
+ if (tell_parent(ypdb->dom_domain, &ypdb->dom_server_addr))
+ syslog(LOG_WARNING, "lost connection to parent");
+ }
+
+ _exit(0);
+}
+
+/*
+ * The right way to check if a server is alive.
+ * Attempt to get a client handle pointing to the server and send a
+ * YPPROC_DOMAIN. If we can't get a handle or we get a reply of FALSE,
+ * we invalidate this binding entry and send out a broadcast to try to
+ * establish a new binding. Note that we treat non-default domains
+ * specially: once bound, we keep tabs on our server, but if it
+ * goes away and fails to respond after one round of broadcasting, we
+ * abandon it until a client specifically references it again. We make
+ * every effort to keep our default domain bound, however, since we
+ * need it to keep the system on its feet.
+ */
+int
+ping(struct _dom_binding *ypdb)
+{
+ bool_t out;
+ struct timeval interval, timeout;
+ enum clnt_stat stat;
+ int rpcsock = RPC_ANYSOCK;
+ CLIENT *client_handle;
+
+ interval.tv_sec = FAIL_THRESHOLD;
+ interval.tv_usec = 0;
+ timeout.tv_sec = FAIL_THRESHOLD;
+ timeout.tv_usec = 0;
+
+ if (ypdb->dom_broadcast_pid)
+ return(1);
+
+ if ((client_handle = clntudp_bufcreate(&ypdb->dom_server_addr,
+ YPPROG, YPVERS, interval, &rpcsock, RPCSMALLMSGSIZE,
+ RPCSMALLMSGSIZE)) == (CLIENT *)NULL) {
+ /* Can't get a handle: we're dead. */
+ ypdb->dom_alive = 0;
+ ypdb->dom_vers = -1;
+ broadcast(ypdb);
+ return(1);
+ }
+
+ {
+ char *ptr;
+
+ ptr = ypdb->dom_domain;
+
+ stat = clnt_call(client_handle, YPPROC_DOMAIN,
+ (xdrproc_t)xdr_domainname, &ptr,
+ (xdrproc_t)xdr_bool, &out, timeout);
+ if (stat != RPC_SUCCESS || out == FALSE) {
+ ypdb->dom_alive = 0;
+ ypdb->dom_vers = -1;
+ clnt_destroy(client_handle);
+ broadcast(ypdb);
+ return(1);
+ }
+ }
+
+ clnt_destroy(client_handle);
+ return(0);
+}
+
+void
+rpc_received(char *dom, struct sockaddr_in *raddrp, int force)
+{
+ struct _dom_binding *ypdb, *prev = NULL;
+ struct iovec iov[2];
+ struct ypbind_resp ybr;
+ char path[MAXPATHLEN];
+ int fd;
+
+ /*printf("returned from %s/%d about %s\n", inet_ntoa(raddrp->sin_addr),
+ ntohs(raddrp->sin_port), dom);*/
+
+ if (dom == NULL)
+ return;
+
+ for (ypdb = ypbindlist; ypdb; ypdb = ypdb->dom_pnext) {
+ if (strcmp(ypdb->dom_domain, dom) == 0)
+ break;
+ prev = ypdb;
+ }
+
+ if (ypdb && force) {
+ if (ypdb->dom_broadcast_pid) {
+ kill(ypdb->dom_broadcast_pid, SIGINT);
+ close(READFD);
+ FD_CLR(READFD, &fdsr);
+ FD_CLR(READFD, &svc_fdset);
+ READFD = WRITEFD = -1;
+ }
+ }
+
+ /* if in secure mode, check originating port number */
+ if ((ypsecuremode && (ntohs(raddrp->sin_port) >= IPPORT_RESERVED))) {
+ syslog(LOG_WARNING, "Rejected NIS server on [%s/%d] for domain %s.",
+ inet_ntoa(raddrp->sin_addr), ntohs(raddrp->sin_port),
+ dom);
+ if (ypdb != NULL) {
+ ypdb->dom_broadcast_pid = 0;
+ ypdb->dom_alive = 0;
+ }
+ return;
+ }
+
+ if (raddrp->sin_addr.s_addr == (long)0) {
+ switch (ypdb->dom_default) {
+ case 0:
+ if (prev == NULL)
+ ypbindlist = ypdb->dom_pnext;
+ else
+ prev->dom_pnext = ypdb->dom_pnext;
+ sprintf(path, "%s/%s.%ld", BINDINGDIR,
+ ypdb->dom_domain, YPVERS);
+ close(ypdb->dom_lockfd);
+ unlink(path);
+ free(ypdb);
+ domains--;
+ return;
+ case 1:
+ ypdb->dom_broadcast_pid = 0;
+ ypdb->dom_alive = 0;
+ broadcast(ypdb);
+ return;
+ default:
+ break;
+ }
+ }
+
+ if (ypdb == NULL) {
+ if (force == 0)
+ return;
+ ypdb = (struct _dom_binding *)malloc(sizeof *ypdb);
+ if (ypdb == NULL) {
+ syslog(LOG_WARNING, "malloc: %m");
+ return;
+ }
+ bzero(ypdb, sizeof *ypdb);
+ strncpy(ypdb->dom_domain, dom, sizeof ypdb->dom_domain);
+ ypdb->dom_lockfd = -1;
+ ypdb->dom_default = 0;
+ ypdb->dom_pnext = ypbindlist;
+ ypbindlist = ypdb;
+ }
+
+ /* We've recovered from a crash: inform the world. */
+ if (ypdb->dom_vers == -1 && ypdb->dom_server_addr.sin_addr.s_addr)
+ syslog(LOG_WARNING, "NIS server [%s] for domain \"%s\" OK",
+ inet_ntoa(raddrp->sin_addr), ypdb->dom_domain);
+
+ bcopy(raddrp, &ypdb->dom_server_addr,
+ sizeof ypdb->dom_server_addr);
+
+ ypdb->dom_vers = YPVERS;
+ ypdb->dom_alive = 1;
+ ypdb->dom_broadcast_pid = 0;
+
+ if (ypdb->dom_lockfd != -1)
+ close(ypdb->dom_lockfd);
+
+ sprintf(path, "%s/%s.%ld", BINDINGDIR,
+ ypdb->dom_domain, ypdb->dom_vers);
+#ifdef O_SHLOCK
+ if ((fd = open(path, O_CREAT|O_SHLOCK|O_RDWR|O_TRUNC, 0644)) == -1) {
+ (void)mkdir(BINDINGDIR, 0755);
+ if ((fd = open(path, O_CREAT|O_SHLOCK|O_RDWR|O_TRUNC, 0644)) == -1)
+ return;
+ }
+#else
+ if ((fd = open(path, O_CREAT|O_RDWR|O_TRUNC, 0644)) == -1) {
+ (void)mkdir(BINDINGDIR, 0755);
+ if ((fd = open(path, O_CREAT|O_RDWR|O_TRUNC, 0644)) == -1)
+ return;
+ }
+ flock(fd, LOCK_SH);
+#endif
+
+ /*
+ * ok, if BINDINGDIR exists, and we can create the binding file,
+ * then write to it..
+ */
+ ypdb->dom_lockfd = fd;
+
+ iov[0].iov_base = (char *)&(udptransp->xp_port);
+ iov[0].iov_len = sizeof udptransp->xp_port;
+ iov[1].iov_base = (char *)&ybr;
+ iov[1].iov_len = sizeof ybr;
+
+ bzero(&ybr, sizeof ybr);
+ ybr.ypbind_status = YPBIND_SUCC_VAL;
+ *(u_int32_t *)&ybr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_addr = raddrp->sin_addr.s_addr;
+ *(u_short *)&ybr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_port = raddrp->sin_port;
+
+ if (writev(ypdb->dom_lockfd, iov, 2) != iov[0].iov_len + iov[1].iov_len) {
+ syslog(LOG_WARNING, "write: %m");
+ close(ypdb->dom_lockfd);
+ ypdb->dom_lockfd = -1;
+ return;
+ }
+}
+
+/*
+ * Check address against list of allowed servers. Return 0 if okay,
+ * 1 if not matched.
+ */
+int
+verify(struct in_addr addr)
+{
+ int i;
+
+ for (i = 0; i < RESTRICTED_SERVERS; i++)
+ if (!bcmp(&addr, &restricted_addrs[i], sizeof(struct in_addr)))
+ return(0);
+
+ return(1);
+}
+
+/*
+ * Try to set restricted mode. We default to normal mode if we can't
+ * resolve the specified hostnames.
+ */
+void
+yp_restricted_mode(char *args)
+{
+ struct hostent *h;
+ int i = 0;
+ char *s;
+
+ /* Find the restricted domain. */
+ if ((s = strsep(&args, ",")) == NULL)
+ return;
+ domain_name = s;
+
+ /* Get the addresses of the servers. */
+ while ((s = strsep(&args, ",")) != NULL && i < RESTRICTED_SERVERS) {
+ if ((h = gethostbyname(s)) == NULL)
+ return;
+ bcopy (h->h_addr_list[0], &restricted_addrs[i],
+ sizeof(struct in_addr));
+ i++;
+ }
+
+ /* ypset and ypsetme not allowed with restricted mode */
+ ypsetmode = YPSET_NO;
+
+ yp_restricted = i;
+ return;
+}
diff --git a/usr.sbin/yppoll/Makefile b/usr.sbin/yppoll/Makefile
new file mode 100644
index 0000000..63763316
--- /dev/null
+++ b/usr.sbin/yppoll/Makefile
@@ -0,0 +1,7 @@
+# from: @(#)Makefile 5.8 (Berkeley) 7/28/90
+# $FreeBSD$
+
+PROG= yppoll
+MAN= yppoll.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/yppoll/yppoll.8 b/usr.sbin/yppoll/yppoll.8
new file mode 100644
index 0000000..1784154
--- /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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd October 25, 1994
+.Dt YPPOLL 8
+.Os
+.Sh NAME
+.Nm yppoll
+.Nd ask version of YP map from YP server
+.Sh SYNOPSIS
+.Nm
+.Op Fl h Ar host
+.Op Fl d Ar domain
+.Ar mapname
+.Sh DESCRIPTION
+.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 8 ,
+.Xr ypbind 8 ,
+.Xr ypset 8
+.Sh AUTHORS
+.An Theo De Raadt
+and
+.An John Brezak
diff --git a/usr.sbin/yppoll/yppoll.c b/usr.sbin/yppoll/yppoll.c
new file mode 100644
index 0000000..c528b0d
--- /dev/null
+++ b/usr.sbin/yppoll/yppoll.c
@@ -0,0 +1,98 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <rpc/rpc.h>
+#include <rpc/xdr.h>
+#include <rpcsvc/yp_prot.h>
+#include <rpcsvc/ypclnt.h>
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: yppoll [-h host] [-d domainname] mapname\n");
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ char *domainname;
+ char *hostname = "localhost";
+ char *inmap, *master;
+ int order;
+ int c, r;
+ time_t t;
+
+ yp_get_default_domain(&domainname);
+
+ while ((c = getopt(argc, argv, "h:d:")) != -1)
+ switch (c) {
+ case 'd':
+ domainname = optarg;
+ break;
+ case 'h':
+ hostname = optarg;
+ break;
+ case '?':
+ usage();
+ /*NOTREACHED*/
+ }
+
+ if (optind + 1 != argc)
+ usage();
+
+ inmap = argv[optind];
+
+ r = yp_order(domainname, inmap, &order);
+ if (r != 0)
+ errx(1, "no such map %s. Reason: %s", inmap, yperr_string(r));
+ t = _int_to_time(order);
+ printf("Map %s has order number %d. %s", inmap, order, ctime(&t));
+ r = yp_master(domainname, inmap, &master);
+ if (r != 0)
+ errx(1, "no such map %s. Reason: %s", inmap, yperr_string(r));
+ printf("The master server is %s.\n", master);
+
+ exit(0);
+}
diff --git a/usr.sbin/yppush/Makefile b/usr.sbin/yppush/Makefile
new file mode 100644
index 0000000..7ce1c61
--- /dev/null
+++ b/usr.sbin/yppush/Makefile
@@ -0,0 +1,28 @@
+# $FreeBSD$
+
+RPCDIR= ${.CURDIR}/../../include/rpcsvc
+.PATH: ${RPCDIR} ${.CURDIR}/../../usr.sbin/ypserv \
+ ${.CURDIR}/../../libexec/ypxfr
+
+PROG= yppush
+MAN= yppush.8
+SRCS= ypxfr_getmap.c yp_dblookup.c yp_error.c ypxfr_misc.c yppush_main.c \
+ ${GENSRCS}
+GENSRCS=yp.h yp_clnt.c yppush_svc.c
+
+CFLAGS+= -I. -I${.CURDIR}/../../libexec/ypxfr
+
+RPCGEN= rpcgen -C
+
+CLEANFILES= ${GENSRCS}
+
+yppush_svc.c: yp.x
+ ${RPCGEN} -DYPPUSH_ONLY -m -o ${.TARGET} ${RPCDIR}/yp.x
+
+yp_clnt.c: yp.x
+ ${RPCGEN} -DYPSERV_ONLY -l -o ${.TARGET} ${RPCDIR}/yp.x
+
+yp.h: yp.x
+ ${RPCGEN} -h -o ${.TARGET} ${RPCDIR}/yp.x
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/yppush/yppush.8 b/usr.sbin/yppush/yppush.8
new file mode 100644
index 0000000..d4dedd3
--- /dev/null
+++ b/usr.sbin/yppush/yppush.8
@@ -0,0 +1,179 @@
+.\" Copyright (c) 1991, 1993, 1995
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd February 5, 1995
+.Dt YPPUSH 8
+.Os
+.Sh NAME
+.Nm yppush
+.Nd "force propagation of updated NIS databases"
+.Sh SYNOPSIS
+.Nm
+.Op Fl d Ar domain
+.Op Fl t Ar timeout
+.Op Fl j Ar #parallel jobs
+.Op Fl h Ar host
+.Op Fl p Ar path
+.Op Fl v
+.Ar mapname
+.Sh DESCRIPTION
+.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
+.Dq Li NOPUSH=True
+entry in the Makefile must first be commented out
+(the default
+.Fx
+configuration assumes a small network with only
+a single NIS server; in such a configuration,
+.Nm
+is not needed).
+.Pp
+By default,
+.Nm
+determines the names of the slave servers for a domain by searching the
+.Pa ypservers
+map. A destination host (or a list of hosts) can also be manually
+specified on the command line.
+Once it has a complete list of slave servers, it sends a 'map transfer'
+request to each slave, which in turn reads a copy of the map from
+the master NIS server using
+.Xr ypxfr 8 .
+Included within each request is the name of the map to be copied
+and some special information required by
+.Xr ypxfr 8
+to successfully 'callback' to
+.Nm
+and carry out the transfer.
+Any error messages
+.Nm
+receives from
+.Xr ypxfr 8
+via callback will be printed to stderr.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl d Ar domain
+Specify a particular domain.
+The NIS domain of
+the local host system is used by default.
+If the local host's domain
+name is not set, the domain name must be specified with this flag.
+.It Fl t Ar timeout
+Specify a timeout value in seconds.
+This timeout
+controls how long
+.Nm
+will wait for a response from a slave server before sending a
+map transfer request to the next slave server in its list.
+.It Fl j Ar #parallel jobs
+.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.
+.El
+.Sh FILES
+.Bl -tag -width Pa -compact
+.It Pa /var/yp/[domainname]/ypservers
+the NIS ypservers map containing the names of all servers in
+a particular NIS domain
+.El
+.Sh SEE ALSO
+.Xr yp 8 ,
+.Xr ypserv 8 ,
+.Xr ypxfr 8
+.Sh BUGS
+The mechanism for transferring NIS maps in NIS v1 is different
+than that in NIS version 2. This version of
+.Nm
+has support for transferring maps to NIS v2 systems only.
+.Sh AUTHORS
+.An Bill Paul Aq wpaul@ctr.columbia.edu
diff --git a/usr.sbin/yppush/yppush_extern.h b/usr.sbin/yppush/yppush_extern.h
new file mode 100644
index 0000000..2a4fbcc
--- /dev/null
+++ b/usr.sbin/yppush/yppush_extern.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 1995
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/* Privately defined error codes. */
+#define YPPUSH_TIMEDOUT 255 /* Timed out trying to talk to ypserv */
+#define YPPUSH_NOHOST 254 /* No such host */
+#define YPPUSH_YPSERV 252 /* Failed to contact ypserv. */
+#define YPPUSH_PMAP 251 /* Portmapper failure. */
+#ifndef YPPUSH_RESPONSE_TIMEOUT
+#define YPPUSH_RESPONSE_TIMEOUT 5*60
+#endif
+extern int _rpc_dtablesize(void);
+extern void yppush_xfrrespprog_1(struct svc_req *, SVCXPRT *);
diff --git a/usr.sbin/yppush/yppush_main.c b/usr.sbin/yppush/yppush_main.c
new file mode 100644
index 0000000..a0e1ddb
--- /dev/null
+++ b/usr.sbin/yppush/yppush_main.c
@@ -0,0 +1,686 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#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 const char *
+yppusherr_string(int err)
+{
+ switch (err) {
+ case YPPUSH_TIMEDOUT:
+ return("transfer or callback timed out");
+ case YPPUSH_YPSERV:
+ return("failed to contact ypserv");
+ case YPPUSH_NOHOST:
+ return("no such host");
+ case YPPUSH_PMAP:
+ return("portmapper failure");
+ default:
+ return("unknown error code");
+ }
+}
+
+/*
+ * Report state of a job.
+ */
+static int
+yppush_show_status(ypxfrstat status, unsigned long tid)
+{
+ struct jobs *job;
+
+ job = yppush_joblist;
+
+ while (job) {
+ if (job->tid == tid)
+ break;
+ job = job->next;
+ }
+
+ if (job->polled) {
+ return(0);
+ }
+
+ if (verbose > 1)
+ yp_error("checking return status: transaction ID: %lu",
+ job->tid);
+ if (status != YPPUSH_SUCC || verbose) {
+ yp_error("transfer of map %s to server %s %s",
+ job->map, job->server, status == YPPUSH_SUCC ?
+ "succeeded" : "failed");
+ yp_error("status returned by ypxfr: %s", status > YPPUSH_AGE ?
+ yppusherr_string(status) :
+ ypxfrerr_string(status));
+ }
+
+ job->polled = 1;
+
+ svc_unregister(job->prognum, 1);
+
+ yppush_running_jobs--;
+ return(0);
+}
+
+/* Exit routine. */
+static void
+yppush_exit(int now)
+{
+ struct jobs *jptr;
+ int still_pending = 1;
+
+ /* Let all the information trickle in. */
+ while (!now && still_pending) {
+ jptr = yppush_joblist;
+ still_pending = 0;
+ while (jptr) {
+ if (jptr->polled == 0) {
+ still_pending++;
+ if (verbose > 1)
+ yp_error("%s has not responded",
+ jptr->server);
+ } else {
+ if (verbose > 1)
+ yp_error("%s has responded",
+ jptr->server);
+ }
+ jptr = jptr->next;
+ }
+ if (still_pending) {
+ if (verbose > 1)
+ yp_error("%d transfer%sstill pending",
+ still_pending,
+ still_pending > 1 ? "s " : " ");
+ yppush_alarm_tripped = 0;
+ alarm(YPPUSH_RESPONSE_TIMEOUT);
+ pause();
+ alarm(0);
+ if (yppush_alarm_tripped == 1) {
+ yp_error("timed out");
+ now = 1;
+ }
+ } else {
+ if (verbose)
+ yp_error("all transfers complete");
+ break;
+ }
+ }
+
+
+ /* All stats collected and reported -- kill all the stragglers. */
+ jptr = yppush_joblist;
+ while (jptr) {
+ if (!jptr->polled)
+ yp_error("warning: exiting with transfer \
+to %s (transid = %lu) still pending", jptr->server, jptr->tid);
+ svc_unregister(jptr->prognum, 1);
+ jptr = jptr->next;
+ }
+
+ exit(0);
+}
+
+/*
+ * Handler for 'normal' signals.
+ */
+
+static void
+handler(int sig)
+{
+ if (sig == SIGTERM || sig == SIGINT || sig == SIGABRT) {
+ yppush_jobs = 0;
+ yppush_exit(1);
+ }
+
+ if (sig == SIGALRM) {
+ alarm(0);
+ yppush_alarm_tripped++;
+ }
+
+ return;
+}
+
+/*
+ * Dispatch loop for callback RPC services.
+ */
+static void
+yppush_svc_run(void)
+{
+#ifdef FD_SETSIZE
+ fd_set readfds;
+#else
+ int readfds;
+#endif /* def FD_SETSIZE */
+ struct timeval timeout;
+
+ timeout.tv_usec = 0;
+ timeout.tv_sec = 5;
+
+retry:
+#ifdef FD_SETSIZE
+ readfds = svc_fdset;
+#else
+ readfds = svc_fds;
+#endif /* def FD_SETSIZE */
+ switch (select(_rpc_dtablesize(), &readfds, NULL, NULL, &timeout)) {
+ case -1:
+ if (errno == EINTR)
+ goto retry;
+ yp_error("select failed: %s", strerror(errno));
+ break;
+ case 0:
+ yp_error("select() timed out");
+ break;
+ default:
+ svc_getreqset(&readfds);
+ break;
+ }
+ return;
+}
+
+/*
+ * Special handler for asynchronous socket I/O. We mark the
+ * sockets of the callback handlers as O_ASYNC and handle SIGIO
+ * events here, which will occur when the callback handler has
+ * something interesting to tell us.
+ */
+static void
+async_handler(int sig)
+{
+ yppush_svc_run();
+
+ /* reset any pending alarms. */
+ alarm(0);
+ yppush_alarm_tripped++;
+ kill(getpid(), SIGALRM);
+ return;
+}
+
+/*
+ * RPC service routines for callbacks.
+ */
+void *
+yppushproc_null_1_svc(void *argp, struct svc_req *rqstp)
+{
+ static char * result;
+ /* Do nothing -- RPC conventions call for all a null proc. */
+ return((void *) &result);
+}
+
+void *
+yppushproc_xfrresp_1_svc(yppushresp_xfr *argp, struct svc_req *rqstp)
+{
+ static char * result;
+ yppush_show_status(argp->status, argp->transid);
+ return((void *) &result);
+}
+
+/*
+ * Transmit a YPPROC_XFR request to ypserv.
+ */
+static int
+yppush_send_xfr(struct jobs *job)
+{
+ ypreq_xfr req;
+/* ypresp_xfr *resp; */
+ DBT key, data;
+ CLIENT *clnt;
+ struct rpc_err err;
+ struct timeval timeout;
+
+ timeout.tv_usec = 0;
+ timeout.tv_sec = 0;
+
+ /*
+ * The ypreq_xfr structure has a member of type map_parms,
+ * which seems to require the order number of the map.
+ * It isn't actually used at the other end (at least the
+ * FreeBSD ypserv doesn't use it) but we fill it in here
+ * for the sake of completeness.
+ */
+ key.data = "YP_LAST_MODIFIED";
+ key.size = sizeof ("YP_LAST_MODIFIED") - 1;
+
+ if (yp_get_record(yppush_domain, yppush_mapname, &key, &data,
+ 1) != YP_TRUE) {
+ yp_error("failed to read order number from %s: %s: %s",
+ yppush_mapname, yperr_string(yp_errno),
+ strerror(errno));
+ return(1);
+ }
+
+ /* Fill in the request arguments */
+ req.map_parms.ordernum = atoi(data.data);
+ req.map_parms.domain = yppush_domain;
+ req.map_parms.peer = yppush_master;
+ req.map_parms.map = job->map;
+ req.transid = job->tid;
+ req.prog = job->prognum;
+ req.port = job->port;
+
+ /* Get a handle to the remote ypserv. */
+ if ((clnt = clnt_create(job->server, YPPROG, YPVERS, "udp")) == NULL) {
+ yp_error("%s: %s",job->server,clnt_spcreateerror("couldn't \
+create udp handle to NIS server"));
+ switch (rpc_createerr.cf_stat) {
+ case RPC_UNKNOWNHOST:
+ job->stat = YPPUSH_NOHOST;
+ break;
+ case RPC_PMAPFAILURE:
+ job->stat = YPPUSH_PMAP;
+ break;
+ default:
+ job->stat = YPPUSH_RPC;
+ break;
+ }
+ return(1);
+ }
+
+ /*
+ * Reduce timeout to nothing since we may not
+ * get a response from ypserv and we don't want to block.
+ */
+ if (clnt_control(clnt, CLSET_TIMEOUT, (char *)&timeout) == FALSE)
+ yp_error("failed to set timeout on ypproc_xfr call");
+
+ /* Invoke the ypproc_xfr service. */
+ if (ypproc_xfr_2(&req, clnt) == NULL) {
+ clnt_geterr(clnt, &err);
+ if (err.re_status != RPC_SUCCESS &&
+ err.re_status != RPC_TIMEDOUT) {
+ yp_error("%s: %s", job->server, clnt_sperror(clnt,
+ "yp_xfr failed"));
+ job->stat = YPPUSH_YPSERV;
+ clnt_destroy(clnt);
+ return(1);
+ }
+ }
+
+ clnt_destroy(clnt);
+
+ return(0);
+}
+
+/*
+ * Main driver function. Register the callback service, add the transfer
+ * request to the internal list, send the YPPROC_XFR request to ypserv
+ * do other magic things.
+ */
+int
+yp_push(char *server, char *map, unsigned long tid)
+{
+ unsigned long prognum;
+ int sock = RPC_ANYSOCK;
+ SVCXPRT *xprt;
+ struct jobs *job;
+
+ /*
+ * Register the callback service on the first free
+ * transient program number.
+ */
+ xprt = svcudp_create(sock);
+ for (prognum = 0x40000000; prognum < 0x5FFFFFFF; prognum++) {
+ if (svc_register(xprt, prognum, 1,
+ yppush_xfrrespprog_1, IPPROTO_UDP) == TRUE)
+ break;
+ }
+
+ /* Register the job in our linked list of jobs. */
+ if ((job = (struct jobs *)malloc(sizeof (struct jobs))) == NULL) {
+ yp_error("malloc failed");
+ yppush_exit(1);
+ }
+
+ /* Initialize the info for this job. */
+ job->stat = 0;
+ job->tid = tid;
+ job->port = xprt->xp_port;
+ job->sock = xprt->xp_fd; /*XXX: Evil!! EEEEEEEVIL!!! */
+ job->server = strdup(server);
+ job->map = strdup(map);
+ job->prognum = prognum;
+ job->polled = 0;
+ job->next = yppush_joblist;
+ yppush_joblist = job;
+
+ /*
+ * Set the RPC sockets to asynchronous mode. This will
+ * cause the system to smack us with a SIGIO when an RPC
+ * callback is delivered. This in turn allows us to handle
+ * the callback even though we may be in the middle of doing
+ * something else at the time.
+ *
+ * XXX This is a horrible thing to do for two reasons,
+ * both of which have to do with portability:
+ * 1) We really ought not to be sticking our grubby mits
+ * into the RPC service transport handle like this.
+ * 2) Even in this day and age, there are still some *NIXes
+ * that don't support async socket I/O.
+ */
+ if (fcntl(xprt->xp_fd, F_SETOWN, getpid()) == -1 ||
+ fcntl(xprt->xp_fd, F_SETFL, O_ASYNC) == -1) {
+ yp_error("failed to set async I/O mode: %s",
+ strerror(errno));
+ yppush_exit(1);
+ }
+
+ if (verbose) {
+ yp_error("initiating transfer: %s -> %s (transid = %lu)",
+ yppush_mapname, server, tid);
+ }
+
+ /*
+ * Send the XFR request to ypserv. We don't have to wait for
+ * a response here since we can handle them asynchronously.
+ */
+
+ if (yppush_send_xfr(job)){
+ /* Transfer request blew up. */
+ yppush_show_status(job->stat ? job->stat :
+ YPPUSH_YPSERV,job->tid);
+ } else {
+ if (verbose > 1)
+ yp_error("%s has been called", server);
+ }
+
+ return(0);
+}
+
+/*
+ * Called for each entry in the ypservers map from yp_get_map(), which
+ * is our private yp_all() routine.
+ */
+int
+yppush_foreach(int status, char *key, int keylen, char *val, int vallen,
+ char *data)
+{
+ char server[YPMAXRECORD + 2];
+
+ if (status != YP_TRUE)
+ return (status);
+
+ snprintf(server, sizeof(server), "%.*s", vallen, val);
+
+ /*
+ * Restrict the number of concurrent jobs. If yppush_jobs number
+ * of jobs have already been dispatched and are still pending,
+ * wait for one of them to finish so we can reuse its slot.
+ */
+ if (yppush_jobs <= 1) {
+ yppush_alarm_tripped = 0;
+ while (!yppush_alarm_tripped && yppush_running_jobs) {
+ alarm(yppush_timeout);
+ yppush_alarm_tripped = 0;
+ pause();
+ alarm(0);
+ }
+ } else {
+ yppush_alarm_tripped = 0;
+ while (!yppush_alarm_tripped && yppush_running_jobs >= yppush_jobs) {
+ alarm(yppush_timeout);
+ yppush_alarm_tripped = 0;
+ pause();
+ alarm(0);
+ }
+ }
+
+ /* Cleared for takeoff: set everything in motion. */
+ if (yp_push(server, yppush_mapname, yppush_transid))
+ return(yp_errno);
+
+ /* Bump the job counter and transaction ID. */
+ yppush_running_jobs++;
+ yppush_transid++;
+ return (0);
+}
+
+static void usage()
+{
+ fprintf (stderr, "%s\n%s\n",
+ "usage: yppush [-d domain] [-t timeout] [-j #parallel jobs] [-h host]",
+ " [-p path] mapname");
+ exit(1);
+}
+
+/*
+ * Entry point. (About time!)
+ */
+int
+main(int argc, char *argv[])
+{
+ int ch;
+ DBT key, data;
+ char myname[MAXHOSTNAMELEN];
+ struct hostlist {
+ char *name;
+ struct hostlist *next;
+ };
+ struct hostlist *yppush_hostlist = NULL;
+ struct hostlist *tmp;
+ struct sigaction sa;
+
+ while ((ch = getopt(argc, argv, "d:j:p:h:t:v")) != -1) {
+ switch (ch) {
+ case 'd':
+ yppush_domain = optarg;
+ break;
+ case 'j':
+ yppush_jobs = atoi(optarg);
+ if (yppush_jobs <= 0)
+ yppush_jobs = 1;
+ break;
+ case 'p':
+ yp_dir = optarg;
+ break;
+ case 'h': /* we can handle multiple hosts */
+ if ((tmp = (struct hostlist *)malloc(sizeof(struct hostlist))) == NULL) {
+ yp_error("malloc failed");
+ yppush_exit(1);
+ }
+ tmp->name = strdup(optarg);
+ tmp->next = yppush_hostlist;
+ yppush_hostlist = tmp;
+ break;
+ case 't':
+ yppush_timeout = atoi(optarg);
+ break;
+ case 'v':
+ verbose++;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ yppush_mapname = argv[0];
+
+ if (yppush_mapname == NULL) {
+ /* "No guts, no glory." */
+ usage();
+ }
+
+ /*
+ * If no domain was specified, try to find the default
+ * domain. If we can't find that, we're doomed and must bail.
+ */
+ if (yppush_domain == NULL) {
+ char *yppush_check_domain;
+ if (!yp_get_default_domain(&yppush_check_domain) &&
+ !_yp_check(&yppush_check_domain)) {
+ yp_error("no domain specified and NIS not running");
+ usage();
+ } else
+ yp_get_default_domain(&yppush_domain);
+ }
+
+ /* Check to see that we are the master for this map. */
+
+ if (gethostname ((char *)&myname, sizeof(myname))) {
+ yp_error("failed to get name of local host: %s",
+ strerror(errno));
+ yppush_exit(1);
+ }
+
+ key.data = "YP_MASTER_NAME";
+ key.size = sizeof("YP_MASTER_NAME") - 1;
+
+ if (yp_get_record(yppush_domain, yppush_mapname,
+ &key, &data, 1) != YP_TRUE) {
+ yp_error("couldn't open %s map: %s", yppush_mapname,
+ strerror(errno));
+ yppush_exit(1);
+ }
+
+ if (strncmp(myname, data.data, data.size)) {
+ yp_error("warning: this host is not the master for %s",
+ yppush_mapname);
+#ifdef NITPICKY
+ yppush_exit(1);
+#endif
+ }
+
+ yppush_master = malloc(data.size + 1);
+ strncpy(yppush_master, data.data, data.size);
+ yppush_master[data.size] = '\0';
+
+ /* Install some handy handlers. */
+ signal(SIGALRM, handler);
+ signal(SIGTERM, handler);
+ signal(SIGINT, handler);
+ signal(SIGABRT, handler);
+
+ /*
+ * Set up the SIGIO handler. Make sure that some of the
+ * other signals are blocked while the handler is running so
+ * select() doesn't get interrupted.
+ */
+ sigemptyset(&sa.sa_mask);
+ sigaddset(&sa.sa_mask, SIGIO); /* Goes without saying. */
+ sigaddset(&sa.sa_mask, SIGPIPE);
+ sigaddset(&sa.sa_mask, SIGCHLD);
+ sigaddset(&sa.sa_mask, SIGALRM);
+ sigaddset(&sa.sa_mask, SIGINT);
+ sa.sa_handler = async_handler;
+ sa.sa_flags = 0;
+
+ sigaction(SIGIO, &sa, NULL);
+
+ /* set initial transaction ID */
+ yppush_transid = time((time_t *)NULL);
+
+ if (yppush_hostlist) {
+ /*
+ * Host list was specified on the command line:
+ * kick off the transfers by hand.
+ */
+ tmp = yppush_hostlist;
+ while (tmp) {
+ yppush_foreach(YP_TRUE, NULL, 0, tmp->name,
+ strlen(tmp->name), NULL);
+ tmp = tmp->next;
+ }
+ } else {
+ /*
+ * Do a yp_all() on the ypservers map and initiate a ypxfr
+ * for each one.
+ */
+ ypxfr_get_map("ypservers", yppush_domain,
+ "localhost", yppush_foreach);
+ }
+
+ if (verbose > 1)
+ yp_error("all jobs dispatched");
+
+ /* All done -- normal exit. */
+ yppush_exit(0);
+
+ /* Just in case. */
+ exit(0);
+}
diff --git a/usr.sbin/ypserv/Makefile b/usr.sbin/ypserv/Makefile
new file mode 100644
index 0000000..30c3f3f
--- /dev/null
+++ b/usr.sbin/ypserv/Makefile
@@ -0,0 +1,43 @@
+# $FreeBSD$
+
+RPCDIR= ${.CURDIR}/../../include/rpcsvc
+.PATH: ${RPCDIR}
+
+PROG= ypserv
+MAN= ypserv.8 ypinit.8
+SRCS= yp_svc.c yp_server.c yp_dblookup.c yp_dnslookup.c \
+ ypxfr_clnt.c yp.h yp_main.c yp_error.c yp_access.c yp_svc_udp.c
+
+DPADD= ${LIBWRAP}
+LDADD= -lwrap
+
+CFLAGS+= -I. -DDB_CACHE -DTCP_WRAPPER
+
+CLEANFILES= yp_svc.c ypxfr_clnt.c yp.h
+
+RPCGEN= rpcgen -I -C
+
+# We need to remove the 'static' keyword from _rpcsvcstate so that
+# yp_main.c can see it.
+yp_svc.c: yp.x
+ rm -f ${.TARGET}
+ ${RPCGEN} -DYPSERV_ONLY -m ${RPCDIR}/yp.x | \
+ sed s/"static int _rpcsvcstate"/"int _rpcsvcstate"/g > ${.TARGET}
+
+ypxfr_clnt.c: yp.x
+ ${RPCGEN} -DYPPUSH_ONLY -l -o ${.TARGET} ${RPCDIR}/yp.x
+
+yp.h: yp.x
+ ${RPCGEN} -h -o ${.TARGET} ${RPCDIR}/yp.x
+
+afterinstall:
+ ${INSTALL} -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..74bb41c
--- /dev/null
+++ b/usr.sbin/ypserv/Makefile.yp
@@ -0,0 +1,589 @@
+#
+# Makefile for the NIS databases
+#
+# $FreeBSD$
+#
+# This Makefile should only be run on the NIS master server of a domain.
+# All updated maps will be pushed to all NIS slave servers listed in the
+# /var/yp/ypservers file. Please make sure that the hostnames of all
+# NIS servers in your domain are listed in /var/yp/ypservers.
+#
+# This Makefile can be modified to support more NIS maps if desired.
+#
+
+# If this machine is an NIS master, comment out this next line so
+# that changes to the NIS maps can be propagated to the slave servers.
+# (By default we assume that we are only serving a small domain with
+# only one server.)
+#
+NOPUSH = "True"
+
+# If you want to use a FreeBSD NIS server to serve non-FreeBSD clients
+# (i.e. clients who expect the password field in the passwd maps to be
+# valid) then uncomment this line. This will cause $YPDIR/passwd to
+# be generated with valid password fields. This is insecure: FreeBSD
+# normally only serves the master.passwd maps (which have real encrypted
+# passwords in them) to the superuser on other FreeBSD machines, but
+# non-FreeBSD clients (e.g. SunOS, Solaris (without NIS+), IRIX, HP-UX,
+# etc...) will only work properly in 'unsecure' mode.
+#
+#UNSECURE = "True"
+
+# The following line encodes the YP_INTERDOMAIN key into the hosts.byname
+# and hosts.byaddr maps so that ypserv(8) will do DNS lookups to resolve
+# hosts not in the current domain. Commenting this line out will disable
+# the DNS lookups.
+B=-b
+
+# Normally, the master.passwd.* maps are guarded against access from
+# non-privileged users. By commenting out the following line, the YP_SECURE
+# key will be removed from these maps, allowing anyone to access them.
+S=-s
+
+# These are commands which this Makefile needs to properly rebuild the
+# NIS databases. Don't change these unless you have a good reason. Also
+# be sure not to place an @ in front of /usr/bin/awk: it isn't necessary
+# and it'll break everything in sight.
+#
+AWK = /usr/bin/awk
+RM = @/bin/rm -f
+MV = @/bin/mv -f
+RMV = /bin/mv -f
+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
+# separate from your NIS server's actual configuration files. Note that the
+# NIS passwd and master.passwd files are stored in /var/yp: the server's
+# real password database is not used by default. However, you may use
+# the real /etc/passwd and /etc/master.passwd files by:
+#
+#
+# - invoking yppasswdd with `-t /etc/master.passwd' (yppasswdd will do a
+# 'pwd_mkdb' as needed if /etc/master.passwd is thus specified).
+# - Specifying the location of the master.passwd file using the
+# MASTER_PASSWD variable, i.e.:
+#
+# # make MASTER_PASSWD=/path/to/some/other/master.passwd
+#
+# - (optionally): editing this Makefile to change the default location.
+#
+# To add a user, edit $(YPDIR)/master.passwd and type 'make'. The raw
+# passwd file will be generated from the master.passwd file automagically.
+#
+ETHERS = $(YPSRCDIR)/ethers # ethernet addresses (for rarpd)
+BOOTPARAMS= $(YPSRCDIR)/bootparams # for booting Sun boxes (bootparamd)
+HOSTS = $(YPSRCDIR)/hosts
+NETWORKS = $(YPSRCDIR)/networks
+PROTOCOLS = $(YPSRCDIR)/protocols
+RPC = $(YPSRCDIR)/rpc
+SERVICES = $(YPSRCDIR)/services
+GROUP = $(YPSRCDIR)/group
+ALIASES = $(YPSRCDIR)/mail/aliases
+NETGROUP = $(YPDIR)/netgroup
+PASSWD = $(YPDIR)/passwd
+.if !defined(MASTER_PASSWD)
+MASTER = $(YPDIR)/master.passwd
+.else
+MASTER = $(MASTER_PASSWD)
+.endif
+YPSERVERS = $(YPDIR)/ypservers # List of all NIS servers for a domain
+PUBLICKEY = $(YPSRCDIR)/publickey
+NETID = $(YPSRCDIR)/netid
+AMDHOST = $(YPSRCDIR)/amd.map
+
+# List of maps that are always built.
+# If you want to omit some of them, feel free to comment
+# them out from this list.
+TARGETS= servers hosts networks protocols rpc services group
+#TARGETS+= aliases
+
+# Sanity checks: filter out targets we can't build
+# Note that we don't build the ethers or boorparams maps by default
+# since /etc/ethers and /etc/bootparams are not likely to be present
+# on all systems.
+.if exists($(ETHERS))
+TARGETS+= ethers
+.else
+ETHERS= /dev/null
+.endif
+
+.if exists($(BOOTPARAMS))
+TARGETS+= bootparams
+.else
+BOOTPARAMS= /dev/null
+.endif
+
+.if exists($(NETGROUP))
+TARGETS+= netgrp
+.else
+NETGROUP= /dev/null
+.endif
+
+.if exists($(MASTER))
+TARGETS+= passwd master.passwd netid
+.else
+MASTER= /dev/null
+TARGETS+= nopass
+.endif
+
+.if exists($(PUBLICKEY))
+TARGETS+= publickey
+.else
+PUBLICKEY= /dev/null
+.endif
+
+.if exists($(AMDHOST))
+TARGETS+= amd.map
+.else
+AMDHOST= /dev/null
+.endif
+
+target:
+ @if [ ! -d $(DOMAIN) ]; then mkdir $(DOMAIN); fi; \
+ cd $(DOMAIN) ; echo "NIS Map update started on `date` for domain $(DOMAIN)" ; \
+ make -f ../Makefile all; echo "NIS Map update completed."
+
+all: $(TARGETS)
+
+ethers: ethers.byname ethers.byaddr
+bootparam: bootparams
+hosts: hosts.byname hosts.byaddr
+networks: networks.byaddr networks.byname
+protocols: protocols.bynumber protocols.byname
+rpc: rpc.byname rpc.bynumber
+services: services.byname
+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 != "" && $$1 !~ "^#.*" && $$1 != "+") \
+ print $$1":*:"$$3":"$$4":"$$8":"$$9":"$$10}' $^ \
+ > $(PASSWD) ; \
+ else $(RCAT) $(MASTER) | \
+ $(AWK) -F: '{if ($$1 != "" && $$1 !~ "^#.*" && $$1 != "+") \
+ print $$1":"$$2":"$$3":"$$4":"$$8":"$$9":"$$10}' $^ \
+ > $(PASSWD) ; fi
+
+
+passwd.byname: $(PASSWD)
+ @echo "Updating $@..."
+ $(CAT) $(PASSWD) | \
+ $(AWK) -F: '{ if ($$1 != "" && $$1 !~ "^#.*" && $$1 != "+") \
+ print $$1"\t"$$0 }' $^ \
+ | $(DBLOAD) -f -i $(PASSWD) -o $(YPMAPDIR)/$@ - $(TMP); \
+ $(RMV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+
+
+passwd.byuid: $(PASSWD)
+ @echo "Updating $@..."
+ $(CAT) $(PASSWD) | \
+ $(AWK) -F: '{ if ($$1 != "" && $$1 !~ "^#.*" && $$1 != "+") \
+ print $$3"\t"$$0 }' $^ \
+ | $(DBLOAD) -f -i $(PASSWD) -o $(YPMAPDIR)/$@ - $(TMP); \
+ $(RMV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+ @$(MAKE) -f ../Makefile netid
+
+
+group.byname: $(GROUP)
+ @echo "Updating $@..."
+ $(CAT) $(GROUP) | \
+ $(AWK) -F: '{ if ($$1 != "" && $$1 !~ "^#.*" && $$1 != "+") \
+ print $$1"\t"$$0 }' $^ \
+ | $(DBLOAD) -f -i $(GROUP) -o $(YPMAPDIR)/$@ - $(TMP); \
+ $(RMV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+
+
+group.bygid: $(GROUP)
+ @echo "Updating $@..."
+ $(CAT) $(GROUP) | \
+ $(AWK) -F: '{ if ($$1 != "" && $$1 !~ "^#.*" && $$1 != "+") \
+ print $$3"\t"$$0 }' $^ \
+ | $(DBLOAD) -f -i $(GROUP) -o $(YPMAPDIR)/$@ - $(TMP); \
+ $(RMV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+ @$(MAKE) -f ../Makefile netid
+
+
+netid.byname: $(GROUP) $(PASSWD)
+ @echo "Updating $@..."
+ @$(MKNETID) -q -p $(PASSWD) -g $(GROUP) -h $(HOSTS) -n $(NETID) \
+ -d $(DOMAIN) | $(DBLOAD) -o $(YPMAPDIR)/$@ - $(TMP); \
+ $(RMV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+
+
+master.passwd.byname: $(MASTER)
+ @echo "Updating $@..."
+.if ${MASTER} == "/dev/null"
+ @echo "Master.passwd source file not found -- skipping"
+.else
+ $(CAT) $(MASTER) | \
+ $(AWK) -F: '{ if ($$1 != "" && $$1 !~ "^#.*" && $$1 != "+") \
+ print $$1"\t"$$0 }' $^ \
+ | $(DBLOAD) ${S} -f -i $(MASTER) -o $(YPMAPDIR)/$@ - $(TMP); \
+ $(RMV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+.endif
+
+
+master.passwd.byuid: $(MASTER)
+ @echo "Updating $@..."
+.if ${MASTER} == "/dev/null"
+ @echo "Master.passwd source file not found -- skipping"
+.else
+ $(CAT) $(MASTER) | \
+ $(AWK) -F: '{ if ($$1 != "" && $$1 !~ "^#.*" && $$1 != "+") \
+ print $$3"\t"$$0 }' $^ \
+ | $(DBLOAD) ${S} -f -i $(MASTER) -o $(YPMAPDIR)/$@ - $(TMP); \
+ $(RMV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+.endif
+
+
+amd.map: $(AMDHOST)
+ @echo "Updating $@..."
+ $(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..9f7bb51
--- /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[] =
+ "$FreeBSD$";
+#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 */
+const char *yp_procs[] = {
+ "ypoldproc_null",
+ "ypoldproc_domain",
+ "ypoldproc_domain_nonack",
+ "ypoldproc_match",
+ "ypoldproc_first",
+ "ypoldproc_next",
+ "ypoldproc_poll",
+ "ypoldproc_push",
+ "ypoldproc_get",
+ "badproc1", /* placeholder */
+ "badproc2", /* placeholder */
+ "badproc3", /* placeholder */
+
+ /* NIS v2 */
+ "ypproc_null",
+ "ypproc_domain",
+ "ypproc_domain_nonack",
+ "ypproc_match",
+ "ypproc_first",
+ "ypproc_next",
+ "ypproc_xfr",
+ "ypproc_clear",
+ "ypproc_all",
+ "ypproc_master",
+ "ypproc_order",
+ "ypproc_maplist"
+};
+
+#ifdef TCP_WRAPPER
+void
+load_securenets(void)
+{
+}
+#else
+struct securenet {
+ struct in_addr net;
+ struct in_addr mask;
+ struct securenet *next;
+};
+
+struct securenet *securenets;
+
+#define LINEBUFSZ 1024
+
+/*
+ * Read /var/yp/securenets file and initialize the securenets
+ * list. If the file doesn't exist, we set up a dummy entry that
+ * allows all hosts to connect.
+ */
+void
+load_securenets(void)
+{
+ FILE *fp;
+ char path[MAXPATHLEN + 2];
+ char linebuf[1024 + 2];
+ struct securenet *tmp;
+
+ /*
+ * If securenets is not NULL, we are being called to reload
+ * the list; free the existing list before re-reading the
+ * securenets file.
+ */
+ while (securenets) {
+ tmp = securenets->next;
+ free(securenets);
+ securenets = tmp;
+ }
+
+ snprintf(path, MAXPATHLEN, "%s/securenets", yp_dir);
+
+ if ((fp = fopen(path, "r")) == NULL) {
+ if (errno == ENOENT) {
+ securenets = (struct securenet *)malloc(sizeof(struct securenet));
+ securenets->net.s_addr = INADDR_ANY;
+ securenets->mask.s_addr = INADDR_ANY;
+ securenets->next = NULL;
+ return;
+ } else {
+ yp_error("fopen(%s) failed: %s", path, strerror(errno));
+ exit(1);
+ }
+ }
+
+ securenets = NULL;
+
+ while (fgets(linebuf, LINEBUFSZ, fp)) {
+ char addr1[20], addr2[20];
+
+ if ((linebuf[0] == '#')
+ || (strspn(linebuf, " \t\r\n") == strlen(linebuf)))
+ continue;
+ if (sscanf(linebuf, "%s %s", addr1, addr2) < 2) {
+ yp_error("badly formatted securenets entry: %s",
+ linebuf);
+ continue;
+ }
+
+ tmp = (struct securenet *)malloc(sizeof(struct securenet));
+
+ if (!inet_aton((char *)&addr1, (struct in_addr *)&tmp->net)) {
+ yp_error("badly formatted securenets entry: %s", addr1);
+ free(tmp);
+ continue;
+ }
+
+ if (!inet_aton((char *)&addr2, (struct in_addr *)&tmp->mask)) {
+ yp_error("badly formatted securenets entry: %s", addr2);
+ free(tmp);
+ continue;
+ }
+
+ tmp->next = securenets;
+ securenets = tmp;
+ }
+
+ fclose(fp);
+
+}
+#endif
+
+/*
+ * Access control functions.
+ *
+ * yp_access() checks the mapname and client host address and watches for
+ * the following things:
+ *
+ * - If the client is referencing one of the master.passwd.* maps, it must
+ * be using a privileged port to make its RPC to us. If it is, then we can
+ * assume that the caller is root and allow the RPC to succeed. If it
+ * isn't access is denied.
+ *
+ * - The client's IP address is checked against the securenets rules.
+ * There are two kinds of securenets support: the built-in support,
+ * which is very simple and depends on the presence of a
+ * /var/yp/securenets file, and tcp-wrapper support, which requires
+ * Wietse Venema's libwrap.a and tcpd.h. (Since the tcp-wrapper
+ * package does not ship with FreeBSD, we use the built-in support
+ * by default. Users can recompile the server with the tcp-wrapper library
+ * if they already have it installed and want to use hosts.allow and
+ * hosts.deny to control access instead of having a separate securenets
+ * file.)
+ *
+ * If no /var/yp/securenets file is present, the host access checks
+ * are bypassed and all hosts are allowed to connect.
+ *
+ * The yp_validdomain() function checks the domain specified by the caller
+ * to make sure it's actually served by this server. This is more a sanity
+ * check than an a security check, but this seems to be the best place for
+ * it.
+ */
+
+#ifdef DB_CACHE
+int
+yp_access(const char *map, const char *domain, const struct svc_req *rqstp)
+#else
+int
+yp_access(const char *map, const struct svc_req *rqstp)
+#endif
+{
+ struct sockaddr_in *rqhost;
+ int status = 0;
+ static unsigned long oldaddr = 0;
+#ifndef TCP_WRAPPER
+ struct securenet *tmp;
+#endif
+ const char *yp_procedure = NULL;
+ char procbuf[50];
+
+ if (rqstp->rq_prog != YPPASSWDPROG && rqstp->rq_prog != YPPROG) {
+ snprintf(procbuf, sizeof(procbuf), "#%lu/#%lu",
+ (unsigned long)rqstp->rq_prog,
+ (unsigned long)rqstp->rq_proc);
+ yp_procedure = (char *)&procbuf;
+ } else {
+ yp_procedure = rqstp->rq_prog == YPPASSWDPROG ?
+ "yppasswdprog_update" :
+ yp_procs[rqstp->rq_proc + (12 * (rqstp->rq_vers - 1))];
+ }
+
+ rqhost = svc_getcaller(rqstp->rq_xprt);
+
+ if (debug) {
+ yp_error("procedure %s called from %s:%d", yp_procedure,
+ inet_ntoa(rqhost->sin_addr),
+ ntohs(rqhost->sin_port));
+ if (map != NULL)
+ yp_error("client is referencing map \"%s\".", map);
+ }
+
+ /* Check the map name if one was supplied. */
+ if (map != NULL) {
+ if (strchr(map, '/')) {
+ yp_error("embedded slash in map name \"%s\" -- \
+possible spoof attempt from %s:%d",
+ map, inet_ntoa(rqhost->sin_addr),
+ ntohs(rqhost->sin_port));
+ return(1);
+ }
+#ifdef DB_CACHE
+ if ((yp_testflag((char *)map, (char *)domain, YP_SECURE) ||
+#else
+ if ((strstr(map, "master.passwd.") ||
+#endif
+ (rqstp->rq_prog == YPPROG &&
+ rqstp->rq_proc == YPPROC_XFR) ||
+ (rqstp->rq_prog == YPXFRD_FREEBSD_PROG &&
+ rqstp->rq_proc == YPXFRD_GETMAP)) &&
+ ntohs(rqhost->sin_port) >= IPPORT_RESERVED) {
+ yp_error("access to %s denied -- client %s:%d \
+not privileged", map, inet_ntoa(rqhost->sin_addr), ntohs(rqhost->sin_port));
+ return(1);
+ }
+ }
+
+#ifdef TCP_WRAPPER
+ status = hosts_ctl("ypserv", STRING_UNKNOWN,
+ inet_ntoa(rqhost->sin_addr), "");
+#else
+ tmp = securenets;
+ while (tmp) {
+ if (((rqhost->sin_addr.s_addr & ~tmp->mask.s_addr)
+ | tmp->net.s_addr) == rqhost->sin_addr.s_addr) {
+ status = 1;
+ break;
+ }
+ tmp = tmp->next;
+ }
+#endif
+
+ if (!status) {
+ if (rqhost->sin_addr.s_addr != oldaddr) {
+ yp_error("connect from %s:%d to procedure %s refused",
+ inet_ntoa(rqhost->sin_addr),
+ ntohs(rqhost->sin_port),
+ yp_procedure);
+ oldaddr = rqhost->sin_addr.s_addr;
+ }
+ return(1);
+ }
+ return(0);
+
+}
+
+int
+yp_validdomain(const char *domain)
+{
+ struct stat statbuf;
+ char dompath[MAXPATHLEN + 2];
+
+ if (domain == NULL || strstr(domain, "binding") ||
+ !strcmp(domain, ".") || !strcmp(domain, "..") ||
+ strchr(domain, '/') || strlen(domain) > YPMAXDOMAIN)
+ return(1);
+
+ snprintf(dompath, sizeof(dompath), "%s/%s", yp_dir, domain);
+
+ if (stat(dompath, &statbuf) < 0 || !S_ISDIR(statbuf.st_mode))
+ return(1);
+
+
+ return(0);
+}
diff --git a/usr.sbin/ypserv/yp_dblookup.c b/usr.sbin/ypserv/yp_dblookup.c
new file mode 100644
index 0000000..1c68626
--- /dev/null
+++ b/usr.sbin/ypserv/yp_dblookup.c
@@ -0,0 +1,735 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#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 TAILQ_HEAD(circlehead, circleq_entry) qhead;
+
+struct circleq_entry {
+ struct dbent *dbptr;
+ TAILQ_ENTRY(circleq_entry) links;
+};
+
+/*
+ * Initialize the circular queue.
+ */
+void
+yp_init_dbs(void)
+{
+ TAILQ_INIT(&qhead);
+ return;
+}
+
+/*
+ * Dynamically allocate an entry for the circular queue.
+ * Return a NULL pointer on failure.
+ */
+static struct circleq_entry *
+yp_malloc_qent(void)
+{
+ register struct circleq_entry *q;
+
+ q = (struct circleq_entry *)malloc(sizeof(struct circleq_entry));
+ if (q == NULL) {
+ yp_error("failed to malloc() circleq entry");
+ return(NULL);
+ }
+ bzero((char *)q, sizeof(struct circleq_entry));
+ q->dbptr = (struct dbent *)malloc(sizeof(struct dbent));
+ if (q->dbptr == NULL) {
+ yp_error("failed to malloc() circleq entry");
+ free(q);
+ return(NULL);
+ }
+ bzero((char *)q->dbptr, sizeof(struct dbent));
+
+ return(q);
+}
+
+/*
+ * Free a previously allocated circular queue
+ * entry.
+ */
+static void
+yp_free_qent(struct circleq_entry *q)
+{
+ /*
+ * First, close the database. In theory, this is also
+ * supposed to free the resources allocated by the DB
+ * package, including the memory pointed to by q->dbptr->key.
+ * This means we don't have to free q->dbptr->key here.
+ */
+ if (q->dbptr->dbp) {
+ (void)(q->dbptr->dbp->close)(q->dbptr->dbp);
+ q->dbptr->dbp = NULL;
+ }
+ /*
+ * Then free the database name, which was strdup()'ed.
+ */
+ free(q->dbptr->name);
+
+ /*
+ * Free the rest of the dbent struct.
+ */
+ free(q->dbptr);
+ q->dbptr = NULL;
+
+ /*
+ * Free the circleq struct.
+ */
+ free(q);
+ q = NULL;
+
+ return;
+}
+
+/*
+ * Zorch a single entry in the dbent queue and release
+ * all its resources. (This always removes the last entry
+ * in the queue.)
+ */
+static void
+yp_flush(void)
+{
+ register struct circleq_entry *qptr;
+
+ qptr = TAILQ_LAST(&qhead, circlehead);
+ TAILQ_REMOVE(&qhead, qptr, links);
+ yp_free_qent(qptr);
+ numdbs--;
+
+ return;
+}
+
+/*
+ * Close all databases, erase all database names and empty the queue.
+ */
+void
+yp_flush_all(void)
+{
+ register struct circleq_entry *qptr;
+
+ while (!TAILQ_EMPTY(&qhead)) {
+ qptr = TAILQ_FIRST(&qhead); /* save this */
+ TAILQ_REMOVE(&qhead, qptr, links);
+ yp_free_qent(qptr);
+ }
+ numdbs = 0;
+
+ return;
+}
+
+static char *inter_string = "YP_INTERDOMAIN";
+static char *secure_string = "YP_SECURE";
+static int inter_sz = sizeof("YP_INTERDOMAIN") - 1;
+static int secure_sz = sizeof("YP_SECURE") - 1;
+
+static int
+yp_setflags(DB *dbp)
+{
+ DBT key = { NULL, 0 }, data = { NULL, 0 };
+ int flags = 0;
+
+ key.data = inter_string;
+ key.size = inter_sz;
+
+ if (!(dbp->get)(dbp, &key, &data, 0))
+ flags |= YP_INTERDOMAIN;
+
+ key.data = secure_string;
+ key.size = secure_sz;
+
+ if (!(dbp->get)(dbp, &key, &data, 0))
+ flags |= YP_SECURE;
+
+ return(flags);
+}
+
+int
+yp_testflag(char *map, char *domain, int flag)
+{
+ char buf[MAXPATHLEN + 2];
+ register struct circleq_entry *qptr;
+
+ if (map == NULL || domain == NULL)
+ return(0);
+
+ strcpy(buf, domain);
+ strcat(buf, "/");
+ strcat(buf, map);
+
+ TAILQ_FOREACH(qptr, &qhead, links) {
+ if (!strcmp(qptr->dbptr->name, buf)) {
+ if (qptr->dbptr->flags & flag)
+ return(1);
+ else
+ return(0);
+ }
+ }
+
+ if (yp_open_db_cache(domain, map, NULL, 0) == NULL)
+ return(0);
+
+ if (TAILQ_FIRST(&qhead)->dbptr->flags & flag)
+ return(1);
+
+ return(0);
+}
+
+/*
+ * Add a DB handle and database name to the cache. We only maintain
+ * fixed number of entries in the cache, so if we're asked to store
+ * a new entry when all our slots are already filled, we have to kick
+ * out the entry in the last slot to make room.
+ */
+static int
+yp_cache_db(DB *dbp, char *name, int size)
+{
+ register struct circleq_entry *qptr;
+
+ if (numdbs == MAXDBS) {
+ if (ypdb_debug)
+ yp_error("queue overflow -- releasing last slot");
+ yp_flush();
+ }
+
+ /*
+ * Allocate a new queue entry.
+ */
+
+ if ((qptr = yp_malloc_qent()) == NULL) {
+ yp_error("failed to allocate a new cache entry");
+ return(1);
+ }
+
+ qptr->dbptr->dbp = dbp;
+ qptr->dbptr->name = strdup(name);
+ qptr->dbptr->size = size;
+ qptr->dbptr->key = NULL;
+
+ qptr->dbptr->flags = yp_setflags(dbp);
+
+ TAILQ_INSERT_HEAD(&qhead, qptr, links);
+ numdbs++;
+
+ return(0);
+}
+
+/*
+ * Search the list for a database matching 'name.' If we find it,
+ * move it to the head of the list and return its DB handle. If
+ * not, just fail: yp_open_db_cache() will subsequently try to open
+ * the database itself and call yp_cache_db() to add it to the
+ * list.
+ *
+ * The search works like this:
+ *
+ * - The caller specifies the name of a database to locate. We try to
+ * find an entry in our queue with a matching name.
+ *
+ * - If the caller doesn't specify a key or size, we assume that the
+ * first entry that we encounter with a matching name is returned.
+ * This will result in matches regardless of the key/size values
+ * stored in the queue entry.
+ *
+ * - If the caller also specifies a key and length, we check to see
+ * if the key and length saved in the queue entry also matches.
+ * This lets us return a DB handle that's already positioned at the
+ * correct location within a database.
+ *
+ * - Once we have a match, it gets migrated to the top of the queue
+ * so that it will be easier to find if another request for
+ * the same database comes in later.
+ */
+static DB *
+yp_find_db(const char *name, const char *key, int size)
+{
+ register struct circleq_entry *qptr;
+
+ TAILQ_FOREACH(qptr, &qhead, links) {
+ if (!strcmp(qptr->dbptr->name, name)) {
+ if (size) {
+ if (size != qptr->dbptr->size ||
+ strncmp(qptr->dbptr->key, key, size))
+ continue;
+ } else {
+ if (qptr->dbptr->size)
+ continue;
+ }
+ if (qptr != TAILQ_FIRST(&qhead)) {
+ TAILQ_REMOVE(&qhead, qptr, links);
+ TAILQ_INSERT_HEAD(&qhead, qptr, links);
+ }
+ return(qptr->dbptr->dbp);
+ }
+ }
+
+ return(NULL);
+}
+
+/*
+ * Open a DB database and cache the handle for later use. We first
+ * check the cache to see if the required database is already open.
+ * If so, we fetch the handle from the cache. If not, we try to open
+ * the database and save the handle in the cache for later use.
+ */
+DB *
+yp_open_db_cache(const char *domain, const char *map, const char *key,
+ const int size)
+{
+ DB *dbp = NULL;
+ char buf[MAXPATHLEN + 2];
+/*
+ snprintf(buf, sizeof(buf), "%s/%s", domain, map);
+*/
+ yp_errno = YP_TRUE;
+
+ strcpy(buf, domain);
+ strcat(buf, "/");
+ strcat(buf, map);
+
+ if ((dbp = yp_find_db(buf, key, size)) != NULL) {
+ return(dbp);
+ } else {
+ if ((dbp = yp_open_db(domain, map)) != NULL) {
+ if (yp_cache_db(dbp, buf, size)) {
+ (void)(dbp->close)(dbp);
+ yp_errno = YP_YPERR;
+ return(NULL);
+ }
+ }
+ }
+
+ return (dbp);
+}
+#endif
+
+/*
+ * Open a DB database.
+ */
+DB *
+yp_open_db(const char *domain, const char *map)
+{
+ DB *dbp = NULL;
+ char buf[MAXPATHLEN + 2];
+
+ yp_errno = YP_TRUE;
+
+ if (map[0] == '.' || strchr(map, '/')) {
+ yp_errno = YP_BADARGS;
+ return (NULL);
+ }
+
+#ifdef DB_CACHE
+ if (yp_validdomain(domain)) {
+ yp_errno = YP_NODOM;
+ return(NULL);
+ }
+#endif
+ snprintf(buf, sizeof(buf), "%s/%s/%s", yp_dir, domain, map);
+
+#ifdef DB_CACHE
+again:
+#endif
+ dbp = dbopen(buf,O_RDONLY, PERM_SECURE, DB_HASH, NULL);
+
+ if (dbp == NULL) {
+ switch (errno) {
+#ifdef DB_CACHE
+ case ENFILE:
+ /*
+ * We ran out of file descriptors. Nuke an
+ * open one and try again.
+ */
+ yp_error("ran out of file descriptors");
+ yp_flush();
+ goto again;
+ break;
+#endif
+ case ENOENT:
+ yp_errno = YP_NOMAP;
+ break;
+ case EFTYPE:
+ yp_errno = YP_BADDB;
+ break;
+ default:
+ yp_errno = YP_YPERR;
+ break;
+ }
+ }
+
+ return (dbp);
+}
+
+/*
+ * Database access routines.
+ *
+ * - yp_get_record(): retrieve an arbitrary key/data pair given one key
+ * to match against.
+ *
+ * - yp_first_record(): retrieve first key/data base in a database.
+ *
+ * - yp_next_record(): retrieve key/data pair that sequentially follows
+ * the supplied key value in the database.
+ */
+
+#ifdef DB_CACHE
+int
+yp_get_record(DB *dbp, const DBT *key, DBT *data, int allow)
+#else
+int
+yp_get_record(const char *domain, const char *map,
+ const DBT *key, DBT *data, int allow)
+#endif
+{
+#ifndef DB_CACHE
+ DB *dbp;
+#endif
+ int rval = 0;
+#ifndef DB_CACHE
+ static unsigned char buf[YPMAXRECORD];
+#endif
+
+ if (ypdb_debug)
+ yp_error("looking up key [%.*s]",
+ (int)key->size, (char *)key->data);
+
+ /*
+ * Avoid passing back magic "YP_*" entries unless
+ * the caller specifically requested them by setting
+ * the 'allow' flag.
+ */
+ if (!allow && !strncmp(key->data, "YP_", 3))
+ return(YP_NOKEY);
+
+#ifndef DB_CACHE
+ if ((dbp = yp_open_db(domain, map)) == NULL) {
+ return(yp_errno);
+ }
+#endif
+
+ if ((rval = (dbp->get)(dbp, key, data, 0)) != 0) {
+#ifdef DB_CACHE
+ TAILQ_FIRST(&qhead)->dbptr->size = 0;
+#else
+ (void)(dbp->close)(dbp);
+#endif
+ if (rval == 1)
+ return(YP_NOKEY);
+ else
+ return(YP_BADDB);
+ }
+
+ if (ypdb_debug)
+ yp_error("result of lookup: key: [%.*s] data: [%.*s]",
+ (int)key->size, (char *)key->data,
+ (int)data->size, (char *)data->data);
+
+#ifdef DB_CACHE
+ if (TAILQ_FIRST(&qhead)->dbptr->size) {
+ TAILQ_FIRST(&qhead)->dbptr->key = "";
+ TAILQ_FIRST(&qhead)->dbptr->size = 0;
+ }
+#else
+ bcopy(data->data, &buf, data->size);
+ data->data = &buf;
+ (void)(dbp->close)(dbp);
+#endif
+
+ return(YP_TRUE);
+}
+
+int
+yp_first_record(const DB *dbp, DBT *key, DBT *data, int allow)
+{
+ int rval;
+#ifndef DB_CACHE
+ static unsigned char buf[YPMAXRECORD];
+#endif
+
+ if (ypdb_debug)
+ yp_error("retrieving first key in map");
+
+ if ((rval = (dbp->seq)(dbp,key,data,R_FIRST)) != 0) {
+#ifdef DB_CACHE
+ TAILQ_FIRST(&qhead)->dbptr->size = 0;
+#endif
+ if (rval == 1)
+ return(YP_NOKEY);
+ else
+ return(YP_BADDB);
+ }
+
+ /* Avoid passing back magic "YP_*" records. */
+ while (!strncmp(key->data, "YP_", 3) && !allow) {
+ if ((rval = (dbp->seq)(dbp,key,data,R_NEXT)) != 0) {
+#ifdef DB_CACHE
+ TAILQ_FIRST(&qhead)->dbptr->size = 0;
+#endif
+ if (rval == 1)
+ return(YP_NOKEY);
+ else
+ return(YP_BADDB);
+ }
+ }
+
+ if (ypdb_debug)
+ yp_error("result of lookup: key: [%.*s] data: [%.*s]",
+ (int)key->size, (char *)key->data,
+ (int)data->size, (char *)data->data);
+
+#ifdef DB_CACHE
+ if (TAILQ_FIRST(&qhead)->dbptr->size) {
+ TAILQ_FIRST(&qhead)->dbptr->key = key->data;
+ TAILQ_FIRST(&qhead)->dbptr->size = key->size;
+ }
+#else
+ bcopy(data->data, &buf, data->size);
+ data->data = &buf;
+#endif
+
+ return(YP_TRUE);
+}
+
+int
+yp_next_record(const DB *dbp, DBT *key, DBT *data, int all, int allow)
+{
+ static DBT lkey = { NULL, 0 };
+ static DBT ldata = { NULL, 0 };
+ int rval;
+#ifndef DB_CACHE
+ static unsigned char keybuf[YPMAXRECORD];
+ static unsigned char datbuf[YPMAXRECORD];
+#endif
+
+ if (key == NULL || !key->size || key->data == NULL) {
+ rval = yp_first_record(dbp,key,data,allow);
+ if (rval == YP_NOKEY)
+ return(YP_NOMORE);
+ else {
+#ifdef DB_CACHE
+ TAILQ_FIRST(&qhead)->dbptr->key = key->data;
+ TAILQ_FIRST(&qhead)->dbptr->size = key->size;
+#endif
+ return(rval);
+ }
+ }
+
+ if (ypdb_debug)
+ yp_error("retrieving next key, previous was: [%.*s]",
+ (int)key->size, (char *)key->data);
+
+ if (!all) {
+#ifdef DB_CACHE
+ if (TAILQ_FIRST(&qhead)->dbptr->key == NULL) {
+#endif
+ (dbp->seq)(dbp,&lkey,&ldata,R_FIRST);
+ while (key->size != lkey.size ||
+ strncmp(key->data, lkey.data,
+ (int)key->size))
+ if ((dbp->seq)(dbp,&lkey,&ldata,R_NEXT)) {
+#ifdef DB_CACHE
+ TAILQ_FIRST(&qhead)->dbptr->size = 0;
+#endif
+ return(YP_NOKEY);
+ }
+
+#ifdef DB_CACHE
+ }
+#endif
+ }
+
+ if ((dbp->seq)(dbp,key,data,R_NEXT)) {
+#ifdef DB_CACHE
+ TAILQ_FIRST(&qhead)->dbptr->size = 0;
+#endif
+ return(YP_NOMORE);
+ }
+
+ /* Avoid passing back magic "YP_*" records. */
+ while (!strncmp(key->data, "YP_", 3) && !allow)
+ if ((dbp->seq)(dbp,key,data,R_NEXT)) {
+#ifdef DB_CACHE
+ TAILQ_FIRST(&qhead)->dbptr->size = 0;
+#endif
+ return(YP_NOMORE);
+ }
+
+ if (ypdb_debug)
+ yp_error("result of lookup: key: [%.*s] data: [%.*s]",
+ (int)key->size, (char *)key->data,
+ (int)data->size, (char *)data->data);
+
+#ifdef DB_CACHE
+ if (TAILQ_FIRST(&qhead)->dbptr->size) {
+ TAILQ_FIRST(&qhead)->dbptr->key = key->data;
+ TAILQ_FIRST(&qhead)->dbptr->size = key->size;
+ }
+#else
+ bcopy(key->data, &keybuf, key->size);
+ lkey.data = &keybuf;
+ lkey.size = key->size;
+ bcopy(data->data, &datbuf, data->size);
+ data->data = &datbuf;
+#endif
+
+ return(YP_TRUE);
+}
+
+#ifdef DB_CACHE
+/*
+ * Database glue functions.
+ */
+
+static DB *yp_currmap_db = NULL;
+static int yp_allow_db = 0;
+
+ypstat
+yp_select_map(char *map, char *domain, keydat *key, int allow)
+{
+ if (key == NULL)
+ yp_currmap_db = yp_open_db_cache(domain, map, NULL, 0);
+ else
+ yp_currmap_db = yp_open_db_cache(domain, map,
+ key->keydat_val,
+ key->keydat_len);
+
+ yp_allow_db = allow;
+ return(yp_errno);
+}
+
+ypstat
+yp_getbykey(keydat *key, valdat *val)
+{
+ DBT db_key = { NULL, 0 }, db_val = { NULL, 0 };
+ ypstat rval;
+
+ db_key.data = key->keydat_val;
+ db_key.size = key->keydat_len;
+
+ rval = yp_get_record(yp_currmap_db,
+ &db_key, &db_val, yp_allow_db);
+
+ if (rval == YP_TRUE) {
+ val->valdat_val = db_val.data;
+ val->valdat_len = db_val.size;
+ }
+
+ return(rval);
+}
+
+ypstat
+yp_firstbykey(keydat *key, valdat *val)
+{
+ DBT db_key = { NULL, 0 }, db_val = { NULL, 0 };
+ ypstat rval;
+
+ rval = yp_first_record(yp_currmap_db, &db_key, &db_val, yp_allow_db);
+
+ if (rval == YP_TRUE) {
+ key->keydat_val = db_key.data;
+ key->keydat_len = db_key.size;
+ val->valdat_val = db_val.data;
+ val->valdat_len = db_val.size;
+ }
+
+ return(rval);
+}
+
+ypstat
+yp_nextbykey(keydat *key, valdat *val)
+{
+ DBT db_key = { NULL, 0 }, db_val = { NULL, 0 };
+ ypstat rval;
+
+ db_key.data = key->keydat_val;
+ db_key.size = key->keydat_len;
+
+ rval = yp_next_record(yp_currmap_db, &db_key, &db_val, 0, yp_allow_db);
+
+ if (rval == YP_TRUE) {
+ key->keydat_val = db_key.data;
+ key->keydat_len = db_key.size;
+ val->valdat_val = db_val.data;
+ val->valdat_len = db_val.size;
+ }
+
+ return(rval);
+}
+#endif
diff --git a/usr.sbin/ypserv/yp_dnslookup.c b/usr.sbin/ypserv/yp_dnslookup.c
new file mode 100644
index 0000000..016cbb0c
--- /dev/null
+++ b/usr.sbin/ypserv/yp_dnslookup.c
@@ -0,0 +1,541 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#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(struct hostent *hp)
+{
+ static char result[MAXHOSTNAMELEN * 2];
+ int len,i;
+ struct in_addr addr;
+
+ if (hp == NULL)
+ return(NULL);
+
+ len = 16 + strlen(hp->h_name);
+ for (i = 0; hp->h_aliases[i]; i++)
+ len += strlen(hp->h_aliases[i]) + 1;
+ len++;
+
+ if (len > sizeof(result))
+ return(NULL);
+
+ bzero(result, sizeof(result));
+
+ bcopy(hp->h_addr, &addr, sizeof(struct in_addr));
+ snprintf(result, sizeof(result), "%s %s", inet_ntoa(addr), hp->h_name);
+
+ for (i = 0; hp->h_aliases[i]; i++) {
+ strcat(result, " ");
+ strcat(result, hp->h_aliases[i]);
+ }
+
+ return ((char *)&result);
+}
+
+#define MAXPACKET 1024
+#define DEF_TTL 50
+
+#define BY_DNS_ID 1
+#define BY_RPC_XID 2
+
+extern struct hostent *__dns_getanswer(char *, int, char *, int);
+
+static TAILQ_HEAD(dns_qhead, circleq_dnsentry) qhead;
+
+struct circleq_dnsentry {
+ SVCXPRT *xprt;
+ unsigned long xid;
+ struct sockaddr_in client_addr;
+ unsigned long ypvers;
+ unsigned long id;
+ unsigned long ttl;
+ unsigned long type;
+ unsigned short prot_type;
+ char **domain;
+ char *name;
+ struct in_addr addr;
+ TAILQ_ENTRY(circleq_dnsentry) links;
+};
+
+static int pending = 0;
+
+int
+yp_init_resolver(void)
+{
+ TAILQ_INIT(&qhead);
+ if (!(_res.options & RES_INIT) && res_init() == -1) {
+ yp_error("res_init failed");
+ return(1);
+ }
+ if ((resfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
+ yp_error("couldn't create socket");
+ return(1);
+ }
+ if (fcntl(resfd, F_SETFL, O_NONBLOCK) == -1) {
+ yp_error("couldn't make resolver socket non-blocking");
+ return(1);
+ }
+ return(0);
+}
+
+static struct
+circleq_dnsentry *yp_malloc_dnsent(void)
+{
+ register struct circleq_dnsentry *q;
+
+ q = (struct circleq_dnsentry *)malloc(sizeof(struct circleq_dnsentry));
+
+ if (q == NULL) {
+ yp_error("failed to malloc() circleq dns entry");
+ return(NULL);
+ }
+
+ return(q);
+}
+
+/*
+ * Transmit a query.
+ */
+static unsigned long
+yp_send_dns_query(char *name, int type)
+{
+ char buf[MAXPACKET];
+ int n;
+ HEADER *hptr;
+ int ns;
+ int rval;
+ unsigned long id;
+
+ bzero(buf, sizeof(buf));
+
+ n = res_mkquery(QUERY,name,C_IN,type,NULL,0,NULL,buf,sizeof(buf));
+
+ if (n <= 0) {
+ yp_error("res_mkquery failed");
+ return(0);
+ }
+
+ hptr = (HEADER *)&buf;
+ id = ntohs(hptr->id);
+
+ for (ns = 0; ns < _res.nscount; ns++) {
+ rval = sendto(resfd, buf, n, 0,
+ (struct sockaddr *)&_res.nsaddr_list[ns],
+ sizeof(struct sockaddr));
+ if (rval == -1) {
+ yp_error("sendto failed");
+ return(0);
+ }
+ }
+
+ return(id);
+}
+
+static struct circleq_dnsentry *
+yp_find_dnsqent(unsigned long id, int type)
+{
+ register struct circleq_dnsentry *q;
+
+ TAILQ_FOREACH(q, &qhead, links) {
+ switch (type) {
+ case BY_RPC_XID:
+ if (id == q->xid)
+ return(q);
+ break;
+ case BY_DNS_ID:
+ default:
+ if (id == q->id)
+ return(q);
+ break;
+ }
+ }
+ return (NULL);
+}
+
+static void
+yp_send_dns_reply(struct circleq_dnsentry *q, char *buf)
+{
+ ypresponse result_v1;
+ ypresp_val result_v2;
+ unsigned long xid;
+ struct sockaddr_in client_addr;
+ xdrproc_t xdrfunc;
+ char *result;
+
+ /*
+ * Set up correct reply struct and
+ * XDR filter depending on ypvers.
+ */
+ switch (q->ypvers) {
+ case YPVERS:
+ bzero((char *)&result_v2, sizeof(result_v2));
+
+ if (buf == NULL)
+ result_v2.stat = YP_NOKEY;
+ else {
+ result_v2.val.valdat_len = strlen(buf);
+ result_v2.val.valdat_val = buf;
+ result_v2.stat = YP_TRUE;
+ }
+ result = (char *)&result_v2;
+ xdrfunc = (xdrproc_t)xdr_ypresp_val;
+ break;
+ case YPOLDVERS:
+ /*
+ * The odds are we will _never_ execute this
+ * particular code, but we include it anyway
+ * for the sake of completeness.
+ */
+ bzero((char *)&result_v1, sizeof(result_v1));
+ result_v1.yp_resptype = YPRESP_VAL;
+
+#define YPVAL ypresponse_u.yp_resp_valtype
+ if (buf == NULL)
+ result_v1.YPVAL.stat = YP_NOKEY;
+ else {
+ result_v1.YPVAL.val.valdat_len = strlen(buf);
+ result_v1.YPVAL.val.valdat_val = buf;
+ result_v1.YPVAL.stat = YP_TRUE;
+ }
+ result = (char *)&result_v1;
+ xdrfunc = (xdrproc_t)xdr_ypresponse;
+ break;
+ default:
+ yp_error("bad YP program version (%lu)!", q->ypvers);
+ return;
+ break;
+ }
+
+ if (debug)
+ yp_error("sending dns reply to %s (%lu)",
+ inet_ntoa(q->client_addr.sin_addr), q->id);
+ /*
+ * XXX This is disgusting. There's basically one transport
+ * handle for UDP, but we're holding off on replying to a
+ * client until we're ready, by which time we may have received
+ * several other queries from other clients with different
+ * transaction IDs. So to make the delayed response thing work,
+ * we have to save the transaction ID and client address of
+ * each request, then jam them into the transport handle when
+ * we're ready to send a reply. Then after we've send the reply,
+ * we put the old transaction ID and remote address back the
+ * way we found 'em. This is _INCREDIBLY_ non-portable; it's
+ * not even supported by the RPC library.
+ */
+ /*
+ * XXX Don't frob the transaction ID for TCP handles.
+ */
+ if (q->prot_type == SOCK_DGRAM)
+ xid = svcudp_set_xid(q->xprt, q->xid);
+ client_addr = q->xprt->xp_raddr;
+ q->xprt->xp_raddr = q->client_addr;
+
+ if (!svc_sendreply(q->xprt, xdrfunc, result))
+ yp_error("svc_sendreply failed");
+
+ /*
+ * Now that we sent the reply,
+ * put the handle back the way it was.
+ */
+ if (q->prot_type == SOCK_DGRAM)
+ svcudp_set_xid(q->xprt, xid);
+ q->xprt->xp_raddr = client_addr;
+
+ return;
+}
+
+/*
+ * Decrement TTL on all queue entries, possibly nuking
+ * any that have been around too long without being serviced.
+ */
+void
+yp_prune_dnsq(void)
+{
+ register struct circleq_dnsentry *q, *n;
+
+ q = TAILQ_FIRST(&qhead);
+ while (q != NULL) {
+ q->ttl--;
+ n = TAILQ_NEXT(q, links);
+ if (!q->ttl) {
+ TAILQ_REMOVE(&qhead, q, links);
+ free(q->name);
+ free(q);
+ pending--;
+ }
+ q = n;
+ }
+
+ if (pending < 0)
+ pending = 0;
+
+ return;
+}
+
+/*
+ * Data is pending on the DNS socket; check for valid replies
+ * to our queries and dispatch them to waiting clients.
+ */
+void
+yp_run_dnsq(void)
+{
+ register struct circleq_dnsentry *q;
+ char buf[sizeof(HEADER) + MAXPACKET];
+ char retrybuf[MAXHOSTNAMELEN];
+ struct sockaddr_in sin;
+ int rval;
+ int len;
+ HEADER *hptr;
+ struct hostent *hent;
+
+ if (debug)
+ yp_error("running dns queue");
+
+ bzero(buf, sizeof(buf));
+
+ len = sizeof(struct sockaddr_in);
+ rval = recvfrom(resfd, buf, sizeof(buf), 0,
+ (struct sockaddr *)&sin, &len);
+
+ if (rval == -1) {
+ yp_error("recvfrom failed: %s", strerror(errno));
+ return;
+ }
+
+ /*
+ * We may have data left in the socket that represents
+ * replies to earlier queries that we don't care about
+ * anymore. If there are no lookups pending or the packet
+ * ID doesn't match any of the queue IDs, just drop it
+ * on the floor.
+ */
+ hptr = (HEADER *)&buf;
+ if (!pending ||
+ (q = yp_find_dnsqent(ntohs(hptr->id), BY_DNS_ID)) == NULL) {
+ /* ignore */
+ return;
+ }
+
+ if (debug)
+ yp_error("got dns reply from %s", inet_ntoa(sin.sin_addr));
+
+ hent = __dns_getanswer(buf, rval, q->name, q->type);
+
+ /*
+ * If 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--;
+ TAILQ_REMOVE(&qhead, q, links);
+ free(q->name);
+ free(q);
+
+ /* Decrement TTLs on other entries while we're here. */
+ yp_prune_dnsq();
+
+ return;
+}
+
+/*
+ * Queue and transmit an asynchronous DNS hostname lookup.
+ */
+ypstat
+yp_async_lookup_name(struct svc_req *rqstp, char *name)
+{
+ register struct circleq_dnsentry *q;
+ int type, len;
+
+ /* Check for SOCK_DGRAM or SOCK_STREAM -- we need to know later */
+ type = -1; len = sizeof(type);
+ if (getsockopt(rqstp->rq_xprt->xp_fd, SOL_SOCKET,
+ SO_TYPE, &type, &len) == -1) {
+ yp_error("getsockopt failed: %s", strerror(errno));
+ return(YP_YPERR);
+ }
+
+ /* Avoid transmitting dupe requests. */
+ if (type == SOCK_DGRAM &&
+ yp_find_dnsqent(svcudp_get_xid(rqstp->rq_xprt),BY_RPC_XID) != NULL)
+ return(YP_TRUE);
+
+ if ((q = yp_malloc_dnsent()) == NULL)
+ return(YP_YPERR);
+
+ q->type = T_A;
+ q->ttl = DEF_TTL;
+ q->xprt = rqstp->rq_xprt;
+ q->ypvers = rqstp->rq_vers;
+ q->prot_type = type;
+ if (q->prot_type == SOCK_DGRAM)
+ q->xid = svcudp_get_xid(q->xprt);
+ q->client_addr = q->xprt->xp_raddr;
+ q->domain = _res.dnsrch;
+ q->id = yp_send_dns_query(name, q->type);
+
+ if (q->id == 0) {
+ yp_error("DNS query failed");
+ free(q);
+ return(YP_YPERR);
+ }
+
+ q->name = strdup(name);
+ TAILQ_INSERT_HEAD(&qhead, q, links);
+ pending++;
+
+ if (debug)
+ yp_error("queueing async DNS name lookup (%d)", q->id);
+
+ yp_prune_dnsq();
+ return(YP_TRUE);
+}
+
+/*
+ * Queue and transmit an asynchronous DNS IP address lookup.
+ */
+ypstat
+yp_async_lookup_addr(struct svc_req *rqstp, char *addr)
+{
+ register struct circleq_dnsentry *q;
+ char buf[MAXHOSTNAMELEN];
+ int a, b, c, d;
+ int type, len;
+
+ /* Check for SOCK_DGRAM or SOCK_STREAM -- we need to know later */
+ type = -1; len = sizeof(type);
+ if (getsockopt(rqstp->rq_xprt->xp_fd, SOL_SOCKET,
+ SO_TYPE, &type, &len) == -1) {
+ yp_error("getsockopt failed: %s", strerror(errno));
+ return(YP_YPERR);
+ }
+
+ /* Avoid transmitting dupe requests. */
+ if (type == SOCK_DGRAM &&
+ yp_find_dnsqent(svcudp_get_xid(rqstp->rq_xprt),BY_RPC_XID) != NULL)
+ return(YP_TRUE);
+
+ if ((q = yp_malloc_dnsent()) == NULL)
+ return(YP_YPERR);
+
+ if (sscanf(addr, "%d.%d.%d.%d", &a, &b, &c, &d) != 4)
+ return(YP_NOKEY);
+
+ snprintf(buf, sizeof(buf), "%d.%d.%d.%d.in-addr.arpa", d, c, b, a);
+
+ if (debug)
+ yp_error("DNS address is: %s", buf);
+
+ q->type = T_PTR;
+ q->ttl = DEF_TTL;
+ q->xprt = rqstp->rq_xprt;
+ q->ypvers = rqstp->rq_vers;
+ q->domain = NULL;
+ q->prot_type = type;
+ if (q->prot_type == SOCK_DGRAM)
+ q->xid = svcudp_get_xid(q->xprt);
+ q->client_addr = q->xprt->xp_raddr;
+ q->id = yp_send_dns_query(buf, q->type);
+
+ if (q->id == 0) {
+ yp_error("DNS query failed");
+ free(q);
+ return(YP_YPERR);
+ }
+
+ inet_aton(addr, &q->addr);
+ q->name = strdup(buf);
+ TAILQ_INSERT_HEAD(&qhead, q, links);
+ pending++;
+
+ if (debug)
+ yp_error("queueing async DNS address lookup (%d)", q->id);
+
+ yp_prune_dnsq();
+ return(YP_TRUE);
+}
diff --git a/usr.sbin/ypserv/yp_error.c b/usr.sbin/ypserv/yp_error.c
new file mode 100644
index 0000000..020d5f7
--- /dev/null
+++ b/usr.sbin/ypserv/yp_error.c
@@ -0,0 +1,75 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/*
+ * error logging/reporting facilities
+ * stolen from /usr/libexec/mail.local via ypserv
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include "yp_extern.h"
+
+int debug;
+extern int _rpcpmstart;
+
+extern char *progname;
+
+static void __verr(const char *fmt, va_list ap) __printflike(1, 0);
+
+static void __verr(const char *fmt, va_list ap)
+{
+ if (debug && !_rpcpmstart) {
+ fprintf(stderr,"%s: ",progname);
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ } else {
+ vsyslog(LOG_NOTICE, fmt, ap);
+ }
+}
+
+void
+yp_error(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ __verr(fmt,ap);
+ va_end(ap);
+}
diff --git a/usr.sbin/ypserv/yp_extern.h b/usr.sbin/ypserv/yp_extern.h
new file mode 100644
index 0000000..553e296
--- /dev/null
+++ b/usr.sbin/ypserv/yp_extern.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 1995
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <db.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/yp.h>
+
+#ifndef _PATH_YP
+#define _PATH_YP "/var/yp/"
+#endif
+
+#ifndef _PATH_LIBEXEC
+#define _PATH_LIBEXEC "/usr/libexec/"
+#endif
+
+#ifndef MAX_CHILDREN
+#define MAX_CHILDREN 20
+#endif
+
+#define YP_SECURE 0x1
+#define YP_INTERDOMAIN 0x2
+
+/*
+ * External functions and variables.
+ */
+
+extern int debug;
+extern int ypdb_debug;
+extern int do_dns;
+extern int children;
+extern int resfd;
+extern char *progname;
+extern char *yp_dir;
+extern pid_t yp_pid;
+
+extern enum ypstat yp_errno;
+extern void yp_error(const char *, ...) __printflike(1, 2);
+#ifdef DB_CACHE
+extern int yp_get_record(DB *, const DBT *, DBT *, int);
+#else
+extern int yp_get_record(const char *, const char *, const DBT *, DBT *, int);
+#endif
+extern int yp_first_record(const DB *, DBT *, DBT *, int);
+extern int yp_next_record(const DB *, DBT *, DBT *, int, int);
+extern char *yp_dnsname(char *);
+extern char *yp_dnsaddr(const char *);
+#ifdef DB_CACHE
+extern int yp_access(const char *, const char *, const struct svc_req *);
+#else
+extern int yp_access(const char *, const struct svc_req *);
+#endif
+extern int yp_validdomain(const char *);
+extern DB *yp_open_db(const char *, const char *);
+extern DB *yp_open_db_cache(const char *, const char *, const char *, int);
+extern void yp_flush_all(void);
+extern void yp_init_dbs(void);
+extern int yp_testflag(char *, char *, int);
+extern void load_securenets(void);
+
+#ifdef DB_CACHE
+extern ypstat yp_select_map(char *, char *, keydat *, int);
+extern ypstat yp_getbykey(keydat *, valdat *);
+extern ypstat yp_firstbykey(keydat *, valdat *);
+extern ypstat yp_nextbykey(keydat *, valdat *);
+#endif
+
+extern unsigned long svcudp_set_xid(SVCXPRT *, unsigned long);
+extern unsigned long svcudp_get_xid(SVCXPRT *);
+
+#ifndef RESOLVER_TIMEOUT
+#define RESOLVER_TIMEOUT 3600
+#endif
+
+extern int yp_init_resolver(void);
+extern void yp_run_dnsq(void);
+extern void yp_prune_dnsq(void);
+extern ypstat yp_async_lookup_name(struct svc_req *, char *);
+extern ypstat yp_async_lookup_addr(struct svc_req *, char *);
diff --git a/usr.sbin/ypserv/yp_main.c b/usr.sbin/ypserv/yp_main.c
new file mode 100644
index 0000000..e7f6b0b
--- /dev/null
+++ b/usr.sbin/ypserv/yp_main.c
@@ -0,0 +1,340 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#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 */
+#ifdef __cplusplus
+#include <sysent.h> /* getdtablesize, open */
+#endif /* __cplusplus */
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/wait.h>
+#include "yp_extern.h"
+#include <rpc/rpc.h>
+
+#ifndef SIG_PF
+#define SIG_PF void(*)(int)
+#endif
+
+#define _RPCSVC_CLOSEDOWN 120
+int _rpcpmstart; /* Started by a port monitor ? */
+static int _rpcfdtype;
+ /* Whether Stream or Datagram ? */
+ /* States a server can be in wrt request */
+
+#define _IDLE 0
+#define _SERVED 1
+#define _SERVING 2
+
+extern void ypprog_1(struct svc_req *, register SVCXPRT *);
+extern void ypprog_2(struct svc_req *, register SVCXPRT *);
+extern int _rpc_dtablesize(void);
+extern int _rpcsvcstate; /* Set when a request is serviced */
+char *progname = "ypserv";
+char *yp_dir = _PATH_YP;
+/*int debug = 0;*/
+int do_dns = 0;
+int resfd;
+
+static
+void _msgout(char* msg)
+{
+ if (debug) {
+ if (_rpcpmstart)
+ syslog(LOG_ERR, "%s", msg);
+ else
+ warnx("%s", msg);
+ } else
+ syslog(LOG_ERR, "%s", msg);
+}
+
+pid_t yp_pid;
+
+static void
+yp_svc_run(void)
+{
+#ifdef FD_SETSIZE
+ fd_set readfds;
+#else
+ int readfds;
+#endif /* def FD_SETSIZE */
+ extern int forked;
+ int fd_setsize = _rpc_dtablesize();
+ struct timeval timeout;
+
+ /* Establish the identity of the parent ypserv process. */
+ yp_pid = getpid();
+
+ for (;;) {
+#ifdef FD_SETSIZE
+ readfds = svc_fdset;
+#else
+ readfds = svc_fds;
+#endif /* def FD_SETSIZE */
+
+ FD_SET(resfd, &readfds);
+
+ timeout.tv_sec = RESOLVER_TIMEOUT;
+ timeout.tv_usec = 0;
+ switch (select(fd_setsize, &readfds, NULL, NULL,
+ &timeout)) {
+ case -1:
+ if (errno == EINTR) {
+ continue;
+ }
+ warn("svc_run: - select failed");
+ return;
+ case 0:
+ if (getpid() == yp_pid)
+ yp_prune_dnsq();
+ break;
+ default:
+ if (getpid() == yp_pid) {
+ if (FD_ISSET(resfd, &readfds)) {
+ yp_run_dnsq();
+ FD_CLR(resfd, &readfds);
+ }
+ svc_getreqset(&readfds);
+ }
+ }
+ if (yp_pid != getpid())
+ _exit(0);
+ }
+}
+
+static void
+unregister(void)
+{
+ (void) pmap_unset(YPPROG, YPVERS);
+ (void) pmap_unset(YPPROG, YPOLDVERS);
+}
+
+static void
+reaper(int sig)
+{
+ int status;
+ int saved_errno;
+
+ saved_errno = errno;
+
+ if (sig == SIGHUP) {
+ load_securenets();
+#ifdef DB_CACHE
+ yp_flush_all();
+#endif
+ errno = saved_errno;
+ return;
+ }
+
+ if (sig == SIGCHLD) {
+ while (wait3(&status, WNOHANG, NULL) > 0)
+ children--;
+ } else {
+ unregister();
+ exit(0);
+ }
+ errno = saved_errno;
+ return;
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: ypserv [-h] [-d] [-n] [-p path]\n");
+ exit(1);
+}
+
+static void
+closedown(int sig)
+{
+ if (_rpcsvcstate == _IDLE) {
+ extern fd_set svc_fdset;
+ static int size;
+ int i, openfd;
+
+ if (_rpcfdtype == SOCK_DGRAM) {
+ unregister();
+ exit(0);
+ }
+ if (size == 0) {
+ size = getdtablesize();
+ }
+ for (i = 0, openfd = 0; i < size && openfd < 2; i++)
+ if (FD_ISSET(i, &svc_fdset))
+ openfd++;
+ if (openfd <= 1) {
+ unregister();
+ exit(0);
+ }
+ }
+ if (_rpcsvcstate == _SERVED)
+ _rpcsvcstate = _IDLE;
+
+ (void) signal(SIGALRM, (SIG_PF) closedown);
+ (void) alarm(_RPCSVC_CLOSEDOWN/2);
+}
+
+int
+main(int argc, char *argv[])
+{
+ register SVCXPRT *transp = NULL;
+ int sock;
+ int proto = 0;
+ struct sockaddr_in saddr;
+ int asize = sizeof (saddr);
+ int ch;
+
+ while ((ch = getopt(argc, argv, "hdnp:")) != -1) {
+ switch (ch) {
+ case 'd':
+ debug = ypdb_debug = 1;
+ break;
+ case 'n':
+ do_dns = 1;
+ break;
+ case 'p':
+ yp_dir = optarg;
+ break;
+ case 'h':
+ default:
+ usage();
+ }
+ }
+
+ load_securenets();
+ yp_init_resolver();
+#ifdef DB_CACHE
+ yp_init_dbs();
+#endif
+ if (getsockname(0, (struct sockaddr *)&saddr, &asize) == 0) {
+ int ssize = sizeof (int);
+
+ if (saddr.sin_family != AF_INET)
+ exit(1);
+ if (getsockopt(0, SOL_SOCKET, SO_TYPE,
+ (char *)&_rpcfdtype, &ssize) == -1)
+ exit(1);
+ sock = 0;
+ _rpcpmstart = 1;
+ proto = 0;
+ openlog("ypserv", LOG_PID, LOG_DAEMON);
+ } else {
+ if (!debug) {
+ if (daemon(0,0)) {
+ err(1,"cannot fork");
+ }
+ openlog("ypserv", LOG_PID, LOG_DAEMON);
+ }
+ sock = RPC_ANYSOCK;
+ (void) pmap_unset(YPPROG, YPVERS);
+ (void) pmap_unset(YPPROG, 1);
+ }
+
+ if ((_rpcfdtype == 0) || (_rpcfdtype == SOCK_DGRAM)) {
+ transp = svcudp_create(sock);
+ if (transp == NULL) {
+ _msgout("cannot create udp service");
+ exit(1);
+ }
+ if (!_rpcpmstart)
+ proto = IPPROTO_UDP;
+ if (!svc_register(transp, YPPROG, YPOLDVERS, ypprog_1, proto)) {
+ _msgout("unable to register (YPPROG, YPOLDVERS, udp)");
+ exit(1);
+ }
+ if (!svc_register(transp, YPPROG, YPVERS, ypprog_2, proto)) {
+ _msgout("unable to register (YPPROG, YPVERS, udp)");
+ exit(1);
+ }
+ }
+
+ if ((_rpcfdtype == 0) || (_rpcfdtype == SOCK_STREAM)) {
+ transp = svctcp_create(sock, 0, 0);
+ if (transp == NULL) {
+ _msgout("cannot create tcp service");
+ exit(1);
+ }
+ if (!_rpcpmstart)
+ proto = IPPROTO_TCP;
+ if (!svc_register(transp, YPPROG, YPOLDVERS, ypprog_1, proto)) {
+ _msgout("unable to register (YPPROG, YPOLDVERS, tcp)");
+ exit(1);
+ }
+ if (!svc_register(transp, YPPROG, YPVERS, ypprog_2, proto)) {
+ _msgout("unable to register (YPPROG, YPVERS, tcp)");
+ exit(1);
+ }
+ }
+
+ if (transp == (SVCXPRT *)NULL) {
+ _msgout("could not create a handle");
+ exit(1);
+ }
+ if (_rpcpmstart) {
+ (void) signal(SIGALRM, (SIG_PF) closedown);
+ (void) alarm(_RPCSVC_CLOSEDOWN/2);
+ }
+/*
+ * Make sure SIGPIPE doesn't blow us away while servicing TCP
+ * connections.
+ */
+ (void) signal(SIGPIPE, SIG_IGN);
+ (void) signal(SIGCHLD, (SIG_PF) reaper);
+ (void) signal(SIGTERM, (SIG_PF) reaper);
+ (void) signal(SIGINT, (SIG_PF) reaper);
+ (void) signal(SIGHUP, (SIG_PF) reaper);
+ yp_svc_run();
+ _msgout("svc_run returned");
+ exit(1);
+ /* NOTREACHED */
+}
diff --git a/usr.sbin/ypserv/yp_server.c b/usr.sbin/ypserv/yp_server.c
new file mode 100644
index 0000000..5ab4628
--- /dev/null
+++ b/usr.sbin/ypserv/yp_server.c
@@ -0,0 +1,975 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#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 children = 0;
+
+#define MASTER_STRING "YP_MASTER_NAME"
+#define MASTER_SZ sizeof(MASTER_STRING) - 1
+#define ORDER_STRING "YP_LAST_MODIFIED"
+#define ORDER_SZ sizeof(ORDER_STRING) - 1
+
+static pid_t
+yp_fork(void)
+{
+ if (yp_pid != getpid()) {
+ yp_error("child %d trying to fork!", getpid());
+ errno = EEXIST;
+ return(-1);
+ }
+
+ return(fork());
+}
+
+/*
+ * NIS v2 support. This is where most of the action happens.
+ */
+
+void *
+ypproc_null_2_svc(void *argp, struct svc_req *rqstp)
+{
+ static char * result;
+ static char rval = 0;
+
+#ifdef DB_CACHE
+ if (yp_access(NULL, NULL, (struct svc_req *)rqstp))
+#else
+ if (yp_access(NULL, (struct svc_req *)rqstp))
+#endif
+ return(NULL);
+
+ result = &rval;
+
+ return((void *) &result);
+}
+
+bool_t *
+ypproc_domain_2_svc(domainname *argp, struct svc_req *rqstp)
+{
+ static bool_t result;
+
+#ifdef DB_CACHE
+ if (yp_access(NULL, NULL, (struct svc_req *)rqstp)) {
+#else
+ if (yp_access(NULL, (struct svc_req *)rqstp)) {
+#endif
+ result = FALSE;
+ return (&result);
+ }
+
+ if (argp == NULL || yp_validdomain(*argp))
+ result = FALSE;
+ else
+ result = TRUE;
+
+ return (&result);
+}
+
+bool_t *
+ypproc_domain_nonack_2_svc(domainname *argp, struct svc_req *rqstp)
+{
+ static bool_t result;
+
+#ifdef DB_CACHE
+ if (yp_access(NULL, NULL, (struct svc_req *)rqstp))
+#else
+ if (yp_access(NULL, (struct svc_req *)rqstp))
+#endif
+ return (NULL);
+
+ if (argp == NULL || yp_validdomain(*argp))
+ return (NULL);
+ else
+ result = TRUE;
+
+ return (&result);
+}
+
+ypresp_val *
+ypproc_match_2_svc(ypreq_key *argp, struct svc_req *rqstp)
+{
+ static ypresp_val result;
+
+ result.val.valdat_val = "";
+ result.val.valdat_len = 0;
+
+#ifdef DB_CACHE
+ if (yp_access(argp->map, argp->domain, (struct svc_req *)rqstp)) {
+#else
+ if (yp_access(argp->map, (struct svc_req *)rqstp)) {
+#endif
+ result.stat = YP_YPERR;
+ return (&result);
+ }
+
+ if (argp->domain == NULL || argp->map == NULL) {
+ result.stat = YP_BADARGS;
+ return (&result);
+ }
+
+ if (yp_select_map(argp->map, argp->domain, NULL, 1) != YP_TRUE) {
+ result.stat = yp_errno;
+ return(&result);
+ }
+
+ result.stat = yp_getbykey(&argp->key, &result.val);
+
+ /*
+ * Do DNS lookups for hosts maps if database lookup failed.
+ */
+
+#ifdef DB_CACHE
+ if (result.stat != YP_TRUE &&
+ (yp_testflag(argp->map, argp->domain, YP_INTERDOMAIN) ||
+ (strstr(argp->map, "hosts") && do_dns))) {
+#else
+ if (do_dns && result.stat != YP_TRUE && strstr(argp->map, "hosts")) {
+#endif
+ char nbuf[YPMAXRECORD];
+
+ /* NUL terminate! NUL terminate!! NUL TERMINATE!!! */
+ bcopy(argp->key.keydat_val, nbuf, argp->key.keydat_len);
+ nbuf[argp->key.keydat_len] = '\0';
+
+ if (debug)
+ yp_error("doing DNS lookup of %s", nbuf);
+
+ if (!strcmp(argp->map, "hosts.byname"))
+ result.stat = yp_async_lookup_name(rqstp, nbuf);
+ else if (!strcmp(argp->map, "hosts.byaddr"))
+ result.stat = yp_async_lookup_addr(rqstp, nbuf);
+
+ if (result.stat == YP_TRUE)
+ return(NULL);
+ }
+
+ return (&result);
+}
+
+ypresp_key_val *
+ypproc_first_2_svc(ypreq_nokey *argp, struct svc_req *rqstp)
+{
+ static ypresp_key_val result;
+
+ result.val.valdat_val = result.key.keydat_val = "";
+ result.val.valdat_len = result.key.keydat_len = 0;
+
+#ifdef DB_CACHE
+ if (yp_access(argp->map, argp->domain, (struct svc_req *)rqstp)) {
+#else
+ if (yp_access(argp->map, (struct svc_req *)rqstp)) {
+#endif
+ result.stat = YP_YPERR;
+ return (&result);
+ }
+
+ if (argp->domain == NULL) {
+ result.stat = YP_BADARGS;
+ return (&result);
+ }
+
+ if (yp_select_map(argp->map, argp->domain, NULL, 0) != YP_TRUE) {
+ result.stat = yp_errno;
+ return(&result);
+ }
+
+ result.stat = yp_firstbykey(&result.key, &result.val);
+
+ return (&result);
+}
+
+ypresp_key_val *
+ypproc_next_2_svc(ypreq_key *argp, struct svc_req *rqstp)
+{
+ static ypresp_key_val result;
+
+ result.val.valdat_val = result.key.keydat_val = "";
+ result.val.valdat_len = result.key.keydat_len = 0;
+
+#ifdef DB_CACHE
+ if (yp_access(argp->map, argp->domain, (struct svc_req *)rqstp)) {
+#else
+ if (yp_access(argp->map, (struct svc_req *)rqstp)) {
+#endif
+ result.stat = YP_YPERR;
+ return (&result);
+ }
+
+ if (argp->domain == NULL || argp->map == NULL) {
+ result.stat = YP_BADARGS;
+ return (&result);
+ }
+
+ if (yp_select_map(argp->map, argp->domain, &argp->key, 0) != YP_TRUE) {
+ result.stat = yp_errno;
+ return(&result);
+ }
+
+ result.key.keydat_len = argp->key.keydat_len;
+ result.key.keydat_val = argp->key.keydat_val;
+
+ result.stat = yp_nextbykey(&result.key, &result.val);
+
+ return (&result);
+}
+
+static void
+ypxfr_callback(ypxfrstat rval, struct sockaddr_in *addr, unsigned int transid,
+ unsigned int prognum, unsigned long port)
+{
+ CLIENT *clnt;
+ int sock = RPC_ANYSOCK;
+ struct timeval timeout;
+ yppushresp_xfr ypxfr_resp;
+ struct rpc_err err;
+
+ timeout.tv_sec = 5;
+ timeout.tv_usec = 0;
+ addr->sin_port = htons(port);
+
+ if ((clnt = clntudp_create(addr,prognum,1,timeout,&sock)) == NULL) {
+ yp_error("%s: %s", inet_ntoa(addr->sin_addr),
+ clnt_spcreateerror("failed to establish callback handle"));
+ return;
+ }
+
+ ypxfr_resp.status = rval;
+ ypxfr_resp.transid = transid;
+
+ /* Turn the timeout off -- we don't want to block. */
+ timeout.tv_sec = 0;
+ if (clnt_control(clnt, CLSET_TIMEOUT, &timeout) == FALSE)
+ yp_error("failed to set timeout on ypproc_xfr callback");
+
+ if (yppushproc_xfrresp_1(&ypxfr_resp, clnt) == NULL) {
+ clnt_geterr(clnt, &err);
+ if (err.re_status != RPC_SUCCESS &&
+ err.re_status != RPC_TIMEDOUT)
+ yp_error("%s", clnt_sperror(clnt,
+ "ypxfr callback failed"));
+ }
+
+ clnt_destroy(clnt);
+ return;
+}
+
+#define YPXFR_RETURN(CODE) \
+ /* Order is important: send regular RPC reply, then callback */ \
+ result.xfrstat = CODE; \
+ svc_sendreply(rqstp->rq_xprt, (xdrproc_t)xdr_ypresp_xfr, &result); \
+ ypxfr_callback(CODE,rqhost,argp->transid, \
+ argp->prog,argp->port); \
+ return(NULL);
+
+ypresp_xfr *
+ypproc_xfr_2_svc(ypreq_xfr *argp, struct svc_req *rqstp)
+{
+ static ypresp_xfr result;
+ struct sockaddr_in *rqhost;
+ ypresp_master *mres;
+ ypreq_nokey mreq;
+
+ result.transid = argp->transid;
+ rqhost = svc_getcaller(rqstp->rq_xprt);
+
+#ifdef DB_CACHE
+ if (yp_access(argp->map_parms.map,
+ argp->map_parms.domain, (struct svc_req *)rqstp)) {
+#else
+ if (yp_access(argp->map_parms.map, (struct svc_req *)rqstp)) {
+#endif
+ YPXFR_RETURN(YPXFR_REFUSED)
+ }
+
+
+ if (argp->map_parms.domain == NULL) {
+ YPXFR_RETURN(YPXFR_BADARGS)
+ }
+
+ if (yp_validdomain(argp->map_parms.domain)) {
+ YPXFR_RETURN(YPXFR_NODOM)
+ }
+
+ /*
+ * Determine the master host ourselves. The caller may
+ * be up to no good. This has the side effect of verifying
+ * that the requested map and domain actually exist.
+ */
+
+ mreq.domain = argp->map_parms.domain;
+ mreq.map = argp->map_parms.map;
+
+ mres = ypproc_master_2_svc(&mreq, rqstp);
+
+ if (mres->stat != YP_TRUE) {
+ yp_error("couldn't find master for map %s@%s",
+ argp->map_parms.map,
+ argp->map_parms.domain);
+ yp_error("host at %s (%s) may be pulling my leg",
+ argp->map_parms.peer,
+ inet_ntoa(rqhost->sin_addr));
+ YPXFR_RETURN(YPXFR_REFUSED)
+ }
+
+ switch (yp_fork()) {
+ case 0:
+ {
+ char g[11], t[11], p[11];
+ char ypxfr_command[MAXPATHLEN + 2];
+
+ snprintf (ypxfr_command, sizeof(ypxfr_command), "%sypxfr", _PATH_LIBEXEC);
+ snprintf (t, sizeof(t), "%u", argp->transid);
+ snprintf (g, sizeof(g), "%u", argp->prog);
+ snprintf (p, sizeof(p), "%u", argp->port);
+ if (debug) {
+ close(0); close(1); close(2);
+ }
+ if (strcmp(yp_dir, _PATH_YP)) {
+ execl(ypxfr_command, "ypxfr",
+ "-d", argp->map_parms.domain,
+ "-h", mres->peer,
+ "-p", yp_dir, "-C", t,
+ g, inet_ntoa(rqhost->sin_addr),
+ p, argp->map_parms.map,
+ NULL);
+ } else {
+ execl(ypxfr_command, "ypxfr",
+ "-d", argp->map_parms.domain,
+ "-h", mres->peer,
+ "-C", t,
+ g, inet_ntoa(rqhost->sin_addr),
+ p, argp->map_parms.map,
+ NULL);
+ }
+ yp_error("ypxfr execl(%s): %s", ypxfr_command, strerror(errno));
+ YPXFR_RETURN(YPXFR_XFRERR)
+ /*
+ * Just to safe, prevent PR #10970 from biting us in
+ * the unlikely case that execing ypxfr fails. We don't
+ * want to have any child processes spawned from this
+ * child process.
+ */
+ _exit(0);
+ break;
+ }
+ case -1:
+ yp_error("ypxfr fork(): %s", strerror(errno));
+ YPXFR_RETURN(YPXFR_XFRERR)
+ break;
+ default:
+ result.xfrstat = YPXFR_SUCC;
+ children++;
+ break;
+ }
+
+ return (&result);
+}
+#undef YPXFR_RETURN
+
+void *
+ypproc_clear_2_svc(void *argp, struct svc_req *rqstp)
+{
+ static char * result;
+ static char rval = 0;
+
+#ifdef DB_CACHE
+ if (yp_access(NULL, NULL, (struct svc_req *)rqstp))
+#else
+ if (yp_access(NULL, (struct svc_req *)rqstp))
+#endif
+ return (NULL);
+#ifdef DB_CACHE
+ /* clear out the database cache */
+ yp_flush_all();
+#endif
+ /* Re-read the securenets database for the hell of it. */
+ load_securenets();
+
+ result = &rval;
+ return((void *) &result);
+}
+
+/*
+ * For ypproc_all, we have to send a stream of ypresp_all structures
+ * via TCP, but the XDR filter generated from the yp.x protocol
+ * definition file only serializes one such structure. This means that
+ * to send the whole stream, you need a wrapper which feeds all the
+ * records into the underlying XDR routine until it hits an 'EOF.'
+ * But to use the wrapper, you have to violate the boundaries between
+ * RPC layers by calling svc_sendreply() directly from the ypproc_all
+ * service routine instead of letting the RPC dispatcher do it.
+ *
+ * Bleah.
+ */
+
+/*
+ * Custom XDR routine for serialzing results of ypproc_all: keep
+ * reading from the database and spew until we run out of records
+ * or encounter an error.
+ */
+static bool_t
+xdr_my_ypresp_all(register XDR *xdrs, ypresp_all *objp)
+{
+ while (1) {
+ /* Get a record. */
+ if ((objp->ypresp_all_u.val.stat =
+ yp_nextbykey(&objp->ypresp_all_u.val.key,
+ &objp->ypresp_all_u.val.val)) == YP_TRUE) {
+ objp->more = TRUE;
+ } else {
+ objp->more = FALSE;
+ }
+
+ /* Serialize. */
+ if (!xdr_ypresp_all(xdrs, objp))
+ return(FALSE);
+ if (objp->more == FALSE)
+ return(TRUE);
+ }
+}
+
+ypresp_all *
+ypproc_all_2_svc(ypreq_nokey *argp, struct svc_req *rqstp)
+{
+ static ypresp_all result;
+
+ /*
+ * Set this here so that the client will be forced to make
+ * at least one attempt to read from us even if all we're
+ * doing is returning an error.
+ */
+ result.more = TRUE;
+ result.ypresp_all_u.val.key.keydat_len = 0;
+ result.ypresp_all_u.val.key.keydat_val = "";
+
+#ifdef DB_CACHE
+ if (yp_access(argp->map, argp->domain, (struct svc_req *)rqstp)) {
+#else
+ if (yp_access(argp->map, (struct svc_req *)rqstp)) {
+#endif
+ result.ypresp_all_u.val.stat = YP_YPERR;
+ return (&result);
+ }
+
+ if (argp->domain == NULL || argp->map == NULL) {
+ result.ypresp_all_u.val.stat = YP_BADARGS;
+ return (&result);
+ }
+
+ /*
+ * XXX If we hit the child limit, fail the request.
+ * If we don't, and the map is large, we could block for
+ * a long time in the parent.
+ */
+ if (children >= MAX_CHILDREN) {
+ result.ypresp_all_u.val.stat = YP_YPERR;
+ return(&result);
+ }
+
+ /*
+ * The ypproc_all procedure can take a while to complete.
+ * Best to handle it in a subprocess so the parent doesn't
+ * block. (Is there a better way to do this? Maybe with
+ * async socket I/O?)
+ */
+ if (!debug) {
+ switch (yp_fork()) {
+ case 0:
+ break;
+ case -1:
+ yp_error("ypall fork(): %s", strerror(errno));
+ result.ypresp_all_u.val.stat = YP_YPERR;
+ return(&result);
+ break;
+ default:
+ children++;
+ return (NULL);
+ break;
+ }
+ }
+
+ /*
+ * Fix for PR #10971: don't let the child ypserv share
+ * DB handles with the parent process.
+ */
+#ifdef DB_CACHE
+ yp_flush_all();
+#endif
+
+ if (yp_select_map(argp->map, argp->domain,
+ &result.ypresp_all_u.val.key, 0) != YP_TRUE) {
+ result.ypresp_all_u.val.stat = yp_errno;
+ return(&result);
+ }
+
+ /* Kick off the actual data transfer. */
+ svc_sendreply(rqstp->rq_xprt, (xdrproc_t)xdr_my_ypresp_all, &result);
+
+ /*
+ * Proper fix for PR #10970: exit here so that we don't risk
+ * having a child spawned from this sub-process.
+ */
+ _exit(0);
+}
+
+ypresp_master *
+ypproc_master_2_svc(ypreq_nokey *argp, struct svc_req *rqstp)
+{
+ static ypresp_master result;
+ static char ypvalbuf[YPMAXRECORD];
+ keydat key = { MASTER_SZ, MASTER_STRING };
+ valdat val;
+
+ result.peer = "";
+
+#ifdef DB_CACHE
+ if (yp_access(argp->map, argp->domain, (struct svc_req *)rqstp)) {
+#else
+ if (yp_access(argp->map, (struct svc_req *)rqstp)) {
+#endif
+ result.stat = YP_YPERR;
+ return(&result);
+ }
+
+ if (argp->domain == NULL) {
+ result.stat = YP_BADARGS;
+ return (&result);
+ }
+
+ if (yp_select_map(argp->map, argp->domain, &key, 1) != YP_TRUE) {
+ result.stat = yp_errno;
+ return(&result);
+ }
+
+ /*
+ * Note that we copy the data retrieved from the database to
+ * a private buffer and NUL terminate the buffer rather than
+ * terminating the data in place. We do this because by stuffing
+ * a '\0' into data.data, we will actually be corrupting memory
+ * allocated by the DB package. This is a bad thing now that we
+ * cache DB handles rather than closing the database immediately.
+ */
+ result.stat = yp_getbykey(&key, &val);
+ if (result.stat == YP_TRUE) {
+ bcopy(val.valdat_val, &ypvalbuf, val.valdat_len);
+ ypvalbuf[val.valdat_len] = '\0';
+ result.peer = ypvalbuf;
+ } else
+ result.peer = "";
+
+ return (&result);
+}
+
+ypresp_order *
+ypproc_order_2_svc(ypreq_nokey *argp, struct svc_req *rqstp)
+{
+ static ypresp_order result;
+ keydat key = { ORDER_SZ, ORDER_STRING };
+ valdat val;
+
+ result.ordernum = 0;
+
+#ifdef DB_CACHE
+ if (yp_access(argp->map, argp->domain, (struct svc_req *)rqstp)) {
+#else
+ if (yp_access(argp->map, (struct svc_req *)rqstp)) {
+#endif
+ result.stat = YP_YPERR;
+ return(&result);
+ }
+
+ if (argp->domain == NULL) {
+ result.stat = YP_BADARGS;
+ return (&result);
+ }
+
+ /*
+ * We could just check the timestamp on the map file,
+ * but that's a hack: we'll only know the last time the file
+ * was touched, not the last time the database contents were
+ * updated.
+ */
+
+ if (yp_select_map(argp->map, argp->domain, &key, 1) != YP_TRUE) {
+ result.stat = yp_errno;
+ return(&result);
+ }
+
+ result.stat = yp_getbykey(&key, &val);
+
+ if (result.stat == YP_TRUE)
+ result.ordernum = atoi(val.valdat_val);
+ else
+ result.ordernum = 0;
+
+ return (&result);
+}
+
+static void yp_maplist_free(struct ypmaplist *yp_maplist)
+{
+ register struct ypmaplist *next;
+
+ while (yp_maplist) {
+ next = yp_maplist->next;
+ free(yp_maplist->map);
+ free(yp_maplist);
+ yp_maplist = next;
+ }
+ return;
+}
+
+static struct ypmaplist *
+yp_maplist_create(const char *domain)
+{
+ char yp_mapdir[MAXPATHLEN + 2];
+ char yp_mapname[MAXPATHLEN + 2];
+ struct ypmaplist *cur = NULL;
+ struct ypmaplist *yp_maplist = NULL;
+ DIR *dird;
+ struct dirent *dirp;
+ struct stat statbuf;
+
+ snprintf(yp_mapdir, sizeof(yp_mapdir), "%s/%s", yp_dir, domain);
+
+ if ((dird = opendir(yp_mapdir)) == NULL) {
+ yp_error("opendir(%s) failed: %s", yp_mapdir, strerror(errno));
+ return(NULL);
+ }
+
+ while ((dirp = readdir(dird)) != NULL) {
+ if (strcmp(dirp->d_name, ".") && strcmp(dirp->d_name, "..")) {
+ snprintf(yp_mapname, sizeof(yp_mapname), "%s/%s",
+ yp_mapdir,dirp->d_name);
+ if (stat(yp_mapname, &statbuf) < 0 ||
+ !S_ISREG(statbuf.st_mode))
+ continue;
+ if ((cur = (struct ypmaplist *)
+ malloc(sizeof(struct ypmaplist))) == NULL) {
+ yp_error("malloc() failed");
+ closedir(dird);
+ yp_maplist_free(yp_maplist);
+ return(NULL);
+ }
+ if ((cur->map = strdup(dirp->d_name)) == NULL) {
+ yp_error("strdup() failed: %s",strerror(errno));
+ closedir(dird);
+ yp_maplist_free(yp_maplist);
+ return(NULL);
+ }
+ cur->next = yp_maplist;
+ yp_maplist = cur;
+ if (debug)
+ yp_error("map: %s", yp_maplist->map);
+ }
+
+ }
+ closedir(dird);
+ return(yp_maplist);
+}
+
+ypresp_maplist *
+ypproc_maplist_2_svc(domainname *argp, struct svc_req *rqstp)
+{
+ static ypresp_maplist result = { 0, NULL };
+
+#ifdef DB_CACHE
+ if (yp_access(NULL, NULL, (struct svc_req *)rqstp)) {
+#else
+ if (yp_access(NULL, (struct svc_req *)rqstp)) {
+#endif
+ result.stat = YP_YPERR;
+ return(&result);
+ }
+
+ if (argp == NULL) {
+ result.stat = YP_BADARGS;
+ return (&result);
+ }
+
+ if (yp_validdomain(*argp)) {
+ result.stat = YP_NODOM;
+ return (&result);
+ }
+
+ /*
+ * We have to construct a linked list for the ypproc_maplist
+ * procedure using dynamically allocated memory. Since the XDR
+ * layer won't free this list for us, we have to deal with it
+ * ourselves. We call yp_maplist_free() first to free any
+ * previously allocated data we may have accumulated to insure
+ * that we have only one linked list in memory at any given
+ * time.
+ */
+
+ yp_maplist_free(result.maps);
+
+ if ((result.maps = yp_maplist_create(*argp)) == NULL) {
+ yp_error("yp_maplist_create failed");
+ result.stat = YP_YPERR;
+ return(&result);
+ } else
+ result.stat = YP_TRUE;
+
+ return (&result);
+}
+
+/*
+ * NIS v1 support. The nullproc, domain and domain_nonack
+ * functions from v1 are identical to those in v2, so all
+ * we have to do is hand off to them.
+ *
+ * The other functions are mostly just wrappers around their v2
+ * counterparts. For example, for the v1 'match' procedure, we
+ * crack open the argument structure, make a request to the v2
+ * 'match' function, repackage the data into a v1 response and
+ * then send it on its way.
+ *
+ * Note that we don't support the pull, push and get procedures.
+ * There's little documentation available to show what they
+ * do, and I suspect they're meant largely for map transfers
+ * between master and slave servers.
+ */
+
+void *
+ypoldproc_null_1_svc(void *argp, struct svc_req *rqstp)
+{
+ return(ypproc_null_2_svc(argp, rqstp));
+}
+
+bool_t *
+ypoldproc_domain_1_svc(domainname *argp, struct svc_req *rqstp)
+{
+ return(ypproc_domain_2_svc(argp, rqstp));
+}
+
+bool_t *
+ypoldproc_domain_nonack_1_svc(domainname *argp, struct svc_req *rqstp)
+{
+ return (ypproc_domain_nonack_2_svc(argp, rqstp));
+}
+
+/*
+ * the 'match' procedure sends a response of type YPRESP_VAL
+ */
+ypresponse *
+ypoldproc_match_1_svc(yprequest *argp, struct svc_req *rqstp)
+{
+ static ypresponse result;
+ ypresp_val *v2_result;
+
+ result.yp_resptype = YPRESP_VAL;
+ result.ypresponse_u.yp_resp_valtype.val.valdat_val = "";
+ result.ypresponse_u.yp_resp_valtype.val.valdat_len = 0;
+
+ if (argp->yp_reqtype != YPREQ_KEY) {
+ result.ypresponse_u.yp_resp_valtype.stat = YP_BADARGS;
+ return(&result);
+ }
+
+ v2_result = ypproc_match_2_svc(&argp->yprequest_u.yp_req_keytype,rqstp);
+ if (v2_result == NULL)
+ return(NULL);
+
+ bcopy(v2_result, &result.ypresponse_u.yp_resp_valtype,
+ sizeof(ypresp_val));
+
+ return (&result);
+}
+
+/*
+ * the 'first' procedure sends a response of type YPRESP_KEY_VAL
+ */
+ypresponse *
+ypoldproc_first_1_svc(yprequest *argp, struct svc_req *rqstp)
+{
+ static ypresponse result;
+ ypresp_key_val *v2_result;
+
+ result.yp_resptype = YPRESP_KEY_VAL;
+ result.ypresponse_u.yp_resp_key_valtype.val.valdat_val =
+ result.ypresponse_u.yp_resp_key_valtype.key.keydat_val = "";
+ result.ypresponse_u.yp_resp_key_valtype.val.valdat_len =
+ result.ypresponse_u.yp_resp_key_valtype.key.keydat_len = 0;
+
+ if (argp->yp_reqtype != YPREQ_NOKEY) {
+ result.ypresponse_u.yp_resp_key_valtype.stat = YP_BADARGS;
+ return(&result);
+ }
+
+ v2_result = ypproc_first_2_svc(&argp->yprequest_u.yp_req_nokeytype,
+ rqstp);
+ if (v2_result == NULL)
+ return(NULL);
+
+ bcopy(v2_result, &result.ypresponse_u.yp_resp_key_valtype,
+ sizeof(ypresp_key_val));
+
+ return (&result);
+}
+
+/*
+ * the 'next' procedure sends a response of type YPRESP_KEY_VAL
+ */
+ypresponse *
+ypoldproc_next_1_svc(yprequest *argp, struct svc_req *rqstp)
+{
+ static ypresponse result;
+ ypresp_key_val *v2_result;
+
+ result.yp_resptype = YPRESP_KEY_VAL;
+ result.ypresponse_u.yp_resp_key_valtype.val.valdat_val =
+ result.ypresponse_u.yp_resp_key_valtype.key.keydat_val = "";
+ result.ypresponse_u.yp_resp_key_valtype.val.valdat_len =
+ result.ypresponse_u.yp_resp_key_valtype.key.keydat_len = 0;
+
+ if (argp->yp_reqtype != YPREQ_KEY) {
+ result.ypresponse_u.yp_resp_key_valtype.stat = YP_BADARGS;
+ return(&result);
+ }
+
+ v2_result = ypproc_next_2_svc(&argp->yprequest_u.yp_req_keytype,rqstp);
+ if (v2_result == NULL)
+ return(NULL);
+
+ bcopy(v2_result, &result.ypresponse_u.yp_resp_key_valtype,
+ sizeof(ypresp_key_val));
+
+ return (&result);
+}
+
+/*
+ * the 'poll' procedure sends a response of type YPRESP_MAP_PARMS
+ */
+ypresponse *
+ypoldproc_poll_1_svc(yprequest *argp, struct svc_req *rqstp)
+{
+ static ypresponse result;
+ ypresp_master *v2_result1;
+ ypresp_order *v2_result2;
+
+ result.yp_resptype = YPRESP_MAP_PARMS;
+ result.ypresponse_u.yp_resp_map_parmstype.domain =
+ argp->yprequest_u.yp_req_nokeytype.domain;
+ result.ypresponse_u.yp_resp_map_parmstype.map =
+ argp->yprequest_u.yp_req_nokeytype.map;
+ /*
+ * Hmm... there is no 'status' value in the
+ * yp_resp_map_parmstype structure, so I have to
+ * guess at what to do to indicate a failure.
+ * I hope this is right.
+ */
+ result.ypresponse_u.yp_resp_map_parmstype.ordernum = 0;
+ result.ypresponse_u.yp_resp_map_parmstype.peer = "";
+
+ if (argp->yp_reqtype != YPREQ_MAP_PARMS) {
+ return(&result);
+ }
+
+ v2_result1 = ypproc_master_2_svc(&argp->yprequest_u.yp_req_nokeytype,
+ rqstp);
+ if (v2_result1 == NULL)
+ return(NULL);
+
+ if (v2_result1->stat != YP_TRUE) {
+ return(&result);
+ }
+
+ v2_result2 = ypproc_order_2_svc(&argp->yprequest_u.yp_req_nokeytype,
+ rqstp);
+ if (v2_result2 == NULL)
+ return(NULL);
+
+ if (v2_result2->stat != YP_TRUE) {
+ return(&result);
+ }
+
+ result.ypresponse_u.yp_resp_map_parmstype.peer =
+ v2_result1->peer;
+ result.ypresponse_u.yp_resp_map_parmstype.ordernum =
+ v2_result2->ordernum;
+
+ return (&result);
+}
+
+ypresponse *
+ypoldproc_push_1_svc(yprequest *argp, struct svc_req *rqstp)
+{
+ static ypresponse result;
+
+ /*
+ * Not implemented.
+ */
+
+ return (&result);
+}
+
+ypresponse *
+ypoldproc_pull_1_svc(yprequest *argp, struct svc_req *rqstp)
+{
+ static ypresponse result;
+
+ /*
+ * Not implemented.
+ */
+
+ return (&result);
+}
+
+ypresponse *
+ypoldproc_get_1_svc(yprequest *argp, struct svc_req *rqstp)
+{
+ static ypresponse result;
+
+ /*
+ * Not implemented.
+ */
+
+ return (&result);
+}
diff --git a/usr.sbin/ypserv/yp_svc_udp.c b/usr.sbin/ypserv/yp_svc_udp.c
new file mode 100644
index 0000000..ea4fbcb
--- /dev/null
+++ b/usr.sbin/ypserv/yp_svc_udp.c
@@ -0,0 +1,73 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <rpc/rpc.h>
+#include <rpc/svc_dg.h>
+#include "yp_extern.h"
+
+#define su_data(xprt) ((struct svc_dg_data *)(xprt->xp_p2))
+
+/*
+ * We need to be able to manually set the transaction ID in the
+ * UDP transport handle, but the standard library offers us no way
+ * to do that. Hence we need this garbage.
+ */
+
+unsigned long
+svcudp_get_xid(SVCXPRT *xprt)
+{
+ struct svc_dg_data *su;
+
+ if (xprt == NULL)
+ return(0);
+ su = su_data(xprt);
+ return(su->su_xid);
+}
+
+unsigned long
+svcudp_set_xid(SVCXPRT *xprt, unsigned long xid)
+{
+ struct svc_dg_data *su;
+ unsigned long old_xid;
+
+ if (xprt == NULL)
+ return(0);
+ su = su_data(xprt);
+ old_xid = su->su_xid;
+ su->su_xid = xid;
+ return(old_xid);
+}
diff --git a/usr.sbin/ypserv/ypinit.8 b/usr.sbin/ypserv/ypinit.8
new file mode 100644
index 0000000..6d25e42
--- /dev/null
+++ b/usr.sbin/ypserv/ypinit.8
@@ -0,0 +1,188 @@
+.\" Copyright (c) 1997
+.\" Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Bill Paul.
+.\" 4. Neither the name of the author nor the names of any co-contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+.\" THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd November 10, 1997
+.Dt YPINIT 8
+.Os
+.Sh NAME
+.Nm ypinit
+.Nd build and install NIS databases
+.Sh SYNOPSIS
+.Nm
+.Fl m
+.Op Ar domainname
+.Nm
+.Fl s
+.Ar master_server
+.Op Ar domainname
+.Nm
+.Fl u
+.Op Ar domainname
+.Sh DESCRIPTION
+The
+.Nm
+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.
+.El
+.Sh FILES
+.Bl -tag -width /var/yp/master.passwd -compact
+.It Pa /etc/bootparams
+Bootparams source file
+.It Pa /etc/ethers
+Ethers data source file
+.It Pa /etc/group
+Group source file
+.It Pa /etc/hosts
+Hostname/IP address source file
+.It Pa /etc/netid
+RPC netid source file
+.It Pa /etc/networks
+Networks source file
+.It Pa /etc/protocols
+Protocols source file
+.It Pa /etc/publickey
+RPC public key/secret key source file
+.It Pa /etc/services
+Services data source file
+.It Pa /var/yp/master.passwd
+Passwd database source file
+.It Pa /var/yp/netgroup
+Netgroup data source file
+.It Pa /var/yp/ypservers
+Ypservers source file (generated by
+.Nm )
+.El
+.Sh SEE ALSO
+.Xr mknetid 8 ,
+.Xr revnetgroup 8 ,
+.Xr yp 8 ,
+.Xr yppush 8 ,
+.Xr ypserv 8 ,
+.Xr ypxfr 8 ,
+.Xr yp_mkdb 8
+.Sh HISTORY
+This version of
+.Nm
+is based on the
+.Nm
+script in
+.Ox .
+It first appeared in
+.Fx 3.0 .
+.Sh AUTHORS
+.An -nosplit
+The original script was written by
+.An Mats O Jansson Aq moj@stacken.kth.se .
+It was modified for
+.Fx
+by
+.An Bill Paul Aq wpaul@ctr.columbia.edu .
diff --git a/usr.sbin/ypserv/ypinit.sh b/usr.sbin/ypserv/ypinit.sh
new file mode 100644
index 0000000..8d1871b
--- /dev/null
+++ b/usr.sbin/ypserv/ypinit.sh
@@ -0,0 +1,390 @@
+#!/bin/sh
+# $FreeBSD$
+#
+# 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..d8c0990
--- /dev/null
+++ b/usr.sbin/ypserv/ypserv.8
@@ -0,0 +1,447 @@
+.\" Copyright (c) 1995
+.\" Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Bill Paul.
+.\" 4. Neither the name of the author nor the names of any co-contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd February 4, 1995
+.Dt YPSERV 8
+.Os
+.Sh NAME
+.Nm ypserv
+.Nd NIS database server
+.Sh SYNOPSIS
+.Nm
+.Op Fl n
+.Op Fl d
+.Op Fl p Ar path
+.Sh DESCRIPTION
+.Tn NIS
+is an RPC-based service designed to allow a number of UNIX-based
+machines to share a common set of configuration files.
+Rather than
+requiring a system administrator to update several copies of files
+such as
+.Pa /etc/hosts ,
+.Pa /etc/passwd
+and
+.Pa /etc/group ,
+which tend to require frequent changes in most environments,
+.Tn NIS
+allows groups of computers to share one set of data which can be
+updated from a single location.
+.Pp
+The
+.Nm
+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
+.Fx ,
+the maps are always readable and writable only by root for security
+reasons.
+Technically this is only necessary for the password
+maps, but since the data in the other maps can be found in
+other world-readable files anyway, it doesn't hurt and it's considered
+good general practice.
+.Pp
+The
+.Nm
+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 a
+.Fx
+password
+database via
+.Tn NIS Ns :
+.Fx
+normally only stores encrypted passwords
+in
+.Pa /etc/master.passwd ,
+which is readable and writable only by root.
+By turning this file
+into an
+.Tn NIS
+map, this security feature would be completely defeated.
+.Pp
+To make up for this, the
+.Fx
+version of
+.Nm
+handles the
+.Pa master.passwd.byname
+and
+.Pa master.passwd.byuid
+maps in a special way.
+When the server receives a request to access
+either of these two maps, it will check the TCP port from which the
+request originated and return an error if the port number is greater
+than 1023.
+Since only the superuser is allowed to bind to TCP ports
+with values less than 1024, the server can use this test to determine
+whether or not the access request came from a privileged user.
+Any requests made by non-privileged users are therefore rejected.
+.Pp
+Furthermore, the
+.Xr getpwent 3
+routines in the
+.Fx
+standard C library will only attempt to retrieve
+data from the
+.Pa master.passwd.byname
+and
+.Pa master.passwd.byuid
+maps for the superuser: if a normal user calls any of these functions,
+the standard
+.Pa passwd.byname
+and
+.Pa passwd.byuid
+maps will be accessed instead.
+The latter two maps are constructed by
+.Pa /var/yp/Makefile
+by parsing the
+.Pa master.passwd
+file and stripping out the password fields, and are therefore
+safe to pass on to unprivileged users.
+In this way, the shadow password
+aspect of the protected
+.Pa master.passwd
+database is maintained through
+.Tn NIS .
+.Sh NOTES
+.Ss Setting Up Master and Slave Servers
+.Xr ypinit 8
+is a convenient script that will help setup master and slave
+.Tn NIS
+servers.
+.Ss Limitations
+There are two problems inherent with password shadowing in
+.Tn NIS
+that users should
+be aware of:
+.Bl -enum -offset indent
+.It
+The
+.Sq TCP port less than 1024
+test is trivial to defeat for users with
+unrestricted access to machines on your network (even those machines
+which do not run UNIX-based operating systems).
+.It
+If you plan to use a
+.Fx
+system to serve
+.No non- Ns Fx
+clients that
+have no support for password shadowing (which is most of them), you
+will have to disable the password shadowing entirely by uncommenting the
+.Em UNSECURE=True
+entry in
+.Pa /var/yp/Makefile .
+This will cause the standard
+.Pa passwd.byname
+and
+.Pa passwd.byuid
+maps to be generated with valid encrypted password fields, which is
+necessary in order for
+.No non- Ns Fx
+clients to perform user
+authentication through
+.Tn NIS .
+.El
+.Pp
+.Ss Security
+In general, any remote user can issue an RPC to
+.Nm
+and retrieve the contents of your
+.Tn NIS
+maps, provided the remote user
+knows your domain name.
+To prevent such unauthorized transactions,
+.Nm
+supports a feature called
+.Pa securenets
+which can be used to restrict access to a given set of hosts.
+At startup,
+.Nm
+will attempt to load the securenets information from a file
+called
+.Pa /var/yp/securenets .
+(Note that this path varies depending on the path specified with
+the
+.Fl p
+option, which is explained below.)
+This file contains entries
+that consist of a network specification and a network mask separated
+by white space.
+Lines starting with
+.Dq \&#
+are considered to be comments.
+A
+sample securenets file might look like this:
+.Bd -unfilled -offset indent
+# allow connections from local host -- mandatory
+127.0.0.1 255.255.255.255
+# allow connections from any host
+# on the 192.168.128.0 network
+192.168.128.0 255.255.255.0
+# allow connections from any host
+# between 10.0.0.0 to 10.0.15.255
+10.0.0.0 255.255.240.0
+.Ed
+.Pp
+If
+.Nm
+receives a request from an address that matches one of these rules,
+it will process the request normally.
+If the address fails to match
+a rule, the request will be ignored and a warning message will be
+logged.
+If the
+.Pa /var/yp/securenets
+file does not exist,
+.Nm
+will allow connections from any host.
+.Pp
+The
+.Nm
+program also has support for Wietse Venema's
+.Em tcpwrapper
+package.
+This allows the administrator to use the tcpwrapper
+configuration files
+.Pa ( /etc/hosts.allow
+and
+.Pa /etc/hosts.deny )
+for access control instead of
+.Pa /var/yp/securenets .
+.Pp
+Note: while both of these access control mechanisms provide some
+security, they, like the privileged port test, are both vulnerable
+to
+.Dq IP spoofing
+attacks.
+.Pp
+.Ss NIS v1 compatibility
+This version of
+.Nm
+has some support for serving
+.Tn NIS
+v1 clients.
+The
+.Fx
+.Tn NIS
+implementation only uses the
+.Tn NIS
+v2 protocol, however other implementations
+include support for the v1 protocol for backwards compatibility
+with older systems.
+The
+.Xr ypbind 8
+daemons supplied with these systems will try to establish a binding
+to an
+.Tn NIS
+v1 server even though they may never actually need it (and they may
+persist in broadcasting in search of one even after they receive a
+response from a v2 server). Note that while
+support for normal client calls is provided, this version of
+.Nm
+does not handle v1 map transfer requests; consequently, it cannot
+be used as a master or slave in conjunction with older
+.Tn NIS
+servers that
+only support the v1 protocol.
+Fortunately, there probably aren't any
+such servers still in use today.
+.Ss NIS servers that are also NIS clients
+Care must be taken when running
+.Nm
+in a multi-server domain where the server machines are also
+.Tn NIS
+clients.
+It is generally a good idea to force the servers to
+bind to themselves rather than allowing them to broadcast bind
+requests and possibly become bound to each other: strange failure
+modes can result if one server goes down and
+others are dependent upon on it.
+(Eventually all the clients will
+time out and attempt to bind to other servers, but the delay
+involved can be considerable and the failure mode is still present
+since the servers might bind to each other all over again).
+.Pp
+Refer to the
+.Xr ypbind 8
+man page for details on how to force it to bind to a particular
+server.
+.Sh OPTIONS
+The following options are supported by
+.Nm :
+.Bl -tag -width flag
+.It Fl n
+This option affects the way
+.Nm
+handles yp_match requests for the
+.Pa hosts.byname
+and
+.Pa hosts.byaddress
+maps.
+By default, if
+.Nm
+can't find an entry for a given host in its hosts maps, it will
+return an error and perform no further processing.
+With the
+.Fl n
+flag,
+.Nm
+will go one step further: rather than giving up immediately, it
+will try to resolve the hostname or address using a DNS nameserver
+query.
+If the query is successful,
+.Nm
+will construct a fake database record and return it to the client,
+thereby making it seem as though the client's yp_match request
+succeeded.
+.Pp
+This feature is provided for 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.
+The
+.Fx
+resolver can be configured to do DNS
+queries directly, therefore it is not necessary to enable this
+option when serving only
+.Fx
+.Tn NIS
+clients.
+.It Fl d
+Cause the server to run in debugging mode.
+Normally,
+.Nm
+reports only unusual errors (access violations, file access failures)
+using the
+.Xr syslog 3
+facility.
+In debug mode, the server does not background
+itself and prints extra status messages to stderr for each
+request that it receives.
+Also, while running in debug mode,
+.Nm
+will not spawn any additional subprocesses as it normally does
+when handling yp_all requests or doing DNS lookups.
+(These actions
+often take a fair amount of time to complete and are therefore handled
+in subprocesses, allowing the parent server process to go on handling
+other requests.)
+This makes it easier to trace the server with
+a debugging tool.
+.It Fl p Ar path
+Normally,
+.Nm
+assumes that all
+.Tn NIS
+maps are stored under
+.Pa /var/yp .
+The
+.Fl p
+flag may be used to specify an alternate
+.Tn NIS
+root path, allowing
+the system administrator to move the map files to a different place
+within the filesystem.
+.El
+.Sh FILES
+.Bl -tag -width Pa -compact
+.It Pa /var/yp/[domainname]/[maps]
+the
+.Tn NIS
+maps
+.It Pa /etc/nsswitch.conf
+name switch configuration file
+.It Pa /var/yp/securenets
+host access control file
+.El
+.Sh SEE ALSO
+.Xr ypcat 1 ,
+.Xr db 3 ,
+.Xr hosts_access 5 ,
+.Xr rpc.yppasswdd 8 ,
+.Xr yp 8 ,
+.Xr ypbind 8 ,
+.Xr ypinit 8 ,
+.Xr yppush 8 ,
+.Xr ypxfr 8
+.Sh AUTHORS
+.An Bill Paul Aq wpaul@ctr.columbia.edu
+.Sh HISTORY
+This version of
+.Nm
+first appeared in
+.Fx 2.2 .
diff --git a/usr.sbin/ypset/Makefile b/usr.sbin/ypset/Makefile
new file mode 100644
index 0000000..10ed5f8
--- /dev/null
+++ b/usr.sbin/ypset/Makefile
@@ -0,0 +1,7 @@
+# from: @(#)Makefile 5.8 (Berkeley) 7/28/90
+# $FreeBSD$
+
+PROG= ypset
+MAN= ypset.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ypset/ypset.8 b/usr.sbin/ypset/ypset.8
new file mode 100644
index 0000000..d15b60d
--- /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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd October 25, 1994
+.Dt YPSET 8
+.Os
+.Sh NAME
+.Nm ypset
+.Nd tell
+.Xr ypbind 8
+which YP server process to use
+.Sh SYNOPSIS
+.Nm
+.Op Fl h Ar host
+.Op Fl d Ar domain
+.Ar server
+.Sh DESCRIPTION
+.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 8 ,
+.Xr ypbind 8
+.Sh AUTHORS
+.An Theo De Raadt
diff --git a/usr.sbin/ypset/ypset.c b/usr.sbin/ypset/ypset.c
new file mode 100644
index 0000000..730a6ed
--- /dev/null
+++ b/usr.sbin/ypset/ypset.c
@@ -0,0 +1,152 @@
+/*
+ * 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[] =
+ "$FreeBSD$";
+#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(void)
+{
+ fprintf(stderr, "usage: ypset [-h host] [-d domain] server\n");
+ exit(1);
+}
+
+int
+bind_tohost(struct sockaddr_in *sin, char *dom, char *server)
+{
+ struct ypbind_setdom ypsd;
+ struct timeval tv;
+ struct hostent *hp;
+ CLIENT *client;
+ int sock, port;
+ int r;
+ unsigned long server_addr;
+
+ if ((port = htons(getrpcport(server, YPPROG, YPPROC_NULL, IPPROTO_UDP))) == 0)
+ errx(1, "%s not running ypserv", server);
+
+ bzero(&ypsd, sizeof ypsd);
+
+ if ((hp = gethostbyname (server)) != NULL) {
+ /* is this the most compatible way?? */
+ bcopy (hp->h_addr_list[0],
+ (u_long *)&ypsd.ypsetdom_binding.ypbind_binding_addr,
+ sizeof (unsigned long));
+ } else if ((long)(server_addr = inet_addr (server)) == -1) {
+ errx(1, "can't find address for %s", server);
+ } else
+ bcopy (&server_addr,
+ (u_long *)&ypsd.ypsetdom_binding.ypbind_binding_addr,
+ sizeof (server_addr));
+
+/* strncpy(ypsd.ypsetdom_domain, dom, sizeof ypsd.ypsetdom_domain); */
+ ypsd.ypsetdom_domain = dom;
+ *(u_long *)&ypsd.ypsetdom_binding.ypbind_binding_port = port;
+ ypsd.ypsetdom_vers = YPVERS;
+
+ tv.tv_sec = 15;
+ tv.tv_usec = 0;
+ sock = RPC_ANYSOCK;
+ client = clntudp_create(sin, YPBINDPROG, YPBINDVERS, tv, &sock);
+ if (client == NULL) {
+ warnx("can't yp_bind, reason: %s", yperr_string(YPERR_YPBIND));
+ return (YPERR_YPBIND);
+ }
+ client->cl_auth = authunix_create_default();
+
+ r = clnt_call(client, YPBINDPROC_SETDOM,
+ (xdrproc_t)xdr_ypbind_setdom, &ypsd,
+ (xdrproc_t)xdr_void, NULL, tv);
+ if (r) {
+ warnx("sorry, cannot ypset for domain %s on host", dom);
+ clnt_destroy(client);
+ return (YPERR_YPBIND);
+ }
+ clnt_destroy(client);
+ return (0);
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct sockaddr_in sin;
+ struct hostent *hent;
+ char *domainname;
+ int c;
+
+ yp_get_default_domain(&domainname);
+
+ bzero(&sin, sizeof sin);
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ while ((c = getopt(argc, argv, "h:d:")) != -1)
+ switch (c) {
+ case 'd':
+ domainname = optarg;
+ break;
+ case 'h':
+ if ((sin.sin_addr.s_addr = inet_addr(optarg)) == -1) {
+ hent = gethostbyname(optarg);
+ if (hent == NULL)
+ errx(1, "host %s unknown", optarg);
+ bcopy(&hent->h_addr_list[0], &sin.sin_addr,
+ sizeof sin.sin_addr);
+ }
+ break;
+ default:
+ usage();
+ }
+
+ if (optind + 1 != argc)
+ usage();
+
+ if (bind_tohost(&sin, domainname, argv[optind]))
+ exit(1);
+ exit(0);
+}
diff --git a/usr.sbin/zic/Arts.htm b/usr.sbin/zic/Arts.htm
new file mode 100644
index 0000000..baa84ed
--- /dev/null
+++ b/usr.sbin/zic/Arts.htm
@@ -0,0 +1,178 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
+<!-- $FreeBSD$ -->
+<HTML>
+<HEAD>
+<TITLE>Time and the Arts</TITLE>
+</HEAD>
+<BODY>
+<H1>Time and the Arts</H1>
+<P>
+<H6>
+@(#)Arts.htm 7.18
+</H6>
+</P>
+<PRE>
+Data on recordings of "Save That Time," Russ Long, Serrob Publishing, BMI:
+--------------------------------------------------------------------------
+Artist: Karrin Allyson
+CD: I Didn't Know About You
+Copyright Date: 1993
+Label: Concord Jazz, Inc.
+ID: CCD-4543
+Track Time: 3:44
+Personnel: Karrin Allyson, vocal
+ Russ Long, piano
+ Gerald Spaits, bass
+ Todd Strait, drums
+Notes: CD notes "additional lyric by Karrin Allyson;
+ arranged by Russ Long and Karrin Allyson"
+ADO Rating: 1 star
+<A HREF="http://205.186.189.2/cgi-win/amg.exe?sql=1A_IDR|||175928">AMG Rating: 3.5 stars</A>
+Penguin Rating: 3.5 stars
+--------------------------------------------------------------------------
+Artist: Kevin Mahogany
+CD: Double Rainbow
+Copyright Date: 1993
+Label: Enja Records
+ID: ENJ-7097 2
+Track Time: 6:27
+Personnel: Kevin Mahogany, vocal
+ Kenny Barron, piano
+ Ray Drummond, bss
+ Ralph Moore, tenor saxophone
+ Lewis Nash, drums
+ADO Rating: 1.5 stars
+<A HREF="http://205.186.189.2/cgi-win/amg.exe?sql=1A_IDR|||262654">AMG Rating: unrated</A>
+Penguin Rating: 3 stars
+--------------------------------------------------------------------------
+Artist: Joe Williams
+CD: Here's to Life
+Copyright Date: 1994
+Label: Telarc International Corporation
+ID: CD-83357
+Track Time: 3:58
+Personnel: Joe Williams, vocal
+ The Robert Farnon [39 piece] Orchestra
+Notes: On-line information and samples available at
+ <A HREF="http://www.telarc.com/telarc/releases/release.req?ID=83357">http://telarc.dmn.com/telarc/releases/release.req?ID=83357</A>
+ADO Rating: black dot
+<A HREF="http://205.186.189.2/cgi-win/amg.exe?sql=1A_IDR|||194434">AMG Rating: 2 stars</A>
+Penguin Rating: 3 stars
+--------------------------------------------------------------------------
+Artist: Charles Fambrough
+CD: Keeper of the Spirit
+Copyright Date: 1995
+Label: AudioQuest Music
+ID: AQ-CD1033
+Track Time: 7:07
+Personnel: Charles Fambrough, bass
+ Joel Levine, tenor recorder
+ Edward Simon, piano
+ Lenny White, drums
+ Marion Simon, percussion
+Notes: On-line information and samples available at
+ <A HREF="http://wwmusic.com/~music/audioq/rel/1033.html">http://wwmusic.com/~music/audioq/rel/1033.html</A>
+ADO Rating: 2 stars
+<A HREF="http://205.186.189.2/cgi-win/AMG.exe?sql=1A_IDR|||224430">AMG Rating: unrated</A>
+Penguin Rating: 3 stars
+==========================================================================
+Also of note:
+--------------------------------------------------------------------------
+Artist: Holly Cole Trio
+CD: Blame It On My Youth
+Copyright Date: 1992
+Label: Manhattan
+ID: CDP 7 97349 2
+Total Time: 37:45
+Personnel: Holly Cole, voice
+ Aaron Davis, piano
+ David Piltch, string bass
+Notes: Lyrical reference to "Eastern Standard Time" in
+ Tom Waits' "Purple Avenue"
+ADO Rating: 2.5 stars
+<A HREF="http://205.186.189.2/cgi-win/AMG.exe?sql=1A_IDR|||157959">AMG Rating: 2 stars</A>
+Penguin Rating: unrated
+--------------------------------------------------------------------------
+Artist: Milt Hinton
+CD: Old Man Time
+Copyright Date: 1990
+Label: Chiaroscuro
+ID: CR(D) 310
+Total Time: 149:38 (two CDs)
+Personnel: Milt Hinton, bass
+ Doc Cheatham, Dizzy Gillespie, Clark Terry, trumpet
+ Al Grey, trombone
+ Eddie Barefield, Joe Camel (Flip Phillips), Buddy Tate,
+ clarinet and saxophone
+ John Bunch, Red Richards, Norman Simmons, Derek Smith,
+ Ralph Sutton, piano
+ Danny Barker, Al Casey, guitar
+ Gus Johnson, Gerryck King, Bob Rosengarden, Jackie Williams,
+ drums
+ Lionel Hampton, vibraphone
+ Cab Calloway, Joe Williams, vocal
+ Buck Clayton, arrangements
+Notes: tunes include Old Man Time, Time After Time,
+ Sometimes I'm Happy,
+ A Hot Time in the Old Town Tonight,
+ Four or Five Times, Now's the Time,
+ Time on My Hands, This Time It's Us,
+ and Good Time Charlie
+ On-line samples available at
+ <A HREF="http://www.globalmusic.com/labels/chiaroscuro/chiaro_cd_gallery.html">http://www.globalmusic.com/labels/chiaroscuro/chiaro_cd_gallery.html</A>
+ADO Rating: 3 stars
+<A HREF="http://205.186.189.2/cgi-win/AMG.exe?sql=1A_IDR|||162344">AMG Rating: 4.5 stars</A>
+Penguin Rating: 3 stars
+--------------------------------------------------------------------------
+Artist: Paul Broadbent
+CD: Pacific Standard Time
+Copyright Date: 1995
+Label: Concord Jazz, Inc.
+ID: CCD-4664
+Total Time: 62:42
+Personnel: Paul Broadbent, piano
+ Putter Smith, Bass
+ Frank Gibson, Jr., drums
+Notes: The CD cover features an analemma for equation of time fans
+ADO Rating: 1 star
+<A HREF="http://205.186.189.2/cgi-win/AMG.exe?sql=1A_IDR|||223722">AMG Rating: 3 stars</A>
+Penguin Rating: 3.5 stars
+--------------------------------------------------------------------------
+Artist: Anthony Braxton/Richard Teitelbaum
+CD: Silence/Time Zones
+Copyright Date: 1996
+Label: Black Lion
+ID: BLCD 760221
+Total Time: 72:58
+Personnel: Anthony Braxton, sopranino and alto saxophones,
+ contrebasse clarinet, miscellaneous instruments
+ Leo Smith, trumpet and miscellaneous instruments
+ Leroy Jenkins, violin and miscellaneous instruments
+ Richard Teitelbaum, modular moog and micromoog synthesizer
+ADO Rating: black dot
+<A HREF="http://205.186.189.2/cg/AMG_.exe?sql=A310757">AMG Rating: unrated</A>
+--------------------------------------------------------------------------
+Artist: Jules Verne
+Book: Le Tour du Monde en Quatre-Vingts Jours
+ (Around the World in Eighty Days)
+Notes: Wall-clock time plays a central role in the plot.
+ European readers of the 1870s clearly held the U.S. press in
+ deep contempt; the protagonists cross the U.S. without once
+ reading a paper.
+ An on-line French-language version of the book
+ "with illustrations from the original 1873 French-language edition"
+ is available at
+ <A HREF="http://fourmilab.ch/etexts/www/tdm80j">http://fourmilab.ch/etexts/www/tdm80j</A>
+ An on-line English-language translation of the book is available at
+ <A HREF="http://www.literature.org/Works/Jules-Verne/eighty">http://www.literature.org/Works/Jules-Verne/eighty</A>
+--------------------------------------------------------------------------
+Film: Bell Science - About Time
+Notes: The Frank Baxter/Richard Deacon extravaganza
+ Information on ordering is available at
+ <A HREF="http://www.videoflicks.com/VF/38/038332.htm">http://www.videoflicks.com/VF/38/038332.htm</A>
+--------------------------------------------------------------------------
+The syndicated comic strip "Dilbert" featured an all-too-rare example of
+time zone humor on 1998-03-14.
+</PRE>
+</BODY>
+</HTML>
diff --git a/usr.sbin/zic/Makefile b/usr.sbin/zic/Makefile
new file mode 100644
index 0000000..2496e82
--- /dev/null
+++ b/usr.sbin/zic/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+# Vendor contact: tz@elsie.nci.nih.gov
+MAINTAINER= wollman@FreeBSD.org
+
+SUBDIR= zic zdump
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/zic/Makefile.inc b/usr.sbin/zic/Makefile.inc
new file mode 100644
index 0000000..b915adf
--- /dev/null
+++ b/usr.sbin/zic/Makefile.inc
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+.include "${.CURDIR}/../../Makefile.inc"
diff --git a/usr.sbin/zic/README b/usr.sbin/zic/README
new file mode 100644
index 0000000..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..a069032
--- /dev/null
+++ b/usr.sbin/zic/ialloc.c
@@ -0,0 +1,86 @@
+#ifndef lint
+#ifndef NOID
+static const char elsieid[] = "@(#)ialloc.c 8.29";
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/*LINTLIBRARY*/
+
+#include "private.h"
+
+#define nonzero(n) (((n) == 0) ? 1 : (n))
+
+char *
+imalloc(n)
+const int n;
+{
+ return malloc((size_t) nonzero(n));
+}
+
+char *
+icalloc(nelem, elsize)
+int nelem;
+int elsize;
+{
+ if (nelem == 0 || elsize == 0)
+ nelem = elsize = 1;
+ return calloc((size_t) nelem, (size_t) elsize);
+}
+
+void *
+irealloc(pointer, size)
+void * const pointer;
+const int size;
+{
+ if (pointer == NULL)
+ return imalloc(size);
+ return realloc((void *) pointer, (size_t) nonzero(size));
+}
+
+char *
+icatalloc(old, new)
+char * const old;
+const char * const new;
+{
+ register char * result;
+ register int oldsize, newsize;
+
+ newsize = (new == NULL) ? 0 : strlen(new);
+ if (old == NULL)
+ oldsize = 0;
+ else if (newsize == 0)
+ return old;
+ else oldsize = strlen(old);
+ if ((result = irealloc(old, oldsize + newsize + 1)) != NULL)
+ if (new != NULL)
+ (void) strcpy(result + oldsize, new);
+ return result;
+}
+
+char *
+icpyalloc(string)
+const char * const string;
+{
+ return icatalloc((char *) NULL, string);
+}
+
+void
+ifree(p)
+char * const p;
+{
+ if (p != NULL)
+ (void) free(p);
+}
+
+void
+icfree(p)
+char * const p;
+{
+ if (p != NULL)
+ (void) free(p);
+}
diff --git a/usr.sbin/zic/private.h b/usr.sbin/zic/private.h
new file mode 100644
index 0000000..585d6e6
--- /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.
+ *
+ * $FreeBSD$
+ */
+
+/*
+** This header is for use ONLY with the time conversion code.
+** There is no guarantee that it will remain unchanged,
+** or that it will remain at all.
+** Do NOT copy it to any system include directory.
+** Thank you!
+*/
+
+/*
+** ID
+*/
+
+#ifndef lint
+#ifndef NOID
+static const char privatehid[] = "@(#)private.h 7.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..692bf1a
--- /dev/null
+++ b/usr.sbin/zic/scheck.c
@@ -0,0 +1,64 @@
+#ifndef lint
+#ifndef NOID
+static const char elsieid[] = "@(#)scheck.c 8.15";
+#endif /* !defined lint */
+#endif /* !defined NOID */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/*LINTLIBRARY*/
+
+#include "private.h"
+
+char *
+scheck(string, format)
+const char * const string;
+const char * const format;
+{
+ register char * fbuf;
+ register const char * fp;
+ register char * tp;
+ register int c;
+ register char * result;
+ char dummy;
+ static char nada;
+
+ result = &nada;
+ if (string == NULL || format == NULL)
+ return result;
+ fbuf = imalloc((int) (2 * strlen(format) + 4));
+ if (fbuf == NULL)
+ return result;
+ fp = format;
+ tp = fbuf;
+ while ((*tp++ = c = *fp++) != '\0') {
+ if (c != '%')
+ continue;
+ if (*fp == '%') {
+ *tp++ = *fp++;
+ continue;
+ }
+ *tp++ = '*';
+ if (*fp == '*')
+ ++fp;
+ while (is_digit(*fp))
+ *tp++ = *fp++;
+ if (*fp == 'l' || *fp == 'h')
+ *tp++ = *fp++;
+ else if (*fp == '[')
+ do *tp++ = *fp++;
+ while (*fp != '\0' && *fp != ']');
+ if ((*tp++ = *fp++) == '\0')
+ break;
+ }
+ *(tp - 1) = '%';
+ *tp++ = 'c';
+ *tp = '\0';
+ if (sscanf(string, fbuf, &dummy) != 1)
+ result = (char *) format;
+ ifree(fbuf);
+ return result;
+}
diff --git a/usr.sbin/zic/zdump.8 b/usr.sbin/zic/zdump.8
new file mode 100644
index 0000000..eb4d954
--- /dev/null
+++ b/usr.sbin/zic/zdump.8
@@ -0,0 +1,45 @@
+.\"
+.\" @(#)zdump.8 7.3
+.\" $FreeBSD$
+.\"
+.Dd September 13, 1994
+.Dt ZDUMP 8
+.Os
+.Sh NAME
+.Nm zdump
+.Nd timezone dumper
+.Sh SYNOPSIS
+.Nm
+.Op Fl v
+.Op Fl c Ar cutoffyear
+.Op Ar zonename ...
+.Sh DESCRIPTION
+.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.
+.El
+.Sh "SEE ALSO"
+.Xr ctime 3 ,
+.Xr tzfile 5 ,
+.Xr zic 8
diff --git a/usr.sbin/zic/zdump.c b/usr.sbin/zic/zdump.c
new file mode 100644
index 0000000..cfd94ea
--- /dev/null
+++ b/usr.sbin/zic/zdump.c
@@ -0,0 +1,374 @@
+#ifndef lint
+#ifndef NOID
+static const char elsieid[] = "@(#)zdump.c 7.28";
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/*
+** This code has been made independent of the rest of the time
+** conversion package to increase confidence in the verification it provides.
+** You can use this code to help in verifying other implementations.
+*/
+
+#include <err.h>
+#include <stdio.h> /* for stdout, stderr */
+#include <stdlib.h> /* for exit, malloc, atoi */
+#include <string.h> /* for strcpy */
+#include <sys/types.h> /* for time_t */
+#include <time.h> /* for struct tm */
+#include <unistd.h>
+
+#ifndef MAX_STRING_LENGTH
+#define MAX_STRING_LENGTH 1024
+#endif /* !defined MAX_STRING_LENGTH */
+
+#ifndef TRUE
+#define TRUE 1
+#endif /* !defined TRUE */
+
+#ifndef FALSE
+#define FALSE 0
+#endif /* !defined FALSE */
+
+#ifndef EXIT_SUCCESS
+#define EXIT_SUCCESS 0
+#endif /* !defined EXIT_SUCCESS */
+
+#ifndef EXIT_FAILURE
+#define EXIT_FAILURE 1
+#endif /* !defined EXIT_FAILURE */
+
+#ifndef SECSPERMIN
+#define SECSPERMIN 60
+#endif /* !defined SECSPERMIN */
+
+#ifndef MINSPERHOUR
+#define MINSPERHOUR 60
+#endif /* !defined MINSPERHOUR */
+
+#ifndef SECSPERHOUR
+#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
+#endif /* !defined SECSPERHOUR */
+
+#ifndef HOURSPERDAY
+#define HOURSPERDAY 24
+#endif /* !defined HOURSPERDAY */
+
+#ifndef EPOCH_YEAR
+#define EPOCH_YEAR 1970
+#endif /* !defined EPOCH_YEAR */
+
+#ifndef TM_YEAR_BASE
+#define TM_YEAR_BASE 1900
+#endif /* !defined TM_YEAR_BASE */
+
+#ifndef DAYSPERNYEAR
+#define DAYSPERNYEAR 365
+#endif /* !defined DAYSPERNYEAR */
+
+#ifndef isleap
+#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0)
+#endif /* !defined isleap */
+
+#if HAVE_GETTEXT - 0
+#include "locale.h" /* for setlocale */
+#include "libintl.h"
+#endif /* HAVE_GETTEXT - 0 */
+
+#ifndef GNUC_or_lint
+#ifdef lint
+#define GNUC_or_lint
+#endif /* defined lint */
+#ifndef lint
+#ifdef __GNUC__
+#define GNUC_or_lint
+#endif /* defined __GNUC__ */
+#endif /* !defined lint */
+#endif /* !defined GNUC_or_lint */
+
+#ifndef INITIALIZE
+#ifdef GNUC_or_lint
+#define INITIALIZE(x) ((x) = 0)
+#endif /* defined GNUC_or_lint */
+#ifndef GNUC_or_lint
+#define INITIALIZE(x)
+#endif /* !defined GNUC_or_lint */
+#endif /* !defined INITIALIZE */
+
+/*
+** For the benefit of GNU folk...
+** `_(MSGID)' uses the current locale's message library string for MSGID.
+** The default is to use gettext if available, and use MSGID otherwise.
+*/
+
+#ifndef _
+#if HAVE_GETTEXT - 0
+#define _(msgid) gettext(msgid)
+#else /* !(HAVE_GETTEXT - 0) */
+#define _(msgid) msgid
+#endif /* !(HAVE_GETTEXT - 0) */
+#endif /* !defined _ */
+
+#ifndef TZ_DOMAIN
+#define TZ_DOMAIN "tz"
+#endif /* !defined TZ_DOMAIN */
+
+#ifndef P
+#ifdef __STDC__
+#define P(x) x
+#endif /* defined __STDC__ */
+#ifndef __STDC__
+#define P(x) ()
+#endif /* !defined __STDC__ */
+#endif /* !defined P */
+
+extern char ** environ;
+extern char * tzname[2];
+
+static char * abbr P((struct tm * tmp));
+static long delta P((struct tm * newp, struct tm * oldp));
+static time_t hunt P((char * name, time_t lot, time_t hit));
+static size_t longest;
+static void show P((char * zone, time_t t, int v));
+static void usage(void);
+
+int
+main(argc, argv)
+int argc;
+char * argv[];
+{
+ register int i;
+ register int c;
+ register int vflag;
+ register char * cutoff;
+ register int cutyear;
+ register long cuttime;
+ char ** fakeenv;
+ time_t now;
+ time_t t;
+ time_t newt;
+ time_t hibit;
+ struct tm tm;
+ struct tm newtm;
+
+ INITIALIZE(cuttime);
+#if HAVE_GETTEXT - 0
+ (void) setlocale(LC_MESSAGES, "");
+#ifdef TZ_DOMAINDIR
+ (void) bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
+#endif /* defined(TEXTDOMAINDIR) */
+ (void) textdomain(TZ_DOMAIN);
+#endif /* HAVE_GETTEXT - 0 */
+ 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..620d8b0
--- /dev/null
+++ b/usr.sbin/zic/zdump/Makefile
@@ -0,0 +1,13 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/..
+
+PROG= zdump
+MAN= ${.CURDIR}/../zdump.8
+SRCS= zdump.c ialloc.c scheck.c
+
+CFLAGS+= -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..4bc115e
--- /dev/null
+++ b/usr.sbin/zic/zic.8
@@ -0,0 +1,387 @@
+.\" $FreeBSD$
+.Dd October 29, 1997
+.Dt ZIC 8
+.Os
+.Sh NAME
+.Nm zic
+.Nd timezone compiler
+.Sh SYNOPSIS
+.Nm
+.Op Fl Dsv
+.Op Fl d Ar directory
+.Op Fl g Ar group
+.Op Fl L Ar leapsecondfilename
+.Op Fl l Ar localtime
+.Op Fl m Ar mode
+.Op Fl p Ar posixrules
+.Op Fl u Ar user
+.Op Fl y Ar command
+.Op Ar filename ...
+.Sh DESCRIPTION
+.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
+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
+.No "Link timezone localtime
+.Ed
+(Note that this action has no effect on
+.Fx ,
+since the local time zone is specified in
+.Pa /etc/localtime
+and not
+.Pa /usr/share/zoneinfo/localtime . )
+.It Fl m Ar mode
+After creating each output file, change its access mode to
+.Ar mode .
+Both numeric and alphabetic modes are accepted
+(see
+.Xr chmod 1 ) .
+.It Fl p Ar timezone
+Use the given
+.Ar "time zone" Ns 's
+rules when handling POSIX-format
+time zone environment variables.
+.Nm Zic
+will act as if the input contained a link line of the form
+.Bd -literal -offset indent
+.No "Link timezone posixrules
+.Ed
+.It Fl u Ar user
+After creating each output file, change its owner to
+.Ar user
+(which can be either a name or a numeric user ID).
+.It Fl v
+Complain if a year that appears in a data file is outside the range
+of years representable by
+.Xr time 3
+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 FILES
+.Bl -tag -width /usr/share/zoneinfo -compact
+.It /usr/share/zoneinfo
+standard directory used for created files
+.El
+.Sh "SEE ALSO"
+.Xr ctime 3 ,
+.Xr tzfile 5 ,
+.Xr zdump 8
+.\" @(#)zic.8 7.18
diff --git a/usr.sbin/zic/zic.c b/usr.sbin/zic/zic.c
new file mode 100644
index 0000000..109b978
--- /dev/null
+++ b/usr.sbin/zic/zic.c
@@ -0,0 +1,2228 @@
+#ifndef lint
+#ifndef NOID
+static const char elsieid[] = "@(#)zic.c 7.96";
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include "private.h"
+#include "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..8f3c622
--- /dev/null
+++ b/usr.sbin/zic/zic/Makefile
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/..
+
+PROG= zic
+MAN= ${.CURDIR}/../zic.8
+SRCS= zic.c ialloc.c scheck.c
+
+CFLAGS+= -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